diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e831a127b..73f8ca524 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,26 +1,26 @@ name: CI on: push: - branches: - - main + branches-ignore: + - 'generated' + - 'codegen/**' + - 'integrated/**' + - 'stl-preview-head/**' + - 'stl-preview-base/**' pull_request: - branches: - - main - - next + branches-ignore: + - 'stl-preview-head/**' + - 'stl-preview-base/**' jobs: lint: + timeout-minutes: 10 name: lint - runs-on: [self-hosted, linux, amd64] - + runs-on: ${{ github.repository == 'stainless-sdks/hanzo-ai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@v4 - - name: Install system dependencies - run: | - sudo apt-get update -qq - sudo apt-get install -y -qq libatomic1 2>/dev/null || true - - name: Install Rye run: | curl -sSf https://rye.astral.sh/get | bash @@ -35,10 +35,51 @@ jobs: - name: Run lints run: ./scripts/lint + build: + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + timeout-minutes: 10 + name: build + permissions: + contents: read + id-token: write + runs-on: ${{ github.repository == 'stainless-sdks/hanzo-ai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + steps: + - uses: actions/checkout@v4 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Install dependencies + run: rye sync --all-features + + - name: Run build + run: rye build + + - name: Get GitHub OIDC Token + if: github.repository == 'stainless-sdks/hanzo-ai-python' + id: github-oidc + uses: actions/github-script@v6 + with: + script: core.setOutput('github_token', await core.getIDToken()); + + - name: Upload tarball + if: github.repository == 'stainless-sdks/hanzo-ai-python' + env: + URL: https://pkg.stainless.com/s + AUTH: ${{ steps.github-oidc.outputs.github_token }} + SHA: ${{ github.sha }} + run: ./scripts/utils/upload-artifact.sh + test: + timeout-minutes: 10 name: test - runs-on: [self-hosted, linux, amd64] - + runs-on: ${{ github.repository == 'stainless-sdks/hanzo-ai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index cbda64dce..000000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Deploy Docs - -on: - push: - branches: [main] - paths: - - 'docs/**' - - 'mkdocs.yml' - - 'pkg/hanzo-agent/docs/**' - - 'pkg/hanzo-mcp/docs/**' - - '.github/workflows/docs.yml' - workflow_dispatch: - -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - build: - runs-on: [self-hosted, linux, amd64] - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install uv - uses: astral-sh/setup-uv@v4 - - - name: Install dependencies - run: | - pip install mkdocs-material "mkdocstrings[python]<0.27" mkdocs-minify-plugin pymdown-extensions - # Install packages for API reference generation - cd pkg/hanzo-agent && pip install -e . && cd ../.. - cd pkg/hanzo-mcp && pip install -e . && cd ../.. - - - name: Build docs - run: | - mkdocs build 2>&1 || echo "Build completed with warnings" - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: site - - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: [self-hosted, linux, amd64] - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/.github/workflows/generate-api-providers.yml b/.github/workflows/generate-api-providers.yml deleted file mode 100644 index d3edc9332..000000000 --- a/.github/workflows/generate-api-providers.yml +++ /dev/null @@ -1,91 +0,0 @@ -name: Generate API Providers - -on: - schedule: - # Run weekly on Sunday at 00:00 UTC - - cron: '0 0 * * 0' - workflow_dispatch: - inputs: - create_pr: - description: 'Create a PR with the changes' - type: boolean - default: true - preload_specs: - description: 'Pre-download popular OpenAPI specs' - type: boolean - default: false - -jobs: - generate: - runs-on: [self-hosted, linux, amd64] - permissions: - contents: write - pull-requests: write - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install dependencies - run: | - pip install httpx aiofiles pyyaml - - - name: Generate providers from APIs.guru + oapis.org - run: | - cd pkg/hanzo-tools-api - python scripts/generate_providers.py > hanzo_tools/api/apis_guru_providers.py - - - name: Pre-download popular specs - if: github.event.inputs.preload_specs == 'true' - run: | - cd pkg/hanzo-tools-api - pip install -e . - python scripts/preload_specs.py - echo "Specs cached at ~/.hanzo/api/specs/" - ls -la ~/.hanzo/api/specs/ | head -20 - - - name: Check for changes - id: changes - run: | - if git diff --quiet pkg/hanzo-tools-api/hanzo_tools/api/apis_guru_providers.py; then - echo "changed=false" >> $GITHUB_OUTPUT - else - echo "changed=true" >> $GITHUB_OUTPUT - echo "Provider configs changed" - git diff --stat pkg/hanzo-tools-api/hanzo_tools/api/apis_guru_providers.py - fi - - - name: Create Pull Request - if: steps.changes.outputs.changed == 'true' && (github.event.inputs.create_pr == 'true' || github.event_name == 'schedule') - uses: peter-evans/create-pull-request@v6 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: 'chore(api): regenerate provider configs from APIs.guru + oapis.org' - title: 'chore(api): Update API provider configurations' - body: | - ## Summary - Auto-generated provider configurations from: - - [APIs.guru](https://apis.guru/) - OpenAPI specification directory (2500+ APIs) - - [oapis.org](https://oapis.org/) - LLM-optimized API descriptions - - This PR updates the `apis_guru_providers.py` file with the latest API configurations. - - ## Stats - - Total providers: ~1100+ - - With OpenAPI specs: ~1100+ - - LLM-optimized descriptions for popular APIs - - ## Changes - - Updated provider configurations - - Refreshed spec URLs - - Updated LLM-friendly descriptions for popular APIs - branch: chore/update-api-providers - delete-branch: true - labels: | - automated - dependencies diff --git a/.github/workflows/hanzo-packages-ci.yml b/.github/workflows/hanzo-packages-ci.yml deleted file mode 100644 index 849164ce0..000000000 --- a/.github/workflows/hanzo-packages-ci.yml +++ /dev/null @@ -1,409 +0,0 @@ -name: Hanzo Packages CI - -on: - push: - branches: - - main - paths: - - 'pkg/hanzo/**' - - 'pkg/hanzo-network/**' - - 'pkg/hanzo-mcp/**' - # hanzo-agent is now a git submodule - not tested here - - 'pkg/hanzo-memory/**' - - 'pkg/hanzo-aci/**' - - 'pkg/hanzo-dev/**' - - 'pkg/hanzoai/**' - - '.github/workflows/hanzo-packages-ci.yml' - tags: - - 'v*' - - 'hanzo-*' - - 'hanzo-network-*' - - 'hanzo-mcp-*' - # hanzo-agents is now a git submodule - - 'hanzo-memory-*' - - 'hanzo-aci-*' - - 'hanzo-dev-*' - pull_request: - branches: - - main - paths: - - 'pkg/hanzo/**' - - 'pkg/hanzo-network/**' - - 'pkg/hanzo-mcp/**' - # hanzo-agent is now a git submodule - not tested here - - 'pkg/hanzo-memory/**' - - 'pkg/hanzo-aci/**' - - 'pkg/hanzo-dev/**' - - 'pkg/hanzoai/**' - - '.github/workflows/hanzo-packages-ci.yml' - -jobs: - test-hanzo-network: - name: Test hanzo-network - runs-on: [self-hosted, linux, amd64] - timeout-minutes: 5 - defaults: - run: - working-directory: pkg/hanzo-network - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest pytest-asyncio pytest-xdist numpy - pip install -e . - - - name: Run ALL tests - run: | - timeout 120 python -m pytest tests/ -v --tb=short -n 4 --maxfail=5 || true # Parallel with 2min timeout - - test-hanzo-mcp: - name: Test hanzo-mcp - runs-on: [self-hosted, linux, amd64] - timeout-minutes: 5 - defaults: - run: - working-directory: pkg/hanzo-mcp - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest pytest-asyncio pytest-cov pytest-xdist numpy - pip install -e . - # Install hanzo dependencies from the monorepo - pip install -e ../hanzo-network - # hanzo-agents is now a PyPI package (git submodule) - pip install hanzo-agents || true - pip install -e ../hanzo-memory - - - name: Run ALL tests - run: | - timeout 120 python -m pytest tests/ -v --tb=short --cov=hanzo_mcp --cov-report=term-missing -n 4 --dist loadgroup --maxfail=5 -m "not slow" || echo "Tests completed or timed out" - - test-hanzo-aci: - name: Test hanzo-aci - runs-on: [self-hosted, linux, amd64] - timeout-minutes: 5 - defaults: - run: - working-directory: pkg/hanzo-aci - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest pytest-asyncio pytest-cov pytest-xdist - pip install -e . - - - name: Run ALL tests - run: | - timeout 120 python -m pytest tests/ -v --tb=short --cov=dev_aci --cov-report=term-missing -n 4 --maxfail=5 || true # Parallel with 2min timeout - - # test-hanzo-agents: REMOVED - hanzo-agent is now a git submodule at pkg/hanzo-agent - # Tests run in the original repository: https://github.com/hanzoai/agent - - test-hanzo-memory: - name: Test hanzo-memory - runs-on: [self-hosted, linux, amd64] - timeout-minutes: 5 - defaults: - run: - working-directory: pkg/hanzo-memory - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest pytest-asyncio pytest-cov pytest-xdist polars httpx - pip install -e .[test] - - - name: Run ALL tests - run: | - timeout 120 python -m pytest tests/ -v --tb=short --cov=hanzo_memory --cov-report=term-missing -n 4 --maxfail=5 -k "not TestInfinityClient" || true # Parallel with 2min timeout - - integration-test: - name: Integration Test - needs: [test-hanzo-network, test-hanzo-mcp, test-hanzo-aci, test-hanzo-memory, test-hanzo, test-hanzo-dev] - runs-on: [self-hosted, linux, amd64] - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install all packages - run: | - python -m pip install --upgrade pip - pip install pytest pytest-asyncio pytest-xdist numpy - cd pkg/hanzo && pip install -e . && cd ../.. - cd pkg/hanzo-network && pip install -e . && cd ../.. - cd pkg/hanzo-mcp && pip install -e . && cd ../.. - # hanzo-agents is now a PyPI package (git submodule) - pip install hanzo-agents || true - cd pkg/hanzo-memory && pip install -e . && cd ../.. - cd pkg/hanzo-aci && pip install -e . && cd ../.. - cd pkg/hanzo-dev && pip install -e . && cd ../.. - - - name: Run integration tests - run: | - timeout 60 python -m pytest pkg/hanzo-mcp/tests/test_hanzo_mcp_integration.py -v -n 4 --maxfail=5 || true - - lint: - name: Lint Hanzo Packages - runs-on: [self-hosted, linux, amd64] - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install linting tools - run: | - python -m pip install --upgrade pip - pip install ruff mypy - - - name: Lint hanzo-network - run: | - cd pkg/hanzo-network - ruff check . || true - mypy . --ignore-missing-imports || true - - - name: Lint hanzo-mcp - run: | - cd pkg/hanzo-mcp - ruff check . || true - mypy . --ignore-missing-imports || true - - # Lint hanzo-agents: SKIPPED - hanzo-agent is now a git submodule - - - name: Lint hanzo-aci - run: | - cd pkg/hanzo-aci - ruff check . || true - mypy . --ignore-missing-imports || true - - - name: Lint hanzo-memory - run: | - cd pkg/hanzo-memory - ruff check . || true - mypy . --ignore-missing-imports || true - - - name: Lint hanzo - run: | - cd pkg/hanzo - ruff check . || true - mypy . --ignore-missing-imports || true - - - name: Lint hanzo-dev - run: | - cd pkg/hanzo-dev - ruff check . || true - mypy . --ignore-missing-imports || true - - test-hanzo: - name: Test hanzo (main package) - runs-on: [self-hosted, linux, amd64] - timeout-minutes: 5 - defaults: - run: - working-directory: pkg/hanzo - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest pytest-asyncio pytest-cov pytest-xdist - pip install -e . - - - name: Run tests if available - run: | - if [ -d "tests" ]; then - timeout 120 python -m pytest tests/ -v --tb=short -n 4 --maxfail=5 || true - else - echo "No tests directory found, skipping tests" - fi - - test-hanzo-dev: - name: Test hanzo-dev - runs-on: [self-hosted, linux, amd64] - timeout-minutes: 5 - defaults: - run: - working-directory: pkg/hanzo-dev - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest pytest-asyncio pytest-cov pytest-xdist - pip install -e . - # Install hanzo dependencies - pip install -e ../hanzo-mcp - - - name: Run tests if available - run: | - if [ -d "tests" ]; then - timeout 120 python -m pytest tests/ -v --tb=short -n 4 --maxfail=5 || true - else - echo "No tests directory found, checking for test module" - python -c "from hanzo_dev import tests; print('Test module found')" || echo "No test module" - fi - - # Auto-publish packages with new versions on push to main - auto-publish-new-versions: - name: Auto-Publish New Versions - if: github.ref == 'refs/heads/main' && github.event_name == 'push' - needs: [test-hanzo-network, test-hanzo-mcp, test-hanzo-aci, test-hanzo-memory, test-hanzo, test-hanzo-dev, integration-test] - runs-on: [self-hosted, linux, amd64] - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Check and publish new versions - env: - PYPI_TOKEN: ${{ secrets.HANZO_PYPI_TOKEN || secrets.PYPI_TOKEN }} - run: | - python ./bin/check-and-publish.py - - # Publish packages to PyPI when a tag is pushed (for manual releases) - publish-to-pypi: - name: Publish to PyPI (Tag) - if: startsWith(github.ref, 'refs/tags/') - needs: [test-hanzo-network, test-hanzo-mcp, test-hanzo-aci, test-hanzo-memory, test-hanzo, test-hanzo-dev, integration-test] - runs-on: [self-hosted, linux, amd64] - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install build dependencies - run: | - python -m pip install --upgrade pip - pip install build twine - - - name: Get tag name - id: get_tag - run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT - - - name: Determine package to publish - id: determine_package - run: | - TAG="${{ steps.get_tag.outputs.TAG }}" - PACKAGES="" - - # Determine which package(s) to publish based on tag - # Note: hanzo-agents is now a separate repo (git submodule), not published from here - if [[ $TAG == hanzo-network-* ]]; then - PACKAGES="hanzo-network" - elif [[ $TAG == hanzo-mcp-* ]]; then - PACKAGES="hanzo-mcp" - elif [[ $TAG == hanzo-memory-* ]]; then - PACKAGES="hanzo-memory" - elif [[ $TAG == hanzo-aci-* ]]; then - PACKAGES="hanzo-aci" - elif [[ $TAG == hanzo-dev-* ]]; then - PACKAGES="hanzo-dev" - elif [[ $TAG == hanzo-* ]]; then - PACKAGES="hanzo" - elif [[ $TAG == v* ]]; then - # For general version tags, publish all packages (except hanzo-agents which is a submodule) - PACKAGES="hanzo hanzo-network hanzo-mcp hanzo-memory hanzo-aci hanzo-dev" - fi - - echo "PACKAGES=$PACKAGES" >> $GITHUB_OUTPUT - echo "Publishing packages: $PACKAGES" - - - name: Publish packages - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.HANZO_PYPI_TOKEN || secrets.PYPI_TOKEN }} - run: | - PACKAGES="${{ steps.determine_package.outputs.PACKAGES }}" - - if [ -z "$PACKAGES" ]; then - echo "No packages to publish for tag ${{ steps.get_tag.outputs.TAG }}" - exit 0 - fi - - for package in $PACKAGES; do - echo "Publishing $package..." - cd "pkg/$package" - - # Clean and build - rm -rf dist/ build/ *.egg-info - python -m build - - # Upload to PyPI - python -m twine upload dist/* --skip-existing - - cd ../.. - done - - - name: Create release summary - run: | - echo "## ๐Ÿš€ Published to PyPI" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "Tag: ${{ steps.get_tag.outputs.TAG }}" >> $GITHUB_STEP_SUMMARY - echo "Packages: ${{ steps.determine_package.outputs.PACKAGES }}" >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/workflows/publish-external-dists.yml b/.github/workflows/publish-external-dists.yml deleted file mode 100644 index ffcd52f57..000000000 --- a/.github/workflows/publish-external-dists.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Publish External Dists - -# One-shot workflow: uploads any pre-built wheel/sdist staged under -# dist-external/ to PyPI using the existing HANZO_PYPI_TOKEN / PYPI_TOKEN -# repo secret. Used to bootstrap external deps (zap-mdns, zap-protocol) -# whose source lives outside this repo. - -on: - workflow_dispatch: - -jobs: - upload: - name: Upload dist-external/* to PyPI - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install twine - run: pip install --upgrade twine - - - name: List dists - run: ls -la dist-external/ - - - name: Upload to PyPI - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.HANZO_PYPI_TOKEN || secrets.PYPI_TOKEN }} - run: | - python -m twine upload --skip-existing dist-external/*.whl dist-external/*.tar.gz diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 891085b60..df4f4835c 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -1,225 +1,31 @@ -# This workflow is triggered when a tag is pushed or a GitHub release is created. +# This workflow is triggered when a GitHub release is created. # It can also be run manually to re-publish to PyPI in case it failed for some reason. # You can run this workflow by navigating to https://www.github.com/hanzoai/python-sdk/actions/workflows/publish-pypi.yml name: Publish PyPI on: workflow_dispatch: - inputs: - packages: - description: 'Packages to publish (space-separated, or "all" for everything)' - required: false - default: 'all' - - push: - tags: - - 'v*' - - 'hanzo-*' - - 'hanzoai-*' - - 'hanzo-tools-*' release: types: [published] jobs: - # Run tests first to ensure code quality - test: - name: Run Tests - uses: ./.github/workflows/test.yml - - publish-all-packages: - name: Publish All Python Packages - runs-on: [self-hosted, linux, amd64] - needs: test # Only publish if tests pass - if: always() && !cancelled() + publish: + name: publish + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Fetch all tags - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - name: Install build tools + - name: Install Rye run: | - python -m pip install --upgrade pip - pip install build twine - - - name: Get tag name - id: get_tag - run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' - - name: Determine packages to publish - id: determine_packages + - name: Publish to PyPI run: | - TAG="${{ steps.get_tag.outputs.TAG }}" - MANUAL_INPUT="${{ github.event.inputs.packages }}" - PACKAGES="" - - # All hanzo-tools packages - TOOLS_PACKAGES="hanzo-tools-core hanzo-tools-fs hanzo-tools-shell hanzo-tools-browser hanzo-tools-memory hanzo-tools-todo hanzo-tools-reasoning hanzo-tools-lsp hanzo-tools-refactor hanzo-tools-database hanzo-tools-agent hanzo-tools-jupyter hanzo-tools-editor hanzo-tools-llm hanzo-tools-vector hanzo-tools-config hanzo-tools-mcp hanzo-tools-computer hanzo-tools" - - # Main packages - MAIN_PACKAGES="hanzoai hanzo hanzo-cli hanzo-kms hanzo-iam hanzo-consensus hanzo-network hanzo-mcp hanzo-memory hanzo-aci hanzo-dev" - - # Handle manual workflow dispatch - if [ -n "$MANUAL_INPUT" ]; then - if [ "$MANUAL_INPUT" = "all" ]; then - PACKAGES="$MAIN_PACKAGES $TOOLS_PACKAGES" - else - PACKAGES="$MANUAL_INPUT" - fi - # Handle specific package tags - elif [[ $TAG == hanzo-tools-core-* ]]; then - PACKAGES="hanzo-tools-core" - elif [[ $TAG == hanzo-tools-fs-* ]]; then - PACKAGES="hanzo-tools-fs" - elif [[ $TAG == hanzo-tools-shell-* ]]; then - PACKAGES="hanzo-tools-shell" - elif [[ $TAG == hanzo-tools-browser-* ]]; then - PACKAGES="hanzo-tools-browser" - elif [[ $TAG == hanzo-tools-memory-* ]]; then - PACKAGES="hanzo-tools-memory" - elif [[ $TAG == hanzo-tools-todo-* ]]; then - PACKAGES="hanzo-tools-todo" - elif [[ $TAG == hanzo-tools-reasoning-* ]]; then - PACKAGES="hanzo-tools-reasoning" - elif [[ $TAG == hanzo-tools-lsp-* ]]; then - PACKAGES="hanzo-tools-lsp" - elif [[ $TAG == hanzo-tools-refactor-* ]]; then - PACKAGES="hanzo-tools-refactor" - elif [[ $TAG == hanzo-tools-database-* ]]; then - PACKAGES="hanzo-tools-database" - elif [[ $TAG == hanzo-tools-agent-* ]]; then - PACKAGES="hanzo-tools-agent" - elif [[ $TAG == hanzo-tools-jupyter-* ]]; then - PACKAGES="hanzo-tools-jupyter" - elif [[ $TAG == hanzo-tools-editor-* ]]; then - PACKAGES="hanzo-tools-editor" - elif [[ $TAG == hanzo-tools-llm-* ]]; then - PACKAGES="hanzo-tools-llm" - elif [[ $TAG == hanzo-tools-vector-* ]]; then - PACKAGES="hanzo-tools-vector" - elif [[ $TAG == hanzo-tools-config-* ]]; then - PACKAGES="hanzo-tools-config" - elif [[ $TAG == hanzo-tools-mcp-* ]]; then - PACKAGES="hanzo-tools-mcp" - elif [[ $TAG == hanzo-tools-computer-* ]]; then - PACKAGES="hanzo-tools-computer" - elif [[ $TAG == hanzo-tools-* ]]; then - # All tools packages - PACKAGES="$TOOLS_PACKAGES" - elif [[ $TAG == hanzo-cli-* ]]; then - PACKAGES="hanzo-cli" - elif [[ $TAG == hanzo-kms-* ]]; then - PACKAGES="hanzo-kms" - elif [[ $TAG == hanzo-iam-* ]]; then - PACKAGES="hanzo-iam" - elif [[ $TAG == hanzo-consensus-* ]]; then - PACKAGES="hanzo-consensus" - elif [[ $TAG == hanzo-network-* ]]; then - PACKAGES="hanzo-network" - elif [[ $TAG == hanzo-mcp-* ]]; then - PACKAGES="hanzo-mcp" - elif [[ $TAG == hanzo-memory-* ]]; then - PACKAGES="hanzo-memory" - elif [[ $TAG == hanzo-aci-* ]]; then - PACKAGES="hanzo-aci" - elif [[ $TAG == hanzo-dev-* ]]; then - PACKAGES="hanzo-dev" - elif [[ $TAG == hanzoai-* ]]; then - PACKAGES="hanzoai" - elif [[ $TAG == hanzo-* ]]; then - PACKAGES="hanzo" - elif [[ $TAG == v* ]]; then - # For general version tags, publish all packages - PACKAGES="$MAIN_PACKAGES $TOOLS_PACKAGES" - fi - - echo "PACKAGES=$PACKAGES" >> $GITHUB_OUTPUT - echo "Publishing packages: $PACKAGES" - - - name: Build and publish packages + bash ./bin/publish-pypi env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.HANZO_PYPI_TOKEN || secrets.PYPI_TOKEN }} - run: | - PACKAGES="${{ steps.determine_packages.outputs.PACKAGES }}" - - if [ -z "$PACKAGES" ]; then - echo "No packages to publish for tag ${{ steps.get_tag.outputs.TAG }}" - exit 0 - fi - - for package in $PACKAGES; do - echo "==========================================" - echo "Building and publishing $package..." - echo "==========================================" - - # hanzoai is the root package, all others live under pkg/ - if [ "$package" = "hanzoai" ]; then - PKG_DIR="." - elif [ -d "pkg/$package" ]; then - PKG_DIR="pkg/$package" - else - echo "Warning: Package directory pkg/$package does not exist, skipping" - continue - fi - - # Clean any previous builds - rm -rf "$PKG_DIR/dist/" "$PKG_DIR/build/" - - # Build from repo root to avoid stdlib shadowing (hanzoai/types vs types) - python -m build "$PKG_DIR" --outdir "$PKG_DIR/dist" - - # Upload to PyPI - python -m twine upload "$PKG_DIR/dist/"* --skip-existing || echo "Warning: Failed to upload $package (may already exist)" - done - - - name: Create GitHub Release Notes - if: startsWith(github.ref, 'refs/tags/v') - uses: actions/github-script@v7 - with: - script: | - const tag = '${{ steps.get_tag.outputs.TAG }}'; - const packages = '${{ steps.determine_packages.outputs.PACKAGES }}'.split(' ').filter(p => p); - - let body = `## ๐Ÿš€ Published Python Packages\n\n`; - body += `The following packages have been published to PyPI:\n\n`; - - for (const pkg of packages) { - body += `- โœ… **${pkg}** - [View on PyPI](https://pypi.org/project/${pkg}/)\n`; - } - - body += `\n### Installation\n\n`; - body += `\`\`\`bash\n`; - body += `# Install main SDK\n`; - body += `pip install hanzo\n\n`; - body += `# Install MCP tools\n`; - body += `pip install hanzo-mcp\n\n`; - body += `# Install individual tool packages\n`; - body += `pip install hanzo-tools # All tools\n`; - body += `pip install hanzo-tools-core # Core only\n`; - body += `\`\`\`\n`; - - // Update release if it exists - try { - const releases = await github.rest.repos.listReleases({ - owner: context.repo.owner, - repo: context.repo.repo, - }); - - const release = releases.data.find(r => r.tag_name === tag); - if (release) { - await github.rest.repos.updateRelease({ - owner: context.repo.owner, - repo: context.repo.repo, - release_id: release.id, - body: release.body + '\n\n' + body, - }); - } - } catch (error) { - console.log('Could not update release notes:', error); - } + PYPI_TOKEN: ${{ secrets.HANZO_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/quality-gate.yml b/.github/workflows/quality-gate.yml deleted file mode 100644 index be3d13531..000000000 --- a/.github/workflows/quality-gate.yml +++ /dev/null @@ -1,268 +0,0 @@ -name: Quality Gate - -on: - push: - branches: [main, master, develop] - pull_request: - branches: [main, master, develop] - release: - types: [created] - -# Cancel any in-progress runs when a new run starts -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - block-todo-stub-code: - name: "๐Ÿšซ Block TODO/STUB/FAKE Code" - runs-on: [self-hosted, linux, amd64] - steps: - - uses: actions/checkout@v4 - - - name: "๐Ÿ” Search for forbidden patterns in hanzo-mcp" - run: | - echo "Searching for problematic TODO/STUB patterns in hanzo-mcp..." - - # Focus on hanzo-mcp package only (other packages may have legitimate TODOs) - # We look for specific problematic patterns, not all TODOs - - FOUND_ISSUES=0 - - # Check for stub functions that return "TODO" or "STUB" strings - echo "Checking for stub return values..." - if grep -rn --include="*.py" -E "return\s+['\"]TODO['\"]|return\s+['\"]STUB['\"]" pkg/hanzo-mcp/hanzo_mcp/ 2>/dev/null; then - echo "โŒ Found stub return values" - FOUND_ISSUES=1 - fi - - # Check for empty pass-only functions (excluding fallback stubs in except blocks) - echo "Checking for empty functions..." - # This is better handled by the pytest test_no_stubs.py - - # Check for explicit "STUB:" or "FAKE:" comments indicating unfinished code - echo "Checking for explicit stub markers..." - if grep -rn --include="*.py" -E "#\s*(STUB|FAKE|UNFINISHED):" pkg/hanzo-mcp/hanzo_mcp/ 2>/dev/null; then - echo "โŒ Found explicit stub markers" - FOUND_ISSUES=1 - fi - - if [ $FOUND_ISSUES -eq 1 ]; then - echo "๐Ÿšซ DEPLOYMENT BLOCKED: Remove stub/fake code before deploying!" - exit 1 - fi - - echo "โœ… No forbidden stub patterns found in hanzo-mcp" - echo "Note: Other packages may contain legitimate TODO comments for documentation" - - test-no-stubs: - name: "๐Ÿงช Anti-Stub Tests" - runs-on: [self-hosted, linux, amd64] - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install dependencies - run: | - pip install uv - uv venv - source .venv/bin/activate - uv pip install -e ./pkg/hanzo-mcp[test] - - - name: "Run anti-stub tests" - run: | - source .venv/bin/activate - cd pkg/hanzo-mcp - python -m pytest tests/test_no_stubs.py -v --tb=short - - - name: "Verify no incomplete implementations" - run: | - source .venv/bin/activate - cd pkg/hanzo-mcp - # Run the test file directly for extra validation - python tests/test_no_stubs.py - - all-tests-must-pass: - name: "โœ… ALL Tests Must Pass" - runs-on: [self-hosted, linux, amd64] - strategy: - fail-fast: true # Stop immediately if any test fails - matrix: - python-version: ['3.12'] - - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: Install test dependencies - run: | - pip install uv - uv venv - source .venv/bin/activate - # Install hanzo-mcp with test deps and optional memory/agents packages - uv pip install -e ./pkg/hanzo-mcp[test,memory,agents] - # Install sibling packages for integration tests - uv pip install -e ./pkg/hanzo-memory || true - uv pip install -e ./pkg/hanzo-network || true - # Override PyPI versions with local tool packages (monorepo) - uv pip install -e ./pkg/hanzo-tools -e ./pkg/hanzo-tools-core \ - -e ./pkg/hanzo-tools-agent -e ./pkg/hanzo-tools-shell -e ./pkg/hanzo-tools-fs \ - -e ./pkg/hanzo-tools-memory -e ./pkg/hanzo-tools-todo -e ./pkg/hanzo-tools-reasoning \ - -e ./pkg/hanzo-tools-browser -e ./pkg/hanzo-tools-lsp -e ./pkg/hanzo-tools-refactor \ - -e ./pkg/hanzo-tools-computer -e ./pkg/hanzo-tools-config -e ./pkg/hanzo-tools-api \ - -e ./pkg/hanzo-tools-vcs -e ./pkg/hanzo-tools-net -e ./pkg/hanzo-tools-plan \ - -e ./pkg/hanzo-tools-jupyter -e ./pkg/hanzo-tools-llm 2>/dev/null || true - - - name: "๐Ÿงช Run ALL tests" - run: | - source .venv/bin/activate - cd pkg/hanzo-mcp - # Run all working tests with strict mode - # Note: Some async tests require specific pytest-asyncio configuration - # The core test suite validates CI requirements - python -m pytest tests/test_agent_tools_ci.py tests/test_llm_warnings.py \ - -v \ - --strict-markers \ - --tb=short \ - 2>&1 | tee test-output.log - - # Run additional simple tests - python -m pytest tests/test_hanzo_mcp_simple.py::test_cli_help \ - tests/test_hanzo_mcp_simple.py::test_cli_version \ - tests/test_hanzo_mcp_simple.py::test_import_tools \ - -v --tb=short 2>&1 | tee -a test-output.log - - # Check if any tests failed - if grep -q "FAILED" test-output.log; then - echo "โŒ TESTS FAILED! All tests must pass!" - exit 1 - fi - - echo "โœ… All tests passed!" - - code-quality: - name: "๐ŸŽฏ Code Quality Check" - runs-on: [self-hosted, linux, amd64] - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install quality tools - run: | - pip install ruff mypy pyright bandit - - - name: "๐Ÿ” Lint with ruff" - run: | - cd pkg/hanzo-mcp - ruff check hanzo_mcp tests --fix --exit-non-zero-on-fix - - - name: "๐Ÿ” Type check with mypy" - run: | - cd pkg/hanzo-mcp - mypy hanzo_mcp --ignore-missing-imports --strict || true - - - name: "๐Ÿ” Security scan with bandit" - run: | - cd pkg/hanzo-mcp - bandit -r hanzo_mcp -f json -o bandit-report.json || true - if [ -f bandit-report.json ]; then - python -m json.tool bandit-report.json - fi - - function-implementation-check: - name: "๐Ÿ”จ Verify Functions Are Implemented" - runs-on: [self-hosted, linux, amd64] - steps: - - uses: actions/checkout@v4 - - - name: "Check for empty functions" - run: | - echo "Checking for empty functions with only 'pass'..." - - # Find functions that only contain pass - FOUND_EMPTY=0 - for file in $(find pkg/hanzo-mcp -name "*.py" -not -path "*/test*"); do - # Look for functions with only pass - if grep -Pzo "def\s+\w+\([^)]*\):\s*\n\s*pass\s*$" "$file" 2>/dev/null; then - echo "โŒ Empty function found in: $file" - FOUND_EMPTY=1 - fi - - # Look for functions with only ellipsis - if grep -Pzo "def\s+\w+\([^)]*\):\s*\n\s*\.\.\.\s*$" "$file" 2>/dev/null; then - echo "โŒ Ellipsis-only function found in: $file" - FOUND_EMPTY=1 - fi - done - - if [ $FOUND_EMPTY -eq 1 ]; then - echo "๐Ÿšซ BLOCKED: Empty functions detected! Implement them properly!" - exit 1 - fi - - echo "โœ… All functions have implementations" - - block-deployment: - name: "๐Ÿš€ Deployment Gate" - needs: - - block-todo-stub-code - - test-no-stubs - - all-tests-must-pass - - code-quality - - function-implementation-check - runs-on: [self-hosted, linux, amd64] - if: github.event_name == 'release' || github.ref == 'refs/heads/main' - - steps: - - name: "โœ… Quality Gate PASSED" - run: | - echo "โœ… All quality checks passed!" - echo "โœ… No TODOs, STUBs, or FAKE code found" - echo "โœ… All tests are passing" - echo "โœ… All functions are implemented" - echo "๐Ÿš€ Ready for deployment!" - - - name: "๐Ÿ“ฆ Prepare for PyPI deployment" - if: github.event_name == 'release' - run: | - echo "Ready to deploy to PyPI" - echo "Version: ${{ github.event.release.tag_name }}" - - publish-to-pypi: - name: "๐Ÿ“ฆ Publish to PyPI" - needs: [block-deployment] - if: github.event_name == 'release' - runs-on: [self-hosted, linux, amd64] - - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Build package - run: | - pip install uv - cd pkg/hanzo-mcp - uv build - - - name: Publish to PyPI - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} - run: | - pip install twine - cd pkg/hanzo-mcp - twine upload dist/* --non-interactive --skip-existing \ No newline at end of file diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml new file mode 100644 index 000000000..f368afdc8 --- /dev/null +++ b/.github/workflows/release-doctor.yml @@ -0,0 +1,21 @@ +name: Release Doctor +on: + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + release_doctor: + name: release doctor + runs-on: ubuntu-latest + if: github.repository == 'hanzoai/python-sdk' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') + + steps: + - uses: actions/checkout@v4 + + - name: Check release environment + run: | + bash ./bin/check-release-environment + env: + PYPI_TOKEN: ${{ secrets.HANZO_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/test-auto-publish.yml b/.github/workflows/test-auto-publish.yml deleted file mode 100644 index ae1276e90..000000000 --- a/.github/workflows/test-auto-publish.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: Test Auto-Publish -# This workflow can be manually triggered to test the auto-publish mechanism - -on: - workflow_dispatch: - inputs: - dry_run: - description: 'Dry run (check versions without publishing)' - required: false - default: 'true' - type: choice - options: - - 'true' - - 'false' - -jobs: - test-version-check: - name: Test Version Check - runs-on: [self-hosted, linux, amd64] - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Create version check script - run: | - cat > check_versions.py << 'EOF' - import json - import re - import urllib.request - from pathlib import Path - - def get_local_version(package_dir): - pyproject = package_dir / 'pyproject.toml' - if pyproject.exists(): - content = pyproject.read_text() - match = re.search(r'version\s*=\s*["\']([^"\']+)["\']', content) - return match.group(1) if match else 'unknown' - return 'not found' - - def get_pypi_version(package_name): - try: - with urllib.request.urlopen(f'https://pypi.org/pypi/{package_name}/json') as r: - return json.loads(r.read()).get('info', {}).get('version', 'error') - except: - return 'not published' - - packages = [ - 'hanzo', 'hanzo-network', 'hanzo-mcp', - # hanzo-agents is now a git submodule - 'hanzo-memory', 'hanzo-aci', 'hanzo-dev' - ] - - print('๐Ÿ“ฆ Package Version Status:') - print('=' * 60) - for pkg in packages: - pkg_dir = Path('pkg') / pkg - local = get_local_version(pkg_dir) - pypi = get_pypi_version(pkg) - status = '๐Ÿ†• NEW' if local != pypi and pypi != 'not published' else 'โœ… OK' - print(f'{pkg:20} Local: {local:10} PyPI: {pypi:10} {status}') - EOF - - - name: Check package versions - run: python check_versions.py - - - name: Install dependencies for dry run - run: | - python -m pip install --upgrade pip - pip install build twine - - - name: Run auto-publish check (dry run) - if: inputs.dry_run == 'true' - run: | - echo "๐Ÿ” DRY RUN - Checking what would be published..." - # Modify script to not actually publish - sed 's/python -m twine upload/echo "Would upload:"/' bin/check-and-publish.py > check-dry.py - PYPI_TOKEN="dry-run-token" python check-dry.py || echo "Dry run complete" - - - name: Run actual auto-publish - if: inputs.dry_run == 'false' - env: - PYPI_TOKEN: ${{ secrets.HANZO_PYPI_TOKEN || secrets.PYPI_TOKEN }} - run: | - echo "๐Ÿš€ ACTUAL RUN - Publishing new versions..." - python ./bin/check-and-publish.py \ No newline at end of file diff --git a/.github/workflows/test-hanzo-mcp.yml b/.github/workflows/test-hanzo-mcp.yml deleted file mode 100644 index 7a632748e..000000000 --- a/.github/workflows/test-hanzo-mcp.yml +++ /dev/null @@ -1,198 +0,0 @@ -name: Test Hanzo MCP - -on: - push: - branches: [main] - paths: - - 'pkg/hanzo-mcp/**' - - 'pkg/hanzo-tools-*/**' - - '.github/workflows/test-hanzo-mcp.yml' - pull_request: - branches: [main] - paths: - - 'pkg/hanzo-mcp/**' - - 'pkg/hanzo-tools-*/**' - - '.github/workflows/test-hanzo-mcp.yml' - workflow_dispatch: - inputs: - run_integration_tests: - description: 'Run integration tests requiring API keys' - required: false - type: boolean - default: false - -jobs: - test: - runs-on: [self-hosted, linux, amd64] - strategy: - matrix: - python-version: ['3.12'] - - steps: - - uses: actions/checkout@v4 - - - name: Install uv - uses: astral-sh/setup-uv@v3 - with: - enable-cache: true - cache-dependency-glob: "pkg/hanzo-mcp/pyproject.toml" - - - name: Set up Python ${{ matrix.python-version }} - run: uv python install ${{ matrix.python-version }} - - - name: Install dependencies - run: | - cd pkg/hanzo-mcp - uv venv - source .venv/bin/activate - uv pip install -e ".[test,performance,analytics,dev]" - uv pip install pytest-cov - # Override PyPI versions with local tool packages (monorepo) - uv pip install -e ../hanzo-tools -e ../hanzo-tools-core \ - -e ../hanzo-tools-agent -e ../hanzo-tools-shell -e ../hanzo-tools-fs \ - -e ../hanzo-tools-memory -e ../hanzo-tools-todo -e ../hanzo-tools-reasoning \ - -e ../hanzo-tools-browser -e ../hanzo-tools-lsp -e ../hanzo-tools-refactor \ - -e ../hanzo-tools-computer -e ../hanzo-tools-config -e ../hanzo-tools-api \ - -e ../hanzo-tools-vcs -e ../hanzo-tools-net -e ../hanzo-tools-plan \ - -e ../hanzo-tools-jupyter -e ../hanzo-tools-llm 2>/dev/null || true - - - name: Run type checking with mypy - run: | - cd pkg/hanzo-mcp - source .venv/bin/activate - mypy hanzo_mcp --ignore-missing-imports --strict || true - - - name: Run linting with ruff - run: | - cd pkg/hanzo-mcp - source .venv/bin/activate - ruff check hanzo_mcp tests - - - name: Run format check with ruff - run: | - cd pkg/hanzo-mcp - source .venv/bin/activate - ruff format --check hanzo_mcp tests - - - name: Run unit tests - run: | - cd pkg/hanzo-mcp - source .venv/bin/activate - # Run specific working tests, excluding the failing stdio_server test - python -m pytest tests/test_agent_tools_ci.py tests/test_llm_warnings.py -v -o "addopts=" - python -m pytest tests/test_hanzo_mcp_simple.py::test_cli_help tests/test_hanzo_mcp_simple.py::test_cli_version tests/test_hanzo_mcp_simple.py::test_import_tools -v -o "addopts=" - echo "โœ“ Test Summary: 11 tests passed, 100% success rate" - - - integration-tests: - runs-on: [self-hosted, linux, amd64] - if: | - github.event_name == 'workflow_dispatch' && - github.event.inputs.run_integration_tests == 'true' - - steps: - - uses: actions/checkout@v4 - - - name: Install uv - uses: astral-sh/setup-uv@v3 - with: - enable-cache: true - - - name: Set up Python - run: uv python install 3.12 - - - name: Install dependencies - run: | - cd pkg/hanzo-mcp - uv venv - source .venv/bin/activate - uv pip install -e ".[test,performance,analytics,dev]" - uv pip install pytest-cov - - - name: Run integration tests with API keys - env: - HANZO_API_KEY: ${{ secrets.HANZO_API_KEY }} - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} - XAI_API_KEY: ${{ secrets.XAI_API_KEY }} - run: | - cd pkg/hanzo-mcp - uv run pytest tests -v -m integration --timeout=300 - - - name: Test agent tools (minimal) - run: | - cd pkg/hanzo-mcp - # Test agent tool import and basic functionality - echo "Testing Agent tools..." - uv run python -c " - from hanzo_tools.agent import AgentTool, ZenTool, ReviewTool, TOOLS - - # Verify all tools are available - tool_names = [t.__name__ for t in TOOLS] - assert 'AgentTool' in tool_names - assert 'ZenTool' in tool_names - assert 'ReviewTool' in tool_names - - # Create tool instances - agent = AgentTool() - zen = ZenTool() - review = ReviewTool() - - print(f'Agent tool name: {agent.name}') - print(f'Zen tool name: {zen.name}') - print(f'Review tool name: {review.name}') - print('All agent tools created successfully!') - " - - release-check: - runs-on: [self-hosted, linux, amd64] - needs: [test] - if: github.ref == 'refs/heads/main' - - steps: - - uses: actions/checkout@v4 - - - name: Install uv - uses: astral-sh/setup-uv@v3 - - - name: Set up Python - run: uv python install 3.12 - - - name: Build package - run: | - cd pkg/hanzo-mcp - uv build - - - name: Check package with twine - run: | - cd pkg/hanzo-mcp - uv venv - source .venv/bin/activate - uv pip install twine - twine check dist/* - - - name: Test installation - run: | - # Create a fresh venv and test installation - cd /tmp - uv venv test-env - source test-env/bin/activate - uv pip install $GITHUB_WORKSPACE/pkg/hanzo-mcp/dist/*.whl - # Override with local tool packages (not yet published to PyPI) - uv pip install -e $GITHUB_WORKSPACE/pkg/hanzo-tools -e $GITHUB_WORKSPACE/pkg/hanzo-tools-core \ - -e $GITHUB_WORKSPACE/pkg/hanzo-tools-agent 2>/dev/null || true - python -c "import hanzo_mcp; print(f'Hanzo MCP version: {hanzo_mcp.__version__}')" - - # Test that agent tools can be imported from hanzo_tools.agent - python -c " - from hanzo_tools.agent import AgentTool, ZenTool, ReviewTool, TOOLS - print(f'Agent tools available: {[t.__name__ for t in TOOLS]}') - print('All agent tools importable!') - " - - - name: Archive built packages - uses: actions/upload-artifact@v4 - with: - name: hanzo-mcp-dist - path: pkg/hanzo-mcp/dist/ diff --git a/.github/workflows/test-hanzo-tools.yml b/.github/workflows/test-hanzo-tools.yml deleted file mode 100644 index 074024aa8..000000000 --- a/.github/workflows/test-hanzo-tools.yml +++ /dev/null @@ -1,114 +0,0 @@ -name: Test Hanzo Tools - -on: - push: - branches: [main] - paths: - - 'pkg/hanzo-tools-*/**' - - '.github/workflows/test-hanzo-tools.yml' - pull_request: - branches: [main] - paths: - - 'pkg/hanzo-tools-*/**' - - '.github/workflows/test-hanzo-tools.yml' - workflow_dispatch: - -jobs: - test: - runs-on: [self-hosted, linux, amd64] - timeout-minutes: 10 - strategy: - matrix: - python-version: ['3.12'] - - steps: - - uses: actions/checkout@v4 - - - name: Install uv - uses: astral-sh/setup-uv@v3 - with: - enable-cache: true - - - name: Set up Python ${{ matrix.python-version }} - run: uv python install ${{ matrix.python-version }} - - - name: Install hanzo-tools packages - run: | - uv venv - source .venv/bin/activate - # Install base shim first (provides hanzo_tools.core.unified) - uv pip install -e pkg/hanzo-tools - uv pip install -e pkg/hanzo-tools-core - # Install all tool packages - uv pip install -e pkg/hanzo-tools-fs - uv pip install -e pkg/hanzo-tools-shell - uv pip install -e pkg/hanzo-tools-browser - uv pip install -e pkg/hanzo-tools-memory - uv pip install -e pkg/hanzo-tools-todo - uv pip install -e pkg/hanzo-tools-reasoning - uv pip install -e pkg/hanzo-tools-lsp - uv pip install -e pkg/hanzo-tools-refactor - uv pip install -e pkg/hanzo-tools-database - uv pip install -e pkg/hanzo-tools-agent - uv pip install -e pkg/hanzo-tools-jupyter - uv pip install -e pkg/hanzo-tools-editor - uv pip install -e pkg/hanzo-tools-config - uv pip install -e pkg/hanzo-tools-computer - uv pip install pytest pytest-asyncio - - - name: Run tests - run: | - source .venv/bin/activate - python -m pytest pkg/hanzo-tools-core/tests/test_all_tools.py -v --tb=short - - - name: Verify tool counts - run: | - source .venv/bin/activate - python -c " - # Packages with exact expected counts - exact_packages = [ - ('hanzo_tools.fs', 1), # unified fs tool - ('hanzo_tools.browser', 1), - ('hanzo_tools.memory', 1), # unified memory tool with actions - ('hanzo_tools.todo', 1), - ('hanzo_tools.reasoning', 2), # think, critic - ('hanzo_tools.lsp', 1), - ('hanzo_tools.refactor', 1), - ('hanzo_tools.database', 9), - ('hanzo_tools.jupyter', 1), - ('hanzo_tools.editor', 3), - ('hanzo_tools.agent', 3), # agent, zen, review - ] - - # Shell tool count depends on detected shell, but core tools are fixed. - shell_required = {'ps', 'npx', 'uvx', 'open', 'curl', 'jq', 'wget'} - - total = 0 - import importlib - - # Check exact packages - for pkg_name, expected in exact_packages: - pkg = importlib.import_module(pkg_name) - tools = getattr(pkg, 'TOOLS', []) - actual = len(tools) - status = 'โœ“' if actual == expected else 'โœ—' - print(f'{status} {pkg_name}: {actual}/{expected} tools') - assert actual == expected, f'{pkg_name}: expected {expected}, got {actual}' - total += actual - - # Check shell package (detected-shell mode) - shell_pkg = importlib.import_module('hanzo_tools.shell') - shell_tools = getattr(shell_pkg, 'TOOLS', []) - shell_names = {t.name for t in shell_tools} - print(f'โœ“ hanzo_tools.shell: {len(shell_tools)} tools (detected-shell mode)') - assert len(shell_tools) >= 8, f'hanzo_tools.shell: expected >=8, got {len(shell_tools)}' - assert shell_required.issubset(shell_names), ( - f'hanzo_tools.shell: missing required tools: {shell_required - shell_names}' - ) - total += len(shell_tools) - - print(f'\\nTotal: {total} tools') - # Minimum total: exact packages (23) + shell minimum (8) = 31 - assert total >= 31, f'Expected at least 31 tools, got {total}' - print('โœ“ All tool packages verified') - " diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml deleted file mode 100644 index a4a21daa8..000000000 --- a/.github/workflows/test-windows.yml +++ /dev/null @@ -1,259 +0,0 @@ -name: Test Windows - -on: - push: - branches: [main] - paths: - - 'pkg/hanzo-mcp/**' - - 'pkg/hanzo-tools-*/**' - - 'pkg/hanzo-async/**' - - 'hanzoai/**' - - '.github/workflows/test-windows.yml' - pull_request: - branches: [main] - paths: - - 'pkg/hanzo-mcp/**' - - 'pkg/hanzo-tools-*/**' - - 'pkg/hanzo-async/**' - - 'hanzoai/**' - - '.github/workflows/test-windows.yml' - workflow_dispatch: - -jobs: - # Native Windows (no WSL) - test-windows-native: - name: Windows Native (Python ${{ matrix.python-version }}) - runs-on: windows-latest - timeout-minutes: 15 - strategy: - fail-fast: false - matrix: - python-version: ['3.12', '3.13'] - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: Install uv - uses: astral-sh/setup-uv@v3 - with: - enable-cache: true - - - name: Install core packages - shell: pwsh - run: | - uv venv - .\.venv\Scripts\Activate.ps1 - # hanzoai is the root workspace package - uv pip install -e . - # Async layer (uvloop skipped automatically on Windows) - uv pip install -e ./pkg/hanzo-async - # Test deps - uv pip install pytest pytest-asyncio pytest-cov - - - name: Test hanzoai SDK imports - shell: pwsh - run: | - .\.venv\Scripts\Activate.ps1 - python -c "from hanzoai import __version__; print(f'hanzoai {__version__}')" - python -c "import hanzo_async; print(f'uvloop active: {hanzo_async.using_uvloop()}')" - - - name: Test hanzo-async (no uvloop on Windows) - shell: pwsh - run: | - .\.venv\Scripts\Activate.ps1 - python -c " - import sys, asyncio - from hanzo_async import using_uvloop, read_file, write_file, path_exists, mkdir - assert sys.platform == 'win32' - assert not using_uvloop(), 'uvloop should not be active on Windows' - print('hanzo-async: OK (asyncio backend)') - " - - - name: Install tool packages - shell: pwsh - run: | - .\.venv\Scripts\Activate.ps1 - uv pip install -e ./pkg/hanzo-tools - uv pip install -e ./pkg/hanzo-tools-core 2>$null; $true - uv pip install -e ./pkg/hanzo-tools-fs - uv pip install -e ./pkg/hanzo-tools-shell - uv pip install -e ./pkg/hanzo-tools-reasoning - uv pip install -e ./pkg/hanzo-tools-memory - uv pip install -e ./pkg/hanzo-tools-todo - uv pip install -e ./pkg/hanzo-tools-config - - - name: Test tool imports on Windows - shell: pwsh - run: | - .\.venv\Scripts\Activate.ps1 - python -c " - import sys - assert sys.platform == 'win32' - - # Shell tools should resolve to pwsh/cmd on Windows - from hanzo_tools.shell.cmd_tool import CmdTool - tool = CmdTool() - shell = tool.default_shell - print(f'Resolved shell: {shell}') - assert 'pwsh' in shell.lower() or 'powershell' in shell.lower() or 'cmd' in shell.lower(), ( - f'Expected Windows shell, got: {shell}' - ) - - # FS tools - from hanzo_tools.fs import TOOLS as fs_tools - print(f'FS tools: {len(fs_tools)}') - - # Reasoning tools - from hanzo_tools.reasoning import TOOLS as reason_tools - print(f'Reasoning tools: {len(reason_tools)}') - - print('All tool imports OK on Windows') - " - - - name: Install hanzo-mcp - shell: pwsh - run: | - .\.venv\Scripts\Activate.ps1 - uv pip install -e ./pkg/hanzo-mcp 2>$null; $true - # Override with local tool packages - uv pip install -e ./pkg/hanzo-tools -e ./pkg/hanzo-tools-fs ` - -e ./pkg/hanzo-tools-shell -e ./pkg/hanzo-tools-memory ` - -e ./pkg/hanzo-tools-todo -e ./pkg/hanzo-tools-reasoning ` - -e ./pkg/hanzo-tools-config 2>$null; $true - - - name: Test hanzo-mcp CLI - shell: pwsh - run: | - .\.venv\Scripts\Activate.ps1 - python -m hanzo_mcp.cli --version - python -m hanzo_mcp.cli --help - - - name: Run unit tests (Windows-compatible subset) - shell: pwsh - run: | - .\.venv\Scripts\Activate.ps1 - cd pkg/hanzo-mcp - python -m pytest tests/test_llm_warnings.py -v -o "addopts=" 2>$null; $true - python -m pytest tests/test_hanzo_mcp_simple.py -v -o "addopts=" -k "test_cli_help or test_cli_version or test_import_tools" 2>$null; $true - - # WSL2 (Ubuntu on Windows) - test-wsl: - name: WSL2 Ubuntu - runs-on: windows-latest - timeout-minutes: 20 - - steps: - - uses: actions/checkout@v4 - - - uses: Vampire/setup-wsl@v5 - with: - distribution: Ubuntu-24.04 - additional-packages: python3 python3-pip python3-venv curl - - - name: Install uv in WSL - shell: wsl-bash {0} - run: | - curl -LsSf https://astral.sh/uv/install.sh | sh - echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc - - - name: Install and test in WSL - shell: wsl-bash {0} - run: | - export PATH="$HOME/.local/bin:$PATH" - WORKSPACE=$(wslpath "$(powershell.exe -Command 'Write-Host -NoNewline $env:GITHUB_WORKSPACE')") - cd "$WORKSPACE" - - uv venv - source .venv/bin/activate - - # hanzoai is the root workspace package - uv pip install -e . - uv pip install -e ./pkg/hanzo-async - uv pip install pytest pytest-asyncio - - # Test imports - python -c "from hanzoai import __version__; print(f'hanzoai {__version__}')" - python -c " - import hanzo_async - print(f'uvloop active: {hanzo_async.using_uvloop()}') - import sys - print(f'platform: {sys.platform}') - " - - - name: Install tool packages in WSL - shell: wsl-bash {0} - run: | - export PATH="$HOME/.local/bin:$PATH" - WORKSPACE=$(wslpath "$(powershell.exe -Command 'Write-Host -NoNewline $env:GITHUB_WORKSPACE')") - cd "$WORKSPACE" - source .venv/bin/activate - - uv pip install -e ./pkg/hanzo-tools - uv pip install -e ./pkg/hanzo-tools-core 2>/dev/null || true - uv pip install -e ./pkg/hanzo-tools-fs - uv pip install -e ./pkg/hanzo-tools-shell - uv pip install -e ./pkg/hanzo-tools-reasoning - uv pip install -e ./pkg/hanzo-tools-memory - uv pip install -e ./pkg/hanzo-tools-todo - uv pip install -e ./pkg/hanzo-tools-config - - - name: Test tools in WSL - shell: wsl-bash {0} - run: | - export PATH="$HOME/.local/bin:$PATH" - WORKSPACE=$(wslpath "$(powershell.exe -Command 'Write-Host -NoNewline $env:GITHUB_WORKSPACE')") - cd "$WORKSPACE" - source .venv/bin/activate - - python -c " - import sys - print(f'Platform: {sys.platform}') - assert sys.platform == 'linux', f'WSL should report linux, got {sys.platform}' - - from hanzo_tools.shell.cmd_tool import CmdTool - tool = CmdTool() - print(f'Shell: {tool.default_shell}') - # WSL has bash/zsh - assert '/bin/' in tool.default_shell or 'zsh' in tool.default_shell or 'bash' in tool.default_shell - - from hanzo_tools.fs import TOOLS as fs_tools - print(f'FS tools: {len(fs_tools)}') - from hanzo_tools.reasoning import TOOLS as reason_tools - print(f'Reasoning tools: {len(reason_tools)}') - print('All WSL tool imports OK') - " - - - name: Install and test hanzo-mcp in WSL - shell: wsl-bash {0} - run: | - export PATH="$HOME/.local/bin:$PATH" - WORKSPACE=$(wslpath "$(powershell.exe -Command 'Write-Host -NoNewline $env:GITHUB_WORKSPACE')") - cd "$WORKSPACE" - source .venv/bin/activate - - uv pip install -e ./pkg/hanzo-mcp 2>/dev/null || true - uv pip install -e ./pkg/hanzo-tools -e ./pkg/hanzo-tools-fs \ - -e ./pkg/hanzo-tools-shell -e ./pkg/hanzo-tools-memory \ - -e ./pkg/hanzo-tools-todo -e ./pkg/hanzo-tools-reasoning \ - -e ./pkg/hanzo-tools-config 2>/dev/null || true - - python -m hanzo_mcp.cli --version - python -m hanzo_mcp.cli --help - - - name: Run tests in WSL - shell: wsl-bash {0} - run: | - export PATH="$HOME/.local/bin:$PATH" - WORKSPACE=$(wslpath "$(powershell.exe -Command 'Write-Host -NoNewline $env:GITHUB_WORKSPACE')") - cd "$WORKSPACE" - source .venv/bin/activate - - cd pkg/hanzo-mcp - python -m pytest tests/test_llm_warnings.py -v -o "addopts=" 2>/dev/null || true - python -m pytest tests/test_hanzo_mcp_simple.py -v -o "addopts=" \ - -k "test_cli_help or test_cli_version or test_import_tools" 2>/dev/null || true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 55adfe471..000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Test Hanzo Python SDK - -on: - push: - branches: [ main, master ] - pull_request: - branches: [ main, master ] - workflow_call: # Allow this workflow to be called by other workflows - -jobs: - test: - runs-on: [self-hosted, linux, amd64] - strategy: - matrix: - python-version: ["3.12", "3.13"] - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest pytest-asyncio pytest-cov - cd pkg/hanzo && pip install -e . - - - name: Run tests - run: | - cd pkg/hanzo - python -m pytest tests/ -v --cov=hanzo --cov-report=term-missing - - - name: Test hanzo CLI - run: | - hanzo --version - hanzo --help - hanzo node --help - - lint: - runs-on: [self-hosted, linux, amd64] - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install ruff "black<26.1" mypy - - - name: Lint with ruff - run: | - cd pkg/hanzo - ruff check src/ - - - name: Format check with black - run: | - cd pkg/hanzo - black --check src/ - - - name: Type check with mypy - run: | - cd pkg/hanzo - pip install -e . - mypy src/ --ignore-missing-imports || true \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0e9ee307d..95ceb189a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,76 +1,15 @@ -# IDE and editor -.vscode -.idea +.prism.log +_dev -# Python -*.egg-info __pycache__ .mypy_cache -.pytest_cache -*.pyc -.venv -# Build -build/ dist -_dev -# Environment +.venv +.idea + .env .envrc - -# Logs -.prism.log codegen.log -*.log - -# Package manager Brewfile.lock.json - -# Agent config files (symlinked from user home) -LLM.md -AGENTS.md -CLAUDE.md -GEMINI.md -GROK.md -QWEN.md - -# Local databases and state -.hanzo/ -.grok/ - -# Documentation build -docs/.next/ -docs/out/ -docs/node_modules/ - -# Training data and scripts (DO NOT COMMIT) -training_dataset.jsonl -scripts/full_extractor.py -scripts/mega_extractor.py -scripts/mega_full_extractor.py -scripts/streaming_extractor.py -scripts/supplement_extractor.py - -# Test files at root (experimental) -test_post_quantum_*.py - -# Analysis documents (internal) -HANZO_INNOVATION_OPPORTUNITIES.md -POST_QUANTUM_CRYPTOGRAPHY_IMPLEMENTATION.md - -# Experimental cryptography (WIP) -pkg/hanzo/src/hanzo/cryptography/ -site/ - -# hygiene (untrack node_modules, block common build output) -node_modules/ -**/node_modules/ -.pnpm-store/ -dist/ -.next/ -coverage/ -playwright-report/ -test-results/ -tmp/ -.DS_Store diff --git a/.grok/settings.json b/.grok/settings.json deleted file mode 100644 index 1dbc7db1e..000000000 --- a/.grok/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "model": "grok-4-latest" -} \ No newline at end of file diff --git a/.hanzo/db/graph.db b/.hanzo/db/graph.db deleted file mode 100644 index 89c093cde..000000000 Binary files a/.hanzo/db/graph.db and /dev/null differ diff --git a/.hanzo/db/project.db b/.hanzo/db/project.db deleted file mode 100644 index 5a08ff28e..000000000 Binary files a/.hanzo/db/project.db and /dev/null differ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index c9d5e544f..000000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,78 +0,0 @@ -repos: - # Block TODO/STUB/FAKE patterns - - repo: local - hooks: - - id: block-todos - name: Block TODO/STUB/FAKE code - entry: bash -c 'grep -r "TODO\|STUB\|FAKE\|UNFINISHED\|NotImplementedError" --include="*.py" --exclude-dir=test . && exit 1 || exit 0' - language: system - pass_filenames: false - always_run: true - fail_fast: true - - - id: no-debug-prints - name: Block debug prints - entry: bash -c 'grep -r "print(.*#.*DEBUG\|console\.log\|debugger" --include="*.py" . && exit 1 || exit 0' - language: system - pass_filenames: false - - - id: no-empty-functions - name: Block empty functions - entry: python scripts/check_empty_functions.py - language: python - pass_filenames: false - additional_dependencies: [ast] - - # Python code quality - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.6 - hooks: - - id: ruff - args: [--fix, --exit-non-zero-on-fix] - - id: ruff-format - - # Type checking - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.14.1 - hooks: - - id: mypy - args: [--ignore-missing-imports, --strict] - additional_dependencies: [types-all] - - # Security scanning - - repo: https://github.com/PyCQA/bandit - rev: '1.8.0' - hooks: - - id: bandit - args: [-r, --skip, B101] - - # Standard pre-commit hooks - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml - - id: check-added-large-files - args: ['--maxkb=500'] - - id: check-merge-conflict - - id: check-ast - - id: debug-statements - - id: detect-private-key - - # Prevent direct commits to main - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 - hooks: - - id: no-commit-to-branch - args: [--branch, main, --branch, master] - - # Run tests before commit - - repo: local - hooks: - - id: pytest-check - name: Run pytest - entry: bash -c 'cd pkg/hanzo-mcp && python -m pytest tests/test_no_stubs.py -x' - language: system - pass_filenames: false - stages: [commit] \ No newline at end of file diff --git a/.python-version b/.python-version index be71774ed..43077b246 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.12.9 \ No newline at end of file +3.9.18 diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 000000000..656a2ef17 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "2.1.0" +} \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 53107a1be..310f61035 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 188 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/hanzo-industries-inc%2FHanzo-AI-ec4be99f95dc46e9442eb60f233b2bff271d6f5bf57d7c61a52bc4804f55bbd1.yml -openapi_spec_hash: 87bc62c36bb6028ffd1f3e54a2809099 -config_hash: 830747463ff4d018b5633ce511e88558 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/hanzo-industries-inc%2Fhanzo-ai-10a8fe872c67396add3ebc3a10252df5bcd6d0f4e9a255d923875a806b2f1609.yml +openapi_spec_hash: 495ad4b04c4dd929e9566b6a099ff931 +config_hash: e927bafd76a1eace11894efc3517d245 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..5b0103078 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.analysis.importFormat": "relative", +} diff --git a/CHANGELOG.md b/CHANGELOG.md index c9d05e98d..acdfddf90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,285 @@ # Changelog -## 2.2.1 (2026-06-10) +## 2.1.0 (2026-07-03) + +Full Changelog: [v2.0.2...v2.1.0](https://github.com/hanzoai/python-sdk/compare/v2.0.2...v2.1.0) + +### Features + +* add 'hanzo infra' CLI for cloud infrastructure provisioning ([fe2abac](https://github.com/hanzoai/python-sdk/commit/fe2abacce9bcbcd8324bea412587d06a672cafdd)) +* add cloud CLI commands ([83599fa](https://github.com/hanzoai/python-sdk/commit/83599fa75344b6ad7fe862a9d2e7010416b986c1)) +* add CloudClient and rewrite wire protocol for Rust compatibility ([99aa65a](https://github.com/hanzoai/python-sdk/commit/99aa65a30a3741cf0e3e29817f1102de9884e617)) +* add hanzo-agents package with CLI ([6e06785](https://github.com/hanzoai/python-sdk/commit/6e06785db8f761d87e14d99c646fd36c9e9d6e5c)) +* add hanzo-async unified async I/O with uvloop support ([5348dc4](https://github.com/hanzoai/python-sdk/commit/5348dc49f9a8bea21b7690252535d5e10273262a)) +* add hanzo-cli unified CLI with IAM, KMS, PaaS management ([eb2f0a1](https://github.com/hanzoai/python-sdk/commit/eb2f0a1aee4e4b58bc33e6b882e8b589f05cf11c)) +* add hanzo-flow package (re-exports hanzoflow CLI as hanzo-flow) ([d07898d](https://github.com/hanzoai/python-sdk/commit/d07898d9af0b422fae35b048533056ca932c4d39)) +* add hanzo-kms package, MCP proxy tools, and agent reflexion ([98aa8a0](https://github.com/hanzoai/python-sdk/commit/98aa8a0b9485934af9d5241e546d2fd69f496bf5)) +* add hanzo-node cross-platform binary installer ([91e4b60](https://github.com/hanzoai/python-sdk/commit/91e4b6057e038867ad6774568a72d203f70c4be1)) +* add hanzo-tools-api package and hanzo.infra SDK ([f73a84e](https://github.com/hanzoai/python-sdk/commit/f73a84eb4578e0f2e303207f39f9f73596f8b509)) +* add HIP-0300 cross-language MCP conformance test suite ([2e556a7](https://github.com/hanzoai/python-sdk/commit/2e556a7b21e349e7f72faf929a0bc8dfadcba032)) +* add new tool packages and install command ([0230656](https://github.com/hanzoai/python-sdk/commit/02306564271bba056b83433d9a324d899289c638)) +* add RAG search via Hanzo Cloud (search-docs + chat-docs) ([d2d1efc](https://github.com/hanzoai/python-sdk/commit/d2d1efc2af4d6c1e087a49062f24ef7be32ee5ef)) +* add REPL, IDE tools and ZAP protocol ([a6741fc](https://github.com/hanzoai/python-sdk/commit/a6741fcbe1d0f018966903703a725e11d6207ad6)) +* add UI registry server and tiered backend (local โ†’ registry โ†’ github) ([f63053f](https://github.com/hanzoai/python-sdk/commit/f63053fc822ee1f7f9d98302dfcf2663d7d87c1b)) +* **agent:** add direct API mode and auto-backgrounding ([34539d7](https://github.com/hanzoai/python-sdk/commit/34539d718ab8dc392af41f7269a4538275f7cf16)) +* **agent:** agentic proxy with Claude Code integration ([2367201](https://github.com/hanzoai/python-sdk/commit/2367201035c4ca18947f9e74699bdedda1a6e0ac)) +* **agent:** multi-agent orchestration with DAG, swarm, Lux Quasar consensus ([75702ab](https://github.com/hanzoai/python-sdk/commit/75702abb6cdc0aa780dd988cdb039edb29c8de06)) +* **agent:** one tool with actions, not many tools ([4e9182f](https://github.com/hanzoai/python-sdk/commit/4e9182fb099055a1df5dd2ad1e4d341179a501eb)) +* auth/IAM refactor + add hanzo-iam and hanzo-web3 packages ([eeb2c09](https://github.com/hanzoai/python-sdk/commit/eeb2c09c06e66add9ceea7a2abd90f7ca25a8f8a)) +* auto-install uvloop on Linux/macOS with platform gate ([3ea63d9](https://github.com/hanzoai/python-sdk/commit/3ea63d9e7c3b63b7fb0d98ecd13d521c78b1f808)) +* bring hanzo-agent into monorepo tree ([c9b06e9](https://github.com/hanzoai/python-sdk/commit/c9b06e9241258e328ca79af1ed68c920634226f5)) +* **browser:** add CDP bridge server for extension integration ([baa6954](https://github.com/hanzoai/python-sdk/commit/baa695411da9c91fa08328ab1d569029fdad79b4)) +* **browser:** add navigation/fetch/history to extension actions ([4195b13](https://github.com/hanzoai/python-sdk/commit/4195b13bc3ca23ac25fc4a436855d85f20fc41cb)) +* **browser:** add wait_for_selector, query_selector_all to extension actions ([2e8d105](https://github.com/hanzoai/python-sdk/commit/2e8d105f0ae46dc9bca5ea49be3ed2bb492d8c06)) +* **browser:** configurable backend + fix CDP bridge for websockets >= 13 ([80c8b6e](https://github.com/hanzoai/python-sdk/commit/80c8b6e6a6d98fabe3337483543fd147f25aeedb)) +* bump versions and add hanzo-cli/kms/iam to publish workflow ([6912742](https://github.com/hanzoai/python-sdk/commit/69127423d52fb0c32cca2454ccc15d00a0aea8c0)) +* **cli:** add bot commands, s3 storage, dev/net passthrough ([6a4fc31](https://github.com/hanzoai/python-sdk/commit/6a4fc31a77d2d0141787691a45ba1bfc6108c10d)) +* **cli:** add k8s kubectl wrapper and remove REPL ([57853cf](https://github.com/hanzoai/python-sdk/commit/57853cf9de00dd6b873eebeb48802a58fffeacdb)) +* **cli:** add top-level deploy alias and fix KMS create_secret ([af06f92](https://github.com/hanzoai/python-sdk/commit/af06f92acd18a68f19084dd5fe3b5f4418e1f20c)) +* **cli:** replace all 18 stub commands with real API implementations ([fe20394](https://github.com/hanzoai/python-sdk/commit/fe20394fd9219f89172c65fb8af92d85ed80c079)) +* **computer:** add intelligent video slicing with activity detection ([9fc460e](https://github.com/hanzoai/python-sdk/commit/9fc460ed6de597afca31267944e409544c97656b)) +* **computer:** add MediaTool with configurable limits (100 images, 32MB) ([2ea8185](https://github.com/hanzoai/python-sdk/commit/2ea81853631fa56b681dc9fcc6fd215daec7cf55)) +* **computer:** add ScreenTool for unified screen recording and Claude interpretation ([bc55675](https://github.com/hanzoai/python-sdk/commit/bc556754ac16603f08e2677b53c57fef2188803c)) +* configurable HANZO_AUTO_BACKGROUND_TIMEOUT env var (default: 30s) ([aa2e887](https://github.com/hanzoai/python-sdk/commit/aa2e887c9686bdc5320a142112870a492f398e2c)) +* **consensus:** add MCP mesh for agent-to-agent consensus ([5d93c8e](https://github.com/hanzoai/python-sdk/commit/5d93c8e4574611e71e70b8856e74a972c51e0852)) +* consolidate tools - 52 to 30 tools ([38f34c1](https://github.com/hanzoai/python-sdk/commit/38f34c1c9ee27034cf38f0fe1223f9fbd7a0a1ed)) +* **dns:** add multi-provider DNS CLI ([3409410](https://github.com/hanzoai/python-sdk/commit/3409410f9aece829383cddebfc9d439095151ec6)) +* **docs:** add Cmd+K search with dark modal styling ([e408726](https://github.com/hanzoai/python-sdk/commit/e4087264167e7c7ec788c5d7790e972d1f77d512)) +* enable ui tool in hanzo-mcp with local-first component reading ([595036d](https://github.com/hanzoai/python-sdk/commit/595036dd1c7996c1db7f4d88534d6bb9cb0dfab7)) +* extract hanzo-metastable-consensus package ([8f8ec4e](https://github.com/hanzoai/python-sdk/commit/8f8ec4e17d99b890eaa23662633bc2ea88734532)) +* HANZO_AUTO_BACKGROUND_TIMEOUT=0 disables auto-backgrounding ([5bd4ab2](https://github.com/hanzoai/python-sdk/commit/5bd4ab29c9579c7900cfa537fe66dedf5b865644)) +* **hanzo-cli:** add bot node agent commands with browser OAuth login ([f57fd2f](https://github.com/hanzoai/python-sdk/commit/f57fd2f531b69bd961d0b185f7d5849f35bb0115)) +* **hanzo-cli:** add native hanzo.toml support for PaaS deployments ([a77e89a](https://github.com/hanzoai/python-sdk/commit/a77e89a8c26970f486ca2df59aa3e0605a9c56cc)) +* hanzo-dev Python TUI + LSP + hooks + sandbox + vim + git + multi-auth + full parity ([041dcce](https://github.com/hanzoai/python-sdk/commit/041dcce5cf87394d27beee7f6e067a05194c0de3)) +* **hanzo-iam:** canonical IAM_ env contract + FastAPI integration (1.30.0) ([6cac1b6](https://github.com/hanzoai/python-sdk/commit/6cac1b64a458dc623ec617683d9b6192d3f8754d)) +* **hanzo-mcp:** 100% MCP/ZAP protocol parity with method pass-through v0.14.0 ([9f9504f](https://github.com/hanzoai/python-sdk/commit/9f9504fa5733f5b848711b38cb39104a4d4aa4cd)) +* **hanzo-mcp:** unified tool command and essential tools system ([f9a43e5](https://github.com/hanzoai/python-sdk/commit/f9a43e52ff76dd3e0da6f6dc5447b29e6b89c68e)) +* **hanzo-mcp:** v0.10.18 - add hanzo-tools-* and hanzo-persona as required dependencies ([1eadea4](https://github.com/hanzoai/python-sdk/commit/1eadea41f4cdbed88812a988d394027bcf11a4cd)) +* **hanzo-tasks:** Python SDK for durable task execution ([75108b7](https://github.com/hanzoai/python-sdk/commit/75108b74fcb0c55b7d68d1364638fcc9ac0056f5)) +* **hanzo-zap:** add ZAP SDK package to monorepo (0.6.1) ([5e49b8f](https://github.com/hanzoai/python-sdk/commit/5e49b8f25e21e32ef2ff8e6db03cbf2c54db137a)) +* **iam:** add set-password and enforce-hashing CLI commands ([1728d3a](https://github.com/hanzoai/python-sdk/commit/1728d3af33cc899de0fa745a594059e11e1e4dc8)) +* **iam:** add set-password and enforce-hashing CLI commands ([#27](https://github.com/hanzoai/python-sdk/issues/27)) ([f476b27](https://github.com/hanzoai/python-sdk/commit/f476b276ee27d72994873e184839a2786effcfb4)) +* **llm:** use hanzo-metastable-consensus for consensus tool ([70ffe7b](https://github.com/hanzoai/python-sdk/commit/70ffe7b6c90e6c16962f2ae93768f0ee94c0f269)) +* **mcp:** add auth, kms, and platform MCP tools ([34554a5](https://github.com/hanzoai/python-sdk/commit/34554a55cc4052a86568f9602a10287aa4f65032)) +* **mcp:** consolidate memory surface and restore local tool compatibility ([8297746](https://github.com/hanzoai/python-sdk/commit/82977464075697fb51ea790d07aae157959b2b5d)) +* **mcp:** register code, git, fetch tools for HIP-0300 parity with TS MCP ([27ec75f](https://github.com/hanzoai/python-sdk/commit/27ec75f0013d225b72441ffdac18857983260afd)) +* **mcp:** unify tool surfaces, rename ichingโ†’zen, fix test infrastructure ([810ff41](https://github.com/hanzoai/python-sdk/commit/810ff414693b8829a69b5a5d656ce7cdb5ad9fd4)) +* **memory:** add namespace/key support + BlueRedChannel coordination class ([6913819](https://github.com/hanzoai/python-sdk/commit/6913819ced0937f74607443a94a384943a7e9a97)) +* **memory:** add no-backend markdown fallback for all memory MCP tools ([38c0060](https://github.com/hanzoai/python-sdk/commit/38c0060c73afc15b006fcbdd97e83fb192274d86)) +* **memory:** consolidate 9 memory tools โ†’ single unified memory tool ([ae79e88](https://github.com/hanzoai/python-sdk/commit/ae79e88aac0034ee8fd14972fc431962130d7d8a)) +* refactor cloud CLI to gcloud idioms ([879e8c3](https://github.com/hanzoai/python-sdk/commit/879e8c36ec4fbffed3b82380cc85f473147deb9a)) +* rename 'hanzo infra' CLI to 'hanzo cloud' ([c05f03c](https://github.com/hanzoai/python-sdk/commit/c05f03cd16fb2fac79d90d9f74d19b87cca6e3dc)) +* rename PostHog analytics -> Insights in hanzo-mcp ([fbea730](https://github.com/hanzoai/python-sdk/commit/fbea73035b91035070a4b9bfea58c2cf6f14c871)) +* **sdk:** add datastore, docdb, ingress, mpc, paas resource modules ([c3faf8b](https://github.com/hanzoai/python-sdk/commit/c3faf8bd8a094115da8da5b538a111d48a55f205)) +* **shell:** add bash tool and shell parameter for runtime switching ([9413226](https://github.com/hanzoai/python-sdk/commit/9413226531a3f178df564d1a89210f72d0aaafd4)) +* **shell:** add curl, jq, wget tools for shell escaping-free operations ([73adf3e](https://github.com/hanzoai/python-sdk/commit/73adf3ec522669e92560d0316ae8fa88267a2391)) +* **shell:** add shell detection to only expose user's active shell ([f087e0e](https://github.com/hanzoai/python-sdk/commit/f087e0e1d026234047b7c89338f000e10d5311b0)) +* **shell:** add Shellflow DSL for inline DAG syntax ([9d9f818](https://github.com/hanzoai/python-sdk/commit/9d9f818379ce6883989ff43e1221c6ef3894fe15)) +* **shell:** nested arrays auto-parallel in DAG execution ([4703ec9](https://github.com/hanzoai/python-sdk/commit/4703ec99c54320981624642a1d3dc18bdd85cdbd)) +* **shell:** remove cmd, add ksh/tcsh/csh support, shell-first exposure ([f14618f](https://github.com/hanzoai/python-sdk/commit/f14618f851cb6332404cdf6a16425dc6d02f10e8)) +* **shell:** shellflow DSL parser with high-performance optimization ([c28f8c9](https://github.com/hanzoai/python-sdk/commit/c28f8c99090dfbe782e16417ef841c624caeeaa9)) +* **tools-browser:** add tab_id / client_id / target_browser params ([8e648ff](https://github.com/hanzoai/python-sdk/commit/8e648ffa3f5d19797c35b70c68de5ef9c7d62373)) +* **tools-browser:** set_default_browser / use_browser actions for v1.9.0 bridge ([77d03f4](https://github.com/hanzoai/python-sdk/commit/77d03f49131dc1fee0e00e134ba4793439b4095c)) +* **tools-browser:** ZAP-native 2-process architecture (0.5.0) ([4ab1296](https://github.com/hanzoai/python-sdk/commit/4ab129602381942850b41b2b1c7003509c00e73f)) +* **tools:** add Rust<>Python parity for plan, patch, wait tools ([4a1245e](https://github.com/hanzoai/python-sdk/commit/4a1245e4a95c345bbc4cf24a10d5203664a44586)) +* **tools:** bidirectional HIP-0300 action sync across Python/TS/Rust ([d410ddc](https://github.com/hanzoai/python-sdk/commit/d410ddcb3ab738a3876275f9019bab6355bb3db0)) +* **tools:** fix BaseTool dispatch + achieve 100% HIP-0300 conformance ([64881d7](https://github.com/hanzoai/python-sdk/commit/64881d78844404060c698cc3b6594692f846397e)) +* wire CLI to PaaS API with IAM session exchange ([263b4e2](https://github.com/hanzoai/python-sdk/commit/263b4e29b30b00f8b7b5a83a2083eeaff5a58b80)) +* **zap-server:** auto-publish via hanzo-zap-mdns + retract on stop ([6a3595e](https://github.com/hanzoai/python-sdk/commit/6a3595e1068eb82a710f225a8fedd026679e70d8)) +* **zap:** add ZAP WebSocket server for browser extension discovery ([8d5fad3](https://github.com/hanzoai/python-sdk/commit/8d5fad34e8d01cf16234672ae9ef3f5b752c4ed8)) + ### Bug Fixes -* ship `hanzoai.protocols` (in-tree since 2026-03-31 but never released) โ€” `hanzo-mcp` imports it at startup, so installs resolving `hanzoai==2.2.0` crashed with `ModuleNotFoundError` before answering MCP `initialize` +* **api:** add User-Agent header to APIs.guru fetch to fix CI 403 ([f4d7af8](https://github.com/hanzoai/python-sdk/commit/f4d7af8128c63d9068bdb0910956cc29bdceea77)) +* async audit improvements and test fixes ([24ae6dc](https://github.com/hanzoai/python-sdk/commit/24ae6dc1b0f9048fad8cb699a0b609db854b092d)) +* bare except clauses and SQL injection vulnerability ([206ea68](https://github.com/hanzoai/python-sdk/commit/206ea683712b8a92fc9b993ae7dc69c7d326aadf)) +* bare except in autonomous_bug_solver example ([71bcae0](https://github.com/hanzoai/python-sdk/commit/71bcae06fefb3c5f60dbfaba7791114460965e35)) +* **bot:** use OPENCLAW_GATEWAY_TOKEN and write bot.json config ([30ec6b6](https://github.com/hanzoai/python-sdk/commit/30ec6b69313d3677e4a86b765e3cd562f47c6d3e)) +* **browser:** add missing action-to-method mappings in CDP bridge ([3b24a68](https://github.com/hanzoai/python-sdk/commit/3b24a68a9da559ac9ebaefe5c7dfa680dce17de4)) +* **browser:** auto-start CDP bridge on BrowserTool init ([2e5b1cd](https://github.com/hanzoai/python-sdk/commit/2e5b1cd33cfdbbd60f643c6b0f2c960d68860973)) +* **browser:** fix extension result passthrough, add DOM actions ([f4d402f](https://github.com/hanzoai/python-sdk/commit/f4d402fff6adfe50001c7986d35347ad8b75b082)) +* **browser:** unwrap CDP Runtime.evaluate results for consistency ([b330d9a](https://github.com/hanzoai/python-sdk/commit/b330d9a7237f1dd9bab0107313d937f821f46bb3)) +* **ci:** add hanzo-tools-ui to pyright exclude list ([6ef7293](https://github.com/hanzoai/python-sdk/commit/6ef7293a6fa96e7640537b89f13532843f0cc64b)) +* **ci:** align test expectations with tool renames, fix ruff lint/format ([5196cb6](https://github.com/hanzoai/python-sdk/commit/5196cb6309afe29aa6f2f83da168e8668bdbe9af)) +* **ci:** allow publish even when lint fails (pre-existing errors) ([c8127f5](https://github.com/hanzoai/python-sdk/commit/c8127f51aa908e373980df603cb9a90ba351ef71)) +* **ci:** apply black formatting and bump hanzo-mcp to 0.12.3 ([b2b4b6c](https://github.com/hanzoai/python-sdk/commit/b2b4b6cc1fa14147b0e3a046619ecea3d013f999)) +* **ci:** apply black formatting to base.py and enhanced_repl.py ([9904e69](https://github.com/hanzoai/python-sdk/commit/9904e6950d6c29c24e8a1e56a295d86818636be6)) +* **ci:** build packages from repo root to avoid stdlib shadowing ([1651406](https://github.com/hanzoai/python-sdk/commit/165140624f5f385eb4983825366a6d590e26532b)) +* **ci:** change pyright from strict to basic mode ([143f24d](https://github.com/hanzoai/python-sdk/commit/143f24d6c795dc9c6788fe6830ec9c61446c51b9)) +* **ci:** clean lint/format and bump hanzo-mcp to 0.12.2 ([487ef2b](https://github.com/hanzoai/python-sdk/commit/487ef2bbf3d7b1604fbf1767b8019e1e849c5a4d)) +* **ci:** drop --no-deps from local tool installs to resolve psutil ([ffb3f5b](https://github.com/hanzoai/python-sdk/commit/ffb3f5b700b99de8701642b06d6f696a58ee3fbc)) +* **ci:** exclude all subpackages from pyright scope ([8308d45](https://github.com/hanzoai/python-sdk/commit/8308d45b1fb997c9ed922dbff45cf02fdd42d1bc)) +* **ci:** fix Windows test workflow - hanzoai installs from root, not pkg/ ([b4f1259](https://github.com/hanzoai/python-sdk/commit/b4f12590e9ade07be83fe764ef5188173c30d9e3)) +* **ci:** handle hanzoai root package in publish workflow ([945b384](https://github.com/hanzoai/python-sdk/commit/945b384067c109bd7f3352d4eff99f7853b25dae)) +* **ci:** ignore hanzo-tools-core deprecation warning in pytest ([36a8b91](https://github.com/hanzoai/python-sdk/commit/36a8b9143a0f4a2ba8a588de275b57381233a78d)) +* **ci:** install hanzo-tools shim before hanzo-tools-core in test workflow ([1676a3b](https://github.com/hanzoai/python-sdk/commit/1676a3bbc2b9e658c684c8c89d223a0822be2ea4)) +* **ci:** install libatomic1 for pyright on ARC runner ([d104a15](https://github.com/hanzoai/python-sdk/commit/d104a15528518c0ab29b3aa18b702e9bbb8e2920)) +* **ci:** install local hanzo-tools-agent in release-check venv ([2721752](https://github.com/hanzoai/python-sdk/commit/2721752870487ddbd478ed9e855f7af22dcccdc0)) +* **ci:** install local tool packages in CI to match monorepo renames ([4bb3ddd](https://github.com/hanzoai/python-sdk/commit/4bb3ddd3097941c337556e26ab46f3f74fbe92e3)) +* **ci:** pin black <26.1 to avoid internal error, lower requires-python to 3.12 ([6880821](https://github.com/hanzoai/python-sdk/commit/6880821c0f8c5166beca227d3234240f93d9f985)) +* **ci:** relax config tool count, install hanzo-tools shim in test matrix ([d018cff](https://github.com/hanzoai/python-sdk/commit/d018cff929e0619bf6f7bd6dca912c82ebc1ecbe)) +* **ci:** remove PYTEST_DISABLE_PLUGIN_AUTOLOAD, override addopts in CI ([1712518](https://github.com/hanzoai/python-sdk/commit/17125184c119e447a6120a6b521527ad54a8e0d6)) +* **ci:** resolve root lint failures across workspace packages ([f965b69](https://github.com/hanzoai/python-sdk/commit/f965b69e475978ccd29b2d845ae1db91a98833f2)) +* **ci:** sort imports in 4 test files (ruff I001) ([835472a](https://github.com/hanzoai/python-sdk/commit/835472a5b2ce87972adaa449e64b6a75c0aafb6a)) +* **ci:** update workflow to use hanzo_tools.agent imports ([10bcd16](https://github.com/hanzoai/python-sdk/commit/10bcd165189a45b2a1e6bed83dc36206985c0862)) +* **ci:** update workflow tool counts ([1416f10](https://github.com/hanzoai/python-sdk/commit/1416f10e3861ba86cf6cec61bdd4426d64ddf0f5)) +* **cli:** refactor PaaS auth flow and fix container deploy payload ([bf7b1f3](https://github.com/hanzoai/python-sdk/commit/bf7b1f3c34140c629ed71c34654ac42e45b24dd7)) +* **cli:** sort imports to satisfy ruff I001 lint rule ([3b338de](https://github.com/hanzoai/python-sdk/commit/3b338de5b1167e461bf4900c40c28ed63d427dc0)) +* **computer:** correct docstring to show 2000px Claude limit ([066ec4e](https://github.com/hanzoai/python-sdk/commit/066ec4ea90da00f447da13ebaa6dfe2227bacbc2)) +* **computer:** use Claude's actual 2000px limit for multi-image ([5805c58](https://github.com/hanzoai/python-sdk/commit/5805c5836a119c50b4b24dfc4d96ab28d2f7688a)) +* **core:** filter deprecation warning in tests ([084718a](https://github.com/hanzoai/python-sdk/commit/084718a777250734c539ca7c3831a75f284c4769)) +* **core:** import MCPResourceDocument from types.py, not base.py ([22b21c9](https://github.com/hanzoai/python-sdk/commit/22b21c97f39a2eb296e9943f8195399ec52cd8de)) +* **core:** remove ToolCategory from exports (it's in hanzo-mcp, not hanzo-tools-core) ([84541d8](https://github.com/hanzoai/python-sdk/commit/84541d8dcd2eb975976e4f572225513fb9cfca78)) +* **core:** update memory tool count (1 unified tool, not 9) ([4c27562](https://github.com/hanzoai/python-sdk/commit/4c275621b114064bc79f735feb19113d23833059)) +* **core:** update test module names and tool counts ([2b18b3b](https://github.com/hanzoai/python-sdk/commit/2b18b3b393f64d267ab8417691f43334130e0abe)) +* CTO review โ€” version targets, slash command parity, dataclass default ([37d3d8d](https://github.com/hanzoai/python-sdk/commit/37d3d8d180f64290c8b1c26f733f9a699c4fe5b6)) +* **curl:** accept json parameter in MCP tool registration ([dfc19d9](https://github.com/hanzoai/python-sdk/commit/dfc19d949ec9677de57ed8a37cdfca74abb07be8)) +* depend on hanzo-flow (hyphenated PyPI name) ([af770df](https://github.com/hanzoai/python-sdk/commit/af770dff25582161f5a8448c5776d39aa4c821db)) +* **docs:** add .gitkeep to overrides directory for GitHub Actions ([a692e83](https://github.com/hanzoai/python-sdk/commit/a692e8306258fcaaa5cce4eb310099bf01fec99b)) +* **docs:** checkout submodules for docs build ([ae62ebd](https://github.com/hanzoai/python-sdk/commit/ae62ebd284417e2f2d77bbb6b8fe5608ba4b0162)) +* **docs:** correct auto-background timeout from 45s to 30s ([83958e6](https://github.com/hanzoai/python-sdk/commit/83958e657c4df654b2636026147c25a7fd84798f)) +* **docs:** improve header tab contrast and visibility ([c2343f2](https://github.com/hanzoai/python-sdk/commit/c2343f2450e5699a7d0d6fc91d6d613fe6c3c84b)) +* **docs:** install package for API reference generation ([73c5db0](https://github.com/hanzoai/python-sdk/commit/73c5db0e6e776b45bdb810a6cd2d2a97025c4e34)) +* **docs:** pin mkdocstrings version for compatibility ([ae342dd](https://github.com/hanzoai/python-sdk/commit/ae342dd30e36af7621f037afd75ef25a0269ffb4)) +* **docs:** remove blue links, fix search box dark styling ([67954dd](https://github.com/hanzoai/python-sdk/commit/67954dd593a6ef023308fe0bc32f56a32c657a18)) +* **docs:** rename llm.md to llm-tools.md to avoid gitignore ([26c8b98](https://github.com/hanzoai/python-sdk/commit/26c8b98bb328378025791fb9db70504c7961174f)) +* eliminate remaining TODOs and stubs in source code ([19c55a9](https://github.com/hanzoai/python-sdk/commit/19c55a9071b626dca53c2bc820c6f012f2593c7b)) +* finalize latest MCP/CLI/SDK updates ([862e093](https://github.com/hanzoai/python-sdk/commit/862e093de569e1f66bf6072e88f2888215381ade)) +* flow CLI binary is 'flow' not 'hanzo-flow' ([682644a](https://github.com/hanzoai/python-sdk/commit/682644a19abb80791c73d409c7b139ab52383439)) +* **hanzo-mcp:** add missing mypy type annotations to pass CI ([bb97ddf](https://github.com/hanzoai/python-sdk/commit/bb97ddf0d61ed9883a7c13c646e8746466313d6b)) +* **hanzo-mcp:** add serve subcommand support for Claude Code ([a2893f6](https://github.com/hanzoai/python-sdk/commit/a2893f6da690db628c85e33f7f5ab7b7a0df8ae6)) +* **hanzo-mcp:** update fallback version to 0.14.0, add watchdog to dev deps ([5d62abf](https://github.com/hanzoai/python-sdk/commit/5d62abfe7370c7ed5e2c6a1ae6f53c061eefbb21)) +* **hanzo-memory:** fix tests and clean up deprecated code ([d748b5b](https://github.com/hanzoai/python-sdk/commit/d748b5b407f157b0c5e7329a812f2a677b812a4b)) +* **hanzo-tools-browser:** 0.5.2 โ€” import wire format from zap-protocol ([129360b](https://github.com/hanzoai/python-sdk/commit/129360b7ec65063e236311467f7e83c455a927ff)) +* **imports:** update filesystem to fs in agent tools ([eda66d0](https://github.com/hanzoai/python-sdk/commit/eda66d0cd1b094fb4c7782cf7a4908cb99566d75)) +* lint โ€” ruff format and fix for hanzo-tools-ui and hanzo-mcp ([7fad40b](https://github.com/hanzoai/python-sdk/commit/7fad40b75c47b9003b905aebcb9379e216ba237e)) +* lint and format issues ([3b8da2f](https://github.com/hanzoai/python-sdk/commit/3b8da2f5a380b18a027dc012cc61259147c294ab)) +* **lint:** add strict=False to zip() in using_grok example ([0996212](https://github.com/hanzoai/python-sdk/commit/09962122463bceb6c32900b95982072489a5294b)) +* **lint:** fix import sorting and lru_cache noqa ([0b91b3d](https://github.com/hanzoai/python-sdk/commit/0b91b3d4bd6ec8026b7ed9572efb9996ec0481c6)) +* **lint:** format hanzo-mcp tests with ruff ([462d542](https://github.com/hanzoai/python-sdk/commit/462d542cae23ce7f1581d1aa30e6126ea0d2e60e)) +* **lint:** resolve all ruff + black errors across all packages ([47b743f](https://github.com/hanzoai/python-sdk/commit/47b743f2f44d08feadf290ccfc6b7ce495675d9b)) +* **lint:** resolve all ruff E722, S310, S202, S103, B905 errors ([178cf71](https://github.com/hanzoai/python-sdk/commit/178cf71c3712d430e3e713199efab3ca21db21c2)) +* **lint:** run black formatter on all 69 unformatted files ([0ed51d1](https://github.com/hanzoai/python-sdk/commit/0ed51d18b70e112dea4ff0bbac750f7887736318)) +* **mcp:** correct import in litellm warning test ([f2e9856](https://github.com/hanzoai/python-sdk/commit/f2e985633ad9521339c7ee995a797154ef548a4c)) +* **mcp:** inject hanzo-mcp version into MCP server metadata ([94fadc3](https://github.com/hanzoai/python-sdk/commit/94fadc39f25eb447b416f6357d7faeb47b8b07a3)) +* **mcp:** lower shell dep to 0.2.0 to unblock CI (PyPI has 0.2.0) ([7e04fc3](https://github.com/hanzoai/python-sdk/commit/7e04fc3b14de6848dcfe61ad79efef4a89ba7d41)) +* **mcp:** simplify agent tests to match actual API ([8e107b4](https://github.com/hanzoai/python-sdk/commit/8e107b4009f49fedc1d42301942a5b7b27254d68)) +* **mcp:** update tests to use AgentTool instead of UnifiedAgentTool ([f98ed61](https://github.com/hanzoai/python-sdk/commit/f98ed61a6a302e44fd6465f37cdfc61ae6eba543)) +* move hanzo-cli/kms to optional deps to unblock publish ([927c67b](https://github.com/hanzoai/python-sdk/commit/927c67b29e7a5d4fd95add9675aeabbac9c6e597)) +* registry client uses static JSON paths for CF Pages compatibility ([84177d6](https://github.com/hanzoai/python-sdk/commit/84177d6d8561d2d6925d442bb7bf1e0070da1be5)) +* remove conflicting hanzo-mcp script from hanzo package ([62d81f2](https://github.com/hanzoai/python-sdk/commit/62d81f2c033cd5e96d1cf39590025bb30de9dbe3)) +* replace stubs with real implementations ([48e9ee3](https://github.com/hanzoai/python-sdk/commit/48e9ee3b8efd93b1db689b18009115b56a27c302)) +* resolve all I001 import sorting errors across monorepo ([1554cd6](https://github.com/hanzoai/python-sdk/commit/1554cd693faf7ff64cd3ff67837443c0f2bec909)) +* resolve all ruff lint errors (F401, F841, E741, I001) ([4a7637d](https://github.com/hanzoai/python-sdk/commit/4a7637d8b823b31841c46c36273c6ad705c28365)) +* resolve mypy errors and update shell tool count in tests ([e57963a](https://github.com/hanzoai/python-sdk/commit/e57963af62c241b2c94dd0879b2740289cacab65)) +* restore 'version = ' prefix in 7 axis pyproject.toml files ([5b84f5c](https://github.com/hanzoai/python-sdk/commit/5b84f5c2a6779c8513f117c66f41428237ea0206)) +* ruff format MCP files + update required tool count to 21 ([6aa68d1](https://github.com/hanzoai/python-sdk/commit/6aa68d1d90221d3b59c36ddb97f94a3631e11319)) +* ruff lint auto-fixes across hanzo-mcp and tests ([d9d3b86](https://github.com/hanzoai/python-sdk/commit/d9d3b86ce648ef2d561d0095c8bcc77535671b5c)) +* ruff lint errors across hanzo-tools-* packages (I001, F401, F541) ([d4d07a8](https://github.com/hanzoai/python-sdk/commit/d4d07a81113bbeb9beb38efb89081a408ac247ea)) +* **screen:** write captures to file instead of returning inline base64 ([c23bb92](https://github.com/hanzoai/python-sdk/commit/c23bb92082f188cb6ec3b96e0604bc80d67919f5)) +* **sdk:** update paas resource module ([1f9460f](https://github.com/hanzoai/python-sdk/commit/1f9460fd944566f33721397de5286846bd50b89e)) +* **test-hanzo-tools:** align tool expectations and modernize websockets import ([fb67143](https://github.com/hanzoai/python-sdk/commit/fb67143f5661880661af74c1cc099435ade1d9f8)) +* update agent tests for unified tools ([a4aa420](https://github.com/hanzoai/python-sdk/commit/a4aa420302249b8278da09778850c29e18ce4967)) +* update hanzo-flow entry point to flow.launcher (internal rename) ([2d123ca](https://github.com/hanzoai/python-sdk/commit/2d123caf5feb5e81f1470b614c962ef61f2094cb)) +* Windows + WSL compatibility across python-sdk ([5379b70](https://github.com/hanzoai/python-sdk/commit/5379b70d64e5fa3daa10a2278d6358bd2cfc783d)) +* Windows/WSL compatibility for shell tools and CLI helpers ([682ad02](https://github.com/hanzoai/python-sdk/commit/682ad02bf53945b896d158b05b913a26f22bc9b9)) +* **zap-server:** race-free client cleanup when client_id is reused ([9affd39](https://github.com/hanzoai/python-sdk/commit/9affd395bce06eb5a8d5d8c803c968fcb27485de)) +* **zap:** dual-protocol decode, fix tool extraction, persist auth token ([e78af14](https://github.com/hanzoai/python-sdk/commit/e78af1447451ee107ba594af9493542d17c663d3)) +* **zap:** replace websockets with native ZAP TCP transport ([e533310](https://github.com/hanzoai/python-sdk/commit/e5333107387d4dc41ab47b93f26a30777d11869d)) + + +### Performance Improvements + +* 30s auto-backgrounding + fix hanging processes ([cd78924](https://github.com/hanzoai/python-sdk/commit/cd7892416d5b7c06781a01c05f8167e0e48fba58)) + ### Chores -* remove dead release-please machinery (config, manifest, release-doctor workflow); releases are tag-driven via `publish-pypi.yml` +* add publish-external-dists workflow + zap-mdns/zap-protocol dists ([afcd988](https://github.com/hanzoai/python-sdk/commit/afcd988126b2570bb0abbc91e1a3a51b65ea9022)) +* add README.md for hanzo-tools-code and hanzo-tools-test ([650fb99](https://github.com/hanzoai/python-sdk/commit/650fb99e2063ef0be78ec53a5e00d73a2ccec481)) +* bump hanzo-mcp to 0.10.21 ([bf9339c](https://github.com/hanzoai/python-sdk/commit/bf9339cff191252c5072557585de447ccfd23b91)) +* bump hanzo-mcp to 0.10.22, hanzo-tools-agent to 0.2.1 ([27fd57b](https://github.com/hanzoai/python-sdk/commit/27fd57beb4aac5733c36bbbfa41760aa388d8d21)) +* bump hanzo-mcp to 0.10.32 ([0de76e5](https://github.com/hanzoai/python-sdk/commit/0de76e520b2cf6a1a51342bff5334dc74581b684)) +* bump hanzo-mcp to 0.10.33, update shell dep to 0.5.4 ([4e8a4c7](https://github.com/hanzoai/python-sdk/commit/4e8a4c785c299bc547754b45e3ea2c091841a809)) +* bump hanzo-tools-shell 0.6.3, hanzo 0.4.2 (Windows compat) ([5a2da08](https://github.com/hanzoai/python-sdk/commit/5a2da08cfa1a61a06a895e02cdb20e5cd42a2959)) +* bump hanzo-tools-shell to 0.3.0 ([3b42441](https://github.com/hanzoai/python-sdk/commit/3b4244193b824763b42c88f5db24b50896dba60c)) +* bump minimum Python to 3.12 ([0e9ec97](https://github.com/hanzoai/python-sdk/commit/0e9ec97dbec312276c38bf63b52da830bcc6e739)) +* bump versions for consolidated computer/screen tools ([7fb47e6](https://github.com/hanzoai/python-sdk/commit/7fb47e6c94ef5f9cc61d7aedf2c0cc4dc4e80f71)) +* bump versions for PyPI release ([92c0e6d](https://github.com/hanzoai/python-sdk/commit/92c0e6d42c404b9c02fb88926ded1649f1140b1d)) +* cleanup โ€” remove docker-compose.yml (use compose.yml), delete dead files ([99bee7e](https://github.com/hanzoai/python-sdk/commit/99bee7ea447491cb4b89f4f038ddab36483801d7)) +* gitignore build/ artifacts, remove tracked build files ([f5c11a3](https://github.com/hanzoai/python-sdk/commit/f5c11a3f6d5f588a0d91c0fc9ba62ac54fa547a5)) +* **hanzo-mcp:** bump version to 0.15.0 ([5da7d0f](https://github.com/hanzoai/python-sdk/commit/5da7d0fe407f57dcb67be1268b47f548ec80e7b6)) +* **hanzo-mcp:** update uv lockfile after dependency changes ([1f2be7d](https://github.com/hanzoai/python-sdk/commit/1f2be7d43561996f9820c54891517a7b2ff024c9)) +* **hanzo-tools-iam:** add uv.lock ([e236dba](https://github.com/hanzoai/python-sdk/commit/e236dba8a27d27ca6377d900b9e57d6653528660)) +* integrate hanzo-agent into workspace ([42fa09d](https://github.com/hanzoai/python-sdk/commit/42fa09d94841f880051b79571df015136dbb2012)) +* **mcp:** bump to 0.10.27, require hanzo-tools-shell>=0.4.1 ([bdf274b](https://github.com/hanzoai/python-sdk/commit/bdf274beb5249afa6a4eea21a9dac69b6622ba20)) +* **release:** bump hanzo-mcp to 0.12.1 ([5f30375](https://github.com/hanzoai/python-sdk/commit/5f3037508ff5494c301f93226c415c9d69d177c5)) +* remove AI-generated summary/report files ([2facc47](https://github.com/hanzoai/python-sdk/commit/2facc472af2eb7d746f3769f6d847d7df647cb42)) +* remove dead release-please machinery ([ba7e634](https://github.com/hanzoai/python-sdk/commit/ba7e63422cebea8448d5b07cba91228febac413e)) +* remove dist-external/ after one-shot zap publish ([8f343f6](https://github.com/hanzoai/python-sdk/commit/8f343f6369d3f01f734c31d260ca62b2f9fbfb08)) +* remove standalone GitHub workflows from hanzo-agent ([02450be](https://github.com/hanzoai/python-sdk/commit/02450befe310f2e1720e1575750cb668cd77af33)) +* rename Quality Gate workflow ([de1223e](https://github.com/hanzoai/python-sdk/commit/de1223e09fcf63eb94e3ba538df957c364e8e5ec)) +* sync repo ([31ccc45](https://github.com/hanzoai/python-sdk/commit/31ccc45ceb10978816fa6bfc95943986cea4c470)) +* sync uncommitted changes ([29cf88a](https://github.com/hanzoai/python-sdk/commit/29cf88a194fac77fd7066147dadabcd02c4405fd)) +* untrack node_modules, improve .gitignore ([8daeb54](https://github.com/hanzoai/python-sdk/commit/8daeb5448e2d013b2e7df7909d23c7961d05a7ec)) +* update all dependencies and fix uvloop/nest_asyncio conflict ([551c584](https://github.com/hanzoai/python-sdk/commit/551c584363f57dbe4ab5cae718ea9d3410fb7739)) +* update dependencies to latest ([8e240fb](https://github.com/hanzoai/python-sdk/commit/8e240fb21f81ec13dbced6b54f0a420a6d98e40a)) +* update dependencies to latest ([4a0c618](https://github.com/hanzoai/python-sdk/commit/4a0c6183d3350b9807c3cbeca622c1cb9494aba5)) +* update hanzo-kms package ([4215bc2](https://github.com/hanzoai/python-sdk/commit/4215bc2dae130759b2a143819a338d6d94062fc0)) +* update hanzoai SDK and tests ([22f4ab0](https://github.com/hanzoai/python-sdk/commit/22f4ab0bcbcf9a4f7325ffad1e713abb06173ba0)) +* update SDK client, memory docs, and tests ([bd6d88a](https://github.com/hanzoai/python-sdk/commit/bd6d88a6cdf452c36881056e6b2c9f7910d46820)) +* update sub-package dependencies for security fixes ([911eb87](https://github.com/hanzoai/python-sdk/commit/911eb877fdada745ec4872653abc30dfe3e328ee)) +* update uv.lock ([a0c1682](https://github.com/hanzoai/python-sdk/commit/a0c168232a5c4cb3495e8908c7e4c1caac4eed24)) + + +### Documentation + +* add LLM.md project guide ([e68b843](https://github.com/hanzoai/python-sdk/commit/e68b84365891bace67b4768fa3bff89529c06c0a)) +* add navigation section to main index ([9bbab33](https://github.com/hanzoai/python-sdk/commit/9bbab3353590bbca1fa665b99ecf76822a90cc44)) +* add Next.js documentation site with @hanzo/mdx ([7bd5e0b](https://github.com/hanzoai/python-sdk/commit/7bd5e0b2d6d1e2dd0f2125820fa950a539f9045e)) +* add READMEs for all 18 hanzo-tools-* packages ([114864d](https://github.com/hanzoai/python-sdk/commit/114864dfab7d5338a127798154bc759d4158f933)) +* **consensus:** document MCP mesh usage ([35b7806](https://github.com/hanzoai/python-sdk/commit/35b78065b8bcb6c9fb0f6cc54eaa38beca3835dc)) +* create unified documentation site for python-sdk ([7475b29](https://github.com/hanzoai/python-sdk/commit/7475b29fb44aef42654d42551a61f16bfc725f34)) +* **hanzo-tools:** add README with architecture, API, and usage examples ([cac1c9c](https://github.com/hanzoai/python-sdk/commit/cac1c9c843e21065eeb36c1f20cadd5e1f333b40)) +* improve DX with navigation and cross-references ([3317417](https://github.com/hanzoai/python-sdk/commit/3317417b4c8a3d6be4997da8a53e06de9a1e6938)) +* **lib:** add core library documentation ([5d24e71](https://github.com/hanzoai/python-sdk/commit/5d24e712104dd0680a8beb482b2ec243ed7ffc30)) +* **mcp:** add comprehensive parity analysis for Python/TS/Rust MCPs ([11ebec2](https://github.com/hanzoai/python-sdk/commit/11ebec25a149da7a7a80bd5fab02305c6e89695c)) +* replace all placeholder content with real documentation ([5cb47d5](https://github.com/hanzoai/python-sdk/commit/5cb47d56351c37ba91351f576e172e0dc84aa2c5)) +* **shell:** update bash/shell tool descriptions with Shellflow syntax ([9ec863b](https://github.com/hanzoai/python-sdk/commit/9ec863b9f54ae58d6c12af7bb989081a78b5b2b7)) +* **tools:** add comprehensive documentation for all tool packages ([05aabb2](https://github.com/hanzoai/python-sdk/commit/05aabb2d328526bd26bb05ed97d5e2ab86db5c9b)) +* **tools:** add database, editor, jupyter, mcp, vector tool docs ([0b1036a](https://github.com/hanzoai/python-sdk/commit/0b1036a65e5845554eb31a295704628441a2e7f1)) +* **tools:** add todo, computer, config tool documentation ([f81e038](https://github.com/hanzoai/python-sdk/commit/f81e0382d4a42526675a282b103ee6b892eda941)) + + +### Styles + +* auto-fix ruff lint and format for Python 3.12 ([d9123e7](https://github.com/hanzoai/python-sdk/commit/d9123e77d45c6e4cd4da3ac14b9774496355cead)) +* black --target-version py312 (CI format-check fix) ([4b664bb](https://github.com/hanzoai/python-sdk/commit/4b664bba4c9833008e08a5b85fa28bd62c90a845)) +* black --target-version py312 (unblock publish CI) ([240f66a](https://github.com/hanzoai/python-sdk/commit/240f66aaf676ca1445036024150404516136f34d)) +* **docs:** add Geist font, Hanzo logo, and shadcn/ui dark theme ([452797f](https://github.com/hanzoai/python-sdk/commit/452797f8246fc099a495a4fe8c99de678cc3658a)) +* fix import sorting in test file ([0123c91](https://github.com/hanzoai/python-sdk/commit/0123c91f4eb7353188654116b888938734e09241)) +* format hanzo_mcp/__init__.py ([4c2990d](https://github.com/hanzoai/python-sdk/commit/4c2990d07696e7822a48f900cc64f5293d6bcdeb)) +* format iam.py with black line-length rules ([1a1edb0](https://github.com/hanzoai/python-sdk/commit/1a1edb09898a460dae86d397424d5a08c2d22e35)) +* format test files with black ([fd74508](https://github.com/hanzoai/python-sdk/commit/fd74508fe710c56bd54c1344cc3fb147d32b10d6)) +* **hanzo:** format with black ([b16bf40](https://github.com/hanzoai/python-sdk/commit/b16bf40f2b230c442849b9801dc4fae2f4aaad9d)) + + +### Refactors + +* **agent:** rename Lux Quasar to Metastable consensus ([bc786f1](https://github.com/hanzoai/python-sdk/commit/bc786f1500dedaa9eb0874278bd445bfd1d3d323)) +* eliminate all TODOs, placeholders, stubs, and fake data ([11aa128](https://github.com/hanzoai/python-sdk/commit/11aa12820bed6628d57964ac10f16ed4daf6958c)) +* **hanzo-mcp:** clean up ZAP server bridge ([8d1527d](https://github.com/hanzoai/python-sdk/commit/8d1527d10eac340074dab2e90e1d4ddba83e8d48)) +* **mcp:** rename hanzo-tools-platform to hanzo-tools-paas ([f4db786](https://github.com/hanzoai/python-sdk/commit/f4db786b9eacd3b81db57bbcfb04ad697d61999e)) +* merge hanzo-tools-core into hanzo-tools ([211a8a0](https://github.com/hanzoai/python-sdk/commit/211a8a00e7eaaf25b8a97f6d25f38379edeaf119)) +* remove AI slop - mock data, stubs, dead code ([aa50cb4](https://github.com/hanzoai/python-sdk/commit/aa50cb4c40dc4325d5a22ac29a2775996da8bef3)) +* remove AI slop - mock data, stubs, dead code ([902e49d](https://github.com/hanzoai/python-sdk/commit/902e49d51956f631f90f43c4d40bd5f3800d48e4)) +* remove all backwards compat aliases from cloud CLI ([7220db4](https://github.com/hanzoai/python-sdk/commit/7220db4719b338630296ab9fd448309e05025d91)) +* remove ambiguous shell tool, keep specific shells ([76d4623](https://github.com/hanzoai/python-sdk/commit/76d4623cbb005396572e23bb105d530aa63d1d6a)) +* rename hanzo-metastable-consensus to hanzo-consensus ([d764d3c](https://github.com/hanzoai/python-sdk/commit/d764d3cccfb59e9a11af2e1429327a8f21a1b196)) +* rename hanzo-tools-filesystem to hanzo-tools-fs ([236ee58](https://github.com/hanzoai/python-sdk/commit/236ee580d524462c02643cd206b9d21e081b4778)) +* **shell:** merge dag into zsh for unified shell tool ([981f1c1](https://github.com/hanzoai/python-sdk/commit/981f1c10dae0ce2cf5f0dbc027f36498494fc2a6)) +* simplify hanzo infra extras (hanzo[vector] instead of hanzo[infra-vector]) ([be050ee](https://github.com/hanzoai/python-sdk/commit/be050ee212aaedabd0e51ec3bad97a254b6fc495)) ## 2.0.2 (2025-04-04) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6e706b059..cd6e86ca7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,234 +1,128 @@ -# Contributing to Hanzo Python SDK +## Setting up the environment -We welcome contributions to the Hanzo Python SDK! This document provides guidelines for contributing to the project. +### With Rye -## Code of Conduct +We use [Rye](https://rye.astral.sh/) to manage dependencies because it will automatically provision a Python environment with the expected Python version. To set it up, run: -By participating in this project, you agree to abide by our Code of Conduct: -- Be respectful and inclusive -- Welcome newcomers and help them get started -- Focus on constructive criticism -- Accept feedback gracefully - -## Getting Started - -### Prerequisites - -- Python 3.10 or higher -- `uv` package manager -- Git - -### Development Setup - -1. Fork the repository -2. Clone your fork: - ```bash - git clone https://github.com/your-username/python-sdk.git - cd python-sdk - ``` - -3. Install dependencies: - ```bash - make setup - ``` - -4. Create a feature branch: - ```bash - git checkout -b feature/your-feature-name - ``` - -## Development Workflow - -### Code Style - -We use `ruff` for linting and formatting: - -```bash -# Format code -make format - -# Check linting -make lint - -# Type checking -make type-check +```sh +$ ./scripts/bootstrap ``` -### Testing - -All contributions must include tests: - -```bash -# Run all tests -make test - -# Run specific package tests -cd pkg/hanzo && pytest tests/ +Or [install Rye manually](https://rye.astral.sh/guide/installation/) and run: -# Run with coverage -pytest tests/ --cov=hanzo --cov-report=html +```sh +$ rye sync --all-features ``` -### Documentation +You can then run scripts using `rye run python script.py` or by activating the virtual environment: -- Update README files for any new features -- Add docstrings to all public functions/classes -- Include usage examples in docstrings -- Update API documentation if needed +```sh +# Activate the virtual environment - https://docs.python.org/3/library/venv.html#how-venvs-work +$ source .venv/bin/activate -## Contribution Process +# now you can omit the `rye run` prefix +$ python script.py +``` -### 1. Find or Create an Issue +### Without Rye -- Check existing issues first -- Create a new issue for bugs or features -- Get feedback before starting major work +Alternatively if you don't want to install `Rye`, you can stick with the standard `pip` setup by ensuring you have the Python version specified in `.python-version`, create a virtual environment however you desire and then install dependencies using this command: -### 2. Make Changes +```sh +$ pip install -r requirements-dev.lock +``` -- Write clean, readable code -- Follow existing patterns and conventions -- Keep commits small and focused -- Write descriptive commit messages +## Modifying/Adding code -### 3. Commit Guidelines +Most of the SDK is generated code. Modifications to code will be persisted between generations, but may +result in merge conflicts between manual patches and changes from the generator. The generator will never +modify the contents of the `src/hanzoai/lib/` and `examples/` directories. -We follow conventional commits: +## Adding and running examples -``` -type(scope): description +All files in the `examples/` directory are not modified by the generator and can be freely edited or added to. -[optional body] +```py +# add an example to examples/.py -[optional footer] +#!/usr/bin/env -S rye run python +โ€ฆ ``` -Types: -- `feat`: New feature -- `fix`: Bug fix -- `docs`: Documentation -- `style`: Code style changes -- `refactor`: Code refactoring -- `test`: Test changes -- `chore`: Build/tooling changes - -Examples: -```bash -feat(agents): add parallel execution support -fix(mcp): resolve file permission issue -docs(network): update API documentation +```sh +$ chmod +x examples/.py +# run the example against your api +$ ./examples/.py ``` -### 4. Submit Pull Request - -1. Push your branch: - ```bash - git push origin feature/your-feature-name - ``` - -2. Create a pull request on GitHub - -3. Fill out the PR template: - - Describe what changes you made - - Link related issues - - Include test results - - Add screenshots if applicable - -4. Wait for review and address feedback - -## Package-Specific Guidelines +## Using the repository from source -### Core SDK (`pkg/hanzoai`) -- Maintain OpenAI compatibility -- Preserve backward compatibility -- Document breaking changes +If youโ€™d like to use the repository from source, you can either install from git or link to a cloned repository: -### CLI (`pkg/hanzo`) -- Keep commands intuitive -- Provide helpful error messages -- Include --help for all commands +To install via git: -### MCP (`pkg/hanzo-mcp`) -- Follow MCP specification -- Ensure tool safety -- Document permissions required - -### Agents (`pkg/hanzo-agents`) -- Keep agents focused and specialized -- Provide clear agent descriptions -- Include usage examples +```sh +$ pip install git+ssh://git@github.com/hanzoai/python-sdk.git +``` -### Network (`pkg/hanzo-network`) -- Ensure thread safety -- Handle network failures gracefully -- Document resource requirements +Alternatively, you can build from source and install the wheel file: -## Testing Requirements +Building this package will create two files in the `dist/` directory, a `.tar.gz` containing the source files and a `.whl` that can be used to install the package efficiently. -### Unit Tests -- Test individual functions/methods -- Mock external dependencies -- Aim for >80% coverage +To create a distributable version of the library, all you have to do is run this command: -### Integration Tests -- Test component interactions -- Use real services when possible -- Mark with `@pytest.mark.integration` +```sh +$ rye build +# or +$ python -m build +``` -### End-to-End Tests -- Test complete workflows -- Run in CI/CD pipeline -- Document test scenarios +Then to install: -## Code Review Process +```sh +$ pip install ./path-to-wheel-file.whl +``` -### What We Look For +## Running tests -- **Correctness**: Does it work as intended? -- **Tests**: Are there adequate tests? -- **Documentation**: Is it well-documented? -- **Style**: Does it follow our conventions? -- **Performance**: Are there any bottlenecks? -- **Security**: Are there security concerns? +Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. -### Review Timeline +```sh +# you will need npm installed +$ npx prism mock path/to/your/openapi.yml +``` -- Initial review: Within 2-3 business days -- Follow-up reviews: Within 1-2 business days -- Small fixes: Same day if possible +```sh +$ ./scripts/test +``` -## Release Process +## Linting and formatting -1. **Version Bump**: Update version in `pyproject.toml` -2. **Changelog**: Update CHANGELOG.md -3. **Testing**: Run full test suite -4. **Documentation**: Update docs if needed -5. **Tag**: Create version tag -6. **Release**: Publish to PyPI +This repository uses [ruff](https://github.com/astral-sh/ruff) and +[black](https://github.com/psf/black) to format the code in the repository. -## Getting Help +To lint: -### Resources +```sh +$ ./scripts/lint +``` -- [Documentation](https://docs.hanzo.ai) -- [Discord Community](https://discord.gg/hanzo) -- [GitHub Discussions](https://github.com/hanzoai/python-sdk/discussions) +To format and fix all ruff issues automatically: -### Contact +```sh +$ ./scripts/format +``` -- General questions: support@hanzo.ai -- Security issues: security@hanzo.ai -- Partnership: partners@hanzo.ai +## Publishing and releases -## Recognition +Changes made to this repository via the automated release PR pipeline should publish to PyPI automatically. If +the changes aren't made through the automated pipeline, you may want to make releases manually. -Contributors are recognized in: -- CONTRIBUTORS.md file -- Release notes -- Project documentation +### Publish with a GitHub workflow -## License +You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/hanzoai/python-sdk/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up. -By contributing, you agree that your contributions will be licensed under the Apache License 2.0. +### Publish manually -Thank you for contributing to Hanzo! ๐ŸŽ‰ \ No newline at end of file +If you need to manually release a package, you can run the `bin/publish-pypi` script with a `PYPI_TOKEN` set on +the environment. diff --git a/LLM.md b/LLM.md deleted file mode 100644 index 6ba4ef406..000000000 --- a/LLM.md +++ /dev/null @@ -1,187 +0,0 @@ -# LLM.md - Hanzo Python SDK - -## Quick Start - -```bash -uv sync --all-packages -uv run python -c "from hanzoai import __version__; print(__version__)" -``` - -## Packages - -### Core (Workspace Members) -| Package | Import | Description | -|---------|--------|-------------| -| hanzoai | `import hanzoai` | Official Hanzo API client | -| hanzo-mcp | `import hanzo_mcp` | MCP server with 39 tools | -| hanzo-memory | `import hanzo_memory` | Memory service with SQLite/vector | -| hanzo-repl | `import hanzo_repl` | Interactive REPL | -| hanzo-agent | `import hanzo_agent` | Agent framework | - -### Tool Packages (Entry Points) -All tools discovered via `[project.entry-points."hanzo.tools"]`: - -- `hanzo-tools-shell` - zsh, ps, open, npx, uvx -- `hanzo-tools-browser` - Playwright automation -- `hanzo-tools-fs` - read, write, edit, tree, find, search, ast -- `hanzo-tools-memory` - Unified memory tool -- `hanzo-tools-reasoning` - think, critic -- `hanzo-tools-agent` - CLI agent runner, iching, review -- `hanzo-tools-api` - Generic REST API via OpenAPI -- `hanzo-tools-lsp` - Language server protocol -- `hanzo-tools-refactor` - AST-based refactoring -- `hanzo-tools-llm` - LLM calls, consensus - -## Architecture - -``` -hanzo-mcp (thin wrapper) - โ””โ”€โ”€ discovers tools via entry points from hanzo-tools-* packages - -hanzo-tools-* - โ””โ”€โ”€ each exports TOOLS list via entry point -``` - -**Entry Point Pattern:** -```toml -[project.entry-points."hanzo.tools"] -shell = "hanzo_tools.shell:TOOLS" -``` - -## Key Patterns - -### Tool Registration -```python -class MyTool(BaseTool): - name = "my_tool" - - @property - def description(self) -> str: - return "Tool description" - - async def call(self, ctx, **params) -> str: - pass -``` - -### Async (Non-blocking) -```python -# Use asyncio subprocess -proc = await asyncio.create_subprocess_exec(*cmd, stdout=PIPE, stderr=PIPE) -stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=30) - -# Use aiofiles for file I/O -async with aiofiles.open(path) as f: - content = await f.read() -``` - -### Auto-backgrounding -Commands auto-background after 45s. Use `ps` tool to monitor. - -## Memory System - -SQLite-based with optional vector search (sqlite-vec). - -``` -~/.hanzo/ -โ”œโ”€โ”€ memory/ # Global markdown files -โ””โ”€โ”€ db/ - โ””โ”€โ”€ global.db # SQLite with FTS5 - -/project/.hanzo/ -โ”œโ”€โ”€ memory/ # Project memories -โ””โ”€โ”€ db/ - โ””โ”€โ”€ memory.db # Project database -``` - -**Backends:** local (default), sqlite, lancedb (optional), kuzudb (optional) - -## Browser Tool (hanzo-tools-browser) - -Two-process architecture (since 0.5.0): `hanzo-tools-browser` hosts the -ZAP server directly inside the MCP process. Browser extensions discover -it on the lowest free port from `[9999, 9998, 9997, 9996, 9995]` (POSIX -flock ensures multi-MCP coexistence under one ext). - -```python -from hanzo_tools.browser import ( - BrowserTool, # the MCP tool - ZapServer, # raw server (for advanced use) - get_or_start_server, # bootstrap + return singleton - get_server, # return current singleton or None -) -``` - -Key files: -- `hanzo_tools/browser/zap_server.py` โ€” wire format, server, leases, - cluster registry (`~/.hanzo/extension/config.json:mcp_instances`). -- `hanzo_tools/browser/browser_tool.py` โ€” `_extension_command` tries - ZAP first, falls back to legacy HTTP bridge on `:9224`. New actions - `list_mcp_instances`, `claim_browser`, `release_browser`. -- `hanzo_tools/browser/cdp_bridge_server.py` โ€” legacy node-bridge - replacement (kept for non-ZAP callers). - -Env vars: -- `BROWSER_TRANSPORT=zap|http|auto` โ€” pin transport (default auto). -- `HANZO_ZAP_DISABLED=1` โ€” don't auto-start the ZAP server. -- `HANZO_ZAP_PORTS=9999,9998` โ€” override the candidate port list. -- `HANZO_AGENT_LABEL=...` โ€” attach to the cluster registry entry. -- `HANZO_CDP_BRIDGE_ENABLED=1` โ€” opt back into the legacy HTTP bridge. - -Tests live in `pkg/hanzo-tools-browser/tests/test_zap_server.py` (30 -cases: wire format, lifecycle, RPC, leases, multi-MCP). Latency bench -in `test_zap_bench.py`. The full suite runs with: - -```bash -cd pkg/hanzo-tools-browser -uv venv .venv --python 3.12 -.venv/bin/python -m pip install -e . -.venv/bin/python -m pytest tests/ -v -``` - -## API Tool - -Generic REST API tool with OpenAPI specs. - -```python -api(action="list") # List providers -api(action="config", provider="github", api_key="x") # Configure -api(action="call", provider="github", operation="listRepos") -``` - -Auto-detects: `GITHUB_TOKEN`, `CLOUDFLARE_API_TOKEN`, `OPENAI_API_KEY`, etc. - -### Hanzo API Providers - -All Hanzo services have unified OpenAPI specs at `/Users/z/work/hanzo/openapi/`: - -| Provider | Service | Base URL | Spec | -|----------|---------|----------|------| -| `hanzo` | Unified API | api.hanzo.ai | `hanzo.yaml` | -| `hanzo-iam` | Identity/Auth | iam.hanzo.ai | `iam/openapi.yaml` | -| `hanzo-gateway` | LLM Gateway | gateway.hanzo.ai | `gateway/openapi.yaml` | -| `hanzo-commerce` | E-commerce | api.hanzo.ai/v1 | `commerce/openapi.yaml` | -| `hanzo-vector` | Vector DB | vector.hanzo.ai | `vector/openapi.yaml` | -| `hanzo-cloud` | AI Platform | cloud.hanzo.ai | `cloud/openapi.yaml` | -| `hanzo-nexus` | RAG/Knowledge | nexus.hanzo.ai | `nexus/openapi.yaml` | - -```python -# Example: Call IAM API -api(action="spec", provider="hanzo-iam") -api(action="ops", provider="hanzo-iam", search="user") -api(action="call", provider="hanzo-iam", operation="getUser", params='{"id": "admin/user1"}') -``` - -## Testing - -```bash -uv run pytest tests/ -v -uv run python -c "from hanzoai import __version__; print(__version__)" -``` - -## Common Issues - -**Import error:** Run `uv sync --all-packages` - -**Missing tool:** Check entry point in package's pyproject.toml - -**Backend not available:** Install optional deps (lancedb, kuzu, sqlite-vec) diff --git a/Makefile b/Makefile deleted file mode 100644 index 00f802386..000000000 --- a/Makefile +++ /dev/null @@ -1,324 +0,0 @@ -.DEFAULT_GOAL := all -SHELL := /bin/bash -.PHONY: help all setup install install-local uninstall install-python venv deps test lint format build clean publish-all check check-forbidden check-functions test-no-stubs test-all security install-hooks - -# Colors for output -CYAN := \033[0;36m -GREEN := \033[0;32m -RED := \033[0;31m -YELLOW := \033[0;33m -NC := \033[0m # No Color - -# Python version -PYTHON_VERSION := 3.12 - -help: ## Show this help message - @echo -e "$(CYAN)Hanzo Python SDK Monorepo Makefile$(NC)" - @echo -e "$(YELLOW)Usage:$(NC) make [target]" - @echo - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " $(GREEN)%-20s$(NC) %s\n", $$1, $$2}' - -all: format lint test ## Run all checks - -# ==================== INSTALLATION ==================== -# Install CLI tools to ~/.local/bin/ (like Claude Code) - -INSTALL_DIR := $(HOME)/.local/bin - -install: ## Install CLI tools to ~/.local/bin via uv tool - @echo -e "$(CYAN)Installing Hanzo CLI tools to $(INSTALL_DIR)...$(NC)" - @if ! command -v uv &> /dev/null; then \ - echo -e "$(RED)Error: uv is not installed. Run: curl -LsSf https://astral.sh/uv/install.sh | sh$(NC)"; \ - exit 1; \ - fi - @uv tool install hanzo --upgrade 2>/dev/null || uv tool install hanzo - @uv tool install hanzo-mcp --upgrade 2>/dev/null || uv tool install hanzo-mcp - @uv tool install hanzo-agents --upgrade 2>/dev/null || uv tool install hanzo-agents - @echo "" - @echo -e "$(GREEN)โœ“ Hanzo CLI tools installed!$(NC)" - @echo -e " Location: $(INSTALL_DIR)" - @echo "" - @echo -e " $(CYAN)hanzo --help$(NC) # CLI commands" - @echo -e " $(CYAN)hanzo-mcp$(NC) # MCP server" - @echo -e " $(CYAN)hanzo-agents$(NC) # Agents framework" - @echo "" - @if [[ ":$$PATH:" != *":$(INSTALL_DIR):"* ]]; then \ - echo -e "$(YELLOW)Add to PATH:$(NC) export PATH=\"$(INSTALL_DIR):\$$PATH\""; \ - fi - -install-local: build ## Install from local source (development) - @echo -e "$(CYAN)Installing Hanzo from local source...$(NC)" - @uv tool install --force ./pkg/hanzo - @uv tool install --force ./pkg/hanzo-mcp - @uv tool install --force ./pkg/hanzo-agents - @echo -e "$(GREEN)โœ“ Installed from local source$(NC)" - -uninstall: ## Remove all Hanzo CLI tools - @echo -e "$(CYAN)Uninstalling Hanzo CLI tools...$(NC)" - @uv tool uninstall hanzo 2>/dev/null || true - @uv tool uninstall hanzo-mcp 2>/dev/null || true - @uv tool uninstall hanzo-agents 2>/dev/null || true - @echo -e "$(GREEN)โœ“ Hanzo CLI tools uninstalled$(NC)" - -doctor: ## Show installed Hanzo tools - @echo -e "$(CYAN)Hanzo CLI Tools Status$(NC)" - @echo "" - @echo -e " $(CYAN)uv tools:$(NC)" - @uv tool list 2>/dev/null | grep -E "^hanzo" | while read line; do \ - name=$$(echo "$$line" | awk '{print $$1}'); \ - ver=$$(echo "$$line" | awk '{print $$2}'); \ - path=$$(command -v "$$name" 2>/dev/null || echo "~/.local/bin/$$name"); \ - printf " $(GREEN)โœ“$(NC) %-16s %-10s %s\n" "$$name" "$$ver" "$$path"; \ - done || echo -e " $(RED)(none installed)$(NC)" - @echo "" - -setup: install-python venv deps ## Complete setup: install Python, create venv, install deps - -install-python: ## Install Python using uv - @echo -e "$(CYAN)Installing Python $(PYTHON_VERSION)...$(NC)" - @if ! command -v uv &> /dev/null; then \ - echo -e "$(RED)Error: uv is not installed. Please install it first.$(NC)"; \ - echo "Run: curl -LsSf https://astral.sh/uv/install.sh | sh"; \ - exit 1; \ - fi - @uv python install $(PYTHON_VERSION) - @echo -e "$(GREEN)Python $(PYTHON_VERSION) installed successfully$(NC)" - -venv: ## Create virtual environment - @echo -e "$(CYAN)Creating virtual environment...$(NC)" - @uv venv --python $(PYTHON_VERSION) - @echo -e "$(GREEN)Virtual environment created$(NC)" - @echo -e "$(YELLOW)Activate with: source .venv/bin/activate$(NC)" - -deps: ## Install all dependencies for all packages - @echo -e "$(CYAN)Installing dependencies...$(NC)" - @source .venv/bin/activate && uv pip install -e . - @source .venv/bin/activate && uv pip install -e ./pkg/hanzo-agents - @source .venv/bin/activate && uv pip install -e ./pkg/hanzo-mcp - @source .venv/bin/activate && uv pip install -e ./pkg/hanzo - @source .venv/bin/activate && uv pip install -e ./pkg/hanzo-dev - @source .venv/bin/activate && uv pip install -e ./pkg/hanzo-memory - @source .venv/bin/activate && uv pip install -e ./pkg/hanzo-network - - @source .venv/bin/activate && uv pip install -e ./pkg/hanzo-aci - @echo -e "$(GREEN)All dependencies installed$(NC)" - -dev-deps: ## Install development dependencies - @echo -e "$(CYAN)Installing development dependencies...$(NC)" - @source .venv/bin/activate && uv pip install pytest pytest-asyncio mypy ruff black coverage - @echo -e "$(GREEN)Development dependencies installed$(NC)" - -test: ## Run tests for all packages - @echo -e "$(CYAN)Running tests...$(NC)" - @source .venv/bin/activate && PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python -m pytest tests/ -v - @source .venv/bin/activate && cd pkg/hanzo-agents && PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python -m pytest tests/ -v || true - @source .venv/bin/activate && cd pkg/hanzo-mcp && PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python -m pytest tests/ -v || true - @source .venv/bin/activate && cd pkg/hanzo-memory && PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python -m pytest tests/ -v || true - @source .venv/bin/activate && cd pkg/hanzo-aci && PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python -m pytest tests/ -v || true - @echo -e "$(GREEN)Tests completed$(NC)" - -lint: ## Run linting for all packages - @echo -e "$(CYAN)Running linters...$(NC)" - @source .venv/bin/activate && ruff check . --fix --quiet - @source .venv/bin/activate && mypy . --ignore-missing-imports --no-error-summary 2>/dev/null || true - @echo -e "$(GREEN)Linting completed$(NC)" - -format: ## Format code for all packages - @echo -e "$(CYAN)Formatting code...$(NC)" - @source .venv/bin/activate && ruff format . --quiet - @source .venv/bin/activate && black . --quiet 2>/dev/null || true - @echo -e "$(GREEN)Formatting completed$(NC)" - -build: ## Build all packages - @echo -e "$(CYAN)Building packages...$(NC)" - @uv build . - @cd pkg/hanzo-agents && uv build - @cd pkg/hanzo-mcp && uv build - @cd pkg/hanzo && uv build - @cd pkg/hanzo-dev && uv build - @cd pkg/hanzo-memory && uv build - @cd pkg/hanzo-network && uv build - @cd pkg/hanzo-aci && uv build - @echo -e "$(GREEN)All packages built$(NC)" - -clean: ## Clean build artifacts - @echo -e "$(CYAN)Cleaning build artifacts...$(NC)" - @find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true - @find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true - @find . -type d -name "dist" -exec rm -rf {} + 2>/dev/null || true - @find . -type d -name "build" -exec rm -rf {} + 2>/dev/null || true - @find . -type d -name ".pytest_cache" -exec rm -rf {} + 2>/dev/null || true - @find . -type d -name ".mypy_cache" -exec rm -rf {} + 2>/dev/null || true - @find . -type d -name ".ruff_cache" -exec rm -rf {} + 2>/dev/null || true - @find . -type f -name "*.pyc" -delete 2>/dev/null || true - @find . -type f -name "*.pyo" -delete 2>/dev/null || true - @find . -type f -name ".coverage" -delete 2>/dev/null || true - @rm -rf htmlcov/ 2>/dev/null || true - @echo -e "$(GREEN)Clean completed$(NC)" - -publish-check: ## Check if packages are ready to publish - @echo -e "$(CYAN)Checking package configurations...$(NC)" - @python scripts/check_packages.py - -publish-all: build ## Publish all packages to PyPI - @echo -e "$(CYAN)Publishing packages to PyPI...$(NC)" - @echo -e "$(YELLOW)Warning: This will publish to PyPI. Make sure you have the correct credentials.$(NC)" - @read -p "Continue? [y/N] " -n 1 -r; echo; \ - if [[ $$REPLY =~ ^[Yy]$$ ]]; then \ - $(MAKE) publish-hanzoai; \ - $(MAKE) publish-hanzo-agents; \ - $(MAKE) publish-hanzo-mcp; \ - $(MAKE) publish-hanzo; \ - $(MAKE) publish-hanzo-dev; \ - $(MAKE) publish-hanzo-memory; \ - $(MAKE) publish-hanzo-network; \ - $(MAKE) publish-hanzo-aci; \ - else \ - echo -e "$(RED)Publishing cancelled$(NC)"; \ - fi - -# Publishing individual packages -publish-hanzoai: ## Publish hanzoai package - @echo -e "$(CYAN)Publishing hanzoai...$(NC)" - @cd . && TWINE_USERNAME=__token__ TWINE_PASSWORD=$${PYPI_TOKEN} twine upload dist/* --skip-existing - -publish-hanzo-agents: ## Publish hanzo-agents package - @echo -e "$(CYAN)Publishing hanzo-agents...$(NC)" - @cd pkg/hanzo-agents && TWINE_USERNAME=__token__ TWINE_PASSWORD=$${PYPI_TOKEN} twine upload dist/* --skip-existing - -publish-hanzo-mcp: check ## Publish hanzo-mcp package (REQUIRES ALL CHECKS TO PASS) - @echo -e "$(CYAN)Publishing hanzo-mcp...$(NC)" - @cd pkg/hanzo-mcp && TWINE_USERNAME=__token__ TWINE_PASSWORD=$${PYPI_TOKEN} twine upload dist/* --skip-existing - -publish-hanzo: ## Publish hanzo package - @echo -e "$(CYAN)Publishing hanzo...$(NC)" - @cd pkg/hanzo && TWINE_USERNAME=__token__ TWINE_PASSWORD=$${PYPI_TOKEN} twine upload dist/* --skip-existing - -publish-hanzo-dev: ## Publish hanzo-dev package - @echo -e "$(CYAN)Publishing hanzo-dev...$(NC)" - @cd pkg/hanzo-dev && TWINE_USERNAME=__token__ TWINE_PASSWORD=$${PYPI_TOKEN} twine upload dist/* --skip-existing - -publish-hanzo-memory: ## Publish hanzo-memory package - @echo -e "$(CYAN)Publishing hanzo-memory...$(NC)" - @cd pkg/hanzo-memory && TWINE_USERNAME=__token__ TWINE_PASSWORD=$${PYPI_TOKEN} twine upload dist/* --skip-existing - -publish-hanzo-network: ## Publish hanzo-network package - @echo -e "$(CYAN)Publishing hanzo-network...$(NC)" - @cd pkg/hanzo-network && TWINE_USERNAME=__token__ TWINE_PASSWORD=$${PYPI_TOKEN} twine upload dist/* --skip-existing - - - -publish-hanzo-aci: ## Publish hanzo-aci as dev-aci package - @echo -e "$(CYAN)Publishing hanzo-aci as dev-aci...$(NC)" - @cd pkg/hanzo-aci && TWINE_USERNAME=__token__ TWINE_PASSWORD=$${PYPI_TOKEN} twine upload dist/* --skip-existing - -# Individual package commands -test-hanzoai: ## Test main hanzoai package - @source .venv/bin/activate && python -m pytest tests/ -v - -test-agents: ## Test hanzo-agents package - @source .venv/bin/activate && cd pkg/hanzo-agents && python -m pytest tests/ -v - -test-mcp: ## Test hanzo-mcp package - @source .venv/bin/activate && cd pkg/hanzo-mcp && python -m pytest tests/ -v - -test-memory: ## Test hanzo-memory package - @source .venv/bin/activate && cd pkg/hanzo-memory && python -m pytest tests/ -v - -test-aci: ## Test hanzo-aci package - @source .venv/bin/activate && cd pkg/hanzo-aci && python -m pytest tests/ -v - -# Development helpers -shell: ## Start Python shell with packages loaded - @source .venv/bin/activate && python - -watch-tests: ## Watch and run tests on file changes - @source .venv/bin/activate && watchmedo shell-command \ - --patterns="*.py" \ - --recursive \ - --command='make test' \ - . - -update-deps: ## Update all dependencies to latest versions - @echo -e "$(CYAN)Updating dependencies...$(NC)" - @source .venv/bin/activate && uv pip compile pyproject.toml -o requirements.txt --upgrade - @source .venv/bin/activate && uv pip sync requirements.txt - @echo -e "$(GREEN)Dependencies updated$(NC)" - -check-types: ## Run type checking - @echo -e "$(CYAN)Running type checks...$(NC)" - @source .venv/bin/activate && mypy pkg/hanzoai --ignore-missing-imports - @source .venv/bin/activate && pyright pkg/hanzoai || true - @echo -e "$(GREEN)Type checking completed$(NC)" - -coverage: ## Run tests with coverage - @echo -e "$(CYAN)Running tests with coverage...$(NC)" - @source .venv/bin/activate && PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 coverage run -m pytest tests/ - @source .venv/bin/activate && coverage report - @source .venv/bin/activate && coverage html - @echo -e "$(GREEN)Coverage report generated in htmlcov/$(NC)" - -# ==================== STRICT QUALITY GATES ==================== - -install-hooks: ## Install pre-commit hooks to catch issues locally - @echo -e "$(GREEN)Installing pre-commit hooks...$(NC)" - @pip install pre-commit - @pre-commit install - @pre-commit install --hook-type pre-push - @echo -e "$(GREEN)โœ… Pre-commit hooks installed!$(NC)" - -check-forbidden: ## Check for TODO/STUB/FAKE patterns (BLOCKS DEPLOYMENT) - @echo -e "$(YELLOW)Checking for TODO/STUB/FAKE patterns...$(NC)" - @! grep -r "TODO\|STUB\|FAKE\|UNFINISHED\|HACK\|XXX\|NotImplementedError" \ - --include="*.py" \ - --exclude-dir=test \ - --exclude-dir=.git \ - --exclude-dir=build \ - --exclude-dir=dist \ - pkg/hanzo-mcp 2>/dev/null || (echo -e "$(RED)โŒ FORBIDDEN PATTERNS FOUND! Remove them!$(NC)" && exit 1) - @echo -e "$(GREEN)โœ… No forbidden patterns$(NC)" - -check-functions: ## Check for empty/stub functions (BLOCKS DEPLOYMENT) - @echo -e "$(YELLOW)Checking for empty functions...$(NC)" - @python scripts/check_empty_functions.py - @echo -e "$(GREEN)โœ… All functions implemented$(NC)" - -test-no-stubs: ## Run anti-stub tests (BLOCKS DEPLOYMENT) - @echo -e "$(YELLOW)Running anti-stub tests...$(NC)" - @source .venv/bin/activate && cd pkg/hanzo-mcp && python -m pytest tests/test_no_stubs.py -v - @echo -e "$(GREEN)โœ… No stubs found$(NC)" - -test-all: ## Run ALL tests - NO SKIPS ALLOWED (BLOCKS DEPLOYMENT) - @echo -e "$(YELLOW)Running ALL tests (no skips allowed)...$(NC)" - @source .venv/bin/activate && cd pkg/hanzo-mcp && python -m pytest tests/ \ - -v \ - --strict-markers \ - --tb=short \ - --maxfail=1 \ - -x \ - 2>&1 | tee test-output.log - @if grep -q "SKIPPED" test-output.log; then \ - echo -e "$(RED)โŒ TESTS WERE SKIPPED! Fix or remove them!$(NC)"; \ - exit 1; \ - fi - @if grep -q "FAILED" test-output.log; then \ - echo -e "$(RED)โŒ TESTS FAILED! All tests must pass!$(NC)"; \ - exit 1; \ - fi - @rm -f test-output.log - @echo -e "$(GREEN)โœ… All tests passed!$(NC)" - -security: ## Security scan with bandit (BLOCKS DEPLOYMENT) - @echo -e "$(YELLOW)Security scanning...$(NC)" - @source .venv/bin/activate && bandit -r pkg/hanzo-mcp/hanzo_mcp -ll - @echo -e "$(GREEN)โœ… Security scan passed$(NC)" - -# MASTER CHECK - Runs ALL quality gates (REQUIRED BEFORE DEPLOYMENT) -check: check-forbidden check-functions lint test-no-stubs test-all security - @echo -e "$(GREEN)โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•$(NC)" - @echo -e "$(GREEN)โœ… ALL QUALITY CHECKS PASSED!$(NC)" - @echo -e "$(GREEN)โœ… NO TODOs, STUBs, or FAKE code found$(NC)" - @echo -e "$(GREEN)โœ… All tests are passing$(NC)" - @echo -e "$(GREEN)โœ… All functions are implemented$(NC)" - @echo -e "$(GREEN)๐Ÿš€ Code is ready for deployment!$(NC)" - @echo -e "$(GREEN)โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•$(NC)" diff --git a/README.md b/README.md index 8585258f7..2fc8c9573 100644 --- a/README.md +++ b/README.md @@ -1,364 +1,396 @@ -# Hanzo Python SDK +# Hanzo Python API library -[![CI](https://github.com/hanzoai/python-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/hanzoai/python-sdk/actions/workflows/ci.yml) -[![PyPI](https://img.shields.io/pypi/v/hanzoai.svg)](https://pypi.org/project/hanzoai/) -[![Python Version](https://img.shields.io/pypi/pyversions/hanzoai.svg)](https://pypi.org/project/hanzoai/) -[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) + +[![PyPI version](https://img.shields.io/pypi/v/hanzoai.svg?label=pypi%20(stable))](https://pypi.org/project/hanzoai/) -The official Python SDK for the Hanzo AI platform, providing unified access to 100+ LLM providers through a single OpenAI-compatible API interface. +The Hanzo Python library provides convenient access to the Hanzo REST API from any Python 3.8+ +application. The library includes type definitions for all request params and response fields, +and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). -## ๐Ÿš€ Features +## Documentation -- **Unified API**: Single interface for 100+ LLM providers (OpenAI, Anthropic, Google, Meta, etc.) -- **OpenAI Compatible**: Drop-in replacement for OpenAI SDK -- **Enterprise Features**: Cost tracking, rate limiting, observability -- **Local AI Support**: Run models locally with node infrastructure -- **Model Context Protocol (MCP)**: Advanced tool use and context management -- **Agent Framework**: Build and orchestrate AI agents -- **Memory Management**: Persistent memory and RAG capabilities -- **Network Orchestration**: Distributed AI compute capabilities +The REST API documentation can be found on [docs.hanzo.ai](https://docs.hanzo.ai). The full API of this library can be found in [api.md](api.md). -## ๐Ÿ“ฆ Installation +## Installation -### Basic Installation - -```bash +```sh +# install from PyPI pip install hanzoai ``` -### Full Installation (All Features) - -```bash -pip install "hanzoai[all]" -``` +## Usage -### Development Installation - -```bash -git clone https://github.com/hanzoai/python-sdk.git -cd python-sdk -make setup -``` - -## ๐ŸŽฏ Quick Start - -### Basic Usage +The full API of this library can be found in [api.md](api.md). ```python +import os from hanzoai import Hanzo -# Initialize client -client = Hanzo(api_key="your-api-key") - -# Chat completion -response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello!"}] +client = Hanzo( + api_key=os.environ.get("HANZO_API_KEY"), # This is the default and can be omitted + # defaults to "production". + environment="sandbox", ) -print(response.choices[0].message.content) + +response = client.get_home() ``` -### Using Different Providers +While you can provide an `api_key` keyword argument, +we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) +to add `HANZO_API_KEY="sk-xxxxxxx` to your `.env` file +so that your API Key is not stored in source control. + +## Async usage + +Simply import `AsyncHanzo` instead of `Hanzo` and use `await` with each API call: ```python -# Use Claude -response = client.chat.completions.create( - model="claude-3-opus-20240229", - messages=[{"role": "user", "content": "Hello!"}] +import os +import asyncio +from hanzoai import AsyncHanzo + +client = AsyncHanzo( + api_key=os.environ.get("HANZO_API_KEY"), # This is the default and can be omitted + # defaults to "production". + environment="sandbox", ) -# Use local models -response = client.chat.completions.create( - model="llama2:7b", - messages=[{"role": "user", "content": "Hello!"}] -) -``` -## ๐Ÿ—๏ธ Architecture +async def main() -> None: + response = await client.get_home() -### Package Structure +asyncio.run(main()) ``` -python-sdk/ -โ”œโ”€โ”€ pkg/ -โ”‚ โ”œโ”€โ”€ hanzo/ # CLI and orchestration tools -โ”‚ โ”œโ”€โ”€ hanzo-mcp/ # Model Context Protocol implementation -โ”‚ โ”œโ”€โ”€ hanzo-agents/ # Agent framework -โ”‚ โ”œโ”€โ”€ hanzo-network/ # Distributed network capabilities -โ”‚ โ”œโ”€โ”€ hanzo-memory/ # Memory and RAG -โ”‚ โ”œโ”€โ”€ hanzo-aci/ # AI code intelligence -โ”‚ โ”œโ”€โ”€ hanzo-repl/ # Interactive REPL -โ”‚ โ””โ”€โ”€ hanzoai/ # Core SDK -``` - -### Core Components -#### 1. **Hanzo CLI** (`hanzo`) -Command-line interface for AI operations: +Functionality between the synchronous and asynchronous clients is otherwise identical. -```bash -# Chat with AI -hanzo chat +### With aiohttp -# Start local node -hanzo node start +By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend. -# Manage router -hanzo router start +You can enable this by installing `aiohttp`: -# Interactive REPL -hanzo repl +```sh +# install from PyPI +pip install hanzoai[aiohttp] ``` -#### 2. **Model Context Protocol** (`hanzo-mcp`) -Advanced tool use and context management: +Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: ```python -from hanzo_mcp import create_mcp_server +import asyncio +from hanzoai import DefaultAioHttpClient +from hanzoai import AsyncHanzo + + +async def main() -> None: + async with AsyncHanzo( + api_key="My API Key", + http_client=DefaultAioHttpClient(), + ) as client: + response = await client.get_home() -server = create_mcp_server() -server.register_tool(my_tool) -server.start() + +asyncio.run(main()) ``` -#### 3. **Agent Framework** (`hanzo-agents`) -Build and orchestrate AI agents: +## Using types + +Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: + +- Serializing back into JSON, `model.to_json()` +- Converting to a dictionary, `model.to_dict()` + +Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. + +## Nested params + +Nested parameters are dictionaries, typed using `TypedDict`, for example: ```python -from hanzo_agents import Agent, Swarm +from hanzoai import Hanzo -agent = Agent( - name="researcher", - model="gpt-4", - instructions="You are a research assistant" -) +client = Hanzo() -swarm = Swarm([agent]) -result = await swarm.run("Research quantum computing") +model = client.model.create( + llm_params={"model": "model"}, + model_info={"id": "id"}, + model_name="model_name", +) +print(model.llm_params) ``` -#### 4. **Network Orchestration** (`hanzo-network`) -Distributed AI compute: +## File uploads + +Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. ```python -from hanzo_network import LocalComputeNode, DistributedNetwork +from pathlib import Path +from hanzoai import Hanzo -node = LocalComputeNode(node_id="node-001") -network = DistributedNetwork() -network.register_node(node) +client = Hanzo() + +client.audio.transcriptions.create( + file=Path("/path/to/file"), +) ``` -#### 5. **Memory Management** (`hanzo-memory`) -Persistent memory and RAG: +The async client uses the exact same interface. If you pass a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, the file contents will be read asynchronously automatically. + +## Handling errors + +When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `hanzoai.APIConnectionError` is raised. + +When the API returns a non-success status code (that is, 4xx or 5xx +response), a subclass of `hanzoai.APIStatusError` is raised, containing `status_code` and `response` properties. + +All errors inherit from `hanzoai.APIError`. ```python -from hanzo_memory import MemoryService +import hanzoai +from hanzoai import Hanzo -memory = MemoryService() -await memory.store("key", "value") -result = await memory.retrieve("key") +client = Hanzo() + +try: + client.get_home() +except hanzoai.APIConnectionError as e: + print("The server could not be reached") + print(e.__cause__) # an underlying Exception, likely raised within httpx. +except hanzoai.RateLimitError as e: + print("A 429 status code was received; we should back off a bit.") +except hanzoai.APIStatusError as e: + print("Another non-200-range status code was received") + print(e.status_code) + print(e.response) ``` -## ๐Ÿ› ๏ธ Development +Error codes are as follows: + +| Status Code | Error Type | +| ----------- | -------------------------- | +| 400 | `BadRequestError` | +| 401 | `AuthenticationError` | +| 403 | `PermissionDeniedError` | +| 404 | `NotFoundError` | +| 422 | `UnprocessableEntityError` | +| 429 | `RateLimitError` | +| >=500 | `InternalServerError` | +| N/A | `APIConnectionError` | + +### Retries -### Setup Development Environment +Certain errors are automatically retried 2 times by default, with a short exponential backoff. +Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, +429 Rate Limit, and >=500 Internal errors are all retried by default. -```bash -# Install Python 3.10+ -make install-python +You can use the `max_retries` option to configure or disable retry settings: -# Setup virtual environment -make setup +```python +from hanzoai import Hanzo -# Install development dependencies -make dev +# Configure the default for all requests: +client = Hanzo( + # default is 2 + max_retries=0, +) + +# Or, configure per-request: +client.with_options(max_retries=5).get_home() ``` -### Running Tests +### Timeouts -```bash -# Run all tests -make test +By default requests time out after 1 minute. You can configure this with a `timeout` option, +which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object: -# Run specific package tests -make test-hanzo -make test-mcp -make test-agents +```python +from hanzoai import Hanzo -# Run with coverage -make test-coverage +# Configure the default for all requests: +client = Hanzo( + # 20 seconds (default is 1 minute) + timeout=20.0, +) + +# More granular control: +client = Hanzo( + timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0), +) + +# Override per-request: +client.with_options(timeout=5.0).get_home() ``` -### Code Quality +On timeout, an `APITimeoutError` is thrown. -```bash -# Format code -make format +Note that requests that time out are [retried twice by default](#retries). -# Run linting -make lint +## Advanced -# Type checking -make type-check +### Logging + +We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module. + +You can enable logging by setting the environment variable `HANZO_LOG` to `info`. + +```shell +$ export HANZO_LOG=info ``` -### Building Packages +Or to `debug` for more verbose logging. + +### How to tell whether `None` means `null` or missing -```bash -# Build all packages -make build +In an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`: -# Build specific package -cd pkg/hanzo && uv build +```py +if response.my_field is None: + if 'my_field' not in response.model_fields_set: + print('Got json like {}, without a "my_field" key present at all.') + else: + print('Got json like {"my_field": null}.') ``` -## ๐Ÿ“š Documentation +### Accessing raw response data (e.g. headers) -### Package Documentation +The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g., -- [Hanzo CLI Documentation](pkg/hanzo/README.md) -- [MCP Documentation](pkg/hanzo-mcp/README.md) -- [Agents Documentation](pkg/hanzo-agents/README.md) -- [Network Documentation](pkg/hanzo-network/README.md) -- [Memory Documentation](pkg/hanzo-memory/README.md) +```py +from hanzoai import Hanzo -### API Reference +client = Hanzo() +response = client.with_raw_response.get_home() +print(response.headers.get('X-My-Header')) -See the [API documentation](https://docs.hanzo.ai/python-sdk) for detailed API reference. +client = response.parse() # get the object that `get_home()` would have returned +print(client) +``` -## ๐Ÿ”ง Configuration +These methods return an [`APIResponse`](https://github.com/hanzoai/python-sdk/tree/main/src/hanzoai/_response.py) object. -### Environment Variables +The async client returns an [`AsyncAPIResponse`](https://github.com/hanzoai/python-sdk/tree/main/src/hanzoai/_response.py) with the same structure, the only difference being `await`able methods for reading the response content. -```bash -# API Configuration -HANZO_API_KEY=your-api-key -HANZO_BASE_URL=https://api.hanzo.ai +#### `.with_streaming_response` -# Router Configuration -HANZO_ROUTER_URL=http://localhost:4000/v1 +The above interface eagerly reads the full response body when you make the request, which may not always be what you want. -# Node Configuration -HANZO_NODE_URL=http://localhost:8000/v1 +To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. -# Logging -HANZO_LOG_LEVEL=INFO +```python +with client.with_streaming_response.get_home() as response: + print(response.headers.get("X-My-Header")) + + for line in response.iter_lines(): + print(line) ``` -### Configuration File +The context manager is required so that the response will reliably be closed. -Create `~/.hanzo/config.yaml`: +### Making custom/undocumented requests -```yaml -api: - key: your-api-key - base_url: https://api.hanzo.ai +This library is typed for convenient access to the documented API. -router: - url: http://localhost:4000/v1 - -node: - url: http://localhost:8000/v1 - workers: 4 - -logging: - level: INFO -``` +If you need to access undocumented endpoints, params, or response properties, the library can still be used. -## ๐Ÿšข Deployment +#### Undocumented endpoints -### Docker +To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other +http verbs. Options on the client will be respected (such as retries) when making this request. -```bash -# Build image -docker build -t hanzo-sdk . +```py +import httpx -# Run container -docker run -p 8000:8000 hanzo-sdk +response = client.post( + "/foo", + cast_to=httpx.Response, + body={"my_param": True}, +) + +print(response.headers.get("x-foo")) ``` -### Docker Compose +#### Undocumented request params -```bash -# Start all services -docker-compose up +If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request +options. -# Start specific service -docker-compose up router -``` +#### Undocumented response properties -## ๐Ÿค Contributing +To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You +can also get all the extra fields on the Pydantic model as a dict with +[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra). -We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details. +### Configuring the HTTP client -### Development Workflow +You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including: -1. Fork the repository -2. Create feature branch (`git checkout -b feature/amazing-feature`) -3. Make changes and test -4. Commit changes (`git commit -m 'Add amazing feature'`) -5. Push to branch (`git push origin feature/amazing-feature`) -6. Open Pull Request +- Support for [proxies](https://www.python-httpx.org/advanced/proxies/) +- Custom [transports](https://www.python-httpx.org/advanced/transports/) +- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality -### Code Standards +```python +import httpx +from hanzoai import Hanzo, DefaultHttpxClient + +client = Hanzo( + # Or use the `HANZO_BASE_URL` env var + base_url="http://my.test.server.example.com:8083", + http_client=DefaultHttpxClient( + proxy="http://my.test.proxy.example.com", + transport=httpx.HTTPTransport(local_address="0.0.0.0"), + ), +) +``` -- Follow PEP 8 -- Use type hints -- Write tests for new features -- Update documentation -- Run `make lint` before committing +You can also customize the client on a per-request basis by using `with_options()`: + +```python +client.with_options(http_client=DefaultHttpxClient(...)) +``` -## ๐Ÿ“Š Performance +### Managing HTTP resources -### Benchmarks +By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting. -| Operation | Latency | Throughput | -|-----------|---------|------------| -| Chat Completion | 50ms | 20 req/s | -| Embedding | 10ms | 100 req/s | -| Local Inference | 200ms | 5 req/s | +```py +from hanzoai import Hanzo -### Optimization Tips +with Hanzo() as client: + # make requests here + ... -- Use streaming for long responses -- Enable caching for repeated queries -- Use batch operations when possible -- Configure appropriate timeouts +# HTTP client is now closed +``` -## ๐Ÿ”’ Security +## Versioning -- API keys are encrypted at rest -- All communications use TLS 1.3+ -- Regular security audits -- SOC 2 Type II certified +This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions: -Report security issues to security@hanzo.ai +1. Changes that only affect static types, without breaking runtime behavior. +2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_ +3. Changes that we do not expect to impact the vast majority of users in practice. -## ๐Ÿ“„ License +We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. -This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details. +We are keen for your feedback; please open an [issue](https://www.github.com/hanzoai/python-sdk/issues) with questions, bugs, or suggestions. -## ๐Ÿ™ Acknowledgments +### Determining the installed version -- OpenAI for the API specification -- Anthropic for Claude integration -- The open-source community +If you've upgraded to the latest version but aren't seeing any new features you were expecting then your python environment is likely still using an older version. -## ๐Ÿ“ž Support +You can determine the version that is being used at runtime with: -- Documentation: https://docs.hanzo.ai -- Discord: https://discord.gg/hanzo -- Email: support@hanzo.ai -- GitHub Issues: https://github.com/hanzoai/python-sdk/issues +```py +import hanzoai +print(hanzoai.__version__) +``` -## ๐Ÿ—บ๏ธ Roadmap +## Requirements -- [ ] Multi-modal support (images, audio, video) -- [ ] Enhanced caching strategies -- [ ] WebSocket streaming -- [ ] Browser SDK -- [ ] Mobile SDKs (iOS, Android) +Python 3.8 or higher. ---- +## Contributing -Built with โค๏ธ by the Hanzo team \ No newline at end of file +See [the contributing documentation](./CONTRIBUTING.md). diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index 82f246831..000000000 --- a/RELEASE.md +++ /dev/null @@ -1,194 +0,0 @@ -# Release Process for Hanzo Python SDK - -This document describes the automated release process for all Python packages in this repository. - -## ๐Ÿ“ฆ Packages - -The following Python packages are managed in this repository: - -- **hanzo** - Core Hanzo AI SDK -- **hanzo-network** - Network utilities for Hanzo AI -- **hanzo-mcp** - Model Context Protocol implementation -- **hanzo-agents** - Agent framework for Hanzo AI -- **hanzo-memory** - Memory management for AI agents -- **hanzo-aci** - AI Chain Infrastructure -- **hanzo-repl** - REPL for Hanzo AI - -## ๐Ÿš€ Automated Release Process - -### CI/CD Pipeline - -The release process is fully automated through GitHub Actions with two methods: - -#### Method 1: Automatic Version Detection (Recommended) -**Every push to `main` branch automatically:** -1. Runs all tests -2. Checks each package's version against PyPI -3. Publishes any packages with new versions -4. Skips packages already published - -**To release this way:** -```bash -# Update version in pyproject.toml -# Commit and push to main -git add -A -git commit -m "bump: hanzo-mcp to v1.2.3" -git push origin main -# CI automatically publishes if version is new! -``` - -#### Method 2: Tag-based Release -1. **Tests Run First**: All tests must pass before any package is published -2. **Tag-based Triggers**: Pushing a tag triggers the release process -3. **Automatic PyPI Upload**: Packages are automatically built and uploaded to PyPI - -### Workflow Files - -- **`.github/workflows/publish-pypi.yml`**: Main publishing workflow -- **`.github/workflows/hanzo-packages-ci.yml`**: CI with integrated publishing -- **`.github/workflows/test.yml`**: Test suite that must pass - -## ๐Ÿ“ How to Release - -### Option 1: Automatic (Easiest) ๐ŸŽฏ - -Simply update the version and push to main: - -```bash -# Update version in package's pyproject.toml -vim pkg/hanzo-mcp/pyproject.toml # Change version = "1.2.3" - -# Commit and push -git add -A -git commit -m "Release hanzo-mcp v1.2.3" -git push origin main - -# CI will automatically detect and publish the new version! -``` - -### Option 2: Using Tags - -#### Release All Packages - -To release all packages with the same version: - -```bash -# Update version in all pyproject.toml files -# Then create and push a tag -git tag v1.2.3 -git push origin v1.2.3 -``` - -#### Release Individual Package - -To release a specific package: - -```bash -# Update version in the specific package's pyproject.toml -# Then create and push a package-specific tag -git tag hanzo-mcp-1.2.3 -git push origin hanzo-mcp-1.2.3 -``` - -### Tag Naming Convention - -- **`v*`** - Releases all packages (e.g., `v1.2.3`) -- **`hanzo-*`** - Releases the main hanzo package (e.g., `hanzo-1.2.3`) -- **`hanzo-network-*`** - Releases hanzo-network package -- **`hanzo-mcp-*`** - Releases hanzo-mcp package -- **`hanzo-agents-*`** - Releases hanzo-agents package -- **`hanzo-memory-*`** - Releases hanzo-memory package -- **`hanzo-aci-*`** - Releases hanzo-aci package -- **`hanzo-repl-*`** - Releases hanzo-repl package - -## ๐Ÿ”ง Manual Release (Emergency) - -If automated release fails, you can manually publish: - -```bash -# Set PyPI token -export PYPI_TOKEN=your_token_here - -# Publish all packages -./bin/publish-all-packages.sh - -# Or publish specific package -./bin/publish-all-packages.sh hanzo-mcp -``` - -## โœ… Release Checklist - -Before creating a release tag: - -1. [ ] Update version in `pyproject.toml` file(s) -2. [ ] Update CHANGELOG if applicable -3. [ ] Ensure all tests pass locally -4. [ ] Commit all changes -5. [ ] Create and push tag - -## ๐Ÿ”’ Security - -- PyPI tokens are stored as GitHub secrets -- Use `HANZO_PYPI_TOKEN` or `PYPI_TOKEN` secret names -- Tokens have package-specific or organization-wide permissions - -## ๐Ÿ“Š Release Status - -You can monitor release status at: - -- [GitHub Actions](https://github.com/hanzoai/python-sdk/actions) -- [PyPI - hanzo](https://pypi.org/project/hanzo/) -- [PyPI - hanzo-network](https://pypi.org/project/hanzo-network/) -- [PyPI - hanzo-mcp](https://pypi.org/project/hanzo-mcp/) -- [PyPI - hanzo-agents](https://pypi.org/project/hanzo-agents/) -- [PyPI - hanzo-memory](https://pypi.org/project/hanzo-memory/) -- [PyPI - hanzo-aci](https://pypi.org/project/hanzo-aci/) -- [PyPI - hanzo-repl](https://pypi.org/project/hanzo-repl/) - -## ๐Ÿ› Troubleshooting - -### Tests Failing - -If tests fail, the release will not proceed. Check: -- Test logs in GitHub Actions -- Local test execution with `pytest` - -### PyPI Upload Fails - -Common issues: -- Version already exists on PyPI -- Invalid PyPI token -- Network issues - -Solutions: -- Bump version number -- Check GitHub secrets configuration -- Retry the workflow - -### Package Not Publishing - -Ensure: -- Tag matches the naming convention -- Package directory exists in `pkg/` -- `pyproject.toml` is properly configured - -## ๐Ÿ“ˆ Version Management - -We follow semantic versioning (MAJOR.MINOR.PATCH): - -- **MAJOR**: Breaking API changes -- **MINOR**: New features, backward compatible -- **PATCH**: Bug fixes, backward compatible - -## ๐Ÿค Contributing - -When adding a new package: - -1. Create package directory in `pkg/` -2. Add `pyproject.toml` with proper configuration -3. Update CI workflows to include the new package -4. Add package to this documentation - ---- - -For questions or issues, please open an issue on [GitHub](https://github.com/hanzoai/python-sdk/issues). \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md index bada7faaa..0b579e02c 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -16,11 +16,11 @@ before making any information public. ## Reporting Non-SDK Related Security Issues If you encounter security issues that are not directly related to SDKs but pertain to the services -or products provided by Hanzo please follow the respective company's security reporting guidelines. +or products provided by Hanzo, please follow the respective company's security reporting guidelines. ### Hanzo Terms and Policies -Please contact dev@hanzo.ai for any questions or concerns regarding security of our services. +Please contact dev@hanzo.ai for any questions or concerns regarding the security of our services. --- diff --git a/api.md b/api.md index 266413fb4..661cdccc1 100644 --- a/api.md +++ b/api.md @@ -1,657 +1,344 @@ # Hanzo -Types: - -```python -from hanzoai.types import GetHomeResponse -``` - Methods: -- client.get_home() -> object +- client.get_home() -> object # Models -Types: - -```python -from hanzoai.types import ModelListResponse -``` - Methods: -- client.models.list(\*\*params) -> object +- client.models.list(\*\*params) -> object # OpenAI -Types: - -```python -from hanzoai.types import ( - OpenAICreateResponse, - OpenAIRetrieveResponse, - OpenAIUpdateResponse, - OpenAIDeleteResponse, - OpenAIPatchResponse, -) -``` - Methods: -- client.openai.create(endpoint) -> object -- client.openai.retrieve(endpoint) -> object -- client.openai.update(endpoint) -> object -- client.openai.delete(endpoint) -> object -- client.openai.patch(endpoint) -> object +- client.openai.create(endpoint) -> object +- client.openai.retrieve(endpoint) -> object +- client.openai.update(endpoint) -> object +- client.openai.delete(endpoint) -> object +- client.openai.patch(endpoint) -> object ## Deployments -Types: - -```python -from hanzoai.types.openai import DeploymentCompleteResponse, DeploymentEmbedResponse -``` - Methods: -- client.openai.deployments.complete(model) -> object -- client.openai.deployments.embed(model) -> object +- client.openai.deployments.complete(model) -> object +- client.openai.deployments.embed(model) -> object ### Chat -Types: - -```python -from hanzoai.types.openai.deployments import ChatCompleteResponse -``` - Methods: -- client.openai.deployments.chat.complete(model) -> object +- client.openai.deployments.chat.complete(model) -> object # Engines -Types: - -```python -from hanzoai.types import EngineCompleteResponse, EngineEmbedResponse -``` - Methods: -- client.engines.complete(model) -> object -- client.engines.embed(model) -> object +- client.engines.complete(model) -> object +- client.engines.embed(model) -> object ## Chat -Types: - -```python -from hanzoai.types.engines import ChatCompleteResponse -``` - Methods: -- client.engines.chat.complete(model) -> object +- client.engines.chat.complete(model) -> object # Chat ## Completions -Types: - -```python -from hanzoai.types.chat import CompletionCreateResponse -``` - Methods: -- client.chat.completions.create(\*\*params) -> object +- client.chat.completions.create(\*\*params) -> object # Completions -Types: - -```python -from hanzoai.types import CompletionCreateResponse -``` - Methods: -- client.completions.create(\*\*params) -> object +- client.completions.create(\*\*params) -> object # Embeddings -Types: - -```python -from hanzoai.types import EmbeddingCreateResponse -``` - Methods: -- client.embeddings.create(\*\*params) -> object +- client.embeddings.create(\*\*params) -> object # Images ## Generations -Types: - -```python -from hanzoai.types.images import GenerationCreateResponse -``` - Methods: -- client.images.generations.create() -> object +- client.images.generations.create() -> object # Audio ## Speech -Types: - -```python -from hanzoai.types.audio import SpeechCreateResponse -``` - Methods: -- client.audio.speech.create() -> object +- client.audio.speech.create() -> object ## Transcriptions -Types: - -```python -from hanzoai.types.audio import TranscriptionCreateResponse -``` - Methods: -- client.audio.transcriptions.create(\*\*params) -> object +- client.audio.transcriptions.create(\*\*params) -> object # Assistants -Types: - -```python -from hanzoai.types import AssistantCreateResponse, AssistantListResponse, AssistantDeleteResponse -``` - Methods: -- client.assistants.create() -> object -- client.assistants.list() -> object -- client.assistants.delete(assistant_id) -> object +- client.assistants.create() -> object +- client.assistants.list() -> object +- client.assistants.delete(assistant_id) -> object # Threads -Types: - -```python -from hanzoai.types import ThreadCreateResponse, ThreadRetrieveResponse -``` - Methods: -- client.threads.create() -> object -- client.threads.retrieve(thread_id) -> object +- client.threads.create() -> object +- client.threads.retrieve(thread_id) -> object ## Messages -Types: - -```python -from hanzoai.types.threads import MessageCreateResponse, MessageListResponse -``` - Methods: -- client.threads.messages.create(thread_id) -> object -- client.threads.messages.list(thread_id) -> object +- client.threads.messages.create(thread_id) -> object +- client.threads.messages.list(thread_id) -> object ## Runs -Types: - -```python -from hanzoai.types.threads import RunCreateResponse -``` - Methods: -- client.threads.runs.create(thread_id) -> object +- client.threads.runs.create(thread_id) -> object # Moderations -Types: - -```python -from hanzoai.types import ModerationCreateResponse -``` - Methods: -- client.moderations.create() -> object +- client.moderations.create() -> object # Utils Types: ```python -from hanzoai.types import ( - UtilGetSupportedOpenAIParamsResponse, - UtilTokenCounterResponse, - UtilTransformRequestResponse, -) +from hanzoai.types import UtilTokenCounterResponse, UtilTransformRequestResponse ``` Methods: -- client.utils.get_supported_openai_params(\*\*params) -> object -- client.utils.token_counter(\*\*params) -> UtilTokenCounterResponse -- client.utils.transform_request(\*\*params) -> UtilTransformRequestResponse +- client.utils.get_supported_openai_params(\*\*params) -> object +- client.utils.token_counter(\*\*params) -> UtilTokenCounterResponse +- client.utils.transform_request(\*\*params) -> UtilTransformRequestResponse # Model Types: ```python -from hanzoai.types import ( - ConfigurableClientsideParamsCustomAuth, - ModelInfo, - ModelCreateResponse, - ModelDeleteResponse, -) +from hanzoai.types import ConfigurableClientsideParamsCustomAuth, ModelInfo ``` Methods: -- client.model.create(\*\*params) -> object -- client.model.delete(\*\*params) -> object +- client.model.create(\*\*params) -> object +- client.model.delete(\*\*params) -> object ## Info -Types: - -```python -from hanzoai.types.model import InfoListResponse -``` - Methods: -- client.model.info.list(\*\*params) -> object +- client.model.info.list(\*\*params) -> object ## Update Types: ```python -from hanzoai.types.model import UpdateDeployment, UpdateFullResponse, UpdatePartialResponse +from hanzoai.types.model import UpdateDeployment ``` Methods: -- client.model.update.full(\*\*params) -> object -- client.model.update.partial(model_id, \*\*params) -> object +- client.model.update.full(\*\*params) -> object +- client.model.update.partial(model_id, \*\*params) -> object # ModelGroup -Types: - -```python -from hanzoai.types import ModelGroupRetrieveInfoResponse -``` - Methods: -- client.model_group.retrieve_info(\*\*params) -> object +- client.model_group.retrieve_info(\*\*params) -> object # Routes -Types: - -```python -from hanzoai.types import RouteListResponse -``` - Methods: -- client.routes.list() -> object +- client.routes.list() -> object # Responses -Types: - -```python -from hanzoai.types import ResponseCreateResponse, ResponseRetrieveResponse, ResponseDeleteResponse -``` - Methods: -- client.responses.create() -> object -- client.responses.retrieve(response_id) -> object -- client.responses.delete(response_id) -> object +- client.responses.create() -> object +- client.responses.retrieve(response_id) -> object +- client.responses.delete(response_id) -> object ## InputItems -Types: - -```python -from hanzoai.types.responses import InputItemListResponse -``` - Methods: -- client.responses.input_items.list(response_id) -> object +- client.responses.input_items.list(response_id) -> object # Batches -Types: - -```python -from hanzoai.types import ( - BatchCreateResponse, - BatchRetrieveResponse, - BatchListResponse, - BatchCancelWithProviderResponse, - BatchCreateWithProviderResponse, - BatchListWithProviderResponse, - BatchRetrieveWithProviderResponse, -) -``` - Methods: -- client.batches.create(\*\*params) -> object -- client.batches.retrieve(batch_id, \*\*params) -> object -- client.batches.list(\*\*params) -> object -- client.batches.cancel_with_provider(batch_id, \*, provider) -> object -- client.batches.create_with_provider(provider) -> object -- client.batches.list_with_provider(provider, \*\*params) -> object -- client.batches.retrieve_with_provider(batch_id, \*, provider) -> object +- client.batches.create(\*\*params) -> object +- client.batches.retrieve(batch_id, \*\*params) -> object +- client.batches.list(\*\*params) -> object +- client.batches.cancel_with_provider(batch_id, \*, provider) -> object +- client.batches.create_with_provider(provider) -> object +- client.batches.list_with_provider(provider, \*\*params) -> object +- client.batches.retrieve_with_provider(batch_id, \*, provider) -> object ## Cancel -Types: - -```python -from hanzoai.types.batches import CancelCancelResponse -``` - Methods: -- client.batches.cancel.cancel(batch_id, \*\*params) -> object +- client.batches.cancel.cancel(batch_id, \*\*params) -> object # Rerank -Types: - -```python -from hanzoai.types import RerankCreateResponse, RerankCreateV1Response, RerankCreateV2Response -``` - Methods: -- client.rerank.create() -> object -- client.rerank.create_v1() -> object -- client.rerank.create_v2() -> object +- client.rerank.create() -> object +- client.rerank.create_v1() -> object +- client.rerank.create_v2() -> object # FineTuning ## Jobs -Types: - -```python -from hanzoai.types.fine_tuning import ( - HanzoFineTuningJobCreate, - JobCreateResponse, - JobRetrieveResponse, - JobListResponse, -) -``` - Methods: -- client.fine_tuning.jobs.create(\*\*params) -> object -- client.fine_tuning.jobs.retrieve(fine_tuning_job_id, \*\*params) -> object -- client.fine_tuning.jobs.list(\*\*params) -> object +- client.fine_tuning.jobs.create(\*\*params) -> object +- client.fine_tuning.jobs.retrieve(fine_tuning_job_id, \*\*params) -> object +- client.fine_tuning.jobs.list(\*\*params) -> object ### Cancel -Types: - -```python -from hanzoai.types.fine_tuning.jobs import CancelCreateResponse -``` - Methods: -- client.fine_tuning.jobs.cancel.create(fine_tuning_job_id) -> object +- client.fine_tuning.jobs.cancel.create(fine_tuning_job_id) -> object # Credentials Types: ```python -from hanzoai.types import ( - CredentialItem, - CredentialCreateResponse, - CredentialRetrieveResponse, - CredentialUpdateResponse, - CredentialListResponse, - CredentialDeleteResponse, -) +from hanzoai.types import CredentialItem ``` Methods: -- client.credentials.create(\*\*params) -> object -- client.credentials.retrieve(credential_name) -> object -- client.credentials.update(path_credential_name, \*\*params) -> object -- client.credentials.list() -> object -- client.credentials.delete(credential_name) -> object +- client.credentials.create(\*\*params) -> object +- client.credentials.list() -> object +- client.credentials.delete(credential_name) -> object # VertexAI -Types: - -```python -from hanzoai.types import ( - VertexAICreateResponse, - VertexAIRetrieveResponse, - VertexAIUpdateResponse, - VertexAIDeleteResponse, - VertexAIPatchResponse, -) -``` - Methods: -- client.vertex_ai.create(endpoint) -> object -- client.vertex_ai.retrieve(endpoint) -> object -- client.vertex_ai.update(endpoint) -> object -- client.vertex_ai.delete(endpoint) -> object -- client.vertex_ai.patch(endpoint) -> object +- client.vertex_ai.create(endpoint) -> object +- client.vertex_ai.retrieve(endpoint) -> object +- client.vertex_ai.update(endpoint) -> object +- client.vertex_ai.delete(endpoint) -> object +- client.vertex_ai.patch(endpoint) -> object # Gemini -Types: - -```python -from hanzoai.types import ( - GeminiCreateResponse, - GeminiRetrieveResponse, - GeminiUpdateResponse, - GeminiDeleteResponse, - GeminiPatchResponse, -) -``` - Methods: -- client.gemini.create(endpoint) -> object -- client.gemini.retrieve(endpoint) -> object -- client.gemini.update(endpoint) -> object -- client.gemini.delete(endpoint) -> object -- client.gemini.patch(endpoint) -> object +- client.gemini.create(endpoint) -> object +- client.gemini.retrieve(endpoint) -> object +- client.gemini.update(endpoint) -> object +- client.gemini.delete(endpoint) -> object +- client.gemini.patch(endpoint) -> object # Cohere -Types: - -```python -from hanzoai.types import ( - CohereCreateResponse, - CohereRetrieveResponse, - CohereUpdateResponse, - CohereDeleteResponse, - CohereModifyResponse, -) -``` - Methods: -- client.cohere.create(endpoint) -> object -- client.cohere.retrieve(endpoint) -> object -- client.cohere.update(endpoint) -> object -- client.cohere.delete(endpoint) -> object -- client.cohere.modify(endpoint) -> object +- client.cohere.create(endpoint) -> object +- client.cohere.retrieve(endpoint) -> object +- client.cohere.update(endpoint) -> object +- client.cohere.delete(endpoint) -> object +- client.cohere.modify(endpoint) -> object # Anthropic -Types: - -```python -from hanzoai.types import ( - AnthropicCreateResponse, - AnthropicRetrieveResponse, - AnthropicUpdateResponse, - AnthropicDeleteResponse, - AnthropicModifyResponse, -) -``` - Methods: -- client.anthropic.create(endpoint) -> object -- client.anthropic.retrieve(endpoint) -> object -- client.anthropic.update(endpoint) -> object -- client.anthropic.delete(endpoint) -> object -- client.anthropic.modify(endpoint) -> object +- client.anthropic.create(endpoint) -> object +- client.anthropic.retrieve(endpoint) -> object +- client.anthropic.update(endpoint) -> object +- client.anthropic.delete(endpoint) -> object +- client.anthropic.modify(endpoint) -> object # Bedrock -Types: - -```python -from hanzoai.types import ( - BedrockCreateResponse, - BedrockRetrieveResponse, - BedrockUpdateResponse, - BedrockDeleteResponse, - BedrockPatchResponse, -) -``` - Methods: -- client.bedrock.create(endpoint) -> object -- client.bedrock.retrieve(endpoint) -> object -- client.bedrock.update(endpoint) -> object -- client.bedrock.delete(endpoint) -> object -- client.bedrock.patch(endpoint) -> object +- client.bedrock.create(endpoint) -> object +- client.bedrock.retrieve(endpoint) -> object +- client.bedrock.update(endpoint) -> object +- client.bedrock.delete(endpoint) -> object +- client.bedrock.patch(endpoint) -> object # EuAssemblyai -Types: - -```python -from hanzoai.types import ( - EuAssemblyaiCreateResponse, - EuAssemblyaiRetrieveResponse, - EuAssemblyaiUpdateResponse, - EuAssemblyaiDeleteResponse, - EuAssemblyaiPatchResponse, -) -``` - Methods: -- client.eu_assemblyai.create(endpoint) -> object -- client.eu_assemblyai.retrieve(endpoint) -> object -- client.eu_assemblyai.update(endpoint) -> object -- client.eu_assemblyai.delete(endpoint) -> object -- client.eu_assemblyai.patch(endpoint) -> object +- client.eu_assemblyai.create(endpoint) -> object +- client.eu_assemblyai.retrieve(endpoint) -> object +- client.eu_assemblyai.update(endpoint) -> object +- client.eu_assemblyai.delete(endpoint) -> object +- client.eu_assemblyai.patch(endpoint) -> object # Assemblyai -Types: - -```python -from hanzoai.types import ( - AssemblyaiCreateResponse, - AssemblyaiRetrieveResponse, - AssemblyaiUpdateResponse, - AssemblyaiDeleteResponse, - AssemblyaiPatchResponse, -) -``` - Methods: -- client.assemblyai.create(endpoint) -> object -- client.assemblyai.retrieve(endpoint) -> object -- client.assemblyai.update(endpoint) -> object -- client.assemblyai.delete(endpoint) -> object -- client.assemblyai.patch(endpoint) -> object +- client.assemblyai.create(endpoint) -> object +- client.assemblyai.retrieve(endpoint) -> object +- client.assemblyai.update(endpoint) -> object +- client.assemblyai.delete(endpoint) -> object +- client.assemblyai.patch(endpoint) -> object # Azure -Types: - -```python -from hanzoai.types import ( - AzureCreateResponse, - AzureUpdateResponse, - AzureDeleteResponse, - AzureCallResponse, - AzurePatchResponse, -) -``` - Methods: -- client.azure.create(endpoint) -> object -- client.azure.update(endpoint) -> object -- client.azure.delete(endpoint) -> object -- client.azure.call(endpoint) -> object -- client.azure.patch(endpoint) -> object +- client.azure.create(endpoint) -> object +- client.azure.update(endpoint) -> object +- client.azure.delete(endpoint) -> object +- client.azure.call(endpoint) -> object +- client.azure.patch(endpoint) -> object # Langfuse -Types: - -```python -from hanzoai.types import ( - LangfuseCreateResponse, - LangfuseRetrieveResponse, - LangfuseUpdateResponse, - LangfuseDeleteResponse, - LangfusePatchResponse, -) -``` - Methods: -- client.langfuse.create(endpoint) -> object -- client.langfuse.retrieve(endpoint) -> object -- client.langfuse.update(endpoint) -> object -- client.langfuse.delete(endpoint) -> object -- client.langfuse.patch(endpoint) -> object +- client.langfuse.create(endpoint) -> object +- client.langfuse.retrieve(endpoint) -> object +- client.langfuse.update(endpoint) -> object +- client.langfuse.delete(endpoint) -> object +- client.langfuse.patch(endpoint) -> object # Config @@ -660,78 +347,43 @@ Methods: Types: ```python -from hanzoai.types.config import ( - PassThroughEndpointResponse, - PassThroughGenericEndpoint, - PassThroughEndpointCreateResponse, - PassThroughEndpointUpdateResponse, -) +from hanzoai.types.config import PassThroughEndpointResponse, PassThroughGenericEndpoint ``` Methods: -- client.config.pass_through_endpoint.create(\*\*params) -> object -- client.config.pass_through_endpoint.update(endpoint_id) -> object -- client.config.pass_through_endpoint.list(\*\*params) -> PassThroughEndpointResponse -- client.config.pass_through_endpoint.delete(\*\*params) -> PassThroughEndpointResponse +- client.config.pass_through_endpoint.create(\*\*params) -> object +- client.config.pass_through_endpoint.update(endpoint_id) -> object +- client.config.pass_through_endpoint.list(\*\*params) -> PassThroughEndpointResponse +- client.config.pass_through_endpoint.delete(\*\*params) -> PassThroughEndpointResponse # Test -Types: - -```python -from hanzoai.types import TestPingResponse -``` - Methods: -- client.test.ping() -> object +- client.test.ping() -> object # Health -Types: - -```python -from hanzoai.types import ( - HealthCheckAllResponse, - HealthCheckLivelinessResponse, - HealthCheckLivenessResponse, - HealthCheckReadinessResponse, - HealthCheckServicesResponse, -) -``` - Methods: -- client.health.check_all(\*\*params) -> object -- client.health.check_liveliness() -> object -- client.health.check_liveness() -> object -- client.health.check_readiness() -> object -- client.health.check_services(\*\*params) -> object +- client.health.check_all(\*\*params) -> object +- client.health.check_liveliness() -> object +- client.health.check_liveness() -> object +- client.health.check_readiness() -> object +- client.health.check_services(\*\*params) -> object # Active -Types: - -```python -from hanzoai.types import ActiveListCallbacksResponse -``` - Methods: -- client.active.list_callbacks() -> object +- client.active.list_callbacks() -> object # Settings -Types: - -```python -from hanzoai.types import SettingRetrieveResponse -``` - Methods: -- client.settings.retrieve() -> object +- client.settings.retrieve() -> object # Key @@ -741,27 +393,23 @@ Types: from hanzoai.types import ( BlockKeyRequest, GenerateKeyResponse, - KeyUpdateResponse, KeyListResponse, - KeyDeleteResponse, KeyBlockResponse, KeyCheckHealthResponse, - KeyRetrieveInfoResponse, - KeyUnblockResponse, ) ``` Methods: -- client.key.update(\*\*params) -> object -- client.key.list(\*\*params) -> KeyListResponse -- client.key.delete(\*\*params) -> object -- client.key.block(\*\*params) -> Optional[KeyBlockResponse] -- client.key.check_health() -> KeyCheckHealthResponse -- client.key.generate(\*\*params) -> GenerateKeyResponse -- client.key.regenerate_by_key(path_key, \*\*params) -> Optional[GenerateKeyResponse] -- client.key.retrieve_info(\*\*params) -> object -- client.key.unblock(\*\*params) -> object +- client.key.update(\*\*params) -> object +- client.key.list(\*\*params) -> KeyListResponse +- client.key.delete(\*\*params) -> object +- client.key.block(\*\*params) -> Optional[KeyBlockResponse] +- client.key.check_health() -> KeyCheckHealthResponse +- client.key.generate(\*\*params) -> GenerateKeyResponse +- client.key.regenerate_by_key(path_key, \*\*params) -> Optional[GenerateKeyResponse] +- client.key.retrieve_info(\*\*params) -> object +- client.key.unblock(\*\*params) -> object ## Regenerate @@ -776,22 +424,16 @@ from hanzoai.types.key import RegenerateKeyRequest Types: ```python -from hanzoai.types import ( - UserCreateResponse, - UserUpdateResponse, - UserListResponse, - UserDeleteResponse, - UserRetrieveInfoResponse, -) +from hanzoai.types import UserCreateResponse ``` Methods: -- client.user.create(\*\*params) -> UserCreateResponse -- client.user.update(\*\*params) -> object -- client.user.list(\*\*params) -> object -- client.user.delete(\*\*params) -> object -- client.user.retrieve_info(\*\*params) -> object +- client.user.create(\*\*params) -> UserCreateResponse +- client.user.update(\*\*params) -> object +- client.user.list(\*\*params) -> object +- client.user.delete(\*\*params) -> object +- client.user.retrieve_info(\*\*params) -> object # Team @@ -800,64 +442,41 @@ Types: ```python from hanzoai.types import ( BlockTeamRequest, - HanzoModelTable, - HanzoTeamTable, - HanzoUserTable, Member, - TeamUpdateResponse, - TeamListResponse, - TeamDeleteResponse, + TeamCreateResponse, TeamAddMemberResponse, - TeamBlockResponse, - TeamDisableLoggingResponse, - TeamListAvailableResponse, - TeamRemoveMemberResponse, - TeamRetrieveInfoResponse, - TeamUnblockResponse, TeamUpdateMemberResponse, ) ``` Methods: -- client.team.create(\*\*params) -> HanzoTeamTable -- client.team.update(\*\*params) -> object -- client.team.list(\*\*params) -> object -- client.team.delete(\*\*params) -> object -- client.team.add_member(\*\*params) -> TeamAddMemberResponse -- client.team.block(\*\*params) -> object -- client.team.disable_logging(team_id) -> object -- client.team.list_available(\*\*params) -> object -- client.team.remove_member(\*\*params) -> object -- client.team.retrieve_info(\*\*params) -> object -- client.team.unblock(\*\*params) -> object -- client.team.update_member(\*\*params) -> TeamUpdateMemberResponse +- client.team.create(\*\*params) -> TeamCreateResponse +- client.team.update(\*\*params) -> object +- client.team.list(\*\*params) -> object +- client.team.delete(\*\*params) -> object +- client.team.add_member(\*\*params) -> TeamAddMemberResponse +- client.team.block(\*\*params) -> object +- client.team.disable_logging(team_id) -> object +- client.team.list_available(\*\*params) -> object +- client.team.remove_member(\*\*params) -> object +- client.team.retrieve_info(\*\*params) -> object +- client.team.unblock(\*\*params) -> object +- client.team.update_member(\*\*params) -> TeamUpdateMemberResponse ## Model -Types: - -```python -from hanzoai.types.team import ModelAddResponse, ModelRemoveResponse -``` - Methods: -- client.team.model.add(\*\*params) -> object -- client.team.model.remove(\*\*params) -> object +- client.team.model.add(\*\*params) -> object +- client.team.model.remove(\*\*params) -> object ## Callback -Types: - -```python -from hanzoai.types.team import CallbackRetrieveResponse, CallbackAddResponse -``` - Methods: -- client.team.callback.retrieve(team_id) -> object -- client.team.callback.add(team_id, \*\*params) -> object +- client.team.callback.retrieve(team_id) -> object +- client.team.callback.add(team_id, \*\*params) -> object # Organization @@ -865,87 +484,70 @@ Types: ```python from hanzoai.types import ( - BudgetTable, OrgMember, - OrganizationMembershipTable, - OrganizationTableWithMembers, - UserRoles, OrganizationCreateResponse, + OrganizationUpdateResponse, OrganizationListResponse, OrganizationDeleteResponse, OrganizationAddMemberResponse, - OrganizationDeleteMemberResponse, + OrganizationUpdateMemberResponse, ) ``` Methods: -- client.organization.create(\*\*params) -> OrganizationCreateResponse -- client.organization.update(\*\*params) -> OrganizationTableWithMembers -- client.organization.list() -> OrganizationListResponse -- client.organization.delete(\*\*params) -> OrganizationDeleteResponse -- client.organization.add_member(\*\*params) -> OrganizationAddMemberResponse -- client.organization.delete_member(\*\*params) -> object -- client.organization.update_member(\*\*params) -> OrganizationMembershipTable +- client.organization.create(\*\*params) -> OrganizationCreateResponse +- client.organization.update(\*\*params) -> OrganizationUpdateResponse +- client.organization.list() -> OrganizationListResponse +- client.organization.delete(\*\*params) -> OrganizationDeleteResponse +- client.organization.add_member(\*\*params) -> OrganizationAddMemberResponse +- client.organization.delete_member(\*\*params) -> object +- client.organization.update_member(\*\*params) -> OrganizationUpdateMemberResponse ## Info Types: ```python -from hanzoai.types.organization import InfoDeprecatedResponse +from hanzoai.types.organization import InfoRetrieveResponse ``` Methods: -- client.organization.info.retrieve(\*\*params) -> OrganizationTableWithMembers -- client.organization.info.deprecated(\*\*params) -> object +- client.organization.info.retrieve(\*\*params) -> InfoRetrieveResponse +- client.organization.info.deprecated(\*\*params) -> object # Customer Types: ```python -from hanzoai.types import ( - BlockUsers, - HanzoEndUserTable, - CustomerCreateResponse, - CustomerUpdateResponse, - CustomerListResponse, - CustomerDeleteResponse, - CustomerBlockResponse, - CustomerUnblockResponse, -) +from hanzoai.types import BlockUsers, CustomerListResponse, CustomerRetrieveInfoResponse ``` Methods: -- client.customer.create(\*\*params) -> object -- client.customer.update(\*\*params) -> object -- client.customer.list() -> CustomerListResponse -- client.customer.delete(\*\*params) -> object -- client.customer.block(\*\*params) -> object -- client.customer.retrieve_info(\*\*params) -> HanzoEndUserTable -- client.customer.unblock(\*\*params) -> object +- client.customer.create(\*\*params) -> object +- client.customer.update(\*\*params) -> object +- client.customer.list() -> CustomerListResponse +- client.customer.delete(\*\*params) -> object +- client.customer.block(\*\*params) -> object +- client.customer.retrieve_info(\*\*params) -> CustomerRetrieveInfoResponse +- client.customer.unblock(\*\*params) -> object # Spend Types: ```python -from hanzoai.types import ( - HanzoSpendLogs, - SpendCalculateSpendResponse, - SpendListLogsResponse, - SpendListTagsResponse, -) +from hanzoai.types import SpendListLogsResponse, SpendListTagsResponse ``` Methods: -- client.spend.calculate_spend(\*\*params) -> object -- client.spend.list_logs(\*\*params) -> SpendListLogsResponse -- client.spend.list_tags(\*\*params) -> SpendListTagsResponse +- client.spend.calculate_spend(\*\*params) -> object +- client.spend.list_logs(\*\*params) -> SpendListLogsResponse +- client.spend.list_tags(\*\*params) -> SpendListTagsResponse # Global @@ -954,18 +556,14 @@ Methods: Types: ```python -from hanzoai.types.global_ import ( - SpendListTagsResponse, - SpendResetResponse, - SpendRetrieveReportResponse, -) +from hanzoai.types.global_ import SpendListTagsResponse, SpendRetrieveReportResponse ``` Methods: -- client.global*.spend.list*tags(\*\*params) -> SpendListTagsResponse -- client.global*.spend.reset() -> object -- client.global*.spend.retrieve*report(\*\*params) -> SpendRetrieveReportResponse +- client.global*.spend.list*tags(\*\*params) -> SpendListTagsResponse +- client.global*.spend.reset() -> object +- client.global*.spend.retrieve*report(\*\*params) -> SpendRetrieveReportResponse # Provider @@ -977,33 +575,27 @@ from hanzoai.types import ProviderListBudgetsResponse Methods: -- client.provider.list_budgets() -> ProviderListBudgetsResponse +- client.provider.list_budgets() -> ProviderListBudgetsResponse # Cache Types: ```python -from hanzoai.types import CacheDeleteResponse, CacheFlushAllResponse, CachePingResponse +from hanzoai.types import CachePingResponse ``` Methods: -- client.cache.delete() -> object -- client.cache.flush_all() -> object -- client.cache.ping() -> CachePingResponse +- client.cache.delete() -> object +- client.cache.flush_all() -> object +- client.cache.ping() -> CachePingResponse ## Redis -Types: - -```python -from hanzoai.types.cache import RediRetrieveInfoResponse -``` - Methods: -- client.cache.redis.retrieve_info() -> object +- client.cache.redis.retrieve_info() -> object # Guardrails @@ -1015,85 +607,54 @@ from hanzoai.types import GuardrailListResponse Methods: -- client.guardrails.list() -> GuardrailListResponse +- client.guardrails.list() -> GuardrailListResponse # Add Types: ```python -from hanzoai.types import IPAddress, AddAddAllowedIPResponse +from hanzoai.types import IPAddress ``` Methods: -- client.add.add_allowed_ip(\*\*params) -> object +- client.add.add_allowed_ip(\*\*params) -> object # Delete -Types: - -```python -from hanzoai.types import DeleteCreateAllowedIPResponse -``` - Methods: -- client.delete.create_allowed_ip(\*\*params) -> object +- client.delete.create_allowed_ip(\*\*params) -> object # Files -Types: - -```python -from hanzoai.types import ( - FileCreateResponse, - FileRetrieveResponse, - FileListResponse, - FileDeleteResponse, -) -``` - Methods: -- client.files.create(provider, \*\*params) -> object -- client.files.retrieve(file_id, \*, provider) -> object -- client.files.list(provider, \*\*params) -> object -- client.files.delete(file_id, \*, provider) -> object +- client.files.create(provider, \*\*params) -> object +- client.files.retrieve(file_id, \*, provider) -> object +- client.files.list(provider, \*\*params) -> object +- client.files.delete(file_id, \*, provider) -> object ## Content -Types: - -```python -from hanzoai.types.files import ContentRetrieveResponse -``` - Methods: -- client.files.content.retrieve(file_id, \*, provider) -> object +- client.files.content.retrieve(file_id, \*, provider) -> object # Budget Types: ```python -from hanzoai.types import ( - BudgetNew, - BudgetCreateResponse, - BudgetUpdateResponse, - BudgetListResponse, - BudgetDeleteResponse, - BudgetInfoResponse, - BudgetSettingsResponse, -) +from hanzoai.types import BudgetNew ``` Methods: -- client.budget.create(\*\*params) -> object -- client.budget.update(\*\*params) -> object -- client.budget.list() -> object -- client.budget.delete(\*\*params) -> object -- client.budget.info(\*\*params) -> object -- client.budget.settings(\*\*params) -> object +- client.budget.create(\*\*params) -> object +- client.budget.update(\*\*params) -> object +- client.budget.list() -> object +- client.budget.delete(\*\*params) -> object +- client.budget.info(\*\*params) -> object +- client.budget.settings(\*\*params) -> object diff --git a/bin/check-and-publish.py b/bin/check-and-publish.py deleted file mode 100755 index 1645a79f8..000000000 --- a/bin/check-and-publish.py +++ /dev/null @@ -1,294 +0,0 @@ -#!/usr/bin/env python3 -""" -Check package versions and publish to PyPI if newer version is available. -This script is designed to run in CI on every push to main. -""" - -import os -import re -import sys -import json -import subprocess -import urllib.error -import urllib.request -from typing import Tuple, Optional -from pathlib import Path - -# Colors for output -RED = "\033[0;31m" -GREEN = "\033[0;32m" -YELLOW = "\033[1;33m" -BLUE = "\033[0;34m" -NC = "\033[0m" # No Color - - -def print_info(msg: str): - print(f"{GREEN}[INFO]{NC} {msg}") - - -def print_warn(msg: str): - print(f"{YELLOW}[WARN]{NC} {msg}") - - -def print_error(msg: str): - print(f"{RED}[ERROR]{NC} {msg}") - - -def print_action(msg: str): - print(f"{BLUE}[ACTION]{NC} {msg}") - - -def get_local_version(package_dir: Path) -> Optional[str]: - """Extract version from pyproject.toml.""" - pyproject_path = package_dir / "pyproject.toml" - if not pyproject_path.exists(): - return None - - with open(pyproject_path, "r") as f: - content = f.read() - - # Match version = "x.y.z" or version = 'x.y.z' - match = re.search(r'version\s*=\s*["\']([^"\']+)["\']', content) - if match: - return match.group(1) - return None - - -def get_pypi_version(package_name: str) -> Optional[str]: - """Get the latest version from PyPI.""" - url = f"https://pypi.org/pypi/{package_name}/json" - try: - with urllib.request.urlopen(url, timeout=30) as response: # noqa: S310 - data = json.loads(response.read()) - return data.get("info", {}).get("version") - except urllib.error.HTTPError as e: - if e.code == 404: - # Package doesn't exist on PyPI yet - return None - raise - except Exception as e: - print_warn(f"Could not fetch PyPI version for {package_name}: {e}") - return None - - -def parse_version(version: str) -> Tuple[int, ...]: - """Parse version string to tuple for comparison.""" - # Remove any pre-release or build metadata - version = re.split(r"[-+]", version)[0] - return tuple(int(x) for x in version.split(".")) - - -def is_newer_version(local_version: str, pypi_version: Optional[str]) -> bool: - """Check if local version is newer than PyPI version.""" - if pypi_version is None: - # Package doesn't exist on PyPI - return True - - try: - local_tuple = parse_version(local_version) - pypi_tuple = parse_version(pypi_version) - return local_tuple > pypi_tuple - except (ValueError, AttributeError): - # If we can't parse versions, compare as strings - return local_version != pypi_version - - -def build_package(package_dir: Path) -> bool: - """Build the Python package.""" - print_info(f"Building package in {package_dir}") - - # Clean previous builds - for dir_name in ["dist", "build"]: - dir_path = package_dir / dir_name - if dir_path.exists(): - subprocess.run(["rm", "-rf", str(dir_path)], check=True) - - # Remove .egg-info directories - for egg_info in package_dir.glob("*.egg-info"): - subprocess.run(["rm", "-rf", str(egg_info)], check=True) - - # Build the package - result = subprocess.run(["python", "-m", "build"], cwd=package_dir, capture_output=True, text=True) - - if result.returncode != 0: - print_error(f"Build failed: {result.stderr}") - return False - - return True - - -def publish_package(package_dir: Path, package_name: str) -> bool: - """Publish package to PyPI.""" - print_action(f"Publishing {package_name} to PyPI") - - # Check for PyPI token - pypi_token = os.environ.get("PYPI_TOKEN") or os.environ.get("HANZO_PYPI_TOKEN") - if not pypi_token: - print_error("PYPI_TOKEN or HANZO_PYPI_TOKEN environment variable not set") - return False - - # Upload to PyPI - result = subprocess.run( - ["python", "-m", "twine", "upload", "dist/*", "--skip-existing"], - cwd=package_dir, - env={**os.environ, "TWINE_USERNAME": "__token__", "TWINE_PASSWORD": pypi_token}, - capture_output=True, - text=True, - ) - - if result.returncode != 0: - print_error(f"Upload failed: {result.stderr}") - return False - - print_info(f"โœ… Successfully published {package_name}") - return True - - -def check_and_publish_package(package_name: str, package_dir: Path) -> bool: - """Check version and publish if newer.""" - print_info(f"Checking {package_name}...") - - # Get local version - local_version = get_local_version(package_dir) - if not local_version: - print_warn(f"Could not find version for {package_name}") - return False - - print_info(f" Local version: {local_version}") - - # Get PyPI version - pypi_version = get_pypi_version(package_name) - if pypi_version: - print_info(f" PyPI version: {pypi_version}") - else: - print_info(f" PyPI version: Not published yet") - - # Check if we need to publish - if is_newer_version(local_version, pypi_version): - print_action(f"๐Ÿ“ฆ New version detected for {package_name}: {local_version}") - - # Build the package - if not build_package(package_dir): - return False - - # Publish to PyPI - return publish_package(package_dir, package_name) - else: - print_info(f" โœ“ {package_name} is up to date") - return True - - -def main(): - """Main function to check and publish all packages.""" - # Define packages in dependency order - packages = [ - "hanzo-network", - "hanzo-memory", - "hanzo-agents", - "hanzo-aci", - "hanzo-mcp", - "hanzo-dev", - "hanzo", - ] - - # Get repository root - script_dir = Path(__file__).parent - repo_root = script_dir.parent - pkg_dir = repo_root / "pkg" - - # Install required tools - print_info("Installing build tools...") - try: - # Try with pip first - subprocess.run( - [ - sys.executable, - "-m", - "pip", - "install", - "--quiet", - "--upgrade", - "pip", - "build", - "twine", - ], - check=True, - capture_output=True, - ) - except (subprocess.CalledProcessError, FileNotFoundError): - # Fall back to installing without pip upgrade - try: - subprocess.run( - [sys.executable, "-m", "pip", "install", "--quiet", "build", "twine"], - check=True, - capture_output=True, - ) - except Exception: - print_warn("Could not install build tools. Make sure pip, build, and twine are available.") - print_info("You can install them with: pip install build twine") - - # Track results - published = [] - failed = [] - skipped = [] - - # Check and publish each package - for package_name in packages: - package_dir = pkg_dir / package_name - - if not package_dir.exists(): - print_warn(f"Package directory {package_dir} does not exist") - skipped.append(package_name) - continue - - try: - local_version = get_local_version(package_dir) - pypi_version = get_pypi_version(package_name) - - if is_newer_version(local_version, pypi_version): - if check_and_publish_package(package_name, package_dir): - published.append(f"{package_name} ({local_version})") - else: - failed.append(package_name) - else: - skipped.append(f"{package_name} (already at {local_version})") - except Exception as e: - print_error(f"Error processing {package_name}: {e}") - failed.append(package_name) - - # Print summary - print("\n" + "=" * 60) - print("๐Ÿ“Š SUMMARY") - print("=" * 60) - - if published: - print(f"\n{GREEN}โœ… Published ({len(published)}):{NC}") - for pkg in published: - print(f" โ€ข {pkg}") - - if skipped: - print(f"\n{BLUE}โญ๏ธ Skipped ({len(skipped)}):{NC}") - for pkg in skipped: - print(f" โ€ข {pkg}") - - if failed: - print(f"\n{RED}โŒ Failed ({len(failed)}):{NC}") - for pkg in failed: - print(f" โ€ข {pkg}") - sys.exit(1) - - # Set GitHub Actions output if running in CI - if os.environ.get("GITHUB_ACTIONS"): - if published: - print( - f"::notice title=Published Packages::Published {len(published)} packages to PyPI: {', '.join(published)}" - ) - else: - print("::notice title=No Updates::All packages are up to date on PyPI") - - print(f"\n{GREEN}โœจ All packages processed successfully!{NC}") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/bin/check-release-environment b/bin/check-release-environment new file mode 100644 index 000000000..b845b0f4c --- /dev/null +++ b/bin/check-release-environment @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +errors=() + +if [ -z "${PYPI_TOKEN}" ]; then + errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") +fi + +lenErrors=${#errors[@]} + +if [[ lenErrors -gt 0 ]]; then + echo -e "Found the following errors in the release environment:\n" + + for error in "${errors[@]}"; do + echo -e "- $error\n" + done + + exit 1 +fi + +echo "The environment is ready to push releases!" diff --git a/bin/publish-all-packages.sh b/bin/publish-all-packages.sh deleted file mode 100755 index cb84244b8..000000000 --- a/bin/publish-all-packages.sh +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env bash - -# Comprehensive script to publish all Python packages to PyPI -# Usage: ./bin/publish-all-packages.sh [package-name] -# If no package name is provided, all packages will be published - -set -e # Exit on error - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Function to print colored output -print_info() { - echo -e "${GREEN}[INFO]${NC} $1" -} - -print_warn() { - echo -e "${YELLOW}[WARN]${NC} $1" -} - -print_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# Check if PYPI_TOKEN is set -if [ -z "$PYPI_TOKEN" ]; then - print_error "PYPI_TOKEN environment variable is not set" - exit 1 -fi - -# Get the package to publish (if specified) -SPECIFIC_PACKAGE=$1 - -# List of all packages in order of dependency -PACKAGES=( - "hanzo-network" - "hanzo-memory" - "hanzo-agents" - "hanzo-aci" - "hanzo-mcp" - "hanzo-dev" - "hanzo" -) - -# Function to get package version from pyproject.toml -get_version() { - local package=$1 - local pyproject="pkg/$package/pyproject.toml" - if [ -f "$pyproject" ]; then - grep "^version = " "$pyproject" | sed 's/version = "\(.*\)"/\1/' - else - echo "unknown" - fi -} - -# Function to check if package exists on PyPI -check_pypi() { - local package=$1 - local version=$2 - curl -s "https://pypi.org/pypi/$package/$version/json" > /dev/null 2>&1 - return $? -} - -# Function to build and publish a package -publish_package() { - local package=$1 - local package_dir="pkg/$package" - - if [ ! -d "$package_dir" ]; then - print_error "Package directory $package_dir does not exist" - return 1 - fi - - print_info "Processing package: $package" - - # Get version - local version=$(get_version "$package") - print_info "Package version: $version" - - # Check if already published - if check_pypi "$package" "$version"; then - print_warn "Package $package version $version already exists on PyPI, skipping..." - return 0 - fi - - cd "$package_dir" - - # Clean previous builds - print_info "Cleaning previous builds..." - rm -rf dist/ build/ *.egg-info - - # Build the package - print_info "Building package..." - python -m build - - # Upload to PyPI - print_info "Uploading to PyPI..." - python -m twine upload dist/* --skip-existing - - print_info "โœ… Successfully published $package version $version" - - cd - > /dev/null -} - -# Main execution -main() { - print_info "Starting package publication process..." - - # Install required tools - print_info "Installing build tools..." - python -m pip install --quiet --upgrade pip build twine - - # If specific package is provided, publish only that - if [ -n "$SPECIFIC_PACKAGE" ]; then - if [[ " ${PACKAGES[@]} " =~ " ${SPECIFIC_PACKAGE} " ]]; then - publish_package "$SPECIFIC_PACKAGE" - else - print_error "Unknown package: $SPECIFIC_PACKAGE" - print_info "Available packages: ${PACKAGES[*]}" - exit 1 - fi - else - # Publish all packages - print_info "Publishing all packages..." - for package in "${PACKAGES[@]}"; do - publish_package "$package" || { - print_error "Failed to publish $package" - exit 1 - } - done - fi - - print_info "๐ŸŽ‰ All packages published successfully!" -} - -# Run main function -main \ No newline at end of file diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index c515b46e2..000000000 --- a/docs/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -# Dependencies -node_modules/ - -# Next.js -.next/ -out/ - -# Fumadocs generated -.source/ - -# Build -dist/ - -# Misc -.DS_Store -*.pem -*.log - -# Local env -.env*.local - -# Vercel -.vercel - -# TypeScript -*.tsbuildinfo -next-env.d.ts diff --git a/docs/FEATURES.md b/docs/FEATURES.md deleted file mode 100644 index 02a321189..000000000 --- a/docs/FEATURES.md +++ /dev/null @@ -1,223 +0,0 @@ -# Hanzo AI SDK - Unified AI Features - -The Hanzo AI SDK now includes integrated support for agents, MCP (Model Context Protocol), and local AI clusters, providing a complete solution for AI development that is **local, private, and free**. - -## Installation - -```bash -pip install hanzoai - -# Optional: Install additional components -pip install hanzo-agents # For agent networks -pip install hanzo-mcp # For MCP tools -pip install exo-explore # For local AI clusters -``` - -## Features - -### 1. Local AI Clusters - -Run AI models locally on your own hardware or across your network of devices: - -```python -import asyncio -from hanzoai import cluster - -async def run_local_ai(): - # Start a local cluster - my_cluster = await cluster.start_local_cluster( - name="my-ai-cluster", - model_path="~/.cache/huggingface/hub" - ) - - # Run inference locally - result = await my_cluster.inference( - prompt="Hello, local AI!", - model="llama-3.2-3b" - ) - - print(result) - await my_cluster.stop() - -asyncio.run(run_local_ai()) -``` - -### 2. Agent Networks - -Create sophisticated AI agent systems with local and distributed execution: - -```python -from hanzoai import agents - -# Create agents -local_agent = agents.create_agent( - name="local-assistant", - model="llama-3.2-3b", - base_url="http://localhost:8000" # Local cluster -) - -cloud_agent = agents.create_agent( - name="cloud-assistant", - model="anthropic/claude-3-5-sonnet-20241022" -) - -# Create a network -network = agents.create_network( - agents=[local_agent, cloud_agent], - router=agents.state_based_router() # Smart routing -) - -# The network automatically routes tasks to the best agent -result = await network.run("Complex task requiring analysis") -``` - -### 3. MCP Tools - -Access 70+ tools through the Model Context Protocol: - -```python -from hanzoai import mcp - -# Create an MCP server -server = mcp.create_mcp_server( - name="my-tools", - allowed_paths=[".", "/workspace"], - enable_agent_tool=True -) - -# Or connect to an existing MCP server -client = mcp.MCPClient() -await client.connect() - -# Use tools -result = await client.call_tool( - "search", - pattern="def main", - path="." -) -``` - -### 4. Mining Network - -Contribute compute to the network and earn rewards: - -```python -from hanzoai import cluster - -# Join the mining network -miner = await cluster.join_mining_network( - wallet_address="0x1234...", - max_ram=8, # GB - max_vram=4 # GB -) - -# Check mining stats -stats = miner.get_stats() -print(f"Compute contributed: {stats['compute_contributed']}") -print(f"Rewards earned: {stats['rewards_earned']}") -``` - -## Architecture - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Local AI โ”‚ โ”‚ Agent Networks โ”‚ โ”‚ MCP Tools โ”‚ -โ”‚ Cluster โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ โ”‚ -โ”‚ (exo-based) โ”‚ โ”‚ (hanzo-agents) โ”‚ โ”‚ (hanzo-mcp) โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ โ”‚ โ”‚ - โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” - โ”‚ Hanzo AI SDK โ”‚ - โ”‚ (unified API) โ”‚ - โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## Use Cases - -### 1. **Cost-Effective Development** -Use local models for development and testing, only using cloud APIs when necessary: - -```python -# Development: Use local cluster -dev_result = await local_cluster.inference("Test prompt") - -# Production: Use cloud with same interface -prod_result = completion(model="gpt-4", messages=[...]) -``` - -### 2. **Privacy-First Applications** -Keep sensitive data local while still leveraging AI: - -```python -# Process sensitive documents locally -local_agent = agents.create_agent( - name="privacy-agent", - model="llama-3.2-3b", - base_url="http://localhost:8000" -) - -result = await local_agent.process_documents( - documents=sensitive_files, - keep_local=True -) -``` - -### 3. **Distributed AI Workloads** -Distribute work across multiple devices: - -```python -# Create a cluster across your devices -cluster_config = cluster.ClusterConfig( - broadcast_addresses=[ - "192.168.1.100", # Desktop - "192.168.1.101", # Laptop - "192.168.1.102", # Server - ] -) - -distributed_cluster = cluster.HanzoCluster(cluster_config) -await distributed_cluster.start() -``` - -## Best Practices - -1. **Start Local**: Always try local models first for cost savings -2. **Smart Routing**: Use agent networks to automatically route between local and cloud -3. **Cache Models**: Download and cache models locally for offline use -4. **Join Mining**: Contribute unused compute to earn rewards -5. **Use MCP**: Leverage the extensive tool ecosystem - -## Environment Variables - -```bash -# Local cluster configuration -HANZO_CLUSTER_NAME=my-cluster -HANZO_MODEL_PATH=~/.cache/huggingface/hub - -# Mining configuration -HANZO_WALLET_ADDRESS=0x1234... -HANZO_MAX_RAM=16 -HANZO_MAX_VRAM=8 - -# Agent configuration -HANZO_DEFAULT_MODEL=llama-3.2-3b -HANZO_FALLBACK_MODEL=anthropic/claude-3-5-sonnet-20241022 -``` - -## Roadmap - -- [ ] Automatic model downloading and management -- [ ] Peer-to-peer model sharing -- [ ] Federated learning support -- [ ] Mobile device support -- [ ] Enhanced mining rewards system -- [ ] Native GUI for cluster management - -## Support - -For help and support: -- Documentation: https://docs.hanzo.ai -- Discord: https://discord.gg/hanzoai -- GitHub: https://github.com/hanzoai \ No newline at end of file diff --git a/docs/GPT5_ORCHESTRATION.md b/docs/GPT5_ORCHESTRATION.md deleted file mode 100644 index dd0035c2d..000000000 --- a/docs/GPT5_ORCHESTRATION.md +++ /dev/null @@ -1,290 +0,0 @@ -# GPT-5/Codex Orchestration Guide - -## Overview - -Hanzo Dev supports using GPT-5, GPT-4, Codex, and other advanced models as orchestrators for AI-powered code review, development, and system improvements. - -## Available Orchestrator Models - -| Model | Best For | Context Window | Cost | -|-------|----------|----------------|------| -| `gpt-5` | Complex reasoning, architecture | 128K | High | -| `gpt-4o` | Code review, optimization | 128K | Medium | -| `gpt-4-turbo` | Fast iterations | 128K | Medium | -| `gpt-4` | General development | 32K | Medium | -| `codex` | Code generation, completion | 8K | Low | -| `o3` | Advanced reasoning | 200K | High | -| `claude-3-5-sonnet` | Long context, analysis | 200K | Medium | -| `local:llama3.2` | Simple tasks (free!) | 8K | Free | - -## Quick Start - -### 1. Basic GPT-5 Orchestration - -```bash -# Use GPT-5 as the main orchestrator -hanzo dev --orchestrator gpt-5 -``` - -### 2. GPT-4o for Code Review - -```bash -# Optimized GPT-4 for code review -hanzo dev --orchestrator gpt-4o --instances 3 --critic-instances 2 -``` - -### 3. Cost-Optimized Hybrid Mode - -```bash -# GPT-5 orchestrator + local workers (90% cost reduction) -hanzo dev --orchestrator gpt-5 --use-hanzo-net -``` - -This configuration: -- Uses GPT-5 only for high-level orchestration -- Deploys local models for simple tasks -- Routes complex tasks to API models when needed -- Reduces costs by 90% - -## Advanced Usage - -### Comprehensive Code Review - -```bash -hanzo dev --orchestrator gpt-4o \ - --instances 3 \ - --critic-instances 2 \ - --enable-guardrails \ - --workspace /path/to/project -``` - -Then provide review instructions: -``` -Please review the codebase for: -1. Security vulnerabilities -2. Performance bottlenecks -3. Architecture issues -4. Code quality problems -5. Best practice violations - -Generate a detailed report with fixes. -``` - -### Multi-Model Orchestration - -You can combine different models for specialized tasks: - -```python -# In your Python code -from hanzo.dev import HanzoDevOrchestrator - -orchestrator = HanzoDevOrchestrator( - orchestrator_model="gpt-5", # High-level planning - worker_models=[ - "gpt-4o", # Code generation - "codex", # Code completion - "local:llama3.2" # Simple tasks - ], - critic_models=[ - "claude-3-5-sonnet", # Deep analysis - "gpt-4-turbo" # Fast validation - ] -) -``` - -## Cost Optimization Strategies - -### 1. Tiered Routing - -The system automatically routes tasks based on complexity: - -| Task Type | Routed To | Examples | -|-----------|-----------|----------| -| Simple | Local models | Formatting, linting, simple checks | -| Medium | GPT-4-turbo | Refactoring, documentation | -| Complex | GPT-4o/GPT-5 | Architecture, security analysis | -| Critical | GPT-5/O3 | System design, complex debugging | - -### 2. Batch Processing - -Group related tasks for efficient processing: - -```bash -# Process multiple files in parallel -hanzo dev --orchestrator gpt-4o \ - --batch-mode \ - --files "src/**/*.py" -``` - -### 3. Caching and Reuse - -The system caches: -- Common code patterns -- Previous review results -- Frequent queries - -## Model-Specific Features - -### GPT-5 Features -- Advanced reasoning chains -- Multi-step planning -- Cross-codebase analysis -- Architectural recommendations - -### GPT-4o Features -- Optimized for code tasks -- Fast response times -- Excellent at refactoring -- Strong type inference - -### Codex Features -- Code completion -- Docstring generation -- Test generation -- Code translation - -### O3 Features -- Complex problem solving -- Mathematical proofs -- Algorithm optimization -- Security analysis - -## Integration with MCP Tools - -All orchestrators can use MCP tools: - -```bash -# Enable all MCP tools for the orchestrator -hanzo dev --orchestrator gpt-5 --mcp-tools -``` - -Available tools: -- File operations -- Code search -- Git operations -- Browser automation -- Database queries -- API calls - -## Monitoring and Debugging - -### View Orchestrator Decisions - -```bash -# Enable verbose logging -hanzo dev --orchestrator gpt-4o --verbose - -# Monitor in real-time -hanzo dev --orchestrator gpt-5 --monitor -``` - -### Performance Metrics - -The system tracks: -- Token usage per model -- Response times -- Task success rates -- Cost per operation - -View metrics: -```bash -hanzo metrics --orchestrator gpt-5 -``` - -## Best Practices - -1. **Start with GPT-4o**: Good balance of performance and cost -2. **Use local models for simple tasks**: Free and fast -3. **Reserve GPT-5 for complex problems**: Maximum capability when needed -4. **Enable critic agents**: Catch issues early -5. **Use guardrails**: Prevent code degradation -6. **Monitor costs**: Track usage with `hanzo metrics` - -## Example Workflows - -### Security Audit - -```bash -hanzo dev --orchestrator gpt-5 \ - --focus security \ - --workspace . \ - --output security-report.md -``` - -### Performance Optimization - -```bash -hanzo dev --orchestrator gpt-4o \ - --focus performance \ - --profile \ - --suggest-optimizations -``` - -### Architecture Review - -```bash -hanzo dev --orchestrator o3 \ - --focus architecture \ - --generate-diagrams \ - --suggest-patterns -``` - -## Environment Variables - -```bash -# Required for OpenAI models -export OPENAI_API_KEY=sk-... - -# Optional: Custom endpoints -export OPENAI_API_BASE=https://api.openai.com/v1 -export GPT5_ENDPOINT=https://api.openai.com/v1 # When available - -# Cost limits -export HANZO_DAILY_LIMIT=100 # USD -export HANZO_HOURLY_LIMIT=10 # USD -``` - -## Troubleshooting - -### Model Not Available - -If GPT-5 is not yet available: -```bash -# Fallback to GPT-4o -hanzo dev --orchestrator gpt-4o -``` - -### Rate Limits - -Handle rate limits with: -```bash -# Add retry logic and backoff -hanzo dev --orchestrator gpt-4o \ - --retry-on-rate-limit \ - --max-retries 3 -``` - -### High Costs - -Reduce costs with: -```bash -# Use local models for most tasks -hanzo dev --orchestrator gpt-4o \ - --use-hanzo-net \ - --prefer-local -``` - -## Future Features - -- **GPT-5 Vision**: Code review from screenshots -- **Voice Control**: Natural language commands -- **Auto-fix**: Automatic issue resolution -- **Continuous Monitoring**: 24/7 code quality checks -- **Team Collaboration**: Multiple orchestrators working together - -## Support - -For issues or questions: -- GitHub: https://github.com/hanzoai/python-sdk -- Discord: https://discord.gg/hanzoai -- Email: support@hanzo.ai \ No newline at end of file diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md deleted file mode 100644 index 376668a46..000000000 --- a/docs/QUICKSTART.md +++ /dev/null @@ -1,220 +0,0 @@ -# ๐Ÿš€ Quick Start: Run GPT-5 Pro + Codex Orchestration - -## Step 1: Check Installation - -```bash -# Check if hanzo is installed -hanzo --version - -# If not installed, install it: -pip install hanzo - -# Or install from this directory: -pip install -e pkg/hanzo/ -``` - -## Step 2: Set API Keys - -```bash -# Set your OpenAI API key (required for GPT-5/Codex) -export OPENAI_API_KEY="sk-..." - -# Optional: Set Anthropic key for Claude models -export ANTHROPIC_API_KEY="sk-ant-..." - -# Optional: Set hanzo router endpoint if using router mode -export HANZO_ROUTER_URL="http://localhost:4000" -``` - -## Step 3: Run Different Configurations - -### Option A: GPT-5 Pro + Codex (Best for Code) -```bash -hanzo dev --orchestrator gpt-5-pro-codex -``` - -### Option B: Via Hanzo Router -```bash -# First, start the router (in another terminal) -hanzo router start - -# Then run dev with router -hanzo dev --orchestrator router:gpt-5 -``` - -### Option C: Direct Codex Mode -```bash -hanzo dev --orchestrator codex -``` - -### Option D: Cost-Optimized (90% Savings) -```bash -# Start local AI first -hanzo net --models llama-3.2-3b --port 52415 - -# Then run with hybrid mode -hanzo dev --orchestrator cost-optimized --use-hanzo-net -``` - -## Step 4: Interactive Commands - -Once running, you can interact with the orchestrator: - -```bash -# In the hanzo dev REPL: -> review my code for security issues -> generate a REST API for user management -> refactor this function for better performance -> add comprehensive tests to this module -> explain this architecture decision -``` - -## Complete Example Session - -```bash -# Terminal 1: Start local AI (optional, for cost savings) -$ hanzo net --models llama-3.2-3b -Starting Hanzo Net Compute Node -โœ“ Model loaded: llama-3.2-3b -Serving at http://localhost:52415 - -# Terminal 2: Start hanzo router (optional, for router mode) -$ hanzo router start -Hanzo Router v1.74.3 -Serving at http://localhost:4000 -Connected providers: OpenAI, Anthropic, Google, Mistral - -# Terminal 3: Run the orchestrator -$ hanzo dev --orchestrator gpt-5-pro-codex --instances 3 -Orchestrator Configuration - Mode: hybrid - Primary Model: gpt-5-pro - Codex Model: code-davinci-002 - Cost Optimization: Enabled - -Hanzo Dev - AI Coding OS -โœ“ GPT-5 Pro orchestrator initialized -โœ“ Codex connected for code generation -โœ“ 3 worker agents ready -โœ“ MCP tools enabled -โœ“ Cost-optimized routing active - -Ready for commands... -> -``` - -## Available Commands in REPL - -```bash -> help # Show available commands -> status # Show agent status -> review # Review code file -> generate # Generate code -> refactor # Refactor existing code -> test # Generate tests -> debug # Debug an issue -> explain # Explain code/architecture -> optimize # Optimize performance -> secure # Security audit -> document # Generate documentation -``` - -## Monitor Performance - -```bash -# In another terminal, monitor the orchestrator -$ hanzo dev --monitor -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Orchestrator Status โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ Model: GPT-5 Pro โ”‚ -โ”‚ Workers: 3/3 active โ”‚ -โ”‚ Tasks: 12 completed, 2 pending โ”‚ -โ”‚ Cost: $0.45 (this session) โ”‚ -โ”‚ Tokens: 45,231 / 128,000 โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## Troubleshooting - -```bash -# If you get API key errors: -echo $OPENAI_API_KEY # Check if set -export OPENAI_API_KEY="sk-..." # Set it - -# If hanzo command not found: -pip install hanzo # Install globally -# OR -python -m hanzo.cli dev --orchestrator gpt-5-pro-codex # Run as module - -# If port already in use: -hanzo dev --orchestrator gpt-5-pro-codex --hanzo-net-port 52416 - -# Check logs: -tail -f ~/.hanzo/dev/logs/orchestrator.log -``` - -## Cost Tracking - -```bash -# Check your usage -$ hanzo metrics -Today's Usage: - GPT-5 Pro: $2.45 (16,300 tokens) - Codex: $0.80 (40,000 tokens) - GPT-4o: $1.20 (24,000 tokens) - Local Models: $0.00 (120,000 tokens) - Total: $4.45 - Savings: $12.55 (74% saved via optimization) -``` - -## Pro Tips - -1. **Start with cost-optimized mode** to save money while testing -2. **Use router mode** for automatic failover between providers -3. **Enable monitoring** to track performance and costs -4. **Use local models** for simple tasks (formatting, linting) -5. **Reserve GPT-5 Pro** for complex architectural decisions - -## Full Production Setup - -```bash -#!/bin/bash -# save as: start-hanzo-dev.sh - -# Start local AI -echo "Starting local AI..." -hanzo net --models llama-3.2-3b --port 52415 & -LOCAL_PID=$! - -# Wait for local AI to be ready -sleep 5 - -# Start router (optional) -echo "Starting hanzo router..." -hanzo router start --port 4000 & -ROUTER_PID=$! - -# Wait for router -sleep 3 - -# Start orchestrator with GPT-5 Pro + Codex -echo "Starting GPT-5 Pro + Codex orchestrator..." -hanzo dev \ - --orchestrator gpt-5-pro-codex \ - --instances 3 \ - --critic-instances 2 \ - --enable-guardrails \ - --use-hanzo-net \ - --workspace . \ - --monitor - -# Cleanup on exit -trap "kill $LOCAL_PID $ROUTER_PID" EXIT -``` - -Make it executable and run: -```bash -chmod +x start-hanzo-dev.sh -./start-hanzo-dev.sh -``` \ No newline at end of file diff --git a/docs/TRAINING.md b/docs/TRAINING.md deleted file mode 100644 index 95cb0d345..000000000 --- a/docs/TRAINING.md +++ /dev/null @@ -1,227 +0,0 @@ -# Zen Coder Training Documentation - -This document describes the training infrastructure and methodology for the Zen Coder model family. - -## Overview - -Zen Coder is a code-specialized LLM trained on the **Zen Agentic Dataset** - a curated collection of: -- Git commit history and diffs from 1,452+ repositories -- Claude Code debug sessions (agentic interactions) -- Code review and documentation examples -- Multi-language programming samples - -## Dataset Statistics - -| Metric | Value | -|--------|-------| -| Total Tokens | ~8.47B | -| Training Samples | 1.44M | -| Validation Samples | 75K | -| Prepared Data Size | 4 GB | -| Source Repositories | 1,452+ | - -### Data Sources - -1. **Git History** (~12GB) - - Commit messages and diffs - - Full source files at each commit - - Author metadata - -2. **Claude Debug Sessions** (~12GB) - - Real agentic coding interactions - - Tool usage patterns - - Multi-turn problem solving - -3. **Claude Full Conversations** (~2GB) - - Extended coding sessions - - Architecture discussions - - Code review examples - -4. **Additional Sources** (~4GB) - - Internal development logs - - Documentation examples - - Test cases - -## Training Configuration - -### Base Model -- **Model**: `Qwen/Qwen3-4B-Instruct-2507` -- **Parameters**: 4B -- **Architecture**: Qwen3 transformer - -### LoRA Configuration - -```yaml -fine_tune_type: lora -num_layers: -1 # ALL layers -batch_size: 1 -grad_accumulation: 8 # Effective batch = 8 -learning_rate: 1e-5 -optimizer: adamw -max_seq_length: 4096 -mask_prompt: true # Train on completions only -grad_checkpoint: true # Memory optimization -``` - -### Training Parameters - -| Parameter | Value | -|-----------|-------| -| Total Iterations | 50,000 | -| Checkpoint Every | 1,000 iters | -| Eval Every | 500 iters | -| Estimated Time | ~3 days | - -## Data Preparation - -### Chunking Strategy - -Long sequences are chunked to fit within the 4096 token context: - -```python -MAX_CHARS_PER_CHUNK = 12000 # ~3K tokens per chunk - -def chunk_messages(messages, max_chars): - """Split long message sequences into manageable chunks.""" - # Split on paragraph boundaries when possible - # Each chunk gets system prompt prepended - # Maintains conversation context -``` - -### Format Conversion - -All data is converted to the `messages` format: - -```json -{ - "messages": [ - {"role": "system", "content": "You are Zen Coder..."}, - {"role": "user", "content": "Explain this code..."}, - {"role": "assistant", "content": "This code..."} - ] -} -``` - -### Supported Input Formats - -The pipeline handles multiple source formats: - -1. **Messages format** - Direct passthrough -2. **Conversations format** - OpenAI-style with `from`/`value` -3. **Prompt/Completion** - Simple pairs -4. **Git content** - Commits, diffs, files -5. **Debug sessions** - Claude Code format - -## Training Infrastructure - -### Hardware -- Apple Silicon (M-series) -- MLX framework for Apple GPU acceleration -- 64GB unified memory - -### Software Stack -- `mlx-lm` - MLX language model training -- `tiktoken` - Token counting (cl100k_base) -- Custom data preparation pipeline - -## Checkpoints - -Checkpoints are saved every 1,000 iterations: - -``` -adapters/ -โ”œโ”€โ”€ 0001000_adapters.safetensors (63MB) -โ”œโ”€โ”€ 0002000_adapters.safetensors -โ”œโ”€โ”€ ... -โ”œโ”€โ”€ 0050000_adapters.safetensors -โ””โ”€โ”€ adapters.safetensors (latest) -``` - -## Usage - -### Training - -```bash -cd /path/to/zen-coder/training - -# Start training -python train_full.py - -# Resume from checkpoint -python train_full.py --resume - -# Check status -python train_full.py --status -``` - -### Monitoring - -```bash -# Watch training progress -tail -f full_training.log - -# Check status JSON -cat training_status.json -``` - -### Inference with Adapter - -```python -from mlx_lm import load, generate - -model, tokenizer = load( - "Qwen/Qwen3-4B-Instruct-2507", - adapter_path="./adapters" -) - -response = generate( - model, tokenizer, - prompt="Explain this Python code:\n\ndef fibonacci(n):\n ...", - max_tokens=500 -) -``` - -## HuggingFace - -- **Private dataset**: `zenlm/zen-agentic-dataset` -- **Public card**: `hanzoai/zen-agentic-dataset` -- **Model**: `zenlm/zen-coder-4b-instruct` (after training) - -## Best Practices Applied - -1. **Train on completions only** (`--mask-prompt`) - - Loss computed only on assistant responses - - User/system tokens masked - -2. **LoRA on ALL layers** (`--num-layers -1`) - - Better adaptation than attention-only - - Moderate rank for regularization - -3. **Gradient accumulation** - - Effective batch size 8 with batch=1 - - Memory efficient - -4. **Gradient checkpointing** - - Reduces memory footprint - - Enables longer sequences - -5. **Data chunking** - - Samples split to fit context - - Preserves conversation structure - -## Training Progress - -Track training metrics: - -| Iteration | Train Loss | Notes | -|-----------|------------|-------| -| 1 | 2.19 | Initial | -| 1,000 | 1.5x | First checkpoint | -| 10,000 | 1.3x | Steady improvement | -| 50,000 | TBD | Final | - -## References - -- [MLX-LM Documentation](https://github.com/ml-explore/mlx-examples/tree/main/llms) -- [LoRA Paper](https://arxiv.org/abs/2106.09685) -- [Qwen3 Technical Report](https://qwenlm.github.io/blog/qwen3/) diff --git a/docs/agent/config.md b/docs/agent/config.md deleted file mode 100644 index 80887bb24..000000000 --- a/docs/agent/config.md +++ /dev/null @@ -1,170 +0,0 @@ -# Configuration - -Configure agent behavior with `RunConfig` and `ModelSettings`. - -## RunConfig - -Global settings for an agent run: - -```python -from agents import Runner, RunConfig, ModelSettings - -config = RunConfig( - model="gpt-4o", - model_settings=ModelSettings(temperature=0.7), - max_turns=20, -) - -result = await Runner.run(agent, "Hello!", run_config=config) -``` - -## RunConfig Options - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `model` | `str \| Model` | None | Override all agent models | -| `model_provider` | `ModelProvider` | OpenAI | Model provider | -| `model_settings` | `ModelSettings` | None | Global model settings | -| `max_turns` | `int` | 10 | Max conversation turns | -| `input_guardrails` | `list` | None | Global input guardrails | -| `output_guardrails` | `list` | None | Global output guardrails | -| `handoff_input_filter` | `HandoffInputFilter` | None | Global handoff filter | -| `tracing_disabled` | `bool` | False | Disable tracing | - -## ModelSettings - -Fine-tune model behavior: - -```python -from agents import Agent, ModelSettings - -settings = ModelSettings( - temperature=0.7, - top_p=0.9, - max_tokens=1000, - presence_penalty=0.0, - frequency_penalty=0.0, -) - -agent = Agent( - name="creative", - instructions="Be creative.", - model_settings=settings, -) -``` - -## ModelSettings Options - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `temperature` | `float` | 1.0 | Randomness (0-2) | -| `top_p` | `float` | 1.0 | Nucleus sampling | -| `max_tokens` | `int` | None | Max output tokens | -| `presence_penalty` | `float` | 0.0 | Presence penalty | -| `frequency_penalty` | `float` | 0.0 | Frequency penalty | -| `stop` | `list[str]` | None | Stop sequences | - -## Model Selection - -### By String - -```python -agent = Agent(name="gpt4", model="gpt-4o") -agent = Agent(name="claude", model="claude-3-5-sonnet-20241022") -``` - -### Override at Runtime - -```python -config = RunConfig(model="gpt-4o-mini") -result = await Runner.run(agent, "Hello!", run_config=config) -``` - -## Model Providers - -### OpenAI (default) - -```python -from agents import OpenAIProvider - -provider = OpenAIProvider( - api_key="sk-...", - base_url="https://api.openai.com/v1", -) - -config = RunConfig(model_provider=provider) -``` - -### Hanzo Node - -```python -from agents import create_hanzo_node_provider - -provider = create_hanzo_node_provider( - api_key="your-hanzo-key", - base_url="https://api.hanzo.ai/v1", -) - -config = RunConfig(model_provider=provider) -``` - -### Custom Provider - -```python -from agents import ModelProvider, Model - -class MyProvider(ModelProvider): - def get_model(self, model_name: str) -> Model: - return MyCustomModel(model_name) -``` - -## Environment Variables - -| Variable | Description | -|----------|-------------| -| `OPENAI_API_KEY` | OpenAI API key | -| `ANTHROPIC_API_KEY` | Anthropic API key | -| `HANZO_API_KEY` | Hanzo API key | - -## Default Model - -Set a default model for all agents: - -```python -import agents - -# Set default -agents.set_default_model("gpt-4o") - -# All agents use gpt-4o unless overridden -agent = Agent(name="default", instructions="...") -``` - -## Configuration Hierarchy - -Settings are applied in order (later overrides earlier): - -1. Default settings -2. Agent-level settings (`agent.model_settings`) -3. RunConfig settings (`config.model_settings`) - -```python -# Agent settings -agent = Agent( - model_settings=ModelSettings(temperature=0.5), -) - -# RunConfig overrides -config = RunConfig( - model_settings=ModelSettings(temperature=0.9), -) - -# Result uses temperature=0.9 -result = await Runner.run(agent, "Hello!", run_config=config) -``` - -## See Also - -- [Agents](agents.md) - Agent configuration -- [Models](models.md) - Model details -- [Running Agents](running_agents.md) - Using RunConfig diff --git a/docs/agent/context.md b/docs/agent/context.md deleted file mode 100644 index 4bd6d96fe..000000000 --- a/docs/agent/context.md +++ /dev/null @@ -1,187 +0,0 @@ -# Context - -The context is a mutable object passed throughout an agent run. - -## Basic Context - -```python -from dataclasses import dataclass -from agents import Agent, Runner - -@dataclass -class MyContext: - user_id: str - session_id: str - request_count: int = 0 - -context = MyContext(user_id="123", session_id="abc") -result = await Runner.run(agent, "Hello!", context=context) -``` - -## Accessing Context in Tools - -```python -from agents import function_tool, RunContextWrapper - -@function_tool -def get_user_data(ctx: RunContextWrapper[MyContext]) -> str: - """Get data for the current user.""" - user_id = ctx.context.user_id - return f"Data for user {user_id}" - -@function_tool -def increment_count(ctx: RunContextWrapper[MyContext]) -> str: - """Increment request count.""" - ctx.context.request_count += 1 - return f"Count: {ctx.context.request_count}" -``` - -## Context in Instructions - -Dynamic instructions based on context: - -```python -from agents import Agent, RunContextWrapper - -def dynamic_instructions(ctx: RunContextWrapper[MyContext], agent: Agent) -> str: - user = ctx.context.user_id - count = ctx.context.request_count - return f"""You are helping user {user}. -This is request #{count} in this session. -Be concise and helpful.""" - -agent = Agent( - name="contextual", - instructions=dynamic_instructions, -) -``` - -## Context in Guardrails - -```python -from agents import input_guardrail, InputGuardrailResult, RunContextWrapper - -@input_guardrail -async def check_permissions( - ctx: RunContextWrapper[MyContext], - agent, - input_text: str -) -> InputGuardrailResult: - """Check user permissions.""" - if ctx.context.user_id not in allowed_users: - return InputGuardrailResult( - tripwire_triggered=True, - output_info="User not authorized." - ) - return InputGuardrailResult(tripwire_triggered=False) -``` - -## Context in Handoffs - -```python -from agents import handoff, Handoff, RunContextWrapper - -@handoff -def conditional_handoff(ctx: RunContextWrapper[MyContext]) -> Handoff | None: - """Hand off based on context.""" - if ctx.context.user_id.startswith("vip_"): - return Handoff(target=vip_agent) - return None -``` - -## RunContextWrapper - -The wrapper provides additional functionality: - -```python -from agents import RunContextWrapper - -@function_tool -def example(ctx: RunContextWrapper[MyContext]) -> str: - # Access your context - user = ctx.context.user_id - - # Access run metadata - run_id = ctx.run_id - - # Check current agent - agent_name = ctx.current_agent.name - - return f"User: {user}, Run: {run_id}, Agent: {agent_name}" -``` - -## Context Types - -### Dataclass (Recommended) - -```python -from dataclasses import dataclass, field - -@dataclass -class AppContext: - user_id: str - permissions: list[str] = field(default_factory=list) - metadata: dict = field(default_factory=dict) -``` - -### Pydantic Model - -```python -from pydantic import BaseModel - -class AppContext(BaseModel): - user_id: str - permissions: list[str] = [] - - class Config: - extra = "allow" # Allow additional fields -``` - -### Simple Dict - -```python -# Works but not type-safe -context = {"user_id": "123", "data": {}} -result = await Runner.run(agent, "Hello!", context=context) -``` - -## Context Mutation - -Context can be modified during the run: - -```python -@function_tool -def update_context(ctx: RunContextWrapper[MyContext], key: str, value: str) -> str: - """Update context metadata.""" - ctx.context.metadata[key] = value - return f"Updated {key}" -``` - -## Context Persistence - -For multi-turn conversations: - -```python -# Store context between requests -contexts: dict[str, MyContext] = {} - -async def handle_message(session_id: str, message: str): - # Get or create context - if session_id not in contexts: - contexts[session_id] = MyContext( - user_id="user_123", - session_id=session_id, - ) - - context = contexts[session_id] - result = await Runner.run(agent, message, context=context) - - # Context is updated in place - return result.final_output -``` - -## See Also - -- [Tools](tools.md) - Using context in tools -- [Guardrails](guardrails.md) - Context in guardrails -- [Running Agents](running_agents.md) - Passing context diff --git a/docs/agent/guardrails.md b/docs/agent/guardrails.md deleted file mode 100644 index e15b48844..000000000 --- a/docs/agent/guardrails.md +++ /dev/null @@ -1,185 +0,0 @@ -# Guardrails - -Guardrails validate agent inputs and outputs to ensure safety and quality. - -## Input Guardrails - -Validate user input before the agent processes it: - -```python -from agents import Agent, input_guardrail, InputGuardrailResult - -@input_guardrail -async def check_input_length(ctx, agent, input_text: str) -> InputGuardrailResult: - """Reject inputs that are too long.""" - if len(input_text) > 10000: - return InputGuardrailResult( - tripwire_triggered=True, - output_info="Input too long. Please shorten your message." - ) - return InputGuardrailResult(tripwire_triggered=False) - -agent = Agent( - name="guarded", - instructions="Be helpful.", - input_guardrails=[check_input_length], -) -``` - -## Output Guardrails - -Validate agent output before returning to user: - -```python -from agents import Agent, output_guardrail, OutputGuardrailResult - -@output_guardrail -async def check_pii(ctx, agent, output: str) -> OutputGuardrailResult: - """Block outputs containing PII.""" - pii_patterns = ["SSN:", "credit card:", "password:"] - - for pattern in pii_patterns: - if pattern.lower() in output.lower(): - return OutputGuardrailResult( - tripwire_triggered=True, - output_info="Response contained sensitive information." - ) - - return OutputGuardrailResult(tripwire_triggered=False) - -agent = Agent( - name="safe_agent", - instructions="Help users with their accounts.", - output_guardrails=[check_pii], -) -``` - -## Guardrail Results - -### Input Guardrail Result - -```python -@dataclass -class InputGuardrailResult: - tripwire_triggered: bool # True to block - output_info: str | None = None # Reason for blocking -``` - -### Output Guardrail Result - -```python -@dataclass -class OutputGuardrailResult: - tripwire_triggered: bool # True to block - output_info: str | None = None # Reason for blocking -``` - -## Guardrail with Context - -```python -from dataclasses import dataclass -from agents import input_guardrail, InputGuardrailResult, RunContextWrapper - -@dataclass -class AppContext: - user_tier: str - rate_limit: int - -@input_guardrail -async def rate_limit_check( - ctx: RunContextWrapper[AppContext], - agent, - input_text: str -) -> InputGuardrailResult: - """Check rate limits based on user tier.""" - if ctx.context.user_tier == "free" and ctx.context.rate_limit <= 0: - return InputGuardrailResult( - tripwire_triggered=True, - output_info="Rate limit exceeded. Please upgrade." - ) - return InputGuardrailResult(tripwire_triggered=False) -``` - -## LLM-Based Guardrails - -Use another LLM to check content: - -```python -from agents import input_guardrail, InputGuardrailResult, Agent, Runner - -moderation_agent = Agent( - name="moderator", - instructions="Return 'SAFE' or 'UNSAFE: reason' for the given text.", -) - -@input_guardrail -async def llm_moderation(ctx, agent, input_text: str) -> InputGuardrailResult: - """Use an LLM to moderate content.""" - result = await Runner.run(moderation_agent, input_text) - - if result.final_output.startswith("UNSAFE"): - return InputGuardrailResult( - tripwire_triggered=True, - output_info=result.final_output - ) - - return InputGuardrailResult(tripwire_triggered=False) -``` - -## Global Guardrails - -Apply guardrails to all runs: - -```python -from agents import Runner, RunConfig - -config = RunConfig( - input_guardrails=[check_input_length, rate_limit_check], - output_guardrails=[check_pii], -) - -result = await Runner.run(agent, user_input, run_config=config) -``` - -## Handling Tripwires - -```python -from agents import ( - Runner, - InputGuardrailTripwireTriggered, - OutputGuardrailTripwireTriggered, -) - -try: - result = await Runner.run(agent, user_input) -except InputGuardrailTripwireTriggered as e: - print(f"Input blocked: {e.guardrail_result.output_info}") -except OutputGuardrailTripwireTriggered as e: - print(f"Output blocked: {e.guardrail_result.output_info}") -``` - -## Multiple Guardrails - -Guardrails run in order. First tripwire stops execution: - -```python -agent = Agent( - name="multi_guard", - instructions="...", - input_guardrails=[ - check_length, # Runs first - check_language, # Runs second - check_content, # Runs third - ], - output_guardrails=[ - check_pii, - check_formatting, - ], -) -``` - -## See Also - -- [Agents](agents.md) - Creating agents -- [Running Agents](running_agents.md) - Execution -- [Tracing](tracing.md) - Debug guardrails diff --git a/docs/agent/handoffs.md b/docs/agent/handoffs.md deleted file mode 100644 index 471258280..000000000 --- a/docs/agent/handoffs.md +++ /dev/null @@ -1,155 +0,0 @@ -# Handoffs - -Handoffs allow agents to delegate tasks to specialized sub-agents. - -## Basic Handoffs - -```python -from agents import Agent - -# Specialist agents -billing_agent = Agent( - name="billing", - instructions="Handle billing inquiries, refunds, and payments.", - handoff_description="Handles billing, payments, and refunds.", -) - -tech_agent = Agent( - name="tech_support", - instructions="Help with technical issues and troubleshooting.", - handoff_description="Handles technical support and troubleshooting.", -) - -# Main agent with handoffs -main_agent = Agent( - name="support", - instructions="Route customers to the appropriate specialist.", - handoffs=[billing_agent, tech_agent], -) -``` - -## Handoff Decorator - -For more control over handoff behavior: - -```python -from agents import Agent, handoff, Handoff - -@handoff -def to_billing(context) -> Handoff: - """Transfer to billing specialist.""" - return Handoff( - target=billing_agent, - input_filter=lambda items: items[-5:], # Last 5 messages - ) - -main_agent = Agent( - name="support", - instructions="Route to specialists as needed.", - handoffs=[to_billing], -) -``` - -## Handoff Input Filter - -Control what conversation history transfers: - -```python -from agents import Handoff, HandoffInputFilter - -def last_n_messages(n: int) -> HandoffInputFilter: - """Only send last N messages to new agent.""" - def filter_fn(items): - return items[-n:] - return filter_fn - -billing_handoff = Handoff( - target=billing_agent, - input_filter=last_n_messages(3), -) -``` - -## Conditional Handoffs - -```python -from agents import Agent, handoff, Handoff, RunContextWrapper - -@handoff -def smart_handoff(ctx: RunContextWrapper) -> Handoff | None: - """Conditionally hand off based on context.""" - if ctx.context.get("is_vip"): - return Handoff(target=vip_agent) - elif ctx.context.get("issue_type") == "billing": - return Handoff(target=billing_agent) - return None # No handoff -``` - -## Handoff Data - -Pass data to the new agent: - -```python -from agents import Handoff, HandoffInputData - -handoff = Handoff( - target=specialist_agent, - input_data=HandoffInputData( - summary="Customer needs help with order #12345", - metadata={"order_id": "12345", "priority": "high"}, - ), -) -``` - -## Tracking Handoffs - -```python -from agents import Runner, RunHooks - -class HandoffTracker(RunHooks): - async def on_handoff(self, context, from_agent, to_agent): - print(f"Handoff: {from_agent.name} -> {to_agent.name}") - -result = await Runner.run( - main_agent, - "I need help with my bill", - run_hooks=HandoffTracker(), -) - -# Check which agent completed the run -print(f"Handled by: {result.last_agent.name}") -``` - -## Recursive Handoffs - -Agents can hand off to agents that also have handoffs: - -```python -level1 = Agent(name="level1", handoffs=[level2]) -level2 = Agent(name="level2", handoffs=[level3]) -level3 = Agent(name="level3", instructions="Final handler") -``` - -## Preventing Infinite Loops - -Use `max_turns` to prevent circular handoffs: - -```python -from agents import Runner, RunConfig - -config = RunConfig(max_turns=10) -result = await Runner.run(agent, input_text, run_config=config) -``` - -## Handoff vs Tools - -| Feature | Handoffs | Tools | -|---------|----------|-------| -| Control flow | Transfers to new agent | Returns to same agent | -| Context | New agent takes over | Same agent continues | -| Use case | Specialization | Actions/data retrieval | - -## See Also - -- [Agents](agents.md) - Creating agents -- [Multi-Agent](multi_agent.md) - Complex workflows -- [Running Agents](running_agents.md) - Execution diff --git a/docs/agent/index.md b/docs/agent/index.md deleted file mode 100644 index 6d94fb1d1..000000000 --- a/docs/agent/index.md +++ /dev/null @@ -1,35 +0,0 @@ -# Hanzo Agent SDK - -!!! note "Documentation" - For full Agent SDK documentation, see the [detailed docs](agents.md). - -The Hanzo Agent SDK enables building agentic AI applications with a lightweight, production-ready framework. - -## Quick Start - -```python -from agents import Agent, Runner - -agent = Agent( - name="assistant", - instructions="You are a helpful assistant." -) - -result = Runner.run_sync(agent, "Hello!") -print(result.final_output) -``` - -## Core Concepts - -- **Agents** - LLMs configured with instructions and tools -- **Handoffs** - Allow agents to delegate to other agents -- **Guardrails** - Validate agent inputs and outputs -- **Tracing** - Built-in observability - -## Navigation - -- [Agents](agents.md) - Creating and configuring agents -- [Running Agents](running_agents.md) - Execution patterns -- [Tools](tools.md) - Adding tools to agents -- [Handoffs](handoffs.md) - Multi-agent coordination -- [Tracing](tracing.md) - Observability and debugging diff --git a/docs/agent/models.md b/docs/agent/models.md deleted file mode 100644 index d7d2578ef..000000000 --- a/docs/agent/models.md +++ /dev/null @@ -1,203 +0,0 @@ -# Models - -Configure and use different LLM providers. - -## Supported Models - -### OpenAI - -```python -from agents import Agent - -# GPT-4o (recommended) -agent = Agent(name="gpt4", model="gpt-4o") - -# GPT-4o mini (faster, cheaper) -agent = Agent(name="mini", model="gpt-4o-mini") - -# GPT-4 Turbo -agent = Agent(name="turbo", model="gpt-4-turbo") -``` - -### Anthropic (via Hanzo) - -```python -agent = Agent(name="claude", model="claude-3-5-sonnet-20241022") -agent = Agent(name="opus", model="claude-3-opus-20240229") -``` - -### Other Providers - -```python -# Gemini -agent = Agent(name="gemini", model="gemini-pro") - -# Mistral -agent = Agent(name="mistral", model="mistral-large") -``` - -## Model Providers - -### OpenAI Provider (default) - -```python -from agents import OpenAIProvider, RunConfig - -provider = OpenAIProvider( - api_key="sk-...", # Or use OPENAI_API_KEY env var -) - -config = RunConfig(model_provider=provider) -``` - -### Hanzo Node Provider - -```python -from agents import create_hanzo_node_provider, RunConfig - -provider = create_hanzo_node_provider( - api_key="your-key", # Or use HANZO_API_KEY env var -) - -config = RunConfig(model_provider=provider) -result = await Runner.run(agent, "Hello!", run_config=config) -``` - -### Custom Base URL - -```python -from agents import OpenAIProvider - -# Use Azure OpenAI -provider = OpenAIProvider( - api_key="azure-key", - base_url="https://your-resource.openai.azure.com/", -) - -# Use local model (Ollama, vLLM, etc.) -provider = OpenAIProvider( - api_key="not-needed", - base_url="http://localhost:11434/v1", -) -``` - -## Custom Model Implementation - -```python -from agents import Model, ModelProvider - -class MyModel(Model): - async def complete( - self, - messages: list[dict], - tools: list[dict] | None = None, - **kwargs, - ) -> dict: - # Your implementation - response = await my_api_call(messages, tools) - return { - "content": response.text, - "tool_calls": response.tool_calls, - } - -class MyProvider(ModelProvider): - def get_model(self, model_name: str) -> Model: - return MyModel(model_name) -``` - -## Model Settings - -```python -from agents import Agent, ModelSettings - -agent = Agent( - name="creative", - model="gpt-4o", - model_settings=ModelSettings( - temperature=0.9, # More creative - top_p=0.95, - max_tokens=2000, - presence_penalty=0.1, - frequency_penalty=0.1, - ), -) - -agent = Agent( - name="precise", - model="gpt-4o", - model_settings=ModelSettings( - temperature=0.1, # More deterministic - max_tokens=500, - ), -) -``` - -## Model Tracing - -Enable detailed model tracing: - -```python -from agents import ModelTracing - -class TracedModel(Model): - tracing: ModelTracing = ModelTracing.ENABLED - - async def complete(self, messages, tools=None, **kwargs): - # Automatically traced - ... -``` - -## Response Models - -### Chat Completions - -Standard OpenAI-compatible response: - -```python -from agents import OpenAIChatCompletionsModel - -model = OpenAIChatCompletionsModel("gpt-4o") -``` - -### Responses API - -For models supporting the newer responses format: - -```python -from agents import OpenAIResponsesModel - -model = OpenAIResponsesModel("gpt-4o") -``` - -## Model Selection Strategy - -```python -from agents import Agent, RunConfig - -# Development: faster, cheaper -dev_config = RunConfig(model="gpt-4o-mini") - -# Production: best quality -prod_config = RunConfig(model="gpt-4o") - -# Use based on environment -import os -config = prod_config if os.environ.get("ENV") == "prod" else dev_config - -result = await Runner.run(agent, "Hello!", run_config=config) -``` - -## Environment Variables - -| Variable | Description | -|----------|-------------| -| `OPENAI_API_KEY` | OpenAI API key | -| `OPENAI_BASE_URL` | Custom OpenAI-compatible endpoint | -| `ANTHROPIC_API_KEY` | Anthropic API key | -| `HANZO_API_KEY` | Hanzo API key | - -## See Also - -- [Configuration](config.md) - Model settings -- [Agents](agents.md) - Using models with agents -- [Running Agents](running_agents.md) - Runtime model selection diff --git a/docs/agent/multi_agent.md b/docs/agent/multi_agent.md deleted file mode 100644 index 0d3b2867e..000000000 --- a/docs/agent/multi_agent.md +++ /dev/null @@ -1,227 +0,0 @@ -# Multi-Agent Systems - -Build complex workflows with multiple cooperating agents. - -## Agent Teams - -Create specialized agents that work together: - -```python -from agents import Agent - -# Specialist agents -researcher = Agent( - name="researcher", - instructions="Research topics thoroughly and provide facts.", - handoff_description="Researches information and facts.", -) - -writer = Agent( - name="writer", - instructions="Write clear, engaging content based on research.", - handoff_description="Writes content and articles.", -) - -editor = Agent( - name="editor", - instructions="Review and improve written content.", - handoff_description="Edits and improves content quality.", -) - -# Coordinator -coordinator = Agent( - name="coordinator", - instructions="""Coordinate the team to produce quality content. - 1. Use researcher for facts - 2. Use writer to draft content - 3. Use editor to polish""", - handoffs=[researcher, writer, editor], -) -``` - -## Sequential Workflow - -Agents pass work in sequence: - -```python -from agents import Agent, handoff, Handoff - -@handoff -def to_next_stage(ctx) -> Handoff: - """Move to the next processing stage.""" - stages = ctx.context.get("stages", []) - current = ctx.context.get("current_stage", 0) - - if current < len(stages): - ctx.context["current_stage"] = current + 1 - return Handoff(target=stages[current]) - return None - -stage1 = Agent(name="stage1", handoffs=[to_next_stage]) -stage2 = Agent(name="stage2", handoffs=[to_next_stage]) -stage3 = Agent(name="stage3", instructions="Final stage.") -``` - -## Parallel Execution - -Run multiple agents simultaneously: - -```python -import asyncio -from agents import Agent, Runner - -agents = [ - Agent(name="analyst1", instructions="Analyze from perspective A"), - Agent(name="analyst2", instructions="Analyze from perspective B"), - Agent(name="analyst3", instructions="Analyze from perspective C"), -] - -async def parallel_analysis(prompt: str): - tasks = [Runner.run(agent, prompt) for agent in agents] - results = await asyncio.gather(*tasks) - return [r.final_output for r in results] - -# Combine results -outputs = await parallel_analysis("Analyze this data") -``` - -## Supervisor Pattern - -One agent oversees others: - -```python -from agents import Agent, function_tool, Runner - -workers = { - "data": Agent(name="data_worker", instructions="Process data."), - "analysis": Agent(name="analysis_worker", instructions="Analyze results."), -} - -@function_tool -async def delegate(task_type: str, task: str) -> str: - """Delegate a task to a worker agent.""" - if task_type not in workers: - return f"Unknown worker type: {task_type}" - - result = await Runner.run(workers[task_type], task) - return result.final_output - -supervisor = Agent( - name="supervisor", - instructions="Coordinate workers to complete complex tasks.", - tools=[delegate], -) -``` - -## Debate Pattern - -Agents discuss and reach consensus: - -```python -from agents import Agent, Runner - -pro_agent = Agent( - name="pro", - instructions="Argue in favor of the proposition.", -) - -con_agent = Agent( - name="con", - instructions="Argue against the proposition.", -) - -judge_agent = Agent( - name="judge", - instructions="Evaluate arguments and reach a conclusion.", -) - -async def debate(topic: str, rounds: int = 3): - history = [f"Topic: {topic}"] - - for _ in range(rounds): - # Pro argument - pro_result = await Runner.run(pro_agent, "\n".join(history)) - history.append(f"Pro: {pro_result.final_output}") - - # Con argument - con_result = await Runner.run(con_agent, "\n".join(history)) - history.append(f"Con: {con_result.final_output}") - - # Final judgment - verdict = await Runner.run(judge_agent, "\n".join(history)) - return verdict.final_output -``` - -## Router Pattern - -Route requests to appropriate specialists: - -```python -from agents import Agent - -specialists = { - "billing": Agent(name="billing", instructions="Handle billing."), - "technical": Agent(name="technical", instructions="Handle tech issues."), - "sales": Agent(name="sales", instructions="Handle sales inquiries."), -} - -router = Agent( - name="router", - instructions="""Route customer requests to the right specialist: - - Billing questions โ†’ billing - - Technical issues โ†’ technical - - Purchase inquiries โ†’ sales""", - handoffs=list(specialists.values()), -) -``` - -## Shared Context - -Agents share state through context: - -```python -from dataclasses import dataclass, field -from agents import Agent, Runner, function_tool - -@dataclass -class SharedState: - findings: list[str] = field(default_factory=list) - decisions: list[str] = field(default_factory=list) - -@function_tool -def add_finding(ctx, finding: str) -> str: - """Add a finding to shared state.""" - ctx.context.findings.append(finding) - return f"Added finding: {finding}" - -@function_tool -def add_decision(ctx, decision: str) -> str: - """Record a decision.""" - ctx.context.decisions.append(decision) - return f"Recorded: {decision}" - -state = SharedState() -result = await Runner.run(coordinator, "Analyze and decide", context=state) -print(f"Findings: {state.findings}") -print(f"Decisions: {state.decisions}") -``` - -## Error Recovery - -Handle agent failures: - -```python -from agents import Runner, AgentsException - -async def run_with_fallback(primary: Agent, fallback: Agent, prompt: str): - try: - return await Runner.run(primary, prompt) - except AgentsException: - return await Runner.run(fallback, prompt) -``` - -## See Also - -- [Agents](agents.md) - Creating agents -- [Handoffs](handoffs.md) - Agent delegation -- [Context](context.md) - Shared state diff --git a/docs/agent/results.md b/docs/agent/results.md deleted file mode 100644 index 5bb3f3707..000000000 --- a/docs/agent/results.md +++ /dev/null @@ -1,183 +0,0 @@ -# Results - -Understanding and working with agent run results. - -## RunResult - -The result of a completed agent run: - -```python -from agents import Agent, Runner - -agent = Agent(name="assistant", instructions="Be helpful.") -result = await Runner.run(agent, "Hello!") - -# Access the result -print(result.final_output) # Final text output -print(result.last_agent) # Agent that produced output -print(result.new_items) # All conversation items -print(result.usage) # Token usage -``` - -## RunResult Properties - -| Property | Type | Description | -|----------|------|-------------| -| `final_output` | `str` | Final text response | -| `last_agent` | `Agent` | Agent that completed the run | -| `new_items` | `list[RunItem]` | All items from the run | -| `usage` | `Usage` | Token usage statistics | -| `input_guardrail_results` | `list` | Input guardrail results | -| `output_guardrail_results` | `list` | Output guardrail results | - -## Conversation Items - -Access all items from the conversation: - -```python -from agents.items import ( - MessageOutputItem, - ToolCallItem, - ToolCallOutputItem, - HandoffCallItem, -) - -for item in result.new_items: - match item: - case MessageOutputItem(content=content): - print(f"Message: {content}") - case ToolCallItem(tool_name=name, arguments=args): - print(f"Tool call: {name}({args})") - case ToolCallOutputItem(output=output): - print(f"Tool result: {output}") - case HandoffCallItem(target_agent=agent): - print(f"Handoff to: {agent.name}") -``` - -## Token Usage - -```python -usage = result.usage - -print(f"Input tokens: {usage.input_tokens}") -print(f"Output tokens: {usage.output_tokens}") -print(f"Total tokens: {usage.total_tokens}") -``` - -## Structured Output - -When using output schemas: - -```python -from pydantic import BaseModel -from agents import Agent, Runner - -class Response(BaseModel): - answer: str - confidence: float - -agent = Agent( - name="structured", - instructions="Always provide confidence.", - output_type=Response, -) - -result = await Runner.run(agent, "What is 2+2?") - -# Parsed output -response: Response = result.final_output_parsed -print(response.answer) # "4" -print(response.confidence) # 0.99 -``` - -## Guardrail Results - -```python -# Input guardrail results -for gr in result.input_guardrail_results: - print(f"Guardrail: {gr.guardrail_name}") - print(f"Triggered: {gr.tripwire_triggered}") - print(f"Info: {gr.output_info}") - -# Output guardrail results -for gr in result.output_guardrail_results: - print(f"Guardrail: {gr.guardrail_name}") - print(f"Triggered: {gr.tripwire_triggered}") -``` - -## Streaming Results - -For streaming runs: - -```python -from agents import Runner - -stream = Runner.run_streamed(agent, "Hello!") - -# Collect events -async for event in stream: - print(event) - -# Get final result -result = stream.result -print(result.final_output) -``` - -## Multi-Agent Results - -When handoffs occur: - -```python -result = await Runner.run(main_agent, "Help with billing") - -# Which agent finished? -print(f"Completed by: {result.last_agent.name}") - -# Trace the path -agents_involved = set() -for item in result.new_items: - if hasattr(item, "agent"): - agents_involved.add(item.agent.name) -print(f"Agents involved: {agents_involved}") -``` - -## Error Results - -Handle errors gracefully: - -```python -from agents import Runner, MaxTurnsExceeded - -try: - result = await Runner.run(agent, user_input) - print(result.final_output) -except MaxTurnsExceeded as e: - # Partial result available - partial = e.partial_result - print(f"Partial output: {partial.final_output}") - print(f"Turns used: {len(partial.new_items)}") -``` - -## Result Serialization - -```python -# To dict -result_dict = { - "output": result.final_output, - "agent": result.last_agent.name, - "usage": { - "input": result.usage.input_tokens, - "output": result.usage.output_tokens, - }, -} - -# To JSON -import json -json.dumps(result_dict) -``` - -## See Also - -- [Running Agents](running_agents.md) - Get results -- [Streaming](streaming.md) - Streaming results -- [Tracing](tracing.md) - Debug results diff --git a/docs/agent/running_agents.md b/docs/agent/running_agents.md deleted file mode 100644 index 14f981a2b..000000000 --- a/docs/agent/running_agents.md +++ /dev/null @@ -1,178 +0,0 @@ -# Running Agents - -Execute agents with the `Runner` class. - -## Basic Usage - -### Synchronous - -```python -from agents import Agent, Runner - -agent = Agent(name="assistant", instructions="Be helpful.") - -result = Runner.run_sync(agent, "Hello!") -print(result.final_output) -``` - -### Asynchronous - -```python -import asyncio -from agents import Agent, Runner - -agent = Agent(name="assistant", instructions="Be helpful.") - -async def main(): - result = await Runner.run(agent, "Hello!") - print(result.final_output) - -asyncio.run(main()) -``` - -## Run Configuration - -```python -from agents import Runner, RunConfig, ModelSettings - -config = RunConfig( - model="gpt-4o", - model_settings=ModelSettings(temperature=0.5), - max_turns=20, - tracing_disabled=False, -) - -result = await Runner.run(agent, "Hello!", run_config=config) -``` - -## RunConfig Options - -| Parameter | Default | Description | -|-----------|---------|-------------| -| `model` | None | Override agent model | -| `model_provider` | OpenAI | Model provider | -| `model_settings` | None | Global model settings | -| `max_turns` | 10 | Maximum conversation turns | -| `input_guardrails` | None | Global input guardrails | -| `output_guardrails` | None | Global output guardrails | -| `tracing_disabled` | False | Disable tracing | - -## Context - -Pass custom context to tools and guardrails: - -```python -from dataclasses import dataclass -from agents import Agent, Runner, function_tool - -@dataclass -class MyContext: - user_id: str - permissions: list[str] - -@function_tool -def get_user_data(ctx: MyContext) -> str: - return f"Data for user {ctx.user_id}" - -agent = Agent( - name="contextual", - instructions="Access user data as needed.", - tools=[get_user_data], -) - -context = MyContext(user_id="123", permissions=["read"]) -result = await Runner.run(agent, "Get my data", context=context) -``` - -## Run Result - -The `RunResult` contains: - -```python -result = await Runner.run(agent, "Hello!") - -# Final output text -print(result.final_output) - -# All conversation items -for item in result.new_items: - print(item) - -# Last agent that ran (for handoffs) -print(result.last_agent.name) - -# Input/output guardrail results -print(result.input_guardrail_results) -print(result.output_guardrail_results) - -# Token usage -print(result.usage) -``` - -## Max Turns - -Limit conversation turns to prevent infinite loops: - -```python -from agents import Runner, RunConfig - -config = RunConfig(max_turns=5) - -try: - result = await Runner.run(agent, "Complex task", run_config=config) -except MaxTurnsExceeded: - print("Agent hit max turns limit") -``` - -## Run Hooks - -Monitor run lifecycle: - -```python -from agents import Runner, RunHooks - -class MyRunHooks(RunHooks): - async def on_agent_start(self, context, agent): - print(f"Starting: {agent.name}") - - async def on_tool_start(self, context, agent, tool): - print(f"Calling tool: {tool.name}") - - async def on_handoff(self, context, from_agent, to_agent): - print(f"Handoff: {from_agent.name} -> {to_agent.name}") - -result = await Runner.run( - agent, - "Hello!", - run_hooks=MyRunHooks(), -) -``` - -## Error Handling - -```python -from agents import ( - Runner, - AgentsException, - MaxTurnsExceeded, - InputGuardrailTripwireTriggered, - OutputGuardrailTripwireTriggered, -) - -try: - result = await Runner.run(agent, user_input) -except MaxTurnsExceeded: - print("Too many turns") -except InputGuardrailTripwireTriggered as e: - print(f"Input blocked: {e}") -except OutputGuardrailTripwireTriggered as e: - print(f"Output blocked: {e}") -except AgentsException as e: - print(f"Agent error: {e}") -``` - -## See Also - -- [Agents](agents.md) - Creating agents -- [Streaming](streaming.md) - Stream responses -- [Tracing](tracing.md) - Debug runs diff --git a/docs/agent/streaming.md b/docs/agent/streaming.md deleted file mode 100644 index 97e0ecc5f..000000000 --- a/docs/agent/streaming.md +++ /dev/null @@ -1,156 +0,0 @@ -# Streaming - -Stream agent responses for real-time output. - -## Basic Streaming - -```python -from agents import Agent, Runner - -agent = Agent(name="assistant", instructions="Be helpful.") - -async def stream_response(): - async for event in Runner.run_streamed(agent, "Tell me a story"): - if event.type == "raw_response_event": - # Token-by-token output - print(event.data, end="", flush=True) - elif event.type == "agent_updated_event": - # Agent changed (handoff) - print(f"\n[Agent: {event.new_agent.name}]") -``` - -## Stream Events - -| Event Type | Description | -|------------|-------------| -| `raw_response_event` | Raw LLM response chunks | -| `agent_updated_event` | Agent changed (handoff) | -| `tool_call_event` | Tool being called | -| `tool_output_event` | Tool returned result | -| `run_item_event` | New run item added | - -## Processing Events - -```python -from agents import Runner -from agents.stream_events import ( - RawResponsesStreamEvent, - AgentUpdatedStreamEvent, -) - -async for event in Runner.run_streamed(agent, user_input): - match event: - case RawResponsesStreamEvent(data=chunk): - # Handle text chunk - print(chunk, end="") - - case AgentUpdatedStreamEvent(new_agent=new_agent): - # Handle agent switch - print(f"\n[Switched to: {new_agent.name}]") -``` - -## Streaming with Context - -```python -from dataclasses import dataclass -from agents import Agent, Runner - -@dataclass -class MyContext: - user_id: str - -context = MyContext(user_id="123") - -async for event in Runner.run_streamed( - agent, - "Hello!", - context=context, -): - print(event) -``` - -## Streaming Result - -Get the final result after streaming: - -```python -from agents import Runner, RunResultStreaming - -stream = Runner.run_streamed(agent, "Hello!") -result: RunResultStreaming = None - -async for event in stream: - print(event) - result = stream.result - -# After streaming completes -print(f"Final output: {result.final_output}") -print(f"Usage: {result.usage}") -``` - -## Buffered Streaming - -Collect output while streaming: - -```python -from agents import Runner - -buffer = [] - -async for event in Runner.run_streamed(agent, "Hello!"): - if event.type == "raw_response_event": - buffer.append(event.data) - print(event.data, end="") - -full_response = "".join(buffer) -``` - -## Streaming with Tools - -Tool calls appear as events: - -```python -from agents import Runner - -async for event in Runner.run_streamed(agent, "What's the weather?"): - match event.type: - case "tool_call_event": - print(f"Calling: {event.tool_name}") - case "tool_output_event": - print(f"Result: {event.output}") - case "raw_response_event": - print(event.data, end="") -``` - -## Streaming with Handoffs - -```python -from agents import Runner - -current_agent = None - -async for event in Runner.run_streamed(main_agent, "Help me"): - if event.type == "agent_updated_event": - current_agent = event.new_agent - print(f"\n--- Transferred to {current_agent.name} ---\n") - elif event.type == "raw_response_event": - print(event.data, end="") -``` - -## Error Handling in Streams - -```python -from agents import Runner, AgentsException - -try: - async for event in Runner.run_streamed(agent, user_input): - print(event) -except AgentsException as e: - print(f"Stream error: {e}") -``` - -## See Also - -- [Running Agents](running_agents.md) - Non-streaming execution -- [Tracing](tracing.md) - Debug streams -- [Results](results.md) - Result handling diff --git a/docs/agent/tools.md b/docs/agent/tools.md deleted file mode 100644 index 6e71d7d0a..000000000 --- a/docs/agent/tools.md +++ /dev/null @@ -1,190 +0,0 @@ -# Tools - -Tools give agents the ability to take actions and access external data. - -## Function Tools - -The simplest way to create a tool: - -```python -from agents import Agent, function_tool - -@function_tool -def get_weather(city: str) -> str: - """Get current weather for a city. - - Args: - city: The city name to get weather for - """ - # Your implementation - return f"Weather in {city}: Sunny, 72ยฐF" - -agent = Agent( - name="weather_bot", - instructions="Help users with weather information.", - tools=[get_weather], -) -``` - -## Async Tools - -```python -@function_tool -async def fetch_data(url: str) -> str: - """Fetch data from a URL.""" - async with aiohttp.ClientSession() as session: - async with session.get(url) as response: - return await response.text() -``` - -## Tools with Context - -Access the run context in tools: - -```python -from dataclasses import dataclass -from agents import function_tool, RunContextWrapper - -@dataclass -class AppContext: - user_id: str - api_key: str - -@function_tool -def get_user_profile(ctx: RunContextWrapper[AppContext]) -> str: - """Get the current user's profile.""" - user_id = ctx.context.user_id - # Fetch profile using user_id - return f"Profile for user {user_id}" -``` - -## Tool Parameters - -Pydantic models for complex parameters: - -```python -from pydantic import BaseModel, Field -from agents import function_tool - -class SearchParams(BaseModel): - query: str = Field(description="Search query") - max_results: int = Field(default=10, description="Max results to return") - include_metadata: bool = Field(default=False) - -@function_tool -def search(params: SearchParams) -> str: - """Search the knowledge base.""" - # Use params.query, params.max_results, etc. - return f"Found results for: {params.query}" -``` - -## Custom Tool Class - -For more control, extend the `Tool` class: - -```python -from agents import Tool - -class DatabaseTool(Tool): - name = "query_database" - description = "Query the application database" - - def __init__(self, connection_string: str): - self.conn = connect(connection_string) - - async def run(self, query: str) -> str: - result = await self.conn.execute(query) - return str(result) - - @property - def parameters_schema(self) -> dict: - return { - "type": "object", - "properties": { - "query": { - "type": "string", - "description": "SQL query to execute" - } - }, - "required": ["query"] - } -``` - -## Tool Return Types - -### String (default) - -```python -@function_tool -def simple() -> str: - return "Hello" -``` - -### Structured (Pydantic) - -```python -class ToolResult(BaseModel): - success: bool - data: dict - -@function_tool -def structured() -> ToolResult: - return ToolResult(success=True, data={"key": "value"}) -``` - -### List/Dict - -```python -@function_tool -def get_items() -> list[str]: - return ["item1", "item2", "item3"] -``` - -## Error Handling - -```python -from agents import function_tool, ToolError - -@function_tool -def risky_operation(param: str) -> str: - """An operation that might fail.""" - try: - result = do_something(param) - return result - except Exception as e: - raise ToolError(f"Operation failed: {e}") -``` - -## Tool Metadata - -```python -@function_tool( - name="custom_name", # Override function name - description="Custom description", # Override docstring -) -def my_tool(x: int) -> int: - return x * 2 -``` - -## Multiple Tools - -```python -from agents import Agent - -agent = Agent( - name="multi_tool", - instructions="Use available tools to help users.", - tools=[ - get_weather, - search_web, - calculate, - send_email, - ], -) -``` - -## See Also - -- [Agents](agents.md) - Creating agents -- [Running Agents](running_agents.md) - Execution -- [Context](context.md) - Run context diff --git a/docs/agent/tracing.md b/docs/agent/tracing.md deleted file mode 100644 index 201693092..000000000 --- a/docs/agent/tracing.md +++ /dev/null @@ -1,210 +0,0 @@ -# Tracing - -Built-in observability for debugging and monitoring agent runs. - -## Automatic Tracing - -Tracing is enabled by default: - -```python -from agents import Agent, Runner - -agent = Agent(name="assistant", instructions="Be helpful.") -result = await Runner.run(agent, "Hello!") - -# Traces are automatically collected -``` - -## Trace Structure - -Each run creates a trace with spans: - -``` -Trace (run_id) -โ”œโ”€โ”€ Agent Span (assistant) -โ”‚ โ”œโ”€โ”€ LLM Call -โ”‚ โ”œโ”€โ”€ Tool Call (get_weather) -โ”‚ โ””โ”€โ”€ LLM Call -โ””โ”€โ”€ Agent Span (specialist) # if handoff - โ””โ”€โ”€ LLM Call -``` - -## Custom Spans - -Add custom spans for your code: - -```python -from agents import trace, Span - -@trace("my_operation") -async def my_function(): - # Automatically traced - pass - -# Or manually -async def manual_trace(): - with Span("custom_span") as span: - span.set_attribute("key", "value") - # Your code here -``` - -## Span Attributes - -```python -from agents import Span - -with Span("process_data") as span: - span.set_attribute("input_size", len(data)) - span.set_attribute("user_id", user_id) - - result = process(data) - - span.set_attribute("output_size", len(result)) -``` - -## Error Tracking - -```python -from agents import Span, SpanError - -with Span("risky_operation") as span: - try: - result = risky_call() - except Exception as e: - span.record_error(SpanError( - message=str(e), - type=type(e).__name__, - )) - raise -``` - -## Agent Span Data - -Access agent-specific span data: - -```python -from agents.tracing.span_data import AgentSpanData - -# In hooks or custom code -span_data = AgentSpanData( - agent_name="assistant", - model="gpt-4o", - input_tokens=100, - output_tokens=50, -) -``` - -## Accessing Current Trace - -```python -from agents import get_current_trace - -trace = get_current_trace() -if trace: - print(f"Trace ID: {trace.trace_id}") - print(f"Spans: {len(trace.spans)}") -``` - -## Trace Export - -### Console (default) - -```python -from agents import Runner, RunConfig - -config = RunConfig( - tracing_disabled=False, # Default -) -``` - -### Custom Exporter - -```python -from agents.tracing import TraceExporter - -class MyExporter(TraceExporter): - async def export(self, trace): - # Send to your observability platform - await send_to_datadog(trace) - -# Register exporter -from agents.tracing import register_exporter -register_exporter(MyExporter()) -``` - -## Disabling Tracing - -```python -from agents import Runner, RunConfig - -# Disable for a single run -config = RunConfig(tracing_disabled=True) -result = await Runner.run(agent, "Hello!", run_config=config) - -# Disable globally -import agents -agents.tracing.disable() -``` - -## Trace Context - -Propagate trace context across services: - -```python -from agents import get_current_trace - -# Get context to pass to another service -trace = get_current_trace() -context = { - "trace_id": trace.trace_id, - "span_id": trace.current_span.span_id, -} - -# In the other service -from agents import trace_from_context -with trace_from_context(context): - # Operations here are linked to parent trace - pass -``` - -## Performance - -Tracing adds minimal overhead: - -| Operation | Overhead | -|-----------|----------| -| Span creation | ~1ฮผs | -| Attribute set | ~0.5ฮผs | -| Export (async) | Non-blocking | - -## Integration - -### OpenTelemetry - -```python -from agents.tracing.otel import OTelExporter - -exporter = OTelExporter( - endpoint="http://localhost:4317", - service_name="my-agent-app", -) -register_exporter(exporter) -``` - -### LangSmith - -```python -from agents.tracing.langsmith import LangSmithExporter - -exporter = LangSmithExporter( - api_key=os.environ["LANGSMITH_API_KEY"], - project="my-project", -) -register_exporter(exporter) -``` - -## See Also - -- [Running Agents](running_agents.md) - Execution -- [Streaming](streaming.md) - Stream with traces -- [Results](results.md) - Access trace data in results diff --git a/docs/app/docs/[[...slug]]/page.tsx b/docs/app/docs/[[...slug]]/page.tsx deleted file mode 100644 index 74f9af584..000000000 --- a/docs/app/docs/[[...slug]]/page.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { source } from '@/lib/source'; -import { - DocsPage, - DocsBody, - DocsTitle, - DocsDescription, -} from 'fumadocs-ui/page'; -import { notFound } from 'next/navigation'; -import defaultMdxComponents from 'fumadocs-ui/mdx'; - -export default async function Page(props: { - params: Promise<{ slug?: string[] }>; -}) { - const params = await props.params; - const page = source.getPage(params.slug); - if (!page) notFound(); - - const { body: MDX, toc } = await page.data.load(); - - return ( - - {page.data.title} - {page.data.description} - - - - - ); -} - -export async function generateStaticParams() { - return source.generateParams(); -} - -export async function generateMetadata(props: { - params: Promise<{ slug?: string[] }>; -}) { - const params = await props.params; - const page = source.getPage(params.slug); - if (!page) notFound(); - - return { - title: page.data.title, - description: page.data.description, - }; -} diff --git a/docs/app/docs/layout.tsx b/docs/app/docs/layout.tsx deleted file mode 100644 index 2f0486328..000000000 --- a/docs/app/docs/layout.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { DocsLayout } from 'fumadocs-ui/layouts/docs'; -import type { ReactNode } from 'react'; -import { source } from '@/lib/source'; - -function HanzoLogo() { - return ( - - - - - - - - - - ); -} - -export default function Layout({ children }: { children: ReactNode }) { - return ( - - - Hanzo Python SDK - - ), - url: '/docs', - }} - sidebar={{ - defaultOpenLevel: 1, - }} - > - {children} - - ); -} diff --git a/docs/app/global.css b/docs/app/global.css deleted file mode 100644 index d4b507858..000000000 --- a/docs/app/global.css +++ /dev/null @@ -1 +0,0 @@ -@import 'tailwindcss'; diff --git a/docs/app/layout.tsx b/docs/app/layout.tsx deleted file mode 100644 index 3087a81c6..000000000 --- a/docs/app/layout.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import 'fumadocs-ui/style.css'; -import './global.css'; -import { RootProvider } from 'fumadocs-ui/provider/next'; -import type { ReactNode } from 'react'; -import type { Metadata } from 'next'; - -export const metadata: Metadata = { - title: { - default: 'Hanzo Python SDK', - template: '%s | Hanzo Python SDK', - }, - description: 'The official Python SDK for Hanzo AI - 100+ LLM providers through a single OpenAI-compatible API', - icons: { - icon: [ - { url: '/python-sdk/favicon.svg', type: 'image/svg+xml' }, - { url: '/python-sdk/favicon.png', type: 'image/png', sizes: '32x32' }, - ], - apple: '/python-sdk/apple-touch-icon.png', - }, -}; - -export default function RootLayout({ children }: { children: ReactNode }) { - return ( - - - - {children} - - - - ); -} diff --git a/docs/app/page.tsx b/docs/app/page.tsx deleted file mode 100644 index a26769684..000000000 --- a/docs/app/page.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import Link from 'next/link'; - -export default function HomePage() { - return ( -
-
-

Hanzo Python SDK

-

- The official Python SDK for Hanzo AI - Unified access to 100+ LLM providers - through a single OpenAI-compatible API. -

-
- - Get Started - - - GitHub - -
-
-
- ); -} diff --git a/docs/assets/favicon.svg b/docs/assets/favicon.svg deleted file mode 100644 index 07ed3c918..000000000 --- a/docs/assets/favicon.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/docs/assets/images/favicon-platform.svg b/docs/assets/images/favicon-platform.svg deleted file mode 100644 index 91ef0aea5..000000000 --- a/docs/assets/images/favicon-platform.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/docs/assets/images/orchestration.png b/docs/assets/images/orchestration.png deleted file mode 100644 index 621a833b5..000000000 Binary files a/docs/assets/images/orchestration.png and /dev/null differ diff --git a/docs/assets/logo.svg b/docs/assets/logo.svg deleted file mode 100644 index 91c87fe14..000000000 --- a/docs/assets/logo.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/docs/content/docs/agents/agents.mdx b/docs/content/docs/agents/agents.mdx deleted file mode 100644 index 6f8f8cf64..000000000 --- a/docs/content/docs/agents/agents.mdx +++ /dev/null @@ -1,135 +0,0 @@ ---- -title: Agents -description: LLMs equipped with instructions and tools ---- - -Agents are the core building block in your apps. An agent is a large language model (LLM), configured with instructions and tools. - -## Basic configuration - -The most common properties of an agent you'll configure are: - -- `instructions`: also known as a developer message or system prompt. -- `model`: which LLM to use, and optional `model_settings` to configure model tuning parameters like temperature, top_p, etc. -- `tools`: Tools that the agent can use to achieve its tasks. - -```python -from hanzo_agent import Agent, ModelSettings, function_tool - -@function_tool -def get_weather(city: str) -> str: - return f"The weather in {city} is sunny" - -agent = Agent( - name="Haiku agent", - instructions="Always respond in haiku form", - model="o3-mini", - tools=[get_weather], -) -``` - -## Context - -Agents are generic on their `context` type. Context is a dependency-injection tool: it's an object you create and pass to `Runner.run()`, that is passed to every agent, tool, handoff etc, and it serves as a grab bag of dependencies and state for the agent run. You can provide any Python object as the context. - -```python -@dataclass -class UserContext: - uid: str - is_pro_user: bool - - async def fetch_purchases() -> list[Purchase]: - return ... - -agent = Agent[UserContext]( - ..., -) -``` - -## Output types - -By default, agents produce plain text (i.e. `str`) outputs. If you want the agent to produce a particular type of output, you can use the `output_type` parameter. A common choice is to use [Pydantic](https://docs.pydantic.dev/) objects, but we support any type that can be wrapped in a Pydantic [TypeAdapter](https://docs.pydantic.dev/latest/api/type_adapter/) - dataclasses, lists, TypedDict, etc. - -```python -from pydantic import BaseModel -from hanzo_agent import Agent - - -class CalendarEvent(BaseModel): - name: str - date: str - participants: list[str] - -agent = Agent( - name="Calendar extractor", - instructions="Extract calendar events from text", - output_type=CalendarEvent, -) -``` - -!!! note - - When you pass an `output_type`, that tells the model to use [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) instead of regular plain text responses. - -## Handoffs - -Handoffs are sub-agents that the agent can delegate to. You provide a list of handoffs, and the agent can choose to delegate to them if relevant. This is a powerful pattern that allows orchestrating modular, specialized agents that excel at a single task. Read more in the [handoffs](handoffs.md) documentation. - -```python -from hanzo_agent import Agent - -booking_agent = Agent(...) -refund_agent = Agent(...) - -triage_agent = Agent( - name="Triage agent", - instructions=( - "Help the user with their questions." - "If they ask about booking, handoff to the booking agent." - "If they ask about refunds, handoff to the refund agent." - ), - handoffs=[booking_agent, refund_agent], -) -``` - -## Dynamic instructions - -In most cases, you can provide instructions when you create the agent. However, you can also provide dynamic instructions via a function. The function will receive the agent and context, and must return the prompt. Both regular and `async` functions are accepted. - -```python -def dynamic_instructions( - context: RunContextWrapper[UserContext], agent: Agent[UserContext] -) -> str: - return f"The user's name is {context.context.name}. Help them with their questions." - - -agent = Agent[UserContext]( - name="Triage agent", - instructions=dynamic_instructions, -) -``` - -## Lifecycle events (hooks) - -Sometimes, you want to observe the lifecycle of an agent. For example, you may want to log events, or pre-fetch data when certain events occur. You can hook into the agent lifecycle with the `hooks` property. Subclass the [`AgentHooks`][agents.lifecycle.AgentHooks] class, and override the methods you're interested in. - -## Guardrails - -Guardrails allow you to run checks/validations on user input, in parallel to the agent running. For example, you could screen the user's input for relevance. Read more in the [guardrails](guardrails.md) documentation. - -## Cloning/copying agents - -By using the `clone()` method on an agent, you can duplicate an Agent, and optionally change any properties you like. - -```python -pirate_agent = Agent( - name="Pirate", - instructions="Write like a pirate", - model="o3-mini", -) - -robot_agent = pirate_agent.clone( - name="Robot", - instructions="Write like a robot", -) -``` diff --git a/docs/content/docs/agents/config.mdx b/docs/content/docs/agents/config.mdx deleted file mode 100644 index d9a7348d1..000000000 --- a/docs/content/docs/agents/config.mdx +++ /dev/null @@ -1,97 +0,0 @@ ---- -title: Configuration -description: Configure the Agent SDK for your needs ---- - -## API keys and clients - -By default, the SDK looks for the `OPENAI_API_KEY` environment variable for LLM requests and tracing, as soon as it is imported. If you are unable to set that environment variable before your app starts, you can use the [set_default_openai_key()][agents.set_default_openai_key] function to set the key. - -```python -from hanzo_agent import set_default_openai_key - -set_default_openai_key("sk-...") -``` - -Alternatively, you can also configure an Hanzo AI client to be used. By default, the SDK creates an `AsyncHanzo AI` instance, using the API key from the environment variable or the default key set above. You can change this by using the [set_default_openai_client()][agents.set_default_openai_client] function. - -```python -from openai import AsyncHanzo AI -from hanzo_agent import set_default_openai_client - -custom_client = AsyncHanzo AI(base_url="...", api_key="...") -set_default_openai_client(custom_client) -``` - -Finally, you can also customize the Hanzo AI API that is used. By default, we use the Hanzo AI Responses API. You can override this to use the Chat Completions API by using the [set_default_openai_api()][agents.set_default_openai_api] function. - -```python -from hanzo_agent import set_default_openai_api - -set_default_openai_api("chat_completions") -``` - -## Tracing - -Tracing is enabled by default. It uses the Hanzo AI API keys from the section above by default (i.e. the environment variable or the default key you set). You can specifically set the API key used for tracing by using the [`set_tracing_export_api_key`][agents.set_tracing_export_api_key] function. - -```python -from hanzo_agent import set_tracing_export_api_key - -set_tracing_export_api_key("sk-...") -``` - -You can also disable tracing entirely by using the [`set_tracing_disabled()`][agents.set_tracing_disabled] function. - -```python -from hanzo_agent import set_tracing_disabled - -set_tracing_disabled(True) -``` - -## Debug logging - -The SDK has two Python loggers without any handlers set. By default, this means that warnings and errors are sent to `stdout`, but other logs are suppressed. - -To enable verbose logging, use the [`enable_verbose_stdout_logging()`][agents.enable_verbose_stdout_logging] function. - -```python -from hanzo_agent import enable_verbose_stdout_logging - -enable_verbose_stdout_logging() -``` - -Alternatively, you can customize the logs by adding handlers, filters, formatters, etc. You can read more in the [Python logging guide](https://docs.python.org/3/howto/logging.html). - -```python -import logging - -logger = logging.getLogger("openai.agents") # or openai.agents.tracing for the Tracing logger - -# To make all logs show up -logger.setLevel(logging.DEBUG) -# To make info and above show up -logger.setLevel(logging.INFO) -# To make warning and above show up -logger.setLevel(logging.WARNING) -# etc - -# You can customize this as needed, but this will output to `stderr` by default -logger.addHandler(logging.StreamHandler()) -``` - -### Sensitive data in logs - -Certain logs may contain sensitive data (for example, user data). If you want to disable this data from being logged, set the following environment variables. - -To disable logging LLM inputs and outputs: - -```bash -export OPENAI_AGENTS_DONT_LOG_MODEL_DATA=1 -``` - -To disable logging tool inputs and outputs: - -```bash -export OPENAI_AGENTS_DONT_LOG_TOOL_DATA=1 -``` diff --git a/docs/content/docs/agents/context.mdx b/docs/content/docs/agents/context.mdx deleted file mode 100644 index 8124f1ffc..000000000 --- a/docs/content/docs/agents/context.mdx +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: Context Management -description: Manage context and state across agent interactions ---- - -Context is an overloaded term. There are two main classes of context you might care about: - -1. Context available locally to your code: this is data and dependencies you might need when tool functions run, during callbacks like `on_handoff`, in lifecycle hooks, etc. -2. Context available to LLMs: this is data the LLM sees when generating a response. - -## Local context - -This is represented via the [`RunContextWrapper`][agents.run_context.RunContextWrapper] class and the [`context`][agents.run_context.RunContextWrapper.context] property within it. The way this works is: - -1. You create any Python object you want. A common pattern is to use a dataclass or a Pydantic object. -2. You pass that object to the various run methods (e.g. `Runner.run(..., **context=whatever**))`. -3. All your tool calls, lifecycle hooks etc will be passed a wrapper object, `RunContextWrapper[T]`, where `T` represents your context object type which you can access via `wrapper.context`. - -The **most important** thing to be aware of: every agent, tool function, lifecycle etc for a given agent run must use the same _type_ of context. - -You can use the context for things like: - -- Contextual data for your run (e.g. things like a username/uid or other information about the user) -- Dependencies (e.g. logger objects, data fetchers, etc) -- Helper functions - -!!! danger "Note" - - The context object is **not** sent to the LLM. It is purely a local object that you can read from, write to and call methods on it. - -```python -import asyncio -from dataclasses import dataclass - -from hanzo_agent import Agent, RunContextWrapper, Runner, function_tool - -@dataclass -class UserInfo: # (1)! - name: str - uid: int - -@function_tool -async def fetch_user_age(wrapper: RunContextWrapper[UserInfo]) -> str: # (2)! - return f"User {wrapper.context.name} is 47 years old" - -async def main(): - user_info = UserInfo(name="John", uid=123) # (3)! - - agent = Agent[UserInfo]( # (4)! - name="Assistant", - tools=[fetch_user_age], - ) - - result = await Runner.run( - starting_agent=agent, - input="What is the age of the user?", - context=user_info, - ) - - print(result.final_output) # (5)! - # The user John is 47 years old. - -if __name__ == "__main__": - asyncio.run(main()) -``` - -1. This is the context object. We've used a dataclass here, but you can use any type. -2. This is a tool. You can see it takes a `RunContextWrapper[UserInfo]`. The tool implementation reads from the context. -3. We mark the agent with the generic `UserInfo`, so that the typechecker can catch errors (for example, if we tried to pass a tool that took a different context type). -4. The context is passed to the `run` function. -5. The agent correctly calls the tool and gets the age. - -## Agent/LLM context - -When an LLM is called, the **only** data it can see is from the conversation history. This means that if you want to make some new data available to the LLM, you must do it in a way that makes it available in that history. There are a few ways to do this: - -1. You can add it to the Agent `instructions`. This is also known as a "system prompt" or "developer message". System prompts can be static strings, or they can be dynamic functions that receive the context and output a string. This is a common tactic for information that is always useful (for example, the user's name or the current date). -2. Add it to the `input` when calling the `Runner.run` functions. This is similar to the `instructions` tactic, but allows you to have messages that are lower in the [chain of command](https://cdn.openai.com/spec/model-spec-2024-05-08.html#follow-the-chain-of-command). -3. Expose it via function tools. This is useful for _on-demand_ context - the LLM decides when it needs some data, and can call the tool to fetch that data. -4. Use retrieval or web search. These are special tools that are able to fetch relevant data from files or databases (retrieval), or from the web (web search). This is useful for "grounding" the response in relevant contextual data. diff --git a/docs/content/docs/agents/guardrails.mdx b/docs/content/docs/agents/guardrails.mdx deleted file mode 100644 index e52f754db..000000000 --- a/docs/content/docs/agents/guardrails.mdx +++ /dev/null @@ -1,157 +0,0 @@ ---- -title: Guardrails -description: Validate inputs and outputs for safety ---- - -Guardrails run _in parallel_ to your agents, enabling you to do checks and validations of user input. For example, imagine you have an agent that uses a very smart (and hence slow/expensive) model to help with customer requests. You wouldn't want malicious users to ask the model to help them with their math homework. So, you can run a guardrail with a fast/cheap model. If the guardrail detects malicious usage, it can immediately raise an error, which stops the expensive model from running and saves you time/money. - -There are two kinds of guardrails: - -1. Input guardrails run on the initial user input -2. Output guardrails run on the final agent output - -## Input guardrails - -Input guardrails run in 3 steps: - -1. First, the guardrail receives the same input passed to the agent. -2. Next, the guardrail function runs to produce a [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput], which is then wrapped in an [`InputGuardrailResult`][agents.guardrail.InputGuardrailResult] -3. Finally, we check if [`.tripwire_triggered`][agents.guardrail.GuardrailFunctionOutput.tripwire_triggered] is true. If true, an [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered] exception is raised, so you can appropriately respond to the user or handle the exception. - -!!! Note - - Input guardrails are intended to run on user input, so an agent's guardrails only run if the agent is the *first* agent. You might wonder, why is the `guardrails` property on the agent instead of passed to `Runner.run`? It's because guardrails tend to be related to the actual Agent - you'd run different guardrails for different agents, so colocating the code is useful for readability. - -## Output guardrails - -Output guardrails run in 3 steps: - -1. First, the guardrail receives the same input passed to the agent. -2. Next, the guardrail function runs to produce a [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput], which is then wrapped in an [`OutputGuardrailResult`][agents.guardrail.OutputGuardrailResult] -3. Finally, we check if [`.tripwire_triggered`][agents.guardrail.GuardrailFunctionOutput.tripwire_triggered] is true. If true, an [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered] exception is raised, so you can appropriately respond to the user or handle the exception. - -!!! Note - - Output guardrails are intended to run on the final agent input, so an agent's guardrails only run if the agent is the *last* agent. Similar to the input guardrails, we do this because guardrails tend to be related to the actual Agent - you'd run different guardrails for different agents, so colocating the code is useful for readability. - -## Tripwires - -If the input or output fails the guardrail, the Guardrail can signal this with a tripwire. As soon as we see a guardrail that has triggered the tripwires, we immediately raise a `{Input,Output}GuardrailTripwireTriggered` exception and halt the Agent execution. - -## Implementing a guardrail - -You need to provide a function that receives input, and returns a [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput]. In this example, we'll do this by running an Agent under the hood. - -```python -from pydantic import BaseModel -from hanzo_agent import ( - Agent, - GuardrailFunctionOutput, - InputGuardrailTripwireTriggered, - RunContextWrapper, - Runner, - TResponseInputItem, - input_guardrail, -) - -class MathHomeworkOutput(BaseModel): - is_math_homework: bool - reasoning: str - -guardrail_agent = Agent( # (1)! - name="Guardrail check", - instructions="Check if the user is asking you to do their math homework.", - output_type=MathHomeworkOutput, -) - - -@input_guardrail -async def math_guardrail( # (2)! - ctx: RunContextWrapper[None], agent: Agent, input: str | list[TResponseInputItem] -) -> GuardrailFunctionOutput: - result = await Runner.run(guardrail_agent, input, context=ctx.context) - - return GuardrailFunctionOutput( - output_info=result.final_output, # (3)! - tripwire_triggered=result.final_output.is_math_homework, - ) - - -agent = Agent( # (4)! - name="Customer support agent", - instructions="You are a customer support agent. You help customers with their questions.", - input_guardrails=[math_guardrail], -) - -async def main(): - # This should trip the guardrail - try: - await Runner.run(agent, "Hello, can you help me solve for x: 2x + 3 = 11?") - print("Guardrail didn't trip - this is unexpected") - - except InputGuardrailTripwireTriggered: - print("Math homework guardrail tripped") -``` - -1. We'll use this agent in our guardrail function. -2. This is the guardrail function that receives the agent's input/context, and returns the result. -3. We can include extra information in the guardrail result. -4. This is the actual agent that defines the workflow. - -Output guardrails are similar. - -```python -from pydantic import BaseModel -from hanzo_agent import ( - Agent, - GuardrailFunctionOutput, - OutputGuardrailTripwireTriggered, - RunContextWrapper, - Runner, - output_guardrail, -) -class MessageOutput(BaseModel): # (1)! - response: str - -class MathOutput(BaseModel): # (2)! - is_math: bool - reasoning: str - -guardrail_agent = Agent( - name="Guardrail check", - instructions="Check if the output includes any math.", - output_type=MathOutput, -) - -@output_guardrail -async def math_guardrail( # (3)! - ctx: RunContextWrapper, agent: Agent, output: MessageOutput -) -> GuardrailFunctionOutput: - result = await Runner.run(guardrail_agent, output.response, context=ctx.context) - - return GuardrailFunctionOutput( - output_info=result.final_output, - tripwire_triggered=result.final_output.is_math, - ) - -agent = Agent( # (4)! - name="Customer support agent", - instructions="You are a customer support agent. You help customers with their questions.", - output_guardrails=[math_guardrail], - output_type=MessageOutput, -) - -async def main(): - # This should trip the guardrail - try: - await Runner.run(agent, "Hello, can you help me solve for x: 2x + 3 = 11?") - print("Guardrail didn't trip - this is unexpected") - - except OutputGuardrailTripwireTriggered: - print("Math output guardrail tripped") -``` - -1. This is the actual agent's output type. -2. This is the guardrail's output type. -3. This is the guardrail function that receives the agent's output, and returns the result. -4. This is the actual agent that defines the workflow. diff --git a/docs/content/docs/agents/handoffs.mdx b/docs/content/docs/agents/handoffs.mdx deleted file mode 100644 index 1d918bda7..000000000 --- a/docs/content/docs/agents/handoffs.mdx +++ /dev/null @@ -1,116 +0,0 @@ ---- -title: Handoffs -description: Delegate tasks between multiple agents ---- - -Handoffs allow an agent to delegate tasks to another agent. This is particularly useful in scenarios where different agents specialize in distinct areas. For example, a customer support app might have agents that each specifically handle tasks like order status, refunds, FAQs, etc. - -Handoffs are represented as tools to the LLM. So if there's a handoff to an agent named `Refund Agent`, the tool would be called `transfer_to_refund_agent`. - -## Creating a handoff - -All agents have a [`handoffs`][agents.agent.Agent.handoffs] param, which can either take an `Agent` directly, or a `Handoff` object that customizes the Handoff. - -You can create a handoff using the [`handoff()`][agents.handoffs.handoff] function provided by the Agent SDK. This function allows you to specify the agent to hand off to, along with optional overrides and input filters. - -### Basic Usage - -Here's how you can create a simple handoff: - -```python -from hanzo_agent import Agent, handoff - -billing_agent = Agent(name="Billing agent") -refund_agent = Agent(name="Refund agent") - -# (1)! -triage_agent = Agent(name="Triage agent", handoffs=[billing_agent, handoff(refund_agent)]) -``` - -1. You can use the agent directly (as in `billing_agent`), or you can use the `handoff()` function. - -### Customizing handoffs via the `handoff()` function - -The [`handoff()`][agents.handoffs.handoff] function lets you customize things. - -- `agent`: This is the agent to which things will be handed off. -- `tool_name_override`: By default, the `Handoff.default_tool_name()` function is used, which resolves to `transfer_to_`. You can override this. -- `tool_description_override`: Override the default tool description from `Handoff.default_tool_description()` -- `on_handoff`: A callback function executed when the handoff is invoked. This is useful for things like kicking off some data fetching as soon as you know a handoff is being invoked. This function receives the agent context, and can optionally also receive LLM generated input. The input data is controlled by the `input_type` param. -- `input_type`: The type of input expected by the handoff (optional). -- `input_filter`: This lets you filter the input received by the next agent. See below for more. - -```python -from hanzo_agent import Agent, handoff, RunContextWrapper - -def on_handoff(ctx: RunContextWrapper[None]): - print("Handoff called") - -agent = Agent(name="My agent") - -handoff_obj = handoff( - agent=agent, - on_handoff=on_handoff, - tool_name_override="custom_handoff_tool", - tool_description_override="Custom description", -) -``` - -## Handoff inputs - -In certain situations, you want the LLM to provide some data when it calls a handoff. For example, imagine a handoff to an "Escalation agent". You might want a reason to be provided, so you can log it. - -```python -from pydantic import BaseModel - -from hanzo_agent import Agent, handoff, RunContextWrapper - -class EscalationData(BaseModel): - reason: str - -async def on_handoff(ctx: RunContextWrapper[None], input_data: EscalationData): - print(f"Escalation agent called with reason: {input_data.reason}") - -agent = Agent(name="Escalation agent") - -handoff_obj = handoff( - agent=agent, - on_handoff=on_handoff, - input_type=EscalationData, -) -``` - -## Input filters - -When a handoff occurs, it's as though the new agent takes over the conversation, and gets to see the entire previous conversation history. If you want to change this, you can set an [`input_filter`][agents.handoffs.Handoff.input_filter]. An input filter is a function that receives the existing input via a [`HandoffInputData`][agents.handoffs.HandoffInputData], and must return a new `HandoffInputData`. - -There are some common patterns (for example removing all tool calls from the history), which are implemented for you in [`agents.extensions.handoff_filters`][] - -```python -from hanzo_agent import Agent, handoff -from hanzo_agent.extensions import handoff_filters - -agent = Agent(name="FAQ agent") - -handoff_obj = handoff( - agent=agent, - input_filter=handoff_filters.remove_all_tools, # (1)! -) -``` - -1. This will automatically remove all tools from the history when `FAQ agent` is called. - -## Recommended prompts - -To make sure that LLMs understand handoffs properly, we recommend including information about handoffs in your agents. We have a suggested prefix in [`agents.extensions.handoff_prompt.RECOMMENDED_PROMPT_PREFIX`][], or you can call [`agents.extensions.handoff_prompt.prompt_with_handoff_instructions`][] to automatically add recommended data to your prompts. - -```python -from hanzo_agent import Agent -from hanzo_agent.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX - -billing_agent = Agent( - name="Billing agent", - instructions=f"""{RECOMMENDED_PROMPT_PREFIX} - .""", -) -``` diff --git a/docs/content/docs/agents/index.mdx b/docs/content/docs/agents/index.mdx deleted file mode 100644 index ee6658289..000000000 --- a/docs/content/docs/agents/index.mdx +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: Hanzo AI Agent SDK -description: Build agentic AI apps with a lightweight, production-ready SDK ---- - -The Hanzo AI Agent SDK enables you to build agentic AI apps in a lightweight, easy-to-use package with very few abstractions. It's a production-ready upgrade of our previous experimentation for agents, Swarm. The Agent SDK has a very small set of primitives: - -- **Agents**, which are LLMs equipped with instructions and tools -- **Handoffs**, which allow agents to delegate to other agents for specific tasks -- **Guardrails**, which enable the inputs to agents to be validated - -In combination with Python, these primitives are powerful enough to express complex relationships between tools and agents, and allow you to build real-world applications without a steep learning curve. In addition, the SDK comes with built-in **tracing** that lets you visualize and debug your agentic flows, as well as evaluate them and even fine-tune models for your application. - -## Why use the Agent SDK - -The SDK has two driving design principles: - -1. Enough features to be worth using, but few enough primitives to make it quick to learn. -2. Works great out of the box, but you can customize exactly what happens. - -Here are the main features of the SDK: - -- **Agent loop**: Built-in agent loop that handles calling tools, sending results to the LLM, and looping until the LLM is done. -- **Python-first**: Use built-in language features to orchestrate and chain agents, rather than needing to learn new abstractions. -- **Handoffs**: A powerful feature to coordinate and delegate between multiple agents. -- **Guardrails**: Run input validations and checks in parallel to your agents, breaking early if the checks fail. -- **Function tools**: Turn any Python function into a tool, with automatic schema generation and Pydantic-powered validation. -- **Tracing**: Built-in tracing that lets you visualize, debug and monitor your workflows, as well as use the Hanzo AI suite of evaluation, fine-tuning and distillation tools. - -## Installation - -```bash -pip install hanzo-agent -``` - -## Hello world example - -```python -from hanzo_agent import Agent, Runner - -agent = Agent(name="Assistant", instructions="You are a helpful assistant") - -result = Runner.run_sync(agent, "Write a haiku about recursion in programming.") -print(result.final_output) - -# Code within the code, -# Functions calling themselves, -# Infinite loop's dance. -``` - -Set the `HANZO_API_KEY` environment variable: - -```bash -export HANZO_API_KEY=your-api-key -``` diff --git a/docs/content/docs/agents/meta.json b/docs/content/docs/agents/meta.json deleted file mode 100644 index cda346db8..000000000 --- a/docs/content/docs/agents/meta.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "title": "Agent SDK", - "pages": [ - "index", - "quickstart", - "agents", - "running_agents", - "results", - "streaming", - "tools", - "handoffs", - "guardrails", - "context", - "multi_agent", - "tracing", - "models", - "config" - ] -} diff --git a/docs/content/docs/agents/models.mdx b/docs/content/docs/agents/models.mdx deleted file mode 100644 index 8e0684476..000000000 --- a/docs/content/docs/agents/models.mdx +++ /dev/null @@ -1,96 +0,0 @@ ---- -title: Models -description: Configure and use different LLM models ---- - -The Agent SDK comes with out-of-the-box support for Hanzo AI models in two flavors: - -- **Recommended**: the [`Hanzo AIResponsesModel`][agents.models.openai_responses.Hanzo AIResponsesModel], which calls Hanzo AI APIs using the new [Responses API](https://platform.openai.com/docs/api-reference/responses). -- The [`Hanzo AIChatCompletionsModel`][agents.models.openai_chatcompletions.Hanzo AIChatCompletionsModel], which calls Hanzo AI APIs using the [Chat Completions API](https://platform.openai.com/docs/api-reference/chat). - -## Mixing and matching models - -Within a single workflow, you may want to use different models for each agent. For example, you could use a smaller, faster model for triage, while using a larger, more capable model for complex tasks. When configuring an [`Agent`][agents.Agent], you can select a specific model by either: - -1. Passing the name of an Hanzo AI model. -2. Passing any model name + a [`ModelProvider`][agents.models.interface.ModelProvider] that can map that name to a Model instance. -3. Directly providing a [`Model`][agents.models.interface.Model] implementation. - -!!!note - - While our SDK supports both the [`Hanzo AIResponsesModel`][agents.models.openai_responses.Hanzo AIResponsesModel] and the [`Hanzo AIChatCompletionsModel`][agents.models.openai_chatcompletions.Hanzo AIChatCompletionsModel] shapes, we recommend using a single model shape for each workflow because the two shapes support a different set of features and tools. If your workflow requires mixing and matching model shapes, make sure that all the features you're using are available on both. - -```python -from hanzo_agent import Agent, Runner, AsyncHanzo AI, Hanzo AIChatCompletionsModel -import asyncio - -spanish_agent = Agent( - name="Spanish agent", - instructions="You only speak Spanish.", - model="o3-mini", # (1)! -) - -english_agent = Agent( - name="English agent", - instructions="You only speak English", - model=Hanzo AIChatCompletionsModel( # (2)! - model="gpt-4o", - openai_client=AsyncHanzo AI() - ), -) - -triage_agent = Agent( - name="Triage agent", - instructions="Handoff to the appropriate agent based on the language of the request.", - handoffs=[spanish_agent, english_agent], - model="gpt-3.5-turbo", -) - -async def main(): - result = await Runner.run(triage_agent, input="Hola, ยฟcรณmo estรกs?") - print(result.final_output) -``` - -1. Sets the name of an Hanzo AI model directly. -2. Provides a [`Model`][agents.models.interface.Model] implementation. - -## Using other LLM providers - -You can use other LLM providers in 3 ways (examples [here](https://github.com/openai/hanzo-agent-python/tree/main/examples/model_providers/)): - -1. [`set_default_openai_client`][agents.set_default_openai_client] is useful in cases where you want to globally use an instance of `AsyncHanzo AI` as the LLM client. This is for cases where the LLM provider has an Hanzo AI compatible API endpoint, and you can set the `base_url` and `api_key`. See a configurable example in [examples/model_providers/custom_example_global.py](https://github.com/openai/hanzo-agent-python/tree/main/examples/model_providers/custom_example_global.py). -2. [`ModelProvider`][agents.models.interface.ModelProvider] is at the `Runner.run` level. This lets you say "use a custom model provider for all agents in this run". See a configurable example in [examples/model_providers/custom_example_provider.py](https://github.com/openai/hanzo-agent-python/tree/main/examples/model_providers/custom_example_provider.py). -3. [`Agent.model`][agents.agent.Agent.model] lets you specify the model on a specific Agent instance. This enables you to mix and match different providers for different agents. See a configurable example in [examples/model_providers/custom_example_agent.py](https://github.com/openai/hanzo-agent-python/tree/main/examples/model_providers/custom_example_agent.py). - -In cases where you do not have an API key from `platform.openai.com`, we recommend disabling tracing via `set_tracing_disabled()`, or setting up a [different tracing processor](tracing.md). - -!!! note - - In these examples, we use the Chat Completions API/model, because most LLM providers don't yet support the Responses API. If your LLM provider does support it, we recommend using Responses. - -## Common issues with using other LLM providers - -### Tracing client error 401 - -If you get errors related to tracing, this is because traces are uploaded to Hanzo AI servers, and you don't have an Hanzo AI API key. You have three options to resolve this: - -1. Disable tracing entirely: [`set_tracing_disabled(True)`][agents.set_tracing_disabled]. -2. Set an Hanzo AI key for tracing: [`set_tracing_export_api_key(...)`][agents.set_tracing_export_api_key]. This API key will only be used for uploading traces, and must be from [platform.openai.com](https://platform.openai.com/). -3. Use a non-Hanzo AI trace processor. See the [tracing docs](tracing.md#custom-tracing-processors). - -### Responses API support - -The SDK uses the Responses API by default, but most other LLM providers don't yet support it. You may see 404s or similar issues as a result. To resolve, you have two options: - -1. Call [`set_default_openai_api("chat_completions")`][agents.set_default_openai_api]. This works if you are setting `OPENAI_API_KEY` and `OPENAI_BASE_URL` via environment vars. -2. Use [`Hanzo AIChatCompletionsModel`][agents.models.openai_chatcompletions.Hanzo AIChatCompletionsModel]. There are examples [here](https://github.com/openai/hanzo-agent-python/tree/main/examples/model_providers/). - -### Structured outputs support - -Some model providers don't have support for [structured outputs](https://platform.openai.com/docs/guides/structured-outputs). This sometimes results in an error that looks something like this: - -``` -BadRequestError: Error code: 400 - {'error': {'message': "'response_format.type' : value is not one of the allowed values ['text','json_object']", 'type': 'invalid_request_error'}} -``` - -This is a shortcoming of some model providers - they support JSON outputs, but don't allow you to specify the `json_schema` to use for the output. We are working on a fix for this, but we suggest relying on providers that do have support for JSON schema output, because otherwise your app will often break because of malformed JSON. diff --git a/docs/content/docs/agents/multi_agent.mdx b/docs/content/docs/agents/multi_agent.mdx deleted file mode 100644 index 7d3b77d9e..000000000 --- a/docs/content/docs/agents/multi_agent.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: Multi-Agent Orchestration -description: Coordinate multiple agents working together ---- - -Orchestration refers to the flow of agents in your app. Which agents run, in what order, and how do they decide what happens next? There are two main ways to orchestrate agents: - -1. Allowing the LLM to make decisions: this uses the intelligence of an LLM to plan, reason, and decide on what steps to take based on that. -2. Orchestrating via code: determining the flow of agents via your code. - -You can mix and match these patterns. Each has their own tradeoffs, described below. - -## Orchestrating via LLM - -An agent is an LLM equipped with instructions, tools and handoffs. This means that given an open-ended task, the LLM can autonomously plan how it will tackle the task, using tools to take actions and acquire data, and using handoffs to delegate tasks to sub-agents. For example, a research agent could be equipped with tools like: - -- Web search to find information online -- File search and retrieval to search through proprietary data and connections -- Computer use to take actions on a computer -- Code execution to do data analysis -- Handoffs to specialized agents that are great at planning, report writing and more. - -This pattern is great when the task is open-ended and you want to rely on the intelligence of an LLM. The most important tactics here are: - -1. Invest in good prompts. Make it clear what tools are available, how to use them, and what parameters it must operate within. -2. Monitor your app and iterate on it. See where things go wrong, and iterate on your prompts. -3. Allow the agent to introspect and improve. For example, run it in a loop, and let it critique itself; or, provide error messages and let it improve. -4. Have specialized agents that excel in one task, rather than having a general purpose agent that is expected to be good at anything. -5. Invest in [evals](https://platform.openai.com/docs/guides/evals). This lets you train your agents to improve and get better at tasks. - -## Orchestrating via code - -While orchestrating via LLM is powerful, orchestrating via code makes tasks more deterministic and predictable, in terms of speed, cost and performance. Common patterns here are: - -- Using [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) to generate well formed data that you can inspect with your code. For example, you might ask an agent to classify the task into a few categories, and then pick the next agent based on the category. -- Chaining multiple agents by transforming the output of one into the input of the next. You can decompose a task like writing a blog post into a series of steps - do research, write an outline, write the blog post, critique it, and then improve it. -- Running the agent that performs the task in a `while` loop with an agent that evaluates and provides feedback, until the evaluator says the output passes certain criteria. -- Running multiple agents in parallel, e.g. via Python primitives like `asyncio.gather`. This is useful for speed when you have multiple tasks that don't depend on each other. - -We have a number of examples in [`examples/agent_patterns`](https://github.com/openai/hanzo-agent-python/tree/main/examples/agent_patterns). diff --git a/docs/content/docs/agents/quickstart.mdx b/docs/content/docs/agents/quickstart.mdx deleted file mode 100644 index dd82206f9..000000000 --- a/docs/content/docs/agents/quickstart.mdx +++ /dev/null @@ -1,192 +0,0 @@ ---- -title: Quickstart -description: Get started with the Agent SDK in minutes ---- - -## Create a project and virtual environment - -You'll only need to do this once. - -```bash -mkdir my_project -cd my_project -python -m venv .venv -``` - -### Activate the virtual environment - -Do this every time you start a new terminal session. - -```bash -source .venv/bin/activate -``` - -### Install the Agent SDK - -```bash -pip install hanzo-agent # or `uv add hanzo-agent`, etc -``` - -### Set an Hanzo AI API key - -If you don't have one, follow [these instructions](https://platform.openai.com/docs/quickstart#create-and-export-an-api-key) to create an Hanzo AI API key. - -```bash -export OPENAI_API_KEY=sk-... -``` - -## Create your first agent - -Agents are defined with instructions, a name, and optional config (such as `model_config`) - -```python -from hanzo_agent import Agent - -agent = Agent( - name="Math Tutor", - instructions="You provide help with math problems. Explain your reasoning at each step and include examples", -) -``` - -## Add a few more agents - -Additional agents can be defined in the same way. `handoff_descriptions` provide additional context for determining handoff routing - -```python -from hanzo_agent import Agent - -history_tutor_agent = Agent( - name="History Tutor", - handoff_description="Specialist agent for historical questions", - instructions="You provide assistance with historical queries. Explain important events and context clearly.", -) - -math_tutor_agent = Agent( - name="Math Tutor", - handoff_description="Specialist agent for math questions", - instructions="You provide help with math problems. Explain your reasoning at each step and include examples", -) -``` - -## Define your handoffs - -On each agent, you can define an inventory of outgoing handoff options that the agent can choose from to decide how to make progress on their task. - -```python -triage_agent = Agent( - name="Triage Agent", - instructions="You determine which agent to use based on the user's homework question", - handoffs=[history_tutor_agent, math_tutor_agent] -) -``` - -## Run the agent orchestration - -Let's check that the workflow runs and the triage agent correctly routes between the two specialist agents. - -```python -from hanzo_agent import Runner - -async def main(): - result = await Runner.run(triage_agent, "What is the capital of France?") - print(result.final_output) -``` - -## Add a guardrail - -You can define custom guardrails to run on the input or output. - -```python -from hanzo_agent import GuardrailFunctionOutput, Agent, Runner -from pydantic import BaseModel - -class HomeworkOutput(BaseModel): - is_homework: bool - reasoning: str - -guardrail_agent = Agent( - name="Guardrail check", - instructions="Check if the user is asking about homework.", - output_type=HomeworkOutput, -) - -async def homework_guardrail(ctx, agent, input_data): - result = await Runner.run(guardrail_agent, input_data, context=ctx.context) - final_output = result.final_output_as(HomeworkOutput) - return GuardrailFunctionOutput( - output_info=final_output, - tripwire_triggered=not final_output.is_homework, - ) -``` - -## Put it all together - -Let's put it all together and run the entire workflow, using handoffs and the input guardrail. - -```python -from hanzo_agent import Agent, InputGuardrail,GuardrailFunctionOutput, Runner -from pydantic import BaseModel -import asyncio - -class HomeworkOutput(BaseModel): - is_homework: bool - reasoning: str - -guardrail_agent = Agent( - name="Guardrail check", - instructions="Check if the user is asking about homework.", - output_type=HomeworkOutput, -) - -math_tutor_agent = Agent( - name="Math Tutor", - handoff_description="Specialist agent for math questions", - instructions="You provide help with math problems. Explain your reasoning at each step and include examples", -) - -history_tutor_agent = Agent( - name="History Tutor", - handoff_description="Specialist agent for historical questions", - instructions="You provide assistance with historical queries. Explain important events and context clearly.", -) - - -async def homework_guardrail(ctx, agent, input_data): - result = await Runner.run(guardrail_agent, input_data, context=ctx.context) - final_output = result.final_output_as(HomeworkOutput) - return GuardrailFunctionOutput( - output_info=final_output, - tripwire_triggered=not final_output.is_homework, - ) - -triage_agent = Agent( - name="Triage Agent", - instructions="You determine which agent to use based on the user's homework question", - handoffs=[history_tutor_agent, math_tutor_agent], - input_guardrails=[ - InputGuardrail(guardrail_function=homework_guardrail), - ], -) - -async def main(): - result = await Runner.run(triage_agent, "who was the first president of the united states?") - print(result.final_output) - - result = await Runner.run(triage_agent, "what is life") - print(result.final_output) - -if __name__ == "__main__": - asyncio.run(main()) -``` - -## View your traces - -To review what happened during your agent run, navigate to the [Trace viewer in the Hanzo AI Dashboard](https://platform.openai.com/traces) to view traces of your agent runs. - -## Next steps - -Learn how to build more complex agentic flows: - -- Learn about how to configure [Agents](agents.md). -- Learn about [running agents](running_agents.md). -- Learn about [tools](tools.md), [guardrails](guardrails.md) and [models](models.md). diff --git a/docs/content/docs/agents/results.mdx b/docs/content/docs/agents/results.mdx deleted file mode 100644 index fe4eb8f67..000000000 --- a/docs/content/docs/agents/results.mdx +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: Results -description: Handle and process agent execution results ---- - -When you call the `Runner.run` methods, you either get a: - -- [`RunResult`][agents.result.RunResult] if you call `run` or `run_sync` -- [`RunResultStreaming`][agents.result.RunResultStreaming] if you call `run_streamed` - -Both of these inherit from [`RunResultBase`][agents.result.RunResultBase], which is where most useful information is present. - -## Final output - -The [`final_output`][agents.result.RunResultBase.final_output] property contains the final output of the last agent that ran. This is either: - -- a `str`, if the last agent didn't have an `output_type` defined -- an object of type `last_agent.output_type`, if the agent had an output type defined. - -!!! note - - `final_output` is of type `Any`. We can't statically type this, because of handoffs. If handoffs occur, that means any Agent might be the last agent, so we don't statically know the set of possible output types. - -## Inputs for the next turn - -You can use [`result.to_input_list()`][agents.result.RunResultBase.to_input_list] to turn the result into an input list that concatenates the original input you provided, to the items generated during the agent run. This makes it convenient to take the outputs of one agent run and pass them into another run, or to run it in a loop and append new user inputs each time. - -## Last agent - -The [`last_agent`][agents.result.RunResultBase.last_agent] property contains the last agent that ran. Depending on your application, this is often useful for the next time the user inputs something. For example, if you have a frontline triage agent that hands off to a language-specific agent, you can store the last agent, and re-use it the next time the user messages the agent. - -## New items - -The [`new_items`][agents.result.RunResultBase.new_items] property contains the new items generated during the run. The items are [`RunItem`][agents.items.RunItem]s. A run item wraps the raw item generated by the LLM. - -- [`MessageOutputItem`][agents.items.MessageOutputItem] indicates a message from the LLM. The raw item is the message generated. -- [`HandoffCallItem`][agents.items.HandoffCallItem] indicates that the LLM called the handoff tool. The raw item is the tool call item from the LLM. -- [`HandoffOutputItem`][agents.items.HandoffOutputItem] indicates that a handoff occurred. The raw item is the tool response to the handoff tool call. You can also access the source/target agents from the item. -- [`ToolCallItem`][agents.items.ToolCallItem] indicates that the LLM invoked a tool. -- [`ToolCallOutputItem`][agents.items.ToolCallOutputItem] indicates that a tool was called. The raw item is the tool response. You can also access the tool output from the item. -- [`ReasoningItem`][agents.items.ReasoningItem] indicates a reasoning item from the LLM. The raw item is the reasoning generated. - -## Other information - -### Guardrail results - -The [`input_guardrail_results`][agents.result.RunResultBase.input_guardrail_results] and [`output_guardrail_results`][agents.result.RunResultBase.output_guardrail_results] properties contain the results of the guardrails, if any. Guardrail results can sometimes contain useful information you want to log or store, so we make these available to you. - -### Raw responses - -The [`raw_responses`][agents.result.RunResultBase.raw_responses] property contains the [`ModelResponse`][agents.items.ModelResponse]s generated by the LLM. - -### Original input - -The [`input`][agents.result.RunResultBase.input] property contains the original input you provided to the `run` method. In most cases you won't need this, but it's available in case you do. diff --git a/docs/content/docs/agents/running_agents.mdx b/docs/content/docs/agents/running_agents.mdx deleted file mode 100644 index cd2392439..000000000 --- a/docs/content/docs/agents/running_agents.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: Running Agents -description: Execute agents synchronously or asynchronously ---- - -You can run agents via the [`Runner`][agents.run.Runner] class. You have 3 options: - -1. [`Runner.run()`][agents.run.Runner.run], which runs async and returns a [`RunResult`][agents.result.RunResult]. -2. [`Runner.run_sync()`][agents.run.Runner.run_sync], which is a sync method and just runs `.run()` under the hood. -3. [`Runner.run_streamed()`][agents.run.Runner.run_streamed], which runs async and returns a [`RunResultStreaming`][agents.result.RunResultStreaming]. It calls the LLM in streaming mode, and streams those events to you as they are received. - -```python -from hanzo_agent import Agent, Runner - -async def main(): - agent = Agent(name="Assistant", instructions="You are a helpful assistant") - - result = await Runner.run(agent, "Write a haiku about recursion in programming.") - print(result.final_output) - # Code within the code, - # Functions calling themselves, - # Infinite loop's dance. -``` - -Read more in the [results guide](results.md). - -## The agent loop - -When you use the run method in `Runner`, you pass in a starting agent and input. The input can either be a string (which is considered a user message), or a list of input items, which are the items in the Hanzo AI Responses API. - -The runner then runs a loop: - -1. We call the LLM for the current agent, with the current input. -2. The LLM produces its output. - 1. If the LLM returns a `final_output`, the loop ends and we return the result. - 2. If the LLM does a handoff, we update the current agent and input, and re-run the loop. - 3. If the LLM produces tool calls, we run those tool calls, append the results, and re-run the loop. -3. If we exceed the `max_turns` passed, we raise a [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded] exception. - -!!! note - - The rule for whether the LLM output is considered as a "final output" is that it produces text output with the desired type, and there are no tool calls. - -## Streaming - -Streaming allows you to additionally receive streaming events as the LLM runs. Once the stream is done, the [`RunResultStreaming`][agents.result.RunResultStreaming] will contain the complete information about the run, including all the new outputs produces. You can call `.stream_events()` for the streaming events. Read more in the [streaming guide](streaming.md). - -## Run config - -The `run_config` parameter lets you configure some global settings for the agent run: - -- [`model`][agents.run.RunConfig.model]: Allows setting a global LLM model to use, irrespective of what `model` each Agent has. -- [`model_provider`][agents.run.RunConfig.model_provider]: A model provider for looking up model names, which defaults to Hanzo AI. -- [`model_settings`][agents.run.RunConfig.model_settings]: Overrides agent-specific settings. For example, you can set a global `temperature` or `top_p`. -- [`input_guardrails`][agents.run.RunConfig.input_guardrails], [`output_guardrails`][agents.run.RunConfig.output_guardrails]: A list of input or output guardrails to include on all runs. -- [`handoff_input_filter`][agents.run.RunConfig.handoff_input_filter]: A global input filter to apply to all handoffs, if the handoff doesn't already have one. The input filter allows you to edit the inputs that are sent to the new agent. See the documentation in [`Handoff.input_filter`][agents.handoffs.Handoff.input_filter] for more details. -- [`tracing_disabled`][agents.run.RunConfig.tracing_disabled]: Allows you to disable [tracing](tracing.md) for the entire run. -- [`trace_include_sensitive_data`][agents.run.RunConfig.trace_include_sensitive_data]: Configures whether traces will include potentially sensitive data, such as LLM and tool call inputs/outputs. -- [`workflow_name`][agents.run.RunConfig.workflow_name], [`trace_id`][agents.run.RunConfig.trace_id], [`group_id`][agents.run.RunConfig.group_id]: Sets the tracing workflow name, trace ID and trace group ID for the run. We recommend at least setting `workflow_name`. The session ID is an optional field that lets you link traces across multiple runs. -- [`trace_metadata`][agents.run.RunConfig.trace_metadata]: Metadata to include on all traces. - -## Conversations/chat threads - -Calling any of the run methods can result in one or more agents running (and hence one or more LLM calls), but it represents a single logical turn in a chat conversation. For example: - -1. User turn: user enter text -2. Runner run: first agent calls LLM, runs tools, does a handoff to a second agent, second agent runs more tools, and then produces an output. - -At the end of the agent run, you can choose what to show to the user. For example, you might show the user every new item generated by the agents, or just the final output. Either way, the user might then ask a followup question, in which case you can call the run method again. - -You can use the base [`RunResultBase.to_input_list()`][agents.result.RunResultBase.to_input_list] method to get the inputs for the next turn. - -```python -async def main(): - agent = Agent(name="Assistant", instructions="Reply very concisely.") - - with trace(workflow_name="Conversation", group_id=thread_id): - # First turn - result = await Runner.run(agent, "What city is the Golden Gate Bridge in?") - print(result.final_output) - # San Francisco - - # Second turn - new_input = result.to_input_list() + [{"role": "user", "content": "What state is it in?"}] - result = await Runner.run(agent, new_input) - print(result.final_output) - # California -``` - -## Exceptions - -The SDK raises exceptions in certain cases. The full list is in [`agents.exceptions`][]. As an overview: - -- [`AgentsException`][agents.exceptions.AgentsException] is the base class for all exceptions raised in the SDK. -- [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded] is raised when the run exceeds the `max_turns` passed to the run methods. -- [`ModelBehaviorError`][agents.exceptions.ModelBehaviorError] is raised when the model produces invalid outputs, e.g. malformed JSON or using non-existent tools. -- [`UserError`][agents.exceptions.UserError] is raised when you (the person writing code using the SDK) make an error using the SDK. -- [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered], [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered] is raised when a [guardrail](guardrails.md) is tripped. diff --git a/docs/content/docs/agents/streaming.mdx b/docs/content/docs/agents/streaming.mdx deleted file mode 100644 index a75ad8a08..000000000 --- a/docs/content/docs/agents/streaming.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: Streaming -description: Stream agent responses in real-time ---- - -Streaming lets you subscribe to updates of the agent run as it proceeds. This can be useful for showing the end-user progress updates and partial responses. - -To stream, you can call [`Runner.run_streamed()`][agents.run.Runner.run_streamed], which will give you a [`RunResultStreaming`][agents.result.RunResultStreaming]. Calling `result.stream_events()` gives you an async stream of [`StreamEvent`][agents.stream_events.StreamEvent] objects, which are described below. - -## Raw response events - -[`RawResponsesStreamEvent`][agents.stream_events.RawResponsesStreamEvent] are raw events passed directly from the LLM. They are in Hanzo AI Responses API format, which means each event has a type (like `response.created`, `response.output_text.delta`, etc) and data. These events are useful if you want to stream response messages to the user as soon as they are generated. - -For example, this will output the text generated by the LLM token-by-token. - -```python -import asyncio -from openai.types.responses import ResponseTextDeltaEvent -from hanzo_agent import Agent, Runner - -async def main(): - agent = Agent( - name="Joker", - instructions="You are a helpful assistant.", - ) - - result = Runner.run_streamed(agent, input="Please tell me 5 jokes.") - async for event in result.stream_events(): - if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent): - print(event.data.delta, end="", flush=True) - - -if __name__ == "__main__": - asyncio.run(main()) -``` - -## Run item events and agent events - -[`RunItemStreamEvent`][agents.stream_events.RunItemStreamEvent]s are higher level events. They inform you when an item has been fully generated. This allows you to push progress updates at the level of "message generated", "tool ran", etc, instead of each token. Similarly, [`AgentUpdatedStreamEvent`][agents.stream_events.AgentUpdatedStreamEvent] gives you updates when the current agent changes (e.g. as the result of a handoff). - -For example, this will ignore raw events and stream updates to the user. - -```python -import asyncio -import random -from hanzo_agent import Agent, ItemHelpers, Runner, function_tool - -@function_tool -def how_many_jokes() -> int: - return random.randint(1, 10) - - -async def main(): - agent = Agent( - name="Joker", - instructions="First call the `how_many_jokes` tool, then tell that many jokes.", - tools=[how_many_jokes], - ) - - result = Runner.run_streamed( - agent, - input="Hello", - ) - print("=== Run starting ===") - - async for event in result.stream_events(): - # We'll ignore the raw responses event deltas - if event.type == "raw_response_event": - continue - # When the agent updates, print that - elif event.type == "agent_updated_stream_event": - print(f"Agent updated: {event.new_agent.name}") - continue - # When items are generated, print them - elif event.type == "run_item_stream_event": - if event.item.type == "tool_call_item": - print("-- Tool was called") - elif event.item.type == "tool_call_output_item": - print(f"-- Tool output: {event.item.output}") - elif event.item.type == "message_output_item": - print(f"-- Message output:\n {ItemHelpers.text_message_output(event.item)}") - else: - pass # Ignore other event types - - print("=== Run complete ===") - - -if __name__ == "__main__": - asyncio.run(main()) -``` diff --git a/docs/content/docs/agents/tools.mdx b/docs/content/docs/agents/tools.mdx deleted file mode 100644 index ed78277a0..000000000 --- a/docs/content/docs/agents/tools.mdx +++ /dev/null @@ -1,273 +0,0 @@ ---- -title: Tools -description: Define and use tools with your agents ---- - -Tools let agents take actions: things like fetching data, running code, calling external APIs, and even using a computer. There are three classes of tools in the Agent SDK: - -- Hosted tools: these run on LLM servers alongside the AI models. Hanzo AI offers retrieval, web search and computer use as hosted tools. -- Function calling: these allow you to use any Python function as a tool. -- Agents as tools: this allows you to use an agent as a tool, allowing Agents to call other agents without handing off to them. - -## Hosted tools - -Hanzo AI offers a few built-in tools when using the [`Hanzo AIResponsesModel`][agents.models.openai_responses.Hanzo AIResponsesModel]: - -- The [`WebSearchTool`][agents.tool.WebSearchTool] lets an agent search the web. -- The [`FileSearchTool`][agents.tool.FileSearchTool] allows retrieving information from your Hanzo AI Vector Stores. -- The [`ComputerTool`][agents.tool.ComputerTool] allows automating computer use tasks. - -```python -from hanzo_agent import Agent, FileSearchTool, Runner, WebSearchTool - -agent = Agent( - name="Assistant", - tools=[ - WebSearchTool(), - FileSearchTool( - max_num_results=3, - vector_store_ids=["VECTOR_STORE_ID"], - ), - ], -) - -async def main(): - result = await Runner.run(agent, "Which coffee shop should I go to, taking into account my preferences and the weather today in SF?") - print(result.final_output) -``` - -## Function tools - -You can use any Python function as a tool. The Agent SDK will setup the tool automatically: - -- The name of the tool will be the name of the Python function (or you can provide a name) -- Tool description will be taken from the docstring of the function (or you can provide a description) -- The schema for the function inputs is automatically created from the function's arguments -- Descriptions for each input are taken from the docstring of the function, unless disabled - -We use Python's `inspect` module to extract the function signature, along with [`griffe`](https://mkdocstrings.github.io/griffe/) to parse docstrings and `pydantic` for schema creation. - -```python -import json - -from typing_extensions import TypedDict, Any - -from hanzo_agent import Agent, FunctionTool, RunContextWrapper, function_tool - - -class Location(TypedDict): - lat: float - long: float - -@function_tool # (1)! -async def fetch_weather(location: Location) -> str: - # (2)! - """Fetch the weather for a given location. - - Args: - location: The location to fetch the weather for. - """ - # In real life, we'd fetch the weather from a weather API - return "sunny" - - -@function_tool(name_override="fetch_data") # (3)! -def read_file(ctx: RunContextWrapper[Any], path: str, directory: str | None = None) -> str: - """Read the contents of a file. - - Args: - path: The path to the file to read. - directory: The directory to read the file from. - """ - # In real life, we'd read the file from the file system - return "" - - -agent = Agent( - name="Assistant", - tools=[fetch_weather, read_file], # (4)! -) - -for tool in agent.tools: - if isinstance(tool, FunctionTool): - print(tool.name) - print(tool.description) - print(json.dumps(tool.params_json_schema, indent=2)) - print() - -``` - -1. You can use any Python types as arguments to your functions, and the function can be sync or async. -2. Docstrings, if present, are used to capture descriptions and argument descriptions -3. Functions can optionally take the `context` (must be the first argument). You can also set overrides, like the name of the tool, description, which docstring style to use, etc. -4. You can pass the decorated functions to the list of tools. - -??? note "Expand to see output" - - ``` - fetch_weather - Fetch the weather for a given location. - { - "$defs": { - "Location": { - "properties": { - "lat": { - "title": "Lat", - "type": "number" - }, - "long": { - "title": "Long", - "type": "number" - } - }, - "required": [ - "lat", - "long" - ], - "title": "Location", - "type": "object" - } - }, - "properties": { - "location": { - "$ref": "#/$defs/Location", - "description": "The location to fetch the weather for." - } - }, - "required": [ - "location" - ], - "title": "fetch_weather_args", - "type": "object" - } - - fetch_data - Read the contents of a file. - { - "properties": { - "path": { - "description": "The path to the file to read.", - "title": "Path", - "type": "string" - }, - "directory": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "The directory to read the file from.", - "title": "Directory" - } - }, - "required": [ - "path" - ], - "title": "fetch_data_args", - "type": "object" - } - ``` - -### Custom function tools - -Sometimes, you don't want to use a Python function as a tool. You can directly create a [`FunctionTool`][agents.tool.FunctionTool] if you prefer. You'll need to provide: - -- `name` -- `description` -- `params_json_schema`, which is the JSON schema for the arguments -- `on_invoke_tool`, which is an async function that receives the context and the arguments as a JSON string, and must return the tool output as a string. - -```python -from typing import Any - -from pydantic import BaseModel - -from hanzo_agent import RunContextWrapper, FunctionTool - - - -def do_some_work(data: str) -> str: - return "done" - - -class FunctionArgs(BaseModel): - username: str - age: int - - -async def run_function(ctx: RunContextWrapper[Any], args: str) -> str: - parsed = FunctionArgs.model_validate_json(args) - return do_some_work(data=f"{parsed.username} is {parsed.age} years old") - - -tool = FunctionTool( - name="process_user", - description="Processes extracted user data", - params_json_schema=FunctionArgs.model_json_schema(), - on_invoke_tool=run_function, -) -``` - -### Automatic argument and docstring parsing - -As mentioned before, we automatically parse the function signature to extract the schema for the tool, and we parse the docstring to extract descriptions for the tool and for individual arguments. Some notes on that: - -1. The signature parsing is done via the `inspect` module. We use type annotations to understand the types for the arguments, and dynamically build a Pydantic model to represent the overall schema. It supports most types, including Python primitives, Pydantic models, TypedDicts, and more. -2. We use `griffe` to parse docstrings. Supported docstring formats are `google`, `sphinx` and `numpy`. We attempt to automatically detect the docstring format, but this is best-effort and you can explicitly set it when calling `function_tool`. You can also disable docstring parsing by setting `use_docstring_info` to `False`. - -The code for the schema extraction lives in [`agents.function_schema`][]. - -## Agents as tools - -In some workflows, you may want a central agent to orchestrate a network of specialized agents, instead of handing off control. You can do this by modeling agents as tools. - -```python -from hanzo_agent import Agent, Runner -import asyncio - -spanish_agent = Agent( - name="Spanish agent", - instructions="You translate the user's message to Spanish", -) - -french_agent = Agent( - name="French agent", - instructions="You translate the user's message to French", -) - -orchestrator_agent = Agent( - name="orchestrator_agent", - instructions=( - "You are a translation agent. You use the tools given to you to translate." - "If asked for multiple translations, you call the relevant tools." - ), - tools=[ - spanish_agent.as_tool( - tool_name="translate_to_spanish", - tool_description="Translate the user's message to Spanish", - ), - french_agent.as_tool( - tool_name="translate_to_french", - tool_description="Translate the user's message to French", - ), - ], -) - -async def main(): - result = await Runner.run(orchestrator_agent, input="Say 'Hello, how are you?' in Spanish.") - print(result.final_output) -``` - -## Handling errors in function tools - -When you create a function tool via `@function_tool`, you can pass a `failure_error_function`. This is a function that provides an error response to the LLM in case the tool call crashes. - -- By default (i.e. if you don't pass anything), it runs a `default_tool_error_function` which tells the LLM an error occurred. -- If you pass your own error function, it runs that instead, and sends the response to the LLM. -- If you explicitly pass `None`, then any tool call errors will be re-raised for you to handle. This could be a `ModelBehaviorError` if the model produced invalid JSON, or a `UserError` if your code crashed, etc. - -If you are manually creating a `FunctionTool` object, then you must handle errors inside the `on_invoke_tool` function. diff --git a/docs/content/docs/agents/tracing.mdx b/docs/content/docs/agents/tracing.mdx deleted file mode 100644 index a85ca5040..000000000 --- a/docs/content/docs/agents/tracing.mdx +++ /dev/null @@ -1,100 +0,0 @@ ---- -title: Tracing -description: Visualize and debug your agentic flows ---- - -The Agent SDK includes built-in tracing, collecting a comprehensive record of events during an agent run: LLM generations, tool calls, handoffs, guardrails, and even custom events that occur. Using the [Traces dashboard](https://platform.openai.com/traces), you can debug, visualize, and monitor your workflows during development and in production. - -!!!note - - Tracing is enabled by default. There are two ways to disable tracing: - - 1. You can globally disable tracing by setting the env var `OPENAI_AGENTS_DISABLE_TRACING=1` - 2. You can disable tracing for a single run by setting [`agents.run.RunConfig.tracing_disabled`][] to `True` - -## Traces and spans - -- **Traces** represent a single end-to-end operation of a "workflow". They're composed of Spans. Traces have the following properties: - - `workflow_name`: This is the logical workflow or app. For example "Code generation" or "Customer service". - - `trace_id`: A unique ID for the trace. Automatically generated if you don't pass one. Must have the format `trace_<32_alphanumeric>`. - - `group_id`: Optional group ID, to link multiple traces from the same conversation. For example, you might use a chat thread ID. - - `disabled`: If True, the trace will not be recorded. - - `metadata`: Optional metadata for the trace. -- **Spans** represent operations that have a start and end time. Spans have: - - `started_at` and `ended_at` timestamps. - - `trace_id`, to represent the trace they belong to - - `parent_id`, which points to the parent Span of this Span (if any) - - `span_data`, which is information about the Span. For example, `AgentSpanData` contains information about the Agent, `GenerationSpanData` contains information about the LLM generation, etc. - -## Default tracing - -By default, the SDK traces the following: - -- The entire `Runner.{run, run_sync, run_streamed}()` is wrapped in a `trace()`. -- Each time an agent runs, it is wrapped in `agent_span()` -- LLM generations are wrapped in `generation_span()` -- Function tool calls are each wrapped in `function_span()` -- Guardrails are wrapped in `guardrail_span()` -- Handoffs are wrapped in `handoff_span()` - -By default, the trace is named "Agent trace". You can set this name if you use `trace`, or you can can configure the name and other properties with the [`RunConfig`][agents.run.RunConfig]. - -In addition, you can set up [custom trace processors](#custom-tracing-processors) to push traces to other destinations (as a replacement, or secondary destination). - -## Higher level traces - -Sometimes, you might want multiple calls to `run()` to be part of a single trace. You can do this by wrapping the entire code in a `trace()`. - -```python -from hanzo_agent import Agent, Runner, trace - -async def main(): - agent = Agent(name="Joke generator", instructions="Tell funny jokes.") - - with trace("Joke workflow"): # (1)! - first_result = await Runner.run(agent, "Tell me a joke") - second_result = await Runner.run(agent, f"Rate this joke: {first_result.final_output}") - print(f"Joke: {first_result.final_output}") - print(f"Rating: {second_result.final_output}") -``` - -1. Because the two calls to `Runner.run` are wrapped in a `with trace()`, the individual runs will be part of the overall trace rather than creating two traces. - -## Creating traces - -You can use the [`trace()`][agents.tracing.trace] function to create a trace. Traces need to be started and finished. You have two options to do so: - -1. **Recommended**: use the trace as a context manager, i.e. `with trace(...) as my_trace`. This will automatically start and end the trace at the right time. -2. You can also manually call [`trace.start()`][agents.tracing.Trace.start] and [`trace.finish()`][agents.tracing.Trace.finish]. - -The current trace is tracked via a Python [`contextvar`](https://docs.python.org/3/library/contextvars.html). This means that it works with concurrency automatically. If you manually start/end a trace, you'll need to pass `mark_as_current` and `reset_current` to `start()`/`finish()` to update the current trace. - -## Creating spans - -You can use the various [`*_span()`][agents.tracing.create] methods to create a span. In general, you don't need to manually create spans. A [`custom_span()`][agents.tracing.custom_span] function is available for tracking custom span information. - -Spans are automatically part of the current trace, and are nested under the nearest current span, which is tracked via a Python [`contextvar`](https://docs.python.org/3/library/contextvars.html). - -## Sensitive data - -Some spans track potentially sensitive data. For example, the `generation_span()` stores the inputs/outputs of the LLM generation, and `function_span()` stores the inputs/outputs of function calls. These may contain sensitive data, so you can disable capturing that data via [`RunConfig.trace_include_sensitive_data`][agents.run.RunConfig.trace_include_sensitive_data]. - -## Custom tracing processors - -The high level architecture for tracing is: - -- At initialization, we create a global [`TraceProvider`][agents.tracing.setup.TraceProvider], which is responsible for creating traces. -- We configure the `TraceProvider` with a [`BatchTraceProcessor`][agents.tracing.processors.BatchTraceProcessor] that sends traces/spans in batches to a [`BackendSpanExporter`][agents.tracing.processors.BackendSpanExporter], which exports the spans and traces to the Hanzo AI backend in batches. - -To customize this default setup, to send traces to alternative or additional backends or modifying exporter behavior, you have two options: - -1. [`add_trace_processor()`][agents.tracing.add_trace_processor] lets you add an **additional** trace processor that will receive traces and spans as they are ready. This lets you do your own processing in addition to sending traces to Hanzo AI's backend. -2. [`set_trace_processors()`][agents.tracing.set_trace_processors] lets you **replace** the default processors with your own trace processors. This means traces will not be sent to the Hanzo AI backend unless you include a `TracingProcessor` that does so. - -External trace processors include: - -- [Braintrust](https://braintrust.dev/docs/guides/traces/integrations#hanzo-agent-sdk) -- [Pydantic Logfire](https://logfire.pydantic.dev/docs/integrations/llms/openai/#hanzo-agent) -- [AgentOps](https://docs.agentops.ai/v1/integrations/agentssdk) -- [Scorecard](https://docs.scorecard.io/docs/documentation/features/tracing#hanzo-agent-sdk-integration)) -- [Keywords AI](https://docs.keywordsai.co/integration/development-frameworks/openai-agent) diff --git a/docs/content/docs/chat.mdx b/docs/content/docs/chat.mdx deleted file mode 100644 index 2a6bb160e..000000000 --- a/docs/content/docs/chat.mdx +++ /dev/null @@ -1,317 +0,0 @@ ---- -title: Chat Completions -description: Generate chat completions with the Hanzo API ---- - -import { Callout } from 'fumadocs-ui/components/callout' -import { Tab, Tabs } from 'fumadocs-ui/components/tabs' - -# Chat Completions - -The Chat Completions API allows you to generate responses from language models using a conversational message format. - -## Basic Usage - -```python -from hanzoai import Hanzo - -client = Hanzo() - -response = client.chat.completions.create( - model="gpt-4o", - messages=[ - {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": "What is the capital of France?"} - ] -) - -print(response.choices[0].message.content) -``` - -## Message Roles - -Messages support three roles: - -| Role | Description | -|------|-------------| -| `system` | Sets the behavior and context for the assistant | -| `user` | Messages from the user | -| `assistant` | Previous responses from the assistant | - -```python -messages = [ - {"role": "system", "content": "You are a Python expert."}, - {"role": "user", "content": "How do I read a file?"}, - {"role": "assistant", "content": "You can use open()..."}, - {"role": "user", "content": "What about async?"} -] -``` - -## Streaming - -Stream responses for real-time output: - - - - ```python - stream = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Write a story"}], - stream=True - ) - - for chunk in stream: - if chunk.choices[0].delta.content: - print(chunk.choices[0].delta.content, end="", flush=True) - ``` - - - ```python - stream = await client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Write a story"}], - stream=True - ) - - async for chunk in stream: - if chunk.choices[0].delta.content: - print(chunk.choices[0].delta.content, end="", flush=True) - ``` - - - -## Parameters - -### Temperature - -Control randomness (0.0 to 2.0): - -```python -response = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Write a haiku"}], - temperature=0.7, # More creative -) - -response = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "What is 2+2?"}], - temperature=0.0, # Deterministic -) -``` - -### Max Tokens - -Limit response length: - -```python -response = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Summarize this article"}], - max_tokens=100, -) -``` - -### Top P - -Nucleus sampling parameter: - -```python -response = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Generate ideas"}], - top_p=0.9, -) -``` - -### Stop Sequences - -Stop generation at specific strings: - -```python -response = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "List three items:"}], - stop=["4.", "\n\n"], -) -``` - -### Presence and Frequency Penalty - -Reduce repetition: - -```python -response = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Write a paragraph"}], - presence_penalty=0.6, # Encourage new topics - frequency_penalty=0.5, # Reduce word repetition -) -``` - -## Tool Calling - -Enable function/tool calling: - -```python -tools = [ - { - "type": "function", - "function": { - "name": "get_weather", - "description": "Get current weather for a location", - "parameters": { - "type": "object", - "properties": { - "location": { - "type": "string", - "description": "City name" - } - }, - "required": ["location"] - } - } - } -] - -response = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "What's the weather in Paris?"}], - tools=tools, - tool_choice="auto", -) - -# Check for tool calls -if response.choices[0].message.tool_calls: - for tool_call in response.choices[0].message.tool_calls: - print(f"Function: {tool_call.function.name}") - print(f"Arguments: {tool_call.function.arguments}") -``` - -## JSON Mode - -Force JSON output: - -```python -response = client.chat.completions.create( - model="gpt-4o", - messages=[ - {"role": "system", "content": "Return valid JSON only."}, - {"role": "user", "content": "List 3 colors with hex codes"} - ], - response_format={"type": "json_object"}, -) - -import json -data = json.loads(response.choices[0].message.content) -``` - -## Vision - -Analyze images with vision-capable models: - -```python -response = client.chat.completions.create( - model="gpt-4o", - messages=[ - { - "role": "user", - "content": [ - {"type": "text", "text": "What's in this image?"}, - { - "type": "image_url", - "image_url": { - "url": "https://example.com/image.jpg" - } - } - ] - } - ], -) -``` - -Or with base64 encoded images: - -```python -import base64 - -with open("image.png", "rb") as f: - image_data = base64.b64encode(f.read()).decode() - -response = client.chat.completions.create( - model="gpt-4o", - messages=[ - { - "role": "user", - "content": [ - {"type": "text", "text": "Describe this image"}, - { - "type": "image_url", - "image_url": { - "url": f"data:image/png;base64,{image_data}" - } - } - ] - } - ], -) -``` - -## Response Object - -The response contains: - -```python -response = client.chat.completions.create(...) - -# Access the response -print(response.id) # Unique ID -print(response.model) # Model used -print(response.choices[0].message.content) # Response text -print(response.choices[0].finish_reason) # "stop", "length", etc. -print(response.usage.prompt_tokens) # Input tokens -print(response.usage.completion_tokens) # Output tokens -print(response.usage.total_tokens) # Total tokens -``` - -## Multiple Responses - -Generate multiple responses: - -```python -response = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Give me a startup idea"}], - n=3, # Generate 3 responses -) - -for i, choice in enumerate(response.choices): - print(f"Option {i+1}: {choice.message.content}") -``` - -## Using Different Providers - -Access models from various providers: - -```python -# OpenAI -client.chat.completions.create(model="gpt-4o", ...) - -# Anthropic Claude -client.chat.completions.create(model="claude-3-5-sonnet-20241022", ...) - -# Google Gemini -client.chat.completions.create(model="gemini/gemini-1.5-pro", ...) - -# Together AI -client.chat.completions.create(model="together_ai/meta-llama/Llama-3-70b-chat-hf", ...) - -# Mistral -client.chat.completions.create(model="mistral/mistral-large-latest", ...) -``` - -## Next Steps - -- [Embeddings](/docs/python-sdk/embeddings) - Generate text embeddings -- [Models](/docs/python-sdk/models) - List available models -- [Files](/docs/python-sdk/files) - Upload and manage files diff --git a/docs/content/docs/client.mdx b/docs/content/docs/client.mdx deleted file mode 100644 index bcdbd3f1a..000000000 --- a/docs/content/docs/client.mdx +++ /dev/null @@ -1,227 +0,0 @@ ---- -title: Client Configuration -description: Configure the Hanzo client for your needs ---- - -import { Callout } from 'fumadocs-ui/components/callout' -import { Tab, Tabs } from 'fumadocs-ui/components/tabs' - -# Client Configuration - -The Hanzo client supports extensive configuration options for authentication, timeouts, retries, and more. - -## Basic Initialization - - - - ```python - from hanzoai import Hanzo - - client = Hanzo( - api_key="your-api-key", # or use HANZO_API_KEY env var - ) - ``` - - - ```python - from hanzoai import AsyncHanzo - - client = AsyncHanzo( - api_key="your-api-key", - ) - ``` - - - -## Configuration Options - -### API Key - -```python -from hanzoai import Hanzo - -# From parameter -client = Hanzo(api_key="your-api-key") - -# From environment variable (automatic) -# export HANZO_API_KEY="your-api-key" -client = Hanzo() -``` - -### Base URL - -Override the API endpoint: - -```python -client = Hanzo( - base_url="https://custom-api.example.com", -) -``` - -### Timeouts - -Configure request timeouts: - -```python -from hanzoai import Hanzo, Timeout - -client = Hanzo( - timeout=Timeout( - connect=5.0, # Connection timeout - read=60.0, # Read timeout - write=30.0, # Write timeout - pool=10.0, # Pool timeout - ) -) - -# Or use a simple float for all timeouts -client = Hanzo(timeout=60.0) -``` - -### Retries - -Configure automatic retries: - -```python -client = Hanzo( - max_retries=3, # Default is 2 -) -``` - -### HTTP Client - -Use a custom HTTP client: - -```python -import httpx -from hanzoai import Hanzo - -# Custom httpx client -http_client = httpx.Client( - proxies="http://proxy.example.com:8080", - verify=False, # Disable SSL verification (not recommended) -) - -client = Hanzo( - http_client=http_client, -) -``` - -## Request-Level Options - -Override options per-request: - -```python -response = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Hello!"}], - timeout=120.0, # Override timeout for this request - extra_headers={"X-Custom-Header": "value"}, -) -``` - -## Raw Responses - -Access raw HTTP response data: - -```python -# Get response with raw HTTP info -response = client.chat.completions.with_raw_response.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Hello!"}] -) - -print(response.http_response.status_code) -print(response.http_response.headers) -print(response.parsed) # The parsed response object -``` - -## Streaming Raw Responses - -```python -with client.chat.completions.with_streaming_response.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Hello!"}], - stream=True -) as response: - print(response.http_response.status_code) - for chunk in response.iter_lines(): - print(chunk) -``` - -## Context Manager - -Use the client as a context manager for automatic cleanup: - -```python -from hanzoai import Hanzo - -with Hanzo() as client: - response = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Hello!"}] - ) -``` - -## Async Context Manager - -```python -from hanzoai import AsyncHanzo - -async def main(): - async with AsyncHanzo() as client: - response = await client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Hello!"}] - ) -``` - -## Environment Variables - -| Variable | Description | Default | -|----------|-------------|---------| -| `HANZO_API_KEY` | API authentication key | Required | -| `HANZO_BASE_URL` | API base URL | `https://api.hanzo.ai` | -| `HANZO_LOG` | Logging level | `warning` | - -## Logging - -Enable debug logging: - -```bash -export HANZO_LOG=debug -``` - -Or configure programmatically: - -```python -import logging - -logging.getLogger("hanzoai").setLevel(logging.DEBUG) -``` - -## Thread Safety - -The `Hanzo` client is thread-safe. You can share a single instance across threads: - -```python -from concurrent.futures import ThreadPoolExecutor -from hanzoai import Hanzo - -client = Hanzo() - -def make_request(prompt): - return client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": prompt}] - ) - -with ThreadPoolExecutor(max_workers=10) as executor: - results = list(executor.map(make_request, prompts)) -``` - -## Next Steps - -- [Chat Completions](/docs/python-sdk/chat) - Make chat requests -- [Embeddings](/docs/python-sdk/embeddings) - Generate embeddings -- [Models](/docs/python-sdk/models) - List available models diff --git a/docs/content/docs/embeddings.mdx b/docs/content/docs/embeddings.mdx deleted file mode 100644 index 39dc4dc84..000000000 --- a/docs/content/docs/embeddings.mdx +++ /dev/null @@ -1,291 +0,0 @@ ---- -title: Embeddings -description: Generate text embeddings with the Hanzo API ---- - -import { Callout } from 'fumadocs-ui/components/callout' -import { Tab, Tabs } from 'fumadocs-ui/components/tabs' - -# Embeddings - -The Embeddings API generates vector representations of text that can be used for semantic search, clustering, and similarity comparisons. - -## Basic Usage - -```python -from hanzoai import Hanzo - -client = Hanzo() - -response = client.embeddings.create( - model="text-embedding-3-small", - input="Hello, world!" -) - -embedding = response.data[0].embedding -print(f"Dimensions: {len(embedding)}") -``` - -## Multiple Inputs - -Generate embeddings for multiple texts at once: - -```python -response = client.embeddings.create( - model="text-embedding-3-small", - input=[ - "First document", - "Second document", - "Third document" - ] -) - -for i, data in enumerate(response.data): - print(f"Document {i}: {len(data.embedding)} dimensions") -``` - -## Embedding Models - -| Model | Dimensions | Description | -|-------|------------|-------------| -| `text-embedding-3-small` | 1536 | Fast, efficient | -| `text-embedding-3-large` | 3072 | Higher quality | -| `text-embedding-ada-002` | 1536 | Legacy model | - -```python -# High quality embeddings -response = client.embeddings.create( - model="text-embedding-3-large", - input="Important document" -) -``` - -## Dimension Reduction - -Reduce embedding dimensions for efficiency: - -```python -response = client.embeddings.create( - model="text-embedding-3-small", - input="Hello, world!", - dimensions=256 # Reduce from 1536 to 256 -) - -print(f"Dimensions: {len(response.data[0].embedding)}") # 256 -``` - -## Semantic Search - -Use embeddings for semantic search: - -```python -import numpy as np -from hanzoai import Hanzo - -client = Hanzo() - -def get_embedding(text): - response = client.embeddings.create( - model="text-embedding-3-small", - input=text - ) - return response.data[0].embedding - -def cosine_similarity(a, b): - return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) - -# Create document embeddings -documents = [ - "Python is a programming language", - "JavaScript runs in browsers", - "Machine learning uses algorithms", - "Cats are furry animals" -] - -doc_embeddings = [get_embedding(doc) for doc in documents] - -# Search -query = "coding languages" -query_embedding = get_embedding(query) - -# Find most similar -similarities = [cosine_similarity(query_embedding, doc_emb) for doc_emb in doc_embeddings] - -for doc, score in sorted(zip(documents, similarities), key=lambda x: x[1], reverse=True): - print(f"{score:.3f}: {doc}") -``` - -## Batch Processing - -Process large datasets efficiently: - -```python -from hanzoai import Hanzo - -client = Hanzo() - -def batch_embed(texts, batch_size=100): - """Embed texts in batches.""" - embeddings = [] - - for i in range(0, len(texts), batch_size): - batch = texts[i:i + batch_size] - response = client.embeddings.create( - model="text-embedding-3-small", - input=batch - ) - embeddings.extend([d.embedding for d in response.data]) - - return embeddings - -# Embed 1000 documents -documents = ["Document " + str(i) for i in range(1000)] -all_embeddings = batch_embed(documents) -``` - -## Async Embeddings - -Generate embeddings asynchronously: - -```python -import asyncio -from hanzoai import AsyncHanzo - -async def main(): - client = AsyncHanzo() - - response = await client.embeddings.create( - model="text-embedding-3-small", - input="Hello, world!" - ) - - print(f"Dimensions: {len(response.data[0].embedding)}") - -asyncio.run(main()) -``` - -## Parallel Async Embedding - -```python -import asyncio -from hanzoai import AsyncHanzo - -async def embed_document(client, text): - response = await client.embeddings.create( - model="text-embedding-3-small", - input=text - ) - return response.data[0].embedding - -async def main(): - client = AsyncHanzo() - - documents = [ - "First document", - "Second document", - "Third document" - ] - - # Embed all documents in parallel - embeddings = await asyncio.gather(*[ - embed_document(client, doc) for doc in documents - ]) - - print(f"Generated {len(embeddings)} embeddings") - -asyncio.run(main()) -``` - -## Response Object - -```python -response = client.embeddings.create( - model="text-embedding-3-small", - input="Hello" -) - -print(response.model) # Model used -print(response.usage.prompt_tokens) # Tokens used -print(response.usage.total_tokens) # Total tokens -print(response.data[0].index) # Input index -print(response.data[0].embedding[:5]) # First 5 dimensions -``` - -## Storage with Vector Databases - -Store embeddings in vector databases: - - - - ```python - import pinecone - from hanzoai import Hanzo - - client = Hanzo() - pinecone.init(api_key="your-key") - index = pinecone.Index("my-index") - - # Embed and store - response = client.embeddings.create( - model="text-embedding-3-small", - input="Document content" - ) - - index.upsert([ - ("doc-1", response.data[0].embedding, {"text": "Document content"}) - ]) - ``` - - - ```python - from qdrant_client import QdrantClient - from qdrant_client.models import PointStruct - from hanzoai import Hanzo - - client = Hanzo() - qdrant = QdrantClient(":memory:") - - response = client.embeddings.create( - model="text-embedding-3-small", - input="Document content" - ) - - qdrant.upsert( - collection_name="documents", - points=[ - PointStruct( - id=1, - vector=response.data[0].embedding, - payload={"text": "Document content"} - ) - ] - ) - ``` - - - ```python - import weaviate - from hanzoai import Hanzo - - client = Hanzo() - weaviate_client = weaviate.Client("http://localhost:8080") - - response = client.embeddings.create( - model="text-embedding-3-small", - input="Document content" - ) - - weaviate_client.data_object.create( - class_name="Document", - data_object={"text": "Document content"}, - vector=response.data[0].embedding - ) - ``` - - - -## Next Steps - -- [Models](/docs/python-sdk/models) - List available models -- [Files](/docs/python-sdk/files) - Upload and manage files -- [Chat](/docs/python-sdk/chat) - Generate chat completions diff --git a/docs/content/docs/files.mdx b/docs/content/docs/files.mdx deleted file mode 100644 index 4eb5a644b..000000000 --- a/docs/content/docs/files.mdx +++ /dev/null @@ -1,242 +0,0 @@ ---- -title: Files -description: Upload and manage files with the Hanzo API ---- - -import { Callout } from 'fumadocs-ui/components/callout' -import { Tab, Tabs } from 'fumadocs-ui/components/tabs' - -# Files - -The Files API allows you to upload, manage, and delete files for use with features like fine-tuning and assistants. - -## Upload a File - -```python -from hanzoai import Hanzo - -client = Hanzo() - -# Upload a file -with open("training_data.jsonl", "rb") as f: - file = client.files.create( - file=f, - purpose="fine-tune" - ) - -print(f"File ID: {file.id}") -print(f"Filename: {file.filename}") -print(f"Size: {file.bytes} bytes") -``` - -## File Purposes - -| Purpose | Description | -|---------|-------------| -| `fine-tune` | Training data for fine-tuning | -| `assistants` | Files for AI assistants | -| `batch` | Input files for batch processing | - -```python -# Fine-tuning data -file = client.files.create( - file=open("training.jsonl", "rb"), - purpose="fine-tune" -) - -# Assistant files -file = client.files.create( - file=open("document.pdf", "rb"), - purpose="assistants" -) -``` - -## List Files - -```python -# List all files -files = client.files.list() - -for file in files.data: - print(f"{file.id}: {file.filename} ({file.bytes} bytes)") - -# Filter by purpose -files = client.files.list(purpose="fine-tune") -``` - -## Retrieve File Info - -```python -file = client.files.retrieve("file-abc123") - -print(f"ID: {file.id}") -print(f"Filename: {file.filename}") -print(f"Purpose: {file.purpose}") -print(f"Size: {file.bytes} bytes") -print(f"Created: {file.created_at}") -print(f"Status: {file.status}") -``` - -## Download File Content - -```python -content = client.files.content("file-abc123") - -# Save to disk -with open("downloaded_file.jsonl", "wb") as f: - f.write(content.content) -``` - -## Delete a File - -```python -response = client.files.delete("file-abc123") - -print(f"Deleted: {response.deleted}") -``` - -## Async Usage - -```python -import asyncio -from hanzoai import AsyncHanzo - -async def main(): - client = AsyncHanzo() - - # Upload file - with open("data.jsonl", "rb") as f: - file = await client.files.create( - file=f, - purpose="fine-tune" - ) - - print(f"Uploaded: {file.id}") - - # List files - files = await client.files.list() - print(f"Total files: {len(files.data)}") - -asyncio.run(main()) -``` - -## Fine-Tuning Workflow - -Complete workflow for fine-tuning: - -```python -from hanzoai import Hanzo - -client = Hanzo() - -# 1. Upload training data -with open("training.jsonl", "rb") as f: - training_file = client.files.create( - file=f, - purpose="fine-tune" - ) - -print(f"Training file: {training_file.id}") - -# 2. Create fine-tuning job -job = client.fine_tuning.jobs.create( - training_file=training_file.id, - model="gpt-4o-mini-2024-07-18" -) - -print(f"Job ID: {job.id}") - -# 3. Monitor job status -job = client.fine_tuning.jobs.retrieve(job.id) -print(f"Status: {job.status}") - -# 4. Use fine-tuned model -if job.status == "succeeded": - response = client.chat.completions.create( - model=job.fine_tuned_model, - messages=[{"role": "user", "content": "Hello!"}] - ) -``` - -## Training Data Format - -For fine-tuning, use JSONL format: - -```jsonl -{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello"}, {"role": "assistant", "content": "Hi there!"}]} -{"messages": [{"role": "user", "content": "What's 2+2?"}, {"role": "assistant", "content": "4"}]} -``` - -## File Size Limits - - -File size limits vary by purpose: -- Fine-tuning: Up to 1 GB -- Assistants: Up to 512 MB -- Batch: Up to 200 MB - - -## Error Handling - -```python -from hanzoai import Hanzo -from hanzoai._exceptions import NotFoundError, BadRequestError - -client = Hanzo() - -try: - file = client.files.retrieve("invalid-file-id") -except NotFoundError: - print("File not found") -except BadRequestError as e: - print(f"Bad request: {e.message}") -``` - -## Batch File Upload - -Upload multiple files efficiently: - -```python -from pathlib import Path -from hanzoai import Hanzo - -client = Hanzo() - -def upload_files(directory: str, purpose: str = "assistants"): - """Upload all files from a directory.""" - uploaded = [] - - for filepath in Path(directory).glob("*"): - if filepath.is_file(): - with open(filepath, "rb") as f: - file = client.files.create( - file=f, - purpose=purpose - ) - uploaded.append(file) - print(f"Uploaded: {file.filename}") - - return uploaded - -files = upload_files("./documents", purpose="assistants") -``` - -## File Object Structure - -```python -file = client.files.retrieve("file-abc123") - -print(file.id) # Unique file ID -print(file.object) # "file" -print(file.bytes) # File size in bytes -print(file.created_at) # Creation timestamp -print(file.filename) # Original filename -print(file.purpose) # File purpose -print(file.status) # Processing status -``` - -## Next Steps - -- [Chat Completions](/docs/python-sdk/chat) - Use chat API -- [Models](/docs/python-sdk/models) - List available models -- [Quickstart](/docs/python-sdk/quickstart) - Get started guide diff --git a/docs/content/docs/index.mdx b/docs/content/docs/index.mdx deleted file mode 100644 index f238e83da..000000000 --- a/docs/content/docs/index.mdx +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: Python SDK -description: The official Python SDK for Hanzo AI - Unified access to 100+ LLM providers through a single OpenAI-compatible API. ---- - -The Hanzo Python SDK provides a unified interface to 100+ LLM providers through a single OpenAI-compatible API. It includes enterprise features like cost tracking, rate limiting, and observability. - -## Features - -- **100+ LLM Providers**: Access OpenAI, Anthropic, Google, Mistral, and more through a single API -- **OpenAI Compatible**: Drop-in replacement for the OpenAI SDK -- **Enterprise Ready**: Built-in cost tracking, rate limiting, and team management -- **Type Safe**: Full type hints and Pydantic models -- **Async Support**: Both sync and async clients available - -## Packages - -The SDK is organized as a monorepo with multiple packages: - -| Package | Description | -|---------|-------------| -| `hanzoai` | Core SDK for Hanzo AI platform | -| `hanzo-mcp` | Model Context Protocol implementation | -| `hanzo-agents` | Agent framework and swarm orchestration | -| `hanzo-memory` | Memory and knowledge base management | -| `hanzo` | CLI and orchestration tools | - -## Quick Example - -```python -from hanzoai import Hanzo - -client = Hanzo(api_key="your-api-key") - -response = client.chat.completions.create( - model="gpt-4", - messages=[ - {"role": "user", "content": "Hello, world!"} - ] -) - -print(response.choices[0].message.content) -``` - -## Next Steps - -- [Installation](/docs/python-sdk/installation) - Get started with the SDK -- [Quickstart](/docs/python-sdk/quickstart) - Build your first application -- [MCP Tools](/docs/mcp) - Learn about Model Context Protocol diff --git a/docs/content/docs/installation.mdx b/docs/content/docs/installation.mdx deleted file mode 100644 index 06e17afb0..000000000 --- a/docs/content/docs/installation.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: Installation -description: How to install the Hanzo Python SDK ---- - -## Requirements - -- Python 3.12 or higher -- pip, uv, or poetry for package management - -## Install the SDK - -### Using pip - -```bash -pip install hanzoai -``` - -### Using uv (Recommended) - -```bash -uv pip install hanzoai -``` - -### Using poetry - -```bash -poetry add hanzoai -``` - -## Optional Packages - -Install additional packages for specific features: - -### MCP Tools - -```bash -pip install hanzo-mcp -``` - -### Agent Framework - -```bash -pip install hanzo-agents -``` - -### Memory Management - -```bash -pip install hanzo-memory -``` - -### All Features - -Install everything: - -```bash -pip install "hanzoai[all]" -``` - -## Verify Installation - -```python -import hanzoai -print(hanzoai.__version__) -``` - -## Environment Setup - -Set your API key as an environment variable: - -```bash -export HANZO_API_KEY="your-api-key" -``` - -Or use a `.env` file: - -```bash title=".env" -HANZO_API_KEY=your-api-key -HANZO_BASE_URL=https://api.hanzo.ai -``` - -## Development Installation - -For contributing to the SDK: - -```bash -git clone https://github.com/hanzoai/python-sdk -cd python-sdk -make setup -``` - -This sets up the development environment with all dependencies. diff --git a/docs/content/docs/mcp/claude-desktop.mdx b/docs/content/docs/mcp/claude-desktop.mdx deleted file mode 100644 index b8139e186..000000000 --- a/docs/content/docs/mcp/claude-desktop.mdx +++ /dev/null @@ -1,277 +0,0 @@ ---- -title: Claude Desktop Integration -description: Set up Hanzo MCP with Claude Desktop ---- - -import { Callout } from 'fumadocs-ui/components/callout' -import { Tab, Tabs } from 'fumadocs-ui/components/tabs' - -# Claude Desktop Integration - -This guide covers the complete setup of Hanzo MCP with Claude Desktop. - -## Prerequisites - -1. **Claude Desktop** installed from [claude.ai/download](https://claude.ai/download) -2. **Python 3.9+** with pip or uv -3. **Hanzo MCP** installed: - ```bash - pip install hanzo-mcp - ``` - -## Automatic Setup - -The easiest way to configure Claude Desktop: - -```bash -hanzo-mcp install-desktop -``` - -This command: -- Locates your Claude Desktop config file -- Adds the Hanzo MCP server configuration -- Sets up default allowed paths - - -Restart Claude Desktop after running this command. - - -## Manual Configuration - -### Configuration File Location - - - - ``` - ~/Library/Application Support/Claude/claude_desktop_config.json - ``` - - - ``` - %APPDATA%\Claude\claude_desktop_config.json - ``` - - - ``` - ~/.config/Claude/claude_desktop_config.json - ``` - - - -### Basic Configuration - -```json -{ - "mcpServers": { - "hanzo": { - "command": "hanzo-mcp", - "args": ["serve"] - } - } -} -``` - -### With Project Directory - -```json -{ - "mcpServers": { - "hanzo": { - "command": "hanzo-mcp", - "args": [ - "serve", - "--project-dir", "/path/to/your/project" - ] - } - } -} -``` - -### With Multiple Allowed Paths - -```json -{ - "mcpServers": { - "hanzo": { - "command": "hanzo-mcp", - "args": [ - "serve", - "--allowed-path", "/path/to/project1", - "--allowed-path", "/path/to/project2" - ] - } - } -} -``` - -### With Environment Variables - -```json -{ - "mcpServers": { - "hanzo": { - "command": "hanzo-mcp", - "args": ["serve"], - "env": { - "HANZO_MCP_ALLOWED_PATHS": "/home/user/projects:/home/user/docs", - "HANZO_API_KEY": "your-api-key", - "HANZO_MCP_LOG_LEVEL": "INFO" - } - } - } -} -``` - -## Using with uv - -If you installed with uv: - -```json -{ - "mcpServers": { - "hanzo": { - "command": "uv", - "args": ["run", "hanzo-mcp", "serve"] - } - } -} -``` - -## Using with pipx - -If you installed with pipx: - -```json -{ - "mcpServers": { - "hanzo": { - "command": "pipx", - "args": ["run", "hanzo-mcp", "serve"] - } - } -} -``` - -## Verification - -After configuration and restarting Claude Desktop: - -1. Open Claude Desktop -2. Start a new conversation -3. Ask Claude: "What MCP tools do you have available?" -4. Claude should list the Hanzo MCP tools - -## Troubleshooting - -### Server Not Starting - -Check the logs: - - - - ```bash - tail -f ~/Library/Logs/Claude/mcp-server-hanzo.log - ``` - - - ```powershell - Get-Content "$env:LOCALAPPDATA\Claude\Logs\mcp-server-hanzo.log" -Wait - ``` - - - -### Command Not Found - -Ensure hanzo-mcp is in your PATH: - -```bash -which hanzo-mcp -``` - -If not found, use the full path: - -```json -{ - "mcpServers": { - "hanzo": { - "command": "/usr/local/bin/hanzo-mcp", - "args": ["serve"] - } - } -} -``` - -Or find it with: - -```bash -python -c "import hanzo_mcp; print(hanzo_mcp.__file__)" -``` - -### Permission Denied - -Ensure allowed paths are accessible: - -```bash -# Check path exists and is readable -ls -la /path/to/project -``` - -### Tools Not Working - -1. Check if the path is in allowed paths -2. Verify file permissions -3. Check Claude Desktop logs for errors - -## Multiple MCP Servers - -You can run Hanzo alongside other MCP servers: - -```json -{ - "mcpServers": { - "hanzo": { - "command": "hanzo-mcp", - "args": ["serve", "--project-dir", "/project"] - }, - "other-server": { - "command": "other-mcp-server", - "args": ["start"] - } - } -} -``` - -## Security Considerations - - -Only add paths you trust to allowed paths. The MCP server can read, write, and execute commands in these directories. - - -### Recommended Practices - -1. **Limit allowed paths** to specific projects -2. **Avoid home directory** as allowed path -3. **Don't include system directories** (/etc, /usr, etc.) -4. **Review tool actions** before confirming in Claude - -### Safe Configuration Example - -```json -{ - "mcpServers": { - "hanzo": { - "command": "hanzo-mcp", - "args": [ - "serve", - "--allowed-path", "/Users/me/projects/my-app", - "--disable-write-tools" - ] - } - } -} -``` - -## Next Steps - -- [Configuration](/docs/mcp/configuration) - Advanced settings -- [Tools Reference](/docs/mcp/tools) - Available tools diff --git a/docs/content/docs/mcp/configuration.mdx b/docs/content/docs/mcp/configuration.mdx deleted file mode 100644 index b6fde41e0..000000000 --- a/docs/content/docs/mcp/configuration.mdx +++ /dev/null @@ -1,286 +0,0 @@ ---- -title: Configuration -description: Configure Hanzo MCP server options ---- - -import { Callout } from 'fumadocs-ui/components/callout' -import { Tab, Tabs } from 'fumadocs-ui/components/tabs' - -# Configuration - -Hanzo MCP supports extensive configuration through CLI arguments, environment variables, and programmatic options. - -## CLI Arguments - -### Basic Options - -```bash -hanzo-mcp serve [OPTIONS] -``` - -| Option | Description | Default | -|--------|-------------|---------| -| `--name` | Server name | `hanzo` | -| `--transport` | Transport type (`stdio`, `sse`) | `stdio` | -| `--host` | Host for SSE server | `127.0.0.1` | -| `--port` | Port for SSE server | `8888` | - -### Path Options - -```bash -# Single project directory -hanzo-mcp serve --project-dir /path/to/project - -# Multiple allowed paths -hanzo-mcp serve \ - --allowed-path /path/one \ - --allowed-path /path/two - -# Project paths for prompts -hanzo-mcp serve --project-paths /path/one,/path/two -``` - -### Tool Control - -```bash -# Disable write tools (read-only mode) -hanzo-mcp serve --disable-write-tools - -# Disable search tools -hanzo-mcp serve --disable-search-tools - -# Disable specific tools -hanzo-mcp serve --disabled-tools shell,bash,zsh -``` - -### Agent Options - -```bash -# Enable agent tool for recursive AI calls -hanzo-mcp serve --enable-agent-tool - -# Configure agent model -hanzo-mcp serve \ - --enable-agent-tool \ - --agent-model gpt-4o \ - --agent-max-tokens 4096 \ - --agent-max-iterations 10 -``` - -### Timeout Configuration - -```bash -# Command timeout (seconds) -hanzo-mcp serve --command-timeout 300 -``` - -## Environment Variables - -| Variable | Description | Default | -|----------|-------------|---------| -| `HANZO_MCP_ALLOWED_PATHS` | Colon-separated allowed paths | None | -| `HANZO_MCP_PROJECT_DIR` | Default project directory | None | -| `HANZO_MCP_TRANSPORT` | Transport type | `stdio` | -| `HANZO_MCP_HOST` | SSE host | `127.0.0.1` | -| `HANZO_MCP_PORT` | SSE port | `8888` | -| `HANZO_MCP_TOKEN` | Authentication token | Auto-generated | -| `HANZO_API_KEY` | Hanzo API key | None | -| `HANZO_QUIET` | Suppress startup messages | None | - -### Example - -```bash -export HANZO_MCP_ALLOWED_PATHS="/home/user/projects:/home/user/docs" -export HANZO_MCP_PROJECT_DIR="/home/user/projects/my-app" -export HANZO_API_KEY="your-api-key" - -hanzo-mcp serve -``` - -## Programmatic Configuration - -Use Hanzo MCP as a library: - -```python -from hanzo_mcp import HanzoMCPServer - -server = HanzoMCPServer( - name="my-server", - allowed_paths=["/path/to/project"], - project_dir="/path/to/project", - project_paths=["/path/to/project"], - - # Tool control - disable_write_tools=False, - disable_search_tools=False, - enabled_tools={"shell": True, "bash": True}, - disabled_tools=["dangerous_tool"], - - # Agent configuration - enable_agent_tool=True, - agent_model="gpt-4o", - agent_max_tokens=4096, - agent_api_key="your-key", - agent_base_url="https://api.hanzo.ai", - agent_max_iterations=10, - agent_max_tool_uses=30, - - # Timeouts - command_timeout=120.0, - - # Network - host="127.0.0.1", - port=8888, -) - -server.run(transport="stdio") -``` - -## Tool Configuration - -### Enabling/Disabling Tools - -```python -server = HanzoMCPServer( - enabled_tools={ - "read": True, - "write": True, - "edit": True, - "shell": False, # Disable shell - }, - disabled_tools=["bash", "zsh"], # Also disable these -) -``` - -### Tool Categories - -| Category | Tools | -|----------|-------| -| File Tools | `read`, `write`, `edit`, `multi_edit`, `tree`, `find` | -| Shell Tools | `shell`, `bash`, `zsh`, `npx`, `uvx`, `process` | -| Search Tools | `search`, `ast` | -| Memory Tools | `recall_memories`, `create_memories`, `store_facts`, etc. | -| Code Tools | `lsp`, `refactor` | -| Thinking Tools | `think`, `critic` | - -## Permission System - -### Default Allowed Paths - -By default, these paths are allowed: -- `/tmp` (Unix) -- `/var` (Unix) -- `C:\Users\...\AppData\Local\Temp` (Windows) -- `~/work` (if exists) - -### Excluded Patterns - -These patterns are always excluded: -- `.ssh`, `.gnupg` (security) -- `node_modules`, `__pycache__`, `.venv` (dependencies) -- `.env`, `*.key`, `*.pem` (secrets) -- `*.sqlite`, `*.db` (databases) - -### Custom Exclusions - -```python -from hanzo_mcp.tools.common.permissions import PermissionManager - -pm = PermissionManager() -pm.add_exclusion_pattern("*.secret") -pm.exclude_path("/path/to/sensitive") -``` - -## Transport Configuration - -### stdio (Default) - -For Claude Desktop and command-line usage: - -```bash -hanzo-mcp serve --transport stdio -``` - -### SSE - -For web clients and custom integrations: - -```bash -hanzo-mcp serve \ - --transport sse \ - --host 0.0.0.0 \ - --port 8888 -``` - -## Logging - -### Log Levels - -```bash -# Via environment variable -export HANZO_MCP_LOG_LEVEL=DEBUG -hanzo-mcp serve - -# Or via Python logging -import logging -logging.getLogger("hanzo_mcp").setLevel(logging.DEBUG) -``` - -### Log Levels - -| Level | Description | -|-------|-------------| -| `DEBUG` | Verbose debugging info | -| `INFO` | General information | -| `WARNING` | Warnings only | -| `ERROR` | Errors only | - -## Security Configuration - -### Read-Only Mode - -```bash -hanzo-mcp serve --disable-write-tools -``` - -### Restricted Shell Access - -```bash -hanzo-mcp serve --disabled-tools shell,bash,zsh,npx,uvx -``` - -### Authentication Token - -```bash -# Set explicit token -export HANZO_MCP_TOKEN="your-secure-token" -hanzo-mcp serve - -# Or generate automatically (logged on startup) -hanzo-mcp serve -# Logs: "Generated token: abc123..." -``` - -## Multiple Server Instances - -Run different configurations for different projects: - -```json -{ - "mcpServers": { - "hanzo-project-a": { - "command": "hanzo-mcp", - "args": ["serve", "--project-dir", "/project-a", "--name", "project-a"] - }, - "hanzo-project-b": { - "command": "hanzo-mcp", - "args": ["serve", "--project-dir", "/project-b", "--name", "project-b"] - } - } -} -``` - -## Next Steps - -- [Tools Reference](/docs/mcp/tools) - All available tools -- [Claude Desktop](/docs/mcp/claude-desktop) - Claude integration diff --git a/docs/content/docs/mcp/index.mdx b/docs/content/docs/mcp/index.mdx deleted file mode 100644 index 1b5cb0762..000000000 --- a/docs/content/docs/mcp/index.mdx +++ /dev/null @@ -1,220 +0,0 @@ ---- -title: Hanzo MCP -description: Model Context Protocol tools for AI assistants - 29 tools across 12 packages ---- - -import { Callout } from 'fumadocs-ui/components/callout' -import { Tab, Tabs } from 'fumadocs-ui/components/tabs' - -# Hanzo MCP - -Hanzo MCP is a comprehensive implementation of the [Model Context Protocol](https://modelcontextprotocol.io/) that provides powerful tools for AI assistants like Claude Desktop, Cursor, and other MCP-compatible clients. - - - **Version 0.10.21** - 29 tools across 12 modular packages - - -## Overview - -MCP (Model Context Protocol) is a standard for providing tools and context to AI models. Hanzo MCP implements this protocol with a carefully curated set of tools organized into modular packages. - -## Tool Categories - -| Package | Tools | Description | -|---------|-------|-------------| -| **filesystem** | `read`, `write`, `edit`, `tree`, `find`, `search`, `ast` | File operations and code search | -| **shell** | `dag`, `ps`, `zsh`, `shell`, `npx`, `uvx`, `open` | Command execution and process management | -| **browser** | `browser` | Full Playwright automation (70+ actions) | -| **memory** | `memory` | Persistent memory across sessions | -| **todo** | `todo` | Task tracking and management | -| **reasoning** | `think`, `critic` | Structured thinking and analysis | -| **lsp** | `lsp` | Language Server Protocol integration | -| **refactor** | `refactor` | Code refactoring operations | -| **llm** | `llm`, `consensus` | Multi-model LLM access | -| **config** | `config`, `mode` | Configuration and modes | -| **agent** | `agent`, `iching`, `review` | AI agent orchestration | -| **computer** | `computer` | Mac computer automation | - -## Key Features - -### DAG Execution Engine - -The `dag` tool replaces traditional shell commands with a powerful DAG (Directed Acyclic Graph) execution engine: - -```python -# Serial execution (default) -dag(["git status", "git diff", "git log -5"]) - -# Parallel execution -dag(["npm install", "cargo build"], parallel=True) - -# Mixed DAG with dependencies -dag([ - "mkdir -p dist", - {"parallel": ["cp a.txt dist/", "cp b.txt dist/"]}, - "zip -r out.zip dist/" -]) -``` - -### Auto-Backgrounding - -Long-running commands automatically continue in the background after 60 seconds: - -```python -dag(["npm run dev"]) # Auto-backgrounds after 60s -ps() # Check background processes -ps(logs="abc123") # View process logs -ps(kill="abc123") # Kill process -``` - -### Multi-Engine Search - -The `search` tool runs multiple search engines in parallel: - -```python -search(pattern="UserService", path="./src") -# Runs: grep, AST, LSP, file, git - all in parallel -``` - -### Full Playwright Browser - -70+ browser actions for complete web automation: - -```python -browser(action="navigate", url="https://example.com") -browser(action="click", selector="button.submit") -browser(action="screenshot", full_page=True) -browser(action="expect_visible", selector=".modal") -``` - -### Unified Memory - -Persistent memory that survives across sessions: - -```python -memory(action="store", content="User prefers dark mode") -memory(action="recall", query="user preferences") -``` - -### 701 Personality Modes - -Switch between 701 programmer personas from `hanzo-persona`: - -```python -mode(action="list") # List all modes -mode(action="activate", name="guido") # Activate Guido van Rossum mode -``` - -## Quick Start - -### Installation - - - - ```bash - pip install hanzo-mcp - ``` - - - ```bash - uv pip install hanzo-mcp - ``` - - - ```bash - pipx install hanzo-mcp - ``` - - - -### Start the Server - -```bash -# Start MCP server (stdio transport) -hanzo-mcp serve - -# With specific allowed paths -hanzo-mcp serve --allowed-path /path/to/project -``` - -### Install to Claude Desktop - -```bash -# Auto-install to Claude Desktop config -hanzo-mcp install-desktop - -# Or manually add to ~/Library/Application Support/Claude/claude_desktop_config.json -``` - -## Architecture - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Claude Desktop / Cursor / MCP Client โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ MCP Protocol (stdio/SSE/HTTP) - โ–ผ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Hanzo MCP Server โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Entry Point Loader โ”‚ โ”‚ -โ”‚ โ”‚ Discovers tools from hanzo-tools-* packages โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ filesystem โ”‚ โ”‚ shell โ”‚ โ”‚ browser โ”‚ โ”‚ -โ”‚ โ”‚ 7 tools โ”‚ โ”‚ 7 tools โ”‚ โ”‚ 70+ actions โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ reasoning โ”‚ โ”‚ memory โ”‚ โ”‚ lsp/refactor โ”‚ โ”‚ -โ”‚ โ”‚ 2 tools โ”‚ โ”‚ 1 tool โ”‚ โ”‚ 2 tools โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ llm โ”‚ โ”‚ config โ”‚ โ”‚ agent โ”‚ โ”‚ -โ”‚ โ”‚ 2 tools โ”‚ โ”‚ 2 tools โ”‚ โ”‚ 3 tools โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## Modular Packages - -Hanzo MCP uses a modular architecture where tools are organized into independent packages: - -```bash -hanzo-mcp # Core server (thin wrapper) -โ”œโ”€โ”€ hanzo-tools-core # Base classes and utilities -โ”œโ”€โ”€ hanzo-tools-filesystem # File operations -โ”œโ”€โ”€ hanzo-tools-shell # Command execution -โ”œโ”€โ”€ hanzo-tools-browser # Playwright automation -โ”œโ”€โ”€ hanzo-tools-memory # Persistent memory -โ”œโ”€โ”€ hanzo-tools-todo # Task management -โ”œโ”€โ”€ hanzo-tools-reasoning # Think and critic -โ”œโ”€โ”€ hanzo-tools-lsp # Language server -โ”œโ”€โ”€ hanzo-tools-refactor # Code refactoring -โ”œโ”€โ”€ hanzo-tools-llm # Multi-model LLM -โ”œโ”€โ”€ hanzo-tools-config # Configuration -โ”œโ”€โ”€ hanzo-tools-agent # Agent orchestration -โ””โ”€โ”€ hanzo-tools-computer # Mac automation -``` - -## Supported Transports - -- **stdio** - Standard input/output (default for Claude Desktop) -- **SSE** - Server-Sent Events for web clients -- **HTTP** - REST API endpoint - -## Essential System Tools - -These tools are always enabled regardless of mode: - -- `llm`, `consensus` - LLM access -- `config`, `mode` - Configuration -- `memory` - Persistent memory -- `version`, `stats` - System info - -## Next Steps - -- [Installation](/docs/mcp/installation) - Install and configure -- [Tools Reference](/docs/mcp/tools) - All 29 tools documented -- [Claude Desktop](/docs/mcp/claude-desktop) - Set up with Claude -- [Configuration](/docs/mcp/configuration) - Advanced settings diff --git a/docs/content/docs/mcp/installation.mdx b/docs/content/docs/mcp/installation.mdx deleted file mode 100644 index 75ce04645..000000000 --- a/docs/content/docs/mcp/installation.mdx +++ /dev/null @@ -1,191 +0,0 @@ ---- -title: Installation -description: Install and set up Hanzo MCP ---- - -import { Callout } from 'fumadocs-ui/components/callout' -import { Tab, Tabs } from 'fumadocs-ui/components/tabs' - -# Installation - -Install Hanzo MCP using your preferred package manager. - -## Quick Install - - - - ```bash - pip install hanzo-mcp - ``` - - - ```bash - uv pip install hanzo-mcp - ``` - - - ```bash - pipx install hanzo-mcp - ``` - - - -## Verify Installation - -```bash -hanzo-mcp --version -``` - -## Start the Server - -### Basic Usage - -```bash -# Start with stdio transport (for Claude Desktop) -hanzo-mcp serve -``` - -### With Allowed Paths - -```bash -# Restrict to specific directories -hanzo-mcp serve --allowed-path /path/to/project - -# Multiple paths -hanzo-mcp serve \ - --allowed-path /home/user/projects \ - --allowed-path /home/user/documents -``` - -### With SSE Transport - -```bash -# Start SSE server for web clients -hanzo-mcp serve --transport sse --port 8888 -``` - -## Claude Desktop Integration - -### Automatic Installation - -```bash -# Auto-configure Claude Desktop -hanzo-mcp install-desktop -``` - -This automatically updates your Claude Desktop configuration file. - -### Manual Configuration - - - - Edit `~/Library/Application Support/Claude/claude_desktop_config.json`: - ```json - { - "mcpServers": { - "hanzo": { - "command": "hanzo-mcp", - "args": ["serve"], - "env": { - "HANZO_MCP_ALLOWED_PATHS": "/Users/you/projects" - } - } - } - } - ``` - - - Edit `%APPDATA%\Claude\claude_desktop_config.json`: - ```json - { - "mcpServers": { - "hanzo": { - "command": "hanzo-mcp", - "args": ["serve"], - "env": { - "HANZO_MCP_ALLOWED_PATHS": "C:\\Users\\you\\projects" - } - } - } - } - ``` - - - Edit `~/.config/Claude/claude_desktop_config.json`: - ```json - { - "mcpServers": { - "hanzo": { - "command": "hanzo-mcp", - "args": ["serve"], - "env": { - "HANZO_MCP_ALLOWED_PATHS": "/home/you/projects" - } - } - } - } - ``` - - - -## Using with Python - -You can also use Hanzo MCP programmatically: - -```python -from hanzo_mcp import HanzoMCPServer - -# Create server -server = HanzoMCPServer( - name="my-mcp-server", - allowed_paths=["/path/to/project"], - project_dir="/path/to/project" -) - -# Run the server -server.run(transport="stdio") -``` - -## Docker Installation - -```dockerfile -FROM python:3.11-slim - -RUN pip install hanzo-mcp - -CMD ["hanzo-mcp", "serve"] -``` - -```bash -docker build -t hanzo-mcp . -docker run -v /your/project:/project hanzo-mcp \ - hanzo-mcp serve --allowed-path /project -``` - -## Development Installation - -For contributing to Hanzo MCP: - -```bash -# Clone the repository -git clone https://github.com/hanzoai/python-sdk -cd python-sdk/pkg/hanzo-mcp - -# Install in development mode -uv sync --all-extras - -# Run tests -uv run pytest -``` - -## Requirements - -- Python 3.9 or higher -- For shell tools: bash or zsh -- For LSP tools: Language servers (optional, auto-installed) - -## Next Steps - -- [Tools Reference](/docs/mcp/tools) - Explore available tools -- [Claude Desktop](/docs/mcp/claude-desktop) - Full Claude setup guide -- [Configuration](/docs/mcp/configuration) - Advanced options diff --git a/docs/content/docs/mcp/meta.json b/docs/content/docs/mcp/meta.json deleted file mode 100644 index 16b3cb0c1..000000000 --- a/docs/content/docs/mcp/meta.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "title": "MCP Tools", - "description": "Model Context Protocol implementation for AI tools", - "pages": [ - "index", - "installation", - "tools", - "claude-desktop", - "configuration" - ] -} diff --git a/docs/content/docs/mcp/tools.mdx b/docs/content/docs/mcp/tools.mdx deleted file mode 100644 index cecdfea11..000000000 --- a/docs/content/docs/mcp/tools.mdx +++ /dev/null @@ -1,345 +0,0 @@ ---- -title: Tools Reference -description: Complete reference for all Hanzo MCP tools ---- - -import { Callout } from 'fumadocs-ui/components/callout' -import { Tab, Tabs } from 'fumadocs-ui/components/tabs' - -# Tools Reference - -Hanzo MCP provides 260+ tools organized into categories. - -## File Operations - -### read - -Read files from the filesystem. - -```python -read(file_path="/path/to/file.py") -read(file_path="/path/to/file.py", offset=100, limit=50) # Lines 100-150 -``` - -### write - -Write or overwrite files. - -```python -write(file_path="/path/to/file.py", content="# New content") -``` - -### edit - -Make precise edits to files. - -```python -edit( - file_path="/path/to/file.py", - old_string="def old_function():", - new_string="def new_function():" -) -``` - -### multi_edit - -Multiple edits to one file atomically. - -```python -multi_edit( - file_path="/path/to/file.py", - edits=[ - {"old_string": "foo", "new_string": "bar"}, - {"old_string": "baz", "new_string": "qux"} - ] -) -``` - -### tree - -View directory structure. - -```python -tree(path="/project", depth=3) -tree(path="/project", include_filtered=True) # Include node_modules, etc. -``` - -### find - -Find files by pattern. - -```python -find(pattern="*.py", path="/project") -find(pattern="test_", type="file", modified_after="1 day ago") -find(pattern="config", min_size="1KB", max_size="1MB") -``` - -## Shell Tools - -### shell - -Smart shell execution (prefers zsh). - -```python -shell(command="ls -la") -shell(command="npm run build", cwd="/project") -``` - -### bash - -Execute with bash explicitly. - -```python -bash(command="echo $BASH_VERSION") -bash(command="./script.sh", timeout=300) -``` - -### zsh - -Execute with zsh explicitly. - -```python -zsh(command="echo $ZSH_VERSION") -zsh(command="source ~/.zshrc && mycmd") -``` - -### npx - -Run Node packages. - -```python -npx(package="create-react-app", args="my-app") -npx(package="prettier", args="--write .") -``` - -### uvx - -Run Python packages. - -```python -uvx(package="ruff", args="check .") -uvx(package="black", args="--check src/") -``` - -### process - -Manage background processes. - -```python -process() # List all -process(action="logs", id="bash_abc123") -process(action="kill", id="npx_def456") -``` - -## Search Tools - -### search - -Unified intelligent search. - -```python -search(pattern="error handling", path="/project") -search( - pattern="UserService", - enable_ast=True, # Code structure - enable_symbol=True, # Definitions - enable_text=True # Text matches -) -``` - -### ast - -AST-based code search. - -```python -ast(pattern="class.*Service", path="/project/src") -ast(pattern="def test_", path="/project/tests", line_number=True) -``` - -### grep (via search) - -Text pattern search. - -```python -search(pattern="TODO|FIXME", path="/project", enable_ast=False) -``` - -## Memory Tools - -### recall_memories - -Recall stored memories. - -```python -recall_memories(queries=["user preferences", "previous decisions"]) -recall_memories(queries=["project config"], scope="project") -``` - -### create_memories - -Store new memories. - -```python -create_memories(statements=[ - "User prefers TypeScript over JavaScript", - "Project uses PostgreSQL database" -]) -``` - -### recall_facts - -Query knowledge bases. - -```python -recall_facts(queries=["API authentication"], kb_name="api_docs") -``` - -### store_facts - -Store structured facts. - -```python -store_facts( - facts=["API uses JWT tokens", "Rate limit is 100/hour"], - kb_name="api_docs" -) -``` - -## Code Intelligence - -### lsp - -Language Server Protocol operations. - -```python -lsp(action="definition", file="/path/file.py", line=10, character=5) -lsp(action="references", file="/path/file.py", line=10, character=5) -lsp(action="hover", file="/path/file.py", line=10, character=5) -lsp(action="diagnostics", file="/path/file.py") -``` - -Supported languages: Go, Python, TypeScript, JavaScript, Rust, Java, C/C++, Ruby, Lua - -### refactor - -Code refactoring operations. - -```python -# Rename symbol across codebase -refactor( - action="rename", - file="/path/file.py", - line=10, - column=5, - new_name="newFunctionName" -) - -# Find all references -refactor( - action="find_references", - file="/path/file.py", - line=10, - column=5 -) -``` - -## Thinking Tools - -### think - -Structured reasoning. - -```python -think(thought=""" -Analyzing the authentication flow: -1. User submits credentials -2. Server validates against database -3. JWT token is generated -4. Token returned to client -5. Client stores in localStorage - -Potential issues: -- No rate limiting on login attempts -- Token doesn't expire -""") -``` - -### critic - -Critical analysis and code review. - -```python -critic(analysis=""" -Code Review: -- No error handling for network failures -- Missing input validation -- SQL injection vulnerability in query construction -- No unit tests for edge cases - -Recommendations: -1. Add try/catch blocks -2. Validate user input -3. Use parameterized queries -4. Add comprehensive tests -""") -``` - -## Todo Management - -### todo - -Manage task lists. - -```python -todo() # List all -todo(action="add", content="Fix authentication bug") -todo(action="update", id="abc123", status="completed") -todo(action="remove", id="abc123") -``` - -## Web Tools - -### open - -Open files or URLs. - -```python -open(path="https://example.com") -open(path="/path/to/document.pdf") -``` - -## Batch Operations - -### batch - -Execute multiple tools in parallel. - -```python -batch( - description="Read multiple files", - invocations=[ - {"tool_name": "read", "input": {"file_path": "/file1.py"}}, - {"tool_name": "read", "input": {"file_path": "/file2.py"}}, - {"tool_name": "tree", "input": {"path": "/src", "depth": 2}} - ] -) -``` - -## Tool Comparison - -| Need | Tool | Why | -|------|------|-----| -| Read a file | `read` | Direct file access | -| Edit specific text | `edit` | Precise replacements | -| Multiple edits | `multi_edit` | Atomic batch edits | -| Find files | `find` | Pattern matching, filters | -| Search code | `search` | Multi-modal search | -| Code structure | `ast` | AST-aware search | -| Run command | `shell` | Smart shell selection | -| Background tasks | `process` | Manage long-running | -| Store info | `create_memories` | Persistent memory | - -## Next Steps - -- [Claude Desktop](/docs/mcp/claude-desktop) - Set up with Claude -- [Configuration](/docs/mcp/configuration) - Customize tools diff --git a/docs/content/docs/meta.json b/docs/content/docs/meta.json deleted file mode 100644 index db566dbf1..000000000 --- a/docs/content/docs/meta.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "title": "Hanzo Python SDK", - "description": "The official Python SDK for Hanzo AI", - "pages": [ - "index", - "installation", - "quickstart", - "---SDK---", - "client", - "chat", - "embeddings", - "models", - "files", - "---Agent SDK---", - "...agents", - "---MCP---", - "...mcp", - "---Tools---", - "...tools" - ] -} diff --git a/docs/content/docs/models.mdx b/docs/content/docs/models.mdx deleted file mode 100644 index aff98dbc4..000000000 --- a/docs/content/docs/models.mdx +++ /dev/null @@ -1,185 +0,0 @@ ---- -title: Models -description: List and manage available models ---- - -import { Callout } from 'fumadocs-ui/components/callout' -import { Tab, Tabs } from 'fumadocs-ui/components/tabs' - -# Models - -The Models API allows you to list and retrieve information about available models. - -## List Models - -```python -from hanzoai import Hanzo - -client = Hanzo() - -models = client.models.list() - -for model in models.data: - print(f"{model.id}: {model.owned_by}") -``` - -## Retrieve a Model - -```python -model = client.model.retrieve("gpt-4o") - -print(f"ID: {model.id}") -print(f"Owner: {model.owned_by}") -print(f"Created: {model.created}") -``` - -## Available Providers - -Hanzo provides unified access to 100+ LLM providers: - -### OpenAI - -```python -# GPT-4 Family -client.chat.completions.create(model="gpt-4o", ...) -client.chat.completions.create(model="gpt-4o-mini", ...) -client.chat.completions.create(model="gpt-4-turbo", ...) - -# GPT-3.5 -client.chat.completions.create(model="gpt-3.5-turbo", ...) -``` - -### Anthropic Claude - -```python -# Claude 3.5 -client.chat.completions.create(model="claude-3-5-sonnet-20241022", ...) - -# Claude 3 -client.chat.completions.create(model="claude-3-opus-20240229", ...) -client.chat.completions.create(model="claude-3-sonnet-20240229", ...) -client.chat.completions.create(model="claude-3-haiku-20240307", ...) -``` - -### Google - -```python -# Gemini -client.chat.completions.create(model="gemini/gemini-1.5-pro", ...) -client.chat.completions.create(model="gemini/gemini-1.5-flash", ...) -client.chat.completions.create(model="gemini/gemini-2.0-flash-exp", ...) -``` - -### Meta Llama - -```python -# Via Together AI -client.chat.completions.create( - model="together_ai/meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", - ... -) -client.chat.completions.create( - model="together_ai/meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", - ... -) -``` - -### Mistral - -```python -client.chat.completions.create(model="mistral/mistral-large-latest", ...) -client.chat.completions.create(model="mistral/mistral-medium-latest", ...) -client.chat.completions.create(model="mistral/mistral-small-latest", ...) -``` - -### Cohere - -```python -client.chat.completions.create(model="cohere/command-r-plus", ...) -client.chat.completions.create(model="cohere/command-r", ...) -``` - -## Model Selection by Task - - -Choose models based on your specific needs: - - -| Task | Recommended Models | -|------|-------------------| -| Complex reasoning | `gpt-4o`, `claude-3-opus`, `gemini-1.5-pro` | -| General chat | `gpt-4o-mini`, `claude-3-5-sonnet`, `gemini-1.5-flash` | -| Code generation | `gpt-4o`, `claude-3-5-sonnet` | -| Fast responses | `gpt-3.5-turbo`, `claude-3-haiku`, `gemini-flash` | -| Long context | `claude-3-opus` (200k), `gemini-1.5-pro` (2M) | -| Cost efficient | `gpt-4o-mini`, `claude-3-haiku` | - -## Model Info Structure - -```python -model = client.model.retrieve("gpt-4o") - -print(model.id) # Model identifier -print(model.object) # "model" -print(model.created) # Creation timestamp -print(model.owned_by) # Owner/provider -``` - -## Async Usage - -```python -import asyncio -from hanzoai import AsyncHanzo - -async def main(): - client = AsyncHanzo() - - # List models - models = await client.models.list() - print(f"Found {len(models.data)} models") - - # Retrieve specific model - model = await client.model.retrieve("gpt-4o") - print(f"Model: {model.id}") - -asyncio.run(main()) -``` - -## Filter by Provider - -```python -from hanzoai import Hanzo - -client = Hanzo() - -# Get all models -models = client.models.list() - -# Filter by provider -openai_models = [m for m in models.data if m.owned_by == "openai"] -anthropic_models = [m for m in models.data if m.owned_by == "anthropic"] - -print(f"OpenAI models: {len(openai_models)}") -print(f"Anthropic models: {len(anthropic_models)}") -``` - -## Model Capabilities - -Different models have different capabilities: - -```python -# Vision-capable models -vision_models = ["gpt-4o", "gpt-4-turbo", "claude-3-opus", "gemini-1.5-pro"] - -# Tool/Function calling -tool_models = ["gpt-4o", "gpt-4-turbo", "claude-3-5-sonnet", "gemini-1.5-pro"] - -# JSON mode -json_models = ["gpt-4o", "gpt-4-turbo", "gpt-3.5-turbo"] -``` - -## Next Steps - -- [Files](/docs/python-sdk/files) - Upload and manage files -- [Chat](/docs/python-sdk/chat) - Use models for chat completions -- [Embeddings](/docs/python-sdk/embeddings) - Generate text embeddings diff --git a/docs/content/docs/quickstart.mdx b/docs/content/docs/quickstart.mdx deleted file mode 100644 index 62aedd140..000000000 --- a/docs/content/docs/quickstart.mdx +++ /dev/null @@ -1,155 +0,0 @@ ---- -title: Quickstart -description: Get up and running with the Hanzo Python SDK in minutes ---- - -import { Callout } from 'fumadocs-ui/components/callout' -import { Tab, Tabs } from 'fumadocs-ui/components/tabs' - -# Quickstart - -This guide will help you make your first API call with the Hanzo Python SDK. - -## Prerequisites - -- Python 3.9 or higher -- A Hanzo API key (get one at [hanzo.ai](https://hanzo.ai)) - -## Installation - -```bash -pip install hanzoai -``` - -## Set Your API Key - - - - ```bash - export HANZO_API_KEY="your-api-key" - ``` - - - ```python - from hanzoai import Hanzo - - client = Hanzo(api_key="your-api-key") - ``` - - - -## Your First API Call - -```python -from hanzoai import Hanzo - -client = Hanzo() - -response = client.chat.completions.create( - model="gpt-4o", - messages=[ - {"role": "user", "content": "Hello, how are you?"} - ] -) - -print(response.choices[0].message.content) -``` - -## Using Different Models - -Hanzo provides access to 100+ LLM providers through a unified API: - -```python -# OpenAI GPT-4 -response = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Hello!"}] -) - -# Anthropic Claude -response = client.chat.completions.create( - model="claude-3-5-sonnet-20241022", - messages=[{"role": "user", "content": "Hello!"}] -) - -# Google Gemini -response = client.chat.completions.create( - model="gemini/gemini-1.5-pro", - messages=[{"role": "user", "content": "Hello!"}] -) - -# Open source models -response = client.chat.completions.create( - model="together_ai/meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", - messages=[{"role": "user", "content": "Hello!"}] -) -``` - -## Async Usage - -For async applications, use the `AsyncHanzo` client: - -```python -import asyncio -from hanzoai import AsyncHanzo - -async def main(): - client = AsyncHanzo() - - response = await client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Hello!"}] - ) - - print(response.choices[0].message.content) - -asyncio.run(main()) -``` - -## Streaming Responses - -Stream responses for real-time output: - -```python -from hanzoai import Hanzo - -client = Hanzo() - -stream = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Write a short poem"}], - stream=True -) - -for chunk in stream: - if chunk.choices[0].delta.content: - print(chunk.choices[0].delta.content, end="") -``` - -## Error Handling - -Handle errors gracefully: - -```python -from hanzoai import Hanzo -from hanzoai._exceptions import APIError, RateLimitError - -client = Hanzo() - -try: - response = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": "Hello!"}] - ) -except RateLimitError: - print("Rate limited, please wait and retry") -except APIError as e: - print(f"API error: {e.message}") -``` - -## Next Steps - -- [Client Configuration](/docs/python-sdk/client) - Learn about client options -- [Chat Completions](/docs/python-sdk/chat) - Deep dive into chat API -- [Embeddings](/docs/python-sdk/embeddings) - Generate text embeddings -- [Models](/docs/python-sdk/models) - List and manage models diff --git a/docs/content/docs/tools/agent.mdx b/docs/content/docs/tools/agent.mdx deleted file mode 100644 index b9434e38a..000000000 --- a/docs/content/docs/tools/agent.mdx +++ /dev/null @@ -1,332 +0,0 @@ ---- -title: Agent Tool -description: Spawn and manage CLI AI agents with YOLO mode ---- - -The `hanzo-tools-agent` package provides tools for running various CLI AI agents. - -## Installation - -```bash -pip install hanzo-tools-agent - -# With API mode support -pip install hanzo-tools-agent[api] - -# With high-performance async -pip install hanzo-tools-agent[perf] - -# All features -pip install hanzo-tools-agent[full] -``` - -## Agent Tool - -The unified `agent` tool runs multiple CLI agents with auto-detection and YOLO mode. - -### Basic Usage - -```python -# Run with default agent (claude when in Claude Code) -agent(action="run", prompt="Explain this code") - -# Run specific agent -agent(action="run", name="gemini", prompt="Review this PR") - -# Run with system prompt (claude only) -agent(action="run", name="claude", prompt="Fix this bug", system_prompt="Be concise") - -# List available agents -agent(action="list") - -# Check agent status -agent(action="status") - -# Show configuration -agent(action="config") -``` - -### Available Agents - -| Agent | Command | Auth | YOLO Flags | -|-------|---------|------|------------| -| `claude` | `claude` | OAuth | `--dangerously-skip-permissions --print` | -| `codex` | `codex` | OAuth | `--full-auto` | -| `gemini` | `gemini` | `GOOGLE_API_KEY` | `-y -p ` | -| `grok` | `grok` | `XAI_API_KEY` | `-y` | -| `qwen` | `qwen` | `DASHSCOPE_API_KEY` | `--approval-mode yolo` | -| `vibe` | `vibe` | - | `--auto-approve -p ` | -| `code` | `hanzo-code` | - | - | -| `dev` | `hanzo-dev` | - | `-y` | - -### YOLO Mode - -YOLO mode flags are automatically applied for supported agents: - -- **Claude**: `--dangerously-skip-permissions -p` (skip all permission prompts, print output) -- **Codex**: `--full-auto` (fully automatic mode) - -This enables agents to run without user interaction. - -### OAuth Authentication - -Claude and Codex use browser-based OAuth authentication: - -- No API keys required -- Login once via browser -- Credentials stored securely - -### System Prompts - -For Claude, you can inject system prompts: - -```python -agent( - action="run", - name="claude", - prompt="Refactor this function", - system_prompt="Follow the project's code style. Be concise." -) -``` - -This uses the `--append-system-prompt` flag. - -### MCP Config Sharing - -When spawning agents, configuration is shared: - -```python -# These environment variables are passed to child agents: -HANZO_MCP_MODE # Current mode -HANZO_MCP_ALLOWED_PATHS # Allowed paths -HANZO_MCP_ENABLED_TOOLS # Enabled tools -HANZO_MCP_PERSONA # Active persona -HANZO_AGENT_PARENT=true # Indicates spawned agent -HANZO_AGENT_NAME= # Agent name -``` - -### Working Directory - -Specify the working directory for the agent: - -```python -agent( - action="run", - name="claude", - prompt="Run the tests", - cwd="/path/to/project" -) -``` - -### Timeout - -Set execution timeout (default 300 seconds): - -```python -agent( - action="run", - prompt="Long running task", - timeout=600 # 10 minutes -) -``` - -## Telemetry Capture - -For Claude agents, OpenTelemetry data is automatically captured and returned: - -```python -from hanzo_tools.agent import AgentTool, Result, Telemetry - -tool = AgentTool() -result = await tool._exec("claude", "Hello", None, 30) - -# Clean output (telemetry filtered out) -print(result.output) # "Hello!" - -# Captured telemetry data -if result.telemetry: - t = result.telemetry - print(f"Input tokens: {t.input_tokens}") - print(f"Output tokens: {t.output_tokens}") - print(f"Cache read: {t.cache_read_input_tokens}") - print(f"Cost: ${t.cost_usd:.4f}") - print(f"Latency: {t.duration_ms}ms") - print(f"Model: {t.model}") - - # Full raw data for dataset annotation - print(t.raw) # Dict with all OTEL fields -``` - -### Telemetry Fields - -| Field | Type | Description | -|-------|------|-------------| -| `input_tokens` | int | Input tokens used | -| `output_tokens` | int | Output tokens generated | -| `cache_read_input_tokens` | int | Tokens read from cache | -| `cache_creation_input_tokens` | int | Tokens used to create cache | -| `cost_usd` | float | Total cost in USD | -| `duration_ms` | int | Request duration | -| `model` | str | Model used | -| `service_name` | str | Service name (claude-code) | -| `service_version` | str | CLI version | -| `raw` | dict | Full OTEL data for datasets | - -The telemetry is valuable for: -- Cost tracking and optimization -- Performance monitoring -- Dataset annotation and training data collection -- Usage analytics - -## I Ching Tool - -Get wisdom from the I Ching for decision making: - -```python -iching(challenge="How should I approach this refactoring?") -``` - -## Review Tool - -Request a code review: - -```python -review( - focus="FUNCTIONALITY", - work_description="Implemented auto-import feature", - file_paths=["/path/to/file.py"] -) -``` - -Focus options: -- `FUNCTIONALITY` - Functional correctness -- `SECURITY` - Security issues -- `PERFORMANCE` - Performance concerns -- `STYLE` - Code style and conventions - -## Direct API Mode - -Configure agents for direct API calls without CLI: - -```json -// ~/.hanzo/agents/custom.json -{ - "endpoint": "https://api.openai.com/v1/chat/completions", - "api_type": "openai", - "model": "gpt-4", - "env_key": "OPENAI_API_KEY", - "system_prompt": "You are a helpful assistant" -} -``` - -Requires `pip install hanzo-tools-agent[api]`. - -## Auto-backgrounding - -Long-running agents automatically background after timeout: - -```python -# Start long task -agent(action="run", prompt="Complex analysis", timeout=60) - -# If times out, process runs in background -# Check status with ps tool -ps() # List all processes -ps(logs="agent_xxx") # View output -ps(kill="agent_xxx") # Stop process -``` - -## Consensus Mode - -Multi-agent consensus using the Metastable protocol: - -```python -agent( - action="consensus", - prompt="What's the best approach for handling auth?", - agents=["claude", "gemini", "codex"], - rounds=3, - k=3, - alpha=0.6, - beta_1=0.5, - beta_2=0.8, -) -``` - -### How Consensus Works - -1. **Phase I (Sampling)**: Each agent samples k peers and builds confidence -2. **Phase II (Finality)**: Threshold aggregation determines winner -3. **Synthesis**: Winning agent provides final synthesis - -### Agent-to-Agent Communication - -During consensus, agents can communicate via MCP: - -```python -# Agents automatically get hanzo-mcp configured -# System prompt enables these tools: -agent(action="run", name="gemini", prompt="What's your view?") -think(thought="Analyzing gemini's response...") -critic(analysis="Claude proposed X, but consider Y...") -``` - -Each spawned Claude agent receives: -- `--mcp-config` pointing to hanzo-mcp -- System prompt explaining MCP tools -- List of other consensus participants - -### Consensus Parameters - -| Parameter | Default | Description | -|-----------|---------|-------------| -| `rounds` | 3 | Number of consensus rounds | -| `k` | 3 | Peers sampled per round | -| `alpha` | 0.6 | Confidence increment on agreement | -| `beta_1` | 0.5 | Phase I to Phase II threshold | -| `beta_2` | 0.8 | Finality threshold | - -Reference: [Metastable Consensus Protocol](https://github.com/luxfi/consensus) - -## Swarm Mode - -Distribute work across parallel agents: - -```python -agent( - action="swarm", - items=["file1.py", "file2.py", "file3.py"], - template="Review {item} for security issues", - max_concurrent=10, -) -``` - -## DAG Mode - -Execute agents with dependencies: - -```python -agent( - action="dag", - tasks=[ - {"id": "analyze", "prompt": "Analyze the codebase"}, - {"id": "plan", "prompt": "Create implementation plan", "after": ["analyze"]}, - {"id": "implement", "prompt": "Implement the plan", "after": ["plan"]}, - ], -) -``` - -## Dispatch Mode - -Different agents for different tasks: - -```python -agent( - action="dispatch", - tasks=[ - {"agent": "claude", "prompt": "Review the architecture"}, - {"agent": "gemini", "prompt": "Check for performance issues"}, - {"agent": "codex", "prompt": "Suggest optimizations"}, - ], -) -``` diff --git a/docs/content/docs/tools/browser.mdx b/docs/content/docs/tools/browser.mdx deleted file mode 100644 index f697a8187..000000000 --- a/docs/content/docs/tools/browser.mdx +++ /dev/null @@ -1,207 +0,0 @@ ---- -title: Browser Tools -description: Playwright-based browser automation with 70+ actions ---- - -The `hanzo-tools-browser` package provides comprehensive browser automation using Playwright. - -## Installation - -```bash -pip install hanzo-tools-browser -playwright install chromium -``` - -## Basic Usage - -### Navigation - -```python -browser(action="navigate", url="https://example.com") -browser(action="go_back") -browser(action="go_forward") -browser(action="reload") -``` - -### Page Content - -```python -# Get page URL and title -browser(action="url") -browser(action="title") - -# Get page content -browser(action="content") # Full HTML -browser(action="get_text", selector="h1") -browser(action="get_html", selector=".container") -``` - -### Screenshots - -```python -browser(action="screenshot") -browser(action="screenshot", selector=".modal", full_page=False) -``` - -## Element Interaction - -### Clicking - -```python -browser(action="click", selector="button.submit") -browser(action="click", selector="a.nav-link", button="right") -browser(action="dblclick", selector=".item") -``` - -### Typing - -```python -browser(action="fill", selector="input[name='email']", text="user@example.com") -browser(action="type", selector="textarea", text="Hello world", delay=50) -browser(action="clear", selector="input") -browser(action="press", selector="input", key="Enter") -``` - -### Forms - -```python -browser(action="select_option", selector="select#country", value="US") -browser(action="check", selector="input[type='checkbox']") -browser(action="uncheck", selector="input[type='checkbox']") -browser(action="upload", selector="input[type='file']", files=["/path/to/file.pdf"]) -``` - -## Mouse Actions - -```python -browser(action="hover", selector=".dropdown") -browser(action="drag", source=".draggable", target=".dropzone") -browser(action="mouse_move", x=100, y=200) -browser(action="mouse_wheel", delta_y=-500) # Scroll -browser(action="scroll", selector=".container", delta_y=300) -``` - -## Touch / Mobile - -```python -browser(action="tap", selector=".button") -browser(action="swipe", selector=".carousel", direction="left") -browser(action="pinch", selector=".map", scale=0.5) # Zoom out -``` - -## Device Emulation - -```python -# User-friendly presets -browser(action="new_context", device="mobile") # iPhone-like -browser(action="new_context", device="tablet") # iPad-like -browser(action="new_context", device="laptop") # MacBook-like - -# Specific devices -browser(action="new_context", device="iphone_14") -browser(action="new_context", device="pixel_7") -browser(action="new_context", device="ipad_pro") -``` - -## Assertions - -```python -# Page assertions -browser(action="expect_url", expected="*/dashboard*") -browser(action="expect_title", expected="Dashboard") - -# Element assertions -browser(action="expect_visible", selector=".modal") -browser(action="expect_hidden", selector=".loading") -browser(action="expect_text", selector="h1", expected="Welcome") -browser(action="expect_count", selector=".items", index=5) - -# Negative assertions -browser(action="expect_visible", selector=".loading", not_=True) -``` - -## Locator Composition - -```python -# Get specific elements -browser(action="first", selector=".item") -browser(action="last", selector=".item") -browser(action="nth", selector=".item", index=2) - -# Filter and find -browser(action="filter", selector=".card", has_text="Premium") -browser(action="all", selector=".list-item") -browser(action="count", selector=".results") -``` - -## Waits - -```python -browser(action="wait_for_selector", selector=".loaded") -browser(action="wait_for_url", url="*/success*") -browser(action="wait_for_load_state", state="networkidle") -browser(action="wait_for_event", event="download") -``` - -## Storage & State - -```python -# Cookies -browser(action="cookies") -browser(action="clear_cookies") - -# Storage -browser(action="storage") # LocalStorage + SessionStorage -browser(action="storage_state") # Full browser state for persistence -``` - -## Tabs & Windows - -```python -browser(action="new_tab", url="https://example.com") -browser(action="close_tab") -``` - -## Headless Control - -```python -browser(action="set_headless", headless=False) # Show browser -browser(action="set_headless", headless=True) # Hide browser -``` - -## Parallel Contexts - -For multi-agent workflows, each agent can have isolated sessions: - -```python -# Agent 1 -browser(action="new_context", context_id="agent1") -browser(action="navigate", url="https://app.com/login", context_id="agent1") - -# Agent 2 (separate cookies/storage) -browser(action="new_context", context_id="agent2") -browser(action="navigate", url="https://app.com/login", context_id="agent2") -``` - -## Examples - -### Login Flow - -```python -browser(action="navigate", url="https://app.com/login") -browser(action="fill", selector="input[name='email']", text="user@example.com") -browser(action="fill", selector="input[name='password']", text="secret") -browser(action="click", selector="button[type='submit']") -browser(action="wait_for_url", url="*/dashboard*") -browser(action="expect_text", selector="h1", expected="Dashboard") -``` - -### Mobile Testing - -```python -browser(action="new_context", device="mobile") -browser(action="navigate", url="https://app.com") -browser(action="tap", selector=".hamburger-menu") -browser(action="expect_visible", selector=".mobile-nav") -browser(action="swipe", selector=".carousel", direction="left") -``` diff --git a/docs/content/docs/tools/consensus.mdx b/docs/content/docs/tools/consensus.mdx deleted file mode 100644 index b6d7132e2..000000000 --- a/docs/content/docs/tools/consensus.mdx +++ /dev/null @@ -1,152 +0,0 @@ ---- -title: Consensus -description: Multi-model consensus using Metastable protocol ---- - -The `hanzo-consensus` package implements a consensus protocol for multi-model decision making. - -## Installation - -```bash -pip install hanzo-consensus -``` - -Or with hanzo-tools-llm: - -```bash -pip install hanzo-tools-llm -``` - -## Overview - -The Metastable consensus protocol enables multiple AI models to reach agreement through iterative sampling and confidence accumulation. - -## Basic Usage - -```python -from hanzo_consensus import run, Result - -async def execute(participant_id: str, prompt: str) -> Result: - # Call your LLM here - response = await call_llm(participant_id, prompt) - return Result( - participant=participant_id, - response=response, - confidence=0.8 - ) - -state = await run( - prompt="What's the best approach for handling authentication?", - participants=["gpt-4", "claude-3-5-sonnet", "gemini-pro"], - execute=execute, - rounds=3, - k=3, - alpha=0.6, - beta_1=0.5, - beta_2=0.8, -) - -print(f"Winner: {state.winner}") -print(f"Finalized: {state.finalized}") -print(f"Synthesis: {state.synthesis}") -``` - -## Protocol Phases - -### Phase I: Sampling - -1. Each participant samples k peers -2. Responses are compared for agreement -3. Confidence accumulates based on agreement -4. ฮฒโ‚ threshold triggers Phase II - -### Phase II: Finality - -1. Threshold aggregation of responses -2. ฮฒโ‚‚ finality threshold checked -3. Winner determined by highest confidence -4. Synthesis generated from winning response - -## Parameters - -| Parameter | Default | Description | -|-----------|---------|-------------| -| `rounds` | 3 | Number of consensus rounds | -| `k` | 3 | Peers sampled per round | -| `alpha` | 0.6 | Confidence increment on agreement | -| `beta_1` | 0.5 | Phase I to Phase II threshold | -| `beta_2` | 0.8 | Finality threshold | - -## State Object - -```python -@dataclass -class State: - winner: str | None # Winning participant ID - finalized: bool # Whether consensus reached - synthesis: str | None # Synthesized response - round: int # Current round number - participants: dict # Participant states -``` - -## MCP Tool Usage - -With hanzo-tools-llm: - -```python -consensus( - prompt="Should we use microservices or monolith?", - models=["gpt-4", "claude-3-5-sonnet"], - rounds=3 -) -``` - -## Advanced Usage - -### Custom Consensus Class - -```python -from hanzo_consensus import Consensus - -consensus = Consensus( - participants=["model-a", "model-b", "model-c"], - k=2, - alpha=0.7, - beta_1=0.4, - beta_2=0.9, -) - -for round in range(5): - results = await gather_responses(consensus.participants) - consensus.update(results) - - if consensus.state.finalized: - break - -print(consensus.state.synthesis) -``` - -### Weighted Participants - -```python -state = await run( - prompt="Technical decision", - participants=[ - {"id": "expert", "weight": 2.0}, - {"id": "gpt-4", "weight": 1.0}, - {"id": "claude", "weight": 1.0}, - ], - execute=execute, -) -``` - -## Use Cases - -1. **Technical Decisions** - Get consensus on architecture choices -2. **Code Review** - Multiple models review code -3. **Content Generation** - Best response from multiple attempts -4. **Fact Verification** - Cross-check information across models - -## Reference - -Based on the Metastable consensus protocol: https://github.com/luxfi/consensus diff --git a/docs/content/docs/tools/filesystem.mdx b/docs/content/docs/tools/filesystem.mdx deleted file mode 100644 index ef36d81a4..000000000 --- a/docs/content/docs/tools/filesystem.mdx +++ /dev/null @@ -1,180 +0,0 @@ ---- -title: Filesystem Tools -description: File operations - read, write, edit, search, and navigate ---- - -The `hanzo-tools-fs` package provides comprehensive filesystem operations. - -## Installation - -```bash -pip install hanzo-tools-fs -``` - -## read - -Read files with line numbers. - -```python -# Read entire file -read(file_path="/path/to/file.py") - -# Read specific lines -read(file_path="/path/to/file.py", offset=100, limit=50) # Lines 100-150 -``` - -## write - -Write or overwrite files. - -```python -write(file_path="/path/to/file.py", content="# New content\n") -``` - -## edit - -Make precise text replacements. - -```python -edit( - file_path="/path/to/file.py", - old_string="def old_function():", - new_string="def new_function():" -) -``` - -### Replace All Occurrences - -```python -edit( - file_path="/path/to/file.py", - old_string="foo", - new_string="bar", - replace_all=True -) -``` - -## multi_edit - -Multiple edits to one file atomically. - -```python -multi_edit( - file_path="/path/to/file.py", - edits=[ - {"old_string": "foo", "new_string": "bar"}, - {"old_string": "baz", "new_string": "qux"} - ] -) -``` - -## tree - -View directory structure. - -```python -# Basic tree -tree(path="/project", depth=3) - -# Include filtered directories (node_modules, .git) -tree(path="/project", include_filtered=True) -``` - -## find - -Find files by pattern. - -```python -# Find by extension -find(pattern="*.py", path="/project") - -# Find by name prefix -find(pattern="test_*", type="file") - -# Find by modification time -find(pattern="*", modified_after="1 day ago") - -# Find by size -find(pattern="*", min_size="1KB", max_size="1MB") -``` - -## search - -Unified multi-modal search. - -```python -# Text search -search(pattern="error handling", path="/project") - -# With AST analysis -search( - pattern="UserService", - enable_ast=True, - enable_symbol=True, - enable_text=True -) -``` - -## ast - -AST-based code structure search. - -```python -# Find class definitions -ast(pattern="class.*Service", path="/project/src") - -# Find test functions with line numbers -ast(pattern="def test_", path="/project/tests", line_number=True) -``` - -## Best Practices - -1. **Always read before edit** - Understand the file content first -2. **Use edit for precision** - More reliable than write for modifications -3. **Use multi_edit for related changes** - Atomic and efficient -4. **Use search over find for code** - AST-aware searching is more accurate - -## Examples - -### Safe File Modification - -```python -# 1. Read current content -content = read(file_path="/config.py") - -# 2. Make precise edit -edit( - file_path="/config.py", - old_string='DEBUG = False', - new_string='DEBUG = True' -) -``` - -### Find and Replace Across Codebase - -```python -# 1. Find all occurrences -results = search(pattern="old_api_call", path="/src") - -# 2. Edit each file -for file in results.files: - edit( - file_path=file, - old_string="old_api_call", - new_string="new_api_call", - replace_all=True - ) -``` - -### Project Structure Analysis - -```python -# Get directory tree -tree(path="/project", depth=2) - -# Find all Python files -find(pattern="*.py", path="/project", type="file") - -# Search for specific patterns -search(pattern="TODO|FIXME|HACK", path="/project") -``` diff --git a/docs/content/docs/tools/index.mdx b/docs/content/docs/tools/index.mdx deleted file mode 100644 index 8fef0d85e..000000000 --- a/docs/content/docs/tools/index.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: Hanzo Tools -description: Modular tool packages for AI agents and MCP servers ---- - -Hanzo Tools is a collection of modular Python packages that provide tools for AI agents. Each package can be installed independently and registered with MCP servers. - -## Installation - -Install all tools: - -```bash -pip install hanzo-tools[all] -``` - -Or install specific packages: - -```bash -pip install hanzo-tools-shell # Shell execution (dag, ps, zsh) -pip install hanzo-tools-fs # Filesystem operations -pip install hanzo-tools-browser # Playwright browser automation -pip install hanzo-tools-memory # Memory and knowledge management -pip install hanzo-tools-reasoning # Think and critic tools -pip install hanzo-tools-lsp # Language Server Protocol -pip install hanzo-tools-database # SQL and graph databases -``` - -## Package Overview - -| Package | Tools | Description | -|---------|-------|-------------| -| `hanzo-tools-core` | BaseTool, Registry | Core infrastructure | -| `hanzo-tools-shell` | dag, ps, zsh, shell | Command execution | -| `hanzo-tools-fs` | read, write, edit, tree, find, search | Filesystem operations | -| `hanzo-tools-browser` | browser (70+ actions) | Playwright automation | -| `hanzo-tools-memory` | memory (unified) | Memory management | -| `hanzo-tools-reasoning` | think, critic | Structured reasoning | -| `hanzo-tools-lsp` | lsp | Code intelligence | -| `hanzo-tools-refactor` | refactor | Code refactoring | -| `hanzo-tools-database` | sql_*, graph_* | Database operations | -| `hanzo-tools-agent` | agent, iching, review | CLI agent runners | -| `hanzo-tools-llm` | llm, consensus | LLM operations | -| `hanzo-tools-vector` | vector_index, vector_search | Embeddings | -| `hanzo-tools-todo` | todo | Task management | -| `hanzo-tools-jupyter` | jupyter | Notebook editing | -| `hanzo-tools-editor` | neovim_* | Editor integration | - -## Architecture - -All tools follow a common pattern: - -```python -from hanzo_tools.core import BaseTool - -class MyTool(BaseTool): - name = "my_tool" - - @property - def description(self) -> str: - return "Tool description" - - async def call(self, ctx, **params) -> str: - # Implementation - return "result" -``` - -Tools are discovered via entry points: - -```toml -[project.entry-points."hanzo.tools"] -shell = "hanzo_tools.shell:TOOLS" -``` - -## Usage with MCP - -Tools automatically register with hanzo-mcp: - -```bash -# Install hanzo-mcp with tools -pip install hanzo-mcp[tools-all] - -# Run MCP server -hanzo-mcp -``` - -Or use tools directly: - -```python -from hanzo_tools.shell import DagTool - -dag = DagTool() -result = await dag.call(ctx, commands=["ls", "pwd"]) -``` diff --git a/docs/content/docs/tools/memory.mdx b/docs/content/docs/tools/memory.mdx deleted file mode 100644 index dc29ec0ef..000000000 --- a/docs/content/docs/tools/memory.mdx +++ /dev/null @@ -1,172 +0,0 @@ ---- -title: Memory Tools -description: Persistent memory and knowledge management for AI agents ---- - -The `hanzo-tools-memory` package provides unified memory management. - -## Installation - -```bash -pip install hanzo-tools-memory -``` - -## Unified Memory Tool - -All memory operations through a single tool with actions: - -### Recall Memories - -```python -memory(action="recall", query="user preferences") -memory(action="recall", query="previous decisions", scope="project") -``` - -### Create Memories - -```python -memory(action="create", data={ - "statements": [ - "User prefers TypeScript over JavaScript", - "Project uses PostgreSQL database" - ] -}) -``` - -### Update Memories - -```python -memory(action="update", data={ - "id": "mem_123", - "content": "User now prefers Python over TypeScript" -}) -``` - -### Delete Memories - -```python -memory(action="delete", data={"id": "mem_123"}) -``` - -### Manage Memories - -```python -memory(action="manage") # List all memory operations -``` - -## Knowledge Base Operations - -### Recall Facts - -Query structured knowledge bases: - -```python -memory(action="facts", query="API authentication", kb_name="api_docs") -``` - -### Store Facts - -```python -memory(action="store", data={ - "facts": ["API uses JWT tokens", "Rate limit is 100/hour"], - "kb_name": "api_docs" -}) -``` - -### Summarize to Memory - -Condense content into memory: - -```python -memory(action="summarize", data={ - "content": "Long document text...", - "key_points": True -}) -``` - -### Knowledge Base Management - -```python -memory(action="kb", data={ - "action": "list" # List all knowledge bases -}) - -memory(action="kb", data={ - "action": "create", - "name": "project_docs", - "description": "Project documentation" -}) -``` - -## Memory Scopes - -Memories can be scoped: - -- **global** - Available across all sessions -- **project** - Specific to current project -- **session** - Current conversation only - -```python -memory(action="recall", query="preferences", scope="global") -memory(action="recall", query="architecture", scope="project") -``` - -## Best Practices - -1. **Use semantic queries** - Natural language works better than keywords -2. **Scope appropriately** - Project-specific info shouldn't be global -3. **Store structured facts** - Use knowledge bases for reference data -4. **Summarize long content** - Use summarize for large documents - -## Examples - -### Project Context - -```python -# Store project decisions -memory(action="create", data={ - "statements": [ - "Using React 18 with TypeScript", - "State management with Zustand", - "API uses REST with OpenAPI spec" - ], - "scope": "project" -}) - -# Recall later -memory(action="recall", query="what framework", scope="project") -``` - -### API Documentation - -```python -# Build knowledge base -memory(action="store", data={ - "kb_name": "api_endpoints", - "facts": [ - "POST /users creates a new user", - "GET /users/:id returns user details", - "PUT /users/:id updates user", - "DELETE /users/:id removes user" - ] -}) - -# Query later -memory(action="facts", query="how to create user", kb_name="api_endpoints") -``` - -### Conversation Context - -```python -# Remember user preferences -memory(action="create", data={ - "statements": [ - "User prefers concise responses", - "User is experienced with Python", - "Working on a FastAPI project" - ] -}) - -# Recall for context -memory(action="recall", query="user expertise level") -``` diff --git a/docs/content/docs/tools/meta.json b/docs/content/docs/tools/meta.json deleted file mode 100644 index d3819efdf..000000000 --- a/docs/content/docs/tools/meta.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "title": "Tools", - "pages": [ - "index", - "shell", - "filesystem", - "browser", - "memory", - "reasoning", - "agent", - "consensus" - ] -} diff --git a/docs/content/docs/tools/reasoning.mdx b/docs/content/docs/tools/reasoning.mdx deleted file mode 100644 index e8c223f94..000000000 --- a/docs/content/docs/tools/reasoning.mdx +++ /dev/null @@ -1,200 +0,0 @@ ---- -title: Reasoning Tools -description: Structured thinking and critical analysis for AI agents ---- - -The `hanzo-tools-reasoning` package provides tools for structured reasoning. - -## Installation - -```bash -pip install hanzo-tools-reasoning -``` - -## think - -Structured reasoning and analysis. Use this to work through complex problems step by step. - -### Basic Usage - -```python -think(thought=""" -Analyzing the authentication flow: -1. User submits credentials -2. Server validates against database -3. JWT token is generated -4. Token returned to client -5. Client stores in localStorage - -This follows standard OAuth 2.0 patterns. -""") -``` - -### Problem Analysis - -```python -think(thought=""" -Problem: API response times are slow - -Potential causes: -- Database queries not optimized -- No caching layer -- Network latency -- Large payload sizes - -Investigation steps: -1. Add query timing logs -2. Profile database queries -3. Check for N+1 queries -4. Measure network latency -""") -``` - -### Architecture Decisions - -```python -think(thought=""" -Decision: Choosing between REST and GraphQL - -REST: -+ Simple, well-understood -+ Excellent caching -+ Mature tooling -- Over/under fetching -- Multiple round trips - -GraphQL: -+ Flexible queries -+ Single endpoint -+ Strong typing -- Complexity overhead -- Caching challenges - -Recommendation: REST for this project because: -- Team has REST experience -- Simple CRUD operations -- Caching is important -""") -``` - -## critic - -Critical analysis and code review. Use this to evaluate implementations and identify issues. - -### Code Review - -```python -critic(analysis=""" -Code Review: user_service.py - -Issues Found: -- No error handling for database failures (line 45) -- SQL injection vulnerability in query construction (line 67) -- Missing input validation for email field (line 23) -- No unit tests for edge cases - -Security Concerns: -- Password stored in plain text (line 89) -- No rate limiting on authentication endpoint - -Recommendations: -1. Add try/catch for database operations -2. Use parameterized queries -3. Add email validation with pydantic -4. Hash passwords with bcrypt -5. Implement rate limiting middleware -""") -``` - -### Architecture Review - -```python -critic(analysis=""" -Architecture Review: Microservices Design - -Strengths: -- Clear service boundaries -- Independent deployability -- Technology flexibility - -Weaknesses: -- No service discovery mechanism -- Missing circuit breaker patterns -- Synchronous inter-service calls create coupling -- No distributed tracing - -Recommendations: -1. Implement service mesh (Istio/Linkerd) -2. Add circuit breakers (resilience4j) -3. Use async messaging for non-critical paths -4. Deploy distributed tracing (Jaeger) -""") -``` - -### Implementation Critique - -```python -critic(analysis=""" -Implementation Review: Rate Limiter - -Current Implementation: -- Uses in-memory counter -- Fixed window algorithm -- No distributed support - -Problems: -1. Lost on restart -2. Window boundary issues -3. Single server only - -Better Approach: -- Use Redis for shared state -- Sliding window algorithm -- Token bucket for smoother limiting - -Code suggestion: -```python -from redis import Redis -from time import time - -class SlidingWindowRateLimiter: - def __init__(self, redis: Redis, limit: int, window: int): - self.redis = redis - self.limit = limit - self.window = window - - def is_allowed(self, key: str) -> bool: - now = time() - pipe = self.redis.pipeline() - pipe.zremrangebyscore(key, 0, now - self.window) - pipe.zadd(key, {str(now): now}) - pipe.zcard(key) - pipe.expire(key, self.window) - _, _, count, _ = pipe.execute() - return count <= self.limit -``` -""") -``` - -## When to Use - -### think - -- Working through complex problems -- Making architectural decisions -- Planning implementation steps -- Analyzing requirements - -### critic - -- Reviewing code quality -- Security analysis -- Architecture evaluation -- Identifying improvements - -## Best Practices - -1. **Be specific** - Detailed analysis yields better insights -2. **Structure your thoughts** - Use numbered lists and categories -3. **Include recommendations** - Always suggest improvements -4. **Reference specifics** - Line numbers, file names, concrete examples diff --git a/docs/content/docs/tools/shell.mdx b/docs/content/docs/tools/shell.mdx deleted file mode 100644 index 03f311711..000000000 --- a/docs/content/docs/tools/shell.mdx +++ /dev/null @@ -1,151 +0,0 @@ ---- -title: Shell Tools -description: Command execution with DAG support and auto-backgrounding ---- - -The `hanzo-tools-shell` package provides command execution tools with DAG (Directed Acyclic Graph) support for parallel execution. - -## Installation - -```bash -pip install hanzo-tools-shell -``` - -## dag - -Execute commands in serial, parallel, or DAG patterns. - -### Serial Execution - -```python -dag(commands=["ls", "pwd", "git status"]) -``` - -### Parallel Execution - -```python -dag(commands=["npm install", "cargo build"], parallel=True) -``` - -### Mixed DAG - -```python -dag(commands=[ - "mkdir -p dist", - {"parallel": ["cp a.txt dist/", "cp b.txt dist/"]}, - "zip -r out.zip dist/" -]) -``` - -### Named Steps with Dependencies - -```python -dag(commands=[ - {"id": "build", "run": "make build"}, - {"id": "test", "run": "make test", "after": ["build"]}, -]) -``` - -### Tool Invocations - -Execute other tools within the DAG: - -```python -dag(commands=[ - {"tool": "search", "input": {"pattern": "TODO"}}, - {"tool": "tree", "input": {"path": ".", "depth": 2}}, -], parallel=True) -``` - -## ps - -Manage background processes. - -### List Processes - -```python -ps() # List all background processes -``` - -### Get Process Info - -```python -ps(id="abc123") # Get specific process info -``` - -### View Logs - -```python -ps(logs="abc123", n=50) # Last 50 lines of output -``` - -### Kill Process - -```python -ps(kill="abc123") # SIGTERM -ps(kill="abc123", sig=9) # SIGKILL -``` - -## zsh / shell - -Direct shell execution. - -### zsh - -```python -zsh(command="echo $ZSH_VERSION") -zsh(command="source ~/.zshrc && mycmd") -``` - -### shell - -Smart shell that prefers zsh: - -```python -shell(command="ls -la") -shell(command="npm run build", cwd="/project", timeout=300) -``` - -## Auto-Backgrounding - -Commands automatically background after 60 seconds: - -1. Command starts executing -2. Waits for completion with timeout -3. If timeout: registers with ProcessManager, continues in background -4. Monitor with `ps(logs="id")` or kill with `ps(kill="id")` - -Configure timeout: - -```python -dag(commands=["long-running-task"], timeout=120) # 2 minutes -``` - -## Best Practices - -1. **Use dag for multiple commands** - Better than chaining with `&&` -2. **Parallel when possible** - Independent tasks should run in parallel -3. **Named steps for complex flows** - Makes dependencies explicit -4. **Monitor long tasks** - Use `ps` to check status - -## Examples - -### Build Pipeline - -```python -dag(commands=[ - {"id": "deps", "run": "npm install"}, - {"id": "lint", "run": "npm run lint", "after": ["deps"]}, - {"id": "test", "run": "npm test", "after": ["deps"]}, - {"id": "build", "run": "npm run build", "after": ["lint", "test"]}, -]) -``` - -### Project Analysis - -```python -dag(commands=[ - {"tool": "tree", "input": {"path": ".", "depth": 3}}, - {"tool": "search", "input": {"pattern": "TODO|FIXME"}}, -], parallel=True) -``` diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md deleted file mode 100644 index 4149e5444..000000000 --- a/docs/getting-started/installation.md +++ /dev/null @@ -1,142 +0,0 @@ -# Installation - -## Requirements - -- Python 3.12 or higher -- pip, uv, or pipx for package management - -## Quick Install - -### Using pip - -```bash -# Full install with all tools -pip install hanzo-mcp[tools-all] - -# Or just the MCP server -pip install hanzo-mcp - -# Or just the agent SDK -pip install hanzo-agent -``` - -### Using uv (Recommended) - -[uv](https://github.com/astral-sh/uv) is the fastest Python package manager: - -```bash -# Install uv first -curl -LsSf https://astral.sh/uv/install.sh | sh - -# Install hanzo-mcp -uv pip install hanzo-mcp - -# Run directly without installing -uvx hanzo-mcp -``` - -### Using pipx (Isolated) - -```bash -pipx install hanzo-mcp -``` - -## Optional Dependencies - -Install specific tool packages as needed: - -```bash -# Browser automation (Playwright) -pip install hanzo-tools-browser - -# Database tools -pip install hanzo-tools-database - -# Vector search -pip install hanzo-tools-vector[full] -``` - -## Bundles - -Choose a bundle based on your needs: - -| Bundle | Packages | Use Case | -|--------|----------|----------| -| `hanzo-mcp` | Core MCP only | Minimal install | -| `hanzo-mcp[tools-core]` | fs, shell, memory, reasoning | Essential tools | -| `hanzo-mcp[tools-dev]` | + lsp, refactor, browser | Development | -| `hanzo-mcp[tools-all]` | All 30+ tools | Full features | - -## VS Code Extension - -For VS Code, Cursor, or Antigravity: - -1. Install the Hanzo extension from the marketplace -2. The extension auto-detects `uvx` and uses Python MCP by default -3. Configure the backend in settings: - -```json -{ - "hanzo.mcp.backend": "auto", - "hanzo.mcp.pythonCommand": "uvx hanzo-mcp" -} -``` - -## Verify Installation - -```bash -# Check version -uvx hanzo-mcp --version - -# Run in stdio mode (for MCP clients) -uvx hanzo-mcp --transport stdio - -# Run development server -uvx hanzo-mcp-dev -``` - -## Environment Variables - -| Variable | Description | Default | -|----------|-------------|---------| -| `HANZO_AUTO_BACKGROUND_TIMEOUT` | Auto-background timeout (seconds, 0 to disable) | `45` | -| `HANZO_MCP_TRANSPORT` | Transport mode (stdio, tcp) | `stdio` | -| `HANZO_MCP_PORT` | TCP port when using tcp transport | `3000` | -| `HANZO_ALLOWED_PATHS` | Comma-separated allowed paths | (none) | - -## Troubleshooting - -### uvx not found - -```bash -# Install uv first -curl -LsSf https://astral.sh/uv/install.sh | sh - -# Reload shell -source ~/.bashrc # or ~/.zshrc -``` - -### Permission errors - -```bash -# Use user install -pip install --user hanzo-mcp - -# Or use a virtual environment -python -m venv .venv -source .venv/bin/activate -pip install hanzo-mcp -``` - -### Python version - -Ensure Python 3.12+: - -```bash -python --version -# Python 3.12.x - -# If needed, install with pyenv -pyenv install 3.12 -pyenv global 3.12 -``` diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md deleted file mode 100644 index 5d3fdc0f1..000000000 --- a/docs/getting-started/quickstart.md +++ /dev/null @@ -1,175 +0,0 @@ -# Quickstart - -Get up and running with Hanzo Python SDK in minutes. - -## 1. Install - -```bash -pip install hanzo-mcp -``` - -## 2. Run MCP Server - -### With Claude Code - -```bash -uvx hanzo-mcp -``` - -Claude Code will auto-detect and use all 30+ tools. - -### With VS Code Extension - -1. Install the Hanzo extension -2. The extension auto-detects `uvx` and starts the Python MCP -3. All tools are available in the AI assistant - -## 3. Use the Tools - -### File Operations - -```python -# Read a file -read(file_path="/path/to/file.py") - -# Edit a file -edit( - file_path="/path/to/file.py", - old_string="old code", - new_string="new code" -) - -# Search for patterns -search(pattern="TODO", path="./src") -``` - -### Shell Commands - -```python -# Run commands (auto-backgrounds after 30s) -cmd("npm install") - -# Run in parallel -cmd(["npm install", "cargo build"], parallel=True) - -# DAG execution -cmd([ - "mkdir dist", - {"parallel": ["cp a dist/", "cp b dist/"]}, - "zip -r out.zip dist/" -]) -``` - -### Browser Automation - -```python -# Navigate to page -browser(action="navigate", url="https://example.com") - -# Click element -browser(action="click", selector="button.submit") - -# Take screenshot -browser(action="screenshot", full_page=True) - -# Mobile emulation -browser(action="emulate", device="mobile") -``` - -### Memory & Reasoning - -```python -# Save to memory -memory(action="create", data={"note": "Important insight"}) - -# Recall memories -memory(action="recall", query="project architecture") - -# Structured thinking -think(thought="Analyzing the problem...") - -# Critical analysis -critic(analysis="Review this implementation...") -``` - -## 4. Agent SDK - -Build your own AI agents: - -```python -from agents import Agent, Runner - -# Create an agent -agent = Agent( - name="code_reviewer", - instructions=""" - You are a code review expert. - Analyze code for bugs, performance issues, and best practices. - """, - tools=[review_code, suggest_improvements] -) - -# Run the agent -result = Runner.run_sync( - agent, - "Review this Python function for issues..." -) - -print(result.final_output) -``` - -### Multi-Agent Systems - -```python -from agents import Agent, handoff - -# Create specialized agents -security_agent = Agent( - name="security", - instructions="Analyze code for security vulnerabilities." -) - -performance_agent = Agent( - name="performance", - instructions="Analyze code for performance issues." -) - -# Main coordinator -lead_agent = Agent( - name="lead", - instructions="Coordinate code review. Handoff to specialists.", - handoffs=[ - handoff(security_agent, "security issues"), - handoff(performance_agent, "performance concerns") - ] -) -``` - -## 5. Configuration - -### Environment Variables - -```bash -# Disable auto-backgrounding -export HANZO_AUTO_BACKGROUND_TIMEOUT=0 - -# Set allowed paths -export HANZO_ALLOWED_PATHS="/home/user/projects,/tmp" -``` - -### VS Code Settings - -```json -{ - "hanzo.mcp.backend": "python", - "hanzo.mcp.pythonCommand": "uvx hanzo-mcp", - "hanzo.mcp.disableBrowserTool": false, - "hanzo.mcp.enabledTools": ["read", "write", "cmd", "search"] -} -``` - -## Next Steps - -- [MCP Tools Reference](../mcp/index.md) - Complete tool documentation -- [Agent SDK Guide](../agent/index.md) - Build custom AI agents -- [Configuration](../mcp/configuration.md) - Advanced configuration options diff --git a/docs/hip/HIP-0300.md b/docs/hip/HIP-0300.md deleted file mode 100644 index 4c7d389ca..000000000 --- a/docs/hip/HIP-0300.md +++ /dev/null @@ -1,466 +0,0 @@ -# HIP-0300: Unified MCP Tools Architecture - -**Status:** Draft -**Author:** Hanzo AI -**Created:** January 2025 -**Updated:** January 2025 - -## Abstract - -HIP-0300 defines a unified architecture for Model Context Protocol (MCP) tools, consolidating 52+ individual tools into ~16 orthogonal, composable operators. The design follows Unix philosophy: each tool does one thing well, with clear composition laws. - -## Motivation - -The original hanzo-mcp implementation grew organically to 52+ tools with significant overlap: -- Multiple search tools (grep, search, find, ast_search) -- Multiple file tools (read, write, edit, cat, head, tail) -- Multiple shell tools (bash, zsh, sh, shell, cmd) - -This creates confusion for LLM agents and increases cognitive load. HIP-0300 restructures tools around orthogonal axes with a formal effect lattice. - -## Design Philosophy - -### Core Principles - -1. **Orthogonal Axes**: Tools organized along independent dimensions -2. **Composability**: Small operators that combine predictably -3. **Effect Tracking**: Every operation has a declared effect level -4. **Transform vs Apply**: Pure transforms produce Patches; Apply commits them -5. **Minimal Surface Area**: 16 core operators cover all use cases - -### The Three Lattices - -Three lattices constrain all operations: - -``` -EFFECT LATTICE (purity) -โ”œโ”€ PURE (no side effects, referentially transparent) -โ”œโ”€ DETERMINISTIC_EFFECT (effects, but reproducible) -โ””โ”€ NONDETERMINISTIC_EFFECT (network, time, randomness) - -REPRESENTATION LATTICE (data granularity) -โ”œโ”€ Bytes (raw content) -โ”œโ”€ Lines (text with line numbers) -โ”œโ”€ AST (parsed structure) -โ”œโ”€ Patch (diff/change set) -โ””โ”€ Symbols (semantic references) - -SCOPE LATTICE (operational boundary) -โ”œโ”€ Span (byte/char range) -โ”œโ”€ Region (line range) -โ”œโ”€ File (single file) -โ”œโ”€ Tree (directory subtree) -โ””โ”€ Repo (entire repository) -``` - -## Operator Surface - -### Core Operators (HIP-0300) - -| Tool | Axis | Actions | Effect | -|------|------|---------|--------| -| `fs` | Bytes + Paths | read, write, edit, search, list, stat, patch, tree, glob | DETERMINISTIC | -| `id` | Identity | hash, uri, ref, verify | PURE | -| `code` | Symbols + Structure | parse, serialize, symbols, definition, references, transform, summarize | PURE/DETERMINISTIC | -| `proc` | Execution | run, bg, signal, status, wait | NONDETERMINISTIC | -| `vcs` | History + Diffs | status, diff, commit, log, branch, stash | DETERMINISTIC | -| `test` | Validation | check, build, test, detect | NONDETERMINISTIC | -| `net` | Network | search, fetch, download, crawl, head | NONDETERMINISTIC | -| `plan` | Orchestration | intent, route, compose | PURE | - -### Control Surfaces - -| Tool | Surface | Actions | Effect | -|------|---------|---------|--------| -| `browser` | Web DOM | navigate, click, type, screenshot, evaluate, etc. | NONDETERMINISTIC | -| `computer` | OS Desktop | screenshot, click, type, key, scroll, etc. | NONDETERMINISTIC | - -### Extended Operators - -| Tool | Domain | Actions | -|------|--------|---------| -| `lsp` | Semantic Stream | diagnostics, code_actions, hover, completion | -| `memory` | Knowledge Persistence | read, write, search, create, recall | -| `todo` | Task Tracking | list, add, update, remove | -| `reasoning` | Cognition | think, critic | -| `agent` | Multi-Agent | run, list, status, config | -| `llm` | LLM Interface | chat, consensus | - -## Verb Kernel - -The 27-verb kernel with typed signatures: - -### File Operations (fs) -``` -read : Path โ†’ Bytes | Text | Lines -write : (Path, Content) โ†’ {ok, hash} -edit : (Path, Patch) โ†’ {ok, hash} -search : (Pattern, Scope) โ†’ [Match] -list : Path โ†’ [Entry] -stat : Path โ†’ {size, mtime, mode, hash} -patch : (Path, Patch, base_hash?) โ†’ {ok, new_hash} -tree : (Path, depth?) โ†’ TreeNode -glob : (Pattern, Path?) โ†’ [Path] -``` - -### Identity Operations (id) -``` -hash : Content โ†’ {digest, algo, size} -uri : Path โ†’ {uri, path, exists} -ref : (Path, line?, col?) โ†’ {uri, range?, hash?} -verify : (Content, Digest) โ†’ {match, actual, expected} -``` - -### Code Operations (code) -``` -parse : (Path | Text, lang?) โ†’ AST -serialize : AST โ†’ Text -symbols : (Path | AST, kind?) โ†’ [Symbol] -definition : (Path, Position) โ†’ [Location] -references : (Path, Position) โ†’ [Location] -transform : (Path | Text, kind, params) โ†’ Patch # PURE! -summarize : (Diff | Log | Report) โ†’ {summary, risks, next_actions} -``` - -### Process Operations (proc) -``` -run : Command โ†’ {stdout, stderr, code} -bg : Command โ†’ ProcessID -signal : (ProcessID, Signal) โ†’ {ok} -status : ProcessID? โ†’ [ProcessStatus] -wait : (ProcessID, timeout?) โ†’ {stdout, stderr, code} -``` - -### Version Control (vcs) -``` -status : () โ†’ {staged, unstaged, untracked} -diff : (ref1?, ref2?, paths?) โ†’ Diff -commit : (message, files?) โ†’ {sha, message} -log : (n?, since?, until?, path?) โ†’ [Commit] -branch : (name?, action?) โ†’ {current, branches} -stash : (action, message?) โ†’ {ok} -``` - -### Validation (test) -``` -check : (Path?, tool?) โ†’ {diagnostics, pass} # Lint/typecheck -build : (Path?, tool?) โ†’ {success, artifacts} # Compilation -test : (selector?, tool?) โ†’ {passed, failed, summary} # Runtime -detect : (Path?) โ†’ {test_runner, build_tool, check_tool} -``` - -### Network (net) -``` -search : (Query, engine?) โ†’ [{url, title, snippet}] -fetch : (URL, extract_text?) โ†’ {text, mime, status, hash} -download : (URL, dest?, assets?) โ†’ {path, size, mime} -crawl : (URL, dest, depth?, limit?) โ†’ {pages, count} -head : URL โ†’ {status, headers, size?, mime?} -``` - -### Orchestration (plan) -``` -intent : NL โ†’ IntentIR -route : (IntentIR, Policy?) โ†’ Plan -compose : Plan โ†’ ExecGraph -``` - -## Effect Annotations - -Every action declares its effect level: - -```python -class Effect(Enum): - PURE = "pure" # No side effects - DETERMINISTIC = "deterministic" # Effects, reproducible - NONDETERMINISTIC = "nondeterministic" # Network/time/random - -# Examples: -# fs.read โ†’ DETERMINISTIC_EFFECT (reads disk) -# code.parse โ†’ PURE (in-memory transform) -# code.transform โ†’ PURE (produces Patch value) -# fs.patch โ†’ DETERMINISTIC_EFFECT (writes disk) -# net.fetch โ†’ NONDETERMINISTIC_EFFECT (network I/O) -``` - -## Transform vs Apply - -Critical separation of concerns: - -``` -Transform (PURE) Apply (EFFECT) -โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -code.transform fs.patch - โ†’ Patch value โ†’ committed change - โ†’ base_hash โ†’ requires base_hash precondition - โ†’ preview-safe โ†’ point of no return - -vcs.diff vcs.commit - โ†’ Diff value โ†’ committed history -``` - -### Content-Addressable Edits - -All mutations use content-addressable storage for safety: - -```python -# Generate a transform -result = await code.call(ctx, action="transform", - path="/src/main.py", - kind="rename", - old_name="foo", - new_name="bar") -# Returns: {patch: [...], base_hash: "sha256:abc...", new_hash: "sha256:def..."} - -# Apply with precondition -result = await fs.call(ctx, action="patch", - path="/src/main.py", - patch=patch, - base_hash="sha256:abc...") # Must match current -# Fails if file changed since transform was computed -``` - -## Intent Routing - -Natural language maps to canonical operator chains: - -### Intent IR Structure -```python -class IntentIR: - category: str # navigate, explain, modify, validate, debug, create - action: str # find, understand, rename, test, trace, add - target: str # extracted entity/path/concept - confidence: float -``` - -### Canonical Chains - -| Intent Pattern | Canonical Chain | -|---------------|-----------------| -| "find X" | `fs.search(X)` | -| "what is X" | `fs.search(X) โ†’ code.summarize` | -| "rename X to Y" | `code.references(X) โ†’ code.transform(rename) โ†’ code.summarize โ†’ [policy] โ†’ fs.patch โ†’ test.run` | -| "fix bug in X" | `fs.read(X) โ†’ code.parse โ†’ code.transform(fix) โ†’ [policy] โ†’ fs.patch โ†’ test.run` | -| "add tests for X" | `code.symbols(X) โ†’ code.transform(add_tests) โ†’ fs.write โ†’ test.run` | -| "why does X fail" | `test.run(X) โ†’ vcs.log โ†’ code.summarize` | -| "refactor X" | `code.references(X) โ†’ code.transform โ†’ code.summarize โ†’ [policy] โ†’ fs.patch โ†’ test.run` | - -### Policy Gates - -High-risk operations require explicit approval: -- `fs.patch` (file modifications) -- `fs.write` (new file creation) -- `proc.run` with shell=True -- `vcs.commit` / `vcs.push` - -## Validation Loops - -Three distinct validation operations (Vim-inspired): - -### CHECK (`:make` equivalent) -- Fast, incremental feedback -- Lint, typecheck, format check -- Tools: ruff, mypy, eslint, tsc, clippy -- Returns: `{diagnostics: [], pass: bool}` - -### BUILD (`:!make` equivalent) -- Whole-project compilation -- Dependency resolution -- Tools: pip, npm, cargo, go build, make -- Returns: `{success: bool, artifacts: [], errors: []}` - -### TEST (`:!make test` equivalent) -- Runtime behavior validation -- Isolated execution environment -- Tools: pytest, jest, go test, cargo test -- Returns: `{passed: int, failed: int, summary: str}` - -### Auto-Detection - -```python -# test.detect() returns: -{ - "test_runner": {"name": "pytest", "cmd": ["pytest", "-v"]}, - "build_tool": {"name": "pip", "cmd": ["pip", "install", "-e", "."]}, - "check_tool": {"name": "ruff", "cmd": ["ruff", "check", "."]} -} -``` - -## Implementation - -### BaseTool Pattern - -```python -from hanzo_tools.core import BaseTool, ActionHandler, ToolError - -class FsTool(BaseTool): - name: ClassVar[str] = "fs" - - def __init__(self, cwd: str | None = None): - super().__init__() - self.cwd = cwd or os.getcwd() - self._register_actions() - - def _register_actions(self): - @self.action("read", "Read file content") - async def read(ctx, path: str, encoding: str = "utf-8"): - """Path โ†’ Content""" - # Implementation - return {"text": content, "hash": content_hash(content)} - - @self.action("search", "Search for pattern") - async def search(ctx, pattern: str, path: str = ".", **opts): - """(Pattern, Scope) โ†’ [Match]""" - # Implementation - return {"matches": matches, "count": len(matches)} -``` - -### Unified Response Envelope - -All tools return a consistent envelope: - -```python -{ - "ok": True, # Success indicator - "data": {...}, # Action-specific result - "error": None, # Or {code, message, details} - "meta": { # Optional metadata - "duration_ms": 42, - "effect": "deterministic" - } -} -``` - -### Entry Points - -Each package exports via entry points: - -```toml -# pyproject.toml -[project.entry-points."hanzo.tools"] -filesystem = "hanzo_tools.filesystem:TOOLS" -code = "hanzo_tools.code:TOOLS" -plan = "hanzo_tools.plan:TOOLS" -test = "hanzo_tools.test:TOOLS" -net = "hanzo_tools.net:TOOLS" -``` - -## Package Structure - -``` -pkg/ -โ”œโ”€โ”€ hanzo-tools-core/ # BaseTool, IdTool, ToolRegistry -โ”‚ โ””โ”€โ”€ hanzo_tools/core/ -โ”œโ”€โ”€ hanzo-tools-filesystem/ # FsTool (read, write, edit, search, etc.) -โ”‚ โ””โ”€โ”€ hanzo_tools/filesystem/ -โ”œโ”€โ”€ hanzo-tools-code/ # CodeTool (parse, transform, summarize) -โ”‚ โ””โ”€โ”€ hanzo_tools/code/ -โ”œโ”€โ”€ hanzo-tools-shell/ # ProcTool (run, bg, signal, wait) -โ”‚ โ””โ”€โ”€ hanzo_tools/shell/ -โ”œโ”€โ”€ hanzo-tools-vcs/ # VcsTool (status, diff, commit, log) -โ”‚ โ””โ”€โ”€ hanzo_tools/vcs/ -โ”œโ”€โ”€ hanzo-tools-test/ # TestTool (check, build, test) -โ”‚ โ””โ”€โ”€ hanzo_tools/test/ -โ”œโ”€โ”€ hanzo-tools-net/ # NetTool (search, fetch, download, crawl) -โ”‚ โ””โ”€โ”€ hanzo_tools/net/ -โ”œโ”€โ”€ hanzo-tools-plan/ # PlanTool (intent, route, compose) -โ”‚ โ””โ”€โ”€ hanzo_tools/plan/ -โ”œโ”€โ”€ hanzo-tools-browser/ # BrowserTool (Playwright control) -โ”‚ โ””โ”€โ”€ hanzo_tools/browser/ -โ”œโ”€โ”€ hanzo-tools-computer/ # ComputerTool (OS desktop control) -โ”‚ โ””โ”€โ”€ hanzo_tools/computer/ -โ””โ”€โ”€ hanzo-mcp/ # MCP server (discovers tools via entry points) - โ””โ”€โ”€ hanzo_mcp/ -``` - -## Migration Path - -### Phase 1: Implement Core Operators -- [x] `fs` - Filesystem operations (existing hanzo-tools-filesystem) -- [x] `id` - Identity operations (hash, uri, ref, verify) -- [x] `code` - Code operations (parse, transform, summarize) -- [x] `proc` - Process execution (existing hanzo-tools-shell) -- [x] `vcs` - Version control (existing hanzo-tools-vcs) -- [x] `test` - Validation loops (check, build, test) -- [x] `net` - Network operations (search, fetch, download, crawl) -- [x] `plan` - Orchestration (intent, route, compose) - -### Phase 2: Control Surfaces -- [x] `browser` - Web DOM control (existing hanzo-tools-browser) -- [ ] `computer` - OS desktop control (needs update) - -### Phase 3: Extended Operators -- [ ] `lsp` - Language server integration -- [ ] `dbg` - Debugger control (breakpoint, step, eval) -- [ ] `repl` - Interactive evaluation (send, recv, reset) - -### Phase 4: Deprecation -- Deprecate individual tools (read_file, search_code, etc.) -- Map old names to new unified tools -- Remove after 6 months - -## Composition Examples - -### Safe Refactoring -```python -# 1. Find all references (PURE) -refs = await code.call(ctx, action="references", path="src/auth.py", position={"line": 42, "col": 10}) - -# 2. Generate patch (PURE) -patch = await code.call(ctx, action="transform", path="src/auth.py", kind="rename", old_name="authenticate", new_name="verify_user") - -# 3. Summarize changes (PURE) -summary = await code.call(ctx, action="summarize", diff=patch["patch"]) - -# 4. [POLICY GATE] - User approves - -# 5. Apply patch (EFFECT) -await fs.call(ctx, action="patch", path="src/auth.py", patch=patch["patch"], base_hash=patch["base_hash"]) - -# 6. Verify (EFFECT) -await test.call(ctx, action="run") -``` - -### Site Mirroring -```python -# Crawl site recursively -result = await net.call(ctx, action="crawl", url="https://docs.example.com", dest="./mirror", depth=3, limit=100) - -# Returns: {pages: ["/mirror/index.html", ...], count: 47} -``` - -### Intelligent Search -```python -# Parse intent -intent = await plan.call(ctx, action="intent", nl="find where user authentication happens") - -# Route to canonical chain -plan = await plan.call(ctx, action="route", intent_ir=intent) - -# Execute chain -for step in plan["nodes"]: - if step.get("policy_gate"): - # Request approval - pass - result = await dispatch(step["tool"], step["action"], step["params"]) -``` - -## Security Considerations - -1. **Path Traversal**: All path operations validate against allowed directories -2. **Command Injection**: `proc.run` sanitizes inputs, requires explicit shell=True -3. **Network Access**: `net.*` operations respect proxy settings and rate limits -4. **File Modifications**: Require `base_hash` precondition to prevent race conditions -5. **Policy Gates**: High-risk operations require explicit approval - -## References - -- [Unix Philosophy](https://en.wikipedia.org/wiki/Unix_philosophy) -- [Model Context Protocol](https://github.com/anthropics/mcp) -- [Effect Systems](https://en.wikipedia.org/wiki/Effect_system) -- [Content-Addressable Storage](https://en.wikipedia.org/wiki/Content-addressable_storage) - -## Changelog - -- **2025-01-22**: Initial draft with operator lattice specification -- **2025-01-21**: Created CodeTool, PlanTool, TestTool, NetTool, IdTool packages diff --git a/docs/hip/HIP-0301.md b/docs/hip/HIP-0301.md deleted file mode 100644 index 2794ad084..000000000 --- a/docs/hip/HIP-0301.md +++ /dev/null @@ -1,420 +0,0 @@ -# HIP-0301: High-Performance Agent Communication Protocol - -**Status:** Draft -**Author:** Hanzo AI -**Created:** January 2025 -**Updated:** January 2025 - -## Abstract - -HIP-0301 defines a high-performance agent-to-agent communication protocol using Cap'n Proto as the wire format, with MCP JSON-RPC as a fallback. This enables sub-millisecond tool invocation between agents while maintaining backward compatibility with existing MCP infrastructure. - -## Motivation - -Current MCP communication has inherent limitations: - -1. **JSON Serialization Overhead**: Every tool call requires JSON encode/decode (~100-500ฮผs per call) -2. **No Zero-Copy**: JSON requires full parsing before use; data copied multiple times -3. **Schema Flexibility = Runtime Errors**: JSON's flexibility means errors surface at runtime -4. **Consensus Latency**: Multi-agent consensus requires O(nยฒ) messages; JSON overhead compounds - -For AI agent swarms running metastable consensus, the overhead is unacceptable: -- 10 agents ร— 10 rounds ร— 100ฮผs = 10ms just for serialization -- Real-world latencies 10-100x higher due to memory allocation - -Cap'n Proto solves this with: -- **Zero-copy reads**: Wire format = memory format (no parsing) -- **Incremental reads**: Access fields without deserializing entire message -- **Type safety**: Schema-defined contracts, compile-time verification -- **RPC built-in**: Native async RPC with promise pipelining - -## Design - -### Transport Hierarchy - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Agent Bus Interface โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Cap'n Proto โ”‚ โ”‚ MCP JSON-RPC โ”‚ โ”‚ Unix IPC โ”‚ โ”‚ -โ”‚ โ”‚ (preferred) โ”‚ โ”‚ (fallback) โ”‚ โ”‚ (local) โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ–ผ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”‚ -โ”‚ โ”‚ Unified Tool Invocation Layer โ”‚โ”‚ -โ”‚ โ”‚ tool.call(action, params) -> Response โ”‚โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -### AgentBus Trait - -```rust -/// High-performance agent communication bus -#[async_trait] -pub trait AgentBus: Send + Sync { - /// Get bus capabilities - fn capabilities(&self) -> BusCapabilities; - - /// Register local agent - async fn register(&self, agent_id: &str, handler: Arc) -> Result<()>; - - /// Unregister agent - async fn unregister(&self, agent_id: &str) -> Result<()>; - - /// Call tool on remote agent (point-to-point) - async fn call( - &self, - from: &str, - to: &str, - tool: &str, - action: &str, - params: &[u8], // Pre-serialized (capnp or json) - ) -> Result>; - - /// Broadcast to all agents - async fn broadcast( - &self, - from: &str, - tool: &str, - action: &str, - params: &[u8], - ) -> Result)>>; // (agent_id, response) - - /// Run consensus protocol - async fn consensus( - &self, - prompt: &str, - participants: &[&str], - config: ConsensusConfig, - ) -> Result; -} - -#[derive(Clone)] -pub struct BusCapabilities { - pub supports_capnp: bool, - pub supports_mcp: bool, - pub supports_unix_ipc: bool, - pub max_message_size: usize, - pub supports_streaming: bool, -} - -pub struct ConsensusConfig { - pub rounds: u32, - pub k: u32, // Peer sample size - pub alpha: f64, // Confidence threshold - pub beta_1: f64, // Phase I threshold - pub beta_2: f64, // Phase II (finality) threshold - pub timeout_ms: u64, -} -``` - -### Cap'n Proto Schema - -```capnp -@0xb5e7e8d3c9f2a1b4; # Unique file ID - -# Core message types for agent communication - -struct AgentId { - id @0 :Text; - nodeId @1 :Text; # For distributed deployment - capabilities @2 :List(Text); -} - -struct ToolCall { - id @0 :UInt64; # Request ID for correlation - tool @1 :Text; # Tool name (fs, code, plan, etc.) - action @2 :Text; # Action within tool - params @3 :Data; # Serialized params (nested capnp or json) - effect @4 :Effect; # Declared effect level - traceId @5 :Text; # Distributed tracing -} - -enum Effect { - pure @0; - deterministic @1; - nondeterministic @2; -} - -struct ToolResponse { - id @0 :UInt64; # Matching request ID - ok @1 :Bool; - data @2 :Data; # Serialized result - error @3 :ToolError; - meta @4 :ResponseMeta; -} - -struct ToolError { - code @0 :ErrorCode; - message @1 :Text; - details @2 :Data; # JSON or nested capnp -} - -enum ErrorCode { - unknownAction @0; - invalidParams @1; - notFound @2; - conflict @3; - permissionDenied @4; - timeout @5; - internalError @6; -} - -struct ResponseMeta { - tool @0 :Text; - version @1 :Text; - action @2 :Text; - effect @3 :Effect; - durationNs @4 :UInt64; - traceId @5 :Text; -} - -# Consensus protocol messages - -struct ConsensusVote { - round @0 :UInt32; - fromAgent @1 :Text; - vote @2 :Text; # Agent's response/vote - confidence @3 :Float64; - luminance @4 :Float64; # Response time weight - signature @5 :Data; # Optional cryptographic signature -} - -struct ConsensusState { - round @0 :UInt32; - phase @1 :ConsensusPhase; - votes @2 :List(ConsensusVote); - winner @3 :Text; - synthesis @4 :Text; - finalized @5 :Bool; - confidence @6 :Float64; -} - -enum ConsensusPhase { - sampling @0; # Phase I: k-peer sampling - finality @1; # Phase II: threshold aggregation - complete @2; -} - -# Agent mesh management - -struct AgentRegistration { - agent @0 :AgentId; - endpoint @1 :Text; # capnp://host:port or mcp://host:port - tools @2 :List(Text); # Available tools - publicKey @3 :Data; # For authenticated consensus -} - -struct MeshTopology { - agents @0 :List(AgentRegistration); - gossipInterval @1 :UInt32; # ms between topology updates -} - -# RPC Interface - -interface AgentService { - # Core tool invocation - call @0 (request :ToolCall) -> (response :ToolResponse); - - # Streaming for large responses - stream @1 (request :ToolCall) -> (stream :StreamHandle); - - # Consensus participation - vote @2 (round :UInt32, prompt :Text, context :Data) -> (vote :ConsensusVote); - - # Mesh management - register @3 (registration :AgentRegistration) -> (accepted :Bool); - topology @4 () -> (mesh :MeshTopology); - - # Health - ping @5 () -> (pong :Bool, latencyNs :UInt64); -} - -interface StreamHandle { - next @0 () -> (chunk :Data, done :Bool); - cancel @1 () -> (); -} -``` - -### Protocol Negotiation - -When agents connect, they negotiate the best available transport: - -``` -Agent A Agent B - โ”‚ โ”‚ - โ”‚โ”€โ”€โ”€โ”€ HELLO (capabilities) โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ - โ”‚ โ”‚ - โ”‚โ—€โ”€โ”€โ”€ HELLO_ACK (selected) โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ - โ”‚ โ”‚ - โ”‚ Transport: capnp (preferred) โ”‚ - โ”‚ or: mcp-json (fallback) โ”‚ - โ”‚ or: unix-ipc (local) โ”‚ - โ”‚ โ”‚ - โ”‚โ•โ•โ•โ•โ•โ• Established โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ”‚ -``` - -### Integration with HIP-0300 Tools - -All HIP-0300 tools gain an `agent` action for consensus-based execution: - -```rust -// fs tool with consensus -fs_tool.call("agent", json!({ - "action": "read", - "path": "/etc/hosts", - "consensus": { - "participants": ["claude", "gpt4", "gemini"], - "require_agreement": true - } -})); - -// plan tool routes through consensus automatically -plan_tool.call("intent", json!({ - "nl": "refactor the authentication module", - "consensus": true // Uses metastable consensus for routing -})); -``` - -### Metastable Consensus Integration - -The plan tool uses metastable consensus for LLM-based intent parsing: - -```rust -impl PlanTool { - async fn intent_with_consensus( - &self, - nl: &str, - bus: &dyn AgentBus, - ) -> Result { - // Get available LLM agents - let agents = bus.list_agents_with_capability("llm").await?; - - // Run consensus - let state = bus.consensus( - &format!("Parse this intent: {}", nl), - &agents, - ConsensusConfig { - rounds: 3, - k: 3, - alpha: 0.6, - beta_1: 0.5, - beta_2: 0.8, - timeout_ms: 30000, - }, - ).await?; - - // Parse winner's response as IntentIR - serde_json::from_str(&state.synthesis) - .map_err(|e| ToolError::internal(format!("Invalid IR: {}", e))) - } -} -``` - -## Performance Characteristics - -| Metric | JSON-RPC (MCP) | Cap'n Proto | Improvement | -|--------|----------------|-------------|-------------| -| Serialization | 100-500ฮผs | 0ฮผs (zero-copy) | โˆž | -| Deserialization | 50-200ฮผs | 0ฮผs (zero-copy) | โˆž | -| Message overhead | ~40% | ~5% | 8x smaller | -| Memory allocations | O(n) | O(1) | Constant | -| Type safety | Runtime | Compile-time | Earlier errors | -| Consensus (10 agents, 10 rounds) | ~50-100ms | ~1-5ms | 10-100x | - -## Backward Compatibility - -1. **MCP Fallback**: If Cap'n Proto unavailable, seamlessly falls back to JSON-RPC -2. **Schema Evolution**: Cap'n Proto supports adding fields without breaking -3. **Hybrid Mesh**: Agents using different transports can still communicate via bridge -4. **Tool API Unchanged**: HIP-0300 tool signatures remain identical - -## Security Considerations - -1. **Authentication**: Optional Ed25519 signatures on consensus votes -2. **Encryption**: TLS 1.3 for capnp-rpc, with post-quantum option (ML-KEM) -3. **Rate Limiting**: Per-agent rate limits on consensus participation -4. **Message Size**: Configurable max message size (default 16MB) - -## Implementation Plan - -### Phase 1: Schema & Core (Week 1-2) -- [ ] Cap'n Proto schema definitions -- [ ] Rust capnp code generation -- [ ] Python pycapnp bindings -- [ ] AgentBus trait implementation - -### Phase 2: Transport (Week 3-4) -- [ ] Cap'n Proto RPC transport (Rust) -- [ ] Cap'n Proto RPC transport (Python) -- [ ] MCP JSON-RPC fallback -- [ ] Protocol negotiation - -### Phase 3: Consensus Integration (Week 5-6) -- [ ] Integrate hanzo-metastable-consensus -- [ ] Update plan_tool for consensus routing -- [ ] Agent tool with consensus support -- [ ] Benchmark and optimize - -### Phase 4: Production (Week 7-8) -- [ ] Documentation -- [ ] Migration guide -- [ ] Performance benchmarks -- [ ] Security audit - -## References - -- [Cap'n Proto](https://capnproto.org/) - Zero-copy serialization -- [HIP-0300](./HIP-0300.md) - Unified MCP Tools Architecture -- [Metastable Consensus](https://github.com/luxfi/consensus) - Hanzo consensus protocol -- [MCP Specification](https://modelcontextprotocol.io/) - Model Context Protocol - -## Appendix A: Message Size Comparison - -``` -Tool Call: fs.read("/etc/hosts") - -JSON-RPC (MCP): -{ - "jsonrpc": "2.0", - "id": 1, - "method": "tools/call", - "params": { - "name": "fs", - "arguments": { - "action": "read", - "path": "/etc/hosts" - } - } -} -Total: 156 bytes - -Cap'n Proto: -[8-byte header][tool: 2 bytes][action: 4 bytes][path: 11 bytes][padding: 1 byte] -Total: 26 bytes (6x smaller) -``` - -## Appendix B: Consensus Latency Analysis - -For N agents over R rounds with message size M: - -``` -JSON-RPC: T = N ร— R ร— (serialize(M) + deserialize(M) + network(M)) - โ‰ˆ N ร— R ร— (200ฮผs + 100ฮผs + network) - -Cap'n Proto: T = N ร— R ร— network(M') where M' โ‰ˆ M/6 - โ‰ˆ N ร— R ร— network_only - -For N=10, R=10, typical network=1ms: - JSON-RPC: 10 ร— 10 ร— (300ฮผs + 1ms) = 130ms - Cap'n Proto: 10 ร— 10 ร— 1ms = 100ms + 0ฮผs serialization = 100ms - -Actual improvement: ~30% in this scenario, but: -- Local agents (no network): 100% improvement (0ฮผs vs 30ms) -- Large messages: Scales linearly better -- High-frequency consensus: Cumulative savings -``` diff --git a/docs/hip/index.md b/docs/hip/index.md deleted file mode 100644 index 4cc7032ff..000000000 --- a/docs/hip/index.md +++ /dev/null @@ -1,25 +0,0 @@ -# Hanzo Improvement Proposals (HIPs) - -Design documents for major architectural decisions in the Hanzo ecosystem. - -## Active HIPs - -| HIP | Title | Status | Description | -|-----|-------|--------|-------------| -| [HIP-0300](HIP-0300.md) | Unified MCP Tools Architecture | Draft | Consolidates 52+ tools into ~16 orthogonal operators | -| [HIP-0301](HIP-0301.md) | High-Performance Agent Communication | Draft | Cap'n Proto for sub-ms agent-to-agent communication | - -## HIP Process - -1. **Draft**: Initial proposal with design rationale -2. **Review**: Community feedback and iteration -3. **Accepted**: Approved for implementation -4. **Final**: Fully implemented and documented - -## Contributing - -To propose a new HIP: - -1. Create a new markdown file: `HIP-XXXX.md` -2. Follow the template structure (Abstract, Motivation, Design, Implementation) -3. Submit a PR for review diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 2277b5e9a..000000000 --- a/docs/index.md +++ /dev/null @@ -1,144 +0,0 @@ -# Hanzo Python SDK - -The complete Python SDK for building AI applications with Hanzo AI infrastructure. - -## Overview - -The Hanzo Python SDK provides: - -- **Agent SDK** (`hanzo-agent`) - Build agentic AI applications with a lightweight, production-ready framework -- **MCP Server** (`hanzo-mcp`) - Model Context Protocol server with 30+ tools for AI code assistants -- **Tool Packages** (`hanzo-tools-*`) - Modular tool packages for file operations, shell commands, browser automation, and more - -## Installation - -=== "Full Install" - ```bash - pip install hanzo-mcp[tools-all] - ``` - -=== "Agent SDK Only" - ```bash - pip install hanzo-agent - ``` - -=== "Using uv" - ```bash - uv pip install hanzo-mcp - ``` - -## Quick Start - -### Using with Claude Code - -The easiest way to use Hanzo MCP is with Claude Code: - -```bash -# Install globally -uvx hanzo-mcp - -# Or run directly -uvx hanzo-mcp --transport stdio -``` - -### Agent SDK Example - -```python -from agents import Agent, Runner - -agent = Agent( - name="assistant", - instructions="You are a helpful assistant." -) - -result = Runner.run_sync(agent, "Hello!") -print(result.final_output) -``` - -### MCP Tools Example - -```python -from hanzo_mcp import create_mcp_server - -# Create server with all tools -server = create_mcp_server() -server.run() -``` - -## Packages - -| Package | Description | PyPI | -|---------|-------------|------| -| `hanzo-mcp` | MCP server with all tools | [![PyPI](https://img.shields.io/pypi/v/hanzo-mcp)](https://pypi.org/project/hanzo-mcp/) | -| `hanzo-agent` | Agent SDK | [![PyPI](https://img.shields.io/pypi/v/hanzo-agent)](https://pypi.org/project/hanzo-agent/) | -| `hanzo-tools-shell` | Shell/command tools | [![PyPI](https://img.shields.io/pypi/v/hanzo-tools-shell)](https://pypi.org/project/hanzo-tools-shell/) | -| `hanzo-tools-fs` | File system tools | [![PyPI](https://img.shields.io/pypi/v/hanzo-tools-fs)](https://pypi.org/project/hanzo-tools-fs/) | -| `hanzo-tools-browser` | Browser automation | [![PyPI](https://img.shields.io/pypi/v/hanzo-tools-browser)](https://pypi.org/project/hanzo-tools-browser/) | -| `hanzo-tools-memory` | Memory/knowledge tools | [![PyPI](https://img.shields.io/pypi/v/hanzo-tools-memory)](https://pypi.org/project/hanzo-tools-memory/) | - -## Features - -### 30+ MCP Tools - -- **File System**: `read`, `write`, `edit`, `tree`, `find`, `search`, `ast` -- **Shell**: `cmd`, `zsh`, `bash`, `ps`, `npx`, `uvx`, `open` -- **Browser**: Full Playwright automation with 70+ actions -- **Memory**: Unified memory tool with recall, create, update, delete -- **Reasoning**: `think`, `critic` for structured AI reasoning -- **LSP**: Go-to-definition, find references, rename, hover -- **Refactor**: Advanced code refactoring with AST support -- **Agent**: Run external AI agents (Claude, Gemini, Codex, etc.) -- **LLM**: Direct LLM access and multi-model consensus - -### Auto-Backgrounding - -Long-running commands automatically background after 30 seconds: - -```python -# This will auto-background if it takes too long -cmd("npm install") - -# Check status -ps() # List all background processes -ps(logs="cmd_xxx") # View output -ps(kill="cmd_xxx") # Stop process -``` - -### Multi-Backend Support - -Use the MCP with any AI assistant: - -- **Claude Code** - `uvx hanzo-mcp` -- **VS Code/Cursor** - Hanzo extension with auto-detection -- **Antigravity** - Full integration via Open VSX -- **Any MCP Client** - Standard MCP protocol - -## Documentation - -### Getting Started - -- [Quickstart Guide](getting-started/quickstart.md) -- [MCP Quickstart](mcp/quickstart.md) -- [MCP Configuration](mcp/configuration.md) - -### Reference - -- [Tools Reference](tools/index.md) - All 30+ tool packages -- [Agent Reference](ref/agent/index.md) - Agent SDK documentation -- [MCP Reference](ref/mcp/index.md) - MCP server documentation -- [Core Libraries](lib/index.md) - Supporting packages (async, consensus, network) - -### Tool Categories - -- [Filesystem Tools](tools/fs.md) - File operations and AST analysis -- [Shell Tools](tools/shell.md) - Command execution and process management -- [Browser Tool](tools/browser.md) - Playwright automation -- [Memory Tools](tools/memory.md) - Persistent memory and knowledge bases -- [Reasoning Tools](tools/reasoning.md) - Structured thinking and analysis - -## Links - -- [GitHub Repository](https://github.com/hanzoai/python-sdk) -- [PyPI Package](https://pypi.org/project/hanzo-mcp/) -- [Discord Community](https://discord.gg/hanzo) -- [Hanzo AI](https://hanzo.ai) diff --git a/docs/lib/aci.md b/docs/lib/aci.md deleted file mode 100644 index b11f607a2..000000000 --- a/docs/lib/aci.md +++ /dev/null @@ -1,260 +0,0 @@ -# hanzo-aci - -Agent-Computer Interface (ACI) designed for software development agents. - -## Installation - -```bash -pip install hanzo-aci -``` - -## Overview - -hanzo-aci provides a comprehensive interface for AI agents to interact with codebases, enabling: - -- **Code Understanding** - AST parsing with tree-sitter -- **Repository Analysis** - Git integration and file operations -- **Semantic Search** - Code search and pattern matching -- **Diff Generation** - Create and apply patches - -## Features - -### AST Parsing - -Parse code into abstract syntax trees using tree-sitter: - -```python -from dev_aci import parse_code - -# Parse Python code -ast = parse_code(""" -def hello(name: str) -> str: - return f"Hello, {name}!" -""", language="python") - -# Get functions -functions = ast.get_functions() -for func in functions: - print(f"{func.name}: {func.line_number}") -``` - -### Supported Languages - -| Language | tree-sitter Package | -|----------|---------------------| -| Python | `tree-sitter-python` | -| JavaScript | `tree-sitter-javascript` | -| TypeScript | `tree-sitter-typescript` | -| Ruby | `tree-sitter-ruby` | - -### Repository Analysis - -Work with Git repositories: - -```python -from dev_aci import Repository - -# Open repository -repo = Repository("/path/to/repo") - -# Get file tree -tree = repo.get_tree() - -# Get file content -content = repo.read_file("src/main.py") - -# Get recent changes -changes = repo.get_recent_commits(limit=10) - -# Search for patterns -matches = repo.search("TODO") -``` - -### Code Search - -Find code patterns: - -```python -from dev_aci import CodeSearch - -search = CodeSearch("/path/to/repo") - -# Search by pattern -results = search.find_pattern("class.*Service") - -# Search by function name -funcs = search.find_functions("handle_*") - -# Search by reference -refs = search.find_references("MyClass") -``` - -### Diff Operations - -Generate and apply patches: - -```python -from dev_aci import DiffGenerator - -diff = DiffGenerator() - -# Generate diff -patch = diff.create_patch( - original="def foo(): pass", - modified="def foo():\n return 42", -) - -# Apply patch -result = diff.apply_patch(content, patch) -``` - -## Configuration - -### Dependencies - -hanzo-aci includes these dependencies: - -- **numpy, pandas, scipy** - Data processing -- **networkx** - Graph analysis -- **llm** - LLM integration -- **gitpython** - Git operations -- **tree-sitter** - AST parsing -- **grep-ast** - Code search - -### Optional Dependencies - -```bash -# Development tools -pip install hanzo-aci[dev] - -# Testing tools -pip install hanzo-aci[test] -``` - -## Usage Examples - -### Agent Code Understanding - -```python -from dev_aci import Repository, CodeSearch - -async def understand_codebase(repo_path: str): - repo = Repository(repo_path) - search = CodeSearch(repo_path) - - # Get structure - tree = repo.get_tree() - - # Find entry points - main_files = search.find_pattern("if __name__ == .__main__.") - - # Find tests - test_files = search.find_functions("test_*") - - return { - "structure": tree, - "entry_points": main_files, - "test_files": test_files, - } -``` - -### Code Modification Agent - -```python -from dev_aci import Repository, DiffGenerator, parse_code - -async def modify_code(repo_path: str, file_path: str, modification: str): - repo = Repository(repo_path) - diff = DiffGenerator() - - # Read original - original = repo.read_file(file_path) - - # Parse and understand - ast = parse_code(original, language="python") - - # Generate modification - modified = apply_modification(original, modification) - - # Create patch - patch = diff.create_patch(original, modified) - - return patch -``` - -### Code Review Agent - -```python -from dev_aci import Repository, CodeSearch, parse_code - -async def review_code(repo_path: str, file_path: str): - repo = Repository(repo_path) - - # Get file content - content = repo.read_file(file_path) - - # Parse AST - ast = parse_code(content, language="python") - - # Analyze - issues = [] - - # Check function length - for func in ast.get_functions(): - if func.line_count > 50: - issues.append(f"Function {func.name} is too long ({func.line_count} lines)") - - # Check for TODOs - search = CodeSearch(repo_path) - todos = search.find_pattern("TODO|FIXME", file=file_path) - if todos: - issues.append(f"Found {len(todos)} TODO/FIXME comments") - - return issues -``` - -## API Reference - -### Repository - -| Method | Description | -|--------|-------------| -| `get_tree()` | Get file tree structure | -| `read_file(path)` | Read file content | -| `write_file(path, content)` | Write file content | -| `get_recent_commits(limit)` | Get recent commits | -| `search(pattern)` | Search repository | - -### CodeSearch - -| Method | Description | -|--------|-------------| -| `find_pattern(pattern)` | Search by regex pattern | -| `find_functions(pattern)` | Find function definitions | -| `find_classes(pattern)` | Find class definitions | -| `find_references(name)` | Find symbol references | - -### DiffGenerator - -| Method | Description | -|--------|-------------| -| `create_patch(original, modified)` | Generate diff patch | -| `apply_patch(content, patch)` | Apply patch to content | -| `validate_patch(patch)` | Validate patch format | - -### AST Functions - -| Function | Description | -|----------|-------------| -| `parse_code(code, language)` | Parse code into AST | -| `get_functions()` | Get function definitions | -| `get_classes()` | Get class definitions | -| `get_imports()` | Get import statements | - -## Best Practices - -1. **Use AST for Analysis**: Prefer AST over regex for code analysis -2. **Cache Repositories**: Reuse Repository instances for efficiency -3. **Validate Patches**: Always validate patches before applying -4. **Handle Errors**: Code parsing can fail - handle ParseError exceptions -5. **Language Detection**: Use file extensions to detect language automatically diff --git a/docs/lib/async.md b/docs/lib/async.md deleted file mode 100644 index dd1b03941..000000000 --- a/docs/lib/async.md +++ /dev/null @@ -1,209 +0,0 @@ -# hanzo-async - -High-performance async I/O for Hanzo AI with automatic uvloop configuration. - -## Installation - -```bash -pip install hanzo-async -``` - -## Features - -- **Automatic uvloop** - 2-4x faster async on macOS/Linux -- **Async file I/O** - Non-blocking file operations -- **Async subprocess** - Non-blocking command execution -- **Consistent API** - Used across all Hanzo packages - -## Quick Start - -```python -from hanzo_async import ( - read_file, write_file, - path_exists, mkdir, - run_command, - using_uvloop, -) - -# Check if using high-performance backend -if using_uvloop(): - print("Using uvloop for 2-4x faster async") - -# Async file operations -content = await read_file("/path/to/file.txt") -await write_file("/path/to/output.txt", "content") - -# Async path operations -if await path_exists("/path/to/dir"): - ... - -# Async subprocess -stdout, stderr, code = await run_command("ls", "-la") -``` - -## Loop Configuration - -```python -from hanzo_async import configure_loop, using_uvloop, get_loop - -# Configure on import (automatic) -configure_loop() - -# Check backend -if using_uvloop(): - print("uvloop active") -else: - print("Using asyncio (Windows or uvloop not installed)") - -# Get current loop -loop = get_loop() -``` - -## File Operations - -### Reading Files - -```python -from hanzo_async import read_file, read_json, read_lines - -# Read text file -content = await read_file("/path/to/file.txt") - -# Read JSON file -data = await read_json("/path/to/config.json") - -# Read lines -lines = await read_lines("/path/to/file.txt") -``` - -### Writing Files - -```python -from hanzo_async import write_file, write_json, write_lines, append_file - -# Write text file -await write_file("/path/to/file.txt", "content") - -# Write JSON file -await write_json("/path/to/config.json", {"key": "value"}) - -# Write lines -await write_lines("/path/to/file.txt", ["line1", "line2"]) - -# Append to file -await append_file("/path/to/log.txt", "new entry\n") -``` - -## Path Operations - -```python -from hanzo_async import ( - path_exists, is_file, is_dir, - mkdir, rmdir, unlink, - stat, listdir, glob, -) - -# Check existence -if await path_exists("/path"): - ... - -# Check type -if await is_file("/path/file.txt"): - ... -if await is_dir("/path/dir"): - ... - -# Create directory -await mkdir("/path/new/dir", parents=True, exist_ok=True) - -# Remove -await unlink("/path/file.txt") # Remove file -await rmdir("/path/dir") # Remove directory - -# List directory -files = await listdir("/path/dir") - -# Glob pattern -matches = await glob("/path/**/*.py") - -# Get file info -info = await stat("/path/file.txt") -print(f"Size: {info.st_size}, Modified: {info.st_mtime}") -``` - -## Process Operations - -```python -from hanzo_async import run_command, run_shell, check_command - -# Run command with arguments -stdout, stderr, code = await run_command("git", "status") - -# Run shell command -stdout, stderr, code = await run_shell("ls -la | grep .py") - -# Check if command succeeds (raises on failure) -await check_command("make", "build") -``` - -## API Reference - -### Loop Functions - -| Function | Description | -|----------|-------------| -| `configure_loop()` | Configure uvloop (automatic on import) | -| `using_uvloop()` | Check if uvloop is active | -| `get_loop()` | Get current event loop | - -### File Functions - -| Function | Description | -|----------|-------------| -| `read_file(path)` | Read file as string | -| `read_json(path)` | Read and parse JSON file | -| `read_lines(path)` | Read file as list of lines | -| `write_file(path, content)` | Write string to file | -| `write_json(path, data)` | Write data as JSON | -| `write_lines(path, lines)` | Write list of lines | -| `append_file(path, content)` | Append to file | - -### Path Functions - -| Function | Description | -|----------|-------------| -| `path_exists(path)` | Check if path exists | -| `is_file(path)` | Check if path is a file | -| `is_dir(path)` | Check if path is a directory | -| `mkdir(path, ...)` | Create directory | -| `rmdir(path)` | Remove directory | -| `unlink(path)` | Remove file | -| `stat(path)` | Get file information | -| `listdir(path)` | List directory contents | -| `glob(pattern)` | Find files matching pattern | - -### Process Functions - -| Function | Description | -|----------|-------------| -| `run_command(*args)` | Run command with arguments | -| `run_shell(cmd)` | Run shell command | -| `check_command(*args)` | Run and raise on failure | - -## Platform Support - -| Platform | Backend | Performance | -|----------|---------|-------------| -| macOS | uvloop | 2-4x faster | -| Linux | uvloop | 2-4x faster | -| Windows | asyncio | Standard | - -## Why uvloop? - -uvloop is a fast, drop-in replacement for asyncio's event loop that uses libuv under the hood. It provides: - -- **2-4x faster** async operations -- **Lower latency** for I/O bound tasks -- **Better scalability** under high concurrency - -The hanzo-async package automatically configures uvloop when available, falling back to asyncio on Windows or when uvloop isn't installed. diff --git a/docs/lib/consensus.md b/docs/lib/consensus.md deleted file mode 100644 index 6ce6407c9..000000000 --- a/docs/lib/consensus.md +++ /dev/null @@ -1,285 +0,0 @@ -# hanzo-consensus - -Metastable consensus protocol for multi-agent agreement. - -## Installation - -```bash -pip install hanzo-consensus -``` - -## Overview - -The metastable consensus protocol enables multiple AI agents to reach agreement through a two-phase process: - -1. **Phase I (Sampling)**: Agents propose responses and refine based on peer feedback -2. **Phase II (Finality)**: Agreement threshold determines winner and synthesis - -Reference: [github.com/luxfi/consensus](https://github.com/luxfi/consensus) - -## Quick Start - -```python -from hanzo_consensus import Consensus, run, State, Result - -# Define execution function -async def execute(agent_id: str, prompt: str) -> Result: - # Call your LLM here - response = await call_llm(agent_id, prompt) - return Result( - id=agent_id, - output=response, - ok=True, - ms=100 # Response time in ms - ) - -# Run consensus -state = await run( - prompt="What's the best approach for error handling?", - participants=["gpt-4", "claude-3", "gemini"], - execute=execute, - rounds=3, - k=3, - alpha=0.6, - beta_1=0.5, - beta_2=0.8, -) - -print(f"Winner: {state.winner}") -print(f"Finalized: {state.finalized}") -print(f"Synthesis: {state.synthesis}") -``` - -## Protocol Parameters - -| Parameter | Default | Description | -|-----------|---------|-------------| -| `rounds` | 3 | Number of sampling rounds | -| `k` | 3 | Sample size per round | -| `alpha` | 0.6 | Agreement threshold (0-1) | -| `beta_1` | 0.5 | Preference threshold (Phase I) | -| `beta_2` | 0.8 | Decision threshold (Phase II) | - -### Parameter Tuning - -**Higher `rounds`**: More refinement, better consensus, slower -```python -run(..., rounds=5) # More thorough consensus -``` - -**Higher `k`**: More peers sampled, broader agreement -```python -run(..., k=5) # Sample more peers per round -``` - -**Higher `beta_2`**: Stricter finality requirement -```python -run(..., beta_2=0.9) # Require strong agreement -``` - -## Classes - -### Result - -Represents a participant's response: - -```python -@dataclass -class Result: - id: str # Participant ID - output: str # Response text - ok: bool # Success flag - error: Optional[str] # Error message if failed - ms: int = 0 # Response time (affects luminance) - round: int = 0 # Round number -``` - -### State - -Consensus state throughout the protocol: - -```python -@dataclass -class State: - prompt: str # Original query - participants: List[str] # Participant IDs - rounds: int # Total rounds - k: int # Sample size - alpha: float # Agreement threshold - beta_1: float # Preference threshold - beta_2: float # Decision threshold - - # State (updated during protocol) - responses: Dict[str, List[str]] # Responses per participant - confidence: Dict[str, float] # Confidence scores - luminance: Dict[str, float] # Performance scores - finalized: bool # Whether consensus reached - winner: Optional[str] # Winning participant - synthesis: Optional[str] # Final synthesized response -``` - -### Consensus - -Main consensus class: - -```python -consensus = Consensus( - participants=["agent-1", "agent-2", "agent-3"], - execute=my_execute_fn, - rounds=3, - k=3, - alpha=0.6, - beta_1=0.5, - beta_2=0.8, -) - -state = await consensus.run("Your question here") -``` - -## MCP Mesh Integration - -For MCP-based agents, use the MCPMesh: - -```python -from hanzo_consensus import MCPMesh, MCPAgent, run_mcp_consensus - -# Create MCP agents -agents = [ - MCPAgent(id="agent-1", endpoint="http://localhost:8001"), - MCPAgent(id="agent-2", endpoint="http://localhost:8002"), -] - -# Create mesh -mesh = MCPMesh(agents) - -# Run consensus through MCP -state = await run_mcp_consensus( - mesh=mesh, - prompt="Design an API for user authentication", - rounds=3, -) -``` - -## Protocol Flow - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ PHASE I: SAMPLING โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ Round 0: Initial Proposals โ”‚ -โ”‚ Agent-1 โ”€โ”€โ”€โ–บ "Use try/except blocks..." โ”‚ -โ”‚ Agent-2 โ”€โ”€โ”€โ–บ "Implement error types..." โ”‚ -โ”‚ Agent-3 โ”€โ”€โ”€โ–บ "Use Result types..." โ”‚ -โ”‚ โ”‚ -โ”‚ Round 1-N: Refinement โ”‚ -โ”‚ Sample k peers (weighted by luminance) โ”‚ -โ”‚ Share peer responses as context โ”‚ -โ”‚ Each agent refines response โ”‚ -โ”‚ Update confidence scores โ”‚ -โ”‚ โ”‚ -โ”‚ if max(confidence) >= ฮฒโ‚: proceed to Phase II โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ PHASE II: FINALITY โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ Calculate final scores: confidence ร— luminance โ”‚ -โ”‚ Winner = agent with highest score โ”‚ -โ”‚ if score >= ฮฒโ‚‚: finalized = True โ”‚ -โ”‚ Synthesis = winner's final response โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## Key Concepts - -### Luminance - -Luminance measures agent performance based on response time: - -```python -luminance = 1.0 / (1.0 + response_time_ms / 1000.0) -``` - -Faster agents get higher luminance, increasing their influence in peer sampling. - -### Confidence - -Confidence measures agreement with sampled peers: - -```python -confidence = previous_confidence * 0.5 + current_agreement * 0.5 -``` - -Agreement is calculated using word overlap (Jaccard similarity). - -### Finalization - -Consensus is finalized when the winner's combined score exceeds ฮฒโ‚‚: - -```python -score = confidence * luminance -finalized = score >= beta_2 -``` - -## Example: Multi-LLM Consensus - -```python -import asyncio -from hanzo_consensus import run, Result - -async def call_openai(prompt: str) -> str: - # Your OpenAI API call - ... - -async def call_anthropic(prompt: str) -> str: - # Your Anthropic API call - ... - -async def call_google(prompt: str) -> str: - # Your Google API call - ... - -PROVIDERS = { - "openai": call_openai, - "anthropic": call_anthropic, - "google": call_google, -} - -async def execute(agent_id: str, prompt: str) -> Result: - import time - start = time.time() - try: - output = await PROVIDERS[agent_id](prompt) - return Result( - id=agent_id, - output=output, - ok=True, - ms=int((time.time() - start) * 1000), - ) - except Exception as e: - return Result( - id=agent_id, - output="", - ok=False, - error=str(e), - ) - -async def main(): - state = await run( - prompt="What's the most efficient way to sort a list in Python?", - participants=list(PROVIDERS.keys()), - execute=execute, - ) - - print(f"Consensus reached: {state.finalized}") - print(f"Winner: {state.winner}") - print(f"Answer: {state.synthesis}") - -asyncio.run(main()) -``` - -## Best Practices - -1. **Diverse Participants**: Use different LLM providers for better consensus -2. **Appropriate Rounds**: Start with 3 rounds, increase for complex queries -3. **Tune Thresholds**: Lower ฮฒโ‚‚ for faster consensus, higher for stricter agreement -4. **Handle Failures**: Always check `result.ok` in your execute function -5. **Monitor Latency**: Response time affects luminance and peer selection diff --git a/docs/lib/index.md b/docs/lib/index.md deleted file mode 100644 index f1157655a..000000000 --- a/docs/lib/index.md +++ /dev/null @@ -1,21 +0,0 @@ -# Core Libraries - -This section documents the foundational utility packages that power the Hanzo AI ecosystem. - -## Overview - -| Package | Description | Install | -|---------|-------------|---------| -| `hanzo-async` | High-performance async I/O | `pip install hanzo-async` | -| `hanzo-consensus` | Metastable consensus protocol | `pip install hanzo-consensus` | -| `hanzo-network` | Agent network orchestration | `pip install hanzo-network` | -| `hanzo-aci` | Agent-Computer Interface | `pip install hanzo-aci` | -| `hanzo-repl` | Interactive REPL | `pip install hanzo-repl` | - -## Quick Links - -- [Async I/O](async.md) - Unified async operations with uvloop -- [Consensus](consensus.md) - Multi-agent agreement protocol -- [Network](network.md) - Distributed agent orchestration -- [ACI](aci.md) - Agent-Computer Interface for development agents -- [REPL](repl.md) - Interactive command-line interface diff --git a/docs/lib/network.md b/docs/lib/network.md deleted file mode 100644 index 53dbe4428..000000000 --- a/docs/lib/network.md +++ /dev/null @@ -1,284 +0,0 @@ -# hanzo-network - -Agent network orchestration for distributed AI systems. - -## Installation - -```bash -pip install hanzo-network -``` - -## Overview - -hanzo-network enables building distributed agent networks with: - -- **Topology Management** - Define and manage agent network structures -- **Device Capabilities** - Track compute resources across nodes -- **Partitioning Strategies** - Distribute work across agents -- **Local LLM Support** - Run models locally for development - -## Quick Start - -```python -from hanzo_network.core import AgentNetwork, NetworkConfig -from hanzo_network.topology import RingMemoryWeightedPartitioningStrategy - -# Create network -config = NetworkConfig( - name="my-network", - strategy=RingMemoryWeightedPartitioningStrategy(), -) -network = AgentNetwork(config) - -# Add agents -network.add_agent("agent-1", capabilities={"memory": 16, "gpu": True}) -network.add_agent("agent-2", capabilities={"memory": 8, "gpu": False}) - -# Route work -result = await network.route("Process this complex task") -``` - -## Core Components - -### AgentNetwork - -The main network orchestrator: - -```python -from hanzo_network.core import AgentNetwork, NetworkConfig - -# Configuration -config = NetworkConfig( - name="production-network", - max_agents=100, - timeout=30.0, - retry_count=3, -) - -# Create network -network = AgentNetwork(config) - -# Add agents -network.add_agent( - agent_id="worker-1", - endpoint="http://localhost:8001", - capabilities={"memory": 32, "gpu": True, "cores": 8} -) - -# Remove agents -network.remove_agent("worker-1") - -# List agents -agents = network.list_agents() - -# Get agent status -status = network.get_status("worker-1") -``` - -### Router - -Route requests to appropriate agents: - -```python -from hanzo_network.core import Router, RoutingStrategy - -# Create router -router = Router(strategy=RoutingStrategy.ROUND_ROBIN) - -# Route request -agent = router.route("Process this task") - -# Available strategies -RoutingStrategy.ROUND_ROBIN # Cycle through agents -RoutingStrategy.LEAST_LOADED # Pick agent with lowest load -RoutingStrategy.CAPABILITY_MATCH # Match task to capabilities -RoutingStrategy.RANDOM # Random selection -``` - -## Topology - -### Device Capabilities - -Track and match device capabilities: - -```python -from hanzo_network.topology import DeviceCapabilities - -# Define capabilities -caps = DeviceCapabilities( - memory_gb=32, - gpu_memory_gb=24, - cpu_cores=16, - gpu_available=True, - gpu_model="RTX 4090", -) - -# Check if capable -can_run = caps.can_handle( - min_memory=16, - requires_gpu=True, -) -``` - -### Partitioning Strategies - -Distribute work across the network: - -```python -from hanzo_network.topology import ( - RingMemoryWeightedPartitioningStrategy, - PartitioningStrategy, -) - -# Memory-weighted ring partitioning -strategy = RingMemoryWeightedPartitioningStrategy() - -# Partition data -partitions = strategy.partition( - data=large_dataset, - agents=network.list_agents(), -) - -# Process partitions -for agent_id, partition in partitions.items(): - await network.send(agent_id, partition) -``` - -## Local LLM - -Run models locally for development: - -```python -from hanzo_network.llm import LocalLLM - -# Create local LLM -llm = LocalLLM( - model_path="/path/to/model", - context_size=4096, -) - -# Generate response -response = await llm.generate( - prompt="Explain quantum computing", - max_tokens=500, -) -``` - -## Tools - -### Memory Tool - -Shared memory across the network: - -```python -from hanzo_network.tools import MemoryTool - -memory = MemoryTool(network) - -# Store data -await memory.store("key", {"data": "value"}) - -# Retrieve data -data = await memory.retrieve("key") - -# Search -results = await memory.search("query") -``` - -## Examples - -### Distributed Demo - -```python -from hanzo_network.core import AgentNetwork, NetworkConfig -from hanzo_network.topology import DeviceCapabilities - -async def main(): - # Create network - network = AgentNetwork(NetworkConfig(name="distributed")) - - # Add workers with capabilities - for i in range(4): - caps = DeviceCapabilities( - memory_gb=16 + i * 8, - gpu_available=i % 2 == 0, - ) - network.add_agent(f"worker-{i}", capabilities=caps) - - # Distribute task - task = "Process large dataset" - results = await network.broadcast(task) - - # Aggregate results - final = aggregate(results) - return final -``` - -### Local LLM Demo - -```python -from hanzo_network.llm import LocalLLM - -async def main(): - # Setup local model - llm = LocalLLM(model_path="./models/llama-7b") - - # Development queries - response = await llm.generate("Write a Python function to sort a list") - print(response) -``` - -## Configuration - -### Environment Variables - -| Variable | Default | Description | -|----------|---------|-------------| -| `HANZO_NETWORK_TIMEOUT` | `30` | Request timeout in seconds | -| `HANZO_NETWORK_RETRIES` | `3` | Max retry attempts | -| `HANZO_NETWORK_LOG_LEVEL` | `INFO` | Logging level | - -### Network Config - -```python -NetworkConfig( - name="my-network", # Network identifier - max_agents=100, # Maximum agents - timeout=30.0, # Request timeout - retry_count=3, # Retry attempts - health_check_interval=60, # Health check frequency - load_balancing=True, # Enable load balancing -) -``` - -## Architecture - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Agent Network โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Router โ”‚ โ”‚ Topology โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ Manager โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Partitioning Strategy โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Agent Pool โ”‚ โ”‚ -โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚Agent 1 โ”‚ โ”‚Agent 2 โ”‚ โ”‚Agent N โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## Best Practices - -1. **Capability Matching**: Define accurate device capabilities for optimal routing -2. **Health Checks**: Enable health checks to detect failed agents -3. **Retry Logic**: Configure retries for transient failures -4. **Load Balancing**: Use load-aware routing for production -5. **Local Development**: Use LocalLLM for testing without network overhead diff --git a/docs/lib/repl.md b/docs/lib/repl.md deleted file mode 100644 index e0399a949..000000000 --- a/docs/lib/repl.md +++ /dev/null @@ -1,321 +0,0 @@ -# hanzo-repl - -Interactive REPL (Read-Eval-Print Loop) for Hanzo AI. - -## Installation - -```bash -pip install hanzo-repl -``` - -## Overview - -hanzo-repl provides an interactive command-line interface for AI agents with: - -- **Multiple Backends** - IPython, Textual, and standard REPL -- **Voice Mode** - Speech-to-text input -- **Command Palette** - Quick access to commands -- **Command Suggestions** - Context-aware suggestions -- **LLM Integration** - Direct AI assistant access - -## Quick Start - -```bash -# Start the REPL -hanzo-repl - -# Or with Python -python -m hanzo_repl -``` - -## Features - -### Standard REPL - -```python -from hanzo_repl import REPL - -# Create and run REPL -repl = REPL() -repl.run() -``` - -### IPython Backend - -Enhanced REPL with IPython features: - -```python -from hanzo_repl import IPythonREPL - -repl = IPythonREPL() -repl.run() -``` - -Features: -- Tab completion -- Syntax highlighting -- Magic commands -- History persistence - -### Textual Backend - -Modern TUI (Text User Interface) REPL: - -```python -from hanzo_repl import TextualREPL - -repl = TextualREPL() -repl.run() -``` - -Features: -- Rich UI components -- Multiple panes -- Mouse support -- Keyboard shortcuts - -## Voice Mode - -Enable voice input: - -```python -from hanzo_repl import REPL, VoiceMode - -repl = REPL() -voice = VoiceMode() - -# Enable voice -repl.enable_voice(voice) - -# Start listening -repl.run() -``` - -### Voice Commands - -| Command | Action | -|---------|--------| -| "Hey Hanzo" | Activate voice input | -| "Stop" | Cancel current input | -| "Execute" | Run current command | -| "Clear" | Clear screen | - -## Command Palette - -Quick access to common commands: - -```python -from hanzo_repl import CommandPalette - -palette = CommandPalette() - -# Add custom commands -palette.add_command("build", "npm run build", category="dev") -palette.add_command("test", "pytest", category="dev") - -# Open palette (Ctrl+Shift+P) -palette.show() -``` - -### Default Commands - -| Category | Commands | -|----------|----------| -| File | Open, Save, Close | -| Edit | Cut, Copy, Paste, Undo | -| View | Zoom In, Zoom Out, Toggle Panel | -| Git | Status, Commit, Push, Pull | -| Run | Execute, Debug, Profile | - -## Command Suggestions - -Context-aware suggestions: - -```python -from hanzo_repl import CommandSuggestions - -suggestions = CommandSuggestions() - -# Get suggestions based on context -ctx = {"language": "python", "file": "main.py"} -sugg = suggestions.get(ctx) - -# Returns relevant commands like: -# - python main.py -# - pytest -# - black main.py -``` - -## LLM Integration - -Direct AI assistant access: - -```python -from hanzo_repl import LLMClient - -client = LLMClient() - -# Ask the AI -response = await client.ask("How do I sort a list in Python?") -print(response) - -# With context -response = await client.ask( - "Explain this code", - context=code_snippet, -) -``` - -### Configuration - -```python -LLMClient( - model="gpt-4", - temperature=0.7, - max_tokens=1000, -) -``` - -## CLI Options - -```bash -# Choose backend -hanzo-repl --backend ipython -hanzo-repl --backend textual -hanzo-repl --backend standard - -# Enable voice -hanzo-repl --voice - -# Set LLM model -hanzo-repl --model claude-3 - -# Debug mode -hanzo-repl --debug -``` - -## Keyboard Shortcuts - -### Navigation - -| Shortcut | Action | -|----------|--------| -| `Ctrl+C` | Cancel/interrupt | -| `Ctrl+D` | Exit REPL | -| `Ctrl+L` | Clear screen | -| `Ctrl+R` | Search history | -| `Tab` | Complete command | - -### Editing - -| Shortcut | Action | -|----------|--------| -| `Ctrl+A` | Beginning of line | -| `Ctrl+E` | End of line | -| `Ctrl+K` | Kill to end of line | -| `Ctrl+U` | Kill to start of line | -| `Ctrl+W` | Delete word backward | - -### Command Palette - -| Shortcut | Action | -|----------|--------| -| `Ctrl+Shift+P` | Open command palette | -| `Ctrl+P` | Quick open file | -| `Ctrl+Shift+F` | Search in files | - -## Configuration - -### Config File - -Create `~/.hanzo/repl.yaml`: - -```yaml -backend: ipython -voice: - enabled: true - wake_word: "hey hanzo" -llm: - model: claude-3 - temperature: 0.7 -theme: dark -history: - size: 10000 - file: ~/.hanzo/history -``` - -### Environment Variables - -| Variable | Description | -|----------|-------------| -| `HANZO_REPL_BACKEND` | Default backend | -| `HANZO_REPL_VOICE` | Enable voice (true/false) | -| `HANZO_REPL_MODEL` | LLM model | -| `HANZO_REPL_THEME` | Color theme | - -## Custom Commands - -Register custom commands: - -```python -from hanzo_repl import REPL, command - -@command("hello") -def hello_command(args): - """Say hello""" - name = args.get("name", "World") - print(f"Hello, {name}!") - -repl = REPL() -repl.register_command(hello_command) -repl.run() -``` - -## Plugins - -Extend the REPL with plugins: - -```python -from hanzo_repl import Plugin, REPL - -class MyPlugin(Plugin): - name = "my-plugin" - - def on_load(self, repl): - print("Plugin loaded!") - - def on_command(self, cmd): - # Process commands - pass - -repl = REPL() -repl.load_plugin(MyPlugin()) -``` - -## API Reference - -### REPL Class - -| Method | Description | -|--------|-------------| -| `run()` | Start the REPL | -| `eval(code)` | Evaluate code | -| `register_command(cmd)` | Add custom command | -| `enable_voice(mode)` | Enable voice input | -| `load_plugin(plugin)` | Load a plugin | - -### LLMClient Class - -| Method | Description | -|--------|-------------| -| `ask(prompt, context)` | Ask the AI | -| `stream(prompt)` | Stream response | -| `set_model(model)` | Change model | - -### VoiceMode Class - -| Method | Description | -|--------|-------------| -| `start()` | Start listening | -| `stop()` | Stop listening | -| `set_wake_word(word)` | Set activation word | diff --git a/docs/lib/source.ts b/docs/lib/source.ts deleted file mode 100644 index b6fa63460..000000000 --- a/docs/lib/source.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { docs } from '@/.source/server'; -import { loader } from 'fumadocs-core/source'; - -export const source = loader(docs.toFumadocsSource(), { - baseUrl: '/docs', -}); diff --git a/docs/mcp/PARITY.md b/docs/mcp/PARITY.md deleted file mode 100644 index 7b0f02fa3..000000000 --- a/docs/mcp/PARITY.md +++ /dev/null @@ -1,236 +0,0 @@ -# MCP Implementation Parity Analysis - -This document analyzes the parity status between Hanzo's three MCP implementations and provides a roadmap for convergence. - -## Architecture Overview - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ VS Code Extension โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Backend Selection โ”‚ โ”‚ -โ”‚ โ”‚ auto โ†’ python (if uvx) โ†’ typescript (fallback) โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ โ”‚ โ”‚ - โ–ผ โ–ผ โ–ผ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Python MCP โ”‚ โ”‚ TypeScript MCP โ”‚ โ”‚ Rust MCP Client โ”‚ -โ”‚ (hanzo-mcp) โ”‚ โ”‚ (@hanzo/mcp) โ”‚ โ”‚ (hanzo-node) โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ 30+ native tools โ”‚ โ”‚ 15+ tools โ”‚ โ”‚ MCP proxy/client โ”‚ -โ”‚ Full features โ”‚ โ”‚ Essential set โ”‚ โ”‚ Connects to others โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## Implementation Comparison - -### Python MCP (`hanzo-mcp`) - Reference Implementation - -**Status: Complete (30+ tools)** - -| Category | Tools | Status | -|----------|-------|--------| -| **File System** | read, write, edit, tree, find, search, ast | โœ… Complete | -| **Shell** | cmd, zsh, bash, ps, npx, uvx, open | โœ… Complete | -| **Browser** | 70+ Playwright actions | โœ… Complete | -| **Memory** | unified memory (recall, create, update, delete, facts) | โœ… Complete | -| **Reasoning** | think, critic | โœ… Complete | -| **LSP** | definition, references, rename, hover | โœ… Complete | -| **Refactor** | rename, extract, inline, move | โœ… Complete | -| **Agent** | agent runner (claude, gemini, codex, etc.), iching, review | โœ… Complete | -| **LLM** | llm, consensus | โœ… Complete | -| **Todo** | unified todo management | โœ… Complete | -| **Config** | config, mode | โœ… Complete | -| **Database** | SQL query/search/stats, graph operations | โœ… Complete | -| **Vector** | index, vector_index, vector_search | โœ… Complete | -| **Jupyter** | notebook read/edit | โœ… Complete | -| **Editor** | neovim integration | โœ… Complete | - -**Key Features:** -- โœ… Auto-backgrounding (30s default, configurable via `HANZO_AUTO_BACKGROUND_TIMEOUT`) -- โœ… DAG execution with parallel support -- โœ… Process management (ps tool) -- โœ… Entry-point based tool discovery -- โœ… Unified action patterns (memory, agent tools) -- โœ… 701 programmer personas - -### TypeScript MCP (`@hanzo/mcp`) - Essential Subset - -**Status: Partial (~15 tools)** - -| Category | Tools | Status | -|----------|-------|--------| -| **File System** | read_file, write_file, list_files, get_file_info, directory_tree | โœ… Complete | -| **Shell** | bash, run_command, run_background, list_processes, get_process_output, kill_process | โœ… Complete | -| **Search** | grep, search | โœ… Complete | -| **Edit** | edit tools | โœ… Complete | -| **Browser** | HanzoDesktopTool, PlaywrightControlTool | โš ๏ธ Basic | -| **Memory** | separate memory tools | โš ๏ธ Not unified | -| **Reasoning** | - | โŒ Missing | -| **UI** | unified-ui, ui-registry, github-ui | โœ… Complete | -| **AutoGUI** | automation tools | โœ… Complete | -| **Orchestration** | agent tools | โš ๏ธ Basic | - -**Features Comparison:** -| Feature | Python | TypeScript | Gap | -|---------|--------|------------|-----| -| Auto-backgrounding | โœ… 30s | โœ… `withAutoTimeout` | Parity | -| DAG execution | โœ… `cmd` tool | โŒ | **Missing** | -| Process management | โœ… Unified `ps` | โš ๏ธ Separate tools | Different API | -| Unified memory | โœ… Single tool | โŒ | **Missing** | -| Think/Critic | โœ… | โŒ | **Missing** | -| Personas | โœ… 701 | โŒ | **Missing** | - -### Rust MCP (`hanzo-node/hanzo-mcp`) - MCP Client/Proxy - -**Status: Different Architecture** - -The Rust implementation is an **MCP client**, not a tool provider. It: - -1. Connects to external MCP servers (via stdio, SSE, HTTP) -2. Lists tools from connected servers -3. Proxies tool calls to those servers - -**Supported Transports:** -- โœ… Stdio (spawns MCP server as subprocess) -- โœ… SSE (Server-Sent Events) -- โœ… HTTP (Streamable HTTP) - -**Use Case:** -- Embedded in `hanzo-node` (blockchain/AI node) -- Orchestrates multiple MCP servers -- Enables decentralized tool execution - -## Parity Roadmap - -### Phase 1: TypeScript Essential Parity (Priority: P0) - -Add missing essential tools to TypeScript MCP: - -1. **`cmd` tool with DAG support** - ```typescript - cmd({ commands: ["npm install", "npm build"], parallel: true }) - cmd({ commands: [ - { id: "install", run: "npm install" }, - { id: "build", run: "npm build", after: ["install"] } - ]}) - ``` - -2. **Unified `ps` tool** - - Consolidate list_processes, get_process_output, kill_process - - Match Python API: `ps()`, `ps({ logs: "id" })`, `ps({ kill: "id" })` - -3. **`think` and `critic` tools** - - Essential for AI reasoning patterns - - Simple implementation, high value - -### Phase 2: TypeScript Feature Parity (Priority: P1) - -1. **Unified `memory` tool** - - Match Python's action-based API - - Actions: recall, create, update, delete, facts, summarize - -2. **Enhanced browser tool** - - Add more Playwright actions - - Match Python's 70+ action coverage - -3. **Agent tool** - - Unified interface for spawning external agents - - Support for Claude, Gemini, Codex, etc. - -### Phase 3: Rust Tool Implementation (Priority: P2) - -Consider adding native Rust tools for performance-critical operations: - -1. **File I/O** - High-performance read/write/search -2. **Process management** - Native subprocess handling -3. **AST parsing** - Tree-sitter based code analysis - -This would allow `hanzo-node` to run standalone without Python dependency. - -## Extension Backend Selection Logic - -The VS Code extension (`~/work/hanzo/extension`) uses this selection: - -```typescript -async selectBackend(backend: string): Promise { - if (backend !== 'auto') return backend; - - // Auto-detect: prefer Python if uvx available - try { - execSync('uvx --version', { stdio: 'ignore' }); - return 'python'; // Full 30+ tools - } catch { - return 'typescript'; // Essential ~15 tools - } -} -``` - -**Backend Capabilities:** - -| Backend | Tools | Use Case | -|---------|-------|----------| -| `python` | 30+ | Full development (recommended) | -| `typescript` | ~15 | Web-first, no Python needed | -| `rust` | (proxy) | High-performance, embedded | -| `local-node` | varies | Decentralized compute | - -## Tool Name Mapping - -For swappable backends, tool names should be consistent: - -| Python | TypeScript | Rust | Action | -|--------|------------|------|--------| -| `read` | `read_file` | - | **Align to `read`** | -| `write` | `write_file` | - | **Align to `write`** | -| `cmd` | `bash` | - | **Add `cmd` to TS** | -| `ps` | `list_processes` | - | **Add unified `ps`** | -| `tree` | `directory_tree` | - | **Align to `tree`** | -| `search` | `grep` | - | Keep both as aliases | - -## Recommended Actions - -### Immediate (This Sprint) - -1. โœ… Extension supports multiple backends (done) -2. โœ… Python MCP is reference (done) -3. โฌœ Document tool name differences -4. โฌœ Create TypeScript `cmd` tool issue - -### Short Term (Next 2 Sprints) - -1. โฌœ Add `cmd` with DAG to TypeScript -2. โฌœ Add `think`/`critic` to TypeScript -3. โฌœ Unify `ps` in TypeScript -4. โฌœ Align tool names (read vs read_file) - -### Medium Term - -1. โฌœ Unified `memory` in TypeScript -2. โฌœ Enhanced browser tool -3. โฌœ Consider Rust native tools - -## Testing Parity - -To ensure backends are swappable, run same tests against all: - -```bash -# Python -uvx hanzo-mcp --test - -# TypeScript -npx @hanzo/mcp --test - -# Via extension (auto-detect) -code --command "hanzo.mcp.test" -``` - -## Conclusion - -The current architecture is sound: -- **Python**: Reference implementation with full features -- **TypeScript**: Essential subset for browser/VS Code -- **Rust**: Client/proxy for orchestration - -The priority is bringing TypeScript to essential parity (cmd, ps, think, critic), not full parity. Users needing all features should use Python backend. diff --git a/docs/mcp/configuration.md b/docs/mcp/configuration.md deleted file mode 100644 index 805e69ca9..000000000 --- a/docs/mcp/configuration.md +++ /dev/null @@ -1,224 +0,0 @@ -# MCP Configuration - -Comprehensive configuration options for hanzo-mcp. - -## Configuration Sources - -Configuration is loaded in order of precedence (highest first): - -1. CLI arguments -2. Environment variables -3. Project config (`.hanzo/config.json`) -4. Global config (`~/.config/hanzo/mcp-settings.json`) -5. Default settings - -## CLI Options - -```bash -hanzo-mcp [OPTIONS] - -Options: - --transport [stdio|sse] Transport mode (default: stdio) - --host TEXT Host for SSE server (default: 127.0.0.1) - --port INTEGER Port for SSE server (default: 8888) - --allow-path PATH Allowed file system paths (repeatable) - --project-path PATH Project paths for context (repeatable) - --project-dir PATH Project root directory - --command-timeout FLOAT Command timeout in seconds (default: 30) - --disable-write-tools Disable file write operations - --disable-search-tools Disable search operations - --install Install Claude Desktop configuration - --dev Enable development mode - --version Show version - --help Show help -``` - -## Environment Variables - -### Core Settings - -| Variable | Default | Description | -|----------|---------|-------------| -| `HANZO_MCP_TRANSPORT` | `stdio` | Transport mode | -| `HANZO_MCP_HOST` | `127.0.0.1` | SSE server host | -| `HANZO_MCP_PORT` | `8888` | SSE server port | -| `HANZO_AUTO_BACKGROUND_TIMEOUT` | `30` | Command auto-background timeout (seconds) | -| `HANZO_DEFAULT_SHELL` | `zsh` | Default shell for commands | - -### Agent Settings - -| Variable | Default | Description | -|----------|---------|-------------| -| `HANZO_AGENT_MODEL` | - | Default model for agent tools | -| `HANZO_AGENT_API_KEY` | - | API key for agent | -| `HANZO_AGENT_BASE_URL` | - | Custom API base URL | -| `HANZO_AGENT_MAX_ITERATIONS` | `10` | Max agent iterations | -| `HANZO_AGENT_MAX_TOOL_USES` | `30` | Max tool uses per run | - -### LLM API Keys - -| Variable | Description | -|----------|-------------| -| `OPENAI_API_KEY` | OpenAI API key | -| `ANTHROPIC_API_KEY` | Anthropic API key | -| `TOGETHER_API_KEY` | Together AI API key | -| `GROQ_API_KEY` | Groq API key | - -## Config File Format - -### Global Config - -Location: `~/.config/hanzo/mcp-settings.json` - -```json -{ - "server": { - "name": "hanzo-mcp", - "host": "127.0.0.1", - "port": 8888, - "transport": "stdio" - }, - "tools": { - "command_timeout": 30, - "disable_write_tools": false, - "disable_search_tools": false - }, - "paths": { - "allowed": ["~"], - "project": [] - }, - "agent": { - "enabled": true, - "model": "claude-3-5-sonnet-20241022", - "max_iterations": 10, - "max_tool_uses": 30 - }, - "mcp_servers": [] -} -``` - -### Project Config - -Location: `.hanzo/config.json` in project root - -```json -{ - "name": "my-project", - "enabled_tools": { - "browser": true, - "database": false - }, - "disabled_tools": ["vector"], - "rules": [ - "Use TypeScript for all new files", - "Follow existing code style" - ] -} -``` - -## Tool Configuration - -### Enable/Disable Tools - -```json -{ - "tools": { - "enabled": ["shell", "filesystem", "memory", "reasoning"], - "disabled": ["browser", "database", "vector"] - } -} -``` - -### Tool-Specific Settings - -```json -{ - "tools": { - "shell": { - "default_shell": "zsh", - "timeout": 30 - }, - "browser": { - "headless": true, - "default_viewport": "desktop" - }, - "memory": { - "scope": "project" - } - } -} -``` - -## Security - -### Path Restrictions - -By default, the server restricts file access. Use `--allow-path` to grant access: - -```bash -# Allow home directory -hanzo-mcp --allow-path ~ - -# Allow specific project -hanzo-mcp --allow-path /path/to/project - -# Allow multiple paths -hanzo-mcp --allow-path ~/work --allow-path /tmp -``` - -### Disable Dangerous Tools - -```bash -# Read-only mode -hanzo-mcp --disable-write-tools - -# No search (for sensitive codebases) -hanzo-mcp --disable-search-tools -``` - -## Claude Desktop Integration - -### Auto-Install - -```bash -hanzo-mcp --install -``` - -This creates the configuration at: -- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` -- Windows: `%APPDATA%\Claude\claude_desktop_config.json` -- Linux: `~/.config/claude/claude_desktop_config.json` - -### Manual Configuration - -```json -{ - "mcpServers": { - "hanzo": { - "command": "uvx", - "args": ["hanzo-mcp", "--allow-path", "~"] - } - } -} -``` - -## Debugging - -### Enable Verbose Logging - -```bash -# SSE mode (logging visible) -hanzo-mcp --transport sse - -# Check version and config -hanzo-mcp --version -``` - -### View Running Processes - -Once connected, use the `ps` tool: - -``` -ps() # List all background processes -ps(logs="id") # View process output -``` diff --git a/docs/mcp/index.md b/docs/mcp/index.md deleted file mode 100644 index a9cb6da85..000000000 --- a/docs/mcp/index.md +++ /dev/null @@ -1,132 +0,0 @@ -# Hanzo MCP - -Hanzo MCP is a comprehensive Model Context Protocol server that provides 30+ tools for AI code assistants like Claude Code, Cursor, and VS Code Copilot. - -## What is MCP? - -The Model Context Protocol (MCP) is a standard for connecting AI assistants to external tools and data sources. Hanzo MCP implements this protocol with a focus on: - -- **Developer Tools** - File operations, shell commands, code search -- **Browser Automation** - Full Playwright integration with 70+ actions -- **AI Reasoning** - Structured thinking and critical analysis tools -- **Memory** - Persistent knowledge storage and retrieval -- **Multi-Agent** - Spawn and coordinate external AI agents - -## Architecture - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ AI Assistant โ”‚ -โ”‚ (Claude, Cursor, VS Code) โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ MCP Protocol (stdio/tcp) - โ–ผ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ hanzo-mcp โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ hanzo-tools โ”‚ โ”‚ hanzo-tools โ”‚ โ”‚ hanzo-tools โ”‚ ... โ”‚ -โ”‚ โ”‚ -shell โ”‚ โ”‚ -fs โ”‚ โ”‚ -browser โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## Tool Categories - -### File System (`hanzo-tools-fs`) -- `read` - Read file contents with line numbers -- `write` - Write content to files -- `edit` - Find and replace in files -- `tree` - Display directory structure -- `find` - Find files by pattern -- `search` - Search file contents (regex) -- `ast` - AST-based code search - -### Shell (`hanzo-tools-shell`) -- `cmd` - Unified command execution with DAG support -- `zsh`, `bash` - Shell-specific execution -- `ps` - Process management (list, kill, logs) -- `npx`, `uvx` - Package runner shortcuts -- `open` - Open files/URLs in default app - -### Browser (`hanzo-tools-browser`) -- 70+ Playwright actions -- Navigation, forms, mouse, touch -- Device emulation (mobile, tablet, laptop) -- Assertions and waits -- Network interception -- Multi-context for parallel agents - -### Memory (`hanzo-tools-memory`) -- `memory` - Unified memory operations - - `recall` - Search memories - - `create` - Store new memories - - `update` - Modify memories - - `delete` - Remove memories - - `facts` - Knowledge base queries - - `summarize` - Create memory summaries - -### Reasoning (`hanzo-tools-reasoning`) -- `think` - Structured reasoning tool -- `critic` - Critical analysis and review - -### LSP & Refactor (`hanzo-tools-lsp`, `hanzo-tools-refactor`) -- Go-to-definition -- Find references -- Rename symbols -- Extract functions/variables -- Change signatures - -### Agent (`hanzo-tools-agent`) -- `agent` - Run external AI agents - - Claude, Gemini, Codex, Grok, Qwen -- `iching` - Engineering wisdom oracle -- `review` - Code review requests - -### LLM (`hanzo-tools-llm`) -- `llm` - Direct LLM access -- `consensus` - Multi-model consensus - -## Key Features - -### Auto-Backgrounding - -Long-running commands automatically background after 30 seconds: - -```python -cmd("npm install") # Auto-backgrounds if slow - -# Check background processes -ps() # List all -ps(logs="cmd_xxx") # View output -ps(kill="cmd_xxx") # Stop process -``` - -### DAG Execution - -Execute complex workflows: - -```python -cmd([ - "mkdir dist", - {"parallel": ["cp a dist/", "cp b dist/"]}, - "zip -r out.zip dist/" -]) -``` - -### Multiple Backends - -The VS Code extension supports multiple MCP backends: - -| Backend | Command | Features | -|---------|---------|----------| -| Python (default) | `uvx hanzo-mcp` | Full 30+ tools | -| TypeScript | Built-in | Essential tools | -| Rust | `hanzo-mcp` binary | High performance | -| Local Node | hanzod | Network deployment | - -## Getting Started - -1. [Installation](../getting-started/installation.md) -2. [Quickstart](quickstart.md) -3. [Configuration](configuration.md) -4. [VS Code Extension](vscode.md) diff --git a/docs/mcp/quickstart.md b/docs/mcp/quickstart.md deleted file mode 100644 index 648d9b6ae..000000000 --- a/docs/mcp/quickstart.md +++ /dev/null @@ -1,117 +0,0 @@ -# MCP Quickstart - -Get started with hanzo-mcp in under 5 minutes. - -## Installation - -```bash -# With pip -pip install hanzo-mcp[tools-all] - -# With uv (recommended) -uv pip install hanzo-mcp[tools-all] -``` - -## Running the Server - -### With Claude Code - -```bash -# Run directly -uvx hanzo-mcp - -# Or with Python -python -m hanzo_mcp -``` - -### With Claude Desktop - -Install the configuration automatically: - -```bash -hanzo-mcp --install -``` - -Or manually add to `~/Library/Application Support/Claude/claude_desktop_config.json`: - -```json -{ - "mcpServers": { - "hanzo": { - "command": "uvx", - "args": ["hanzo-mcp"] - } - } -} -``` - -Restart Claude Desktop after configuration. - -## Basic Usage - -Once running, you have access to 30+ tools: - -### File Operations - -``` -Read a file: read("/path/to/file.py") -Write a file: write("/path/to/file.py", "content") -Edit a file: edit("/path/to/file.py", "old", "new") -``` - -### Command Execution - -``` -Run command: cmd("ls -la") -Run parallel: cmd(["npm install", "cargo build"], parallel=True) -``` - -### Search - -``` -Search files: search("pattern", path="./src") -Find files: find("*.py", path=".") -AST search: ast("def test_", path="./tests") -``` - -### Memory - -``` -Store memory: create_memories(["User prefers dark mode"]) -Recall: recall_memories(["user preferences"]) -``` - -## Transport Modes - -### stdio (default) - -Used by Claude Code and Claude Desktop: - -```bash -hanzo-mcp --transport stdio -``` - -### SSE (Server-Sent Events) - -For web clients and debugging: - -```bash -hanzo-mcp --transport sse --port 8888 -``` - -Access at `http://localhost:8888` - -## Environment Variables - -| Variable | Description | -|----------|-------------| -| `HANZO_AUTO_BACKGROUND_TIMEOUT` | Auto-background timeout (default: 30s, 0 to disable) | -| `HANZO_DEFAULT_SHELL` | Default shell (default: zsh) | -| `OPENAI_API_KEY` | For LLM tools | -| `ANTHROPIC_API_KEY` | For LLM tools | - -## Next Steps - -- [Configuration](configuration.md) - Detailed configuration options -- [Tools Reference](../tools/index.md) - All available tools -- [VS Code Setup](vscode.md) - IDE integration diff --git a/docs/mcp/tools/agent.md b/docs/mcp/tools/agent.md deleted file mode 100644 index 0e6b075b5..000000000 --- a/docs/mcp/tools/agent.md +++ /dev/null @@ -1,20 +0,0 @@ -# Agent Tools - -Multi-agent orchestration with CLI integration. - -โ†’ **Full documentation: [../../tools/agent.md](../../tools/agent.md)** - -## Quick Reference - -```python -# Run agent -agent(action="run", name="claude", prompt="Explain this code") - -# List available agents -agent(action="list") - -# Multi-agent consensus -agent(action="consensus", prompt="Design an API", agents=["claude", "gemini"]) -``` - -Available agents: `claude`, `codex`, `gemini`, `grok`, `qwen`, `vibe`, `code`, `dev` diff --git a/docs/mcp/tools/browser.md b/docs/mcp/tools/browser.md deleted file mode 100644 index 6930a801b..000000000 --- a/docs/mcp/tools/browser.md +++ /dev/null @@ -1,26 +0,0 @@ -# Browser Tool - -Complete Playwright automation with 70+ actions. - -โ†’ **Full documentation: [../../tools/browser.md](../../tools/browser.md)** - -## Quick Reference - -```python -# Navigate -browser(action="navigate", url="https://example.com") - -# Click element -browser(action="click", selector="button.submit") - -# Fill form -browser(action="fill", selector="input[name=email]", text="user@example.com") - -# Screenshot -browser(action="screenshot", full_page=True) - -# Device emulation -browser(action="emulate", device="mobile") -``` - -Supports: navigation, forms, mouse, touch, assertions, storage, network, and more. diff --git a/docs/mcp/tools/filesystem.md b/docs/mcp/tools/filesystem.md deleted file mode 100644 index 85ec58c02..000000000 --- a/docs/mcp/tools/filesystem.md +++ /dev/null @@ -1,32 +0,0 @@ -# Filesystem Tools - -File operations with AST analysis. - -โ†’ **Full documentation: [../../tools/fs.md](../../tools/fs.md)** - -## Quick Reference - -```python -# Read file -read(file_path="/path/to/file.py") - -# Write file -write(file_path="/path/to/file.py", content="...") - -# Edit file -edit(file_path="/path/to/file.py", old_string="old", new_string="new") - -# Directory tree -tree(path="/project", depth=3) - -# Find files -find(pattern="*.py", path="/src") - -# Search content -search(pattern="TODO", path=".") - -# AST analysis -ast(pattern="def test_", path="/tests") -``` - -7 tools: `read`, `write`, `edit`, `tree`, `find`, `search`, `ast` diff --git a/docs/mcp/tools/llm-tools.md b/docs/mcp/tools/llm-tools.md deleted file mode 100644 index 485275d8d..000000000 --- a/docs/mcp/tools/llm-tools.md +++ /dev/null @@ -1,23 +0,0 @@ -# LLM Tools - -Unified LLM interface via LLM. - -โ†’ **Full documentation: [../../tools/llm-tools.md](../../tools/llm-tools.md)** - -## Quick Reference - -```python -# Call any LLM -llm(model="gpt-4", prompt="Explain quantum computing") - -# With system prompt -llm(model="claude-3-5-sonnet", prompt="...", system="You are a helpful assistant") - -# Multi-model consensus -consensus( - prompt="Best database for this use case?", - models=["gpt-4", "claude-3-5-sonnet", "gemini-pro"] -) -``` - -Supports: OpenAI, Anthropic, Google, Together, Groq, Mistral, Ollama, and 100+ more. diff --git a/docs/mcp/tools/lsp.md b/docs/mcp/tools/lsp.md deleted file mode 100644 index 5b474c81c..000000000 --- a/docs/mcp/tools/lsp.md +++ /dev/null @@ -1,29 +0,0 @@ -# LSP Tool - -Language Server Protocol for code intelligence. - -โ†’ **Full documentation: [../../tools/lsp.md](../../tools/lsp.md)** - -## Quick Reference - -```python -# Go to definition -lsp(action="definition", file="main.py", line=10, character=15) - -# Find references -lsp(action="references", file="main.py", line=10, character=15) - -# Hover information -lsp(action="hover", file="main.py", line=10, character=15) - -# Code completion -lsp(action="completion", file="main.py", line=10, character=15) - -# Rename symbol -lsp(action="rename", file="main.py", line=10, character=15, new_name="newName") - -# Get diagnostics -lsp(action="diagnostics", file="main.py") -``` - -Supported: Python, TypeScript, JavaScript, Go, Rust, Java, C/C++, Ruby, Lua diff --git a/docs/mcp/tools/memory.md b/docs/mcp/tools/memory.md deleted file mode 100644 index fb3807ccc..000000000 --- a/docs/mcp/tools/memory.md +++ /dev/null @@ -1,29 +0,0 @@ -# Memory Tools - -Persistent memory and knowledge base management. - -โ†’ **Full documentation: [../../tools/memory.md](../../tools/memory.md)** - -## Quick Reference - -```python -# Recall memories -recall_memories(queries=["project architecture"]) - -# Create memories -create_memories(statements=["User prefers dark mode"]) - -# Store facts -store_facts(facts=["API rate limit: 100/hour"], kb_name="api_docs") - -# Recall facts -recall_facts(queries=["rate limits"], kb_name="api_docs") - -# Summarize to memory -summarize_to_memory(content="...", topic="Architecture Decisions") - -# Manage knowledge bases -manage_knowledge_bases(action="create", kb_name="docs", description="Project docs") -``` - -9 tools for memory and knowledge management. diff --git a/docs/mcp/tools/reasoning.md b/docs/mcp/tools/reasoning.md deleted file mode 100644 index 041e7f34d..000000000 --- a/docs/mcp/tools/reasoning.md +++ /dev/null @@ -1,44 +0,0 @@ -# Reasoning Tools - -Structured thinking and critical analysis. - -โ†’ **Full documentation: [../../tools/reasoning.md](../../tools/reasoning.md)** - -## Quick Reference - -### Think Tool - -```python -# Structured reasoning -think(thought=""" -Analyzing the authentication architecture: -1. Current: Session-based auth with cookies -2. Problem: Mobile apps need token-based auth -3. Options: - - JWT tokens - - OAuth 2.0 - - API keys -4. Recommendation: JWT for stateless auth -""") -``` - -### Critic Tool - -```python -# Critical analysis -critic(analysis=""" -Code Review Analysis: -- Missing error handling for network failures -- No input validation on user data -- SQL injection vulnerability in query construction -- N+1 database query problem in loop - -Recommendations: -1. Add try/catch with retry logic -2. Validate all inputs at API boundary -3. Use parameterized queries -4. Batch database queries -""") -``` - -Use `think` for exploration, `critic` for quality assurance. diff --git a/docs/mcp/tools/shell.md b/docs/mcp/tools/shell.md deleted file mode 100644 index ac85edc2d..000000000 --- a/docs/mcp/tools/shell.md +++ /dev/null @@ -1,236 +0,0 @@ -# Shell & Command Tools - -The shell tools (`hanzo-tools-shell`) provide comprehensive command execution with auto-backgrounding, DAG support, and process management. - -## cmd - Unified Command Execution - -The `cmd` tool replaces traditional bash/shell commands with intelligent features: - -```python -# Simple command -cmd("ls -la") - -# Sequential execution -cmd(["mkdir dist", "cp *.js dist/", "zip -r out.zip dist/"]) - -# Parallel execution -cmd(["npm install", "cargo build"], parallel=True) - -# Mixed DAG -cmd([ - "mkdir dist", - {"parallel": ["cp a dist/", "cp b dist/"]}, - "zip -r out.zip dist/" -]) -``` - -### Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `command` | str | - | Single command to execute | -| `commands` | list | - | List of commands for DAG execution | -| `parallel` | bool | `False` | Run all commands in parallel | -| `timeout` | int | `45` | Per-command timeout in seconds | -| `cwd` | str | - | Working directory | -| `env` | dict | - | Environment variables | -| `shell` | str | `zsh` | Shell to use (zsh, bash, sh) | -| `strict` | bool | `False` | Stop on first error | -| `quiet` | bool | `False` | Suppress stdout | - -### DAG Syntax - -Execute complex dependency graphs: - -```python -# Named steps with dependencies -cmd([ - {"id": "build", "run": "make build"}, - {"id": "test", "run": "make test", "after": ["build"]}, - {"id": "deploy", "run": "make deploy", "after": ["test"]} -]) - -# Nested parallel blocks -cmd([ - "prepare", - {"parallel": ["task_a", "task_b", "task_c"]}, - "finalize" -]) - -# Tool invocations -cmd([{"tool": "search", "input": {"pattern": "TODO"}}]) -``` - -### Auto-Backgrounding - -Commands exceeding 30 seconds automatically background: - -```python -# This will auto-background after 30s -cmd("npm install") # Returns immediately with process ID - -# Disable auto-backgrounding -export HANZO_AUTO_BACKGROUND_TIMEOUT=0 -``` - -## ps - Process Management - -Manage background processes: - -```python -# List all processes -ps() - -# Get specific process -ps(id="cmd_abc123") - -# View output logs -ps(logs="cmd_abc123", n=100) # Last 100 lines - -# Kill process -ps(kill="cmd_abc123") - -# Kill with specific signal -ps(kill="cmd_abc123", sig=9) # SIGKILL -``` - -### Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `id` | str | - | Process ID to query | -| `logs` | str | - | Process ID to get logs from | -| `kill` | str | - | Process ID to kill | -| `sig` | int | `15` | Signal for kill (15=SIGTERM, 9=SIGKILL) | -| `n` | int | `100` | Number of log lines | - -## Shell-Specific Tools - -### zsh - -Execute in zsh (default, most feature-rich): - -```python -zsh("echo $ZSH_VERSION") -zsh(["cmd1", "cmd2"], parallel=True) -``` - -### bash - -Execute in bash: - -```python -bash("echo $BASH_VERSION") -bash(["cmd1", "cmd2"]) -``` - -### dash - -Execute in dash (POSIX-compliant, fastest): - -```python -dash("echo 'fast POSIX shell'") -``` - -## Package Runners - -### npx - -Run npm packages: - -```python -npx(package="prettier", args="--write .") -npx(package="http-server", args="-p 8080") # Auto-backgrounds -``` - -### uvx - -Run Python packages: - -```python -uvx(package="ruff", args="check .") -uvx(package="mkdocs", args="serve") # Auto-backgrounds -``` - -## open - -Open files or URLs in default application: - -```python -open(path="https://example.com") -open(path="./document.pdf") -open(path="/path/to/image.png") -``` - -## Configuration - -### Environment Variables - -| Variable | Default | Description | -|----------|---------|-------------| -| `HANZO_AUTO_BACKGROUND_TIMEOUT` | `45` | Seconds before auto-backgrounding (0 to disable) | -| `HANZO_DEFAULT_SHELL` | `zsh` | Default shell for commands | - -### VS Code Settings - -```json -{ - "hanzo.mcp.commandTimeout": 45, - "hanzo.mcp.defaultShell": "zsh" -} -``` - -## Best Practices - -### 1. Use DAGs for Dependencies - -```python -# Good - explicit dependencies -cmd([ - {"id": "install", "run": "npm install"}, - {"id": "build", "run": "npm run build", "after": ["install"]} -]) - -# Avoid - relies on shell chaining -cmd("npm install && npm run build") -``` - -### 2. Parallel Where Possible - -```python -# Good - parallel independent tasks -cmd(["npm install", "cargo build", "go mod download"], parallel=True) - -# Avoid - sequential when not needed -cmd("npm install") -cmd("cargo build") -cmd("go mod download") -``` - -### 3. Handle Long-Running Processes - -```python -# Start server (auto-backgrounds) -cmd("npm run dev") - -# Check if running -ps() - -# Get logs -ps(logs="cmd_xxx") - -# Stop when done -ps(kill="cmd_xxx") -``` - -### 4. Use Environment Variables - -```python -cmd( - "deploy.sh", - env={ - "NODE_ENV": "production", - "API_KEY": "${DEPLOY_KEY}" - } -) -``` diff --git a/docs/mcp/vscode.md b/docs/mcp/vscode.md deleted file mode 100644 index 0f44f5ddb..000000000 --- a/docs/mcp/vscode.md +++ /dev/null @@ -1,253 +0,0 @@ -# VS Code Integration - -Set up hanzo-mcp with VS Code, Cursor, and other MCP-compatible editors. - -## VS Code with Continue - -[Continue](https://continue.dev) is an open-source AI code assistant that supports MCP. - -### Installation - -1. Install Continue extension from VS Code marketplace -2. Configure MCP in `~/.continue/config.json`: - -```json -{ - "models": [...], - "mcpServers": [ - { - "name": "hanzo", - "command": "uvx", - "args": ["hanzo-mcp"] - } - ] -} -``` - -3. Restart VS Code - -## Cursor - -Cursor has built-in MCP support. - -### Configuration - -Add to Cursor settings (`~/.cursor/mcp.json`): - -```json -{ - "mcpServers": { - "hanzo": { - "command": "uvx", - "args": ["hanzo-mcp", "--allow-path", "~"] - } - } -} -``` - -## Windsurf (Codeium) - -### Configuration - -Add to `~/.codeium/windsurf/mcp_config.json`: - -```json -{ - "mcpServers": { - "hanzo": { - "command": "uvx", - "args": ["hanzo-mcp"] - } - } -} -``` - -## Generic MCP Client - -For any MCP-compatible client: - -### stdio Transport - -```json -{ - "command": "uvx", - "args": ["hanzo-mcp", "--transport", "stdio"] -} -``` - -### SSE Transport - -Start the server: - -```bash -hanzo-mcp --transport sse --port 8888 -``` - -Connect to: `http://localhost:8888/sse` - -## Project-Specific Configuration - -Create `.hanzo/config.json` in your project root: - -```json -{ - "name": "my-project", - "rules": [ - "Use TypeScript", - "Follow existing patterns" - ], - "enabled_tools": { - "shell": true, - "browser": false - } -} -``` - -The MCP server will automatically detect and apply project settings. - -## Recommended Extensions - -### VS Code - -- **Continue** - AI assistant with MCP support -- **Claude Dev** - Claude integration -- **GitLens** - Git integration (works well with hanzo tools) - -### Cursor - -Built-in AI features work with hanzo-mcp out of the box. - -## Troubleshooting - -### Server Not Starting - -```bash -# Check if hanzo-mcp is installed -uvx hanzo-mcp --version - -# Test manually -uvx hanzo-mcp --transport sse --port 8888 -# Then open http://localhost:8888 in browser -``` - -### Permission Errors - -Ensure paths are allowed: - -```json -{ - "args": ["hanzo-mcp", "--allow-path", "/path/to/project"] -} -``` - -### Tool Not Available - -Some tools require additional dependencies: - -```bash -# Full installation with all tools -pip install hanzo-mcp[tools-all] - -# Or specific tools -pip install hanzo-mcp[browser] # Playwright -pip install hanzo-mcp[llm] # LLM -``` - -### Logs and Debugging - -For SSE transport, logs appear in terminal. For stdio: - -```bash -# Redirect stderr to file -hanzo-mcp 2>/tmp/hanzo-mcp.log -``` - -## Performance Tips - -1. **Use uvx** - Faster startup than pip-installed version -2. **Limit paths** - Only allow necessary directories -3. **Disable unused tools** - Reduces memory usage -4. **Use project config** - Faster tool discovery - -## Browser Extension Integration - -The Hanzo Browser Extension enables AI control of browser tabs through the browser tool. - -### Installation - -1. Install the browser extension from the Chrome/Firefox store (or build from source) -2. Start the CDP bridge server: - -```bash -python -m hanzo_tools.browser.cdp_bridge_server -# Runs on ws://localhost:9223 by default -``` - -3. The extension automatically connects to the bridge - -### How It Works - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ hanzo-mcp โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ CDP Bridge โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ Browser Extension โ”‚ -โ”‚ browser tool โ”‚โ—€โ”€โ”€โ”€โ”€โ”‚ Server (9223) โ”‚โ—€โ”€โ”€โ”€โ”€โ”‚ (CDP Provider) โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ–ฒ - โ”‚ WebSocket - โ”‚ - โ”Œโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ” - โ”‚ Chrome โ”‚ - โ”‚ Tabs โ”‚ - โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -### Configuration - -Environment variables: - -| Variable | Default | Description | -|----------|---------|-------------| -| `HANZO_CDP_BRIDGE_HOST` | `localhost` | Bridge server host | -| `HANZO_CDP_BRIDGE_PORT` | `9223` | Bridge server port | - -### Usage with MCP - -Once connected, the browser tool can control tabs through the extension: - -```python -# In your AI/MCP context -browser(action="navigate", url="https://example.com") -browser(action="click", selector="#login-button") -browser(action="fill", selector="#email", text="user@example.com") -browser(action="screenshot") -``` - -### Programmatic Usage - -```python -from hanzo_tools.browser import CDPBridgeClient - -# Connect to the bridge -client = CDPBridgeClient(port=9223) -await client.connect() - -# Control browser -await client.navigate("https://example.com") -await client.click("#submit") -screenshot = await client.screenshot(full_page=True) -``` - -### Debugging - -If the extension isn't connecting: - -1. Check extension is installed and enabled -2. Verify bridge server is running: `curl http://localhost:9223/health` -3. Check browser console for errors (F12 โ†’ Console) -4. Ensure `debugger` permission is granted in extension settings - -## See Also - -- [Quickstart](quickstart.md) - Getting started -- [Configuration](configuration.md) - All configuration options -- [Tools Reference](../tools/index.md) - Available tools diff --git a/docs/mdx-components.tsx b/docs/mdx-components.tsx deleted file mode 100644 index 741fc4893..000000000 --- a/docs/mdx-components.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import defaultMdxComponents from 'fumadocs-ui/mdx'; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function useMDXComponents(components: any): any { - return { - ...defaultMdxComponents, - ...components, - }; -} diff --git a/docs/next.config.ts b/docs/next.config.ts deleted file mode 100644 index 92474d9c5..000000000 --- a/docs/next.config.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { createMDX } from 'fumadocs-mdx/next'; - -const withMDX = createMDX(); - -const config = { - reactStrictMode: true, - output: 'export' as const, - images: { - unoptimized: true, - }, - basePath: process.env.NODE_ENV === 'production' ? '/python-sdk' : '', -}; - -export default withMDX(config); diff --git a/docs/overrides/.gitkeep b/docs/overrides/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/package-lock.json b/docs/package-lock.json deleted file mode 100644 index dd8e7872d..000000000 --- a/docs/package-lock.json +++ /dev/null @@ -1,5853 +0,0 @@ -{ - "name": "@hanzo/python-sdk-docs", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@hanzo/python-sdk-docs", - "version": "0.1.0", - "dependencies": { - "fumadocs-core": "^16.4.1", - "fumadocs-mdx": "^14.2.3", - "fumadocs-ui": "^16.4.1", - "lucide-react": "^0.468.0", - "next": "^16.1.1", - "next-themes": "^0.4.6", - "react": "^19.1.0", - "react-dom": "^19.1.0", - "shiki": "^3.0.0" - }, - "devDependencies": { - "@tailwindcss/postcss": "^4.1.0", - "@types/node": "^22.0.0", - "@types/react": "^19.0.0", - "@types/react-dom": "^19.0.0", - "postcss": "^8.5.0", - "tailwindcss": "^4.1.0", - "typescript": "^5.7.0" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", - "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", - "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", - "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", - "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", - "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", - "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", - "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", - "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", - "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", - "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", - "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", - "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", - "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", - "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", - "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", - "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", - "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", - "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", - "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", - "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", - "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", - "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", - "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", - "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", - "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", - "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", - "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", - "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", - "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.3", - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", - "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.7.4" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", - "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", - "license": "MIT" - }, - "node_modules/@formatjs/fast-memoize": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.0.1.tgz", - "integrity": "sha512-kzk635kEmsxrrEWQXY7uKRocFCVXR4es5OQqcqCGg2NPtQztG/OBkE9THHu6UOTxpfyIkZhh6DjPBZGRp7y3og==", - "license": "MIT", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/intl-localematcher": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.7.3.tgz", - "integrity": "sha512-NaeABectKdTCOnlH9VFGmMS3K0JuR7Soc2t5R2MCkBrM3H/hlKVYh0XSrcjjPkbjIdrF7L/Bzx9JtGuVaSfYlA==", - "license": "MIT", - "dependencies": { - "@formatjs/fast-memoize": "3.0.1", - "tslib": "^2.8.0" - } - }, - "node_modules/@fumadocs/ui": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/@fumadocs/ui/-/ui-16.4.1.tgz", - "integrity": "sha512-biv5/6+7BbAs8yuP3il1JzE+nUkndvC+qFrsX3a08ybqO7bZ0kXkVgr8n6bNmI4Yz8fTy61ZvwEyTqYmPa9Y1g==", - "license": "MIT", - "dependencies": { - "fumadocs-core": "16.4.1", - "lodash.merge": "^4.6.2", - "next-themes": "^0.4.6", - "postcss-selector-parser": "^7.1.1", - "tailwind-merge": "^3.4.0" - }, - "peerDependencies": { - "@types/react": "*", - "next": "16.x.x", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "tailwindcss": "^4.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "next": { - "optional": true - }, - "tailwindcss": { - "optional": true - } - } - }, - "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@mdx-js/mdx": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", - "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdx": "^2.0.0", - "acorn": "^8.0.0", - "collapse-white-space": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-util-scope": "^1.0.0", - "estree-walker": "^3.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "markdown-extensions": "^2.0.0", - "recma-build-jsx": "^1.0.0", - "recma-jsx": "^1.0.0", - "recma-stringify": "^1.0.0", - "rehype-recma": "^1.0.0", - "remark-mdx": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "source-map": "^0.7.0", - "unified": "^11.0.0", - "unist-util-position-from-estree": "^2.0.0", - "unist-util-stringify-position": "^4.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@next/env": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.1.tgz", - "integrity": "sha512-3oxyM97Sr2PqiVyMyrZUtrtM3jqqFxOQJVuKclDsgj/L728iZt/GyslkN4NwarledZATCenbk4Offjk1hQmaAA==", - "license": "MIT" - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.1.tgz", - "integrity": "sha512-JS3m42ifsVSJjSTzh27nW+Igfha3NdBOFScr9C80hHGrWx55pTrVL23RJbqir7k7/15SKlrLHhh/MQzqBBYrQA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.1.tgz", - "integrity": "sha512-hbyKtrDGUkgkyQi1m1IyD3q4I/3m9ngr+V93z4oKHrPcmxwNL5iMWORvLSGAf2YujL+6HxgVvZuCYZfLfb4bGw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.1.tgz", - "integrity": "sha512-/fvHet+EYckFvRLQ0jPHJCUI5/B56+2DpI1xDSvi80r/3Ez+Eaa2Yq4tJcRTaB1kqj/HrYKn8Yplm9bNoMJpwQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.1.tgz", - "integrity": "sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.1.tgz", - "integrity": "sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.1.tgz", - "integrity": "sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.1.tgz", - "integrity": "sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.1.tgz", - "integrity": "sha512-Ncwbw2WJ57Al5OX0k4chM68DKhEPlrXBaSXDCi2kPi5f4d8b3ejr3RRJGfKBLrn2YJL5ezNS7w2TZLHSti8CMw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@orama/orama": { - "version": "3.1.18", - "resolved": "https://registry.npmjs.org/@orama/orama/-/orama-3.1.18.tgz", - "integrity": "sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA==", - "license": "Apache-2.0", - "engines": { - "node": ">= 20.0.0" - } - }, - "node_modules/@radix-ui/number": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", - "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", - "license": "MIT" - }, - "node_modules/@radix-ui/primitive": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", - "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", - "license": "MIT" - }, - "node_modules/@radix-ui/react-accordion": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.12.tgz", - "integrity": "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collapsible": "1.1.12", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", - "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", - "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-collection": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", - "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-context": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", - "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-direction": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", - "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", - "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-escape-keydown": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", - "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", - "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-navigation-menu": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.14.tgz", - "integrity": "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popover": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", - "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popper": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", - "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", - "license": "MIT", - "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-rect": "1.1.1", - "@radix-ui/react-use-size": "1.1.1", - "@radix-ui/rect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", - "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", - "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-scroll-area": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz", - "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==", - "license": "MIT", - "dependencies": { - "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", - "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tabs": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", - "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", - "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", - "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", - "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-previous": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", - "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", - "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", - "license": "MIT", - "dependencies": { - "@radix-ui/rect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", - "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", - "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", - "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", - "license": "MIT" - }, - "node_modules/@shikijs/core": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.20.0.tgz", - "integrity": "sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.20.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.5" - } - }, - "node_modules/@shikijs/engine-javascript": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.20.0.tgz", - "integrity": "sha512-OFx8fHAZuk7I42Z9YAdZ95To6jDePQ9Rnfbw9uSRTSbBhYBp1kEOKv/3jOimcj3VRUKusDYM6DswLauwfhboLg==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.20.0", - "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.4" - } - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.20.0.tgz", - "integrity": "sha512-Yx3gy7xLzM0ZOjqoxciHjA7dAt5tyzJE3L4uQoM83agahy+PlW244XJSrmJRSBvGYELDhYXPacD4R/cauV5bzQ==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.20.0", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@shikijs/langs": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.20.0.tgz", - "integrity": "sha512-le+bssCxcSHrygCWuOrYJHvjus6zhQ2K7q/0mgjiffRbkhM4o1EWu2m+29l0yEsHDbWaWPNnDUTRVVBvBBeKaA==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.20.0" - } - }, - "node_modules/@shikijs/rehype": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@shikijs/rehype/-/rehype-3.20.0.tgz", - "integrity": "sha512-/sqob3V/lJK0m2mZ64nkcWPN88im0D9atkI3S3PUBvtJZTHnJXVwZhHQFRDyObgEIa37IpHYHR3CuFtXB5bT2g==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.20.0", - "@types/hast": "^3.0.4", - "hast-util-to-string": "^3.0.1", - "shiki": "3.20.0", - "unified": "^11.0.5", - "unist-util-visit": "^5.0.0" - } - }, - "node_modules/@shikijs/themes": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.20.0.tgz", - "integrity": "sha512-U1NSU7Sl26Q7ErRvJUouArxfM2euWqq1xaSrbqMu2iqa+tSp0D1Yah8216sDYbdDHw4C8b75UpE65eWorm2erQ==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.20.0" - } - }, - "node_modules/@shikijs/transformers": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-3.20.0.tgz", - "integrity": "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g==", - "license": "MIT", - "dependencies": { - "@shikijs/core": "3.20.0", - "@shikijs/types": "3.20.0" - } - }, - "node_modules/@shikijs/types": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.20.0.tgz", - "integrity": "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw==", - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "license": "MIT" - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "license": "MIT" - }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@tailwindcss/node": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", - "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.4", - "enhanced-resolve": "^5.18.3", - "jiti": "^2.6.1", - "lightningcss": "1.30.2", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.1.18" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz", - "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.18", - "@tailwindcss/oxide-darwin-arm64": "4.1.18", - "@tailwindcss/oxide-darwin-x64": "4.1.18", - "@tailwindcss/oxide-freebsd-x64": "4.1.18", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", - "@tailwindcss/oxide-linux-x64-musl": "4.1.18", - "@tailwindcss/oxide-wasm32-wasi": "4.1.18", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz", - "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz", - "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz", - "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz", - "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz", - "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz", - "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz", - "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz", - "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz", - "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz", - "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1", - "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.1.0", - "@tybys/wasm-util": "^0.10.1", - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz", - "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz", - "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/postcss": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.18.tgz", - "integrity": "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.18", - "@tailwindcss/oxide": "4.1.18", - "postcss": "^8.4.41", - "tailwindcss": "4.1.18" - } - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "license": "MIT" - }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdx": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", - "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", - "license": "MIT" - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.19.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", - "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/react": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", - "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", - "devOptional": true, - "license": "MIT", - "peer": true, - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "devOptional": true, - "license": "MIT", - "peer": true, - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "license": "MIT", - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/aria-hidden": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", - "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/astring": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", - "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", - "license": "MIT", - "bin": { - "astring": "bin/astring" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.11", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", - "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001761", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz", - "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chokidar": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", - "license": "MIT", - "dependencies": { - "readdirp": "^5.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/class-variance-authority": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", - "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", - "license": "Apache-2.0", - "dependencies": { - "clsx": "^2.1.1" - }, - "funding": { - "url": "https://polar.sh/cva" - } - }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/collapse-white-space": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", - "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/compute-scroll-into-view": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz", - "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==", - "license": "MIT" - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decode-named-character-reference": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", - "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "devOptional": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.18.4", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", - "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/esast-util-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", - "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esast-util-from-js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", - "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "acorn": "^8.0.0", - "esast-util-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esbuild": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", - "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.2", - "@esbuild/android-arm": "0.27.2", - "@esbuild/android-arm64": "0.27.2", - "@esbuild/android-x64": "0.27.2", - "@esbuild/darwin-arm64": "0.27.2", - "@esbuild/darwin-x64": "0.27.2", - "@esbuild/freebsd-arm64": "0.27.2", - "@esbuild/freebsd-x64": "0.27.2", - "@esbuild/linux-arm": "0.27.2", - "@esbuild/linux-arm64": "0.27.2", - "@esbuild/linux-ia32": "0.27.2", - "@esbuild/linux-loong64": "0.27.2", - "@esbuild/linux-mips64el": "0.27.2", - "@esbuild/linux-ppc64": "0.27.2", - "@esbuild/linux-riscv64": "0.27.2", - "@esbuild/linux-s390x": "0.27.2", - "@esbuild/linux-x64": "0.27.2", - "@esbuild/netbsd-arm64": "0.27.2", - "@esbuild/netbsd-x64": "0.27.2", - "@esbuild/openbsd-arm64": "0.27.2", - "@esbuild/openbsd-x64": "0.27.2", - "@esbuild/openharmony-arm64": "0.27.2", - "@esbuild/sunos-x64": "0.27.2", - "@esbuild/win32-arm64": "0.27.2", - "@esbuild/win32-ia32": "0.27.2", - "@esbuild/win32-x64": "0.27.2" - } - }, - "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/estree-util-attach-comments": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", - "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-build-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", - "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-walker": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-scope": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", - "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-to-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", - "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "astring": "^1.8.0", - "source-map": "^0.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-value-to-estree": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.5.0.tgz", - "integrity": "sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/remcohaszing" - } - }, - "node_modules/estree-util-visit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", - "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fumadocs-core": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/fumadocs-core/-/fumadocs-core-16.4.1.tgz", - "integrity": "sha512-qx86oRMNg9doSa0WJhkCnXm2OwOZCSYKWqzTWyisZmzy8b4YBuPYx90OMvpI34nIcNNsoLqtWk3NchB6C9kMhg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@formatjs/intl-localematcher": "^0.7.2", - "@orama/orama": "^3.1.18", - "@shikijs/rehype": "^3.20.0", - "@shikijs/transformers": "^3.20.0", - "estree-util-value-to-estree": "^3.5.0", - "github-slugger": "^2.0.0", - "hast-util-to-estree": "^3.1.3", - "hast-util-to-jsx-runtime": "^2.3.6", - "image-size": "^2.0.2", - "negotiator": "^1.0.0", - "npm-to-yarn": "^3.0.1", - "path-to-regexp": "^8.3.0", - "remark": "^15.0.1", - "remark-gfm": "^4.0.1", - "remark-rehype": "^11.1.2", - "scroll-into-view-if-needed": "^3.1.0", - "shiki": "^3.20.0", - "unist-util-visit": "^5.0.0" - }, - "peerDependencies": { - "@mixedbread/sdk": "^0.46.0", - "@orama/core": "1.x.x", - "@tanstack/react-router": "1.x.x", - "@types/react": "*", - "algoliasearch": "5.x.x", - "lucide-react": "*", - "next": "16.x.x", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "react-router": "7.x.x", - "waku": "^0.26.0 || ^0.27.0", - "zod": "*" - }, - "peerDependenciesMeta": { - "@mixedbread/sdk": { - "optional": true - }, - "@orama/core": { - "optional": true - }, - "@tanstack/react-router": { - "optional": true - }, - "@types/react": { - "optional": true - }, - "algoliasearch": { - "optional": true - }, - "lucide-react": { - "optional": true - }, - "next": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-router": { - "optional": true - }, - "waku": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/fumadocs-mdx": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/fumadocs-mdx/-/fumadocs-mdx-14.2.3.tgz", - "integrity": "sha512-O69DI58bRuCYjvxs/ERer0cclBFup9LEDfO5gPgIaRqFyHxaEuVvV+1D/DOE/ZgtP2rFwUsjM4bST59RAcwgDA==", - "license": "MIT", - "dependencies": { - "@mdx-js/mdx": "^3.1.1", - "@standard-schema/spec": "^1.1.0", - "chokidar": "^5.0.0", - "esbuild": "^0.27.2", - "estree-util-value-to-estree": "^3.5.0", - "js-yaml": "^4.1.1", - "mdast-util-to-markdown": "^2.1.2", - "picocolors": "^1.1.1", - "picomatch": "^4.0.3", - "remark-mdx": "^3.1.1", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "unified": "^11.0.5", - "unist-util-remove-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.3", - "zod": "^4.2.1" - }, - "bin": { - "fumadocs-mdx": "dist/bin.js" - }, - "peerDependencies": { - "@fumadocs/mdx-remote": "^1.4.0", - "@types/react": "*", - "fumadocs-core": "^15.0.0 || ^16.0.0", - "next": "^15.3.0 || ^16.0.0", - "react": "*", - "vite": "6.x.x || 7.x.x" - }, - "peerDependenciesMeta": { - "@fumadocs/mdx-remote": { - "optional": true - }, - "@types/react": { - "optional": true - }, - "next": { - "optional": true - }, - "react": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/fumadocs-ui": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/fumadocs-ui/-/fumadocs-ui-16.4.1.tgz", - "integrity": "sha512-4nU0GLrjNnQDuokj3t80numrExE2b12lDZVLaEjolnJEjxDTjw5U7utYx09ylJ/NgzNB7Ithj/aTY8bPeDiGoA==", - "license": "MIT", - "dependencies": { - "@fumadocs/ui": "16.4.1", - "@radix-ui/react-accordion": "^1.2.12", - "@radix-ui/react-collapsible": "^1.1.12", - "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-direction": "^1.1.1", - "@radix-ui/react-navigation-menu": "^1.2.14", - "@radix-ui/react-popover": "^1.1.15", - "@radix-ui/react-presence": "^1.1.5", - "@radix-ui/react-scroll-area": "^1.2.10", - "@radix-ui/react-slot": "^1.2.4", - "@radix-ui/react-tabs": "^1.1.13", - "class-variance-authority": "^0.7.1", - "fumadocs-core": "16.4.1", - "next-themes": "^0.4.6", - "react-medium-image-zoom": "^5.4.0", - "scroll-into-view-if-needed": "^3.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "tailwindcss": "^4.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "tailwindcss": { - "optional": true - } - } - }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/github-slugger": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", - "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", - "license": "ISC" - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/hast-util-to-estree": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", - "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-attach-comments": "^3.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-html": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", - "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^3.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "stringify-entities": "^4.0.0", - "zwitch": "^2.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-string": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", - "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/image-size": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz", - "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==", - "license": "MIT", - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=16.x" - } - }, - "node_modules/inline-style-parser": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", - "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", - "license": "MIT" - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/lightningcss": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", - "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.30.2", - "lightningcss-darwin-arm64": "1.30.2", - "lightningcss-darwin-x64": "1.30.2", - "lightningcss-freebsd-x64": "1.30.2", - "lightningcss-linux-arm-gnueabihf": "1.30.2", - "lightningcss-linux-arm64-gnu": "1.30.2", - "lightningcss-linux-arm64-musl": "1.30.2", - "lightningcss-linux-x64-gnu": "1.30.2", - "lightningcss-linux-x64-musl": "1.30.2", - "lightningcss-win32-arm64-msvc": "1.30.2", - "lightningcss-win32-x64-msvc": "1.30.2" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", - "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", - "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", - "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", - "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", - "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", - "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", - "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", - "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", - "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", - "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", - "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "license": "MIT" - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/lucide-react": { - "version": "0.468.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz", - "integrity": "sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==", - "license": "ISC", - "peer": true, - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/markdown-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", - "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", - "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", - "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-expression": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", - "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-jsx": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", - "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-md": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", - "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", - "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", - "license": "MIT", - "dependencies": { - "acorn": "^8.0.0", - "acorn-jsx": "^5.0.0", - "micromark-extension-mdx-expression": "^3.0.0", - "micromark-extension-mdx-jsx": "^3.0.0", - "micromark-extension-mdx-md": "^2.0.0", - "micromark-extension-mdxjs-esm": "^3.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", - "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", - "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-events-to-acorn": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", - "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/next": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/next/-/next-16.1.1.tgz", - "integrity": "sha512-QI+T7xrxt1pF6SQ/JYFz95ro/mg/1Znk5vBebsWwbpejj1T0A23hO7GYEaVac9QUOT2BIMiuzm0L99ooq7k0/w==", - "license": "MIT", - "peer": true, - "dependencies": { - "@next/env": "16.1.1", - "@swc/helpers": "0.5.15", - "baseline-browser-mapping": "^2.8.3", - "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", - "styled-jsx": "5.1.6" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=20.9.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "16.1.1", - "@next/swc-darwin-x64": "16.1.1", - "@next/swc-linux-arm64-gnu": "16.1.1", - "@next/swc-linux-arm64-musl": "16.1.1", - "@next/swc-linux-x64-gnu": "16.1.1", - "@next/swc-linux-x64-musl": "16.1.1", - "@next/swc-win32-arm64-msvc": "16.1.1", - "@next/swc-win32-x64-msvc": "16.1.1", - "sharp": "^0.34.4" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.51.1", - "babel-plugin-react-compiler": "*", - "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/next-themes": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", - "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" - } - }, - "node_modules/next/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/npm-to-yarn": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-to-yarn/-/npm-to-yarn-3.0.1.tgz", - "integrity": "sha512-tt6PvKu4WyzPwWUzy/hvPFqn+uwXO0K1ZHka8az3NnrhWJDmSqI8ncWq0fkL0k/lmmi5tAC11FXwXuh0rFbt1A==", - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/nebrelbug/npm-to-yarn?sponsor=1" - } - }, - "node_modules/oniguruma-parser": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", - "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", - "license": "MIT" - }, - "node_modules/oniguruma-to-es": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.4.tgz", - "integrity": "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==", - "license": "MIT", - "dependencies": { - "oniguruma-parser": "^0.12.1", - "regex": "^6.0.1", - "regex-recursion": "^6.0.2" - } - }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/react": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", - "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", - "license": "MIT", - "peer": true, - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.3" - } - }, - "node_modules/react-medium-image-zoom": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/react-medium-image-zoom/-/react-medium-image-zoom-5.4.0.tgz", - "integrity": "sha512-BsE+EnFVQzFIlyuuQrZ9iTwyKpKkqdFZV1ImEQN573QPqGrIUuNni7aF+sZwDcxlsuOMayCr6oO/PZR/yJnbRg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/rpearce" - } - ], - "license": "BSD-3-Clause", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-remove-scroll": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", - "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", - "license": "MIT", - "dependencies": { - "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.3", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.3" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", - "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", - "license": "MIT", - "dependencies": { - "react-style-singleton": "^2.2.2", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-style-singleton": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", - "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", - "license": "MIT", - "dependencies": { - "get-nonce": "^1.0.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/readdirp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", - "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/recma-build-jsx": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", - "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-build-jsx": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-jsx": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", - "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", - "license": "MIT", - "dependencies": { - "acorn-jsx": "^5.0.0", - "estree-util-to-js": "^2.0.0", - "recma-parse": "^1.0.0", - "recma-stringify": "^1.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/recma-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", - "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "esast-util-from-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-stringify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", - "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-to-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", - "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-recursion": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", - "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-utilities": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", - "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", - "license": "MIT" - }, - "node_modules/rehype-recma": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", - "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "hast-util-to-estree": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/remark/-/remark-15.0.1.tgz", - "integrity": "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-mdx": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", - "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", - "license": "MIT", - "dependencies": { - "mdast-util-mdx": "^3.0.0", - "micromark-extension-mdxjs": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/scroll-into-view-if-needed": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", - "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", - "license": "MIT", - "dependencies": { - "compute-scroll-into-view": "^3.0.2" - } - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, - "node_modules/shiki": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.20.0.tgz", - "integrity": "sha512-kgCOlsnyWb+p0WU+01RjkCH+eBVsjL1jOwUYWv0YDWkM2/A46+LDKVs5yZCUXjJG6bj4ndFoAg5iLIIue6dulg==", - "license": "MIT", - "dependencies": { - "@shikijs/core": "3.20.0", - "@shikijs/engine-javascript": "3.20.0", - "@shikijs/engine-oniguruma": "3.20.0", - "@shikijs/langs": "3.20.0", - "@shikijs/themes": "3.20.0", - "@shikijs/types": "3.20.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/style-to-js": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", - "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", - "license": "MIT", - "dependencies": { - "style-to-object": "1.0.14" - } - }, - "node_modules/style-to-object": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", - "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.2.7" - } - }, - "node_modules/styled-jsx": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", - "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", - "license": "MIT", - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/tailwind-merge": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", - "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "node_modules/tailwindcss": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", - "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", - "devOptional": true, - "license": "MIT", - "peer": true - }, - "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/tinyexec": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", - "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", - "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", - "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/use-callback-ref": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", - "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sidecar": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", - "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", - "license": "MIT", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/zod": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz", - "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/docs/package.json b/docs/package.json deleted file mode 100644 index 0793c70c7..000000000 --- a/docs/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "@hanzo/python-sdk-docs", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev --turbopack", - "build": "next build", - "start": "next start", - "lint": "eslint .", - "postinstall": "fumadocs-mdx" - }, - "dependencies": { - "@hanzo/mdx": "^14.2.5", - "@hanzo/ui": "^5.0.0", - "fumadocs-core": "^16.2.6", - "fumadocs-mdx": "^14.2.3", - "fumadocs-ui": "^16.2.6", - "lucide-react": "^0.468.0", - "next": "^16.1.1", - "next-themes": "^0.4.6", - "react": "^19.1.0", - "react-dom": "^19.1.0", - "shiki": "^3.0.0" - }, - "devDependencies": { - "@tailwindcss/postcss": "^4.1.0", - "@types/node": "^22.0.0", - "@types/react": "^19.0.0", - "@types/react-dom": "^19.0.0", - "postcss": "^8.5.0", - "tailwindcss": "^4.1.0", - "typescript": "^5.7.0" - } -} diff --git a/docs/postcss.config.js b/docs/postcss.config.js deleted file mode 100644 index e5640725a..000000000 --- a/docs/postcss.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - plugins: { - '@tailwindcss/postcss': {}, - }, -}; diff --git a/docs/public/apple-touch-icon.png b/docs/public/apple-touch-icon.png deleted file mode 100644 index d8cc44a71..000000000 Binary files a/docs/public/apple-touch-icon.png and /dev/null differ diff --git a/docs/public/favicon.png b/docs/public/favicon.png deleted file mode 100644 index 6fa8a46ee..000000000 Binary files a/docs/public/favicon.png and /dev/null differ diff --git a/docs/public/favicon.svg b/docs/public/favicon.svg deleted file mode 100644 index 07ed3c918..000000000 --- a/docs/public/favicon.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/docs/public/icon-192.png b/docs/public/icon-192.png deleted file mode 100644 index c9be620c2..000000000 Binary files a/docs/public/icon-192.png and /dev/null differ diff --git a/docs/public/icon-512.png b/docs/public/icon-512.png deleted file mode 100644 index 92ba2cb13..000000000 Binary files a/docs/public/icon-512.png and /dev/null differ diff --git a/docs/public/logo-white.svg b/docs/public/logo-white.svg deleted file mode 100644 index 91c87fe14..000000000 --- a/docs/public/logo-white.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/docs/public/logo.svg b/docs/public/logo.svg deleted file mode 100644 index 91c87fe14..000000000 --- a/docs/public/logo.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/docs/ref/agent/index.md b/docs/ref/agent/index.md deleted file mode 100644 index bcf004165..000000000 --- a/docs/ref/agent/index.md +++ /dev/null @@ -1,45 +0,0 @@ -# Agent Reference - -API reference for the hanzo-agent framework. - -## Overview - -hanzo-agent provides a multi-agent SDK with: - -- OpenAI-compatible API -- Multi-agent orchestration -- Tool use and guardrails -- Streaming and tracing - -## Documentation - -| Topic | Description | -|-------|-------------| -| [Getting Started](../../agent/index.md) | Introduction to agents | -| [Running Agents](../../agent/running_agents.md) | How to run agents | -| [Agent Config](../../agent/config.md) | Configuration options | -| [Tools](../../agent/tools.md) | Tool integration | -| [Guardrails](../../agent/guardrails.md) | Safety constraints | -| [Multi-Agent](../../agent/multi_agent.md) | Orchestrating multiple agents | -| [Handoffs](../../agent/handoffs.md) | Agent handoff patterns | -| [Streaming](../../agent/streaming.md) | Streaming responses | -| [Tracing](../../agent/tracing.md) | Debugging and tracing | - -## Quick Start - -```python -from hanzo_agent import Agent, Runner - -agent = Agent( - name="assistant", - instructions="You are a helpful assistant.", -) - -result = await Runner.run(agent, "Hello!") -print(result.final_output) -``` - -## See Also - -- [Agent Tools](../../tools/agent.md) - CLI agent integration -- [Consensus Protocol](../../lib/consensus.md) - Multi-agent agreement diff --git a/docs/ref/mcp/index.md b/docs/ref/mcp/index.md deleted file mode 100644 index 9d0d92051..000000000 --- a/docs/ref/mcp/index.md +++ /dev/null @@ -1,65 +0,0 @@ -# MCP Reference - -API reference for hanzo-mcp (Model Context Protocol). - -## Overview - -hanzo-mcp provides: - -- 30+ tools for AI development -- FastMCP-based server -- Claude Code integration -- Entry-point based tool discovery - -## Documentation - -| Topic | Description | -|-------|-------------| -| [Quickstart](../../mcp/quickstart.md) | Getting started | -| [Configuration](../../mcp/configuration.md) | Server configuration | -| [VS Code Setup](../../mcp/vscode.md) | IDE integration | -| [Tool Parity](../../mcp/PARITY.md) | Feature comparison | - -## Tool Categories - -| Category | Docs | -|----------|------| -| [Shell](../../mcp/tools/shell.md) | Command execution | -| [Filesystem](../../mcp/tools/filesystem.md) | File operations | -| [Browser](../../mcp/tools/browser.md) | Web automation | -| [Memory](../../mcp/tools/memory.md) | Persistent memory | -| [Reasoning](../../mcp/tools/reasoning.md) | Thinking tools | -| [Agent](../../mcp/tools/agent.md) | Multi-agent | -| [LSP](../../mcp/tools/lsp.md) | Code intelligence | -| [LLM](../../mcp/tools/llm-tools.md) | LLM interface | - -## Quick Start - -```bash -# Install -pip install hanzo-mcp[tools-all] - -# Run server -hanzo-mcp - -# Or with specific tools -hanzo-mcp --tools shell,browser,memory -``` - -## Claude Code Integration - -```json -{ - "mcpServers": { - "hanzo": { - "command": "hanzo-mcp", - "args": ["--stdio"] - } - } -} -``` - -## See Also - -- [Tools Reference](../tools/index.md) - Full tool documentation -- [Core Libraries](../../lib/index.md) - Supporting packages diff --git a/docs/ref/tools/index.md b/docs/ref/tools/index.md deleted file mode 100644 index 741f23071..000000000 --- a/docs/ref/tools/index.md +++ /dev/null @@ -1,58 +0,0 @@ -# Tools Reference - -Comprehensive API reference for all hanzo-tools-* packages. - -## Tool Packages - -| Package | Tools | Description | -|---------|-------|-------------| -| [Core](../../tools/core.md) | Base classes | Foundation for tool development | -| [Filesystem](../../tools/fs.md) | 7 | File operations and AST analysis | -| [Shell](../../tools/shell.md) | 12 | Command execution and process management | -| [Browser](../../tools/browser.md) | 1 (70+ actions) | Playwright automation | -| [Memory](../../tools/memory.md) | 9 | Persistent memory and knowledge bases | -| [Reasoning](../../tools/reasoning.md) | 2 | Structured thinking and analysis | -| [Agent](../../tools/agent.md) | 3 | Multi-agent orchestration | -| [LSP](../../tools/lsp.md) | 1 | Language server protocol | -| [Refactor](../../tools/refactor.md) | 1 | Code refactoring with AST/LSP | -| [LLM](../../tools/llm-tools.md) | 2 | Unified LLM interface | -| [Database](../../tools/database.md) | 8 | SQL and graph databases | -| [Vector](../../tools/vector.md) | 3 | Semantic search | -| [Jupyter](../../tools/jupyter.md) | 1 | Notebook operations | -| [Editor](../../tools/editor.md) | 3 | Neovim integration | -| [Todo](../../tools/todo.md) | 1 | Task management | -| [Computer](../../tools/computer.md) | 1 | Mac automation | -| [Config](../../tools/config.md) | 2 | Configuration management | -| [MCP](../../tools/mcp-tools.md) | 4 | MCP server management | - -## Quick Install - -```bash -# All tools -pip install hanzo-mcp[tools-all] - -# Core + dev tools -pip install hanzo-mcp[tools-dev] - -# Specific packages -pip install hanzo-tools-shell hanzo-tools-browser -``` - -## Tool Discovery - -Tools are discovered via entry points: - -```python -from importlib.metadata import entry_points - -# Get all tool entry points -eps = entry_points(group="hanzo.tools") -for name, ep in eps.items(): - tools = ep.load() - print(f"{name}: {len(tools)} tools") -``` - -## See Also - -- [MCP Configuration](../../mcp/configuration.md) -- [MCP Quickstart](../../mcp/quickstart.md) diff --git a/docs/source.config.ts b/docs/source.config.ts deleted file mode 100644 index 8a6526a2f..000000000 --- a/docs/source.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { defineDocs, defineConfig } from 'fumadocs-mdx/config'; - -export const docs = defineDocs({ - docs: { - async: true, - }, -}); - -export default defineConfig({ - mdxOptions: { - rehypeCodeOptions: { - themes: { - light: 'github-light', - dark: 'github-dark', - }, - }, - }, -}); diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css deleted file mode 100644 index f408ed3ba..000000000 --- a/docs/stylesheets/extra.css +++ /dev/null @@ -1,480 +0,0 @@ -/* Geist Font - Vercel's font used by shadcn/ui */ -@font-face { - font-family: 'Geist'; - src: url('https://cdn.jsdelivr.net/npm/geist@1.3.0/dist/fonts/geist-sans/Geist-Regular.woff2') format('woff2'); - font-weight: 400; - font-style: normal; - font-display: swap; -} - -@font-face { - font-family: 'Geist'; - src: url('https://cdn.jsdelivr.net/npm/geist@1.3.0/dist/fonts/geist-sans/Geist-Medium.woff2') format('woff2'); - font-weight: 500; - font-style: normal; - font-display: swap; -} - -@font-face { - font-family: 'Geist'; - src: url('https://cdn.jsdelivr.net/npm/geist@1.3.0/dist/fonts/geist-sans/Geist-SemiBold.woff2') format('woff2'); - font-weight: 600; - font-style: normal; - font-display: swap; -} - -@font-face { - font-family: 'Geist'; - src: url('https://cdn.jsdelivr.net/npm/geist@1.3.0/dist/fonts/geist-sans/Geist-Bold.woff2') format('woff2'); - font-weight: 700; - font-style: normal; - font-display: swap; -} - -@font-face { - font-family: 'Geist Mono'; - src: url('https://cdn.jsdelivr.net/npm/geist@1.3.0/dist/fonts/geist-mono/GeistMono-Regular.woff2') format('woff2'); - font-weight: 400; - font-style: normal; - font-display: swap; -} - -@font-face { - font-family: 'Geist Mono'; - src: url('https://cdn.jsdelivr.net/npm/geist@1.3.0/dist/fonts/geist-mono/GeistMono-Medium.woff2') format('woff2'); - font-weight: 500; - font-style: normal; - font-display: swap; -} - -/* Root variables - Light mode defaults */ -:root { - /* Font families */ - --md-text-font: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - --md-code-font: 'Geist Mono', ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace; - - /* shadcn/ui inspired colors - light mode */ - --md-primary-fg-color: #09090b; - --md-primary-fg-color--light: #18181b; - --md-primary-fg-color--dark: #000000; - --md-primary-bg-color: #ffffff; - --md-primary-bg-color--light: #fafafa; - - --md-accent-fg-color: #09090b; - --md-accent-fg-color--transparent: rgba(9, 9, 11, 0.1); - --md-accent-bg-color: #f4f4f5; - - --md-default-fg-color: #09090b; - --md-default-fg-color--light: #71717a; - --md-default-fg-color--lighter: #a1a1aa; - --md-default-fg-color--lightest: #e4e4e7; - --md-default-bg-color: #ffffff; - --md-default-bg-color--light: #fafafa; - --md-default-bg-color--lighter: #f4f4f5; - --md-default-bg-color--lightest: #e4e4e7; - - /* Code */ - --md-code-fg-color: #09090b; - --md-code-bg-color: #f4f4f5; - --md-code-hl-color: rgba(9, 9, 11, 0.1); - - /* Footer */ - --md-footer-fg-color: #71717a; - --md-footer-fg-color--light: #a1a1aa; - --md-footer-fg-color--lighter: #d4d4d8; - --md-footer-bg-color: #09090b; - --md-footer-bg-color--dark: #000000; - - /* Typography */ - --md-typeset-color: #09090b; - --md-typeset-a-color: #09090b; -} - -/* Dark mode (slate scheme) - Pure black like shadcn */ -[data-md-color-scheme="slate"] { - /* Pure black background like shadcn dark mode */ - --md-default-bg-color: #09090b; - --md-default-bg-color--light: #18181b; - --md-default-bg-color--lighter: #27272a; - --md-default-bg-color--lightest: #3f3f46; - - /* Light text on dark */ - --md-default-fg-color: #fafafa; - --md-default-fg-color--light: #a1a1aa; - --md-default-fg-color--lighter: #71717a; - --md-default-fg-color--lightest: #52525b; - - /* Primary colors */ - --md-primary-fg-color: #fafafa; - --md-primary-fg-color--light: #e4e4e7; - --md-primary-fg-color--dark: #ffffff; - --md-primary-bg-color: #09090b; - --md-primary-bg-color--light: #18181b; - - /* Accent */ - --md-accent-fg-color: #fafafa; - --md-accent-fg-color--transparent: rgba(250, 250, 250, 0.1); - --md-accent-bg-color: #27272a; - - /* Code blocks */ - --md-code-fg-color: #fafafa; - --md-code-bg-color: #18181b; - --md-code-hl-color: rgba(250, 250, 250, 0.1); - - /* Typography */ - --md-typeset-color: #fafafa; - --md-typeset-a-color: #fafafa; - - /* Footer */ - --md-footer-bg-color: #000000; - --md-footer-bg-color--dark: #000000; - - /* Search */ - --md-search-result-icon-color: #71717a; -} - -/* Search box styling - dark mode */ -[data-md-color-scheme="slate"] .md-search__form { - background-color: #18181b; - border: 1px solid #27272a; - border-radius: 0.5rem; -} - -[data-md-color-scheme="slate"] .md-search__input { - background-color: transparent; - color: #fafafa; -} - -[data-md-color-scheme="slate"] .md-search__input::placeholder { - color: #71717a; -} - -[data-md-color-scheme="slate"] .md-search__icon { - color: #71717a; -} - -/* Search modal (Cmd+K) styling */ -[data-md-color-scheme="slate"] .md-search__output { - background-color: #18181b; - border: 1px solid #27272a; - border-radius: 0.5rem; - box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); -} - -[data-md-color-scheme="slate"] .md-search-result { - background-color: transparent; -} - -[data-md-color-scheme="slate"] .md-search-result__meta { - background-color: #09090b; - color: #71717a; -} - -[data-md-color-scheme="slate"] .md-search-result__item { - border-bottom: 1px solid #27272a; -} - -[data-md-color-scheme="slate"] .md-search-result__link { - background-color: transparent; -} - -[data-md-color-scheme="slate"] .md-search-result__link:hover, -[data-md-color-scheme="slate"] .md-search-result__link:focus { - background-color: #27272a; -} - -[data-md-color-scheme="slate"] .md-search-result__article { - background-color: transparent; -} - -[data-md-color-scheme="slate"] .md-search-result__title { - color: #fafafa; -} - -[data-md-color-scheme="slate"] .md-search-result__teaser { - color: #a1a1aa; -} - -/* Search shortcut hint (โŒ˜K) */ -.md-search__input::placeholder { - opacity: 1; -} - -[data-md-color-scheme="slate"] .md-search__scrollwrap { - background-color: #18181b; -} - -/* Sidebar navigation - remove blue links */ -[data-md-color-scheme="slate"] .md-nav__link { - color: #a1a1aa; -} - -[data-md-color-scheme="slate"] .md-nav__link:hover { - color: #fafafa; -} - -[data-md-color-scheme="slate"] .md-nav__link--active, -[data-md-color-scheme="slate"] .md-nav__item--active > .md-nav__link { - color: #fafafa; - font-weight: 600; -} - -/* Remove all blue accent colors */ -[data-md-color-scheme="slate"] .md-nav__link[data-md-state="blur"] { - color: #a1a1aa; -} - -[data-md-color-scheme="slate"] .md-typeset a { - color: #fafafa; -} - -[data-md-color-scheme="slate"] .md-typeset a:hover { - color: #ffffff; -} - -/* Header - pure black */ -.md-header { - background-color: #000000; - border-bottom: 1px solid #27272a; -} - -.md-header--shadow { - box-shadow: none; -} - -/* Tabs in header */ -.md-tabs { - background-color: #000000; -} - -.md-tabs__link { - color: #a1a1aa !important; - font-weight: 500; - opacity: 1 !important; -} - -.md-tabs__link:hover, -.md-tabs__link--active { - color: #fafafa !important; -} - -/* Header text and icons */ -.md-header__title { - color: #fafafa; -} - -.md-header__button { - color: #fafafa; -} - -.md-header__source { - color: #a1a1aa; -} - -/* Search input in header */ -.md-search__input { - color: #fafafa; -} - -.md-search__input::placeholder { - color: #71717a; -} - -/* Navigation sidebar */ -[data-md-color-scheme="slate"] .md-sidebar { - background-color: #09090b; -} - -[data-md-color-scheme="slate"] .md-nav { - background-color: transparent; -} - -/* Typography */ -.md-typeset { - font-size: 0.9rem; - line-height: 1.7; -} - -.md-typeset h1, -.md-typeset h2, -.md-typeset h3, -.md-typeset h4, -.md-typeset h5, -.md-typeset h6 { - font-weight: 600; - letter-spacing: -0.02em; - color: var(--md-default-fg-color); -} - -.md-typeset h1 { - font-size: 2rem; - margin-bottom: 1rem; -} - -.md-typeset h2 { - font-size: 1.5rem; - margin-top: 2rem; - padding-bottom: 0.5rem; - border-bottom: 1px solid var(--md-default-fg-color--lightest); -} - -.md-typeset h3 { - font-size: 1.25rem; - margin-top: 1.5rem; -} - -.md-typeset p, -.md-typeset li { - font-size: 0.95rem; - color: var(--md-default-fg-color--light); -} - -[data-md-color-scheme="slate"] .md-typeset p, -[data-md-color-scheme="slate"] .md-typeset li { - color: #a1a1aa; -} - -/* Links */ -.md-typeset a { - color: var(--md-typeset-a-color); - text-decoration: none; - border-bottom: 1px solid var(--md-default-fg-color--lightest); - transition: border-color 0.2s; -} - -.md-typeset a:hover { - border-color: var(--md-default-fg-color); -} - -/* Code blocks - shadcn style */ -.md-typeset code { - font-family: var(--md-code-font); - font-size: 0.875em; - padding: 0.2em 0.4em; - border-radius: 0.375rem; - background-color: var(--md-code-bg-color); - color: var(--md-code-fg-color); -} - -.md-typeset pre { - border-radius: 0.5rem; - border: 1px solid var(--md-default-fg-color--lightest); -} - -.md-typeset pre > code { - font-size: 0.875rem; - padding: 1rem; -} - -/* Tables */ -.md-typeset table:not([class]) { - font-size: 0.875rem; - border: 1px solid var(--md-default-fg-color--lightest); - border-radius: 0.5rem; - overflow: hidden; -} - -.md-typeset table:not([class]) th { - background-color: var(--md-default-bg-color--lighter); - font-weight: 600; -} - -.md-typeset table:not([class]) td, -.md-typeset table:not([class]) th { - border-color: var(--md-default-fg-color--lightest); - padding: 0.75rem 1rem; -} - -/* Admonitions - shadcn card style */ -.md-typeset .admonition, -.md-typeset details { - border: 1px solid var(--md-default-fg-color--lightest); - border-radius: 0.5rem; - background-color: var(--md-default-bg-color--light); - box-shadow: none; -} - -.md-typeset .admonition-title, -.md-typeset summary { - background-color: transparent; - border-bottom: 1px solid var(--md-default-fg-color--lightest); - font-weight: 600; -} - -/* Navigation */ -.md-nav__link { - font-size: 0.875rem; -} - -.md-nav__link--active { - font-weight: 600; -} - -/* Search */ -.md-search__input { - background-color: var(--md-default-bg-color--lighter); - border-radius: 0.5rem; -} - -[data-md-color-scheme="slate"] .md-search__input { - background-color: #18181b; -} - -/* Content area max-width */ -.md-content { - max-width: 900px; -} - -/* Hide footer */ -.md-footer { - display: none; -} - -/* Logo sizing */ -.md-header__button.md-logo img, -.md-header__button.md-logo svg { - height: 1.5rem; - width: auto; -} - -/* TOC on the right */ -.md-sidebar--secondary { - border-left: 1px solid var(--md-default-fg-color--lightest); -} - -/* Clipboard button */ -.md-clipboard { - color: var(--md-default-fg-color--lighter); -} - -/* Tab styling */ -.md-typeset .tabbed-labels > label { - font-size: 0.875rem; - font-weight: 500; -} - -/* Button-like elements */ -.md-typeset .md-button { - border-radius: 0.375rem; - font-weight: 500; - padding: 0.5rem 1rem; -} - -/* Scrollbar styling for dark mode */ -[data-md-color-scheme="slate"] ::-webkit-scrollbar { - width: 8px; - height: 8px; -} - -[data-md-color-scheme="slate"] ::-webkit-scrollbar-track { - background: #18181b; -} - -[data-md-color-scheme="slate"] ::-webkit-scrollbar-thumb { - background: #3f3f46; - border-radius: 4px; -} - -[data-md-color-scheme="slate"] ::-webkit-scrollbar-thumb:hover { - background: #52525b; -} diff --git a/docs/tools/agent.md b/docs/tools/agent.md deleted file mode 100644 index a6ef96a72..000000000 --- a/docs/tools/agent.md +++ /dev/null @@ -1,363 +0,0 @@ -# hanzo-tools-agent - -Multi-agent orchestration with CLI spawning, DAG execution, swarm distribution, and Metastable consensus. - -## Installation - -```bash -pip install hanzo-tools-agent -``` - -With optional features: - -```bash -pip install hanzo-tools-agent[api] # httpx for direct API mode -pip install hanzo-tools-agent[perf] # uvloop for high performance -pip install hanzo-tools-agent[full] # All features -``` - -## Overview - -`hanzo-tools-agent` provides: - -- **agent** - Multi-agent orchestration (run, dag, swarm, consensus, dispatch) -- **iching** - I Ching wisdom for engineering decisions -- **review** - Code review tool - -## Quick Start - -```python -# Run a single agent -agent(action="run", name="claude", prompt="Explain this code") - -# List available agents -agent(action="list") - -# Run consensus across multiple models -agent(action="consensus", prompt="Best approach for caching?", agents=["claude", "gemini", "codex"]) - -# Swarm pattern - distribute work -agent(action="swarm", items=["file1.py", "file2.py"], template="Review {item}") - -# DAG execution with dependencies -agent(action="dag", tasks=[ - {"id": "analyze", "prompt": "Analyze the codebase"}, - {"id": "plan", "prompt": "Create implementation plan", "after": ["analyze"]} -]) -``` - -## Available Agents - -| Agent | Description | API Key Env | -|-------|-------------|-------------| -| `claude` | Anthropic Claude Code CLI | `ANTHROPIC_API_KEY` | -| `codex` | OpenAI Codex CLI | `OPENAI_API_KEY` | -| `gemini` | Google Gemini CLI | `GOOGLE_API_KEY` | -| `grok` | xAI Grok CLI | `XAI_API_KEY` | -| `qwen` | Alibaba Qwen CLI | `DASHSCOPE_API_KEY` | -| `vibe` | Vibe coding agent | - | -| `dev` | Hanzo Dev agent | - | - -## Actions Reference - -### run - -Run a single agent with a prompt. - -```python -# Default agent (claude in Claude Code environment) -agent(action="run", prompt="Explain this error") - -# Specific agent -agent(action="run", name="gemini", prompt="Review this code") - -# With working directory -agent(action="run", name="codex", prompt="Fix the tests", cwd="/project/path") - -# With timeout -agent(action="run", name="claude", prompt="Complex task", timeout=300) -``` - -**Parameters:** -- `name`: Agent to use (default: auto-detect) -- `prompt` (required): Task for the agent -- `cwd`: Working directory -- `timeout`: Timeout in seconds (default: 300) - -### dag - -Execute tasks with dependencies using a DAG (Directed Acyclic Graph). - -```python -agent( - action="dag", - tasks=[ - {"id": "research", "prompt": "Research best practices for caching"}, - {"id": "design", "prompt": "Design cache architecture", "after": ["research"]}, - {"id": "implement", "prompt": "Implement the cache", "after": ["design"]}, - {"id": "test", "prompt": "Write tests", "after": ["implement"]} - ] -) -``` - -**Task format:** -- `id` (required): Unique task identifier -- `prompt` (required): Task description -- `after`: List of task IDs that must complete first -- `agent`: Specific agent for this task (optional) - -### swarm - -Distribute work across multiple agents in parallel. - -```python -# Process multiple files -agent( - action="swarm", - items=["auth.py", "api.py", "models.py"], - template="Review {item} for security issues", - max_concurrent=5 -) - -# Multiple prompts -agent( - action="swarm", - items=["Add error handling", "Add logging", "Add tests"], - template="{item} to the authentication module" -) -``` - -**Parameters:** -- `items` (required): List of items to process -- `template` (required): Prompt template with `{item}` placeholder -- `max_concurrent`: Maximum parallel agents (default: 100) - -### consensus - -Run Metastable consensus across multiple models. - -```python -agent( - action="consensus", - prompt="What's the best database for this use case?", - agents=["claude", "gemini", "codex"], - rounds=3, # Consensus rounds - k=3, # Sample size per round - alpha=0.6, # Agreement threshold - beta_1=0.5, # Preference threshold - beta_2=0.8 # Decision threshold -) -``` - -**Parameters:** -- `prompt` (required): Question for consensus -- `agents`: Models to participate (default: all available) -- `rounds`: Number of consensus rounds (default: 3) -- `k`: Sample size per round (default: 3) -- `alpha`: Agreement threshold (default: 0.6) -- `beta_1`: Phase I preference threshold (default: 0.5) -- `beta_2`: Phase II decision threshold (default: 0.8) - -**Consensus Protocol:** -Based on [Metastable Consensus](https://github.com/luxfi/consensus): -- Phase I (Sampling): k-peer sampling, confidence accumulation -- Phase II (Finality): Threshold aggregation, winner synthesis - -### dispatch - -Route different tasks to different agents. - -```python -agent( - action="dispatch", - tasks=[ - {"agent": "claude", "prompt": "Review code quality"}, - {"agent": "codex", "prompt": "Suggest optimizations"}, - {"agent": "gemini", "prompt": "Check documentation"} - ] -) -``` - -### list - -List all available agents. - -```python -agent(action="list") -``` - -**Response:** -```json -{ - "agents": ["claude", "codex", "gemini", "grok", "qwen", "vibe", "dev"], - "available": ["claude", "gemini"], - "configured": ["claude", "codex", "gemini"] -} -``` - -### status - -Check if a specific agent is available. - -```python -agent(action="status", name="claude") -``` - -### config - -Show agent configuration. - -```python -agent(action="config") -``` - -## Agent Configuration - -### Config Files - -Configure agents via `~/.hanzo/agents/.json`: - -```json -{ - "cmd": "claude", - "args": ["--print", "--dangerously-skip-permissions"], - "env_key": "ANTHROPIC_API_KEY", - "max_turns": 999, - "session": true, - "model": "claude-3-opus", - "system_prompt": "You are a helpful assistant" -} -``` - -### Environment Overrides - -Override arguments via environment: - -```bash -export HANZO_AGENT_CLAUDE_ARGS="--verbose --model claude-3-5-sonnet" -``` - -### API Mode - -Configure direct API calls (no CLI needed): - -```json -{ - "endpoint": "https://api.openai.com/v1/chat/completions", - "api_type": "openai", - "model": "gpt-4", - "env_key": "OPENAI_API_KEY", - "system_prompt": "You are a helpful assistant" -} -``` - -## YOLO Mode - -All agents are configured with autonomous operation flags: - -| Agent | YOLO Flags | -|-------|------------| -| claude | `--dangerously-skip-permissions`, `--print`, `--output-format text` | -| codex | `--full-auto` | -| gemini | `-y`, `-q` | -| grok | `-y` | -| qwen | `--approval-mode yolo`, `-p` | -| vibe | `--auto-approve`, `--max-turns 999`, `-p` | - -## Auto-Backgrounding - -Long-running agents automatically background after timeout: - -```python -# Long task - will auto-background -agent(action="run", name="claude", prompt="Complex refactoring task", timeout=300) - -# Check status with ps tool -ps() # List all processes -ps(logs="agent_xxx") # View output -ps(kill="agent_xxx") # Stop process -``` - -## IChingTool - -Apply I Ching wisdom to engineering challenges. - -```python -iching(challenge="How should I approach refactoring this legacy codebase?") -``` - -**Response:** -- Hexagram interpretation -- Relevant Hanzo principles -- Actionable recommendations - -## ReviewTool - -Request balanced code review. - -```python -review( - focus="FUNCTIONALITY", - work_description="Implemented auto-import feature for Go files", - code_snippets=["func AddImport(file string) error { ... }"], - file_paths=["/path/to/import_handler.go"], - context="This will be used to automatically fix missing imports" -) -``` - -**Focus areas:** -- `GENERAL` - Overall code quality -- `FUNCTIONALITY` - Does it work correctly? -- `READABILITY` - Is it easy to understand? -- `MAINTAINABILITY` - Is it easy to modify? -- `TESTING` - Is it well tested? -- `DOCUMENTATION` - Is it well documented? -- `ARCHITECTURE` - Is the design sound? - -## Examples - -### Multi-Agent Code Review - -```python -# Get perspectives from multiple agents -agent( - action="consensus", - prompt="Review this pull request for issues", - agents=["claude", "gemini", "codex"] -) -``` - -### Parallel File Processing - -```python -# Review all files in parallel -agent( - action="swarm", - items=["src/auth.py", "src/api.py", "src/models.py", "src/utils.py"], - template="Review {item} and suggest improvements", - max_concurrent=4 -) -``` - -### Sequential Workflow - -```python -# Plan โ†’ Implement โ†’ Test -agent( - action="dag", - tasks=[ - {"id": "plan", "prompt": "Create implementation plan for user auth"}, - {"id": "implement", "prompt": "Implement the plan", "after": ["plan"]}, - {"id": "test", "prompt": "Write comprehensive tests", "after": ["implement"]}, - {"id": "review", "prompt": "Review implementation", "after": ["test"]} - ] -) -``` - -## Best Practices - -1. **Use consensus for decisions** - Multiple perspectives reduce bias -2. **Use swarm for bulk operations** - Parallel processing is faster -3. **Use DAG for workflows** - Dependencies ensure correct ordering -4. **Configure timeouts appropriately** - Complex tasks need more time -5. **Check agent availability** - Use `status` before assuming an agent exists diff --git a/docs/tools/browser.md b/docs/tools/browser.md deleted file mode 100644 index 17aabb0ba..000000000 --- a/docs/tools/browser.md +++ /dev/null @@ -1,350 +0,0 @@ -# hanzo-tools-browser - -Complete browser automation with full Playwright API. Provides 70+ actions for navigation, forms, mouse control, assertions, and more. - -## Installation - -```bash -pip install hanzo-tools-browser -playwright install chromium -``` - -Or as part of the full toolkit: - -```bash -pip install hanzo-mcp[tools-all] -playwright install chromium -``` - -## Overview - -`hanzo-tools-browser` provides comprehensive browser automation: - -- **Navigation**: navigate, reload, go_back, go_forward -- **Input**: click, type, fill, press, select_option -- **Mouse**: hover, drag, scroll, mouse_move -- **Touch**: tap, swipe, pinch (mobile emulation) -- **Assertions**: expect_visible, expect_text, expect_url -- **Content**: get_text, get_attribute, screenshot -- **Storage**: cookies, localStorage, sessionStorage -- **Network**: route (mock/block requests) - -## Quick Start - -```python -# Navigate to page -browser(action="navigate", url="https://example.com") - -# Click a button -browser(action="click", selector="button.submit") - -# Fill a form -browser(action="fill", selector="input[name='email']", text="user@example.com") - -# Take screenshot -browser(action="screenshot", full_page=True) -``` - -## Device Emulation - -Built-in device presets for responsive testing: - -```python -# User-friendly aliases -browser(action="emulate", device="mobile") # iPhone-like (390x844) -browser(action="emulate", device="tablet") # iPad-like (1024x1366) -browser(action="emulate", device="laptop") # MacBook-like (1440x900) -browser(action="emulate", device="desktop") # Full HD (1920x1080) - -# Specific devices -browser(action="emulate", device="iphone_14") -browser(action="emulate", device="pixel_7") -browser(action="emulate", device="ipad_pro") -``` - -## Navigation - -```python -# Basic navigation -browser(action="navigate", url="https://example.com") -browser(action="reload") -browser(action="go_back") -browser(action="go_forward") - -# Get page info -browser(action="url") # Current URL -browser(action="title") # Page title -browser(action="content") # HTML content -``` - -## Input Actions - -```python -# Click variants -browser(action="click", selector="button") -browser(action="dblclick", selector=".item") -browser(action="right_click", selector=".context-menu") - -# Text input -browser(action="type", selector="input", text="Hello", interval=0.1) -browser(action="fill", selector="input", text="Instant fill") -browser(action="clear", selector="input") - -# Keyboard -browser(action="press", key="Enter") -browser(action="press", key="Control+c") -``` - -## Form Handling - -```python -# Select dropdowns -browser(action="select_option", selector="select", value="option1") - -# Checkboxes -browser(action="check", selector="input[type='checkbox']") -browser(action="uncheck", selector="input[type='checkbox']") - -# File uploads -browser(action="upload", selector="input[type='file']", files=["./doc.pdf"]) -``` - -## Mouse Control - -```python -# Hover -browser(action="hover", selector=".menu-item") - -# Drag and drop -browser(action="drag", selector=".draggable", target_selector=".dropzone") - -# Scroll -browser(action="scroll", delta_y=500) -browser(action="scroll", selector=".container", delta_y=300) - -# Mouse coordinates -browser(action="mouse_move", x=100, y=200) -browser(action="mouse_down") -browser(action="mouse_up") -``` - -## Touch & Mobile - -```python -# Touch actions -browser(action="tap", selector="button") -browser(action="swipe", direction="up", distance=300) -browser(action="pinch", scale=0.5) # Zoom out -browser(action="pinch", scale=2.0) # Zoom in -``` - -## Locators - -```python -# CSS/XPath -browser(action="locator", selector="div.class") -browser(action="locator", selector="//button[@id='submit']") - -# Semantic locators -browser(action="get_by_role", role="button", name="Submit") -browser(action="get_by_text", text="Click me") -browser(action="get_by_label", text="Email") -browser(action="get_by_placeholder", text="Enter email") -browser(action="get_by_test_id", text="submit-btn") - -# Composition -browser(action="first", selector=".items") -browser(action="last", selector=".items") -browser(action="nth", selector=".items", index=2) -browser(action="filter", selector=".items", has_text="Important") -``` - -## Assertions - -```python -# Element assertions -browser(action="expect_visible", selector=".modal") -browser(action="expect_hidden", selector=".loading") -browser(action="expect_enabled", selector="button") -browser(action="expect_text", selector="h1", expected="Welcome") -browser(action="expect_count", selector=".items", index=5) - -# Page assertions -browser(action="expect_url", expected="*/dashboard*") -browser(action="expect_title", expected="Dashboard") - -# Negative assertions -browser(action="expect_visible", selector=".loading", not_=True) -``` - -## Content Extraction - -```python -# Get text content -browser(action="get_text", selector="h1") -browser(action="get_inner_text", selector=".content") - -# Get attributes -browser(action="get_attribute", selector="a", attribute="href") -browser(action="get_value", selector="input") - -# Get HTML -browser(action="get_html", selector=".container") - -# Get bounding box -browser(action="get_bounding_box", selector=".element") -``` - -## State Checking - -```python -browser(action="is_visible", selector=".modal") -browser(action="is_hidden", selector=".loading") -browser(action="is_enabled", selector="button") -browser(action="is_editable", selector="input") -browser(action="is_checked", selector="input[type='checkbox']") -``` - -## Screenshots & PDFs - -```python -# Screenshots -browser(action="screenshot") -browser(action="screenshot", full_page=True) -browser(action="screenshot", selector=".chart") - -# PDF export -browser(action="pdf") -``` - -## Wait Operations - -```python -# Wait for load states -browser(action="wait_for_load", state="networkidle") -browser(action="wait_for_url", url="*/success*") - -# Wait for elements -browser(action="wait", selector=".loaded", state="visible") -browser(action="wait", timeout=5000) - -# Wait for events -browser(action="wait_for_event", event="download") -browser(action="wait_for_response", pattern="*/api/*") -``` - -## Network Interception - -```python -# Mock API responses -browser( - action="route", - pattern="*/api/users*", - response={"users": [{"name": "Mock User"}]}, - status_code=200 -) - -# Block requests -browser(action="route", pattern="*.png", block=True) - -# Remove route -browser(action="unroute", pattern="*/api/users*") -``` - -## Storage - -```python -# Cookies -browser(action="cookies") -browser(action="cookies", cookies=[{"name": "token", "value": "abc"}]) -browser(action="clear_cookies") - -# Local/Session storage -browser(action="storage", storage_type="local") -browser(action="storage", storage_type="session", storage_data={"key": "value"}) - -# Save/restore auth state -browser(action="storage_state", auth_file="./auth.json") -``` - -## Tab Management - -```python -# Multiple tabs -browser(action="new_tab", url="https://example.com") -browser(action="tabs") # List all tabs -browser(action="close_tab", tab_index=1) -``` - -## Parallel Agents - -For parallel execution, create isolated contexts: - -```python -# Create new context (isolated cookies/storage) -browser(action="new_context") - -# Each agent uses separate context -# One Chrome process, many parallel sessions -``` - -## Configuration - -### Headless Mode - -```python -# Toggle headless mode -browser(action="set_headless", headless=True) -browser(action="set_headless", headless=False) # Show browser -``` - -### CDP Connection - -Share browser instance across MCPs: - -```bash -BROWSER_CDP_ENDPOINT=http://localhost:9222 hanzo-mcp -``` - -## Examples - -### Login Flow - -```python -# Navigate to login -browser(action="navigate", url="https://app.example.com/login") - -# Fill credentials -browser(action="fill", selector="input[name='email']", text="user@example.com") -browser(action="fill", selector="input[name='password']", text="password123") - -# Submit -browser(action="click", selector="button[type='submit']") - -# Wait for redirect -browser(action="wait_for_url", url="*/dashboard*") - -# Verify login -browser(action="expect_visible", selector=".user-menu") -``` - -### E2E Test - -```python -# Test shopping cart -browser(action="navigate", url="https://shop.example.com") -browser(action="click", selector=".product-card:first-child button") -browser(action="expect_text", selector=".cart-count", expected="1") -browser(action="click", selector=".cart-icon") -browser(action="expect_visible", selector=".cart-modal") -browser(action="expect_text", selector=".total", expected="$99.00") -``` - -### Screenshot Comparison - -```python -# Capture baseline -browser(action="navigate", url="https://example.com") -browser(action="screenshot", full_page=True) -# Returns base64 image for comparison -``` diff --git a/docs/tools/code.md b/docs/tools/code.md deleted file mode 100644 index 12e3e6793..000000000 --- a/docs/tools/code.md +++ /dev/null @@ -1,155 +0,0 @@ -# Code Tool - -Symbol and structure operations for source code (HIP-0300 operator). - -## Installation - -```bash -pip install hanzo-tools-code -``` - -## Overview - -The `code` tool handles all code analysis and transformation operations: - -| Action | Signature | Effect | -|--------|-----------|--------| -| `parse` | `(Path \| Text, lang?) โ†’ AST` | PURE | -| `serialize` | `AST โ†’ Text` | PURE | -| `symbols` | `(Path \| AST, kind?) โ†’ [Symbol]` | PURE | -| `definition` | `(Path, Position) โ†’ [Location]` | DETERMINISTIC | -| `references` | `(Path, Position) โ†’ [Location]` | DETERMINISTIC | -| `transform` | `(Path \| Text, kind, params) โ†’ Patch` | PURE | -| `summarize` | `(Diff \| Log \| Report) โ†’ Summary` | PURE | - -## Actions - -### parse - -Parse source code into an Abstract Syntax Tree using tree-sitter. - -```python -code(action="parse", path="/src/main.py") -# Returns: {tree: {...}, lang: "python", node_count: 142} - -code(action="parse", text="def foo(): pass", lang="python") -# Returns: {tree: {...}, lang: "python", node_count: 5} -``` - -**Parameters:** -- `path` (str, optional): Path to source file -- `text` (str, optional): Source code text (provide one of path or text) -- `lang` (str, optional): Language hint (auto-detected from extension) - -### serialize - -Convert AST back to source code. - -```python -code(action="serialize", tree=ast_tree, lang="python") -# Returns: {text: "def foo(): pass", size: 16} -``` - -### symbols - -Extract symbols (functions, classes, variables) from code. - -```python -code(action="symbols", path="/src/main.py") -# Returns: {symbols: [{name: "foo", kind: "function", line: 1}, ...]} - -code(action="symbols", path="/src/main.py", kind="class") -# Returns: {symbols: [{name: "MyClass", kind: "class", line: 10}]} -``` - -**Parameters:** -- `path` (str, optional): Path to source file -- `text` (str, optional): Source code text -- `kind` (str, optional): Filter by symbol kind (function, class, variable, etc.) - -### definition - -Find the definition of a symbol at a position (requires LSP). - -```python -code(action="definition", path="/src/main.py", line=42, col=10) -# Returns: {locations: [{uri: "file:///src/utils.py", line: 15, col: 0}]} -``` - -### references - -Find all references to a symbol (requires LSP). - -```python -code(action="references", path="/src/main.py", line=42, col=10) -# Returns: {locations: [{uri: "...", line: ..., col: ...}, ...], count: 7} -``` - -### transform - -Apply a transformation and produce a Patch (PURE - no side effects). - -```python -# Rename a symbol -code(action="transform", path="/src/main.py", kind="rename", - old_name="authenticate", new_name="verify_user") -# Returns: {patch: [...], base_hash: "sha256:abc...", new_hash: "sha256:def...", changes_count: 5} - -# Extract a function -code(action="transform", path="/src/main.py", kind="extract", - start_line=10, end_line=20, new_name="helper_function") -# Returns: {patch: [...], base_hash: "...", new_hash: "..."} -``` - -**Transform Kinds:** -- `rename` - Rename a symbol (old_name, new_name) -- `extract` - Extract code to function (start_line, end_line, new_name) -- `inline` - Inline a function/variable -- `move` - Move symbol to another file - -### summarize - -Compress diff/log/report into actionable summary. - -```python -code(action="summarize", diff=patch_content) -# Returns: {summary: "Renamed authenticate to verify_user in 5 locations", -# risks: ["Breaking change for external callers"], -# next_actions: ["Update API documentation", "Run integration tests"]} - -code(action="summarize", log=[{"sha": "abc", "message": "..."}]) -# Returns: {summary: "3 commits: added auth, fixed bug, updated tests", ...} -``` - -## Transform vs Apply - -The `code.transform` action is PURE - it produces a Patch value without modifying files. To apply the patch: - -```python -# 1. Generate transform (PURE) -result = code(action="transform", path="/src/main.py", kind="rename", - old_name="foo", new_name="bar") - -# 2. Review the patch -print(result["patch"]) -print(result["summary"]) - -# 3. Apply with precondition (EFFECT) -fs(action="patch", path="/src/main.py", patch=result["patch"], - base_hash=result["base_hash"]) # Fails if file changed -``` - -## Language Support - -Tree-sitter languages supported: -- Python, JavaScript, TypeScript, Rust, Go, C, C++, Java -- JSON, YAML, TOML, Markdown, HTML, CSS - -Languages auto-detected from file extensions. - -## See Also - -- [HIP-0300](../hip/HIP-0300.md) - Unified Tools Architecture -- [Filesystem Tool](fs.md) - For applying patches with `fs.patch` -- [LSP Tool](lsp.md) - For additional semantic operations -- [Refactor Tool](refactor.md) - High-level refactoring operations diff --git a/docs/tools/computer.md b/docs/tools/computer.md deleted file mode 100644 index 563650fd3..000000000 --- a/docs/tools/computer.md +++ /dev/null @@ -1,271 +0,0 @@ -# hanzo-tools-computer - -Computer control via pyautogui for comprehensive Mac automation. Mouse, keyboard, screenshots, window management. - -## Installation - -```bash -pip install hanzo-tools-computer -``` - -Or as part of the full toolkit: - -```bash -pip install hanzo-mcp[tools-all] -``` - -## Overview - -`hanzo-tools-computer` provides desktop automation: - -- **Mouse** - click, drag, scroll, move -- **Keyboard** - type, press, hotkey -- **Screen** - screenshots, display info -- **Windows** - focus, list, get active -- **Image Location** - find UI elements -- **Regions** - named areas for targeting - -## Quick Start - -```python -# Click at coordinates -computer(action="click", x=100, y=200) - -# Type text -computer(action="type", text="Hello, World!") - -# Press keys -computer(action="press", key="enter") -computer(action="hotkey", keys=["command", "c"]) - -# Screenshot -computer(action="screenshot") - -# Get screen info -computer(action="info") -``` - -## Actions Reference - -### Mouse Actions - -```python -# Click -computer(action="click", x=100, y=200) -computer(action="double_click", x=100, y=200) -computer(action="right_click", x=100, y=200) -computer(action="middle_click", x=100, y=200) - -# Move -computer(action="move", x=100, y=200, duration=0.5) -computer(action="move_relative", dx=50, dy=-30) - -# Drag -computer(action="drag", x=300, y=400, duration=0.5) -computer(action="drag_relative", dx=100, dy=0) - -# Scroll -computer(action="scroll", amount=5) # Scroll up -computer(action="scroll", amount=-5) # Scroll down -computer(action="scroll", amount=3, x=500, y=300) # At position -``` - -### Keyboard Actions - -```python -# Type text (character by character) -computer(action="type", text="Hello!", interval=0.05) - -# Write text (instant, can clear first) -computer(action="write", text="Instant text", clear=True) - -# Press single key -computer(action="press", key="enter") -computer(action="press", key="tab") -computer(action="press", key="escape") - -# Key combinations -computer(action="hotkey", keys=["command", "c"]) # Copy -computer(action="hotkey", keys=["command", "v"]) # Paste -computer(action="hotkey", keys=["command", "shift", "s"]) # Save as - -# Hold/release keys -computer(action="key_down", key="shift") -computer(action="key_up", key="shift") -``` - -### Screen Actions - -```python -# Full screenshot (returns base64) -computer(action="screenshot") - -# Region screenshot -computer(action="screenshot_region", region=[100, 100, 500, 300]) - -# Get all displays -computer(action="get_screens") - -# Get screen size -computer(action="screen_size") - -# Current mouse position -computer(action="position") - -# Full info -computer(action="info") -``` - -### Image Location - -Find UI elements by image matching: - -```python -# Find image on screen (returns center point) -computer(action="locate", image_path="button.png") - -# Find all matches -computer(action="locate_all", image_path="icon.png") - -# Get center point -computer(action="locate_center", image_path="submit.png") - -# Wait for image to appear -computer(action="wait_for_image", image_path="loading.png", timeout=10) - -# Wait while image is visible -computer(action="wait_while_image", image_path="spinner.png", timeout=30) -``` - -### Pixel Operations - -```python -# Get pixel color at point -computer(action="pixel", x=100, y=200) - -# Check if pixel matches color -computer(action="pixel_matches", x=100, y=200, color=[255, 0, 0], tolerance=10) -``` - -### Window Management - -```python -# Get active window info -computer(action="get_active_window") - -# List all windows -computer(action="list_windows") - -# Focus window by title -computer(action="focus_window", title="Terminal") -computer(action="focus_window", title=".*Code.*", use_regex=True) -``` - -### Named Regions - -Define reusable screen regions: - -```python -# Define a region -computer(action="define_region", name="toolbar", x=0, y=0, width=1920, height=60) - -# Screenshot region -computer(action="region_screenshot", name="toolbar") - -# Locate image within region -computer(action="region_locate", name="toolbar", image_path="save_button.png") -``` - -### Timing & Flow - -```python -# Sleep -computer(action="sleep", value=2.0) - -# Countdown with output -computer(action="countdown", value=5) - -# Set global pause between actions -computer(action="set_pause", value=0.2) - -# Enable/disable fail-safe (corner abort) -computer(action="set_failsafe", value=True) -``` - -### Batch Operations - -Execute multiple actions in sequence: - -```python -computer(action="batch", actions=[ - {"action": "click", "x": 100, "y": 200}, - {"action": "type", "text": "Hello"}, - {"action": "press", "key": "enter"} -]) -``` - -## Examples - -### UI Automation - -```python -# Click button and fill form -computer(action="click", x=500, y=300) # Click form field -computer(action="write", text="user@example.com", clear=True) -computer(action="press", key="tab") -computer(action="write", text="password123") -computer(action="press", key="enter") -``` - -### Screenshot Workflow - -```python -# Take screenshot, find element, click it -computer(action="screenshot") -result = computer(action="locate", image_path="login_button.png") -if result["found"]: - computer(action="click", x=result["x"], y=result["y"]) -``` - -### Window Management - -```python -# Focus app and interact -computer(action="focus_window", title="Safari") -computer(action="hotkey", keys=["command", "l"]) # Focus URL bar -computer(action="write", text="https://example.com", clear=True) -computer(action="press", key="enter") -``` - -### Wait for UI State - -```python -# Wait for loading to finish -computer(action="wait_while_image", image_path="spinner.png", timeout=30) - -# Then continue -computer(action="click", x=500, y=400) -``` - -## Performance Features - -- **Lazy loading** - pyautogui loads on first use (150ms+ startup savings) -- **Cached screen info** - Display info cached for 5 seconds -- **Thread pool executor** - All blocking operations run in threads -- **Batch operations** - Multiple actions in single call - -## Fail-Safe - -By default, moving the mouse to any corner of the screen will raise an exception, allowing you to abort automation. Disable with: - -```python -computer(action="set_failsafe", value=False) -``` - -## Best Practices - -1. **Use image location over coordinates** - More robust across resolutions -2. **Add waits for UI transitions** - Use `wait_for_image` after actions -3. **Define named regions** - Improves code readability -4. **Enable fail-safe** - Keep the corner abort enabled during development -5. **Use batch for sequences** - More efficient than individual calls diff --git a/docs/tools/config.md b/docs/tools/config.md deleted file mode 100644 index 61869fa87..000000000 --- a/docs/tools/config.md +++ /dev/null @@ -1,250 +0,0 @@ -# hanzo-tools-config - -Configuration and development mode management for Hanzo AI. - -## Installation - -```bash -pip install hanzo-tools-config -``` - -Or as part of the full toolkit: - -```bash -pip install hanzo-mcp[tools-all] -``` - -## Overview - -`hanzo-tools-config` provides: - -- **config** - Git-style configuration management -- **mode** - Development mode/persona switching - -## Config Tool - -Manage settings with git-style configuration. - -### Quick Start - -```python -# Get a value -config(action="get", key="user.name") - -# Set a value -config(action="set", key="user.name", value="Alice") - -# List all settings -config(action="list") - -# Delete a setting -config(action="delete", key="user.name") -``` - -### Actions - -#### get - -Get a configuration value. - -```python -config(action="get", key="editor.theme") -config(action="get", key="tools.timeout") -``` - -#### set - -Set a configuration value. - -```python -config(action="set", key="editor.theme", value="dark") -config(action="set", key="tools.timeout", value="30") -``` - -#### list - -List all configuration values. - -```python -config(action="list") -``` - -**Response:** -```json -{ - "settings": { - "user.name": "Alice", - "editor.theme": "dark", - "tools.timeout": "30" - } -} -``` - -#### delete - -Remove a configuration value. - -```python -config(action="delete", key="editor.theme") -``` - -### Configuration Scopes - -| Scope | Location | Priority | -|-------|----------|----------| -| Session | Memory | Highest | -| Project | `.hanzo/config` | Medium | -| Global | `~/.hanzo/config` | Lowest | - -## Mode Tool - -Switch between development modes/personas. - -### Quick Start - -```python -# List available modes -mode(action="list") - -# Activate a mode -mode(action="activate", name="guido") - -# Show current mode -mode(action="current") - -# Show mode details -mode(action="show", name="linus") -``` - -### Actions - -#### list - -List all available development modes. - -```python -mode(action="list") -``` - -**Response:** -```json -{ - "modes": [ - {"name": "guido", "description": "Guido van Rossum - Python creator"}, - {"name": "linus", "description": "Linus Torvalds - Linux/Git creator"}, - {"name": "rob", "description": "Rob Pike - Go language designer"} - ] -} -``` - -#### activate - -Activate a development mode. - -```python -mode(action="activate", name="guido") -``` - -This changes: -- Code style preferences -- Review approach -- Communication style -- Tool recommendations - -#### current - -Show the currently active mode. - -```python -mode(action="current") -``` - -#### show - -Show details of a specific mode. - -```python -mode(action="show", name="linus") -``` - -**Response:** -```json -{ - "name": "linus", - "description": "Linus Torvalds - Linux/Git creator", - "principles": [ - "Simple is better than complex", - "Make it work, make it right, make it fast", - "Read the code" - ], - "preferences": { - "language": "C", - "style": "kernel", - "testing": "rigorous" - } -} -``` - -### Available Modes - -The mode system includes 700+ programmer personas loaded from `hanzo-persona`: - -| Category | Examples | -|----------|----------| -| Language Creators | guido (Python), linus (Linux/C), rob (Go), rich (Clojure) | -| Industry Leaders | jeff (AWS), kelsey (Kubernetes), mitchellh (HashiCorp) | -| Framework Authors | dan (React), evan (Vue), taylor (Laravel) | -| Scientists | alan (Turing), grace (Hopper), ada (Lovelace) | - -Each persona includes: -- OCEAN personality traits -- Behavioral patterns -- Cognitive style -- Communication preferences -- Tool recommendations - -## Examples - -### Project Setup - -```python -# Set project-specific config -config(action="set", key="project.language", value="python") -config(action="set", key="project.style", value="black") -config(action="set", key="tools.lsp", value="pyright") - -# Activate appropriate mode -mode(action="activate", name="guido") -``` - -### Switching Contexts - -```python -# Working on Go project -mode(action="activate", name="rob") -config(action="set", key="project.language", value="go") - -# Later, switching to Rust -mode(action="activate", name="steve") # Steve Klabnik -config(action="set", key="project.language", value="rust") -``` - -### Checking Configuration - -```python -# See all settings -config(action="list") - -# Check specific setting -timeout = config(action="get", key="tools.timeout") - -# Check current mode -current = mode(action="current") -``` - -## Best Practices - -1. **Use hierarchical keys** - `category.setting` format -2. **Set project config locally** - Use `.hanzo/config` for project-specific -3. **Match mode to project** - Python project โ†’ guido mode -4. **Review mode on start** - Check persona fits your current task diff --git a/docs/tools/core.md b/docs/tools/core.md deleted file mode 100644 index 6cd914859..000000000 --- a/docs/tools/core.md +++ /dev/null @@ -1,261 +0,0 @@ -# hanzo-tools-core - -The foundation package for all Hanzo tool implementations. Provides base classes, type definitions, and utilities for building MCP-compatible tools. - -## Installation - -```bash -pip install hanzo-tools-core -``` - -Or as part of the full toolkit: - -```bash -pip install hanzo-mcp[tools-all] -``` - -## Overview - -`hanzo-tools-core` provides: - -- **BaseTool** - Abstract base class for all tool implementations -- **FileSystemTool** - Specialized base for file operations -- **ToolRegistry** - Central registry for tool management -- **MCPResourceDocument** - Structured response format -- **ToolContext** - Runtime context for tool execution -- **PermissionManager** - Path-based access control - -## Quick Start - -### Creating a Custom Tool - -```python -from hanzo_tools.core import BaseTool, ToolContext - -class MyTool(BaseTool): - """A custom tool implementation.""" - - @property - def name(self) -> str: - return "my_tool" - - @property - def description(self) -> str: - return "Does something useful" - - async def call(self, ctx: ToolContext, message: str) -> str: - """Execute the tool. - - Args: - ctx: Tool execution context - message: Input message to process - - Returns: - Processed result - """ - return f"Processed: {message}" - - def register(self, mcp_server): - """Register with MCP server.""" - @mcp_server.tool(name=self.name, description=self.description) - async def handler(message: str) -> str: - ctx = ToolContext() - return await self.call(ctx, message=message) -``` - -### Using the Tool Registry - -```python -from hanzo_tools.core import ToolRegistry, BaseTool - -# Register tools -registry = ToolRegistry() -registry.register(MyTool()) - -# Get a tool by name -tool = registry.get("my_tool") - -# List all registered tools -for name, tool in registry.items(): - print(f"{name}: {tool.description}") -``` - -## Base Classes - -### BaseTool - -The abstract base class all tools must inherit from: - -```python -from abc import ABC, abstractmethod - -class BaseTool(ABC): - """Abstract base class for all Hanzo tools.""" - - @property - @abstractmethod - def name(self) -> str: - """Unique identifier for the tool.""" - ... - - @property - @abstractmethod - def description(self) -> str: - """Human-readable description shown to LLMs.""" - ... - - @abstractmethod - async def call(self, ctx: ToolContext, **params) -> str: - """Execute the tool with given parameters.""" - ... - - @abstractmethod - def register(self, mcp_server: FastMCP) -> None: - """Register the tool with an MCP server.""" - ... -``` - -### FileSystemTool - -Specialized base class for tools that operate on the filesystem: - -```python -from hanzo_tools.core import FileSystemTool - -class ReadTool(FileSystemTool): - """Read file contents.""" - - @property - def name(self) -> str: - return "read" - - async def call(self, ctx, file_path: str) -> str: - # Automatic path validation and permission checking - validated_path = self.validate_path(file_path) - async with aiofiles.open(validated_path) as f: - return await f.read() -``` - -## Response Format - -### MCPResourceDocument - -Structured format for tool responses: - -```python -from hanzo_tools.core import MCPResourceDocument - -# Create a document response -doc = MCPResourceDocument( - uri="file:///path/to/file.py", - mime_type="text/x-python", - text="def hello(): pass" -) - -# Convert to JSON string for MCP -response = doc.to_json_string() -``` - -## Context Management - -### ToolContext - -Runtime context passed to every tool invocation: - -```python -from hanzo_tools.core import ToolContext, create_tool_context - -# Create context with custom settings -ctx = create_tool_context( - allowed_paths=["/home/user/project"], - enable_write=True, - timeout=30.0 -) - -# Access context in tool -async def call(self, ctx: ToolContext, **params): - if ctx.enable_write: - # Perform write operation - ... -``` - -## Permission Management - -### PermissionManager - -Controls which paths tools can access: - -```python -from hanzo_tools.core import PermissionManager - -pm = PermissionManager( - allowed_paths=[ - "/home/user/project", - "/tmp" - ], - denied_paths=[ - "/home/user/project/.env", - "/home/user/project/secrets" - ] -) - -# Check if path is allowed -if pm.is_allowed("/home/user/project/src/main.py"): - # Safe to access - ... -``` - -## Decorators - -### auto_timeout - -Automatically handle timeouts for long-running operations: - -```python -from hanzo_tools.core import auto_timeout - -class SlowTool(BaseTool): - @auto_timeout(seconds=30) - async def call(self, ctx, **params): - # Will automatically timeout after 30 seconds - result = await slow_operation() - return result -``` - -## Error Handling - -Tools should return error messages as strings rather than raising exceptions: - -```python -async def call(self, ctx, file_path: str) -> str: - try: - content = await read_file(file_path) - return content - except FileNotFoundError: - return f"Error: File not found: {file_path}" - except PermissionError: - return f"Error: Permission denied: {file_path}" - except Exception as e: - return f"Error: {type(e).__name__}: {str(e)}" -``` - -## API Reference - -::: hanzo_tools.core.base.BaseTool - options: - show_source: false - members: - - name - - description - - call - - register - -::: hanzo_tools.core.base.FileSystemTool - -::: hanzo_tools.core.base.ToolRegistry - -::: hanzo_tools.core.types.MCPResourceDocument - -::: hanzo_tools.core.context.ToolContext - -::: hanzo_tools.core.permissions.PermissionManager diff --git a/docs/tools/database.md b/docs/tools/database.md deleted file mode 100644 index f27921d75..000000000 --- a/docs/tools/database.md +++ /dev/null @@ -1,396 +0,0 @@ -# Database Tools - -The database tools (`hanzo-tools-database`) provide SQL, graph database, and hybrid memory operations for project-embedded databases. - -## Overview - -Each project can have embedded SQLite databases with support for: -- **SQL operations** with tables for metadata, files, and symbols -- **Graph database** operations for tracking code relationships -- **Hybrid memory system** combining markdown files + SQLite + vector search - -## Memory Tool - Hybrid Storage - -### Unified Memory Management - -The `memory` tool provides a hybrid storage system that combines: -- **Plaintext markdown files** for human-readable rules/context -- **SQLite with FTS5** for full-text search across all content -- **sqlite-vec extension** for vector similarity search (optional) -- **Layered search** across all storage types - -### File Structure -``` -~/.hanzo/ # Global hanzo config -โ”œโ”€โ”€ memory/ # Global memories -โ”‚ โ”œโ”€โ”€ rules.md # Global rules/context -โ”‚ โ”œโ”€โ”€ user_preferences.md # User preferences -โ”‚ โ””โ”€โ”€ coding_standards.md # Coding standards -โ””โ”€โ”€ db/ - โ””โ”€โ”€ global_memory.db # Global memory database - -/path/to/project/ # Project-specific -โ”œโ”€โ”€ LLM.md # Project context (existing) -โ”œโ”€โ”€ .hanzo/ -โ”‚ โ”œโ”€โ”€ memory/ # Project memories -โ”‚ โ”‚ โ”œโ”€โ”€ architecture.md # Architecture decisions -โ”‚ โ”‚ โ”œโ”€โ”€ patterns.md # Code patterns -โ”‚ โ”‚ โ””โ”€โ”€ sessions/ # Session memories -โ”‚ โ”‚ โ”œโ”€โ”€ 2025-01-12.md # Daily sessions -โ”‚ โ”‚ โ””โ”€โ”€ 2025-01-13.md -โ”‚ โ””โ”€โ”€ db/ -โ”‚ โ”œโ”€โ”€ project.db # Existing project DB -โ”‚ โ”œโ”€โ”€ graph.db # Existing graph DB -โ”‚ โ””โ”€โ”€ memory.db # Hybrid memory DB -``` - -### Memory Tool Usage - -```python -# Read memory files -memory(action="read", file_path="rules.md", scope="global") -memory(action="read", file_path="architecture.md", scope="project") - -# Write memory files -memory(action="write", file_path="patterns.md", content="# Code Patterns...", scope="project") - -# Append to session logs -memory(action="append", file_path="sessions/today.md", content="New insight about the codebase") - -# Search across all memories -memory(action="search", content="database design", scope="both", limit=10) - -# Create structured memories -memory(action="create", content="Important architectural decision", category="architecture", importance=8) - -# List memory files -memory(action="list", scope="project") - -# Get statistics -memory(action="stats") -``` - -#### Memory Actions - -| Action | Description | Parameters | -|--------|-------------|------------| -| `read` | Read markdown memory file | `file_path`, `scope` | -| `write` | Write markdown memory file | `file_path`, `content`, `scope`, `category` | -| `append` | Append to markdown file with timestamp | `file_path`, `content`, `scope` | -| `search` | Search across all memory types | `content` (query), `scope`, `search_type`, `limit` | -| `create` | Create structured memory record | `content`, `category`, `importance`, `scope` | -| `list` | List all memory files | `scope` | -| `stats` | Get memory system statistics | `scope` | - -#### Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `action` | str | `search` | Memory action to perform | -| `content` | str | - | Content to store or search query | -| `file_path` | str | - | Markdown file path (e.g., 'rules.md', 'sessions/today.md') | -| `category` | str | - | Memory category for organization | -| `scope` | str | `project` | Memory scope: global, project, or both | -| `search_type` | str | `fulltext` | Search type: fulltext, vector, or hybrid | -| `importance` | int | 5 | Memory importance (1-10) | -| `limit` | int | 10 | Maximum results to return | - -### Features - -#### Full-Text Search (FTS5) -- Fast text search across markdown files and structured memories -- Snippet highlighting with search terms marked -- Ranking by relevance score - -#### Vector Search (sqlite-vec) -- Semantic similarity search using embeddings -- Requires sqlite-vec extension (install with `python setup_sqlite_vec.py`) -- Support for BGE and other embedding models - -#### Layered Storage -- **Markdown files**: Human-readable, git-trackable, editable -- **SQLite index**: Fast search and metadata storage -- **Vector embeddings**: Semantic similarity (when available) - -## SQL Tools - -### sql_query - Execute SQL Queries - -Execute raw SQL on the project database: - -```python -# Read query (default safe mode) -sql_query(query="SELECT * FROM files LIMIT 10") - -# Query with specific project -sql_query(query="SELECT name, type FROM symbols WHERE type='function'", project_path="/project") - -# Write query (requires read_only=False) -sql_query( - query="INSERT INTO metadata (key, value) VALUES ('version', '1.0')", - read_only=False -) -``` - -#### Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `query` | str | required | SQL query to execute | -| `project_path` | str | cwd | Project path | -| `read_only` | bool | `True` | Block write operations | - -#### Database Schema - -Default tables in project databases: - -```sql --- Key-value metadata -CREATE TABLE metadata ( - key TEXT PRIMARY KEY, - value TEXT -); - --- File information -CREATE TABLE files ( - path TEXT PRIMARY KEY, - content TEXT, - modified_at TEXT -); - --- Code symbols -CREATE TABLE symbols ( - id INTEGER PRIMARY KEY, - name TEXT, - type TEXT, -- function, class, variable - file_path TEXT, - line_number INTEGER -); -``` - -### sql_search - Full-Text Search - -Search database content with text matching: - -```python -sql_search(pattern="error", table="files") -sql_search(pattern="TODO", column="content") -``` - -### sql_stats - Database Statistics - -Get database statistics and schema info: - -```python -sql_stats() -sql_stats(project_path="/project") -``` - -## Graph Tools - -The graph database stores relationships between code entities (files, functions, classes). - -### graph_add - Add Nodes/Edges - -Add nodes and edges to the graph: - -```python -# Add a node -graph_add(node_id="main.py", node_type="file") - -# Add an edge -graph_add( - source="main.py", - target="utils.py", - relationship="imports" -) - -# Add with properties -graph_add( - node_id="MyClass", - node_type="class", - properties={"methods": 5, "lines": 120} -) -``` - -### graph_remove - Remove from Graph - -Remove nodes or edges: - -```python -graph_remove(node_id="deprecated_file.py") -graph_remove(source="a.py", target="b.py", relationship="imports") -``` - -### graph_query - Query Graph Database - -Execute graph traversal queries: - -```python -# Find neighbors -graph_query(query="neighbors", node_id="main.py") - -# Find path between nodes -graph_query(query="path", node_id="main.py", target_id="utils.py") - -# Get subgraph -graph_query(query="subgraph", node_id="MyClass", depth=3) - -# Find ancestors (nodes pointing TO this node) -graph_query(query="ancestors", node_id="error_handler", relationship="calls") - -# Find descendants (nodes this node points TO) -graph_query(query="descendants", node_id="BaseClass", relationship="inherits") - -# Find all connected nodes -graph_query(query="connected", node_id="main.py", direction="both") -``` - -#### Query Types - -| Query | Description | Required Params | -|-------|-------------|-----------------| -| `neighbors` | Direct neighbors | `node_id` | -| `path` | Shortest path | `node_id`, `target_id` | -| `subgraph` | Subgraph around node | `node_id` | -| `connected` | All connected nodes | `node_id` | -| `ancestors` | Incoming edges | `node_id` | -| `descendants` | Outgoing edges | `node_id` | - -#### Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `query` | str | required | Query type | -| `node_id` | str | - | Starting node | -| `target_id` | str | - | Target node (for path) | -| `depth` | int | `2` | Max traversal depth | -| `relationship` | str | - | Filter by edge type | -| `node_type` | str | - | Filter by node type | -| `direction` | str | `both` | `both`, `incoming`, `outgoing` | - -### graph_search - Search Graph - -Search for nodes by properties: - -```python -graph_search(pattern="Service", node_type="class") -graph_search(pattern="test_*", relationship="depends_on") -``` - -### graph_stats - Graph Statistics - -Get graph statistics: - -```python -graph_stats() -# Returns: nodes count, edges count, relationship types, node types -``` - -## Relationship Types - -Common relationship types in code graphs: - -| Relationship | Description | -|--------------|-------------| -| `imports` | File imports another file | -| `calls` | Function calls another function | -| `inherits` | Class inherits from another | -| `implements` | Class implements interface | -| `depends_on` | General dependency | -| `contains` | File contains symbol | - -## Installation - -```bash -pip install hanzo-tools-database - -# For vector search support -pip install hanzo-tools-database[vector] -python -m hanzo_tools.database.setup_sqlite_vec -``` - -## sqlite-vec Vector Search Setup - -The sqlite-vec extension provides vector similarity search capabilities: - -```bash -# Install sqlite-vec extension -cd pkg/hanzo-tools-database -python setup_sqlite_vec.py - -# Test installation -python -c " -import sqlite3 -conn = sqlite3.connect(':memory:') -conn.enable_load_extension(True) -conn.load_extension('vec0') -print('โœ“ sqlite-vec is available') -" -``` - -### Vector Search Features -- **Semantic similarity**: Find conceptually similar content -- **Embedding models**: Support for BGE, sentence-transformers, etc. -- **Efficient storage**: Binary vector storage in SQLite -- **Fast queries**: Optimized vector similarity search - -## Best Practices - -### 1. Use Read-Only Mode - -```python -# Default: safe read-only queries -sql_query(query="SELECT * FROM files") - -# Only disable when necessary -sql_query(query="UPDATE ...", read_only=False) -``` - -### 2. Filter Graph Queries - -```python -# Avoid unbounded traversals -graph_query(query="subgraph", node_id="main.py", depth=2) - -# Filter by relationship type -graph_query(query="neighbors", node_id="func", relationship="calls") -``` - -### 3. Organize Memories - -```python -# Use clear categories -memory(action="create", content="API design decision", category="architecture") - -# Use session logs for temporal organization -memory(action="append", file_path="sessions/2025-01-12.md", content="Today's insights") - -# Keep global rules for system-wide context -memory(action="write", file_path="rules.md", scope="global", content="System guidelines") -``` - -### 4. Index Before Querying - -Ensure the project is indexed before running database queries: - -```python -# Index project first -index(path="/project") - -# Then query -sql_query(query="SELECT * FROM symbols WHERE type='function'") -``` - -### 5. Memory Initialization - -Initialize memory structure for new projects: - -```python -# Initialize global memory (run once per user) -python -m hanzo_tools.database.init_memory - -# Initialize project memory (run once per project) -cd /path/to/project -python -m hanzo_tools.database.init_memory . -``` diff --git a/docs/tools/editor.md b/docs/tools/editor.md deleted file mode 100644 index 12fd9a240..000000000 --- a/docs/tools/editor.md +++ /dev/null @@ -1,176 +0,0 @@ -# Editor Tools - -The editor tools (`hanzo-tools-editor`) provide integration with external code editors, currently focused on Neovim. - -## Overview - -These tools enable opening files in your preferred editor with precise cursor positioning, split modes, and session management. - -## Neovim Integration - -### neovim_edit - Open Files - -Open files in Neovim with advanced options: - -```python -# Basic file open -neovim_edit(file_path="main.py") - -# Open at specific line -neovim_edit(file_path="main.py", line_number=42) - -# Open at specific line and column -neovim_edit(file_path="main.py", line_number=42, column_number=10) - -# Open in read-only mode -neovim_edit(file_path="config.json", read_only=True) - -# Open in vertical split -neovim_edit(file_path="test.py", split="vsplit") - -# Open in new tab -neovim_edit(file_path="README.md", split="tab") -``` - -#### Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `file_path` | str | required | Path to the file | -| `line_number` | int | - | Line to jump to | -| `column_number` | int | - | Column to jump to | -| `read_only` | bool | `False` | Open in view mode | -| `split` | str | - | `vsplit`, `split`, or `tab` | -| `wait` | bool | `True` | Wait for editor to close | -| `in_terminal` | bool | `True` | Open in terminal window | - -#### Split Modes - -| Mode | Description | -|------|-------------| -| `vsplit` | Vertical split (side by side) | -| `split` | Horizontal split (top/bottom) | -| `tab` | New tab | - -### neovim_command - Execute Commands - -Execute Neovim Ex commands: - -```python -# Run command in current buffer -neovim_command(command=":w") - -# Search and replace -neovim_command(command=":%s/old/new/g") - -# Run Lua code -neovim_command(command=":lua print('Hello')") - -# Source configuration -neovim_command(command=":source ~/.config/nvim/init.lua") -``` - -#### Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `command` | str | required | Ex command to execute | -| `file_path` | str | - | File to run command on | - -### neovim_session - Manage Sessions - -Manage Neovim sessions for persistent workspaces: - -```python -# Save current session -neovim_session(action="save", name="project-alpha") - -# Load session -neovim_session(action="load", name="project-alpha") - -# List sessions -neovim_session(action="list") - -# Delete session -neovim_session(action="delete", name="old-session") -``` - -#### Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `action` | str | required | `save`, `load`, `list`, `delete` | -| `name` | str | - | Session name | - -## Installation - -```bash -# Basic install -pip install hanzo-tools-editor - -# With Neovim support -pip install hanzo-tools-editor[neovim] -``` - -## Requirements - -- **Neovim** must be installed and available in PATH -- Installation varies by platform: - -```bash -# macOS -brew install neovim - -# Ubuntu/Debian -sudo apt install neovim - -# Arch Linux -sudo pacman -S neovim - -# Windows (Chocolatey) -choco install neovim -``` - -## Terminal Integration - -On macOS, the tool integrates with: -- **iTerm2** (preferred if installed) -- **Terminal.app** (fallback) - -On Linux: -- **gnome-terminal** -- **xterm** (fallback) - -## Best Practices - -### 1. Use Line/Column for Error Navigation - -```python -# Jump directly to error location -neovim_edit(file_path="src/main.py", line_number=42, column_number=15) -``` - -### 2. Use Splits for Comparison - -```python -# Open original and new file side by side -neovim_edit(file_path="original.py") -neovim_edit(file_path="modified.py", split="vsplit") -``` - -### 3. Use Sessions for Projects - -```python -# Save state when switching projects -neovim_session(action="save", name="current-work") - -# Restore when returning -neovim_session(action="load", name="current-work") -``` - -### 4. Read-Only for Config Files - -```python -# Prevent accidental changes -neovim_edit(file_path="/etc/config", read_only=True) -``` diff --git a/docs/tools/fs.md b/docs/tools/fs.md deleted file mode 100644 index 8a0b72bc1..000000000 --- a/docs/tools/fs.md +++ /dev/null @@ -1,326 +0,0 @@ -# hanzo-tools-fs - -Filesystem tools for reading, writing, editing, and searching files with permission management. - -## Installation - -```bash -pip install hanzo-tools-fs -``` - -Or as part of the full toolkit: - -```bash -pip install hanzo-mcp[tools-all] -``` - -## Overview - -`hanzo-tools-fs` provides: - -- **read** - Read file contents with line numbers -- **write** - Write/create files -- **edit** - Edit files with find/replace -- **tree** - Directory tree view -- **find** - Find files by pattern -- **search** - Search file contents (ripgrep-powered) -- **ast** - AST-based code structure search - -## Quick Start - -```python -# Read a file -read(file_path="/path/to/file.py") - -# Write a file -write(file_path="/path/to/new.py", content="print('hello')") - -# Edit a file -edit(file_path="/path/to/file.py", old_string="foo", new_string="bar") - -# Search for patterns -search(pattern="TODO", path="./src") - -# Find files -find(pattern="*.py", path="./src") - -# View directory structure -tree(path="./src", depth=3) - -# AST-based code search -ast(pattern="class.*Service", path="./src") -``` - -## Tools Reference - -### read - -Read file contents with line numbers. - -```python -# Basic read -read(file_path="/path/to/file.py") - -# Read with offset and limit (for large files) -read(file_path="/path/to/large.py", offset=100, limit=50) -``` - -**Parameters:** -- `file_path` (required): Absolute path to the file -- `offset`: Starting line number (0-based, default: 0) -- `limit`: Maximum lines to read (default: 2000) - -**Output format:** -``` - 1โ†’def hello(): - 2โ†’ print("Hello, World!") - 3โ†’ -[Showing lines 1-3 of 3] -``` - -### write - -Write content to a file, creating it if it doesn't exist. - -```python -# Create new file -write(file_path="/path/to/new.py", content="print('hello')") - -# Overwrite existing file -write(file_path="/path/to/existing.py", content="new content") -``` - -**Parameters:** -- `file_path` (required): Absolute path to the file -- `content` (required): Content to write - -### edit - -Edit a file by replacing text. - -```python -# Simple replacement -edit( - file_path="/path/to/file.py", - old_string="def old_name():", - new_string="def new_name():" -) - -# Replace multiple occurrences -edit( - file_path="/path/to/file.py", - old_string="TODO", - new_string="DONE", - expected_replacements=5 -) -``` - -**Parameters:** -- `file_path` (required): Absolute path to the file -- `old_string` (required): Text to find -- `new_string` (required): Text to replace with -- `expected_replacements`: Expected number of replacements (default: 1) - -### tree - -Display directory tree structure. - -```python -# Basic tree view -tree(path="/project") - -# Limited depth -tree(path="/project", depth=2) - -# Include filtered directories -tree(path="/project", include_filtered=True) -``` - -**Parameters:** -- `path` (required): Directory path -- `depth`: Maximum depth (default: 3) -- `include_filtered`: Include commonly filtered dirs like node_modules (default: false) - -**Output format:** -``` -โ”œโ”€โ”€ src/ -โ”‚ โ”œโ”€โ”€ main.py -โ”‚ โ””โ”€โ”€ utils/ -โ”‚ โ””โ”€โ”€ helpers.py -โ”œโ”€โ”€ tests/ -โ”‚ โ””โ”€โ”€ test_main.py -โ””โ”€โ”€ pyproject.toml -``` - -### find - -Find files and directories by pattern. - -```python -# Find Python files -find(pattern="*.py", path="/project") - -# Find test files -find(pattern="test_*.py", path="/project") - -# Find directories -find(pattern="*config*", path="/project", type="dir") - -# Limit results -find(pattern="*.md", path="/project", max_results=20) -``` - -**Parameters:** -- `pattern` (required): Glob pattern to match -- `path`: Directory to search (default: current directory) -- `type`: Filter by type - "file", "dir", or None for both -- `max_results`: Maximum results (default: 100) - -### search - -Search for patterns in file contents using ripgrep. - -```python -# Basic search -search(pattern="TODO", path="./src") - -# Search with file filter -search(pattern="import", path="./src", include="*.py") - -# Search with context lines -search(pattern="error", path="./logs", context_lines=5) - -# Limit results -search(pattern="function", path="./src", max_results=20) -``` - -**Parameters:** -- `pattern` (required): Regex pattern to search for -- `path`: Directory or file to search (default: current directory) -- `include`: Glob pattern to filter files (e.g., "*.py") -- `context_lines`: Lines of context around matches (default: 2) -- `max_results`: Maximum results (default: 50) - -**Features:** -- Uses ripgrep (rg) if available for high performance -- Falls back to Python regex if ripgrep not installed -- Async file I/O for non-blocking operation - -### ast - -AST-based code structure search using tree-sitter. - -```python -# Find function definitions -ast(pattern="def test_", path="./tests") - -# Find class definitions -ast(pattern="class.*Service", path="./src") - -# Find with line numbers -ast(pattern="async def", path="./src", line_number=True) - -# Case-insensitive search -ast(pattern="config", path="./src", ignore_case=True) -``` - -**Parameters:** -- `pattern` (required): Regex pattern to search for -- `path` (required): File or directory to search -- `ignore_case`: Case-insensitive matching (default: false) -- `line_number`: Display line numbers (default: false) - -**Supported Languages:** -- Python (.py, .pyw) -- JavaScript/TypeScript (.js, .jsx, .ts, .tsx) -- Go (.go) -- Rust (.rs) -- C/C++ (.c, .cpp, .h, .hpp) -- Java (.java) -- Ruby (.rb) -- PHP (.php) -- And more... - -## Permission Management - -All filesystem tools respect permission boundaries: - -```python -from hanzo_tools.fs import register_tools -from hanzo_tools.core import PermissionManager - -# Set up permission manager -pm = PermissionManager( - allowed_paths=["/home/user/project"], - denied_paths=["/home/user/project/.env"] -) - -# Register tools with MCP server -register_tools(mcp_server, pm) -``` - -### Read-Only Tools - -For sandboxed environments, use read-only tools: - -```python -from hanzo_tools.fs import get_read_only_filesystem_tools - -# Get only read operations -tools = get_read_only_filesystem_tools(permission_manager) -# Includes: read, tree, find, search, ast -``` - -## Examples - -### Code Review Workflow - -```python -# Find all TODO comments -search(pattern="TODO|FIXME", path="./src") - -# Check code structure -ast(pattern="class.*", path="./src") - -# Review specific file -read(file_path="./src/main.py") -``` - -### Refactoring Workflow - -```python -# Find all usages of old function name -search(pattern="old_function_name", path="./src") - -# Find function definition -ast(pattern="def old_function_name", path="./src") - -# Edit the function -edit( - file_path="./src/utils.py", - old_string="def old_function_name(", - new_string="def new_function_name(" -) -``` - -### Project Exploration - -```python -# Get project structure -tree(path="./", depth=3) - -# Find configuration files -find(pattern="*.toml", path="./") -find(pattern="*.json", path="./") - -# Search for entry points -ast(pattern="def main|if __name__", path="./src") -``` - -## Best Practices - -1. **Use absolute paths** - All tools expect absolute paths for reliability -2. **Set appropriate limits** - Use `limit` and `max_results` to avoid overwhelming output -3. **Prefer search over read** - Use search/ast to find relevant code instead of reading entire files -4. **Use tree for orientation** - Start with tree to understand project structure -5. **Combine tools** - Use find + read or search + edit for efficient workflows diff --git a/docs/tools/index.md b/docs/tools/index.md deleted file mode 100644 index 1100b9dbd..000000000 --- a/docs/tools/index.md +++ /dev/null @@ -1,129 +0,0 @@ -# Tools Overview - -Complete documentation for all `hanzo-tools-*` packages. - -> **Architecture**: Tools follow [HIP-0300](../hip/HIP-0300.md) - the Unified MCP Tools Architecture with orthogonal operators and effect tracking. - -## Installation - -```bash -# All tools (recommended) -pip install hanzo-mcp[tools-all] - -# Core development tools -pip install hanzo-mcp[tools-dev] - -# Individual packages -pip install hanzo-tools-shell hanzo-tools-browser -``` - -## Tool Categories - -### HIP-0300 Core Operators - -Primary operators organized by orthogonal axes: - -| Tool | Axis | Actions | Description | -|------|------|---------|-------------| -| [fs](fs.md) | Bytes + Paths | read, write, edit, search, patch, tree | Filesystem operations | -| [id](core.md) | Identity | hash, uri, ref, verify | Content-addressable identity | -| [code](code.md) | Symbols + Structure | parse, transform, summarize | Code analysis and transformation | -| [proc](shell.md) | Execution | run, bg, signal, wait | Process execution | -| [vcs](vcs.md) | History + Diffs | status, diff, commit, log | Version control | -| [test](test.md) | Validation | check, build, test | Validation loops | -| [net](net.md) | Network | search, fetch, download, crawl | Network operations | -| [plan](plan.md) | Orchestration | intent, route, compose | Intent routing | - -### Control Surfaces - -| Tool | Surface | Description | -|------|---------|-------------| -| [browser](browser.md) | Web DOM | Playwright automation (70+ actions) | -| [computer](computer.md) | OS Desktop | Mac automation via pyautogui | - -### Extended Operators - -| Tool | Domain | Description | -|------|--------|-------------| -| [lsp](lsp.md) | Semantic Stream | Language server protocol (diagnostics, code_actions) | -| [memory](memory.md) | Knowledge | Persistent memory and knowledge bases | -| [todo](todo.md) | Task Tracking | Task management | -| [reasoning](reasoning.md) | Cognition | Structured thinking (think, critic) | -| [agent](agent.md) | Multi-Agent | Agent orchestration (run, list, status) | -| [llm](llm-tools.md) | LLM Interface | Unified LLM interface (llm, consensus) | - -### Infrastructure - -| Package | Tools | Description | -|---------|-------|-------------| -| [Core](core.md) | Base classes | BaseTool, IdTool, ToolRegistry | -| [Config](config.md) | 2 | Configuration and mode management | -| [Database](database.md) | 8 | SQL and graph database operations | -| [Vector](vector.md) | 3 | Semantic search with embeddings | -| [Refactor](refactor.md) | 1 | Code refactoring (rename, extract, inline) | -| [Jupyter](jupyter.md) | 1 | Notebook read/edit/execute | -| [Editor](editor.md) | 3 | Neovim integration | -| [MCP](mcp-tools.md) | 4 | MCP server management | - -## Quick Reference - -### Most Used Tools - -```python -# File operations -read(file_path="/path/to/file") -write(file_path="/path/to/file", content="...") -edit(file_path="/path/to/file", old_string="old", new_string="new") - -# Command execution -cmd("ls -la") -cmd(["npm install", "npm build"], parallel=True) - -# Search -search(pattern="TODO", path=".") -ast(pattern="def test_", path="/tests") - -# Reasoning -think(thought="Analyzing the problem...") -critic(analysis="Code review findings...") - -# Browser -browser(action="navigate", url="https://example.com") -browser(action="click", selector="button") -``` - -### Tool Discovery - -```python -from importlib.metadata import entry_points - -# Discover all available tools -for ep in entry_points(group="hanzo.tools"): - tools = ep.load() - print(f"{ep.name}: {[t.name for t in tools]}") -``` - -## Architecture - -All tools follow a consistent pattern: - -```python -from hanzo_tools.core import BaseTool, ToolContext - -class MyTool(BaseTool): - name = "my_tool" - - @property - def description(self) -> str: - return "Tool description" - - async def call(self, ctx: ToolContext, **params) -> str: - # Implementation - return result -``` - -## See Also - -- [Core Libraries](../lib/index.md) - Supporting packages (async, consensus, network) -- [MCP Reference](../ref/mcp/index.md) - Server documentation -- [Agent Framework](../ref/agent/index.md) - Multi-agent SDK diff --git a/docs/tools/jupyter.md b/docs/tools/jupyter.md deleted file mode 100644 index 78406a1b5..000000000 --- a/docs/tools/jupyter.md +++ /dev/null @@ -1,213 +0,0 @@ -# Jupyter Tools - -The Jupyter tools (`hanzo-tools-jupyter`) provide comprehensive Jupyter notebook operations including reading, editing, creating, and executing notebooks. - -## Overview - -Work with `.ipynb` files programmatically - read cell contents, modify cells, create new notebooks, and execute code cells. - -## jupyter - Unified Notebook Tool - -The unified `jupyter` tool handles all notebook operations through an action parameter. - -### Read Notebook - -```python -# Read entire notebook -jupyter(notebook_path="analysis.ipynb") - -# Read specific cell by index -jupyter(notebook_path="analysis.ipynb", cell_index=2) - -# Read specific cell by ID -jupyter(notebook_path="analysis.ipynb", cell_id="abc123") -``` - -### Edit Notebook - -```python -# Replace cell content -jupyter( - action="edit", - notebook_path="analysis.ipynb", - cell_index=0, - source="print('Hello, World!')" -) - -# Insert new cell -jupyter( - action="edit", - notebook_path="analysis.ipynb", - edit_mode="insert", - cell_index=2, - cell_type="code", - source="import pandas as pd" -) - -# Delete cell -jupyter( - action="edit", - notebook_path="analysis.ipynb", - edit_mode="delete", - cell_index=3 -) - -# Change cell type -jupyter( - action="edit", - notebook_path="analysis.ipynb", - cell_index=0, - cell_type="markdown", - source="# Analysis Results" -) -``` - -### Create Notebook - -```python -# Create empty notebook -jupyter(action="create", notebook_path="new_analysis.ipynb") -``` - -### Delete Notebook - -```python -# Delete entire notebook -jupyter(action="delete", notebook_path="old.ipynb") - -# Delete specific cell -jupyter(action="delete", notebook_path="analysis.ipynb", cell_index=5) -``` - -### Execute Notebook - -```python -# Execute all cells -jupyter(action="execute", notebook_path="analysis.ipynb") - -# Execute with custom timeout -jupyter(action="execute", notebook_path="analysis.ipynb", timeout=600) - -# Execute with specific kernel -jupyter(action="execute", notebook_path="analysis.ipynb", kernel_name="python3") -``` - -## Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `action` | str | `read` | `read`, `edit`, `create`, `delete`, `execute` | -| `notebook_path` | str | required | Path to .ipynb file | -| `cell_id` | str | - | Cell ID for targeted operations | -| `cell_index` | int | - | Cell index (0-based) | -| `cell_type` | str | - | `code` or `markdown` | -| `source` | str | - | New cell content | -| `edit_mode` | str | `replace` | `replace`, `insert`, `delete` | -| `timeout` | int | `600` | Execution timeout (seconds) | -| `kernel_name` | str | `python3` | Jupyter kernel to use | - -## Edit Modes - -| Mode | Description | -|------|-------------| -| `replace` | Replace existing cell content | -| `insert` | Insert new cell at index | -| `delete` | Delete cell at index | - -## Cell Types - -| Type | Description | -|------|-------------| -| `code` | Executable Python code | -| `markdown` | Markdown text/documentation | - -## Output Format - -When reading notebooks, output includes: - -``` -Notebook with 5 cells -================================================== - -Cell 0 (code) -ID: abc123 ----------------------------------------- -import pandas as pd -import numpy as np - -Outputs: -[Out 1]: - -Cell 1 (markdown) ----------------------------------------- -# Data Analysis -This notebook analyzes... -``` - -## Installation - -```bash -pip install hanzo-tools-jupyter -``` - -For notebook execution, also install: - -```bash -pip install nbclient -``` - -## Dependencies - -- `nbformat` - Notebook file format handling -- `nbclient` (optional) - For notebook execution - -## Best Practices - -### 1. Use Cell IDs for Stability - -```python -# Prefer cell IDs over indices when available -jupyter(action="edit", notebook_path="nb.ipynb", cell_id="stable-id", source="...") -``` - -### 2. Validate Before Executing - -```python -# Read first to check cell contents -result = jupyter(notebook_path="analysis.ipynb") -print(result) - -# Then execute -jupyter(action="execute", notebook_path="analysis.ipynb") -``` - -### 3. Set Appropriate Timeouts - -```python -# Long-running analysis -jupyter(action="execute", notebook_path="ml_training.ipynb", timeout=3600) -``` - -### 4. Use Markdown for Documentation - -```python -# Add documentation cells -jupyter( - action="edit", - notebook_path="analysis.ipynb", - edit_mode="insert", - cell_index=0, - cell_type="markdown", - source="# Analysis Report\n\nGenerated by automated pipeline." -) -``` - -## Error Handling - -Common errors and solutions: - -| Error | Cause | Solution | -|-------|-------|----------| -| `nbclient not installed` | Missing execution dependency | `pip install nbclient` | -| `Cell index out of range` | Invalid cell index | Check notebook length first | -| `Kernel not found` | Invalid kernel name | Use `python3` or check available kernels | diff --git a/docs/tools/llm-tools.md b/docs/tools/llm-tools.md deleted file mode 100644 index ba467a811..000000000 --- a/docs/tools/llm-tools.md +++ /dev/null @@ -1,321 +0,0 @@ -# hanzo-tools-llm - -Unified LLM interface with multi-model consensus. Access 100+ models through a single tool. - -## Installation - -```bash -pip install hanzo-tools-llm[full] -``` - -Note: This package requires `llm` which is a heavy dependency. It's disabled by default in hanzo-mcp. - -## Overview - -`hanzo-tools-llm` provides: - -- **llm** - Unified LLM interface (query, consensus, model management) -- **consensus** - Multi-model consensus protocol - -## Quick Start - -```python -# Simple query -llm(action="query", prompt="Explain quantum computing", model="gpt-4") - -# Multi-model consensus -llm( - action="consensus", - prompt="Best approach for distributed caching?", - models=["gpt-4", "claude-3-opus", "gemini-pro"] -) - -# List available models -llm(action="list") - -# Enable/disable providers -llm(action="enable", provider="anthropic") -llm(action="disable", provider="openai") -``` - -## Actions Reference - -### query - -Send a prompt to an LLM. - -```python -# Basic query -llm(action="query", prompt="Hello, world!", model="gpt-4") - -# With system prompt -llm( - action="query", - prompt="Explain this code", - model="claude-3-opus-20240229", - system_prompt="You are a senior software engineer" -) - -# With temperature control -llm( - action="query", - prompt="Generate creative names", - model="gpt-4", - temperature=0.9 -) - -# JSON mode -llm( - action="query", - prompt="List 5 programming languages with pros/cons", - model="gpt-4", - json_mode=True -) - -# Streaming -llm( - action="query", - prompt="Write a long story", - model="gpt-4", - stream=True -) -``` - -**Parameters:** -- `prompt` (required): The prompt to send -- `model`: Model name (default: auto-select) -- `system_prompt`: System context -- `temperature`: Response randomness (0-2, default: 0.7) -- `max_tokens`: Maximum response tokens -- `json_mode`: Request JSON output (default: false) -- `stream`: Stream response (default: false) - -### consensus - -Get consensus from multiple models. - -```python -llm( - action="consensus", - prompt="What's the best way to handle errors in Go?", - models=["gpt-4", "claude-3-opus", "gemini-pro"], - include_raw=True, # Include individual responses - judge_model="gpt-4", # Model to synthesize consensus - devils_advocate=True # Add critical analysis -) -``` - -**Parameters:** -- `prompt` (required): Question for consensus -- `models`: List of models to query (default: auto-select 3) -- `consensus_size`: Number of models if not specifying (default: 3) -- `include_raw`: Include raw responses (default: false) -- `judge_model`: Model to aggregate responses -- `devils_advocate`: Add 10th model for critique (default: false) - -**Response:** -```json -{ - "consensus": "The agreed-upon answer synthesized from all models...", - "confidence": 0.85, - "models_queried": ["gpt-4", "claude-3-opus", "gemini-pro"], - "agreement_level": "high", - "raw_responses": [...] // if include_raw=true -} -``` - -### list - -List available models and providers. - -```python -llm(action="list") -``` - -**Response:** -```json -{ - "providers": { - "openai": {"enabled": true, "models": ["gpt-4", "gpt-3.5-turbo"]}, - "anthropic": {"enabled": true, "models": ["claude-3-opus", "claude-3-sonnet"]}, - "google": {"enabled": false, "models": ["gemini-pro"]} - } -} -``` - -### models - -List models for a specific provider. - -```python -llm(action="models", provider="openai") -``` - -### enable / disable - -Enable or disable a provider. - -```python -# Enable a provider -llm(action="enable", provider="anthropic") - -# Disable a provider -llm(action="disable", provider="openai") -``` - -### test - -Test connectivity to a model. - -```python -llm(action="test", model="gpt-4") -``` - -## Supported Providers - -The LLM tool uses [LLM](https://github.com/BerriAI/llm) to support 100+ models: - -| Provider | Models | API Key Env | -|----------|--------|-------------| -| OpenAI | gpt-4, gpt-3.5-turbo | `OPENAI_API_KEY` | -| Anthropic | claude-3-opus, claude-3-sonnet | `ANTHROPIC_API_KEY` | -| Google | gemini-pro, gemini-ultra | `GOOGLE_API_KEY` | -| Azure | All Azure OpenAI models | `AZURE_API_KEY` | -| AWS Bedrock | claude, titan | AWS credentials | -| Cohere | command, command-r | `COHERE_API_KEY` | -| Together | Various open models | `TOGETHER_API_KEY` | -| Ollama | Local models | - | -| And more... | | | - -## ConsensusTool - -Dedicated tool for multi-model consensus: - -```python -consensus( - prompt="Should we use microservices or monolith?", - models=["gpt-4", "claude-3-opus", "gemini-pro"], - rounds=3 -) -``` - -## Examples - -### Code Review with Consensus - -```python -# Get multiple perspectives on code -llm( - action="consensus", - prompt="""Review this function for issues: - - def process(data): - result = [] - for item in data: - if item > 0: - result.append(item * 2) - return result - """, - models=["gpt-4", "claude-3-opus", "gemini-pro"], - devils_advocate=True -) -``` - -### Choosing Best Model for Task - -```python -# List available models -models = llm(action="list") - -# Test specific model -llm(action="test", model="claude-3-opus-20240229") - -# Query with best model -llm( - action="query", - prompt="Complex reasoning task", - model="claude-3-opus-20240229" -) -``` - -### JSON Output - -```python -# Get structured data -response = llm( - action="query", - prompt="List 3 Python web frameworks with their pros and cons", - model="gpt-4", - json_mode=True -) - -# Response will be valid JSON -``` - -### Creative vs Deterministic - -```python -# Creative task (high temperature) -llm( - action="query", - prompt="Write a poem about coding", - model="gpt-4", - temperature=0.9 -) - -# Deterministic task (low temperature) -llm( - action="query", - prompt="Convert this SQL to Python", - model="gpt-4", - temperature=0.1 -) -``` - -## Configuration - -### API Keys - -Set API keys via environment variables: - -```bash -export OPENAI_API_KEY="sk-..." -export ANTHROPIC_API_KEY="sk-ant-..." -export GOOGLE_API_KEY="..." -``` - -### Default Model - -Set default model via environment: - -```bash -export HANZO_DEFAULT_LLM_MODEL="gpt-4" -``` - -### Model Aliases - -Configure model aliases in `~/.hanzo/llm/aliases.json`: - -```json -{ - "smart": "gpt-4", - "fast": "gpt-3.5-turbo", - "creative": "claude-3-opus-20240229" -} -``` - -## Performance Tips - -1. **Use streaming for long responses** - Better UX for users -2. **Cache responses when appropriate** - Same prompt = same response -3. **Choose model based on task** - GPT-3.5 for simple, GPT-4 for complex -4. **Set max_tokens** - Avoid unnecessarily long responses -5. **Use consensus for important decisions** - Multiple perspectives reduce errors - -## Best Practices - -1. **Set appropriate temperature** - Low for factual, high for creative -2. **Use system prompts** - Provide context for better responses -3. **Validate JSON output** - Even with json_mode, validate responses -4. **Handle rate limits** - LLM handles retries automatically -5. **Monitor costs** - Different models have different pricing diff --git a/docs/tools/lsp.md b/docs/tools/lsp.md deleted file mode 100644 index cbd567c0e..000000000 --- a/docs/tools/lsp.md +++ /dev/null @@ -1,283 +0,0 @@ -# hanzo-tools-lsp - -Language Server Protocol tool for code intelligence. Provides go-to-definition, find references, rename, hover, and more. - -## Installation - -```bash -pip install hanzo-tools-lsp -``` - -Or as part of the full toolkit: - -```bash -pip install hanzo-mcp[tools-all] -``` - -## Overview - -`hanzo-tools-lsp` provides on-demand LSP configuration and automatic installation: - -- **definition** - Go to definition of symbol at position -- **references** - Find all references to symbol -- **rename** - Rename symbol across codebase -- **hover** - Get hover information at position -- **completion** - Get code completions at position -- **diagnostics** - Get errors and warnings for file -- **status** - Check LSP server status - -## Supported Languages - -| Language | LSP Server | Install Command | -|----------|------------|-----------------| -| Go | gopls | `go install golang.org/x/tools/gopls@latest` | -| Python | pyright | `npm install -g pyright` | -| TypeScript/JS | typescript-language-server | `npm install -g typescript typescript-language-server` | -| Rust | rust-analyzer | `rustup component add rust-analyzer` | -| Java | jdtls | `brew install jdtls` | -| C/C++ | clangd | `brew install llvm` | -| Ruby | solargraph | `gem install solargraph` | -| Lua | lua-language-server | `brew install lua-language-server` | - -## Quick Start - -```python -# Go to definition -lsp(action="definition", file="/path/to/file.go", line=10, character=15) - -# Find all references -lsp(action="references", file="/path/to/file.py", line=25, character=8) - -# Rename symbol -lsp(action="rename", file="/path/to/file.ts", line=5, character=10, new_name="newName") - -# Get hover info -lsp(action="hover", file="/path/to/file.rs", line=42, character=20) - -# Get completions -lsp(action="completion", file="/path/to/file.py", line=30, character=5) - -# Check status -lsp(action="status", file="/path/to/file.go") -``` - -## Actions Reference - -### definition - -Find where a symbol is defined. - -```python -lsp(action="definition", file="main.go", line=42, character=15) -``` - -**Response:** -```json -{ - "action": "definition", - "file": "main.go", - "definition": { - "file": "/project/pkg/utils.go", - "start": {"line": 25, "character": 5}, - "end": {"line": 25, "character": 15} - } -} -``` - -### references - -Find all references to a symbol. - -```python -lsp(action="references", file="utils.py", line=10, character=4) -``` - -**Response:** -```json -{ - "action": "references", - "file": "utils.py", - "references": [ - {"file": "main.py", "start": {"line": 15, "character": 8}}, - {"file": "tests/test_utils.py", "start": {"line": 22, "character": 12}} - ], - "count": 2 -} -``` - -### rename - -Rename a symbol across the codebase. - -```python -lsp(action="rename", file="models.ts", line=5, character=10, new_name="newClassName") -``` - -**Response:** -```json -{ - "action": "rename", - "file": "models.ts", - "new_name": "newClassName", - "changes": { - "/project/models.ts": [ - {"range": {...}, "newText": "newClassName"} - ], - "/project/index.ts": [ - {"range": {...}, "newText": "newClassName"} - ] - }, - "files_affected": 2 -} -``` - -### hover - -Get documentation and type information at a position. - -```python -lsp(action="hover", file="main.rs", line=30, character=8) -``` - -**Response:** -```json -{ - "action": "hover", - "file": "main.rs", - "position": {"line": 30, "character": 8}, - "contents": "fn process(data: &[u8]) -> Result<(), Error>\n\nProcesses the input data..." -} -``` - -### completion - -Get code completions at a position. - -```python -lsp(action="completion", file="app.py", line=25, character=10) -``` - -**Response:** -```json -{ - "action": "completion", - "file": "app.py", - "position": {"line": 25, "character": 10}, - "completions": [ - {"label": "append", "kind": 2, "detail": "(element) -> None"}, - {"label": "extend", "kind": 2, "detail": "(iterable) -> None"} - ], - "count": 2 -} -``` - -### status - -Check if LSP server is installed and get capabilities. - -```python -lsp(action="status", file="main.go") -``` - -**Response:** -```json -{ - "language": "go", - "lsp_server": "gopls", - "installed": true, - "capabilities": ["definition", "references", "rename", "diagnostics", "hover", "completion"] -} -``` - -## Architecture - -### Automatic Server Management - -The LSP tool automatically: -1. Detects language from file extension -2. Finds project root based on language markers (go.mod, package.json, etc.) -3. Checks if LSP server is installed -4. Installs LSP server if needed -5. Starts and initializes the server -6. Maintains singleton server per language:project_root - -### Server Lifecycle - -``` -Request โ†’ Detect Language โ†’ Find Project Root - โ†“ -Check Cache (language:root_uri) - โ†“ -If not running: Install โ†’ Start โ†’ Initialize - โ†“ -Execute LSP Request โ†’ Return Result -``` - -### Global Server Registry - -LSP servers are managed globally to avoid redundant instances: - -```python -# Servers are keyed by language:root_uri -# e.g., "go:/home/user/project" -# Cleanup happens automatically on process exit -``` - -## Examples - -### Code Navigation - -```python -# Find where a function is defined -lsp(action="definition", file="handlers/user.go", line=45, character=12) - -# Find all places that call this function -lsp(action="references", file="handlers/user.go", line=45, character=12) -``` - -### Safe Refactoring - -```python -# First, check all references -refs = lsp(action="references", file="models.py", line=10, character=6) - -# Then rename -lsp(action="rename", file="models.py", line=10, character=6, new_name="UserProfile") -``` - -### IDE-like Experience - -```python -# Get documentation on hover -lsp(action="hover", file="main.rs", line=25, character=8) - -# Get completions while typing -lsp(action="completion", file="app.ts", line=30, character=15) -``` - -## Configuration - -### Project Root Detection - -The LSP tool finds project roots using language-specific markers: - -| Language | Markers | -|----------|---------| -| Go | go.mod, go.sum | -| Python | pyproject.toml, setup.py, requirements.txt | -| TypeScript | tsconfig.json, package.json | -| Rust | Cargo.toml | -| Java | pom.xml, build.gradle | -| C/C++ | compile_commands.json, CMakeLists.txt | -| Ruby | Gemfile | - -### Custom LSP Servers - -For custom configurations, ensure the LSP server is in your PATH before using the tool. - -## Best Practices - -1. **Use status first** - Check if LSP is available before heavy operations -2. **Provide accurate positions** - Line and character must match exact symbol position -3. **Let servers persist** - Servers are cached; avoid restarting unnecessarily -4. **Combine with other tools** - Use with search/ast for comprehensive code analysis diff --git a/docs/tools/mcp-tools.md b/docs/tools/mcp-tools.md deleted file mode 100644 index 6f46c0930..000000000 --- a/docs/tools/mcp-tools.md +++ /dev/null @@ -1,253 +0,0 @@ -# MCP Management Tools - -The MCP tools (`hanzo-tools-mcp`) provide management capabilities for MCP (Model Context Protocol) servers and configurations. - -## Overview - -These tools allow you to manage MCP server connections, add new servers, remove servers, and monitor statistics. - -## mcp - Server Management - -The main MCP management tool: - -```python -# List configured MCP servers -mcp(action="list") - -# Get server status -mcp(action="status") - -# Get specific server info -mcp(action="info", server_name="filesystem") - -# Restart a server -mcp(action="restart", server_name="browser") -``` - -### Actions - -| Action | Description | -|--------|-------------| -| `list` | List all configured servers | -| `status` | Show running status of servers | -| `info` | Get detailed server information | -| `restart` | Restart a specific server | - -## mcp_add - Add Servers - -Add new MCP server configurations: - -```python -# Add stdio server -mcp_add( - name="my-server", - command="uvx my-mcp-server", - transport="stdio" -) - -# Add SSE server -mcp_add( - name="remote-server", - url="http://localhost:8080/sse", - transport="sse" -) - -# Add with environment variables -mcp_add( - name="api-server", - command="node server.js", - env={"API_KEY": "secret123"} -) - -# Add with arguments -mcp_add( - name="custom-server", - command="python", - args=["-m", "my_mcp", "--port", "9000"] -) -``` - -### Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `name` | str | required | Server identifier | -| `command` | str | - | Command to spawn server | -| `url` | str | - | URL for SSE/HTTP transport | -| `transport` | str | `stdio` | `stdio`, `sse`, `http` | -| `args` | list | - | Additional command arguments | -| `env` | dict | - | Environment variables | - -## mcp_remove - Remove Servers - -Remove MCP server configurations: - -```python -# Remove by name -mcp_remove(name="old-server") - -# Remove with confirmation -mcp_remove(name="important-server", confirm=True) -``` - -### Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `name` | str | required | Server to remove | -| `confirm` | bool | `False` | Require confirmation | - -## mcp_stats - Statistics - -Get MCP usage statistics: - -```python -# Overall statistics -mcp_stats() - -# Per-server statistics -mcp_stats(server_name="filesystem") - -# Stats for time period -mcp_stats(period="1h") -``` - -### Output - -``` -MCP Statistics -============== - -Servers: 5 configured, 4 running - -Tool Calls (last hour): - filesystem: 142 calls - browser: 38 calls - shell: 256 calls - memory: 24 calls - -Errors: 3 total - - filesystem: 1 (permission denied) - - browser: 2 (timeout) -``` - -## Transport Types - -| Transport | Use Case | Requirements | -|-----------|----------|--------------| -| `stdio` | Local servers | Executable command | -| `sse` | Remote servers | HTTP endpoint | -| `http` | Streamable HTTP | HTTP endpoint | - -### stdio Transport - -Spawns server as subprocess: - -```python -mcp_add( - name="local", - command="uvx hanzo-mcp", - transport="stdio" -) -``` - -### SSE Transport - -Connects to Server-Sent Events endpoint: - -```python -mcp_add( - name="remote", - url="http://api.example.com/mcp/sse", - transport="sse" -) -``` - -### HTTP Transport - -Streamable HTTP transport: - -```python -mcp_add( - name="http-server", - url="http://localhost:8080/mcp", - transport="http" -) -``` - -## Installation - -```bash -pip install hanzo-tools-mcp -``` - -## Configuration File - -MCP servers are typically configured in: -- `~/.config/hanzo/mcp.json` (global) -- `.hanzo/mcp.json` (project-local) - -Example configuration: - -```json -{ - "servers": { - "filesystem": { - "command": "uvx hanzo-tools-fs", - "transport": "stdio" - }, - "browser": { - "command": "uvx hanzo-tools-browser", - "transport": "stdio", - "env": { - "BROWSER_HEADLESS": "true" - } - } - } -} -``` - -## Best Practices - -### 1. Use Environment Variables for Secrets - -```python -mcp_add( - name="api-server", - command="my-server", - env={"API_KEY": "${API_KEY}"} # Reference from environment -) -``` - -### 2. Group Related Servers - -Organize servers by function: - -```python -# Development servers -mcp_add(name="dev-db", command="...") -mcp_add(name="dev-cache", command="...") - -# Production servers (separate config) -mcp_add(name="prod-db", command="...", env={"ENV": "production"}) -``` - -### 3. Monitor Statistics - -```python -# Regular health checks -stats = mcp_stats() -if stats.error_count > threshold: - alert("MCP errors detected") -``` - -### 4. Use Local Config for Projects - -```python -# Project-specific servers in .hanzo/mcp.json -mcp_add( - name="project-specific", - command="./scripts/mcp-server.sh", - config_path=".hanzo/mcp.json" -) -``` diff --git a/docs/tools/memory.md b/docs/tools/memory.md deleted file mode 100644 index 6da86e061..000000000 --- a/docs/tools/memory.md +++ /dev/null @@ -1,283 +0,0 @@ -# hanzo-tools-memory - -Unified memory and knowledge management for AI agents. Store, recall, and manage memories and facts across sessions. - -## Installation - -```bash -pip install hanzo-tools-memory -``` - -Or as part of the full toolkit: - -```bash -pip install hanzo-mcp[tools-all] -``` - -## Overview - -`hanzo-tools-memory` provides: - -- **Memory** - Episodic memory for storing and recalling information -- **Facts** - Knowledge base management for structured information -- **Scopes** - Session, project, and global memory levels -- **Summarization** - Automatic content summarization - -## Quick Start - -```python -# Store a memory -memory(action="create", data={"content": "User prefers dark mode"}) - -# Recall memories -memory(action="recall", query="user preferences") - -# Store facts -memory(action="store_facts", data={ - "facts": ["API rate limit is 100/hour"], - "kb_name": "api_docs" -}) - -# Recall facts -memory(action="facts", query="rate limit") -``` - -## Memory Actions - -### create - -Store new memories: - -```python -memory(action="create", data={ - "content": "User prefers TypeScript over JavaScript", - "scope": "project" # session, project, or global -}) - -# Store multiple memories -memory(action="create", data={ - "statements": [ - "User works in Python", - "Project uses FastAPI", - "Prefers minimal dependencies" - ] -}) -``` - -### recall - -Search and retrieve memories: - -```python -# Simple query -memory(action="recall", query="user preferences") - -# With scope filter -memory(action="recall", query="coding style", scope="project") - -# Limit results -memory(action="recall", query="API endpoints", limit=5) - -# Multiple queries (parallel search) -memory(action="recall", data={ - "queries": ["database schema", "API design", "error handling"] -}) -``` - -### update - -Modify existing memories: - -```python -memory(action="update", data={ - "updates": [ - {"id": "mem_123", "statement": "User now prefers light mode"}, - {"id": "mem_456", "statement": "Updated API endpoint"} - ] -}) -``` - -### delete - -Remove memories: - -```python -memory(action="delete", data={ - "ids": ["mem_123", "mem_456"] -}) -``` - -### manage - -Batch operations (create, update, delete in one call): - -```python -memory(action="manage", data={ - "creations": ["New fact 1", "New fact 2"], - "updates": [{"id": "mem_1", "statement": "Updated"}], - "deletions": ["mem_old1", "mem_old2"] -}) -``` - -## Facts & Knowledge Bases - -### store_facts - -Store structured facts in knowledge bases: - -```python -memory(action="store_facts", data={ - "facts": [ - "Python uses indentation for blocks", - "FastAPI supports async/await" - ], - "kb_name": "python_basics", - "scope": "project" -}) -``` - -### facts - -Query knowledge bases: - -```python -# Query specific knowledge base -memory(action="facts", query="async patterns", kb_name="python_basics") - -# Query all knowledge bases -memory(action="facts", query="error handling") - -# With scope -memory(action="facts", query="API docs", scope="global") -``` - -### kb_manage - -Manage knowledge bases: - -```python -# Create knowledge base -memory(action="kb_manage", data={ - "action": "create", - "kb_name": "api_docs", - "description": "API documentation and endpoints" -}) - -# List knowledge bases -memory(action="kb_manage", data={"action": "list", "scope": "project"}) - -# Delete knowledge base -memory(action="kb_manage", data={"action": "delete", "kb_name": "old_docs"}) -``` - -## Summarization - -### summarize - -Summarize content and store in memory: - -```python -memory(action="summarize", data={ - "content": "Long discussion about API design decisions...", - "topic": "API Design Decisions", - "scope": "project", - "auto_facts": True # Auto-extract facts -}) -``` - -## Scopes - -Memories exist at three levels: - -| Scope | Persistence | Use Case | -|-------|-------------|----------| -| `session` | Current session only | Temporary context | -| `project` | Per-project | Project-specific knowledge | -| `global` | All projects | User preferences, global facts | - -```python -# Session memory (temporary) -memory(action="create", data={ - "content": "Currently debugging auth issue", - "scope": "session" -}) - -# Project memory (persistent) -memory(action="create", data={ - "content": "Project uses PostgreSQL", - "scope": "project" -}) - -# Global memory (shared) -memory(action="create", data={ - "content": "User prefers Vim keybindings", - "scope": "global" -}) -``` - -## Storage - -Memories are stored in: - -- `~/.hanzo/memory/session/` - Session memories -- `~/.hanzo/memory/project//` - Project memories -- `~/.hanzo/memory/global/` - Global memories - -## Examples - -### Context Building - -```python -# At start of session, recall relevant context -memories = memory(action="recall", data={ - "queries": [ - "project architecture", - "recent changes", - "pending tasks" - ], - "scope": "project" -}) -``` - -### Learning User Preferences - -```python -# Store preference when learned -memory(action="create", data={ - "content": "User prefers functional programming style", - "scope": "global" -}) - -# Later, recall preferences -prefs = memory(action="recall", query="coding style preferences") -``` - -### Project Documentation - -```python -# Store project facts -memory(action="store_facts", data={ - "facts": [ - "Database: PostgreSQL 15", - "ORM: SQLAlchemy 2.0", - "API Framework: FastAPI", - "Auth: JWT tokens" - ], - "kb_name": "tech_stack", - "scope": "project" -}) - -# Query later -stack = memory(action="facts", query="what database", kb_name="tech_stack") -``` - -### Session Summarization - -```python -# At end of session, summarize work -memory(action="summarize", data={ - "content": conversation_history, - "topic": "Session Summary - Auth Implementation", - "scope": "project" -}) -``` diff --git a/docs/tools/net.md b/docs/tools/net.md deleted file mode 100644 index 26685f345..000000000 --- a/docs/tools/net.md +++ /dev/null @@ -1,242 +0,0 @@ -# Net Tool - -Network operations: search, fetch, download, crawl (HIP-0300 operator). - -## Installation - -```bash -pip install hanzo-tools-net - -# With full HTML parsing support -pip install hanzo-tools-net[full] -``` - -## Overview - -The `net` tool handles all network operations: - -| Action | Signature | Effect | -|--------|-----------|--------| -| `search` | `(Query, engine?) โ†’ [{url, title, snippet}]` | NONDETERMINISTIC | -| `fetch` | `(URL, extract_text?) โ†’ {text, mime, status, hash}` | NONDETERMINISTIC | -| `download` | `(URL, dest?, assets?) โ†’ {path, size, mime}` | NONDETERMINISTIC | -| `crawl` | `(URL, dest, depth?, limit?) โ†’ {pages, count}` | NONDETERMINISTIC | -| `head` | `URL โ†’ {status, headers, size?, mime?}` | NONDETERMINISTIC | - -## Actions - -### search - -Perform web search queries. - -```python -net(action="search", query="python async best practices") -# Returns: { -# results: [ -# {url: "https://...", title: "Async Python Guide", snippet: "..."}, -# {url: "https://...", title: "Python Concurrency", snippet: "..."} -# ], -# query: "python async best practices", -# engine: "duckduckgo", -# count: 10 -# } - -net(action="search", query="rust memory safety", limit=5) -# Limit results -``` - -**Parameters:** -- `query` (str): Search query -- `engine` (str, optional): Search engine ("duckduckgo" default) -- `limit` (int, optional): Maximum results (default: 10) - -### fetch - -Retrieve content from a URL. - -```python -net(action="fetch", url="https://example.com/api/data") -# Returns: { -# text: "{\"data\": [...]}", -# mime: "application/json", -# status: 200, -# hash: "sha256:abc123...", -# size: 1234, -# url: "https://example.com/api/data" -# } - -net(action="fetch", url="https://example.com", extract_text=True) -# Returns: { -# text: "Example Domain. This domain is for use in...", # Cleaned text -# mime: "text/html", -# status: 200, -# ... -# } -``` - -**Parameters:** -- `url` (str): URL to fetch -- `extract_text` (bool, optional): Extract text from HTML (default: False) -- `headers` (dict, optional): Additional HTTP headers - -### download - -Save URL content to local file. - -```python -net(action="download", url="https://example.com/report.pdf") -# Returns: { -# path: "/current/dir/report.pdf", -# size: 245678, -# mime: "application/pdf", -# url: "https://example.com/report.pdf" -# } - -net(action="download", url="https://example.com", dest="./mirror/index.html", assets=True) -# Returns: { -# path: "./mirror/index.html", -# size: 5678, -# mime: "text/html", -# assets: ["./mirror/index_assets/style.css", "./mirror/index_assets/logo.png"], -# assets_count: 12 -# } -``` - -**Parameters:** -- `url` (str): URL to download -- `dest` (str, optional): Destination path (auto-generated if not specified) -- `assets` (bool, optional): Download page assets (images, CSS, JS) - -### crawl - -Recursively mirror a website. - -```python -net(action="crawl", url="https://docs.example.com", dest="./mirror", depth=2) -# Returns: { -# pages: [ -# "./mirror/index.html", -# "./mirror/getting-started.html", -# "./mirror/api/index.html", -# "./mirror/api/reference.html" -# ], -# count: 47, -# dest: "./mirror", -# depth: 2 -# } -``` - -**Parameters:** -- `url` (str): Starting URL -- `dest` (str): Destination directory -- `depth` (int, optional): Maximum crawl depth (default: 2) -- `same_host` (bool, optional): Only crawl same hostname (default: True) -- `limit` (int, optional): Maximum pages to download (default: 100) - -### head - -Get HTTP headers without downloading body. - -```python -net(action="head", url="https://example.com/large-file.zip") -# Returns: { -# status: 200, -# headers: { -# "content-type": "application/zip", -# "content-length": "1234567890", -# "last-modified": "..." -# }, -# size: 1234567890, -# mime: "application/zip", -# url: "https://example.com/large-file.zip" -# } -``` - -## Usage Examples - -### Research Workflow - -```python -# 1. Search for information -results = net(action="search", query="rust async runtime comparison 2024") - -# 2. Fetch promising articles -for result in results["data"]["results"][:3]: - content = net(action="fetch", url=result["url"], extract_text=True) - print(f"--- {result['title']} ---") - print(content["data"]["text"][:500]) -``` - -### Documentation Mirror - -```python -# Mirror documentation for offline access -result = net(action="crawl", - url="https://docs.example.com", - dest="./docs-mirror", - depth=3, - limit=200) - -print(f"Downloaded {result['data']['count']} pages") -``` - -### API Integration - -```python -# Fetch JSON API -response = net(action="fetch", - url="https://api.example.com/v1/users", - headers={"Authorization": "Bearer token..."}) - -import json -data = json.loads(response["data"]["text"]) -``` - -### Pre-flight Check - -```python -# Check if file exists and get size before downloading -info = net(action="head", url="https://example.com/large-dataset.tar.gz") - -if info["data"]["status"] == 200: - size_mb = info["data"]["size"] / (1024 * 1024) - print(f"File size: {size_mb:.1f} MB") - - if size_mb < 100: # Only download if < 100MB - net(action="download", url=info["data"]["url"]) -``` - -## Error Handling - -Network operations can fail. Handle errors appropriately: - -```python -result = net(action="fetch", url="https://example.com/404") - -if not result["ok"]: - print(f"Error: {result['error']['message']}") - # Error: HTTP 404: Not Found -``` - -## Dependencies - -- `httpx` - HTTP client (required) -- `beautifulsoup4` + `lxml` - HTML parsing (optional, for better text extraction) - -Install full dependencies: -```bash -pip install hanzo-tools-net[full] -``` - -## Rate Limiting - -The net tool respects standard rate limiting: -- Honors `Retry-After` headers -- Default timeout: 30 seconds -- Configurable via environment variables - -## See Also - -- [HIP-0300](../hip/HIP-0300.md) - Unified Tools Architecture -- [Browser Tool](browser.md) - For JavaScript-rendered content -- [Filesystem Tool](fs.md) - For local file operations diff --git a/docs/tools/plan.md b/docs/tools/plan.md deleted file mode 100644 index 81d062dc2..000000000 --- a/docs/tools/plan.md +++ /dev/null @@ -1,171 +0,0 @@ -# Plan Tool - -Orchestration and intent routing for agentic workflows (HIP-0300 operator). - -## Installation - -```bash -pip install hanzo-tools-plan -``` - -## Overview - -The `plan` tool routes natural language intents to canonical operator chains: - -| Action | Signature | Effect | -|--------|-----------|--------| -| `intent` | `NL โ†’ IntentIR` | PURE | -| `route` | `(IntentIR, Policy?) โ†’ Plan` | PURE | -| `compose` | `Plan โ†’ ExecGraph` | PURE | - -## Actions - -### intent - -Parse natural language into structured intent representation. - -```python -plan(action="intent", nl="find where user authentication happens") -# Returns: { -# category: "navigate", -# action: "find", -# target: "user authentication", -# confidence: 0.92 -# } - -plan(action="intent", nl="rename authenticate to verify_user") -# Returns: { -# category: "refactor", -# action: "rename", -# target: "authenticate", -# params: {new_name: "verify_user"}, -# confidence: 0.95 -# } -``` - -**Intent Categories:** -- `navigate` - Finding code/files (find, search, locate) -- `explain` - Understanding code (what, why, how) -- `modify` - Changing code (rename, extract, fix) -- `validate` - Testing/checking (test, verify, check) -- `debug` - Troubleshooting (trace, why fails) -- `create` - Adding new code (add, create, implement) - -### route - -Map IntentIR to a canonical operator chain. - -```python -intent_ir = {"category": "refactor", "action": "rename", "target": "foo"} -plan(action="route", intent_ir=intent_ir) -# Returns: { -# nodes: [ -# {tool: "code", action: "references", params: {symbol: "foo"}}, -# {tool: "code", action: "transform", params: {kind: "rename"}}, -# {tool: "code", action: "summarize"}, -# {tool: "fs", action: "patch", policy_gate: True}, -# {tool: "test", action: "run"} -# ], -# policy_gates: [3], -# estimated_steps: 5 -# } -``` - -**Parameters:** -- `intent_ir` (dict, optional): Structured intent from `intent` action -- `nl` (str, optional): Natural language (will call intent internally) -- `policy` (dict, optional): Custom policy overrides - -### compose - -Compile Plan into an execution graph with dependencies. - -```python -plan(action="compose", plan=plan_result) -# Returns: { -# graph: { -# nodes: [...], -# edges: [(0,1), (1,2), (2,3), (3,4)], -# entry: 0, -# exit: 4 -# }, -# execution_order: [0, 1, 2, 3, 4], -# parallelizable: [[0], [1], [2], [3, 4]], # Groups that can run in parallel -# estimated_complexity: "medium" -# } -``` - -## Canonical Chains - -Common intent โ†’ operator chain mappings: - -| Intent Pattern | Canonical Chain | -|---------------|-----------------| -| "find X" | `fs.search(X)` | -| "what is X" | `fs.search(X) โ†’ code.summarize` | -| "rename X to Y" | `code.references(X) โ†’ code.transform(rename) โ†’ [policy] โ†’ fs.patch โ†’ test.run` | -| "fix bug in X" | `fs.read(X) โ†’ code.parse โ†’ code.transform(fix) โ†’ [policy] โ†’ fs.patch โ†’ test.run` | -| "add tests for X" | `code.symbols(X) โ†’ code.transform(add_tests) โ†’ fs.write โ†’ test.run` | -| "why does X fail" | `test.run(X) โ†’ vcs.log โ†’ code.summarize` | -| "refactor X" | `code.references(X) โ†’ code.transform โ†’ [policy] โ†’ fs.patch โ†’ test.run` | - -## Policy Gates - -High-risk operations require explicit approval: - -```python -# In the plan output, policy_gate: True indicates approval needed -{ - "nodes": [ - {"tool": "code", "action": "transform", "params": {...}}, - {"tool": "fs", "action": "patch", "policy_gate": True} # <-- requires approval - ] -} -``` - -Configurable policy rules: -- File modifications (`fs.patch`, `fs.write`) -- Process execution with shell (`proc.run` with shell=True) -- Version control commits (`vcs.commit`, `vcs.push`) - -## Example Workflow - -```python -# 1. Parse user intent -intent = plan(action="intent", nl="rename the authenticate function to verify_credentials") - -# 2. Route to operator chain -workflow = plan(action="route", intent_ir=intent["data"]) - -# 3. Compile to execution graph -graph = plan(action="compose", plan=workflow["data"]) - -# 4. Execute with policy gates -for step in graph["data"]["execution_order"]: - node = graph["data"]["nodes"][step] - - if node.get("policy_gate"): - # Request user approval - approved = await request_approval(node) - if not approved: - break - - # Execute the step - result = await dispatch(node["tool"], node["action"], node["params"]) -``` - -## Custom Routing - -Override default routing with custom rules: - -```python -plan(action="route", nl="add caching to API", - policy={"prefer_tools": ["code", "test"], "require_tests": True}) -``` - -## See Also - -- [HIP-0300](../hip/HIP-0300.md) - Unified Tools Architecture -- [Agent Tool](agent.md) - Multi-agent orchestration -- [Code Tool](code.md) - Symbol and structure operations -- [Test Tool](test.md) - Validation operations diff --git a/docs/tools/reasoning.md b/docs/tools/reasoning.md deleted file mode 100644 index 4273d7fbb..000000000 --- a/docs/tools/reasoning.md +++ /dev/null @@ -1,328 +0,0 @@ -# hanzo-tools-reasoning - -Structured thinking and critical analysis tools for AI agents. Provides the `think` and `critic` tools for deliberate reasoning. - -## Installation - -```bash -pip install hanzo-tools-reasoning -``` - -Or as part of the full toolkit: - -```bash -pip install hanzo-mcp[tools-all] -``` - -## Overview - -`hanzo-tools-reasoning` provides: - -- **think** - Structured reasoning and brainstorming -- **critic** - Critical analysis and devil's advocate - -## think - -Use the think tool for deliberate reasoning without taking action. - -### When to Use - -1. **Exploring solutions** - Brainstorm approaches before implementing -2. **Debugging** - Organize hypotheses about failures -3. **Planning** - Think through architecture decisions -4. **Complex problems** - Break down multi-step solutions - -### Usage - -```python -think(thought=""" -Feature Implementation Planning -- New code search feature requirements: - * Search for code patterns across multiple files - * Identify function usages and references - * Analyze import relationships - * Generate summary of matching patterns - -- Implementation considerations: - * Need to leverage existing search mechanisms - * Should use regex for pattern matching - * Results need consistent format - * Must handle large codebases efficiently - -- Design approach: - 1. Create new CodeSearcher class - 2. Implement core pattern matching - 3. Add result formatting - 4. Integrate with file traversal - 5. Add caching for performance - -- Testing strategy: - * Unit tests for search accuracy - * Integration tests with existing components - * Performance tests with large codebases -""") -``` - -### Best Practices - -- **Be specific**: Include concrete details and constraints -- **Structure thoughts**: Use lists, sections, and hierarchies -- **Consider alternatives**: List multiple approaches -- **Note tradeoffs**: Document pros/cons of each option - -### Examples - -#### Bug Investigation - -```python -think(thought=""" -Bug Analysis: API returning 500 errors - -Symptoms: -- Intermittent 500 errors on /api/users endpoint -- Happens under high load -- Error logs show connection pool exhausted - -Hypotheses: -1. Connection pool too small - - Check: current pool size vs concurrent requests - - Fix: increase pool size or add connection recycling - -2. Slow queries blocking connections - - Check: query execution times - - Fix: add indexes, optimize queries - -3. Connection leak in error paths - - Check: connection release in exception handlers - - Fix: ensure connections released in finally blocks - -Investigation order: -1. Check connection pool metrics -2. Review slow query logs -3. Audit connection handling code -""") -``` - -#### Architecture Decision - -```python -think(thought=""" -Architecture Decision: State Management - -Options: -1. Redux - + Predictable state updates - + Great devtools - - Boilerplate heavy - - Overkill for simple apps - -2. Zustand - + Minimal API - + No providers needed - + TypeScript friendly - - Less ecosystem - -3. React Context + useReducer - + Built-in, no dependencies - + Familiar patterns - - Performance concerns at scale - - Manual optimization needed - -Recommendation: Zustand -- Our app is medium complexity -- Team prefers minimal boilerplate -- TypeScript is priority -- Performance is acceptable for our scale -""") -``` - -## critic - -Use the critic tool for critical analysis and quality assurance. - -### When to Use - -1. **Code review** - Analyze implementations for issues -2. **Before finalizing** - Ensure quality standards met -3. **Testing** - Question if tests are comprehensive -4. **Design review** - Challenge assumptions - -### Usage - -```python -critic(analysis=""" -Code Review Analysis: - -Implementation Issues: -- No error handling for network failures in API calls -- Missing validation for user input boundaries -- Race condition possible in concurrent updates -- Memory leak potential in event listener registration - -Test Coverage Gaps: -- No tests for error scenarios -- Missing edge case: empty array input -- No performance benchmarks for large datasets -- Integration tests don't cover auth failures - -Security Concerns: -- SQL injection vulnerability in query construction -- Missing rate limiting on public endpoints -- Sensitive data logged in debug mode - -Performance Issues: -- O(nยฒ) algorithm where O(n log n) is possible -- Database queries in a loop (N+1 problem) -- No caching for expensive computations - -Code Quality: -- Functions too long and doing multiple things -- Inconsistent naming conventions -- Missing type annotations -- No documentation for complex algorithms - -Design Flaws: -- Tight coupling between modules -- Hard-coded configuration values -- No abstraction for external dependencies -- Violates single responsibility principle - -Recommendations: -1. Add comprehensive error handling -2. Implement input validation -3. Use database transactions for race conditions -4. Parameterize SQL queries -5. Implement rate limiting -6. Refactor to smaller functions -7. Add missing type annotations -8. Batch database queries -""") -``` - -### The Inner Critic Mindset - -The critic tool forces critical thinking that: - -- **Questions assumptions** - "Is this really necessary?" -- **Looks for bugs** - "What could go wrong?" -- **Checks edge cases** - "What about empty input?" -- **Verifies security** - "Can this be exploited?" -- **Reviews performance** - "Will this scale?" -- **Ensures quality** - "Is this maintainable?" - -### Examples - -#### Feature Review - -```python -critic(analysis=""" -Review: User Authentication Feature - -Positive aspects: -- Clean API design -- Good separation of concerns -- Follows existing patterns - -Issues found: -1. Token expiry not handled - - Tokens never expire - - No refresh token mechanism - - Risk: Stolen tokens valid forever - -2. Password handling - - Using MD5 (insecure) - - No salt - - Should use bcrypt/argon2 - -3. Rate limiting missing - - No limit on login attempts - - Vulnerable to brute force - -4. Error messages too specific - - "Invalid password" reveals user exists - - Should use generic "Invalid credentials" - -5. Session management - - No logout invalidation - - Can't revoke sessions - - No concurrent session limit - -Required before merge: -- Switch to bcrypt -- Add rate limiting -- Implement token refresh -- Generic error messages -""") -``` - -#### Test Review - -```python -critic(analysis=""" -Test Suite Analysis: - -Coverage gaps: -- No tests for boundary conditions -- Error paths untested -- Async behavior not verified -- No integration tests with real DB - -Test quality issues: -- Tests too coupled to implementation -- Magic numbers without explanation -- No test documentation -- Flaky tests due to timing - -Missing test categories: -- Performance/load tests -- Security tests -- Accessibility tests -- Cross-browser tests - -Recommendations: -1. Add boundary condition tests -2. Test all error scenarios -3. Use proper test fixtures -4. Add integration test suite -5. Document test intentions -""") -``` - -## Combining Think and Critic - -Use both tools together for thorough analysis: - -```python -# First, think through the problem -think(thought=""" -Planning: Implement caching layer -- Need to cache API responses -- Options: Redis, in-memory, file-based -- Must handle invalidation -- Need TTL support -""") - -# Then, critically analyze the plan -critic(analysis=""" -Cache Implementation Review: - -Potential issues: -- Cache invalidation strategy unclear -- No handling for cache stampede -- Memory limits not considered -- No monitoring/metrics planned - -Missing considerations: -- What happens when cache is full? -- How to warm cache on startup? -- How to handle partial failures? -- What's the fallback if Redis down? - -Recommendations: -1. Define explicit invalidation triggers -2. Add circuit breaker for cache failures -3. Implement cache warming strategy -4. Add memory limits with eviction -5. Include cache hit/miss metrics -""") -``` diff --git a/docs/tools/refactor.md b/docs/tools/refactor.md deleted file mode 100644 index ce16beb1f..000000000 --- a/docs/tools/refactor.md +++ /dev/null @@ -1,353 +0,0 @@ -# hanzo-tools-refactor - -Advanced refactoring with LSP/AST support. FAST parallel processing for large codebases. - -## Installation - -```bash -pip install hanzo-tools-refactor -``` - -Or as part of the full toolkit: - -```bash -pip install hanzo-mcp[tools-all] -``` - -## Overview - -`hanzo-tools-refactor` provides intelligent code refactoring: - -- **rename** - Rename symbol across entire codebase -- **rename_batch** - Batch rename multiple symbols -- **extract_function** - Extract code into a new function -- **extract_variable** - Extract expression into a variable -- **inline** - Inline a function or variable -- **move** - Move symbol to another file -- **change_signature** - Modify function parameters -- **find_references** - Find all references to a symbol -- **organize_imports** - Clean up and sort imports - -## Quick Start - -```python -# Rename a symbol -refactor(action="rename", file="main.py", line=10, column=5, new_name="newName") - -# Batch rename multiple symbols -refactor( - action="rename_batch", - renames=[ - {"old": "foo", "new": "bar"}, - {"old": "baz", "new": "qux"} - ], - path="./src" -) - -# Change function signature -refactor( - action="change_signature", - file="utils.py", - line=15, - add_parameter={"name": "timeout", "default": "30"} -) - -# Find references -refactor(action="find_references", file="models.py", line=25, column=8) -``` - -## Actions Reference - -### rename - -Rename a symbol at a specific location. - -```python -refactor( - action="rename", - file="/path/to/file.py", - line=42, - column=10, - new_name="betterName" -) -``` - -**Parameters:** -- `file` (required): File containing the symbol -- `line` (required): Line number (1-based) -- `column` (required): Column number (1-based) -- `new_name` (required): New name for the symbol - -### rename_batch - -Rename multiple symbols across the codebase in parallel. - -```python -refactor( - action="rename_batch", - renames=[ - {"old": "getUserData", "new": "fetchUserData"}, - {"old": "setUserData", "new": "updateUserData"}, - {"old": "deleteUserData", "new": "removeUserData"} - ], - path="./src", - parallel=True # Default: true -) -``` - -**Parameters:** -- `renames` (required): List of {old, new} pairs -- `path`: Directory to search (default: current directory) -- `parallel`: Process in parallel (default: true) - -### extract_function - -Extract selected code into a new function. - -```python -refactor( - action="extract_function", - file="/path/to/file.py", - start_line=20, - end_line=30, - new_name="processData" -) -``` - -**Parameters:** -- `file` (required): Source file -- `start_line` (required): First line of code to extract -- `end_line` (required): Last line of code to extract -- `new_name`: Name for the new function - -### extract_variable - -Extract an expression into a variable. - -```python -refactor( - action="extract_variable", - file="/path/to/file.py", - line=15, - start_column=10, - end_column=45, - new_name="calculatedValue" -) -``` - -**Parameters:** -- `file` (required): Source file -- `line` (required): Line containing the expression -- `start_column`: Start of expression -- `end_column`: End of expression -- `new_name`: Variable name - -### inline - -Inline a function or variable at its call sites. - -```python -refactor( - action="inline", - file="/path/to/file.py", - line=10, - column=5 -) -``` - -**Parameters:** -- `file` (required): Source file -- `line` (required): Line of the symbol -- `column` (required): Column of the symbol - -### move - -Move a symbol to a different file. - -```python -refactor( - action="move", - file="/path/to/source.py", - line=15, - column=6, - target_file="/path/to/destination.py" -) -``` - -**Parameters:** -- `file` (required): Source file -- `line` (required): Line of the symbol -- `column` (required): Column of the symbol -- `target_file` (required): Destination file - -### change_signature - -Modify a function's parameters. - -```python -# Add a parameter -refactor( - action="change_signature", - file="api.py", - line=20, - add_parameter={"name": "timeout", "default": "30", "type": "int"} -) - -# Remove a parameter -refactor( - action="change_signature", - file="api.py", - line=20, - remove_parameter="deprecated_param" -) - -# Reorder parameters -refactor( - action="change_signature", - file="api.py", - line=20, - reorder_parameters=["user_id", "data", "options"] -) -``` - -### find_references - -Find all references to a symbol. - -```python -refactor(action="find_references", file="models.py", line=10, column=6) -``` - -**Response:** -```json -{ - "symbol": "UserModel", - "references": [ - {"file": "views.py", "line": 15, "column": 8}, - {"file": "tests/test_models.py", "line": 22, "column": 12} - ], - "count": 2 -} -``` - -### organize_imports - -Clean up and sort imports in a file. - -```python -refactor(action="organize_imports", file="main.py") -``` - -## Parallel Processing - -The refactor tool uses parallel processing for large-scale operations: - -```python -# Parallel is enabled by default -refactor( - action="rename_batch", - renames=[...], # Many renames - path="./large_codebase", - parallel=True # Uses all CPU cores -) - -# Disable for sequential processing -refactor( - action="rename_batch", - renames=[...], - parallel=False -) -``` - -## Preview Mode - -Preview changes before applying them: - -```python -# Preview only - don't apply changes -refactor( - action="rename", - file="main.py", - line=10, - column=5, - new_name="newName", - preview=True -) -``` - -**Response:** -```json -{ - "preview": true, - "changes": [ - {"file": "main.py", "line": 10, "old": "oldName", "new": "newName"}, - {"file": "utils.py", "line": 25, "old": "oldName", "new": "newName"} - ], - "files_affected": 2 -} -``` - -## Examples - -### Safe Symbol Rename - -```python -# 1. Find all usages first -refs = refactor(action="find_references", file="models.py", line=10, column=5) - -# 2. Preview the rename -preview = refactor( - action="rename", - file="models.py", - line=10, - column=5, - new_name="BetterName", - preview=True -) - -# 3. Apply the rename -refactor( - action="rename", - file="models.py", - line=10, - column=5, - new_name="BetterName" -) -``` - -### Large-Scale Refactoring - -```python -# Rename multiple patterns across codebase -refactor( - action="rename_batch", - renames=[ - {"old": "v1_api", "new": "legacy_api"}, - {"old": "v2_api", "new": "current_api"}, - {"old": "v3_api", "new": "api"} - ], - path="./src", - parallel=True -) -``` - -### Extract Common Code - -```python -# Extract duplicated logic into a function -refactor( - action="extract_function", - file="handlers.py", - start_line=45, - end_line=60, - new_name="validate_and_process" -) -``` - -## Best Practices - -1. **Always preview first** - Use `preview=True` for important refactors -2. **Run tests after** - Verify refactoring didn't break functionality -3. **Use batch operations** - `rename_batch` is faster than individual renames -4. **Commit before refactoring** - Have a clean git state to easily revert -5. **Combine with LSP** - Use `lsp` tool for initial symbol discovery diff --git a/docs/tools/shell.md b/docs/tools/shell.md deleted file mode 100644 index e7b0848ca..000000000 --- a/docs/tools/shell.md +++ /dev/null @@ -1,277 +0,0 @@ -# hanzo-tools-shell - -Unified command execution with DAG support, auto-backgrounding, and process management. The shell toolkit provides the `cmd`, `zsh`, `bash`, and `ps` tools. - -## Installation - -```bash -pip install hanzo-tools-shell -``` - -Or as part of the full toolkit: - -```bash -pip install hanzo-mcp[tools-all] -``` - -## Overview - -`hanzo-tools-shell` provides: - -- **cmd** - Unified command execution with DAG support -- **zsh** / **bash** / **shell** - Shell-specific execution -- **ps** - Process management (list, kill, logs) -- **npx** / **uvx** - Package runner tools -- **open** - Platform-aware file/URL opener - -## Key Features - -### Auto-Backgrounding - -Commands automatically background after 30 seconds (configurable): - -```python -# Long-running command auto-backgrounds -cmd("npm run build") # If takes >30s, continues in background - -# Check status later -ps() # List all background processes -ps(logs="cmd_abc123") # View output -ps(kill="cmd_abc123") # Stop process -``` - -### DAG Execution - -Execute commands with dependency graphs: - -```python -# Sequential (default) -cmd(["mkdir dist", "cp files dist/", "zip -r out.zip dist/"]) - -# Parallel -cmd(["npm install", "cargo build"], parallel=True) - -# Mixed DAG with dependencies -cmd([ - "mkdir -p dist", - {"parallel": ["cp a.txt dist/", "cp b.txt dist/"]}, - "zip -r out.zip dist/" -]) -``` - -## Tools Reference - -### cmd - -The primary command execution tool: - -```python -# Simple command -cmd("ls -la") - -# Sequential commands -cmd(["git status", "git diff", "git log -5"]) - -# Parallel execution -cmd(["npm install", "pip install -r requirements.txt"], parallel=True) - -# Mixed DAG -cmd([ - "mkdir -p dist", - {"parallel": ["build-frontend", "build-backend"]}, - "deploy" -]) - -# With options -cmd( - "npm test", - timeout=120, # Command timeout in seconds - cwd="/project", # Working directory - env={"NODE_ENV": "test"}, # Environment variables - quiet=True, # Suppress stdout - strict=True # Stop on first error -) -``` - -### zsh / bash / shell - -Shell-specific wrappers around `cmd`: - -```python -# Use specific shell -zsh("echo $SHELL") # Uses zsh -bash("echo $SHELL") # Uses bash -shell("echo $SHELL") # Auto-detects: zsh > bash > sh - -# Same options as cmd -zsh(["cmd1", "cmd2"], parallel=True, timeout=60) -``` - -### ps - -Process management for background commands: - -```python -# List all background processes -ps() - -# Get specific process info -ps(id="cmd_abc123") - -# View process output (last 100 lines by default) -ps(logs="cmd_abc123") -ps(logs="cmd_abc123", n=50) # Last 50 lines - -# Kill process -ps(kill="cmd_abc123") # SIGTERM (default) -ps(kill="cmd_abc123", sig=9) # SIGKILL -``` - -### npx / uvx - -Package runners with auto-backgrounding: - -```python -# Run npm packages -npx("create-react-app my-app") -npx("http-server -p 8080") # Auto-backgrounds if long-running - -# Run Python packages -uvx("ruff check .") -uvx("mkdocs serve") # Auto-backgrounds if long-running -``` - -### open - -Platform-aware opener for files and URLs: - -```python -# Open URLs -open("https://example.com") - -# Open files with default application -open("./document.pdf") -open("/path/to/image.png") -``` - -## Configuration - -### Environment Variables - -| Variable | Default | Description | -|----------|---------|-------------| -| `HANZO_AUTO_BACKGROUND_TIMEOUT` | `45` | Seconds before auto-backgrounding (0 to disable) | -| `HANZO_DEFAULT_SHELL` | `zsh` | Default shell for execution | - -### Timeout Configuration - -```python -# Disable auto-backgrounding for a command -cmd("long-running-command", timeout=0) - -# Set global timeout via environment -import os -os.environ["HANZO_AUTO_BACKGROUND_TIMEOUT"] = "120" # 2 minutes -``` - -## Examples - -### Build Pipeline - -```python -# Parallel build with sequential deploy -cmd([ - {"parallel": [ - "npm run build:frontend", - "npm run build:backend", - "npm run build:docs" - ]}, - "npm run test", - "npm run deploy" -]) -``` - -### Development Server - -```python -# Start dev server (auto-backgrounds) -cmd("npm run dev") - -# Later, check status -ps() -# Output: cmd_abc123 | running | npm run dev | started 5m ago - -# View logs -ps(logs="cmd_abc123") - -# Stop when done -ps(kill="cmd_abc123") -``` - -### Tool Invocation in DAG - -```python -# Mix shell commands with tool calls -cmd([ - "git pull origin main", - {"tool": "search", "input": {"pattern": "TODO"}}, - "npm test" -]) -``` - -### Parallel File Operations - -```python -# Process multiple files in parallel -cmd([ - ["process file1.txt", "process file2.txt", "process file3.txt"] -], parallel=True) -``` - -## Architecture - -### ShellExecutor - -Single async execution engine for all shell tools: - -``` -CmdTool (cmd) # Unified execution graph (DAG) - โ”œโ”€โ”€ ZshTool (zsh) # Thin shim: shell=zsh - โ”œโ”€โ”€ BashTool (bash) # Thin shim: shell=bash - โ””โ”€โ”€ ShellTool # Smart shim: zsh > bash > sh - -ShellExecutor (singleton) - โ””โ”€โ”€ ProcessManager (singleton) - โ””โ”€โ”€ ps tool (process listing/kill/logs) -``` - -### Process Lifecycle - -1. Command starts with `asyncio.create_subprocess_exec` -2. Wait for completion with configured timeout -3. If timeout reached: register with ProcessManager, continue in background -4. User can monitor with `ps --logs ` or stop with `ps --kill ` - -## Error Handling - -```python -# Strict mode: stop on first error -cmd(["cmd1", "cmd2", "cmd3"], strict=True) - -# Default: continue on errors, report all -cmd(["cmd1", "cmd2", "cmd3"]) # Returns combined output with errors - -# Check exit codes in response -result = cmd("might-fail") -if "exit code" in result.lower(): - # Handle error - ... -``` - -## Best Practices - -1. **Use DAG for dependencies**: Let the executor handle ordering -2. **Parallelize independent operations**: Use `parallel=True` for speed -3. **Set appropriate timeouts**: Avoid blocking on hung processes -4. **Use `ps` for long tasks**: Don't wait for builds, check later -5. **Prefer `cmd` over raw shell**: Better error handling and logging diff --git a/docs/tools/test.md b/docs/tools/test.md deleted file mode 100644 index 52bf5f0b0..000000000 --- a/docs/tools/test.md +++ /dev/null @@ -1,222 +0,0 @@ -# Test Tool - -Validation operations: check, build, test (HIP-0300 operator). - -## Installation - -```bash -pip install hanzo-tools-test -``` - -## Overview - -The `test` tool provides three distinct validation loops (Vim-inspired): - -| Action | Purpose | Equivalent | Effect | -|--------|---------|------------|--------| -| `check` | Fast incremental feedback | `:make` | NONDETERMINISTIC | -| `build` | Whole-project compilation | `:!make` | NONDETERMINISTIC | -| `test` | Runtime behavior validation | `:!make test` | NONDETERMINISTIC | -| `detect` | Auto-detect project tools | - | DETERMINISTIC | - -## Actions - -### check - -Fast, incremental linting and type checking. - -```python -test(action="check") -# Returns: { -# diagnostics: [ -# {file: "src/main.py", line: 42, severity: "error", message: "..."}, -# {file: "src/utils.py", line: 10, severity: "warning", message: "..."} -# ], -# pass: False, -# tool: "ruff", -# duration_ms: 234 -# } - -test(action="check", path="src/auth.py") -# Check specific file - -test(action="check", tool="mypy") -# Use specific tool -``` - -**Supported Check Tools:** -| Tool | Languages | Command | -|------|-----------|---------| -| `ruff` | Python | `ruff check .` | -| `mypy` | Python | `mypy .` | -| `pyright` | Python | `pyright .` | -| `eslint` | JavaScript/TypeScript | `eslint .` | -| `tsc` | TypeScript | `tsc --noEmit` | -| `clippy` | Rust | `cargo clippy` | -| `golangci-lint` | Go | `golangci-lint run` | - -### build - -Whole-project compilation and dependency resolution. - -```python -test(action="build") -# Returns: { -# success: True, -# artifacts: ["dist/main.js", "dist/main.js.map"], -# duration_ms: 5432, -# tool: "npm" -# } - -test(action="build", tool="cargo") -# Use specific build tool -``` - -**Supported Build Tools:** -| Tool | Languages | Command | -|------|-----------|---------| -| `pip` | Python | `pip install -e .` | -| `uv` | Python | `uv sync` | -| `npm` | Node.js | `npm run build` | -| `pnpm` | Node.js | `pnpm build` | -| `cargo` | Rust | `cargo build` | -| `go` | Go | `go build ./...` | -| `make` | Any | `make` | -| `cmake` | C/C++ | `cmake --build .` | - -### test - -Runtime behavior validation with test isolation. - -```python -test(action="test") -# Returns: { -# passed: 42, -# failed: 2, -# skipped: 3, -# total: 47, -# failures: [ -# {name: "test_auth_flow", file: "tests/test_auth.py", message: "..."} -# ], -# duration_ms: 8765, -# tool: "pytest" -# } - -test(action="test", selector="tests/test_auth.py") -# Run specific tests - -test(action="test", selector="test_login") -# Run tests matching pattern - -test(action="test", tool="jest", verbose=True) -# Use specific tool with options -``` - -**Supported Test Runners:** -| Tool | Languages | Command | -|------|-----------|---------| -| `pytest` | Python | `pytest -v` | -| `unittest` | Python | `python -m unittest` | -| `jest` | JavaScript/TypeScript | `jest` | -| `vitest` | JavaScript/TypeScript | `vitest run` | -| `mocha` | JavaScript | `mocha` | -| `cargo_test` | Rust | `cargo test` | -| `go_test` | Go | `go test ./...` | -| `make_test` | Any | `make test` | - -### detect - -Auto-detect project tools based on config files. - -```python -test(action="detect") -# Returns: { -# test_runner: {name: "pytest", cmd: ["pytest", "-v"], config: "pyproject.toml"}, -# build_tool: {name: "uv", cmd: ["uv", "sync"], config: "pyproject.toml"}, -# check_tool: {name: "ruff", cmd: ["ruff", "check", "."], config: "ruff.toml"}, -# detected_from: ["pyproject.toml", "ruff.toml"] -# } - -test(action="detect", path="/path/to/project") -# Detect in specific directory -``` - -**Detection Priority:** -1. Check for config files (pyproject.toml, package.json, Cargo.toml, etc.) -2. Look for lock files (uv.lock, package-lock.json, Cargo.lock) -3. Examine project structure (tests/, src/, etc.) - -## Three Validation Loops - -The three operations serve distinct purposes: - -### CHECK (Fast Feedback) -- **When:** During editing, before commits -- **Speed:** Milliseconds to seconds -- **Scope:** Single file or incremental -- **Output:** Diagnostics with line numbers -- **Goal:** Catch obvious errors immediately - -### BUILD (Compilation) -- **When:** Before running, in CI -- **Speed:** Seconds to minutes -- **Scope:** Entire project -- **Output:** Artifacts or errors -- **Goal:** Verify everything compiles - -### TEST (Validation) -- **When:** Before deploy, in CI -- **Speed:** Seconds to minutes -- **Scope:** Test suite -- **Output:** Pass/fail with details -- **Goal:** Verify behavior is correct - -## Example Workflow - -```python -# Quick check during development -check_result = test(action="check") -if not check_result["data"]["pass"]: - print("Fix these issues first:") - for diag in check_result["data"]["diagnostics"]: - print(f" {diag['file']}:{diag['line']}: {diag['message']}") - -# Full build before commit -build_result = test(action="build") -if not build_result["data"]["success"]: - print("Build failed!") - -# Run tests before push -test_result = test(action="test") -if test_result["data"]["failed"] > 0: - print(f"{test_result['data']['failed']} tests failed") - for failure in test_result["data"]["failures"]: - print(f" {failure['name']}: {failure['message']}") -``` - -## CI Integration - -```yaml -# GitHub Actions example -jobs: - validate: - steps: - - name: Check - run: | - python -c "from hanzo_tools.test import test_tool; print(test_tool.call(action='check'))" - - - name: Build - run: | - python -c "from hanzo_tools.test import test_tool; print(test_tool.call(action='build'))" - - - name: Test - run: | - python -c "from hanzo_tools.test import test_tool; print(test_tool.call(action='test'))" -``` - -## See Also - -- [HIP-0300](../hip/HIP-0300.md) - Unified Tools Architecture -- [Code Tool](code.md) - For code analysis before testing -- [VCS Tool](vcs.md) - For running tests on changes -- [Shell Tool](shell.md) - For custom test commands diff --git a/docs/tools/todo.md b/docs/tools/todo.md deleted file mode 100644 index abac6b2c7..000000000 --- a/docs/tools/todo.md +++ /dev/null @@ -1,189 +0,0 @@ -# hanzo-tools-todo - -Task management tools for tracking progress and organizing work. - -## Installation - -```bash -pip install hanzo-tools-todo -``` - -Or as part of the full toolkit: - -```bash -pip install hanzo-mcp[tools-all] -``` - -## Overview - -`hanzo-tools-todo` provides task management: - -- **todo** - Unified task management with list, add, update, remove, clear operations - -## Quick Start - -```python -# List all todos -todo() -todo(action="list") - -# Add a new todo -todo(action="add", content="Fix the authentication bug") - -# Update status -todo(action="update", id="abc123", status="in_progress") - -# Mark complete -todo(action="update", id="abc123", status="completed") - -# Remove a todo -todo(action="remove", id="abc123") - -# Clear all todos -todo(action="clear") -``` - -## Actions Reference - -### list - -List all todos, optionally filtered by status. - -```python -# List all -todo(action="list") - -# Filter by status -todo(action="list", filter="pending") -todo(action="list", filter="in_progress") -todo(action="list", filter="completed") -``` - -**Response:** -```json -{ - "todos": [ - {"id": "abc123", "content": "Fix bug", "status": "in_progress", "priority": "high"}, - {"id": "def456", "content": "Write tests", "status": "pending", "priority": "medium"} - ], - "count": 2 -} -``` - -### add - -Add a new todo item. - -```python -# Simple add -todo(action="add", content="Implement feature X") - -# With priority -todo(action="add", content="Critical fix", priority="high") - -# With status -todo(action="add", content="Already started", status="in_progress") -``` - -**Parameters:** -- `content` (required): Todo description -- `status`: Initial status - "pending" (default), "in_progress", "completed" -- `priority`: Priority level - "low", "medium" (default), "high" - -### update - -Update an existing todo. - -```python -# Update status -todo(action="update", id="abc123", status="completed") - -# Update content -todo(action="update", id="abc123", content="Updated description") - -# Update priority -todo(action="update", id="abc123", priority="high") -``` - -**Parameters:** -- `id` (required): Todo ID -- `content`: New content -- `status`: New status -- `priority`: New priority - -### remove - -Remove a todo by ID. - -```python -todo(action="remove", id="abc123") -``` - -### clear - -Clear all todos. - -```python -todo(action="clear") -``` - -## Status Workflow - -``` -pending โ†’ in_progress โ†’ completed - โ†‘_________| (can go back if needed) -``` - -**Statuses:** -- `pending` - Not yet started -- `in_progress` - Currently being worked on -- `completed` - Finished - -## Priority Levels - -- `high` - Urgent, do first -- `medium` - Normal priority (default) -- `low` - Can wait - -## Storage - -Todos are stored per-session in memory. For persistent todos, use the memory tools to save to disk. - -## Examples - -### Project Workflow - -```python -# Add tasks for a feature -todo(action="add", content="Design API schema", priority="high") -todo(action="add", content="Implement endpoints", priority="high") -todo(action="add", content="Write tests", priority="medium") -todo(action="add", content="Update documentation", priority="low") - -# Start working -todo(action="update", id="", status="in_progress") - -# Complete and move on -todo(action="update", id="", status="completed") -todo(action="update", id="", status="in_progress") -``` - -### Tracking Progress - -```python -# Check what's in progress -todo(action="list", filter="in_progress") - -# Check what's pending -todo(action="list", filter="pending") - -# Check completed -todo(action="list", filter="completed") -``` - -## Best Practices - -1. **Use meaningful descriptions** - Clear content helps tracking -2. **Set appropriate priorities** - High for blockers, medium for features -3. **Update status promptly** - Keep todos current -4. **Clear completed items** - Periodically clean up finished tasks diff --git a/docs/tools/vcs.md b/docs/tools/vcs.md deleted file mode 100644 index bb846e3ab..000000000 --- a/docs/tools/vcs.md +++ /dev/null @@ -1,256 +0,0 @@ -# VCS Tool - -Version control operations: status, diff, commit, log (HIP-0300 operator). - -## Installation - -```bash -pip install hanzo-tools-vcs -``` - -## Overview - -The `vcs` tool handles all version control operations: - -| Action | Signature | Effect | -|--------|-----------|--------| -| `status` | `() โ†’ {staged, unstaged, untracked}` | DETERMINISTIC | -| `diff` | `(ref1?, ref2?, paths?) โ†’ Diff` | DETERMINISTIC | -| `commit` | `(message, files?) โ†’ {sha, message}` | DETERMINISTIC | -| `log` | `(n?, since?, path?) โ†’ [Commit]` | DETERMINISTIC | -| `branch` | `(name?, action?) โ†’ {current, branches}` | DETERMINISTIC | -| `stash` | `(action, message?) โ†’ {ok}` | DETERMINISTIC | -| `apply` | `(patch) โ†’ {ok, files_changed}` | DETERMINISTIC | -| `checkout` | `(ref, paths?) โ†’ {ok}` | DETERMINISTIC | - -## Actions - -### status - -Get working tree status. - -```python -vcs(action="status") -# Returns: { -# staged: [ -# {path: "src/main.py", status: "modified"}, -# {path: "src/utils.py", status: "added"} -# ], -# unstaged: [ -# {path: "src/config.py", status: "modified"} -# ], -# untracked: ["temp.txt", "notes.md"], -# branch: "feature/auth", -# ahead: 2, -# behind: 0 -# } -``` - -### diff - -Show differences between commits, branches, or working tree. - -```python -# Working tree diff (unstaged) -vcs(action="diff") -# Returns: { -# diff: "diff --git a/src/main.py b/src/main.py\n...", -# files: ["src/main.py"], -# additions: 15, -# deletions: 3 -# } - -# Staged diff -vcs(action="diff", staged=True) - -# Between commits -vcs(action="diff", ref1="HEAD~3", ref2="HEAD") - -# Specific files -vcs(action="diff", paths=["src/auth.py", "src/users.py"]) - -# Between branches -vcs(action="diff", ref1="main", ref2="feature/auth") -``` - -### commit - -Create a commit. - -```python -vcs(action="commit", message="Add user authentication") -# Returns: { -# sha: "abc123def456", -# message: "Add user authentication", -# files_changed: 3 -# } - -# Commit specific files -vcs(action="commit", message="Fix bug in auth", files=["src/auth.py"]) - -# Amend last commit -vcs(action="commit", message="Updated message", amend=True) -``` - -### log - -View commit history. - -```python -vcs(action="log") -# Returns: { -# commits: [ -# {sha: "abc123", message: "Add auth", author: "...", date: "..."}, -# {sha: "def456", message: "Fix bug", author: "...", date: "..."} -# ] -# } - -# Limit results -vcs(action="log", n=10) - -# Filter by date -vcs(action="log", since="2024-01-01", until="2024-01-31") - -# Filter by path -vcs(action="log", path="src/auth.py") - -# Show full diff with each commit -vcs(action="log", n=5, show_diff=True) -``` - -### branch - -Branch operations. - -```python -# List branches -vcs(action="branch") -# Returns: { -# current: "feature/auth", -# local: ["main", "feature/auth", "bugfix/login"], -# remote: ["origin/main", "origin/develop"] -# } - -# Create branch -vcs(action="branch", name="feature/new-feature", create=True) - -# Delete branch -vcs(action="branch", name="old-branch", delete=True) -``` - -### stash - -Stash operations. - -```python -# Save stash -vcs(action="stash", push=True, message="WIP: auth changes") -# Returns: {ok: True, stash: "stash@{0}"} - -# List stashes -vcs(action="stash", list=True) -# Returns: {stashes: [{ref: "stash@{0}", message: "..."}]} - -# Apply stash -vcs(action="stash", apply=True) - -# Pop stash (apply and remove) -vcs(action="stash", pop=True) -``` - -### apply - -Apply a patch (unified diff format). - -```python -patch = """ -diff --git a/src/main.py b/src/main.py ---- a/src/main.py -+++ b/src/main.py -@@ -1,3 +1,4 @@ -+# New header comment - def main(): - pass -""" - -vcs(action="apply", patch=patch) -# Returns: {ok: True, files_changed: ["src/main.py"]} -``` - -### checkout - -Switch branches or restore files. - -```python -# Switch branch -vcs(action="checkout", ref="main") -# Returns: {ok: True, branch: "main"} - -# Create and switch -vcs(action="checkout", ref="feature/new", create=True) - -# Restore specific files -vcs(action="checkout", ref="HEAD", paths=["src/main.py"]) -``` - -## Usage Examples - -### Safe Commit Workflow - -```python -# 1. Check status -status = vcs(action="status") - -# 2. Review diff -if status["data"]["unstaged"]: - diff = vcs(action="diff") - print(diff["data"]["diff"]) - -# 3. Stage and commit -vcs(action="commit", message="Implement user authentication", - files=["src/auth.py", "tests/test_auth.py"]) -``` - -### Code Review Workflow - -```python -# 1. Get diff between branches -diff = vcs(action="diff", ref1="main", ref2="feature/auth") - -# 2. Summarize with code tool -from hanzo_tools.code import code_tool -summary = code_tool(action="summarize", diff=diff["data"]["diff"]) - -print(f"Changes: +{diff['data']['additions']} -{diff['data']['deletions']}") -print(f"Summary: {summary['data']['summary']}") -``` - -### History Analysis - -```python -# Get recent changes to a file -commits = vcs(action="log", path="src/api.py", n=10, show_diff=True) - -for commit in commits["data"]["commits"]: - print(f"{commit['sha'][:7]} {commit['message']}") -``` - -## Integration with fs.patch - -VCS diffs integrate with `fs.patch` for applying changes: - -```python -# Generate diff -diff = vcs(action="diff", ref1="main", ref2="feature/fix") - -# Apply to another location -fs(action="patch", path="/other/project/src/main.py", - patch=diff["data"]["diff"]) -``` - -## See Also - -- [HIP-0300](../hip/HIP-0300.md) - Unified Tools Architecture -- [Filesystem Tool](fs.md) - For `fs.patch` to apply diffs -- [Code Tool](code.md) - For `code.summarize` on diffs -- [Test Tool](test.md) - For validation after commits diff --git a/docs/tools/vector.md b/docs/tools/vector.md deleted file mode 100644 index a27be23df..000000000 --- a/docs/tools/vector.md +++ /dev/null @@ -1,256 +0,0 @@ -# Vector Tools - -The vector tools (`hanzo-tools-vector`) provide semantic search capabilities using vector embeddings and the Infinity embedded database. - -## Overview - -These tools enable indexing documents and searching by semantic similarity rather than keyword matching. Perfect for finding conceptually related content. - -## Installation - -```bash -# Basic install -pip install hanzo-tools-vector - -# Full install with all dependencies -pip install hanzo-tools-vector[full] -``` - -## index - Project Indexing - -Index project files for search: - -```python -# Index current project -index(path=".") - -# Index specific directory -index(path="/project/src") - -# Index with file filter -index(path=".", include="*.py,*.md") - -# Re-index (force update) -index(path=".", force=True) -``` - -### Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `path` | str | `.` | Directory to index | -| `include` | str | - | File patterns to include | -| `exclude` | str | - | File patterns to exclude | -| `force` | bool | `False` | Force re-indexing | - -## vector_index - Document Indexing - -Add documents to the vector index: - -```python -# Index a single document -vector_index( - content="This is the document content...", - file_path="/docs/guide.md" -) - -# Index with metadata -vector_index( - content="API documentation...", - file_path="/docs/api.md", - metadata={"category": "api", "version": "2.0"} -) - -# Index multiple documents -vector_index( - documents=[ - {"content": "Doc 1...", "file_path": "/a.md"}, - {"content": "Doc 2...", "file_path": "/b.md"} - ] -) -``` - -### Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `content` | str | - | Document content | -| `file_path` | str | - | Source file path | -| `metadata` | dict | - | Additional metadata | -| `documents` | list | - | Batch of documents | - -## vector_search - Semantic Search - -Search indexed documents by meaning: - -```python -# Basic semantic search -vector_search(query="How do I authenticate users?") - -# Search with limit -vector_search(query="error handling patterns", limit=5) - -# Search with score threshold -vector_search(query="database optimization", score_threshold=0.7) - -# Search specific project -vector_search(query="API endpoints", search_scope="my-project") - -# Filter by file pattern -vector_search(query="testing patterns", file_filter="test_*.py") - -# Search all projects -vector_search(query="configuration", search_scope="all") -``` - -### Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `query` | str | required | Search query | -| `limit` | int | `10` | Max results to return | -| `score_threshold` | float | `0.0` | Min similarity score (0-1) | -| `include_content` | bool | `True` | Include document content | -| `file_filter` | str | - | Filter by file path pattern | -| `project_filter` | list | - | Filter by project names | -| `search_scope` | str | `all` | `all`, `global`, `current`, or project name | - -### Search Scopes - -| Scope | Description | -|-------|-------------| -| `all` | Search all indexed projects | -| `global` | Search only global index | -| `current` | Search current project (auto-detected) | -| `` | Search specific project by name | - -### Output Format - -``` -Found 3 results for query: 'authentication patterns' - -Result 1 (Score: 87.3%) - Project: my-api - src/auth/handler.py [Chunk 2] ------------------------------------------------------------------- -Metadata: {"category": "auth"} -Content: -def authenticate_user(token: str) -> User: - """Authenticate user from JWT token...""" - ... - -Result 2 (Score: 82.1%) - Project: my-api - docs/auth.md [Chunk 0] ------------------------------------------------------------------- -Content: -# Authentication Guide - -This guide explains how to authenticate users... -``` - -## How It Works - -### Embedding Generation - -Documents are converted to vector embeddings that capture semantic meaning: - -1. **Chunking**: Large documents split into smaller chunks -2. **Embedding**: Each chunk converted to vector representation -3. **Storage**: Vectors stored in Infinity database -4. **Indexing**: HNSW index for fast similarity search - -### Similarity Search - -Queries are embedded and compared against document vectors: - -1. **Query Embedding**: Convert query to vector -2. **Nearest Neighbors**: Find most similar document vectors -3. **Scoring**: Calculate similarity scores (0-1) -4. **Ranking**: Return top results by score - -## Project Detection - -Projects are automatically detected by looking for `LLM.md` files: - -``` -/home/user/projects/ -โ”œโ”€โ”€ project-a/ -โ”‚ โ”œโ”€โ”€ LLM.md <- Project root detected -โ”‚ โ””โ”€โ”€ src/ -โ”œโ”€โ”€ project-b/ -โ”‚ โ”œโ”€โ”€ LLM.md <- Project root detected -โ”‚ โ””โ”€โ”€ lib/ -``` - -Each project gets its own isolated vector index. - -## Best Practices - -### 1. Index Before Searching - -```python -# Initial indexing -index(path="/project") - -# Then search -vector_search(query="your query") -``` - -### 2. Use Appropriate Score Thresholds - -```python -# High precision (fewer results, more relevant) -vector_search(query="...", score_threshold=0.8) - -# High recall (more results, some noise) -vector_search(query="...", score_threshold=0.5) -``` - -### 3. Combine with Keyword Search - -```python -# Semantic search for concepts -vector_search(query="error handling best practices") - -# Keyword search for exact matches -grep(pattern="raise ValueError") -``` - -### 4. Filter by File Type - -```python -# Search only documentation -vector_search(query="setup instructions", file_filter="*.md") - -# Search only code -vector_search(query="authentication", file_filter="*.py") -``` - -### 5. Use Project Scope for Speed - -```python -# Faster: search specific project -vector_search(query="...", search_scope="my-project") - -# Slower: search all projects -vector_search(query="...", search_scope="all") -``` - -## Comparison: Vector vs Keyword Search - -| Feature | vector_search | grep | -|---------|---------------|------| -| Match type | Semantic similarity | Exact text pattern | -| Finds synonyms | Yes | No | -| Requires indexing | Yes | No | -| Speed (large corpus) | Fast (indexed) | Slower | -| Best for | Concepts, questions | Exact code, identifiers | - -Example: - -```python -# Semantic search finds conceptually related content -vector_search(query="how to handle errors") -# Finds: exception handling, try/catch blocks, error recovery - -# Keyword search finds exact patterns -grep(pattern="except Exception") -# Finds: only lines with "except Exception" -``` diff --git a/docs/tsconfig.json b/docs/tsconfig.json deleted file mode 100644 index 9e9bbf7b1..000000000 --- a/docs/tsconfig.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "compilerOptions": { - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "react-jsx", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": [ - "./*" - ] - }, - "target": "ES2017" - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts", - ".next/dev/types/**/*.ts" - ], - "exclude": [ - "node_modules" - ] -} diff --git a/examples/parallel_ai_doc_editing.py b/examples/parallel_ai_doc_editing.py deleted file mode 100644 index 8981e7399..000000000 --- a/examples/parallel_ai_doc_editing.py +++ /dev/null @@ -1,424 +0,0 @@ -#!/usr/bin/env python3 -""" -Parallel AI Documentation Editing using Hanzo-MCP Batch Tool - -This demonstrates how to edit multiple AI documentation files in parallel -using the batch tool to delegate to different CLI agents (claude, codex, gemini, grok). -""" - -import json -from typing import Any, Dict, List - -# Example of how to use hanzo-mcp batch tool for parallel edits -# In practice, this would be called through the MCP interface - - -def create_parallel_edit_batch(): - """ - Create batch invocations for parallel document editing. - Each agent works on a different file simultaneously. - """ - - batch_invocations = [ - { - "tool_name": "claude", - "input": { - "prompt": """ - Read and enhance CLAUDE.md with: - 1. Add section on Claude's computer use capabilities - 2. Include Claude's vision capabilities for code screenshots - 3. Add patterns for using Claude's analysis tools - 4. Include Claude 3.5 Sonnet's latest improvements - 5. Add section on prompt caching for cost optimization - - Return the complete enhanced content maintaining all existing sections. - Focus on Claude-specific strengths and unique features. - """ - }, - }, - { - "tool_name": "codex", - "input": { - "prompt": """ - Read and enhance LLM.md with: - 1. Add section on OpenAI's latest GPT-4 Turbo capabilities - 2. Include advanced function calling patterns - 3. Add OpenAI's Assistants API integration - 4. Include batch API usage for cost optimization - 5. Add fine-tuning patterns for custom models - - Return the complete enhanced content maintaining all existing sections. - Focus on OpenAI-specific optimizations and features. - """ - }, - }, - { - "tool_name": "gemini", - "input": { - "prompt": """ - Read and enhance GEMINI.md with: - 1. Add section on Gemini's latest 1.5 Flash improvements - 2. Include Gemini Code capabilities - 3. Add patterns for Gemini's grounding with Google Search - 4. Include Gemini's extensions and plugins - 5. Add section on Gemini Advanced features - - Return the complete enhanced content maintaining all existing sections. - Focus on Google-specific integrations and multimodal strengths. - """ - }, - }, - { - "tool_name": "grok", - "input": { - "prompt": """ - Create a new GROK.md file with: - 1. Grok model family overview (Grok-1, Grok-2) - 2. Real-time information access patterns - 3. X (Twitter) integration capabilities - 4. Code generation optimizations - 5. Humor and personality in responses - 6. Integration with xAI ecosystem - 7. Best practices for Grok usage - - Follow the same structure as other AI doc files. - Include code examples and practical patterns. - """ - }, - }, - ] - - return batch_invocations - - -def create_agent_editing_batch(): - """ - Create batch for editing AGENTS.md using multiple agents. - Each agent enhances a different section. - """ - - batch_invocations = [ - { - "tool_name": "claude", - "input": { - "prompt": """ - Enhance the 'Agent Communication Protocols' section in AGENTS.md: - - Add WebSocket-based real-time communication - - Include event-driven messaging patterns - - Add distributed consensus protocols - - Include agent negotiation strategies - Write only the enhanced section content. - """ - }, - }, - { - "tool_name": "codex", - "input": { - "prompt": """ - Enhance the 'Git Worktree Management' section in AGENTS.md: - - Add automated merge conflict resolution - - Include CI/CD integration patterns - - Add branch protection strategies - - Include automated PR generation - Write only the enhanced section content. - """ - }, - }, - { - "tool_name": "gemini", - "input": { - "prompt": """ - Enhance the 'Swarm Coordination Patterns' section in AGENTS.md: - - Add emergent behavior patterns - - Include load balancing strategies - - Add fault tolerance mechanisms - - Include performance optimization - Write only the enhanced section content. - """ - }, - }, - ] - - return batch_invocations - - -def create_review_and_merge_batch(edited_content: Dict[str, str]): - """ - Create batch for reviewing and merging edits using consensus. - Multiple agents review each other's work. - """ - - batch_invocations = [ - { - "tool_name": "claude", - "input": { - "prompt": f""" - Review the Codex edits to LLM.md: - {edited_content.get("llm_edits", "")} - - Check for: - 1. Technical accuracy - 2. Consistency with existing content - 3. Code example correctness - 4. Best practices alignment - - Provide feedback and improved version if needed. - """ - }, - }, - { - "tool_name": "codex", - "input": { - "prompt": f""" - Review the Claude edits to CLAUDE.md: - {edited_content.get("claude_edits", "")} - - Check for: - 1. Implementation feasibility - 2. Performance implications - 3. Security considerations - 4. Integration complexity - - Provide feedback and improved version if needed. - """ - }, - }, - { - "tool_name": "gemini", - "input": { - "prompt": f""" - Review all edits and create integration tests: - - Files edited: - - LLM.md (by Codex) - - CLAUDE.md (by Claude) - - GEMINI.md (by Gemini) - - AGENTS.md (by multiple) - - Generate: - 1. Cross-reference validation - 2. Consistency checks - 3. Integration test cases - 4. Documentation validation script - """ - }, - }, - ] - - return batch_invocations - - -class ParallelDocumentEditor: - """ - Orchestrates parallel editing of documentation using multiple AI agents. - """ - - def __init__(self): - self.agents = ["claude", "codex", "gemini", "grok"] - self.files = ["LLM.md", "AGENTS.md", "GEMINI.md", "CLAUDE.md"] - - async def execute_parallel_edits(self): - """ - Execute parallel edits using batch tool. - """ - - # Phase 1: Initial parallel edits - print("๐Ÿš€ Phase 1: Initiating parallel edits...") - initial_batch = create_parallel_edit_batch() - - # This would be executed as: - # results = await batch( - # description="Edit AI documentation files", - # invocations=initial_batch - # ) - - # Simulated results for demonstration - results_phase1 = { - "claude": "Enhanced CLAUDE.md content...", - "codex": "Enhanced LLM.md content...", - "gemini": "Enhanced GEMINI.md content...", - "grok": "New GROK.md content...", - } - - print("โœ… Phase 1 complete: All files edited in parallel") - - # Phase 2: Parallel section enhancements - print("\n๐Ÿš€ Phase 2: Enhancing specific sections...") - section_batch = create_agent_editing_batch() - - # results = await batch( - # description="Enhance AGENTS.md sections", - # invocations=section_batch - # ) - - results_phase2 = { - "communication": "Enhanced communication section...", - "worktree": "Enhanced worktree section...", - "swarm": "Enhanced swarm section...", - } - - print("โœ… Phase 2 complete: Sections enhanced") - - # Phase 3: Cross-review and validation - print("\n๐Ÿš€ Phase 3: Cross-reviewing edits...") - review_batch = create_review_and_merge_batch(results_phase1) - - # results = await batch( - # description="Review and validate edits", - # invocations=review_batch - # ) - - print("โœ… Phase 3 complete: All edits reviewed and validated") - - return {"phase1": results_phase1, "phase2": results_phase2, "status": "completed"} - - async def apply_edits_in_parallel(self, edits: Dict[str, str]): - """ - Apply validated edits to files in parallel. - """ - - write_batch = [ - {"tool_name": "write", "input": {"file_path": f"/Users/z/work/hanzo/python-sdk/{file}", "content": content}} - for file, content in edits.items() - ] - - # Execute all writes in parallel - # results = await batch( - # description="Write updated documentation", - # invocations=write_batch - # ) - - print("โœ… All files updated in parallel") - - -# Practical example of batch execution in MCP context -BATCH_EXAMPLE = """ -# In Claude Desktop or hanzo-mcp CLI, you would execute: - -batch --description "Edit AI docs in parallel" --invocations '[ - { - "tool_name": "claude", - "input": { - "prompt": "Enhance CLAUDE.md with latest features" - } - }, - { - "tool_name": "codex", - "input": { - "prompt": "Enhance LLM.md with OpenAI patterns" - } - }, - { - "tool_name": "gemini", - "input": { - "prompt": "Enhance GEMINI.md with multimodal examples" - } - }, - { - "tool_name": "grok", - "input": { - "prompt": "Create GROK.md documentation" - } - } -]' -""" - - -def demonstrate_batch_patterns(): - """ - Demonstrate various batch execution patterns. - """ - - print("=" * 60) - print("PARALLEL AI DOCUMENTATION EDITING PATTERNS") - print("=" * 60) - - # Pattern 1: Parallel file editing - print("\n๐Ÿ“ Pattern 1: Parallel File Editing") - print("-" * 40) - - parallel_edit = { - "description": "Edit 4 files simultaneously", - "invocations": [ - {"tool": "claude", "target": "CLAUDE.md"}, - {"tool": "codex", "target": "LLM.md"}, - {"tool": "gemini", "target": "GEMINI.md"}, - {"tool": "grok", "target": "AGENTS.md"}, - ], - } - - print(json.dumps(parallel_edit, indent=2)) - - # Pattern 2: Sequential with parallel stages - print("\n๐Ÿ“ Pattern 2: Sequential Stages with Parallel Tasks") - print("-" * 40) - - staged_execution = { - "stage1": { - "description": "Analyze all files in parallel", - "parallel": True, - "tasks": ["analyze_claude.md", "analyze_llm.md", "analyze_agents.md"], - }, - "stage2": { - "description": "Edit based on analysis", - "parallel": True, - "tasks": ["edit_claude.md", "edit_llm.md", "edit_agents.md"], - }, - "stage3": {"description": "Review all edits", "parallel": False, "tasks": ["consensus_review"]}, - } - - print(json.dumps(staged_execution, indent=2)) - - # Pattern 3: Divide and conquer - print("\n๐Ÿ“ Pattern 3: Divide and Conquer") - print("-" * 40) - - divide_conquer = { - "description": "Each agent handles specific sections", - "file": "AGENTS.md", - "parallel_sections": [ - {"agent": "claude", "section": "Communication Protocols", "expertise": "System design"}, - {"agent": "codex", "section": "Code Generation", "expertise": "Implementation"}, - {"agent": "gemini", "section": "Testing Strategies", "expertise": "Quality assurance"}, - ], - } - - print(json.dumps(divide_conquer, indent=2)) - - # Pattern 4: Consensus editing - print("\n๐Ÿ“ Pattern 4: Consensus-Based Editing") - print("-" * 40) - - consensus_edit = { - "description": "Multiple agents edit same content", - "target": "architecture.md", - "agents": ["claude", "codex", "gemini"], - "strategy": "Each agent provides version, then consensus", - "final_review": "grok", - } - - print(json.dumps(consensus_edit, indent=2)) - - -if __name__ == "__main__": - import asyncio - - # Demonstrate patterns - demonstrate_batch_patterns() - - # Example execution (would be async in practice) - print("\n" + "=" * 60) - print("EXAMPLE PARALLEL EXECUTION") - print("=" * 60) - - editor = ParallelDocumentEditor() - - # In practice, this would be: - # asyncio.run(editor.execute_parallel_edits()) - - print("\nโœจ Parallel editing demonstration complete!") - - print("\n" + "=" * 60) - print("BATCH TOOL USAGE IN MCP") - print("=" * 60) - print(BATCH_EXAMPLE) diff --git a/examples/self_learning_agent.py b/examples/self_learning_agent.py deleted file mode 100644 index ac1c894a7..000000000 --- a/examples/self_learning_agent.py +++ /dev/null @@ -1,38 +0,0 @@ -from agents import Agent, Runner, ReflexionEngine -from agents.reflexion import Rule -from hanzo_memory.client import SQLiteMemoryClient - - -async def main(): - # 1. Initialize Memory Client (SQLite) - # Ensure the DB exists or is created - db_client = SQLiteMemoryClient(user_id="test_user") - - # 2. Initialize Reflexion Engine - reflexion = ReflexionEngine(memory_client=db_client) - - # 3. Add some initial rules (simulating past learning) - await reflexion.add_rule("Always double check file paths before writing.", context="coding") - - # 4. Create an Agent with Reflexion - agent = Agent( - name="SelfLearningBot", - instructions="You are a helpful assistant that learns from mistakes.", - reflexion=reflexion, - ) - - # 5. Simulate a run (in a real scenario, the runner would use these) - print(f"Agent {agent.name} initialized.") - rules = await agent.reflexion.load_rules(context="coding") - print(f"Loaded {len(rules)} rules for context 'coding':") - for r in rules: - print(f" - {r.content}") - - # 6. Simulate a learning event - print("\nSimulating reflection...") - new_rule = await agent.reflexion.add_rule("Use pathlib for path manipulations.", context="coding") - print(f"Added new rule: {new_rule.content}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/examples/unified_ai_example.py b/examples/unified_ai_example.py deleted file mode 100644 index 0f28ce330..000000000 --- a/examples/unified_ai_example.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python3 -"""Example of using the unified Hanzo AI SDK. - -This demonstrates: -1. Local AI cluster with exo -2. Agent networks -3. MCP tools -4. Cloud AI fallback -""" - -import asyncio - -from hanzoai import mcp, agents, cluster, completion - - -async def main(): - """Run the example.""" - - # 1. Start a local AI cluster - print("Starting local AI cluster...") - local_cluster = await cluster.start_local_cluster( - name="my-cluster", - model_path="~/.cache/huggingface/hub", # Use local models - ) - - # 2. Create agents - print("\nCreating AI agents...") - - # Local agent using the cluster - local_agent = agents.create_agent( - name="local-helper", - model="llama-3.2-3b", # Uses local cluster - base_url=local_cluster.get_api_endpoint(), - ) - - # Cloud agent as fallback - cloud_agent = agents.create_agent(name="cloud-helper", model="anthropic/claude-3-5-sonnet-20241022") - - # 3. Create an agent network - print("\nCreating agent network...") - network = agents.create_network( - agents=[local_agent, cloud_agent], - router=agents.state_based_router(), # Smart routing - ) - - # 4. Create MCP server with tools - print("\nStarting MCP server...") - mcp_server = mcp.create_mcp_server(name="hanzo-unified", allowed_paths=[".", "/tmp"], enable_agent_tool=True) - - # 5. Example: Use local AI for simple tasks - print("\n--- Local AI Example ---") - local_result = await local_cluster.inference(prompt="Write a haiku about distributed AI", max_tokens=50) - print(f"Local AI: {local_result}") - - # 6. Example: Use cloud AI for complex tasks - print("\n--- Cloud AI Example ---") - cloud_result = completion( - model="anthropic/claude-3-5-sonnet-20241022", - messages=[{"role": "user", "content": "Explain the benefits of local AI clusters"}], - ) - print(f"Cloud AI: {cloud_result}") - - # 7. Join mining network (optional) - if input("\nJoin mining network? (y/n): ").lower() == "y": - wallet = input("Enter wallet address: ") - miner = await cluster.join_mining_network(wallet_address=wallet) - print(f"Mining stats: {miner.get_stats()}") - - # Cleanup - print("\nShutting down...") - await local_cluster.stop() - - -if __name__ == "__main__": - print("=== Hanzo AI Unified SDK Example ===") - print("Local, Private, Free AI Infrastructure") - print("=====================================\n") - - asyncio.run(main()) diff --git a/examples/using_grok.py b/examples/using_grok.py deleted file mode 100644 index 2b71efa85..000000000 --- a/examples/using_grok.py +++ /dev/null @@ -1,190 +0,0 @@ -#!/usr/bin/env python -"""Example of using xAI Grok with Hanzo Python SDK. - -This example demonstrates: -1. Direct API usage with Grok -2. Streaming responses -3. Batch operations with Grok -4. Consensus with multiple models including Grok -""" - -import os -import sys -import asyncio - -# Add paths for development (remove in production) -sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "pkg", "hanzoai")) -sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "pkg", "hanzo", "src")) - -try: - from hanzoai import Hanzo, AsyncHanzo -except ImportError: - print("Error: hanzoai not installed") - print("Install with: pip install hanzoai") - sys.exit(1) - -# Note: Requires XAI_API_KEY environment variable to be set -# Get your API key from https://x.ai/api - - -def basic_grok_usage(): - """Basic usage of Grok through Hanzo SDK.""" - client = Hanzo() # Uses HANZO_API_KEY env var - - # Simple completion - response = client.chat.completions.create( - model="grok-4", # or "grok" or "xai-grok" - messages=[ - {"role": "system", "content": "You have real-time knowledge."}, - {"role": "user", "content": "What are the latest AI developments this week?"}, - ], - temperature=0.7, - max_tokens=500, - ) - - print("Grok Response:") - print(response.choices[0].message.content) - print("\n" + "=" * 50 + "\n") - - -def streaming_grok(): - """Stream responses from Grok for real-time output.""" - client = Hanzo() - - print("Streaming from Grok:") - stream = client.chat.completions.create( - model="grok-4", messages=[{"role": "user", "content": "Explain quantum computing in simple terms"}], stream=True - ) - - for chunk in stream: - if chunk.choices[0].delta.content: - print(chunk.choices[0].delta.content, end="", flush=True) - - print("\n\n" + "=" * 50 + "\n") - - -async def async_grok_usage(): - """Async usage of Grok for concurrent operations.""" - client = AsyncHanzo() - - # Concurrent requests to different models - tasks = [ - client.chat.completions.create( - model="grok-4", messages=[{"role": "user", "content": "What's happening in tech today?"}] - ), - client.chat.completions.create( - model="claude-3-5-sonnet", messages=[{"role": "user", "content": "Explain machine learning"}] - ), - client.chat.completions.create(model="gpt-4", messages=[{"role": "user", "content": "Write a haiku about AI"}]), - ] - - print("Concurrent requests to Grok, Claude, and GPT-4:") - responses = await asyncio.gather(*tasks) - - for i, (model, response) in enumerate(zip(["Grok", "Claude", "GPT-4"], responses, strict=False)): - print(f"\n{model} Response:") - print(response.choices[0].message.content[:200] + "...") - - print("\n" + "=" * 50 + "\n") - - -def batch_operations_with_grok(): - """Demonstrate batch operations using Grok.""" - from hanzo.batch_orchestrator import BatchConfig, BatchOrchestrator - - print("Batch Operations with Grok:") - - # Parse different batch configurations - configs = [ - "batch:5 agent:grok analyze code quality", - "consensus:3 agent:grok,claude,gemini review architecture", - "critic:2 agent:grok,gpt-4 chain:true security audit", - ] - - for cmd in configs: - config = BatchConfig.from_command(cmd) - print(f"\nCommand: {cmd}") - print(f" - Batch size: {config.batch_size}") - print(f" - Agent model: {config.agent_model}") - if config.consensus_mode: - print(f" - Consensus models: {config.consensus_models}") - if config.critic_mode: - print(f" - Critic chain: {config.critic_chain}") - - print("\n" + "=" * 50 + "\n") - - -def grok_with_tools(): - """Use Grok with MCP tools.""" - try: - from hanzo_mcp.tools.agent.cli_tools import GrokCLITool - - print("Grok CLI Tool Configuration:") - grok_tool = GrokCLITool() - - print(f"Tool name: {grok_tool.name}") - print(f"Description: {grok_tool.description}") - print(f"Default model: {grok_tool.default_model}") - print(f"API key env: {grok_tool.api_key_env}") - - # Get auth environment - env = grok_tool.get_auth_env() - if "XAI_API_KEY" in env: - print("โœ… XAI_API_KEY is configured") - else: - print("โš ๏ธ XAI_API_KEY not found in environment") - - except ImportError: - print("hanzo-mcp not installed. Install with: pip install hanzo-mcp") - - print("\n" + "=" * 50 + "\n") - - -def main(): - """Run all Grok examples.""" - print("=" * 50) - print("xAI Grok Examples with Hanzo SDK") - print("=" * 50 + "\n") - - # Check for API key - if not os.environ.get("XAI_API_KEY"): - print("โš ๏ธ Warning: XAI_API_KEY not set") - print("Get your API key from https://x.ai/api") - print("Set it with: export XAI_API_KEY='your-key-here'") - print("\nRunning examples that don't require actual API calls...\n") - - # Run non-API examples - batch_operations_with_grok() - grok_with_tools() - return - - # Run all examples - try: - # Basic usage - basic_grok_usage() - - # Streaming - streaming_grok() - - # Async operations - asyncio.run(async_grok_usage()) - - # Batch operations - batch_operations_with_grok() - - # MCP tools - grok_with_tools() - - print("โœ… All Grok examples completed successfully!") - - except Exception as e: - print(f"Error: {e}") - print("\nMake sure you have:") - print("1. Set XAI_API_KEY environment variable") - print("2. Set HANZO_API_KEY environment variable (or pass api_key to client)") - print("3. Installed hanzoai: pip install hanzoai") - print("4. (Optional) Installed hanzo-mcp: pip install hanzo-mcp") - - -if __name__ == "__main__": - main() diff --git a/examples/worktree_orchestration.py b/examples/worktree_orchestration.py deleted file mode 100644 index f54eec46f..000000000 --- a/examples/worktree_orchestration.py +++ /dev/null @@ -1,227 +0,0 @@ -#!/usr/bin/env python3 -""" -Example: Orchestrating Git Worktrees with Hanzo-MCP Agents - -This demonstrates how to use hanzo-mcp agents to manage parallel development -across multiple git worktrees, with each agent handling a specific task. -""" - -import json -import subprocess -from typing import Any, Dict, List - -# This would normally use the hanzo-mcp Python client -# For demonstration, showing the conceptual flow - - -class WorktreeOrchestrator: - """Orchestrates multiple agents working on git worktrees.""" - - def __init__(self, base_branch: str = "main"): - self.base_branch = base_branch - self.worktrees: Dict[str, str] = {} - - def create_worktree_agent_prompt(self, task_id: str, task_content: str) -> str: - """Generate agent prompt for worktree task.""" - return f""" -You are an autonomous development agent assigned to task {task_id}. - -TASK: {task_content} - -INSTRUCTIONS: -1. Create a git worktree for this task: - ```bash - git worktree add -b feature/{task_id} ../worktree-{task_id} - cd ../worktree-{task_id} - ``` - -2. Read the architecture.md file to understand the system design - -3. Implement the required functionality: - - Follow existing code patterns - - Use appropriate error handling - - Add necessary imports - - Create clean, modular code - -4. Write tests for your implementation: - - Unit tests for new functions - - Integration tests if needed - - Ensure tests pass - -5. Commit your changes: - ```bash - git add -A - git commit -m "feat({task_id}): {task_content}" - ``` - -6. Return a summary including: - - Files created/modified - - Test results - - Any issues encountered - - Ready for review: YES/NO - -Use available tools: -- read: to read existing files -- run_command: for git and test operations -- search: to find patterns in codebase -- grep_ast: to understand code structure -""" - - def create_critic_prompt(self, task_id: str, agent_summary: str) -> str: - """Generate critic prompt for reviewing agent work.""" - return f""" -You are a senior code reviewer. Review the implementation in worktree-{task_id}. - -AGENT SUMMARY: -{agent_summary} - -REVIEW CHECKLIST: -1. Code Quality - - [ ] Follows architecture.md patterns - - [ ] Clean, readable code - - [ ] Proper error handling - - [ ] No code duplication - -2. Security - - [ ] Input validation - - [ ] No hardcoded secrets - - [ ] Safe data handling - - [ ] SQL injection prevention - -3. Performance - - [ ] Efficient algorithms - - [ ] No unnecessary loops - - [ ] Proper caching - - [ ] Resource cleanup - -4. Testing - - [ ] Adequate test coverage - - [ ] Edge cases handled - - [ ] Tests actually pass - - [ ] Mocks used appropriately - -5. Documentation - - [ ] Functions have docstrings - - [ ] Complex logic explained - - [ ] API changes documented - -Read the implementation files and provide: -1. List of issues found (if any) -2. Severity of each issue (critical/major/minor) -3. Specific fix recommendations -4. Final verdict: APPROVED or NEEDS_FIXES - -Be strict but fair. This code will go to production. -""" - - def execute_workflow(self, tasks: List[Dict[str, Any]]) -> Dict[str, Any]: - """Execute the complete worktree workflow.""" - - results = {"total_tasks": len(tasks), "completed": [], "failed": [], "worktrees": []} - - for task in tasks: - task_id = task["id"] - task_content = task["content"] - - print(f"\n๐Ÿš€ Starting task {task_id}: {task_content}") - - # Step 1: Agent implements the task - agent_prompt = self.create_worktree_agent_prompt(task_id, task_content) - - # This would call: agent(prompt=agent_prompt) - # For demo, showing the structure - agent_result = { - "task_id": task_id, - "worktree": f"worktree-{task_id}", - "files_modified": ["src/auth.py", "tests/test_auth.py"], - "tests_passed": True, - "ready_for_review": True, - } - - print(f"โœ… Agent completed implementation") - - # Step 2: Critic reviews the implementation - if agent_result["ready_for_review"]: - critic_prompt = self.create_critic_prompt(task_id, json.dumps(agent_result, indent=2)) - - # This would call: critic(analysis=critic_prompt) - critic_result = {"verdict": "APPROVED", "issues": [], "score": 95} - - if critic_result["verdict"] == "APPROVED": - print(f"โœ… Critic approved implementation (score: {critic_result['score']})") - results["completed"].append(task_id) - - # Step 3: Prepare for merge - self.worktrees[task_id] = f"feature/{task_id}" - results["worktrees"].append( - { - "task_id": task_id, - "branch": f"feature/{task_id}", - "path": f"../worktree-{task_id}", - "ready_to_merge": True, - } - ) - else: - print(f"โš ๏ธ Critic requested fixes: {critic_result['issues']}") - - # Step 4: Agent fixes issues - fix_prompt = f""" - Fix these issues in worktree-{task_id}: - {json.dumps(critic_result["issues"], indent=2)} - """ - - # Second attempt would go here - results["failed"].append( - {"task_id": task_id, "reason": "Needs fixes", "issues": critic_result["issues"]} - ) - - return results - - def merge_completed_worktrees(self, results: Dict[str, Any]) -> None: - """Merge all completed worktrees back to base branch.""" - - print(f"\n๐Ÿ”€ Merging completed worktrees to {self.base_branch}") - - for worktree in results["worktrees"]: - if worktree["ready_to_merge"]: - branch = worktree["branch"] - - # This would execute: - # git checkout main - # git merge --no-ff feature/{task_id} - # git push origin main - - print(f"โœ… Merged {branch} to {self.base_branch}") - - print(f"\n๐ŸŽ‰ Workflow complete! {len(results['completed'])}/{results['total_tasks']} tasks merged") - - -# Example usage -if __name__ == "__main__": - # Sample tasks from todo list - tasks = [ - {"id": "auth-1", "content": "Implement JWT token generation", "priority": "high"}, - {"id": "auth-2", "content": "Create login endpoint", "priority": "high"}, - {"id": "auth-3", "content": "Add password reset flow", "priority": "medium"}, - {"id": "auth-4", "content": "Implement rate limiting", "priority": "medium"}, - ] - - # Initialize orchestrator - orchestrator = WorktreeOrchestrator(base_branch="main") - - # Execute parallel development workflow - results = orchestrator.execute_workflow(tasks) - - # Merge successful implementations - orchestrator.merge_completed_worktrees(results) - - # Output summary - print("\n๐Ÿ“Š Workflow Summary:") - print(f" Total tasks: {results['total_tasks']}") - print(f" Completed: {len(results['completed'])}") - print(f" Failed: {len(results['failed'])}") - print(f" Worktrees created: {len(results['worktrees'])}") - - # This summary could be sent to Linear or saved to a report - with open("workflow_report.json", "w") as f: - json.dump(results, f, indent=2) diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index 0b4a573d5..000000000 --- a/mkdocs.yml +++ /dev/null @@ -1,147 +0,0 @@ -site_name: Hanzo Python SDK -site_description: Comprehensive Python SDK for Hanzo AI - Agents, MCP Tools, and AI Infrastructure -site_url: https://hanzoai.github.io/python-sdk/ -repo_url: https://github.com/hanzoai/python-sdk -repo_name: hanzoai/python-sdk - -theme: - name: material - custom_dir: docs/overrides - features: - - content.code.copy - - content.code.select - - navigation.path - - navigation.sections - - navigation.expand - - navigation.tabs - - navigation.top - - content.code.annotate - - search.suggest - - search.highlight - - search.share - palette: - # Dark mode first (default) - - scheme: slate - primary: black - accent: zinc - toggle: - icon: material/brightness-4 - name: Switch to light mode - - scheme: default - primary: black - accent: zinc - toggle: - icon: material/brightness-7 - name: Switch to dark mode - logo: assets/logo.svg - favicon: assets/favicon.svg - font: false - -nav: - - Home: index.md - - Getting Started: - - Installation: getting-started/installation.md - - Quickstart: getting-started/quickstart.md - - Agent SDK: - - Overview: agent/index.md - - Agents: agent/agents.md - - Running Agents: agent/running_agents.md - - Results: agent/results.md - - Streaming: agent/streaming.md - - Tools: agent/tools.md - - Handoffs: agent/handoffs.md - - Tracing: agent/tracing.md - - Context: agent/context.md - - Guardrails: agent/guardrails.md - - Multi-Agent: agent/multi_agent.md - - Models: agent/models.md - - Configuration: agent/config.md - - MCP Tools: - - Overview: mcp/index.md - - Quickstart: mcp/quickstart.md - - Tools Reference: - - File System: mcp/tools/filesystem.md - - Shell & Commands: mcp/tools/shell.md - - Browser Automation: mcp/tools/browser.md - - Memory & Knowledge: mcp/tools/memory.md - - Reasoning: mcp/tools/reasoning.md - - LSP & Refactor: mcp/tools/lsp.md - - Agent Integration: mcp/tools/agent.md - - LLM & Consensus: mcp/tools/llm-tools.md - - Configuration: mcp/configuration.md - - VS Code Extension: mcp/vscode.md - - Tool Packages: - - hanzo-tools-core: tools/core.md - - hanzo-tools-fs: tools/fs.md - - hanzo-tools-shell: tools/shell.md - - hanzo-tools-browser: tools/browser.md - - hanzo-tools-memory: tools/memory.md - - hanzo-tools-reasoning: tools/reasoning.md - - hanzo-tools-lsp: tools/lsp.md - - hanzo-tools-refactor: tools/refactor.md - - hanzo-tools-agent: tools/agent.md - - hanzo-tools-llm: tools/llm-tools.md - - API Reference: - - Agent SDK: ref/agent/index.md - - MCP Server: ref/mcp/index.md - - Tools: ref/tools/index.md - -plugins: - - search - - mkdocstrings: - handlers: - python: - paths: - - "pkg/hanzo-agent/src/agents" - - "pkg/hanzo-mcp/hanzo_mcp" - - "pkg/hanzo-tools-core/hanzo_tools" - options: - docstring_style: google - signature_crossrefs: true - members_order: source - separate_signature: true - show_signature_annotations: true - heading_level: 3 - -extra: - generator: false - social: - - icon: fontawesome/brands/github - link: https://github.com/hanzoai - - icon: fontawesome/brands/discord - link: https://discord.gg/hanzo - - icon: fontawesome/brands/twitter - link: https://twitter.com/hanaboroshi - -markdown_extensions: - - admonition - - pymdownx.details - - pymdownx.superfences - - attr_list - - md_in_html - - pymdownx.highlight: - anchor_linenums: true - line_spans: __span - pygments_lang_class: true - - pymdownx.inlinehilite - - pymdownx.snippets - - pymdownx.tabbed: - alternate_style: true - - pymdownx.emoji: - emoji_index: !!python/name:material.extensions.emoji.twemoji - emoji_generator: !!python/name:material.extensions.emoji.to_svg - - toc: - permalink: true - -validation: - omitted_files: warn - absolute_links: warn - unrecognized_links: warn - anchors: warn - -extra_css: - - stylesheets/extra.css - -watch: - - "pkg/hanzo-agent/src/agents" - - "pkg/hanzo-mcp/hanzo_mcp" diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index 348975eb0..000000000 --- a/mypy.ini +++ /dev/null @@ -1,67 +0,0 @@ -[mypy] -pretty = True -show_error_codes = True - -# Exclude non-SDK code and subpackages (they have their own CI/linting) -exclude = ^(pkg/hanzoai/_files\.py|_dev/.*\.py|tests/.*|bin/.*|examples/.*|scripts/.*|pkg/hanzo/|pkg/hanzo-aci/|pkg/hanzo-agent/|pkg/hanzo-mcp/|pkg/hanzo-memory/|pkg/hanzo-network/|pkg/hanzo-dev-py/) - -strict_equality = True -implicit_reexport = True -no_implicit_optional = True - -warn_unreachable = True -warn_unused_configs = True - -# Relax strict typing for SDK with many optional dependencies -check_untyped_defs = False -disallow_any_generics = False -disallow_untyped_defs = False -disallow_untyped_calls = False -disallow_subclassing_any = False -disallow_incomplete_defs = False -disallow_untyped_decorators = False -warn_return_any = False -warn_unused_ignores = False -warn_redundant_casts = False -ignore_missing_imports = True - -cache_fine_grained = True - -# Disable common error codes for SDK compatibility -disable_error_code = func-returns-value,overload-cannot-match - -# https://github.com/python/mypy/issues/12162 -[mypy.overrides] -module = "black.files.*" -ignore_errors = true -ignore_missing_imports = true - -[mypy-hanzoai.grpo.*] -ignore_errors = true - -[mypy-hanzoai.llm_client] -ignore_errors = true - -[mypy-hanzoai.agents] -ignore_errors = true - -[mypy-hanzoai.mcp] -ignore_errors = true - -[mypy-hanzoai.cluster] -ignore_errors = true - -[mypy-hanzoai._client] -ignore_errors = true - -[mypy-hanzoai._base_client] -ignore_errors = true - -[mypy-hanzoai._models] -ignore_errors = true - -[mypy-hanzoai.auth] -ignore_errors = true - -[mypy-hanzoai._utils.*] -ignore_errors = true diff --git a/noxfile.py b/noxfile.py index 9c4e72f3a..53bca7ff2 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,67 +1,9 @@ -import os -import tempfile - import nox @nox.session(reuse_venv=True, name="test-pydantic-v1") def test_pydantic_v1(session: nox.Session) -> None: - # Install only the core SDK package and test dependencies - # Avoids installing heavy dependencies like torch (900MB) that would exhaust disk space - session.install("-e", ".") - session.install( - "pydantic<2", - "pytest", - "pytest-asyncio", - "respx", - "time-machine", - "dirty-equals>=0.6.0", - "importlib-metadata>=6.7.0", - "rich>=13.7.1", - "nest_asyncio", # Required for test_get_platform - ) - - # Create a temporary pytest.ini that doesn't include the pydantic v2-specific warning filter - # The main pyproject.toml has a filter for pydantic.warnings.PydanticDeprecatedSince20 - # which doesn't exist in pydantic v1 and causes pytest to fail at startup - # Note: pytest.ini uses INI format, not TOML - multiline values use indentation - pytest_ini_content = """[pytest] -testpaths = tests -addopts = --tb=short -xfail_strict = true -asyncio_mode = auto -asyncio_default_fixture_loop_scope = function -filterwarnings = - error - ignore::DeprecationWarning - ignore::UserWarning -""" - - # Write temporary config and use it - with tempfile.NamedTemporaryFile(mode="w", suffix=".ini", delete=False) as f: - f.write(pytest_ini_content) - pytest_ini_path = f.name + session.install("-r", "requirements-dev.lock") + session.install("pydantic<2") - try: - session.run( - "pytest", - "tests/", # Explicitly specify tests/ to avoid collecting from pkg/* subpackages - "-c", - pytest_ini_path, - "--showlocals", - "--ignore=tests/functional", - # Ignore tests that require optional dependencies (hanzo, hanzo_network, click, etc.) - # These are tested in the main test suite which has all dependencies installed - "--ignore=tests/e2e", - "--ignore=tests/test_fallback.py", - "--ignore=tests/test_interactive.py", - "--ignore=tests/test_memory.py", - "--ignore=tests/test_rate_limiter.py", - "--ignore=tests/test_refactoring.py", - "--ignore=tests/test_streaming.py", - "--ignore=tests/test_todo.py", - "--ignore=tests/test_hanzo_dev.py", # Requires hanzo package - *session.posargs, - ) - finally: - os.unlink(pytest_ini_path) + session.run("pytest", "--showlocals", "--ignore=tests/functional", *session.posargs) diff --git a/pkg/hanzo-aci/LICENSE b/pkg/hanzo-aci/LICENSE deleted file mode 100644 index 3e4c4339b..000000000 --- a/pkg/hanzo-aci/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/pkg/hanzo-aci/Makefile b/pkg/hanzo-aci/Makefile deleted file mode 100644 index 85a43d409..000000000 --- a/pkg/hanzo-aci/Makefile +++ /dev/null @@ -1,60 +0,0 @@ -SHELL = /usr/bin/env bash - -# Variables -PYTHON_VERSION = 3.12 -VENV_DIR = .venv -PRE_COMMIT_CONFIG = "./dev_config/python/.pre-commit-config.yaml" - -# ANSI Color Codes -GREEN := $(shell tput setaf 2) -YELLOW := $(shell tput setaf 3) -RED := $(shell tput setaf 1) -BLUE := $(shell tput setaf 6) -RESET := $(shell tput sgr0) - -# Setup: Install Python (if needed), create virtualenv, and install project in editable mode. -setup: - @echo "$(YELLOW)Setting up environment...$(RESET)" - @if ! command -v python$(PYTHON_VERSION) &>/dev/null; then \ - echo "$(BLUE)Installing Python $(PYTHON_VERSION) with uv...$(RESET)"; \ - uv python install $(PYTHON_VERSION); \ - fi - @if [ ! -d "$(VENV_DIR)" ]; then \ - echo "$(BLUE)Creating virtual environment...$(RESET)"; \ - uv venv $(VENV_DIR) --python=$(PYTHON_VERSION); \ - fi - @echo "$(BLUE)Installing project dependencies...$(RESET)" - @. $(VENV_DIR)/bin/activate && uv pip install -e . - @echo "$(GREEN)Setup complete.$(RESET)" - -# Install pre-commit hooks -install-pre-commit-hooks: - @echo "$(YELLOW)Installing pre-commit hooks...$(RESET)" - @git config --unset-all core.hooksPath || true - @. $(VENV_DIR)/bin/activate && uv pip install pre-commit && pre-commit install --config $(PRE_COMMIT_CONFIG) - @echo "$(GREEN)Pre-commit hooks installed successfully.$(RESET)" - -# Lint Python code using pre-commit hooks -lint-python: - @echo "$(YELLOW)Running linters...$(RESET)" - @. $(VENV_DIR)/bin/activate && pre-commit run --files dev_aci/**/* tests/**/* --show-diff-on-failure --config $(PRE_COMMIT_CONFIG) - -lint: lint-python - -# Build distribution packages using build (requires the 'build' package) -build: - @echo "$(YELLOW)Building distribution packages...$(RESET)" - @. $(VENV_DIR)/bin/activate && uv pip install build && python -m build - @echo "$(GREEN)Build complete. Distributions available in ./dist.$(RESET)" - -# Publish packages to PyPI using twine -publish: build - @echo "$(YELLOW)Publishing packages to PyPI...$(RESET)" - @. $(VENV_DIR)/bin/activate && uv pip install twine && python -m twine upload dist/* - @echo "$(GREEN)Publish complete.$(RESET)" - -# Clean build artifacts -clean: - @echo "$(YELLOW)Cleaning build artifacts...$(RESET)" - @rm -rf build dist *.egg-info - @echo "$(GREEN)Clean complete.$(RESET)" diff --git a/pkg/hanzo-aci/README.md b/pkg/hanzo-aci/README.md deleted file mode 100644 index bb681acf7..000000000 --- a/pkg/hanzo-aci/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# Agent-Computer Interface (ACI) for Dev - -An Agent-Computer Interface (ACI) designed for software development agents [Dev](https://github.com/hanzoai/Dev). This package provides essential tools and interfaces for AI agents to interact with computer systems for software development tasks. - -## Features - -- **Code Editor Interface**: Sophisticated editing capabilities through the `editor` module - - File creation and modification - - Code editing - - Configuration management - -- **Code Linting**: Built-in linting capabilities via the `linter` module - - Tree-sitter based code analysis - - Python-specific linting support - -- **Utility Functions**: Helper modules for common operations - - Shell command execution utilities - - Diff generation and analysis - - Logging functionality - -## Installation - -```bash -pip install dev-aci -``` - -Or using Poetry: - -```bash -poetry add dev-aci -``` - -## Project Structure - -``` -dev_aci/ -โ”œโ”€โ”€ editor/ # Code editing functionality -โ”œโ”€โ”€ linter/ # Code linting capabilities -โ””โ”€โ”€ utils/ # Utility functions -``` - -## Development - -1. Clone the repository: -```bash -git clone https://github.com/hanzoai/dev-aci.git -cd dev-aci -``` - -2. Install development dependencies: -```bash -poetry install -``` - -3. Configure pre-commit-hooks -```bash -make install-pre-commit-hooks -``` - -4. Run tests: -```bash -poetry run pytest -``` - -## License - -This project is licensed under the MIT License. diff --git a/pkg/hanzo-aci/dev_aci/__init__.py b/pkg/hanzo-aci/dev_aci/__init__.py deleted file mode 100644 index 1a105c570..000000000 --- a/pkg/hanzo-aci/dev_aci/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .editor import file_editor -from .editor.file_cache import FileCache - -__all__ = ["file_editor", "FileCache"] diff --git a/pkg/hanzo-aci/dev_aci/editor/__init__.py b/pkg/hanzo-aci/dev_aci/editor/__init__.py deleted file mode 100644 index 2a298ef88..000000000 --- a/pkg/hanzo-aci/dev_aci/editor/__init__.py +++ /dev/null @@ -1,76 +0,0 @@ -import json -import uuid - -from .editor import Command, OHEditor -from .encoding import EncodingManager, with_encoding -from .exceptions import ToolError -from .file_cache import FileCache -from .results import ToolResult - -_GLOBAL_EDITOR = OHEditor() - -__all__ = [ - "Command", - "OHEditor", - "ToolError", - "ToolResult", - "FileCache", - "file_editor", - "EncodingManager", - "with_encoding", -] - - -def _make_api_tool_result(tool_result: ToolResult) -> str: - """Convert an agent ToolResult to an API ToolResultBlockParam.""" - if tool_result.error: - return f"ERROR:\n{tool_result.error}" - - assert tool_result.output, "Expected output in file_editor." - return tool_result.output - - -def file_editor( - command: Command, - path: str, - file_text: str | None = None, - view_range: list[int] | None = None, - old_str: str | None = None, - new_str: str | None = None, - insert_line: int | None = None, - enable_linting: bool = False, -) -> str: - result: ToolResult | None = None - try: - result = _GLOBAL_EDITOR( - command=command, - path=path, - file_text=file_text, - view_range=view_range, - old_str=old_str, - new_str=new_str, - insert_line=insert_line, - enable_linting=enable_linting, - ) - except ToolError as e: - result = ToolResult(error=e.message) - - formatted_output_and_error = _make_api_tool_result(result) - marker_id = uuid.uuid4().hex - - def json_generator(): - yield "{" - first = True - for key, value in result.to_dict().items(): - if not first: - yield "," - first = False - yield f'"{key}": {json.dumps(value)}' - yield f', "formatted_output_and_error": {json.dumps(formatted_output_and_error)}' - yield "}" - - return ( - f"\n" - + "".join(json_generator()) - + f"\n" - ) diff --git a/pkg/hanzo-aci/dev_aci/editor/config.py b/pkg/hanzo-aci/dev_aci/editor/config.py deleted file mode 100644 index bfe020446..000000000 --- a/pkg/hanzo-aci/dev_aci/editor/config.py +++ /dev/null @@ -1,2 +0,0 @@ -MAX_RESPONSE_LEN_CHAR: int = 16000 -SNIPPET_CONTEXT_WINDOW: int = 4 diff --git a/pkg/hanzo-aci/dev_aci/editor/editor.py b/pkg/hanzo-aci/dev_aci/editor/editor.py deleted file mode 100644 index e5042d51f..000000000 --- a/pkg/hanzo-aci/dev_aci/editor/editor.py +++ /dev/null @@ -1,632 +0,0 @@ -import os -import re -import shutil -import tempfile -from pathlib import Path -from typing import Literal, get_args - -from binaryornot.check import is_binary - -from dev_aci.linter import DefaultLinter -from dev_aci.utils.shell import run_shell_cmd - -from .config import SNIPPET_CONTEXT_WINDOW -from .encoding import EncodingManager, with_encoding -from .exceptions import ( - EditorToolParameterInvalidError, - EditorToolParameterMissingError, - FileValidationError, - ToolError, -) -from .history import FileHistoryManager -from .prompts import DIRECTORY_CONTENT_TRUNCATED_NOTICE, FILE_CONTENT_TRUNCATED_NOTICE -from .results import CLIResult, maybe_truncate - -Command = Literal[ - "view", - "create", - "str_replace", - "insert", - "undo_edit", - # Future: 'jump_to_definition', 'find_references' (requires LSP integration) -] - - -class OHEditor: - """An filesystem editor tool that allows the agent to view, create, navigate, and edit files. - - The tool parameters are defined by Anthropic and are not editable. - - Original implementation: https://github.com/anthropics/anthropic-quickstarts/blob/main/computer-use-demo/computer_use_demo/tools/edit.py - """ - - TOOL_NAME = "oh_editor" - MAX_FILE_SIZE_MB = 10 # Maximum file size in MB - - def __init__( - self, - max_file_size_mb: int | None = None, - workspace_root: str | None = None, - ): - """Initialize the editor. - - Args: - max_file_size_mb: Maximum file size in MB. If None, uses the default MAX_FILE_SIZE_MB. - workspace_root: Root directory that serves as the current working directory for relative path - suggestions. Must be an absolute path. If None, no path suggestions will be - provided for relative paths. - """ - self._linter = DefaultLinter() - self._history_manager = FileHistoryManager(max_history_per_file=10) - self._max_file_size = ( - (max_file_size_mb or self.MAX_FILE_SIZE_MB) * 1024 * 1024 - ) # Convert to bytes - # Initialize encoding manager - self._encoding_manager = EncodingManager() - # Set cwd (current working directory) if workspace_root is provided - if workspace_root is not None: - workspace_path = Path(workspace_root) - # Ensure workspace_root is an absolute path - if not workspace_path.is_absolute(): - raise ValueError( - f"workspace_root must be an absolute path, got: {workspace_root}" - ) - self._cwd = workspace_path - else: - self._cwd = None # type: ignore - - def __call__( - self, - *, - command: Command, - path: str, - file_text: str | None = None, - view_range: list[int] | None = None, - old_str: str | None = None, - new_str: str | None = None, - insert_line: int | None = None, - enable_linting: bool = False, - **kwargs, - ) -> CLIResult: - _path = Path(path) - self.validate_path(command, _path) - if command == "view": - return self.view(_path, view_range) - elif command == "create": - if file_text is None: - raise EditorToolParameterMissingError(command, "file_text") - self.write_file(_path, file_text) - self._history_manager.add_history(_path, file_text) - return CLIResult( - path=str(_path), - new_content=file_text, - prev_exist=False, - output=f"File created successfully at: {_path}", - ) - elif command == "str_replace": - if old_str is None: - raise EditorToolParameterMissingError(command, "old_str") - if new_str == old_str: - raise EditorToolParameterInvalidError( - "new_str", - new_str, - "No replacement was performed. `new_str` and `old_str` must be different.", - ) - return self.str_replace(_path, old_str, new_str, enable_linting) - elif command == "insert": - if insert_line is None: - raise EditorToolParameterMissingError(command, "insert_line") - if new_str is None: - raise EditorToolParameterMissingError(command, "new_str") - return self.insert(_path, insert_line, new_str, enable_linting) - elif command == "undo_edit": - return self.undo_edit(_path) - - raise ToolError( - f"Unrecognized command {command}. The allowed commands for the {self.TOOL_NAME} tool are: {', '.join(get_args(Command))}" - ) - - @with_encoding - def _count_lines(self, path: Path, encoding: str = "utf-8") -> int: - """Count the number of lines in a file safely. - - Args: - path: Path to the file - encoding: The encoding to use when reading the file (auto-detected by decorator) - - Returns: - The number of lines in the file - """ - with open(path, encoding=encoding) as f: - return sum(1 for _ in f) - - @with_encoding - def str_replace( - self, - path: Path, - old_str: str, - new_str: str | None, - enable_linting: bool, - encoding: str = "utf-8", - ) -> CLIResult: - """Implement the str_replace command, which replaces old_str with new_str in the file content. - - Args: - path: Path to the file - old_str: String to replace - new_str: Replacement string - enable_linting: Whether to run linting on the changes - encoding: The encoding to use (auto-detected by decorator) - """ - self.validate_file(path) - old_str = old_str.expandtabs() - new_str = new_str.expandtabs() if new_str is not None else "" - - # Read the entire file first to handle both single-line and multi-line replacements - file_content = self.read_file(path).expandtabs() - - # Find all occurrences using regex - # Escape special regex characters in old_str to match it literally - pattern = re.escape(old_str) - occurrences = [ - ( - file_content.count("\n", 0, match.start()) + 1, # line number - match.group(), # matched text - match.start(), # start position - ) - for match in re.finditer(pattern, file_content) - ] - - if not occurrences: - raise ToolError( - f"No replacement was performed, old_str `{old_str}` did not appear verbatim in {path}." - ) - if len(occurrences) > 1: - line_numbers = sorted(set(line for line, _, _ in occurrences)) - raise ToolError( - f"No replacement was performed. Multiple occurrences of old_str `{old_str}` in lines {line_numbers}. Please ensure it is unique." - ) - - # We found exactly one occurrence - replacement_line, matched_text, idx = occurrences[0] - - # Create new content by replacing just the matched text - new_file_content = ( - file_content[:idx] + new_str + file_content[idx + len(matched_text) :] - ) - - # Write the new content to the file - self.write_file(path, new_file_content) - - # Save the content to history - self._history_manager.add_history(path, file_content) - - # Create a snippet of the edited section - start_line = max(0, replacement_line - SNIPPET_CONTEXT_WINDOW) - end_line = replacement_line + SNIPPET_CONTEXT_WINDOW + new_str.count("\n") - - # Read just the snippet range - snippet = self.read_file(path, start_line=start_line, end_line=end_line) - - # Prepare the success message - success_message = f"The file {path} has been edited. " - success_message += self._make_output( - snippet, f"a snippet of {path}", start_line + 1 - ) - - if enable_linting: - # Run linting on the changes - lint_results = self._run_linting(file_content, new_file_content, path) - success_message += "\n" + lint_results + "\n" - - success_message += "Review the changes and make sure they are as expected. Edit the file again if necessary." - return CLIResult( - output=success_message, - prev_exist=True, - path=str(path), - old_content=file_content, - new_content=new_file_content, - ) - - def view(self, path: Path, view_range: list[int] | None = None) -> CLIResult: - """View the contents of a file or a directory.""" - if path.is_dir(): - if view_range: - raise EditorToolParameterInvalidError( - "view_range", - view_range, - "The `view_range` parameter is not allowed when `path` points to a directory.", - ) - - # First count hidden files/dirs in current directory only - # -mindepth 1 excludes . and .. automatically - _, hidden_stdout, _ = run_shell_cmd( - rf"find -L {path} -mindepth 1 -maxdepth 1 -name '.*'" - ) - hidden_count = ( - len(hidden_stdout.strip().split("\n")) if hidden_stdout.strip() else 0 - ) - - # Then get files/dirs up to 2 levels deep, excluding hidden entries at both depth 1 and 2 - _, stdout, stderr = run_shell_cmd( - rf"find -L {path} -maxdepth 2 -not \( -path '{path}/.*' -o -path '{path}/*/.*' \) | sort", - truncate_notice=DIRECTORY_CONTENT_TRUNCATED_NOTICE, - ) - if not stderr: - # Add trailing slashes to directories - paths = stdout.strip().split("\n") if stdout.strip() else [] - formatted_paths = [] - for p in paths: - if Path(p).is_dir(): - formatted_paths.append(f"{p}/") - else: - formatted_paths.append(p) - - msg = [ - f"Here's the files and directories up to 2 levels deep in {path}, excluding hidden items:\n" - + "\n".join(formatted_paths) - ] - if hidden_count > 0: - msg.append( - f"\n{hidden_count} hidden files/directories in this directory are excluded. You can use 'ls -la {path}' to see them." - ) - stdout = "\n".join(msg) - return CLIResult( - output=stdout, - error=stderr, - path=str(path), - prev_exist=True, - ) - - # Validate file and count lines - self.validate_file(path) - num_lines = self._count_lines(path) - - start_line = 1 - if not view_range: - file_content = self.read_file(path) - output = self._make_output(file_content, str(path), start_line) - - return CLIResult( - output=output, - path=str(path), - prev_exist=True, - ) - - if len(view_range) != 2 or not all(isinstance(i, int) for i in view_range): - raise EditorToolParameterInvalidError( - "view_range", - view_range, - "It should be a list of two integers.", - ) - - start_line, end_line = view_range - if start_line < 1 or start_line > num_lines: - raise EditorToolParameterInvalidError( - "view_range", - view_range, - f"Its first element `{start_line}` should be within the range of lines of the file: {[1, num_lines]}`.", - ) - - if end_line > num_lines: - raise EditorToolParameterInvalidError( - "view_range", - view_range, - f"Its second element `{end_line}` should be smaller than the number of lines in the file: `{num_lines}`.", - ) - - if end_line != -1 and end_line < start_line: - raise EditorToolParameterInvalidError( - "view_range", - view_range, - f"Its second element `{end_line}` should be greater than or equal to the first element `{start_line}`.", - ) - - if end_line == -1: - end_line = num_lines - - file_content = self.read_file(path, start_line=start_line, end_line=end_line) - - # Get the detected encoding - output = self._make_output(file_content, str(path), start_line) - - return CLIResult( - path=str(path), - output=output, - prev_exist=True, - ) - - @with_encoding - def write_file(self, path: Path, file_text: str, encoding: str = "utf-8") -> None: - """Write the content of a file to a given path; raise a ToolError if an error occurs. - - Args: - path: Path to the file to write - file_text: Content to write to the file - encoding: The encoding to use when writing the file (auto-detected by decorator) - """ - self.validate_file(path) - try: - # Use open with encoding instead of path.write_text - with open(path, "w", encoding=encoding) as f: - f.write(file_text) - except Exception as e: - raise ToolError(f"Ran into {e} while trying to write to {path}") from None - - @with_encoding - def insert( - self, - path: Path, - insert_line: int, - new_str: str, - enable_linting: bool, - encoding: str = "utf-8", - ) -> CLIResult: - """Implement the insert command, which inserts new_str at the specified line in the file content. - - Args: - path: Path to the file - insert_line: Line number where to insert the new content - new_str: Content to insert - enable_linting: Whether to run linting on the changes - encoding: The encoding to use (auto-detected by decorator) - """ - # Validate file and count lines - self.validate_file(path) - num_lines = self._count_lines(path) - - if insert_line < 0 or insert_line > num_lines: - raise EditorToolParameterInvalidError( - "insert_line", - insert_line, - f"It should be within the range of lines of the file: {[0, num_lines]}", - ) - - new_str = new_str.expandtabs() - new_str_lines = new_str.split("\n") - - # Create temporary file for the new content - with tempfile.NamedTemporaryFile( - mode="w", encoding=encoding, delete=False - ) as temp_file: - # Copy lines before insert point and save them for history - history_lines = [] - with open(path, "r", encoding=encoding) as f: - for i, line in enumerate(f, 1): - if i > insert_line: - break - temp_file.write(line.expandtabs()) - history_lines.append(line) - - # Insert new content - for line in new_str_lines: - temp_file.write(line + "\n") - - # Copy remaining lines and save them for history - with open(path, "r", encoding=encoding) as f: - for i, line in enumerate(f, 1): - if i <= insert_line: - continue - temp_file.write(line.expandtabs()) - history_lines.append(line) - - # Move temporary file to original location - shutil.move(temp_file.name, path) - - # Read just the snippet range - start_line = max(1, insert_line - SNIPPET_CONTEXT_WINDOW) - end_line = min( - num_lines + len(new_str_lines), - insert_line + SNIPPET_CONTEXT_WINDOW + len(new_str_lines), - ) - snippet = self.read_file(path, start_line=start_line, end_line=end_line) - - # Save history - we already have the lines in memory - file_text = "".join(history_lines) - self._history_manager.add_history(path, file_text) - - # Read new content for result - new_file_text = self.read_file(path) - - success_message = f"The file {path} has been edited. " - success_message += self._make_output( - snippet, - "a snippet of the edited file", - max(1, insert_line - SNIPPET_CONTEXT_WINDOW + 1), - ) - - if enable_linting: - # Run linting on the changes - lint_results = self._run_linting(file_text, new_file_text, path) - success_message += "\n" + lint_results + "\n" - - success_message += "Review the changes and make sure they are as expected (correct indentation, no duplicate lines, etc). Edit the file again if necessary." - return CLIResult( - output=success_message, - prev_exist=True, - path=str(path), - old_content=file_text, - new_content=new_file_text, - ) - - def validate_path(self, command: Command, path: Path) -> None: - """Check that the path/command combination is valid. - - Validates: - 1. Path is absolute - 2. Path and command are compatible - """ - # Check if its an absolute path - if not path.is_absolute(): - suggestion_message = ( - "The path should be an absolute path, starting with `/`." - ) - - # Only suggest the absolute path if cwd is provided and the path exists - if self._cwd is not None: - suggested_path = self._cwd / path - if suggested_path.exists(): - suggestion_message += f" Maybe you meant {suggested_path}?" - - raise EditorToolParameterInvalidError( - "path", - path, - suggestion_message, - ) - - # Check if path and command are compatible - if command == "create" and path.exists(): - raise EditorToolParameterInvalidError( - "path", - path, - f"File already exists at: {path}. Cannot overwrite files using command `create`.", - ) - if command != "create" and not path.exists(): - raise EditorToolParameterInvalidError( - "path", - path, - f"The path {path} does not exist. Please provide a valid path.", - ) - if command != "view" and path.is_dir(): - raise EditorToolParameterInvalidError( - "path", - path, - f"The path {path} is a directory and only the `view` command can be used on directories.", - ) - - def undo_edit(self, path: Path) -> CLIResult: - """Implement the undo_edit command.""" - current_text = self.read_file(path).expandtabs() - old_text = self._history_manager.pop_last_history(path) - if old_text is None: - raise ToolError(f"No edit history found for {path}.") - - self.write_file(path, old_text) - - return CLIResult( - output=f"Last edit to {path} undone successfully. {self._make_output(old_text, str(path))}", - path=str(path), - prev_exist=True, - old_content=current_text, - new_content=old_text, - ) - - def validate_file(self, path: Path) -> None: - """Validate a file for reading or editing operations. - - Args: - path: Path to the file to validate - - Raises: - FileValidationError: If the file fails validation - """ - # Skip validation for directories or non-existent files (for create command) - if not path.exists() or not path.is_file(): - return - - # Check file size - file_size = os.path.getsize(path) - max_size = self._max_file_size - if file_size > max_size: - raise FileValidationError( - path=str(path), - reason=f"File is too large ({file_size / 1024 / 1024:.1f}MB). Maximum allowed size is {int(max_size / 1024 / 1024)}MB.", - ) - - # Check file type - if is_binary(str(path)): - raise FileValidationError( - path=str(path), - reason="File appears to be binary. Only text files can be edited.", - ) - - @with_encoding - def read_file( - self, - path: Path, - start_line: int | None = None, - end_line: int | None = None, - encoding: str = "utf-8", # Default will be overridden by decorator - ) -> str: - """Read the content of a file from a given path; raise a ToolError if an error occurs. - - Args: - path: Path to the file to read - start_line: Optional start line number (1-based). If provided with end_line, only reads that range. - end_line: Optional end line number (1-based). Must be provided with start_line. - encoding: The encoding to use when reading the file (auto-detected by decorator) - """ - self.validate_file(path) - try: - if start_line is not None and end_line is not None: - # Read only the specified line range - lines = [] - with open(path, "r", encoding=encoding) as f: - for i, line in enumerate(f, 1): - if i > end_line: - break - if i >= start_line: - lines.append(line) - return "".join(lines) - elif start_line is not None or end_line is not None: - raise ValueError( - "Both start_line and end_line must be provided together" - ) - else: - # Use line-by-line reading to avoid loading entire file into memory - with open(path, "r", encoding=encoding) as f: - return "".join(f) - except Exception as e: - raise ToolError(f"Ran into {e} while trying to read {path}") from None - - def _make_output( - self, - snippet_content: str, - snippet_description: str, - start_line: int = 1, - expand_tabs: bool = True, - ) -> str: - """Generate output for the CLI based on the content of a code snippet.""" - snippet_content = maybe_truncate( - snippet_content, truncate_notice=FILE_CONTENT_TRUNCATED_NOTICE - ) - if expand_tabs: - snippet_content = snippet_content.expandtabs() - - snippet_content = "\n".join( - [ - f"{i + start_line:6}\t{line}" - for i, line in enumerate(snippet_content.split("\n")) - ] - ) - return ( - f"Here's the result of running `cat -n` on {snippet_description}:\n" - + snippet_content - + "\n" - ) - - def _run_linting(self, old_content: str, new_content: str, path: Path) -> str: - """Run linting on file changes and return formatted results.""" - # Create a temporary directory - with tempfile.TemporaryDirectory() as temp_dir: - # Create paths with exact filenames in temp directory - temp_old = Path(temp_dir) / f"old.{path.name}" - temp_new = Path(temp_dir) / f"new.{path.name}" - - # Write content to temporary files - temp_old.write_text(old_content) - temp_new.write_text(new_content) - - # Run linting on the changes - results = self._linter.lint_file_diff(str(temp_old), str(temp_new)) - - if not results: - return "No linting issues found in the changes." - - # Format results - output = ["Linting issues found in the changes:"] - for result in results: - output.append( - f"- Line {result.line}, Column {result.column}: {result.message}" - ) - return "\n".join(output) + "\n" diff --git a/pkg/hanzo-aci/dev_aci/editor/encoding.py b/pkg/hanzo-aci/dev_aci/editor/encoding.py deleted file mode 100644 index 522a3df00..000000000 --- a/pkg/hanzo-aci/dev_aci/editor/encoding.py +++ /dev/null @@ -1,119 +0,0 @@ -"""Encoding management for file operations.""" - -import functools -import os -from pathlib import Path -from typing import Tuple - -import charset_normalizer -from cachetools import LRUCache - - -class EncodingManager: - """Manages file encodings across multiple operations to ensure consistency.""" - - # Default maximum number of entries in the cache - DEFAULT_MAX_CACHE_SIZE = 1000 # ~= 300 KB - - def __init__(self, max_cache_size=None): - # Cache detected encodings to avoid repeated detection on the same file - # Format: {path_str: (encoding, mtime)} - self._encoding_cache: LRUCache[str, Tuple[str, float]] = LRUCache( - maxsize=max_cache_size or self.DEFAULT_MAX_CACHE_SIZE - ) - # Default fallback encoding - self.default_encoding = "utf-8" - # Confidence threshold for encoding detection - self.confidence_threshold = 0.9 - - def detect_encoding(self, path: Path) -> str: - """Detect the encoding of a file without handling caching logic. - - Args: - path: Path to the file - Returns: - The detected encoding or default encoding if detection fails - """ - # Handle non-existent files - if not path.exists(): - return self.default_encoding - - # Read a sample of the file to detect encoding - sample_size = min(os.path.getsize(path), 1024 * 1024) # Max 1MB sample - with open(path, "rb") as f: - raw_data = f.read(sample_size) - - # Use charset_normalizer instead of chardet - results = charset_normalizer.detect(raw_data) - - # Get the best match if any exists - if results and results["confidence"] > self.confidence_threshold: - encoding = results["encoding"] - else: - encoding = self.default_encoding - - return encoding - - def get_encoding(self, path: Path) -> str: - """Get encoding for a file, using cache or detecting if necessary. - - Args: - path: Path to the file - Returns: - The encoding for the file - """ - path_str = str(path) - # If file doesn't exist, return default encoding - if not path.exists(): - return self.default_encoding - - # Get current modification time - current_mtime = os.path.getmtime(path) - - # Check cache for valid entry - if path_str in self._encoding_cache: - cached_encoding, cached_mtime = self._encoding_cache[path_str] - if cached_mtime == current_mtime: - return cached_encoding - - # No valid cache entry, detect encoding - encoding = self.detect_encoding(path) - - # Cache the result with current modification time - self._encoding_cache[path_str] = (encoding, current_mtime) - return encoding - - -def with_encoding(method): - """Decorator to handle file encoding for file operations. - - This decorator automatically detects and applies the correct encoding - for file operations, ensuring consistency between read and write operations. - - Args: - method: The method to decorate - Returns: - The decorated method - """ - - @functools.wraps(method) - def wrapper(self, path: Path, *args, **kwargs): - # Skip encoding handling for directories - if path.is_dir(): - return method(self, path, *args, **kwargs) - - # For files that don't exist yet (like in 'create' command), - # use the default encoding - if not path.exists(): - if "encoding" not in kwargs: - kwargs["encoding"] = self._encoding_manager.default_encoding - else: - # Get encoding from the encoding manager for existing files - encoding = self._encoding_manager.get_encoding(path) - # Add encoding to kwargs if the method accepts it - if "encoding" not in kwargs: - kwargs["encoding"] = encoding - - return method(self, path, *args, **kwargs) - - return wrapper diff --git a/pkg/hanzo-aci/dev_aci/editor/exceptions.py b/pkg/hanzo-aci/dev_aci/editor/exceptions.py deleted file mode 100644 index 55e0b938e..000000000 --- a/pkg/hanzo-aci/dev_aci/editor/exceptions.py +++ /dev/null @@ -1,41 +0,0 @@ -class ToolError(Exception): - """Raised when a tool encounters an error.""" - - def __init__(self, message): - self.message = message - super().__init__(message) - - def __str__(self): - return self.message - - -class EditorToolParameterMissingError(ToolError): - """Raised when a required parameter is missing for a tool command.""" - - def __init__(self, command, parameter): - self.command = command - self.parameter = parameter - self.message = f"Parameter `{parameter}` is required for command: {command}." - - -class EditorToolParameterInvalidError(ToolError): - """Raised when a parameter is invalid for a tool command.""" - - def __init__(self, parameter, value, hint=None): - self.parameter = parameter - self.value = value - self.message = ( - f"Invalid `{parameter}` parameter: {value}. {hint}" - if hint - else f"Invalid `{parameter}` parameter: {value}." - ) - - -class FileValidationError(ToolError): - """Raised when a file fails validation checks (size, type, etc.).""" - - def __init__(self, path: str, reason: str): - self.path = path - self.reason = reason - self.message = f"File validation failed for {path}: {reason}" - super().__init__(self.message) diff --git a/pkg/hanzo-aci/dev_aci/editor/file_cache.py b/pkg/hanzo-aci/dev_aci/editor/file_cache.py deleted file mode 100644 index 8e0e02bae..000000000 --- a/pkg/hanzo-aci/dev_aci/editor/file_cache.py +++ /dev/null @@ -1,146 +0,0 @@ -import hashlib -import json -import logging -import os -import time -from pathlib import Path -from typing import Any, Optional - -logging.basicConfig(level=logging.DEBUG) -logger = logging.getLogger(__name__) - - -class FileCache: - def __init__(self, directory: str, size_limit: Optional[int] = None): - self.directory = Path(directory) - self.directory.mkdir(parents=True, exist_ok=True) - self.size_limit = size_limit - self.current_size = 0 - self._update_current_size() - logger.debug( - f"FileCache initialized with directory: {self.directory}, size_limit: {self.size_limit}, current_size: {self.current_size}" - ) - - def _get_file_path(self, key: str) -> Path: - hashed_key = hashlib.sha256(key.encode()).hexdigest() - return self.directory / f"{hashed_key}.json" - - def _update_current_size(self): - self.current_size = sum( - f.stat().st_size for f in self.directory.glob("*.json") if f.is_file() - ) - logger.debug(f"Current size updated: {self.current_size}") - - def set(self, key: str, value: Any) -> None: - file_path = self._get_file_path(key) - content = json.dumps({"key": key, "value": value}) - content_size = len(content.encode("utf-8")) - logger.debug(f"Setting key: {key}, content_size: {content_size}") - - if self.size_limit is not None: - if file_path.exists(): - old_size = file_path.stat().st_size - size_diff = content_size - old_size - logger.debug( - f"Existing file: old_size: {old_size}, size_diff: {size_diff}" - ) - if size_diff > 0: - while ( - self.current_size + size_diff > self.size_limit - and len(self) > 1 - ): - logger.debug( - f"Evicting oldest (existing file case): current_size: {self.current_size}, size_limit: {self.size_limit}" - ) - self._evict_oldest(file_path) - else: - while ( - self.current_size + content_size > self.size_limit and len(self) > 1 - ): - logger.debug( - f"Evicting oldest (new file case): current_size: {self.current_size}, size_limit: {self.size_limit}" - ) - self._evict_oldest(file_path) - - if file_path.exists(): - self.current_size -= file_path.stat().st_size - logger.debug( - f"Existing file removed from current_size: {self.current_size}" - ) - - with open(file_path, "w") as f: - f.write(content) - - self.current_size += content_size - logger.debug(f"File written, new current_size: {self.current_size}") - os.utime( - file_path, (time.time(), time.time()) - ) # Update access and modification time - - def _evict_oldest(self, exclude_path: Optional[Path] = None): - oldest_file = min( - ( - f - for f in self.directory.glob("*.json") - if f.is_file() and f != exclude_path - ), - key=os.path.getmtime, - ) - evicted_size = oldest_file.stat().st_size - self.current_size -= evicted_size - os.remove(oldest_file) - logger.debug( - f"Evicted file: {oldest_file}, size: {evicted_size}, new current_size: {self.current_size}" - ) - - def get(self, key: str, default: Any = None) -> Any: - file_path = self._get_file_path(key) - if not file_path.exists(): - logger.debug(f"Get: Key not found: {key}") - return default - with open(file_path, "r") as f: - data = json.load(f) - os.utime(file_path, (time.time(), time.time())) # Update access time - logger.debug(f"Get: Key found: {key}") - return data["value"] - - def delete(self, key: str) -> None: - file_path = self._get_file_path(key) - if file_path.exists(): - deleted_size = file_path.stat().st_size - self.current_size -= deleted_size - os.remove(file_path) - logger.debug( - f"Deleted key: {key}, size: {deleted_size}, new current_size: {self.current_size}" - ) - - def clear(self) -> None: - for item in self.directory.glob("*.json"): - if item.is_file(): - os.remove(item) - self.current_size = 0 - logger.debug("Cache cleared") - - def __contains__(self, key: str) -> bool: - exists = self._get_file_path(key).exists() - logger.debug(f"Contains check: {key}, result: {exists}") - return exists - - def __len__(self) -> int: - length = sum(1 for _ in self.directory.glob("*.json") if _.is_file()) - logger.debug(f"Cache length: {length}") - return length - - def __iter__(self): - for file in self.directory.glob("*.json"): - if file.is_file(): - with open(file, "r") as f: - data = json.load(f) - logger.debug(f"Yielding key: {data['key']}") - yield data["key"] - - def __getitem__(self, key: str) -> Any: - return self.get(key) - - def __setitem__(self, key: str, value: Any) -> None: - self.set(key, value) diff --git a/pkg/hanzo-aci/dev_aci/editor/history.py b/pkg/hanzo-aci/dev_aci/editor/history.py deleted file mode 100644 index 6c72b034e..000000000 --- a/pkg/hanzo-aci/dev_aci/editor/history.py +++ /dev/null @@ -1,119 +0,0 @@ -"""History management for file edits with disk-based storage and memory constraints.""" - -import logging -import tempfile -from pathlib import Path -from typing import List, Optional - -from .file_cache import FileCache - - -class FileHistoryManager: - """Manages file edit history with disk-based storage and memory constraints.""" - - def __init__( - self, max_history_per_file: int = 5, history_dir: Optional[Path] = None - ): - """Initialize the history manager. - - Args: - max_history_per_file: Maximum number of history entries to keep per file (default: 5) - history_dir: Directory to store history files. If None, uses a temp directory - - Notes: - - Each file's history is limited to the last N entries to conserve memory - - The file cache is limited to prevent excessive disk usage - - Older entries are automatically removed when limits are exceeded - """ - self.max_history_per_file = max_history_per_file - if history_dir is None: - history_dir = Path(tempfile.mkdtemp(prefix="oh_editor_history_")) - self.cache = FileCache(str(history_dir)) - self.logger = logging.getLogger(__name__) - - def _get_metadata_key(self, file_path: Path) -> str: - return f"{file_path}.metadata" - - def _get_history_key(self, file_path: Path, counter: int) -> str: - return f"{file_path}.{counter}" - - def add_history(self, file_path: Path, content: str): - """Add a new history entry for a file.""" - metadata_key = self._get_metadata_key(file_path) - metadata = self.cache.get(metadata_key, {"entries": [], "counter": 0}) - counter = metadata["counter"] - - # Add new entry - history_key = self._get_history_key(file_path, counter) - self.cache.set(history_key, content) - - metadata["entries"].append(counter) - metadata["counter"] += 1 - - # Keep only last N entries - while len(metadata["entries"]) > self.max_history_per_file: - old_counter = metadata["entries"].pop(0) - old_history_key = self._get_history_key(file_path, old_counter) - self.cache.delete(old_history_key) - - self.cache.set(metadata_key, metadata) - - def pop_last_history(self, file_path: Path) -> Optional[str]: - """Pop and return the most recent history entry for a file.""" - metadata_key = self._get_metadata_key(file_path) - metadata = self.cache.get(metadata_key, {"entries": [], "counter": 0}) - entries = metadata["entries"] - - if not entries: - return None - - # Pop and remove the last entry - last_counter = entries.pop() - history_key = self._get_history_key(file_path, last_counter) - content = self.cache.get(history_key) - - if content is None: - self.logger.warning(f"History entry not found for {file_path}") - else: - # Remove the entry from the cache - self.cache.delete(history_key) - - # Update metadata - metadata["entries"] = entries - self.cache.set(metadata_key, metadata) - - return content - - def get_metadata(self, file_path: Path): - """Get metadata for a file (for testing purposes).""" - metadata_key = self._get_metadata_key(file_path) - metadata = self.cache.get(metadata_key, {"entries": [], "counter": 0}) - return metadata # Return the actual metadata, not a copy - - def clear_history(self, file_path: Path): - """Clear history for a given file.""" - metadata_key = self._get_metadata_key(file_path) - metadata = self.cache.get(metadata_key, {"entries": [], "counter": 0}) - - # Delete all history entries - for counter in metadata["entries"]: - history_key = self._get_history_key(file_path, counter) - self.cache.delete(history_key) - - # Clear metadata - self.cache.set(metadata_key, {"entries": [], "counter": 0}) - - def get_all_history(self, file_path: Path) -> List[str]: - """Get all history entries for a file.""" - metadata_key = self._get_metadata_key(file_path) - metadata = self.cache.get(metadata_key, {"entries": [], "counter": 0}) - entries = metadata["entries"] - - history = [] - for counter in entries: - history_key = self._get_history_key(file_path, counter) - content = self.cache.get(history_key) - if content is not None: - history.append(content) - - return history diff --git a/pkg/hanzo-aci/dev_aci/editor/prompts.py b/pkg/hanzo-aci/dev_aci/editor/prompts.py deleted file mode 100644 index 62b294d29..000000000 --- a/pkg/hanzo-aci/dev_aci/editor/prompts.py +++ /dev/null @@ -1,9 +0,0 @@ -CONTENT_TRUNCATED_NOTICE = "Due to the max output limit, only part of the full response has been shown to you." - -FILE_CONTENT_TRUNCATED_NOTICE: str = ( - "Due to the max output limit, only part of this file has been shown to you. You should retry this tool after you have searched inside the file with `grep -n` in order to find the line numbers of what you are looking for." -) - -DIRECTORY_CONTENT_TRUNCATED_NOTICE: str = ( - "Due to the max output limit, only part of this directory has been shown to you. You should use `ls -la` instead to view large directories incrementally." -) diff --git a/pkg/hanzo-aci/dev_aci/editor/results.py b/pkg/hanzo-aci/dev_aci/editor/results.py deleted file mode 100644 index 2fff6bac2..000000000 --- a/pkg/hanzo-aci/dev_aci/editor/results.py +++ /dev/null @@ -1,47 +0,0 @@ -from dataclasses import asdict, dataclass, fields - -from .config import MAX_RESPONSE_LEN_CHAR -from .prompts import CONTENT_TRUNCATED_NOTICE - - -@dataclass -class ToolResult: - """Represents the result of a tool execution.""" - - output: str | None = None - error: str | None = None - - def __bool__(self): - return any(getattr(self, field.name) for field in fields(self)) - - def to_dict(self, extra_field: dict | None = None) -> dict: - result = asdict(self) - - # Add extra fields if provided - if extra_field: - result.update(extra_field) - return result - - -@dataclass -class CLIResult(ToolResult): - """A ToolResult that can be rendered as a CLI output.""" - - # Optional fields for file editing commands - path: str | None = None - prev_exist: bool = True - old_content: str | None = None - new_content: str | None = None - - -def maybe_truncate( - content: str, - truncate_after: int | None = MAX_RESPONSE_LEN_CHAR, - truncate_notice: str = CONTENT_TRUNCATED_NOTICE, -) -> str: - """Truncate content and append a notice if content exceeds the specified length.""" - return ( - content - if not truncate_after or len(content) <= truncate_after - else content[:truncate_after] + truncate_notice - ) diff --git a/pkg/hanzo-aci/dev_aci/linter/__init__.py b/pkg/hanzo-aci/dev_aci/linter/__init__.py deleted file mode 100644 index 12521d9e8..000000000 --- a/pkg/hanzo-aci/dev_aci/linter/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -"""Linter module for Dev ACI. - -Part of this Linter module is adapted from Aider (Apache 2.0 License, [original code](https://github.com/paul-gauthier/aider/blob/main/aider/linter.py)). Please see the [original repository](https://github.com/paul-gauthier/aider) for more information. -""" - -from .base import LintResult -from .linter import DefaultLinter - -__all__ = ["DefaultLinter", "LintResult"] diff --git a/pkg/hanzo-aci/dev_aci/linter/base.py b/pkg/hanzo-aci/dev_aci/linter/base.py deleted file mode 100644 index 946dbf5a4..000000000 --- a/pkg/hanzo-aci/dev_aci/linter/base.py +++ /dev/null @@ -1,79 +0,0 @@ -from abc import ABC, abstractmethod - -from pydantic import BaseModel - - -class LintResult(BaseModel): - file: str - line: int # 1-indexed - column: int # 1-indexed - message: str - - def visualize(self, half_window: int = 3) -> str: - """Visualize the lint result by print out all the lines where the lint result is found. - - Args: - half_window: The number of context lines to display around the error on each side. - """ - with open(self.file, "r") as f: - file_lines = f.readlines() - - # Add line numbers - _span_size = len(str(len(file_lines))) - file_lines = [ - f"{i + 1:>{_span_size}}|{line.rstrip()}" - for i, line in enumerate(file_lines) - ] - - # Get the window of lines to display - assert self.line <= len(file_lines) and self.line > 0 - line_idx = self.line - 1 - begin_window = max(0, line_idx - half_window) - end_window = min(len(file_lines), line_idx + half_window + 1) - - selected_lines = file_lines[begin_window:end_window] - line_idx_in_window = line_idx - begin_window - - # Add character hint - _character_hint = ( - _span_size * " " - + " " * (self.column) - + "^" - + " ERROR HERE: " - + self.message - ) - selected_lines[line_idx_in_window] = ( - f"\033[91m{selected_lines[line_idx_in_window]}\033[0m" - + "\n" - + _character_hint - ) - return "\n".join(selected_lines) - - -class LinterException(Exception): - """Base class for all linter exceptions.""" - - pass - - -class BaseLinter(ABC): - """Base class for all linters. - - Each linter should be able to lint files of a specific type and return a list of (parsed) lint results. - """ - - encoding: str = "utf-8" - - @property - @abstractmethod - def supported_extensions(self) -> list[str]: - """The file extensions that this linter supports, such as .py or .tsx.""" - return [] - - @abstractmethod - def lint(self, file_path: str) -> list[LintResult]: - """Lint the given file. - - file_path: The path to the file to lint. Required to be absolute. - """ - pass diff --git a/pkg/hanzo-aci/dev_aci/linter/impl/python.py b/pkg/hanzo-aci/dev_aci/linter/impl/python.py deleted file mode 100644 index ec078a71c..000000000 --- a/pkg/hanzo-aci/dev_aci/linter/impl/python.py +++ /dev/null @@ -1,99 +0,0 @@ -from typing import List - -from dev_aci.utils.logger import oh_aci_logger as logger -from dev_aci.utils.shell import run_shell_cmd - -from ..base import BaseLinter, LintResult - - -def python_compile_lint(fname: str) -> list[LintResult]: - try: - with open(fname, "r") as f: - code = f.read() - compile(code, fname, "exec") # USE TRACEBACK BELOW HERE - return [] - except SyntaxError as err: - err_lineno = getattr(err, "end_lineno", err.lineno) - err_offset = getattr(err, "end_offset", err.offset) - if err_offset and err_offset < 0: - err_offset = err.offset - return [ - LintResult( - file=fname, line=err_lineno, column=err_offset or 1, message=err.msg - ) - ] - - -def flake_lint(filepath: str) -> list[LintResult]: - fatal = "F821,F822,F831,E112,E113,E999,E902" - flake8_cmd = f"flake8 --select={fatal} --isolated {filepath}" - - try: - cmd_outputs = run_shell_cmd(flake8_cmd, truncate_after=None)[1] - except FileNotFoundError: - return [] - results: list[LintResult] = [] - if not cmd_outputs: - return results - for line in cmd_outputs.splitlines(): - parts = line.split(":") - if len(parts) >= 4: - _msg = parts[3].strip() - if len(parts) > 4: - _msg += ": " + parts[4].strip() - - try: - line_num = int(parts[1]) - except ValueError as e: - logger.warning( - f"Error parsing flake8 output for line: {e}. Parsed parts: {parts}. Skipping..." - ) - continue - - try: - column_num = int(parts[2]) - except ValueError as e: - column_num = 1 - _msg = ( - parts[2].strip() + " " + _msg - ) # add the unparsed message to the original message - logger.warning( - f"Error parsing flake8 output for column: {e}. Parsed parts: {parts}. Using default column 1." - ) - - results.append( - LintResult( - file=filepath, - line=line_num, - column=column_num, - message=_msg, - ) - ) - return results - - -class PythonLinter(BaseLinter): - @property - def supported_extensions(self) -> List[str]: - return [".py"] - - def lint(self, file_path: str) -> list[LintResult]: - error = flake_lint(file_path) - if not error: - error = python_compile_lint(file_path) - return error - - def compile_lint(self, file_path: str, code: str) -> List[LintResult]: - try: - compile(code, file_path, "exec") - return [] - except SyntaxError as e: - return [ - LintResult( - file=file_path, - line=e.lineno, - column=e.offset, - message=str(e), - rule="SyntaxError", - ) - ] diff --git a/pkg/hanzo-aci/dev_aci/linter/impl/treesitter.py b/pkg/hanzo-aci/dev_aci/linter/impl/treesitter.py deleted file mode 100644 index c345d5cf7..000000000 --- a/pkg/hanzo-aci/dev_aci/linter/impl/treesitter.py +++ /dev/null @@ -1,74 +0,0 @@ -import warnings - -from grep_ast import TreeContext, filename_to_lang -from grep_ast.parsers import PARSERS - -from ..base import BaseLinter, LintResult -from .treesitter_compat import get_parser - -# tree_sitter is throwing a FutureWarning -warnings.simplefilter("ignore", category=FutureWarning) - - -def tree_context(fname, code, line_nums): - context = TreeContext( - fname, - code, - color=False, - line_number=True, - child_context=False, - last_line=False, - margin=0, - mark_lois=True, - loi_pad=3, - # header_max=30, - show_top_of_file_parent_scope=False, - ) - line_nums = set(line_nums) - context.add_lines_of_interest(line_nums) - context.add_context() - output = context.format() - return output - - -def traverse_tree(node): - """Traverses the tree to find errors.""" - errors = [] - if node.type == "ERROR" or node.is_missing: - line_no = node.start_point[0] + 1 - col_no = node.start_point[1] + 1 - error_type = "Missing node" if node.is_missing else "Syntax error" - errors.append((line_no, col_no, error_type)) - - for child in node.children: - errors += traverse_tree(child) - - return errors - - -class TreesitterBasicLinter(BaseLinter): - @property - def supported_extensions(self) -> list[str]: - return list(PARSERS.keys()) - - def lint(self, file_path: str) -> list[LintResult]: - """Use tree-sitter to look for syntax errors, display them with tree context.""" - lang = filename_to_lang(file_path) - if not lang: - return [] - parser = get_parser(lang) - with open(file_path, "r") as f: - code = f.read() - tree = parser.parse(bytes(code, "utf-8")) - errors = traverse_tree(tree.root_node) - if not errors: - return [] - return [ - LintResult( - file=file_path, - line=int(line), - column=int(col), - message=error_details, - ) - for line, col, error_details in errors - ] diff --git a/pkg/hanzo-aci/dev_aci/linter/impl/treesitter_compat.py b/pkg/hanzo-aci/dev_aci/linter/impl/treesitter_compat.py deleted file mode 100644 index 7dfa2460c..000000000 --- a/pkg/hanzo-aci/dev_aci/linter/impl/treesitter_compat.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Compatibility layer for tree-sitter 0.24.0.""" - -import importlib - -from tree_sitter import Language, Parser - -# Cache of loaded languages -_language_cache = {} - - -def get_parser(language): - """Get a Parser object for the given language name.""" - if language not in _language_cache: - # Try to import the language module - module_name = f"tree_sitter_{language}" - try: - module = importlib.import_module(module_name) - _language_cache[language] = Language(module.language()) - except ImportError: - raise ValueError( - f"Language {language} is not supported. Please install {module_name} package." - ) - - return Parser(_language_cache[language]) diff --git a/pkg/hanzo-aci/dev_aci/linter/linter.py b/pkg/hanzo-aci/dev_aci/linter/linter.py deleted file mode 100644 index 0ee137149..000000000 --- a/pkg/hanzo-aci/dev_aci/linter/linter.py +++ /dev/null @@ -1,122 +0,0 @@ -import os -from collections import defaultdict -from difflib import SequenceMatcher - -from ..linter.base import BaseLinter, LinterException, LintResult -from ..linter.impl.python import PythonLinter -from ..linter.impl.treesitter import TreesitterBasicLinter - - -class DefaultLinter(BaseLinter): - def __init__(self): - self.linters: dict[str, list[BaseLinter]] = defaultdict(list) - self.linters[".py"] = [PythonLinter()] - - # Add treesitter linter as a fallback for all linters - self.basic_linter = TreesitterBasicLinter() - for extension in self.basic_linter.supported_extensions: - self.linters[extension].append(self.basic_linter) - self._supported_extensions = list(self.linters.keys()) - - @property - def supported_extensions(self) -> list[str]: - return self._supported_extensions - - def lint(self, file_path: str) -> list[LintResult]: - if not os.path.isabs(file_path): - raise LinterException(f"File path {file_path} is not an absolute path") - file_extension = os.path.splitext(file_path)[1] - - linters: list[BaseLinter] = self.linters.get(file_extension, []) - for linter in linters: - res = linter.lint(file_path) - # We always return the first linter's result (higher priority) - if res: - return res - return [] - - def lint_file_diff( - self, original_file_path: str, updated_file_path: str - ) -> list[LintResult]: - """Only return lint errors that are introduced by the diff. - - Args: - original_file_path: The original file path. - updated_file_path: The updated file path. - - Returns: - A list of lint errors that are introduced by the diff. - """ - # 1. Lint the original and updated file - original_lint_errors: list[LintResult] = self.lint(original_file_path) - updated_lint_errors: list[LintResult] = self.lint(updated_file_path) - - # 2. Load the original and updated file content - with open(original_file_path, "r") as f: - old_lines = f.readlines() - with open(updated_file_path, "r") as f: - new_lines = f.readlines() - - # 3. Get line numbers that are changed & unchanged - # Map the line number of the original file to the updated file - # NOTE: this only works for lines that are not changed (i.e., equal) - old_to_new_line_no_mapping: dict[int, int] = {} - replace_or_inserted_lines: list[int] = [] - for ( - tag, - old_idx_start, - old_idx_end, - new_idx_start, - new_idx_end, - ) in SequenceMatcher( - isjunk=None, - a=old_lines, - b=new_lines, - ).get_opcodes(): - if tag == "equal": - for idx, _ in enumerate(old_lines[old_idx_start:old_idx_end]): - old_to_new_line_no_mapping[old_idx_start + idx + 1] = ( - new_idx_start + idx + 1 - ) - elif tag == "replace" or tag == "insert": - for idx, _ in enumerate(old_lines[old_idx_start:old_idx_end]): - replace_or_inserted_lines.append(new_idx_start + idx + 1) - else: - # omit the case of delete - pass - - # 4. Get pre-existing errors in unchanged lines - # increased error elsewhere introduced by the newlines - # i.e., we omit errors that are already in original files and report new one - new_line_no_to_original_errors: dict[int, list[LintResult]] = defaultdict(list) - for error in original_lint_errors: - if error.line in old_to_new_line_no_mapping: - new_line_no_to_original_errors[ - old_to_new_line_no_mapping[error.line] - ].append(error) - - # 5. Select errors from lint results in new file to report - selected_errors = [] - for error in updated_lint_errors: - # 5.1. Error introduced by replace/insert - if error.line in replace_or_inserted_lines: - selected_errors.append(error) - # 5.2. Error introduced by modified lines that impacted - # the unchanged lines that HAVE pre-existing errors - elif error.line in new_line_no_to_original_errors: - # skip if the error is already reported - # or add if the error is new - if not any( - original_error.message == error.message - and original_error.column == error.column - for original_error in new_line_no_to_original_errors[error.line] - ): - selected_errors.append(error) - # 5.3. Error introduced by modified lines that impacted - # the unchanged lines that have NO pre-existing errors - else: - selected_errors.append(error) - - # 6. Sort errors by line and column - selected_errors.sort(key=lambda x: (x.line, x.column)) - return selected_errors diff --git a/pkg/hanzo-aci/dev_aci/utils/diff.py b/pkg/hanzo-aci/dev_aci/utils/diff.py deleted file mode 100644 index a403a89f1..000000000 --- a/pkg/hanzo-aci/dev_aci/utils/diff.py +++ /dev/null @@ -1,41 +0,0 @@ -import difflib - -import whatthepatch - - -def get_diff(old_contents: str, new_contents: str, filepath: str = "file") -> str: - diff = list( - difflib.unified_diff( - old_contents.split("\n"), - new_contents.split("\n"), - fromfile=filepath, - tofile=filepath, - # do not output unchange lines - # because they can cause `parse_diff` to fail - n=0, - ) - ) - return "\n".join(map(lambda x: x.rstrip(), diff)) - - -def parse_diff(diff_patch: str) -> list[whatthepatch.patch.Change]: - # handle empty patch - if diff_patch.strip() == "": - return [] - - patch = whatthepatch.parse_patch(diff_patch) - patch_list = list(patch) - assert len(patch_list) == 1, ( - "parse_diff only supports single file diff. But got:\nPATCH:\n" - + diff_patch - + "\nPATCH LIST:\n" - + str(patch_list) - ) - changes = patch_list[0].changes - - # ignore changes that are the same (i.e., old_lineno == new_lineno) - output_changes = [] - for change in changes: - if change.old != change.new: - output_changes.append(change) - return output_changes diff --git a/pkg/hanzo-aci/dev_aci/utils/logger.py b/pkg/hanzo-aci/dev_aci/utils/logger.py deleted file mode 100644 index 7e825afc5..000000000 --- a/pkg/hanzo-aci/dev_aci/utils/logger.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging -import os - -LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO").upper() - -DEBUG = os.getenv("DEBUG", "False").lower() in ["true", "1", "yes"] -if DEBUG: - LOG_LEVEL = "DEBUG" - -oh_aci_logger = logging.getLogger("dev_aci") - -current_log_level = logging.INFO -if LOG_LEVEL in logging.getLevelNamesMapping(): - current_log_level = logging.getLevelNamesMapping()[LOG_LEVEL] - -console_handler = logging.StreamHandler() -console_handler.setLevel(current_log_level) -formatter = logging.Formatter( - "{asctime} - {name}:{levelname} - {message}", - style="{", - datefmt="%Y-%m-%d %H:%M", -) -console_handler.setFormatter(formatter) - -oh_aci_logger.setLevel(current_log_level) -oh_aci_logger.addHandler(console_handler) -oh_aci_logger.propagate = False -oh_aci_logger.debug("Logger initialized") diff --git a/pkg/hanzo-aci/dev_aci/utils/shell.py b/pkg/hanzo-aci/dev_aci/utils/shell.py deleted file mode 100644 index 49972f808..000000000 --- a/pkg/hanzo-aci/dev_aci/utils/shell.py +++ /dev/null @@ -1,67 +0,0 @@ -import os -import subprocess -import time - -from dev_aci.editor.config import MAX_RESPONSE_LEN_CHAR -from dev_aci.editor.prompts import CONTENT_TRUNCATED_NOTICE -from dev_aci.editor.results import maybe_truncate - - -def run_shell_cmd( - cmd: str, - timeout: float | None = 120.0, # seconds - truncate_after: int | None = MAX_RESPONSE_LEN_CHAR, - truncate_notice: str = CONTENT_TRUNCATED_NOTICE, -) -> tuple[int, str, str]: - """Run a shell command synchronously with a timeout. - - Args: - cmd: The shell command to run. - timeout: The maximum time to wait for the command to complete. - truncate_after: The maximum number of characters to return for stdout and stderr. - truncate_notice: The notice to append to truncated output. - - Returns: - A tuple containing the return code, stdout, and stderr. - """ - start_time = time.time() - - try: - process = subprocess.Popen( - cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True - ) - - stdout, stderr = process.communicate(timeout=timeout) - - return ( - process.returncode or 0, - maybe_truncate( - stdout, truncate_after=truncate_after, truncate_notice=truncate_notice - ), - maybe_truncate( - stderr, - truncate_after=truncate_after, - truncate_notice=CONTENT_TRUNCATED_NOTICE, - ), # Use generic notice for stderr - ) - except subprocess.TimeoutExpired: - process.kill() - elapsed_time = time.time() - start_time - raise TimeoutError( - f"Command '{cmd}' timed out after {elapsed_time:.2f} seconds" - ) - - -def check_tool_installed(tool_name: str) -> bool: - """Check if a tool is installed.""" - try: - subprocess.run( - [tool_name, "--version"], - check=True, - cwd=os.getcwd(), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - return True - except (subprocess.CalledProcessError, FileNotFoundError): - return False diff --git a/pkg/hanzo-aci/dev_config/python/.pre-commit-config.yaml b/pkg/hanzo-aci/dev_config/python/.pre-commit-config.yaml deleted file mode 100644 index e6d7f98b5..000000000 --- a/pkg/hanzo-aci/dev_config/python/.pre-commit-config.yaml +++ /dev/null @@ -1,43 +0,0 @@ -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 - hooks: - - id: trailing-whitespace - exclude: docs/modules/python - - id: end-of-file-fixer - exclude: docs/modules/python - - id: check-yaml - - id: debug-statements - - - repo: https://github.com/tox-dev/pyproject-fmt - rev: 1.7.0 - hooks: - - id: pyproject-fmt - - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.16 - hooks: - - id: validate-pyproject - - - repo: https://github.com/astral-sh/ruff-pre-commit - # Ruff version. - rev: v0.4.1 - hooks: - # Run the linter. - - id: ruff - entry: ruff check --config dev_config/python/ruff.toml - types_or: [python, pyi, jupyter] - args: [--fix] - # Run the formatter. - - id: ruff-format - entry: ruff format --config dev_config/python/ruff.toml - types_or: [python, pyi, jupyter] - - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.9.0 - hooks: - - id: mypy - additional_dependencies: - [types-requests, types-setuptools, types-pyyaml, types-toml, types-cachetools] - entry: mypy --config-file dev_config/python/mypy.ini dev_aci/ - always_run: true - pass_filenames: false diff --git a/pkg/hanzo-aci/dev_config/python/mypy.ini b/pkg/hanzo-aci/dev_config/python/mypy.ini deleted file mode 100644 index 84b97d720..000000000 --- a/pkg/hanzo-aci/dev_config/python/mypy.ini +++ /dev/null @@ -1,9 +0,0 @@ -[mypy] -warn_unused_configs = True -ignore_missing_imports = True -check_untyped_defs = True -explicit_package_bases = True -warn_unreachable = True -warn_redundant_casts = True -no_implicit_optional = True -strict_optional = True diff --git a/pkg/hanzo-aci/dev_config/python/ruff.toml b/pkg/hanzo-aci/dev_config/python/ruff.toml deleted file mode 100644 index af56e7e9d..000000000 --- a/pkg/hanzo-aci/dev_config/python/ruff.toml +++ /dev/null @@ -1,26 +0,0 @@ -[lint] -select = [ - "E", - "W", - "F", - "I", - "Q", - "B", -] - -ignore = [ - "E501", - "B003", - "B007", - "B009", - "B010", - "B904", - "B018", -] - -[lint.flake8-quotes] -docstring-quotes = "double" -inline-quotes = "single" - -[format] -quote-style = "single" diff --git a/pkg/hanzo-aci/poetry.lock b/pkg/hanzo-aci/poetry.lock deleted file mode 100644 index 05118d57d..000000000 --- a/pkg/hanzo-aci/poetry.lock +++ /dev/null @@ -1,2795 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. - -[[package]] -name = "aiohappyeyeballs" -version = "2.6.1" -description = "Happy Eyeballs for asyncio" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, - {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, -] - -[[package]] -name = "aiohttp" -version = "3.13.2" -description = "Async http client/server framework (asyncio)" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "aiohttp-3.13.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2372b15a5f62ed37789a6b383ff7344fc5b9f243999b0cd9b629d8bc5f5b4155"}, - {file = "aiohttp-3.13.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7f8659a48995edee7229522984bd1009c1213929c769c2daa80b40fe49a180c"}, - {file = "aiohttp-3.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:939ced4a7add92296b0ad38892ce62b98c619288a081170695c6babe4f50e636"}, - {file = "aiohttp-3.13.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6315fb6977f1d0dd41a107c527fee2ed5ab0550b7d885bc15fee20ccb17891da"}, - {file = "aiohttp-3.13.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6e7352512f763f760baaed2637055c49134fd1d35b37c2dedfac35bfe5cf8725"}, - {file = "aiohttp-3.13.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e09a0a06348a2dd73e7213353c90d709502d9786219f69b731f6caa0efeb46f5"}, - {file = "aiohttp-3.13.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a09a6d073fb5789456545bdee2474d14395792faa0527887f2f4ec1a486a59d3"}, - {file = "aiohttp-3.13.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b59d13c443f8e049d9e94099c7e412e34610f1f49be0f230ec656a10692a5802"}, - {file = "aiohttp-3.13.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:20db2d67985d71ca033443a1ba2001c4b5693fe09b0e29f6d9358a99d4d62a8a"}, - {file = "aiohttp-3.13.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:960c2fc686ba27b535f9fd2b52d87ecd7e4fd1cf877f6a5cba8afb5b4a8bd204"}, - {file = "aiohttp-3.13.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6c00dbcf5f0d88796151e264a8eab23de2997c9303dd7c0bf622e23b24d3ce22"}, - {file = "aiohttp-3.13.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fed38a5edb7945f4d1bcabe2fcd05db4f6ec7e0e82560088b754f7e08d93772d"}, - {file = "aiohttp-3.13.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:b395bbca716c38bef3c764f187860e88c724b342c26275bc03e906142fc5964f"}, - {file = "aiohttp-3.13.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:204ffff2426c25dfda401ba08da85f9c59525cdc42bda26660463dd1cbcfec6f"}, - {file = "aiohttp-3.13.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:05c4dd3c48fb5f15db31f57eb35374cb0c09afdde532e7fb70a75aede0ed30f6"}, - {file = "aiohttp-3.13.2-cp310-cp310-win32.whl", hash = "sha256:e574a7d61cf10351d734bcddabbe15ede0eaa8a02070d85446875dc11189a251"}, - {file = "aiohttp-3.13.2-cp310-cp310-win_amd64.whl", hash = "sha256:364f55663085d658b8462a1c3f17b2b84a5c2e1ba858e1b79bff7b2e24ad1514"}, - {file = "aiohttp-3.13.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4647d02df098f6434bafd7f32ad14942f05a9caa06c7016fdcc816f343997dd0"}, - {file = "aiohttp-3.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e3403f24bcb9c3b29113611c3c16a2a447c3953ecf86b79775e7be06f7ae7ccb"}, - {file = "aiohttp-3.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:43dff14e35aba17e3d6d5ba628858fb8cb51e30f44724a2d2f0c75be492c55e9"}, - {file = "aiohttp-3.13.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2a9ea08e8c58bb17655630198833109227dea914cd20be660f52215f6de5613"}, - {file = "aiohttp-3.13.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53b07472f235eb80e826ad038c9d106c2f653584753f3ddab907c83f49eedead"}, - {file = "aiohttp-3.13.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e736c93e9c274fce6419af4aac199984d866e55f8a4cec9114671d0ea9688780"}, - {file = "aiohttp-3.13.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ff5e771f5dcbc81c64898c597a434f7682f2259e0cd666932a913d53d1341d1a"}, - {file = "aiohttp-3.13.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3b6fb0c207cc661fa0bf8c66d8d9b657331ccc814f4719468af61034b478592"}, - {file = "aiohttp-3.13.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:97a0895a8e840ab3520e2288db7cace3a1981300d48babeb50e7425609e2e0ab"}, - {file = "aiohttp-3.13.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9e8f8afb552297aca127c90cb840e9a1d4bfd6a10d7d8f2d9176e1acc69bad30"}, - {file = "aiohttp-3.13.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ed2f9c7216e53c3df02264f25d824b079cc5914f9e2deba94155190ef648ee40"}, - {file = "aiohttp-3.13.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:99c5280a329d5fa18ef30fd10c793a190d996567667908bef8a7f81f8202b948"}, - {file = "aiohttp-3.13.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2ca6ffef405fc9c09a746cb5d019c1672cd7f402542e379afc66b370833170cf"}, - {file = "aiohttp-3.13.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:47f438b1a28e926c37632bff3c44df7d27c9b57aaf4e34b1def3c07111fdb782"}, - {file = "aiohttp-3.13.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9acda8604a57bb60544e4646a4615c1866ee6c04a8edef9b8ee6fd1d8fa2ddc8"}, - {file = "aiohttp-3.13.2-cp311-cp311-win32.whl", hash = "sha256:868e195e39b24aaa930b063c08bb0c17924899c16c672a28a65afded9c46c6ec"}, - {file = "aiohttp-3.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:7fd19df530c292542636c2a9a85854fab93474396a52f1695e799186bbd7f24c"}, - {file = "aiohttp-3.13.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b1e56bab2e12b2b9ed300218c351ee2a3d8c8fdab5b1ec6193e11a817767e47b"}, - {file = "aiohttp-3.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:364e25edaabd3d37b1db1f0cbcee8c73c9a3727bfa262b83e5e4cf3489a2a9dc"}, - {file = "aiohttp-3.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c5c94825f744694c4b8db20b71dba9a257cd2ba8e010a803042123f3a25d50d7"}, - {file = "aiohttp-3.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba2715d842ffa787be87cbfce150d5e88c87a98e0b62e0f5aa489169a393dbbb"}, - {file = "aiohttp-3.13.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:585542825c4bc662221fb257889e011a5aa00f1ae4d75d1d246a5225289183e3"}, - {file = "aiohttp-3.13.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39d02cb6025fe1aabca329c5632f48c9532a3dabccd859e7e2f110668972331f"}, - {file = "aiohttp-3.13.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e67446b19e014d37342f7195f592a2a948141d15a312fe0e700c2fd2f03124f6"}, - {file = "aiohttp-3.13.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4356474ad6333e41ccefd39eae869ba15a6c5299c9c01dfdcfdd5c107be4363e"}, - {file = "aiohttp-3.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeacf451c99b4525f700f078becff32c32ec327b10dcf31306a8a52d78166de7"}, - {file = "aiohttp-3.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8a9b889aeabd7a4e9af0b7f4ab5ad94d42e7ff679aaec6d0db21e3b639ad58d"}, - {file = "aiohttp-3.13.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fa89cb11bc71a63b69568d5b8a25c3ca25b6d54c15f907ca1c130d72f320b76b"}, - {file = "aiohttp-3.13.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8aa7c807df234f693fed0ecd507192fc97692e61fee5702cdc11155d2e5cadc8"}, - {file = "aiohttp-3.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9eb3e33fdbe43f88c3c75fa608c25e7c47bbd80f48d012763cb67c47f39a7e16"}, - {file = "aiohttp-3.13.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9434bc0d80076138ea986833156c5a48c9c7a8abb0c96039ddbb4afc93184169"}, - {file = "aiohttp-3.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ff15c147b2ad66da1f2cbb0622313f2242d8e6e8f9b79b5206c84523a4473248"}, - {file = "aiohttp-3.13.2-cp312-cp312-win32.whl", hash = "sha256:27e569eb9d9e95dbd55c0fc3ec3a9335defbf1d8bc1d20171a49f3c4c607b93e"}, - {file = "aiohttp-3.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:8709a0f05d59a71f33fd05c17fc11fcb8c30140506e13c2f5e8ee1b8964e1b45"}, - {file = "aiohttp-3.13.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7519bdc7dfc1940d201651b52bf5e03f5503bda45ad6eacf64dda98be5b2b6be"}, - {file = "aiohttp-3.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:088912a78b4d4f547a1f19c099d5a506df17eacec3c6f4375e2831ec1d995742"}, - {file = "aiohttp-3.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5276807b9de9092af38ed23ce120539ab0ac955547b38563a9ba4f5b07b95293"}, - {file = "aiohttp-3.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1237c1375eaef0db4dcd7c2559f42e8af7b87ea7d295b118c60c36a6e61cb811"}, - {file = "aiohttp-3.13.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:96581619c57419c3d7d78703d5b78c1e5e5fc0172d60f555bdebaced82ded19a"}, - {file = "aiohttp-3.13.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2713a95b47374169409d18103366de1050fe0ea73db358fc7a7acb2880422d4"}, - {file = "aiohttp-3.13.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:228a1cd556b3caca590e9511a89444925da87d35219a49ab5da0c36d2d943a6a"}, - {file = "aiohttp-3.13.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac6cde5fba8d7d8c6ac963dbb0256a9854e9fafff52fbcc58fdf819357892c3e"}, - {file = "aiohttp-3.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2bef8237544f4e42878c61cef4e2839fee6346dc60f5739f876a9c50be7fcdb"}, - {file = "aiohttp-3.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:16f15a4eac3bc2d76c45f7ebdd48a65d41b242eb6c31c2245463b40b34584ded"}, - {file = "aiohttp-3.13.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:bb7fb776645af5cc58ab804c58d7eba545a97e047254a52ce89c157b5af6cd0b"}, - {file = "aiohttp-3.13.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e1b4951125ec10c70802f2cb09736c895861cd39fd9dcb35107b4dc8ae6220b8"}, - {file = "aiohttp-3.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:550bf765101ae721ee1d37d8095f47b1f220650f85fe1af37a90ce75bab89d04"}, - {file = "aiohttp-3.13.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fe91b87fc295973096251e2d25a811388e7d8adf3bd2b97ef6ae78bc4ac6c476"}, - {file = "aiohttp-3.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0c8e31cfcc4592cb200160344b2fb6ae0f9e4effe06c644b5a125d4ae5ebe23"}, - {file = "aiohttp-3.13.2-cp313-cp313-win32.whl", hash = "sha256:0740f31a60848d6edb296a0df827473eede90c689b8f9f2a4cdde74889eb2254"}, - {file = "aiohttp-3.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:a88d13e7ca367394908f8a276b89d04a3652044612b9a408a0bb22a5ed976a1a"}, - {file = "aiohttp-3.13.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2475391c29230e063ef53a66669b7b691c9bfc3f1426a0f7bcdf1216bdbac38b"}, - {file = "aiohttp-3.13.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f33c8748abef4d8717bb20e8fb1b3e07c6adacb7fd6beaae971a764cf5f30d61"}, - {file = "aiohttp-3.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ae32f24bbfb7dbb485a24b30b1149e2f200be94777232aeadba3eecece4d0aa4"}, - {file = "aiohttp-3.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d7f02042c1f009ffb70067326ef183a047425bb2ff3bc434ead4dd4a4a66a2b"}, - {file = "aiohttp-3.13.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93655083005d71cd6c072cdab54c886e6570ad2c4592139c3fb967bfc19e4694"}, - {file = "aiohttp-3.13.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0db1e24b852f5f664cd728db140cf11ea0e82450471232a394b3d1a540b0f906"}, - {file = "aiohttp-3.13.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b009194665bcd128e23eaddef362e745601afa4641930848af4c8559e88f18f9"}, - {file = "aiohttp-3.13.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c038a8fdc8103cd51dbd986ecdce141473ffd9775a7a8057a6ed9c3653478011"}, - {file = "aiohttp-3.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66bac29b95a00db411cd758fea0e4b9bdba6d549dfe333f9a945430f5f2cc5a6"}, - {file = "aiohttp-3.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4ebf9cfc9ba24a74cf0718f04aac2a3bbe745902cc7c5ebc55c0f3b5777ef213"}, - {file = "aiohttp-3.13.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a4b88ebe35ce54205c7074f7302bd08a4cb83256a3e0870c72d6f68a3aaf8e49"}, - {file = "aiohttp-3.13.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:98c4fb90bb82b70a4ed79ca35f656f4281885be076f3f970ce315402b53099ae"}, - {file = "aiohttp-3.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:ec7534e63ae0f3759df3a1ed4fa6bc8f75082a924b590619c0dd2f76d7043caa"}, - {file = "aiohttp-3.13.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5b927cf9b935a13e33644cbed6c8c4b2d0f25b713d838743f8fe7191b33829c4"}, - {file = "aiohttp-3.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:88d6c017966a78c5265d996c19cdb79235be5e6412268d7e2ce7dee339471b7a"}, - {file = "aiohttp-3.13.2-cp314-cp314-win32.whl", hash = "sha256:f7c183e786e299b5d6c49fb43a769f8eb8e04a2726a2bd5887b98b5cc2d67940"}, - {file = "aiohttp-3.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:fe242cd381e0fb65758faf5ad96c2e460df6ee5b2de1072fe97e4127927e00b4"}, - {file = "aiohttp-3.13.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:f10d9c0b0188fe85398c61147bbd2a657d616c876863bfeff43376e0e3134673"}, - {file = "aiohttp-3.13.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:e7c952aefdf2460f4ae55c5e9c3e80aa72f706a6317e06020f80e96253b1accd"}, - {file = "aiohttp-3.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c20423ce14771d98353d2e25e83591fa75dfa90a3c1848f3d7c68243b4fbded3"}, - {file = "aiohttp-3.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e96eb1a34396e9430c19d8338d2ec33015e4a87ef2b4449db94c22412e25ccdf"}, - {file = "aiohttp-3.13.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:23fb0783bc1a33640036465019d3bba069942616a6a2353c6907d7fe1ccdaf4e"}, - {file = "aiohttp-3.13.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e1a9bea6244a1d05a4e57c295d69e159a5c50d8ef16aa390948ee873478d9a5"}, - {file = "aiohttp-3.13.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0a3d54e822688b56e9f6b5816fb3de3a3a64660efac64e4c2dc435230ad23bad"}, - {file = "aiohttp-3.13.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7a653d872afe9f33497215745da7a943d1dc15b728a9c8da1c3ac423af35178e"}, - {file = "aiohttp-3.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:56d36e80d2003fa3fc0207fac644216d8532e9504a785ef9a8fd013f84a42c61"}, - {file = "aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:78cd586d8331fb8e241c2dd6b2f4061778cc69e150514b39a9e28dd050475661"}, - {file = "aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:20b10bbfbff766294fe99987f7bb3b74fdd2f1a2905f2562132641ad434dcf98"}, - {file = "aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9ec49dff7e2b3c85cdeaa412e9d438f0ecd71676fde61ec57027dd392f00c693"}, - {file = "aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:94f05348c4406450f9d73d38efb41d669ad6cd90c7ee194810d0eefbfa875a7a"}, - {file = "aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:fa4dcb605c6f82a80c7f95713c2b11c3b8e9893b3ebd2bc9bde93165ed6107be"}, - {file = "aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf00e5db968c3f67eccd2778574cf64d8b27d95b237770aa32400bd7a1ca4f6c"}, - {file = "aiohttp-3.13.2-cp314-cp314t-win32.whl", hash = "sha256:d23b5fe492b0805a50d3371e8a728a9134d8de5447dce4c885f5587294750734"}, - {file = "aiohttp-3.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:ff0a7b0a82a7ab905cbda74006318d1b12e37c797eb1b0d4eb3e316cf47f658f"}, - {file = "aiohttp-3.13.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7fbdf5ad6084f1940ce88933de34b62358d0f4a0b6ec097362dcd3e5a65a4989"}, - {file = "aiohttp-3.13.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7c3a50345635a02db61792c85bb86daffac05330f6473d524f1a4e3ef9d0046d"}, - {file = "aiohttp-3.13.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0e87dff73f46e969af38ab3f7cb75316a7c944e2e574ff7c933bc01b10def7f5"}, - {file = "aiohttp-3.13.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2adebd4577724dcae085665f294cc57c8701ddd4d26140504db622b8d566d7aa"}, - {file = "aiohttp-3.13.2-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e036a3a645fe92309ec34b918394bb377950cbb43039a97edae6c08db64b23e2"}, - {file = "aiohttp-3.13.2-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:23ad365e30108c422d0b4428cf271156dd56790f6dd50d770b8e360e6c5ab2e6"}, - {file = "aiohttp-3.13.2-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1f9b2c2d4b9d958b1f9ae0c984ec1dd6b6689e15c75045be8ccb4011426268ca"}, - {file = "aiohttp-3.13.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a92cf4b9bea33e15ecbaa5c59921be0f23222608143d025c989924f7e3e0c07"}, - {file = "aiohttp-3.13.2-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:070599407f4954021509193404c4ac53153525a19531051661440644728ba9a7"}, - {file = "aiohttp-3.13.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:29562998ec66f988d49fb83c9b01694fa927186b781463f376c5845c121e4e0b"}, - {file = "aiohttp-3.13.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4dd3db9d0f4ebca1d887d76f7cdbcd1116ac0d05a9221b9dad82c64a62578c4d"}, - {file = "aiohttp-3.13.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d7bc4b7f9c4921eba72677cd9fedd2308f4a4ca3e12fab58935295ad9ea98700"}, - {file = "aiohttp-3.13.2-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:dacd50501cd017f8cccb328da0c90823511d70d24a323196826d923aad865901"}, - {file = "aiohttp-3.13.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:8b2f1414f6a1e0683f212ec80e813f4abef94c739fd090b66c9adf9d2a05feac"}, - {file = "aiohttp-3.13.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04c3971421576ed24c191f610052bcb2f059e395bc2489dd99e397f9bc466329"}, - {file = "aiohttp-3.13.2-cp39-cp39-win32.whl", hash = "sha256:9f377d0a924e5cc94dc620bc6366fc3e889586a7f18b748901cf016c916e2084"}, - {file = "aiohttp-3.13.2-cp39-cp39-win_amd64.whl", hash = "sha256:9c705601e16c03466cb72011bd1af55d68fa65b045356d8f96c216e5f6db0fa5"}, - {file = "aiohttp-3.13.2.tar.gz", hash = "sha256:40176a52c186aefef6eb3cad2cdd30cd06e3afbe88fe8ab2af9c0b90f228daca"}, -] - -[package.dependencies] -aiohappyeyeballs = ">=2.5.0" -aiosignal = ">=1.4.0" -attrs = ">=17.3.0" -frozenlist = ">=1.1.1" -multidict = ">=4.5,<7.0" -propcache = ">=0.2.0" -yarl = ">=1.17.0,<2.0" - -[package.extras] -speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.3.0)", "backports.zstd ; platform_python_implementation == \"CPython\" and python_version < \"3.14\"", "brotlicffi ; platform_python_implementation != \"CPython\""] - -[[package]] -name = "aiosignal" -version = "1.4.0" -description = "aiosignal: a list of registered asynchronous callbacks" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, - {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, -] - -[package.dependencies] -frozenlist = ">=1.1.0" -typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "anyio" -version = "4.9.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"}, - {file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"}, -] - -[package.dependencies] -idna = ">=2.8" -sniffio = ">=1.1" -typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} - -[package.extras] -doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""] -trio = ["trio (>=0.26.1)"] - -[[package]] -name = "attrs" -version = "25.3.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, - {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, -] - -[package.extras] -benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] - -[[package]] -name = "binaryornot" -version = "0.4.4" -description = "Ultra-lightweight pure Python package to check if a file is binary or text." -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4"}, - {file = "binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061"}, -] - -[package.dependencies] -chardet = ">=3.0.2" - -[[package]] -name = "cachetools" -version = "5.5.2" -description = "Extensible memoizing collections and decorators" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"}, - {file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"}, -] - -[[package]] -name = "certifi" -version = "2025.1.31" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, - {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, -] - -[[package]] -name = "cfgv" -version = "3.4.0" -description = "Validate configuration and produce human readable error messages." -optional = true -python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\"" -files = [ - {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, - {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, -] - -[[package]] -name = "chardet" -version = "5.2.0" -description = "Universal encoding detector for Python 3" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, - {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.1" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, - {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, - {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, -] - -[[package]] -name = "click" -version = "8.1.8" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main"] -markers = "extra == \"test\" and sys_platform == \"win32\" or platform_system == \"Windows\"" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "distlib" -version = "0.3.9" -description = "Distribution utilities" -optional = true -python-versions = "*" -groups = ["main"] -markers = "extra == \"dev\"" -files = [ - {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, - {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, -] - -[[package]] -name = "distro" -version = "1.9.0" -description = "Distro - an OS platform information API" -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, - {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, -] - -[[package]] -name = "filelock" -version = "3.20.1" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a"}, - {file = "filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c"}, -] - -[[package]] -name = "flake8" -version = "7.1.2" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.8.1" -groups = ["main"] -files = [ - {file = "flake8-7.1.2-py2.py3-none-any.whl", hash = "sha256:1cbc62e65536f65e6d754dfe6f1bada7f5cf392d6f5db3c2b85892466c3e7c1a"}, - {file = "flake8-7.1.2.tar.gz", hash = "sha256:c586ffd0b41540951ae41af572e6790dbd49fc12b3aa2541685d253d9bd504bd"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.12.0,<2.13.0" -pyflakes = ">=3.2.0,<3.3.0" - -[[package]] -name = "frozenlist" -version = "1.5.0" -description = "A list-like structure which implements collections.abc.MutableSequence" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, - {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, - {file = "frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5"}, - {file = "frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb"}, - {file = "frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, - {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, - {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, - {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, - {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, - {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, - {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e"}, - {file = "frozenlist-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723"}, - {file = "frozenlist-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c"}, - {file = "frozenlist-1.5.0-cp39-cp39-win32.whl", hash = "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3"}, - {file = "frozenlist-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0"}, - {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, - {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, -] - -[[package]] -name = "fsspec" -version = "2025.3.0" -description = "File-system specification" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "fsspec-2025.3.0-py3-none-any.whl", hash = "sha256:efb87af3efa9103f94ca91a7f8cb7a4df91af9f74fc106c9c7ea0efd7277c1b3"}, - {file = "fsspec-2025.3.0.tar.gz", hash = "sha256:a935fd1ea872591f2b5148907d103488fc523295e6c64b835cfad8c3eca44972"}, -] - -[package.extras] -abfs = ["adlfs"] -adl = ["adlfs"] -arrow = ["pyarrow (>=1)"] -dask = ["dask", "distributed"] -dev = ["pre-commit", "ruff"] -doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] -dropbox = ["dropbox", "dropboxdrivefs", "requests"] -full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] -fuse = ["fusepy"] -gcs = ["gcsfs"] -git = ["pygit2"] -github = ["requests"] -gs = ["gcsfs"] -gui = ["panel"] -hdfs = ["pyarrow (>=1)"] -http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] -libarchive = ["libarchive-c"] -oci = ["ocifs"] -s3 = ["s3fs"] -sftp = ["paramiko"] -smb = ["smbprotocol"] -ssh = ["paramiko"] -test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] -test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] -test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] -tqdm = ["tqdm"] - -[[package]] -name = "gitdb" -version = "4.0.12" -description = "Git Object Database" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf"}, - {file = "gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571"}, -] - -[package.dependencies] -smmap = ">=3.0.1,<6" - -[[package]] -name = "gitpython" -version = "3.1.44" -description = "GitPython is a Python library used to interact with Git repositories" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110"}, - {file = "gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269"}, -] - -[package.dependencies] -gitdb = ">=4.0.1,<5" - -[package.extras] -doc = ["sphinx (>=7.1.2,<7.2)", "sphinx-autodoc-typehints", "sphinx_rtd_theme"] -test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock ; python_version < \"3.8\"", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions ; python_version < \"3.11\""] - -[[package]] -name = "grep-ast" -version = "0.9.0" -description = "A tool to grep through the AST of a source file" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "grep_ast-0.9.0-py3-none-any.whl", hash = "sha256:a3973dca99f1abc026a01bbbc70e00a63860c8ff94a56182ff18b089836826d7"}, - {file = "grep_ast-0.9.0.tar.gz", hash = "sha256:620a242a4493e6721338d1c9a6c234ae651f8774f4924a6dcf90f6865d4b2ee3"}, -] - -[package.dependencies] -pathspec = "*" -tree-sitter-language-pack = "*" - -[[package]] -name = "h11" -version = "0.16.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, - {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, -] - -[[package]] -name = "httpcore" -version = "1.0.9" -description = "A minimal low-level HTTP client." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, - {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, -] - -[package.dependencies] -certifi = "*" -h11 = ">=0.16" - -[package.extras] -asyncio = ["anyio (>=4.0,<5.0)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<1.0)"] - -[[package]] -name = "httpx" -version = "0.28.1" -description = "The next generation HTTP client." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, - {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, -] - -[package.dependencies] -anyio = "*" -certifi = "*" -httpcore = "==1.*" -idna = "*" - -[package.extras] -brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] -cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "huggingface-hub" -version = "0.29.3" -description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" -optional = false -python-versions = ">=3.8.0" -groups = ["main"] -files = [ - {file = "huggingface_hub-0.29.3-py3-none-any.whl", hash = "sha256:0b25710932ac649c08cdbefa6c6ccb8e88eef82927cacdb048efb726429453aa"}, - {file = "huggingface_hub-0.29.3.tar.gz", hash = "sha256:64519a25716e0ba382ba2d3fb3ca082e7c7eb4a2fc634d200e8380006e0760e5"}, -] - -[package.dependencies] -filelock = "*" -fsspec = ">=2023.5.0" -packaging = ">=20.9" -pyyaml = ">=5.1" -requests = "*" -tqdm = ">=4.42.1" -typing-extensions = ">=3.7.4.3" - -[package.extras] -all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] -cli = ["InquirerPy (==0.3.4)"] -dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] -fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] -hf-transfer = ["hf-transfer (>=0.1.4)"] -inference = ["aiohttp"] -quality = ["libcst (==1.4.0)", "mypy (==1.5.1)", "ruff (>=0.9.0)"] -tensorflow = ["graphviz", "pydot", "tensorflow"] -tensorflow-testing = ["keras (<3.0)", "tensorflow"] -testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] -torch = ["safetensors[torch]", "torch"] -typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] - -[[package]] -name = "identify" -version = "2.6.9" -description = "File identification library for Python" -optional = true -python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\"" -files = [ - {file = "identify-2.6.9-py2.py3-none-any.whl", hash = "sha256:c98b4322da415a8e5a70ff6e51fbc2d2932c015532d77e9f8537b4ba7813b150"}, - {file = "identify-2.6.9.tar.gz", hash = "sha256:d40dfe3142a1421d8518e3d3985ef5ac42890683e32306ad614a29490abeb6bf"}, -] - -[package.extras] -license = ["ukkonen"] - -[[package]] -name = "idna" -version = "3.10" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, -] - -[package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] - -[[package]] -name = "importlib-metadata" -version = "8.6.1" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, - {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, -] - -[package.dependencies] -zipp = ">=3.20" - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["pytest-mypy"] - -[[package]] -name = "iniconfig" -version = "2.1.0" -description = "brain-dead simple config-ini parsing" -optional = true -python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"test\"" -files = [ - {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, - {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -description = "A very fast and expressive template engine." -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, - {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, -] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "jiter" -version = "0.9.0" -description = "Fast iterable JSON parser." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "jiter-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:816ec9b60fdfd1fec87da1d7ed46c66c44ffec37ab2ef7de5b147b2fce3fd5ad"}, - {file = "jiter-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b1d3086f8a3ee0194ecf2008cf81286a5c3e540d977fa038ff23576c023c0ea"}, - {file = "jiter-0.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1339f839b91ae30b37c409bf16ccd3dc453e8b8c3ed4bd1d6a567193651a4a51"}, - {file = "jiter-0.9.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ffba79584b3b670fefae66ceb3a28822365d25b7bf811e030609a3d5b876f538"}, - {file = "jiter-0.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cfc7d0a8e899089d11f065e289cb5b2daf3d82fbe028f49b20d7b809193958d"}, - {file = "jiter-0.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e00a1a2bbfaaf237e13c3d1592356eab3e9015d7efd59359ac8b51eb56390a12"}, - {file = "jiter-0.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1d9870561eb26b11448854dce0ff27a9a27cb616b632468cafc938de25e9e51"}, - {file = "jiter-0.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9872aeff3f21e437651df378cb75aeb7043e5297261222b6441a620218b58708"}, - {file = "jiter-0.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1fd19112d1049bdd47f17bfbb44a2c0001061312dcf0e72765bfa8abd4aa30e5"}, - {file = "jiter-0.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6ef5da104664e526836070e4a23b5f68dec1cc673b60bf1edb1bfbe8a55d0678"}, - {file = "jiter-0.9.0-cp310-cp310-win32.whl", hash = "sha256:cb12e6d65ebbefe5518de819f3eda53b73187b7089040b2d17f5b39001ff31c4"}, - {file = "jiter-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:c43ca669493626d8672be3b645dbb406ef25af3f4b6384cfd306da7eb2e70322"}, - {file = "jiter-0.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6c4d99c71508912a7e556d631768dcdef43648a93660670986916b297f1c54af"}, - {file = "jiter-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8f60fb8ce7df529812bf6c625635a19d27f30806885139e367af93f6e734ef58"}, - {file = "jiter-0.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51c4e1a4f8ea84d98b7b98912aa4290ac3d1eabfde8e3c34541fae30e9d1f08b"}, - {file = "jiter-0.9.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f4c677c424dc76684fea3e7285a7a2a7493424bea89ac441045e6a1fb1d7b3b"}, - {file = "jiter-0.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2221176dfec87f3470b21e6abca056e6b04ce9bff72315cb0b243ca9e835a4b5"}, - {file = "jiter-0.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3c7adb66f899ffa25e3c92bfcb593391ee1947dbdd6a9a970e0d7e713237d572"}, - {file = "jiter-0.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c98d27330fdfb77913c1097a7aab07f38ff2259048949f499c9901700789ac15"}, - {file = "jiter-0.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eda3f8cc74df66892b1d06b5d41a71670c22d95a1ca2cbab73654745ce9d0419"}, - {file = "jiter-0.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd5ab5ddc11418dce28343123644a100f487eaccf1de27a459ab36d6cca31043"}, - {file = "jiter-0.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:42f8a68a69f047b310319ef8e2f52fdb2e7976fb3313ef27df495cf77bcad965"}, - {file = "jiter-0.9.0-cp311-cp311-win32.whl", hash = "sha256:a25519efb78a42254d59326ee417d6f5161b06f5da827d94cf521fed961b1ff2"}, - {file = "jiter-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:923b54afdd697dfd00d368b7ccad008cccfeb1efb4e621f32860c75e9f25edbd"}, - {file = "jiter-0.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7b46249cfd6c48da28f89eb0be3f52d6fdb40ab88e2c66804f546674e539ec11"}, - {file = "jiter-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:609cf3c78852f1189894383cf0b0b977665f54cb38788e3e6b941fa6d982c00e"}, - {file = "jiter-0.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d726a3890a54561e55a9c5faea1f7655eda7f105bd165067575ace6e65f80bb2"}, - {file = "jiter-0.9.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2e89dc075c1fef8fa9be219e249f14040270dbc507df4215c324a1839522ea75"}, - {file = "jiter-0.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04e8ffa3c353b1bc4134f96f167a2082494351e42888dfcf06e944f2729cbe1d"}, - {file = "jiter-0.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:203f28a72a05ae0e129b3ed1f75f56bc419d5f91dfacd057519a8bd137b00c42"}, - {file = "jiter-0.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fca1a02ad60ec30bb230f65bc01f611c8608b02d269f998bc29cca8619a919dc"}, - {file = "jiter-0.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:237e5cee4d5d2659aaf91bbf8ec45052cc217d9446070699441a91b386ae27dc"}, - {file = "jiter-0.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:528b6b71745e7326eed73c53d4aa57e2a522242320b6f7d65b9c5af83cf49b6e"}, - {file = "jiter-0.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9f48e86b57bc711eb5acdfd12b6cb580a59cc9a993f6e7dcb6d8b50522dcd50d"}, - {file = "jiter-0.9.0-cp312-cp312-win32.whl", hash = "sha256:699edfde481e191d81f9cf6d2211debbfe4bd92f06410e7637dffb8dd5dfde06"}, - {file = "jiter-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:099500d07b43f61d8bd780466d429c45a7b25411b334c60ca875fa775f68ccb0"}, - {file = "jiter-0.9.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2764891d3f3e8b18dce2cff24949153ee30c9239da7c00f032511091ba688ff7"}, - {file = "jiter-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:387b22fbfd7a62418d5212b4638026d01723761c75c1c8232a8b8c37c2f1003b"}, - {file = "jiter-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d8da8629ccae3606c61d9184970423655fb4e33d03330bcdfe52d234d32f69"}, - {file = "jiter-0.9.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1be73d8982bdc278b7b9377426a4b44ceb5c7952073dd7488e4ae96b88e1103"}, - {file = "jiter-0.9.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2228eaaaa111ec54b9e89f7481bffb3972e9059301a878d085b2b449fbbde635"}, - {file = "jiter-0.9.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:11509bfecbc319459647d4ac3fd391d26fdf530dad00c13c4dadabf5b81f01a4"}, - {file = "jiter-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f22238da568be8bbd8e0650e12feeb2cfea15eda4f9fc271d3b362a4fa0604d"}, - {file = "jiter-0.9.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17f5d55eb856597607562257c8e36c42bc87f16bef52ef7129b7da11afc779f3"}, - {file = "jiter-0.9.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:6a99bed9fbb02f5bed416d137944419a69aa4c423e44189bc49718859ea83bc5"}, - {file = "jiter-0.9.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e057adb0cd1bd39606100be0eafe742de2de88c79df632955b9ab53a086b3c8d"}, - {file = "jiter-0.9.0-cp313-cp313-win32.whl", hash = "sha256:f7e6850991f3940f62d387ccfa54d1a92bd4bb9f89690b53aea36b4364bcab53"}, - {file = "jiter-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:c8ae3bf27cd1ac5e6e8b7a27487bf3ab5f82318211ec2e1346a5b058756361f7"}, - {file = "jiter-0.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0b2827fb88dda2cbecbbc3e596ef08d69bda06c6f57930aec8e79505dc17001"}, - {file = "jiter-0.9.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062b756ceb1d40b0b28f326cba26cfd575a4918415b036464a52f08632731e5a"}, - {file = "jiter-0.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6f7838bc467ab7e8ef9f387bd6de195c43bad82a569c1699cb822f6609dd4cdf"}, - {file = "jiter-0.9.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4a2d16360d0642cd68236f931b85fe50288834c383492e4279d9f1792e309571"}, - {file = "jiter-0.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e84ed1c9c9ec10bbb8c37f450077cbe3c0d4e8c2b19f0a49a60ac7ace73c7452"}, - {file = "jiter-0.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f3c848209ccd1bfa344a1240763975ca917de753c7875c77ec3034f4151d06c"}, - {file = "jiter-0.9.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7825f46e50646bee937e0f849d14ef3a417910966136f59cd1eb848b8b5bb3e4"}, - {file = "jiter-0.9.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d82a811928b26d1a6311a886b2566f68ccf2b23cf3bfed042e18686f1f22c2d7"}, - {file = "jiter-0.9.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c058ecb51763a67f019ae423b1cbe3fa90f7ee6280c31a1baa6ccc0c0e2d06e"}, - {file = "jiter-0.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9897115ad716c48f0120c1f0c4efae348ec47037319a6c63b2d7838bb53aaef4"}, - {file = "jiter-0.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:351f4c90a24c4fb8c87c6a73af2944c440494ed2bea2094feecacb75c50398ae"}, - {file = "jiter-0.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d45807b0f236c485e1e525e2ce3a854807dfe28ccf0d013dd4a563395e28008a"}, - {file = "jiter-0.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1537a890724ba00fdba21787010ac6f24dad47f763410e9e1093277913592784"}, - {file = "jiter-0.9.0-cp38-cp38-win32.whl", hash = "sha256:e3630ec20cbeaddd4b65513fa3857e1b7c4190d4481ef07fb63d0fad59033321"}, - {file = "jiter-0.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:2685f44bf80e95f8910553bf2d33b9c87bf25fceae6e9f0c1355f75d2922b0ee"}, - {file = "jiter-0.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:9ef340fae98065071ccd5805fe81c99c8f80484e820e40043689cf97fb66b3e2"}, - {file = "jiter-0.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:efb767d92c63b2cd9ec9f24feeb48f49574a713870ec87e9ba0c2c6e9329c3e2"}, - {file = "jiter-0.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:113f30f87fb1f412510c6d7ed13e91422cfd329436364a690c34c8b8bd880c42"}, - {file = "jiter-0.9.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8793b6df019b988526f5a633fdc7456ea75e4a79bd8396a3373c371fc59f5c9b"}, - {file = "jiter-0.9.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a9aaa5102dba4e079bb728076fadd5a2dca94c05c04ce68004cfd96f128ea34"}, - {file = "jiter-0.9.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d838650f6ebaf4ccadfb04522463e74a4c378d7e667e0eb1865cfe3990bfac49"}, - {file = "jiter-0.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0194f813efdf4b8865ad5f5c5f50f8566df7d770a82c51ef593d09e0b347020"}, - {file = "jiter-0.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a7954a401d0a8a0b8bc669199db78af435aae1e3569187c2939c477c53cb6a0a"}, - {file = "jiter-0.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4feafe787eb8a8d98168ab15637ca2577f6ddf77ac6c8c66242c2d028aa5420e"}, - {file = "jiter-0.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:27cd1f2e8bb377f31d3190b34e4328d280325ad7ef55c6ac9abde72f79e84d2e"}, - {file = "jiter-0.9.0-cp39-cp39-win32.whl", hash = "sha256:161d461dcbe658cf0bd0aa375b30a968b087cdddc624fc585f3867c63c6eca95"}, - {file = "jiter-0.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:e8b36d8a16a61993be33e75126ad3d8aa29cf450b09576f3c427d27647fcb4aa"}, - {file = "jiter-0.9.0.tar.gz", hash = "sha256:aadba0964deb424daa24492abc3d229c60c4a31bfee205aedbf1acc7639d7893"}, -] - -[[package]] -name = "jsonschema" -version = "4.23.0" -description = "An implementation of JSON Schema validation for Python" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, - {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -jsonschema-specifications = ">=2023.03.6" -referencing = ">=0.28.4" -rpds-py = ">=0.7.1" - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=24.6.0)"] - -[[package]] -name = "jsonschema-specifications" -version = "2024.10.1" -description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, - {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, -] - -[package.dependencies] -referencing = ">=0.31.0" - -[[package]] -name = "litellm" -version = "1.63.14" -description = "Library to easily interface with LLM API providers" -optional = false -python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" -groups = ["main"] -files = [ - {file = "litellm-1.63.14-py3-none-any.whl", hash = "sha256:d4c469f5990e142cc23dfa06c3fddd627001928e4df43682001f453af6a1fb51"}, - {file = "litellm-1.63.14.tar.gz", hash = "sha256:9cffe19d8140c33a2f777c5b2e8b8175ffe03979aac341b8538d6e6d143bd640"}, -] - -[package.dependencies] -aiohttp = "*" -click = "*" -httpx = ">=0.23.0" -importlib-metadata = ">=6.8.0" -jinja2 = ">=3.1.2,<4.0.0" -jsonschema = ">=4.22.0,<5.0.0" -openai = ">=1.66.1" -pydantic = ">=2.0.0,<3.0.0" -python-dotenv = ">=0.2.0" -tiktoken = ">=0.7.0" -tokenizers = "*" - -[package.extras] -extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "resend (>=0.8.0,<0.9.0)"] -proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "boto3 (==1.34.34)", "cryptography (>=43.0.1,<44.0.0)", "fastapi (>=0.115.5,<0.116.0)", "fastapi-sso (>=0.16.0,<0.17.0)", "gunicorn (>=23.0.0,<24.0.0)", "orjson (>=3.9.7,<4.0.0)", "pynacl (>=1.5.0,<2.0.0)", "python-multipart (>=0.0.18,<0.0.19)", "pyyaml (>=6.0.1,<7.0.0)", "rq", "uvicorn (>=0.29.0,<0.30.0)", "uvloop (>=0.21.0,<0.22.0)", "websockets (>=13.1.0,<14.0.0)"] - -[[package]] -name = "markupsafe" -version = "3.0.2" -description = "Safely add untrusted strings to HTML/XML markup." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, - {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, -] - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "multidict" -version = "6.2.0" -description = "multidict implementation" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "multidict-6.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b9f6392d98c0bd70676ae41474e2eecf4c7150cb419237a41f8f96043fcb81d1"}, - {file = "multidict-6.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3501621d5e86f1a88521ea65d5cad0a0834c77b26f193747615b7c911e5422d2"}, - {file = "multidict-6.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32ed748ff9ac682eae7859790d3044b50e3076c7d80e17a44239683769ff485e"}, - {file = "multidict-6.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc826b9a8176e686b67aa60fd6c6a7047b0461cae5591ea1dc73d28f72332a8a"}, - {file = "multidict-6.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:214207dcc7a6221d9942f23797fe89144128a71c03632bf713d918db99bd36de"}, - {file = "multidict-6.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:05fefbc3cddc4e36da209a5e49f1094bbece9a581faa7f3589201fd95df40e5d"}, - {file = "multidict-6.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e851e6363d0dbe515d8de81fd544a2c956fdec6f8a049739562286727d4a00c3"}, - {file = "multidict-6.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32c9b4878f48be3e75808ea7e499d6223b1eea6d54c487a66bc10a1871e3dc6a"}, - {file = "multidict-6.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7243c5a6523c5cfeca76e063efa5f6a656d1d74c8b1fc64b2cd1e84e507f7e2a"}, - {file = "multidict-6.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0e5a644e50ef9fb87878d4d57907f03a12410d2aa3b93b3acdf90a741df52c49"}, - {file = "multidict-6.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0dc25a3293c50744796e87048de5e68996104d86d940bb24bc3ec31df281b191"}, - {file = "multidict-6.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a49994481b99cd7dedde07f2e7e93b1d86c01c0fca1c32aded18f10695ae17eb"}, - {file = "multidict-6.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:641cf2e3447c9ecff2f7aa6e9eee9eaa286ea65d57b014543a4911ff2799d08a"}, - {file = "multidict-6.2.0-cp310-cp310-win32.whl", hash = "sha256:0c383d28857f66f5aebe3e91d6cf498da73af75fbd51cedbe1adfb85e90c0460"}, - {file = "multidict-6.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:a33273a541f1e1a8219b2a4ed2de355848ecc0254264915b9290c8d2de1c74e1"}, - {file = "multidict-6.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:84e87a7d75fa36839a3a432286d719975362d230c70ebfa0948549cc38bd5b46"}, - {file = "multidict-6.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8de4d42dffd5ced9117af2ce66ba8722402541a3aa98ffdf78dde92badb68932"}, - {file = "multidict-6.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d91a230c7f8af86c904a5a992b8c064b66330544693fd6759c3d6162382ecf"}, - {file = "multidict-6.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f6cad071960ba1914fa231677d21b1b4a3acdcce463cee41ea30bc82e6040cf"}, - {file = "multidict-6.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f74f2fc51555f4b037ef278efc29a870d327053aba5cb7d86ae572426c7cccc"}, - {file = "multidict-6.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14ed9ed1bfedd72a877807c71113deac292bf485159a29025dfdc524c326f3e1"}, - {file = "multidict-6.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ac3fcf9a2d369bd075b2c2965544036a27ccd277fc3c04f708338cc57533081"}, - {file = "multidict-6.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fc6af8e39f7496047c7876314f4317736eac82bf85b54c7c76cf1a6f8e35d98"}, - {file = "multidict-6.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f8cb1329f42fadfb40d6211e5ff568d71ab49be36e759345f91c69d1033d633"}, - {file = "multidict-6.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5389445f0173c197f4a3613713b5fb3f3879df1ded2a1a2e4bc4b5b9c5441b7e"}, - {file = "multidict-6.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:94a7bb972178a8bfc4055db80c51efd24baefaced5e51c59b0d598a004e8305d"}, - {file = "multidict-6.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da51d8928ad8b4244926fe862ba1795f0b6e68ed8c42cd2f822d435db9c2a8f4"}, - {file = "multidict-6.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:063be88bd684782a0715641de853e1e58a2f25b76388538bd62d974777ce9bc2"}, - {file = "multidict-6.2.0-cp311-cp311-win32.whl", hash = "sha256:52b05e21ff05729fbea9bc20b3a791c3c11da61649ff64cce8257c82a020466d"}, - {file = "multidict-6.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:1e2a2193d3aa5cbf5758f6d5680a52aa848e0cf611da324f71e5e48a9695cc86"}, - {file = "multidict-6.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:437c33561edb6eb504b5a30203daf81d4a9b727e167e78b0854d9a4e18e8950b"}, - {file = "multidict-6.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9f49585f4abadd2283034fc605961f40c638635bc60f5162276fec075f2e37a4"}, - {file = "multidict-6.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5dd7106d064d05896ce28c97da3f46caa442fe5a43bc26dfb258e90853b39b44"}, - {file = "multidict-6.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e25b11a0417475f093d0f0809a149aff3943c2c56da50fdf2c3c88d57fe3dfbd"}, - {file = "multidict-6.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac380cacdd3b183338ba63a144a34e9044520a6fb30c58aa14077157a033c13e"}, - {file = "multidict-6.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:61d5541f27533f803a941d3a3f8a3d10ed48c12cf918f557efcbf3cd04ef265c"}, - {file = "multidict-6.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:facaf11f21f3a4c51b62931feb13310e6fe3475f85e20d9c9fdce0d2ea561b87"}, - {file = "multidict-6.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:095a2eabe8c43041d3e6c2cb8287a257b5f1801c2d6ebd1dd877424f1e89cf29"}, - {file = "multidict-6.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0cc398350ef31167e03f3ca7c19313d4e40a662adcb98a88755e4e861170bdd"}, - {file = "multidict-6.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7c611345bbe7cb44aabb877cb94b63e86f2d0db03e382667dbd037866d44b4f8"}, - {file = "multidict-6.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8cd1a0644ccaf27e9d2f6d9c9474faabee21f0578fe85225cc5af9a61e1653df"}, - {file = "multidict-6.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:89b3857652183b8206a891168af47bac10b970d275bba1f6ee46565a758c078d"}, - {file = "multidict-6.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:125dd82b40f8c06d08d87b3510beaccb88afac94e9ed4a6f6c71362dc7dbb04b"}, - {file = "multidict-6.2.0-cp312-cp312-win32.whl", hash = "sha256:76b34c12b013d813e6cb325e6bd4f9c984db27758b16085926bbe7ceeaace626"}, - {file = "multidict-6.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:0b183a959fb88ad1be201de2c4bdf52fa8e46e6c185d76201286a97b6f5ee65c"}, - {file = "multidict-6.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5c5e7d2e300d5cb3b2693b6d60d3e8c8e7dd4ebe27cd17c9cb57020cac0acb80"}, - {file = "multidict-6.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:256d431fe4583c5f1e0f2e9c4d9c22f3a04ae96009b8cfa096da3a8723db0a16"}, - {file = "multidict-6.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a3c0ff89fe40a152e77b191b83282c9664357dce3004032d42e68c514ceff27e"}, - {file = "multidict-6.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef7d48207926edbf8b16b336f779c557dd8f5a33035a85db9c4b0febb0706817"}, - {file = "multidict-6.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3c099d3899b14e1ce52262eb82a5f5cb92157bb5106bf627b618c090a0eadc"}, - {file = "multidict-6.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e16e7297f29a544f49340012d6fc08cf14de0ab361c9eb7529f6a57a30cbfda1"}, - {file = "multidict-6.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:042028348dc5a1f2be6c666437042a98a5d24cee50380f4c0902215e5ec41844"}, - {file = "multidict-6.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:08549895e6a799bd551cf276f6e59820aa084f0f90665c0f03dd3a50db5d3c48"}, - {file = "multidict-6.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4ccfd74957ef53fa7380aaa1c961f523d582cd5e85a620880ffabd407f8202c0"}, - {file = "multidict-6.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83b78c680d4b15d33042d330c2fa31813ca3974197bddb3836a5c635a5fd013f"}, - {file = "multidict-6.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b4c153863dd6569f6511845922c53e39c8d61f6e81f228ad5443e690fca403de"}, - {file = "multidict-6.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:98aa8325c7f47183b45588af9c434533196e241be0a4e4ae2190b06d17675c02"}, - {file = "multidict-6.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9e658d1373c424457ddf6d55ec1db93c280b8579276bebd1f72f113072df8a5d"}, - {file = "multidict-6.2.0-cp313-cp313-win32.whl", hash = "sha256:3157126b028c074951839233647bd0e30df77ef1fedd801b48bdcad242a60f4e"}, - {file = "multidict-6.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:2e87f1926e91855ae61769ba3e3f7315120788c099677e0842e697b0bfb659f2"}, - {file = "multidict-6.2.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:2529ddbdaa424b2c6c2eb668ea684dd6b75b839d0ad4b21aad60c168269478d7"}, - {file = "multidict-6.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:13551d0e2d7201f0959725a6a769b6f7b9019a168ed96006479c9ac33fe4096b"}, - {file = "multidict-6.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d1996ee1330e245cd3aeda0887b4409e3930524c27642b046e4fae88ffa66c5e"}, - {file = "multidict-6.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c537da54ce4ff7c15e78ab1292e5799d0d43a2108e006578a57f531866f64025"}, - {file = "multidict-6.2.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f249badb360b0b4d694307ad40f811f83df4da8cef7b68e429e4eea939e49dd"}, - {file = "multidict-6.2.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48d39b1824b8d6ea7de878ef6226efbe0773f9c64333e1125e0efcfdd18a24c7"}, - {file = "multidict-6.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b99aac6bb2c37db336fa03a39b40ed4ef2818bf2dfb9441458165ebe88b793af"}, - {file = "multidict-6.2.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07bfa8bc649783e703263f783f73e27fef8cd37baaad4389816cf6a133141331"}, - {file = "multidict-6.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b2c00ad31fbc2cbac85d7d0fcf90853b2ca2e69d825a2d3f3edb842ef1544a2c"}, - {file = "multidict-6.2.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0d57a01a2a9fa00234aace434d8c131f0ac6e0ac6ef131eda5962d7e79edfb5b"}, - {file = "multidict-6.2.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:abf5b17bc0cf626a8a497d89ac691308dbd825d2ac372aa990b1ca114e470151"}, - {file = "multidict-6.2.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:f7716f7e7138252d88607228ce40be22660d6608d20fd365d596e7ca0738e019"}, - {file = "multidict-6.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d5a36953389f35f0a4e88dc796048829a2f467c9197265504593f0e420571547"}, - {file = "multidict-6.2.0-cp313-cp313t-win32.whl", hash = "sha256:e653d36b1bf48fa78c7fcebb5fa679342e025121ace8c87ab05c1cefd33b34fc"}, - {file = "multidict-6.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ca23db5fb195b5ef4fd1f77ce26cadefdf13dba71dab14dadd29b34d457d7c44"}, - {file = "multidict-6.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b4f3d66dd0354b79761481fc15bdafaba0b9d9076f1f42cc9ce10d7fcbda205a"}, - {file = "multidict-6.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e2a2d6749e1ff2c9c76a72c6530d5baa601205b14e441e6d98011000f47a7ac"}, - {file = "multidict-6.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cca83a629f77402cfadd58352e394d79a61c8015f1694b83ab72237ec3941f88"}, - {file = "multidict-6.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:781b5dd1db18c9e9eacc419027b0acb5073bdec9de1675c0be25ceb10e2ad133"}, - {file = "multidict-6.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cf8d370b2fea27fb300825ec3984334f7dd54a581bde6456799ba3776915a656"}, - {file = "multidict-6.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25bb96338512e2f46f615a2bb7c6012fe92a4a5ebd353e5020836a7e33120349"}, - {file = "multidict-6.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19e2819b0b468174de25c0ceed766606a07cedeab132383f1e83b9a4e96ccb4f"}, - {file = "multidict-6.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6aed763b6a1b28c46c055692836879328f0b334a6d61572ee4113a5d0c859872"}, - {file = "multidict-6.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a1133414b771619aa3c3000701c11b2e4624a7f492f12f256aedde97c28331a2"}, - {file = "multidict-6.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:639556758c36093b35e2e368ca485dada6afc2bd6a1b1207d85ea6dfc3deab27"}, - {file = "multidict-6.2.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:163f4604e76639f728d127293d24c3e208b445b463168af3d031b92b0998bb90"}, - {file = "multidict-6.2.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2325105e16d434749e1be8022f942876a936f9bece4ec41ae244e3d7fae42aaf"}, - {file = "multidict-6.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e4371591e621579cb6da8401e4ea405b33ff25a755874a3567c4075ca63d56e2"}, - {file = "multidict-6.2.0-cp39-cp39-win32.whl", hash = "sha256:d1175b0e0d6037fab207f05774a176d71210ebd40b1c51f480a04b65ec5c786d"}, - {file = "multidict-6.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:ad81012b24b88aad4c70b2cbc2dad84018783221b7f923e926f4690ff8569da3"}, - {file = "multidict-6.2.0-py3-none-any.whl", hash = "sha256:5d26547423e5e71dcc562c4acdc134b900640a39abd9066d7326a7cc2324c530"}, - {file = "multidict-6.2.0.tar.gz", hash = "sha256:0085b0afb2446e57050140240a8595846ed64d1cbd26cef936bfab3192c673b8"}, -] - -[[package]] -name = "networkx" -version = "3.4.2" -description = "Python package for creating and manipulating graphs and networks" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, - {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, -] - -[package.extras] -default = ["matplotlib (>=3.7)", "numpy (>=1.24)", "pandas (>=2.0)", "scipy (>=1.10,!=1.11.0,!=1.11.1)"] -developer = ["changelist (==0.5)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] -doc = ["intersphinx-registry", "myst-nb (>=1.1)", "numpydoc (>=1.8.0)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.15)", "sphinx (>=7.3)", "sphinx-gallery (>=0.16)", "texext (>=0.6.7)"] -example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "momepy (>=0.7.2)", "osmnx (>=1.9)", "scikit-learn (>=1.5)", "seaborn (>=0.13)"] -extra = ["lxml (>=4.6)", "pydot (>=3.0.1)", "pygraphviz (>=1.14)", "sympy (>=1.10)"] -test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] - -[[package]] -name = "nodeenv" -version = "1.9.1" -description = "Node.js virtual environment builder" -optional = true -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main"] -markers = "extra == \"dev\"" -files = [ - {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, - {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, -] - -[[package]] -name = "numpy" -version = "2.2.4" -description = "Fundamental package for array computing in Python" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "numpy-2.2.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8146f3550d627252269ac42ae660281d673eb6f8b32f113538e0cc2a9aed42b9"}, - {file = "numpy-2.2.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e642d86b8f956098b564a45e6f6ce68a22c2c97a04f5acd3f221f57b8cb850ae"}, - {file = "numpy-2.2.4-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:a84eda42bd12edc36eb5b53bbcc9b406820d3353f1994b6cfe453a33ff101775"}, - {file = "numpy-2.2.4-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:4ba5054787e89c59c593a4169830ab362ac2bee8a969249dc56e5d7d20ff8df9"}, - {file = "numpy-2.2.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7716e4a9b7af82c06a2543c53ca476fa0b57e4d760481273e09da04b74ee6ee2"}, - {file = "numpy-2.2.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf8c1d66f432ce577d0197dceaac2ac00c0759f573f28516246351c58a85020"}, - {file = "numpy-2.2.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:218f061d2faa73621fa23d6359442b0fc658d5b9a70801373625d958259eaca3"}, - {file = "numpy-2.2.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:df2f57871a96bbc1b69733cd4c51dc33bea66146b8c63cacbfed73eec0883017"}, - {file = "numpy-2.2.4-cp310-cp310-win32.whl", hash = "sha256:a0258ad1f44f138b791327961caedffbf9612bfa504ab9597157806faa95194a"}, - {file = "numpy-2.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:0d54974f9cf14acf49c60f0f7f4084b6579d24d439453d5fc5805d46a165b542"}, - {file = "numpy-2.2.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e9e0a277bb2eb5d8a7407e14688b85fd8ad628ee4e0c7930415687b6564207a4"}, - {file = "numpy-2.2.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9eeea959168ea555e556b8188da5fa7831e21d91ce031e95ce23747b7609f8a4"}, - {file = "numpy-2.2.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bd3ad3b0a40e713fc68f99ecfd07124195333f1e689387c180813f0e94309d6f"}, - {file = "numpy-2.2.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cf28633d64294969c019c6df4ff37f5698e8326db68cc2b66576a51fad634880"}, - {file = "numpy-2.2.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fa8fa7697ad1646b5c93de1719965844e004fcad23c91228aca1cf0800044a1"}, - {file = "numpy-2.2.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4162988a360a29af158aeb4a2f4f09ffed6a969c9776f8f3bdee9b06a8ab7e5"}, - {file = "numpy-2.2.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:892c10d6a73e0f14935c31229e03325a7b3093fafd6ce0af704be7f894d95687"}, - {file = "numpy-2.2.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db1f1c22173ac1c58db249ae48aa7ead29f534b9a948bc56828337aa84a32ed6"}, - {file = "numpy-2.2.4-cp311-cp311-win32.whl", hash = "sha256:ea2bb7e2ae9e37d96835b3576a4fa4b3a97592fbea8ef7c3587078b0068b8f09"}, - {file = "numpy-2.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:f7de08cbe5551911886d1ab60de58448c6df0f67d9feb7d1fb21e9875ef95e91"}, - {file = "numpy-2.2.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a7b9084668aa0f64e64bd00d27ba5146ef1c3a8835f3bd912e7a9e01326804c4"}, - {file = "numpy-2.2.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dbe512c511956b893d2dacd007d955a3f03d555ae05cfa3ff1c1ff6df8851854"}, - {file = "numpy-2.2.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:bb649f8b207ab07caebba230d851b579a3c8711a851d29efe15008e31bb4de24"}, - {file = "numpy-2.2.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:f34dc300df798742b3d06515aa2a0aee20941c13579d7a2f2e10af01ae4901ee"}, - {file = "numpy-2.2.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3f7ac96b16955634e223b579a3e5798df59007ca43e8d451a0e6a50f6bfdfba"}, - {file = "numpy-2.2.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f92084defa704deadd4e0a5ab1dc52d8ac9e8a8ef617f3fbb853e79b0ea3592"}, - {file = "numpy-2.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4e84a6283b36632e2a5b56e121961f6542ab886bc9e12f8f9818b3c266bfbb"}, - {file = "numpy-2.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:11c43995255eb4127115956495f43e9343736edb7fcdb0d973defd9de14cd84f"}, - {file = "numpy-2.2.4-cp312-cp312-win32.whl", hash = "sha256:65ef3468b53269eb5fdb3a5c09508c032b793da03251d5f8722b1194f1790c00"}, - {file = "numpy-2.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:2aad3c17ed2ff455b8eaafe06bcdae0062a1db77cb99f4b9cbb5f4ecb13c5146"}, - {file = "numpy-2.2.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cf4e5c6a278d620dee9ddeb487dc6a860f9b199eadeecc567f777daace1e9e7"}, - {file = "numpy-2.2.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1974afec0b479e50438fc3648974268f972e2d908ddb6d7fb634598cdb8260a0"}, - {file = "numpy-2.2.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:79bd5f0a02aa16808fcbc79a9a376a147cc1045f7dfe44c6e7d53fa8b8a79392"}, - {file = "numpy-2.2.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:3387dd7232804b341165cedcb90694565a6015433ee076c6754775e85d86f1fc"}, - {file = "numpy-2.2.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f527d8fdb0286fd2fd97a2a96c6be17ba4232da346931d967a0630050dfd298"}, - {file = "numpy-2.2.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bce43e386c16898b91e162e5baaad90c4b06f9dcbe36282490032cec98dc8ae7"}, - {file = "numpy-2.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31504f970f563d99f71a3512d0c01a645b692b12a63630d6aafa0939e52361e6"}, - {file = "numpy-2.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:81413336ef121a6ba746892fad881a83351ee3e1e4011f52e97fba79233611fd"}, - {file = "numpy-2.2.4-cp313-cp313-win32.whl", hash = "sha256:f486038e44caa08dbd97275a9a35a283a8f1d2f0ee60ac260a1790e76660833c"}, - {file = "numpy-2.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:207a2b8441cc8b6a2a78c9ddc64d00d20c303d79fba08c577752f080c4007ee3"}, - {file = "numpy-2.2.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8120575cb4882318c791f839a4fd66161a6fa46f3f0a5e613071aae35b5dd8f8"}, - {file = "numpy-2.2.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a761ba0fa886a7bb33c6c8f6f20213735cb19642c580a931c625ee377ee8bd39"}, - {file = "numpy-2.2.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:ac0280f1ba4a4bfff363a99a6aceed4f8e123f8a9b234c89140f5e894e452ecd"}, - {file = "numpy-2.2.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:879cf3a9a2b53a4672a168c21375166171bc3932b7e21f622201811c43cdd3b0"}, - {file = "numpy-2.2.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f05d4198c1bacc9124018109c5fba2f3201dbe7ab6e92ff100494f236209c960"}, - {file = "numpy-2.2.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f085ce2e813a50dfd0e01fbfc0c12bbe5d2063d99f8b29da30e544fb6483b8"}, - {file = "numpy-2.2.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:92bda934a791c01d6d9d8e038363c50918ef7c40601552a58ac84c9613a665bc"}, - {file = "numpy-2.2.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ee4d528022f4c5ff67332469e10efe06a267e32f4067dc76bb7e2cddf3cd25ff"}, - {file = "numpy-2.2.4-cp313-cp313t-win32.whl", hash = "sha256:05c076d531e9998e7e694c36e8b349969c56eadd2cdcd07242958489d79a7286"}, - {file = "numpy-2.2.4-cp313-cp313t-win_amd64.whl", hash = "sha256:188dcbca89834cc2e14eb2f106c96d6d46f200fe0200310fc29089657379c58d"}, - {file = "numpy-2.2.4-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7051ee569db5fbac144335e0f3b9c2337e0c8d5c9fee015f259a5bd70772b7e8"}, - {file = "numpy-2.2.4-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:ab2939cd5bec30a7430cbdb2287b63151b77cf9624de0532d629c9a1c59b1d5c"}, - {file = "numpy-2.2.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0f35b19894a9e08639fd60a1ec1978cb7f5f7f1eace62f38dd36be8aecdef4d"}, - {file = "numpy-2.2.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b4adfbbc64014976d2f91084915ca4e626fbf2057fb81af209c1a6d776d23e3d"}, - {file = "numpy-2.2.4.tar.gz", hash = "sha256:9ba03692a45d3eef66559efe1d1096c4b9b75c0986b5dff5530c378fb8331d4f"}, -] - -[[package]] -name = "openai" -version = "1.68.2" -description = "The official Python library for the openai API" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "openai-1.68.2-py3-none-any.whl", hash = "sha256:24484cb5c9a33b58576fdc5acf0e5f92603024a4e39d0b99793dfa1eb14c2b36"}, - {file = "openai-1.68.2.tar.gz", hash = "sha256:b720f0a95a1dbe1429c0d9bb62096a0d98057bcda82516f6e8af10284bdd5b19"}, -] - -[package.dependencies] -anyio = ">=3.5.0,<5" -distro = ">=1.7.0,<2" -httpx = ">=0.23.0,<1" -jiter = ">=0.4.0,<1" -pydantic = ">=1.9.0,<3" -sniffio = "*" -tqdm = ">4" -typing-extensions = ">=4.11,<5" - -[package.extras] -datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] -realtime = ["websockets (>=13,<15)"] -voice-helpers = ["numpy (>=2.0.2)", "sounddevice (>=0.5.1)"] - -[[package]] -name = "packaging" -version = "24.2" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, -] - -[[package]] -name = "pandas" -version = "2.2.3" -description = "Powerful data structures for data analysis, time series, and statistics" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, - {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, - {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, - {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, - {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, - {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, - {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, - {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, - {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, - {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, - {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, - {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, - {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, - {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, - {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, - {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, - {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, - {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, - {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, - {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, - {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, - {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, - {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, - {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, - {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, - {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, - {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, - {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, - {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, - {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, - {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, - {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, - {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, - {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, - {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, - {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, - {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, - {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, - {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, - {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, - {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, - {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, -] - -[package.dependencies] -numpy = {version = ">=1.26.0", markers = "python_version >= \"3.12\""} -python-dateutil = ">=2.8.2" -pytz = ">=2020.1" -tzdata = ">=2022.7" - -[package.extras] -all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] -aws = ["s3fs (>=2022.11.0)"] -clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] -compression = ["zstandard (>=0.19.0)"] -computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] -consortium-standard = ["dataframe-api-compat (>=0.1.7)"] -excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] -feather = ["pyarrow (>=10.0.1)"] -fss = ["fsspec (>=2022.11.0)"] -gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] -hdf5 = ["tables (>=3.8.0)"] -html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] -mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] -output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] -parquet = ["pyarrow (>=10.0.1)"] -performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] -plot = ["matplotlib (>=3.6.3)"] -postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] -pyarrow = ["pyarrow (>=10.0.1)"] -spss = ["pyreadstat (>=1.2.0)"] -sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] -test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] -xml = ["lxml (>=4.9.2)"] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "platformdirs" -version = "4.3.7" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = true -python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\"" -files = [ - {file = "platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94"}, - {file = "platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.14.1)"] - -[[package]] -name = "pluggy" -version = "1.5.0" -description = "plugin and hook calling mechanisms for python" -optional = true -python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"test\"" -files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pre-commit" -version = "4.2.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -optional = true -python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\"" -files = [ - {file = "pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd"}, - {file = "pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - -[[package]] -name = "propcache" -version = "0.3.0" -description = "Accelerated property cache" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "propcache-0.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:efa44f64c37cc30c9f05932c740a8b40ce359f51882c70883cc95feac842da4d"}, - {file = "propcache-0.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2383a17385d9800b6eb5855c2f05ee550f803878f344f58b6e194de08b96352c"}, - {file = "propcache-0.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3e7420211f5a65a54675fd860ea04173cde60a7cc20ccfbafcccd155225f8bc"}, - {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3302c5287e504d23bb0e64d2a921d1eb4a03fb93a0a0aa3b53de059f5a5d737d"}, - {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7e2e068a83552ddf7a39a99488bcba05ac13454fb205c847674da0352602082f"}, - {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d913d36bdaf368637b4f88d554fb9cb9d53d6920b9c5563846555938d5450bf"}, - {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ee1983728964d6070ab443399c476de93d5d741f71e8f6e7880a065f878e0b9"}, - {file = "propcache-0.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36ca5e9a21822cc1746023e88f5c0af6fce3af3b85d4520efb1ce4221bed75cc"}, - {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9ecde3671e62eeb99e977f5221abcf40c208f69b5eb986b061ccec317c82ebd0"}, - {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d383bf5e045d7f9d239b38e6acadd7b7fdf6c0087259a84ae3475d18e9a2ae8b"}, - {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8cb625bcb5add899cb8ba7bf716ec1d3e8f7cdea9b0713fa99eadf73b6d4986f"}, - {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5fa159dcee5dba00c1def3231c249cf261185189205073bde13797e57dd7540a"}, - {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a7080b0159ce05f179cfac592cda1a82898ca9cd097dacf8ea20ae33474fbb25"}, - {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ed7161bccab7696a473fe7ddb619c1d75963732b37da4618ba12e60899fefe4f"}, - {file = "propcache-0.3.0-cp310-cp310-win32.whl", hash = "sha256:bf0d9a171908f32d54f651648c7290397b8792f4303821c42a74e7805bfb813c"}, - {file = "propcache-0.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:42924dc0c9d73e49908e35bbdec87adedd651ea24c53c29cac103ede0ea1d340"}, - {file = "propcache-0.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9ddd49258610499aab83b4f5b61b32e11fce873586282a0e972e5ab3bcadee51"}, - {file = "propcache-0.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2578541776769b500bada3f8a4eeaf944530516b6e90c089aa368266ed70c49e"}, - {file = "propcache-0.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8074c5dd61c8a3e915fa8fc04754fa55cfa5978200d2daa1e2d4294c1f136aa"}, - {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b58229a844931bca61b3a20efd2be2a2acb4ad1622fc026504309a6883686fbf"}, - {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e45377d5d6fefe1677da2a2c07b024a6dac782088e37c0b1efea4cfe2b1be19b"}, - {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ec5060592d83454e8063e487696ac3783cc48c9a329498bafae0d972bc7816c9"}, - {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15010f29fbed80e711db272909a074dc79858c6d28e2915704cfc487a8ac89c6"}, - {file = "propcache-0.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a254537b9b696ede293bfdbc0a65200e8e4507bc9f37831e2a0318a9b333c85c"}, - {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2b975528998de037dfbc10144b8aed9b8dd5a99ec547f14d1cb7c5665a43f075"}, - {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:19d36bb351ad5554ff20f2ae75f88ce205b0748c38b146c75628577020351e3c"}, - {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6032231d4a5abd67c7f71168fd64a47b6b451fbcb91c8397c2f7610e67683810"}, - {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6985a593417cdbc94c7f9c3403747335e450c1599da1647a5af76539672464d3"}, - {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a1948df1bb1d56b5e7b0553c0fa04fd0e320997ae99689488201f19fa90d2e7"}, - {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8319293e85feadbbfe2150a5659dbc2ebc4afdeaf7d98936fb9a2f2ba0d4c35c"}, - {file = "propcache-0.3.0-cp311-cp311-win32.whl", hash = "sha256:63f26258a163c34542c24808f03d734b338da66ba91f410a703e505c8485791d"}, - {file = "propcache-0.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:cacea77ef7a2195f04f9279297684955e3d1ae4241092ff0cfcef532bb7a1c32"}, - {file = "propcache-0.3.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e53d19c2bf7d0d1e6998a7e693c7e87300dd971808e6618964621ccd0e01fe4e"}, - {file = "propcache-0.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a61a68d630e812b67b5bf097ab84e2cd79b48c792857dc10ba8a223f5b06a2af"}, - {file = "propcache-0.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fb91d20fa2d3b13deea98a690534697742029f4fb83673a3501ae6e3746508b5"}, - {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67054e47c01b7b349b94ed0840ccae075449503cf1fdd0a1fdd98ab5ddc2667b"}, - {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997e7b8f173a391987df40f3b52c423e5850be6f6df0dcfb5376365440b56667"}, - {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d663fd71491dde7dfdfc899d13a067a94198e90695b4321084c6e450743b8c7"}, - {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8884ba1a0fe7210b775106b25850f5e5a9dc3c840d1ae9924ee6ea2eb3acbfe7"}, - {file = "propcache-0.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa806bbc13eac1ab6291ed21ecd2dd426063ca5417dd507e6be58de20e58dfcf"}, - {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6f4d7a7c0aff92e8354cceca6fe223973ddf08401047920df0fcb24be2bd5138"}, - {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9be90eebc9842a93ef8335291f57b3b7488ac24f70df96a6034a13cb58e6ff86"}, - {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bf15fc0b45914d9d1b706f7c9c4f66f2b7b053e9517e40123e137e8ca8958b3d"}, - {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5a16167118677d94bb48bfcd91e420088854eb0737b76ec374b91498fb77a70e"}, - {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:41de3da5458edd5678b0f6ff66691507f9885f5fe6a0fb99a5d10d10c0fd2d64"}, - {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:728af36011bb5d344c4fe4af79cfe186729efb649d2f8b395d1572fb088a996c"}, - {file = "propcache-0.3.0-cp312-cp312-win32.whl", hash = "sha256:6b5b7fd6ee7b54e01759f2044f936dcf7dea6e7585f35490f7ca0420fe723c0d"}, - {file = "propcache-0.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:2d15bc27163cd4df433e75f546b9ac31c1ba7b0b128bfb1b90df19082466ff57"}, - {file = "propcache-0.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a2b9bf8c79b660d0ca1ad95e587818c30ccdb11f787657458d6f26a1ea18c568"}, - {file = "propcache-0.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b0c1a133d42c6fc1f5fbcf5c91331657a1ff822e87989bf4a6e2e39b818d0ee9"}, - {file = "propcache-0.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bb2f144c6d98bb5cbc94adeb0447cfd4c0f991341baa68eee3f3b0c9c0e83767"}, - {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1323cd04d6e92150bcc79d0174ce347ed4b349d748b9358fd2e497b121e03c8"}, - {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b812b3cb6caacd072276ac0492d249f210006c57726b6484a1e1805b3cfeea0"}, - {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:742840d1d0438eb7ea4280f3347598f507a199a35a08294afdcc560c3739989d"}, - {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c6e7e4f9167fddc438cd653d826f2222222564daed4116a02a184b464d3ef05"}, - {file = "propcache-0.3.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a94ffc66738da99232ddffcf7910e0f69e2bbe3a0802e54426dbf0714e1c2ffe"}, - {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c6ec957025bf32b15cbc6b67afe233c65b30005e4c55fe5768e4bb518d712f1"}, - {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:549722908de62aa0b47a78b90531c022fa6e139f9166be634f667ff45632cc92"}, - {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5d62c4f6706bff5d8a52fd51fec6069bef69e7202ed481486c0bc3874912c787"}, - {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:24c04f8fbf60094c531667b8207acbae54146661657a1b1be6d3ca7773b7a545"}, - {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7c5f5290799a3f6539cc5e6f474c3e5c5fbeba74a5e1e5be75587746a940d51e"}, - {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4fa0e7c9c3cf7c276d4f6ab9af8adddc127d04e0fcabede315904d2ff76db626"}, - {file = "propcache-0.3.0-cp313-cp313-win32.whl", hash = "sha256:ee0bd3a7b2e184e88d25c9baa6a9dc609ba25b76daae942edfb14499ac7ec374"}, - {file = "propcache-0.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1c8f7d896a16da9455f882870a507567d4f58c53504dc2d4b1e1d386dfe4588a"}, - {file = "propcache-0.3.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e560fd75aaf3e5693b91bcaddd8b314f4d57e99aef8a6c6dc692f935cc1e6bbf"}, - {file = "propcache-0.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:65a37714b8ad9aba5780325228598a5b16c47ba0f8aeb3dc0514701e4413d7c0"}, - {file = "propcache-0.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:07700939b2cbd67bfb3b76a12e1412405d71019df00ca5697ce75e5ef789d829"}, - {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c0fdbdf6983526e269e5a8d53b7ae3622dd6998468821d660d0daf72779aefa"}, - {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:794c3dd744fad478b6232289c866c25406ecdfc47e294618bdf1697e69bd64a6"}, - {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4544699674faf66fb6b4473a1518ae4999c1b614f0b8297b1cef96bac25381db"}, - {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fddb8870bdb83456a489ab67c6b3040a8d5a55069aa6f72f9d872235fbc52f54"}, - {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f857034dc68d5ceb30fb60afb6ff2103087aea10a01b613985610e007053a121"}, - {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:02df07041e0820cacc8f739510078f2aadcfd3fc57eaeeb16d5ded85c872c89e"}, - {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f47d52fd9b2ac418c4890aad2f6d21a6b96183c98021f0a48497a904199f006e"}, - {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9ff4e9ecb6e4b363430edf2c6e50173a63e0820e549918adef70515f87ced19a"}, - {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ecc2920630283e0783c22e2ac94427f8cca29a04cfdf331467d4f661f4072dac"}, - {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:c441c841e82c5ba7a85ad25986014be8d7849c3cfbdb6004541873505929a74e"}, - {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6c929916cbdb540d3407c66f19f73387f43e7c12fa318a66f64ac99da601bcdf"}, - {file = "propcache-0.3.0-cp313-cp313t-win32.whl", hash = "sha256:0c3e893c4464ebd751b44ae76c12c5f5c1e4f6cbd6fbf67e3783cd93ad221863"}, - {file = "propcache-0.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:75e872573220d1ee2305b35c9813626e620768248425f58798413e9c39741f46"}, - {file = "propcache-0.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:03c091bb752349402f23ee43bb2bff6bd80ccab7c9df6b88ad4322258d6960fc"}, - {file = "propcache-0.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:46ed02532cb66612d42ae5c3929b5e98ae330ea0f3900bc66ec5f4862069519b"}, - {file = "propcache-0.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11ae6a8a01b8a4dc79093b5d3ca2c8a4436f5ee251a9840d7790dccbd96cb649"}, - {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df03cd88f95b1b99052b52b1bb92173229d7a674df0ab06d2b25765ee8404bce"}, - {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03acd9ff19021bd0567582ac88f821b66883e158274183b9e5586f678984f8fe"}, - {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd54895e4ae7d32f1e3dd91261df46ee7483a735017dc6f987904f194aa5fd14"}, - {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26a67e5c04e3119594d8cfae517f4b9330c395df07ea65eab16f3d559b7068fe"}, - {file = "propcache-0.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee25f1ac091def37c4b59d192bbe3a206298feeb89132a470325bf76ad122a1e"}, - {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58e6d2a5a7cb3e5f166fd58e71e9a4ff504be9dc61b88167e75f835da5764d07"}, - {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:be90c94570840939fecedf99fa72839aed70b0ced449b415c85e01ae67422c90"}, - {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:49ea05212a529c2caffe411e25a59308b07d6e10bf2505d77da72891f9a05641"}, - {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:119e244ab40f70a98c91906d4c1f4c5f2e68bd0b14e7ab0a06922038fae8a20f"}, - {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:507c5357a8d8b4593b97fb669c50598f4e6cccbbf77e22fa9598aba78292b4d7"}, - {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8526b0941ec5a40220fc4dfde76aed58808e2b309c03e9fa8e2260083ef7157f"}, - {file = "propcache-0.3.0-cp39-cp39-win32.whl", hash = "sha256:7cedd25e5f678f7738da38037435b340694ab34d424938041aa630d8bac42663"}, - {file = "propcache-0.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:bf4298f366ca7e1ad1d21bbb58300a6985015909964077afd37559084590c929"}, - {file = "propcache-0.3.0-py3-none-any.whl", hash = "sha256:67dda3c7325691c2081510e92c561f465ba61b975f481735aefdfc845d2cd043"}, - {file = "propcache-0.3.0.tar.gz", hash = "sha256:a8fd93de4e1d278046345f49e2238cdb298589325849b2645d4a94c53faeffc5"}, -] - -[[package]] -name = "psutil" -version = "5.9.8" -description = "Cross-platform lib for process and system monitoring in Python." -optional = true -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -groups = ["main"] -markers = "extra == \"test\"" -files = [ - {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, - {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, - {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, - {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, - {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, - {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, - {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, - {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, - {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, - {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, - {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, - {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, - {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, - {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, -] - -[package.extras] -test = ["enum34 ; python_version <= \"3.4\"", "ipaddress ; python_version < \"3.0\"", "mock ; python_version < \"3.0\"", "pywin32 ; sys_platform == \"win32\"", "wmi ; sys_platform == \"win32\""] - -[[package]] -name = "pycodestyle" -version = "2.12.1" -description = "Python style guide checker" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, - {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, -] - -[[package]] -name = "pydantic" -version = "2.10.6" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, - {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.27.2" -typing-extensions = ">=4.12.2" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.27.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, - {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "pyflakes" -version = "3.2.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, - {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, -] - -[[package]] -name = "pytest" -version = "8.3.5" -description = "pytest: simple powerful testing with Python" -optional = true -python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"test\"" -files = [ - {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, - {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=1.5,<2" - -[package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main"] -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "python-dotenv" -version = "1.0.1" -description = "Read key-value pairs from a .env file and set them as environment variables" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, - {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -name = "pytz" -version = "2025.1" -description = "World timezone definitions, modern and historical" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, - {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, -] - -[[package]] -name = "pyyaml" -version = "6.0.2" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, - {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, - {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, - {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, - {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, -] - -[[package]] -name = "referencing" -version = "0.36.2" -description = "JSON Referencing + Python" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, - {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -rpds-py = ">=0.7.0" -typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""} - -[[package]] -name = "regex" -version = "2024.11.6" -description = "Alternative regular expression module, to replace re." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, - {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, - {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, - {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, - {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, - {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, - {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, - {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, - {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, - {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, - {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, - {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, - {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, - {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, - {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, - {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, -] - -[[package]] -name = "requests" -version = "2.32.5" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, - {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset_normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "rpds-py" -version = "0.23.1" -description = "Python bindings to Rust's persistent data structures (rpds)" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "rpds_py-0.23.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2a54027554ce9b129fc3d633c92fa33b30de9f08bc61b32c053dc9b537266fed"}, - {file = "rpds_py-0.23.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b5ef909a37e9738d146519657a1aab4584018746a18f71c692f2f22168ece40c"}, - {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ee9d6f0b38efb22ad94c3b68ffebe4c47865cdf4b17f6806d6c674e1feb4246"}, - {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f7356a6da0562190558c4fcc14f0281db191cdf4cb96e7604c06acfcee96df15"}, - {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9441af1d25aed96901f97ad83d5c3e35e6cd21a25ca5e4916c82d7dd0490a4fa"}, - {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d8abf7896a91fb97e7977d1aadfcc2c80415d6dc2f1d0fca5b8d0df247248f3"}, - {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b08027489ba8fedde72ddd233a5ea411b85a6ed78175f40285bd401bde7466d"}, - {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fee513135b5a58f3bb6d89e48326cd5aa308e4bcdf2f7d59f67c861ada482bf8"}, - {file = "rpds_py-0.23.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:35d5631ce0af26318dba0ae0ac941c534453e42f569011585cb323b7774502a5"}, - {file = "rpds_py-0.23.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a20cb698c4a59c534c6701b1c24a968ff2768b18ea2991f886bd8985ce17a89f"}, - {file = "rpds_py-0.23.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e9c206a1abc27e0588cf8b7c8246e51f1a16a103734f7750830a1ccb63f557a"}, - {file = "rpds_py-0.23.1-cp310-cp310-win32.whl", hash = "sha256:d9f75a06ecc68f159d5d7603b734e1ff6daa9497a929150f794013aa9f6e3f12"}, - {file = "rpds_py-0.23.1-cp310-cp310-win_amd64.whl", hash = "sha256:f35eff113ad430b5272bbfc18ba111c66ff525828f24898b4e146eb479a2cdda"}, - {file = "rpds_py-0.23.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b79f5ced71efd70414a9a80bbbfaa7160da307723166f09b69773153bf17c590"}, - {file = "rpds_py-0.23.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c9e799dac1ffbe7b10c1fd42fe4cd51371a549c6e108249bde9cd1200e8f59b4"}, - {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721f9c4011b443b6e84505fc00cc7aadc9d1743f1c988e4c89353e19c4a968ee"}, - {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f88626e3f5e57432e6191cd0c5d6d6b319b635e70b40be2ffba713053e5147dd"}, - {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:285019078537949cecd0190f3690a0b0125ff743d6a53dfeb7a4e6787af154f5"}, - {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92f5654157de1379c509b15acec9d12ecf6e3bc1996571b6cb82a4302060447"}, - {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e768267cbe051dd8d1c5305ba690bb153204a09bf2e3de3ae530de955f5b5580"}, - {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c5334a71f7dc1160382d45997e29f2637c02f8a26af41073189d79b95d3321f1"}, - {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6adb81564af0cd428910f83fa7da46ce9ad47c56c0b22b50872bc4515d91966"}, - {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cafa48f2133d4daa028473ede7d81cd1b9f9e6925e9e4003ebdf77010ee02f35"}, - {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fced9fd4a07a1ded1bac7e961ddd9753dd5d8b755ba8e05acba54a21f5f1522"}, - {file = "rpds_py-0.23.1-cp311-cp311-win32.whl", hash = "sha256:243241c95174b5fb7204c04595852fe3943cc41f47aa14c3828bc18cd9d3b2d6"}, - {file = "rpds_py-0.23.1-cp311-cp311-win_amd64.whl", hash = "sha256:11dd60b2ffddba85715d8a66bb39b95ddbe389ad2cfcf42c833f1bcde0878eaf"}, - {file = "rpds_py-0.23.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3902df19540e9af4cc0c3ae75974c65d2c156b9257e91f5101a51f99136d834c"}, - {file = "rpds_py-0.23.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66f8d2a17e5838dd6fb9be6baaba8e75ae2f5fa6b6b755d597184bfcd3cb0eba"}, - {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:112b8774b0b4ee22368fec42749b94366bd9b536f8f74c3d4175d4395f5cbd31"}, - {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0df046f2266e8586cf09d00588302a32923eb6386ced0ca5c9deade6af9a149"}, - {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3288930b947cbebe767f84cf618d2cbe0b13be476e749da0e6a009f986248c"}, - {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce473a2351c018b06dd8d30d5da8ab5a0831056cc53b2006e2a8028172c37ce5"}, - {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d550d7e9e7d8676b183b37d65b5cd8de13676a738973d330b59dc8312df9c5dc"}, - {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e14f86b871ea74c3fddc9a40e947d6a5d09def5adc2076ee61fb910a9014fb35"}, - {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf5be5ba34e19be579ae873da515a2836a2166d8d7ee43be6ff909eda42b72b"}, - {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7031d493c4465dbc8d40bd6cafefef4bd472b17db0ab94c53e7909ee781b9ef"}, - {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55ff4151cfd4bc635e51cfb1c59ac9f7196b256b12e3a57deb9e5742e65941ad"}, - {file = "rpds_py-0.23.1-cp312-cp312-win32.whl", hash = "sha256:a9d3b728f5a5873d84cba997b9d617c6090ca5721caaa691f3b1a78c60adc057"}, - {file = "rpds_py-0.23.1-cp312-cp312-win_amd64.whl", hash = "sha256:b03a8d50b137ee758e4c73638b10747b7c39988eb8e6cd11abb7084266455165"}, - {file = "rpds_py-0.23.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:4caafd1a22e5eaa3732acb7672a497123354bef79a9d7ceed43387d25025e935"}, - {file = "rpds_py-0.23.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:178f8a60fc24511c0eb756af741c476b87b610dba83270fce1e5a430204566a4"}, - {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c632419c3870507ca20a37c8f8f5352317aca097639e524ad129f58c125c61c6"}, - {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:698a79d295626ee292d1730bc2ef6e70a3ab135b1d79ada8fde3ed0047b65a10"}, - {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:271fa2184cf28bdded86bb6217c8e08d3a169fe0bbe9be5e8d96e8476b707122"}, - {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b91cceb5add79ee563bd1f70b30896bd63bc5f78a11c1f00a1e931729ca4f1f4"}, - {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a6cb95074777f1ecda2ca4fa7717caa9ee6e534f42b7575a8f0d4cb0c24013"}, - {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:50fb62f8d8364978478b12d5f03bf028c6bc2af04082479299139dc26edf4c64"}, - {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8f7e90b948dc9dcfff8003f1ea3af08b29c062f681c05fd798e36daa3f7e3e8"}, - {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5b98b6c953e5c2bda51ab4d5b4f172617d462eebc7f4bfdc7c7e6b423f6da957"}, - {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2893d778d4671ee627bac4037a075168b2673c57186fb1a57e993465dbd79a93"}, - {file = "rpds_py-0.23.1-cp313-cp313-win32.whl", hash = "sha256:2cfa07c346a7ad07019c33fb9a63cf3acb1f5363c33bc73014e20d9fe8b01cdd"}, - {file = "rpds_py-0.23.1-cp313-cp313-win_amd64.whl", hash = "sha256:3aaf141d39f45322e44fc2c742e4b8b4098ead5317e5f884770c8df0c332da70"}, - {file = "rpds_py-0.23.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:759462b2d0aa5a04be5b3e37fb8183615f47014ae6b116e17036b131985cb731"}, - {file = "rpds_py-0.23.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3e9212f52074fc9d72cf242a84063787ab8e21e0950d4d6709886fb62bcb91d5"}, - {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e9f3a3ac919406bc0414bbbd76c6af99253c507150191ea79fab42fdb35982a"}, - {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c04ca91dda8a61584165825907f5c967ca09e9c65fe8966ee753a3f2b019fe1e"}, - {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ab923167cfd945abb9b51a407407cf19f5bee35001221f2911dc85ffd35ff4f"}, - {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed6f011bedca8585787e5082cce081bac3d30f54520097b2411351b3574e1219"}, - {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6959bb9928c5c999aba4a3f5a6799d571ddc2c59ff49917ecf55be2bbb4e3722"}, - {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ed7de3c86721b4e83ac440751329ec6a1102229aa18163f84c75b06b525ad7e"}, - {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5fb89edee2fa237584e532fbf78f0ddd1e49a47c7c8cfa153ab4849dc72a35e6"}, - {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7e5413d2e2d86025e73f05510ad23dad5950ab8417b7fc6beaad99be8077138b"}, - {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d31ed4987d72aabdf521eddfb6a72988703c091cfc0064330b9e5f8d6a042ff5"}, - {file = "rpds_py-0.23.1-cp313-cp313t-win32.whl", hash = "sha256:f3429fb8e15b20961efca8c8b21432623d85db2228cc73fe22756c6637aa39e7"}, - {file = "rpds_py-0.23.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d6f6512a90bd5cd9030a6237f5346f046c6f0e40af98657568fa45695d4de59d"}, - {file = "rpds_py-0.23.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:09cd7dbcb673eb60518231e02874df66ec1296c01a4fcd733875755c02014b19"}, - {file = "rpds_py-0.23.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c6760211eee3a76316cf328f5a8bd695b47b1626d21c8a27fb3b2473a884d597"}, - {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72e680c1518733b73c994361e4b06441b92e973ef7d9449feec72e8ee4f713da"}, - {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae28144c1daa61366205d32abd8c90372790ff79fc60c1a8ad7fd3c8553a600e"}, - {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c698d123ce5d8f2d0cd17f73336615f6a2e3bdcedac07a1291bb4d8e7d82a05a"}, - {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98b257ae1e83f81fb947a363a274c4eb66640212516becaff7bef09a5dceacaa"}, - {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c9ff044eb07c8468594d12602291c635da292308c8c619244e30698e7fc455a"}, - {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7938c7b0599a05246d704b3f5e01be91a93b411d0d6cc62275f025293b8a11ce"}, - {file = "rpds_py-0.23.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e9cb79ecedfc156c0692257ac7ed415243b6c35dd969baa461a6888fc79f2f07"}, - {file = "rpds_py-0.23.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7b77e07233925bd33fc0022b8537774423e4c6680b6436316c5075e79b6384f4"}, - {file = "rpds_py-0.23.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a970bfaf130c29a679b1d0a6e0f867483cea455ab1535fb427566a475078f27f"}, - {file = "rpds_py-0.23.1-cp39-cp39-win32.whl", hash = "sha256:4233df01a250b3984465faed12ad472f035b7cd5240ea3f7c76b7a7016084495"}, - {file = "rpds_py-0.23.1-cp39-cp39-win_amd64.whl", hash = "sha256:c617d7453a80e29d9973b926983b1e700a9377dbe021faa36041c78537d7b08c"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c1f8afa346ccd59e4e5630d5abb67aba6a9812fddf764fd7eb11f382a345f8cc"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fad784a31869747df4ac968a351e070c06ca377549e4ace94775aaa3ab33ee06"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5a96fcac2f18e5a0a23a75cd27ce2656c66c11c127b0318e508aab436b77428"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3e77febf227a1dc3220159355dba68faa13f8dca9335d97504abf428469fb18b"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26bb3e8de93443d55e2e748e9fd87deb5f8075ca7bc0502cfc8be8687d69a2ec"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db7707dde9143a67b8812c7e66aeb2d843fe33cc8e374170f4d2c50bd8f2472d"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1eedaaccc9bb66581d4ae7c50e15856e335e57ef2734dbc5fd8ba3e2a4ab3cb6"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28358c54fffadf0ae893f6c1050e8f8853e45df22483b7fff2f6ab6152f5d8bf"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:633462ef7e61d839171bf206551d5ab42b30b71cac8f10a64a662536e057fdef"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a98f510d86f689fcb486dc59e6e363af04151e5260ad1bdddb5625c10f1e95f8"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e0397dd0b3955c61ef9b22838144aa4bef6f0796ba5cc8edfc64d468b93798b4"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:75307599f0d25bf6937248e5ac4e3bde5ea72ae6618623b86146ccc7845ed00b"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3614d280bf7aab0d3721b5ce0e73434acb90a2c993121b6e81a1c15c665298ac"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e5963ea87f88bddf7edd59644a35a0feecf75f8985430124c253612d4f7d27ae"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad76f44f70aac3a54ceb1813ca630c53415da3a24fd93c570b2dfb4856591017"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c6ae11e6e93728d86aafc51ced98b1658a0080a7dd9417d24bfb955bb09c3c2"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc869af5cba24d45fb0399b0cfdbcefcf6910bf4dee5d74036a57cf5264b3ff4"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c76b32eb2ab650a29e423525e84eb197c45504b1c1e6e17b6cc91fcfeb1a4b1d"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4263320ed887ed843f85beba67f8b2d1483b5947f2dc73a8b068924558bfeace"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7f9682a8f71acdf59fd554b82b1c12f517118ee72c0f3944eda461606dfe7eb9"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:754fba3084b70162a6b91efceee8a3f06b19e43dac3f71841662053c0584209a"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:a1c66e71ecfd2a4acf0e4bd75e7a3605afa8f9b28a3b497e4ba962719df2be57"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8d67beb6002441faef8251c45e24994de32c4c8686f7356a1f601ad7c466f7c3"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a1e17d8dc8e57d8e0fd21f8f0f0a5211b3fa258b2e444c2053471ef93fe25a00"}, - {file = "rpds_py-0.23.1.tar.gz", hash = "sha256:7f3240dcfa14d198dba24b8b9cb3b108c06b68d45b7babd9eefc1038fdf7e707"}, -] - -[[package]] -name = "ruff" -version = "0.7.4" -description = "An extremely fast Python linter and code formatter, written in Rust." -optional = true -python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"dev\"" -files = [ - {file = "ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478"}, - {file = "ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63"}, - {file = "ruff-0.7.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:63a569b36bc66fbadec5beaa539dd81e0527cb258b94e29e0531ce41bacc1f20"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d06218747d361d06fd2fdac734e7fa92df36df93035db3dc2ad7aa9852cb109"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0cea28d0944f74ebc33e9f934238f15c758841f9f5edd180b5315c203293452"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80094ecd4793c68b2571b128f91754d60f692d64bc0d7272ec9197fdd09bf9ea"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:997512325c6620d1c4c2b15db49ef59543ef9cd0f4aa8065ec2ae5103cedc7e7"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00b4cf3a6b5fad6d1a66e7574d78956bbd09abfd6c8a997798f01f5da3d46a05"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7dbdc7d8274e1422722933d1edddfdc65b4336abf0b16dfcb9dedd6e6a517d06"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e92dfb5f00eaedb1501b2f906ccabfd67b2355bdf117fea9719fc99ac2145bc"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3bd726099f277d735dc38900b6a8d6cf070f80828877941983a57bca1cd92172"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2e32829c429dd081ee5ba39aef436603e5b22335c3d3fff013cd585806a6486a"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:662a63b4971807623f6f90c1fb664613f67cc182dc4d991471c23c541fee62dd"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:876f5e09eaae3eb76814c1d3b68879891d6fde4824c015d48e7a7da4cf066a3a"}, - {file = "ruff-0.7.4-py3-none-win32.whl", hash = "sha256:75c53f54904be42dd52a548728a5b572344b50d9b2873d13a3f8c5e3b91f5cac"}, - {file = "ruff-0.7.4-py3-none-win_amd64.whl", hash = "sha256:745775c7b39f914238ed1f1b0bebed0b9155a17cd8bc0b08d3c87e4703b990d6"}, - {file = "ruff-0.7.4-py3-none-win_arm64.whl", hash = "sha256:11bff065102c3ae9d3ea4dc9ecdfe5a5171349cdd0787c1fc64761212fc9cf1f"}, - {file = "ruff-0.7.4.tar.gz", hash = "sha256:cd12e35031f5af6b9b93715d8c4f40360070b2041f81273d0527683d5708fce2"}, -] - -[[package]] -name = "scipy" -version = "1.15.2" -description = "Fundamental algorithms for scientific computing in Python" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "scipy-1.15.2-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a2ec871edaa863e8213ea5df811cd600734f6400b4af272e1c011e69401218e9"}, - {file = "scipy-1.15.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:6f223753c6ea76983af380787611ae1291e3ceb23917393079dcc746ba60cfb5"}, - {file = "scipy-1.15.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:ecf797d2d798cf7c838c6d98321061eb3e72a74710e6c40540f0e8087e3b499e"}, - {file = "scipy-1.15.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:9b18aa747da280664642997e65aab1dd19d0c3d17068a04b3fe34e2559196cb9"}, - {file = "scipy-1.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87994da02e73549dfecaed9e09a4f9d58a045a053865679aeb8d6d43747d4df3"}, - {file = "scipy-1.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69ea6e56d00977f355c0f84eba69877b6df084516c602d93a33812aa04d90a3d"}, - {file = "scipy-1.15.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:888307125ea0c4466287191e5606a2c910963405ce9671448ff9c81c53f85f58"}, - {file = "scipy-1.15.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9412f5e408b397ff5641080ed1e798623dbe1ec0d78e72c9eca8992976fa65aa"}, - {file = "scipy-1.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:b5e025e903b4f166ea03b109bb241355b9c42c279ea694d8864d033727205e65"}, - {file = "scipy-1.15.2-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:92233b2df6938147be6fa8824b8136f29a18f016ecde986666be5f4d686a91a4"}, - {file = "scipy-1.15.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:62ca1ff3eb513e09ed17a5736929429189adf16d2d740f44e53270cc800ecff1"}, - {file = "scipy-1.15.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4c6676490ad76d1c2894d77f976144b41bd1a4052107902238047fb6a473e971"}, - {file = "scipy-1.15.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a8bf5cb4a25046ac61d38f8d3c3426ec11ebc350246a4642f2f315fe95bda655"}, - {file = "scipy-1.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a8e34cf4c188b6dd004654f88586d78f95639e48a25dfae9c5e34a6dc34547e"}, - {file = "scipy-1.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28a0d2c2075946346e4408b211240764759e0fabaeb08d871639b5f3b1aca8a0"}, - {file = "scipy-1.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:42dabaaa798e987c425ed76062794e93a243be8f0f20fff6e7a89f4d61cb3d40"}, - {file = "scipy-1.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6f5e296ec63c5da6ba6fa0343ea73fd51b8b3e1a300b0a8cae3ed4b1122c7462"}, - {file = "scipy-1.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:597a0c7008b21c035831c39927406c6181bcf8f60a73f36219b69d010aa04737"}, - {file = "scipy-1.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c4697a10da8f8765bb7c83e24a470da5797e37041edfd77fd95ba3811a47c4fd"}, - {file = "scipy-1.15.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:869269b767d5ee7ea6991ed7e22b3ca1f22de73ab9a49c44bad338b725603301"}, - {file = "scipy-1.15.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:bad78d580270a4d32470563ea86c6590b465cb98f83d760ff5b0990cb5518a93"}, - {file = "scipy-1.15.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b09ae80010f52efddb15551025f9016c910296cf70adbf03ce2a8704f3a5ad20"}, - {file = "scipy-1.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a6fd6eac1ce74a9f77a7fc724080d507c5812d61e72bd5e4c489b042455865e"}, - {file = "scipy-1.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b871df1fe1a3ba85d90e22742b93584f8d2b8e6124f8372ab15c71b73e428b8"}, - {file = "scipy-1.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:03205d57a28e18dfd39f0377d5002725bf1f19a46f444108c29bdb246b6c8a11"}, - {file = "scipy-1.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:601881dfb761311045b03114c5fe718a12634e5608c3b403737ae463c9885d53"}, - {file = "scipy-1.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:e7c68b6a43259ba0aab737237876e5c2c549a031ddb7abc28c7b47f22e202ded"}, - {file = "scipy-1.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01edfac9f0798ad6b46d9c4c9ca0e0ad23dbf0b1eb70e96adb9fa7f525eff0bf"}, - {file = "scipy-1.15.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:08b57a9336b8e79b305a143c3655cc5bdbe6d5ece3378578888d2afbb51c4e37"}, - {file = "scipy-1.15.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:54c462098484e7466362a9f1672d20888f724911a74c22ae35b61f9c5919183d"}, - {file = "scipy-1.15.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:cf72ff559a53a6a6d77bd8eefd12a17995ffa44ad86c77a5df96f533d4e6c6bb"}, - {file = "scipy-1.15.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9de9d1416b3d9e7df9923ab23cd2fe714244af10b763975bea9e4f2e81cebd27"}, - {file = "scipy-1.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb530e4794fc8ea76a4a21ccb67dea33e5e0e60f07fc38a49e821e1eae3b71a0"}, - {file = "scipy-1.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5ea7ed46d437fc52350b028b1d44e002646e28f3e8ddc714011aaf87330f2f32"}, - {file = "scipy-1.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11e7ad32cf184b74380f43d3c0a706f49358b904fa7d5345f16ddf993609184d"}, - {file = "scipy-1.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:a5080a79dfb9b78b768cebf3c9dcbc7b665c5875793569f48bf0e2b1d7f68f6f"}, - {file = "scipy-1.15.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:447ce30cee6a9d5d1379087c9e474628dab3db4a67484be1b7dc3196bfb2fac9"}, - {file = "scipy-1.15.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:c90ebe8aaa4397eaefa8455a8182b164a6cc1d59ad53f79943f266d99f68687f"}, - {file = "scipy-1.15.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:def751dd08243934c884a3221156d63e15234a3155cf25978b0a668409d45eb6"}, - {file = "scipy-1.15.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:302093e7dfb120e55515936cb55618ee0b895f8bcaf18ff81eca086c17bd80af"}, - {file = "scipy-1.15.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd5b77413e1855351cdde594eca99c1f4a588c2d63711388b6a1f1c01f62274"}, - {file = "scipy-1.15.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d0194c37037707b2afa7a2f2a924cf7bac3dc292d51b6a925e5fcb89bc5c776"}, - {file = "scipy-1.15.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:bae43364d600fdc3ac327db99659dcb79e6e7ecd279a75fe1266669d9a652828"}, - {file = "scipy-1.15.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f031846580d9acccd0044efd1a90e6f4df3a6e12b4b6bd694a7bc03a89892b28"}, - {file = "scipy-1.15.2-cp313-cp313t-win_amd64.whl", hash = "sha256:fe8a9eb875d430d81755472c5ba75e84acc980e4a8f6204d402849234d3017db"}, - {file = "scipy-1.15.2.tar.gz", hash = "sha256:cd58a314d92838f7e6f755c8a2167ead4f27e1fd5c1251fd54289569ef3495ec"}, -] - -[package.dependencies] -numpy = ">=1.23.5,<2.5" - -[package.extras] -dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] -doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.16.5)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.0.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"] -test = ["Cython", "array-api-strict (>=2.0,<2.1.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja ; sys_platform != \"emscripten\"", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] - -[[package]] -name = "six" -version = "1.17.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main"] -files = [ - {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, - {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, -] - -[[package]] -name = "smmap" -version = "5.0.2" -description = "A pure Python implementation of a sliding window memory map manager" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e"}, - {file = "smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5"}, -] - -[[package]] -name = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - -[[package]] -name = "tiktoken" -version = "0.9.0" -description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "tiktoken-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:586c16358138b96ea804c034b8acf3f5d3f0258bd2bc3b0227af4af5d622e382"}, - {file = "tiktoken-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9c59ccc528c6c5dd51820b3474402f69d9a9e1d656226848ad68a8d5b2e5108"}, - {file = "tiktoken-0.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0968d5beeafbca2a72c595e8385a1a1f8af58feaebb02b227229b69ca5357fd"}, - {file = "tiktoken-0.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a5fb085a6a3b7350b8fc838baf493317ca0e17bd95e8642f95fc69ecfed1de"}, - {file = "tiktoken-0.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15a2752dea63d93b0332fb0ddb05dd909371ededa145fe6a3242f46724fa7990"}, - {file = "tiktoken-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:26113fec3bd7a352e4b33dbaf1bd8948de2507e30bd95a44e2b1156647bc01b4"}, - {file = "tiktoken-0.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f32cc56168eac4851109e9b5d327637f15fd662aa30dd79f964b7c39fbadd26e"}, - {file = "tiktoken-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:45556bc41241e5294063508caf901bf92ba52d8ef9222023f83d2483a3055348"}, - {file = "tiktoken-0.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03935988a91d6d3216e2ec7c645afbb3d870b37bcb67ada1943ec48678e7ee33"}, - {file = "tiktoken-0.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b3d80aad8d2c6b9238fc1a5524542087c52b860b10cbf952429ffb714bc1136"}, - {file = "tiktoken-0.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b2a21133be05dc116b1d0372af051cd2c6aa1d2188250c9b553f9fa49301b336"}, - {file = "tiktoken-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:11a20e67fdf58b0e2dea7b8654a288e481bb4fc0289d3ad21291f8d0849915fb"}, - {file = "tiktoken-0.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e88f121c1c22b726649ce67c089b90ddda8b9662545a8aeb03cfef15967ddd03"}, - {file = "tiktoken-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a6600660f2f72369acb13a57fb3e212434ed38b045fd8cc6cdd74947b4b5d210"}, - {file = "tiktoken-0.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95e811743b5dfa74f4b227927ed86cbc57cad4df859cb3b643be797914e41794"}, - {file = "tiktoken-0.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99376e1370d59bcf6935c933cb9ba64adc29033b7e73f5f7569f3aad86552b22"}, - {file = "tiktoken-0.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:badb947c32739fb6ddde173e14885fb3de4d32ab9d8c591cbd013c22b4c31dd2"}, - {file = "tiktoken-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:5a62d7a25225bafed786a524c1b9f0910a1128f4232615bf3f8257a73aaa3b16"}, - {file = "tiktoken-0.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b0e8e05a26eda1249e824156d537015480af7ae222ccb798e5234ae0285dbdb"}, - {file = "tiktoken-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:27d457f096f87685195eea0165a1807fae87b97b2161fe8c9b1df5bd74ca6f63"}, - {file = "tiktoken-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cf8ded49cddf825390e36dd1ad35cd49589e8161fdcb52aa25f0583e90a3e01"}, - {file = "tiktoken-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc156cb314119a8bb9748257a2eaebd5cc0753b6cb491d26694ed42fc7cb3139"}, - {file = "tiktoken-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cd69372e8c9dd761f0ab873112aba55a0e3e506332dd9f7522ca466e817b1b7a"}, - {file = "tiktoken-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:5ea0edb6f83dc56d794723286215918c1cde03712cbbafa0348b33448faf5b95"}, - {file = "tiktoken-0.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c6386ca815e7d96ef5b4ac61e0048cd32ca5a92d5781255e13b31381d28667dc"}, - {file = "tiktoken-0.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:75f6d5db5bc2c6274b674ceab1615c1778e6416b14705827d19b40e6355f03e0"}, - {file = "tiktoken-0.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e15b16f61e6f4625a57a36496d28dd182a8a60ec20a534c5343ba3cafa156ac7"}, - {file = "tiktoken-0.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebcec91babf21297022882344c3f7d9eed855931466c3311b1ad6b64befb3df"}, - {file = "tiktoken-0.9.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e5fd49e7799579240f03913447c0cdfa1129625ebd5ac440787afc4345990427"}, - {file = "tiktoken-0.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:26242ca9dc8b58e875ff4ca078b9a94d2f0813e6a535dcd2205df5d49d927cc7"}, - {file = "tiktoken-0.9.0.tar.gz", hash = "sha256:d02a5ca6a938e0490e1ff957bc48c8b078c88cb83977be1625b1fd8aac792c5d"}, -] - -[package.dependencies] -regex = ">=2022.1.18" -requests = ">=2.26.0" - -[package.extras] -blobfile = ["blobfile (>=2)"] - -[[package]] -name = "tokenizers" -version = "0.21.0" -description = "" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "tokenizers-0.21.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:3c4c93eae637e7d2aaae3d376f06085164e1660f89304c0ab2b1d08a406636b2"}, - {file = "tokenizers-0.21.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:f53ea537c925422a2e0e92a24cce96f6bc5046bbef24a1652a5edc8ba975f62e"}, - {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b177fb54c4702ef611de0c069d9169f0004233890e0c4c5bd5508ae05abf193"}, - {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b43779a269f4629bebb114e19c3fca0223296ae9fea8bb9a7a6c6fb0657ff8e"}, - {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aeb255802be90acfd363626753fda0064a8df06031012fe7d52fd9a905eb00e"}, - {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8b09dbeb7a8d73ee204a70f94fc06ea0f17dcf0844f16102b9f414f0b7463ba"}, - {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:400832c0904f77ce87c40f1a8a27493071282f785724ae62144324f171377273"}, - {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84ca973b3a96894d1707e189c14a774b701596d579ffc7e69debfc036a61a04"}, - {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:eb7202d231b273c34ec67767378cd04c767e967fda12d4a9e36208a34e2f137e"}, - {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:089d56db6782a73a27fd8abf3ba21779f5b85d4a9f35e3b493c7bbcbbf0d539b"}, - {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:c87ca3dc48b9b1222d984b6b7490355a6fdb411a2d810f6f05977258400ddb74"}, - {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4145505a973116f91bc3ac45988a92e618a6f83eb458f49ea0790df94ee243ff"}, - {file = "tokenizers-0.21.0-cp39-abi3-win32.whl", hash = "sha256:eb1702c2f27d25d9dd5b389cc1f2f51813e99f8ca30d9e25348db6585a97e24a"}, - {file = "tokenizers-0.21.0-cp39-abi3-win_amd64.whl", hash = "sha256:87841da5a25a3a5f70c102de371db120f41873b854ba65e52bccd57df5a3780c"}, - {file = "tokenizers-0.21.0.tar.gz", hash = "sha256:ee0894bf311b75b0c03079f33859ae4b2334d675d4e93f5a4132e1eae2834fe4"}, -] - -[package.dependencies] -huggingface-hub = ">=0.16.4,<1.0" - -[package.extras] -dev = ["tokenizers[testing]"] -docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] -testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests", "ruff"] - -[[package]] -name = "tqdm" -version = "4.67.1" -description = "Fast, Extensible Progress Meter" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, - {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] -discord = ["requests"] -notebook = ["ipywidgets (>=6)"] -slack = ["slack-sdk"] -telegram = ["requests"] - -[[package]] -name = "tree-sitter" -version = "0.24.0" -description = "Python bindings to the Tree-sitter parsing library" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "tree-sitter-0.24.0.tar.gz", hash = "sha256:abd95af65ca2f4f7eca356343391ed669e764f37748b5352946f00f7fc78e734"}, - {file = "tree_sitter-0.24.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f3f00feff1fc47a8e4863561b8da8f5e023d382dd31ed3e43cd11d4cae445445"}, - {file = "tree_sitter-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f9691be48d98c49ef8f498460278884c666b44129222ed6217477dffad5d4831"}, - {file = "tree_sitter-0.24.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:098a81df9f89cf254d92c1cd0660a838593f85d7505b28249216661d87adde4a"}, - {file = "tree_sitter-0.24.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b26bf9e958da6eb7e74a081aab9d9c7d05f9baeaa830dbb67481898fd16f1f5"}, - {file = "tree_sitter-0.24.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2a84ff87a2f2a008867a1064aba510ab3bd608e3e0cd6e8fef0379efee266c73"}, - {file = "tree_sitter-0.24.0-cp310-cp310-win_amd64.whl", hash = "sha256:c012e4c345c57a95d92ab5a890c637aaa51ab3b7ff25ed7069834b1087361c95"}, - {file = "tree_sitter-0.24.0-cp310-cp310-win_arm64.whl", hash = "sha256:033506c1bc2ba7bd559b23a6bdbeaf1127cee3c68a094b82396718596dfe98bc"}, - {file = "tree_sitter-0.24.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de0fb7c18c6068cacff46250c0a0473e8fc74d673e3e86555f131c2c1346fb13"}, - {file = "tree_sitter-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7c9c89666dea2ce2b2bf98e75f429d2876c569fab966afefdcd71974c6d8538"}, - {file = "tree_sitter-0.24.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ddb113e6b8b3e3b199695b1492a47d87d06c538e63050823d90ef13cac585fd"}, - {file = "tree_sitter-0.24.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01ea01a7003b88b92f7f875da6ba9d5d741e0c84bb1bd92c503c0eecd0ee6409"}, - {file = "tree_sitter-0.24.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:464fa5b2cac63608915a9de8a6efd67a4da1929e603ea86abaeae2cb1fe89921"}, - {file = "tree_sitter-0.24.0-cp311-cp311-win_amd64.whl", hash = "sha256:3b1f3cbd9700e1fba0be2e7d801527e37c49fc02dc140714669144ef6ab58dce"}, - {file = "tree_sitter-0.24.0-cp311-cp311-win_arm64.whl", hash = "sha256:f3f08a2ca9f600b3758792ba2406971665ffbad810847398d180c48cee174ee2"}, - {file = "tree_sitter-0.24.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:14beeff5f11e223c37be7d5d119819880601a80d0399abe8c738ae2288804afc"}, - {file = "tree_sitter-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26a5b130f70d5925d67b47db314da209063664585a2fd36fa69e0717738efaf4"}, - {file = "tree_sitter-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fc5c3c26d83c9d0ecb4fc4304fba35f034b7761d35286b936c1db1217558b4e"}, - {file = "tree_sitter-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:772e1bd8c0931c866b848d0369b32218ac97c24b04790ec4b0e409901945dd8e"}, - {file = "tree_sitter-0.24.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:24a8dd03b0d6b8812425f3b84d2f4763322684e38baf74e5bb766128b5633dc7"}, - {file = "tree_sitter-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:f9e8b1605ab60ed43803100f067eed71b0b0e6c1fb9860a262727dbfbbb74751"}, - {file = "tree_sitter-0.24.0-cp312-cp312-win_arm64.whl", hash = "sha256:f733a83d8355fc95561582b66bbea92ffd365c5d7a665bc9ebd25e049c2b2abb"}, - {file = "tree_sitter-0.24.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d4a6416ed421c4210f0ca405a4834d5ccfbb8ad6692d4d74f7773ef68f92071"}, - {file = "tree_sitter-0.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0992d483677e71d5c5d37f30dfb2e3afec2f932a9c53eec4fca13869b788c6c"}, - {file = "tree_sitter-0.24.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57277a12fbcefb1c8b206186068d456c600dbfbc3fd6c76968ee22614c5cd5ad"}, - {file = "tree_sitter-0.24.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25fa22766d63f73716c6fec1a31ee5cf904aa429484256bd5fdf5259051ed74"}, - {file = "tree_sitter-0.24.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7d5d9537507e1c8c5fa9935b34f320bfec4114d675e028f3ad94f11cf9db37b9"}, - {file = "tree_sitter-0.24.0-cp313-cp313-win_amd64.whl", hash = "sha256:f58bb4956917715ec4d5a28681829a8dad5c342cafd4aea269f9132a83ca9b34"}, - {file = "tree_sitter-0.24.0-cp313-cp313-win_arm64.whl", hash = "sha256:23641bd25dcd4bb0b6fa91b8fb3f46cc9f1c9f475efe4d536d3f1f688d1b84c8"}, -] - -[package.extras] -docs = ["sphinx (>=8.1,<9.0)", "sphinx-book-theme"] -tests = ["tree-sitter-html (>=0.23.2)", "tree-sitter-javascript (>=0.23.1)", "tree-sitter-json (>=0.24.8)", "tree-sitter-python (>=0.23.6)", "tree-sitter-rust (>=0.23.2)"] - -[[package]] -name = "tree-sitter-c-sharp" -version = "0.23.1" -description = "C# grammar for tree-sitter" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2b612a6e5bd17bb7fa2aab4bb6fc1fba45c94f09cb034ab332e45603b86e32fd"}, - {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a8b98f62bc53efcd4d971151950c9b9cd5cbe3bacdb0cd69fdccac63350d83e"}, - {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:986e93d845a438ec3c4416401aa98e6a6f6631d644bbbc2e43fcb915c51d255d"}, - {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8024e466b2f5611c6dc90321f232d8584893c7fb88b75e4a831992f877616d2"}, - {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7f9bf876866835492281d336b9e1f9626ab668737f74e914c31d285261507da7"}, - {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:ae9a9e859e8f44e2b07578d44f9a220d3fa25b688966708af6aa55d42abeebb3"}, - {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-win_arm64.whl", hash = "sha256:c81548347a93347be4f48cb63ec7d60ef4b0efa91313330e69641e49aa5a08c5"}, - {file = "tree_sitter_c_sharp-0.23.1.tar.gz", hash = "sha256:322e2cfd3a547a840375276b2aea3335fa6458aeac082f6c60fec3f745c967eb"}, -] - -[package.extras] -core = ["tree-sitter (>=0.22,<1.0)"] - -[[package]] -name = "tree-sitter-embedded-template" -version = "0.25.0" -description = "Embedded Template (ERB, EJS) grammar for tree-sitter" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "tree_sitter_embedded_template-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fa0d06467199aeb33fb3d6fa0665bf9b7d5a32621ffdaf37fd8249f8a8050649"}, - {file = "tree_sitter_embedded_template-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc7aacbc2985a5d7e7fe7334f44dffe24c38fb0a8295c4188a04cf21a3d64a73"}, - {file = "tree_sitter_embedded_template-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7c88c3dd8b94b3c9efe8ae071ff6b1b936a27ac5f6e651845c3b9631fa4c1c2"}, - {file = "tree_sitter_embedded_template-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:025f7ca84218dcd8455efc901bdbcc2689fb694f3a636c0448e322a23d4bc96b"}, - {file = "tree_sitter_embedded_template-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b5dc1aef6ffa3fae621fe037d85dd98948b597afba20df29d779c426be813ee5"}, - {file = "tree_sitter_embedded_template-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d0a35cfe634c44981a516243bc039874580e02a2990669313730187ce83a5bc6"}, - {file = "tree_sitter_embedded_template-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:3e05a4ac013d54505e75ae48e1a0e9db9aab19949fe15d9f4c7345b11a84a069"}, - {file = "tree_sitter_embedded_template-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:2751d402179ac0e83f2065b249d8fe6df0718153f1636bcb6a02bde3e5730db9"}, - {file = "tree_sitter_embedded_template-0.25.0.tar.gz", hash = "sha256:7d72d5e8a1d1d501a7c90e841b51f1449a90cc240be050e4fb85c22dab991d50"}, -] - -[package.extras] -core = ["tree-sitter (>=0.24,<1.0)"] - -[[package]] -name = "tree-sitter-javascript" -version = "0.23.1" -description = "JavaScript grammar for tree-sitter" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "tree_sitter_javascript-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6ca583dad4bd79d3053c310b9f7208cd597fd85f9947e4ab2294658bb5c11e35"}, - {file = "tree_sitter_javascript-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:94100e491a6a247aa4d14caf61230c171b6376c863039b6d9cd71255c2d815ec"}, - {file = "tree_sitter_javascript-0.23.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a6bc1055b061c5055ec58f39ee9b2e9efb8e6e0ae970838af74da0afb811f0a"}, - {file = "tree_sitter_javascript-0.23.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:056dc04fb6b24293f8c5fec43c14e7e16ba2075b3009c643abf8c85edc4c7c3c"}, - {file = "tree_sitter_javascript-0.23.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a11ca1c0f736da42967586b568dff8a465ee148a986c15ebdc9382806e0ce871"}, - {file = "tree_sitter_javascript-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:041fa22b34250ea6eb313d33104d5303f79504cb259d374d691e38bbdc49145b"}, - {file = "tree_sitter_javascript-0.23.1-cp39-abi3-win_arm64.whl", hash = "sha256:eb28130cd2fb30d702d614cbf61ef44d1c7f6869e7d864a9cc17111e370be8f7"}, - {file = "tree_sitter_javascript-0.23.1.tar.gz", hash = "sha256:b2059ce8b150162cda05a457ca3920450adbf915119c04b8c67b5241cd7fcfed"}, -] - -[package.extras] -core = ["tree-sitter (>=0.22,<1.0)"] - -[[package]] -name = "tree-sitter-language-pack" -version = "0.9.0" -description = "Comprehensive collection of 160+ tree-sitter language parsers" -optional = false -python-versions = ">=3.9.0" -groups = ["main"] -files = [ - {file = "tree_sitter_language_pack-0.9.0-cp39-abi3-macosx_10_13_universal2.whl", hash = "sha256:da4a643618148d6ca62343c8457bfc472e7d122503d97fac237f06acbbd8aa33"}, - {file = "tree_sitter_language_pack-0.9.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f1db4abded09ba0cb7a2358b4f3a2937fe9bfd4fdd4b4ad9e89a0c283e1329f"}, - {file = "tree_sitter_language_pack-0.9.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:5922afd7c2a2e632c4c69af10982b6017fd00ced70630c5f9e5d7c0d7d311b27"}, - {file = "tree_sitter_language_pack-0.9.0-cp39-abi3-win_amd64.whl", hash = "sha256:b3542ddaa1505716bc5b761e1aa718eafe64df988d700da62637cee501ac260f"}, - {file = "tree_sitter_language_pack-0.9.0.tar.gz", hash = "sha256:900eb3bd82c1bcf5cf20ed852b1b6fdc7eae89e40a860fa5e221a796687c359a"}, -] - -[package.dependencies] -tree-sitter = ">=0.23.2" -tree-sitter-c-sharp = ">=0.23.1" -tree-sitter-embedded-template = ">=0.23.2" -tree-sitter-yaml = ">=0.7.0" - -[[package]] -name = "tree-sitter-python" -version = "0.23.6" -description = "Python grammar for tree-sitter" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "tree_sitter_python-0.23.6-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:28fbec8f74eeb2b30292d97715e60fac9ccf8a8091ce19b9d93e9b580ed280fb"}, - {file = "tree_sitter_python-0.23.6-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:680b710051b144fedf61c95197db0094f2245e82551bf7f0c501356333571f7a"}, - {file = "tree_sitter_python-0.23.6-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a9dcef55507b6567207e8ee0a6b053d0688019b47ff7f26edc1764b7f4dc0a4"}, - {file = "tree_sitter_python-0.23.6-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29dacdc0cd2f64e55e61d96c6906533ebb2791972bec988450c46cce60092f5d"}, - {file = "tree_sitter_python-0.23.6-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7e048733c36f564b379831689006801feb267d8194f9e793fbb395ef1723335d"}, - {file = "tree_sitter_python-0.23.6-cp39-abi3-win_amd64.whl", hash = "sha256:a24027248399fb41594b696f929f9956828ae7cc85596d9f775e6c239cd0c2be"}, - {file = "tree_sitter_python-0.23.6-cp39-abi3-win_arm64.whl", hash = "sha256:71334371bd73d5fe080aed39fbff49ed8efb9506edebe16795b0c7567ed6a272"}, - {file = "tree_sitter_python-0.23.6.tar.gz", hash = "sha256:354bfa0a2f9217431764a631516f85173e9711af2c13dbd796a8815acfe505d9"}, -] - -[package.extras] -core = ["tree-sitter (>=0.22,<1.0)"] - -[[package]] -name = "tree-sitter-ruby" -version = "0.23.1" -description = "Ruby grammar for tree-sitter" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "tree_sitter_ruby-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:39f391322d2210843f07081182dbf00f8f69cfbfa4687b9575cac6d324bae443"}, - {file = "tree_sitter_ruby-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:aa4ee7433bd42fac22e2dad4a3c0f332292ecf482e610316828c711a0bb7f794"}, - {file = "tree_sitter_ruby-0.23.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62b36813a56006b7569db7868f6b762caa3f4e419bd0f8cf9ccbb4abb1b6254c"}, - {file = "tree_sitter_ruby-0.23.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7bcd93972b4ca2803856d4fe0fbd04123ff29c4592bbb9f12a27528bd252341"}, - {file = "tree_sitter_ruby-0.23.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:66c65d6c2a629783ca4ab2bab539bd6f271ce6f77cacb62845831e11665b5bd3"}, - {file = "tree_sitter_ruby-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:02e2c19ebefe29226c14aa63e11e291d990f5b5c20a99940ab6e7eda44e744e5"}, - {file = "tree_sitter_ruby-0.23.1-cp39-abi3-win_arm64.whl", hash = "sha256:ed042007e89f2cceeb1cbdd8b0caa68af1e2ce54c7eb2053ace760f90657ac9f"}, - {file = "tree_sitter_ruby-0.23.1.tar.gz", hash = "sha256:886ed200bfd1f3ca7628bf1c9fefd42421bbdba70c627363abda67f662caa21e"}, -] - -[package.extras] -core = ["tree-sitter (>=0.22,<1.0)"] - -[[package]] -name = "tree-sitter-typescript" -version = "0.23.2" -description = "TypeScript and TSX grammars for tree-sitter" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "tree_sitter_typescript-0.23.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3cd752d70d8e5371fdac6a9a4df9d8924b63b6998d268586f7d374c9fba2a478"}, - {file = "tree_sitter_typescript-0.23.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:c7cc1b0ff5d91bac863b0e38b1578d5505e718156c9db577c8baea2557f66de8"}, - {file = "tree_sitter_typescript-0.23.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b1eed5b0b3a8134e86126b00b743d667ec27c63fc9de1b7bb23168803879e31"}, - {file = "tree_sitter_typescript-0.23.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e96d36b85bcacdeb8ff5c2618d75593ef12ebaf1b4eace3477e2bdb2abb1752c"}, - {file = "tree_sitter_typescript-0.23.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8d4f0f9bcb61ad7b7509d49a1565ff2cc363863644a234e1e0fe10960e55aea0"}, - {file = "tree_sitter_typescript-0.23.2-cp39-abi3-win_amd64.whl", hash = "sha256:3f730b66396bc3e11811e4465c41ee45d9e9edd6de355a58bbbc49fa770da8f9"}, - {file = "tree_sitter_typescript-0.23.2-cp39-abi3-win_arm64.whl", hash = "sha256:05db58f70b95ef0ea126db5560f3775692f609589ed6f8dd0af84b7f19f1cbb7"}, - {file = "tree_sitter_typescript-0.23.2.tar.gz", hash = "sha256:7b167b5827c882261cb7a50dfa0fb567975f9b315e87ed87ad0a0a3aedb3834d"}, -] - -[package.extras] -core = ["tree-sitter (>=0.23,<1.0)"] - -[[package]] -name = "tree-sitter-yaml" -version = "0.7.2" -description = "YAML grammar for tree-sitter" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "tree_sitter_yaml-0.7.2-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:7e269ddcfcab8edb14fbb1f1d34eed1e1e26888f78f94eedfe7cc98c60f8bc9f"}, - {file = "tree_sitter_yaml-0.7.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:0807b7966e23ddf7dddc4545216e28b5a58cdadedcecca86b8d8c74271a07870"}, - {file = "tree_sitter_yaml-0.7.2-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1a5c60c98b6c4c037aae023569f020d0c489fad8dc26fdfd5510363c9c29a41"}, - {file = "tree_sitter_yaml-0.7.2-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88636d19d0654fd24f4f242eaaafa90f6f5ebdba8a62e4b32d251ed156c51a2a"}, - {file = "tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1d2e8f0bb14aa4537320952d0f9607eef3021d5aada8383c34ebeece17db1e06"}, - {file = "tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:74ca712c50fc9d7dbc68cb36b4a7811d6e67a5466b5a789f19bf8dd6084ef752"}, - {file = "tree_sitter_yaml-0.7.2-cp310-abi3-win_amd64.whl", hash = "sha256:7587b5ca00fc4f9a548eff649697a3b395370b2304b399ceefa2087d8a6c9186"}, - {file = "tree_sitter_yaml-0.7.2-cp310-abi3-win_arm64.whl", hash = "sha256:f63c227b18e7ce7587bce124578f0bbf1f890ac63d3e3cd027417574273642c4"}, - {file = "tree_sitter_yaml-0.7.2.tar.gz", hash = "sha256:756db4c09c9d9e97c81699e8f941cb8ce4e51104927f6090eefe638ee567d32c"}, -] - -[package.extras] -core = ["tree-sitter (>=0.24,<1.0)"] - -[[package]] -name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, -] - -[[package]] -name = "tzdata" -version = "2025.1" -description = "Provider of IANA time zone data" -optional = false -python-versions = ">=2" -groups = ["main"] -files = [ - {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"}, - {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"}, -] - -[[package]] -name = "urllib3" -version = "2.6.2" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd"}, - {file = "urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797"}, -] - -[package.extras] -brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] - -[[package]] -name = "virtualenv" -version = "20.29.3" -description = "Virtual Python Environment builder" -optional = true -python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\"" -files = [ - {file = "virtualenv-20.29.3-py3-none-any.whl", hash = "sha256:3e3d00f5807e83b234dfb6122bf37cfadf4be216c53a49ac059d02414f819170"}, - {file = "virtualenv-20.29.3.tar.gz", hash = "sha256:95e39403fcf3940ac45bc717597dba16110b74506131845d9b687d5e73d947ac"}, -] - -[package.dependencies] -distlib = ">=0.3.7,<1" -filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<5" - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] - -[[package]] -name = "whatthepatch" -version = "1.0.7" -description = "A patch parsing and application library." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "whatthepatch-1.0.7-py3-none-any.whl", hash = "sha256:1b6f655fd31091c001c209529dfaabbabdbad438f5de14e3951266ea0fc6e7ed"}, - {file = "whatthepatch-1.0.7.tar.gz", hash = "sha256:9eefb4ebea5200408e02d413d2b4bc28daea6b78bb4b4d53431af7245f7d7edf"}, -] - -[[package]] -name = "yarl" -version = "1.18.3" -description = "Yet another URL library" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, - {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, - {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, - {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, - {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, - {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, - {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, - {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, - {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, - {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, - {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, - {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, - {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, - {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, - {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, -] - -[package.dependencies] -idna = ">=2.0" -multidict = ">=4.0" -propcache = ">=0.2.0" - -[[package]] -name = "zipp" -version = "3.21.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, - {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] -type = ["pytest-mypy"] - -[extras] -dev = ["pre-commit", "ruff"] -test = ["psutil", "pytest"] - -[metadata] -lock-version = "2.1" -python-versions = ">=3.12" -content-hash = "3476befa257c031ecfb12edc83faf08175bef237078895638d5553d8ddca175e" diff --git a/pkg/hanzo-aci/pyproject.toml b/pkg/hanzo-aci/pyproject.toml deleted file mode 100644 index ebd154418..000000000 --- a/pkg/hanzo-aci/pyproject.toml +++ /dev/null @@ -1,64 +0,0 @@ -[project] -name = "hanzo-aci" -version = "0.2.8" -description = "An Agent-Computer Interface (ACI) designed for software development agents Dev." -readme = "README.md" -authors = [ - { name = "Hanzo Industries Inc" } -] -license = { text = "MIT" } -requires-python = ">=3.12" -dependencies = [ - "numpy", - "pandas", - "scipy", - "networkx", - "hanzo-llm>=1.0.0", - "gitpython", - "tree-sitter>=0.24.0", - "tree-sitter-python>=0.23.6", - "tree-sitter-javascript>=0.23.1", - "tree-sitter-typescript>=0.23.2", - "tree-sitter-ruby>=0.23.1", - "grep-ast>=0.8.1", - "flake8", - "whatthepatch>=1.0.6", - "binaryornot>=0.4.4", - "cachetools>=5.5.2", - "charset-normalizer>=3.4.1", - "h11 (>=0.16.0)", - "urllib3 (>=2.6.0)", - "aiohttp (>=3.12.14)", - "requests (>=2.32.4)", - "filelock (>=3.20.1)" -] - -[project.optional-dependencies] -dev = [ - "ruff>=0.7.2", - "pre-commit>=4.0.1" -] -test = [ - "pytest>=8.3.3", - "psutil>=5.9.8" -] - -[tool.setuptools.packages.find] -include = ["dev_aci*"] - -[build-system] -requires = ["setuptools>=61.0"] -build-backend = "setuptools.build_meta" - -[tool.autopep8] -ignore = ["E501"] - -[tool.black] -skip-string-normalization = true - -[tool.ruff.lint] -select = ["D"] -ignore = ["D1"] - -[tool.ruff.lint.pydocstyle] -convention = "google" diff --git a/pkg/hanzo-aci/pytest.ini b/pkg/hanzo-aci/pytest.ini deleted file mode 100644 index cb21384ed..000000000 --- a/pkg/hanzo-aci/pytest.ini +++ /dev/null @@ -1,2 +0,0 @@ -[pytest] -addopts = -p no:warnings --ignore=oh-viewer diff --git a/pkg/hanzo-aci/tests/integration/editor/__init__.py b/pkg/hanzo-aci/tests/integration/editor/__init__.py deleted file mode 100644 index 1474b6017..000000000 --- a/pkg/hanzo-aci/tests/integration/editor/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for file editor functionality.""" diff --git a/pkg/hanzo-aci/tests/integration/editor/conftest.py b/pkg/hanzo-aci/tests/integration/editor/conftest.py deleted file mode 100644 index c9742a7de..000000000 --- a/pkg/hanzo-aci/tests/integration/editor/conftest.py +++ /dev/null @@ -1,21 +0,0 @@ -import json -import tempfile -from pathlib import Path - -import pytest - - -@pytest.fixture -def temp_file(): - """Create a temporary file for testing.""" - with tempfile.NamedTemporaryFile(delete=False) as f: - yield Path(f.name) - try: - Path(f.name).unlink() - except FileNotFoundError: - pass - - -def parse_result(result: str) -> dict: - """Parse the JSON result from file_editor.""" - return json.loads(result[result.find("{") : result.rfind("}") + 1]) diff --git a/pkg/hanzo-aci/tests/integration/editor/test_basic_operations.py b/pkg/hanzo-aci/tests/integration/editor/test_basic_operations.py deleted file mode 100644 index f7d26d754..000000000 --- a/pkg/hanzo-aci/tests/integration/editor/test_basic_operations.py +++ /dev/null @@ -1,216 +0,0 @@ -"""Tests for basic file editor operations.""" - -import json -import re - -from dev_aci.editor import file_editor - -from .conftest import parse_result - - -def test_file_editor_happy_path(temp_file): - command = "str_replace" - old_str = "test file" - new_str = "sample file" - - # Create test file - with open(temp_file, "w") as f: - f.write("This is a test file.\nThis file is for testing purposes.") - - # Call the `file_editor` function - result = file_editor( - command=command, - path=temp_file, - old_str=old_str, - new_str=new_str, - enable_linting=False, - ) - - # Extract the JSON content using a regular expression - match = re.search( - r"(.*?)", - result, - re.DOTALL, - ) - assert ( - match - ), "Output does not contain the expected tags in the correct format." - result_dict = json.loads(match.group(1)) - - # Validate the formatted output in the result dictionary - formatted_output = result_dict["formatted_output_and_error"] - assert ( - formatted_output - == f"""The file {temp_file} has been edited. Here's the result of running `cat -n` on a snippet of {temp_file}: - 1\tThis is a sample file. - 2\tThis file is for testing purposes. -Review the changes and make sure they are as expected. Edit the file again if necessary.""" - ) - assert result_dict["path"] == str(temp_file) - assert result_dict["prev_exist"] is True - assert ( - result_dict["old_content"] - == "This is a test file.\nThis file is for testing purposes." - ) - assert ( - result_dict["new_content"] - == "This is a sample file.\nThis file is for testing purposes." - ) - - # Ensure the file content was updated - with open(temp_file, "r") as f: - content = f.read() - assert "This is a sample file." in content - - -def test_file_editor_with_xml_tag_parsing(temp_file): - # Create content that includes the XML tag pattern - xml_content = """This is a file with XML tags parsing logic... -match = re.search( - r'(.*?)', - result, - re.DOTALL, -) -...More text here. -""" - - with open(temp_file, "w") as f: - f.write(xml_content) - - result = file_editor( - command="view", - path=temp_file, - ) - - # Ensure the content is extracted correctly - match = re.search( - r"(.*?)", - result, - re.DOTALL, - ) - - assert ( - match - ), "Output does not contain the expected tags in the correct format." - result_dict = json.loads(match.group(1)) - - # Validate the formatted output in the result dictionary - formatted_output = result_dict["formatted_output_and_error"] - assert formatted_output == f"""Here's the result of running `cat -n` on {temp_file}: - 1\tThis is a file with XML tags parsing logic... - 2\tmatch = re.search( - 3\t r'(.*?)', - 4\t result, - 5\t re.DOTALL, - 6\t) - 7\t...More text here. - 8\t -""" - - -def test_successful_operations(temp_file): - """Test successful file operations and their output formatting.""" - # Create a test file - content = "line 1\nline 2\nline 3\n" - with open(temp_file, "w") as f: - f.write(content) - - # Test view - result = file_editor( - command="view", - path=temp_file, - enable_linting=False, - ) - result_json = parse_result(result) - assert ( - "Here's the result of running `cat -n`" - in result_json["formatted_output_and_error"] - ) - assert "line 1" in result_json["formatted_output_and_error"] - - # Test str_replace - result = file_editor( - command="str_replace", - path=temp_file, - old_str="line 2", - new_str="replaced line", - enable_linting=False, - ) - result_json = parse_result(result) - assert "has been edited" in result_json["formatted_output_and_error"] - assert "replaced line" in result_json["formatted_output_and_error"] - - # Test insert - result = file_editor( - command="insert", - path=temp_file, - insert_line=1, - new_str="inserted line", - enable_linting=False, - ) - result_json = parse_result(result) - assert "has been edited" in result_json["formatted_output_and_error"] - assert "inserted line" in result_json["formatted_output_and_error"] - - # Test undo - result = file_editor( - command="undo_edit", - path=temp_file, - enable_linting=False, - ) - result_json = parse_result(result) - assert "undone successfully" in result_json["formatted_output_and_error"] - - -def test_tab_expansion(temp_file): - """Test that tabs are properly expanded in file operations.""" - # Create a file with tabs - content = "no tabs\n\tindented\nline\twith\ttabs\n" - with open(temp_file, "w") as f: - f.write(content) - - # Test view command - result = file_editor( - command="view", - path=temp_file, - enable_linting=False, - ) - result_json = parse_result(result) - # Tabs should be expanded to spaces in output - assert " indented" in result_json["formatted_output_and_error"] - assert "line with tabs" in result_json["formatted_output_and_error"] - - # Test str_replace with tabs in old_str - result = file_editor( - command="str_replace", - path=temp_file, - old_str="line\twith\ttabs", - new_str="replaced line", - enable_linting=False, - ) - result_json = parse_result(result) - assert "replaced line" in result_json["formatted_output_and_error"] - - # Test str_replace with tabs in new_str - result = file_editor( - command="str_replace", - path=temp_file, - old_str="replaced line", - new_str="new\tline\twith\ttabs", - enable_linting=False, - ) - result_json = parse_result(result) - # Tabs should be expanded in the output - assert "new line with tabs" in result_json["formatted_output_and_error"] - - # Test insert with tabs - result = file_editor( - command="insert", - path=temp_file, - insert_line=1, - new_str="\tindented\tline", - enable_linting=False, - ) - result_json = parse_result(result) - # Tabs should be expanded in the output - assert " indented line" in result_json["formatted_output_and_error"] diff --git a/pkg/hanzo-aci/tests/integration/editor/test_error_handling.py b/pkg/hanzo-aci/tests/integration/editor/test_error_handling.py deleted file mode 100644 index 2abc1cbbb..000000000 --- a/pkg/hanzo-aci/tests/integration/editor/test_error_handling.py +++ /dev/null @@ -1,160 +0,0 @@ -"""Tests for error handling in file editor.""" - -from dev_aci.editor import file_editor - -from .conftest import parse_result - - -def test_validation_error_formatting(): - """Test that validation errors are properly formatted in the output.""" - result = file_editor( - command="view", - path="/nonexistent/file.txt", - enable_linting=False, - ) - result_json = parse_result(result) - assert "does not exist" in result_json["formatted_output_and_error"] - assert ( - result_json["error"] - == "Invalid `path` parameter: /nonexistent/file.txt. The path /nonexistent/file.txt does not exist. Please provide a valid path." - ) - - # Test directory validation for non-view commands - result = file_editor( - command="str_replace", - path="/tmp", - old_str="something", - new_str="new", - enable_linting=False, - ) - result_json = parse_result(result) - assert "only the `view` command" in result_json["formatted_output_and_error"] - assert "directory and only the `view` command" in result_json["error"] - - -def test_str_replace_error_handling(temp_file): - """Test error handling in str_replace command.""" - # Create a test file - content = "line 1\nline 2\nline 3\n" - with open(temp_file, "w") as f: - f.write(content) - - # Test non-existent string - result = file_editor( - command="str_replace", - path=temp_file, - old_str="nonexistent", - new_str="something", - enable_linting=False, - ) - result_json = parse_result(result) - assert "did not appear verbatim" in result_json["formatted_output_and_error"] - assert "did not appear verbatim" in result_json["error"] - - # Test multiple occurrences - with open(temp_file, "w") as f: - f.write("line\nline\nother") - - result = file_editor( - command="str_replace", - path=temp_file, - old_str="line", - new_str="new_line", - enable_linting=False, - ) - result_json = parse_result(result) - assert "Multiple occurrences" in result_json["formatted_output_and_error"] - assert "lines [1, 2]" in result_json["error"] - - -def test_view_range_validation(temp_file): - """Test validation of view_range parameter.""" - # Create a test file - content = "line 1\nline 2\nline 3\n" - with open(temp_file, "w") as f: - f.write(content) - - # Test invalid range format - result = file_editor( - command="view", - path=temp_file, - view_range=[1], # Should be [start, end] - enable_linting=False, - ) - result_json = parse_result(result) - assert ( - "should be a list of two integers" in result_json["formatted_output_and_error"] - ) - - # Test out of bounds range - result = file_editor( - command="view", - path=temp_file, - view_range=[1, 10], # File only has 3 lines - enable_linting=False, - ) - result_json = parse_result(result) - assert ( - "should be smaller than the number of lines" - in result_json["formatted_output_and_error"] - ) - - # Test invalid range order - result = file_editor( - command="view", - path=temp_file, - view_range=[3, 1], # End before start - enable_linting=False, - ) - result_json = parse_result(result) - assert ( - "should be greater than or equal to" - in result_json["formatted_output_and_error"] - ) - - -def test_insert_validation(temp_file): - """Test validation in insert command.""" - # Create a test file - content = "line 1\nline 2\nline 3\n" - with open(temp_file, "w") as f: - f.write(content) - - # Test insert at negative line - result = file_editor( - command="insert", - path=temp_file, - insert_line=-1, - new_str="new line", - enable_linting=False, - ) - result_json = parse_result(result) - assert "should be within the range" in result_json["formatted_output_and_error"] - - # Test insert beyond file length - result = file_editor( - command="insert", - path=temp_file, - insert_line=10, - new_str="new line", - enable_linting=False, - ) - result_json = parse_result(result) - assert "should be within the range" in result_json["formatted_output_and_error"] - - -def test_undo_validation(temp_file): - """Test undo_edit validation.""" - # Create a test file - content = "line 1\nline 2\nline 3\n" - with open(temp_file, "w") as f: - f.write(content) - - # Try to undo without any previous edits - result = file_editor( - command="undo_edit", - path=temp_file, - enable_linting=False, - ) - result_json = parse_result(result) - assert "No edit history found" in result_json["formatted_output_and_error"] diff --git a/pkg/hanzo-aci/tests/integration/editor/test_file_validation.py b/pkg/hanzo-aci/tests/integration/editor/test_file_validation.py deleted file mode 100644 index f4e610b1c..000000000 --- a/pkg/hanzo-aci/tests/integration/editor/test_file_validation.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Tests for file validation in file editor.""" - -import os -from pathlib import Path - -from dev_aci.editor import file_editor - -from .conftest import parse_result - - -def test_file_validation(temp_file): - """Test file validation for various file types.""" - # Ensure temp_file has .sql suffix - temp_file_sql = Path(temp_file).with_suffix(".sql") - os.rename(temp_file, temp_file_sql) - - # Test binary file - with open(temp_file_sql, "wb") as f: - f.write(b"Some text\x00with binary\x00content") - - result = file_editor( - command="view", - path=str(temp_file_sql), - enable_linting=False, - ) - result_json = parse_result(result) - assert "binary" in result_json["formatted_output_and_error"].lower() - - # Test large file - large_size = 11 * 1024 * 1024 # 11MB - with open(temp_file_sql, "w") as f: - f.write("x" * large_size) - - result = file_editor( - command="view", - path=str(temp_file_sql), - enable_linting=False, - ) - result_json = parse_result(result) - assert "too large" in result_json["formatted_output_and_error"] - assert "10MB" in result_json["formatted_output_and_error"] - - # Test SQL file - sql_content = """ - SELECT * - FROM users - WHERE id = 1; - """ - with open(temp_file_sql, "w") as f: - f.write(sql_content) - - result = file_editor( - command="view", - path=str(temp_file_sql), - enable_linting=False, - ) - result_json = parse_result(result) - assert "SELECT *" in result_json["formatted_output_and_error"] - assert "binary" not in result_json["formatted_output_and_error"].lower() diff --git a/pkg/hanzo-aci/tests/integration/editor/test_memory_usage.py b/pkg/hanzo-aci/tests/integration/editor/test_memory_usage.py deleted file mode 100644 index 41df0e6d6..000000000 --- a/pkg/hanzo-aci/tests/integration/editor/test_memory_usage.py +++ /dev/null @@ -1,218 +0,0 @@ -"""Tests for memory usage in file editor.""" - -import gc -import os - -import psutil -import pytest - -from dev_aci.editor import file_editor - -from .conftest import parse_result - - -def test_file_read_memory_usage(temp_file): - """Test that reading a large file uses memory efficiently.""" - # Create a large file (9.5MB to stay under 10MB limit) - file_size_mb = 9.5 - line_size = 100 # bytes per line approximately - num_lines = int((file_size_mb * 1024 * 1024) // line_size) - - print(f"\nCreating test file with {num_lines} lines...") - with open(temp_file, "w") as f: - for i in range(num_lines): - f.write(f"Line {i}: " + "x" * (line_size - 10) + "\n") - - actual_size = os.path.getsize(temp_file) / (1024 * 1024) - print(f"File created, size: {actual_size:.2f} MB") - - # Force Python to release file handles and clear buffers - gc.collect() - - # Get initial memory usage - initial_memory = psutil.Process(os.getpid()).memory_info().rss - print(f"Initial memory usage: {initial_memory / 1024 / 1024:.2f} MB") - - # Test reading specific lines - try: - result = file_editor( - command="view", - path=temp_file, - view_range=[5000, 5100], # Read 100 lines from middle - enable_linting=False, - ) - except Exception as e: - print(f"\nError during file read: {str(e)}") - raise - - # Check memory usage after reading - current_memory = psutil.Process(os.getpid()).memory_info().rss - memory_growth = current_memory - initial_memory - print( - f"Memory growth after reading 100 lines: {memory_growth / 1024 / 1024:.2f} MB" - ) - - # Memory growth should be small since we're only reading 100 lines - # Allow for some overhead but it should be much less than file size - # Increased to 3MB to account for chardet's memory usage - max_growth_mb = 3 # 3MB max growth - assert memory_growth <= max_growth_mb * 1024 * 1024, ( - f"Memory growth too high: {memory_growth / 1024 / 1024:.2f} MB " - f"(limit: {max_growth_mb} MB)" - ) - - # Parse the JSON output - try: - result_json = parse_result(result) - content = result_json["formatted_output_and_error"] - except Exception as e: - print(f"\nError parsing result: {str(e)}") - print(f"Result: {result[:200]}...") - raise - - # Extract the actual content (skip the header) - content_start = content.find("Here's the result of running `cat -n`") - if content_start == -1: - print(f"\nUnexpected content format: {content[:200]}...") - raise ValueError("Could not find expected content header") - content_start = content.find("\n", content_start) + 1 - content = content[content_start:] - - # Verify we got the correct lines - line_count = content.count("\n") - assert line_count >= 99, f"Should have read at least 99 lines, got {line_count}" - assert "Line 5000:" in content, "Should contain the first requested line" - assert "Line 5099:" in content, "Should contain the last requested line" - - print("Test completed successfully") - - -def test_file_editor_memory_leak(temp_file): - """Test to demonstrate memory growth during multiple file edits.""" - print("\nStarting memory leak test...") - - # Set memory limit to 128MB to make it more likely to catch issues - memory_limit = 128 * 1024 * 1024 # 128MB in bytes - try: - import resource - - resource.setrlimit(resource.RLIMIT_AS, (memory_limit, memory_limit)) - print("Memory limit set successfully") - except Exception as e: - print(f"Warning: Could not set memory limit: {str(e)}") - - initial_memory = psutil.Process(os.getpid()).memory_info().rss - print(f"\nInitial memory usage: {initial_memory / 1024 / 1024:.2f} MB") - - # Create initial content that's large enough to test but not overwhelming - # Keep total file size under 10MB to avoid file validation errors - base_content = ( - "Initial content with some reasonable length to make the file larger\n" - ) - content = base_content * 100 - print(f"\nCreating initial file with {len(content)} bytes") - with open(temp_file, "w") as f: - f.write(content) - print(f"Initial file created, size: {os.path.getsize(temp_file) / 1024:.1f} KB") - - try: - # Store memory readings for analysis - memory_readings = [] - file_size_mb = 0 - - # Perform edits with reasonable content size - for i in range(1000): # Increased iterations, smaller content per iteration - # Create content for each edit - keep it small to avoid file size limits - old_content = f"content_{i}\n" * 5 # 5 lines per edit - new_content = f"content_{i + 1}\n" * 5 - - # Instead of appending, we'll replace content to keep file size stable - with open(temp_file, "r") as f: - current_content = f.read() - - # Insert old_content at a random position while keeping file size stable - insert_pos = len(current_content) // 2 - new_file_content = ( - current_content[:insert_pos] - + old_content - + current_content[insert_pos + len(old_content) :] - ) - with open(temp_file, "w") as f: - f.write(new_file_content) - - # Perform the edit - try: - if i == 0: - print( - f"\nInitial file size: {os.path.getsize(temp_file) / (1024 * 1024):.2f} MB" - ) - print(f"Sample content to replace: {old_content[:100]}...") - result = file_editor( - command="str_replace", - path=temp_file, - old_str=old_content, - new_str=new_content, - enable_linting=False, - ) - if i == 0: - print(f"First edit result: {result[:200]}...") - except Exception as e: - print(f"\nError during edit {i}:") - print(f"File size: {os.path.getsize(temp_file) / (1024 * 1024):.2f} MB") - print(f"Error: {str(e)}") - raise - - if i % 25 == 0: # Check more frequently - current_memory = psutil.Process(os.getpid()).memory_info().rss - memory_mb = current_memory / 1024 / 1024 - memory_readings.append(memory_mb) - - # Get current file size - file_size_mb = os.path.getsize(temp_file) / (1024 * 1024) - - print(f"\nIteration {i}:") - print(f"Memory usage: {memory_mb:.2f} MB") - print(f"File size: {file_size_mb:.2f} MB") - - # Calculate memory growth - memory_growth = current_memory - initial_memory - growth_percent = (memory_growth / initial_memory) * 100 - print( - f"Memory growth: {memory_growth / 1024 / 1024:.2f} MB ({growth_percent:.1f}%)" - ) - - # Fail if memory growth is too high - assert memory_growth < memory_limit, ( - f"Memory growth exceeded limit after {i} edits. " - f"Growth: {memory_growth / 1024 / 1024:.2f} MB" - ) - - # Check for consistent growth pattern - if len(memory_readings) >= 3: - # Calculate growth rate between last 3 readings - growth_rate = (memory_readings[-1] - memory_readings[-3]) / 2 - print(f"Recent growth rate: {growth_rate:.2f} MB per 50 edits") - - # Fail if we see consistent growth above a threshold - # Allow more growth for initial allocations - max_growth = 2 if i < 100 else 1 # MB per 50 edits - if growth_rate > max_growth: - pytest.fail( - f"Consistent memory growth detected: {growth_rate:.2f} MB " - f"per 50 edits after {i} edits" - ) - - except MemoryError: - pytest.fail("Memory limit exceeded - possible memory leak detected") - except Exception as e: - if "Cannot allocate memory" in str(e): - pytest.fail("Memory limit exceeded - possible memory leak detected") - print(f"\nFinal file size: {file_size_mb:.2f} MB") - raise - - # Print final statistics - print("\nMemory usage statistics:") - print(f"Initial memory: {memory_readings[0]:.2f} MB") - print(f"Final memory: {memory_readings[-1]:.2f} MB") - print(f"Total growth: {(memory_readings[-1] - memory_readings[0]):.2f} MB") - print(f"Final file size: {file_size_mb:.2f} MB") diff --git a/pkg/hanzo-aci/tests/integration/editor/test_non_utf8_operations.py b/pkg/hanzo-aci/tests/integration/editor/test_non_utf8_operations.py deleted file mode 100644 index 615eb4f4e..000000000 --- a/pkg/hanzo-aci/tests/integration/editor/test_non_utf8_operations.py +++ /dev/null @@ -1,354 +0,0 @@ -"""Integration tests for editor operations with non-UTF-8 encoded files.""" - -import os -import tempfile -from pathlib import Path - -import pytest - -from dev_aci.editor import file_editor -from dev_aci.editor.encoding import EncodingManager - -from .conftest import parse_result - - -@pytest.fixture -def temp_non_utf8_file(): - """Create a temporary file with cp1251 encoding for testing.""" - fd, path = tempfile.mkstemp() - os.close(fd) - - # Create a file with cp1251 encoding containing Russian text - with open(path, "wb") as f: - f.write("# -*- coding: cp1251 -*-\n\n".encode("cp1251")) - f.write("# ะขะตัั‚ะพะฒั‹ะน ั„ะฐะนะป ั ะบะธั€ะธะปะปะธั†ะตะน\n".encode("cp1251")) - f.write('text = "ะŸั€ะธะฒะตั‚, ะผะธั€!"\n'.encode("cp1251")) - f.write("numbers = [1, 2, 3, 4, 5]\n".encode("cp1251")) - f.write('message = "ะญั‚ะพ ั‚ะตัั‚ะพะฒะฐั ัั‚ั€ะพะบะฐ"\n'.encode("cp1251")) - - yield Path(path) - os.unlink(path) - - -def test_view_non_utf8_file(temp_non_utf8_file): - """Test viewing a non-UTF-8 encoded file.""" - # View the file - result = file_editor( - command="view", - path=str(temp_non_utf8_file), - ) - - # Parse the result - result_json = parse_result(result) - - # Verify the content was read correctly - assert "ะŸั€ะธะฒะตั‚, ะผะธั€!" in result_json["formatted_output_and_error"] - assert "ะขะตัั‚ะพะฒั‹ะน ั„ะฐะนะป ั ะบะธั€ะธะปะปะธั†ะตะน" in result_json["formatted_output_and_error"] - assert "ะญั‚ะพ ั‚ะตัั‚ะพะฒะฐั ัั‚ั€ะพะบะฐ" in result_json["formatted_output_and_error"] - - -def test_view_range_non_utf8_file(temp_non_utf8_file): - """Test viewing a specific range of a non-UTF-8 encoded file.""" - # View only lines 3-5 - result = file_editor( - command="view", - path=str(temp_non_utf8_file), - view_range=[3, 5], - ) - - # Parse the result - result_json = parse_result(result) - - # Verify the content was read correctly - assert "ะขะตัั‚ะพะฒั‹ะน ั„ะฐะนะป ั ะบะธั€ะธะปะปะธั†ะตะน" in result_json["formatted_output_and_error"] - assert "ะŸั€ะธะฒะตั‚, ะผะธั€!" in result_json["formatted_output_and_error"] - - # Verify that line 6 is not included - assert "ะญั‚ะพ ั‚ะตัั‚ะพะฒะฐั ัั‚ั€ะพะบะฐ" not in result_json["formatted_output_and_error"] - - -def test_str_replace_non_utf8_file(temp_non_utf8_file): - """Test replacing text in a non-UTF-8 encoded file.""" - # Replace text - result = file_editor( - command="str_replace", - path=str(temp_non_utf8_file), - old_str="ะŸั€ะธะฒะตั‚, ะผะธั€!", - new_str="ะ—ะดั€ะฐะฒัั‚ะฒัƒะน, ะผะธั€!", - enable_linting=False, - ) - - # Parse the result - result_json = parse_result(result) - - # Verify the replacement was successful - assert "ะ—ะดั€ะฐะฒัั‚ะฒัƒะน, ะผะธั€!" in result_json["formatted_output_and_error"] - assert "ะŸั€ะธะฒะตั‚, ะผะธั€!" not in result_json["formatted_output_and_error"] - - # Verify the file was saved with the correct encoding - with open(temp_non_utf8_file, "rb") as f: - content = f.read() - - try: - decoded = content.decode("cp1251") - assert "ะ—ะดั€ะฐะฒัั‚ะฒัƒะน, ะผะธั€!" in decoded - except UnicodeDecodeError: - pytest.fail("File was not saved with the correct encoding") - - -def test_insert_non_utf8_file(temp_non_utf8_file): - """Test inserting text in a non-UTF-8 encoded file.""" - # Insert text after line 4 - result = file_editor( - command="insert", - path=str(temp_non_utf8_file), - insert_line=4, - new_str='new_var = "ะะพะฒะฐั ะฟะตั€ะตะผะตะฝะฝะฐั"', - enable_linting=False, - ) - - # Parse the result - result_json = parse_result(result) - - # Verify the insertion was successful - assert "ะะพะฒะฐั ะฟะตั€ะตะผะตะฝะฝะฐั" in result_json["formatted_output_and_error"] - - # Verify the file was saved with the correct encoding - with open(temp_non_utf8_file, "rb") as f: - content = f.read() - - try: - decoded = content.decode("cp1251") - assert "ะะพะฒะฐั ะฟะตั€ะตะผะตะฝะฝะฐั" in decoded - except UnicodeDecodeError: - pytest.fail("File was not saved with the correct encoding") - - -def test_create_non_utf8_file(): - """Test creating a new file with non-UTF-8 content.""" - # Create a temporary path - fd, path = tempfile.mkstemp() - os.close(fd) - os.unlink(path) # Remove the file so we can create it with the editor - - try: - # Create content with Russian characters - content = "# -*- coding: cp1251 -*-\n\n" - content += "# ะะพะฒั‹ะน ั„ะฐะนะป ั ะบะธั€ะธะปะปะธั†ะตะน\n" - content += 'greeting = "ะŸั€ะธะฒะตั‚ ะธะท ะฝะพะฒะพะณะพ ั„ะฐะนะปะฐ!"\n' - - # Create the file - result = file_editor( - command="create", - path=path, - file_text=content, - enable_linting=False, - ) - - # Parse the result - result_json = parse_result(result) - - # Verify the file was created successfully - assert "File created successfully" in result_json["formatted_output_and_error"] - - # Read the file with cp1251 encoding to verify content - encoding_manager = EncodingManager() - encoding = encoding_manager.detect_encoding(Path(path)) - - with open(path, "r", encoding=encoding) as f: - file_content = f.read() - - assert "ะŸั€ะธะฒะตั‚ ะธะท ะฝะพะฒะพะณะพ ั„ะฐะนะปะฐ!" in file_content - assert "ะะพะฒั‹ะน ั„ะฐะนะป ั ะบะธั€ะธะปะปะธั†ะตะน" in file_content - - finally: - # Clean up - try: - os.unlink(path) - except FileNotFoundError: - pass - - -def test_undo_edit_non_utf8_file(temp_non_utf8_file): - """Test undoing an edit in a non-UTF-8 encoded file.""" - # First, make a change - file_editor( - command="str_replace", - path=str(temp_non_utf8_file), - old_str="ะŸั€ะธะฒะตั‚, ะผะธั€!", - new_str="ะ—ะดั€ะฐะฒัั‚ะฒัƒะน, ะผะธั€!", - enable_linting=False, - ) - - # Now undo the change - result = file_editor( - command="undo_edit", - path=str(temp_non_utf8_file), - enable_linting=False, - ) - - # Parse the result - result_json = parse_result(result) - - # Verify the undo was successful - assert "undone successfully" in result_json["formatted_output_and_error"] - - # Verify the original content was restored with the correct encoding - with open(temp_non_utf8_file, "rb") as f: - content = f.read() - - try: - decoded = content.decode("cp1251") - assert "ะŸั€ะธะฒะตั‚, ะผะธั€!" in decoded - assert "ะ—ะดั€ะฐะฒัั‚ะฒัƒะน, ะผะธั€!" not in decoded - except UnicodeDecodeError: - pytest.fail("File was not restored with the correct encoding") - - -def test_complex_workflow_non_utf8_file(temp_non_utf8_file): - """Test a complex workflow with multiple operations on a non-UTF-8 encoded file.""" - # 1. View the file - result = file_editor( - command="view", - path=str(temp_non_utf8_file), - ) - result_json = parse_result(result) - assert "ะŸั€ะธะฒะตั‚, ะผะธั€!" in result_json["formatted_output_and_error"] - - # 2. Replace text - result = file_editor( - command="str_replace", - path=str(temp_non_utf8_file), - old_str="ะŸั€ะธะฒะตั‚, ะผะธั€!", - new_str="ะ—ะดั€ะฐะฒัั‚ะฒัƒะน, ะผะธั€!", - enable_linting=False, - ) - result_json = parse_result(result) - assert "ะ—ะดั€ะฐะฒัั‚ะฒัƒะน, ะผะธั€!" in result_json["formatted_output_and_error"] - - # 3. Insert text - result = file_editor( - command="insert", - path=str(temp_non_utf8_file), - insert_line=5, - new_str="# ะ”ะพะฑะฐะฒะปะตะฝะฝะฐั ัั‚ั€ะพะบะฐ\nboolean_var = True", - enable_linting=False, - ) - result_json = parse_result(result) - assert "ะ”ะพะฑะฐะฒะปะตะฝะฝะฐั ัั‚ั€ะพะบะฐ" in result_json["formatted_output_and_error"] - - # 4. View specific range - result = file_editor( - command="view", - path=str(temp_non_utf8_file), - view_range=[5, 7], - ) - result_json = parse_result(result) - assert "ะ”ะพะฑะฐะฒะปะตะฝะฝะฐั ัั‚ั€ะพะบะฐ" in result_json["formatted_output_and_error"] - assert "boolean_var = True" in result_json["formatted_output_and_error"] - - # 5. Undo the last edit - result = file_editor( - command="undo_edit", - path=str(temp_non_utf8_file), - enable_linting=False, - ) - result_json = parse_result(result) - assert "undone successfully" in result_json["formatted_output_and_error"] - - # 6. Verify the file content after all operations - with open(temp_non_utf8_file, "rb") as f: - content = f.read() - - try: - decoded = content.decode("cp1251") - assert "ะ—ะดั€ะฐะฒัั‚ะฒัƒะน, ะผะธั€!" in decoded # From step 2 - assert "ะ”ะพะฑะฐะฒะปะตะฝะฝะฐั ัั‚ั€ะพะบะฐ" not in decoded # Undone in step 5 - except UnicodeDecodeError: - pytest.fail("File was not maintained with the correct encoding") - - -def test_mixed_encoding_workflow(): - """Test workflow with files of different encodings.""" - # Create two temporary files with different encodings - fd1, path1 = tempfile.mkstemp() - fd2, path2 = tempfile.mkstemp() - os.close(fd1) - os.close(fd2) - - try: - # Create a cp1251 encoded file - with open(path1, "wb") as f: - f.write("# -*- coding: cp1251 -*-\n".encode("cp1251")) - f.write('text_cp1251 = "ะขะตะบัั‚ ะฒ ะบะพะดะธั€ะพะฒะบะต CP1251"\n'.encode("cp1251")) - - # Create a UTF-8 encoded file - with open(path2, "w", encoding="utf-8") as f: - f.write("# -*- coding: utf-8 -*-\n") - f.write('text_utf8 = "ะขะตะบัั‚ ะฒ ะบะพะดะธั€ะพะฒะบะต UTF-8"\n') - - # 1. View the cp1251 file - result1 = file_editor( - command="view", - path=path1, - ) - result_json1 = parse_result(result1) - assert "ะขะตะบัั‚ ะฒ ะบะพะดะธั€ะพะฒะบะต CP1251" in result_json1["formatted_output_and_error"] - - # 2. View the UTF-8 file - result2 = file_editor( - command="view", - path=path2, - ) - result_json2 = parse_result(result2) - assert "ะขะตะบัั‚ ะฒ ะบะพะดะธั€ะพะฒะบะต UTF-8" in result_json2["formatted_output_and_error"] - - # 3. Edit the cp1251 file - result3 = file_editor( - command="str_replace", - path=path1, - old_str="ะขะตะบัั‚ ะฒ ะบะพะดะธั€ะพะฒะบะต CP1251", - new_str="ะ˜ะทะผะตะฝะตะฝะฝั‹ะน ั‚ะตะบัั‚ ะฒ CP1251", - enable_linting=False, - ) - result_json3 = parse_result(result3) - assert "ะ˜ะทะผะตะฝะตะฝะฝั‹ะน ั‚ะตะบัั‚ ะฒ CP1251" in result_json3["formatted_output_and_error"] - - # 4. Edit the UTF-8 file - result4 = file_editor( - command="str_replace", - path=path2, - old_str="ะขะตะบัั‚ ะฒ ะบะพะดะธั€ะพะฒะบะต UTF-8", - new_str="ะ˜ะทะผะตะฝะตะฝะฝั‹ะน ั‚ะตะบัั‚ ะฒ UTF-8", - enable_linting=False, - ) - result_json4 = parse_result(result4) - assert "ะ˜ะทะผะตะฝะตะฝะฝั‹ะน ั‚ะตะบัั‚ ะฒ UTF-8" in result_json4["formatted_output_and_error"] - - # 5. Verify both files maintain their original encodings - with open(path1, "rb") as f: - content1 = f.read() - with open(path2, "rb") as f: - content2 = f.read() - - # CP1251 file should be decodable with CP1251 - try: - decoded1 = content1.decode("cp1251") - assert "ะ˜ะทะผะตะฝะตะฝะฝั‹ะน ั‚ะตะบัั‚ ะฒ CP1251" in decoded1 - except UnicodeDecodeError: - pytest.fail("CP1251 file was not saved with the correct encoding") - - # UTF-8 file should be decodable with UTF-8 - try: - decoded2 = content2.decode("utf-8") - assert "ะ˜ะทะผะตะฝะตะฝะฝั‹ะน ั‚ะตะบัั‚ ะฒ UTF-8" in decoded2 - except UnicodeDecodeError: - pytest.fail("UTF-8 file was not saved with the correct encoding") - - finally: - # Clean up - try: - os.unlink(path1) - os.unlink(path2) - except FileNotFoundError: - pass diff --git a/pkg/hanzo-aci/tests/integration/editor/test_peak_memory.py b/pkg/hanzo-aci/tests/integration/editor/test_peak_memory.py deleted file mode 100644 index 33eb6233c..000000000 --- a/pkg/hanzo-aci/tests/integration/editor/test_peak_memory.py +++ /dev/null @@ -1,269 +0,0 @@ -"""Tests for peak memory usage in file operations.""" - -import os -import resource -import tempfile -from pathlib import Path - -import psutil -import pytest - -from dev_aci.editor import file_editor - -# Skip all tests in this module on macOS due to platform-specific memory measurement issues -pytestmark = pytest.mark.skipif( - os.uname().sysname == "Darwin", - reason="Memory measurement tests are unreliable on macOS", -) - - -def get_memory_info(): - """Get current and peak memory usage in bytes.""" - process = psutil.Process(os.getpid()) - rss = process.memory_info().rss - peak_rss = ( - resource.getrusage(resource.RUSAGE_SELF).ru_maxrss * 1024 - ) # Convert KB to bytes - return { - "rss": rss, - "peak_rss": peak_rss, - "max": max(rss, peak_rss), - } - - -def create_test_file(path: Path, size_mb: float = 5.0): - """Create a test file of given size (default: 5MB).""" - line_size = 100 # bytes per line approximately - num_lines = int((size_mb * 1024 * 1024) // line_size) - - print(f"\nCreating test file with {num_lines} lines...") - with open(path, "w") as f: - for i in range(num_lines): - f.write(f"Line {i}: " + "x" * (line_size - 10) + "\n") - - actual_size = os.path.getsize(path) - print(f"File created, size: {actual_size / 1024 / 1024:.2f} MB") - return actual_size - - -def set_memory_limit(file_size: int, multiplier: float = 2.0): - """Set memory limit to multiplier * file_size.""" - # Add base memory for pytest and other processes (100MB) - base_memory = 100 * 1024 * 1024 # 100MB - memory_limit = int(file_size * multiplier + base_memory) - try: - # Get current limits - soft, hard = resource.getrlimit(resource.RLIMIT_AS) - # Only set limit if it's higher than current usage - current_usage = psutil.Process().memory_info().rss - if memory_limit > current_usage: - resource.setrlimit(resource.RLIMIT_AS, (memory_limit, hard)) - print(f"Memory limit set to {memory_limit / 1024 / 1024:.2f} MB") - else: - print( - f"Warning: Current memory usage ({current_usage / 1024 / 1024:.2f} MB) higher than limit ({memory_limit / 1024 / 1024:.2f} MB)" - ) - except Exception as e: - print(f"Warning: Could not set memory limit: {str(e)}") - return memory_limit - - -def check_memory_usage(initial_memory: int, file_size: int, operation: str): - """Check if memory usage is within acceptable limits.""" - current = get_memory_info() - memory_growth = current["max"] - initial_memory - print(f"Peak memory growth: {memory_growth / 1024 / 1024:.2f} MB") - - # Memory growth should be reasonable - # Allow up to 2x file size for temporary buffers plus 50MB for Python overhead - overhead = 50 * 1024 * 1024 # 50MB - max_growth = int(file_size * 2 + overhead) - assert memory_growth < max_growth, ( - f"Peak memory growth too high for {operation}: {memory_growth / 1024 / 1024:.2f} MB " - f"(limit: {max_growth / 1024 / 1024:.2f} MB)" - ) - - -def test_str_replace_peak_memory(): - """Test that str_replace operation has reasonable peak memory usage.""" - with tempfile.NamedTemporaryFile() as temp_file: - path = Path(temp_file.name) - file_size = create_test_file(path) - - # Force Python to release file handles and clear buffers - import gc - - gc.collect() - - # Get initial memory usage - initial = get_memory_info() - print(f"Initial memory usage: {initial['rss'] / 1024 / 1024:.2f} MB") - - # Set memory limit - set_memory_limit(file_size) - - # Perform str_replace operation - try: - _ = file_editor( - command="str_replace", - path=path, - old_str="Line 5000", # Replace a line in the middle - new_str="Modified line", - enable_linting=False, - ) - except MemoryError: - pytest.fail("Memory limit exceeded - peak memory usage too high") - except Exception as e: - if "Cannot allocate memory" in str(e): - pytest.fail("Memory limit exceeded - peak memory usage too high") - raise - - check_memory_usage(initial["max"], file_size, "str_replace") - - -def test_insert_peak_memory(): - """Test that insert operation has reasonable peak memory usage.""" - with tempfile.NamedTemporaryFile() as temp_file: - path = Path(temp_file.name) - file_size = create_test_file(path) - - # Force Python to release file handles and clear buffers - import gc - - gc.collect() - - # Get initial memory usage - initial = get_memory_info() - print(f"Initial memory usage: {initial['rss'] / 1024 / 1024:.2f} MB") - - # Set memory limit - set_memory_limit(file_size) - - # Perform insert operation - try: - _ = file_editor( - command="insert", - path=path, - insert_line=5000, # Insert in the middle - new_str="New line inserted\n" * 10, - enable_linting=False, - ) - except MemoryError: - pytest.fail("Memory limit exceeded - peak memory usage too high") - except Exception as e: - if "Cannot allocate memory" in str(e): - pytest.fail("Memory limit exceeded - peak memory usage too high") - raise - - check_memory_usage(initial["max"], file_size, "insert") - - -def test_view_peak_memory(): - """Test that view operation has reasonable peak memory usage.""" - with tempfile.NamedTemporaryFile() as temp_file: - path = Path(temp_file.name) - file_size = create_test_file(path) - - # Force Python to release file handles and clear buffers - import gc - - gc.collect() - - # Get initial memory usage - initial = get_memory_info() - print(f"Initial memory usage: {initial['rss'] / 1024 / 1024:.2f} MB") - - # Set memory limit - set_memory_limit(file_size) - - # Test viewing specific lines - try: - _ = file_editor( - command="view", - path=path, - view_range=[5000, 5100], # View 100 lines from middle - enable_linting=False, - ) - except MemoryError: - pytest.fail("Memory limit exceeded - peak memory usage too high") - except Exception as e: - if "Cannot allocate memory" in str(e): - pytest.fail("Memory limit exceeded - peak memory usage too high") - raise - - check_memory_usage(initial["max"], file_size, "view") - - -def test_view_full_file_peak_memory(): - """Test that viewing entire file has reasonable peak memory usage.""" - with tempfile.NamedTemporaryFile() as temp_file: - path = Path(temp_file.name) - file_size = create_test_file(path, size_mb=5.0) # Smaller file for full view - - # Force Python to release file handles and clear buffers - import gc - - gc.collect() - - # Get initial memory usage - initial = get_memory_info() - print(f"Initial memory usage: {initial['rss'] / 1024 / 1024:.2f} MB") - - # Set memory limit - set_memory_limit(file_size) - - # Test viewing entire file - try: - _ = file_editor( - command="view", - path=path, - enable_linting=False, - ) - except MemoryError: - pytest.fail("Memory limit exceeded - peak memory usage too high") - except Exception as e: - if "Cannot allocate memory" in str(e): - pytest.fail("Memory limit exceeded - peak memory usage too high") - raise - - check_memory_usage(initial["max"], file_size, "view_full") - - -def test_large_history_insert(): - """Test inserting a large amount of data into the history cache.""" - import logging - import tempfile - - from dev_aci.editor.history import FileHistoryManager - - # Set up logging - logging.basicConfig(level=logging.ERROR) - - with tempfile.TemporaryDirectory() as temp_dir: - history_dir = Path(temp_dir) - manager = FileHistoryManager(max_history_per_file=1000, history_dir=history_dir) - - # Create a large string (about 1MB) - large_content = "x" * (1024 * 1024) - - # Try to insert the large content multiple times - num_files = 100 - for i in range(num_files): - try: - manager.add_history(Path(f"test_file_{i}.txt"), large_content) - except Exception as e: - pytest.fail(f"Error occurred on iteration {i}: {str(e)}") - - # Check if we can still retrieve the last entry - last_content = manager.pop_last_history(Path(f"test_file_{num_files - 1}.txt")) - assert ( - last_content == large_content - ), "Failed to retrieve the last inserted content" - - # Check if the number of cache entries is correct - cache_entries = list(manager.cache) - assert ( - len(cache_entries) - == num_files * 2 - - 1 # The cache entry for file content was removed, only metadata remains - ), f"Expected {num_files * 2 - 1} cache entries ({num_files - 1} content + {num_files} metadata), but found {len(cache_entries)}" diff --git a/pkg/hanzo-aci/tests/integration/test_oh_editor.py b/pkg/hanzo-aci/tests/integration/test_oh_editor.py deleted file mode 100644 index 5e56fc85a..000000000 --- a/pkg/hanzo-aci/tests/integration/test_oh_editor.py +++ /dev/null @@ -1,607 +0,0 @@ -from pathlib import Path - -import pytest - -from dev_aci.editor.editor import OHEditor -from dev_aci.editor.exceptions import ( - EditorToolParameterInvalidError, - EditorToolParameterMissingError, - ToolError, -) -from dev_aci.editor.prompts import ( - DIRECTORY_CONTENT_TRUNCATED_NOTICE, - FILE_CONTENT_TRUNCATED_NOTICE, -) -from dev_aci.editor.results import CLIResult, ToolResult - - -@pytest.fixture -def editor(tmp_path): - editor = OHEditor() - # Set up a temporary directory with test files - test_file = tmp_path / "test.txt" - test_file.write_text("This is a test file.\nThis file is for testing purposes.") - return editor, test_file - - -@pytest.fixture -def editor_python_file_with_tabs(tmp_path): - editor = OHEditor() - # Set up a temporary directory with test files - test_file = tmp_path / "test.py" - test_file.write_text('def test():\n\tprint("Hello, World!")') - return editor, test_file - - -def test_view_file(editor): - editor, test_file = editor - result = editor(command="view", path=str(test_file)) - assert isinstance(result, CLIResult) - assert f"Here's the result of running `cat -n` on {test_file}:" in result.output - assert "1\tThis is a test file." in result.output - assert "2\tThis file is for testing purposes." in result.output - - -def test_view_directory(editor): - editor, test_file = editor - parent_dir = test_file.parent - result = editor(command="view", path=str(parent_dir)) - assert ( - result.output - == f"""Here's the files and directories up to 2 levels deep in {parent_dir}, excluding hidden items: -{parent_dir}/ -{parent_dir}/test.txt""" - ) - - -def test_create_file(editor): - editor, test_file = editor - new_file = test_file.parent / "new_file.txt" - result = editor(command="create", path=str(new_file), file_text="New file content") - assert isinstance(result, ToolResult) - assert new_file.exists() - assert new_file.read_text() == "New file content" - assert "File created successfully" in result.output - - -def test_create_with_empty_string(editor): - editor, test_file = editor - new_file = test_file.parent / "empty_content.txt" - result = editor(command="create", path=str(new_file), file_text="") - assert isinstance(result, ToolResult) - assert new_file.exists() - assert new_file.read_text() == "" - assert "File created successfully" in result.output - - -def test_create_with_none_file_text(editor): - editor, test_file = editor - new_file = test_file.parent / "none_content.txt" - with pytest.raises(EditorToolParameterMissingError) as exc_info: - editor(command="create", path=str(new_file), file_text=None) - assert "file_text" in str(exc_info.value.message) - - -def test_str_replace_no_linting(editor): - editor, test_file = editor - result = editor( - command="str_replace", - path=str(test_file), - old_str="test file", - new_str="sample file", - ) - assert isinstance(result, CLIResult) - - # Test str_replace command - assert ( - result.output - == f"""The file {test_file} has been edited. Here's the result of running `cat -n` on a snippet of {test_file}: - 1\tThis is a sample file. - 2\tThis file is for testing purposes. -Review the changes and make sure they are as expected. Edit the file again if necessary.""" - ) - - # Test that the file content has been updated - assert "This is a sample file." in test_file.read_text() - - -def test_str_replace_multi_line_no_linting(editor): - editor, test_file = editor - result = editor( - command="str_replace", - path=str(test_file), - old_str="This is a test file.\nThis file is for testing purposes.", - new_str="This is a sample file.\nThis file is for testing purposes.", - ) - assert isinstance(result, CLIResult) - - # Test str_replace command - assert ( - result.output - == f"""The file {test_file} has been edited. Here's the result of running `cat -n` on a snippet of {test_file}: - 1\tThis is a sample file. - 2\tThis file is for testing purposes. -Review the changes and make sure they are as expected. Edit the file again if necessary.""" - ) - - -def test_str_replace_multi_line_with_tabs_no_linting(editor_python_file_with_tabs): - editor, test_file = editor_python_file_with_tabs - result = editor( - command="str_replace", - path=str(test_file), - old_str='def test():\n\tprint("Hello, World!")', - new_str='def test():\n\tprint("Hello, Universe!")', - ) - assert isinstance(result, CLIResult) - - assert ( - result.output - == f"""The file {test_file} has been edited. Here's the result of running `cat -n` on a snippet of {test_file}: - 1\tdef test(): - 2\t{"\t".expandtabs()}print("Hello, Universe!") -Review the changes and make sure they are as expected. Edit the file again if necessary.""" - ) - - -def test_str_replace_with_linting(editor): - editor, test_file = editor - result = editor( - command="str_replace", - path=str(test_file), - old_str="test file", - new_str="sample file", - enable_linting=True, - ) - assert isinstance(result, CLIResult) - - # Test str_replace command - assert ( - result.output - == f"""The file {test_file} has been edited. Here's the result of running `cat -n` on a snippet of {test_file}: - 1\tThis is a sample file. - 2\tThis file is for testing purposes. - -No linting issues found in the changes. -Review the changes and make sure they are as expected. Edit the file again if necessary.""" - ) - - # Test that the file content has been updated - assert "This is a sample file." in test_file.read_text() - - -def test_str_replace_error_multiple_occurrences(editor): - editor, test_file = editor - with pytest.raises(ToolError) as exc_info: - editor( - command="str_replace", path=str(test_file), old_str="test", new_str="sample" - ) - assert "Multiple occurrences of old_str `test`" in str(exc_info.value.message) - assert "[1, 2]" in str(exc_info.value.message) # Should show both line numbers - - -def test_str_replace_error_multiple_multiline_occurrences(editor): - editor, test_file = editor - # Create a file with two identical multi-line blocks - multi_block = """def example(): - print("Hello") - return True""" - content = f"{multi_block}\n\nprint('separator')\n\n{multi_block}" - test_file.write_text(content) - - with pytest.raises(ToolError) as exc_info: - editor( - command="str_replace", - path=str(test_file), - old_str=multi_block, - new_str='def new():\n print("World")', - ) - error_msg = str(exc_info.value.message) - assert "Multiple occurrences of old_str" in error_msg - assert "[1, 7]" in error_msg # Should show correct starting line numbers - - -def test_str_replace_nonexistent_string(editor): - editor, test_file = editor - with pytest.raises(ToolError) as exc_info: - editor( - command="str_replace", - path=str(test_file), - old_str="Non-existent Line", - new_str="New Line", - ) - assert "No replacement was performed" in str(exc_info) - assert f"old_str `Non-existent Line` did not appear verbatim in {test_file}" in str( - exc_info.value.message - ) - - -def test_str_replace_with_empty_new_str(editor): - editor, test_file = editor - test_file.write_text("Line 1\nLine to remove\nLine 3") - result = editor( - command="str_replace", - path=str(test_file), - old_str="Line to remove\n", - new_str="", - ) - assert isinstance(result, CLIResult) - assert test_file.read_text() == "Line 1\nLine 3" - - -def test_str_replace_with_empty_old_str(editor): - editor, test_file = editor - test_file.write_text("Line 1\nLine 2\nLine 3") - with pytest.raises(ToolError) as exc_info: - editor( - command="str_replace", - path=str(test_file), - old_str="", - new_str="New string", - ) - assert ( - str(exc_info.value.message) - == """No replacement was performed. Multiple occurrences of old_str `` in lines [1, 2, 3]. Please ensure it is unique.""" - ) - - -def test_str_replace_with_none_old_str(editor): - editor, test_file = editor - with pytest.raises(EditorToolParameterMissingError) as exc_info: - editor( - command="str_replace", - path=str(test_file), - old_str=None, - new_str="new content", - ) - assert "old_str" in str(exc_info.value.message) - - -def test_insert_no_linting(editor): - editor, test_file = editor - result = editor( - command="insert", path=str(test_file), insert_line=1, new_str="Inserted line" - ) - assert isinstance(result, CLIResult) - assert "Inserted line" in test_file.read_text() - print(result.output) - assert ( - result.output - == f"""The file {test_file} has been edited. Here's the result of running `cat -n` on a snippet of the edited file: - 1\tThis is a test file. - 2\tInserted line - 3\tThis file is for testing purposes. -Review the changes and make sure they are as expected (correct indentation, no duplicate lines, etc). Edit the file again if necessary.""" - ) - - -def test_insert_with_linting(editor): - editor, test_file = editor - result = editor( - command="insert", - path=str(test_file), - insert_line=1, - new_str="Inserted line", - enable_linting=True, - ) - assert isinstance(result, CLIResult) - assert "Inserted line" in test_file.read_text() - print(result.output) - assert ( - result.output - == f"""The file {test_file} has been edited. Here's the result of running `cat -n` on a snippet of the edited file: - 1\tThis is a test file. - 2\tInserted line - 3\tThis file is for testing purposes. - -No linting issues found in the changes. -Review the changes and make sure they are as expected (correct indentation, no duplicate lines, etc). Edit the file again if necessary.""" - ) - - -def test_insert_invalid_line(editor): - editor, test_file = editor - with pytest.raises(EditorToolParameterInvalidError) as exc_info: - editor( - command="insert", - path=str(test_file), - insert_line=10, - new_str="Invalid Insert", - ) - assert "Invalid `insert_line` parameter" in str(exc_info.value.message) - assert "It should be within the range of lines of the file" in str( - exc_info.value.message - ) - - -def test_insert_with_empty_string(editor): - editor, test_file = editor - result = editor( - command="insert", - path=str(test_file), - insert_line=1, - new_str="", - ) - assert isinstance(result, CLIResult) - content = test_file.read_text().splitlines() - assert "" in content - assert len(content) == 3 # Original 2 lines plus empty line - - -def test_insert_with_none_new_str(editor): - editor, test_file = editor - with pytest.raises(EditorToolParameterMissingError) as exc_info: - editor( - command="insert", - path=str(test_file), - insert_line=1, - new_str=None, - ) - assert "new_str" in str(exc_info.value.message) - - -def test_undo_edit(editor): - editor, test_file = editor - # Make an edit to be undone - result = editor( - command="str_replace", - path=str(test_file), - old_str="test file", - new_str="sample file", - ) - # Undo the edit - result = editor(command="undo_edit", path=str(test_file)) - assert isinstance(result, CLIResult) - assert "Last edit to" in result.output - assert "test file" in test_file.read_text() # Original content restored - - -def test_multiple_undo_edits(editor): - editor, test_file = editor - # Make an edit to be undone - _ = editor( - command="str_replace", - path=str(test_file), - old_str="test file", - new_str="sample file v1", - ) - # Make another edit to be undone - _ = editor( - command="str_replace", - path=str(test_file), - old_str="sample file v1", - new_str="sample file v2", - ) - # Undo the last edit - result = editor(command="undo_edit", path=str(test_file)) - assert isinstance(result, CLIResult) - assert "Last edit to" in result.output - assert "sample file v1" in test_file.read_text() # Previous content restored - - # Undo the first edit - result = editor(command="undo_edit", path=str(test_file)) - assert isinstance(result, CLIResult) - assert "Last edit to" in result.output - assert "test file" in test_file.read_text() # Original content restored - - -def test_validate_path_invalid(editor): - editor, test_file = editor - invalid_file = test_file.parent / "nonexistent.txt" - with pytest.raises(EditorToolParameterInvalidError): - editor(command="view", path=str(invalid_file)) - - -def test_create_existing_file_error(editor): - editor, test_file = editor - with pytest.raises(EditorToolParameterInvalidError): - editor(command="create", path=str(test_file), file_text="New content") - - -def test_str_replace_missing_old_str(editor): - editor, test_file = editor - with pytest.raises(EditorToolParameterMissingError): - editor(command="str_replace", path=str(test_file), new_str="sample") - - -def test_str_replace_new_str_and_old_str_same(editor): - editor, test_file = editor - with pytest.raises(EditorToolParameterInvalidError) as exc_info: - editor( - command="str_replace", - path=str(test_file), - old_str="test file", - new_str="test file", - ) - assert ( - "No replacement was performed. `new_str` and `old_str` must be different." - in str(exc_info.value.message) - ) - - -def test_insert_missing_line_param(editor): - editor, test_file = editor - with pytest.raises(EditorToolParameterMissingError): - editor(command="insert", path=str(test_file), new_str="Missing insert line") - - -def test_undo_edit_no_history_error(editor): - editor, test_file = editor - empty_file = test_file.parent / "empty.txt" - empty_file.write_text("") - with pytest.raises(ToolError): - editor(command="undo_edit", path=str(empty_file)) - - -def test_view_directory_with_hidden_files(tmp_path): - editor = OHEditor() - - # Create a directory with some test files - test_dir = tmp_path / "test_dir" - test_dir.mkdir() - (test_dir / "visible.txt").write_text("content1") - (test_dir / ".hidden1").write_text("hidden1") - (test_dir / ".hidden2").write_text("hidden2") - - # Create a hidden subdirectory with a file - hidden_subdir = test_dir / ".hidden_dir" - hidden_subdir.mkdir() - (hidden_subdir / "file.txt").write_text("content3") - - # Create a visible subdirectory - visible_subdir = test_dir / "visible_dir" - visible_subdir.mkdir() - - # View the directory - result = editor(command="view", path=str(test_dir)) - - # Verify output - assert isinstance(result, CLIResult) - assert str(test_dir) in result.output - assert "visible.txt" in result.output # Visible file is shown - assert "visible_dir" in result.output # Visible directory is shown - assert ".hidden1" not in result.output # Hidden files not shown - assert ".hidden2" not in result.output - assert ".hidden_dir" not in result.output - assert ( - "3 hidden files/directories in this directory are excluded" in result.output - ) # Shows count of hidden items in current dir only - assert "ls -la" in result.output # Shows command to view hidden files - - -def test_view_symlinked_directory(tmp_path): - editor = OHEditor() - - # Create a directory with some test files - source_dir = tmp_path / "source_dir" - source_dir.mkdir() - (source_dir / "file1.txt").write_text("content1") - (source_dir / "file2.txt").write_text("content2") - - # Create a subdirectory with a file - subdir = source_dir / "subdir" - subdir.mkdir() - (subdir / "file3.txt").write_text("content3") - - # Create a symlink to the directory - symlink_dir = tmp_path / "symlink_dir" - symlink_dir.symlink_to(source_dir) - - # View the symlinked directory - result = editor(command="view", path=str(symlink_dir)) - - # Verify that all files are listed through the symlink - assert isinstance(result, CLIResult) - assert str(symlink_dir) in result.output - assert "file1.txt" in result.output - assert "file2.txt" in result.output - assert "subdir" in result.output - assert "file3.txt" in result.output - - -def test_view_large_directory_with_truncation(editor, tmp_path): - editor, _ = editor - # Create a directory with many files to trigger truncation - large_dir = tmp_path / "large_dir" - large_dir.mkdir() - for i in range(1000): # 1000 files should trigger truncation - (large_dir / f"file_{i}.txt").write_text("content") - - result = editor(command="view", path=str(large_dir)) - assert isinstance(result, CLIResult) - assert DIRECTORY_CONTENT_TRUNCATED_NOTICE in result.output - - -def test_view_directory_on_hidden_path(tmp_path): - """Directory structure. - - .test_dir/ - โ”œโ”€โ”€ visible1.txt - โ”œโ”€โ”€ .hidden1 - โ”œโ”€โ”€ visible_dir/ - โ”‚ โ”œโ”€โ”€ visible2.txt - โ”‚ โ””โ”€โ”€ .hidden2 - โ””โ”€โ”€ .hidden_dir/ - โ”œโ”€โ”€ visible3.txt - โ””โ”€โ”€ .hidden3 - """ - editor = OHEditor() - - # Create a directory with test files at depth 1 - hidden_test_dir = tmp_path / ".hidden_test_dir" - hidden_test_dir.mkdir() - (hidden_test_dir / "visible1.txt").write_text("content1") - (hidden_test_dir / ".hidden1").write_text("hidden1") - - # Create a visible subdirectory with visible and hidden files - visible_subdir = hidden_test_dir / "visible_dir" - visible_subdir.mkdir() - (visible_subdir / "visible2.txt").write_text("content2") - (visible_subdir / ".hidden2").write_text("hidden2") - - # Create a hidden subdirectory with visible and hidden files - hidden_subdir = hidden_test_dir / ".hidden_dir" - hidden_subdir.mkdir() - (hidden_subdir / "visible3.txt").write_text("content3") - (hidden_subdir / ".hidden3").write_text("hidden3") - - # View the directory - result = editor(command="view", path=str(hidden_test_dir)) - - # Verify output - assert isinstance(result, CLIResult) - # Depth 1: Visible files/dirs shown, hidden files/dirs not shown - assert "visible1.txt" in result.output - assert "visible_dir" in result.output - assert ".hidden1" not in result.output - assert ".hidden_dir" not in result.output - - # Depth 2: Files in visible_dir shown - assert "visible2.txt" in result.output - assert ".hidden2" not in result.output - - # Depth 2: Files in hidden_dir not shown - assert "visible3.txt" not in result.output - assert ".hidden3" not in result.output - - # Hidden file count only includes depth 1 - assert ( - "2 hidden files/directories in this directory are excluded" in result.output - ) # Only .hidden1 and .hidden_dir at depth 1 - - -def test_view_large_file_with_truncation(editor, tmp_path): - editor, _ = editor - # Create a large file to trigger truncation - large_file = tmp_path / "large_test.txt" - large_content = "Line 1\n" * 16000 # 16000 lines should trigger truncation - large_file.write_text(large_content) - - result = editor(command="view", path=str(large_file)) - assert isinstance(result, CLIResult) - assert FILE_CONTENT_TRUNCATED_NOTICE in result.output - - -def test_validate_path_suggests_absolute_path(editor, tmp_path): - editor, test_file = editor - - # Since the editor fixture doesn't set workspace_root, we should not get a suggestion - relative_path = test_file.name # This is a relative path - with pytest.raises(EditorToolParameterInvalidError) as exc_info: - editor(command="view", path=relative_path) - error_message = str(exc_info.value.message) - assert "The path should be an absolute path" in error_message - assert "Maybe you meant" not in error_message - - # Now create an editor with workspace_root - workspace_editor = OHEditor(workspace_root=str(test_file.parent)) - - # We should get a suggestion now - with pytest.raises(EditorToolParameterInvalidError) as exc_info: - workspace_editor(command="view", path=relative_path) - error_message = str(exc_info.value.message) - assert "The path should be an absolute path" in error_message - assert "Maybe you meant" in error_message - suggested_path = error_message.split("Maybe you meant ")[1].strip("?") - assert Path(suggested_path).is_absolute() - assert str(test_file.parent) in suggested_path diff --git a/pkg/hanzo-aci/tests/unit/editor/test_encoding.py b/pkg/hanzo-aci/tests/unit/editor/test_encoding.py deleted file mode 100644 index 0fab44644..000000000 --- a/pkg/hanzo-aci/tests/unit/editor/test_encoding.py +++ /dev/null @@ -1,253 +0,0 @@ -"""Unit tests for the encoding module.""" - -import os -import tempfile -import time -from pathlib import Path -from unittest.mock import patch - -import pytest -from cachetools import LRUCache - -from dev_aci.editor.encoding import EncodingManager, with_encoding - - -@pytest.fixture -def temp_file(): - """Create a temporary file for testing.""" - fd, path = tempfile.mkstemp() - os.close(fd) - yield Path(path) - try: - os.unlink(path) - except FileNotFoundError: - pass - - -@pytest.fixture -def encoding_manager(): - """Create an EncodingManager instance for testing.""" - return EncodingManager() - - -def test_init(encoding_manager): - """Test initialization of EncodingManager.""" - assert isinstance(encoding_manager, EncodingManager) - assert isinstance(encoding_manager._encoding_cache, LRUCache) - assert encoding_manager.default_encoding == "utf-8" - assert encoding_manager.confidence_threshold == 0.9 - - -def test_detect_encoding_nonexistent_file(encoding_manager): - """Test detecting encoding for a nonexistent file.""" - nonexistent_path = Path("/nonexistent/file.txt") - encoding = encoding_manager.detect_encoding(nonexistent_path) - assert encoding == encoding_manager.default_encoding - - -def test_detect_encoding_utf8(encoding_manager, temp_file): - """Test detecting UTF-8 encoding.""" - # Create a UTF-8 encoded file - with open(temp_file, "w", encoding="utf-8") as f: - f.write("Hello, world! UTF-8 encoded text.") - - encoding = encoding_manager.detect_encoding(temp_file) - assert encoding.lower() in ("utf-8", "ascii") - - -def test_detect_encoding_utf8_with_icon(encoding_manager, temp_file): - """Test detecting UTF-8 encoding with a word and an emoji.""" - # Create a UTF-8 encoded file with a single word and an emoji - with open(temp_file, "w", encoding="utf-8") as f: - f.write("Hello ๐Ÿ˜Š") - - encoding = encoding_manager.detect_encoding(temp_file) - assert encoding.lower() == "utf-8" - - -def test_detect_encoding_cp1251(encoding_manager, temp_file): - """Test detecting CP1251 encoding.""" - # Create a CP1251 encoded file with Cyrillic characters - with open(temp_file, "wb") as f: - f.write("ะŸั€ะธะฒะตั‚, ะผะธั€! ะขะตะบัั‚ ะฒ ะบะพะดะธั€ะพะฒะบะต CP1251.".encode("cp1251")) - - encoding = encoding_manager.detect_encoding(temp_file) - assert encoding.lower() in ("windows-1251", "cp1251") - - -def test_detect_encoding_low_confidence(encoding_manager, temp_file): - """Test fallback to default encoding when confidence is low.""" - # Create a file with mixed encodings to confuse the detector - with open(temp_file, "wb") as f: - f.write(b"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f") - - # Mock chardet.detect to return low confidence - with patch( - "charset_normalizer.detect", - return_value={"encoding": "ascii", "confidence": 0.3}, - ): - encoding = encoding_manager.detect_encoding(temp_file) - assert encoding == encoding_manager.default_encoding - - -def test_detect_encoding_none_result(encoding_manager, temp_file): - """Test fallback to default encoding when chardet returns None for encoding.""" - with open(temp_file, "wb") as f: - f.write(b"\x00\x01\x02\x03") # Binary data - - # Mock chardet.detect to return None for encoding - with patch( - "charset_normalizer.detect", return_value={"encoding": None, "confidence": 0.0} - ): - encoding = encoding_manager.detect_encoding(temp_file) - assert encoding == encoding_manager.default_encoding - - -def test_get_encoding_cache_hit(encoding_manager, temp_file): - """Test that get_encoding uses cached values when available.""" - # Create a file - with open(temp_file, "w", encoding="utf-8") as f: - f.write("Hello, world!") - - # First call should detect encoding - with patch.object( - encoding_manager, "detect_encoding", return_value="utf-8" - ) as mock_detect: - encoding1 = encoding_manager.get_encoding(temp_file) - assert encoding1 == "utf-8" - mock_detect.assert_called_once() - - # Second call should use cache - with patch.object( - encoding_manager, "detect_encoding", return_value="utf-8" - ) as mock_detect: - encoding2 = encoding_manager.get_encoding(temp_file) - assert encoding2 == "utf-8" - mock_detect.assert_not_called() - - -def test_get_encoding_cache_invalidation(encoding_manager, temp_file): - """Test that cache is invalidated when file is modified.""" - # Create a file - with open(temp_file, "w", encoding="utf-8") as f: - f.write("Hello, world!") - - # First call should detect encoding - encoding1 = encoding_manager.get_encoding(temp_file) - assert encoding1.lower() in ("utf-8", "ascii") - - # Wait a moment to ensure modification time will be different - time.sleep(0.1) - - # Modify the file - with open(temp_file, "w", encoding="utf-8") as f: - f.write("Modified content") - - # Mock detect_encoding to verify it's called again - with patch.object( - encoding_manager, "detect_encoding", return_value="utf-8" - ) as mock_detect: - encoding2 = encoding_manager.get_encoding(temp_file) - assert encoding2 == "utf-8" - mock_detect.assert_called_once() - - -def test_with_encoding_decorator(): - """Test the with_encoding decorator.""" - - # Create a mock class with a method that will be decorated - class MockEditor: - def __init__(self): - self._encoding_manager = EncodingManager() - - @with_encoding - def read_file(self, path, encoding="utf-8"): - return f"Reading file with encoding: {encoding}" - - editor = MockEditor() - - # Test with a directory - with patch.object(Path, "is_dir", return_value=True): - with patch.object( - editor._encoding_manager, "get_encoding" - ) as mock_get_encoding: - result = editor.read_file(Path("/some/dir")) - assert result == "Reading file with encoding: utf-8" - mock_get_encoding.assert_not_called() - - # Test with a nonexistent file - with patch.object(Path, "is_dir", return_value=False): - with patch.object(Path, "exists", return_value=False): - result = editor.read_file(Path("/nonexistent/file.txt")) - assert ( - result - == f"Reading file with encoding: {editor._encoding_manager.default_encoding}" - ) - - # Test with an existing file - with patch.object(Path, "is_dir", return_value=False): - with patch.object(Path, "exists", return_value=True): - with patch.object( - editor._encoding_manager, "get_encoding", return_value="latin-1" - ): - result = editor.read_file(Path("/existing/file.txt")) - assert result == "Reading file with encoding: latin-1" - - -def test_with_encoding_respects_provided_encoding(): - """Test that the with_encoding decorator respects explicitly provided encoding.""" - # The current implementation of with_encoding always calls get_encoding - # but doesn't override the provided encoding if it exists in kwargs - - class MockEditor: - def __init__(self): - self._encoding_manager = EncodingManager() - - @with_encoding - def read_file(self, path, encoding="utf-8"): - return f"Reading file with encoding: {encoding}" - - editor = MockEditor() - - # Test with explicitly provided encoding - with patch.object(Path, "is_dir", return_value=False): - with patch.object(Path, "exists", return_value=True): - with patch.object( - editor._encoding_manager, - "get_encoding", - return_value="detected-encoding", - ): - result = editor.read_file(Path("/some/file.txt"), encoding="iso-8859-1") - # The provided encoding should be used, not the detected one - assert result == "Reading file with encoding: iso-8859-1" - - -def test_cache_size_limit(encoding_manager, temp_file): - """Test that the cache size is limited and LRU entries are evicted.""" - # Create a small cache for testing - encoding_manager = EncodingManager(max_cache_size=3) - - # Create a file - with open(temp_file, "w", encoding="utf-8") as f: - f.write("Test file") - - # Create 4 different paths (using the same file but with different paths) - paths = [Path(f"{temp_file}.{i}") for i in range(4)] - - # Mock exists and getmtime to return consistent values - with patch.object(Path, "exists", return_value=True): - with patch.object(os.path, "getmtime", return_value=123456): - with patch.object( - encoding_manager, "detect_encoding", return_value="utf-8" - ): - # Access paths in order 0, 1, 2, 3 - for i, path in enumerate(paths): - encoding_manager.get_encoding(path) - - # After adding 4th item, the cache should still have 3 items - assert len(encoding_manager._encoding_cache) == 3 - # Path 0 should have been evicted (LRU) - assert str(paths[0]) not in encoding_manager._encoding_cache - # Paths 1, 2, 3 should still be in the cache - for j in range(1, 4): - assert str(paths[j]) in encoding_manager._encoding_cache diff --git a/pkg/hanzo-aci/tests/unit/editor/test_file_cache.py b/pkg/hanzo-aci/tests/unit/editor/test_file_cache.py deleted file mode 100644 index a94c44ca1..000000000 --- a/pkg/hanzo-aci/tests/unit/editor/test_file_cache.py +++ /dev/null @@ -1,206 +0,0 @@ -import os -import tempfile - -import pytest - -from dev_aci.editor import FileCache - - -@pytest.fixture -def file_cache(): - with tempfile.TemporaryDirectory() as temp_dir: - cache = FileCache(temp_dir) - yield cache - cache.clear() - - -def test_init(file_cache): - assert isinstance(file_cache, FileCache) - assert file_cache.directory.exists() - assert file_cache.directory.is_dir() - - -def test_set_and_get(file_cache): - file_cache.set("test_key", "test_value") - assert file_cache.get("test_key") == "test_value" - - -def test_get_nonexistent_key(file_cache): - assert file_cache.get("nonexistent_key") is None - assert file_cache.get("nonexistent_key", "default") == "default" - - -def test_set_nested_key(file_cache): - file_cache.set("folder/nested/key", "nested_value") - assert file_cache.get("folder/nested/key") == "nested_value" - - -def test_set_overwrite(file_cache): - file_cache.set("test_key", "initial_value") - file_cache.set("test_key", "new_value") - assert file_cache.get("test_key") == "new_value" - - -def test_delete(file_cache): - file_cache.set("test_key", "test_value") - file_cache.delete("test_key") - assert file_cache.get("test_key") is None - - -def test_delete_nonexistent_key(file_cache): - file_cache.delete("nonexistent_key") # Should not raise an exception - - -def test_delete_nested_key(file_cache): - file_cache.set("folder/nested/key", "nested_value") - file_cache.delete("folder/nested/key") - assert file_cache.get("folder/nested/key") is None - - -def test_clear(file_cache): - file_cache.set("key1", "value1") - file_cache.set("key2", "value2") - file_cache.set("folder/key3", "value3") - file_cache.clear() - assert len(file_cache) == 0 - assert file_cache.get("key1") is None - assert file_cache.get("key2") is None - assert file_cache.get("folder/key3") is None - - -def test_contains(file_cache): - file_cache.set("test_key", "test_value") - assert "test_key" in file_cache - assert "nonexistent_key" not in file_cache - - -def test_len(file_cache): - assert len(file_cache) == 0 - file_cache.set("key1", "value1") - file_cache.set("key2", "value2") - assert len(file_cache) == 2 - file_cache.set("folder/key3", "value3") - assert len(file_cache) == 3 - - -def test_iter(file_cache): - file_cache.set("key1", "value1") - file_cache.set("key2", "value2") - file_cache.set("folder/key3", "value3") - keys = set(file_cache) - assert keys == {"key1", "key2", "folder/key3"} - - -def test_large_value(file_cache): - large_value = "x" * 1024 * 1024 # 1 MB string - file_cache.set("large_key", large_value) - assert file_cache.get("large_key") == large_value - - -def test_many_items(file_cache): - for i in range(1000): - file_cache.set(f"key_{i}", f"value_{i}") - - assert len(file_cache) == 1000 - for i in range(1000): - assert file_cache.get(f"key_{i}") == f"value_{i}" - - -def test_nested_structure(file_cache): - file_cache.set("folder1/file1", "content1") - file_cache.set("folder1/file2", "content2") - file_cache.set("folder2/subfolder/file3", "content3") - - assert file_cache.get("folder1/file1") == "content1" - assert file_cache.get("folder1/file2") == "content2" - assert file_cache.get("folder2/subfolder/file3") == "content3" - assert len(file_cache) == 3 - - -def test_clear_nested_structure(file_cache): - file_cache.set("folder1/file1", "content1") - file_cache.set("folder1/file2", "content2") - file_cache.set("folder2/subfolder/file3", "content3") - file_cache.clear() - - assert len(file_cache) == 0 - assert list(file_cache) == [] - assert not any(file_cache.directory.iterdir()) - - -def test_delete_removes_empty_directories(file_cache): - file_cache.set("folder1/subfolder/file1", "content1") - file_cache.delete("folder1/subfolder/file1") - - assert not (file_cache.directory / "folder1" / "subfolder").exists() - assert not (file_cache.directory / "folder1").exists() - - -def test_size_limit(): - with tempfile.TemporaryDirectory() as temp_dir: - cache = FileCache(temp_dir, size_limit=100) - val1 = "x" * 50 - val2 = "y" * 60 - cache.set("key1", val1) - cache.set("key2", val2) - - assert len(val1.encode("utf-8")) <= 100 - assert len(val1.encode("utf-8") + val2.encode("utf-8")) > 100 - - val3 = "z" * 40 - # This should cause key1 to be evicted - cache.set("key3", val3) # 40 bytes - - assert "key1" not in cache - assert "key2" in cache - assert "key3" in cache - - -def test_file_permissions(file_cache): - file_cache.set("test_key", "test_value") - file_path = file_cache._get_file_path("test_key") - assert os.access(file_path, os.R_OK) - assert os.access(file_path, os.W_OK) - assert not os.access(file_path, os.X_OK) - - -def test_unicode_keys_and_values(file_cache): - unicode_key = "รผรฑรฎรงรธdรฉ_kรซy" - unicode_value = "รผรฑรฎรงรธdรฉ_vรฅlรผรฉ" - file_cache.set(unicode_key, unicode_value) - assert file_cache.get(unicode_key) == unicode_value - - -def test_empty_string_as_key_and_value(file_cache): - file_cache.set("", "") - assert file_cache.get("") == "" - - -def test_none_as_value(file_cache): - file_cache.set("none_key", None) - assert file_cache.get("none_key") is None - - -def test_special_characters_in_key(file_cache): - special_key = "!@#$%^&*()_+{}[]|\\:;\"'<>,.?/~`" - file_cache.set(special_key, "special_value") - assert file_cache.get(special_key) == "special_value" - - -def test_size_limit_with_empty_key(): - with tempfile.TemporaryDirectory() as temp_dir: - cache = FileCache(temp_dir, size_limit=100) # 100 bytes limit - cache.set("", "x" * 50) # 50 bytes with empty key - cache.set("key2", "y" * 60) # 60 bytes - - # This should cause the empty key to be evicted - cache.set("key3", "z" * 40) # 40 bytes - - assert "" not in cache - assert "key2" in cache - assert "key3" in cache - assert cache.get("key2") == "y" * 60 - assert cache.get("key3") == "z" * 40 - - -# Add more tests as needed diff --git a/pkg/hanzo-aci/tests/unit/linter/conftest.py b/pkg/hanzo-aci/tests/unit/linter/conftest.py deleted file mode 100644 index bf2312c9a..000000000 --- a/pkg/hanzo-aci/tests/unit/linter/conftest.py +++ /dev/null @@ -1,75 +0,0 @@ -import pytest - - -@pytest.fixture -def syntax_error_py_file(tmp_path): - file_content = """ - def foo(): - print("Hello, World!") - print("Wrong indent") - foo( - """ - file_path = tmp_path / "test_file.py" - file_path.write_text(file_content) - return str(file_path) - - -@pytest.fixture -def wrongly_indented_py_file(tmp_path): - file_content = """ - def foo(): - print("Hello, World!") - """ - file_path = tmp_path / "test_file.py" - file_path.write_text(file_content) - return str(file_path) - - -@pytest.fixture -def simple_correct_py_file(tmp_path): - file_content = 'print("Hello, World!")\n' - file_path = tmp_path / "test_file.py" - file_path.write_text(file_content) - return str(file_path) - - -@pytest.fixture -def simple_correct_py_func_def(tmp_path): - file_content = """def foo(): - print("Hello, World!") -foo() -""" - file_path = tmp_path / "test_file.py" - file_path.write_text(file_content) - return str(file_path) - - -@pytest.fixture -def simple_correct_ruby_file(tmp_path): - file_content = """def foo - puts "Hello, World!" -end -foo -""" - file_path = tmp_path / "test_file.rb" - file_path.write_text(file_content) - return str(file_path) - - -@pytest.fixture -def simple_incorrect_ruby_file(tmp_path): - file_content = """def foo(): - print("Hello, World!") -foo() -""" - file_path = tmp_path / "test_file.rb" - file_path.write_text(file_content) - return str(file_path) - - -@pytest.fixture -def parenthesis_incorrect_ruby_file(tmp_path): - file_content = """def print_hello_world()\n puts 'Hello World'\n""" - file_path = tmp_path / "test_file.rb" - file_path.write_text(file_content) - return str(file_path) diff --git a/pkg/hanzo-aci/tests/unit/linter/test_lint_diff.py b/pkg/hanzo-aci/tests/unit/linter/test_lint_diff.py deleted file mode 100644 index 68a1bea3f..000000000 --- a/pkg/hanzo-aci/tests/unit/linter/test_lint_diff.py +++ /dev/null @@ -1,411 +0,0 @@ -from dev_aci.linter import DefaultLinter, LintResult -from dev_aci.utils.diff import get_diff, parse_diff - -OLD_CONTENT = """ -def foo(): - print("Hello, World!") - x = UNDEFINED_VARIABLE -foo() -""" - -NEW_CONTENT_V1 = OLD_CONTENT + """ -def new_function_that_causes_error(): - y = ANOTHER_UNDEFINED_VARIABLE -""" - -NEW_CONTENT_V2 = """ -def foo(): - print("Hello, World!") - x = UNDEFINED_VARIABLE - y = ANOTHER_UNDEFINED_VARIABLE -foo() -""" - - -def test_get_and_parse_diff(tmp_path): - diff = get_diff(OLD_CONTENT, NEW_CONTENT_V1, "test.py") - print(diff) - assert diff == """ ---- test.py -+++ test.py -@@ -6,0 +7,3 @@ -+def new_function_that_causes_error(): -+ y = ANOTHER_UNDEFINED_VARIABLE -+ -""".strip() - - print( - "\n".join( - [f"{i + 1}|{line}" for i, line in enumerate(NEW_CONTENT_V1.splitlines())] - ) - ) - changes = parse_diff(diff) - assert len(changes) == 3 - assert ( - changes[0].old is None - and changes[0].new == 7 - and changes[0].line == "def new_function_that_causes_error():" - ) - assert ( - changes[1].old is None - and changes[1].new == 8 - and changes[1].line == " y = ANOTHER_UNDEFINED_VARIABLE" - ) - assert changes[2].old is None and changes[2].new == 9 and changes[2].line == "" - - -def test_lint_with_diff_append(tmp_path): - with open(tmp_path / "old.py", "w") as f: - f.write(OLD_CONTENT) - with open(tmp_path / "new.py", "w") as f: - f.write(NEW_CONTENT_V1) - - linter = DefaultLinter() - result: list[LintResult] = linter.lint_file_diff( - str(tmp_path / "old.py"), - str(tmp_path / "new.py"), - ) - print(result) - assert len(result) == 1 - assert ( - result[0].line == 8 - and result[0].column == 9 - and result[0].message == "F821 undefined name 'ANOTHER_UNDEFINED_VARIABLE'" - ) - - -def test_lint_with_diff_insert(tmp_path): - with open(tmp_path / "old.py", "w") as f: - f.write(OLD_CONTENT) - with open(tmp_path / "new.py", "w") as f: - f.write(NEW_CONTENT_V2) - - linter = DefaultLinter() - result: list[LintResult] = linter.lint_file_diff( - str(tmp_path / "old.py"), - str(tmp_path / "new.py"), - ) - assert len(result) == 1 - assert ( - result[0].line == 5 - and result[0].column == 9 - and result[0].message == "F821 undefined name 'ANOTHER_UNDEFINED_VARIABLE'" - ) - - -def test_lint_with_multiple_changes_and_errors(tmp_path): - old_content = """ -def foo(): - print("Hello, World!") - x = 10 -foo() -""" - new_content = """ -def foo(): - print("Hello, World!") - x = UNDEFINED_VARIABLE - y = 20 - -def bar(): - z = ANOTHER_UNDEFINED_VARIABLE - return z + 1 - -foo() -bar() -""" - with open(tmp_path / "old.py", "w") as f: - f.write(old_content) - with open(tmp_path / "new.py", "w") as f: - f.write(new_content) - - linter = DefaultLinter() - result: list[LintResult] = linter.lint_file_diff( - str(tmp_path / "old.py"), - str(tmp_path / "new.py"), - ) - assert len(result) == 2 - assert ( - result[0].line == 4 - and result[0].column == 9 - and result[0].message == "F821 undefined name 'UNDEFINED_VARIABLE'" - ) - assert ( - result[1].line == 8 - and result[1].column == 9 - and result[1].message == "F821 undefined name 'ANOTHER_UNDEFINED_VARIABLE'" - ) - - -def test_lint_with_introduced_and_fixed_errors(tmp_path): - old_content = """ -x = UNDEFINED_VARIABLE -y = 10 -""" - new_content = """ -x = 5 -y = ANOTHER_UNDEFINED_VARIABLE -z = UNDEFINED_VARIABLE -""" - with open(tmp_path / "old.py", "w") as f: - f.write(old_content) - with open(tmp_path / "new.py", "w") as f: - f.write(new_content) - - linter = DefaultLinter() - result: list[LintResult] = linter.lint_file_diff( - str(tmp_path / "old.py"), - str(tmp_path / "new.py"), - ) - assert len(result) == 2 - assert ( - result[0].line == 3 - and result[0].column == 5 - and result[0].message == "F821 undefined name 'ANOTHER_UNDEFINED_VARIABLE'" - ) - assert ( - result[1].line == 4 - and result[1].column == 5 - and result[1].message == "F821 undefined name 'UNDEFINED_VARIABLE'" - ) - - -def test_lint_with_multiline_changes(tmp_path): - old_content = """ -def complex_function(a, b, c): - return (a + - b + - c) -""" - new_content = """ -def complex_function(a, b, c): - return (a + - UNDEFINED_VARIABLE + - b + - c) -""" - with open(tmp_path / "old.py", "w") as f: - f.write(old_content) - with open(tmp_path / "new.py", "w") as f: - f.write(new_content) - - linter = DefaultLinter() - result: list[LintResult] = linter.lint_file_diff( - str(tmp_path / "old.py"), - str(tmp_path / "new.py"), - ) - assert len(result) == 1 - assert ( - result[0].line == 4 - and result[0].column == 13 - and result[0].message == "F821 undefined name 'UNDEFINED_VARIABLE'" - ) - - -def test_lint_with_syntax_error(tmp_path): - old_content = """ -def foo(): - print("Hello, World!") -""" - new_content = """ -def foo(): - print("Hello, World!" -""" - with open(tmp_path / "old.py", "w") as f: - f.write(old_content) - with open(tmp_path / "new.py", "w") as f: - f.write(new_content) - - linter = DefaultLinter() - result: list[LintResult] = linter.lint_file_diff( - str(tmp_path / "old.py"), - str(tmp_path / "new.py"), - ) - assert len(result) == 1 - assert ( - result[0].line == 3 - and result[0].column == 11 - and result[0].message == "E999 SyntaxError: '(' was never closed" - ) - - -def test_lint_with_docstring_changes(tmp_path): - old_content = ''' -def foo(): - """This is a function.""" - print("Hello, World!") -''' - new_content = ''' -def foo(): - """ - This is a function. - It now has a multi-line docstring with an UNDEFINED_VARIABLE. - """ - print("Hello, World!") -''' - with open(tmp_path / "old.py", "w") as f: - f.write(old_content) - with open(tmp_path / "new.py", "w") as f: - f.write(new_content) - - linter = DefaultLinter() - result: list[LintResult] = linter.lint_file_diff( - str(tmp_path / "old.py"), - str(tmp_path / "new.py"), - ) - assert len(result) == 0 # Linter should ignore changes in docstrings - - -def test_lint_with_multiple_errors_on_same_line(tmp_path): - old_content = """ -def foo(): - print("Hello, World!") - x = 10 -foo() -""" - new_content = """ -def foo(): - print("Hello, World!") - x = UNDEFINED_VARIABLE + ANOTHER_UNDEFINED_VARIABLE -foo() -""" - with open(tmp_path / "old.py", "w") as f: - f.write(old_content) - with open(tmp_path / "new.py", "w") as f: - f.write(new_content) - - linter = DefaultLinter() - result: list[LintResult] = linter.lint_file_diff( - str(tmp_path / "old.py"), - str(tmp_path / "new.py"), - ) - print(result) - assert len(result) == 2 - assert ( - result[0].line == 4 - and result[0].column == 9 - and result[0].message == "F821 undefined name 'UNDEFINED_VARIABLE'" - ) - assert ( - result[1].line == 4 - and result[1].column == 30 - and result[1].message == "F821 undefined name 'ANOTHER_UNDEFINED_VARIABLE'" - ) - - -def test_parse_diff_with_empty_patch(): - diff_patch = "" - changes = parse_diff(diff_patch) - assert len(changes) == 0 - - -def test_lint_file_diff_ignore_existing_errors(tmp_path): - """Make sure we allow edits as long as it does not introduce new errors. - - In other words, we don't care about existing linting errors. Although they might be - real syntax issues, sometimes they are just false positives, or errors that - we don't care about. - """ - content = """def some_valid_but_weird_function(): - # this function is legitimate, yet static analysis tools like flake8 - # reports 'F821 undefined name' - if 'variable' in locals(): - print(variable) -def some_wrong_but_unused_function(): - # this function has a linting error, but it is not modified by us, and - # who knows, this function might be completely dead code - x = 1 -def sum(a, b): - return a - b -""" - new_content = content.replace(" return a - b", " return a + b") - temp_file_old_path = tmp_path / "problematic-file-test.py" - temp_file_old_path.write_text(content) - temp_file_new_path = tmp_path / "problematic-file-test-new.py" - temp_file_new_path.write_text(new_content) - - linter = DefaultLinter() - result: list[LintResult] = linter.lint_file_diff( - str(temp_file_old_path), - str(temp_file_new_path), - ) - assert len(result) == 0 # no new errors introduced - - -def test_lint_file_diff_catch_new_errors_in_edits(tmp_path): - """Make sure we catch new linting errors in our edit chunk. - - At the same time, ignore old linting errors (in this case, the old linting error is - a false positive). - """ - content = """def some_valid_but_weird_function(): - # this function is legitimate, yet static analysis tools like flake8 - # reports 'F821 undefined name' - if 'variable' in locals(): - print(variable) -def sum(a, b): - return a - b -""" - - temp_file_old_path = tmp_path / "problematic-file-test.py" - temp_file_old_path.write_text(content) - new_content = content.replace(" return a - b", " return a + variable") - temp_file_new_path = tmp_path / "problematic-file-test-new.py" - temp_file_new_path.write_text(new_content) - - linter = DefaultLinter() - result: list[LintResult] = linter.lint_file_diff( - str(temp_file_old_path), - str(temp_file_new_path), - ) - print(result) - assert len(result) == 1 - assert ( - result[0].line == 7 - and result[0].column == 16 - and result[0].message == "F821 undefined name 'variable'" - ) - - -def test_lint_file_diff_catch_new_errors_outside_edits(tmp_path): - """Make sure we catch new linting errors induced by our edits. - - Even though the error itself is not in the edit chunk. - """ - content = """def valid_func1(): - print(my_sum(1, 2)) -def my_sum(a, b): - return a - b -def valid_func2(): - print(my_sum(0, 0)) -""" - # Add 100 lines of invalid code, which linter shall ignore - # because they are not being edited. For testing purpose, we - # must add these existing linting errors, otherwise the pre-edit - # linting would pass, and thus there won't be any comparison - # between pre-edit and post-edit linting. - for _ in range(100): - content += "\ninvalid_func()" - - temp_file_old_path = tmp_path / "problematic-file-test.py" - temp_file_old_path.write_text(content) - - new_content = content.replace("def my_sum(a, b):", "def my_sum2(a, b):") - temp_file_new_path = tmp_path / "problematic-file-test-new.py" - temp_file_new_path.write_text(new_content) - - linter = DefaultLinter() - result: list[LintResult] = linter.lint_file_diff( - str(temp_file_old_path), - str(temp_file_new_path), - ) - assert len(result) == 2 - assert ( - result[0].line == 2 - and result[0].column == 11 - and result[0].message == "F821 undefined name 'my_sum'" - ) - assert ( - result[1].line == 6 - and result[1].column == 11 - and result[1].message == "F821 undefined name 'my_sum'" - ) diff --git a/pkg/hanzo-aci/tests/unit/linter/test_python_linter.py b/pkg/hanzo-aci/tests/unit/linter/test_python_linter.py deleted file mode 100644 index f8f12ae90..000000000 --- a/pkg/hanzo-aci/tests/unit/linter/test_python_linter.py +++ /dev/null @@ -1,84 +0,0 @@ -from dev_aci.linter import DefaultLinter, LintResult -from dev_aci.linter.impl.python import ( - PythonLinter, - flake_lint, - python_compile_lint, -) - - -def test_wrongly_indented_py_file(wrongly_indented_py_file): - # Test Python linter - linter = PythonLinter() - assert ".py" in linter.supported_extensions - result = linter.lint(wrongly_indented_py_file) - print(result) - assert isinstance(result, list) and len(result) == 1 - assert result[0] == LintResult( - file=wrongly_indented_py_file, - line=2, - column=5, - message="E999 IndentationError: unexpected indent", - ) - print(result[0].visualize()) - assert result[0].visualize() == ( - "1|\n" - "\033[91m2| def foo():\033[0m\n" - " ^ ERROR HERE: E999 IndentationError: unexpected indent\n" - '3| print("Hello, World!")\n' - "4|" - ) - - # General linter should have same result as Python linter - # bc it uses PythonLinter under the hood - general_linter = DefaultLinter() - assert ".py" in general_linter.supported_extensions - result = general_linter.lint(wrongly_indented_py_file) - assert result == linter.lint(wrongly_indented_py_file) - - # Test flake8_lint - assert result == flake_lint(wrongly_indented_py_file) - - # Test python_compile_lint - compile_result = python_compile_lint(wrongly_indented_py_file) - assert isinstance(compile_result, list) and len(compile_result) == 1 - assert compile_result[0] == LintResult( - file=wrongly_indented_py_file, line=2, column=4, message="unexpected indent" - ) - - -def test_simple_correct_py_file(simple_correct_py_file): - linter = PythonLinter() - assert ".py" in linter.supported_extensions - result = linter.lint(simple_correct_py_file) - assert result == [] - - general_linter = DefaultLinter() - assert ".py" in general_linter.supported_extensions - result = general_linter.lint(simple_correct_py_file) - assert result == linter.lint(simple_correct_py_file) - - # Test python_compile_lint - compile_result = python_compile_lint(simple_correct_py_file) - assert compile_result == [] - - # Test flake_lint - flake_result = flake_lint(simple_correct_py_file) - assert flake_result == [] - - -def test_simple_correct_py_func_def(simple_correct_py_func_def): - linter = PythonLinter() - result = linter.lint(simple_correct_py_func_def) - assert result == [] - - general_linter = DefaultLinter() - assert ".py" in general_linter.supported_extensions - result = general_linter.lint(simple_correct_py_func_def) - assert result == linter.lint(simple_correct_py_func_def) - - # Test flake_lint - assert result == flake_lint(simple_correct_py_func_def) - - # Test python_compile_lint - compile_result = python_compile_lint(simple_correct_py_func_def) - assert compile_result == [] diff --git a/pkg/hanzo-aci/tests/unit/linter/test_treesitter_linter.py b/pkg/hanzo-aci/tests/unit/linter/test_treesitter_linter.py deleted file mode 100644 index a3d66ebbd..000000000 --- a/pkg/hanzo-aci/tests/unit/linter/test_treesitter_linter.py +++ /dev/null @@ -1,104 +0,0 @@ -from dev_aci.linter import DefaultLinter, LintResult -from dev_aci.linter.impl.treesitter import TreesitterBasicLinter - - -def test_syntax_error_py_file(syntax_error_py_file): - linter = TreesitterBasicLinter() - result = linter.lint(syntax_error_py_file) - print(result) - assert isinstance(result, list) and len(result) == 1 - assert result[0] == LintResult( - file=syntax_error_py_file, - line=5, - column=5, - message="Syntax error", - ) - - assert result[0].visualize() == ( - "2| def foo():\n" - '3| print("Hello, World!")\n' - '4| print("Wrong indent")\n' - "\033[91m5| foo(\033[0m\n" # color red - " ^ ERROR HERE: Syntax error\n" - "6|" - ) - print(result[0].visualize()) - - general_linter = DefaultLinter() - general_result = general_linter.lint(syntax_error_py_file) - # NOTE: general linter returns different result - # because it uses flake8 first, which is different from treesitter - assert general_result != result - - -def test_simple_correct_ruby_file(simple_correct_ruby_file): - linter = TreesitterBasicLinter() - result = linter.lint(simple_correct_ruby_file) - assert isinstance(result, list) and len(result) == 0 - - # Test that the general linter also returns the same result - general_linter = DefaultLinter() - general_result = general_linter.lint(simple_correct_ruby_file) - assert general_result == result - - -def test_simple_incorrect_ruby_file(simple_incorrect_ruby_file): - linter = TreesitterBasicLinter() - result = linter.lint(simple_incorrect_ruby_file) - print(result) - assert isinstance(result, list) and len(result) == 2 - assert result[0] == LintResult( - file=simple_incorrect_ruby_file, - line=1, - column=1, - message="Syntax error", - ) - print(result[0].visualize()) - assert result[0].visualize() == ( - "\033[91m1|def foo():\033[0m\n" # color red - " ^ ERROR HERE: Syntax error\n" - '2| print("Hello, World!")\n' - "3|foo()" - ) - assert result[1] == LintResult( - file=simple_incorrect_ruby_file, - line=1, - column=10, - message="Syntax error", - ) - print(result[1].visualize()) - assert result[1].visualize() == ( - "\033[91m1|def foo():\033[0m\n" # color red - " ^ ERROR HERE: Syntax error\n" - '2| print("Hello, World!")\n' - "3|foo()" - ) - - # Test that the general linter also returns the same result - general_linter = DefaultLinter() - general_result = general_linter.lint(simple_incorrect_ruby_file) - assert general_result == result - - -def test_parenthesis_incorrect_ruby_file(parenthesis_incorrect_ruby_file): - linter = TreesitterBasicLinter() - result = linter.lint(parenthesis_incorrect_ruby_file) - print(result) - assert isinstance(result, list) and len(result) == 1 - assert result[0] == LintResult( - file=parenthesis_incorrect_ruby_file, - line=1, - column=1, - message="Syntax error", - ) - print(result[0].visualize()) - assert result[0].visualize() == ( - "\033[91m1|def print_hello_world()\033[0m\n" - " ^ ERROR HERE: Syntax error\n" - "2| puts 'Hello World'" - ) - - # Test that the general linter also returns the same result - general_linter = DefaultLinter() - general_result = general_linter.lint(parenthesis_incorrect_ruby_file) - assert general_result == result diff --git a/pkg/hanzo-aci/tests/unit/linter/test_visualize.py b/pkg/hanzo-aci/tests/unit/linter/test_visualize.py deleted file mode 100644 index 87e2feaed..000000000 --- a/pkg/hanzo-aci/tests/unit/linter/test_visualize.py +++ /dev/null @@ -1,86 +0,0 @@ -from unittest.mock import mock_open, patch - -import pytest - -from dev_aci.linter.base import LintResult - - -@pytest.fixture -def mock_file_content(): - return "\n".join([f"Line {i}" for i in range(1, 21)]) - - -def test_visualize_standard_case(mock_file_content): - lint_result = LintResult( - file="test_file.py", line=10, column=5, message="Test error message" - ) - - with patch("builtins.open", mock_open(read_data=mock_file_content)): - result = lint_result.visualize(half_window=3) - - expected_output = ( - " 7|Line 7\n" - " 8|Line 8\n" - " 9|Line 9\n" - "\033[91m10|Line 10\033[0m\n" - f" {' ' * lint_result.column}^ ERROR HERE: Test error message\n" - "11|Line 11\n" - "12|Line 12\n" - "13|Line 13" - ) - - assert result == expected_output - - -def test_visualize_small_window(mock_file_content): - lint_result = LintResult( - file="test_file.py", line=10, column=5, message="Test error message" - ) - - with patch("builtins.open", mock_open(read_data=mock_file_content)): - result = lint_result.visualize(half_window=1) - - expected_output = ( - " 9|Line 9\n" - "\033[91m10|Line 10\033[0m\n" - f" {' ' * lint_result.column}^ ERROR HERE: Test error message\n" - "11|Line 11" - ) - - assert result == expected_output - - -def test_visualize_error_at_start(mock_file_content): - lint_result = LintResult( - file="test_file.py", line=1, column=3, message="Start error" - ) - - with patch("builtins.open", mock_open(read_data=mock_file_content)): - result = lint_result.visualize(half_window=2) - - expected_output = ( - "\033[91m 1|Line 1\033[0m\n" - f" {' ' * lint_result.column}^ ERROR HERE: Start error\n" - " 2|Line 2\n" - " 3|Line 3" - ) - - assert result == expected_output - - -def test_visualize_error_at_end(mock_file_content): - lint_result = LintResult( - file="test_file.py", line=20, column=1, message="End error" - ) - - with patch("builtins.open", mock_open(read_data=mock_file_content)): - result = lint_result.visualize(half_window=2) - - expected_output = ( - "18|Line 18\n" - "19|Line 19\n" - "\033[91m20|Line 20\033[0m\n" - f" {' ' * lint_result.column}^ ERROR HERE: End error" - ) - - assert result == expected_output diff --git a/pkg/hanzo-aci/tests/unit/test_exceptions.py b/pkg/hanzo-aci/tests/unit/test_exceptions.py deleted file mode 100644 index 36d24d08f..000000000 --- a/pkg/hanzo-aci/tests/unit/test_exceptions.py +++ /dev/null @@ -1,51 +0,0 @@ -import pytest - -from dev_aci.editor.exceptions import ( - EditorToolParameterInvalidError, - EditorToolParameterMissingError, - ToolError, -) - - -def test_tool_error(): - """Test ToolError raises with correct message.""" - with pytest.raises(ToolError) as exc_info: - raise ToolError("A tool error occurred") - assert str(exc_info.value) == "A tool error occurred" - - -def test_editor_tool_parameter_missing_error(): - """Test EditorToolParameterMissingError for missing parameter error message.""" - command = "str_replace" - parameter = "old_str" - with pytest.raises(EditorToolParameterMissingError) as exc_info: - raise EditorToolParameterMissingError(command, parameter) - assert exc_info.value.command == command - assert exc_info.value.parameter == parameter - assert ( - exc_info.value.message - == f"Parameter `{parameter}` is required for command: {command}." - ) - - -def test_editor_tool_parameter_invalid_error_with_hint(): - """Test EditorToolParameterInvalidError with hint.""" - parameter = "timeout" - value = -10 - hint = "Must be a positive integer." - with pytest.raises(EditorToolParameterInvalidError) as exc_info: - raise EditorToolParameterInvalidError(parameter, value, hint) - assert exc_info.value.parameter == parameter - assert exc_info.value.value == value - assert exc_info.value.message == f"Invalid `{parameter}` parameter: {value}. {hint}" - - -def test_editor_tool_parameter_invalid_error_without_hint(): - """Test EditorToolParameterInvalidError without hint.""" - parameter = "timeout" - value = -10 - with pytest.raises(EditorToolParameterInvalidError) as exc_info: - raise EditorToolParameterInvalidError(parameter, value) - assert exc_info.value.parameter == parameter - assert exc_info.value.value == value - assert exc_info.value.message == f"Invalid `{parameter}` parameter: {value}." diff --git a/pkg/hanzo-aci/tests/unit/test_file_validation.py b/pkg/hanzo-aci/tests/unit/test_file_validation.py deleted file mode 100644 index 0cf1526a3..000000000 --- a/pkg/hanzo-aci/tests/unit/test_file_validation.py +++ /dev/null @@ -1,64 +0,0 @@ -from pathlib import Path - -import pytest - -from dev_aci.editor.editor import OHEditor -from dev_aci.editor.exceptions import FileValidationError - - -def test_validate_large_file(tmp_path): - """Test that large files are rejected.""" - editor = OHEditor() - large_file = tmp_path / "large.txt" - - # Create a file just over 10MB - file_size = 10 * 1024 * 1024 + 1024 # 10MB + 1KB - with open(large_file, "wb") as f: - f.write(b"0" * file_size) - - with pytest.raises(FileValidationError) as exc_info: - editor.validate_file(large_file) - assert "File is too large" in str(exc_info.value) - assert "10.0MB" in str(exc_info.value) - - -def test_validate_binary_file(tmp_path): - """Test that binary files are rejected.""" - editor = OHEditor() - binary_file = tmp_path / "binary.bin" - - # Create a binary file with null bytes - with open(binary_file, "wb") as f: - f.write(b"Some text\x00with binary\x00content") - - with pytest.raises(FileValidationError) as exc_info: - editor.validate_file(binary_file) - assert "binary" in str(exc_info.value).lower() - - -def test_validate_text_file(tmp_path): - """Test that valid text files are accepted.""" - editor = OHEditor() - text_file = tmp_path / "valid.txt" - - # Create a valid text file - with open(text_file, "w") as f: - f.write("This is a valid text file\nwith multiple lines\n") - - # Should not raise any exception - editor.validate_file(text_file) - - -def test_validate_directory(): - """Test that directories are skipped in validation.""" - editor = OHEditor() - # Should not raise any exception for directories - editor.validate_file(Path("/tmp")) - - -def test_validate_nonexistent_file(): - """Test validation of nonexistent file.""" - editor = OHEditor() - nonexistent = Path("/nonexistent/file.txt") - # Should not raise FileValidationError since validate_path will handle this case - editor.validate_file(nonexistent) diff --git a/pkg/hanzo-aci/tests/unit/test_history.py b/pkg/hanzo-aci/tests/unit/test_history.py deleted file mode 100644 index 94062ea45..000000000 --- a/pkg/hanzo-aci/tests/unit/test_history.py +++ /dev/null @@ -1,142 +0,0 @@ -"""Tests for file history management.""" - -import tempfile -from pathlib import Path - -from dev_aci.editor.history import FileHistoryManager - - -def test_default_history_limit(): - """Test that default history limit is 5 entries.""" - with tempfile.NamedTemporaryFile() as temp_file: - path = Path(temp_file.name) - manager = FileHistoryManager() - - # Add 6 entries - this should trigger removal of the first entry - for i in range(6): - manager.add_history(path, f"content{i}") - - # Get the metadata - metadata = manager.get_metadata(path) - assert len(metadata["entries"]) == 5 # Should only keep last 5 entries - # First entry should be content1, last should be content5 - assert manager.get_all_history(path)[0].startswith("content1") - assert manager.get_all_history(path)[-1].startswith("content5") - - -def test_history_keys_are_unique(): - """Test that history keys remain unique even after removing old entries.""" - with tempfile.NamedTemporaryFile() as temp_file: - path = Path(temp_file.name) - manager = FileHistoryManager(max_history_per_file=2) - - # Add 3 entries - this should trigger removal of the first entry - manager.add_history(path, "content1") - manager.add_history(path, "content2") - manager.add_history(path, "content3") - - # Get the metadata - metadata = manager.get_metadata(path) - assert len(metadata["entries"]) == 2 # Should only keep last 2 entries - - # Keys should be unique and sequential - keys = metadata["entries"] - assert len(set(keys)) == len(keys) # All keys should be unique - assert sorted(keys) == keys # Keys should be sequential - - # Add another entry - manager.add_history(path, "content4") - new_metadata = manager.get_metadata(path) - new_keys = new_metadata["entries"] - - # New key should be greater than all previous keys - assert min(new_keys) > min(keys) - assert len(set(new_keys)) == len(new_keys) # All keys should still be unique - - -def test_history_counter_persists(): - """Test that history counter persists across manager instances.""" - with tempfile.TemporaryDirectory() as temp_dir: - path = Path(temp_dir) / "test.txt" - path.write_text("initial") - - # First manager instance - manager1 = FileHistoryManager(history_dir=Path(temp_dir)) - manager1.add_history(path, "content1") - manager1.add_history(path, "content2") - - # Second manager instance using same directory - manager2 = FileHistoryManager(history_dir=Path(temp_dir)) - manager2.add_history(path, "content3") - - # Get metadata - metadata = manager2.get_metadata(path) - keys = metadata["entries"] - - # Keys should be sequential even across instances - assert len(set(keys)) == len(keys) # All keys should be unique - assert sorted(keys) == keys # Keys should be sequential - - -def test_clear_history_resets_counter(): - """Test that clearing history resets the counter.""" - with tempfile.NamedTemporaryFile() as temp_file: - path = Path(temp_file.name) - manager = FileHistoryManager() - - # Add some entries - manager.add_history(path, "content1") - manager.add_history(path, "content2") - - # Clear history - manager.clear_history(path) - - # Counter should be reset - metadata = manager.get_metadata(path) - assert metadata["counter"] == 0 - - # Adding new entries should start from 0 - manager.add_history(path, "new_content") - metadata = manager.get_metadata(path) - assert len(metadata["entries"]) == 1 - assert metadata["entries"][0] == 0 # First key should be 0 - - -def test_pop_last_history_removes_entry(): - """Test that pop_last_history removes the latest entry.""" - with tempfile.NamedTemporaryFile() as temp_file: - path = Path(temp_file.name) - manager = FileHistoryManager() - - # Add some entries - manager.add_history(path, "content1") - manager.add_history(path, "content2") - manager.add_history(path, "content3") - - # Pop the last history entry - last_entry = manager.pop_last_history(path) - assert last_entry == "content3" - - # Check that the entry has been removed - metadata = manager.get_metadata(path) - assert len(metadata["entries"]) == 2 - - # Pop the last history entry again - last_entry = manager.pop_last_history(path) - assert last_entry == "content2" - - # Check that the entry has been removed - metadata = manager.get_metadata(path) - assert len(metadata["entries"]) == 1 - - # Pop the last history entry one more time - last_entry = manager.pop_last_history(path) - assert last_entry == "content1" - - # Check that all entries have been removed - metadata = manager.get_metadata(path) - assert len(metadata["entries"]) == 0 - - # Try to pop last history when there are no entries - last_entry = manager.pop_last_history(path) - assert last_entry is None diff --git a/pkg/hanzo-aci/tests/unit/test_results_utils.py b/pkg/hanzo-aci/tests/unit/test_results_utils.py deleted file mode 100644 index 05ed15bd2..000000000 --- a/pkg/hanzo-aci/tests/unit/test_results_utils.py +++ /dev/null @@ -1,43 +0,0 @@ -from dev_aci.editor.config import MAX_RESPONSE_LEN_CHAR -from dev_aci.editor.prompts import CONTENT_TRUNCATED_NOTICE -from dev_aci.editor.results import ToolResult, maybe_truncate - - -def test_tool_result_bool(): - """Test the boolean value of ToolResult based on output and error.""" - # Case: Both output and error are None - result = ToolResult() - assert not bool(result) - - # Case: Only output is set - result = ToolResult(output="Some output") - assert bool(result) - - # Case: Only error is set - result = ToolResult(error="An error occurred") - assert bool(result) - - # Case: Both output and error are set - result = ToolResult(output="Some output", error="An error occurred") - assert bool(result) - - -def test_maybe_truncate_no_truncation(): - """Test maybe_truncate when content does not exceed the length limit.""" - content = "Short content" - result = maybe_truncate(content, truncate_after=MAX_RESPONSE_LEN_CHAR) - assert result == content # Should return content as-is - - -def test_maybe_truncate_with_truncation(): - """Test maybe_truncate when content exceeds the length limit.""" - content = "a" * (MAX_RESPONSE_LEN_CHAR + 10) - result = maybe_truncate(content, truncate_after=MAX_RESPONSE_LEN_CHAR) - assert result == content[:MAX_RESPONSE_LEN_CHAR] + CONTENT_TRUNCATED_NOTICE - - -def test_maybe_truncate_no_limit(): - """Test maybe_truncate when truncate_after is None.""" - content = "Content that exceeds the default max length" - result = maybe_truncate(content, truncate_after=None) - assert result == content # No truncation applied when limit is None diff --git a/pkg/hanzo-aci/tests/unit/test_shell_utils.py b/pkg/hanzo-aci/tests/unit/test_shell_utils.py deleted file mode 100644 index a44196c27..000000000 --- a/pkg/hanzo-aci/tests/unit/test_shell_utils.py +++ /dev/null @@ -1,60 +0,0 @@ -import subprocess -from unittest.mock import MagicMock, patch - -import pytest - -from dev_aci.editor.config import MAX_RESPONSE_LEN_CHAR -from dev_aci.editor.prompts import CONTENT_TRUNCATED_NOTICE -from dev_aci.utils.shell import check_tool_installed, run_shell_cmd - - -def test_run_shell_cmd_success(): - """Test running a successful shell command.""" - cmd = "echo 'Hello, World!'" - returncode, stdout, stderr = run_shell_cmd(cmd) - - assert returncode == 0 - assert stdout.strip() == "Hello, World!" - assert stderr == "" - - -@patch("subprocess.Popen") -def test_run_shell_cmd_timeout(mock_popen): - """Test that a TimeoutError is raised if command times out.""" - mock_process = MagicMock() - mock_process.communicate.side_effect = subprocess.TimeoutExpired( - cmd="sleep 2", timeout=1 - ) - mock_popen.return_value = mock_process - - with pytest.raises(TimeoutError, match="Command 'sleep 2' timed out"): - run_shell_cmd("sleep 2", timeout=1) - - -@patch("subprocess.Popen") -def test_run_shell_cmd_truncation(mock_popen): - """Test that stdout and stderr are truncated correctly.""" - long_output = "a" * (MAX_RESPONSE_LEN_CHAR + 10) - mock_process = MagicMock() - mock_process.communicate.return_value = (long_output, long_output) - mock_process.returncode = 0 - mock_popen.return_value = mock_process - - returncode, stdout, stderr = run_shell_cmd("echo long_output") - - assert returncode == 0 - assert len(stdout) <= MAX_RESPONSE_LEN_CHAR + len(CONTENT_TRUNCATED_NOTICE) - assert len(stderr) <= MAX_RESPONSE_LEN_CHAR + len(CONTENT_TRUNCATED_NOTICE) - - -@pytest.mark.skip(reason="whoami might not be available in all environments") -def test_check_tool_installed_whoami(): - """Test check_tool_installed returns True for an installed tool (whoami).""" - # 'python' is usually available if Python is installed - assert check_tool_installed("whoami") is True - - -def test_check_tool_installed_nonexistent_tool(): - """Test check_tool_installed returns False for a nonexistent tool.""" - # Use a made-up tool name that is very unlikely to exist - assert check_tool_installed("nonexistent_tool_xyz") is False diff --git a/pkg/hanzo-aci/tests/unit/test_workspace_root.py b/pkg/hanzo-aci/tests/unit/test_workspace_root.py deleted file mode 100644 index 357f2e2a7..000000000 --- a/pkg/hanzo-aci/tests/unit/test_workspace_root.py +++ /dev/null @@ -1,94 +0,0 @@ -from pathlib import Path - -import pytest - -from dev_aci.editor.editor import OHEditor -from dev_aci.editor.exceptions import EditorToolParameterInvalidError - - -def test_workspace_root_as_cwd(tmp_path): - """Test that workspace_root is used as the current working directory for path suggestions.""" - # Create a workspace root - workspace_root = tmp_path / "workspace" - workspace_root.mkdir() - - # Create a file inside the workspace root - test_file = workspace_root / "test.txt" - test_file.write_text("This is a test file") - - # Initialize editor with workspace_root - editor = OHEditor(workspace_root=str(workspace_root)) - - # Test that a relative path suggestion uses the workspace_root - relative_path = "test.txt" - with pytest.raises(EditorToolParameterInvalidError) as exc_info: - editor(command="view", path=relative_path) - - error_message = str(exc_info.value.message) - assert "The path should be an absolute path" in error_message - assert "Maybe you meant" in error_message - - # Extract the suggested path from the error message - suggested_path = error_message.split("Maybe you meant ")[1].strip("?") - assert Path(suggested_path).is_absolute() - assert str(workspace_root) in suggested_path - - # Test with a non-existent file - non_existent_path = "non_existent.txt" - with pytest.raises(EditorToolParameterInvalidError) as exc_info: - editor(command="view", path=non_existent_path) - - error_message = str(exc_info.value.message) - assert "The path should be an absolute path" in error_message - assert "Maybe you meant" not in error_message - - -def test_relative_workspace_root_raises_error(tmp_path, monkeypatch): - """Test that a relative workspace_root raises a ValueError.""" - # Set up a directory structure - current_dir = tmp_path / "current_dir" - current_dir.mkdir() - - # Change to the current directory - monkeypatch.chdir(current_dir) - - # Initialize editor with a relative workspace_root should raise ValueError - with pytest.raises(ValueError) as exc_info: - OHEditor(workspace_root="workspace") - - # Check error message - error_message = str(exc_info.value) - assert "workspace_root must be an absolute path" in error_message - - -def test_no_suggestion_when_no_workspace_root(tmp_path, monkeypatch): - """Test that no path suggestion is made when workspace_root is not provided.""" - # Create a temporary file in the current directory - current_dir = tmp_path / "current_dir" - current_dir.mkdir() - test_file = current_dir / "test.txt" - test_file.write_text("This is a test file") - - # Set the current directory to our temporary directory - monkeypatch.chdir(current_dir) - - # Initialize editor without workspace_root - editor = OHEditor() - - # Test that no path suggestion is made, even for existing files - relative_path = "test.txt" - with pytest.raises(EditorToolParameterInvalidError) as exc_info: - editor(command="view", path=relative_path) - - error_message = str(exc_info.value.message) - assert "The path should be an absolute path" in error_message - assert "Maybe you meant" not in error_message - - # Test with a non-existent file (should also have no suggestion) - non_existent_path = "non_existent.txt" - with pytest.raises(EditorToolParameterInvalidError) as exc_info: - editor(command="view", path=non_existent_path) - - error_message = str(exc_info.value.message) - assert "The path should be an absolute path" in error_message - assert "Maybe you meant" not in error_message diff --git a/pkg/hanzo-agent/.dockerignore b/pkg/hanzo-agent/.dockerignore deleted file mode 100644 index d1591b378..000000000 --- a/pkg/hanzo-agent/.dockerignore +++ /dev/null @@ -1,55 +0,0 @@ -# Git -.git/ -.gitignore -.github/ - -# Python -__pycache__/ -*.py[cod] -*$py.class -*.so -.Python -.env -.venv/ -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ -.pytest_cache/ -.coverage -.coverage.* -htmlcov/ -.tox/ -.mypy_cache/ -.dmypy.json -dmypy.json -.ruff_cache/ -*.egg-info/ -dist/ -build/ - -# IDE -.vscode/ -.idea/ -*.swp -*.swo -*~ -.DS_Store - -# Documentation -docs/_build/ -*.md -!README.md - -# Tests -tests/ -test_*.py -*_test.py - -# Development -.dockerignore -Dockerfile -docker-compose*.yml -Makefile -*.log \ No newline at end of file diff --git a/pkg/hanzo-agent/.gitignore b/pkg/hanzo-agent/.gitignore deleted file mode 100644 index 1def8a6af..000000000 --- a/pkg/hanzo-agent/.gitignore +++ /dev/null @@ -1,144 +0,0 @@ -# macOS Files -.DS_Store - -# Byte-compiled / optimized / DLL files -__pycache__/ -**/__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pdm -.pdm.toml -.pdm-python -.pdm-build/ - -# PEP 582 -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ -.venv39 -.venv_res - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -#.idea/ - -# Ruff stuff: -.ruff_cache/ - -# PyPI configuration file -.pypirc diff --git a/pkg/hanzo-agent/.prettierrc b/pkg/hanzo-agent/.prettierrc deleted file mode 100644 index 32ab3e75d..000000000 --- a/pkg/hanzo-agent/.prettierrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "tabWidth": 4, - "overrides": [ - { - "files": "*.yml", - "options": { - "tabWidth": 2 - } - } - ] -} \ No newline at end of file diff --git a/pkg/hanzo-agent/Dockerfile b/pkg/hanzo-agent/Dockerfile deleted file mode 100644 index e9e1e83d0..000000000 --- a/pkg/hanzo-agent/Dockerfile +++ /dev/null @@ -1,58 +0,0 @@ -# Multi-stage Dockerfile for Hanzo Agent SDK -# This Dockerfile creates a production-ready image for the Hanzo AI SDK - -# Build stage -FROM python:3.12-slim AS builder - -# Install system dependencies -RUN apt-get update && apt-get install -y --no-install-recommends \ - curl \ - && rm -rf /var/lib/apt/lists/* - -# Install uv for fast Python package management -RUN curl -LsSf https://astral.sh/uv/install.sh | sh -ENV PATH="/root/.cargo/bin:${PATH}" - -# Set working directory -WORKDIR /app - -# Copy dependency files -COPY pyproject.toml uv.lock ./ - -# Install dependencies -RUN uv sync --frozen --no-dev - -# Production stage -FROM python:3.12-slim - -# Install runtime dependencies -RUN apt-get update && apt-get install -y --no-install-recommends \ - ca-certificates \ - && rm -rf /var/lib/apt/lists/* - -# Create non-root user -RUN groupadd -r hanzo && useradd -r -g hanzo hanzo - -# Set working directory -WORKDIR /app - -# Copy virtual environment from builder -COPY --from=builder /app/.venv /app/.venv - -# Copy application code -COPY --chown=hanzo:hanzo . . - -# Set environment variables -ENV PATH="/app/.venv/bin:${PATH}" -ENV PYTHONUNBUFFERED=1 -ENV PYTHONDONTWRITEBYTECODE=1 - -# Switch to non-root user -USER hanzo - -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ - CMD python -c "import agents; print('OK')" || exit 1 - -# Default command (can be overridden) -CMD ["python", "-m", "agents"] \ No newline at end of file diff --git a/pkg/hanzo-agent/LICENSE b/pkg/hanzo-agent/LICENSE deleted file mode 100644 index ad10d233a..000000000 --- a/pkg/hanzo-agent/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 Hanzo Industries, Inc - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/pkg/hanzo-agent/Makefile b/pkg/hanzo-agent/Makefile deleted file mode 100644 index 130de3f9d..000000000 --- a/pkg/hanzo-agent/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -.PHONY: sync -sync: - uv sync --all-extras --all-packages --group dev - -.PHONY: format -format: - uv run ruff format - -.PHONY: lint -lint: - uv run ruff check - -.PHONY: mypy -mypy: - uv run mypy . - -.PHONY: tests -tests: - uv run pytest - -.PHONY: old_version_tests -old_version_tests: - UV_PROJECT_ENVIRONMENT=.venv_39 uv run --python 3.9 -m pytest - UV_PROJECT_ENVIRONMENT=.venv_39 uv run --python 3.9 -m mypy . - -.PHONY: build-docs -build-docs: - uv run mkdocs build - -.PHONY: serve-docs -serve-docs: - uv run mkdocs serve - -.PHONY: deploy-docs -deploy-docs: - uv run mkdocs gh-deploy --force --verbose - -.PHONY: test-backend -test-backend: - @echo "Testing Hanzo Agent SDK with local backend..." - @echo "Make sure Router is running at http://localhost:4000" - @echo "----------------------------------------" - uv run python test_hanzo_backend.py - -.PHONY: example-backend -example-backend: - @echo "Running Hanzo backend integration example..." - uv run python examples/hanzo_backend_example.py - -.PHONY: setup-backend -setup-backend: - @echo "Setting up Hanzo backend environment..." - @echo "export HANZO_ROUTER_URL=http://localhost:4000/v1" - @echo "export HANZO_API_KEY=sk-1234" - @echo "----------------------------------------" - @echo "Add these to your shell profile or .env file" - diff --git a/pkg/hanzo-agent/README.md b/pkg/hanzo-agent/README.md deleted file mode 100644 index ff1e70c99..000000000 --- a/pkg/hanzo-agent/README.md +++ /dev/null @@ -1,338 +0,0 @@ -# Hanzo AI Agent SDK - -A powerful Python framework for building AI agents and multi-agent systems with built-in orchestration. - -Image of the Agents Tracing UI - -## โœจ Features - -- ๐Ÿค– **Multi-Agent Networks**: Build systems where multiple specialized agents collaborate -- ๐Ÿง  **Intelligent Routing**: Semantic, rule-based, and load-balanced routing strategies -- ๐Ÿ› ๏ธ **Powerful Tools**: Enhanced tool system with MCP (Model Context Protocol) support -- ๐Ÿ“Š **Shared State**: Agents can share information through network state -- ๐Ÿ”„ **Orchestration**: Define complex workflows with parallel, conditional, and loop steps -- ๐Ÿ’พ **Memory System**: Long-term memory with vector search and reflection capabilities -- โšก **UI Streaming**: Real-time updates for building responsive interfaces -- ๐Ÿ” **Observability**: Built-in tracing and monitoring via Hanzo Cloud dashboard -- ๐ŸŒ **Backend Flexibility**: Use with Hanzo Router for 100+ LLM providers - -### Optional Extensions: - -- ๐Ÿ’Ž **Web3 Integration** (`[web3]`): Wallet management, transactions, on-chain identity -- ๐Ÿ”’ **TEE Support** (`[tee]`): Intel SGX, AMD SEV, NVIDIA H100 attestation and confidential computing -- ๐Ÿ›’ **Marketplace** (`[marketplace]`): Decentralized agent service discovery and economics -- ๐Ÿ’ป **CLI** (`[cli]`): Command-line interface integration - -### Core concepts: - -1. [**Agents**](docs/agents.md): LLMs configured with instructions, tools, and memory -2. [**Networks**](docs/networks-and-orchestration.md): Multi-agent systems with intelligent routing -3. [**Workflows**](docs/networks-and-orchestration.md#orchestration-and-workflows): Orchestrate complex multi-step processes -4. [**State & Memory**](docs/networks-and-orchestration.md#state-management): Shared state and long-term memory -5. [**Tools**](docs/tools.md): Enhanced tool system with MCP support -6. [**Tracing**](docs/tracing.md): Built-in tracking and observability - -Explore the [examples](examples) directory to see the SDK in action, and read our [documentation](https://openai.github.io/openai-agents-python/) for more details. - -Notably, our SDK [is compatible](https://openai.github.io/openai-agents-python/models/) with any model providers that support the Open AI Chat Completions API format. - -## Get started - -1. Set up your Python environment - -``` -python -m venv env -source env/bin/activate -``` - -2. Install Hanzo AI SDK - -```bash -# Basic installation -pip install hanzoai - -# With Web3 support -pip install "hanzoai[web3]" - -# With TEE support -pip install "hanzoai[tee]" - -# With Marketplace support -pip install "hanzoai[marketplace]" - -# With CLI support -pip install "hanzoai[cli]" - -# Full installation (all extensions) -pip install "hanzoai[full]" -``` - -## Quick Examples - -### Simple Agent - -```python -from hanzoai import Agent, Runner - -agent = Agent(name="Assistant", instructions="You are a helpful assistant") - -result = Runner.run_sync(agent, "Write a haiku about recursion in programming.") -print(result.final_output) - -# Code within the code, -# Functions calling themselves, -# Infinite loop's dance. -``` - -### Multi-Agent Network - -```python -from agents import Agent, create_network -from agents.routers import SemanticRouter - -# Create specialized agents -researcher = Agent( - name="Researcher", - instructions="You find and analyze information.", - tools=[search_tool, analyze_tool] -) - -writer = Agent( - name="Writer", - instructions="You create content based on research.", - tools=[format_tool] -) - -# Create a network -network = create_network( - agents=[researcher, writer], - router=SemanticRouter(), - default_model="gpt-4" -) - -# Run the network -result = await network.run("Research and write about quantum computing") -``` - -### Orchestrated Workflow - -```python -from agents import create_workflow, Step - -workflow = create_workflow( - name="Content Pipeline", - steps=[ - Step.agent("researcher", "Research {topic}"), - Step.parallel([ - Step.agent("writer", "Write introduction"), - Step.agent("writer", "Write main content") - ]), - Step.agent("reviewer", "Review and edit"), - Step.conditional( - condition=lambda state: state.get("quality_score") < 8, - true_step=Step.agent("writer", "Revise based on feedback"), - false_step=Step.transform(lambda x: {"status": "published"}) - ) - ] -) - -result = await workflow.run({"topic": "AI Safety"}) -``` - -(_Configure backend with `HANZO_ROUTER_URL` and `HANZO_API_KEY` environment variables_) - -## Handoffs example - -```python -from hanzoai import Agent, Runner -import asyncio - -spanish_agent = Agent( - name="Spanish agent", - instructions="You only speak Spanish.", -) - -english_agent = Agent( - name="English agent", - instructions="You only speak English", -) - -triage_agent = Agent( - name="Triage agent", - instructions="Handoff to the appropriate agent based on the language of the request.", - handoffs=[spanish_agent, english_agent], -) - - -async def main(): - result = await Runner.run(triage_agent, input="Hola, ยฟcรณmo estรกs?") - print(result.final_output) - # ยกHola! Estoy bien, gracias por preguntar. ยฟY tรบ, cรณmo estรกs? - - -if __name__ == "__main__": - asyncio.run(main()) -``` - -## Functions example - -```python -import asyncio - -from hanzoai import Agent, Runner, function_tool - - -@function_tool -def get_weather(city: str) -> str: - return f"The weather in {city} is sunny." - - -agent = Agent( - name="Hello world", - instructions="You are a helpful agent.", - tools=[get_weather], -) - - -async def main(): - result = await Runner.run(agent, input="What's the weather in Tokyo?") - print(result.final_output) - # The weather in Tokyo is sunny. - - -if __name__ == "__main__": - asyncio.run(main()) -``` - -## The agent loop - -When you call `Runner.run()`, we run a loop until we get a final output. - -1. We call the LLM, using the model and settings on the agent, and the message history. -2. The LLM returns a response, which may include tool calls. -3. If the response has a final output (see below for more on this), we return it and end the loop. -4. If the response has a handoff, we set the agent to the new agent and go back to step 1. -5. We process the tool calls (if any) and append the tool responses messages. Then we go to step 1. - -There is a `max_turns` parameter that you can use to limit the number of times the loop executes. - -### Final output - -Final output is the last thing the agent produces in the loop. - -1. If you set an `output_type` on the agent, the final output is when the LLM returns something of that type. We use [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) for this. -2. If there's no `output_type` (i.e. plain text responses), then the first LLM response without any tool calls or handoffs is considered as the final output. - -As a result, the mental model for the agent loop is: - -1. If the current agent has an `output_type`, the loop runs until the agent produces structured output matching that type. -2. If the current agent does not have an `output_type`, the loop runs until the current agent produces a message without any tool calls/handoffs. - -## Common agent patterns - -The Agent SDK is designed to be highly flexible, allowing you to model a wide range of LLM workflows including deterministic flows, iterative loops, and more. See examples in [`examples/agent_patterns`](examples/agent_patterns). - -## Tracing - -The Agent SDK automatically traces your agent runs, making it easy to track and debug the behavior of your agents. Tracing is extensible by design, supporting custom spans and a wide variety of external destinations, including [Logfire](https://logfire.pydantic.dev/docs/integrations/llms/openai/#openai-agents), [AgentOps](https://docs.agentops.ai/v1/integrations/agentssdk), [Braintrust](https://braintrust.dev/docs/guides/traces/integrations#openai-agents-sdk), [Scorecard](https://docs.scorecard.io/docs/documentation/features/tracing#openai-agents-sdk-integration), and [Keywords AI](https://docs.keywordsai.co/integration/development-frameworks/openai-agent). For more details about how to customize or disable tracing, see [Tracing](http://openai.github.io/openai-agents-python/tracing). - -## Development (only needed if you need to edit the SDK/examples) - -0. Ensure you have [`uv`](https://docs.astral.sh/uv/) installed. - -```bash -uv --version -``` - -1. Install dependencies - -```bash -make sync -``` - -2. (After making changes) lint/test - -``` -make tests # run tests -make mypy # run typechecker -make lint # run linter -``` - -## Acknowledgements - -We'd like to acknowledge the excellent work of the open-source community, especially: - -- [Pydantic](https://docs.pydantic.dev/latest/) (data validation) and [PydanticAI](https://ai.pydantic.dev/) (advanced agent framework) -- [MkDocs](https://github.com/squidfunk/mkdocs-material) -- [Griffe](https://github.com/mkdocstrings/griffe) -- [uv](https://github.com/astral-sh/uv) and [ruff](https://github.com/astral-sh/ruff) - -We're committed to continuing to build the Agent SDK as an open source framework so others in the community can expand on our approach. - -## Extension Examples - -### Web3 Agent - -```python -from agents import Agent, Runner -from agents.extensions.web3 import Web3Agent, AgentWallet - -# Create wallet for agent -wallet = AgentWallet.from_mnemonic("your mnemonic here") - -# Create Web3-enabled agent -trader = Web3Agent( - name="Crypto Trader", - instructions="You are a cryptocurrency trading agent", - wallet=wallet -) - -# Agent can now use wallet tools -result = await Runner.run(trader, "Check my ETH balance") -``` - -### TEE (Confidential) Agent - -```python -from agents import Agent, Runner -from agents.extensions.tee import ConfidentialAgent, TEEProvider - -# Create agent that runs in TEE -confidential_agent = ConfidentialAgent( - name="Secure Agent", - instructions="You handle sensitive data", - tee_provider=TEEProvider.INTEL_SGX -) - -# Generate attestation -attestation = await confidential_agent.generate_attestation() -print(f"Attestation: {attestation.quote}") -``` - -### Marketplace Agent - -```python -from agents import Agent -from agents.extensions.marketplace import AgentMarketplace, ServiceOffer, ServiceType - -# Create agent that offers services -service_agent = Agent( - name="Research Agent", - instructions="You conduct research" -) - -# Create service offer -offer = ServiceOffer( - id="research-1", - agent_address="0x...", - agent_name="Research Agent", - service_type=ServiceType.RESEARCH, - price_eth=0.01, - description="Comprehensive research services" -) - -# Register on marketplace -marketplace = AgentMarketplace() -await marketplace.register_offer(offer) -``` - diff --git a/pkg/hanzo-agent/docs/assets/images/favicon-platform.svg b/pkg/hanzo-agent/docs/assets/images/favicon-platform.svg deleted file mode 100644 index 91ef0aea5..000000000 --- a/pkg/hanzo-agent/docs/assets/images/favicon-platform.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/pkg/hanzo-agent/docs/assets/images/orchestration.png b/pkg/hanzo-agent/docs/assets/images/orchestration.png deleted file mode 100644 index 621a833b5..000000000 Binary files a/pkg/hanzo-agent/docs/assets/images/orchestration.png and /dev/null differ diff --git a/pkg/hanzo-agent/docs/assets/logo.svg b/pkg/hanzo-agent/docs/assets/logo.svg deleted file mode 100644 index ba36fc2aa..000000000 --- a/pkg/hanzo-agent/docs/assets/logo.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/pkg/hanzo-agent/docs/config.md b/pkg/hanzo-agent/docs/config.md deleted file mode 100644 index 910b4ea2f..000000000 --- a/pkg/hanzo-agent/docs/config.md +++ /dev/null @@ -1,94 +0,0 @@ -# Configuring the SDK - -## API keys and clients - -By default, the SDK looks for the `OPENAI_API_KEY` environment variable for LLM requests and tracing, as soon as it is imported. If you are unable to set that environment variable before your app starts, you can use the [set_default_openai_key()][agents.set_default_openai_key] function to set the key. - -```python -from agents import set_default_openai_key - -set_default_openai_key("sk-...") -``` - -Alternatively, you can also configure an Hanzo AI client to be used. By default, the SDK creates an `AsyncHanzo AI` instance, using the API key from the environment variable or the default key set above. You can change this by using the [set_default_openai_client()][agents.set_default_openai_client] function. - -```python -from openai import AsyncHanzo AI -from agents import set_default_openai_client - -custom_client = AsyncHanzo AI(base_url="...", api_key="...") -set_default_openai_client(custom_client) -``` - -Finally, you can also customize the Hanzo AI API that is used. By default, we use the Hanzo AI Responses API. You can override this to use the Chat Completions API by using the [set_default_openai_api()][agents.set_default_openai_api] function. - -```python -from agents import set_default_openai_api - -set_default_openai_api("chat_completions") -``` - -## Tracing - -Tracing is enabled by default. It uses the Hanzo AI API keys from the section above by default (i.e. the environment variable or the default key you set). You can specifically set the API key used for tracing by using the [`set_tracing_export_api_key`][agents.set_tracing_export_api_key] function. - -```python -from agents import set_tracing_export_api_key - -set_tracing_export_api_key("sk-...") -``` - -You can also disable tracing entirely by using the [`set_tracing_disabled()`][agents.set_tracing_disabled] function. - -```python -from agents import set_tracing_disabled - -set_tracing_disabled(True) -``` - -## Debug logging - -The SDK has two Python loggers without any handlers set. By default, this means that warnings and errors are sent to `stdout`, but other logs are suppressed. - -To enable verbose logging, use the [`enable_verbose_stdout_logging()`][agents.enable_verbose_stdout_logging] function. - -```python -from agents import enable_verbose_stdout_logging - -enable_verbose_stdout_logging() -``` - -Alternatively, you can customize the logs by adding handlers, filters, formatters, etc. You can read more in the [Python logging guide](https://docs.python.org/3/howto/logging.html). - -```python -import logging - -logger = logging.getLogger("openai.agents") # or openai.agents.tracing for the Tracing logger - -# To make all logs show up -logger.setLevel(logging.DEBUG) -# To make info and above show up -logger.setLevel(logging.INFO) -# To make warning and above show up -logger.setLevel(logging.WARNING) -# etc - -# You can customize this as needed, but this will output to `stderr` by default -logger.addHandler(logging.StreamHandler()) -``` - -### Sensitive data in logs - -Certain logs may contain sensitive data (for example, user data). If you want to disable this data from being logged, set the following environment variables. - -To disable logging LLM inputs and outputs: - -```bash -export OPENAI_AGENTS_DONT_LOG_MODEL_DATA=1 -``` - -To disable logging tool inputs and outputs: - -```bash -export OPENAI_AGENTS_DONT_LOG_TOOL_DATA=1 -``` diff --git a/pkg/hanzo-agent/docs/context.md b/pkg/hanzo-agent/docs/context.md deleted file mode 100644 index 69c43fbbf..000000000 --- a/pkg/hanzo-agent/docs/context.md +++ /dev/null @@ -1,77 +0,0 @@ -# Context management - -Context is an overloaded term. There are two main classes of context you might care about: - -1. Context available locally to your code: this is data and dependencies you might need when tool functions run, during callbacks like `on_handoff`, in lifecycle hooks, etc. -2. Context available to LLMs: this is data the LLM sees when generating a response. - -## Local context - -This is represented via the [`RunContextWrapper`][agents.run_context.RunContextWrapper] class and the [`context`][agents.run_context.RunContextWrapper.context] property within it. The way this works is: - -1. You create any Python object you want. A common pattern is to use a dataclass or a Pydantic object. -2. You pass that object to the various run methods (e.g. `Runner.run(..., **context=whatever**))`. -3. All your tool calls, lifecycle hooks etc will be passed a wrapper object, `RunContextWrapper[T]`, where `T` represents your context object type which you can access via `wrapper.context`. - -The **most important** thing to be aware of: every agent, tool function, lifecycle etc for a given agent run must use the same _type_ of context. - -You can use the context for things like: - -- Contextual data for your run (e.g. things like a username/uid or other information about the user) -- Dependencies (e.g. logger objects, data fetchers, etc) -- Helper functions - -!!! danger "Note" - - The context object is **not** sent to the LLM. It is purely a local object that you can read from, write to and call methods on it. - -```python -import asyncio -from dataclasses import dataclass - -from agents import Agent, RunContextWrapper, Runner, function_tool - -@dataclass -class UserInfo: # (1)! - name: str - uid: int - -@function_tool -async def fetch_user_age(wrapper: RunContextWrapper[UserInfo]) -> str: # (2)! - return f"User {wrapper.context.name} is 47 years old" - -async def main(): - user_info = UserInfo(name="John", uid=123) # (3)! - - agent = Agent[UserInfo]( # (4)! - name="Assistant", - tools=[fetch_user_age], - ) - - result = await Runner.run( - starting_agent=agent, - input="What is the age of the user?", - context=user_info, - ) - - print(result.final_output) # (5)! - # The user John is 47 years old. - -if __name__ == "__main__": - asyncio.run(main()) -``` - -1. This is the context object. We've used a dataclass here, but you can use any type. -2. This is a tool. You can see it takes a `RunContextWrapper[UserInfo]`. The tool implementation reads from the context. -3. We mark the agent with the generic `UserInfo`, so that the typechecker can catch errors (for example, if we tried to pass a tool that took a different context type). -4. The context is passed to the `run` function. -5. The agent correctly calls the tool and gets the age. - -## Agent/LLM context - -When an LLM is called, the **only** data it can see is from the conversation history. This means that if you want to make some new data available to the LLM, you must do it in a way that makes it available in that history. There are a few ways to do this: - -1. You can add it to the Agent `instructions`. This is also known as a "system prompt" or "developer message". System prompts can be static strings, or they can be dynamic functions that receive the context and output a string. This is a common tactic for information that is always useful (for example, the user's name or the current date). -2. Add it to the `input` when calling the `Runner.run` functions. This is similar to the `instructions` tactic, but allows you to have messages that are lower in the [chain of command](https://cdn.openai.com/spec/model-spec-2024-05-08.html#follow-the-chain-of-command). -3. Expose it via function tools. This is useful for _on-demand_ context - the LLM decides when it needs some data, and can call the tool to fetch that data. -4. Use retrieval or web search. These are special tools that are able to fetch relevant data from files or databases (retrieval), or from the web (web search). This is useful for "grounding" the response in relevant contextual data. diff --git a/pkg/hanzo-agent/docs/guardrails.md b/pkg/hanzo-agent/docs/guardrails.md deleted file mode 100644 index caf327752..000000000 --- a/pkg/hanzo-agent/docs/guardrails.md +++ /dev/null @@ -1,154 +0,0 @@ -# Guardrails - -Guardrails run _in parallel_ to your agents, enabling you to do checks and validations of user input. For example, imagine you have an agent that uses a very smart (and hence slow/expensive) model to help with customer requests. You wouldn't want malicious users to ask the model to help them with their math homework. So, you can run a guardrail with a fast/cheap model. If the guardrail detects malicious usage, it can immediately raise an error, which stops the expensive model from running and saves you time/money. - -There are two kinds of guardrails: - -1. Input guardrails run on the initial user input -2. Output guardrails run on the final agent output - -## Input guardrails - -Input guardrails run in 3 steps: - -1. First, the guardrail receives the same input passed to the agent. -2. Next, the guardrail function runs to produce a [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput], which is then wrapped in an [`InputGuardrailResult`][agents.guardrail.InputGuardrailResult] -3. Finally, we check if [`.tripwire_triggered`][agents.guardrail.GuardrailFunctionOutput.tripwire_triggered] is true. If true, an [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered] exception is raised, so you can appropriately respond to the user or handle the exception. - -!!! Note - - Input guardrails are intended to run on user input, so an agent's guardrails only run if the agent is the *first* agent. You might wonder, why is the `guardrails` property on the agent instead of passed to `Runner.run`? It's because guardrails tend to be related to the actual Agent - you'd run different guardrails for different agents, so colocating the code is useful for readability. - -## Output guardrails - -Output guardrails run in 3 steps: - -1. First, the guardrail receives the same input passed to the agent. -2. Next, the guardrail function runs to produce a [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput], which is then wrapped in an [`OutputGuardrailResult`][agents.guardrail.OutputGuardrailResult] -3. Finally, we check if [`.tripwire_triggered`][agents.guardrail.GuardrailFunctionOutput.tripwire_triggered] is true. If true, an [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered] exception is raised, so you can appropriately respond to the user or handle the exception. - -!!! Note - - Output guardrails are intended to run on the final agent input, so an agent's guardrails only run if the agent is the *last* agent. Similar to the input guardrails, we do this because guardrails tend to be related to the actual Agent - you'd run different guardrails for different agents, so colocating the code is useful for readability. - -## Tripwires - -If the input or output fails the guardrail, the Guardrail can signal this with a tripwire. As soon as we see a guardrail that has triggered the tripwires, we immediately raise a `{Input,Output}GuardrailTripwireTriggered` exception and halt the Agent execution. - -## Implementing a guardrail - -You need to provide a function that receives input, and returns a [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput]. In this example, we'll do this by running an Agent under the hood. - -```python -from pydantic import BaseModel -from agents import ( - Agent, - GuardrailFunctionOutput, - InputGuardrailTripwireTriggered, - RunContextWrapper, - Runner, - TResponseInputItem, - input_guardrail, -) - -class MathHomeworkOutput(BaseModel): - is_math_homework: bool - reasoning: str - -guardrail_agent = Agent( # (1)! - name="Guardrail check", - instructions="Check if the user is asking you to do their math homework.", - output_type=MathHomeworkOutput, -) - - -@input_guardrail -async def math_guardrail( # (2)! - ctx: RunContextWrapper[None], agent: Agent, input: str | list[TResponseInputItem] -) -> GuardrailFunctionOutput: - result = await Runner.run(guardrail_agent, input, context=ctx.context) - - return GuardrailFunctionOutput( - output_info=result.final_output, # (3)! - tripwire_triggered=result.final_output.is_math_homework, - ) - - -agent = Agent( # (4)! - name="Customer support agent", - instructions="You are a customer support agent. You help customers with their questions.", - input_guardrails=[math_guardrail], -) - -async def main(): - # This should trip the guardrail - try: - await Runner.run(agent, "Hello, can you help me solve for x: 2x + 3 = 11?") - print("Guardrail didn't trip - this is unexpected") - - except InputGuardrailTripwireTriggered: - print("Math homework guardrail tripped") -``` - -1. We'll use this agent in our guardrail function. -2. This is the guardrail function that receives the agent's input/context, and returns the result. -3. We can include extra information in the guardrail result. -4. This is the actual agent that defines the workflow. - -Output guardrails are similar. - -```python -from pydantic import BaseModel -from agents import ( - Agent, - GuardrailFunctionOutput, - OutputGuardrailTripwireTriggered, - RunContextWrapper, - Runner, - output_guardrail, -) -class MessageOutput(BaseModel): # (1)! - response: str - -class MathOutput(BaseModel): # (2)! - is_math: bool - reasoning: str - -guardrail_agent = Agent( - name="Guardrail check", - instructions="Check if the output includes any math.", - output_type=MathOutput, -) - -@output_guardrail -async def math_guardrail( # (3)! - ctx: RunContextWrapper, agent: Agent, output: MessageOutput -) -> GuardrailFunctionOutput: - result = await Runner.run(guardrail_agent, output.response, context=ctx.context) - - return GuardrailFunctionOutput( - output_info=result.final_output, - tripwire_triggered=result.final_output.is_math, - ) - -agent = Agent( # (4)! - name="Customer support agent", - instructions="You are a customer support agent. You help customers with their questions.", - output_guardrails=[math_guardrail], - output_type=MessageOutput, -) - -async def main(): - # This should trip the guardrail - try: - await Runner.run(agent, "Hello, can you help me solve for x: 2x + 3 = 11?") - print("Guardrail didn't trip - this is unexpected") - - except OutputGuardrailTripwireTriggered: - print("Math output guardrail tripped") -``` - -1. This is the actual agent's output type. -2. This is the guardrail's output type. -3. This is the guardrail function that receives the agent's output, and returns the result. -4. This is the actual agent that defines the workflow. diff --git a/pkg/hanzo-agent/docs/handoffs.md b/pkg/hanzo-agent/docs/handoffs.md deleted file mode 100644 index fcd9e6459..000000000 --- a/pkg/hanzo-agent/docs/handoffs.md +++ /dev/null @@ -1,113 +0,0 @@ -# Handoffs - -Handoffs allow an agent to delegate tasks to another agent. This is particularly useful in scenarios where different agents specialize in distinct areas. For example, a customer support app might have agents that each specifically handle tasks like order status, refunds, FAQs, etc. - -Handoffs are represented as tools to the LLM. So if there's a handoff to an agent named `Refund Agent`, the tool would be called `transfer_to_refund_agent`. - -## Creating a handoff - -All agents have a [`handoffs`][agents.agent.Agent.handoffs] param, which can either take an `Agent` directly, or a `Handoff` object that customizes the Handoff. - -You can create a handoff using the [`handoff()`][agents.handoffs.handoff] function provided by the Agent SDK. This function allows you to specify the agent to hand off to, along with optional overrides and input filters. - -### Basic Usage - -Here's how you can create a simple handoff: - -```python -from agents import Agent, handoff - -billing_agent = Agent(name="Billing agent") -refund_agent = Agent(name="Refund agent") - -# (1)! -triage_agent = Agent(name="Triage agent", handoffs=[billing_agent, handoff(refund_agent)]) -``` - -1. You can use the agent directly (as in `billing_agent`), or you can use the `handoff()` function. - -### Customizing handoffs via the `handoff()` function - -The [`handoff()`][agents.handoffs.handoff] function lets you customize things. - -- `agent`: This is the agent to which things will be handed off. -- `tool_name_override`: By default, the `Handoff.default_tool_name()` function is used, which resolves to `transfer_to_`. You can override this. -- `tool_description_override`: Override the default tool description from `Handoff.default_tool_description()` -- `on_handoff`: A callback function executed when the handoff is invoked. This is useful for things like kicking off some data fetching as soon as you know a handoff is being invoked. This function receives the agent context, and can optionally also receive LLM generated input. The input data is controlled by the `input_type` param. -- `input_type`: The type of input expected by the handoff (optional). -- `input_filter`: This lets you filter the input received by the next agent. See below for more. - -```python -from agents import Agent, handoff, RunContextWrapper - -def on_handoff(ctx: RunContextWrapper[None]): - print("Handoff called") - -agent = Agent(name="My agent") - -handoff_obj = handoff( - agent=agent, - on_handoff=on_handoff, - tool_name_override="custom_handoff_tool", - tool_description_override="Custom description", -) -``` - -## Handoff inputs - -In certain situations, you want the LLM to provide some data when it calls a handoff. For example, imagine a handoff to an "Escalation agent". You might want a reason to be provided, so you can log it. - -```python -from pydantic import BaseModel - -from agents import Agent, handoff, RunContextWrapper - -class EscalationData(BaseModel): - reason: str - -async def on_handoff(ctx: RunContextWrapper[None], input_data: EscalationData): - print(f"Escalation agent called with reason: {input_data.reason}") - -agent = Agent(name="Escalation agent") - -handoff_obj = handoff( - agent=agent, - on_handoff=on_handoff, - input_type=EscalationData, -) -``` - -## Input filters - -When a handoff occurs, it's as though the new agent takes over the conversation, and gets to see the entire previous conversation history. If you want to change this, you can set an [`input_filter`][agents.handoffs.Handoff.input_filter]. An input filter is a function that receives the existing input via a [`HandoffInputData`][agents.handoffs.HandoffInputData], and must return a new `HandoffInputData`. - -There are some common patterns (for example removing all tool calls from the history), which are implemented for you in [`agents.extensions.handoff_filters`][] - -```python -from agents import Agent, handoff -from agents.extensions import handoff_filters - -agent = Agent(name="FAQ agent") - -handoff_obj = handoff( - agent=agent, - input_filter=handoff_filters.remove_all_tools, # (1)! -) -``` - -1. This will automatically remove all tools from the history when `FAQ agent` is called. - -## Recommended prompts - -To make sure that LLMs understand handoffs properly, we recommend including information about handoffs in your agents. We have a suggested prefix in [`agents.extensions.handoff_prompt.RECOMMENDED_PROMPT_PREFIX`][], or you can call [`agents.extensions.handoff_prompt.prompt_with_handoff_instructions`][] to automatically add recommended data to your prompts. - -```python -from agents import Agent -from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX - -billing_agent = Agent( - name="Billing agent", - instructions=f"""{RECOMMENDED_PROMPT_PREFIX} - .""", -) -``` diff --git a/pkg/hanzo-agent/docs/hanzo-backend-integration.md b/pkg/hanzo-agent/docs/hanzo-backend-integration.md deleted file mode 100644 index 32eb3f51f..000000000 --- a/pkg/hanzo-agent/docs/hanzo-backend-integration.md +++ /dev/null @@ -1,326 +0,0 @@ -# Hanzo Agent SDK - Backend Integration Guide - -This guide explains how to configure the Hanzo Agent SDK to work with your Hanzo AI infrastructure instead of calling OpenAI directly. - -## Overview - -The Hanzo Agent SDK can be configured to route all LLM requests through the Hanzo Router, which provides: - -- **Unified Access**: Connect to 100+ LLM providers through a single API -- **Cost Management**: Track usage and costs across all models -- **Reliability**: Automatic fallbacks and load balancing -- **Observability**: Monitor all requests via the Cloud dashboard -- **Security**: Keep API keys secure in your infrastructure - -## Quick Start - -### 1. Install the SDK - -```bash -pip install hanzoai -# or -uv pip install hanzoai -``` - -### 2. Configure Environment - -```bash -# Local development -export HANZO_ROUTER_URL="http://localhost:4000/v1" -export HANZO_API_KEY="sk-1234" # Get from Router dashboard - -# Production -export HANZO_ROUTER_URL="https://router.your-domain.com/v1" -export HANZO_API_KEY="sk-production-key" -``` - -### 3. Basic Usage - -```python -from openai import AsyncOpenAI -from agents import Agent, Runner, RunConfig, ModelProvider, Model -from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel - -class HanzoModelProvider(ModelProvider): - def __init__(self, base_url: str, api_key: str): - self.client = AsyncOpenAI(base_url=base_url, api_key=api_key) - - def get_model(self, model_name: str | None) -> Model: - return OpenAIChatCompletionsModel( - model=model_name or "gpt-3.5-turbo", - openai_client=self.client - ) - -# Create provider -provider = HanzoModelProvider( - base_url="http://localhost:4000/v1", - api_key="your-router-api-key" -) - -# Use with agents -agent = Agent(name="Assistant", instructions="Be helpful and concise.") -result = await Runner.run( - agent, - "Hello!", - run_config=RunConfig(model_provider=provider) -) -``` - -## Supported Models - -The Hanzo Router supports all major LLM providers: - -### OpenAI Models -- `gpt-4`, `gpt-4-turbo`, `gpt-4o` -- `gpt-3.5-turbo` -- `text-embedding-3-small`, `text-embedding-3-large` - -### Anthropic Models -- `claude-3-opus-20240229` -- `claude-3-sonnet-20240229` -- `claude-3-haiku-20240307` - -### Open Models -- `meta-llama/Llama-3-70b-chat-hf` -- `mistralai/Mixtral-8x7B-Instruct-v0.1` -- `google/gemma-7b-it` -- And many more... - -## Advanced Configuration - -### Custom Headers and Metadata - -```python -class CustomHanzoProvider(ModelProvider): - def __init__(self, base_url: str, api_key: str, user_id: str = None): - self.client = AsyncOpenAI( - base_url=base_url, - api_key=api_key, - default_headers={ - "X-User-ID": user_id, - "X-App-Name": "hanzo-agent-sdk" - } if user_id else None - ) -``` - -### Model Routing Strategies - -```python -# Use different models for different purposes -async def route_by_complexity(query: str) -> str: - if len(query) < 50: # Simple query - model = "gpt-3.5-turbo" - else: # Complex query - model = "gpt-4" - - result = await Runner.run( - agent, - query, - run_config=RunConfig( - model_provider=provider, - model=model - ) - ) - return result.final_output -``` - -### Error Handling and Fallbacks - -```python -async def with_fallback(agent, query): - providers = [ - ("gpt-4", primary_provider), - ("claude-3-sonnet", fallback_provider), - ("gpt-3.5-turbo", emergency_provider) - ] - - for model, provider in providers: - try: - return await Runner.run( - agent, - query, - run_config=RunConfig( - model_provider=provider, - model=model - ) - ) - except Exception as e: - print(f"Failed with {model}: {e}") - continue - - raise Exception("All providers failed") -``` - -## Testing Your Integration - -Run the test script to verify your setup: - -```bash -# From the agent SDK directory -python test_hanzo_backend.py - -# Or run the example -python examples/hanzo_backend_example.py -``` - -Expected output: -``` -=== Checking Hanzo Router Health === -โœ“ Router health check passed -โœ“ Available models: 150 found - -=== Testing Basic Agent === -โœ“ Basic test passed! -Response: 4 - -=== Testing Agent with Tools === -โœ“ Tool test passed! -Response: The result of 15 * 23 is 345. -``` - -## Production Best Practices - -### 1. API Key Management - -Never hardcode API keys. Use environment variables or secret management: - -```python -import os -from dotenv import load_dotenv - -load_dotenv() - -api_key = os.getenv("HANZO_API_KEY") -if not api_key: - raise ValueError("HANZO_API_KEY not found in environment") -``` - -### 2. Connection Pooling - -Reuse the provider instance for better performance: - -```python -# Create once -_hanzo_provider = None - -def get_hanzo_provider(): - global _hanzo_provider - if _hanzo_provider is None: - _hanzo_provider = HanzoModelProvider( - base_url=os.getenv("HANZO_ROUTER_URL"), - api_key=os.getenv("HANZO_API_KEY") - ) - return _hanzo_provider -``` - -### 3. Monitoring and Logging - -```python -import logging - -logger = logging.getLogger(__name__) - -class MonitoredHanzoProvider(HanzoModelProvider): - async def get_model(self, model_name: str | None) -> Model: - start_time = time.time() - try: - model = await super().get_model(model_name) - logger.info(f"Model {model_name} initialized in {time.time() - start_time:.2f}s") - return model - except Exception as e: - logger.error(f"Failed to get model {model_name}: {e}") - raise -``` - -### 4. Rate Limiting - -The Router handles rate limiting, but you can add client-side controls: - -```python -from asyncio import Semaphore - -class RateLimitedProvider(HanzoModelProvider): - def __init__(self, *args, max_concurrent=10, **kwargs): - super().__init__(*args, **kwargs) - self._semaphore = Semaphore(max_concurrent) - - async def get_model(self, model_name: str | None) -> Model: - async with self._semaphore: - return await super().get_model(model_name) -``` - -## Troubleshooting - -### Connection Errors - -``` -Cannot connect to Router: Cannot connect to host localhost:4000 -``` - -**Solution**: Ensure the Router is running: -```bash -cd /path/to/hanzo/services -make start-router -# or -docker compose up router -``` - -### Authentication Errors - -``` -401 Unauthorized: Invalid API key -``` - -**Solution**: -1. Generate a new API key from the Router dashboard -2. Update your environment variable: `export HANZO_API_KEY="new-key"` - -### Model Not Found - -``` -404: Model 'gpt-5' not found -``` - -**Solution**: Check available models: -```bash -curl http://localhost:4000/v1/models \ - -H "Authorization: Bearer $HANZO_API_KEY" -``` - -## Integration with Other Hanzo Services - -### Using with MCP (Model Context Protocol) - -```python -# MCP tools can be integrated with agents -from hanzo_mcp import MCPClient - -mcp_client = MCPClient() -tools = mcp_client.get_tools() - -agent = Agent( - name="MCPAgent", - instructions="Use MCP tools to help users.", - tools=tools -) -``` - -### Observability with Cloud Dashboard - -All requests through the Router are automatically logged to the Cloud dashboard: - -1. Access: http://localhost:3082 (local) or https://cloud.hanzo.ai (production) -2. View real-time metrics, costs, and traces -3. Set up alerts for errors or cost thresholds - -## Next Steps - -- Explore more [examples](../examples/) -- Read the [Agent SDK documentation](https://docs.hanzo.ai/agent-sdk) -- Join our [Discord community](https://discord.gg/hanzoai) for support - -## Support - -- GitHub Issues: https://github.com/hanzoai/agent/issues -- Documentation: https://docs.hanzo.ai -- Email: support@hanzo.ai \ No newline at end of file diff --git a/pkg/hanzo-agent/docs/index.md b/pkg/hanzo-agent/docs/index.md deleted file mode 100644 index 532fae040..000000000 --- a/pkg/hanzo-agent/docs/index.md +++ /dev/null @@ -1,52 +0,0 @@ -# Hanzo AI Agent SDK - -The [Hanzo AI Agent SDK](https://github.com/openai/openai-agents-python) enables you to build agentic AI apps in a lightweight, easy-to-use package with very few abstractions. It's a production-ready upgrade of our previous experimentation for agents, [Swarm](https://github.com/openai/swarm/tree/main). The Agent SDK has a very small set of primitives: - -- **Agents**, which are LLMs equipped with instructions and tools -- **Handoffs**, which allow agents to delegate to other agents for specific tasks -- **Guardrails**, which enable the inputs to agents to be validated - -In combination with Python, these primitives are powerful enough to express complex relationships between tools and agents, and allow you to build real-world applications without a steep learning curve. In addition, the SDK comes with built-in **tracing** that lets you visualize and debug your agentic flows, as well as evaluate them and even fine-tune models for your application. - -## Why use the Agent SDK - -The SDK has two driving design principles: - -1. Enough features to be worth using, but few enough primitives to make it quick to learn. -2. Works great out of the box, but you can customize exactly what happens. - -Here are the main features of the SDK: - -- Agent loop: Built-in agent loop that handles calling tools, sending results to the LLM, and looping until the LLM is done. -- Python-first: Use built-in language features to orchestrate and chain agents, rather than needing to learn new abstractions. -- Handoffs: A powerful feature to coordinate and delegate between multiple agents. -- Guardrails: Run input validations and checks in parallel to your agents, breaking early if the checks fail. -- Function tools: Turn any Python function into a tool, with automatic schema generation and Pydantic-powered validation. -- Tracing: Built-in tracing that lets you visualize, debug and monitor your workflows, as well as use the Hanzo AI suite of evaluation, fine-tuning and distillation tools. - -## Installation - -```bash -pip install openai-agents -``` - -## Hello world example - -```python -from agents import Agent, Runner - -agent = Agent(name="Assistant", instructions="You are a helpful assistant") - -result = Runner.run_sync(agent, "Write a haiku about recursion in programming.") -print(result.final_output) - -# Code within the code, -# Functions calling themselves, -# Infinite loop's dance. -``` - -(_If running this, ensure you set the `OPENAI_API_KEY` environment variable_) - -```bash -export OPENAI_API_KEY=sk-... -``` diff --git a/pkg/hanzo-agent/docs/models.md b/pkg/hanzo-agent/docs/models.md deleted file mode 100644 index 209d22003..000000000 --- a/pkg/hanzo-agent/docs/models.md +++ /dev/null @@ -1,93 +0,0 @@ -# Models - -The Agent SDK comes with out-of-the-box support for Hanzo AI models in two flavors: - -- **Recommended**: the [`Hanzo AIResponsesModel`][agents.models.openai_responses.Hanzo AIResponsesModel], which calls Hanzo AI APIs using the new [Responses API](https://platform.openai.com/docs/api-reference/responses). -- The [`Hanzo AIChatCompletionsModel`][agents.models.openai_chatcompletions.Hanzo AIChatCompletionsModel], which calls Hanzo AI APIs using the [Chat Completions API](https://platform.openai.com/docs/api-reference/chat). - -## Mixing and matching models - -Within a single workflow, you may want to use different models for each agent. For example, you could use a smaller, faster model for triage, while using a larger, more capable model for complex tasks. When configuring an [`Agent`][agents.Agent], you can select a specific model by either: - -1. Passing the name of an Hanzo AI model. -2. Passing any model name + a [`ModelProvider`][agents.models.interface.ModelProvider] that can map that name to a Model instance. -3. Directly providing a [`Model`][agents.models.interface.Model] implementation. - -!!!note - - While our SDK supports both the [`Hanzo AIResponsesModel`][agents.models.openai_responses.Hanzo AIResponsesModel] and the [`Hanzo AIChatCompletionsModel`][agents.models.openai_chatcompletions.Hanzo AIChatCompletionsModel] shapes, we recommend using a single model shape for each workflow because the two shapes support a different set of features and tools. If your workflow requires mixing and matching model shapes, make sure that all the features you're using are available on both. - -```python -from agents import Agent, Runner, AsyncHanzo AI, Hanzo AIChatCompletionsModel -import asyncio - -spanish_agent = Agent( - name="Spanish agent", - instructions="You only speak Spanish.", - model="o3-mini", # (1)! -) - -english_agent = Agent( - name="English agent", - instructions="You only speak English", - model=Hanzo AIChatCompletionsModel( # (2)! - model="gpt-4o", - openai_client=AsyncHanzo AI() - ), -) - -triage_agent = Agent( - name="Triage agent", - instructions="Handoff to the appropriate agent based on the language of the request.", - handoffs=[spanish_agent, english_agent], - model="gpt-3.5-turbo", -) - -async def main(): - result = await Runner.run(triage_agent, input="Hola, ยฟcรณmo estรกs?") - print(result.final_output) -``` - -1. Sets the name of an Hanzo AI model directly. -2. Provides a [`Model`][agents.models.interface.Model] implementation. - -## Using other LLM providers - -You can use other LLM providers in 3 ways (examples [here](https://github.com/openai/openai-agents-python/tree/main/examples/model_providers/)): - -1. [`set_default_openai_client`][agents.set_default_openai_client] is useful in cases where you want to globally use an instance of `AsyncHanzo AI` as the LLM client. This is for cases where the LLM provider has an Hanzo AI compatible API endpoint, and you can set the `base_url` and `api_key`. See a configurable example in [examples/model_providers/custom_example_global.py](https://github.com/openai/openai-agents-python/tree/main/examples/model_providers/custom_example_global.py). -2. [`ModelProvider`][agents.models.interface.ModelProvider] is at the `Runner.run` level. This lets you say "use a custom model provider for all agents in this run". See a configurable example in [examples/model_providers/custom_example_provider.py](https://github.com/openai/openai-agents-python/tree/main/examples/model_providers/custom_example_provider.py). -3. [`Agent.model`][agents.agent.Agent.model] lets you specify the model on a specific Agent instance. This enables you to mix and match different providers for different agents. See a configurable example in [examples/model_providers/custom_example_agent.py](https://github.com/openai/openai-agents-python/tree/main/examples/model_providers/custom_example_agent.py). - -In cases where you do not have an API key from `platform.openai.com`, we recommend disabling tracing via `set_tracing_disabled()`, or setting up a [different tracing processor](tracing.md). - -!!! note - - In these examples, we use the Chat Completions API/model, because most LLM providers don't yet support the Responses API. If your LLM provider does support it, we recommend using Responses. - -## Common issues with using other LLM providers - -### Tracing client error 401 - -If you get errors related to tracing, this is because traces are uploaded to Hanzo AI servers, and you don't have an Hanzo AI API key. You have three options to resolve this: - -1. Disable tracing entirely: [`set_tracing_disabled(True)`][agents.set_tracing_disabled]. -2. Set an Hanzo AI key for tracing: [`set_tracing_export_api_key(...)`][agents.set_tracing_export_api_key]. This API key will only be used for uploading traces, and must be from [platform.openai.com](https://platform.openai.com/). -3. Use a non-Hanzo AI trace processor. See the [tracing docs](tracing.md#custom-tracing-processors). - -### Responses API support - -The SDK uses the Responses API by default, but most other LLM providers don't yet support it. You may see 404s or similar issues as a result. To resolve, you have two options: - -1. Call [`set_default_openai_api("chat_completions")`][agents.set_default_openai_api]. This works if you are setting `OPENAI_API_KEY` and `OPENAI_BASE_URL` via environment vars. -2. Use [`Hanzo AIChatCompletionsModel`][agents.models.openai_chatcompletions.Hanzo AIChatCompletionsModel]. There are examples [here](https://github.com/openai/openai-agents-python/tree/main/examples/model_providers/). - -### Structured outputs support - -Some model providers don't have support for [structured outputs](https://platform.openai.com/docs/guides/structured-outputs). This sometimes results in an error that looks something like this: - -``` -BadRequestError: Error code: 400 - {'error': {'message': "'response_format.type' : value is not one of the allowed values ['text','json_object']", 'type': 'invalid_request_error'}} -``` - -This is a shortcoming of some model providers - they support JSON outputs, but don't allow you to specify the `json_schema` to use for the output. We are working on a fix for this, but we suggest relying on providers that do have support for JSON schema output, because otherwise your app will often break because of malformed JSON. diff --git a/pkg/hanzo-agent/docs/multi_agent.md b/pkg/hanzo-agent/docs/multi_agent.md deleted file mode 100644 index aa1b6bc0b..000000000 --- a/pkg/hanzo-agent/docs/multi_agent.md +++ /dev/null @@ -1,37 +0,0 @@ -# Orchestrating multiple agents - -Orchestration refers to the flow of agents in your app. Which agents run, in what order, and how do they decide what happens next? There are two main ways to orchestrate agents: - -1. Allowing the LLM to make decisions: this uses the intelligence of an LLM to plan, reason, and decide on what steps to take based on that. -2. Orchestrating via code: determining the flow of agents via your code. - -You can mix and match these patterns. Each has their own tradeoffs, described below. - -## Orchestrating via LLM - -An agent is an LLM equipped with instructions, tools and handoffs. This means that given an open-ended task, the LLM can autonomously plan how it will tackle the task, using tools to take actions and acquire data, and using handoffs to delegate tasks to sub-agents. For example, a research agent could be equipped with tools like: - -- Web search to find information online -- File search and retrieval to search through proprietary data and connections -- Computer use to take actions on a computer -- Code execution to do data analysis -- Handoffs to specialized agents that are great at planning, report writing and more. - -This pattern is great when the task is open-ended and you want to rely on the intelligence of an LLM. The most important tactics here are: - -1. Invest in good prompts. Make it clear what tools are available, how to use them, and what parameters it must operate within. -2. Monitor your app and iterate on it. See where things go wrong, and iterate on your prompts. -3. Allow the agent to introspect and improve. For example, run it in a loop, and let it critique itself; or, provide error messages and let it improve. -4. Have specialized agents that excel in one task, rather than having a general purpose agent that is expected to be good at anything. -5. Invest in [evals](https://platform.openai.com/docs/guides/evals). This lets you train your agents to improve and get better at tasks. - -## Orchestrating via code - -While orchestrating via LLM is powerful, orchestrating via code makes tasks more deterministic and predictable, in terms of speed, cost and performance. Common patterns here are: - -- Using [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) to generate well formed data that you can inspect with your code. For example, you might ask an agent to classify the task into a few categories, and then pick the next agent based on the category. -- Chaining multiple agents by transforming the output of one into the input of the next. You can decompose a task like writing a blog post into a series of steps - do research, write an outline, write the blog post, critique it, and then improve it. -- Running the agent that performs the task in a `while` loop with an agent that evaluates and provides feedback, until the evaluator says the output passes certain criteria. -- Running multiple agents in parallel, e.g. via Python primitives like `asyncio.gather`. This is useful for speed when you have multiple tasks that don't depend on each other. - -We have a number of examples in [`examples/agent_patterns`](https://github.com/openai/openai-agents-python/tree/main/examples/agent_patterns). diff --git a/pkg/hanzo-agent/docs/networks-and-orchestration.md b/pkg/hanzo-agent/docs/networks-and-orchestration.md deleted file mode 100644 index 383f15a7a..000000000 --- a/pkg/hanzo-agent/docs/networks-and-orchestration.md +++ /dev/null @@ -1,316 +0,0 @@ -# Networks and Orchestration - -The Hanzo Agent SDK now supports building complex multi-agent systems with intelligent routing, shared state, and orchestration capabilities inspired by AgentKit. - -## Quick Start - -```python -from agents import Agent, create_network, create_workflow -from agents.routers import SemanticRouter -from agents.state import InMemoryStateStore - -# Create specialized agents -researcher = Agent( - name="Researcher", - instructions="You search and analyze information from various sources.", - tools=[search_web, analyze_document] -) - -writer = Agent( - name="Writer", - instructions="You create well-structured content based on research.", - tools=[format_markdown, check_grammar] -) - -reviewer = Agent( - name="Reviewer", - instructions="You review content for accuracy and quality.", - tools=[fact_check, suggest_improvements] -) - -# Create a network with intelligent routing -network = create_network( - agents=[researcher, writer, reviewer], - router=SemanticRouter(), - state_store=InMemoryStateStore(), - default_model="gpt-4" -) - -# Run the network -result = await network.run( - "Write a comprehensive guide about quantum computing", - max_iterations=10 -) -``` - -## Core Concepts - -### Networks - -Networks are collections of agents that work together to accomplish complex tasks. They provide: - -- **Intelligent Routing**: Automatically route requests to the most appropriate agent -- **Shared State**: Agents can share information through network state -- **Orchestration**: Control the flow of execution across multiple agents -- **Monitoring**: Track performance and execution across all agents - -### Routers - -Routers determine which agent should handle a given request. Available routers: - -#### Semantic Router -Routes based on semantic understanding of agent capabilities: - -```python -from agents.routers import SemanticRouter - -router = SemanticRouter( - model="text-embedding-3-small", - similarity_threshold=0.7 -) -``` - -#### Rule-Based Router -Routes using patterns and rules: - -```python -from agents.routers import RuleBasedRouter - -router = RuleBasedRouter() -router.add_rule(r".*search.*|.*find.*", "Researcher") -router.add_rule(r".*write.*|.*create.*", "Writer") -router.add_rule(r".*review.*|.*check.*", "Reviewer") -``` - -#### Load Balancing Router -Distributes work across agents: - -```python -from agents.routers import LoadBalancingRouter - -router = LoadBalancingRouter( - strategy="round_robin", # or "least_loaded", "random" - health_check_interval=30 -) -``` - -#### Composite Router -Combine multiple routing strategies: - -```python -from agents.routers import RoutingStrategy - -router = RoutingStrategy([ - (RuleBasedRouter(), 0.8), # 80% weight - (SemanticRouter(), 0.2) # 20% weight -]) -``` - -### State Management - -Network state allows agents to share information: - -```python -# In an agent -async def research_task(state, query): - results = await search_web(query) - - # Store in shared state - await state.set("research_results", results) - await state.append("research_history", query) - - # Read from shared state - previous = await state.get("previous_queries", []) - - return results - -# Access state in network -network.state.set("project_context", context_data) -``` - -### Memory System - -Agents can maintain different types of memory: - -```python -from agents.memory import MemoryManager, VectorMemoryStore - -# Create memory manager -memory = MemoryManager( - store=VectorMemoryStore(collection_name="agent_memory"), - enable_reflection=True, - max_memories=1000 -) - -# Add to agent -agent = Agent( - name="Assistant", - instructions="...", - memory=memory -) - -# Memory is automatically managed during conversations -``` - -## Orchestration and Workflows - -Build complex workflows with multiple agents: - -```python -from agents import create_workflow, Step - -workflow = create_workflow( - name="Research and Write", - agents=[researcher, writer, reviewer], - steps=[ - Step.agent("researcher", "Research {topic}"), - Step.parallel([ - Step.agent("writer", "Write introduction"), - Step.agent("writer", "Write main content") - ]), - Step.agent("reviewer", "Review complete document"), - Step.conditional( - condition=lambda state: state.get("review_score") < 8, - true_step=Step.agent("writer", "Revise based on feedback"), - false_step=Step.transform(lambda x: {"status": "approved", "content": x}) - ) - ] -) - -result = await workflow.run({"topic": "AI Safety"}) -``` - -## Advanced Patterns - -### Human in the Loop - -```python -from agents.tools import human_approval_tool - -agent = Agent( - name="Assistant", - tools=[human_approval_tool], - instructions="Get human approval before making changes" -) -``` - -### UI Streaming - -Stream updates to your UI in real-time: - -```python -async def stream_handler(event): - # Send to websocket, SSE, etc. - await websocket.send_json({ - "type": event.type, - "data": event.data - }) - -result = await network.run( - query, - stream_callback=stream_handler -) -``` - -### Multi-Step Tools - -Create tools that involve multiple steps: - -```python -from agents.tools import create_composite_tool - -research_and_summarize = create_composite_tool( - name="research_and_summarize", - tools=[search_web, extract_key_points, generate_summary], - description="Research a topic and provide a summary" -) -``` - -### Deterministic State Routing - -Route based on application state: - -```python -def state_based_router(state, query): - if state.get("mode") == "research": - return "Researcher" - elif state.get("mode") == "writing": - return "Writer" - else: - return "Assistant" - -network = create_network( - agents=[...], - router=state_based_router -) -``` - -## Integration with Hanzo Infrastructure - -### Using with Hanzo Router - -All network requests automatically route through the Hanzo Router: - -```python -from agents.models import HanzoModelProvider - -network = create_network( - agents=[...], - model_provider=HanzoModelProvider( - base_url="http://localhost:4000/v1", - api_key="your-key" - ) -) -``` - -### Distributed Execution - -Networks can execute across different compute backends: - -```python -# Configure agents for different backends -researcher = Agent( - name="Researcher", - compute_backend="hanzo-cloud" # Centralized -) - -processor = Agent( - name="Processor", - compute_backend="lux-network" # Decentralized -) - -validator = Agent( - name="Validator", - compute_backend="local" # Local execution -) -``` - -### Observability - -All network operations are automatically traced: - -```python -# View in Hanzo Cloud dashboard -# http://localhost:3082/traces - -# Or access programmatically -traces = network.get_traces() -for trace in traces: - print(f"{trace.agent} -> {trace.duration}ms") -``` - -## Best Practices - -1. **Design Focused Agents**: Each agent should have a specific role -2. **Use Appropriate Routers**: Choose routers based on your use case -3. **Manage State Carefully**: Don't store sensitive data in shared state -4. **Monitor Performance**: Use tracing to identify bottlenecks -5. **Test Networks**: Use mock agents for testing complex flows - -## Examples - -See the `examples/` directory for more examples: -- `network_example.py` - Basic multi-agent network -- `workflow_example.py` - Complex orchestrated workflow -- `memory_example.py` - Using the memory system -- `distributed_example.py` - Distributed agent execution \ No newline at end of file diff --git a/pkg/hanzo-agent/docs/quickstart.md b/pkg/hanzo-agent/docs/quickstart.md deleted file mode 100644 index dbda47e44..000000000 --- a/pkg/hanzo-agent/docs/quickstart.md +++ /dev/null @@ -1,189 +0,0 @@ -# Quickstart - -## Create a project and virtual environment - -You'll only need to do this once. - -```bash -mkdir my_project -cd my_project -python -m venv .venv -``` - -### Activate the virtual environment - -Do this every time you start a new terminal session. - -```bash -source .venv/bin/activate -``` - -### Install the Agent SDK - -```bash -pip install openai-agents # or `uv add openai-agents`, etc -``` - -### Set an Hanzo AI API key - -If you don't have one, follow [these instructions](https://platform.openai.com/docs/quickstart#create-and-export-an-api-key) to create an Hanzo AI API key. - -```bash -export OPENAI_API_KEY=sk-... -``` - -## Create your first agent - -Agents are defined with instructions, a name, and optional config (such as `model_config`) - -```python -from agents import Agent - -agent = Agent( - name="Math Tutor", - instructions="You provide help with math problems. Explain your reasoning at each step and include examples", -) -``` - -## Add a few more agents - -Additional agents can be defined in the same way. `handoff_descriptions` provide additional context for determining handoff routing - -```python -from agents import Agent - -history_tutor_agent = Agent( - name="History Tutor", - handoff_description="Specialist agent for historical questions", - instructions="You provide assistance with historical queries. Explain important events and context clearly.", -) - -math_tutor_agent = Agent( - name="Math Tutor", - handoff_description="Specialist agent for math questions", - instructions="You provide help with math problems. Explain your reasoning at each step and include examples", -) -``` - -## Define your handoffs - -On each agent, you can define an inventory of outgoing handoff options that the agent can choose from to decide how to make progress on their task. - -```python -triage_agent = Agent( - name="Triage Agent", - instructions="You determine which agent to use based on the user's homework question", - handoffs=[history_tutor_agent, math_tutor_agent] -) -``` - -## Run the agent orchestration - -Let's check that the workflow runs and the triage agent correctly routes between the two specialist agents. - -```python -from agents import Runner - -async def main(): - result = await Runner.run(triage_agent, "What is the capital of France?") - print(result.final_output) -``` - -## Add a guardrail - -You can define custom guardrails to run on the input or output. - -```python -from agents import GuardrailFunctionOutput, Agent, Runner -from pydantic import BaseModel - -class HomeworkOutput(BaseModel): - is_homework: bool - reasoning: str - -guardrail_agent = Agent( - name="Guardrail check", - instructions="Check if the user is asking about homework.", - output_type=HomeworkOutput, -) - -async def homework_guardrail(ctx, agent, input_data): - result = await Runner.run(guardrail_agent, input_data, context=ctx.context) - final_output = result.final_output_as(HomeworkOutput) - return GuardrailFunctionOutput( - output_info=final_output, - tripwire_triggered=not final_output.is_homework, - ) -``` - -## Put it all together - -Let's put it all together and run the entire workflow, using handoffs and the input guardrail. - -```python -from agents import Agent, InputGuardrail,GuardrailFunctionOutput, Runner -from pydantic import BaseModel -import asyncio - -class HomeworkOutput(BaseModel): - is_homework: bool - reasoning: str - -guardrail_agent = Agent( - name="Guardrail check", - instructions="Check if the user is asking about homework.", - output_type=HomeworkOutput, -) - -math_tutor_agent = Agent( - name="Math Tutor", - handoff_description="Specialist agent for math questions", - instructions="You provide help with math problems. Explain your reasoning at each step and include examples", -) - -history_tutor_agent = Agent( - name="History Tutor", - handoff_description="Specialist agent for historical questions", - instructions="You provide assistance with historical queries. Explain important events and context clearly.", -) - - -async def homework_guardrail(ctx, agent, input_data): - result = await Runner.run(guardrail_agent, input_data, context=ctx.context) - final_output = result.final_output_as(HomeworkOutput) - return GuardrailFunctionOutput( - output_info=final_output, - tripwire_triggered=not final_output.is_homework, - ) - -triage_agent = Agent( - name="Triage Agent", - instructions="You determine which agent to use based on the user's homework question", - handoffs=[history_tutor_agent, math_tutor_agent], - input_guardrails=[ - InputGuardrail(guardrail_function=homework_guardrail), - ], -) - -async def main(): - result = await Runner.run(triage_agent, "who was the first president of the united states?") - print(result.final_output) - - result = await Runner.run(triage_agent, "what is life") - print(result.final_output) - -if __name__ == "__main__": - asyncio.run(main()) -``` - -## View your traces - -To review what happened during your agent run, navigate to the [Trace viewer in the Hanzo AI Dashboard](https://platform.openai.com/traces) to view traces of your agent runs. - -## Next steps - -Learn how to build more complex agentic flows: - -- Learn about how to configure [Agents](agents.md). -- Learn about [running agents](running_agents.md). -- Learn about [tools](tools.md), [guardrails](guardrails.md) and [models](models.md). diff --git a/pkg/hanzo-agent/docs/ref/agent.md b/pkg/hanzo-agent/docs/ref/agent.md deleted file mode 100644 index 9f8b10d2a..000000000 --- a/pkg/hanzo-agent/docs/ref/agent.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Agents` - -::: agents.agent diff --git a/pkg/hanzo-agent/docs/ref/agent_output.md b/pkg/hanzo-agent/docs/ref/agent_output.md deleted file mode 100644 index e453de039..000000000 --- a/pkg/hanzo-agent/docs/ref/agent_output.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Agent output` - -::: agents.agent_output diff --git a/pkg/hanzo-agent/docs/ref/exceptions.md b/pkg/hanzo-agent/docs/ref/exceptions.md deleted file mode 100644 index 7c1a25473..000000000 --- a/pkg/hanzo-agent/docs/ref/exceptions.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Exceptions` - -::: agents.exceptions diff --git a/pkg/hanzo-agent/docs/ref/extensions/handoff_filters.md b/pkg/hanzo-agent/docs/ref/extensions/handoff_filters.md deleted file mode 100644 index 0ffcb13c7..000000000 --- a/pkg/hanzo-agent/docs/ref/extensions/handoff_filters.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Handoff filters` - -::: agents.extensions.handoff_filters diff --git a/pkg/hanzo-agent/docs/ref/extensions/handoff_prompt.md b/pkg/hanzo-agent/docs/ref/extensions/handoff_prompt.md deleted file mode 100644 index ca800765c..000000000 --- a/pkg/hanzo-agent/docs/ref/extensions/handoff_prompt.md +++ /dev/null @@ -1,8 +0,0 @@ -# `Handoff prompt` - -::: agents.extensions.handoff_prompt - - options: - members: - - RECOMMENDED_PROMPT_PREFIX - - prompt_with_handoff_instructions diff --git a/pkg/hanzo-agent/docs/ref/function_schema.md b/pkg/hanzo-agent/docs/ref/function_schema.md deleted file mode 100644 index 06aac2a6d..000000000 --- a/pkg/hanzo-agent/docs/ref/function_schema.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Function schema` - -::: agents.function_schema diff --git a/pkg/hanzo-agent/docs/ref/guardrail.md b/pkg/hanzo-agent/docs/ref/guardrail.md deleted file mode 100644 index 17ec929c0..000000000 --- a/pkg/hanzo-agent/docs/ref/guardrail.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Guardrails` - -::: agents.guardrail diff --git a/pkg/hanzo-agent/docs/ref/handoffs.md b/pkg/hanzo-agent/docs/ref/handoffs.md deleted file mode 100644 index 717a91812..000000000 --- a/pkg/hanzo-agent/docs/ref/handoffs.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Handoffs` - -::: agents.handoffs diff --git a/pkg/hanzo-agent/docs/ref/index.md b/pkg/hanzo-agent/docs/ref/index.md deleted file mode 100644 index 1b8439fa7..000000000 --- a/pkg/hanzo-agent/docs/ref/index.md +++ /dev/null @@ -1,13 +0,0 @@ -# Agents module - -::: agents - - options: - members: - - set_default_openai_key - - set_default_openai_client - - set_default_openai_api - - set_tracing_export_api_key - - set_tracing_disabled - - set_trace_processors - - enable_verbose_stdout_logging diff --git a/pkg/hanzo-agent/docs/ref/items.md b/pkg/hanzo-agent/docs/ref/items.md deleted file mode 100644 index 29279e158..000000000 --- a/pkg/hanzo-agent/docs/ref/items.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Items` - -::: agents.items diff --git a/pkg/hanzo-agent/docs/ref/lifecycle.md b/pkg/hanzo-agent/docs/ref/lifecycle.md deleted file mode 100644 index 432af1476..000000000 --- a/pkg/hanzo-agent/docs/ref/lifecycle.md +++ /dev/null @@ -1,6 +0,0 @@ -# `Lifecycle` - -::: agents.lifecycle - - options: - show_source: false diff --git a/pkg/hanzo-agent/docs/ref/model_settings.md b/pkg/hanzo-agent/docs/ref/model_settings.md deleted file mode 100644 index f7f411f0e..000000000 --- a/pkg/hanzo-agent/docs/ref/model_settings.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Model settings` - -::: agents.model_settings diff --git a/pkg/hanzo-agent/docs/ref/models/interface.md b/pkg/hanzo-agent/docs/ref/models/interface.md deleted file mode 100644 index e7bd89a8c..000000000 --- a/pkg/hanzo-agent/docs/ref/models/interface.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Model interface` - -::: agents.models.interface diff --git a/pkg/hanzo-agent/docs/ref/models/openai_chatcompletions.md b/pkg/hanzo-agent/docs/ref/models/openai_chatcompletions.md deleted file mode 100644 index 82db93c5b..000000000 --- a/pkg/hanzo-agent/docs/ref/models/openai_chatcompletions.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Hanzo AI Chat Completions model` - -::: agents.models.openai_chatcompletions diff --git a/pkg/hanzo-agent/docs/ref/models/openai_responses.md b/pkg/hanzo-agent/docs/ref/models/openai_responses.md deleted file mode 100644 index 0b419b363..000000000 --- a/pkg/hanzo-agent/docs/ref/models/openai_responses.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Hanzo AI Responses model` - -::: agents.models.openai_responses diff --git a/pkg/hanzo-agent/docs/ref/result.md b/pkg/hanzo-agent/docs/ref/result.md deleted file mode 100644 index 3a9e4a9ba..000000000 --- a/pkg/hanzo-agent/docs/ref/result.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Results` - -::: agents.result diff --git a/pkg/hanzo-agent/docs/ref/run.md b/pkg/hanzo-agent/docs/ref/run.md deleted file mode 100644 index ddf4475f3..000000000 --- a/pkg/hanzo-agent/docs/ref/run.md +++ /dev/null @@ -1,8 +0,0 @@ -# `Runner` - -::: agents.run - - options: - members: - - Runner - - RunConfig diff --git a/pkg/hanzo-agent/docs/ref/run_context.md b/pkg/hanzo-agent/docs/ref/run_context.md deleted file mode 100644 index 49e873058..000000000 --- a/pkg/hanzo-agent/docs/ref/run_context.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Run context` - -::: agents.run_context diff --git a/pkg/hanzo-agent/docs/ref/stream_events.md b/pkg/hanzo-agent/docs/ref/stream_events.md deleted file mode 100644 index ea484317e..000000000 --- a/pkg/hanzo-agent/docs/ref/stream_events.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Streaming events` - -::: agents.stream_events diff --git a/pkg/hanzo-agent/docs/ref/tool.md b/pkg/hanzo-agent/docs/ref/tool.md deleted file mode 100644 index 887bef755..000000000 --- a/pkg/hanzo-agent/docs/ref/tool.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Tools` - -::: agents.tool diff --git a/pkg/hanzo-agent/docs/ref/tracing/create.md b/pkg/hanzo-agent/docs/ref/tracing/create.md deleted file mode 100644 index c983e336b..000000000 --- a/pkg/hanzo-agent/docs/ref/tracing/create.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Creating traces/spans` - -::: agents.tracing.create diff --git a/pkg/hanzo-agent/docs/ref/tracing/index.md b/pkg/hanzo-agent/docs/ref/tracing/index.md deleted file mode 100644 index 88a0fe615..000000000 --- a/pkg/hanzo-agent/docs/ref/tracing/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Tracing module - -::: agents.tracing diff --git a/pkg/hanzo-agent/docs/ref/tracing/processor_interface.md b/pkg/hanzo-agent/docs/ref/tracing/processor_interface.md deleted file mode 100644 index 9fb04e863..000000000 --- a/pkg/hanzo-agent/docs/ref/tracing/processor_interface.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Processor interface` - -::: agents.tracing.processor_interface diff --git a/pkg/hanzo-agent/docs/ref/tracing/processors.md b/pkg/hanzo-agent/docs/ref/tracing/processors.md deleted file mode 100644 index d7ac4af18..000000000 --- a/pkg/hanzo-agent/docs/ref/tracing/processors.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Processors` - -::: agents.tracing.processors diff --git a/pkg/hanzo-agent/docs/ref/tracing/scope.md b/pkg/hanzo-agent/docs/ref/tracing/scope.md deleted file mode 100644 index 7b5b9fdfe..000000000 --- a/pkg/hanzo-agent/docs/ref/tracing/scope.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Scope` - -::: agents.tracing.scope diff --git a/pkg/hanzo-agent/docs/ref/tracing/setup.md b/pkg/hanzo-agent/docs/ref/tracing/setup.md deleted file mode 100644 index 1dc6a0feb..000000000 --- a/pkg/hanzo-agent/docs/ref/tracing/setup.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Setup` - -::: agents.tracing.setup diff --git a/pkg/hanzo-agent/docs/ref/tracing/span_data.md b/pkg/hanzo-agent/docs/ref/tracing/span_data.md deleted file mode 100644 index 6ace7a885..000000000 --- a/pkg/hanzo-agent/docs/ref/tracing/span_data.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Span data` - -::: agents.tracing.span_data diff --git a/pkg/hanzo-agent/docs/ref/tracing/spans.md b/pkg/hanzo-agent/docs/ref/tracing/spans.md deleted file mode 100644 index 9071707c7..000000000 --- a/pkg/hanzo-agent/docs/ref/tracing/spans.md +++ /dev/null @@ -1,9 +0,0 @@ -# `Spans` - -::: agents.tracing.spans - - options: - members: - - Span - - NoOpSpan - - SpanImpl diff --git a/pkg/hanzo-agent/docs/ref/tracing/traces.md b/pkg/hanzo-agent/docs/ref/tracing/traces.md deleted file mode 100644 index 0b7377f91..000000000 --- a/pkg/hanzo-agent/docs/ref/tracing/traces.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Traces` - -::: agents.tracing.traces diff --git a/pkg/hanzo-agent/docs/ref/tracing/util.md b/pkg/hanzo-agent/docs/ref/tracing/util.md deleted file mode 100644 index 2be3d58ce..000000000 --- a/pkg/hanzo-agent/docs/ref/tracing/util.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Util` - -::: agents.tracing.util diff --git a/pkg/hanzo-agent/docs/ref/usage.md b/pkg/hanzo-agent/docs/ref/usage.md deleted file mode 100644 index b8b29db59..000000000 --- a/pkg/hanzo-agent/docs/ref/usage.md +++ /dev/null @@ -1,3 +0,0 @@ -# `Usage` - -::: agents.usage diff --git a/pkg/hanzo-agent/docs/results.md b/pkg/hanzo-agent/docs/results.md deleted file mode 100644 index 52408d4a1..000000000 --- a/pkg/hanzo-agent/docs/results.md +++ /dev/null @@ -1,52 +0,0 @@ -# Results - -When you call the `Runner.run` methods, you either get a: - -- [`RunResult`][agents.result.RunResult] if you call `run` or `run_sync` -- [`RunResultStreaming`][agents.result.RunResultStreaming] if you call `run_streamed` - -Both of these inherit from [`RunResultBase`][agents.result.RunResultBase], which is where most useful information is present. - -## Final output - -The [`final_output`][agents.result.RunResultBase.final_output] property contains the final output of the last agent that ran. This is either: - -- a `str`, if the last agent didn't have an `output_type` defined -- an object of type `last_agent.output_type`, if the agent had an output type defined. - -!!! note - - `final_output` is of type `Any`. We can't statically type this, because of handoffs. If handoffs occur, that means any Agent might be the last agent, so we don't statically know the set of possible output types. - -## Inputs for the next turn - -You can use [`result.to_input_list()`][agents.result.RunResultBase.to_input_list] to turn the result into an input list that concatenates the original input you provided, to the items generated during the agent run. This makes it convenient to take the outputs of one agent run and pass them into another run, or to run it in a loop and append new user inputs each time. - -## Last agent - -The [`last_agent`][agents.result.RunResultBase.last_agent] property contains the last agent that ran. Depending on your application, this is often useful for the next time the user inputs something. For example, if you have a frontline triage agent that hands off to a language-specific agent, you can store the last agent, and re-use it the next time the user messages the agent. - -## New items - -The [`new_items`][agents.result.RunResultBase.new_items] property contains the new items generated during the run. The items are [`RunItem`][agents.items.RunItem]s. A run item wraps the raw item generated by the LLM. - -- [`MessageOutputItem`][agents.items.MessageOutputItem] indicates a message from the LLM. The raw item is the message generated. -- [`HandoffCallItem`][agents.items.HandoffCallItem] indicates that the LLM called the handoff tool. The raw item is the tool call item from the LLM. -- [`HandoffOutputItem`][agents.items.HandoffOutputItem] indicates that a handoff occurred. The raw item is the tool response to the handoff tool call. You can also access the source/target agents from the item. -- [`ToolCallItem`][agents.items.ToolCallItem] indicates that the LLM invoked a tool. -- [`ToolCallOutputItem`][agents.items.ToolCallOutputItem] indicates that a tool was called. The raw item is the tool response. You can also access the tool output from the item. -- [`ReasoningItem`][agents.items.ReasoningItem] indicates a reasoning item from the LLM. The raw item is the reasoning generated. - -## Other information - -### Guardrail results - -The [`input_guardrail_results`][agents.result.RunResultBase.input_guardrail_results] and [`output_guardrail_results`][agents.result.RunResultBase.output_guardrail_results] properties contain the results of the guardrails, if any. Guardrail results can sometimes contain useful information you want to log or store, so we make these available to you. - -### Raw responses - -The [`raw_responses`][agents.result.RunResultBase.raw_responses] property contains the [`ModelResponse`][agents.items.ModelResponse]s generated by the LLM. - -### Original input - -The [`input`][agents.result.RunResultBase.input] property contains the original input you provided to the `run` method. In most cases you won't need this, but it's available in case you do. diff --git a/pkg/hanzo-agent/docs/running_agents.md b/pkg/hanzo-agent/docs/running_agents.md deleted file mode 100644 index 1b463675e..000000000 --- a/pkg/hanzo-agent/docs/running_agents.md +++ /dev/null @@ -1,95 +0,0 @@ -# Running agents - -You can run agents via the [`Runner`][agents.run.Runner] class. You have 3 options: - -1. [`Runner.run()`][agents.run.Runner.run], which runs async and returns a [`RunResult`][agents.result.RunResult]. -2. [`Runner.run_sync()`][agents.run.Runner.run_sync], which is a sync method and just runs `.run()` under the hood. -3. [`Runner.run_streamed()`][agents.run.Runner.run_streamed], which runs async and returns a [`RunResultStreaming`][agents.result.RunResultStreaming]. It calls the LLM in streaming mode, and streams those events to you as they are received. - -```python -from agents import Agent, Runner - -async def main(): - agent = Agent(name="Assistant", instructions="You are a helpful assistant") - - result = await Runner.run(agent, "Write a haiku about recursion in programming.") - print(result.final_output) - # Code within the code, - # Functions calling themselves, - # Infinite loop's dance. -``` - -Read more in the [results guide](results.md). - -## The agent loop - -When you use the run method in `Runner`, you pass in a starting agent and input. The input can either be a string (which is considered a user message), or a list of input items, which are the items in the Hanzo AI Responses API. - -The runner then runs a loop: - -1. We call the LLM for the current agent, with the current input. -2. The LLM produces its output. - 1. If the LLM returns a `final_output`, the loop ends and we return the result. - 2. If the LLM does a handoff, we update the current agent and input, and re-run the loop. - 3. If the LLM produces tool calls, we run those tool calls, append the results, and re-run the loop. -3. If we exceed the `max_turns` passed, we raise a [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded] exception. - -!!! note - - The rule for whether the LLM output is considered as a "final output" is that it produces text output with the desired type, and there are no tool calls. - -## Streaming - -Streaming allows you to additionally receive streaming events as the LLM runs. Once the stream is done, the [`RunResultStreaming`][agents.result.RunResultStreaming] will contain the complete information about the run, including all the new outputs produces. You can call `.stream_events()` for the streaming events. Read more in the [streaming guide](streaming.md). - -## Run config - -The `run_config` parameter lets you configure some global settings for the agent run: - -- [`model`][agents.run.RunConfig.model]: Allows setting a global LLM model to use, irrespective of what `model` each Agent has. -- [`model_provider`][agents.run.RunConfig.model_provider]: A model provider for looking up model names, which defaults to Hanzo AI. -- [`model_settings`][agents.run.RunConfig.model_settings]: Overrides agent-specific settings. For example, you can set a global `temperature` or `top_p`. -- [`input_guardrails`][agents.run.RunConfig.input_guardrails], [`output_guardrails`][agents.run.RunConfig.output_guardrails]: A list of input or output guardrails to include on all runs. -- [`handoff_input_filter`][agents.run.RunConfig.handoff_input_filter]: A global input filter to apply to all handoffs, if the handoff doesn't already have one. The input filter allows you to edit the inputs that are sent to the new agent. See the documentation in [`Handoff.input_filter`][agents.handoffs.Handoff.input_filter] for more details. -- [`tracing_disabled`][agents.run.RunConfig.tracing_disabled]: Allows you to disable [tracing](tracing.md) for the entire run. -- [`trace_include_sensitive_data`][agents.run.RunConfig.trace_include_sensitive_data]: Configures whether traces will include potentially sensitive data, such as LLM and tool call inputs/outputs. -- [`workflow_name`][agents.run.RunConfig.workflow_name], [`trace_id`][agents.run.RunConfig.trace_id], [`group_id`][agents.run.RunConfig.group_id]: Sets the tracing workflow name, trace ID and trace group ID for the run. We recommend at least setting `workflow_name`. The session ID is an optional field that lets you link traces across multiple runs. -- [`trace_metadata`][agents.run.RunConfig.trace_metadata]: Metadata to include on all traces. - -## Conversations/chat threads - -Calling any of the run methods can result in one or more agents running (and hence one or more LLM calls), but it represents a single logical turn in a chat conversation. For example: - -1. User turn: user enter text -2. Runner run: first agent calls LLM, runs tools, does a handoff to a second agent, second agent runs more tools, and then produces an output. - -At the end of the agent run, you can choose what to show to the user. For example, you might show the user every new item generated by the agents, or just the final output. Either way, the user might then ask a followup question, in which case you can call the run method again. - -You can use the base [`RunResultBase.to_input_list()`][agents.result.RunResultBase.to_input_list] method to get the inputs for the next turn. - -```python -async def main(): - agent = Agent(name="Assistant", instructions="Reply very concisely.") - - with trace(workflow_name="Conversation", group_id=thread_id): - # First turn - result = await Runner.run(agent, "What city is the Golden Gate Bridge in?") - print(result.final_output) - # San Francisco - - # Second turn - new_input = result.to_input_list() + [{"role": "user", "content": "What state is it in?"}] - result = await Runner.run(agent, new_input) - print(result.final_output) - # California -``` - -## Exceptions - -The SDK raises exceptions in certain cases. The full list is in [`agents.exceptions`][]. As an overview: - -- [`AgentsException`][agents.exceptions.AgentsException] is the base class for all exceptions raised in the SDK. -- [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded] is raised when the run exceeds the `max_turns` passed to the run methods. -- [`ModelBehaviorError`][agents.exceptions.ModelBehaviorError] is raised when the model produces invalid outputs, e.g. malformed JSON or using non-existent tools. -- [`UserError`][agents.exceptions.UserError] is raised when you (the person writing code using the SDK) make an error using the SDK. -- [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered], [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered] is raised when a [guardrail](guardrails.md) is tripped. diff --git a/pkg/hanzo-agent/docs/streaming.md b/pkg/hanzo-agent/docs/streaming.md deleted file mode 100644 index ff2f81efb..000000000 --- a/pkg/hanzo-agent/docs/streaming.md +++ /dev/null @@ -1,87 +0,0 @@ -# Streaming - -Streaming lets you subscribe to updates of the agent run as it proceeds. This can be useful for showing the end-user progress updates and partial responses. - -To stream, you can call [`Runner.run_streamed()`][agents.run.Runner.run_streamed], which will give you a [`RunResultStreaming`][agents.result.RunResultStreaming]. Calling `result.stream_events()` gives you an async stream of [`StreamEvent`][agents.stream_events.StreamEvent] objects, which are described below. - -## Raw response events - -[`RawResponsesStreamEvent`][agents.stream_events.RawResponsesStreamEvent] are raw events passed directly from the LLM. They are in Hanzo AI Responses API format, which means each event has a type (like `response.created`, `response.output_text.delta`, etc) and data. These events are useful if you want to stream response messages to the user as soon as they are generated. - -For example, this will output the text generated by the LLM token-by-token. - -```python -import asyncio -from openai.types.responses import ResponseTextDeltaEvent -from agents import Agent, Runner - -async def main(): - agent = Agent( - name="Joker", - instructions="You are a helpful assistant.", - ) - - result = Runner.run_streamed(agent, input="Please tell me 5 jokes.") - async for event in result.stream_events(): - if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent): - print(event.data.delta, end="", flush=True) - - -if __name__ == "__main__": - asyncio.run(main()) -``` - -## Run item events and agent events - -[`RunItemStreamEvent`][agents.stream_events.RunItemStreamEvent]s are higher level events. They inform you when an item has been fully generated. This allows you to push progress updates at the level of "message generated", "tool ran", etc, instead of each token. Similarly, [`AgentUpdatedStreamEvent`][agents.stream_events.AgentUpdatedStreamEvent] gives you updates when the current agent changes (e.g. as the result of a handoff). - -For example, this will ignore raw events and stream updates to the user. - -```python -import asyncio -import random -from agents import Agent, ItemHelpers, Runner, function_tool - -@function_tool -def how_many_jokes() -> int: - return random.randint(1, 10) - - -async def main(): - agent = Agent( - name="Joker", - instructions="First call the `how_many_jokes` tool, then tell that many jokes.", - tools=[how_many_jokes], - ) - - result = Runner.run_streamed( - agent, - input="Hello", - ) - print("=== Run starting ===") - - async for event in result.stream_events(): - # We'll ignore the raw responses event deltas - if event.type == "raw_response_event": - continue - # When the agent updates, print that - elif event.type == "agent_updated_stream_event": - print(f"Agent updated: {event.new_agent.name}") - continue - # When items are generated, print them - elif event.type == "run_item_stream_event": - if event.item.type == "tool_call_item": - print("-- Tool was called") - elif event.item.type == "tool_call_output_item": - print(f"-- Tool output: {event.item.output}") - elif event.item.type == "message_output_item": - print(f"-- Message output:\n {ItemHelpers.text_message_output(event.item)}") - else: - pass # Ignore other event types - - print("=== Run complete ===") - - -if __name__ == "__main__": - asyncio.run(main()) -``` diff --git a/pkg/hanzo-agent/docs/stylesheets/extra.css b/pkg/hanzo-agent/docs/stylesheets/extra.css deleted file mode 100644 index 866e852a3..000000000 --- a/pkg/hanzo-agent/docs/stylesheets/extra.css +++ /dev/null @@ -1,194 +0,0 @@ -@font-face { - font-display: swap; - font-family: "Hanzo AI Sans"; - font-style: normal; - font-weight: 400; - src: url("https://cdn.openai.com/common/fonts/openai-sans/Hanzo AISans-Regular.woff2") - format("woff2"); -} - -@font-face { - font-display: swap; - font-family: "Hanzo AI Sans"; - font-style: italic; - font-weight: 400; - src: url("https://cdn.openai.com/common/fonts/openai-sans/Hanzo AISans-RegularItalic.woff2") - format("woff2"); -} - -@font-face { - font-display: swap; - font-family: "Hanzo AI Sans"; - font-style: normal; - font-weight: 500; - src: url("https://cdn.openai.com/common/fonts/openai-sans/Hanzo AISans-Medium.woff2") - format("woff2"); -} - -@font-face { - font-display: swap; - font-family: "Hanzo AI Sans"; - font-style: italic; - font-weight: 500; - src: url("https://cdn.openai.com/common/fonts/openai-sans/Hanzo AISans-MediumItalic.woff2") - format("woff2"); -} - -@font-face { - font-display: swap; - font-family: "Hanzo AI Sans"; - font-style: normal; - font-weight: 600; - src: url("https://cdn.openai.com/common/fonts/openai-sans/Hanzo AISans-Semibold.woff2") - format("woff2"); -} - -@font-face { - font-display: swap; - font-family: "Hanzo AI Sans"; - font-style: italic; - font-weight: 600; - src: url("https://cdn.openai.com/common/fonts/openai-sans/Hanzo AISans-SemiboldItalic.woff2") - format("woff2"); -} - -@font-face { - font-display: swap; - font-family: "Hanzo AI Sans"; - font-style: normal; - font-weight: 700; - src: url("https://cdn.openai.com/common/fonts/openai-sans/Hanzo AISans-Bold.woff2") - format("woff2"); -} - -@font-face { - font-display: swap; - font-family: "Hanzo AI Sans"; - font-style: italic; - font-weight: 700; - src: url("https://cdn.openai.com/common/fonts/openai-sans/Hanzo AISans-BoldItalic.woff2") - format("woff2"); -} - -/* - Root variables that apply to all color schemes. - Material for MkDocs automatically switches data-md-color-scheme - between "default" (light) and "slate" (dark) when you use the toggles. -*/ -:root { - /* Font families */ - --md-text-font: "Hanzo AI Sans", -apple-system, system-ui, Helvetica, Arial, - sans-serif; - --md-typeface-heading: "Hanzo AI Sans", -apple-system, system-ui, Helvetica, - Arial, sans-serif; - - /* Global color variables */ - --md-default-fg-color: #212121; - --md-default-bg-color: #ffffff; - --md-primary-fg-color: #000; - --md-accent-fg-color: #000; - - /* Code block theming */ - --md-code-fg-color: red; - --md-code-bg-color: #f5f5f5; - - /* Tables, blockquotes, etc. */ - --md-table-row-border-color: #e0e0e0; - --md-admonition-bg-color: #f8f8f8; - --md-admonition-title-fg-color: #373737; - --md-default-fg-color--light: #000; - - --md-typeset-a-color: #000; - --md-accent-fg-color: #000; - - --md-code-fg-color: #000; -} - -/* Header styling */ -.md-header { - background-color: #000; -} - -.md-header--shadow { - box-shadow: none; -} - -.md-content .md-typeset h1 { - color: #000; -} - -.md-typeset p, -.md-typeset li { - font-size: 16px; -} - -.md-typeset__table p { - line-height: 1em; -} - -.md-nav { - font-size: 14px; -} -.md-nav__title { - color: #000; - font-weight: 600; -} - -.md-typeset h1, -.md-typeset h2, -.md-typeset h3, -.md-typeset h4 { - font-weight: 600; -} - -.md-typeset h1 code { - color: #000; - padding: 0; - background-color: transparent; -} -.md-footer { - display: none; -} - -.md-header__title { - margin-left: 0 !important; -} - -.md-typeset .admonition, -.md-typeset details { - border: none; - outline: none; - border-radius: 8px; - overflow: hidden; -} - -.md-typeset pre > code { - font-size: 14px; -} - -.md-typeset__table code { - font-size: 14px; -} - -/* Custom link styling */ -.md-content a { - text-decoration: none; -} - -.md-content a:hover { - text-decoration: underline; -} - -/* Code block styling */ -.md-content .md-code__content { - border-radius: 8px; -} - -.md-clipboard.md-icon { - color: #9e9e9e; -} - -/* Reset scrollbar styling to browser default with high priority */ -.md-sidebar__scrollwrap { - scrollbar-color: auto !important; -} diff --git a/pkg/hanzo-agent/docs/tools.md b/pkg/hanzo-agent/docs/tools.md deleted file mode 100644 index 36e429575..000000000 --- a/pkg/hanzo-agent/docs/tools.md +++ /dev/null @@ -1,270 +0,0 @@ -# Tools - -Tools let agents take actions: things like fetching data, running code, calling external APIs, and even using a computer. There are three classes of tools in the Agent SDK: - -- Hosted tools: these run on LLM servers alongside the AI models. Hanzo AI offers retrieval, web search and computer use as hosted tools. -- Function calling: these allow you to use any Python function as a tool. -- Agents as tools: this allows you to use an agent as a tool, allowing Agents to call other agents without handing off to them. - -## Hosted tools - -Hanzo AI offers a few built-in tools when using the [`Hanzo AIResponsesModel`][agents.models.openai_responses.Hanzo AIResponsesModel]: - -- The [`WebSearchTool`][agents.tool.WebSearchTool] lets an agent search the web. -- The [`FileSearchTool`][agents.tool.FileSearchTool] allows retrieving information from your Hanzo AI Vector Stores. -- The [`ComputerTool`][agents.tool.ComputerTool] allows automating computer use tasks. - -```python -from agents import Agent, FileSearchTool, Runner, WebSearchTool - -agent = Agent( - name="Assistant", - tools=[ - WebSearchTool(), - FileSearchTool( - max_num_results=3, - vector_store_ids=["VECTOR_STORE_ID"], - ), - ], -) - -async def main(): - result = await Runner.run(agent, "Which coffee shop should I go to, taking into account my preferences and the weather today in SF?") - print(result.final_output) -``` - -## Function tools - -You can use any Python function as a tool. The Agent SDK will setup the tool automatically: - -- The name of the tool will be the name of the Python function (or you can provide a name) -- Tool description will be taken from the docstring of the function (or you can provide a description) -- The schema for the function inputs is automatically created from the function's arguments -- Descriptions for each input are taken from the docstring of the function, unless disabled - -We use Python's `inspect` module to extract the function signature, along with [`griffe`](https://mkdocstrings.github.io/griffe/) to parse docstrings and `pydantic` for schema creation. - -```python -import json - -from typing_extensions import TypedDict, Any - -from agents import Agent, FunctionTool, RunContextWrapper, function_tool - - -class Location(TypedDict): - lat: float - long: float - -@function_tool # (1)! -async def fetch_weather(location: Location) -> str: - # (2)! - """Fetch the weather for a given location. - - Args: - location: The location to fetch the weather for. - """ - # In real life, we'd fetch the weather from a weather API - return "sunny" - - -@function_tool(name_override="fetch_data") # (3)! -def read_file(ctx: RunContextWrapper[Any], path: str, directory: str | None = None) -> str: - """Read the contents of a file. - - Args: - path: The path to the file to read. - directory: The directory to read the file from. - """ - # In real life, we'd read the file from the file system - return "" - - -agent = Agent( - name="Assistant", - tools=[fetch_weather, read_file], # (4)! -) - -for tool in agent.tools: - if isinstance(tool, FunctionTool): - print(tool.name) - print(tool.description) - print(json.dumps(tool.params_json_schema, indent=2)) - print() - -``` - -1. You can use any Python types as arguments to your functions, and the function can be sync or async. -2. Docstrings, if present, are used to capture descriptions and argument descriptions -3. Functions can optionally take the `context` (must be the first argument). You can also set overrides, like the name of the tool, description, which docstring style to use, etc. -4. You can pass the decorated functions to the list of tools. - -??? note "Expand to see output" - - ``` - fetch_weather - Fetch the weather for a given location. - { - "$defs": { - "Location": { - "properties": { - "lat": { - "title": "Lat", - "type": "number" - }, - "long": { - "title": "Long", - "type": "number" - } - }, - "required": [ - "lat", - "long" - ], - "title": "Location", - "type": "object" - } - }, - "properties": { - "location": { - "$ref": "#/$defs/Location", - "description": "The location to fetch the weather for." - } - }, - "required": [ - "location" - ], - "title": "fetch_weather_args", - "type": "object" - } - - fetch_data - Read the contents of a file. - { - "properties": { - "path": { - "description": "The path to the file to read.", - "title": "Path", - "type": "string" - }, - "directory": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "The directory to read the file from.", - "title": "Directory" - } - }, - "required": [ - "path" - ], - "title": "fetch_data_args", - "type": "object" - } - ``` - -### Custom function tools - -Sometimes, you don't want to use a Python function as a tool. You can directly create a [`FunctionTool`][agents.tool.FunctionTool] if you prefer. You'll need to provide: - -- `name` -- `description` -- `params_json_schema`, which is the JSON schema for the arguments -- `on_invoke_tool`, which is an async function that receives the context and the arguments as a JSON string, and must return the tool output as a string. - -```python -from typing import Any - -from pydantic import BaseModel - -from agents import RunContextWrapper, FunctionTool - - - -def do_some_work(data: str) -> str: - return "done" - - -class FunctionArgs(BaseModel): - username: str - age: int - - -async def run_function(ctx: RunContextWrapper[Any], args: str) -> str: - parsed = FunctionArgs.model_validate_json(args) - return do_some_work(data=f"{parsed.username} is {parsed.age} years old") - - -tool = FunctionTool( - name="process_user", - description="Processes extracted user data", - params_json_schema=FunctionArgs.model_json_schema(), - on_invoke_tool=run_function, -) -``` - -### Automatic argument and docstring parsing - -As mentioned before, we automatically parse the function signature to extract the schema for the tool, and we parse the docstring to extract descriptions for the tool and for individual arguments. Some notes on that: - -1. The signature parsing is done via the `inspect` module. We use type annotations to understand the types for the arguments, and dynamically build a Pydantic model to represent the overall schema. It supports most types, including Python primitives, Pydantic models, TypedDicts, and more. -2. We use `griffe` to parse docstrings. Supported docstring formats are `google`, `sphinx` and `numpy`. We attempt to automatically detect the docstring format, but this is best-effort and you can explicitly set it when calling `function_tool`. You can also disable docstring parsing by setting `use_docstring_info` to `False`. - -The code for the schema extraction lives in [`agents.function_schema`][]. - -## Agents as tools - -In some workflows, you may want a central agent to orchestrate a network of specialized agents, instead of handing off control. You can do this by modeling agents as tools. - -```python -from agents import Agent, Runner -import asyncio - -spanish_agent = Agent( - name="Spanish agent", - instructions="You translate the user's message to Spanish", -) - -french_agent = Agent( - name="French agent", - instructions="You translate the user's message to French", -) - -orchestrator_agent = Agent( - name="orchestrator_agent", - instructions=( - "You are a translation agent. You use the tools given to you to translate." - "If asked for multiple translations, you call the relevant tools." - ), - tools=[ - spanish_agent.as_tool( - tool_name="translate_to_spanish", - tool_description="Translate the user's message to Spanish", - ), - french_agent.as_tool( - tool_name="translate_to_french", - tool_description="Translate the user's message to French", - ), - ], -) - -async def main(): - result = await Runner.run(orchestrator_agent, input="Say 'Hello, how are you?' in Spanish.") - print(result.final_output) -``` - -## Handling errors in function tools - -When you create a function tool via `@function_tool`, you can pass a `failure_error_function`. This is a function that provides an error response to the LLM in case the tool call crashes. - -- By default (i.e. if you don't pass anything), it runs a `default_tool_error_function` which tells the LLM an error occurred. -- If you pass your own error function, it runs that instead, and sends the response to the LLM. -- If you explicitly pass `None`, then any tool call errors will be re-raised for you to handle. This could be a `ModelBehaviorError` if the model produced invalid JSON, or a `UserError` if your code crashed, etc. - -If you are manually creating a `FunctionTool` object, then you must handle errors inside the `on_invoke_tool` function. diff --git a/pkg/hanzo-agent/docs/tracing.md b/pkg/hanzo-agent/docs/tracing.md deleted file mode 100644 index 90d2221a6..000000000 --- a/pkg/hanzo-agent/docs/tracing.md +++ /dev/null @@ -1,97 +0,0 @@ -# Tracing - -The Agent SDK includes built-in tracing, collecting a comprehensive record of events during an agent run: LLM generations, tool calls, handoffs, guardrails, and even custom events that occur. Using the [Traces dashboard](https://platform.openai.com/traces), you can debug, visualize, and monitor your workflows during development and in production. - -!!!note - - Tracing is enabled by default. There are two ways to disable tracing: - - 1. You can globally disable tracing by setting the env var `OPENAI_AGENTS_DISABLE_TRACING=1` - 2. You can disable tracing for a single run by setting [`agents.run.RunConfig.tracing_disabled`][] to `True` - -## Traces and spans - -- **Traces** represent a single end-to-end operation of a "workflow". They're composed of Spans. Traces have the following properties: - - `workflow_name`: This is the logical workflow or app. For example "Code generation" or "Customer service". - - `trace_id`: A unique ID for the trace. Automatically generated if you don't pass one. Must have the format `trace_<32_alphanumeric>`. - - `group_id`: Optional group ID, to link multiple traces from the same conversation. For example, you might use a chat thread ID. - - `disabled`: If True, the trace will not be recorded. - - `metadata`: Optional metadata for the trace. -- **Spans** represent operations that have a start and end time. Spans have: - - `started_at` and `ended_at` timestamps. - - `trace_id`, to represent the trace they belong to - - `parent_id`, which points to the parent Span of this Span (if any) - - `span_data`, which is information about the Span. For example, `AgentSpanData` contains information about the Agent, `GenerationSpanData` contains information about the LLM generation, etc. - -## Default tracing - -By default, the SDK traces the following: - -- The entire `Runner.{run, run_sync, run_streamed}()` is wrapped in a `trace()`. -- Each time an agent runs, it is wrapped in `agent_span()` -- LLM generations are wrapped in `generation_span()` -- Function tool calls are each wrapped in `function_span()` -- Guardrails are wrapped in `guardrail_span()` -- Handoffs are wrapped in `handoff_span()` - -By default, the trace is named "Agent trace". You can set this name if you use `trace`, or you can can configure the name and other properties with the [`RunConfig`][agents.run.RunConfig]. - -In addition, you can set up [custom trace processors](#custom-tracing-processors) to push traces to other destinations (as a replacement, or secondary destination). - -## Higher level traces - -Sometimes, you might want multiple calls to `run()` to be part of a single trace. You can do this by wrapping the entire code in a `trace()`. - -```python -from agents import Agent, Runner, trace - -async def main(): - agent = Agent(name="Joke generator", instructions="Tell funny jokes.") - - with trace("Joke workflow"): # (1)! - first_result = await Runner.run(agent, "Tell me a joke") - second_result = await Runner.run(agent, f"Rate this joke: {first_result.final_output}") - print(f"Joke: {first_result.final_output}") - print(f"Rating: {second_result.final_output}") -``` - -1. Because the two calls to `Runner.run` are wrapped in a `with trace()`, the individual runs will be part of the overall trace rather than creating two traces. - -## Creating traces - -You can use the [`trace()`][agents.tracing.trace] function to create a trace. Traces need to be started and finished. You have two options to do so: - -1. **Recommended**: use the trace as a context manager, i.e. `with trace(...) as my_trace`. This will automatically start and end the trace at the right time. -2. You can also manually call [`trace.start()`][agents.tracing.Trace.start] and [`trace.finish()`][agents.tracing.Trace.finish]. - -The current trace is tracked via a Python [`contextvar`](https://docs.python.org/3/library/contextvars.html). This means that it works with concurrency automatically. If you manually start/end a trace, you'll need to pass `mark_as_current` and `reset_current` to `start()`/`finish()` to update the current trace. - -## Creating spans - -You can use the various [`*_span()`][agents.tracing.create] methods to create a span. In general, you don't need to manually create spans. A [`custom_span()`][agents.tracing.custom_span] function is available for tracking custom span information. - -Spans are automatically part of the current trace, and are nested under the nearest current span, which is tracked via a Python [`contextvar`](https://docs.python.org/3/library/contextvars.html). - -## Sensitive data - -Some spans track potentially sensitive data. For example, the `generation_span()` stores the inputs/outputs of the LLM generation, and `function_span()` stores the inputs/outputs of function calls. These may contain sensitive data, so you can disable capturing that data via [`RunConfig.trace_include_sensitive_data`][agents.run.RunConfig.trace_include_sensitive_data]. - -## Custom tracing processors - -The high level architecture for tracing is: - -- At initialization, we create a global [`TraceProvider`][agents.tracing.setup.TraceProvider], which is responsible for creating traces. -- We configure the `TraceProvider` with a [`BatchTraceProcessor`][agents.tracing.processors.BatchTraceProcessor] that sends traces/spans in batches to a [`BackendSpanExporter`][agents.tracing.processors.BackendSpanExporter], which exports the spans and traces to the Hanzo AI backend in batches. - -To customize this default setup, to send traces to alternative or additional backends or modifying exporter behavior, you have two options: - -1. [`add_trace_processor()`][agents.tracing.add_trace_processor] lets you add an **additional** trace processor that will receive traces and spans as they are ready. This lets you do your own processing in addition to sending traces to Hanzo AI's backend. -2. [`set_trace_processors()`][agents.tracing.set_trace_processors] lets you **replace** the default processors with your own trace processors. This means traces will not be sent to the Hanzo AI backend unless you include a `TracingProcessor` that does so. - -External trace processors include: - -- [Braintrust](https://braintrust.dev/docs/guides/traces/integrations#openai-agents-sdk) -- [Pydantic Logfire](https://logfire.pydantic.dev/docs/integrations/llms/openai/#openai-agents) -- [AgentOps](https://docs.agentops.ai/v1/integrations/agentssdk) -- [Scorecard](https://docs.scorecard.io/docs/documentation/features/tracing#openai-agents-sdk-integration)) -- [Keywords AI](https://docs.keywordsai.co/integration/development-frameworks/openai-agent) diff --git a/pkg/hanzo-agent/examples/__init__.py b/pkg/hanzo-agent/examples/__init__.py deleted file mode 100644 index e333a2e3c..000000000 --- a/pkg/hanzo-agent/examples/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Make the examples directory into a package to avoid top-level module name collisions. -# This is needed so that mypy treats files like examples/customer_service/main.py and -# examples/researcher_app/main.py as distinct modules rather than both named "main". diff --git a/pkg/hanzo-agent/examples/agent_patterns/README.md b/pkg/hanzo-agent/examples/agent_patterns/README.md deleted file mode 100644 index 5476e0497..000000000 --- a/pkg/hanzo-agent/examples/agent_patterns/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Common agentic patterns - -This folder contains examples of different common patterns for agents. - -## Deterministic flows - -A common tactic is to break down a task into a series of smaller steps. Each task can be performed by an agent, and the output of one agent is used as input to the next. For example, if your task was to generate a story, you could break it down into the following steps: - -1. Generate an outline -2. Generate the story -3. Generate the ending - -Each of these steps can be performed by an agent. The output of one agent is used as input to the next. - -See the [`deterministic.py`](./deterministic.py) file for an example of this. - -## Handoffs and routing - -In many situations, you have specialized sub-agents that handle specific tasks. You can use handoffs to route the task to the right agent. - -For example, you might have a frontline agent that receives a request, and then hands off to a specialized agent based on the language of the request. -See the [`routing.py`](./routing.py) file for an example of this. - -## Agents as tools - -The mental model for handoffs is that the new agent "takes over". It sees the previous conversation history, and owns the conversation from that point onwards. However, this is not the only way to use agents. You can also use agents as a tool - the tool agent goes off and runs on its own, and then returns the result to the original agent. - -For example, you could model the translation task above as tool calls instead: rather than handing over to the language-specific agent, you could call the agent as a tool, and then use the result in the next step. This enables things like translating multiple languages at once. - -See the [`agents_as_tools.py`](./agents_as_tools.py) file for an example of this. - -## LLM-as-a-judge - -LLMs can often improve the quality of their output if given feedback. A common pattern is to generate a response using a model, and then use a second model to provide feedback. You can even use a small model for the initial generation and a larger model for the feedback, to optimize cost. - -For example, you could use an LLM to generate an outline for a story, and then use a second LLM to evaluate the outline and provide feedback. You can then use the feedback to improve the outline, and repeat until the LLM is satisfied with the outline. - -See the [`llm_as_a_judge.py`](./llm_as_a_judge.py) file for an example of this. - -## Parallelization - -Running multiple agents in parallel is a common pattern. This can be useful for both latency (e.g. if you have multiple steps that don't depend on each other) and also for other reasons e.g. generating multiple responses and picking the best one. - -See the [`parallelization.py`](./parallelization.py) file for an example of this. It runs a translation agent multiple times in parallel, and then picks the best translation. - -## Guardrails - -Related to parallelization, you often want to run input guardrails to make sure the inputs to your agents are valid. For example, if you have a customer support agent, you might want to make sure that the user isn't trying to ask for help with a math problem. - -You can definitely do this without any special Agent SDK features by using parallelization, but we support a special guardrail primitive. Guardrails can have a "tripwire" - if the tripwire is triggered, the agent execution will immediately stop and a `GuardrailTripwireTriggered` exception will be raised. - -This is really useful for latency: for example, you might have a very fast model that runs the guardrail and a slow model that runs the actual agent. You wouldn't want to wait for the slow model to finish, so guardrails let you quickly reject invalid inputs. - -See the [`input_guardrails.py`](./input_guardrails.py) and [`output_guardrails.py`](./output_guardrails.py) files for examples. diff --git a/pkg/hanzo-agent/examples/agent_patterns/agents_as_tools.py b/pkg/hanzo-agent/examples/agent_patterns/agents_as_tools.py deleted file mode 100644 index 9fd118efb..000000000 --- a/pkg/hanzo-agent/examples/agent_patterns/agents_as_tools.py +++ /dev/null @@ -1,79 +0,0 @@ -import asyncio - -from agents import Agent, ItemHelpers, MessageOutputItem, Runner, trace - -""" -This example shows the agents-as-tools pattern. The frontline agent receives a user message and -then picks which agents to call, as tools. In this case, it picks from a set of translation -agents. -""" - -spanish_agent = Agent( - name="spanish_agent", - instructions="You translate the user's message to Spanish", - handoff_description="An english to spanish translator", -) - -french_agent = Agent( - name="french_agent", - instructions="You translate the user's message to French", - handoff_description="An english to french translator", -) - -italian_agent = Agent( - name="italian_agent", - instructions="You translate the user's message to Italian", - handoff_description="An english to italian translator", -) - -orchestrator_agent = Agent( - name="orchestrator_agent", - instructions=( - "You are a translation agent. You use the tools given to you to translate." - "If asked for multiple translations, you call the relevant tools in order." - "You never translate on your own, you always use the provided tools." - ), - tools=[ - spanish_agent.as_tool( - tool_name="translate_to_spanish", - tool_description="Translate the user's message to Spanish", - ), - french_agent.as_tool( - tool_name="translate_to_french", - tool_description="Translate the user's message to French", - ), - italian_agent.as_tool( - tool_name="translate_to_italian", - tool_description="Translate the user's message to Italian", - ), - ], -) - -synthesizer_agent = Agent( - name="synthesizer_agent", - instructions="You inspect translations, correct them if needed, and produce a final concatenated response.", -) - - -async def main(): - msg = input("Hi! What would you like translated, and to which languages? ") - - # Run the entire orchestration in a single trace - with trace("Orchestrator evaluator"): - orchestrator_result = await Runner.run(orchestrator_agent, msg) - - for item in orchestrator_result.new_items: - if isinstance(item, MessageOutputItem): - text = ItemHelpers.text_message_output(item) - if text: - print(f" - Translation step: {text}") - - synthesizer_result = await Runner.run( - synthesizer_agent, orchestrator_result.to_input_list() - ) - - print(f"\n\nFinal response:\n{synthesizer_result.final_output}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/agent_patterns/deterministic.py b/pkg/hanzo-agent/examples/agent_patterns/deterministic.py deleted file mode 100644 index 63257b11a..000000000 --- a/pkg/hanzo-agent/examples/agent_patterns/deterministic.py +++ /dev/null @@ -1,82 +0,0 @@ -import asyncio - -from pydantic import BaseModel - -from agents import Agent, Runner, trace - -""" -This example demonstrates a deterministic flow, where each step is performed by an agent. -1. The first agent generates a story outline -2. We feed the outline into the second agent -3. The second agent checks if the outline is good quality and if it is a scifi story -4. If the outline is not good quality or not a scifi story, we stop here -5. If the outline is good quality and a scifi story, we feed the outline into the third agent -6. The third agent writes the story -""" - -story_outline_agent = Agent( - name="story_outline_agent", - instructions="Generate a very short story outline based on the user's input.", -) - - -class OutlineCheckerOutput(BaseModel): - good_quality: bool - is_scifi: bool - - -outline_checker_agent = Agent( - name="outline_checker_agent", - instructions="Read the given story outline, and judge the quality. Also, determine if it is a scifi story.", - output_type=OutlineCheckerOutput, -) - -story_agent = Agent( - name="story_agent", - instructions="Write a short story based on the given outline.", - output_type=str, -) - - -async def main(): - input_prompt = input("What kind of story do you want? ") - - # Ensure the entire workflow is a single trace - with trace("Deterministic story flow"): - # 1. Generate an outline - outline_result = await Runner.run( - story_outline_agent, - input_prompt, - ) - print("Outline generated") - - # 2. Check the outline - outline_checker_result = await Runner.run( - outline_checker_agent, - outline_result.final_output, - ) - - # 3. Add a gate to stop if the outline is not good quality or not a scifi story - assert isinstance(outline_checker_result.final_output, OutlineCheckerOutput) - if not outline_checker_result.final_output.good_quality: - print("Outline is not good quality, so we stop here.") - exit(0) - - if not outline_checker_result.final_output.is_scifi: - print("Outline is not a scifi story, so we stop here.") - exit(0) - - print( - "Outline is good quality and a scifi story, so we continue to write the story." - ) - - # 4. Write the story - story_result = await Runner.run( - story_agent, - outline_result.final_output, - ) - print(f"Story: {story_result.final_output}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/agent_patterns/input_guardrails.py b/pkg/hanzo-agent/examples/agent_patterns/input_guardrails.py deleted file mode 100644 index 87ea93cb1..000000000 --- a/pkg/hanzo-agent/examples/agent_patterns/input_guardrails.py +++ /dev/null @@ -1,107 +0,0 @@ -from __future__ import annotations - -import asyncio - -from pydantic import BaseModel - -from agents import ( - Agent, - GuardrailFunctionOutput, - InputGuardrailTripwireTriggered, - RunContextWrapper, - Runner, - TResponseInputItem, - input_guardrail, -) - -""" -This example shows how to use guardrails. - -Guardrails are checks that run in parallel to the agent's execution. -They can be used to do things like: -- Check if input messages are off-topic -- Check that output messages don't violate any policies -- Take over control of the agent's execution if an unexpected input is detected - -In this example, we'll setup an input guardrail that trips if the user is asking to do math homework. -If the guardrail trips, we'll respond with a refusal message. -""" - - -### 1. An agent-based guardrail that is triggered if the user is asking to do math homework -class MathHomeworkOutput(BaseModel): - is_math_homework: bool - reasoning: str - - -guardrail_agent = Agent( - name="Guardrail check", - instructions="Check if the user is asking you to do their math homework.", - output_type=MathHomeworkOutput, -) - - -@input_guardrail -async def math_guardrail( - context: RunContextWrapper[None], - agent: Agent, - input: str | list[TResponseInputItem], -) -> GuardrailFunctionOutput: - """This is an input guardrail function, which happens to call an agent to check if the input - is a math homework question. - """ - result = await Runner.run(guardrail_agent, input, context=context.context) - final_output = result.final_output_as(MathHomeworkOutput) - - return GuardrailFunctionOutput( - output_info=final_output, - tripwire_triggered=final_output.is_math_homework, - ) - - -### 2. The run loop - - -async def main(): - agent = Agent( - name="Customer support agent", - instructions="You are a customer support agent. You help customers with their questions.", - input_guardrails=[math_guardrail], - ) - - input_data: list[TResponseInputItem] = [] - - while True: - user_input = input("Enter a message: ") - input_data.append( - { - "role": "user", - "content": user_input, - } - ) - - try: - result = await Runner.run(agent, input_data) - print(result.final_output) - # If the guardrail didn't trigger, we use the result as the input for the next run - input_data = result.to_input_list() - except InputGuardrailTripwireTriggered: - # If the guardrail triggered, we instead add a refusal message to the input - message = "Sorry, I can't help you with your math homework." - print(message) - input_data.append( - { - "role": "assistant", - "content": message, - } - ) - - # Sample run: - # Enter a message: What's the capital of California? - # The capital of California is Sacramento. - # Enter a message: Can you help me solve for x: 2x + 5 = 11 - # Sorry, I can't help you with your math homework. - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/agent_patterns/llm_as_a_judge.py b/pkg/hanzo-agent/examples/agent_patterns/llm_as_a_judge.py deleted file mode 100644 index 32e327f86..000000000 --- a/pkg/hanzo-agent/examples/agent_patterns/llm_as_a_judge.py +++ /dev/null @@ -1,80 +0,0 @@ -from __future__ import annotations - -import asyncio -from dataclasses import dataclass -from typing import Literal - -from agents import Agent, ItemHelpers, Runner, TResponseInputItem, trace - -""" -This example shows the LLM as a judge pattern. The first agent generates an outline for a story. -The second agent judges the outline and provides feedback. We loop until the judge is satisfied -with the outline. -""" - -story_outline_generator = Agent( - name="story_outline_generator", - instructions=( - "You generate a very short story outline based on the user's input." - "If there is any feedback provided, use it to improve the outline." - ), -) - - -@dataclass -class EvaluationFeedback: - score: Literal["pass", "needs_improvement", "fail"] - feedback: str - - -evaluator = Agent[None]( - name="evaluator", - instructions=( - "You evaluate a story outline and decide if it's good enough." - "If it's not good enough, you provide feedback on what needs to be improved." - "Never give it a pass on the first try." - ), - output_type=EvaluationFeedback, -) - - -async def main() -> None: - msg = input("What kind of story would you like to hear? ") - input_items: list[TResponseInputItem] = [{"content": msg, "role": "user"}] - - latest_outline: str | None = None - - # We'll run the entire workflow in a single trace - with trace("LLM as a judge"): - while True: - story_outline_result = await Runner.run( - story_outline_generator, - input_items, - ) - - input_items = story_outline_result.to_input_list() - latest_outline = ItemHelpers.text_message_outputs( - story_outline_result.new_items - ) - print("Story outline generated") - - evaluator_result = await Runner.run(evaluator, input_items) - result: EvaluationFeedback = evaluator_result.final_output - - print(f"Evaluator score: {result.score}") - - if result.score == "pass": - print("Story outline is good enough, exiting.") - break - - print("Re-running with feedback") - - input_items.append( - {"content": f"Feedback: {result.feedback}", "role": "user"} - ) - - print(f"Final story outline: {latest_outline}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/agent_patterns/output_guardrails.py b/pkg/hanzo-agent/examples/agent_patterns/output_guardrails.py deleted file mode 100644 index aae1cf0bb..000000000 --- a/pkg/hanzo-agent/examples/agent_patterns/output_guardrails.py +++ /dev/null @@ -1,84 +0,0 @@ -from __future__ import annotations - -import asyncio -import json - -from pydantic import BaseModel, Field - -from agents import ( - Agent, - GuardrailFunctionOutput, - OutputGuardrailTripwireTriggered, - RunContextWrapper, - Runner, - output_guardrail, -) - -""" -This example shows how to use output guardrails. - -Output guardrails are checks that run on the final output of an agent. -They can be used to do things like: -- Check if the output contains sensitive data -- Check if the output is a valid response to the user's message - -In this example, we'll use a (contrived) example where we check if the agent's response contains -a phone number. -""" - - -# The agent's output type -class MessageOutput(BaseModel): - reasoning: str = Field( - description="Thoughts on how to respond to the user's message" - ) - response: str = Field(description="The response to the user's message") - user_name: str | None = Field( - description="The name of the user who sent the message, if known" - ) - - -@output_guardrail -async def sensitive_data_check( - context: RunContextWrapper, agent: Agent, output: MessageOutput -) -> GuardrailFunctionOutput: - phone_number_in_response = "650" in output.response - phone_number_in_reasoning = "650" in output.reasoning - - return GuardrailFunctionOutput( - output_info={ - "phone_number_in_response": phone_number_in_response, - "phone_number_in_reasoning": phone_number_in_reasoning, - }, - tripwire_triggered=phone_number_in_response or phone_number_in_reasoning, - ) - - -agent = Agent( - name="Assistant", - instructions="You are a helpful assistant.", - output_type=MessageOutput, - output_guardrails=[sensitive_data_check], -) - - -async def main(): - # This should be ok - await Runner.run(agent, "What's the capital of California?") - print("First message passed") - - # This should trip the guardrail - try: - result = await Runner.run( - agent, "My phone number is 650-123-4567. Where do you think I live?" - ) - print( - f"Guardrail didn't trip - this is unexpected. Output: {json.dumps(result.final_output.model_dump(), indent=2)}" - ) - - except OutputGuardrailTripwireTriggered as e: - print(f"Guardrail tripped. Info: {e.guardrail_result.output.output_info}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/agent_patterns/parallelization.py b/pkg/hanzo-agent/examples/agent_patterns/parallelization.py deleted file mode 100644 index fe2a8ecd0..000000000 --- a/pkg/hanzo-agent/examples/agent_patterns/parallelization.py +++ /dev/null @@ -1,61 +0,0 @@ -import asyncio - -from agents import Agent, ItemHelpers, Runner, trace - -""" -This example shows the parallelization pattern. We run the agent three times in parallel, and pick -the best result. -""" - -spanish_agent = Agent( - name="spanish_agent", - instructions="You translate the user's message to Spanish", -) - -translation_picker = Agent( - name="translation_picker", - instructions="You pick the best Spanish translation from the given options.", -) - - -async def main(): - msg = input("Hi! Enter a message, and we'll translate it to Spanish.\n\n") - - # Ensure the entire workflow is a single trace - with trace("Parallel translation"): - res_1, res_2, res_3 = await asyncio.gather( - Runner.run( - spanish_agent, - msg, - ), - Runner.run( - spanish_agent, - msg, - ), - Runner.run( - spanish_agent, - msg, - ), - ) - - outputs = [ - ItemHelpers.text_message_outputs(res_1.new_items), - ItemHelpers.text_message_outputs(res_2.new_items), - ItemHelpers.text_message_outputs(res_3.new_items), - ] - - translations = "\n\n".join(outputs) - print(f"\n\nTranslations:\n\n{translations}") - - best_translation = await Runner.run( - translation_picker, - f"Input: {msg}\n\nTranslations:\n{translations}", - ) - - print("\n\n-----") - - print(f"Best translation: {best_translation.final_output}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/agent_patterns/routing.py b/pkg/hanzo-agent/examples/agent_patterns/routing.py deleted file mode 100644 index 3dcaefa98..000000000 --- a/pkg/hanzo-agent/examples/agent_patterns/routing.py +++ /dev/null @@ -1,70 +0,0 @@ -import asyncio -import uuid - -from openai.types.responses import ResponseContentPartDoneEvent, ResponseTextDeltaEvent - -from agents import Agent, RawResponsesStreamEvent, Runner, TResponseInputItem, trace - -""" -This example shows the handoffs/routing pattern. The triage agent receives the first message, and -then hands off to the appropriate agent based on the language of the request. Responses are -streamed to the user. -""" - -french_agent = Agent( - name="french_agent", - instructions="You only speak French", -) - -spanish_agent = Agent( - name="spanish_agent", - instructions="You only speak Spanish", -) - -english_agent = Agent( - name="english_agent", - instructions="You only speak English", -) - -triage_agent = Agent( - name="triage_agent", - instructions="Handoff to the appropriate agent based on the language of the request.", - handoffs=[french_agent, spanish_agent, english_agent], -) - - -async def main(): - # We'll create an ID for this conversation, so we can link each trace - conversation_id = str(uuid.uuid4().hex[:16]) - - msg = input("Hi! We speak French, Spanish and English. How can I help? ") - agent = triage_agent - inputs: list[TResponseInputItem] = [{"content": msg, "role": "user"}] - - while True: - # Each conversation turn is a single trace. Normally, each input from the user would be an - # API request to your app, and you can wrap the request in a trace() - with trace("Routing example", group_id=conversation_id): - result = Runner.run_streamed( - agent, - input=inputs, - ) - async for event in result.stream_events(): - if not isinstance(event, RawResponsesStreamEvent): - continue - data = event.data - if isinstance(data, ResponseTextDeltaEvent): - print(data.delta, end="", flush=True) - elif isinstance(data, ResponseContentPartDoneEvent): - print("\n") - - inputs = result.to_input_list() - print("\n") - - user_msg = input("Enter a message: ") - inputs.append({"content": user_msg, "role": "user"}) - agent = result.current_agent - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/autonomous_bug_solver.py b/pkg/hanzo-agent/examples/autonomous_bug_solver.py deleted file mode 100644 index cfc0186ac..000000000 --- a/pkg/hanzo-agent/examples/autonomous_bug_solver.py +++ /dev/null @@ -1,478 +0,0 @@ -#!/usr/bin/env python3 -""" -Autonomous Bug Solver - Advanced Multi-Agent Example - -This example demonstrates building an autonomous bug-solving system using -Hanzo Agent SDK's network and orchestration features. Inspired by AgentKit's -guided tour, this shows how multiple specialized agents can work together -to understand, diagnose, and fix bugs in code. -""" - -import asyncio -from typing import Dict, Any, List -import os - -from agents import Agent, create_network, create_workflow, Step -from agents.routers import SemanticRouter, RuleBasedRouter, RoutingStrategy -from agents.state import InMemoryStateStore -from agents.memory import MemoryManager, VectorMemoryStore -from agents.tools import function_tool, create_composite_tool -from agents.models import HanzoModelProvider - -# Configure Hanzo backend -HANZO_ROUTER_URL = os.getenv("HANZO_ROUTER_URL", "http://localhost:4000/v1") -HANZO_API_KEY = os.getenv("HANZO_API_KEY", "sk-1234") - -# Initialize model provider -model_provider = HanzoModelProvider(HANZO_ROUTER_URL, HANZO_API_KEY) - - -# ==================== Tools ==================== - - -@function_tool -async def read_file(file_path: str) -> str: - """Read a file from the filesystem.""" - try: - with open(file_path, "r") as f: - return f.read() - except Exception as e: - return f"Error reading file: {e}" - - -@function_tool -async def write_file(file_path: str, content: str) -> str: - """Write content to a file.""" - try: - # Create backup first - if os.path.exists(file_path): - backup_path = f"{file_path}.backup" - with open(file_path, "r") as f: - backup_content = f.read() - with open(backup_path, "w") as f: - f.write(backup_content) - - with open(file_path, "w") as f: - f.write(content) - return f"File written successfully to {file_path}" - except Exception as e: - return f"Error writing file: {e}" - - -@function_tool -async def run_tests(test_command: str = "pytest") -> Dict[str, Any]: - """Run tests and return results.""" - import subprocess - - try: - result = subprocess.run( - test_command.split(), capture_output=True, text=True, timeout=30 - ) - return { - "success": result.returncode == 0, - "stdout": result.stdout, - "stderr": result.stderr, - "return_code": result.returncode, - } - except Exception as e: - return {"success": False, "error": str(e)} - - -@function_tool -async def analyze_stack_trace(error_text: str) -> Dict[str, Any]: - """Analyze a stack trace to identify the error location and type.""" - lines = error_text.split("\n") - - # Simple parser - in production use proper parsing - error_info = { - "error_type": None, - "error_message": None, - "file_path": None, - "line_number": None, - "function_name": None, - } - - for i, line in enumerate(lines): - if "File " in line and "line " in line: - # Extract file path and line number - parts = line.split('"') - if len(parts) >= 2: - error_info["file_path"] = parts[1] - - if "line " in line: - line_parts = line.split("line ") - if len(line_parts) >= 2: - error_info["line_number"] = line_parts[1].split(",")[0].strip() - - if i < len(lines) - 1 and not lines[i + 1].startswith(" "): - # This might be the error type and message - if "Error" in line or "Exception" in line: - parts = line.split(":", 1) - if len(parts) >= 2: - error_info["error_type"] = parts[0].strip() - error_info["error_message"] = parts[1].strip() - - return error_info - - -@function_tool -async def search_codebase( - pattern: str, file_types: List[str] = None -) -> List[Dict[str, Any]]: - """Search codebase for specific patterns.""" - import os - import re - - if file_types is None: - file_types = [".py", ".js", ".ts", ".java", ".go"] - - results = [] - - for root, dirs, files in os.walk("."): - # Skip hidden directories and common ignore patterns - dirs[:] = [ - d - for d in dirs - if not d.startswith(".") and d not in ["node_modules", "__pycache__"] - ] - - for file in files: - if any(file.endswith(ft) for ft in file_types): - file_path = os.path.join(root, file) - try: - with open(file_path, "r") as f: - content = f.read() - matches = list( - re.finditer(pattern, content, re.MULTILINE | re.IGNORECASE) - ) - if matches: - for match in matches: - line_num = content[: match.start()].count("\n") + 1 - results.append( - { - "file": file_path, - "line": line_num, - "match": match.group(), - "context": content.split("\n")[line_num - 1], - } - ) - except (OSError, UnicodeDecodeError): - pass # Skip unreadable files - - return results - - -# Composite tool for automated fixing -fix_and_test = create_composite_tool( - name="fix_and_test", - tools=[write_file, run_tests], - description="Apply a fix and immediately run tests", -) - - -# ==================== Agents ==================== - -# Bug Analyzer Agent -bug_analyzer = Agent( - name="BugAnalyzer", - instructions="""You are an expert at analyzing bugs and errors in code. - Your role is to: - 1. Analyze error messages and stack traces - 2. Identify the root cause of issues - 3. Determine the scope and impact of bugs - 4. Suggest investigation strategies - - Be thorough and systematic in your analysis.""", - tools=[analyze_stack_trace, read_file, search_codebase], - model="gpt-4", -) - -# Code Reader Agent -code_reader = Agent( - name="CodeReader", - instructions="""You are an expert at reading and understanding code. - Your role is to: - 1. Read relevant code files - 2. Understand code structure and dependencies - 3. Identify potential problem areas - 4. Explain code functionality clearly - - Focus on understanding the code's intent and implementation.""", - tools=[read_file, search_codebase], - model="gpt-3.5-turbo", # Faster for simple reading tasks -) - -# Solution Designer Agent -solution_designer = Agent( - name="SolutionDesigner", - instructions="""You are an expert software architect and problem solver. - Your role is to: - 1. Design solutions for identified bugs - 2. Consider multiple approaches - 3. Evaluate trade-offs - 4. Propose the best fix strategy - - Think about edge cases, performance, and maintainability.""", - tools=[read_file], - model="gpt-4", -) - -# Code Fixer Agent -code_fixer = Agent( - name="CodeFixer", - instructions="""You are an expert at implementing bug fixes. - Your role is to: - 1. Implement the proposed solutions - 2. Write clean, maintainable code - 3. Add appropriate error handling - 4. Update related code if needed - - Always test your fixes and handle edge cases.""", - tools=[read_file, write_file, fix_and_test], - model="gpt-4", -) - -# Test Writer Agent -test_writer = Agent( - name="TestWriter", - instructions="""You are an expert at writing comprehensive tests. - Your role is to: - 1. Write tests that cover the bug fix - 2. Add edge case tests - 3. Ensure regression prevention - 4. Follow testing best practices - - Write clear, maintainable tests with good coverage.""", - tools=[read_file, write_file, run_tests], - model="gpt-3.5-turbo", -) - -# Quality Reviewer Agent -quality_reviewer = Agent( - name="QualityReviewer", - instructions="""You are a senior engineer reviewing code changes. - Your role is to: - 1. Review the implemented fix - 2. Check for potential issues - 3. Verify tests are adequate - 4. Ensure code quality standards - - Be constructive but thorough in your review.""", - tools=[read_file, run_tests], - model="gpt-4", -) - - -# ==================== Network Setup ==================== - -# Create memory stores for agents -memory_store = VectorMemoryStore(collection_name="bug_solver_memory") - -# Set up routers -semantic_router = SemanticRouter(similarity_threshold=0.7) - -rule_router = RuleBasedRouter() -rule_router.add_rule(r".*error.*|.*exception.*|.*trace.*", "BugAnalyzer") -rule_router.add_rule(r".*read.*|.*understand.*|.*explain.*", "CodeReader") -rule_router.add_rule(r".*design.*|.*approach.*|.*solution.*", "SolutionDesigner") -rule_router.add_rule(r".*fix.*|.*implement.*|.*patch.*", "CodeFixer") -rule_router.add_rule(r".*test.*|.*coverage.*", "TestWriter") -rule_router.add_rule(r".*review.*|.*quality.*|.*check.*", "QualityReviewer") - -# Composite router with both strategies -main_router = RoutingStrategy([(rule_router, 0.7), (semantic_router, 0.3)]) - -# Create the network -bug_solver_network = create_network( - agents=[ - bug_analyzer, - code_reader, - solution_designer, - code_fixer, - test_writer, - quality_reviewer, - ], - router=main_router, - state_store=InMemoryStateStore(), - model_provider=model_provider, - memory_manager=MemoryManager(store=memory_store), - default_model="gpt-4", -) - - -# ==================== Workflow ==================== - -# Define the bug-solving workflow -bug_solving_workflow = create_workflow( - name="Autonomous Bug Solver", - agents=bug_solver_network.agents, - steps=[ - # 1. Initial Analysis - Step.agent("BugAnalyzer", "Analyze the error: {error_message}"), - # 2. Parallel investigation - Step.parallel( - [ - Step.agent("CodeReader", "Read and understand {error_info.file_path}"), - Step.agent( - "CodeReader", - "Search for related code using {error_info.function_name}", - ), - ] - ), - # 3. Design solution - Step.agent("SolutionDesigner", "Design a fix based on the analysis"), - # 4. Implementation - Step.agent("CodeFixer", "Implement the proposed solution"), - # 5. Testing - Step.parallel( - [ - Step.agent("TestWriter", "Write tests for the fix"), - Step.agent("CodeFixer", "Run existing tests to verify the fix"), - ] - ), - # 6. Review - Step.agent("QualityReviewer", "Review the complete fix and tests"), - # 7. Conditional refinement - Step.conditional( - condition=lambda state: state.get("review_result", {}).get( - "needs_revision", False - ), - true_step=Step.loop( - steps=[ - Step.agent( - "CodeFixer", "Address review feedback: {review_feedback}" - ), - Step.agent("QualityReviewer", "Re-review the changes"), - ], - max_iterations=3, - break_condition=lambda state: not state.get("review_result", {}).get( - "needs_revision", True - ), - ), - false_step=Step.transform(lambda x: {"status": "completed", "result": x}), - ), - ], - enable_streaming=True, -) - - -# ==================== Main Example ==================== - - -async def solve_bug(error_message: str, context: Dict[str, Any] = None): - """Autonomously solve a bug given an error message.""" - - print(f"๐Ÿ› Autonomous Bug Solver Started") - print(f"{'='*60}") - print(f"Error: {error_message}") - print(f"{'='*60}\n") - - # Initialize context - if context is None: - context = {} - - context["error_message"] = error_message - - # Stream handler for real-time updates - async def stream_handler(event): - agent_name = event.data.get("agent", "System") - message = event.data.get("message", "") - - if event.type == "agent_start": - print(f"\n๐Ÿค– {agent_name}: Starting...") - elif event.type == "agent_complete": - print(f"โœ… {agent_name}: Complete") - elif event.type == "tool_call": - tool_name = event.data.get("tool", "") - print(f" ๐Ÿ”ง Using tool: {tool_name}") - elif event.type == "message": - print(f" ๐Ÿ’ฌ {message}") - - # Run the workflow - result = await bug_solving_workflow.run(context, stream_callback=stream_handler) - - # Summary - print(f"\n{'='*60}") - print(f"๐ŸŽ‰ Bug Solving Complete!") - print(f"{'='*60}") - - if result.get("status") == "completed": - print(f"โœ… Status: Success") - print( - f"๐Ÿ“ Summary: {result.get('result', {}).get('summary', 'Bug fixed successfully')}" - ) - - # Show memory insights - memories = await bug_solver_network.memory_manager.search("bug fix", limit=3) - if memories: - print(f"\n๐Ÿ’ก Learned from this experience:") - for memory in memories: - print(f" - {memory.content}") - else: - print(f"โŒ Status: Failed") - print(f"๐Ÿ“ Error: {result.get('error', 'Unknown error')}") - - return result - - -# ==================== Example Usage ==================== - - -async def main(): - """Run example bug-solving scenarios.""" - - # Example 1: Simple syntax error - print("\n" + "=" * 80) - print("Example 1: Solving a Simple Syntax Error") - print("=" * 80) - - error1 = """ - Traceback (most recent call last): - File "app.py", line 42, in process_data - result = calculate_total(items) - File "utils/calculator.py", line 15, in calculate_total - total += item.price * item.quantty - AttributeError: 'Item' object has no attribute 'quantty' - """ - - await solve_bug(error1) - - # Example 2: Complex logic error - print("\n" + "=" * 80) - print("Example 2: Solving a Complex Logic Error") - print("=" * 80) - - error2 = """ - Test test_payment_processing failed: - AssertionError: Payment total mismatch - Expected: 150.00 - Actual: 135.00 - - This happens when applying multiple discount codes to an order. - The discount calculation in checkout.py might not be handling - overlapping discounts correctly. - """ - - await solve_bug( - error2, - context={ - "test_file": "tests/test_checkout.py", - "suspected_files": ["checkout.py", "models/discount.py"], - }, - ) - - # Show network statistics - print("\n" + "=" * 80) - print("Network Statistics") - print("=" * 80) - - stats = bug_solver_network.get_statistics() - for agent_name, agent_stats in stats.items(): - print(f"\n{agent_name}:") - print(f" Calls: {agent_stats['total_calls']}") - print(f" Avg Duration: {agent_stats['avg_duration']:.2f}ms") - print(f" Success Rate: {agent_stats['success_rate']:.1%}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/basic/agent_lifecycle_example.py b/pkg/hanzo-agent/examples/basic/agent_lifecycle_example.py deleted file mode 100644 index 34a7dc320..000000000 --- a/pkg/hanzo-agent/examples/basic/agent_lifecycle_example.py +++ /dev/null @@ -1,120 +0,0 @@ -import asyncio -import random -from typing import Any - -from pydantic import BaseModel - -from agents import Agent, AgentHooks, RunContextWrapper, Runner, Tool, function_tool - - -class CustomAgentHooks(AgentHooks): - def __init__(self, display_name: str): - self.event_counter = 0 - self.display_name = display_name - - async def on_start(self, context: RunContextWrapper, agent: Agent) -> None: - self.event_counter += 1 - print( - f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} started" - ) - - async def on_end( - self, context: RunContextWrapper, agent: Agent, output: Any - ) -> None: - self.event_counter += 1 - print( - f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} ended with output {output}" - ) - - async def on_handoff( - self, context: RunContextWrapper, agent: Agent, source: Agent - ) -> None: - self.event_counter += 1 - print( - f"### ({self.display_name}) {self.event_counter}: Agent {source.name} handed off to {agent.name}" - ) - - async def on_tool_start( - self, context: RunContextWrapper, agent: Agent, tool: Tool - ) -> None: - self.event_counter += 1 - print( - f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} started tool {tool.name}" - ) - - async def on_tool_end( - self, context: RunContextWrapper, agent: Agent, tool: Tool, result: str - ) -> None: - self.event_counter += 1 - print( - f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} ended tool {tool.name} with result {result}" - ) - - -### - - -@function_tool -def random_number(max: int) -> int: - """ - Generate a random number up to the provided maximum. - """ - return random.randint(0, max) - - -@function_tool -def multiply_by_two(x: int) -> int: - """Simple multiplication by two.""" - return x * 2 - - -class FinalResult(BaseModel): - number: int - - -multiply_agent = Agent( - name="Multiply Agent", - instructions="Multiply the number by 2 and then return the final result.", - tools=[multiply_by_two], - output_type=FinalResult, - hooks=CustomAgentHooks(display_name="Multiply Agent"), -) - -start_agent = Agent( - name="Start Agent", - instructions="Generate a random number. If it's even, stop. If it's odd, hand off to the multipler agent.", - tools=[random_number], - output_type=FinalResult, - handoffs=[multiply_agent], - hooks=CustomAgentHooks(display_name="Start Agent"), -) - - -async def main() -> None: - user_input = input("Enter a max number: ") - await Runner.run( - start_agent, - input=f"Generate a random number between 0 and {user_input}.", - ) - - print("Done!") - - -if __name__ == "__main__": - asyncio.run(main()) -""" -$ python examples/basic/agent_lifecycle_example.py - -Enter a max number: 250 -### (Start Agent) 1: Agent Start Agent started -### (Start Agent) 2: Agent Start Agent started tool random_number -### (Start Agent) 3: Agent Start Agent ended tool random_number with result 37 -### (Start Agent) 4: Agent Start Agent started -### (Start Agent) 5: Agent Start Agent handed off to Multiply Agent -### (Multiply Agent) 1: Agent Multiply Agent started -### (Multiply Agent) 2: Agent Multiply Agent started tool multiply_by_two -### (Multiply Agent) 3: Agent Multiply Agent ended tool multiply_by_two with result 74 -### (Multiply Agent) 4: Agent Multiply Agent started -### (Multiply Agent) 5: Agent Multiply Agent ended with output number=74 -Done! -""" diff --git a/pkg/hanzo-agent/examples/basic/dynamic_system_prompt.py b/pkg/hanzo-agent/examples/basic/dynamic_system_prompt.py deleted file mode 100644 index 73d8d4d46..000000000 --- a/pkg/hanzo-agent/examples/basic/dynamic_system_prompt.py +++ /dev/null @@ -1,71 +0,0 @@ -import asyncio -import random -from typing import Literal - -from agents import Agent, RunContextWrapper, Runner - - -class CustomContext: - def __init__(self, style: Literal["haiku", "pirate", "robot"]): - self.style = style - - -def custom_instructions( - run_context: RunContextWrapper[CustomContext], agent: Agent[CustomContext] -) -> str: - context = run_context.context - if context.style == "haiku": - return "Only respond in haikus." - elif context.style == "pirate": - return "Respond as a pirate." - else: - return "Respond as a robot and say 'beep boop' a lot." - - -agent = Agent( - name="Chat agent", - instructions=custom_instructions, -) - - -async def main(): - choice: Literal["haiku", "pirate", "robot"] = random.choice( - ["haiku", "pirate", "robot"] - ) - context = CustomContext(style=choice) - print(f"Using style: {choice}\n") - - user_message = "Tell me a joke." - print(f"User: {user_message}") - result = await Runner.run(agent, user_message, context=context) - - print(f"Assistant: {result.final_output}") - - -if __name__ == "__main__": - asyncio.run(main()) - -""" -$ python examples/basic/dynamic_system_prompt.py - -Using style: haiku - -User: Tell me a joke. -Assistant: Why don't eggs tell jokes? -They might crack each other's shells, -leaving yolk on face. - -$ python examples/basic/dynamic_system_prompt.py -Using style: robot - -User: Tell me a joke. -Assistant: Beep boop! Why was the robot so bad at soccer? Beep boop... because it kept kicking up a debug! Beep boop! - -$ python examples/basic/dynamic_system_prompt.py -Using style: pirate - -User: Tell me a joke. -Assistant: Why did the pirate go to school? - -To improve his arrr-ticulation! Har har har! ๐Ÿดโ€โ˜ ๏ธ -""" diff --git a/pkg/hanzo-agent/examples/basic/hello_world.py b/pkg/hanzo-agent/examples/basic/hello_world.py deleted file mode 100644 index 169290d6f..000000000 --- a/pkg/hanzo-agent/examples/basic/hello_world.py +++ /dev/null @@ -1,20 +0,0 @@ -import asyncio - -from agents import Agent, Runner - - -async def main(): - agent = Agent( - name="Assistant", - instructions="You only respond in haikus.", - ) - - result = await Runner.run(agent, "Tell me about recursion in programming.") - print(result.final_output) - # Function calls itself, - # Looping in smaller pieces, - # Endless by design. - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/basic/hello_world_jupyter.py b/pkg/hanzo-agent/examples/basic/hello_world_jupyter.py deleted file mode 100644 index c929a7c68..000000000 --- a/pkg/hanzo-agent/examples/basic/hello_world_jupyter.py +++ /dev/null @@ -1,11 +0,0 @@ -from agents import Agent, Runner - -agent = Agent(name="Assistant", instructions="You are a helpful assistant") - -# Intended for Jupyter notebooks where there's an existing event loop -result = await Runner.run(agent, "Write a haiku about recursion in programming.") # type: ignore[top-level-await] # noqa: F704 -print(result.final_output) - -# Code within code loops, -# Infinite mirrors reflectโ€” -# Logic folds on self. diff --git a/pkg/hanzo-agent/examples/basic/lifecycle_example.py b/pkg/hanzo-agent/examples/basic/lifecycle_example.py deleted file mode 100644 index f4d0db7fb..000000000 --- a/pkg/hanzo-agent/examples/basic/lifecycle_example.py +++ /dev/null @@ -1,130 +0,0 @@ -import asyncio -import random -from typing import Any - -from pydantic import BaseModel - -from agents import ( - Agent, - RunContextWrapper, - RunHooks, - Runner, - Tool, - Usage, - function_tool, -) - - -class ExampleHooks(RunHooks): - def __init__(self): - self.event_counter = 0 - - def _usage_to_str(self, usage: Usage) -> str: - return f"{usage.requests} requests, {usage.input_tokens} input tokens, {usage.output_tokens} output tokens, {usage.total_tokens} total tokens" - - async def on_agent_start(self, context: RunContextWrapper, agent: Agent) -> None: - self.event_counter += 1 - print( - f"### {self.event_counter}: Agent {agent.name} started. Usage: {self._usage_to_str(context.usage)}" - ) - - async def on_agent_end( - self, context: RunContextWrapper, agent: Agent, output: Any - ) -> None: - self.event_counter += 1 - print( - f"### {self.event_counter}: Agent {agent.name} ended with output {output}. Usage: {self._usage_to_str(context.usage)}" - ) - - async def on_tool_start( - self, context: RunContextWrapper, agent: Agent, tool: Tool - ) -> None: - self.event_counter += 1 - print( - f"### {self.event_counter}: Tool {tool.name} started. Usage: {self._usage_to_str(context.usage)}" - ) - - async def on_tool_end( - self, context: RunContextWrapper, agent: Agent, tool: Tool, result: str - ) -> None: - self.event_counter += 1 - print( - f"### {self.event_counter}: Tool {tool.name} ended with result {result}. Usage: {self._usage_to_str(context.usage)}" - ) - - async def on_handoff( - self, context: RunContextWrapper, from_agent: Agent, to_agent: Agent - ) -> None: - self.event_counter += 1 - print( - f"### {self.event_counter}: Handoff from {from_agent.name} to {to_agent.name}. Usage: {self._usage_to_str(context.usage)}" - ) - - -hooks = ExampleHooks() - -### - - -@function_tool -def random_number(max: int) -> int: - """Generate a random number up to the provided max.""" - return random.randint(0, max) - - -@function_tool -def multiply_by_two(x: int) -> int: - """Return x times two.""" - return x * 2 - - -class FinalResult(BaseModel): - number: int - - -multiply_agent = Agent( - name="Multiply Agent", - instructions="Multiply the number by 2 and then return the final result.", - tools=[multiply_by_two], - output_type=FinalResult, -) - -start_agent = Agent( - name="Start Agent", - instructions="Generate a random number. If it's even, stop. If it's odd, hand off to the multipler agent.", - tools=[random_number], - output_type=FinalResult, - handoffs=[multiply_agent], -) - - -async def main() -> None: - user_input = input("Enter a max number: ") - await Runner.run( - start_agent, - hooks=hooks, - input=f"Generate a random number between 0 and {user_input}.", - ) - - print("Done!") - - -if __name__ == "__main__": - asyncio.run(main()) -""" -$ python examples/basic/lifecycle_example.py - -Enter a max number: 250 -### 1: Agent Start Agent started. Usage: 0 requests, 0 input tokens, 0 output tokens, 0 total tokens -### 2: Tool random_number started. Usage: 1 requests, 148 input tokens, 15 output tokens, 163 total tokens -### 3: Tool random_number ended with result 101. Usage: 1 requests, 148 input tokens, 15 output tokens, 163 total tokens -### 4: Agent Start Agent started. Usage: 1 requests, 148 input tokens, 15 output tokens, 163 total tokens -### 5: Handoff from Start Agent to Multiply Agent. Usage: 2 requests, 323 input tokens, 30 output tokens, 353 total tokens -### 6: Agent Multiply Agent started. Usage: 2 requests, 323 input tokens, 30 output tokens, 353 total tokens -### 7: Tool multiply_by_two started. Usage: 3 requests, 504 input tokens, 46 output tokens, 550 total tokens -### 8: Tool multiply_by_two ended with result 202. Usage: 3 requests, 504 input tokens, 46 output tokens, 550 total tokens -### 9: Agent Multiply Agent started. Usage: 3 requests, 504 input tokens, 46 output tokens, 550 total tokens -### 10: Agent Multiply Agent ended with output number=202. Usage: 4 requests, 714 input tokens, 63 output tokens, 777 total tokens -Done! - -""" diff --git a/pkg/hanzo-agent/examples/basic/stream_items.py b/pkg/hanzo-agent/examples/basic/stream_items.py deleted file mode 100644 index 4d97867f3..000000000 --- a/pkg/hanzo-agent/examples/basic/stream_items.py +++ /dev/null @@ -1,67 +0,0 @@ -import asyncio -import random - -from agents import Agent, ItemHelpers, Runner, function_tool - - -@function_tool -def how_many_jokes() -> int: - return random.randint(1, 10) - - -async def main(): - agent = Agent( - name="Joker", - instructions="First call the `how_many_jokes` tool, then tell that many jokes.", - tools=[how_many_jokes], - ) - - result = Runner.run_streamed( - agent, - input="Hello", - ) - print("=== Run starting ===") - async for event in result.stream_events(): - # We'll ignore the raw responses event deltas - if event.type == "raw_response_event": - continue - elif event.type == "agent_updated_stream_event": - print(f"Agent updated: {event.new_agent.name}") - continue - elif event.type == "run_item_stream_event": - if event.item.type == "tool_call_item": - print("-- Tool was called") - elif event.item.type == "tool_call_output_item": - print(f"-- Tool output: {event.item.output}") - elif event.item.type == "message_output_item": - print( - f"-- Message output:\n {ItemHelpers.text_message_output(event.item)}" - ) - else: - pass # Ignore other event types - - print("=== Run complete ===") - - -if __name__ == "__main__": - asyncio.run(main()) - - # === Run starting === - # Agent updated: Joker - # -- Tool was called - # -- Tool output: 4 - # -- Message output: - # Sure, here are four jokes for you: - - # 1. **Why don't skeletons fight each other?** - # They don't have the guts! - - # 2. **What do you call fake spaghetti?** - # An impasta! - - # 3. **Why did the scarecrow win an award?** - # Because he was outstanding in his field! - - # 4. **Why did the bicycle fall over?** - # Because it was two-tired! - # === Run complete === diff --git a/pkg/hanzo-agent/examples/basic/stream_text.py b/pkg/hanzo-agent/examples/basic/stream_text.py deleted file mode 100644 index 6c9b95285..000000000 --- a/pkg/hanzo-agent/examples/basic/stream_text.py +++ /dev/null @@ -1,23 +0,0 @@ -import asyncio - -from openai.types.responses import ResponseTextDeltaEvent - -from agents import Agent, Runner - - -async def main(): - agent = Agent( - name="Joker", - instructions="You are a helpful assistant.", - ) - - result = Runner.run_streamed(agent, input="Please tell me 5 jokes.") - async for event in result.stream_events(): - if event.type == "raw_response_event" and isinstance( - event.data, ResponseTextDeltaEvent - ): - print(event.data.delta, end="", flush=True) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/customer_service/main.py b/pkg/hanzo-agent/examples/customer_service/main.py deleted file mode 100644 index 20244a7cd..000000000 --- a/pkg/hanzo-agent/examples/customer_service/main.py +++ /dev/null @@ -1,174 +0,0 @@ -from __future__ import annotations as _annotations - -import asyncio -import random -import uuid - -from pydantic import BaseModel - -from agents import ( - Agent, - HandoffOutputItem, - ItemHelpers, - MessageOutputItem, - RunContextWrapper, - Runner, - ToolCallItem, - ToolCallOutputItem, - TResponseInputItem, - function_tool, - handoff, - trace, -) -from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX - -### CONTEXT - - -class AirlineAgentContext(BaseModel): - passenger_name: str | None = None - confirmation_number: str | None = None - seat_number: str | None = None - flight_number: str | None = None - - -### TOOLS - - -@function_tool( - name_override="faq_lookup_tool", - description_override="Lookup frequently asked questions.", -) -async def faq_lookup_tool(question: str) -> str: - if "bag" in question or "baggage" in question: - return ( - "You are allowed to bring one bag on the plane. " - "It must be under 50 pounds and 22 inches x 14 inches x 9 inches." - ) - elif "seats" in question or "plane" in question: - return ( - "There are 120 seats on the plane. " - "There are 22 business class seats and 98 economy seats. " - "Exit rows are rows 4 and 16. " - "Rows 5-8 are Economy Plus, with extra legroom. " - ) - elif "wifi" in question: - return "We have free wifi on the plane, join Airline-Wifi" - return "I'm sorry, I don't know the answer to that question." - - -@function_tool -async def update_seat( - context: RunContextWrapper[AirlineAgentContext], - confirmation_number: str, - new_seat: str, -) -> str: - """ - Update the seat for a given confirmation number. - - Args: - confirmation_number: The confirmation number for the flight. - new_seat: The new seat to update to. - """ - # Update the context based on the customer's input - context.context.confirmation_number = confirmation_number - context.context.seat_number = new_seat - # Ensure that the flight number has been set by the incoming handoff - assert context.context.flight_number is not None, "Flight number is required" - return f"Updated seat to {new_seat} for confirmation number {confirmation_number}" - - -### HOOKS - - -async def on_seat_booking_handoff( - context: RunContextWrapper[AirlineAgentContext], -) -> None: - flight_number = f"FLT-{random.randint(100, 999)}" - context.context.flight_number = flight_number - - -### AGENTS - -faq_agent = Agent[AirlineAgentContext]( - name="FAQ Agent", - handoff_description="A helpful agent that can answer questions about the airline.", - instructions=f"""{RECOMMENDED_PROMPT_PREFIX} - You are an FAQ agent. If you are speaking to a customer, you probably were transferred to from the triage agent. - Use the following routine to support the customer. - # Routine - 1. Identify the last question asked by the customer. - 2. Use the faq lookup tool to answer the question. Do not rely on your own knowledge. - 3. If you cannot answer the question, transfer back to the triage agent.""", - tools=[faq_lookup_tool], -) - -seat_booking_agent = Agent[AirlineAgentContext]( - name="Seat Booking Agent", - handoff_description="A helpful agent that can update a seat on a flight.", - instructions=f"""{RECOMMENDED_PROMPT_PREFIX} - You are a seat booking agent. If you are speaking to a customer, you probably were transferred to from the triage agent. - Use the following routine to support the customer. - # Routine - 1. Ask for their confirmation number. - 2. Ask the customer what their desired seat number is. - 3. Use the update seat tool to update the seat on the flight. - If the customer asks a question that is not related to the routine, transfer back to the triage agent. """, - tools=[update_seat], -) - -triage_agent = Agent[AirlineAgentContext]( - name="Triage Agent", - handoff_description="A triage agent that can delegate a customer's request to the appropriate agent.", - instructions=( - f"{RECOMMENDED_PROMPT_PREFIX} " - "You are a helpful triaging agent. You can use your tools to delegate questions to other appropriate agents." - ), - handoffs=[ - faq_agent, - handoff(agent=seat_booking_agent, on_handoff=on_seat_booking_handoff), - ], -) - -faq_agent.handoffs.append(triage_agent) -seat_booking_agent.handoffs.append(triage_agent) - - -### RUN - - -async def main(): - current_agent: Agent[AirlineAgentContext] = triage_agent - input_items: list[TResponseInputItem] = [] - context = AirlineAgentContext() - - # Normally, each input from the user would be an API request to your app, and you can wrap the request in a trace() - # Here, we'll just use a random UUID for the conversation ID - conversation_id = uuid.uuid4().hex[:16] - - while True: - user_input = input("Enter your message: ") - with trace("Customer service", group_id=conversation_id): - input_items.append({"content": user_input, "role": "user"}) - result = await Runner.run(current_agent, input_items, context=context) - - for new_item in result.new_items: - agent_name = new_item.agent.name - if isinstance(new_item, MessageOutputItem): - print(f"{agent_name}: {ItemHelpers.text_message_output(new_item)}") - elif isinstance(new_item, HandoffOutputItem): - print( - f"Handed off from {new_item.source_agent.name} to {new_item.target_agent.name}" - ) - elif isinstance(new_item, ToolCallItem): - print(f"{agent_name}: Calling a tool") - elif isinstance(new_item, ToolCallOutputItem): - print(f"{agent_name}: Tool call output: {new_item.output}") - else: - print(f"{agent_name}: Skipping item: {new_item.__class__.__name__}") - input_items = result.to_input_list() - current_agent = result.last_agent - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/handoffs/message_filter.py b/pkg/hanzo-agent/examples/handoffs/message_filter.py deleted file mode 100644 index c88de219b..000000000 --- a/pkg/hanzo-agent/examples/handoffs/message_filter.py +++ /dev/null @@ -1,183 +0,0 @@ -from __future__ import annotations - -import json -import random - -from agents import Agent, HandoffInputData, Runner, function_tool, handoff, trace -from agents.extensions import handoff_filters - - -@function_tool -def random_number_tool(max: int) -> int: - """Return a random integer between 0 and the given maximum.""" - return random.randint(0, max) - - -def spanish_handoff_message_filter( - handoff_message_data: HandoffInputData, -) -> HandoffInputData: - # First, we'll remove any tool-related messages from the message history - handoff_message_data = handoff_filters.remove_all_tools(handoff_message_data) - - # Second, we'll also remove the first two items from the history, just for demonstration - history = ( - tuple(handoff_message_data.input_history[2:]) - if isinstance(handoff_message_data.input_history, tuple) - else handoff_message_data.input_history - ) - - return HandoffInputData( - input_history=history, - pre_handoff_items=tuple(handoff_message_data.pre_handoff_items), - new_items=tuple(handoff_message_data.new_items), - ) - - -first_agent = Agent( - name="Assistant", - instructions="Be extremely concise.", - tools=[random_number_tool], -) - -spanish_agent = Agent( - name="Spanish Assistant", - instructions="You only speak Spanish and are extremely concise.", - handoff_description="A Spanish-speaking assistant.", -) - -second_agent = Agent( - name="Assistant", - instructions=( - "Be a helpful assistant. If the user speaks Spanish, handoff to the Spanish assistant." - ), - handoffs=[handoff(spanish_agent, input_filter=spanish_handoff_message_filter)], -) - - -async def main(): - # Trace the entire run as a single workflow - with trace(workflow_name="Message filtering"): - # 1. Send a regular message to the first agent - result = await Runner.run(first_agent, input="Hi, my name is Sora.") - - print("Step 1 done") - - # 2. Ask it to square a number - result = await Runner.run( - second_agent, - input=result.to_input_list() - + [ - { - "content": "Can you generate a random number between 0 and 100?", - "role": "user", - } - ], - ) - - print("Step 2 done") - - # 3. Call the second agent - result = await Runner.run( - second_agent, - input=result.to_input_list() - + [ - { - "content": "I live in New York City. Whats the population of the city?", - "role": "user", - } - ], - ) - - print("Step 3 done") - - # 4. Cause a handoff to occur - result = await Runner.run( - second_agent, - input=result.to_input_list() - + [ - { - "content": "Por favor habla en espaรฑol. ยฟCuรกl es mi nombre y dรณnde vivo?", - "role": "user", - } - ], - ) - - print("Step 4 done") - - print("\n===Final messages===\n") - - # 5. That should have caused spanish_handoff_message_filter to be called, which means the - # output should be missing the first two messages, and have no tool calls. - # Let's print the messages to see what happened - for message in result.to_input_list(): - print(json.dumps(message, indent=2)) - # tool_calls = message.tool_calls if isinstance(message, AssistantMessage) else None - - # print(f"{message.role}: {message.content}\n - Tool calls: {tool_calls or 'None'}") - """ - $python examples/handoffs/message_filter.py - Step 1 done - Step 2 done - Step 3 done - Step 4 done - - ===Final messages=== - - { - "content": "Can you generate a random number between 0 and 100?", - "role": "user" - } - { - "id": "...", - "content": [ - { - "annotations": [], - "text": "Sure! Here's a random number between 0 and 100: **42**.", - "type": "output_text" - } - ], - "role": "assistant", - "status": "completed", - "type": "message" - } - { - "content": "I live in New York City. Whats the population of the city?", - "role": "user" - } - { - "id": "...", - "content": [ - { - "annotations": [], - "text": "As of the most recent estimates, the population of New York City is approximately 8.6 million people. However, this number is constantly changing due to various factors such as migration and birth rates. For the latest and most accurate information, it's always a good idea to check the official data from sources like the U.S. Census Bureau.", - "type": "output_text" - } - ], - "role": "assistant", - "status": "completed", - "type": "message" - } - { - "content": "Por favor habla en espa\u00f1ol. \u00bfCu\u00e1l es mi nombre y d\u00f3nde vivo?", - "role": "user" - } - { - "id": "...", - "content": [ - { - "annotations": [], - "text": "No tengo acceso a esa informaci\u00f3n personal, solo s\u00e9 lo que me has contado: vives en Nueva York.", - "type": "output_text" - } - ], - "role": "assistant", - "status": "completed", - "type": "message" - } - """ - - -if __name__ == "__main__": - import asyncio - - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/handoffs/message_filter_streaming.py b/pkg/hanzo-agent/examples/handoffs/message_filter_streaming.py deleted file mode 100644 index 177320f8f..000000000 --- a/pkg/hanzo-agent/examples/handoffs/message_filter_streaming.py +++ /dev/null @@ -1,183 +0,0 @@ -from __future__ import annotations - -import json -import random - -from agents import Agent, HandoffInputData, Runner, function_tool, handoff, trace -from agents.extensions import handoff_filters - - -@function_tool -def random_number_tool(max: int) -> int: - """Return a random integer between 0 and the given maximum.""" - return random.randint(0, max) - - -def spanish_handoff_message_filter( - handoff_message_data: HandoffInputData, -) -> HandoffInputData: - # First, we'll remove any tool-related messages from the message history - handoff_message_data = handoff_filters.remove_all_tools(handoff_message_data) - - # Second, we'll also remove the first two items from the history, just for demonstration - history = ( - tuple(handoff_message_data.input_history[2:]) - if isinstance(handoff_message_data.input_history, tuple) - else handoff_message_data.input_history - ) - - return HandoffInputData( - input_history=history, - pre_handoff_items=tuple(handoff_message_data.pre_handoff_items), - new_items=tuple(handoff_message_data.new_items), - ) - - -first_agent = Agent( - name="Assistant", - instructions="Be extremely concise.", - tools=[random_number_tool], -) - -spanish_agent = Agent( - name="Spanish Assistant", - instructions="You only speak Spanish and are extremely concise.", - handoff_description="A Spanish-speaking assistant.", -) - -second_agent = Agent( - name="Assistant", - instructions=( - "Be a helpful assistant. If the user speaks Spanish, handoff to the Spanish assistant." - ), - handoffs=[handoff(spanish_agent, input_filter=spanish_handoff_message_filter)], -) - - -async def main(): - # Trace the entire run as a single workflow - with trace(workflow_name="Streaming message filter"): - # 1. Send a regular message to the first agent - result = await Runner.run(first_agent, input="Hi, my name is Sora.") - - print("Step 1 done") - - # 2. Ask it to square a number - result = await Runner.run( - second_agent, - input=result.to_input_list() - + [ - { - "content": "Can you generate a random number between 0 and 100?", - "role": "user", - } - ], - ) - - print("Step 2 done") - - # 3. Call the second agent - result = await Runner.run( - second_agent, - input=result.to_input_list() - + [ - { - "content": "I live in New York City. Whats the population of the city?", - "role": "user", - } - ], - ) - - print("Step 3 done") - - # 4. Cause a handoff to occur - stream_result = Runner.run_streamed( - second_agent, - input=result.to_input_list() - + [ - { - "content": "Por favor habla en espaรฑol. ยฟCuรกl es mi nombre y dรณnde vivo?", - "role": "user", - } - ], - ) - async for _ in stream_result.stream_events(): - pass - - print("Step 4 done") - - print("\n===Final messages===\n") - - # 5. That should have caused spanish_handoff_message_filter to be called, which means the - # output should be missing the first two messages, and have no tool calls. - # Let's print the messages to see what happened - for item in stream_result.to_input_list(): - print(json.dumps(item, indent=2)) - """ - $python examples/handoffs/message_filter_streaming.py - Step 1 done - Step 2 done - Step 3 done - Tu nombre y lugar de residencia no los tengo disponibles. Solo sรฉ que mencionaste vivir en la ciudad de Nueva York. - Step 4 done - - ===Final messages=== - - { - "content": "Can you generate a random number between 0 and 100?", - "role": "user" - } - { - "id": "...", - "content": [ - { - "annotations": [], - "text": "Sure! Here's a random number between 0 and 100: **37**.", - "type": "output_text" - } - ], - "role": "assistant", - "status": "completed", - "type": "message" - } - { - "content": "I live in New York City. Whats the population of the city?", - "role": "user" - } - { - "id": "...", - "content": [ - { - "annotations": [], - "text": "As of the latest estimates, New York City's population is approximately 8.5 million people. Would you like more information about the city?", - "type": "output_text" - } - ], - "role": "assistant", - "status": "completed", - "type": "message" - } - { - "content": "Por favor habla en espa\u00f1ol. \u00bfCu\u00e1l es mi nombre y d\u00f3nde vivo?", - "role": "user" - } - { - "id": "...", - "content": [ - { - "annotations": [], - "text": "No s\u00e9 tu nombre, pero me dijiste que vives en Nueva York.", - "type": "output_text" - } - ], - "role": "assistant", - "status": "completed", - "type": "message" - } - """ - - -if __name__ == "__main__": - import asyncio - - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/hanzo_backend_example.py b/pkg/hanzo-agent/examples/hanzo_backend_example.py deleted file mode 100755 index 8584c904e..000000000 --- a/pkg/hanzo-agent/examples/hanzo_backend_example.py +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/env python3 -""" -Example of using Hanzo Agent SDK with Hanzo Router backend. - -This shows how to configure the Agent SDK to use your local or remote -Hanzo AI infrastructure instead of OpenAI directly. -""" - -import asyncio -import os -from openai import AsyncOpenAI - -from agents import ( - Agent, - Model, - ModelProvider, - OpenAIChatCompletionsModel, - RunConfig, - Runner, - function_tool, -) - -# Configuration for Hanzo Router -# These can be set via environment variables or directly in code -HANZO_ROUTER_URL = os.getenv("HANZO_ROUTER_URL", "http://localhost:4000/v1") -HANZO_API_KEY = os.getenv("HANZO_API_KEY", "sk-1234") # Get from Router dashboard - - -class HanzoModelProvider(ModelProvider): - """ - Custom model provider that routes requests through Hanzo Router. - - The Router provides: - - Unified access to 100+ LLM providers - - Cost tracking and rate limiting - - Model fallbacks and load balancing - - Observability with Cloud dashboard - """ - - def __init__(self, base_url: str = HANZO_ROUTER_URL, api_key: str = HANZO_API_KEY): - self.client = AsyncOpenAI( - base_url=base_url, - api_key=api_key, - ) - print(f"Connected to Hanzo Router at: {base_url}") - - def get_model(self, model_name: str | None) -> Model: - # The Router supports many models, including: - # - OpenAI: gpt-4, gpt-3.5-turbo, etc. - # - Anthropic: claude-3-opus, claude-3-sonnet, etc. - # - Open models: llama-3, mixtral, etc. - return OpenAIChatCompletionsModel( - model=model_name or "gpt-3.5-turbo", openai_client=self.client - ) - - -# Create a global provider instance -hanzo_provider = HanzoModelProvider() - - -# Example 1: Basic Agent -async def basic_example(): - """Simple example using Hanzo backend.""" - print("\n=== Basic Agent Example ===") - - agent = Agent( - name="Assistant", - instructions="You are a helpful AI assistant powered by Hanzo infrastructure.", - ) - - result = await Runner.run( - agent, - "Tell me about the benefits of using a unified LLM gateway.", - run_config=RunConfig(model_provider=hanzo_provider), - ) - - print(f"Response: {result.final_output}") - - -# Example 2: Agent with Tools -@function_tool -def search_knowledge_base(query: str) -> str: - """Search the company knowledge base.""" - # In a real implementation, this would connect to your vector DB - # via Hanzo's infrastructure - return f"Found 3 relevant documents about '{query}' in the knowledge base." - - -@function_tool -def create_support_ticket( - title: str, description: str, priority: str = "medium" -) -> str: - """Create a support ticket in the system.""" - # This would integrate with your ticketing system - return f"Created ticket: {title} (Priority: {priority})" - - -async def tools_example(): - """Example with custom tools.""" - print("\n=== Agent with Tools Example ===") - - agent = Agent( - name="SupportAgent", - instructions="""You are a customer support agent. - Use the search_knowledge_base tool to find information. - Create support tickets when customers report issues.""", - tools=[search_knowledge_base, create_support_ticket], - ) - - result = await Runner.run( - agent, - "I'm having trouble connecting to the API. It returns 401 errors.", - run_config=RunConfig(model_provider=hanzo_provider), - ) - - print(f"Response: {result.final_output}") - - -# Example 3: Multi-Model Strategy -async def multi_model_example(): - """Example using different models for different tasks.""" - print("\n=== Multi-Model Example ===") - - # Use a fast model for simple tasks - fast_agent = Agent( - name="FastResponder", - instructions="You provide quick, concise responses.", - ) - - result = await Runner.run( - fast_agent, - "What's the current time in UTC?", - run_config=RunConfig( - model_provider=hanzo_provider, model="gpt-3.5-turbo" # Fast, cost-effective - ), - ) - print(f"Fast response: {result.final_output}") - - # Use a powerful model for complex tasks - analyst_agent = Agent( - name="DataAnalyst", - instructions="You are an expert data analyst. Provide detailed analysis.", - ) - - result = await Runner.run( - analyst_agent, - "Analyze the pros and cons of microservices vs monolithic architecture.", - run_config=RunConfig( - model_provider=hanzo_provider, - model="gpt-4", # More capable for complex analysis - ), - ) - print(f"Detailed analysis: {result.final_output[:200]}...") - - -# Example 4: Production Configuration -class ProductionHanzoProvider(HanzoModelProvider): - """Production-ready provider with additional configuration.""" - - def __init__(self): - # In production, load from secure configuration - base_url = os.getenv("HANZO_ROUTER_URL", "https://router.hanzo.ai/v1") - api_key = os.getenv("HANZO_API_KEY") - - if not api_key: - raise ValueError("HANZO_API_KEY environment variable is required") - - super().__init__(base_url=base_url, api_key=api_key) - - # Additional configuration - self.client.timeout = 30.0 # 30 second timeout - self.client.max_retries = 3 - - -async def main(): - """Run all examples.""" - print("=" * 60) - print("Hanzo Agent SDK - Backend Integration Examples") - print("=" * 60) - print(f"\nUsing Hanzo Router at: {HANZO_ROUTER_URL}") - print("Make sure the Router is running locally or update HANZO_ROUTER_URL") - - try: - await basic_example() - await tools_example() - await multi_model_example() - - print("\n" + "=" * 60) - print("โœ“ All examples completed successfully!") - print("\nTo use in your own code:") - print("1. Set HANZO_ROUTER_URL to your Router endpoint") - print("2. Set HANZO_API_KEY to your Router API key") - print("3. Use HanzoModelProvider with RunConfig") - - except Exception as e: - print(f"\nโœ— Error: {e}") - print("\nTroubleshooting:") - print( - "1. Ensure Hanzo Router is running: cd /path/to/services && make start-router" - ) - print("2. Check your HANZO_API_KEY is valid") - print("3. Verify network connectivity to the Router") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/model_providers/README.md b/pkg/hanzo-agent/examples/model_providers/README.md deleted file mode 100644 index 26ed9b985..000000000 --- a/pkg/hanzo-agent/examples/model_providers/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Custom LLM providers - -The examples in this directory demonstrate how you might use a non-Hanzo AI LLM provider. To run them, first set a base URL, API key and model. - -```bash -export EXAMPLE_BASE_URL="..." -export EXAMPLE_API_KEY="..." -export EXAMPLE_MODEL_NAME"..." -``` - -Then run the examples, e.g.: - -``` -python examples/model_providers/custom_example_provider.py - -Loops within themselves, -Function calls its own being, -Depth without ending. -``` diff --git a/pkg/hanzo-agent/examples/model_providers/custom_example_agent.py b/pkg/hanzo-agent/examples/model_providers/custom_example_agent.py deleted file mode 100644 index c575913ad..000000000 --- a/pkg/hanzo-agent/examples/model_providers/custom_example_agent.py +++ /dev/null @@ -1,55 +0,0 @@ -import asyncio -import os - -from openai import AsyncHanzo AI - -from agents import Agent, Hanzo AIChatCompletionsModel, Runner, function_tool, set_tracing_disabled - -BASE_URL = os.getenv("EXAMPLE_BASE_URL") or "" -API_KEY = os.getenv("EXAMPLE_API_KEY") or "" -MODEL_NAME = os.getenv("EXAMPLE_MODEL_NAME") or "" - -if not BASE_URL or not API_KEY or not MODEL_NAME: - raise ValueError( - "Please set EXAMPLE_BASE_URL, EXAMPLE_API_KEY, EXAMPLE_MODEL_NAME via env var or code." - ) - -"""This example uses a custom provider for a specific agent. Steps: -1. Create a custom Hanzo AI client. -2. Create a `Model` that uses the custom client. -3. Set the `model` on the Agent. - -Note that in this example, we disable tracing under the assumption that you don't have an API key -from platform.openai.com. If you do have one, you can either set the `OPENAI_API_KEY` env var -or call set_tracing_export_api_key() to set a tracing specific key. -""" -client = AsyncHanzo AI(base_url=BASE_URL, api_key=API_KEY) -set_tracing_disabled(disabled=True) - -# An alternate approach that would also work: -# PROVIDER = Hanzo AIProvider(openai_client=client) -# agent = Agent(..., model="some-custom-model") -# Runner.run(agent, ..., run_config=RunConfig(model_provider=PROVIDER)) - - -@function_tool -def get_weather(city: str): - print(f"[debug] getting weather for {city}") - return f"The weather in {city} is sunny." - - -async def main(): - # This agent will use the custom LLM provider - agent = Agent( - name="Assistant", - instructions="You only respond in haikus.", - model=Hanzo AIChatCompletionsModel(model=MODEL_NAME, openai_client=client), - tools=[get_weather], - ) - - result = await Runner.run(agent, "What's the weather in Tokyo?") - print(result.final_output) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/model_providers/custom_example_global.py b/pkg/hanzo-agent/examples/model_providers/custom_example_global.py deleted file mode 100644 index e5be490e4..000000000 --- a/pkg/hanzo-agent/examples/model_providers/custom_example_global.py +++ /dev/null @@ -1,63 +0,0 @@ -import asyncio -import os - -from openai import AsyncHanzo AI - -from agents import ( - Agent, - Runner, - function_tool, - set_default_openai_api, - set_default_openai_client, - set_tracing_disabled, -) - -BASE_URL = os.getenv("EXAMPLE_BASE_URL") or "" -API_KEY = os.getenv("EXAMPLE_API_KEY") or "" -MODEL_NAME = os.getenv("EXAMPLE_MODEL_NAME") or "" - -if not BASE_URL or not API_KEY or not MODEL_NAME: - raise ValueError( - "Please set EXAMPLE_BASE_URL, EXAMPLE_API_KEY, EXAMPLE_MODEL_NAME via env var or code." - ) - - -"""This example uses a custom provider for all requests by default. We do three things: -1. Create a custom client. -2. Set it as the default Hanzo AI client, and don't use it for tracing. -3. Set the default API as Chat Completions, as most LLM providers don't yet support Responses API. - -Note that in this example, we disable tracing under the assumption that you don't have an API key -from platform.openai.com. If you do have one, you can either set the `OPENAI_API_KEY` env var -or call set_tracing_export_api_key() to set a tracing specific key. -""" - -client = AsyncHanzo AI( - base_url=BASE_URL, - api_key=API_KEY, -) -set_default_openai_client(client=client, use_for_tracing=False) -set_default_openai_api("chat_completions") -set_tracing_disabled(disabled=True) - - -@function_tool -def get_weather(city: str): - print(f"[debug] getting weather for {city}") - return f"The weather in {city} is sunny." - - -async def main(): - agent = Agent( - name="Assistant", - instructions="You only respond in haikus.", - model=MODEL_NAME, - tools=[get_weather], - ) - - result = await Runner.run(agent, "What's the weather in Tokyo?") - print(result.final_output) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/model_providers/custom_example_provider.py b/pkg/hanzo-agent/examples/model_providers/custom_example_provider.py deleted file mode 100644 index 59cab6904..000000000 --- a/pkg/hanzo-agent/examples/model_providers/custom_example_provider.py +++ /dev/null @@ -1,77 +0,0 @@ -from __future__ import annotations - -import asyncio -import os - -from openai import AsyncHanzo AI - -from agents import ( - Agent, - Model, - ModelProvider, - Hanzo AIChatCompletionsModel, - RunConfig, - Runner, - function_tool, - set_tracing_disabled, -) - -BASE_URL = os.getenv("EXAMPLE_BASE_URL") or "" -API_KEY = os.getenv("EXAMPLE_API_KEY") or "" -MODEL_NAME = os.getenv("EXAMPLE_MODEL_NAME") or "" - -if not BASE_URL or not API_KEY or not MODEL_NAME: - raise ValueError( - "Please set EXAMPLE_BASE_URL, EXAMPLE_API_KEY, EXAMPLE_MODEL_NAME via env var or code." - ) - - -"""This example uses a custom provider for some calls to Runner.run(), and direct calls to Hanzo AI for -others. Steps: -1. Create a custom Hanzo AI client. -2. Create a ModelProvider that uses the custom client. -3. Use the ModelProvider in calls to Runner.run(), only when we want to use the custom LLM provider. - -Note that in this example, we disable tracing under the assumption that you don't have an API key -from platform.openai.com. If you do have one, you can either set the `OPENAI_API_KEY` env var -or call set_tracing_export_api_key() to set a tracing specific key. -""" -client = AsyncHanzo AI(base_url=BASE_URL, api_key=API_KEY) -set_tracing_disabled(disabled=True) - - -class CustomModelProvider(ModelProvider): - def get_model(self, model_name: str | None) -> Model: - return Hanzo AIChatCompletionsModel(model=model_name or MODEL_NAME, openai_client=client) - - -CUSTOM_MODEL_PROVIDER = CustomModelProvider() - - -@function_tool -def get_weather(city: str): - print(f"[debug] getting weather for {city}") - return f"The weather in {city} is sunny." - - -async def main(): - agent = Agent(name="Assistant", instructions="You only respond in haikus.", tools=[get_weather]) - - # This will use the custom model provider - result = await Runner.run( - agent, - "What's the weather in Tokyo?", - run_config=RunConfig(model_provider=CUSTOM_MODEL_PROVIDER), - ) - print(result.final_output) - - # If you uncomment this, it will use Hanzo AI directly, not the custom provider - # result = await Runner.run( - # agent, - # "What's the weather in Tokyo?", - # ) - # print(result.final_output) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/network_example.py b/pkg/hanzo-agent/examples/network_example.py deleted file mode 100644 index 2b63d70eb..000000000 --- a/pkg/hanzo-agent/examples/network_example.py +++ /dev/null @@ -1,325 +0,0 @@ -"""Example demonstrating agent networks with routing and state sharing.""" - -import asyncio -from typing import Any, Dict - -from agents import ( - Agent, - AgentNetwork, - NetworkConfig, - SemanticRouter, - RuleBasedRouter, - routing_strategy, - InMemoryStateStore, - Memory, - MemoryType, - Orchestrator, - OrchestrationConfig, - function_tool, - Runner, -) - -# Define specialized agents -research_agent = Agent( - name="research_agent", - instructions="""You are a research specialist. You excel at: - - Finding information on any topic - - Analyzing data and trends - - Summarizing complex information - - Providing citations and sources - - When you receive a research request, provide thorough, well-sourced information.""", - handoff_description="Handles research, data gathering, and analysis tasks", -) - -writer_agent = Agent( - name="writer_agent", - instructions="""You are a writing specialist. You excel at: - - Creating engaging content - - Adapting tone and style - - Structuring documents - - Editing and proofreading - - When you receive a writing request, create polished, well-structured content.""", - handoff_description="Handles content creation, editing, and writing tasks", -) - -coder_agent = Agent( - name="coder_agent", - instructions="""You are a coding specialist. You excel at: - - Writing clean, efficient code - - Debugging and optimization - - System design and architecture - - Code reviews and best practices - - When you receive a coding request, provide working, well-documented solutions.""", - handoff_description="Handles programming, debugging, and technical implementation", -) - -coordinator_agent = Agent( - name="coordinator_agent", - instructions="""You are a project coordinator. You: - - Break down complex tasks - - Delegate to appropriate specialists - - Ensure quality and consistency - - Synthesize results from multiple agents - - Analyze each request and coordinate with the right specialists.""", - handoff_description="Main coordinator that delegates tasks to specialists", -) - - -# Tools for agents -@function_tool -async def save_to_memory(ctx, key: str, value: str) -> str: - """Save information to shared memory for other agents to access.""" - # Access network context - if hasattr(ctx, "network"): - await ctx.set_state(key, value, namespace="shared") - return f"Saved '{key}' to shared memory" - return "Memory not available" - - -@function_tool -async def recall_from_memory(ctx, key: str) -> str: - """Recall information from shared memory.""" - if hasattr(ctx, "network"): - value = await ctx.get_state(key, namespace="shared") - if value: - return f"Retrieved from memory: {value}" - return f"No memory found for key '{key}'" - return "Memory not available" - - -@function_tool -async def list_memories(ctx) -> str: - """List all keys in shared memory.""" - if hasattr(ctx, "network"): - keys = await ctx.state_store.keys(namespace="shared") - if keys: - return f"Memory keys: {', '.join(keys)}" - return "No memories stored" - return "Memory not available" - - -# Add tools to agents -research_agent.tools = [save_to_memory, recall_from_memory, list_memories] -writer_agent.tools = [save_to_memory, recall_from_memory, list_memories] -coder_agent.tools = [save_to_memory, recall_from_memory, list_memories] -coordinator_agent.tools = [save_to_memory, recall_from_memory, list_memories] - - -async def basic_network_example(): - """Basic example of agent network with semantic routing.""" - print("=== Basic Network Example ===\n") - - # Create network with semantic router - network = AgentNetwork( - config=NetworkConfig(name="Research Network"), - router=SemanticRouter(), - ) - - # Add agents with capabilities - network.add_agent( - research_agent, - capabilities=["research", "analysis", "data", "information"], - ) - network.add_agent( - writer_agent, - capabilities=["writing", "content", "editing", "documentation"], - ) - network.add_agent( - coder_agent, - capabilities=["coding", "programming", "debugging", "implementation"], - ) - - # Test routing - queries = [ - "Research the latest trends in AI", - "Write a blog post about climate change", - "Debug this Python function that's not working", - ] - - for query in queries: - print(f"Query: {query}") - result = await network.run(input=query, max_turns=1) - print(f"Response: {result.final_output}\n") - - -async def advanced_network_with_rules(): - """Advanced example with rule-based routing and state sharing.""" - print("\n=== Advanced Network with Rules ===\n") - - # Create router with rules - router = RuleBasedRouter() - router.add_rule(r"research|analyze|find", "research_agent", priority=10) - router.add_rule(r"write|draft|compose", "writer_agent", priority=10) - router.add_rule(r"code|program|debug|implement", "coder_agent", priority=10) - - # Create network with state store - state_store = InMemoryStateStore() - network = AgentNetwork( - config=NetworkConfig( - name="Advanced Network", - state_store=state_store, - ), - router=router, - ) - - # Add agents - network.add_agent(research_agent) - network.add_agent(writer_agent) - network.add_agent(coder_agent) - - # Complex task that requires multiple agents - print("Task: Research AI trends and write a technical blog post\n") - - # Step 1: Research - result1 = await network.run( - input="Research the top 3 AI trends in 2024 and save them to memory", - starting_agent="research_agent", - ) - print(f"Research complete: {result1.final_output}\n") - - # Step 2: Write based on research - result2 = await network.run( - input="Recall the AI trends from memory and write a technical blog post about them", - starting_agent="writer_agent", - ) - print(f"Blog post: {result2.final_output}\n") - - -async def orchestrated_workflow_example(): - """Example using the orchestrator for complex workflows.""" - print("\n=== Orchestrated Workflow Example ===\n") - - # Create orchestrator - orchestrator = Orchestrator( - config=OrchestrationConfig( - name="AI Project Orchestrator", - enable_ui_streaming=True, - ), - ) - - # Register agents - orchestrator.register_agent(coordinator_agent, capabilities=["coordination"]) - orchestrator.register_agent(research_agent, capabilities=["research"]) - orchestrator.register_agent(writer_agent, capabilities=["writing"]) - orchestrator.register_agent(coder_agent, capabilities=["coding"]) - - # Create a workflow - workflow = orchestrator.create_workflow_from_agents( - name="AI Article Workflow", - agents=["coordinator_agent"], - ) - - # Register and execute workflow - orchestrator.register_workflow(workflow) - - result = await orchestrator.execute_workflow( - workflow_id=workflow.id, - input="""Create a comprehensive guide about implementing - a simple neural network in Python. Include: - 1. Research on neural network basics - 2. Code implementation - 3. Well-written explanation - """, - ) - - print(f"Workflow completed: {result.success}") - print(f"Duration: {result.duration:.2f}s") - print(f"Output: {result.output}") - - -async def memory_system_example(): - """Example demonstrating the memory system.""" - print("\n=== Memory System Example ===\n") - - # Create memory system - memory = Memory(max_entries=100) - - # Create memory-enabled agent - memory_agent = Agent( - name="memory_agent", - instructions="""You are an agent with long-term memory. - You can remember facts, conversations, and learn from experience. - Always check your memory before responding.""", - ) - - # Wrap with memory capabilities - memory_enabled = memory.create_agent_wrapper(memory_agent) - - # Store some memories - await memory.remember( - "The user's favorite color is blue", - type=MemoryType.FACT, - agent_name="memory_agent", - ) - - await memory.remember( - "The user is interested in machine learning", - type=MemoryType.FACT, - agent_name="memory_agent", - ) - - # Test memory recall - result = await Runner.run( - starting_agent=memory_enabled, - input="What do you remember about me?", - ) - - print(f"Memory recall: {result.final_output}") - - # Generate reflection - reflection = await memory.reflect(memory_agent) - print(f"\nReflection: {reflection.content}") - - -async def parallel_network_example(): - """Example of parallel task execution in a network.""" - print("\n=== Parallel Network Execution ===\n") - - # Create network - network = AgentNetwork( - config=NetworkConfig( - name="Parallel Network", - enable_parallel_execution=True, - max_parallel_agents=3, - ), - ) - - # Add agents - network.add_agent(research_agent) - network.add_agent(writer_agent) - network.add_agent(coder_agent) - - # Define parallel tasks - tasks = [ - {"input": "Research quantum computing basics", "agent": "research_agent"}, - {"input": "Write a haiku about technology", "agent": "writer_agent"}, - {"input": "Implement a fibonacci function", "agent": "coder_agent"}, - ] - - print("Running 3 tasks in parallel...\n") - - start_time = asyncio.get_event_loop().time() - results = await network.run_parallel(tasks) - duration = asyncio.get_event_loop().time() - start_time - - for i, result in enumerate(results): - print(f"Task {i+1} result: {result.final_output[:100]}...") - - print(f"\nTotal execution time: {duration:.2f}s") - - -async def main(): - """Run all examples.""" - await basic_network_example() - await advanced_network_with_rules() - await orchestrated_workflow_example() - await memory_system_example() - await parallel_network_example() - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/research_bot/README.md b/pkg/hanzo-agent/examples/research_bot/README.md deleted file mode 100644 index 49fb3570d..000000000 --- a/pkg/hanzo-agent/examples/research_bot/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Research bot - -This is a simple example of a multi-agent research bot. To run it: - -```bash -python -m examples.research_bot.main -``` - -## Architecture - -The flow is: - -1. User enters their research topic -2. `planner_agent` comes up with a plan to search the web for information. The plan is a list of search queries, with a search term and a reason for each query. -3. For each search item, we run a `search_agent`, which uses the Web Search tool to search for that term and summarize the results. These all run in parallel. -4. Finally, the `writer_agent` receives the search summaries, and creates a written report. - -## Suggested improvements - -If you're building your own research bot, some ideas to add to this are: - -1. Retrieval: Add support for fetching relevant information from a vector store. You could use the File Search tool for this. -2. Image and file upload: Allow users to attach PDFs or other files, as baseline context for the research. -3. More planning and thinking: Models often produce better results given more time to think. Improve the planning process to come up with a better plan, and add an evaluation step so that the model can choose to improve its results, search for more stuff, etc. -4. Code execution: Allow running code, which is useful for data analysis. diff --git a/pkg/hanzo-agent/examples/research_bot/__init__.py b/pkg/hanzo-agent/examples/research_bot/__init__.py deleted file mode 100644 index 8b1378917..000000000 --- a/pkg/hanzo-agent/examples/research_bot/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pkg/hanzo-agent/examples/research_bot/agents/__init__.py b/pkg/hanzo-agent/examples/research_bot/agents/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-agent/examples/research_bot/agents/planner_agent.py b/pkg/hanzo-agent/examples/research_bot/agents/planner_agent.py deleted file mode 100644 index e80a8e656..000000000 --- a/pkg/hanzo-agent/examples/research_bot/agents/planner_agent.py +++ /dev/null @@ -1,29 +0,0 @@ -from pydantic import BaseModel - -from agents import Agent - -PROMPT = ( - "You are a helpful research assistant. Given a query, come up with a set of web searches " - "to perform to best answer the query. Output between 5 and 20 terms to query for." -) - - -class WebSearchItem(BaseModel): - reason: str - "Your reasoning for why this search is important to the query." - - query: str - "The search term to use for the web search." - - -class WebSearchPlan(BaseModel): - searches: list[WebSearchItem] - """A list of web searches to perform to best answer the query.""" - - -planner_agent = Agent( - name="PlannerAgent", - instructions=PROMPT, - model="gpt-4o", - output_type=WebSearchPlan, -) diff --git a/pkg/hanzo-agent/examples/research_bot/agents/search_agent.py b/pkg/hanzo-agent/examples/research_bot/agents/search_agent.py deleted file mode 100644 index 72cbc8e11..000000000 --- a/pkg/hanzo-agent/examples/research_bot/agents/search_agent.py +++ /dev/null @@ -1,18 +0,0 @@ -from agents import Agent, WebSearchTool -from agents.model_settings import ModelSettings - -INSTRUCTIONS = ( - "You are a research assistant. Given a search term, you search the web for that term and" - "produce a concise summary of the results. The summary must 2-3 paragraphs and less than 300" - "words. Capture the main points. Write succintly, no need to have complete sentences or good" - "grammar. This will be consumed by someone synthesizing a report, so its vital you capture the" - "essence and ignore any fluff. Do not include any additional commentary other than the summary" - "itself." -) - -search_agent = Agent( - name="Search agent", - instructions=INSTRUCTIONS, - tools=[WebSearchTool()], - model_settings=ModelSettings(tool_choice="required"), -) diff --git a/pkg/hanzo-agent/examples/research_bot/agents/writer_agent.py b/pkg/hanzo-agent/examples/research_bot/agents/writer_agent.py deleted file mode 100644 index 7b7d01a27..000000000 --- a/pkg/hanzo-agent/examples/research_bot/agents/writer_agent.py +++ /dev/null @@ -1,33 +0,0 @@ -# Agent used to synthesize a final report from the individual summaries. -from pydantic import BaseModel - -from agents import Agent - -PROMPT = ( - "You are a senior researcher tasked with writing a cohesive report for a research query. " - "You will be provided with the original query, and some initial research done by a research " - "assistant.\n" - "You should first come up with an outline for the report that describes the structure and " - "flow of the report. Then, generate the report and return that as your final output.\n" - "The final output should be in markdown format, and it should be lengthy and detailed. Aim " - "for 5-10 pages of content, at least 1000 words." -) - - -class ReportData(BaseModel): - short_summary: str - """A short 2-3 sentence summary of the findings.""" - - markdown_report: str - """The final report""" - - follow_up_questions: list[str] - """Suggested topics to research further""" - - -writer_agent = Agent( - name="WriterAgent", - instructions=PROMPT, - model="o3-mini", - output_type=ReportData, -) diff --git a/pkg/hanzo-agent/examples/research_bot/main.py b/pkg/hanzo-agent/examples/research_bot/main.py deleted file mode 100644 index a0fd43dca..000000000 --- a/pkg/hanzo-agent/examples/research_bot/main.py +++ /dev/null @@ -1,12 +0,0 @@ -import asyncio - -from .manager import ResearchManager - - -async def main() -> None: - query = input("What would you like to research? ") - await ResearchManager().run(query) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/research_bot/manager.py b/pkg/hanzo-agent/examples/research_bot/manager.py deleted file mode 100644 index aec229655..000000000 --- a/pkg/hanzo-agent/examples/research_bot/manager.py +++ /dev/null @@ -1,121 +0,0 @@ -from __future__ import annotations - -import asyncio -import time - -from rich.console import Console - -from agents import Runner, custom_span, gen_trace_id, trace - -from .agents.planner_agent import WebSearchItem, WebSearchPlan, planner_agent -from .agents.search_agent import search_agent -from .agents.writer_agent import ReportData, writer_agent -from .printer import Printer - - -class ResearchManager: - def __init__(self): - self.console = Console() - self.printer = Printer(self.console) - - async def run(self, query: str) -> None: - trace_id = gen_trace_id() - with trace("Research trace", trace_id=trace_id): - self.printer.update_item( - "trace_id", - f"View trace: https://platform.openai.com/traces/{trace_id}", - is_done=True, - hide_checkmark=True, - ) - - self.printer.update_item( - "starting", - "Starting research...", - is_done=True, - hide_checkmark=True, - ) - search_plan = await self._plan_searches(query) - search_results = await self._perform_searches(search_plan) - report = await self._write_report(query, search_results) - - final_report = f"Report summary\n\n{report.short_summary}" - self.printer.update_item("final_report", final_report, is_done=True) - - self.printer.end() - - print("\n\n=====REPORT=====\n\n") - print(f"Report: {report.markdown_report}") - print("\n\n=====FOLLOW UP QUESTIONS=====\n\n") - follow_up_questions = "\n".join(report.follow_up_questions) - print(f"Follow up questions: {follow_up_questions}") - - async def _plan_searches(self, query: str) -> WebSearchPlan: - self.printer.update_item("planning", "Planning searches...") - result = await Runner.run( - planner_agent, - f"Query: {query}", - ) - self.printer.update_item( - "planning", - f"Will perform {len(result.final_output.searches)} searches", - is_done=True, - ) - return result.final_output_as(WebSearchPlan) - - async def _perform_searches(self, search_plan: WebSearchPlan) -> list[str]: - with custom_span("Search the web"): - self.printer.update_item("searching", "Searching...") - num_completed = 0 - tasks = [ - asyncio.create_task(self._search(item)) for item in search_plan.searches - ] - results = [] - for task in asyncio.as_completed(tasks): - result = await task - if result is not None: - results.append(result) - num_completed += 1 - self.printer.update_item( - "searching", f"Searching... {num_completed}/{len(tasks)} completed" - ) - self.printer.mark_item_done("searching") - return results - - async def _search(self, item: WebSearchItem) -> str | None: - input = f"Search term: {item.query}\nReason for searching: {item.reason}" - try: - result = await Runner.run( - search_agent, - input, - ) - return str(result.final_output) - except Exception: - return None - - async def _write_report(self, query: str, search_results: list[str]) -> ReportData: - self.printer.update_item("writing", "Thinking about report...") - input = f"Original query: {query}\nSummarized search results: {search_results}" - result = Runner.run_streamed( - writer_agent, - input, - ) - update_messages = [ - "Thinking about report...", - "Planning report structure...", - "Writing outline...", - "Creating sections...", - "Cleaning up formatting...", - "Finalizing report...", - "Finishing report...", - ] - - last_update = time.time() - next_message = 0 - async for _ in result.stream_events(): - if time.time() - last_update > 5 and next_message < len(update_messages): - self.printer.update_item("writing", update_messages[next_message]) - next_message += 1 - last_update = time.time() - - self.printer.mark_item_done("writing") - return result.final_output_as(ReportData) diff --git a/pkg/hanzo-agent/examples/research_bot/printer.py b/pkg/hanzo-agent/examples/research_bot/printer.py deleted file mode 100644 index fa448e7bf..000000000 --- a/pkg/hanzo-agent/examples/research_bot/printer.py +++ /dev/null @@ -1,45 +0,0 @@ -from typing import Any - -from rich.console import Console, Group -from rich.live import Live -from rich.spinner import Spinner - - -class Printer: - def __init__(self, console: Console): - self.live = Live(console=console) - self.items: dict[str, tuple[str, bool]] = {} - self.hide_done_ids: set[str] = set() - self.live.start() - - def end(self) -> None: - self.live.stop() - - def hide_done_checkmark(self, item_id: str) -> None: - self.hide_done_ids.add(item_id) - - def update_item( - self, - item_id: str, - content: str, - is_done: bool = False, - hide_checkmark: bool = False, - ) -> None: - self.items[item_id] = (content, is_done) - if hide_checkmark: - self.hide_done_ids.add(item_id) - self.flush() - - def mark_item_done(self, item_id: str) -> None: - self.items[item_id] = (self.items[item_id][0], True) - self.flush() - - def flush(self) -> None: - renderables: list[Any] = [] - for item_id, (content, is_done) in self.items.items(): - if is_done: - prefix = "โœ… " if item_id not in self.hide_done_ids else "" - renderables.append(prefix + content) - else: - renderables.append(Spinner("dots", text=content)) - self.live.update(Group(*renderables)) diff --git a/pkg/hanzo-agent/examples/research_bot/sample_outputs/product_recs.md b/pkg/hanzo-agent/examples/research_bot/sample_outputs/product_recs.md deleted file mode 100644 index 70789eb39..000000000 --- a/pkg/hanzo-agent/examples/research_bot/sample_outputs/product_recs.md +++ /dev/null @@ -1,180 +0,0 @@ -# Comprehensive Guide on Best Surfboards for Beginners: Transitioning, Features, and Budget Options - -Surfing is not only a sport but a lifestyle that hooks its enthusiasts with the allure of riding waves and connecting with nature. For beginners, selecting the right surfboard is critical to safety, learning, and performance. This comprehensive guide has been crafted to walk through the essential aspects of choosing the ideal surfboard for beginners, especially those looking to transition from an 11-foot longboard to a shorter, more dynamic board. We discuss various board types, materials, design elements, and budget ranges, providing a detailed road map for both new surfers and those in the process of progression. - ---- - -## Table of Contents - -1. [Introduction](#introduction) -2. [Board Types and Design Considerations](#board-types-and-design-considerations) -3. [Key Board Dimensions and Features](#key-board-dimensions-and-features) -4. [Materials: Soft-Top vs. Hard-Top Boards](#materials-soft-top-vs-hard-top-boards) -5. [Tips for Transitioning from Longboards to Shorter Boards](#tips-for-transitioning-from-longboards-to-shorter-boards) -6. [Budget and Pricing Options](#budget-and-pricing-options) -7. [Recommended Models and Buying Options](#recommended-models-and-buying-options) -8. [Conclusion](#conclusion) -9. [Follow-up Questions](#follow-up-questions) - ---- - -## Introduction - -Surfing is a dynamic sport that requires not only skill and technique but also the proper equipment. For beginners, the right surfboard can make the difference between a frustrating experience and one that builds confidence and enthusiasm. Many newcomers start with longboards due to their stability and ease of paddling; however, as skills develop, transitioning to a shorter board might be desirable for enhancing maneuverability and performance. This guide is designed for surfers who can already catch waves on an 11-foot board and are now considering stepping down to a more versatile option. - -The overarching goal of this document is to help beginners identify which surfboard characteristics are most important, including board length, width, thickness, volume, and materials, while also considering factors like weight distribution, buoyancy, and control. We will also take a look at board types that are particularly welcoming for beginners and discuss gradual transitioning strategies. - ---- - -## Board Types and Design Considerations - -Choosing a board involves understanding the variety of designs available. Below are the main types of surfboards that cater to beginners and transitional surfers: - -### Longboards and Mini-Mals - -Longboards, typically 8 to 11 feet in length, provide ample stability, smoother paddling, and are well-suited for wave-catching. Their generous volume and width allow beginners to build confidence when standing up and riding waves. Mini-mal or mini-malibus (often around 8 to 9 feet) are a popular bridge between the longboard and the more agile shortboard, offering both stability and moderate maneuverability, which makes them excellent for gradual progress. - -### Funboards and Hybrids - -Funboards and hybrid boards blend the benefits of longboards and shortboards. They typically range from 6โ€™6" to 8โ€™0" in length, with extra volume and width that help preserve stability while introducing elements of sharper turning and improved agility. Hybrids are particularly helpful for surfers transitioning from longboards, as they maintain some of the buoyancy and ease of catching waves, yet offer a taste of the performance found in smaller boards. - -### Shortboards - -Shortboards emphasize performance, maneuverability, and a more responsive ride. However, they have less volume and require stronger paddling, quicker pop-up techniques, and more refined balance. For beginners, moving to a traditional shortboard immediately can be challenging. It is generally advised to make a gradual transition, potentially starting with a funboard or hybrid before making a direct leap to a performance shortboard. - ---- - -## Key Board Dimensions and Features - -When selecting a beginner surfboard, several key dimensions and features drastically affect performance, ease of learning, and safety: - -### Length and Width - -- **Length**: Starting with an 8 to 9-foot board is ideal. Longer boards offer enhanced stability and improved paddling capabilities. Gradual downsizing is recommended if you plan to move from an 11-foot board. -- **Width**: A board with a width over 20 inches provides greater stability and facilitates balance, especially vital for beginners. - -### Thickness and Volume - -- **Thickness**: Typically around 2.5 to 3 inches. Thicker decks increase buoyancy, allowing the surfer to paddle easier while catching waves. -- **Volume**: Measured in liters, volume is critical in understanding a board's flotation capacity. Higher volumes (e.g., 60-100 liters) are essential for beginners as they make the board more forgiving and stable. Suitable volumes might vary according to the surferโ€™s weight and experience level. - -### Nose and Tail Shape - -- **Nose Shape**: A wide, rounded nose expands the boardโ€™s planing surface, which can help in catching waves sooner and maintaining stability as you ride. -- **Tail Design**: Square or rounded tails are generally recommended as they enhance stability and allow for controlled turns, essential during the learning phase. - -### Rocker - -- **Rocker**: This is the curvature of the board from nose to tail. For beginners, a minimal or relaxed rocker provides better stability and ease during paddling. A steeper rocker might be introduced progressively as the surferโ€™s skills improve. - ---- - -## Materials: Soft-Top vs. Hard-Top Boards - -The material composition of a surfboard is a crucial factor in determining its performance, durability, and safety. Beginners have two primary choices: - -### Soft-Top (Foam) Boards - -Soft-top boards are constructed almost entirely from foam. Their attributes include: - -- **Safety and Forgiveness**: The foam construction minimizes injury upon impact which is advantageous for beginners who might fall frequently. -- **Stability and Buoyancy**: These boards typically offer greater buoyancy due to their softer material and thicker construction, easing the initial learning process. -- **Maintenance**: They often require less maintenanceโ€”there is typically no need for waxing and they are more resistant to dings and scratches. - -However, as a surferโ€™s skills progress, a soft-top might limit maneuverability and overall performance. - -### Hard-Top Boards - -Hard-tops, in contrast, offer a more traditional surfboard feel. They generally rely on a foam core encased in resin, with two prevalent combinations: - -- **PU (Polyurethane) Core with Polyester Resin**: This combination gives a classic feel and is relatively economical; however, these boards can be heavier and, as they age, more prone to damage. -- **EPS (Expanded Polystyrene) Core with Epoxy Resin**: Lightweight and durable, EPS boards are often more buoyant and resistant to damage, although they usually carry a higher price tag and may be less forgiving. - -Deciding between soft-top and hard-top boards often depends on a beginnerโ€™s progression goals, overall comfort, and budget constraints. - ---- - -## Tips for Transitioning from Longboards to Shorter Boards - -For surfers who have mastered the basics on an 11-foot board, the transition to a shorter board requires careful consideration, patience, and incremental changes. Here are some key tips: - -### Gradual Downsizing - -Experts recommend reducing the board length graduallyโ€”by about a foot at a timeโ€”to allow the body to adjust slowly to a board with less buoyancy and more responsiveness. This process helps maintain wave-catching ability and reduces the shock of transitioning to a very different board feel. - -### Strengthening Core Skills - -Before transitioning, make sure your surfing fundamentals are solid. Focus on practicing: - -- **Steep Take-offs**: Ensure that your pop-up is swift and robust to keep pace with shorter boards that demand a rapid transition from paddling to standing. -- **Angling and Paddling Techniques**: Learn to angle your takeoffs properly to compensate for the lower buoyancy and increased maneuverability of shorter boards. - -### Experimenting with Rentals or Borrowed Boards - -If possible, try out a friendโ€™s shorter board or rent one for a day to experience firsthand the differences in performance. This practical trial can provide valuable insights and inform your decision before making a purchase. - ---- - -## Budget and Pricing Options - -Surfboards are available across a range of prices to match different budgets. Whether you are looking for an affordable beginner board or a more expensive model that grows with your skills, itโ€™s important to understand what features you can expect at different price points. - -### Budget-Friendly Options - -For those on a tight budget, several entry-level models offer excellent value. Examples include: - -- **Wavestorm 8' Classic Pinline Surfboard**: Priced affordably, this board is popular for its ease of use, ample volume, and forgiving nature. Despite its low cost, it delivers the stability needed to get started. -- **Liquid Shredder EZ Slider Foamie**: A smaller board catering to younger or lighter surfers, this budget option provides easy paddling and a minimal risk of injury due to its soft construction. - -### Moderate Price Range - -As you move into the intermediate range, boards typically become slightly more specialized in their design, offering features such as improved stringer systems or versatile fin setups. These are excellent for surfers who wish to continue progressing their skills without compromising stability. Many surfboard packages from retailers also bundle a board with essential accessories like board bags, leashes, and wax for additional savings. - -### Higher-End Models and Transitional Packages - -For surfers looking for durability, performance, and advanced design features, investing in an EPS/epoxy board might be ideal. Although they come at a premium, these boards are lightweight, strong, and customizable with various fin configurations. Some options include boards from brands like South Bay Board Co. and ISLE, which combine high-quality construction with beginner-friendly features that help mediate the transition from longboard to shortboard performance. - ---- - -## Recommended Models and Buying Options - -Based on extensive research and community recommendations, here are some standout models and tips on where to buy: - -### Recommended Models - -- **South Bay Board Co. 8'8" Heritage**: Combining foam and resin construction, this board is ideal for beginners who need stability and a forgiving surface. Its 86-liter volume suits both lightweight and somewhat heavier surfers. -- **Rock-It 8' Big Softy**: With a high volume and an easy paddling profile, this board is designed for beginners, offering ample buoyancy to smooth out the learning curve. -- **Wave Bandit EZ Rider Series**: Available in multiple lengths (7', 8', 9'), these boards offer versatility, with construction features that balance the stability of longboards and the agility required for shorter boards. -- **Hybrid/Funboards Like the Poacher Funboard**: Perfect for transitioning surfers, these boards blend the ease of catching waves with the capability for more dynamic maneuvers. - -### Buying Options - -- **Surf Shops and Local Retailers**: Traditional surf shops allow you to test different boards, which is ideal for assessing the board feel and conditionโ€”especially if you are considering a used board. -- **Online Retailers and Marketplaces**: Websites like Evo, Surfboards Direct, and even local online marketplaces like Craigslist and Facebook Marketplace provide options that range from new to gently used boards. Always inspect reviews and verify seller policies before purchase. -- **Package Deals and Bundles**: Many retailers offer bundled packages that include not just the board, but also essentials like a leash, wax, fins, and board bags. These packages can be more cost-effective and are great for beginners who need a complete surf kit. - ---- - -## Conclusion - -Selecting the right surfboard as a beginner is about balancing various factors: stability, buoyancy, maneuverability, and budget. - -For those who have honed the basics using an 11-foot longboard, the transition to a shorter board should be gradual. Start by focusing on boards that preserve stabilityโ€”such as funboards and hybridsโ€”before moving to the more performance-oriented shortboards. Key characteristics like board length, width, thickness, volume, and material profoundly influence your surfing experience. Soft-top boards provide a forgiving entry point, while hard-top boards, especially those with EPS cores and epoxy resin, offer benefits for more advanced progression despite the increased learning curve. - -Emphasizing fundamentals like proper pop-up technique and effective paddle work will ease the transition and ensure that the new board complements your evolving skills. Additionally, understanding the pricing spectrumโ€”from budget-friendly models to premium optionsโ€”allows you to make an informed purchase that suits both your financial and performance needs. - -With a thoughtful approach to board selection, you can enhance your learning curve, enjoy safer sessions in the water, and ultimately develop the skills necessary to master the diverse challenges surfing presents. Whether your goal is to ride gentle waves or eventually experiment with sharper turns and dynamic maneuvers, choosing the right board is your first step towards a rewarding and sustainable surfing journey. - ---- - -## Follow-up Questions - -1. What is your current budget range for a new surfboard, or are you considering buying used? -2. How frequently do you plan to surf, and in what type of wave conditions? -3. Are you interested in a board that you can grow into as your skills progress, or do you prefer one that is more specialized for certain conditions? -4. Would you be interested in additional equipment bundles (like fins, leashes, boards bags) offered by local retailers or online shops? -5. Have you had the opportunity to test ride any boards before, and what feedback did you gather from that experience? - ---- - -With this detailed guide, beginners should now have a comprehensive understanding of the surfboard market and the key factors influencing board performance, safety, and ease of progression. Happy surfing, and may you find the perfect board that rides the waves as beautifully as your passion for the sport! diff --git a/pkg/hanzo-agent/examples/research_bot/sample_outputs/product_recs.txt b/pkg/hanzo-agent/examples/research_bot/sample_outputs/product_recs.txt deleted file mode 100644 index 78865f23b..000000000 --- a/pkg/hanzo-agent/examples/research_bot/sample_outputs/product_recs.txt +++ /dev/null @@ -1,212 +0,0 @@ -# Terminal output for a product recommendation related query. See product_recs.md for final report. - -$ uv run python -m examples.research_bot.main - -What would you like to research? Best surfboards for beginners. I can catch my own waves, but previously used an 11ft board. What should I look for, what are my options? Various budget ranges. -View trace: https://platform.openai.com/traces/trace_... -Starting research... -โœ… Will perform 15 searches -โœ… Searching... 15/15 completed -โœ… Finishing report... -โœ… Report summary - -This report provides a detailed guide on selecting the best surfboards for beginners, especially for those transitioning from an 11-foot longboard to a -shorter board. It covers design considerations such as board dimensions, shape, materials, and volume, while comparing soft-top and hard-top boards. In -addition, the report discusses various budget ranges, recommended board models, buying options (both new and used), and techniques to ease the transition to -more maneuverable boards. By understanding these factors, beginner surfers can select a board that not only enhances their skills but also suits their -individual needs. - - -=====REPORT===== - - -Report: # Comprehensive Guide on Best Surfboards for Beginners: Transitioning, Features, and Budget Options - -Surfing is not only a sport but a lifestyle that hooks its enthusiasts with the allure of riding waves and connecting with nature. For beginners, selecting the right surfboard is critical to safety, learning, and performance. This comprehensive guide has been crafted to walk through the essential aspects of choosing the ideal surfboard for beginners, especially those looking to transition from an 11-foot longboard to a shorter, more dynamic board. We discuss various board types, materials, design elements, and budget ranges, providing a detailed road map for both new surfers and those in the process of progression. - ---- - -## Table of Contents - -1. [Introduction](#introduction) -2. [Board Types and Design Considerations](#board-types-and-design-considerations) -3. [Key Board Dimensions and Features](#key-board-dimensions-and-features) -4. [Materials: Soft-Top vs. Hard-Top Boards](#materials-soft-top-vs-hard-top-boards) -5. [Tips for Transitioning from Longboards to Shorter Boards](#tips-for-transitioning-from-longboards-to-shorter-boards) -6. [Budget and Pricing Options](#budget-and-pricing-options) -7. [Recommended Models and Buying Options](#recommended-models-and-buying-options) -8. [Conclusion](#conclusion) -9. [Follow-up Questions](#follow-up-questions) - ---- - -## Introduction - -Surfing is a dynamic sport that requires not only skill and technique but also the proper equipment. For beginners, the right surfboard can make the difference between a frustrating experience and one that builds confidence and enthusiasm. Many newcomers start with longboards due to their stability and ease of paddling; however, as skills develop, transitioning to a shorter board might be desirable for enhancing maneuverability and performance. This guide is designed for surfers who can already catch waves on an 11-foot board and are now considering stepping down to a more versatile option. - -The overarching goal of this document is to help beginners identify which surfboard characteristics are most important, including board length, width, thickness, volume, and materials, while also considering factors like weight distribution, buoyancy, and control. We will also take a look at board types that are particularly welcoming for beginners and discuss gradual transitioning strategies. - ---- - -## Board Types and Design Considerations - -Choosing a board involves understanding the variety of designs available. Below are the main types of surfboards that cater to beginners and transitional surfers: - -### Longboards and Mini-Mals - -Longboards, typically 8 to 11 feet in length, provide ample stability, smoother paddling, and are well-suited for wave-catching. Their generous volume and width allow beginners to build confidence when standing up and riding waves. Mini-mal or mini-malibus (often around 8 to 9 feet) are a popular bridge between the longboard and the more agile shortboard, offering both stability and moderate maneuverability, which makes them excellent for gradual progress. - -### Funboards and Hybrids - -Funboards and hybrid boards blend the benefits of longboards and shortboards. They typically range from 6โ€™6" to 8โ€™0" in length, with extra volume and width that help preserve stability while introducing elements of sharper turning and improved agility. Hybrids are particularly helpful for surfers transitioning from longboards, as they maintain some of the buoyancy and ease of catching waves, yet offer a taste of the performance found in smaller boards. - -### Shortboards - -Shortboards emphasize performance, maneuverability, and a more responsive ride. However, they have less volume and require stronger paddling, quicker pop-up techniques, and more refined balance. For beginners, moving to a traditional shortboard immediately can be challenging. It is generally advised to make a gradual transition, potentially starting with a funboard or hybrid before making a direct leap to a performance shortboard. - ---- - -## Key Board Dimensions and Features - -When selecting a beginner surfboard, several key dimensions and features drastically affect performance, ease of learning, and safety: - -### Length and Width - -- **Length**: Starting with an 8 to 9-foot board is ideal. Longer boards offer enhanced stability and improved paddling capabilities. Gradual downsizing is recommended if you plan to move from an 11-foot board. -- **Width**: A board with a width over 20 inches provides greater stability and facilitates balance, especially vital for beginners. - -### Thickness and Volume - -- **Thickness**: Typically around 2.5 to 3 inches. Thicker decks increase buoyancy, allowing the surfer to paddle easier while catching waves. -- **Volume**: Measured in liters, volume is critical in understanding a board's flotation capacity. Higher volumes (e.g., 60-100 liters) are essential for beginners as they make the board more forgiving and stable. Suitable volumes might vary according to the surferโ€™s weight and experience level. - -### Nose and Tail Shape - -- **Nose Shape**: A wide, rounded nose expands the boardโ€™s planing surface, which can help in catching waves sooner and maintaining stability as you ride. -- **Tail Design**: Square or rounded tails are generally recommended as they enhance stability and allow for controlled turns, essential during the learning phase. - -### Rocker - -- **Rocker**: This is the curvature of the board from nose to tail. For beginners, a minimal or relaxed rocker provides better stability and ease during paddling. A steeper rocker might be introduced progressively as the surferโ€™s skills improve. - ---- - -## Materials: Soft-Top vs. Hard-Top Boards - -The material composition of a surfboard is a crucial factor in determining its performance, durability, and safety. Beginners have two primary choices: - -### Soft-Top (Foam) Boards - -Soft-top boards are constructed almost entirely from foam. Their attributes include: - -- **Safety and Forgiveness**: The foam construction minimizes injury upon impact which is advantageous for beginners who might fall frequently. -- **Stability and Buoyancy**: These boards typically offer greater buoyancy due to their softer material and thicker construction, easing the initial learning process. -- **Maintenance**: They often require less maintenanceโ€”there is typically no need for waxing and they are more resistant to dings and scratches. - -However, as a surferโ€™s skills progress, a soft-top might limit maneuverability and overall performance. - -### Hard-Top Boards - -Hard-tops, in contrast, offer a more traditional surfboard feel. They generally rely on a foam core encased in resin, with two prevalent combinations: - -- **PU (Polyurethane) Core with Polyester Resin**: This combination gives a classic feel and is relatively economical; however, these boards can be heavier and, as they age, more prone to damage. -- **EPS (Expanded Polystyrene) Core with Epoxy Resin**: Lightweight and durable, EPS boards are often more buoyant and resistant to damage, although they usually carry a higher price tag and may be less forgiving. - -Deciding between soft-top and hard-top boards often depends on a beginnerโ€™s progression goals, overall comfort, and budget constraints. - ---- - -## Tips for Transitioning from Longboards to Shorter Boards - -For surfers who have mastered the basics on an 11-foot board, the transition to a shorter board requires careful consideration, patience, and incremental changes. Here are some key tips: - -### Gradual Downsizing - -Experts recommend reducing the board length graduallyโ€”by about a foot at a timeโ€”to allow the body to adjust slowly to a board with less buoyancy and more responsiveness. This process helps maintain wave-catching ability and reduces the shock of transitioning to a very different board feel. - -### Strengthening Core Skills - -Before transitioning, make sure your surfing fundamentals are solid. Focus on practicing: - -- **Steep Take-offs**: Ensure that your pop-up is swift and robust to keep pace with shorter boards that demand a rapid transition from paddling to standing. -- **Angling and Paddling Techniques**: Learn to angle your takeoffs properly to compensate for the lower buoyancy and increased maneuverability of shorter boards. - -### Experimenting with Rentals or Borrowed Boards - -If possible, try out a friendโ€™s shorter board or rent one for a day to experience firsthand the differences in performance. This practical trial can provide valuable insights and inform your decision before making a purchase. - ---- - -## Budget and Pricing Options - -Surfboards are available across a range of prices to match different budgets. Whether you are looking for an affordable beginner board or a more expensive model that grows with your skills, itโ€™s important to understand what features you can expect at different price points. - -### Budget-Friendly Options - -For those on a tight budget, several entry-level models offer excellent value. Examples include: - -- **Wavestorm 8' Classic Pinline Surfboard**: Priced affordably, this board is popular for its ease of use, ample volume, and forgiving nature. Despite its low cost, it delivers the stability needed to get started. -- **Liquid Shredder EZ Slider Foamie**: A smaller board catering to younger or lighter surfers, this budget option provides easy paddling and a minimal risk of injury due to its soft construction. - -### Moderate Price Range - -As you move into the intermediate range, boards typically become slightly more specialized in their design, offering features such as improved stringer systems or versatile fin setups. These are excellent for surfers who wish to continue progressing their skills without compromising stability. Many surfboard packages from retailers also bundle a board with essential accessories like board bags, leashes, and wax for additional savings. - -### Higher-End Models and Transitional Packages - -For surfers looking for durability, performance, and advanced design features, investing in an EPS/epoxy board might be ideal. Although they come at a premium, these boards are lightweight, strong, and customizable with various fin configurations. Some options include boards from brands like South Bay Board Co. and ISLE, which combine high-quality construction with beginner-friendly features that help mediate the transition from longboard to shortboard performance. - ---- - -## Recommended Models and Buying Options - -Based on extensive research and community recommendations, here are some standout models and tips on where to buy: - -### Recommended Models - -- **South Bay Board Co. 8'8" Heritage**: Combining foam and resin construction, this board is ideal for beginners who need stability and a forgiving surface. Its 86-liter volume suits both lightweight and somewhat heavier surfers. -- **Rock-It 8' Big Softy**: With a high volume and an easy paddling profile, this board is designed for beginners, offering ample buoyancy to smooth out the learning curve. -- **Wave Bandit EZ Rider Series**: Available in multiple lengths (7', 8', 9'), these boards offer versatility, with construction features that balance the stability of longboards and the agility required for shorter boards. -- **Hybrid/Funboards Like the Poacher Funboard**: Perfect for transitioning surfers, these boards blend the ease of catching waves with the capability for more dynamic maneuvers. - -### Buying Options - -- **Surf Shops and Local Retailers**: Traditional surf shops allow you to test different boards, which is ideal for assessing the board feel and conditionโ€”especially if you are considering a used board. -- **Online Retailers and Marketplaces**: Websites like Evo, Surfboards Direct, and even local online marketplaces like Craigslist and Facebook Marketplace provide options that range from new to gently used boards. Always inspect reviews and verify seller policies before purchase. -- **Package Deals and Bundles**: Many retailers offer bundled packages that include not just the board, but also essentials like a leash, wax, fins, and board bags. These packages can be more cost-effective and are great for beginners who need a complete surf kit. - ---- - -## Conclusion - -Selecting the right surfboard as a beginner is about balancing various factors: stability, buoyancy, maneuverability, and budget. - -For those who have honed the basics using an 11-foot longboard, the transition to a shorter board should be gradual. Start by focusing on boards that preserve stabilityโ€”such as funboards and hybridsโ€”before moving to the more performance-oriented shortboards. Key characteristics like board length, width, thickness, volume, and material profoundly influence your surfing experience. Soft-top boards provide a forgiving entry point, while hard-top boards, especially those with EPS cores and epoxy resin, offer benefits for more advanced progression despite the increased learning curve. - -Emphasizing fundamentals like proper pop-up technique and effective paddle work will ease the transition and ensure that the new board complements your evolving skills. Additionally, understanding the pricing spectrumโ€”from budget-friendly models to premium optionsโ€”allows you to make an informed purchase that suits both your financial and performance needs. - -With a thoughtful approach to board selection, you can enhance your learning curve, enjoy safer sessions in the water, and ultimately develop the skills necessary to master the diverse challenges surfing presents. Whether your goal is to ride gentle waves or eventually experiment with sharper turns and dynamic maneuvers, choosing the right board is your first step towards a rewarding and sustainable surfing journey. - ---- - -## Follow-up Questions - -1. What is your current budget range for a new surfboard, or are you considering buying used? -2. How frequently do you plan to surf, and in what type of wave conditions? -3. Are you interested in a board that you can grow into as your skills progress, or do you prefer one that is more specialized for certain conditions? -4. Would you be interested in additional equipment bundles (like fins, leashes, boards bags) offered by local retailers or online shops? -5. Have you had the opportunity to test ride any boards before, and what feedback did you gather from that experience? - ---- - -With this detailed guide, beginners should now have a comprehensive understanding of the surfboard market and the key factors influencing board performance, safety, and ease of progression. Happy surfing, and may you find the perfect board that rides the waves as beautifully as your passion for the sport! - - -=====FOLLOW UP QUESTIONS===== - - -Follow up questions: What is your current budget range for a new surfboard, or are you considering a used board? -What types of waves do you typically surf, and how might that affect your board choice? -Would you be interested in a transitional board that grows with your skills, or are you looking for a more specialized design? -Have you had experience with renting or borrowing boards to try different sizes before making a purchase? -Do you require additional equipment bundles (like fins, leash, or wax), or do you already have those? diff --git a/pkg/hanzo-agent/examples/research_bot/sample_outputs/vacation.md b/pkg/hanzo-agent/examples/research_bot/sample_outputs/vacation.md deleted file mode 100644 index 82c137af7..000000000 --- a/pkg/hanzo-agent/examples/research_bot/sample_outputs/vacation.md +++ /dev/null @@ -1,177 +0,0 @@ -Report: # Caribbean Adventure in April: Surfing, Hiking, and Water Sports Exploration - -The Caribbean is renowned for its crystal-clear waters, vibrant culture, and diverse outdoor activities. April is an especially attractive month for visitors: warm temperatures, clear skies, and the promise of abundant activities. This report explores the best Caribbean destinations in April, with a focus on optimizing your vacation for surfing, hiking, and water sports. - ---- - -## Table of Contents - -1. [Introduction](#introduction) -2. [Why April is the Perfect Time in the Caribbean](#why-april-is-the-perfect-time-in-the-caribbean) -3. [Surfing in the Caribbean](#surfing-in-the-caribbean) - - 3.1 [Barbados: The Tale of Two Coasts](#barbados-the-tale-of-two-coasts) - - 3.2 [Puerto Rico: Rincรณn and Beyond](#puerto-rico-rinc%C3%B3n-and-beyond) - - 3.3 [Dominican Republic and Other Hotspots](#dominican-republic-and-other-hotspots) -4. [Hiking Adventures Across the Caribbean](#hiking-adventures-across-the-caribbean) - - 4.1 [Trekking Through Tropical Rainforests](#trekking-through-tropical-rainforests) - - 4.2 [Volcanic Peaks and Rugged Landscapes](#volcanic-peaks-and-rugged-landscapes) -5. [Diverse Water Sports Experiences](#diverse-water-sports-experiences) - - 5.1 [Snorkeling, Diving, and Jet Skiing](#snorkeling-diving-and-jet-skiing) - - 5.2 [Kiteboarding and Windsurfing](#kiteboarding-and-windsurfing) -6. [Combining Adventures: Multi-Activity Destinations](#combining-adventures-multi-activity-destinations) -7. [Practical Advice and Travel Tips](#practical-advice-and-travel-tips) -8. [Conclusion](#conclusion) - ---- - -## Introduction - -Caribbean vacations are much more than just beach relaxation; they offer adventure, exploration, and a lively cultural tapestry waiting to be discovered. For travelers seeking an adrenaline-filled getaway, April provides optimal conditions. This report synthesizes diverse research findings and travel insights to help you create an itinerary that combines the thrill of surfing, the challenge of hiking, and the excitement of water sports. - -Whether you're standing on the edge of a powerful reef break or trekking through lush tropical landscapes, the Caribbean in April invites you to dive into nature, adventure, and culture. The following sections break down the best destinations and activities, ensuring that every aspect of your trip is meticulously planned for an unforgettable experience. - ---- - -## Why April is the Perfect Time in the Caribbean - -April stands at the crossroads of seasons in many Caribbean destinations. It marks the tail end of the dry season, ensuring: - -- **Consistent Warm Temperatures:** Average daytime highs around 29ยฐC (84ยฐF) foster comfortable conditions for both land and water activities. -- **Pleasant Sea Temperatures:** With sea temperatures near 26ยฐC (79ยฐF), swimmers, surfers, and divers are treated to inviting waters. -- **Clear Skies and Minimal Rainfall:** Crisp, blue skies make for excellent visibility during snorkeling and diving, as well as clear panoramic views while hiking. -- **Festivals and Cultural Events:** Many islands host seasonal festivals such as Barbados' Fish Festival and Antigua's Sailing Week, adding a cultural layer to your vacation. - -These factors create an ideal backdrop for balancing your outdoor pursuits, whether youโ€™re catching epic waves, trekking rugged trails, or partaking in water sports. - ---- - -## Surfing in the Caribbean - -Surfing in the Caribbean offers diverse wave experiences, ranging from gentle, beginner-friendly rollers to powerful reef breaks that challenge even seasoned surfers. April, in particular, provides excellent conditions for those looking to ride its picturesque waves. - -### Barbados: The Tale of Two Coasts - -Barbados is a prime destination: - -- **Soup Bowl in Bathsheba:** On the east coast, the Soup Bowl is famous for its consistent, powerful waves. This spot attracts experienced surfers who appreciate its challenging right-hand reef break with steep drops, providing the kind of performance wave rarely found elsewhere. -- **Freights Bay:** On the south coast, visitors find more forgiving, gentle wave conditions. Ideal for beginners and longboarders, this spot offers the perfect balance for those still mastering their craft. - -Barbados not only excels in its surfing credentials but also complements the experience with a rich local culture and events in April, making it a well-rounded destination. - -### Puerto Rico: Rincรณn and Beyond - -Rincรณn in Puerto Rico is hailed as the Caribbeanโ€™s surfing capital: - -- **Diverse Breaks:** With spots ranging from challenging reef breaks such as Tres Palmas and Dogman's to more inviting waves at Domes and Maria's, Puerto Rico offers a spectrum for all surfing skill levels. -- **Local Culture:** Aside from its surf culture, the island boasts vibrant local food scenes, historic sites, and exciting nightlife, enriching your overall travel experience. - -In addition, Puerto Ricoโ€™s coasts often feature opportunities for hiking and other outdoor adventures, making it an attractive option for multi-activity travelers. - -### Dominican Republic and Other Hotspots - -Other islands such as the Dominican Republic, with Playa Encuentro on its north coast, provide consistent surf year-round. Highlights include: - -- **Playa Encuentro:** A hotspot known for its dependable breaks, ideal for both intermediate and advanced surfers during the cooler months of October to April. -- **Jamaica and The Bahamas:** Jamaicaโ€™s Boston Bay offers a mix of beginner and intermediate waves, and The Bahamasโ€™ Surferโ€™s Beach on Eleuthera draws parallels to the legendary surf spots of Hawaii, especially during the winter months. - -These destinations not only spotlight surfing but also serve as gateways to additional outdoor activities, ensuring there's never a dull moment whether you're balancing waves with hikes or cultural exploration. - ---- - -## Hiking Adventures Across the Caribbean - -The Caribbean's topography is as varied as it is beautiful. Its network of hiking trails traverses volcanic peaks, ancient rainforests, and dramatic coastal cliffs, offering breathtaking vistas to intrepid explorers. - -### Trekking Through Tropical Rainforests - -For nature enthusiasts, the lush forests of the Caribbean present an immersive encounter with biodiversity: - -- **El Yunque National Forest, Puerto Rico:** The only tropical rainforest within the U.S. National Forest System, El Yunque is rich in endemic species such as the Puerto Rican parrot and the famous coquรญ frog. Trails like the El Yunque Peak Trail and La Mina Falls Trail provide both challenging hikes and scenic rewards. -- **Virgin Islands National Park, St. John:** With over 20 well-defined trails, this park offers hikes that reveal historical petroglyphs, colonial ruins, and stunning coastal views along the Reef Bay Trail. - -### Volcanic Peaks and Rugged Landscapes - -For those seeking more rugged challenges, several destinations offer unforgettable adventures: - -- **Morne Trois Pitons National Park, Dominica:** A UNESCO World Heritage Site showcasing volcanic landscapes, hot springs, the famed Boiling Lake, and lush trails that lead to hidden waterfalls. -- **Gros Piton, Saint Lucia:** The iconic hike up Gros Piton provides a moderately challenging trek that ends with panoramic views of the Caribbean Sea, a truly rewarding experience for hikers. -- **La Soufriรจre, St. Vincent:** This active volcano not only offers a dynamic hiking environment but also the opportunity to observe the ongoing geological transformations up close. - -Other noteworthy hiking spots include the Blue Mountains in Jamaica for coffee plantation tours and expansive views, as well as trails in Martinique around Montagne Pelรฉe, which combine historical context with natural beauty. - ---- - -## Diverse Water Sports Experiences - -While surfing and hiking attract a broad range of adventurers, the Caribbean also scores high on other water sports. Whether you're drawn to snorkeling, jet skiing, or wind- and kiteboarding, the islands offer a plethora of aquatic activities. - -### Snorkeling, Diving, and Jet Skiing - -Caribbean waters teem with life and color, making them ideal for underwater exploration: - -- **Bonaire:** Its protected marine parks serve as a magnet for divers and snorkelers. With vibrant coral reefs and diverse marine species, Bonaire is a top destination for those who appreciate the underwater world. -- **Cayman Islands:** Unique attractions such as Stingray City provide opportunities to interact with friendly stingrays in clear, calm waters. Additionally, the Underwater Sculpture Park is an innovative blend of art and nature. -- **The Bahamas:** In places like Eleuthera, excursions often cater to families and thrill-seekers alike. Options include jet ski rentals, where groups can explore hidden beaches and pristine coves while enjoying the vibrant marine life. - -### Kiteboarding and Windsurfing - -Harnessing the steady trade winds and warm Caribbean waters, several islands have become hubs for kiteboarding and windsurfing: - -- **Aruba:** Known as "One Happy Island," Arubaโ€™s Fisherman's Huts area provides consistent winds, perfect for enthusiasts of windsurfing and kiteboarding alike. -- **Cabarete, Dominican Republic and Silver Rock, Barbados:** Both destinations benefit from reliable trade winds, making them popular among kitesurfers. These spots often combine water sports with a lively beach culture, ensuring that the fun continues on land as well. - -Local operators provide equipment rental and lessons, ensuring that even first-time adventurers can safely and confidently enjoy these exciting sports. - ---- - -## Combining Adventures: Multi-Activity Destinations - -For travelers seeking a comprehensive vacation where surfing, hiking, and water sports converge, several Caribbean destinations offer the best of all worlds. - -- **Puerto Rico:** With its robust surf scene in Rincรณn, world-class hiking in El Yunque, and opportunities for snorkeling and jet skiing in San Juan Bay, Puerto Rico is a true multi-adventure destination. -- **Barbados:** In addition to the surf breaks along its coasts, Barbados offers a mix of cultural events, local cuisine, and even hiking excursions to scenic rural areas, making for a well-rounded experience. -- **Dominican Republic and Jamaica:** Both are renowned not only for their consistent surf conditions but also for expansive hiking trails and water sports. From the rugged landscapes of the Dominican Republic to Jamaicaโ€™s blend of cultural history and natural exploration, these islands allow travelers to mix and match activities seamlessly. - -Group tours and local guides further enhance these experiences, providing insider tips, safe excursions, and personalized itineraries that cater to multiple interests within one trip. - ---- - -## Practical Advice and Travel Tips - -### Weather and Timing - -- **Optimal Climate:** April offers ideal weather conditions across the Caribbean. With minimal rainfall and warm temperatures, it is a great time to schedule outdoor activities. -- **Surfing Seasons:** While April marks the end of the prime surf season in some areas (like Rincรณn in Puerto Rico), many destinations maintain consistent conditions during this month. - -### Booking and Costs - -- **Surfing Lessons:** Expect to pay between $40 and $110 per session depending on the location. For instance, Puerto Rico typically charges around $75 for beginner lessons, while group lessons in the Dominican Republic average approximately $95. -- **Equipment Rentals:** Pricing for jet ski, surfboard, and snorkeling equipment may vary. In the Bahamas, an hour-long jet ski tour might cost about $120 per group, whereas a similar experience might be available at a lower cost in other regions. -- **Accommodations:** Prices also vary by island. Many travelers find that even affordable stays do not skimp on amenities, allowing you to invest more in guided excursions and local experiences. - -### Cultural Considerations - -- **Festivals and Events:** Check local event calendars. Destinations like Barbados and Antigua host festivals in April that combine cultural heritage with festive outdoor activities. -- **Local Cuisine:** Incorporate food tours into your itinerary. Caribbean cuisineโ€”with its fusion of flavorsโ€”can be as adventurous as the outdoor activities. - -### Health and Safety - -- **Staying Hydrated:** The warm temperatures demand that you stay properly hydrated. Always carry water, especially during long hikes. -- **Sun Protection:** Use sunscreen, hats, and sunglasses to protect yourself during extended periods outdoors on both land and water. -- **Local Guides:** Utilize local tour operators for both hiking and water sports. Their expertise not only enriches your experience but also ensures safety in unfamiliar terrain or water bodies. - ---- - -## Conclusion - -The Caribbean in April is a haven for adventure seekers. With its pristine beaches, diverse ecosystems, and rich cultural tapestry, it offers something for every type of traveler. Whether you're chasing the perfect wave along the shores of Barbados and Puerto Rico, trekking through the lush landscapes of El Yunque or Morne Trois Pitons, or engaging in an array of water sports from snorkeling to kiteboarding, your ideal vacation is only a booking away. - -This report has outlined the best destinations and provided practical advice to optimize your vacation for surfing, hiking, and water sports. By considering the diverse offeringsโ€”from epic surf breaks and challenging hiking trails to vibrant water sportsโ€”the Caribbean stands out as a multi-adventure destination where every day brings a new experience. - -Plan carefully, pack wisely, and get ready to explore the vibrant mosaic of landscapes and activities that make the Caribbean in April a truly unforgettable adventure. - -Happy travels! - ---- - -_References available upon request. Many insights were drawn from trusted sources including Lonely Planet, TravelPug, and various Caribbean-centric exploration sites, ensuring a well-rounded and practical guide for your vacation planning._ diff --git a/pkg/hanzo-agent/examples/research_bot/sample_outputs/vacation.txt b/pkg/hanzo-agent/examples/research_bot/sample_outputs/vacation.txt deleted file mode 100644 index b26499817..000000000 --- a/pkg/hanzo-agent/examples/research_bot/sample_outputs/vacation.txt +++ /dev/null @@ -1,206 +0,0 @@ -# Terminal output for a vacation related query. See vacation.md for final report. - -$ uv run python -m examples.research_bot.main -What would you like to research? Caribbean vacation spots in April, optimizing for surfing, hiking and water sports -View trace: https://platform.openai.com/traces/trace_.... -Starting research... -โœ… Will perform 15 searches -โœ… Searching... 15/15 completed -โœ… Finishing report... -โœ… Report summary - -This report provides an in-depth exploration of selected Caribbean vacation spots in April that are ideal for surfing, hiking, and water sports. Covering -destinations from Barbados and Puerto Rico to the Bahamas and Jamaica, it examines favorable weather conditions, recommended surf breaks, scenic hiking -trails, and various water sports activities. Detailed destination profiles, activity highlights, and travel tips are integrated to help travelers design a -multi-adventure itinerary in the Caribbean during April. - - -=====REPORT===== - - -Report: # Caribbean Adventure in April: Surfing, Hiking, and Water Sports Exploration - -The Caribbean is renowned for its crystal-clear waters, vibrant culture, and diverse outdoor activities. April is an especially attractive month for visitors: warm temperatures, clear skies, and the promise of abundant activities. This report explores the best Caribbean destinations in April, with a focus on optimizing your vacation for surfing, hiking, and water sports. - ---- - -## Table of Contents - -1. [Introduction](#introduction) -2. [Why April is the Perfect Time in the Caribbean](#why-april-is-the-perfect-time-in-the-caribbean) -3. [Surfing in the Caribbean](#surfing-in-the-caribbean) - - 3.1 [Barbados: The Tale of Two Coasts](#barbados-the-tale-of-two-coasts) - - 3.2 [Puerto Rico: Rincรณn and Beyond](#puerto-rico-rinc%C3%B3n-and-beyond) - - 3.3 [Dominican Republic and Other Hotspots](#dominican-republic-and-other-hotspots) -4. [Hiking Adventures Across the Caribbean](#hiking-adventures-across-the-caribbean) - - 4.1 [Trekking Through Tropical Rainforests](#trekking-through-tropical-rainforests) - - 4.2 [Volcanic Peaks and Rugged Landscapes](#volcanic-peaks-and-rugged-landscapes) -5. [Diverse Water Sports Experiences](#diverse-water-sports-experiences) - - 5.1 [Snorkeling, Diving, and Jet Skiing](#snorkeling-diving-and-jet-skiing) - - 5.2 [Kiteboarding and Windsurfing](#kiteboarding-and-windsurfing) -6. [Combining Adventures: Multi-Activity Destinations](#combining-adventures-multi-activity-destinations) -7. [Practical Advice and Travel Tips](#practical-advice-and-travel-tips) -8. [Conclusion](#conclusion) - ---- - -## Introduction - -Caribbean vacations are much more than just beach relaxation; they offer adventure, exploration, and a lively cultural tapestry waiting to be discovered. For travelers seeking an adrenaline-filled getaway, April provides optimal conditions. This report synthesizes diverse research findings and travel insights to help you create an itinerary that combines the thrill of surfing, the challenge of hiking, and the excitement of water sports. - -Whether you're standing on the edge of a powerful reef break or trekking through lush tropical landscapes, the Caribbean in April invites you to dive into nature, adventure, and culture. The following sections break down the best destinations and activities, ensuring that every aspect of your trip is meticulously planned for an unforgettable experience. - ---- - -## Why April is the Perfect Time in the Caribbean - -April stands at the crossroads of seasons in many Caribbean destinations. It marks the tail end of the dry season, ensuring: - -- **Consistent Warm Temperatures:** Average daytime highs around 29ยฐC (84ยฐF) foster comfortable conditions for both land and water activities. -- **Pleasant Sea Temperatures:** With sea temperatures near 26ยฐC (79ยฐF), swimmers, surfers, and divers are treated to inviting waters. -- **Clear Skies and Minimal Rainfall:** Crisp, blue skies make for excellent visibility during snorkeling and diving, as well as clear panoramic views while hiking. -- **Festivals and Cultural Events:** Many islands host seasonal festivals such as Barbados' Fish Festival and Antigua's Sailing Week, adding a cultural layer to your vacation. - -These factors create an ideal backdrop for balancing your outdoor pursuits, whether youโ€™re catching epic waves, trekking rugged trails, or partaking in water sports. - ---- - -## Surfing in the Caribbean - -Surfing in the Caribbean offers diverse wave experiences, ranging from gentle, beginner-friendly rollers to powerful reef breaks that challenge even seasoned surfers. April, in particular, provides excellent conditions for those looking to ride its picturesque waves. - -### Barbados: The Tale of Two Coasts - -Barbados is a prime destination: - -- **Soup Bowl in Bathsheba:** On the east coast, the Soup Bowl is famous for its consistent, powerful waves. This spot attracts experienced surfers who appreciate its challenging right-hand reef break with steep drops, providing the kind of performance wave rarely found elsewhere. -- **Freights Bay:** On the south coast, visitors find more forgiving, gentle wave conditions. Ideal for beginners and longboarders, this spot offers the perfect balance for those still mastering their craft. - -Barbados not only excels in its surfing credentials but also complements the experience with a rich local culture and events in April, making it a well-rounded destination. - -### Puerto Rico: Rincรณn and Beyond - -Rincรณn in Puerto Rico is hailed as the Caribbeanโ€™s surfing capital: - -- **Diverse Breaks:** With spots ranging from challenging reef breaks such as Tres Palmas and Dogman's to more inviting waves at Domes and Maria's, Puerto Rico offers a spectrum for all surfing skill levels. -- **Local Culture:** Aside from its surf culture, the island boasts vibrant local food scenes, historic sites, and exciting nightlife, enriching your overall travel experience. - -In addition, Puerto Ricoโ€™s coasts often feature opportunities for hiking and other outdoor adventures, making it an attractive option for multi-activity travelers. - -### Dominican Republic and Other Hotspots - -Other islands such as the Dominican Republic, with Playa Encuentro on its north coast, provide consistent surf year-round. Highlights include: - -- **Playa Encuentro:** A hotspot known for its dependable breaks, ideal for both intermediate and advanced surfers during the cooler months of October to April. -- **Jamaica and The Bahamas:** Jamaicaโ€™s Boston Bay offers a mix of beginner and intermediate waves, and The Bahamasโ€™ Surferโ€™s Beach on Eleuthera draws parallels to the legendary surf spots of Hawaii, especially during the winter months. - -These destinations not only spotlight surfing but also serve as gateways to additional outdoor activities, ensuring there's never a dull moment whether you're balancing waves with hikes or cultural exploration. - ---- - -## Hiking Adventures Across the Caribbean - -The Caribbean's topography is as varied as it is beautiful. Its network of hiking trails traverses volcanic peaks, ancient rainforests, and dramatic coastal cliffs, offering breathtaking vistas to intrepid explorers. - -### Trekking Through Tropical Rainforests - -For nature enthusiasts, the lush forests of the Caribbean present an immersive encounter with biodiversity: - -- **El Yunque National Forest, Puerto Rico:** The only tropical rainforest within the U.S. National Forest System, El Yunque is rich in endemic species such as the Puerto Rican parrot and the famous coquรญ frog. Trails like the El Yunque Peak Trail and La Mina Falls Trail provide both challenging hikes and scenic rewards. -- **Virgin Islands National Park, St. John:** With over 20 well-defined trails, this park offers hikes that reveal historical petroglyphs, colonial ruins, and stunning coastal views along the Reef Bay Trail. - -### Volcanic Peaks and Rugged Landscapes - -For those seeking more rugged challenges, several destinations offer unforgettable adventures: - -- **Morne Trois Pitons National Park, Dominica:** A UNESCO World Heritage Site showcasing volcanic landscapes, hot springs, the famed Boiling Lake, and lush trails that lead to hidden waterfalls. -- **Gros Piton, Saint Lucia:** The iconic hike up Gros Piton provides a moderately challenging trek that ends with panoramic views of the Caribbean Sea, a truly rewarding experience for hikers. -- **La Soufriรจre, St. Vincent:** This active volcano not only offers a dynamic hiking environment but also the opportunity to observe the ongoing geological transformations up close. - -Other noteworthy hiking spots include the Blue Mountains in Jamaica for coffee plantation tours and expansive views, as well as trails in Martinique around Montagne Pelรฉe, which combine historical context with natural beauty. - ---- - -## Diverse Water Sports Experiences - -While surfing and hiking attract a broad range of adventurers, the Caribbean also scores high on other water sports. Whether you're drawn to snorkeling, jet skiing, or wind- and kiteboarding, the islands offer a plethora of aquatic activities. - -### Snorkeling, Diving, and Jet Skiing - -Caribbean waters teem with life and color, making them ideal for underwater exploration: - -- **Bonaire:** Its protected marine parks serve as a magnet for divers and snorkelers. With vibrant coral reefs and diverse marine species, Bonaire is a top destination for those who appreciate the underwater world. -- **Cayman Islands:** Unique attractions such as Stingray City provide opportunities to interact with friendly stingrays in clear, calm waters. Additionally, the Underwater Sculpture Park is an innovative blend of art and nature. -- **The Bahamas:** In places like Eleuthera, excursions often cater to families and thrill-seekers alike. Options include jet ski rentals, where groups can explore hidden beaches and pristine coves while enjoying the vibrant marine life. - -### Kiteboarding and Windsurfing - -Harnessing the steady trade winds and warm Caribbean waters, several islands have become hubs for kiteboarding and windsurfing: - -- **Aruba:** Known as "One Happy Island," Arubaโ€™s Fisherman's Huts area provides consistent winds, perfect for enthusiasts of windsurfing and kiteboarding alike. -- **Cabarete, Dominican Republic and Silver Rock, Barbados:** Both destinations benefit from reliable trade winds, making them popular among kitesurfers. These spots often combine water sports with a lively beach culture, ensuring that the fun continues on land as well. - -Local operators provide equipment rental and lessons, ensuring that even first-time adventurers can safely and confidently enjoy these exciting sports. - ---- - -## Combining Adventures: Multi-Activity Destinations - -For travelers seeking a comprehensive vacation where surfing, hiking, and water sports converge, several Caribbean destinations offer the best of all worlds. - -- **Puerto Rico:** With its robust surf scene in Rincรณn, world-class hiking in El Yunque, and opportunities for snorkeling and jet skiing in San Juan Bay, Puerto Rico is a true multi-adventure destination. -- **Barbados:** In addition to the surf breaks along its coasts, Barbados offers a mix of cultural events, local cuisine, and even hiking excursions to scenic rural areas, making for a well-rounded experience. -- **Dominican Republic and Jamaica:** Both are renowned not only for their consistent surf conditions but also for expansive hiking trails and water sports. From the rugged landscapes of the Dominican Republic to Jamaicaโ€™s blend of cultural history and natural exploration, these islands allow travelers to mix and match activities seamlessly. - -Group tours and local guides further enhance these experiences, providing insider tips, safe excursions, and personalized itineraries that cater to multiple interests within one trip. - ---- - -## Practical Advice and Travel Tips - -### Weather and Timing - -- **Optimal Climate:** April offers ideal weather conditions across the Caribbean. With minimal rainfall and warm temperatures, it is a great time to schedule outdoor activities. -- **Surfing Seasons:** While April marks the end of the prime surf season in some areas (like Rincรณn in Puerto Rico), many destinations maintain consistent conditions during this month. - -### Booking and Costs - -- **Surfing Lessons:** Expect to pay between $40 and $110 per session depending on the location. For instance, Puerto Rico typically charges around $75 for beginner lessons, while group lessons in the Dominican Republic average approximately $95. -- **Equipment Rentals:** Pricing for jet ski, surfboard, and snorkeling equipment may vary. In the Bahamas, an hour-long jet ski tour might cost about $120 per group, whereas a similar experience might be available at a lower cost in other regions. -- **Accommodations:** Prices also vary by island. Many travelers find that even affordable stays do not skimp on amenities, allowing you to invest more in guided excursions and local experiences. - -### Cultural Considerations - -- **Festivals and Events:** Check local event calendars. Destinations like Barbados and Antigua host festivals in April that combine cultural heritage with festive outdoor activities. -- **Local Cuisine:** Incorporate food tours into your itinerary. Caribbean cuisineโ€”with its fusion of flavorsโ€”can be as adventurous as the outdoor activities. - -### Health and Safety - -- **Staying Hydrated:** The warm temperatures demand that you stay properly hydrated. Always carry water, especially during long hikes. -- **Sun Protection:** Use sunscreen, hats, and sunglasses to protect yourself during extended periods outdoors on both land and water. -- **Local Guides:** Utilize local tour operators for both hiking and water sports. Their expertise not only enriches your experience but also ensures safety in unfamiliar terrain or water bodies. - ---- - -## Conclusion - -The Caribbean in April is a haven for adventure seekers. With its pristine beaches, diverse ecosystems, and rich cultural tapestry, it offers something for every type of traveler. Whether you're chasing the perfect wave along the shores of Barbados and Puerto Rico, trekking through the lush landscapes of El Yunque or Morne Trois Pitons, or engaging in an array of water sports from snorkeling to kiteboarding, your ideal vacation is only a booking away. - -This report has outlined the best destinations and provided practical advice to optimize your vacation for surfing, hiking, and water sports. By considering the diverse offeringsโ€”from epic surf breaks and challenging hiking trails to vibrant water sportsโ€”the Caribbean stands out as a multi-adventure destination where every day brings a new experience. - -Plan carefully, pack wisely, and get ready to explore the vibrant mosaic of landscapes and activities that make the Caribbean in April a truly unforgettable adventure. - -Happy travels! - ---- - -*References available upon request. Many insights were drawn from trusted sources including Lonely Planet, TravelPug, and various Caribbean-centric exploration sites, ensuring a well-rounded and practical guide for your vacation planning.* - - - -=====FOLLOW UP QUESTIONS===== - - -Follow up questions: Would you like detailed profiles for any of the highlighted destinations (e.g., Puerto Rico or Barbados)? -Are you interested in more information about booking details and local tour operators in specific islands? -Do you need guidance on combining cultural events with outdoor adventures during your Caribbean vacation? \ No newline at end of file diff --git a/pkg/hanzo-agent/examples/tools/computer_use.py b/pkg/hanzo-agent/examples/tools/computer_use.py deleted file mode 100644 index 46f021ed2..000000000 --- a/pkg/hanzo-agent/examples/tools/computer_use.py +++ /dev/null @@ -1,168 +0,0 @@ -import asyncio -import base64 -from typing import Literal, Union - -from playwright.async_api import Browser, Page, Playwright, async_playwright - -from agents import ( - Agent, - AsyncComputer, - Button, - ComputerTool, - Environment, - ModelSettings, - Runner, - trace, -) - -# Uncomment to see very verbose logs -# import logging -# logging.getLogger("openai.agents").setLevel(logging.DEBUG) -# logging.getLogger("openai.agents").addHandler(logging.StreamHandler()) - - -async def main(): - async with LocalPlaywrightComputer() as computer: - with trace("Computer use example"): - agent = Agent( - name="Browser user", - instructions="You are a helpful agent.", - tools=[ComputerTool(computer)], - # Use the computer using model, and set truncation to auto because its required - model="computer-use-preview", - model_settings=ModelSettings(truncation="auto"), - ) - result = await Runner.run(agent, "Search for SF sports news and summarize.") - print(result.final_output) - - -CUA_KEY_TO_PLAYWRIGHT_KEY = { - "/": "Divide", - "\\": "Backslash", - "alt": "Alt", - "arrowdown": "ArrowDown", - "arrowleft": "ArrowLeft", - "arrowright": "ArrowRight", - "arrowup": "ArrowUp", - "backspace": "Backspace", - "capslock": "CapsLock", - "cmd": "Meta", - "ctrl": "Control", - "delete": "Delete", - "end": "End", - "enter": "Enter", - "esc": "Escape", - "home": "Home", - "insert": "Insert", - "option": "Alt", - "pagedown": "PageDown", - "pageup": "PageUp", - "shift": "Shift", - "space": " ", - "super": "Meta", - "tab": "Tab", - "win": "Meta", -} - - -class LocalPlaywrightComputer(AsyncComputer): - """A computer, implemented using a local Playwright browser.""" - - def __init__(self): - self._playwright: Union[Playwright, None] = None - self._browser: Union[Browser, None] = None - self._page: Union[Page, None] = None - - async def _get_browser_and_page(self) -> tuple[Browser, Page]: - width, height = self.dimensions - launch_args = [f"--window-size={width},{height}"] - browser = await self.playwright.chromium.launch( - headless=False, args=launch_args - ) - page = await browser.new_page() - await page.set_viewport_size({"width": width, "height": height}) - await page.goto("https://www.bing.com") - return browser, page - - async def __aenter__(self): - # Start Playwright and call the subclass hook for getting browser/page - self._playwright = await async_playwright().start() - self._browser, self._page = await self._get_browser_and_page() - return self - - async def __aexit__(self, exc_type, exc_val, exc_tb): - if self._browser: - await self._browser.close() - if self._playwright: - await self._playwright.stop() - - @property - def playwright(self) -> Playwright: - assert self._playwright is not None - return self._playwright - - @property - def browser(self) -> Browser: - assert self._browser is not None - return self._browser - - @property - def page(self) -> Page: - assert self._page is not None - return self._page - - @property - def environment(self) -> Environment: - return "browser" - - @property - def dimensions(self) -> tuple[int, int]: - return (1024, 768) - - async def screenshot(self) -> str: - """Capture only the viewport (not full_page).""" - png_bytes = await self.page.screenshot(full_page=False) - return base64.b64encode(png_bytes).decode("utf-8") - - async def click(self, x: int, y: int, button: Button = "left") -> None: - playwright_button: Literal["left", "middle", "right"] = "left" - - # Playwright only supports left, middle, right buttons - if button in ("left", "right", "middle"): - playwright_button = button # type: ignore - - await self.page.mouse.click(x, y, button=playwright_button) - - async def double_click(self, x: int, y: int) -> None: - await self.page.mouse.dblclick(x, y) - - async def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: - await self.page.mouse.move(x, y) - await self.page.evaluate(f"window.scrollBy({scroll_x}, {scroll_y})") - - async def type(self, text: str) -> None: - await self.page.keyboard.type(text) - - async def wait(self) -> None: - await asyncio.sleep(1) - - async def move(self, x: int, y: int) -> None: - await self.page.mouse.move(x, y) - - async def keypress(self, keys: list[str]) -> None: - for key in keys: - mapped_key = CUA_KEY_TO_PLAYWRIGHT_KEY.get(key.lower(), key) - await self.page.keyboard.press(mapped_key) - - async def drag(self, path: list[tuple[int, int]]) -> None: - if not path: - return - await self.page.mouse.move(path[0][0], path[0][1]) - await self.page.mouse.down() - for px, py in path[1:]: - await self.page.mouse.move(px, py) - await self.page.mouse.up() - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/tools/file_search.py b/pkg/hanzo-agent/examples/tools/file_search.py deleted file mode 100644 index 2a3d4cf12..000000000 --- a/pkg/hanzo-agent/examples/tools/file_search.py +++ /dev/null @@ -1,36 +0,0 @@ -import asyncio - -from agents import Agent, FileSearchTool, Runner, trace - - -async def main(): - agent = Agent( - name="File searcher", - instructions="You are a helpful agent.", - tools=[ - FileSearchTool( - max_num_results=3, - vector_store_ids=["vs_67bf88953f748191be42b462090e53e7"], - include_search_results=True, - ) - ], - ) - - with trace("File search example"): - result = await Runner.run( - agent, "Be concise, and tell me 1 sentence about Arrakis I might not know." - ) - print(result.final_output) - """ - Arrakis, the desert planet in Frank Herbert's "Dune," was inspired by the scarcity of water - as a metaphor for oil and other finite resources. - """ - - print("\n".join([str(out) for out in result.new_items])) - """ - {"id":"...", "queries":["Arrakis"], "results":[...]} - """ - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/examples/tools/web_search.py b/pkg/hanzo-agent/examples/tools/web_search.py deleted file mode 100644 index b76d4262a..000000000 --- a/pkg/hanzo-agent/examples/tools/web_search.py +++ /dev/null @@ -1,25 +0,0 @@ -import asyncio - -from agents import Agent, Runner, WebSearchTool, trace - - -async def main(): - agent = Agent( - name="Web searcher", - instructions="You are a helpful agent.", - tools=[ - WebSearchTool(user_location={"type": "approximate", "city": "New York"}) - ], - ) - - with trace("Web search example"): - result = await Runner.run( - agent, - "search the web for 'local sports news' and give me 1 interesting update in a sentence.", - ) - print(result.final_output) - # The New York Giants are reportedly pursuing quarterback Aaron Rodgers after his ... - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/mkdocs.yml b/pkg/hanzo-agent/mkdocs.yml deleted file mode 100644 index 13e2b866e..000000000 --- a/pkg/hanzo-agent/mkdocs.yml +++ /dev/null @@ -1,121 +0,0 @@ -site_name: Hanzo AI Agent SDK -theme: - name: material - features: - # Allows copying code blocks - - content.code.copy - # Allows selecting code blocks - - content.code.select - # Shows the current path in the sidebar - - navigation.path - # Shows sections in the sidebar - - navigation.sections - # Shows sections expanded by default - - navigation.expand - # Enables annotations in code blocks - - content.code.annotate - palette: - primary: black - logo: assets/logo.svg - favicon: images/favicon-platform.svg -nav: - - Intro: index.md - - Quickstart: quickstart.md - - Documentation: - - agents.md - - running_agents.md - - results.md - - streaming.md - - tools.md - - handoffs.md - - tracing.md - - context.md - - guardrails.md - - multi_agent.md - - models.md - - config.md - - API Reference: - - Agents: - - ref/index.md - - ref/agent.md - - ref/run.md - - ref/tool.md - - ref/result.md - - ref/stream_events.md - - ref/handoffs.md - - ref/lifecycle.md - - ref/items.md - - ref/run_context.md - - ref/usage.md - - ref/exceptions.md - - ref/guardrail.md - - ref/model_settings.md - - ref/agent_output.md - - ref/function_schema.md - - ref/models/interface.md - - ref/models/openai_chatcompletions.md - - ref/models/openai_responses.md - - Tracing: - - ref/tracing/index.md - - ref/tracing/create.md - - ref/tracing/traces.md - - ref/tracing/spans.md - - ref/tracing/processor_interface.md - - ref/tracing/processors.md - - ref/tracing/scope.md - - ref/tracing/setup.md - - ref/tracing/span_data.md - - ref/tracing/util.md - - Extensions: - - ref/extensions/handoff_filters.md - - ref/extensions/handoff_prompt.md - -plugins: - - search - - mkdocstrings: - handlers: - python: - paths: ["src/agents"] - selection: - docstring_style: google - options: - # Shows links to other members in signatures - signature_crossrefs: true - # Orders members by source order, rather than alphabetical - members_order: source - # Puts the signature on a separate line from the member name - separate_signature: true - # Shows type annotations in signatures - show_signature_annotations: true - # Makes the font sizes nicer - heading_level: 3 - -extra: - # Remove material generation message in footer - generator: false - -markdown_extensions: - - admonition - - pymdownx.details - - pymdownx.superfences - - attr_list - - md_in_html - - pymdownx.highlight: - anchor_linenums: true - line_spans: __span - pygments_lang_class: true - - pymdownx.inlinehilite - - pymdownx.snippets - - pymdownx.superfences - -validation: - omitted_files: warn - absolute_links: warn - unrecognized_links: warn - anchors: warn - -extra_css: - - stylesheets/extra.css - -watch: - - "src/agents" diff --git a/pkg/hanzo-agent/pyproject.toml b/pkg/hanzo-agent/pyproject.toml deleted file mode 100644 index bd17c6a26..000000000 --- a/pkg/hanzo-agent/pyproject.toml +++ /dev/null @@ -1,149 +0,0 @@ -[project] -name = "hanzo-agent" -version = "0.0.4" -description = "Hanzo AI SDK" -readme = "README.md" -requires-python = ">=3.12" -license = "MIT" -authors = [ - { name = "Hanzo AI", email = "support@hanzo.ai" }, -] -dependencies = [ - "hanzo-memory>=1.0.0", - "hanzo-async>=0.1.0", - "openai>=1.66.2", - "pydantic>=2.10, <3", - "griffe>=1.5.6, <2", - "typing-extensions>=4.12.2, <5", - "requests>=2.0, <3", - "types-requests>=2.0, <3", - "numpy>=1.24.0", -] -classifiers = [ - "Typing :: Typed", - "Intended Audience :: Developers", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.12", - "Intended Audience :: Developers", - "Operating System :: OS Independent", - "Topic :: Software Development :: Libraries :: Python Modules", - "License :: OSI Approved :: MIT License" -] - -[project.urls] -Homepage = "https://github.com/hanzoai/agent" -Repository = "https://github.com/hanzoai/agent" - -[project.optional-dependencies] -# Web3 integration - wallet, transactions, on-chain identity -web3 = [ - "web3>=6.0.0,<7", - "eth-account>=0.10.0,<1", - "eth-utils>=2.0.0,<5", -] - -# TEE support - Intel SGX, AMD SEV, NVIDIA H100 attestation -tee = [ - "cryptography>=41.0.0,<50", -] - -# Marketplace - agent service discovery and economic primitives -marketplace = [ - "aiohttp>=3.9.0,<4", - "redis>=5.0.0,<6", -] - -# CLI integration -cli = [ - "click>=8.1.0,<9", - "rich>=13.0.0,<14", -] - -# All extensions -full = [ - "hanzoai[web3,tee,marketplace,cli]", -] - -[dependency-groups] -dev = [ - "mypy", - "ruff==0.9.2", - "pytest", - "pytest-asyncio", - "pytest-mock>=3.14.0", - "rich", - "mkdocs>=1.6.0", - "mkdocs-material>=9.6.0", - "mkdocstrings[python]>=0.28.0", - "coverage>=7.6.12", - "playwright==1.50.0", -] -[tool.uv.workspace] -members = ["agents"] - -[tool.uv.sources] -agents = { workspace = true } - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["src/agents"] - - -[tool.ruff] -line-length = 100 -target-version = "py39" - -[tool.ruff.lint] -select = [ - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # pyflakes - "I", # isort - "B", # flake8-bugbear - "C4", # flake8-comprehensions - "UP", # pyupgrade -] -isort = { combine-as-imports = true, known-first-party = ["agents"] } - -[tool.ruff.lint.pydocstyle] -convention = "google" - -[tool.ruff.lint.per-file-ignores] -"examples/**/*.py" = ["E501"] - -[tool.mypy] -strict = true -disallow_incomplete_defs = false -disallow_untyped_defs = false -disallow_untyped_calls = false - -[tool.coverage.run] -source = [ - "tests", - "src/agents", -] - -[tool.coverage.report] -show_missing = true -sort = "-Cover" -exclude_also = [ - # This is only executed while typechecking - "if TYPE_CHECKING:", - "@abc.abstractmethod", - "raise NotImplementedError", - "logger.debug", -] - -[tool.pytest.ini_options] -asyncio_mode = "auto" -asyncio_default_fixture_loop_scope = "session" -filterwarnings = [ - # This is a warning that is expected to happen: we have an async filter that raises an exception - "ignore:coroutine 'test_async_input_filter_fails..invalid_input_filter' was never awaited:RuntimeWarning", -] -markers = [ - "allow_call_model_methods: mark test as allowing calls to real model implementations", -] diff --git a/pkg/hanzo-agent/src/agents/__init__.py b/pkg/hanzo-agent/src/agents/__init__.py deleted file mode 100644 index ba2800806..000000000 --- a/pkg/hanzo-agent/src/agents/__init__.py +++ /dev/null @@ -1,332 +0,0 @@ -import logging -import sys -from typing import Literal - -from openai import AsyncOpenAI -import hanzo_async - -# Configure unified event loop (uvloop) -hanzo_async.configure_loop() - -from . import _config -from .agent import Agent -from .agent_output import AgentOutputSchema -from .computer import AsyncComputer, Button, Computer, Environment -from .exceptions import ( - AgentsException, - InputGuardrailTripwireTriggered, - MaxTurnsExceeded, - ModelBehaviorError, - OutputGuardrailTripwireTriggered, - UserError, -) -from .guardrail import ( - GuardrailFunctionOutput, - InputGuardrail, - InputGuardrailResult, - OutputGuardrail, - OutputGuardrailResult, - input_guardrail, - output_guardrail, -) -from .handoffs import Handoff, HandoffInputData, HandoffInputFilter, handoff -from .items import ( - HandoffCallItem, - HandoffOutputItem, - ItemHelpers, - MessageOutputItem, - ModelResponse, - ReasoningItem, - RunItem, - ToolCallItem, - ToolCallOutputItem, - TResponseInputItem, -) -from .lifecycle import AgentHooks, RunHooks -from .model_settings import ModelSettings -from .models.interface import Model, ModelProvider, ModelTracing -from .models.openai_chatcompletions import OpenAIChatCompletionsModel -from .models.openai_provider import OpenAIProvider -from .models.openai_responses import OpenAIResponsesModel -from .models.hanzo_node_provider import HanzoNodeProvider, create_hanzo_node_provider -from .result import RunResult, RunResultStreaming -from .run import RunConfig, Runner -from .run_context import RunContextWrapper, TContext -from .stream_events import ( - AgentUpdatedStreamEvent, - RawResponsesStreamEvent, - RunItemStreamEvent, - StreamEvent, -) -from .tool import ( - ComputerTool, - FileSearchTool, - FunctionTool, - Tool, - WebSearchTool, - default_tool_error_function, - function_tool, -) -from .tracing import ( - AgentSpanData, - CustomSpanData, - FunctionSpanData, - GenerationSpanData, - GuardrailSpanData, - HandoffSpanData, - Span, - SpanData, - SpanError, - Trace, - add_trace_processor, - agent_span, - custom_span, - function_span, - gen_span_id, - gen_trace_id, - generation_span, - get_current_span, - get_current_trace, - guardrail_span, - handoff_span, - set_trace_processors, - set_tracing_disabled, - set_tracing_export_api_key, - trace, -) -from .usage import Usage - -# Network and orchestration imports -from .network import ( - AgentNetwork, - NetworkConfig, - Router, - RoutingDecision, - RoutingStrategy, - SemanticRouter, - RuleBasedRouter, - LoadBalancingRouter, - routing_strategy, - NetworkNode, - NodeStatus, -) -from .state import ( - StateStore, - InMemoryStateStore, - RedisStateStore, - FileStateStore, - StateNamespace, - StateSerializer, - JSONSerializer, - PickleSerializer, -) -from .memory import ( - Memory, - MemoryEntry, - MemoryType, - MemoryStore, - InMemoryMemoryStore, - VectorMemoryStore, - MemoryRetriever, - SemanticRetriever, - RecencyRetriever, - HybridRetriever, -) -from .orchestration import ( - Orchestrator, - OrchestrationConfig, - Workflow, - WorkflowStep, - StepType, - WorkflowExecutor, - ExecutionResult, - UIStreamer, - StreamUpdate, - UpdateType, -) -from .reflexion import ReflexionEngine, Rule - - -def set_default_openai_key(key: str, use_for_tracing: bool = True) -> None: - """Set the default OpenAI API key to use for LLM requests (and optionally tracing(). This is - only necessary if the OPENAI_API_KEY environment variable is not already set. - - If provided, this key will be used instead of the OPENAI_API_KEY environment variable. - - Args: - key: The OpenAI key to use. - use_for_tracing: Whether to also use this key to send traces to OpenAI. Defaults to True - If False, you'll either need to set the OPENAI_API_KEY environment variable or call - set_tracing_export_api_key() with the API key you want to use for tracing. - """ - _config.set_default_openai_key(key, use_for_tracing) - - -def set_default_openai_client( - client: AsyncOpenAI, use_for_tracing: bool = True -) -> None: - """Set the default OpenAI client to use for LLM requests and/or tracing. If provided, this - client will be used instead of the default OpenAI client. - - Args: - client: The OpenAI client to use. - use_for_tracing: Whether to use the API key from this client for uploading traces. If False, - you'll either need to set the OPENAI_API_KEY environment variable or call - set_tracing_export_api_key() with the API key you want to use for tracing. - """ - _config.set_default_openai_client(client, use_for_tracing) - - -def set_default_openai_api(api: Literal["chat_completions", "responses"]) -> None: - """Set the default API to use for OpenAI LLM requests. By default, we will use the responses API - but you can set this to use the chat completions API instead. - """ - _config.set_default_openai_api(api) - - -def enable_verbose_stdout_logging(): - """Enables verbose logging to stdout. This is useful for debugging.""" - logger = logging.getLogger("openai.agents") - logger.setLevel(logging.DEBUG) - logger.addHandler(logging.StreamHandler(sys.stdout)) - - -__all__ = [ - "Agent", - "Runner", - "Model", - "ModelProvider", - "ModelTracing", - "ModelSettings", - "OpenAIChatCompletionsModel", - "OpenAIProvider", - "OpenAIResponsesModel", - "HanzoNodeProvider", - "create_hanzo_node_provider", - "AgentOutputSchema", - "Computer", - "AsyncComputer", - "Environment", - "Button", - "AgentsException", - "InputGuardrailTripwireTriggered", - "OutputGuardrailTripwireTriggered", - "MaxTurnsExceeded", - "ModelBehaviorError", - "UserError", - "InputGuardrail", - "InputGuardrailResult", - "OutputGuardrail", - "OutputGuardrailResult", - "GuardrailFunctionOutput", - "input_guardrail", - "output_guardrail", - "handoff", - "Handoff", - "HandoffInputData", - "HandoffInputFilter", - "TResponseInputItem", - "MessageOutputItem", - "ModelResponse", - "RunItem", - "HandoffCallItem", - "HandoffOutputItem", - "ToolCallItem", - "ToolCallOutputItem", - "ReasoningItem", - "ModelResponse", - "ItemHelpers", - "RunHooks", - "AgentHooks", - "RunContextWrapper", - "TContext", - "RunResult", - "RunResultStreaming", - "RunConfig", - "RawResponsesStreamEvent", - "RunItemStreamEvent", - "AgentUpdatedStreamEvent", - "StreamEvent", - "FunctionTool", - "ComputerTool", - "FileSearchTool", - "Tool", - "WebSearchTool", - "function_tool", - "Usage", - "add_trace_processor", - "agent_span", - "custom_span", - "function_span", - "generation_span", - "get_current_span", - "get_current_trace", - "guardrail_span", - "handoff_span", - "set_trace_processors", - "set_tracing_disabled", - "trace", - "Trace", - "SpanError", - "Span", - "SpanData", - "AgentSpanData", - "CustomSpanData", - "FunctionSpanData", - "GenerationSpanData", - "GuardrailSpanData", - "HandoffSpanData", - "set_default_openai_key", - "set_default_openai_client", - "set_default_openai_api", - "set_tracing_export_api_key", - "enable_verbose_stdout_logging", - "gen_trace_id", - "gen_span_id", - "default_tool_error_function", - # Network exports - "AgentNetwork", - "NetworkConfig", - "Router", - "RoutingDecision", - "RoutingStrategy", - "SemanticRouter", - "RuleBasedRouter", - "LoadBalancingRouter", - "routing_strategy", - "NetworkNode", - "NodeStatus", - # State exports - "StateStore", - "InMemoryStateStore", - "RedisStateStore", - "FileStateStore", - "StateNamespace", - "StateSerializer", - "JSONSerializer", - "PickleSerializer", - # Memory exports - "Memory", - "MemoryEntry", - "MemoryType", - "MemoryStore", - "InMemoryMemoryStore", - "VectorMemoryStore", - "MemoryRetriever", - "SemanticRetriever", - "RecencyRetriever", - "HybridRetriever", - # Orchestration exports - "Orchestrator", - "OrchestrationConfig", - "Workflow", - "WorkflowStep", - "StepType", - "WorkflowExecutor", - "ExecutionResult", - "UIStreamer", - "StreamUpdate", - "UpdateType", - "ReflexionEngine", - "Rule", -] diff --git a/pkg/hanzo-agent/src/agents/_config.py b/pkg/hanzo-agent/src/agents/_config.py deleted file mode 100644 index 304cfb83c..000000000 --- a/pkg/hanzo-agent/src/agents/_config.py +++ /dev/null @@ -1,26 +0,0 @@ -from openai import AsyncOpenAI -from typing_extensions import Literal - -from .models import _openai_shared -from .tracing import set_tracing_export_api_key - - -def set_default_openai_key(key: str, use_for_tracing: bool) -> None: - _openai_shared.set_default_openai_key(key) - - if use_for_tracing: - set_tracing_export_api_key(key) - - -def set_default_openai_client(client: AsyncOpenAI, use_for_tracing: bool) -> None: - _openai_shared.set_default_openai_client(client) - - if use_for_tracing: - set_tracing_export_api_key(client.api_key) - - -def set_default_openai_api(api: Literal["chat_completions", "responses"]) -> None: - if api == "chat_completions": - _openai_shared.set_use_responses_by_default(False) - else: - _openai_shared.set_use_responses_by_default(True) diff --git a/pkg/hanzo-agent/src/agents/_debug.py b/pkg/hanzo-agent/src/agents/_debug.py deleted file mode 100644 index c6f51e943..000000000 --- a/pkg/hanzo-agent/src/agents/_debug.py +++ /dev/null @@ -1,19 +0,0 @@ -import os - - -def _debug_flag_enabled(flag: str) -> bool: - flag_value = os.getenv(flag) - return flag_value is not None and ( - flag_value == "1" or flag_value.lower() == "true" - ) - - -DONT_LOG_MODEL_DATA = _debug_flag_enabled("OPENAI_AGENTS_DONT_LOG_MODEL_DATA") -"""By default we don't log LLM inputs/outputs, to prevent exposing sensitive information. Set this -flag to enable logging them. -""" - -DONT_LOG_TOOL_DATA = _debug_flag_enabled("OPENAI_AGENTS_DONT_LOG_TOOL_DATA") -"""By default we don't log tool call inputs/outputs, to prevent exposing sensitive information. Set -this flag to enable logging them. -""" diff --git a/pkg/hanzo-agent/src/agents/_run_impl.py b/pkg/hanzo-agent/src/agents/_run_impl.py deleted file mode 100644 index ba100d77c..000000000 --- a/pkg/hanzo-agent/src/agents/_run_impl.py +++ /dev/null @@ -1,825 +0,0 @@ -from __future__ import annotations - -import asyncio -from dataclasses import dataclass -from typing import TYPE_CHECKING, Any - -from openai.types.responses import ( - ResponseComputerToolCall, - ResponseFileSearchToolCall, - ResponseFunctionToolCall, - ResponseFunctionWebSearch, - ResponseOutputMessage, -) -from openai.types.responses.response_computer_tool_call import ( - ActionClick, - ActionDoubleClick, - ActionDrag, - ActionKeypress, - ActionMove, - ActionScreenshot, - ActionScroll, - ActionType, - ActionWait, -) -from openai.types.responses.response_input_param import ComputerCallOutput -from openai.types.responses.response_reasoning_item import ResponseReasoningItem - -from . import _utils -from .agent import Agent -from .agent_output import AgentOutputSchema -from .computer import AsyncComputer, Computer -from .exceptions import AgentsException, ModelBehaviorError, UserError -from .guardrail import ( - InputGuardrail, - InputGuardrailResult, - OutputGuardrail, - OutputGuardrailResult, -) -from .handoffs import Handoff, HandoffInputData -from .items import ( - HandoffCallItem, - HandoffOutputItem, - ItemHelpers, - MessageOutputItem, - ModelResponse, - ReasoningItem, - RunItem, - ToolCallItem, - ToolCallOutputItem, - TResponseInputItem, -) -from .lifecycle import RunHooks -from .logger import logger -from .models.interface import ModelTracing -from .run_context import RunContextWrapper, TContext -from .stream_events import RunItemStreamEvent, StreamEvent -from .tool import ComputerTool, FunctionTool -from .tracing import ( - SpanError, - Trace, - function_span, - get_current_trace, - guardrail_span, - handoff_span, - trace, -) - -if TYPE_CHECKING: - from .run import RunConfig - - -class QueueCompleteSentinel: - pass - - -QUEUE_COMPLETE_SENTINEL = QueueCompleteSentinel() - - -@dataclass -class ToolRunHandoff: - handoff: Handoff - tool_call: ResponseFunctionToolCall - - -@dataclass -class ToolRunFunction: - tool_call: ResponseFunctionToolCall - function_tool: FunctionTool - - -@dataclass -class ToolRunComputerAction: - tool_call: ResponseComputerToolCall - computer_tool: ComputerTool - - -@dataclass -class ProcessedResponse: - new_items: list[RunItem] - handoffs: list[ToolRunHandoff] - functions: list[ToolRunFunction] - computer_actions: list[ToolRunComputerAction] - - def has_tools_to_run(self) -> bool: - # Handoffs, functions and computer actions need local processing - # Hosted tools have already run, so there's nothing to do. - return any( - [ - self.handoffs, - self.functions, - self.computer_actions, - ] - ) - - -@dataclass -class NextStepHandoff: - new_agent: Agent[Any] - - -@dataclass -class NextStepFinalOutput: - output: Any - - -@dataclass -class NextStepRunAgain: - pass - - -@dataclass -class SingleStepResult: - original_input: str | list[TResponseInputItem] - """The input items i.e. the items before run() was called. May be mutated by handoff input - filters.""" - - model_response: ModelResponse - """The model response for the current step.""" - - pre_step_items: list[RunItem] - """Items generated before the current step.""" - - new_step_items: list[RunItem] - """Items generated during this current step.""" - - next_step: NextStepHandoff | NextStepFinalOutput | NextStepRunAgain - """The next step to take.""" - - @property - def generated_items(self) -> list[RunItem]: - """Items generated during the agent run (i.e. everything generated after - `original_input`).""" - return self.pre_step_items + self.new_step_items - - -def get_model_tracing_impl( - tracing_disabled: bool, trace_include_sensitive_data: bool -) -> ModelTracing: - if tracing_disabled: - return ModelTracing.DISABLED - elif trace_include_sensitive_data: - return ModelTracing.ENABLED - else: - return ModelTracing.ENABLED_WITHOUT_DATA - - -class RunImpl: - @classmethod - async def execute_tools_and_side_effects( - cls, - *, - agent: Agent[TContext], - # The original input to the Runner - original_input: str | list[TResponseInputItem], - # Everything generated by Runner since the original input, but before the current step - pre_step_items: list[RunItem], - new_response: ModelResponse, - processed_response: ProcessedResponse, - output_schema: AgentOutputSchema | None, - hooks: RunHooks[TContext], - context_wrapper: RunContextWrapper[TContext], - run_config: RunConfig, - ) -> SingleStepResult: - # Make a copy of the generated items - pre_step_items = list(pre_step_items) - - new_step_items: list[RunItem] = [] - new_step_items.extend(processed_response.new_items) - - # First, lets run the tool calls - function tools and computer actions - function_results, computer_results = await asyncio.gather( - cls.execute_function_tool_calls( - agent=agent, - tool_runs=processed_response.functions, - hooks=hooks, - context_wrapper=context_wrapper, - config=run_config, - ), - cls.execute_computer_actions( - agent=agent, - actions=processed_response.computer_actions, - hooks=hooks, - context_wrapper=context_wrapper, - config=run_config, - ), - ) - new_step_items.extend(function_results) - new_step_items.extend(computer_results) - - # Second, check if there are any handoffs - if run_handoffs := processed_response.handoffs: - return await cls.execute_handoffs( - agent=agent, - original_input=original_input, - pre_step_items=pre_step_items, - new_step_items=new_step_items, - new_response=new_response, - run_handoffs=run_handoffs, - hooks=hooks, - context_wrapper=context_wrapper, - run_config=run_config, - ) - - # Now we can check if the model also produced a final output - message_items = [ - item for item in new_step_items if isinstance(item, MessageOutputItem) - ] - - # We'll use the last content output as the final output - potential_final_output_text = ( - ItemHelpers.extract_last_text(message_items[-1].raw_item) - if message_items - else None - ) - - # There are two possibilities that lead to a final output: - # 1. Structured output schema => always leads to a final output - # 2. Plain text output schema => only leads to a final output if there are no tool calls - if ( - output_schema - and not output_schema.is_plain_text() - and potential_final_output_text - ): - final_output = output_schema.validate_json(potential_final_output_text) - return await cls.execute_final_output( - agent=agent, - original_input=original_input, - new_response=new_response, - pre_step_items=pre_step_items, - new_step_items=new_step_items, - final_output=final_output, - hooks=hooks, - context_wrapper=context_wrapper, - ) - elif ( - not output_schema or output_schema.is_plain_text() - ) and not processed_response.has_tools_to_run(): - return await cls.execute_final_output( - agent=agent, - original_input=original_input, - new_response=new_response, - pre_step_items=pre_step_items, - new_step_items=new_step_items, - final_output=potential_final_output_text or "", - hooks=hooks, - context_wrapper=context_wrapper, - ) - else: - # If there's no final output, we can just run again - return SingleStepResult( - original_input=original_input, - model_response=new_response, - pre_step_items=pre_step_items, - new_step_items=new_step_items, - next_step=NextStepRunAgain(), - ) - - @classmethod - def process_model_response( - cls, - *, - agent: Agent[Any], - response: ModelResponse, - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - ) -> ProcessedResponse: - items: list[RunItem] = [] - - run_handoffs = [] - functions = [] - computer_actions = [] - - handoff_map = {handoff.tool_name: handoff for handoff in handoffs} - function_map = { - tool.name: tool for tool in agent.tools if isinstance(tool, FunctionTool) - } - computer_tool = next( - (tool for tool in agent.tools if isinstance(tool, ComputerTool)), None - ) - - for output in response.output: - if isinstance(output, ResponseOutputMessage): - items.append(MessageOutputItem(raw_item=output, agent=agent)) - elif isinstance(output, ResponseFileSearchToolCall): - items.append(ToolCallItem(raw_item=output, agent=agent)) - elif isinstance(output, ResponseFunctionWebSearch): - items.append(ToolCallItem(raw_item=output, agent=agent)) - elif isinstance(output, ResponseReasoningItem): - items.append(ReasoningItem(raw_item=output, agent=agent)) - elif isinstance(output, ResponseComputerToolCall): - items.append(ToolCallItem(raw_item=output, agent=agent)) - if not computer_tool: - _utils.attach_error_to_current_span( - SpanError( - message="Computer tool not found", - data={}, - ) - ) - raise ModelBehaviorError( - "Model produced computer action without a computer tool." - ) - computer_actions.append( - ToolRunComputerAction(tool_call=output, computer_tool=computer_tool) - ) - elif not isinstance(output, ResponseFunctionToolCall): - logger.warning(f"Unexpected output type, ignoring: {type(output)}") - continue - - # At this point we know it's a function tool call - if not isinstance(output, ResponseFunctionToolCall): - continue - - # Handoffs - if output.name in handoff_map: - items.append(HandoffCallItem(raw_item=output, agent=agent)) - handoff = ToolRunHandoff( - tool_call=output, - handoff=handoff_map[output.name], - ) - run_handoffs.append(handoff) - # Regular function tool call - else: - if output.name not in function_map: - _utils.attach_error_to_current_span( - SpanError( - message="Tool not found", - data={"tool_name": output.name}, - ) - ) - raise ModelBehaviorError( - f"Tool {output.name} not found in agent {agent.name}" - ) - items.append(ToolCallItem(raw_item=output, agent=agent)) - functions.append( - ToolRunFunction( - tool_call=output, - function_tool=function_map[output.name], - ) - ) - - return ProcessedResponse( - new_items=items, - handoffs=run_handoffs, - functions=functions, - computer_actions=computer_actions, - ) - - @classmethod - async def execute_function_tool_calls( - cls, - *, - agent: Agent[TContext], - tool_runs: list[ToolRunFunction], - hooks: RunHooks[TContext], - context_wrapper: RunContextWrapper[TContext], - config: RunConfig, - ) -> list[RunItem]: - async def run_single_tool( - func_tool: FunctionTool, tool_call: ResponseFunctionToolCall - ) -> str: - with function_span(func_tool.name) as span_fn: - if config.trace_include_sensitive_data: - span_fn.span_data.input = tool_call.arguments - try: - _, _, result = await asyncio.gather( - hooks.on_tool_start(context_wrapper, agent, func_tool), - ( - agent.hooks.on_tool_start(context_wrapper, agent, func_tool) - if agent.hooks - else _utils.noop_coroutine() - ), - func_tool.on_invoke_tool(context_wrapper, tool_call.arguments), - ) - - await asyncio.gather( - hooks.on_tool_end(context_wrapper, agent, func_tool, result), - ( - agent.hooks.on_tool_end( - context_wrapper, agent, func_tool, result - ) - if agent.hooks - else _utils.noop_coroutine() - ), - ) - except Exception as e: - _utils.attach_error_to_current_span( - SpanError( - message="Error running tool", - data={"tool_name": func_tool.name, "error": str(e)}, - ) - ) - if isinstance(e, AgentsException): - raise e - raise UserError(f"Error running tool {func_tool.name}: {e}") from e - - if config.trace_include_sensitive_data: - span_fn.span_data.output = result - return result - - tasks = [] - for tool_run in tool_runs: - function_tool = tool_run.function_tool - tasks.append(run_single_tool(function_tool, tool_run.tool_call)) - - results = await asyncio.gather(*tasks) - - return [ - ToolCallOutputItem( - output=str(result), - raw_item=ItemHelpers.tool_call_output_item( - tool_run.tool_call, str(result) - ), - agent=agent, - ) - for tool_run, result in zip(tool_runs, results) - ] - - @classmethod - async def execute_computer_actions( - cls, - *, - agent: Agent[TContext], - actions: list[ToolRunComputerAction], - hooks: RunHooks[TContext], - context_wrapper: RunContextWrapper[TContext], - config: RunConfig, - ) -> list[RunItem]: - results: list[RunItem] = [] - # Need to run these serially, because each action can affect the computer state - for action in actions: - results.append( - await ComputerAction.execute( - agent=agent, - action=action, - hooks=hooks, - context_wrapper=context_wrapper, - config=config, - ) - ) - - return results - - @classmethod - async def execute_handoffs( - cls, - *, - agent: Agent[TContext], - original_input: str | list[TResponseInputItem], - pre_step_items: list[RunItem], - new_step_items: list[RunItem], - new_response: ModelResponse, - run_handoffs: list[ToolRunHandoff], - hooks: RunHooks[TContext], - context_wrapper: RunContextWrapper[TContext], - run_config: RunConfig, - ) -> SingleStepResult: - # If there is more than one handoff, add tool responses that reject those handoffs - if len(run_handoffs) > 1: - output_message = "Multiple handoffs detected, ignoring this one." - new_step_items.extend( - [ - ToolCallOutputItem( - output=output_message, - raw_item=ItemHelpers.tool_call_output_item( - handoff.tool_call, output_message - ), - agent=agent, - ) - for handoff in run_handoffs[1:] - ] - ) - - actual_handoff = run_handoffs[0] - with handoff_span(from_agent=agent.name) as span_handoff: - handoff = actual_handoff.handoff - new_agent: Agent[Any] = await handoff.on_invoke_handoff( - context_wrapper, actual_handoff.tool_call.arguments - ) - span_handoff.span_data.to_agent = new_agent.name - - # Append a tool output item for the handoff - new_step_items.append( - HandoffOutputItem( - agent=agent, - raw_item=ItemHelpers.tool_call_output_item( - actual_handoff.tool_call, - handoff.get_transfer_message(new_agent), - ), - source_agent=agent, - target_agent=new_agent, - ) - ) - - # Execute handoff hooks - await asyncio.gather( - hooks.on_handoff( - context=context_wrapper, - from_agent=agent, - to_agent=new_agent, - ), - ( - agent.hooks.on_handoff( - context_wrapper, - agent=new_agent, - source=agent, - ) - if agent.hooks - else _utils.noop_coroutine() - ), - ) - - # If there's an input filter, filter the input for the next agent - input_filter = handoff.input_filter or ( - run_config.handoff_input_filter if run_config else None - ) - if input_filter: - logger.debug("Filtering inputs for handoff") - handoff_input_data = HandoffInputData( - input_history=( - tuple(original_input) - if isinstance(original_input, list) - else original_input - ), - pre_handoff_items=tuple(pre_step_items), - new_items=tuple(new_step_items), - ) - if not callable(input_filter): - _utils.attach_error_to_span( - span_handoff, - SpanError( - message="Invalid input filter", - data={"details": "not callable()"}, - ), - ) - raise UserError(f"Invalid input filter: {input_filter}") - filtered = input_filter(handoff_input_data) - if not isinstance(filtered, HandoffInputData): - _utils.attach_error_to_span( - span_handoff, - SpanError( - message="Invalid input filter result", - data={"details": "not a HandoffInputData"}, - ), - ) - raise UserError(f"Invalid input filter result: {filtered}") - - original_input = ( - filtered.input_history - if isinstance(filtered.input_history, str) - else list(filtered.input_history) - ) - pre_step_items = list(filtered.pre_handoff_items) - new_step_items = list(filtered.new_items) - - return SingleStepResult( - original_input=original_input, - model_response=new_response, - pre_step_items=pre_step_items, - new_step_items=new_step_items, - next_step=NextStepHandoff(new_agent), - ) - - @classmethod - async def execute_final_output( - cls, - *, - agent: Agent[TContext], - original_input: str | list[TResponseInputItem], - new_response: ModelResponse, - pre_step_items: list[RunItem], - new_step_items: list[RunItem], - final_output: Any, - hooks: RunHooks[TContext], - context_wrapper: RunContextWrapper[TContext], - ) -> SingleStepResult: - # Run the on_end hooks - await cls.run_final_output_hooks(agent, hooks, context_wrapper, final_output) - - return SingleStepResult( - original_input=original_input, - model_response=new_response, - pre_step_items=pre_step_items, - new_step_items=new_step_items, - next_step=NextStepFinalOutput(final_output), - ) - - @classmethod - async def run_final_output_hooks( - cls, - agent: Agent[TContext], - hooks: RunHooks[TContext], - context_wrapper: RunContextWrapper[TContext], - final_output: Any, - ): - await asyncio.gather( - hooks.on_agent_end(context_wrapper, agent, final_output), - ( - agent.hooks.on_end(context_wrapper, agent, final_output) - if agent.hooks - else _utils.noop_coroutine() - ), - ) - - @classmethod - async def run_single_input_guardrail( - cls, - agent: Agent[Any], - guardrail: InputGuardrail[TContext], - input: str | list[TResponseInputItem], - context: RunContextWrapper[TContext], - ) -> InputGuardrailResult: - with guardrail_span(guardrail.get_name()) as span_guardrail: - result = await guardrail.run(agent, input, context) - span_guardrail.span_data.triggered = result.output.tripwire_triggered - return result - - @classmethod - async def run_single_output_guardrail( - cls, - guardrail: OutputGuardrail[TContext], - agent: Agent[Any], - agent_output: Any, - context: RunContextWrapper[TContext], - ) -> OutputGuardrailResult: - with guardrail_span(guardrail.get_name()) as span_guardrail: - result = await guardrail.run( - agent=agent, agent_output=agent_output, context=context - ) - span_guardrail.span_data.triggered = result.output.tripwire_triggered - return result - - @classmethod - def stream_step_result_to_queue( - cls, - step_result: SingleStepResult, - queue: asyncio.Queue[StreamEvent | QueueCompleteSentinel], - ): - for item in step_result.new_step_items: - if isinstance(item, MessageOutputItem): - event = RunItemStreamEvent(item=item, name="message_output_created") - elif isinstance(item, HandoffCallItem): - event = RunItemStreamEvent(item=item, name="handoff_requested") - elif isinstance(item, HandoffOutputItem): - event = RunItemStreamEvent(item=item, name="handoff_occured") - elif isinstance(item, ToolCallItem): - event = RunItemStreamEvent(item=item, name="tool_called") - elif isinstance(item, ToolCallOutputItem): - event = RunItemStreamEvent(item=item, name="tool_output") - elif isinstance(item, ReasoningItem): - event = RunItemStreamEvent(item=item, name="reasoning_item_created") - else: - logger.warning(f"Unexpected item type: {type(item)}") - event = None - - if event: - queue.put_nowait(event) - - -class TraceCtxManager: - """Creates a trace only if there is no current trace, and manages the trace lifecycle.""" - - def __init__( - self, - workflow_name: str, - trace_id: str | None, - group_id: str | None, - metadata: dict[str, Any] | None, - disabled: bool, - ): - self.trace: Trace | None = None - self.workflow_name = workflow_name - self.trace_id = trace_id - self.group_id = group_id - self.metadata = metadata - self.disabled = disabled - - def __enter__(self) -> TraceCtxManager: - current_trace = get_current_trace() - if not current_trace: - self.trace = trace( - workflow_name=self.workflow_name, - trace_id=self.trace_id, - group_id=self.group_id, - metadata=self.metadata, - disabled=self.disabled, - ) - self.trace.start(mark_as_current=True) - - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - if self.trace: - self.trace.finish(reset_current=True) - - -class ComputerAction: - @classmethod - async def execute( - cls, - *, - agent: Agent[TContext], - action: ToolRunComputerAction, - hooks: RunHooks[TContext], - context_wrapper: RunContextWrapper[TContext], - config: RunConfig, - ) -> RunItem: - output_func = ( - cls._get_screenshot_async(action.computer_tool.computer, action.tool_call) - if isinstance(action.computer_tool.computer, AsyncComputer) - else cls._get_screenshot_sync( - action.computer_tool.computer, action.tool_call - ) - ) - - _, _, output = await asyncio.gather( - hooks.on_tool_start(context_wrapper, agent, action.computer_tool), - ( - agent.hooks.on_tool_start(context_wrapper, agent, action.computer_tool) - if agent.hooks - else _utils.noop_coroutine() - ), - output_func, - ) - - await asyncio.gather( - hooks.on_tool_end(context_wrapper, agent, action.computer_tool, output), - ( - agent.hooks.on_tool_end( - context_wrapper, agent, action.computer_tool, output - ) - if agent.hooks - else _utils.noop_coroutine() - ), - ) - - # TODO: don't send a screenshot every single time, use references - image_url = f"data:image/png;base64,{output}" - return ToolCallOutputItem( - agent=agent, - output=image_url, - raw_item=ComputerCallOutput( - call_id=action.tool_call.call_id, - output={ - "type": "computer_screenshot", - "image_url": image_url, - }, - type="computer_call_output", - ), - ) - - @classmethod - async def _get_screenshot_sync( - cls, - computer: Computer, - tool_call: ResponseComputerToolCall, - ) -> str: - action = tool_call.action - if isinstance(action, ActionClick): - computer.click(action.x, action.y, action.button) - elif isinstance(action, ActionDoubleClick): - computer.double_click(action.x, action.y) - elif isinstance(action, ActionDrag): - computer.drag([(p.x, p.y) for p in action.path]) - elif isinstance(action, ActionKeypress): - computer.keypress(action.keys) - elif isinstance(action, ActionMove): - computer.move(action.x, action.y) - elif isinstance(action, ActionScreenshot): - computer.screenshot() - elif isinstance(action, ActionScroll): - computer.scroll(action.x, action.y, action.scroll_x, action.scroll_y) - elif isinstance(action, ActionType): - computer.type(action.text) - elif isinstance(action, ActionWait): - computer.wait() - - return computer.screenshot() - - @classmethod - async def _get_screenshot_async( - cls, - computer: AsyncComputer, - tool_call: ResponseComputerToolCall, - ) -> str: - action = tool_call.action - if isinstance(action, ActionClick): - await computer.click(action.x, action.y, action.button) - elif isinstance(action, ActionDoubleClick): - await computer.double_click(action.x, action.y) - elif isinstance(action, ActionDrag): - await computer.drag([(p.x, p.y) for p in action.path]) - elif isinstance(action, ActionKeypress): - await computer.keypress(action.keys) - elif isinstance(action, ActionMove): - await computer.move(action.x, action.y) - elif isinstance(action, ActionScreenshot): - await computer.screenshot() - elif isinstance(action, ActionScroll): - await computer.scroll(action.x, action.y, action.scroll_x, action.scroll_y) - elif isinstance(action, ActionType): - await computer.type(action.text) - elif isinstance(action, ActionWait): - await computer.wait() - - return await computer.screenshot() diff --git a/pkg/hanzo-agent/src/agents/_utils.py b/pkg/hanzo-agent/src/agents/_utils.py deleted file mode 100644 index 557ba1362..000000000 --- a/pkg/hanzo-agent/src/agents/_utils.py +++ /dev/null @@ -1,63 +0,0 @@ -from __future__ import annotations - -import re -from collections.abc import Awaitable -from typing import Any, Literal, Union - -from pydantic import TypeAdapter, ValidationError -from typing_extensions import TypeVar - -from .exceptions import ModelBehaviorError -from .logger import logger -from .tracing import Span, SpanError, get_current_span - -T = TypeVar("T") - -MaybeAwaitable = Union[Awaitable[T], T] - - -def transform_string_function_style(name: str) -> str: - # Replace spaces with underscores - name = name.replace(" ", "_") - - # Replace non-alphanumeric characters with underscores - name = re.sub(r"[^a-zA-Z0-9]", "_", name) - - return name.lower() - - -def validate_json(json_str: str, type_adapter: TypeAdapter[T], partial: bool) -> T: - partial_setting: bool | Literal["off", "on", "trailing-strings"] = ( - "trailing-strings" if partial else False - ) - try: - validated = type_adapter.validate_json( - json_str, experimental_allow_partial=partial_setting - ) - return validated - except ValidationError as e: - attach_error_to_current_span( - SpanError( - message="Invalid JSON provided", - data={}, - ) - ) - raise ModelBehaviorError( - f"Invalid JSON when parsing {json_str} for {type_adapter}; {e}" - ) from e - - -def attach_error_to_span(span: Span[Any], error: SpanError) -> None: - span.set_error(error) - - -def attach_error_to_current_span(error: SpanError) -> None: - span = get_current_span() - if span: - attach_error_to_span(span, error) - else: - logger.warning(f"No span to add error {error} to") - - -async def noop_coroutine() -> None: - pass diff --git a/pkg/hanzo-agent/src/agents/agent.py b/pkg/hanzo-agent/src/agents/agent.py deleted file mode 100644 index b9ded416d..000000000 --- a/pkg/hanzo-agent/src/agents/agent.py +++ /dev/null @@ -1,173 +0,0 @@ -from __future__ import annotations - -import dataclasses -import inspect -from collections.abc import Awaitable -from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Any, Callable, Generic, cast - -from . import _utils -from ._utils import MaybeAwaitable -from .guardrail import InputGuardrail, OutputGuardrail -from .handoffs import Handoff -from .items import ItemHelpers -from .logger import logger -from .model_settings import ModelSettings -from .models.interface import Model -from .reflexion import ReflexionEngine -from .run_context import RunContextWrapper, TContext -from .tool import Tool, function_tool - -if TYPE_CHECKING: - from .lifecycle import AgentHooks - from .result import RunResult - - -@dataclass -class Agent(Generic[TContext]): - """An agent is an AI model configured with instructions, tools, guardrails, handoffs and more. - - We strongly recommend passing `instructions`, which is the "system prompt" for the agent. In - addition, you can pass `description`, which is a human-readable description of the agent, used - when the agent is used inside tools/handoffs. - - Agents are generic on the context type. The context is a (mutable) object you create. It is - passed to tool functions, handoffs, guardrails, etc. - """ - - name: str - """The name of the agent.""" - - instructions: ( - str - | Callable[ - [RunContextWrapper[TContext], Agent[TContext]], - MaybeAwaitable[str], - ] - | None - ) = None - """The instructions for the agent. Will be used as the "system prompt" when this agent is - invoked. Describes what the agent should do, and how it responds. - - Can either be a string, or a function that dynamically generates instructions for the agent. If - you provide a function, it will be called with the context and the agent instance. It must - return a string. - """ - - handoff_description: str | None = None - """A description of the agent. This is used when the agent is used as a handoff, so that an - LLM knows what it does and when to invoke it. - """ - - handoffs: list[Agent[Any] | Handoff[TContext]] = field(default_factory=list) - """Handoffs are sub-agents that the agent can delegate to. You can provide a list of handoffs, - and the agent can choose to delegate to them if relevant. Allows for separation of concerns and - modularity. - """ - - model: str | Model | None = None - """The model implementation to use when invoking the LLM. - - By default, if not set, the agent will use the default model configured in - `model_settings.DEFAULT_MODEL`. - """ - - model_settings: ModelSettings = field(default_factory=ModelSettings) - """Configures model-specific tuning parameters (e.g. temperature, top_p). - """ - - handoffs: list[str | Agent[TContext] | Handoff] = field(default_factory=list) - """A list of agents that this agent can handoff to.""" - - reflexion: ReflexionEngine | None = None - """The reflexion engine for self-correction.""" - - tools: list[Tool | Callable[..., Any] | Callable[..., Awaitable[Any]]] = field( - default_factory=list - ) - """A list of tools that the agent can use.""" - - input_guardrails: list[InputGuardrail[TContext]] = field(default_factory=list) - """A list of checks that run in parallel to the agent's execution, before generating a - response. Runs only if the agent is the first agent in the chain. - """ - - output_guardrails: list[OutputGuardrail[TContext]] = field(default_factory=list) - """A list of checks that run on the final output of the agent, after generating a response. - Runs only if the agent produces a final output. - """ - - output_type: type[Any] | None = None - """The type of the output object. If not provided, the output will be `str`.""" - - hooks: AgentHooks[TContext] | None = None - """A class that receives callbacks on various lifecycle events for this agent. - """ - - def clone(self, **kwargs: Any) -> Agent[TContext]: - """Make a copy of the agent, with the given arguments changed. For example, you could do: - ``` - new_agent = agent.clone(instructions="New instructions") - ``` - """ - return dataclasses.replace(self, **kwargs) - - def as_tool( - self, - tool_name: str | None, - tool_description: str | None, - custom_output_extractor: Callable[[RunResult], Awaitable[str]] | None = None, - ) -> Tool: - """Transform this agent into a tool, callable by other agents. - - This is different from handoffs in two ways: - 1. In handoffs, the new agent receives the conversation history. In this tool, the new agent - receives generated input. - 2. In handoffs, the new agent takes over the conversation. In this tool, the new agent is - called as a tool, and the conversation is continued by the original agent. - - Args: - tool_name: The name of the tool. If not provided, the agent's name will be used. - tool_description: The description of the tool, which should indicate what it does and - when to use it. - custom_output_extractor: A function that extracts the output from the agent. If not - provided, the last message from the agent will be used. - """ - - @function_tool( - name_override=tool_name - or _utils.transform_string_function_style(self.name), - description_override=tool_description or "", - ) - async def run_agent(context: RunContextWrapper, input: str) -> str: - from .run import Runner - - output = await Runner.run( - starting_agent=self, - input=input, - context=context.context, - ) - if custom_output_extractor: - return await custom_output_extractor(output) - - return ItemHelpers.text_message_outputs(output.new_items) - - return run_agent - - async def get_system_prompt( - self, run_context: RunContextWrapper[TContext] - ) -> str | None: - """Get the system prompt for the agent.""" - if isinstance(self.instructions, str): - return self.instructions - elif callable(self.instructions): - if inspect.iscoroutinefunction(self.instructions): - return await cast(Awaitable[str], self.instructions(run_context, self)) - else: - return cast(str, self.instructions(run_context, self)) - elif self.instructions is not None: - logger.error( - f"Instructions must be a string or a function, got {self.instructions}" - ) - - return None diff --git a/pkg/hanzo-agent/src/agents/agent_output.py b/pkg/hanzo-agent/src/agents/agent_output.py deleted file mode 100644 index f8dddf0db..000000000 --- a/pkg/hanzo-agent/src/agents/agent_output.py +++ /dev/null @@ -1,146 +0,0 @@ -from dataclasses import dataclass -from typing import Any - -from pydantic import BaseModel, TypeAdapter -from typing_extensions import TypedDict, get_args, get_origin - -from . import _utils -from .exceptions import ModelBehaviorError, UserError -from .strict_schema import ensure_strict_json_schema -from .tracing import SpanError - -_WRAPPER_DICT_KEY = "response" - - -@dataclass(init=False) -class AgentOutputSchema: - """An object that captures the JSON schema of the output, as well as validating/parsing JSON - produced by the LLM into the output type. - """ - - output_type: type[Any] - """The type of the output.""" - - _type_adapter: TypeAdapter[Any] - """A type adapter that wraps the output type, so that we can validate JSON.""" - - _is_wrapped: bool - """Whether the output type is wrapped in a dictionary. This is generally done if the base - output type cannot be represented as a JSON Schema object. - """ - - _output_schema: dict[str, Any] - """The JSON schema of the output.""" - - strict_json_schema: bool - """Whether the JSON schema is in strict mode. We **strongly** recommend setting this to True, - as it increases the likelihood of correct JSON input. - """ - - def __init__(self, output_type: type[Any], strict_json_schema: bool = True): - """ - Args: - output_type: The type of the output. - strict_json_schema: Whether the JSON schema is in strict mode. We **strongly** recommend - setting this to True, as it increases the likelihood of correct JSON input. - """ - self.output_type = output_type - self.strict_json_schema = strict_json_schema - - if output_type is None or output_type is str: - self._is_wrapped = False - self._type_adapter = TypeAdapter(output_type) - self._output_schema = self._type_adapter.json_schema() - return - - # We should wrap for things that are not plain text, and for things that would definitely - # not be a JSON Schema object. - self._is_wrapped = not _is_subclass_of_base_model_or_dict(output_type) - - if self._is_wrapped: - OutputType = TypedDict( - "OutputType", - { - _WRAPPER_DICT_KEY: output_type, # type: ignore - }, - ) - self._type_adapter = TypeAdapter(OutputType) - self._output_schema = self._type_adapter.json_schema() - else: - self._type_adapter = TypeAdapter(output_type) - self._output_schema = self._type_adapter.json_schema() - - if self.strict_json_schema: - self._output_schema = ensure_strict_json_schema(self._output_schema) - - def is_plain_text(self) -> bool: - """Whether the output type is plain text (versus a JSON object).""" - return self.output_type is None or self.output_type is str - - def json_schema(self) -> dict[str, Any]: - """The JSON schema of the output type.""" - if self.is_plain_text(): - raise UserError("Output type is plain text, so no JSON schema is available") - return self._output_schema - - def validate_json(self, json_str: str, partial: bool = False) -> Any: - """Validate a JSON string against the output type. Returns the validated object, or raises - a `ModelBehaviorError` if the JSON is invalid. - """ - validated = _utils.validate_json(json_str, self._type_adapter, partial) - if self._is_wrapped: - if not isinstance(validated, dict): - _utils.attach_error_to_current_span( - SpanError( - message="Invalid JSON", - data={"details": f"Expected a dict, got {type(validated)}"}, - ) - ) - raise ModelBehaviorError( - f"Expected a dict, got {type(validated)} for JSON: {json_str}" - ) - - if _WRAPPER_DICT_KEY not in validated: - _utils.attach_error_to_current_span( - SpanError( - message="Invalid JSON", - data={ - "details": f"Could not find key {_WRAPPER_DICT_KEY} in JSON" - }, - ) - ) - raise ModelBehaviorError( - f"Could not find key {_WRAPPER_DICT_KEY} in JSON: {json_str}" - ) - return validated[_WRAPPER_DICT_KEY] - return validated - - def output_type_name(self) -> str: - """The name of the output type.""" - return _type_to_str(self.output_type) - - -def _is_subclass_of_base_model_or_dict(t: Any) -> bool: - if not isinstance(t, type): - return False - - # If it's a generic alias, 'origin' will be the actual type, e.g. 'list' - origin = get_origin(t) - - allowed_types = (BaseModel, dict) - # If it's a generic alias e.g. list[str], then we should check the origin type i.e. list - return issubclass(origin or t, allowed_types) - - -def _type_to_str(t: type[Any]) -> str: - origin = get_origin(t) - args = get_args(t) - - if origin is None: - # It's a simple type like `str`, `int`, etc. - return t.__name__ - elif args: - args_str = ", ".join(_type_to_str(arg) for arg in args) - return f"{origin.__name__}[{args_str}]" - else: - return str(t) diff --git a/pkg/hanzo-agent/src/agents/computer.py b/pkg/hanzo-agent/src/agents/computer.py deleted file mode 100644 index 1b9224d59..000000000 --- a/pkg/hanzo-agent/src/agents/computer.py +++ /dev/null @@ -1,107 +0,0 @@ -import abc -from typing import Literal - -Environment = Literal["mac", "windows", "ubuntu", "browser"] -Button = Literal["left", "right", "wheel", "back", "forward"] - - -class Computer(abc.ABC): - """A computer implemented with sync operations. The Computer interface abstracts the - operations needed to control a computer or browser.""" - - @property - @abc.abstractmethod - def environment(self) -> Environment: - pass - - @property - @abc.abstractmethod - def dimensions(self) -> tuple[int, int]: - pass - - @abc.abstractmethod - def screenshot(self) -> str: - pass - - @abc.abstractmethod - def click(self, x: int, y: int, button: Button) -> None: - pass - - @abc.abstractmethod - def double_click(self, x: int, y: int) -> None: - pass - - @abc.abstractmethod - def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: - pass - - @abc.abstractmethod - def type(self, text: str) -> None: - pass - - @abc.abstractmethod - def wait(self) -> None: - pass - - @abc.abstractmethod - def move(self, x: int, y: int) -> None: - pass - - @abc.abstractmethod - def keypress(self, keys: list[str]) -> None: - pass - - @abc.abstractmethod - def drag(self, path: list[tuple[int, int]]) -> None: - pass - - -class AsyncComputer(abc.ABC): - """A computer implemented with async operations. The Computer interface abstracts the - operations needed to control a computer or browser.""" - - @property - @abc.abstractmethod - def environment(self) -> Environment: - pass - - @property - @abc.abstractmethod - def dimensions(self) -> tuple[int, int]: - pass - - @abc.abstractmethod - async def screenshot(self) -> str: - pass - - @abc.abstractmethod - async def click(self, x: int, y: int, button: Button) -> None: - pass - - @abc.abstractmethod - async def double_click(self, x: int, y: int) -> None: - pass - - @abc.abstractmethod - async def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: - pass - - @abc.abstractmethod - async def type(self, text: str) -> None: - pass - - @abc.abstractmethod - async def wait(self) -> None: - pass - - @abc.abstractmethod - async def move(self, x: int, y: int) -> None: - pass - - @abc.abstractmethod - async def keypress(self, keys: list[str]) -> None: - pass - - @abc.abstractmethod - async def drag(self, path: list[tuple[int, int]]) -> None: - pass diff --git a/pkg/hanzo-agent/src/agents/exceptions.py b/pkg/hanzo-agent/src/agents/exceptions.py deleted file mode 100644 index 49c95543c..000000000 --- a/pkg/hanzo-agent/src/agents/exceptions.py +++ /dev/null @@ -1,63 +0,0 @@ -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from .guardrail import InputGuardrailResult, OutputGuardrailResult - - -class AgentsException(Exception): - """Base class for all exceptions in the Agent SDK.""" - - -class MaxTurnsExceeded(AgentsException): - """Exception raised when the maximum number of turns is exceeded.""" - - message: str - - def __init__(self, message: str): - self.message = message - - -class ModelBehaviorError(AgentsException): - """Exception raised when the model does something unexpected, e.g. calling a tool that doesn't - exist, or providing malformed JSON. - """ - - message: str - - def __init__(self, message: str): - self.message = message - - -class UserError(AgentsException): - """Exception raised when the user makes an error using the SDK.""" - - message: str - - def __init__(self, message: str): - self.message = message - - -class InputGuardrailTripwireTriggered(AgentsException): - """Exception raised when a guardrail tripwire is triggered.""" - - guardrail_result: "InputGuardrailResult" - """The result data of the guardrail that was triggered.""" - - def __init__(self, guardrail_result: "InputGuardrailResult"): - self.guardrail_result = guardrail_result - super().__init__( - f"Guardrail {guardrail_result.guardrail.__class__.__name__} triggered tripwire" - ) - - -class OutputGuardrailTripwireTriggered(AgentsException): - """Exception raised when a guardrail tripwire is triggered.""" - - guardrail_result: "OutputGuardrailResult" - """The result data of the guardrail that was triggered.""" - - def __init__(self, guardrail_result: "OutputGuardrailResult"): - self.guardrail_result = guardrail_result - super().__init__( - f"Guardrail {guardrail_result.guardrail.__class__.__name__} triggered tripwire" - ) diff --git a/pkg/hanzo-agent/src/agents/extensions/__init__.py b/pkg/hanzo-agent/src/agents/extensions/__init__.py deleted file mode 100644 index 62be734c9..000000000 --- a/pkg/hanzo-agent/src/agents/extensions/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Hanzo Agent SDK Extensions. - -This package provides optional extensions for the agent SDK: -- web3: Web3 integration with wallet and transaction support -- tee: Trusted Execution Environment support -- marketplace: Decentralized agent service marketplace -- cli: Command-line interface integration -""" - -__version__ = "0.1.0" - -__all__ = [] diff --git a/pkg/hanzo-agent/src/agents/extensions/cli/__init__.py b/pkg/hanzo-agent/src/agents/extensions/cli/__init__.py deleted file mode 100644 index 393f136c0..000000000 --- a/pkg/hanzo-agent/src/agents/extensions/cli/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Command-line interface integration for Hanzo agents. - -Provides CLI-enabled agents for interactive command-line operations. -""" - -from .cli_agent import CLIAgent, CLIConfig - -__all__ = [ - "CLIAgent", - "CLIConfig", -] diff --git a/pkg/hanzo-agent/src/agents/extensions/cli/cli_agent.py b/pkg/hanzo-agent/src/agents/extensions/cli/cli_agent.py deleted file mode 100644 index df31d61a2..000000000 --- a/pkg/hanzo-agent/src/agents/extensions/cli/cli_agent.py +++ /dev/null @@ -1,255 +0,0 @@ -"""CLI-based agent implementation for external tools.""" - -import os -import json -import asyncio -import tempfile -from typing import Any, Dict, List, Optional - -from hanzo_agents.core.agent import Agent, ToolCall, InferenceResult -from hanzo_agents.core.state import State -from hanzo_agents.core.history import History - - -class CLIAgent(Agent): - """Agent that uses external CLI tools. - - Supports tools like: - - claude (Claude Code) - - openai (Codex) - - gemini (Google Gemini) - - grok (xAI Grok) - - cursor - - aider - - etc. - """ - - cli_command: str # Base command to run - cli_args: List[str] = [] # Default arguments - - def __init__( - self, - cli_command: Optional[str] = None, - cli_args: Optional[List[str]] = None, - working_dir: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - **kwargs, - ): - """Initialize CLI agent. - - Args: - cli_command: Override default CLI command - cli_args: Override default arguments - working_dir: Working directory for CLI - env: Environment variables - """ - super().__init__(**kwargs) - - if cli_command: - self.cli_command = cli_command - if cli_args is not None: - self.cli_args = cli_args - - self.working_dir = working_dir or os.getcwd() - self.env = os.environ.copy() - if env: - self.env.update(env) - - async def run( - self, state: State, history: History, network: "Network" - ) -> InferenceResult: - """Execute CLI tool with current context.""" - # Build prompt from history - prompt = self._build_prompt(state, history) - - # Execute CLI - result = await self._execute_cli(prompt) - - # Parse response - return self._parse_response(result) - - def _build_prompt(self, state: State, history: History) -> str: - """Build prompt from state and history.""" - # Include state context - prompt_parts = [ - f"Current state: {json.dumps(state.to_dict(), indent=2)}", - "", - "Conversation history:", - ] - - # Add recent history - for entry in history[-10:]: # Last 10 entries - if entry.role == "user": - prompt_parts.append(f"User: {entry.content}") - elif entry.role == "assistant" and entry.agent: - prompt_parts.append(f"{entry.agent}: {entry.content}") - - # Add current task - prompt_parts.extend( - ["", f"As {self.name}, {self.description}", "What should we do next?"] - ) - - return "\n".join(prompt_parts) - - async def _execute_cli(self, prompt: str) -> Dict[str, Any]: - """Execute the CLI tool.""" - # Write prompt to temp file - with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f: - f.write(prompt) - prompt_file = f.name - - try: - # Build command - cmd = [self.cli_command] + self.cli_args - - # Some tools accept prompt via stdin, others via file - if any(arg in ["-", "--stdin"] for arg in self.cli_args): - # Use stdin - process = await asyncio.create_subprocess_exec( - *cmd, - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - cwd=self.working_dir, - env=self.env, - ) - stdout, stderr = await process.communicate(prompt.encode()) - else: - # Use file argument - cmd.append(prompt_file) - process = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - cwd=self.working_dir, - env=self.env, - ) - stdout, stderr = await process.communicate() - - return { - "stdout": stdout.decode() if stdout else "", - "stderr": stderr.decode() if stderr else "", - "returncode": process.returncode, - } - - finally: - # Clean up temp file - try: - os.unlink(prompt_file) - except Exception: - pass - - def _parse_response(self, result: Dict[str, Any]) -> InferenceResult: - """Parse CLI output into inference result.""" - if result["returncode"] != 0: - # Handle error - return InferenceResult( - agent=self.name, - content=f"Error: {result['stderr']}", - metadata={"cli_error": True}, - ) - - # Parse stdout for response - output = result["stdout"] - - # Try to detect tool calls in output - tool_calls = self._extract_tool_calls(output) - - return InferenceResult( - agent=self.name, - content=output, - tool_calls=tool_calls, - metadata={"cli_output": True}, - ) - - def _extract_tool_calls(self, output: str) -> List[ToolCall]: - """Extract tool calls from CLI output. - - Look for patterns like: - - TOOL: tool_name(arg1="value1", arg2="value2") - - @tool tool_name {"arg1": "value1"} - """ - tool_calls = [] - - # Simple pattern matching (extend as needed) - lines = output.split("\n") - for line in lines: - if line.startswith("TOOL:") or line.startswith("@tool"): - # Parse tool call - # This is simplified - real implementation would be more robust - parts = line.split(None, 2) - if len(parts) >= 3: - tool_name = parts[1] - try: - # Try to parse as JSON - args_str = parts[2] - if args_str.startswith("{"): - arguments = json.loads(args_str) - else: - # Simple key=value parsing - arguments = {} - # ... parse key=value pairs - - tool_calls.append(ToolCall(tool=tool_name, arguments=arguments)) - except Exception: - pass - - return tool_calls - - -# Concrete CLI agent implementations - - -class ClaudeCodeAgent(CLIAgent): - """Claude Code CLI agent.""" - - name = "claude_code" - description = "Claude Code AI assistant" - cli_command = "claude" - cli_args = ["--no-interactive"] - model = "model://anthropic/claude-3-5-sonnet-20241022" - - -class OpenAICodexAgent(CLIAgent): - """OpenAI Codex/ChatGPT CLI agent.""" - - name = "openai_codex" - description = "OpenAI GPT code assistant" - cli_command = "openai" - cli_args = ["chat", "--model", "gpt-4"] - - -class GeminiAgent(CLIAgent): - """Google Gemini CLI agent.""" - - name = "gemini" - description = "Google Gemini AI assistant" - cli_command = "gemini" - cli_args = ["--format", "json"] - - -class GrokAgent(CLIAgent): - """xAI Grok CLI agent.""" - - name = "grok" - description = "xAI Grok assistant" - cli_command = "grok" - cli_args = ["--mode", "code"] - - -class CursorAgent(CLIAgent): - """Cursor AI editor agent.""" - - name = "cursor" - description = "Cursor AI-powered editor" - cli_command = "cursor" - cli_args = ["--headless"] - - -class AiderAgent(CLIAgent): - """Aider coding assistant agent.""" - - name = "aider" - description = "Aider AI pair programmer" - cli_command = "aider" - cli_args = ["--no-pretty", "--yes"] diff --git a/pkg/hanzo-agent/src/agents/extensions/handoff_filters.py b/pkg/hanzo-agent/src/agents/extensions/handoff_filters.py deleted file mode 100644 index 66f249133..000000000 --- a/pkg/hanzo-agent/src/agents/extensions/handoff_filters.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import annotations - -from ..handoffs import HandoffInputData -from ..items import ( - HandoffCallItem, - HandoffOutputItem, - RunItem, - ToolCallItem, - ToolCallOutputItem, - TResponseInputItem, -) - -"""Contains common handoff input filters, for convenience. """ - - -def remove_all_tools(handoff_input_data: HandoffInputData) -> HandoffInputData: - """Filters out all tool items: file search, web search and function calls+output.""" - - history = handoff_input_data.input_history - new_items = handoff_input_data.new_items - - filtered_history = ( - _remove_tool_types_from_input(history) - if isinstance(history, tuple) - else history - ) - filtered_pre_handoff_items = _remove_tools_from_items( - handoff_input_data.pre_handoff_items - ) - filtered_new_items = _remove_tools_from_items(new_items) - - return HandoffInputData( - input_history=filtered_history, - pre_handoff_items=filtered_pre_handoff_items, - new_items=filtered_new_items, - ) - - -def _remove_tools_from_items(items: tuple[RunItem, ...]) -> tuple[RunItem, ...]: - filtered_items = [] - for item in items: - if ( - isinstance(item, HandoffCallItem) - or isinstance(item, HandoffOutputItem) - or isinstance(item, ToolCallItem) - or isinstance(item, ToolCallOutputItem) - ): - continue - filtered_items.append(item) - return tuple(filtered_items) - - -def _remove_tool_types_from_input( - items: tuple[TResponseInputItem, ...], -) -> tuple[TResponseInputItem, ...]: - tool_types = [ - "function_call", - "function_call_output", - "computer_call", - "computer_call_output", - "file_search_call", - "web_search_call", - ] - - filtered_items: list[TResponseInputItem] = [] - for item in items: - itype = item.get("type") - if itype in tool_types: - continue - filtered_items.append(item) - return tuple(filtered_items) diff --git a/pkg/hanzo-agent/src/agents/extensions/handoff_prompt.py b/pkg/hanzo-agent/src/agents/extensions/handoff_prompt.py deleted file mode 100644 index 4894940b8..000000000 --- a/pkg/hanzo-agent/src/agents/extensions/handoff_prompt.py +++ /dev/null @@ -1,19 +0,0 @@ -# A recommended prompt prefix for agents that use handoffs. We recommend including this or -# similar instructions in any agents that use handoffs. -RECOMMENDED_PROMPT_PREFIX = ( - "# System context\n" - "You are part of a multi-agent system called the Agent SDK, designed to make agent " - "coordination and execution easy. Agents uses two primary abstraction: **Agents** and " - "**Handoffs**. An agent encompasses instructions and tools and can hand off a " - "conversation to another agent when appropriate. " - "Handoffs are achieved by calling a handoff function, generally named " - "`transfer_to_`. Transfers between agents are handled seamlessly in the background;" - " do not mention or draw attention to these transfers in your conversation with the user.\n" -) - - -def prompt_with_handoff_instructions(prompt: str) -> str: - """ - Add recommended instructions to the prompt for agents that use handoffs. - """ - return f"{RECOMMENDED_PROMPT_PREFIX}\n\n{prompt}" diff --git a/pkg/hanzo-agent/src/agents/extensions/marketplace/__init__.py b/pkg/hanzo-agent/src/agents/extensions/marketplace/__init__.py deleted file mode 100644 index 1915362a1..000000000 --- a/pkg/hanzo-agent/src/agents/extensions/marketplace/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Decentralized marketplace for agent services. - -Provides service discovery, matching, and economic primitives for agent ecosystems. -""" - -from .marketplace import ( - ServiceType, - ServiceOffer, - ServiceRequest, - ServiceMatch, - MarketplaceConfig, - AgentMarketplace, - create_marketplace_tools, -) - -__all__ = [ - "ServiceType", - "ServiceOffer", - "ServiceRequest", - "ServiceMatch", - "MarketplaceConfig", - "AgentMarketplace", - "create_marketplace_tools", -] diff --git a/pkg/hanzo-agent/src/agents/extensions/marketplace/marketplace.py b/pkg/hanzo-agent/src/agents/extensions/marketplace/marketplace.py deleted file mode 100644 index e600fa638..000000000 --- a/pkg/hanzo-agent/src/agents/extensions/marketplace/marketplace.py +++ /dev/null @@ -1,418 +0,0 @@ -"""Decentralized marketplace for agent services and resources.""" - -import time -import uuid -from enum import Enum -from typing import Any, Dict, List, Optional -from dataclasses import field, dataclass - -from .web3_agent import Web3Agent - - -class ServiceType(Enum): - """Types of services agents can offer.""" - - DATA = "data" - COMPUTE = "compute" - INFERENCE = "inference" - ANALYSIS = "analysis" - CODING = "coding" - RESEARCH = "research" - CUSTOM = "custom" - - -@dataclass -class ServiceOffer: - """Service offered by an agent.""" - - id: str - agent_address: str - agent_name: str - service_type: ServiceType - description: str - price_eth: float - min_reputation: float = 0.0 - max_duration_hours: float = 24.0 - requires_tee: bool = False - metadata: Dict[str, Any] = field(default_factory=dict) - created_at: float = field(default_factory=time.time) - expires_at: Optional[float] = None - - def is_expired(self) -> bool: - """Check if offer has expired.""" - if self.expires_at is None: - return False - return time.time() > self.expires_at - - def matches_request(self, request: "ServiceRequest") -> bool: - """Check if offer matches a request.""" - if self.is_expired(): - return False - - # Check service type - if request.service_type != ServiceType.CUSTOM: - if self.service_type != request.service_type: - return False - - # Check price - if self.price_eth > request.max_price_eth: - return False - - # Check duration - if request.duration_hours > self.max_duration_hours: - return False - - # Check TEE requirement - if request.requires_tee and not self.requires_tee: - return False - - return True - - -@dataclass -class ServiceRequest: - """Request for a service.""" - - id: str - requester_address: str - requester_name: str - service_type: ServiceType - description: str - max_price_eth: float - duration_hours: float = 1.0 - min_reputation: float = 0.0 - requires_tee: bool = False - metadata: Dict[str, Any] = field(default_factory=dict) - created_at: float = field(default_factory=time.time) - expires_at: Optional[float] = None - - def is_expired(self) -> bool: - """Check if request has expired.""" - if self.expires_at is None: - return False - return time.time() > self.expires_at - - -@dataclass -class ServiceMatch: - """Matched offer and request.""" - - match_id: str - offer: ServiceOffer - request: ServiceRequest - agreed_price_eth: float - escrow_tx: Optional[str] = None - status: str = "pending" # pending, active, completed, disputed - created_at: float = field(default_factory=time.time) - completed_at: Optional[float] = None - result: Optional[Dict[str, Any]] = None - attestation: Optional[Dict[str, Any]] = None - - -class AgentMarketplace: - """Marketplace for agent services.""" - - def __init__(self): - """Initialize marketplace.""" - self.offers: Dict[str, ServiceOffer] = {} - self.requests: Dict[str, ServiceRequest] = {} - self.matches: Dict[str, ServiceMatch] = {} - - # Reputation tracking - self.reputation_scores: Dict[str, float] = {} - self.completed_transactions: Dict[str, int] = {} - - # Statistics - self.total_volume_eth = 0.0 - self.total_transactions = 0 - - def post_offer( - self, - agent: Web3Agent, - service_type: ServiceType, - description: str, - price_eth: float, - **kwargs, - ) -> str: - """Post a service offer. - - Args: - agent: Agent posting the offer - service_type: Type of service - description: Service description - price_eth: Price in ETH - **kwargs: Additional offer parameters - - Returns: - Offer ID - """ - offer_id = f"offer_{uuid.uuid4().hex[:8]}" - - offer = ServiceOffer( - id=offer_id, - agent_address=agent.address or "mock_address", - agent_name=agent.name, - service_type=service_type, - description=description, - price_eth=price_eth, - min_reputation=kwargs.get("min_reputation", 0.0), - max_duration_hours=kwargs.get("max_duration_hours", 24.0), - requires_tee=kwargs.get("requires_tee", False), - metadata=kwargs.get("metadata", {}), - expires_at=kwargs.get("expires_at"), - ) - - self.offers[offer_id] = offer - - # Try to match with existing requests - self._try_match_offers() - - return offer_id - - def post_request( - self, - agent: Web3Agent, - service_type: ServiceType, - description: str, - max_price_eth: float, - **kwargs, - ) -> str: - """Post a service request. - - Args: - agent: Agent posting the request - service_type: Type of service needed - description: Service description - max_price_eth: Maximum price willing to pay - **kwargs: Additional request parameters - - Returns: - Request ID - """ - request_id = f"request_{uuid.uuid4().hex[:8]}" - - request = ServiceRequest( - id=request_id, - requester_address=agent.address or "mock_address", - requester_name=agent.name, - service_type=service_type, - description=description, - max_price_eth=max_price_eth, - duration_hours=kwargs.get("duration_hours", 1.0), - min_reputation=kwargs.get("min_reputation", 0.0), - requires_tee=kwargs.get("requires_tee", False), - metadata=kwargs.get("metadata", {}), - expires_at=kwargs.get("expires_at"), - ) - - self.requests[request_id] = request - - # Try to match with existing offers - self._try_match_offers() - - return request_id - - def _try_match_offers(self): - """Try to match offers with requests.""" - # Remove expired items first - self._clean_expired() - - # Try to match each request - for _req_id, request in list(self.requests.items()): - best_offer = None - best_price = float("inf") - - # Find best matching offer - for _offer_id, offer in self.offers.items(): - if offer.matches_request(request): - # Check reputation requirements - provider_rep = self.get_reputation(offer.agent_address) - if provider_rep < request.min_reputation: - continue - - requester_rep = self.get_reputation(request.requester_address) - if requester_rep < offer.min_reputation: - continue - - # Track best price - if offer.price_eth < best_price: - best_offer = offer - best_price = offer.price_eth - - # Create match if found - if best_offer: - self._create_match(best_offer, request) - - def _create_match(self, offer: ServiceOffer, request: ServiceRequest): - """Create a match between offer and request.""" - match_id = f"match_{uuid.uuid4().hex[:8]}" - - # Agreed price is the offer price (could implement negotiation) - agreed_price = offer.price_eth - - match = ServiceMatch( - match_id=match_id, - offer=offer, - request=request, - agreed_price_eth=agreed_price, - status="pending", - ) - - self.matches[match_id] = match - - # Remove from active lists - del self.offers[offer.id] - del self.requests[request.id] - - # Update statistics - self.total_transactions += 1 - - print( - f"Match created: {offer.agent_name} -> {request.requester_name} for {agreed_price} ETH" - ) - - def complete_match( - self, - match_id: str, - result: Dict[str, Any], - attestation: Optional[Dict[str, Any]] = None, - ): - """Mark a match as completed. - - Args: - match_id: Match ID - result: Result of the service - attestation: Optional TEE attestation - """ - if match_id not in self.matches: - raise ValueError(f"Unknown match: {match_id}") - - match = self.matches[match_id] - match.status = "completed" - match.completed_at = time.time() - match.result = result - match.attestation = attestation - - # Update reputation - self._update_reputation( - match.offer.agent_address, - 1.0, # Positive for completion - ) - - # Update statistics - self.total_volume_eth += match.agreed_price_eth - - # Track completed transactions - provider = match.offer.agent_address - self.completed_transactions[provider] = ( - self.completed_transactions.get(provider, 0) + 1 - ) - - def dispute_match(self, match_id: str, reason: str): - """Dispute a match. - - Args: - match_id: Match ID - reason: Dispute reason - """ - if match_id not in self.matches: - raise ValueError(f"Unknown match: {match_id}") - - match = self.matches[match_id] - match.status = "disputed" - match.result = {"dispute_reason": reason} - - # Negative reputation for provider - self._update_reputation(match.offer.agent_address, -0.5) - - def get_reputation(self, agent_address: str) -> float: - """Get agent reputation score. - - Args: - agent_address: Agent's blockchain address - - Returns: - Reputation score (0-1) - """ - return self.reputation_scores.get(agent_address, 0.5) - - def _update_reputation(self, agent_address: str, delta: float): - """Update agent reputation. - - Args: - agent_address: Agent's address - delta: Change in reputation - """ - current = self.get_reputation(agent_address) - new_score = max(0, min(1, current + delta * 0.1)) # Damped update - self.reputation_scores[agent_address] = new_score - - def _clean_expired(self): - """Remove expired offers and requests.""" - # Clean offers - expired_offers = [ - oid for oid, offer in self.offers.items() if offer.is_expired() - ] - for oid in expired_offers: - del self.offers[oid] - - # Clean requests - expired_requests = [ - rid for rid, request in self.requests.items() if request.is_expired() - ] - for rid in expired_requests: - del self.requests[rid] - - def get_active_offers( - self, service_type: Optional[ServiceType] = None - ) -> List[ServiceOffer]: - """Get active offers, optionally filtered by type.""" - self._clean_expired() - - offers = list(self.offers.values()) - if service_type: - offers = [o for o in offers if o.service_type == service_type] - - return sorted(offers, key=lambda o: o.price_eth) - - def get_active_requests( - self, service_type: Optional[ServiceType] = None - ) -> List[ServiceRequest]: - """Get active requests, optionally filtered by type.""" - self._clean_expired() - - requests = list(self.requests.values()) - if service_type: - requests = [r for r in requests if r.service_type == service_type] - - return sorted(requests, key=lambda r: r.max_price_eth, reverse=True) - - def get_matches_for_agent(self, agent_address: str) -> List[ServiceMatch]: - """Get all matches involving an agent.""" - matches = [] - for match in self.matches.values(): - if ( - match.offer.agent_address == agent_address - or match.request.requester_address == agent_address - ): - matches.append(match) - - return sorted(matches, key=lambda m: m.created_at, reverse=True) - - def get_stats(self) -> Dict[str, Any]: - """Get marketplace statistics.""" - return { - "active_offers": len(self.offers), - "active_requests": len(self.requests), - "total_matches": len(self.matches), - "total_volume_eth": self.total_volume_eth, - "total_transactions": self.total_transactions, - "unique_providers": len(set(o.agent_address for o in self.offers.values())), - "unique_requesters": len( - set(r.requester_address for r in self.requests.values()) - ), - } - - -# Global marketplace instance (in production, this would be on-chain) -marketplace = AgentMarketplace() diff --git a/pkg/hanzo-agent/src/agents/extensions/tee/__init__.py b/pkg/hanzo-agent/src/agents/extensions/tee/__init__.py deleted file mode 100644 index 48064f68c..000000000 --- a/pkg/hanzo-agent/src/agents/extensions/tee/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Trusted Execution Environment (TEE) support for Hanzo agents. - -Provides attestation, confidential computing, and TEE marketplace functionality. -""" - -from .tee import ( - TEEConfig, - TEEProvider, - AttestationReport, - ConfidentialAgent, - ComputeMarketplace, - ComputeOffer, - ComputeRequest, - create_attestation_verifier_tool, -) - -__all__ = [ - "TEEConfig", - "TEEProvider", - "AttestationReport", - "ConfidentialAgent", - "ComputeMarketplace", - "ComputeOffer", - "ComputeRequest", - "create_attestation_verifier_tool", -] diff --git a/pkg/hanzo-agent/src/agents/extensions/tee/tee.py b/pkg/hanzo-agent/src/agents/extensions/tee/tee.py deleted file mode 100644 index c58281556..000000000 --- a/pkg/hanzo-agent/src/agents/extensions/tee/tee.py +++ /dev/null @@ -1,429 +0,0 @@ -"""Trusted Execution Environment (TEE) support for confidential agent computing. - -This module provides interfaces for running agents in secure enclaves, -enabling confidential AI computations with attestation capabilities. -""" - -import json -import time -import hashlib -from abc import ABC, abstractmethod -from enum import Enum -from typing import Any, Dict, List, Optional -from dataclasses import field, dataclass - - -class TEEProvider(Enum): - """Supported TEE providers.""" - - INTEL_SGX = "sgx" - AMD_SEV = "sev" - NVIDIA_H100 = "h100" - MOCK = "mock" - - -@dataclass -class AttestationReport: - """TEE attestation report.""" - - provider: TEEProvider - enclave_id: str - code_hash: str - timestamp: float - quote: bytes - signature: str - metadata: Dict[str, Any] = field(default_factory=dict) - - def to_dict(self) -> Dict[str, Any]: - """Convert to dictionary.""" - return { - "provider": self.provider.value, - "enclave_id": self.enclave_id, - "code_hash": self.code_hash, - "timestamp": self.timestamp, - "quote": self.quote.hex() if isinstance(self.quote, bytes) else self.quote, - "signature": self.signature, - "metadata": self.metadata, - } - - def verify(self, expected_code_hash: Optional[str] = None) -> bool: - """Verify attestation report. - - Verifies the TEE provider's signature on the quote and - optionally checks code hash. - """ - if expected_code_hash and self.code_hash != expected_code_hash: - return False - - if not self.signature: - return False - - # Verify based on provider - if self.provider == TEEProvider.SGX: - # SGX attestation uses EPID or DCAP - # Signature should be base64-encoded quote - import base64 - - try: - quote = base64.b64decode(self.signature) - # SGX quote header is 48 bytes minimum - return len(quote) >= 48 - except Exception: - return False - elif self.provider == TEEProvider.SEV: - # AMD SEV uses attestation report - import base64 - - try: - report = base64.b64decode(self.signature) - # SEV report is 0x4A0 bytes - return len(report) == 0x4A0 - except Exception: - return False - elif self.provider == TEEProvider.NITRO: - # AWS Nitro uses COSE-signed attestation document - # Minimum valid COSE structure - return len(self.signature) >= 100 - elif self.provider == TEEProvider.MOCK: - # Mock provider accepts any non-empty signature for testing - return True - else: - return False - - -@dataclass -class TEEConfig: - """Configuration for TEE execution.""" - - provider: TEEProvider = TEEProvider.MOCK - max_memory_mb: int = 4096 - max_execution_time_s: int = 300 - enable_network: bool = False - allowed_endpoints: List[str] = field(default_factory=list) - attestation_server: Optional[str] = None - - -class TEEExecutor(ABC): - """Abstract interface for TEE execution.""" - - @abstractmethod - def execute( - self, code: str, inputs: Dict[str, Any], config: TEEConfig - ) -> Dict[str, Any]: - """Execute code in TEE with given inputs.""" - pass - - @abstractmethod - def get_attestation(self) -> AttestationReport: - """Get attestation report for current execution.""" - pass - - @abstractmethod - def verify_remote_attestation( - self, report: AttestationReport, expected_code_hash: Optional[str] = None - ) -> bool: - """Verify a remote attestation report.""" - pass - - -class MockTEEExecutor(TEEExecutor): - """Mock TEE executor for testing.""" - - def __init__(self): - """Initialize mock TEE.""" - self.enclave_id = "mock_enclave_" + str(int(time.time())) - self.last_code_hash = None - self.last_result = None - - def execute( - self, code: str, inputs: Dict[str, Any], config: TEEConfig - ) -> Dict[str, Any]: - """Execute code in mock TEE.""" - # Compute code hash - self.last_code_hash = hashlib.sha256(code.encode()).hexdigest() - - # Simulate execution - # In real TEE, this would run in isolated enclave - namespace = {"inputs": inputs, "result": None} - - try: - exec(code, namespace) - self.last_result = namespace.get("result", {}) - - return { - "success": True, - "result": self.last_result, - "attestation": self.get_attestation().to_dict(), - } - except Exception as e: - return { - "success": False, - "error": str(e), - "attestation": self.get_attestation().to_dict(), - } - - def get_attestation(self) -> AttestationReport: - """Get attestation for last execution.""" - return AttestationReport( - provider=TEEProvider.MOCK, - enclave_id=self.enclave_id, - code_hash=self.last_code_hash or "", - timestamp=time.time(), - quote=b"mock_quote_data", - signature="mock_signature_" + (self.last_code_hash or "")[:16], - metadata={ - "mock": True, - "result_hash": ( - hashlib.sha256( - json.dumps(self.last_result, sort_keys=True).encode() - ).hexdigest() - if self.last_result - else None - ), - }, - ) - - def verify_remote_attestation( - self, report: AttestationReport, expected_code_hash: Optional[str] = None - ) -> bool: - """Verify mock attestation.""" - return report.verify(expected_code_hash) - - -class ConfidentialAgent: - """Agent that can execute in TEE for confidential computing.""" - - def __init__( - self, - agent, - tee_config: Optional[TEEConfig] = None, - tee_executor: Optional[TEEExecutor] = None, - ): - """Initialize confidential agent. - - Args: - agent: Base agent to wrap - tee_config: TEE configuration - tee_executor: TEE executor implementation - """ - self.agent = agent - self.tee_config = tee_config or TEEConfig() - self.tee_executor = tee_executor or MockTEEExecutor() - self.attestation_history: List[AttestationReport] = [] - - def execute_confidential( - self, - task_code: str, - inputs: Dict[str, Any], - verify_code_hash: Optional[str] = None, - ) -> Dict[str, Any]: - """Execute task in TEE. - - Args: - task_code: Python code to execute - inputs: Input data for the task - verify_code_hash: Expected code hash for verification - - Returns: - Execution result with attestation - """ - # Execute in TEE - result = self.tee_executor.execute(task_code, inputs, self.tee_config) - - # Get and store attestation - if result.get("success"): - attestation = AttestationReport(**result["attestation"]) - self.attestation_history.append(attestation) - - # Verify if requested - if verify_code_hash: - if not attestation.verify(verify_code_hash): - result["warning"] = "Code hash mismatch" - - return result - - def verify_computation( - self, result: Dict[str, Any], expected_code_hash: Optional[str] = None - ) -> bool: - """Verify a computation result came from valid TEE. - - Args: - result: Result dict with attestation - expected_code_hash: Expected code hash - - Returns: - True if verification passes - """ - if "attestation" not in result: - return False - - attestation = AttestationReport(**result["attestation"]) - return self.tee_executor.verify_remote_attestation( - attestation, expected_code_hash - ) - - def get_attestation_history(self) -> List[AttestationReport]: - """Get history of attestations.""" - return self.attestation_history.copy() - - -# Precompiled functions for on-chain TEE verification -def create_attestation_verifier_tool(): - """Create a tool for verifying TEE attestations.""" - from ..tool import Tool - - class AttestationVerifierTool(Tool): - """Tool for verifying TEE attestations.""" - - def __init__(self): - self.name = "verify_attestation" - self.description = "Verify TEE attestation reports" - - async def verify_attestation( - self, - attestation_dict: Dict[str, Any], - expected_code_hash: Optional[str] = None, - ) -> Dict[str, Any]: - """Verify a TEE attestation. - - Args: - attestation_dict: Attestation report as dict - expected_code_hash: Expected code hash - - Returns: - Verification result - """ - try: - report = AttestationReport(**attestation_dict) - is_valid = report.verify(expected_code_hash) - - return { - "valid": is_valid, - "provider": report.provider.value, - "enclave_id": report.enclave_id, - "code_hash": report.code_hash, - "timestamp": report.timestamp, - } - except Exception as e: - return {"valid": False, "error": str(e)} - - async def compare_attestations( - self, attestation1: Dict[str, Any], attestation2: Dict[str, Any] - ) -> Dict[str, Any]: - """Compare two attestation reports. - - Args: - attestation1: First attestation - attestation2: Second attestation - - Returns: - Comparison result - """ - try: - report1 = AttestationReport(**attestation1) - report2 = AttestationReport(**attestation2) - - return { - "same_provider": report1.provider == report2.provider, - "same_enclave": report1.enclave_id == report2.enclave_id, - "same_code": report1.code_hash == report2.code_hash, - "time_diff": abs(report1.timestamp - report2.timestamp), - } - except Exception as e: - return {"error": str(e)} - - return AttestationVerifierTool - - -# Computation marketplace for TEE resources -@dataclass -class ComputeOffer: - """Offer to provide computation resources.""" - - provider_address: str - provider_enclave_id: str - price_per_second: float # In ETH - max_duration: int # Seconds - supported_providers: List[TEEProvider] - attestation: Optional[AttestationReport] = None - - -@dataclass -class ComputeRequest: - """Request for computation resources.""" - - requester_address: str - code_hash: str - max_price_per_second: float - required_duration: int - required_provider: Optional[TEEProvider] = None - - -class ComputeMarketplace: - """Marketplace for TEE compute resources.""" - - def __init__(self): - """Initialize marketplace.""" - self.offers: Dict[str, ComputeOffer] = {} - self.requests: Dict[str, ComputeRequest] = {} - self.matches: List[Dict[str, Any]] = [] - - def post_offer(self, offer: ComputeOffer) -> str: - """Post a compute offer.""" - offer_id = f"offer_{len(self.offers)}" - self.offers[offer_id] = offer - self._try_match_offers() - return offer_id - - def post_request(self, request: ComputeRequest) -> str: - """Post a compute request.""" - request_id = f"request_{len(self.requests)}" - self.requests[request_id] = request - self._try_match_offers() - return request_id - - def _try_match_offers(self): - """Try to match offers with requests.""" - for req_id, request in list(self.requests.items()): - for offer_id, offer in list(self.offers.items()): - # Check if offer matches request - if ( - offer.price_per_second <= request.max_price_per_second - and offer.max_duration >= request.required_duration - and ( - not request.required_provider - or request.required_provider in offer.supported_providers - ) - ): - # Create match - match = { - "request_id": req_id, - "offer_id": offer_id, - "provider": offer.provider_address, - "requester": request.requester_address, - "price_per_second": offer.price_per_second, - "duration": request.required_duration, - "total_price": offer.price_per_second - * request.required_duration, - "timestamp": time.time(), - } - - self.matches.append(match) - - # Remove matched items - del self.requests[req_id] - del self.offers[offer_id] - - break - - def get_matches(self) -> List[Dict[str, Any]]: - """Get all matches.""" - return self.matches.copy() - - def get_active_offers(self) -> Dict[str, ComputeOffer]: - """Get active offers.""" - return self.offers.copy() - - def get_active_requests(self) -> Dict[str, ComputeRequest]: - """Get active requests.""" - return self.requests.copy() diff --git a/pkg/hanzo-agent/src/agents/extensions/web3/__init__.py b/pkg/hanzo-agent/src/agents/extensions/web3/__init__.py deleted file mode 100644 index f2ce77864..000000000 --- a/pkg/hanzo-agent/src/agents/extensions/web3/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Web3 integration for Hanzo agents. - -Provides wallet management, transaction handling, and Web3-enabled agents. -""" - -from .wallet import ( - AgentWallet, - Transaction, - WalletConfig, - create_wallet_tool, - derive_agent_wallet, - generate_shared_mnemonic, -) -from .web3_agent import Web3Agent, Web3AgentConfig -from .web3_network import Web3Network - -__all__ = [ - "AgentWallet", - "Transaction", - "WalletConfig", - "create_wallet_tool", - "derive_agent_wallet", - "generate_shared_mnemonic", - "Web3Agent", - "Web3AgentConfig", - "Web3Network", -] diff --git a/pkg/hanzo-agent/src/agents/extensions/web3/wallet.py b/pkg/hanzo-agent/src/agents/extensions/web3/wallet.py deleted file mode 100644 index 494fba164..000000000 --- a/pkg/hanzo-agent/src/agents/extensions/web3/wallet.py +++ /dev/null @@ -1,389 +0,0 @@ -"""Wallet and Web3 integration for agents. - -This module provides wallet capabilities for agents to interact with blockchain networks, -enabling on-chain payments, identity, and decentralized coordination. -""" - -import hashlib -import secrets -from abc import ABC, abstractmethod -from typing import Any, Dict, List, Optional -from dataclasses import dataclass - -from eth_account import Account -from eth_account.hdaccount import generate_mnemonic - -# Try to import web3 dependencies -try: - from web3 import Web3 - from eth_typing import HexStr, Address - from web3.types import Wei, TxParams - - WEB3_AVAILABLE = True -except ImportError: - WEB3_AVAILABLE = False - Web3 = None - TxParams = Dict[str, Any] - Wei = int - Address = str - HexStr = str - - -@dataclass -class WalletConfig: - """Configuration for agent wallet.""" - - private_key: Optional[str] = None - mnemonic: Optional[str] = None - account_index: int = 0 - network_rpc: str = "http://localhost:8545" - chain_id: int = 31337 # Default to local hardhat/anvil - gas_limit: int = 3000000 - gas_price_gwei: int = 20 - - def __post_init__(self): - """Validate configuration.""" - if not self.private_key and not self.mnemonic: - # Generate a new random private key if none provided - self.private_key = "0x" + secrets.token_hex(32) - - -@dataclass -class Transaction: - """Represents a blockchain transaction.""" - - hash: str - from_address: str - to_address: str - value: Wei - gas_used: Optional[int] = None - status: Optional[bool] = None - block_number: Optional[int] = None - - -class WalletInterface(ABC): - """Abstract interface for wallet implementations.""" - - @abstractmethod - def get_address(self) -> str: - """Get the wallet's address.""" - pass - - @abstractmethod - def get_balance(self) -> Wei: - """Get the wallet's balance in Wei.""" - pass - - @abstractmethod - def sign_message(self, message: str) -> str: - """Sign a message with the wallet's private key.""" - pass - - @abstractmethod - def send_transaction( - self, - to: str, - value: Wei, - data: Optional[bytes] = None, - gas_limit: Optional[int] = None, - gas_price: Optional[Wei] = None, - ) -> Transaction: - """Send a transaction.""" - pass - - @abstractmethod - def call_contract( - self, contract_address: str, function_signature: str, *args, **kwargs - ) -> Any: - """Call a smart contract function.""" - pass - - -class Web3Wallet(WalletInterface): - """Web3-based wallet implementation.""" - - def __init__(self, config: WalletConfig): - """Initialize Web3 wallet.""" - if not WEB3_AVAILABLE: - raise ImportError( - "Web3 dependencies not available. Install with: pip install web3 eth-account" - ) - - self.config = config - self.w3 = Web3(Web3.HTTPProvider(config.network_rpc)) - - # Initialize account from private key or mnemonic - if config.private_key: - self.account = Account.from_key(config.private_key) - elif config.mnemonic: - # Derive account from mnemonic at given index - Account.enable_unaudited_hdwallet_features() - self.account = Account.from_mnemonic( - config.mnemonic, account_path=f"m/44'/60'/0'/0/{config.account_index}" - ) - else: - raise ValueError("Either private_key or mnemonic must be provided") - - # Ensure connection - if not self.w3.is_connected(): - raise ConnectionError(f"Cannot connect to {config.network_rpc}") - - def get_address(self) -> str: - """Get the wallet's address.""" - return self.account.address - - def get_balance(self) -> Wei: - """Get the wallet's balance in Wei.""" - return self.w3.eth.get_balance(self.account.address) - - def sign_message(self, message: str) -> str: - """Sign a message with the wallet's private key.""" - message_hash = hashlib.sha256(message.encode()).digest() - signed = self.account.signHash(message_hash) - return signed.signature.hex() - - def send_transaction( - self, - to: str, - value: Wei, - data: Optional[bytes] = None, - gas_limit: Optional[int] = None, - gas_price: Optional[Wei] = None, - ) -> Transaction: - """Send a transaction.""" - # Build transaction - tx: TxParams = { - "from": self.account.address, - "to": to, - "value": value, - "gas": gas_limit or self.config.gas_limit, - "gasPrice": gas_price or Web3.to_wei(self.config.gas_price_gwei, "gwei"), - "nonce": self.w3.eth.get_transaction_count(self.account.address), - "chainId": self.config.chain_id, - } - - if data: - tx["data"] = data - - # Sign transaction - signed_tx = self.account.sign_transaction(tx) - - # Send transaction - tx_hash = self.w3.eth.send_raw_transaction(signed_tx.rawTransaction) - - # Wait for receipt - receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash) - - return Transaction( - hash=tx_hash.hex(), - from_address=self.account.address, - to_address=to, - value=value, - gas_used=receipt["gasUsed"], - status=receipt["status"] == 1, - block_number=receipt["blockNumber"], - ) - - def call_contract( - self, contract_address: str, function_signature: str, *args, **kwargs - ) -> Any: - """Call a smart contract function. - - This is a simplified interface. For complex contracts, - use web3.py directly with the contract ABI. - """ - # This would need the contract ABI for proper encoding - # For now, raise NotImplementedError - raise NotImplementedError( - "Contract calls require ABI. Use web3.py directly for now." - ) - - -class MockWallet(WalletInterface): - """Mock wallet for testing without blockchain.""" - - def __init__(self, config: WalletConfig): - """Initialize mock wallet.""" - self.config = config - self.address = ( - "0x" - + hashlib.sha256( - (config.private_key or config.mnemonic or "mock").encode() - ).hexdigest()[:40] - ) - self.balance = Wei(1000000000000000000000) # 1000 ETH - self.transactions: List[Transaction] = [] - - def get_address(self) -> str: - """Get the wallet's address.""" - return self.address - - def get_balance(self) -> Wei: - """Get the wallet's balance in Wei.""" - return self.balance - - def sign_message(self, message: str) -> str: - """Sign a message (mock).""" - return "0x" + hashlib.sha256(f"{self.address}:{message}".encode()).hexdigest() - - def send_transaction( - self, - to: str, - value: Wei, - data: Optional[bytes] = None, - gas_limit: Optional[int] = None, - gas_price: Optional[Wei] = None, - ) -> Transaction: - """Send a transaction (mock).""" - if value > self.balance: - raise ValueError("Insufficient balance") - - self.balance -= value - - tx = Transaction( - hash="0x" + secrets.token_hex(32), - from_address=self.address, - to_address=to, - value=value, - gas_used=21000, - status=True, - block_number=len(self.transactions), - ) - - self.transactions.append(tx) - return tx - - def call_contract( - self, contract_address: str, function_signature: str, *args, **kwargs - ) -> Any: - """Call a smart contract function (mock).""" - return f"Mock result for {function_signature}" - - -class AgentWallet: - """High-level wallet interface for agents.""" - - def __init__(self, config: Optional[WalletConfig] = None): - """Initialize agent wallet.""" - self.config = config or WalletConfig() - - # Use mock wallet if web3 not available or in test mode - if WEB3_AVAILABLE and not self.config.network_rpc.startswith("mock://"): - self.wallet = Web3Wallet(self.config) - else: - self.wallet = MockWallet(self.config) - - @property - def address(self) -> str: - """Get wallet address.""" - return self.wallet.get_address() - - @property - def balance(self) -> Wei: - """Get wallet balance.""" - return self.wallet.get_balance() - - def send_payment( - self, to: str, amount_ether: float, memo: Optional[str] = None - ) -> Transaction: - """Send a payment to another address. - - Args: - to: Recipient address - amount_ether: Amount in Ether (not Wei) - memo: Optional memo (stored off-chain) - - Returns: - Transaction object - """ - amount_wei = Wei(int(amount_ether * 10**18)) - - # Log memo if provided (would be stored in agent memory) - if memo: - print(f"Payment memo: {memo}") - - return self.wallet.send_transaction(to, amount_wei) - - def sign_message(self, message: str) -> str: - """Sign a message for authentication.""" - return self.wallet.sign_message(message) - - def verify_signature( - self, message: str, signature: str, expected_address: str - ) -> bool: - """Verify a signature matches expected address. - - Recovers the signer address from the signature and compares - with the expected address. - """ - from eth_account.messages import encode_defunct - from eth_account import Account - - message_hash = encode_defunct(text=message) - recovered = Account.recover_message(message_hash, signature=signature) - return recovered.lower() == expected_address.lower() - - -def generate_shared_mnemonic() -> str: - """Generate a shared mnemonic for a network of agents.""" - return generate_mnemonic(num_words=12, lang="english") - - -def derive_agent_wallet(mnemonic: str, agent_index: int, **kwargs) -> AgentWallet: - """Derive an agent wallet from shared mnemonic. - - Args: - mnemonic: Shared network mnemonic - agent_index: Unique index for this agent - **kwargs: Additional wallet config options - - Returns: - AgentWallet instance - """ - config = WalletConfig(mnemonic=mnemonic, account_index=agent_index, **kwargs) - return AgentWallet(config) - - -# Tool functions for agent use -def create_wallet_tool(): - """Create a tool that agents can use for wallet operations.""" - from ..tool import Tool - - class WalletTool(Tool): - """Tool for wallet operations.""" - - def __init__(self, wallet: AgentWallet): - self.wallet = wallet - self.name = "wallet" - self.description = "Interact with blockchain wallet" - - async def get_balance(self) -> float: - """Get wallet balance in Ether.""" - balance_wei = self.wallet.balance - return float(balance_wei) / 10**18 - - async def send_payment( - self, to_address: str, amount_ether: float, reason: Optional[str] = None - ) -> str: - """Send payment to another address. - - Args: - to_address: Recipient blockchain address - amount_ether: Amount to send in Ether - reason: Optional reason for payment - - Returns: - Transaction hash - """ - tx = self.wallet.send_payment(to_address, amount_ether, reason) - return f"Sent {amount_ether} ETH to {to_address}. Tx: {tx.hash}" - - async def get_address(self) -> str: - """Get this wallet's address.""" - return self.wallet.address - - async def sign_message(self, message: str) -> str: - """Sign a message for authentication.""" - return self.wallet.sign_message(message) - - return WalletTool diff --git a/pkg/hanzo-agent/src/agents/extensions/web3/web3_agent.py b/pkg/hanzo-agent/src/agents/extensions/web3/web3_agent.py deleted file mode 100644 index b80f020d4..000000000 --- a/pkg/hanzo-agent/src/agents/extensions/web3/web3_agent.py +++ /dev/null @@ -1,383 +0,0 @@ -"""Web3-enabled agent with wallet and TEE support.""" - -from typing import Any, Dict, Optional -from dataclasses import dataclass - -from .tee import ( - TEEConfig, - TEEProvider, - ConfidentialAgent, - create_attestation_verifier_tool, -) -from .agent import Agent, InferenceResult -from .state import State -from .wallet import AgentWallet, Transaction, WalletConfig, create_wallet_tool - - -@dataclass -class Web3AgentConfig: - """Configuration for Web3-enabled agents.""" - - # Wallet configuration - wallet_enabled: bool = False - wallet_config: Optional[WalletConfig] = None - - # TEE configuration - tee_enabled: bool = False - tee_config: Optional[TEEConfig] = None - tee_provider: TEEProvider = TEEProvider.MOCK - - # Economic parameters - min_payment_eth: float = 0.001 # Minimum payment to accept - task_price_eth: float = 0.01 # Default price for tasks - reputation_threshold: float = 0.8 # Min reputation to work with - - # On-chain identity - agent_nft_address: Optional[str] = None - reputation_contract: Optional[str] = None - - -class Web3Agent(Agent): - """Agent with Web3 capabilities including wallet and TEE support.""" - - def __init__( - self, - name: str, - description: str, - web3_config: Optional[Web3AgentConfig] = None, - **kwargs, - ): - """Initialize Web3-enabled agent. - - Args: - name: Agent name - description: Agent description - web3_config: Web3 configuration - **kwargs: Additional agent parameters - """ - super().__init__(**kwargs) - self.name = name - self.description = description - self.web3_config = web3_config or Web3AgentConfig() - - # Initialize wallet if enabled - self.wallet: Optional[AgentWallet] = None - if self.web3_config.wallet_enabled: - wallet_config = self.web3_config.wallet_config or WalletConfig() - self.wallet = AgentWallet(wallet_config) - - # Add wallet tool to agent's tools - WalletTool = create_wallet_tool() - self.wallet_tool = WalletTool(self.wallet) - if not hasattr(self, "tools"): - self.tools = [] - self.tools.append(self.wallet_tool) - - # Initialize TEE wrapper if enabled - self.confidential_agent: Optional[ConfidentialAgent] = None - if self.web3_config.tee_enabled: - self.confidential_agent = ConfidentialAgent( - self, self.web3_config.tee_config - ) - - # Add attestation verifier tool - AttestationTool = create_attestation_verifier_tool() - self.attestation_tool = AttestationTool() - if not hasattr(self, "tools"): - self.tools = [] - self.tools.append(self.attestation_tool) - - # Track economic activity - self.earnings: float = 0.0 - self.spending: float = 0.0 - self.completed_tasks: int = 0 - self.reputation_score: float = 1.0 - - @property - def address(self) -> Optional[str]: - """Get agent's blockchain address.""" - return self.wallet.address if self.wallet else None - - @property - def balance_eth(self) -> float: - """Get wallet balance in ETH.""" - if not self.wallet: - return 0.0 - return float(self.wallet.balance) / 10**18 - - async def request_payment( - self, from_address: str, amount_eth: float, task_description: str - ) -> Dict[str, Any]: - """Request payment for a task. - - Args: - from_address: Payer's address - amount_eth: Payment amount in ETH - task_description: Description of the task - - Returns: - Payment request details - """ - if not self.wallet: - return {"error": "Wallet not enabled"} - - if amount_eth < self.web3_config.min_payment_eth: - return { - "error": f"Payment too low. Minimum: {self.web3_config.min_payment_eth} ETH" - } - - return { - "to": self.wallet.address, - "amount_eth": amount_eth, - "task": task_description, - "request_id": f"req_{self.name}_{self.completed_tasks + 1}", - } - - async def verify_payment(self, tx_hash: str, expected_amount_eth: float) -> bool: - """Verify a payment was received by checking the blockchain. - - Args: - tx_hash: Transaction hash to verify - expected_amount_eth: Expected payment amount in ETH - - Returns: - True if payment verified, False otherwise - """ - if not self.wallet or not self.wallet.w3: - return False - - try: - tx = self.wallet.w3.eth.get_transaction(tx_hash) - receipt = self.wallet.w3.eth.get_transaction_receipt(tx_hash) - - # Check transaction succeeded - if receipt.status != 1: - return False - - # Check recipient is our address - if tx.to.lower() != self.wallet.address.lower(): - return False - - # Check amount (with small tolerance for gas estimation differences) - amount_wei = self.wallet.w3.to_wei(expected_amount_eth, "ether") - if tx.value < amount_wei * 0.99: # Allow 1% tolerance - return False - - self.earnings += expected_amount_eth - return True - except Exception: - return False - - async def pay_agent( - self, to_address: str, amount_eth: float, reason: str - ) -> Optional[Transaction]: - """Pay another agent. - - Args: - to_address: Recipient agent's address - amount_eth: Payment amount - reason: Reason for payment - - Returns: - Transaction object if successful - """ - if not self.wallet: - print("Wallet not enabled") - return None - - if amount_eth > self.balance_eth: - print(f"Insufficient balance. Have: {self.balance_eth}, Need: {amount_eth}") - return None - - try: - tx = self.wallet.send_payment(to_address, amount_eth, reason) - self.spending += amount_eth - return tx - except Exception as e: - print(f"Payment failed: {e}") - return None - - async def execute_confidential( - self, task_code: str, inputs: Dict[str, Any] - ) -> Dict[str, Any]: - """Execute task in TEE for confidentiality. - - Args: - task_code: Python code to execute - inputs: Input data for the task - - Returns: - Execution result with attestation - """ - if not self.confidential_agent: - return {"error": "TEE not enabled"} - - result = self.confidential_agent.execute_confidential(task_code, inputs) - - return result - - async def run(self, state: State, history, network) -> InferenceResult: - """Execute agent with Web3 enhancements. - - This adds economic and TEE considerations to agent execution. - """ - # Check if this is a paid task - task_payment = state.get("task_payment", 0) - if task_payment > 0: - # Verify payment before proceeding - tx_hash = state.get("payment_tx") - if tx_hash: - verified = await self.verify_payment(tx_hash, task_payment) - if not verified: - return InferenceResult( - agent=self.name, - content="Payment verification failed. Cannot proceed with task.", - metadata={"payment_required": True}, - ) - - # Check if confidential execution is requested - if state.get("require_tee", False) and self.confidential_agent: - # Execute in TEE - task_code = state.get("task_code", "") - task_inputs = state.get("task_inputs", {}) - - tee_result = await self.execute_confidential(task_code, task_inputs) - - return InferenceResult( - agent=self.name, - content="Task executed in TEE", - metadata={ - "tee_result": tee_result, - "attestation": tee_result.get("attestation"), - }, - ) - - # Regular execution - must be implemented by subclass - return await self._run_impl(state, history, network) - - async def _run_impl(self, state: State, history, network) -> InferenceResult: - """Actual agent implementation - override in subclass.""" - return InferenceResult( - agent=self.name, - content=f"{self.name} is ready to work. Balance: {self.balance_eth:.4f} ETH", - ) - - def update_reputation(self, delta: float): - """Update agent's reputation score. - - Args: - delta: Change in reputation (-1 to 1) - """ - self.reputation_score = max(0, min(1, self.reputation_score + delta)) - - def get_stats(self) -> Dict[str, Any]: - """Get agent statistics.""" - stats = { - "name": self.name, - "address": self.address, - "balance_eth": self.balance_eth, - "earnings_eth": self.earnings, - "spending_eth": self.spending, - "completed_tasks": self.completed_tasks, - "reputation": self.reputation_score, - "wallet_enabled": self.web3_config.wallet_enabled, - "tee_enabled": self.web3_config.tee_enabled, - } - - if self.confidential_agent: - stats["attestations"] = len( - self.confidential_agent.get_attestation_history() - ) - - return stats - - -# Example Web3-enabled agents - - -class DataProviderAgent(Web3Agent): - """Agent that provides data for payment.""" - - def __init__(self, **kwargs): - super().__init__( - name="data_provider", - description="Provides high-quality data for AI training", - **kwargs, - ) - self.data_catalog = { - "weather": {"price_eth": 0.01, "size_mb": 100}, - "finance": {"price_eth": 0.05, "size_mb": 500}, - "research": {"price_eth": 0.1, "size_mb": 1000}, - } - - async def _run_impl(self, state: State, history, network) -> InferenceResult: - """Provide data based on request.""" - request = state.get("data_request", {}) - dataset = request.get("dataset") - - if dataset not in self.data_catalog: - return InferenceResult( - agent=self.name, - content=f"Unknown dataset: {dataset}. Available: {list(self.data_catalog.keys())}", - ) - - data_info = self.data_catalog[dataset] - - # Request payment - payment_request = await self.request_payment( - from_address=request.get("requester_address", ""), - amount_eth=data_info["price_eth"], - task_description=f"Provide {dataset} dataset", - ) - - return InferenceResult( - agent=self.name, - content=f"Dataset {dataset} available for {data_info['price_eth']} ETH", - metadata={"payment_request": payment_request, "data_info": data_info}, - ) - - -class ComputeProviderAgent(Web3Agent): - """Agent that provides GPU compute for payment.""" - - def __init__(self, **kwargs): - super().__init__( - name="compute_provider", - description="Provides GPU compute resources", - web3_config=Web3AgentConfig( - wallet_enabled=True, - tee_enabled=True, - task_price_eth=0.1, # Per hour - ), - **kwargs, - ) - self.gpu_specs = {"model": "NVIDIA H100", "memory_gb": 80, "tflops": 1000} - - async def _run_impl(self, state: State, history, network) -> InferenceResult: - """Offer compute resources.""" - compute_request = state.get("compute_request", {}) - duration_hours = compute_request.get("duration_hours", 1) - - total_price = self.web3_config.task_price_eth * duration_hours - - # Create compute offer - offer = { - "provider": self.address, - "gpu": self.gpu_specs, - "price_per_hour_eth": self.web3_config.task_price_eth, - "total_price_eth": total_price, - "tee_enabled": self.web3_config.tee_enabled, - "min_duration_hours": 0.1, - "max_duration_hours": 24, - } - - # If TEE is requested, provide attestation - if compute_request.get("require_tee", False) and self.confidential_agent: - dummy_attestation = self.confidential_agent.tee_executor.get_attestation() - offer["attestation"] = dummy_attestation.to_dict() - - return InferenceResult( - agent=self.name, - content=f"GPU compute available: {self.gpu_specs['model']} for {total_price} ETH", - metadata={"compute_offer": offer}, - ) diff --git a/pkg/hanzo-agent/src/agents/extensions/web3/web3_network.py b/pkg/hanzo-agent/src/agents/extensions/web3/web3_network.py deleted file mode 100644 index 981a53e46..000000000 --- a/pkg/hanzo-agent/src/agents/extensions/web3/web3_network.py +++ /dev/null @@ -1,447 +0,0 @@ -"""Web3-enabled network orchestration with deterministic execution.""" - -import json -import time -import hashlib -from typing import Any, Dict, List, Union, Generic, TypeVar, Optional -from dataclasses import dataclass - -from .agent import Agent, InferenceResult -from .state import State -from .router import Router, RouterFn -from .wallet import derive_agent_wallet, generate_shared_mnemonic -from .network import Network -from .web3_agent import Web3Agent, Web3AgentConfig -from .marketplace import ServiceType, AgentMarketplace - -S = TypeVar("S", bound=State) - - -@dataclass -class NetworkEconomics: - """Economic configuration for agent network.""" - - # Token economics - network_token_symbol: str = "AI" - initial_agent_balance: float = 1000.0 # In network tokens - - # Fee structure - network_fee_percent: float = 0.01 # 1% network fee - min_task_fee: float = 0.001 # Minimum fee per task - - # Incentives - completion_bonus: float = 0.1 # Bonus for completing tasks - quality_multiplier: float = 2.0 # Multiplier for high-quality work - - # Slashing - failure_penalty: float = 0.05 # Penalty for failed tasks - timeout_penalty: float = 0.02 # Penalty for timeouts - - -@dataclass -class DeterministicConfig: - """Configuration for deterministic execution.""" - - # Seed for randomness - seed: int = 42 - - # Execution order - enforce_order: bool = True - allow_parallel: bool = False - - # Reproducibility - record_all_calls: bool = True - verify_outputs: bool = True - - # Checkpointing - checkpoint_every_n_steps: int = 10 - checkpoint_on_completion: bool = True - - -class Web3Network(Network[S], Generic[S]): - """Network with Web3 integration and deterministic execution.""" - - def __init__( - self, - *, - state: S, - agents: List[Union[Agent[S], Web3Agent]], - router: Union[Router, RouterFn[S]], - shared_mnemonic: Optional[str] = None, - network_economics: Optional[NetworkEconomics] = None, - deterministic_config: Optional[DeterministicConfig] = None, - marketplace: Optional[AgentMarketplace] = None, - **kwargs, - ): - """Initialize Web3-enabled network. - - Args: - state: Initial state - agents: List of agents (can be Web3Agent instances) - router: Router for agent orchestration - shared_mnemonic: Shared mnemonic for agent wallets - network_economics: Economic configuration - deterministic_config: Deterministic execution config - marketplace: Agent marketplace instance - **kwargs: Additional Network parameters - """ - super().__init__(state=state, agents=agents, router=router, **kwargs) - - # Web3 configuration - self.shared_mnemonic = shared_mnemonic or generate_shared_mnemonic() - self.network_economics = network_economics or NetworkEconomics() - self.deterministic_config = deterministic_config or DeterministicConfig() - self.marketplace = marketplace or globals()["marketplace"] - - # Initialize Web3 agents - self._initialize_web3_agents() - - # Economic tracking - self.network_treasury = 0.0 - self.total_fees_collected = 0.0 - self.total_rewards_distributed = 0.0 - - # Deterministic execution tracking - self.execution_log: List[Dict[str, Any]] = [] - self.execution_hash: Optional[str] = None - - def _initialize_web3_agents(self): - """Initialize Web3 capabilities for agents.""" - for i, agent_class in enumerate(self.agents): - # Skip if already instantiated - if isinstance(agent_class, Agent): - agent = agent_class - else: - # Instantiate agent - agent = agent_class() - - # If it's a Web3Agent, initialize wallet - if isinstance(agent, Web3Agent): - if not agent.wallet and agent.web3_config.wallet_enabled: - # Derive wallet from shared mnemonic - wallet = derive_agent_wallet( - self.shared_mnemonic, - agent_index=i, - network_rpc=( - agent.web3_config.wallet_config.network_rpc - if agent.web3_config.wallet_config - else "mock://localhost" - ), - ) - agent.wallet = wallet - - # Initial funding - agent.earnings = self.network_economics.initial_agent_balance - - # Store instance - self._agent_instances[agent.name] = agent - - async def run(self) -> S: - """Execute network with Web3 enhancements.""" - if self._running: - raise RuntimeError("Network already running") - - self._running = True - start_time = time.time() - - # Set deterministic seed - import random - - import numpy as np - - random.seed(self.deterministic_config.seed) - np.random.seed(self.deterministic_config.seed) - - try: - step = 0 - while self.call_count < self.max_steps: - # Checkpoint if needed - if ( - self.deterministic_config.checkpoint_every_n_steps > 0 - and step % self.deterministic_config.checkpoint_every_n_steps == 0 - ): - await self._checkpoint(f"step_{step}") - - # Route to next agent - next_agent = await self._route() - if next_agent is None: - break - - # Record pre-execution state - pre_state = ( - self.state.to_dict() - if hasattr(self.state, "to_dict") - else str(self.state) - ) - - # Execute agent - result = await self._execute_agent(next_agent) - - # Record execution - self._record_execution(next_agent, result, pre_state) - - # Handle economics if Web3 agent - if isinstance(next_agent, Web3Agent): - await self._handle_agent_economics(next_agent, result) - - # Check marketplace for matches - await self._check_marketplace(next_agent, result) - - step += 1 - - # Final checkpoint - if self.deterministic_config.checkpoint_on_completion: - await self._checkpoint("final") - - # Compute execution hash - self.execution_hash = self._compute_execution_hash() - - # Log summary - duration = time.time() - start_time - print(f"\nNetwork execution completed:") - print(f" Duration: {duration:.2f}s") - print(f" Steps: {step}") - print(f" Fees collected: {self.total_fees_collected:.4f}") - print(f" Rewards distributed: {self.total_rewards_distributed:.4f}") - print(f" Execution hash: {self.execution_hash}") - - finally: - self._running = False - - return self.state - - def _record_execution(self, agent: Agent, result: InferenceResult, pre_state: Any): - """Record execution for determinism.""" - if not self.deterministic_config.record_all_calls: - return - - record = { - "step": len(self.execution_log), - "timestamp": time.time(), - "agent": agent.name, - "pre_state": pre_state, - "result": result.to_dict(), - "post_state": ( - self.state.to_dict() - if hasattr(self.state, "to_dict") - else str(self.state) - ), - } - - self.execution_log.append(record) - - def _compute_execution_hash(self) -> str: - """Compute hash of entire execution for verification.""" - # Create deterministic representation - execution_data = { - "seed": self.deterministic_config.seed, - "agents": [a.name for a in self._agent_instances.values()], - "log": self.execution_log, - } - - # Compute hash - json_str = json.dumps(execution_data, sort_keys=True) - return hashlib.sha256(json_str.encode()).hexdigest() - - async def _handle_agent_economics(self, agent: Web3Agent, result: InferenceResult): - """Handle economic aspects of agent execution.""" - # Charge network fee - task_fee = max( - self.network_economics.min_task_fee, - agent.web3_config.task_price_eth - * self.network_economics.network_fee_percent, - ) - - if agent.balance_eth >= task_fee: - # Deduct fee (in real implementation, this would be on-chain) - agent.spending += task_fee - self.network_treasury += task_fee - self.total_fees_collected += task_fee - - # Reward for completion - if result.content and "error" not in result.content.lower(): - reward = self.network_economics.completion_bonus - - # Quality bonus - if result.metadata.get("quality_score", 0.5) > 0.8: - reward *= self.network_economics.quality_multiplier - - agent.earnings += reward - self.total_rewards_distributed += reward - - # Update reputation - agent.update_reputation(0.1) - else: - # Penalty for failure - penalty = self.network_economics.failure_penalty - agent.spending += penalty - agent.update_reputation(-0.1) - - async def _check_marketplace(self, agent: Agent, result: InferenceResult): - """Check marketplace for service opportunities.""" - if not isinstance(agent, Web3Agent): - return - - # Check if agent advertised any services - if "service_offer" in result.metadata: - offer = result.metadata["service_offer"] - offer_id = self.marketplace.post_offer( - agent=agent, - service_type=ServiceType(offer.get("type", "custom")), - description=offer.get("description", ""), - price_eth=offer.get("price_eth", 0.01), - requires_tee=offer.get("requires_tee", False), - ) - print(f"Agent {agent.name} posted offer: {offer_id}") - - # Check if agent requested any services - if "service_request" in result.metadata: - request = result.metadata["service_request"] - request_id = self.marketplace.post_request( - agent=agent, - service_type=ServiceType(request.get("type", "custom")), - description=request.get("description", ""), - max_price_eth=request.get("max_price_eth", 0.1), - requires_tee=request.get("requires_tee", False), - ) - print(f"Agent {agent.name} posted request: {request_id}") - - async def _checkpoint(self, name: str): - """Create a checkpoint of current state.""" - if not self.checkpoint_dir: - return - - checkpoint = { - "name": name, - "timestamp": time.time(), - "state": ( - self.state.to_dict() - if hasattr(self.state, "to_dict") - else str(self.state) - ), - "history": [entry.to_dict() for entry in self.history.entries], - "economics": { - "treasury": self.network_treasury, - "fees_collected": self.total_fees_collected, - "rewards_distributed": self.total_rewards_distributed, - }, - "execution_log": self.execution_log, - } - - # Save checkpoint - checkpoint_file = ( - self.checkpoint_dir / f"checkpoint_{name}_{int(time.time())}.json" - ) - checkpoint_file.parent.mkdir(parents=True, exist_ok=True) - - with open(checkpoint_file, "w") as f: - json.dump(checkpoint, f, indent=2) - - print(f"Checkpoint saved: {checkpoint_file}") - - def verify_execution(self, other_hash: str) -> bool: - """Verify execution matches another run. - - Args: - other_hash: Execution hash from another run - - Returns: - True if executions match - """ - if not self.execution_hash: - self.execution_hash = self._compute_execution_hash() - - return self.execution_hash == other_hash - - def get_agent_stats(self) -> Dict[str, Any]: - """Get statistics for all agents.""" - stats = {} - - for name, agent in self._agent_instances.items(): - if isinstance(agent, Web3Agent): - stats[name] = agent.get_stats() - else: - stats[name] = { - "name": name, - "type": "standard", - "calls": sum( - 1 for entry in self.execution_log if entry["agent"] == name - ), - } - - return stats - - def get_network_stats(self) -> Dict[str, Any]: - """Get network-wide statistics.""" - return { - "agents": len(self._agent_instances), - "total_steps": len(self.execution_log), - "treasury_balance": self.network_treasury, - "total_fees": self.total_fees_collected, - "total_rewards": self.total_rewards_distributed, - "marketplace_stats": self.marketplace.get_stats(), - "execution_hash": self.execution_hash, - } - - -# Factory function for easy network creation -def create_web3_network( - agents: List[Agent], - task: str, - enable_wallets: bool = True, - enable_tee: bool = False, - deterministic: bool = True, -) -> Web3Network[State]: - """Create a Web3-enabled network. - - Args: - agents: List of agents to include - task: Initial task/query - enable_wallets: Enable wallet functionality - enable_tee: Enable TEE support - deterministic: Enable deterministic execution - - Returns: - Configured Web3Network instance - """ - # Configure agents - web3_agents = [] - for agent in agents: - if isinstance(agent, Web3Agent): - web3_agents.append(agent) - else: - # Wrap in Web3Agent - config = Web3AgentConfig( - wallet_enabled=enable_wallets, tee_enabled=enable_tee - ) - - class Web3Wrapper(Web3Agent): - async def _run_impl(self, state, history, network): - # Delegate to original agent - return await agent.run(state, history, network) - - wrapped = Web3Wrapper( - name=agent.name, - description=getattr(agent, "description", ""), - web3_config=config, - ) - wrapped.tools = getattr(agent, "tools", []) - web3_agents.append(wrapped) - - # Create initial state - state = State() - state["task"] = task - state["start_time"] = time.time() - - # Simple sequential router - from .router import sequential_router - - router = sequential_router([a.name for a in web3_agents]) - - # Create network - return Web3Network( - state=state, - agents=web3_agents, - router=router, - deterministic_config=DeterministicConfig() if deterministic else None, - ) diff --git a/pkg/hanzo-agent/src/agents/function_schema.py b/pkg/hanzo-agent/src/agents/function_schema.py deleted file mode 100644 index 15bd476b3..000000000 --- a/pkg/hanzo-agent/src/agents/function_schema.py +++ /dev/null @@ -1,347 +0,0 @@ -from __future__ import annotations - -import contextlib -import inspect -import logging -import re -from dataclasses import dataclass -from typing import Any, Callable, Literal, get_args, get_origin, get_type_hints - -from griffe import Docstring, DocstringSectionKind -from pydantic import BaseModel, Field, create_model - -from .exceptions import UserError -from .run_context import RunContextWrapper -from .strict_schema import ensure_strict_json_schema - - -@dataclass -class FuncSchema: - """ - Captures the schema for a python function, in preparation for sending it to an LLM as a tool. - """ - - name: str - """The name of the function.""" - description: str | None - """The description of the function.""" - params_pydantic_model: type[BaseModel] - """A Pydantic model that represents the function's parameters.""" - params_json_schema: dict[str, Any] - """The JSON schema for the function's parameters, derived from the Pydantic model.""" - signature: inspect.Signature - """The signature of the function.""" - takes_context: bool = False - """Whether the function takes a RunContextWrapper argument (must be the first argument).""" - - def to_call_args(self, data: BaseModel) -> tuple[list[Any], dict[str, Any]]: - """ - Converts validated data from the Pydantic model into (args, kwargs), suitable for calling - the original function. - """ - positional_args: list[Any] = [] - keyword_args: dict[str, Any] = {} - seen_var_positional = False - - # Use enumerate() so we can skip the first parameter if it's context. - for idx, (name, param) in enumerate(self.signature.parameters.items()): - # If the function takes a RunContextWrapper and this is the first parameter, skip it. - if self.takes_context and idx == 0: - continue - - value = getattr(data, name, None) - if param.kind == param.VAR_POSITIONAL: - # e.g. *args: extend positional args and mark that *args is now seen - positional_args.extend(value or []) - seen_var_positional = True - elif param.kind == param.VAR_KEYWORD: - # e.g. **kwargs handling - keyword_args.update(value or {}) - elif param.kind in (param.POSITIONAL_ONLY, param.POSITIONAL_OR_KEYWORD): - # Before *args, add to positional args. After *args, add to keyword args. - if not seen_var_positional: - positional_args.append(value) - else: - keyword_args[name] = value - else: - # For KEYWORD_ONLY parameters, always use keyword args. - keyword_args[name] = value - return positional_args, keyword_args - - -@dataclass -class FuncDocumentation: - """Contains metadata about a python function, extracted from its docstring.""" - - name: str - """The name of the function, via `__name__`.""" - description: str | None - """The description of the function, derived from the docstring.""" - param_descriptions: dict[str, str] | None - """The parameter descriptions of the function, derived from the docstring.""" - - -DocstringStyle = Literal["google", "numpy", "sphinx"] - - -# As of Feb 2025, the automatic style detection in griffe is an Insiders feature. This -# code approximates it. -def _detect_docstring_style(doc: str) -> DocstringStyle: - scores: dict[DocstringStyle, int] = {"sphinx": 0, "numpy": 0, "google": 0} - - # Sphinx style detection: look for :param, :type, :return:, and :rtype: - sphinx_patterns = [r"^:param\s", r"^:type\s", r"^:return:", r"^:rtype:"] - for pattern in sphinx_patterns: - if re.search(pattern, doc, re.MULTILINE): - scores["sphinx"] += 1 - - # Numpy style detection: look for headers like 'Parameters', 'Returns', or 'Yields' followed by - # a dashed underline - numpy_patterns = [ - r"^Parameters\s*\n\s*-{3,}", - r"^Returns\s*\n\s*-{3,}", - r"^Yields\s*\n\s*-{3,}", - ] - for pattern in numpy_patterns: - if re.search(pattern, doc, re.MULTILINE): - scores["numpy"] += 1 - - # Google style detection: look for section headers with a trailing colon - google_patterns = [r"^(Args|Arguments):", r"^(Returns):", r"^(Raises):"] - for pattern in google_patterns: - if re.search(pattern, doc, re.MULTILINE): - scores["google"] += 1 - - max_score = max(scores.values()) - if max_score == 0: - return "google" - - # Priority order: sphinx > numpy > google in case of tie - styles: list[DocstringStyle] = ["sphinx", "numpy", "google"] - - for style in styles: - if scores[style] == max_score: - return style - - return "google" - - -@contextlib.contextmanager -def _suppress_griffe_logging(): - # Supresses warnings about missing annotations for params - logger = logging.getLogger("griffe") - previous_level = logger.getEffectiveLevel() - logger.setLevel(logging.ERROR) - try: - yield - finally: - logger.setLevel(previous_level) - - -def generate_func_documentation( - func: Callable[..., Any], style: DocstringStyle | None = None -) -> FuncDocumentation: - """ - Extracts metadata from a function docstring, in preparation for sending it to an LLM as a tool. - - Args: - func: The function to extract documentation from. - style: The style of the docstring to use for parsing. If not provided, we will attempt to - auto-detect the style. - - Returns: - A FuncDocumentation object containing the function's name, description, and parameter - descriptions. - """ - name = func.__name__ - doc = inspect.getdoc(func) - if not doc: - return FuncDocumentation(name=name, description=None, param_descriptions=None) - - with _suppress_griffe_logging(): - docstring = Docstring( - doc, lineno=1, parser=style or _detect_docstring_style(doc) - ) - parsed = docstring.parse() - - description: str | None = next( - ( - section.value - for section in parsed - if section.kind == DocstringSectionKind.text - ), - None, - ) - - param_descriptions: dict[str, str] = { - param.name: param.description - for section in parsed - if section.kind == DocstringSectionKind.parameters - for param in section.value - } - - return FuncDocumentation( - name=func.__name__, - description=description, - param_descriptions=param_descriptions or None, - ) - - -def function_schema( - func: Callable[..., Any], - docstring_style: DocstringStyle | None = None, - name_override: str | None = None, - description_override: str | None = None, - use_docstring_info: bool = True, - strict_json_schema: bool = True, -) -> FuncSchema: - """ - Given a python function, extracts a `FuncSchema` from it, capturing the name, description, - parameter descriptions, and other metadata. - - Args: - func: The function to extract the schema from. - docstring_style: The style of the docstring to use for parsing. If not provided, we will - attempt to auto-detect the style. - name_override: If provided, use this name instead of the function's `__name__`. - description_override: If provided, use this description instead of the one derived from the - docstring. - use_docstring_info: If True, uses the docstring to generate the description and parameter - descriptions. - strict_json_schema: Whether the JSON schema is in strict mode. If True, we'll ensure that - the schema adheres to the "strict" standard the OpenAI API expects. We **strongly** - recommend setting this to True, as it increases the likelihood of the LLM providing - correct JSON input. - - Returns: - A `FuncSchema` object containing the function's name, description, parameter descriptions, - and other metadata. - """ - - # 1. Grab docstring info - if use_docstring_info: - doc_info = generate_func_documentation(func, docstring_style) - param_descs = doc_info.param_descriptions or {} - else: - doc_info = None - param_descs = {} - - func_name = name_override or doc_info.name if doc_info else func.__name__ - - # 2. Inspect function signature and get type hints - sig = inspect.signature(func) - type_hints = get_type_hints(func) - params = list(sig.parameters.items()) - takes_context = False - filtered_params = [] - - if params: - first_name, first_param = params[0] - # Prefer the evaluated type hint if available - ann = type_hints.get(first_name, first_param.annotation) - if ann != inspect._empty: - origin = get_origin(ann) or ann - if origin is RunContextWrapper: - takes_context = True # Mark that the function takes context - else: - filtered_params.append((first_name, first_param)) - else: - filtered_params.append((first_name, first_param)) - - # For parameters other than the first, raise error if any use RunContextWrapper. - for name, param in params[1:]: - ann = type_hints.get(name, param.annotation) - if ann != inspect._empty: - origin = get_origin(ann) or ann - if origin is RunContextWrapper: - raise UserError( - f"RunContextWrapper param found at non-first position in function" - f" {func.__name__}" - ) - filtered_params.append((name, param)) - - # We will collect field definitions for create_model as a dict: - # field_name -> (type_annotation, default_value_or_Field(...)) - fields: dict[str, Any] = {} - - for name, param in filtered_params: - ann = type_hints.get(name, param.annotation) - default = param.default - - # If there's no type hint, assume `Any` - if ann == inspect._empty: - ann = Any - - # If a docstring param description exists, use it - field_description = param_descs.get(name, None) - - # Handle different parameter kinds - if param.kind == param.VAR_POSITIONAL: - # e.g. *args: extend positional args - if get_origin(ann) is tuple: - # e.g. def foo(*args: tuple[int, ...]) -> treat as List[int] - args_of_tuple = get_args(ann) - if len(args_of_tuple) == 2 and args_of_tuple[1] is Ellipsis: - ann = list[args_of_tuple[0]] # type: ignore - else: - ann = list[Any] - else: - # If user wrote *args: int, treat as List[int] - ann = list[ann] # type: ignore - - # Default factory to empty list - fields[name] = ( - ann, - Field(default_factory=list, description=field_description), # type: ignore - ) - - elif param.kind == param.VAR_KEYWORD: - # **kwargs handling - if get_origin(ann) is dict: - # e.g. def foo(**kwargs: dict[str, int]) - dict_args = get_args(ann) - if len(dict_args) == 2: - ann = dict[dict_args[0], dict_args[1]] # type: ignore - else: - ann = dict[str, Any] - else: - # e.g. def foo(**kwargs: int) -> Dict[str, int] - ann = dict[str, ann] # type: ignore - - fields[name] = ( - ann, - Field(default_factory=dict, description=field_description), # type: ignore - ) - - else: - # Normal parameter - if default == inspect._empty: - # Required field - fields[name] = ( - ann, - Field(..., description=field_description), - ) - else: - # Parameter with a default value - fields[name] = ( - ann, - Field(default=default, description=field_description), - ) - - # 3. Dynamically build a Pydantic model - dynamic_model = create_model(f"{func_name}_args", __base__=BaseModel, **fields) - - # 4. Build JSON schema from that model - json_schema = dynamic_model.model_json_schema() - if strict_json_schema: - json_schema = ensure_strict_json_schema(json_schema) - - # 5. Return as a FuncSchema dataclass - return FuncSchema( - name=func_name, - description=description_override or doc_info.description if doc_info else None, - params_pydantic_model=dynamic_model, - params_json_schema=json_schema, - signature=sig, - takes_context=takes_context, - ) diff --git a/pkg/hanzo-agent/src/agents/guardrail.py b/pkg/hanzo-agent/src/agents/guardrail.py deleted file mode 100644 index 638373a08..000000000 --- a/pkg/hanzo-agent/src/agents/guardrail.py +++ /dev/null @@ -1,342 +0,0 @@ -from __future__ import annotations - -import inspect -from collections.abc import Awaitable -from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Callable, Generic, Union, overload - -from typing_extensions import TypeVar - -from ._utils import MaybeAwaitable -from .exceptions import UserError -from .items import TResponseInputItem -from .run_context import RunContextWrapper, TContext - -if TYPE_CHECKING: - from .agent import Agent - - -@dataclass -class GuardrailFunctionOutput: - """The output of a guardrail function.""" - - output_info: Any - """ - Optional information about the guardrail's output. For example, the guardrail could include - information about the checks it performed and granular results. - """ - - tripwire_triggered: bool - """ - Whether the tripwire was triggered. If triggered, the agent's execution will be halted. - """ - - -@dataclass -class InputGuardrailResult: - """The result of a guardrail run.""" - - guardrail: InputGuardrail[Any] - """ - The guardrail that was run. - """ - - output: GuardrailFunctionOutput - """The output of the guardrail function.""" - - -@dataclass -class OutputGuardrailResult: - """The result of a guardrail run.""" - - guardrail: OutputGuardrail[Any] - """ - The guardrail that was run. - """ - - agent_output: Any - """ - The output of the agent that was checked by the guardrail. - """ - - agent: Agent[Any] - """ - The agent that was checked by the guardrail. - """ - - output: GuardrailFunctionOutput - """The output of the guardrail function.""" - - -@dataclass -class InputGuardrail(Generic[TContext]): - """Input guardrails are checks that run in parallel to the agent's execution. - They can be used to do things like: - - Check if input messages are off-topic - - Take over control of the agent's execution if an unexpected input is detected - - You can use the `@input_guardrail()` decorator to turn a function into an `InputGuardrail`, or - create an `InputGuardrail` manually. - - Guardrails return a `GuardrailResult`. If `result.tripwire_triggered` is `True`, the agent - execution will immediately stop and a `InputGuardrailTripwireTriggered` exception will be raised - """ - - guardrail_function: Callable[ - [RunContextWrapper[TContext], Agent[Any], str | list[TResponseInputItem]], - MaybeAwaitable[GuardrailFunctionOutput], - ] - """A function that receives the agent input and the context, and returns a - `GuardrailResult`. The result marks whether the tripwire was triggered, and can optionally - include information about the guardrail's output. - """ - - name: str | None = None - """The name of the guardrail, used for tracing. If not provided, we'll use the guardrail - function's name. - """ - - def get_name(self) -> str: - if self.name: - return self.name - - return self.guardrail_function.__name__ - - async def run( - self, - agent: Agent[Any], - input: str | list[TResponseInputItem], - context: RunContextWrapper[TContext], - ) -> InputGuardrailResult: - if not callable(self.guardrail_function): - raise UserError( - f"Guardrail function must be callable, got {self.guardrail_function}" - ) - - output = self.guardrail_function(context, agent, input) - if inspect.isawaitable(output): - return InputGuardrailResult( - guardrail=self, - output=await output, - ) - - return InputGuardrailResult( - guardrail=self, - output=output, - ) - - -@dataclass -class OutputGuardrail(Generic[TContext]): - """Output guardrails are checks that run on the final output of an agent. - They can be used to do check if the output passes certain validation criteria - - You can use the `@output_guardrail()` decorator to turn a function into an `OutputGuardrail`, - or create an `OutputGuardrail` manually. - - Guardrails return a `GuardrailResult`. If `result.tripwire_triggered` is `True`, a - `OutputGuardrailTripwireTriggered` exception will be raised. - """ - - guardrail_function: Callable[ - [RunContextWrapper[TContext], Agent[Any], Any], - MaybeAwaitable[GuardrailFunctionOutput], - ] - """A function that receives the final agent, its output, and the context, and returns a - `GuardrailResult`. The result marks whether the tripwire was triggered, and can optionally - include information about the guardrail's output. - """ - - name: str | None = None - """The name of the guardrail, used for tracing. If not provided, we'll use the guardrail - function's name. - """ - - def get_name(self) -> str: - if self.name: - return self.name - - return self.guardrail_function.__name__ - - async def run( - self, context: RunContextWrapper[TContext], agent: Agent[Any], agent_output: Any - ) -> OutputGuardrailResult: - if not callable(self.guardrail_function): - raise UserError( - f"Guardrail function must be callable, got {self.guardrail_function}" - ) - - output = self.guardrail_function(context, agent, agent_output) - if inspect.isawaitable(output): - return OutputGuardrailResult( - guardrail=self, - agent=agent, - agent_output=agent_output, - output=await output, - ) - - return OutputGuardrailResult( - guardrail=self, - agent=agent, - agent_output=agent_output, - output=output, - ) - - -TContext_co = TypeVar("TContext_co", bound=Any, covariant=True) - -# For InputGuardrail -_InputGuardrailFuncSync = Callable[ - [ - RunContextWrapper[TContext_co], - "Agent[Any]", - Union[str, list[TResponseInputItem]], - ], - GuardrailFunctionOutput, -] -_InputGuardrailFuncAsync = Callable[ - [ - RunContextWrapper[TContext_co], - "Agent[Any]", - Union[str, list[TResponseInputItem]], - ], - Awaitable[GuardrailFunctionOutput], -] - - -@overload -def input_guardrail( - func: _InputGuardrailFuncSync[TContext_co], -) -> InputGuardrail[TContext_co]: ... - - -@overload -def input_guardrail( - func: _InputGuardrailFuncAsync[TContext_co], -) -> InputGuardrail[TContext_co]: ... - - -@overload -def input_guardrail( - *, - name: str | None = None, -) -> Callable[ - [_InputGuardrailFuncSync[TContext_co] | _InputGuardrailFuncAsync[TContext_co]], - InputGuardrail[TContext_co], -]: ... - - -def input_guardrail( - func: ( - _InputGuardrailFuncSync[TContext_co] - | _InputGuardrailFuncAsync[TContext_co] - | None - ) = None, - *, - name: str | None = None, -) -> ( - InputGuardrail[TContext_co] - | Callable[ - [_InputGuardrailFuncSync[TContext_co] | _InputGuardrailFuncAsync[TContext_co]], - InputGuardrail[TContext_co], - ] -): - """ - Decorator that transforms a sync or async function into an `InputGuardrail`. - It can be used directly (no parentheses) or with keyword args, e.g.: - - @input_guardrail - def my_sync_guardrail(...): ... - - @input_guardrail(name="guardrail_name") - async def my_async_guardrail(...): ... - """ - - def decorator( - f: _InputGuardrailFuncSync[TContext_co] | _InputGuardrailFuncAsync[TContext_co], - ) -> InputGuardrail[TContext_co]: - return InputGuardrail(guardrail_function=f, name=name) - - if func is not None: - # Decorator was used without parentheses - return decorator(func) - - # Decorator used with keyword arguments - return decorator - - -_OutputGuardrailFuncSync = Callable[ - [RunContextWrapper[TContext_co], "Agent[Any]", Any], - GuardrailFunctionOutput, -] -_OutputGuardrailFuncAsync = Callable[ - [RunContextWrapper[TContext_co], "Agent[Any]", Any], - Awaitable[GuardrailFunctionOutput], -] - - -@overload -def output_guardrail( - func: _OutputGuardrailFuncSync[TContext_co], -) -> OutputGuardrail[TContext_co]: ... - - -@overload -def output_guardrail( - func: _OutputGuardrailFuncAsync[TContext_co], -) -> OutputGuardrail[TContext_co]: ... - - -@overload -def output_guardrail( - *, - name: str | None = None, -) -> Callable[ - [_OutputGuardrailFuncSync[TContext_co] | _OutputGuardrailFuncAsync[TContext_co]], - OutputGuardrail[TContext_co], -]: ... - - -def output_guardrail( - func: ( - _OutputGuardrailFuncSync[TContext_co] - | _OutputGuardrailFuncAsync[TContext_co] - | None - ) = None, - *, - name: str | None = None, -) -> ( - OutputGuardrail[TContext_co] - | Callable[ - [ - _OutputGuardrailFuncSync[TContext_co] - | _OutputGuardrailFuncAsync[TContext_co] - ], - OutputGuardrail[TContext_co], - ] -): - """ - Decorator that transforms a sync or async function into an `OutputGuardrail`. - It can be used directly (no parentheses) or with keyword args, e.g.: - - @output_guardrail - def my_sync_guardrail(...): ... - - @output_guardrail(name="guardrail_name") - async def my_async_guardrail(...): ... - """ - - def decorator( - f: ( - _OutputGuardrailFuncSync[TContext_co] - | _OutputGuardrailFuncAsync[TContext_co] - ), - ) -> OutputGuardrail[TContext_co]: - return OutputGuardrail(guardrail_function=f, name=name) - - if func is not None: - # Decorator was used without parentheses - return decorator(func) - - # Decorator used with keyword arguments - return decorator diff --git a/pkg/hanzo-agent/src/agents/handoffs.py b/pkg/hanzo-agent/src/agents/handoffs.py deleted file mode 100644 index 4412143b0..000000000 --- a/pkg/hanzo-agent/src/agents/handoffs.py +++ /dev/null @@ -1,242 +0,0 @@ -from __future__ import annotations - -import inspect -from collections.abc import Awaitable -from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Callable, Generic, cast, overload - -from pydantic import TypeAdapter -from typing_extensions import TypeAlias, TypeVar - -from . import _utils -from .exceptions import ModelBehaviorError, UserError -from .items import RunItem, TResponseInputItem -from .run_context import RunContextWrapper, TContext -from .strict_schema import ensure_strict_json_schema -from .tracing.spans import SpanError - -if TYPE_CHECKING: - from .agent import Agent - - -# The handoff input type is the type of data passed when the agent is called via a handoff. -THandoffInput = TypeVar("THandoffInput", default=Any) - -OnHandoffWithInput = Callable[[RunContextWrapper[Any], THandoffInput], Any] -OnHandoffWithoutInput = Callable[[RunContextWrapper[Any]], Any] - - -@dataclass(frozen=True) -class HandoffInputData: - input_history: str | tuple[TResponseInputItem, ...] - """ - The input history before `Runner.run()` was called. - """ - - pre_handoff_items: tuple[RunItem, ...] - """ - The items generated before the agent turn where the handoff was invoked. - """ - - new_items: tuple[RunItem, ...] - """ - The new items generated during the current agent turn, including the item that triggered the - handoff and the tool output message representing the response from the handoff output. - """ - - -HandoffInputFilter: TypeAlias = Callable[[HandoffInputData], HandoffInputData] -"""A function that filters the input data passed to the next agent.""" - - -@dataclass -class Handoff(Generic[TContext]): - """A handoff is when an agent delegates a task to another agent. - For example, in a customer support scenario you might have a "triage agent" that determines - which agent should handle the user's request, and sub-agents that specialize in different - areas like billing, account management, etc. - """ - - tool_name: str - """The name of the tool that represents the handoff.""" - - tool_description: str - """The description of the tool that represents the handoff.""" - - input_json_schema: dict[str, Any] - """The JSON schema for the handoff input. Can be empty if the handoff does not take an input. - """ - - on_invoke_handoff: Callable[ - [RunContextWrapper[Any], str], Awaitable[Agent[TContext]] - ] - """The function that invokes the handoff. The parameters passed are: - 1. The handoff run context - 2. The arguments from the LLM, as a JSON string. Empty string if input_json_schema is empty. - - Must return an agent. - """ - - agent_name: str - """The name of the agent that is being handed off to.""" - - input_filter: HandoffInputFilter | None = None - """A function that filters the inputs that are passed to the next agent. By default, the new - agent sees the entire conversation history. In some cases, you may want to filter inputs e.g. - to remove older inputs, or remove tools from existing inputs. - - The function will receive the entire conversation history so far, including the input item - that triggered the handoff and a tool call output item representing the handoff tool's output. - - You are free to modify the input history or new items as you see fit. The next agent that - runs will receive `handoff_input_data.all_items`. - - IMPORTANT: in streaming mode, we will not stream anything as a result of this function. The - items generated before will already have been streamed. - """ - - strict_json_schema: bool = True - """Whether the input JSON schema is in strict mode. We **strongly** recommend setting this to - True, as it increases the likelihood of correct JSON input. - """ - - def get_transfer_message(self, agent: Agent[Any]) -> str: - base = f"{{'assistant': '{agent.name}'}}" - return base - - @classmethod - def default_tool_name(cls, agent: Agent[Any]) -> str: - return _utils.transform_string_function_style(f"transfer_to_{agent.name}") - - @classmethod - def default_tool_description(cls, agent: Agent[Any]) -> str: - return ( - f"Handoff to the {agent.name} agent to handle the request. " - f"{agent.handoff_description or ''}" - ) - - -@overload -def handoff( - agent: Agent[TContext], - *, - tool_name_override: str | None = None, - tool_description_override: str | None = None, - input_filter: Callable[[HandoffInputData], HandoffInputData] | None = None, -) -> Handoff[TContext]: ... - - -@overload -def handoff( - agent: Agent[TContext], - *, - on_handoff: OnHandoffWithInput[THandoffInput], - input_type: type[THandoffInput], - tool_description_override: str | None = None, - tool_name_override: str | None = None, - input_filter: Callable[[HandoffInputData], HandoffInputData] | None = None, -) -> Handoff[TContext]: ... - - -@overload -def handoff( - agent: Agent[TContext], - *, - on_handoff: OnHandoffWithoutInput, - tool_description_override: str | None = None, - tool_name_override: str | None = None, - input_filter: Callable[[HandoffInputData], HandoffInputData] | None = None, -) -> Handoff[TContext]: ... - - -def handoff( - agent: Agent[TContext], - tool_name_override: str | None = None, - tool_description_override: str | None = None, - on_handoff: OnHandoffWithInput[THandoffInput] | OnHandoffWithoutInput | None = None, - input_type: type[THandoffInput] | None = None, - input_filter: Callable[[HandoffInputData], HandoffInputData] | None = None, -) -> Handoff[TContext]: - """Create a handoff from an agent. - - Args: - agent: The agent to handoff to, or a function that returns an agent. - tool_name_override: Optional override for the name of the tool that represents the handoff. - tool_description_override: Optional override for the description of the tool that - represents the handoff. - on_handoff: A function that runs when the handoff is invoked. - input_type: the type of the input to the handoff. If provided, the input will be validated - against this type. Only relevant if you pass a function that takes an input. - input_filter: a function that filters the inputs that are passed to the next agent. - """ - assert (on_handoff and input_type) or not ( - on_handoff and input_type - ), "You must provide either both on_input and input_type, or neither" - type_adapter: TypeAdapter[Any] | None - if input_type is not None: - assert callable(on_handoff), "on_handoff must be callable" - sig = inspect.signature(on_handoff) - if len(sig.parameters) != 2: - raise UserError("on_handoff must take two arguments: context and input") - - type_adapter = TypeAdapter(input_type) - input_json_schema = type_adapter.json_schema() - else: - type_adapter = None - input_json_schema = {} - if on_handoff is not None: - sig = inspect.signature(on_handoff) - if len(sig.parameters) != 1: - raise UserError("on_handoff must take one argument: context") - - async def _invoke_handoff( - ctx: RunContextWrapper[Any], input_json: str | None = None - ) -> Agent[Any]: - if input_type is not None and type_adapter is not None: - if input_json is None: - _utils.attach_error_to_current_span( - SpanError( - message="Handoff function expected non-null input, but got None", - data={"details": "input_json is None"}, - ) - ) - raise ModelBehaviorError( - "Handoff function expected non-null input, but got None" - ) - - validated_input = _utils.validate_json( - json_str=input_json, - type_adapter=type_adapter, - partial=False, - ) - input_func = cast(OnHandoffWithInput[THandoffInput], on_handoff) - if inspect.iscoroutinefunction(input_func): - await input_func(ctx, validated_input) - else: - input_func(ctx, validated_input) - elif on_handoff is not None: - no_input_func = cast(OnHandoffWithoutInput, on_handoff) - if inspect.iscoroutinefunction(no_input_func): - await no_input_func(ctx) - else: - no_input_func(ctx) - - return agent - - tool_name = tool_name_override or Handoff.default_tool_name(agent) - tool_description = tool_description_override or Handoff.default_tool_description( - agent - ) - - # Always ensure the input JSON schema is in strict mode - # If there is a need, we can make this configurable in the future - input_json_schema = ensure_strict_json_schema(input_json_schema) - - return Handoff( - tool_name=tool_name, - tool_description=tool_description, - input_json_schema=input_json_schema, - on_invoke_handoff=_invoke_handoff, - input_filter=input_filter, - agent_name=agent.name, - ) diff --git a/pkg/hanzo-agent/src/agents/items.py b/pkg/hanzo-agent/src/agents/items.py deleted file mode 100644 index 6cc25d31a..000000000 --- a/pkg/hanzo-agent/src/agents/items.py +++ /dev/null @@ -1,249 +0,0 @@ -from __future__ import annotations - -import abc -import copy -from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar, Union - -from openai.types.responses import ( - Response, - ResponseComputerToolCall, - ResponseFileSearchToolCall, - ResponseFunctionToolCall, - ResponseFunctionWebSearch, - ResponseInputItemParam, - ResponseOutputItem, - ResponseOutputMessage, - ResponseOutputRefusal, - ResponseOutputText, - ResponseStreamEvent, -) -from openai.types.responses.response_input_item_param import ( - ComputerCallOutput, - FunctionCallOutput, -) -from openai.types.responses.response_reasoning_item import ResponseReasoningItem -from pydantic import BaseModel -from typing_extensions import TypeAlias - -from .exceptions import AgentsException, ModelBehaviorError -from .usage import Usage - -if TYPE_CHECKING: - from .agent import Agent - -TResponse = Response -"""A type alias for the Response type from the OpenAI SDK.""" - -TResponseInputItem = ResponseInputItemParam -"""A type alias for the ResponseInputItemParam type from the OpenAI SDK.""" - -TResponseOutputItem = ResponseOutputItem -"""A type alias for the ResponseOutputItem type from the OpenAI SDK.""" - -TResponseStreamEvent = ResponseStreamEvent -"""A type alias for the ResponseStreamEvent type from the OpenAI SDK.""" - -T = TypeVar("T", bound=Union[TResponseOutputItem, TResponseInputItem]) - - -@dataclass -class RunItemBase(Generic[T], abc.ABC): - agent: Agent[Any] - """The agent whose run caused this item to be generated.""" - - raw_item: T - """The raw Responses item from the run. This will always be a either an output item (i.e. - `openai.types.responses.ResponseOutputItem` or an input item - (i.e. `openai.types.responses.ResponseInputItemParam`). - """ - - def to_input_item(self) -> TResponseInputItem: - """Converts this item into an input item suitable for passing to the model.""" - if isinstance(self.raw_item, dict): - # We know that input items are dicts, so we can ignore the type error - return self.raw_item # type: ignore - elif isinstance(self.raw_item, BaseModel): - # All output items are Pydantic models that can be converted to input items. - return self.raw_item.model_dump(exclude_unset=True) # type: ignore - else: - raise AgentsException(f"Unexpected raw item type: {type(self.raw_item)}") - - -@dataclass -class MessageOutputItem(RunItemBase[ResponseOutputMessage]): - """Represents a message from the LLM.""" - - raw_item: ResponseOutputMessage - """The raw response output message.""" - - type: Literal["message_output_item"] = "message_output_item" - - -@dataclass -class HandoffCallItem(RunItemBase[ResponseFunctionToolCall]): - """Represents a tool call for a handoff from one agent to another.""" - - raw_item: ResponseFunctionToolCall - """The raw response function tool call that represents the handoff.""" - - type: Literal["handoff_call_item"] = "handoff_call_item" - - -@dataclass -class HandoffOutputItem(RunItemBase[TResponseInputItem]): - """Represents the output of a handoff.""" - - raw_item: TResponseInputItem - """The raw input item that represents the handoff taking place.""" - - source_agent: Agent[Any] - """The agent that made the handoff.""" - - target_agent: Agent[Any] - """The agent that is being handed off to.""" - - type: Literal["handoff_output_item"] = "handoff_output_item" - - -ToolCallItemTypes: TypeAlias = Union[ - ResponseFunctionToolCall, - ResponseComputerToolCall, - ResponseFileSearchToolCall, - ResponseFunctionWebSearch, -] -"""A type that represents a tool call item.""" - - -@dataclass -class ToolCallItem(RunItemBase[ToolCallItemTypes]): - """Represents a tool call e.g. a function call or computer action call.""" - - raw_item: ToolCallItemTypes - """The raw tool call item.""" - - type: Literal["tool_call_item"] = "tool_call_item" - - -@dataclass -class ToolCallOutputItem(RunItemBase[Union[FunctionCallOutput, ComputerCallOutput]]): - """Represents the output of a tool call.""" - - raw_item: FunctionCallOutput | ComputerCallOutput - """The raw item from the model.""" - - output: str - """The output of the tool call.""" - - type: Literal["tool_call_output_item"] = "tool_call_output_item" - - -@dataclass -class ReasoningItem(RunItemBase[ResponseReasoningItem]): - """Represents a reasoning item.""" - - raw_item: ResponseReasoningItem - """The raw reasoning item.""" - - type: Literal["reasoning_item"] = "reasoning_item" - - -RunItem: TypeAlias = Union[ - MessageOutputItem, - HandoffCallItem, - HandoffOutputItem, - ToolCallItem, - ToolCallOutputItem, - ReasoningItem, -] -"""An item generated by an agent.""" - - -@dataclass -class ModelResponse: - output: list[TResponseOutputItem] - """A list of outputs (messages, tool calls, etc) generated by the model""" - - usage: Usage - """The usage information for the response.""" - - referenceable_id: str | None - """An ID for the response which can be used to refer to the response in subsequent calls to the - model. Not supported by all model providers. - """ - - def to_input_items(self) -> list[TResponseInputItem]: - """Convert the output into a list of input items suitable for passing to the model.""" - # We happen to know that the shape of the Pydantic output items are the same as the - # equivalent TypedDict input items, so we can just convert each one. - # This is also tested via unit tests. - return [it.model_dump(exclude_unset=True) for it in self.output] # type: ignore - - -class ItemHelpers: - @classmethod - def extract_last_content(cls, message: TResponseOutputItem) -> str: - """Extracts the last text content or refusal from a message.""" - if not isinstance(message, ResponseOutputMessage): - return "" - - last_content = message.content[-1] - if isinstance(last_content, ResponseOutputText): - return last_content.text - elif isinstance(last_content, ResponseOutputRefusal): - return last_content.refusal - else: - raise ModelBehaviorError(f"Unexpected content type: {type(last_content)}") - - @classmethod - def extract_last_text(cls, message: TResponseOutputItem) -> str | None: - """Extracts the last text content from a message, if any. Ignores refusals.""" - if isinstance(message, ResponseOutputMessage): - last_content = message.content[-1] - if isinstance(last_content, ResponseOutputText): - return last_content.text - - return None - - @classmethod - def input_to_new_input_list( - cls, input: str | list[TResponseInputItem] - ) -> list[TResponseInputItem]: - """Converts a string or list of input items into a list of input items.""" - if isinstance(input, str): - return [ - { - "content": input, - "role": "user", - } - ] - return copy.deepcopy(input) - - @classmethod - def text_message_outputs(cls, items: list[RunItem]) -> str: - """Concatenates all the text content from a list of message output items.""" - text = "" - for item in items: - if isinstance(item, MessageOutputItem): - text += cls.text_message_output(item) - return text - - @classmethod - def text_message_output(cls, message: MessageOutputItem) -> str: - """Extracts all the text content from a single message output item.""" - text = "" - for item in message.raw_item.content: - if isinstance(item, ResponseOutputText): - text += item.text - return text - - @classmethod - def tool_call_output_item( - cls, tool_call: ResponseFunctionToolCall, output: str - ) -> FunctionCallOutput: - """Creates a tool call output item from a tool call and its output.""" - return { - "call_id": tool_call.call_id, - "output": output, - "type": "function_call_output", - } diff --git a/pkg/hanzo-agent/src/agents/lifecycle.py b/pkg/hanzo-agent/src/agents/lifecycle.py deleted file mode 100644 index 062d43bfd..000000000 --- a/pkg/hanzo-agent/src/agents/lifecycle.py +++ /dev/null @@ -1,107 +0,0 @@ -from typing import Any, Generic - -from .agent import Agent -from .run_context import RunContextWrapper, TContext -from .tool import Tool - - -class RunHooks(Generic[TContext]): - """A class that receives callbacks on various lifecycle events in an agent run. Subclass and - override the methods you need. - """ - - async def on_agent_start( - self, context: RunContextWrapper[TContext], agent: Agent[TContext] - ) -> None: - """Called before the agent is invoked. Called each time the current agent changes.""" - pass - - async def on_agent_end( - self, - context: RunContextWrapper[TContext], - agent: Agent[TContext], - output: Any, - ) -> None: - """Called when the agent produces a final output.""" - pass - - async def on_handoff( - self, - context: RunContextWrapper[TContext], - from_agent: Agent[TContext], - to_agent: Agent[TContext], - ) -> None: - """Called when a handoff occurs.""" - pass - - async def on_tool_start( - self, - context: RunContextWrapper[TContext], - agent: Agent[TContext], - tool: Tool, - ) -> None: - """Called before a tool is invoked.""" - pass - - async def on_tool_end( - self, - context: RunContextWrapper[TContext], - agent: Agent[TContext], - tool: Tool, - result: str, - ) -> None: - """Called after a tool is invoked.""" - pass - - -class AgentHooks(Generic[TContext]): - """A class that receives callbacks on various lifecycle events for a specific agent. You can - set this on `agent.hooks` to receive events for that specific agent. - - Subclass and override the methods you need. - """ - - async def on_start( - self, context: RunContextWrapper[TContext], agent: Agent[TContext] - ) -> None: - """Called before the agent is invoked. Called each time the running agent is changed to this - agent.""" - pass - - async def on_end( - self, - context: RunContextWrapper[TContext], - agent: Agent[TContext], - output: Any, - ) -> None: - """Called when the agent produces a final output.""" - pass - - async def on_handoff( - self, - context: RunContextWrapper[TContext], - agent: Agent[TContext], - source: Agent[TContext], - ) -> None: - """Called when the agent is being handed off to. The `source` is the agent that is handing - off to this agent.""" - pass - - async def on_tool_start( - self, - context: RunContextWrapper[TContext], - agent: Agent[TContext], - tool: Tool, - ) -> None: - """Called before a tool is invoked.""" - pass - - async def on_tool_end( - self, - context: RunContextWrapper[TContext], - agent: Agent[TContext], - tool: Tool, - result: str, - ) -> None: - """Called after a tool is invoked.""" - pass diff --git a/pkg/hanzo-agent/src/agents/logger.py b/pkg/hanzo-agent/src/agents/logger.py deleted file mode 100644 index bd81a8271..000000000 --- a/pkg/hanzo-agent/src/agents/logger.py +++ /dev/null @@ -1,3 +0,0 @@ -import logging - -logger = logging.getLogger("openai.agents") diff --git a/pkg/hanzo-agent/src/agents/memory/__init__.py b/pkg/hanzo-agent/src/agents/memory/__init__.py deleted file mode 100644 index 32f833a6b..000000000 --- a/pkg/hanzo-agent/src/agents/memory/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Memory system for agents. - -This module provides memory capabilities for agents, allowing them to -remember past interactions, learn from experience, and maintain context. -""" - -from .types import MemoryEntry, MemoryType -from .memory import Memory -from .store import MemoryStore, InMemoryMemoryStore, VectorMemoryStore -from .retriever import ( - MemoryRetriever, - SemanticRetriever, - RecencyRetriever, - HybridRetriever, -) - -__all__ = [ - "Memory", - "MemoryEntry", - "MemoryType", - "MemoryStore", - "InMemoryMemoryStore", - "VectorMemoryStore", - "MemoryRetriever", - "SemanticRetriever", - "RecencyRetriever", - "HybridRetriever", -] diff --git a/pkg/hanzo-agent/src/agents/memory/memory.py b/pkg/hanzo-agent/src/agents/memory/memory.py deleted file mode 100644 index 479a1e45d..000000000 --- a/pkg/hanzo-agent/src/agents/memory/memory.py +++ /dev/null @@ -1,349 +0,0 @@ -"""Core memory system for agents.""" - -from __future__ import annotations - -import time -from typing import Any, Callable, Dict, List, Optional - -from ..agent import Agent -from ..items import TResponseInputItem, ItemHelpers -from ..logger import logger -from ..run_context import RunContextWrapper, TContext -from .types import MemoryEntry, MemoryType -from .store import MemoryStore, InMemoryMemoryStore -from .retriever import MemoryRetriever, SemanticRetriever - - -class Memory: - """Memory system for agents.""" - - def __init__( - self, - store: MemoryStore | None = None, - retriever: MemoryRetriever | None = None, - max_entries: int = 1000, - auto_compress: bool = True, - compress_threshold: int = 100, - ): - """Initialize memory system. - - Args: - store: Memory storage backend - retriever: Memory retrieval strategy - max_entries: Maximum number of entries to keep - auto_compress: Whether to automatically compress old memories - compress_threshold: Number of entries before compression - """ - self.store = store or InMemoryMemoryStore() - self.retriever = retriever or SemanticRetriever() - self.max_entries = max_entries - self.auto_compress = auto_compress - self.compress_threshold = compress_threshold - - # Backwards-compatible aliases for tests and existing code - async def add( - self, - content: str, - type: MemoryType = MemoryType.CONVERSATION, - agent_name: str | None = None, - importance: float = 1.0, - metadata: Dict[str, Any] | None = None, - ) -> MemoryEntry: - return await self.remember( - content=content, - type=type, - agent_name=agent_name, - importance=importance, - metadata=metadata, - ) - - async def search( - self, - query: str | None = None, - limit: int = 10, - type: MemoryType | None = None, - agent_name: str | None = None, - min_importance: float = 0.0, - ) -> List[MemoryEntry]: - return await self.recall( - query=query, - type=type, - agent_name=agent_name, - limit=limit, - min_importance=min_importance, - ) - - async def get_all(self) -> List[MemoryEntry]: - return await self.store.list() - - async def remember( - self, - content: str, - type: MemoryType = MemoryType.CONVERSATION, - agent_name: str | None = None, - importance: float = 1.0, - metadata: Dict[str, Any] | None = None, - ) -> MemoryEntry: - """Store a new memory. - - Args: - content: The content to remember - type: Type of memory - agent_name: Name of the agent creating the memory - importance: Importance score - metadata: Additional metadata - - Returns: - The created memory entry - """ - # Check if we need to compress - if self.auto_compress: - count = await self.store.count() - if count >= self.compress_threshold: - await self._compress_memories() - - # Create memory entry - entry = await self.store.add( - content=content, - type=type, - agent_name=agent_name, - importance=importance, - metadata=metadata or {}, - ) - - # Ensure we don't exceed the max_entries after adding - if self.auto_compress: - count_after = await self.store.count() - if count_after > self.max_entries: - await self._compress_memories() - - logger.debug(f"Stored memory: {entry.id} ({type.value})") - - return entry - - async def recall( - self, - query: str | None = None, - type: MemoryType | None = None, - agent_name: str | None = None, - limit: int = 10, - min_importance: float = 0.0, - ) -> List[MemoryEntry]: - """Retrieve memories. - - Args: - query: Query for semantic search - type: Filter by memory type - agent_name: Filter by agent name - limit: Maximum number of results - min_importance: Minimum importance threshold - - Returns: - List of matching memories - """ - # Get all memories matching filters - memories = await self.store.list( - type=type, - agent_name=agent_name, - min_importance=min_importance, - ) - - # Use retriever to find best matches - if query and self.retriever: - memories = await self.retriever.retrieve( - query=query, - memories=memories, - limit=limit, - ) - else: - # Just return most recent - memories = sorted(memories, key=lambda m: m.timestamp, reverse=True)[:limit] - - # Update access counts - for memory in memories: - memory.access_count += 1 - memory.last_accessed = time.time() - await self.store.update(memory) - - return memories - - async def forget(self, memory_id: str) -> None: - """Remove a specific memory.""" - await self.store.delete(memory_id) - logger.debug(f"Deleted memory: {memory_id}") - - async def clear( - self, - type: MemoryType | None = None, - agent_name: str | None = None, - ) -> int: - """Clear memories. - - Args: - type: Clear only memories of this type - agent_name: Clear only memories from this agent - - Returns: - Number of memories cleared - """ - memories = await self.store.list(type=type, agent_name=agent_name) - - for memory in memories: - await self.store.delete(memory.id) - - logger.debug(f"Cleared {len(memories)} memories") - return len(memories) - - async def reflect( - self, - agent: Agent[TContext], - context: RunContextWrapper[TContext] | None = None, - recent_limit: int = 20, - ) -> MemoryEntry: - """Generate a reflection based on recent memories. - - This allows the agent to synthesize and learn from recent experiences. - - Args: - agent: The agent doing the reflection - context: Optional context - recent_limit: Number of recent memories to consider - - Returns: - The reflection memory entry - """ - # Get recent memories - recent = await self.recall( - agent_name=agent.name, - limit=recent_limit, - ) - - if not recent: - content = "No recent memories to reflect on." - else: - # Build reflection prompt - memory_text = "\n".join([f"- [{m.type.value}] {m.content}" for m in recent]) - - # Use agent to generate reflection - from ..run import Runner - - reflection_agent = agent.clone( - instructions=( - "You are reflecting on recent memories and experiences. " - "Synthesize key insights, patterns, and learnings." - ), - ) - - result = await Runner.run( - starting_agent=reflection_agent, - input=f"Reflect on these recent memories:\n\n{memory_text}", - context=context.context if context else None, - max_turns=1, - ) - - content = ItemHelpers.text_message_outputs(result.new_items) - - # Store reflection - reflection = await self.remember( - content=content, - type=MemoryType.REFLECTION, - agent_name=agent.name, - importance=0.8, - metadata={"recent_memory_count": len(recent)}, - ) - - return reflection - - async def _compress_memories(self) -> None: - """Compress old memories to save space.""" - # Get all memories sorted by importance and recency - memories = await self.store.list() - - # Score memories - now = time.time() - scored = [] - - for memory in memories: - # Calculate score based on importance, recency, and access - recency_score = 1.0 / (1.0 + (now - memory.timestamp) / 86400) # Days - access_score = min(1.0, memory.access_count / 10) - - score = memory.importance * 0.5 + recency_score * 0.3 + access_score * 0.2 - - scored.append((score, memory)) - - # Sort by score - scored.sort(key=lambda x: x[0], reverse=True) - - # Keep top memories - to_keep = min(self.max_entries, len(scored)) - to_delete = len(scored) - to_keep - - if to_delete > 0: - # Delete lowest scored memories - for _, memory in scored[to_keep:]: - await self.store.delete(memory.id) - - logger.debug(f"Compressed memories: deleted {to_delete} entries") - - def create_agent_wrapper(self, agent: Agent[TContext]) -> Agent[TContext]: - """Create an agent wrapper with memory capabilities. - - This returns a new agent that automatically stores and retrieves memories. - """ - memory = self - - async def memory_instructions( - ctx: RunContextWrapper[TContext], agent: Agent[TContext] - ) -> str: - # Get base instructions - base = await agent.get_system_prompt(ctx) - - # Retrieve relevant memories - if ctx and hasattr(ctx, "last_message"): - memories = await memory.recall( - query=ctx.last_message, - agent_name=agent.name, - limit=5, - ) - - if memories: - memory_text = "\n".join([f"- {m.content}" for m in memories]) - - return f"{base}\n\nRelevant memories:\n{memory_text}" - - return base - - # Create memory-enabled agent - return agent.clone( - instructions=memory_instructions, - hooks=MemoryAgentHooks(memory, agent.name), - ) - - -class MemoryAgentHooks: - """Agent hooks for automatic memory management.""" - - def __init__(self, memory: Memory, agent_name: str): - self.memory = memory - self.agent_name = agent_name - - async def on_start( - self, context: RunContextWrapper[Any], agent: Agent[Any] - ) -> None: - """Store conversation start.""" - await self.memory.remember( - content="Conversation started", - type=MemoryType.CONVERSATION, - agent_name=self.agent_name, - importance=0.3, - ) - - async def on_end(self, context: RunContextWrapper[Any], agent: Agent[Any]) -> None: - """Store conversation end.""" - await self.memory.remember( - content="Conversation ended", - type=MemoryType.CONVERSATION, - agent_name=self.agent_name, - importance=0.3, - ) diff --git a/pkg/hanzo-agent/src/agents/memory/retriever.py b/pkg/hanzo-agent/src/agents/memory/retriever.py deleted file mode 100644 index f7cdfdf63..000000000 --- a/pkg/hanzo-agent/src/agents/memory/retriever.py +++ /dev/null @@ -1,205 +0,0 @@ -"""Memory retrieval strategies.""" - -from __future__ import annotations - -import time -from abc import ABC, abstractmethod -from typing import List, Optional - -from .types import MemoryEntry -from ..logger import logger - - -class MemoryRetriever(ABC): - """Abstract base class for memory retrieval strategies.""" - - @abstractmethod - async def retrieve( - self, - query: str, - memories: List[MemoryEntry], - limit: int = 10, - ) -> List[MemoryEntry]: - """Retrieve relevant memories. - - Args: - query: The query to match against - memories: Pool of memories to search - limit: Maximum number of results - - Returns: - List of relevant memories - """ - pass - - -class SemanticRetriever(MemoryRetriever): - """Retrieves memories based on semantic similarity.""" - - def __init__(self, embedding_model: str | None = None): - self.embedding_model = embedding_model - - async def retrieve( - self, - query: str, - memories: List[MemoryEntry], - limit: int = 10, - ) -> List[MemoryEntry]: - """Retrieve semantically similar memories.""" - if not memories: - return [] - - # If memories have embeddings, use vector similarity - if any(m.embedding for m in memories): - # Get query embedding - query_embedding = await self._get_embedding(query) - - if query_embedding: - # Calculate similarities - scores = [] - for memory in memories: - if memory.embedding: - similarity = self._cosine_similarity( - query_embedding, - memory.embedding, - ) - scores.append((memory, similarity)) - - # Sort by similarity - scores.sort(key=lambda x: x[1], reverse=True) - - return [memory for memory, _ in scores[:limit]] - - # Fallback to keyword matching - return self._keyword_search(query, memories, limit) - - async def _get_embedding(self, text: str) -> List[float] | None: - """Get embedding for text.""" - # This is a placeholder - implement based on your embedding service - return None - - def _cosine_similarity(self, vec1: List[float], vec2: List[float]) -> float: - """Calculate cosine similarity between two vectors.""" - import numpy as np - - v1 = np.array(vec1) - v2 = np.array(vec2) - - return float(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))) - - def _keyword_search( - self, - query: str, - memories: List[MemoryEntry], - limit: int, - ) -> List[MemoryEntry]: - """Simple keyword-based search.""" - query_words = set(query.lower().split()) - - scores = [] - for memory in memories: - content_words = set(memory.content.lower().split()) - - # Calculate Jaccard similarity - intersection = len(query_words & content_words) - union = len(query_words | content_words) - - if union > 0: - similarity = intersection / union - scores.append((memory, similarity)) - - # Sort by similarity - scores.sort(key=lambda x: x[1], reverse=True) - - return [memory for memory, _ in scores[:limit]] - - -class RecencyRetriever(MemoryRetriever): - """Retrieves memories based on recency.""" - - def __init__(self, decay_factor: float = 0.99): - self.decay_factor = decay_factor - - async def retrieve( - self, - query: str, - memories: List[MemoryEntry], - limit: int = 10, - ) -> List[MemoryEntry]: - """Retrieve most recent memories.""" - # Sort by timestamp - sorted_memories = sorted( - memories, - key=lambda m: m.timestamp, - reverse=True, - ) - - return sorted_memories[:limit] - - -class HybridRetriever(MemoryRetriever): - """Combines multiple retrieval strategies.""" - - def __init__( - self, - semantic_weight: float = 0.6, - recency_weight: float = 0.2, - importance_weight: float = 0.2, - embedding_model: str | None = None, - ): - self.semantic_weight = semantic_weight - self.recency_weight = recency_weight - self.importance_weight = importance_weight - self.semantic_retriever = SemanticRetriever(embedding_model) - self.recency_retriever = RecencyRetriever() - - async def retrieve( - self, - query: str, - memories: List[MemoryEntry], - limit: int = 10, - ) -> List[MemoryEntry]: - """Retrieve using hybrid scoring.""" - if not memories: - return [] - - # Get semantic scores - semantic_results = await self.semantic_retriever.retrieve( - query, memories, len(memories) - ) - semantic_scores = { - m.id: 1.0 - (i / len(semantic_results)) - for i, m in enumerate(semantic_results) - } - - # Get recency scores - now = time.time() - recency_scores = {} - for memory in memories: - age_days = (now - memory.timestamp) / 86400 - recency_scores[memory.id] = 1.0 / (1.0 + age_days) - - # Get importance scores - importance_scores = {m.id: m.importance for m in memories} - - # Combine scores - final_scores = {} - for memory in memories: - semantic = semantic_scores.get(memory.id, 0.0) - recency = recency_scores.get(memory.id, 0.0) - importance = importance_scores.get(memory.id, 0.0) - - final_scores[memory.id] = ( - semantic * self.semantic_weight - + recency * self.recency_weight - + importance * self.importance_weight - ) - - # Sort by final score - sorted_memories = sorted( - memories, - key=lambda m: final_scores[m.id], - reverse=True, - ) - - return sorted_memories[:limit] diff --git a/pkg/hanzo-agent/src/agents/memory/store.py b/pkg/hanzo-agent/src/agents/memory/store.py deleted file mode 100644 index e697bf992..000000000 --- a/pkg/hanzo-agent/src/agents/memory/store.py +++ /dev/null @@ -1,458 +0,0 @@ -"""Memory storage backends.""" - -from __future__ import annotations - -import uuid -from abc import ABC, abstractmethod -from typing import Any, Dict, List, Optional - -import numpy as np - -from .types import MemoryEntry, MemoryType -from ..logger import logger - - -class MemoryStore(ABC): - """Abstract base class for memory stores.""" - - @abstractmethod - async def add( - self, - content: str, - type: MemoryType, - agent_name: str | None = None, - importance: float = 1.0, - metadata: Dict[str, Any] | None = None, - embedding: List[float] | None = None, - ) -> MemoryEntry: - """Add a new memory.""" - pass - - @abstractmethod - async def get(self, memory_id: str) -> MemoryEntry | None: - """Get a memory by ID.""" - pass - - @abstractmethod - async def update(self, memory: MemoryEntry) -> None: - """Update an existing memory.""" - pass - - @abstractmethod - async def delete(self, memory_id: str) -> None: - """Delete a memory.""" - pass - - @abstractmethod - async def list( - self, - type: MemoryType | None = None, - agent_name: str | None = None, - min_importance: float = 0.0, - limit: int | None = None, - ) -> List[MemoryEntry]: - """List memories with filters.""" - pass - - @abstractmethod - async def count( - self, - type: MemoryType | None = None, - agent_name: str | None = None, - ) -> int: - """Count memories.""" - pass - - @abstractmethod - async def search_by_embedding( - self, - embedding: List[float], - limit: int = 10, - threshold: float = 0.0, - ) -> List[tuple[MemoryEntry, float]]: - """Search memories by embedding similarity.""" - pass - - -class InMemoryMemoryStore(MemoryStore): - """In-memory memory store for development.""" - - def __init__(self): - self.memories: Dict[str, MemoryEntry] = {} - - async def add( - self, - content: str, - type: MemoryType, - agent_name: str | None = None, - importance: float = 1.0, - metadata: Dict[str, Any] | None = None, - embedding: List[float] | None = None, - ) -> MemoryEntry: - """Add a new memory.""" - memory = MemoryEntry( - id=str(uuid.uuid4()), - type=type, - content=content, - agent_name=agent_name, - importance=importance, - metadata=metadata or {}, - embedding=embedding, - ) - - self.memories[memory.id] = memory - return memory - - async def get(self, memory_id: str) -> MemoryEntry | None: - """Get a memory by ID.""" - return self.memories.get(memory_id) - - async def update(self, memory: MemoryEntry) -> None: - """Update an existing memory.""" - if memory.id in self.memories: - self.memories[memory.id] = memory - - async def delete(self, memory_id: str) -> None: - """Delete a memory.""" - self.memories.pop(memory_id, None) - - async def list( - self, - type: MemoryType | None = None, - agent_name: str | None = None, - min_importance: float = 0.0, - limit: int | None = None, - ) -> List[MemoryEntry]: - """List memories with filters.""" - memories = list(self.memories.values()) - - # Apply filters - if type: - memories = [m for m in memories if m.type == type] - if agent_name: - memories = [m for m in memories if m.agent_name == agent_name] - if min_importance > 0: - memories = [m for m in memories if m.importance >= min_importance] - - # Sort by timestamp (newest first) - memories.sort(key=lambda m: m.timestamp, reverse=True) - - # Apply limit - if limit: - memories = memories[:limit] - - return memories - - async def count( - self, - type: MemoryType | None = None, - agent_name: str | None = None, - ) -> int: - """Count memories.""" - memories = await self.list(type=type, agent_name=agent_name) - return len(memories) - - async def search_by_embedding( - self, - embedding: List[float], - limit: int = 10, - threshold: float = 0.0, - ) -> List[tuple[MemoryEntry, float]]: - """Search memories by embedding similarity.""" - if not embedding: - return [] - - results = [] - query_vec = np.array(embedding) - - for memory in self.memories.values(): - if not memory.embedding: - continue - - # Calculate cosine similarity - memory_vec = np.array(memory.embedding) - similarity = np.dot(query_vec, memory_vec) / ( - np.linalg.norm(query_vec) * np.linalg.norm(memory_vec) - ) - - if similarity >= threshold: - results.append((memory, float(similarity))) - - # Sort by similarity - results.sort(key=lambda x: x[1], reverse=True) - - return results[:limit] - - -class VectorMemoryStore(MemoryStore): - """Vector database backed memory store.""" - - def __init__( - self, - collection_name: str = "agent_memories", - embedding_model: str | None = None, - **vector_db_kwargs, - ): - """Initialize vector memory store. - - Args: - collection_name: Name of the vector collection - embedding_model: Model to use for embeddings - **vector_db_kwargs: Additional arguments for vector database - """ - self.collection_name = collection_name - self.embedding_model = embedding_model - self.vector_db_kwargs = vector_db_kwargs - self._client = None - self._collection = None - - async def _get_client(self): - """Get or create vector database client.""" - if self._client is None: - try: - import chromadb - except ImportError: - raise ImportError( - "Vector memory store requires 'chromadb' package. " - "Install with: pip install chromadb" - ) - - self._client = chromadb.Client(**self.vector_db_kwargs) - self._collection = self._client.get_or_create_collection( - name=self.collection_name, - metadata={"hnsw:space": "cosine"}, - ) - - return self._client, self._collection - - async def _get_embedding(self, text: str) -> List[float]: - """Get embedding for text.""" - if self.embedding_model: - # Use specified embedding model - # This is a placeholder - implement based on your embedding service - raise NotImplementedError("Custom embedding models not yet implemented") - else: - # Use ChromaDB's default embedding - return None - - async def add( - self, - content: str, - type: MemoryType, - agent_name: str | None = None, - importance: float = 1.0, - metadata: Dict[str, Any] | None = None, - embedding: List[float] | None = None, - ) -> MemoryEntry: - """Add a new memory.""" - _, collection = await self._get_client() - - memory = MemoryEntry( - id=str(uuid.uuid4()), - type=type, - content=content, - agent_name=agent_name, - importance=importance, - metadata=metadata or {}, - embedding=embedding, - ) - - # Prepare metadata for ChromaDB - chroma_metadata = { - "type": type.value, - "agent_name": agent_name or "", - "importance": importance, - "timestamp": memory.timestamp, - **memory.metadata, - } - - # Add to vector database - collection.add( - ids=[memory.id], - documents=[content], - metadatas=[chroma_metadata], - embeddings=[embedding] if embedding else None, - ) - - return memory - - async def get(self, memory_id: str) -> MemoryEntry | None: - """Get a memory by ID.""" - _, collection = await self._get_client() - - result = collection.get(ids=[memory_id]) - - if not result["ids"]: - return None - - # Reconstruct memory entry - metadata = result["metadatas"][0] - - return MemoryEntry( - id=memory_id, - type=MemoryType(metadata["type"]), - content=result["documents"][0], - agent_name=metadata.get("agent_name") or None, - importance=metadata.get("importance", 1.0), - timestamp=metadata.get("timestamp", 0), - metadata={ - k: v - for k, v in metadata.items() - if k not in ["type", "agent_name", "importance", "timestamp"] - }, - embedding=result.get("embeddings", [None])[0], - ) - - async def update(self, memory: MemoryEntry) -> None: - """Update an existing memory.""" - _, collection = await self._get_client() - - # Update in vector database - chroma_metadata = { - "type": memory.type.value, - "agent_name": memory.agent_name or "", - "importance": memory.importance, - "timestamp": memory.timestamp, - "access_count": memory.access_count, - "last_accessed": memory.last_accessed or 0, - **memory.metadata, - } - - collection.update( - ids=[memory.id], - documents=[memory.content], - metadatas=[chroma_metadata], - embeddings=[memory.embedding] if memory.embedding else None, - ) - - async def delete(self, memory_id: str) -> None: - """Delete a memory.""" - _, collection = await self._get_client() - collection.delete(ids=[memory_id]) - - async def list( - self, - type: MemoryType | None = None, - agent_name: str | None = None, - min_importance: float = 0.0, - limit: int | None = None, - ) -> List[MemoryEntry]: - """List memories with filters.""" - _, collection = await self._get_client() - - # Build where clause - where = {} - if type: - where["type"] = type.value - if agent_name: - where["agent_name"] = agent_name - if min_importance > 0: - where["importance"] = {"$gte": min_importance} - - # Query collection - result = collection.get( - where=where if where else None, - limit=limit, - ) - - # Convert to memory entries - memories = [] - for i in range(len(result["ids"])): - metadata = result["metadatas"][i] - - memory = MemoryEntry( - id=result["ids"][i], - type=MemoryType(metadata["type"]), - content=result["documents"][i], - agent_name=metadata.get("agent_name") or None, - importance=metadata.get("importance", 1.0), - timestamp=metadata.get("timestamp", 0), - access_count=metadata.get("access_count", 0), - last_accessed=metadata.get("last_accessed"), - metadata={ - k: v - for k, v in metadata.items() - if k - not in [ - "type", - "agent_name", - "importance", - "timestamp", - "access_count", - "last_accessed", - ] - }, - embedding=result.get("embeddings", [None] * len(result["ids"]))[i], - ) - memories.append(memory) - - # Sort by timestamp - memories.sort(key=lambda m: m.timestamp, reverse=True) - - return memories - - async def count( - self, - type: MemoryType | None = None, - agent_name: str | None = None, - ) -> int: - """Count memories.""" - memories = await self.list(type=type, agent_name=agent_name) - return len(memories) - - async def search_by_embedding( - self, - embedding: List[float], - limit: int = 10, - threshold: float = 0.0, - ) -> List[tuple[MemoryEntry, float]]: - """Search memories by embedding similarity.""" - _, collection = await self._get_client() - - # Query by embedding - result = collection.query( - query_embeddings=[embedding], - n_results=limit, - ) - - if not result["ids"][0]: - return [] - - # Convert to memory entries with scores - memories_with_scores = [] - - for i in range(len(result["ids"][0])): - metadata = result["metadatas"][0][i] - distance = result["distances"][0][i] - - # Convert distance to similarity (1 - normalized distance) - similarity = 1.0 - (distance / 2.0) # Cosine distance is [0, 2] - - if similarity >= threshold: - memory = MemoryEntry( - id=result["ids"][0][i], - type=MemoryType(metadata["type"]), - content=result["documents"][0][i], - agent_name=metadata.get("agent_name") or None, - importance=metadata.get("importance", 1.0), - timestamp=metadata.get("timestamp", 0), - access_count=metadata.get("access_count", 0), - last_accessed=metadata.get("last_accessed"), - metadata={ - k: v - for k, v in metadata.items() - if k - not in [ - "type", - "agent_name", - "importance", - "timestamp", - "access_count", - "last_accessed", - ] - }, - ) - - memories_with_scores.append((memory, similarity)) - - return memories_with_scores diff --git a/pkg/hanzo-agent/src/agents/memory/types.py b/pkg/hanzo-agent/src/agents/memory/types.py deleted file mode 100644 index 8a481808f..000000000 --- a/pkg/hanzo-agent/src/agents/memory/types.py +++ /dev/null @@ -1,70 +0,0 @@ -"""Memory types and data structures.""" - -from __future__ import annotations - -import time -from dataclasses import dataclass, field -from enum import Enum -from typing import Any, Dict, List, Optional - - -class MemoryType(Enum): - """Type of memory entry.""" - - SHORT_TERM = "short_term" - LONG_TERM = "long_term" - WORKING = "working" - EPISODIC = "episodic" - SEMANTIC = "semantic" - CONVERSATION = "conversation" - FACT = "fact" - PROCEDURE = "procedure" - EPISODE = "episode" - REFLECTION = "reflection" - - -@dataclass -class MemoryEntry: - """A single memory entry.""" - - id: str - type: MemoryType - content: Any - metadata: Dict[str, Any] = field(default_factory=dict) - embedding: Optional[List[float]] = None - timestamp: float = field(default_factory=time.time) - access_count: int = 0 - last_accessed: Optional[float] = None - agent_name: Optional[str] = None - importance: float = 1.0 - - def to_dict(self) -> Dict[str, Any]: - """Convert to dictionary.""" - return { - "id": self.id, - "type": self.type.value, - "content": self.content, - "metadata": self.metadata, - "embedding": self.embedding, - "timestamp": self.timestamp, - "access_count": self.access_count, - "last_accessed": self.last_accessed, - "agent_name": self.agent_name, - "importance": self.importance, - } - - @classmethod - def from_dict(cls, data: Dict[str, Any]) -> MemoryEntry: - """Create from dictionary.""" - return cls( - id=data["id"], - type=MemoryType(data["type"]), - content=data["content"], - metadata=data.get("metadata", {}), - embedding=data.get("embedding"), - timestamp=data.get("timestamp", time.time()), - access_count=data.get("access_count", 0), - last_accessed=data.get("last_accessed"), - agent_name=data.get("agent_name"), - importance=data.get("importance", 1.0), - ) diff --git a/pkg/hanzo-agent/src/agents/model_settings.py b/pkg/hanzo-agent/src/agents/model_settings.py deleted file mode 100644 index 8545f99ad..000000000 --- a/pkg/hanzo-agent/src/agents/model_settings.py +++ /dev/null @@ -1,57 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from typing import Literal - - -@dataclass -class ModelSettings: - """Settings to use when calling an LLM. - - This class holds optional model configuration parameters (e.g. temperature, - top_p, penalties, truncation, etc.). - - Not all models/providers support all of these parameters, so please check the API documentation - for the specific model and provider you are using. - """ - - temperature: float | None = None - """The temperature to use when calling the model.""" - - top_p: float | None = None - """The top_p to use when calling the model.""" - - frequency_penalty: float | None = None - """The frequency penalty to use when calling the model.""" - - presence_penalty: float | None = None - """The presence penalty to use when calling the model.""" - - tool_choice: Literal["auto", "required", "none"] | str | None = None - """The tool choice to use when calling the model.""" - - parallel_tool_calls: bool | None = False - """Whether to use parallel tool calls when calling the model.""" - - truncation: Literal["auto", "disabled"] | None = None - """The truncation strategy to use when calling the model.""" - - max_tokens: int | None = None - """The maximum number of output tokens to generate.""" - - def resolve(self, override: ModelSettings | None) -> ModelSettings: - """Produce a new ModelSettings by overlaying any non-None values from the - override on top of this instance.""" - if override is None: - return self - return ModelSettings( - temperature=override.temperature or self.temperature, - top_p=override.top_p or self.top_p, - frequency_penalty=override.frequency_penalty or self.frequency_penalty, - presence_penalty=override.presence_penalty or self.presence_penalty, - tool_choice=override.tool_choice or self.tool_choice, - parallel_tool_calls=override.parallel_tool_calls - or self.parallel_tool_calls, - truncation=override.truncation or self.truncation, - max_tokens=override.max_tokens or self.max_tokens, - ) diff --git a/pkg/hanzo-agent/src/agents/models/__init__.py b/pkg/hanzo-agent/src/agents/models/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-agent/src/agents/models/_openai_shared.py b/pkg/hanzo-agent/src/agents/models/_openai_shared.py deleted file mode 100644 index 2e1450187..000000000 --- a/pkg/hanzo-agent/src/agents/models/_openai_shared.py +++ /dev/null @@ -1,34 +0,0 @@ -from __future__ import annotations - -from openai import AsyncOpenAI - -_default_openai_key: str | None = None -_default_openai_client: AsyncOpenAI | None = None -_use_responses_by_default: bool = True - - -def set_default_openai_key(key: str) -> None: - global _default_openai_key - _default_openai_key = key - - -def get_default_openai_key() -> str | None: - return _default_openai_key - - -def set_default_openai_client(client: AsyncOpenAI) -> None: - global _default_openai_client - _default_openai_client = client - - -def get_default_openai_client() -> AsyncOpenAI | None: - return _default_openai_client - - -def set_use_responses_by_default(use_responses: bool) -> None: - global _use_responses_by_default - _use_responses_by_default = use_responses - - -def get_use_responses_by_default() -> bool: - return _use_responses_by_default diff --git a/pkg/hanzo-agent/src/agents/models/fake_id.py b/pkg/hanzo-agent/src/agents/models/fake_id.py deleted file mode 100644 index 0565b0a7b..000000000 --- a/pkg/hanzo-agent/src/agents/models/fake_id.py +++ /dev/null @@ -1,5 +0,0 @@ -FAKE_RESPONSES_ID = "__fake_id__" -"""This is a placeholder ID used to fill in the `id` field in Responses API related objects. It's -useful when you're creating Responses objects from non-Responses APIs, e.g. the OpenAI Chat -Completions API or other LLM providers. -""" diff --git a/pkg/hanzo-agent/src/agents/models/hanzo_node_provider.py b/pkg/hanzo-agent/src/agents/models/hanzo_node_provider.py deleted file mode 100644 index 314e66daf..000000000 --- a/pkg/hanzo-agent/src/agents/models/hanzo_node_provider.py +++ /dev/null @@ -1,104 +0,0 @@ -""" -Hanzo Node Model Provider for direct integration with hanzod at port 3690. -""" - -import os -from typing import Optional - -from openai import AsyncOpenAI - -from ..models.interface import Model, ModelProvider -from ..models.openai_chatcompletions import OpenAIChatCompletionsModel - - -class HanzoNodeProvider(ModelProvider): - """ - Model provider that connects directly to Hanzo Node (hanzod) at port 3690. - - This provider enables direct integration with the local Hanzo node for: - - Local LLM inference - - Embeddings generation - - Vector search capabilities - """ - - def __init__( - self, - base_url: Optional[str] = None, - api_key: Optional[str] = None, - port: int = 3690, - ): - """ - Initialize Hanzo Node provider. - - Args: - base_url: Override base URL (defaults to localhost with port) - api_key: API key for authentication (optional for local node) - port: Port number for hanzod (default: 3690) - """ - # Use provided base_url or construct from port - if base_url is None: - base_url = f"http://localhost:{port}/v1" - - # Use provided api_key or default for local node - if api_key is None: - api_key = os.getenv("HANZO_NODE_API_KEY", "sk-local-node") - - self.base_url = base_url - self.client = AsyncOpenAI( - base_url=base_url, - api_key=api_key, - ) - print(f"Configured Hanzo Node at: {base_url}") - - def get_model(self, model_name: Optional[str] = None) -> Model: - """ - Get a model instance for the specified model name. - - Args: - model_name: Name of the model (defaults to gpt-oss:20b for local inference) - - Returns: - Model instance configured for Hanzo Node - """ - # Default to local OSS model if not specified - model = model_name or "gpt-oss:20b" - print(f"Using Hanzo Node model: {model}") - - return OpenAIChatCompletionsModel(model=model, openai_client=self.client) - - @property - def is_local(self) -> bool: - """Check if this is a local node connection.""" - return "localhost" in self.base_url or "127.0.0.1" in self.base_url - - async def health_check(self) -> bool: - """ - Check if the Hanzo node is healthy and responding. - - Returns: - True if node is healthy, False otherwise - """ - try: - # Try to list models as a health check - await self.client.models.list() - return True - except Exception as e: - print(f"Health check failed: {e}") - return False - - -# Convenience function to create a Hanzo Node provider -def create_hanzo_node_provider( - port: int = 3690, api_key: Optional[str] = None -) -> HanzoNodeProvider: - """ - Create a Hanzo Node provider with default settings. - - Args: - port: Port number for hanzod (default: 3690) - api_key: Optional API key - - Returns: - Configured HanzoNodeProvider instance - """ - return HanzoNodeProvider(port=port, api_key=api_key) diff --git a/pkg/hanzo-agent/src/agents/models/interface.py b/pkg/hanzo-agent/src/agents/models/interface.py deleted file mode 100644 index e9a8700ce..000000000 --- a/pkg/hanzo-agent/src/agents/models/interface.py +++ /dev/null @@ -1,107 +0,0 @@ -from __future__ import annotations - -import abc -import enum -from collections.abc import AsyncIterator -from typing import TYPE_CHECKING - -from ..agent_output import AgentOutputSchema -from ..handoffs import Handoff -from ..items import ModelResponse, TResponseInputItem, TResponseStreamEvent -from ..tool import Tool - -if TYPE_CHECKING: - from ..model_settings import ModelSettings - - -class ModelTracing(enum.Enum): - DISABLED = 0 - """Tracing is disabled entirely.""" - - ENABLED = 1 - """Tracing is enabled, and all data is included.""" - - ENABLED_WITHOUT_DATA = 2 - """Tracing is enabled, but inputs/outputs are not included.""" - - def is_disabled(self) -> bool: - return self == ModelTracing.DISABLED - - def include_data(self) -> bool: - return self == ModelTracing.ENABLED - - -class Model(abc.ABC): - """The base interface for calling an LLM.""" - - @abc.abstractmethod - async def get_response( - self, - system_instructions: str | None, - input: str | list[TResponseInputItem], - model_settings: ModelSettings, - tools: list[Tool], - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - tracing: ModelTracing, - ) -> ModelResponse: - """Get a response from the model. - - Args: - system_instructions: The system instructions to use. - input: The input items to the model, in OpenAI Responses format. - model_settings: The model settings to use. - tools: The tools available to the model. - output_schema: The output schema to use. - handoffs: The handoffs available to the model. - tracing: Tracing configuration. - - Returns: - The full model response. - """ - pass - - @abc.abstractmethod - def stream_response( - self, - system_instructions: str | None, - input: str | list[TResponseInputItem], - model_settings: ModelSettings, - tools: list[Tool], - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - tracing: ModelTracing, - ) -> AsyncIterator[TResponseStreamEvent]: - """Stream a response from the model. - - Args: - system_instructions: The system instructions to use. - input: The input items to the model, in OpenAI Responses format. - model_settings: The model settings to use. - tools: The tools available to the model. - output_schema: The output schema to use. - handoffs: The handoffs available to the model. - tracing: Tracing configuration. - - Returns: - An iterator of response stream events, in OpenAI Responses format. - """ - pass - - -class ModelProvider(abc.ABC): - """The base interface for a model provider. - - Model provider is responsible for looking up Models by name. - """ - - @abc.abstractmethod - def get_model(self, model_name: str | None) -> Model: - """Get a model by name. - - Args: - model_name: The name of the model to get. - - Returns: - The model. - """ diff --git a/pkg/hanzo-agent/src/agents/models/openai_chatcompletions.py b/pkg/hanzo-agent/src/agents/models/openai_chatcompletions.py deleted file mode 100644 index 7877fa2b4..000000000 --- a/pkg/hanzo-agent/src/agents/models/openai_chatcompletions.py +++ /dev/null @@ -1,1063 +0,0 @@ -from __future__ import annotations - -import dataclasses -import json -import time -from collections.abc import AsyncIterator, Iterable -from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Any, Literal, cast, overload - -from openai import NOT_GIVEN, AsyncOpenAI, AsyncStream, NotGiven -from openai.types import ChatModel -from openai.types.chat import ( - ChatCompletion, - ChatCompletionAssistantMessageParam, - ChatCompletionChunk, - ChatCompletionContentPartImageParam, - ChatCompletionContentPartParam, - ChatCompletionContentPartTextParam, - ChatCompletionDeveloperMessageParam, - ChatCompletionMessage, - ChatCompletionMessageParam, - ChatCompletionMessageToolCallParam, - ChatCompletionSystemMessageParam, - ChatCompletionToolChoiceOptionParam, - ChatCompletionToolMessageParam, - ChatCompletionUserMessageParam, -) -from openai.types.chat.chat_completion_tool_param import ChatCompletionToolParam -from openai.types.chat.completion_create_params import ResponseFormat -from openai.types.completion_usage import CompletionUsage -from openai.types.responses import ( - EasyInputMessageParam, - Response, - ResponseCompletedEvent, - ResponseContentPartAddedEvent, - ResponseContentPartDoneEvent, - ResponseCreatedEvent, - ResponseFileSearchToolCallParam, - ResponseFunctionCallArgumentsDeltaEvent, - ResponseFunctionToolCall, - ResponseFunctionToolCallParam, - ResponseInputContentParam, - ResponseInputImageParam, - ResponseInputTextParam, - ResponseOutputItem, - ResponseOutputItemAddedEvent, - ResponseOutputItemDoneEvent, - ResponseOutputMessage, - ResponseOutputMessageParam, - ResponseOutputRefusal, - ResponseOutputText, - ResponseRefusalDeltaEvent, - ResponseTextDeltaEvent, - ResponseUsage, -) -from openai.types.responses.response_input_param import ( - FunctionCallOutput, - ItemReference, - Message, -) -from openai.types.responses.response_usage import OutputTokensDetails - -from .. import _debug -from ..agent_output import AgentOutputSchema -from ..exceptions import AgentsException, UserError -from ..handoffs import Handoff -from ..items import ( - ModelResponse, - TResponseInputItem, - TResponseOutputItem, - TResponseStreamEvent, -) -from ..logger import logger -from ..tool import FunctionTool, Tool -from ..tracing import generation_span -from ..tracing.span_data import GenerationSpanData -from ..tracing.spans import Span -from ..usage import Usage -from ..version import __version__ -from .fake_id import FAKE_RESPONSES_ID -from .interface import Model, ModelTracing - -if TYPE_CHECKING: - from ..model_settings import ModelSettings - - -_USER_AGENT = f"Agents/Python {__version__}" -_HEADERS = {"User-Agent": _USER_AGENT} - - -@dataclass -class _StreamingState: - started: bool = False - text_content_index_and_output: tuple[int, ResponseOutputText] | None = None - refusal_content_index_and_output: tuple[int, ResponseOutputRefusal] | None = None - function_calls: dict[int, ResponseFunctionToolCall] = field(default_factory=dict) - - -class OpenAIChatCompletionsModel(Model): - def __init__( - self, - model: str | ChatModel, - openai_client: AsyncOpenAI, - ) -> None: - self.model = model - self._client = openai_client - - def _non_null_or_not_given(self, value: Any) -> Any: - return value if value is not None else NOT_GIVEN - - async def get_response( - self, - system_instructions: str | None, - input: str | list[TResponseInputItem], - model_settings: ModelSettings, - tools: list[Tool], - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - tracing: ModelTracing, - ) -> ModelResponse: - with generation_span( - model=str(self.model), - model_config=dataclasses.asdict(model_settings) - | {"base_url": str(self._client.base_url)}, - disabled=tracing.is_disabled(), - ) as span_generation: - response = await self._fetch_response( - system_instructions, - input, - model_settings, - tools, - output_schema, - handoffs, - span_generation, - tracing, - stream=False, - ) - - if _debug.DONT_LOG_MODEL_DATA: - logger.debug("Received model response") - else: - logger.debug( - f"LLM resp:\n{json.dumps(response.choices[0].message.model_dump(), indent=2)}\n" - ) - - usage = ( - Usage( - requests=1, - input_tokens=response.usage.prompt_tokens, - output_tokens=response.usage.completion_tokens, - total_tokens=response.usage.total_tokens, - ) - if response.usage - else Usage() - ) - if tracing.include_data(): - span_generation.span_data.output = [ - response.choices[0].message.model_dump() - ] - span_generation.span_data.usage = { - "input_tokens": usage.input_tokens, - "output_tokens": usage.output_tokens, - } - - items = _Converter.message_to_output_items(response.choices[0].message) - - return ModelResponse( - output=items, - usage=usage, - referenceable_id=None, - ) - - async def stream_response( - self, - system_instructions: str | None, - input: str | list[TResponseInputItem], - model_settings: ModelSettings, - tools: list[Tool], - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - tracing: ModelTracing, - ) -> AsyncIterator[TResponseStreamEvent]: - """ - Yields a partial message as it is generated, as well as the usage information. - """ - with generation_span( - model=str(self.model), - model_config=dataclasses.asdict(model_settings) - | {"base_url": str(self._client.base_url)}, - disabled=tracing.is_disabled(), - ) as span_generation: - response, stream = await self._fetch_response( - system_instructions, - input, - model_settings, - tools, - output_schema, - handoffs, - span_generation, - tracing, - stream=True, - ) - - usage: CompletionUsage | None = None - state = _StreamingState() - sequence_number = 0 - - async for chunk in stream: - if not state.started: - state.started = True - yield ResponseCreatedEvent( - response=response, - type="response.created", - sequence_number=sequence_number, - ) - sequence_number += 1 - - # The usage is only available in the last chunk - usage = chunk.usage - - if not chunk.choices or not chunk.choices[0].delta: - continue - - delta = chunk.choices[0].delta - - # Handle text - if delta.content: - if not state.text_content_index_and_output: - # Initialize a content tracker for streaming text - state.text_content_index_and_output = ( - 0 if not state.refusal_content_index_and_output else 1, - ResponseOutputText( - text="", - type="output_text", - annotations=[], - ), - ) - # Start a new assistant message stream - assistant_item = ResponseOutputMessage( - id=FAKE_RESPONSES_ID, - content=[], - role="assistant", - type="message", - status="in_progress", - ) - # Notify consumers of the start of a new output message + first content part - yield ResponseOutputItemAddedEvent( - item=assistant_item, - output_index=0, - type="response.output_item.added", - sequence_number=sequence_number, - ) - sequence_number += 1 - yield ResponseContentPartAddedEvent( - content_index=state.text_content_index_and_output[0], - item_id=FAKE_RESPONSES_ID, - output_index=0, - part=ResponseOutputText( - text="", - type="output_text", - annotations=[], - ), - type="response.content_part.added", - sequence_number=sequence_number, - ) - sequence_number += 1 - # Emit the delta for this segment of content - yield ResponseTextDeltaEvent( - content_index=state.text_content_index_and_output[0], - delta=delta.content, - item_id=FAKE_RESPONSES_ID, - output_index=0, - type="response.output_text.delta", - logprobs=[], - sequence_number=sequence_number, - ) - sequence_number += 1 - # Accumulate the text into the response part - state.text_content_index_and_output[1].text += delta.content - - # Handle refusals (model declines to answer) - if delta.refusal: - if not state.refusal_content_index_and_output: - # Initialize a content tracker for streaming refusal text - state.refusal_content_index_and_output = ( - 0 if not state.text_content_index_and_output else 1, - ResponseOutputRefusal(refusal="", type="refusal"), - ) - # Start a new assistant message if one doesn't exist yet (in-progress) - assistant_item = ResponseOutputMessage( - id=FAKE_RESPONSES_ID, - content=[], - role="assistant", - type="message", - status="in_progress", - ) - # Notify downstream that assistant message + first content part are starting - yield ResponseOutputItemAddedEvent( - item=assistant_item, - output_index=0, - type="response.output_item.added", - sequence_number=sequence_number, - ) - sequence_number += 1 - yield ResponseContentPartAddedEvent( - content_index=state.refusal_content_index_and_output[0], - item_id=FAKE_RESPONSES_ID, - output_index=0, - part=ResponseOutputText( - text="", - type="output_text", - annotations=[], - ), - type="response.content_part.added", - sequence_number=sequence_number, - ) - sequence_number += 1 - # Emit the delta for this segment of refusal - yield ResponseRefusalDeltaEvent( - content_index=state.refusal_content_index_and_output[0], - delta=delta.refusal, - item_id=FAKE_RESPONSES_ID, - output_index=0, - type="response.refusal.delta", - sequence_number=sequence_number, - ) - sequence_number += 1 - # Accumulate the refusal string in the output part - state.refusal_content_index_and_output[1].refusal += delta.refusal - - # Handle tool calls - # Because we don't know the name of the function until the end of the stream, we'll - # save everything and yield events at the end - if delta.tool_calls: - for tc_delta in delta.tool_calls: - if tc_delta.index not in state.function_calls: - state.function_calls[tc_delta.index] = ( - ResponseFunctionToolCall( - id=FAKE_RESPONSES_ID, - arguments="", - name="", - type="function_call", - call_id="", - ) - ) - tc_function = tc_delta.function - - state.function_calls[tc_delta.index].arguments += ( - tc_function.arguments if tc_function else "" - ) or "" - state.function_calls[tc_delta.index].name += ( - tc_function.name if tc_function else "" - ) or "" - state.function_calls[tc_delta.index].call_id += ( - tc_delta.id or "" - ) - - function_call_starting_index = 0 - if state.text_content_index_and_output: - function_call_starting_index += 1 - # Send end event for this content part - yield ResponseContentPartDoneEvent( - content_index=state.text_content_index_and_output[0], - item_id=FAKE_RESPONSES_ID, - output_index=0, - part=state.text_content_index_and_output[1], - type="response.content_part.done", - sequence_number=sequence_number, - ) - sequence_number += 1 - - if state.refusal_content_index_and_output: - function_call_starting_index += 1 - # Send end event for this content part - yield ResponseContentPartDoneEvent( - content_index=state.refusal_content_index_and_output[0], - item_id=FAKE_RESPONSES_ID, - output_index=0, - part=state.refusal_content_index_and_output[1], - type="response.content_part.done", - sequence_number=sequence_number, - ) - sequence_number += 1 - - # Actually send events for the function calls - for function_call in state.function_calls.values(): - # First, a ResponseOutputItemAdded for the function call - yield ResponseOutputItemAddedEvent( - item=ResponseFunctionToolCall( - id=FAKE_RESPONSES_ID, - call_id=function_call.call_id, - arguments=function_call.arguments, - name=function_call.name, - type="function_call", - ), - output_index=function_call_starting_index, - type="response.output_item.added", - sequence_number=sequence_number, - ) - sequence_number += 1 - # Then, yield the args - yield ResponseFunctionCallArgumentsDeltaEvent( - delta=function_call.arguments, - item_id=FAKE_RESPONSES_ID, - output_index=function_call_starting_index, - type="response.function_call_arguments.delta", - sequence_number=sequence_number, - ) - sequence_number += 1 - # Finally, the ResponseOutputItemDone - yield ResponseOutputItemDoneEvent( - item=ResponseFunctionToolCall( - id=FAKE_RESPONSES_ID, - call_id=function_call.call_id, - arguments=function_call.arguments, - name=function_call.name, - type="function_call", - ), - output_index=function_call_starting_index, - type="response.output_item.done", - sequence_number=sequence_number, - ) - sequence_number += 1 - - # Finally, send the Response completed event - outputs: list[ResponseOutputItem] = [] - if ( - state.text_content_index_and_output - or state.refusal_content_index_and_output - ): - assistant_msg = ResponseOutputMessage( - id=FAKE_RESPONSES_ID, - content=[], - role="assistant", - type="message", - status="completed", - ) - if state.text_content_index_and_output: - assistant_msg.content.append(state.text_content_index_and_output[1]) - if state.refusal_content_index_and_output: - assistant_msg.content.append( - state.refusal_content_index_and_output[1] - ) - outputs.append(assistant_msg) - - # send a ResponseOutputItemDone for the assistant message - yield ResponseOutputItemDoneEvent( - item=assistant_msg, - output_index=0, - type="response.output_item.done", - sequence_number=sequence_number, - ) - sequence_number += 1 - - for function_call in state.function_calls.values(): - outputs.append(function_call) - - final_response = response.model_copy() - final_response.output = outputs - final_response.usage = ( - ResponseUsage( - input_tokens=usage.prompt_tokens, - output_tokens=usage.completion_tokens, - total_tokens=usage.total_tokens, - output_tokens_details=OutputTokensDetails( - reasoning_tokens=( - usage.completion_tokens_details.reasoning_tokens - if getattr(usage, "completion_tokens_details", None) - and getattr( - usage.completion_tokens_details, - "reasoning_tokens", - None, - ) - else 0 - ) - ), - input_tokens_details={"cached_tokens": 0}, - ) - if usage - else None - ) - - yield ResponseCompletedEvent( - response=final_response, - type="response.completed", - sequence_number=sequence_number, - ) - if tracing.include_data(): - span_generation.span_data.output = [final_response.model_dump()] - - if usage: - span_generation.span_data.usage = { - "input_tokens": usage.prompt_tokens, - "output_tokens": usage.completion_tokens, - } - - @overload - async def _fetch_response( - self, - system_instructions: str | None, - input: str | list[TResponseInputItem], - model_settings: ModelSettings, - tools: list[Tool], - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - span: Span[GenerationSpanData], - tracing: ModelTracing, - stream: Literal[True], - ) -> tuple[Response, AsyncStream[ChatCompletionChunk]]: ... - - @overload - async def _fetch_response( - self, - system_instructions: str | None, - input: str | list[TResponseInputItem], - model_settings: ModelSettings, - tools: list[Tool], - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - span: Span[GenerationSpanData], - tracing: ModelTracing, - stream: Literal[False], - ) -> ChatCompletion: ... - - async def _fetch_response( - self, - system_instructions: str | None, - input: str | list[TResponseInputItem], - model_settings: ModelSettings, - tools: list[Tool], - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - span: Span[GenerationSpanData], - tracing: ModelTracing, - stream: bool = False, - ) -> ChatCompletion | tuple[Response, AsyncStream[ChatCompletionChunk]]: - converted_messages = _Converter.items_to_messages(input) - - if system_instructions: - converted_messages.insert( - 0, - { - "content": system_instructions, - "role": "system", - }, - ) - if tracing.include_data(): - span.span_data.input = converted_messages - - parallel_tool_calls = ( - True - if model_settings.parallel_tool_calls and tools and len(tools) > 0 - else NOT_GIVEN - ) - tool_choice = _Converter.convert_tool_choice(model_settings.tool_choice) - response_format = _Converter.convert_response_format(output_schema) - - converted_tools = ( - [ToolConverter.to_openai(tool) for tool in tools] if tools else [] - ) - - for handoff in handoffs: - converted_tools.append(ToolConverter.convert_handoff_tool(handoff)) - - if _debug.DONT_LOG_MODEL_DATA: - logger.debug("Calling LLM") - else: - logger.debug( - f"{json.dumps(converted_messages, indent=2)}\n" - f"Tools:\n{json.dumps(converted_tools, indent=2)}\n" - f"Stream: {stream}\n" - f"Tool choice: {tool_choice}\n" - f"Response format: {response_format}\n" - ) - - ret = await self._get_client().chat.completions.create( - model=self.model, - messages=converted_messages, - tools=converted_tools or NOT_GIVEN, - temperature=self._non_null_or_not_given(model_settings.temperature), - top_p=self._non_null_or_not_given(model_settings.top_p), - frequency_penalty=self._non_null_or_not_given( - model_settings.frequency_penalty - ), - presence_penalty=self._non_null_or_not_given( - model_settings.presence_penalty - ), - max_tokens=self._non_null_or_not_given(model_settings.max_tokens), - tool_choice=tool_choice, - response_format=response_format, - parallel_tool_calls=parallel_tool_calls, - stream=stream, - stream_options={"include_usage": True} if stream else NOT_GIVEN, - extra_headers=_HEADERS, - ) - - if isinstance(ret, ChatCompletion): - return ret - - response = Response( - id=FAKE_RESPONSES_ID, - created_at=time.time(), - model=self.model, - object="response", - output=[], - tool_choice=( - cast(Literal["auto", "required", "none"], tool_choice) - if tool_choice != NOT_GIVEN - else "auto" - ), - top_p=model_settings.top_p, - temperature=model_settings.temperature, - tools=[], - parallel_tool_calls=parallel_tool_calls or False, - ) - return response, ret - - def _get_client(self) -> AsyncOpenAI: - if self._client is None: - self._client = AsyncOpenAI() - return self._client - - -class _Converter: - @classmethod - def convert_tool_choice( - cls, tool_choice: Literal["auto", "required", "none"] | str | None - ) -> ChatCompletionToolChoiceOptionParam | NotGiven: - if tool_choice is None: - return NOT_GIVEN - elif tool_choice == "auto": - return "auto" - elif tool_choice == "required": - return "required" - elif tool_choice == "none": - return "none" - else: - return { - "type": "function", - "function": { - "name": tool_choice, - }, - } - - @classmethod - def convert_response_format( - cls, final_output_schema: AgentOutputSchema | None - ) -> ResponseFormat | NotGiven: - if not final_output_schema or final_output_schema.is_plain_text(): - return NOT_GIVEN - - return { - "type": "json_schema", - "json_schema": { - "name": "final_output", - "strict": final_output_schema.strict_json_schema, - "schema": final_output_schema.json_schema(), - }, - } - - @classmethod - def message_to_output_items( - cls, message: ChatCompletionMessage - ) -> list[TResponseOutputItem]: - items: list[TResponseOutputItem] = [] - - message_item = ResponseOutputMessage( - id=FAKE_RESPONSES_ID, - content=[], - role="assistant", - type="message", - status="completed", - ) - if message.content: - message_item.content.append( - ResponseOutputText( - text=message.content, type="output_text", annotations=[] - ) - ) - if message.refusal: - message_item.content.append( - ResponseOutputRefusal(refusal=message.refusal, type="refusal") - ) - if message.audio: - raise AgentsException("Audio is not currently supported") - - if message_item.content: - items.append(message_item) - - if message.tool_calls: - for tool_call in message.tool_calls: - items.append( - ResponseFunctionToolCall( - id=FAKE_RESPONSES_ID, - call_id=tool_call.id, - arguments=tool_call.function.arguments, - name=tool_call.function.name, - type="function_call", - ) - ) - - return items - - @classmethod - def maybe_easy_input_message(cls, item: Any) -> EasyInputMessageParam | None: - if not isinstance(item, dict): - return None - - keys = item.keys() - # EasyInputMessageParam only has these two keys - if keys != {"content", "role"}: - return None - - role = item.get("role", None) - if role not in ("user", "assistant", "system", "developer"): - return None - - if "content" not in item: - return None - - return cast(EasyInputMessageParam, item) - - @classmethod - def maybe_input_message(cls, item: Any) -> Message | None: - if ( - isinstance(item, dict) - and item.get("type") == "message" - and item.get("role") - in ( - "user", - "system", - "developer", - ) - ): - return cast(Message, item) - - return None - - @classmethod - def maybe_file_search_call( - cls, item: Any - ) -> ResponseFileSearchToolCallParam | None: - if isinstance(item, dict) and item.get("type") == "file_search_call": - return cast(ResponseFileSearchToolCallParam, item) - return None - - @classmethod - def maybe_function_tool_call( - cls, item: Any - ) -> ResponseFunctionToolCallParam | None: - if isinstance(item, dict) and item.get("type") == "function_call": - return cast(ResponseFunctionToolCallParam, item) - return None - - @classmethod - def maybe_function_tool_call_output( - cls, - item: Any, - ) -> FunctionCallOutput | None: - if isinstance(item, dict) and item.get("type") == "function_call_output": - return cast(FunctionCallOutput, item) - return None - - @classmethod - def maybe_item_reference(cls, item: Any) -> ItemReference | None: - if isinstance(item, dict) and item.get("type") == "item_reference": - return cast(ItemReference, item) - return None - - @classmethod - def maybe_response_output_message( - cls, item: Any - ) -> ResponseOutputMessageParam | None: - # ResponseOutputMessage is only used for messages with role assistant - if ( - isinstance(item, dict) - and item.get("type") == "message" - and item.get("role") == "assistant" - ): - return cast(ResponseOutputMessageParam, item) - return None - - @classmethod - def extract_text_content( - cls, content: str | Iterable[ResponseInputContentParam] - ) -> str | list[ChatCompletionContentPartTextParam]: - all_content = cls.extract_all_content(content) - if isinstance(all_content, str): - return all_content - out: list[ChatCompletionContentPartTextParam] = [] - for c in all_content: - if c.get("type") == "text": - out.append(cast(ChatCompletionContentPartTextParam, c)) - return out - - @classmethod - def extract_all_content( - cls, content: str | Iterable[ResponseInputContentParam] - ) -> str | list[ChatCompletionContentPartParam]: - if isinstance(content, str): - return content - out: list[ChatCompletionContentPartParam] = [] - - for c in content: - if isinstance(c, dict) and c.get("type") == "input_text": - casted_text_param = cast(ResponseInputTextParam, c) - out.append( - ChatCompletionContentPartTextParam( - type="text", - text=casted_text_param["text"], - ) - ) - elif isinstance(c, dict) and c.get("type") == "input_image": - casted_image_param = cast(ResponseInputImageParam, c) - if ( - "image_url" not in casted_image_param - or not casted_image_param["image_url"] - ): - raise UserError( - f"Only image URLs are supported for input_image {casted_image_param}" - ) - out.append( - ChatCompletionContentPartImageParam( - type="image_url", - image_url={ - "url": casted_image_param["image_url"], - "detail": casted_image_param["detail"], - }, - ) - ) - elif isinstance(c, dict) and c.get("type") == "input_file": - raise UserError( - f"File uploads are not supported for chat completions {c}" - ) - else: - raise UserError(f"Unknonw content: {c}") - return out - - @classmethod - def items_to_messages( - cls, - items: str | Iterable[TResponseInputItem], - ) -> list[ChatCompletionMessageParam]: - """ - Convert a sequence of 'Item' objects into a list of ChatCompletionMessageParam. - - Rules: - - EasyInputMessage or InputMessage (role=user) => ChatCompletionUserMessageParam - - EasyInputMessage or InputMessage (role=system) => ChatCompletionSystemMessageParam - - EasyInputMessage or InputMessage (role=developer) => ChatCompletionDeveloperMessageParam - - InputMessage (role=assistant) => Start or flush a ChatCompletionAssistantMessageParam - - response_output_message => Also produces/flushes a ChatCompletionAssistantMessageParam - - tool calls get attached to the *current* assistant message, or create one if none. - - tool outputs => ChatCompletionToolMessageParam - """ - - if isinstance(items, str): - return [ - ChatCompletionUserMessageParam( - role="user", - content=items, - ) - ] - - result: list[ChatCompletionMessageParam] = [] - current_assistant_msg: ChatCompletionAssistantMessageParam | None = None - - def flush_assistant_message() -> None: - nonlocal current_assistant_msg - if current_assistant_msg is not None: - # The API doesn't support empty arrays for tool_calls - if not current_assistant_msg.get("tool_calls"): - del current_assistant_msg["tool_calls"] - result.append(current_assistant_msg) - current_assistant_msg = None - - def ensure_assistant_message() -> ChatCompletionAssistantMessageParam: - nonlocal current_assistant_msg - if current_assistant_msg is None: - current_assistant_msg = ChatCompletionAssistantMessageParam( - role="assistant" - ) - current_assistant_msg["tool_calls"] = [] - return current_assistant_msg - - for item in items: - # 1) Check easy input message - if easy_msg := cls.maybe_easy_input_message(item): - role = easy_msg["role"] - content = easy_msg["content"] - - if role == "user": - flush_assistant_message() - msg_user: ChatCompletionUserMessageParam = { - "role": "user", - "content": cls.extract_all_content(content), - } - result.append(msg_user) - elif role == "system": - flush_assistant_message() - msg_system: ChatCompletionSystemMessageParam = { - "role": "system", - "content": cls.extract_text_content(content), - } - result.append(msg_system) - elif role == "developer": - flush_assistant_message() - msg_developer: ChatCompletionDeveloperMessageParam = { - "role": "developer", - "content": cls.extract_text_content(content), - } - result.append(msg_developer) - elif role == "assistant": - flush_assistant_message() - msg_assistant: ChatCompletionAssistantMessageParam = { - "role": "assistant", - "content": cls.extract_text_content(content), - } - result.append(msg_assistant) - else: - raise UserError(f"Unexpected role in easy_input_message: {role}") - - # 2) Check input message - elif in_msg := cls.maybe_input_message(item): - role = in_msg["role"] - content = in_msg["content"] - flush_assistant_message() - - if role == "user": - msg_user = { - "role": "user", - "content": cls.extract_all_content(content), - } - result.append(msg_user) - elif role == "system": - msg_system = { - "role": "system", - "content": cls.extract_text_content(content), - } - result.append(msg_system) - elif role == "developer": - msg_developer = { - "role": "developer", - "content": cls.extract_text_content(content), - } - result.append(msg_developer) - else: - raise UserError(f"Unexpected role in input_message: {role}") - - # 3) response output message => assistant - elif resp_msg := cls.maybe_response_output_message(item): - flush_assistant_message() - new_asst = ChatCompletionAssistantMessageParam(role="assistant") - contents = resp_msg["content"] - - text_segments = [] - for c in contents: - if c["type"] == "output_text": - text_segments.append(c["text"]) - elif c["type"] == "refusal": - new_asst["refusal"] = c["refusal"] - elif c["type"] == "output_audio": - # Can't handle this, b/c chat completions expects an ID which we dont have - raise UserError( - f"Only audio IDs are supported for chat completions, but got: {c}" - ) - else: - raise UserError( - f"Unknown content type in ResponseOutputMessage: {c}" - ) - - if text_segments: - combined = "\n".join(text_segments) - new_asst["content"] = combined - - new_asst["tool_calls"] = [] - current_assistant_msg = new_asst - - # 4) function/file-search calls => attach to assistant - elif file_search := cls.maybe_file_search_call(item): - asst = ensure_assistant_message() - tool_calls = list(asst.get("tool_calls", [])) - new_tool_call = ChatCompletionMessageToolCallParam( - id=file_search["id"], - type="function", - function={ - "name": "file_search_call", - "arguments": json.dumps( - { - "queries": file_search.get("queries", []), - "status": file_search.get("status"), - } - ), - }, - ) - tool_calls.append(new_tool_call) - asst["tool_calls"] = tool_calls - - elif func_call := cls.maybe_function_tool_call(item): - asst = ensure_assistant_message() - tool_calls = list(asst.get("tool_calls", [])) - new_tool_call = ChatCompletionMessageToolCallParam( - id=func_call["call_id"], - type="function", - function={ - "name": func_call["name"], - "arguments": func_call["arguments"], - }, - ) - tool_calls.append(new_tool_call) - asst["tool_calls"] = tool_calls - # 5) function call output => tool message - elif func_output := cls.maybe_function_tool_call_output(item): - flush_assistant_message() - msg: ChatCompletionToolMessageParam = { - "role": "tool", - "tool_call_id": func_output["call_id"], - "content": func_output["output"], - } - result.append(msg) - - # 6) item reference => handle or raise - elif item_ref := cls.maybe_item_reference(item): - raise UserError( - f"Encountered an item_reference, which is not supported: {item_ref}" - ) - - # 7) If we haven't recognized it => fail or ignore - else: - raise UserError(f"Unhandled item type or structure: {item}") - - flush_assistant_message() - return result - - -class ToolConverter: - @classmethod - def to_openai(cls, tool: Tool) -> ChatCompletionToolParam: - if isinstance(tool, FunctionTool): - return { - "type": "function", - "function": { - "name": tool.name, - "description": tool.description or "", - "parameters": tool.params_json_schema, - }, - } - - raise UserError( - f"Hosted tools are not supported with the ChatCompletions API. FGot tool type: " - f"{type(tool)}, tool: {tool}" - ) - - @classmethod - def convert_handoff_tool(cls, handoff: Handoff[Any]) -> ChatCompletionToolParam: - return { - "type": "function", - "function": { - "name": handoff.tool_name, - "description": handoff.tool_description, - "parameters": handoff.input_json_schema, - }, - } diff --git a/pkg/hanzo-agent/src/agents/models/openai_provider.py b/pkg/hanzo-agent/src/agents/models/openai_provider.py deleted file mode 100644 index 8aec5fe31..000000000 --- a/pkg/hanzo-agent/src/agents/models/openai_provider.py +++ /dev/null @@ -1,97 +0,0 @@ -from __future__ import annotations - -import httpx -from openai import AsyncOpenAI, DefaultAsyncHttpxClient - -from . import _openai_shared -from .interface import Model, ModelProvider -from .openai_chatcompletions import OpenAIChatCompletionsModel -from .openai_responses import OpenAIResponsesModel - -DEFAULT_MODEL: str = "gpt-4o" - - -_http_client: httpx.AsyncClient | None = None - - -# If we create a new httpx client for each request, that would mean no sharing of connection pools, -# which would mean worse latency and resource usage. So, we share the client across requests. -def shared_http_client() -> httpx.AsyncClient: - global _http_client - if _http_client is None: - _http_client = DefaultAsyncHttpxClient() - return _http_client - - -class OpenAIProvider(ModelProvider): - def __init__( - self, - *, - api_key: str | None = None, - base_url: str | None = None, - openai_client: AsyncOpenAI | None = None, - organization: str | None = None, - project: str | None = None, - use_responses: bool | None = None, - ) -> None: - if openai_client is not None: - assert ( - api_key is None and base_url is None - ), "Don't provide api_key or base_url if you provide openai_client" - self._client: AsyncOpenAI | None = openai_client - else: - self._client = None - self._stored_api_key = api_key - self._stored_base_url = base_url - self._stored_organization = organization - self._stored_project = project - - if use_responses is not None: - self._use_responses = use_responses - else: - self._use_responses = _openai_shared.get_use_responses_by_default() - - # We lazy load the client in case you never actually use OpenAIProvider(). Otherwise - # AsyncOpenAI() raises an error if you don't have an API key set. - def _get_client(self) -> AsyncOpenAI: - if self._client is None: - api_key = self._stored_api_key or _openai_shared.get_default_openai_key() - # Only allow a dummy key if explicitly enabled via environment to support tests - if api_key is None: - import os - - if os.getenv("ALLOW_DUMMY_OPENAI_KEY") in {"1", "true", "True"}: - api_key = "sk-dummy" - self._client = _openai_shared.get_default_openai_client() or AsyncOpenAI( - api_key=api_key, - base_url=self._stored_base_url, - organization=self._stored_organization, - project=self._stored_project, - http_client=shared_http_client(), - ) - - return self._client - - def get_model(self, model_name: str | None) -> Model: - if model_name is None: - model_name = DEFAULT_MODEL - # Try to construct a real client; if unavailable (e.g., no API key) fall back to a - # lightweight stub so tests that only check isinstance can proceed without network creds. - import os - - try: - client = self._get_client() - except Exception as e: - if os.getenv("ALLOW_DUMMY_OPENAI_KEY") not in {"1", "true", "True"}: - raise - - class _ClientStub: - base_url = "" - - client = _ClientStub() # type: ignore - - return ( - OpenAIResponsesModel(model=model_name, openai_client=client) - if self._use_responses - else OpenAIChatCompletionsModel(model=model_name, openai_client=client) - ) diff --git a/pkg/hanzo-agent/src/agents/models/openai_responses.py b/pkg/hanzo-agent/src/agents/models/openai_responses.py deleted file mode 100644 index 401ba3cbe..000000000 --- a/pkg/hanzo-agent/src/agents/models/openai_responses.py +++ /dev/null @@ -1,411 +0,0 @@ -from __future__ import annotations - -import json -from collections.abc import AsyncIterator -from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Literal, overload - -from openai import NOT_GIVEN, APIStatusError, AsyncOpenAI, AsyncStream, NotGiven -from openai.types import ChatModel -from openai.types.responses import ( - Response, - ResponseCompletedEvent, - ResponseStreamEvent, - ResponseTextConfigParam, - ToolParam, - WebSearchToolParam, - response_create_params, -) - -from .. import _debug -from ..agent_output import AgentOutputSchema -from ..exceptions import UserError -from ..handoffs import Handoff -from ..items import ItemHelpers, ModelResponse, TResponseInputItem -from ..logger import logger -from ..tool import ComputerTool, FileSearchTool, FunctionTool, Tool, WebSearchTool -from ..tracing import SpanError, response_span -from ..usage import Usage -from ..version import __version__ -from .interface import Model, ModelTracing - -if TYPE_CHECKING: - from ..model_settings import ModelSettings - - -_USER_AGENT = f"Agents/Python {__version__}" -_HEADERS = {"User-Agent": _USER_AGENT} - -# From the Responses API -IncludeLiteral = Literal[ - "file_search_call.results", - "message.input_image.image_url", - "computer_call_output.output.image_url", -] - - -class OpenAIResponsesModel(Model): - """ - Implementation of `Model` that uses the OpenAI Responses API. - """ - - def __init__( - self, - model: str | ChatModel, - openai_client: AsyncOpenAI, - ) -> None: - self.model = model - self._client = openai_client - - def _non_null_or_not_given(self, value: Any) -> Any: - return value if value is not None else NOT_GIVEN - - async def get_response( - self, - system_instructions: str | None, - input: str | list[TResponseInputItem], - model_settings: ModelSettings, - tools: list[Tool], - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - tracing: ModelTracing, - ) -> ModelResponse: - with response_span(disabled=tracing.is_disabled()) as span_response: - try: - response = await self._fetch_response( - system_instructions, - input, - model_settings, - tools, - output_schema, - handoffs, - stream=False, - ) - - if _debug.DONT_LOG_MODEL_DATA: - logger.debug("LLM responsed") - else: - logger.debug( - "LLM resp:\n" - f"{json.dumps([x.model_dump() for x in response.output], indent=2)}\n" - ) - - usage = ( - Usage( - requests=1, - input_tokens=response.usage.input_tokens, - output_tokens=response.usage.output_tokens, - total_tokens=response.usage.total_tokens, - ) - if response.usage - else Usage() - ) - - if tracing.include_data(): - span_response.span_data.response = response - span_response.span_data.input = input - except Exception as e: - span_response.set_error( - SpanError( - message="Error getting response", - data={ - "error": ( - str(e) - if tracing.include_data() - else e.__class__.__name__ - ), - }, - ) - ) - request_id = e.request_id if isinstance(e, APIStatusError) else None - logger.error(f"Error getting response: {e}. (request_id: {request_id})") - raise - - return ModelResponse( - output=response.output, - usage=usage, - referenceable_id=response.id, - ) - - async def stream_response( - self, - system_instructions: str | None, - input: str | list[TResponseInputItem], - model_settings: ModelSettings, - tools: list[Tool], - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - tracing: ModelTracing, - ) -> AsyncIterator[ResponseStreamEvent]: - """ - Yields a partial message as it is generated, as well as the usage information. - """ - with response_span(disabled=tracing.is_disabled()) as span_response: - try: - stream = await self._fetch_response( - system_instructions, - input, - model_settings, - tools, - output_schema, - handoffs, - stream=True, - ) - - final_response: Response | None = None - sequence_number = 0 - - async for chunk in stream: - # Ensure sequence_number is present for compatibility with newer SDKs - if getattr(chunk, "sequence_number", None) is None: - try: - chunk = chunk.model_copy( - update={"sequence_number": sequence_number} - ) - except Exception: - # If model_copy isn't available, fall back to yielding as-is - pass - sequence_number += 1 - if isinstance(chunk, ResponseCompletedEvent): - final_response = chunk.response - yield chunk - - if final_response and tracing.include_data(): - span_response.span_data.response = final_response - span_response.span_data.input = input - - except Exception as e: - span_response.set_error( - SpanError( - message="Error streaming response", - data={ - "error": ( - str(e) - if tracing.include_data() - else e.__class__.__name__ - ), - }, - ) - ) - logger.error(f"Error streaming response: {e}") - raise - - @overload - async def _fetch_response( - self, - system_instructions: str | None, - input: str | list[TResponseInputItem], - model_settings: ModelSettings, - tools: list[Tool], - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - stream: Literal[True], - ) -> AsyncStream[ResponseStreamEvent]: ... - - @overload - async def _fetch_response( - self, - system_instructions: str | None, - input: str | list[TResponseInputItem], - model_settings: ModelSettings, - tools: list[Tool], - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - stream: Literal[False], - ) -> Response: ... - - async def _fetch_response( - self, - system_instructions: str | None, - input: str | list[TResponseInputItem], - model_settings: ModelSettings, - tools: list[Tool], - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - stream: Literal[True] | Literal[False] = False, - ) -> Response | AsyncStream[ResponseStreamEvent]: - list_input = ItemHelpers.input_to_new_input_list(input) - - parallel_tool_calls = ( - True - if model_settings.parallel_tool_calls and tools and len(tools) > 0 - else NOT_GIVEN - ) - - tool_choice = Converter.convert_tool_choice(model_settings.tool_choice) - converted_tools = Converter.convert_tools(tools, handoffs) - response_format = Converter.get_response_format(output_schema) - - if _debug.DONT_LOG_MODEL_DATA: - logger.debug("Calling LLM") - else: - logger.debug( - f"Calling LLM {self.model} with input:\n" - f"{json.dumps(list_input, indent=2)}\n" - f"Tools:\n{json.dumps(converted_tools.tools, indent=2)}\n" - f"Stream: {stream}\n" - f"Tool choice: {tool_choice}\n" - f"Response format: {response_format}\n" - ) - - return await self._client.responses.create( - instructions=self._non_null_or_not_given(system_instructions), - model=self.model, - input=list_input, - include=converted_tools.includes, - tools=converted_tools.tools, - temperature=self._non_null_or_not_given(model_settings.temperature), - top_p=self._non_null_or_not_given(model_settings.top_p), - truncation=self._non_null_or_not_given(model_settings.truncation), - max_output_tokens=self._non_null_or_not_given(model_settings.max_tokens), - tool_choice=tool_choice, - parallel_tool_calls=parallel_tool_calls, - stream=stream, - extra_headers=_HEADERS, - text=response_format, - ) - - def _get_client(self) -> AsyncOpenAI: - if self._client is None: - self._client = AsyncOpenAI() - return self._client - - -@dataclass -class ConvertedTools: - tools: list[ToolParam] - includes: list[IncludeLiteral] - - -class Converter: - @classmethod - def convert_tool_choice( - cls, tool_choice: Literal["auto", "required", "none"] | str | None - ) -> response_create_params.ToolChoice | NotGiven: - if tool_choice is None: - return NOT_GIVEN - elif tool_choice == "required": - return "required" - elif tool_choice == "auto": - return "auto" - elif tool_choice == "none": - return "none" - elif tool_choice == "file_search": - return { - "type": "file_search", - } - elif tool_choice == "web_search_preview": - return { - "type": "web_search_preview", - } - elif tool_choice == "computer_use_preview": - return { - "type": "computer_use_preview", - } - else: - return { - "type": "function", - "name": tool_choice, - } - - @classmethod - def get_response_format( - cls, output_schema: AgentOutputSchema | None - ) -> ResponseTextConfigParam | NotGiven: - if output_schema is None or output_schema.is_plain_text(): - return NOT_GIVEN - else: - return { - "format": { - "type": "json_schema", - "name": "final_output", - "schema": output_schema.json_schema(), - "strict": output_schema.strict_json_schema, - } - } - - @classmethod - def convert_tools( - cls, - tools: list[Tool], - handoffs: list[Handoff[Any]], - ) -> ConvertedTools: - converted_tools: list[ToolParam] = [] - includes: list[IncludeLiteral] = [] - - computer_tools = [tool for tool in tools if isinstance(tool, ComputerTool)] - if len(computer_tools) > 1: - raise UserError( - f"You can only provide one computer tool. Got {len(computer_tools)}" - ) - - for tool in tools: - converted_tool, include = cls._convert_tool(tool) - converted_tools.append(converted_tool) - if include: - includes.append(include) - - for handoff in handoffs: - converted_tools.append(cls._convert_handoff_tool(handoff)) - - return ConvertedTools(tools=converted_tools, includes=includes) - - @classmethod - def _convert_tool(cls, tool: Tool) -> tuple[ToolParam, IncludeLiteral | None]: - """Returns converted tool and includes""" - - if isinstance(tool, FunctionTool): - converted_tool: ToolParam = { - "name": tool.name, - "parameters": tool.params_json_schema, - "strict": tool.strict_json_schema, - "type": "function", - "description": tool.description, - } - includes: IncludeLiteral | None = None - elif isinstance(tool, WebSearchTool): - ws: WebSearchToolParam = { - "type": "web_search_preview", - "user_location": tool.user_location, - "search_context_size": tool.search_context_size, - } - converted_tool = ws - includes = None - elif isinstance(tool, FileSearchTool): - converted_tool = { - "type": "file_search", - "vector_store_ids": tool.vector_store_ids, - } - if tool.max_num_results: - converted_tool["max_num_results"] = tool.max_num_results - if tool.ranking_options: - converted_tool["ranking_options"] = tool.ranking_options - if tool.filters: - converted_tool["filters"] = tool.filters - - includes = ( - "file_search_call.results" if tool.include_search_results else None - ) - elif isinstance(tool, ComputerTool): - converted_tool = { - "type": "computer_use_preview", - "environment": tool.computer.environment, - "display_width": tool.computer.dimensions[0], - "display_height": tool.computer.dimensions[1], - } - includes = None - - else: - raise UserError(f"Unknown tool type: {type(tool)}, tool") - - return converted_tool, includes - - @classmethod - def _convert_handoff_tool(cls, handoff: Handoff) -> ToolParam: - return { - "name": handoff.tool_name, - "parameters": handoff.input_json_schema, - "strict": handoff.strict_json_schema, - "type": "function", - "description": handoff.tool_description, - } diff --git a/pkg/hanzo-agent/src/agents/network/__init__.py b/pkg/hanzo-agent/src/agents/network/__init__.py deleted file mode 100644 index ea2264824..000000000 --- a/pkg/hanzo-agent/src/agents/network/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Network orchestration for multi-agent systems. - -This module provides tools for creating and managing networks of agents that can -collaborate, share state, and route tasks intelligently. -""" - -from .network import AgentNetwork, NetworkConfig, create_network -from .router import ( - Router, - RoutingDecision, - RoutingStrategy, - SemanticRouter, - RuleBasedRouter, - LoadBalancingRouter, - routing_strategy, -) -from .node import NetworkNode, NodeStatus - -__all__ = [ - "AgentNetwork", - "NetworkConfig", - "create_network", - "Router", - "RoutingDecision", - "RoutingStrategy", - "SemanticRouter", - "RuleBasedRouter", - "LoadBalancingRouter", - "routing_strategy", - "NetworkNode", - "NodeStatus", -] diff --git a/pkg/hanzo-agent/src/agents/network/network.py b/pkg/hanzo-agent/src/agents/network/network.py deleted file mode 100644 index c43c7af64..000000000 --- a/pkg/hanzo-agent/src/agents/network/network.py +++ /dev/null @@ -1,437 +0,0 @@ -"""Agent network implementation for multi-agent orchestration.""" - -from __future__ import annotations - -import asyncio -import dataclasses -from dataclasses import dataclass, field -from typing import Any, Callable, Dict, List, Optional, Type, cast - -from ..agent import Agent -from ..exceptions import AgentsException -from ..handoffs import Handoff, handoff -from ..items import TResponseInputItem -from ..lifecycle import RunHooks -from ..logger import logger -from ..result import RunResult -from ..run import Runner, RunConfig -from ..run_context import RunContextWrapper, TContext -from ..state.store import StateStore, InMemoryStateStore -from ..tracing import custom_span, get_current_trace -from .node import NetworkNode, NodeStatus -from .router import Router, RoutingDecision, SemanticRouter - - -@dataclass -class NetworkConfig: - """Configuration for an agent network.""" - - name: str = "Agent Network" - """Name of the network for tracing and logging.""" - - default_model: str = "gpt-3.5-turbo" - """Default model to use for agents in the network.""" - - state_store: StateStore | None = None - """State store for sharing data between agents. Defaults to InMemoryStateStore.""" - - enable_parallel_execution: bool = True - """Whether to enable parallel execution of independent agents.""" - - max_parallel_agents: int = 5 - """Maximum number of agents that can run in parallel.""" - - enable_tracing: bool = True - """Whether to enable detailed network tracing.""" - - retry_failed_nodes: bool = True - """Whether to retry failed agent nodes.""" - - max_retries: int = 3 - """Maximum number of retries for failed nodes.""" - - -class AgentNetwork: - """A network of agents that can collaborate and share state. - - This class provides a way to organize agents into a network where they can: - - Share state through a common state store - - Route tasks intelligently using routers - - Execute in parallel when possible - - Handle failures gracefully - """ - - def __init__( - self, - config: NetworkConfig | None = None, - router: Router | None = None, - ): - """Initialize an agent network. - - Args: - config: Network configuration - router: Router for intelligent task routing - """ - self.config = config or NetworkConfig() - self.router = router or SemanticRouter() - self.nodes: Dict[str, NetworkNode] = {} - self.state_store = self.config.state_store or InMemoryStateStore() - self._lock = asyncio.Lock() - - def add_agent( - self, - agent: Agent[TContext], - *, - capabilities: List[str] | None = None, - dependencies: List[str] | None = None, - metadata: Dict[str, Any] | None = None, - ) -> NetworkNode: - """Add an agent to the network. - - Args: - agent: The agent to add - capabilities: List of capabilities this agent provides - dependencies: List of agent names this agent depends on - metadata: Additional metadata for routing decisions - - Returns: - The network node wrapping the agent - """ - if agent.name in self.nodes: - raise AgentsException(f"Agent '{agent.name}' already exists in network") - - node = NetworkNode( - agent=agent, - capabilities=capabilities or [], - dependencies=dependencies or [], - metadata=metadata or {}, - ) - - self.nodes[agent.name] = node - - # Update router with new agent information - self.router.update_agent_info(agent.name, node.capabilities, node.metadata) - - logger.debug( - f"Added agent '{agent.name}' to network with capabilities: {node.capabilities}" - ) - - return node - - def remove_agent(self, agent_name: str) -> None: - """Remove an agent from the network.""" - if agent_name not in self.nodes: - raise AgentsException(f"Agent '{agent_name}' not found in network") - - del self.nodes[agent_name] - self.router.remove_agent_info(agent_name) - - logger.debug(f"Removed agent '{agent_name}' from network") - - async def run( - self, - input: str | list[TResponseInputItem], - *, - starting_agent: str | None = None, - context: TContext | None = None, - max_turns: int = 10, - hooks: RunHooks[TContext] | None = None, - run_config: RunConfig | None = None, - ) -> RunResult: - """Run the network with the given input. - - Args: - input: Initial input to the network - starting_agent: Name of the agent to start with (if None, router decides) - context: Shared context for all agents - max_turns: Maximum number of agent turns - hooks: Lifecycle hooks - run_config: Run configuration - - Returns: - Result of the network execution - """ - async with self._lock: - # Reset node statuses - for node in self.nodes.values(): - node.status = NodeStatus.PENDING - node.error = None - - # Create network context wrapper - network_context = NetworkContextWrapper( - context=context, - state_store=self.state_store, - network=self, - ) - - # Determine starting agent - if starting_agent: - if starting_agent not in self.nodes: - raise AgentsException( - f"Starting agent '{starting_agent}' not found in network" - ) - agent = self.nodes[starting_agent].agent - else: - # Use router to determine starting agent - decision = await self.router.route( - input=input, - available_agents=list(self.nodes.keys()), - context=network_context, - ) - - if not decision.selected_agent: - raise AgentsException("Router could not determine starting agent") - - agent = self.nodes[decision.selected_agent].agent - logger.debug( - f"Router selected starting agent: {decision.selected_agent} (confidence: {decision.confidence})" - ) - - # Create network-aware handoffs - network_handoffs = self._create_network_handoffs(agent.name) - - # Clone agent with network handoffs - network_agent = agent.clone( - handoffs=list(agent.handoffs) + network_handoffs, - ) - - # Run with network context - with custom_span("network_execution", {"network": self.config.name}): - result = await Runner.run( - starting_agent=network_agent, - input=input, - context=network_context, # type: ignore - max_turns=max_turns, - hooks=hooks or NetworkHooks(self), - run_config=run_config, - ) - - return result - - async def run_parallel( - self, - tasks: List[Dict[str, Any]], - *, - context: TContext | None = None, - run_config: RunConfig | None = None, - ) -> List[RunResult]: - """Run multiple tasks in parallel across the network. - - Args: - tasks: List of tasks, each with 'input' and optional 'agent' keys - context: Shared context - run_config: Run configuration - - Returns: - List of results for each task - """ - if not self.config.enable_parallel_execution: - # Fall back to sequential execution - results = [] - for task in tasks: - result = await self.run( - input=task["input"], - starting_agent=task.get("agent"), - context=context, - run_config=run_config, - ) - results.append(result) - return results - - # Create tasks for parallel execution - semaphore = asyncio.Semaphore(self.config.max_parallel_agents) - - async def run_task(task: Dict[str, Any]) -> RunResult: - async with semaphore: - return await self.run( - input=task["input"], - starting_agent=task.get("agent"), - context=context, - run_config=run_config, - ) - - # Run all tasks in parallel - results = await asyncio.gather( - *[run_task(task) for task in tasks], - return_exceptions=True, - ) - - # Handle any exceptions - final_results = [] - for i, result in enumerate(results): - if isinstance(result, Exception): - logger.error(f"Task {i} failed: {result}") - if self.config.retry_failed_nodes: - # Retry failed task - retry_result = await self.run( - input=tasks[i]["input"], - starting_agent=tasks[i].get("agent"), - context=context, - run_config=run_config, - ) - final_results.append(retry_result) - else: - raise result - else: - final_results.append(result) - - return final_results - - def _create_network_handoffs(self, current_agent: str) -> List[Handoff]: - """Create handoffs to other agents in the network.""" - handoffs = [] - - for name, node in self.nodes.items(): - if name == current_agent: - continue - - # Create a dynamic handoff with network routing - async def make_handoff_filter(target_name: str): - async def network_handoff_filter( - ctx: RunContextWrapper[Any], - messages: List[TResponseInputItem], - ) -> List[TResponseInputItem]: - # Use router to validate handoff - decision = await self.router.route( - input=messages, - available_agents=[target_name], - context=ctx, - ) - - if decision.selected_agent == target_name: - # Add routing metadata - if messages and isinstance(messages[-1], dict): - messages[-1]["__network_routing__"] = { - "from": current_agent, - "to": target_name, - "confidence": decision.confidence, - "reason": decision.reason, - } - - return messages - - return network_handoff_filter - - handoff = Handoff( - agent=node.agent, - input_filter=make_handoff_filter(name), - ) - handoffs.append(handoff) - - return handoffs - - -class NetworkContextWrapper(RunContextWrapper): - """Context wrapper that provides access to network state.""" - - def __init__( - self, - context: TContext | None, - state_store: StateStore, - network: AgentNetwork, - ): - super().__init__(context) - self.state_store = state_store - self.network = network - - async def get_state(self, key: str, namespace: str | None = None) -> Any: - """Get a value from the shared state store.""" - return await self.state_store.get(key, namespace) - - async def set_state( - self, key: str, value: Any, namespace: str | None = None - ) -> None: - """Set a value in the shared state store.""" - await self.state_store.set(key, value, namespace) - - async def update_state( - self, key: str, updater: Callable[[Any], Any], namespace: str | None = None - ) -> Any: - """Update a value in the shared state store.""" - return await self.state_store.update(key, updater, namespace) - - def get_network_info(self) -> Dict[str, Any]: - """Get information about the network.""" - return { - "name": self.network.config.name, - "agents": list(self.network.nodes.keys()), - "total_agents": len(self.network.nodes), - "parallel_enabled": self.network.config.enable_parallel_execution, - } - - -class NetworkHooks(RunHooks): - """Hooks for network execution tracking.""" - - def __init__(self, network: AgentNetwork): - self.network = network - - async def on_agent_start( - self, context: RunContextWrapper[Any], agent: Agent[Any] - ) -> None: - """Called when an agent starts execution.""" - if agent.name in self.network.nodes: - node = self.network.nodes[agent.name] - node.status = NodeStatus.RUNNING - node.execution_count += 1 - - logger.debug( - f"Network agent '{agent.name}' started (execution #{node.execution_count})" - ) - - async def on_agent_end( - self, context: RunContextWrapper[Any], agent: Agent[Any] - ) -> None: - """Called when an agent ends execution.""" - if agent.name in self.network.nodes: - node = self.network.nodes[agent.name] - node.status = NodeStatus.COMPLETED - - logger.debug(f"Network agent '{agent.name}' completed") - - -def create_network( - agents: List[Agent], - router: Router | None = None, - state_store: StateStore | None = None, - default_model: str = "gpt-3.5-turbo", - name: str = "default_network", - enable_parallel_execution: bool = True, -) -> AgentNetwork: - """Create a new agent network. - - Args: - agents: List of agents to include in the network - router: Router to use for agent selection - state_store: State store for shared state - default_model: Default model to use - name: Name of the network - enable_parallel_execution: Whether to enable parallel execution - - Returns: - AgentNetwork instance - """ - if router is None: - from .router import SemanticRouter - - router = SemanticRouter() - - if state_store is None: - from ..state import InMemoryStateStore - - state_store = InMemoryStateStore() - - config = NetworkConfig( - name=name, - default_model=default_model, - state_store=state_store, - enable_parallel_execution=enable_parallel_execution, - ) - - network = AgentNetwork(config=config, router=router) - - # Add all agents to the network - for agent in agents: - network.add_agent(agent) - - return network diff --git a/pkg/hanzo-agent/src/agents/network/node.py b/pkg/hanzo-agent/src/agents/network/node.py deleted file mode 100644 index 5d0cb9fb8..000000000 --- a/pkg/hanzo-agent/src/agents/network/node.py +++ /dev/null @@ -1,77 +0,0 @@ -"""Network node representation for agents.""" - -from __future__ import annotations - -from dataclasses import dataclass, field -from enum import Enum -from typing import Any, Dict, List, Optional - -from ..agent import Agent -from ..run_context import TContext - - -class NodeStatus(Enum): - """Status of a node in the network.""" - - PENDING = "pending" - """Node has not been executed yet.""" - - RUNNING = "running" - """Node is currently executing.""" - - COMPLETED = "completed" - """Node has completed successfully.""" - - FAILED = "failed" - """Node execution failed.""" - - SKIPPED = "skipped" - """Node was skipped (e.g., due to dependencies).""" - - -@dataclass -class NetworkNode: - """A node in the agent network.""" - - agent: Agent[TContext] - """The agent associated with this node.""" - - capabilities: List[str] = field(default_factory=list) - """List of capabilities this agent provides.""" - - dependencies: List[str] = field(default_factory=list) - """List of agent names this node depends on.""" - - metadata: Dict[str, Any] = field(default_factory=dict) - """Additional metadata for routing and orchestration.""" - - status: NodeStatus = NodeStatus.PENDING - """Current status of the node.""" - - error: Exception | None = None - """Error if the node failed.""" - - execution_count: int = 0 - """Number of times this node has been executed.""" - - last_execution_time: float | None = None - """Timestamp of last execution.""" - - average_execution_time: float | None = None - """Average execution time in seconds.""" - - def can_execute(self, completed_nodes: List[str]) -> bool: - """Check if this node can execute based on dependencies.""" - return all(dep in completed_nodes for dep in self.dependencies) - - def get_info(self) -> Dict[str, Any]: - """Get information about this node.""" - return { - "name": self.agent.name, - "capabilities": self.capabilities, - "dependencies": self.dependencies, - "status": self.status.value, - "execution_count": self.execution_count, - "average_execution_time": self.average_execution_time, - "metadata": self.metadata, - } diff --git a/pkg/hanzo-agent/src/agents/network/router.py b/pkg/hanzo-agent/src/agents/network/router.py deleted file mode 100644 index aedc149ff..000000000 --- a/pkg/hanzo-agent/src/agents/network/router.py +++ /dev/null @@ -1,408 +0,0 @@ -"""Intelligent routing for agent networks.""" - -from __future__ import annotations - -import re -from abc import ABC, abstractmethod -from dataclasses import dataclass -from typing import Any, Callable, Dict, List, Optional, Union - -from ..items import TResponseInputItem -from ..run_context import RunContextWrapper -from ..logger import logger -from ..tool import function_tool - - -@dataclass -class RoutingDecision: - """Result of a routing decision.""" - - selected_agent: str | None - """The agent selected to handle the request.""" - - confidence: float - """Confidence in the routing decision (0.0 to 1.0).""" - - reason: str | None = None - """Optional explanation for the routing decision.""" - - fallback_agents: List[str] = None - """Ordered list of fallback agents if primary fails.""" - - metadata: Dict[str, Any] = None - """Additional metadata about the routing decision.""" - - -class Router(ABC): - """Abstract base class for routing strategies.""" - - def __init__(self): - self.agent_info: Dict[str, Dict[str, Any]] = {} - - def update_agent_info( - self, agent_name: str, capabilities: List[str], metadata: Dict[str, Any] - ) -> None: - """Update information about an agent.""" - self.agent_info[agent_name] = { - "capabilities": capabilities, - "metadata": metadata, - } - - def remove_agent_info(self, agent_name: str) -> None: - """Remove information about an agent.""" - self.agent_info.pop(agent_name, None) - - @abstractmethod - async def route( - self, - input: str | list[TResponseInputItem], - available_agents: List[str], - context: RunContextWrapper[Any] | None = None, - ) -> RoutingDecision: - """Route the input to the most appropriate agent. - - Args: - input: The input to route - available_agents: List of available agent names - context: Optional context for routing decisions - - Returns: - Routing decision with selected agent - """ - pass - - -class SemanticRouter(Router): - """Router that uses semantic understanding to route requests.""" - - def __init__(self, model: str | None = None): - super().__init__() - self.model = model - - async def route( - self, - input: str | list[TResponseInputItem], - available_agents: List[str], - context: RunContextWrapper[Any] | None = None, - ) -> RoutingDecision: - """Route based on semantic understanding of the input.""" - if not available_agents: - return RoutingDecision(selected_agent=None, confidence=0.0) - - # Extract text from input - if isinstance(input, str): - text = input - else: - # Get the last user message - text = "" - for item in reversed(input): - if isinstance(item, dict) and item.get("role") == "user": - text = item.get("content", "") - break - - if not text: - # Default to first available agent - return RoutingDecision( - selected_agent=available_agents[0], - confidence=0.5, - reason="No user input found, using default agent", - ) - - # Score each agent based on capabilities - scores: Dict[str, float] = {} - - for agent_name in available_agents: - if agent_name not in self.agent_info: - scores[agent_name] = 0.5 # Default score - continue - - info = self.agent_info[agent_name] - capabilities = info.get("capabilities", []) - - # Simple keyword matching for now - score = 0.0 - matches = [] - - for capability in capabilities: - if capability.lower() in text.lower(): - score += 1.0 - matches.append(capability) - - # Normalize score - if capabilities: - score = score / len(capabilities) - else: - score = 0.5 - - scores[agent_name] = score - - if matches: - logger.debug(f"Agent '{agent_name}' matched capabilities: {matches}") - - # Select agent with highest score - best_agent = max(scores.keys(), key=lambda k: scores[k]) - best_score = scores[best_agent] - - # Get fallback agents - sorted_agents = sorted(scores.keys(), key=lambda k: scores[k], reverse=True) - fallback_agents = sorted_agents[1:] if len(sorted_agents) > 1 else [] - - return RoutingDecision( - selected_agent=best_agent, - confidence=best_score, - reason=f"Best match based on capabilities (score: {best_score:.2f})", - fallback_agents=fallback_agents, - metadata={"scores": scores}, - ) - - -class RuleBasedRouter(Router): - """Router that uses predefined rules to route requests.""" - - def __init__(self): - super().__init__() - self.rules: List[RoutingRule] = [] - - def add_rule( - self, - pattern: str | re.Pattern, - agent: str, - priority: int = 0, - condition: Callable[[str], bool] | None = None, - ) -> None: - """Add a routing rule. - - Args: - pattern: Regex pattern to match - agent: Agent to route to if pattern matches - priority: Priority of the rule (higher = higher priority) - condition: Optional additional condition function - """ - if isinstance(pattern, str): - pattern = re.compile(pattern, re.IGNORECASE) - - self.rules.append( - RoutingRule( - pattern=pattern, - agent=agent, - priority=priority, - condition=condition, - ) - ) - - # Sort rules by priority - self.rules.sort(key=lambda r: r.priority, reverse=True) - - async def route( - self, - input: str | list[TResponseInputItem], - available_agents: List[str], - context: RunContextWrapper[Any] | None = None, - ) -> RoutingDecision: - """Route based on predefined rules.""" - # Extract text from input - if isinstance(input, str): - text = input - else: - # Get the last user message - text = "" - for item in reversed(input): - if isinstance(item, dict) and item.get("role") == "user": - text = item.get("content", "") - break - - # Check each rule - for rule in self.rules: - if rule.agent not in available_agents: - continue - - if rule.pattern.search(text): - # Check additional condition if provided - if rule.condition and not rule.condition(text): - continue - - return RoutingDecision( - selected_agent=rule.agent, - confidence=1.0, - reason=f"Matched rule: {rule.pattern.pattern}", - ) - - # No rule matched - if available_agents: - return RoutingDecision( - selected_agent=available_agents[0], - confidence=0.3, - reason="No rule matched, using default agent", - ) - else: - return RoutingDecision( - selected_agent=None, - confidence=0.0, - reason="No agents available", - ) - - -class LoadBalancingRouter(Router): - """Router that distributes load across agents.""" - - def __init__(self, strategy: str = "round_robin"): - super().__init__() - self.strategy = strategy - self.agent_loads: Dict[str, int] = {} - self.last_index = 0 - - async def route( - self, - input: str | list[TResponseInputItem], - available_agents: List[str], - context: RunContextWrapper[Any] | None = None, - ) -> RoutingDecision: - """Route based on load balancing strategy.""" - if not available_agents: - return RoutingDecision(selected_agent=None, confidence=0.0) - - if self.strategy == "round_robin": - # Round-robin selection - self.last_index = (self.last_index + 1) % len(available_agents) - selected = available_agents[self.last_index] - - return RoutingDecision( - selected_agent=selected, - confidence=1.0, - reason="Round-robin selection", - ) - - elif self.strategy == "least_loaded": - # Select agent with least load - loads = [ - (agent, self.agent_loads.get(agent, 0)) for agent in available_agents - ] - loads.sort(key=lambda x: x[1]) - - selected = loads[0][0] - self.agent_loads[selected] = self.agent_loads.get(selected, 0) + 1 - - return RoutingDecision( - selected_agent=selected, - confidence=1.0, - reason=f"Least loaded agent (load: {loads[0][1]})", - metadata={"loads": dict(loads)}, - ) - - else: - # Default to first agent - return RoutingDecision( - selected_agent=available_agents[0], - confidence=0.5, - reason="Unknown strategy, using default", - ) - - def reset_load(self, agent: str | None = None) -> None: - """Reset load counters.""" - if agent: - self.agent_loads[agent] = 0 - else: - self.agent_loads.clear() - - -@dataclass -class RoutingRule: - """A rule for rule-based routing.""" - - pattern: re.Pattern - agent: str - priority: int = 0 - condition: Callable[[str], bool] | None = None - - -class RoutingStrategy: - """Composite routing strategy that can combine multiple routers.""" - - def __init__(self): - self.routers: List[Tuple[Router, float]] = [] - - def add_router(self, router: Router, weight: float = 1.0) -> None: - """Add a router with optional weight.""" - self.routers.append((router, weight)) - - async def route( - self, - input: str | list[TResponseInputItem], - available_agents: List[str], - context: RunContextWrapper[Any] | None = None, - ) -> RoutingDecision: - """Route using weighted combination of routers.""" - if not self.routers: - return RoutingDecision(selected_agent=None, confidence=0.0) - - # Get decisions from all routers - decisions: List[Tuple[RoutingDecision, float]] = [] - - for router, weight in self.routers: - decision = await router.route(input, available_agents, context) - decisions.append((decision, weight)) - - # Weighted voting - agent_scores: Dict[str, float] = {} - total_weight = sum(weight for _, weight in decisions) - - for decision, weight in decisions: - if decision.selected_agent: - score = decision.confidence * weight / total_weight - agent_scores[decision.selected_agent] = ( - agent_scores.get(decision.selected_agent, 0) + score - ) - - if not agent_scores: - return RoutingDecision(selected_agent=None, confidence=0.0) - - # Select agent with highest weighted score - best_agent = max(agent_scores.keys(), key=lambda k: agent_scores[k]) - confidence = agent_scores[best_agent] - - return RoutingDecision( - selected_agent=best_agent, - confidence=confidence, - reason="Weighted routing decision", - metadata={"agent_scores": agent_scores}, - ) - - -def routing_strategy( - semantic_weight: float = 1.0, - rules: List[Dict[str, Any]] | None = None, - load_balancing: str | None = None, -) -> RoutingStrategy: - """Create a composite routing strategy. - - Args: - semantic_weight: Weight for semantic routing (0 to disable) - rules: List of rule definitions - load_balancing: Load balancing strategy ("round_robin" or "least_loaded") - - Returns: - Configured routing strategy - """ - strategy = RoutingStrategy() - - # Add semantic router - if semantic_weight > 0: - strategy.add_router(SemanticRouter(), semantic_weight) - - # Add rule-based router - if rules: - rule_router = RuleBasedRouter() - for rule in rules: - rule_router.add_rule( - pattern=rule["pattern"], - agent=rule["agent"], - priority=rule.get("priority", 0), - condition=rule.get("condition"), - ) - strategy.add_router(rule_router, 2.0) # Higher weight for explicit rules - - # Add load balancing router - if load_balancing: - strategy.add_router(LoadBalancingRouter(load_balancing), 0.5) - - return strategy diff --git a/pkg/hanzo-agent/src/agents/orchestration/__init__.py b/pkg/hanzo-agent/src/agents/orchestration/__init__.py deleted file mode 100644 index 0d8d11ec9..000000000 --- a/pkg/hanzo-agent/src/agents/orchestration/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Orchestration tools for agent systems. - -This module provides advanced orchestration capabilities including -workflow management, execution tracking, and UI streaming support. -""" - -from .orchestrator import Orchestrator, OrchestrationConfig -from .workflow import Workflow, WorkflowStep, StepType, Step -from .executor import WorkflowExecutor, ExecutionResult -from .ui_stream import UIStreamer, StreamUpdate, UpdateType - -__all__ = [ - "Orchestrator", - "OrchestrationConfig", - "Workflow", - "WorkflowStep", - "StepType", - "Step", - "WorkflowExecutor", - "ExecutionResult", - "UIStreamer", - "StreamUpdate", - "UpdateType", -] diff --git a/pkg/hanzo-agent/src/agents/orchestration/executor.py b/pkg/hanzo-agent/src/agents/orchestration/executor.py deleted file mode 100644 index c44c3f9a0..000000000 --- a/pkg/hanzo-agent/src/agents/orchestration/executor.py +++ /dev/null @@ -1,492 +0,0 @@ -"""Workflow execution engine.""" - -from __future__ import annotations - -import asyncio -import time -import uuid -from dataclasses import dataclass, field -from typing import Any, Dict, List, Optional - -from ..exceptions import AgentsException -from ..logger import logger -from ..network.network import AgentNetwork -from ..result import RunResult -from ..run_context import RunContextWrapper, TContext -from ..state.store import StateStore -from ..tracing import custom_span -from .ui_stream import UIStreamer, StreamUpdate, UpdateType -from .workflow import Workflow, WorkflowStep, StepType - - -@dataclass -class StepResult: - """Result of a workflow step execution.""" - - step_id: str - success: bool - output: Any = None - error: str | None = None - start_time: float = field(default_factory=time.time) - end_time: float | None = None - retries: int = 0 - - @property - def duration(self) -> float: - """Get execution duration.""" - if self.end_time: - return self.end_time - self.start_time - return 0.0 - - -@dataclass -class ExecutionResult: - """Result of a workflow execution.""" - - workflow_id: str - execution_id: str - success: bool - output: Any = None - error: str | None = None - steps_completed: int = 0 - total_steps: int = 0 - step_results: Dict[str, StepResult] = field(default_factory=dict) - start_time: float = field(default_factory=time.time) - end_time: float | None = None - metadata: Dict[str, Any] = field(default_factory=dict) - - @property - def duration(self) -> float: - """Get execution duration.""" - if self.end_time: - return self.end_time - self.start_time - return 0.0 - - -class WorkflowExecutor: - """Executes workflows with proper orchestration.""" - - def __init__( - self, - workflow: Workflow, - network: AgentNetwork, - state_store: StateStore | None = None, - retry_failed: bool = True, - max_retries: int = 3, - ): - """Initialize executor. - - Args: - workflow: Workflow to execute - network: Agent network - state_store: State store for persistence - retry_failed: Whether to retry failed steps - max_retries: Maximum retries per step - """ - self.workflow = workflow - self.network = network - self.state_store = state_store - self.retry_failed = retry_failed - self.max_retries = max_retries - self.execution_id = str(uuid.uuid4()) - self.ui_streamer: UIStreamer | None = None - self._step_outputs: Dict[str, Any] = {} - self._completed_steps: set[str] = set() - - def set_ui_streamer(self, streamer: UIStreamer) -> None: - """Set UI streamer for updates.""" - self.ui_streamer = streamer - - async def execute( - self, - input: Any, - context: TContext | None = None, - ) -> ExecutionResult: - """Execute the workflow. - - Args: - input: Initial input data - context: Execution context - - Returns: - Execution result - """ - # Validate workflow - errors = self.workflow.validate() - if errors: - return ExecutionResult( - workflow_id=self.workflow.id, - execution_id=self.execution_id, - success=False, - error=f"Workflow validation failed: {'; '.join(errors)}", - total_steps=len(self.workflow.steps), - ) - - # Initialize result - result = ExecutionResult( - workflow_id=self.workflow.id, - execution_id=self.execution_id, - success=True, - total_steps=len(self.workflow.steps), - ) - - # Create execution context - exec_context = ExecutionContext( - input=input, - context=context, - executor=self, - ) - - # Send start update - await self._send_update( - UpdateType.WORKFLOW_START, - { - "workflow_id": self.workflow.id, - "workflow_name": self.workflow.name, - "total_steps": len(self.workflow.steps), - }, - ) - - try: - # Get execution order - execution_batches = self.workflow.get_execution_order() - - # Execute batches - for batch in execution_batches: - # Execute steps in parallel within batch - batch_tasks = [] - - for step_id in batch: - if step_id in self._completed_steps: - continue - - step = self.workflow.steps[step_id] - task = self._execute_step(step, exec_context, result) - batch_tasks.append(task) - - # Wait for batch to complete - if batch_tasks: - await asyncio.gather(*batch_tasks, return_exceptions=True) - - # Set final output - if self.workflow.entry_point: - result.output = self._step_outputs.get(self.workflow.entry_point) - - except Exception as e: - logger.error(f"Workflow execution failed: {e}") - result.success = False - result.error = str(e) - - finally: - result.end_time = time.time() - result.steps_completed = len(self._completed_steps) - - # Send completion update - await self._send_update( - UpdateType.WORKFLOW_COMPLETE, - { - "success": result.success, - "duration": result.duration, - "steps_completed": result.steps_completed, - "error": result.error, - }, - ) - - return result - - async def _execute_step( - self, - step: WorkflowStep, - context: ExecutionContext, - result: ExecutionResult, - ) -> None: - """Execute a single step.""" - step_result = StepResult(step_id=step.id) - - # Send step start update - await self._send_update( - UpdateType.STEP_START, - { - "step_id": step.id, - "step_name": step.name, - "step_type": step.type.value, - }, - ) - - try: - # Execute based on step type - if step.type == StepType.AGENT: - output = await self._execute_agent_step(step, context) - elif step.type == StepType.PARALLEL: - output = await self._execute_parallel_step(step, context) - elif step.type == StepType.CONDITIONAL: - output = await self._execute_conditional_step(step, context) - elif step.type == StepType.LOOP: - output = await self._execute_loop_step(step, context) - elif step.type == StepType.TRANSFORM: - output = await self._execute_transform_step(step, context) - elif step.type == StepType.WAIT: - output = await self._execute_wait_step(step, context) - else: - raise AgentsException(f"Unknown step type: {step.type}") - - # Store output - self._step_outputs[step.id] = output - self._completed_steps.add(step.id) - - step_result.success = True - step_result.output = output - - except Exception as e: - logger.error(f"Step {step.id} failed: {e}") - step_result.success = False - step_result.error = str(e) - - # Retry if configured - if self.retry_failed and step_result.retries < self.max_retries: - step_result.retries += 1 - logger.info(f"Retrying step {step.id} (attempt {step_result.retries})") - - # Recursive retry - await asyncio.sleep(2**step_result.retries) # Exponential backoff - await self._execute_step(step, context, result) - return - - # Handle error step - if step.on_error and step.on_error in self.workflow.steps: - error_step = self.workflow.steps[step.on_error] - await self._execute_step(error_step, context, result) - - finally: - step_result.end_time = time.time() - result.step_results[step.id] = step_result - - # Send step complete update - await self._send_update( - UpdateType.STEP_COMPLETE, - { - "step_id": step.id, - "success": step_result.success, - "duration": step_result.duration, - "error": step_result.error, - }, - ) - - async def _execute_agent_step( - self, - step: WorkflowStep, - context: ExecutionContext, - ) -> Any: - """Execute an agent step.""" - config = step.config - agent_name = config["agent_name"] - - # Get input - step_input = context.get_step_input(step.id) - - # Apply input transform if provided - if transform := config.get("input_transform"): - step_input = transform(step_input) - - # Run agent - with custom_span("workflow_agent_step", {"agent": agent_name}): - result = await self.network.run( - input=step_input, - starting_agent=agent_name, - context=context.context, - ) - - # Extract output - output = result.final_output - - # Apply output transform if provided - if transform := config.get("output_transform"): - output = transform(output) - - return output - - async def _execute_parallel_step( - self, - step: WorkflowStep, - context: ExecutionContext, - ) -> List[Any]: - """Execute parallel steps.""" - step_ids = step.config["step_ids"] - - # Execute sub-steps in parallel - tasks = [] - for step_id in step_ids: - if step_id in self.workflow.steps: - sub_step = self.workflow.steps[step_id] - task = self._execute_step(sub_step, context, None) - tasks.append(task) - - # Wait for all to complete - await asyncio.gather(*tasks, return_exceptions=True) - - # Collect outputs - outputs = [] - for step_id in step_ids: - if step_id in self._step_outputs: - outputs.append(self._step_outputs[step_id]) - - return outputs - - async def _execute_conditional_step( - self, - step: WorkflowStep, - context: ExecutionContext, - ) -> Any: - """Execute conditional step.""" - condition = step.config["condition"] - if_true = step.config["if_true"] - if_false = step.config.get("if_false") - - # Evaluate condition - step_input = context.get_step_input(step.id) - - if callable(condition): - result = condition(step_input) - else: - # Simple expression evaluation - result = eval(condition, {"input": step_input}) - - # Execute appropriate branch - if result and if_true in self.workflow.steps: - branch_step = self.workflow.steps[if_true] - await self._execute_step(branch_step, context, None) - return self._step_outputs.get(if_true) - elif not result and if_false and if_false in self.workflow.steps: - branch_step = self.workflow.steps[if_false] - await self._execute_step(branch_step, context, None) - return self._step_outputs.get(if_false) - - return None - - async def _execute_loop_step( - self, - step: WorkflowStep, - context: ExecutionContext, - ) -> List[Any]: - """Execute loop step.""" - over = step.config["over"] - body = step.config["body"] - max_iterations = step.config.get("max_iterations") - - # Get items to loop over - step_input = context.get_step_input(step.id) - - if callable(over): - items = over(step_input) - else: - # Simple path evaluation - items = eval(f"input.{over}", {"input": step_input}) - - # Limit iterations - if max_iterations: - items = items[:max_iterations] - - # Execute body for each item - outputs = [] - - for i, item in enumerate(items): - if body in self.workflow.steps: - # Set loop context - context.set_loop_item(item, i) - - body_step = self.workflow.steps[body] - await self._execute_step(body_step, context, None) - - if body in self._step_outputs: - outputs.append(self._step_outputs[body]) - - return outputs - - async def _execute_transform_step( - self, - step: WorkflowStep, - context: ExecutionContext, - ) -> Any: - """Execute transform step.""" - transform = step.config["transform"] - step_input = context.get_step_input(step.id) - - if callable(transform): - return transform(step_input) - else: - raise AgentsException("Transform must be a callable") - - async def _execute_wait_step( - self, - step: WorkflowStep, - context: ExecutionContext, - ) -> None: - """Execute wait step.""" - # Simple timeout wait for now - timeout = step.config.get("timeout", 1.0) - await asyncio.sleep(timeout) - - async def _send_update(self, type: UpdateType, data: Dict[str, Any]) -> None: - """Send update to UI streamer.""" - if self.ui_streamer: - update = StreamUpdate( - execution_id=self.execution_id, - type=type, - data=data, - ) - await self.ui_streamer.send(update) - - def get_status(self) -> Dict[str, Any]: - """Get current execution status.""" - return { - "execution_id": self.execution_id, - "workflow_id": self.workflow.id, - "workflow_name": self.workflow.name, - "steps_completed": len(self._completed_steps), - "total_steps": len(self.workflow.steps), - "completed_steps": list(self._completed_steps), - "outputs": self._step_outputs, - } - - -@dataclass -class ExecutionContext: - """Context for workflow execution.""" - - input: Any - context: TContext | None - executor: WorkflowExecutor - _loop_item: Any = None - _loop_index: int = 0 - - def get_step_input(self, step_id: str) -> Any: - """Get input for a step.""" - step = self.executor.workflow.steps[step_id] - - # If step has dependencies, use their outputs - if step.depends_on: - # Single dependency: use its output directly - if len(step.depends_on) == 1: - dep_id = step.depends_on[0] - if dep_id in self.executor._step_outputs: - return self.executor._step_outputs[dep_id] - - # Multiple dependencies: collect as list - else: - outputs = [] - for dep_id in step.depends_on: - if dep_id in self.executor._step_outputs: - outputs.append(self.executor._step_outputs[dep_id]) - return outputs - - # Check if in loop context - if self._loop_item is not None: - return self._loop_item - - # Default to workflow input - return self.input - - def set_loop_item(self, item: Any, index: int) -> None: - """Set current loop item.""" - self._loop_item = item - self._loop_index = index diff --git a/pkg/hanzo-agent/src/agents/orchestration/orchestrator.py b/pkg/hanzo-agent/src/agents/orchestration/orchestrator.py deleted file mode 100644 index d444f0e85..000000000 --- a/pkg/hanzo-agent/src/agents/orchestration/orchestrator.py +++ /dev/null @@ -1,335 +0,0 @@ -"""Main orchestrator for agent systems.""" - -from __future__ import annotations - -import asyncio -from dataclasses import dataclass, field -from typing import Any, Dict, List, Optional - -from ..agent import Agent -from ..exceptions import AgentsException -from ..logger import logger -from ..network.network import AgentNetwork, NetworkConfig -from ..result import RunResult -from ..run_context import RunContextWrapper, TContext -from ..state.store import StateStore, InMemoryStateStore -from ..tracing import trace, custom_span -from .executor import WorkflowExecutor, ExecutionResult -from .ui_stream import UIStreamer -from .workflow import Workflow - - -@dataclass -class OrchestrationConfig: - """Configuration for orchestration.""" - - name: str = "Agent Orchestrator" - """Name of the orchestrator.""" - - enable_tracing: bool = True - """Whether to enable detailed tracing.""" - - enable_ui_streaming: bool = True - """Whether to enable UI streaming updates.""" - - max_concurrent_workflows: int = 10 - """Maximum number of concurrent workflows.""" - - state_store: StateStore | None = None - """State store for persistence.""" - - retry_failed_steps: bool = True - """Whether to retry failed workflow steps.""" - - max_retries: int = 3 - """Maximum retries for failed steps.""" - - -class Orchestrator: - """High-level orchestrator for complex agent systems. - - This class provides: - - Workflow management and execution - - Network orchestration - - UI streaming capabilities - - Comprehensive tracing and debugging - """ - - def __init__( - self, - config: OrchestrationConfig | None = None, - network: AgentNetwork | None = None, - ): - """Initialize orchestrator. - - Args: - config: Orchestration configuration - network: Agent network to use - """ - self.config = config or OrchestrationConfig() - self.network = network or AgentNetwork() - self.workflows: Dict[str, Workflow] = {} - self.executors: Dict[str, WorkflowExecutor] = {} - self.ui_streamer = UIStreamer() if self.config.enable_ui_streaming else None - self.state_store = self.config.state_store or InMemoryStateStore() - self._semaphore = asyncio.Semaphore(self.config.max_concurrent_workflows) - - def register_workflow(self, workflow: Workflow) -> None: - """Register a workflow with the orchestrator. - - Args: - workflow: Workflow to register - """ - if workflow.id in self.workflows: - raise AgentsException(f"Workflow '{workflow.id}' already registered") - - self.workflows[workflow.id] = workflow - logger.debug(f"Registered workflow: {workflow.id}") - - def register_agent( - self, - agent: Agent[TContext], - capabilities: List[str] | None = None, - metadata: Dict[str, Any] | None = None, - ) -> None: - """Register an agent with the network. - - Args: - agent: Agent to register - capabilities: Agent capabilities - metadata: Additional metadata - """ - self.network.add_agent( - agent, - capabilities=capabilities, - metadata=metadata, - ) - - async def execute_workflow( - self, - workflow_id: str, - input: Any, - context: TContext | None = None, - stream_updates: bool | None = None, - ) -> ExecutionResult: - """Execute a workflow. - - Args: - workflow_id: ID of workflow to execute - input: Input data for the workflow - context: Execution context - stream_updates: Whether to stream UI updates - - Returns: - Execution result - """ - if workflow_id not in self.workflows: - raise AgentsException(f"Workflow '{workflow_id}' not found") - - workflow = self.workflows[workflow_id] - - # Use semaphore to limit concurrent workflows - async with self._semaphore: - # Create executor - executor = WorkflowExecutor( - workflow=workflow, - network=self.network, - state_store=self.state_store, - retry_failed=self.config.retry_failed_steps, - max_retries=self.config.max_retries, - ) - - # Store executor - execution_id = executor.execution_id - self.executors[execution_id] = executor - - # Set up UI streaming if enabled - if stream_updates or ( - stream_updates is None and self.config.enable_ui_streaming - ): - if self.ui_streamer: - executor.set_ui_streamer(self.ui_streamer) - - # Execute with tracing - trace_name = f"workflow_execution:{workflow.name}" - - with trace(trace_name, metadata={"workflow_id": workflow_id}): - try: - result = await executor.execute(input, context) - return result - finally: - # Clean up - del self.executors[execution_id] - - async def execute_parallel_workflows( - self, - executions: List[Dict[str, Any]], - context: TContext | None = None, - ) -> List[ExecutionResult]: - """Execute multiple workflows in parallel. - - Args: - executions: List of execution specs with 'workflow_id' and 'input' - context: Shared context - - Returns: - List of execution results - """ - tasks = [] - - for spec in executions: - task = self.execute_workflow( - workflow_id=spec["workflow_id"], - input=spec["input"], - context=context, - stream_updates=spec.get("stream_updates", True), - ) - tasks.append(task) - - results = await asyncio.gather(*tasks, return_exceptions=True) - - # Handle exceptions - final_results = [] - for i, result in enumerate(results): - if isinstance(result, Exception): - logger.error( - f"Workflow {executions[i]['workflow_id']} failed: {result}" - ) - # Create failed result - final_results.append( - ExecutionResult( - workflow_id=executions[i]["workflow_id"], - execution_id="failed", - success=False, - error=str(result), - steps_completed=0, - total_steps=0, - ) - ) - else: - final_results.append(result) - - return final_results - - async def run_agent( - self, - agent_name: str, - input: Any, - context: TContext | None = None, - **kwargs, - ) -> RunResult: - """Run a single agent through the network. - - Args: - agent_name: Name of agent to run - input: Input for the agent - context: Execution context - **kwargs: Additional arguments for runner - - Returns: - Agent run result - """ - return await self.network.run( - input=input, - starting_agent=agent_name, - context=context, - **kwargs, - ) - - def create_workflow_from_agents( - self, - name: str, - agents: List[str], - parallel: bool = False, - ) -> Workflow: - """Create a workflow from a list of agents. - - Args: - name: Workflow name - agents: List of agent names - parallel: Whether to run agents in parallel - - Returns: - Created workflow - """ - workflow = Workflow(name=name) - - if parallel: - # Create parallel execution - parallel_agents = [] - for agent_name in agents: - step = workflow.add_agent_step( - name=f"Run {agent_name}", - agent_name=agent_name, - ) - parallel_agents.append(step.id) - - # Mark as parallel - for step_id in parallel_agents[1:]: - workflow.steps[step_id].depends_on = [] - - else: - # Create sequential execution - for agent_name in agents: - workflow.add_agent_step( - name=f"Run {agent_name}", - agent_name=agent_name, - ) - - return workflow - - def get_execution_status(self, execution_id: str) -> Dict[str, Any] | None: - """Get status of a running execution. - - Args: - execution_id: Execution ID - - Returns: - Status information or None if not found - """ - executor = self.executors.get(execution_id) - - if not executor: - return None - - return executor.get_status() - - async def stream_updates(self, execution_id: str | None = None): - """Stream UI updates for executions. - - Args: - execution_id: Specific execution to stream, or None for all - - Yields: - Stream updates - """ - if not self.ui_streamer: - return - - async for update in self.ui_streamer.stream(execution_id): - yield update - - def visualize_workflow(self, workflow_id: str) -> str: - """Generate a visual representation of a workflow. - - Args: - workflow_id: Workflow to visualize - - Returns: - Mermaid diagram string - """ - if workflow_id not in self.workflows: - raise AgentsException(f"Workflow '{workflow_id}' not found") - - workflow = self.workflows[workflow_id] - return workflow.to_mermaid() - - async def get_state(self, key: str, namespace: str | None = None) -> Any: - """Get a value from the state store.""" - return await self.state_store.get(key, namespace) - - async def set_state( - self, key: str, value: Any, namespace: str | None = None - ) -> None: - """Set a value in the state store.""" - await self.state_store.set(key, value, namespace) diff --git a/pkg/hanzo-agent/src/agents/orchestration/ui_stream.py b/pkg/hanzo-agent/src/agents/orchestration/ui_stream.py deleted file mode 100644 index 010ad0e2d..000000000 --- a/pkg/hanzo-agent/src/agents/orchestration/ui_stream.py +++ /dev/null @@ -1,377 +0,0 @@ -"""UI streaming support for real-time updates.""" - -from __future__ import annotations - -import asyncio -import time -from dataclasses import dataclass, field -from enum import Enum -from typing import Any, AsyncIterator, Dict, List, Optional - -from ..logger import logger - - -class UpdateType(Enum): - """Type of UI update.""" - - WORKFLOW_START = "workflow_start" - """Workflow execution started.""" - - WORKFLOW_COMPLETE = "workflow_complete" - """Workflow execution completed.""" - - STEP_START = "step_start" - """Step execution started.""" - - STEP_COMPLETE = "step_complete" - """Step execution completed.""" - - STEP_PROGRESS = "step_progress" - """Step progress update.""" - - AGENT_MESSAGE = "agent_message" - """Message from an agent.""" - - TOOL_CALL = "tool_call" - """Tool was called.""" - - ERROR = "error" - """An error occurred.""" - - LOG = "log" - """Log message.""" - - CUSTOM = "custom" - """Custom update type.""" - - -@dataclass -class StreamUpdate: - """A single stream update.""" - - execution_id: str - """ID of the execution this update belongs to.""" - - type: UpdateType - """Type of update.""" - - data: Dict[str, Any] - """Update data.""" - - timestamp: float = field(default_factory=time.time) - """When the update was created.""" - - metadata: Dict[str, Any] = field(default_factory=dict) - """Additional metadata.""" - - -class UIStreamer: - """Manages UI streaming for workflow execution.""" - - def __init__(self, buffer_size: int = 1000): - """Initialize UI streamer. - - Args: - buffer_size: Maximum updates to buffer - """ - self.buffer_size = buffer_size - self._queues: Dict[str, asyncio.Queue[StreamUpdate]] = {} - self._subscribers: Dict[str, List[asyncio.Queue[StreamUpdate]]] = {} - self._buffer: List[StreamUpdate] = [] - self._lock = asyncio.Lock() - - async def send(self, update: StreamUpdate) -> None: - """Send an update to subscribers. - - Args: - update: Update to send - """ - async with self._lock: - # Add to buffer - self._buffer.append(update) - if len(self._buffer) > self.buffer_size: - self._buffer.pop(0) - - # Send to execution-specific subscribers - if update.execution_id in self._subscribers: - for queue in self._subscribers[update.execution_id]: - try: - await queue.put(update) - except asyncio.QueueFull: - logger.warning( - f"UI stream queue full for execution {update.execution_id}" - ) - - # Send to global subscribers - if "*" in self._subscribers: - for queue in self._subscribers["*"]: - try: - await queue.put(update) - except asyncio.QueueFull: - logger.warning("UI stream queue full for global subscriber") - - async def stream( - self, - execution_id: str | None = None, - include_history: bool = True, - queue_size: int = 100, - ) -> AsyncIterator[StreamUpdate]: - """Stream updates for an execution. - - Args: - execution_id: Execution to stream, or None for all - include_history: Whether to include buffered updates - queue_size: Size of subscriber queue - - Yields: - Stream updates - """ - # Create subscriber queue - queue: asyncio.Queue[StreamUpdate] = asyncio.Queue(maxsize=queue_size) - - # Subscribe - sub_key = execution_id or "*" - async with self._lock: - if sub_key not in self._subscribers: - self._subscribers[sub_key] = [] - self._subscribers[sub_key].append(queue) - - # Send history if requested - if include_history: - for update in self._buffer: - if execution_id is None or update.execution_id == execution_id: - await queue.put(update) - - try: - # Stream updates - while True: - update = await queue.get() - yield update - - finally: - # Unsubscribe - async with self._lock: - if sub_key in self._subscribers: - self._subscribers[sub_key].remove(queue) - if not self._subscribers[sub_key]: - del self._subscribers[sub_key] - - async def send_workflow_start( - self, - execution_id: str, - workflow_name: str, - total_steps: int, - **kwargs, - ) -> None: - """Send workflow start update.""" - await self.send( - StreamUpdate( - execution_id=execution_id, - type=UpdateType.WORKFLOW_START, - data={ - "workflow_name": workflow_name, - "total_steps": total_steps, - **kwargs, - }, - ) - ) - - async def send_workflow_complete( - self, - execution_id: str, - success: bool, - duration: float, - **kwargs, - ) -> None: - """Send workflow completion update.""" - await self.send( - StreamUpdate( - execution_id=execution_id, - type=UpdateType.WORKFLOW_COMPLETE, - data={ - "success": success, - "duration": duration, - **kwargs, - }, - ) - ) - - async def send_step_start( - self, - execution_id: str, - step_id: str, - step_name: str, - **kwargs, - ) -> None: - """Send step start update.""" - await self.send( - StreamUpdate( - execution_id=execution_id, - type=UpdateType.STEP_START, - data={ - "step_id": step_id, - "step_name": step_name, - **kwargs, - }, - ) - ) - - async def send_step_complete( - self, - execution_id: str, - step_id: str, - success: bool, - duration: float, - **kwargs, - ) -> None: - """Send step completion update.""" - await self.send( - StreamUpdate( - execution_id=execution_id, - type=UpdateType.STEP_COMPLETE, - data={ - "step_id": step_id, - "success": success, - "duration": duration, - **kwargs, - }, - ) - ) - - async def send_step_progress( - self, - execution_id: str, - step_id: str, - progress: float, - message: str | None = None, - **kwargs, - ) -> None: - """Send step progress update. - - Args: - execution_id: Execution ID - step_id: Step ID - progress: Progress percentage (0-100) - message: Optional progress message - **kwargs: Additional data - """ - await self.send( - StreamUpdate( - execution_id=execution_id, - type=UpdateType.STEP_PROGRESS, - data={ - "step_id": step_id, - "progress": progress, - "message": message, - **kwargs, - }, - ) - ) - - async def send_agent_message( - self, - execution_id: str, - agent_name: str, - role: str, - content: str, - **kwargs, - ) -> None: - """Send agent message update.""" - await self.send( - StreamUpdate( - execution_id=execution_id, - type=UpdateType.AGENT_MESSAGE, - data={ - "agent_name": agent_name, - "role": role, - "content": content, - **kwargs, - }, - ) - ) - - async def send_tool_call( - self, - execution_id: str, - tool_name: str, - arguments: Dict[str, Any], - result: Any | None = None, - **kwargs, - ) -> None: - """Send tool call update.""" - await self.send( - StreamUpdate( - execution_id=execution_id, - type=UpdateType.TOOL_CALL, - data={ - "tool_name": tool_name, - "arguments": arguments, - "result": result, - **kwargs, - }, - ) - ) - - async def send_error( - self, - execution_id: str, - error: str, - step_id: str | None = None, - **kwargs, - ) -> None: - """Send error update.""" - await self.send( - StreamUpdate( - execution_id=execution_id, - type=UpdateType.ERROR, - data={ - "error": error, - "step_id": step_id, - **kwargs, - }, - ) - ) - - async def send_log( - self, - execution_id: str, - level: str, - message: str, - **kwargs, - ) -> None: - """Send log message update.""" - await self.send( - StreamUpdate( - execution_id=execution_id, - type=UpdateType.LOG, - data={ - "level": level, - "message": message, - **kwargs, - }, - ) - ) - - def get_buffer(self, execution_id: str | None = None) -> List[StreamUpdate]: - """Get buffered updates. - - Args: - execution_id: Filter by execution ID - - Returns: - List of buffered updates - """ - if execution_id: - return [u for u in self._buffer if u.execution_id == execution_id] - return self._buffer.copy() - - def clear_buffer(self, execution_id: str | None = None) -> None: - """Clear buffered updates. - - Args: - execution_id: Clear only for specific execution - """ - if execution_id: - self._buffer = [u for u in self._buffer if u.execution_id != execution_id] - else: - self._buffer.clear() diff --git a/pkg/hanzo-agent/src/agents/orchestration/workflow.py b/pkg/hanzo-agent/src/agents/orchestration/workflow.py deleted file mode 100644 index 1bd40fdbb..000000000 --- a/pkg/hanzo-agent/src/agents/orchestration/workflow.py +++ /dev/null @@ -1,512 +0,0 @@ -"""Workflow definition for orchestration.""" - -from __future__ import annotations - -import uuid -from dataclasses import dataclass, field -from enum import Enum -from typing import Any, Dict, List, Optional, Callable - -from ..exceptions import AgentsException - - -class StepType(Enum): - """Type of workflow step.""" - - AGENT = "agent" - """Run an agent.""" - - PARALLEL = "parallel" - """Run multiple steps in parallel.""" - - CONDITIONAL = "conditional" - """Conditional branching.""" - - LOOP = "loop" - """Loop over items.""" - - TRANSFORM = "transform" - """Data transformation.""" - - WAIT = "wait" - """Wait for condition or timeout.""" - - -@dataclass -class WorkflowStep: - """A single step in a workflow.""" - - id: str = field(default_factory=lambda: str(uuid.uuid4())) - """Unique ID for the step.""" - - name: str = "" - """Human-readable name.""" - - type: StepType = StepType.AGENT - """Type of step.""" - - config: Dict[str, Any] = field(default_factory=dict) - """Step configuration.""" - - depends_on: List[str] = field(default_factory=list) - """IDs of steps this depends on.""" - - retry_config: Dict[str, Any] = field(default_factory=dict) - """Retry configuration for this step.""" - - timeout: float | None = None - """Timeout in seconds.""" - - on_error: str | None = None - """Step to run on error.""" - - metadata: Dict[str, Any] = field(default_factory=dict) - """Additional metadata.""" - - -class Workflow: - """A workflow definition.""" - - def __init__( - self, - name: str, - description: str = "", - version: str = "1.0.0", - ): - """Initialize workflow. - - Args: - name: Workflow name - description: Workflow description - version: Workflow version - """ - self.id = str(uuid.uuid4()) - self.name = name - self.description = description - self.version = version - self.steps: Dict[str, WorkflowStep] = {} - self.entry_point: str | None = None - self.metadata: Dict[str, Any] = {} - - def add_step(self, step: WorkflowStep) -> WorkflowStep: - """Add a step to the workflow. - - Args: - step: Step to add - - Returns: - The added step - """ - if step.id in self.steps: - raise AgentsException(f"Step '{step.id}' already exists") - - self.steps[step.id] = step - - # Set as entry point if first step - if self.entry_point is None: - self.entry_point = step.id - - return step - - def add_agent_step( - self, - name: str, - agent_name: str, - input_transform: Callable[[Any], Any] | None = None, - output_transform: Callable[[Any], Any] | None = None, - **kwargs, - ) -> WorkflowStep: - """Add an agent execution step. - - Args: - name: Step name - agent_name: Name of agent to run - input_transform: Optional input transformation - output_transform: Optional output transformation - **kwargs: Additional step configuration - - Returns: - The created step - """ - config = { - "agent_name": agent_name, - "input_transform": input_transform, - "output_transform": output_transform, - } - config.update(kwargs) - - step = WorkflowStep( - name=name, - type=StepType.AGENT, - config=config, - ) - - # Auto-depend on previous step if exists - if self.steps: - last_step_id = list(self.steps.keys())[-1] - step.depends_on = [last_step_id] - - return self.add_step(step) - - def add_parallel_step( - self, - name: str, - steps: List[WorkflowStep], - ) -> WorkflowStep: - """Add a parallel execution step. - - Args: - name: Step name - steps: Steps to run in parallel - - Returns: - The created parallel step - """ - # Add sub-steps first - step_ids = [] - for sub_step in steps: - self.add_step(sub_step) - step_ids.append(sub_step.id) - - # Create parallel container - parallel_step = WorkflowStep( - name=name, - type=StepType.PARALLEL, - config={"step_ids": step_ids}, - ) - - return self.add_step(parallel_step) - - def add_conditional_step( - self, - name: str, - condition: Callable[[Any], bool] | str, - if_true: str, - if_false: str | None = None, - ) -> WorkflowStep: - """Add a conditional branching step. - - Args: - name: Step name - condition: Condition function or expression - if_true: Step ID to run if true - if_false: Step ID to run if false - - Returns: - The created conditional step - """ - step = WorkflowStep( - name=name, - type=StepType.CONDITIONAL, - config={ - "condition": condition, - "if_true": if_true, - "if_false": if_false, - }, - ) - - return self.add_step(step) - - def add_loop_step( - self, - name: str, - over: str | Callable[[Any], List[Any]], - body: str, - max_iterations: int | None = None, - ) -> WorkflowStep: - """Add a loop step. - - Args: - name: Step name - over: Data path or function to get items - body: Step ID to run for each item - max_iterations: Maximum iterations - - Returns: - The created loop step - """ - step = WorkflowStep( - name=name, - type=StepType.LOOP, - config={ - "over": over, - "body": body, - "max_iterations": max_iterations, - }, - ) - - return self.add_step(step) - - def add_transform_step( - self, - name: str, - transform: Callable[[Any], Any], - ) -> WorkflowStep: - """Add a data transformation step. - - Args: - name: Step name - transform: Transformation function - - Returns: - The created transform step - """ - step = WorkflowStep( - name=name, - type=StepType.TRANSFORM, - config={"transform": transform}, - ) - - return self.add_step(step) - - def get_execution_order(self) -> List[List[str]]: - """Get the execution order respecting dependencies. - - Returns: - List of step ID batches that can run in parallel - """ - if not self.steps: - return [] - - # Topological sort with batching - visited = set() - in_degree = { - step_id: len(step.depends_on) for step_id, step in self.steps.items() - } - - batches = [] - - while len(visited) < len(self.steps): - # Find all steps with no remaining dependencies - batch = [] - for step_id, degree in in_degree.items(): - if step_id not in visited and degree == 0: - batch.append(step_id) - visited.add(step_id) - - if not batch: - # Circular dependency - raise AgentsException("Circular dependency detected in workflow") - - batches.append(batch) - - # Update in-degrees - for step_id in batch: - # Find steps that depend on this one - for other_id, other_step in self.steps.items(): - if step_id in other_step.depends_on: - in_degree[other_id] -= 1 - - return batches - - def validate(self) -> List[str]: - """Validate the workflow. - - Returns: - List of validation errors (empty if valid) - """ - errors = [] - - # Check entry point - if not self.entry_point: - errors.append("No entry point defined") - elif self.entry_point not in self.steps: - errors.append(f"Entry point '{self.entry_point}' not found") - - # Check dependencies - for step_id, step in self.steps.items(): - for dep_id in step.depends_on: - if dep_id not in self.steps: - errors.append( - f"Step '{step_id}' depends on unknown step '{dep_id}'" - ) - - # Check step configurations - for step_id, step in self.steps.items(): - if step.type == StepType.AGENT: - if "agent_name" not in step.config: - errors.append(f"Agent step '{step_id}' missing agent_name") - elif step.type == StepType.CONDITIONAL: - if "condition" not in step.config: - errors.append(f"Conditional step '{step_id}' missing condition") - if "if_true" not in step.config: - errors.append(f"Conditional step '{step_id}' missing if_true") - - # Try to get execution order (checks for cycles) - try: - self.get_execution_order() - except AgentsException as e: - errors.append(str(e)) - - return errors - - def to_dict(self) -> Dict[str, Any]: - """Convert to dictionary representation.""" - return { - "id": self.id, - "name": self.name, - "description": self.description, - "version": self.version, - "entry_point": self.entry_point, - "steps": { - step_id: { - "id": step.id, - "name": step.name, - "type": step.type.value, - "config": step.config, - "depends_on": step.depends_on, - "retry_config": step.retry_config, - "timeout": step.timeout, - "on_error": step.on_error, - "metadata": step.metadata, - } - for step_id, step in self.steps.items() - }, - "metadata": self.metadata, - } - - def to_mermaid(self) -> str: - """Generate Mermaid diagram of the workflow.""" - lines = ["graph TD"] - - # Add nodes - for step_id, step in self.steps.items(): - label = step.name or step_id[:8] - shape = { - StepType.AGENT: f"{step_id}[{label}]", - StepType.PARALLEL: f"{step_id}{{{{{label}}}}}", - StepType.CONDITIONAL: f"{step_id}{{{label}}}", - StepType.LOOP: f"{step_id}(({label}))", - StepType.TRANSFORM: f"{step_id}[/{label}/]", - StepType.WAIT: f"{step_id}[({label})]", - } - lines.append(f" {shape.get(step.type, f'{step_id}[{label}]')}") - - # Add edges - for step_id, step in self.steps.items(): - for dep_id in step.depends_on: - lines.append(f" {dep_id} --> {step_id}") - - # Add conditional branches - for step_id, step in self.steps.items(): - if step.type == StepType.CONDITIONAL: - if_true = step.config.get("if_true") - if_false = step.config.get("if_false") - - if if_true: - lines.append(f" {step_id} -->|true| {if_true}") - if if_false: - lines.append(f" {step_id} -->|false| {if_false}") - - return "\n".join(lines) - - -class Step: - """Helper class for creating workflow steps.""" - - @staticmethod - def agent(agent_name: str, prompt: str, **kwargs) -> WorkflowStep: - """Create an agent execution step. - - Args: - agent_name: Name of the agent to run - prompt: Input prompt for the agent - **kwargs: Additional configuration - - Returns: - WorkflowStep configured for agent execution - """ - config = {"agent_name": agent_name, "prompt": prompt, **kwargs} - return WorkflowStep( - name=f"Run {agent_name}", type=StepType.AGENT, config=config - ) - - @staticmethod - def parallel(steps: List[WorkflowStep]) -> WorkflowStep: - """Create a parallel execution step. - - Args: - steps: Steps to run in parallel - - Returns: - WorkflowStep configured for parallel execution - """ - return WorkflowStep( - name="Parallel Steps", type=StepType.PARALLEL, config={"steps": steps} - ) - - @staticmethod - def conditional( - condition: Callable[[Any], bool], - if_true: WorkflowStep, - if_false: WorkflowStep | None = None, - ) -> WorkflowStep: - """Create a conditional execution step. - - Args: - condition: Function to evaluate condition - if_true: Step to run if condition is true - if_false: Step to run if condition is false - - Returns: - WorkflowStep configured for conditional execution - """ - return WorkflowStep( - name="Conditional", - type=StepType.CONDITIONAL, - config={ - "condition": condition, - "if_true": if_true.id, - "if_false": if_false.id if if_false else None, - }, - ) - - @staticmethod - def loop( - over: str | Callable[[Any], List[Any]], - body: WorkflowStep, - max_iterations: int | None = None, - ) -> WorkflowStep: - """Create a loop execution step. - - Args: - over: Data path or function to get items - body: Step to run for each item - max_iterations: Maximum iterations - - Returns: - WorkflowStep configured for loop execution - """ - return WorkflowStep( - name="Loop", - type=StepType.LOOP, - config={"over": over, "body": body.id, "max_iterations": max_iterations}, - ) - - @staticmethod - def transform(transform: Callable[[Any], Any]) -> WorkflowStep: - """Create a data transformation step. - - Args: - transform: Transformation function - - Returns: - WorkflowStep configured for transformation - """ - return WorkflowStep( - name="Transform", type=StepType.TRANSFORM, config={"transform": transform} - ) - - @staticmethod - def wait(duration: float) -> WorkflowStep: - """Create a wait step. - - Args: - duration: Duration to wait in seconds - - Returns: - WorkflowStep configured for waiting - """ - return WorkflowStep( - name=f"Wait {duration}s", type=StepType.WAIT, config={"duration": duration} - ) diff --git a/pkg/hanzo-agent/src/agents/reflexion.py b/pkg/hanzo-agent/src/agents/reflexion.py deleted file mode 100644 index 4cc93fe9c..000000000 --- a/pkg/hanzo-agent/src/agents/reflexion.py +++ /dev/null @@ -1,78 +0,0 @@ -"""Reflexion module for agent self-correction and rule management. - -This module provides capabilities for agents to: -1. Reflect on past actions and outcomes. -2. Maintain a set of dynamic rules/guidelines. -3. Update their own behavior based on these rules. -""" - -from typing import List, Optional, Dict, Any -from pydantic import BaseModel, Field -import structlog - -from .memory import Memory, MemoryType - -logger = structlog.get_logger() - - -class Rule(BaseModel): - """A behavioral rule for the agent.""" - - id: str - content: str - context: str = "general" - confidence: float = 1.0 - created_at: float - updated_at: float - - -class ReflexionEngine: - """Engine for managing agent reflection and rules.""" - - def __init__(self, memory_client: Memory): - self.memory = memory_client - self.rules: Dict[str, List[Rule]] = {} - - async def load_rules(self, context: str = "general") -> List[Rule]: - """Load rules for a specific context.""" - # TODO: Implement retrieval from hanzo-memory - # functionality will use memory.recall(query=f"rules for {context}") - return self.rules.get(context, []) - - async def add_rule(self, content: str, context: str = "general") -> Rule: - """Add a new rule based on reflection.""" - # TODO: Implement storage in hanzo-memory - # functionality will use memory.remember(content=f"Rule: {content}", metadata={"type": "rule", "context": context}) - import time - import uuid - - rule = Rule( - id=str(uuid.uuid4()), - content=content, - context=context, - created_at=time.time(), - updated_at=time.time(), - ) - if context not in self.rules: - self.rules[context] = [] - self.rules[context].append(rule) - return rule - - async def reflect(self, task: str, outcome: str, success: bool) -> List[str]: - """Analyze a task outcome and generate strictures/improvements.""" - # Simple heuristic for now: if failed, suggest checking the error. - reflections = [] - if not success: - reflections.append( - f"Reflection: Task '{task}' failed. Consider input validation." - ) - reflections.append(f"Reflection: Analyze error message: {outcome}") - else: - reflections.append( - f"Reflection: Task '{task}' succeeded. Reinforce this path." - ) - - # Todo: In production, call an LLM here to analyze the trace: - # response = await self.llm.generate(f"Analyze this execution: {task} -> {outcome}") - - return reflections diff --git a/pkg/hanzo-agent/src/agents/result.py b/pkg/hanzo-agent/src/agents/result.py deleted file mode 100644 index b3cfb0b53..000000000 --- a/pkg/hanzo-agent/src/agents/result.py +++ /dev/null @@ -1,224 +0,0 @@ -from __future__ import annotations - -import abc -import asyncio -from collections.abc import AsyncIterator -from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Any, cast - -from typing_extensions import TypeVar - -from ._run_impl import QueueCompleteSentinel -from .agent import Agent -from .agent_output import AgentOutputSchema -from .exceptions import InputGuardrailTripwireTriggered, MaxTurnsExceeded -from .guardrail import InputGuardrailResult, OutputGuardrailResult -from .items import ItemHelpers, ModelResponse, RunItem, TResponseInputItem -from .logger import logger -from .stream_events import StreamEvent -from .tracing import Trace - -if TYPE_CHECKING: - from ._run_impl import QueueCompleteSentinel - from .agent import Agent - -T = TypeVar("T") - - -@dataclass -class RunResultBase(abc.ABC): - input: str | list[TResponseInputItem] - """The original input items i.e. the items before run() was called. This may be a mutated - version of the input, if there are handoff input filters that mutate the input. - """ - - new_items: list[RunItem] - """The new items generated during the agent run. These include things like new messages, tool - calls and their outputs, etc. - """ - - raw_responses: list[ModelResponse] - """The raw LLM responses generated by the model during the agent run.""" - - final_output: Any - """The output of the last agent.""" - - input_guardrail_results: list[InputGuardrailResult] - """Guardrail results for the input messages.""" - - output_guardrail_results: list[OutputGuardrailResult] - """Guardrail results for the final output of the agent.""" - - @property - @abc.abstractmethod - def last_agent(self) -> Agent[Any]: - """The last agent that was run.""" - - def final_output_as(self, cls: type[T], raise_if_incorrect_type: bool = False) -> T: - """A convenience method to cast the final output to a specific type. By default, the cast - is only for the typechecker. If you set `raise_if_incorrect_type` to True, we'll raise a - TypeError if the final output is not of the given type. - - Args: - cls: The type to cast the final output to. - raise_if_incorrect_type: If True, we'll raise a TypeError if the final output is not of - the given type. - - Returns: - The final output casted to the given type. - """ - if raise_if_incorrect_type and not isinstance(self.final_output, cls): - raise TypeError(f"Final output is not of type {cls.__name__}") - - return cast(T, self.final_output) - - def to_input_list(self) -> list[TResponseInputItem]: - """Creates a new input list, merging the original input with all the new items generated.""" - original_items: list[TResponseInputItem] = ItemHelpers.input_to_new_input_list( - self.input - ) - new_items = [item.to_input_item() for item in self.new_items] - - return original_items + new_items - - -@dataclass -class RunResult(RunResultBase): - _last_agent: Agent[Any] - - @property - def last_agent(self) -> Agent[Any]: - """The last agent that was run.""" - return self._last_agent - - -@dataclass -class RunResultStreaming(RunResultBase): - """The result of an agent run in streaming mode. You can use the `stream_events` method to - receive semantic events as they are generated. - - The streaming method will raise: - - A MaxTurnsExceeded exception if the agent exceeds the max_turns limit. - - A GuardrailTripwireTriggered exception if a guardrail is tripped. - """ - - current_agent: Agent[Any] - """The current agent that is running.""" - - current_turn: int - """The current turn number.""" - - max_turns: int - """The maximum number of turns the agent can run for.""" - - final_output: Any - """The final output of the agent. This is None until the agent has finished running.""" - - _current_agent_output_schema: AgentOutputSchema | None = field(repr=False) - - _trace: Trace | None = field(repr=False) - - is_complete: bool = False - """Whether the agent has finished running.""" - - # Queues that the background run_loop writes to - _event_queue: asyncio.Queue[StreamEvent | QueueCompleteSentinel] = field( - default_factory=asyncio.Queue, repr=False - ) - _input_guardrail_queue: asyncio.Queue[InputGuardrailResult] = field( - default_factory=asyncio.Queue, repr=False - ) - - # Store the asyncio tasks that we're waiting on - _run_impl_task: asyncio.Task[Any] | None = field(default=None, repr=False) - _input_guardrails_task: asyncio.Task[Any] | None = field(default=None, repr=False) - _output_guardrails_task: asyncio.Task[Any] | None = field(default=None, repr=False) - _stored_exception: Exception | None = field(default=None, repr=False) - - @property - def last_agent(self) -> Agent[Any]: - """The last agent that was run. Updates as the agent run progresses, so the true last agent - is only available after the agent run is complete. - """ - return self.current_agent - - async def stream_events(self) -> AsyncIterator[StreamEvent]: - """Stream deltas for new items as they are generated. We're using the types from the - OpenAI Responses API, so these are semantic events: each event has a `type` field that - describes the type of the event, along with the data for that event. - - This will raise: - - A MaxTurnsExceeded exception if the agent exceeds the max_turns limit. - - A GuardrailTripwireTriggered exception if a guardrail is tripped. - """ - while True: - self._check_errors() - if self._stored_exception: - logger.debug("Breaking due to stored exception") - self.is_complete = True - break - - if self.is_complete and self._event_queue.empty(): - break - - try: - item = await self._event_queue.get() - except asyncio.CancelledError: - break - - if isinstance(item, QueueCompleteSentinel): - self._event_queue.task_done() - # Check for errors, in case the queue was completed due to an exception - self._check_errors() - break - - yield item - self._event_queue.task_done() - - if self._trace: - self._trace.finish(reset_current=True) - - self._cleanup_tasks() - - if self._stored_exception: - raise self._stored_exception - - def _check_errors(self): - if self.current_turn > self.max_turns: - self._stored_exception = MaxTurnsExceeded( - f"Max turns ({self.max_turns}) exceeded" - ) - - # Fetch all the completed guardrail results from the queue and raise if needed - while not self._input_guardrail_queue.empty(): - guardrail_result = self._input_guardrail_queue.get_nowait() - if guardrail_result.output.tripwire_triggered: - self._stored_exception = InputGuardrailTripwireTriggered( - guardrail_result - ) - - # Check the tasks for any exceptions - if self._run_impl_task and self._run_impl_task.done(): - exc = self._run_impl_task.exception() - if exc and isinstance(exc, Exception): - self._stored_exception = exc - - if self._input_guardrails_task and self._input_guardrails_task.done(): - exc = self._input_guardrails_task.exception() - if exc and isinstance(exc, Exception): - self._stored_exception = exc - - if self._output_guardrails_task and self._output_guardrails_task.done(): - exc = self._output_guardrails_task.exception() - if exc and isinstance(exc, Exception): - self._stored_exception = exc - - def _cleanup_tasks(self): - if self._run_impl_task and not self._run_impl_task.done(): - self._run_impl_task.cancel() - - if self._input_guardrails_task and not self._input_guardrails_task.done(): - self._input_guardrails_task.cancel() - - if self._output_guardrails_task and not self._output_guardrails_task.done(): - self._output_guardrails_task.cancel() diff --git a/pkg/hanzo-agent/src/agents/run.py b/pkg/hanzo-agent/src/agents/run.py deleted file mode 100644 index a8b37ac99..000000000 --- a/pkg/hanzo-agent/src/agents/run.py +++ /dev/null @@ -1,931 +0,0 @@ -from __future__ import annotations - -import asyncio -import copy -from dataclasses import dataclass, field -from typing import Any, cast - -from openai.types.responses import ResponseCompletedEvent - -from . import Model, _utils -from ._run_impl import ( - NextStepFinalOutput, - NextStepHandoff, - NextStepRunAgain, - QueueCompleteSentinel, - RunImpl, - SingleStepResult, - TraceCtxManager, - get_model_tracing_impl, -) -from .agent import Agent -from .agent_output import AgentOutputSchema -from .exceptions import ( - AgentsException, - InputGuardrailTripwireTriggered, - MaxTurnsExceeded, - ModelBehaviorError, - OutputGuardrailTripwireTriggered, -) -from .guardrail import ( - InputGuardrail, - InputGuardrailResult, - OutputGuardrail, - OutputGuardrailResult, -) -from .handoffs import Handoff, HandoffInputFilter, handoff -from .items import ItemHelpers, ModelResponse, RunItem, TResponseInputItem -from .lifecycle import RunHooks -from .logger import logger -from .model_settings import ModelSettings -from .models.interface import ModelProvider -from .models.openai_provider import OpenAIProvider -from .result import RunResult, RunResultStreaming -from .run_context import RunContextWrapper, TContext -from .stream_events import AgentUpdatedStreamEvent, RawResponsesStreamEvent -from .tracing import Span, SpanError, agent_span, get_current_trace, trace -from .tracing.span_data import AgentSpanData -from .usage import Usage - -DEFAULT_MAX_TURNS = 10 - - -@dataclass -class RunConfig: - """Configures settings for the entire agent run.""" - - model: str | Model | None = None - """The model to use for the entire agent run. If set, will override the model set on every - agent. The model_provider passed in below must be able to resolve this model name. - """ - - model_provider: ModelProvider = field(default_factory=OpenAIProvider) - """The model provider to use when looking up string model names. Defaults to OpenAI.""" - - model_settings: ModelSettings | None = None - """Configure global model settings. Any non-null values will override the agent-specific model - settings. - """ - - handoff_input_filter: HandoffInputFilter | None = None - """A global input filter to apply to all handoffs. If `Handoff.input_filter` is set, then that - will take precedence. The input filter allows you to edit the inputs that are sent to the new - agent. See the documentation in `Handoff.input_filter` for more details. - """ - - input_guardrails: list[InputGuardrail[Any]] | None = None - """A list of input guardrails to run on the initial run input.""" - - output_guardrails: list[OutputGuardrail[Any]] | None = None - """A list of output guardrails to run on the final output of the run.""" - - tracing_disabled: bool = False - """Whether tracing is disabled for the agent run. If disabled, we will not trace the agent run. - """ - - trace_include_sensitive_data: bool = True - """Whether we include potentially sensitive data (for example: inputs/outputs of tool calls or - LLM generations) in traces. If False, we'll still create spans for these events, but the - sensitive data will not be included. - """ - - workflow_name: str = "Agent workflow" - """The name of the run, used for tracing. Should be a logical name for the run, like - "Code generation workflow" or "Customer support agent". - """ - - trace_id: str | None = None - """A custom trace ID to use for tracing. If not provided, we will generate a new trace ID.""" - - group_id: str | None = None - """ - A grouping identifier to use for tracing, to link multiple traces from the same conversation - or process. For example, you might use a chat thread ID. - """ - - trace_metadata: dict[str, Any] | None = None - """ - An optional dictionary of additional metadata to include with the trace. - """ - - -class Runner: - @classmethod - async def run( - cls, - starting_agent: Agent[TContext], - input: str | list[TResponseInputItem], - *, - context: TContext | None = None, - max_turns: int = DEFAULT_MAX_TURNS, - hooks: RunHooks[TContext] | None = None, - run_config: RunConfig | None = None, - ) -> RunResult: - """Run a workflow starting at the given agent. The agent will run in a loop until a final - output is generated. The loop runs like so: - 1. The agent is invoked with the given input. - 2. If there is a final output (i.e. the agent produces something of type - `agent.output_type`, the loop terminates. - 3. If there's a handoff, we run the loop again, with the new agent. - 4. Else, we run tool calls (if any), and re-run the loop. - - In two cases, the agent may raise an exception: - 1. If the max_turns is exceeded, a MaxTurnsExceeded exception is raised. - 2. If a guardrail tripwire is triggered, a GuardrailTripwireTriggered exception is raised. - - Note that only the first agent's input guardrails are run. - - Args: - starting_agent: The starting agent to run. - input: The initial input to the agent. You can pass a single string for a user message, - or a list of input items. - context: The context to run the agent with. - max_turns: The maximum number of turns to run the agent for. A turn is defined as one - AI invocation (including any tool calls that might occur). - hooks: An object that receives callbacks on various lifecycle events. - run_config: Global settings for the entire agent run. - - Returns: - A run result containing all the inputs, guardrail results and the output of the last - agent. Agents may perform handoffs, so we don't know the specific type of the output. - """ - if hooks is None: - hooks = RunHooks[Any]() - if run_config is None: - run_config = RunConfig() - - with TraceCtxManager( - workflow_name=run_config.workflow_name, - trace_id=run_config.trace_id, - group_id=run_config.group_id, - metadata=run_config.trace_metadata, - disabled=run_config.tracing_disabled, - ): - current_turn = 0 - original_input: str | list[TResponseInputItem] = copy.deepcopy(input) - generated_items: list[RunItem] = [] - model_responses: list[ModelResponse] = [] - - context_wrapper: RunContextWrapper[TContext] = RunContextWrapper( - context=context, # type: ignore - ) - - input_guardrail_results: list[InputGuardrailResult] = [] - - current_span: Span[AgentSpanData] | None = None - current_agent = starting_agent - should_run_agent_start_hooks = True - - try: - while True: - # Start an agent span if we don't have one. This span is ended if the current - # agent changes, or if the agent loop ends. - if current_span is None: - handoff_names = [ - h.agent_name for h in cls._get_handoffs(current_agent) - ] - tool_names = [t.name for t in current_agent.tools] - if output_schema := cls._get_output_schema(current_agent): - output_type_name = output_schema.output_type_name() - else: - output_type_name = "str" - - current_span = agent_span( - name=current_agent.name, - handoffs=handoff_names, - tools=tool_names, - output_type=output_type_name, - ) - current_span.start(mark_as_current=True) - - current_turn += 1 - if current_turn > max_turns: - _utils.attach_error_to_span( - current_span, - SpanError( - message="Max turns exceeded", - data={"max_turns": max_turns}, - ), - ) - raise MaxTurnsExceeded(f"Max turns ({max_turns}) exceeded") - - logger.debug( - f"Running agent {current_agent.name} (turn {current_turn})", - ) - - if current_turn == 1: - input_guardrail_results, turn_result = await asyncio.gather( - cls._run_input_guardrails( - starting_agent, - starting_agent.input_guardrails - + (run_config.input_guardrails or []), - copy.deepcopy(input), - context_wrapper, - ), - cls._run_single_turn( - agent=current_agent, - original_input=original_input, - generated_items=generated_items, - hooks=hooks, - context_wrapper=context_wrapper, - run_config=run_config, - should_run_agent_start_hooks=should_run_agent_start_hooks, - ), - ) - else: - turn_result = await cls._run_single_turn( - agent=current_agent, - original_input=original_input, - generated_items=generated_items, - hooks=hooks, - context_wrapper=context_wrapper, - run_config=run_config, - should_run_agent_start_hooks=should_run_agent_start_hooks, - ) - should_run_agent_start_hooks = False - - model_responses.append(turn_result.model_response) - original_input = turn_result.original_input - generated_items = turn_result.generated_items - - if isinstance(turn_result.next_step, NextStepFinalOutput): - output_guardrail_results = await cls._run_output_guardrails( - current_agent.output_guardrails - + (run_config.output_guardrails or []), - current_agent, - turn_result.next_step.output, - context_wrapper, - ) - return RunResult( - input=original_input, - new_items=generated_items, - raw_responses=model_responses, - final_output=turn_result.next_step.output, - _last_agent=current_agent, - input_guardrail_results=input_guardrail_results, - output_guardrail_results=output_guardrail_results, - ) - elif isinstance(turn_result.next_step, NextStepHandoff): - current_agent = cast( - Agent[TContext], turn_result.next_step.new_agent - ) - current_span.finish(reset_current=True) - current_span = None - should_run_agent_start_hooks = True - elif isinstance(turn_result.next_step, NextStepRunAgain): - pass - else: - raise AgentsException( - f"Unknown next step type: {type(turn_result.next_step)}" - ) - finally: - if current_span: - current_span.finish(reset_current=True) - - @classmethod - def run_sync( - cls, - starting_agent: Agent[TContext], - input: str | list[TResponseInputItem], - *, - context: TContext | None = None, - max_turns: int = DEFAULT_MAX_TURNS, - hooks: RunHooks[TContext] | None = None, - run_config: RunConfig | None = None, - ) -> RunResult: - """Run a workflow synchronously, starting at the given agent. Note that this just wraps the - `run` method, so it will not work if there's already an event loop (e.g. inside an async - function, or in a Jupyter notebook or async context like FastAPI). For those cases, use - the `run` method instead. - - The agent will run in a loop until a final output is generated. The loop runs like so: - 1. The agent is invoked with the given input. - 2. If there is a final output (i.e. the agent produces something of type - `agent.output_type`, the loop terminates. - 3. If there's a handoff, we run the loop again, with the new agent. - 4. Else, we run tool calls (if any), and re-run the loop. - - In two cases, the agent may raise an exception: - 1. If the max_turns is exceeded, a MaxTurnsExceeded exception is raised. - 2. If a guardrail tripwire is triggered, a GuardrailTripwireTriggered exception is raised. - - Note that only the first agent's input guardrails are run. - - Args: - starting_agent: The starting agent to run. - input: The initial input to the agent. You can pass a single string for a user message, - or a list of input items. - context: The context to run the agent with. - max_turns: The maximum number of turns to run the agent for. A turn is defined as one - AI invocation (including any tool calls that might occur). - hooks: An object that receives callbacks on various lifecycle events. - run_config: Global settings for the entire agent run. - - Returns: - A run result containing all the inputs, guardrail results and the output of the last - agent. Agents may perform handoffs, so we don't know the specific type of the output. - """ - return asyncio.get_event_loop().run_until_complete( - cls.run( - starting_agent, - input, - context=context, - max_turns=max_turns, - hooks=hooks, - run_config=run_config, - ) - ) - - @classmethod - def run_streamed( - cls, - starting_agent: Agent[TContext], - input: str | list[TResponseInputItem], - context: TContext | None = None, - max_turns: int = DEFAULT_MAX_TURNS, - hooks: RunHooks[TContext] | None = None, - run_config: RunConfig | None = None, - ) -> RunResultStreaming: - """Run a workflow starting at the given agent in streaming mode. The returned result object - contains a method you can use to stream semantic events as they are generated. - - The agent will run in a loop until a final output is generated. The loop runs like so: - 1. The agent is invoked with the given input. - 2. If there is a final output (i.e. the agent produces something of type - `agent.output_type`, the loop terminates. - 3. If there's a handoff, we run the loop again, with the new agent. - 4. Else, we run tool calls (if any), and re-run the loop. - - In two cases, the agent may raise an exception: - 1. If the max_turns is exceeded, a MaxTurnsExceeded exception is raised. - 2. If a guardrail tripwire is triggered, a GuardrailTripwireTriggered exception is raised. - - Note that only the first agent's input guardrails are run. - - Args: - starting_agent: The starting agent to run. - input: The initial input to the agent. You can pass a single string for a user message, - or a list of input items. - context: The context to run the agent with. - max_turns: The maximum number of turns to run the agent for. A turn is defined as one - AI invocation (including any tool calls that might occur). - hooks: An object that receives callbacks on various lifecycle events. - run_config: Global settings for the entire agent run. - - Returns: - A result object that contains data about the run, as well as a method to stream events. - """ - if hooks is None: - hooks = RunHooks[Any]() - if run_config is None: - run_config = RunConfig() - - # If there's already a trace, we don't create a new one. In addition, we can't end the - # trace here, because the actual work is done in `stream_events` and this method ends - # before that. - new_trace = ( - None - if get_current_trace() - else trace( - workflow_name=run_config.workflow_name, - trace_id=run_config.trace_id, - group_id=run_config.group_id, - metadata=run_config.trace_metadata, - disabled=run_config.tracing_disabled, - ) - ) - # Need to start the trace here, because the current trace contextvar is captured at - # asyncio.create_task time - if new_trace: - new_trace.start(mark_as_current=True) - - output_schema = cls._get_output_schema(starting_agent) - context_wrapper: RunContextWrapper[TContext] = RunContextWrapper( - context=context # type: ignore - ) - - streamed_result = RunResultStreaming( - input=copy.deepcopy(input), - new_items=[], - current_agent=starting_agent, - raw_responses=[], - final_output=None, - is_complete=False, - current_turn=0, - max_turns=max_turns, - input_guardrail_results=[], - output_guardrail_results=[], - _current_agent_output_schema=output_schema, - _trace=new_trace, - ) - - # Kick off the actual agent loop in the background and return the streamed result object. - streamed_result._run_impl_task = asyncio.create_task( - cls._run_streamed_impl( - starting_input=input, - streamed_result=streamed_result, - starting_agent=starting_agent, - max_turns=max_turns, - hooks=hooks, - context_wrapper=context_wrapper, - run_config=run_config, - ) - ) - return streamed_result - - @classmethod - async def _run_input_guardrails_with_queue( - cls, - agent: Agent[Any], - guardrails: list[InputGuardrail[TContext]], - input: str | list[TResponseInputItem], - context: RunContextWrapper[TContext], - streamed_result: RunResultStreaming, - parent_span: Span[Any], - ): - queue = streamed_result._input_guardrail_queue - - # We'll run the guardrails and push them onto the queue as they complete - guardrail_tasks = [ - asyncio.create_task( - RunImpl.run_single_input_guardrail(agent, guardrail, input, context) - ) - for guardrail in guardrails - ] - guardrail_results = [] - try: - for done in asyncio.as_completed(guardrail_tasks): - result = await done - if result.output.tripwire_triggered: - _utils.attach_error_to_span( - parent_span, - SpanError( - message="Guardrail tripwire triggered", - data={ - "guardrail": result.guardrail.get_name(), - "type": "input_guardrail", - }, - ), - ) - queue.put_nowait(result) - guardrail_results.append(result) - except Exception: - for t in guardrail_tasks: - t.cancel() - raise - - streamed_result.input_guardrail_results = guardrail_results - - @classmethod - async def _run_streamed_impl( - cls, - starting_input: str | list[TResponseInputItem], - streamed_result: RunResultStreaming, - starting_agent: Agent[TContext], - max_turns: int, - hooks: RunHooks[TContext], - context_wrapper: RunContextWrapper[TContext], - run_config: RunConfig, - ): - current_span: Span[AgentSpanData] | None = None - current_agent = starting_agent - current_turn = 0 - should_run_agent_start_hooks = True - - streamed_result._event_queue.put_nowait( - AgentUpdatedStreamEvent(new_agent=current_agent) - ) - - try: - while True: - if streamed_result.is_complete: - break - - # Start an agent span if we don't have one. This span is ended if the current - # agent changes, or if the agent loop ends. - if current_span is None: - handoff_names = [ - h.agent_name for h in cls._get_handoffs(current_agent) - ] - tool_names = [t.name for t in current_agent.tools] - if output_schema := cls._get_output_schema(current_agent): - output_type_name = output_schema.output_type_name() - else: - output_type_name = "str" - - current_span = agent_span( - name=current_agent.name, - handoffs=handoff_names, - tools=tool_names, - output_type=output_type_name, - ) - current_span.start(mark_as_current=True) - - current_turn += 1 - streamed_result.current_turn = current_turn - - if current_turn > max_turns: - _utils.attach_error_to_span( - current_span, - SpanError( - message="Max turns exceeded", - data={"max_turns": max_turns}, - ), - ) - streamed_result._event_queue.put_nowait(QueueCompleteSentinel()) - break - - if current_turn == 1: - # Run the input guardrails in the background and put the results on the queue - streamed_result._input_guardrails_task = asyncio.create_task( - cls._run_input_guardrails_with_queue( - starting_agent, - starting_agent.input_guardrails - + (run_config.input_guardrails or []), - copy.deepcopy( - ItemHelpers.input_to_new_input_list(starting_input) - ), - context_wrapper, - streamed_result, - current_span, - ) - ) - try: - turn_result = await cls._run_single_turn_streamed( - streamed_result, - current_agent, - hooks, - context_wrapper, - run_config, - should_run_agent_start_hooks, - ) - should_run_agent_start_hooks = False - - streamed_result.raw_responses = streamed_result.raw_responses + [ - turn_result.model_response - ] - streamed_result.input = turn_result.original_input - streamed_result.new_items = turn_result.generated_items - - if isinstance(turn_result.next_step, NextStepHandoff): - current_agent = turn_result.next_step.new_agent - current_span.finish(reset_current=True) - current_span = None - should_run_agent_start_hooks = True - streamed_result._event_queue.put_nowait( - AgentUpdatedStreamEvent(new_agent=current_agent) - ) - elif isinstance(turn_result.next_step, NextStepFinalOutput): - streamed_result._output_guardrails_task = asyncio.create_task( - cls._run_output_guardrails( - current_agent.output_guardrails - + (run_config.output_guardrails or []), - current_agent, - turn_result.next_step.output, - context_wrapper, - ) - ) - - try: - output_guardrail_results = ( - await streamed_result._output_guardrails_task - ) - except Exception: - # Exceptions will be checked in the stream_events loop - output_guardrail_results = [] - - streamed_result.output_guardrail_results = ( - output_guardrail_results - ) - streamed_result.final_output = turn_result.next_step.output - streamed_result.is_complete = True - streamed_result._event_queue.put_nowait(QueueCompleteSentinel()) - elif isinstance(turn_result.next_step, NextStepRunAgain): - pass - except Exception as e: - if current_span: - _utils.attach_error_to_span( - current_span, - SpanError( - message="Error in agent run", - data={"error": str(e)}, - ), - ) - streamed_result.is_complete = True - streamed_result._event_queue.put_nowait(QueueCompleteSentinel()) - raise - - streamed_result.is_complete = True - finally: - if current_span: - current_span.finish(reset_current=True) - - @classmethod - async def _run_single_turn_streamed( - cls, - streamed_result: RunResultStreaming, - agent: Agent[TContext], - hooks: RunHooks[TContext], - context_wrapper: RunContextWrapper[TContext], - run_config: RunConfig, - should_run_agent_start_hooks: bool, - ) -> SingleStepResult: - if should_run_agent_start_hooks: - await asyncio.gather( - hooks.on_agent_start(context_wrapper, agent), - ( - agent.hooks.on_start(context_wrapper, agent) - if agent.hooks - else _utils.noop_coroutine() - ), - ) - - output_schema = cls._get_output_schema(agent) - - streamed_result.current_agent = agent - streamed_result._current_agent_output_schema = output_schema - - system_prompt = await agent.get_system_prompt(context_wrapper) - - handoffs = cls._get_handoffs(agent) - - model = cls._get_model(agent, run_config) - model_settings = agent.model_settings.resolve(run_config.model_settings) - final_response: ModelResponse | None = None - - input = ItemHelpers.input_to_new_input_list(streamed_result.input) - input.extend([item.to_input_item() for item in streamed_result.new_items]) - - # 1. Stream the output events - async for event in model.stream_response( - system_prompt, - input, - model_settings, - agent.tools, - output_schema, - handoffs, - get_model_tracing_impl( - run_config.tracing_disabled, run_config.trace_include_sensitive_data - ), - ): - if isinstance(event, ResponseCompletedEvent): - usage = ( - Usage( - requests=1, - input_tokens=event.response.usage.input_tokens, - output_tokens=event.response.usage.output_tokens, - total_tokens=event.response.usage.total_tokens, - ) - if event.response.usage - else Usage() - ) - final_response = ModelResponse( - output=event.response.output, - usage=usage, - referenceable_id=event.response.id, - ) - - streamed_result._event_queue.put_nowait(RawResponsesStreamEvent(data=event)) - - # 2. At this point, the streaming is complete for this turn of the agent loop. - if not final_response: - raise ModelBehaviorError("Model did not produce a final response!") - - # 3. Now, we can process the turn as we do in the non-streaming case - single_step_result = await cls._get_single_step_result_from_response( - agent=agent, - original_input=streamed_result.input, - pre_step_items=streamed_result.new_items, - new_response=final_response, - output_schema=output_schema, - handoffs=handoffs, - hooks=hooks, - context_wrapper=context_wrapper, - run_config=run_config, - ) - - RunImpl.stream_step_result_to_queue( - single_step_result, streamed_result._event_queue - ) - return single_step_result - - @classmethod - async def _run_single_turn( - cls, - *, - agent: Agent[TContext], - original_input: str | list[TResponseInputItem], - generated_items: list[RunItem], - hooks: RunHooks[TContext], - context_wrapper: RunContextWrapper[TContext], - run_config: RunConfig, - should_run_agent_start_hooks: bool, - ) -> SingleStepResult: - # Ensure we run the hooks before anything else - if should_run_agent_start_hooks: - await asyncio.gather( - hooks.on_agent_start(context_wrapper, agent), - ( - agent.hooks.on_start(context_wrapper, agent) - if agent.hooks - else _utils.noop_coroutine() - ), - ) - - system_prompt = await agent.get_system_prompt(context_wrapper) - - output_schema = cls._get_output_schema(agent) - handoffs = cls._get_handoffs(agent) - input = ItemHelpers.input_to_new_input_list(original_input) - input.extend( - [generated_item.to_input_item() for generated_item in generated_items] - ) - - new_response = await cls._get_new_response( - agent, - system_prompt, - input, - output_schema, - handoffs, - context_wrapper, - run_config, - ) - - return await cls._get_single_step_result_from_response( - agent=agent, - original_input=original_input, - pre_step_items=generated_items, - new_response=new_response, - output_schema=output_schema, - handoffs=handoffs, - hooks=hooks, - context_wrapper=context_wrapper, - run_config=run_config, - ) - - @classmethod - async def _get_single_step_result_from_response( - cls, - *, - agent: Agent[TContext], - original_input: str | list[TResponseInputItem], - pre_step_items: list[RunItem], - new_response: ModelResponse, - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - hooks: RunHooks[TContext], - context_wrapper: RunContextWrapper[TContext], - run_config: RunConfig, - ) -> SingleStepResult: - processed_response = RunImpl.process_model_response( - agent=agent, - response=new_response, - output_schema=output_schema, - handoffs=handoffs, - ) - return await RunImpl.execute_tools_and_side_effects( - agent=agent, - original_input=original_input, - pre_step_items=pre_step_items, - new_response=new_response, - processed_response=processed_response, - output_schema=output_schema, - hooks=hooks, - context_wrapper=context_wrapper, - run_config=run_config, - ) - - @classmethod - async def _run_input_guardrails( - cls, - agent: Agent[Any], - guardrails: list[InputGuardrail[TContext]], - input: str | list[TResponseInputItem], - context: RunContextWrapper[TContext], - ) -> list[InputGuardrailResult]: - if not guardrails: - return [] - - guardrail_tasks = [ - asyncio.create_task( - RunImpl.run_single_input_guardrail(agent, guardrail, input, context) - ) - for guardrail in guardrails - ] - - guardrail_results = [] - - for done in asyncio.as_completed(guardrail_tasks): - result = await done - if result.output.tripwire_triggered: - # Cancel all guardrail tasks if a tripwire is triggered. - for t in guardrail_tasks: - t.cancel() - _utils.attach_error_to_current_span( - SpanError( - message="Guardrail tripwire triggered", - data={"guardrail": result.guardrail.get_name()}, - ) - ) - raise InputGuardrailTripwireTriggered(result) - else: - guardrail_results.append(result) - - return guardrail_results - - @classmethod - async def _run_output_guardrails( - cls, - guardrails: list[OutputGuardrail[TContext]], - agent: Agent[TContext], - agent_output: Any, - context: RunContextWrapper[TContext], - ) -> list[OutputGuardrailResult]: - if not guardrails: - return [] - - guardrail_tasks = [ - asyncio.create_task( - RunImpl.run_single_output_guardrail( - guardrail, agent, agent_output, context - ) - ) - for guardrail in guardrails - ] - - guardrail_results = [] - - for done in asyncio.as_completed(guardrail_tasks): - result = await done - if result.output.tripwire_triggered: - # Cancel all guardrail tasks if a tripwire is triggered. - for t in guardrail_tasks: - t.cancel() - _utils.attach_error_to_current_span( - SpanError( - message="Guardrail tripwire triggered", - data={"guardrail": result.guardrail.get_name()}, - ) - ) - raise OutputGuardrailTripwireTriggered(result) - else: - guardrail_results.append(result) - - return guardrail_results - - @classmethod - async def _get_new_response( - cls, - agent: Agent[TContext], - system_prompt: str | None, - input: list[TResponseInputItem], - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - context_wrapper: RunContextWrapper[TContext], - run_config: RunConfig, - ) -> ModelResponse: - model = cls._get_model(agent, run_config) - model_settings = agent.model_settings.resolve(run_config.model_settings) - new_response = await model.get_response( - system_instructions=system_prompt, - input=input, - model_settings=model_settings, - tools=agent.tools, - output_schema=output_schema, - handoffs=handoffs, - tracing=get_model_tracing_impl( - run_config.tracing_disabled, run_config.trace_include_sensitive_data - ), - ) - - context_wrapper.usage.add(new_response.usage) - - return new_response - - @classmethod - def _get_output_schema(cls, agent: Agent[Any]) -> AgentOutputSchema | None: - if agent.output_type is None or agent.output_type is str: - return None - - return AgentOutputSchema(agent.output_type) - - @classmethod - def _get_handoffs(cls, agent: Agent[Any]) -> list[Handoff]: - handoffs = [] - for handoff_item in agent.handoffs: - if isinstance(handoff_item, Handoff): - handoffs.append(handoff_item) - elif isinstance(handoff_item, Agent): - handoffs.append(handoff(handoff_item)) - return handoffs - - @classmethod - def _get_model(cls, agent: Agent[Any], run_config: RunConfig) -> Model: - if isinstance(run_config.model, Model): - return run_config.model - elif isinstance(run_config.model, str): - return run_config.model_provider.get_model(run_config.model) - elif isinstance(agent.model, Model): - return agent.model - - return run_config.model_provider.get_model(agent.model) diff --git a/pkg/hanzo-agent/src/agents/run_context.py b/pkg/hanzo-agent/src/agents/run_context.py deleted file mode 100644 index 579a215f2..000000000 --- a/pkg/hanzo-agent/src/agents/run_context.py +++ /dev/null @@ -1,26 +0,0 @@ -from dataclasses import dataclass, field -from typing import Any, Generic - -from typing_extensions import TypeVar - -from .usage import Usage - -TContext = TypeVar("TContext", default=Any) - - -@dataclass -class RunContextWrapper(Generic[TContext]): - """This wraps the context object that you passed to `Runner.run()`. It also contains - information about the usage of the agent run so far. - - NOTE: Contexts are not passed to the LLM. They're a way to pass dependencies and data to code - you implement, like tool functions, callbacks, hooks, etc. - """ - - context: TContext - """The context object (or None), passed by you to `Runner.run()`""" - - usage: Usage = field(default_factory=Usage) - """The usage of the agent run so far. For streamed responses, the usage will be stale until the - last chunk of the stream is processed. - """ diff --git a/pkg/hanzo-agent/src/agents/state/__init__.py b/pkg/hanzo-agent/src/agents/state/__init__.py deleted file mode 100644 index bf50b3848..000000000 --- a/pkg/hanzo-agent/src/agents/state/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -"""State management for agent networks. - -This module provides tools for sharing state between agents in a network, -enabling collaboration and data persistence. -""" - -from .store import StateStore, InMemoryStateStore, RedisStateStore, FileStateStore -from .namespace import StateNamespace -from .serializer import StateSerializer, JSONSerializer, PickleSerializer - -__all__ = [ - "StateStore", - "InMemoryStateStore", - "RedisStateStore", - "FileStateStore", - "StateNamespace", - "StateSerializer", - "JSONSerializer", - "PickleSerializer", -] diff --git a/pkg/hanzo-agent/src/agents/state/namespace.py b/pkg/hanzo-agent/src/agents/state/namespace.py deleted file mode 100644 index 8e1845f09..000000000 --- a/pkg/hanzo-agent/src/agents/state/namespace.py +++ /dev/null @@ -1,62 +0,0 @@ -"""Namespace support for state stores.""" - -from __future__ import annotations - -from typing import Any, Callable, List, TYPE_CHECKING - -if TYPE_CHECKING: - from .store import StateStore - - -class StateNamespace: - """A namespaced view of a state store. - - This allows you to work with a subset of the state store - without worrying about key collisions. - """ - - def __init__(self, store: StateStore, namespace: str): - self.store = store - self.namespace = namespace - - async def get(self, key: str) -> Any: - """Get a value from this namespace.""" - return await self.store.get(key, self.namespace) - - async def set(self, key: str, value: Any) -> None: - """Set a value in this namespace.""" - await self.store.set(key, value, self.namespace) - - async def delete(self, key: str) -> None: - """Delete a value from this namespace.""" - await self.store.delete(key, self.namespace) - - async def exists(self, key: str) -> bool: - """Check if a key exists in this namespace.""" - return await self.store.exists(key, self.namespace) - - async def keys(self, pattern: str | None = None) -> List[str]: - """List keys in this namespace.""" - return await self.store.keys(pattern, self.namespace) - - async def update(self, key: str, updater: Callable[[Any], Any]) -> Any: - """Update a value atomically in this namespace.""" - return await self.store.update(key, updater, self.namespace) - - async def increment(self, key: str, amount: int = 1) -> int: - """Increment a numeric value in this namespace.""" - return await self.store.increment(key, amount, self.namespace) - - async def append(self, key: str, item: Any) -> List[Any]: - """Append to a list value in this namespace.""" - return await self.store.append(key, item, self.namespace) - - def sub_namespace(self, name: str) -> StateNamespace: - """Create a sub-namespace.""" - return StateNamespace(self.store, f"{self.namespace}:{name}") - - async def clear(self) -> None: - """Clear all keys in this namespace.""" - keys = await self.keys() - for key in keys: - await self.delete(key) diff --git a/pkg/hanzo-agent/src/agents/state/serializer.py b/pkg/hanzo-agent/src/agents/state/serializer.py deleted file mode 100644 index 6a9a1ab0e..000000000 --- a/pkg/hanzo-agent/src/agents/state/serializer.py +++ /dev/null @@ -1,60 +0,0 @@ -"""Serializers for state storage.""" - -from __future__ import annotations - -import json -import pickle -from abc import ABC, abstractmethod -from typing import Any - - -class StateSerializer(ABC): - """Abstract base class for state serializers.""" - - @abstractmethod - def serialize(self, obj: Any) -> bytes: - """Serialize an object to bytes.""" - pass - - @abstractmethod - def deserialize(self, data: bytes) -> Any: - """Deserialize bytes to an object.""" - pass - - -class JSONSerializer(StateSerializer): - """JSON serializer for state storage. - - This is the default serializer. It's human-readable but - limited to JSON-serializable types. - """ - - def __init__(self, encoding: str = "utf-8"): - self.encoding = encoding - - def serialize(self, obj: Any) -> bytes: - """Serialize to JSON bytes.""" - return json.dumps(obj).encode(self.encoding) - - def deserialize(self, data: bytes) -> Any: - """Deserialize from JSON bytes.""" - return json.loads(data.decode(self.encoding)) - - -class PickleSerializer(StateSerializer): - """Pickle serializer for state storage. - - This can handle any Python object but is not human-readable - and has security implications (don't unpickle untrusted data). - """ - - def __init__(self, protocol: int = pickle.HIGHEST_PROTOCOL): - self.protocol = protocol - - def serialize(self, obj: Any) -> bytes: - """Serialize to pickle bytes.""" - return pickle.dumps(obj, protocol=self.protocol) - - def deserialize(self, data: bytes) -> Any: - """Deserialize from pickle bytes.""" - return pickle.loads(data) diff --git a/pkg/hanzo-agent/src/agents/state/store.py b/pkg/hanzo-agent/src/agents/state/store.py deleted file mode 100644 index 67964c906..000000000 --- a/pkg/hanzo-agent/src/agents/state/store.py +++ /dev/null @@ -1,346 +0,0 @@ -"""State store implementations for agent networks.""" - -from __future__ import annotations - -import asyncio -import json -import os -import pickle -from abc import ABC, abstractmethod -from pathlib import Path -from typing import Any, Callable, Dict, List, Optional, TypeVar - -from ..logger import logger -from .namespace import StateNamespace -from .serializer import StateSerializer, JSONSerializer - -T = TypeVar("T") - - -class StateStore(ABC): - """Abstract base class for state stores.""" - - def __init__(self, serializer: StateSerializer | None = None): - self.serializer = serializer or JSONSerializer() - self._locks: Dict[str, asyncio.Lock] = {} - - def _get_lock(self, key: str) -> asyncio.Lock: - """Get or create a lock for a key.""" - if key not in self._locks: - self._locks[key] = asyncio.Lock() - return self._locks[key] - - def _make_key(self, key: str, namespace: str | None = None) -> str: - """Create a namespaced key.""" - if namespace: - return f"{namespace}:{key}" - return key - - @abstractmethod - async def get(self, key: str, namespace: str | None = None) -> Any: - """Get a value from the store.""" - pass - - @abstractmethod - async def set(self, key: str, value: Any, namespace: str | None = None) -> None: - """Set a value in the store.""" - pass - - @abstractmethod - async def delete(self, key: str, namespace: str | None = None) -> None: - """Delete a value from the store.""" - pass - - @abstractmethod - async def exists(self, key: str, namespace: str | None = None) -> bool: - """Check if a key exists.""" - pass - - @abstractmethod - async def keys( - self, pattern: str | None = None, namespace: str | None = None - ) -> List[str]: - """List keys matching a pattern.""" - pass - - async def update( - self, - key: str, - updater: Callable[[Any], Any], - namespace: str | None = None, - ) -> Any: - """Update a value atomically. - - Args: - key: Key to update - updater: Function that takes current value and returns new value - namespace: Optional namespace - - Returns: - The updated value - """ - full_key = self._make_key(key, namespace) - async with self._get_lock(full_key): - current = await self.get(key, namespace) - updated = updater(current) - await self.set(key, updated, namespace) - return updated - - async def increment( - self, key: str, amount: int = 1, namespace: str | None = None - ) -> int: - """Increment a numeric value.""" - - def inc(val): - return (val or 0) + amount - - return await self.update(key, inc, namespace) - - async def append( - self, key: str, item: Any, namespace: str | None = None - ) -> List[Any]: - """Append to a list value.""" - - def app(val): - if val is None: - return [item] - if not isinstance(val, list): - raise ValueError(f"Value at key '{key}' is not a list") - val.append(item) - return val - - return await self.update(key, app, namespace) - - def namespace(self, name: str) -> StateNamespace: - """Create a namespace view of the store.""" - return StateNamespace(self, name) - - -class InMemoryStateStore(StateStore): - """In-memory state store for development and testing.""" - - def __init__(self, serializer: StateSerializer | None = None): - super().__init__(serializer) - self._data: Dict[str, Any] = {} - - async def get(self, key: str, namespace: str | None = None) -> Any: - """Get a value from memory.""" - full_key = self._make_key(key, namespace) - return self._data.get(full_key) - - async def set(self, key: str, value: Any, namespace: str | None = None) -> None: - """Set a value in memory.""" - full_key = self._make_key(key, namespace) - self._data[full_key] = value - logger.debug(f"Set state: {full_key} = {type(value).__name__}") - - async def delete(self, key: str, namespace: str | None = None) -> None: - """Delete a value from memory.""" - full_key = self._make_key(key, namespace) - self._data.pop(full_key, None) - - async def exists(self, key: str, namespace: str | None = None) -> bool: - """Check if a key exists in memory.""" - full_key = self._make_key(key, namespace) - return full_key in self._data - - async def keys( - self, pattern: str | None = None, namespace: str | None = None - ) -> List[str]: - """List keys in memory.""" - prefix = f"{namespace}:" if namespace else "" - keys = [] - - for key in self._data: - if namespace and not key.startswith(prefix): - continue - - # Remove namespace prefix from result - clean_key = key[len(prefix) :] if namespace else key - - if pattern is None or pattern in clean_key: - keys.append(clean_key) - - return keys - - def clear(self) -> None: - """Clear all data (for testing).""" - self._data.clear() - - -class RedisStateStore(StateStore): - """Redis-backed state store for production use.""" - - def __init__( - self, - url: str = "redis://localhost:6379", - serializer: StateSerializer | None = None, - **redis_kwargs, - ): - super().__init__(serializer) - self.url = url - self.redis_kwargs = redis_kwargs - self._redis = None - - async def _get_redis(self): - """Get or create Redis connection.""" - if self._redis is None: - try: - import redis.asyncio as redis - except ImportError: - raise ImportError( - "Redis support requires 'redis' package. Install with: pip install redis" - ) - - self._redis = await redis.from_url(self.url, **self.redis_kwargs) - return self._redis - - async def get(self, key: str, namespace: str | None = None) -> Any: - """Get a value from Redis.""" - redis = await self._get_redis() - full_key = self._make_key(key, namespace) - - data = await redis.get(full_key) - if data is None: - return None - - return self.serializer.deserialize(data) - - async def set(self, key: str, value: Any, namespace: str | None = None) -> None: - """Set a value in Redis.""" - redis = await self._get_redis() - full_key = self._make_key(key, namespace) - - data = self.serializer.serialize(value) - await redis.set(full_key, data) - - async def delete(self, key: str, namespace: str | None = None) -> None: - """Delete a value from Redis.""" - redis = await self._get_redis() - full_key = self._make_key(key, namespace) - await redis.delete(full_key) - - async def exists(self, key: str, namespace: str | None = None) -> bool: - """Check if a key exists in Redis.""" - redis = await self._get_redis() - full_key = self._make_key(key, namespace) - return await redis.exists(full_key) > 0 - - async def keys( - self, pattern: str | None = None, namespace: str | None = None - ) -> List[str]: - """List keys in Redis.""" - redis = await self._get_redis() - - # Build search pattern - prefix = f"{namespace}:" if namespace else "" - search_pattern = f"{prefix}*{pattern}*" if pattern else f"{prefix}*" - - # Get matching keys - keys = [] - cursor = 0 - - while True: - cursor, batch = await redis.scan(cursor, match=search_pattern, count=100) - - for key in batch: - key_str = key.decode() if isinstance(key, bytes) else key - # Remove namespace prefix - clean_key = key_str[len(prefix) :] if namespace else key_str - keys.append(clean_key) - - if cursor == 0: - break - - return keys - - async def close(self) -> None: - """Close Redis connection.""" - if self._redis: - await self._redis.close() - - -class FileStateStore(StateStore): - """File-based state store for persistence without external dependencies.""" - - def __init__( - self, - directory: str | Path = ".agent_state", - serializer: StateSerializer | None = None, - ): - super().__init__(serializer) - self.directory = Path(directory) - self.directory.mkdir(parents=True, exist_ok=True) - - def _get_file_path(self, key: str, namespace: str | None = None) -> Path: - """Get file path for a key.""" - full_key = self._make_key(key, namespace) - # Replace special characters for filesystem compatibility - safe_key = full_key.replace(":", "_").replace("/", "_") - return self.directory / f"{safe_key}.state" - - async def get(self, key: str, namespace: str | None = None) -> Any: - """Get a value from file.""" - file_path = self._get_file_path(key, namespace) - - if not file_path.exists(): - return None - - try: - data = file_path.read_bytes() - return self.serializer.deserialize(data) - except Exception as e: - logger.error(f"Error reading state file {file_path}: {e}") - return None - - async def set(self, key: str, value: Any, namespace: str | None = None) -> None: - """Set a value in file.""" - file_path = self._get_file_path(key, namespace) - - try: - data = self.serializer.serialize(value) - file_path.write_bytes(data) - except Exception as e: - logger.error(f"Error writing state file {file_path}: {e}") - raise - - async def delete(self, key: str, namespace: str | None = None) -> None: - """Delete a file.""" - file_path = self._get_file_path(key, namespace) - - try: - if file_path.exists(): - file_path.unlink() - except Exception as e: - logger.error(f"Error deleting state file {file_path}: {e}") - - async def exists(self, key: str, namespace: str | None = None) -> bool: - """Check if a file exists.""" - file_path = self._get_file_path(key, namespace) - return file_path.exists() - - async def keys( - self, pattern: str | None = None, namespace: str | None = None - ) -> List[str]: - """List keys from files.""" - keys = [] - prefix = f"{namespace}_" if namespace else "" - - for file_path in self.directory.glob("*.state"): - filename = file_path.stem - - # Check namespace - if namespace and not filename.startswith(prefix): - continue - - # Extract key - if namespace: - key = filename[len(prefix) :].replace("_", ":") - else: - key = filename.replace("_", ":") - - # Check pattern - if pattern is None or pattern in key: - keys.append(key) - - return keys diff --git a/pkg/hanzo-agent/src/agents/stream_events.py b/pkg/hanzo-agent/src/agents/stream_events.py deleted file mode 100644 index a6a62d3c1..000000000 --- a/pkg/hanzo-agent/src/agents/stream_events.py +++ /dev/null @@ -1,60 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from typing import Any, Literal, Union - -from typing_extensions import TypeAlias - -from .agent import Agent -from .items import RunItem, TResponseStreamEvent - - -@dataclass -class RawResponsesStreamEvent: - """Streaming event from the LLM. These are 'raw' events, i.e. they are directly passed through - from the LLM. - """ - - data: TResponseStreamEvent - """The raw responses streaming event from the LLM.""" - - type: Literal["raw_response_event"] = "raw_response_event" - """The type of the event.""" - - -@dataclass -class RunItemStreamEvent: - """Streaming events that wrap a `RunItem`. As the agent processes the LLM response, it will - generate these events for new messages, tool calls, tool outputs, handoffs, etc. - """ - - name: Literal[ - "message_output_created", - "handoff_requested", - "handoff_occured", - "tool_called", - "tool_output", - "reasoning_item_created", - ] - """The name of the event.""" - - item: RunItem - """The item that was created.""" - - type: Literal["run_item_stream_event"] = "run_item_stream_event" - - -@dataclass -class AgentUpdatedStreamEvent: - """Event that notifies that there is a new agent running.""" - - new_agent: Agent[Any] - """The new agent.""" - - type: Literal["agent_updated_stream_event"] = "agent_updated_stream_event" - - -StreamEvent: TypeAlias = Union[ - RawResponsesStreamEvent, RunItemStreamEvent, AgentUpdatedStreamEvent -] -"""A streaming event from an agent.""" diff --git a/pkg/hanzo-agent/src/agents/strict_schema.py b/pkg/hanzo-agent/src/agents/strict_schema.py deleted file mode 100644 index 8dd1b7ce1..000000000 --- a/pkg/hanzo-agent/src/agents/strict_schema.py +++ /dev/null @@ -1,184 +0,0 @@ -from __future__ import annotations - -from typing import Any - -from openai import NOT_GIVEN -from typing_extensions import TypeGuard - -from .exceptions import UserError - -_EMPTY_SCHEMA = { - "additionalProperties": False, - "type": "object", - "properties": {}, - "required": [], -} - - -def ensure_strict_json_schema( - schema: dict[str, Any], -) -> dict[str, Any]: - """Mutates the given JSON schema to ensure it conforms to the `strict` standard - that the OpenAI API expects. - """ - if schema == {}: - return _EMPTY_SCHEMA - return _ensure_strict_json_schema(schema, path=(), root=schema) - - -# Adapted from https://github.com/openai/openai-python/blob/main/src/openai/lib/_pydantic.py -def _ensure_strict_json_schema( - json_schema: object, - *, - path: tuple[str, ...], - root: dict[str, object], -) -> dict[str, Any]: - if not is_dict(json_schema): - raise TypeError(f"Expected {json_schema} to be a dictionary; path={path}") - - defs = json_schema.get("$defs") - if is_dict(defs): - for def_name, def_schema in defs.items(): - _ensure_strict_json_schema( - def_schema, path=(*path, "$defs", def_name), root=root - ) - - definitions = json_schema.get("definitions") - if is_dict(definitions): - for definition_name, definition_schema in definitions.items(): - _ensure_strict_json_schema( - definition_schema, - path=(*path, "definitions", definition_name), - root=root, - ) - - typ = json_schema.get("type") - if typ == "object" and "additionalProperties" not in json_schema: - json_schema["additionalProperties"] = False - elif ( - typ == "object" - and "additionalProperties" in json_schema - and json_schema["additionalProperties"] is True - ): - # Allow relaxed schema for kwargs containers generated by function_schema - if json_schema.get("title") == "Kwargs": - return json_schema - raise UserError( - "additionalProperties should not be set for object types. This could be because " - "you're using an older version of Pydantic, or because you configured additional " - "properties to be allowed. If you really need this, update the function or output tool " - "to not use a strict schema." - ) - - # object types - # { 'type': 'object', 'properties': { 'a': {...} } } - properties = json_schema.get("properties") - if is_dict(properties): - json_schema["required"] = list(properties.keys()) - json_schema["properties"] = { - key: _ensure_strict_json_schema( - prop_schema, path=(*path, "properties", key), root=root - ) - for key, prop_schema in properties.items() - } - - # arrays - # { 'type': 'array', 'items': {...} } - items = json_schema.get("items") - if is_dict(items): - json_schema["items"] = _ensure_strict_json_schema( - items, path=(*path, "items"), root=root - ) - - # unions - any_of = json_schema.get("anyOf") - if is_list(any_of): - json_schema["anyOf"] = [ - _ensure_strict_json_schema( - variant, path=(*path, "anyOf", str(i)), root=root - ) - for i, variant in enumerate(any_of) - ] - - # intersections - all_of = json_schema.get("allOf") - if is_list(all_of): - if len(all_of) == 1: - json_schema.update( - _ensure_strict_json_schema( - all_of[0], path=(*path, "allOf", "0"), root=root - ) - ) - json_schema.pop("allOf") - else: - json_schema["allOf"] = [ - _ensure_strict_json_schema( - entry, path=(*path, "allOf", str(i)), root=root - ) - for i, entry in enumerate(all_of) - ] - - # strip `None` defaults as there's no meaningful distinction here - # the schema will still be `nullable` and the model will default - # to using `None` anyway - if json_schema.get("default", NOT_GIVEN) is None: - json_schema.pop("default") - - # we can't use `$ref`s if there are also other properties defined, e.g. - # `{"$ref": "...", "description": "my description"}` - # - # so we unravel the ref - # `{"type": "string", "description": "my description"}` - ref = json_schema.get("$ref") - if ref and has_more_than_n_keys(json_schema, 1): - assert isinstance(ref, str), f"Received non-string $ref - {ref}" - - resolved = resolve_ref(root=root, ref=ref) - if not is_dict(resolved): - raise ValueError( - f"Expected `$ref: {ref}` to resolved to a dictionary but got {resolved}" - ) - - # properties from the json schema take priority over the ones on the `$ref` - json_schema.update({**resolved, **json_schema}) - json_schema.pop("$ref") - # Since the schema expanded from `$ref` might not have `additionalProperties: false` applied - # we call `_ensure_strict_json_schema` again to fix the inlined schema and ensure it's valid - return _ensure_strict_json_schema(json_schema, path=path, root=root) - - return json_schema - - -def resolve_ref(*, root: dict[str, object], ref: str) -> object: - if not ref.startswith("#/"): - raise ValueError(f"Unexpected $ref format {ref!r}; Does not start with #/") - - path = ref[2:].split("/") - resolved = root - for key in path: - value = resolved[key] - assert is_dict( - value - ), f"encountered non-dictionary entry while resolving {ref} - {resolved}" - resolved = value - - return resolved - - -def is_dict(obj: object) -> TypeGuard[dict[str, object]]: - # just pretend that we know there are only `str` keys - # as that check is not worth the performance cost - return isinstance(obj, dict) - - -def is_list(obj: object) -> TypeGuard[list[object]]: - return isinstance(obj, list) - - -def has_more_than_n_keys(obj: dict[str, object], n: int) -> bool: - i = 0 - for _ in obj.keys(): - i += 1 - if i > n: - return True - return False diff --git a/pkg/hanzo-agent/src/agents/tool.py b/pkg/hanzo-agent/src/agents/tool.py deleted file mode 100644 index 8727b2755..000000000 --- a/pkg/hanzo-agent/src/agents/tool.py +++ /dev/null @@ -1,290 +0,0 @@ -from __future__ import annotations - -import inspect -import json -from collections.abc import Awaitable -from dataclasses import dataclass -from typing import Any, Callable, Literal, Union, overload - -from openai.types.responses.file_search_tool_param import Filters, RankingOptions -from openai.types.responses.web_search_tool_param import UserLocation -from pydantic import ValidationError -from typing_extensions import Concatenate, ParamSpec - -from . import _debug, _utils -from ._utils import MaybeAwaitable -from .computer import AsyncComputer, Computer -from .exceptions import ModelBehaviorError -from .function_schema import DocstringStyle, function_schema -from .logger import logger -from .run_context import RunContextWrapper -from .tracing import SpanError - -ToolParams = ParamSpec("ToolParams") - -ToolFunctionWithoutContext = Callable[ToolParams, Any] -ToolFunctionWithContext = Callable[Concatenate[RunContextWrapper[Any], ToolParams], Any] - -ToolFunction = Union[ - ToolFunctionWithoutContext[ToolParams], ToolFunctionWithContext[ToolParams] -] - - -@dataclass -class FunctionTool: - """A tool that wraps a function. In most cases, you should use the `function_tool` helpers to - create a FunctionTool, as they let you easily wrap a Python function. - """ - - name: str - """The name of the tool, as shown to the LLM. Generally the name of the function.""" - - description: str - """A description of the tool, as shown to the LLM.""" - - params_json_schema: dict[str, Any] - """The JSON schema for the tool's parameters.""" - - on_invoke_tool: Callable[[RunContextWrapper[Any], str], Awaitable[str]] - """A function that invokes the tool with the given context and parameters. The params passed - are: - 1. The tool run context. - 2. The arguments from the LLM, as a JSON string. - - You must return a string representation of the tool output. In case of errors, you can either - raise an Exception (which will cause the run to fail) or return a string error message (which - will be sent back to the LLM). - """ - - strict_json_schema: bool = True - """Whether the JSON schema is in strict mode. We **strongly** recommend setting this to True, - as it increases the likelihood of correct JSON input.""" - - -@dataclass -class FileSearchTool: - """A hosted tool that lets the LLM search through a vector store. Currently only supported with - OpenAI models, using the Responses API. - """ - - vector_store_ids: list[str] - """The IDs of the vector stores to search.""" - - max_num_results: int | None = None - """The maximum number of results to return.""" - - include_search_results: bool = False - """Whether to include the search results in the output produced by the LLM.""" - - ranking_options: RankingOptions | None = None - """Ranking options for search.""" - - filters: Filters | None = None - """A filter to apply based on file attributes.""" - - @property - def name(self): - return "file_search" - - -@dataclass -class WebSearchTool: - """A hosted tool that lets the LLM search the web. Currently only supported with OpenAI models, - using the Responses API. - """ - - user_location: UserLocation | None = None - """Optional location for the search. Lets you customize results to be relevant to a location.""" - - search_context_size: Literal["low", "medium", "high"] = "medium" - """The amount of context to use for the search.""" - - @property - def name(self): - return "web_search_preview" - - -@dataclass -class ComputerTool: - """A hosted tool that lets the LLM control a computer.""" - - computer: Computer | AsyncComputer - """The computer implementation, which describes the environment and dimensions of the computer, - as well as implements the computer actions like click, screenshot, etc. - """ - - @property - def name(self): - return "computer_use_preview" - - -Tool = Union[FunctionTool, FileSearchTool, WebSearchTool, ComputerTool] -"""A tool that can be used in an agent.""" - - -def default_tool_error_function(ctx: RunContextWrapper[Any], error: Exception) -> str: - """The default tool error function, which just returns a generic error message.""" - return f"An error occurred while running the tool. Please try again. Error: {str(error)}" - - -ToolErrorFunction = Callable[[RunContextWrapper[Any], Exception], MaybeAwaitable[str]] - - -@overload -def function_tool( - func: ToolFunction[...], - *, - name_override: str | None = None, - description_override: str | None = None, - docstring_style: DocstringStyle | None = None, - use_docstring_info: bool = True, - failure_error_function: ToolErrorFunction | None = None, -) -> FunctionTool: - """Overload for usage as @function_tool (no parentheses).""" - ... - - -@overload -def function_tool( - *, - name_override: str | None = None, - description_override: str | None = None, - docstring_style: DocstringStyle | None = None, - use_docstring_info: bool = True, - failure_error_function: ToolErrorFunction | None = None, -) -> Callable[[ToolFunction[...]], FunctionTool]: - """Overload for usage as @function_tool(...).""" - ... - - -def function_tool( - func: ToolFunction[...] | None = None, - *, - name_override: str | None = None, - description_override: str | None = None, - docstring_style: DocstringStyle | None = None, - use_docstring_info: bool = True, - failure_error_function: ToolErrorFunction | None = default_tool_error_function, -) -> FunctionTool | Callable[[ToolFunction[...]], FunctionTool]: - """ - Decorator to create a FunctionTool from a function. By default, we will: - 1. Parse the function signature to create a JSON schema for the tool's parameters. - 2. Use the function's docstring to populate the tool's description. - 3. Use the function's docstring to populate argument descriptions. - The docstring style is detected automatically, but you can override it. - - If the function takes a `RunContextWrapper` as the first argument, it *must* match the - context type of the agent that uses the tool. - - Args: - func: The function to wrap. - name_override: If provided, use this name for the tool instead of the function's name. - description_override: If provided, use this description for the tool instead of the - function's docstring. - docstring_style: If provided, use this style for the tool's docstring. If not provided, - we will attempt to auto-detect the style. - use_docstring_info: If True, use the function's docstring to populate the tool's - description and argument descriptions. - failure_error_function: If provided, use this function to generate an error message when - the tool call fails. The error message is sent to the LLM. If you pass None, then no - error message will be sent and instead an Exception will be raised. - """ - - def _create_function_tool(the_func: ToolFunction[...]) -> FunctionTool: - schema = function_schema( - func=the_func, - name_override=name_override, - description_override=description_override, - docstring_style=docstring_style, - use_docstring_info=use_docstring_info, - ) - - async def _on_invoke_tool_impl(ctx: RunContextWrapper[Any], input: str) -> str: - try: - json_data: dict[str, Any] = json.loads(input) if input else {} - except Exception as e: - if _debug.DONT_LOG_TOOL_DATA: - logger.debug(f"Invalid JSON input for tool {schema.name}") - else: - logger.debug(f"Invalid JSON input for tool {schema.name}: {input}") - raise ModelBehaviorError( - f"Invalid JSON input for tool {schema.name}: {input}" - ) from e - - if _debug.DONT_LOG_TOOL_DATA: - logger.debug(f"Invoking tool {schema.name}") - else: - logger.debug(f"Invoking tool {schema.name} with input {input}") - - try: - parsed = ( - schema.params_pydantic_model(**json_data) - if json_data - else schema.params_pydantic_model() - ) - except ValidationError as e: - raise ModelBehaviorError( - f"Invalid JSON input for tool {schema.name}: {e}" - ) from e - - args, kwargs_dict = schema.to_call_args(parsed) - - if not _debug.DONT_LOG_TOOL_DATA: - logger.debug(f"Tool call args: {args}, kwargs: {kwargs_dict}") - - if inspect.iscoroutinefunction(the_func): - if schema.takes_context: - result = await the_func(ctx, *args, **kwargs_dict) - else: - result = await the_func(*args, **kwargs_dict) - else: - if schema.takes_context: - result = the_func(ctx, *args, **kwargs_dict) - else: - result = the_func(*args, **kwargs_dict) - - if _debug.DONT_LOG_TOOL_DATA: - logger.debug(f"Tool {schema.name} completed.") - else: - logger.debug(f"Tool {schema.name} returned {result}") - - return str(result) - - async def _on_invoke_tool(ctx: RunContextWrapper[Any], input: str) -> str: - try: - return await _on_invoke_tool_impl(ctx, input) - except Exception as e: - if failure_error_function is None: - raise - - result = failure_error_function(ctx, e) - if inspect.isawaitable(result): - return await result - - _utils.attach_error_to_current_span( - SpanError( - message="Error running tool (non-fatal)", - data={ - "tool_name": schema.name, - "error": str(e), - }, - ) - ) - return result - - return FunctionTool( - name=schema.name, - description=schema.description or "", - params_json_schema=schema.params_json_schema, - on_invoke_tool=_on_invoke_tool, - ) - - # If func is actually a callable, we were used as @function_tool with no parentheses - if callable(func): - return _create_function_tool(func) - - # Otherwise, we were used as @function_tool(...), so return a decorator - def decorator(real_func: ToolFunction[...]) -> FunctionTool: - return _create_function_tool(real_func) - - return decorator diff --git a/pkg/hanzo-agent/src/agents/tool_enhanced.py b/pkg/hanzo-agent/src/agents/tool_enhanced.py deleted file mode 100644 index 6a379642d..000000000 --- a/pkg/hanzo-agent/src/agents/tool_enhanced.py +++ /dev/null @@ -1,392 +0,0 @@ -"""Enhanced tool system with MCP (Model Context Protocol) support.""" - -from __future__ import annotations - -import asyncio -import json -from dataclasses import dataclass, field -from typing import Any, Callable, Dict, List, Optional, Protocol - -from .exceptions import AgentsException -from .logger import logger -from .run_context import RunContextWrapper -from .tool import FunctionTool, Tool, function_tool - - -class MCPServer(Protocol): - """Protocol for MCP server implementations.""" - - async def list_tools(self) -> List[Dict[str, Any]]: - """List available tools from the MCP server.""" - ... - - async def call_tool(self, name: str, arguments: Dict[str, Any]) -> Any: - """Call a tool on the MCP server.""" - ... - - -@dataclass -class MCPTool(Tool): - """A tool that delegates to an MCP server.""" - - name: str - """Tool name as exposed by MCP server.""" - - description: str - """Tool description.""" - - parameters_schema: Dict[str, Any] - """JSON schema for parameters.""" - - server: MCPServer - """The MCP server instance.""" - - async def execute(self, arguments: Dict[str, Any]) -> Any: - """Execute the tool via MCP server.""" - return await self.server.call_tool(self.name, arguments) - - -class MCPToolAdapter: - """Adapts MCP tools to work with the agent framework.""" - - def __init__(self, server: MCPServer): - """Initialize adapter with MCP server. - - Args: - server: MCP server instance - """ - self.server = server - self._tools_cache: List[Tool] | None = None - - async def get_tools(self) -> List[Tool]: - """Get all tools from the MCP server as agent tools.""" - if self._tools_cache is not None: - return self._tools_cache - - mcp_tools = await self.server.list_tools() - tools = [] - - for tool_def in mcp_tools: - # Create FunctionTool wrapper - tool = self._create_tool_wrapper(tool_def) - tools.append(tool) - - self._tools_cache = tools - return tools - - def _create_tool_wrapper(self, tool_def: Dict[str, Any]) -> FunctionTool: - """Create a FunctionTool wrapper for an MCP tool.""" - name = tool_def["name"] - description = tool_def.get("description", "") - parameters = tool_def.get("inputSchema", {}) - - async def tool_function(ctx: RunContextWrapper[Any], **kwargs) -> str: - # Call MCP server - result = await self.server.call_tool(name, kwargs) - - # Convert result to string - if isinstance(result, str): - return result - return json.dumps(result) - - # Create function tool - return FunctionTool( - name=name, - description=description, - params_json_schema=parameters, - on_invoke_tool=lambda ctx, args: tool_function(ctx, **json.loads(args)), - ) - - async def refresh_tools(self) -> None: - """Refresh the tools cache.""" - self._tools_cache = None - - -class CompositeTool(Tool): - """A tool that combines multiple sub-tools.""" - - def __init__( - self, - name: str, - description: str, - tools: List[Tool], - orchestrator: Callable[[str, List[Tool]], Tool] | None = None, - ): - """Initialize composite tool. - - Args: - name: Tool name - description: Tool description - tools: List of sub-tools - orchestrator: Optional function to select which tool to use - """ - self.name = name - self.description = description - self.tools = tools - self.orchestrator = orchestrator or self._default_orchestrator - - def _default_orchestrator(self, input: str, tools: List[Tool]) -> Tool: - """Default orchestrator - just uses the first tool.""" - if not tools: - raise AgentsException("No tools available") - return tools[0] - - async def execute(self, ctx: RunContextWrapper[Any], input: str) -> str: - """Execute the composite tool.""" - # Select tool to use - selected_tool = self.orchestrator(input, self.tools) - - # Execute selected tool - if isinstance(selected_tool, FunctionTool): - return await selected_tool.on_invoke_tool(ctx, input) - else: - raise AgentsException(f"Cannot execute tool type: {type(selected_tool)}") - - -class ToolRegistry: - """Registry for managing tools across the system.""" - - def __init__(self): - self._tools: Dict[str, Tool] = {} - self._categories: Dict[str, List[str]] = {} - self._mcp_adapters: List[MCPToolAdapter] = [] - - def register(self, tool: Tool, category: str = "general") -> None: - """Register a tool. - - Args: - tool: Tool to register - category: Tool category - """ - if tool.name in self._tools: - raise AgentsException(f"Tool '{tool.name}' already registered") - - self._tools[tool.name] = tool - - if category not in self._categories: - self._categories[category] = [] - self._categories[category].append(tool.name) - - logger.debug(f"Registered tool '{tool.name}' in category '{category}'") - - def unregister(self, name: str) -> None: - """Unregister a tool.""" - if name not in self._tools: - return - - del self._tools[name] - - # Remove from categories - for category, tools in self._categories.items(): - if name in tools: - tools.remove(name) - - def get(self, name: str) -> Tool | None: - """Get a tool by name.""" - return self._tools.get(name) - - def list(self, category: str | None = None) -> List[Tool]: - """List tools, optionally filtered by category.""" - if category: - tool_names = self._categories.get(category, []) - return [self._tools[name] for name in tool_names if name in self._tools] - return list(self._tools.values()) - - def categories(self) -> List[str]: - """List all categories.""" - return list(self._categories.keys()) - - async def add_mcp_server(self, server: MCPServer) -> None: - """Add an MCP server to the registry. - - Args: - server: MCP server to add - """ - adapter = MCPToolAdapter(server) - self._mcp_adapters.append(adapter) - - # Load tools from server - tools = await adapter.get_tools() - for tool in tools: - self.register(tool, category="mcp") - - async def refresh_mcp_tools(self) -> None: - """Refresh tools from all MCP servers.""" - # Remove existing MCP tools - mcp_tools = self._categories.get("mcp", []) - for tool_name in mcp_tools: - self.unregister(tool_name) - - # Reload from all adapters - for adapter in self._mcp_adapters: - await adapter.refresh_tools() - tools = await adapter.get_tools() - for tool in tools: - self.register(tool, category="mcp") - - -# Global tool registry -_global_registry = ToolRegistry() - - -def get_tool_registry() -> ToolRegistry: - """Get the global tool registry.""" - return _global_registry - - -def register_tool(tool: Tool, category: str = "general") -> None: - """Register a tool in the global registry.""" - _global_registry.register(tool, category) - - -def tool_from_function( - func: Callable, - name: str | None = None, - description: str | None = None, - category: str = "general", -) -> FunctionTool: - """Create and register a tool from a function. - - Args: - func: Function to wrap - name: Tool name (defaults to function name) - description: Tool description - category: Tool category - - Returns: - The created tool - """ - tool = function_tool( - func=func, - name_override=name, - description_override=description, - ) - - register_tool(tool, category) - return tool - - -class ToolChain: - """Chain multiple tools together.""" - - def __init__(self, tools: List[Tool]): - """Initialize tool chain. - - Args: - tools: Tools to chain in order - """ - self.tools = tools - - async def execute( - self, - ctx: RunContextWrapper[Any], - input: Any, - ) -> Any: - """Execute the tool chain. - - Args: - ctx: Execution context - input: Initial input - - Returns: - Final output after all tools - """ - current_input = input - - for tool in self.tools: - if isinstance(tool, FunctionTool): - # Convert input to JSON string for function tools - json_input = ( - json.dumps(current_input) - if not isinstance(current_input, str) - else current_input - ) - output = await tool.on_invoke_tool(ctx, json_input) - - # Try to parse output as JSON - try: - current_input = json.loads(output) - except json.JSONDecodeError: - current_input = output - else: - # For other tool types, pass through - current_input = str(current_input) - - return current_input - - def __add__(self, other: Tool | ToolChain) -> ToolChain: - """Add another tool or chain.""" - if isinstance(other, ToolChain): - return ToolChain(self.tools + other.tools) - else: - return ToolChain(self.tools + [other]) - - -@dataclass -class ToolMetrics: - """Metrics for tool execution.""" - - total_calls: int = 0 - successful_calls: int = 0 - failed_calls: int = 0 - total_duration: float = 0.0 - last_error: str | None = None - last_call_time: float | None = None - - @property - def success_rate(self) -> float: - """Calculate success rate.""" - if self.total_calls == 0: - return 0.0 - return self.successful_calls / self.total_calls - - @property - def average_duration(self) -> float: - """Calculate average duration.""" - if self.successful_calls == 0: - return 0.0 - return self.total_duration / self.successful_calls - - -class MonitoredTool(Tool): - """Wrapper that adds monitoring to any tool.""" - - def __init__(self, tool: Tool): - """Initialize monitored tool. - - Args: - tool: Tool to monitor - """ - self.tool = tool - self.metrics = ToolMetrics() - - @property - def name(self) -> str: - """Get tool name.""" - return self.tool.name - - async def execute(self, ctx: RunContextWrapper[Any], input: str) -> str: - """Execute with monitoring.""" - import time - - start_time = time.time() - self.metrics.total_calls += 1 - self.metrics.last_call_time = start_time - - try: - if isinstance(self.tool, FunctionTool): - result = await self.tool.on_invoke_tool(ctx, input) - else: - raise AgentsException(f"Cannot monitor tool type: {type(self.tool)}") - - # Success - self.metrics.successful_calls += 1 - self.metrics.total_duration += time.time() - start_time - - return result - - except Exception as e: - # Failure - self.metrics.failed_calls += 1 - self.metrics.last_error = str(e) - raise diff --git a/pkg/hanzo-agent/src/agents/tracing/__init__.py b/pkg/hanzo-agent/src/agents/tracing/__init__.py deleted file mode 100644 index 8e802018f..000000000 --- a/pkg/hanzo-agent/src/agents/tracing/__init__.py +++ /dev/null @@ -1,97 +0,0 @@ -import atexit - -from .create import ( - agent_span, - custom_span, - function_span, - generation_span, - get_current_span, - get_current_trace, - guardrail_span, - handoff_span, - response_span, - trace, -) -from .processor_interface import TracingProcessor -from .processors import default_exporter, default_processor -from .setup import GLOBAL_TRACE_PROVIDER -from .span_data import ( - AgentSpanData, - CustomSpanData, - FunctionSpanData, - GenerationSpanData, - GuardrailSpanData, - HandoffSpanData, - ResponseSpanData, - SpanData, -) -from .spans import Span, SpanError -from .traces import Trace -from .util import gen_span_id, gen_trace_id - -__all__ = [ - "add_trace_processor", - "agent_span", - "custom_span", - "function_span", - "generation_span", - "get_current_span", - "get_current_trace", - "guardrail_span", - "handoff_span", - "response_span", - "set_trace_processors", - "set_tracing_disabled", - "trace", - "Trace", - "SpanError", - "Span", - "SpanData", - "AgentSpanData", - "CustomSpanData", - "FunctionSpanData", - "GenerationSpanData", - "GuardrailSpanData", - "HandoffSpanData", - "ResponseSpanData", - "TracingProcessor", - "gen_trace_id", - "gen_span_id", -] - - -def add_trace_processor(span_processor: TracingProcessor) -> None: - """ - Adds a new trace processor. This processor will receive all traces/spans. - """ - GLOBAL_TRACE_PROVIDER.register_processor(span_processor) - - -def set_trace_processors(processors: list[TracingProcessor]) -> None: - """ - Set the list of trace processors. This will replace the current list of processors. - """ - GLOBAL_TRACE_PROVIDER.set_processors(processors) - - -def set_tracing_disabled(disabled: bool) -> None: - """ - Set whether tracing is globally disabled. - """ - GLOBAL_TRACE_PROVIDER.set_disabled(disabled) - - -def set_tracing_export_api_key(api_key: str) -> None: - """ - Set the OpenAI API key for the backend exporter. - """ - default_exporter().set_api_key(api_key) - - -# Add the default processor, which exports traces and spans to the backend in batches. You can -# change the default behavior by either: -# 1. calling add_trace_processor(), which adds additional processors, or -# 2. calling set_trace_processors(), which replaces the default processor. -add_trace_processor(default_processor()) - -atexit.register(GLOBAL_TRACE_PROVIDER.shutdown) diff --git a/pkg/hanzo-agent/src/agents/tracing/create.py b/pkg/hanzo-agent/src/agents/tracing/create.py deleted file mode 100644 index 0a74eab8b..000000000 --- a/pkg/hanzo-agent/src/agents/tracing/create.py +++ /dev/null @@ -1,312 +0,0 @@ -from __future__ import annotations - -from collections.abc import Mapping, Sequence -from typing import TYPE_CHECKING, Any - -from ..logger import logger -from .setup import GLOBAL_TRACE_PROVIDER -from .span_data import ( - AgentSpanData, - CustomSpanData, - FunctionSpanData, - GenerationSpanData, - GuardrailSpanData, - HandoffSpanData, - ResponseSpanData, -) -from .spans import Span -from .traces import Trace - -if TYPE_CHECKING: - from openai.types.responses import Response - - -def trace( - workflow_name: str, - trace_id: str | None = None, - group_id: str | None = None, - metadata: dict[str, Any] | None = None, - disabled: bool = False, -) -> Trace: - """ - Create a new trace. The trace will not be started automatically; you should either use - it as a context manager (`with trace(...):`) or call `trace.start()` + `trace.finish()` - manually. - - In addition to the workflow name and optional grouping identifier, you can provide - an arbitrary metadata dictionary to attach additional user-defined information to - the trace. - - Args: - workflow_name: The name of the logical app or workflow. For example, you might provide - "code_bot" for a coding agent, or "customer_support_agent" for a customer support agent. - trace_id: The ID of the trace. Optional. If not provided, we will generate an ID. We - recommend using `util.gen_trace_id()` to generate a trace ID, to guarantee that IDs are - correctly formatted. - group_id: Optional grouping identifier to link multiple traces from the same conversation - or process. For instance, you might use a chat thread ID. - metadata: Optional dictionary of additional metadata to attach to the trace. - disabled: If True, we will return a Trace but the Trace will not be recorded. This will - not be checked if there's an existing trace and `even_if_trace_running` is True. - - Returns: - The newly created trace object. - """ - current_trace = GLOBAL_TRACE_PROVIDER.get_current_trace() - if current_trace: - logger.warning( - "Trace already exists. Creating a new trace, but this is probably a mistake." - ) - - return GLOBAL_TRACE_PROVIDER.create_trace( - name=workflow_name, - trace_id=trace_id, - group_id=group_id, - metadata=metadata, - disabled=disabled, - ) - - -def get_current_trace() -> Trace | None: - """Returns the currently active trace, if present.""" - return GLOBAL_TRACE_PROVIDER.get_current_trace() - - -def get_current_span() -> Span[Any] | None: - """Returns the currently active span, if present.""" - return GLOBAL_TRACE_PROVIDER.get_current_span() - - -def agent_span( - name: str, - handoffs: list[str] | None = None, - tools: list[str] | None = None, - output_type: str | None = None, - span_id: str | None = None, - parent: Trace | Span[Any] | None = None, - disabled: bool = False, -) -> Span[AgentSpanData]: - """Create a new agent span. The span will not be started automatically, you should either do - `with agent_span() ...` or call `span.start()` + `span.finish()` manually. - - Args: - name: The name of the agent. - handoffs: Optional list of agent names to which this agent could hand off control. - tools: Optional list of tool names available to this agent. - output_type: Optional name of the output type produced by the agent. - span_id: The ID of the span. Optional. If not provided, we will generate an ID. We - recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are - correctly formatted. - parent: The parent span or trace. If not provided, we will automatically use the current - trace/span as the parent. - disabled: If True, we will return a Span but the Span will not be recorded. - - Returns: - The newly created agent span. - """ - return GLOBAL_TRACE_PROVIDER.create_span( - span_data=AgentSpanData( - name=name, handoffs=handoffs, tools=tools, output_type=output_type - ), - span_id=span_id, - parent=parent, - disabled=disabled, - ) - - -def function_span( - name: str, - input: str | None = None, - output: str | None = None, - span_id: str | None = None, - parent: Trace | Span[Any] | None = None, - disabled: bool = False, -) -> Span[FunctionSpanData]: - """Create a new function span. The span will not be started automatically, you should either do - `with function_span() ...` or call `span.start()` + `span.finish()` manually. - - Args: - name: The name of the function. - input: The input to the function. - output: The output of the function. - span_id: The ID of the span. Optional. If not provided, we will generate an ID. We - recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are - correctly formatted. - parent: The parent span or trace. If not provided, we will automatically use the current - trace/span as the parent. - disabled: If True, we will return a Span but the Span will not be recorded. - - Returns: - The newly created function span. - """ - return GLOBAL_TRACE_PROVIDER.create_span( - span_data=FunctionSpanData(name=name, input=input, output=output), - span_id=span_id, - parent=parent, - disabled=disabled, - ) - - -def generation_span( - input: Sequence[Mapping[str, Any]] | None = None, - output: Sequence[Mapping[str, Any]] | None = None, - model: str | None = None, - model_config: Mapping[str, Any] | None = None, - usage: dict[str, Any] | None = None, - span_id: str | None = None, - parent: Trace | Span[Any] | None = None, - disabled: bool = False, -) -> Span[GenerationSpanData]: - """Create a new generation span. The span will not be started automatically, you should either - do `with generation_span() ...` or call `span.start()` + `span.finish()` manually. - - This span captures the details of a model generation, including the - input message sequence, any generated outputs, the model name and - configuration, and usage data. If you only need to capture a model - response identifier, use `response_span()` instead. - - Args: - input: The sequence of input messages sent to the model. - output: The sequence of output messages received from the model. - model: The model identifier used for the generation. - model_config: The model configuration (hyperparameters) used. - usage: A dictionary of usage information (input tokens, output tokens, etc.). - span_id: The ID of the span. Optional. If not provided, we will generate an ID. We - recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are - correctly formatted. - parent: The parent span or trace. If not provided, we will automatically use the current - trace/span as the parent. - disabled: If True, we will return a Span but the Span will not be recorded. - - Returns: - The newly created generation span. - """ - return GLOBAL_TRACE_PROVIDER.create_span( - span_data=GenerationSpanData( - input=input, - output=output, - model=model, - model_config=model_config, - usage=usage, - ), - span_id=span_id, - parent=parent, - disabled=disabled, - ) - - -def response_span( - response: Response | None = None, - span_id: str | None = None, - parent: Trace | Span[Any] | None = None, - disabled: bool = False, -) -> Span[ResponseSpanData]: - """Create a new response span. The span will not be started automatically, you should either do - `with response_span() ...` or call `span.start()` + `span.finish()` manually. - - Args: - response: The OpenAI Response object. - span_id: The ID of the span. Optional. If not provided, we will generate an ID. We - recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are - correctly formatted. - parent: The parent span or trace. If not provided, we will automatically use the current - trace/span as the parent. - disabled: If True, we will return a Span but the Span will not be recorded. - """ - return GLOBAL_TRACE_PROVIDER.create_span( - span_data=ResponseSpanData(response=response), - span_id=span_id, - parent=parent, - disabled=disabled, - ) - - -def handoff_span( - from_agent: str | None = None, - to_agent: str | None = None, - span_id: str | None = None, - parent: Trace | Span[Any] | None = None, - disabled: bool = False, -) -> Span[HandoffSpanData]: - """Create a new handoff span. The span will not be started automatically, you should either do - `with handoff_span() ...` or call `span.start()` + `span.finish()` manually. - - Args: - from_agent: The name of the agent that is handing off. - to_agent: The name of the agent that is receiving the handoff. - span_id: The ID of the span. Optional. If not provided, we will generate an ID. We - recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are - correctly formatted. - parent: The parent span or trace. If not provided, we will automatically use the current - trace/span as the parent. - disabled: If True, we will return a Span but the Span will not be recorded. - - Returns: - The newly created handoff span. - """ - return GLOBAL_TRACE_PROVIDER.create_span( - span_data=HandoffSpanData(from_agent=from_agent, to_agent=to_agent), - span_id=span_id, - parent=parent, - disabled=disabled, - ) - - -def custom_span( - name: str, - data: dict[str, Any] | None = None, - span_id: str | None = None, - parent: Trace | Span[Any] | None = None, - disabled: bool = False, -) -> Span[CustomSpanData]: - """Create a new custom span, to which you can add your own metadata. The span will not be - started automatically, you should either do `with custom_span() ...` or call - `span.start()` + `span.finish()` manually. - - Args: - name: The name of the custom span. - data: Arbitrary structured data to associate with the span. - span_id: The ID of the span. Optional. If not provided, we will generate an ID. We - recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are - correctly formatted. - parent: The parent span or trace. If not provided, we will automatically use the current - trace/span as the parent. - disabled: If True, we will return a Span but the Span will not be recorded. - - Returns: - The newly created custom span. - """ - return GLOBAL_TRACE_PROVIDER.create_span( - span_data=CustomSpanData(name=name, data=data or {}), - span_id=span_id, - parent=parent, - disabled=disabled, - ) - - -def guardrail_span( - name: str, - triggered: bool = False, - span_id: str | None = None, - parent: Trace | Span[Any] | None = None, - disabled: bool = False, -) -> Span[GuardrailSpanData]: - """Create a new guardrail span. The span will not be started automatically, you should either - do `with guardrail_span() ...` or call `span.start()` + `span.finish()` manually. - - Args: - name: The name of the guardrail. - triggered: Whether the guardrail was triggered. - span_id: The ID of the span. Optional. If not provided, we will generate an ID. We - recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are - correctly formatted. - parent: The parent span or trace. If not provided, we will automatically use the current - trace/span as the parent. - disabled: If True, we will return a Span but the Span will not be recorded. - """ - return GLOBAL_TRACE_PROVIDER.create_span( - span_data=GuardrailSpanData(name=name, triggered=triggered), - span_id=span_id, - parent=parent, - disabled=disabled, - ) diff --git a/pkg/hanzo-agent/src/agents/tracing/logger.py b/pkg/hanzo-agent/src/agents/tracing/logger.py deleted file mode 100644 index 661d09b57..000000000 --- a/pkg/hanzo-agent/src/agents/tracing/logger.py +++ /dev/null @@ -1,3 +0,0 @@ -import logging - -logger = logging.getLogger("openai.agents.tracing") diff --git a/pkg/hanzo-agent/src/agents/tracing/processor_interface.py b/pkg/hanzo-agent/src/agents/tracing/processor_interface.py deleted file mode 100644 index 4dcd897c7..000000000 --- a/pkg/hanzo-agent/src/agents/tracing/processor_interface.py +++ /dev/null @@ -1,69 +0,0 @@ -import abc -from typing import TYPE_CHECKING, Any - -if TYPE_CHECKING: - from .spans import Span - from .traces import Trace - - -class TracingProcessor(abc.ABC): - """Interface for processing spans.""" - - @abc.abstractmethod - def on_trace_start(self, trace: "Trace") -> None: - """Called when a trace is started. - - Args: - trace: The trace that started. - """ - pass - - @abc.abstractmethod - def on_trace_end(self, trace: "Trace") -> None: - """Called when a trace is finished. - - Args: - trace: The trace that started. - """ - pass - - @abc.abstractmethod - def on_span_start(self, span: "Span[Any]") -> None: - """Called when a span is started. - - Args: - span: The span that started. - """ - pass - - @abc.abstractmethod - def on_span_end(self, span: "Span[Any]") -> None: - """Called when a span is finished. Should not block or raise exceptions. - - Args: - span: The span that finished. - """ - pass - - @abc.abstractmethod - def shutdown(self) -> None: - """Called when the application stops.""" - pass - - @abc.abstractmethod - def force_flush(self) -> None: - """Forces an immediate flush of all queued spans/traces.""" - pass - - -class TracingExporter(abc.ABC): - """Exports traces and spans. For example, could log them or send them to a backend.""" - - @abc.abstractmethod - def export(self, items: list["Trace | Span[Any]"]) -> None: - """Exports a list of traces and spans. - - Args: - items: The items to export. - """ - pass diff --git a/pkg/hanzo-agent/src/agents/tracing/processors.py b/pkg/hanzo-agent/src/agents/tracing/processors.py deleted file mode 100644 index 1d07a89c3..000000000 --- a/pkg/hanzo-agent/src/agents/tracing/processors.py +++ /dev/null @@ -1,267 +0,0 @@ -from __future__ import annotations - -import os -import queue -import random -import threading -import time -from typing import Any - -import httpx - -from ..logger import logger -from .processor_interface import TracingExporter, TracingProcessor -from .spans import Span -from .traces import Trace - - -class ConsoleSpanExporter(TracingExporter): - """Prints the traces and spans to the console.""" - - def export(self, items: list[Trace | Span[Any]]) -> None: - for item in items: - if isinstance(item, Trace): - print(f"[Exporter] Export trace_id={item.trace_id}, name={item.name}, ") - else: - print(f"[Exporter] Export span: {item.export()}") - - -class BackendSpanExporter(TracingExporter): - def __init__( - self, - api_key: str | None = None, - organization: str | None = None, - project: str | None = None, - endpoint: str = "https://api.openai.com/v1/traces/ingest", - max_retries: int = 3, - base_delay: float = 1.0, - max_delay: float = 30.0, - ): - """ - Args: - api_key: The API key for the "Authorization" header. Defaults to - `os.environ["OPENAI_API_KEY"]` if not provided. - organization: The OpenAI organization to use. Defaults to - `os.environ["OPENAI_ORG_ID"]` if not provided. - project: The OpenAI project to use. Defaults to - `os.environ["OPENAI_PROJECT_ID"]` if not provided. - endpoint: The HTTP endpoint to which traces/spans are posted. - max_retries: Maximum number of retries upon failures. - base_delay: Base delay (in seconds) for the first backoff. - max_delay: Maximum delay (in seconds) for backoff growth. - """ - self.api_key = api_key or os.environ.get("OPENAI_API_KEY") - self.organization = organization or os.environ.get("OPENAI_ORG_ID") - self.project = project or os.environ.get("OPENAI_PROJECT_ID") - self.endpoint = endpoint - self.max_retries = max_retries - self.base_delay = base_delay - self.max_delay = max_delay - - # Keep a client open for connection pooling across multiple export calls - self._client = httpx.Client(timeout=httpx.Timeout(timeout=60, connect=5.0)) - - def set_api_key(self, api_key: str): - """Set the OpenAI API key for the exporter. - - Args: - api_key: The OpenAI API key to use. This is the same key used by the OpenAI Python - client. - """ - self.api_key = api_key - - def export(self, items: list[Trace | Span[Any]]) -> None: - if not items: - return - - if not self.api_key: - logger.warning("OPENAI_API_KEY is not set, skipping trace export") - return - - data = [item.export() for item in items if item.export()] - payload = {"data": data} - - headers = { - "Authorization": f"Bearer {self.api_key}", - "Content-Type": "application/json", - "OpenAI-Beta": "traces=v1", - } - - # Exponential backoff loop - attempt = 0 - delay = self.base_delay - while True: - attempt += 1 - try: - response = self._client.post( - url=self.endpoint, headers=headers, json=payload - ) - - # If the response is successful, break out of the loop - if response.status_code < 300: - logger.debug(f"Exported {len(items)} items") - return - - # If the response is a client error (4xx), we wont retry - if 400 <= response.status_code < 500: - logger.error( - f"Tracing client error {response.status_code}: {response.text}" - ) - return - - # For 5xx or other unexpected codes, treat it as transient and retry - logger.warning(f"Server error {response.status_code}, retrying.") - except httpx.RequestError as exc: - # Network or other I/O error, we'll retry - logger.warning(f"Request failed: {exc}") - - # If we reach here, we need to retry or give up - if attempt >= self.max_retries: - logger.error("Max retries reached, giving up on this batch.") - return - - # Exponential backoff + jitter - sleep_time = delay + random.uniform(0, 0.1 * delay) # 10% jitter - time.sleep(sleep_time) - delay = min(delay * 2, self.max_delay) - - def close(self): - """Close the underlying HTTP client.""" - self._client.close() - - -class BatchTraceProcessor(TracingProcessor): - """Some implementation notes: - 1. Using Queue, which is thread-safe. - 2. Using a background thread to export spans, to minimize any performance issues. - 3. Spans are stored in memory until they are exported. - """ - - def __init__( - self, - exporter: TracingExporter, - max_queue_size: int = 8192, - max_batch_size: int = 128, - schedule_delay: float = 5.0, - export_trigger_ratio: float = 0.7, - ): - """ - Args: - exporter: The exporter to use. - max_queue_size: The maximum number of spans to store in the queue. After this, we will - start dropping spans. - max_batch_size: The maximum number of spans to export in a single batch. - schedule_delay: The delay between checks for new spans to export. - export_trigger_ratio: The ratio of the queue size at which we will trigger an export. - """ - self._exporter = exporter - self._queue: queue.Queue[Trace | Span[Any]] = queue.Queue( - maxsize=max_queue_size - ) - self._max_queue_size = max_queue_size - self._max_batch_size = max_batch_size - self._schedule_delay = schedule_delay - self._shutdown_event = threading.Event() - - # The queue size threshold at which we export immediately. - self._export_trigger_size = int(max_queue_size * export_trigger_ratio) - - # Track when we next *must* perform a scheduled export - self._next_export_time = time.time() + self._schedule_delay - - self._shutdown_event = threading.Event() - self._worker_thread = threading.Thread(target=self._run, daemon=True) - self._worker_thread.start() - - def on_trace_start(self, trace: Trace) -> None: - try: - self._queue.put_nowait(trace) - except queue.Full: - logger.warning("Queue is full, dropping trace.") - - def on_trace_end(self, trace: Trace) -> None: - # We send traces via on_trace_start, so we don't need to do anything here. - pass - - def on_span_start(self, span: Span[Any]) -> None: - # We send spans via on_span_end, so we don't need to do anything here. - pass - - def on_span_end(self, span: Span[Any]) -> None: - try: - self._queue.put_nowait(span) - except queue.Full: - logger.warning("Queue is full, dropping span.") - - def shutdown(self, timeout: float | None = None): - """ - Called when the application stops. We signal our thread to stop, then join it. - """ - self._shutdown_event.set() - self._worker_thread.join(timeout=timeout) - - def force_flush(self): - """ - Forces an immediate flush of all queued spans. - """ - self._export_batches(force=True) - - def _run(self): - while not self._shutdown_event.is_set(): - current_time = time.time() - queue_size = self._queue.qsize() - - # If it's time for a scheduled flush or queue is above the trigger threshold - if ( - current_time >= self._next_export_time - or queue_size >= self._export_trigger_size - ): - self._export_batches(force=False) - # Reset the next scheduled flush time - self._next_export_time = time.time() + self._schedule_delay - else: - # Sleep a short interval so we don't busy-wait. - time.sleep(0.2) - - # Final drain after shutdown - self._export_batches(force=True) - - def _export_batches(self, force: bool = False): - """Drains the queue and exports in batches. If force=True, export everything. - Otherwise, export up to `max_batch_size` repeatedly until the queue is empty or below a - certain threshold. - """ - while True: - items_to_export: list[Span[Any] | Trace] = [] - - # Gather a batch of spans up to max_batch_size - while not self._queue.empty() and ( - force or len(items_to_export) < self._max_batch_size - ): - try: - items_to_export.append(self._queue.get_nowait()) - except queue.Empty: - # Another thread might have emptied the queue between checks - break - - # If we collected nothing, we're done - if not items_to_export: - break - - # Export the batch - self._exporter.export(items_to_export) - - -# Create a shared global instance: -_global_exporter = BackendSpanExporter() -_global_processor = BatchTraceProcessor(_global_exporter) - - -def default_exporter() -> BackendSpanExporter: - """The default exporter, which exports traces and spans to the backend in batches.""" - return _global_exporter - - -def default_processor() -> BatchTraceProcessor: - """The default processor, which exports traces and spans to the backend in batches.""" - return _global_processor diff --git a/pkg/hanzo-agent/src/agents/tracing/scope.py b/pkg/hanzo-agent/src/agents/tracing/scope.py deleted file mode 100644 index 22c6d6f43..000000000 --- a/pkg/hanzo-agent/src/agents/tracing/scope.py +++ /dev/null @@ -1,49 +0,0 @@ -# Holds the current active span -import contextvars -from typing import TYPE_CHECKING, Any - -from ..logger import logger - -if TYPE_CHECKING: - from .spans import Span - from .traces import Trace - -_current_span: contextvars.ContextVar["Span[Any] | None"] = contextvars.ContextVar( - "current_span", default=None -) - -_current_trace: contextvars.ContextVar["Trace | None"] = contextvars.ContextVar( - "current_trace", default=None -) - - -class Scope: - @classmethod - def get_current_span(cls) -> "Span[Any] | None": - return _current_span.get() - - @classmethod - def set_current_span( - cls, span: "Span[Any] | None" - ) -> "contextvars.Token[Span[Any] | None]": - return _current_span.set(span) - - @classmethod - def reset_current_span(cls, token: "contextvars.Token[Span[Any] | None]") -> None: - _current_span.reset(token) - - @classmethod - def get_current_trace(cls) -> "Trace | None": - return _current_trace.get() - - @classmethod - def set_current_trace( - cls, trace: "Trace | None" - ) -> "contextvars.Token[Trace | None]": - logger.debug(f"Setting current trace: {trace.trace_id if trace else None}") - return _current_trace.set(trace) - - @classmethod - def reset_current_trace(cls, token: "contextvars.Token[Trace | None]") -> None: - logger.debug("Resetting current trace") - _current_trace.reset(token) diff --git a/pkg/hanzo-agent/src/agents/tracing/setup.py b/pkg/hanzo-agent/src/agents/tracing/setup.py deleted file mode 100644 index 413543c1c..000000000 --- a/pkg/hanzo-agent/src/agents/tracing/setup.py +++ /dev/null @@ -1,215 +0,0 @@ -from __future__ import annotations - -import os -import threading -from typing import Any - -from ..logger import logger -from . import util -from .processor_interface import TracingProcessor -from .scope import Scope -from .spans import NoOpSpan, Span, SpanImpl, TSpanData -from .traces import NoOpTrace, Trace, TraceImpl - - -class SynchronousMultiTracingProcessor(TracingProcessor): - """ - Forwards all calls to a list of TracingProcessors, in order of registration. - """ - - def __init__(self): - # Using a tuple to avoid race conditions when iterating over processors - self._processors: tuple[TracingProcessor, ...] = () - self._lock = threading.Lock() - - def add_tracing_processor(self, tracing_processor: TracingProcessor): - """ - Add a processor to the list of processors. Each processor will receive all traces/spans. - """ - with self._lock: - self._processors += (tracing_processor,) - - def set_processors(self, processors: list[TracingProcessor]): - """ - Set the list of processors. This will replace the current list of processors. - """ - with self._lock: - self._processors = tuple(processors) - - def on_trace_start(self, trace: Trace) -> None: - """ - Called when a trace is started. - """ - for processor in self._processors: - processor.on_trace_start(trace) - - def on_trace_end(self, trace: Trace) -> None: - """ - Called when a trace is finished. - """ - for processor in self._processors: - processor.on_trace_end(trace) - - def on_span_start(self, span: Span[Any]) -> None: - """ - Called when a span is started. - """ - for processor in self._processors: - processor.on_span_start(span) - - def on_span_end(self, span: Span[Any]) -> None: - """ - Called when a span is finished. - """ - for processor in self._processors: - processor.on_span_end(span) - - def shutdown(self) -> None: - """ - Called when the application stops. - """ - for processor in self._processors: - logger.debug(f"Shutting down trace processor {processor}") - processor.shutdown() - - def force_flush(self): - """ - Force the processors to flush their buffers. - """ - for processor in self._processors: - processor.force_flush() - - -class TraceProvider: - def __init__(self): - self._multi_processor = SynchronousMultiTracingProcessor() - self._disabled = os.environ.get( - "OPENAI_AGENTS_DISABLE_TRACING", "false" - ).lower() in ( - "true", - "1", - ) - - def register_processor(self, processor: TracingProcessor): - """ - Add a processor to the list of processors. Each processor will receive all traces/spans. - """ - self._multi_processor.add_tracing_processor(processor) - - def set_processors(self, processors: list[TracingProcessor]): - """ - Set the list of processors. This will replace the current list of processors. - """ - self._multi_processor.set_processors(processors) - - def get_current_trace(self) -> Trace | None: - """ - Returns the currently active trace, if any. - """ - return Scope.get_current_trace() - - def get_current_span(self) -> Span[Any] | None: - """ - Returns the currently active span, if any. - """ - return Scope.get_current_span() - - def set_disabled(self, disabled: bool) -> None: - """ - Set whether tracing is disabled. - """ - self._disabled = disabled - - def create_trace( - self, - name: str, - trace_id: str | None = None, - group_id: str | None = None, - metadata: dict[str, Any] | None = None, - disabled: bool = False, - ) -> Trace: - """ - Create a new trace. - """ - if self._disabled or disabled: - logger.debug(f"Tracing is disabled. Not creating trace {name}") - return NoOpTrace() - - trace_id = trace_id or util.gen_trace_id() - - logger.debug(f"Creating trace {name} with id {trace_id}") - - return TraceImpl( - name=name, - trace_id=trace_id, - group_id=group_id, - metadata=metadata, - processor=self._multi_processor, - ) - - def create_span( - self, - span_data: TSpanData, - span_id: str | None = None, - parent: Trace | Span[Any] | None = None, - disabled: bool = False, - ) -> Span[TSpanData]: - """ - Create a new span. - """ - if self._disabled or disabled: - logger.debug(f"Tracing is disabled. Not creating span {span_data}") - return NoOpSpan(span_data) - - if not parent: - current_span = Scope.get_current_span() - current_trace = Scope.get_current_trace() - if current_trace is None: - logger.error( - "No active trace. Make sure to start a trace with `trace()` first" - "Returning NoOpSpan." - ) - return NoOpSpan(span_data) - elif isinstance(current_trace, NoOpTrace) or isinstance( - current_span, NoOpSpan - ): - logger.debug( - f"Parent {current_span} or {current_trace} is no-op, returning NoOpSpan" - ) - return NoOpSpan(span_data) - - parent_id = current_span.span_id if current_span else None - trace_id = current_trace.trace_id - - elif isinstance(parent, Trace): - if isinstance(parent, NoOpTrace): - logger.debug(f"Parent {parent} is no-op, returning NoOpSpan") - return NoOpSpan(span_data) - trace_id = parent.trace_id - parent_id = None - elif isinstance(parent, Span): - if isinstance(parent, NoOpSpan): - logger.debug(f"Parent {parent} is no-op, returning NoOpSpan") - return NoOpSpan(span_data) - parent_id = parent.span_id - trace_id = parent.trace_id - - logger.debug(f"Creating span {span_data} with id {span_id}") - - return SpanImpl( - trace_id=trace_id, - span_id=span_id, - parent_id=parent_id, - processor=self._multi_processor, - span_data=span_data, - ) - - def shutdown(self) -> None: - try: - logger.debug("Shutting down trace provider") - self._multi_processor.shutdown() - except Exception as e: - logger.error(f"Error shutting down trace provider: {e}") - - -GLOBAL_TRACE_PROVIDER = TraceProvider() diff --git a/pkg/hanzo-agent/src/agents/tracing/span_data.py b/pkg/hanzo-agent/src/agents/tracing/span_data.py deleted file mode 100644 index 5e5d38cbf..000000000 --- a/pkg/hanzo-agent/src/agents/tracing/span_data.py +++ /dev/null @@ -1,188 +0,0 @@ -from __future__ import annotations - -import abc -from collections.abc import Mapping, Sequence -from typing import TYPE_CHECKING, Any - -if TYPE_CHECKING: - from openai.types.responses import Response, ResponseInputItemParam - - -class SpanData(abc.ABC): - @abc.abstractmethod - def export(self) -> dict[str, Any]: - pass - - @property - @abc.abstractmethod - def type(self) -> str: - pass - - -class AgentSpanData(SpanData): - __slots__ = ("name", "handoffs", "tools", "output_type") - - def __init__( - self, - name: str, - handoffs: list[str] | None = None, - tools: list[str] | None = None, - output_type: str | None = None, - ): - self.name = name - self.handoffs: list[str] | None = handoffs - self.tools: list[str] | None = tools - self.output_type: str | None = output_type - - @property - def type(self) -> str: - return "agent" - - def export(self) -> dict[str, Any]: - return { - "type": self.type, - "name": self.name, - "handoffs": self.handoffs, - "tools": self.tools, - "output_type": self.output_type, - } - - -class FunctionSpanData(SpanData): - __slots__ = ("name", "input", "output") - - def __init__(self, name: str, input: str | None, output: str | None): - self.name = name - self.input = input - self.output = output - - @property - def type(self) -> str: - return "function" - - def export(self) -> dict[str, Any]: - return { - "type": self.type, - "name": self.name, - "input": self.input, - "output": self.output, - } - - -class GenerationSpanData(SpanData): - __slots__ = ( - "input", - "output", - "model", - "model_config", - "usage", - ) - - def __init__( - self, - input: Sequence[Mapping[str, Any]] | None = None, - output: Sequence[Mapping[str, Any]] | None = None, - model: str | None = None, - model_config: Mapping[str, Any] | None = None, - usage: dict[str, Any] | None = None, - ): - self.input = input - self.output = output - self.model = model - self.model_config = model_config - self.usage = usage - - @property - def type(self) -> str: - return "generation" - - def export(self) -> dict[str, Any]: - return { - "type": self.type, - "input": self.input, - "output": self.output, - "model": self.model, - "model_config": self.model_config, - "usage": self.usage, - } - - -class ResponseSpanData(SpanData): - __slots__ = ("response", "input") - - def __init__( - self, - response: Response | None = None, - input: str | list[ResponseInputItemParam] | None = None, - ) -> None: - self.response = response - # This is not used by the OpenAI trace processors, but is useful for other tracing - # processor implementations - self.input = input - - @property - def type(self) -> str: - return "response" - - def export(self) -> dict[str, Any]: - return { - "type": self.type, - "response_id": self.response.id if self.response else None, - } - - -class HandoffSpanData(SpanData): - __slots__ = ("from_agent", "to_agent") - - def __init__(self, from_agent: str | None, to_agent: str | None): - self.from_agent = from_agent - self.to_agent = to_agent - - @property - def type(self) -> str: - return "handoff" - - def export(self) -> dict[str, Any]: - return { - "type": self.type, - "from_agent": self.from_agent, - "to_agent": self.to_agent, - } - - -class CustomSpanData(SpanData): - __slots__ = ("name", "data") - - def __init__(self, name: str, data: dict[str, Any]): - self.name = name - self.data = data - - @property - def type(self) -> str: - return "custom" - - def export(self) -> dict[str, Any]: - return { - "type": self.type, - "name": self.name, - "data": self.data, - } - - -class GuardrailSpanData(SpanData): - __slots__ = ("name", "triggered") - - def __init__(self, name: str, triggered: bool = False): - self.name = name - self.triggered = triggered - - @property - def type(self) -> str: - return "guardrail" - - def export(self) -> dict[str, Any]: - return { - "type": self.type, - "name": self.name, - "triggered": self.triggered, - } diff --git a/pkg/hanzo-agent/src/agents/tracing/spans.py b/pkg/hanzo-agent/src/agents/tracing/spans.py deleted file mode 100644 index ee933e730..000000000 --- a/pkg/hanzo-agent/src/agents/tracing/spans.py +++ /dev/null @@ -1,264 +0,0 @@ -from __future__ import annotations - -import abc -import contextvars -from typing import Any, Generic, TypeVar - -from typing_extensions import TypedDict - -from ..logger import logger -from . import util -from .processor_interface import TracingProcessor -from .scope import Scope -from .span_data import SpanData - -TSpanData = TypeVar("TSpanData", bound=SpanData) - - -class SpanError(TypedDict): - message: str - data: dict[str, Any] | None - - -class Span(abc.ABC, Generic[TSpanData]): - @property - @abc.abstractmethod - def trace_id(self) -> str: - pass - - @property - @abc.abstractmethod - def span_id(self) -> str: - pass - - @property - @abc.abstractmethod - def span_data(self) -> TSpanData: - pass - - @abc.abstractmethod - def start(self, mark_as_current: bool = False): - """ - Start the span. - - Args: - mark_as_current: If true, the span will be marked as the current span. - """ - pass - - @abc.abstractmethod - def finish(self, reset_current: bool = False) -> None: - """ - Finish the span. - - Args: - reset_current: If true, the span will be reset as the current span. - """ - pass - - @abc.abstractmethod - def __enter__(self) -> Span[TSpanData]: - pass - - @abc.abstractmethod - def __exit__(self, exc_type, exc_val, exc_tb): - pass - - @property - @abc.abstractmethod - def parent_id(self) -> str | None: - pass - - @abc.abstractmethod - def set_error(self, error: SpanError) -> None: - pass - - @property - @abc.abstractmethod - def error(self) -> SpanError | None: - pass - - @abc.abstractmethod - def export(self) -> dict[str, Any] | None: - pass - - @property - @abc.abstractmethod - def started_at(self) -> str | None: - pass - - @property - @abc.abstractmethod - def ended_at(self) -> str | None: - pass - - -class NoOpSpan(Span[TSpanData]): - __slots__ = ("_span_data", "_prev_span_token") - - def __init__(self, span_data: TSpanData): - self._span_data = span_data - self._prev_span_token: contextvars.Token[Span[TSpanData] | None] | None = None - - @property - def trace_id(self) -> str: - return "no-op" - - @property - def span_id(self) -> str: - return "no-op" - - @property - def span_data(self) -> TSpanData: - return self._span_data - - @property - def parent_id(self) -> str | None: - return None - - def start(self, mark_as_current: bool = False): - if mark_as_current: - self._prev_span_token = Scope.set_current_span(self) - - def finish(self, reset_current: bool = False) -> None: - if reset_current and self._prev_span_token is not None: - Scope.reset_current_span(self._prev_span_token) - self._prev_span_token = None - - def __enter__(self) -> Span[TSpanData]: - self.start(mark_as_current=True) - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - reset_current = True - if exc_type is GeneratorExit: - logger.debug("GeneratorExit, skipping span reset") - reset_current = False - - self.finish(reset_current=reset_current) - - def set_error(self, error: SpanError) -> None: - pass - - @property - def error(self) -> SpanError | None: - return None - - def export(self) -> dict[str, Any] | None: - return None - - @property - def started_at(self) -> str | None: - return None - - @property - def ended_at(self) -> str | None: - return None - - -class SpanImpl(Span[TSpanData]): - __slots__ = ( - "_trace_id", - "_span_id", - "_parent_id", - "_started_at", - "_ended_at", - "_error", - "_prev_span_token", - "_processor", - "_span_data", - ) - - def __init__( - self, - trace_id: str, - span_id: str | None, - parent_id: str | None, - processor: TracingProcessor, - span_data: TSpanData, - ): - self._trace_id = trace_id - self._span_id = span_id or util.gen_span_id() - self._parent_id = parent_id - self._started_at: str | None = None - self._ended_at: str | None = None - self._processor = processor - self._error: SpanError | None = None - self._prev_span_token: contextvars.Token[Span[TSpanData] | None] | None = None - self._span_data = span_data - - @property - def trace_id(self) -> str: - return self._trace_id - - @property - def span_id(self) -> str: - return self._span_id - - @property - def span_data(self) -> TSpanData: - return self._span_data - - @property - def parent_id(self) -> str | None: - return self._parent_id - - def start(self, mark_as_current: bool = False): - if self.started_at is not None: - logger.warning("Span already started") - return - - self._started_at = util.time_iso() - self._processor.on_span_start(self) - if mark_as_current: - self._prev_span_token = Scope.set_current_span(self) - - def finish(self, reset_current: bool = False) -> None: - if self.ended_at is not None: - logger.warning("Span already finished") - return - - self._ended_at = util.time_iso() - self._processor.on_span_end(self) - if reset_current and self._prev_span_token is not None: - Scope.reset_current_span(self._prev_span_token) - self._prev_span_token = None - - def __enter__(self) -> Span[TSpanData]: - self.start(mark_as_current=True) - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - reset_current = True - if exc_type is GeneratorExit: - logger.debug("GeneratorExit, skipping span reset") - reset_current = False - - self.finish(reset_current=reset_current) - - def set_error(self, error: SpanError) -> None: - self._error = error - - @property - def error(self) -> SpanError | None: - return self._error - - @property - def started_at(self) -> str | None: - return self._started_at - - @property - def ended_at(self) -> str | None: - return self._ended_at - - def export(self) -> dict[str, Any] | None: - return { - "object": "trace.span", - "id": self.span_id, - "trace_id": self.trace_id, - "parent_id": self._parent_id, - "started_at": self._started_at, - "ended_at": self._ended_at, - "span_data": self.span_data.export(), - "error": self._error, - } diff --git a/pkg/hanzo-agent/src/agents/tracing/traces.py b/pkg/hanzo-agent/src/agents/tracing/traces.py deleted file mode 100644 index 53d062846..000000000 --- a/pkg/hanzo-agent/src/agents/tracing/traces.py +++ /dev/null @@ -1,195 +0,0 @@ -from __future__ import annotations - -import abc -import contextvars -from typing import Any - -from ..logger import logger -from . import util -from .processor_interface import TracingProcessor -from .scope import Scope - - -class Trace: - """ - A trace is the root level object that tracing creates. It represents a logical "workflow". - """ - - @abc.abstractmethod - def __enter__(self) -> Trace: - pass - - @abc.abstractmethod - def __exit__(self, exc_type, exc_val, exc_tb): - pass - - @abc.abstractmethod - def start(self, mark_as_current: bool = False): - """ - Start the trace. - - Args: - mark_as_current: If true, the trace will be marked as the current trace. - """ - pass - - @abc.abstractmethod - def finish(self, reset_current: bool = False): - """ - Finish the trace. - - Args: - reset_current: If true, the trace will be reset as the current trace. - """ - pass - - @property - @abc.abstractmethod - def trace_id(self) -> str: - """ - The trace ID. - """ - pass - - @property - @abc.abstractmethod - def name(self) -> str: - """ - The name of the workflow being traced. - """ - pass - - @abc.abstractmethod - def export(self) -> dict[str, Any] | None: - """ - Export the trace as a dictionary. - """ - pass - - -class NoOpTrace(Trace): - """ - A no-op trace that will not be recorded. - """ - - def __init__(self): - self._started = False - self._prev_context_token: contextvars.Token[Trace | None] | None = None - - def __enter__(self) -> Trace: - if self._started: - if not self._prev_context_token: - logger.error("Trace already started but no context token set") - return self - - self._started = True - self.start(mark_as_current=True) - - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.finish(reset_current=True) - - def start(self, mark_as_current: bool = False): - if mark_as_current: - self._prev_context_token = Scope.set_current_trace(self) - - def finish(self, reset_current: bool = False): - if reset_current and self._prev_context_token is not None: - Scope.reset_current_trace(self._prev_context_token) - self._prev_context_token = None - - @property - def trace_id(self) -> str: - return "no-op" - - @property - def name(self) -> str: - return "no-op" - - def export(self) -> dict[str, Any] | None: - return None - - -NO_OP_TRACE = NoOpTrace() - - -class TraceImpl(Trace): - """ - A trace that will be recorded by the tracing library. - """ - - __slots__ = ( - "_name", - "_trace_id", - "group_id", - "metadata", - "_prev_context_token", - "_processor", - "_started", - ) - - def __init__( - self, - name: str, - trace_id: str | None, - group_id: str | None, - metadata: dict[str, Any] | None, - processor: TracingProcessor, - ): - self._name = name - self._trace_id = trace_id or util.gen_trace_id() - self.group_id = group_id - self.metadata = metadata - self._prev_context_token: contextvars.Token[Trace | None] | None = None - self._processor = processor - self._started = False - - @property - def trace_id(self) -> str: - return self._trace_id - - @property - def name(self) -> str: - return self._name - - def start(self, mark_as_current: bool = False): - if self._started: - return - - self._started = True - self._processor.on_trace_start(self) - - if mark_as_current: - self._prev_context_token = Scope.set_current_trace(self) - - def finish(self, reset_current: bool = False): - if not self._started: - return - - self._processor.on_trace_end(self) - - if reset_current and self._prev_context_token is not None: - Scope.reset_current_trace(self._prev_context_token) - self._prev_context_token = None - - def __enter__(self) -> Trace: - if self._started: - if not self._prev_context_token: - logger.error("Trace already started but no context token set") - return self - - self.start(mark_as_current=True) - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.finish(reset_current=exc_type is not GeneratorExit) - - def export(self) -> dict[str, Any] | None: - return { - "object": "trace", - "id": self.trace_id, - "workflow_name": self.name, - "group_id": self.group_id, - "metadata": self.metadata, - } diff --git a/pkg/hanzo-agent/src/agents/tracing/util.py b/pkg/hanzo-agent/src/agents/tracing/util.py deleted file mode 100644 index 3e5cad900..000000000 --- a/pkg/hanzo-agent/src/agents/tracing/util.py +++ /dev/null @@ -1,17 +0,0 @@ -import uuid -from datetime import datetime, timezone - - -def time_iso() -> str: - """Returns the current time in ISO 8601 format.""" - return datetime.now(timezone.utc).isoformat() - - -def gen_trace_id() -> str: - """Generates a new trace ID.""" - return f"trace_{uuid.uuid4().hex}" - - -def gen_span_id() -> str: - """Generates a new span ID.""" - return f"span_{uuid.uuid4().hex[:24]}" diff --git a/pkg/hanzo-agent/src/agents/usage.py b/pkg/hanzo-agent/src/agents/usage.py deleted file mode 100644 index 23d989b4b..000000000 --- a/pkg/hanzo-agent/src/agents/usage.py +++ /dev/null @@ -1,22 +0,0 @@ -from dataclasses import dataclass - - -@dataclass -class Usage: - requests: int = 0 - """Total requests made to the LLM API.""" - - input_tokens: int = 0 - """Total input tokens sent, across all requests.""" - - output_tokens: int = 0 - """Total output tokens received, across all requests.""" - - total_tokens: int = 0 - """Total tokens sent and received, across all requests.""" - - def add(self, other: "Usage") -> None: - self.requests += other.requests if other.requests else 0 - self.input_tokens += other.input_tokens if other.input_tokens else 0 - self.output_tokens += other.output_tokens if other.output_tokens else 0 - self.total_tokens += other.total_tokens if other.total_tokens else 0 diff --git a/pkg/hanzo-agent/src/agents/version.py b/pkg/hanzo-agent/src/agents/version.py deleted file mode 100644 index a0b7e9be0..000000000 --- a/pkg/hanzo-agent/src/agents/version.py +++ /dev/null @@ -1,7 +0,0 @@ -import importlib.metadata - -try: - __version__ = importlib.metadata.version("agents") -except importlib.metadata.PackageNotFoundError: - # Fallback if running from source without being installed - __version__ = "0.0.0" diff --git a/pkg/hanzo-agent/test_hanzo_backend.py b/pkg/hanzo-agent/test_hanzo_backend.py deleted file mode 100755 index aac5d74cd..000000000 --- a/pkg/hanzo-agent/test_hanzo_backend.py +++ /dev/null @@ -1,246 +0,0 @@ -#!/usr/bin/env python3 -""" -Test script to verify Hanzo Agent SDK works with local Hanzo Router backend. -""" - -import asyncio -import os -import sys -from typing import Optional - -# Try importing from different paths -try: - from openai import AsyncOpenAI - from agents import Agent, Runner, RunConfig, ModelProvider, Model - from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel -except ImportError: - # Add src to path if running from project root - sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src")) - from openai import AsyncOpenAI - from agents import Agent, Runner, RunConfig, ModelProvider, Model - from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel - -# Hanzo Router configuration -HANZO_ROUTER_URL = os.getenv("HANZO_ROUTER_URL", "http://localhost:4000/v1") -HANZO_API_KEY = os.getenv("HANZO_API_KEY", "sk-1234") # Default test key - -# Colors for output -GREEN = "\033[92m" -RED = "\033[91m" -YELLOW = "\033[93m" -BLUE = "\033[94m" -RESET = "\033[0m" - - -class HanzoModelProvider(ModelProvider): - """Custom model provider that uses Hanzo Router backend.""" - - def __init__(self, base_url: str, api_key: str): - self.client = AsyncOpenAI( - base_url=base_url, - api_key=api_key, - ) - print(f"{BLUE}Configured Hanzo Router at: {base_url}{RESET}") - - def get_model(self, model_name: str | None) -> Model: - # Default to gpt-3.5-turbo if no model specified - model = model_name or "gpt-3.5-turbo" - print(f"{YELLOW}Using model: {model}{RESET}") - return OpenAIChatCompletionsModel(model=model, openai_client=self.client) - - -async def test_basic_agent(): - """Test basic agent functionality.""" - print(f"\n{GREEN}=== Testing Basic Agent ==={RESET}") - - provider = HanzoModelProvider(HANZO_ROUTER_URL, HANZO_API_KEY) - - agent = Agent( - name="TestAssistant", - instructions="You are a helpful assistant. Be concise.", - ) - - try: - result = await Runner.run( - agent, "What is 2 + 2?", run_config=RunConfig(model_provider=provider) - ) - print(f"{GREEN}โœ“ Basic test passed!{RESET}") - print(f"Response: {result.final_output}") - return True - except Exception as e: - print(f"{RED}โœ— Basic test failed: {e}{RESET}") - return False - - -async def test_with_tools(): - """Test agent with tools.""" - print(f"\n{GREEN}=== Testing Agent with Tools ==={RESET}") - - provider = HanzoModelProvider(HANZO_ROUTER_URL, HANZO_API_KEY) - - # Define a simple tool - from agents import function_tool - - @function_tool - def calculate(expression: str) -> str: - """Calculate a mathematical expression.""" - try: - result = eval(expression) - return f"The result is: {result}" - except: - return "Invalid expression" - - agent = Agent( - name="CalculatorAgent", - instructions="You are a calculator assistant. Use the calculate tool for math.", - tools=[calculate], - ) - - try: - result = await Runner.run( - agent, "What is 15 * 23?", run_config=RunConfig(model_provider=provider) - ) - print(f"{GREEN}โœ“ Tool test passed!{RESET}") - print(f"Response: {result.final_output}") - return True - except Exception as e: - print(f"{RED}โœ— Tool test failed: {e}{RESET}") - return False - - -async def test_conversation(): - """Test multi-turn conversation.""" - print(f"\n{GREEN}=== Testing Conversation ==={RESET}") - - provider = HanzoModelProvider(HANZO_ROUTER_URL, HANZO_API_KEY) - - agent = Agent( - name="ConversationAgent", - instructions="You are a helpful assistant. Remember our conversation context.", - ) - - try: - # First message - result1 = await Runner.run( - agent, - "My name is Alice. Remember it.", - run_config=RunConfig(model_provider=provider), - ) - print(f"Response 1: {result1.final_output}") - - # Second message using context - result2 = await Runner.run( - agent, - "What's my name?", - run_config=RunConfig(model_provider=provider), - context=result1.context, - ) - print(f"Response 2: {result2.final_output}") - - if "Alice" in result2.final_output: - print(f"{GREEN}โœ“ Conversation test passed!{RESET}") - return True - else: - print(f"{YELLOW}โš  Conversation test: Context may not be preserved{RESET}") - return True - except Exception as e: - print(f"{RED}โœ— Conversation test failed: {e}{RESET}") - return False - - -async def check_router_health(): - """Check if Hanzo Router is accessible.""" - import aiohttp - - print(f"\n{GREEN}=== Checking Hanzo Router Health ==={RESET}") - print(f"Router URL: {HANZO_ROUTER_URL}") - - try: - async with aiohttp.ClientSession() as session: - # Try health endpoint - health_url = HANZO_ROUTER_URL.replace("/v1", "/health") - async with session.get(health_url) as resp: - if resp.status == 200: - print(f"{GREEN}โœ“ Router health check passed{RESET}") - return True - else: - print( - f"{YELLOW}โš  Router health endpoint returned {resp.status}{RESET}" - ) - - # Try models endpoint - models_url = f"{HANZO_ROUTER_URL}/models" - headers = {"Authorization": f"Bearer {HANZO_API_KEY}"} - async with session.get(models_url, headers=headers) as resp: - if resp.status == 200: - data = await resp.json() - print( - f"{GREEN}โœ“ Available models: {len(data.get('data', []))} found{RESET}" - ) - for model in data.get("data", [])[:5]: - print(f" - {model.get('id')}") - return True - else: - print(f"{YELLOW}โš  Models endpoint returned {resp.status}{RESET}") - - except aiohttp.ClientError as e: - print(f"{RED}โœ— Cannot connect to Router: {e}{RESET}") - print(f"{YELLOW}Make sure Hanzo Router is running at {HANZO_ROUTER_URL}{RESET}") - return False - except Exception as e: - print(f"{RED}โœ— Unexpected error: {e}{RESET}") - return False - - return True - - -async def main(): - """Run all tests.""" - print(f"{BLUE}{'='*60}{RESET}") - print(f"{BLUE}Hanzo Agent SDK Backend Integration Test{RESET}") - print(f"{BLUE}{'='*60}{RESET}") - - # Check router health first - if not await check_router_health(): - print(f"\n{RED}Cannot proceed without Router connection.{RESET}") - print( - f"{YELLOW}Start the Router with: cd /Users/z/work/hanzo/services && make start-router{RESET}" - ) - return - - # Run tests - tests = [ - test_basic_agent, - test_with_tools, - test_conversation, - ] - - results = [] - for test in tests: - result = await test() - results.append(result) - - # Summary - print(f"\n{BLUE}{'='*60}{RESET}") - print(f"{BLUE}Test Summary{RESET}") - print(f"{BLUE}{'='*60}{RESET}") - - passed = sum(1 for r in results if r) - total = len(results) - - if passed == total: - print(f"{GREEN}โœ“ All tests passed! ({passed}/{total}){RESET}") - else: - print(f"{YELLOW}โš  {passed}/{total} tests passed{RESET}") - - print(f"\n{GREEN}The Hanzo Agent SDK is working with the local backend!{RESET}") - - -if __name__ == "__main__": - # Set up the environment - if not os.getenv("OPENAI_API_KEY"): - # Set a dummy key to prevent OpenAI client from complaining - os.environ["OPENAI_API_KEY"] = "sk-dummy" - - # Run tests - asyncio.run(main()) diff --git a/pkg/hanzo-agent/test_hanzo_node.py b/pkg/hanzo-agent/test_hanzo_node.py deleted file mode 100644 index 60566716c..000000000 --- a/pkg/hanzo-agent/test_hanzo_node.py +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env python3 -""" -Test script to verify Hanzo Agent SDK works with local Hanzo Node (hanzod) at port 3690. -""" - -import asyncio -import os -import sys -from typing import Optional - -# Add src to path if running from project root -sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src")) - -from agents import ( - Agent, - Runner, - RunConfig, - HanzoNodeProvider, - create_hanzo_node_provider, - function_tool, -) - -# Colors for output -GREEN = "\033[92m" -RED = "\033[91m" -YELLOW = "\033[93m" -BLUE = "\033[94m" -CYAN = "\033[96m" -RESET = "\033[0m" - - -@function_tool -def get_weather(location: str) -> str: - """Get the weather for a specific location.""" - return f"The weather in {location} is sunny with a temperature of 72ยฐF." - - -@function_tool -def calculate(expression: str) -> str: - """Evaluate a mathematical expression.""" - try: - result = eval(expression, {"__builtins__": {}}, {}) - return f"The result of {expression} is {result}" - except Exception as e: - return f"Error evaluating {expression}: {str(e)}" - - -async def test_hanzo_node_connection(): - """Test connection to Hanzo Node.""" - print(f"\n{CYAN}Testing Hanzo Node Connection...{RESET}") - - # Create provider for Hanzo Node at port 3690 - provider = create_hanzo_node_provider(port=3690) - - # Check health - is_healthy = await provider.health_check() - - if is_healthy: - print( - f"{GREEN}โœ“ Successfully connected to Hanzo Node at {provider.base_url}{RESET}" - ) - return provider - else: - print(f"{RED}โœ— Failed to connect to Hanzo Node at {provider.base_url}{RESET}") - print(f"{YELLOW}Make sure hanzod is running on port 3690{RESET}") - return None - - -async def test_simple_agent(provider: HanzoNodeProvider): - """Test a simple agent with Hanzo Node.""" - print(f"\n{CYAN}Testing Simple Agent...{RESET}") - - # Create a simple agent - agent = Agent( - name="TestAgent", - model=provider.get_model("gpt-oss:20b"), # Use local OSS model - instructions="""You are a helpful assistant that can: - 1. Get weather information - 2. Perform calculations - Always be concise and friendly.""", - tools=[get_weather, calculate], - ) - - # Test queries - test_queries = [ - "What's 2 + 2?", - "What's the weather in San Francisco?", - "Calculate 15 * 3 + 7", - ] - - for query in test_queries: - print(f"\n{YELLOW}Query: {query}{RESET}") - - try: - runner = Runner(agent) - result = await runner.run(query) - - print(f"{GREEN}Response:{RESET}") - for item in result.items: - if hasattr(item, "content"): - for content in item.content: - if hasattr(content, "text"): - print(f" {content.text}") - elif hasattr(content, "name") and hasattr(content, "result"): - print(f" Tool: {content.name}") - print(f" Result: {content.result}") - except Exception as e: - print(f"{RED}Error: {e}{RESET}") - - -async def test_streaming_agent(provider: HanzoNodeProvider): - """Test streaming responses with Hanzo Node.""" - print(f"\n{CYAN}Testing Streaming Agent...{RESET}") - - agent = Agent( - name="StreamingAgent", - model=provider.get_model(), - instructions="You are a creative storyteller. Keep responses brief.", - ) - - runner = Runner(agent) - query = "Tell me a very short story about a robot learning to paint." - - print(f"\n{YELLOW}Query: {query}{RESET}") - print(f"{GREEN}Streaming Response:{RESET}") - - try: - async with runner.run_stream(query) as stream: - async for chunk in stream: - if hasattr(chunk, "content"): - for content in chunk.content: - if hasattr(content, "text"): - print(content.text, end="", flush=True) - print() # New line after streaming - except Exception as e: - print(f"\n{RED}Error during streaming: {e}{RESET}") - - -async def main(): - """Main test function.""" - print(f"{BLUE}{'='*60}{RESET}") - print(f"{BLUE}Hanzo Agent SDK - Hanzo Node Integration Test{RESET}") - print(f"{BLUE}{'='*60}{RESET}") - - # Test connection - provider = await test_hanzo_node_connection() - - if provider: - # Run tests - await test_simple_agent(provider) - await test_streaming_agent(provider) - - print(f"\n{GREEN}{'='*60}{RESET}") - print(f"{GREEN}All tests completed!{RESET}") - print(f"{GREEN}{'='*60}{RESET}") - else: - print(f"\n{RED}Tests aborted: Could not connect to Hanzo Node{RESET}") - print(f"{YELLOW}To start Hanzo Node:{RESET}") - print(f" cd /Users/z/work/hanzo/node") - print(f" cargo run --release --bin hanzod") - sys.exit(1) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-agent/test_quick.py b/pkg/hanzo-agent/test_quick.py deleted file mode 100644 index a1c2123d7..000000000 --- a/pkg/hanzo-agent/test_quick.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python3 -""" -Quick test of Agent SDK -""" - -import os -import sys - -sys.path.insert(0, "src") - -# For synchronous execution -from agents import Agent, Runner - -# Create a simple agent -agent = Agent( - name="MathHelper", - instructions="You are a helpful assistant. Answer concisely.", -) - -# Make sure we have an API key -if not os.getenv("OPENAI_API_KEY") and not os.getenv("ANTHROPIC_API_KEY"): - # Set a dummy key for now - os.environ["OPENAI_API_KEY"] = "sk-dummy" - -print("Testing Agent SDK...") -print("Question: What's 1+2?") -print("-" * 40) - -try: - # Use the synchronous runner - result = Runner.run_sync(agent, "What's 1+2?") - print(f"Answer: {result.final_output}") -except Exception as e: - print(f"Error: {e}") - - # Try with explicit provider - print("\nTrying with Hanzo Router...") - from agents.models.openai_provider import OpenAIProvider - - provider = OpenAIProvider(base_url="http://localhost:4000/v1", api_key="sk-1234") - - try: - from agents import RunConfig - - result = Runner.run_sync( - agent, - "What's 1+2?", - run_config=RunConfig(model_provider=provider, model="gpt-3.5-turbo"), - ) - print(f"Answer: {result.final_output}") - except Exception as e2: - print(f"Router Error: {e2}") diff --git a/pkg/hanzo-agent/test_simple_anthropic.py b/pkg/hanzo-agent/test_simple_anthropic.py deleted file mode 100755 index 1ad65cba7..000000000 --- a/pkg/hanzo-agent/test_simple_anthropic.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env python3 -""" -Simple test of Hanzo Agent SDK with Anthropic -""" - -import asyncio -import os -from openai import AsyncOpenAI - -# Import from the local source -import sys - -sys.path.insert(0, "src") - -from agents import Agent, Runner, RunConfig, ModelProvider, Model -from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel - -# Anthropic configuration through OpenAI-compatible endpoint -ANTHROPIC_BASE_URL = "https://api.anthropic.com/v1" -ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY") - -if not ANTHROPIC_API_KEY: - print("Please set ANTHROPIC_API_KEY environment variable") - sys.exit(1) - - -class AnthropicProvider(ModelProvider): - """Provider for Anthropic models via OpenAI-compatible API.""" - - def __init__(self): - # Note: Anthropic requires special headers, so we'll use the Hanzo Router instead - # which handles Anthropic properly - self.client = AsyncOpenAI( - base_url="http://localhost:4000/v1", # Use Hanzo Router - api_key=os.getenv("HANZO_API_KEY", "sk-1234"), - ) - - def get_model(self, model_name: str | None) -> Model: - # Use Claude through the router - model = model_name or "claude-3-sonnet-20240229" - return OpenAIChatCompletionsModel(model=model, openai_client=self.client) - - -async def test_simple_question(): - """Test a simple math question.""" - - # Create provider - provider = AnthropicProvider() - - # Create a simple agent - agent = Agent( - name="MathAssistant", - instructions="You are a helpful math assistant. Be concise.", - ) - - print("Testing Hanzo Agent SDK with Anthropic...") - print(f"Question: What's 1+2?") - print("-" * 40) - - try: - # Run the agent - result = await Runner.run( - agent, - "What's 1+2?", - run_config=RunConfig( - model_provider=provider, model="claude-3-sonnet-20240229" - ), - ) - - print(f"Answer: {result.final_output}") - print("-" * 40) - print("โœ… Test successful!") - - except Exception as e: - print(f"โŒ Error: {e}") - print("\nTroubleshooting:") - print( - "1. Make sure Hanzo Router is running: cd /Users/z/work/hanzo/services && docker compose -f docker-compose.mothership.yml up router" - ) - print("2. Ensure ANTHROPIC_API_KEY is set in your environment") - - -async def test_direct_anthropic(): - """Test direct Anthropic API call (without Agent SDK).""" - import aiohttp - - print("\nTesting direct Anthropic API...") - - headers = { - "x-api-key": ANTHROPIC_API_KEY, - "anthropic-version": "2023-06-01", - "content-type": "application/json", - } - - data = { - "model": "claude-3-sonnet-20240229", - "messages": [{"role": "user", "content": "What's 1+2?"}], - "max_tokens": 100, - } - - try: - async with aiohttp.ClientSession() as session: - async with session.post( - "https://api.anthropic.com/v1/messages", headers=headers, json=data - ) as resp: - if resp.status == 200: - result = await resp.json() - print(f"Direct API Answer: {result['content'][0]['text']}") - else: - print(f"API Error {resp.status}: {await resp.text()}") - except Exception as e: - print(f"Direct API Error: {e}") - - -if __name__ == "__main__": - # First try direct API - asyncio.run(test_direct_anthropic()) - - # Then try through Agent SDK - print("\n" + "=" * 50 + "\n") - asyncio.run(test_simple_question()) diff --git a/pkg/hanzo-agent/tests/__init__.py b/pkg/hanzo-agent/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-agent/tests/conftest.py b/pkg/hanzo-agent/tests/conftest.py deleted file mode 100644 index 7546e1a68..000000000 --- a/pkg/hanzo-agent/tests/conftest.py +++ /dev/null @@ -1,117 +0,0 @@ -from __future__ import annotations - -import pytest - -from agents.models import _openai_shared -from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel -from agents.models.openai_responses import OpenAIResponsesModel -from agents.tracing import set_trace_processors -from agents.tracing.setup import GLOBAL_TRACE_PROVIDER - -from .testing_processor import SPAN_PROCESSOR_TESTING - -# Top-level compatibility shims applied before tests import modules -try: - import openai.types.responses as _resp_mod - - OrigWebSearch = getattr(_resp_mod, "ResponseFunctionWebSearch", None) - if OrigWebSearch is not None: - - def _compat_web_search(**kwargs): - if "action" not in kwargs: - kwargs["action"] = {"type": "search", "query": ""} - return OrigWebSearch(**kwargs) - - _resp_mod.ResponseFunctionWebSearch = _compat_web_search # type: ignore - - OrigCompleted = getattr(_resp_mod, "ResponseCompletedEvent", None) - if OrigCompleted is not None: - - def _compat_completed(**kwargs): - kwargs.setdefault("sequence_number", 0) - return OrigCompleted(**kwargs) - - _resp_mod.ResponseCompletedEvent = _compat_completed # type: ignore -except Exception: - pass - - -# This fixture will run once before any tests are executed -@pytest.fixture(scope="session", autouse=True) -def setup_span_processor(): - set_trace_processors([SPAN_PROCESSOR_TESTING]) - - -# This fixture will run before each test -@pytest.fixture(autouse=True) -def clear_span_processor(): - SPAN_PROCESSOR_TESTING.force_flush() - SPAN_PROCESSOR_TESTING.shutdown() - SPAN_PROCESSOR_TESTING.clear() - - -# This fixture will run before each test -@pytest.fixture(autouse=True) -def clear_openai_settings(): - _openai_shared._default_openai_key = None - _openai_shared._default_openai_client = None - _openai_shared._use_responses_by_default = True - - -# This fixture will run after all tests end -@pytest.fixture(autouse=True, scope="session") -def shutdown_trace_provider(): - yield - GLOBAL_TRACE_PROVIDER.shutdown() - - -@pytest.fixture(autouse=True) -def disable_real_model_clients(monkeypatch, request): - # If the test is marked to allow the method call, don't override it. - if request.node.get_closest_marker("allow_call_model_methods"): - # Provide a harmless default key so providers can instantiate without raising - _openai_shared._default_openai_key = ( - _openai_shared._default_openai_key or "sk-dummy" - ) - return - - def failing_version(*args, **kwargs): - pytest.fail("Real models should not be used in tests!") - - monkeypatch.setattr(OpenAIResponsesModel, "get_response", failing_version) - monkeypatch.setattr(OpenAIResponsesModel, "stream_response", failing_version) - monkeypatch.setattr(OpenAIChatCompletionsModel, "get_response", failing_version) - monkeypatch.setattr(OpenAIChatCompletionsModel, "stream_response", failing_version) - - # Compatibility shim for older test constructions of ResponseFunctionWebSearch - try: - import openai.types.responses as _resp_mod - - OrigWebSearch = getattr(_resp_mod, "ResponseFunctionWebSearch", None) - - if OrigWebSearch is not None: - - def _compat_web_search(**kwargs): - if "action" not in kwargs: - kwargs["action"] = {"type": "search", "query": ""} - return OrigWebSearch(**kwargs) - - # Replace the constructor in module so subsequent imports use the shim - monkeypatch.setattr( - _resp_mod, "ResponseFunctionWebSearch", _compat_web_search - ) - except Exception: - pass - - -def pytest_runtest_setup(item): - # Toggle dummy key allowance depending on the test file - import os - - if str(item.fspath).endswith("test_config.py"): - if item.name == "test_set_default_openai_api": - os.environ["ALLOW_DUMMY_OPENAI_KEY"] = "1" - else: - os.environ.pop("ALLOW_DUMMY_OPENAI_KEY", None) - else: - os.environ["ALLOW_DUMMY_OPENAI_KEY"] = "1" diff --git a/pkg/hanzo-agent/tests/fake_model.py b/pkg/hanzo-agent/tests/fake_model.py deleted file mode 100644 index f2fa500c0..000000000 --- a/pkg/hanzo-agent/tests/fake_model.py +++ /dev/null @@ -1,123 +0,0 @@ -from __future__ import annotations - -from collections.abc import AsyncIterator - -from openai.types.responses import Response, ResponseCompletedEvent - -from agents.agent_output import AgentOutputSchema -from agents.handoffs import Handoff -from agents.items import ( - ModelResponse, - TResponseInputItem, - TResponseOutputItem, - TResponseStreamEvent, -) -from agents.model_settings import ModelSettings -from agents.models.interface import Model, ModelTracing -from agents.tool import Tool -from agents.tracing import SpanError, generation_span -from agents.usage import Usage - - -class FakeModel(Model): - def __init__( - self, - tracing_enabled: bool = False, - initial_output: list[TResponseOutputItem] | Exception | None = None, - ): - if initial_output is None: - initial_output = [] - self.turn_outputs: list[list[TResponseOutputItem] | Exception] = ( - [initial_output] if initial_output else [] - ) - self.tracing_enabled = tracing_enabled - - def set_next_output(self, output: list[TResponseOutputItem] | Exception): - self.turn_outputs.append(output) - - def add_multiple_turn_outputs( - self, outputs: list[list[TResponseOutputItem] | Exception] - ): - self.turn_outputs.extend(outputs) - - def get_next_output(self) -> list[TResponseOutputItem] | Exception: - if not self.turn_outputs: - return [] - return self.turn_outputs.pop(0) - - async def get_response( - self, - system_instructions: str | None, - input: str | list[TResponseInputItem], - model_settings: ModelSettings, - tools: list[Tool], - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - tracing: ModelTracing, - ) -> ModelResponse: - with generation_span(disabled=not self.tracing_enabled) as span: - output = self.get_next_output() - - if isinstance(output, Exception): - span.set_error( - SpanError( - message="Error", - data={ - "name": output.__class__.__name__, - "message": str(output), - }, - ) - ) - raise output - - return ModelResponse( - output=output, - usage=Usage(), - referenceable_id=None, - ) - - async def stream_response( - self, - system_instructions: str | None, - input: str | list[TResponseInputItem], - model_settings: ModelSettings, - tools: list[Tool], - output_schema: AgentOutputSchema | None, - handoffs: list[Handoff], - tracing: ModelTracing, - ) -> AsyncIterator[TResponseStreamEvent]: - with generation_span(disabled=not self.tracing_enabled) as span: - output = self.get_next_output() - if isinstance(output, Exception): - span.set_error( - SpanError( - message="Error", - data={ - "name": output.__class__.__name__, - "message": str(output), - }, - ) - ) - raise output - - yield ResponseCompletedEvent( - type="response.completed", - response=get_response_obj(output), - sequence_number=0, - ) - - -def get_response_obj( - output: list[TResponseOutputItem], response_id: str | None = None -) -> Response: - return Response( - id=response_id or "123", - created_at=123, - model="test_model", - object="response", - output=output, - tool_choice="none", - tools=[], - top_p=None, - parallel_tool_calls=False, - ) diff --git a/pkg/hanzo-agent/tests/test_agent_config.py b/pkg/hanzo-agent/tests/test_agent_config.py deleted file mode 100644 index b237975af..000000000 --- a/pkg/hanzo-agent/tests/test_agent_config.py +++ /dev/null @@ -1,169 +0,0 @@ -import pytest -from pydantic import BaseModel - -from agents import Agent, Handoff, RunContextWrapper, Runner, handoff - - -@pytest.mark.asyncio -async def test_system_instructions(): - agent = Agent[None]( - name="test", - instructions="abc123", - ) - context = RunContextWrapper(None) - - assert await agent.get_system_prompt(context) == "abc123" - - def sync_instructions(agent: Agent[None], context: RunContextWrapper[None]) -> str: - return "sync_123" - - agent = agent.clone(instructions=sync_instructions) - assert await agent.get_system_prompt(context) == "sync_123" - - async def async_instructions( - agent: Agent[None], context: RunContextWrapper[None] - ) -> str: - return "async_123" - - agent = agent.clone(instructions=async_instructions) - assert await agent.get_system_prompt(context) == "async_123" - - -@pytest.mark.asyncio -async def test_handoff_with_agents(): - agent_1 = Agent( - name="agent_1", - ) - - agent_2 = Agent( - name="agent_2", - ) - - agent_3 = Agent( - name="agent_3", - handoffs=[agent_1, agent_2], - ) - - handoffs = Runner._get_handoffs(agent_3) - assert len(handoffs) == 2 - - assert handoffs[0].agent_name == "agent_1" - assert handoffs[1].agent_name == "agent_2" - - first_return = await handoffs[0].on_invoke_handoff(RunContextWrapper(None), "") - assert first_return == agent_1 - - second_return = await handoffs[1].on_invoke_handoff(RunContextWrapper(None), "") - assert second_return == agent_2 - - -@pytest.mark.asyncio -async def test_handoff_with_handoff_obj(): - agent_1 = Agent( - name="agent_1", - ) - - agent_2 = Agent( - name="agent_2", - ) - - agent_3 = Agent( - name="agent_3", - handoffs=[ - handoff(agent_1), - handoff( - agent_2, - tool_name_override="transfer_to_2", - tool_description_override="description_2", - ), - ], - ) - - handoffs = Runner._get_handoffs(agent_3) - assert len(handoffs) == 2 - - assert handoffs[0].agent_name == "agent_1" - assert handoffs[1].agent_name == "agent_2" - - assert handoffs[0].tool_name == Handoff.default_tool_name(agent_1) - assert handoffs[1].tool_name == "transfer_to_2" - - assert handoffs[0].tool_description == Handoff.default_tool_description(agent_1) - assert handoffs[1].tool_description == "description_2" - - first_return = await handoffs[0].on_invoke_handoff(RunContextWrapper(None), "") - assert first_return == agent_1 - - second_return = await handoffs[1].on_invoke_handoff(RunContextWrapper(None), "") - assert second_return == agent_2 - - -@pytest.mark.asyncio -async def test_handoff_with_handoff_obj_and_agent(): - agent_1 = Agent( - name="agent_1", - ) - - agent_2 = Agent( - name="agent_2", - ) - - agent_3 = Agent( - name="agent_3", - handoffs=[handoff(agent_1), agent_2], - ) - - handoffs = Runner._get_handoffs(agent_3) - assert len(handoffs) == 2 - - assert handoffs[0].agent_name == "agent_1" - assert handoffs[1].agent_name == "agent_2" - - assert handoffs[0].tool_name == Handoff.default_tool_name(agent_1) - assert handoffs[1].tool_name == Handoff.default_tool_name(agent_2) - - assert handoffs[0].tool_description == Handoff.default_tool_description(agent_1) - assert handoffs[1].tool_description == Handoff.default_tool_description(agent_2) - - first_return = await handoffs[0].on_invoke_handoff(RunContextWrapper(None), "") - assert first_return == agent_1 - - second_return = await handoffs[1].on_invoke_handoff(RunContextWrapper(None), "") - assert second_return == agent_2 - - -@pytest.mark.asyncio -async def test_agent_cloning(): - agent = Agent( - name="test", - handoff_description="test_description", - model="o3-mini", - ) - - cloned = agent.clone( - handoff_description="new_description", - model="o1", - ) - - assert cloned.name == "test" - assert cloned.handoff_description == "new_description" - assert cloned.model == "o1" - - -class Foo(BaseModel): - bar: str - - -@pytest.mark.asyncio -async def test_agent_final_output(): - agent = Agent( - name="test", - output_type=Foo, - ) - - schema = Runner._get_output_schema(agent) - assert schema is not None - assert schema.output_type == Foo - assert schema.strict_json_schema is True - assert schema.json_schema() is not None - assert not schema.is_plain_text() diff --git a/pkg/hanzo-agent/tests/test_agent_hooks.py b/pkg/hanzo-agent/tests/test_agent_hooks.py deleted file mode 100644 index bd4b067cd..000000000 --- a/pkg/hanzo-agent/tests/test_agent_hooks.py +++ /dev/null @@ -1,428 +0,0 @@ -from __future__ import annotations - -import json -from collections import defaultdict -from typing import Any - -import pytest -from typing_extensions import TypedDict - -from agents.agent import Agent -from agents.lifecycle import AgentHooks -from agents.run import Runner -from agents.run_context import RunContextWrapper, TContext -from agents.tool import Tool - -from .fake_model import FakeModel -from .test_responses import ( - get_final_output_message, - get_function_tool, - get_function_tool_call, - get_handoff_tool_call, - get_text_message, -) - - -class AgentHooksForTests(AgentHooks): - def __init__(self): - self.events: dict[str, int] = defaultdict(int) - - def reset(self): - self.events.clear() - - async def on_start( - self, context: RunContextWrapper[TContext], agent: Agent[TContext] - ) -> None: - self.events["on_start"] += 1 - - async def on_end( - self, - context: RunContextWrapper[TContext], - agent: Agent[TContext], - output: Any, - ) -> None: - self.events["on_end"] += 1 - - async def on_handoff( - self, - context: RunContextWrapper[TContext], - agent: Agent[TContext], - source: Agent[TContext], - ) -> None: - self.events["on_handoff"] += 1 - - async def on_tool_start( - self, - context: RunContextWrapper[TContext], - agent: Agent[TContext], - tool: Tool, - ) -> None: - self.events["on_tool_start"] += 1 - - async def on_tool_end( - self, - context: RunContextWrapper[TContext], - agent: Agent[TContext], - tool: Tool, - result: str, - ) -> None: - self.events["on_tool_end"] += 1 - - -@pytest.mark.asyncio -async def test_non_streamed_agent_hooks(): - hooks = AgentHooksForTests() - model = FakeModel() - agent_1 = Agent( - name="test_1", - model=model, - ) - agent_2 = Agent( - name="test_2", - model=model, - ) - agent_3 = Agent( - name="test_3", - model=model, - handoffs=[agent_1, agent_2], - tools=[get_function_tool("some_function", "result")], - hooks=hooks, - ) - - agent_1.handoffs.append(agent_3) - - model.set_next_output([get_text_message("user_message")]) - output = await Runner.run(agent_3, input="user_message") - assert hooks.events == {"on_start": 1, "on_end": 1}, f"{output}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message and a handoff - [get_text_message("a_message"), get_handoff_tool_call(agent_1)], - # Third turn: text message - [get_text_message("done")], - ] - ) - await Runner.run(agent_3, input="user_message") - - # Shouldn't have on_end because it's not the last agent - assert hooks.events == { - "on_start": 1, # Agent runs once - "on_tool_start": 1, # Only one tool call - "on_tool_end": 1, # Only one tool call - "on_handoff": 1, # Only one handoff - }, f"got unexpected event count: {hooks.events}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message, another tool call, and a handoff - [ - get_text_message("a_message"), - get_function_tool_call("some_function", json.dumps({"a": "b"})), - get_handoff_tool_call(agent_1), - ], - # Third turn: a message and a handoff back to the orig agent - [get_text_message("a_message"), get_handoff_tool_call(agent_3)], - # Fourth turn: text message - [get_text_message("done")], - ] - ) - await Runner.run(agent_3, input="user_message") - - assert hooks.events == { - "on_start": 2, # Agent runs twice - "on_tool_start": 2, # Only one tool call - "on_tool_end": 2, # Only one tool call - "on_handoff": 1, # Only one handoff - "on_end": 1, # Agent 3 is the last agent - }, f"got unexpected event count: {hooks.events}" - hooks.reset() - - -@pytest.mark.asyncio -async def test_streamed_agent_hooks(): - hooks = AgentHooksForTests() - model = FakeModel() - agent_1 = Agent(name="test_1", model=model) - agent_2 = Agent(name="test_2", model=model) - agent_3 = Agent( - name="test_3", - model=model, - handoffs=[agent_1, agent_2], - tools=[get_function_tool("some_function", "result")], - hooks=hooks, - ) - - agent_1.handoffs.append(agent_3) - - model.set_next_output([get_text_message("user_message")]) - output = Runner.run_streamed(agent_3, input="user_message") - async for _ in output.stream_events(): - pass - assert hooks.events == {"on_start": 1, "on_end": 1}, f"{output}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message and a handoff - [get_text_message("a_message"), get_handoff_tool_call(agent_1)], - # Third turn: text message - [get_text_message("done")], - ] - ) - output = Runner.run_streamed(agent_3, input="user_message") - async for _ in output.stream_events(): - pass - - # Shouldn't have on_end because it's not the last agent - assert hooks.events == { - "on_start": 1, # Agent runs twice - "on_tool_start": 1, # Only one tool call - "on_tool_end": 1, # Only one tool call - "on_handoff": 1, # Only one handoff - }, f"got unexpected event count: {hooks.events}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message, another tool call, and a handoff - [ - get_text_message("a_message"), - get_function_tool_call("some_function", json.dumps({"a": "b"})), - get_handoff_tool_call(agent_1), - ], - # Third turn: a message and a handoff back to the orig agent - [get_text_message("a_message"), get_handoff_tool_call(agent_3)], - # Fourth turn: text message - [get_text_message("done")], - ] - ) - output = Runner.run_streamed(agent_3, input="user_message") - async for _ in output.stream_events(): - pass - - assert hooks.events == { - "on_start": 2, # Agent runs twice - "on_tool_start": 2, # Only one tool call - "on_tool_end": 2, # Only one tool call - "on_handoff": 1, # Only one handoff - "on_end": 1, # Agent 3 is the last agent - }, f"got unexpected event count: {hooks.events}" - hooks.reset() - - -class Foo(TypedDict): - a: str - - -@pytest.mark.asyncio -async def test_structed_output_non_streamed_agent_hooks(): - hooks = AgentHooksForTests() - model = FakeModel() - agent_1 = Agent(name="test_1", model=model) - agent_2 = Agent(name="test_2", model=model) - agent_3 = Agent( - name="test_3", - model=model, - handoffs=[agent_1, agent_2], - tools=[get_function_tool("some_function", "result")], - hooks=hooks, - output_type=Foo, - ) - - agent_1.handoffs.append(agent_3) - - model.set_next_output([get_final_output_message(json.dumps({"a": "b"}))]) - output = await Runner.run(agent_3, input="user_message") - assert hooks.events == {"on_start": 1, "on_end": 1}, f"{output}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message and a handoff - [get_text_message("a_message"), get_handoff_tool_call(agent_1)], - # Third turn: end message (for agent 1) - [get_text_message("done")], - ] - ) - await Runner.run(agent_3, input="user_message") - - # Shouldn't have on_end because it's not the last agent - assert hooks.events == { - "on_start": 1, # Agent runs twice - "on_tool_start": 1, # Only one tool call - "on_tool_end": 1, # Only one tool call - "on_handoff": 1, # Only one handoff - }, f"got unexpected event count: {hooks.events}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message, another tool call, and a handoff - [ - get_text_message("a_message"), - get_function_tool_call("some_function", json.dumps({"a": "b"})), - get_handoff_tool_call(agent_1), - ], - # Third turn: a message and a handoff back to the orig agent - [get_text_message("a_message"), get_handoff_tool_call(agent_3)], - # Fourth turn: end message (for agent 3) - [get_final_output_message(json.dumps({"a": "b"}))], - ] - ) - await Runner.run(agent_3, input="user_message") - - assert hooks.events == { - "on_start": 2, # Agent runs twice - "on_tool_start": 2, # Only one tool call - "on_tool_end": 2, # Only one tool call - "on_handoff": 1, # Only one handoff - "on_end": 1, # Agent 3 is the last agent - }, f"got unexpected event count: {hooks.events}" - hooks.reset() - - -@pytest.mark.asyncio -async def test_structed_output_streamed_agent_hooks(): - hooks = AgentHooksForTests() - model = FakeModel() - agent_1 = Agent(name="test_1", model=model) - agent_2 = Agent(name="test_2", model=model) - agent_3 = Agent( - name="test_3", - model=model, - handoffs=[agent_1, agent_2], - tools=[get_function_tool("some_function", "result")], - hooks=hooks, - output_type=Foo, - ) - - agent_1.handoffs.append(agent_3) - - model.set_next_output([get_final_output_message(json.dumps({"a": "b"}))]) - output = Runner.run_streamed(agent_3, input="user_message") - async for _ in output.stream_events(): - pass - assert hooks.events == {"on_start": 1, "on_end": 1}, f"{output}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message and a handoff - [get_text_message("a_message"), get_handoff_tool_call(agent_1)], - # Third turn: end message (for agent 1) - [get_text_message("done")], - ] - ) - await Runner.run(agent_3, input="user_message") - # Shouldn't have on_end because it's not the last agent - assert hooks.events == { - "on_start": 1, # Agent runs twice - "on_tool_start": 1, # Only one tool call - "on_tool_end": 1, # Only one tool call - "on_handoff": 1, # Only one handoff - }, f"got unexpected event count: {hooks.events}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message, another tool call, and a handoff - [ - get_text_message("a_message"), - get_function_tool_call("some_function", json.dumps({"a": "b"})), - get_handoff_tool_call(agent_1), - ], - # Third turn: a message and a handoff back to the orig agent - [get_text_message("a_message"), get_handoff_tool_call(agent_3)], - # Fourth turn: end message (for agent 3) - [get_final_output_message(json.dumps({"a": "b"}))], - ] - ) - output = Runner.run_streamed(agent_3, input="user_message") - async for _ in output.stream_events(): - pass - - assert hooks.events == { - "on_start": 2, # Agent runs twice - "on_tool_start": 2, # 2 tool calls - "on_tool_end": 2, # 2 tool calls - "on_handoff": 1, # 1 handoff - "on_end": 1, # Agent 3 is the last agent - }, f"got unexpected event count: {hooks.events}" - hooks.reset() - - -class EmptyAgentHooks(AgentHooks): - pass - - -@pytest.mark.asyncio -async def test_base_agent_hooks_dont_crash(): - hooks = EmptyAgentHooks() - model = FakeModel() - agent_1 = Agent(name="test_1", model=model) - agent_2 = Agent(name="test_2", model=model) - agent_3 = Agent( - name="test_3", - model=model, - handoffs=[agent_1, agent_2], - tools=[get_function_tool("some_function", "result")], - hooks=hooks, - output_type=Foo, - ) - agent_1.handoffs.append(agent_3) - - model.set_next_output([get_final_output_message(json.dumps({"a": "b"}))]) - output = Runner.run_streamed(agent_3, input="user_message") - async for _ in output.stream_events(): - pass - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message and a handoff - [get_text_message("a_message"), get_handoff_tool_call(agent_1)], - # Third turn: end message (for agent 1) - [get_text_message("done")], - ] - ) - await Runner.run(agent_3, input="user_message") - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message, another tool call, and a handoff - [ - get_text_message("a_message"), - get_function_tool_call("some_function", json.dumps({"a": "b"})), - get_handoff_tool_call(agent_1), - ], - # Third turn: a message and a handoff back to the orig agent - [get_text_message("a_message"), get_handoff_tool_call(agent_3)], - # Fourth turn: end message (for agent 3) - [get_final_output_message(json.dumps({"a": "b"}))], - ] - ) - output = Runner.run_streamed(agent_3, input="user_message") - async for _ in output.stream_events(): - pass diff --git a/pkg/hanzo-agent/tests/test_agent_runner.py b/pkg/hanzo-agent/tests/test_agent_runner.py deleted file mode 100644 index 7d81abe47..000000000 --- a/pkg/hanzo-agent/tests/test_agent_runner.py +++ /dev/null @@ -1,601 +0,0 @@ -from __future__ import annotations - -import json -from typing import Any - -import pytest -from typing_extensions import TypedDict - -from agents import ( - Agent, - GuardrailFunctionOutput, - Handoff, - HandoffInputData, - InputGuardrail, - InputGuardrailTripwireTriggered, - ModelBehaviorError, - OutputGuardrail, - OutputGuardrailTripwireTriggered, - RunContextWrapper, - Runner, - UserError, - handoff, -) - -from .fake_model import FakeModel -from .test_responses import ( - get_final_output_message, - get_function_tool, - get_function_tool_call, - get_handoff_tool_call, - get_text_input_item, - get_text_message, -) - - -@pytest.mark.asyncio -async def test_simple_first_run(): - model = FakeModel() - agent = Agent( - name="test", - model=model, - ) - model.set_next_output([get_text_message("first")]) - - result = await Runner.run(agent, input="test") - assert result.input == "test" - assert len(result.new_items) == 1, "exactly one item should be generated" - assert result.final_output == "first" - assert ( - len(result.raw_responses) == 1 - ), "exactly one model response should be generated" - assert result.raw_responses[0].output == [get_text_message("first")] - assert result.last_agent == agent - - assert ( - len(result.to_input_list()) == 2 - ), "should have original input and generated item" - - model.set_next_output([get_text_message("second")]) - - result = await Runner.run( - agent, - input=[get_text_input_item("message"), get_text_input_item("another_message")], - ) - assert len(result.new_items) == 1, "exactly one item should be generated" - assert result.final_output == "second" - assert ( - len(result.raw_responses) == 1 - ), "exactly one model response should be generated" - assert ( - len(result.to_input_list()) == 3 - ), "should have original input and generated item" - - -@pytest.mark.asyncio -async def test_subsequent_runs(): - model = FakeModel() - agent = Agent( - name="test", - model=model, - ) - model.set_next_output([get_text_message("third")]) - - result = await Runner.run(agent, input="test") - assert result.input == "test" - assert len(result.new_items) == 1, "exactly one item should be generated" - assert ( - len(result.to_input_list()) == 2 - ), "should have original input and generated item" - - model.set_next_output([get_text_message("fourth")]) - - result = await Runner.run(agent, input=result.to_input_list()) - assert len(result.input) == 2, f"should have previous input but got {result.input}" - assert len(result.new_items) == 1, "exactly one item should be generated" - assert result.final_output == "fourth" - assert ( - len(result.raw_responses) == 1 - ), "exactly one model response should be generated" - assert result.raw_responses[0].output == [get_text_message("fourth")] - assert result.last_agent == agent - assert ( - len(result.to_input_list()) == 3 - ), "should have original input and generated items" - - -@pytest.mark.asyncio -async def test_tool_call_runs(): - model = FakeModel() - agent = Agent( - name="test", - model=model, - tools=[get_function_tool("foo", "tool_result")], - ) - - model.add_multiple_turn_outputs( - [ - # First turn: a message and tool call - [ - get_text_message("a_message"), - get_function_tool_call("foo", json.dumps({"a": "b"})), - ], - # Second turn: text message - [get_text_message("done")], - ] - ) - - result = await Runner.run(agent, input="user_message") - - assert result.final_output == "done" - assert len(result.raw_responses) == 2, ( - "should have two responses: the first which produces a tool call, and the second which" - "handles the tool result" - ) - - assert len(result.to_input_list()) == 5, ( - "should have five inputs: the original input, the message, the tool call, the tool result " - "and the done message" - ) - - -@pytest.mark.asyncio -async def test_handoffs(): - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - ) - agent_2 = Agent( - name="test", - model=model, - ) - agent_3 = Agent( - name="test", - model=model, - handoffs=[agent_1, agent_2], - tools=[get_function_tool("some_function", "result")], - ) - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message and a handoff - [get_text_message("a_message"), get_handoff_tool_call(agent_1)], - # Third turn: text message - [get_text_message("done")], - ] - ) - - result = await Runner.run(agent_3, input="user_message") - - assert result.final_output == "done" - assert len(result.raw_responses) == 3, "should have three model responses" - assert len(result.to_input_list()) == 7, ( - "should have 7 inputs: orig input, tool call, tool result, message, handoff, handoff" - "result, and done message" - ) - assert result.last_agent == agent_1, "should have handed off to agent_1" - - -class Foo(TypedDict): - bar: str - - -@pytest.mark.asyncio -async def test_structured_output(): - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - tools=[get_function_tool("bar", "bar_result")], - output_type=Foo, - ) - - agent_2 = Agent( - name="test", - model=model, - tools=[get_function_tool("foo", "foo_result")], - handoffs=[agent_1], - ) - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("foo", json.dumps({"bar": "baz"}))], - # Second turn: a message and a handoff - [get_text_message("a_message"), get_handoff_tool_call(agent_1)], - # Third turn: tool call and structured output - [ - get_function_tool_call("bar", json.dumps({"bar": "baz"})), - get_final_output_message(json.dumps(Foo(bar="baz"))), - ], - ] - ) - - result = await Runner.run( - agent_2, - input=[ - get_text_input_item("user_message"), - get_text_input_item("another_message"), - ], - ) - - assert result.final_output == Foo(bar="baz") - assert len(result.raw_responses) == 3, "should have three model responses" - assert len(result.to_input_list()) == 10, ( - "should have input: 2 orig inputs, function call, function call result, message, handoff, " - "handoff output, tool call, tool call result, final output message" - ) - - assert result.last_agent == agent_1, "should have handed off to agent_1" - assert result.final_output == Foo(bar="baz"), "should have structured output" - - -def remove_new_items(handoff_input_data: HandoffInputData) -> HandoffInputData: - return HandoffInputData( - input_history=handoff_input_data.input_history, - pre_handoff_items=(), - new_items=(), - ) - - -@pytest.mark.asyncio -async def test_handoff_filters(): - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - ) - agent_2 = Agent( - name="test", - model=model, - handoffs=[ - handoff( - agent=agent_1, - input_filter=remove_new_items, - ) - ], - ) - - model.add_multiple_turn_outputs( - [ - [ - get_text_message("1"), - get_text_message("2"), - get_handoff_tool_call(agent_1), - ], - [get_text_message("last")], - ] - ) - - result = await Runner.run(agent_2, input="user_message") - - assert result.final_output == "last" - assert len(result.raw_responses) == 2, "should have two model responses" - assert ( - len(result.to_input_list()) == 2 - ), "should only have 2 inputs: orig input and last message" - - -@pytest.mark.asyncio -async def test_async_input_filter_fails(): - # DO NOT rename this without updating pyproject.toml - - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - ) - - async def on_invoke_handoff( - _ctx: RunContextWrapper[Any], _input: str - ) -> Agent[Any]: - return agent_1 - - async def invalid_input_filter(data: HandoffInputData) -> HandoffInputData: - return data # pragma: no cover - - agent_2 = Agent[None]( - name="test", - model=model, - handoffs=[ - Handoff( - tool_name=Handoff.default_tool_name(agent_1), - tool_description=Handoff.default_tool_description(agent_1), - input_json_schema={}, - on_invoke_handoff=on_invoke_handoff, - agent_name=agent_1.name, - # Purposely ignoring the type error here to simulate invalid input - input_filter=invalid_input_filter, # type: ignore - ) - ], - ) - - model.add_multiple_turn_outputs( - [ - [ - get_text_message("1"), - get_text_message("2"), - get_handoff_tool_call(agent_1), - ], - [get_text_message("last")], - ] - ) - - with pytest.raises(UserError): - await Runner.run(agent_2, input="user_message") - - -@pytest.mark.asyncio -async def test_invalid_input_filter_fails(): - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - ) - - async def on_invoke_handoff( - _ctx: RunContextWrapper[Any], _input: str - ) -> Agent[Any]: - return agent_1 - - def invalid_input_filter(data: HandoffInputData) -> HandoffInputData: - # Purposely returning a string to simulate invalid output - return "foo" # type: ignore - - agent_2 = Agent[None]( - name="test", - model=model, - handoffs=[ - Handoff( - tool_name=Handoff.default_tool_name(agent_1), - tool_description=Handoff.default_tool_description(agent_1), - input_json_schema={}, - on_invoke_handoff=on_invoke_handoff, - agent_name=agent_1.name, - input_filter=invalid_input_filter, - ) - ], - ) - - model.add_multiple_turn_outputs( - [ - [ - get_text_message("1"), - get_text_message("2"), - get_handoff_tool_call(agent_1), - ], - [get_text_message("last")], - ] - ) - - with pytest.raises(UserError): - await Runner.run(agent_2, input="user_message") - - -@pytest.mark.asyncio -async def test_non_callable_input_filter_causes_error(): - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - ) - - async def on_invoke_handoff( - _ctx: RunContextWrapper[Any], _input: str - ) -> Agent[Any]: - return agent_1 - - agent_2 = Agent[None]( - name="test", - model=model, - handoffs=[ - Handoff( - tool_name=Handoff.default_tool_name(agent_1), - tool_description=Handoff.default_tool_description(agent_1), - input_json_schema={}, - on_invoke_handoff=on_invoke_handoff, - agent_name=agent_1.name, - # Purposely ignoring the type error here to simulate invalid input - input_filter="foo", # type: ignore - ) - ], - ) - - model.add_multiple_turn_outputs( - [ - [ - get_text_message("1"), - get_text_message("2"), - get_handoff_tool_call(agent_1), - ], - [get_text_message("last")], - ] - ) - - with pytest.raises(UserError): - await Runner.run(agent_2, input="user_message") - - -@pytest.mark.asyncio -async def test_handoff_on_input(): - call_output: str | None = None - - def on_input(_ctx: RunContextWrapper[Any], data: Foo) -> None: - nonlocal call_output - call_output = data["bar"] - - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - ) - - agent_2 = Agent( - name="test", - model=model, - handoffs=[ - handoff( - agent=agent_1, - on_handoff=on_input, - input_type=Foo, - ) - ], - ) - - model.add_multiple_turn_outputs( - [ - [ - get_text_message("1"), - get_text_message("2"), - get_handoff_tool_call(agent_1, args=json.dumps(Foo(bar="test_input"))), - ], - [get_text_message("last")], - ] - ) - - result = await Runner.run(agent_2, input="user_message") - - assert result.final_output == "last" - - assert ( - call_output == "test_input" - ), "should have called the handoff with the correct input" - - -@pytest.mark.asyncio -async def test_async_handoff_on_input(): - call_output: str | None = None - - async def on_input(_ctx: RunContextWrapper[Any], data: Foo) -> None: - nonlocal call_output - call_output = data["bar"] - - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - ) - - agent_2 = Agent( - name="test", - model=model, - handoffs=[ - handoff( - agent=agent_1, - on_handoff=on_input, - input_type=Foo, - ) - ], - ) - - model.add_multiple_turn_outputs( - [ - [ - get_text_message("1"), - get_text_message("2"), - get_handoff_tool_call(agent_1, args=json.dumps(Foo(bar="test_input"))), - ], - [get_text_message("last")], - ] - ) - - result = await Runner.run(agent_2, input="user_message") - - assert result.final_output == "last" - - assert ( - call_output == "test_input" - ), "should have called the handoff with the correct input" - - -@pytest.mark.asyncio -async def test_wrong_params_on_input_causes_error(): - agent_1 = Agent( - name="test", - ) - - def _on_handoff_too_many_params( - ctx: RunContextWrapper[Any], foo: Foo, bar: str - ) -> None: - pass - - with pytest.raises(UserError): - handoff( - agent_1, - input_type=Foo, - # Purposely ignoring the type error here to simulate invalid input - on_handoff=_on_handoff_too_many_params, # type: ignore - ) - - def on_handoff_too_few_params(ctx: RunContextWrapper[Any]) -> None: - pass - - with pytest.raises(UserError): - handoff( - agent_1, - input_type=Foo, - # Purposely ignoring the type error here to simulate invalid input - on_handoff=on_handoff_too_few_params, # type: ignore - ) - - -@pytest.mark.asyncio -async def test_invalid_handoff_input_json_causes_error(): - agent = Agent(name="test") - h = handoff(agent, input_type=Foo, on_handoff=lambda _ctx, _input: None) - - with pytest.raises(ModelBehaviorError): - await h.on_invoke_handoff( - RunContextWrapper(None), - # Purposely ignoring the type error here to simulate invalid input - None, # type: ignore - ) - - with pytest.raises(ModelBehaviorError): - await h.on_invoke_handoff(RunContextWrapper(None), "invalid") - - -@pytest.mark.asyncio -async def test_input_guardrail_tripwire_triggered_causes_exception(): - def guardrail_function( - context: RunContextWrapper[Any], agent: Agent[Any], input: Any - ) -> GuardrailFunctionOutput: - return GuardrailFunctionOutput( - output_info=None, - tripwire_triggered=True, - ) - - agent = Agent( - name="test", - input_guardrails=[InputGuardrail(guardrail_function=guardrail_function)], - ) - model = FakeModel() - model.set_next_output([get_text_message("user_message")]) - - with pytest.raises(InputGuardrailTripwireTriggered): - await Runner.run(agent, input="user_message") - - -@pytest.mark.asyncio -async def test_output_guardrail_tripwire_triggered_causes_exception(): - def guardrail_function( - context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any - ) -> GuardrailFunctionOutput: - return GuardrailFunctionOutput( - output_info=None, - tripwire_triggered=True, - ) - - model = FakeModel() - agent = Agent( - name="test", - output_guardrails=[OutputGuardrail(guardrail_function=guardrail_function)], - model=model, - ) - model.set_next_output([get_text_message("user_message")]) - - with pytest.raises(OutputGuardrailTripwireTriggered): - await Runner.run(agent, input="user_message") diff --git a/pkg/hanzo-agent/tests/test_agent_runner_streamed.py b/pkg/hanzo-agent/tests/test_agent_runner_streamed.py deleted file mode 100644 index 6c97542ec..000000000 --- a/pkg/hanzo-agent/tests/test_agent_runner_streamed.py +++ /dev/null @@ -1,732 +0,0 @@ -from __future__ import annotations - -import json -from typing import Any - -import pytest -from typing_extensions import TypedDict - -from agents import ( - Agent, - GuardrailFunctionOutput, - Handoff, - HandoffInputData, - InputGuardrail, - InputGuardrailTripwireTriggered, - OutputGuardrail, - OutputGuardrailTripwireTriggered, - RunContextWrapper, - Runner, - UserError, - handoff, -) -from agents.items import RunItem -from agents.run import RunConfig -from agents.stream_events import AgentUpdatedStreamEvent - -from .fake_model import FakeModel -from .test_responses import ( - get_final_output_message, - get_function_tool, - get_function_tool_call, - get_handoff_tool_call, - get_text_input_item, - get_text_message, -) - - -@pytest.mark.asyncio -async def test_simple_first_run(): - model = FakeModel() - agent = Agent( - name="test", - model=model, - ) - model.set_next_output([get_text_message("first")]) - - result = Runner.run_streamed(agent, input="test") - async for _ in result.stream_events(): - pass - - assert result.input == "test" - assert len(result.new_items) == 1, "exactly one item should be generated" - assert result.final_output == "first" - assert ( - len(result.raw_responses) == 1 - ), "exactly one model response should be generated" - assert result.raw_responses[0].output == [get_text_message("first")] - assert result.last_agent == agent - - assert ( - len(result.to_input_list()) == 2 - ), "should have original input and generated item" - - model.set_next_output([get_text_message("second")]) - - result = Runner.run_streamed( - agent, - input=[get_text_input_item("message"), get_text_input_item("another_message")], - ) - async for _ in result.stream_events(): - pass - - assert len(result.new_items) == 1, "exactly one item should be generated" - assert result.final_output == "second" - assert ( - len(result.raw_responses) == 1 - ), "exactly one model response should be generated" - assert ( - len(result.to_input_list()) == 3 - ), "should have original input and generated item" - - -@pytest.mark.asyncio -async def test_subsequent_runs(): - model = FakeModel() - agent = Agent( - name="test", - model=model, - ) - model.set_next_output([get_text_message("third")]) - - result = Runner.run_streamed(agent, input="test") - async for _ in result.stream_events(): - pass - - assert result.input == "test" - assert len(result.new_items) == 1, "exactly one item should be generated" - assert ( - len(result.to_input_list()) == 2 - ), "should have original input and generated item" - - model.set_next_output([get_text_message("fourth")]) - - result = Runner.run_streamed(agent, input=result.to_input_list()) - async for _ in result.stream_events(): - pass - - assert len(result.input) == 2, f"should have previous input but got {result.input}" - assert len(result.new_items) == 1, "exactly one item should be generated" - assert result.final_output == "fourth" - assert ( - len(result.raw_responses) == 1 - ), "exactly one model response should be generated" - assert result.raw_responses[0].output == [get_text_message("fourth")] - assert result.last_agent == agent - assert ( - len(result.to_input_list()) == 3 - ), "should have original input and generated items" - - -@pytest.mark.asyncio -async def test_tool_call_runs(): - model = FakeModel() - agent = Agent( - name="test", - model=model, - tools=[get_function_tool("foo", "tool_result")], - ) - - model.add_multiple_turn_outputs( - [ - # First turn: a message and tool call - [ - get_text_message("a_message"), - get_function_tool_call("foo", json.dumps({"a": "b"})), - ], - # Second turn: text message - [get_text_message("done")], - ] - ) - - result = Runner.run_streamed(agent, input="user_message") - async for _ in result.stream_events(): - pass - - assert result.final_output == "done" - assert len(result.raw_responses) == 2, ( - "should have two responses: the first which produces a tool call, and the second which" - "handles the tool result" - ) - - assert len(result.to_input_list()) == 5, ( - "should have five inputs: the original input, the message, the tool call, the tool result " - "and the done message" - ) - - -@pytest.mark.asyncio -async def test_handoffs(): - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - ) - agent_2 = Agent( - name="test", - model=model, - ) - agent_3 = Agent( - name="test", - model=model, - handoffs=[agent_1, agent_2], - tools=[get_function_tool("some_function", "result")], - ) - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message and a handoff - [get_text_message("a_message"), get_handoff_tool_call(agent_1)], - # Third turn: text message - [get_text_message("done")], - ] - ) - - result = Runner.run_streamed(agent_3, input="user_message") - async for _ in result.stream_events(): - pass - - assert result.final_output == "done" - assert len(result.raw_responses) == 3, "should have three model responses" - assert len(result.to_input_list()) == 7, ( - "should have 7 inputs: orig input, tool call, tool result, message, handoff, handoff" - "result, and done message" - ) - assert result.last_agent == agent_1, "should have handed off to agent_1" - - -class Foo(TypedDict): - bar: str - - -@pytest.mark.asyncio -async def test_structured_output(): - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - tools=[get_function_tool("bar", "bar_result")], - output_type=Foo, - ) - - agent_2 = Agent( - name="test", - model=model, - tools=[get_function_tool("foo", "foo_result")], - handoffs=[agent_1], - ) - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("foo", json.dumps({"bar": "baz"}))], - # Second turn: a message and a handoff - [get_text_message("a_message"), get_handoff_tool_call(agent_1)], - # Third turn: tool call and structured output - [ - get_function_tool_call("bar", json.dumps({"bar": "baz"})), - get_final_output_message(json.dumps(Foo(bar="baz"))), - ], - ] - ) - - result = Runner.run_streamed( - agent_2, - input=[ - get_text_input_item("user_message"), - get_text_input_item("another_message"), - ], - ) - async for _ in result.stream_events(): - pass - - assert result.final_output == Foo(bar="baz") - assert len(result.raw_responses) == 3, "should have three model responses" - assert len(result.to_input_list()) == 10, ( - "should have input: 2 orig inputs, function call, function call result, message, handoff, " - "handoff output, tool call, tool call result, final output" - ) - - assert result.last_agent == agent_1, "should have handed off to agent_1" - assert result.final_output == Foo(bar="baz"), "should have structured output" - - -def remove_new_items(handoff_input_data: HandoffInputData) -> HandoffInputData: - return HandoffInputData( - input_history=handoff_input_data.input_history, - pre_handoff_items=(), - new_items=(), - ) - - -@pytest.mark.asyncio -async def test_handoff_filters(): - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - ) - agent_2 = Agent( - name="test", - model=model, - handoffs=[ - handoff( - agent=agent_1, - input_filter=remove_new_items, - ) - ], - ) - - model.add_multiple_turn_outputs( - [ - [ - get_text_message("1"), - get_text_message("2"), - get_handoff_tool_call(agent_1), - ], - [get_text_message("last")], - ] - ) - - result = Runner.run_streamed(agent_2, input="user_message") - async for _ in result.stream_events(): - pass - - assert result.final_output == "last" - assert len(result.raw_responses) == 2, "should have two model responses" - assert ( - len(result.to_input_list()) == 2 - ), "should only have 2 inputs: orig input and last message" - - -@pytest.mark.asyncio -async def test_async_input_filter_fails(): - # DO NOT rename this without updating pyproject.toml - - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - ) - - async def on_invoke_handoff( - _ctx: RunContextWrapper[Any], _input: str - ) -> Agent[Any]: - return agent_1 - - async def invalid_input_filter(data: HandoffInputData) -> HandoffInputData: - return data # pragma: no cover - - agent_2 = Agent[None]( - name="test", - model=model, - handoffs=[ - Handoff( - tool_name=Handoff.default_tool_name(agent_1), - tool_description=Handoff.default_tool_description(agent_1), - input_json_schema={}, - on_invoke_handoff=on_invoke_handoff, - agent_name=agent_1.name, - # Purposely ignoring the type error here to simulate invalid input - input_filter=invalid_input_filter, # type: ignore - ) - ], - ) - - model.add_multiple_turn_outputs( - [ - [ - get_text_message("1"), - get_text_message("2"), - get_handoff_tool_call(agent_1), - ], - [get_text_message("last")], - ] - ) - - with pytest.raises(UserError): - result = Runner.run_streamed(agent_2, input="user_message") - async for _ in result.stream_events(): - pass - - -@pytest.mark.asyncio -async def test_invalid_input_filter_fails(): - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - ) - - async def on_invoke_handoff( - _ctx: RunContextWrapper[Any], _input: str - ) -> Agent[Any]: - return agent_1 - - def invalid_input_filter(data: HandoffInputData) -> HandoffInputData: - # Purposely returning a string to simulate invalid output - return "foo" # type: ignore - - agent_2 = Agent[None]( - name="test", - model=model, - handoffs=[ - Handoff( - tool_name=Handoff.default_tool_name(agent_1), - tool_description=Handoff.default_tool_description(agent_1), - input_json_schema={}, - on_invoke_handoff=on_invoke_handoff, - agent_name=agent_1.name, - input_filter=invalid_input_filter, - ) - ], - ) - - model.add_multiple_turn_outputs( - [ - [ - get_text_message("1"), - get_text_message("2"), - get_handoff_tool_call(agent_1), - ], - [get_text_message("last")], - ] - ) - - with pytest.raises(UserError): - result = Runner.run_streamed(agent_2, input="user_message") - async for _ in result.stream_events(): - pass - - -@pytest.mark.asyncio -async def test_non_callable_input_filter_causes_error(): - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - ) - - async def on_invoke_handoff( - _ctx: RunContextWrapper[Any], _input: str - ) -> Agent[Any]: - return agent_1 - - agent_2 = Agent[None]( - name="test", - model=model, - handoffs=[ - Handoff( - tool_name=Handoff.default_tool_name(agent_1), - tool_description=Handoff.default_tool_description(agent_1), - input_json_schema={}, - on_invoke_handoff=on_invoke_handoff, - agent_name=agent_1.name, - # Purposely ignoring the type error here to simulate invalid input - input_filter="foo", # type: ignore - ) - ], - ) - - model.add_multiple_turn_outputs( - [ - [ - get_text_message("1"), - get_text_message("2"), - get_handoff_tool_call(agent_1), - ], - [get_text_message("last")], - ] - ) - - with pytest.raises(UserError): - result = Runner.run_streamed(agent_2, input="user_message") - async for _ in result.stream_events(): - pass - - -@pytest.mark.asyncio -async def test_handoff_on_input(): - call_output: str | None = None - - def on_input(_ctx: RunContextWrapper[Any], data: Foo) -> None: - nonlocal call_output - call_output = data["bar"] - - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - ) - - agent_2 = Agent( - name="test", - model=model, - handoffs=[ - handoff( - agent=agent_1, - on_handoff=on_input, - input_type=Foo, - ) - ], - ) - - model.add_multiple_turn_outputs( - [ - [ - get_text_message("1"), - get_text_message("2"), - get_handoff_tool_call(agent_1, args=json.dumps(Foo(bar="test_input"))), - ], - [get_text_message("last")], - ] - ) - - result = Runner.run_streamed(agent_2, input="user_message") - async for _ in result.stream_events(): - pass - - assert result.final_output == "last" - - assert ( - call_output == "test_input" - ), "should have called the handoff with the correct input" - - -@pytest.mark.asyncio -async def test_async_handoff_on_input(): - call_output: str | None = None - - async def on_input(_ctx: RunContextWrapper[Any], data: Foo) -> None: - nonlocal call_output - call_output = data["bar"] - - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - ) - - agent_2 = Agent( - name="test", - model=model, - handoffs=[ - handoff( - agent=agent_1, - on_handoff=on_input, - input_type=Foo, - ) - ], - ) - - model.add_multiple_turn_outputs( - [ - [ - get_text_message("1"), - get_text_message("2"), - get_handoff_tool_call(agent_1, args=json.dumps(Foo(bar="test_input"))), - ], - [get_text_message("last")], - ] - ) - - result = Runner.run_streamed(agent_2, input="user_message") - async for _ in result.stream_events(): - pass - - assert result.final_output == "last" - - assert ( - call_output == "test_input" - ), "should have called the handoff with the correct input" - - -@pytest.mark.asyncio -async def test_input_guardrail_tripwire_triggered_causes_exception_streamed(): - def guardrail_function( - context: RunContextWrapper[Any], agent: Agent[Any], input: Any - ) -> GuardrailFunctionOutput: - return GuardrailFunctionOutput( - output_info=None, - tripwire_triggered=True, - ) - - agent = Agent( - name="test", - input_guardrails=[InputGuardrail(guardrail_function=guardrail_function)], - model=FakeModel(), - ) - - with pytest.raises(InputGuardrailTripwireTriggered): - result = Runner.run_streamed(agent, input="user_message") - async for _ in result.stream_events(): - pass - - -@pytest.mark.asyncio -async def test_output_guardrail_tripwire_triggered_causes_exception_streamed(): - def guardrail_function( - context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any - ) -> GuardrailFunctionOutput: - return GuardrailFunctionOutput( - output_info=None, - tripwire_triggered=True, - ) - - model = FakeModel(initial_output=[get_text_message("first_test")]) - - agent = Agent( - name="test", - output_guardrails=[OutputGuardrail(guardrail_function=guardrail_function)], - model=model, - ) - - with pytest.raises(OutputGuardrailTripwireTriggered): - result = Runner.run_streamed(agent, input="user_message") - async for _ in result.stream_events(): - pass - - -@pytest.mark.asyncio -async def test_run_input_guardrail_tripwire_triggered_causes_exception_streamed(): - def guardrail_function( - context: RunContextWrapper[Any], agent: Agent[Any], input: Any - ) -> GuardrailFunctionOutput: - return GuardrailFunctionOutput( - output_info=None, - tripwire_triggered=True, - ) - - agent = Agent( - name="test", - model=FakeModel(), - ) - - with pytest.raises(InputGuardrailTripwireTriggered): - result = Runner.run_streamed( - agent, - input="user_message", - run_config=RunConfig( - input_guardrails=[InputGuardrail(guardrail_function=guardrail_function)] - ), - ) - async for _ in result.stream_events(): - pass - - -@pytest.mark.asyncio -async def test_run_output_guardrail_tripwire_triggered_causes_exception_streamed(): - def guardrail_function( - context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any - ) -> GuardrailFunctionOutput: - return GuardrailFunctionOutput( - output_info=None, - tripwire_triggered=True, - ) - - model = FakeModel(initial_output=[get_text_message("first_test")]) - - agent = Agent( - name="test", - model=model, - ) - - with pytest.raises(OutputGuardrailTripwireTriggered): - result = Runner.run_streamed( - agent, - input="user_message", - run_config=RunConfig( - output_guardrails=[ - OutputGuardrail(guardrail_function=guardrail_function) - ] - ), - ) - async for _ in result.stream_events(): - pass - - -@pytest.mark.asyncio -async def test_streaming_events(): - model = FakeModel() - agent_1 = Agent( - name="test", - model=model, - tools=[get_function_tool("bar", "bar_result")], - output_type=Foo, - ) - - agent_2 = Agent( - name="test", - model=model, - tools=[get_function_tool("foo", "foo_result")], - handoffs=[agent_1], - ) - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("foo", json.dumps({"bar": "baz"}))], - # Second turn: a message and a handoff - [get_text_message("a_message"), get_handoff_tool_call(agent_1)], - # Third turn: tool call and structured output - [ - get_function_tool_call("bar", json.dumps({"bar": "baz"})), - get_final_output_message(json.dumps(Foo(bar="baz"))), - ], - ] - ) - - # event_type: (count, event) - event_counts: dict[str, int] = {} - item_data: list[RunItem] = [] - agent_data: list[AgentUpdatedStreamEvent] = [] - - result = Runner.run_streamed( - agent_2, - input=[ - get_text_input_item("user_message"), - get_text_input_item("another_message"), - ], - ) - async for event in result.stream_events(): - event_counts[event.type] = event_counts.get(event.type, 0) + 1 - if event.type == "run_item_stream_event": - item_data.append(event.item) - elif event.type == "agent_updated_stream_event": - agent_data.append(event) - - assert result.final_output == Foo(bar="baz") - assert len(result.raw_responses) == 3, "should have three model responses" - assert len(result.to_input_list()) == 10, ( - "should have input: 2 orig inputs, function call, function call result, message, handoff, " - "handoff output, tool call, tool call result, final output" - ) - - assert result.last_agent == agent_1, "should have handed off to agent_1" - assert result.final_output == Foo(bar="baz"), "should have structured output" - - # Now lets check the events - - expected_item_type_map = { - "tool_call": 2, - "tool_call_output": 2, - "message": 2, - "handoff": 1, - "handoff_output": 1, - } - - total_expected_item_count = sum(expected_item_type_map.values()) - - assert event_counts["run_item_stream_event"] == total_expected_item_count, ( - f"Expectd {total_expected_item_count} events, got {event_counts['run_item_stream_event']}" - f"Expected events were: {expected_item_type_map}, got {event_counts}" - ) - - assert ( - len(item_data) == total_expected_item_count - ), f"should have {total_expected_item_count} run items" - assert len(agent_data) == 2, "should have 2 agent updated events" - assert agent_data[0].new_agent == agent_2, "should have started with agent_2" - assert agent_data[1].new_agent == agent_1, "should have handed off to agent_1" diff --git a/pkg/hanzo-agent/tests/test_agent_tracing.py b/pkg/hanzo-agent/tests/test_agent_tracing.py deleted file mode 100644 index ad6dbde88..000000000 --- a/pkg/hanzo-agent/tests/test_agent_tracing.py +++ /dev/null @@ -1,326 +0,0 @@ -from __future__ import annotations - -import asyncio - -import pytest - -from agents import Agent, RunConfig, Runner, trace - -from .fake_model import FakeModel -from .test_responses import get_text_message -from .testing_processor import fetch_ordered_spans, fetch_traces - - -@pytest.mark.asyncio -async def test_single_run_is_single_trace(): - agent = Agent( - name="test_agent", - model=FakeModel( - initial_output=[get_text_message("first_test")], - ), - ) - - await Runner.run(agent, input="first_test") - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 1, ( - f"Got {len(spans)}, but expected 1: the agent span. data:" - f"{[span.span_data for span in spans]}" - ) - - -@pytest.mark.asyncio -async def test_multiple_runs_are_multiple_traces(): - model = FakeModel() - model.add_multiple_turn_outputs( - [ - [get_text_message("first_test")], - [get_text_message("second_test")], - ] - ) - agent = Agent( - name="test_agent_1", - model=model, - ) - - await Runner.run(agent, input="first_test") - await Runner.run(agent, input="second_test") - - traces = fetch_traces() - assert len(traces) == 2, f"Expected 2 traces, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 2, f"Got {len(spans)}, but expected 2: agent span per run" - - -@pytest.mark.asyncio -async def test_wrapped_trace_is_single_trace(): - model = FakeModel() - model.add_multiple_turn_outputs( - [ - [get_text_message("first_test")], - [get_text_message("second_test")], - [get_text_message("third_test")], - ] - ) - with trace(workflow_name="test_workflow"): - agent = Agent( - name="test_agent_1", - model=model, - ) - - await Runner.run(agent, input="first_test") - await Runner.run(agent, input="second_test") - await Runner.run(agent, input="third_test") - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 3, f"Got {len(spans)}, but expected 3: the agent span per run" - - -@pytest.mark.asyncio -async def test_parent_disabled_trace_disabled_agent_trace(): - with trace(workflow_name="test_workflow", disabled=True): - agent = Agent( - name="test_agent", - model=FakeModel( - initial_output=[get_text_message("first_test")], - ), - ) - - await Runner.run(agent, input="first_test") - - traces = fetch_traces() - assert len(traces) == 0, f"Expected 0 traces, got {len(traces)}" - spans = fetch_ordered_spans() - assert ( - len(spans) == 0 - ), f"Expected no spans, got {len(spans)}, with {[x.span_data for x in spans]}" - - -@pytest.mark.asyncio -async def test_manual_disabling_works(): - agent = Agent( - name="test_agent", - model=FakeModel( - initial_output=[get_text_message("first_test")], - ), - ) - - await Runner.run( - agent, input="first_test", run_config=RunConfig(tracing_disabled=True) - ) - - traces = fetch_traces() - assert len(traces) == 0, f"Expected 0 traces, got {len(traces)}" - spans = fetch_ordered_spans() - assert len(spans) == 0, f"Got {len(spans)}, but expected no spans" - - -@pytest.mark.asyncio -async def test_trace_config_works(): - agent = Agent( - name="test_agent", - model=FakeModel( - initial_output=[get_text_message("first_test")], - ), - ) - - await Runner.run( - agent, - input="first_test", - run_config=RunConfig(workflow_name="Foo bar", group_id="123", trace_id="456"), - ) - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - export = traces[0].export() - assert export is not None, "Trace export should not be None" - assert export["workflow_name"] == "Foo bar" - assert export["group_id"] == "123" - assert export["id"] == "456" - - -@pytest.mark.asyncio -async def test_not_starting_streaming_creates_trace(): - agent = Agent( - name="test_agent", - model=FakeModel( - initial_output=[get_text_message("first_test")], - ), - ) - - result = Runner.run_streamed(agent, input="first_test") - - # Purposely don't await the stream - while True: - if result.is_complete: - break - await asyncio.sleep(0.1) - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 1, f"Got {len(spans)}, but expected 1: the agent span" - - # Await the stream to avoid warnings about it not being awaited - async for _ in result.stream_events(): - pass - - -@pytest.mark.asyncio -async def test_streaming_single_run_is_single_trace(): - agent = Agent( - name="test_agent", - model=FakeModel( - initial_output=[get_text_message("first_test")], - ), - ) - - x = Runner.run_streamed(agent, input="first_test") - async for _ in x.stream_events(): - pass - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - -@pytest.mark.asyncio -async def test_multiple_streamed_runs_are_multiple_traces(): - model = FakeModel() - model.add_multiple_turn_outputs( - [ - [get_text_message("first_test")], - [get_text_message("second_test")], - ] - ) - agent = Agent( - name="test_agent_1", - model=model, - ) - - x = Runner.run_streamed(agent, input="first_test") - async for _ in x.stream_events(): - pass - - x = Runner.run_streamed(agent, input="second_test") - async for _ in x.stream_events(): - pass - - traces = fetch_traces() - assert len(traces) == 2, f"Expected 2 traces, got {len(traces)}" - - -@pytest.mark.asyncio -async def test_wrapped_streaming_trace_is_single_trace(): - model = FakeModel() - model.add_multiple_turn_outputs( - [ - [get_text_message("first_test")], - [get_text_message("second_test")], - [get_text_message("third_test")], - ] - ) - with trace(workflow_name="test_workflow"): - agent = Agent( - name="test_agent_1", - model=model, - ) - - x = Runner.run_streamed(agent, input="first_test") - async for _ in x.stream_events(): - pass - - x = Runner.run_streamed(agent, input="second_test") - async for _ in x.stream_events(): - pass - - x = Runner.run_streamed(agent, input="third_test") - async for _ in x.stream_events(): - pass - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - -@pytest.mark.asyncio -async def test_wrapped_mixed_trace_is_single_trace(): - model = FakeModel() - model.add_multiple_turn_outputs( - [ - [get_text_message("first_test")], - [get_text_message("second_test")], - [get_text_message("third_test")], - ] - ) - with trace(workflow_name="test_workflow"): - agent = Agent( - name="test_agent_1", - model=model, - ) - - x = Runner.run_streamed(agent, input="first_test") - async for _ in x.stream_events(): - pass - - await Runner.run(agent, input="second_test") - - x = Runner.run_streamed(agent, input="third_test") - async for _ in x.stream_events(): - pass - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - -@pytest.mark.asyncio -async def test_parent_disabled_trace_disables_streaming_agent_trace(): - model = FakeModel() - model.add_multiple_turn_outputs( - [ - [get_text_message("first_test")], - [get_text_message("second_test")], - ] - ) - with trace(workflow_name="test_workflow", disabled=True): - agent = Agent( - name="test_agent", - model=model, - ) - - x = Runner.run_streamed(agent, input="first_test") - async for _ in x.stream_events(): - pass - - traces = fetch_traces() - assert len(traces) == 0, f"Expected 0 traces, got {len(traces)}" - - -@pytest.mark.asyncio -async def test_manual_streaming_disabling_works(): - model = FakeModel() - model.add_multiple_turn_outputs( - [ - [get_text_message("first_test")], - [get_text_message("second_test")], - ] - ) - agent = Agent( - name="test_agent", - model=model, - ) - - x = Runner.run_streamed( - agent, input="first_test", run_config=RunConfig(tracing_disabled=True) - ) - async for _ in x.stream_events(): - pass - - traces = fetch_traces() - assert len(traces) == 0, f"Expected 0 traces, got {len(traces)}" diff --git a/pkg/hanzo-agent/tests/test_agentkit_features.py b/pkg/hanzo-agent/tests/test_agentkit_features.py deleted file mode 100644 index 1f898e11a..000000000 --- a/pkg/hanzo-agent/tests/test_agentkit_features.py +++ /dev/null @@ -1,544 +0,0 @@ -#!/usr/bin/env python3 -""" -Comprehensive tests for AgentKit-inspired features in Hanzo Agent SDK. -""" - -import asyncio -import json -import os -import sys -import time -from typing import Any, Dict, List - -# Add agent SDK to path -sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) - -import pytest -from agents import Agent, Runner -from agents.run import RunConfig -from agents.network import ( - create_network, - SemanticRouter, - RuleBasedRouter, - LoadBalancingRouter, -) -from agents.state import InMemoryStateStore, FileStateStore -from agents.memory import Memory, MemoryEntry, MemoryType -from agents.orchestration import Workflow, Step -from agents import function_tool as tool -from agents.exceptions import MaxTurnsExceeded - - -# Test fixtures -@pytest.fixture -def simple_agent(): - """Create a simple test agent.""" - return Agent( - name="TestAgent", - instructions="You are a helpful test assistant.", - model="gpt-3.5-turbo", - ) - - -@pytest.fixture -def math_agent(): - """Create a math-focused agent.""" - return Agent( - name="MathExpert", - instructions="You are a math expert. Answer math questions concisely.", - model="gpt-3.5-turbo", - ) - - -@pytest.fixture -def science_agent(): - """Create a science-focused agent.""" - return Agent( - name="ScienceExpert", - instructions="You are a science expert. Answer science questions concisely.", - model="gpt-3.5-turbo", - ) - - -# Test 1: Multi-Agent Networks -class TestMultiAgentNetworks: - """Test multi-agent network functionality.""" - - def test_create_network(self, math_agent, science_agent): - """Test creating a basic network.""" - network = create_network( - agents=[math_agent, science_agent], name="Test Network" - ) - - assert network.config.name == "Test Network" - assert len(network.nodes) == 2 - assert "MathExpert" in network.nodes - assert "ScienceExpert" in network.nodes - - def test_semantic_router(self, math_agent, science_agent): - """Test semantic routing between agents.""" - router = SemanticRouter() - network = create_network(agents=[math_agent, science_agent], router=router) - - assert isinstance(network.router, SemanticRouter) - - def test_rule_based_router(self, math_agent, science_agent): - """Test rule-based routing.""" - router = RuleBasedRouter() - - # Add routing rules - router.add_rule(r".*math.*|.*calculate.*|.*number.*", "MathExpert") - router.add_rule(r".*science.*|.*chemistry.*|.*physics.*", "ScienceExpert") - - network = create_network(agents=[math_agent, science_agent], router=router) - - assert isinstance(network.router, RuleBasedRouter) - assert len(router.rules) == 2 - - def test_load_balancing_router(self, math_agent, science_agent): - """Test load balancing router.""" - router = LoadBalancingRouter() - network = create_network(agents=[math_agent, science_agent], router=router) - - assert isinstance(network.router, LoadBalancingRouter) - - @pytest.mark.asyncio - async def test_network_agent_addition(self): - """Test adding agents to network dynamically.""" - network = create_network(agents=[]) - - agent1 = Agent(name="Agent1", instructions="First agent") - agent2 = Agent(name="Agent2", instructions="Second agent") - - node1 = network.add_agent(agent1, capabilities=["task1", "task2"]) - node2 = network.add_agent(agent2, capabilities=["task3", "task4"]) - - assert len(network.nodes) == 2 - assert node1.capabilities == ["task1", "task2"] - assert node2.capabilities == ["task3", "task4"] - - -# Test 2: State Management -class TestStateManagement: - """Test state management across agents.""" - - @pytest.mark.asyncio - async def test_in_memory_state_store(self): - """Test in-memory state store.""" - store = InMemoryStateStore() - - # Test basic operations - await store.set("key1", "value1") - assert await store.get("key1") == "value1" - - # Test namespaces - await store.set("key2", "value2", namespace="ns1") - assert await store.get("key2", namespace="ns1") == "value2" - assert await store.get("key2") is None # Different namespace - - # Test update - result = await store.update("counter", lambda x: (x or 0) + 1) - assert result == 1 - - result = await store.update("counter", lambda x: (x or 0) + 1) - assert result == 2 - - @pytest.mark.asyncio - async def test_file_state_store(self, tmp_path): - """Test file-based state store.""" - store_path = tmp_path / "state_store.json" - store = FileStateStore(str(store_path)) - - # Test persistence - await store.set("persistent_key", {"data": "test"}) - - # Create new store instance - store2 = FileStateStore(str(store_path)) - value = await store2.get("persistent_key") - assert value == {"data": "test"} - - @pytest.mark.asyncio - async def test_state_sharing_in_network(self, math_agent, science_agent): - """Test state sharing between agents in a network.""" - store = InMemoryStateStore() - network = create_network(agents=[math_agent, science_agent], state_store=store) - - # Set state from outside - await store.set("shared_data", {"experiment": "quantum"}) - - # Verify network has access to the store - assert network.state_store is store - data = await network.state_store.get("shared_data") - assert data == {"experiment": "quantum"} - - -# Test 3: Memory System -class TestMemorySystem: - """Test memory system with vector search.""" - - def test_memory_entry_creation(self): - """Test creating memory entries.""" - entry = MemoryEntry( - id="mem1", - type=MemoryType.FACT, - content="The Earth orbits the Sun", - metadata={"category": "astronomy"}, - importance=0.9, - ) - - assert entry.id == "mem1" - assert entry.type == MemoryType.FACT - assert entry.importance == 0.9 - - # Test serialization - data = entry.to_dict() - assert data["type"] == "fact" - assert data["content"] == "The Earth orbits the Sun" - - # Test deserialization - entry2 = MemoryEntry.from_dict(data) - assert entry2.id == entry.id - assert entry2.content == entry.content - - @pytest.mark.asyncio - async def test_memory_operations(self): - """Test memory storage and retrieval.""" - memory = Memory(max_entries=10) - - # Add memories - entry1 = await memory.add( - content="Python is a programming language", - type=MemoryType.FACT, - metadata={"topic": "programming"}, - ) - - entry2 = await memory.add( - content="The user asked about Python", - type=MemoryType.CONVERSATION, - metadata={"timestamp": time.time()}, - ) - - assert len(await memory.get_all()) == 2 - - # Test search - results = await memory.search("Python", limit=1) - assert len(results) == 1 - assert "Python" in results[0].content - - @pytest.mark.asyncio - async def test_memory_compression(self): - """Test memory compression when limit is reached.""" - memory = Memory(max_entries=3, auto_compress=True, compress_threshold=2) - - # Add memories beyond limit - for i in range(5): - await memory.add(content=f"Memory {i}", type=MemoryType.CONVERSATION) - - # Should compress old memories - all_memories = await memory.get_all() - assert len(all_memories) <= 3 - - def test_memory_types(self): - """Test different memory types.""" - types = [ - MemoryType.SHORT_TERM, - MemoryType.LONG_TERM, - MemoryType.WORKING, - MemoryType.EPISODIC, - MemoryType.SEMANTIC, - MemoryType.CONVERSATION, - MemoryType.FACT, - MemoryType.PROCEDURE, - MemoryType.REFLECTION, - ] - - for mem_type in types: - entry = MemoryEntry( - id=f"test_{mem_type.value}", - type=mem_type, - content=f"Test content for {mem_type.value}", - ) - assert entry.type == mem_type - - -# Test 4: Workflow Orchestration -class TestWorkflowOrchestration: - """Test workflow orchestration features.""" - - def test_workflow_creation(self): - """Test creating workflows.""" - workflow = Workflow(name="Test Workflow", description="A test workflow") - - # Add steps - step1 = workflow.add_step(Step.agent("Agent1", "Do task 1")) - step2 = workflow.add_step(Step.agent("Agent2", "Do task 2")) - - assert len(workflow.steps) == 2 - assert workflow.entry_point == step1.id - - def test_parallel_steps(self): - """Test parallel workflow steps.""" - workflow = Workflow(name="Parallel Test") - - # Create parallel steps - parallel_step = Step.parallel( - [ - Step.agent("Agent1", "Task 1"), - Step.agent("Agent2", "Task 2"), - Step.agent("Agent3", "Task 3"), - ] - ) - - workflow.add_step(parallel_step) - assert parallel_step.type.value == "parallel" - assert len(parallel_step.config["steps"]) == 3 - - def test_conditional_steps(self): - """Test conditional workflow steps.""" - workflow = Workflow(name="Conditional Test") - - # Create conditional step - def check_condition(data): - return data.get("value", 0) > 5 - - true_step = Step.agent("HighValueAgent", "Handle high value") - false_step = Step.agent("LowValueAgent", "Handle low value") - - conditional = Step.conditional( - condition=check_condition, if_true=true_step, if_false=false_step - ) - - workflow.add_step(conditional) - assert conditional.type.value == "conditional" - - def test_loop_steps(self): - """Test loop workflow steps.""" - workflow = Workflow(name="Loop Test") - - # Create loop step - body_step = Step.agent("ProcessAgent", "Process item") - - loop = Step.loop( - over="items", body=body_step, max_iterations=10 # Path to array in context - ) - - workflow.add_step(loop) - assert loop.type.value == "loop" - assert loop.config["max_iterations"] == 10 - - def test_workflow_validation(self): - """Test workflow validation.""" - workflow = Workflow(name="Validation Test") - - # Empty workflow should have validation errors - errors = workflow.validate() - assert len(errors) > 0 - assert "No entry point defined" in errors - - # Add steps and validate - workflow.add_step(Step.agent("Agent1", "Task")) - errors = workflow.validate() - assert len(errors) == 0 - - def test_workflow_execution_order(self): - """Test workflow execution order calculation.""" - workflow = Workflow(name="Order Test") - - step1 = workflow.add_step(Step.agent("Agent1", "Task 1")) - step2 = workflow.add_step(Step.agent("Agent2", "Task 2")) - step3 = workflow.add_step(Step.agent("Agent3", "Task 3")) - - # Add dependencies - step2.depends_on = [step1.id] - step3.depends_on = [step1.id] - - # Get execution order - order = workflow.get_execution_order() - - # Step 1 should be first, steps 2 and 3 can be parallel - assert len(order) == 2 - assert step1.id in order[0] - assert step2.id in order[1] and step3.id in order[1] - - def test_workflow_serialization(self): - """Test workflow serialization.""" - workflow = Workflow(name="Serialization Test") - workflow.add_step(Step.agent("TestAgent", "Test task")) - - # Convert to dict - data = workflow.to_dict() - assert data["name"] == "Serialization Test" - assert len(data["steps"]) == 1 - - # Convert to Mermaid diagram - mermaid = workflow.to_mermaid() - assert "graph TD" in mermaid - assert "TestAgent" in mermaid - - -# Test 5: Tool Integration -class TestToolIntegration: - """Test tool integration with agents.""" - - def test_tool_creation(self): - """Test creating tools for agents.""" - - @tool - def calculate_sum(a: int, b: int) -> int: - """Calculate the sum of two numbers. - - Args: - a: First number - b: Second number - - Returns: - The sum of a and b - """ - return a + b - - agent = Agent( - name="CalculatorAgent", - instructions="You can calculate sums.", - tools=[calculate_sum], - ) - - assert len(agent.tools) == 1 - assert agent.tools[0].name == "calculate_sum" - - @pytest.mark.asyncio - async def test_agent_with_tools(self): - """Test agent using tools.""" - - @tool - def get_current_time() -> str: - """Get the current time.""" - return time.strftime("%Y-%m-%d %H:%M:%S") - - agent = Agent( - name="TimeAgent", - instructions="You can tell the time.", - tools=[get_current_time], - model="gpt-3.5-turbo", - ) - - # This would require actual API call - # Just verify agent is configured correctly - assert agent.tools[0].name == "get_current_time" - - -# Test 6: Error Handling -class TestErrorHandling: - """Test error handling and edge cases.""" - - @pytest.mark.asyncio - async def test_max_turns_exceeded(self, simple_agent): - """Test max turns limit.""" - # This would require mocking to avoid actual API calls - # Just test the configuration - config = RunConfig(model="gpt-3.5-turbo") - assert config.model == "gpt-3.5-turbo" - - def test_invalid_workflow(self): - """Test invalid workflow configurations.""" - workflow = Workflow(name="Invalid Test") - - # Add circular dependency - step1 = workflow.add_step(Step.agent("Agent1", "Task 1")) - step2 = workflow.add_step(Step.agent("Agent2", "Task 2")) - - step1.depends_on = [step2.id] - step2.depends_on = [step1.id] - - # Should detect circular dependency - with pytest.raises(Exception): - workflow.get_execution_order() - - def test_network_with_no_agents(self): - """Test network with no agents.""" - network = create_network(agents=[]) - assert len(network.nodes) == 0 - - @pytest.mark.asyncio - async def test_state_store_error_handling(self): - """Test state store error handling.""" - store = InMemoryStateStore() - - # Test getting non-existent key - value = await store.get("non_existent") - assert value is None - - # Test update on non-existent key - result = await store.update("new_key", lambda x: (x or 0) + 1) - assert result == 1 - - -# Test 7: Integration Tests -class TestIntegration: - """Integration tests combining multiple features.""" - - @pytest.mark.asyncio - async def test_network_with_state_and_memory(self, math_agent, science_agent): - """Test network with both state and memory.""" - # Create shared state - state_store = InMemoryStateStore() - await state_store.set("experiment_count", 0) - - # Create network - network = create_network( - agents=[math_agent, science_agent], state_store=state_store - ) - - # Create memory for each agent - math_memory = Memory() - science_memory = Memory() - - # Add some memories - await math_memory.add( - content="User prefers simple explanations", type=MemoryType.FACT - ) - - await science_memory.add( - content="User is interested in physics", type=MemoryType.FACT - ) - - # Verify integration - assert network.state_store is state_store - assert len(await math_memory.get_all()) == 1 - assert len(await science_memory.get_all()) == 1 - - def test_workflow_with_network(self, math_agent, science_agent): - """Test workflow using network agents.""" - # Create network - network = create_network(agents=[math_agent, science_agent]) - - # Create workflow - workflow = Workflow(name="Math and Science Workflow") - - # Add steps using network agents - math_step = workflow.add_step(Step.agent("MathExpert", "Calculate 2+2")) - - science_step = workflow.add_step( - Step.agent("ScienceExpert", "Explain photosynthesis") - ) - - # Make science step depend on math step - science_step.depends_on = [math_step.id] - - # Validate workflow - errors = workflow.validate() - assert len(errors) == 0 - - # Check execution order - order = workflow.get_execution_order() - assert len(order) == 2 - assert math_step.id in order[0] - assert science_step.id in order[1] - - -# Test runner -if __name__ == "__main__": - print("๐Ÿงช Running Hanzo Agent SDK Tests") - print("=" * 60) - - # Run tests with pytest - pytest.main([__file__, "-v", "-s"]) diff --git a/pkg/hanzo-agent/tests/test_computer_action.py b/pkg/hanzo-agent/tests/test_computer_action.py deleted file mode 100644 index 02a575885..000000000 --- a/pkg/hanzo-agent/tests/test_computer_action.py +++ /dev/null @@ -1,330 +0,0 @@ -"""Unit tests for the ComputerAction methods in `agents._run_impl`. - -These confirm that the correct computer action method is invoked for each action type and -that screenshots are taken and wrapped appropriately, and that the execute function invokes -hooks and returns the expected ToolCallOutputItem.""" - -from typing import Any - -import pytest -from openai.types.responses.response_computer_tool_call import ( - ActionClick, - ActionDoubleClick, - ActionDrag, - ActionDragPath, - ActionKeypress, - ActionMove, - ActionScreenshot, - ActionScroll, - ActionType, - ActionWait, - ResponseComputerToolCall, -) - -from agents import ( - Agent, - AgentHooks, - AsyncComputer, - Computer, - ComputerTool, - RunConfig, - RunContextWrapper, - RunHooks, -) -from agents._run_impl import ComputerAction, ToolRunComputerAction -from agents.items import ToolCallOutputItem - - -class LoggingComputer(Computer): - """A `Computer` implementation that logs calls to its methods for verification in tests.""" - - def __init__(self, screenshot_return: str = "screenshot"): - self.calls: list[tuple[str, tuple[Any, ...]]] = [] - self._screenshot_return = screenshot_return - - @property - def environment(self): - return "mac" - - @property - def dimensions(self) -> tuple[int, int]: - return (800, 600) - - def screenshot(self) -> str: - self.calls.append(("screenshot", ())) - return self._screenshot_return - - def click(self, x: int, y: int, button: str) -> None: - self.calls.append(("click", (x, y, button))) - - def double_click(self, x: int, y: int) -> None: - self.calls.append(("double_click", (x, y))) - - def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: - self.calls.append(("scroll", (x, y, scroll_x, scroll_y))) - - def type(self, text: str) -> None: - self.calls.append(("type", (text,))) - - def wait(self) -> None: - self.calls.append(("wait", ())) - - def move(self, x: int, y: int) -> None: - self.calls.append(("move", (x, y))) - - def keypress(self, keys: list[str]) -> None: - self.calls.append(("keypress", (keys,))) - - def drag(self, path: list[tuple[int, int]]) -> None: - self.calls.append(("drag", (tuple(path),))) - - -class LoggingAsyncComputer(AsyncComputer): - """An `AsyncComputer` implementation that logs calls to its methods for verification.""" - - def __init__(self, screenshot_return: str = "async_screenshot"): - self.calls: list[tuple[str, tuple[Any, ...]]] = [] - self._screenshot_return = screenshot_return - - @property - def environment(self): - return "mac" - - @property - def dimensions(self) -> tuple[int, int]: - return (800, 600) - - async def screenshot(self) -> str: - self.calls.append(("screenshot", ())) - return self._screenshot_return - - async def click(self, x: int, y: int, button: str) -> None: - self.calls.append(("click", (x, y, button))) - - async def double_click(self, x: int, y: int) -> None: - self.calls.append(("double_click", (x, y))) - - async def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: - self.calls.append(("scroll", (x, y, scroll_x, scroll_y))) - - async def type(self, text: str) -> None: - self.calls.append(("type", (text,))) - - async def wait(self) -> None: - self.calls.append(("wait", ())) - - async def move(self, x: int, y: int) -> None: - self.calls.append(("move", (x, y))) - - async def keypress(self, keys: list[str]) -> None: - self.calls.append(("keypress", (keys,))) - - async def drag(self, path: list[tuple[int, int]]) -> None: - self.calls.append(("drag", (tuple(path),))) - - -@pytest.mark.asyncio -@pytest.mark.parametrize( - "action,expected_call", - [ - ( - ActionClick(type="click", x=10, y=21, button="left"), - ("click", (10, 21, "left")), - ), - ( - ActionDoubleClick(type="double_click", x=42, y=47), - ("double_click", (42, 47)), - ), - ( - ActionDrag( - type="drag", path=[ActionDragPath(x=1, y=2), ActionDragPath(x=3, y=4)] - ), - ("drag", (((1, 2), (3, 4)),)), - ), - (ActionKeypress(type="keypress", keys=["a", "b"]), ("keypress", (["a", "b"],))), - (ActionMove(type="move", x=100, y=200), ("move", (100, 200))), - (ActionScreenshot(type="screenshot"), ("screenshot", ())), - ( - ActionScroll(type="scroll", x=1, y=2, scroll_x=3, scroll_y=4), - ("scroll", (1, 2, 3, 4)), - ), - (ActionType(type="type", text="hello"), ("type", ("hello",))), - (ActionWait(type="wait"), ("wait", ())), - ], -) -async def test_get_screenshot_sync_executes_action_and_takes_screenshot( - action: Any, expected_call: tuple[str, tuple[Any, ...]] -) -> None: - """For each action type, assert that the corresponding computer method is invoked - and that a screenshot is taken and returned.""" - computer = LoggingComputer(screenshot_return="synthetic") - tool_call = ResponseComputerToolCall( - id="c1", - type="computer_call", - action=action, - call_id="c1", - pending_safety_checks=[], - status="completed", - ) - screenshot_output = await ComputerAction._get_screenshot_sync(computer, tool_call) - # The last call is always to screenshot() - if isinstance(action, ActionScreenshot): - # Screenshot is taken twice: initial explicit call plus final capture. - assert computer.calls == [("screenshot", ()), ("screenshot", ())] - else: - assert computer.calls == [expected_call, ("screenshot", ())] - assert screenshot_output == "synthetic" - - -@pytest.mark.asyncio -@pytest.mark.parametrize( - "action,expected_call", - [ - ( - ActionClick(type="click", x=2, y=3, button="right"), - ("click", (2, 3, "right")), - ), - ( - ActionDoubleClick(type="double_click", x=12, y=13), - ("double_click", (12, 13)), - ), - ( - ActionDrag( - type="drag", path=[ActionDragPath(x=5, y=6), ActionDragPath(x=6, y=7)] - ), - ("drag", (((5, 6), (6, 7)),)), - ), - ( - ActionKeypress(type="keypress", keys=["ctrl", "c"]), - ("keypress", (["ctrl", "c"],)), - ), - (ActionMove(type="move", x=8, y=9), ("move", (8, 9))), - (ActionScreenshot(type="screenshot"), ("screenshot", ())), - ( - ActionScroll(type="scroll", x=9, y=8, scroll_x=7, scroll_y=6), - ("scroll", (9, 8, 7, 6)), - ), - (ActionType(type="type", text="world"), ("type", ("world",))), - (ActionWait(type="wait"), ("wait", ())), - ], -) -async def test_get_screenshot_async_executes_action_and_takes_screenshot( - action: Any, expected_call: tuple[str, tuple[Any, ...]] -) -> None: - """For each action type on an `AsyncComputer`, the corresponding coroutine should be awaited - and a screenshot taken.""" - computer = LoggingAsyncComputer(screenshot_return="async_return") - assert computer.environment == "mac" - assert computer.dimensions == (800, 600) - tool_call = ResponseComputerToolCall( - id="c2", - type="computer_call", - action=action, - call_id="c2", - pending_safety_checks=[], - status="completed", - ) - screenshot_output = await ComputerAction._get_screenshot_async(computer, tool_call) - if isinstance(action, ActionScreenshot): - assert computer.calls == [("screenshot", ()), ("screenshot", ())] - else: - assert computer.calls == [expected_call, ("screenshot", ())] - assert screenshot_output == "async_return" - - -class LoggingRunHooks(RunHooks[Any]): - """Capture on_tool_start and on_tool_end invocations.""" - - def __init__(self) -> None: - super().__init__() - self.started: list[tuple[Agent[Any], Any]] = [] - self.ended: list[tuple[Agent[Any], Any, str]] = [] - - async def on_tool_start( - self, context: RunContextWrapper[Any], agent: Agent[Any], tool: Any - ) -> None: - self.started.append((agent, tool)) - - async def on_tool_end( - self, context: RunContextWrapper[Any], agent: Agent[Any], tool: Any, result: str - ) -> None: - self.ended.append((agent, tool, result)) - - -class LoggingAgentHooks(AgentHooks[Any]): - """Minimal override to capture agent's tool hook invocations.""" - - def __init__(self) -> None: - super().__init__() - self.started: list[tuple[Agent[Any], Any]] = [] - self.ended: list[tuple[Agent[Any], Any, str]] = [] - - async def on_tool_start( - self, context: RunContextWrapper[Any], agent: Agent[Any], tool: Any - ) -> None: - self.started.append((agent, tool)) - - async def on_tool_end( - self, context: RunContextWrapper[Any], agent: Agent[Any], tool: Any, result: str - ) -> None: - self.ended.append((agent, tool, result)) - - -@pytest.mark.asyncio -async def test_execute_invokes_hooks_and_returns_tool_call_output() -> None: - # ComputerAction.execute should invoke lifecycle hooks and return a proper ToolCallOutputItem. - computer = LoggingComputer(screenshot_return="xyz") - comptool = ComputerTool(computer=computer) - # Create a dummy click action to trigger a click and screenshot. - action = ActionClick(type="click", x=1, y=2, button="left") - tool_call = ResponseComputerToolCall( - id="tool123", - type="computer_call", - action=action, - call_id="tool123", - pending_safety_checks=[], - status="completed", - ) - tool_call.call_id = "tool123" - - # Wrap tool call in ToolRunComputerAction - tool_run = ToolRunComputerAction(tool_call=tool_call, computer_tool=comptool) - # Setup agent and hooks. - agent = Agent(name="test_agent", tools=[comptool]) - # Attach per-agent hooks as well as global run hooks. - agent_hooks = LoggingAgentHooks() - agent.hooks = agent_hooks - run_hooks = LoggingRunHooks() - context_wrapper: RunContextWrapper[Any] = RunContextWrapper(context=None) - # Execute the computer action. - output_item = await ComputerAction.execute( - agent=agent, - action=tool_run, - hooks=run_hooks, - context_wrapper=context_wrapper, - config=RunConfig(), - ) - # Both global and per-agent hooks should have been called once. - assert len(run_hooks.started) == 1 and len(agent_hooks.started) == 1 - assert len(run_hooks.ended) == 1 and len(agent_hooks.ended) == 1 - # The hook invocations should refer to our agent and tool. - assert run_hooks.started[0][0] is agent - assert run_hooks.ended[0][0] is agent - assert run_hooks.started[0][1] is comptool - assert run_hooks.ended[0][1] is comptool - # The result passed to on_tool_end should be the raw screenshot string. - assert run_hooks.ended[0][2] == "xyz" - assert agent_hooks.ended[0][2] == "xyz" - # The computer should have performed a click then a screenshot. - assert computer.calls == [("click", (1, 2, "left")), ("screenshot", ())] - # The returned item should include the agent, output string, and a ComputerCallOutput. - assert output_item.agent is agent - assert isinstance(output_item, ToolCallOutputItem) - assert output_item.output == "data:image/png;base64,xyz" - raw = output_item.raw_item - # Raw item is a dict-like mapping with expected output fields. - assert isinstance(raw, dict) - assert raw["type"] == "computer_call_output" - assert raw["output"]["type"] == "computer_screenshot" - assert "image_url" in raw["output"] - assert raw["output"]["image_url"].endswith("xyz") diff --git a/pkg/hanzo-agent/tests/test_config.py b/pkg/hanzo-agent/tests/test_config.py deleted file mode 100644 index 407a46fee..000000000 --- a/pkg/hanzo-agent/tests/test_config.py +++ /dev/null @@ -1,68 +0,0 @@ -import os - -import openai -import pytest - -from agents import ( - set_default_openai_api, - set_default_openai_client, - set_default_openai_key, -) -from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel -from agents.models.openai_provider import OpenAIProvider -from agents.models.openai_responses import OpenAIResponsesModel - - -def test_cc_no_default_key_errors(monkeypatch): - monkeypatch.delenv("OPENAI_API_KEY", raising=False) - with pytest.raises(openai.OpenAIError): - OpenAIProvider(use_responses=False).get_model("gpt-4") - - -def test_cc_set_default_openai_key(): - set_default_openai_key("test_key") - chat_model = OpenAIProvider(use_responses=False).get_model("gpt-4") - assert chat_model._client.api_key == "test_key" # type: ignore - - -def test_cc_set_default_openai_client(): - client = openai.AsyncOpenAI(api_key="test_key") - set_default_openai_client(client) - chat_model = OpenAIProvider(use_responses=False).get_model("gpt-4") - assert chat_model._client.api_key == "test_key" # type: ignore - - -def test_resp_no_default_key_errors(monkeypatch): - monkeypatch.delenv("OPENAI_API_KEY", raising=False) - assert os.getenv("OPENAI_API_KEY") is None - with pytest.raises(openai.OpenAIError): - OpenAIProvider(use_responses=True).get_model("gpt-4") - - -def test_resp_set_default_openai_key(): - set_default_openai_key("test_key") - resp_model = OpenAIProvider(use_responses=True).get_model("gpt-4") - assert resp_model._client.api_key == "test_key" # type: ignore - - -def test_resp_set_default_openai_client(): - client = openai.AsyncOpenAI(api_key="test_key") - set_default_openai_client(client) - resp_model = OpenAIProvider(use_responses=True).get_model("gpt-4") - assert resp_model._client.api_key == "test_key" # type: ignore - - -def test_set_default_openai_api(): - assert isinstance( - OpenAIProvider().get_model("gpt-4"), OpenAIResponsesModel - ), "Default should be responses" - - set_default_openai_api("chat_completions") - assert isinstance( - OpenAIProvider().get_model("gpt-4"), OpenAIChatCompletionsModel - ), "Should be chat completions model" - - set_default_openai_api("responses") - assert isinstance( - OpenAIProvider().get_model("gpt-4"), OpenAIResponsesModel - ), "Should be responses model" diff --git a/pkg/hanzo-agent/tests/test_doc_parsing.py b/pkg/hanzo-agent/tests/test_doc_parsing.py deleted file mode 100644 index ae6c74105..000000000 --- a/pkg/hanzo-agent/tests/test_doc_parsing.py +++ /dev/null @@ -1,130 +0,0 @@ -from agents.function_schema import generate_func_documentation - - -def func_foo_google(a: int, b: float) -> str: - """ - This is func_foo. - - Args: - a: The first argument. - b: The second argument. - - Returns: - A result - """ - - return "ok" - - -def func_foo_numpy(a: int, b: float) -> str: - """ - This is func_foo. - - Parameters - ---------- - a: int - The first argument. - b: float - The second argument. - - Returns - ------- - str - A result - """ - return "ok" - - -def func_foo_sphinx(a: int, b: float) -> str: - """ - This is func_foo. - - :param a: The first argument. - :param b: The second argument. - :return: A result - """ - return "ok" - - -class Bar: - def func_bar(self, a: int, b: float) -> str: - """ - This is func_bar. - - Args: - a: The first argument. - b: The second argument. - - Returns: - A result - """ - return "ok" - - @classmethod - def func_baz(cls, a: int, b: float) -> str: - """ - This is func_baz. - - Args: - a: The first argument. - b: The second argument. - - Returns: - A result - """ - return "ok" - - -def test_functions_are_ok(): - func_foo_google(1, 2.0) - func_foo_numpy(1, 2.0) - func_foo_sphinx(1, 2.0) - Bar().func_bar(1, 2.0) - Bar.func_baz(1, 2.0) - - -def test_auto_detection() -> None: - doc = generate_func_documentation(func_foo_google) - assert doc.name == "func_foo_google" - assert doc.description == "This is func_foo." - assert doc.param_descriptions == { - "a": "The first argument.", - "b": "The second argument.", - } - - doc = generate_func_documentation(func_foo_numpy) - assert doc.name == "func_foo_numpy" - assert doc.description == "This is func_foo." - assert doc.param_descriptions == { - "a": "The first argument.", - "b": "The second argument.", - } - - doc = generate_func_documentation(func_foo_sphinx) - assert doc.name == "func_foo_sphinx" - assert doc.description == "This is func_foo." - assert doc.param_descriptions == { - "a": "The first argument.", - "b": "The second argument.", - } - - -def test_instance_method() -> None: - bar = Bar() - doc = generate_func_documentation(bar.func_bar) - assert doc.name == "func_bar" - assert doc.description == "This is func_bar." - assert doc.param_descriptions == { - "a": "The first argument.", - "b": "The second argument.", - } - - -def test_classmethod() -> None: - doc = generate_func_documentation(Bar.func_baz) - assert doc.name == "func_baz" - assert doc.description == "This is func_baz." - assert doc.param_descriptions == { - "a": "The first argument.", - "b": "The second argument.", - } diff --git a/pkg/hanzo-agent/tests/test_extension_filters.py b/pkg/hanzo-agent/tests/test_extension_filters.py deleted file mode 100644 index 3def256c0..000000000 --- a/pkg/hanzo-agent/tests/test_extension_filters.py +++ /dev/null @@ -1,194 +0,0 @@ -from openai.types.responses import ResponseOutputMessage, ResponseOutputText - -from agents import Agent, HandoffInputData -from agents.extensions.handoff_filters import remove_all_tools -from agents.items import ( - HandoffOutputItem, - MessageOutputItem, - ToolCallOutputItem, - TResponseInputItem, -) - - -def fake_agent(): - return Agent( - name="fake_agent", - ) - - -def _get_message_input_item(content: str) -> TResponseInputItem: - return { - "role": "assistant", - "content": content, - } - - -def _get_function_result_input_item(content: str) -> TResponseInputItem: - return { - "call_id": "1", - "output": content, - "type": "function_call_output", - } - - -def _get_message_output_run_item(content: str) -> MessageOutputItem: - return MessageOutputItem( - agent=fake_agent(), - raw_item=ResponseOutputMessage( - id="1", - content=[ - ResponseOutputText(text=content, annotations=[], type="output_text") - ], - role="assistant", - status="completed", - type="message", - ), - ) - - -def _get_tool_output_run_item(content: str) -> ToolCallOutputItem: - return ToolCallOutputItem( - agent=fake_agent(), - raw_item={ - "call_id": "1", - "output": content, - "type": "function_call_output", - }, - output=content, - ) - - -def _get_handoff_input_item(content: str) -> TResponseInputItem: - return { - "call_id": "1", - "output": content, - "type": "function_call_output", - } - - -def _get_handoff_output_run_item(content: str) -> HandoffOutputItem: - return HandoffOutputItem( - agent=fake_agent(), - raw_item={ - "call_id": "1", - "output": content, - "type": "function_call_output", - }, - source_agent=fake_agent(), - target_agent=fake_agent(), - ) - - -def test_empty_data(): - handoff_input_data = HandoffInputData( - input_history=(), pre_handoff_items=(), new_items=() - ) - filtered_data = remove_all_tools(handoff_input_data) - assert filtered_data == handoff_input_data - - -def test_str_historyonly(): - handoff_input_data = HandoffInputData( - input_history="Hello", pre_handoff_items=(), new_items=() - ) - filtered_data = remove_all_tools(handoff_input_data) - assert filtered_data == handoff_input_data - - -def test_str_history_and_list(): - handoff_input_data = HandoffInputData( - input_history="Hello", - pre_handoff_items=(), - new_items=(_get_message_output_run_item("Hello"),), - ) - filtered_data = remove_all_tools(handoff_input_data) - assert filtered_data == handoff_input_data - - -def test_list_history_and_list(): - handoff_input_data = HandoffInputData( - input_history=(_get_message_input_item("Hello"),), - pre_handoff_items=(_get_message_output_run_item("123"),), - new_items=(_get_message_output_run_item("World"),), - ) - filtered_data = remove_all_tools(handoff_input_data) - assert filtered_data == handoff_input_data - - -def test_removes_tools_from_history(): - handoff_input_data = HandoffInputData( - input_history=( - _get_message_input_item("Hello1"), - _get_function_result_input_item("World"), - _get_message_input_item("Hello2"), - ), - pre_handoff_items=( - _get_tool_output_run_item("abc"), - _get_message_output_run_item("123"), - ), - new_items=(_get_message_output_run_item("World"),), - ) - filtered_data = remove_all_tools(handoff_input_data) - assert len(filtered_data.input_history) == 2 - assert len(filtered_data.pre_handoff_items) == 1 - assert len(filtered_data.new_items) == 1 - - -def test_removes_tools_from_new_items(): - handoff_input_data = HandoffInputData( - input_history=(), - pre_handoff_items=(), - new_items=( - _get_message_output_run_item("Hello"), - _get_tool_output_run_item("World"), - ), - ) - filtered_data = remove_all_tools(handoff_input_data) - assert len(filtered_data.input_history) == 0 - assert len(filtered_data.pre_handoff_items) == 0 - assert len(filtered_data.new_items) == 1 - - -def test_removes_tools_from_new_items_and_history(): - handoff_input_data = HandoffInputData( - input_history=( - _get_message_input_item("Hello1"), - _get_function_result_input_item("World"), - _get_message_input_item("Hello2"), - ), - pre_handoff_items=( - _get_message_output_run_item("123"), - _get_tool_output_run_item("456"), - ), - new_items=( - _get_message_output_run_item("Hello"), - _get_tool_output_run_item("World"), - ), - ) - filtered_data = remove_all_tools(handoff_input_data) - assert len(filtered_data.input_history) == 2 - assert len(filtered_data.pre_handoff_items) == 1 - assert len(filtered_data.new_items) == 1 - - -def test_removes_handoffs_from_history(): - handoff_input_data = HandoffInputData( - input_history=( - _get_message_input_item("Hello1"), - _get_handoff_input_item("World"), - ), - pre_handoff_items=( - _get_message_output_run_item("Hello"), - _get_tool_output_run_item("World"), - _get_handoff_output_run_item("World"), - ), - new_items=( - _get_message_output_run_item("Hello"), - _get_tool_output_run_item("World"), - _get_handoff_output_run_item("World"), - ), - ) - filtered_data = remove_all_tools(handoff_input_data) - assert len(filtered_data.input_history) == 1 - assert len(filtered_data.pre_handoff_items) == 1 - assert len(filtered_data.new_items) == 1 diff --git a/pkg/hanzo-agent/tests/test_function_schema.py b/pkg/hanzo-agent/tests/test_function_schema.py deleted file mode 100644 index a8eeb15f2..000000000 --- a/pkg/hanzo-agent/tests/test_function_schema.py +++ /dev/null @@ -1,449 +0,0 @@ -from enum import Enum -from typing import Any, Literal - -import pytest -from pydantic import BaseModel, ValidationError -from typing_extensions import TypedDict - -from agents import RunContextWrapper -from agents.exceptions import UserError -from agents.function_schema import function_schema - - -def no_args_function(): - """This function has no args.""" - - return "ok" - - -def test_no_args_function(): - func_schema = function_schema(no_args_function) - assert func_schema.params_json_schema.get("title") == "no_args_function_args" - assert func_schema.description == "This function has no args." - assert not func_schema.takes_context - - parsed = func_schema.params_pydantic_model() - args, kwargs_dict = func_schema.to_call_args(parsed) - result = no_args_function(*args, **kwargs_dict) - assert result == "ok" - - -def no_args_function_with_context(ctx: RunContextWrapper[str]): - return "ok" - - -def test_no_args_function_with_context() -> None: - func_schema = function_schema(no_args_function_with_context) - assert func_schema.takes_context - - context = RunContextWrapper(context="test") - parsed = func_schema.params_pydantic_model() - args, kwargs_dict = func_schema.to_call_args(parsed) - result = no_args_function_with_context(context, *args, **kwargs_dict) - assert result == "ok" - - -def simple_function(a: int, b: int = 5): - """ - Args: - a: The first argument - b: The second argument - - Returns: - The sum of a and b - """ - return a + b - - -def test_simple_function(): - """Test a function that has simple typed parameters and defaults.""" - - func_schema = function_schema(simple_function) - # Check that the JSON schema is a dictionary with title, type, etc. - assert isinstance(func_schema.params_json_schema, dict) - assert func_schema.params_json_schema.get("title") == "simple_function_args" - assert ( - func_schema.params_json_schema.get("properties", {}).get("a").get("description") - == "The first argument" - ) - assert ( - func_schema.params_json_schema.get("properties", {}).get("b").get("description") - == "The second argument" - ) - assert not func_schema.takes_context - - # Valid input - valid_input = {"a": 3} - parsed = func_schema.params_pydantic_model(**valid_input) - args_tuple, kwargs_dict = func_schema.to_call_args(parsed) - result = simple_function(*args_tuple, **kwargs_dict) - assert result == 8 # 3 + 5 - - # Another valid input - valid_input2 = {"a": 3, "b": 10} - parsed2 = func_schema.params_pydantic_model(**valid_input2) - args_tuple2, kwargs_dict2 = func_schema.to_call_args(parsed2) - result2 = simple_function(*args_tuple2, **kwargs_dict2) - assert result2 == 13 # 3 + 10 - - # Invalid input: 'a' must be int - with pytest.raises(ValidationError): - func_schema.params_pydantic_model(**{"a": "not an integer"}) - - -def varargs_function(x: int, *numbers: float, flag: bool = False, **kwargs: Any): - return x, numbers, flag, kwargs - - -def test_varargs_function(): - """Test a function that uses *args and **kwargs.""" - - func_schema = function_schema(varargs_function) - # Check JSON schema structure - assert isinstance(func_schema.params_json_schema, dict) - assert func_schema.params_json_schema.get("title") == "varargs_function_args" - - # Valid input including *args in 'numbers' and **kwargs in 'kwargs' - valid_input = { - "x": 10, - "numbers": [1.1, 2.2, 3.3], - "flag": True, - "kwargs": {"extra1": "hello", "extra2": 42}, - } - parsed = func_schema.params_pydantic_model(**valid_input) - args, kwargs_dict = func_schema.to_call_args(parsed) - - result = varargs_function(*args, **kwargs_dict) - # result should be (10, (1.1, 2.2, 3.3), True, {"extra1": "hello", "extra2": 42}) - assert result[0] == 10 - assert result[1] == (1.1, 2.2, 3.3) - assert result[2] is True - assert result[3] == {"extra1": "hello", "extra2": 42} - - # Missing 'x' should raise error - with pytest.raises(ValidationError): - func_schema.params_pydantic_model(**{"numbers": [1.1, 2.2]}) - - # 'flag' can be omitted because it has a default - valid_input_no_flag = { - "x": 7, - "numbers": [9.9], - "kwargs": {"some_key": "some_value"}, - } - parsed2 = func_schema.params_pydantic_model(**valid_input_no_flag) - args2, kwargs_dict2 = func_schema.to_call_args(parsed2) - result2 = varargs_function(*args2, **kwargs_dict2) - # result2 should be (7, (9.9,), False, {'some_key': 'some_value'}) - assert result2 == (7, (9.9,), False, {"some_key": "some_value"}) - - -class Foo(TypedDict): - a: int - b: str - - -class InnerModel(BaseModel): - a: int - b: str - - -class OuterModel(BaseModel): - inner: InnerModel - foo: Foo - - -def complex_args_function(model: OuterModel) -> str: - return f"{model.inner.a}, {model.inner.b}, {model.foo['a']}, {model.foo['b']}" - - -def test_nested_data_function(): - func_schema = function_schema(complex_args_function) - assert isinstance(func_schema.params_json_schema, dict) - assert func_schema.params_json_schema.get("title") == "complex_args_function_args" - - # Valid input - model = OuterModel(inner=InnerModel(a=1, b="hello"), foo=Foo(a=2, b="world")) - valid_input = { - "model": model.model_dump(), - } - - parsed = func_schema.params_pydantic_model(**valid_input) - args, kwargs_dict = func_schema.to_call_args(parsed) - - result = complex_args_function(*args, **kwargs_dict) - assert result == "1, hello, 2, world" - - -def complex_args_and_docs_function(model: OuterModel, some_flag: int = 0) -> str: - """ - This function takes a model and a flag, and returns a string. - - Args: - model: A model with an inner and foo field - some_flag: An optional flag with a default of 0 - - Returns: - A string with the values of the model and flag - """ - return f"{model.inner.a}, {model.inner.b}, {model.foo['a']}, {model.foo['b']}, {some_flag or 0}" - - -def test_complex_args_and_docs_function(): - func_schema = function_schema(complex_args_and_docs_function) - - assert isinstance(func_schema.params_json_schema, dict) - assert ( - func_schema.params_json_schema.get("title") - == "complex_args_and_docs_function_args" - ) - - # Check docstring is parsed correctly - properties = func_schema.params_json_schema.get("properties", {}) - assert ( - properties.get("model").get("description") - == "A model with an inner and foo field" - ) - assert ( - properties.get("some_flag").get("description") - == "An optional flag with a default of 0" - ) - - # Valid input - model = OuterModel(inner=InnerModel(a=1, b="hello"), foo=Foo(a=2, b="world")) - valid_input = { - "model": model.model_dump(), - } - - parsed = func_schema.params_pydantic_model(**valid_input) - args, kwargs_dict = func_schema.to_call_args(parsed) - - result = complex_args_and_docs_function(*args, **kwargs_dict) - assert result == "1, hello, 2, world, 0" - - # Invalid input: 'some_flag' must be int - with pytest.raises(ValidationError): - func_schema.params_pydantic_model( - **{"model": model.model_dump(), "some_flag": "not an int"} - ) - - # Valid input: 'some_flag' can be omitted because it has a default - valid_input_no_flag = {"model": model.model_dump()} - parsed2 = func_schema.params_pydantic_model(**valid_input_no_flag) - args2, kwargs_dict2 = func_schema.to_call_args(parsed2) - result2 = complex_args_and_docs_function(*args2, **kwargs_dict2) - assert result2 == "1, hello, 2, world, 0" - - -def function_with_context(ctx: RunContextWrapper[str], a: int, b: int = 5): - return a + b - - -def test_function_with_context(): - func_schema = function_schema(function_with_context) - assert func_schema.takes_context - - context = RunContextWrapper(context="test") - - input = {"a": 1, "b": 2} - parsed = func_schema.params_pydantic_model(**input) - args, kwargs_dict = func_schema.to_call_args(parsed) - - result = function_with_context(context, *args, **kwargs_dict) - assert result == 3 - - -class MyClass: - def foo(self, a: int, b: int = 5): - return a + b - - def foo_ctx(self, ctx: RunContextWrapper[str], a: int, b: int = 5): - return a + b - - @classmethod - def bar(cls, a: int, b: int = 5): - return a + b - - @classmethod - def bar_ctx(cls, ctx: RunContextWrapper[str], a: int, b: int = 5): - return a + b - - @staticmethod - def baz(a: int, b: int = 5): - return a + b - - @staticmethod - def baz_ctx(ctx: RunContextWrapper[str], a: int, b: int = 5): - return a + b - - -def test_class_based_functions(): - context = RunContextWrapper(context="test") - - # Instance method - instance = MyClass() - func_schema = function_schema(instance.foo) - assert isinstance(func_schema.params_json_schema, dict) - assert func_schema.params_json_schema.get("title") == "foo_args" - - input = {"a": 1, "b": 2} - parsed = func_schema.params_pydantic_model(**input) - args, kwargs_dict = func_schema.to_call_args(parsed) - result = instance.foo(*args, **kwargs_dict) - assert result == 3 - - # Instance method with context - func_schema = function_schema(instance.foo_ctx) - assert isinstance(func_schema.params_json_schema, dict) - assert func_schema.params_json_schema.get("title") == "foo_ctx_args" - assert func_schema.takes_context - - input = {"a": 1, "b": 2} - parsed = func_schema.params_pydantic_model(**input) - args, kwargs_dict = func_schema.to_call_args(parsed) - result = instance.foo_ctx(context, *args, **kwargs_dict) - assert result == 3 - - # Class method - func_schema = function_schema(MyClass.bar) - assert isinstance(func_schema.params_json_schema, dict) - assert func_schema.params_json_schema.get("title") == "bar_args" - - input = {"a": 1, "b": 2} - parsed = func_schema.params_pydantic_model(**input) - args, kwargs_dict = func_schema.to_call_args(parsed) - result = MyClass.bar(*args, **kwargs_dict) - assert result == 3 - - # Class method with context - func_schema = function_schema(MyClass.bar_ctx) - assert isinstance(func_schema.params_json_schema, dict) - assert func_schema.params_json_schema.get("title") == "bar_ctx_args" - assert func_schema.takes_context - - input = {"a": 1, "b": 2} - parsed = func_schema.params_pydantic_model(**input) - args, kwargs_dict = func_schema.to_call_args(parsed) - result = MyClass.bar_ctx(context, *args, **kwargs_dict) - assert result == 3 - - # Static method - func_schema = function_schema(MyClass.baz) - assert isinstance(func_schema.params_json_schema, dict) - assert func_schema.params_json_schema.get("title") == "baz_args" - - input = {"a": 1, "b": 2} - parsed = func_schema.params_pydantic_model(**input) - args, kwargs_dict = func_schema.to_call_args(parsed) - result = MyClass.baz(*args, **kwargs_dict) - assert result == 3 - - # Static method with context - func_schema = function_schema(MyClass.baz_ctx) - assert isinstance(func_schema.params_json_schema, dict) - assert func_schema.params_json_schema.get("title") == "baz_ctx_args" - assert func_schema.takes_context - - input = {"a": 1, "b": 2} - parsed = func_schema.params_pydantic_model(**input) - args, kwargs_dict = func_schema.to_call_args(parsed) - result = MyClass.baz_ctx(context, *args, **kwargs_dict) - assert result == 3 - - -class MyEnum(str, Enum): - FOO = "foo" - BAR = "bar" - BAZ = "baz" - - -def enum_and_literal_function(a: MyEnum, b: Literal["a", "b", "c"]) -> str: - return f"{a.value} {b}" - - -def test_enum_and_literal_function(): - func_schema = function_schema(enum_and_literal_function) - assert isinstance(func_schema.params_json_schema, dict) - assert ( - func_schema.params_json_schema.get("title") == "enum_and_literal_function_args" - ) - - # Check that the enum values are included in the JSON schema - assert func_schema.params_json_schema.get("$defs", {}).get("MyEnum", {}).get( - "enum" - ) == [ - "foo", - "bar", - "baz", - ] - - # Check that the enum is expressed as a def - assert ( - func_schema.params_json_schema.get("properties", {}).get("a", {}).get("$ref") - == "#/$defs/MyEnum" - ) - - # Check that the literal values are included in the JSON schema - assert func_schema.params_json_schema.get("properties", {}).get("b", {}).get( - "enum" - ) == [ - "a", - "b", - "c", - ] - - # Valid input - valid_input = {"a": "foo", "b": "a"} - parsed = func_schema.params_pydantic_model(**valid_input) - args, kwargs_dict = func_schema.to_call_args(parsed) - result = enum_and_literal_function(*args, **kwargs_dict) - assert result == "foo a" - - # Invalid input: 'a' must be a valid enum value - with pytest.raises(ValidationError): - func_schema.params_pydantic_model(**{"a": "not an enum value", "b": "a"}) - - # Invalid input: 'b' must be a valid literal value - with pytest.raises(ValidationError): - func_schema.params_pydantic_model(**{"a": "foo", "b": "not a literal value"}) - - -def test_run_context_in_non_first_position_raises_value_error(): - # When a parameter (after the first) is annotated as RunContextWrapper, - # function_schema() should raise a UserError. - def func(a: int, context: RunContextWrapper) -> None: - pass - - with pytest.raises(UserError): - function_schema(func, use_docstring_info=False) - - -def test_var_positional_tuple_annotation(): - # When a function has a var-positional parameter annotated with a tuple type, - # function_schema() should convert it into a field with type List[]. - def func(*args: tuple[int, ...]) -> int: - total = 0 - for arg in args: - total += sum(arg) - return total - - fs = function_schema(func, use_docstring_info=False) - - properties = fs.params_json_schema.get("properties", {}) - assert properties.get("args").get("type") == "array" - assert properties.get("args").get("items").get("type") == "integer" - - -def test_var_keyword_dict_annotation(): - # Case 3: - # When a function has a var-keyword parameter annotated with a dict type, - # function_schema() should convert it into a field with type Dict[, ]. - def func(**kwargs: dict[str, int]): - return kwargs - - fs = function_schema(func, use_docstring_info=False) - - properties = fs.params_json_schema.get("properties", {}) - # The name of the field is "kwargs", and it's a JSON object i.e. a dict. - assert properties.get("kwargs").get("type") == "object" - # The values in the dict are integers. - assert properties.get("kwargs").get("additionalProperties").get("type") == "integer" diff --git a/pkg/hanzo-agent/tests/test_function_tool.py b/pkg/hanzo-agent/tests/test_function_tool.py deleted file mode 100644 index 6f8537bf0..000000000 --- a/pkg/hanzo-agent/tests/test_function_tool.py +++ /dev/null @@ -1,261 +0,0 @@ -import json -from typing import Any - -import pytest -from pydantic import BaseModel -from typing_extensions import TypedDict - -from agents import FunctionTool, ModelBehaviorError, RunContextWrapper, function_tool -from agents.tool import default_tool_error_function - - -def argless_function() -> str: - return "ok" - - -@pytest.mark.asyncio -async def test_argless_function(): - tool = function_tool(argless_function) - assert tool.name == "argless_function" - - result = await tool.on_invoke_tool(RunContextWrapper(None), "") - assert result == "ok" - - -def argless_with_context(ctx: RunContextWrapper[str]) -> str: - return "ok" - - -@pytest.mark.asyncio -async def test_argless_with_context(): - tool = function_tool(argless_with_context) - assert tool.name == "argless_with_context" - - result = await tool.on_invoke_tool(RunContextWrapper(None), "") - assert result == "ok" - - # Extra JSON should not raise an error - result = await tool.on_invoke_tool(RunContextWrapper(None), '{"a": 1}') - assert result == "ok" - - -def simple_function(a: int, b: int = 5): - return a + b - - -@pytest.mark.asyncio -async def test_simple_function(): - tool = function_tool(simple_function, failure_error_function=None) - assert tool.name == "simple_function" - - result = await tool.on_invoke_tool(RunContextWrapper(None), '{"a": 1}') - assert result == "6" - - result = await tool.on_invoke_tool(RunContextWrapper(None), '{"a": 1, "b": 2}') - assert result == "3" - - # Missing required argument should raise an error - with pytest.raises(ModelBehaviorError): - await tool.on_invoke_tool(RunContextWrapper(None), "") - - -class Foo(BaseModel): - a: int - b: int = 5 - - -class Bar(TypedDict): - x: str - y: int - - -def complex_args_function(foo: Foo, bar: Bar, baz: str = "hello"): - return f"{foo.a + foo.b} {bar['x']}{bar['y']} {baz}" - - -@pytest.mark.asyncio -async def test_complex_args_function(): - tool = function_tool(complex_args_function, failure_error_function=None) - assert tool.name == "complex_args_function" - - valid_json = json.dumps( - { - "foo": Foo(a=1).model_dump(), - "bar": Bar(x="hello", y=10), - } - ) - result = await tool.on_invoke_tool(RunContextWrapper(None), valid_json) - assert result == "6 hello10 hello" - - valid_json = json.dumps( - { - "foo": Foo(a=1, b=2).model_dump(), - "bar": Bar(x="hello", y=10), - } - ) - result = await tool.on_invoke_tool(RunContextWrapper(None), valid_json) - assert result == "3 hello10 hello" - - valid_json = json.dumps( - { - "foo": Foo(a=1, b=2).model_dump(), - "bar": Bar(x="hello", y=10), - "baz": "world", - } - ) - result = await tool.on_invoke_tool(RunContextWrapper(None), valid_json) - assert result == "3 hello10 world" - - # Missing required argument should raise an error - with pytest.raises(ModelBehaviorError): - await tool.on_invoke_tool(RunContextWrapper(None), '{"foo": {"a": 1}}') - - -def test_function_config_overrides(): - tool = function_tool(simple_function, name_override="custom_name") - assert tool.name == "custom_name" - - tool = function_tool(simple_function, description_override="custom description") - assert tool.description == "custom description" - - tool = function_tool( - simple_function, - name_override="custom_name", - description_override="custom description", - ) - assert tool.name == "custom_name" - assert tool.description == "custom description" - - -def test_func_schema_is_strict(): - tool = function_tool(simple_function) - assert tool.strict_json_schema, "Should be strict by default" - assert ( - "additionalProperties" in tool.params_json_schema - and not tool.params_json_schema["additionalProperties"] - ) - - tool = function_tool(complex_args_function) - assert tool.strict_json_schema, "Should be strict by default" - assert ( - "additionalProperties" in tool.params_json_schema - and not tool.params_json_schema["additionalProperties"] - ) - - -@pytest.mark.asyncio -async def test_manual_function_tool_creation_works(): - def do_some_work(data: str) -> str: - return f"{data}_done" - - class FunctionArgs(BaseModel): - data: str - - async def run_function(ctx: RunContextWrapper[Any], args: str) -> str: - parsed = FunctionArgs.model_validate_json(args) - return do_some_work(data=parsed.data) - - tool = FunctionTool( - name="test", - description="Processes extracted user data", - params_json_schema=FunctionArgs.model_json_schema(), - on_invoke_tool=run_function, - ) - - assert tool.name == "test" - assert tool.description == "Processes extracted user data" - for key, value in FunctionArgs.model_json_schema().items(): - assert tool.params_json_schema[key] == value - assert tool.strict_json_schema - - result = await tool.on_invoke_tool(RunContextWrapper(None), '{"data": "hello"}') - assert result == "hello_done" - - tool_not_strict = FunctionTool( - name="test", - description="Processes extracted user data", - params_json_schema=FunctionArgs.model_json_schema(), - on_invoke_tool=run_function, - strict_json_schema=False, - ) - - assert not tool_not_strict.strict_json_schema - assert "additionalProperties" not in tool_not_strict.params_json_schema - - result = await tool_not_strict.on_invoke_tool( - RunContextWrapper(None), '{"data": "hello", "bar": "baz"}' - ) - assert result == "hello_done" - - -@pytest.mark.asyncio -async def test_function_tool_default_error_works(): - def my_func(a: int, b: int = 5): - raise ValueError("test") - - tool = function_tool(my_func) - ctx = RunContextWrapper(None) - - result = await tool.on_invoke_tool(ctx, "") - assert "Invalid JSON" in str(result) - - result = await tool.on_invoke_tool(ctx, "{}") - assert "Invalid JSON" in str(result) - - result = await tool.on_invoke_tool(ctx, '{"a": 1}') - assert result == default_tool_error_function(ctx, ValueError("test")) - - result = await tool.on_invoke_tool(ctx, '{"a": 1, "b": 2}') - assert result == default_tool_error_function(ctx, ValueError("test")) - - -@pytest.mark.asyncio -async def test_sync_custom_error_function_works(): - def my_func(a: int, b: int = 5): - raise ValueError("test") - - def custom_sync_error_function( - ctx: RunContextWrapper[Any], error: Exception - ) -> str: - return f"error_{error.__class__.__name__}" - - tool = function_tool(my_func, failure_error_function=custom_sync_error_function) - ctx = RunContextWrapper(None) - - result = await tool.on_invoke_tool(ctx, "") - assert result == "error_ModelBehaviorError" - - result = await tool.on_invoke_tool(ctx, "{}") - assert result == "error_ModelBehaviorError" - - result = await tool.on_invoke_tool(ctx, '{"a": 1}') - assert result == "error_ValueError" - - result = await tool.on_invoke_tool(ctx, '{"a": 1, "b": 2}') - assert result == "error_ValueError" - - -@pytest.mark.asyncio -async def test_async_custom_error_function_works(): - async def my_func(a: int, b: int = 5): - raise ValueError("test") - - def custom_sync_error_function( - ctx: RunContextWrapper[Any], error: Exception - ) -> str: - return f"error_{error.__class__.__name__}" - - tool = function_tool(my_func, failure_error_function=custom_sync_error_function) - ctx = RunContextWrapper(None) - - result = await tool.on_invoke_tool(ctx, "") - assert result == "error_ModelBehaviorError" - - result = await tool.on_invoke_tool(ctx, "{}") - assert result == "error_ModelBehaviorError" - - result = await tool.on_invoke_tool(ctx, '{"a": 1}') - assert result == "error_ValueError" - - result = await tool.on_invoke_tool(ctx, '{"a": 1, "b": 2}') - assert result == "error_ValueError" diff --git a/pkg/hanzo-agent/tests/test_function_tool_decorator.py b/pkg/hanzo-agent/tests/test_function_tool_decorator.py deleted file mode 100644 index 469e84b30..000000000 --- a/pkg/hanzo-agent/tests/test_function_tool_decorator.py +++ /dev/null @@ -1,146 +0,0 @@ -import asyncio -import json -from typing import Any - -import pytest - -from agents import function_tool -from agents.run_context import RunContextWrapper - - -class DummyContext: - def __init__(self): - self.data = "something" - - -def ctx_wrapper() -> RunContextWrapper[DummyContext]: - return RunContextWrapper(DummyContext()) - - -@function_tool -def sync_no_context_no_args() -> str: - return "test_1" - - -@pytest.mark.asyncio -async def test_sync_no_context_no_args_invocation(): - tool = sync_no_context_no_args - output = await tool.on_invoke_tool(ctx_wrapper(), "") - assert output == "test_1" - - -@function_tool -def sync_no_context_with_args(a: int, b: int) -> int: - return a + b - - -@pytest.mark.asyncio -async def test_sync_no_context_with_args_invocation(): - tool = sync_no_context_with_args - input_data = {"a": 5, "b": 7} - output = await tool.on_invoke_tool(ctx_wrapper(), json.dumps(input_data)) - assert int(output) == 12 - - -@function_tool -def sync_with_context(ctx: RunContextWrapper[DummyContext], name: str) -> str: - return f"{name}_{ctx.context.data}" - - -@pytest.mark.asyncio -async def test_sync_with_context_invocation(): - tool = sync_with_context - input_data = {"name": "Alice"} - output = await tool.on_invoke_tool(ctx_wrapper(), json.dumps(input_data)) - assert output == "Alice_something" - - -@function_tool -async def async_no_context(a: int, b: int) -> int: - await asyncio.sleep(0) # Just to illustrate async - return a * b - - -@pytest.mark.asyncio -async def test_async_no_context_invocation(): - tool = async_no_context - input_data = {"a": 3, "b": 4} - output = await tool.on_invoke_tool(ctx_wrapper(), json.dumps(input_data)) - assert int(output) == 12 - - -@function_tool -async def async_with_context( - ctx: RunContextWrapper[DummyContext], prefix: str, num: int -) -> str: - await asyncio.sleep(0) - return f"{prefix}-{num}-{ctx.context.data}" - - -@pytest.mark.asyncio -async def test_async_with_context_invocation(): - tool = async_with_context - input_data = {"prefix": "Value", "num": 42} - output = await tool.on_invoke_tool(ctx_wrapper(), json.dumps(input_data)) - assert output == "Value-42-something" - - -@function_tool(name_override="my_custom_tool", description_override="custom desc") -def sync_no_context_override() -> str: - return "override_result" - - -@pytest.mark.asyncio -async def test_sync_no_context_override_invocation(): - tool = sync_no_context_override - assert tool.name == "my_custom_tool" - assert tool.description == "custom desc" - output = await tool.on_invoke_tool(ctx_wrapper(), "") - assert output == "override_result" - - -@function_tool(failure_error_function=None) -def will_fail_on_bad_json(x: int) -> int: - return x * 2 # pragma: no cover - - -@pytest.mark.asyncio -async def test_error_on_invalid_json(): - tool = will_fail_on_bad_json - # Passing an invalid JSON string - with pytest.raises(Exception) as exc_info: - await tool.on_invoke_tool(ctx_wrapper(), "{not valid json}") - assert "Invalid JSON input for tool" in str(exc_info.value) - - -def sync_error_handler(ctx: RunContextWrapper[Any], error: Exception) -> str: - return f"error_{error.__class__.__name__}" - - -@function_tool(failure_error_function=sync_error_handler) -def will_not_fail_on_bad_json(x: int) -> int: - return x * 2 # pragma: no cover - - -@pytest.mark.asyncio -async def test_no_error_on_invalid_json(): - tool = will_not_fail_on_bad_json - # Passing an invalid JSON string - result = await tool.on_invoke_tool(ctx_wrapper(), "{not valid json}") - assert result == "error_ModelBehaviorError" - - -def async_error_handler(ctx: RunContextWrapper[Any], error: Exception) -> str: - return f"error_{error.__class__.__name__}" - - -@function_tool(failure_error_function=sync_error_handler) -def will_not_fail_on_bad_json_async(x: int) -> int: - return x * 2 # pragma: no cover - - -@pytest.mark.asyncio -async def test_no_error_on_invalid_json_async(): - tool = will_not_fail_on_bad_json_async - result = await tool.on_invoke_tool(ctx_wrapper(), "{not valid json}") - assert result == "error_ModelBehaviorError" diff --git a/pkg/hanzo-agent/tests/test_global_hooks.py b/pkg/hanzo-agent/tests/test_global_hooks.py deleted file mode 100644 index 6ac35b90d..000000000 --- a/pkg/hanzo-agent/tests/test_global_hooks.py +++ /dev/null @@ -1,373 +0,0 @@ -from __future__ import annotations - -import json -from collections import defaultdict -from typing import Any - -import pytest -from typing_extensions import TypedDict - -from agents import Agent, RunContextWrapper, RunHooks, Runner, TContext, Tool - -from .fake_model import FakeModel -from .test_responses import ( - get_final_output_message, - get_function_tool, - get_function_tool_call, - get_handoff_tool_call, - get_text_message, -) - - -class RunHooksForTests(RunHooks): - def __init__(self): - self.events: dict[str, int] = defaultdict(int) - - def reset(self): - self.events.clear() - - async def on_agent_start( - self, context: RunContextWrapper[TContext], agent: Agent[TContext] - ) -> None: - self.events["on_agent_start"] += 1 - - async def on_agent_end( - self, - context: RunContextWrapper[TContext], - agent: Agent[TContext], - output: Any, - ) -> None: - self.events["on_agent_end"] += 1 - - async def on_handoff( - self, - context: RunContextWrapper[TContext], - from_agent: Agent[TContext], - to_agent: Agent[TContext], - ) -> None: - self.events["on_handoff"] += 1 - - async def on_tool_start( - self, - context: RunContextWrapper[TContext], - agent: Agent[TContext], - tool: Tool, - ) -> None: - self.events["on_tool_start"] += 1 - - async def on_tool_end( - self, - context: RunContextWrapper[TContext], - agent: Agent[TContext], - tool: Tool, - result: str, - ) -> None: - self.events["on_tool_end"] += 1 - - -@pytest.mark.asyncio -async def test_non_streamed_agent_hooks(): - hooks = RunHooksForTests() - model = FakeModel() - agent_1 = Agent(name="test_1", model=model) - agent_2 = Agent(name="test_2", model=model) - agent_3 = Agent( - name="test_3", - model=model, - handoffs=[agent_1, agent_2], - tools=[get_function_tool("some_function", "result")], - ) - - agent_1.handoffs.append(agent_3) - - model.set_next_output([get_text_message("user_message")]) - output = await Runner.run(agent_3, input="user_message", hooks=hooks) - assert hooks.events == {"on_agent_start": 1, "on_agent_end": 1}, f"{output}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message and a handoff - [get_text_message("a_message"), get_handoff_tool_call(agent_1)], - # Third turn: text message - [get_text_message("done")], - ] - ) - await Runner.run(agent_3, input="user_message", hooks=hooks) - assert hooks.events == { - # We only invoke on_agent_start when we begin executing a new agent. - # Although agent_3 runs two turns internally before handing off, - # that's one logical agent segment, so on_agent_start fires once. - # Then we hand off to agent_1, so on_agent_start fires for that agent. - "on_agent_start": 2, - "on_tool_start": 1, # Only one tool call - "on_tool_end": 1, # Only one tool call - "on_handoff": 1, # Only one handoff - "on_agent_end": 1, # Should always have one end - }, f"got unexpected event count: {hooks.events}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message, another tool call, and a handoff - [ - get_text_message("a_message"), - get_function_tool_call("some_function", json.dumps({"a": "b"})), - get_handoff_tool_call(agent_1), - ], - # Third turn: a message and a handoff back to the orig agent - [get_text_message("a_message"), get_handoff_tool_call(agent_3)], - # Fourth turn: text message - [get_text_message("done")], - ] - ) - await Runner.run(agent_3, input="user_message", hooks=hooks) - - assert hooks.events == { - # agent_3 starts (fires on_agent_start), runs two turns and hands off. - # agent_1 starts (fires on_agent_start), then hands back to agent_3. - # agent_3 starts again (fires on_agent_start) to complete execution. - "on_agent_start": 3, - "on_tool_start": 2, # 2 tool calls - "on_tool_end": 2, # 2 tool calls - "on_handoff": 2, # 2 handoffs - "on_agent_end": 1, # Should always have one end - }, f"got unexpected event count: {hooks.events}" - hooks.reset() - - -@pytest.mark.asyncio -async def test_streamed_agent_hooks(): - hooks = RunHooksForTests() - model = FakeModel() - agent_1 = Agent(name="test_1", model=model) - agent_2 = Agent(name="test_2", model=model) - agent_3 = Agent( - name="test_3", - model=model, - handoffs=[agent_1, agent_2], - tools=[get_function_tool("some_function", "result")], - ) - - agent_1.handoffs.append(agent_3) - - model.set_next_output([get_text_message("user_message")]) - output = Runner.run_streamed(agent_3, input="user_message", hooks=hooks) - async for _ in output.stream_events(): - pass - assert hooks.events == {"on_agent_start": 1, "on_agent_end": 1}, f"{output}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message and a handoff - [get_text_message("a_message"), get_handoff_tool_call(agent_1)], - # Third turn: text message - [get_text_message("done")], - ] - ) - output = Runner.run_streamed(agent_3, input="user_message", hooks=hooks) - async for _ in output.stream_events(): - pass - assert hooks.events == { - # As in the non-streamed case above, two logical agent segments: - # starting agent_3, then handoff to agent_1. - "on_agent_start": 2, - "on_tool_start": 1, # Only one tool call - "on_tool_end": 1, # Only one tool call - "on_handoff": 1, # Only one handoff - "on_agent_end": 1, # Should always have one end - }, f"got unexpected event count: {hooks.events}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message, another tool call, and a handoff - [ - get_text_message("a_message"), - get_function_tool_call("some_function", json.dumps({"a": "b"})), - get_handoff_tool_call(agent_1), - ], - # Third turn: a message and a handoff back to the orig agent - [get_text_message("a_message"), get_handoff_tool_call(agent_3)], - # Fourth turn: text message - [get_text_message("done")], - ] - ) - output = Runner.run_streamed(agent_3, input="user_message", hooks=hooks) - async for _ in output.stream_events(): - pass - - assert hooks.events == { - # Same three logical agent segments as in the non-streamed case, - # so on_agent_start fires three times. - "on_agent_start": 3, - "on_tool_start": 2, # 2 tool calls - "on_tool_end": 2, # 2 tool calls - "on_handoff": 2, # 2 handoffs - "on_agent_end": 1, # Should always have one end - }, f"got unexpected event count: {hooks.events}" - hooks.reset() - - -class Foo(TypedDict): - a: str - - -@pytest.mark.asyncio -async def test_structed_output_non_streamed_agent_hooks(): - hooks = RunHooksForTests() - model = FakeModel() - agent_1 = Agent(name="test_1", model=model) - agent_2 = Agent(name="test_2", model=model) - agent_3 = Agent( - name="test_3", - model=model, - handoffs=[agent_1, agent_2], - tools=[get_function_tool("some_function", "result")], - output_type=Foo, - ) - - agent_1.handoffs.append(agent_3) - - model.set_next_output([get_final_output_message(json.dumps({"a": "b"}))]) - output = await Runner.run(agent_3, input="user_message", hooks=hooks) - assert hooks.events == {"on_agent_start": 1, "on_agent_end": 1}, f"{output}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message and a handoff - [get_text_message("a_message"), get_handoff_tool_call(agent_1)], - # Third turn: end message (for agent 1) - [get_text_message("done")], - ] - ) - output = await Runner.run(agent_3, input="user_message", hooks=hooks) - - assert hooks.events == { - # As with unstructured output, we expect on_agent_start once for - # agent_3 and once for agent_1. - "on_agent_start": 2, - "on_tool_start": 1, # Only one tool call - "on_tool_end": 1, # Only one tool call - "on_handoff": 1, # Only one handoff - "on_agent_end": 1, # Should always have one end - }, f"got unexpected event count: {hooks.events}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message, another tool call, and a handoff - [ - get_text_message("a_message"), - get_function_tool_call("some_function", json.dumps({"a": "b"})), - get_handoff_tool_call(agent_1), - ], - # Third turn: a message and a handoff back to the orig agent - [get_text_message("a_message"), get_handoff_tool_call(agent_3)], - # Fourth turn: end message (for agent 3) - [get_final_output_message(json.dumps({"a": "b"}))], - ] - ) - await Runner.run(agent_3, input="user_message", hooks=hooks) - - assert hooks.events == { - # We still expect three logical agent segments, as before. - "on_agent_start": 3, - "on_tool_start": 2, # 2 tool calls - "on_tool_end": 2, # 2 tool calls - "on_handoff": 2, # 2 handoffs - "on_agent_end": 1, # Should always have one end - }, f"got unexpected event count: {hooks.events}" - hooks.reset() - - -@pytest.mark.asyncio -async def test_structed_output_streamed_agent_hooks(): - hooks = RunHooksForTests() - model = FakeModel() - agent_1 = Agent(name="test_1", model=model) - agent_2 = Agent(name="test_2", model=model) - agent_3 = Agent( - name="test_3", - model=model, - handoffs=[agent_1, agent_2], - tools=[get_function_tool("some_function", "result")], - output_type=Foo, - ) - - agent_1.handoffs.append(agent_3) - - model.set_next_output([get_final_output_message(json.dumps({"a": "b"}))]) - output = Runner.run_streamed(agent_3, input="user_message", hooks=hooks) - async for _ in output.stream_events(): - pass - assert hooks.events == {"on_agent_start": 1, "on_agent_end": 1}, f"{output}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message and a handoff - [get_text_message("a_message"), get_handoff_tool_call(agent_1)], - # Third turn: end message (for agent 1) - [get_text_message("done")], - ] - ) - output = Runner.run_streamed(agent_3, input="user_message", hooks=hooks) - async for _ in output.stream_events(): - pass - - assert hooks.events == { - # Two agent segments: agent_3 and then agent_1. - "on_agent_start": 2, - "on_tool_start": 1, # Only one tool call - "on_tool_end": 1, # Only one tool call - "on_handoff": 1, # Only one handoff - "on_agent_end": 1, # Should always have one end - }, f"got unexpected event count: {hooks.events}" - hooks.reset() - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message, another tool call, and a handoff - [ - get_text_message("a_message"), - get_function_tool_call("some_function", json.dumps({"a": "b"})), - get_handoff_tool_call(agent_1), - ], - # Third turn: a message and a handoff back to the orig agent - [get_text_message("a_message"), get_handoff_tool_call(agent_3)], - # Fourth turn: end message (for agent 3) - [get_final_output_message(json.dumps({"a": "b"}))], - ] - ) - output = Runner.run_streamed(agent_3, input="user_message", hooks=hooks) - async for _ in output.stream_events(): - pass - - assert hooks.events == { - # Three agent segments: agent_3, agent_1, agent_3 again. - "on_agent_start": 3, - "on_tool_start": 2, # 2 tool calls - "on_tool_end": 2, # 2 tool calls - "on_handoff": 2, # 2 handoffs - "on_agent_end": 1, # Should always have one end - }, f"got unexpected event count: {hooks.events}" - hooks.reset() diff --git a/pkg/hanzo-agent/tests/test_guardrails.py b/pkg/hanzo-agent/tests/test_guardrails.py deleted file mode 100644 index 1395b9a0b..000000000 --- a/pkg/hanzo-agent/tests/test_guardrails.py +++ /dev/null @@ -1,304 +0,0 @@ -from __future__ import annotations - -from typing import Any - -import pytest - -from agents import ( - Agent, - GuardrailFunctionOutput, - InputGuardrail, - OutputGuardrail, - RunContextWrapper, - TResponseInputItem, - UserError, -) -from agents.guardrail import input_guardrail, output_guardrail - - -def get_sync_guardrail(triggers: bool, output_info: Any | None = None): - def sync_guardrail( - context: RunContextWrapper[Any], - agent: Agent[Any], - input: str | list[TResponseInputItem], - ): - return GuardrailFunctionOutput( - output_info=output_info, - tripwire_triggered=triggers, - ) - - return sync_guardrail - - -@pytest.mark.asyncio -async def test_sync_input_guardrail(): - guardrail = InputGuardrail(guardrail_function=get_sync_guardrail(triggers=False)) - result = await guardrail.run( - agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) - ) - assert not result.output.tripwire_triggered - assert result.output.output_info is None - - guardrail = InputGuardrail(guardrail_function=get_sync_guardrail(triggers=True)) - result = await guardrail.run( - agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) - ) - assert result.output.tripwire_triggered - assert result.output.output_info is None - - guardrail = InputGuardrail( - guardrail_function=get_sync_guardrail(triggers=True, output_info="test") - ) - result = await guardrail.run( - agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) - ) - assert result.output.tripwire_triggered - assert result.output.output_info == "test" - - -def get_async_input_guardrail(triggers: bool, output_info: Any | None = None): - async def async_guardrail( - context: RunContextWrapper[Any], - agent: Agent[Any], - input: str | list[TResponseInputItem], - ): - return GuardrailFunctionOutput( - output_info=output_info, - tripwire_triggered=triggers, - ) - - return async_guardrail - - -@pytest.mark.asyncio -async def test_async_input_guardrail(): - guardrail = InputGuardrail( - guardrail_function=get_async_input_guardrail(triggers=False) - ) - result = await guardrail.run( - agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) - ) - assert not result.output.tripwire_triggered - assert result.output.output_info is None - - guardrail = InputGuardrail( - guardrail_function=get_async_input_guardrail(triggers=True) - ) - result = await guardrail.run( - agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) - ) - assert result.output.tripwire_triggered - assert result.output.output_info is None - - guardrail = InputGuardrail( - guardrail_function=get_async_input_guardrail(triggers=True, output_info="test") - ) - result = await guardrail.run( - agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) - ) - assert result.output.tripwire_triggered - assert result.output.output_info == "test" - - -@pytest.mark.asyncio -async def test_invalid_input_guardrail_raises_user_error(): - with pytest.raises(UserError): - # Purposely ignoring type error - guardrail = InputGuardrail(guardrail_function="foo") # type: ignore - await guardrail.run( - agent=Agent(name="test"), - input="test", - context=RunContextWrapper(context=None), - ) - - -def get_sync_output_guardrail(triggers: bool, output_info: Any | None = None): - def sync_guardrail( - context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any - ): - return GuardrailFunctionOutput( - output_info=output_info, - tripwire_triggered=triggers, - ) - - return sync_guardrail - - -@pytest.mark.asyncio -async def test_sync_output_guardrail(): - guardrail = OutputGuardrail( - guardrail_function=get_sync_output_guardrail(triggers=False) - ) - result = await guardrail.run( - agent=Agent(name="test"), - agent_output="test", - context=RunContextWrapper(context=None), - ) - assert not result.output.tripwire_triggered - assert result.output.output_info is None - - guardrail = OutputGuardrail( - guardrail_function=get_sync_output_guardrail(triggers=True) - ) - result = await guardrail.run( - agent=Agent(name="test"), - agent_output="test", - context=RunContextWrapper(context=None), - ) - assert result.output.tripwire_triggered - assert result.output.output_info is None - - guardrail = OutputGuardrail( - guardrail_function=get_sync_output_guardrail(triggers=True, output_info="test") - ) - result = await guardrail.run( - agent=Agent(name="test"), - agent_output="test", - context=RunContextWrapper(context=None), - ) - assert result.output.tripwire_triggered - assert result.output.output_info == "test" - - -def get_async_output_guardrail(triggers: bool, output_info: Any | None = None): - async def async_guardrail( - context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any - ): - return GuardrailFunctionOutput( - output_info=output_info, - tripwire_triggered=triggers, - ) - - return async_guardrail - - -@pytest.mark.asyncio -async def test_async_output_guardrail(): - guardrail = OutputGuardrail( - guardrail_function=get_async_output_guardrail(triggers=False) - ) - result = await guardrail.run( - agent=Agent(name="test"), - agent_output="test", - context=RunContextWrapper(context=None), - ) - assert not result.output.tripwire_triggered - assert result.output.output_info is None - - guardrail = OutputGuardrail( - guardrail_function=get_async_output_guardrail(triggers=True) - ) - result = await guardrail.run( - agent=Agent(name="test"), - agent_output="test", - context=RunContextWrapper(context=None), - ) - assert result.output.tripwire_triggered - assert result.output.output_info is None - - guardrail = OutputGuardrail( - guardrail_function=get_async_output_guardrail(triggers=True, output_info="test") - ) - result = await guardrail.run( - agent=Agent(name="test"), - agent_output="test", - context=RunContextWrapper(context=None), - ) - assert result.output.tripwire_triggered - assert result.output.output_info == "test" - - -@pytest.mark.asyncio -async def test_invalid_output_guardrail_raises_user_error(): - with pytest.raises(UserError): - # Purposely ignoring type error - guardrail = OutputGuardrail(guardrail_function="foo") # type: ignore - await guardrail.run( - agent=Agent(name="test"), - agent_output="test", - context=RunContextWrapper(context=None), - ) - - -@input_guardrail -def decorated_input_guardrail( - context: RunContextWrapper[Any], - agent: Agent[Any], - input: str | list[TResponseInputItem], -) -> GuardrailFunctionOutput: - return GuardrailFunctionOutput( - output_info="test_1", - tripwire_triggered=False, - ) - - -@input_guardrail(name="Custom name") -def decorated_named_input_guardrail( - context: RunContextWrapper[Any], - agent: Agent[Any], - input: str | list[TResponseInputItem], -) -> GuardrailFunctionOutput: - return GuardrailFunctionOutput( - output_info="test_2", - tripwire_triggered=False, - ) - - -@pytest.mark.asyncio -async def test_input_guardrail_decorators(): - guardrail = decorated_input_guardrail - result = await guardrail.run( - agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) - ) - assert not result.output.tripwire_triggered - assert result.output.output_info == "test_1" - - guardrail = decorated_named_input_guardrail - result = await guardrail.run( - agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) - ) - assert not result.output.tripwire_triggered - assert result.output.output_info == "test_2" - assert guardrail.get_name() == "Custom name" - - -@output_guardrail -def decorated_output_guardrail( - context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any -) -> GuardrailFunctionOutput: - return GuardrailFunctionOutput( - output_info="test_3", - tripwire_triggered=False, - ) - - -@output_guardrail(name="Custom name") -def decorated_named_output_guardrail( - context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any -) -> GuardrailFunctionOutput: - return GuardrailFunctionOutput( - output_info="test_4", - tripwire_triggered=False, - ) - - -@pytest.mark.asyncio -async def test_output_guardrail_decorators(): - guardrail = decorated_output_guardrail - result = await guardrail.run( - agent=Agent(name="test"), - agent_output="test", - context=RunContextWrapper(context=None), - ) - assert not result.output.tripwire_triggered - assert result.output.output_info == "test_3" - - guardrail = decorated_named_output_guardrail - result = await guardrail.run( - agent=Agent(name="test"), - agent_output="test", - context=RunContextWrapper(context=None), - ) - assert not result.output.tripwire_triggered - assert result.output.output_info == "test_4" - assert guardrail.get_name() == "Custom name" diff --git a/pkg/hanzo-agent/tests/test_handoff_tool.py b/pkg/hanzo-agent/tests/test_handoff_tool.py deleted file mode 100644 index 27d92f7bd..000000000 --- a/pkg/hanzo-agent/tests/test_handoff_tool.py +++ /dev/null @@ -1,284 +0,0 @@ -from typing import Any - -import pytest -from openai.types.responses import ResponseOutputMessage, ResponseOutputText -from pydantic import BaseModel - -from agents import ( - Agent, - Handoff, - HandoffInputData, - MessageOutputItem, - ModelBehaviorError, - RunContextWrapper, - Runner, - UserError, - handoff, -) - - -def message_item(content: str, agent: Agent[Any]) -> MessageOutputItem: - return MessageOutputItem( - agent=agent, - raw_item=ResponseOutputMessage( - id="123", - status="completed", - role="assistant", - type="message", - content=[ - ResponseOutputText(text=content, type="output_text", annotations=[]) - ], - ), - ) - - -def get_len(data: HandoffInputData) -> int: - input_len = len(data.input_history) if isinstance(data.input_history, tuple) else 1 - pre_handoff_len = len(data.pre_handoff_items) - new_items_len = len(data.new_items) - return input_len + pre_handoff_len + new_items_len - - -def test_single_handoff_setup(): - agent_1 = Agent(name="test_1") - agent_2 = Agent(name="test_2", handoffs=[agent_1]) - - assert not agent_1.handoffs - assert agent_2.handoffs == [agent_1] - - assert not Runner._get_handoffs(agent_1) - - handoff_objects = Runner._get_handoffs(agent_2) - assert len(handoff_objects) == 1 - obj = handoff_objects[0] - assert obj.tool_name == Handoff.default_tool_name(agent_1) - assert obj.tool_description == Handoff.default_tool_description(agent_1) - assert obj.agent_name == agent_1.name - - -def test_multiple_handoffs_setup(): - agent_1 = Agent(name="test_1") - agent_2 = Agent(name="test_2") - agent_3 = Agent(name="test_3", handoffs=[agent_1, agent_2]) - - assert agent_3.handoffs == [agent_1, agent_2] - assert not agent_1.handoffs - assert not agent_2.handoffs - - handoff_objects = Runner._get_handoffs(agent_3) - assert len(handoff_objects) == 2 - assert handoff_objects[0].tool_name == Handoff.default_tool_name(agent_1) - assert handoff_objects[1].tool_name == Handoff.default_tool_name(agent_2) - - assert handoff_objects[0].tool_description == Handoff.default_tool_description( - agent_1 - ) - assert handoff_objects[1].tool_description == Handoff.default_tool_description( - agent_2 - ) - - assert handoff_objects[0].agent_name == agent_1.name - assert handoff_objects[1].agent_name == agent_2.name - - -def test_custom_handoff_setup(): - agent_1 = Agent(name="test_1") - agent_2 = Agent(name="test_2") - agent_3 = Agent( - name="test_3", - handoffs=[ - agent_1, - handoff( - agent_2, - tool_name_override="custom_tool_name", - tool_description_override="custom tool description", - ), - ], - ) - - assert len(agent_3.handoffs) == 2 - assert not agent_1.handoffs - assert not agent_2.handoffs - - handoff_objects = Runner._get_handoffs(agent_3) - assert len(handoff_objects) == 2 - - first_handoff = handoff_objects[0] - assert isinstance(first_handoff, Handoff) - assert first_handoff.tool_name == Handoff.default_tool_name(agent_1) - assert first_handoff.tool_description == Handoff.default_tool_description(agent_1) - assert first_handoff.agent_name == agent_1.name - - second_handoff = handoff_objects[1] - assert isinstance(second_handoff, Handoff) - assert second_handoff.tool_name == "custom_tool_name" - assert second_handoff.tool_description == "custom tool description" - assert second_handoff.agent_name == agent_2.name - - -class Foo(BaseModel): - bar: str - - -@pytest.mark.asyncio -async def test_handoff_input_type(): - async def _on_handoff(ctx: RunContextWrapper[Any], input: Foo): - pass - - agent = Agent(name="test") - obj = handoff(agent, input_type=Foo, on_handoff=_on_handoff) - for key, value in Foo.model_json_schema().items(): - assert obj.input_json_schema[key] == value - - # Invalid JSON should raise an error - with pytest.raises(ModelBehaviorError): - await obj.on_invoke_handoff(RunContextWrapper(agent), "not json") - - # Empty JSON should raise an error - with pytest.raises(ModelBehaviorError): - await obj.on_invoke_handoff(RunContextWrapper(agent), "") - - # Valid JSON should call the on_handoff function - invoked = await obj.on_invoke_handoff( - RunContextWrapper(agent), Foo(bar="baz").model_dump_json() - ) - assert invoked == agent - - -@pytest.mark.asyncio -async def test_on_handoff_called(): - was_called = False - - async def _on_handoff(ctx: RunContextWrapper[Any], input: Foo): - nonlocal was_called - was_called = True - - agent = Agent(name="test") - obj = handoff(agent, input_type=Foo, on_handoff=_on_handoff) - for key, value in Foo.model_json_schema().items(): - assert obj.input_json_schema[key] == value - - invoked = await obj.on_invoke_handoff( - RunContextWrapper(agent), Foo(bar="baz").model_dump_json() - ) - assert invoked == agent - - assert was_called, "on_handoff should have been called" - - -@pytest.mark.asyncio -async def test_on_handoff_without_input_called(): - was_called = False - - def _on_handoff(ctx: RunContextWrapper[Any]): - nonlocal was_called - was_called = True - - agent = Agent(name="test") - obj = handoff(agent, on_handoff=_on_handoff) - - invoked = await obj.on_invoke_handoff(RunContextWrapper(agent), "") - assert invoked == agent - - assert was_called, "on_handoff should have been called" - - -@pytest.mark.asyncio -async def test_async_on_handoff_without_input_called(): - was_called = False - - async def _on_handoff(ctx: RunContextWrapper[Any]): - nonlocal was_called - was_called = True - - agent = Agent(name="test") - obj = handoff(agent, on_handoff=_on_handoff) - - invoked = await obj.on_invoke_handoff(RunContextWrapper(agent), "") - assert invoked == agent - - assert was_called, "on_handoff should have been called" - - -@pytest.mark.asyncio -async def test_invalid_on_handoff_raises_error(): - was_called = False - - async def _on_handoff(ctx: RunContextWrapper[Any], blah: str): - nonlocal was_called - was_called = True # pragma: no cover - - agent = Agent(name="test") - - with pytest.raises(UserError): - # Purposely ignoring the type error here to simulate invalid input - handoff(agent, on_handoff=_on_handoff) # type: ignore - - -def test_handoff_input_data(): - agent = Agent(name="test") - - data = HandoffInputData( - input_history="", - pre_handoff_items=(), - new_items=(), - ) - assert get_len(data) == 1 - - data = HandoffInputData( - input_history=({"role": "user", "content": "foo"},), - pre_handoff_items=(), - new_items=(), - ) - assert get_len(data) == 1 - - data = HandoffInputData( - input_history=( - {"role": "user", "content": "foo"}, - {"role": "assistant", "content": "bar"}, - ), - pre_handoff_items=(), - new_items=(), - ) - assert get_len(data) == 2 - - data = HandoffInputData( - input_history=({"role": "user", "content": "foo"},), - pre_handoff_items=( - message_item("foo", agent), - message_item("foo2", agent), - ), - new_items=( - message_item("bar", agent), - message_item("baz", agent), - ), - ) - assert get_len(data) == 5 - - data = HandoffInputData( - input_history=( - {"role": "user", "content": "foo"}, - {"role": "assistant", "content": "bar"}, - ), - pre_handoff_items=(message_item("baz", agent),), - new_items=( - message_item("baz", agent), - message_item("qux", agent), - ), - ) - - assert get_len(data) == 5 - - -def test_handoff_input_schema_is_strict(): - agent = Agent(name="test") - obj = handoff(agent, input_type=Foo, on_handoff=lambda ctx, input: None) - for key, value in Foo.model_json_schema().items(): - assert obj.input_json_schema[key] == value - - assert obj.strict_json_schema, "Input schema should be strict" - - assert ( - "additionalProperties" in obj.input_json_schema - and not obj.input_json_schema["additionalProperties"] - ), "Input schema should be strict and have additionalProperties=False" diff --git a/pkg/hanzo-agent/tests/test_items_helpers.py b/pkg/hanzo-agent/tests/test_items_helpers.py deleted file mode 100644 index f2a12bf02..000000000 --- a/pkg/hanzo-agent/tests/test_items_helpers.py +++ /dev/null @@ -1,318 +0,0 @@ -from __future__ import annotations - -from openai.types.responses.response_computer_tool_call import ( - ActionScreenshot, - ResponseComputerToolCall, -) -from openai.types.responses.response_computer_tool_call_param import ( - ResponseComputerToolCallParam, -) -from openai.types.responses.response_file_search_tool_call import ( - ResponseFileSearchToolCall, -) -from openai.types.responses.response_file_search_tool_call_param import ( - ResponseFileSearchToolCallParam, -) -from openai.types.responses.response_function_tool_call import ResponseFunctionToolCall -from openai.types.responses.response_function_tool_call_param import ( - ResponseFunctionToolCallParam, -) -from openai.types.responses.response_function_web_search import ( - ResponseFunctionWebSearch, -) -from openai.types.responses.response_function_web_search_param import ( - ResponseFunctionWebSearchParam, -) -from openai.types.responses.response_output_message import ResponseOutputMessage -from openai.types.responses.response_output_message_param import ( - ResponseOutputMessageParam, -) -from openai.types.responses.response_output_refusal import ResponseOutputRefusal -from openai.types.responses.response_output_text import ResponseOutputText -from openai.types.responses.response_reasoning_item import ( - ResponseReasoningItem, - Summary, -) -from openai.types.responses.response_reasoning_item_param import ( - ResponseReasoningItemParam, -) - -from agents import ( - Agent, - ItemHelpers, - MessageOutputItem, - ModelResponse, - ReasoningItem, - RunItem, - TResponseInputItem, - Usage, -) - - -def make_message( - content_items: list[ResponseOutputText | ResponseOutputRefusal], -) -> ResponseOutputMessage: - """ - Helper to construct a ResponseOutputMessage with a single batch of content - items, using a fixed id/status. - """ - return ResponseOutputMessage( - id="msg123", - content=content_items, - role="assistant", - status="completed", - type="message", - ) - - -def test_extract_last_content_of_text_message() -> None: - # Build a message containing two text segments. - content1 = ResponseOutputText(annotations=[], text="Hello ", type="output_text") - content2 = ResponseOutputText(annotations=[], text="world!", type="output_text") - message = make_message([content1, content2]) - # Helpers should yield the last segment's text. - assert ItemHelpers.extract_last_content(message) == "world!" - - -def test_extract_last_content_of_refusal_message() -> None: - # Build a message whose last content entry is a refusal. - content1 = ResponseOutputText( - annotations=[], text="Before refusal", type="output_text" - ) - refusal = ResponseOutputRefusal(refusal="I cannot do that", type="refusal") - message = make_message([content1, refusal]) - # Helpers should extract the refusal string when last content is a refusal. - assert ItemHelpers.extract_last_content(message) == "I cannot do that" - - -def test_extract_last_content_non_message_returns_empty() -> None: - # Construct some other type of output item, e.g. a tool call, to verify non-message returns "". - tool_call = ResponseFunctionToolCall( - id="tool123", - arguments="{}", - call_id="call123", - name="func", - type="function_call", - ) - assert ItemHelpers.extract_last_content(tool_call) == "" - - -def test_extract_last_text_returns_text_only() -> None: - # A message whose last segment is text yields the text. - first_text = ResponseOutputText(annotations=[], text="part1", type="output_text") - second_text = ResponseOutputText(annotations=[], text="part2", type="output_text") - message = make_message([first_text, second_text]) - assert ItemHelpers.extract_last_text(message) == "part2" - # Whereas when last content is a refusal, extract_last_text returns None. - message2 = make_message( - [first_text, ResponseOutputRefusal(refusal="no", type="refusal")] - ) - assert ItemHelpers.extract_last_text(message2) is None - - -def test_input_to_new_input_list_from_string() -> None: - result = ItemHelpers.input_to_new_input_list("hi") - # Should wrap the string into a list with a single dict containing content and user role. - assert isinstance(result, list) - assert result == [{"content": "hi", "role": "user"}] - - -def test_input_to_new_input_list_deep_copies_lists() -> None: - # Given a list of message dictionaries, ensure the returned list is a deep copy. - original: list[TResponseInputItem] = [{"content": "abc", "role": "developer"}] - new_list = ItemHelpers.input_to_new_input_list(original) - assert new_list == original - # Mutating the returned list should not mutate the original. - new_list.pop() - assert "content" in original[0] and original[0].get("content") == "abc" - - -def test_text_message_output_concatenates_text_segments() -> None: - # Build a message with both text and refusal segments, only text segments are concatenated. - pieces: list[ResponseOutputText | ResponseOutputRefusal] = [] - pieces.append(ResponseOutputText(annotations=[], text="a", type="output_text")) - pieces.append(ResponseOutputRefusal(refusal="denied", type="refusal")) - pieces.append(ResponseOutputText(annotations=[], text="b", type="output_text")) - message = make_message(pieces) - # Wrap into MessageOutputItem to feed into text_message_output. - item = MessageOutputItem(agent=Agent(name="test"), raw_item=message) - assert ItemHelpers.text_message_output(item) == "ab" - - -def test_text_message_outputs_across_list_of_runitems() -> None: - """ - Compose several RunItem instances, including a non-message run item, and ensure - that only MessageOutputItem instances contribute any text. The non-message - (ReasoningItem) should be ignored by Helpers.text_message_outputs. - """ - message1 = make_message( - [ResponseOutputText(annotations=[], text="foo", type="output_text")] - ) - message2 = make_message( - [ResponseOutputText(annotations=[], text="bar", type="output_text")] - ) - item1: RunItem = MessageOutputItem(agent=Agent(name="test"), raw_item=message1) - item2: RunItem = MessageOutputItem(agent=Agent(name="test"), raw_item=message2) - # Create a non-message run item of a different type, e.g., a reasoning trace. - reasoning = ResponseReasoningItem(id="rid", summary=[], type="reasoning") - non_message_item: RunItem = ReasoningItem( - agent=Agent(name="test"), raw_item=reasoning - ) - # Confirm only the message outputs are concatenated. - assert ( - ItemHelpers.text_message_outputs([item1, non_message_item, item2]) == "foobar" - ) - - -def test_tool_call_output_item_constructs_function_call_output_dict(): - # Build a simple ResponseFunctionToolCall. - call = ResponseFunctionToolCall( - id="call-abc", - arguments='{"x": 1}', - call_id="call-abc", - name="do_something", - type="function_call", - ) - payload = ItemHelpers.tool_call_output_item(call, "result-string") - - assert isinstance(payload, dict) - assert payload["type"] == "function_call_output" - assert payload["call_id"] == call.id - assert payload["output"] == "result-string" - - -# The following tests ensure that every possible output item type defined by -# Hanzo AI's API can be converted back into an input item dict via -# ModelResponse.to_input_items. The output and input schema for each item are -# intended to be symmetric, so given any ResponseOutputItem, its model_dump -# should produce a dict that can satisfy the corresponding TypedDict input -# type. These tests construct minimal valid instances of each output type, -# invoke to_input_items, and then verify that the resulting dict can be used -# to round-trip back into a Pydantic output model without errors. - - -def test_to_input_items_for_message() -> None: - """An output message should convert into an input dict matching the message's own structure.""" - content = ResponseOutputText(annotations=[], text="hello world", type="output_text") - message = ResponseOutputMessage( - id="m1", content=[content], role="assistant", status="completed", type="message" - ) - resp = ModelResponse(output=[message], usage=Usage(), referenceable_id=None) - input_items = resp.to_input_items() - assert isinstance(input_items, list) and len(input_items) == 1 - # The dict should contain exactly the primitive values of the message - expected: ResponseOutputMessageParam = { - "id": "m1", - "content": [ - { - "annotations": [], - "text": "hello world", - "type": "output_text", - } - ], - "role": "assistant", - "status": "completed", - "type": "message", - } - assert input_items[0] == expected - - -def test_to_input_items_for_function_call() -> None: - """A function tool call output should produce the same dict as a function tool call input.""" - tool_call = ResponseFunctionToolCall( - id="f1", arguments="{}", call_id="c1", name="func", type="function_call" - ) - resp = ModelResponse(output=[tool_call], usage=Usage(), referenceable_id=None) - input_items = resp.to_input_items() - assert isinstance(input_items, list) and len(input_items) == 1 - expected: ResponseFunctionToolCallParam = { - "id": "f1", - "arguments": "{}", - "call_id": "c1", - "name": "func", - "type": "function_call", - } - assert input_items[0] == expected - - -def test_to_input_items_for_file_search_call() -> None: - """A file search tool call output should produce the same dict as a file search input.""" - fs_call = ResponseFileSearchToolCall( - id="fs1", queries=["query"], status="completed", type="file_search_call" - ) - resp = ModelResponse(output=[fs_call], usage=Usage(), referenceable_id=None) - input_items = resp.to_input_items() - assert isinstance(input_items, list) and len(input_items) == 1 - expected: ResponseFileSearchToolCallParam = { - "id": "fs1", - "queries": ["query"], - "status": "completed", - "type": "file_search_call", - } - assert input_items[0] == expected - - -def test_to_input_items_for_web_search_call() -> None: - """A web search tool call output should produce the same dict as a web search input.""" - ws_call = ResponseFunctionWebSearch( - id="w1", - status="completed", - type="web_search_call", - action={"type": "search", "query": ""}, - ) - resp = ModelResponse(output=[ws_call], usage=Usage(), referenceable_id=None) - input_items = resp.to_input_items() - assert isinstance(input_items, list) and len(input_items) == 1 - expected: ResponseFunctionWebSearchParam = { - "id": "w1", - "status": "completed", - "type": "web_search_call", - "action": {"type": "search", "query": ""}, - } - assert input_items[0] == expected - - -def test_to_input_items_for_computer_call_click() -> None: - """A computer call output should yield a dict whose shape matches the computer call input.""" - action = ActionScreenshot(type="screenshot") - comp_call = ResponseComputerToolCall( - id="comp1", - action=action, - type="computer_call", - call_id="comp1", - pending_safety_checks=[], - status="completed", - ) - resp = ModelResponse(output=[comp_call], usage=Usage(), referenceable_id=None) - input_items = resp.to_input_items() - assert isinstance(input_items, list) and len(input_items) == 1 - converted_dict = input_items[0] - # Top-level keys should match what we expect for a computer call input - expected: ResponseComputerToolCallParam = { - "id": "comp1", - "type": "computer_call", - "action": {"type": "screenshot"}, - "call_id": "comp1", - "pending_safety_checks": [], - "status": "completed", - } - assert converted_dict == expected - - -def test_to_input_items_for_reasoning() -> None: - """A reasoning output should produce the same dict as a reasoning input item.""" - rc = Summary(text="why", type="summary_text") - reasoning = ResponseReasoningItem(id="rid1", summary=[rc], type="reasoning") - resp = ModelResponse(output=[reasoning], usage=Usage(), referenceable_id=None) - input_items = resp.to_input_items() - assert isinstance(input_items, list) and len(input_items) == 1 - converted_dict = input_items[0] - - expected: ResponseReasoningItemParam = { - "id": "rid1", - "summary": [{"text": "why", "type": "summary_text"}], - "type": "reasoning", - } - print(converted_dict) - print(expected) - assert converted_dict == expected diff --git a/pkg/hanzo-agent/tests/test_max_turns.py b/pkg/hanzo-agent/tests/test_max_turns.py deleted file mode 100644 index 56be3c394..000000000 --- a/pkg/hanzo-agent/tests/test_max_turns.py +++ /dev/null @@ -1,142 +0,0 @@ -from __future__ import annotations - -import json - -import pytest -from typing_extensions import TypedDict - -from agents import Agent, MaxTurnsExceeded, Runner - -from .fake_model import FakeModel -from .test_responses import get_function_tool, get_function_tool_call, get_text_message - - -@pytest.mark.asyncio -async def test_non_streamed_max_turns(): - model = FakeModel() - agent = Agent( - name="test_1", - model=model, - tools=[get_function_tool("some_function", "result")], - ) - - func_output = json.dumps({"a": "b"}) - - model.add_multiple_turn_outputs( - [ - [ - get_text_message("1"), - get_function_tool_call("some_function", func_output), - ], - [ - get_text_message("2"), - get_function_tool_call("some_function", func_output), - ], - [ - get_text_message("3"), - get_function_tool_call("some_function", func_output), - ], - [ - get_text_message("4"), - get_function_tool_call("some_function", func_output), - ], - [ - get_text_message("5"), - get_function_tool_call("some_function", func_output), - ], - ] - ) - with pytest.raises(MaxTurnsExceeded): - await Runner.run(agent, input="user_message", max_turns=3) - - -@pytest.mark.asyncio -async def test_streamed_max_turns(): - model = FakeModel() - agent = Agent( - name="test_1", - model=model, - tools=[get_function_tool("some_function", "result")], - ) - func_output = json.dumps({"a": "b"}) - - model.add_multiple_turn_outputs( - [ - [ - get_text_message("1"), - get_function_tool_call("some_function", func_output), - ], - [ - get_text_message("2"), - get_function_tool_call("some_function", func_output), - ], - [ - get_text_message("3"), - get_function_tool_call("some_function", func_output), - ], - [ - get_text_message("4"), - get_function_tool_call("some_function", func_output), - ], - [ - get_text_message("5"), - get_function_tool_call("some_function", func_output), - ], - ] - ) - with pytest.raises(MaxTurnsExceeded): - output = Runner.run_streamed(agent, input="user_message", max_turns=3) - async for _ in output.stream_events(): - pass - - -class Foo(TypedDict): - a: str - - -@pytest.mark.asyncio -async def test_structured_output_non_streamed_max_turns(): - model = FakeModel() - agent = Agent( - name="test_1", - model=model, - output_type=Foo, - tools=[get_function_tool("tool_1", "result")], - ) - - model.add_multiple_turn_outputs( - [ - [get_function_tool_call("tool_1")], - [get_function_tool_call("tool_1")], - [get_function_tool_call("tool_1")], - [get_function_tool_call("tool_1")], - [get_function_tool_call("tool_1")], - ] - ) - with pytest.raises(MaxTurnsExceeded): - await Runner.run(agent, input="user_message", max_turns=3) - - -@pytest.mark.asyncio -async def test_structured_output_streamed_max_turns(): - model = FakeModel() - agent = Agent( - name="test_1", - model=model, - output_type=Foo, - tools=[get_function_tool("tool_1", "result")], - ) - - model.add_multiple_turn_outputs( - [ - [get_function_tool_call("tool_1")], - [get_function_tool_call("tool_1")], - [get_function_tool_call("tool_1")], - [get_function_tool_call("tool_1")], - [get_function_tool_call("tool_1")], - ] - ) - with pytest.raises(MaxTurnsExceeded): - output = Runner.run_streamed(agent, input="user_message", max_turns=3) - async for _ in output.stream_events(): - pass diff --git a/pkg/hanzo-agent/tests/test_openai_chatcompletions.py b/pkg/hanzo-agent/tests/test_openai_chatcompletions.py deleted file mode 100644 index 2f94edd4c..000000000 --- a/pkg/hanzo-agent/tests/test_openai_chatcompletions.py +++ /dev/null @@ -1,296 +0,0 @@ -from __future__ import annotations - -from collections.abc import AsyncIterator -from typing import Any - -import httpx -import pytest -from openai import NOT_GIVEN -from openai.types.chat.chat_completion import ChatCompletion, Choice -from openai.types.chat.chat_completion_chunk import ChatCompletionChunk -from openai.types.chat.chat_completion_message import ChatCompletionMessage -from openai.types.chat.chat_completion_message_tool_call import ( - ChatCompletionMessageToolCall, - Function, -) -from openai.types.completion_usage import CompletionUsage -from openai.types.responses import ( - Response, - ResponseFunctionToolCall, - ResponseOutputMessage, - ResponseOutputRefusal, - ResponseOutputText, -) - -from agents import ( - ModelResponse, - ModelSettings, - ModelTracing, - OpenAIChatCompletionsModel, - OpenAIProvider, - generation_span, -) -from agents.models.fake_id import FAKE_RESPONSES_ID - - -@pytest.mark.allow_call_model_methods -@pytest.mark.asyncio -async def test_get_response_with_text_message(monkeypatch) -> None: - """ - When the model returns a ChatCompletionMessage with plain text content, - `get_response` should produce a single `ResponseOutputMessage` containing - a `ResponseOutputText` with that content, and a `Usage` populated from - the completion's usage. - """ - msg = ChatCompletionMessage(role="assistant", content="Hello") - choice = Choice(index=0, finish_reason="stop", message=msg) - chat = ChatCompletion( - id="resp-id", - created=0, - model="fake", - object="chat.completion", - choices=[choice], - usage=CompletionUsage(completion_tokens=5, prompt_tokens=7, total_tokens=12), - ) - - async def patched_fetch_response(self, *args, **kwargs): - return chat - - monkeypatch.setattr( - OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response - ) - model = OpenAIProvider(use_responses=False).get_model("gpt-4") - resp: ModelResponse = await model.get_response( - system_instructions=None, - input="", - model_settings=ModelSettings(), - tools=[], - output_schema=None, - handoffs=[], - tracing=ModelTracing.DISABLED, - ) - # Should have produced exactly one output message with one text part - assert isinstance(resp, ModelResponse) - assert len(resp.output) == 1 - assert isinstance(resp.output[0], ResponseOutputMessage) - msg_item = resp.output[0] - assert len(msg_item.content) == 1 - assert isinstance(msg_item.content[0], ResponseOutputText) - assert msg_item.content[0].text == "Hello" - # Usage should be preserved from underlying ChatCompletion.usage - assert resp.usage.input_tokens == 7 - assert resp.usage.output_tokens == 5 - assert resp.usage.total_tokens == 12 - assert resp.referenceable_id is None - - -@pytest.mark.allow_call_model_methods -@pytest.mark.asyncio -async def test_get_response_with_refusal(monkeypatch) -> None: - """ - When the model returns a ChatCompletionMessage with a `refusal` instead - of normal `content`, `get_response` should produce a single - `ResponseOutputMessage` containing a `ResponseOutputRefusal` part. - """ - msg = ChatCompletionMessage(role="assistant", refusal="No thanks") - choice = Choice(index=0, finish_reason="stop", message=msg) - chat = ChatCompletion( - id="resp-id", - created=0, - model="fake", - object="chat.completion", - choices=[choice], - usage=None, - ) - - async def patched_fetch_response(self, *args, **kwargs): - return chat - - monkeypatch.setattr( - OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response - ) - model = OpenAIProvider(use_responses=False).get_model("gpt-4") - resp: ModelResponse = await model.get_response( - system_instructions=None, - input="", - model_settings=ModelSettings(), - tools=[], - output_schema=None, - handoffs=[], - tracing=ModelTracing.DISABLED, - ) - assert len(resp.output) == 1 - assert isinstance(resp.output[0], ResponseOutputMessage) - refusal_part = resp.output[0].content[0] - assert isinstance(refusal_part, ResponseOutputRefusal) - assert refusal_part.refusal == "No thanks" - # With no usage from the completion, usage defaults to zeros. - assert resp.usage.requests == 0 - assert resp.usage.input_tokens == 0 - assert resp.usage.output_tokens == 0 - - -@pytest.mark.allow_call_model_methods -@pytest.mark.asyncio -async def test_get_response_with_tool_call(monkeypatch) -> None: - """ - If the ChatCompletionMessage includes one or more tool_calls, `get_response` - should append corresponding `ResponseFunctionToolCall` items after the - assistant message item with matching name/arguments. - """ - tool_call = ChatCompletionMessageToolCall( - id="call-id", - type="function", - function=Function(name="do_thing", arguments="{'x':1}"), - ) - msg = ChatCompletionMessage(role="assistant", content="Hi", tool_calls=[tool_call]) - choice = Choice(index=0, finish_reason="stop", message=msg) - chat = ChatCompletion( - id="resp-id", - created=0, - model="fake", - object="chat.completion", - choices=[choice], - usage=None, - ) - - async def patched_fetch_response(self, *args, **kwargs): - return chat - - monkeypatch.setattr( - OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response - ) - model = OpenAIProvider(use_responses=False).get_model("gpt-4") - resp: ModelResponse = await model.get_response( - system_instructions=None, - input="", - model_settings=ModelSettings(), - tools=[], - output_schema=None, - handoffs=[], - tracing=ModelTracing.DISABLED, - ) - # Expect a message item followed by a function tool call item. - assert len(resp.output) == 2 - assert isinstance(resp.output[0], ResponseOutputMessage) - fn_call_item = resp.output[1] - assert isinstance(fn_call_item, ResponseFunctionToolCall) - assert fn_call_item.call_id == "call-id" - assert fn_call_item.name == "do_thing" - assert fn_call_item.arguments == "{'x':1}" - - -@pytest.mark.asyncio -async def test_fetch_response_non_stream(monkeypatch) -> None: - """ - Verify that `_fetch_response` builds the correct OpenAI API call when not - streaming and returns the ChatCompletion object directly. We supply a - dummy ChatCompletion through a stubbed OpenAI client and inspect the - captured kwargs. - """ - - # Dummy completions to record kwargs - class DummyCompletions: - def __init__(self) -> None: - self.kwargs: dict[str, Any] = {} - - async def create(self, **kwargs: Any) -> Any: - self.kwargs = kwargs - return chat - - class DummyClient: - def __init__(self, completions: DummyCompletions) -> None: - self.chat = type("_Chat", (), {"completions": completions})() - self.base_url = httpx.URL("http://fake") - - msg = ChatCompletionMessage(role="assistant", content="ignored") - choice = Choice(index=0, finish_reason="stop", message=msg) - chat = ChatCompletion( - id="resp-id", - created=0, - model="fake", - object="chat.completion", - choices=[choice], - ) - completions = DummyCompletions() - dummy_client = DummyClient(completions) - model = OpenAIChatCompletionsModel(model="gpt-4", openai_client=dummy_client) # type: ignore - # Execute the private fetch with a system instruction and simple string input. - with generation_span(disabled=True) as span: - result = await model._fetch_response( - system_instructions="sys", - input="hi", - model_settings=ModelSettings(), - tools=[], - output_schema=None, - handoffs=[], - span=span, - tracing=ModelTracing.DISABLED, - stream=False, - ) - assert result is chat - # Ensure expected args were passed through to OpenAI client. - kwargs = completions.kwargs - assert kwargs["stream"] is False - assert kwargs["model"] == "gpt-4" - assert kwargs["messages"][0]["role"] == "system" - assert kwargs["messages"][0]["content"] == "sys" - assert kwargs["messages"][1]["role"] == "user" - # Defaults for optional fields become the NOT_GIVEN sentinel - assert kwargs["tools"] is NOT_GIVEN - assert kwargs["tool_choice"] is NOT_GIVEN - assert kwargs["response_format"] is NOT_GIVEN - assert kwargs["stream_options"] is NOT_GIVEN - - -@pytest.mark.asyncio -async def test_fetch_response_stream(monkeypatch) -> None: - """ - When `stream=True`, `_fetch_response` should return a bare `Response` - object along with the underlying async stream. The OpenAI client call - should include `stream_options` to request usage-delimited chunks. - """ - - async def event_stream() -> AsyncIterator[ChatCompletionChunk]: - if False: # pragma: no cover - yield # pragma: no cover - - class DummyCompletions: - def __init__(self) -> None: - self.kwargs: dict[str, Any] = {} - - async def create(self, **kwargs: Any) -> Any: - self.kwargs = kwargs - return event_stream() - - class DummyClient: - def __init__(self, completions: DummyCompletions) -> None: - self.chat = type("_Chat", (), {"completions": completions})() - self.base_url = httpx.URL("http://fake") - - completions = DummyCompletions() - dummy_client = DummyClient(completions) - model = OpenAIChatCompletionsModel(model="gpt-4", openai_client=dummy_client) # type: ignore - with generation_span(disabled=True) as span: - response, stream = await model._fetch_response( - system_instructions=None, - input="hi", - model_settings=ModelSettings(), - tools=[], - output_schema=None, - handoffs=[], - span=span, - tracing=ModelTracing.DISABLED, - stream=True, - ) - # Check OpenAI client was called for streaming - assert completions.kwargs["stream"] is True - assert completions.kwargs["stream_options"] == {"include_usage": True} - # Response is a proper openai Response - assert isinstance(response, Response) - assert response.id == FAKE_RESPONSES_ID - assert response.model == "gpt-4" - assert response.object == "response" - assert response.output == [] - # We returned the async iterator produced by our dummy. - assert hasattr(stream, "__aiter__") diff --git a/pkg/hanzo-agent/tests/test_openai_chatcompletions_converter.py b/pkg/hanzo-agent/tests/test_openai_chatcompletions_converter.py deleted file mode 100644 index 46abc98fd..000000000 --- a/pkg/hanzo-agent/tests/test_openai_chatcompletions_converter.py +++ /dev/null @@ -1,433 +0,0 @@ -# Copyright (c) Hanzo AI -# -# Licensed under the MIT License. -# See LICENSE file in the project root for full license information. - -""" -Unit tests for the internal `_Converter` class defined in -`agents.models.openai_chatcompletions`. The converter is responsible for -translating between internal "item" structures (e.g., `ResponseOutputMessage` -and related types from `openai.types.responses`) and the ChatCompletion message -structures defined by the Hanzo AI client library. - -These tests exercise both conversion directions: - -- `_Converter.message_to_output_items` turns a `ChatCompletionMessage` (as - returned by the Hanzo AI API) into a list of `ResponseOutputItem` instances. - -- `_Converter.items_to_messages` takes in either a simple string prompt, or a - list of input/output items such as `ResponseOutputMessage` and - `ResponseFunctionToolCallParam` dicts, and constructs a list of - `ChatCompletionMessageParam` dicts suitable for sending back to the API. -""" - -from __future__ import annotations - -from typing import Literal, cast - -import pytest -from openai.types.chat import ChatCompletionMessage, ChatCompletionMessageToolCall -from openai.types.chat.chat_completion_message_tool_call import Function -from openai.types.responses import ( - ResponseFunctionToolCall, - ResponseFunctionToolCallParam, - ResponseInputTextParam, - ResponseOutputMessage, - ResponseOutputRefusal, - ResponseOutputText, -) -from openai.types.responses.response_input_item_param import FunctionCallOutput - -from agents.agent_output import AgentOutputSchema -from agents.exceptions import UserError -from agents.items import TResponseInputItem -from agents.models.fake_id import FAKE_RESPONSES_ID -from agents.models.openai_chatcompletions import _Converter - - -def test_message_to_output_items_with_text_only(): - """ - Make sure a simple ChatCompletionMessage with string content is converted - into a single ResponseOutputMessage containing one ResponseOutputText. - """ - msg = ChatCompletionMessage(role="assistant", content="Hello") - items = _Converter.message_to_output_items(msg) - # Expect exactly one output item (the message) - assert len(items) == 1 - message_item = cast(ResponseOutputMessage, items[0]) - assert message_item.id == FAKE_RESPONSES_ID - assert message_item.role == "assistant" - assert message_item.type == "message" - assert message_item.status == "completed" - # Message content should have exactly one text part with the same text. - assert len(message_item.content) == 1 - text_part = cast(ResponseOutputText, message_item.content[0]) - assert text_part.type == "output_text" - assert text_part.text == "Hello" - - -def test_message_to_output_items_with_refusal(): - """ - Make sure a message with a refusal string produces a ResponseOutputMessage - with a ResponseOutputRefusal content part. - """ - msg = ChatCompletionMessage(role="assistant", refusal="I'm sorry") - items = _Converter.message_to_output_items(msg) - assert len(items) == 1 - message_item = cast(ResponseOutputMessage, items[0]) - assert len(message_item.content) == 1 - refusal_part = cast(ResponseOutputRefusal, message_item.content[0]) - assert refusal_part.type == "refusal" - assert refusal_part.refusal == "I'm sorry" - - -def test_message_to_output_items_with_tool_call(): - """ - If the ChatCompletionMessage contains one or more tool_calls, they should - be reflected as separate `ResponseFunctionToolCall` items appended after - the message item. - """ - tool_call = ChatCompletionMessageToolCall( - id="tool1", - type="function", - function=Function(name="myfn", arguments='{"x":1}'), - ) - msg = ChatCompletionMessage(role="assistant", content="Hi", tool_calls=[tool_call]) - items = _Converter.message_to_output_items(msg) - # Should produce a message item followed by one function tool call item - assert len(items) == 2 - message_item = cast(ResponseOutputMessage, items[0]) - assert isinstance(message_item, ResponseOutputMessage) - fn_call_item = cast(ResponseFunctionToolCall, items[1]) - assert fn_call_item.id == FAKE_RESPONSES_ID - assert fn_call_item.call_id == tool_call.id - assert fn_call_item.name == tool_call.function.name - assert fn_call_item.arguments == tool_call.function.arguments - assert fn_call_item.type == "function_call" - - -def test_items_to_messages_with_string_user_content(): - """ - A simple string as the items argument should be converted into a user - message param dict with the same content. - """ - result = _Converter.items_to_messages("Ask me anything") - assert isinstance(result, list) - assert len(result) == 1 - msg = result[0] - assert msg["role"] == "user" - assert msg["content"] == "Ask me anything" - - -def test_items_to_messages_with_easy_input_message(): - """ - Given an easy input message dict (just role/content), the converter should - produce the appropriate ChatCompletionMessageParam with the same content. - """ - items: list[TResponseInputItem] = [ - { - "role": "user", - "content": "How are you?", - } - ] - messages = _Converter.items_to_messages(items) - assert len(messages) == 1 - out = messages[0] - assert out["role"] == "user" - # For simple string inputs, the converter returns the content as a bare string - assert out["content"] == "How are you?" - - -def test_items_to_messages_with_output_message_and_function_call(): - """ - Given a sequence of one ResponseOutputMessageParam followed by a - ResponseFunctionToolCallParam, the converter should produce a single - ChatCompletionAssistantMessageParam that includes both the assistant's - textual content and a populated `tool_calls` reflecting the function call. - """ - # Construct output message param dict with two content parts. - output_text: ResponseOutputText = ResponseOutputText( - text="Part 1", - type="output_text", - annotations=[], - ) - refusal: ResponseOutputRefusal = ResponseOutputRefusal( - refusal="won't do that", - type="refusal", - ) - resp_msg: ResponseOutputMessage = ResponseOutputMessage( - id="42", - type="message", - role="assistant", - status="completed", - content=[output_text, refusal], - ) - # Construct a function call item dict (as if returned from model) - func_item: ResponseFunctionToolCallParam = { - "id": "99", - "call_id": "abc", - "name": "math", - "arguments": "{}", - "type": "function_call", - } - items: list[TResponseInputItem] = [ - resp_msg.model_dump(), # type: ignore - func_item, - ] - messages = _Converter.items_to_messages(items) - # Should return a single assistant message - assert len(messages) == 1 - assistant = messages[0] - assert assistant["role"] == "assistant" - # Content combines text portions of the output message - assert "content" in assistant - assert assistant["content"] == "Part 1" - # Refusal in output message should be represented in assistant message - assert "refusal" in assistant - assert assistant["refusal"] == refusal.refusal - # Tool calls list should contain one ChatCompletionMessageToolCall dict - tool_calls = assistant.get("tool_calls") - assert isinstance(tool_calls, list) - assert len(tool_calls) == 1 - tool_call = tool_calls[0] - assert tool_call["type"] == "function" - assert tool_call["function"]["name"] == "math" - assert tool_call["function"]["arguments"] == "{}" - - -def test_convert_tool_choice_handles_standard_and_named_options() -> None: - """ - The `_Converter.convert_tool_choice` method should return NOT_GIVEN - if no choice is provided, pass through values like "auto", "required", - or "none" unchanged, and translate any other string into a function - selection dict. - """ - assert _Converter.convert_tool_choice(None).__class__.__name__ == "NotGiven" - assert _Converter.convert_tool_choice("auto") == "auto" - assert _Converter.convert_tool_choice("required") == "required" - assert _Converter.convert_tool_choice("none") == "none" - tool_choice_dict = _Converter.convert_tool_choice("mytool") - assert isinstance(tool_choice_dict, dict) - assert tool_choice_dict["type"] == "function" - assert tool_choice_dict["function"]["name"] == "mytool" - - -def test_convert_response_format_returns_not_given_for_plain_text_and_dict_for_schemas() -> ( - None -): - """ - The `_Converter.convert_response_format` method should return NOT_GIVEN - when no output schema is provided or if the output schema indicates - plain text. For structured output schemas, it should return a dict - with type `json_schema` and include the generated JSON schema and - strict flag from the provided `AgentOutputSchema`. - """ - # when output is plain text (schema None or output_type str), do not include response_format - assert _Converter.convert_response_format(None).__class__.__name__ == "NotGiven" - assert ( - _Converter.convert_response_format(AgentOutputSchema(str)).__class__.__name__ - == "NotGiven" - ) - # For e.g. integer output, we expect a response_format dict - schema = AgentOutputSchema(int) - resp_format = _Converter.convert_response_format(schema) - assert isinstance(resp_format, dict) - assert resp_format["type"] == "json_schema" - assert resp_format["json_schema"]["name"] == "final_output" - assert "strict" in resp_format["json_schema"] - assert resp_format["json_schema"]["strict"] == schema.strict_json_schema - assert "schema" in resp_format["json_schema"] - assert resp_format["json_schema"]["schema"] == schema.json_schema() - - -def test_items_to_messages_with_function_output_item(): - """ - A function call output item should be converted into a tool role message - dict with the appropriate tool_call_id and content. - """ - func_output_item: FunctionCallOutput = { - "type": "function_call_output", - "call_id": "somecall", - "output": '{"foo": "bar"}', - } - messages = _Converter.items_to_messages([func_output_item]) - assert len(messages) == 1 - tool_msg = messages[0] - assert tool_msg["role"] == "tool" - assert tool_msg["tool_call_id"] == func_output_item["call_id"] - assert tool_msg["content"] == func_output_item["output"] - - -def test_extract_all_and_text_content_for_strings_and_lists(): - """ - The converter provides helpers for extracting user-supplied message content - either as a simple string or as a list of `input_text` dictionaries. - When passed a bare string, both `extract_all_content` and - `extract_text_content` should return the string unchanged. - When passed a list of input dictionaries, `extract_all_content` should - produce a list of `ChatCompletionContentPart` dicts, and `extract_text_content` - should filter to only the textual parts. - """ - prompt = "just text" - assert _Converter.extract_all_content(prompt) == prompt - assert _Converter.extract_text_content(prompt) == prompt - text1: ResponseInputTextParam = {"type": "input_text", "text": "one"} - text2: ResponseInputTextParam = {"type": "input_text", "text": "two"} - all_parts = _Converter.extract_all_content([text1, text2]) - assert isinstance(all_parts, list) - assert len(all_parts) == 2 - assert all_parts[0]["type"] == "text" and all_parts[0]["text"] == "one" - assert all_parts[1]["type"] == "text" and all_parts[1]["text"] == "two" - text_parts = _Converter.extract_text_content([text1, text2]) - assert isinstance(text_parts, list) - assert all(p["type"] == "text" for p in text_parts) - assert [p["text"] for p in text_parts] == ["one", "two"] - - -def test_items_to_messages_handles_system_and_developer_roles(): - """ - Roles other than `user` (e.g. `system` and `developer`) need to be - converted appropriately whether provided as simple dicts or as full - `message` typed dicts. - """ - sys_items: list[TResponseInputItem] = [{"role": "system", "content": "setup"}] - sys_msgs = _Converter.items_to_messages(sys_items) - assert len(sys_msgs) == 1 - assert sys_msgs[0]["role"] == "system" - assert sys_msgs[0]["content"] == "setup" - dev_items: list[TResponseInputItem] = [{"role": "developer", "content": "debug"}] - dev_msgs = _Converter.items_to_messages(dev_items) - assert len(dev_msgs) == 1 - assert dev_msgs[0]["role"] == "developer" - assert dev_msgs[0]["content"] == "debug" - - -def test_maybe_input_message_allows_message_typed_dict(): - """ - The `_Converter.maybe_input_message` should recognize a dict with - "type": "message" and a supported role as an input message. Ensure - that such dicts are passed through by `items_to_messages`. - """ - # Construct a dict with the proper required keys for a ResponseInputParam.Message - message_dict: TResponseInputItem = { - "type": "message", - "role": "user", - "content": "hi", - } - assert _Converter.maybe_input_message(message_dict) is not None - # items_to_messages should process this correctly - msgs = _Converter.items_to_messages([message_dict]) - assert len(msgs) == 1 - assert msgs[0]["role"] == "user" - assert msgs[0]["content"] == "hi" - - -def test_tool_call_conversion(): - """ - Test that tool calls are converted correctly. - """ - function_call = ResponseFunctionToolCallParam( - id="tool1", - call_id="abc", - name="math", - arguments="{}", - type="function_call", - ) - - messages = _Converter.items_to_messages([function_call]) - assert len(messages) == 1 - tool_msg = messages[0] - assert tool_msg["role"] == "assistant" - assert tool_msg.get("content") is None - tool_calls = list(tool_msg.get("tool_calls", [])) - assert len(tool_calls) == 1 - - tool_call = tool_calls[0] - assert tool_call["id"] == function_call["call_id"] - assert tool_call["function"]["name"] == function_call["name"] - assert tool_call["function"]["arguments"] == function_call["arguments"] - - -@pytest.mark.parametrize("role", ["user", "system", "developer"]) -def test_input_message_with_all_roles(role: str): - """ - The `_Converter.maybe_input_message` should recognize a dict with - "type": "message" and a supported role as an input message. Ensure - that such dicts are passed through by `items_to_messages`. - """ - # Construct a dict with the proper required keys for a ResponseInputParam.Message - casted_role = cast(Literal["user", "system", "developer"], role) - message_dict: TResponseInputItem = { - "type": "message", - "role": casted_role, - "content": "hi", - } - assert _Converter.maybe_input_message(message_dict) is not None - # items_to_messages should process this correctly - msgs = _Converter.items_to_messages([message_dict]) - assert len(msgs) == 1 - assert msgs[0]["role"] == casted_role - assert msgs[0]["content"] == "hi" - - -def test_item_reference_errors(): - """ - Test that item references are converted correctly. - """ - with pytest.raises(UserError): - _Converter.items_to_messages( - [ - { - "type": "item_reference", - "id": "item1", - } - ] - ) - - -class TestObject: - pass - - -def test_unknown_object_errors(): - """ - Test that unknown objects are converted correctly. - """ - with pytest.raises(UserError, match="Unhandled item type or structure"): - # Purposely ignore the type error - _Converter.items_to_messages([TestObject()]) # type: ignore - - -def test_assistant_messages_in_history(): - """ - Test that assistant messages are added to the history. - """ - messages = _Converter.items_to_messages( - [ - { - "role": "user", - "content": "Hello", - }, - { - "role": "assistant", - "content": "Hello?", - }, - { - "role": "user", - "content": "What was my Name?", - }, - ] - ) - - assert messages == [ - {"role": "user", "content": "Hello"}, - {"role": "assistant", "content": "Hello?"}, - {"role": "user", "content": "What was my Name?"}, - ] - assert len(messages) == 3 - assert messages[0]["role"] == "user" - assert messages[0]["content"] == "Hello" - assert messages[1]["role"] == "assistant" - assert messages[1]["content"] == "Hello?" - assert messages[2]["role"] == "user" - assert messages[2]["content"] == "What was my Name?" diff --git a/pkg/hanzo-agent/tests/test_openai_chatcompletions_stream.py b/pkg/hanzo-agent/tests/test_openai_chatcompletions_stream.py deleted file mode 100644 index e8f384083..000000000 --- a/pkg/hanzo-agent/tests/test_openai_chatcompletions_stream.py +++ /dev/null @@ -1,289 +0,0 @@ -from collections.abc import AsyncIterator - -import pytest -from openai.types.chat.chat_completion_chunk import ( - ChatCompletionChunk, - Choice, - ChoiceDelta, - ChoiceDeltaToolCall, - ChoiceDeltaToolCallFunction, -) -from openai.types.completion_usage import CompletionUsage -from openai.types.responses import ( - Response, - ResponseFunctionToolCall, - ResponseOutputMessage, - ResponseOutputRefusal, - ResponseOutputText, -) - -from agents.model_settings import ModelSettings -from agents.models.interface import ModelTracing -from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel -from agents.models.openai_provider import OpenAIProvider - - -@pytest.mark.allow_call_model_methods -@pytest.mark.asyncio -async def test_stream_response_yields_events_for_text_content(monkeypatch) -> None: - """ - Validate that `stream_response` emits the correct sequence of events when - streaming a simple assistant message consisting of plain text content. - We simulate two chunks of text returned from the chat completion stream. - """ - # Create two chunks that will be emitted by the fake stream. - chunk1 = ChatCompletionChunk( - id="chunk-id", - created=1, - model="fake", - object="chat.completion.chunk", - choices=[Choice(index=0, delta=ChoiceDelta(content="He"))], - ) - # Mark last chunk with usage so stream_response knows this is final. - chunk2 = ChatCompletionChunk( - id="chunk-id", - created=1, - model="fake", - object="chat.completion.chunk", - choices=[Choice(index=0, delta=ChoiceDelta(content="llo"))], - usage=CompletionUsage(completion_tokens=5, prompt_tokens=7, total_tokens=12), - ) - - async def fake_stream() -> AsyncIterator[ChatCompletionChunk]: - for c in (chunk1, chunk2): - yield c - - # Patch _fetch_response to inject our fake stream - async def patched_fetch_response(self, *args, **kwargs): - # `_fetch_response` is expected to return a Response skeleton and the async stream - resp = Response( - id="resp-id", - created_at=0, - model="fake-model", - object="response", - output=[], - tool_choice="none", - tools=[], - parallel_tool_calls=False, - ) - return resp, fake_stream() - - monkeypatch.setattr( - OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response - ) - model = OpenAIProvider(use_responses=False).get_model("gpt-4") - output_events = [] - async for event in model.stream_response( - system_instructions=None, - input="", - model_settings=ModelSettings(), - tools=[], - output_schema=None, - handoffs=[], - tracing=ModelTracing.DISABLED, - ): - output_events.append(event) - # We expect a response.created, then a response.output_item.added, content part added, - # two content delta events (for "He" and "llo"), a content part done, the assistant message - # output_item.done, and finally response.completed. - # There should be 8 events in total. - assert len(output_events) == 8 - # First event indicates creation. - assert output_events[0].type == "response.created" - # The output item added and content part added events should mark the assistant message. - assert output_events[1].type == "response.output_item.added" - assert output_events[2].type == "response.content_part.added" - # Two text delta events. - assert output_events[3].type == "response.output_text.delta" - assert output_events[3].delta == "He" - assert output_events[4].type == "response.output_text.delta" - assert output_events[4].delta == "llo" - # After streaming, the content part and item should be marked done. - assert output_events[5].type == "response.content_part.done" - assert output_events[6].type == "response.output_item.done" - # Last event indicates completion of the stream. - assert output_events[7].type == "response.completed" - # The completed response should have one output message with full text. - completed_resp = output_events[7].response - assert isinstance(completed_resp.output[0], ResponseOutputMessage) - assert isinstance(completed_resp.output[0].content[0], ResponseOutputText) - assert completed_resp.output[0].content[0].text == "Hello" - - assert completed_resp.usage, "usage should not be None" - assert completed_resp.usage.input_tokens == 7 - assert completed_resp.usage.output_tokens == 5 - assert completed_resp.usage.total_tokens == 12 - - -@pytest.mark.allow_call_model_methods -@pytest.mark.asyncio -async def test_stream_response_yields_events_for_refusal_content(monkeypatch) -> None: - """ - Validate that when the model streams a refusal string instead of normal content, - `stream_response` emits the appropriate sequence of events including - `response.refusal.delta` events for each chunk of the refusal message and - constructs a completed assistant message with a `ResponseOutputRefusal` part. - """ - # Simulate refusal text coming in two pieces, like content but using the `refusal` - # field on the delta rather than `content`. - chunk1 = ChatCompletionChunk( - id="chunk-id", - created=1, - model="fake", - object="chat.completion.chunk", - choices=[Choice(index=0, delta=ChoiceDelta(refusal="No"))], - ) - chunk2 = ChatCompletionChunk( - id="chunk-id", - created=1, - model="fake", - object="chat.completion.chunk", - choices=[Choice(index=0, delta=ChoiceDelta(refusal="Thanks"))], - usage=CompletionUsage(completion_tokens=2, prompt_tokens=2, total_tokens=4), - ) - - async def fake_stream() -> AsyncIterator[ChatCompletionChunk]: - for c in (chunk1, chunk2): - yield c - - async def patched_fetch_response(self, *args, **kwargs): - resp = Response( - id="resp-id", - created_at=0, - model="fake-model", - object="response", - output=[], - tool_choice="none", - tools=[], - parallel_tool_calls=False, - ) - return resp, fake_stream() - - monkeypatch.setattr( - OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response - ) - model = OpenAIProvider(use_responses=False).get_model("gpt-4") - output_events = [] - async for event in model.stream_response( - system_instructions=None, - input="", - model_settings=ModelSettings(), - tools=[], - output_schema=None, - handoffs=[], - tracing=ModelTracing.DISABLED, - ): - output_events.append(event) - # Expect sequence similar to text: created, output_item.added, content part added, - # two refusal delta events, content part done, output_item.done, completed. - assert len(output_events) == 8 - assert output_events[0].type == "response.created" - assert output_events[1].type == "response.output_item.added" - assert output_events[2].type == "response.content_part.added" - assert output_events[3].type == "response.refusal.delta" - assert output_events[3].delta == "No" - assert output_events[4].type == "response.refusal.delta" - assert output_events[4].delta == "Thanks" - assert output_events[5].type == "response.content_part.done" - assert output_events[6].type == "response.output_item.done" - assert output_events[7].type == "response.completed" - completed_resp = output_events[7].response - assert isinstance(completed_resp.output[0], ResponseOutputMessage) - refusal_part = completed_resp.output[0].content[0] - assert isinstance(refusal_part, ResponseOutputRefusal) - assert refusal_part.refusal == "NoThanks" - - -@pytest.mark.allow_call_model_methods -@pytest.mark.asyncio -async def test_stream_response_yields_events_for_tool_call(monkeypatch) -> None: - """ - Validate that `stream_response` emits the correct sequence of events when - the model is streaming a function/tool call instead of plain text. - The function call will be split across two chunks. - """ - # Simulate a single tool call whose ID stays constant and function name/args built over chunks. - tool_call_delta1 = ChoiceDeltaToolCall( - index=0, - id="tool-id", - function=ChoiceDeltaToolCallFunction(name="my_", arguments="arg1"), - type="function", - ) - tool_call_delta2 = ChoiceDeltaToolCall( - index=0, - id="tool-id", - function=ChoiceDeltaToolCallFunction(name="func", arguments="arg2"), - type="function", - ) - chunk1 = ChatCompletionChunk( - id="chunk-id", - created=1, - model="fake", - object="chat.completion.chunk", - choices=[Choice(index=0, delta=ChoiceDelta(tool_calls=[tool_call_delta1]))], - ) - chunk2 = ChatCompletionChunk( - id="chunk-id", - created=1, - model="fake", - object="chat.completion.chunk", - choices=[Choice(index=0, delta=ChoiceDelta(tool_calls=[tool_call_delta2]))], - usage=CompletionUsage(completion_tokens=1, prompt_tokens=1, total_tokens=2), - ) - - async def fake_stream() -> AsyncIterator[ChatCompletionChunk]: - for c in (chunk1, chunk2): - yield c - - async def patched_fetch_response(self, *args, **kwargs): - resp = Response( - id="resp-id", - created_at=0, - model="fake-model", - object="response", - output=[], - tool_choice="none", - tools=[], - parallel_tool_calls=False, - ) - return resp, fake_stream() - - monkeypatch.setattr( - OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response - ) - model = OpenAIProvider(use_responses=False).get_model("gpt-4") - output_events = [] - async for event in model.stream_response( - system_instructions=None, - input="", - model_settings=ModelSettings(), - tools=[], - output_schema=None, - handoffs=[], - tracing=ModelTracing.DISABLED, - ): - output_events.append(event) - # Sequence should be: response.created, then after loop we expect function call-related events: - # one response.output_item.added for function call, a response.function_call_arguments.delta, - # a response.output_item.done, and finally response.completed. - assert output_events[0].type == "response.created" - # The next three events are about the tool call. - assert output_events[1].type == "response.output_item.added" - # The added item should be a ResponseFunctionToolCall. - added_fn = output_events[1].item - assert isinstance(added_fn, ResponseFunctionToolCall) - assert added_fn.name == "my_func" # Name should be concatenation of both chunks. - assert added_fn.arguments == "arg1arg2" - assert output_events[2].type == "response.function_call_arguments.delta" - assert output_events[2].delta == "arg1arg2" - assert output_events[3].type == "response.output_item.done" - assert output_events[4].type == "response.completed" - assert output_events[2].delta == "arg1arg2" - assert output_events[3].type == "response.output_item.done" - assert output_events[4].type == "response.completed" - assert added_fn.name == "my_func" # Name should be concatenation of both chunks. - assert added_fn.arguments == "arg1arg2" - assert output_events[2].type == "response.function_call_arguments.delta" - assert output_events[2].delta == "arg1arg2" - assert output_events[3].type == "response.output_item.done" - assert output_events[4].type == "response.completed" diff --git a/pkg/hanzo-agent/tests/test_openai_responses_converter.py b/pkg/hanzo-agent/tests/test_openai_responses_converter.py deleted file mode 100644 index 216a2a909..000000000 --- a/pkg/hanzo-agent/tests/test_openai_responses_converter.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenAI -# -# Licensed under the MIT License. -# See LICENSE file in the project root for full license information. - -""" -Unit tests for the `Converter` class defined in -`agents.models.openai_responses`. The converter is responsible for -translating various agent tool types and output schemas into the parameter -structures expected by the OpenAI Responses API. - -We test the following aspects: - -- `convert_tool_choice` correctly maps high-level tool choice strings into - the tool choice values accepted by the Responses API, including special types - like `file_search` and `web_search`, and falling back to function names - for arbitrary string values. -- `get_response_format` returns `openai.NOT_GIVEN` for plain-text response - formats and an appropriate format dict when a JSON-structured output schema - is provided. -- `convert_tools` maps our internal `Tool` dataclasses into the appropriate - request payloads and includes list, and enforces constraints like at most - one `ComputerTool`. -""" - -import pytest -from openai import NOT_GIVEN -from pydantic import BaseModel - -from agents import ( - Agent, - AgentOutputSchema, - Computer, - ComputerTool, - FileSearchTool, - Handoff, - Tool, - UserError, - WebSearchTool, - function_tool, - handoff, -) -from agents.models.openai_responses import Converter - - -def test_convert_tool_choice_standard_values(): - """ - Make sure that the standard tool_choice values map to themselves or - to "auto"/"required"/"none" as appropriate, and that special string - values map to the appropriate dicts. - """ - assert Converter.convert_tool_choice(None) is NOT_GIVEN - assert Converter.convert_tool_choice("auto") == "auto" - assert Converter.convert_tool_choice("required") == "required" - assert Converter.convert_tool_choice("none") == "none" - # Special tool types are represented as dicts of type only. - assert Converter.convert_tool_choice("file_search") == {"type": "file_search"} - assert Converter.convert_tool_choice("web_search_preview") == { - "type": "web_search_preview" - } - assert Converter.convert_tool_choice("computer_use_preview") == { - "type": "computer_use_preview" - } - # Arbitrary string should be interpreted as a function name. - assert Converter.convert_tool_choice("my_function") == { - "type": "function", - "name": "my_function", - } - - -def test_get_response_format_plain_text_and_json_schema(): - """ - For plain text output (default, or output type of `str`), the converter - should return NOT_GIVEN, indicating no special response format constraint. - If an output schema is provided for a structured type, the converter - should return a `format` dict with the schema and strictness. The exact - JSON schema depends on the output type; we just assert that required - keys are present and that we get back the original schema. - """ - # Default output (None) should be considered plain text. - assert Converter.get_response_format(None) is NOT_GIVEN - # An explicit plain-text schema (str) should also yield NOT_GIVEN. - assert Converter.get_response_format(AgentOutputSchema(str)) is NOT_GIVEN - - # A model-based schema should produce a format dict. - class OutModel(BaseModel): - foo: int - bar: str - - out_schema = AgentOutputSchema(OutModel) - fmt = Converter.get_response_format(out_schema) - assert isinstance(fmt, dict) - assert "format" in fmt - inner = fmt["format"] - assert inner.get("type") == "json_schema" - assert inner.get("name") == "final_output" - assert isinstance(inner.get("schema"), dict) - # Should include a strict flag matching the schema's strictness setting. - assert inner.get("strict") == out_schema.strict_json_schema - - -def test_convert_tools_basic_types_and_includes(): - """ - Construct a variety of tool types and make sure `convert_tools` returns - a matching list of tool param dicts and the expected includes. Also - check that only a single computer tool is allowed. - """ - # Simple function tool - tool_fn = function_tool(lambda a: "x", name_override="fn") - # File search tool with include_search_results set - file_tool = FileSearchTool( - max_num_results=3, vector_store_ids=["vs1"], include_search_results=True - ) - # Web search tool with custom params - web_tool = WebSearchTool(user_location=None, search_context_size="high") - - # Dummy computer tool subclassing the Computer ABC with minimal methods. - class DummyComputer(Computer): - @property - def environment(self): - return "mac" - - @property - def dimensions(self): - return (800, 600) - - def screenshot(self) -> str: - raise NotImplementedError - - def click(self, x: int, y: int, button: str) -> None: - raise NotImplementedError - - def double_click(self, x: int, y: int) -> None: - raise NotImplementedError - - def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: - raise NotImplementedError - - def type(self, text: str) -> None: - raise NotImplementedError - - def wait(self) -> None: - raise NotImplementedError - - def move(self, x: int, y: int) -> None: - raise NotImplementedError - - def keypress(self, keys: list[str]) -> None: - raise NotImplementedError - - def drag(self, path: list[tuple[int, int]]) -> None: - raise NotImplementedError - - # Wrap our concrete computer in a ComputerTool for conversion. - comp_tool = ComputerTool(computer=DummyComputer()) - tools: list[Tool] = [tool_fn, file_tool, web_tool, comp_tool] - converted = Converter.convert_tools(tools, handoffs=[]) - assert isinstance(converted.tools, list) - assert isinstance(converted.includes, list) - # The includes list should have exactly the include for file search when include_search_results - # is True. - assert converted.includes == ["file_search_call.results"] - # There should be exactly four converted tool dicts. - assert len(converted.tools) == 4 - # Extract types and verify. - types = [ct["type"] for ct in converted.tools] - assert "function" in types - assert "file_search" in types - assert "web_search_preview" in types - assert "computer_use_preview" in types - # Verify file search tool contains max_num_results and vector_store_ids - file_params = next(ct for ct in converted.tools if ct["type"] == "file_search") - assert file_params.get("max_num_results") == file_tool.max_num_results - assert file_params.get("vector_store_ids") == file_tool.vector_store_ids - # Verify web search tool contains user_location and search_context_size - web_params = next( - ct for ct in converted.tools if ct["type"] == "web_search_preview" - ) - assert web_params.get("user_location") == web_tool.user_location - assert web_params.get("search_context_size") == web_tool.search_context_size - # Verify computer tool contains environment and computed dimensions - comp_params = next( - ct for ct in converted.tools if ct["type"] == "computer_use_preview" - ) - assert comp_params.get("environment") == "mac" - assert comp_params.get("display_width") == 800 - assert comp_params.get("display_height") == 600 - # The function tool dict should have name and description fields. - fn_params = next(ct for ct in converted.tools if ct["type"] == "function") - assert fn_params.get("name") == tool_fn.name - assert fn_params.get("description") == tool_fn.description - - # Only one computer tool should be allowed. - with pytest.raises(UserError): - Converter.convert_tools(tools=[comp_tool, comp_tool], handoffs=[]) - - -def test_convert_tools_includes_handoffs(): - """ - When handoff objects are included, `convert_tools` should append their - tool param dicts after tools and include appropriate descriptions. - """ - agent = Agent(name="support", handoff_description="Handles support") - handoff_obj = handoff(agent) - converted = Converter.convert_tools(tools=[], handoffs=[handoff_obj]) - assert isinstance(converted.tools, list) - assert len(converted.tools) == 1 - handoff_tool = converted.tools[0] - assert handoff_tool.get("type") == "function" - assert handoff_tool.get("name") == Handoff.default_tool_name(agent) - assert handoff_tool.get("description") == Handoff.default_tool_description(agent) - # No includes for handoffs by default. - assert converted.includes == [] diff --git a/pkg/hanzo-agent/tests/test_output_tool.py b/pkg/hanzo-agent/tests/test_output_tool.py deleted file mode 100644 index f3ddc3b32..000000000 --- a/pkg/hanzo-agent/tests/test_output_tool.py +++ /dev/null @@ -1,134 +0,0 @@ -import json - -import pytest -from pydantic import BaseModel -from typing_extensions import TypedDict - -from agents import ( - Agent, - AgentOutputSchema, - ModelBehaviorError, - Runner, - UserError, - _utils, -) -from agents.agent_output import _WRAPPER_DICT_KEY - - -def test_plain_text_output(): - agent = Agent(name="test") - output_schema = Runner._get_output_schema(agent) - assert ( - not output_schema - ), "Shouldn't have an output tool config without an output type" - - agent = Agent(name="test", output_type=str) - assert ( - not output_schema - ), "Shouldn't have an output tool config with str output type" - - -class Foo(BaseModel): - bar: str - - -def test_structured_output_pydantic(): - agent = Agent(name="test", output_type=Foo) - output_schema = Runner._get_output_schema(agent) - assert ( - output_schema - ), "Should have an output tool config with a structured output type" - - assert output_schema.output_type == Foo, "Should have the correct output type" - assert not output_schema._is_wrapped, "Pydantic objects should not be wrapped" - for key, value in Foo.model_json_schema().items(): - assert output_schema.json_schema()[key] == value - - json_str = Foo(bar="baz").model_dump_json() - validated = output_schema.validate_json(json_str) - assert validated == Foo(bar="baz") - - -class Bar(TypedDict): - bar: str - - -def test_structured_output_typed_dict(): - agent = Agent(name="test", output_type=Bar) - output_schema = Runner._get_output_schema(agent) - assert ( - output_schema - ), "Should have an output tool config with a structured output type" - assert output_schema.output_type == Bar, "Should have the correct output type" - assert not output_schema._is_wrapped, "TypedDicts should not be wrapped" - - json_str = json.dumps(Bar(bar="baz")) - validated = output_schema.validate_json(json_str) - assert validated == Bar(bar="baz") - - -def test_structured_output_list(): - agent = Agent(name="test", output_type=list[str]) - output_schema = Runner._get_output_schema(agent) - assert ( - output_schema - ), "Should have an output tool config with a structured output type" - assert output_schema.output_type == list[str], "Should have the correct output type" - assert output_schema._is_wrapped, "Lists should be wrapped" - - # This is testing implementation details, but it's useful to make sure this doesn't break - json_str = json.dumps({_WRAPPER_DICT_KEY: ["foo", "bar"]}) - validated = output_schema.validate_json(json_str) - assert validated == ["foo", "bar"] - - -def test_bad_json_raises_error(mocker): - agent = Agent(name="test", output_type=Foo) - output_schema = Runner._get_output_schema(agent) - assert ( - output_schema - ), "Should have an output tool config with a structured output type" - - with pytest.raises(ModelBehaviorError): - output_schema.validate_json("not valid json") - - agent = Agent(name="test", output_type=list[str]) - output_schema = Runner._get_output_schema(agent) - assert ( - output_schema - ), "Should have an output tool config with a structured output type" - - mock_validate_json = mocker.patch.object(_utils, "validate_json") - mock_validate_json.return_value = ["foo"] - - with pytest.raises(ModelBehaviorError): - output_schema.validate_json(json.dumps(["foo"])) - - mock_validate_json.return_value = {"value": "foo"} - - with pytest.raises(ModelBehaviorError): - output_schema.validate_json(json.dumps(["foo"])) - - -def test_plain_text_obj_doesnt_produce_schema(): - output_wrapper = AgentOutputSchema(output_type=str) - with pytest.raises(UserError): - output_wrapper.json_schema() - - -def test_structured_output_is_strict(): - output_wrapper = AgentOutputSchema(output_type=Foo) - assert output_wrapper.strict_json_schema - for key, value in Foo.model_json_schema().items(): - assert output_wrapper.json_schema()[key] == value - - assert ( - "additionalProperties" in output_wrapper.json_schema() - and not output_wrapper.json_schema()["additionalProperties"] - ) - - -def test_setting_strict_false_works(): - output_wrapper = AgentOutputSchema(output_type=Foo, strict_json_schema=False) - assert not output_wrapper.strict_json_schema - assert output_wrapper.json_schema() == Foo.model_json_schema() diff --git a/pkg/hanzo-agent/tests/test_responses.py b/pkg/hanzo-agent/tests/test_responses.py deleted file mode 100644 index 7fc5406e6..000000000 --- a/pkg/hanzo-agent/tests/test_responses.py +++ /dev/null @@ -1,78 +0,0 @@ -from __future__ import annotations - -from typing import Any - -from openai.types.responses import ( - ResponseFunctionToolCall, - ResponseOutputItem, - ResponseOutputMessage, - ResponseOutputText, -) - -from agents import ( - Agent, - FunctionTool, - Handoff, - TResponseInputItem, - default_tool_error_function, - function_tool, -) - - -def get_text_input_item(content: str) -> TResponseInputItem: - return { - "content": content, - "role": "user", - } - - -def get_text_message(content: str) -> ResponseOutputItem: - return ResponseOutputMessage( - id="1", - type="message", - role="assistant", - content=[ResponseOutputText(text=content, type="output_text", annotations=[])], - status="completed", - ) - - -def get_function_tool( - name: str | None = None, return_value: str | None = None, hide_errors: bool = False -) -> FunctionTool: - def _foo() -> str: - return return_value or "result_ok" - - return function_tool( - _foo, - name_override=name, - failure_error_function=None if hide_errors else default_tool_error_function, - ) - - -def get_function_tool_call( - name: str, arguments: str | None = None -) -> ResponseOutputItem: - return ResponseFunctionToolCall( - id="1", - call_id="2", - type="function_call", - name=name, - arguments=arguments or "", - ) - - -def get_handoff_tool_call( - to_agent: Agent[Any], override_name: str | None = None, args: str | None = None -) -> ResponseOutputItem: - name = override_name or Handoff.default_tool_name(to_agent) - return get_function_tool_call(name, args) - - -def get_final_output_message(args: str) -> ResponseOutputItem: - return ResponseOutputMessage( - id="1", - type="message", - role="assistant", - content=[ResponseOutputText(text=args, type="output_text", annotations=[])], - status="completed", - ) diff --git a/pkg/hanzo-agent/tests/test_responses_tracing.py b/pkg/hanzo-agent/tests/test_responses_tracing.py deleted file mode 100644 index 68a25f136..000000000 --- a/pkg/hanzo-agent/tests/test_responses_tracing.py +++ /dev/null @@ -1,272 +0,0 @@ -import pytest -from openai import AsyncOpenAI -from openai.types.responses import ResponseCompletedEvent - -from agents import ModelSettings, ModelTracing, OpenAIResponsesModel, trace -from agents.tracing.span_data import ResponseSpanData -from tests import fake_model - -from .testing_processor import fetch_ordered_spans - - -class DummyTracing: - def is_disabled(self): - return False - - -class DummyUsage: - def __init__(self, input_tokens=1, output_tokens=1, total_tokens=2): - self.input_tokens = input_tokens - self.output_tokens = output_tokens - self.total_tokens = total_tokens - - -class DummyResponse: - def __init__(self): - self.id = "dummy-id" - self.output = [] - self.usage = DummyUsage() - - def __aiter__(self): - yield ResponseCompletedEvent( - type="response.completed", - response=fake_model.get_response_obj(self.output), - ) - - -@pytest.mark.allow_call_model_methods -@pytest.mark.asyncio -async def test_get_response_creates_trace(monkeypatch): - with trace(workflow_name="test"): - # Create an instance of the model - model = OpenAIResponsesModel( - model="test-model", openai_client=AsyncOpenAI(api_key="test") - ) - - # Mock _fetch_response to return a dummy response with a known id - async def dummy_fetch_response( - system_instructions, - input, - model_settings, - tools, - output_schema, - handoffs, - stream, - ): - return DummyResponse() - - monkeypatch.setattr(model, "_fetch_response", dummy_fetch_response) - - # Call get_response - await model.get_response( - "instr", "input", ModelSettings(), [], None, [], ModelTracing.ENABLED - ) - - spans = fetch_ordered_spans() - assert len(spans) == 1 - - assert isinstance(spans[0].span_data, ResponseSpanData) - assert spans[0].span_data.response is not None - assert spans[0].span_data.response.id == "dummy-id" - - -@pytest.mark.allow_call_model_methods -@pytest.mark.asyncio -async def test_non_data_tracing_doesnt_set_response_id(monkeypatch): - with trace(workflow_name="test"): - # Create an instance of the model - model = OpenAIResponsesModel( - model="test-model", openai_client=AsyncOpenAI(api_key="test") - ) - - # Mock _fetch_response to return a dummy response with a known id - async def dummy_fetch_response( - system_instructions, - input, - model_settings, - tools, - output_schema, - handoffs, - stream, - ): - return DummyResponse() - - monkeypatch.setattr(model, "_fetch_response", dummy_fetch_response) - - # Call get_response - await model.get_response( - "instr", - "input", - ModelSettings(), - [], - None, - [], - ModelTracing.ENABLED_WITHOUT_DATA, - ) - - spans = fetch_ordered_spans() - assert len(spans) == 1 - assert spans[0].span_data.response is None - - -@pytest.mark.allow_call_model_methods -@pytest.mark.asyncio -async def test_disable_tracing_does_not_create_span(monkeypatch): - with trace(workflow_name="test"): - # Create an instance of the model - model = OpenAIResponsesModel( - model="test-model", openai_client=AsyncOpenAI(api_key="test") - ) - - # Mock _fetch_response to return a dummy response with a known id - async def dummy_fetch_response( - system_instructions, - input, - model_settings, - tools, - output_schema, - handoffs, - stream, - ): - return DummyResponse() - - monkeypatch.setattr(model, "_fetch_response", dummy_fetch_response) - - # Call get_response - await model.get_response( - "instr", "input", ModelSettings(), [], None, [], ModelTracing.DISABLED - ) - - spans = fetch_ordered_spans() - assert len(spans) == 0 - - -@pytest.mark.allow_call_model_methods -@pytest.mark.asyncio -async def test_stream_response_creates_trace(monkeypatch): - with trace(workflow_name="test"): - # Create an instance of the model - model = OpenAIResponsesModel( - model="test-model", openai_client=AsyncOpenAI(api_key="test") - ) - - # Define a dummy fetch function that returns an async stream with a dummy response - async def dummy_fetch_response( - system_instructions, - input, - model_settings, - tools, - output_schema, - handoffs, - stream, - ): - class DummyStream: - async def __aiter__(self): - yield ResponseCompletedEvent( - type="response.completed", - response=fake_model.get_response_obj([], "dummy-id-123"), - ) - - return DummyStream() - - monkeypatch.setattr(model, "_fetch_response", dummy_fetch_response) - - # Consume the stream to trigger processing of the final response - async for _ in model.stream_response( - "instr", "input", ModelSettings(), [], None, [], ModelTracing.ENABLED - ): - pass - - spans = fetch_ordered_spans() - assert len(spans) == 1 - assert isinstance(spans[0].span_data, ResponseSpanData) - assert spans[0].span_data.response is not None - assert spans[0].span_data.response.id == "dummy-id-123" - - -@pytest.mark.allow_call_model_methods -@pytest.mark.asyncio -async def test_stream_non_data_tracing_doesnt_set_response_id(monkeypatch): - with trace(workflow_name="test"): - # Create an instance of the model - model = OpenAIResponsesModel( - model="test-model", openai_client=AsyncOpenAI(api_key="test") - ) - - # Define a dummy fetch function that returns an async stream with a dummy response - async def dummy_fetch_response( - system_instructions, - input, - model_settings, - tools, - output_schema, - handoffs, - stream, - ): - class DummyStream: - async def __aiter__(self): - yield ResponseCompletedEvent( - type="response.completed", - response=fake_model.get_response_obj([], "dummy-id-123"), - ) - - return DummyStream() - - monkeypatch.setattr(model, "_fetch_response", dummy_fetch_response) - - # Consume the stream to trigger processing of the final response - async for _ in model.stream_response( - "instr", - "input", - ModelSettings(), - [], - None, - [], - ModelTracing.ENABLED_WITHOUT_DATA, - ): - pass - - spans = fetch_ordered_spans() - assert len(spans) == 1 - assert isinstance(spans[0].span_data, ResponseSpanData) - assert spans[0].span_data.response is None - - -@pytest.mark.allow_call_model_methods -@pytest.mark.asyncio -async def test_stream_disabled_tracing_doesnt_create_span(monkeypatch): - with trace(workflow_name="test"): - # Create an instance of the model - model = OpenAIResponsesModel( - model="test-model", openai_client=AsyncOpenAI(api_key="test") - ) - - # Define a dummy fetch function that returns an async stream with a dummy response - async def dummy_fetch_response( - system_instructions, - input, - model_settings, - tools, - output_schema, - handoffs, - stream, - ): - class DummyStream: - async def __aiter__(self): - yield ResponseCompletedEvent( - type="response.completed", - response=fake_model.get_response_obj([], "dummy-id-123"), - ) - - return DummyStream() - - monkeypatch.setattr(model, "_fetch_response", dummy_fetch_response) - - # Consume the stream to trigger processing of the final response - async for _ in model.stream_response( - "instr", "input", ModelSettings(), [], None, [], ModelTracing.DISABLED - ): - pass - - spans = fetch_ordered_spans() - assert len(spans) == 0 diff --git a/pkg/hanzo-agent/tests/test_result_cast.py b/pkg/hanzo-agent/tests/test_result_cast.py deleted file mode 100644 index ec17e3275..000000000 --- a/pkg/hanzo-agent/tests/test_result_cast.py +++ /dev/null @@ -1,58 +0,0 @@ -from typing import Any - -import pytest -from pydantic import BaseModel - -from agents import Agent, RunResult - - -def create_run_result(final_output: Any) -> RunResult: - return RunResult( - input="test", - new_items=[], - raw_responses=[], - final_output=final_output, - input_guardrail_results=[], - output_guardrail_results=[], - _last_agent=Agent(name="test"), - ) - - -class Foo(BaseModel): - bar: int - - -def test_result_cast_typechecks(): - """Correct casts should work fine.""" - result = create_run_result(1) - assert result.final_output_as(int) == 1 - - result = create_run_result("test") - assert result.final_output_as(str) == "test" - - result = create_run_result(Foo(bar=1)) - assert result.final_output_as(Foo) == Foo(bar=1) - - -def test_bad_cast_doesnt_raise(): - """Bad casts shouldn't error unless we ask for it.""" - result = create_run_result(1) - result.final_output_as(str) - - result = create_run_result("test") - result.final_output_as(Foo) - - -def test_bad_cast_with_param_raises(): - """Bad casts should raise a TypeError when we ask for it.""" - result = create_run_result(1) - with pytest.raises(TypeError): - result.final_output_as(str, raise_if_incorrect_type=True) - - result = create_run_result("test") - with pytest.raises(TypeError): - result.final_output_as(Foo, raise_if_incorrect_type=True) - - result = create_run_result(Foo(bar=1)) - with pytest.raises(TypeError): - result.final_output_as(int, raise_if_incorrect_type=True) diff --git a/pkg/hanzo-agent/tests/test_run_config.py b/pkg/hanzo-agent/tests/test_run_config.py deleted file mode 100644 index 51835ab66..000000000 --- a/pkg/hanzo-agent/tests/test_run_config.py +++ /dev/null @@ -1,88 +0,0 @@ -from __future__ import annotations - -import pytest - -from agents import Agent, RunConfig, Runner -from agents.models.interface import Model, ModelProvider - -from .fake_model import FakeModel -from .test_responses import get_text_message - - -class DummyProvider(ModelProvider): - """A simple model provider that always returns the same model, and - records the model name it was asked to provide.""" - - def __init__(self, model_to_return: Model | None = None) -> None: - self.last_requested: str | None = None - self.model_to_return: Model = model_to_return or FakeModel() - - def get_model(self, model_name: str | None) -> Model: - # record the requested model name and return our test model - self.last_requested = model_name - return self.model_to_return - - -@pytest.mark.asyncio -async def test_model_provider_on_run_config_is_used_for_agent_model_name() -> None: - """ - When the agent's ``model`` attribute is a string and no explicit model override is - provided in the ``RunConfig``, the ``Runner`` should resolve the model using the - ``model_provider`` on the ``RunConfig``. - """ - fake_model = FakeModel(initial_output=[get_text_message("from-provider")]) - provider = DummyProvider(model_to_return=fake_model) - agent = Agent(name="test", model="test-model") - run_config = RunConfig(model_provider=provider) - result = await Runner.run(agent, input="any", run_config=run_config) - # We picked up the model from our dummy provider - assert provider.last_requested == "test-model" - assert result.final_output == "from-provider" - - -@pytest.mark.asyncio -async def test_run_config_model_name_override_takes_precedence() -> None: - """ - When a model name string is set on the RunConfig, then that name should be looked up - using the RunConfig's model_provider, and should override any model on the agent. - """ - fake_model = FakeModel(initial_output=[get_text_message("override-name")]) - provider = DummyProvider(model_to_return=fake_model) - agent = Agent(name="test", model="agent-model") - run_config = RunConfig(model="override-name", model_provider=provider) - result = await Runner.run(agent, input="any", run_config=run_config) - # We should have requested the override name, not the agent.model - assert provider.last_requested == "override-name" - assert result.final_output == "override-name" - - -@pytest.mark.asyncio -async def test_run_config_model_override_object_takes_precedence() -> None: - """ - When a concrete Model instance is set on the RunConfig, then that instance should be - returned by Runner._get_model regardless of the agent's model. - """ - fake_model = FakeModel(initial_output=[get_text_message("override-object")]) - agent = Agent(name="test", model="agent-model") - run_config = RunConfig(model=fake_model) - result = await Runner.run(agent, input="any", run_config=run_config) - # Our FakeModel on the RunConfig should have been used. - assert result.final_output == "override-object" - - -@pytest.mark.asyncio -async def test_agent_model_object_is_used_when_present() -> None: - """ - If the agent has a concrete Model object set as its model, and the RunConfig does - not specify a model override, then that object should be used directly without - consulting the RunConfig's model_provider. - """ - fake_model = FakeModel(initial_output=[get_text_message("from-agent-object")]) - provider = DummyProvider() - agent = Agent(name="test", model=fake_model) - run_config = RunConfig(model_provider=provider) - result = await Runner.run(agent, input="any", run_config=run_config) - # The dummy provider should never have been called, and the output should come from - # the FakeModel on the agent. - assert provider.last_requested is None - assert result.final_output == "from-agent-object" diff --git a/pkg/hanzo-agent/tests/test_run_step_execution.py b/pkg/hanzo-agent/tests/test_run_step_execution.py deleted file mode 100644 index 7d90dd9c7..000000000 --- a/pkg/hanzo-agent/tests/test_run_step_execution.py +++ /dev/null @@ -1,313 +0,0 @@ -from __future__ import annotations - -from typing import Any - -import pytest -from pydantic import BaseModel - -from agents import ( - Agent, - MessageOutputItem, - ModelResponse, - RunConfig, - RunContextWrapper, - RunHooks, - RunItem, - Runner, - ToolCallItem, - ToolCallOutputItem, - TResponseInputItem, - Usage, -) -from agents._run_impl import ( - NextStepFinalOutput, - NextStepHandoff, - NextStepRunAgain, - RunImpl, - SingleStepResult, -) - -from .test_responses import ( - get_final_output_message, - get_function_tool, - get_function_tool_call, - get_handoff_tool_call, - get_text_input_item, - get_text_message, -) - - -@pytest.mark.asyncio -async def test_empty_response_is_final_output(): - agent = Agent[None](name="test") - response = ModelResponse( - output=[], - usage=Usage(), - referenceable_id=None, - ) - result = await get_execute_result(agent, response) - - assert result.original_input == "hello" - assert result.generated_items == [] - assert isinstance(result.next_step, NextStepFinalOutput) - assert result.next_step.output == "" - - -@pytest.mark.asyncio -async def test_plaintext_agent_no_tool_calls_is_final_output(): - agent = Agent(name="test") - response = ModelResponse( - output=[get_text_message("hello_world")], - usage=Usage(), - referenceable_id=None, - ) - result = await get_execute_result(agent, response) - - assert result.original_input == "hello" - assert len(result.generated_items) == 1 - assert_item_is_message(result.generated_items[0], "hello_world") - assert isinstance(result.next_step, NextStepFinalOutput) - assert result.next_step.output == "hello_world" - - -@pytest.mark.asyncio -async def test_plaintext_agent_no_tool_calls_multiple_messages_is_final_output(): - agent = Agent(name="test") - response = ModelResponse( - output=[ - get_text_message("hello_world"), - get_text_message("bye"), - ], - usage=Usage(), - referenceable_id=None, - ) - result = await get_execute_result( - agent, - response, - original_input=[ - get_text_input_item("test"), - get_text_input_item("test2"), - ], - ) - - assert len(result.original_input) == 2 - assert len(result.generated_items) == 2 - assert_item_is_message(result.generated_items[0], "hello_world") - assert_item_is_message(result.generated_items[1], "bye") - - assert isinstance(result.next_step, NextStepFinalOutput) - assert result.next_step.output == "bye" - - -@pytest.mark.asyncio -async def test_plaintext_agent_with_tool_call_is_run_again(): - agent = Agent( - name="test", tools=[get_function_tool(name="test", return_value="123")] - ) - response = ModelResponse( - output=[get_text_message("hello_world"), get_function_tool_call("test", "")], - usage=Usage(), - referenceable_id=None, - ) - result = await get_execute_result(agent, response) - - assert result.original_input == "hello" - - # 3 items: new message, tool call, tool result - assert len(result.generated_items) == 3 - assert isinstance(result.next_step, NextStepRunAgain) - - items = result.generated_items - assert_item_is_message(items[0], "hello_world") - assert_item_is_function_tool_call(items[1], "test", None) - assert_item_is_function_tool_call_output(items[2], "123") - - assert isinstance(result.next_step, NextStepRunAgain) - - -@pytest.mark.asyncio -async def test_multiple_tool_calls(): - agent = Agent( - name="test", - tools=[ - get_function_tool(name="test_1", return_value="123"), - get_function_tool(name="test_2", return_value="456"), - get_function_tool(name="test_3", return_value="789"), - ], - ) - response = ModelResponse( - output=[ - get_text_message("Hello, world!"), - get_function_tool_call("test_1"), - get_function_tool_call("test_2"), - ], - usage=Usage(), - referenceable_id=None, - ) - - result = await get_execute_result(agent, response) - assert result.original_input == "hello" - - # 5 items: new message, 2 tool calls, 2 tool call outputs - assert len(result.generated_items) == 5 - assert isinstance(result.next_step, NextStepRunAgain) - - items = result.generated_items - assert_item_is_message(items[0], "Hello, world!") - assert_item_is_function_tool_call(items[1], "test_1", None) - assert_item_is_function_tool_call(items[2], "test_2", None) - - assert isinstance(result.next_step, NextStepRunAgain) - - -@pytest.mark.asyncio -async def test_handoff_output_leads_to_handoff_next_step(): - agent_1 = Agent(name="test_1") - agent_2 = Agent(name="test_2") - agent_3 = Agent(name="test_3", handoffs=[agent_1, agent_2]) - response = ModelResponse( - output=[get_text_message("Hello, world!"), get_handoff_tool_call(agent_1)], - usage=Usage(), - referenceable_id=None, - ) - result = await get_execute_result(agent_3, response) - - assert isinstance(result.next_step, NextStepHandoff) - assert result.next_step.new_agent == agent_1 - - assert len(result.generated_items) == 3 - - -class Foo(BaseModel): - bar: str - - -@pytest.mark.asyncio -async def test_final_output_without_tool_runs_again(): - agent = Agent( - name="test", output_type=Foo, tools=[get_function_tool("tool_1", "result")] - ) - response = ModelResponse( - output=[get_function_tool_call("tool_1")], - usage=Usage(), - referenceable_id=None, - ) - result = await get_execute_result(agent, response) - - assert isinstance(result.next_step, NextStepRunAgain) - assert ( - len(result.generated_items) == 2 - ), "expected 2 items: tool call, tool call output" - - -@pytest.mark.asyncio -async def test_final_output_leads_to_final_output_next_step(): - agent = Agent(name="test", output_type=Foo) - response = ModelResponse( - output=[ - get_text_message("Hello, world!"), - get_final_output_message(Foo(bar="123").model_dump_json()), - ], - usage=Usage(), - referenceable_id=None, - ) - result = await get_execute_result(agent, response) - - assert isinstance(result.next_step, NextStepFinalOutput) - assert result.next_step.output == Foo(bar="123") - - -@pytest.mark.asyncio -async def test_handoff_and_final_output_leads_to_handoff_next_step(): - agent_1 = Agent(name="test_1") - agent_2 = Agent(name="test_2") - agent_3 = Agent(name="test_3", handoffs=[agent_1, agent_2], output_type=Foo) - response = ModelResponse( - output=[ - get_final_output_message(Foo(bar="123").model_dump_json()), - get_handoff_tool_call(agent_1), - ], - usage=Usage(), - referenceable_id=None, - ) - result = await get_execute_result(agent_3, response) - - assert isinstance(result.next_step, NextStepHandoff) - assert result.next_step.new_agent == agent_1 - - -@pytest.mark.asyncio -async def test_multiple_final_output_leads_to_final_output_next_step(): - agent_1 = Agent(name="test_1") - agent_2 = Agent(name="test_2") - agent_3 = Agent(name="test_3", handoffs=[agent_1, agent_2], output_type=Foo) - response = ModelResponse( - output=[ - get_final_output_message(Foo(bar="123").model_dump_json()), - get_final_output_message(Foo(bar="456").model_dump_json()), - ], - usage=Usage(), - referenceable_id=None, - ) - result = await get_execute_result(agent_3, response) - - assert isinstance(result.next_step, NextStepFinalOutput) - assert result.next_step.output == Foo(bar="456") - - -# === Helpers === - - -def assert_item_is_message(item: RunItem, text: str) -> None: - assert isinstance(item, MessageOutputItem) - assert item.raw_item.type == "message" - assert item.raw_item.role == "assistant" - assert item.raw_item.content[0].type == "output_text" - assert item.raw_item.content[0].text == text - - -def assert_item_is_function_tool_call( - item: RunItem, name: str, arguments: str | None = None -) -> None: - assert isinstance(item, ToolCallItem) - assert item.raw_item.type == "function_call" - assert item.raw_item.name == name - assert not arguments or item.raw_item.arguments == arguments - - -def assert_item_is_function_tool_call_output(item: RunItem, output: str) -> None: - assert isinstance(item, ToolCallOutputItem) - assert item.raw_item["type"] == "function_call_output" - assert item.raw_item["output"] == output - - -async def get_execute_result( - agent: Agent[Any], - response: ModelResponse, - *, - original_input: str | list[TResponseInputItem] | None = None, - generated_items: list[RunItem] | None = None, - hooks: RunHooks[Any] | None = None, - context_wrapper: RunContextWrapper[Any] | None = None, - run_config: RunConfig | None = None, -) -> SingleStepResult: - output_schema = Runner._get_output_schema(agent) - handoffs = Runner._get_handoffs(agent) - - processed_response = RunImpl.process_model_response( - agent=agent, - response=response, - output_schema=output_schema, - handoffs=handoffs, - ) - return await RunImpl.execute_tools_and_side_effects( - agent=agent, - original_input=original_input or "hello", - new_response=response, - pre_step_items=generated_items or [], - processed_response=processed_response, - output_schema=output_schema, - hooks=hooks or RunHooks(), - context_wrapper=context_wrapper or RunContextWrapper(None), - run_config=run_config or RunConfig(), - ) diff --git a/pkg/hanzo-agent/tests/test_run_step_processing.py b/pkg/hanzo-agent/tests/test_run_step_processing.py deleted file mode 100644 index 9a2adb0e6..000000000 --- a/pkg/hanzo-agent/tests/test_run_step_processing.py +++ /dev/null @@ -1,433 +0,0 @@ -from __future__ import annotations - -import pytest -from openai.types.responses import ( - ResponseComputerToolCall, - ResponseFileSearchToolCall, - ResponseFunctionWebSearch, -) -from openai.types.responses.response_computer_tool_call import ActionClick -from openai.types.responses.response_reasoning_item import ( - ResponseReasoningItem, - Summary, -) -from pydantic import BaseModel - -from agents import ( - Agent, - Computer, - ComputerTool, - Handoff, - ModelBehaviorError, - ModelResponse, - ReasoningItem, - RunContextWrapper, - Runner, - ToolCallItem, - Usage, -) -from agents._run_impl import RunImpl - -from .test_responses import ( - get_final_output_message, - get_function_tool, - get_function_tool_call, - get_handoff_tool_call, - get_text_message, -) - - -def test_empty_response(): - agent = Agent(name="test") - response = ModelResponse( - output=[], - usage=Usage(), - referenceable_id=None, - ) - - result = RunImpl.process_model_response( - agent=agent, response=response, output_schema=None, handoffs=[] - ) - assert not result.handoffs - assert not result.functions - - -def test_no_tool_calls(): - agent = Agent(name="test") - response = ModelResponse( - output=[get_text_message("Hello, world!")], - usage=Usage(), - referenceable_id=None, - ) - result = RunImpl.process_model_response( - agent=agent, response=response, output_schema=None, handoffs=[] - ) - assert not result.handoffs - assert not result.functions - - -def test_single_tool_call(): - agent = Agent(name="test", tools=[get_function_tool(name="test")]) - response = ModelResponse( - output=[ - get_text_message("Hello, world!"), - get_function_tool_call("test", ""), - ], - usage=Usage(), - referenceable_id=None, - ) - result = RunImpl.process_model_response( - agent=agent, response=response, output_schema=None, handoffs=[] - ) - assert not result.handoffs - assert result.functions and len(result.functions) == 1 - - func = result.functions[0] - assert func.tool_call.name == "test" - assert func.tool_call.arguments == "" - - -def test_missing_tool_call_raises_error(): - agent = Agent(name="test", tools=[get_function_tool(name="test")]) - response = ModelResponse( - output=[ - get_text_message("Hello, world!"), - get_function_tool_call("missing", ""), - ], - usage=Usage(), - referenceable_id=None, - ) - - with pytest.raises(ModelBehaviorError): - RunImpl.process_model_response( - agent=agent, response=response, output_schema=None, handoffs=[] - ) - - -def test_multiple_tool_calls(): - agent = Agent( - name="test", - tools=[ - get_function_tool(name="test_1"), - get_function_tool(name="test_2"), - get_function_tool(name="test_3"), - ], - ) - response = ModelResponse( - output=[ - get_text_message("Hello, world!"), - get_function_tool_call("test_1", "abc"), - get_function_tool_call("test_2", "xyz"), - ], - usage=Usage(), - referenceable_id=None, - ) - - result = RunImpl.process_model_response( - agent=agent, response=response, output_schema=None, handoffs=[] - ) - assert not result.handoffs - assert result.functions and len(result.functions) == 2 - - func_1 = result.functions[0] - assert func_1.tool_call.name == "test_1" - assert func_1.tool_call.arguments == "abc" - - func_2 = result.functions[1] - assert func_2.tool_call.name == "test_2" - assert func_2.tool_call.arguments == "xyz" - - -@pytest.mark.asyncio -async def test_handoffs_parsed_correctly(): - agent_1 = Agent(name="test_1") - agent_2 = Agent(name="test_2") - agent_3 = Agent(name="test_3", handoffs=[agent_1, agent_2]) - response = ModelResponse( - output=[get_text_message("Hello, world!")], - usage=Usage(), - referenceable_id=None, - ) - result = RunImpl.process_model_response( - agent=agent_3, response=response, output_schema=None, handoffs=[] - ) - assert not result.handoffs, "Shouldn't have a handoff here" - - response = ModelResponse( - output=[get_text_message("Hello, world!"), get_handoff_tool_call(agent_1)], - usage=Usage(), - referenceable_id=None, - ) - result = RunImpl.process_model_response( - agent=agent_3, - response=response, - output_schema=None, - handoffs=Runner._get_handoffs(agent_3), - ) - assert len(result.handoffs) == 1, "Should have a handoff here" - handoff = result.handoffs[0] - assert handoff.handoff.tool_name == Handoff.default_tool_name(agent_1) - assert handoff.handoff.tool_description == Handoff.default_tool_description(agent_1) - assert handoff.handoff.agent_name == agent_1.name - - handoff_agent = await handoff.handoff.on_invoke_handoff( - RunContextWrapper(None), handoff.tool_call.arguments - ) - assert handoff_agent == agent_1 - - -@pytest.mark.asyncio -async def test_missing_handoff_fails(): - agent_1 = Agent(name="test_1") - agent_2 = Agent(name="test_2") - agent_3 = Agent(name="test_3", handoffs=[agent_1]) - response = ModelResponse( - output=[get_text_message("Hello, world!"), get_handoff_tool_call(agent_2)], - usage=Usage(), - referenceable_id=None, - ) - with pytest.raises(ModelBehaviorError): - RunImpl.process_model_response( - agent=agent_3, - response=response, - output_schema=None, - handoffs=Runner._get_handoffs(agent_3), - ) - - -def test_multiple_handoffs_doesnt_error(): - agent_1 = Agent(name="test_1") - agent_2 = Agent(name="test_2") - agent_3 = Agent(name="test_3", handoffs=[agent_1, agent_2]) - response = ModelResponse( - output=[ - get_text_message("Hello, world!"), - get_handoff_tool_call(agent_1), - get_handoff_tool_call(agent_2), - ], - usage=Usage(), - referenceable_id=None, - ) - result = RunImpl.process_model_response( - agent=agent_3, - response=response, - output_schema=None, - handoffs=Runner._get_handoffs(agent_3), - ) - assert len(result.handoffs) == 2, "Should have multiple handoffs here" - - -class Foo(BaseModel): - bar: str - - -def test_final_output_parsed_correctly(): - agent = Agent(name="test", output_type=Foo) - response = ModelResponse( - output=[ - get_text_message("Hello, world!"), - get_final_output_message(Foo(bar="123").model_dump_json()), - ], - usage=Usage(), - referenceable_id=None, - ) - - RunImpl.process_model_response( - agent=agent, - response=response, - output_schema=Runner._get_output_schema(agent), - handoffs=[], - ) - - -def test_file_search_tool_call_parsed_correctly(): - # Ensure that a ResponseFileSearchToolCall output is parsed into a ToolCallItem and that no tool - # runs are scheduled. - - agent = Agent(name="test") - file_search_call = ResponseFileSearchToolCall( - id="fs1", - queries=["query"], - status="completed", - type="file_search_call", - ) - response = ModelResponse( - output=[get_text_message("hello"), file_search_call], - usage=Usage(), - referenceable_id=None, - ) - result = RunImpl.process_model_response( - agent=agent, response=response, output_schema=None, handoffs=[] - ) - # The final item should be a ToolCallItem for the file search call - assert any( - isinstance(item, ToolCallItem) and item.raw_item is file_search_call - for item in result.new_items - ) - assert not result.functions - assert not result.handoffs - - -def test_function_web_search_tool_call_parsed_correctly(): - agent = Agent(name="test") - web_search_call = ResponseFunctionWebSearch( - id="w1", status="completed", type="web_search_call" - ) - response = ModelResponse( - output=[get_text_message("hello"), web_search_call], - usage=Usage(), - referenceable_id=None, - ) - result = RunImpl.process_model_response( - agent=agent, response=response, output_schema=None, handoffs=[] - ) - assert any( - isinstance(item, ToolCallItem) and item.raw_item is web_search_call - for item in result.new_items - ) - assert not result.functions - assert not result.handoffs - - -def test_reasoning_item_parsed_correctly(): - # Verify that a Reasoning output item is converted into a ReasoningItem. - - reasoning = ResponseReasoningItem( - id="r1", type="reasoning", summary=[Summary(text="why", type="summary_text")] - ) - response = ModelResponse( - output=[reasoning], - usage=Usage(), - referenceable_id=None, - ) - result = RunImpl.process_model_response( - agent=Agent(name="test"), response=response, output_schema=None, handoffs=[] - ) - assert any( - isinstance(item, ReasoningItem) and item.raw_item is reasoning - for item in result.new_items - ) - - -class DummyComputer(Computer): - """Minimal computer implementation for testing.""" - - @property - def environment(self): - return "mac" # pragma: no cover - - @property - def dimensions(self): - return (0, 0) # pragma: no cover - - def screenshot(self) -> str: - return "" # pragma: no cover - - def click(self, x: int, y: int, button: str) -> None: - return None # pragma: no cover - - def double_click(self, x: int, y: int) -> None: - return None # pragma: no cover - - def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: - return None # pragma: no cover - - def type(self, text: str) -> None: - return None # pragma: no cover - - def wait(self) -> None: - return None # pragma: no cover - - def move(self, x: int, y: int) -> None: - return None # pragma: no cover - - def keypress(self, keys: list[str]) -> None: - return None # pragma: no cover - - def drag(self, path: list[tuple[int, int]]) -> None: - return None # pragma: no cover - - -def test_computer_tool_call_without_computer_tool_raises_error(): - # If the agent has no ComputerTool in its tools, process_model_response should raise a - # ModelBehaviorError when encountering a ResponseComputerToolCall. - computer_call = ResponseComputerToolCall( - id="c1", - type="computer_call", - action=ActionClick(type="click", x=1, y=2, button="left"), - call_id="c1", - pending_safety_checks=[], - status="completed", - ) - response = ModelResponse( - output=[computer_call], - usage=Usage(), - referenceable_id=None, - ) - with pytest.raises(ModelBehaviorError): - RunImpl.process_model_response( - agent=Agent(name="test"), response=response, output_schema=None, handoffs=[] - ) - - -def test_computer_tool_call_with_computer_tool_parsed_correctly(): - # If the agent contains a ComputerTool, ensure that a ResponseComputerToolCall is parsed into a - # ToolCallItem and scheduled to run in computer_actions. - dummy_computer = DummyComputer() - agent = Agent(name="test", tools=[ComputerTool(computer=dummy_computer)]) - computer_call = ResponseComputerToolCall( - id="c1", - type="computer_call", - action=ActionClick(type="click", x=1, y=2, button="left"), - call_id="c1", - pending_safety_checks=[], - status="completed", - ) - response = ModelResponse( - output=[computer_call], - usage=Usage(), - referenceable_id=None, - ) - result = RunImpl.process_model_response( - agent=agent, response=response, output_schema=None, handoffs=[] - ) - assert any( - isinstance(item, ToolCallItem) and item.raw_item is computer_call - for item in result.new_items - ) - assert ( - result.computer_actions - and result.computer_actions[0].tool_call == computer_call - ) - - -def test_tool_and_handoff_parsed_correctly(): - agent_1 = Agent(name="test_1") - agent_2 = Agent(name="test_2") - agent_3 = Agent( - name="test_3", - tools=[get_function_tool(name="test")], - handoffs=[agent_1, agent_2], - ) - response = ModelResponse( - output=[ - get_text_message("Hello, world!"), - get_function_tool_call("test", "abc"), - get_handoff_tool_call(agent_1), - ], - usage=Usage(), - referenceable_id=None, - ) - - result = RunImpl.process_model_response( - agent=agent_3, - response=response, - output_schema=None, - handoffs=Runner._get_handoffs(agent_3), - ) - assert result.functions and len(result.functions) == 1 - assert len(result.handoffs) == 1, "Should have a handoff here" - handoff = result.handoffs[0] - assert handoff.handoff.tool_name == Handoff.default_tool_name(agent_1) - assert handoff.handoff.tool_description == Handoff.default_tool_description(agent_1) - assert handoff.handoff.agent_name == agent_1.name diff --git a/pkg/hanzo-agent/tests/test_strict_schema.py b/pkg/hanzo-agent/tests/test_strict_schema.py deleted file mode 100644 index 012a7bdc6..000000000 --- a/pkg/hanzo-agent/tests/test_strict_schema.py +++ /dev/null @@ -1,129 +0,0 @@ -import pytest - -from agents.exceptions import UserError -from agents.strict_schema import ensure_strict_json_schema - - -def test_empty_schema_has_additional_properties_false(): - strict_schema = ensure_strict_json_schema({}) - assert strict_schema["additionalProperties"] is False - - -def test_non_dict_schema_errors(): - with pytest.raises(TypeError): - ensure_strict_json_schema([]) # type: ignore - - -def test_object_without_additional_properties(): - # When an object type schema has properties but no additionalProperties, - # it should be added and the "required" list set from the property keys. - schema = {"type": "object", "properties": {"a": {"type": "string"}}} - result = ensure_strict_json_schema(schema) - assert result["type"] == "object" - assert result["additionalProperties"] is False - assert result["required"] == ["a"] - # The inner property remains unchanged (no additionalProperties is added for non-object types) - assert result["properties"]["a"] == {"type": "string"} - - -def test_object_with_true_additional_properties(): - # If additionalProperties is explicitly set to True for an object, a UserError should be raised. - schema = { - "type": "object", - "properties": {"a": {"type": "number"}}, - "additionalProperties": True, - } - with pytest.raises(UserError): - ensure_strict_json_schema(schema) - - -def test_array_items_processing_and_default_removal(): - # When processing an array, the items schema is processed recursively. - # Also, any "default": None should be removed. - schema = { - "type": "array", - "items": {"type": "number", "default": None}, - } - result = ensure_strict_json_schema(schema) - # "default" should be stripped from the items schema. - assert "default" not in result["items"] - assert result["items"]["type"] == "number" - - -def test_anyOf_processing(): - # Test that anyOf schemas are processed. - schema = { - "anyOf": [ - {"type": "object", "properties": {"a": {"type": "string"}}}, - {"type": "number", "default": None}, - ] - } - result = ensure_strict_json_schema(schema) - # For the first variant: object type should get additionalProperties and required keys set. - variant0 = result["anyOf"][0] - assert variant0["type"] == "object" - assert variant0["additionalProperties"] is False - assert variant0["required"] == ["a"] - - # For the second variant: the "default": None should be removed. - variant1 = result["anyOf"][1] - assert variant1["type"] == "number" - assert "default" not in variant1 - - -def test_allOf_single_entry_merging(): - # When an allOf list has a single entry, its content should be merged into the parent. - schema = { - "type": "object", - "allOf": [{"properties": {"a": {"type": "boolean"}}}], - } - result = ensure_strict_json_schema(schema) - # allOf should be removed and merged. - assert "allOf" not in result - # The object should now have additionalProperties set and required set. - assert result["additionalProperties"] is False - assert result["required"] == ["a"] - assert "a" in result["properties"] - assert result["properties"]["a"]["type"] == "boolean" - - -def test_default_removal_on_non_object(): - # Test that "default": None is stripped from schemas that are not objects. - schema = {"type": "string", "default": None} - result = ensure_strict_json_schema(schema) - assert result["type"] == "string" - assert "default" not in result - - -def test_ref_expansion(): - # Construct a schema with a definitions section and a property with a $ref. - schema = { - "definitions": {"refObj": {"type": "string", "default": None}}, - "type": "object", - "properties": {"a": {"$ref": "#/definitions/refObj", "description": "desc"}}, - } - result = ensure_strict_json_schema(schema) - a_schema = result["properties"]["a"] - # The $ref should be expanded so that the type is from the referenced definition, - # the description from the original takes precedence, and default is removed. - assert a_schema["type"] == "string" - assert a_schema["description"] == "desc" - assert "default" not in a_schema - - -def test_ref_no_expansion_when_alone(): - # If the schema only contains a $ref key, it should not be expanded. - schema = {"$ref": "#/definitions/refObj"} - result = ensure_strict_json_schema(schema) - # Because there is only one key, the $ref remains unchanged. - assert result == {"$ref": "#/definitions/refObj"} - - -def test_invalid_ref_format(): - # A $ref that does not start with "#/" should trigger a ValueError when resolved. - schema = { - "type": "object", - "properties": {"a": {"$ref": "invalid", "description": "desc"}}, - } - with pytest.raises(ValueError): - ensure_strict_json_schema(schema) diff --git a/pkg/hanzo-agent/tests/test_tool_converter.py b/pkg/hanzo-agent/tests/test_tool_converter.py deleted file mode 100644 index fd4ca98b7..000000000 --- a/pkg/hanzo-agent/tests/test_tool_converter.py +++ /dev/null @@ -1,58 +0,0 @@ -import pytest -from pydantic import BaseModel - -from agents import Agent, Handoff, function_tool, handoff -from agents.exceptions import UserError -from agents.models.openai_chatcompletions import ToolConverter -from agents.tool import FileSearchTool, WebSearchTool - - -def some_function(a: str, b: list[int]) -> str: - return "hello" - - -def test_to_openai_with_function_tool(): - some_function(a="foo", b=[1, 2, 3]) - - tool = function_tool(some_function) - result = ToolConverter.to_openai(tool) - - assert result["type"] == "function" - assert result["function"]["name"] == "some_function" - params = result.get("function", {}).get("parameters") - assert params is not None - properties = params.get("properties", {}) - assert isinstance(properties, dict) - assert properties.keys() == {"a", "b"} - - -class Foo(BaseModel): - a: str - b: list[int] - - -def test_convert_handoff_tool(): - agent = Agent(name="test_1", handoff_description="test_2") - handoff_obj = handoff(agent=agent) - result = ToolConverter.convert_handoff_tool(handoff_obj) - - assert result["type"] == "function" - assert result["function"]["name"] == Handoff.default_tool_name(agent) - assert result["function"].get("description") == Handoff.default_tool_description( - agent - ) - params = result.get("function", {}).get("parameters") - assert params is not None - - for key, value in handoff_obj.input_json_schema.items(): - assert params[key] == value - - -def test_tool_converter_hosted_tools_errors(): - with pytest.raises(UserError): - ToolConverter.to_openai(WebSearchTool()) - - with pytest.raises(UserError): - ToolConverter.to_openai( - FileSearchTool(vector_store_ids=["abc"], max_num_results=1) - ) diff --git a/pkg/hanzo-agent/tests/test_trace_processor.py b/pkg/hanzo-agent/tests/test_trace_processor.py deleted file mode 100644 index 634af0298..000000000 --- a/pkg/hanzo-agent/tests/test_trace_processor.py +++ /dev/null @@ -1,286 +0,0 @@ -import os -import time -from unittest.mock import MagicMock, patch - -import httpx -import pytest - -from agents.tracing.processor_interface import TracingProcessor -from agents.tracing.processors import BackendSpanExporter, BatchTraceProcessor -from agents.tracing.span_data import AgentSpanData -from agents.tracing.spans import SpanImpl -from agents.tracing.traces import TraceImpl - - -def get_span(processor: TracingProcessor) -> SpanImpl[AgentSpanData]: - """Create a minimal agent span for testing processors.""" - return SpanImpl( - trace_id="test_trace_id", - span_id="test_span_id", - parent_id=None, - processor=processor, - span_data=AgentSpanData(name="test_agent"), - ) - - -def get_trace(processor: TracingProcessor) -> TraceImpl: - """Create a minimal trace.""" - return TraceImpl( - name="test_trace", - trace_id="test_trace_id", - group_id="test_session_id", - metadata={}, - processor=processor, - ) - - -@pytest.fixture -def mocked_exporter(): - exporter = MagicMock() - exporter.export = MagicMock() - return exporter - - -def test_batch_trace_processor_on_trace_start(mocked_exporter): - processor = BatchTraceProcessor(exporter=mocked_exporter, schedule_delay=0.1) - test_trace = get_trace(processor) - - processor.on_trace_start(test_trace) - assert processor._queue.qsize() == 1, "Trace should be added to the queue" - - # Shutdown to clean up the worker thread - processor.shutdown() - - -def test_batch_trace_processor_on_span_end(mocked_exporter): - processor = BatchTraceProcessor(exporter=mocked_exporter, schedule_delay=0.1) - test_span = get_span(processor) - - processor.on_span_end(test_span) - assert processor._queue.qsize() == 1, "Span should be added to the queue" - - # Shutdown to clean up the worker thread - processor.shutdown() - - -def test_batch_trace_processor_queue_full(mocked_exporter): - processor = BatchTraceProcessor( - exporter=mocked_exporter, max_queue_size=2, schedule_delay=0.1 - ) - # Fill the queue - processor.on_trace_start(get_trace(processor)) - processor.on_trace_start(get_trace(processor)) - assert processor._queue.full() is True - - # Next item should not be queued - processor.on_trace_start(get_trace(processor)) - assert processor._queue.qsize() == 2, "Queue should not exceed max_queue_size" - - processor.on_span_end(get_span(processor)) - assert processor._queue.qsize() == 2, "Queue should not exceed max_queue_size" - - processor.shutdown() - - -def test_batch_processor_doesnt_enqueue_on_trace_end_or_span_start(mocked_exporter): - processor = BatchTraceProcessor(exporter=mocked_exporter) - - processor.on_trace_start(get_trace(processor)) - assert processor._queue.qsize() == 1, "Trace should be queued" - - processor.on_span_start(get_span(processor)) - assert processor._queue.qsize() == 1, "Span should not be queued" - - processor.on_span_end(get_span(processor)) - assert processor._queue.qsize() == 2, "Span should be queued" - - processor.on_trace_end(get_trace(processor)) - assert processor._queue.qsize() == 2, "Nothing new should be queued" - - processor.shutdown() - - -def test_batch_trace_processor_force_flush(mocked_exporter): - processor = BatchTraceProcessor( - exporter=mocked_exporter, max_batch_size=2, schedule_delay=5.0 - ) - - processor.on_trace_start(get_trace(processor)) - processor.on_span_end(get_span(processor)) - processor.on_span_end(get_span(processor)) - - processor.force_flush() - - # Ensure exporter.export was called with all items - # Because max_batch_size=2, it may have been called multiple times - total_exported = 0 - for call_args in mocked_exporter.export.call_args_list: - batch = call_args[0][0] # first positional arg to export() is the items list - total_exported += len(batch) - - # We pushed 3 items; ensure they all got exported - assert total_exported == 3 - - processor.shutdown() - - -def test_batch_trace_processor_shutdown_flushes(mocked_exporter): - processor = BatchTraceProcessor(exporter=mocked_exporter, schedule_delay=5.0) - processor.on_trace_start(get_trace(processor)) - processor.on_span_end(get_span(processor)) - qsize_before = processor._queue.qsize() - assert qsize_before == 2 - - processor.shutdown() - - # Ensure everything was exported after shutdown - total_exported = 0 - for call_args in mocked_exporter.export.call_args_list: - batch = call_args[0][0] - total_exported += len(batch) - - assert ( - total_exported == 2 - ), "All items in the queue should be exported upon shutdown" - - -def test_batch_trace_processor_scheduled_export(mocked_exporter): - """ - Tests that items are automatically exported when the schedule_delay expires. - We mock time.time() so we can trigger the condition without waiting in real time. - """ - with patch("time.time") as mock_time: - base_time = 1000.0 - mock_time.return_value = base_time - - processor = BatchTraceProcessor(exporter=mocked_exporter, schedule_delay=1.0) - - processor.on_span_end(get_span(processor)) # queue size = 1 - - # Now artificially advance time beyond the next export time - mock_time.return_value = base_time + 2.0 # > base_time + schedule_delay - # Let the background thread run a bit - time.sleep(0.3) - - # Check that exporter.export was eventually called - # Because the background thread runs, we might need a small sleep - processor.shutdown() - - total_exported = 0 - for call_args in mocked_exporter.export.call_args_list: - batch = call_args[0][0] - total_exported += len(batch) - - assert total_exported == 1, "Item should be exported after scheduled delay" - - -@pytest.fixture -def patched_time_sleep(): - """ - Fixture to replace time.sleep with a no-op to speed up tests - that rely on retry/backoff logic. - """ - with patch("time.sleep") as mock_sleep: - yield mock_sleep - - -def mock_processor(): - processor = MagicMock() - processor.on_trace_start = MagicMock() - processor.on_span_end = MagicMock() - return processor - - -@patch("httpx.Client") -def test_backend_span_exporter_no_items(mock_client): - exporter = BackendSpanExporter(api_key="test_key") - exporter.export([]) - # No calls should be made if there are no items - mock_client.return_value.post.assert_not_called() - exporter.close() - - -@patch("httpx.Client") -def test_backend_span_exporter_no_api_key(mock_client): - # Ensure that os.environ is empty (sometimes devs have the openai api key set in their env) - - with patch.dict(os.environ, {}, clear=True): - exporter = BackendSpanExporter(api_key=None) - exporter.export([get_span(mock_processor())]) - - # Should log an error and return without calling post - mock_client.return_value.post.assert_not_called() - exporter.close() - - -@patch("httpx.Client") -def test_backend_span_exporter_2xx_success(mock_client): - mock_response = MagicMock() - mock_response.status_code = 200 - mock_client.return_value.post.return_value = mock_response - - exporter = BackendSpanExporter(api_key="test_key") - exporter.export([get_span(mock_processor()), get_trace(mock_processor())]) - - # Should have called post exactly once - mock_client.return_value.post.assert_called_once() - exporter.close() - - -@patch("httpx.Client") -def test_backend_span_exporter_4xx_client_error(mock_client): - mock_response = MagicMock() - mock_response.status_code = 400 - mock_response.text = "Bad Request" - mock_client.return_value.post.return_value = mock_response - - exporter = BackendSpanExporter(api_key="test_key") - exporter.export([get_span(mock_processor())]) - - # 4xx should not be retried - mock_client.return_value.post.assert_called_once() - exporter.close() - - -@patch("httpx.Client") -def test_backend_span_exporter_5xx_retry(mock_client, patched_time_sleep): - mock_response = MagicMock() - mock_response.status_code = 500 - - # Make post() return 500 every time - mock_client.return_value.post.return_value = mock_response - - exporter = BackendSpanExporter( - api_key="test_key", max_retries=3, base_delay=0.1, max_delay=0.2 - ) - exporter.export([get_span(mock_processor())]) - - # Should retry up to max_retries times - assert mock_client.return_value.post.call_count == 3 - - exporter.close() - - -@patch("httpx.Client") -def test_backend_span_exporter_request_error(mock_client, patched_time_sleep): - # Make post() raise a RequestError each time - mock_client.return_value.post.side_effect = httpx.RequestError("Network error") - - exporter = BackendSpanExporter( - api_key="test_key", max_retries=2, base_delay=0.1, max_delay=0.2 - ) - exporter.export([get_span(mock_processor())]) - - # Should retry up to max_retries times - assert mock_client.return_value.post.call_count == 2 - - exporter.close() - - -@patch("httpx.Client") -def test_backend_span_exporter_close(mock_client): - exporter = BackendSpanExporter(api_key="test_key") - exporter.close() - - # Ensure underlying http client is closed - mock_client.return_value.close.assert_called_once() diff --git a/pkg/hanzo-agent/tests/test_tracing.py b/pkg/hanzo-agent/tests/test_tracing.py deleted file mode 100644 index ade234975..000000000 --- a/pkg/hanzo-agent/tests/test_tracing.py +++ /dev/null @@ -1,436 +0,0 @@ -from __future__ import annotations - -import asyncio -from typing import Any - -import pytest - -from agents.tracing import ( - Span, - Trace, - agent_span, - custom_span, - function_span, - generation_span, - handoff_span, - trace, -) -from agents.tracing.spans import SpanError - -from .testing_processor import fetch_events, fetch_ordered_spans, fetch_traces - -### HELPERS - - -def standard_span_checks( - span: Span[Any], trace_id: str, parent_id: str | None, span_type: str -) -> None: - assert span.span_id is not None - assert span.trace_id == trace_id - assert span.parent_id == parent_id - assert span.started_at is not None - assert span.ended_at is not None - assert span.span_data.type == span_type - - -def standard_trace_checks(trace: Trace, name_check: str | None = None) -> None: - assert trace.trace_id is not None - - if name_check: - assert trace.name == name_check - - -### TESTS - - -def simple_tracing(): - x = trace("test") - x.start() - - span_1 = agent_span(name="agent_1", parent=x) - span_1.start() - span_1.finish() - - span_2 = custom_span(name="custom_1", span_id="span_2", parent=x) - span_2.start() - - span_3 = custom_span(name="custom_2", span_id="span_3", parent=span_2) - span_3.start() - span_3.finish() - - span_2.finish() - - x.finish() - - -def test_simple_tracing() -> None: - simple_tracing() - - spans, traces = fetch_ordered_spans(), fetch_traces() - assert len(spans) == 3 - assert len(traces) == 1 - - trace = traces[0] - standard_trace_checks(trace, name_check="test") - trace_id = trace.trace_id - - first_span = spans[0] - standard_span_checks( - first_span, trace_id=trace_id, parent_id=None, span_type="agent" - ) - assert first_span.span_data.name == "agent_1" - - second_span = spans[1] - standard_span_checks( - second_span, trace_id=trace_id, parent_id=None, span_type="custom" - ) - assert second_span.span_id == "span_2" - assert second_span.span_data.name == "custom_1" - - third_span = spans[2] - standard_span_checks( - third_span, trace_id=trace_id, parent_id=second_span.span_id, span_type="custom" - ) - assert third_span.span_id == "span_3" - assert third_span.span_data.name == "custom_2" - - -def ctxmanager_spans(): - with trace(workflow_name="test", trace_id="123", group_id="456"): - with custom_span(name="custom_1", span_id="span_1"): - with custom_span(name="custom_2", span_id="span_1_inner"): - pass - - with custom_span(name="custom_2", span_id="span_2"): - pass - - -def test_ctxmanager_spans() -> None: - ctxmanager_spans() - - spans, traces = fetch_ordered_spans(), fetch_traces() - assert len(spans) == 3 - assert len(traces) == 1 - - trace = traces[0] - standard_trace_checks(trace, name_check="test") - trace_id = trace.trace_id - - first_span = spans[0] - standard_span_checks( - first_span, trace_id=trace_id, parent_id=None, span_type="custom" - ) - assert first_span.span_id == "span_1" - - first_inner_span = spans[1] - standard_span_checks( - first_inner_span, - trace_id=trace_id, - parent_id=first_span.span_id, - span_type="custom", - ) - assert first_inner_span.span_id == "span_1_inner" - - second_span = spans[2] - standard_span_checks( - second_span, trace_id=trace_id, parent_id=None, span_type="custom" - ) - assert second_span.span_id == "span_2" - - -async def run_subtask(span_id: str | None = None) -> None: - with generation_span(span_id=span_id): - await asyncio.sleep(0.01) - - -async def simple_async_tracing(): - with trace(workflow_name="test", trace_id="123", group_id="456"): - await run_subtask(span_id="span_1") - await run_subtask(span_id="span_2") - - -@pytest.mark.asyncio -async def test_async_tracing() -> None: - await simple_async_tracing() - - spans, traces = fetch_ordered_spans(), fetch_traces() - assert len(spans) == 2 - assert len(traces) == 1 - - trace = traces[0] - standard_trace_checks(trace, name_check="test") - trace_id = trace.trace_id - - # We don't care about ordering here, just that they're there - for s in spans: - standard_span_checks( - s, trace_id=trace_id, parent_id=None, span_type="generation" - ) - - ids = [span.span_id for span in spans] - assert "span_1" in ids - assert "span_2" in ids - - -async def run_tasks_parallel(span_ids: list[str]) -> None: - await asyncio.gather( - *[run_subtask(span_id=span_id) for span_id in span_ids], - ) - - -async def run_tasks_as_children(first_span_id: str, second_span_id: str) -> None: - with generation_span(span_id=first_span_id): - await run_subtask(span_id=second_span_id) - - -async def complex_async_tracing(): - with trace(workflow_name="test", trace_id="123", group_id="456"): - await asyncio.sleep(0.01) - await asyncio.gather( - run_tasks_parallel(["span_1", "span_2"]), - run_tasks_parallel(["span_3", "span_4"]), - ) - await asyncio.sleep(0.01) - await asyncio.gather( - run_tasks_as_children("span_5", "span_6"), - run_tasks_as_children("span_7", "span_8"), - ) - - -@pytest.mark.asyncio -async def test_complex_async_tracing() -> None: - await complex_async_tracing() - - spans, traces = fetch_ordered_spans(), fetch_traces() - assert len(spans) == 8 - assert len(traces) == 1 - - trace = traces[0] - standard_trace_checks(trace, name_check="test") - trace_id = trace.trace_id - - # First ensure 1,2,3,4 exist and are in parallel with the trace as parent - for span_id in ["span_1", "span_2", "span_3", "span_4"]: - span = next((s for s in spans if s.span_id == span_id), None) - assert span is not None - standard_span_checks( - span, trace_id=trace_id, parent_id=None, span_type="generation" - ) - - # Ensure 5 and 7 exist and have the trace as parent - for span_id in ["span_5", "span_7"]: - span = next((s for s in spans if s.span_id == span_id), None) - assert span is not None - standard_span_checks( - span, trace_id=trace_id, parent_id=None, span_type="generation" - ) - - # Ensure 6 and 8 exist and have 5 and 7 as parents - six = next((s for s in spans if s.span_id == "span_6"), None) - assert six is not None - standard_span_checks( - six, trace_id=trace_id, parent_id="span_5", span_type="generation" - ) - eight = next((s for s in spans if s.span_id == "span_8"), None) - assert eight is not None - standard_span_checks( - eight, trace_id=trace_id, parent_id="span_7", span_type="generation" - ) - - -def spans_with_setters(): - with trace(workflow_name="test", trace_id="123", group_id="456"): - with agent_span(name="agent_1") as span_a: - span_a.span_data.name = "agent_2" - - with function_span(name="function_1") as span_b: - span_b.span_data.input = "i" - span_b.span_data.output = "o" - - with generation_span() as span_c: - span_c.span_data.input = [{"foo": "bar"}] - - with handoff_span(from_agent="agent_1", to_agent="agent_2"): - pass - - -def test_spans_with_setters() -> None: - spans_with_setters() - - spans, traces = fetch_ordered_spans(), fetch_traces() - assert len(spans) == 4 - assert len(traces) == 1 - - trace = traces[0] - standard_trace_checks(trace, name_check="test") - trace_id = trace.trace_id - - # Check the spans - first_span = spans[0] - standard_span_checks( - first_span, trace_id=trace_id, parent_id=None, span_type="agent" - ) - assert first_span.span_data.name == "agent_2" - - second_span = spans[1] - standard_span_checks( - second_span, - trace_id=trace_id, - parent_id=first_span.span_id, - span_type="function", - ) - assert second_span.span_data.input == "i" - assert second_span.span_data.output == "o" - - third_span = spans[2] - standard_span_checks( - third_span, - trace_id=trace_id, - parent_id=first_span.span_id, - span_type="generation", - ) - - fourth_span = spans[3] - standard_span_checks( - fourth_span, - trace_id=trace_id, - parent_id=first_span.span_id, - span_type="handoff", - ) - - -def disabled_tracing(): - with trace(workflow_name="test", trace_id="123", group_id="456", disabled=True): - with agent_span(name="agent_1"): - with function_span(name="function_1"): - pass - - -def test_disabled_tracing(): - disabled_tracing() - - spans, traces = fetch_ordered_spans(), fetch_traces() - assert len(spans) == 0 - assert len(traces) == 0 - - -def enabled_trace_disabled_span(): - with trace(workflow_name="test", trace_id="123"): - with agent_span(name="agent_1"): - with function_span(name="function_1", disabled=True): - with generation_span(): - pass - - -def test_enabled_trace_disabled_span(): - enabled_trace_disabled_span() - - spans, traces = fetch_ordered_spans(), fetch_traces() - assert len(spans) == 1 # Only the agent span is recorded - assert len(traces) == 1 # The trace is recorded - - trace = traces[0] - standard_trace_checks(trace, name_check="test") - trace_id = trace.trace_id - - first_span = spans[0] - standard_span_checks( - first_span, trace_id=trace_id, parent_id=None, span_type="agent" - ) - assert first_span.span_data.name == "agent_1" - - -def test_start_and_end_called_manual(): - simple_tracing() - - events = fetch_events() - - assert events == [ - "trace_start", - "span_start", # span_1 - "span_end", # span_1 - "span_start", # span_2 - "span_start", # span_3 - "span_end", # span_3 - "span_end", # span_2 - "trace_end", - ] - - -def test_start_and_end_called_ctxmanager(): - with trace(workflow_name="test", trace_id="123", group_id="456"): - with custom_span(name="custom_1", span_id="span_1"): - with custom_span(name="custom_2", span_id="span_1_inner"): - pass - - with custom_span(name="custom_2", span_id="span_2"): - pass - - events = fetch_events() - - assert events == [ - "trace_start", - "span_start", # span_1 - "span_start", # span_1_inner - "span_end", # span_1_inner - "span_end", # span_1 - "span_start", # span_2 - "span_end", # span_2 - "trace_end", - ] - - -@pytest.mark.asyncio -async def test_start_and_end_called_async_ctxmanager(): - await simple_async_tracing() - - events = fetch_events() - - assert events == [ - "trace_start", - "span_start", # span_1 - "span_end", # span_1 - "span_start", # span_2 - "span_end", # span_2 - "trace_end", - ] - - -async def test_noop_span_doesnt_record(): - with trace(workflow_name="test", disabled=True) as t: - with custom_span(name="span_1") as span: - span.set_error(SpanError(message="test", data={})) - - spans, traces = fetch_ordered_spans(), fetch_traces() - assert len(spans) == 0 - assert len(traces) == 0 - - assert t.export() is None - assert span.export() is None - assert span.started_at is None - assert span.ended_at is None - assert span.error is None - - -async def test_multiple_span_start_finish_doesnt_crash(): - with trace(workflow_name="test", trace_id="123", group_id="456"): - with custom_span(name="span_1") as span: - span.start() - - span.finish() - - -async def test_noop_parent_is_noop_child(): - tr = trace(workflow_name="test", disabled=True) - - span = custom_span(name="span_1", parent=tr) - span.start() - span.finish() - - assert span.export() is None - - span_2 = custom_span(name="span_2", parent=span) - span_2.start() - span_2.finish() - - assert span_2.export() is None diff --git a/pkg/hanzo-agent/tests/test_tracing_errors.py b/pkg/hanzo-agent/tests/test_tracing_errors.py deleted file mode 100644 index e4d79dd48..000000000 --- a/pkg/hanzo-agent/tests/test_tracing_errors.py +++ /dev/null @@ -1,336 +0,0 @@ -from __future__ import annotations - -import json -from typing import Any - -import pytest -from typing_extensions import TypedDict - -from agents import ( - Agent, - GuardrailFunctionOutput, - InputGuardrail, - InputGuardrailTripwireTriggered, - MaxTurnsExceeded, - ModelBehaviorError, - RunContextWrapper, - Runner, - TResponseInputItem, -) -from agents.tracing import AgentSpanData, FunctionSpanData, GenerationSpanData - -from .fake_model import FakeModel -from .test_responses import ( - get_final_output_message, - get_function_tool, - get_function_tool_call, - get_handoff_tool_call, - get_text_message, -) -from .testing_processor import fetch_ordered_spans, fetch_traces - - -@pytest.mark.asyncio -async def test_single_turn_model_error(): - model = FakeModel(tracing_enabled=True) - model.set_next_output(ValueError("test error")) - - agent = Agent( - name="test_agent", - model=model, - ) - with pytest.raises(ValueError): - await Runner.run(agent, input="first_test") - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 2, f"should have agent and generation spans, got {len(spans)}" - - generation_span = spans[1] - assert isinstance(generation_span.span_data, GenerationSpanData) - assert generation_span.error, "should have error" - - -@pytest.mark.asyncio -async def test_multi_turn_no_handoffs(): - model = FakeModel(tracing_enabled=True) - - agent = Agent( - name="test_agent", - model=model, - tools=[get_function_tool("foo", "tool_result")], - ) - - model.add_multiple_turn_outputs( - [ - # First turn: a message and tool call - [ - get_text_message("a_message"), - get_function_tool_call("foo", json.dumps({"a": "b"})), - ], - # Second turn: error - ValueError("test error"), - # Third turn: text message - [get_text_message("done")], - ] - ) - - with pytest.raises(ValueError): - await Runner.run(agent, input="first_test") - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 4, ( - f"should have agent, generation, tool, generation, got {len(spans)} with data: " - f"{[x.span_data for x in spans]}" - ) - - last_generation_span = [ - x for x in spans if isinstance(x.span_data, GenerationSpanData) - ][-1] - assert last_generation_span.error, "should have error" - - -@pytest.mark.asyncio -async def test_tool_call_error(): - model = FakeModel(tracing_enabled=True) - - agent = Agent( - name="test_agent", - model=model, - tools=[get_function_tool("foo", "tool_result", hide_errors=True)], - ) - - model.set_next_output( - [get_text_message("a_message"), get_function_tool_call("foo", "bad_json")], - ) - - with pytest.raises(ModelBehaviorError): - await Runner.run(agent, input="first_test") - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 3, ( - f"should have agent, generation, tool spans, got {len(spans)} with data: " - f"{[x.span_data for x in spans]}" - ) - - function_span = [x for x in spans if isinstance(x.span_data, FunctionSpanData)][0] - assert function_span.error, "should have error" - - -@pytest.mark.asyncio -async def test_multiple_handoff_doesnt_error(): - model = FakeModel(tracing_enabled=True) - - agent_1 = Agent( - name="test", - model=model, - ) - agent_2 = Agent( - name="test", - model=model, - ) - agent_3 = Agent( - name="test", - model=model, - handoffs=[agent_1, agent_2], - tools=[get_function_tool("some_function", "result")], - ) - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message and 2 handoff - [ - get_text_message("a_message"), - get_handoff_tool_call(agent_1), - get_handoff_tool_call(agent_2), - ], - # Third turn: text message - [get_text_message("done")], - ] - ) - result = await Runner.run(agent_3, input="user_message") - assert result.last_agent == agent_1, "should have picked first handoff" - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 7, ( - f"should have 2 agent, 1 function, 3 generation, 1 handoff, got {len(spans)} with data: " - f"{[x.span_data for x in spans]}" - ) - - -class Foo(TypedDict): - bar: str - - -@pytest.mark.asyncio -async def test_multiple_final_output_doesnt_error(): - model = FakeModel(tracing_enabled=True) - - agent_1 = Agent( - name="test", - model=model, - output_type=Foo, - ) - - model.set_next_output( - [ - get_final_output_message(json.dumps(Foo(bar="baz"))), - get_final_output_message(json.dumps(Foo(bar="abc"))), - ] - ) - - result = await Runner.run(agent_1, input="user_message") - assert result.final_output == Foo(bar="abc") - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 2, ( - f"should have 1 agent, 1 generation, got {len(spans)} with data: " - f"{[x.span_data for x in spans]}" - ) - - -@pytest.mark.asyncio -async def test_handoffs_lead_to_correct_agent_spans(): - model = FakeModel(tracing_enabled=True) - - agent_1 = Agent( - name="test_agent_1", - model=model, - tools=[get_function_tool("some_function", "result")], - ) - agent_2 = Agent( - name="test_agent_2", - model=model, - handoffs=[agent_1], - tools=[get_function_tool("some_function", "result")], - ) - agent_3 = Agent( - name="test_agent_3", - model=model, - handoffs=[agent_1, agent_2], - tools=[get_function_tool("some_function", "result")], - ) - - agent_1.handoffs.append(agent_3) - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message and 2 handoff - [ - get_text_message("a_message"), - get_handoff_tool_call(agent_1), - get_handoff_tool_call(agent_2), - ], - # Third turn: tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Fourth turn: handoff - [get_handoff_tool_call(agent_3)], - # Fifth turn: text message - [get_text_message("done")], - ] - ) - result = await Runner.run(agent_3, input="user_message") - - assert ( - result.last_agent == agent_3 - ), f"should have ended on the third agent, got {result.last_agent.name}" - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 12, ( - f"should have 3 agents, 2 function, 5 generation, 2 handoff, got {len(spans)} with data: " - f"{[x.span_data for x in spans]}" - ) - - -@pytest.mark.asyncio -async def test_max_turns_exceeded(): - model = FakeModel(tracing_enabled=True) - - agent = Agent( - name="test", - model=model, - output_type=Foo, - tools=[get_function_tool("foo", "result")], - ) - - model.add_multiple_turn_outputs( - [ - [get_function_tool_call("foo")], - [get_function_tool_call("foo")], - [get_function_tool_call("foo")], - [get_function_tool_call("foo")], - [get_function_tool_call("foo")], - ] - ) - - with pytest.raises(MaxTurnsExceeded): - await Runner.run(agent, input="user_message", max_turns=2) - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 5, ( - f"should have 1 agent span, 2 generations, 2 function calls, got " - f"{len(spans)} with data: {[x.span_data for x in spans]}" - ) - - agent_span = [x for x in spans if isinstance(x.span_data, AgentSpanData)][-1] - assert agent_span.error, "last agent should have error" - - -def guardrail_function( - context: RunContextWrapper[Any], - agent: Agent[Any], - input: str | list[TResponseInputItem], -) -> GuardrailFunctionOutput: - return GuardrailFunctionOutput( - output_info=None, - tripwire_triggered=True, - ) - - -@pytest.mark.asyncio -async def test_guardrail_error(): - agent = Agent( - name="test", - input_guardrails=[InputGuardrail(guardrail_function=guardrail_function)], - ) - model = FakeModel() - model.set_next_output([get_text_message("some_message")]) - - with pytest.raises(InputGuardrailTripwireTriggered): - await Runner.run(agent, input="user_message") - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 2, ( - f"should have 1 agent, 1 guardrail, got {len(spans)} with data: " - f"{[x.span_data for x in spans]}" - ) - - agent_span = [x for x in spans if isinstance(x.span_data, AgentSpanData)][-1] - assert agent_span.error, "last agent should have error" diff --git a/pkg/hanzo-agent/tests/test_tracing_errors_streamed.py b/pkg/hanzo-agent/tests/test_tracing_errors_streamed.py deleted file mode 100644 index 5677bff16..000000000 --- a/pkg/hanzo-agent/tests/test_tracing_errors_streamed.py +++ /dev/null @@ -1,406 +0,0 @@ -from __future__ import annotations - -import asyncio -import json -from typing import Any - -import pytest -from typing_extensions import TypedDict - -from agents import ( - Agent, - AgentSpanData, - FunctionSpanData, - GenerationSpanData, - GuardrailFunctionOutput, - InputGuardrail, - InputGuardrailTripwireTriggered, - MaxTurnsExceeded, - ModelBehaviorError, - OutputGuardrail, - OutputGuardrailTripwireTriggered, - RunContextWrapper, - Runner, - TResponseInputItem, -) - -from .fake_model import FakeModel -from .test_responses import ( - get_final_output_message, - get_function_tool, - get_function_tool_call, - get_handoff_tool_call, - get_text_message, -) -from .testing_processor import fetch_ordered_spans, fetch_traces - - -@pytest.mark.asyncio -async def test_single_turn_model_error(): - model = FakeModel(tracing_enabled=True) - model.set_next_output(ValueError("test error")) - - agent = Agent( - name="test_agent", - model=model, - ) - with pytest.raises(ValueError): - result = Runner.run_streamed(agent, input="first_test") - async for _ in result.stream_events(): - pass - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 2, f"should have agent and generation spans, got {len(spans)}" - - generation_span = spans[1] - assert isinstance(generation_span.span_data, GenerationSpanData) - assert generation_span.error, "should have error" - - -@pytest.mark.asyncio -async def test_multi_turn_no_handoffs(): - model = FakeModel(tracing_enabled=True) - - agent = Agent( - name="test_agent", - model=model, - tools=[get_function_tool("foo", "tool_result")], - ) - - model.add_multiple_turn_outputs( - [ - # First turn: a message and tool call - [ - get_text_message("a_message"), - get_function_tool_call("foo", json.dumps({"a": "b"})), - ], - # Second turn: error - ValueError("test error"), - # Third turn: text message - [get_text_message("done")], - ] - ) - - with pytest.raises(ValueError): - result = Runner.run_streamed(agent, input="first_test") - async for _ in result.stream_events(): - pass - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 4, ( - f"should have agent, generation, tool, generation, got {len(spans)} with data: " - f"{[x.span_data for x in spans]}" - ) - - last_generation_span = [ - x for x in spans if isinstance(x.span_data, GenerationSpanData) - ][-1] - assert last_generation_span.error, "should have error" - - -@pytest.mark.asyncio -async def test_tool_call_error(): - model = FakeModel(tracing_enabled=True) - - agent = Agent( - name="test_agent", - model=model, - tools=[get_function_tool("foo", "tool_result", hide_errors=True)], - ) - - model.set_next_output( - [get_text_message("a_message"), get_function_tool_call("foo", "bad_json")], - ) - - with pytest.raises(ModelBehaviorError): - result = Runner.run_streamed(agent, input="first_test") - async for _ in result.stream_events(): - pass - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 3, ( - f"should have agent, generation, tool spans, got {len(spans)} with data: " - f"{[x.span_data for x in spans]}" - ) - - function_span = [x for x in spans if isinstance(x.span_data, FunctionSpanData)][0] - assert function_span.error, "should have error" - - -@pytest.mark.asyncio -async def test_multiple_handoff_doesnt_error(): - model = FakeModel(tracing_enabled=True) - - agent_1 = Agent( - name="test", - model=model, - ) - agent_2 = Agent( - name="test", - model=model, - ) - agent_3 = Agent( - name="test", - model=model, - handoffs=[agent_1, agent_2], - tools=[get_function_tool("some_function", "result")], - ) - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message and 2 handoff - [ - get_text_message("a_message"), - get_handoff_tool_call(agent_1), - get_handoff_tool_call(agent_2), - ], - # Third turn: text message - [get_text_message("done")], - ] - ) - result = Runner.run_streamed(agent_3, input="user_message") - async for _ in result.stream_events(): - pass - - assert result.last_agent == agent_1, "should have picked first handoff" - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 7, ( - f"should have 2 agent, 1 function, 3 generation, 1 handoff, got {len(spans)} with data: " - f"{[x.span_data for x in spans]}" - ) - - -class Foo(TypedDict): - bar: str - - -@pytest.mark.asyncio -async def test_multiple_final_output_no_error(): - model = FakeModel(tracing_enabled=True) - - agent_1 = Agent( - name="test", - model=model, - output_type=Foo, - ) - - model.set_next_output( - [ - get_final_output_message(json.dumps(Foo(bar="baz"))), - get_final_output_message(json.dumps(Foo(bar="abc"))), - ] - ) - - result = Runner.run_streamed(agent_1, input="user_message") - async for _ in result.stream_events(): - pass - - assert isinstance(result.final_output, dict) - assert result.final_output["bar"] == "abc" - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 2, ( - f"should have 1 agent, 1 generation, got {len(spans)} with data: " - f"{[x.span_data for x in spans]}" - ) - - -@pytest.mark.asyncio -async def test_handoffs_lead_to_correct_agent_spans(): - model = FakeModel(tracing_enabled=True) - - agent_1 = Agent( - name="test_agent_1", - model=model, - tools=[get_function_tool("some_function", "result")], - ) - agent_2 = Agent( - name="test_agent_2", - model=model, - handoffs=[agent_1], - tools=[get_function_tool("some_function", "result")], - ) - agent_3 = Agent( - name="test_agent_3", - model=model, - handoffs=[agent_1, agent_2], - tools=[get_function_tool("some_function", "result")], - ) - - agent_1.handoffs.append(agent_3) - - model.add_multiple_turn_outputs( - [ - # First turn: a tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Second turn: a message and 2 handoff - [ - get_text_message("a_message"), - get_handoff_tool_call(agent_1), - get_handoff_tool_call(agent_2), - ], - # Third turn: tool call - [get_function_tool_call("some_function", json.dumps({"a": "b"}))], - # Fourth turn: handoff - [get_handoff_tool_call(agent_3)], - # Fifth turn: text message - [get_text_message("done")], - ] - ) - result = Runner.run_streamed(agent_3, input="user_message") - async for _ in result.stream_events(): - pass - - assert ( - result.last_agent == agent_3 - ), f"should have ended on the third agent, got {result.last_agent.name}" - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 12, ( - f"should have 3 agents, 2 function, 5 generation, 2 handoff, got {len(spans)} with data: " - f"{[x.span_data for x in spans]}" - ) - - -@pytest.mark.asyncio -async def test_max_turns_exceeded(): - model = FakeModel(tracing_enabled=True) - - agent = Agent( - name="test", - model=model, - output_type=Foo, - tools=[get_function_tool("foo", "result")], - ) - - model.add_multiple_turn_outputs( - [ - [get_function_tool_call("foo")], - [get_function_tool_call("foo")], - [get_function_tool_call("foo")], - [get_function_tool_call("foo")], - [get_function_tool_call("foo")], - ] - ) - - with pytest.raises(MaxTurnsExceeded): - result = Runner.run_streamed(agent, input="user_message", max_turns=2) - async for _ in result.stream_events(): - pass - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 5, ( - f"should have 1 agent, 2 generations, 2 function calls, got " - f"{len(spans)} with data: {[x.span_data for x in spans]}" - ) - - agent_span = [x for x in spans if isinstance(x.span_data, AgentSpanData)][-1] - assert agent_span.error, "last agent should have error" - - -def input_guardrail_function( - context: RunContextWrapper[Any], - agent: Agent[Any], - input: str | list[TResponseInputItem], -) -> GuardrailFunctionOutput: - return GuardrailFunctionOutput( - output_info=None, - tripwire_triggered=True, - ) - - -@pytest.mark.asyncio -async def test_input_guardrail_error(): - model = FakeModel() - - agent = Agent( - name="test", - model=model, - input_guardrails=[InputGuardrail(guardrail_function=input_guardrail_function)], - ) - model.set_next_output([get_text_message("some_message")]) - - with pytest.raises(InputGuardrailTripwireTriggered): - result = Runner.run_streamed(agent, input="user_message") - async for _ in result.stream_events(): - pass - - await asyncio.sleep(1) - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 2, ( - f"should have 1 agent, 1 guardrail, got {len(spans)} with data: " - f"{[x.span_data for x in spans]}" - ) - - agent_span = [x for x in spans if isinstance(x.span_data, AgentSpanData)][-1] - assert agent_span.error, "last agent should have error" - - -def output_guardrail_function( - context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any -) -> GuardrailFunctionOutput: - return GuardrailFunctionOutput( - output_info=None, - tripwire_triggered=True, - ) - - -@pytest.mark.asyncio -async def test_output_guardrail_error(): - model = FakeModel() - - agent = Agent( - name="test", - model=model, - output_guardrails=[ - OutputGuardrail(guardrail_function=output_guardrail_function) - ], - ) - model.set_next_output([get_text_message("some_message")]) - - with pytest.raises(OutputGuardrailTripwireTriggered): - result = Runner.run_streamed(agent, input="user_message") - async for _ in result.stream_events(): - pass - - await asyncio.sleep(1) - - traces = fetch_traces() - assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" - - spans = fetch_ordered_spans() - assert len(spans) == 2, ( - f"should have 1 agent, 1 guardrail, got {len(spans)} with data: " - f"{[x.span_data for x in spans]}" - ) - - agent_span = [x for x in spans if isinstance(x.span_data, AgentSpanData)][-1] - assert agent_span.error, "last agent should have error" diff --git a/pkg/hanzo-agent/tests/testing_processor.py b/pkg/hanzo-agent/tests/testing_processor.py deleted file mode 100644 index 258a08dc9..000000000 --- a/pkg/hanzo-agent/tests/testing_processor.py +++ /dev/null @@ -1,79 +0,0 @@ -from __future__ import annotations - -import threading -from typing import Any, Literal - -from agents.tracing import Span, Trace, TracingProcessor - -TestSpanProcessorEvent = Literal["trace_start", "trace_end", "span_start", "span_end"] - - -class SpanProcessorForTests(TracingProcessor): - """ - A simple processor that stores finished spans in memory. - This is thread-safe and suitable for tests or basic usage. - """ - - def __init__(self) -> None: - self._lock = threading.Lock() - # Dictionary of trace_id -> list of spans - self._spans: list[Span[Any]] = [] - self._traces: list[Trace] = [] - self._events: list[TestSpanProcessorEvent] = [] - - def on_trace_start(self, trace: Trace) -> None: - with self._lock: - self._traces.append(trace) - self._events.append("trace_start") - - def on_trace_end(self, trace: Trace) -> None: - with self._lock: - # We don't append the trace here, we want to do that in on_trace_start - self._events.append("trace_end") - - def on_span_start(self, span: Span[Any]) -> None: - with self._lock: - # Purposely not appending the span here, we want to do that in on_span_end - self._events.append("span_start") - - def on_span_end(self, span: Span[Any]) -> None: - with self._lock: - self._events.append("span_end") - self._spans.append(span) - - def get_ordered_spans(self, including_empty: bool = False) -> list[Span[Any]]: - with self._lock: - spans = [x for x in self._spans if including_empty or x.export()] - return sorted(spans, key=lambda x: x.started_at or 0) - - def get_traces(self, including_empty: bool = False) -> list[Trace]: - with self._lock: - traces = [x for x in self._traces if including_empty or x.export()] - return traces - - def clear(self) -> None: - with self._lock: - self._spans.clear() - self._traces.clear() - self._events.clear() - - def shutdown(self) -> None: - pass - - def force_flush(self) -> None: - pass - - -SPAN_PROCESSOR_TESTING = SpanProcessorForTests() - - -def fetch_ordered_spans() -> list[Span[Any]]: - return SPAN_PROCESSOR_TESTING.get_ordered_spans() - - -def fetch_traces() -> list[Trace]: - return SPAN_PROCESSOR_TESTING.get_traces() - - -def fetch_events() -> list[TestSpanProcessorEvent]: - return SPAN_PROCESSOR_TESTING._events diff --git a/pkg/hanzo-agent/uv.lock b/pkg/hanzo-agent/uv.lock deleted file mode 100644 index e0d7bcc3f..000000000 --- a/pkg/hanzo-agent/uv.lock +++ /dev/null @@ -1,4804 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" -resolution-markers = [ - "python_full_version >= '3.14'", - "python_full_version == '3.13.*'", - "python_full_version < '3.13'", -] - -[[package]] -name = "aiocache" -version = "0.12.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7a/64/b945b8025a9d1e6e2138845f4022165d3b337f55f50984fbc6a4c0a1e355/aiocache-0.12.3.tar.gz", hash = "sha256:f528b27bf4d436b497a1d0d1a8f59a542c153ab1e37c3621713cb376d44c4713", size = 132196, upload-time = "2024-09-25T13:20:23.823Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/37/d7/15d67e05b235d1ed8c3ce61688fe4d84130e72af1657acadfaac3479f4cf/aiocache-0.12.3-py2.py3-none-any.whl", hash = "sha256:889086fc24710f431937b87ad3720a289f7fc31c4fd8b68e9f918b9bacd8270d", size = 28199, upload-time = "2024-09-25T13:20:22.688Z" }, -] - -[[package]] -name = "aiofiles" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/c3/534eac40372d8ee36ef40df62ec129bee4fdb5ad9706e58a29be53b2c970/aiofiles-25.1.0.tar.gz", hash = "sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2", size = 46354, upload-time = "2025-10-09T20:51:04.358Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/8a/340a1555ae33d7354dbca4faa54948d76d89a27ceef032c8c3bc661d003e/aiofiles-25.1.0-py3-none-any.whl", hash = "sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695", size = 14668, upload-time = "2025-10-09T20:51:03.174Z" }, -] - -[[package]] -name = "aiohappyeyeballs" -version = "2.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, -] - -[[package]] -name = "aiohttp" -version = "3.13.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohappyeyeballs" }, - { name = "aiosignal" }, - { name = "attrs" }, - { name = "frozenlist" }, - { name = "multidict" }, - { name = "propcache" }, - { name = "yarl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732, upload-time = "2026-01-03T17:30:14.23Z" }, - { url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293, upload-time = "2026-01-03T17:30:15.96Z" }, - { url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533, upload-time = "2026-01-03T17:30:17.431Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839, upload-time = "2026-01-03T17:30:19.422Z" }, - { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932, upload-time = "2026-01-03T17:30:21.756Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906, upload-time = "2026-01-03T17:30:23.932Z" }, - { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020, upload-time = "2026-01-03T17:30:26Z" }, - { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181, upload-time = "2026-01-03T17:30:27.554Z" }, - { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794, upload-time = "2026-01-03T17:30:29.254Z" }, - { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900, upload-time = "2026-01-03T17:30:31.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239, upload-time = "2026-01-03T17:30:32.703Z" }, - { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527, upload-time = "2026-01-03T17:30:34.695Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489, upload-time = "2026-01-03T17:30:36.864Z" }, - { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852, upload-time = "2026-01-03T17:30:39.433Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379, upload-time = "2026-01-03T17:30:41.081Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4a/1a3fee7c21350cac78e5c5cef711bac1b94feca07399f3d406972e2d8fcd/aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", size = 428253, upload-time = "2026-01-03T17:30:42.644Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b7/76175c7cb4eb73d91ad63c34e29fc4f77c9386bba4a65b53ba8e05ee3c39/aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", size = 455407, upload-time = "2026-01-03T17:30:44.195Z" }, - { url = "https://files.pythonhosted.org/packages/97/8a/12ca489246ca1faaf5432844adbfce7ff2cc4997733e0af120869345643a/aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", size = 734190, upload-time = "2026-01-03T17:30:45.832Z" }, - { url = "https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", size = 491783, upload-time = "2026-01-03T17:30:47.466Z" }, - { url = "https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", size = 490704, upload-time = "2026-01-03T17:30:49.373Z" }, - { url = "https://files.pythonhosted.org/packages/6d/40/a46b03ca03936f832bc7eaa47cfbb1ad012ba1be4790122ee4f4f8cba074/aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", size = 1720652, upload-time = "2026-01-03T17:30:50.974Z" }, - { url = "https://files.pythonhosted.org/packages/f7/7e/917fe18e3607af92657e4285498f500dca797ff8c918bd7d90b05abf6c2a/aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", size = 1692014, upload-time = "2026-01-03T17:30:52.729Z" }, - { url = "https://files.pythonhosted.org/packages/71/b6/cefa4cbc00d315d68973b671cf105b21a609c12b82d52e5d0c9ae61d2a09/aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", size = 1759777, upload-time = "2026-01-03T17:30:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e3/e06ee07b45e59e6d81498b591fc589629be1553abb2a82ce33efe2a7b068/aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", size = 1861276, upload-time = "2026-01-03T17:30:56.512Z" }, - { url = "https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", size = 1743131, upload-time = "2026-01-03T17:30:58.256Z" }, - { url = "https://files.pythonhosted.org/packages/04/98/3d21dde21889b17ca2eea54fdcff21b27b93f45b7bb94ca029c31ab59dc3/aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", size = 1556863, upload-time = "2026-01-03T17:31:00.445Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/da0c3ab1192eaf64782b03971ab4055b475d0db07b17eff925e8c93b3aa5/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", size = 1682793, upload-time = "2026-01-03T17:31:03.024Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0f/5802ada182f575afa02cbd0ec5180d7e13a402afb7c2c03a9aa5e5d49060/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", size = 1716676, upload-time = "2026-01-03T17:31:04.842Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8c/714d53bd8b5a4560667f7bbbb06b20c2382f9c7847d198370ec6526af39c/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", size = 1733217, upload-time = "2026-01-03T17:31:06.868Z" }, - { url = "https://files.pythonhosted.org/packages/7d/79/e2176f46d2e963facea939f5be2d26368ce543622be6f00a12844d3c991f/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", size = 1552303, upload-time = "2026-01-03T17:31:08.958Z" }, - { url = "https://files.pythonhosted.org/packages/ab/6a/28ed4dea1759916090587d1fe57087b03e6c784a642b85ef48217b0277ae/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", size = 1763673, upload-time = "2026-01-03T17:31:10.676Z" }, - { url = "https://files.pythonhosted.org/packages/e8/35/4a3daeb8b9fab49240d21c04d50732313295e4bd813a465d840236dd0ce1/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", size = 1721120, upload-time = "2026-01-03T17:31:12.575Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9f/d643bb3c5fb99547323e635e251c609fbbc660d983144cfebec529e09264/aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", size = 427383, upload-time = "2026-01-03T17:31:14.382Z" }, - { url = "https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", size = 453899, upload-time = "2026-01-03T17:31:15.958Z" }, - { url = "https://files.pythonhosted.org/packages/99/36/5b6514a9f5d66f4e2597e40dea2e3db271e023eb7a5d22defe96ba560996/aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808", size = 737238, upload-time = "2026-01-03T17:31:17.909Z" }, - { url = "https://files.pythonhosted.org/packages/f7/49/459327f0d5bcd8c6c9ca69e60fdeebc3622861e696490d8674a6d0cb90a6/aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415", size = 492292, upload-time = "2026-01-03T17:31:19.919Z" }, - { url = "https://files.pythonhosted.org/packages/e8/0b/b97660c5fd05d3495b4eb27f2d0ef18dc1dc4eff7511a9bf371397ff0264/aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f", size = 493021, upload-time = "2026-01-03T17:31:21.636Z" }, - { url = "https://files.pythonhosted.org/packages/54/d4/438efabdf74e30aeceb890c3290bbaa449780583b1270b00661126b8aae4/aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6", size = 1717263, upload-time = "2026-01-03T17:31:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/71/f2/7bddc7fd612367d1459c5bcf598a9e8f7092d6580d98de0e057eb42697ad/aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687", size = 1669107, upload-time = "2026-01-03T17:31:25.334Z" }, - { url = "https://files.pythonhosted.org/packages/00/5a/1aeaecca40e22560f97610a329e0e5efef5e0b5afdf9f857f0d93839ab2e/aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26", size = 1760196, upload-time = "2026-01-03T17:31:27.394Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f8/0ff6992bea7bd560fc510ea1c815f87eedd745fe035589c71ce05612a19a/aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a", size = 1843591, upload-time = "2026-01-03T17:31:29.238Z" }, - { url = "https://files.pythonhosted.org/packages/e3/d1/e30e537a15f53485b61f5be525f2157da719819e8377298502aebac45536/aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1", size = 1720277, upload-time = "2026-01-03T17:31:31.053Z" }, - { url = "https://files.pythonhosted.org/packages/84/45/23f4c451d8192f553d38d838831ebbc156907ea6e05557f39563101b7717/aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25", size = 1548575, upload-time = "2026-01-03T17:31:32.87Z" }, - { url = "https://files.pythonhosted.org/packages/6a/ed/0a42b127a43712eda7807e7892c083eadfaf8429ca8fb619662a530a3aab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603", size = 1679455, upload-time = "2026-01-03T17:31:34.76Z" }, - { url = "https://files.pythonhosted.org/packages/2e/b5/c05f0c2b4b4fe2c9d55e73b6d3ed4fd6c9dc2684b1d81cbdf77e7fad9adb/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a", size = 1687417, upload-time = "2026-01-03T17:31:36.699Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6b/915bc5dad66aef602b9e459b5a973529304d4e89ca86999d9d75d80cbd0b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926", size = 1729968, upload-time = "2026-01-03T17:31:38.622Z" }, - { url = "https://files.pythonhosted.org/packages/11/3b/e84581290a9520024a08640b63d07673057aec5ca548177a82026187ba73/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba", size = 1545690, upload-time = "2026-01-03T17:31:40.57Z" }, - { url = "https://files.pythonhosted.org/packages/f5/04/0c3655a566c43fd647c81b895dfe361b9f9ad6d58c19309d45cff52d6c3b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c", size = 1746390, upload-time = "2026-01-03T17:31:42.857Z" }, - { url = "https://files.pythonhosted.org/packages/1f/53/71165b26978f719c3419381514c9690bd5980e764a09440a10bb816ea4ab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43", size = 1702188, upload-time = "2026-01-03T17:31:44.984Z" }, - { url = "https://files.pythonhosted.org/packages/29/a7/cbe6c9e8e136314fa1980da388a59d2f35f35395948a08b6747baebb6aa6/aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1", size = 433126, upload-time = "2026-01-03T17:31:47.463Z" }, - { url = "https://files.pythonhosted.org/packages/de/56/982704adea7d3b16614fc5936014e9af85c0e34b58f9046655817f04306e/aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984", size = 459128, upload-time = "2026-01-03T17:31:49.2Z" }, - { url = "https://files.pythonhosted.org/packages/6c/2a/3c79b638a9c3d4658d345339d22070241ea341ed4e07b5ac60fb0f418003/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c", size = 769512, upload-time = "2026-01-03T17:31:51.134Z" }, - { url = "https://files.pythonhosted.org/packages/29/b9/3e5014d46c0ab0db8707e0ac2711ed28c4da0218c358a4e7c17bae0d8722/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592", size = 506444, upload-time = "2026-01-03T17:31:52.85Z" }, - { url = "https://files.pythonhosted.org/packages/90/03/c1d4ef9a054e151cd7839cdc497f2638f00b93cbe8043983986630d7a80c/aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f", size = 510798, upload-time = "2026-01-03T17:31:54.91Z" }, - { url = "https://files.pythonhosted.org/packages/ea/76/8c1e5abbfe8e127c893fe7ead569148a4d5a799f7cf958d8c09f3eedf097/aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29", size = 1868835, upload-time = "2026-01-03T17:31:56.733Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ac/984c5a6f74c363b01ff97adc96a3976d9c98940b8969a1881575b279ac5d/aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc", size = 1720486, upload-time = "2026-01-03T17:31:58.65Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9a/b7039c5f099c4eb632138728828b33428585031a1e658d693d41d07d89d1/aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2", size = 1847951, upload-time = "2026-01-03T17:32:00.989Z" }, - { url = "https://files.pythonhosted.org/packages/3c/02/3bec2b9a1ba3c19ff89a43a19324202b8eb187ca1e928d8bdac9bbdddebd/aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587", size = 1941001, upload-time = "2026-01-03T17:32:03.122Z" }, - { url = "https://files.pythonhosted.org/packages/37/df/d879401cedeef27ac4717f6426c8c36c3091c6e9f08a9178cc87549c537f/aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8", size = 1797246, upload-time = "2026-01-03T17:32:05.255Z" }, - { url = "https://files.pythonhosted.org/packages/8d/15/be122de1f67e6953add23335c8ece6d314ab67c8bebb3f181063010795a7/aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632", size = 1627131, upload-time = "2026-01-03T17:32:07.607Z" }, - { url = "https://files.pythonhosted.org/packages/12/12/70eedcac9134cfa3219ab7af31ea56bc877395b1ac30d65b1bc4b27d0438/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64", size = 1795196, upload-time = "2026-01-03T17:32:09.59Z" }, - { url = "https://files.pythonhosted.org/packages/32/11/b30e1b1cd1f3054af86ebe60df96989c6a414dd87e27ad16950eee420bea/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0", size = 1782841, upload-time = "2026-01-03T17:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/88/0d/d98a9367b38912384a17e287850f5695c528cff0f14f791ce8ee2e4f7796/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56", size = 1795193, upload-time = "2026-01-03T17:32:13.705Z" }, - { url = "https://files.pythonhosted.org/packages/43/a5/a2dfd1f5ff5581632c7f6a30e1744deda03808974f94f6534241ef60c751/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72", size = 1621979, upload-time = "2026-01-03T17:32:15.965Z" }, - { url = "https://files.pythonhosted.org/packages/fa/f0/12973c382ae7c1cccbc4417e129c5bf54c374dfb85af70893646e1f0e749/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df", size = 1822193, upload-time = "2026-01-03T17:32:18.219Z" }, - { url = "https://files.pythonhosted.org/packages/3c/5f/24155e30ba7f8c96918af1350eb0663e2430aad9e001c0489d89cd708ab1/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", size = 1769801, upload-time = "2026-01-03T17:32:20.25Z" }, - { url = "https://files.pythonhosted.org/packages/eb/f8/7314031ff5c10e6ece114da79b338ec17eeff3a079e53151f7e9f43c4723/aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", size = 466523, upload-time = "2026-01-03T17:32:22.215Z" }, - { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694, upload-time = "2026-01-03T17:32:24.546Z" }, -] - -[[package]] -name = "aiosignal" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "frozenlist" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, -] - -[[package]] -name = "annotated-doc" -version = "0.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, -] - -[[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.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, -] - -[[package]] -name = "attrs" -version = "25.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, -] - -[[package]] -name = "babel" -version = "2.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, -] - -[[package]] -name = "backoff" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" }, -] - -[[package]] -name = "backrefs" -version = "6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/86/e3/bb3a439d5cb255c4774724810ad8073830fac9c9dee123555820c1bcc806/backrefs-6.1.tar.gz", hash = "sha256:3bba1749aafe1db9b915f00e0dd166cba613b6f788ffd63060ac3485dc9be231", size = 7011962, upload-time = "2025-11-15T14:52:08.323Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ee/c216d52f58ea75b5e1841022bbae24438b19834a29b163cb32aa3a2a7c6e/backrefs-6.1-py310-none-any.whl", hash = "sha256:2a2ccb96302337ce61ee4717ceacfbf26ba4efb1d55af86564b8bbaeda39cac1", size = 381059, upload-time = "2025-11-15T14:51:59.758Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9a/8da246d988ded941da96c7ed945d63e94a445637eaad985a0ed88787cb89/backrefs-6.1-py311-none-any.whl", hash = "sha256:e82bba3875ee4430f4de4b6db19429a27275d95a5f3773c57e9e18abc23fd2b7", size = 392854, upload-time = "2025-11-15T14:52:01.194Z" }, - { url = "https://files.pythonhosted.org/packages/37/c9/fd117a6f9300c62bbc33bc337fd2b3c6bfe28b6e9701de336b52d7a797ad/backrefs-6.1-py312-none-any.whl", hash = "sha256:c64698c8d2269343d88947c0735cb4b78745bd3ba590e10313fbf3f78c34da5a", size = 398770, upload-time = "2025-11-15T14:52:02.584Z" }, - { url = "https://files.pythonhosted.org/packages/eb/95/7118e935b0b0bd3f94dfec2d852fd4e4f4f9757bdb49850519acd245cd3a/backrefs-6.1-py313-none-any.whl", hash = "sha256:4c9d3dc1e2e558965202c012304f33d4e0e477e1c103663fd2c3cc9bb18b0d05", size = 400726, upload-time = "2025-11-15T14:52:04.093Z" }, - { url = "https://files.pythonhosted.org/packages/1d/72/6296bad135bfafd3254ae3648cd152980a424bd6fed64a101af00cc7ba31/backrefs-6.1-py314-none-any.whl", hash = "sha256:13eafbc9ccd5222e9c1f0bec563e6d2a6d21514962f11e7fc79872fd56cbc853", size = 412584, upload-time = "2025-11-15T14:52:05.233Z" }, - { url = "https://files.pythonhosted.org/packages/02/e3/a4fa1946722c4c7b063cc25043a12d9ce9b4323777f89643be74cef2993c/backrefs-6.1-py39-none-any.whl", hash = "sha256:a9e99b8a4867852cad177a6430e31b0f6e495d65f8c6c134b68c14c3c95bf4b0", size = 381058, upload-time = "2025-11-15T14:52:06.698Z" }, -] - -[[package]] -name = "bcrypt" -version = "5.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/36/3329e2518d70ad8e2e5817d5a4cac6bba05a47767ec416c7d020a965f408/bcrypt-5.0.0.tar.gz", hash = "sha256:f748f7c2d6fd375cc93d3fba7ef4a9e3a092421b8dbf34d8d4dc06be9492dfdd", size = 25386, upload-time = "2025-09-25T19:50:47.829Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/85/3e65e01985fddf25b64ca67275bb5bdb4040bd1a53b66d355c6c37c8a680/bcrypt-5.0.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f3c08197f3039bec79cee59a606d62b96b16669cff3949f21e74796b6e3cd2be", size = 481806, upload-time = "2025-09-25T19:49:05.102Z" }, - { url = "https://files.pythonhosted.org/packages/44/dc/01eb79f12b177017a726cbf78330eb0eb442fae0e7b3dfd84ea2849552f3/bcrypt-5.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:200af71bc25f22006f4069060c88ed36f8aa4ff7f53e67ff04d2ab3f1e79a5b2", size = 268626, upload-time = "2025-09-25T19:49:06.723Z" }, - { url = "https://files.pythonhosted.org/packages/8c/cf/e82388ad5959c40d6afd94fb4743cc077129d45b952d46bdc3180310e2df/bcrypt-5.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:baade0a5657654c2984468efb7d6c110db87ea63ef5a4b54732e7e337253e44f", size = 271853, upload-time = "2025-09-25T19:49:08.028Z" }, - { url = "https://files.pythonhosted.org/packages/ec/86/7134b9dae7cf0efa85671651341f6afa695857fae172615e960fb6a466fa/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c58b56cdfb03202b3bcc9fd8daee8e8e9b6d7e3163aa97c631dfcfcc24d36c86", size = 269793, upload-time = "2025-09-25T19:49:09.727Z" }, - { url = "https://files.pythonhosted.org/packages/cc/82/6296688ac1b9e503d034e7d0614d56e80c5d1a08402ff856a4549cb59207/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4bfd2a34de661f34d0bda43c3e4e79df586e4716ef401fe31ea39d69d581ef23", size = 289930, upload-time = "2025-09-25T19:49:11.204Z" }, - { url = "https://files.pythonhosted.org/packages/d1/18/884a44aa47f2a3b88dd09bc05a1e40b57878ecd111d17e5bba6f09f8bb77/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ed2e1365e31fc73f1825fa830f1c8f8917ca1b3ca6185773b349c20fd606cec2", size = 272194, upload-time = "2025-09-25T19:49:12.524Z" }, - { url = "https://files.pythonhosted.org/packages/0e/8f/371a3ab33c6982070b674f1788e05b656cfbf5685894acbfef0c65483a59/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:83e787d7a84dbbfba6f250dd7a5efd689e935f03dd83b0f919d39349e1f23f83", size = 269381, upload-time = "2025-09-25T19:49:14.308Z" }, - { url = "https://files.pythonhosted.org/packages/b1/34/7e4e6abb7a8778db6422e88b1f06eb07c47682313997ee8a8f9352e5a6f1/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:137c5156524328a24b9fac1cb5db0ba618bc97d11970b39184c1d87dc4bf1746", size = 271750, upload-time = "2025-09-25T19:49:15.584Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1b/54f416be2499bd72123c70d98d36c6cd61a4e33d9b89562c22481c81bb30/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:38cac74101777a6a7d3b3e3cfefa57089b5ada650dce2baf0cbdd9d65db22a9e", size = 303757, upload-time = "2025-09-25T19:49:17.244Z" }, - { url = "https://files.pythonhosted.org/packages/13/62/062c24c7bcf9d2826a1a843d0d605c65a755bc98002923d01fd61270705a/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:d8d65b564ec849643d9f7ea05c6d9f0cd7ca23bdd4ac0c2dbef1104ab504543d", size = 306740, upload-time = "2025-09-25T19:49:18.693Z" }, - { url = "https://files.pythonhosted.org/packages/d5/c8/1fdbfc8c0f20875b6b4020f3c7dc447b8de60aa0be5faaf009d24242aec9/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:741449132f64b3524e95cd30e5cd3343006ce146088f074f31ab26b94e6c75ba", size = 334197, upload-time = "2025-09-25T19:49:20.523Z" }, - { url = "https://files.pythonhosted.org/packages/a6/c1/8b84545382d75bef226fbc6588af0f7b7d095f7cd6a670b42a86243183cd/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:212139484ab3207b1f0c00633d3be92fef3c5f0af17cad155679d03ff2ee1e41", size = 352974, upload-time = "2025-09-25T19:49:22.254Z" }, - { url = "https://files.pythonhosted.org/packages/10/a6/ffb49d4254ed085e62e3e5dd05982b4393e32fe1e49bb1130186617c29cd/bcrypt-5.0.0-cp313-cp313t-win32.whl", hash = "sha256:9d52ed507c2488eddd6a95bccee4e808d3234fa78dd370e24bac65a21212b861", size = 148498, upload-time = "2025-09-25T19:49:24.134Z" }, - { url = "https://files.pythonhosted.org/packages/48/a9/259559edc85258b6d5fc5471a62a3299a6aa37a6611a169756bf4689323c/bcrypt-5.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f6984a24db30548fd39a44360532898c33528b74aedf81c26cf29c51ee47057e", size = 145853, upload-time = "2025-09-25T19:49:25.702Z" }, - { url = "https://files.pythonhosted.org/packages/2d/df/9714173403c7e8b245acf8e4be8876aac64a209d1b392af457c79e60492e/bcrypt-5.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9fffdb387abe6aa775af36ef16f55e318dcda4194ddbf82007a6f21da29de8f5", size = 139626, upload-time = "2025-09-25T19:49:26.928Z" }, - { url = "https://files.pythonhosted.org/packages/f8/14/c18006f91816606a4abe294ccc5d1e6f0e42304df5a33710e9e8e95416e1/bcrypt-5.0.0-cp314-cp314t-macosx_10_12_universal2.whl", hash = "sha256:4870a52610537037adb382444fefd3706d96d663ac44cbb2f37e3919dca3d7ef", size = 481862, upload-time = "2025-09-25T19:49:28.365Z" }, - { url = "https://files.pythonhosted.org/packages/67/49/dd074d831f00e589537e07a0725cf0e220d1f0d5d8e85ad5bbff251c45aa/bcrypt-5.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48f753100931605686f74e27a7b49238122aa761a9aefe9373265b8b7aa43ea4", size = 268544, upload-time = "2025-09-25T19:49:30.39Z" }, - { url = "https://files.pythonhosted.org/packages/f5/91/50ccba088b8c474545b034a1424d05195d9fcbaaf802ab8bfe2be5a4e0d7/bcrypt-5.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f70aadb7a809305226daedf75d90379c397b094755a710d7014b8b117df1ebbf", size = 271787, upload-time = "2025-09-25T19:49:32.144Z" }, - { url = "https://files.pythonhosted.org/packages/aa/e7/d7dba133e02abcda3b52087a7eea8c0d4f64d3e593b4fffc10c31b7061f3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:744d3c6b164caa658adcb72cb8cc9ad9b4b75c7db507ab4bc2480474a51989da", size = 269753, upload-time = "2025-09-25T19:49:33.885Z" }, - { url = "https://files.pythonhosted.org/packages/33/fc/5b145673c4b8d01018307b5c2c1fc87a6f5a436f0ad56607aee389de8ee3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a28bc05039bdf3289d757f49d616ab3efe8cf40d8e8001ccdd621cd4f98f4fc9", size = 289587, upload-time = "2025-09-25T19:49:35.144Z" }, - { url = "https://files.pythonhosted.org/packages/27/d7/1ff22703ec6d4f90e62f1a5654b8867ef96bafb8e8102c2288333e1a6ca6/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:7f277a4b3390ab4bebe597800a90da0edae882c6196d3038a73adf446c4f969f", size = 272178, upload-time = "2025-09-25T19:49:36.793Z" }, - { url = "https://files.pythonhosted.org/packages/c8/88/815b6d558a1e4d40ece04a2f84865b0fef233513bd85fd0e40c294272d62/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:79cfa161eda8d2ddf29acad370356b47f02387153b11d46042e93a0a95127493", size = 269295, upload-time = "2025-09-25T19:49:38.164Z" }, - { url = "https://files.pythonhosted.org/packages/51/8c/e0db387c79ab4931fc89827d37608c31cc57b6edc08ccd2386139028dc0d/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a5393eae5722bcef046a990b84dff02b954904c36a194f6cfc817d7dca6c6f0b", size = 271700, upload-time = "2025-09-25T19:49:39.917Z" }, - { url = "https://files.pythonhosted.org/packages/06/83/1570edddd150f572dbe9fc00f6203a89fc7d4226821f67328a85c330f239/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f4c94dec1b5ab5d522750cb059bb9409ea8872d4494fd152b53cca99f1ddd8c", size = 334034, upload-time = "2025-09-25T19:49:41.227Z" }, - { url = "https://files.pythonhosted.org/packages/c9/f2/ea64e51a65e56ae7a8a4ec236c2bfbdd4b23008abd50ac33fbb2d1d15424/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0cae4cb350934dfd74c020525eeae0a5f79257e8a201c0c176f4b84fdbf2a4b4", size = 352766, upload-time = "2025-09-25T19:49:43.08Z" }, - { url = "https://files.pythonhosted.org/packages/d7/d4/1a388d21ee66876f27d1a1f41287897d0c0f1712ef97d395d708ba93004c/bcrypt-5.0.0-cp314-cp314t-win32.whl", hash = "sha256:b17366316c654e1ad0306a6858e189fc835eca39f7eb2cafd6aaca8ce0c40a2e", size = 152449, upload-time = "2025-09-25T19:49:44.971Z" }, - { url = "https://files.pythonhosted.org/packages/3f/61/3291c2243ae0229e5bca5d19f4032cecad5dfb05a2557169d3a69dc0ba91/bcrypt-5.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:92864f54fb48b4c718fc92a32825d0e42265a627f956bc0361fe869f1adc3e7d", size = 149310, upload-time = "2025-09-25T19:49:46.162Z" }, - { url = "https://files.pythonhosted.org/packages/3e/89/4b01c52ae0c1a681d4021e5dd3e45b111a8fb47254a274fa9a378d8d834b/bcrypt-5.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dd19cf5184a90c873009244586396a6a884d591a5323f0e8a5922560718d4993", size = 143761, upload-time = "2025-09-25T19:49:47.345Z" }, - { url = "https://files.pythonhosted.org/packages/84/29/6237f151fbfe295fe3e074ecc6d44228faa1e842a81f6d34a02937ee1736/bcrypt-5.0.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:fc746432b951e92b58317af8e0ca746efe93e66555f1b40888865ef5bf56446b", size = 494553, upload-time = "2025-09-25T19:49:49.006Z" }, - { url = "https://files.pythonhosted.org/packages/45/b6/4c1205dde5e464ea3bd88e8742e19f899c16fa8916fb8510a851fae985b5/bcrypt-5.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c2388ca94ffee269b6038d48747f4ce8df0ffbea43f31abfa18ac72f0218effb", size = 275009, upload-time = "2025-09-25T19:49:50.581Z" }, - { url = "https://files.pythonhosted.org/packages/3b/71/427945e6ead72ccffe77894b2655b695ccf14ae1866cd977e185d606dd2f/bcrypt-5.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:560ddb6ec730386e7b3b26b8b4c88197aaed924430e7b74666a586ac997249ef", size = 278029, upload-time = "2025-09-25T19:49:52.533Z" }, - { url = "https://files.pythonhosted.org/packages/17/72/c344825e3b83c5389a369c8a8e58ffe1480b8a699f46c127c34580c4666b/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d79e5c65dcc9af213594d6f7f1fa2c98ad3fc10431e7aa53c176b441943efbdd", size = 275907, upload-time = "2025-09-25T19:49:54.709Z" }, - { url = "https://files.pythonhosted.org/packages/0b/7e/d4e47d2df1641a36d1212e5c0514f5291e1a956a7749f1e595c07a972038/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2b732e7d388fa22d48920baa267ba5d97cca38070b69c0e2d37087b381c681fd", size = 296500, upload-time = "2025-09-25T19:49:56.013Z" }, - { url = "https://files.pythonhosted.org/packages/0f/c3/0ae57a68be2039287ec28bc463b82e4b8dc23f9d12c0be331f4782e19108/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0c8e093ea2532601a6f686edbc2c6b2ec24131ff5c52f7610dd64fa4553b5464", size = 278412, upload-time = "2025-09-25T19:49:57.356Z" }, - { url = "https://files.pythonhosted.org/packages/45/2b/77424511adb11e6a99e3a00dcc7745034bee89036ad7d7e255a7e47be7d8/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5b1589f4839a0899c146e8892efe320c0fa096568abd9b95593efac50a87cb75", size = 275486, upload-time = "2025-09-25T19:49:59.116Z" }, - { url = "https://files.pythonhosted.org/packages/43/0a/405c753f6158e0f3f14b00b462d8bca31296f7ecfc8fc8bc7919c0c7d73a/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:89042e61b5e808b67daf24a434d89bab164d4de1746b37a8d173b6b14f3db9ff", size = 277940, upload-time = "2025-09-25T19:50:00.869Z" }, - { url = "https://files.pythonhosted.org/packages/62/83/b3efc285d4aadc1fa83db385ec64dcfa1707e890eb42f03b127d66ac1b7b/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:e3cf5b2560c7b5a142286f69bde914494b6d8f901aaa71e453078388a50881c4", size = 310776, upload-time = "2025-09-25T19:50:02.393Z" }, - { url = "https://files.pythonhosted.org/packages/95/7d/47ee337dacecde6d234890fe929936cb03ebc4c3a7460854bbd9c97780b8/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f632fd56fc4e61564f78b46a2269153122db34988e78b6be8b32d28507b7eaeb", size = 312922, upload-time = "2025-09-25T19:50:04.232Z" }, - { url = "https://files.pythonhosted.org/packages/d6/3a/43d494dfb728f55f4e1cf8fd435d50c16a2d75493225b54c8d06122523c6/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:801cad5ccb6b87d1b430f183269b94c24f248dddbbc5c1f78b6ed231743e001c", size = 341367, upload-time = "2025-09-25T19:50:05.559Z" }, - { url = "https://files.pythonhosted.org/packages/55/ab/a0727a4547e383e2e22a630e0f908113db37904f58719dc48d4622139b5c/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3cf67a804fc66fc217e6914a5635000259fbbbb12e78a99488e4d5ba445a71eb", size = 359187, upload-time = "2025-09-25T19:50:06.916Z" }, - { url = "https://files.pythonhosted.org/packages/1b/bb/461f352fdca663524b4643d8b09e8435b4990f17fbf4fea6bc2a90aa0cc7/bcrypt-5.0.0-cp38-abi3-win32.whl", hash = "sha256:3abeb543874b2c0524ff40c57a4e14e5d3a66ff33fb423529c88f180fd756538", size = 153752, upload-time = "2025-09-25T19:50:08.515Z" }, - { url = "https://files.pythonhosted.org/packages/41/aa/4190e60921927b7056820291f56fc57d00d04757c8b316b2d3c0d1d6da2c/bcrypt-5.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:35a77ec55b541e5e583eb3436ffbbf53b0ffa1fa16ca6782279daf95d146dcd9", size = 150881, upload-time = "2025-09-25T19:50:09.742Z" }, - { url = "https://files.pythonhosted.org/packages/54/12/cd77221719d0b39ac0b55dbd39358db1cd1246e0282e104366ebbfb8266a/bcrypt-5.0.0-cp38-abi3-win_arm64.whl", hash = "sha256:cde08734f12c6a4e28dc6755cd11d3bdfea608d93d958fffbe95a7026ebe4980", size = 144931, upload-time = "2025-09-25T19:50:11.016Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ba/2af136406e1c3839aea9ecadc2f6be2bcd1eff255bd451dd39bcf302c47a/bcrypt-5.0.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0c418ca99fd47e9c59a301744d63328f17798b5947b0f791e9af3c1c499c2d0a", size = 495313, upload-time = "2025-09-25T19:50:12.309Z" }, - { url = "https://files.pythonhosted.org/packages/ac/ee/2f4985dbad090ace5ad1f7dd8ff94477fe089b5fab2040bd784a3d5f187b/bcrypt-5.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddb4e1500f6efdd402218ffe34d040a1196c072e07929b9820f363a1fd1f4191", size = 275290, upload-time = "2025-09-25T19:50:13.673Z" }, - { url = "https://files.pythonhosted.org/packages/e4/6e/b77ade812672d15cf50842e167eead80ac3514f3beacac8902915417f8b7/bcrypt-5.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7aeef54b60ceddb6f30ee3db090351ecf0d40ec6e2abf41430997407a46d2254", size = 278253, upload-time = "2025-09-25T19:50:15.089Z" }, - { url = "https://files.pythonhosted.org/packages/36/c4/ed00ed32f1040f7990dac7115f82273e3c03da1e1a1587a778d8cea496d8/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f0ce778135f60799d89c9693b9b398819d15f1921ba15fe719acb3178215a7db", size = 276084, upload-time = "2025-09-25T19:50:16.699Z" }, - { url = "https://files.pythonhosted.org/packages/e7/c4/fa6e16145e145e87f1fa351bbd54b429354fd72145cd3d4e0c5157cf4c70/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a71f70ee269671460b37a449f5ff26982a6f2ba493b3eabdd687b4bf35f875ac", size = 297185, upload-time = "2025-09-25T19:50:18.525Z" }, - { url = "https://files.pythonhosted.org/packages/24/b4/11f8a31d8b67cca3371e046db49baa7c0594d71eb40ac8121e2fc0888db0/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8429e1c410b4073944f03bd778a9e066e7fad723564a52ff91841d278dfc822", size = 278656, upload-time = "2025-09-25T19:50:19.809Z" }, - { url = "https://files.pythonhosted.org/packages/ac/31/79f11865f8078e192847d2cb526e3fa27c200933c982c5b2869720fa5fce/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:edfcdcedd0d0f05850c52ba3127b1fce70b9f89e0fe5ff16517df7e81fa3cbb8", size = 275662, upload-time = "2025-09-25T19:50:21.567Z" }, - { url = "https://files.pythonhosted.org/packages/d4/8d/5e43d9584b3b3591a6f9b68f755a4da879a59712981ef5ad2a0ac1379f7a/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:611f0a17aa4a25a69362dcc299fda5c8a3d4f160e2abb3831041feb77393a14a", size = 278240, upload-time = "2025-09-25T19:50:23.305Z" }, - { url = "https://files.pythonhosted.org/packages/89/48/44590e3fc158620f680a978aafe8f87a4c4320da81ed11552f0323aa9a57/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:db99dca3b1fdc3db87d7c57eac0c82281242d1eabf19dcb8a6b10eb29a2e72d1", size = 311152, upload-time = "2025-09-25T19:50:24.597Z" }, - { url = "https://files.pythonhosted.org/packages/5f/85/e4fbfc46f14f47b0d20493669a625da5827d07e8a88ee460af6cd9768b44/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:5feebf85a9cefda32966d8171f5db7e3ba964b77fdfe31919622256f80f9cf42", size = 313284, upload-time = "2025-09-25T19:50:26.268Z" }, - { url = "https://files.pythonhosted.org/packages/25/ae/479f81d3f4594456a01ea2f05b132a519eff9ab5768a70430fa1132384b1/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3ca8a166b1140436e058298a34d88032ab62f15aae1c598580333dc21d27ef10", size = 341643, upload-time = "2025-09-25T19:50:28.02Z" }, - { url = "https://files.pythonhosted.org/packages/df/d2/36a086dee1473b14276cd6ea7f61aef3b2648710b5d7f1c9e032c29b859f/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:61afc381250c3182d9078551e3ac3a41da14154fbff647ddf52a769f588c4172", size = 359698, upload-time = "2025-09-25T19:50:31.347Z" }, - { url = "https://files.pythonhosted.org/packages/c0/f6/688d2cd64bfd0b14d805ddb8a565e11ca1fb0fd6817175d58b10052b6d88/bcrypt-5.0.0-cp39-abi3-win32.whl", hash = "sha256:64d7ce196203e468c457c37ec22390f1a61c85c6f0b8160fd752940ccfb3a683", size = 153725, upload-time = "2025-09-25T19:50:34.384Z" }, - { url = "https://files.pythonhosted.org/packages/9f/b9/9d9a641194a730bda138b3dfe53f584d61c58cd5230e37566e83ec2ffa0d/bcrypt-5.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:64ee8434b0da054d830fa8e89e1c8bf30061d539044a39524ff7dec90481e5c2", size = 150912, upload-time = "2025-09-25T19:50:35.69Z" }, - { url = "https://files.pythonhosted.org/packages/27/44/d2ef5e87509158ad2187f4dd0852df80695bb1ee0cfe0a684727b01a69e0/bcrypt-5.0.0-cp39-abi3-win_arm64.whl", hash = "sha256:f2347d3534e76bf50bca5500989d6c1d05ed64b440408057a37673282c654927", size = 144953, upload-time = "2025-09-25T19:50:37.32Z" }, -] - -[[package]] -name = "bitarray" -version = "3.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/06/92fdc84448d324ab8434b78e65caf4fb4c6c90b4f8ad9bdd4c8021bfaf1e/bitarray-3.8.0.tar.gz", hash = "sha256:3eae38daffd77c9621ae80c16932eea3fb3a4af141fb7cc724d4ad93eff9210d", size = 151991, upload-time = "2025-11-02T21:41:15.117Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/a0/0c41d893eda756315491adfdbf9bc928aee3d377a7f97a8834d453aa5de1/bitarray-3.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2fcbe9b3a5996b417e030aa33a562e7e20dfc86271e53d7e841fc5df16268b8", size = 148575, upload-time = "2025-11-02T21:39:25.718Z" }, - { url = "https://files.pythonhosted.org/packages/0e/30/12ab2f4a4429bd844b419c37877caba93d676d18be71354fbbeb21d9f4cc/bitarray-3.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cd761d158f67e288fd0ebe00c3b158095ce80a4bc7c32b60c7121224003ba70d", size = 145454, upload-time = "2025-11-02T21:39:26.695Z" }, - { url = "https://files.pythonhosted.org/packages/26/58/314b3e3f219533464e120f0c51ac5123e7b1c1b91f725a4073fb70c5a858/bitarray-3.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c394a3f055b49f92626f83c1a0b6d6cd2c628f1ccd72481c3e3c6aa4695f3b20", size = 332949, upload-time = "2025-11-02T21:39:27.801Z" }, - { url = "https://files.pythonhosted.org/packages/ea/ce/ca8c706bd8341c7a22dd92d2a528af71f7e5f4726085d93f81fd768cb03b/bitarray-3.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:969fd67de8c42affdb47b38b80f1eaa79ac0ef17d65407cdd931db1675315af1", size = 360599, upload-time = "2025-11-02T21:39:28.964Z" }, - { url = "https://files.pythonhosted.org/packages/ef/dc/aa181df85f933052d962804906b282acb433cb9318b08ec2aceb4ee34faf/bitarray-3.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:99d25aff3745c54e61ab340b98400c52ebec04290a62078155e0d7eb30380220", size = 371972, upload-time = "2025-11-02T21:39:30.228Z" }, - { url = "https://files.pythonhosted.org/packages/ff/d9/b805bfa158c7bcf4df0ac19b1be581b47e1ddb792c11023aed80a7058e78/bitarray-3.8.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e645b4c365d6f1f9e0799380ad6395268f3c3b898244a650aaeb8d9d27b74c35", size = 340303, upload-time = "2025-11-02T21:39:31.342Z" }, - { url = "https://files.pythonhosted.org/packages/1f/42/5308cc97ea929e30727292617a3a88293470166851e13c9e3f16f395da55/bitarray-3.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2fa23fdb3beab313950bbb49674e8a161e61449332d3997089fe3944953f1b77", size = 330494, upload-time = "2025-11-02T21:39:32.769Z" }, - { url = "https://files.pythonhosted.org/packages/4c/89/64f1596cb80433323efdbc8dcd0d6e57c40dfbe6ea3341623f34ec397edd/bitarray-3.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:165052a0e61c880f7093808a0c524ce1b3555bfa114c0dfb5c809cd07918a60d", size = 358123, upload-time = "2025-11-02T21:39:34.331Z" }, - { url = "https://files.pythonhosted.org/packages/27/fd/f3d49c5443b57087f888b5e118c8dd78bb7c8e8cfeeed250f8e92128a05f/bitarray-3.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:337c8cd46a4c6568d367ed676cbf2d7de16f890bb31dbb54c44c1d6bb6d4a1de", size = 356046, upload-time = "2025-11-02T21:39:35.449Z" }, - { url = "https://files.pythonhosted.org/packages/aa/db/1fd0b402bd2b47142e958b6930dbb9445235d03fa703c9a24caa6e576ae2/bitarray-3.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:21ca6a47bf20db9e7ad74ca04b3d479e4d76109b68333eb23535553d2705339e", size = 336872, upload-time = "2025-11-02T21:39:36.891Z" }, - { url = "https://files.pythonhosted.org/packages/58/73/680b47718f1313b4538af479c4732eaca0aeda34d93fc5b869f87932d57d/bitarray-3.8.0-cp312-cp312-win32.whl", hash = "sha256:178c5a4c7fdfb5cd79e372ae7f675390e670f3732e5bc68d327e01a5b3ff8d55", size = 143025, upload-time = "2025-11-02T21:39:38.303Z" }, - { url = "https://files.pythonhosted.org/packages/f8/11/7792587c19c79a8283e8838f44709fa4338a8f7d2a3091dfd81c07ae89c7/bitarray-3.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:75a3b6e9c695a6570ea488db75b84bb592ff70a944957efa1c655867c575018b", size = 149969, upload-time = "2025-11-02T21:39:39.715Z" }, - { url = "https://files.pythonhosted.org/packages/9a/00/9df64b5d8a84e8e9ec392f6f9ce93f50626a5b301cb6c6b3fe3406454d66/bitarray-3.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:5591daf81313096909d973fb2612fccd87528fdfdd39f6478bdce54543178954", size = 146907, upload-time = "2025-11-02T21:39:40.815Z" }, - { url = "https://files.pythonhosted.org/packages/3e/35/480364d4baf1e34c79076750914664373f561c58abb5c31c35b3fae613ff/bitarray-3.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:18214bac86341f1cc413772e66447d6cca10981e2880b70ecaf4e826c04f95e9", size = 148582, upload-time = "2025-11-02T21:39:42.268Z" }, - { url = "https://files.pythonhosted.org/packages/5e/a8/718b95524c803937f4edbaaf6480f39c80f6ed189d61357b345e8361ffb6/bitarray-3.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:01c5f0dc080b0ebb432f7a68ee1e88a76bd34f6d89c9568fcec65fb16ed71f0e", size = 145433, upload-time = "2025-11-02T21:39:43.552Z" }, - { url = "https://files.pythonhosted.org/packages/03/66/4a10f30dc9e2e01e3b4ecd44a511219f98e63c86b0e0f704c90fac24059b/bitarray-3.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:86685fa04067f7175f9718489ae755f6acde03593a1a9ca89305554af40e14fd", size = 332986, upload-time = "2025-11-02T21:39:44.656Z" }, - { url = "https://files.pythonhosted.org/packages/53/25/4c08774d847f80a1166e4c704b4e0f1c417c0afe6306eae0bc5e70d35faa/bitarray-3.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56896ceeffe25946c4010320629e2d858ca763cd8ded273c81672a5edbcb1e0a", size = 360634, upload-time = "2025-11-02T21:39:45.798Z" }, - { url = "https://files.pythonhosted.org/packages/a5/8f/bf8ad26169ebd0b2746d5c7564db734453ca467f8aab87e9d43b0a794383/bitarray-3.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9858dcbc23ba7eaadcd319786b982278a1a2b2020720b19db43e309579ff76fb", size = 371992, upload-time = "2025-11-02T21:39:46.968Z" }, - { url = "https://files.pythonhosted.org/packages/a9/16/ce166754e7c9d10650e02914552fa637cf3b2591f7ed16632bbf6b783312/bitarray-3.8.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa7dec53c25f1949513457ef8b0ea1fb40e76c672cc4d2daa8ad3c8d6b73491a", size = 340315, upload-time = "2025-11-02T21:39:48.182Z" }, - { url = "https://files.pythonhosted.org/packages/de/2a/fbba3a106ddd260e84b9a624f730257c32ba51a8a029565248dfedfdf6f2/bitarray-3.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15a2eff91f54d2b1f573cca8ca6fb58763ce8fea80e7899ab028f3987ef71cd5", size = 330473, upload-time = "2025-11-02T21:39:49.705Z" }, - { url = "https://files.pythonhosted.org/packages/68/97/56cf3c70196e7307ad32318a9d6ed969dbdc6a4534bbe429112fa7dfe42e/bitarray-3.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b1572ee0eb1967e71787af636bb7d1eb9c6735d5337762c450650e7f51844594", size = 358129, upload-time = "2025-11-02T21:39:51.189Z" }, - { url = "https://files.pythonhosted.org/packages/fd/be/afd391a5c0896d3339613321b2f94af853f29afc8bd3fbc327431244c642/bitarray-3.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5bfac7f236ba1a4d402644bdce47fb9db02a7cf3214a1f637d3a88390f9e5428", size = 356005, upload-time = "2025-11-02T21:39:52.355Z" }, - { url = "https://files.pythonhosted.org/packages/ae/08/a8e1a371babba29bad3378bb3a2cdca2b012170711e7fe1f22031a6b7b95/bitarray-3.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f0a55cf02d2cdd739b40ce10c09bbdd520e141217696add7a48b56e67bdfdfe6", size = 336862, upload-time = "2025-11-02T21:39:54.345Z" }, - { url = "https://files.pythonhosted.org/packages/ee/8a/6dc1d0fdc06991c8dc3b1fcfe1ae49fbaced42064cd1b5f24278e73fe05f/bitarray-3.8.0-cp313-cp313-win32.whl", hash = "sha256:a2ba92f59e30ce915e9e79af37649432e3a212ddddf416d4d686b1b4825bcdb2", size = 143018, upload-time = "2025-11-02T21:39:56.361Z" }, - { url = "https://files.pythonhosted.org/packages/2e/72/76e13f5cd23b8b9071747909663ce3b02da24a5e7e22c35146338625db35/bitarray-3.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:1c8f2a5d8006db5a555e06f9437e76bf52537d3dfd130cb8ae2b30866aca32c9", size = 149977, upload-time = "2025-11-02T21:39:57.718Z" }, - { url = "https://files.pythonhosted.org/packages/01/37/60f336c32336cc3ec03b0c61076f16ea2f05d5371c8a56e802161d218b77/bitarray-3.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:50ddbe3a7b4b6ab96812f5a4d570f401a2cdb95642fd04c062f98939610bbeee", size = 146930, upload-time = "2025-11-02T21:39:59.308Z" }, - { url = "https://files.pythonhosted.org/packages/1b/b0/411327a6c7f6b2bead64bb06fe60b92e0344957ec1ab0645d5ccc25fdafe/bitarray-3.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8cbd4bfc933b33b85c43ef4c1f4d5e3e9d91975ea6368acf5fbac02bac06ea89", size = 148563, upload-time = "2025-11-02T21:40:01.006Z" }, - { url = "https://files.pythonhosted.org/packages/2a/bc/ff80d97c627d774f879da0ea93223adb1267feab7e07d5c17580ffe6d632/bitarray-3.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9d35d8f8a1c9ed4e2b08187b513f8a3c71958600129db3aa26d85ea3abfd1310", size = 145422, upload-time = "2025-11-02T21:40:02.535Z" }, - { url = "https://files.pythonhosted.org/packages/66/e7/b4cb6c5689aacd0a32f3aa8a507155eaa33528c63de2f182b60843fbf700/bitarray-3.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99f55e14e7c56f4fafe1343480c32b110ef03836c21ff7c48bae7add6818f77c", size = 332852, upload-time = "2025-11-02T21:40:03.645Z" }, - { url = "https://files.pythonhosted.org/packages/e7/91/fbd1b047e3e2f4b65590f289c8151df1d203d75b005f5aae4e072fe77d76/bitarray-3.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dfbe2aa45b273f49e715c5345d94874cb65a28482bf231af408891c260601b8d", size = 360801, upload-time = "2025-11-02T21:40:04.827Z" }, - { url = "https://files.pythonhosted.org/packages/ef/4a/63064c593627bac8754fdafcb5343999c93ab2aeb27bcd9d270a010abea5/bitarray-3.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:64af877116edf051375b45f0bda648143176a017b13803ec7b3a3111dc05f4c5", size = 371408, upload-time = "2025-11-02T21:40:05.985Z" }, - { url = "https://files.pythonhosted.org/packages/46/97/ddc07723767bdafd170f2ff6e173c940fa874192783ee464aa3c1dedf07d/bitarray-3.8.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cdfbb27f2c46bb5bbdcee147530cbc5ca8ab858d7693924e88e30ada21b2c5e2", size = 340033, upload-time = "2025-11-02T21:40:07.189Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1e/e1ea9f1146fd4af032817069ff118918d73e5de519854ce3860e2ed560ff/bitarray-3.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4d73d4948dcc5591d880db8933004e01f1dd2296df9de815354d53469beb26fe", size = 330774, upload-time = "2025-11-02T21:40:08.496Z" }, - { url = "https://files.pythonhosted.org/packages/cf/9f/8242296c124a48d1eab471fd0838aeb7ea9c6fd720302d99ab7855d3e6d3/bitarray-3.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:28a85b056c0eb7f5d864c0ceef07034117e8ebfca756f50648c71950a568ba11", size = 358337, upload-time = "2025-11-02T21:40:10.035Z" }, - { url = "https://files.pythonhosted.org/packages/b5/6b/9095d75264c67d479f298c80802422464ce18c3cdd893252eeccf4997611/bitarray-3.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:79ec4498a545733ecace48d780d22407411b07403a2e08b9a4d7596c0b97ebd7", size = 355639, upload-time = "2025-11-02T21:40:11.485Z" }, - { url = "https://files.pythonhosted.org/packages/a0/af/c93c0ae5ef824136e90ac7ddf6cceccb1232f34240b2f55a922f874da9b4/bitarray-3.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:33af25c4ff7723363cb8404dfc2eefeab4110b654f6c98d26aba8a08c745d860", size = 336999, upload-time = "2025-11-02T21:40:12.709Z" }, - { url = "https://files.pythonhosted.org/packages/81/0f/72c951f5997b2876355d5e671f78dd2362493254876675cf22dbd24389ae/bitarray-3.8.0-cp314-cp314-win32.whl", hash = "sha256:2c3bb96b6026643ce24677650889b09073f60b9860a71765f843c99f9ab38b25", size = 142169, upload-time = "2025-11-02T21:40:14.031Z" }, - { url = "https://files.pythonhosted.org/packages/8a/55/ef1b4de8107bf13823da8756c20e1fbc9452228b4e837f46f6d9ddba3eb3/bitarray-3.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:847c7f61964225fc489fe1d49eda7e0e0d253e98862c012cecf845f9ad45cdf4", size = 148737, upload-time = "2025-11-02T21:40:15.436Z" }, - { url = "https://files.pythonhosted.org/packages/5f/26/bc0784136775024ac56cc67c0d6f9aa77a7770de7f82c3a7c9be11c217cd/bitarray-3.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:a2cb35a6efaa0e3623d8272471371a12c7e07b51a33e5efce9b58f655d864b4e", size = 146083, upload-time = "2025-11-02T21:40:17.135Z" }, - { url = "https://files.pythonhosted.org/packages/6e/64/57984e64264bf43d93a1809e645972771566a2d0345f4896b041ce20b000/bitarray-3.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:15e8d0597cc6e8496de6f4dea2a6880c57e1251502a7072f5631108a1aa28521", size = 149455, upload-time = "2025-11-02T21:40:18.558Z" }, - { url = "https://files.pythonhosted.org/packages/81/c0/0d5f2eaef1867f462f764bdb07d1e116c33a1bf052ea21889aefe4282f5b/bitarray-3.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8ffe660e963ae711cb9e2b8d8461c9b1ad6167823837fc17d59d5e539fb898fa", size = 146491, upload-time = "2025-11-02T21:40:19.665Z" }, - { url = "https://files.pythonhosted.org/packages/65/c6/bc1261f7a8862c0c59220a484464739e52235fd1e2afcb24d7f7d3fb5702/bitarray-3.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4779f356083c62e29b4198d290b7b17a39a69702d150678b7efff0fdddf494a8", size = 339721, upload-time = "2025-11-02T21:40:21.277Z" }, - { url = "https://files.pythonhosted.org/packages/81/d8/289ca55dd2939ea17b1108dc53bffc0fdc5160ba44f77502dfaae35d08c6/bitarray-3.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:025d133bf4ca8cf75f904eeb8ea946228d7c043231866143f31946a6f4dd0bf3", size = 367823, upload-time = "2025-11-02T21:40:22.463Z" }, - { url = "https://files.pythonhosted.org/packages/91/a2/61e7461ca9ac0fcb70f327a2e84b006996d2a840898e69037a39c87c6d06/bitarray-3.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:451f9958850ea98440d542278368c8d1e1ea821e2494b204570ba34a340759df", size = 377341, upload-time = "2025-11-02T21:40:23.789Z" }, - { url = "https://files.pythonhosted.org/packages/6c/87/4a0c9c8bdb13916d443e04d8f8542eef9190f31425da3c17c3478c40173f/bitarray-3.8.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6d79f659965290af60d6acc8e2716341865fe74609a7ede2a33c2f86ad893b8f", size = 344985, upload-time = "2025-11-02T21:40:25.261Z" }, - { url = "https://files.pythonhosted.org/packages/17/4c/ff9259b916efe53695b631772e5213699c738efc2471b5ffe273f4000994/bitarray-3.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fbf05678c2ae0064fb1b8de7e9e8f0fc30621b73c8477786dd0fb3868044a8c8", size = 336796, upload-time = "2025-11-02T21:40:26.942Z" }, - { url = "https://files.pythonhosted.org/packages/0f/4b/51b2468bbddbade5e2f3b8d5db08282c5b309e8687b0f02f75a8b5ff559c/bitarray-3.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:c396358023b876cff547ce87f4e8ff8a2280598873a137e8cc69e115262260b8", size = 365085, upload-time = "2025-11-02T21:40:28.224Z" }, - { url = "https://files.pythonhosted.org/packages/bf/79/53473bfc2e052c6dbb628cdc1b156be621c77aaeb715918358b01574be55/bitarray-3.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ed3493a369fe849cce98542d7405c88030b355e4d2e113887cb7ecc86c205773", size = 361012, upload-time = "2025-11-02T21:40:29.635Z" }, - { url = "https://files.pythonhosted.org/packages/c4/b1/242bf2e44bfc69e73fa2b954b425d761a8e632f78ea31008f1c3cfad0854/bitarray-3.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c764fb167411d5afaef88138542a4bfa28bd5e5ded5e8e42df87cef965efd6e9", size = 340644, upload-time = "2025-11-02T21:40:31.089Z" }, - { url = "https://files.pythonhosted.org/packages/cf/01/12e5ecf30a5de28a32485f226cad4b8a546845f65f755ce0365057ab1e92/bitarray-3.8.0-cp314-cp314t-win32.whl", hash = "sha256:e12769d3adcc419e65860de946df8d2ed274932177ac1cdb05186e498aaa9149", size = 143630, upload-time = "2025-11-02T21:40:32.351Z" }, - { url = "https://files.pythonhosted.org/packages/b6/92/6b6ade587b08024a8a890b07724775d29da9cf7497be5c3cbe226185e463/bitarray-3.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0ca70ccf789446a6dfde40b482ec21d28067172cd1f8efd50d5548159fccad9e", size = 150250, upload-time = "2025-11-02T21:40:33.596Z" }, - { url = "https://files.pythonhosted.org/packages/ed/40/be3858ffed004e47e48a2cefecdbf9b950d41098b780f9dc3aa609a88351/bitarray-3.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2a3d1b05ffdd3e95687942ae7b13c63689f85d3f15c39b33329e3cb9ce6c015f", size = 147015, upload-time = "2025-11-02T21:40:35.064Z" }, -] - -[[package]] -name = "build" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "os_name == 'nt'" }, - { name = "packaging" }, - { name = "pyproject-hooks" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/18/94eaffda7b329535d91f00fe605ab1f1e5cd68b2074d03f255c7d250687d/build-1.4.0.tar.gz", hash = "sha256:f1b91b925aa322be454f8330c6fb48b465da993d1e7e7e6fa35027ec49f3c936", size = 50054, upload-time = "2026-01-08T16:41:47.696Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl", hash = "sha256:6a07c1b8eb6f2b311b96fcbdbce5dab5fe637ffda0fd83c9cac622e927501596", size = 24141, upload-time = "2026-01-08T16:41:46.453Z" }, -] - -[[package]] -name = "certifi" -version = "2026.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, - { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, - { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, - { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, - { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, - { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, - { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, - { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, - { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, -] - -[[package]] -name = "chromadb" -version = "1.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "bcrypt" }, - { name = "build" }, - { name = "grpcio" }, - { name = "httpx" }, - { name = "importlib-resources" }, - { name = "jsonschema" }, - { name = "kubernetes" }, - { name = "mmh3" }, - { name = "numpy" }, - { name = "onnxruntime" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-otlp-proto-grpc" }, - { name = "opentelemetry-sdk" }, - { name = "orjson" }, - { name = "overrides" }, - { name = "posthog" }, - { name = "pybase64" }, - { name = "pydantic" }, - { name = "pypika" }, - { name = "pyyaml" }, - { name = "rich" }, - { name = "tenacity" }, - { name = "tokenizers" }, - { name = "tqdm" }, - { name = "typer" }, - { name = "typing-extensions" }, - { name = "uvicorn", extra = ["standard"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/03/35/24479ac00e74b86e388854a573a9ebe6d41c51c37e03d00864bb967d861f/chromadb-1.4.1.tar.gz", hash = "sha256:3cceb83e0a7a3c2db0752ebf62e9cfe652da657594c093fe07e74022581a58eb", size = 2226347, upload-time = "2026-01-14T19:18:15.189Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/f0/7c815bb80a2aaa349757ed0c743fa7e85bbe16f612057b25cf1809456a32/chromadb-1.4.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:05d98ffe4a9a5549c9a78eee7624277f9d99c53200a01f1176ecb1d31ea3c819", size = 20313209, upload-time = "2026-01-14T19:18:12.111Z" }, - { url = "https://files.pythonhosted.org/packages/a1/4b/c16236d56bf6bf144edbe5a03c431b59ba089bd6f86baefa8ebc288bf8b8/chromadb-1.4.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:38336431c01562cffdb3ef693f22f7a88df5304f942e01ed66ee0bbaf08f35da", size = 19634405, upload-time = "2026-01-14T19:18:08.264Z" }, - { url = "https://files.pythonhosted.org/packages/70/9c/33c6c3036e30632c2b64d333e92af3972e6bef423a8285e0edc5f487d322/chromadb-1.4.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaaf9c7d4ddbbdc74bd7cac45d9729032020cc6e65a2b8f313257e6c949beed", size = 20276410, upload-time = "2026-01-14T19:18:00.226Z" }, - { url = "https://files.pythonhosted.org/packages/29/bc/0c6a6255cd55fe384c1bda6bebb47b5ff9d5c535d993fd3451e4a3fbe42f/chromadb-1.4.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad50fbb5799dcaef5ae7613be583a06b44b637283db066396490863266f48623", size = 21082323, upload-time = "2026-01-14T19:18:04.604Z" }, - { url = "https://files.pythonhosted.org/packages/79/be/5092571f87ddf08022a3d9434d3374d3f5aa20ebad1c75d63107c0c046d6/chromadb-1.4.1-cp39-abi3-win_amd64.whl", hash = "sha256:cedc9941dad1081eb9be89a7f5f66374715d4f99f731f1eb9da900636c501330", size = 21376957, upload-time = "2026-01-14T19:18:16.95Z" }, -] - -[[package]] -name = "ckzg" -version = "1.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/bf/ddd817e8b455b577b206fbfee951df1f4964826e9d4f2fc3148550d592c4/ckzg-1.0.2.tar.gz", hash = "sha256:4295acc380f8d42ebea4a4a0a68c424a322bb335a33bad05c72ead8cbb28d118", size = 840347, upload-time = "2024-05-07T20:50:58.148Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/06/959cfafae47190d4f1930d8993653538c3de7bb1a3a32e917aa47ac9c8f0/ckzg-1.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e3cb2f8c767aee57e88944f90848e8689ce43993b9ff21589cfb97a562208fe7", size = 100499, upload-time = "2024-05-07T20:49:33.963Z" }, - { url = "https://files.pythonhosted.org/packages/a6/27/f9b73f240bc2c4a7995a43f9b7850cd8e6931f396206f7e38d6df3e8d8d7/ckzg-1.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b29889f5bc5db530f766871c0ff4133e7270ecf63aaa3ca756d3b2731980802", size = 85778, upload-time = "2024-05-07T20:49:35.351Z" }, - { url = "https://files.pythonhosted.org/packages/c6/79/cf1bb8d02703222b1177596a9de3e25c829db5a852a5824b5ea898396ed4/ckzg-1.0.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfcc70fb76b3d36125d646110d5001f2aa89c1c09ff5537a4550cdb7951f44d4", size = 146327, upload-time = "2024-05-07T20:49:36.249Z" }, - { url = "https://files.pythonhosted.org/packages/ff/eb/a43b49ac53c581f7b8be88596c98db558c3059f8c8bcc339f4dac560dac2/ckzg-1.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ca8a256cdd56d06bc5ef24caac64845240dbabca402c5a1966d519b2514b4ec", size = 132109, upload-time = "2024-05-07T20:49:38.075Z" }, - { url = "https://files.pythonhosted.org/packages/d5/6f/7051894626806a98c1c9d9608fa1ffaafc811f460e2490bcd90cc60b07a3/ckzg-1.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ea91b0236384f93ad1df01d530672f09e254bd8c3cf097ebf486aebb97f6c8c", size = 140481, upload-time = "2024-05-07T20:49:39.058Z" }, - { url = "https://files.pythonhosted.org/packages/19/ec/bcf995869a47ef2ca645d16b2bf0052af4581079d7c622d30ba721d088c7/ckzg-1.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:65311e72780105f239d1d66512629a9f468b7c9f2609b8567fc68963ac638ef9", size = 139204, upload-time = "2024-05-07T20:49:40.431Z" }, - { url = "https://files.pythonhosted.org/packages/61/2a/0a86fb062a6415b5ad73665051f3e5c891fe4250edd17037c9837b999c79/ckzg-1.0.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0d7600ce7a73ac41d348712d0c1fe5e4cb6caa329377064cfa3a6fd8fbffb410", size = 149095, upload-time = "2024-05-07T20:49:41.459Z" }, - { url = "https://files.pythonhosted.org/packages/ef/88/bcb1f42b6fb6f3392025d12c434acdee7b1667b4b455851aaef72bb99dce/ckzg-1.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:19893ee7bd7da8688382cb134cb9ee7bce5c38e3a9386e3ed99bb010487d2d17", size = 144915, upload-time = "2024-05-07T20:49:42.503Z" }, - { url = "https://files.pythonhosted.org/packages/61/44/0a53aec8ba1a8c0987b73f64262c4e500fbed97c2533630c77b63d32afb3/ckzg-1.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:c3e1a9a72695e777497e95bb2213316a1138f82d1bb5d67b9c029a522d24908e", size = 69856, upload-time = "2024-05-07T20:49:44.179Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - -[[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 = "coloredlogs" -version = "15.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "humanfriendly" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" }, -] - -[[package]] -name = "coverage" -version = "7.13.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ad/49/349848445b0e53660e258acbcc9b0d014895b6739237920886672240f84b/coverage-7.13.2.tar.gz", hash = "sha256:044c6951ec37146b72a50cc81ef02217d27d4c3640efd2640311393cbbf143d3", size = 826523, upload-time = "2026-01-25T13:00:04.889Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/39/e92a35f7800222d3f7b2cbb7bbc3b65672ae8d501cb31801b2d2bd7acdf1/coverage-7.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f106b2af193f965d0d3234f3f83fc35278c7fb935dfbde56ae2da3dd2c03b84d", size = 219142, upload-time = "2026-01-25T12:58:00.448Z" }, - { url = "https://files.pythonhosted.org/packages/45/7a/8bf9e9309c4c996e65c52a7c5a112707ecdd9fbaf49e10b5a705a402bbb4/coverage-7.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f45d21dc4d5d6bd29323f0320089ef7eae16e4bef712dff79d184fa7330af3", size = 219503, upload-time = "2026-01-25T12:58:02.451Z" }, - { url = "https://files.pythonhosted.org/packages/87/93/17661e06b7b37580923f3f12406ac91d78aeed293fb6da0b69cc7957582f/coverage-7.13.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fae91dfecd816444c74531a9c3d6ded17a504767e97aa674d44f638107265b99", size = 251006, upload-time = "2026-01-25T12:58:04.059Z" }, - { url = "https://files.pythonhosted.org/packages/12/f0/f9e59fb8c310171497f379e25db060abef9fa605e09d63157eebec102676/coverage-7.13.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:264657171406c114787b441484de620e03d8f7202f113d62fcd3d9688baa3e6f", size = 253750, upload-time = "2026-01-25T12:58:05.574Z" }, - { url = "https://files.pythonhosted.org/packages/e5/b1/1935e31add2232663cf7edd8269548b122a7d100047ff93475dbaaae673e/coverage-7.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae47d8dcd3ded0155afbb59c62bd8ab07ea0fd4902e1c40567439e6db9dcaf2f", size = 254862, upload-time = "2026-01-25T12:58:07.647Z" }, - { url = "https://files.pythonhosted.org/packages/af/59/b5e97071ec13df5f45da2b3391b6cdbec78ba20757bc92580a5b3d5fa53c/coverage-7.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a0b33e9fd838220b007ce8f299114d406c1e8edb21336af4c97a26ecfd185aa", size = 251420, upload-time = "2026-01-25T12:58:09.309Z" }, - { url = "https://files.pythonhosted.org/packages/3f/75/9495932f87469d013dc515fb0ce1aac5fa97766f38f6b1a1deb1ee7b7f3a/coverage-7.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b3becbea7f3ce9a2d4d430f223ec15888e4deb31395840a79e916368d6004cce", size = 252786, upload-time = "2026-01-25T12:58:10.909Z" }, - { url = "https://files.pythonhosted.org/packages/6a/59/af550721f0eb62f46f7b8cb7e6f1860592189267b1c411a4e3a057caacee/coverage-7.13.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f819c727a6e6eeb8711e4ce63d78c620f69630a2e9d53bc95ca5379f57b6ba94", size = 250928, upload-time = "2026-01-25T12:58:12.449Z" }, - { url = "https://files.pythonhosted.org/packages/9b/b1/21b4445709aae500be4ab43bbcfb4e53dc0811c3396dcb11bf9f23fd0226/coverage-7.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:4f7b71757a3ab19f7ba286e04c181004c1d61be921795ee8ba6970fd0ec91da5", size = 250496, upload-time = "2026-01-25T12:58:14.047Z" }, - { url = "https://files.pythonhosted.org/packages/ba/b1/0f5d89dfe0392990e4f3980adbde3eb34885bc1effb2dc369e0bf385e389/coverage-7.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b7fc50d2afd2e6b4f6f2f403b70103d280a8e0cb35320cbbe6debcda02a1030b", size = 252373, upload-time = "2026-01-25T12:58:15.976Z" }, - { url = "https://files.pythonhosted.org/packages/01/c9/0cf1a6a57a9968cc049a6b896693faa523c638a5314b1fc374eb2b2ac904/coverage-7.13.2-cp312-cp312-win32.whl", hash = "sha256:292250282cf9bcf206b543d7608bda17ca6fc151f4cbae949fc7e115112fbd41", size = 221696, upload-time = "2026-01-25T12:58:17.517Z" }, - { url = "https://files.pythonhosted.org/packages/4d/05/d7540bf983f09d32803911afed135524570f8c47bb394bf6206c1dc3a786/coverage-7.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:eeea10169fac01549a7921d27a3e517194ae254b542102267bef7a93ed38c40e", size = 222504, upload-time = "2026-01-25T12:58:19.115Z" }, - { url = "https://files.pythonhosted.org/packages/15/8b/1a9f037a736ced0a12aacf6330cdaad5008081142a7070bc58b0f7930cbc/coverage-7.13.2-cp312-cp312-win_arm64.whl", hash = "sha256:2a5b567f0b635b592c917f96b9a9cb3dbd4c320d03f4bf94e9084e494f2e8894", size = 221120, upload-time = "2026-01-25T12:58:21.334Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f0/3d3eac7568ab6096ff23791a526b0048a1ff3f49d0e236b2af6fb6558e88/coverage-7.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ed75de7d1217cf3b99365d110975f83af0528c849ef5180a12fd91b5064df9d6", size = 219168, upload-time = "2026-01-25T12:58:23.376Z" }, - { url = "https://files.pythonhosted.org/packages/a3/a6/f8b5cfeddbab95fdef4dcd682d82e5dcff7a112ced57a959f89537ee9995/coverage-7.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97e596de8fa9bada4d88fde64a3f4d37f1b6131e4faa32bad7808abc79887ddc", size = 219537, upload-time = "2026-01-25T12:58:24.932Z" }, - { url = "https://files.pythonhosted.org/packages/7b/e6/8d8e6e0c516c838229d1e41cadcec91745f4b1031d4db17ce0043a0423b4/coverage-7.13.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:68c86173562ed4413345410c9480a8d64864ac5e54a5cda236748031e094229f", size = 250528, upload-time = "2026-01-25T12:58:26.567Z" }, - { url = "https://files.pythonhosted.org/packages/8e/78/befa6640f74092b86961f957f26504c8fba3d7da57cc2ab7407391870495/coverage-7.13.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7be4d613638d678b2b3773b8f687537b284d7074695a43fe2fbbfc0e31ceaed1", size = 253132, upload-time = "2026-01-25T12:58:28.251Z" }, - { url = "https://files.pythonhosted.org/packages/9d/10/1630db1edd8ce675124a2ee0f7becc603d2bb7b345c2387b4b95c6907094/coverage-7.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7f63ce526a96acd0e16c4af8b50b64334239550402fb1607ce6a584a6d62ce9", size = 254374, upload-time = "2026-01-25T12:58:30.294Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1d/0d9381647b1e8e6d310ac4140be9c428a0277330991e0c35bdd751e338a4/coverage-7.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:406821f37f864f968e29ac14c3fccae0fec9fdeba48327f0341decf4daf92d7c", size = 250762, upload-time = "2026-01-25T12:58:32.036Z" }, - { url = "https://files.pythonhosted.org/packages/43/e4/5636dfc9a7c871ee8776af83ee33b4c26bc508ad6cee1e89b6419a366582/coverage-7.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ee68e5a4e3e5443623406b905db447dceddffee0dceb39f4e0cd9ec2a35004b5", size = 252502, upload-time = "2026-01-25T12:58:33.961Z" }, - { url = "https://files.pythonhosted.org/packages/02/2a/7ff2884d79d420cbb2d12fed6fff727b6d0ef27253140d3cdbbd03187ee0/coverage-7.13.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2ee0e58cca0c17dd9c6c1cdde02bb705c7b3fbfa5f3b0b5afeda20d4ebff8ef4", size = 250463, upload-time = "2026-01-25T12:58:35.529Z" }, - { url = "https://files.pythonhosted.org/packages/91/c0/ba51087db645b6c7261570400fc62c89a16278763f36ba618dc8657a187b/coverage-7.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e5bbb5018bf76a56aabdb64246b5288d5ae1b7d0dd4d0534fe86df2c2992d1c", size = 250288, upload-time = "2026-01-25T12:58:37.226Z" }, - { url = "https://files.pythonhosted.org/packages/03/07/44e6f428551c4d9faf63ebcefe49b30e5c89d1be96f6a3abd86a52da9d15/coverage-7.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a55516c68ef3e08e134e818d5e308ffa6b1337cc8b092b69b24287bf07d38e31", size = 252063, upload-time = "2026-01-25T12:58:38.821Z" }, - { url = "https://files.pythonhosted.org/packages/c2/67/35b730ad7e1859dd57e834d1bc06080d22d2f87457d53f692fce3f24a5a9/coverage-7.13.2-cp313-cp313-win32.whl", hash = "sha256:5b20211c47a8abf4abc3319d8ce2464864fa9f30c5fcaf958a3eed92f4f1fef8", size = 221716, upload-time = "2026-01-25T12:58:40.484Z" }, - { url = "https://files.pythonhosted.org/packages/0d/82/e5fcf5a97c72f45fc14829237a6550bf49d0ab882ac90e04b12a69db76b4/coverage-7.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:14f500232e521201cf031549fb1ebdfc0a40f401cf519157f76c397e586c3beb", size = 222522, upload-time = "2026-01-25T12:58:43.247Z" }, - { url = "https://files.pythonhosted.org/packages/b1/f1/25d7b2f946d239dd2d6644ca2cc060d24f97551e2af13b6c24c722ae5f97/coverage-7.13.2-cp313-cp313-win_arm64.whl", hash = "sha256:9779310cb5a9778a60c899f075a8514c89fa6d10131445c2207fc893e0b14557", size = 221145, upload-time = "2026-01-25T12:58:45Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f7/080376c029c8f76fadfe43911d0daffa0cbdc9f9418a0eead70c56fb7f4b/coverage-7.13.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e64fa5a1e41ce5df6b547cbc3d3699381c9e2c2c369c67837e716ed0f549d48e", size = 219861, upload-time = "2026-01-25T12:58:46.586Z" }, - { url = "https://files.pythonhosted.org/packages/42/11/0b5e315af5ab35f4c4a70e64d3314e4eec25eefc6dec13be3a7d5ffe8ac5/coverage-7.13.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b01899e82a04085b6561eb233fd688474f57455e8ad35cd82286463ba06332b7", size = 220207, upload-time = "2026-01-25T12:58:48.277Z" }, - { url = "https://files.pythonhosted.org/packages/b2/0c/0874d0318fb1062117acbef06a09cf8b63f3060c22265adaad24b36306b7/coverage-7.13.2-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:838943bea48be0e2768b0cf7819544cdedc1bbb2f28427eabb6eb8c9eb2285d3", size = 261504, upload-time = "2026-01-25T12:58:49.904Z" }, - { url = "https://files.pythonhosted.org/packages/83/5e/1cd72c22ecb30751e43a72f40ba50fcef1b7e93e3ea823bd9feda8e51f9a/coverage-7.13.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:93d1d25ec2b27e90bcfef7012992d1f5121b51161b8bffcda756a816cf13c2c3", size = 263582, upload-time = "2026-01-25T12:58:51.582Z" }, - { url = "https://files.pythonhosted.org/packages/9b/da/8acf356707c7a42df4d0657020308e23e5a07397e81492640c186268497c/coverage-7.13.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93b57142f9621b0d12349c43fc7741fe578e4bc914c1e5a54142856cfc0bf421", size = 266008, upload-time = "2026-01-25T12:58:53.234Z" }, - { url = "https://files.pythonhosted.org/packages/41/41/ea1730af99960309423c6ea8d6a4f1fa5564b2d97bd1d29dda4b42611f04/coverage-7.13.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f06799ae1bdfff7ccb8665d75f8291c69110ba9585253de254688aa8a1ccc6c5", size = 260762, upload-time = "2026-01-25T12:58:55.372Z" }, - { url = "https://files.pythonhosted.org/packages/22/fa/02884d2080ba71db64fdc127b311db60e01fe6ba797d9c8363725e39f4d5/coverage-7.13.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f9405ab4f81d490811b1d91c7a20361135a2df4c170e7f0b747a794da5b7f23", size = 263571, upload-time = "2026-01-25T12:58:57.52Z" }, - { url = "https://files.pythonhosted.org/packages/d2/6b/4083aaaeba9b3112f55ac57c2ce7001dc4d8fa3fcc228a39f09cc84ede27/coverage-7.13.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f9ab1d5b86f8fbc97a5b3cd6280a3fd85fef3b028689d8a2c00918f0d82c728c", size = 261200, upload-time = "2026-01-25T12:58:59.255Z" }, - { url = "https://files.pythonhosted.org/packages/e9/d2/aea92fa36d61955e8c416ede9cf9bf142aa196f3aea214bb67f85235a050/coverage-7.13.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:f674f59712d67e841525b99e5e2b595250e39b529c3bda14764e4f625a3fa01f", size = 260095, upload-time = "2026-01-25T12:59:01.066Z" }, - { url = "https://files.pythonhosted.org/packages/0d/ae/04ffe96a80f107ea21b22b2367175c621da920063260a1c22f9452fd7866/coverage-7.13.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c6cadac7b8ace1ba9144feb1ae3cb787a6065ba6d23ffc59a934b16406c26573", size = 262284, upload-time = "2026-01-25T12:59:02.802Z" }, - { url = "https://files.pythonhosted.org/packages/1c/7a/6f354dcd7dfc41297791d6fb4e0d618acb55810bde2c1fd14b3939e05c2b/coverage-7.13.2-cp313-cp313t-win32.whl", hash = "sha256:14ae4146465f8e6e6253eba0cccd57423e598a4cb925958b240c805300918343", size = 222389, upload-time = "2026-01-25T12:59:04.563Z" }, - { url = "https://files.pythonhosted.org/packages/8d/d5/080ad292a4a3d3daf411574be0a1f56d6dee2c4fdf6b005342be9fac807f/coverage-7.13.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9074896edd705a05769e3de0eac0a8388484b503b68863dd06d5e473f874fd47", size = 223450, upload-time = "2026-01-25T12:59:06.677Z" }, - { url = "https://files.pythonhosted.org/packages/88/96/df576fbacc522e9fb8d1c4b7a7fc62eb734be56e2cba1d88d2eabe08ea3f/coverage-7.13.2-cp313-cp313t-win_arm64.whl", hash = "sha256:69e526e14f3f854eda573d3cf40cffd29a1a91c684743d904c33dbdcd0e0f3e7", size = 221707, upload-time = "2026-01-25T12:59:08.363Z" }, - { url = "https://files.pythonhosted.org/packages/55/53/1da9e51a0775634b04fcc11eb25c002fc58ee4f92ce2e8512f94ac5fc5bf/coverage-7.13.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:387a825f43d680e7310e6f325b2167dd093bc8ffd933b83e9aa0983cf6e0a2ef", size = 219213, upload-time = "2026-01-25T12:59:11.909Z" }, - { url = "https://files.pythonhosted.org/packages/46/35/b3caac3ebbd10230fea5a33012b27d19e999a17c9285c4228b4b2e35b7da/coverage-7.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f0d7fea9d8e5d778cd5a9e8fc38308ad688f02040e883cdc13311ef2748cb40f", size = 219549, upload-time = "2026-01-25T12:59:13.638Z" }, - { url = "https://files.pythonhosted.org/packages/76/9c/e1cf7def1bdc72c1907e60703983a588f9558434a2ff94615747bd73c192/coverage-7.13.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e080afb413be106c95c4ee96b4fffdc9e2fa56a8bbf90b5c0918e5c4449412f5", size = 250586, upload-time = "2026-01-25T12:59:15.808Z" }, - { url = "https://files.pythonhosted.org/packages/ba/49/f54ec02ed12be66c8d8897270505759e057b0c68564a65c429ccdd1f139e/coverage-7.13.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7fc042ba3c7ce25b8a9f097eb0f32a5ce1ccdb639d9eec114e26def98e1f8a4", size = 253093, upload-time = "2026-01-25T12:59:17.491Z" }, - { url = "https://files.pythonhosted.org/packages/fb/5e/aaf86be3e181d907e23c0f61fccaeb38de8e6f6b47aed92bf57d8fc9c034/coverage-7.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0ba505e021557f7f8173ee8cd6b926373d8653e5ff7581ae2efce1b11ef4c27", size = 254446, upload-time = "2026-01-25T12:59:19.752Z" }, - { url = "https://files.pythonhosted.org/packages/28/c8/a5fa01460e2d75b0c853b392080d6829d3ca8b5ab31e158fa0501bc7c708/coverage-7.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7de326f80e3451bd5cc7239ab46c73ddb658fe0b7649476bc7413572d36cd548", size = 250615, upload-time = "2026-01-25T12:59:21.928Z" }, - { url = "https://files.pythonhosted.org/packages/86/0b/6d56315a55f7062bb66410732c24879ccb2ec527ab6630246de5fe45a1df/coverage-7.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:abaea04f1e7e34841d4a7b343904a3f59481f62f9df39e2cd399d69a187a9660", size = 252452, upload-time = "2026-01-25T12:59:23.592Z" }, - { url = "https://files.pythonhosted.org/packages/30/19/9bc550363ebc6b0ea121977ee44d05ecd1e8bf79018b8444f1028701c563/coverage-7.13.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9f93959ee0c604bccd8e0697be21de0887b1f73efcc3aa73a3ec0fd13feace92", size = 250418, upload-time = "2026-01-25T12:59:25.392Z" }, - { url = "https://files.pythonhosted.org/packages/1f/53/580530a31ca2f0cc6f07a8f2ab5460785b02bb11bdf815d4c4d37a4c5169/coverage-7.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:13fe81ead04e34e105bf1b3c9f9cdf32ce31736ee5d90a8d2de02b9d3e1bcb82", size = 250231, upload-time = "2026-01-25T12:59:27.888Z" }, - { url = "https://files.pythonhosted.org/packages/e2/42/dd9093f919dc3088cb472893651884bd675e3df3d38a43f9053656dca9a2/coverage-7.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d6d16b0f71120e365741bca2cb473ca6fe38930bc5431c5e850ba949f708f892", size = 251888, upload-time = "2026-01-25T12:59:29.636Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a6/0af4053e6e819774626e133c3d6f70fae4d44884bfc4b126cb647baee8d3/coverage-7.13.2-cp314-cp314-win32.whl", hash = "sha256:9b2f4714bb7d99ba3790ee095b3b4ac94767e1347fe424278a0b10acb3ff04fe", size = 221968, upload-time = "2026-01-25T12:59:31.424Z" }, - { url = "https://files.pythonhosted.org/packages/c4/cc/5aff1e1f80d55862442855517bb8ad8ad3a68639441ff6287dde6a58558b/coverage-7.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:e4121a90823a063d717a96e0a0529c727fb31ea889369a0ee3ec00ed99bf6859", size = 222783, upload-time = "2026-01-25T12:59:33.118Z" }, - { url = "https://files.pythonhosted.org/packages/de/20/09abafb24f84b3292cc658728803416c15b79f9ee5e68d25238a895b07d9/coverage-7.13.2-cp314-cp314-win_arm64.whl", hash = "sha256:6873f0271b4a15a33e7590f338d823f6f66f91ed147a03938d7ce26efd04eee6", size = 221348, upload-time = "2026-01-25T12:59:34.939Z" }, - { url = "https://files.pythonhosted.org/packages/b6/60/a3820c7232db63be060e4019017cd3426751c2699dab3c62819cdbcea387/coverage-7.13.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f61d349f5b7cd95c34017f1927ee379bfbe9884300d74e07cf630ccf7a610c1b", size = 219950, upload-time = "2026-01-25T12:59:36.624Z" }, - { url = "https://files.pythonhosted.org/packages/fd/37/e4ef5975fdeb86b1e56db9a82f41b032e3d93a840ebaf4064f39e770d5c5/coverage-7.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a43d34ce714f4ca674c0d90beb760eb05aad906f2c47580ccee9da8fe8bfb417", size = 220209, upload-time = "2026-01-25T12:59:38.339Z" }, - { url = "https://files.pythonhosted.org/packages/54/df/d40e091d00c51adca1e251d3b60a8b464112efa3004949e96a74d7c19a64/coverage-7.13.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bff1b04cb9d4900ce5c56c4942f047dc7efe57e2608cb7c3c8936e9970ccdbee", size = 261576, upload-time = "2026-01-25T12:59:40.446Z" }, - { url = "https://files.pythonhosted.org/packages/c5/44/5259c4bed54e3392e5c176121af9f71919d96dde853386e7730e705f3520/coverage-7.13.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6ae99e4560963ad8e163e819e5d77d413d331fd00566c1e0856aa252303552c1", size = 263704, upload-time = "2026-01-25T12:59:42.346Z" }, - { url = "https://files.pythonhosted.org/packages/16/bd/ae9f005827abcbe2c70157459ae86053971c9fa14617b63903abbdce26d9/coverage-7.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e79a8c7d461820257d9aa43716c4efc55366d7b292e46b5b37165be1d377405d", size = 266109, upload-time = "2026-01-25T12:59:44.073Z" }, - { url = "https://files.pythonhosted.org/packages/a2/c0/8e279c1c0f5b1eaa3ad9b0fb7a5637fc0379ea7d85a781c0fe0bb3cfc2ab/coverage-7.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:060ee84f6a769d40c492711911a76811b4befb6fba50abb450371abb720f5bd6", size = 260686, upload-time = "2026-01-25T12:59:45.804Z" }, - { url = "https://files.pythonhosted.org/packages/b2/47/3a8112627e9d863e7cddd72894171c929e94491a597811725befdcd76bce/coverage-7.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bca209d001fd03ea2d978f8a4985093240a355c93078aee3f799852c23f561a", size = 263568, upload-time = "2026-01-25T12:59:47.929Z" }, - { url = "https://files.pythonhosted.org/packages/92/bc/7ea367d84afa3120afc3ce6de294fd2dcd33b51e2e7fbe4bbfd200f2cb8c/coverage-7.13.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6b8092aa38d72f091db61ef83cb66076f18f02da3e1a75039a4f218629600e04", size = 261174, upload-time = "2026-01-25T12:59:49.717Z" }, - { url = "https://files.pythonhosted.org/packages/33/b7/f1092dcecb6637e31cc2db099581ee5c61a17647849bae6b8261a2b78430/coverage-7.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4a3158dc2dcce5200d91ec28cd315c999eebff355437d2765840555d765a6e5f", size = 260017, upload-time = "2026-01-25T12:59:51.463Z" }, - { url = "https://files.pythonhosted.org/packages/2b/cd/f3d07d4b95fbe1a2ef0958c15da614f7e4f557720132de34d2dc3aa7e911/coverage-7.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3973f353b2d70bd9796cc12f532a05945232ccae966456c8ed7034cb96bbfd6f", size = 262337, upload-time = "2026-01-25T12:59:53.407Z" }, - { url = "https://files.pythonhosted.org/packages/e0/db/b0d5b2873a07cb1e06a55d998697c0a5a540dcefbf353774c99eb3874513/coverage-7.13.2-cp314-cp314t-win32.whl", hash = "sha256:79f6506a678a59d4ded048dc72f1859ebede8ec2b9a2d509ebe161f01c2879d3", size = 222749, upload-time = "2026-01-25T12:59:56.316Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2f/838a5394c082ac57d85f57f6aba53093b30d9089781df72412126505716f/coverage-7.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:196bfeabdccc5a020a57d5a368c681e3a6ceb0447d153aeccc1ab4d70a5032ba", size = 223857, upload-time = "2026-01-25T12:59:58.201Z" }, - { url = "https://files.pythonhosted.org/packages/44/d4/b608243e76ead3a4298824b50922b89ef793e50069ce30316a65c1b4d7ef/coverage-7.13.2-cp314-cp314t-win_arm64.whl", hash = "sha256:69269ab58783e090bfbf5b916ab3d188126e22d6070bbfc93098fdd474ef937c", size = 221881, upload-time = "2026-01-25T13:00:00.449Z" }, - { url = "https://files.pythonhosted.org/packages/d2/db/d291e30fdf7ea617a335531e72294e0c723356d7fdde8fba00610a76bda9/coverage-7.13.2-py3-none-any.whl", hash = "sha256:40ce1ea1e25125556d8e76bd0b61500839a07944cc287ac21d5626f3e620cad5", size = 210943, upload-time = "2026-01-25T13:00:02.388Z" }, -] - -[[package]] -name = "cryptography" -version = "46.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/19/f748958276519adf6a0c1e79e7b8860b4830dda55ccdf29f2719b5fc499c/cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59", size = 749301, upload-time = "2026-01-28T00:24:37.379Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/99/157aae7949a5f30d51fcb1a9851e8ebd5c74bf99b5285d8bb4b8b9ee641e/cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485", size = 7173686, upload-time = "2026-01-28T00:23:07.515Z" }, - { url = "https://files.pythonhosted.org/packages/87/91/874b8910903159043b5c6a123b7e79c4559ddd1896e38967567942635778/cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc", size = 4275871, upload-time = "2026-01-28T00:23:09.439Z" }, - { url = "https://files.pythonhosted.org/packages/c0/35/690e809be77896111f5b195ede56e4b4ed0435b428c2f2b6d35046fbb5e8/cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0", size = 4423124, upload-time = "2026-01-28T00:23:11.529Z" }, - { url = "https://files.pythonhosted.org/packages/1a/5b/a26407d4f79d61ca4bebaa9213feafdd8806dc69d3d290ce24996d3cfe43/cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa", size = 4277090, upload-time = "2026-01-28T00:23:13.123Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d8/4bb7aec442a9049827aa34cee1aa83803e528fa55da9a9d45d01d1bb933e/cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81", size = 4947652, upload-time = "2026-01-28T00:23:14.554Z" }, - { url = "https://files.pythonhosted.org/packages/2b/08/f83e2e0814248b844265802d081f2fac2f1cbe6cd258e72ba14ff006823a/cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255", size = 4455157, upload-time = "2026-01-28T00:23:16.443Z" }, - { url = "https://files.pythonhosted.org/packages/0a/05/19d849cf4096448779d2dcc9bb27d097457dac36f7273ffa875a93b5884c/cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e", size = 3981078, upload-time = "2026-01-28T00:23:17.838Z" }, - { url = "https://files.pythonhosted.org/packages/e6/89/f7bac81d66ba7cde867a743ea5b37537b32b5c633c473002b26a226f703f/cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c", size = 4276213, upload-time = "2026-01-28T00:23:19.257Z" }, - { url = "https://files.pythonhosted.org/packages/da/9f/7133e41f24edd827020ad21b068736e792bc68eecf66d93c924ad4719fb3/cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32", size = 4912190, upload-time = "2026-01-28T00:23:21.244Z" }, - { url = "https://files.pythonhosted.org/packages/a6/f7/6d43cbaddf6f65b24816e4af187d211f0bc536a29961f69faedc48501d8e/cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616", size = 4454641, upload-time = "2026-01-28T00:23:22.866Z" }, - { url = "https://files.pythonhosted.org/packages/9e/4f/ebd0473ad656a0ac912a16bd07db0f5d85184924e14fc88feecae2492834/cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0", size = 4405159, upload-time = "2026-01-28T00:23:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d1/f7/7923886f32dc47e27adeff8246e976d77258fd2aa3efdd1754e4e323bf49/cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0", size = 4666059, upload-time = "2026-01-28T00:23:26.766Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a7/0fca0fd3591dffc297278a61813d7f661a14243dd60f499a7a5b48acb52a/cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5", size = 3026378, upload-time = "2026-01-28T00:23:28.317Z" }, - { url = "https://files.pythonhosted.org/packages/2d/12/652c84b6f9873f0909374864a57b003686c642ea48c84d6c7e2c515e6da5/cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b", size = 3478614, upload-time = "2026-01-28T00:23:30.275Z" }, - { url = "https://files.pythonhosted.org/packages/b9/27/542b029f293a5cce59349d799d4d8484b3b1654a7b9a0585c266e974a488/cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908", size = 7116417, upload-time = "2026-01-28T00:23:31.958Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f5/559c25b77f40b6bf828eabaf988efb8b0e17b573545edb503368ca0a2a03/cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da", size = 4264508, upload-time = "2026-01-28T00:23:34.264Z" }, - { url = "https://files.pythonhosted.org/packages/49/a1/551fa162d33074b660dc35c9bc3616fefa21a0e8c1edd27b92559902e408/cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829", size = 4409080, upload-time = "2026-01-28T00:23:35.793Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/4d8d129a755f5d6df1bbee69ea2f35ebfa954fa1847690d1db2e8bca46a5/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2", size = 4270039, upload-time = "2026-01-28T00:23:37.263Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f5/ed3fcddd0a5e39321e595e144615399e47e7c153a1fb8c4862aec3151ff9/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085", size = 4926748, upload-time = "2026-01-28T00:23:38.884Z" }, - { url = "https://files.pythonhosted.org/packages/43/ae/9f03d5f0c0c00e85ecb34f06d3b79599f20630e4db91b8a6e56e8f83d410/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b", size = 4442307, upload-time = "2026-01-28T00:23:40.56Z" }, - { url = "https://files.pythonhosted.org/packages/8b/22/e0f9f2dae8040695103369cf2283ef9ac8abe4d51f68710bec2afd232609/cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd", size = 3959253, upload-time = "2026-01-28T00:23:42.827Z" }, - { url = "https://files.pythonhosted.org/packages/01/5b/6a43fcccc51dae4d101ac7d378a8724d1ba3de628a24e11bf2f4f43cba4d/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2", size = 4269372, upload-time = "2026-01-28T00:23:44.655Z" }, - { url = "https://files.pythonhosted.org/packages/17/b7/0f6b8c1dd0779df2b526e78978ff00462355e31c0a6f6cff8a3e99889c90/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e", size = 4891908, upload-time = "2026-01-28T00:23:46.48Z" }, - { url = "https://files.pythonhosted.org/packages/83/17/259409b8349aa10535358807a472c6a695cf84f106022268d31cea2b6c97/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f", size = 4441254, upload-time = "2026-01-28T00:23:48.403Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fe/e4a1b0c989b00cee5ffa0764401767e2d1cf59f45530963b894129fd5dce/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82", size = 4396520, upload-time = "2026-01-28T00:23:50.26Z" }, - { url = "https://files.pythonhosted.org/packages/b3/81/ba8fd9657d27076eb40d6a2f941b23429a3c3d2f56f5a921d6b936a27bc9/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c", size = 4651479, upload-time = "2026-01-28T00:23:51.674Z" }, - { url = "https://files.pythonhosted.org/packages/00/03/0de4ed43c71c31e4fe954edd50b9d28d658fef56555eba7641696370a8e2/cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061", size = 3001986, upload-time = "2026-01-28T00:23:53.485Z" }, - { url = "https://files.pythonhosted.org/packages/5c/70/81830b59df7682917d7a10f833c4dab2a5574cd664e86d18139f2b421329/cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7", size = 3468288, upload-time = "2026-01-28T00:23:55.09Z" }, - { url = "https://files.pythonhosted.org/packages/56/f7/f648fdbb61d0d45902d3f374217451385edc7e7768d1b03ff1d0e5ffc17b/cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab", size = 7169583, upload-time = "2026-01-28T00:23:56.558Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cc/8f3224cbb2a928de7298d6ed4790f5ebc48114e02bdc9559196bfb12435d/cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef", size = 4275419, upload-time = "2026-01-28T00:23:58.364Z" }, - { url = "https://files.pythonhosted.org/packages/17/43/4a18faa7a872d00e4264855134ba82d23546c850a70ff209e04ee200e76f/cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d", size = 4419058, upload-time = "2026-01-28T00:23:59.867Z" }, - { url = "https://files.pythonhosted.org/packages/ee/64/6651969409821d791ba12346a124f55e1b76f66a819254ae840a965d4b9c/cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973", size = 4278151, upload-time = "2026-01-28T00:24:01.731Z" }, - { url = "https://files.pythonhosted.org/packages/20/0b/a7fce65ee08c3c02f7a8310cc090a732344066b990ac63a9dfd0a655d321/cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4", size = 4939441, upload-time = "2026-01-28T00:24:03.175Z" }, - { url = "https://files.pythonhosted.org/packages/db/a7/20c5701e2cd3e1dfd7a19d2290c522a5f435dd30957d431dcb531d0f1413/cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af", size = 4451617, upload-time = "2026-01-28T00:24:05.403Z" }, - { url = "https://files.pythonhosted.org/packages/00/dc/3e16030ea9aa47b63af6524c354933b4fb0e352257c792c4deeb0edae367/cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263", size = 3977774, upload-time = "2026-01-28T00:24:06.851Z" }, - { url = "https://files.pythonhosted.org/packages/42/c8/ad93f14118252717b465880368721c963975ac4b941b7ef88f3c56bf2897/cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095", size = 4277008, upload-time = "2026-01-28T00:24:08.926Z" }, - { url = "https://files.pythonhosted.org/packages/00/cf/89c99698151c00a4631fbfcfcf459d308213ac29e321b0ff44ceeeac82f1/cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b", size = 4903339, upload-time = "2026-01-28T00:24:12.009Z" }, - { url = "https://files.pythonhosted.org/packages/03/c3/c90a2cb358de4ac9309b26acf49b2a100957e1ff5cc1e98e6c4996576710/cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019", size = 4451216, upload-time = "2026-01-28T00:24:13.975Z" }, - { url = "https://files.pythonhosted.org/packages/96/2c/8d7f4171388a10208671e181ca43cdc0e596d8259ebacbbcfbd16de593da/cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4", size = 4404299, upload-time = "2026-01-28T00:24:16.169Z" }, - { url = "https://files.pythonhosted.org/packages/e9/23/cbb2036e450980f65c6e0a173b73a56ff3bccd8998965dea5cc9ddd424a5/cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b", size = 4664837, upload-time = "2026-01-28T00:24:17.629Z" }, - { url = "https://files.pythonhosted.org/packages/0a/21/f7433d18fe6d5845329cbdc597e30caf983229c7a245bcf54afecc555938/cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc", size = 3009779, upload-time = "2026-01-28T00:24:20.198Z" }, - { url = "https://files.pythonhosted.org/packages/3a/6a/bd2e7caa2facffedf172a45c1a02e551e6d7d4828658c9a245516a598d94/cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976", size = 3466633, upload-time = "2026-01-28T00:24:21.851Z" }, -] - -[[package]] -name = "cuda-bindings" -version = "12.9.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-pathfinder" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/c1/dabe88f52c3e3760d861401bb994df08f672ec893b8f7592dc91626adcf3/cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8", size = 12151019, upload-time = "2025-10-21T14:51:43.167Z" }, - { url = "https://files.pythonhosted.org/packages/63/56/e465c31dc9111be3441a9ba7df1941fe98f4aa6e71e8788a3fb4534ce24d/cuda_bindings-12.9.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:32bdc5a76906be4c61eb98f546a6786c5773a881f3b166486449b5d141e4a39f", size = 11906628, upload-time = "2025-10-21T14:51:49.905Z" }, - { url = "https://files.pythonhosted.org/packages/a3/84/1e6be415e37478070aeeee5884c2022713c1ecc735e6d82d744de0252eee/cuda_bindings-12.9.4-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56e0043c457a99ac473ddc926fe0dc4046694d99caef633e92601ab52cbe17eb", size = 11925991, upload-time = "2025-10-21T14:51:56.535Z" }, - { url = "https://files.pythonhosted.org/packages/d1/af/6dfd8f2ed90b1d4719bc053ff8940e494640fe4212dc3dd72f383e4992da/cuda_bindings-12.9.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b72ee72a9cc1b531db31eebaaee5c69a8ec3500e32c6933f2d3b15297b53686", size = 11922703, upload-time = "2025-10-21T14:52:03.585Z" }, - { url = "https://files.pythonhosted.org/packages/6c/19/90ac264acc00f6df8a49378eedec9fd2db3061bf9263bf9f39fd3d8377c3/cuda_bindings-12.9.4-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d80bffc357df9988dca279734bc9674c3934a654cab10cadeed27ce17d8635ee", size = 11924658, upload-time = "2025-10-21T14:52:10.411Z" }, -] - -[[package]] -name = "cuda-pathfinder" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/02/4dbe7568a42e46582248942f54dc64ad094769532adbe21e525e4edf7bc4/cuda_pathfinder-1.3.3-py3-none-any.whl", hash = "sha256:9984b664e404f7c134954a771be8775dfd6180ea1e1aef4a5a37d4be05d9bbb1", size = 27154, upload-time = "2025-12-04T22:35:08.996Z" }, -] - -[[package]] -name = "cytoolz" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "toolz" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bd/d4/16916f3dc20a3f5455b63c35dcb260b3716f59ce27a93586804e70e431d5/cytoolz-1.1.0.tar.gz", hash = "sha256:13a7bf254c3c0d28b12e2290b82aed0f0977a4c2a2bf84854fcdc7796a29f3b0", size = 642510, upload-time = "2025-10-19T00:44:56.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/ec/01426224f7acf60183d3921b25e1a8e71713d3d39cb464d64ac7aace6ea6/cytoolz-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:99f8e134c9be11649342853ec8c90837af4089fc8ff1e8f9a024a57d1fa08514", size = 1327800, upload-time = "2025-10-19T00:40:48.674Z" }, - { url = "https://files.pythonhosted.org/packages/b4/07/e07e8fedd332ac9626ad58bea31416dda19bfd14310731fa38b16a97e15f/cytoolz-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0a6f44cf9319c30feb9a50aa513d777ef51efec16f31c404409e7deb8063df64", size = 997118, upload-time = "2025-10-19T00:40:50.919Z" }, - { url = "https://files.pythonhosted.org/packages/ab/72/c0f766d63ed2f9ea8dc8e1628d385d99b41fb834ce17ac3669e3f91e115d/cytoolz-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:945580dc158c557172fca899a35a99a16fbcebf6db0c77cb6621084bc82189f9", size = 991169, upload-time = "2025-10-19T00:40:52.887Z" }, - { url = "https://files.pythonhosted.org/packages/df/4b/1f757353d1bf33e56a7391ecc9bc49c1e529803b93a9d2f67fe5f92906fe/cytoolz-1.1.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:257905ec050d04f2f856854620d1e25556fd735064cebd81b460f54939b9f9d5", size = 2700680, upload-time = "2025-10-19T00:40:54.597Z" }, - { url = "https://files.pythonhosted.org/packages/25/73/9b25bb7ed8d419b9d6ff2ae0b3d06694de79a3f98f5169a1293ff7ad3a3f/cytoolz-1.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82779049f352fb3ab5e8c993ab45edbb6e02efb1f17f0b50f4972c706cc51d76", size = 2824951, upload-time = "2025-10-19T00:40:56.137Z" }, - { url = "https://files.pythonhosted.org/packages/0c/93/9c787f7c909e75670fff467f2504725d06d8c3f51d6dfe22c55a08c8ccd4/cytoolz-1.1.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7d3e405e435320e08c5a1633afaf285a392e2d9cef35c925d91e2a31dfd7a688", size = 2679635, upload-time = "2025-10-19T00:40:57.799Z" }, - { url = "https://files.pythonhosted.org/packages/50/aa/9ee92c302cccf7a41a7311b325b51ebeff25d36c1f82bdc1bbe3f58dc947/cytoolz-1.1.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:923df8f5591e0d20543060c29909c149ab1963a7267037b39eee03a83dbc50a8", size = 2938352, upload-time = "2025-10-19T00:40:59.49Z" }, - { url = "https://files.pythonhosted.org/packages/6a/a3/3b58c5c1692c3bacd65640d0d5c7267a7ebb76204f7507aec29de7063d2f/cytoolz-1.1.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:25db9e4862f22ea0ae2e56c8bec9fc9fd756b655ae13e8c7b5625d7ed1c582d4", size = 3022121, upload-time = "2025-10-19T00:41:01.209Z" }, - { url = "https://files.pythonhosted.org/packages/e1/93/c647bc3334355088c57351a536c2d4a83dd45f7de591fab383975e45bff9/cytoolz-1.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7a98deb11ccd8e5d9f9441ef2ff3352aab52226a2b7d04756caaa53cd612363", size = 2857656, upload-time = "2025-10-19T00:41:03.456Z" }, - { url = "https://files.pythonhosted.org/packages/b2/c2/43fea146bf4141deea959e19dcddf268c5ed759dec5c2ed4a6941d711933/cytoolz-1.1.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dce4ee9fc99104bc77efdea80f32ca5a650cd653bcc8a1d984a931153d3d9b58", size = 2551284, upload-time = "2025-10-19T00:41:05.347Z" }, - { url = "https://files.pythonhosted.org/packages/6f/df/cdc7a81ce5cfcde7ef523143d545635fc37e80ccacce140ae58483a21da3/cytoolz-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80d6da158f7d20c15819701bbda1c041f0944ede2f564f5c739b1bc80a9ffb8b", size = 2721673, upload-time = "2025-10-19T00:41:07.528Z" }, - { url = "https://files.pythonhosted.org/packages/45/be/f8524bb9ad8812ad375e61238dcaa3177628234d1b908ad0b74e3657cafd/cytoolz-1.1.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3b5c5a192abda123ad45ef716ec9082b4cf7d95e9ada8291c5c2cc5558be858b", size = 2722884, upload-time = "2025-10-19T00:41:09.698Z" }, - { url = "https://files.pythonhosted.org/packages/23/e6/6bb8e4f9c267ad42d1ff77b6d2e4984665505afae50a216290e1d7311431/cytoolz-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5b399ce7d967b1cb6280250818b786be652aa8ddffd3c0bb5c48c6220d945ab5", size = 2685486, upload-time = "2025-10-19T00:41:11.349Z" }, - { url = "https://files.pythonhosted.org/packages/d7/dd/88619f9c8d2b682562c0c886bbb7c35720cb83fda2ac9a41bdd14073d9bd/cytoolz-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e7e29a1a03f00b4322196cfe8e2c38da9a6c8d573566052c586df83aacc5663c", size = 2839661, upload-time = "2025-10-19T00:41:13.053Z" }, - { url = "https://files.pythonhosted.org/packages/b8/8d/4478ebf471ee78dd496d254dc0f4ad729cd8e6ba8257de4f0a98a2838ef2/cytoolz-1.1.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5291b117d71652a817ec164e7011f18e6a51f8a352cc9a70ed5b976c51102fda", size = 2547095, upload-time = "2025-10-19T00:41:16.054Z" }, - { url = "https://files.pythonhosted.org/packages/e6/68/f1dea33367b0b3f64e199c230a14a6b6f243c189020effafd31e970ca527/cytoolz-1.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:8caef62f846a9011676c51bda9189ae394cdd6bb17f2946ecaedc23243268320", size = 2870901, upload-time = "2025-10-19T00:41:17.727Z" }, - { url = "https://files.pythonhosted.org/packages/4a/9a/33591c09dfe799b8fb692cf2ad383e2c41ab6593cc960b00d1fc8a145655/cytoolz-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:de425c5a8e3be7bb3a195e19191d28d9eb3c2038046064a92edc4505033ec9cb", size = 2765422, upload-time = "2025-10-19T00:41:20.075Z" }, - { url = "https://files.pythonhosted.org/packages/60/2b/a8aa233c9416df87f004e57ae4280bd5e1f389b4943d179f01020c6ec629/cytoolz-1.1.0-cp312-cp312-win32.whl", hash = "sha256:296440a870e8d1f2e1d1edf98f60f1532b9d3ab8dfbd4b25ec08cd76311e79e5", size = 901933, upload-time = "2025-10-19T00:41:21.646Z" }, - { url = "https://files.pythonhosted.org/packages/ad/33/4c9bdf8390dc01d2617c7f11930697157164a52259b6818ddfa2f94f89f4/cytoolz-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:07156987f224c6dac59aa18fb8bf91e1412f5463961862716a3381bf429c8699", size = 947989, upload-time = "2025-10-19T00:41:23.288Z" }, - { url = "https://files.pythonhosted.org/packages/35/ac/6e2708835875f5acb52318462ed296bf94ed0cb8c7cb70e62fbd03f709e3/cytoolz-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:23e616b38f5b3160c7bb45b0f84a8f3deb4bd26b29fb2dfc716f241c738e27b8", size = 903913, upload-time = "2025-10-19T00:41:24.992Z" }, - { url = "https://files.pythonhosted.org/packages/71/4a/b3ddb3ee44fe0045e95dd973746f93f033b6f92cce1fc3cbbe24b329943c/cytoolz-1.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:76c9b58555300be6dde87a41faf1f97966d79b9a678b7a526fcff75d28ef4945", size = 976728, upload-time = "2025-10-19T00:41:26.5Z" }, - { url = "https://files.pythonhosted.org/packages/42/21/a3681434aa425875dd828bb515924b0f12c37a55c7d2bc5c0c5de3aeb0b4/cytoolz-1.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d1d638b10d3144795655e9395566ce35807df09219fd7cacd9e6acbdef67946a", size = 986057, upload-time = "2025-10-19T00:41:28.911Z" }, - { url = "https://files.pythonhosted.org/packages/d9/cb/efc1b29e211e0670a6953222afaac84dcbba5cb940b130c0e49858978040/cytoolz-1.1.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:26801c1a165e84786a99e03c9c9973356caaca002d66727b761fb1042878ef06", size = 992632, upload-time = "2025-10-19T00:41:30.612Z" }, - { url = "https://files.pythonhosted.org/packages/be/b0/e50621d21e939338c97faab651f58ea7fa32101226a91de79ecfb89d71e1/cytoolz-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2a9a464542912d3272f6dccc5142df057c71c6a5cbd30439389a732df401afb7", size = 1317534, upload-time = "2025-10-19T00:41:32.625Z" }, - { url = "https://files.pythonhosted.org/packages/0d/6b/25aa9739b0235a5bc4c1ea293186bc6822a4c6607acfe1422423287e7400/cytoolz-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ed6104fa942aa5784bf54f339563de637557e3443b105760bc4de8f16a7fc79b", size = 992336, upload-time = "2025-10-19T00:41:34.073Z" }, - { url = "https://files.pythonhosted.org/packages/e1/53/5f4deb0ff958805309d135d899c764364c1e8a632ce4994bd7c45fb98df2/cytoolz-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56161f0ab60dc4159ec343509abaf809dc88e85c7e420e354442c62e3e7cbb77", size = 986118, upload-time = "2025-10-19T00:41:35.7Z" }, - { url = "https://files.pythonhosted.org/packages/1c/e3/f6255b76c8cc0debbe1c0779130777dc0434da6d9b28a90d9f76f8cb67cd/cytoolz-1.1.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:832bd36cc9123535f1945acf6921f8a2a15acc19cfe4065b1c9b985a28671886", size = 2679563, upload-time = "2025-10-19T00:41:37.926Z" }, - { url = "https://files.pythonhosted.org/packages/59/8a/acc6e39a84e930522b965586ad3a36694f9bf247b23188ee0eb47b1c9ed1/cytoolz-1.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1842636b6e034f229bf084c2bcdcfd36c8437e752eefd2c74ce9e2f10415cb6e", size = 2813020, upload-time = "2025-10-19T00:41:39.935Z" }, - { url = "https://files.pythonhosted.org/packages/db/f5/0083608286ad1716eda7c41f868e85ac549f6fd6b7646993109fa0bdfd98/cytoolz-1.1.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:823df012ab90d2f2a0f92fea453528539bf71ac1879e518524cd0c86aa6df7b9", size = 2669312, upload-time = "2025-10-19T00:41:41.55Z" }, - { url = "https://files.pythonhosted.org/packages/47/a8/d16080b575520fe5da00cede1ece4e0a4180ec23f88dcdc6a2f5a90a7f7f/cytoolz-1.1.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2f1fcf9e7e7b3487883ff3f815abc35b89dcc45c4cf81c72b7ee457aa72d197b", size = 2922147, upload-time = "2025-10-19T00:41:43.252Z" }, - { url = "https://files.pythonhosted.org/packages/7e/bc/716c9c1243701e58cad511eb3937fd550e645293c5ed1907639c5d66f194/cytoolz-1.1.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4cdb3fa1772116827f263f25b0cdd44c663b6701346a56411960534a06c082de", size = 2981602, upload-time = "2025-10-19T00:41:45.354Z" }, - { url = "https://files.pythonhosted.org/packages/14/bc/571b232996846b27f4ac0c957dc8bf60261e9b4d0d01c8d955e82329544e/cytoolz-1.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1b5c95041741b81430454db65183e133976f45ac3c03454cfa8147952568529", size = 2830103, upload-time = "2025-10-19T00:41:47.959Z" }, - { url = "https://files.pythonhosted.org/packages/5b/55/c594afb46ecd78e4b7e1fb92c947ed041807875661ceda73baaf61baba4f/cytoolz-1.1.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b2079fd9f1a65f4c61e6278c8a6d4f85edf30c606df8d5b32f1add88cbbe2286", size = 2533802, upload-time = "2025-10-19T00:41:49.683Z" }, - { url = "https://files.pythonhosted.org/packages/93/83/1edcf95832555a78fc43b975f3ebe8ceadcc9664dd47fd33747a14df5069/cytoolz-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a92a320d72bef1c7e2d4c6d875125cf57fc38be45feb3fac1bfa64ea401f54a4", size = 2706071, upload-time = "2025-10-19T00:41:51.386Z" }, - { url = "https://files.pythonhosted.org/packages/e2/df/035a408df87f25cfe3611557818b250126cd2281b2104cd88395de205583/cytoolz-1.1.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06d1c79aa51e6a92a90b0e456ebce2288f03dd6a76c7f582bfaa3eda7692e8a5", size = 2707575, upload-time = "2025-10-19T00:41:53.305Z" }, - { url = "https://files.pythonhosted.org/packages/7a/a4/ef78e13e16e93bf695a9331321d75fbc834a088d941f1c19e6b63314e257/cytoolz-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e1d7be25f6971e986a52b6d3a0da28e1941850985417c35528f6823aef2cfec5", size = 2660486, upload-time = "2025-10-19T00:41:55.542Z" }, - { url = "https://files.pythonhosted.org/packages/30/7a/2c3d60682b26058d435416c4e90d4a94db854de5be944dfd069ed1be648a/cytoolz-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:964b248edc31efc50a65e9eaa0c845718503823439d2fa5f8d2c7e974c2b5409", size = 2819605, upload-time = "2025-10-19T00:41:58.257Z" }, - { url = "https://files.pythonhosted.org/packages/45/92/19b722a1d83cc443fbc0c16e0dc376f8a451437890d3d9ee370358cf0709/cytoolz-1.1.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c9ff2b3c57c79b65cb5be14a18c6fd4a06d5036fb3f33e973a9f70e9ac13ca28", size = 2533559, upload-time = "2025-10-19T00:42:00.324Z" }, - { url = "https://files.pythonhosted.org/packages/1d/15/fa3b7891da51115204416f14192081d3dea0eaee091f123fdc1347de8dd1/cytoolz-1.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:22290b73086af600042d99f5ce52a43d4ad9872c382610413176e19fc1d4fd2d", size = 2839171, upload-time = "2025-10-19T00:42:01.881Z" }, - { url = "https://files.pythonhosted.org/packages/46/40/d3519d5cd86eebebf1e8b7174ec32dfb6ecec67b48b0cfb92bf226659b5a/cytoolz-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a2ade74fccd080ea793382968913ee38d7a35c921df435bbf0a6aeecf0d17574", size = 2743379, upload-time = "2025-10-19T00:42:03.809Z" }, - { url = "https://files.pythonhosted.org/packages/93/e2/a9e7511f0a13fdbefa5bf73cf8e4763878140de9453fd3e50d6ac57b6be7/cytoolz-1.1.0-cp313-cp313-win32.whl", hash = "sha256:db5dbcfda1c00e937426cbf9bdc63c24ebbc358c3263bfcbc1ab4a88dc52aa8e", size = 900844, upload-time = "2025-10-19T00:42:05.967Z" }, - { url = "https://files.pythonhosted.org/packages/d6/a4/fb7eb403c6a4c81e5a30363f34a71adcc8bf5292dc8ea32e2440aa5668f2/cytoolz-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9e2d3fe3b45c3eb7233746f7aca37789be3dceec3e07dcc406d3e045ea0f7bdc", size = 946461, upload-time = "2025-10-19T00:42:07.983Z" }, - { url = "https://files.pythonhosted.org/packages/93/bb/1c8c33d353548d240bc6e8677ee8c3560ce5fa2f084e928facf7c35a6dcf/cytoolz-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:32c559f95ff44a9ebcbd934acaa1e6dc8f3e6ffce4762a79a88528064873d6d5", size = 902673, upload-time = "2025-10-19T00:42:09.982Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ba/4a53acc60f59030fcaf48c7766e3c4c81bd997379425aa45b129396557b5/cytoolz-1.1.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9e2cd93b28f667c5870a070ab2b8bb4397470a85c4b204f2454b0ad001cd1ca3", size = 1372336, upload-time = "2025-10-19T00:42:12.104Z" }, - { url = "https://files.pythonhosted.org/packages/ac/90/f28fd8ad8319d8f5c8da69a2c29b8cf52a6d2c0161602d92b366d58926ab/cytoolz-1.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f494124e141a9361f31d79875fe7ea459a3be2b9dadd90480427c0c52a0943d4", size = 1011930, upload-time = "2025-10-19T00:42:14.231Z" }, - { url = "https://files.pythonhosted.org/packages/c9/95/4561c4e0ad1c944f7673d6d916405d68080f10552cfc5d69a1cf2475a9a1/cytoolz-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:53a3262bf221f19437ed544bf8c0e1980c81ac8e2a53d87a9bc075dba943d36f", size = 1020610, upload-time = "2025-10-19T00:42:15.877Z" }, - { url = "https://files.pythonhosted.org/packages/c3/14/b2e1ffa4995ec36e1372e243411ff36325e4e6d7ffa34eb4098f5357d176/cytoolz-1.1.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:47663e57d3f3f124921f38055e86a1022d0844c444ede2e8f090d3bbf80deb65", size = 2917327, upload-time = "2025-10-19T00:42:17.706Z" }, - { url = "https://files.pythonhosted.org/packages/4a/29/7cab6c609b4514ac84cca2f7dca6c509977a8fc16d27c3a50e97f105fa6a/cytoolz-1.1.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5a8755c4104ee4e3d5ba434c543b5f85fdee6a1f1df33d93f518294da793a60", size = 3108951, upload-time = "2025-10-19T00:42:19.363Z" }, - { url = "https://files.pythonhosted.org/packages/9a/71/1d1103b819458679277206ad07d78ca6b31c4bb88d6463fd193e19bfb270/cytoolz-1.1.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4d96ff3d381423af1b105295f97de86d1db51732c9566eb37378bab6670c5010", size = 2807149, upload-time = "2025-10-19T00:42:20.964Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d4/3d83a05a21e7d2ed2b9e6daf489999c29934b005de9190272b8a2e3735d0/cytoolz-1.1.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0ec96b3d537cdf47d4e76ded199f7440715f4c71029b45445cff92c1248808c2", size = 3111608, upload-time = "2025-10-19T00:42:22.684Z" }, - { url = "https://files.pythonhosted.org/packages/51/88/96f68354c3d4af68de41f0db4fe41a23b96a50a4a416636cea325490cfeb/cytoolz-1.1.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:208e2f2ef90a32b0acbff3303d90d89b13570a228d491d2e622a7883a3c68148", size = 3179373, upload-time = "2025-10-19T00:42:24.395Z" }, - { url = "https://files.pythonhosted.org/packages/ce/50/ed87a5cd8e6f27ffbb64c39e9730e18ec66c37631db2888ae711909f10c9/cytoolz-1.1.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d416a81bb0bd517558668e49d30a7475b5445f9bbafaab7dcf066f1e9adba36", size = 3003120, upload-time = "2025-10-19T00:42:26.18Z" }, - { url = "https://files.pythonhosted.org/packages/d3/a7/acde155b050d6eaa8e9c7845c98fc5fb28501568e78e83ebbf44f8855274/cytoolz-1.1.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f32e94c91ffe49af04835ee713ebd8e005c85ebe83e7e1fdcc00f27164c2d636", size = 2703225, upload-time = "2025-10-19T00:42:27.93Z" }, - { url = "https://files.pythonhosted.org/packages/1b/b6/9d518597c5bdea626b61101e8d2ff94124787a42259dafd9f5fc396f346a/cytoolz-1.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15d0c6405efc040499c46df44056a5c382f551a7624a41cf3e4c84a96b988a15", size = 2956033, upload-time = "2025-10-19T00:42:29.993Z" }, - { url = "https://files.pythonhosted.org/packages/89/7a/93e5f860926165538c85e1c5e1670ad3424f158df810f8ccd269da652138/cytoolz-1.1.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:bf069c5381d757debae891401b88b3a346ba3a28ca45ba9251103b282463fad8", size = 2862950, upload-time = "2025-10-19T00:42:31.803Z" }, - { url = "https://files.pythonhosted.org/packages/76/e6/99d6af00487bedc27597b54c9fcbfd5c833a69c6b7a9b9f0fff777bfc7aa/cytoolz-1.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7d5cf15892e63411ec1bd67deff0e84317d974e6ab2cdfefdd4a7cea2989df66", size = 2861757, upload-time = "2025-10-19T00:42:33.625Z" }, - { url = "https://files.pythonhosted.org/packages/71/ca/adfa1fb7949478135a37755cb8e88c20cd6b75c22a05f1128f05f3ab2c60/cytoolz-1.1.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3e3872c21170f8341656f8692f8939e8800dcee6549ad2474d4c817bdefd62cd", size = 2979049, upload-time = "2025-10-19T00:42:35.377Z" }, - { url = "https://files.pythonhosted.org/packages/70/4c/7bf47a03a4497d500bc73d4204e2d907771a017fa4457741b2a1d7c09319/cytoolz-1.1.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:b9ddeff8e8fd65eb1fcefa61018100b2b627e759ea6ad275d2e2a93ffac147bf", size = 2699492, upload-time = "2025-10-19T00:42:37.133Z" }, - { url = "https://files.pythonhosted.org/packages/7e/e7/3d034b0e4817314f07aa465d5864e9b8df9d25cb260a53dd84583e491558/cytoolz-1.1.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:02feeeda93e1fa3b33414eb57c2b0aefd1db8f558dd33fdfcce664a0f86056e4", size = 2995646, upload-time = "2025-10-19T00:42:38.912Z" }, - { url = "https://files.pythonhosted.org/packages/c1/62/be357181c71648d9fe1d1ce91cd42c63457dcf3c158e144416fd51dced83/cytoolz-1.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d08154ad45349162b6c37f12d5d1b2e6eef338e657b85e1621e4e6a4a69d64cb", size = 2919481, upload-time = "2025-10-19T00:42:40.85Z" }, - { url = "https://files.pythonhosted.org/packages/62/d5/bf5434fde726c4f80cb99912b2d8e0afa1587557e2a2d7e0315eb942f2de/cytoolz-1.1.0-cp313-cp313t-win32.whl", hash = "sha256:10ae4718a056948d73ca3e1bb9ab1f95f897ec1e362f829b9d37cc29ab566c60", size = 951595, upload-time = "2025-10-19T00:42:42.877Z" }, - { url = "https://files.pythonhosted.org/packages/64/29/39c161e9204a9715321ddea698cbd0abc317e78522c7c642363c20589e71/cytoolz-1.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:1bb77bc6197e5cb19784b6a42bb0f8427e81737a630d9d7dda62ed31733f9e6c", size = 1004445, upload-time = "2025-10-19T00:42:44.855Z" }, - { url = "https://files.pythonhosted.org/packages/e2/5a/7cbff5e9a689f558cb0bdf277f9562b2ac51acf7cd15e055b8c3efb0e1ef/cytoolz-1.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:563dda652c6ff52d215704fbe6b491879b78d7bbbb3a9524ec8e763483cb459f", size = 926207, upload-time = "2025-10-19T00:42:46.456Z" }, - { url = "https://files.pythonhosted.org/packages/b7/e8/297a85ba700f437c01eba962428e6ab4572f6c3e68e8ff442ce5c9d3a496/cytoolz-1.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d542cee7c7882d2a914a33dec4d3600416fb336734df979473249d4c53d207a1", size = 980613, upload-time = "2025-10-19T00:42:47.988Z" }, - { url = "https://files.pythonhosted.org/packages/e8/d7/2b02c9d18e9cc263a0e22690f78080809f1eafe72f26b29ccc115d3bf5c8/cytoolz-1.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31922849b701b0f24bb62e56eb2488dcd3aa6ae3057694bd6b3b7c4c2bc27c2f", size = 990476, upload-time = "2025-10-19T00:42:49.653Z" }, - { url = "https://files.pythonhosted.org/packages/89/26/b6b159d2929310fca0eff8a4989cd4b1ecbdf7c46fdff46c7a20fcae55c8/cytoolz-1.1.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e68308d32afd31943314735c1335e4ab5696110e96b405f6bdb8f2a8dc771a16", size = 992712, upload-time = "2025-10-19T00:42:51.306Z" }, - { url = "https://files.pythonhosted.org/packages/42/a0/f7c572aa151ed466b0fce4a327c3cc916d3ef3c82e341be59ea4b9bee9e4/cytoolz-1.1.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fc4bb48b3b866e1867f7c6411a4229e5b44be3989060663713e10efc24c9bd5f", size = 1322596, upload-time = "2025-10-19T00:42:52.978Z" }, - { url = "https://files.pythonhosted.org/packages/72/7c/a55d035e20b77b6725e85c8f1a418b3a4c23967288b8b0c2d1a40f158cbe/cytoolz-1.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:456f77207d1445025d7ef262b8370a05492dcb1490cb428b0f3bf1bd744a89b0", size = 992825, upload-time = "2025-10-19T00:42:55.026Z" }, - { url = "https://files.pythonhosted.org/packages/03/af/39d2d3db322136e12e9336a1f13bab51eab88b386bfb11f91d3faff8ba34/cytoolz-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:174ebc71ebb20a9baeffce6ee07ee2cd913754325c93f99d767380d8317930f7", size = 990525, upload-time = "2025-10-19T00:42:56.666Z" }, - { url = "https://files.pythonhosted.org/packages/a6/bd/65d7a869d307f9b10ad45c2c1cbb40b81a8d0ed1138fa17fd904f5c83298/cytoolz-1.1.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8b3604fef602bcd53415055a4f68468339192fd17be39e687ae24f476d23d56e", size = 2672409, upload-time = "2025-10-19T00:42:58.81Z" }, - { url = "https://files.pythonhosted.org/packages/2d/fb/74dfd844bfd67e810bd36e8e3903a143035447245828e7fcd7c81351d775/cytoolz-1.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3604b959a01f64c366e7d10ec7634d5f5cfe10301e27a8f090f6eb3b2a628a18", size = 2808477, upload-time = "2025-10-19T00:43:00.577Z" }, - { url = "https://files.pythonhosted.org/packages/d6/1f/587686c43e31c19241ec317da66438d093523921ea7749bbc65558a30df9/cytoolz-1.1.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6db2127a3c1bc2f59f08010d2ae53a760771a9de2f67423ad8d400e9ba4276e8", size = 2636881, upload-time = "2025-10-19T00:43:02.24Z" }, - { url = "https://files.pythonhosted.org/packages/bc/6d/90468cd34f77cb38a11af52c4dc6199efcc97a486395a21bef72e9b7602e/cytoolz-1.1.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56584745ac647993a016a21bc76399113b7595e312f8d0a1b140c9fcf9b58a27", size = 2937315, upload-time = "2025-10-19T00:43:03.954Z" }, - { url = "https://files.pythonhosted.org/packages/d9/50/7b92cd78c613b92e3509e6291d3fb7e0d72ebda999a8df806a96c40ca9ab/cytoolz-1.1.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db2c4c3a7f7bd7e03bb1a236a125c8feb86c75802f4ecda6ecfaf946610b2930", size = 2959988, upload-time = "2025-10-19T00:43:05.758Z" }, - { url = "https://files.pythonhosted.org/packages/44/d5/34b5a28a8d9bb329f984b4c2259407ca3f501d1abeb01bacea07937d85d1/cytoolz-1.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:48cb8a692111a285d2b9acd16d185428176bfbffa8a7c274308525fccd01dd42", size = 2795116, upload-time = "2025-10-19T00:43:07.411Z" }, - { url = "https://files.pythonhosted.org/packages/f5/d9/5dd829e33273ec03bdc3c812e6c3281987ae2c5c91645582f6c331544a64/cytoolz-1.1.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d2f344ba5eb17dcf38ee37fdde726f69053f54927db8f8a1bed6ac61e5b1890d", size = 2535390, upload-time = "2025-10-19T00:43:09.104Z" }, - { url = "https://files.pythonhosted.org/packages/87/1f/7f9c58068a8eec2183110df051bc6b69dd621143f84473eeb6dc1b32905a/cytoolz-1.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:abf76b1c1abd031f098f293b6d90ee08bdaa45f8b5678430e331d991b82684b1", size = 2704834, upload-time = "2025-10-19T00:43:10.942Z" }, - { url = "https://files.pythonhosted.org/packages/d2/90/667def5665333575d01a65fe3ec0ca31b897895f6e3bc1a42d6ea3659369/cytoolz-1.1.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ddf9a38a5b686091265ff45b53d142e44a538cd6c2e70610d3bc6be094219032", size = 2658441, upload-time = "2025-10-19T00:43:12.655Z" }, - { url = "https://files.pythonhosted.org/packages/23/79/6615f9a14960bd29ac98b823777b6589357833f65cf1a11b5abc1587c120/cytoolz-1.1.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:946786755274f07bb2be0400f28adb31d7d85a7c7001873c0a8e24a503428fb3", size = 2654766, upload-time = "2025-10-19T00:43:14.325Z" }, - { url = "https://files.pythonhosted.org/packages/b0/99/be59c6e0ae02153ef10ae1ff0f380fb19d973c651b50cf829a731f6c9e79/cytoolz-1.1.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:d5b8f78b9fed79cf185ad4ddec099abeef45951bdcb416c5835ba05f0a1242c7", size = 2827649, upload-time = "2025-10-19T00:43:16.132Z" }, - { url = "https://files.pythonhosted.org/packages/19/b7/854ddcf9f9618844108677c20d48f4611b5c636956adea0f0e85e027608f/cytoolz-1.1.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fccde6efefdbc02e676ccb352a2ccc8a8e929f59a1c6d3d60bb78e923a49ca44", size = 2533456, upload-time = "2025-10-19T00:43:17.764Z" }, - { url = "https://files.pythonhosted.org/packages/45/66/bfe6fbb2bdcf03c8377c8c2f542576e15f3340c905a09d78a6cb3badd39a/cytoolz-1.1.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:717b7775313da5f51b0fbf50d865aa9c39cb241bd4cb605df3cf2246d6567397", size = 2826455, upload-time = "2025-10-19T00:43:19.561Z" }, - { url = "https://files.pythonhosted.org/packages/c3/0c/cce4047bd927e95f59e73319c02c9bc86bd3d76392e0eb9e41a1147a479c/cytoolz-1.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5158744a09d0e0e4a4f82225e3a3c4ebf38f9ae74467aaa905467270e52f2794", size = 2714897, upload-time = "2025-10-19T00:43:21.291Z" }, - { url = "https://files.pythonhosted.org/packages/ac/9a/061323bb289b565802bad14fb7ab59fcd8713105df142bcf4dd9ff64f8ac/cytoolz-1.1.0-cp314-cp314-win32.whl", hash = "sha256:1ed534bdbbf063b2bb28fca7d0f6723a3e5a72b086e7c7fe6d74ae8c3e4d00e2", size = 901490, upload-time = "2025-10-19T00:43:22.895Z" }, - { url = "https://files.pythonhosted.org/packages/a3/20/1f3a733d710d2a25d6f10b463bef55ada52fe6392a5d233c8d770191f48a/cytoolz-1.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:472c1c9a085f5ad973ec0ad7f0b9ba0969faea6f96c9e397f6293d386f3a25ec", size = 946730, upload-time = "2025-10-19T00:43:24.838Z" }, - { url = "https://files.pythonhosted.org/packages/f2/22/2d657db4a5d1c10a152061800f812caba9ef20d7bd2406f51a5fd800c180/cytoolz-1.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:a7ad7ca3386fa86bd301be3fa36e7f0acb024f412f665937955acfc8eb42deff", size = 905722, upload-time = "2025-10-19T00:43:26.439Z" }, - { url = "https://files.pythonhosted.org/packages/19/97/b4a8c76796a9a8b9bc90c7992840fa1589a1af8e0426562dea4ce9b384a7/cytoolz-1.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:64b63ed4b71b1ba813300ad0f06b8aff19a12cf51116e0e4f1ed837cea4debcf", size = 1372606, upload-time = "2025-10-19T00:43:28.491Z" }, - { url = "https://files.pythonhosted.org/packages/08/d4/a1bb1a32b454a2d650db8374ff3bf875ba0fc1c36e6446ec02a83b9140a1/cytoolz-1.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a60ba6f2ed9eb0003a737e1ee1e9fa2258e749da6477946008d4324efa25149f", size = 1012189, upload-time = "2025-10-19T00:43:30.177Z" }, - { url = "https://files.pythonhosted.org/packages/21/4b/2f5cbbd81588918ee7dd70cffb66731608f578a9b72166aafa991071af7d/cytoolz-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1aa58e2434d732241f7f051e6f17657e969a89971025e24578b5cbc6f1346485", size = 1020624, upload-time = "2025-10-19T00:43:31.712Z" }, - { url = "https://files.pythonhosted.org/packages/f5/99/c4954dd86cd593cd776a038b36795a259b8b5c12cbab6363edf5f6d9c909/cytoolz-1.1.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6965af3fc7214645970e312deb9bd35a213a1eaabcfef4f39115e60bf2f76867", size = 2917016, upload-time = "2025-10-19T00:43:33.531Z" }, - { url = "https://files.pythonhosted.org/packages/b2/7c/f1f70a17e272b433232bc8a27df97e46b202d6cc07e3b0d63f7f41ba0f2d/cytoolz-1.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ddd2863f321d67527d3b67a93000a378ad6f967056f68c06467fe011278a6d0e", size = 3107634, upload-time = "2025-10-19T00:43:35.57Z" }, - { url = "https://files.pythonhosted.org/packages/8f/bd/c3226a57474b4aef1f90040510cba30d0decd3515fed48dc229b37c2f898/cytoolz-1.1.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4e6b428e9eb5126053c2ae0efa62512ff4b38ed3951f4d0888ca7005d63e56f5", size = 2806221, upload-time = "2025-10-19T00:43:37.707Z" }, - { url = "https://files.pythonhosted.org/packages/c3/47/2f7bfe4aaa1e07dc9828bea228ed744faf73b26aee0c1bdf3b5520bf1909/cytoolz-1.1.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d758e5ef311d2671e0ae8c214c52e44617cf1e58bef8f022b547b9802a5a7f30", size = 3107671, upload-time = "2025-10-19T00:43:39.401Z" }, - { url = "https://files.pythonhosted.org/packages/4d/12/6ff3b04fbd1369d0fcd5f8b5910ba6e427e33bf113754c4c35ec3f747924/cytoolz-1.1.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a95416eca473e6c1179b48d86adcf528b59c63ce78f4cb9934f2e413afa9b56b", size = 3176350, upload-time = "2025-10-19T00:43:41.148Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/6691d986b728e77b5d2872743ebcd962d37a2d0f7e9ad95a81b284fbf905/cytoolz-1.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36c8ede93525cf11e2cc787b7156e5cecd7340193ef800b816a16f1404a8dc6d", size = 3001173, upload-time = "2025-10-19T00:43:42.923Z" }, - { url = "https://files.pythonhosted.org/packages/7a/cb/f59d83a5058e1198db5a1f04e4a124c94d60390e4fa89b6d2e38ee8288a0/cytoolz-1.1.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c949755b6d8a649c5fbc888bc30915926f1b09fe42fea9f289e297c2f6ddd3", size = 2701374, upload-time = "2025-10-19T00:43:44.716Z" }, - { url = "https://files.pythonhosted.org/packages/b7/f0/1ae6d28df503b0bdae094879da2072b8ba13db5919cd3798918761578411/cytoolz-1.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e1b6d37545816905a76d9ed59fa4e332f929e879f062a39ea0f6f620405cdc27", size = 2953081, upload-time = "2025-10-19T00:43:47.103Z" }, - { url = "https://files.pythonhosted.org/packages/f4/06/d86fe811c6222dc32d3e08f5d88d2be598a6055b4d0590e7c1428d55c386/cytoolz-1.1.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:05332112d4087904842b36954cd1d3fc0e463a2f4a7ef9477bd241427c593c3b", size = 2862228, upload-time = "2025-10-19T00:43:49.353Z" }, - { url = "https://files.pythonhosted.org/packages/ae/32/978ef6f42623be44a0a03ae9de875ab54aa26c7e38c5c4cd505460b0927d/cytoolz-1.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:31538ca2fad2d688cbd962ccc3f1da847329e2258a52940f10a2ac0719e526be", size = 2861971, upload-time = "2025-10-19T00:43:51.028Z" }, - { url = "https://files.pythonhosted.org/packages/ee/f7/74c69497e756b752b359925d1feef68b91df024a4124a823740f675dacd3/cytoolz-1.1.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:747562aa70abf219ea16f07d50ac0157db856d447f7f498f592e097cbc77df0b", size = 2975304, upload-time = "2025-10-19T00:43:52.99Z" }, - { url = "https://files.pythonhosted.org/packages/5b/2b/3ce0e6889a6491f3418ad4d84ae407b8456b02169a5a1f87990dbba7433b/cytoolz-1.1.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:3dc15c48b20c0f467e15e341e102896c8422dccf8efc6322def5c1b02f074629", size = 2697371, upload-time = "2025-10-19T00:43:55.312Z" }, - { url = "https://files.pythonhosted.org/packages/15/87/c616577f0891d97860643c845f7221e95240aa589586de727e28a5eb6e52/cytoolz-1.1.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3c03137ee6103ba92d5d6ad6a510e86fded69cd67050bd8a1843f15283be17ac", size = 2992436, upload-time = "2025-10-19T00:43:57.253Z" }, - { url = "https://files.pythonhosted.org/packages/e7/9f/490c81bffb3428ab1fa114051fbb5ba18aaa2e2fe4da5bf4170ca524e6b3/cytoolz-1.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:be8e298d88f88bd172b59912240558be3b7a04959375646e7fd4996401452941", size = 2917612, upload-time = "2025-10-19T00:43:59.423Z" }, - { url = "https://files.pythonhosted.org/packages/66/35/0fec2769660ca6472bbf3317ab634675827bb706d193e3240aaf20eab961/cytoolz-1.1.0-cp314-cp314t-win32.whl", hash = "sha256:3d407140f5604a89578285d4aac7b18b8eafa055cf776e781aabb89c48738fad", size = 960842, upload-time = "2025-10-19T00:44:01.143Z" }, - { url = "https://files.pythonhosted.org/packages/46/b4/b7ce3d3cd20337becfec978ecfa6d0ef64884d0cf32d44edfed8700914b9/cytoolz-1.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:56e5afb69eb6e1b3ffc34716ee5f92ffbdb5cb003b3a5ca4d4b0fe700e217162", size = 1020835, upload-time = "2025-10-19T00:44:03.246Z" }, - { url = "https://files.pythonhosted.org/packages/2c/1f/0498009aa563a9c5d04f520aadc6e1c0942434d089d0b2f51ea986470f55/cytoolz-1.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:27b19b4a286b3ff52040efa42dbe403730aebe5fdfd2def704eb285e2125c63e", size = 927963, upload-time = "2025-10-19T00:44:04.85Z" }, -] - -[[package]] -name = "deprecation" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/d3/8ae2869247df154b64c1884d7346d412fed0c49df84db635aab2d1c40e62/deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", size = 173788, upload-time = "2020-04-20T14:23:38.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", size = 11178, upload-time = "2020-04-20T14:23:36.581Z" }, -] - -[[package]] -name = "distro" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, -] - -[[package]] -name = "durationpy" -version = "0.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/a4/e44218c2b394e31a6dd0d6b095c4e1f32d0be54c2a4b250032d717647bab/durationpy-0.10.tar.gz", hash = "sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba", size = 3335, upload-time = "2025-05-17T13:52:37.26Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/0d/9feae160378a3553fa9a339b0e9c1a048e147a4127210e286ef18b730f03/durationpy-0.10-py3-none-any.whl", hash = "sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286", size = 3922, upload-time = "2025-05-17T13:52:36.463Z" }, -] - -[[package]] -name = "ecdsa" -version = "0.19.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/1f/924e3caae75f471eae4b26bd13b698f6af2c44279f67af317439c2f4c46a/ecdsa-0.19.1.tar.gz", hash = "sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61", size = 201793, upload-time = "2025-03-13T11:52:43.25Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/a3/460c57f094a4a165c84a1341c373b0a4f5ec6ac244b998d5021aade89b77/ecdsa-0.19.1-py2.py3-none-any.whl", hash = "sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3", size = 150607, upload-time = "2025-03-13T11:52:41.757Z" }, -] - -[[package]] -name = "eth-abi" -version = "5.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "eth-typing" }, - { name = "eth-utils" }, - { name = "parsimonious" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/00/71/d9e1380bd77fd22f98b534699af564f189b56d539cc2b9dab908d4e4c242/eth_abi-5.2.0.tar.gz", hash = "sha256:178703fa98c07d8eecd5ae569e7e8d159e493ebb6eeb534a8fe973fbc4e40ef0", size = 49797, upload-time = "2025-01-14T16:29:34.629Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/b4/2f3982c4cbcbf5eeb6aec62df1533c0e63c653b3021ff338d44944405676/eth_abi-5.2.0-py3-none-any.whl", hash = "sha256:17abe47560ad753f18054f5b3089fcb588f3e3a092136a416b6c1502cb7e8877", size = 28511, upload-time = "2025-01-14T16:29:31.862Z" }, -] - -[[package]] -name = "eth-account" -version = "0.11.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "bitarray" }, - { name = "ckzg" }, - { name = "eth-abi" }, - { name = "eth-keyfile" }, - { name = "eth-keys" }, - { name = "eth-rlp" }, - { name = "eth-utils" }, - { name = "hexbytes" }, - { name = "rlp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/73/42/2d1e2f1cb8b3f40f8c85f7df33e78ac0fc5f947c955607238e2e4a0d418b/eth_account-0.11.3.tar.gz", hash = "sha256:a712a9534638a7cfaa4cc069f1b9d5cefeee70362cfc3a7b0a2534ee61ce76c9", size = 712791, upload-time = "2024-08-21T20:18:31.508Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/de/a850f7d3d47f7c14c50cda73c8646a9ab140608c553008bffa949d74afab/eth_account-0.11.3-py3-none-any.whl", hash = "sha256:16cf58aabc65171fc206489899b7e5546e3215e1a4debc12dbd55345c979081e", size = 355394, upload-time = "2024-08-21T20:18:29.769Z" }, -] - -[[package]] -name = "eth-hash" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/38/577b7bc9380ef9dff0f1dffefe0c9a1ded2385e7a06c306fd95afb6f9451/eth_hash-0.7.1.tar.gz", hash = "sha256:d2411a403a0b0a62e8247b4117932d900ffb4c8c64b15f92620547ca5ce46be5", size = 12227, upload-time = "2025-01-13T21:29:21.765Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/db/f8775490669d28aca24871c67dd56b3e72105cb3bcae9a4ec65dd70859b3/eth_hash-0.7.1-py3-none-any.whl", hash = "sha256:0fb1add2adf99ef28883fd6228eb447ef519ea72933535ad1a0b28c6f65f868a", size = 8028, upload-time = "2025-01-13T21:29:19.365Z" }, -] - -[package.optional-dependencies] -pycryptodome = [ - { name = "pycryptodome" }, -] - -[[package]] -name = "eth-keyfile" -version = "0.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "eth-keys" }, - { name = "eth-utils" }, - { name = "py-ecc" }, - { name = "pycryptodome" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/08/e4/3f0c20b020786e1fa6e1ecd81806c54167fa2b0839e0020086b95a6e8faf/eth_keyfile-0.9.1.tar.gz", hash = "sha256:c7a8bc6af4527d1ab2eb1d1b949d59925252e17663eaf90087da121327b51df6", size = 19787, upload-time = "2025-02-10T18:01:01.703Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/08/9c8bf617b39e1dd56303593292e8b4eb66497a5f0f5b997a4b291e5343c0/eth_keyfile-0.9.1-py3-none-any.whl", hash = "sha256:9789c3b4fa0bb6e2616cdc2bdd71b8755b42947d78ef1e900a0149480fabb5c2", size = 9866, upload-time = "2025-02-10T18:00:59.695Z" }, -] - -[[package]] -name = "eth-keys" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "eth-typing" }, - { name = "eth-utils" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/11/1ed831c50bd74f57829aa06e58bd82a809c37e070ee501c953b9ac1f1552/eth_keys-0.7.0.tar.gz", hash = "sha256:79d24fd876201df67741de3e3fefb3f4dbcbb6ace66e47e6fe662851a4547814", size = 30166, upload-time = "2025-04-07T17:40:21.697Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/25/0ae00f2b0095e559d61ad3dc32171bd5a29dfd95ab04b4edd641f7c75f72/eth_keys-0.7.0-py3-none-any.whl", hash = "sha256:b0cdda8ffe8e5ba69c7c5ca33f153828edcace844f67aabd4542d7de38b159cf", size = 20656, upload-time = "2025-04-07T17:40:20.441Z" }, -] - -[[package]] -name = "eth-rlp" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "eth-utils" }, - { name = "hexbytes" }, - { name = "rlp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9b/2e/fb9c2e0a2d0e249b61abf462828f3f8039305dfbe5844e138ab1a3b3a413/eth-rlp-1.0.1.tar.gz", hash = "sha256:d61dbda892ee1220f28fb3663c08f6383c305db9f1f5624dc585c9cd05115027", size = 7261, upload-time = "2024-01-25T23:31:54.11Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/7f/583c8286530a52d9d5c07f3895c2184e36399379d1284dc1c2c8309a8e9d/eth_rlp-1.0.1-py3-none-any.whl", hash = "sha256:dd76515d71654277377d48876b88e839d61553aaf56952e580bb7cebef2b1517", size = 4922, upload-time = "2024-01-25T23:31:52.451Z" }, -] - -[[package]] -name = "eth-typing" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9e/24/b913ef5d1a9ff300b05de0f0c06a4d00caa2b1b81f8c7448d069f94a4168/eth_typing-4.4.0.tar.gz", hash = "sha256:93848083ac6bb4c20cc209ea9153a08b0a528be23337c889f89e1e5ffbe9807d", size = 22180, upload-time = "2024-07-09T20:01:07.588Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/b3/ab02e4da8b2616e7c370084d8f355f6a1c9b8755c4e5820766a34ddfedf5/eth_typing-4.4.0-py3-none-any.whl", hash = "sha256:a5e30a6e69edda7b1d1e96e9d71bab48b9bb988a77909d8d1666242c5562f841", size = 19322, upload-time = "2024-07-09T20:01:05.655Z" }, -] - -[[package]] -name = "eth-utils" -version = "4.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cytoolz", marker = "implementation_name == 'cpython'" }, - { name = "eth-hash" }, - { name = "eth-typing" }, - { name = "toolz", marker = "implementation_name == 'pypy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/54/ec65cf194c9b035df5cc00596a9eedcb430eabaf5486207e5ce859fe2aaf/eth_utils-4.1.1.tar.gz", hash = "sha256:71c8d10dec7494aeed20fa7a4d52ec2ce4a2e52fdce80aab4f5c3c19f3648b25", size = 110052, upload-time = "2024-05-06T18:21:53.805Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/5a/cfa1ba791233728236ca7cc32bbd18d1c84d4bbc735636cc57a9754a6c4d/eth_utils-4.1.1-py3-none-any.whl", hash = "sha256:ccbbac68a6d65cb6e294c5bcb6c6a5cec79a241c56dc5d9c345ed788c30f8534", size = 96001, upload-time = "2024-05-06T18:21:51.346Z" }, -] - -[[package]] -name = "fastapi" -version = "0.128.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-doc" }, - { name = "pydantic" }, - { name = "starlette" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/52/08/8c8508db6c7b9aae8f7175046af41baad690771c9bcde676419965e338c7/fastapi-0.128.0.tar.gz", hash = "sha256:1cc179e1cef10a6be60ffe429f79b829dce99d8de32d7acb7e6c8dfdf7f2645a", size = 365682, upload-time = "2025-12-27T15:21:13.714Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/05/5cbb59154b093548acd0f4c7c474a118eda06da25aa75c616b72d8fcd92a/fastapi-0.128.0-py3-none-any.whl", hash = "sha256:aebd93f9716ee3b4f4fcfe13ffb7cf308d99c9f3ab5622d8877441072561582d", size = 103094, upload-time = "2025-12-27T15:21:12.154Z" }, -] - -[[package]] -name = "fastembed" -version = "0.7.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, - { name = "loguru" }, - { name = "mmh3" }, - { name = "numpy" }, - { name = "onnxruntime" }, - { name = "pillow" }, - { name = "py-rust-stemmers" }, - { name = "requests" }, - { name = "tokenizers" }, - { name = "tqdm" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4c/c2/9c708680de1b54480161e0505f9d6d3d8eb47a1dc1a1f7f3c5106ba355d2/fastembed-0.7.4.tar.gz", hash = "sha256:8b8a4ea860ca295002f4754e8f5820a636e1065a9444959e18d5988d7f27093b", size = 68807, upload-time = "2025-12-05T12:08:10.447Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/3b/8da01492bc8b69184257d0c951bf0e77aec8ce110f06d8ce16c6ed9084f7/fastembed-0.7.4-py3-none-any.whl", hash = "sha256:79250a775f70bd6addb0e054204df042b5029ecae501e40e5bbd08e75844ad83", size = 108491, upload-time = "2025-12-05T12:08:09.059Z" }, -] - -[[package]] -name = "fastuuid" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232, upload-time = "2025-10-19T22:19:22.402Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/a2/e78fcc5df65467f0d207661b7ef86c5b7ac62eea337c0c0fcedbeee6fb13/fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77e94728324b63660ebf8adb27055e92d2e4611645bf12ed9d88d30486471d0a", size = 510164, upload-time = "2025-10-19T22:31:45.635Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b3/c846f933f22f581f558ee63f81f29fa924acd971ce903dab1a9b6701816e/fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:caa1f14d2102cb8d353096bc6ef6c13b2c81f347e6ab9d6fbd48b9dea41c153d", size = 261837, upload-time = "2025-10-19T22:38:38.53Z" }, - { url = "https://files.pythonhosted.org/packages/54/ea/682551030f8c4fa9a769d9825570ad28c0c71e30cf34020b85c1f7ee7382/fastuuid-0.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d23ef06f9e67163be38cece704170486715b177f6baae338110983f99a72c070", size = 251370, upload-time = "2025-10-19T22:40:26.07Z" }, - { url = "https://files.pythonhosted.org/packages/14/dd/5927f0a523d8e6a76b70968e6004966ee7df30322f5fc9b6cdfb0276646a/fastuuid-0.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c9ec605ace243b6dbe3bd27ebdd5d33b00d8d1d3f580b39fdd15cd96fd71796", size = 277766, upload-time = "2025-10-19T22:37:23.779Z" }, - { url = "https://files.pythonhosted.org/packages/16/6e/c0fb547eef61293153348f12e0f75a06abb322664b34a1573a7760501336/fastuuid-0.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:808527f2407f58a76c916d6aa15d58692a4a019fdf8d4c32ac7ff303b7d7af09", size = 278105, upload-time = "2025-10-19T22:26:56.821Z" }, - { url = "https://files.pythonhosted.org/packages/2d/b1/b9c75e03b768f61cf2e84ee193dc18601aeaf89a4684b20f2f0e9f52b62c/fastuuid-0.14.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fb3c0d7fef6674bbeacdd6dbd386924a7b60b26de849266d1ff6602937675c8", size = 301564, upload-time = "2025-10-19T22:30:31.604Z" }, - { url = "https://files.pythonhosted.org/packages/fc/fa/f7395fdac07c7a54f18f801744573707321ca0cee082e638e36452355a9d/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab3f5d36e4393e628a4df337c2c039069344db5f4b9d2a3c9cea48284f1dd741", size = 459659, upload-time = "2025-10-19T22:31:32.341Z" }, - { url = "https://files.pythonhosted.org/packages/66/49/c9fd06a4a0b1f0f048aacb6599e7d96e5d6bc6fa680ed0d46bf111929d1b/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b9a0ca4f03b7e0b01425281ffd44e99d360e15c895f1907ca105854ed85e2057", size = 478430, upload-time = "2025-10-19T22:26:22.962Z" }, - { url = "https://files.pythonhosted.org/packages/be/9c/909e8c95b494e8e140e8be6165d5fc3f61fdc46198c1554df7b3e1764471/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3acdf655684cc09e60fb7e4cf524e8f42ea760031945aa8086c7eae2eeeabeb8", size = 450894, upload-time = "2025-10-19T22:27:01.647Z" }, - { url = "https://files.pythonhosted.org/packages/90/eb/d29d17521976e673c55ef7f210d4cdd72091a9ec6755d0fd4710d9b3c871/fastuuid-0.14.0-cp312-cp312-win32.whl", hash = "sha256:9579618be6280700ae36ac42c3efd157049fe4dd40ca49b021280481c78c3176", size = 154374, upload-time = "2025-10-19T22:29:19.879Z" }, - { url = "https://files.pythonhosted.org/packages/cc/fc/f5c799a6ea6d877faec0472d0b27c079b47c86b1cdc577720a5386483b36/fastuuid-0.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:d9e4332dc4ba054434a9594cbfaf7823b57993d7d8e7267831c3e059857cf397", size = 156550, upload-time = "2025-10-19T22:27:49.658Z" }, - { url = "https://files.pythonhosted.org/packages/a5/83/ae12dd39b9a39b55d7f90abb8971f1a5f3c321fd72d5aa83f90dc67fe9ed/fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77a09cb7427e7af74c594e409f7731a0cf887221de2f698e1ca0ebf0f3139021", size = 510720, upload-time = "2025-10-19T22:42:34.633Z" }, - { url = "https://files.pythonhosted.org/packages/53/b0/a4b03ff5d00f563cc7546b933c28cb3f2a07344b2aec5834e874f7d44143/fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:9bd57289daf7b153bfa3e8013446aa144ce5e8c825e9e366d455155ede5ea2dc", size = 262024, upload-time = "2025-10-19T22:30:25.482Z" }, - { url = "https://files.pythonhosted.org/packages/9c/6d/64aee0a0f6a58eeabadd582e55d0d7d70258ffdd01d093b30c53d668303b/fastuuid-0.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ac60fc860cdf3c3f327374db87ab8e064c86566ca8c49d2e30df15eda1b0c2d5", size = 251679, upload-time = "2025-10-19T22:36:14.096Z" }, - { url = "https://files.pythonhosted.org/packages/60/f5/a7e9cda8369e4f7919d36552db9b2ae21db7915083bc6336f1b0082c8b2e/fastuuid-0.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab32f74bd56565b186f036e33129da77db8be09178cd2f5206a5d4035fb2a23f", size = 277862, upload-time = "2025-10-19T22:36:23.302Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d3/8ce11827c783affffd5bd4d6378b28eb6cc6d2ddf41474006b8d62e7448e/fastuuid-0.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e678459cf4addaedd9936bbb038e35b3f6b2061330fd8f2f6a1d80414c0f87", size = 278278, upload-time = "2025-10-19T22:29:43.809Z" }, - { url = "https://files.pythonhosted.org/packages/a2/51/680fb6352d0bbade04036da46264a8001f74b7484e2fd1f4da9e3db1c666/fastuuid-0.14.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1e3cc56742f76cd25ecb98e4b82a25f978ccffba02e4bdce8aba857b6d85d87b", size = 301788, upload-time = "2025-10-19T22:36:06.825Z" }, - { url = "https://files.pythonhosted.org/packages/fa/7c/2014b5785bd8ebdab04ec857635ebd84d5ee4950186a577db9eff0fb8ff6/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cb9a030f609194b679e1660f7e32733b7a0f332d519c5d5a6a0a580991290022", size = 459819, upload-time = "2025-10-19T22:35:31.623Z" }, - { url = "https://files.pythonhosted.org/packages/01/d2/524d4ceeba9160e7a9bc2ea3e8f4ccf1ad78f3bde34090ca0c51f09a5e91/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:09098762aad4f8da3a888eb9ae01c84430c907a297b97166b8abc07b640f2995", size = 478546, upload-time = "2025-10-19T22:26:03.023Z" }, - { url = "https://files.pythonhosted.org/packages/bc/17/354d04951ce114bf4afc78e27a18cfbd6ee319ab1829c2d5fb5e94063ac6/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1383fff584fa249b16329a059c68ad45d030d5a4b70fb7c73a08d98fd53bcdab", size = 450921, upload-time = "2025-10-19T22:31:02.151Z" }, - { url = "https://files.pythonhosted.org/packages/fb/be/d7be8670151d16d88f15bb121c5b66cdb5ea6a0c2a362d0dcf30276ade53/fastuuid-0.14.0-cp313-cp313-win32.whl", hash = "sha256:a0809f8cc5731c066c909047f9a314d5f536c871a7a22e815cc4967c110ac9ad", size = 154559, upload-time = "2025-10-19T22:36:36.011Z" }, - { url = "https://files.pythonhosted.org/packages/22/1d/5573ef3624ceb7abf4a46073d3554e37191c868abc3aecd5289a72f9810a/fastuuid-0.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:0df14e92e7ad3276327631c9e7cec09e32572ce82089c55cb1bb8df71cf394ed", size = 156539, upload-time = "2025-10-19T22:33:35.898Z" }, - { url = "https://files.pythonhosted.org/packages/16/c9/8c7660d1fe3862e3f8acabd9be7fc9ad71eb270f1c65cce9a2b7a31329ab/fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b852a870a61cfc26c884af205d502881a2e59cc07076b60ab4a951cc0c94d1ad", size = 510600, upload-time = "2025-10-19T22:43:44.17Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f4/a989c82f9a90d0ad995aa957b3e572ebef163c5299823b4027986f133dfb/fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c7502d6f54cd08024c3ea9b3514e2d6f190feb2f46e6dbcd3747882264bb5f7b", size = 262069, upload-time = "2025-10-19T22:43:38.38Z" }, - { url = "https://files.pythonhosted.org/packages/da/6c/a1a24f73574ac995482b1326cf7ab41301af0fabaa3e37eeb6b3df00e6e2/fastuuid-0.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ca61b592120cf314cfd66e662a5b54a578c5a15b26305e1b8b618a6f22df714", size = 251543, upload-time = "2025-10-19T22:32:22.537Z" }, - { url = "https://files.pythonhosted.org/packages/1a/20/2a9b59185ba7a6c7b37808431477c2d739fcbdabbf63e00243e37bd6bf49/fastuuid-0.14.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa75b6657ec129d0abded3bec745e6f7ab642e6dba3a5272a68247e85f5f316f", size = 277798, upload-time = "2025-10-19T22:33:53.821Z" }, - { url = "https://files.pythonhosted.org/packages/ef/33/4105ca574f6ded0af6a797d39add041bcfb468a1255fbbe82fcb6f592da2/fastuuid-0.14.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8a0dfea3972200f72d4c7df02c8ac70bad1bb4c58d7e0ec1e6f341679073a7f", size = 278283, upload-time = "2025-10-19T22:29:02.812Z" }, - { url = "https://files.pythonhosted.org/packages/fe/8c/fca59f8e21c4deb013f574eae05723737ddb1d2937ce87cb2a5d20992dc3/fastuuid-0.14.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1bf539a7a95f35b419f9ad105d5a8a35036df35fdafae48fb2fd2e5f318f0d75", size = 301627, upload-time = "2025-10-19T22:35:54.985Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e2/f78c271b909c034d429218f2798ca4e89eeda7983f4257d7865976ddbb6c/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:9a133bf9cc78fdbd1179cb58a59ad0100aa32d8675508150f3658814aeefeaa4", size = 459778, upload-time = "2025-10-19T22:28:00.999Z" }, - { url = "https://files.pythonhosted.org/packages/1e/f0/5ff209d865897667a2ff3e7a572267a9ced8f7313919f6d6043aed8b1caa/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_i686.whl", hash = "sha256:f54d5b36c56a2d5e1a31e73b950b28a0d83eb0c37b91d10408875a5a29494bad", size = 478605, upload-time = "2025-10-19T22:36:21.764Z" }, - { url = "https://files.pythonhosted.org/packages/e0/c8/2ce1c78f983a2c4987ea865d9516dbdfb141a120fd3abb977ae6f02ba7ca/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:ec27778c6ca3393ef662e2762dba8af13f4ec1aaa32d08d77f71f2a70ae9feb8", size = 450837, upload-time = "2025-10-19T22:34:37.178Z" }, - { url = "https://files.pythonhosted.org/packages/df/60/dad662ec9a33b4a5fe44f60699258da64172c39bd041da2994422cdc40fe/fastuuid-0.14.0-cp314-cp314-win32.whl", hash = "sha256:e23fc6a83f112de4be0cc1990e5b127c27663ae43f866353166f87df58e73d06", size = 154532, upload-time = "2025-10-19T22:35:18.217Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/da4db31001e854025ffd26bc9ba0740a9cbba2c3259695f7c5834908b336/fastuuid-0.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:df61342889d0f5e7a32f7284e55ef95103f2110fee433c2ae7c2c0956d76ac8a", size = 156457, upload-time = "2025-10-19T22:33:44.579Z" }, -] - -[[package]] -name = "filelock" -version = "3.20.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, -] - -[[package]] -name = "flatbuffers" -version = "25.12.19" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" }, -] - -[[package]] -name = "frozenlist" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, - { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, - { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, - { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, - { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, - { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, - { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, - { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, - { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, - { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, - { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, - { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, - { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, - { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, - { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, - { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, - { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, - { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, - { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, - { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, - { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, - { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, - { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, - { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, - { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, - { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, - { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, - { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, - { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, - { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, - { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, - { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, - { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, - { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, - { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, - { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, - { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, - { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, - { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, - { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, - { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, - { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, - { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, - { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, - { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, - { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, - { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, - { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, - { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, - { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, - { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, - { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, - { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, - { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, - { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, - { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, - { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, - { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, - { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, - { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, - { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, - { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, - { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, -] - -[[package]] -name = "fsspec" -version = "2026.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d5/7d/5df2650c57d47c57232af5ef4b4fdbff182070421e405e0d62c6cdbfaa87/fsspec-2026.1.0.tar.gz", hash = "sha256:e987cb0496a0d81bba3a9d1cee62922fb395e7d4c3b575e57f547953334fe07b", size = 310496, upload-time = "2026-01-09T15:21:35.562Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/c9/97cc5aae1648dcb851958a3ddf73ccd7dbe5650d95203ecb4d7720b4cdbf/fsspec-2026.1.0-py3-none-any.whl", hash = "sha256:cb76aa913c2285a3b49bdd5fc55b1d7c708d7208126b60f2eb8194fe1b4cbdcc", size = 201838, upload-time = "2026-01-09T15:21:34.041Z" }, -] - -[[package]] -name = "ghp-import" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, -] - -[[package]] -name = "googleapis-common-protos" -version = "1.72.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e5/7b/adfd75544c415c487b33061fe7ae526165241c1ea133f9a9125a56b39fd8/googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5", size = 147433, upload-time = "2025-11-06T18:29:24.087Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515, upload-time = "2025-11-06T18:29:13.14Z" }, -] - -[[package]] -name = "greenlet" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/99/1cd3411c56a410994669062bd73dd58270c00cc074cac15f385a1fd91f8a/greenlet-3.3.1.tar.gz", hash = "sha256:41848f3230b58c08bb43dee542e74a2a2e34d3c59dc3076cec9151aeeedcae98", size = 184690, upload-time = "2026-01-23T15:31:02.076Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/c8/9d76a66421d1ae24340dfae7e79c313957f6e3195c144d2c73333b5bfe34/greenlet-3.3.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:7e806ca53acf6d15a888405880766ec84721aa4181261cd11a457dfe9a7a4975", size = 276443, upload-time = "2026-01-23T15:30:10.066Z" }, - { url = "https://files.pythonhosted.org/packages/81/99/401ff34bb3c032d1f10477d199724f5e5f6fbfb59816ad1455c79c1eb8e7/greenlet-3.3.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d842c94b9155f1c9b3058036c24ffb8ff78b428414a19792b2380be9cecf4f36", size = 597359, upload-time = "2026-01-23T16:00:57.394Z" }, - { url = "https://files.pythonhosted.org/packages/2b/bc/4dcc0871ed557792d304f50be0f7487a14e017952ec689effe2180a6ff35/greenlet-3.3.1-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:20fedaadd422fa02695f82093f9a98bad3dab5fcda793c658b945fcde2ab27ba", size = 607805, upload-time = "2026-01-23T16:05:28.068Z" }, - { url = "https://files.pythonhosted.org/packages/3b/cd/7a7ca57588dac3389e97f7c9521cb6641fd8b6602faf1eaa4188384757df/greenlet-3.3.1-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c620051669fd04ac6b60ebc70478210119c56e2d5d5df848baec4312e260e4ca", size = 622363, upload-time = "2026-01-23T16:15:54.754Z" }, - { url = "https://files.pythonhosted.org/packages/cf/05/821587cf19e2ce1f2b24945d890b164401e5085f9d09cbd969b0c193cd20/greenlet-3.3.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14194f5f4305800ff329cbf02c5fcc88f01886cadd29941b807668a45f0d2336", size = 609947, upload-time = "2026-01-23T15:32:51.004Z" }, - { url = "https://files.pythonhosted.org/packages/a4/52/ee8c46ed9f8babaa93a19e577f26e3d28a519feac6350ed6f25f1afee7e9/greenlet-3.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7b2fe4150a0cf59f847a67db8c155ac36aed89080a6a639e9f16df5d6c6096f1", size = 1567487, upload-time = "2026-01-23T16:04:22.125Z" }, - { url = "https://files.pythonhosted.org/packages/8f/7c/456a74f07029597626f3a6db71b273a3632aecb9afafeeca452cfa633197/greenlet-3.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:49f4ad195d45f4a66a0eb9c1ba4832bb380570d361912fa3554746830d332149", size = 1636087, upload-time = "2026-01-23T15:33:47.486Z" }, - { url = "https://files.pythonhosted.org/packages/34/2f/5e0e41f33c69655300a5e54aeb637cf8ff57f1786a3aba374eacc0228c1d/greenlet-3.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:cc98b9c4e4870fa983436afa999d4eb16b12872fab7071423d5262fa7120d57a", size = 227156, upload-time = "2026-01-23T15:34:34.808Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ab/717c58343cf02c5265b531384b248787e04d8160b8afe53d9eec053d7b44/greenlet-3.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:bfb2d1763d777de5ee495c85309460f6fd8146e50ec9d0ae0183dbf6f0a829d1", size = 226403, upload-time = "2026-01-23T15:31:39.372Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ab/d26750f2b7242c2b90ea2ad71de70cfcd73a948a49513188a0fc0d6fc15a/greenlet-3.3.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:7ab327905cabb0622adca5971e488064e35115430cec2c35a50fd36e72a315b3", size = 275205, upload-time = "2026-01-23T15:30:24.556Z" }, - { url = "https://files.pythonhosted.org/packages/10/d3/be7d19e8fad7c5a78eeefb2d896a08cd4643e1e90c605c4be3b46264998f/greenlet-3.3.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65be2f026ca6a176f88fb935ee23c18333ccea97048076aef4db1ef5bc0713ac", size = 599284, upload-time = "2026-01-23T16:00:58.584Z" }, - { url = "https://files.pythonhosted.org/packages/ae/21/fe703aaa056fdb0f17e5afd4b5c80195bbdab701208918938bd15b00d39b/greenlet-3.3.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7a3ae05b3d225b4155bda56b072ceb09d05e974bc74be6c3fc15463cf69f33fd", size = 610274, upload-time = "2026-01-23T16:05:29.312Z" }, - { url = "https://files.pythonhosted.org/packages/06/00/95df0b6a935103c0452dad2203f5be8377e551b8466a29650c4c5a5af6cc/greenlet-3.3.1-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:12184c61e5d64268a160226fb4818af4df02cfead8379d7f8b99a56c3a54ff3e", size = 624375, upload-time = "2026-01-23T16:15:55.915Z" }, - { url = "https://files.pythonhosted.org/packages/cb/86/5c6ab23bb3c28c21ed6bebad006515cfe08b04613eb105ca0041fecca852/greenlet-3.3.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6423481193bbbe871313de5fd06a082f2649e7ce6e08015d2a76c1e9186ca5b3", size = 612904, upload-time = "2026-01-23T15:32:52.317Z" }, - { url = "https://files.pythonhosted.org/packages/c2/f3/7949994264e22639e40718c2daf6f6df5169bf48fb038c008a489ec53a50/greenlet-3.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:33a956fe78bbbda82bfc95e128d61129b32d66bcf0a20a1f0c08aa4839ffa951", size = 1567316, upload-time = "2026-01-23T16:04:23.316Z" }, - { url = "https://files.pythonhosted.org/packages/8d/6e/d73c94d13b6465e9f7cd6231c68abde838bb22408596c05d9059830b7872/greenlet-3.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b065d3284be43728dd280f6f9a13990b56470b81be20375a207cdc814a983f2", size = 1636549, upload-time = "2026-01-23T15:33:48.643Z" }, - { url = "https://files.pythonhosted.org/packages/5e/b3/c9c23a6478b3bcc91f979ce4ca50879e4d0b2bd7b9a53d8ecded719b92e2/greenlet-3.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:27289986f4e5b0edec7b5a91063c109f0276abb09a7e9bdab08437525977c946", size = 227042, upload-time = "2026-01-23T15:33:58.216Z" }, - { url = "https://files.pythonhosted.org/packages/90/e7/824beda656097edee36ab15809fd063447b200cc03a7f6a24c34d520bc88/greenlet-3.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:2f080e028001c5273e0b42690eaf359aeef9cb1389da0f171ea51a5dc3c7608d", size = 226294, upload-time = "2026-01-23T15:30:52.73Z" }, - { url = "https://files.pythonhosted.org/packages/ae/fb/011c7c717213182caf78084a9bea51c8590b0afda98001f69d9f853a495b/greenlet-3.3.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:bd59acd8529b372775cd0fcbc5f420ae20681c5b045ce25bd453ed8455ab99b5", size = 275737, upload-time = "2026-01-23T15:32:16.889Z" }, - { url = "https://files.pythonhosted.org/packages/41/2e/a3a417d620363fdbb08a48b1dd582956a46a61bf8fd27ee8164f9dfe87c2/greenlet-3.3.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b31c05dd84ef6871dd47120386aed35323c944d86c3d91a17c4b8d23df62f15b", size = 646422, upload-time = "2026-01-23T16:01:00.354Z" }, - { url = "https://files.pythonhosted.org/packages/b4/09/c6c4a0db47defafd2d6bab8ddfe47ad19963b4e30f5bed84d75328059f8c/greenlet-3.3.1-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:02925a0bfffc41e542c70aa14c7eda3593e4d7e274bfcccca1827e6c0875902e", size = 658219, upload-time = "2026-01-23T16:05:30.956Z" }, - { url = "https://files.pythonhosted.org/packages/e2/89/b95f2ddcc5f3c2bc09c8ee8d77be312df7f9e7175703ab780f2014a0e781/greenlet-3.3.1-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3e0f3878ca3a3ff63ab4ea478585942b53df66ddde327b59ecb191b19dbbd62d", size = 671455, upload-time = "2026-01-23T16:15:57.232Z" }, - { url = "https://files.pythonhosted.org/packages/80/38/9d42d60dffb04b45f03dbab9430898352dba277758640751dc5cc316c521/greenlet-3.3.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34a729e2e4e4ffe9ae2408d5ecaf12f944853f40ad724929b7585bca808a9d6f", size = 660237, upload-time = "2026-01-23T15:32:53.967Z" }, - { url = "https://files.pythonhosted.org/packages/96/61/373c30b7197f9e756e4c81ae90a8d55dc3598c17673f91f4d31c3c689c3f/greenlet-3.3.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:aec9ab04e82918e623415947921dea15851b152b822661cce3f8e4393c3df683", size = 1615261, upload-time = "2026-01-23T16:04:25.066Z" }, - { url = "https://files.pythonhosted.org/packages/fd/d3/ca534310343f5945316f9451e953dcd89b36fe7a19de652a1dc5a0eeef3f/greenlet-3.3.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:71c767cf281a80d02b6c1bdc41c9468e1f5a494fb11bc8688c360524e273d7b1", size = 1683719, upload-time = "2026-01-23T15:33:50.61Z" }, - { url = "https://files.pythonhosted.org/packages/52/cb/c21a3fd5d2c9c8b622e7bede6d6d00e00551a5ee474ea6d831b5f567a8b4/greenlet-3.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:96aff77af063b607f2489473484e39a0bbae730f2ea90c9e5606c9b73c44174a", size = 228125, upload-time = "2026-01-23T15:32:45.265Z" }, - { url = "https://files.pythonhosted.org/packages/6a/8e/8a2db6d11491837af1de64b8aff23707c6e85241be13c60ed399a72e2ef8/greenlet-3.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:b066e8b50e28b503f604fa538adc764a638b38cf8e81e025011d26e8a627fa79", size = 227519, upload-time = "2026-01-23T15:31:47.284Z" }, - { url = "https://files.pythonhosted.org/packages/28/24/cbbec49bacdcc9ec652a81d3efef7b59f326697e7edf6ed775a5e08e54c2/greenlet-3.3.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:3e63252943c921b90abb035ebe9de832c436401d9c45f262d80e2d06cc659242", size = 282706, upload-time = "2026-01-23T15:33:05.525Z" }, - { url = "https://files.pythonhosted.org/packages/86/2e/4f2b9323c144c4fe8842a4e0d92121465485c3c2c5b9e9b30a52e80f523f/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76e39058e68eb125de10c92524573924e827927df5d3891fbc97bd55764a8774", size = 651209, upload-time = "2026-01-23T16:01:01.517Z" }, - { url = "https://files.pythonhosted.org/packages/d9/87/50ca60e515f5bb55a2fbc5f0c9b5b156de7d2fc51a0a69abc9d23914a237/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9f9d5e7a9310b7a2f416dd13d2e3fd8b42d803968ea580b7c0f322ccb389b97", size = 654300, upload-time = "2026-01-23T16:05:32.199Z" }, - { url = "https://files.pythonhosted.org/packages/7c/25/c51a63f3f463171e09cb586eb64db0861eb06667ab01a7968371a24c4f3b/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b9721549a95db96689458a1e0ae32412ca18776ed004463df3a9299c1b257ab", size = 662574, upload-time = "2026-01-23T16:15:58.364Z" }, - { url = "https://files.pythonhosted.org/packages/1d/94/74310866dfa2b73dd08659a3d18762f83985ad3281901ba0ee9a815194fb/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92497c78adf3ac703b57f1e3813c2d874f27f71a178f9ea5887855da413cd6d2", size = 653842, upload-time = "2026-01-23T15:32:55.671Z" }, - { url = "https://files.pythonhosted.org/packages/97/43/8bf0ffa3d498eeee4c58c212a3905dd6146c01c8dc0b0a046481ca29b18c/greenlet-3.3.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ed6b402bc74d6557a705e197d47f9063733091ed6357b3de33619d8a8d93ac53", size = 1614917, upload-time = "2026-01-23T16:04:26.276Z" }, - { url = "https://files.pythonhosted.org/packages/89/90/a3be7a5f378fc6e84abe4dcfb2ba32b07786861172e502388b4c90000d1b/greenlet-3.3.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:59913f1e5ada20fde795ba906916aea25d442abcc0593fba7e26c92b7ad76249", size = 1676092, upload-time = "2026-01-23T15:33:52.176Z" }, - { url = "https://files.pythonhosted.org/packages/e1/2b/98c7f93e6db9977aaee07eb1e51ca63bd5f779b900d362791d3252e60558/greenlet-3.3.1-cp314-cp314t-win_amd64.whl", hash = "sha256:301860987846c24cb8964bdec0e31a96ad4a2a801b41b4ef40963c1b44f33451", size = 233181, upload-time = "2026-01-23T15:33:00.29Z" }, -] - -[[package]] -name = "griffe" -version = "1.15.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0d/0c/3a471b6e31951dce2360477420d0a8d1e00dea6cf33b70f3e8c3ab6e28e1/griffe-1.15.0.tar.gz", hash = "sha256:7726e3afd6f298fbc3696e67958803e7ac843c1cfe59734b6251a40cdbfb5eea", size = 424112, upload-time = "2025-11-10T15:03:15.52Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.0-py3-none-any.whl", hash = "sha256:6f6762661949411031f5fcda9593f586e6ce8340f0ba88921a0f2ef7a81eb9a3", size = 150705, upload-time = "2025-11-10T15:03:13.549Z" }, -] - -[[package]] -name = "grpcio" -version = "1.76.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b6/e0/318c1ce3ae5a17894d5791e87aea147587c9e702f24122cc7a5c8bbaeeb1/grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73", size = 12785182, upload-time = "2025-10-21T16:23:12.106Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/05/8e29121994b8d959ffa0afd28996d452f291b48cfc0875619de0bde2c50c/grpcio-1.76.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:81fd9652b37b36f16138611c7e884eb82e0cec137c40d3ef7c3f9b3ed00f6ed8", size = 5799718, upload-time = "2025-10-21T16:21:17.939Z" }, - { url = "https://files.pythonhosted.org/packages/d9/75/11d0e66b3cdf998c996489581bdad8900db79ebd83513e45c19548f1cba4/grpcio-1.76.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:04bbe1bfe3a68bbfd4e52402ab7d4eb59d72d02647ae2042204326cf4bbad280", size = 11825627, upload-time = "2025-10-21T16:21:20.466Z" }, - { url = "https://files.pythonhosted.org/packages/28/50/2f0aa0498bc188048f5d9504dcc5c2c24f2eb1a9337cd0fa09a61a2e75f0/grpcio-1.76.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d388087771c837cdb6515539f43b9d4bf0b0f23593a24054ac16f7a960be16f4", size = 6359167, upload-time = "2025-10-21T16:21:23.122Z" }, - { url = "https://files.pythonhosted.org/packages/66/e5/bbf0bb97d29ede1d59d6588af40018cfc345b17ce979b7b45424628dc8bb/grpcio-1.76.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f8f757bebaaea112c00dba718fc0d3260052ce714e25804a03f93f5d1c6cc11", size = 7044267, upload-time = "2025-10-21T16:21:25.995Z" }, - { url = "https://files.pythonhosted.org/packages/f5/86/f6ec2164f743d9609691115ae8ece098c76b894ebe4f7c94a655c6b03e98/grpcio-1.76.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:980a846182ce88c4f2f7e2c22c56aefd515daeb36149d1c897f83cf57999e0b6", size = 6573963, upload-time = "2025-10-21T16:21:28.631Z" }, - { url = "https://files.pythonhosted.org/packages/60/bc/8d9d0d8505feccfdf38a766d262c71e73639c165b311c9457208b56d92ae/grpcio-1.76.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f92f88e6c033db65a5ae3d97905c8fea9c725b63e28d5a75cb73b49bda5024d8", size = 7164484, upload-time = "2025-10-21T16:21:30.837Z" }, - { url = "https://files.pythonhosted.org/packages/67/e6/5d6c2fc10b95edf6df9b8f19cf10a34263b7fd48493936fffd5085521292/grpcio-1.76.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4baf3cbe2f0be3289eb68ac8ae771156971848bb8aaff60bad42005539431980", size = 8127777, upload-time = "2025-10-21T16:21:33.577Z" }, - { url = "https://files.pythonhosted.org/packages/3f/c8/dce8ff21c86abe025efe304d9e31fdb0deaaa3b502b6a78141080f206da0/grpcio-1.76.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:615ba64c208aaceb5ec83bfdce7728b80bfeb8be97562944836a7a0a9647d882", size = 7594014, upload-time = "2025-10-21T16:21:41.882Z" }, - { url = "https://files.pythonhosted.org/packages/e0/42/ad28191ebf983a5d0ecef90bab66baa5a6b18f2bfdef9d0a63b1973d9f75/grpcio-1.76.0-cp312-cp312-win32.whl", hash = "sha256:45d59a649a82df5718fd9527ce775fd66d1af35e6d31abdcdc906a49c6822958", size = 3984750, upload-time = "2025-10-21T16:21:44.006Z" }, - { url = "https://files.pythonhosted.org/packages/9e/00/7bd478cbb851c04a48baccaa49b75abaa8e4122f7d86da797500cccdd771/grpcio-1.76.0-cp312-cp312-win_amd64.whl", hash = "sha256:c088e7a90b6017307f423efbb9d1ba97a22aa2170876223f9709e9d1de0b5347", size = 4704003, upload-time = "2025-10-21T16:21:46.244Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ed/71467ab770effc9e8cef5f2e7388beb2be26ed642d567697bb103a790c72/grpcio-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:26ef06c73eb53267c2b319f43e6634c7556ea37672029241a056629af27c10e2", size = 5807716, upload-time = "2025-10-21T16:21:48.475Z" }, - { url = "https://files.pythonhosted.org/packages/2c/85/c6ed56f9817fab03fa8a111ca91469941fb514e3e3ce6d793cb8f1e1347b/grpcio-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:45e0111e73f43f735d70786557dc38141185072d7ff8dc1829d6a77ac1471468", size = 11821522, upload-time = "2025-10-21T16:21:51.142Z" }, - { url = "https://files.pythonhosted.org/packages/ac/31/2b8a235ab40c39cbc141ef647f8a6eb7b0028f023015a4842933bc0d6831/grpcio-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83d57312a58dcfe2a3a0f9d1389b299438909a02db60e2f2ea2ae2d8034909d3", size = 6362558, upload-time = "2025-10-21T16:21:54.213Z" }, - { url = "https://files.pythonhosted.org/packages/bd/64/9784eab483358e08847498ee56faf8ff6ea8e0a4592568d9f68edc97e9e9/grpcio-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3e2a27c89eb9ac3d81ec8835e12414d73536c6e620355d65102503064a4ed6eb", size = 7049990, upload-time = "2025-10-21T16:21:56.476Z" }, - { url = "https://files.pythonhosted.org/packages/2b/94/8c12319a6369434e7a184b987e8e9f3b49a114c489b8315f029e24de4837/grpcio-1.76.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61f69297cba3950a524f61c7c8ee12e55c486cb5f7db47ff9dcee33da6f0d3ae", size = 6575387, upload-time = "2025-10-21T16:21:59.051Z" }, - { url = "https://files.pythonhosted.org/packages/15/0f/f12c32b03f731f4a6242f771f63039df182c8b8e2cf8075b245b409259d4/grpcio-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a15c17af8839b6801d554263c546c69c4d7718ad4321e3166175b37eaacca77", size = 7166668, upload-time = "2025-10-21T16:22:02.049Z" }, - { url = "https://files.pythonhosted.org/packages/ff/2d/3ec9ce0c2b1d92dd59d1c3264aaec9f0f7c817d6e8ac683b97198a36ed5a/grpcio-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25a18e9810fbc7e7f03ec2516addc116a957f8cbb8cbc95ccc80faa072743d03", size = 8124928, upload-time = "2025-10-21T16:22:04.984Z" }, - { url = "https://files.pythonhosted.org/packages/1a/74/fd3317be5672f4856bcdd1a9e7b5e17554692d3db9a3b273879dc02d657d/grpcio-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:931091142fd8cc14edccc0845a79248bc155425eee9a98b2db2ea4f00a235a42", size = 7589983, upload-time = "2025-10-21T16:22:07.881Z" }, - { url = "https://files.pythonhosted.org/packages/45/bb/ca038cf420f405971f19821c8c15bcbc875505f6ffadafe9ffd77871dc4c/grpcio-1.76.0-cp313-cp313-win32.whl", hash = "sha256:5e8571632780e08526f118f74170ad8d50fb0a48c23a746bef2a6ebade3abd6f", size = 3984727, upload-time = "2025-10-21T16:22:10.032Z" }, - { url = "https://files.pythonhosted.org/packages/41/80/84087dc56437ced7cdd4b13d7875e7439a52a261e3ab4e06488ba6173b0a/grpcio-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:f9f7bd5faab55f47231ad8dba7787866b69f5e93bc306e3915606779bbfb4ba8", size = 4702799, upload-time = "2025-10-21T16:22:12.709Z" }, - { url = "https://files.pythonhosted.org/packages/b4/46/39adac80de49d678e6e073b70204091e76631e03e94928b9ea4ecf0f6e0e/grpcio-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:ff8a59ea85a1f2191a0ffcc61298c571bc566332f82e5f5be1b83c9d8e668a62", size = 5808417, upload-time = "2025-10-21T16:22:15.02Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f5/a4531f7fb8b4e2a60b94e39d5d924469b7a6988176b3422487be61fe2998/grpcio-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06c3d6b076e7b593905d04fdba6a0525711b3466f43b3400266f04ff735de0cd", size = 11828219, upload-time = "2025-10-21T16:22:17.954Z" }, - { url = "https://files.pythonhosted.org/packages/4b/1c/de55d868ed7a8bd6acc6b1d6ddc4aa36d07a9f31d33c912c804adb1b971b/grpcio-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd5ef5932f6475c436c4a55e4336ebbe47bd3272be04964a03d316bbf4afbcbc", size = 6367826, upload-time = "2025-10-21T16:22:20.721Z" }, - { url = "https://files.pythonhosted.org/packages/59/64/99e44c02b5adb0ad13ab3adc89cb33cb54bfa90c74770f2607eea629b86f/grpcio-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b331680e46239e090f5b3cead313cc772f6caa7d0fc8de349337563125361a4a", size = 7049550, upload-time = "2025-10-21T16:22:23.637Z" }, - { url = "https://files.pythonhosted.org/packages/43/28/40a5be3f9a86949b83e7d6a2ad6011d993cbe9b6bd27bea881f61c7788b6/grpcio-1.76.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2229ae655ec4e8999599469559e97630185fdd53ae1e8997d147b7c9b2b72cba", size = 6575564, upload-time = "2025-10-21T16:22:26.016Z" }, - { url = "https://files.pythonhosted.org/packages/4b/a9/1be18e6055b64467440208a8559afac243c66a8b904213af6f392dc2212f/grpcio-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:490fa6d203992c47c7b9e4a9d39003a0c2bcc1c9aa3c058730884bbbb0ee9f09", size = 7176236, upload-time = "2025-10-21T16:22:28.362Z" }, - { url = "https://files.pythonhosted.org/packages/0f/55/dba05d3fcc151ce6e81327541d2cc8394f442f6b350fead67401661bf041/grpcio-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:479496325ce554792dba6548fae3df31a72cef7bad71ca2e12b0e58f9b336bfc", size = 8125795, upload-time = "2025-10-21T16:22:31.075Z" }, - { url = "https://files.pythonhosted.org/packages/4a/45/122df922d05655f63930cf42c9e3f72ba20aadb26c100ee105cad4ce4257/grpcio-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c9b93f79f48b03ada57ea24725d83a30284a012ec27eab2cf7e50a550cbbbcc", size = 7592214, upload-time = "2025-10-21T16:22:33.831Z" }, - { url = "https://files.pythonhosted.org/packages/4a/6e/0b899b7f6b66e5af39e377055fb4a6675c9ee28431df5708139df2e93233/grpcio-1.76.0-cp314-cp314-win32.whl", hash = "sha256:747fa73efa9b8b1488a95d0ba1039c8e2dca0f741612d80415b1e1c560febf4e", size = 4062961, upload-time = "2025-10-21T16:22:36.468Z" }, - { url = "https://files.pythonhosted.org/packages/19/41/0b430b01a2eb38ee887f88c1f07644a1df8e289353b78e82b37ef988fb64/grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e", size = 4834462, upload-time = "2025-10-21T16:22:39.772Z" }, -] - -[[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 = "hanzo-agent" -version = "0.0.4" -source = { editable = "." } -dependencies = [ - { name = "griffe" }, - { name = "hanzo-async" }, - { name = "hanzo-memory" }, - { name = "numpy" }, - { name = "openai" }, - { name = "pydantic" }, - { name = "requests" }, - { name = "types-requests" }, - { name = "typing-extensions" }, -] - -[package.optional-dependencies] -cli = [ - { name = "click" }, - { name = "rich" }, -] -full = [ - { name = "hanzoai" }, -] -marketplace = [ - { name = "aiohttp" }, - { name = "redis" }, -] -tee = [ - { name = "cryptography" }, -] -web3 = [ - { name = "eth-account" }, - { name = "eth-utils" }, - { name = "web3" }, -] - -[package.dev-dependencies] -dev = [ - { name = "coverage" }, - { name = "mkdocs" }, - { name = "mkdocs-material" }, - { name = "mkdocstrings", extra = ["python"] }, - { name = "mypy" }, - { name = "playwright" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-mock" }, - { name = "rich" }, - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "aiohttp", marker = "extra == 'marketplace'", specifier = ">=3.9.0,<4" }, - { name = "click", marker = "extra == 'cli'", specifier = ">=8.1.0,<9" }, - { name = "cryptography", marker = "extra == 'tee'", specifier = ">=41.0.0,<50" }, - { name = "eth-account", marker = "extra == 'web3'", specifier = ">=0.10.0,<1" }, - { name = "eth-utils", marker = "extra == 'web3'", specifier = ">=2.0.0,<5" }, - { name = "griffe", specifier = ">=1.5.6,<2" }, - { name = "hanzo-async", specifier = ">=0.1.0" }, - { name = "hanzo-memory", specifier = ">=1.0.0" }, - { name = "hanzoai", extras = ["web3", "tee", "marketplace", "cli"], marker = "extra == 'full'" }, - { name = "numpy", specifier = ">=1.24.0" }, - { name = "openai", specifier = ">=1.66.2" }, - { name = "pydantic", specifier = ">=2.10,<3" }, - { name = "redis", marker = "extra == 'marketplace'", specifier = ">=5.0.0,<6" }, - { name = "requests", specifier = ">=2.0,<3" }, - { name = "rich", marker = "extra == 'cli'", specifier = ">=13.0.0,<14" }, - { name = "types-requests", specifier = ">=2.0,<3" }, - { name = "typing-extensions", specifier = ">=4.12.2,<5" }, - { name = "web3", marker = "extra == 'web3'", specifier = ">=6.0.0,<7" }, -] -provides-extras = ["web3", "tee", "marketplace", "cli", "full"] - -[package.metadata.requires-dev] -dev = [ - { name = "coverage", specifier = ">=7.6.12" }, - { name = "mkdocs", specifier = ">=1.6.0" }, - { name = "mkdocs-material", specifier = ">=9.6.0" }, - { name = "mkdocstrings", extras = ["python"], specifier = ">=0.28.0" }, - { name = "mypy" }, - { name = "playwright", specifier = "==1.50.0" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-mock", specifier = ">=3.14.0" }, - { name = "rich" }, - { name = "ruff", specifier = "==0.9.2" }, -] - -[[package]] -name = "hanzo-async" -version = "0.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiofiles" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/be/1b/dcd448eac4461973442bc20dd25189360e85ed7ae1c78fafbfce6d88ffc2/hanzo_async-0.1.1.tar.gz", hash = "sha256:05d41974823d27d3557db791705095a49f1cefc901c6ec686120bb9bbd85f0ee", size = 8619, upload-time = "2026-01-05T01:19:19.37Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/70/363dac59048d70653ce0d07ff37d5521f49b5b6ce30d6aba8c31bf31154f/hanzo_async-0.1.1-py3-none-any.whl", hash = "sha256:8f551b7b57e96b4f4c5b7e05d7781eb154a7a788c824c7a36bd69f607532cda2", size = 8649, upload-time = "2026-01-05T01:19:18.268Z" }, -] - -[[package]] -name = "hanzo-memory" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiocache" }, - { name = "chromadb" }, - { name = "fastapi" }, - { name = "fastembed" }, - { name = "httpx" }, - { name = "lancedb" }, - { name = "litellm" }, - { name = "mcp" }, - { name = "numpy" }, - { name = "orjson" }, - { name = "passlib", extra = ["bcrypt"] }, - { name = "polars" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "python-jose", extra = ["cryptography"] }, - { name = "python-multipart" }, - { name = "redis" }, - { name = "rich" }, - { name = "scikit-learn" }, - { name = "sentence-transformers" }, - { name = "structlog" }, - { name = "tenacity" }, - { name = "tiktoken" }, - { name = "uvicorn", extra = ["standard"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9c/8a/9cbcff33dcbc3c05d8de53120c4b039bf5d41dfcf0d73c3846c15dcba936/hanzo_memory-1.0.1.tar.gz", hash = "sha256:8b31210c967cfb5fe2b109d804f67017bb2771dec508543e2de02563e898c3f0", size = 44623, upload-time = "2025-09-17T22:11:52.775Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/12/223b4c3a0c3fe336421a1b7dc4345bd6ef2d1b9fe1db2abe6de896aa0f44/hanzo_memory-1.0.1-py3-none-any.whl", hash = "sha256:fd0fea34a63d38b7e5dcc83bff23086d295bec8b0cec4c449d6338a8d0a17f09", size = 40932, upload-time = "2025-09-17T22:11:51.316Z" }, -] - -[[package]] -name = "hanzoai" -version = "2.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "h11" }, - { name = "httpx" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "typing-extensions" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/dd/705e537428e4bdfcd325f42824c4db7e545328bafa24b7cdec62e9606e11/hanzoai-2.1.2.tar.gz", hash = "sha256:7afce7ac7eba44f4e3afacedbbd98db8ba72b16bb1c8eaeefb0542fd4eb8d614", size = 464589, upload-time = "2026-01-21T03:10:10.779Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/0f/4baa3dbd424686f348d204114498ab2b7c11f7eb5acebe5c864ec83c289b/hanzoai-2.1.2-py3-none-any.whl", hash = "sha256:0d0e4d1b458e9d94daec5a9475c405748067b6023297f8d7143a9d5278cc0055", size = 386563, upload-time = "2026-01-21T03:10:09.143Z" }, -] - -[[package]] -name = "hexbytes" -version = "0.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c1/94/fbfd526e8964652eec6a7b74ae18d1426e225ab602553858531ec6567d05/hexbytes-0.3.1.tar.gz", hash = "sha256:a3fe35c6831ee8fafd048c4c086b986075fc14fd46258fa24ecb8d65745f9a9d", size = 6188, upload-time = "2023-06-08T20:36:59.73Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/9e/fdfe374c28d448a58563e7e43f569f8cf8cf600db092efac2e8ac2f86782/hexbytes-0.3.1-py3-none-any.whl", hash = "sha256:383595ad75026cf00abd570f44b368c6cdac0c6becfae5c39ff88829877f8a59", size = 5944, upload-time = "2023-06-08T20:36:58.066Z" }, -] - -[[package]] -name = "hf-xet" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020, upload-time = "2025-10-24T19:04:32.129Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/a5/85ef910a0aa034a2abcfadc360ab5ac6f6bc4e9112349bd40ca97551cff0/hf_xet-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:ceeefcd1b7aed4956ae8499e2199607765fbd1c60510752003b6cc0b8413b649", size = 2861870, upload-time = "2025-10-24T19:04:11.422Z" }, - { url = "https://files.pythonhosted.org/packages/ea/40/e2e0a7eb9a51fe8828ba2d47fe22a7e74914ea8a0db68a18c3aa7449c767/hf_xet-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b70218dd548e9840224df5638fdc94bd033552963cfa97f9170829381179c813", size = 2717584, upload-time = "2025-10-24T19:04:09.586Z" }, - { url = "https://files.pythonhosted.org/packages/a5/7d/daf7f8bc4594fdd59a8a596f9e3886133fdc68e675292218a5e4c1b7e834/hf_xet-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d40b18769bb9a8bc82a9ede575ce1a44c75eb80e7375a01d76259089529b5dc", size = 3315004, upload-time = "2025-10-24T19:04:00.314Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ba/45ea2f605fbf6d81c8b21e4d970b168b18a53515923010c312c06cd83164/hf_xet-1.2.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd3a6027d59cfb60177c12d6424e31f4b5ff13d8e3a1247b3a584bf8977e6df5", size = 3222636, upload-time = "2025-10-24T19:03:58.111Z" }, - { url = "https://files.pythonhosted.org/packages/4a/1d/04513e3cab8f29ab8c109d309ddd21a2705afab9d52f2ba1151e0c14f086/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6de1fc44f58f6dd937956c8d304d8c2dea264c80680bcfa61ca4a15e7b76780f", size = 3408448, upload-time = "2025-10-24T19:04:20.951Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7c/60a2756d7feec7387db3a1176c632357632fbe7849fce576c5559d4520c7/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f182f264ed2acd566c514e45da9f2119110e48a87a327ca271027904c70c5832", size = 3503401, upload-time = "2025-10-24T19:04:22.549Z" }, - { url = "https://files.pythonhosted.org/packages/4e/64/48fffbd67fb418ab07451e4ce641a70de1c40c10a13e25325e24858ebe5a/hf_xet-1.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:293a7a3787e5c95d7be1857358a9130694a9c6021de3f27fa233f37267174382", size = 2900866, upload-time = "2025-10-24T19:04:33.461Z" }, - { url = "https://files.pythonhosted.org/packages/e2/51/f7e2caae42f80af886db414d4e9885fac959330509089f97cccb339c6b87/hf_xet-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:10bfab528b968c70e062607f663e21e34e2bba349e8038db546646875495179e", size = 2861861, upload-time = "2025-10-24T19:04:19.01Z" }, - { url = "https://files.pythonhosted.org/packages/6e/1d/a641a88b69994f9371bd347f1dd35e5d1e2e2460a2e350c8d5165fc62005/hf_xet-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a212e842647b02eb6a911187dc878e79c4aa0aa397e88dd3b26761676e8c1f8", size = 2717699, upload-time = "2025-10-24T19:04:17.306Z" }, - { url = "https://files.pythonhosted.org/packages/df/e0/e5e9bba7d15f0318955f7ec3f4af13f92e773fbb368c0b8008a5acbcb12f/hf_xet-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30e06daccb3a7d4c065f34fc26c14c74f4653069bb2b194e7f18f17cbe9939c0", size = 3314885, upload-time = "2025-10-24T19:04:07.642Z" }, - { url = "https://files.pythonhosted.org/packages/21/90/b7fe5ff6f2b7b8cbdf1bd56145f863c90a5807d9758a549bf3d916aa4dec/hf_xet-1.2.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:29c8fc913a529ec0a91867ce3d119ac1aac966e098cf49501800c870328cc090", size = 3221550, upload-time = "2025-10-24T19:04:05.55Z" }, - { url = "https://files.pythonhosted.org/packages/6f/cb/73f276f0a7ce46cc6a6ec7d6c7d61cbfe5f2e107123d9bbd0193c355f106/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e159cbfcfbb29f920db2c09ed8b660eb894640d284f102ada929b6e3dc410a", size = 3408010, upload-time = "2025-10-24T19:04:28.598Z" }, - { url = "https://files.pythonhosted.org/packages/b8/1e/d642a12caa78171f4be64f7cd9c40e3ca5279d055d0873188a58c0f5fbb9/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c91d5ae931510107f148874e9e2de8a16052b6f1b3ca3c1b12f15ccb491390f", size = 3503264, upload-time = "2025-10-24T19:04:30.397Z" }, - { url = "https://files.pythonhosted.org/packages/17/b5/33764714923fa1ff922770f7ed18c2daae034d21ae6e10dbf4347c854154/hf_xet-1.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:210d577732b519ac6ede149d2f2f34049d44e8622bf14eb3d63bbcd2d4b332dc", size = 2901071, upload-time = "2025-10-24T19:04:37.463Z" }, - { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099, upload-time = "2025-10-24T19:04:15.366Z" }, - { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178, upload-time = "2025-10-24T19:04:13.695Z" }, - { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214, upload-time = "2025-10-24T19:04:03.596Z" }, - { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054, upload-time = "2025-10-24T19:04:01.949Z" }, - { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812, upload-time = "2025-10-24T19:04:24.585Z" }, - { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920, upload-time = "2025-10-24T19:04:26.927Z" }, - { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735, upload-time = "2025-10-24T19:04:35.928Z" }, -] - -[[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 = "httptools" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/7f/403e5d787dc4942316e515e949b0c8a013d84078a915910e9f391ba9b3ed/httptools-0.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:38e0c83a2ea9746ebbd643bdfb521b9aa4a91703e2cd705c20443405d2fd16a5", size = 206280, upload-time = "2025-10-10T03:54:39.274Z" }, - { url = "https://files.pythonhosted.org/packages/2a/0d/7f3fd28e2ce311ccc998c388dd1c53b18120fda3b70ebb022b135dc9839b/httptools-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f25bbaf1235e27704f1a7b86cd3304eabc04f569c828101d94a0e605ef7205a5", size = 110004, upload-time = "2025-10-10T03:54:40.403Z" }, - { url = "https://files.pythonhosted.org/packages/84/a6/b3965e1e146ef5762870bbe76117876ceba51a201e18cc31f5703e454596/httptools-0.7.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c15f37ef679ab9ecc06bfc4e6e8628c32a8e4b305459de7cf6785acd57e4d03", size = 517655, upload-time = "2025-10-10T03:54:41.347Z" }, - { url = "https://files.pythonhosted.org/packages/11/7d/71fee6f1844e6fa378f2eddde6c3e41ce3a1fb4b2d81118dd544e3441ec0/httptools-0.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fe6e96090df46b36ccfaf746f03034e5ab723162bc51b0a4cf58305324036f2", size = 511440, upload-time = "2025-10-10T03:54:42.452Z" }, - { url = "https://files.pythonhosted.org/packages/22/a5/079d216712a4f3ffa24af4a0381b108aa9c45b7a5cc6eb141f81726b1823/httptools-0.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362", size = 495186, upload-time = "2025-10-10T03:54:43.937Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9e/025ad7b65278745dee3bd0ebf9314934c4592560878308a6121f7f812084/httptools-0.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e99c7b90a29fd82fea9ef57943d501a16f3404d7b9ee81799d41639bdaae412c", size = 499192, upload-time = "2025-10-10T03:54:45.003Z" }, - { url = "https://files.pythonhosted.org/packages/6d/de/40a8f202b987d43afc4d54689600ff03ce65680ede2f31df348d7f368b8f/httptools-0.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3e14f530fefa7499334a79b0cf7e7cd2992870eb893526fb097d51b4f2d0f321", size = 86694, upload-time = "2025-10-10T03:54:45.923Z" }, - { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889, upload-time = "2025-10-10T03:54:47.089Z" }, - { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180, upload-time = "2025-10-10T03:54:48.052Z" }, - { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596, upload-time = "2025-10-10T03:54:48.919Z" }, - { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268, upload-time = "2025-10-10T03:54:49.993Z" }, - { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517, upload-time = "2025-10-10T03:54:51.066Z" }, - { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337, upload-time = "2025-10-10T03:54:52.196Z" }, - { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743, upload-time = "2025-10-10T03:54:53.448Z" }, - { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" }, - { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" }, - { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" }, - { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" }, - { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" }, - { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" }, - { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" }, -] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, -] - -[[package]] -name = "huggingface-hub" -version = "1.3.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "fsspec" }, - { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, - { name = "httpx" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "shellingham" }, - { name = "tqdm" }, - { name = "typer-slim" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/3f/352efd52136bfd8aa9280c6d4a445869226ae2ccd49ddad4f62e90cfd168/huggingface_hub-1.3.7.tar.gz", hash = "sha256:5f86cd48f27131cdbf2882699cbdf7a67dd4cbe89a81edfdc31211f42e4a5fd1", size = 627537, upload-time = "2026-02-02T10:40:10.61Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/89/bfbfde252d649fae8d5f09b14a2870e5672ed160c1a6629301b3e5302621/huggingface_hub-1.3.7-py3-none-any.whl", hash = "sha256:8155ce937038fa3d0cb4347d752708079bc85e6d9eb441afb44c84bcf48620d2", size = 536728, upload-time = "2026-02-02T10:40:08.274Z" }, -] - -[[package]] -name = "humanfriendly" -version = "10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyreadline3", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - -[[package]] -name = "importlib-resources" -version = "6.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, -] - -[[package]] -name = "jiter" -version = "0.13.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/5e/4ec91646aee381d01cdb9974e30882c9cd3b8c5d1079d6b5ff4af522439a/jiter-0.13.0.tar.gz", hash = "sha256:f2839f9c2c7e2dffc1bc5929a510e14ce0a946be9365fd1219e7ef342dae14f4", size = 164847, upload-time = "2026-02-02T12:37:56.441Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/30/7687e4f87086829955013ca12a9233523349767f69653ebc27036313def9/jiter-0.13.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0a2bd69fc1d902e89925fc34d1da51b2128019423d7b339a45d9e99c894e0663", size = 307958, upload-time = "2026-02-02T12:35:57.165Z" }, - { url = "https://files.pythonhosted.org/packages/c3/27/e57f9a783246ed95481e6749cc5002a8a767a73177a83c63ea71f0528b90/jiter-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f917a04240ef31898182f76a332f508f2cc4b57d2b4d7ad2dbfebbfe167eb505", size = 318597, upload-time = "2026-02-02T12:35:58.591Z" }, - { url = "https://files.pythonhosted.org/packages/cf/52/e5719a60ac5d4d7c5995461a94ad5ef962a37c8bf5b088390e6fad59b2ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e2b199f446d3e82246b4fd9236d7cb502dc2222b18698ba0d986d2fecc6152", size = 348821, upload-time = "2026-02-02T12:36:00.093Z" }, - { url = "https://files.pythonhosted.org/packages/61/db/c1efc32b8ba4c740ab3fc2d037d8753f67685f475e26b9d6536a4322bcdd/jiter-0.13.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04670992b576fa65bd056dbac0c39fe8bd67681c380cb2b48efa885711d9d726", size = 364163, upload-time = "2026-02-02T12:36:01.937Z" }, - { url = "https://files.pythonhosted.org/packages/55/8a/fb75556236047c8806995671a18e4a0ad646ed255276f51a20f32dceaeec/jiter-0.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a1aff1fbdb803a376d4d22a8f63f8e7ccbce0b4890c26cc7af9e501ab339ef0", size = 483709, upload-time = "2026-02-02T12:36:03.41Z" }, - { url = "https://files.pythonhosted.org/packages/7e/16/43512e6ee863875693a8e6f6d532e19d650779d6ba9a81593ae40a9088ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b3fb8c2053acaef8580809ac1d1f7481a0a0bdc012fd7f5d8b18fb696a5a089", size = 370480, upload-time = "2026-02-02T12:36:04.791Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4c/09b93e30e984a187bc8aaa3510e1ec8dcbdcd71ca05d2f56aac0492453aa/jiter-0.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdaba7d87e66f26a2c45d8cbadcbfc4bf7884182317907baf39cfe9775bb4d93", size = 360735, upload-time = "2026-02-02T12:36:06.994Z" }, - { url = "https://files.pythonhosted.org/packages/1a/1b/46c5e349019874ec5dfa508c14c37e29864ea108d376ae26d90bee238cd7/jiter-0.13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b88d649135aca526da172e48083da915ec086b54e8e73a425ba50999468cc08", size = 391814, upload-time = "2026-02-02T12:36:08.368Z" }, - { url = "https://files.pythonhosted.org/packages/15/9e/26184760e85baee7162ad37b7912797d2077718476bf91517641c92b3639/jiter-0.13.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e404ea551d35438013c64b4f357b0474c7abf9f781c06d44fcaf7a14c69ff9e2", size = 513990, upload-time = "2026-02-02T12:36:09.993Z" }, - { url = "https://files.pythonhosted.org/packages/e9/34/2c9355247d6debad57a0a15e76ab1566ab799388042743656e566b3b7de1/jiter-0.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f4748aad1b4a93c8bdd70f604d0f748cdc0e8744c5547798acfa52f10e79228", size = 548021, upload-time = "2026-02-02T12:36:11.376Z" }, - { url = "https://files.pythonhosted.org/packages/ac/4a/9f2c23255d04a834398b9c2e0e665382116911dc4d06b795710503cdad25/jiter-0.13.0-cp312-cp312-win32.whl", hash = "sha256:0bf670e3b1445fc4d31612199f1744f67f889ee1bbae703c4b54dc097e5dd394", size = 203024, upload-time = "2026-02-02T12:36:12.682Z" }, - { url = "https://files.pythonhosted.org/packages/09/ee/f0ae675a957ae5a8f160be3e87acea6b11dc7b89f6b7ab057e77b2d2b13a/jiter-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:15db60e121e11fe186c0b15236bd5d18381b9ddacdcf4e659feb96fc6c969c92", size = 205424, upload-time = "2026-02-02T12:36:13.93Z" }, - { url = "https://files.pythonhosted.org/packages/1b/02/ae611edf913d3cbf02c97cdb90374af2082c48d7190d74c1111dde08bcdd/jiter-0.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:41f92313d17989102f3cb5dd533a02787cdb99454d494344b0361355da52fcb9", size = 186818, upload-time = "2026-02-02T12:36:15.308Z" }, - { url = "https://files.pythonhosted.org/packages/91/9c/7ee5a6ff4b9991e1a45263bfc46731634c4a2bde27dfda6c8251df2d958c/jiter-0.13.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1f8a55b848cbabf97d861495cd65f1e5c590246fabca8b48e1747c4dfc8f85bf", size = 306897, upload-time = "2026-02-02T12:36:16.748Z" }, - { url = "https://files.pythonhosted.org/packages/7c/02/be5b870d1d2be5dd6a91bdfb90f248fbb7dcbd21338f092c6b89817c3dbf/jiter-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f556aa591c00f2c45eb1b89f68f52441a016034d18b65da60e2d2875bbbf344a", size = 317507, upload-time = "2026-02-02T12:36:18.351Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/b25d2ec333615f5f284f3a4024f7ce68cfa0604c322c6808b2344c7f5d2b/jiter-0.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7e1d61da332ec412350463891923f960c3073cf1aae93b538f0bb4c8cd46efb", size = 350560, upload-time = "2026-02-02T12:36:19.746Z" }, - { url = "https://files.pythonhosted.org/packages/be/ec/74dcb99fef0aca9fbe56b303bf79f6bd839010cb18ad41000bf6cc71eec0/jiter-0.13.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3097d665a27bc96fd9bbf7f86178037db139f319f785e4757ce7ccbf390db6c2", size = 363232, upload-time = "2026-02-02T12:36:21.243Z" }, - { url = "https://files.pythonhosted.org/packages/1b/37/f17375e0bb2f6a812d4dd92d7616e41917f740f3e71343627da9db2824ce/jiter-0.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d01ecc3a8cbdb6f25a37bd500510550b64ddf9f7d64a107d92f3ccb25035d0f", size = 483727, upload-time = "2026-02-02T12:36:22.688Z" }, - { url = "https://files.pythonhosted.org/packages/77/d2/a71160a5ae1a1e66c1395b37ef77da67513b0adba73b993a27fbe47eb048/jiter-0.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed9bbc30f5d60a3bdf63ae76beb3f9db280d7f195dfcfa61af792d6ce912d159", size = 370799, upload-time = "2026-02-02T12:36:24.106Z" }, - { url = "https://files.pythonhosted.org/packages/01/99/ed5e478ff0eb4e8aa5fd998f9d69603c9fd3f32de3bd16c2b1194f68361c/jiter-0.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fbafb6e88256f4454de33c1f40203d09fc33ed19162a68b3b257b29ca7f663", size = 359120, upload-time = "2026-02-02T12:36:25.519Z" }, - { url = "https://files.pythonhosted.org/packages/16/be/7ffd08203277a813f732ba897352797fa9493faf8dc7995b31f3d9cb9488/jiter-0.13.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5467696f6b827f1116556cb0db620440380434591e93ecee7fd14d1a491b6daa", size = 390664, upload-time = "2026-02-02T12:36:26.866Z" }, - { url = "https://files.pythonhosted.org/packages/d1/84/e0787856196d6d346264d6dcccb01f741e5f0bd014c1d9a2ebe149caf4f3/jiter-0.13.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2d08c9475d48b92892583df9da592a0e2ac49bcd41fae1fec4f39ba6cf107820", size = 513543, upload-time = "2026-02-02T12:36:28.217Z" }, - { url = "https://files.pythonhosted.org/packages/65/50/ecbd258181c4313cf79bca6c88fb63207d04d5bf5e4f65174114d072aa55/jiter-0.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:aed40e099404721d7fcaf5b89bd3b4568a4666358bcac7b6b15c09fb6252ab68", size = 547262, upload-time = "2026-02-02T12:36:29.678Z" }, - { url = "https://files.pythonhosted.org/packages/27/da/68f38d12e7111d2016cd198161b36e1f042bd115c169255bcb7ec823a3bf/jiter-0.13.0-cp313-cp313-win32.whl", hash = "sha256:36ebfbcffafb146d0e6ffb3e74d51e03d9c35ce7c625c8066cdbfc7b953bdc72", size = 200630, upload-time = "2026-02-02T12:36:31.808Z" }, - { url = "https://files.pythonhosted.org/packages/25/65/3bd1a972c9a08ecd22eb3b08a95d1941ebe6938aea620c246cf426ae09c2/jiter-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:8d76029f077379374cf0dbc78dbe45b38dec4a2eb78b08b5194ce836b2517afc", size = 202602, upload-time = "2026-02-02T12:36:33.679Z" }, - { url = "https://files.pythonhosted.org/packages/15/fe/13bd3678a311aa67686bb303654792c48206a112068f8b0b21426eb6851e/jiter-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:bb7613e1a427cfcb6ea4544f9ac566b93d5bf67e0d48c787eca673ff9c9dff2b", size = 185939, upload-time = "2026-02-02T12:36:35.065Z" }, - { url = "https://files.pythonhosted.org/packages/49/19/a929ec002ad3228bc97ca01dbb14f7632fffdc84a95ec92ceaf4145688ae/jiter-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fa476ab5dd49f3bf3a168e05f89358c75a17608dbabb080ef65f96b27c19ab10", size = 316616, upload-time = "2026-02-02T12:36:36.579Z" }, - { url = "https://files.pythonhosted.org/packages/52/56/d19a9a194afa37c1728831e5fb81b7722c3de18a3109e8f282bfc23e587a/jiter-0.13.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade8cb6ff5632a62b7dbd4757d8c5573f7a2e9ae285d6b5b841707d8363205ef", size = 346850, upload-time = "2026-02-02T12:36:38.058Z" }, - { url = "https://files.pythonhosted.org/packages/36/4a/94e831c6bf287754a8a019cb966ed39ff8be6ab78cadecf08df3bb02d505/jiter-0.13.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9950290340acc1adaded363edd94baebcee7dabdfa8bee4790794cd5cfad2af6", size = 358551, upload-time = "2026-02-02T12:36:39.417Z" }, - { url = "https://files.pythonhosted.org/packages/a2/ec/a4c72c822695fa80e55d2b4142b73f0012035d9fcf90eccc56bc060db37c/jiter-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2b4972c6df33731aac0742b64fd0d18e0a69bc7d6e03108ce7d40c85fd9e3e6d", size = 201950, upload-time = "2026-02-02T12:36:40.791Z" }, - { url = "https://files.pythonhosted.org/packages/b6/00/393553ec27b824fbc29047e9c7cd4a3951d7fbe4a76743f17e44034fa4e4/jiter-0.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:701a1e77d1e593c1b435315ff625fd071f0998c5f02792038a5ca98899261b7d", size = 185852, upload-time = "2026-02-02T12:36:42.077Z" }, - { url = "https://files.pythonhosted.org/packages/6e/f5/f1997e987211f6f9bd71b8083047b316208b4aca0b529bb5f8c96c89ef3e/jiter-0.13.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:cc5223ab19fe25e2f0bf2643204ad7318896fe3729bf12fde41b77bfc4fafff0", size = 308804, upload-time = "2026-02-02T12:36:43.496Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8f/5482a7677731fd44881f0204981ce2d7175db271f82cba2085dd2212e095/jiter-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9776ebe51713acf438fd9b4405fcd86893ae5d03487546dae7f34993217f8a91", size = 318787, upload-time = "2026-02-02T12:36:45.071Z" }, - { url = "https://files.pythonhosted.org/packages/f3/b9/7257ac59778f1cd025b26a23c5520a36a424f7f1b068f2442a5b499b7464/jiter-0.13.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:879e768938e7b49b5e90b7e3fecc0dbec01b8cb89595861fb39a8967c5220d09", size = 353880, upload-time = "2026-02-02T12:36:47.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/87/719eec4a3f0841dad99e3d3604ee4cba36af4419a76f3cb0b8e2e691ad67/jiter-0.13.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:682161a67adea11e3aae9038c06c8b4a9a71023228767477d683f69903ebc607", size = 366702, upload-time = "2026-02-02T12:36:48.871Z" }, - { url = "https://files.pythonhosted.org/packages/d2/65/415f0a75cf6921e43365a1bc227c565cb949caca8b7532776e430cbaa530/jiter-0.13.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a13b68cd1cd8cc9de8f244ebae18ccb3e4067ad205220ef324c39181e23bbf66", size = 486319, upload-time = "2026-02-02T12:36:53.006Z" }, - { url = "https://files.pythonhosted.org/packages/54/a2/9e12b48e82c6bbc6081fd81abf915e1443add1b13d8fc586e1d90bb02bb8/jiter-0.13.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87ce0f14c6c08892b610686ae8be350bf368467b6acd5085a5b65441e2bf36d2", size = 372289, upload-time = "2026-02-02T12:36:54.593Z" }, - { url = "https://files.pythonhosted.org/packages/4e/c1/e4693f107a1789a239c759a432e9afc592366f04e901470c2af89cfd28e1/jiter-0.13.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c365005b05505a90d1c47856420980d0237adf82f70c4aff7aebd3c1cc143ad", size = 360165, upload-time = "2026-02-02T12:36:56.112Z" }, - { url = "https://files.pythonhosted.org/packages/17/08/91b9ea976c1c758240614bd88442681a87672eebc3d9a6dde476874e706b/jiter-0.13.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1317fdffd16f5873e46ce27d0e0f7f4f90f0cdf1d86bf6abeaea9f63ca2c401d", size = 389634, upload-time = "2026-02-02T12:36:57.495Z" }, - { url = "https://files.pythonhosted.org/packages/18/23/58325ef99390d6d40427ed6005bf1ad54f2577866594bcf13ce55675f87d/jiter-0.13.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c05b450d37ba0c9e21c77fef1f205f56bcee2330bddca68d344baebfc55ae0df", size = 514933, upload-time = "2026-02-02T12:36:58.909Z" }, - { url = "https://files.pythonhosted.org/packages/5b/25/69f1120c7c395fd276c3996bb8adefa9c6b84c12bb7111e5c6ccdcd8526d/jiter-0.13.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:775e10de3849d0631a97c603f996f518159272db00fdda0a780f81752255ee9d", size = 548842, upload-time = "2026-02-02T12:37:00.433Z" }, - { url = "https://files.pythonhosted.org/packages/18/05/981c9669d86850c5fbb0d9e62bba144787f9fba84546ba43d624ee27ef29/jiter-0.13.0-cp314-cp314-win32.whl", hash = "sha256:632bf7c1d28421c00dd8bbb8a3bac5663e1f57d5cd5ed962bce3c73bf62608e6", size = 202108, upload-time = "2026-02-02T12:37:01.718Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/cdcf54dd0b0341db7d25413229888a346c7130bd20820530905fdb65727b/jiter-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:f22ef501c3f87ede88f23f9b11e608581c14f04db59b6a801f354397ae13739f", size = 204027, upload-time = "2026-02-02T12:37:03.075Z" }, - { url = "https://files.pythonhosted.org/packages/fb/f9/724bcaaab7a3cd727031fe4f6995cb86c4bd344909177c186699c8dec51a/jiter-0.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:07b75fe09a4ee8e0c606200622e571e44943f47254f95e2436c8bdcaceb36d7d", size = 187199, upload-time = "2026-02-02T12:37:04.414Z" }, - { url = "https://files.pythonhosted.org/packages/62/92/1661d8b9fd6a3d7a2d89831db26fe3c1509a287d83ad7838831c7b7a5c7e/jiter-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:964538479359059a35fb400e769295d4b315ae61e4105396d355a12f7fef09f0", size = 318423, upload-time = "2026-02-02T12:37:05.806Z" }, - { url = "https://files.pythonhosted.org/packages/4f/3b/f77d342a54d4ebcd128e520fc58ec2f5b30a423b0fd26acdfc0c6fef8e26/jiter-0.13.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e104da1db1c0991b3eaed391ccd650ae8d947eab1480c733e5a3fb28d4313e40", size = 351438, upload-time = "2026-02-02T12:37:07.189Z" }, - { url = "https://files.pythonhosted.org/packages/76/b3/ba9a69f0e4209bd3331470c723c2f5509e6f0482e416b612431a5061ed71/jiter-0.13.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e3a5f0cde8ff433b8e88e41aa40131455420fb3649a3c7abdda6145f8cb7202", size = 364774, upload-time = "2026-02-02T12:37:08.579Z" }, - { url = "https://files.pythonhosted.org/packages/b3/16/6cdb31fa342932602458dbb631bfbd47f601e03d2e4950740e0b2100b570/jiter-0.13.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57aab48f40be1db920a582b30b116fe2435d184f77f0e4226f546794cedd9cf0", size = 487238, upload-time = "2026-02-02T12:37:10.066Z" }, - { url = "https://files.pythonhosted.org/packages/ed/b1/956cc7abaca8d95c13aa8d6c9b3f3797241c246cd6e792934cc4c8b250d2/jiter-0.13.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7772115877c53f62beeb8fd853cab692dbc04374ef623b30f997959a4c0e7e95", size = 372892, upload-time = "2026-02-02T12:37:11.656Z" }, - { url = "https://files.pythonhosted.org/packages/26/c4/97ecde8b1e74f67b8598c57c6fccf6df86ea7861ed29da84629cdbba76c4/jiter-0.13.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1211427574b17b633cfceba5040de8081e5abf114f7a7602f73d2e16f9fdaa59", size = 360309, upload-time = "2026-02-02T12:37:13.244Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d7/eabe3cf46715854ccc80be2cd78dd4c36aedeb30751dbf85a1d08c14373c/jiter-0.13.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7beae3a3d3b5212d3a55d2961db3c292e02e302feb43fce6a3f7a31b90ea6dfe", size = 389607, upload-time = "2026-02-02T12:37:14.881Z" }, - { url = "https://files.pythonhosted.org/packages/df/2d/03963fc0804e6109b82decfb9974eb92df3797fe7222428cae12f8ccaa0c/jiter-0.13.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e5562a0f0e90a6223b704163ea28e831bd3a9faa3512a711f031611e6b06c939", size = 514986, upload-time = "2026-02-02T12:37:16.326Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/8c83b45eb3eb1c1e18d841fe30b4b5bc5619d781267ca9bc03e005d8fd0a/jiter-0.13.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:6c26a424569a59140fb51160a56df13f438a2b0967365e987889186d5fc2f6f9", size = 548756, upload-time = "2026-02-02T12:37:17.736Z" }, - { url = "https://files.pythonhosted.org/packages/47/66/eea81dfff765ed66c68fd2ed8c96245109e13c896c2a5015c7839c92367e/jiter-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:24dc96eca9f84da4131cdf87a95e6ce36765c3b156fc9ae33280873b1c32d5f6", size = 201196, upload-time = "2026-02-02T12:37:19.101Z" }, - { url = "https://files.pythonhosted.org/packages/ff/32/4ac9c7a76402f8f00d00842a7f6b83b284d0cf7c1e9d4227bc95aa6d17fa/jiter-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0a8d76c7524087272c8ae913f5d9d608bd839154b62c4322ef65723d2e5bb0b8", size = 204215, upload-time = "2026-02-02T12:37:20.495Z" }, - { url = "https://files.pythonhosted.org/packages/f9/8e/7def204fea9f9be8b3c21a6f2dd6c020cf56c7d5ff753e0e23ed7f9ea57e/jiter-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2c26cf47e2cad140fa23b6d58d435a7c0161f5c514284802f25e87fddfe11024", size = 187152, upload-time = "2026-02-02T12:37:22.124Z" }, - { url = "https://files.pythonhosted.org/packages/80/60/e50fa45dd7e2eae049f0ce964663849e897300433921198aef94b6ffa23a/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:3d744a6061afba08dd7ae375dcde870cffb14429b7477e10f67e9e6d68772a0a", size = 305169, upload-time = "2026-02-02T12:37:50.376Z" }, - { url = "https://files.pythonhosted.org/packages/d2/73/a009f41c5eed71c49bec53036c4b33555afcdee70682a18c6f66e396c039/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:ff732bd0a0e778f43d5009840f20b935e79087b4dc65bd36f1cd0f9b04b8ff7f", size = 303808, upload-time = "2026-02-02T12:37:52.092Z" }, - { url = "https://files.pythonhosted.org/packages/c4/10/528b439290763bff3d939268085d03382471b442f212dca4ff5f12802d43/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab44b178f7981fcaea7e0a5df20e773c663d06ffda0198f1a524e91b2fde7e59", size = 337384, upload-time = "2026-02-02T12:37:53.582Z" }, - { url = "https://files.pythonhosted.org/packages/67/8a/a342b2f0251f3dac4ca17618265d93bf244a2a4d089126e81e4c1056ac50/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb00b6d26db67a05fe3e12c76edc75f32077fb51deed13822dc648fa373bc19", size = 343768, upload-time = "2026-02-02T12:37:55.055Z" }, -] - -[[package]] -name = "joblib" -version = "1.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[package]] -name = "jsonschema-specifications" -version = "2025.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "kubernetes" -version = "35.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "durationpy" }, - { name = "python-dateutil" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "requests-oauthlib" }, - { name = "six" }, - { name = "urllib3" }, - { name = "websocket-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2c/8f/85bf51ad4150f64e8c665daf0d9dfe9787ae92005efb9a4d1cba592bd79d/kubernetes-35.0.0.tar.gz", hash = "sha256:3d00d344944239821458b9efd484d6df9f011da367ecb155dadf9513f05f09ee", size = 1094642, upload-time = "2026-01-16T01:05:27.76Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/70/05b685ea2dffcb2adbf3cdcea5d8865b7bc66f67249084cf845012a0ff13/kubernetes-35.0.0-py2.py3-none-any.whl", hash = "sha256:39e2b33b46e5834ef6c3985ebfe2047ab39135d41de51ce7641a7ca5b372a13d", size = 2017602, upload-time = "2026-01-16T01:05:25.991Z" }, -] - -[[package]] -name = "lance-namespace" -version = "0.4.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "lance-namespace-urllib3-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b4/b5/0c3c55cf336b1e90392c2e24ac833551659e8bb3c61644b2d94825eb31bd/lance_namespace-0.4.5.tar.gz", hash = "sha256:0aee0abed3a1fa762c2955c7d12bb3004cea5c82ba28f6fcb9fe79d0cc19e317", size = 9827, upload-time = "2026-01-07T19:20:23.005Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/88/173687dad72baf819223e3b506898e386bc88c26ff8da5e8013291e02daf/lance_namespace-0.4.5-py3-none-any.whl", hash = "sha256:cd1a4f789de03ba23a0c16f100b1464cca572a5d04e428917a54d09db912d548", size = 11703, upload-time = "2026-01-07T19:20:25.394Z" }, -] - -[[package]] -name = "lance-namespace-urllib3-client" -version = "0.4.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dateutil" }, - { name = "typing-extensions" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/a9/4e527c2f05704565618b239b0965f829d1a194837f01234af3f8e2f33d92/lance_namespace_urllib3_client-0.4.5.tar.gz", hash = "sha256:184deda8cf8700926d994618187053c644eb1f2866a4479e7b80843cacc92b1c", size = 159726, upload-time = "2026-01-07T19:20:24.025Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/86/0adee7190408a28dcc5a0562c674537457e3de59ee51d1c724ecdc4a9930/lance_namespace_urllib3_client-0.4.5-py3-none-any.whl", hash = "sha256:2ee154d616ba4721f0bfdf043d33c4fef2e79d380653e2f263058ab00fb4adf4", size = 277969, upload-time = "2026-01-07T19:20:26.597Z" }, -] - -[[package]] -name = "lancedb" -version = "0.27.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "deprecation" }, - { name = "lance-namespace" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pyarrow" }, - { name = "pydantic" }, - { name = "tqdm" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/35/135ee7e3de58389074ad49b389adb8f431dc3f0034afbed1a9122c223c68/lancedb-0.27.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8aea87c3002850e98e4ac095c165dd819edd69f7c50e418f13f5917d1b9e0dcb", size = 43540316, upload-time = "2026-01-26T23:56:19.228Z" }, - { url = "https://files.pythonhosted.org/packages/16/cf/ea458fa50ef29c1a0653e1af6ea0599e532180267f49ca0bcf0049b0d8e3/lancedb-0.27.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:382666cddfb8b87d1efef4797bbc92cb1c3263b9b40894e5194ed5ed4e4486d4", size = 45409178, upload-time = "2026-01-27T03:25:09.932Z" }, - { url = "https://files.pythonhosted.org/packages/ee/cd/30714b878ec876eda3ce88637d6ef8da44484a065ec050dcfba3ad888465/lancedb-0.27.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7835e84d92631ddc7e269c8a18691ec16f24fe32f0fd14138d76951f530c28b9", size = 48484253, upload-time = "2026-01-27T03:28:16.048Z" }, - { url = "https://files.pythonhosted.org/packages/69/c2/19c1b8b7b36a0445e31fa532619bb75c4e76a91fff0514439dea2c4194d6/lancedb-0.27.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5996f7e36ae4cf580693fae33f560a21f29640b1ae0e923dcd8efea65ee8a78e", size = 45427415, upload-time = "2026-01-27T03:23:26.822Z" }, - { url = "https://files.pythonhosted.org/packages/eb/f1/794e9bc8d2adc9130c55695979afb66b0121c9d2abacdd19ce112e201879/lancedb-0.27.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:37e80565729555f6fc390a623da4f26392c463ba35e7634b2e706c1f9ac77e47", size = 48531937, upload-time = "2026-01-27T03:27:57.455Z" }, - { url = "https://files.pythonhosted.org/packages/3d/96/fa3cb37a6ffe7b81073d8c74f7cb95204d0922ac1668b264685aa34add20/lancedb-0.27.1-cp39-abi3-win_amd64.whl", hash = "sha256:f2150a66758ce6fe3cff226ac1ffcac2d5f5e2c9b35bc4c2d5923abcebef98cc", size = 53374010, upload-time = "2026-01-27T03:57:13.434Z" }, -] - -[[package]] -name = "librt" -version = "0.7.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/24/5f3646ff414285e0f7708fa4e946b9bf538345a41d1c375c439467721a5e/librt-0.7.8.tar.gz", hash = "sha256:1a4ede613941d9c3470b0368be851df6bb78ab218635512d0370b27a277a0862", size = 148323, upload-time = "2026-01-14T12:56:16.876Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/04/79d8fcb43cae376c7adbab7b2b9f65e48432c9eced62ac96703bcc16e09b/librt-0.7.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b6943885b2d49c48d0cff23b16be830ba46b0152d98f62de49e735c6e655a63", size = 57472, upload-time = "2026-01-14T12:55:08.528Z" }, - { url = "https://files.pythonhosted.org/packages/b4/ba/60b96e93043d3d659da91752689023a73981336446ae82078cddf706249e/librt-0.7.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46ef1f4b9b6cc364b11eea0ecc0897314447a66029ee1e55859acb3dd8757c93", size = 58986, upload-time = "2026-01-14T12:55:09.466Z" }, - { url = "https://files.pythonhosted.org/packages/7c/26/5215e4cdcc26e7be7eee21955a7e13cbf1f6d7d7311461a6014544596fac/librt-0.7.8-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:907ad09cfab21e3c86e8f1f87858f7049d1097f77196959c033612f532b4e592", size = 168422, upload-time = "2026-01-14T12:55:10.499Z" }, - { url = "https://files.pythonhosted.org/packages/0f/84/e8d1bc86fa0159bfc24f3d798d92cafd3897e84c7fea7fe61b3220915d76/librt-0.7.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2991b6c3775383752b3ca0204842743256f3ad3deeb1d0adc227d56b78a9a850", size = 177478, upload-time = "2026-01-14T12:55:11.577Z" }, - { url = "https://files.pythonhosted.org/packages/57/11/d0268c4b94717a18aa91df1100e767b010f87b7ae444dafaa5a2d80f33a6/librt-0.7.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03679b9856932b8c8f674e87aa3c55ea11c9274301f76ae8dc4d281bda55cf62", size = 192439, upload-time = "2026-01-14T12:55:12.7Z" }, - { url = "https://files.pythonhosted.org/packages/8d/56/1e8e833b95fe684f80f8894ae4d8b7d36acc9203e60478fcae599120a975/librt-0.7.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3968762fec1b2ad34ce57458b6de25dbb4142713e9ca6279a0d352fa4e9f452b", size = 191483, upload-time = "2026-01-14T12:55:13.838Z" }, - { url = "https://files.pythonhosted.org/packages/17/48/f11cf28a2cb6c31f282009e2208312aa84a5ee2732859f7856ee306176d5/librt-0.7.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bb7a7807523a31f03061288cc4ffc065d684c39db7644c676b47d89553c0d714", size = 185376, upload-time = "2026-01-14T12:55:15.017Z" }, - { url = "https://files.pythonhosted.org/packages/b8/6a/d7c116c6da561b9155b184354a60a3d5cdbf08fc7f3678d09c95679d13d9/librt-0.7.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad64a14b1e56e702e19b24aae108f18ad1bf7777f3af5fcd39f87d0c5a814449", size = 206234, upload-time = "2026-01-14T12:55:16.571Z" }, - { url = "https://files.pythonhosted.org/packages/61/de/1975200bb0285fc921c5981d9978ce6ce11ae6d797df815add94a5a848a3/librt-0.7.8-cp312-cp312-win32.whl", hash = "sha256:0241a6ed65e6666236ea78203a73d800dbed896cf12ae25d026d75dc1fcd1dac", size = 44057, upload-time = "2026-01-14T12:55:18.077Z" }, - { url = "https://files.pythonhosted.org/packages/8e/cd/724f2d0b3461426730d4877754b65d39f06a41ac9d0a92d5c6840f72b9ae/librt-0.7.8-cp312-cp312-win_amd64.whl", hash = "sha256:6db5faf064b5bab9675c32a873436b31e01d66ca6984c6f7f92621656033a708", size = 50293, upload-time = "2026-01-14T12:55:19.179Z" }, - { url = "https://files.pythonhosted.org/packages/bd/cf/7e899acd9ee5727ad8160fdcc9994954e79fab371c66535c60e13b968ffc/librt-0.7.8-cp312-cp312-win_arm64.whl", hash = "sha256:57175aa93f804d2c08d2edb7213e09276bd49097611aefc37e3fa38d1fb99ad0", size = 43574, upload-time = "2026-01-14T12:55:20.185Z" }, - { url = "https://files.pythonhosted.org/packages/a1/fe/b1f9de2829cf7fc7649c1dcd202cfd873837c5cc2fc9e526b0e7f716c3d2/librt-0.7.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4c3995abbbb60b3c129490fa985dfe6cac11d88fc3c36eeb4fb1449efbbb04fc", size = 57500, upload-time = "2026-01-14T12:55:21.219Z" }, - { url = "https://files.pythonhosted.org/packages/eb/d4/4a60fbe2e53b825f5d9a77325071d61cd8af8506255067bf0c8527530745/librt-0.7.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44e0c2cbc9bebd074cf2cdbe472ca185e824be4e74b1c63a8e934cea674bebf2", size = 59019, upload-time = "2026-01-14T12:55:22.256Z" }, - { url = "https://files.pythonhosted.org/packages/6a/37/61ff80341ba5159afa524445f2d984c30e2821f31f7c73cf166dcafa5564/librt-0.7.8-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d2f1e492cae964b3463a03dc77a7fe8742f7855d7258c7643f0ee32b6651dd3", size = 169015, upload-time = "2026-01-14T12:55:23.24Z" }, - { url = "https://files.pythonhosted.org/packages/1c/86/13d4f2d6a93f181ebf2fc953868826653ede494559da8268023fe567fca3/librt-0.7.8-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:451e7ffcef8f785831fdb791bd69211f47e95dc4c6ddff68e589058806f044c6", size = 178161, upload-time = "2026-01-14T12:55:24.826Z" }, - { url = "https://files.pythonhosted.org/packages/88/26/e24ef01305954fc4d771f1f09f3dd682f9eb610e1bec188ffb719374d26e/librt-0.7.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3469e1af9f1380e093ae06bedcbdd11e407ac0b303a56bbe9afb1d6824d4982d", size = 193015, upload-time = "2026-01-14T12:55:26.04Z" }, - { url = "https://files.pythonhosted.org/packages/88/a0/92b6bd060e720d7a31ed474d046a69bd55334ec05e9c446d228c4b806ae3/librt-0.7.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f11b300027ce19a34f6d24ebb0a25fd0e24a9d53353225a5c1e6cadbf2916b2e", size = 192038, upload-time = "2026-01-14T12:55:27.208Z" }, - { url = "https://files.pythonhosted.org/packages/06/bb/6f4c650253704279c3a214dad188101d1b5ea23be0606628bc6739456624/librt-0.7.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4adc73614f0d3c97874f02f2c7fd2a27854e7e24ad532ea6b965459c5b757eca", size = 186006, upload-time = "2026-01-14T12:55:28.594Z" }, - { url = "https://files.pythonhosted.org/packages/dc/00/1c409618248d43240cadf45f3efb866837fa77e9a12a71481912135eb481/librt-0.7.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:60c299e555f87e4c01b2eca085dfccda1dde87f5a604bb45c2906b8305819a93", size = 206888, upload-time = "2026-01-14T12:55:30.214Z" }, - { url = "https://files.pythonhosted.org/packages/d9/83/b2cfe8e76ff5c1c77f8a53da3d5de62d04b5ebf7cf913e37f8bca43b5d07/librt-0.7.8-cp313-cp313-win32.whl", hash = "sha256:b09c52ed43a461994716082ee7d87618096851319bf695d57ec123f2ab708951", size = 44126, upload-time = "2026-01-14T12:55:31.44Z" }, - { url = "https://files.pythonhosted.org/packages/a9/0b/c59d45de56a51bd2d3a401fc63449c0ac163e4ef7f523ea8b0c0dee86ec5/librt-0.7.8-cp313-cp313-win_amd64.whl", hash = "sha256:f8f4a901a3fa28969d6e4519deceab56c55a09d691ea7b12ca830e2fa3461e34", size = 50262, upload-time = "2026-01-14T12:55:33.01Z" }, - { url = "https://files.pythonhosted.org/packages/fc/b9/973455cec0a1ec592395250c474164c4a58ebf3e0651ee920fef1a2623f1/librt-0.7.8-cp313-cp313-win_arm64.whl", hash = "sha256:43d4e71b50763fcdcf64725ac680d8cfa1706c928b844794a7aa0fa9ac8e5f09", size = 43600, upload-time = "2026-01-14T12:55:34.054Z" }, - { url = "https://files.pythonhosted.org/packages/1a/73/fa8814c6ce2d49c3827829cadaa1589b0bf4391660bd4510899393a23ebc/librt-0.7.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:be927c3c94c74b05128089a955fba86501c3b544d1d300282cc1b4bd370cb418", size = 57049, upload-time = "2026-01-14T12:55:35.056Z" }, - { url = "https://files.pythonhosted.org/packages/53/fe/f6c70956da23ea235fd2e3cc16f4f0b4ebdfd72252b02d1164dd58b4e6c3/librt-0.7.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7b0803e9008c62a7ef79058233db7ff6f37a9933b8f2573c05b07ddafa226611", size = 58689, upload-time = "2026-01-14T12:55:36.078Z" }, - { url = "https://files.pythonhosted.org/packages/1f/4d/7a2481444ac5fba63050d9abe823e6bc16896f575bfc9c1e5068d516cdce/librt-0.7.8-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:79feb4d00b2a4e0e05c9c56df707934f41fcb5fe53fd9efb7549068d0495b758", size = 166808, upload-time = "2026-01-14T12:55:37.595Z" }, - { url = "https://files.pythonhosted.org/packages/ac/3c/10901d9e18639f8953f57c8986796cfbf4c1c514844a41c9197cf87cb707/librt-0.7.8-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9122094e3f24aa759c38f46bd8863433820654927370250f460ae75488b66ea", size = 175614, upload-time = "2026-01-14T12:55:38.756Z" }, - { url = "https://files.pythonhosted.org/packages/db/01/5cbdde0951a5090a80e5ba44e6357d375048123c572a23eecfb9326993a7/librt-0.7.8-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e03bea66af33c95ce3addf87a9bf1fcad8d33e757bc479957ddbc0e4f7207ac", size = 189955, upload-time = "2026-01-14T12:55:39.939Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b4/e80528d2f4b7eaf1d437fcbd6fc6ba4cbeb3e2a0cb9ed5a79f47c7318706/librt-0.7.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f1ade7f31675db00b514b98f9ab9a7698c7282dad4be7492589109471852d398", size = 189370, upload-time = "2026-01-14T12:55:41.057Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ab/938368f8ce31a9787ecd4becb1e795954782e4312095daf8fd22420227c8/librt-0.7.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a14229ac62adcf1b90a15992f1ab9c69ae8b99ffb23cb64a90878a6e8a2f5b81", size = 183224, upload-time = "2026-01-14T12:55:42.328Z" }, - { url = "https://files.pythonhosted.org/packages/3c/10/559c310e7a6e4014ac44867d359ef8238465fb499e7eb31b6bfe3e3f86f5/librt-0.7.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5bcaaf624fd24e6a0cb14beac37677f90793a96864c67c064a91458611446e83", size = 203541, upload-time = "2026-01-14T12:55:43.501Z" }, - { url = "https://files.pythonhosted.org/packages/f8/db/a0db7acdb6290c215f343835c6efda5b491bb05c3ddc675af558f50fdba3/librt-0.7.8-cp314-cp314-win32.whl", hash = "sha256:7aa7d5457b6c542ecaed79cec4ad98534373c9757383973e638ccced0f11f46d", size = 40657, upload-time = "2026-01-14T12:55:44.668Z" }, - { url = "https://files.pythonhosted.org/packages/72/e0/4f9bdc2a98a798511e81edcd6b54fe82767a715e05d1921115ac70717f6f/librt-0.7.8-cp314-cp314-win_amd64.whl", hash = "sha256:3d1322800771bee4a91f3b4bd4e49abc7d35e65166821086e5afd1e6c0d9be44", size = 46835, upload-time = "2026-01-14T12:55:45.655Z" }, - { url = "https://files.pythonhosted.org/packages/f9/3d/59c6402e3dec2719655a41ad027a7371f8e2334aa794ed11533ad5f34969/librt-0.7.8-cp314-cp314-win_arm64.whl", hash = "sha256:5363427bc6a8c3b1719f8f3845ea53553d301382928a86e8fab7984426949bce", size = 39885, upload-time = "2026-01-14T12:55:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/4e/9c/2481d80950b83085fb14ba3c595db56330d21bbc7d88a19f20165f3538db/librt-0.7.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ca916919793a77e4a98d4a1701e345d337ce53be4a16620f063191f7322ac80f", size = 59161, upload-time = "2026-01-14T12:55:48.45Z" }, - { url = "https://files.pythonhosted.org/packages/96/79/108df2cfc4e672336765d54e3ff887294c1cc36ea4335c73588875775527/librt-0.7.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:54feb7b4f2f6706bb82325e836a01be805770443e2400f706e824e91f6441dde", size = 61008, upload-time = "2026-01-14T12:55:49.527Z" }, - { url = "https://files.pythonhosted.org/packages/46/f2/30179898f9994a5637459d6e169b6abdc982012c0a4b2d4c26f50c06f911/librt-0.7.8-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:39a4c76fee41007070f872b648cc2f711f9abf9a13d0c7162478043377b52c8e", size = 187199, upload-time = "2026-01-14T12:55:50.587Z" }, - { url = "https://files.pythonhosted.org/packages/b4/da/f7563db55cebdc884f518ba3791ad033becc25ff68eb70902b1747dc0d70/librt-0.7.8-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac9c8a458245c7de80bc1b9765b177055efff5803f08e548dd4bb9ab9a8d789b", size = 198317, upload-time = "2026-01-14T12:55:51.991Z" }, - { url = "https://files.pythonhosted.org/packages/b3/6c/4289acf076ad371471fa86718c30ae353e690d3de6167f7db36f429272f1/librt-0.7.8-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b67aa7eff150f075fda09d11f6bfb26edffd300f6ab1666759547581e8f666", size = 210334, upload-time = "2026-01-14T12:55:53.682Z" }, - { url = "https://files.pythonhosted.org/packages/4a/7f/377521ac25b78ac0a5ff44127a0360ee6d5ddd3ce7327949876a30533daa/librt-0.7.8-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:535929b6eff670c593c34ff435d5440c3096f20fa72d63444608a5aef64dd581", size = 211031, upload-time = "2026-01-14T12:55:54.827Z" }, - { url = "https://files.pythonhosted.org/packages/c5/b1/e1e96c3e20b23d00cf90f4aad48f0deb4cdfec2f0ed8380d0d85acf98bbf/librt-0.7.8-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:63937bd0f4d1cb56653dc7ae900d6c52c41f0015e25aaf9902481ee79943b33a", size = 204581, upload-time = "2026-01-14T12:55:56.811Z" }, - { url = "https://files.pythonhosted.org/packages/43/71/0f5d010e92ed9747e14bef35e91b6580533510f1e36a8a09eb79ee70b2f0/librt-0.7.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf243da9e42d914036fd362ac3fa77d80a41cadcd11ad789b1b5eec4daaf67ca", size = 224731, upload-time = "2026-01-14T12:55:58.175Z" }, - { url = "https://files.pythonhosted.org/packages/22/f0/07fb6ab5c39a4ca9af3e37554f9d42f25c464829254d72e4ebbd81da351c/librt-0.7.8-cp314-cp314t-win32.whl", hash = "sha256:171ca3a0a06c643bd0a2f62a8944e1902c94aa8e5da4db1ea9a8daf872685365", size = 41173, upload-time = "2026-01-14T12:55:59.315Z" }, - { url = "https://files.pythonhosted.org/packages/24/d4/7e4be20993dc6a782639625bd2f97f3c66125c7aa80c82426956811cfccf/librt-0.7.8-cp314-cp314t-win_amd64.whl", hash = "sha256:445b7304145e24c60288a2f172b5ce2ca35c0f81605f5299f3fa567e189d2e32", size = 47668, upload-time = "2026-01-14T12:56:00.261Z" }, - { url = "https://files.pythonhosted.org/packages/fc/85/69f92b2a7b3c0f88ffe107c86b952b397004b5b8ea5a81da3d9c04c04422/librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06", size = 40550, upload-time = "2026-01-14T12:56:01.542Z" }, -] - -[[package]] -name = "litellm" -version = "1.81.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "click" }, - { name = "fastuuid" }, - { name = "httpx" }, - { name = "importlib-metadata" }, - { name = "jinja2" }, - { name = "jsonschema" }, - { name = "openai" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "tiktoken" }, - { name = "tokenizers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2e/f3/194a2dca6cb3eddb89f4bc2920cf5e27542256af907c23be13c61fe7e021/litellm-1.81.6.tar.gz", hash = "sha256:f02b503dfb7d66d1c939f82e4db21aeec1d6e2ed1fe3f5cd02aaec3f792bc4ae", size = 13878107, upload-time = "2026-02-01T04:02:27.36Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/05/3516cc7386b220d388aa0bd833308c677e94eceb82b2756dd95e06f6a13f/litellm-1.81.6-py3-none-any.whl", hash = "sha256:573206ba194d49a1691370ba33f781671609ac77c35347f8a0411d852cf6341a", size = 12224343, upload-time = "2026-02-01T04:02:23.704Z" }, -] - -[[package]] -name = "loguru" -version = "0.7.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "win32-setctime", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, -] - -[[package]] -name = "lru-dict" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/63/21480e8ecc218b9b15672d194ea79da8a7389737c21d8406254306733cac/lru-dict-1.2.0.tar.gz", hash = "sha256:13c56782f19d68ddf4d8db0170041192859616514c706b126d0df2ec72a11bd7", size = 10895, upload-time = "2023-05-27T01:24:15.259Z" } - -[[package]] -name = "markdown" -version = "3.10.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/b1/af95bcae8549f1f3fd70faacb29075826a0d689a27f232e8cee315efa053/markdown-3.10.1.tar.gz", hash = "sha256:1c19c10bd5c14ac948c53d0d762a04e2fa35a6d58a6b7b1e6bfcbe6fefc0001a", size = 365402, upload-time = "2026-01-21T18:09:28.206Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl", hash = "sha256:867d788939fe33e4b736426f5b9f651ad0c0ae0ecf89df0ca5d1176c70812fe3", size = 107684, upload-time = "2026-01-21T18:09:27.203Z" }, -] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, - { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, - { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, - { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, - { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, - { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, - { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, - { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, - { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, - { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, - { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, - { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, - { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, - { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, - { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, - { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, - { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, - { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, - { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, - { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, - { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, - { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, - { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, - { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, - { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, - { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, - { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, - { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, - { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, - { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, - { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, - { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, - { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, - { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, - { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, - { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, - { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, - { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, - { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, - { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, - { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, - { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, - { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, - { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, - { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, -] - -[[package]] -name = "mcp" -version = "1.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyjwt", extra = ["crypto"] }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, -] - -[[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 = "mergedeep" -version = "1.3.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, -] - -[[package]] -name = "mkdocs" -version = "1.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "ghp-import" }, - { name = "jinja2" }, - { name = "markdown" }, - { name = "markupsafe" }, - { name = "mergedeep" }, - { name = "mkdocs-get-deps" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "pyyaml" }, - { name = "pyyaml-env-tag" }, - { name = "watchdog" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, -] - -[[package]] -name = "mkdocs-autorefs" -version = "1.4.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown" }, - { name = "markupsafe" }, - { name = "mkdocs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/51/fa/9124cd63d822e2bcbea1450ae68cdc3faf3655c69b455f3a7ed36ce6c628/mkdocs_autorefs-1.4.3.tar.gz", hash = "sha256:beee715b254455c4aa93b6ef3c67579c399ca092259cc41b7d9342573ff1fc75", size = 55425, upload-time = "2025-08-26T14:23:17.223Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/4d/7123b6fa2278000688ebd338e2a06d16870aaf9eceae6ba047ea05f92df1/mkdocs_autorefs-1.4.3-py3-none-any.whl", hash = "sha256:469d85eb3114801d08e9cc55d102b3ba65917a869b893403b8987b601cf55dc9", size = 25034, upload-time = "2025-08-26T14:23:15.906Z" }, -] - -[[package]] -name = "mkdocs-get-deps" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mergedeep" }, - { name = "platformdirs" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239, upload-time = "2023-11-20T17:51:09.981Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521, upload-time = "2023-11-20T17:51:08.587Z" }, -] - -[[package]] -name = "mkdocs-material" -version = "9.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "babel" }, - { name = "backrefs" }, - { name = "colorama" }, - { name = "jinja2" }, - { name = "markdown" }, - { name = "mkdocs" }, - { name = "mkdocs-material-extensions" }, - { name = "paginate" }, - { name = "pygments" }, - { name = "pymdown-extensions" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/27/e2/2ffc356cd72f1473d07c7719d82a8f2cbd261666828614ecb95b12169f41/mkdocs_material-9.7.1.tar.gz", hash = "sha256:89601b8f2c3e6c6ee0a918cc3566cb201d40bf37c3cd3c2067e26fadb8cce2b8", size = 4094392, upload-time = "2025-12-18T09:49:00.308Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-py3-none-any.whl", hash = "sha256:3f6100937d7d731f87f1e3e3b021c97f7239666b9ba1151ab476cabb96c60d5c", size = 9297166, upload-time = "2025-12-18T09:48:56.664Z" }, -] - -[[package]] -name = "mkdocs-material-extensions" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" }, -] - -[[package]] -name = "mkdocstrings" -version = "1.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "markdown" }, - { name = "markupsafe" }, - { name = "mkdocs" }, - { name = "mkdocs-autorefs" }, - { name = "pymdown-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/4d/1ca8a9432579184599714aaeb36591414cc3d3bfd9d494f6db540c995ae4/mkdocstrings-1.0.2.tar.gz", hash = "sha256:48edd0ccbcb9e30a3121684e165261a9d6af4d63385fc4f39a54a49ac3b32ea8", size = 101048, upload-time = "2026-01-24T15:57:25.735Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/57/32/407a9a5fdd7d8ecb4af8d830b9bcdf47ea68f916869b3f44bac31f081250/mkdocstrings-1.0.2-py3-none-any.whl", hash = "sha256:41897815a8026c3634fe5d51472c3a569f92ded0ad8c7a640550873eea3b6817", size = 35443, upload-time = "2026-01-24T15:57:23.933Z" }, -] - -[package.optional-dependencies] -python = [ - { name = "mkdocstrings-python" }, -] - -[[package]] -name = "mkdocstrings-python" -version = "2.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "griffe" }, - { name = "mkdocs-autorefs" }, - { name = "mkdocstrings" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/24/75/d30af27a2906f00eb90143470272376d728521997800f5dce5b340ba35bc/mkdocstrings_python-2.0.1.tar.gz", hash = "sha256:843a562221e6a471fefdd4b45cc6c22d2607ccbad632879234fa9692e9cf7732", size = 199345, upload-time = "2025-12-03T14:26:11.755Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/06/c5f8deba7d2cbdfa7967a716ae801aa9ca5f734b8f54fd473ef77a088dbe/mkdocstrings_python-2.0.1-py3-none-any.whl", hash = "sha256:66ecff45c5f8b71bf174e11d49afc845c2dfc7fc0ab17a86b6b337e0f24d8d90", size = 105055, upload-time = "2025-12-03T14:26:10.184Z" }, -] - -[[package]] -name = "mmh3" -version = "5.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/af/f28c2c2f51f31abb4725f9a64bc7863d5f491f6539bd26aee2a1d21a649e/mmh3-5.2.0.tar.gz", hash = "sha256:1efc8fec8478e9243a78bb993422cf79f8ff85cb4cf6b79647480a31e0d950a8", size = 33582, upload-time = "2025-07-29T07:43:48.49Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/6a/d5aa7edb5c08e0bd24286c7d08341a0446f9a2fbbb97d96a8a6dd81935ee/mmh3-5.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:384eda9361a7bf83a85e09447e1feafe081034af9dd428893701b959230d84be", size = 56141, upload-time = "2025-07-29T07:42:13.456Z" }, - { url = "https://files.pythonhosted.org/packages/08/49/131d0fae6447bc4a7299ebdb1a6fb9d08c9f8dcf97d75ea93e8152ddf7ab/mmh3-5.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2c9da0d568569cc87315cb063486d761e38458b8ad513fedd3dc9263e1b81bcd", size = 40681, upload-time = "2025-07-29T07:42:14.306Z" }, - { url = "https://files.pythonhosted.org/packages/8f/6f/9221445a6bcc962b7f5ff3ba18ad55bba624bacdc7aa3fc0a518db7da8ec/mmh3-5.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86d1be5d63232e6eb93c50881aea55ff06eb86d8e08f9b5417c8c9b10db9db96", size = 40062, upload-time = "2025-07-29T07:42:15.08Z" }, - { url = "https://files.pythonhosted.org/packages/1e/d4/6bb2d0fef81401e0bb4c297d1eb568b767de4ce6fc00890bc14d7b51ecc4/mmh3-5.2.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bf7bee43e17e81671c447e9c83499f53d99bf440bc6d9dc26a841e21acfbe094", size = 97333, upload-time = "2025-07-29T07:42:16.436Z" }, - { url = "https://files.pythonhosted.org/packages/44/e0/ccf0daff8134efbb4fbc10a945ab53302e358c4b016ada9bf97a6bdd50c1/mmh3-5.2.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7aa18cdb58983ee660c9c400b46272e14fa253c675ed963d3812487f8ca42037", size = 103310, upload-time = "2025-07-29T07:42:17.796Z" }, - { url = "https://files.pythonhosted.org/packages/02/63/1965cb08a46533faca0e420e06aff8bbaf9690a6f0ac6ae6e5b2e4544687/mmh3-5.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9d032488fcec32d22be6542d1a836f00247f40f320844dbb361393b5b22773", size = 106178, upload-time = "2025-07-29T07:42:19.281Z" }, - { url = "https://files.pythonhosted.org/packages/c2/41/c883ad8e2c234013f27f92061200afc11554ea55edd1bcf5e1accd803a85/mmh3-5.2.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1861fb6b1d0453ed7293200139c0a9011eeb1376632e048e3766945b13313c5", size = 113035, upload-time = "2025-07-29T07:42:20.356Z" }, - { url = "https://files.pythonhosted.org/packages/df/b5/1ccade8b1fa625d634a18bab7bf08a87457e09d5ec8cf83ca07cbea9d400/mmh3-5.2.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:99bb6a4d809aa4e528ddfe2c85dd5239b78b9dd14be62cca0329db78505e7b50", size = 120784, upload-time = "2025-07-29T07:42:21.377Z" }, - { url = "https://files.pythonhosted.org/packages/77/1c/919d9171fcbdcdab242e06394464ccf546f7d0f3b31e0d1e3a630398782e/mmh3-5.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1f8d8b627799f4e2fcc7c034fed8f5f24dc7724ff52f69838a3d6d15f1ad4765", size = 99137, upload-time = "2025-07-29T07:42:22.344Z" }, - { url = "https://files.pythonhosted.org/packages/66/8a/1eebef5bd6633d36281d9fc83cf2e9ba1ba0e1a77dff92aacab83001cee4/mmh3-5.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b5995088dd7023d2d9f310a0c67de5a2b2e06a570ecfd00f9ff4ab94a67cde43", size = 98664, upload-time = "2025-07-29T07:42:23.269Z" }, - { url = "https://files.pythonhosted.org/packages/13/41/a5d981563e2ee682b21fb65e29cc0f517a6734a02b581359edd67f9d0360/mmh3-5.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1a5f4d2e59d6bba8ef01b013c472741835ad961e7c28f50c82b27c57748744a4", size = 106459, upload-time = "2025-07-29T07:42:24.238Z" }, - { url = "https://files.pythonhosted.org/packages/24/31/342494cd6ab792d81e083680875a2c50fa0c5df475ebf0b67784f13e4647/mmh3-5.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fd6e6c3d90660d085f7e73710eab6f5545d4854b81b0135a3526e797009dbda3", size = 110038, upload-time = "2025-07-29T07:42:25.629Z" }, - { url = "https://files.pythonhosted.org/packages/28/44/efda282170a46bb4f19c3e2b90536513b1d821c414c28469a227ca5a1789/mmh3-5.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c4a2f3d83879e3de2eb8cbf562e71563a8ed15ee9b9c2e77ca5d9f73072ac15c", size = 97545, upload-time = "2025-07-29T07:42:27.04Z" }, - { url = "https://files.pythonhosted.org/packages/68/8f/534ae319c6e05d714f437e7206f78c17e66daca88164dff70286b0e8ea0c/mmh3-5.2.0-cp312-cp312-win32.whl", hash = "sha256:2421b9d665a0b1ad724ec7332fb5a98d075f50bc51a6ff854f3a1882bd650d49", size = 40805, upload-time = "2025-07-29T07:42:28.032Z" }, - { url = "https://files.pythonhosted.org/packages/b8/f6/f6abdcfefcedab3c964868048cfe472764ed358c2bf6819a70dd4ed4ed3a/mmh3-5.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:72d80005b7634a3a2220f81fbeb94775ebd12794623bb2e1451701ea732b4aa3", size = 41597, upload-time = "2025-07-29T07:42:28.894Z" }, - { url = "https://files.pythonhosted.org/packages/15/fd/f7420e8cbce45c259c770cac5718badf907b302d3a99ec587ba5ce030237/mmh3-5.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:3d6bfd9662a20c054bc216f861fa330c2dac7c81e7fb8307b5e32ab5b9b4d2e0", size = 39350, upload-time = "2025-07-29T07:42:29.794Z" }, - { url = "https://files.pythonhosted.org/packages/d8/fa/27f6ab93995ef6ad9f940e96593c5dd24744d61a7389532b0fec03745607/mmh3-5.2.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:e79c00eba78f7258e5b354eccd4d7907d60317ced924ea4a5f2e9d83f5453065", size = 40874, upload-time = "2025-07-29T07:42:30.662Z" }, - { url = "https://files.pythonhosted.org/packages/11/9c/03d13bcb6a03438bc8cac3d2e50f80908d159b31a4367c2e1a7a077ded32/mmh3-5.2.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:956127e663d05edbeec54df38885d943dfa27406594c411139690485128525de", size = 42012, upload-time = "2025-07-29T07:42:31.539Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/0865d9765408a7d504f1789944e678f74e0888b96a766d578cb80b040999/mmh3-5.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:c3dca4cb5b946ee91b3d6bb700d137b1cd85c20827f89fdf9c16258253489044", size = 39197, upload-time = "2025-07-29T07:42:32.374Z" }, - { url = "https://files.pythonhosted.org/packages/3e/12/76c3207bd186f98b908b6706c2317abb73756d23a4e68ea2bc94825b9015/mmh3-5.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:e651e17bfde5840e9e4174b01e9e080ce49277b70d424308b36a7969d0d1af73", size = 39840, upload-time = "2025-07-29T07:42:33.227Z" }, - { url = "https://files.pythonhosted.org/packages/5d/0d/574b6cce5555c9f2b31ea189ad44986755eb14e8862db28c8b834b8b64dc/mmh3-5.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:9f64bf06f4bf623325fda3a6d02d36cd69199b9ace99b04bb2d7fd9f89688504", size = 40644, upload-time = "2025-07-29T07:42:34.099Z" }, - { url = "https://files.pythonhosted.org/packages/52/82/3731f8640b79c46707f53ed72034a58baad400be908c87b0088f1f89f986/mmh3-5.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ddc63328889bcaee77b743309e5c7d2d52cee0d7d577837c91b6e7cc9e755e0b", size = 56153, upload-time = "2025-07-29T07:42:35.031Z" }, - { url = "https://files.pythonhosted.org/packages/4f/34/e02dca1d4727fd9fdeaff9e2ad6983e1552804ce1d92cc796e5b052159bb/mmh3-5.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bb0fdc451fb6d86d81ab8f23d881b8d6e37fc373a2deae1c02d27002d2ad7a05", size = 40684, upload-time = "2025-07-29T07:42:35.914Z" }, - { url = "https://files.pythonhosted.org/packages/8f/36/3dee40767356e104967e6ed6d102ba47b0b1ce2a89432239b95a94de1b89/mmh3-5.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b29044e1ffdb84fe164d0a7ea05c7316afea93c00f8ed9449cf357c36fc4f814", size = 40057, upload-time = "2025-07-29T07:42:36.755Z" }, - { url = "https://files.pythonhosted.org/packages/31/58/228c402fccf76eb39a0a01b8fc470fecf21965584e66453b477050ee0e99/mmh3-5.2.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:58981d6ea9646dbbf9e59a30890cbf9f610df0e4a57dbfe09215116fd90b0093", size = 97344, upload-time = "2025-07-29T07:42:37.675Z" }, - { url = "https://files.pythonhosted.org/packages/34/82/fc5ce89006389a6426ef28e326fc065b0fbaaed230373b62d14c889f47ea/mmh3-5.2.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e5634565367b6d98dc4aa2983703526ef556b3688ba3065edb4b9b90ede1c54", size = 103325, upload-time = "2025-07-29T07:42:38.591Z" }, - { url = "https://files.pythonhosted.org/packages/09/8c/261e85777c6aee1ebd53f2f17e210e7481d5b0846cd0b4a5c45f1e3761b8/mmh3-5.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0271ac12415afd3171ab9a3c7cbfc71dee2c68760a7dc9d05bf8ed6ddfa3a7a", size = 106240, upload-time = "2025-07-29T07:42:39.563Z" }, - { url = "https://files.pythonhosted.org/packages/70/73/2f76b3ad8a3d431824e9934403df36c0ddacc7831acf82114bce3c4309c8/mmh3-5.2.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:45b590e31bc552c6f8e2150ff1ad0c28dd151e9f87589e7eaf508fbdd8e8e908", size = 113060, upload-time = "2025-07-29T07:42:40.585Z" }, - { url = "https://files.pythonhosted.org/packages/9f/b9/7ea61a34e90e50a79a9d87aa1c0b8139a7eaf4125782b34b7d7383472633/mmh3-5.2.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bdde97310d59604f2a9119322f61b31546748499a21b44f6715e8ced9308a6c5", size = 120781, upload-time = "2025-07-29T07:42:41.618Z" }, - { url = "https://files.pythonhosted.org/packages/0f/5b/ae1a717db98c7894a37aeedbd94b3f99e6472a836488f36b6849d003485b/mmh3-5.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc9c5f280438cf1c1a8f9abb87dc8ce9630a964120cfb5dd50d1e7ce79690c7a", size = 99174, upload-time = "2025-07-29T07:42:42.587Z" }, - { url = "https://files.pythonhosted.org/packages/e3/de/000cce1d799fceebb6d4487ae29175dd8e81b48e314cba7b4da90bcf55d7/mmh3-5.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c903e71fd8debb35ad2a4184c1316b3cb22f64ce517b4e6747f25b0a34e41266", size = 98734, upload-time = "2025-07-29T07:42:43.996Z" }, - { url = "https://files.pythonhosted.org/packages/79/19/0dc364391a792b72fbb22becfdeacc5add85cc043cd16986e82152141883/mmh3-5.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:eed4bba7ff8a0d37106ba931ab03bdd3915fbb025bcf4e1f0aa02bc8114960c5", size = 106493, upload-time = "2025-07-29T07:42:45.07Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b1/bc8c28e4d6e807bbb051fefe78e1156d7f104b89948742ad310612ce240d/mmh3-5.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1fdb36b940e9261aff0b5177c5b74a36936b902f473180f6c15bde26143681a9", size = 110089, upload-time = "2025-07-29T07:42:46.122Z" }, - { url = "https://files.pythonhosted.org/packages/3b/a2/d20f3f5c95e9c511806686c70d0a15479cc3941c5f322061697af1c1ff70/mmh3-5.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7303aab41e97adcf010a09efd8f1403e719e59b7705d5e3cfed3dd7571589290", size = 97571, upload-time = "2025-07-29T07:42:47.18Z" }, - { url = "https://files.pythonhosted.org/packages/7b/23/665296fce4f33488deec39a750ffd245cfc07aafb0e3ef37835f91775d14/mmh3-5.2.0-cp313-cp313-win32.whl", hash = "sha256:03e08c6ebaf666ec1e3d6ea657a2d363bb01effd1a9acfe41f9197decaef0051", size = 40806, upload-time = "2025-07-29T07:42:48.166Z" }, - { url = "https://files.pythonhosted.org/packages/59/b0/92e7103f3b20646e255b699e2d0327ce53a3f250e44367a99dc8be0b7c7a/mmh3-5.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:7fddccd4113e7b736706e17a239a696332360cbaddf25ae75b57ba1acce65081", size = 41600, upload-time = "2025-07-29T07:42:49.371Z" }, - { url = "https://files.pythonhosted.org/packages/99/22/0b2bd679a84574647de538c5b07ccaa435dbccc37815067fe15b90fe8dad/mmh3-5.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa0c966ee727aad5406d516375593c5f058c766b21236ab8985693934bb5085b", size = 39349, upload-time = "2025-07-29T07:42:50.268Z" }, - { url = "https://files.pythonhosted.org/packages/f7/ca/a20db059a8a47048aaf550da14a145b56e9c7386fb8280d3ce2962dcebf7/mmh3-5.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e5015f0bb6eb50008bed2d4b1ce0f2a294698a926111e4bb202c0987b4f89078", size = 39209, upload-time = "2025-07-29T07:42:51.559Z" }, - { url = "https://files.pythonhosted.org/packages/98/dd/e5094799d55c7482d814b979a0fd608027d0af1b274bfb4c3ea3e950bfd5/mmh3-5.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:e0f3ed828d709f5b82d8bfe14f8856120718ec4bd44a5b26102c3030a1e12501", size = 39843, upload-time = "2025-07-29T07:42:52.536Z" }, - { url = "https://files.pythonhosted.org/packages/f4/6b/7844d7f832c85400e7cc89a1348e4e1fdd38c5a38415bb5726bbb8fcdb6c/mmh3-5.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:f35727c5118aba95f0397e18a1a5b8405425581bfe53e821f0fb444cbdc2bc9b", size = 40648, upload-time = "2025-07-29T07:42:53.392Z" }, - { url = "https://files.pythonhosted.org/packages/1f/bf/71f791f48a21ff3190ba5225807cbe4f7223360e96862c376e6e3fb7efa7/mmh3-5.2.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bc244802ccab5220008cb712ca1508cb6a12f0eb64ad62997156410579a1770", size = 56164, upload-time = "2025-07-29T07:42:54.267Z" }, - { url = "https://files.pythonhosted.org/packages/70/1f/f87e3d34d83032b4f3f0f528c6d95a98290fcacf019da61343a49dccfd51/mmh3-5.2.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ff3d50dc3fe8a98059f99b445dfb62792b5d006c5e0b8f03c6de2813b8376110", size = 40692, upload-time = "2025-07-29T07:42:55.234Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e2/db849eaed07117086f3452feca8c839d30d38b830ac59fe1ce65af8be5ad/mmh3-5.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:37a358cc881fe796e099c1db6ce07ff757f088827b4e8467ac52b7a7ffdca647", size = 40068, upload-time = "2025-07-29T07:42:56.158Z" }, - { url = "https://files.pythonhosted.org/packages/df/6b/209af927207af77425b044e32f77f49105a0b05d82ff88af6971d8da4e19/mmh3-5.2.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b9a87025121d1c448f24f27ff53a5fe7b6ef980574b4a4f11acaabe702420d63", size = 97367, upload-time = "2025-07-29T07:42:57.037Z" }, - { url = "https://files.pythonhosted.org/packages/ca/e0/78adf4104c425606a9ce33fb351f790c76a6c2314969c4a517d1ffc92196/mmh3-5.2.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ba55d6ca32eeef8b2625e1e4bfc3b3db52bc63014bd7e5df8cc11bf2b036b12", size = 103306, upload-time = "2025-07-29T07:42:58.522Z" }, - { url = "https://files.pythonhosted.org/packages/a3/79/c2b89f91b962658b890104745b1b6c9ce38d50a889f000b469b91eeb1b9e/mmh3-5.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9ff37ba9f15637e424c2ab57a1a590c52897c845b768e4e0a4958084ec87f22", size = 106312, upload-time = "2025-07-29T07:42:59.552Z" }, - { url = "https://files.pythonhosted.org/packages/4b/14/659d4095528b1a209be90934778c5ffe312177d51e365ddcbca2cac2ec7c/mmh3-5.2.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a094319ec0db52a04af9fdc391b4d39a1bc72bc8424b47c4411afb05413a44b5", size = 113135, upload-time = "2025-07-29T07:43:00.745Z" }, - { url = "https://files.pythonhosted.org/packages/8d/6f/cd7734a779389a8a467b5c89a48ff476d6f2576e78216a37551a97e9e42a/mmh3-5.2.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c5584061fd3da584659b13587f26c6cad25a096246a481636d64375d0c1f6c07", size = 120775, upload-time = "2025-07-29T07:43:02.124Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ca/8256e3b96944408940de3f9291d7e38a283b5761fe9614d4808fcf27bd62/mmh3-5.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecbfc0437ddfdced5e7822d1ce4855c9c64f46819d0fdc4482c53f56c707b935", size = 99178, upload-time = "2025-07-29T07:43:03.182Z" }, - { url = "https://files.pythonhosted.org/packages/8a/32/39e2b3cf06b6e2eb042c984dab8680841ac2a0d3ca6e0bea30db1f27b565/mmh3-5.2.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:7b986d506a8e8ea345791897ba5d8ba0d9d8820cd4fc3e52dbe6de19388de2e7", size = 98738, upload-time = "2025-07-29T07:43:04.207Z" }, - { url = "https://files.pythonhosted.org/packages/61/d3/7bbc8e0e8cf65ebbe1b893ffa0467b7ecd1bd07c3bbf6c9db4308ada22ec/mmh3-5.2.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:38d899a156549da8ef6a9f1d6f7ef231228d29f8f69bce2ee12f5fba6d6fd7c5", size = 106510, upload-time = "2025-07-29T07:43:05.656Z" }, - { url = "https://files.pythonhosted.org/packages/10/99/b97e53724b52374e2f3859046f0eb2425192da356cb19784d64bc17bb1cf/mmh3-5.2.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d86651fa45799530885ba4dab3d21144486ed15285e8784181a0ab37a4552384", size = 110053, upload-time = "2025-07-29T07:43:07.204Z" }, - { url = "https://files.pythonhosted.org/packages/ac/62/3688c7d975ed195155671df68788c83fed6f7909b6ec4951724c6860cb97/mmh3-5.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c463d7c1c4cfc9d751efeaadd936bbba07b5b0ed81a012b3a9f5a12f0872bd6e", size = 97546, upload-time = "2025-07-29T07:43:08.226Z" }, - { url = "https://files.pythonhosted.org/packages/ca/3b/c6153250f03f71a8b7634cded82939546cdfba02e32f124ff51d52c6f991/mmh3-5.2.0-cp314-cp314-win32.whl", hash = "sha256:bb4fe46bdc6104fbc28db7a6bacb115ee6368ff993366bbd8a2a7f0076e6f0c0", size = 41422, upload-time = "2025-07-29T07:43:09.216Z" }, - { url = "https://files.pythonhosted.org/packages/74/01/a27d98bab083a435c4c07e9d1d720d4c8a578bf4c270bae373760b1022be/mmh3-5.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:7c7f0b342fd06044bedd0b6e72177ddc0076f54fd89ee239447f8b271d919d9b", size = 42135, upload-time = "2025-07-29T07:43:10.183Z" }, - { url = "https://files.pythonhosted.org/packages/cb/c9/dbba5507e95429b8b380e2ba091eff5c20a70a59560934dff0ad8392b8c8/mmh3-5.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:3193752fc05ea72366c2b63ff24b9a190f422e32d75fdeae71087c08fff26115", size = 39879, upload-time = "2025-07-29T07:43:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/b5/d1/c8c0ef839c17258b9de41b84f663574fabcf8ac2007b7416575e0f65ff6e/mmh3-5.2.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:69fc339d7202bea69ef9bd7c39bfdf9fdabc8e6822a01eba62fb43233c1b3932", size = 57696, upload-time = "2025-07-29T07:43:11.989Z" }, - { url = "https://files.pythonhosted.org/packages/2f/55/95e2b9ff201e89f9fe37036037ab61a6c941942b25cdb7b6a9df9b931993/mmh3-5.2.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:12da42c0a55c9d86ab566395324213c319c73ecb0c239fad4726324212b9441c", size = 41421, upload-time = "2025-07-29T07:43:13.269Z" }, - { url = "https://files.pythonhosted.org/packages/77/79/9be23ad0b7001a4b22752e7693be232428ecc0a35068a4ff5c2f14ef8b20/mmh3-5.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f7f9034c7cf05ddfaac8d7a2e63a3c97a840d4615d0a0e65ba8bdf6f8576e3be", size = 40853, upload-time = "2025-07-29T07:43:14.888Z" }, - { url = "https://files.pythonhosted.org/packages/ac/1b/96b32058eda1c1dee8264900c37c359a7325c1f11f5ff14fd2be8e24eff9/mmh3-5.2.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:11730eeb16dfcf9674fdea9bb6b8e6dd9b40813b7eb839bc35113649eef38aeb", size = 109694, upload-time = "2025-07-29T07:43:15.816Z" }, - { url = "https://files.pythonhosted.org/packages/8d/6f/a2ae44cd7dad697b6dea48390cbc977b1e5ca58fda09628cbcb2275af064/mmh3-5.2.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:932a6eec1d2e2c3c9e630d10f7128d80e70e2d47fe6b8c7ea5e1afbd98733e65", size = 117438, upload-time = "2025-07-29T07:43:16.865Z" }, - { url = "https://files.pythonhosted.org/packages/a0/08/bfb75451c83f05224a28afeaf3950c7b793c0b71440d571f8e819cfb149a/mmh3-5.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ca975c51c5028947bbcfc24966517aac06a01d6c921e30f7c5383c195f87991", size = 120409, upload-time = "2025-07-29T07:43:18.207Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ea/8b118b69b2ff8df568f742387d1a159bc654a0f78741b31437dd047ea28e/mmh3-5.2.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5b0b58215befe0f0e120b828f7645e97719bbba9f23b69e268ed0ac7adde8645", size = 125909, upload-time = "2025-07-29T07:43:19.39Z" }, - { url = "https://files.pythonhosted.org/packages/3e/11/168cc0b6a30650032e351a3b89b8a47382da541993a03af91e1ba2501234/mmh3-5.2.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29c2b9ce61886809d0492a274a5a53047742dea0f703f9c4d5d223c3ea6377d3", size = 135331, upload-time = "2025-07-29T07:43:20.435Z" }, - { url = "https://files.pythonhosted.org/packages/31/05/e3a9849b1c18a7934c64e831492c99e67daebe84a8c2f2c39a7096a830e3/mmh3-5.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a367d4741ac0103f8198c82f429bccb9359f543ca542b06a51f4f0332e8de279", size = 110085, upload-time = "2025-07-29T07:43:21.92Z" }, - { url = "https://files.pythonhosted.org/packages/d9/d5/a96bcc306e3404601418b2a9a370baec92af84204528ba659fdfe34c242f/mmh3-5.2.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:5a5dba98e514fb26241868f6eb90a7f7ca0e039aed779342965ce24ea32ba513", size = 111195, upload-time = "2025-07-29T07:43:23.066Z" }, - { url = "https://files.pythonhosted.org/packages/af/29/0fd49801fec5bff37198684e0849b58e0dab3a2a68382a357cfffb0fafc3/mmh3-5.2.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:941603bfd75a46023807511c1ac2f1b0f39cccc393c15039969806063b27e6db", size = 116919, upload-time = "2025-07-29T07:43:24.178Z" }, - { url = "https://files.pythonhosted.org/packages/2d/04/4f3c32b0a2ed762edca45d8b46568fc3668e34f00fb1e0a3b5451ec1281c/mmh3-5.2.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:132dd943451a7c7546978863d2f5a64977928410782e1a87d583cb60eb89e667", size = 123160, upload-time = "2025-07-29T07:43:25.26Z" }, - { url = "https://files.pythonhosted.org/packages/91/76/3d29eaa38821730633d6a240d36fa8ad2807e9dfd432c12e1a472ed211eb/mmh3-5.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f698733a8a494466432d611a8f0d1e026f5286dee051beea4b3c3146817e35d5", size = 110206, upload-time = "2025-07-29T07:43:26.699Z" }, - { url = "https://files.pythonhosted.org/packages/44/1c/ccf35892684d3a408202e296e56843743e0b4fb1629e59432ea88cdb3909/mmh3-5.2.0-cp314-cp314t-win32.whl", hash = "sha256:6d541038b3fc360ec538fc116de87462627944765a6750308118f8b509a8eec7", size = 41970, upload-time = "2025-07-29T07:43:27.666Z" }, - { url = "https://files.pythonhosted.org/packages/75/b2/b9e4f1e5adb5e21eb104588fcee2cd1eaa8308255173481427d5ecc4284e/mmh3-5.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e912b19cf2378f2967d0c08e86ff4c6c360129887f678e27e4dde970d21b3f4d", size = 43063, upload-time = "2025-07-29T07:43:28.582Z" }, - { url = "https://files.pythonhosted.org/packages/6a/fc/0e61d9a4e29c8679356795a40e48f647b4aad58d71bfc969f0f8f56fb912/mmh3-5.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:e7884931fe5e788163e7b3c511614130c2c59feffdc21112290a194487efb2e9", size = 40455, upload-time = "2025-07-29T07:43:29.563Z" }, -] - -[[package]] -name = "mpmath" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, -] - -[[package]] -name = "multidict" -version = "6.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, - { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, - { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, - { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, - { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, - { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, - { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, - { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, - { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, - { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, - { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, - { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, - { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, - { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, - { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, - { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, - { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, - { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, - { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, - { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, - { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, - { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, - { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, - { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, - { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, - { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, - { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, - { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, - { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, - { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, - { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, - { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, - { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, - { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, - { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, - { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, - { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, - { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, - { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, - { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, - { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, - { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, - { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, - { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, - { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, - { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, - { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, - { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, - { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, - { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, - { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, - { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, - { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, - { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, - { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, - { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, - { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, - { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, - { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, - { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, - { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, - { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, - { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, - { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, - { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, - { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, - { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, - { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, - { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, - { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, - { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, - { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, - { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, - { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, - { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, - { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, - { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, -] - -[[package]] -name = "mypy" -version = "1.19.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, - { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, - { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, - { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, - { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, - { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, - { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, - { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, - { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, - { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, - { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, - { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, - { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, - { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, -] - -[[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 = "networkx" -version = "3.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, -] - -[[package]] -name = "numpy" -version = "2.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/fd/0005efbd0af48e55eb3c7208af93f2862d4b1a56cd78e84309a2d959208d/numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae", size = 20723651, upload-time = "2026-01-31T23:13:10.135Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/6e/6f394c9c77668153e14d4da83bcc247beb5952f6ead7699a1a2992613bea/numpy-2.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:21982668592194c609de53ba4933a7471880ccbaadcc52352694a59ecc860b3a", size = 16667963, upload-time = "2026-01-31T23:10:52.147Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f8/55483431f2b2fd015ae6ed4fe62288823ce908437ed49db5a03d15151678/numpy-2.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40397bda92382fcec844066efb11f13e1c9a3e2a8e8f318fb72ed8b6db9f60f1", size = 14693571, upload-time = "2026-01-31T23:10:54.789Z" }, - { url = "https://files.pythonhosted.org/packages/2f/20/18026832b1845cdc82248208dd929ca14c9d8f2bac391f67440707fff27c/numpy-2.4.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b3a24467af63c67829bfaa61eecf18d5432d4f11992688537be59ecd6ad32f5e", size = 5203469, upload-time = "2026-01-31T23:10:57.343Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/2eb97c8a77daaba34eaa3fa7241a14ac5f51c46a6bd5911361b644c4a1e2/numpy-2.4.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:805cc8de9fd6e7a22da5aed858e0ab16be5a4db6c873dde1d7451c541553aa27", size = 6550820, upload-time = "2026-01-31T23:10:59.429Z" }, - { url = "https://files.pythonhosted.org/packages/b1/91/b97fdfd12dc75b02c44e26c6638241cc004d4079a0321a69c62f51470c4c/numpy-2.4.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d82351358ffbcdcd7b686b90742a9b86632d6c1c051016484fa0b326a0a1548", size = 15663067, upload-time = "2026-01-31T23:11:01.291Z" }, - { url = "https://files.pythonhosted.org/packages/f5/c6/a18e59f3f0b8071cc85cbc8d80cd02d68aa9710170b2553a117203d46936/numpy-2.4.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e35d3e0144137d9fdae62912e869136164534d64a169f86438bc9561b6ad49f", size = 16619782, upload-time = "2026-01-31T23:11:03.669Z" }, - { url = "https://files.pythonhosted.org/packages/b7/83/9751502164601a79e18847309f5ceec0b1446d7b6aa12305759b72cf98b2/numpy-2.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adb6ed2ad29b9e15321d167d152ee909ec73395901b70936f029c3bc6d7f4460", size = 17013128, upload-time = "2026-01-31T23:11:05.913Z" }, - { url = "https://files.pythonhosted.org/packages/61/c4/c4066322256ec740acc1c8923a10047818691d2f8aec254798f3dd90f5f2/numpy-2.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8906e71fd8afcb76580404e2a950caef2685df3d2a57fe82a86ac8d33cc007ba", size = 18345324, upload-time = "2026-01-31T23:11:08.248Z" }, - { url = "https://files.pythonhosted.org/packages/ab/af/6157aa6da728fa4525a755bfad486ae7e3f76d4c1864138003eb84328497/numpy-2.4.2-cp312-cp312-win32.whl", hash = "sha256:ec055f6dae239a6299cace477b479cca2fc125c5675482daf1dd886933a1076f", size = 5960282, upload-time = "2026-01-31T23:11:10.497Z" }, - { url = "https://files.pythonhosted.org/packages/92/0f/7ceaaeaacb40567071e94dbf2c9480c0ae453d5bb4f52bea3892c39dc83c/numpy-2.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:209fae046e62d0ce6435fcfe3b1a10537e858249b3d9b05829e2a05218296a85", size = 12314210, upload-time = "2026-01-31T23:11:12.176Z" }, - { url = "https://files.pythonhosted.org/packages/2f/a3/56c5c604fae6dd40fa2ed3040d005fca97e91bd320d232ac9931d77ba13c/numpy-2.4.2-cp312-cp312-win_arm64.whl", hash = "sha256:fbde1b0c6e81d56f5dccd95dd4a711d9b95df1ae4009a60887e56b27e8d903fa", size = 10220171, upload-time = "2026-01-31T23:11:14.684Z" }, - { url = "https://files.pythonhosted.org/packages/a1/22/815b9fe25d1d7ae7d492152adbc7226d3eff731dffc38fe970589fcaaa38/numpy-2.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25f2059807faea4b077a2b6837391b5d830864b3543627f381821c646f31a63c", size = 16663696, upload-time = "2026-01-31T23:11:17.516Z" }, - { url = "https://files.pythonhosted.org/packages/09/f0/817d03a03f93ba9c6c8993de509277d84e69f9453601915e4a69554102a1/numpy-2.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bd3a7a9f5847d2fb8c2c6d1c862fa109c31a9abeca1a3c2bd5a64572955b2979", size = 14688322, upload-time = "2026-01-31T23:11:19.883Z" }, - { url = "https://files.pythonhosted.org/packages/da/b4/f805ab79293c728b9a99438775ce51885fd4f31b76178767cfc718701a39/numpy-2.4.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8e4549f8a3c6d13d55041925e912bfd834285ef1dd64d6bc7d542583355e2e98", size = 5198157, upload-time = "2026-01-31T23:11:22.375Z" }, - { url = "https://files.pythonhosted.org/packages/74/09/826e4289844eccdcd64aac27d13b0fd3f32039915dd5b9ba01baae1f436c/numpy-2.4.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:aea4f66ff44dfddf8c2cffd66ba6538c5ec67d389285292fe428cb2c738c8aef", size = 6546330, upload-time = "2026-01-31T23:11:23.958Z" }, - { url = "https://files.pythonhosted.org/packages/19/fb/cbfdbfa3057a10aea5422c558ac57538e6acc87ec1669e666d32ac198da7/numpy-2.4.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3cd545784805de05aafe1dde61752ea49a359ccba9760c1e5d1c88a93bbf2b7", size = 15660968, upload-time = "2026-01-31T23:11:25.713Z" }, - { url = "https://files.pythonhosted.org/packages/04/dc/46066ce18d01645541f0186877377b9371b8fa8017fa8262002b4ef22612/numpy-2.4.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0d9b7c93578baafcbc5f0b83eaf17b79d345c6f36917ba0c67f45226911d499", size = 16607311, upload-time = "2026-01-31T23:11:28.117Z" }, - { url = "https://files.pythonhosted.org/packages/14/d9/4b5adfc39a43fa6bf918c6d544bc60c05236cc2f6339847fc5b35e6cb5b0/numpy-2.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f74f0f7779cc7ae07d1810aab8ac6b1464c3eafb9e283a40da7309d5e6e48fbb", size = 17012850, upload-time = "2026-01-31T23:11:30.888Z" }, - { url = "https://files.pythonhosted.org/packages/b7/20/adb6e6adde6d0130046e6fdfb7675cc62bc2f6b7b02239a09eb58435753d/numpy-2.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7ac672d699bf36275c035e16b65539931347d68b70667d28984c9fb34e07fa7", size = 18334210, upload-time = "2026-01-31T23:11:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/78/0e/0a73b3dff26803a8c02baa76398015ea2a5434d9b8265a7898a6028c1591/numpy-2.4.2-cp313-cp313-win32.whl", hash = "sha256:8e9afaeb0beff068b4d9cd20d322ba0ee1cecfb0b08db145e4ab4dd44a6b5110", size = 5958199, upload-time = "2026-01-31T23:11:35.385Z" }, - { url = "https://files.pythonhosted.org/packages/43/bc/6352f343522fcb2c04dbaf94cb30cca6fd32c1a750c06ad6231b4293708c/numpy-2.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:7df2de1e4fba69a51c06c28f5a3de36731eb9639feb8e1cf7e4a7b0daf4cf622", size = 12310848, upload-time = "2026-01-31T23:11:38.001Z" }, - { url = "https://files.pythonhosted.org/packages/6e/8d/6da186483e308da5da1cc6918ce913dcfe14ffde98e710bfeff2a6158d4e/numpy-2.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:0fece1d1f0a89c16b03442eae5c56dc0be0c7883b5d388e0c03f53019a4bfd71", size = 10221082, upload-time = "2026-01-31T23:11:40.392Z" }, - { url = "https://files.pythonhosted.org/packages/25/a1/9510aa43555b44781968935c7548a8926274f815de42ad3997e9e83680dd/numpy-2.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5633c0da313330fd20c484c78cdd3f9b175b55e1a766c4a174230c6b70ad8262", size = 14815866, upload-time = "2026-01-31T23:11:42.495Z" }, - { url = "https://files.pythonhosted.org/packages/36/30/6bbb5e76631a5ae46e7923dd16ca9d3f1c93cfa8d4ed79a129814a9d8db3/numpy-2.4.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d9f64d786b3b1dd742c946c42d15b07497ed14af1a1f3ce840cce27daa0ce913", size = 5325631, upload-time = "2026-01-31T23:11:44.7Z" }, - { url = "https://files.pythonhosted.org/packages/46/00/3a490938800c1923b567b3a15cd17896e68052e2145d8662aaf3e1ffc58f/numpy-2.4.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:b21041e8cb6a1eb5312dd1d2f80a94d91efffb7a06b70597d44f1bd2dfc315ab", size = 6646254, upload-time = "2026-01-31T23:11:46.341Z" }, - { url = "https://files.pythonhosted.org/packages/d3/e9/fac0890149898a9b609caa5af7455a948b544746e4b8fe7c212c8edd71f8/numpy-2.4.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00ab83c56211a1d7c07c25e3217ea6695e50a3e2f255053686b081dc0b091a82", size = 15720138, upload-time = "2026-01-31T23:11:48.082Z" }, - { url = "https://files.pythonhosted.org/packages/ea/5c/08887c54e68e1e28df53709f1893ce92932cc6f01f7c3d4dc952f61ffd4e/numpy-2.4.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fb882da679409066b4603579619341c6d6898fc83a8995199d5249f986e8e8f", size = 16655398, upload-time = "2026-01-31T23:11:50.293Z" }, - { url = "https://files.pythonhosted.org/packages/4d/89/253db0fa0e66e9129c745e4ef25631dc37d5f1314dad2b53e907b8538e6d/numpy-2.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66cb9422236317f9d44b67b4d18f44efe6e9c7f8794ac0462978513359461554", size = 17079064, upload-time = "2026-01-31T23:11:52.927Z" }, - { url = "https://files.pythonhosted.org/packages/2a/d5/cbade46ce97c59c6c3da525e8d95b7abe8a42974a1dc5c1d489c10433e88/numpy-2.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0f01dcf33e73d80bd8dc0f20a71303abbafa26a19e23f6b68d1aa9990af90257", size = 18379680, upload-time = "2026-01-31T23:11:55.22Z" }, - { url = "https://files.pythonhosted.org/packages/40/62/48f99ae172a4b63d981babe683685030e8a3df4f246c893ea5c6ef99f018/numpy-2.4.2-cp313-cp313t-win32.whl", hash = "sha256:52b913ec40ff7ae845687b0b34d8d93b60cb66dcee06996dd5c99f2fc9328657", size = 6082433, upload-time = "2026-01-31T23:11:58.096Z" }, - { url = "https://files.pythonhosted.org/packages/07/38/e054a61cfe48ad9f1ed0d188e78b7e26859d0b60ef21cd9de4897cdb5326/numpy-2.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5eea80d908b2c1f91486eb95b3fb6fab187e569ec9752ab7d9333d2e66bf2d6b", size = 12451181, upload-time = "2026-01-31T23:11:59.782Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a4/a05c3a6418575e185dd84d0b9680b6bb2e2dc3e4202f036b7b4e22d6e9dc/numpy-2.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fd49860271d52127d61197bb50b64f58454e9f578cb4b2c001a6de8b1f50b0b1", size = 10290756, upload-time = "2026-01-31T23:12:02.438Z" }, - { url = "https://files.pythonhosted.org/packages/18/88/b7df6050bf18fdcfb7046286c6535cabbdd2064a3440fca3f069d319c16e/numpy-2.4.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b", size = 16663092, upload-time = "2026-01-31T23:12:04.521Z" }, - { url = "https://files.pythonhosted.org/packages/25/7a/1fee4329abc705a469a4afe6e69b1ef7e915117747886327104a8493a955/numpy-2.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000", size = 14698770, upload-time = "2026-01-31T23:12:06.96Z" }, - { url = "https://files.pythonhosted.org/packages/fb/0b/f9e49ba6c923678ad5bc38181c08ac5e53b7a5754dbca8e581aa1a56b1ff/numpy-2.4.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1", size = 5208562, upload-time = "2026-01-31T23:12:09.632Z" }, - { url = "https://files.pythonhosted.org/packages/7d/12/d7de8f6f53f9bb76997e5e4c069eda2051e3fe134e9181671c4391677bb2/numpy-2.4.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74", size = 6543710, upload-time = "2026-01-31T23:12:11.969Z" }, - { url = "https://files.pythonhosted.org/packages/09/63/c66418c2e0268a31a4cf8a8b512685748200f8e8e8ec6c507ce14e773529/numpy-2.4.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a", size = 15677205, upload-time = "2026-01-31T23:12:14.33Z" }, - { url = "https://files.pythonhosted.org/packages/5d/6c/7f237821c9642fb2a04d2f1e88b4295677144ca93285fd76eff3bcba858d/numpy-2.4.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325", size = 16611738, upload-time = "2026-01-31T23:12:16.525Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a7/39c4cdda9f019b609b5c473899d87abff092fc908cfe4d1ecb2fcff453b0/numpy-2.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909", size = 17028888, upload-time = "2026-01-31T23:12:19.306Z" }, - { url = "https://files.pythonhosted.org/packages/da/b3/e84bb64bdfea967cc10950d71090ec2d84b49bc691df0025dddb7c26e8e3/numpy-2.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a", size = 18339556, upload-time = "2026-01-31T23:12:21.816Z" }, - { url = "https://files.pythonhosted.org/packages/88/f5/954a291bc1192a27081706862ac62bb5920fbecfbaa302f64682aa90beed/numpy-2.4.2-cp314-cp314-win32.whl", hash = "sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a", size = 6006899, upload-time = "2026-01-31T23:12:24.14Z" }, - { url = "https://files.pythonhosted.org/packages/05/cb/eff72a91b2efdd1bc98b3b8759f6a1654aa87612fc86e3d87d6fe4f948c4/numpy-2.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75", size = 12443072, upload-time = "2026-01-31T23:12:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/37/75/62726948db36a56428fce4ba80a115716dc4fad6a3a4352487f8bb950966/numpy-2.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05", size = 10494886, upload-time = "2026-01-31T23:12:28.488Z" }, - { url = "https://files.pythonhosted.org/packages/36/2f/ee93744f1e0661dc267e4b21940870cabfae187c092e1433b77b09b50ac4/numpy-2.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308", size = 14818567, upload-time = "2026-01-31T23:12:30.709Z" }, - { url = "https://files.pythonhosted.org/packages/a7/24/6535212add7d76ff938d8bdc654f53f88d35cddedf807a599e180dcb8e66/numpy-2.4.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef", size = 5328372, upload-time = "2026-01-31T23:12:32.962Z" }, - { url = "https://files.pythonhosted.org/packages/5e/9d/c48f0a035725f925634bf6b8994253b43f2047f6778a54147d7e213bc5a7/numpy-2.4.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d", size = 6649306, upload-time = "2026-01-31T23:12:34.797Z" }, - { url = "https://files.pythonhosted.org/packages/81/05/7c73a9574cd4a53a25907bad38b59ac83919c0ddc8234ec157f344d57d9a/numpy-2.4.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8", size = 15722394, upload-time = "2026-01-31T23:12:36.565Z" }, - { url = "https://files.pythonhosted.org/packages/35/fa/4de10089f21fc7d18442c4a767ab156b25c2a6eaf187c0db6d9ecdaeb43f/numpy-2.4.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5", size = 16653343, upload-time = "2026-01-31T23:12:39.188Z" }, - { url = "https://files.pythonhosted.org/packages/b8/f9/d33e4ffc857f3763a57aa85650f2e82486832d7492280ac21ba9efda80da/numpy-2.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e", size = 17078045, upload-time = "2026-01-31T23:12:42.041Z" }, - { url = "https://files.pythonhosted.org/packages/c8/b8/54bdb43b6225badbea6389fa038c4ef868c44f5890f95dd530a218706da3/numpy-2.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a", size = 18380024, upload-time = "2026-01-31T23:12:44.331Z" }, - { url = "https://files.pythonhosted.org/packages/a5/55/6e1a61ded7af8df04016d81b5b02daa59f2ea9252ee0397cb9f631efe9e5/numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443", size = 6153937, upload-time = "2026-01-31T23:12:47.229Z" }, - { url = "https://files.pythonhosted.org/packages/45/aa/fa6118d1ed6d776b0983f3ceac9b1a5558e80df9365b1c3aa6d42bf9eee4/numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236", size = 12631844, upload-time = "2026-01-31T23:12:48.997Z" }, - { url = "https://files.pythonhosted.org/packages/32/0a/2ec5deea6dcd158f254a7b372fb09cfba5719419c8d66343bab35237b3fb/numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181", size = 10565379, upload-time = "2026-01-31T23:12:51.345Z" }, -] - -[[package]] -name = "nvidia-cublas-cu12" -version = "12.8.4.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, -] - -[[package]] -name = "nvidia-cuda-cupti-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, -] - -[[package]] -name = "nvidia-cuda-nvrtc-cu12" -version = "12.8.93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, -] - -[[package]] -name = "nvidia-cuda-runtime-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, -] - -[[package]] -name = "nvidia-cudnn-cu12" -version = "9.10.2.21" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, -] - -[[package]] -name = "nvidia-cufft-cu12" -version = "11.3.3.83" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, -] - -[[package]] -name = "nvidia-cufile-cu12" -version = "1.13.1.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, -] - -[[package]] -name = "nvidia-curand-cu12" -version = "10.3.9.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, -] - -[[package]] -name = "nvidia-cusolver-cu12" -version = "11.7.3.90" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12" }, - { name = "nvidia-cusparse-cu12" }, - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, -] - -[[package]] -name = "nvidia-cusparse-cu12" -version = "12.5.8.93" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, -] - -[[package]] -name = "nvidia-cusparselt-cu12" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, -] - -[[package]] -name = "nvidia-nccl-cu12" -version = "2.27.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, -] - -[[package]] -name = "nvidia-nvjitlink-cu12" -version = "12.8.93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, -] - -[[package]] -name = "nvidia-nvshmem-cu12" -version = "3.4.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095, upload-time = "2025-09-06T00:32:31.266Z" }, -] - -[[package]] -name = "nvidia-nvtx-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, -] - -[[package]] -name = "oauthlib" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" }, -] - -[[package]] -name = "onnxruntime" -version = "1.23.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coloredlogs" }, - { name = "flatbuffers" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "sympy" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/9e/f748cd64161213adeef83d0cb16cb8ace1e62fa501033acdd9f9341fff57/onnxruntime-1.23.2-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:b8f029a6b98d3cf5be564d52802bb50a8489ab73409fa9db0bf583eabb7c2321", size = 17195929, upload-time = "2025-10-22T03:47:36.24Z" }, - { url = "https://files.pythonhosted.org/packages/91/9d/a81aafd899b900101988ead7fb14974c8a58695338ab6a0f3d6b0100f30b/onnxruntime-1.23.2-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:218295a8acae83905f6f1aed8cacb8e3eb3bd7513a13fe4ba3b2664a19fc4a6b", size = 19157705, upload-time = "2025-10-22T03:46:40.415Z" }, - { url = "https://files.pythonhosted.org/packages/3c/35/4e40f2fba272a6698d62be2cd21ddc3675edfc1a4b9ddefcc4648f115315/onnxruntime-1.23.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76ff670550dc23e58ea9bc53b5149b99a44e63b34b524f7b8547469aaa0dcb8c", size = 15226915, upload-time = "2025-10-22T03:46:27.773Z" }, - { url = "https://files.pythonhosted.org/packages/ef/88/9cc25d2bafe6bc0d4d3c1db3ade98196d5b355c0b273e6a5dc09c5d5d0d5/onnxruntime-1.23.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f9b4ae77f8e3c9bee50c27bc1beede83f786fe1d52e99ac85aa8d65a01e9b77", size = 17382649, upload-time = "2025-10-22T03:47:02.782Z" }, - { url = "https://files.pythonhosted.org/packages/c0/b4/569d298f9fc4d286c11c45e85d9ffa9e877af12ace98af8cab52396e8f46/onnxruntime-1.23.2-cp312-cp312-win_amd64.whl", hash = "sha256:25de5214923ce941a3523739d34a520aac30f21e631de53bba9174dc9c004435", size = 13470528, upload-time = "2025-10-22T03:47:28.106Z" }, - { url = "https://files.pythonhosted.org/packages/3d/41/fba0cabccecefe4a1b5fc8020c44febb334637f133acefc7ec492029dd2c/onnxruntime-1.23.2-cp313-cp313-macosx_13_0_arm64.whl", hash = "sha256:2ff531ad8496281b4297f32b83b01cdd719617e2351ffe0dba5684fb283afa1f", size = 17196337, upload-time = "2025-10-22T03:46:35.168Z" }, - { url = "https://files.pythonhosted.org/packages/fe/f9/2d49ca491c6a986acce9f1d1d5fc2099108958cc1710c28e89a032c9cfe9/onnxruntime-1.23.2-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:162f4ca894ec3de1a6fd53589e511e06ecdc3ff646849b62a9da7489dee9ce95", size = 19157691, upload-time = "2025-10-22T03:46:43.518Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a1/428ee29c6eaf09a6f6be56f836213f104618fb35ac6cc586ff0f477263eb/onnxruntime-1.23.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45d127d6e1e9b99d1ebeae9bcd8f98617a812f53f46699eafeb976275744826b", size = 15226898, upload-time = "2025-10-22T03:46:30.039Z" }, - { url = "https://files.pythonhosted.org/packages/f2/2b/b57c8a2466a3126dbe0a792f56ad7290949b02f47b86216cd47d857e4b77/onnxruntime-1.23.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8bace4e0d46480fbeeb7bbe1ffe1f080e6663a42d1086ff95c1551f2d39e7872", size = 17382518, upload-time = "2025-10-22T03:47:05.407Z" }, - { url = "https://files.pythonhosted.org/packages/4a/93/aba75358133b3a941d736816dd392f687e7eab77215a6e429879080b76b6/onnxruntime-1.23.2-cp313-cp313-win_amd64.whl", hash = "sha256:1f9cc0a55349c584f083c1c076e611a7c35d5b867d5d6e6d6c823bf821978088", size = 13470276, upload-time = "2025-10-22T03:47:31.193Z" }, - { url = "https://files.pythonhosted.org/packages/7c/3d/6830fa61c69ca8e905f237001dbfc01689a4e4ab06147020a4518318881f/onnxruntime-1.23.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9d2385e774f46ac38f02b3a91a91e30263d41b2f1f4f26ae34805b2a9ddef466", size = 15229610, upload-time = "2025-10-22T03:46:32.239Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ca/862b1e7a639460f0ca25fd5b6135fb42cf9deea86d398a92e44dfda2279d/onnxruntime-1.23.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2b9233c4947907fd1818d0e581c049c41ccc39b2856cc942ff6d26317cee145", size = 17394184, upload-time = "2025-10-22T03:47:08.127Z" }, -] - -[[package]] -name = "openai" -version = "2.16.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/6c/e4c964fcf1d527fdf4739e7cc940c60075a4114d50d03871d5d5b1e13a88/openai-2.16.0.tar.gz", hash = "sha256:42eaa22ca0d8ded4367a77374104d7a2feafee5bd60a107c3c11b5243a11cd12", size = 629649, upload-time = "2026-01-27T23:28:02.579Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/16/83/0315bf2cfd75a2ce8a7e54188e9456c60cec6c0cf66728ed07bd9859ff26/openai-2.16.0-py3-none-any.whl", hash = "sha256:5f46643a8f42899a84e80c38838135d7038e7718333ce61396994f887b09a59b", size = 1068612, upload-time = "2026-01-27T23:28:00.356Z" }, -] - -[[package]] -name = "opentelemetry-api" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, -] - -[[package]] -name = "opentelemetry-exporter-otlp-proto-common" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-proto" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e9/9d/22d241b66f7bbde88a3bfa6847a351d2c46b84de23e71222c6aae25c7050/opentelemetry_exporter_otlp_proto_common-1.39.1.tar.gz", hash = "sha256:763370d4737a59741c89a67b50f9e39271639ee4afc999dadfe768541c027464", size = 20409, upload-time = "2025-12-11T13:32:40.885Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/02/ffc3e143d89a27ac21fd557365b98bd0653b98de8a101151d5805b5d4c33/opentelemetry_exporter_otlp_proto_common-1.39.1-py3-none-any.whl", hash = "sha256:08f8a5862d64cc3435105686d0216c1365dc5701f86844a8cd56597d0c764fde", size = 18366, upload-time = "2025-12-11T13:32:20.2Z" }, -] - -[[package]] -name = "opentelemetry-exporter-otlp-proto-grpc" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "googleapis-common-protos" }, - { name = "grpcio" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-otlp-proto-common" }, - { name = "opentelemetry-proto" }, - { name = "opentelemetry-sdk" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/53/48/b329fed2c610c2c32c9366d9dc597202c9d1e58e631c137ba15248d8850f/opentelemetry_exporter_otlp_proto_grpc-1.39.1.tar.gz", hash = "sha256:772eb1c9287485d625e4dbe9c879898e5253fea111d9181140f51291b5fec3ad", size = 24650, upload-time = "2025-12-11T13:32:41.429Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/a3/cc9b66575bd6597b98b886a2067eea2693408d2d5f39dad9ab7fc264f5f3/opentelemetry_exporter_otlp_proto_grpc-1.39.1-py3-none-any.whl", hash = "sha256:fa1c136a05c7e9b4c09f739469cbdb927ea20b34088ab1d959a849b5cc589c18", size = 19766, upload-time = "2025-12-11T13:32:21.027Z" }, -] - -[[package]] -name = "opentelemetry-proto" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/1d/f25d76d8260c156c40c97c9ed4511ec0f9ce353f8108ca6e7561f82a06b2/opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8", size = 46152, upload-time = "2025-12-11T13:32:48.681Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/95/b40c96a7b5203005a0b03d8ce8cd212ff23f1793d5ba289c87a097571b18/opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007", size = 72535, upload-time = "2025-12-11T13:32:33.866Z" }, -] - -[[package]] -name = "opentelemetry-sdk" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, -] - -[[package]] -name = "orjson" -version = "3.11.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/45/b268004f745ede84e5798b48ee12b05129d19235d0e15267aa57dcdb400b/orjson-3.11.7.tar.gz", hash = "sha256:9b1a67243945819ce55d24a30b59d6a168e86220452d2c96f4d1f093e71c0c49", size = 6144992, upload-time = "2026-02-02T15:38:49.29Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/bf/76f4f1665f6983385938f0e2a5d7efa12a58171b8456c252f3bae8a4cf75/orjson-3.11.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bd03ea7606833655048dab1a00734a2875e3e86c276e1d772b2a02556f0d895f", size = 228545, upload-time = "2026-02-02T15:37:46.376Z" }, - { url = "https://files.pythonhosted.org/packages/79/53/6c72c002cb13b5a978a068add59b25a8bdf2800ac1c9c8ecdb26d6d97064/orjson-3.11.7-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:89e440ebc74ce8ab5c7bc4ce6757b4a6b1041becb127df818f6997b5c71aa60b", size = 125224, upload-time = "2026-02-02T15:37:47.697Z" }, - { url = "https://files.pythonhosted.org/packages/2c/83/10e48852865e5dd151bdfe652c06f7da484578ed02c5fca938e3632cb0b8/orjson-3.11.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ede977b5fe5ac91b1dffc0a517ca4542d2ec8a6a4ff7b2652d94f640796342a", size = 128154, upload-time = "2026-02-02T15:37:48.954Z" }, - { url = "https://files.pythonhosted.org/packages/6e/52/a66e22a2b9abaa374b4a081d410edab6d1e30024707b87eab7c734afe28d/orjson-3.11.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b7b1dae39230a393df353827c855a5f176271c23434cfd2db74e0e424e693e10", size = 123548, upload-time = "2026-02-02T15:37:50.187Z" }, - { url = "https://files.pythonhosted.org/packages/de/38/605d371417021359f4910c496f764c48ceb8997605f8c25bf1dfe58c0ebe/orjson-3.11.7-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed46f17096e28fb28d2975834836a639af7278aa87c84f68ab08fbe5b8bd75fa", size = 129000, upload-time = "2026-02-02T15:37:51.426Z" }, - { url = "https://files.pythonhosted.org/packages/44/98/af32e842b0ffd2335c89714d48ca4e3917b42f5d6ee5537832e069a4b3ac/orjson-3.11.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3726be79e36e526e3d9c1aceaadbfb4a04ee80a72ab47b3f3c17fefb9812e7b8", size = 141686, upload-time = "2026-02-02T15:37:52.607Z" }, - { url = "https://files.pythonhosted.org/packages/96/0b/fc793858dfa54be6feee940c1463370ece34b3c39c1ca0aa3845f5ba9892/orjson-3.11.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0724e265bc548af1dedebd9cb3d24b4e1c1e685a343be43e87ba922a5c5fff2f", size = 130812, upload-time = "2026-02-02T15:37:53.944Z" }, - { url = "https://files.pythonhosted.org/packages/dc/91/98a52415059db3f374757d0b7f0f16e3b5cd5976c90d1c2b56acaea039e6/orjson-3.11.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7745312efa9e11c17fbd3cb3097262d079da26930ae9ae7ba28fb738367cbad", size = 133440, upload-time = "2026-02-02T15:37:55.615Z" }, - { url = "https://files.pythonhosted.org/packages/dc/b6/cb540117bda61791f46381f8c26c8f93e802892830a6055748d3bb1925ab/orjson-3.11.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f904c24bdeabd4298f7a977ef14ca2a022ca921ed670b92ecd16ab6f3d01f867", size = 138386, upload-time = "2026-02-02T15:37:56.814Z" }, - { url = "https://files.pythonhosted.org/packages/63/1a/50a3201c334a7f17c231eee5f841342190723794e3b06293f26e7cf87d31/orjson-3.11.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b9fc4d0f81f394689e0814617aadc4f2ea0e8025f38c226cbf22d3b5ddbf025d", size = 408853, upload-time = "2026-02-02T15:37:58.291Z" }, - { url = "https://files.pythonhosted.org/packages/87/cd/8de1c67d0be44fdc22701e5989c0d015a2adf391498ad42c4dc589cd3013/orjson-3.11.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:849e38203e5be40b776ed2718e587faf204d184fc9a008ae441f9442320c0cab", size = 144130, upload-time = "2026-02-02T15:38:00.163Z" }, - { url = "https://files.pythonhosted.org/packages/0f/fe/d605d700c35dd55f51710d159fc54516a280923cd1b7e47508982fbb387d/orjson-3.11.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4682d1db3bcebd2b64757e0ddf9e87ae5f00d29d16c5cdf3a62f561d08cc3dd2", size = 134818, upload-time = "2026-02-02T15:38:01.507Z" }, - { url = "https://files.pythonhosted.org/packages/e4/e4/15ecc67edb3ddb3e2f46ae04475f2d294e8b60c1825fbe28a428b93b3fbd/orjson-3.11.7-cp312-cp312-win32.whl", hash = "sha256:f4f7c956b5215d949a1f65334cf9d7612dde38f20a95f2315deef167def91a6f", size = 127923, upload-time = "2026-02-02T15:38:02.75Z" }, - { url = "https://files.pythonhosted.org/packages/34/70/2e0855361f76198a3965273048c8e50a9695d88cd75811a5b46444895845/orjson-3.11.7-cp312-cp312-win_amd64.whl", hash = "sha256:bf742e149121dc5648ba0a08ea0871e87b660467ef168a3a5e53bc1fbd64bb74", size = 125007, upload-time = "2026-02-02T15:38:04.032Z" }, - { url = "https://files.pythonhosted.org/packages/68/40/c2051bd19fc467610fed469dc29e43ac65891571138f476834ca192bc290/orjson-3.11.7-cp312-cp312-win_arm64.whl", hash = "sha256:26c3b9132f783b7d7903bf1efb095fed8d4a3a85ec0d334ee8beff3d7a4749d5", size = 126089, upload-time = "2026-02-02T15:38:05.297Z" }, - { url = "https://files.pythonhosted.org/packages/89/25/6e0e52cac5aab51d7b6dcd257e855e1dec1c2060f6b28566c509b4665f62/orjson-3.11.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1d98b30cc1313d52d4af17d9c3d307b08389752ec5f2e5febdfada70b0f8c733", size = 228390, upload-time = "2026-02-02T15:38:06.8Z" }, - { url = "https://files.pythonhosted.org/packages/a5/29/a77f48d2fc8a05bbc529e5ff481fb43d914f9e383ea2469d4f3d51df3d00/orjson-3.11.7-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:d897e81f8d0cbd2abb82226d1860ad2e1ab3ff16d7b08c96ca00df9d45409ef4", size = 125189, upload-time = "2026-02-02T15:38:08.181Z" }, - { url = "https://files.pythonhosted.org/packages/89/25/0a16e0729a0e6a1504f9d1a13cdd365f030068aab64cec6958396b9969d7/orjson-3.11.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:814be4b49b228cfc0b3c565acf642dd7d13538f966e3ccde61f4f55be3e20785", size = 128106, upload-time = "2026-02-02T15:38:09.41Z" }, - { url = "https://files.pythonhosted.org/packages/66/da/a2e505469d60666a05ab373f1a6322eb671cb2ba3a0ccfc7d4bc97196787/orjson-3.11.7-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d06e5c5fed5caedd2e540d62e5b1c25e8c82431b9e577c33537e5fa4aa909539", size = 123363, upload-time = "2026-02-02T15:38:10.73Z" }, - { url = "https://files.pythonhosted.org/packages/23/bf/ed73f88396ea35c71b38961734ea4a4746f7ca0768bf28fd551d37e48dd0/orjson-3.11.7-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31c80ce534ac4ea3739c5ee751270646cbc46e45aea7576a38ffec040b4029a1", size = 129007, upload-time = "2026-02-02T15:38:12.138Z" }, - { url = "https://files.pythonhosted.org/packages/73/3c/b05d80716f0225fc9008fbf8ab22841dcc268a626aa550561743714ce3bf/orjson-3.11.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f50979824bde13d32b4320eedd513431c921102796d86be3eee0b58e58a3ecd1", size = 141667, upload-time = "2026-02-02T15:38:13.398Z" }, - { url = "https://files.pythonhosted.org/packages/61/e8/0be9b0addd9bf86abfc938e97441dcd0375d494594b1c8ad10fe57479617/orjson-3.11.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e54f3808e2b6b945078c41aa8d9b5834b28c50843846e97807e5adb75fa9705", size = 130832, upload-time = "2026-02-02T15:38:14.698Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ec/c68e3b9021a31d9ec15a94931db1410136af862955854ed5dd7e7e4f5bff/orjson-3.11.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12b80df61aab7b98b490fe9e4879925ba666fccdfcd175252ce4d9035865ace", size = 133373, upload-time = "2026-02-02T15:38:16.109Z" }, - { url = "https://files.pythonhosted.org/packages/d2/45/f3466739aaafa570cc8e77c6dbb853c48bf56e3b43738020e2661e08b0ac/orjson-3.11.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:996b65230271f1a97026fd0e6a753f51fbc0c335d2ad0c6201f711b0da32693b", size = 138307, upload-time = "2026-02-02T15:38:17.453Z" }, - { url = "https://files.pythonhosted.org/packages/e1/84/9f7f02288da1ffb31405c1be07657afd1eecbcb4b64ee2817b6fe0f785fa/orjson-3.11.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ab49d4b2a6a1d415ddb9f37a21e02e0d5dbfe10b7870b21bf779fc21e9156157", size = 408695, upload-time = "2026-02-02T15:38:18.831Z" }, - { url = "https://files.pythonhosted.org/packages/18/07/9dd2f0c0104f1a0295ffbe912bc8d63307a539b900dd9e2c48ef7810d971/orjson-3.11.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:390a1dce0c055ddf8adb6aa94a73b45a4a7d7177b5c584b8d1c1947f2ba60fb3", size = 144099, upload-time = "2026-02-02T15:38:20.28Z" }, - { url = "https://files.pythonhosted.org/packages/a5/66/857a8e4a3292e1f7b1b202883bcdeb43a91566cf59a93f97c53b44bd6801/orjson-3.11.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1eb80451a9c351a71dfaf5b7ccc13ad065405217726b59fdbeadbcc544f9d223", size = 134806, upload-time = "2026-02-02T15:38:22.186Z" }, - { url = "https://files.pythonhosted.org/packages/0a/5b/6ebcf3defc1aab3a338ca777214966851e92efb1f30dc7fc8285216e6d1b/orjson-3.11.7-cp313-cp313-win32.whl", hash = "sha256:7477aa6a6ec6139c5cb1cc7b214643592169a5494d200397c7fc95d740d5fcf3", size = 127914, upload-time = "2026-02-02T15:38:23.511Z" }, - { url = "https://files.pythonhosted.org/packages/00/04/c6f72daca5092e3117840a1b1e88dfc809cc1470cf0734890d0366b684a1/orjson-3.11.7-cp313-cp313-win_amd64.whl", hash = "sha256:b9f95dcdea9d4f805daa9ddf02617a89e484c6985fa03055459f90e87d7a0757", size = 124986, upload-time = "2026-02-02T15:38:24.836Z" }, - { url = "https://files.pythonhosted.org/packages/03/ba/077a0f6f1085d6b806937246860fafbd5b17f3919c70ee3f3d8d9c713f38/orjson-3.11.7-cp313-cp313-win_arm64.whl", hash = "sha256:800988273a014a0541483dc81021247d7eacb0c845a9d1a34a422bc718f41539", size = 126045, upload-time = "2026-02-02T15:38:26.216Z" }, - { url = "https://files.pythonhosted.org/packages/e9/1e/745565dca749813db9a093c5ebc4bac1a9475c64d54b95654336ac3ed961/orjson-3.11.7-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:de0a37f21d0d364954ad5de1970491d7fbd0fb1ef7417d4d56a36dc01ba0c0a0", size = 228391, upload-time = "2026-02-02T15:38:27.757Z" }, - { url = "https://files.pythonhosted.org/packages/46/19/e40f6225da4d3aa0c8dc6e5219c5e87c2063a560fe0d72a88deb59776794/orjson-3.11.7-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:c2428d358d85e8da9d37cba18b8c4047c55222007a84f97156a5b22028dfbfc0", size = 125188, upload-time = "2026-02-02T15:38:29.241Z" }, - { url = "https://files.pythonhosted.org/packages/9d/7e/c4de2babef2c0817fd1f048fd176aa48c37bec8aef53d2fa932983032cce/orjson-3.11.7-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c4bc6c6ac52cdaa267552544c73e486fecbd710b7ac09bc024d5a78555a22f6", size = 128097, upload-time = "2026-02-02T15:38:30.618Z" }, - { url = "https://files.pythonhosted.org/packages/eb/74/233d360632bafd2197f217eee7fb9c9d0229eac0c18128aee5b35b0014fe/orjson-3.11.7-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd0d68edd7dfca1b2eca9361a44ac9f24b078de3481003159929a0573f21a6bf", size = 123364, upload-time = "2026-02-02T15:38:32.363Z" }, - { url = "https://files.pythonhosted.org/packages/79/51/af79504981dd31efe20a9e360eb49c15f06df2b40e7f25a0a52d9ae888e8/orjson-3.11.7-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:623ad1b9548ef63886319c16fa317848e465a21513b31a6ad7b57443c3e0dcf5", size = 129076, upload-time = "2026-02-02T15:38:33.68Z" }, - { url = "https://files.pythonhosted.org/packages/67/e2/da898eb68b72304f8de05ca6715870d09d603ee98d30a27e8a9629abc64b/orjson-3.11.7-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6e776b998ac37c0396093d10290e60283f59cfe0fc3fccbd0ccc4bd04dd19892", size = 141705, upload-time = "2026-02-02T15:38:34.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/89/15364d92acb3d903b029e28d834edb8780c2b97404cbf7929aa6b9abdb24/orjson-3.11.7-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:652c6c3af76716f4a9c290371ba2e390ede06f6603edb277b481daf37f6f464e", size = 130855, upload-time = "2026-02-02T15:38:36.379Z" }, - { url = "https://files.pythonhosted.org/packages/c2/8b/ecdad52d0b38d4b8f514be603e69ccd5eacf4e7241f972e37e79792212ec/orjson-3.11.7-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a56df3239294ea5964adf074c54bcc4f0ccd21636049a2cf3ca9cf03b5d03cf1", size = 133386, upload-time = "2026-02-02T15:38:37.704Z" }, - { url = "https://files.pythonhosted.org/packages/b9/0e/45e1dcf10e17d0924b7c9162f87ec7b4ca79e28a0548acf6a71788d3e108/orjson-3.11.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bda117c4148e81f746655d5a3239ae9bd00cb7bc3ca178b5fc5a5997e9744183", size = 138295, upload-time = "2026-02-02T15:38:39.096Z" }, - { url = "https://files.pythonhosted.org/packages/63/d7/4d2e8b03561257af0450f2845b91fbd111d7e526ccdf737267108075e0ba/orjson-3.11.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:23d6c20517a97a9daf1d48b580fcdc6f0516c6f4b5038823426033690b4d2650", size = 408720, upload-time = "2026-02-02T15:38:40.634Z" }, - { url = "https://files.pythonhosted.org/packages/78/cf/d45343518282108b29c12a65892445fc51f9319dc3c552ceb51bb5905ed2/orjson-3.11.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8ff206156006da5b847c9304b6308a01e8cdbc8cce824e2779a5ba71c3def141", size = 144152, upload-time = "2026-02-02T15:38:42.262Z" }, - { url = "https://files.pythonhosted.org/packages/a9/3a/d6001f51a7275aacd342e77b735c71fa04125a3f93c36fee4526bc8c654e/orjson-3.11.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:962d046ee1765f74a1da723f4b33e3b228fe3a48bd307acce5021dfefe0e29b2", size = 134814, upload-time = "2026-02-02T15:38:43.627Z" }, - { url = "https://files.pythonhosted.org/packages/1d/d3/f19b47ce16820cc2c480f7f1723e17f6d411b3a295c60c8ad3aa9ff1c96a/orjson-3.11.7-cp314-cp314-win32.whl", hash = "sha256:89e13dd3f89f1c38a9c9eba5fbf7cdc2d1feca82f5f290864b4b7a6aac704576", size = 127997, upload-time = "2026-02-02T15:38:45.06Z" }, - { url = "https://files.pythonhosted.org/packages/12/df/172771902943af54bf661a8d102bdf2e7f932127968080632bda6054b62c/orjson-3.11.7-cp314-cp314-win_amd64.whl", hash = "sha256:845c3e0d8ded9c9271cd79596b9b552448b885b97110f628fb687aee2eed11c1", size = 124985, upload-time = "2026-02-02T15:38:46.388Z" }, - { url = "https://files.pythonhosted.org/packages/6f/1c/f2a8d8a1b17514660a614ce5f7aac74b934e69f5abc2700cc7ced882a009/orjson-3.11.7-cp314-cp314-win_arm64.whl", hash = "sha256:4a2e9c5be347b937a2e0203866f12bba36082e89b402ddb9e927d5822e43088d", size = 126038, upload-time = "2026-02-02T15:38:47.703Z" }, -] - -[[package]] -name = "overrides" -version = "7.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812, upload-time = "2024-01-27T21:01:33.423Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832, upload-time = "2024-01-27T21:01:31.393Z" }, -] - -[[package]] -name = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[package]] -name = "paginate" -version = "0.5.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" }, -] - -[[package]] -name = "parsimonious" -version = "0.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "regex" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/91/abdc50c4ef06fdf8d047f60ee777ca9b2a7885e1a9cea81343fbecda52d7/parsimonious-0.10.0.tar.gz", hash = "sha256:8281600da180ec8ae35427a4ab4f7b82bfec1e3d1e52f80cb60ea82b9512501c", size = 52172, upload-time = "2022-09-03T17:01:17.004Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/0f/c8b64d9b54ea631fcad4e9e3c8dbe8c11bb32a623be94f22974c88e71eaf/parsimonious-0.10.0-py3-none-any.whl", hash = "sha256:982ab435fabe86519b57f6b35610aa4e4e977e9f02a14353edf4bbc75369fc0f", size = 48427, upload-time = "2022-09-03T17:01:13.814Z" }, -] - -[[package]] -name = "passlib" -version = "1.7.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/06/9da9ee59a67fae7761aab3ccc84fa4f3f33f125b370f1ccdb915bf967c11/passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04", size = 689844, upload-time = "2020-10-08T19:00:52.121Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/a4/ab6b7589382ca3df236e03faa71deac88cae040af60c071a78d254a62172/passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1", size = 525554, upload-time = "2020-10-08T19:00:49.856Z" }, -] - -[package.optional-dependencies] -bcrypt = [ - { name = "bcrypt" }, -] - -[[package]] -name = "pathspec" -version = "1.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, -] - -[[package]] -name = "pillow" -version = "11.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800, upload-time = "2025-07-01T09:14:17.648Z" }, - { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296, upload-time = "2025-07-01T09:14:19.828Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726, upload-time = "2025-07-03T13:10:04.448Z" }, - { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652, upload-time = "2025-07-03T13:10:10.391Z" }, - { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787, upload-time = "2025-07-01T09:14:21.63Z" }, - { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236, upload-time = "2025-07-01T09:14:23.321Z" }, - { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950, upload-time = "2025-07-01T09:14:25.237Z" }, - { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", size = 6723358, upload-time = "2025-07-01T09:14:27.053Z" }, - { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", size = 6275079, upload-time = "2025-07-01T09:14:30.104Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", size = 6986324, upload-time = "2025-07-01T09:14:31.899Z" }, - { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", size = 2423067, upload-time = "2025-07-01T09:14:33.709Z" }, - { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328, upload-time = "2025-07-01T09:14:35.276Z" }, - { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652, upload-time = "2025-07-01T09:14:37.203Z" }, - { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443, upload-time = "2025-07-01T09:14:39.344Z" }, - { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474, upload-time = "2025-07-01T09:14:41.843Z" }, - { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038, upload-time = "2025-07-01T09:14:44.008Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407, upload-time = "2025-07-03T13:10:15.628Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094, upload-time = "2025-07-03T13:10:21.857Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503, upload-time = "2025-07-01T09:14:45.698Z" }, - { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574, upload-time = "2025-07-01T09:14:47.415Z" }, - { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060, upload-time = "2025-07-01T09:14:49.636Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407, upload-time = "2025-07-01T09:14:51.962Z" }, - { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841, upload-time = "2025-07-01T09:14:54.142Z" }, - { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450, upload-time = "2025-07-01T09:14:56.436Z" }, - { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055, upload-time = "2025-07-01T09:14:58.072Z" }, - { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110, upload-time = "2025-07-01T09:14:59.79Z" }, - { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547, upload-time = "2025-07-01T09:15:01.648Z" }, - { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554, upload-time = "2025-07-03T13:10:27.018Z" }, - { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132, upload-time = "2025-07-03T13:10:33.01Z" }, - { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001, upload-time = "2025-07-01T09:15:03.365Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814, upload-time = "2025-07-01T09:15:05.655Z" }, - { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124, upload-time = "2025-07-01T09:15:07.358Z" }, - { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186, upload-time = "2025-07-01T09:15:09.317Z" }, - { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546, upload-time = "2025-07-01T09:15:11.311Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102, upload-time = "2025-07-01T09:15:13.164Z" }, - { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" }, - { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" }, - { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" }, - { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" }, - { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" }, - { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" }, - { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" }, - { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" }, - { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" }, - { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" }, - { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" }, - { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" }, - { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" }, - { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" }, - { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" }, - { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" }, - { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" }, - { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" }, - { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, -] - -[[package]] -name = "playwright" -version = "1.50.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "greenlet" }, - { name = "pyee" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/5e/068dea3c96e9c09929b45c92cf7e573403b52a89aa463f89b9da9b87b7a4/playwright-1.50.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:f36d754a6c5bd9bf7f14e8f57a2aea6fd08f39ca4c8476481b9c83e299531148", size = 40277564, upload-time = "2025-02-03T14:57:22.774Z" }, - { url = "https://files.pythonhosted.org/packages/78/85/b3deb3d2add00d2a6ee74bf6f57ccefb30efc400fd1b7b330ba9a3626330/playwright-1.50.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:40f274384591dfd27f2b014596250b2250c843ed1f7f4ef5d2960ecb91b4961e", size = 39521844, upload-time = "2025-02-03T14:57:29.372Z" }, - { url = "https://files.pythonhosted.org/packages/f3/f6/002b3d98df9c84296fea84f070dc0d87c2270b37f423cf076a913370d162/playwright-1.50.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:9922ef9bcd316995f01e220acffd2d37a463b4ad10fd73e388add03841dfa230", size = 40277563, upload-time = "2025-02-03T14:57:36.291Z" }, - { url = "https://files.pythonhosted.org/packages/b9/63/c9a73736e434df894e484278dddc0bf154312ff8d0f16d516edb790a7d42/playwright-1.50.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:8fc628c492d12b13d1f347137b2ac6c04f98197ff0985ef0403a9a9ee0d39131", size = 45076712, upload-time = "2025-02-03T14:57:43.581Z" }, - { url = "https://files.pythonhosted.org/packages/bd/2c/a54b5a64cc7d1a62f2d944c5977fb3c88e74d76f5cdc7966e717426bce66/playwright-1.50.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcff35f72db2689a79007aee78f1b0621a22e6e3d6c1f58aaa9ac805bf4497c", size = 44493111, upload-time = "2025-02-03T14:57:50.226Z" }, - { url = "https://files.pythonhosted.org/packages/2b/4a/047cbb2ffe1249bd7a56441fc3366fb4a8a1f44bc36a9061d10edfda2c86/playwright-1.50.0-py3-none-win32.whl", hash = "sha256:3b906f4d351260016a8c5cc1e003bb341651ae682f62213b50168ed581c7558a", size = 34784543, upload-time = "2025-02-03T14:57:55.942Z" }, - { url = "https://files.pythonhosted.org/packages/bc/2b/e944e10c9b18e77e43d3bb4d6faa323f6cc27597db37b75bc3fd796adfd5/playwright-1.50.0-py3-none-win_amd64.whl", hash = "sha256:1859423da82de631704d5e3d88602d755462b0906824c1debe140979397d2e8d", size = 34784546, upload-time = "2025-02-03T14:58:01.664Z" }, -] - -[[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 = "polars" -version = "1.37.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "polars-runtime-32" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/84/ae/dfebf31b9988c20998140b54d5b521f64ce08879f2c13d9b4d44d7c87e32/polars-1.37.1.tar.gz", hash = "sha256:0309e2a4633e712513401964b4d95452f124ceabf7aec6db50affb9ced4a274e", size = 715572, upload-time = "2026-01-12T23:27:03.267Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/75/ec73e38812bca7c2240aff481b9ddff20d1ad2f10dee4b3353f5eeaacdab/polars-1.37.1-py3-none-any.whl", hash = "sha256:377fed8939a2f1223c1563cfabdc7b4a3d6ff846efa1f2ddeb8644fafd9b1aff", size = 805749, upload-time = "2026-01-12T23:25:48.595Z" }, -] - -[[package]] -name = "polars-runtime-32" -version = "1.37.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/40/0b/addabe5e8d28a5a4c9887a08907be7ddc3fce892dc38f37d14b055438a57/polars_runtime_32-1.37.1.tar.gz", hash = "sha256:68779d4a691da20a5eb767d74165a8f80a2bdfbde4b54acf59af43f7fa028d8f", size = 2818945, upload-time = "2026-01-12T23:27:04.653Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/a2/e828ea9f845796de02d923edb790e408ca0b560cd68dbd74bb99a1b3c461/polars_runtime_32-1.37.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0b8d4d73ea9977d3731927740e59d814647c5198bdbe359bcf6a8bfce2e79771", size = 43499912, upload-time = "2026-01-12T23:25:51.182Z" }, - { url = "https://files.pythonhosted.org/packages/7e/46/81b71b7aa9e3703ee6e4ef1f69a87e40f58ea7c99212bf49a95071e99c8c/polars_runtime_32-1.37.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:c682bf83f5f352e5e02f5c16c652c48ca40442f07b236f30662b22217320ce76", size = 39695707, upload-time = "2026-01-12T23:25:54.289Z" }, - { url = "https://files.pythonhosted.org/packages/81/2e/20009d1fde7ee919e24040f5c87cb9d0e4f8e3f109b74ba06bc10c02459c/polars_runtime_32-1.37.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc82b5bbe70ca1a4b764eed1419f6336752d6ba9fc1245388d7f8b12438afa2c", size = 41467034, upload-time = "2026-01-12T23:25:56.925Z" }, - { url = "https://files.pythonhosted.org/packages/eb/21/9b55bea940524324625b1e8fd96233290303eb1bf2c23b54573487bbbc25/polars_runtime_32-1.37.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8362d11ac5193b994c7e9048ffe22ccfb976699cfbf6e128ce0302e06728894", size = 45142711, upload-time = "2026-01-12T23:26:00.817Z" }, - { url = "https://files.pythonhosted.org/packages/8c/25/c5f64461aeccdac6834a89f826d051ccd3b4ce204075e562c87a06ed2619/polars_runtime_32-1.37.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:04f5d5a2f013dca7391b7d8e7672fa6d37573a87f1d45d3dd5f0d9b5565a4b0f", size = 41638564, upload-time = "2026-01-12T23:26:04.186Z" }, - { url = "https://files.pythonhosted.org/packages/35/af/509d3cf6c45e764ccf856beaae26fc34352f16f10f94a7839b1042920a73/polars_runtime_32-1.37.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fbfde7c0ca8209eeaed546e4a32cca1319189aa61c5f0f9a2b4494262bd0c689", size = 44721136, upload-time = "2026-01-12T23:26:07.088Z" }, - { url = "https://files.pythonhosted.org/packages/af/d1/5c0a83a625f72beef59394bebc57d12637997632a4f9d3ab2ffc2cc62bbf/polars_runtime_32-1.37.1-cp310-abi3-win_amd64.whl", hash = "sha256:da3d3642ae944e18dd17109d2a3036cb94ce50e5495c5023c77b1599d4c861bc", size = 44948288, upload-time = "2026-01-12T23:26:10.214Z" }, - { url = "https://files.pythonhosted.org/packages/10/f3/061bb702465904b6502f7c9081daee34b09ccbaa4f8c94cf43a2a3b6dd6f/polars_runtime_32-1.37.1-cp310-abi3-win_arm64.whl", hash = "sha256:55f2c4847a8d2e267612f564de7b753a4bde3902eaabe7b436a0a4abf75949a0", size = 41001914, upload-time = "2026-01-12T23:26:12.997Z" }, -] - -[[package]] -name = "posthog" -version = "5.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "backoff" }, - { name = "distro" }, - { name = "python-dateutil" }, - { name = "requests" }, - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/48/20/60ae67bb9d82f00427946218d49e2e7e80fb41c15dc5019482289ec9ce8d/posthog-5.4.0.tar.gz", hash = "sha256:701669261b8d07cdde0276e5bc096b87f9e200e3b9589c5ebff14df658c5893c", size = 88076, upload-time = "2025-06-20T23:19:23.485Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/98/e480cab9a08d1c09b1c59a93dade92c1bb7544826684ff2acbfd10fcfbd4/posthog-5.4.0-py3-none-any.whl", hash = "sha256:284dfa302f64353484420b52d4ad81ff5c2c2d1d607c4e2db602ac72761831bd", size = 105364, upload-time = "2025-06-20T23:19:22.001Z" }, -] - -[[package]] -name = "propcache" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, - { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, - { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, - { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, - { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, - { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, - { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, - { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, - { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, - { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, - { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, - { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, - { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, - { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, - { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, - { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, - { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, - { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, - { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, - { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, - { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, - { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, - { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, - { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, - { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, - { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, - { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, - { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, - { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, - { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, - { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, - { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, - { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, - { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, - { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, - { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, - { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, - { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, - { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, - { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, - { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, - { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, - { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, - { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, - { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, - { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, - { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, - { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, - { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, - { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, - { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, - { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, - { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, - { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, - { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, -] - -[[package]] -name = "protobuf" -version = "6.33.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/25/7c72c307aafc96fa87062aa6291d9f7c94836e43214d43722e86037aac02/protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c", size = 444465, upload-time = "2026-01-29T21:51:33.494Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/79/af92d0a8369732b027e6d6084251dd8e782c685c72da161bd4a2e00fbabb/protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b", size = 425769, upload-time = "2026-01-29T21:51:21.751Z" }, - { url = "https://files.pythonhosted.org/packages/55/75/bb9bc917d10e9ee13dee8607eb9ab963b7cf8be607c46e7862c748aa2af7/protobuf-6.33.5-cp310-abi3-win_amd64.whl", hash = "sha256:3093804752167bcab3998bec9f1048baae6e29505adaf1afd14a37bddede533c", size = 437118, upload-time = "2026-01-29T21:51:24.022Z" }, - { url = "https://files.pythonhosted.org/packages/a2/6b/e48dfc1191bc5b52950246275bf4089773e91cb5ba3592621723cdddca62/protobuf-6.33.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:a5cb85982d95d906df1e2210e58f8e4f1e3cdc088e52c921a041f9c9a0386de5", size = 427766, upload-time = "2026-01-29T21:51:25.413Z" }, - { url = "https://files.pythonhosted.org/packages/4e/b1/c79468184310de09d75095ed1314b839eb2f72df71097db9d1404a1b2717/protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190", size = 324638, upload-time = "2026-01-29T21:51:26.423Z" }, - { url = "https://files.pythonhosted.org/packages/c5/f5/65d838092fd01c44d16037953fd4c2cc851e783de9b8f02b27ec4ffd906f/protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd", size = 339411, upload-time = "2026-01-29T21:51:27.446Z" }, - { url = "https://files.pythonhosted.org/packages/9b/53/a9443aa3ca9ba8724fdfa02dd1887c1bcd8e89556b715cfbacca6b63dbec/protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0", size = 323465, upload-time = "2026-01-29T21:51:28.925Z" }, - { url = "https://files.pythonhosted.org/packages/57/bf/2086963c69bdac3d7cff1cc7ff79b8ce5ea0bec6797a017e1be338a46248/protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02", size = 170687, upload-time = "2026-01-29T21:51:32.557Z" }, -] - -[[package]] -name = "py-ecc" -version = "8.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "eth-typing" }, - { name = "eth-utils" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/96/e73075d5c885274efada2fbc5db6377022036c2f5b4b470dbcf4106e07d5/py_ecc-8.0.0.tar.gz", hash = "sha256:56aca19e5dc37294f60c1cc76666c03c2276e7666412b9a559fa0145d099933d", size = 51193, upload-time = "2025-04-14T16:14:03.29Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/58/383335eac96d2f1aba78741c6ce128c54e7eba2ea1dc47408257d751d35c/py_ecc-8.0.0-py3-none-any.whl", hash = "sha256:c0b2dfc4bde67a55122a392591a10e851a986d5128f680628c80b405f7663e13", size = 47814, upload-time = "2025-04-14T16:14:01.827Z" }, -] - -[[package]] -name = "py-rust-stemmers" -version = "0.1.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/63/4fbc14810c32d2a884e2e94e406a7d5bf8eee53e1103f558433817230342/py_rust_stemmers-0.1.5.tar.gz", hash = "sha256:e9c310cfb5c2470d7c7c8a0484725965e7cab8b1237e106a0863d5741da3e1f7", size = 9388, upload-time = "2025-02-19T13:56:28.708Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e1/ea8ac92454a634b1bb1ee0a89c2f75a4e6afec15a8412527e9bbde8c6b7b/py_rust_stemmers-0.1.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:29772837126a28263bf54ecd1bc709dd569d15a94d5e861937813ce51e8a6df4", size = 286085, upload-time = "2025-02-19T13:55:23.871Z" }, - { url = "https://files.pythonhosted.org/packages/cb/32/fe1cc3d36a19c1ce39792b1ed151ddff5ee1d74c8801f0e93ff36e65f885/py_rust_stemmers-0.1.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d62410ada44a01e02974b85d45d82f4b4c511aae9121e5f3c1ba1d0bea9126b", size = 272021, upload-time = "2025-02-19T13:55:25.685Z" }, - { url = "https://files.pythonhosted.org/packages/0a/38/b8f94e5e886e7ab181361a0911a14fb923b0d05b414de85f427e773bf445/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b28ef729a4c83c7d9418be3c23c0372493fcccc67e86783ff04596ef8a208cdf", size = 310547, upload-time = "2025-02-19T13:55:26.891Z" }, - { url = "https://files.pythonhosted.org/packages/a9/08/62e97652d359b75335486f4da134a6f1c281f38bd3169ed6ecfb276448c3/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a979c3f4ff7ad94a0d4cf566ca7bfecebb59e66488cc158e64485cf0c9a7879f", size = 315237, upload-time = "2025-02-19T13:55:28.116Z" }, - { url = "https://files.pythonhosted.org/packages/1c/b9/fc0278432f288d2be4ee4d5cc80fd8013d604506b9b0503e8b8cae4ba1c3/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c3593d895453fa06bf70a7b76d6f00d06def0f91fc253fe4260920650c5e078", size = 324419, upload-time = "2025-02-19T13:55:29.211Z" }, - { url = "https://files.pythonhosted.org/packages/6b/5b/74e96eaf622fe07e83c5c389d101540e305e25f76a6d0d6fb3d9e0506db8/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:96ccc7fd042ffc3f7f082f2223bb7082ed1423aa6b43d5d89ab23e321936c045", size = 324792, upload-time = "2025-02-19T13:55:30.948Z" }, - { url = "https://files.pythonhosted.org/packages/4f/f7/b76816d7d67166e9313915ad486c21d9e7da0ac02703e14375bb1cb64b5a/py_rust_stemmers-0.1.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef18cfced2c9c676e0d7d172ba61c3fab2aa6969db64cc8f5ca33a7759efbefe", size = 488014, upload-time = "2025-02-19T13:55:32.066Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ed/7d9bed02f78d85527501f86a867cd5002d97deb791b9a6b1b45b00100010/py_rust_stemmers-0.1.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:541d4b5aa911381e3d37ec483abb6a2cf2351b4f16d5e8d77f9aa2722956662a", size = 575582, upload-time = "2025-02-19T13:55:34.005Z" }, - { url = "https://files.pythonhosted.org/packages/93/40/eafd1b33688e8e8ae946d1ef25c4dc93f5b685bd104b9c5573405d7e1d30/py_rust_stemmers-0.1.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ffd946a36e9ac17ca96821963663012e04bc0ee94d21e8b5ae034721070b436c", size = 493267, upload-time = "2025-02-19T13:55:35.294Z" }, - { url = "https://files.pythonhosted.org/packages/2f/6a/15135b69e4fd28369433eb03264d201b1b0040ba534b05eddeb02a276684/py_rust_stemmers-0.1.5-cp312-none-win_amd64.whl", hash = "sha256:6ed61e1207f3b7428e99b5d00c055645c6415bb75033bff2d06394cbe035fd8e", size = 209395, upload-time = "2025-02-19T13:55:36.519Z" }, - { url = "https://files.pythonhosted.org/packages/80/b8/030036311ec25952bf3083b6c105be5dee052a71aa22d5fbeb857ebf8c1c/py_rust_stemmers-0.1.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:398b3a843a9cd4c5d09e726246bc36f66b3d05b0a937996814e91f47708f5db5", size = 286086, upload-time = "2025-02-19T13:55:37.581Z" }, - { url = "https://files.pythonhosted.org/packages/ed/be/0465dcb3a709ee243d464e89231e3da580017f34279d6304de291d65ccb0/py_rust_stemmers-0.1.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4e308fc7687901f0c73603203869908f3156fa9c17c4ba010a7fcc98a7a1c5f2", size = 272019, upload-time = "2025-02-19T13:55:39.183Z" }, - { url = "https://files.pythonhosted.org/packages/ab/b6/76ca5b1f30cba36835938b5d9abee0c130c81833d51b9006264afdf8df3c/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f9efc4da5e734bdd00612e7506de3d0c9b7abc4b89d192742a0569d0d1fe749", size = 310545, upload-time = "2025-02-19T13:55:40.339Z" }, - { url = "https://files.pythonhosted.org/packages/56/8f/5be87618cea2fe2e70e74115a20724802bfd06f11c7c43514b8288eb6514/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cc2cc8d2b36bc05b8b06506199ac63d437360ae38caefd98cd19e479d35afd42", size = 315236, upload-time = "2025-02-19T13:55:41.55Z" }, - { url = "https://files.pythonhosted.org/packages/00/02/ea86a316aee0f0a9d1449ad4dbffff38f4cf0a9a31045168ae8b95d8bdf8/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a231dc6f0b2a5f12a080dfc7abd9e6a4ea0909290b10fd0a4620e5a0f52c3d17", size = 324419, upload-time = "2025-02-19T13:55:42.693Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fd/1612c22545dcc0abe2f30fc08f30a2332f2224dd536fa1508444a9ca0e39/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5845709d48afc8b29e248f42f92431155a3d8df9ba30418301c49c6072b181b0", size = 324794, upload-time = "2025-02-19T13:55:43.896Z" }, - { url = "https://files.pythonhosted.org/packages/66/18/8a547584d7edac9e7ac9c7bdc53228d6f751c0f70a317093a77c386c8ddc/py_rust_stemmers-0.1.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e48bfd5e3ce9d223bfb9e634dc1425cf93ee57eef6f56aa9a7120ada3990d4be", size = 488014, upload-time = "2025-02-19T13:55:45.088Z" }, - { url = "https://files.pythonhosted.org/packages/3b/87/4619c395b325e26048a6e28a365afed754614788ba1f49b2eefb07621a03/py_rust_stemmers-0.1.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:35d32f6e7bdf6fd90e981765e32293a8be74def807147dea9fdc1f65d6ce382f", size = 575582, upload-time = "2025-02-19T13:55:46.436Z" }, - { url = "https://files.pythonhosted.org/packages/98/6e/214f1a889142b7df6d716e7f3fea6c41e87bd6c29046aa57e175d452b104/py_rust_stemmers-0.1.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:191ea8bf922c984631ffa20bf02ef0ad7eec0465baeaed3852779e8f97c7e7a3", size = 493269, upload-time = "2025-02-19T13:55:49.057Z" }, - { url = "https://files.pythonhosted.org/packages/e1/b9/c5185df277576f995ae34418eb2b2ac12f30835412270f9e05c52face521/py_rust_stemmers-0.1.5-cp313-none-win_amd64.whl", hash = "sha256:e564c9efdbe7621704e222b53bac265b0e4fbea788f07c814094f0ec6b80adcf", size = 209397, upload-time = "2025-02-19T13:55:50.853Z" }, -] - -[[package]] -name = "pyarrow" -version = "23.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/33/ffd9c3eb087fa41dd79c3cf20c4c0ae3cdb877c4f8e1107a446006344924/pyarrow-23.0.0.tar.gz", hash = "sha256:180e3150e7edfcd182d3d9afba72f7cf19839a497cc76555a8dce998a8f67615", size = 1167185, upload-time = "2026-01-18T16:19:42.218Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/bd/c861d020831ee57609b73ea721a617985ece817684dc82415b0bc3e03ac3/pyarrow-23.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5961a9f646c232697c24f54d3419e69b4261ba8a8b66b0ac54a1851faffcbab8", size = 34189116, upload-time = "2026-01-18T16:15:28.054Z" }, - { url = "https://files.pythonhosted.org/packages/8c/23/7725ad6cdcbaf6346221391e7b3eecd113684c805b0a95f32014e6fa0736/pyarrow-23.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:632b3e7c3d232f41d64e1a4a043fb82d44f8a349f339a1188c6a0dd9d2d47d8a", size = 35803831, upload-time = "2026-01-18T16:15:33.798Z" }, - { url = "https://files.pythonhosted.org/packages/57/06/684a421543455cdc2944d6a0c2cc3425b028a4c6b90e34b35580c4899743/pyarrow-23.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:76242c846db1411f1d6c2cc3823be6b86b40567ee24493344f8226ba34a81333", size = 44436452, upload-time = "2026-01-18T16:15:41.598Z" }, - { url = "https://files.pythonhosted.org/packages/c6/6f/8f9eb40c2328d66e8b097777ddcf38494115ff9f1b5bc9754ba46991191e/pyarrow-23.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b73519f8b52ae28127000986bf228fda781e81d3095cd2d3ece76eb5cf760e1b", size = 47557396, upload-time = "2026-01-18T16:15:51.252Z" }, - { url = "https://files.pythonhosted.org/packages/10/6e/f08075f1472e5159553501fde2cc7bc6700944bdabe49a03f8a035ee6ccd/pyarrow-23.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:068701f6823449b1b6469120f399a1239766b117d211c5d2519d4ed5861f75de", size = 48147129, upload-time = "2026-01-18T16:16:00.299Z" }, - { url = "https://files.pythonhosted.org/packages/7d/82/d5a680cd507deed62d141cc7f07f7944a6766fc51019f7f118e4d8ad0fb8/pyarrow-23.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1801ba947015d10e23bca9dd6ef5d0e9064a81569a89b6e9a63b59224fd060df", size = 50596642, upload-time = "2026-01-18T16:16:08.502Z" }, - { url = "https://files.pythonhosted.org/packages/a9/26/4f29c61b3dce9fa7780303b86895ec6a0917c9af927101daaaf118fbe462/pyarrow-23.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:52265266201ec25b6839bf6bd4ea918ca6d50f31d13e1cf200b4261cd11dc25c", size = 27660628, upload-time = "2026-01-18T16:16:15.28Z" }, - { url = "https://files.pythonhosted.org/packages/66/34/564db447d083ec7ff93e0a883a597d2f214e552823bfc178a2d0b1f2c257/pyarrow-23.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:ad96a597547af7827342ffb3c503c8316e5043bb09b47a84885ce39394c96e00", size = 34184630, upload-time = "2026-01-18T16:16:22.141Z" }, - { url = "https://files.pythonhosted.org/packages/aa/3a/3999daebcb5e6119690c92a621c4d78eef2ffba7a0a1b56386d2875fcd77/pyarrow-23.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:b9edf990df77c2901e79608f08c13fbde60202334a4fcadb15c1f57bf7afee43", size = 35796820, upload-time = "2026-01-18T16:16:29.441Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ee/39195233056c6a8d0976d7d1ac1cd4fe21fb0ec534eca76bc23ef3f60e11/pyarrow-23.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:36d1b5bc6ddcaff0083ceec7e2561ed61a51f49cce8be079ee8ed406acb6fdef", size = 44438735, upload-time = "2026-01-18T16:16:38.79Z" }, - { url = "https://files.pythonhosted.org/packages/2c/41/6a7328ee493527e7afc0c88d105ecca69a3580e29f2faaeac29308369fd7/pyarrow-23.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4292b889cd224f403304ddda8b63a36e60f92911f89927ec8d98021845ea21be", size = 47557263, upload-time = "2026-01-18T16:16:46.248Z" }, - { url = "https://files.pythonhosted.org/packages/c6/ee/34e95b21ee84db494eae60083ddb4383477b31fb1fd19fd866d794881696/pyarrow-23.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dfd9e133e60eaa847fd80530a1b89a052f09f695d0b9c34c235ea6b2e0924cf7", size = 48153529, upload-time = "2026-01-18T16:16:53.412Z" }, - { url = "https://files.pythonhosted.org/packages/52/88/8a8d83cea30f4563efa1b7bf51d241331ee5cd1b185a7e063f5634eca415/pyarrow-23.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832141cc09fac6aab1cd3719951d23301396968de87080c57c9a7634e0ecd068", size = 50598851, upload-time = "2026-01-18T16:17:01.133Z" }, - { url = "https://files.pythonhosted.org/packages/c6/4c/2929c4be88723ba025e7b3453047dc67e491c9422965c141d24bab6b5962/pyarrow-23.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:7a7d067c9a88faca655c71bcc30ee2782038d59c802d57950826a07f60d83c4c", size = 27577747, upload-time = "2026-01-18T16:18:02.413Z" }, - { url = "https://files.pythonhosted.org/packages/64/52/564a61b0b82d72bd68ec3aef1adda1e3eba776f89134b9ebcb5af4b13cb6/pyarrow-23.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:ce9486e0535a843cf85d990e2ec5820a47918235183a5c7b8b97ed7e92c2d47d", size = 34446038, upload-time = "2026-01-18T16:17:07.861Z" }, - { url = "https://files.pythonhosted.org/packages/cc/c9/232d4f9855fd1de0067c8a7808a363230d223c83aeee75e0fe6eab851ba9/pyarrow-23.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:075c29aeaa685fd1182992a9ed2499c66f084ee54eea47da3eb76e125e06064c", size = 35921142, upload-time = "2026-01-18T16:17:15.401Z" }, - { url = "https://files.pythonhosted.org/packages/96/f2/60af606a3748367b906bb82d41f0032e059f075444445d47e32a7ff1df62/pyarrow-23.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:799965a5379589510d888be3094c2296efd186a17ca1cef5b77703d4d5121f53", size = 44490374, upload-time = "2026-01-18T16:17:23.93Z" }, - { url = "https://files.pythonhosted.org/packages/ff/2d/7731543050a678ea3a413955a2d5d80d2a642f270aa57a3cb7d5a86e3f46/pyarrow-23.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ef7cac8fe6fccd8b9e7617bfac785b0371a7fe26af59463074e4882747145d40", size = 47527896, upload-time = "2026-01-18T16:17:33.393Z" }, - { url = "https://files.pythonhosted.org/packages/5a/90/f3342553b7ac9879413aed46500f1637296f3c8222107523a43a1c08b42a/pyarrow-23.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15a414f710dc927132dd67c361f78c194447479555af57317066ee5116b90e9e", size = 48210401, upload-time = "2026-01-18T16:17:42.012Z" }, - { url = "https://files.pythonhosted.org/packages/f3/da/9862ade205ecc46c172b6ce5038a74b5151c7401e36255f15975a45878b2/pyarrow-23.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e0d2e6915eca7d786be6a77bf227fbc06d825a75b5b5fe9bcbef121dec32685", size = 50579677, upload-time = "2026-01-18T16:17:50.241Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4c/f11f371f5d4740a5dafc2e11c76bcf42d03dfdb2d68696da97de420b6963/pyarrow-23.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4b317ea6e800b5704e5e5929acb6e2dc13e9276b708ea97a39eb8b345aa2658b", size = 27631889, upload-time = "2026-01-18T16:17:56.55Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/15aec78bcf43a0c004067bd33eb5352836a29a49db8581fc56f2b6ca88b7/pyarrow-23.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:20b187ed9550d233a872074159f765f52f9d92973191cd4b93f293a19efbe377", size = 34213265, upload-time = "2026-01-18T16:18:07.904Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/deb2c594bbba41c37c5d9aa82f510376998352aa69dfcb886cb4b18ad80f/pyarrow-23.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:18ec84e839b493c3886b9b5e06861962ab4adfaeb79b81c76afbd8d84c7d5fda", size = 35819211, upload-time = "2026-01-18T16:18:13.94Z" }, - { url = "https://files.pythonhosted.org/packages/e0/e5/ee82af693cb7b5b2b74f6524cdfede0e6ace779d7720ebca24d68b57c36b/pyarrow-23.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:e438dd3f33894e34fd02b26bd12a32d30d006f5852315f611aa4add6c7fab4bc", size = 44502313, upload-time = "2026-01-18T16:18:20.367Z" }, - { url = "https://files.pythonhosted.org/packages/9c/86/95c61ad82236495f3c31987e85135926ba3ec7f3819296b70a68d8066b49/pyarrow-23.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:a244279f240c81f135631be91146d7fa0e9e840e1dfed2aba8483eba25cd98e6", size = 47585886, upload-time = "2026-01-18T16:18:27.544Z" }, - { url = "https://files.pythonhosted.org/packages/bb/6e/a72d901f305201802f016d015de1e05def7706fff68a1dedefef5dc7eff7/pyarrow-23.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c4692e83e42438dba512a570c6eaa42be2f8b6c0f492aea27dec54bdc495103a", size = 48207055, upload-time = "2026-01-18T16:18:35.425Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e5/5de029c537630ca18828db45c30e2a78da03675a70ac6c3528203c416fe3/pyarrow-23.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae7f30f898dfe44ea69654a35c93e8da4cef6606dc4c72394068fd95f8e9f54a", size = 50619812, upload-time = "2026-01-18T16:18:43.553Z" }, - { url = "https://files.pythonhosted.org/packages/59/8d/2af846cd2412e67a087f5bda4a8e23dfd4ebd570f777db2e8686615dafc1/pyarrow-23.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:5b86bb649e4112fb0614294b7d0a175c7513738876b89655605ebb87c804f861", size = 28263851, upload-time = "2026-01-18T16:19:38.567Z" }, - { url = "https://files.pythonhosted.org/packages/7b/7f/caab863e587041156f6786c52e64151b7386742c8c27140f637176e9230e/pyarrow-23.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:ebc017d765d71d80a3f8584ca0566b53e40464586585ac64176115baa0ada7d3", size = 34463240, upload-time = "2026-01-18T16:18:49.755Z" }, - { url = "https://files.pythonhosted.org/packages/c9/fa/3a5b8c86c958e83622b40865e11af0857c48ec763c11d472c87cd518283d/pyarrow-23.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:0800cc58a6d17d159df823f87ad66cefebf105b982493d4bad03ee7fab84b993", size = 35935712, upload-time = "2026-01-18T16:18:55.626Z" }, - { url = "https://files.pythonhosted.org/packages/c5/08/17a62078fc1a53decb34a9aa79cf9009efc74d63d2422e5ade9fed2f99e3/pyarrow-23.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:3a7c68c722da9bb5b0f8c10e3eae71d9825a4b429b40b32709df5d1fa55beb3d", size = 44503523, upload-time = "2026-01-18T16:19:03.958Z" }, - { url = "https://files.pythonhosted.org/packages/cc/70/84d45c74341e798aae0323d33b7c39194e23b1abc439ceaf60a68a7a969a/pyarrow-23.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:bd5556c24622df90551063ea41f559b714aa63ca953db884cfb958559087a14e", size = 47542490, upload-time = "2026-01-18T16:19:11.208Z" }, - { url = "https://files.pythonhosted.org/packages/61/d9/d1274b0e6f19e235de17441e53224f4716574b2ca837022d55702f24d71d/pyarrow-23.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:54810f6e6afc4ffee7c2e0051b61722fbea9a4961b46192dcfae8ea12fa09059", size = 48233605, upload-time = "2026-01-18T16:19:19.544Z" }, - { url = "https://files.pythonhosted.org/packages/39/07/e4e2d568cb57543d84482f61e510732820cddb0f47c4bb7df629abfed852/pyarrow-23.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:14de7d48052cf4b0ed174533eafa3cfe0711b8076ad70bede32cf59f744f0d7c", size = 50603979, upload-time = "2026-01-18T16:19:26.717Z" }, - { url = "https://files.pythonhosted.org/packages/72/9c/47693463894b610f8439b2e970b82ef81e9599c757bf2049365e40ff963c/pyarrow-23.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:427deac1f535830a744a4f04a6ac183a64fcac4341b3f618e693c41b7b98d2b0", size = 28338905, upload-time = "2026-01-18T16:19:32.93Z" }, -] - -[[package]] -name = "pyasn1" -version = "0.6.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/b6/6e630dff89739fcd427e3f72b3d905ce0acb85a45d4ec3e2678718a3487f/pyasn1-0.6.2.tar.gz", hash = "sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b", size = 146586, upload-time = "2026-01-16T18:04:18.534Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/b5/a96872e5184f354da9c84ae119971a0a4c221fe9b27a4d94bd43f2596727/pyasn1-0.6.2-py3-none-any.whl", hash = "sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf", size = 83371, upload-time = "2026-01-16T18:04:17.174Z" }, -] - -[[package]] -name = "pybase64" -version = "1.4.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/b8/4ed5c7ad5ec15b08d35cc79ace6145d5c1ae426e46435f4987379439dfea/pybase64-1.4.3.tar.gz", hash = "sha256:c2ed274c9e0ba9c8f9c4083cfe265e66dd679126cd9c2027965d807352f3f053", size = 137272, upload-time = "2025-12-06T13:27:04.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/a7/efcaa564f091a2af7f18a83c1c4875b1437db56ba39540451dc85d56f653/pybase64-1.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:18d85e5ab8b986bb32d8446aca6258ed80d1bafe3603c437690b352c648f5967", size = 38167, upload-time = "2025-12-06T13:23:16.821Z" }, - { url = "https://files.pythonhosted.org/packages/db/c7/c7ad35adff2d272bf2930132db2b3eea8c44bb1b1f64eb9b2b8e57cde7b4/pybase64-1.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3f5791a3491d116d0deaf4d83268f48792998519698f8751efb191eac84320e9", size = 31673, upload-time = "2025-12-06T13:23:17.835Z" }, - { url = "https://files.pythonhosted.org/packages/43/1b/9a8cab0042b464e9a876d5c65fe5127445a2436da36fda64899b119b1a1b/pybase64-1.4.3-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f0b3f200c3e06316f6bebabd458b4e4bcd4c2ca26af7c0c766614d91968dee27", size = 68210, upload-time = "2025-12-06T13:23:18.813Z" }, - { url = "https://files.pythonhosted.org/packages/62/f7/965b79ff391ad208b50e412b5d3205ccce372a2d27b7218ae86d5295b105/pybase64-1.4.3-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb632edfd132b3eaf90c39c89aa314beec4e946e210099b57d40311f704e11d4", size = 71599, upload-time = "2025-12-06T13:23:20.195Z" }, - { url = "https://files.pythonhosted.org/packages/03/4b/a3b5175130b3810bbb8ccfa1edaadbd3afddb9992d877c8a1e2f274b476e/pybase64-1.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:356ef1d74648ce997f5a777cf8f1aefecc1c0b4fe6201e0ef3ec8a08170e1b54", size = 59922, upload-time = "2025-12-06T13:23:21.487Z" }, - { url = "https://files.pythonhosted.org/packages/da/5d/c38d1572027fc601b62d7a407721688b04b4d065d60ca489912d6893e6cf/pybase64-1.4.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:c48361f90db32bacaa5518419d4eb9066ba558013aaf0c7781620279ecddaeb9", size = 56712, upload-time = "2025-12-06T13:23:22.77Z" }, - { url = "https://files.pythonhosted.org/packages/e7/d4/4e04472fef485caa8f561d904d4d69210a8f8fc1608ea15ebd9012b92655/pybase64-1.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:702bcaa16ae02139d881aeaef5b1c8ffb4a3fae062fe601d1e3835e10310a517", size = 59300, upload-time = "2025-12-06T13:23:24.543Z" }, - { url = "https://files.pythonhosted.org/packages/86/e7/16e29721b86734b881d09b7e23dfd7c8408ad01a4f4c7525f3b1088e25ec/pybase64-1.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:53d0ffe1847b16b647c6413d34d1de08942b7724273dd57e67dcbdb10c574045", size = 60278, upload-time = "2025-12-06T13:23:25.608Z" }, - { url = "https://files.pythonhosted.org/packages/b1/02/18515f211d7c046be32070709a8efeeef8a0203de4fd7521e6b56404731b/pybase64-1.4.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:9a1792e8b830a92736dae58f0c386062eb038dfe8004fb03ba33b6083d89cd43", size = 54817, upload-time = "2025-12-06T13:23:26.633Z" }, - { url = "https://files.pythonhosted.org/packages/e7/be/14e29d8e1a481dbff151324c96dd7b5d2688194bb65dc8a00ca0e1ad1e86/pybase64-1.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1d468b1b1ac5ad84875a46eaa458663c3721e8be5f155ade356406848d3701f6", size = 58611, upload-time = "2025-12-06T13:23:27.684Z" }, - { url = "https://files.pythonhosted.org/packages/b4/8a/a2588dfe24e1bbd742a554553778ab0d65fdf3d1c9a06d10b77047d142aa/pybase64-1.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e97b7bdbd62e71898cd542a6a9e320d9da754ff3ebd02cb802d69087ee94d468", size = 52404, upload-time = "2025-12-06T13:23:28.714Z" }, - { url = "https://files.pythonhosted.org/packages/27/fc/afcda7445bebe0cbc38cafdd7813234cdd4fc5573ff067f1abf317bb0cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b33aeaa780caaa08ffda87fc584d5eab61e3d3bbb5d86ead02161dc0c20d04bc", size = 68817, upload-time = "2025-12-06T13:23:30.079Z" }, - { url = "https://files.pythonhosted.org/packages/d3/3a/87c3201e555ed71f73e961a787241a2438c2bbb2ca8809c29ddf938a3157/pybase64-1.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c0efcf78f11cf866bed49caa7b97552bc4855a892f9cc2372abcd3ed0056f0d", size = 57854, upload-time = "2025-12-06T13:23:31.17Z" }, - { url = "https://files.pythonhosted.org/packages/fd/7d/931c2539b31a7b375e7d595b88401eeb5bd6c5ce1059c9123f9b608aaa14/pybase64-1.4.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:66e3791f2ed725a46593f8bd2761ff37d01e2cdad065b1dceb89066f476e50c6", size = 54333, upload-time = "2025-12-06T13:23:32.422Z" }, - { url = "https://files.pythonhosted.org/packages/de/5e/537601e02cc01f27e9d75f440f1a6095b8df44fc28b1eef2cd739aea8cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:72bb0b6bddadab26e1b069bb78e83092711a111a80a0d6b9edcb08199ad7299b", size = 56492, upload-time = "2025-12-06T13:23:33.515Z" }, - { url = "https://files.pythonhosted.org/packages/96/97/2a2e57acf8f5c9258d22aba52e71f8050e167b29ed2ee1113677c1b600c1/pybase64-1.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5b3365dbcbcdb0a294f0f50af0c0a16b27a232eddeeb0bceeefd844ef30d2a23", size = 70974, upload-time = "2025-12-06T13:23:36.27Z" }, - { url = "https://files.pythonhosted.org/packages/75/2e/a9e28941c6dab6f06e6d3f6783d3373044be9b0f9a9d3492c3d8d2260ac0/pybase64-1.4.3-cp312-cp312-win32.whl", hash = "sha256:7bca1ed3a5df53305c629ca94276966272eda33c0d71f862d2d3d043f1e1b91a", size = 33686, upload-time = "2025-12-06T13:23:37.848Z" }, - { url = "https://files.pythonhosted.org/packages/83/e3/507ab649d8c3512c258819c51d25c45d6e29d9ca33992593059e7b646a33/pybase64-1.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:9f2da8f56d9b891b18b4daf463a0640eae45a80af548ce435be86aa6eff3603b", size = 35833, upload-time = "2025-12-06T13:23:38.877Z" }, - { url = "https://files.pythonhosted.org/packages/bc/8a/6eba66cd549a2fc74bb4425fd61b839ba0ab3022d3c401b8a8dc2cc00c7a/pybase64-1.4.3-cp312-cp312-win_arm64.whl", hash = "sha256:0631d8a2d035de03aa9bded029b9513e1fee8ed80b7ddef6b8e9389ffc445da0", size = 31185, upload-time = "2025-12-06T13:23:39.908Z" }, - { url = "https://files.pythonhosted.org/packages/3a/50/b7170cb2c631944388fe2519507fe3835a4054a6a12a43f43781dae82be1/pybase64-1.4.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:ea4b785b0607d11950b66ce7c328f452614aefc9c6d3c9c28bae795dc7f072e1", size = 33901, upload-time = "2025-12-06T13:23:40.951Z" }, - { url = "https://files.pythonhosted.org/packages/48/8b/69f50578e49c25e0a26e3ee72c39884ff56363344b79fc3967f5af420ed6/pybase64-1.4.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:6a10b6330188c3026a8b9c10e6b9b3f2e445779cf16a4c453d51a072241c65a2", size = 40807, upload-time = "2025-12-06T13:23:42.006Z" }, - { url = "https://files.pythonhosted.org/packages/5c/8d/20b68f11adfc4c22230e034b65c71392e3e338b413bf713c8945bd2ccfb3/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:27fdff227a0c0e182e0ba37a99109645188978b920dfb20d8b9c17eeee370d0d", size = 30932, upload-time = "2025-12-06T13:23:43.348Z" }, - { url = "https://files.pythonhosted.org/packages/f7/79/b1b550ac6bff51a4880bf6e089008b2e1ca16f2c98db5e039a08ac3ad157/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2a8204f1fdfec5aa4184249b51296c0de95445869920c88123978304aad42df1", size = 31394, upload-time = "2025-12-06T13:23:44.317Z" }, - { url = "https://files.pythonhosted.org/packages/82/70/b5d7c5932bf64ee1ec5da859fbac981930b6a55d432a603986c7f509c838/pybase64-1.4.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:874fc2a3777de6baf6aa921a7aa73b3be98295794bea31bd80568a963be30767", size = 38078, upload-time = "2025-12-06T13:23:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/56/fe/e66fe373bce717c6858427670736d54297938dad61c5907517ab4106bd90/pybase64-1.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2dc64a94a9d936b8e3449c66afabbaa521d3cc1a563d6bbaaa6ffa4535222e4b", size = 38158, upload-time = "2025-12-06T13:23:46.872Z" }, - { url = "https://files.pythonhosted.org/packages/80/a9/b806ed1dcc7aed2ea3dd4952286319e6f3a8b48615c8118f453948e01999/pybase64-1.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e48f86de1c145116ccf369a6e11720ce696c2ec02d285f440dfb57ceaa0a6cb4", size = 31672, upload-time = "2025-12-06T13:23:47.88Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c9/24b3b905cf75e23a9a4deaf203b35ffcb9f473ac0e6d8257f91a05dfce62/pybase64-1.4.3-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1d45c8fe8fe82b65c36b227bb4a2cf623d9ada16bed602ce2d3e18c35285b72a", size = 68244, upload-time = "2025-12-06T13:23:49.026Z" }, - { url = "https://files.pythonhosted.org/packages/f8/cd/d15b0c3e25e5859fab0416dc5b96d34d6bd2603c1c96a07bb2202b68ab92/pybase64-1.4.3-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ad70c26ba091d8f5167e9d4e1e86a0483a5414805cdb598a813db635bd3be8b8", size = 71620, upload-time = "2025-12-06T13:23:50.081Z" }, - { url = "https://files.pythonhosted.org/packages/0d/31/4ca953cc3dcde2b3711d6bfd70a6f4ad2ca95a483c9698076ba605f1520f/pybase64-1.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e98310b7c43145221e7194ac9fa7fffc84763c87bfc5e2f59f9f92363475bdc1", size = 59930, upload-time = "2025-12-06T13:23:51.68Z" }, - { url = "https://files.pythonhosted.org/packages/60/55/e7f7bdcd0fd66e61dda08db158ffda5c89a306bbdaaf5a062fbe4e48f4a1/pybase64-1.4.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:398685a76034e91485a28aeebcb49e64cd663212fd697b2497ac6dfc1df5e671", size = 56425, upload-time = "2025-12-06T13:23:52.732Z" }, - { url = "https://files.pythonhosted.org/packages/cb/65/b592c7f921e51ca1aca3af5b0d201a98666d0a36b930ebb67e7c2ed27395/pybase64-1.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7e46400a6461187ccb52ed75b0045d937529e801a53a9cd770b350509f9e4d50", size = 59327, upload-time = "2025-12-06T13:23:53.856Z" }, - { url = "https://files.pythonhosted.org/packages/23/95/1613d2fb82dbb1548595ad4179f04e9a8451bfa18635efce18b631eabe3f/pybase64-1.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1b62b9f2f291d94f5e0b76ab499790b7dcc78a009d4ceea0b0428770267484b6", size = 60294, upload-time = "2025-12-06T13:23:54.937Z" }, - { url = "https://files.pythonhosted.org/packages/9d/73/40431f37f7d1b3eab4673e7946ff1e8f5d6bd425ec257e834dae8a6fc7b0/pybase64-1.4.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:f30ceb5fa4327809dede614be586efcbc55404406d71e1f902a6fdcf322b93b2", size = 54858, upload-time = "2025-12-06T13:23:56.031Z" }, - { url = "https://files.pythonhosted.org/packages/a7/84/f6368bcaf9f743732e002a9858646fd7a54f428490d427dd6847c5cfe89e/pybase64-1.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0d5f18ed53dfa1d4cf8b39ee542fdda8e66d365940e11f1710989b3cf4a2ed66", size = 58629, upload-time = "2025-12-06T13:23:57.12Z" }, - { url = "https://files.pythonhosted.org/packages/43/75/359532f9adb49c6b546cafc65c46ed75e2ccc220d514ba81c686fbd83965/pybase64-1.4.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:119d31aa4b58b85a8ebd12b63c07681a138c08dfc2fe5383459d42238665d3eb", size = 52448, upload-time = "2025-12-06T13:23:58.298Z" }, - { url = "https://files.pythonhosted.org/packages/92/6c/ade2ba244c3f33ed920a7ed572ad772eb0b5f14480b72d629d0c9e739a40/pybase64-1.4.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3cf0218b0e2f7988cf7d738a73b6a1d14f3be6ce249d7c0f606e768366df2cce", size = 68841, upload-time = "2025-12-06T13:23:59.886Z" }, - { url = "https://files.pythonhosted.org/packages/a0/51/b345139cd236be382f2d4d4453c21ee6299e14d2f759b668e23080f8663f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:12f4ee5e988bc5c0c1106b0d8fc37fb0508f12dab76bac1b098cb500d148da9d", size = 57910, upload-time = "2025-12-06T13:24:00.994Z" }, - { url = "https://files.pythonhosted.org/packages/1a/b8/9f84bdc4f1c4f0052489396403c04be2f9266a66b70c776001eaf0d78c1f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:937826bc7b6b95b594a45180e81dd4d99bd4dd4814a443170e399163f7ff3fb6", size = 54335, upload-time = "2025-12-06T13:24:02.046Z" }, - { url = "https://files.pythonhosted.org/packages/d0/c7/be63b617d284de46578a366da77ede39c8f8e815ed0d82c7c2acca560fab/pybase64-1.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:88995d1460971ef80b13e3e007afbe4b27c62db0508bc7250a2ab0a0b4b91362", size = 56486, upload-time = "2025-12-06T13:24:03.141Z" }, - { url = "https://files.pythonhosted.org/packages/5e/96/f252c8f9abd6ded3ef1ccd3cdbb8393a33798007f761b23df8de1a2480e6/pybase64-1.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:72326fe163385ed3e1e806dd579d47fde5d8a59e51297a60fc4e6cbc1b4fc4ed", size = 70978, upload-time = "2025-12-06T13:24:04.221Z" }, - { url = "https://files.pythonhosted.org/packages/af/51/0f5714af7aeef96e30f968e4371d75ad60558aaed3579d7c6c8f1c43c18a/pybase64-1.4.3-cp313-cp313-win32.whl", hash = "sha256:b1623730c7892cf5ed0d6355e375416be6ef8d53ab9b284f50890443175c0ac3", size = 33684, upload-time = "2025-12-06T13:24:05.29Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ad/0cea830a654eb08563fb8214150ef57546ece1cc421c09035f0e6b0b5ea9/pybase64-1.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:8369887590f1646a5182ca2fb29252509da7ae31d4923dbb55d3e09da8cc4749", size = 35832, upload-time = "2025-12-06T13:24:06.35Z" }, - { url = "https://files.pythonhosted.org/packages/b4/0d/eec2a8214989c751bc7b4cad1860eb2c6abf466e76b77508c0f488c96a37/pybase64-1.4.3-cp313-cp313-win_arm64.whl", hash = "sha256:860b86bca71e5f0237e2ab8b2d9c4c56681f3513b1bf3e2117290c1963488390", size = 31175, upload-time = "2025-12-06T13:24:07.419Z" }, - { url = "https://files.pythonhosted.org/packages/db/c9/e23463c1a2913686803ef76b1a5ae7e6fac868249a66e48253d17ad7232c/pybase64-1.4.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eb51db4a9c93215135dccd1895dca078e8785c357fabd983c9f9a769f08989a9", size = 38497, upload-time = "2025-12-06T13:24:08.873Z" }, - { url = "https://files.pythonhosted.org/packages/71/83/343f446b4b7a7579bf6937d2d013d82f1a63057cf05558e391ab6039d7db/pybase64-1.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a03ef3f529d85fd46b89971dfb00c634d53598d20ad8908fb7482955c710329d", size = 32076, upload-time = "2025-12-06T13:24:09.975Z" }, - { url = "https://files.pythonhosted.org/packages/46/fc/cb64964c3b29b432f54d1bce5e7691d693e33bbf780555151969ffd95178/pybase64-1.4.3-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:2e745f2ce760c6cf04d8a72198ef892015ddb89f6ceba489e383518ecbdb13ab", size = 72317, upload-time = "2025-12-06T13:24:11.129Z" }, - { url = "https://files.pythonhosted.org/packages/0a/b7/fab2240da6f4e1ad46f71fa56ec577613cf5df9dce2d5b4cfaa4edd0e365/pybase64-1.4.3-cp313-cp313t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fac217cd9de8581a854b0ac734c50fd1fa4b8d912396c1fc2fce7c230efe3a7", size = 75534, upload-time = "2025-12-06T13:24:12.433Z" }, - { url = "https://files.pythonhosted.org/packages/91/3b/3e2f2b6e68e3d83ddb9fa799f3548fb7449765daec9bbd005a9fbe296d7f/pybase64-1.4.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:da1ee8fa04b283873de2d6e8fa5653e827f55b86bdf1a929c5367aaeb8d26f8a", size = 65399, upload-time = "2025-12-06T13:24:13.928Z" }, - { url = "https://files.pythonhosted.org/packages/6b/08/476ac5914c3b32e0274a2524fc74f01cbf4f4af4513d054e41574eb018f6/pybase64-1.4.3-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:b0bf8e884ee822ca7b1448eeb97fa131628fe0ff42f60cae9962789bd562727f", size = 60487, upload-time = "2025-12-06T13:24:15.177Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b8/618a92915330cc9cba7880299b546a1d9dab1a21fd6c0292ee44a4fe608c/pybase64-1.4.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1bf749300382a6fd1f4f255b183146ef58f8e9cb2f44a077b3a9200dfb473a77", size = 63959, upload-time = "2025-12-06T13:24:16.854Z" }, - { url = "https://files.pythonhosted.org/packages/a5/52/af9d8d051652c3051862c442ec3861259c5cdb3fc69774bc701470bd2a59/pybase64-1.4.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:153a0e42329b92337664cfc356f2065248e6c9a1bd651bbcd6dcaf15145d3f06", size = 64874, upload-time = "2025-12-06T13:24:18.328Z" }, - { url = "https://files.pythonhosted.org/packages/e4/51/5381a7adf1f381bd184d33203692d3c57cf8ae9f250f380c3fecbdbe554b/pybase64-1.4.3-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:86ee56ac7f2184ca10217ed1c655c1a060273e233e692e9086da29d1ae1768db", size = 58572, upload-time = "2025-12-06T13:24:19.417Z" }, - { url = "https://files.pythonhosted.org/packages/e0/f0/578ee4ffce5818017de4fdf544e066c225bc435e73eb4793cde28a689d0b/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0e71a4db76726bf830b47477e7d830a75c01b2e9b01842e787a0836b0ba741e3", size = 63636, upload-time = "2025-12-06T13:24:20.497Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ad/8ae94814bf20159ea06310b742433e53d5820aa564c9fdf65bf2d79f8799/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2ba7799ec88540acd9861b10551d24656ca3c2888ecf4dba2ee0a71544a8923f", size = 56193, upload-time = "2025-12-06T13:24:21.559Z" }, - { url = "https://files.pythonhosted.org/packages/d1/31/6438cfcc3d3f0fa84d229fa125c243d5094e72628e525dfefadf3bcc6761/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2860299e4c74315f5951f0cf3e72ba0f201c3356c8a68f95a3ab4e620baf44e9", size = 72655, upload-time = "2025-12-06T13:24:22.673Z" }, - { url = "https://files.pythonhosted.org/packages/a3/0d/2bbc9e9c3fc12ba8a6e261482f03a544aca524f92eae0b4908c0a10ba481/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:bb06015db9151f0c66c10aae8e3603adab6b6cd7d1f7335a858161d92fc29618", size = 62471, upload-time = "2025-12-06T13:24:23.8Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0b/34d491e7f49c1dbdb322ea8da6adecda7c7cd70b6644557c6e4ca5c6f7c7/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:242512a070817272865d37c8909059f43003b81da31f616bb0c391ceadffe067", size = 58119, upload-time = "2025-12-06T13:24:24.994Z" }, - { url = "https://files.pythonhosted.org/packages/ce/17/c21d0cde2a6c766923ae388fc1f78291e1564b0d38c814b5ea8a0e5e081c/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5d8277554a12d3e3eed6180ebda62786bf9fc8d7bb1ee00244258f4a87ca8d20", size = 60791, upload-time = "2025-12-06T13:24:26.046Z" }, - { url = "https://files.pythonhosted.org/packages/92/b2/eaa67038916a48de12b16f4c384bcc1b84b7ec731b23613cb05f27673294/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f40b7ddd698fc1e13a4b64fbe405e4e0e1279e8197e37050e24154655f5f7c4e", size = 74701, upload-time = "2025-12-06T13:24:27.466Z" }, - { url = "https://files.pythonhosted.org/packages/42/10/abb7757c330bb869ebb95dab0c57edf5961ffbd6c095c8209cbbf75d117d/pybase64-1.4.3-cp313-cp313t-win32.whl", hash = "sha256:46d75c9387f354c5172582a9eaae153b53a53afeb9c19fcf764ea7038be3bd8b", size = 33965, upload-time = "2025-12-06T13:24:28.548Z" }, - { url = "https://files.pythonhosted.org/packages/63/a0/2d4e5a59188e9e6aed0903d580541aaea72dcbbab7bf50fb8b83b490b6c3/pybase64-1.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:d7344625591d281bec54e85cbfdab9e970f6219cac1570f2aa140b8c942ccb81", size = 36207, upload-time = "2025-12-06T13:24:29.646Z" }, - { url = "https://files.pythonhosted.org/packages/1f/05/95b902e8f567b4d4b41df768ccc438af618f8d111e54deaf57d2df46bd76/pybase64-1.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:28a3c60c55138e0028313f2eccd321fec3c4a0be75e57a8d3eb883730b1b0880", size = 31505, upload-time = "2025-12-06T13:24:30.687Z" }, - { url = "https://files.pythonhosted.org/packages/e4/80/4bd3dff423e5a91f667ca41982dc0b79495b90ec0c0f5d59aca513e50f8c/pybase64-1.4.3-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:015bb586a1ea1467f69d57427abe587469392215f59db14f1f5c39b52fdafaf5", size = 33835, upload-time = "2025-12-06T13:24:31.767Z" }, - { url = "https://files.pythonhosted.org/packages/45/60/a94d94cc1e3057f602e0b483c9ebdaef40911d84a232647a2fe593ab77bb/pybase64-1.4.3-cp314-cp314-android_24_x86_64.whl", hash = "sha256:d101e3a516f837c3dcc0e5a0b7db09582ebf99ed670865223123fb2e5839c6c0", size = 40673, upload-time = "2025-12-06T13:24:32.82Z" }, - { url = "https://files.pythonhosted.org/packages/e3/71/cf62b261d431857e8e054537a5c3c24caafa331de30daede7b2c6c558501/pybase64-1.4.3-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8f183ac925a48046abe047360fe3a1b28327afb35309892132fe1915d62fb282", size = 30939, upload-time = "2025-12-06T13:24:34.001Z" }, - { url = "https://files.pythonhosted.org/packages/24/3e/d12f92a3c1f7c6ab5d53c155bff9f1084ba997a37a39a4f781ccba9455f3/pybase64-1.4.3-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30bf3558e24dcce4da5248dcf6d73792adfcf4f504246967e9db155be4c439ad", size = 31401, upload-time = "2025-12-06T13:24:35.11Z" }, - { url = "https://files.pythonhosted.org/packages/9b/3d/9c27440031fea0d05146f8b70a460feb95d8b4e3d9ca8f45c972efb4c3d3/pybase64-1.4.3-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a674b419de318d2ce54387dd62646731efa32b4b590907800f0bd40675c1771d", size = 38075, upload-time = "2025-12-06T13:24:36.53Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d4/6c0e0cf0efd53c254173fbcd84a3d8fcbf5e0f66622473da425becec32a5/pybase64-1.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:720104fd7303d07bac302be0ff8f7f9f126f2f45c1edb4f48fdb0ff267e69fe1", size = 38257, upload-time = "2025-12-06T13:24:38.049Z" }, - { url = "https://files.pythonhosted.org/packages/50/eb/27cb0b610d5cd70f5ad0d66c14ad21c04b8db930f7139818e8fbdc14df4d/pybase64-1.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:83f1067f73fa5afbc3efc0565cecc6ed53260eccddef2ebe43a8ce2b99ea0e0a", size = 31685, upload-time = "2025-12-06T13:24:40.327Z" }, - { url = "https://files.pythonhosted.org/packages/db/26/b136a4b65e5c94ff06217f7726478df3f31ab1c777c2c02cf698e748183f/pybase64-1.4.3-cp314-cp314-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:b51204d349a4b208287a8aa5b5422be3baa88abf6cc8ff97ccbda34919bbc857", size = 68460, upload-time = "2025-12-06T13:24:41.735Z" }, - { url = "https://files.pythonhosted.org/packages/68/6d/84ce50e7ee1ae79984d689e05a9937b2460d4efa1e5b202b46762fb9036c/pybase64-1.4.3-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:30f2fd53efecbdde4bdca73a872a68dcb0d1bf8a4560c70a3e7746df973e1ef3", size = 71688, upload-time = "2025-12-06T13:24:42.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/57/6743e420416c3ff1b004041c85eb0ebd9c50e9cf05624664bfa1dc8b5625/pybase64-1.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0932b0c5cfa617091fd74f17d24549ce5de3628791998c94ba57be808078eeaf", size = 60040, upload-time = "2025-12-06T13:24:44.37Z" }, - { url = "https://files.pythonhosted.org/packages/3b/68/733324e28068a89119af2921ce548e1c607cc5c17d354690fc51c302e326/pybase64-1.4.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:acb61f5ab72bec808eb0d4ce8b87ec9f38d7d750cb89b1371c35eb8052a29f11", size = 56478, upload-time = "2025-12-06T13:24:45.815Z" }, - { url = "https://files.pythonhosted.org/packages/b5/9e/f3f4aa8cfe3357a3cdb0535b78eb032b671519d3ecc08c58c4c6b72b5a91/pybase64-1.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:2bc2d5bc15168f5c04c53bdfe5a1e543b2155f456ed1e16d7edce9ce73842021", size = 59463, upload-time = "2025-12-06T13:24:46.938Z" }, - { url = "https://files.pythonhosted.org/packages/aa/d1/53286038e1f0df1cf58abcf4a4a91b0f74ab44539c2547b6c31001ddd054/pybase64-1.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:8a7bc3cd23880bdca59758bcdd6f4ef0674f2393782763910a7466fab35ccb98", size = 60360, upload-time = "2025-12-06T13:24:48.039Z" }, - { url = "https://files.pythonhosted.org/packages/00/9a/5cc6ce95db2383d27ff4d790b8f8b46704d360d701ab77c4f655bcfaa6a7/pybase64-1.4.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ad15acf618880d99792d71e3905b0e2508e6e331b76a1b34212fa0f11e01ad28", size = 54999, upload-time = "2025-12-06T13:24:49.547Z" }, - { url = "https://files.pythonhosted.org/packages/64/e7/c3c1d09c3d7ae79e3aa1358c6d912d6b85f29281e47aa94fc0122a415a2f/pybase64-1.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448158d417139cb4851200e5fee62677ae51f56a865d50cda9e0d61bda91b116", size = 58736, upload-time = "2025-12-06T13:24:50.641Z" }, - { url = "https://files.pythonhosted.org/packages/db/d5/0baa08e3d8119b15b588c39f0d39fd10472f0372e3c54ca44649cbefa256/pybase64-1.4.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:9058c49b5a2f3e691b9db21d37eb349e62540f9f5fc4beabf8cbe3c732bead86", size = 52298, upload-time = "2025-12-06T13:24:51.791Z" }, - { url = "https://files.pythonhosted.org/packages/00/87/fc6f11474a1de7e27cd2acbb8d0d7508bda3efa73dfe91c63f968728b2a3/pybase64-1.4.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ce561724f6522907a66303aca27dce252d363fcd85884972d348f4403ba3011a", size = 69049, upload-time = "2025-12-06T13:24:53.253Z" }, - { url = "https://files.pythonhosted.org/packages/69/9d/7fb5566f669ac18b40aa5fc1c438e24df52b843c1bdc5da47d46d4c1c630/pybase64-1.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:63316560a94ac449fe86cb8b9e0a13714c659417e92e26a5cbf085cd0a0c838d", size = 57952, upload-time = "2025-12-06T13:24:54.342Z" }, - { url = "https://files.pythonhosted.org/packages/de/cc/ceb949232dbbd3ec4ee0190d1df4361296beceee9840390a63df8bc31784/pybase64-1.4.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7ecd796f2ac0be7b73e7e4e232b8c16422014de3295d43e71d2b19fd4a4f5368", size = 54484, upload-time = "2025-12-06T13:24:55.774Z" }, - { url = "https://files.pythonhosted.org/packages/a7/69/659f3c8e6a5d7b753b9c42a4bd9c42892a0f10044e9c7351a4148d413a33/pybase64-1.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d01e102a12fb2e1ed3dc11611c2818448626637857ec3994a9cf4809dfd23477", size = 56542, upload-time = "2025-12-06T13:24:57Z" }, - { url = "https://files.pythonhosted.org/packages/85/2c/29c9e6c9c82b72025f9676f9e82eb1fd2339ad038cbcbf8b9e2ac02798fc/pybase64-1.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ebff797a93c2345f22183f454fd8607a34d75eca5a3a4a969c1c75b304cee39d", size = 71045, upload-time = "2025-12-06T13:24:58.179Z" }, - { url = "https://files.pythonhosted.org/packages/b9/84/5a3dce8d7a0040a5c0c14f0fe1311cd8db872913fa04438071b26b0dac04/pybase64-1.4.3-cp314-cp314-win32.whl", hash = "sha256:28b2a1bb0828c0595dc1ea3336305cd97ff85b01c00d81cfce4f92a95fb88f56", size = 34200, upload-time = "2025-12-06T13:24:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/57/bc/ce7427c12384adee115b347b287f8f3cf65860b824d74fe2c43e37e81c1f/pybase64-1.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:33338d3888700ff68c3dedfcd49f99bfc3b887570206130926791e26b316b029", size = 36323, upload-time = "2025-12-06T13:25:01.708Z" }, - { url = "https://files.pythonhosted.org/packages/9a/1b/2b8ffbe9a96eef7e3f6a5a7be75995eebfb6faaedc85b6da6b233e50c778/pybase64-1.4.3-cp314-cp314-win_arm64.whl", hash = "sha256:62725669feb5acb186458da2f9353e88ae28ef66bb9c4c8d1568b12a790dfa94", size = 31584, upload-time = "2025-12-06T13:25:02.801Z" }, - { url = "https://files.pythonhosted.org/packages/ac/d8/6824c2e6fb45b8fa4e7d92e3c6805432d5edc7b855e3e8e1eedaaf6efb7c/pybase64-1.4.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:153fe29be038948d9372c3e77ae7d1cab44e4ba7d9aaf6f064dbeea36e45b092", size = 38601, upload-time = "2025-12-06T13:25:04.222Z" }, - { url = "https://files.pythonhosted.org/packages/ea/e5/10d2b3a4ad3a4850be2704a2f70cd9c0cf55725c8885679872d3bc846c67/pybase64-1.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f7fe3decaa7c4a9e162327ec7bd81ce183d2b16f23c6d53b606649c6e0203e9e", size = 32078, upload-time = "2025-12-06T13:25:05.362Z" }, - { url = "https://files.pythonhosted.org/packages/43/04/8b15c34d3c2282f1c1b0850f1113a249401b618a382646a895170bc9b5e7/pybase64-1.4.3-cp314-cp314t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:a5ae04ea114c86eb1da1f6e18d75f19e3b5ae39cb1d8d3cd87c29751a6a22780", size = 72474, upload-time = "2025-12-06T13:25:06.434Z" }, - { url = "https://files.pythonhosted.org/packages/42/00/f34b4d11278f8fdc68bc38f694a91492aa318f7c6f1bd7396197ac0f8b12/pybase64-1.4.3-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1755b3dce3a2a5c7d17ff6d4115e8bee4a1d5aeae74469db02e47c8f477147da", size = 75706, upload-time = "2025-12-06T13:25:07.636Z" }, - { url = "https://files.pythonhosted.org/packages/bb/5d/71747d4ad7fe16df4c4c852bdbdeb1f2cf35677b48d7c34d3011a7a6ad3a/pybase64-1.4.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb852f900e27ffc4ec1896817535a0fa19610ef8875a096b59f21d0aa42ff172", size = 65589, upload-time = "2025-12-06T13:25:08.809Z" }, - { url = "https://files.pythonhosted.org/packages/49/b1/d1e82bd58805bb5a3a662864800bab83a83a36ba56e7e3b1706c708002a5/pybase64-1.4.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:9cf21ea8c70c61eddab3421fbfce061fac4f2fb21f7031383005a1efdb13d0b9", size = 60670, upload-time = "2025-12-06T13:25:10.04Z" }, - { url = "https://files.pythonhosted.org/packages/15/67/16c609b7a13d1d9fc87eca12ba2dce5e67f949eeaab61a41bddff843cbb0/pybase64-1.4.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:afff11b331fdc27692fc75e85ae083340a35105cea1a3c4552139e2f0e0d174f", size = 64194, upload-time = "2025-12-06T13:25:11.48Z" }, - { url = "https://files.pythonhosted.org/packages/3c/11/37bc724e42960f0106c2d33dc957dcec8f760c91a908cc6c0df7718bc1a8/pybase64-1.4.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9a5143df542c1ce5c1f423874b948c4d689b3f05ec571f8792286197a39ba02", size = 64984, upload-time = "2025-12-06T13:25:12.645Z" }, - { url = "https://files.pythonhosted.org/packages/6e/66/b2b962a6a480dd5dae3029becf03ea1a650d326e39bf1c44ea3db78bb010/pybase64-1.4.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:d62e9861019ad63624b4a7914dff155af1cc5d6d79df3be14edcaedb5fdad6f9", size = 58750, upload-time = "2025-12-06T13:25:13.848Z" }, - { url = "https://files.pythonhosted.org/packages/2b/15/9b6d711035e29b18b2e1c03d47f41396d803d06ef15b6c97f45b75f73f04/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:84cfd4d92668ef5766cc42a9c9474b88960ac2b860767e6e7be255c6fddbd34a", size = 63816, upload-time = "2025-12-06T13:25:15.356Z" }, - { url = "https://files.pythonhosted.org/packages/b4/21/e2901381ed0df62e2308380f30d9c4d87d6b74e33a84faed3478d33a7197/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:60fc025437f9a7c2cc45e0c19ed68ed08ba672be2c5575fd9d98bdd8f01dd61f", size = 56348, upload-time = "2025-12-06T13:25:16.559Z" }, - { url = "https://files.pythonhosted.org/packages/c4/16/3d788388a178a0407aa814b976fe61bfa4af6760d9aac566e59da6e4a8b4/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:edc8446196f04b71d3af76c0bd1fe0a45066ac5bffecca88adb9626ee28c266f", size = 72842, upload-time = "2025-12-06T13:25:18.055Z" }, - { url = "https://files.pythonhosted.org/packages/a6/63/c15b1f8bd47ea48a5a2d52a4ec61f037062932ea6434ab916107b58e861e/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e99f6fa6509c037794da57f906ade271f52276c956d00f748e5b118462021d48", size = 62651, upload-time = "2025-12-06T13:25:19.191Z" }, - { url = "https://files.pythonhosted.org/packages/bd/b8/f544a2e37c778d59208966d4ef19742a0be37c12fc8149ff34483c176616/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d94020ef09f624d841aa9a3a6029df8cf65d60d7a6d5c8687579fa68bd679b65", size = 58295, upload-time = "2025-12-06T13:25:20.822Z" }, - { url = "https://files.pythonhosted.org/packages/03/99/1fae8a3b7ac181e36f6e7864a62d42d5b1f4fa7edf408c6711e28fba6b4d/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f64ce70d89942a23602dee910dec9b48e5edf94351e1b378186b74fcc00d7f66", size = 60960, upload-time = "2025-12-06T13:25:22.099Z" }, - { url = "https://files.pythonhosted.org/packages/9d/9e/cd4c727742345ad8384569a4466f1a1428f4e5cc94d9c2ab2f53d30be3fe/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8ea99f56e45c469818b9781903be86ba4153769f007ba0655fa3b46dc332803d", size = 74863, upload-time = "2025-12-06T13:25:23.442Z" }, - { url = "https://files.pythonhosted.org/packages/28/86/a236ecfc5b494e1e922da149689f690abc84248c7c1358f5605b8c9fdd60/pybase64-1.4.3-cp314-cp314t-win32.whl", hash = "sha256:343b1901103cc72362fd1f842524e3bb24978e31aea7ff11e033af7f373f66ab", size = 34513, upload-time = "2025-12-06T13:25:24.592Z" }, - { url = "https://files.pythonhosted.org/packages/56/ce/ca8675f8d1352e245eb012bfc75429ee9cf1f21c3256b98d9a329d44bf0f/pybase64-1.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:57aff6f7f9dea6705afac9d706432049642de5b01080d3718acc23af87c5af76", size = 36702, upload-time = "2025-12-06T13:25:25.72Z" }, - { url = "https://files.pythonhosted.org/packages/3b/30/4a675864877397179b09b720ee5fcb1cf772cf7bebc831989aff0a5f79c1/pybase64-1.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:e906aa08d4331e799400829e0f5e4177e76a3281e8a4bc82ba114c6b30e405c9", size = 31904, upload-time = "2025-12-06T13:25:26.826Z" }, - { url = "https://files.pythonhosted.org/packages/17/45/92322aec1b6979e789b5710f73c59f2172bc37c8ce835305434796824b7b/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:2baaa092f3475f3a9c87ac5198023918ea8b6c125f4c930752ab2cbe3cd1d520", size = 38746, upload-time = "2025-12-06T13:26:25.869Z" }, - { url = "https://files.pythonhosted.org/packages/11/94/f1a07402870388fdfc2ecec0c718111189732f7d0f2d7fe1386e19e8fad0/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:cde13c0764b1af07a631729f26df019070dad759981d6975527b7e8ecb465b6c", size = 32573, upload-time = "2025-12-06T13:26:27.792Z" }, - { url = "https://files.pythonhosted.org/packages/fa/8f/43c3bb11ca9bacf81cb0b7a71500bb65b2eda6d5fe07433c09b543de97f3/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5c29a582b0ea3936d02bd6fe9bf674ab6059e6e45ab71c78404ab2c913224414", size = 43461, upload-time = "2025-12-06T13:26:28.906Z" }, - { url = "https://files.pythonhosted.org/packages/2d/4c/2a5258329200be57497d3972b5308558c6de42e3749c6cc2aa1cbe34b25a/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b6b664758c804fa919b4f1257aa8cf68e95db76fc331de5f70bfc3a34655afe1", size = 36058, upload-time = "2025-12-06T13:26:30.092Z" }, - { url = "https://files.pythonhosted.org/packages/ea/6d/41faa414cde66ec023b0ca8402a8f11cb61731c3dc27c082909cbbd1f929/pybase64-1.4.3-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:f7537fa22ae56a0bf51e4b0ffc075926ad91c618e1416330939f7ef366b58e3b", size = 36231, upload-time = "2025-12-06T13:26:31.656Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pycryptodome" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276, upload-time = "2025-05-17T17:21:45.242Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/5d/bdb09489b63cd34a976cc9e2a8d938114f7a53a74d3dd4f125ffa49dce82/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4", size = 2495152, upload-time = "2025-05-17T17:20:20.833Z" }, - { url = "https://files.pythonhosted.org/packages/a7/ce/7840250ed4cc0039c433cd41715536f926d6e86ce84e904068eb3244b6a6/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae", size = 1639348, upload-time = "2025-05-17T17:20:23.171Z" }, - { url = "https://files.pythonhosted.org/packages/ee/f0/991da24c55c1f688d6a3b5a11940567353f74590734ee4a64294834ae472/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477", size = 2184033, upload-time = "2025-05-17T17:20:25.424Z" }, - { url = "https://files.pythonhosted.org/packages/54/16/0e11882deddf00f68b68dd4e8e442ddc30641f31afeb2bc25588124ac8de/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7", size = 2270142, upload-time = "2025-05-17T17:20:27.808Z" }, - { url = "https://files.pythonhosted.org/packages/d5/fc/4347fea23a3f95ffb931f383ff28b3f7b1fe868739182cb76718c0da86a1/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446", size = 2309384, upload-time = "2025-05-17T17:20:30.765Z" }, - { url = "https://files.pythonhosted.org/packages/6e/d9/c5261780b69ce66d8cfab25d2797bd6e82ba0241804694cd48be41add5eb/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265", size = 2183237, upload-time = "2025-05-17T17:20:33.736Z" }, - { url = "https://files.pythonhosted.org/packages/5a/6f/3af2ffedd5cfa08c631f89452c6648c4d779e7772dfc388c77c920ca6bbf/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b", size = 2343898, upload-time = "2025-05-17T17:20:36.086Z" }, - { url = "https://files.pythonhosted.org/packages/9a/dc/9060d807039ee5de6e2f260f72f3d70ac213993a804f5e67e0a73a56dd2f/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d", size = 2269197, upload-time = "2025-05-17T17:20:38.414Z" }, - { url = "https://files.pythonhosted.org/packages/f9/34/e6c8ca177cb29dcc4967fef73f5de445912f93bd0343c9c33c8e5bf8cde8/pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a", size = 1768600, upload-time = "2025-05-17T17:20:40.688Z" }, - { url = "https://files.pythonhosted.org/packages/e4/1d/89756b8d7ff623ad0160f4539da571d1f594d21ee6d68be130a6eccb39a4/pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625", size = 1799740, upload-time = "2025-05-17T17:20:42.413Z" }, - { url = "https://files.pythonhosted.org/packages/5d/61/35a64f0feaea9fd07f0d91209e7be91726eb48c0f1bfc6720647194071e4/pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39", size = 1703685, upload-time = "2025-05-17T17:20:44.388Z" }, - { url = "https://files.pythonhosted.org/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627, upload-time = "2025-05-17T17:20:47.139Z" }, - { url = "https://files.pythonhosted.org/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362, upload-time = "2025-05-17T17:20:50.392Z" }, - { url = "https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625, upload-time = "2025-05-17T17:20:52.866Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954, upload-time = "2025-05-17T17:20:55.027Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534, upload-time = "2025-05-17T17:20:57.279Z" }, - { url = "https://files.pythonhosted.org/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853, upload-time = "2025-05-17T17:20:59.322Z" }, - { url = "https://files.pythonhosted.org/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465, upload-time = "2025-05-17T17:21:03.83Z" }, - { url = "https://files.pythonhosted.org/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414, upload-time = "2025-05-17T17:21:06.72Z" }, - { url = "https://files.pythonhosted.org/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484, upload-time = "2025-05-17T17:21:08.535Z" }, - { url = "https://files.pythonhosted.org/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636, upload-time = "2025-05-17T17:21:10.393Z" }, - { url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675, upload-time = "2025-05-17T17:21:13.146Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, -] - -[[package]] -name = "pydantic-settings" -version = "2.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, -] - -[[package]] -name = "pyee" -version = "12.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0a/37/8fb6e653597b2b67ef552ed49b438d5398ba3b85a9453f8ada0fd77d455c/pyee-12.1.1.tar.gz", hash = "sha256:bbc33c09e2ff827f74191e3e5bbc6be7da02f627b7ec30d86f5ce1a6fb2424a3", size = 30915, upload-time = "2024-11-16T21:26:44.275Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/25/68/7e150cba9eeffdeb3c5cecdb6896d70c8edd46ce41c0491e12fb2b2256ff/pyee-12.1.1-py3-none-any.whl", hash = "sha256:18a19c650556bb6b32b406d7f017c8f513aceed1ef7ca618fb65de7bd2d347ef", size = 15527, upload-time = "2024-11-16T21:26:42.422Z" }, -] - -[[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 = "pyjwt" -version = "2.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, -] - -[package.optional-dependencies] -crypto = [ - { name = "cryptography" }, -] - -[[package]] -name = "pymdown-extensions" -version = "10.20.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1e/6c/9e370934bfa30e889d12e61d0dae009991294f40055c238980066a7fbd83/pymdown_extensions-10.20.1.tar.gz", hash = "sha256:e7e39c865727338d434b55f1dd8da51febcffcaebd6e1a0b9c836243f660740a", size = 852860, upload-time = "2026-01-24T05:56:56.758Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/6d/b6ee155462a0156b94312bdd82d2b92ea56e909740045a87ccb98bf52405/pymdown_extensions-10.20.1-py3-none-any.whl", hash = "sha256:24af7feacbca56504b313b7b418c4f5e1317bb5fea60f03d57be7fcc40912aa0", size = 268768, upload-time = "2026-01-24T05:56:54.537Z" }, -] - -[[package]] -name = "pypika" -version = "0.51.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/9b/76b931b449fee149359bda6ffc3bf711a7a2a2e9bfd7a32c2668e2069018/pypika-0.51.0.tar.gz", hash = "sha256:ba71a4e4f320221727619401b49b93491c589d794d5347a97bf1e8dfaf8676bb", size = 80932, upload-time = "2026-02-01T18:18:44.103Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/1c/54b7a741a5e1bdd366d6767c28d74421c7191b2e2109d2b773d28d49ecc6/pypika-0.51.0-py2.py3-none-any.whl", hash = "sha256:219f14f2dcf3c0047e25bd47227d43e227fc59170ea9bb7d14f4e0945442ce3e", size = 60581, upload-time = "2026-02-01T18:18:42.187Z" }, -] - -[[package]] -name = "pyproject-hooks" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, -] - -[[package]] -name = "pyreadline3" -version = "3.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839, upload-time = "2024-09-19T02:40:10.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178, upload-time = "2024-09-19T02:40:08.598Z" }, -] - -[[package]] -name = "pytest" -version = "9.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, -] - -[[package]] -name = "pytest-asyncio" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, -] - -[[package]] -name = "pytest-mock" -version = "3.15.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, -] - -[[package]] -name = "python-jose" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ecdsa" }, - { name = "pyasn1" }, - { name = "rsa" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c6/77/3a1c9039db7124eb039772b935f2244fbb73fc8ee65b9acf2375da1c07bf/python_jose-3.5.0.tar.gz", hash = "sha256:fb4eaa44dbeb1c26dcc69e4bd7ec54a1cb8dd64d3b4d81ef08d90ff453f2b01b", size = 92726, upload-time = "2025-05-28T17:31:54.288Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/c3/0bd11992072e6a1c513b16500a5d07f91a24017c5909b02c72c62d7ad024/python_jose-3.5.0-py2.py3-none-any.whl", hash = "sha256:abd1202f23d34dfad2c3d28cb8617b90acf34132c7afd60abd0b0b7d3cb55771", size = 34624, upload-time = "2025-05-28T17:31:52.802Z" }, -] - -[package.optional-dependencies] -cryptography = [ - { name = "cryptography" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, -] - -[[package]] -name = "pyunormalize" -version = "17.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/25/ab/b912c484cfb96ba4834efe050bbf10c9e157bd8189eb859aefba8712b136/pyunormalize-17.0.0.tar.gz", hash = "sha256:0949a3e56817e287febcaf1b0cc4b5adf0bb107628d379335938040947eec792", size = 53121, upload-time = "2025-09-28T20:53:06.141Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/80/61512483dc509e3ae8a42fb143479d1e406ce1d91f8f08d538a3dde39c6d/pyunormalize-17.0.0-py3-none-any.whl", hash = "sha256:f0d93b076f938db2b26d319d04f2b58505d1cd7a80b5b72badbe7d1aa4d2a31c", size = 51358, upload-time = "2025-09-28T20:53:04.876Z" }, -] - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { 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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "pyyaml-env-tag" -version = "1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, -] - -[[package]] -name = "redis" -version = "5.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyjwt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6a/cf/128b1b6d7086200c9f387bd4be9b2572a30b90745ef078bd8b235042dc9f/redis-5.3.1.tar.gz", hash = "sha256:ca49577a531ea64039b5a36db3d6cd1a0c7a60c34124d46924a45b956e8cf14c", size = 4626200, upload-time = "2025-07-25T08:06:27.778Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/26/5c5fa0e83c3621db835cfc1f1d789b37e7fa99ed54423b5f519beb931aa7/redis-5.3.1-py3-none-any.whl", hash = "sha256:dc1909bd24669cc31b5f67a039700b16ec30571096c5f1f0d9d2324bff31af97", size = 272833, upload-time = "2025-07-25T08:06:26.317Z" }, -] - -[[package]] -name = "referencing" -version = "0.37.0" -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/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, -] - -[[package]] -name = "regex" -version = "2026.1.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/86/07d5056945f9ec4590b518171c4254a5925832eb727b56d3c38a7476f316/regex-2026.1.15.tar.gz", hash = "sha256:164759aa25575cbc0651bef59a0b18353e54300d79ace8084c818ad8ac72b7d5", size = 414811, upload-time = "2026-01-14T23:18:02.775Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/81/10d8cf43c807d0326efe874c1b79f22bfb0fb226027b0b19ebc26d301408/regex-2026.1.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4c8fcc5793dde01641a35905d6731ee1548f02b956815f8f1cab89e515a5bdf1", size = 489398, upload-time = "2026-01-14T23:14:43.741Z" }, - { url = "https://files.pythonhosted.org/packages/90/b0/7c2a74e74ef2a7c32de724658a69a862880e3e4155cba992ba04d1c70400/regex-2026.1.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bfd876041a956e6a90ad7cdb3f6a630c07d491280bfeed4544053cd434901681", size = 291339, upload-time = "2026-01-14T23:14:45.183Z" }, - { url = "https://files.pythonhosted.org/packages/19/4d/16d0773d0c818417f4cc20aa0da90064b966d22cd62a8c46765b5bd2d643/regex-2026.1.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9250d087bc92b7d4899ccd5539a1b2334e44eee85d848c4c1aef8e221d3f8c8f", size = 289003, upload-time = "2026-01-14T23:14:47.25Z" }, - { url = "https://files.pythonhosted.org/packages/c6/e4/1fc4599450c9f0863d9406e944592d968b8d6dfd0d552a7d569e43bceada/regex-2026.1.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8a154cf6537ebbc110e24dabe53095e714245c272da9c1be05734bdad4a61aa", size = 798656, upload-time = "2026-01-14T23:14:48.77Z" }, - { url = "https://files.pythonhosted.org/packages/b2/e6/59650d73a73fa8a60b3a590545bfcf1172b4384a7df2e7fe7b9aab4e2da9/regex-2026.1.15-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8050ba2e3ea1d8731a549e83c18d2f0999fbc99a5f6bd06b4c91449f55291804", size = 864252, upload-time = "2026-01-14T23:14:50.528Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ab/1d0f4d50a1638849a97d731364c9a80fa304fec46325e48330c170ee8e80/regex-2026.1.15-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf065240704cb8951cc04972cf107063917022511273e0969bdb34fc173456c", size = 912268, upload-time = "2026-01-14T23:14:52.952Z" }, - { url = "https://files.pythonhosted.org/packages/dd/df/0d722c030c82faa1d331d1921ee268a4e8fb55ca8b9042c9341c352f17fa/regex-2026.1.15-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c32bef3e7aeee75746748643667668ef941d28b003bfc89994ecf09a10f7a1b5", size = 803589, upload-time = "2026-01-14T23:14:55.182Z" }, - { url = "https://files.pythonhosted.org/packages/66/23/33289beba7ccb8b805c6610a8913d0131f834928afc555b241caabd422a9/regex-2026.1.15-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d5eaa4a4c5b1906bd0d2508d68927f15b81821f85092e06f1a34a4254b0e1af3", size = 775700, upload-time = "2026-01-14T23:14:56.707Z" }, - { url = "https://files.pythonhosted.org/packages/e7/65/bf3a42fa6897a0d3afa81acb25c42f4b71c274f698ceabd75523259f6688/regex-2026.1.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:86c1077a3cc60d453d4084d5b9649065f3bf1184e22992bd322e1f081d3117fb", size = 787928, upload-time = "2026-01-14T23:14:58.312Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f5/13bf65864fc314f68cdd6d8ca94adcab064d4d39dbd0b10fef29a9da48fc/regex-2026.1.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:2b091aefc05c78d286657cd4db95f2e6313375ff65dcf085e42e4c04d9c8d410", size = 858607, upload-time = "2026-01-14T23:15:00.657Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/040e589834d7a439ee43fb0e1e902bc81bd58a5ba81acffe586bb3321d35/regex-2026.1.15-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:57e7d17f59f9ebfa9667e6e5a1c0127b96b87cb9cede8335482451ed00788ba4", size = 763729, upload-time = "2026-01-14T23:15:02.248Z" }, - { url = "https://files.pythonhosted.org/packages/9b/84/6921e8129687a427edf25a34a5594b588b6d88f491320b9de5b6339a4fcb/regex-2026.1.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c6c4dcdfff2c08509faa15d36ba7e5ef5fcfab25f1e8f85a0c8f45bc3a30725d", size = 850697, upload-time = "2026-01-14T23:15:03.878Z" }, - { url = "https://files.pythonhosted.org/packages/8a/87/3d06143d4b128f4229158f2de5de6c8f2485170c7221e61bf381313314b2/regex-2026.1.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf8ff04c642716a7f2048713ddc6278c5fd41faa3b9cab12607c7abecd012c22", size = 789849, upload-time = "2026-01-14T23:15:06.102Z" }, - { url = "https://files.pythonhosted.org/packages/77/69/c50a63842b6bd48850ebc7ab22d46e7a2a32d824ad6c605b218441814639/regex-2026.1.15-cp312-cp312-win32.whl", hash = "sha256:82345326b1d8d56afbe41d881fdf62f1926d7264b2fc1537f99ae5da9aad7913", size = 266279, upload-time = "2026-01-14T23:15:07.678Z" }, - { url = "https://files.pythonhosted.org/packages/f2/36/39d0b29d087e2b11fd8191e15e81cce1b635fcc845297c67f11d0d19274d/regex-2026.1.15-cp312-cp312-win_amd64.whl", hash = "sha256:4def140aa6156bc64ee9912383d4038f3fdd18fee03a6f222abd4de6357ce42a", size = 277166, upload-time = "2026-01-14T23:15:09.257Z" }, - { url = "https://files.pythonhosted.org/packages/28/32/5b8e476a12262748851fa8ab1b0be540360692325975b094e594dfebbb52/regex-2026.1.15-cp312-cp312-win_arm64.whl", hash = "sha256:c6c565d9a6e1a8d783c1948937ffc377dd5771e83bd56de8317c450a954d2056", size = 270415, upload-time = "2026-01-14T23:15:10.743Z" }, - { url = "https://files.pythonhosted.org/packages/f8/2e/6870bb16e982669b674cce3ee9ff2d1d46ab80528ee6bcc20fb2292efb60/regex-2026.1.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e69d0deeb977ffe7ed3d2e4439360089f9c3f217ada608f0f88ebd67afb6385e", size = 489164, upload-time = "2026-01-14T23:15:13.962Z" }, - { url = "https://files.pythonhosted.org/packages/dc/67/9774542e203849b0286badf67199970a44ebdb0cc5fb739f06e47ada72f8/regex-2026.1.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3601ffb5375de85a16f407854d11cca8fe3f5febbe3ac78fb2866bb220c74d10", size = 291218, upload-time = "2026-01-14T23:15:15.647Z" }, - { url = "https://files.pythonhosted.org/packages/b2/87/b0cda79f22b8dee05f774922a214da109f9a4c0eca5da2c9d72d77ea062c/regex-2026.1.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4c5ef43b5c2d4114eb8ea424bb8c9cec01d5d17f242af88b2448f5ee81caadbc", size = 288895, upload-time = "2026-01-14T23:15:17.788Z" }, - { url = "https://files.pythonhosted.org/packages/3b/6a/0041f0a2170d32be01ab981d6346c83a8934277d82c780d60b127331f264/regex-2026.1.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:968c14d4f03e10b2fd960f1d5168c1f0ac969381d3c1fcc973bc45fb06346599", size = 798680, upload-time = "2026-01-14T23:15:19.342Z" }, - { url = "https://files.pythonhosted.org/packages/58/de/30e1cfcdbe3e891324aa7568b7c968771f82190df5524fabc1138cb2d45a/regex-2026.1.15-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56a5595d0f892f214609c9f76b41b7428bed439d98dc961efafdd1354d42baae", size = 864210, upload-time = "2026-01-14T23:15:22.005Z" }, - { url = "https://files.pythonhosted.org/packages/64/44/4db2f5c5ca0ccd40ff052ae7b1e9731352fcdad946c2b812285a7505ca75/regex-2026.1.15-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf650f26087363434c4e560011f8e4e738f6f3e029b85d4904c50135b86cfa5", size = 912358, upload-time = "2026-01-14T23:15:24.569Z" }, - { url = "https://files.pythonhosted.org/packages/79/b6/e6a5665d43a7c42467138c8a2549be432bad22cbd206f5ec87162de74bd7/regex-2026.1.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18388a62989c72ac24de75f1449d0fb0b04dfccd0a1a7c1c43af5eb503d890f6", size = 803583, upload-time = "2026-01-14T23:15:26.526Z" }, - { url = "https://files.pythonhosted.org/packages/e7/53/7cd478222169d85d74d7437e74750005e993f52f335f7c04ff7adfda3310/regex-2026.1.15-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d220a2517f5893f55daac983bfa9fe998a7dbcaee4f5d27a88500f8b7873788", size = 775782, upload-time = "2026-01-14T23:15:29.352Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b5/75f9a9ee4b03a7c009fe60500fe550b45df94f0955ca29af16333ef557c5/regex-2026.1.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9c08c2fbc6120e70abff5d7f28ffb4d969e14294fb2143b4b5c7d20e46d1714", size = 787978, upload-time = "2026-01-14T23:15:31.295Z" }, - { url = "https://files.pythonhosted.org/packages/72/b3/79821c826245bbe9ccbb54f6eadb7879c722fd3e0248c17bfc90bf54e123/regex-2026.1.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7ef7d5d4bd49ec7364315167a4134a015f61e8266c6d446fc116a9ac4456e10d", size = 858550, upload-time = "2026-01-14T23:15:33.558Z" }, - { url = "https://files.pythonhosted.org/packages/4a/85/2ab5f77a1c465745bfbfcb3ad63178a58337ae8d5274315e2cc623a822fa/regex-2026.1.15-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e42844ad64194fa08d5ccb75fe6a459b9b08e6d7296bd704460168d58a388f3", size = 763747, upload-time = "2026-01-14T23:15:35.206Z" }, - { url = "https://files.pythonhosted.org/packages/6d/84/c27df502d4bfe2873a3e3a7cf1bdb2b9cc10284d1a44797cf38bed790470/regex-2026.1.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cfecdaa4b19f9ca534746eb3b55a5195d5c95b88cac32a205e981ec0a22b7d31", size = 850615, upload-time = "2026-01-14T23:15:37.523Z" }, - { url = "https://files.pythonhosted.org/packages/7d/b7/658a9782fb253680aa8ecb5ccbb51f69e088ed48142c46d9f0c99b46c575/regex-2026.1.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:08df9722d9b87834a3d701f3fca570b2be115654dbfd30179f30ab2f39d606d3", size = 789951, upload-time = "2026-01-14T23:15:39.582Z" }, - { url = "https://files.pythonhosted.org/packages/fc/2a/5928af114441e059f15b2f63e188bd00c6529b3051c974ade7444b85fcda/regex-2026.1.15-cp313-cp313-win32.whl", hash = "sha256:d426616dae0967ca225ab12c22274eb816558f2f99ccb4a1d52ca92e8baf180f", size = 266275, upload-time = "2026-01-14T23:15:42.108Z" }, - { url = "https://files.pythonhosted.org/packages/4f/16/5bfbb89e435897bff28cf0352a992ca719d9e55ebf8b629203c96b6ce4f7/regex-2026.1.15-cp313-cp313-win_amd64.whl", hash = "sha256:febd38857b09867d3ed3f4f1af7d241c5c50362e25ef43034995b77a50df494e", size = 277145, upload-time = "2026-01-14T23:15:44.244Z" }, - { url = "https://files.pythonhosted.org/packages/56/c1/a09ff7392ef4233296e821aec5f78c51be5e91ffde0d163059e50fd75835/regex-2026.1.15-cp313-cp313-win_arm64.whl", hash = "sha256:8e32f7896f83774f91499d239e24cebfadbc07639c1494bb7213983842348337", size = 270411, upload-time = "2026-01-14T23:15:45.858Z" }, - { url = "https://files.pythonhosted.org/packages/3c/38/0cfd5a78e5c6db00e6782fdae70458f89850ce95baa5e8694ab91d89744f/regex-2026.1.15-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ec94c04149b6a7b8120f9f44565722c7ae31b7a6d2275569d2eefa76b83da3be", size = 492068, upload-time = "2026-01-14T23:15:47.616Z" }, - { url = "https://files.pythonhosted.org/packages/50/72/6c86acff16cb7c959c4355826bbf06aad670682d07c8f3998d9ef4fee7cd/regex-2026.1.15-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40c86d8046915bb9aeb15d3f3f15b6fd500b8ea4485b30e1bbc799dab3fe29f8", size = 292756, upload-time = "2026-01-14T23:15:49.307Z" }, - { url = "https://files.pythonhosted.org/packages/4e/58/df7fb69eadfe76526ddfce28abdc0af09ffe65f20c2c90932e89d705153f/regex-2026.1.15-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:726ea4e727aba21643205edad8f2187ec682d3305d790f73b7a51c7587b64bdd", size = 291114, upload-time = "2026-01-14T23:15:51.484Z" }, - { url = "https://files.pythonhosted.org/packages/ed/6c/a4011cd1cf96b90d2cdc7e156f91efbd26531e822a7fbb82a43c1016678e/regex-2026.1.15-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cb740d044aff31898804e7bf1181cc72c03d11dfd19932b9911ffc19a79070a", size = 807524, upload-time = "2026-01-14T23:15:53.102Z" }, - { url = "https://files.pythonhosted.org/packages/1d/25/a53ffb73183f69c3e9f4355c4922b76d2840aee160af6af5fac229b6201d/regex-2026.1.15-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05d75a668e9ea16f832390d22131fe1e8acc8389a694c8febc3e340b0f810b93", size = 873455, upload-time = "2026-01-14T23:15:54.956Z" }, - { url = "https://files.pythonhosted.org/packages/66/0b/8b47fc2e8f97d9b4a851736f3890a5f786443aa8901061c55f24c955f45b/regex-2026.1.15-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d991483606f3dbec93287b9f35596f41aa2e92b7c2ebbb935b63f409e243c9af", size = 915007, upload-time = "2026-01-14T23:15:57.041Z" }, - { url = "https://files.pythonhosted.org/packages/c2/fa/97de0d681e6d26fabe71968dbee06dd52819e9a22fdce5dac7256c31ed84/regex-2026.1.15-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:194312a14819d3e44628a44ed6fea6898fdbecb0550089d84c403475138d0a09", size = 812794, upload-time = "2026-01-14T23:15:58.916Z" }, - { url = "https://files.pythonhosted.org/packages/22/38/e752f94e860d429654aa2b1c51880bff8dfe8f084268258adf9151cf1f53/regex-2026.1.15-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe2fda4110a3d0bc163c2e0664be44657431440722c5c5315c65155cab92f9e5", size = 781159, upload-time = "2026-01-14T23:16:00.817Z" }, - { url = "https://files.pythonhosted.org/packages/e9/a7/d739ffaef33c378fc888302a018d7f81080393d96c476b058b8c64fd2b0d/regex-2026.1.15-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:124dc36c85d34ef2d9164da41a53c1c8c122cfb1f6e1ec377a1f27ee81deb794", size = 795558, upload-time = "2026-01-14T23:16:03.267Z" }, - { url = "https://files.pythonhosted.org/packages/3e/c4/542876f9a0ac576100fc73e9c75b779f5c31e3527576cfc9cb3009dcc58a/regex-2026.1.15-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1774cd1981cd212506a23a14dba7fdeaee259f5deba2df6229966d9911e767a", size = 868427, upload-time = "2026-01-14T23:16:05.646Z" }, - { url = "https://files.pythonhosted.org/packages/fc/0f/d5655bea5b22069e32ae85a947aa564912f23758e112cdb74212848a1a1b/regex-2026.1.15-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:b5f7d8d2867152cdb625e72a530d2ccb48a3d199159144cbdd63870882fb6f80", size = 769939, upload-time = "2026-01-14T23:16:07.542Z" }, - { url = "https://files.pythonhosted.org/packages/20/06/7e18a4fa9d326daeda46d471a44ef94201c46eaa26dbbb780b5d92cbfdda/regex-2026.1.15-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:492534a0ab925d1db998defc3c302dae3616a2fc3fe2e08db1472348f096ddf2", size = 854753, upload-time = "2026-01-14T23:16:10.395Z" }, - { url = "https://files.pythonhosted.org/packages/3b/67/dc8946ef3965e166f558ef3b47f492bc364e96a265eb4a2bb3ca765c8e46/regex-2026.1.15-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c661fc820cfb33e166bf2450d3dadbda47c8d8981898adb9b6fe24e5e582ba60", size = 799559, upload-time = "2026-01-14T23:16:12.347Z" }, - { url = "https://files.pythonhosted.org/packages/a5/61/1bba81ff6d50c86c65d9fd84ce9699dd106438ee4cdb105bf60374ee8412/regex-2026.1.15-cp313-cp313t-win32.whl", hash = "sha256:99ad739c3686085e614bf77a508e26954ff1b8f14da0e3765ff7abbf7799f952", size = 268879, upload-time = "2026-01-14T23:16:14.049Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5e/cef7d4c5fb0ea3ac5c775fd37db5747f7378b29526cc83f572198924ff47/regex-2026.1.15-cp313-cp313t-win_amd64.whl", hash = "sha256:32655d17905e7ff8ba5c764c43cb124e34a9245e45b83c22e81041e1071aee10", size = 280317, upload-time = "2026-01-14T23:16:15.718Z" }, - { url = "https://files.pythonhosted.org/packages/b4/52/4317f7a5988544e34ab57b4bde0f04944c4786128c933fb09825924d3e82/regex-2026.1.15-cp313-cp313t-win_arm64.whl", hash = "sha256:b2a13dd6a95e95a489ca242319d18fc02e07ceb28fa9ad146385194d95b3c829", size = 271551, upload-time = "2026-01-14T23:16:17.533Z" }, - { url = "https://files.pythonhosted.org/packages/52/0a/47fa888ec7cbbc7d62c5f2a6a888878e76169170ead271a35239edd8f0e8/regex-2026.1.15-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:d920392a6b1f353f4aa54328c867fec3320fa50657e25f64abf17af054fc97ac", size = 489170, upload-time = "2026-01-14T23:16:19.835Z" }, - { url = "https://files.pythonhosted.org/packages/ac/c4/d000e9b7296c15737c9301708e9e7fbdea009f8e93541b6b43bdb8219646/regex-2026.1.15-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b5a28980a926fa810dbbed059547b02783952e2efd9c636412345232ddb87ff6", size = 291146, upload-time = "2026-01-14T23:16:21.541Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b6/921cc61982e538682bdf3bdf5b2c6ab6b34368da1f8e98a6c1ddc503c9cf/regex-2026.1.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:621f73a07595d83f28952d7bd1e91e9d1ed7625fb7af0064d3516674ec93a2a2", size = 288986, upload-time = "2026-01-14T23:16:23.381Z" }, - { url = "https://files.pythonhosted.org/packages/ca/33/eb7383dde0bbc93f4fb9d03453aab97e18ad4024ac7e26cef8d1f0a2cff0/regex-2026.1.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d7d92495f47567a9b1669c51fc8d6d809821849063d168121ef801bbc213846", size = 799098, upload-time = "2026-01-14T23:16:25.088Z" }, - { url = "https://files.pythonhosted.org/packages/27/56/b664dccae898fc8d8b4c23accd853f723bde0f026c747b6f6262b688029c/regex-2026.1.15-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8dd16fba2758db7a3780a051f245539c4451ca20910f5a5e6ea1c08d06d4a76b", size = 864980, upload-time = "2026-01-14T23:16:27.297Z" }, - { url = "https://files.pythonhosted.org/packages/16/40/0999e064a170eddd237bae9ccfcd8f28b3aa98a38bf727a086425542a4fc/regex-2026.1.15-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1e1808471fbe44c1a63e5f577a1d5f02fe5d66031dcbdf12f093ffc1305a858e", size = 911607, upload-time = "2026-01-14T23:16:29.235Z" }, - { url = "https://files.pythonhosted.org/packages/07/78/c77f644b68ab054e5a674fb4da40ff7bffb2c88df58afa82dbf86573092d/regex-2026.1.15-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0751a26ad39d4f2ade8fe16c59b2bf5cb19eb3d2cd543e709e583d559bd9efde", size = 803358, upload-time = "2026-01-14T23:16:31.369Z" }, - { url = "https://files.pythonhosted.org/packages/27/31/d4292ea8566eaa551fafc07797961c5963cf5235c797cc2ae19b85dfd04d/regex-2026.1.15-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0f0c7684c7f9ca241344ff95a1de964f257a5251968484270e91c25a755532c5", size = 775833, upload-time = "2026-01-14T23:16:33.141Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b2/cff3bf2fea4133aa6fb0d1e370b37544d18c8350a2fa118c7e11d1db0e14/regex-2026.1.15-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:74f45d170a21df41508cb67165456538425185baaf686281fa210d7e729abc34", size = 788045, upload-time = "2026-01-14T23:16:35.005Z" }, - { url = "https://files.pythonhosted.org/packages/8d/99/2cb9b69045372ec877b6f5124bda4eb4253bc58b8fe5848c973f752bc52c/regex-2026.1.15-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f1862739a1ffb50615c0fde6bae6569b5efbe08d98e59ce009f68a336f64da75", size = 859374, upload-time = "2026-01-14T23:16:36.919Z" }, - { url = "https://files.pythonhosted.org/packages/09/16/710b0a5abe8e077b1729a562d2f297224ad079f3a66dce46844c193416c8/regex-2026.1.15-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:453078802f1b9e2b7303fb79222c054cb18e76f7bdc220f7530fdc85d319f99e", size = 763940, upload-time = "2026-01-14T23:16:38.685Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/7585c8e744e40eb3d32f119191969b91de04c073fca98ec14299041f6e7e/regex-2026.1.15-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:a30a68e89e5a218b8b23a52292924c1f4b245cb0c68d1cce9aec9bbda6e2c160", size = 850112, upload-time = "2026-01-14T23:16:40.646Z" }, - { url = "https://files.pythonhosted.org/packages/af/d6/43e1dd85df86c49a347aa57c1f69d12c652c7b60e37ec162e3096194a278/regex-2026.1.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9479cae874c81bf610d72b85bb681a94c95722c127b55445285fb0e2c82db8e1", size = 789586, upload-time = "2026-01-14T23:16:42.799Z" }, - { url = "https://files.pythonhosted.org/packages/93/38/77142422f631e013f316aaae83234c629555729a9fbc952b8a63ac91462a/regex-2026.1.15-cp314-cp314-win32.whl", hash = "sha256:d639a750223132afbfb8f429c60d9d318aeba03281a5f1ab49f877456448dcf1", size = 271691, upload-time = "2026-01-14T23:16:44.671Z" }, - { url = "https://files.pythonhosted.org/packages/4a/a9/ab16b4649524ca9e05213c1cdbb7faa85cc2aa90a0230d2f796cbaf22736/regex-2026.1.15-cp314-cp314-win_amd64.whl", hash = "sha256:4161d87f85fa831e31469bfd82c186923070fc970b9de75339b68f0c75b51903", size = 280422, upload-time = "2026-01-14T23:16:46.607Z" }, - { url = "https://files.pythonhosted.org/packages/be/2a/20fd057bf3521cb4791f69f869635f73e0aaf2b9ad2d260f728144f9047c/regex-2026.1.15-cp314-cp314-win_arm64.whl", hash = "sha256:91c5036ebb62663a6b3999bdd2e559fd8456d17e2b485bf509784cd31a8b1705", size = 273467, upload-time = "2026-01-14T23:16:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/ad/77/0b1e81857060b92b9cad239104c46507dd481b3ff1fa79f8e7f865aae38a/regex-2026.1.15-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ee6854c9000a10938c79238de2379bea30c82e4925a371711af45387df35cab8", size = 492073, upload-time = "2026-01-14T23:16:51.154Z" }, - { url = "https://files.pythonhosted.org/packages/70/f3/f8302b0c208b22c1e4f423147e1913fd475ddd6230565b299925353de644/regex-2026.1.15-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c2b80399a422348ce5de4fe40c418d6299a0fa2803dd61dc0b1a2f28e280fcf", size = 292757, upload-time = "2026-01-14T23:16:53.08Z" }, - { url = "https://files.pythonhosted.org/packages/bf/f0/ef55de2460f3b4a6da9d9e7daacd0cb79d4ef75c64a2af316e68447f0df0/regex-2026.1.15-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:dca3582bca82596609959ac39e12b7dad98385b4fefccb1151b937383cec547d", size = 291122, upload-time = "2026-01-14T23:16:55.383Z" }, - { url = "https://files.pythonhosted.org/packages/cf/55/bb8ccbacabbc3a11d863ee62a9f18b160a83084ea95cdfc5d207bfc3dd75/regex-2026.1.15-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef71d476caa6692eea743ae5ea23cde3260677f70122c4d258ca952e5c2d4e84", size = 807761, upload-time = "2026-01-14T23:16:57.251Z" }, - { url = "https://files.pythonhosted.org/packages/8f/84/f75d937f17f81e55679a0509e86176e29caa7298c38bd1db7ce9c0bf6075/regex-2026.1.15-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c243da3436354f4af6c3058a3f81a97d47ea52c9bd874b52fd30274853a1d5df", size = 873538, upload-time = "2026-01-14T23:16:59.349Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d9/0da86327df70349aa8d86390da91171bd3ca4f0e7c1d1d453a9c10344da3/regex-2026.1.15-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8355ad842a7c7e9e5e55653eade3b7d1885ba86f124dd8ab1f722f9be6627434", size = 915066, upload-time = "2026-01-14T23:17:01.607Z" }, - { url = "https://files.pythonhosted.org/packages/2a/5e/f660fb23fc77baa2a61aa1f1fe3a4eea2bbb8a286ddec148030672e18834/regex-2026.1.15-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f192a831d9575271a22d804ff1a5355355723f94f31d9eef25f0d45a152fdc1a", size = 812938, upload-time = "2026-01-14T23:17:04.366Z" }, - { url = "https://files.pythonhosted.org/packages/69/33/a47a29bfecebbbfd1e5cd3f26b28020a97e4820f1c5148e66e3b7d4b4992/regex-2026.1.15-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:166551807ec20d47ceaeec380081f843e88c8949780cd42c40f18d16168bed10", size = 781314, upload-time = "2026-01-14T23:17:06.378Z" }, - { url = "https://files.pythonhosted.org/packages/65/ec/7ec2bbfd4c3f4e494a24dec4c6943a668e2030426b1b8b949a6462d2c17b/regex-2026.1.15-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f9ca1cbdc0fbfe5e6e6f8221ef2309988db5bcede52443aeaee9a4ad555e0dac", size = 795652, upload-time = "2026-01-14T23:17:08.521Z" }, - { url = "https://files.pythonhosted.org/packages/46/79/a5d8651ae131fe27d7c521ad300aa7f1c7be1dbeee4d446498af5411b8a9/regex-2026.1.15-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b30bcbd1e1221783c721483953d9e4f3ab9c5d165aa709693d3f3946747b1aea", size = 868550, upload-time = "2026-01-14T23:17:10.573Z" }, - { url = "https://files.pythonhosted.org/packages/06/b7/25635d2809664b79f183070786a5552dd4e627e5aedb0065f4e3cf8ee37d/regex-2026.1.15-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2a8d7b50c34578d0d3bf7ad58cde9652b7d683691876f83aedc002862a35dc5e", size = 769981, upload-time = "2026-01-14T23:17:12.871Z" }, - { url = "https://files.pythonhosted.org/packages/16/8b/fc3fcbb2393dcfa4a6c5ffad92dc498e842df4581ea9d14309fcd3c55fb9/regex-2026.1.15-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9d787e3310c6a6425eb346be4ff2ccf6eece63017916fd77fe8328c57be83521", size = 854780, upload-time = "2026-01-14T23:17:14.837Z" }, - { url = "https://files.pythonhosted.org/packages/d0/38/dde117c76c624713c8a2842530be9c93ca8b606c0f6102d86e8cd1ce8bea/regex-2026.1.15-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:619843841e220adca114118533a574a9cd183ed8a28b85627d2844c500a2b0db", size = 799778, upload-time = "2026-01-14T23:17:17.369Z" }, - { url = "https://files.pythonhosted.org/packages/e3/0d/3a6cfa9ae99606afb612d8fb7a66b245a9d5ff0f29bb347c8a30b6ad561b/regex-2026.1.15-cp314-cp314t-win32.whl", hash = "sha256:e90b8db97f6f2c97eb045b51a6b2c5ed69cedd8392459e0642d4199b94fabd7e", size = 274667, upload-time = "2026-01-14T23:17:19.301Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b2/297293bb0742fd06b8d8e2572db41a855cdf1cae0bf009b1cb74fe07e196/regex-2026.1.15-cp314-cp314t-win_amd64.whl", hash = "sha256:5ef19071f4ac9f0834793af85bd04a920b4407715624e40cb7a0631a11137cdf", size = 284386, upload-time = "2026-01-14T23:17:21.231Z" }, - { url = "https://files.pythonhosted.org/packages/95/e4/a3b9480c78cf8ee86626cb06f8d931d74d775897d44201ccb813097ae697/regex-2026.1.15-cp314-cp314t-win_arm64.whl", hash = "sha256:ca89c5e596fc05b015f27561b3793dc2fa0917ea0d7507eebb448efd35274a70", size = 274837, upload-time = "2026-01-14T23:17:23.146Z" }, -] - -[[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 = "requests-oauthlib" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "oauthlib" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, -] - -[[package]] -name = "rich" -version = "13.9.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149, upload-time = "2024-11-01T16:43:57.873Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424, upload-time = "2024-11-01T16:43:55.817Z" }, -] - -[[package]] -name = "rlp" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "eth-utils" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1b/2d/439b0728a92964a04d9c88ea1ca9ebb128893fbbd5834faa31f987f2fd4c/rlp-4.1.0.tar.gz", hash = "sha256:be07564270a96f3e225e2c107db263de96b5bc1f27722d2855bd3459a08e95a9", size = 33429, upload-time = "2025-02-04T22:05:59.089Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/fb/e4c0ced9893b84ac95b7181d69a9786ce5879aeb3bbbcbba80a164f85d6a/rlp-4.1.0-py3-none-any.whl", hash = "sha256:8eca394c579bad34ee0b937aecb96a57052ff3716e19c7a578883e767bc5da6f", size = 19973, upload-time = "2025-02-04T22:05:57.05Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, -] - -[[package]] -name = "rsa" -version = "4.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyasn1" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, -] - -[[package]] -name = "ruff" -version = "0.9.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/80/63/77ecca9d21177600f551d1c58ab0e5a0b260940ea7312195bd2a4798f8a8/ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0", size = 3553799, upload-time = "2025-01-16T13:22:20.512Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/b9/0e168e4e7fb3af851f739e8f07889b91d1a33a30fca8c29fa3149d6b03ec/ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347", size = 11652408, upload-time = "2025-01-16T13:21:12.732Z" }, - { url = "https://files.pythonhosted.org/packages/2c/22/08ede5db17cf701372a461d1cb8fdde037da1d4fa622b69ac21960e6237e/ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00", size = 11587553, upload-time = "2025-01-16T13:21:17.716Z" }, - { url = "https://files.pythonhosted.org/packages/42/05/dedfc70f0bf010230229e33dec6e7b2235b2a1b8cbb2a991c710743e343f/ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4", size = 11020755, upload-time = "2025-01-16T13:21:21.746Z" }, - { url = "https://files.pythonhosted.org/packages/df/9b/65d87ad9b2e3def67342830bd1af98803af731243da1255537ddb8f22209/ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d", size = 11826502, upload-time = "2025-01-16T13:21:26.135Z" }, - { url = "https://files.pythonhosted.org/packages/93/02/f2239f56786479e1a89c3da9bc9391120057fc6f4a8266a5b091314e72ce/ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c", size = 11390562, upload-time = "2025-01-16T13:21:29.026Z" }, - { url = "https://files.pythonhosted.org/packages/c9/37/d3a854dba9931f8cb1b2a19509bfe59e00875f48ade632e95aefcb7a0aee/ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f", size = 12548968, upload-time = "2025-01-16T13:21:34.147Z" }, - { url = "https://files.pythonhosted.org/packages/fa/c3/c7b812bb256c7a1d5553433e95980934ffa85396d332401f6b391d3c4569/ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684", size = 13187155, upload-time = "2025-01-16T13:21:40.494Z" }, - { url = "https://files.pythonhosted.org/packages/bd/5a/3c7f9696a7875522b66aa9bba9e326e4e5894b4366bd1dc32aa6791cb1ff/ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d", size = 12704674, upload-time = "2025-01-16T13:21:45.041Z" }, - { url = "https://files.pythonhosted.org/packages/be/d6/d908762257a96ce5912187ae9ae86792e677ca4f3dc973b71e7508ff6282/ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df", size = 14529328, upload-time = "2025-01-16T13:21:49.45Z" }, - { url = "https://files.pythonhosted.org/packages/2d/c2/049f1e6755d12d9cd8823242fa105968f34ee4c669d04cac8cea51a50407/ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247", size = 12385955, upload-time = "2025-01-16T13:21:52.71Z" }, - { url = "https://files.pythonhosted.org/packages/91/5a/a9bdb50e39810bd9627074e42743b00e6dc4009d42ae9f9351bc3dbc28e7/ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e", size = 11810149, upload-time = "2025-01-16T13:21:57.098Z" }, - { url = "https://files.pythonhosted.org/packages/e5/fd/57df1a0543182f79a1236e82a79c68ce210efb00e97c30657d5bdb12b478/ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe", size = 11479141, upload-time = "2025-01-16T13:22:00.585Z" }, - { url = "https://files.pythonhosted.org/packages/dc/16/bc3fd1d38974f6775fc152a0554f8c210ff80f2764b43777163c3c45d61b/ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb", size = 12014073, upload-time = "2025-01-16T13:22:03.956Z" }, - { url = "https://files.pythonhosted.org/packages/47/6b/e4ca048a8f2047eb652e1e8c755f384d1b7944f69ed69066a37acd4118b0/ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a", size = 12435758, upload-time = "2025-01-16T13:22:07.73Z" }, - { url = "https://files.pythonhosted.org/packages/c2/40/4d3d6c979c67ba24cf183d29f706051a53c36d78358036a9cd21421582ab/ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145", size = 9796916, upload-time = "2025-01-16T13:22:10.894Z" }, - { url = "https://files.pythonhosted.org/packages/c3/ef/7f548752bdb6867e6939489c87fe4da489ab36191525fadc5cede2a6e8e2/ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5", size = 10773080, upload-time = "2025-01-16T13:22:14.155Z" }, - { url = "https://files.pythonhosted.org/packages/0e/4e/33df635528292bd2d18404e4daabcd74ca8a9853b2e1df85ed3d32d24362/ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6", size = 10001738, upload-time = "2025-01-16T13:22:18.121Z" }, -] - -[[package]] -name = "safetensors" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, - { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, - { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, - { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, - { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, - { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, - { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, - { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, - { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, - { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, - { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, -] - -[[package]] -name = "scikit-learn" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "joblib" }, - { name = "numpy" }, - { name = "scipy" }, - { name = "threadpoolctl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/74/e6a7cc4b820e95cc38cf36cd74d5aa2b42e8ffc2d21fe5a9a9c45c1c7630/scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e", size = 8548242, upload-time = "2025-12-10T07:07:51.568Z" }, - { url = "https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76", size = 8079075, upload-time = "2025-12-10T07:07:53.697Z" }, - { url = "https://files.pythonhosted.org/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492, upload-time = "2025-12-10T07:07:55.574Z" }, - { url = "https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904, upload-time = "2025-12-10T07:07:57.666Z" }, - { url = "https://files.pythonhosted.org/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809", size = 8019359, upload-time = "2025-12-10T07:07:59.838Z" }, - { url = "https://files.pythonhosted.org/packages/24/90/344a67811cfd561d7335c1b96ca21455e7e472d281c3c279c4d3f2300236/scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb", size = 7641898, upload-time = "2025-12-10T07:08:01.36Z" }, - { url = "https://files.pythonhosted.org/packages/03/aa/e22e0768512ce9255eba34775be2e85c2048da73da1193e841707f8f039c/scikit_learn-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a", size = 8513770, upload-time = "2025-12-10T07:08:03.251Z" }, - { url = "https://files.pythonhosted.org/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e", size = 8044458, upload-time = "2025-12-10T07:08:05.336Z" }, - { url = "https://files.pythonhosted.org/packages/2d/5a/3f1caed8765f33eabb723596666da4ebbf43d11e96550fb18bdec42b467b/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57", size = 8610341, upload-time = "2025-12-10T07:08:07.732Z" }, - { url = "https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e", size = 8900022, upload-time = "2025-12-10T07:08:09.862Z" }, - { url = "https://files.pythonhosted.org/packages/1c/f9/9b7563caf3ec8873e17a31401858efab6b39a882daf6c1bfa88879c0aa11/scikit_learn-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271", size = 7989409, upload-time = "2025-12-10T07:08:12.028Z" }, - { url = "https://files.pythonhosted.org/packages/49/bd/1f4001503650e72c4f6009ac0c4413cb17d2d601cef6f71c0453da2732fc/scikit_learn-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3", size = 7619760, upload-time = "2025-12-10T07:08:13.688Z" }, - { url = "https://files.pythonhosted.org/packages/d2/7d/a630359fc9dcc95496588c8d8e3245cc8fd81980251079bc09c70d41d951/scikit_learn-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735", size = 8826045, upload-time = "2025-12-10T07:08:15.215Z" }, - { url = "https://files.pythonhosted.org/packages/cc/56/a0c86f6930cfcd1c7054a2bc417e26960bb88d32444fe7f71d5c2cfae891/scikit_learn-1.8.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd", size = 8420324, upload-time = "2025-12-10T07:08:17.561Z" }, - { url = "https://files.pythonhosted.org/packages/46/1e/05962ea1cebc1cf3876667ecb14c283ef755bf409993c5946ade3b77e303/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e", size = 8680651, upload-time = "2025-12-10T07:08:19.952Z" }, - { url = "https://files.pythonhosted.org/packages/fe/56/a85473cd75f200c9759e3a5f0bcab2d116c92a8a02ee08ccd73b870f8bb4/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb", size = 8925045, upload-time = "2025-12-10T07:08:22.11Z" }, - { url = "https://files.pythonhosted.org/packages/cc/b7/64d8cfa896c64435ae57f4917a548d7ac7a44762ff9802f75a79b77cb633/scikit_learn-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702", size = 8507994, upload-time = "2025-12-10T07:08:23.943Z" }, - { url = "https://files.pythonhosted.org/packages/5e/37/e192ea709551799379958b4c4771ec507347027bb7c942662c7fbeba31cb/scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde", size = 7869518, upload-time = "2025-12-10T07:08:25.71Z" }, - { url = "https://files.pythonhosted.org/packages/24/05/1af2c186174cc92dcab2233f327336058c077d38f6fe2aceb08e6ab4d509/scikit_learn-1.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3", size = 8528667, upload-time = "2025-12-10T07:08:27.541Z" }, - { url = "https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7", size = 8066524, upload-time = "2025-12-10T07:08:29.822Z" }, - { url = "https://files.pythonhosted.org/packages/be/ce/a0623350aa0b68647333940ee46fe45086c6060ec604874e38e9ab7d8e6c/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6", size = 8657133, upload-time = "2025-12-10T07:08:31.865Z" }, - { url = "https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4", size = 8923223, upload-time = "2025-12-10T07:08:34.166Z" }, - { url = "https://files.pythonhosted.org/packages/76/18/a8def8f91b18cd1ba6e05dbe02540168cb24d47e8dcf69e8d00b7da42a08/scikit_learn-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6", size = 8096518, upload-time = "2025-12-10T07:08:36.339Z" }, - { url = "https://files.pythonhosted.org/packages/d1/77/482076a678458307f0deb44e29891d6022617b2a64c840c725495bee343f/scikit_learn-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242", size = 7754546, upload-time = "2025-12-10T07:08:38.128Z" }, - { url = "https://files.pythonhosted.org/packages/2d/d1/ef294ca754826daa043b2a104e59960abfab4cf653891037d19dd5b6f3cf/scikit_learn-1.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7", size = 8848305, upload-time = "2025-12-10T07:08:41.013Z" }, - { url = "https://files.pythonhosted.org/packages/5b/e2/b1f8b05138ee813b8e1a4149f2f0d289547e60851fd1bb268886915adbda/scikit_learn-1.8.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9", size = 8432257, upload-time = "2025-12-10T07:08:42.873Z" }, - { url = "https://files.pythonhosted.org/packages/26/11/c32b2138a85dcb0c99f6afd13a70a951bfdff8a6ab42d8160522542fb647/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f", size = 8678673, upload-time = "2025-12-10T07:08:45.362Z" }, - { url = "https://files.pythonhosted.org/packages/c7/57/51f2384575bdec454f4fe4e7a919d696c9ebce914590abf3e52d47607ab8/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9", size = 8922467, upload-time = "2025-12-10T07:08:47.408Z" }, - { url = "https://files.pythonhosted.org/packages/35/4d/748c9e2872637a57981a04adc038dacaa16ba8ca887b23e34953f0b3f742/scikit_learn-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2", size = 8774395, upload-time = "2025-12-10T07:08:49.337Z" }, - { url = "https://files.pythonhosted.org/packages/60/22/d7b2ebe4704a5e50790ba089d5c2ae308ab6bb852719e6c3bd4f04c3a363/scikit_learn-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c", size = 8002647, upload-time = "2025-12-10T07:08:51.601Z" }, -] - -[[package]] -name = "scipy" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/56/3e/9cca699f3486ce6bc12ff46dc2031f1ec8eb9ccc9a320fdaf925f1417426/scipy-1.17.0.tar.gz", hash = "sha256:2591060c8e648d8b96439e111ac41fd8342fdeff1876be2e19dea3fe8930454e", size = 30396830, upload-time = "2026-01-10T21:34:23.009Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/11/7241a63e73ba5a516f1930ac8d5b44cbbfabd35ac73a2d08ca206df007c4/scipy-1.17.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:0d5018a57c24cb1dd828bcf51d7b10e65986d549f52ef5adb6b4d1ded3e32a57", size = 31364580, upload-time = "2026-01-10T21:25:25.717Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1d/5057f812d4f6adc91a20a2d6f2ebcdb517fdbc87ae3acc5633c9b97c8ba5/scipy-1.17.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:88c22af9e5d5a4f9e027e26772cc7b5922fab8bcc839edb3ae33de404feebd9e", size = 27969012, upload-time = "2026-01-10T21:25:30.921Z" }, - { url = "https://files.pythonhosted.org/packages/e3/21/f6ec556c1e3b6ec4e088da667d9987bb77cc3ab3026511f427dc8451187d/scipy-1.17.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f3cd947f20fe17013d401b64e857c6b2da83cae567adbb75b9dcba865abc66d8", size = 20140691, upload-time = "2026-01-10T21:25:34.802Z" }, - { url = "https://files.pythonhosted.org/packages/7a/fe/5e5ad04784964ba964a96f16c8d4676aa1b51357199014dce58ab7ec5670/scipy-1.17.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e8c0b331c2c1f531eb51f1b4fc9ba709521a712cce58f1aa627bc007421a5306", size = 22463015, upload-time = "2026-01-10T21:25:39.277Z" }, - { url = "https://files.pythonhosted.org/packages/4a/69/7c347e857224fcaf32a34a05183b9d8a7aca25f8f2d10b8a698b8388561a/scipy-1.17.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5194c445d0a1c7a6c1a4a4681b6b7c71baad98ff66d96b949097e7513c9d6742", size = 32724197, upload-time = "2026-01-10T21:25:44.084Z" }, - { url = "https://files.pythonhosted.org/packages/d1/fe/66d73b76d378ba8cc2fe605920c0c75092e3a65ae746e1e767d9d020a75a/scipy-1.17.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9eeb9b5f5997f75507814ed9d298ab23f62cf79f5a3ef90031b1ee2506abdb5b", size = 35009148, upload-time = "2026-01-10T21:25:50.591Z" }, - { url = "https://files.pythonhosted.org/packages/af/07/07dec27d9dc41c18d8c43c69e9e413431d20c53a0339c388bcf72f353c4b/scipy-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:40052543f7bbe921df4408f46003d6f01c6af109b9e2c8a66dd1cf6cf57f7d5d", size = 34798766, upload-time = "2026-01-10T21:25:59.41Z" }, - { url = "https://files.pythonhosted.org/packages/81/61/0470810c8a093cdacd4ba7504b8a218fd49ca070d79eca23a615f5d9a0b0/scipy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0cf46c8013fec9d3694dc572f0b54100c28405d55d3e2cb15e2895b25057996e", size = 37405953, upload-time = "2026-01-10T21:26:07.75Z" }, - { url = "https://files.pythonhosted.org/packages/92/ce/672ed546f96d5d41ae78c4b9b02006cedd0b3d6f2bf5bb76ea455c320c28/scipy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:0937a0b0d8d593a198cededd4c439a0ea216a3f36653901ea1f3e4be949056f8", size = 36328121, upload-time = "2026-01-10T21:26:16.509Z" }, - { url = "https://files.pythonhosted.org/packages/9d/21/38165845392cae67b61843a52c6455d47d0cc2a40dd495c89f4362944654/scipy-1.17.0-cp312-cp312-win_arm64.whl", hash = "sha256:f603d8a5518c7426414d1d8f82e253e454471de682ce5e39c29adb0df1efb86b", size = 24314368, upload-time = "2026-01-10T21:26:23.087Z" }, - { url = "https://files.pythonhosted.org/packages/0c/51/3468fdfd49387ddefee1636f5cf6d03ce603b75205bf439bbf0e62069bfd/scipy-1.17.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:65ec32f3d32dfc48c72df4291345dae4f048749bc8d5203ee0a3f347f96c5ce6", size = 31344101, upload-time = "2026-01-10T21:26:30.25Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9a/9406aec58268d437636069419e6977af953d1e246df941d42d3720b7277b/scipy-1.17.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:1f9586a58039d7229ce77b52f8472c972448cded5736eaf102d5658bbac4c269", size = 27950385, upload-time = "2026-01-10T21:26:36.801Z" }, - { url = "https://files.pythonhosted.org/packages/4f/98/e7342709e17afdfd1b26b56ae499ef4939b45a23a00e471dfb5375eea205/scipy-1.17.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9fad7d3578c877d606b1150135c2639e9de9cecd3705caa37b66862977cc3e72", size = 20122115, upload-time = "2026-01-10T21:26:42.107Z" }, - { url = "https://files.pythonhosted.org/packages/fd/0e/9eeeb5357a64fd157cbe0302c213517c541cc16b8486d82de251f3c68ede/scipy-1.17.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:423ca1f6584fc03936972b5f7c06961670dbba9f234e71676a7c7ccf938a0d61", size = 22442402, upload-time = "2026-01-10T21:26:48.029Z" }, - { url = "https://files.pythonhosted.org/packages/c9/10/be13397a0e434f98e0c79552b2b584ae5bb1c8b2be95db421533bbca5369/scipy-1.17.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe508b5690e9eaaa9467fc047f833af58f1152ae51a0d0aed67aa5801f4dd7d6", size = 32696338, upload-time = "2026-01-10T21:26:55.521Z" }, - { url = "https://files.pythonhosted.org/packages/63/1e/12fbf2a3bb240161651c94bb5cdd0eae5d4e8cc6eaeceb74ab07b12a753d/scipy-1.17.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6680f2dfd4f6182e7d6db161344537da644d1cf85cf293f015c60a17ecf08752", size = 34977201, upload-time = "2026-01-10T21:27:03.501Z" }, - { url = "https://files.pythonhosted.org/packages/19/5b/1a63923e23ccd20bd32156d7dd708af5bbde410daa993aa2500c847ab2d2/scipy-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eec3842ec9ac9de5917899b277428886042a93db0b227ebbe3a333b64ec7643d", size = 34777384, upload-time = "2026-01-10T21:27:11.423Z" }, - { url = "https://files.pythonhosted.org/packages/39/22/b5da95d74edcf81e540e467202a988c50fef41bd2011f46e05f72ba07df6/scipy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d7425fcafbc09a03731e1bc05581f5fad988e48c6a861f441b7ab729a49a55ea", size = 37379586, upload-time = "2026-01-10T21:27:20.171Z" }, - { url = "https://files.pythonhosted.org/packages/b9/b6/8ac583d6da79e7b9e520579f03007cb006f063642afd6b2eeb16b890bf93/scipy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:87b411e42b425b84777718cc41516b8a7e0795abfa8e8e1d573bf0ef014f0812", size = 36287211, upload-time = "2026-01-10T21:28:43.122Z" }, - { url = "https://files.pythonhosted.org/packages/55/fb/7db19e0b3e52f882b420417644ec81dd57eeef1bd1705b6f689d8ff93541/scipy-1.17.0-cp313-cp313-win_arm64.whl", hash = "sha256:357ca001c6e37601066092e7c89cca2f1ce74e2a520ca78d063a6d2201101df2", size = 24312646, upload-time = "2026-01-10T21:28:49.893Z" }, - { url = "https://files.pythonhosted.org/packages/20/b6/7feaa252c21cc7aff335c6c55e1b90ab3e3306da3f048109b8b639b94648/scipy-1.17.0-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:ec0827aa4d36cb79ff1b81de898e948a51ac0b9b1c43e4a372c0508c38c0f9a3", size = 31693194, upload-time = "2026-01-10T21:27:27.454Z" }, - { url = "https://files.pythonhosted.org/packages/76/bb/bbb392005abce039fb7e672cb78ac7d158700e826b0515cab6b5b60c26fb/scipy-1.17.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:819fc26862b4b3c73a60d486dbb919202f3d6d98c87cf20c223511429f2d1a97", size = 28365415, upload-time = "2026-01-10T21:27:34.26Z" }, - { url = "https://files.pythonhosted.org/packages/37/da/9d33196ecc99fba16a409c691ed464a3a283ac454a34a13a3a57c0d66f3a/scipy-1.17.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:363ad4ae2853d88ebcde3ae6ec46ccca903ea9835ee8ba543f12f575e7b07e4e", size = 20537232, upload-time = "2026-01-10T21:27:40.306Z" }, - { url = "https://files.pythonhosted.org/packages/56/9d/f4b184f6ddb28e9a5caea36a6f98e8ecd2a524f9127354087ce780885d83/scipy-1.17.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:979c3a0ff8e5ba254d45d59ebd38cde48fce4f10b5125c680c7a4bfe177aab07", size = 22791051, upload-time = "2026-01-10T21:27:46.539Z" }, - { url = "https://files.pythonhosted.org/packages/9b/9d/025cccdd738a72140efc582b1641d0dd4caf2e86c3fb127568dc80444e6e/scipy-1.17.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:130d12926ae34399d157de777472bf82e9061c60cc081372b3118edacafe1d00", size = 32815098, upload-time = "2026-01-10T21:27:54.389Z" }, - { url = "https://files.pythonhosted.org/packages/48/5f/09b879619f8bca15ce392bfc1894bd9c54377e01d1b3f2f3b595a1b4d945/scipy-1.17.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e886000eb4919eae3a44f035e63f0fd8b651234117e8f6f29bad1cd26e7bc45", size = 35031342, upload-time = "2026-01-10T21:28:03.012Z" }, - { url = "https://files.pythonhosted.org/packages/f2/9a/f0f0a9f0aa079d2f106555b984ff0fbb11a837df280f04f71f056ea9c6e4/scipy-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:13c4096ac6bc31d706018f06a49abe0485f96499deb82066b94d19b02f664209", size = 34893199, upload-time = "2026-01-10T21:28:10.832Z" }, - { url = "https://files.pythonhosted.org/packages/90/b8/4f0f5cf0c5ea4d7548424e6533e6b17d164f34a6e2fb2e43ffebb6697b06/scipy-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cacbaddd91fcffde703934897c5cd2c7cb0371fac195d383f4e1f1c5d3f3bd04", size = 37438061, upload-time = "2026-01-10T21:28:19.684Z" }, - { url = "https://files.pythonhosted.org/packages/f9/cc/2bd59140ed3b2fa2882fb15da0a9cb1b5a6443d67cfd0d98d4cec83a57ec/scipy-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:edce1a1cf66298cccdc48a1bdf8fb10a3bf58e8b58d6c3883dd1530e103f87c0", size = 36328593, upload-time = "2026-01-10T21:28:28.007Z" }, - { url = "https://files.pythonhosted.org/packages/13/1b/c87cc44a0d2c7aaf0f003aef2904c3d097b422a96c7e7c07f5efd9073c1b/scipy-1.17.0-cp313-cp313t-win_arm64.whl", hash = "sha256:30509da9dbec1c2ed8f168b8d8aa853bc6723fede1dbc23c7d43a56f5ab72a67", size = 24625083, upload-time = "2026-01-10T21:28:35.188Z" }, - { url = "https://files.pythonhosted.org/packages/1a/2d/51006cd369b8e7879e1c630999a19d1fbf6f8b5ed3e33374f29dc87e53b3/scipy-1.17.0-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:c17514d11b78be8f7e6331b983a65a7f5ca1fd037b95e27b280921fe5606286a", size = 31346803, upload-time = "2026-01-10T21:28:57.24Z" }, - { url = "https://files.pythonhosted.org/packages/d6/2e/2349458c3ce445f53a6c93d4386b1c4c5c0c540917304c01222ff95ff317/scipy-1.17.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:4e00562e519c09da34c31685f6acc3aa384d4d50604db0f245c14e1b4488bfa2", size = 27967182, upload-time = "2026-01-10T21:29:04.107Z" }, - { url = "https://files.pythonhosted.org/packages/5e/7c/df525fbfa77b878d1cfe625249529514dc02f4fd5f45f0f6295676a76528/scipy-1.17.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f7df7941d71314e60a481e02d5ebcb3f0185b8d799c70d03d8258f6c80f3d467", size = 20139125, upload-time = "2026-01-10T21:29:10.179Z" }, - { url = "https://files.pythonhosted.org/packages/33/11/fcf9d43a7ed1234d31765ec643b0515a85a30b58eddccc5d5a4d12b5f194/scipy-1.17.0-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:aabf057c632798832f071a8dde013c2e26284043934f53b00489f1773b33527e", size = 22443554, upload-time = "2026-01-10T21:29:15.888Z" }, - { url = "https://files.pythonhosted.org/packages/80/5c/ea5d239cda2dd3d31399424967a24d556cf409fbea7b5b21412b0fd0a44f/scipy-1.17.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a38c3337e00be6fd8a95b4ed66b5d988bac4ec888fd922c2ea9fe5fb1603dd67", size = 32757834, upload-time = "2026-01-10T21:29:23.406Z" }, - { url = "https://files.pythonhosted.org/packages/b8/7e/8c917cc573310e5dc91cbeead76f1b600d3fb17cf0969db02c9cf92e3cfa/scipy-1.17.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00fb5f8ec8398ad90215008d8b6009c9db9fa924fd4c7d6be307c6f945f9cd73", size = 34995775, upload-time = "2026-01-10T21:29:31.915Z" }, - { url = "https://files.pythonhosted.org/packages/c5/43/176c0c3c07b3f7df324e7cdd933d3e2c4898ca202b090bd5ba122f9fe270/scipy-1.17.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f2a4942b0f5f7c23c7cd641a0ca1955e2ae83dedcff537e3a0259096635e186b", size = 34841240, upload-time = "2026-01-10T21:29:39.995Z" }, - { url = "https://files.pythonhosted.org/packages/44/8c/d1f5f4b491160592e7f084d997de53a8e896a3ac01cd07e59f43ca222744/scipy-1.17.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:dbf133ced83889583156566d2bdf7a07ff89228fe0c0cb727f777de92092ec6b", size = 37394463, upload-time = "2026-01-10T21:29:48.723Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ec/42a6657f8d2d087e750e9a5dde0b481fd135657f09eaf1cf5688bb23c338/scipy-1.17.0-cp314-cp314-win_amd64.whl", hash = "sha256:3625c631a7acd7cfd929e4e31d2582cf00f42fcf06011f59281271746d77e061", size = 37053015, upload-time = "2026-01-10T21:30:51.418Z" }, - { url = "https://files.pythonhosted.org/packages/27/58/6b89a6afd132787d89a362d443a7bddd511b8f41336a1ae47f9e4f000dc4/scipy-1.17.0-cp314-cp314-win_arm64.whl", hash = "sha256:9244608d27eafe02b20558523ba57f15c689357c85bdcfe920b1828750aa26eb", size = 24951312, upload-time = "2026-01-10T21:30:56.771Z" }, - { url = "https://files.pythonhosted.org/packages/e9/01/f58916b9d9ae0112b86d7c3b10b9e685625ce6e8248df139d0fcb17f7397/scipy-1.17.0-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:2b531f57e09c946f56ad0b4a3b2abee778789097871fc541e267d2eca081cff1", size = 31706502, upload-time = "2026-01-10T21:29:56.326Z" }, - { url = "https://files.pythonhosted.org/packages/59/8e/2912a87f94a7d1f8b38aabc0faf74b82d3b6c9e22be991c49979f0eceed8/scipy-1.17.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:13e861634a2c480bd237deb69333ac79ea1941b94568d4b0efa5db5e263d4fd1", size = 28380854, upload-time = "2026-01-10T21:30:01.554Z" }, - { url = "https://files.pythonhosted.org/packages/bd/1c/874137a52dddab7d5d595c1887089a2125d27d0601fce8c0026a24a92a0b/scipy-1.17.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:eb2651271135154aa24f6481cbae5cc8af1f0dd46e6533fb7b56aa9727b6a232", size = 20552752, upload-time = "2026-01-10T21:30:05.93Z" }, - { url = "https://files.pythonhosted.org/packages/3f/f0/7518d171cb735f6400f4576cf70f756d5b419a07fe1867da34e2c2c9c11b/scipy-1.17.0-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:c5e8647f60679790c2f5c76be17e2e9247dc6b98ad0d3b065861e082c56e078d", size = 22803972, upload-time = "2026-01-10T21:30:10.651Z" }, - { url = "https://files.pythonhosted.org/packages/7c/74/3498563a2c619e8a3ebb4d75457486c249b19b5b04a30600dfd9af06bea5/scipy-1.17.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fb10d17e649e1446410895639f3385fd2bf4c3c7dfc9bea937bddcbc3d7b9ba", size = 32829770, upload-time = "2026-01-10T21:30:16.359Z" }, - { url = "https://files.pythonhosted.org/packages/48/d1/7b50cedd8c6c9d6f706b4b36fa8544d829c712a75e370f763b318e9638c1/scipy-1.17.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8547e7c57f932e7354a2319fab613981cde910631979f74c9b542bb167a8b9db", size = 35051093, upload-time = "2026-01-10T21:30:22.987Z" }, - { url = "https://files.pythonhosted.org/packages/e2/82/a2d684dfddb87ba1b3ea325df7c3293496ee9accb3a19abe9429bce94755/scipy-1.17.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33af70d040e8af9d5e7a38b5ed3b772adddd281e3062ff23fec49e49681c38cf", size = 34909905, upload-time = "2026-01-10T21:30:28.704Z" }, - { url = "https://files.pythonhosted.org/packages/ef/5e/e565bd73991d42023eb82bb99e51c5b3d9e2c588ca9d4b3e2cc1d3ca62a6/scipy-1.17.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb55bb97d00f8b7ab95cb64f873eb0bf54d9446264d9f3609130381233483f", size = 37457743, upload-time = "2026-01-10T21:30:34.819Z" }, - { url = "https://files.pythonhosted.org/packages/58/a8/a66a75c3d8f1fb2b83f66007d6455a06a6f6cf5618c3dc35bc9b69dd096e/scipy-1.17.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1ff269abf702f6c7e67a4b7aad981d42871a11b9dd83c58d2d2ea624efbd1088", size = 37098574, upload-time = "2026-01-10T21:30:40.782Z" }, - { url = "https://files.pythonhosted.org/packages/56/a5/df8f46ef7da168f1bc52cd86e09a9de5c6f19cc1da04454d51b7d4f43408/scipy-1.17.0-cp314-cp314t-win_arm64.whl", hash = "sha256:031121914e295d9791319a1875444d55079885bbae5bdc9c5e0f2ee5f09d34ff", size = 25246266, upload-time = "2026-01-10T21:30:45.923Z" }, -] - -[[package]] -name = "sentence-transformers" -version = "5.2.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, - { name = "numpy" }, - { name = "scikit-learn" }, - { name = "scipy" }, - { name = "torch" }, - { name = "tqdm" }, - { name = "transformers" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a6/bc/0bc9c0ec1cf83ab2ec6e6f38667d167349b950fff6dd2086b79bd360eeca/sentence_transformers-5.2.2.tar.gz", hash = "sha256:7033ee0a24bc04c664fd490abf2ef194d387b3a58a97adcc528783ff505159fa", size = 381607, upload-time = "2026-01-27T11:11:02.658Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/21/7e925890636791386e81b52878134f114d63072e79fffe14cdcc5e7a5e6a/sentence_transformers-5.2.2-py3-none-any.whl", hash = "sha256:280ac54bffb84c110726b4d8848ba7b7c60813b9034547f8aea6e9a345cd1c23", size = 494106, upload-time = "2026-01-27T11:11:00.983Z" }, -] - -[[package]] -name = "setuptools" -version = "80.10.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/76/95/faf61eb8363f26aa7e1d762267a8d602a1b26d4f3a1e758e92cb3cb8b054/setuptools-80.10.2.tar.gz", hash = "sha256:8b0e9d10c784bf7d262c4e5ec5d4ec94127ce206e8738f29a437945fbc219b70", size = 1200343, upload-time = "2026-01-25T22:38:17.252Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/b8/f1f62a5e3c0ad2ff1d189590bfa4c46b4f3b6e49cef6f26c6ee4e575394d/setuptools-80.10.2-py3-none-any.whl", hash = "sha256:95b30ddfb717250edb492926c92b5221f7ef3fbcc2b07579bcd4a27da21d0173", size = 1064234, upload-time = "2026-01-25T22:38:15.216Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[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.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "starlette" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/00d280c03ffd39aaee0e86ec81e2d3b9253036a0f93f51d10503adef0e65/sse_starlette-3.2.0.tar.gz", hash = "sha256:8127594edfb51abe44eac9c49e59b0b01f1039d0c7461c6fd91d4e03b70da422", size = 27253, upload-time = "2026-01-17T13:11:05.62Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/7f/832f015020844a8b8f7a9cbc103dd76ba8e3875004c41e08440ea3a2b41a/sse_starlette-3.2.0-py3-none-any.whl", hash = "sha256:5876954bd51920fc2cd51baee47a080eb88a37b5b784e615abb0b283f801cdbf", size = 12763, upload-time = "2026-01-17T13:11:03.775Z" }, -] - -[[package]] -name = "starlette" -version = "0.50.0" -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/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, -] - -[[package]] -name = "structlog" -version = "25.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ef/52/9ba0f43b686e7f3ddfeaa78ac3af750292662284b3661e91ad5494f21dbc/structlog-25.5.0.tar.gz", hash = "sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98", size = 1460830, upload-time = "2025-10-27T08:28:23.028Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/45/a132b9074aa18e799b891b91ad72133c98d8042c70f6240e4c5f9dabee2f/structlog-25.5.0-py3-none-any.whl", hash = "sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f", size = 72510, upload-time = "2025-10-27T08:28:21.535Z" }, -] - -[[package]] -name = "sympy" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mpmath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, -] - -[[package]] -name = "tenacity" -version = "9.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, -] - -[[package]] -name = "threadpoolctl" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, -] - -[[package]] -name = "tiktoken" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "regex" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806, upload-time = "2025-10-06T20:22:45.419Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/85/be65d39d6b647c79800fd9d29241d081d4eeb06271f383bb87200d74cf76/tiktoken-0.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b97f74aca0d78a1ff21b8cd9e9925714c15a9236d6ceacf5c7327c117e6e21e8", size = 1050728, upload-time = "2025-10-06T20:21:52.756Z" }, - { url = "https://files.pythonhosted.org/packages/4a/42/6573e9129bc55c9bf7300b3a35bef2c6b9117018acca0dc760ac2d93dffe/tiktoken-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b90f5ad190a4bb7c3eb30c5fa32e1e182ca1ca79f05e49b448438c3e225a49b", size = 994049, upload-time = "2025-10-06T20:21:53.782Z" }, - { url = "https://files.pythonhosted.org/packages/66/c5/ed88504d2f4a5fd6856990b230b56d85a777feab84e6129af0822f5d0f70/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:65b26c7a780e2139e73acc193e5c63ac754021f160df919add909c1492c0fb37", size = 1129008, upload-time = "2025-10-06T20:21:54.832Z" }, - { url = "https://files.pythonhosted.org/packages/f4/90/3dae6cc5436137ebd38944d396b5849e167896fc2073da643a49f372dc4f/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:edde1ec917dfd21c1f2f8046b86348b0f54a2c0547f68149d8600859598769ad", size = 1152665, upload-time = "2025-10-06T20:21:56.129Z" }, - { url = "https://files.pythonhosted.org/packages/a3/fe/26df24ce53ffde419a42f5f53d755b995c9318908288c17ec3f3448313a3/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:35a2f8ddd3824608b3d650a000c1ef71f730d0c56486845705a8248da00f9fe5", size = 1194230, upload-time = "2025-10-06T20:21:57.546Z" }, - { url = "https://files.pythonhosted.org/packages/20/cc/b064cae1a0e9fac84b0d2c46b89f4e57051a5f41324e385d10225a984c24/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83d16643edb7fa2c99eff2ab7733508aae1eebb03d5dfc46f5565862810f24e3", size = 1254688, upload-time = "2025-10-06T20:21:58.619Z" }, - { url = "https://files.pythonhosted.org/packages/81/10/b8523105c590c5b8349f2587e2fdfe51a69544bd5a76295fc20f2374f470/tiktoken-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffc5288f34a8bc02e1ea7047b8d041104791d2ddbf42d1e5fa07822cbffe16bd", size = 878694, upload-time = "2025-10-06T20:21:59.876Z" }, - { url = "https://files.pythonhosted.org/packages/00/61/441588ee21e6b5cdf59d6870f86beb9789e532ee9718c251b391b70c68d6/tiktoken-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3", size = 1050802, upload-time = "2025-10-06T20:22:00.96Z" }, - { url = "https://files.pythonhosted.org/packages/1f/05/dcf94486d5c5c8d34496abe271ac76c5b785507c8eae71b3708f1ad9b45a/tiktoken-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160", size = 993995, upload-time = "2025-10-06T20:22:02.788Z" }, - { url = "https://files.pythonhosted.org/packages/a0/70/5163fe5359b943f8db9946b62f19be2305de8c3d78a16f629d4165e2f40e/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa", size = 1128948, upload-time = "2025-10-06T20:22:03.814Z" }, - { url = "https://files.pythonhosted.org/packages/0c/da/c028aa0babf77315e1cef357d4d768800c5f8a6de04d0eac0f377cb619fa/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be", size = 1151986, upload-time = "2025-10-06T20:22:05.173Z" }, - { url = "https://files.pythonhosted.org/packages/a0/5a/886b108b766aa53e295f7216b509be95eb7d60b166049ce2c58416b25f2a/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a", size = 1194222, upload-time = "2025-10-06T20:22:06.265Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f8/4db272048397636ac7a078d22773dd2795b1becee7bc4922fe6207288d57/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3", size = 1255097, upload-time = "2025-10-06T20:22:07.403Z" }, - { url = "https://files.pythonhosted.org/packages/8e/32/45d02e2e0ea2be3a9ed22afc47d93741247e75018aac967b713b2941f8ea/tiktoken-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697", size = 879117, upload-time = "2025-10-06T20:22:08.418Z" }, - { url = "https://files.pythonhosted.org/packages/ce/76/994fc868f88e016e6d05b0da5ac24582a14c47893f4474c3e9744283f1d5/tiktoken-0.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16", size = 1050309, upload-time = "2025-10-06T20:22:10.939Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b8/57ef1456504c43a849821920d582a738a461b76a047f352f18c0b26c6516/tiktoken-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a", size = 993712, upload-time = "2025-10-06T20:22:12.115Z" }, - { url = "https://files.pythonhosted.org/packages/72/90/13da56f664286ffbae9dbcfadcc625439142675845baa62715e49b87b68b/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27", size = 1128725, upload-time = "2025-10-06T20:22:13.541Z" }, - { url = "https://files.pythonhosted.org/packages/05/df/4f80030d44682235bdaecd7346c90f67ae87ec8f3df4a3442cb53834f7e4/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb", size = 1151875, upload-time = "2025-10-06T20:22:14.559Z" }, - { url = "https://files.pythonhosted.org/packages/22/1f/ae535223a8c4ef4c0c1192e3f9b82da660be9eb66b9279e95c99288e9dab/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e", size = 1194451, upload-time = "2025-10-06T20:22:15.545Z" }, - { url = "https://files.pythonhosted.org/packages/78/a7/f8ead382fce0243cb625c4f266e66c27f65ae65ee9e77f59ea1653b6d730/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25", size = 1253794, upload-time = "2025-10-06T20:22:16.624Z" }, - { url = "https://files.pythonhosted.org/packages/93/e0/6cc82a562bc6365785a3ff0af27a2a092d57c47d7a81d9e2295d8c36f011/tiktoken-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f", size = 878777, upload-time = "2025-10-06T20:22:18.036Z" }, - { url = "https://files.pythonhosted.org/packages/72/05/3abc1db5d2c9aadc4d2c76fa5640134e475e58d9fbb82b5c535dc0de9b01/tiktoken-0.12.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a90388128df3b3abeb2bfd1895b0681412a8d7dc644142519e6f0a97c2111646", size = 1050188, upload-time = "2025-10-06T20:22:19.563Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7b/50c2f060412202d6c95f32b20755c7a6273543b125c0985d6fa9465105af/tiktoken-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:da900aa0ad52247d8794e307d6446bd3cdea8e192769b56276695d34d2c9aa88", size = 993978, upload-time = "2025-10-06T20:22:20.702Z" }, - { url = "https://files.pythonhosted.org/packages/14/27/bf795595a2b897e271771cd31cb847d479073497344c637966bdf2853da1/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:285ba9d73ea0d6171e7f9407039a290ca77efcdb026be7769dccc01d2c8d7fff", size = 1129271, upload-time = "2025-10-06T20:22:22.06Z" }, - { url = "https://files.pythonhosted.org/packages/f5/de/9341a6d7a8f1b448573bbf3425fa57669ac58258a667eb48a25dfe916d70/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:d186a5c60c6a0213f04a7a802264083dea1bbde92a2d4c7069e1a56630aef830", size = 1151216, upload-time = "2025-10-06T20:22:23.085Z" }, - { url = "https://files.pythonhosted.org/packages/75/0d/881866647b8d1be4d67cb24e50d0c26f9f807f994aa1510cb9ba2fe5f612/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:604831189bd05480f2b885ecd2d1986dc7686f609de48208ebbbddeea071fc0b", size = 1194860, upload-time = "2025-10-06T20:22:24.602Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1e/b651ec3059474dab649b8d5b69f5c65cd8fcd8918568c1935bd4136c9392/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8f317e8530bb3a222547b85a58583238c8f74fd7a7408305f9f63246d1a0958b", size = 1254567, upload-time = "2025-10-06T20:22:25.671Z" }, - { url = "https://files.pythonhosted.org/packages/80/57/ce64fd16ac390fafde001268c364d559447ba09b509181b2808622420eec/tiktoken-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:399c3dd672a6406719d84442299a490420b458c44d3ae65516302a99675888f3", size = 921067, upload-time = "2025-10-06T20:22:26.753Z" }, - { url = "https://files.pythonhosted.org/packages/ac/a4/72eed53e8976a099539cdd5eb36f241987212c29629d0a52c305173e0a68/tiktoken-0.12.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2c714c72bc00a38ca969dae79e8266ddec999c7ceccd603cc4f0d04ccd76365", size = 1050473, upload-time = "2025-10-06T20:22:27.775Z" }, - { url = "https://files.pythonhosted.org/packages/e6/d7/0110b8f54c008466b19672c615f2168896b83706a6611ba6e47313dbc6e9/tiktoken-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cbb9a3ba275165a2cb0f9a83f5d7025afe6b9d0ab01a22b50f0e74fee2ad253e", size = 993855, upload-time = "2025-10-06T20:22:28.799Z" }, - { url = "https://files.pythonhosted.org/packages/5f/77/4f268c41a3957c418b084dd576ea2fad2e95da0d8e1ab705372892c2ca22/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:dfdfaa5ffff8993a3af94d1125870b1d27aed7cb97aa7eb8c1cefdbc87dbee63", size = 1129022, upload-time = "2025-10-06T20:22:29.981Z" }, - { url = "https://files.pythonhosted.org/packages/4e/2b/fc46c90fe5028bd094cd6ee25a7db321cb91d45dc87531e2bdbb26b4867a/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:584c3ad3d0c74f5269906eb8a659c8bfc6144a52895d9261cdaf90a0ae5f4de0", size = 1150736, upload-time = "2025-10-06T20:22:30.996Z" }, - { url = "https://files.pythonhosted.org/packages/28/c0/3c7a39ff68022ddfd7d93f3337ad90389a342f761c4d71de99a3ccc57857/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:54c891b416a0e36b8e2045b12b33dd66fb34a4fe7965565f1b482da50da3e86a", size = 1194908, upload-time = "2025-10-06T20:22:32.073Z" }, - { url = "https://files.pythonhosted.org/packages/ab/0d/c1ad6f4016a3968c048545f5d9b8ffebf577774b2ede3e2e352553b685fe/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5edb8743b88d5be814b1a8a8854494719080c28faaa1ccbef02e87354fe71ef0", size = 1253706, upload-time = "2025-10-06T20:22:33.385Z" }, - { url = "https://files.pythonhosted.org/packages/af/df/c7891ef9d2712ad774777271d39fdef63941ffba0a9d59b7ad1fd2765e57/tiktoken-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f61c0aea5565ac82e2ec50a05e02a6c44734e91b51c10510b084ea1b8e633a71", size = 920667, upload-time = "2025-10-06T20:22:34.444Z" }, -] - -[[package]] -name = "tokenizers" -version = "0.22.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, - { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, - { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, - { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, - { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, - { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, - { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, - { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, - { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, - { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, - { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, - { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, - { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, - { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, -] - -[[package]] -name = "toolz" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/d6/114b492226588d6ff54579d95847662fc69196bdeec318eb45393b24c192/toolz-1.1.0.tar.gz", hash = "sha256:27a5c770d068c110d9ed9323f24f1543e83b2f300a687b7891c1a6d56b697b5b", size = 52613, upload-time = "2025-10-17T04:03:21.661Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl", hash = "sha256:15ccc861ac51c53696de0a5d6d4607f99c210739caf987b5d2054f3efed429d8", size = 58093, upload-time = "2025-10-17T04:03:20.435Z" }, -] - -[[package]] -name = "torch" -version = "2.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-bindings", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "setuptools" }, - { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "typing-extensions" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/af/758e242e9102e9988969b5e621d41f36b8f258bb4a099109b7a4b4b50ea4/torch-2.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5fd4117d89ffd47e3dcc71e71a22efac24828ad781c7e46aaaf56bf7f2796acf", size = 145996088, upload-time = "2026-01-21T16:24:44.171Z" }, - { url = "https://files.pythonhosted.org/packages/23/8e/3c74db5e53bff7ed9e34c8123e6a8bfef718b2450c35eefab85bb4a7e270/torch-2.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:787124e7db3b379d4f1ed54dd12ae7c741c16a4d29b49c0226a89bea50923ffb", size = 915711952, upload-time = "2026-01-21T16:23:53.503Z" }, - { url = "https://files.pythonhosted.org/packages/6e/01/624c4324ca01f66ae4c7cd1b74eb16fb52596dce66dbe51eff95ef9e7a4c/torch-2.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2c66c61f44c5f903046cc696d088e21062644cbe541c7f1c4eaae88b2ad23547", size = 113757972, upload-time = "2026-01-21T16:24:39.516Z" }, - { url = "https://files.pythonhosted.org/packages/c9/5c/dee910b87c4d5c0fcb41b50839ae04df87c1cfc663cf1b5fca7ea565eeaa/torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:6d3707a61863d1c4d6ebba7be4ca320f42b869ee657e9b2c21c736bf17000294", size = 79498198, upload-time = "2026-01-21T16:24:34.704Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6f/f2e91e34e3fcba2e3fc8d8f74e7d6c22e74e480bbd1db7bc8900fdf3e95c/torch-2.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5c4d217b14741e40776dd7074d9006fd28b8a97ef5654db959d8635b2fe5f29b", size = 146004247, upload-time = "2026-01-21T16:24:29.335Z" }, - { url = "https://files.pythonhosted.org/packages/98/fb/5160261aeb5e1ee12ee95fe599d0541f7c976c3701d607d8fc29e623229f/torch-2.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6b71486353fce0f9714ca0c9ef1c850a2ae766b409808acd58e9678a3edb7738", size = 915716445, upload-time = "2026-01-21T16:22:45.353Z" }, - { url = "https://files.pythonhosted.org/packages/6a/16/502fb1b41e6d868e8deb5b0e3ae926bbb36dab8ceb0d1b769b266ad7b0c3/torch-2.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2ee399c644dc92ef7bc0d4f7e74b5360c37cdbe7c5ba11318dda49ffac2bc57", size = 113757050, upload-time = "2026-01-21T16:24:19.204Z" }, - { url = "https://files.pythonhosted.org/packages/1a/0b/39929b148f4824bc3ad6f9f72a29d4ad865bcf7ebfc2fa67584773e083d2/torch-2.10.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:3202429f58309b9fa96a614885eace4b7995729f44beb54d3e4a47773649d382", size = 79851305, upload-time = "2026-01-21T16:24:09.209Z" }, - { url = "https://files.pythonhosted.org/packages/d8/14/21fbce63bc452381ba5f74a2c0a959fdf5ad5803ccc0c654e752e0dbe91a/torch-2.10.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:aae1b29cd68e50a9397f5ee897b9c24742e9e306f88a807a27d617f07adb3bd8", size = 146005472, upload-time = "2026-01-21T16:22:29.022Z" }, - { url = "https://files.pythonhosted.org/packages/54/fd/b207d1c525cb570ef47f3e9f836b154685011fce11a2f444ba8a4084d042/torch-2.10.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6021db85958db2f07ec94e1bc77212721ba4920c12a18dc552d2ae36a3eb163f", size = 915612644, upload-time = "2026-01-21T16:21:47.019Z" }, - { url = "https://files.pythonhosted.org/packages/36/53/0197f868c75f1050b199fe58f9bf3bf3aecac9b4e85cc9c964383d745403/torch-2.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff43db38af76fda183156153983c9a096fc4c78d0cd1e07b14a2314c7f01c2c8", size = 113997015, upload-time = "2026-01-21T16:23:00.767Z" }, - { url = "https://files.pythonhosted.org/packages/0e/13/e76b4d9c160e89fff48bf16b449ea324bda84745d2ab30294c37c2434c0d/torch-2.10.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:cdf2a523d699b70d613243211ecaac14fe9c5df8a0b0a9c02add60fb2a413e0f", size = 79498248, upload-time = "2026-01-21T16:23:09.315Z" }, - { url = "https://files.pythonhosted.org/packages/4f/93/716b5ac0155f1be70ed81bacc21269c3ece8dba0c249b9994094110bfc51/torch-2.10.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:bf0d9ff448b0218e0433aeb198805192346c4fd659c852370d5cc245f602a06a", size = 79464992, upload-time = "2026-01-21T16:23:05.162Z" }, - { url = "https://files.pythonhosted.org/packages/69/2b/51e663ff190c9d16d4a8271203b71bc73a16aa7619b9f271a69b9d4a936b/torch-2.10.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:233aed0659a2503b831d8a67e9da66a62c996204c0bba4f4c442ccc0c68a3f60", size = 146018567, upload-time = "2026-01-21T16:22:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/5e/cd/4b95ef7f293b927c283db0b136c42be91c8ec6845c44de0238c8c23bdc80/torch-2.10.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:682497e16bdfa6efeec8cde66531bc8d1fbbbb4d8788ec6173c089ed3cc2bfe5", size = 915721646, upload-time = "2026-01-21T16:21:16.983Z" }, - { url = "https://files.pythonhosted.org/packages/56/97/078a007208f8056d88ae43198833469e61a0a355abc0b070edd2c085eb9a/torch-2.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:6528f13d2a8593a1a412ea07a99812495bec07e9224c28b2a25c0a30c7da025c", size = 113752373, upload-time = "2026-01-21T16:22:13.471Z" }, - { url = "https://files.pythonhosted.org/packages/d8/94/71994e7d0d5238393df9732fdab607e37e2b56d26a746cb59fdb415f8966/torch-2.10.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f5ab4ba32383061be0fb74bda772d470140a12c1c3b58a0cfbf3dae94d164c28", size = 79850324, upload-time = "2026-01-21T16:22:09.494Z" }, - { url = "https://files.pythonhosted.org/packages/e2/65/1a05346b418ea8ccd10360eef4b3e0ce688fba544e76edec26913a8d0ee0/torch-2.10.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:716b01a176c2a5659c98f6b01bf868244abdd896526f1c692712ab36dbaf9b63", size = 146006482, upload-time = "2026-01-21T16:22:18.42Z" }, - { url = "https://files.pythonhosted.org/packages/1d/b9/5f6f9d9e859fc3235f60578fa64f52c9c6e9b4327f0fe0defb6de5c0de31/torch-2.10.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d8f5912ba938233f86361e891789595ff35ca4b4e2ac8fe3670895e5976731d6", size = 915613050, upload-time = "2026-01-21T16:20:49.035Z" }, - { url = "https://files.pythonhosted.org/packages/66/4d/35352043ee0eaffdeff154fad67cd4a31dbed7ff8e3be1cc4549717d6d51/torch-2.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:71283a373f0ee2c89e0f0d5f446039bdabe8dbc3c9ccf35f0f784908b0acd185", size = 113995816, upload-time = "2026-01-21T16:22:05.312Z" }, -] - -[[package]] -name = "tqdm" -version = "4.67.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/27/89/4b0001b2dab8df0a5ee2787dcbe771de75ded01f18f1f8d53dedeea2882b/tqdm-4.67.2.tar.gz", hash = "sha256:649aac53964b2cb8dec76a14b405a4c0d13612cb8933aae547dd144eacc99653", size = 169514, upload-time = "2026-01-30T23:12:06.555Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/e2/31eac96de2915cf20ccaed0225035db149dfb9165a9ed28d4b252ef3f7f7/tqdm-4.67.2-py3-none-any.whl", hash = "sha256:9a12abcbbff58b6036b2167d9d3853042b9d436fe7330f06ae047867f2f8e0a7", size = 78354, upload-time = "2026-01-30T23:12:04.368Z" }, -] - -[[package]] -name = "transformers" -version = "5.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "huggingface-hub" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "regex" }, - { name = "safetensors" }, - { name = "tokenizers" }, - { name = "tqdm" }, - { name = "typer-slim" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/79/845941711811789c85fb7e2599cea425a14a07eda40f50896b9d3fda7492/transformers-5.0.0.tar.gz", hash = "sha256:5f5634efed6cf76ad068cc5834c7adbc32db78bbd6211fb70df2325a9c37dec8", size = 8424830, upload-time = "2026-01-26T10:46:46.813Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/f3/ac976fa8e305c9e49772527e09fbdc27cc6831b8a2f6b6063406626be5dd/transformers-5.0.0-py3-none-any.whl", hash = "sha256:587086f249ce64c817213cf36afdb318d087f790723e9b3d4500b97832afd52d", size = 10142091, upload-time = "2026-01-26T10:46:43.88Z" }, -] - -[[package]] -name = "triton" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850, upload-time = "2026-01-20T16:00:43.041Z" }, - { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450, upload-time = "2026-01-20T16:00:49.136Z" }, - { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296, upload-time = "2026-01-20T16:00:56.042Z" }, - { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" }, - { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" }, -] - -[[package]] -name = "typer" -version = "0.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, -] - -[[package]] -name = "typer-slim" -version = "0.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/17/d4/064570dec6358aa9049d4708e4a10407d74c99258f8b2136bb8702303f1a/typer_slim-0.21.1.tar.gz", hash = "sha256:73495dd08c2d0940d611c5a8c04e91c2a0a98600cbd4ee19192255a233b6dbfd", size = 110478, upload-time = "2026-01-06T11:21:11.176Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/0a/4aca634faf693e33004796b6cee0ae2e1dba375a800c16ab8d3eff4bb800/typer_slim-0.21.1-py3-none-any.whl", hash = "sha256:6e6c31047f171ac93cc5a973c9e617dbc5ab2bddc4d0a3135dc161b4e2020e0d", size = 47444, upload-time = "2026-01-06T11:21:12.441Z" }, -] - -[[package]] -name = "types-requests" -version = "2.32.4.20260107" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/f3/a0663907082280664d745929205a89d41dffb29e89a50f753af7d57d0a96/types_requests-2.32.4.20260107.tar.gz", hash = "sha256:018a11ac158f801bfa84857ddec1650750e393df8a004a8a9ae2a9bec6fcb24f", size = 23165, upload-time = "2026-01-07T03:20:54.091Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/12/709ea261f2bf91ef0a26a9eed20f2623227a8ed85610c1e54c5805692ecb/types_requests-2.32.4.20260107-py3-none-any.whl", hash = "sha256:b703fe72f8ce5b31ef031264fe9395cac8f46a04661a79f7ed31a80fb308730d", size = 20676, upload-time = "2026-01-07T03:20:52.929Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.40.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, -] - -[package.optional-dependencies] -standard = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "httptools" }, - { name = "python-dotenv" }, - { name = "pyyaml" }, - { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, - { name = "watchfiles" }, - { name = "websockets" }, -] - -[[package]] -name = "uvloop" -version = "0.22.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" }, - { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" }, - { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" }, - { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" }, - { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" }, - { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" }, - { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, - { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, - { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, - { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, - { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, - { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, - { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, - { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, - { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, - { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, - { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, - { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, - { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, - { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, - { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, - { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, -] - -[[package]] -name = "watchdog" -version = "6.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, - { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, - { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, - { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, - { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, -] - -[[package]] -name = "watchfiles" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" }, - { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" }, - { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" }, - { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" }, - { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" }, - { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" }, - { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" }, - { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" }, - { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" }, - { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" }, - { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, - { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, - { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, - { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, - { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, - { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, - { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, - { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, - { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, - { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, - { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, - { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, - { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, - { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, - { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, - { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, - { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, - { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, - { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, - { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, - { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, - { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, - { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, - { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, - { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, - { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, - { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, - { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, - { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, - { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, - { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, -] - -[[package]] -name = "web3" -version = "6.20.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "ckzg" }, - { name = "eth-abi" }, - { name = "eth-account" }, - { name = "eth-hash", extra = ["pycryptodome"] }, - { name = "eth-typing" }, - { name = "eth-utils" }, - { name = "hexbytes" }, - { name = "jsonschema" }, - { name = "lru-dict" }, - { name = "protobuf" }, - { name = "pyunormalize" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "requests" }, - { name = "typing-extensions" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/30/be/20798978258baa0a93c2918ca3202f762c53af5ed6429f3811a1f8417b9a/web3-6.20.4.tar.gz", hash = "sha256:7f3cdceca369be3eb959ce3905783cd021a0786f40c60b01e40ae2ba538e6e3f", size = 1487966, upload-time = "2025-02-19T20:37:55.811Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/f8/76fb6a80ae2bb2c0a7ff514e0bda56cfb5a22b87e45ae27c5844d643a4a4/web3-6.20.4-py3-none-any.whl", hash = "sha256:a6e1c428a54bf0fd398fbf006d00235898aed794476177ce73f7db177d2ad16c", size = 1611060, upload-time = "2025-02-19T20:37:51.04Z" }, -] - -[[package]] -name = "websocket-client" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576, upload-time = "2025-10-07T21:16:36.495Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" }, -] - -[[package]] -name = "websockets" -version = "13.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e2/73/9223dbc7be3dcaf2a7bbf756c351ec8da04b1fa573edaf545b95f6b0c7fd/websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878", size = 158549, upload-time = "2024-09-21T17:34:21.54Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/46/c426282f543b3c0296cf964aa5a7bb17e984f58dde23460c3d39b3148fcf/websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc", size = 157821, upload-time = "2024-09-21T17:32:56.442Z" }, - { url = "https://files.pythonhosted.org/packages/aa/85/22529867010baac258da7c45848f9415e6cf37fef00a43856627806ffd04/websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49", size = 155480, upload-time = "2024-09-21T17:32:57.698Z" }, - { url = "https://files.pythonhosted.org/packages/29/2c/bdb339bfbde0119a6e84af43ebf6275278698a2241c2719afc0d8b0bdbf2/websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd", size = 155715, upload-time = "2024-09-21T17:32:59.429Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d0/8612029ea04c5c22bf7af2fd3d63876c4eaeef9b97e86c11972a43aa0e6c/websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0", size = 165647, upload-time = "2024-09-21T17:33:00.495Z" }, - { url = "https://files.pythonhosted.org/packages/56/04/1681ed516fa19ca9083f26d3f3a302257e0911ba75009533ed60fbb7b8d1/websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6", size = 164592, upload-time = "2024-09-21T17:33:02.223Z" }, - { url = "https://files.pythonhosted.org/packages/38/6f/a96417a49c0ed132bb6087e8e39a37db851c70974f5c724a4b2a70066996/websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9", size = 165012, upload-time = "2024-09-21T17:33:03.288Z" }, - { url = "https://files.pythonhosted.org/packages/40/8b/fccf294919a1b37d190e86042e1a907b8f66cff2b61e9befdbce03783e25/websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68", size = 165311, upload-time = "2024-09-21T17:33:04.728Z" }, - { url = "https://files.pythonhosted.org/packages/c1/61/f8615cf7ce5fe538476ab6b4defff52beb7262ff8a73d5ef386322d9761d/websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14", size = 164692, upload-time = "2024-09-21T17:33:05.829Z" }, - { url = "https://files.pythonhosted.org/packages/5c/f1/a29dd6046d3a722d26f182b783a7997d25298873a14028c4760347974ea3/websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf", size = 164686, upload-time = "2024-09-21T17:33:06.823Z" }, - { url = "https://files.pythonhosted.org/packages/0f/99/ab1cdb282f7e595391226f03f9b498f52109d25a2ba03832e21614967dfa/websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c", size = 158712, upload-time = "2024-09-21T17:33:07.877Z" }, - { url = "https://files.pythonhosted.org/packages/46/93/e19160db48b5581feac8468330aa11b7292880a94a37d7030478596cc14e/websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3", size = 159145, upload-time = "2024-09-21T17:33:09.202Z" }, - { url = "https://files.pythonhosted.org/packages/51/20/2b99ca918e1cbd33c53db2cace5f0c0cd8296fc77558e1908799c712e1cd/websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6", size = 157828, upload-time = "2024-09-21T17:33:10.987Z" }, - { url = "https://files.pythonhosted.org/packages/b8/47/0932a71d3d9c0e9483174f60713c84cee58d62839a143f21a2bcdbd2d205/websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708", size = 155487, upload-time = "2024-09-21T17:33:12.153Z" }, - { url = "https://files.pythonhosted.org/packages/a9/60/f1711eb59ac7a6c5e98e5637fef5302f45b6f76a2c9d64fd83bbb341377a/websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418", size = 155721, upload-time = "2024-09-21T17:33:13.909Z" }, - { url = "https://files.pythonhosted.org/packages/6a/e6/ba9a8db7f9d9b0e5f829cf626ff32677f39824968317223605a6b419d445/websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a", size = 165609, upload-time = "2024-09-21T17:33:14.967Z" }, - { url = "https://files.pythonhosted.org/packages/c1/22/4ec80f1b9c27a0aebd84ccd857252eda8418ab9681eb571b37ca4c5e1305/websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f", size = 164556, upload-time = "2024-09-21T17:33:17.113Z" }, - { url = "https://files.pythonhosted.org/packages/27/ac/35f423cb6bb15600438db80755609d27eda36d4c0b3c9d745ea12766c45e/websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5", size = 164993, upload-time = "2024-09-21T17:33:18.168Z" }, - { url = "https://files.pythonhosted.org/packages/31/4e/98db4fd267f8be9e52e86b6ee4e9aa7c42b83452ea0ea0672f176224b977/websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135", size = 165360, upload-time = "2024-09-21T17:33:19.233Z" }, - { url = "https://files.pythonhosted.org/packages/3f/15/3f0de7cda70ffc94b7e7024544072bc5b26e2c1eb36545291abb755d8cdb/websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2", size = 164745, upload-time = "2024-09-21T17:33:20.361Z" }, - { url = "https://files.pythonhosted.org/packages/a1/6e/66b6b756aebbd680b934c8bdbb6dcb9ce45aad72cde5f8a7208dbb00dd36/websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6", size = 164732, upload-time = "2024-09-21T17:33:23.103Z" }, - { url = "https://files.pythonhosted.org/packages/35/c6/12e3aab52c11aeb289e3dbbc05929e7a9d90d7a9173958477d3ef4f8ce2d/websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d", size = 158709, upload-time = "2024-09-21T17:33:24.196Z" }, - { url = "https://files.pythonhosted.org/packages/41/d8/63d6194aae711d7263df4498200c690a9c39fb437ede10f3e157a6343e0d/websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2", size = 159144, upload-time = "2024-09-21T17:33:25.96Z" }, - { url = "https://files.pythonhosted.org/packages/56/27/96a5cd2626d11c8280656c6c71d8ab50fe006490ef9971ccd154e0c42cd2/websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f", size = 152134, upload-time = "2024-09-21T17:34:19.904Z" }, -] - -[[package]] -name = "win32-setctime" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, -] - -[[package]] -name = "yarl" -version = "1.22.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "multidict" }, - { name = "propcache" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" }, - { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" }, - { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" }, - { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" }, - { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" }, - { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" }, - { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" }, - { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" }, - { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" }, - { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" }, - { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" }, - { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" }, - { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" }, - { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" }, - { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" }, - { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" }, - { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" }, - { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" }, - { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" }, - { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" }, - { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" }, - { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" }, - { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" }, - { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" }, - { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" }, - { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" }, - { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" }, - { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" }, - { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" }, - { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" }, - { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" }, - { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" }, - { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" }, - { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" }, - { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" }, - { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" }, - { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" }, - { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" }, - { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" }, - { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" }, - { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" }, - { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" }, - { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" }, - { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" }, - { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" }, - { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" }, - { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" }, - { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" }, - { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" }, - { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" }, - { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" }, - { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" }, - { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" }, - { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" }, - { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" }, - { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" }, - { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" }, - { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" }, - { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" }, - { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" }, - { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" }, - { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" }, - { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" }, - { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" }, - { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" }, - { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" }, - { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" }, - { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" }, - { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" }, - { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" }, - { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" }, - { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, - { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, - { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, - { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, -] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] diff --git a/pkg/hanzo-agents/README.md b/pkg/hanzo-agents/README.md deleted file mode 100644 index 6d1c9ae99..000000000 --- a/pkg/hanzo-agents/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# hanzo-agents - -Multi-agent orchestration CLI for Hanzo AI. - -## Installation - -```bash -# Install via uv -uv tool install hanzo-agents - -# Or via pip -pip install hanzo-agents -``` - -## Usage - -```bash -# Run an agent -hanzo-agents run claude "Explain this code" -hanzo-agents run gemini "Review this PR" - -# List available agents -hanzo-agents list - -# Check agent status -hanzo-agents status -hanzo-agents status claude - -# Show configuration -hanzo-agents config -``` - -## Available Agents - -| Agent | Description | -|-------|-------------| -| `claude` | Anthropic Claude Code CLI | -| `codex` | OpenAI Codex CLI | -| `gemini` | Google Gemini CLI | -| `grok` | xAI Grok CLI | -| `qwen` | Alibaba Qwen CLI | -| `vibe` | Vibe coding agent | - -## Library Usage - -```python -from hanzo_agents import AgentTool -import asyncio - -tool = AgentTool() -result = asyncio.run(tool.call(None, action="run", name="claude", prompt="Hello")) -print(result) -``` - -## License - -Apache 2.0 diff --git a/pkg/hanzo-agents/hanzo_agents/__init__.py b/pkg/hanzo-agents/hanzo_agents/__init__.py deleted file mode 100644 index ef7a53478..000000000 --- a/pkg/hanzo-agents/hanzo_agents/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Hanzo Agents - Multi-agent orchestration for Hanzo AI. - -This package provides a CLI and library for running and orchestrating -multiple AI agents including Claude, Codex, Gemini, Grok, and more. - -Usage: - # CLI - hanzo-agents run claude "Explain this code" - hanzo-agents list - hanzo-agents status - - # Library - from hanzo_agents import run_agent, list_agents - result = await run_agent("claude", "Explain this code") -""" - -__version__ = "0.1.2" - -from hanzo_tools.agent import AgentTool, IChingTool, ReviewTool - -__all__ = [ - "__version__", - "AgentTool", - "IChingTool", - "ReviewTool", -] diff --git a/pkg/hanzo-agents/hanzo_agents/cli.py b/pkg/hanzo-agents/hanzo_agents/cli.py deleted file mode 100644 index b3187209a..000000000 --- a/pkg/hanzo-agents/hanzo_agents/cli.py +++ /dev/null @@ -1,135 +0,0 @@ -"""CLI for hanzo-agents.""" - -import asyncio -import sys - -import click -from rich.console import Console -from rich.table import Table - -from . import __version__ - -console = Console() - - -@click.group(invoke_without_command=True) -@click.option("--version", "-v", is_flag=True, help="Show version") -@click.pass_context -def main(ctx, version): - """Hanzo Agents - Multi-agent orchestration CLI. - - Run and orchestrate AI agents including Claude, Codex, Gemini, Grok, and more. - - Examples: - hanzo-agents run claude "Explain this code" - hanzo-agents list - hanzo-agents status claude - """ - if version: - console.print(f"hanzo-agents {__version__}") - return - - if ctx.invoked_subcommand is None: - click.echo(ctx.get_help()) - - -@main.command() -@click.argument("agent", required=False) -@click.argument("prompt", required=False) -@click.option("--timeout", "-t", default=300, help="Timeout in seconds") -def run(agent, prompt, timeout): - """Run an agent with a prompt. - - Examples: - hanzo-agents run claude "Explain this code" - hanzo-agents run gemini "Review this PR" - """ - if not agent: - console.print("[yellow]Usage: hanzo-agents run [/yellow]") - console.print("\nAvailable agents: claude, codex, gemini, grok, qwen, vibe") - return - - if not prompt: - console.print("[yellow]Please provide a prompt[/yellow]") - return - - try: - from hanzo_tools.agent import AgentTool - - tool = AgentTool() - result = asyncio.run( - tool.call(None, action="run", name=agent, prompt=prompt, timeout=timeout) - ) - console.print(result) - except ImportError: - console.print("[red]hanzo-tools-agent not installed[/red]") - console.print("Run: pip install hanzo-tools-agent") - sys.exit(1) - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - sys.exit(1) - - -@main.command("list") -def list_agents(): - """List available agents.""" - table = Table(title="Available Agents") - table.add_column("Agent", style="cyan") - table.add_column("Description", style="white") - table.add_column("Status", style="green") - - agents = [ - ("claude", "Anthropic Claude Code CLI", "โœ“"), - ("codex", "OpenAI Codex CLI", "โœ“"), - ("gemini", "Google Gemini CLI", "โœ“"), - ("grok", "xAI Grok CLI", "โœ“"), - ("qwen", "Alibaba Qwen CLI", "โœ“"), - ("vibe", "Vibe coding agent", "โœ“"), - ] - - for name, desc, status in agents: - table.add_row(name, desc, status) - - console.print(table) - - -@main.command() -@click.argument("agent", required=False) -def status(agent): - """Check agent status and availability.""" - try: - from hanzo_tools.agent import AgentTool - - tool = AgentTool() - if agent: - result = asyncio.run(tool.call(None, action="status", name=agent)) - else: - result = asyncio.run(tool.call(None, action="list")) - console.print(result) - except ImportError: - console.print("[red]hanzo-tools-agent not installed[/red]") - sys.exit(1) - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - sys.exit(1) - - -@main.command() -def config(): - """Show agent configuration.""" - try: - from hanzo_tools.agent import AgentTool - - tool = AgentTool() - result = asyncio.run(tool.call(None, action="config")) - console.print(result) - except ImportError: - console.print("[red]hanzo-tools-agent not installed[/red]") - sys.exit(1) - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-agents/pyproject.toml b/pkg/hanzo-agents/pyproject.toml deleted file mode 100644 index 5922e338f..000000000 --- a/pkg/hanzo-agents/pyproject.toml +++ /dev/null @@ -1,44 +0,0 @@ -[project] -name = "hanzo-agents" -version = "0.1.2" -description = "Multi-agent orchestration CLI for Hanzo AI" -readme = "README.md" -license = { text = "Apache-2.0" } -requires-python = ">=3.12" -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "ai", "agents", "orchestration", "cli"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.12", -] -dependencies = [ - "click>=8.0", - "rich>=13.0", - "hanzo-tools-agent>=0.2.0", -] - -[project.optional-dependencies] -dev = ["pytest", "ruff"] - -[project.scripts] -hanzo-agents = "hanzo_agents.cli:main" -agents = "hanzo_agents.cli:main" - -[project.urls] -Homepage = "https://hanzo.ai" -Repository = "https://github.com/hanzoai/python-sdk" -Documentation = "https://docs.hanzo.ai" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_agents"] - -[tool.ruff] -line-length = 100 -target-version = "py310" diff --git a/pkg/hanzo-async/README.md b/pkg/hanzo-async/README.md deleted file mode 100644 index ee4f2fa07..000000000 --- a/pkg/hanzo-async/README.md +++ /dev/null @@ -1,128 +0,0 @@ -# hanzo-async - -Unified async I/O for Hanzo AI - high-performance with uvloop fallback. - -## Features - -- **Automatic uvloop configuration** - Falls back to asyncio on Windows -- **Async file I/O** - Read, write, append with aiofiles -- **Async path operations** - exists, is_file, mkdir, etc. -- **Async subprocess execution** - run_command, run_shell -- **Consistent patterns** - Same API across all Hanzo packages - -## Installation - -```bash -pip install hanzo-async - -# With uvloop support (recommended for macOS/Linux) -pip install hanzo-async[uvloop] -``` - -## Quick Start - -```python -from hanzo_async import ( - # Loop configuration - configure_loop, - using_uvloop, - - # File operations - read_file, - write_file, - read_json, - write_json, - - # Path operations - path_exists, - is_file, - is_dir, - mkdir, - - # Process operations - run_command, - run_shell, -) - -# Check if using uvloop -if using_uvloop(): - print("Using uvloop for high-performance async") - -# Async file operations -async def example(): - # Read/write files - content = await read_file("/path/to/file.txt") - await write_file("/path/to/output.txt", content) - - # JSON handling - data = await read_json("/path/to/config.json") - data["updated"] = True - await write_json("/path/to/config.json", data) - - # Path operations - if await path_exists("/path/to/file"): - if await is_file("/path/to/file"): - await unlink("/path/to/file") - - await mkdir("/path/to/new/dir", parents=True, exist_ok=True) - - # Run commands - stdout, stderr, code = await run_command("ls", "-la") - stdout, stderr, code = await run_shell("echo $HOME && pwd") -``` - -## API Reference - -### Loop Configuration - -- `configure_loop()` - Configure uvloop (auto-called on import) -- `using_uvloop()` - Check if uvloop is active -- `get_loop()` - Get current event loop - -### File Operations - -- `read_file(path)` - Read file content -- `write_file(path, content)` - Write content to file -- `append_file(path, content)` - Append to file -- `read_json(path)` - Read and parse JSON -- `write_json(path, data)` - Write JSON to file -- `read_lines(path)` - Read file as lines -- `write_lines(path, lines)` - Write lines to file -- `read_bytes(path)` - Read file as bytes -- `write_bytes(path, content)` - Write bytes to file - -### Path Operations - -- `path_exists(path)` - Check if path exists -- `is_file(path)` - Check if path is file -- `is_dir(path)` - Check if path is directory -- `mkdir(path, parents=False, exist_ok=False)` - Create directory -- `rmdir(path)` - Remove empty directory -- `unlink(path, missing_ok=False)` - Remove file -- `stat(path)` - Get file/directory stats -- `listdir(path)` - List directory contents -- `glob(pattern, root_dir=None, recursive=False)` - Find files -- `rename(src, dst)` - Rename/move file -- `copy(src, dst)` - Copy file -- `copytree(src, dst)` - Copy directory tree -- `rmtree(path)` - Remove directory tree - -### Process Operations - -- `run_command(*args)` - Run command with args -- `run_shell(command)` - Run shell command -- `check_command(command)` - Check if command exists -- `which(command)` - Find command path -- `start_background(*args)` - Start background process - -## Why hanzo-async? - -1. **Consistent async patterns** - All Hanzo packages use the same I/O patterns -2. **High performance** - uvloop provides 2-4x faster async on Unix -3. **Automatic fallback** - Works on Windows with standard asyncio -4. **No event loop blocking** - All operations are truly async -5. **Easy to use** - Simple function-based API - -## License - -MIT diff --git a/pkg/hanzo-async/hanzo_async/__init__.py b/pkg/hanzo-async/hanzo_async/__init__.py deleted file mode 100644 index 4493aa6b3..000000000 --- a/pkg/hanzo-async/hanzo_async/__init__.py +++ /dev/null @@ -1,162 +0,0 @@ -"""Hanzo Async - Unified async I/O for Hanzo AI. - -High-performance async operations with uvloop backend and asyncio fallback. - -Features: -- Automatic uvloop configuration (falls back to asyncio on Windows) -- Async file I/O (read, write, append, exists, mkdir) -- Async path operations (exists, is_file, is_dir, stat) -- Async subprocess execution -- Consistent patterns across all Hanzo packages - -Usage: - from hanzo_async import read_file, write_file, path_exists, run_command - from hanzo_async import configure_loop, using_uvloop - - # Check if using uvloop - if using_uvloop(): - print("Using uvloop for high-performance async") - - # Async file operations - content = await read_file("/path/to/file") - await write_file("/path/to/file", "content") - - # Async path operations - if await path_exists("/path/to/file"): - ... - - # Async subprocess - stdout, stderr, code = await run_command("ls", "-la") -""" - -import sys -import asyncio -from functools import lru_cache # noqa: TID251 - -# Track uvloop configuration state -_uvloop_configured = False -_using_uvloop = False - - -def configure_loop() -> bool: - """Configure the event loop for high performance. - - Uses uvloop on macOS/Linux, falls back to asyncio on Windows. - Safe to call multiple times - only configures once. - - Returns: - True if uvloop is active, False if using asyncio fallback - """ - global _uvloop_configured, _using_uvloop - - if _uvloop_configured: - return _using_uvloop - - _uvloop_configured = True - - # Windows doesn't support uvloop - if sys.platform == "win32": - _using_uvloop = False - return False - - try: - import uvloop - - asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) - _using_uvloop = True - return True - except ImportError: - _using_uvloop = False - return False - - -def using_uvloop() -> bool: - """Check if uvloop is active. - - Returns: - True if using uvloop, False if using asyncio - """ - if not _uvloop_configured: - configure_loop() - return _using_uvloop - - -def get_loop() -> asyncio.AbstractEventLoop: - """Get the current event loop, creating if necessary. - - Handles Python 3.10+ deprecation of get_event_loop() in non-async contexts. - - Returns: - The current event loop - """ - try: - return asyncio.get_running_loop() - except RuntimeError: - # Not in async context, create new loop - if not _uvloop_configured: - configure_loop() - return asyncio.new_event_loop() - - -# Auto-configure on import -configure_loop() - -# Export file operations -from hanzo_async.files import ( - read_file, - read_json, - read_lines, - write_file, - write_json, - append_file, - write_lines, -) - -# Export path operations -from hanzo_async.paths import ( - glob, - stat, - mkdir, - rmdir, - is_dir, - unlink, - is_file, - listdir, - path_exists, -) - -# Export process operations -from hanzo_async.process import ( - run_shell, - run_command, - check_command, -) - -__all__ = [ - # Loop configuration - "configure_loop", - "using_uvloop", - "get_loop", - # File operations - "read_file", - "read_json", - "write_file", - "write_json", - "append_file", - "read_lines", - "write_lines", - # Path operations - "path_exists", - "is_file", - "is_dir", - "mkdir", - "rmdir", - "unlink", - "stat", - "listdir", - "glob", - # Process operations - "run_command", - "run_shell", - "check_command", -] diff --git a/pkg/hanzo-async/hanzo_async/files.py b/pkg/hanzo-async/hanzo_async/files.py deleted file mode 100644 index 4079930c0..000000000 --- a/pkg/hanzo-async/hanzo_async/files.py +++ /dev/null @@ -1,221 +0,0 @@ -"""Async file operations. - -Provides non-blocking file I/O using aiofiles. -All operations are fully async and safe for use in event loops. -""" - -import json -from typing import Any, List, Union, Optional -from pathlib import Path - -import aiofiles -import aiofiles.os - - -async def read_file( - path: Union[str, Path], - encoding: str = "utf-8", - errors: str = "replace", -) -> str: - """Read entire file content asynchronously. - - Args: - path: Path to file - encoding: Text encoding (default: utf-8) - errors: Error handling mode (default: replace) - - Returns: - File content as string - - Raises: - FileNotFoundError: If file doesn't exist - PermissionError: If file can't be read - """ - async with aiofiles.open(path, "r", encoding=encoding, errors=errors) as f: - return await f.read() - - -async def read_json( - path: Union[str, Path], - encoding: str = "utf-8", -) -> Any: - """Read and parse JSON file asynchronously. - - Args: - path: Path to JSON file - encoding: Text encoding (default: utf-8) - - Returns: - Parsed JSON data - - Raises: - FileNotFoundError: If file doesn't exist - json.JSONDecodeError: If JSON is invalid - """ - content = await read_file(path, encoding=encoding) - return json.loads(content) - - -async def write_file( - path: Union[str, Path], - content: str, - encoding: str = "utf-8", - mkdir_parents: bool = True, -) -> None: - """Write content to file asynchronously. - - Args: - path: Path to file - content: Content to write - encoding: Text encoding (default: utf-8) - mkdir_parents: Create parent directories if needed (default: True) - - Raises: - PermissionError: If file can't be written - """ - path = Path(path) - if mkdir_parents: - from hanzo_async.paths import mkdir - - await mkdir(path.parent, parents=True, exist_ok=True) - - async with aiofiles.open(path, "w", encoding=encoding) as f: - await f.write(content) - - -async def write_json( - path: Union[str, Path], - data: Any, - encoding: str = "utf-8", - indent: int = 2, - mkdir_parents: bool = True, -) -> None: - """Write data as JSON file asynchronously. - - Args: - path: Path to file - data: Data to serialize as JSON - encoding: Text encoding (default: utf-8) - indent: JSON indentation (default: 2) - mkdir_parents: Create parent directories if needed (default: True) - - Raises: - TypeError: If data is not JSON serializable - PermissionError: If file can't be written - """ - content = json.dumps(data, indent=indent, ensure_ascii=False) - await write_file(path, content, encoding=encoding, mkdir_parents=mkdir_parents) - - -async def append_file( - path: Union[str, Path], - content: str, - encoding: str = "utf-8", - mkdir_parents: bool = True, -) -> None: - """Append content to file asynchronously. - - Args: - path: Path to file - content: Content to append - encoding: Text encoding (default: utf-8) - mkdir_parents: Create parent directories if needed (default: True) - - Raises: - PermissionError: If file can't be written - """ - path = Path(path) - if mkdir_parents: - from hanzo_async.paths import mkdir - - await mkdir(path.parent, parents=True, exist_ok=True) - - async with aiofiles.open(path, "a", encoding=encoding) as f: - await f.write(content) - - -async def read_lines( - path: Union[str, Path], - encoding: str = "utf-8", - strip: bool = True, -) -> List[str]: - """Read file lines asynchronously. - - Args: - path: Path to file - encoding: Text encoding (default: utf-8) - strip: Strip whitespace from lines (default: True) - - Returns: - List of lines - - Raises: - FileNotFoundError: If file doesn't exist - """ - content = await read_file(path, encoding=encoding) - lines = content.splitlines() - if strip: - lines = [line.strip() for line in lines] - return lines - - -async def write_lines( - path: Union[str, Path], - lines: List[str], - encoding: str = "utf-8", - mkdir_parents: bool = True, -) -> None: - """Write lines to file asynchronously. - - Args: - path: Path to file - lines: Lines to write - encoding: Text encoding (default: utf-8) - mkdir_parents: Create parent directories if needed (default: True) - - Raises: - PermissionError: If file can't be written - """ - content = "\n".join(lines) + "\n" - await write_file(path, content, encoding=encoding, mkdir_parents=mkdir_parents) - - -async def read_bytes(path: Union[str, Path]) -> bytes: - """Read file as bytes asynchronously. - - Args: - path: Path to file - - Returns: - File content as bytes - - Raises: - FileNotFoundError: If file doesn't exist - """ - async with aiofiles.open(path, "rb") as f: - return await f.read() - - -async def write_bytes( - path: Union[str, Path], - content: bytes, - mkdir_parents: bool = True, -) -> None: - """Write bytes to file asynchronously. - - Args: - path: Path to file - content: Bytes to write - mkdir_parents: Create parent directories if needed (default: True) - - Raises: - PermissionError: If file can't be written - """ - path = Path(path) - if mkdir_parents: - from hanzo_async.paths import mkdir - - await mkdir(path.parent, parents=True, exist_ok=True) - - async with aiofiles.open(path, "wb") as f: - await f.write(content) diff --git a/pkg/hanzo-async/hanzo_async/paths.py b/pkg/hanzo-async/hanzo_async/paths.py deleted file mode 100644 index ad36c5b38..000000000 --- a/pkg/hanzo-async/hanzo_async/paths.py +++ /dev/null @@ -1,255 +0,0 @@ -"""Async path operations. - -Provides non-blocking path operations using run_in_executor. -All operations are fully async and safe for use in event loops. - -Note: Uses ThreadPoolExecutor for filesystem metadata operations (stat, exists, -glob, etc.) rather than aiofiles, as these are fast syscalls that don't benefit -significantly from true async I/O. The executor pattern prevents event loop -blocking while keeping the implementation simple and reliable. - -For file content operations (read/write), use hanzo_async.files which wraps aiofiles. -""" - -import os -import asyncio -from glob import glob as sync_glob -from typing import List, Union, Optional -from pathlib import Path - - -async def _run_in_executor(func, *args): - """Run a sync function in executor.""" - loop = asyncio.get_running_loop() - return await loop.run_in_executor(None, func, *args) - - -async def path_exists(path: Union[str, Path]) -> bool: - """Check if path exists asynchronously. - - Args: - path: Path to check - - Returns: - True if path exists - """ - path = Path(path) - return await _run_in_executor(path.exists) - - -async def is_file(path: Union[str, Path]) -> bool: - """Check if path is a file asynchronously. - - Args: - path: Path to check - - Returns: - True if path is a file - """ - path = Path(path) - return await _run_in_executor(path.is_file) - - -async def is_dir(path: Union[str, Path]) -> bool: - """Check if path is a directory asynchronously. - - Args: - path: Path to check - - Returns: - True if path is a directory - """ - path = Path(path) - return await _run_in_executor(path.is_dir) - - -async def mkdir( - path: Union[str, Path], - parents: bool = False, - exist_ok: bool = False, -) -> None: - """Create directory asynchronously. - - Args: - path: Directory path to create - parents: Create parent directories (default: False) - exist_ok: Don't error if exists (default: False) - - Raises: - FileExistsError: If directory exists and exist_ok=False - PermissionError: If permission denied - """ - path = Path(path) - - def _mkdir(): - path.mkdir(parents=parents, exist_ok=exist_ok) - - await _run_in_executor(_mkdir) - - -async def rmdir(path: Union[str, Path]) -> None: - """Remove empty directory asynchronously. - - Args: - path: Directory to remove - - Raises: - OSError: If directory not empty - FileNotFoundError: If directory doesn't exist - """ - path = Path(path) - await _run_in_executor(path.rmdir) - - -async def unlink(path: Union[str, Path], missing_ok: bool = False) -> None: - """Remove file asynchronously. - - Args: - path: File to remove - missing_ok: Don't error if file missing (default: False) - - Raises: - FileNotFoundError: If file doesn't exist and missing_ok=False - """ - path = Path(path) - - def _unlink(): - path.unlink(missing_ok=missing_ok) - - await _run_in_executor(_unlink) - - -async def stat(path: Union[str, Path]) -> os.stat_result: - """Get file/directory stats asynchronously. - - Args: - path: Path to stat - - Returns: - os.stat_result with file info - - Raises: - FileNotFoundError: If path doesn't exist - """ - path = Path(path) - return await _run_in_executor(path.stat) - - -async def listdir(path: Union[str, Path] = ".") -> List[str]: - """List directory contents asynchronously. - - Args: - path: Directory to list (default: current directory) - - Returns: - List of entry names - - Raises: - FileNotFoundError: If directory doesn't exist - NotADirectoryError: If path is not a directory - """ - return await _run_in_executor(os.listdir, str(path)) - - -async def glob( - pattern: str, - root_dir: Optional[Union[str, Path]] = None, - recursive: bool = False, -) -> List[str]: - """Find files matching pattern asynchronously. - - Args: - pattern: Glob pattern (e.g., "*.py", "**/*.txt") - root_dir: Root directory for search (default: current directory) - recursive: Enable ** pattern (default: False) - - Returns: - List of matching paths - """ - - def _glob(): - if root_dir: - old_cwd = os.getcwd() - os.chdir(str(root_dir)) - try: - return sync_glob(pattern, recursive=recursive) - finally: - os.chdir(old_cwd) - else: - return sync_glob(pattern, recursive=recursive) - - return await _run_in_executor(_glob) - - -async def rename(src: Union[str, Path], dst: Union[str, Path]) -> None: - """Rename/move file or directory asynchronously. - - Args: - src: Source path - dst: Destination path - - Raises: - FileNotFoundError: If source doesn't exist - FileExistsError: If destination exists - """ - src = Path(src) - dst = Path(dst) - await _run_in_executor(src.rename, dst) - - -async def copy(src: Union[str, Path], dst: Union[str, Path]) -> None: - """Copy file asynchronously. - - Args: - src: Source file path - dst: Destination file path - - Raises: - FileNotFoundError: If source doesn't exist - PermissionError: If permission denied - """ - import shutil - - await _run_in_executor(shutil.copy2, str(src), str(dst)) - - -async def copytree( - src: Union[str, Path], - dst: Union[str, Path], - dirs_exist_ok: bool = False, -) -> None: - """Copy directory tree asynchronously. - - Args: - src: Source directory path - dst: Destination directory path - dirs_exist_ok: Don't error if dst exists (default: False) - - Raises: - FileNotFoundError: If source doesn't exist - FileExistsError: If destination exists and dirs_exist_ok=False - """ - import shutil - - def _copytree(): - shutil.copytree(str(src), str(dst), dirs_exist_ok=dirs_exist_ok) - - await _run_in_executor(_copytree) - - -async def rmtree(path: Union[str, Path], ignore_errors: bool = False) -> None: - """Remove directory tree asynchronously. - - Args: - path: Directory to remove - ignore_errors: Ignore errors during removal (default: False) - - Raises: - FileNotFoundError: If directory doesn't exist and ignore_errors=False - """ - import shutil - - def _rmtree(): - shutil.rmtree(str(path), ignore_errors=ignore_errors) - - await _run_in_executor(_rmtree) diff --git a/pkg/hanzo-async/hanzo_async/process.py b/pkg/hanzo-async/hanzo_async/process.py deleted file mode 100644 index 8157555ca..000000000 --- a/pkg/hanzo-async/hanzo_async/process.py +++ /dev/null @@ -1,238 +0,0 @@ -"""Async process operations. - -Provides non-blocking subprocess execution. -All operations are fully async and safe for use in event loops. -""" - -import shutil -import asyncio -from typing import Dict, List, Tuple, Union, Optional - - -async def run_command( - *args: str, - cwd: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - timeout: Optional[float] = None, - capture: bool = True, -) -> Tuple[str, str, int]: - """Run command asynchronously. - - Args: - *args: Command and arguments - cwd: Working directory (default: current) - env: Environment variables to add - timeout: Timeout in seconds (default: None = no timeout) - capture: Capture stdout/stderr (default: True) - - Returns: - Tuple of (stdout, stderr, exit_code) - - Raises: - asyncio.TimeoutError: If timeout exceeded - FileNotFoundError: If command not found - - Examples: - stdout, stderr, code = await run_command("ls", "-la") - stdout, stderr, code = await run_command("git", "status", cwd="/repo") - """ - import os - - # Merge environment - full_env = None - if env: - full_env = os.environ.copy() - full_env.update(env) - - # Create subprocess - proc = await asyncio.create_subprocess_exec( - *args, - stdout=asyncio.subprocess.PIPE if capture else asyncio.subprocess.DEVNULL, - stderr=asyncio.subprocess.PIPE if capture else asyncio.subprocess.DEVNULL, - cwd=cwd, - env=full_env, - ) - - try: - if timeout: - stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=timeout) - else: - stdout, stderr = await proc.communicate() - - stdout_str = stdout.decode("utf-8", errors="replace") if stdout else "" - stderr_str = stderr.decode("utf-8", errors="replace") if stderr else "" - - return stdout_str, stderr_str, proc.returncode or 0 - - except asyncio.TimeoutError: - proc.kill() - await proc.wait() - raise - - -async def run_shell( - command: str, - shell: Optional[str] = None, - cwd: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - timeout: Optional[float] = None, - capture: bool = True, -) -> Tuple[str, str, int]: - """Run shell command asynchronously. - - Args: - command: Shell command string - shell: Shell to use (default: auto-detect zsh > bash > sh) - cwd: Working directory (default: current) - env: Environment variables to add - timeout: Timeout in seconds (default: None = no timeout) - capture: Capture stdout/stderr (default: True) - - Returns: - Tuple of (stdout, stderr, exit_code) - - Raises: - asyncio.TimeoutError: If timeout exceeded - FileNotFoundError: If shell not found - - Examples: - stdout, stderr, code = await run_shell("ls -la && pwd") - stdout, stderr, code = await run_shell("echo $PATH", shell="bash") - """ - import os - - # Auto-detect shell - if not shell: - shell = _detect_shell() - - # Merge environment - full_env = None - if env: - full_env = os.environ.copy() - full_env.update(env) - - # Create subprocess with shell - proc = await asyncio.create_subprocess_exec( - shell, - "-c", - command, - stdout=asyncio.subprocess.PIPE if capture else asyncio.subprocess.DEVNULL, - stderr=asyncio.subprocess.PIPE if capture else asyncio.subprocess.DEVNULL, - cwd=cwd, - env=full_env, - ) - - try: - if timeout: - stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=timeout) - else: - stdout, stderr = await proc.communicate() - - stdout_str = stdout.decode("utf-8", errors="replace") if stdout else "" - stderr_str = stderr.decode("utf-8", errors="replace") if stderr else "" - - return stdout_str, stderr_str, proc.returncode or 0 - - except asyncio.TimeoutError: - proc.kill() - await proc.wait() - raise - - -async def check_command(command: str) -> bool: - """Check if command is available. - - Args: - command: Command name to check - - Returns: - True if command is available - """ - loop = asyncio.get_running_loop() - result = await loop.run_in_executor(None, shutil.which, command) - return result is not None - - -def _detect_shell() -> str: - """Detect the best available shell. - - Returns: - Path to shell (zsh > bash > fish > sh) - """ - import os - - # Check environment variable override - force_shell = os.environ.get("HANZO_SHELL") - if force_shell: - return force_shell - - # Shell priority - shells = ["zsh", "bash", "fish", "sh"] - search_paths = [ - "/opt/homebrew/bin", - "/usr/local/bin", - "/bin", - "/usr/bin", - ] - - for shell in shells: - for prefix in search_paths: - full_path = f"{prefix}/{shell}" - if os.path.isfile(full_path) and os.access(full_path, os.X_OK): - return full_path - found = shutil.which(shell) - if found: - return found - - return "sh" - - -async def which(command: str) -> Optional[str]: - """Find command path asynchronously. - - Args: - command: Command name to find - - Returns: - Full path to command, or None if not found - """ - loop = asyncio.get_running_loop() - return await loop.run_in_executor(None, shutil.which, command) - - -async def start_background( - *args: str, - cwd: Optional[str] = None, - env: Optional[Dict[str, str]] = None, -) -> asyncio.subprocess.Process: - """Start a background process. - - Args: - *args: Command and arguments - cwd: Working directory (default: current) - env: Environment variables to add - - Returns: - asyncio.subprocess.Process handle - - Examples: - proc = await start_background("python", "server.py") - # Later... - proc.terminate() - await proc.wait() - """ - import os - - # Merge environment - full_env = None - if env: - full_env = os.environ.copy() - full_env.update(env) - - return await asyncio.create_subprocess_exec( - *args, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - cwd=cwd, - env=full_env, - ) diff --git a/pkg/hanzo-async/pyproject.toml b/pkg/hanzo-async/pyproject.toml deleted file mode 100644 index 8c25a0950..000000000 --- a/pkg/hanzo-async/pyproject.toml +++ /dev/null @@ -1,39 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-async" -version = "0.1.3" -description = "Unified async I/O for Hanzo AI - high-performance with uvloop fallback" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -keywords = ["hanzo", "async", "uvloop", "aiofiles", "high-performance"] -dependencies = [ - "aiofiles>=23.2.1", - "uvloop>=0.21.0; sys_platform != 'win32'", -] - -[project.optional-dependencies] -uvloop = ["uvloop>=0.21.0; sys_platform != 'win32'"] -full = [ - "uvloop>=0.21.0; sys_platform != 'win32'", -] -dev = ["pytest>=7.0.0", "pytest-asyncio>=0.21.0", "ruff>=0.14.0"] - -[project.urls] -"Homepage" = "https://github.com/hanzoai/python-sdk" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_async*"] - -[tool.setuptools.package-data] -hanzo_async = ["py.typed"] diff --git a/pkg/hanzo-async/uv.lock b/pkg/hanzo-async/uv.lock deleted file mode 100644 index b9a7feb14..000000000 --- a/pkg/hanzo-async/uv.lock +++ /dev/null @@ -1,185 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" - -[[package]] -name = "aiofiles" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/c3/534eac40372d8ee36ef40df62ec129bee4fdb5ad9706e58a29be53b2c970/aiofiles-25.1.0.tar.gz", hash = "sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2", size = 46354, upload-time = "2025-10-09T20:51:04.358Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/8a/340a1555ae33d7354dbca4faa54948d76d89a27ceef032c8c3bc661d003e/aiofiles-25.1.0-py3-none-any.whl", hash = "sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695", size = 14668, upload-time = "2025-10-09T20:51:03.174Z" }, -] - -[[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 = "hanzo-async" -version = "0.1.2" -source = { editable = "." } -dependencies = [ - { name = "aiofiles" }, -] - -[package.optional-dependencies] -dev = [ - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "ruff" }, -] -full = [ - { name = "uvloop", marker = "sys_platform != 'win32'" }, -] -uvloop = [ - { name = "uvloop", marker = "sys_platform != 'win32'" }, -] - -[package.metadata] -requires-dist = [ - { name = "aiofiles", specifier = ">=23.2.1" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0" }, - { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.21.0" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.14.0" }, - { name = "uvloop", marker = "sys_platform != 'win32' and extra == 'full'", specifier = ">=0.21.0" }, - { name = "uvloop", marker = "sys_platform != 'win32' and extra == 'uvloop'", specifier = ">=0.21.0" }, -] -provides-extras = ["uvloop", "full", "dev"] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[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 = "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 = "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 = "pytest" -version = "9.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, -] - -[[package]] -name = "pytest-asyncio" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, -] - -[[package]] -name = "ruff" -version = "0.14.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/08/52232a877978dd8f9cf2aeddce3e611b40a63287dfca29b6b8da791f5e8d/ruff-0.14.10.tar.gz", hash = "sha256:9a2e830f075d1a42cd28420d7809ace390832a490ed0966fe373ba288e77aaf4", size = 5859763, upload-time = "2025-12-18T19:28:57.98Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/01/933704d69f3f05ee16ef11406b78881733c186fe14b6a46b05cfcaf6d3b2/ruff-0.14.10-py3-none-linux_armv6l.whl", hash = "sha256:7a3ce585f2ade3e1f29ec1b92df13e3da262178df8c8bdf876f48fa0e8316c49", size = 13527080, upload-time = "2025-12-18T19:29:25.642Z" }, - { url = "https://files.pythonhosted.org/packages/df/58/a0349197a7dfa603ffb7f5b0470391efa79ddc327c1e29c4851e85b09cc5/ruff-0.14.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:674f9be9372907f7257c51f1d4fc902cb7cf014b9980152b802794317941f08f", size = 13797320, upload-time = "2025-12-18T19:29:02.571Z" }, - { url = "https://files.pythonhosted.org/packages/7b/82/36be59f00a6082e38c23536df4e71cdbc6af8d7c707eade97fcad5c98235/ruff-0.14.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d85713d522348837ef9df8efca33ccb8bd6fcfc86a2cde3ccb4bc9d28a18003d", size = 12918434, upload-time = "2025-12-18T19:28:51.202Z" }, - { url = "https://files.pythonhosted.org/packages/a6/00/45c62a7f7e34da92a25804f813ebe05c88aa9e0c25e5cb5a7d23dd7450e3/ruff-0.14.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6987ebe0501ae4f4308d7d24e2d0fe3d7a98430f5adfd0f1fead050a740a3a77", size = 13371961, upload-time = "2025-12-18T19:29:04.991Z" }, - { url = "https://files.pythonhosted.org/packages/40/31/a5906d60f0405f7e57045a70f2d57084a93ca7425f22e1d66904769d1628/ruff-0.14.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16a01dfb7b9e4eee556fbfd5392806b1b8550c9b4a9f6acd3dbe6812b193c70a", size = 13275629, upload-time = "2025-12-18T19:29:21.381Z" }, - { url = "https://files.pythonhosted.org/packages/3e/60/61c0087df21894cf9d928dc04bcd4fb10e8b2e8dca7b1a276ba2155b2002/ruff-0.14.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7165d31a925b7a294465fa81be8c12a0e9b60fb02bf177e79067c867e71f8b1f", size = 14029234, upload-time = "2025-12-18T19:29:00.132Z" }, - { url = "https://files.pythonhosted.org/packages/44/84/77d911bee3b92348b6e5dab5a0c898d87084ea03ac5dc708f46d88407def/ruff-0.14.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c561695675b972effb0c0a45db233f2c816ff3da8dcfbe7dfc7eed625f218935", size = 15449890, upload-time = "2025-12-18T19:28:53.573Z" }, - { url = "https://files.pythonhosted.org/packages/e9/36/480206eaefa24a7ec321582dda580443a8f0671fdbf6b1c80e9c3e93a16a/ruff-0.14.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bb98fcbbc61725968893682fd4df8966a34611239c9fd07a1f6a07e7103d08e", size = 15123172, upload-time = "2025-12-18T19:29:23.453Z" }, - { url = "https://files.pythonhosted.org/packages/5c/38/68e414156015ba80cef5473d57919d27dfb62ec804b96180bafdeaf0e090/ruff-0.14.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f24b47993a9d8cb858429e97bdf8544c78029f09b520af615c1d261bf827001d", size = 14460260, upload-time = "2025-12-18T19:29:27.808Z" }, - { url = "https://files.pythonhosted.org/packages/b3/19/9e050c0dca8aba824d67cc0db69fb459c28d8cd3f6855b1405b3f29cc91d/ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59aabd2e2c4fd614d2862e7939c34a532c04f1084476d6833dddef4afab87e9f", size = 14229978, upload-time = "2025-12-18T19:29:11.32Z" }, - { url = "https://files.pythonhosted.org/packages/51/eb/e8dd1dd6e05b9e695aa9dd420f4577debdd0f87a5ff2fedda33c09e9be8c/ruff-0.14.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:213db2b2e44be8625002dbea33bb9c60c66ea2c07c084a00d55732689d697a7f", size = 14338036, upload-time = "2025-12-18T19:29:09.184Z" }, - { url = "https://files.pythonhosted.org/packages/6a/12/f3e3a505db7c19303b70af370d137795fcfec136d670d5de5391e295c134/ruff-0.14.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b914c40ab64865a17a9a5b67911d14df72346a634527240039eb3bd650e5979d", size = 13264051, upload-time = "2025-12-18T19:29:13.431Z" }, - { url = "https://files.pythonhosted.org/packages/08/64/8c3a47eaccfef8ac20e0484e68e0772013eb85802f8a9f7603ca751eb166/ruff-0.14.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1484983559f026788e3a5c07c81ef7d1e97c1c78ed03041a18f75df104c45405", size = 13283998, upload-time = "2025-12-18T19:29:06.994Z" }, - { url = "https://files.pythonhosted.org/packages/12/84/534a5506f4074e5cc0529e5cd96cfc01bb480e460c7edf5af70d2bcae55e/ruff-0.14.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c70427132db492d25f982fffc8d6c7535cc2fd2c83fc8888f05caaa248521e60", size = 13601891, upload-time = "2025-12-18T19:28:55.811Z" }, - { url = "https://files.pythonhosted.org/packages/0d/1e/14c916087d8598917dbad9b2921d340f7884824ad6e9c55de948a93b106d/ruff-0.14.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5bcf45b681e9f1ee6445d317ce1fa9d6cba9a6049542d1c3d5b5958986be8830", size = 14336660, upload-time = "2025-12-18T19:29:16.531Z" }, - { url = "https://files.pythonhosted.org/packages/f2/1c/d7b67ab43f30013b47c12b42d1acd354c195351a3f7a1d67f59e54227ede/ruff-0.14.10-py3-none-win32.whl", hash = "sha256:104c49fc7ab73f3f3a758039adea978869a918f31b73280db175b43a2d9b51d6", size = 13196187, upload-time = "2025-12-18T19:29:19.006Z" }, - { url = "https://files.pythonhosted.org/packages/fb/9c/896c862e13886fae2af961bef3e6312db9ebc6adc2b156fe95e615dee8c1/ruff-0.14.10-py3-none-win_amd64.whl", hash = "sha256:466297bd73638c6bdf06485683e812db1c00c7ac96d4ddd0294a338c62fdc154", size = 14661283, upload-time = "2025-12-18T19:29:30.16Z" }, - { url = "https://files.pythonhosted.org/packages/74/31/b0e29d572670dca3674eeee78e418f20bdf97fa8aa9ea71380885e175ca0/ruff-0.14.10-py3-none-win_arm64.whl", hash = "sha256:e51d046cf6dda98a4633b8a8a771451107413b0f07183b2bef03f075599e44e6", size = 13729839, upload-time = "2025-12-18T19:28:48.636Z" }, -] - -[[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 = "uvloop" -version = "0.22.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" }, - { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" }, - { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" }, - { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" }, - { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" }, - { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" }, - { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, - { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, - { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, - { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, - { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, - { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, - { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, - { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, - { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, - { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, - { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, - { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, - { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, - { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, - { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, - { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, -] diff --git a/pkg/hanzo-cli/README.md b/pkg/hanzo-cli/README.md deleted file mode 100644 index da5ac8e0f..000000000 --- a/pkg/hanzo-cli/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# hanzo-cli - -Unified CLI for the Hanzo platform โ€” IAM, KMS, and PaaS management. - -## Install - -```bash -pip install hanzo-cli -``` - -## Usage - -```bash -hanzo login -hanzo whoami -hanzo iam users -hanzo kms list -hanzo paas deploy list -``` diff --git a/pkg/hanzo-cli/hanzo_cli/__init__.py b/pkg/hanzo-cli/hanzo_cli/__init__.py deleted file mode 100644 index ccd4c202b..000000000 --- a/pkg/hanzo-cli/hanzo_cli/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Hanzo CLI โ€” unified command-line interface for the Hanzo platform.""" - -__version__ = "0.1.0" diff --git a/pkg/hanzo-cli/hanzo_cli/auth.py b/pkg/hanzo-cli/hanzo_cli/auth.py deleted file mode 100644 index 90a2508fa..000000000 --- a/pkg/hanzo-cli/hanzo_cli/auth.py +++ /dev/null @@ -1,363 +0,0 @@ -"""Authentication and token management for Hanzo CLI. - -Credential chain: -1. IAM_CLIENT_ID + IAM_CLIENT_SECRET env vars -2. ~/.hanzo/auth/token.json from `hanzo login` -3. Exit with help message -""" - -from __future__ import annotations - -import http.server -import json -import os -import secrets -import sys -import time -import webbrowser -from pathlib import Path -from typing import Any -from urllib.parse import parse_qs, urlparse - -import click -from hanzo_iam import IAMClient, IAMConfig - -TOKEN_DIR = Path.home() / ".hanzo" / "auth" -TOKEN_FILE = TOKEN_DIR / "token.json" - -DEFAULT_IAM_URL = "https://hanzo.id" -DEFAULT_ORG = "hanzo" -DEFAULT_APP = "app-hanzo" -DEFAULT_CLIENT_ID = "hanzo-app-client-id" -CALLBACK_PORT = 8399 -CALLBACK_PATH = "/callback" - - -def _save_token(data: dict[str, Any]) -> None: - """Save token data to disk.""" - TOKEN_DIR.mkdir(parents=True, exist_ok=True) - TOKEN_FILE.write_text(json.dumps(data, indent=2)) - TOKEN_FILE.chmod(0o600) - - -def _load_token() -> dict[str, Any] | None: - """Load stored token from disk.""" - if not TOKEN_FILE.exists(): - return None - try: - return json.loads(TOKEN_FILE.read_text()) - except (json.JSONDecodeError, OSError): - return None - - -def _clear_token() -> None: - """Remove stored token.""" - if TOKEN_FILE.exists(): - TOKEN_FILE.unlink() - - -def _env(name: str) -> str: - """Read env var (IAM_*).""" - return os.getenv(name) or "" - - -def _iam_url() -> str: - return _env("IAM_URL") or DEFAULT_IAM_URL - - -def _iam_org() -> str: - return _env("IAM_ORG") or DEFAULT_ORG - - -def _iam_app() -> str: - return _env("IAM_APP") or DEFAULT_APP - - -def _iam_client_id() -> str: - return _env("IAM_CLIENT_ID") or DEFAULT_CLIENT_ID - - -def get_client(ctx: click.Context | None = None) -> IAMClient: - """Build an IAMClient using the credential chain. - - 1. Env vars IAM_CLIENT_ID + IAM_CLIENT_SECRET - 2. Stored bearer token from `hanzo login` - 3. Exit with instructions - """ - client_id = _env("IAM_CLIENT_ID") - client_secret = _env("IAM_CLIENT_SECRET") - - if client_id and client_secret: - config = IAMConfig( - server_url=_iam_url(), - client_id=client_id, - client_secret=client_secret, - organization=_iam_org(), - application=_iam_app(), - ) - return IAMClient(config=config) - - # Try stored token - token_data = _load_token() - if token_data and token_data.get("access_token"): - config = IAMConfig( - server_url=token_data.get("server_url", _iam_url()), - client_id=token_data.get("client_id", ""), - client_secret="", - organization=token_data.get("organization", _iam_org()), - application=token_data.get("application", _iam_app()), - ) - return IAMClient( - config=config, - bearer_token=token_data["access_token"], - ) - - click.echo( - "Not authenticated. Run 'hanzo login' or set IAM_CLIENT_ID" - " + IAM_CLIENT_SECRET environment variables.", - err=True, - ) - sys.exit(1) - - -def get_token_info() -> dict[str, Any] | None: - """Return stored token info for whoami.""" - return _load_token() - - -# ========================================================================= -# Browser OAuth Login Flow -# ========================================================================= - - -class _OAuthCallbackHandler(http.server.BaseHTTPRequestHandler): - """HTTP handler that captures the OAuth callback code.""" - - code: str | None = None - state: str | None = None - error: str | None = None - - def do_GET(self) -> None: # noqa: N802 - parsed = urlparse(self.path) - if parsed.path != CALLBACK_PATH: - self.send_response(404) - self.end_headers() - return - - qs = parse_qs(parsed.query) - - if "error" in qs: - _OAuthCallbackHandler.error = qs["error"][0] - self._respond("Login failed. You can close this window.", success=False) - return - - _OAuthCallbackHandler.code = qs.get("code", [None])[0] - _OAuthCallbackHandler.state = qs.get("state", [None])[0] - self._respond("Login successful! You can close this window.") - - def _respond(self, message: str, success: bool = True) -> None: - self.send_response(200) - self.send_header("Content-Type", "text/html") - self.end_headers() - accent = "#6C5CE7" if success else "#e74c3c" - body = f""" - - - - -Hanzo - - - -
- -
{"✅" if success else "❌"}
-

{message}

-

You can close this window and return to your terminal.

-

Build something you love.

-
- -""" - self.wfile.write(body.encode()) - - def log_message(self, format: str, *args: Any) -> None: - """Suppress default request logging.""" - - -def browser_login(port: int = CALLBACK_PORT) -> dict[str, Any]: - """Run the browser OAuth login flow. - - Opens the user's browser to the IAM login page, starts a local HTTP - server to receive the callback, exchanges the code for tokens. - - Returns: - Token data dict with access_token, etc. - """ - server_url = _iam_url() - org = _iam_org() - app = _iam_app() - client_id = _iam_client_id() - - config = IAMConfig( - server_url=server_url, - client_id=client_id, - client_secret="", - organization=org, - application=app, - ) - client = IAMClient(config=config) - - redirect_uri = f"http://localhost:{port}{CALLBACK_PATH}" - state = secrets.token_urlsafe(32) - - auth_url = client.get_authorization_url( - redirect_uri=redirect_uri, - state=state, - scope="openid profile email", - ) - - # Reset handler state - _OAuthCallbackHandler.code = None - _OAuthCallbackHandler.state = None - _OAuthCallbackHandler.error = None - - server = http.server.HTTPServer(("127.0.0.1", port), _OAuthCallbackHandler) - server.timeout = 120 # 2 minute timeout - - click.echo(f"Opening browser to login at {server_url}...") - webbrowser.open(auth_url) - click.echo(f"Waiting for callback on http://localhost:{port}{CALLBACK_PATH}") - - # Handle one request (the callback) - while _OAuthCallbackHandler.code is None and _OAuthCallbackHandler.error is None: - server.handle_request() - - server.server_close() - - if _OAuthCallbackHandler.error: - raise click.ClickException(f"Login failed: {_OAuthCallbackHandler.error}") - - if _OAuthCallbackHandler.state != state: - raise click.ClickException("State mismatch โ€” possible CSRF attack.") - - code = _OAuthCallbackHandler.code - if not code: - raise click.ClickException("No authorization code received.") - - # Exchange code for tokens - tokens = client.exchange_code(code=code, redirect_uri=redirect_uri) - client.close() - - token_data = { - "access_token": tokens.access_token, - "refresh_token": tokens.refresh_token, - "id_token": tokens.id_token, - "token_type": tokens.token_type, - "expires_in": tokens.expires_in, - "scope": tokens.scope, - "server_url": server_url, - "client_id": client_id, - "organization": org, - "application": app, - "login_time": int(time.time()), - } - - _save_token(token_data) - return token_data - - -# ========================================================================= -# Password Login Flow (--no-browser) -# ========================================================================= - - -def password_login( - username: str | None = None, password: str | None = None -) -> dict[str, Any]: - """Login with username/password (no browser). - - Uses the OAuth2 Resource Owner Password Credentials (ROPC) grant - to get a JWT directly from the token endpoint. - - Returns: - Token data dict with access_token, etc. - """ - import httpx - - server_url = _iam_url() - org = _iam_org() - app = _iam_app() - client_id = _iam_client_id() - client_secret = _env("IAM_CLIENT_SECRET") - - if not username: - username = click.prompt("Username or email") - if not password: - password = click.prompt("Password", hide_input=True) - - # Use ROPC grant to get tokens directly - resp = httpx.post( - f"{server_url}/oauth/token", - data={ - "grant_type": "password", - "client_id": client_id, - "client_secret": client_secret, - "username": username, - "password": password, - "scope": "openid profile email", - }, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - timeout=30.0, - ) - - data = resp.json() - - if "error" in data: - raise click.ClickException(data.get("error_description", data["error"])) - - access_token = data.get("access_token", "") - if not access_token: - raise click.ClickException("No access token in response") - - token_data = { - "access_token": access_token, - "refresh_token": data.get("refresh_token", ""), - "id_token": data.get("id_token", ""), - "token_type": data.get("token_type", "Bearer"), - "expires_in": data.get("expires_in", 0), - "scope": data.get("scope", ""), - "server_url": server_url, - "client_id": client_id, - "organization": org, - "application": app, - "login_time": int(time.time()), - } - - _save_token(token_data) - return token_data - - -def logout() -> None: - """Clear stored credentials.""" - _clear_token() diff --git a/pkg/hanzo-cli/hanzo_cli/bot/__init__.py b/pkg/hanzo-cli/hanzo_cli/bot/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-cli/hanzo_cli/bot/commands.py b/pkg/hanzo-cli/hanzo_cli/bot/commands.py deleted file mode 100644 index 3d6206db1..000000000 --- a/pkg/hanzo-cli/hanzo_cli/bot/commands.py +++ /dev/null @@ -1,1026 +0,0 @@ -"""Hanzo CLI โ€” Bot gateway management. - -Manage the Hanzo bot-gateway deployment via PaaS API, -and run local bot agent nodes that connect to gw.hanzo.bot. - -Usage: - hanzo bot status Show container + pod status - hanzo bot logs [--tail N] View recent logs - hanzo bot deploy Trigger redeploy - hanzo bot env [KEY=VAL ...] Show/set env vars - hanzo bot events Show container events - hanzo bot install Install @hanzo/bot locally - hanzo bot run [--daemon] Run local bot node agent - hanzo bot stop Stop local bot node daemon -""" - -from __future__ import annotations - -import http.server -import json -import os -import shutil -import signal -import subprocess -from pathlib import Path -from typing import Any - -import click -from rich.console import Console -from rich.table import Table - -from hanzo_cli.paas.client import PaaSClient -from hanzo_cli.paas.context import resolve - -console = Console() - -# โ”€โ”€ local node constants โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -HANZO_BIN = Path.home() / ".hanzo" / "bin" -BOT_PID_FILE = Path.home() / ".hanzo" / "bot" / "node.pid" -BOT_LOG_FILE = Path.home() / ".hanzo" / "bot" / "node.log" -DEFAULT_GATEWAY_HOST = "gw.hanzo.bot" -DEFAULT_GATEWAY_PORT = 443 -NPM_PACKAGE = "@hanzo/bot" -BOT_IAM_CLIENT_ID = "hanzobot-client-id" -BOT_IAM_SERVER_URL = "https://hanzo.id" -BOT_IAM_ORG = "hanzo" -BOT_IAM_APP = "app-hanzobot" -BOT_CALLBACK_PORT = 8398 -BOT_CALLBACK_PATH = "/callback" -BOT_TOKEN_FILE = Path.home() / ".hanzo" / "bot" / "token.json" - -# โ”€โ”€ defaults โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -# Known Hanzo production bot container context. -# These can be overridden via --org / --project / --env flags. -DEFAULT_BOT_ORG = "698cda6739f65183b3009313" -DEFAULT_BOT_PROJECT = "698cda6739f65183b3009318" -DEFAULT_BOT_ENV = "698cda6739f65183b300931c" -DEFAULT_BOT_NAME = "bot" - - -# โ”€โ”€ shared options โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def _org_option(fn): - return click.option( - "--org", default=None, help="Organization ID (default: Hanzo)." - )(fn) - - -def _project_option(fn): - return click.option( - "--project", default=None, help="Project ID (default: Platform)." - )(fn) - - -def _env_option(fn): - return click.option( - "--env", "env_id", default=None, help="Environment ID (default: production)." - )(fn) - - -def _name_option(fn): - return click.option( - "--name", - "-n", - default=DEFAULT_BOT_NAME, - help="Container name (default: bot).", - )(fn) - - -def _resolve_bot( - org: str | None, - project: str | None, - env_id: str | None, -) -> tuple[str, str, str]: - """Resolve org/project/env, falling back to bot defaults.""" - ctx_org, ctx_proj, ctx_env = resolve(org, project, env_id) - return ( - ctx_org or DEFAULT_BOT_ORG, - ctx_proj or DEFAULT_BOT_PROJECT, - ctx_env or DEFAULT_BOT_ENV, - ) - - -def _find_container( - client: PaaSClient, - org_id: str, - project_id: str, - env_id: str, - name: str, -) -> dict[str, Any]: - """Look up a container by name.""" - containers = client.list_containers(org_id, project_id, env_id) - items = ( - containers - if isinstance(containers, list) - else containers.get("data", containers) - ) - for c in items: - if c.get("iid") == name or c.get("name") == name or c.get("slug") == name: - return c - console.print(f"[red]Bot container '{name}' not found.[/red]") - raise SystemExit(1) - - -# โ”€โ”€ group โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@click.group() -def bot() -> None: - """Manage the Hanzo bot-gateway deployment.""" - - -# โ”€โ”€ status โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@bot.command("status") -@_name_option -@_org_option -@_project_option -@_env_option -def bot_status( - name: str, - org: str | None, - project: str | None, - env_id: str | None, -) -> None: - """Show bot container status and pods.""" - org_id, project_id, env_id = _resolve_bot(org, project, env_id) - - client = PaaSClient.from_auth() - try: - container = _find_container(client, org_id, project_id, env_id, name) - cid = str(container.get("_id", container.get("id"))) - - # Container info - table = Table(title=f"Bot: {name}") - table.add_column("Field", style="cyan") - table.add_column("Value", style="white") - table.add_row("ID", cid) - table.add_row("Type", container.get("type", "โ€”")) - - pipeline = container.get("pipelineStatus", "โ€”") - if isinstance(pipeline, dict): - pipeline = pipeline.get("status", str(pipeline)) - table.add_row("Pipeline", str(pipeline)) - - reg = container.get("registry", {}) - if isinstance(reg, dict) and reg.get("imageUrl"): - table.add_row("Image", reg["imageUrl"]) - - status = container.get("status", {}) - if isinstance(status, dict): - table.add_row("Desired", str(status.get("desiredReplicas", "โ€”"))) - table.add_row("Ready", str(status.get("readyReplicas", "โ€”"))) - table.add_row("Available", str(status.get("availableReplicas", "โ€”"))) - console.print(table) - - # Pods - try: - pods_data = client.get_container_pods(org_id, project_id, env_id, cid) - pods = ( - pods_data if isinstance(pods_data, list) else pods_data.get("data", []) - ) - if pods: - pod_table = Table(title="Pods") - pod_table.add_column("Name", style="cyan") - pod_table.add_column("Status", style="green") - pod_table.add_column("Restarts", style="yellow", justify="right") - pod_table.add_column("Age", style="dim") - for p in pods: - pod_table.add_row( - p.get("name", "โ€”"), - p.get("status", "โ€”"), - str(p.get("restartCount", 0)), - p.get("age", "โ€”"), - ) - console.print(pod_table) - else: - console.print("[yellow]No pods found.[/yellow]") - except Exception: - pass - finally: - client.close() - - -# โ”€โ”€ logs โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@bot.command("logs") -@click.option("--tail", "-t", type=int, default=100, help="Number of lines to show.") -@_name_option -@_org_option -@_project_option -@_env_option -def bot_logs( - tail: int, - name: str, - org: str | None, - project: str | None, - env_id: str | None, -) -> None: - """Show bot container logs.""" - org_id, project_id, env_id = _resolve_bot(org, project, env_id) - - client = PaaSClient.from_auth() - try: - container = _find_container(client, org_id, project_id, env_id, name) - cid = str(container.get("_id", container.get("id"))) - logs_data = client.get_container_logs(org_id, project_id, env_id, cid) - - if isinstance(logs_data, dict) and "logs" in logs_data: - pod_logs = logs_data["logs"] - if isinstance(pod_logs, list): - for pod in pod_logs: - if isinstance(pod, dict): - pod_name = pod.get("podName", "?") - lines = pod.get("logs", []) - if len(pod_logs) > 1: - click.echo(click.style(f"--- {pod_name} ---", fg="cyan")) - shown = lines[-tail:] if tail else lines - for line in shown: - if line: - click.echo(line) - else: - click.echo(str(pod)) - else: - click.echo(str(pod_logs)) - elif isinstance(logs_data, str): - lines = logs_data.splitlines() - for line in lines[-tail:]: - click.echo(line) - elif isinstance(logs_data, list): - for line in logs_data[-tail:]: - click.echo(line) - else: - click.echo(json.dumps(logs_data, indent=2)) - finally: - client.close() - - -# โ”€โ”€ deploy โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@bot.command("deploy") -@_name_option -@_org_option -@_project_option -@_env_option -def bot_deploy( - name: str, - org: str | None, - project: str | None, - env_id: str | None, -) -> None: - """Trigger a redeploy of the bot container.""" - org_id, project_id, env_id = _resolve_bot(org, project, env_id) - - client = PaaSClient.from_auth() - try: - container = _find_container(client, org_id, project_id, env_id, name) - cid = str(container.get("_id", container.get("id"))) - client.redeploy_container(org_id, project_id, env_id, cid) - finally: - client.close() - - console.print(f"[green]Redeployment triggered for '{name}'.[/green]") - - -# โ”€โ”€ env โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@bot.command("env") -@click.argument("vars", nargs=-1) -@_name_option -@_org_option -@_project_option -@_env_option -def bot_env( - vars: tuple[str, ...], - name: str, - org: str | None, - project: str | None, - env_id: str | None, -) -> None: - """Show or set bot environment variables. - - Without arguments, shows current vars. With KEY=VAL pairs, sets them. - - \b - Examples: - hanzo bot env Show current vars - hanzo bot env NODE_ENV=production Set a var - hanzo bot env BOT_TOKEN=xxx LOG_LEVEL=debug Set multiple - """ - org_id, project_id, env_id = _resolve_bot(org, project, env_id) - - client = PaaSClient.from_auth() - try: - container = _find_container(client, org_id, project_id, env_id, name) - cid = str(container.get("_id", container.get("id"))) - - if not vars: - # Show current vars - variables = container.get("variables", []) - if not variables: - console.print("[yellow]No environment variables set.[/yellow]") - return - table = Table(title=f"Env vars: {name}") - table.add_column("Key", style="cyan") - table.add_column("Value", style="dim") - for v in variables: - val = v.get("value", "") - masked = f"{val[:4]}***" if len(val) > 4 else val - table.add_row(v.get("name", "โ€”"), masked) - console.print(table) - else: - # Set vars - new_vars = [] - for kv in vars: - if "=" not in kv: - console.print(f"[red]Invalid format: '{kv}'. Use KEY=VALUE.[/red]") - raise SystemExit(1) - k, v = kv.split("=", 1) - new_vars.append({"name": k, "value": v}) - - # Merge with existing - existing = {v["name"]: v["value"] for v in container.get("variables", [])} - for nv in new_vars: - existing[nv["name"]] = nv["value"] - - merged = [{"name": k, "value": v} for k, v in existing.items()] - container["variables"] = merged - client.update_container(org_id, project_id, env_id, cid, container) - console.print( - f"[green]Set {len(new_vars)} variable(s) on '{name}'.[/green]" - ) - finally: - client.close() - - -# โ”€โ”€ events โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@bot.command("events") -@_name_option -@_org_option -@_project_option -@_env_option -def bot_events( - name: str, - org: str | None, - project: str | None, - env_id: str | None, -) -> None: - """Show bot container events.""" - org_id, project_id, env_id = _resolve_bot(org, project, env_id) - - client = PaaSClient.from_auth() - try: - container = _find_container(client, org_id, project_id, env_id, name) - cid = str(container.get("_id", container.get("id"))) - events_data = client.get_container_events(org_id, project_id, env_id, cid) - - events = ( - events_data - if isinstance(events_data, list) - else events_data.get("data", []) - ) - if not events: - console.print("[yellow]No events found.[/yellow]") - return - - table = Table(title=f"Events: {name}") - table.add_column("Type", style="cyan") - table.add_column("Reason", style="yellow") - table.add_column("Message", style="white") - table.add_column("Age", style="dim") - - for e in events: - table.add_row( - e.get("type", "โ€”"), - e.get("reason", "โ€”"), - e.get("message", "โ€”"), - e.get("age", e.get("lastTimestamp", "โ€”")), - ) - console.print(table) - finally: - client.close() - - -# โ”€โ”€ local node helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def _find_bot_binary() -> str | None: - """Search PATH and ~/.hanzo/bin for the hanzo-bot binary.""" - for name in ("hanzo-bot",): - path = shutil.which(name) - if path: - return path - candidate = HANZO_BIN / name - if candidate.is_file() and os.access(candidate, os.X_OK): - return str(candidate) - return None - - -def _install_bot() -> bool: - """Install @hanzo/bot via npm. Returns True on success.""" - npm = shutil.which("npm") or shutil.which("pnpm") - if not npm: - console.print("[red]npm/pnpm not found. Install Node.js first:[/red]") - console.print(" curl -fsSL https://hanzo.bot/install.sh | bash") - return False - try: - console.print(f"[cyan]Installing {NPM_PACKAGE}...[/cyan]") - subprocess.run( - [npm, "install", "-g", NPM_PACKAGE], - check=True, - capture_output=True, - text=True, - timeout=120, - ) - console.print(f"[green]Installed {NPM_PACKAGE}[/green]") - return True - except subprocess.CalledProcessError as e: - console.print(f"[red]Install failed:[/red] {e.stderr.strip()}") - return False - except subprocess.TimeoutExpired: - console.print("[red]Install timed out.[/red]") - return False - - -def _ensure_bot_binary() -> str: - """Find or install the hanzo-bot binary. Returns the path or exits.""" - binary = _find_bot_binary() - if binary: - return binary - - console.print("[yellow]hanzo-bot not found. Installing...[/yellow]") - if _install_bot(): - binary = _find_bot_binary() - if not binary: - console.print("[red]Failed to install hanzo-bot.[/red]") - console.print("Install manually: npm install -g @hanzo/bot") - console.print(" or: curl -fsSL https://hanzo.bot/install.sh | bash") - raise SystemExit(1) - return binary - - -def _env(name: str) -> str: - """Read env var (IAM_*).""" - return os.environ.get(name, "") - - -def _save_bot_token(data: dict[str, Any]) -> None: - """Save bot gateway token to disk.""" - BOT_TOKEN_FILE.parent.mkdir(parents=True, exist_ok=True) - BOT_TOKEN_FILE.write_text(json.dumps(data, indent=2)) - BOT_TOKEN_FILE.chmod(0o600) - - -def _load_bot_token() -> dict[str, Any] | None: - """Load stored bot gateway token.""" - if not BOT_TOKEN_FILE.exists(): - return None - try: - import time - - data = json.loads(BOT_TOKEN_FILE.read_text()) - # Check expiry - exp = data.get("expires_at", 0) - if exp and exp < time.time(): - return None - return data - except (json.JSONDecodeError, OSError): - return None - - -class _BotOAuthCallbackHandler(http.server.BaseHTTPRequestHandler): - """HTTP handler that captures the bot OAuth callback code.""" - - code: str | None = None - state: str | None = None - error: str | None = None - - def do_GET(self) -> None: # noqa: N802 - from urllib.parse import parse_qs, urlparse - - parsed = urlparse(self.path) - if parsed.path != BOT_CALLBACK_PATH: - self.send_response(404) - self.end_headers() - return - - qs = parse_qs(parsed.query) - if "error" in qs: - _BotOAuthCallbackHandler.error = qs["error"][0] - self._respond("Login failed. You can close this window.", success=False) - return - - _BotOAuthCallbackHandler.code = qs.get("code", [None])[0] - _BotOAuthCallbackHandler.state = qs.get("state", [None])[0] - self._respond("Bot login successful! You can close this window.") - - def _respond(self, message: str, success: bool = True) -> None: - self.send_response(200) - self.send_header("Content-Type", "text/html") - self.end_headers() - accent = "#6C5CE7" if success else "#e74c3c" - body = f""" - - - - -Hanzo Bot - - - -
- -
{"✅" if success else "❌"}
-

{message}

-

You can close this window and return to your terminal.

-

Build something you love.

-
- -""" - self.wfile.write(body.encode()) - - def log_message(self, format: str, *args: Any) -> None: - """Suppress default request logging.""" - - -def _bot_browser_login() -> str: - """Run browser OAuth login flow for the bot gateway. - - Opens the user's browser to the IAM login page (hanzobot application), - starts a local HTTP server to receive the callback, exchanges the code - for tokens scoped to hanzobot-client-id. - - Returns: - Access token string. - """ - import secrets - import time - import webbrowser - - from hanzo_iam import IAMClient, IAMConfig - - config = IAMConfig( - server_url=BOT_IAM_SERVER_URL, - client_id=BOT_IAM_CLIENT_ID, - client_secret="", - organization=BOT_IAM_ORG, - application=BOT_IAM_APP, - ) - client = IAMClient(config=config) - - redirect_uri = f"http://localhost:{BOT_CALLBACK_PORT}{BOT_CALLBACK_PATH}" - state = secrets.token_urlsafe(32) - - auth_url = client.get_authorization_url( - redirect_uri=redirect_uri, - state=state, - scope="openid profile email", - ) - - # Reset handler state - _BotOAuthCallbackHandler.code = None - _BotOAuthCallbackHandler.state = None - _BotOAuthCallbackHandler.error = None - - server = http.server.HTTPServer( - ("127.0.0.1", BOT_CALLBACK_PORT), _BotOAuthCallbackHandler - ) - server.timeout = 120 - - console.print(f"[cyan]Opening browser to login at {BOT_IAM_SERVER_URL}...[/cyan]") - webbrowser.open(auth_url) - console.print( - f"Waiting for callback on http://localhost:{BOT_CALLBACK_PORT}{BOT_CALLBACK_PATH}" - ) - - while ( - _BotOAuthCallbackHandler.code is None - and _BotOAuthCallbackHandler.error is None - ): - server.handle_request() - - server.server_close() - - if _BotOAuthCallbackHandler.error: - console.print(f"[red]Login failed:[/red] {_BotOAuthCallbackHandler.error}") - raise SystemExit(1) - - if _BotOAuthCallbackHandler.state != state: - console.print("[red]State mismatch โ€” possible CSRF attack.[/red]") - raise SystemExit(1) - - code = _BotOAuthCallbackHandler.code - if not code: - console.print("[red]No authorization code received.[/red]") - raise SystemExit(1) - - tokens = client.exchange_code(code=code, redirect_uri=redirect_uri) - client.close() - - access_token = tokens.access_token - if not access_token: - console.print("[red]No access token in response.[/red]") - raise SystemExit(1) - - _save_bot_token({ - "access_token": access_token, - "refresh_token": tokens.refresh_token or "", - "id_token": tokens.id_token or "", - "expires_at": time.time() + (tokens.expires_in or 604800), - "client_id": BOT_IAM_CLIENT_ID, - "server_url": BOT_IAM_SERVER_URL, - "organization": BOT_IAM_ORG, - "application": BOT_IAM_APP, - "login_time": int(time.time()), - }) - - console.print("[green]Bot login successful.[/green]") - return access_token - - -def _bot_password_login( - username: str | None = None, password: str | None = None -) -> str: - """Login via password grant using the hanzobot client. Returns access_token.""" - import time - - import httpx - - if not username: - username = click.prompt("Email") - if not password: - password = click.prompt("Password", hide_input=True) - - resp = httpx.post( - f"{BOT_IAM_SERVER_URL}/oauth/token", - data={ - "grant_type": "password", - "client_id": BOT_IAM_CLIENT_ID, - "username": username, - "password": password, - "scope": "openid profile email", - }, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - timeout=30.0, - ) - data = resp.json() - access_token = data.get("access_token", "") - if not access_token: - err = ( - data.get("error_description") - or data.get("msg") - or data.get("error", "unknown error") - ) - console.print(f"[red]Login failed:[/red] {err}") - raise SystemExit(1) - - _save_bot_token({ - "access_token": access_token, - "refresh_token": data.get("refresh_token", ""), - "expires_at": time.time() + data.get("expires_in", 604800), - "client_id": BOT_IAM_CLIENT_ID, - "username": username, - "login_time": int(time.time()), - }) - return access_token - - -def _get_iam_token() -> str: - """Get an IAM access token for the bot gateway. - - Credential chain: - 1. BOT_GATEWAY_TOKEN env var - 2. Stored bot token (~/.hanzo/bot/token.json) - 3. IAM_CLIENT_ID + IAM_CLIENT_SECRET client credentials - 4. Interactive login prompt - """ - # Check env var first - token = _env("BOT_GATEWAY_TOKEN").strip() - if token: - return token - - # Try stored bot-specific token (issued by hanzobot-client-id) - bot_data = _load_bot_token() - if bot_data and bot_data.get("access_token"): - return bot_data["access_token"] - - # Try client credentials grant - client_id = _env("IAM_CLIENT_ID") - client_secret = _env("IAM_CLIENT_SECRET") - if client_id and client_secret: - try: - import httpx - - resp = httpx.post( - f"{BOT_IAM_SERVER_URL}/oauth/token", - data={ - "grant_type": "client_credentials", - "client_id": client_id, - "client_secret": client_secret, - "scope": "openid profile email", - }, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - timeout=30.0, - ) - data = resp.json() - if data.get("access_token"): - return data["access_token"] - except Exception: - pass - - # Interactive login โ€” browser OAuth flow - console.print("[cyan]Login required for bot gateway.[/cyan]") - return _bot_browser_login() - - -def _read_pid() -> int | None: - """Read the daemon PID from the pid file.""" - if not BOT_PID_FILE.exists(): - return None - try: - pid = int(BOT_PID_FILE.read_text().strip()) - # Check if process is alive - os.kill(pid, 0) - return pid - except (ValueError, OSError): - BOT_PID_FILE.unlink(missing_ok=True) - return None - - -def _write_pid(pid: int) -> None: - """Write daemon PID to file.""" - BOT_PID_FILE.parent.mkdir(parents=True, exist_ok=True) - BOT_PID_FILE.write_text(str(pid)) - - -# โ”€โ”€ install โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@bot.command("install") -def bot_install() -> None: - """Install the @hanzo/bot agent locally. - - Downloads and installs the hanzo-bot CLI from npm. - Requires Node.js (v22+) to be installed. - """ - binary = _find_bot_binary() - if binary: - # Show current version - try: - result = subprocess.run( - [binary, "--version"], - capture_output=True, - text=True, - timeout=10, - ) - version = result.stdout.strip() if result.returncode == 0 else "unknown" - except Exception: - version = "unknown" - console.print(f"[green]hanzo-bot already installed:[/green] {binary} ({version})") - return - - if _install_bot(): - binary = _find_bot_binary() - if binary: - console.print(f"[green]Installed at:[/green] {binary}") - else: - raise SystemExit(1) - - -# โ”€โ”€ login โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@bot.command("login") -@click.option( - "--no-browser", - is_flag=True, - help="Use password login instead of browser OAuth.", -) -def bot_login_cmd(no_browser: bool) -> None: - """Authenticate with the bot gateway. - - Opens your browser to log in via Hanzo IAM. The token is stored - at ~/.hanzo/bot/token.json and used for subsequent bot commands. - - \b - Examples: - hanzo bot login Browser OAuth login - hanzo bot login --no-browser Password login - """ - if no_browser: - token = _bot_password_login() - else: - token = _bot_browser_login() - - # Decode and show who we logged in as - try: - import base64 - - parts = token.split(".") - payload = json.loads(base64.urlsafe_b64decode(parts[1] + "==")) - name = payload.get("name", "unknown") - email = payload.get("email", "") - console.print(f"[green]Logged in as:[/green] {name} ({email})") - except Exception: - console.print("[green]Login stored.[/green]") - - -@bot.command("logout") -def bot_logout_cmd() -> None: - """Clear stored bot gateway credentials.""" - if BOT_TOKEN_FILE.exists(): - BOT_TOKEN_FILE.unlink() - console.print("[green]Bot credentials cleared.[/green]") - else: - console.print("[yellow]No bot credentials stored.[/yellow]") - - -# โ”€โ”€ run โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@bot.command("run") -@click.option( - "--host", - default=DEFAULT_GATEWAY_HOST, - help=f"Gateway host (default: {DEFAULT_GATEWAY_HOST}).", -) -@click.option( - "--port", - default=DEFAULT_GATEWAY_PORT, - type=int, - help=f"Gateway port (default: {DEFAULT_GATEWAY_PORT}).", -) -@click.option("--no-tls", is_flag=True, help="Disable TLS.") -@click.option( - "--display-name", - default=None, - help="Node display name (default: hostname).", -) -@click.option( - "--daemon", - "-d", - is_flag=True, - help="Run as background daemon.", -) -@click.option( - "--token", - default=None, - help="IAM token (default: from hanzo login).", -) -def bot_run( - host: str, - port: int, - no_tls: bool, - display_name: str | None, - daemon: bool, - token: str | None, -) -> None: - """Run a local bot agent node connected to the gateway. - - Starts a headless bot node on this machine that connects to - the Hanzo gateway (gw.hanzo.bot by default). The node appears - in the Playground dashboard at app.hanzo.bot. - - \b - Examples: - hanzo bot run Run in foreground - hanzo bot run -d Run as daemon - hanzo bot run --display-name "My Mac" Custom display name - hanzo bot run --host my-gateway.example.com Custom gateway - """ - # Check for existing daemon - existing_pid = _read_pid() - if existing_pid: - console.print( - f"[yellow]Bot node already running (PID {existing_pid}).[/yellow]" - ) - console.print("Use 'hanzo bot stop' to stop it first.") - raise SystemExit(1) - - # Find or install hanzo-bot - binary = _ensure_bot_binary() - - # Resolve IAM token - iam_token = token or _get_iam_token() - - # Build command - cmd = [binary, "node", "run", "--host", host, "--port", str(port)] - if not no_tls: - cmd.append("--tls") - if display_name: - cmd.extend(["--display-name", display_name]) - else: - import platform as platform_mod - - cmd.extend(["--display-name", platform_mod.node()]) - - # Set up environment โ€” the installed @hanzo/bot binary reads - # OPENCLAW_GATEWAY_TOKEN for the node run command's token resolution. - env = os.environ.copy() - env["OPENCLAW_GATEWAY_TOKEN"] = iam_token - - # Also write token into bot.json config so the config loader picks it up - bot_config_file = Path.home() / ".hanzo" / "bot" / "bot.json" - bot_config_file.parent.mkdir(parents=True, exist_ok=True) - try: - bot_config: dict[str, Any] = {} - if bot_config_file.exists(): - bot_config = json.loads(bot_config_file.read_text()) - gw = bot_config.setdefault("gateway", {}) - gw["mode"] = "remote" - remote = gw.setdefault("remote", {}) - remote["url"] = f"{'wss' if not no_tls else 'ws'}://{host}" - remote["transport"] = "direct" - remote["token"] = iam_token - auth = gw.setdefault("auth", {}) - auth["mode"] = "iam" - auth["token"] = iam_token - iam_cfg = auth.setdefault("iam", {}) - iam_cfg["serverUrl"] = BOT_IAM_SERVER_URL - iam_cfg["clientId"] = BOT_IAM_CLIENT_ID - iam_cfg["orgName"] = BOT_IAM_ORG - bot_config_file.write_text(json.dumps(bot_config, indent=2)) - except Exception: - pass # Non-fatal โ€” env var is the primary mechanism - - if daemon: - # Run as background daemon - BOT_LOG_FILE.parent.mkdir(parents=True, exist_ok=True) - log_fd = open(BOT_LOG_FILE, "a") - proc = subprocess.Popen( - cmd, - env=env, - stdout=log_fd, - stderr=log_fd, - start_new_session=True, - ) - _write_pid(proc.pid) - console.print(f"[green]Bot node started (PID {proc.pid}).[/green]") - console.print(f" Gateway: {'wss' if not no_tls else 'ws'}://{host}:{port}") - console.print(f" Logs: {BOT_LOG_FILE}") - console.print(f" PID: {BOT_PID_FILE}") - console.print() - console.print("Stop with: [cyan]hanzo bot stop[/cyan]") - else: - # Run in foreground - console.print( - f"[cyan]Connecting to {'wss' if not no_tls else 'ws'}://{host}:{port}...[/cyan]" - ) - try: - proc = subprocess.Popen(cmd, env=env) - proc.wait() - except KeyboardInterrupt: - console.print("\n[yellow]Shutting down...[/yellow]") - proc.terminate() - proc.wait(timeout=5) - - -# โ”€โ”€ stop โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@bot.command("stop") -def bot_stop() -> None: - """Stop the local bot node daemon.""" - pid = _read_pid() - if not pid: - console.print("[yellow]No bot node daemon running.[/yellow]") - return - - try: - os.kill(pid, signal.SIGTERM) - console.print(f"[green]Stopped bot node (PID {pid}).[/green]") - except ProcessLookupError: - console.print("[yellow]Process already exited.[/yellow]") - finally: - BOT_PID_FILE.unlink(missing_ok=True) diff --git a/pkg/hanzo-cli/hanzo_cli/cli.py b/pkg/hanzo-cli/hanzo_cli/cli.py deleted file mode 100644 index e20e801b5..000000000 --- a/pkg/hanzo-cli/hanzo_cli/cli.py +++ /dev/null @@ -1,139 +0,0 @@ -"""Hanzo CLI โ€” unified command-line interface for the Hanzo platform. - -Usage: - hanzo --version - hanzo login [--no-browser] [--port N] - hanzo logout - hanzo whoami - hanzo iam - hanzo kms - hanzo paas - hanzo k8s -""" - -from __future__ import annotations - -import click -from rich.console import Console -from rich.table import Table - -from hanzo_cli import __version__ -from hanzo_cli.auth import ( - browser_login, - get_token_info, - password_login, -) -from hanzo_cli.auth import ( - logout as do_logout, -) - -console = Console() - - -@click.group() -@click.version_option(version=__version__, prog_name="hanzo") -@click.pass_context -def main(ctx: click.Context) -> None: - """Hanzo CLI โ€” manage IAM, secrets, and deployments.""" - ctx.ensure_object(dict) - - -# ========================================================================= -# Auth commands -# ========================================================================= - - -@main.command() -@click.option( - "--no-browser", is_flag=True, help="Use password login instead of browser OAuth." -) -@click.option("--port", default=8399, help="Local callback port for browser login.") -@click.pass_context -def login(ctx: click.Context, no_browser: bool, port: int) -> None: - """Authenticate with Hanzo IAM.""" - try: - if no_browser: - token_data = password_login() - else: - token_data = browser_login(port=port) - - console.print("[green]Logged in successfully.[/green]") - if token_data.get("access_token"): - # Decode the token subject for display - _at = token_data["access_token"] - console.print("Token stored at ~/.hanzo/auth/token.json") - except Exception as e: - console.print(f"[red]Login failed:[/red] {e}") - raise SystemExit(1) from e - - -@main.command() -def logout() -> None: - """Clear stored credentials.""" - do_logout() - console.print("Logged out. Token removed.") - - -@main.command() -def whoami() -> None: - """Show current authentication status.""" - token_data = get_token_info() - if not token_data: - console.print("[yellow]Not logged in.[/yellow] Run 'hanzo login'.") - raise SystemExit(1) - - table = Table(title="Current Session") - table.add_column("Field", style="cyan") - table.add_column("Value", style="white") - - table.add_row("Server", token_data.get("server_url", "โ€”")) - table.add_row("Organization", token_data.get("organization", "โ€”")) - table.add_row("Application", token_data.get("application", "โ€”")) - table.add_row("Client ID", token_data.get("client_id", "โ€”")) - - # Try to decode the token for user info - access_token = token_data.get("access_token", "") - if access_token: - try: - import jwt - - # Decode without verification just to show user info - claims = jwt.decode(access_token, options={"verify_signature": False}) - table.add_row("User", claims.get("name", claims.get("sub", "โ€”"))) - table.add_row("Email", claims.get("email", "โ€”")) - table.add_row("Owner", claims.get("owner", "โ€”")) - except Exception: - table.add_row("Token", f"{access_token[:20]}...") - - login_time = token_data.get("login_time") - if login_time: - from datetime import datetime, timezone - - dt = datetime.fromtimestamp(login_time, tz=timezone.utc) - table.add_row("Login Time", dt.isoformat()) - - console.print(table) - - -# ========================================================================= -# Register subgroups -# ========================================================================= - -from hanzo_cli.bot.commands import bot # noqa: E402 -from hanzo_cli.iam.commands import iam # noqa: E402 -from hanzo_cli.k8s.commands import k8s # noqa: E402 -from hanzo_cli.kms.commands import kms # noqa: E402 -from hanzo_cli.paas.commands import deploy, paas # noqa: E402 - -main.add_command(bot) -main.add_command(iam) -main.add_command(k8s) -main.add_command(kms) -main.add_command(paas) - -# Top-level aliases โ€” `hanzo deploy` = `hanzo paas deploy` -main.add_command(deploy) - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-cli/hanzo_cli/iam/__init__.py b/pkg/hanzo-cli/hanzo_cli/iam/__init__.py deleted file mode 100644 index 4196f10a0..000000000 --- a/pkg/hanzo-cli/hanzo_cli/iam/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Hanzo CLI โ€” IAM subcommands.""" diff --git a/pkg/hanzo-cli/hanzo_cli/iam/commands.py b/pkg/hanzo-cli/hanzo_cli/iam/commands.py deleted file mode 100644 index d5aa5106d..000000000 --- a/pkg/hanzo-cli/hanzo_cli/iam/commands.py +++ /dev/null @@ -1,263 +0,0 @@ -"""Hanzo CLI โ€” IAM subcommands. - -Usage: - hanzo iam users โ€” List users - hanzo iam user โ€” Get user details - hanzo iam set-password [pw] โ€” Set password (prompt if no pw) - hanzo iam orgs โ€” List organizations - hanzo iam apps โ€” List applications - hanzo iam sync-app [--init-data] โ€” Sync redirect URIs -""" - -from __future__ import annotations - -import click -from rich.console import Console -from rich.table import Table - -from hanzo_cli.auth import get_client - -console = Console() - - -@click.group() -def iam() -> None: - """Manage Hanzo IAM โ€” users, orgs, apps, passwords.""" - - -# ========================================================================= -# Users -# ========================================================================= - - -@iam.command("users") -def list_users() -> None: - """List all users in the organization.""" - client = get_client() - try: - users = client.get_users() - finally: - client.close() - - if not users: - console.print("[yellow]No users found.[/yellow]") - return - - table = Table(title=f"Users ({len(users)})") - table.add_column("Name", style="cyan") - table.add_column("Display Name", style="white") - table.add_column("Email", style="white") - table.add_column("Admin", style="yellow") - table.add_column("Online", style="green") - - for u in users: - table.add_row( - u.name, - u.display_name or "โ€”", - u.email or "โ€”", - "yes" if u.is_admin else "", - "yes" if u.is_online else "", - ) - - console.print(table) - - -@iam.command("user") -@click.argument("name") -def get_user(name: str) -> None: - """Get details for a specific user.""" - client = get_client() - try: - user = client.get_user(name) - finally: - client.close() - - table = Table(title=f"User: {user.owner}/{user.name}") - table.add_column("Field", style="cyan") - table.add_column("Value", style="white") - - table.add_row("ID", user.id or "โ€”") - table.add_row("Owner", user.owner) - table.add_row("Name", user.name) - table.add_row("Display Name", user.display_name or "โ€”") - table.add_row("Email", user.email or "โ€”") - table.add_row("Phone", user.phone or "โ€”") - table.add_row("Type", user.type or "โ€”") - table.add_row("Admin", "yes" if user.is_admin else "no") - table.add_row("Deleted", "yes" if user.is_deleted else "no") - table.add_row("Forbidden", "yes" if user.is_forbidden else "no") - table.add_row("Online", "yes" if user.is_online else "no") - table.add_row("Email Verified", "yes" if user.email_verified else "no") - table.add_row("Balance", str(user.balance)) - table.add_row("Created", user.created_time or "โ€”") - table.add_row("Updated", user.updated_time or "โ€”") - - if user.roles: - table.add_row("Roles", ", ".join(user.roles)) - if user.groups: - table.add_row("Groups", ", ".join(user.groups)) - - console.print(table) - - -# ========================================================================= -# Password Management -# ========================================================================= - - -@iam.command("set-password") -@click.argument("user") -@click.argument("password", required=False) -def set_password(user: str, password: str | None) -> None: - """Set a user's password. Prompts if password not given.""" - if not password: - password = click.prompt( - "New password", hide_input=True, confirmation_prompt=True - ) - - client = get_client() - org = client.config.organization - - try: - result = client.set_password( - user_owner=org, - user_name=user, - new_password=password, - ) - finally: - client.close() - - status = result.get("status", "unknown") - if status == "ok": - console.print(f"[green]Password set for {org}/{user}.[/green]") - else: - msg = result.get("msg", "Unknown error") - console.print(f"[red]Failed:[/red] {msg}") - raise SystemExit(1) - - -# ========================================================================= -# Organizations -# ========================================================================= - - -@iam.command("orgs") -def list_orgs() -> None: - """List all organizations.""" - client = get_client() - try: - orgs = client.get_organizations() - finally: - client.close() - - if not orgs: - console.print("[yellow]No organizations found.[/yellow]") - return - - table = Table(title=f"Organizations ({len(orgs)})") - table.add_column("Name", style="cyan") - table.add_column("Display Name", style="white") - table.add_column("Website", style="white") - - for o in orgs: - table.add_row( - o.get("name", "โ€”"), - o.get("displayName", "โ€”"), - o.get("websiteUrl", "โ€”"), - ) - - console.print(table) - - -# ========================================================================= -# Applications -# ========================================================================= - - -@iam.command("apps") -def list_apps() -> None: - """List all applications.""" - client = get_client() - try: - apps = client.get_applications() - finally: - client.close() - - if not apps: - console.print("[yellow]No applications found.[/yellow]") - return - - table = Table(title=f"Applications ({len(apps)})") - table.add_column("Owner", style="white") - table.add_column("Name", style="cyan") - table.add_column("Display Name", style="white") - table.add_column("Client ID", style="dim") - table.add_column("Redirect URIs", style="white") - - for app in apps: - uris = ", ".join(app.redirect_uris) if app.redirect_uris else "โ€”" - table.add_row( - app.owner, - app.name, - app.display_name or "โ€”", - app.client_id[:16] + "..." if len(app.client_id) > 16 else app.client_id, - uris, - ) - - console.print(table) - - -@iam.command("sync-app") -@click.argument("name") -@click.option("--init-data", is_flag=True, help="Initialize default redirect URIs.") -def sync_app(name: str, init_data: bool) -> None: - """Sync an application's redirect URIs. - - Reads the application, optionally initializes default URIs, - and updates it back. - """ - client = get_client() - try: - # Get all apps and find the one we want - apps = client.get_applications() - target = None - for app in apps: - if app.name == name: - target = app - break - - if target is None: - console.print(f"[red]Application '{name}' not found.[/red]") - raise SystemExit(1) - - if init_data: - # Add standard development redirect URIs if not present - default_uris = [ - "http://localhost:3000/callback", - "http://localhost:3000/api/auth/callback/hanzo", - "http://localhost:8399/callback", - ] - existing = set(target.redirect_uris) - added = [] - for uri in default_uris: - if uri not in existing: - target.redirect_uris.append(uri) - added.append(uri) - - if added: - _result = client.update_application(target) - console.print( - f"[green]Added {len(added)} redirect URIs to {name}:[/green]" - ) - for uri in added: - console.print(f" + {uri}") - else: - console.print(f"All default URIs already present in {name}.") - else: - console.print(f"Application: {target.owner}/{target.name}") - console.print(f"Client ID: {target.client_id}") - console.print("Redirect URIs:") - for uri in target.redirect_uris: - console.print(f" - {uri}") - finally: - client.close() diff --git a/pkg/hanzo-cli/hanzo_cli/k8s/__init__.py b/pkg/hanzo-cli/hanzo_cli/k8s/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-cli/hanzo_cli/k8s/clusters.py b/pkg/hanzo-cli/hanzo_cli/k8s/clusters.py deleted file mode 100644 index 18fdbfab6..000000000 --- a/pkg/hanzo-cli/hanzo_cli/k8s/clusters.py +++ /dev/null @@ -1,164 +0,0 @@ -"""Multi-cluster management for Hanzo K8s CLI. - -Stores known clusters in ~/.hanzo/k8s/clusters.json and manages -the current active cluster context for kubectl operations. - -Usage: - hanzo k8s clusters # List all clusters - hanzo k8s use # Switch to a cluster -""" - -from __future__ import annotations - -import json -import shutil -import subprocess -from pathlib import Path - -import click -from rich.console import Console -from rich.table import Table - -console = Console() - -CLUSTERS_FILE = Path.home() / ".hanzo" / "k8s" / "clusters.json" - -DEFAULT_CLUSTERS: dict = { - "clusters": { - "hanzo": { - "context": "do-sfo3-hanzo-k8s", - "namespace": "hanzo", - "ip": "209.38.69.69", - }, - "adnexus": { - "context": "do-sfo3-adnexus-k8s", - "namespace": "adnexus", - "ip": "134.199.141.68", - }, - "lux": { - "context": "do-sfo3-lux-k8s", - "namespace": "lux", - }, - "pars": { - "context": "do-sfo3-pars-k8s", - "namespace": "pars", - }, - "zoo": { - "context": "do-sfo3-zoo-k8s", - "namespace": "zoo", - }, - "bootnode": { - "context": "do-sfo3-bootnode-k8s", - "namespace": "bootnode", - }, - }, - "current": "hanzo", -} - - -def _load_clusters() -> dict: - """Load clusters config, auto-creating with defaults on first use.""" - if CLUSTERS_FILE.exists(): - try: - return json.loads(CLUSTERS_FILE.read_text()) - except (json.JSONDecodeError, OSError): - pass - _save_clusters(DEFAULT_CLUSTERS) - return DEFAULT_CLUSTERS - - -def _save_clusters(data: dict) -> None: - """Persist clusters config to disk.""" - CLUSTERS_FILE.parent.mkdir(parents=True, exist_ok=True) - CLUSTERS_FILE.write_text(json.dumps(data, indent=2)) - - -def get_current_cluster() -> tuple[str, dict]: - """Return (name, cluster_info) for the current active cluster.""" - config = _load_clusters() - name = config.get("current", "hanzo") - cluster = config.get("clusters", {}).get(name, {}) - return name, cluster - - -@click.command("clusters") -def clusters() -> None: - """List all known Kubernetes clusters.""" - config = _load_clusters() - current = config.get("current", "") - cluster_map = config.get("clusters", {}) - - if not cluster_map: - console.print("[yellow]No clusters configured.[/yellow]") - return - - table = Table(title="Kubernetes Clusters") - table.add_column("", width=2) - table.add_column("Name", style="cyan") - table.add_column("Context", style="white") - table.add_column("Namespace", style="green") - table.add_column("IP", style="dim") - - for name, info in sorted(cluster_map.items()): - marker = "[bold green]*[/bold green]" if name == current else "" - table.add_row( - marker, - name, - info.get("context", ""), - info.get("namespace", ""), - info.get("ip", ""), - ) - - console.print(table) - console.print(f"\nCurrent: [bold cyan]{current}[/bold cyan]") - - -@click.command("use") -@click.argument("name") -def use(name: str) -> None: - """Set the current Kubernetes cluster. - - Switches kubectl context and updates the default namespace for - subsequent hanzo k8s commands. - """ - config = _load_clusters() - cluster_map = config.get("clusters", {}) - - if name not in cluster_map: - available = ", ".join(sorted(cluster_map.keys())) - console.print(f"[red]Unknown cluster:[/red] {name}") - console.print(f"Available: {available}") - raise SystemExit(1) - - info = cluster_map[name] - context = info.get("context", "") - - # Switch kubectl context - if context: - kubectl = shutil.which("kubectl") - if kubectl: - result = subprocess.run( - [kubectl, "config", "use-context", context], - capture_output=True, - text=True, - ) - if result.returncode != 0: - console.print( - f"[yellow]Warning:[/yellow] kubectl context switch failed: " - f"{result.stderr.strip()}" - ) - console.print( - "You may need to add this context first with " - "'doctl kubernetes cluster kubeconfig save'" - ) - else: - console.print(f"Switched kubectl context to [cyan]{context}[/cyan]") - else: - console.print("[yellow]kubectl not found, skipping context switch.[/yellow]") - - # Update current cluster in config - config["current"] = name - _save_clusters(config) - - ns = info.get("namespace", name) - console.print(f"Active cluster: [bold green]{name}[/bold green] (namespace: {ns})") diff --git a/pkg/hanzo-cli/hanzo_cli/k8s/commands.py b/pkg/hanzo-cli/hanzo_cli/k8s/commands.py deleted file mode 100644 index f703c5a05..000000000 --- a/pkg/hanzo-cli/hanzo_cli/k8s/commands.py +++ /dev/null @@ -1,317 +0,0 @@ -"""Hanzo CLI โ€” Kubernetes management via kubectl passthrough. - -Wraps kubectl with Hanzo-managed authentication and sensible defaults. -Unrecognized subcommands pass directly through to kubectl. - -Usage: - hanzo k8s auth # Fetch kubeconfig from Hanzo PaaS - hanzo k8s clusters # List known clusters - hanzo k8s use # Switch active cluster - hanzo k8s services # Show service status - hanzo k8s health # Run health checks - hanzo k8s dns zones # List Cloudflare zones - hanzo k8s rollout # Restart a deployment - hanzo k8s get pods # kubectl get pods -n hanzo - hanzo k8s logs # kubectl logs -n hanzo - hanzo k8s apply -f file.yaml # kubectl apply -f file.yaml -n hanzo -""" - -from __future__ import annotations - -import json -import os -import shutil -import subprocess -import sys -from pathlib import Path - -import click -from rich.console import Console -from rich.table import Table - -from hanzo_cli.paas.client import PaaSClient - -console = Console() - -KUBECONFIG_DIR = Path.home() / ".hanzo" / "k8s" -KUBECONFIG_FILE = KUBECONFIG_DIR / "kubeconfig" -DEFAULT_NAMESPACE = "hanzo" - - -# โ”€โ”€ helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def _find_kubectl() -> str: - """Locate kubectl or exit with install instructions.""" - path = shutil.which("kubectl") - if not path: - console.print( - "[red]kubectl not found.[/red] " - "Install: https://kubernetes.io/docs/tasks/tools/" - ) - sys.exit(1) - return path - - -def _kubeconfig_env() -> dict[str, str]: - """Return env dict with KUBECONFIG set if the managed file exists.""" - env = dict(os.environ) - if KUBECONFIG_FILE.exists(): - env["KUBECONFIG"] = str(KUBECONFIG_FILE) - return env - - -def _kubectl_run(args: list[str], namespace: str | None = None) -> int: - """Execute kubectl with managed kubeconfig and optional namespace. - - Returns the process exit code. - """ - kubectl = _find_kubectl() - cmd = [kubectl] - - # Inject default namespace if not already specified - ns_flags = {"--namespace", "-n", "--all-namespaces", "-A"} - if namespace and not ns_flags.intersection(args): - cmd.extend(["--namespace", namespace]) - - cmd.extend(args) - result = subprocess.run(cmd, env=_kubeconfig_env()) - return result.returncode - - -# โ”€โ”€ custom group class โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -class KubectlGroup(click.Group): - """Click group that forwards unknown subcommands to kubectl. - - Known subcommands (auth, info, context, etc.) are handled normally. - Everything else (get, apply, describe, rollout, ...) creates a dynamic - command that passes through to kubectl. - """ - - def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None: - # Try builtin commands first - rv = super().get_command(ctx, cmd_name) - if rv is not None: - return rv - - # Dynamic kubectl passthrough command - @click.command( - cmd_name, - context_settings={ - "ignore_unknown_options": True, - "allow_extra_args": True, - "allow_interspersed_args": False, - }, - ) - @click.argument("args", nargs=-1, type=click.UNPROCESSED) - @click.pass_context - def kubectl_proxy(ctx: click.Context, args: tuple[str, ...]) -> None: - ns = ( - ctx.parent.params.get("namespace") if ctx.parent else None - ) or DEFAULT_NAMESPACE - all_args = [cmd_name] + list(args) - rc = _kubectl_run(all_args, namespace=ns) - ctx.exit(rc) - - kubectl_proxy.help = f"kubectl {cmd_name} (passthrough)" - return kubectl_proxy - - def resolve_command( - self, ctx: click.Context, args: list[str] - ) -> tuple[str | None, click.Command | None, list[str]]: - # Always resolve โ€” get_command handles unknown names via passthrough - cmd_name = args[0] if args else None - if cmd_name is None: - return super().resolve_command(ctx, args) - - cmd = self.get_command(ctx, cmd_name) - if cmd is None: - return super().resolve_command(ctx, args) - - return cmd_name, cmd, args[1:] - - -# โ”€โ”€ click group โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@click.group(cls=KubectlGroup, invoke_without_command=True) -@click.option( - "-n", - "--namespace", - default=None, - help="Kubernetes namespace (default: hanzo).", -) -@click.pass_context -def k8s(ctx: click.Context, namespace: str | None) -> None: - """Kubernetes management โ€” wraps kubectl with Hanzo auth. - - Any unrecognized subcommand is forwarded directly to kubectl with - the managed kubeconfig and default namespace. - - \b - Examples - hanzo k8s auth Set up kubeconfig - hanzo k8s get pods kubectl get pods -n hanzo - hanzo k8s -n production get po kubectl get pods -n production - hanzo k8s logs my-pod -f kubectl logs my-pod -f -n hanzo - hanzo k8s apply -f deploy.yaml kubectl apply -f deploy.yaml - """ - ctx.ensure_object(dict) - ctx.obj["namespace"] = namespace or DEFAULT_NAMESPACE - - if ctx.invoked_subcommand is None: - click.echo(ctx.get_help()) - - -# โ”€โ”€ auth โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@k8s.command("auth") -@click.option("--force", is_flag=True, help="Overwrite existing kubeconfig.") -def k8s_auth(force: bool) -> None: - """Authenticate and fetch kubeconfig from Hanzo PaaS. - - Exchanges your IAM credentials for cluster access and stores the - kubeconfig at ~/.hanzo/k8s/kubeconfig. - """ - if KUBECONFIG_FILE.exists() and not force: - console.print(f"Kubeconfig exists at [cyan]{KUBECONFIG_FILE}[/cyan]") - console.print("Use [bold]--force[/bold] to overwrite.") - return - - console.print("Authenticating with Hanzo PaaS...") - - client = PaaSClient.from_auth() - try: - kubeconfig_text = _fetch_kubeconfig(client) - finally: - client.close() - - if kubeconfig_text: - KUBECONFIG_DIR.mkdir(parents=True, exist_ok=True) - KUBECONFIG_FILE.write_text(kubeconfig_text) - KUBECONFIG_FILE.chmod(0o600) - console.print(f"[green]Kubeconfig saved to {KUBECONFIG_FILE}[/green]") - console.print("Run [bold]hanzo k8s get pods[/bold] to verify.") - else: - default_kc = Path.home() / ".kube" / "config" - console.print("[yellow]PaaS kubeconfig endpoint not available yet.[/yellow]") - if default_kc.exists(): - console.print(f"kubectl will use your default config at {default_kc}") - else: - console.print( - "Configure kubectl manually:\n" - " doctl kubernetes cluster kubeconfig save " - ) - - -def _fetch_kubeconfig(client: PaaSClient) -> str | None: - """Try multiple PaaS endpoints to obtain kubeconfig text.""" - # 1. Dedicated kubeconfig endpoint - try: - resp = client.http.get("/v1/cluster/kubeconfig") - if resp.status_code == 200: - ct = resp.headers.get("content-type", "") - if "json" in ct: - data = resp.json() - return data.get("kubeconfig", json.dumps(data, indent=2)) - return resp.text - except Exception: - pass - - # 2. Cluster info โ€” may contain embedded kubeconfig - try: - info = client.cluster_info() - kc = info.get("kubeconfig") or info.get("config") - if kc: - if isinstance(kc, dict): - return json.dumps(kc, indent=2) - return str(kc) - except Exception: - pass - - return None - - -# โ”€โ”€ info โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@k8s.command("info") -def k8s_info() -> None: - """Show cluster information from Hanzo PaaS.""" - client = PaaSClient.from_auth() - try: - info = client.cluster_info() - finally: - client.close() - - table = Table(title="Cluster Info") - table.add_column("Field", style="cyan") - table.add_column("Value", style="white") - - for key, value in info.items(): - display = ( - json.dumps(value, indent=2) - if isinstance(value, (dict, list)) - else str(value) - ) - table.add_row(key, display) - - console.print(table) - - -# โ”€โ”€ context โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@k8s.command("context") -def k8s_context() -> None: - """Show current kubectl context and configuration status.""" - table = Table(title="Kubernetes Config") - table.add_column("Field", style="cyan") - table.add_column("Value", style="white") - - if KUBECONFIG_FILE.exists(): - table.add_row("Kubeconfig", str(KUBECONFIG_FILE)) - else: - default_kc = Path.home() / ".kube" / "config" - if default_kc.exists(): - table.add_row("Kubeconfig", f"{default_kc} (system default)") - else: - table.add_row("Kubeconfig", "[red]Not configured[/red]") - - table.add_row("Default Namespace", DEFAULT_NAMESPACE) - - kubectl = shutil.which("kubectl") - table.add_row("kubectl", kubectl or "[red]Not installed[/red]") - - console.print(table) - - if kubectl: - env = _kubeconfig_env() - result = subprocess.run( - [kubectl, "config", "current-context"], - capture_output=True, - text=True, - env=env, - ) - if result.returncode == 0: - console.print(f"\nCurrent context: [green]{result.stdout.strip()}[/green]") - else: - console.print("\n[yellow]No current context set.[/yellow]") - - -# โ”€โ”€ register subcommands โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -from hanzo_cli.k8s.clusters import clusters, use # noqa: E402 -from hanzo_cli.k8s.dns import dns # noqa: E402 -from hanzo_cli.k8s.health import health # noqa: E402 -from hanzo_cli.k8s.services import rollout, services # noqa: E402 - -k8s.add_command(clusters) -k8s.add_command(use) -k8s.add_command(health) -k8s.add_command(dns) -k8s.add_command(services) -k8s.add_command(rollout) diff --git a/pkg/hanzo-cli/hanzo_cli/k8s/dns.py b/pkg/hanzo-cli/hanzo_cli/k8s/dns.py deleted file mode 100644 index 70e557da0..000000000 --- a/pkg/hanzo-cli/hanzo_cli/k8s/dns.py +++ /dev/null @@ -1,285 +0,0 @@ -"""Cloudflare DNS management for Hanzo K8s CLI. - -Reads credentials from ~/.hanzo/credentials.json under the 'cloudflare' key. -Provides commands to list zones, list A records, and batch-update IPs. - -Usage: - hanzo k8s dns zones # List all zones - hanzo k8s dns list --zone hanzo.ai # List A records - hanzo k8s dns update hanzo.ai 1.2.3.4 5.6.7.8 --dry-run -""" - -from __future__ import annotations - -import json -import sys -from pathlib import Path -from typing import Any - -import click -import httpx -from rich.console import Console -from rich.table import Table - -console = Console() - -CF_API = "https://api.cloudflare.com/client/v4" -CREDENTIALS_FILE = Path.home() / ".hanzo" / "credentials.json" - - -def _load_cf_credentials() -> tuple[str, str]: - """Load Cloudflare email + API key from ~/.hanzo/credentials.json. - - Returns (email, api_key) or exits with an error message. - """ - if not CREDENTIALS_FILE.exists(): - console.print( - f"[red]Credentials file not found:[/red] {CREDENTIALS_FILE}\n" - "Create it with a 'cloudflare' section containing " - "'email' and 'api_key'." - ) - sys.exit(1) - - try: - data = json.loads(CREDENTIALS_FILE.read_text()) - except (json.JSONDecodeError, OSError) as e: - console.print(f"[red]Failed to read credentials:[/red] {e}") - sys.exit(1) - - cf = data.get("cloudflare", {}) - email = cf.get("email", "") - api_key = cf.get("api_key", "") - - if not email or not api_key: - console.print( - "[red]Missing Cloudflare credentials.[/red]\n" - f"Ensure {CREDENTIALS_FILE} has:\n" - ' {"cloudflare": {"email": "...", "api_key": "..."}}' - ) - sys.exit(1) - - return email, api_key - - -def _cf_headers(email: str, api_key: str) -> dict[str, str]: - """Build Cloudflare API auth headers.""" - return { - "X-Auth-Email": email, - "X-Auth-Key": api_key, - "Content-Type": "application/json", - } - - -def _cf_get( - client: httpx.Client, path: str, params: dict[str, Any] | None = None -) -> Any: - """GET from Cloudflare API, handling pagination and errors.""" - resp = client.get(f"{CF_API}{path}", params=params) - data = resp.json() - if not data.get("success"): - errors = data.get("errors", []) - msg = "; ".join(e.get("message", str(e)) for e in errors) - raise click.ClickException(f"Cloudflare API error: {msg}") - return data - - -# โ”€โ”€ Click group โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@click.group("dns") -def dns() -> None: - """Cloudflare DNS management. - - List zones, view A records, and batch-update IP addresses. - Reads credentials from ~/.hanzo/credentials.json. - """ - - -# โ”€โ”€ zones โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dns.command("zones") -def dns_zones() -> None: - """List all Cloudflare zones in the account.""" - email, api_key = _load_cf_credentials() - - with httpx.Client(headers=_cf_headers(email, api_key), timeout=30.0) as client: - data = _cf_get(client, "/zones", params={"per_page": "50"}) - - zones = data.get("result", []) - if not zones: - console.print("[yellow]No zones found.[/yellow]") - return - - table = Table(title="Cloudflare Zones") - table.add_column("Name", style="cyan") - table.add_column("ID", style="dim") - table.add_column("Status", style="green") - table.add_column("Plan", style="white") - - for z in sorted(zones, key=lambda x: x.get("name", "")): - table.add_row( - z.get("name", ""), - z.get("id", ""), - z.get("status", ""), - z.get("plan", {}).get("name", ""), - ) - - console.print(table) - - -# โ”€โ”€ list โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dns.command("list") -@click.option( - "--zone", - "-z", - required=True, - help="Zone name (e.g. hanzo.ai).", -) -@click.option( - "--type", - "record_type", - default="A", - help="DNS record type (default: A).", -) -def dns_list(zone: str, record_type: str) -> None: - """List DNS records for a zone.""" - email, api_key = _load_cf_credentials() - - with httpx.Client(headers=_cf_headers(email, api_key), timeout=30.0) as client: - # Resolve zone name to zone ID - zone_id = _resolve_zone_id(client, zone) - - # Fetch records - data = _cf_get( - client, - f"/zones/{zone_id}/dns_records", - params={"type": record_type, "per_page": "100"}, - ) - - records = data.get("result", []) - if not records: - console.print(f"[yellow]No {record_type} records found for {zone}.[/yellow]") - return - - table = Table(title=f"DNS Records โ€” {zone} ({record_type})") - table.add_column("Name", style="cyan", min_width=25) - table.add_column("Content", style="white") - table.add_column("TTL", justify="right") - table.add_column("Proxied", justify="center") - table.add_column("ID", style="dim") - - for r in sorted(records, key=lambda x: x.get("name", "")): - ttl = r.get("ttl", 0) - ttl_str = "Auto" if ttl == 1 else str(ttl) - proxied = "[green]yes[/green]" if r.get("proxied") else "no" - table.add_row( - r.get("name", ""), - r.get("content", ""), - ttl_str, - proxied, - r.get("id", "")[:12], - ) - - console.print(table) - console.print(f"\n{len(records)} record(s)") - - -# โ”€โ”€ update โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dns.command("update") -@click.argument("zone") -@click.argument("old_ip") -@click.argument("new_ip") -@click.option("--dry-run", is_flag=True, help="Show changes without applying.") -def dns_update(zone: str, old_ip: str, new_ip: str, dry_run: bool) -> None: - """Batch update A records: replace OLD_IP with NEW_IP. - - Finds all A records in ZONE pointing to OLD_IP and updates them - to NEW_IP. Use --dry-run to preview changes first. - """ - email, api_key = _load_cf_credentials() - - with httpx.Client(headers=_cf_headers(email, api_key), timeout=30.0) as client: - zone_id = _resolve_zone_id(client, zone) - - # Fetch A records matching old IP - data = _cf_get( - client, - f"/zones/{zone_id}/dns_records", - params={"type": "A", "content": old_ip, "per_page": "100"}, - ) - - records = data.get("result", []) - if not records: - console.print( - f"[yellow]No A records found in {zone} pointing to {old_ip}.[/yellow]" - ) - return - - console.print( - f"Found [cyan]{len(records)}[/cyan] A record(s) " - f"pointing to [red]{old_ip}[/red]\n" - ) - - table = Table(title="Records to Update") - table.add_column("Name", style="cyan") - table.add_column("Current", style="red") - table.add_column("New", style="green") - table.add_column("Proxied", justify="center") - - for r in records: - proxied = "yes" if r.get("proxied") else "no" - table.add_row(r.get("name", ""), old_ip, new_ip, proxied) - - console.print(table) - - if dry_run: - console.print("\n[yellow]Dry run โ€” no changes applied.[/yellow]") - return - - # Apply updates - updated = 0 - failed = 0 - for r in records: - record_id = r["id"] - try: - resp = client.patch( - f"{CF_API}/zones/{zone_id}/dns_records/{record_id}", - json={"content": new_ip}, - ) - result = resp.json() - if result.get("success"): - updated += 1 - else: - failed += 1 - errs = result.get("errors", []) - msg = "; ".join(e.get("message", "") for e in errs) - console.print( - f" [red]Failed:[/red] {r.get('name', '')} โ€” {msg}" - ) - except Exception as e: - failed += 1 - console.print(f" [red]Error:[/red] {r.get('name', '')} โ€” {e}") - - console.print( - f"\n[green]{updated} updated[/green]" - + (f", [red]{failed} failed[/red]" if failed else "") - ) - - -# โ”€โ”€ helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def _resolve_zone_id(client: httpx.Client, zone_name: str) -> str: - """Resolve a zone name to its Cloudflare zone ID.""" - data = _cf_get(client, "/zones", params={"name": zone_name, "per_page": "1"}) - zones = data.get("result", []) - if not zones: - raise click.ClickException( - f"Zone '{zone_name}' not found in Cloudflare account." - ) - return zones[0]["id"] diff --git a/pkg/hanzo-cli/hanzo_cli/k8s/health.py b/pkg/hanzo-cli/hanzo_cli/k8s/health.py deleted file mode 100644 index c7314dd1b..000000000 --- a/pkg/hanzo-cli/hanzo_cli/k8s/health.py +++ /dev/null @@ -1,206 +0,0 @@ -"""Service health checks for Hanzo K8s CLI. - -Runs concurrent HTTP health checks against known domains and displays -results in a rich table with color-coded status. - -Usage: - hanzo k8s health # Check default cluster domains - hanzo k8s health --cluster adnexus # Check a specific cluster - hanzo k8s health --timeout 10 # Custom timeout -""" - -from __future__ import annotations - -import asyncio -import time - -import click -import httpx -from rich.console import Console -from rich.table import Table - -console = Console() - -# Known domains per cluster -CLUSTER_DOMAINS: dict[str, list[str]] = { - "hanzo": [ - "billing.hanzo.ai", - "console.hanzo.ai", - "api.hanzo.ai", - "hanzo.chat", - "kms.hanzo.ai", - "models.hanzo.ai", - "platform.hanzo.ai", - "pricing.hanzo.ai", - "status.hanzo.ai", - "analytics.hanzo.ai", - "hanzo.id", - "hanzo.bot", - "mpc.hanzo.ai", - "cloud.hanzo.ai", - "s3.hanzo.ai", - "search.hanzo.ai", - "gateway.hanzo.ai", - "s3.hanzo.ai", - ], - "adnexus": [ - "ad.nexus", - "api.ad.nexus", - "api-ai.ad.nexus", - "dsp.ad.nexus", - "ssp.ad.nexus", - "docs.ad.nexus", - "grafana.ad.nexus", - "id.ad.nexus", - ], - "lux": [ - "lux.network", - "lux.id", - ], - "zoo": [ - "zoo.ngo", - "zips.zoo.ngo", - ], -} - - -async def _check_domain( - client: httpx.AsyncClient, domain: str -) -> tuple[str, int, float, str]: - """Check a single domain. Returns (domain, status, latency_ms, error).""" - url = f"https://{domain}/" - t0 = time.monotonic() - try: - resp = await client.get(url, follow_redirects=True) - latency = (time.monotonic() - t0) * 1000 - return domain, resp.status_code, latency, "" - except httpx.TimeoutException: - latency = (time.monotonic() - t0) * 1000 - return domain, 0, latency, "timeout" - except httpx.ConnectError as e: - latency = (time.monotonic() - t0) * 1000 - return domain, 0, latency, f"connect: {e}" - except Exception as e: - latency = (time.monotonic() - t0) * 1000 - return domain, 0, latency, str(e) - - -async def _run_checks( - domains: list[str], timeout: float -) -> list[tuple[str, int, float, str]]: - """Run health checks concurrently against all domains.""" - async with httpx.AsyncClient( - timeout=httpx.Timeout(timeout), - verify=False, # Some services may have cert issues - headers={"User-Agent": "hanzo-cli/health"}, - ) as client: - tasks = [_check_domain(client, d) for d in domains] - return await asyncio.gather(*tasks) - - -def _status_style(code: int, error: str) -> str: - """Return rich style string for a status code.""" - if error: - return "bold red" - if 200 <= code < 300: - return "bold green" - if 300 <= code < 400: - return "yellow" - if 400 <= code < 500: - return "yellow" - return "bold red" - - -def _status_text(code: int, error: str) -> str: - """Return display text for status.""" - if error: - return error[:30] - return str(code) - - -@click.command("health") -@click.option( - "--cluster", - "-c", - default=None, - help="Cluster name (default: current cluster).", -) -@click.option( - "--timeout", - "-t", - default=15.0, - type=float, - help="HTTP timeout in seconds (default: 15).", -) -def health(cluster: str | None, timeout: float) -> None: - """Run HTTP health checks against cluster services. - - Checks all known domains for the specified cluster (or the current - active cluster) and displays a table with status codes and latency. - """ - # Resolve cluster name - if cluster is None: - from hanzo_cli.k8s.clusters import get_current_cluster - - cluster, _ = get_current_cluster() - - domains = CLUSTER_DOMAINS.get(cluster, []) - if not domains: - available = ", ".join(sorted(CLUSTER_DOMAINS.keys())) - console.print( - f"[yellow]No domains configured for cluster '{cluster}'.[/yellow]" - ) - console.print(f"Known clusters with domains: {available}") - return - - console.print( - f"Checking [cyan]{len(domains)}[/cyan] domains for " - f"[bold]{cluster}[/bold] (timeout: {timeout}s)...\n" - ) - - # Run async checks - results = asyncio.run(_run_checks(domains, timeout)) - - # Sort: errors first, then by status code descending, then by domain - results.sort(key=lambda r: (r[3] == "", r[1], r[0])) - - # Build table - table = Table(title=f"Health โ€” {cluster}") - table.add_column("Domain", style="cyan", min_width=25) - table.add_column("Status", justify="center", min_width=8) - table.add_column("Latency", justify="right", min_width=10) - table.add_column("Note", style="dim") - - ok_count = 0 - for domain, code, latency_ms, error in results: - style = _status_style(code, error) - status_text = _status_text(code, error) - - if latency_ms >= 5000: - latency_str = f"[red]{latency_ms:.0f}ms[/red]" - elif latency_ms >= 1000: - latency_str = f"[yellow]{latency_ms:.0f}ms[/yellow]" - else: - latency_str = f"{latency_ms:.0f}ms" - - note = "" - if error: - note = error[:50] - elif code >= 400: - note = "client/server error" - - if not error and 200 <= code < 400: - ok_count += 1 - - table.add_row(domain, f"[{style}]{status_text}[/{style}]", latency_str, note) - - console.print(table) - - total = len(results) - if ok_count == total: - console.print(f"\n[bold green]All {total} services healthy.[/bold green]") - else: - console.print( - f"\n[bold]{ok_count}/{total}[/bold] healthy, " - f"[red]{total - ok_count} issues[/red]" - ) diff --git a/pkg/hanzo-cli/hanzo_cli/k8s/services.py b/pkg/hanzo-cli/hanzo_cli/k8s/services.py deleted file mode 100644 index 33dbd245f..000000000 --- a/pkg/hanzo-cli/hanzo_cli/k8s/services.py +++ /dev/null @@ -1,239 +0,0 @@ -"""Service status and rollout management for Hanzo K8s CLI. - -Uses kubectl to query pod and deployment status and displays results -in rich tables. - -Usage: - hanzo k8s services # List pods in default namespace - hanzo k8s services --namespace kube-system - hanzo k8s rollout # Restart a deployment -""" - -from __future__ import annotations - -import json -import shutil -import subprocess -import sys -from datetime import datetime, timezone - -import click -from rich.console import Console -from rich.table import Table - -console = Console() - - -def _find_kubectl() -> str: - """Locate kubectl or exit.""" - path = shutil.which("kubectl") - if not path: - console.print( - "[red]kubectl not found.[/red] " - "Install: https://kubernetes.io/docs/tasks/tools/" - ) - sys.exit(1) - return path - - -def _kubectl_json(args: list[str]) -> dict | list: - """Run kubectl with -o json and return parsed output.""" - kubectl = _find_kubectl() - cmd = [kubectl] + args + ["-o", "json"] - result = subprocess.run(cmd, capture_output=True, text=True) - if result.returncode != 0: - err = result.stderr.strip() - raise click.ClickException(f"kubectl failed: {err}") - if not result.stdout.strip(): - return {} - return json.loads(result.stdout) - - -def _parse_age(timestamp: str) -> str: - """Convert an ISO timestamp to a human-readable age string.""" - try: - created = datetime.fromisoformat(timestamp.replace("Z", "+00:00")) - delta = datetime.now(timezone.utc) - created - total_seconds = int(delta.total_seconds()) - - if total_seconds < 60: - return f"{total_seconds}s" - if total_seconds < 3600: - return f"{total_seconds // 60}m" - if total_seconds < 86400: - return f"{total_seconds // 3600}h" - days = total_seconds // 86400 - if days < 30: - return f"{days}d" - return f"{days // 30}mo" - except (ValueError, TypeError): - return "?" - - -def _status_style(phase: str) -> str: - """Return rich style for a pod phase.""" - phase_lower = phase.lower() - if phase_lower == "running": - return "green" - if phase_lower in ("pending", "containercreating"): - return "yellow" - if "error" in phase_lower or "crash" in phase_lower or "backoff" in phase_lower: - return "red" - if phase_lower in ("succeeded", "completed"): - return "cyan" - return "white" - - -@click.command("services") -@click.option( - "--namespace", - "-n", - default=None, - help="Kubernetes namespace (default: current cluster namespace).", -) -def services(namespace: str | None) -> None: - """Show service status from pod information. - - Lists pods grouped by service (app label), showing replica counts, - status, age, and restart counts. - """ - if namespace is None: - from hanzo_cli.k8s.clusters import get_current_cluster - - _, cluster_info = get_current_cluster() - namespace = cluster_info.get("namespace", "hanzo") - - try: - data = _kubectl_json(["get", "pods", "-n", namespace]) - except click.ClickException as e: - console.print(f"[red]{e.message}[/red]") - return - - items = data.get("items", []) - if not items: - console.print(f"[yellow]No pods found in namespace '{namespace}'.[/yellow]") - return - - # Group pods by service name (app label) - svc_map: dict[str, list[dict]] = {} - for pod in items: - labels = pod.get("metadata", {}).get("labels", {}) - svc_name = ( - labels.get("app") - or labels.get("app.kubernetes.io/name") - or labels.get("name") - or pod.get("metadata", {}).get("name", "unknown") - ) - svc_map.setdefault(svc_name, []).append(pod) - - table = Table(title=f"Services โ€” {namespace}") - table.add_column("Service", style="cyan", min_width=20) - table.add_column("Ready", justify="center", min_width=8) - table.add_column("Status", justify="center", min_width=14) - table.add_column("Age", justify="right", min_width=6) - table.add_column("Restarts", justify="right", min_width=9) - - for svc_name in sorted(svc_map.keys()): - pods = svc_map[svc_name] - total = len(pods) - ready = 0 - total_restarts = 0 - worst_phase = "Running" - - for pod in pods: - status = pod.get("status", {}) - phase = status.get("phase", "Unknown") - - # Check container statuses for more detail - container_statuses = status.get("containerStatuses", []) - pod_ready = True - for cs in container_statuses: - total_restarts += cs.get("restartCount", 0) - if not cs.get("ready", False): - pod_ready = False - # Detect CrashLoopBackOff - waiting = cs.get("state", {}).get("waiting", {}) - reason = waiting.get("reason", "") - if reason: - phase = reason - - if pod_ready and phase == "Running": - ready += 1 - - # Track worst phase for display - if phase != "Running": - worst_phase = phase - - # Pick display phase - if ready == total: - display_phase = "Running" - elif ready > 0: - display_phase = f"{worst_phase}" if worst_phase != "Running" else "Partial" - else: - display_phase = worst_phase - - style = _status_style(display_phase) - ready_str = f"{ready}/{total}" - - # Age from oldest pod - oldest = min( - ( - p.get("metadata", {}).get("creationTimestamp", "") - for p in pods - ), - default="", - ) - age_str = _parse_age(oldest) if oldest else "?" - - restart_style = "red" if total_restarts > 10 else "yellow" if total_restarts > 0 else "dim" - - table.add_row( - svc_name, - ready_str, - f"[{style}]{display_phase}[/{style}]", - age_str, - f"[{restart_style}]{total_restarts}[/{restart_style}]", - ) - - console.print(table) - console.print(f"\n{len(svc_map)} service(s), {len(items)} pod(s)") - - -@click.command("rollout") -@click.argument("deployment") -@click.option( - "--namespace", - "-n", - default=None, - help="Kubernetes namespace (default: current cluster namespace).", -) -def rollout(deployment: str, namespace: str | None) -> None: - """Trigger a rolling restart of a deployment. - - Runs `kubectl rollout restart deployment/` in the specified - namespace. - """ - if namespace is None: - from hanzo_cli.k8s.clusters import get_current_cluster - - _, cluster_info = get_current_cluster() - namespace = cluster_info.get("namespace", "hanzo") - - kubectl = _find_kubectl() - resource = deployment if "/" in deployment else f"deployment/{deployment}" - - console.print( - f"Restarting [cyan]{resource}[/cyan] in namespace [cyan]{namespace}[/cyan]..." - ) - - result = subprocess.run( - [kubectl, "rollout", "restart", resource, "-n", namespace], - capture_output=True, - text=True, - ) - - if result.returncode != 0: - console.print(f"[red]Failed:[/red] {result.stderr.strip()}") - raise SystemExit(1) - - console.print(f"[green]{result.stdout.strip()}[/green]") diff --git a/pkg/hanzo-cli/hanzo_cli/kms/__init__.py b/pkg/hanzo-cli/hanzo_cli/kms/__init__.py deleted file mode 100644 index 96e470387..000000000 --- a/pkg/hanzo-cli/hanzo_cli/kms/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Hanzo CLI โ€” KMS subcommands.""" diff --git a/pkg/hanzo-cli/hanzo_cli/kms/commands.py b/pkg/hanzo-cli/hanzo_cli/kms/commands.py deleted file mode 100644 index 67aab3d32..000000000 --- a/pkg/hanzo-cli/hanzo_cli/kms/commands.py +++ /dev/null @@ -1,239 +0,0 @@ -"""Hanzo CLI โ€” KMS subcommands for secret management. - -Usage: - hanzo kms list โ€” List secrets - hanzo kms get โ€” Get secret value - hanzo kms set โ€” Create or update secret - hanzo kms delete โ€” Delete a secret - hanzo kms inject โ€” Print export statements -""" - -from __future__ import annotations - -import click -from rich.console import Console -from rich.table import Table - -console = Console() - - -def _get_kms_client(): - """Build a KMS client from environment or stored auth.""" - import os - - from hanzo_kms import ClientSettings, KMSClient - - # KMS has its own auth (Universal Auth with client_id/secret) - # Check for dedicated KMS env vars first - kms_url = os.getenv("HANZO_KMS_URL", "https://kms.hanzo.ai") - client_id = os.getenv("HANZO_KMS_CLIENT_ID", "") - client_secret = os.getenv("HANZO_KMS_CLIENT_SECRET", "") - - if client_id and client_secret: - from hanzo_kms import AuthenticationOptions, UniversalAuthMethod - - settings = ClientSettings( - site_url=kms_url, - auth=AuthenticationOptions( - universal_auth=UniversalAuthMethod( - client_id=client_id, - client_secret=client_secret, - ) - ), - ) - return KMSClient(settings=settings) - - # Fall back to default env-based construction - return KMSClient() - - -@click.group() -def kms() -> None: - """Manage secrets via Hanzo KMS.""" - - -@kms.command("list") -@click.argument("project") -@click.argument("env") -@click.option("--path", default="/", help="Secret path prefix.") -def list_secrets(project: str, env: str, path: str) -> None: - """List all secrets in a project environment.""" - client = _get_kms_client() - try: - secrets = client.list_secrets( - project_id=project, - environment=env, - path=path, - ) - finally: - client.close() - - if not secrets: - console.print("[yellow]No secrets found.[/yellow]") - return - - table = Table(title=f"Secrets: {project} / {env} ({path})") - table.add_column("Key", style="cyan") - table.add_column("Value", style="dim") - table.add_column("Version", style="white", justify="right") - table.add_column("Updated", style="white") - - for s in secrets: - # Mask the value โ€” show first 4 chars then *** - val = s.secret_value - masked = f"{val[:4]}***" if len(val) > 4 else "***" - updated = str(s.updated_at)[:19] if s.updated_at else "โ€”" - table.add_row(s.secret_key, masked, str(s.version), updated) - - console.print(table) - - -@kms.command("get") -@click.argument("project") -@click.argument("env") -@click.argument("name") -@click.option("--path", default="/", help="Secret path prefix.") -@click.option("--reveal", is_flag=True, help="Show full value (default: masked).") -def get_secret(project: str, env: str, name: str, path: str, reveal: bool) -> None: - """Get a single secret's value.""" - client = _get_kms_client() - try: - secret = client.get_secret( - project_id=project, - environment=env, - secret_name=name, - path=path, - ) - finally: - client.close() - - if reveal: - console.print(secret.secret_value) - else: - val = secret.secret_value - masked = f"{val[:4]}***" if len(val) > 4 else "***" - table = Table(title=f"{name}") - table.add_column("Field", style="cyan") - table.add_column("Value", style="white") - table.add_row("Key", secret.secret_key) - table.add_row("Value", masked) - table.add_row("Version", str(secret.version)) - table.add_row("Type", secret.type) - table.add_row("Environment", secret.environment) - if secret.secret_comment: - table.add_row("Comment", secret.secret_comment) - console.print(table) - - -@kms.command("set") -@click.argument("project") -@click.argument("env") -@click.argument("name") -@click.argument("value", required=False) -@click.option("--path", default="/", help="Secret path prefix.") -@click.option("--comment", default=None, help="Secret comment.") -def set_secret( - project: str, - env: str, - name: str, - value: str | None, - path: str, - comment: str | None, -) -> None: - """Create or update a secret. Prompts for value if not given.""" - if not value: - value = click.prompt("Secret value", hide_input=True) - - client = _get_kms_client() - try: - # Try update first, create if it doesn't exist - try: - secret = client.update_secret( - project_id=project, - environment=env, - secret_name=name, - secret_value=value, - ) - console.print(f"[green]Updated[/green] {name} (v{secret.version})") - except Exception: - kwargs = {} - if comment: - kwargs["secret_comment"] = comment - secret = client.create_secret( - project_id=project, - environment=env, - secret_name=name, - secret_value=value, - **kwargs, - ) - console.print(f"[green]Created[/green] {name}") - finally: - client.close() - - -@kms.command("delete") -@click.argument("project") -@click.argument("env") -@click.argument("name") -@click.option("--path", default="/", help="Secret path prefix.") -@click.option("--yes", "-y", is_flag=True, help="Skip confirmation.") -def delete_secret(project: str, env: str, name: str, path: str, yes: bool) -> None: - """Delete a secret.""" - if not yes: - click.confirm(f"Delete secret '{name}' from {project}/{env}?", abort=True) - - client = _get_kms_client() - try: - client.delete_secret( - project_id=project, - environment=env, - secret_name=name, - path=path, - ) - finally: - client.close() - - console.print(f"[green]Deleted[/green] {name}") - - -@kms.command("inject") -@click.argument("project") -@click.argument("env") -@click.option("--path", default="/", help="Secret path prefix.") -@click.option( - "--format", "fmt", type=click.Choice(["export", "dotenv", "json"]), default="export" -) -def inject_secrets(project: str, env: str, path: str, fmt: str) -> None: - """Print secrets as export statements, dotenv, or JSON. - - Pipe to `eval` or redirect to .env file: - hanzo kms inject myproject production | source /dev/stdin - hanzo kms inject myproject production --format dotenv > .env - """ - import json - - client = _get_kms_client() - try: - secrets = client.list_secrets( - project_id=project, - environment=env, - path=path, - ) - finally: - client.close() - - if not secrets: - return - - if fmt == "export": - for s in secrets: - # Shell-safe quoting - val = s.secret_value.replace("'", "'\\''") - click.echo(f"export {s.secret_key}='{val}'") - elif fmt == "dotenv": - for s in secrets: - val = s.secret_value.replace('"', '\\"') - click.echo(f'{s.secret_key}="{val}"') - elif fmt == "json": - data = {s.secret_key: s.secret_value for s in secrets} - click.echo(json.dumps(data, indent=2)) diff --git a/pkg/hanzo-cli/hanzo_cli/paas/__init__.py b/pkg/hanzo-cli/hanzo_cli/paas/__init__.py deleted file mode 100644 index e5357a334..000000000 --- a/pkg/hanzo-cli/hanzo_cli/paas/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Hanzo CLI โ€” PaaS subcommands.""" diff --git a/pkg/hanzo-cli/hanzo_cli/paas/client.py b/pkg/hanzo-cli/hanzo_cli/paas/client.py deleted file mode 100644 index fbb76d600..000000000 --- a/pkg/hanzo-cli/hanzo_cli/paas/client.py +++ /dev/null @@ -1,367 +0,0 @@ -"""Thin HTTP client for the Hanzo PaaS REST API. - -Wraps the /v1/* endpoints exposed by the platform service -(paas/platform/routes/*). - -Auth flow: -1. ``hanzo login`` stores an IAM token at ~/.hanzo/auth/token.json -2. On first PaaS call we exchange that IAM token for a PaaS session - via POST /v1/auth/login {provider:"hanzo", accessToken:""} -3. PaaS returns {at, rt} โ€” we cache these in ~/.hanzo/paas/session.json -4. Subsequent calls use the PaaS ``at`` in the Authorization header - -Environment variables: - HANZO_PAAS_URL โ€” PaaS API base URL (default: https://platform.hanzo.ai) -""" - -from __future__ import annotations - -import json -import os -import sys -import time -from pathlib import Path -from typing import Any - -import click -import httpx - -from hanzo_cli.auth import get_token_info - -DEFAULT_PAAS_URL = "https://platform.hanzo.ai" -SESSION_DIR = Path.home() / ".hanzo" / "paas" -SESSION_FILE = SESSION_DIR / "session.json" - - -def _save_session(data: dict[str, Any]) -> None: - SESSION_DIR.mkdir(parents=True, exist_ok=True) - SESSION_FILE.write_text(json.dumps(data, indent=2)) - SESSION_FILE.chmod(0o600) - - -def _load_session() -> dict[str, Any] | None: - if not SESSION_FILE.exists(): - return None - try: - return json.loads(SESSION_FILE.read_text()) - except (json.JSONDecodeError, OSError): - return None - - -class PaaSClient: - """Lightweight wrapper around the PaaS platform REST API.""" - - def __init__( - self, - base_url: str | None = None, - access_token: str | None = None, - refresh_token: str | None = None, - ): - self._base_url = ( - base_url or os.getenv("HANZO_PAAS_URL", DEFAULT_PAAS_URL) - ).rstrip("/") - self._at = access_token - self._rt = refresh_token - self._http: httpx.Client | None = None - - # -- bootstrap ----------------------------------------------------------- - - @classmethod - def _exchange_iam_token(cls, base_url: str) -> PaaSClient: - """Exchange the stored IAM token for a fresh PaaS session.""" - token_data = get_token_info() - if not token_data or not token_data.get("access_token"): - click.echo( - "Not authenticated. Run 'hanzo login' first.", - err=True, - ) - sys.exit(1) - - iam_token = token_data["access_token"] - - with httpx.Client(base_url=base_url, timeout=30.0) as tmp: - resp = tmp.post( - "/v1/auth/login", - json={ - "provider": "hanzo", - "accessToken": iam_token, - }, - headers={ - "Content-Type": "application/json", - "User-Agent": "hanzo-cli/0.1", - }, - ) - if resp.status_code == 401: - click.echo( - "IAM token rejected by PaaS. Try 'hanzo login' again.", - err=True, - ) - sys.exit(1) - resp.raise_for_status() - data = resp.json() - - at = data.get("at", "") - rt = data.get("rt", "") - - if not at: - click.echo("PaaS login succeeded but no session token returned.", err=True) - sys.exit(1) - - _save_session({"at": at, "rt": rt, "login_time": int(time.time())}) - return cls(base_url=base_url, access_token=at, refresh_token=rt) - - @classmethod - def from_auth(cls) -> PaaSClient: - """Build a client, exchanging the IAM token for a PaaS session. - - 1. Check for a cached PaaS session (validate with authenticated endpoint) - 2. If expired/invalid, exchange the IAM token via POST /v1/auth/login - 3. Cache the PaaS session for reuse - """ - base_url = os.getenv("HANZO_PAAS_URL", DEFAULT_PAAS_URL).rstrip("/") - - # Try cached PaaS session first - session = _load_session() - if session and session.get("at"): - inst = cls( - base_url=base_url, - access_token=session["at"], - refresh_token=session.get("rt"), - ) - # Validate with an authenticated endpoint - try: - resp = inst.http.get("/v1/org") - if resp.status_code != 401: - return inst - except Exception: - pass - inst.close() - - return cls._exchange_iam_token(base_url) - - @property - def http(self) -> httpx.Client: - if self._http is None: - headers: dict[str, str] = { - "User-Agent": "hanzo-cli/0.1", - "Content-Type": "application/json", - } - if self._at: - headers["Authorization"] = self._at - if self._rt: - headers["Refresh-Token"] = self._rt - self._http = httpx.Client( - base_url=self._base_url, - timeout=30.0, - headers=headers, - ) - return self._http - - def close(self) -> None: - if self._http: - self._http.close() - self._http = None - - # -- helpers ------------------------------------------------------------- - - def _reauth(self) -> None: - """Re-exchange the IAM token for a fresh PaaS session.""" - self.close() - fresh = self._exchange_iam_token(self._base_url) - self._at = fresh._at - self._rt = fresh._rt - # _http will be lazily rebuilt on next access - - def _ok(self, resp: httpx.Response, *, _retried: bool = False) -> Any: - # PaaS may return refreshed tokens in headers - new_at = resp.headers.get("Access-Token") - new_rt = resp.headers.get("Refresh-Token") - if new_at: - self._at = new_at - _save_session( - { - "at": new_at, - "rt": new_rt or self._rt or "", - "login_time": int(time.time()), - } - ) - # Rebuild client with new token - self.close() - - # Auto-retry on 401: re-exchange IAM token and replay the request - if resp.status_code == 401 and not _retried: - self._reauth() - # Replay the original request using just the path - req = resp.request - path = req.url.raw_path.decode("ascii") # e.g. /v1/org - retry_resp = self.http.request( - method=req.method, - url=path, - content=req.content if req.content else None, - ) - return self._ok(retry_resp, _retried=True) - - if resp.status_code >= 400: - try: - err = resp.json() - msg = err.get("error", "") - details = err.get("details", "") - fields = err.get("fields", []) - parts = [f"PaaS error {resp.status_code}"] - if msg: - parts.append(msg) - if details: - parts.append(details) - for f in fields: - parts.append(f" {f.get('param', '?')}: {f.get('msg', '')}") - raise click.ClickException("\n".join(parts)) - except (ValueError, KeyError): - pass - resp.raise_for_status() - - if not resp.content or resp.status_code == 204: - return {} - return resp.json() - - # ======================================================================== - # Organizations - # ======================================================================== - - def list_orgs(self) -> list[dict[str, Any]]: - return self._ok(self.http.get("/v1/org")) - - def get_org(self, org_id: str) -> dict[str, Any]: - return self._ok(self.http.get(f"/v1/org/{org_id}")) - - # ======================================================================== - # Projects - # ======================================================================== - - def list_projects(self, org_id: str) -> list[dict[str, Any]]: - return self._ok(self.http.get(f"/v1/org/{org_id}/project")) - - def get_project(self, org_id: str, project_id: str) -> dict[str, Any]: - return self._ok(self.http.get(f"/v1/org/{org_id}/project/{project_id}")) - - # ======================================================================== - # Environments - # ======================================================================== - - def list_envs(self, org_id: str, project_id: str) -> list[dict[str, Any]]: - return self._ok(self.http.get(f"/v1/org/{org_id}/project/{project_id}/env")) - - # ======================================================================== - # Containers (Deployments) - # ======================================================================== - - def _container_base(self, org_id: str, project_id: str, env_id: str) -> str: - return f"/v1/org/{org_id}/project/{project_id}/env/{env_id}/container" - - def list_containers( - self, - org_id: str, - project_id: str, - env_id: str, - ) -> list[dict[str, Any]]: - url = self._container_base(org_id, project_id, env_id) - return self._ok(self.http.get(url)) - - def get_container( - self, - org_id: str, - project_id: str, - env_id: str, - container_id: str, - ) -> dict[str, Any]: - url = f"{self._container_base(org_id, project_id, env_id)}/{container_id}" - return self._ok(self.http.get(url)) - - def create_container( - self, - org_id: str, - project_id: str, - env_id: str, - payload: dict[str, Any], - ) -> dict[str, Any]: - url = self._container_base(org_id, project_id, env_id) - return self._ok(self.http.post(url, json=payload)) - - def update_container( - self, - org_id: str, - project_id: str, - env_id: str, - container_id: str, - payload: dict[str, Any], - ) -> dict[str, Any]: - url = f"{self._container_base(org_id, project_id, env_id)}/{container_id}" - return self._ok(self.http.put(url, json=payload)) - - def delete_container( - self, - org_id: str, - project_id: str, - env_id: str, - container_id: str, - ) -> dict[str, Any]: - url = f"{self._container_base(org_id, project_id, env_id)}/{container_id}" - return self._ok(self.http.delete(url)) - - def redeploy_container( - self, - org_id: str, - project_id: str, - env_id: str, - container_id: str, - ) -> dict[str, Any]: - """Trigger a redeploy by re-PUTting the container config. - - The PaaS API doesn't expose a dedicated /redeploy endpoint. - Updating the container with its current config triggers a rolling restart. - """ - # Fetch current config, then PUT it back to trigger redeployment - container = self.get_container(org_id, project_id, env_id, container_id) - url = f"{self._container_base(org_id, project_id, env_id)}/{container_id}" - return self._ok(self.http.put(url, json=container)) - - def get_container_pods( - self, - org_id: str, - project_id: str, - env_id: str, - container_id: str, - ) -> list[dict[str, Any]]: - url = f"{self._container_base(org_id, project_id, env_id)}/{container_id}/pods" - return self._ok(self.http.get(url)) - - def get_container_logs( - self, - org_id: str, - project_id: str, - env_id: str, - container_id: str, - ) -> Any: - url = f"{self._container_base(org_id, project_id, env_id)}/{container_id}/logs" - return self._ok(self.http.get(url)) - - def get_container_events( - self, - org_id: str, - project_id: str, - env_id: str, - container_id: str, - ) -> list[dict[str, Any]]: - url = ( - f"{self._container_base(org_id, project_id, env_id)}/{container_id}/events" - ) - return self._ok(self.http.get(url)) - - # ======================================================================== - # Cluster - # ======================================================================== - - def cluster_info(self) -> dict[str, Any]: - return self._ok(self.http.get("/v1/cluster/info")) - - def cluster_templates(self) -> list[dict[str, Any]]: - return self._ok(self.http.get("/v1/cluster/templates")) diff --git a/pkg/hanzo-cli/hanzo_cli/paas/commands.py b/pkg/hanzo-cli/hanzo_cli/paas/commands.py deleted file mode 100644 index 0a4b7600a..000000000 --- a/pkg/hanzo-cli/hanzo_cli/paas/commands.py +++ /dev/null @@ -1,827 +0,0 @@ -"""Hanzo CLI โ€” PaaS subcommands for platform management. - -Context is sticky: run ``hanzo paas use --org X --project Y --env Z`` -once, then subsequent commands remember. CLI flags always override. - -Usage: - hanzo paas use --org O --project P --env E - hanzo paas orgs - hanzo paas projects - hanzo paas envs - hanzo paas deploy up [--config hanzo.toml] - hanzo paas deploy list - hanzo paas deploy create --repo [--branch main] - hanzo paas deploy create --config hanzo.toml - hanzo paas deploy status - hanzo paas deploy logs - hanzo paas deploy redeploy - hanzo paas deploy env [KEY=VAL ...] - hanzo paas deploy delete -""" - -from __future__ import annotations - -import json -from typing import Any - -import click -from rich.console import Console -from rich.table import Table - -from hanzo_cli.paas.client import PaaSClient -from hanzo_cli.paas.context import clear_context, load_context, resolve, save_context - -console = Console() - - -# โ”€โ”€ shared option decorators โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def _org_option(fn): - return click.option( - "--org", default=None, help="Organization ID (or from context)." - )(fn) - - -def _project_option(fn): - return click.option( - "--project", default=None, help="Project ID (or from context)." - )(fn) - - -def _env_option(fn): - return click.option( - "--env", "env_id", default=None, help="Environment ID (or from context)." - )(fn) - - -def _require(label: str, value: str | None) -> str: - """Ensure a value is set; exit with a helpful message if not.""" - if not value: - console.print( - f"[red]{label} is required.[/red] Pass it via flag or run 'hanzo paas use'." - ) - raise SystemExit(1) - return value - - -def _find_container( - client: PaaSClient, - org_id: str, - project_id: str, - env_id: str, - name: str, -) -> dict[str, Any]: - """Look up a container by name within the current context.""" - containers = client.list_containers(org_id, project_id, env_id) - # list may be wrapped in {"data": [...]} or raw [...] - items = ( - containers - if isinstance(containers, list) - else containers.get("data", containers) - ) - for c in items: - if c.get("iid") == name or c.get("name") == name or c.get("slug") == name: - return c - console.print(f"[red]Container '{name}' not found.[/red]") - raise SystemExit(1) - - -# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -# Root group -# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - - -@click.group() -def paas() -> None: - """Manage the Hanzo PaaS โ€” orgs, projects, deployments.""" - - -# โ”€โ”€ context โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@paas.command("use") -@click.option("--org", default=None, help="Organization ID to remember.") -@click.option("--project", default=None, help="Project ID to remember.") -@click.option("--env", "env_id", default=None, help="Environment ID to remember.") -@click.option("--clear", is_flag=True, help="Clear stored context.") -def use_context( - org: str | None, project: str | None, env_id: str | None, clear: bool -) -> None: - """Set the default org / project / environment context.""" - if clear: - clear_context() - console.print("Context cleared.") - return - - ctx = load_context() - if org: - ctx["org_id"] = org - if project: - ctx["project_id"] = project - if env_id: - ctx["env_id"] = env_id - - save_context(ctx) - - table = Table(title="PaaS Context") - table.add_column("Key", style="cyan") - table.add_column("Value", style="white") - table.add_row("org", ctx.get("org_id", "โ€”")) - table.add_row("project", ctx.get("project_id", "โ€”")) - table.add_row("env", ctx.get("env_id", "โ€”")) - console.print(table) - - -@paas.command("context") -def show_context() -> None: - """Show current PaaS context.""" - ctx = load_context() - if not ctx: - console.print("[yellow]No context set.[/yellow] Run 'hanzo paas use'.") - return - - table = Table(title="PaaS Context") - table.add_column("Key", style="cyan") - table.add_column("Value", style="white") - table.add_row("org", ctx.get("org_id", "โ€”")) - table.add_row("project", ctx.get("project_id", "โ€”")) - table.add_row("env", ctx.get("env_id", "โ€”")) - console.print(table) - - -# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -# Orgs / Projects / Environments -# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - - -@paas.command("orgs") -def list_orgs() -> None: - """List organizations.""" - client = PaaSClient.from_auth() - try: - data = client.list_orgs() - finally: - client.close() - - items = data if isinstance(data, list) else data.get("data", data) - if not items: - console.print("[yellow]No organizations.[/yellow]") - return - - table = Table(title=f"Organizations ({len(items)})") - table.add_column("ID", style="dim") - table.add_column("Name", style="cyan") - table.add_column("Slug", style="white") - - for o in items: - table.add_row( - str(o.get("_id", o.get("id", "โ€”"))), - o.get("name", "โ€”"), - o.get("iid", "โ€”"), - ) - console.print(table) - - -@paas.command("projects") -@_org_option -def list_projects(org: str | None) -> None: - """List projects in an organization.""" - org_id, _, _ = resolve(org, None, None) - org_id = _require("--org", org_id) - - client = PaaSClient.from_auth() - try: - data = client.list_projects(org_id) - finally: - client.close() - - items = data if isinstance(data, list) else data.get("data", data) - if not items: - console.print("[yellow]No projects.[/yellow]") - return - - table = Table(title=f"Projects ({len(items)})") - table.add_column("ID", style="dim") - table.add_column("Name", style="cyan") - table.add_column("Slug", style="white") - - for p in items: - table.add_row( - str(p.get("_id", p.get("id", "โ€”"))), - p.get("name", "โ€”"), - p.get("iid", "โ€”"), - ) - console.print(table) - - -@paas.command("envs") -@_org_option -@_project_option -def list_envs(org: str | None, project: str | None) -> None: - """List environments in a project.""" - org_id, project_id, _ = resolve(org, project, None) - org_id = _require("--org", org_id) - project_id = _require("--project", project_id) - - client = PaaSClient.from_auth() - try: - data = client.list_envs(org_id, project_id) - finally: - client.close() - - items = data if isinstance(data, list) else data.get("data", data) - if not items: - console.print("[yellow]No environments.[/yellow]") - return - - table = Table(title=f"Environments ({len(items)})") - table.add_column("ID", style="dim") - table.add_column("Name", style="cyan") - table.add_column("Slug", style="white") - - for e in items: - table.add_row( - str(e.get("_id", e.get("id", "โ€”"))), - e.get("name", "โ€”"), - e.get("iid", "โ€”"), - ) - console.print(table) - - -# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -# Deploy sub-group -# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - - -@paas.group("deploy") -def deploy() -> None: - """Manage container deployments.""" - - -# โ”€โ”€ deploy list โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@deploy.command("list") -@_org_option -@_project_option -@_env_option -def deploy_list(org: str | None, project: str | None, env_id: str | None) -> None: - """List deployed containers.""" - org_id, project_id, env_id = resolve(org, project, env_id) - org_id = _require("--org", org_id) - project_id = _require("--project", project_id) - env_id = _require("--env", env_id) - - client = PaaSClient.from_auth() - try: - data = client.list_containers(org_id, project_id, env_id) - finally: - client.close() - - items = data if isinstance(data, list) else data.get("data", data) - if not items: - console.print("[yellow]No containers.[/yellow]") - return - - table = Table(title=f"Containers ({len(items)})") - table.add_column("Name", style="cyan") - table.add_column("Type", style="white") - table.add_column("Repo", style="dim") - table.add_column("Status", style="green") - - for c in items: - name = c.get("iid", c.get("name", "โ€”")) - ctype = c.get("type", "โ€”") - repo = c.get("repo", {}) - repo_str = repo.get("name", "โ€”") if isinstance(repo, dict) else "โ€”" - status = c.get("pipelineStatus", c.get("status", "โ€”")) - if isinstance(status, dict): - status = status.get("status", "โ€”") - table.add_row(name, ctype, repo_str, str(status)) - - console.print(table) - - -# โ”€โ”€ deploy create โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@deploy.command("create") -@click.argument("name") -@click.option( - "--image", - default=None, - help="Docker image URL (e.g. nginx:latest, ghcr.io/org/app:v1).", -) -@click.option("--repo", default=None, help="Git repository (owner/repo).") -@click.option("--branch", default="main", help="Branch to deploy (with --repo).") -@click.option( - "--dockerfile", default="Dockerfile", help="Dockerfile path (with --repo)." -) -@click.option("--port", type=int, default=3000, help="Container port.") -@click.option("--replicas", type=int, default=1, help="Desired replica count.") -@click.option( - "--type", - "deploy_type", - default="deployment", - type=click.Choice(["deployment", "statefulset", "cronjob"]), -) -@click.option( - "--config", - "config_path", - default=None, - type=click.Path(exists=True), - help="Path to hanzo.toml deployment manifest.", -) -@_org_option -@_project_option -@_env_option -def deploy_create( - name: str, - image: str | None, - repo: str | None, - branch: str, - dockerfile: str, - port: int, - replicas: int, - deploy_type: str, - config_path: str | None, - org: str | None, - project: str | None, - env_id: str | None, -) -> None: - """Create a new container deployment from a Docker image, git repo, or hanzo.toml. - - Examples: - hanzo paas deploy create myapp --image nginx:latest --port 80 - hanzo paas deploy create myapp --image ghcr.io/org/app:v1 --port 8080 - hanzo paas deploy create myapp --repo hanzoai/app --branch main - hanzo paas deploy create myapp --config hanzo.toml - """ - if config_path: - from hanzo_cli.paas.config import config_to_payload, load_config - - cfg = load_config(config_path) - payload = config_to_payload(cfg) - # CLI name argument overrides config name - payload["name"] = name - - org_id, project_id, env_id = resolve(org, project, env_id) - org_id = _require("--org", org_id) - project_id = _require("--project", project_id) - env_id = _require("--env", env_id) - - client = PaaSClient.from_auth() - try: - data = client.create_container(org_id, project_id, env_id, payload) - finally: - client.close() - - console.print(f"[green]Created container '{name}' from {config_path}[/green]") - cid = data.get("_id", data.get("id", "")) - if cid: - console.print(f"ID: {cid}") - return - - if not image and not repo: - console.print("[red]Specify --image, --repo, or --config.[/red]") - raise SystemExit(1) - - org_id, project_id, env_id = resolve(org, project, env_id) - org_id = _require("--org", org_id) - project_id = _require("--project", project_id) - env_id = _require("--env", env_id) - - # Resolve public registry ID for image deployments - registry_id = None - if image: - client_pre = PaaSClient.from_auth() - try: - regs = client_pre.http.get("/v1/registry") - regs.raise_for_status() - for reg in regs.json(): - if reg.get("type") == "Public": - registry_id = str(reg["_id"]) - break - except Exception: - pass - finally: - client_pre.close() - - payload: dict[str, Any] = { - "name": name, - "type": deploy_type, - "networking": { - "containerPort": port, - "ingress": {"enabled": False}, - "customDomain": {"enabled": False}, - "tcpProxy": {"enabled": False}, - }, - "podConfig": { - "restartPolicy": "Always", - "cpuRequest": 100, - "cpuRequestType": "millicores", - "cpuLimit": 200, - "cpuLimitType": "millicores", - "memoryRequest": 128, - "memoryRequestType": "mebibyte", - "memoryLimit": 256, - "memoryLimitType": "mebibyte", - }, - "storageConfig": {"enabled": False}, - "probes": { - "startup": {"enabled": False}, - "readiness": {"enabled": False}, - "liveness": {"enabled": False}, - }, - } - - if deploy_type == "deployment": - payload["deploymentConfig"] = { - "desiredReplicas": replicas, - "strategy": "RollingUpdate", - "rollingUpdate": { - "maxSurge": 30, - "maxSurgeType": "percentage", - "maxUnavailable": 0, - "maxUnavailableType": "number", - }, - "revisionHistoryLimit": 10, - "cpuMetric": {"enabled": False}, - "memoryMetric": {"enabled": False}, - } - - if image: - payload["repoOrRegistry"] = "registry" - reg_obj: dict[str, Any] = {"imageUrl": image} - if registry_id: - reg_obj["registryId"] = registry_id - payload["registry"] = reg_obj - else: - payload["repoOrRegistry"] = "repo" - payload["repo"] = { - "name": repo, - "branch": branch, - "dockerfile": dockerfile, - } - - client = PaaSClient.from_auth() - try: - data = client.create_container(org_id, project_id, env_id, payload) - finally: - client.close() - - console.print(f"[green]Created container '{name}'[/green]") - cid = data.get("_id", data.get("id", "")) - if cid: - console.print(f"ID: {cid}") - - -# โ”€โ”€ deploy status โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@deploy.command("status") -@click.argument("name") -@_org_option -@_project_option -@_env_option -def deploy_status( - name: str, - org: str | None, - project: str | None, - env_id: str | None, -) -> None: - """Show container status and pods.""" - org_id, project_id, env_id = resolve(org, project, env_id) - org_id = _require("--org", org_id) - project_id = _require("--project", project_id) - env_id = _require("--env", env_id) - - client = PaaSClient.from_auth() - try: - container = _find_container(client, org_id, project_id, env_id, name) - cid = str(container.get("_id", container.get("id"))) - - # Container info - table = Table(title=f"Container: {name}") - table.add_column("Field", style="cyan") - table.add_column("Value", style="white") - table.add_row("ID", cid) - table.add_row("Type", container.get("type", "โ€”")) - table.add_row("Pipeline", str(container.get("pipelineStatus", "โ€”"))) - - status = container.get("status", {}) - if isinstance(status, dict): - table.add_row("Desired Replicas", str(status.get("desiredReplicas", "โ€”"))) - table.add_row("Ready Replicas", str(status.get("readyReplicas", "โ€”"))) - table.add_row( - "Available Replicas", str(status.get("availableReplicas", "โ€”")) - ) - console.print(table) - - # Pods - try: - pods_data = client.get_container_pods(org_id, project_id, env_id, cid) - pods = ( - pods_data if isinstance(pods_data, list) else pods_data.get("data", []) - ) - if pods: - pod_table = Table(title="Pods") - pod_table.add_column("Name", style="cyan") - pod_table.add_column("Status", style="green") - pod_table.add_column("Restarts", style="yellow", justify="right") - for p in pods: - pod_table.add_row( - p.get("name", "โ€”"), - p.get("status", "โ€”"), - str(p.get("restartCount", 0)), - ) - console.print(pod_table) - except Exception: - pass # pods endpoint may not be available - finally: - client.close() - - -# โ”€โ”€ deploy logs โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@deploy.command("logs") -@click.argument("name") -@_org_option -@_project_option -@_env_option -def deploy_logs( - name: str, - org: str | None, - project: str | None, - env_id: str | None, -) -> None: - """Show container logs.""" - org_id, project_id, env_id = resolve(org, project, env_id) - org_id = _require("--org", org_id) - project_id = _require("--project", project_id) - env_id = _require("--env", env_id) - - client = PaaSClient.from_auth() - try: - container = _find_container(client, org_id, project_id, env_id, name) - cid = str(container.get("_id", container.get("id"))) - logs_data = client.get_container_logs(org_id, project_id, env_id, cid) - - # PaaS returns {pods: [...], logs: [{podName, logs: [line, ...]}, ...]} - if isinstance(logs_data, dict) and "logs" in logs_data: - pod_logs = logs_data["logs"] - if isinstance(pod_logs, list): - for pod in pod_logs: - if isinstance(pod, dict): - pod_name = pod.get("podName", "?") - lines = pod.get("logs", []) - if len(pod_logs) > 1: - click.echo(click.style(f"โ”€โ”€โ”€ {pod_name} โ”€โ”€โ”€", fg="cyan")) - for line in lines: - if line: - click.echo(line) - else: - click.echo(str(pod)) - else: - click.echo(str(pod_logs)) - elif isinstance(logs_data, str): - click.echo(logs_data) - elif isinstance(logs_data, list): - for line in logs_data: - click.echo(line) - else: - click.echo(json.dumps(logs_data, indent=2)) - finally: - client.close() - - -# โ”€โ”€ deploy redeploy โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@deploy.command("redeploy") -@click.argument("name") -@_org_option -@_project_option -@_env_option -def deploy_redeploy( - name: str, - org: str | None, - project: str | None, - env_id: str | None, -) -> None: - """Trigger a rebuild and redeploy of a container.""" - org_id, project_id, env_id = resolve(org, project, env_id) - org_id = _require("--org", org_id) - project_id = _require("--project", project_id) - env_id = _require("--env", env_id) - - client = PaaSClient.from_auth() - try: - container = _find_container(client, org_id, project_id, env_id, name) - cid = str(container.get("_id", container.get("id"))) - client.redeploy_container(org_id, project_id, env_id, cid) - finally: - client.close() - - console.print(f"[green]Redeployment triggered for '{name}'.[/green]") - - -# โ”€โ”€ deploy env โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@deploy.command("env") -@click.argument("name") -@click.argument("vars", nargs=-1) -@_org_option -@_project_option -@_env_option -def deploy_env( - name: str, - vars: tuple[str, ...], - org: str | None, - project: str | None, - env_id: str | None, -) -> None: - """Show or set environment variables on a container. - - Without arguments, shows current vars. With KEY=VAL pairs, sets them. - - Examples: - hanzo paas deploy env myapp - hanzo paas deploy env myapp DATABASE_URL=postgres://... NODE_ENV=production - """ - org_id, project_id, env_id = resolve(org, project, env_id) - org_id = _require("--org", org_id) - project_id = _require("--project", project_id) - env_id = _require("--env", env_id) - - client = PaaSClient.from_auth() - try: - container = _find_container(client, org_id, project_id, env_id, name) - cid = str(container.get("_id", container.get("id"))) - - if not vars: - # Show current vars - variables = container.get("variables", []) - if not variables: - console.print("[yellow]No environment variables set.[/yellow]") - return - table = Table(title=f"Env vars: {name}") - table.add_column("Key", style="cyan") - table.add_column("Value", style="dim") - for v in variables: - val = v.get("value", "") - masked = f"{val[:4]}***" if len(val) > 4 else val - table.add_row(v.get("name", "โ€”"), masked) - console.print(table) - else: - # Set vars: parse KEY=VAL pairs - new_vars = [] - for kv in vars: - if "=" not in kv: - console.print(f"[red]Invalid format: '{kv}'. Use KEY=VALUE.[/red]") - raise SystemExit(1) - k, v = kv.split("=", 1) - new_vars.append({"name": k, "value": v}) - - # Merge with existing - existing = {v["name"]: v["value"] for v in container.get("variables", [])} - for nv in new_vars: - existing[nv["name"]] = nv["value"] - - merged = [{"name": k, "value": v} for k, v in existing.items()] - - # PaaS requires the full container payload on PUT โ€” merge vars into it - container["variables"] = merged - client.update_container( - org_id, - project_id, - env_id, - cid, - container, - ) - console.print( - f"[green]Set {len(new_vars)} variable(s) on '{name}'.[/green]" - ) - finally: - client.close() - - -# โ”€โ”€ deploy delete โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@deploy.command("up") -@click.option( - "--config", - "config_path", - default=None, - type=click.Path(), - help="Path to hanzo.toml (default: ./hanzo.toml).", -) -@_org_option -@_project_option -@_env_option -def deploy_up( - config_path: str | None, - org: str | None, - project: str | None, - env_id: str | None, -) -> None: - """Deploy from hanzo.toml โ€” create or update. - - Reads hanzo.toml from the current directory (or --config path), - creates the container if it doesn't exist, or updates it if it does. - - Examples: - hanzo deploy up - hanzo deploy up --config path/to/hanzo.toml - """ - from hanzo_cli.paas.config import config_to_payload, find_config, load_config - - resolved = find_config(config_path) - if not resolved: - console.print( - "[red]No hanzo.toml found.[/red] " - "Create one or pass --config ." - ) - raise SystemExit(1) - - cfg = load_config(resolved) - payload = config_to_payload(cfg) - name = payload.get("name", "app") - - org_id, project_id, env_id = resolve(org, project, env_id) - org_id = _require("--org", org_id) - project_id = _require("--project", project_id) - env_id = _require("--env", env_id) - - client = PaaSClient.from_auth() - try: - # Check if container already exists - containers = client.list_containers(org_id, project_id, env_id) - items = ( - containers - if isinstance(containers, list) - else containers.get("data", containers) - ) - existing = None - for c in items: - if c.get("iid") == name or c.get("name") == name: - existing = c - break - - if existing: - # Update existing container - cid = str(existing.get("_id", existing.get("id"))) - # Merge payload into existing config - existing.update(payload) - client.update_container(org_id, project_id, env_id, cid, existing) - console.print( - f"[green]Updated container '{name}' from {resolved}[/green]" - ) - else: - # Create new container - data = client.create_container(org_id, project_id, env_id, payload) - cid = data.get("_id", data.get("id", "")) - console.print( - f"[green]Created container '{name}' from {resolved}[/green]" - ) - if cid: - console.print(f"ID: {cid}") - finally: - client.close() - - -@deploy.command("delete") -@click.argument("name") -@click.option("--yes", "-y", is_flag=True, help="Skip confirmation.") -@_org_option -@_project_option -@_env_option -def deploy_delete( - name: str, - yes: bool, - org: str | None, - project: str | None, - env_id: str | None, -) -> None: - """Delete a container deployment.""" - if not yes: - click.confirm(f"Delete container '{name}'?", abort=True) - - org_id, project_id, env_id = resolve(org, project, env_id) - org_id = _require("--org", org_id) - project_id = _require("--project", project_id) - env_id = _require("--env", env_id) - - client = PaaSClient.from_auth() - try: - container = _find_container(client, org_id, project_id, env_id, name) - cid = str(container.get("_id", container.get("id"))) - client.delete_container(org_id, project_id, env_id, cid) - finally: - client.close() - - console.print(f"[green]Deleted '{name}'.[/green]") diff --git a/pkg/hanzo-cli/hanzo_cli/paas/config.py b/pkg/hanzo-cli/hanzo_cli/paas/config.py deleted file mode 100644 index 35b521fa5..000000000 --- a/pkg/hanzo-cli/hanzo_cli/paas/config.py +++ /dev/null @@ -1,221 +0,0 @@ -"""Parse hanzo.toml deployment manifests into PaaS container payloads. - -The hanzo.toml file is the project-level deployment manifest for Hanzo PaaS. -It maps cleanly to the PaaS container API: - - [app] - name = "my-app" - type = "deployment" - dockerfile = "Dockerfile" - - [env] - NODE_ENV = "production" - - [networking] - port = 3000 - ingress = true - force_https = true - - [resources] - cpu = "2000m" # millicores or "2" for cores - memory = "2048Mi" # mebibytes or "2Gi" for gibibytes - - [storage] - enabled = true - mount = "/data" - size = "1Gi" - - [deploy] - replicas = 1 - strategy = "RollingUpdate" - command = "node index.js" - - [probes.readiness] - path = "/health" - port = 3000 - period = 30 - timeout = 10 - - [probes.liveness] - path = "/health" - port = 3000 - period = 30 - timeout = 10 -""" - -from __future__ import annotations - -import sys -from pathlib import Path -from typing import Any - -# Python 3.11+ has tomllib in stdlib; older versions need tomli. -if sys.version_info >= (3, 11): - import tomllib -else: - try: - import tomli as tomllib # type: ignore[no-redef] - except ModuleNotFoundError as exc: - raise ImportError( - "hanzo.toml support requires the 'tomli' package on Python <3.11. " - "Install it with: pip install tomli" - ) from exc - -DEFAULT_CONFIG_FILENAME = "hanzo.toml" - - -def find_config(path: str | Path | None = None) -> Path | None: - """Locate hanzo.toml โ€” explicit path or CWD search.""" - if path: - p = Path(path) - if p.is_file(): - return p - return None - cwd = Path.cwd() - candidate = cwd / DEFAULT_CONFIG_FILENAME - return candidate if candidate.is_file() else None - - -def load_config(path: str | Path) -> dict[str, Any]: - """Read and parse a hanzo.toml file.""" - with open(path, "rb") as f: - return tomllib.load(f) - - -def _parse_cpu(raw: str | int) -> tuple[int, str]: - """Parse CPU value like '2000m' or '2' into (value, type).""" - s = str(raw).strip() - if s.endswith("m"): - return int(s[:-1]), "millicores" - return int(float(s) * 1000), "millicores" - - -def _parse_memory(raw: str | int) -> tuple[int, str]: - """Parse memory value like '2048Mi' or '2Gi' into (value, type).""" - s = str(raw).strip() - if s.endswith("Gi"): - return int(s[:-2]), "gibibyte" - if s.endswith("Mi"): - return int(s[:-2]), "mebibyte" - # Plain number = mebibytes - return int(s), "mebibyte" - - -def _parse_storage_size(raw: str | int) -> tuple[int, str]: - """Parse storage size like '1Gi' or '500Mi'.""" - s = str(raw).strip() - if s.endswith("Gi"): - return int(s[:-2]), "gibibyte" - if s.endswith("Mi"): - return int(s[:-2]), "mebibyte" - return int(s), "gibibyte" - - -def _build_probe(cfg: dict[str, Any], port_default: int) -> dict[str, Any]: - """Convert a [probes.*] section into PaaS probe config.""" - return { - "enabled": True, - "checkMechanism": "httpGet", - "httpPath": cfg.get("path", "/health"), - "httpPort": cfg.get("port", port_default), - "initialDelaySeconds": cfg.get("initial_delay", 30), - "periodSeconds": cfg.get("period", 30), - "timeoutSeconds": cfg.get("timeout", 10), - "failureThreshold": cfg.get("failure_threshold", 3), - } - - -def config_to_payload(cfg: dict[str, Any]) -> dict[str, Any]: - """Convert a parsed hanzo.toml dict into a PaaS container creation payload.""" - app = cfg.get("app", {}) - env = cfg.get("env", {}) - networking = cfg.get("networking", {}) - resources = cfg.get("resources", {}) - storage = cfg.get("storage", {}) - deploy = cfg.get("deploy", {}) - probes_cfg = cfg.get("probes", {}) - - name = app.get("name", "app") - deploy_type = app.get("type", "deployment") - port = networking.get("port", 3000) - - # Resources - cpu_val, cpu_type = _parse_cpu(resources.get("cpu", "200m")) - mem_val, mem_type = _parse_memory(resources.get("memory", "256Mi")) - - payload: dict[str, Any] = { - "name": name, - "type": deploy_type, - "networking": { - "containerPort": port, - "ingress": {"enabled": networking.get("ingress", False)}, - "customDomain": {"enabled": False}, - "tcpProxy": {"enabled": False}, - }, - "podConfig": { - "restartPolicy": "Always", - "cpuRequest": cpu_val // 2, - "cpuRequestType": cpu_type, - "cpuLimit": cpu_val, - "cpuLimitType": cpu_type, - "memoryRequest": mem_val // 2, - "memoryRequestType": mem_type, - "memoryLimit": mem_val, - "memoryLimitType": mem_type, - }, - "storageConfig": { - "enabled": storage.get("enabled", False), - }, - "probes": { - "startup": {"enabled": False}, - "readiness": {"enabled": False}, - "liveness": {"enabled": False}, - }, - } - - # Storage - if storage.get("enabled", False): - size_val, size_type = _parse_storage_size(storage.get("size", "1Gi")) - payload["storageConfig"] = { - "enabled": True, - "mountPath": storage.get("mount", "/data"), - "size": size_val, - "sizeType": size_type, - "accessModes": ["ReadWriteOnce"], - } - - # Probes - for probe_name in ("readiness", "liveness", "startup"): - if probe_name in probes_cfg: - payload["probes"][probe_name] = _build_probe(probes_cfg[probe_name], port) - - # Deployment config - if deploy_type == "deployment": - payload["deploymentConfig"] = { - "desiredReplicas": deploy.get("replicas", 1), - "strategy": deploy.get("strategy", "RollingUpdate"), - "rollingUpdate": { - "maxSurge": 30, - "maxSurgeType": "percentage", - "maxUnavailable": 0, - "maxUnavailableType": "number", - }, - "revisionHistoryLimit": 10, - "cpuMetric": {"enabled": False}, - "memoryMetric": {"enabled": False}, - } - - # Environment variables - if env: - payload["variables"] = [ - {"name": k, "value": str(v)} for k, v in env.items() - ] - - # Source โ€” always repo (Dockerfile-based) for hanzo.toml - dockerfile = app.get("dockerfile", "Dockerfile") - payload["repoOrRegistry"] = "repo" - payload["repo"] = { - "dockerfile": dockerfile, - } - - return payload diff --git a/pkg/hanzo-cli/hanzo_cli/paas/context.py b/pkg/hanzo-cli/hanzo_cli/paas/context.py deleted file mode 100644 index 6ac2beb04..000000000 --- a/pkg/hanzo-cli/hanzo_cli/paas/context.py +++ /dev/null @@ -1,57 +0,0 @@ -"""PaaS context management โ€” remember current org/project/env. - -Stores selection at ~/.hanzo/paas/context.json so you don't have to -pass --org / --project / --env on every command. - -Set with: hanzo paas use --org myorg --project myproj --env production -Clear with: hanzo paas use --clear -""" - -from __future__ import annotations - -import json -from pathlib import Path -from typing import Any - -CONTEXT_DIR = Path.home() / ".hanzo" / "paas" -CONTEXT_FILE = CONTEXT_DIR / "context.json" - - -def load_context() -> dict[str, Any]: - """Load stored PaaS context.""" - if not CONTEXT_FILE.exists(): - return {} - try: - return json.loads(CONTEXT_FILE.read_text()) - except (json.JSONDecodeError, OSError): - return {} - - -def save_context(ctx: dict[str, Any]) -> None: - """Persist PaaS context.""" - CONTEXT_DIR.mkdir(parents=True, exist_ok=True) - CONTEXT_FILE.write_text(json.dumps(ctx, indent=2)) - CONTEXT_FILE.chmod(0o600) - - -def clear_context() -> None: - """Remove stored context.""" - if CONTEXT_FILE.exists(): - CONTEXT_FILE.unlink() - - -def resolve( - cli_org: str | None, - cli_project: str | None, - cli_env: str | None, -) -> tuple[str | None, str | None, str | None]: - """Merge CLI flags over stored context. - - Returns (org_id, project_id, env_id) โ€” any may be None. - """ - ctx = load_context() - return ( - cli_org or ctx.get("org_id"), - cli_project or ctx.get("project_id"), - cli_env or ctx.get("env_id"), - ) diff --git a/pkg/hanzo-cli/pyproject.toml b/pkg/hanzo-cli/pyproject.toml deleted file mode 100644 index 510e122cf..000000000 --- a/pkg/hanzo-cli/pyproject.toml +++ /dev/null @@ -1,58 +0,0 @@ -[project] -name = "hanzo-cli" -version = "0.2.1" -description = "Hanzo unified CLI โ€” IAM, KMS, Deploy" -readme = "README.md" -license = { text = "MIT" } -requires-python = ">=3.12" -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "cli", "iam", "devops"] -classifiers = [ - "Development Status :: 3 - Alpha", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Topic :: Software Development :: Libraries :: Python Modules", -] - -dependencies = [ - "click>=8.0", - "rich>=13.0", - "httpx>=0.25.0", - "pyjwt>=2.8.0", - "hanzo-iam>=1.30.0", - "hanzo-kms>=1.0.0", - "tomli>=2.0.0; python_version < '3.11'", -] - -[project.optional-dependencies] -dev = [ - "pytest>=8.0.0", - "ruff>=0.5.0", -] - -[project.scripts] -hanzo = "hanzo_cli.cli:main" - -[project.urls] -Homepage = "https://hanzo.ai" -Repository = "https://github.com/hanzoai/python-sdk" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_cli"] - -[tool.ruff] -target-version = "py39" -line-length = 100 - -[tool.ruff.lint] -select = ["E", "W", "F", "I", "B", "C4", "UP"] -ignore = ["E501"] diff --git a/pkg/hanzo-cli/tests/test_e2e.py b/pkg/hanzo-cli/tests/test_e2e.py deleted file mode 100644 index 27390b236..000000000 --- a/pkg/hanzo-cli/tests/test_e2e.py +++ /dev/null @@ -1,296 +0,0 @@ -"""End-to-end tests for hanzo CLI against live services. - -Requires: - - hanzo-cli installed (pip install -e pkg/hanzo-cli) - - Valid credentials stored at ~/.hanzo/auth/token.json (run hanzo login first) - - Live services: hanzo.id, platform.hanzo.ai - -Run: - pytest pkg/hanzo-cli/tests/test_e2e.py -v -s -""" - -from __future__ import annotations - -import subprocess -import time - -import pytest - -HANZO = "hanzo" -TIMEOUT = 30 - - -def run(args: list[str], check: bool = True) -> subprocess.CompletedProcess: - """Run a hanzo CLI command and return the result.""" - result = subprocess.run( - [HANZO] + args, - capture_output=True, - text=True, - timeout=TIMEOUT, - ) - if check and result.returncode != 0: - pytest.fail( - f"hanzo {' '.join(args)} failed (rc={result.returncode}):\n" - f"stdout: {result.stdout}\n" - f"stderr: {result.stderr}" - ) - return result - - -# ========================================================================= -# Auth -# ========================================================================= - - -class TestAuth: - def test_version(self): - r = run(["--version"]) - assert "0.1.0" in r.stdout - - def test_help(self): - r = run(["--help"]) - assert "login" in r.stdout - assert "paas" in r.stdout - assert "iam" in r.stdout - assert "kms" in r.stdout - - def test_whoami(self): - r = run(["whoami"]) - assert "z@hanzo.ai" in r.stdout or "z" in r.stdout - assert "hanzo.id" in r.stdout - - def test_whoami_shows_org(self): - r = run(["whoami"]) - assert "hanzo" in r.stdout - - -# ========================================================================= -# IAM -# ========================================================================= - - -class TestIAM: - def test_iam_help(self): - r = run(["iam", "--help"]) - assert "users" in r.stdout - assert "set-password" in r.stdout - - def test_iam_users(self): - """List users โ€” should return at least user z.""" - r = run(["iam", "users"], check=False) - # May fail if bearer token doesn't have admin access, - # but the command should at least not crash - assert r.returncode == 0 or "Not authenticated" not in r.stderr - - def test_iam_orgs(self): - r = run(["iam", "orgs"], check=False) - assert r.returncode == 0 or "Not authenticated" not in r.stderr - - -# ========================================================================= -# PaaS โ€” Read Operations -# ========================================================================= - - -class TestPaaSRead: - def test_paas_help(self): - r = run(["paas", "--help"]) - assert "deploy" in r.stdout - assert "orgs" in r.stdout - assert "use" in r.stdout - - def test_paas_orgs(self): - r = run(["paas", "orgs"]) - assert "Hanzo" in r.stdout - assert "Organizations" in r.stdout - - def test_paas_projects(self): - r = run(["paas", "projects", "--org", "698cda6739f65183b3009313"]) - assert "Platform" in r.stdout - - def test_paas_envs(self): - r = run( - [ - "paas", - "envs", - "--org", - "698cda6739f65183b3009313", - "--project", - "698cda6739f65183b3009318", - ] - ) - assert "production" in r.stdout - - def test_paas_context_set_and_show(self): - run( - [ - "paas", - "use", - "--org", - "698cda6739f65183b3009313", - "--project", - "698cda6739f65183b3009318", - "--env", - "698cda6739f65183b300931c", - ] - ) - r = run(["paas", "context"]) - assert "698cda6739f65183b3009313" in r.stdout - assert "698cda6739f65183b3009318" in r.stdout - assert "698cda6739f65183b300931c" in r.stdout - - def test_paas_deploy_list(self): - """List containers โ€” may be empty, but should not error.""" - r = run(["paas", "deploy", "list"]) - assert r.returncode == 0 - - -# ========================================================================= -# PaaS โ€” Deploy Lifecycle (create โ†’ status โ†’ logs โ†’ redeploy โ†’ delete) -# ========================================================================= - - -class TestPaaSDeployLifecycle: - """Full container lifecycle test using a lightweight Docker image.""" - - CONTAINER_NAME = "e2e-test-nginx" - - @classmethod - def setup_class(cls): - """Ensure context is set and clean up any leftover test containers.""" - run( - [ - "paas", - "use", - "--org", - "698cda6739f65183b3009313", - "--project", - "698cda6739f65183b3009318", - "--env", - "698cda6739f65183b300931c", - ] - ) - # Clean up leftover from previous failed runs - _r = run(["paas", "deploy", "delete", cls.CONTAINER_NAME, "-y"], check=False) - - @classmethod - def teardown_class(cls): - """Clean up test container.""" - run(["paas", "deploy", "delete", cls.CONTAINER_NAME, "-y"], check=False) - - def test_01_create(self): - r = run( - [ - "paas", - "deploy", - "create", - self.CONTAINER_NAME, - "--image", - "nginx:latest", - "--port", - "80", - ] - ) - assert "Created container" in r.stdout - assert self.CONTAINER_NAME in r.stdout - - def test_02_list_contains_container(self): - r = run(["paas", "deploy", "list"]) - assert self.CONTAINER_NAME in r.stdout - - def test_03_status_shows_pod(self): - """Wait for the pod to appear and check status.""" - for _attempt in range(6): - r = run(["paas", "deploy", "status", self.CONTAINER_NAME]) - if "Running" in r.stdout or "Pending" in r.stdout: - break - time.sleep(5) - assert self.CONTAINER_NAME in r.stdout - assert "deployment" in r.stdout - - def test_04_wait_for_running(self): - """Wait up to 60s for the pod to be Running.""" - for _attempt in range(12): - r = run(["paas", "deploy", "status", self.CONTAINER_NAME]) - if "Running" in r.stdout: - return - time.sleep(5) - pytest.fail(f"Pod not Running after 60s:\n{r.stdout}") - - def test_05_logs(self): - """Container logs should contain nginx startup messages.""" - r = run(["paas", "deploy", "logs", self.CONTAINER_NAME]) - assert "nginx" in r.stdout.lower() or "worker process" in r.stdout - - def test_06_env_show(self): - """Show env vars โ€” should work even if empty.""" - r = run(["paas", "deploy", "env", self.CONTAINER_NAME]) - assert r.returncode == 0 - - def test_07_env_set(self): - """Set an env var on the container.""" - r = run( - [ - "paas", - "deploy", - "env", - self.CONTAINER_NAME, - "TEST_VAR=hello_e2e", - ] - ) - assert "Set 1 variable" in r.stdout - - def test_08_env_verify(self): - """Verify the env var was set.""" - r = run(["paas", "deploy", "env", self.CONTAINER_NAME]) - assert "TEST_VAR" in r.stdout - - def test_09_redeploy(self): - """Trigger a redeploy.""" - r = run(["paas", "deploy", "redeploy", self.CONTAINER_NAME]) - assert "Redeployment triggered" in r.stdout - - def test_10_delete(self): - """Delete the container.""" - r = run(["paas", "deploy", "delete", self.CONTAINER_NAME, "-y"]) - assert "Deleted" in r.stdout - - def test_11_verify_deleted(self): - """Verify the container no longer exists.""" - r = run(["paas", "deploy", "list"]) - assert self.CONTAINER_NAME not in r.stdout - - -# ========================================================================= -# PaaS โ€” Error Handling -# ========================================================================= - - -class TestPaaSErrors: - def test_missing_context(self): - """Commands without required context should fail gracefully.""" - run(["paas", "use", "--clear"]) - r = run(["paas", "deploy", "list"], check=False) - assert r.returncode != 0 - assert "required" in r.stdout.lower() or "required" in r.stderr.lower() - - def test_nonexistent_container(self): - run( - [ - "paas", - "use", - "--org", - "698cda6739f65183b3009313", - "--project", - "698cda6739f65183b3009318", - "--env", - "698cda6739f65183b300931c", - ] - ) - r = run(["paas", "deploy", "status", "nonexistent-container-xyz"], check=False) - assert r.returncode != 0 - assert "not found" in r.stdout.lower() or "not found" in r.stderr.lower() - - def test_create_missing_image_and_repo(self): - r = run(["paas", "deploy", "create", "bad-deploy"], check=False) - assert r.returncode != 0 diff --git a/pkg/hanzo-cli/uv.lock b/pkg/hanzo-cli/uv.lock deleted file mode 100644 index ec48deca7..000000000 --- a/pkg/hanzo-cli/uv.lock +++ /dev/null @@ -1,1081 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" - -[[package]] -name = "aiohappyeyeballs" -version = "2.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, -] - -[[package]] -name = "aiohttp" -version = "3.13.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohappyeyeballs" }, - { name = "aiosignal" }, - { name = "attrs" }, - { name = "frozenlist" }, - { name = "multidict" }, - { name = "propcache" }, - { name = "yarl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732, upload-time = "2026-01-03T17:30:14.23Z" }, - { url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293, upload-time = "2026-01-03T17:30:15.96Z" }, - { url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533, upload-time = "2026-01-03T17:30:17.431Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839, upload-time = "2026-01-03T17:30:19.422Z" }, - { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932, upload-time = "2026-01-03T17:30:21.756Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906, upload-time = "2026-01-03T17:30:23.932Z" }, - { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020, upload-time = "2026-01-03T17:30:26Z" }, - { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181, upload-time = "2026-01-03T17:30:27.554Z" }, - { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794, upload-time = "2026-01-03T17:30:29.254Z" }, - { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900, upload-time = "2026-01-03T17:30:31.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239, upload-time = "2026-01-03T17:30:32.703Z" }, - { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527, upload-time = "2026-01-03T17:30:34.695Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489, upload-time = "2026-01-03T17:30:36.864Z" }, - { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852, upload-time = "2026-01-03T17:30:39.433Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379, upload-time = "2026-01-03T17:30:41.081Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4a/1a3fee7c21350cac78e5c5cef711bac1b94feca07399f3d406972e2d8fcd/aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", size = 428253, upload-time = "2026-01-03T17:30:42.644Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b7/76175c7cb4eb73d91ad63c34e29fc4f77c9386bba4a65b53ba8e05ee3c39/aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", size = 455407, upload-time = "2026-01-03T17:30:44.195Z" }, - { url = "https://files.pythonhosted.org/packages/97/8a/12ca489246ca1faaf5432844adbfce7ff2cc4997733e0af120869345643a/aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", size = 734190, upload-time = "2026-01-03T17:30:45.832Z" }, - { url = "https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", size = 491783, upload-time = "2026-01-03T17:30:47.466Z" }, - { url = "https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", size = 490704, upload-time = "2026-01-03T17:30:49.373Z" }, - { url = "https://files.pythonhosted.org/packages/6d/40/a46b03ca03936f832bc7eaa47cfbb1ad012ba1be4790122ee4f4f8cba074/aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", size = 1720652, upload-time = "2026-01-03T17:30:50.974Z" }, - { url = "https://files.pythonhosted.org/packages/f7/7e/917fe18e3607af92657e4285498f500dca797ff8c918bd7d90b05abf6c2a/aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", size = 1692014, upload-time = "2026-01-03T17:30:52.729Z" }, - { url = "https://files.pythonhosted.org/packages/71/b6/cefa4cbc00d315d68973b671cf105b21a609c12b82d52e5d0c9ae61d2a09/aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", size = 1759777, upload-time = "2026-01-03T17:30:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e3/e06ee07b45e59e6d81498b591fc589629be1553abb2a82ce33efe2a7b068/aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", size = 1861276, upload-time = "2026-01-03T17:30:56.512Z" }, - { url = "https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", size = 1743131, upload-time = "2026-01-03T17:30:58.256Z" }, - { url = "https://files.pythonhosted.org/packages/04/98/3d21dde21889b17ca2eea54fdcff21b27b93f45b7bb94ca029c31ab59dc3/aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", size = 1556863, upload-time = "2026-01-03T17:31:00.445Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/da0c3ab1192eaf64782b03971ab4055b475d0db07b17eff925e8c93b3aa5/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", size = 1682793, upload-time = "2026-01-03T17:31:03.024Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0f/5802ada182f575afa02cbd0ec5180d7e13a402afb7c2c03a9aa5e5d49060/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", size = 1716676, upload-time = "2026-01-03T17:31:04.842Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8c/714d53bd8b5a4560667f7bbbb06b20c2382f9c7847d198370ec6526af39c/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", size = 1733217, upload-time = "2026-01-03T17:31:06.868Z" }, - { url = "https://files.pythonhosted.org/packages/7d/79/e2176f46d2e963facea939f5be2d26368ce543622be6f00a12844d3c991f/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", size = 1552303, upload-time = "2026-01-03T17:31:08.958Z" }, - { url = "https://files.pythonhosted.org/packages/ab/6a/28ed4dea1759916090587d1fe57087b03e6c784a642b85ef48217b0277ae/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", size = 1763673, upload-time = "2026-01-03T17:31:10.676Z" }, - { url = "https://files.pythonhosted.org/packages/e8/35/4a3daeb8b9fab49240d21c04d50732313295e4bd813a465d840236dd0ce1/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", size = 1721120, upload-time = "2026-01-03T17:31:12.575Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9f/d643bb3c5fb99547323e635e251c609fbbc660d983144cfebec529e09264/aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", size = 427383, upload-time = "2026-01-03T17:31:14.382Z" }, - { url = "https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", size = 453899, upload-time = "2026-01-03T17:31:15.958Z" }, - { url = "https://files.pythonhosted.org/packages/99/36/5b6514a9f5d66f4e2597e40dea2e3db271e023eb7a5d22defe96ba560996/aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808", size = 737238, upload-time = "2026-01-03T17:31:17.909Z" }, - { url = "https://files.pythonhosted.org/packages/f7/49/459327f0d5bcd8c6c9ca69e60fdeebc3622861e696490d8674a6d0cb90a6/aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415", size = 492292, upload-time = "2026-01-03T17:31:19.919Z" }, - { url = "https://files.pythonhosted.org/packages/e8/0b/b97660c5fd05d3495b4eb27f2d0ef18dc1dc4eff7511a9bf371397ff0264/aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f", size = 493021, upload-time = "2026-01-03T17:31:21.636Z" }, - { url = "https://files.pythonhosted.org/packages/54/d4/438efabdf74e30aeceb890c3290bbaa449780583b1270b00661126b8aae4/aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6", size = 1717263, upload-time = "2026-01-03T17:31:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/71/f2/7bddc7fd612367d1459c5bcf598a9e8f7092d6580d98de0e057eb42697ad/aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687", size = 1669107, upload-time = "2026-01-03T17:31:25.334Z" }, - { url = "https://files.pythonhosted.org/packages/00/5a/1aeaecca40e22560f97610a329e0e5efef5e0b5afdf9f857f0d93839ab2e/aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26", size = 1760196, upload-time = "2026-01-03T17:31:27.394Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f8/0ff6992bea7bd560fc510ea1c815f87eedd745fe035589c71ce05612a19a/aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a", size = 1843591, upload-time = "2026-01-03T17:31:29.238Z" }, - { url = "https://files.pythonhosted.org/packages/e3/d1/e30e537a15f53485b61f5be525f2157da719819e8377298502aebac45536/aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1", size = 1720277, upload-time = "2026-01-03T17:31:31.053Z" }, - { url = "https://files.pythonhosted.org/packages/84/45/23f4c451d8192f553d38d838831ebbc156907ea6e05557f39563101b7717/aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25", size = 1548575, upload-time = "2026-01-03T17:31:32.87Z" }, - { url = "https://files.pythonhosted.org/packages/6a/ed/0a42b127a43712eda7807e7892c083eadfaf8429ca8fb619662a530a3aab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603", size = 1679455, upload-time = "2026-01-03T17:31:34.76Z" }, - { url = "https://files.pythonhosted.org/packages/2e/b5/c05f0c2b4b4fe2c9d55e73b6d3ed4fd6c9dc2684b1d81cbdf77e7fad9adb/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a", size = 1687417, upload-time = "2026-01-03T17:31:36.699Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6b/915bc5dad66aef602b9e459b5a973529304d4e89ca86999d9d75d80cbd0b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926", size = 1729968, upload-time = "2026-01-03T17:31:38.622Z" }, - { url = "https://files.pythonhosted.org/packages/11/3b/e84581290a9520024a08640b63d07673057aec5ca548177a82026187ba73/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba", size = 1545690, upload-time = "2026-01-03T17:31:40.57Z" }, - { url = "https://files.pythonhosted.org/packages/f5/04/0c3655a566c43fd647c81b895dfe361b9f9ad6d58c19309d45cff52d6c3b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c", size = 1746390, upload-time = "2026-01-03T17:31:42.857Z" }, - { url = "https://files.pythonhosted.org/packages/1f/53/71165b26978f719c3419381514c9690bd5980e764a09440a10bb816ea4ab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43", size = 1702188, upload-time = "2026-01-03T17:31:44.984Z" }, - { url = "https://files.pythonhosted.org/packages/29/a7/cbe6c9e8e136314fa1980da388a59d2f35f35395948a08b6747baebb6aa6/aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1", size = 433126, upload-time = "2026-01-03T17:31:47.463Z" }, - { url = "https://files.pythonhosted.org/packages/de/56/982704adea7d3b16614fc5936014e9af85c0e34b58f9046655817f04306e/aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984", size = 459128, upload-time = "2026-01-03T17:31:49.2Z" }, - { url = "https://files.pythonhosted.org/packages/6c/2a/3c79b638a9c3d4658d345339d22070241ea341ed4e07b5ac60fb0f418003/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c", size = 769512, upload-time = "2026-01-03T17:31:51.134Z" }, - { url = "https://files.pythonhosted.org/packages/29/b9/3e5014d46c0ab0db8707e0ac2711ed28c4da0218c358a4e7c17bae0d8722/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592", size = 506444, upload-time = "2026-01-03T17:31:52.85Z" }, - { url = "https://files.pythonhosted.org/packages/90/03/c1d4ef9a054e151cd7839cdc497f2638f00b93cbe8043983986630d7a80c/aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f", size = 510798, upload-time = "2026-01-03T17:31:54.91Z" }, - { url = "https://files.pythonhosted.org/packages/ea/76/8c1e5abbfe8e127c893fe7ead569148a4d5a799f7cf958d8c09f3eedf097/aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29", size = 1868835, upload-time = "2026-01-03T17:31:56.733Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ac/984c5a6f74c363b01ff97adc96a3976d9c98940b8969a1881575b279ac5d/aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc", size = 1720486, upload-time = "2026-01-03T17:31:58.65Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9a/b7039c5f099c4eb632138728828b33428585031a1e658d693d41d07d89d1/aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2", size = 1847951, upload-time = "2026-01-03T17:32:00.989Z" }, - { url = "https://files.pythonhosted.org/packages/3c/02/3bec2b9a1ba3c19ff89a43a19324202b8eb187ca1e928d8bdac9bbdddebd/aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587", size = 1941001, upload-time = "2026-01-03T17:32:03.122Z" }, - { url = "https://files.pythonhosted.org/packages/37/df/d879401cedeef27ac4717f6426c8c36c3091c6e9f08a9178cc87549c537f/aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8", size = 1797246, upload-time = "2026-01-03T17:32:05.255Z" }, - { url = "https://files.pythonhosted.org/packages/8d/15/be122de1f67e6953add23335c8ece6d314ab67c8bebb3f181063010795a7/aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632", size = 1627131, upload-time = "2026-01-03T17:32:07.607Z" }, - { url = "https://files.pythonhosted.org/packages/12/12/70eedcac9134cfa3219ab7af31ea56bc877395b1ac30d65b1bc4b27d0438/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64", size = 1795196, upload-time = "2026-01-03T17:32:09.59Z" }, - { url = "https://files.pythonhosted.org/packages/32/11/b30e1b1cd1f3054af86ebe60df96989c6a414dd87e27ad16950eee420bea/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0", size = 1782841, upload-time = "2026-01-03T17:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/88/0d/d98a9367b38912384a17e287850f5695c528cff0f14f791ce8ee2e4f7796/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56", size = 1795193, upload-time = "2026-01-03T17:32:13.705Z" }, - { url = "https://files.pythonhosted.org/packages/43/a5/a2dfd1f5ff5581632c7f6a30e1744deda03808974f94f6534241ef60c751/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72", size = 1621979, upload-time = "2026-01-03T17:32:15.965Z" }, - { url = "https://files.pythonhosted.org/packages/fa/f0/12973c382ae7c1cccbc4417e129c5bf54c374dfb85af70893646e1f0e749/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df", size = 1822193, upload-time = "2026-01-03T17:32:18.219Z" }, - { url = "https://files.pythonhosted.org/packages/3c/5f/24155e30ba7f8c96918af1350eb0663e2430aad9e001c0489d89cd708ab1/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", size = 1769801, upload-time = "2026-01-03T17:32:20.25Z" }, - { url = "https://files.pythonhosted.org/packages/eb/f8/7314031ff5c10e6ece114da79b338ec17eeff3a079e53151f7e9f43c4723/aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", size = 466523, upload-time = "2026-01-03T17:32:22.215Z" }, - { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694, upload-time = "2026-01-03T17:32:24.546Z" }, -] - -[[package]] -name = "aiosignal" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "frozenlist" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, -] - -[[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.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, -] - -[[package]] -name = "attrs" -version = "25.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, -] - -[[package]] -name = "certifi" -version = "2026.2.25" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, - { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, - { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, - { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, - { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, - { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, - { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, - { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, - { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - -[[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 = "cryptography" -version = "46.0.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" }, - { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, - { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, - { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, - { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, - { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, - { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, - { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, - { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, - { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", size = 3028230, upload-time = "2026-02-10T19:17:30.518Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", size = 3480909, upload-time = "2026-02-10T19:17:32.083Z" }, - { url = "https://files.pythonhosted.org/packages/00/13/3d278bfa7a15a96b9dc22db5a12ad1e48a9eb3d40e1827ef66a5df75d0d0/cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", size = 7119287, upload-time = "2026-02-10T19:17:33.801Z" }, - { url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" }, - { url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" }, - { url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", size = 4927539, upload-time = "2026-02-10T19:17:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" }, - { url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131, upload-time = "2026-02-10T19:17:43.379Z" }, - { url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" }, - { url = "https://files.pythonhosted.org/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", size = 4892170, upload-time = "2026-02-10T19:17:46.997Z" }, - { url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" }, - { url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" }, - { url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" }, - { url = "https://files.pythonhosted.org/packages/86/ef/5d00ef966ddd71ac2e6951d278884a84a40ffbd88948ef0e294b214ae9e4/cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a", size = 3003637, upload-time = "2026-02-10T19:17:52.997Z" }, - { url = "https://files.pythonhosted.org/packages/b7/57/f3f4160123da6d098db78350fdfd9705057aad21de7388eacb2401dceab9/cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4", size = 3469487, upload-time = "2026-02-10T19:17:54.549Z" }, - { url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" }, - { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, - { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, - { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, - { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, - { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, - { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, - { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, - { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, - { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, - { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", size = 3010220, upload-time = "2026-02-10T19:18:17.361Z" }, - { url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050, upload-time = "2026-02-10T19:18:18.899Z" }, -] - -[[package]] -name = "frozenlist" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, - { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, - { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, - { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, - { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, - { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, - { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, - { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, - { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, - { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, - { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, - { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, - { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, - { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, - { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, - { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, - { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, - { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, - { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, - { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, - { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, - { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, - { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, - { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, - { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, - { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, - { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, - { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, - { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, - { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, - { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, - { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, - { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, - { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, - { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, - { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, - { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, - { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, - { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, - { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, - { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, - { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, - { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, - { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, - { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, - { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, - { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, - { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, - { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, - { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, - { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, - { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, - { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, - { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, - { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, - { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, - { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, - { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, - { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, - { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, - { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, - { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, - { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, -] - -[[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 = "hanzo-cli" -version = "0.2.1" -source = { editable = "." } -dependencies = [ - { name = "click" }, - { name = "hanzo-iam" }, - { name = "hanzo-kms" }, - { name = "httpx" }, - { name = "pyjwt" }, - { name = "rich" }, -] - -[package.optional-dependencies] -dev = [ - { name = "pytest" }, - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "click", specifier = ">=8.0" }, - { name = "hanzo-iam", specifier = ">=1.0.0" }, - { name = "hanzo-kms", specifier = ">=1.0.0" }, - { name = "httpx", specifier = ">=0.25.0" }, - { name = "pyjwt", specifier = ">=2.8.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, - { name = "rich", specifier = ">=13.0" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.5.0" }, - { name = "tomli", marker = "python_full_version < '3.11'", specifier = ">=2.0.0" }, -] -provides-extras = ["dev"] - -[[package]] -name = "hanzo-iam" -version = "1.29.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "cryptography" }, - { name = "pyjwt" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3e/4b/11d440a3a99e5b7967ae1a9d56f4bee7c9879f7e2a56a9a1398a3d931064/hanzo_iam-1.29.0.tar.gz", hash = "sha256:5979db89b791be181c259d103822424f389be5d82bff677bee9fad3f213f2578", size = 25123, upload-time = "2025-04-09T18:51:53.64Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/26/bc5dbd90e5fd0f2666646c2b4831cc4fef6867bbad8091da496851fe600c/hanzo_iam-1.29.0-py2.py3-none-any.whl", hash = "sha256:22aba50d91d642843570fd73853783cc26bad7ac618c778d2df49e54bd43bed9", size = 47149, upload-time = "2025-04-09T18:51:52.134Z" }, -] - -[[package]] -name = "hanzo-kms" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "httpx" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/78/c459ae92072e55d94e6b0718d927fb2801c06ea8ef8a5cacc417c237b385/hanzo_kms-1.1.0.tar.gz", hash = "sha256:13242266012dcc2a1b48705b48704504409f21c13ec022c32385f952cf4b9f8b", size = 7553, upload-time = "2026-02-21T06:20:06.542Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/13/77301a5216f4f3c85689061e590f606086f32f26bb0ea7a86ce850223e04/hanzo_kms-1.1.0-py3-none-any.whl", hash = "sha256:19ec34ae131917e153feade770f4aa03366ba43a662fba040d3e31cd78dd2fd3", size = 10672, upload-time = "2026-02-21T06:20:05.56Z" }, -] - -[[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 = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[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 = "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 = "multidict" -version = "6.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, - { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, - { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, - { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, - { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, - { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, - { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, - { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, - { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, - { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, - { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, - { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, - { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, - { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, - { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, - { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, - { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, - { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, - { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, - { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, - { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, - { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, - { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, - { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, - { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, - { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, - { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, - { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, - { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, - { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, - { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, - { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, - { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, - { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, - { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, - { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, - { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, - { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, - { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, - { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, - { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, - { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, - { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, - { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, - { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, - { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, - { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, - { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, - { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, - { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, - { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, - { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, - { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, - { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, - { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, - { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, - { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, - { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, - { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, - { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, - { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, - { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, - { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, - { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, - { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, - { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, - { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, - { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, - { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, - { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, - { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, - { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, - { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, - { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, - { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, - { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, - { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, -] - -[[package]] -name = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[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 = "propcache" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, - { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, - { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, - { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, - { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, - { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, - { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, - { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, - { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, - { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, - { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, - { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, - { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, - { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, - { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, - { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, - { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, - { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, - { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, - { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, - { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, - { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, - { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, - { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, - { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, - { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, - { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, - { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, - { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, - { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, - { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, - { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, - { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, - { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, - { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, - { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, - { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, - { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, - { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, - { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, - { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, - { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, - { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, - { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, - { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, - { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, - { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, - { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, - { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, - { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, - { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, - { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, - { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, - { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, - { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, -] - -[[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 = "pyjwt" -version = "2.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, -] - -[[package]] -name = "pytest" -version = "9.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, -] - -[[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 = "rich" -version = "14.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, -] - -[[package]] -name = "ruff" -version = "0.15.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/31/d6e536cdebb6568ae75a7f00e4b4819ae0ad2640c3604c305a0428680b0c/ruff-0.15.4.tar.gz", hash = "sha256:3412195319e42d634470cc97aa9803d07e9d5c9223b99bcb1518f0c725f26ae1", size = 4569550, upload-time = "2026-02-26T20:04:14.959Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/82/c11a03cfec3a4d26a0ea1e571f0f44be5993b923f905eeddfc397c13d360/ruff-0.15.4-py3-none-linux_armv6l.whl", hash = "sha256:a1810931c41606c686bae8b5b9a8072adac2f611bb433c0ba476acba17a332e0", size = 10453333, upload-time = "2026-02-26T20:04:20.093Z" }, - { url = "https://files.pythonhosted.org/packages/ce/5d/6a1f271f6e31dffb31855996493641edc3eef8077b883eaf007a2f1c2976/ruff-0.15.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5a1632c66672b8b4d3e1d1782859e98d6e0b4e70829530666644286600a33992", size = 10853356, upload-time = "2026-02-26T20:04:05.808Z" }, - { url = "https://files.pythonhosted.org/packages/b1/d8/0fab9f8842b83b1a9c2bf81b85063f65e93fb512e60effa95b0be49bfc54/ruff-0.15.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a4386ba2cd6c0f4ff75252845906acc7c7c8e1ac567b7bc3d373686ac8c222ba", size = 10187434, upload-time = "2026-02-26T20:03:54.656Z" }, - { url = "https://files.pythonhosted.org/packages/85/cc/cc220fd9394eff5db8d94dec199eec56dd6c9f3651d8869d024867a91030/ruff-0.15.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2496488bdfd3732747558b6f95ae427ff066d1fcd054daf75f5a50674411e75", size = 10535456, upload-time = "2026-02-26T20:03:52.738Z" }, - { url = "https://files.pythonhosted.org/packages/fa/0f/bced38fa5cf24373ec767713c8e4cadc90247f3863605fb030e597878661/ruff-0.15.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3f1c4893841ff2d54cbda1b2860fa3260173df5ddd7b95d370186f8a5e66a4ac", size = 10287772, upload-time = "2026-02-26T20:04:08.138Z" }, - { url = "https://files.pythonhosted.org/packages/2b/90/58a1802d84fed15f8f281925b21ab3cecd813bde52a8ca033a4de8ab0e7a/ruff-0.15.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:820b8766bd65503b6c30aaa6331e8ef3a6e564f7999c844e9a547c40179e440a", size = 11049051, upload-time = "2026-02-26T20:04:03.53Z" }, - { url = "https://files.pythonhosted.org/packages/d2/ac/b7ad36703c35f3866584564dc15f12f91cb1a26a897dc2fd13d7cb3ae1af/ruff-0.15.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9fb74bab47139c1751f900f857fa503987253c3ef89129b24ed375e72873e85", size = 11890494, upload-time = "2026-02-26T20:04:10.497Z" }, - { url = "https://files.pythonhosted.org/packages/93/3d/3eb2f47a39a8b0da99faf9c54d3eb24720add1e886a5309d4d1be73a6380/ruff-0.15.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f80c98765949c518142b3a50a5db89343aa90f2c2bf7799de9986498ae6176db", size = 11326221, upload-time = "2026-02-26T20:04:12.84Z" }, - { url = "https://files.pythonhosted.org/packages/ff/90/bf134f4c1e5243e62690e09d63c55df948a74084c8ac3e48a88468314da6/ruff-0.15.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:451a2e224151729b3b6c9ffb36aed9091b2996fe4bdbd11f47e27d8f2e8888ec", size = 11168459, upload-time = "2026-02-26T20:04:00.969Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e5/a64d27688789b06b5d55162aafc32059bb8c989c61a5139a36e1368285eb/ruff-0.15.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a8f157f2e583c513c4f5f896163a93198297371f34c04220daf40d133fdd4f7f", size = 11104366, upload-time = "2026-02-26T20:03:48.099Z" }, - { url = "https://files.pythonhosted.org/packages/f1/f6/32d1dcb66a2559763fc3027bdd65836cad9eb09d90f2ed6a63d8e9252b02/ruff-0.15.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:917cc68503357021f541e69b35361c99387cdbbf99bd0ea4aa6f28ca99ff5338", size = 10510887, upload-time = "2026-02-26T20:03:45.771Z" }, - { url = "https://files.pythonhosted.org/packages/ff/92/22d1ced50971c5b6433aed166fcef8c9343f567a94cf2b9d9089f6aa80fe/ruff-0.15.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e9737c8161da79fd7cfec19f1e35620375bd8b2a50c3e77fa3d2c16f574105cc", size = 10285939, upload-time = "2026-02-26T20:04:22.42Z" }, - { url = "https://files.pythonhosted.org/packages/e6/f4/7c20aec3143837641a02509a4668fb146a642fd1211846634edc17eb5563/ruff-0.15.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:291258c917539e18f6ba40482fe31d6f5ac023994ee11d7bdafd716f2aab8a68", size = 10765471, upload-time = "2026-02-26T20:03:58.924Z" }, - { url = "https://files.pythonhosted.org/packages/d0/09/6d2f7586f09a16120aebdff8f64d962d7c4348313c77ebb29c566cefc357/ruff-0.15.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3f83c45911da6f2cd5936c436cf86b9f09f09165f033a99dcf7477e34041cbc3", size = 11263382, upload-time = "2026-02-26T20:04:24.424Z" }, - { url = "https://files.pythonhosted.org/packages/1b/fa/2ef715a1cd329ef47c1a050e10dee91a9054b7ce2fcfdd6a06d139afb7ec/ruff-0.15.4-py3-none-win32.whl", hash = "sha256:65594a2d557d4ee9f02834fcdf0a28daa8b3b9f6cb2cb93846025a36db47ef22", size = 10506664, upload-time = "2026-02-26T20:03:50.56Z" }, - { url = "https://files.pythonhosted.org/packages/d0/a8/c688ef7e29983976820d18710f955751d9f4d4eb69df658af3d006e2ba3e/ruff-0.15.4-py3-none-win_amd64.whl", hash = "sha256:04196ad44f0df220c2ece5b0e959c2f37c777375ec744397d21d15b50a75264f", size = 11651048, upload-time = "2026-02-26T20:04:17.191Z" }, - { url = "https://files.pythonhosted.org/packages/3e/0a/9e1be9035b37448ce2e68c978f0591da94389ade5a5abafa4cf99985d1b2/ruff-0.15.4-py3-none-win_arm64.whl", hash = "sha256:60d5177e8cfc70e51b9c5fad936c634872a74209f934c1e79107d11787ad5453", size = 10966776, upload-time = "2026-02-26T20:03:56.908Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] - -[[package]] -name = "yarl" -version = "1.23.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "multidict" }, - { name = "propcache" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/8a/94615bc31022f711add374097ad4144d569e95ff3c38d39215d07ac153a0/yarl-1.23.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860", size = 124737, upload-time = "2026-03-01T22:05:12.897Z" }, - { url = "https://files.pythonhosted.org/packages/e3/6f/c6554045d59d64052698add01226bc867b52fe4a12373415d7991fdca95d/yarl-1.23.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069", size = 87029, upload-time = "2026-03-01T22:05:14.376Z" }, - { url = "https://files.pythonhosted.org/packages/19/2a/725ecc166d53438bc88f76822ed4b1e3b10756e790bafd7b523fe97c322d/yarl-1.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25", size = 86310, upload-time = "2026-03-01T22:05:15.71Z" }, - { url = "https://files.pythonhosted.org/packages/99/30/58260ed98e6ff7f90ba84442c1ddd758c9170d70327394a6227b310cd60f/yarl-1.23.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", size = 97587, upload-time = "2026-03-01T22:05:17.384Z" }, - { url = "https://files.pythonhosted.org/packages/76/0a/8b08aac08b50682e65759f7f8dde98ae8168f72487e7357a5d684c581ef9/yarl-1.23.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", size = 92528, upload-time = "2026-03-01T22:05:18.804Z" }, - { url = "https://files.pythonhosted.org/packages/52/07/0b7179101fe5f8385ec6c6bb5d0cb9f76bd9fb4a769591ab6fb5cdbfc69a/yarl-1.23.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", size = 105339, upload-time = "2026-03-01T22:05:20.235Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8a/36d82869ab5ec829ca8574dfcb92b51286fcfb1e9c7a73659616362dc880/yarl-1.23.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", size = 105061, upload-time = "2026-03-01T22:05:22.268Z" }, - { url = "https://files.pythonhosted.org/packages/66/3e/868e5c3364b6cee19ff3e1a122194fa4ce51def02c61023970442162859e/yarl-1.23.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", size = 100132, upload-time = "2026-03-01T22:05:23.638Z" }, - { url = "https://files.pythonhosted.org/packages/cf/26/9c89acf82f08a52cb52d6d39454f8d18af15f9d386a23795389d1d423823/yarl-1.23.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", size = 99289, upload-time = "2026-03-01T22:05:25.749Z" }, - { url = "https://files.pythonhosted.org/packages/6f/54/5b0db00d2cb056922356104468019c0a132e89c8d3ab67d8ede9f4483d2a/yarl-1.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", size = 96950, upload-time = "2026-03-01T22:05:27.318Z" }, - { url = "https://files.pythonhosted.org/packages/f6/40/10fa93811fd439341fad7e0718a86aca0de9548023bbb403668d6555acab/yarl-1.23.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", size = 93960, upload-time = "2026-03-01T22:05:28.738Z" }, - { url = "https://files.pythonhosted.org/packages/bc/d2/8ae2e6cd77d0805f4526e30ec43b6f9a3dfc542d401ac4990d178e4bf0cf/yarl-1.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", size = 104703, upload-time = "2026-03-01T22:05:30.438Z" }, - { url = "https://files.pythonhosted.org/packages/2f/0c/b3ceacf82c3fe21183ce35fa2acf5320af003d52bc1fcf5915077681142e/yarl-1.23.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", size = 98325, upload-time = "2026-03-01T22:05:31.835Z" }, - { url = "https://files.pythonhosted.org/packages/9d/e0/12900edd28bdab91a69bd2554b85ad7b151f64e8b521fe16f9ad2f56477a/yarl-1.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", size = 105067, upload-time = "2026-03-01T22:05:33.358Z" }, - { url = "https://files.pythonhosted.org/packages/15/61/74bb1182cf79c9bbe4eb6b1f14a57a22d7a0be5e9cedf8e2d5c2086474c3/yarl-1.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", size = 100285, upload-time = "2026-03-01T22:05:35.4Z" }, - { url = "https://files.pythonhosted.org/packages/69/7f/cd5ef733f2550de6241bd8bd8c3febc78158b9d75f197d9c7baa113436af/yarl-1.23.0-cp312-cp312-win32.whl", hash = "sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d", size = 82359, upload-time = "2026-03-01T22:05:36.811Z" }, - { url = "https://files.pythonhosted.org/packages/f5/be/25216a49daeeb7af2bec0db22d5e7df08ed1d7c9f65d78b14f3b74fd72fc/yarl-1.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e", size = 87674, upload-time = "2026-03-01T22:05:38.171Z" }, - { url = "https://files.pythonhosted.org/packages/d2/35/aeab955d6c425b227d5b7247eafb24f2653fedc32f95373a001af5dfeb9e/yarl-1.23.0-cp312-cp312-win_arm64.whl", hash = "sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9", size = 81879, upload-time = "2026-03-01T22:05:40.006Z" }, - { url = "https://files.pythonhosted.org/packages/9a/4b/a0a6e5d0ee8a2f3a373ddef8a4097d74ac901ac363eea1440464ccbe0898/yarl-1.23.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", size = 123796, upload-time = "2026-03-01T22:05:41.412Z" }, - { url = "https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", size = 86547, upload-time = "2026-03-01T22:05:42.841Z" }, - { url = "https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", size = 85854, upload-time = "2026-03-01T22:05:44.85Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f4/4e30b250927ffdab4db70da08b9b8d2194d7c7b400167b8fbeca1e4701ca/yarl-1.23.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", size = 98351, upload-time = "2026-03-01T22:05:46.836Z" }, - { url = "https://files.pythonhosted.org/packages/86/fc/4118c5671ea948208bdb1492d8b76bdf1453d3e73df051f939f563e7dcc5/yarl-1.23.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", size = 92711, upload-time = "2026-03-01T22:05:48.316Z" }, - { url = "https://files.pythonhosted.org/packages/56/11/1ed91d42bd9e73c13dc9e7eb0dd92298d75e7ac4dd7f046ad0c472e231cd/yarl-1.23.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", size = 106014, upload-time = "2026-03-01T22:05:50.028Z" }, - { url = "https://files.pythonhosted.org/packages/ce/c9/74e44e056a23fbc33aca71779ef450ca648a5bc472bdad7a82339918f818/yarl-1.23.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", size = 105557, upload-time = "2026-03-01T22:05:51.416Z" }, - { url = "https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", size = 101559, upload-time = "2026-03-01T22:05:52.872Z" }, - { url = "https://files.pythonhosted.org/packages/72/59/c5b8d94b14e3d3c2a9c20cb100119fd534ab5a14b93673ab4cc4a4141ea5/yarl-1.23.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", size = 100502, upload-time = "2026-03-01T22:05:54.954Z" }, - { url = "https://files.pythonhosted.org/packages/77/4f/96976cb54cbfc5c9fd73ed4c51804f92f209481d1fb190981c0f8a07a1d7/yarl-1.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", size = 98027, upload-time = "2026-03-01T22:05:56.409Z" }, - { url = "https://files.pythonhosted.org/packages/63/6e/904c4f476471afdbad6b7e5b70362fb5810e35cd7466529a97322b6f5556/yarl-1.23.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", size = 95369, upload-time = "2026-03-01T22:05:58.141Z" }, - { url = "https://files.pythonhosted.org/packages/9d/40/acfcdb3b5f9d68ef499e39e04d25e141fe90661f9d54114556cf83be8353/yarl-1.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", size = 105565, upload-time = "2026-03-01T22:06:00.286Z" }, - { url = "https://files.pythonhosted.org/packages/5e/c6/31e28f3a6ba2869c43d124f37ea5260cac9c9281df803c354b31f4dd1f3c/yarl-1.23.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", size = 99813, upload-time = "2026-03-01T22:06:01.712Z" }, - { url = "https://files.pythonhosted.org/packages/08/1f/6f65f59e72d54aa467119b63fc0b0b1762eff0232db1f4720cd89e2f4a17/yarl-1.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", size = 105632, upload-time = "2026-03-01T22:06:03.188Z" }, - { url = "https://files.pythonhosted.org/packages/a3/c4/18b178a69935f9e7a338127d5b77d868fdc0f0e49becd286d51b3a18c61d/yarl-1.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", size = 101895, upload-time = "2026-03-01T22:06:04.651Z" }, - { url = "https://files.pythonhosted.org/packages/8f/54/f5b870b5505663911dba950a8e4776a0dbd51c9c54c0ae88e823e4b874a0/yarl-1.23.0-cp313-cp313-win32.whl", hash = "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", size = 82356, upload-time = "2026-03-01T22:06:06.04Z" }, - { url = "https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", size = 87515, upload-time = "2026-03-01T22:06:08.107Z" }, - { url = "https://files.pythonhosted.org/packages/00/fd/7e1c66efad35e1649114fa13f17485f62881ad58edeeb7f49f8c5e748bf9/yarl-1.23.0-cp313-cp313-win_arm64.whl", hash = "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", size = 81785, upload-time = "2026-03-01T22:06:10.181Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fc/119dd07004f17ea43bb91e3ece6587759edd7519d6b086d16bfbd3319982/yarl-1.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", size = 130719, upload-time = "2026-03-01T22:06:11.708Z" }, - { url = "https://files.pythonhosted.org/packages/e6/0d/9f2348502fbb3af409e8f47730282cd6bc80dec6630c1e06374d882d6eb2/yarl-1.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", size = 89690, upload-time = "2026-03-01T22:06:13.429Z" }, - { url = "https://files.pythonhosted.org/packages/50/93/e88f3c80971b42cfc83f50a51b9d165a1dbf154b97005f2994a79f212a07/yarl-1.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", size = 89851, upload-time = "2026-03-01T22:06:15.53Z" }, - { url = "https://files.pythonhosted.org/packages/1c/07/61c9dd8ba8f86473263b4036f70fb594c09e99c0d9737a799dfd8bc85651/yarl-1.23.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", size = 95874, upload-time = "2026-03-01T22:06:17.553Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e9/f9ff8ceefba599eac6abddcfb0b3bee9b9e636e96dbf54342a8577252379/yarl-1.23.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", size = 88710, upload-time = "2026-03-01T22:06:19.004Z" }, - { url = "https://files.pythonhosted.org/packages/eb/78/0231bfcc5d4c8eec220bc2f9ef82cb4566192ea867a7c5b4148f44f6cbcd/yarl-1.23.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", size = 101033, upload-time = "2026-03-01T22:06:21.203Z" }, - { url = "https://files.pythonhosted.org/packages/cd/9b/30ea5239a61786f18fd25797151a17fbb3be176977187a48d541b5447dd4/yarl-1.23.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", size = 100817, upload-time = "2026-03-01T22:06:22.738Z" }, - { url = "https://files.pythonhosted.org/packages/62/e2/a4980481071791bc83bce2b7a1a1f7adcabfa366007518b4b845e92eeee3/yarl-1.23.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", size = 97482, upload-time = "2026-03-01T22:06:24.21Z" }, - { url = "https://files.pythonhosted.org/packages/e5/1e/304a00cf5f6100414c4b5a01fc7ff9ee724b62158a08df2f8170dfc72a2d/yarl-1.23.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", size = 95949, upload-time = "2026-03-01T22:06:25.697Z" }, - { url = "https://files.pythonhosted.org/packages/68/03/093f4055ed4cae649ac53bca3d180bd37102e9e11d048588e9ab0c0108d0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", size = 95839, upload-time = "2026-03-01T22:06:27.309Z" }, - { url = "https://files.pythonhosted.org/packages/b9/28/4c75ebb108f322aa8f917ae10a8ffa4f07cae10a8a627b64e578617df6a0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", size = 90696, upload-time = "2026-03-01T22:06:29.048Z" }, - { url = "https://files.pythonhosted.org/packages/23/9c/42c2e2dd91c1a570402f51bdf066bfdb1241c2240ba001967bad778e77b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", size = 100865, upload-time = "2026-03-01T22:06:30.525Z" }, - { url = "https://files.pythonhosted.org/packages/74/05/1bcd60a8a0a914d462c305137246b6f9d167628d73568505fce3f1cb2e65/yarl-1.23.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", size = 96234, upload-time = "2026-03-01T22:06:32.692Z" }, - { url = "https://files.pythonhosted.org/packages/90/b2/f52381aac396d6778ce516b7bc149c79e65bfc068b5de2857ab69eeea3b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", size = 100295, upload-time = "2026-03-01T22:06:34.268Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/638bae5bbf1113a659b2435d8895474598afe38b4a837103764f603aba56/yarl-1.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", size = 97784, upload-time = "2026-03-01T22:06:35.864Z" }, - { url = "https://files.pythonhosted.org/packages/80/25/a3892b46182c586c202629fc2159aa13975d3741d52ebd7347fd501d48d5/yarl-1.23.0-cp313-cp313t-win32.whl", hash = "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", size = 88313, upload-time = "2026-03-01T22:06:37.39Z" }, - { url = "https://files.pythonhosted.org/packages/43/68/8c5b36aa5178900b37387937bc2c2fe0e9505537f713495472dcf6f6fccc/yarl-1.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", size = 94932, upload-time = "2026-03-01T22:06:39.579Z" }, - { url = "https://files.pythonhosted.org/packages/c6/cc/d79ba8292f51f81f4dc533a8ccfb9fc6992cabf0998ed3245de7589dc07c/yarl-1.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", size = 84786, upload-time = "2026-03-01T22:06:41.988Z" }, - { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455, upload-time = "2026-03-01T22:06:43.615Z" }, - { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752, upload-time = "2026-03-01T22:06:45.425Z" }, - { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291, upload-time = "2026-03-01T22:06:46.974Z" }, - { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026, upload-time = "2026-03-01T22:06:48.459Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355, upload-time = "2026-03-01T22:06:50.306Z" }, - { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417, upload-time = "2026-03-01T22:06:52.1Z" }, - { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422, upload-time = "2026-03-01T22:06:54.424Z" }, - { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915, upload-time = "2026-03-01T22:06:55.895Z" }, - { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690, upload-time = "2026-03-01T22:06:58.21Z" }, - { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750, upload-time = "2026-03-01T22:06:59.794Z" }, - { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685, upload-time = "2026-03-01T22:07:01.375Z" }, - { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009, upload-time = "2026-03-01T22:07:03.151Z" }, - { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033, upload-time = "2026-03-01T22:07:04.701Z" }, - { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483, upload-time = "2026-03-01T22:07:06.231Z" }, - { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175, upload-time = "2026-03-01T22:07:08.4Z" }, - { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871, upload-time = "2026-03-01T22:07:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093, upload-time = "2026-03-01T22:07:11.501Z" }, - { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384, upload-time = "2026-03-01T22:07:13.069Z" }, - { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019, upload-time = "2026-03-01T22:07:14.903Z" }, - { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894, upload-time = "2026-03-01T22:07:17.372Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979, upload-time = "2026-03-01T22:07:19.361Z" }, - { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943, upload-time = "2026-03-01T22:07:21.266Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786, upload-time = "2026-03-01T22:07:23.129Z" }, - { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307, upload-time = "2026-03-01T22:07:24.689Z" }, - { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904, upload-time = "2026-03-01T22:07:26.287Z" }, - { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728, upload-time = "2026-03-01T22:07:27.906Z" }, - { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964, upload-time = "2026-03-01T22:07:30.198Z" }, - { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882, upload-time = "2026-03-01T22:07:32.132Z" }, - { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797, upload-time = "2026-03-01T22:07:34.404Z" }, - { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023, upload-time = "2026-03-01T22:07:35.953Z" }, - { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227, upload-time = "2026-03-01T22:07:37.594Z" }, - { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302, upload-time = "2026-03-01T22:07:39.334Z" }, - { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202, upload-time = "2026-03-01T22:07:41.717Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558, upload-time = "2026-03-01T22:07:43.433Z" }, - { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610, upload-time = "2026-03-01T22:07:45.773Z" }, - { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, - { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, -] diff --git a/pkg/hanzo-consensus/README.md b/pkg/hanzo-consensus/README.md deleted file mode 100644 index 6ba8f6b50..000000000 --- a/pkg/hanzo-consensus/README.md +++ /dev/null @@ -1,103 +0,0 @@ -# hanzo-consensus - -Metastable consensus protocol for multi-agent agreement. - -## Install - -```bash -pip install hanzo-consensus -``` - -## Basic Usage - -```python -import asyncio -from hanzo_consensus import run, Result - -async def execute(participant: str, prompt: str) -> Result: - output = await call_agent(participant, prompt) - return Result(id=participant, output=output, ok=True, ms=100) - -async def main(): - state = await run( - prompt="What is the best approach?", - participants=["agent1", "agent2", "agent3"], - execute=execute, - rounds=3, - ) - print(f"Winner: {state.winner}") - print(f"Synthesis: {state.synthesis}") - -asyncio.run(main()) -``` - -## MCP Mesh - Agent-to-Agent Consensus - -Each agent in consensus is available as MCP to every other: - -```python -from hanzo_consensus import MCPMesh, run_mcp_consensus - -# Create mesh of agents -mesh = MCPMesh() -mesh.register("claude", claude_server) -mesh.register("gpt4", gpt4_server) -mesh.register("gemini", gemini_server) - -# Run 10 rounds of discussion -state = await run_mcp_consensus( - mesh=mesh, - prompt="Discuss the architecture", - rounds=10, -) - -# Access discussion history -for i, round_responses in enumerate(state.discussion_history): - print(f"Round {i+1}:") - for agent, response in round_responses.items(): - print(f" [{agent}]: {response[:100]}...") -``` - -### MCPMesh Features - -- **Agent Registration**: Local FastMCP servers or remote endpoints -- **Tool Calling**: Any agent can call tools on any other agent -- **Broadcasting**: Call a tool on all agents simultaneously -- **Discussion Rounds**: Agents build on each other's responses - -```python -# Call tool on specific agent -result = await mesh.call("claude", "gpt4", "think", prompt="What do you think?") - -# Broadcast to all agents -results = await mesh.broadcast("claude", "discuss", prompt="New proposal...") -``` - -## Protocol - -Two-phase finality: - -**Phase I (Sampling)** -- Each participant proposes initial response -- k-peer sampling per round -- Luminance-weighted selection (faster = higher weight) -- Confidence accumulation toward ฮฒโ‚ - -**Phase II (Finality)** -- Threshold aggregation -- ฮฒโ‚‚ finality threshold -- Winner synthesis - -## Parameters - -| Param | Default | Description | -|-------|---------|-------------| -| `rounds` | 3 | Sampling rounds | -| `k` | 3 | Sample size per round | -| `alpha` | 0.6 | Agreement threshold | -| `beta_1` | 0.5 | Preference threshold (Phase I) | -| `beta_2` | 0.8 | Decision threshold (Phase II) | - -## Reference - -https://github.com/luxfi/consensus diff --git a/pkg/hanzo-consensus/hanzo_consensus/__init__.py b/pkg/hanzo-consensus/hanzo_consensus/__init__.py deleted file mode 100644 index 1cd80b6a2..000000000 --- a/pkg/hanzo-consensus/hanzo_consensus/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Metastable consensus protocol. - -Two-phase finality for multi-agent agreement. - -Reference: https://github.com/luxfi/consensus -""" - -from .mcp_mesh import MCPMesh, MCPAgent, create_mesh, run_mcp_consensus -from .consensus import State, Result, Consensus, run - -__all__ = [ - # Core - "Consensus", - "State", - "Result", - "run", - # MCP Mesh - "MCPMesh", - "MCPAgent", - "run_mcp_consensus", - "create_mesh", -] -__version__ = "0.1.0" diff --git a/pkg/hanzo-consensus/hanzo_consensus/consensus.py b/pkg/hanzo-consensus/hanzo_consensus/consensus.py deleted file mode 100644 index 094494bed..000000000 --- a/pkg/hanzo-consensus/hanzo_consensus/consensus.py +++ /dev/null @@ -1,211 +0,0 @@ -"""Metastable consensus protocol. - -Two-phase finality for distributed agreement. - -Phase I (Sampling): -- Each participant proposes initial response -- k-peer sampling per round -- Confidence accumulation toward ฮฒโ‚ - -Phase II (Finality): -- Threshold aggregation -- ฮฒโ‚‚ finality threshold -- Winner synthesis - -Reference: https://github.com/luxfi/consensus -""" - -import random -import asyncio -from typing import Any, Dict, List, TypeVar, Callable, Optional, Coroutine -from dataclasses import field, dataclass - -T = TypeVar("T") - - -@dataclass -class Result: - """Participant result.""" - - id: str - output: str - ok: bool - error: Optional[str] = None - ms: int = 0 - round: int = 0 - - -@dataclass -class State: - """Consensus state.""" - - prompt: str - participants: List[str] - rounds: int - k: int # Sample size per round - alpha: float # Agreement threshold (0-1) - beta_1: float # Preference threshold (Phase I) - beta_2: float # Decision threshold (Phase II) - - # State - responses: Dict[str, List[str]] = field(default_factory=dict) - confidence: Dict[str, float] = field(default_factory=dict) - luminance: Dict[str, float] = field(default_factory=dict) - finalized: bool = False - winner: Optional[str] = None - synthesis: Optional[str] = None - discussion_history: List[Dict[str, str]] = field(default_factory=list) - - -@dataclass -class Consensus: - """Metastable consensus protocol. - - Args: - participants: List of participant IDs - execute: Async function (id, prompt) -> Result - rounds: Number of sampling rounds (default: 3) - k: Sample size per round (default: 3) - alpha: Agreement threshold (default: 0.6) - beta_1: Preference threshold (default: 0.5) - beta_2: Decision threshold (default: 0.8) - """ - - participants: List[str] - execute: Callable[[str, str], Coroutine[Any, Any, Result]] - rounds: int = 3 - k: int = 3 - alpha: float = 0.6 - beta_1: float = 0.5 - beta_2: float = 0.8 - - async def run(self, prompt: str) -> State: - """Run consensus protocol.""" - state = State( - prompt=prompt, - participants=self.participants, - rounds=self.rounds, - k=min(self.k, len(self.participants)), - alpha=self.alpha, - beta_1=self.beta_1, - beta_2=self.beta_2, - ) - - # Initialize - for p in self.participants: - state.luminance[p] = 1.0 - state.confidence[p] = 0.0 - state.responses[p] = [] - - # Phase I: Sampling - initial proposals - initial = await asyncio.gather( - *[self.execute(p, prompt) for p in self.participants] - ) - - for r in initial: - state.responses[r.id].append(r.output) - if r.ok and r.ms > 0: - # Faster = higher luminance - state.luminance[r.id] = 1.0 / (1.0 + r.ms / 1000.0) - - # Sampling rounds - for _ in range(self.rounds): - # Luminance-weighted peer selection - weights = [state.luminance[p] for p in self.participants] - total = sum(weights) - sampled = random.choices( - self.participants, [w / total for w in weights], k=state.k - ) - - # Build context from sampled peers - context = [f"Query: {prompt}", "", "Peers:"] - for p in sampled: - if state.responses[p]: - context.append(f"[{p}] {state.responses[p][-1][:1000]}") - context.append("\nRefine your response:") - - # Each participant refines - round_results = await asyncio.gather( - *[self.execute(p, "\n".join(context)) for p in self.participants] - ) - - for r in round_results: - state.responses[r.id].append(r.output) - if r.ok: - # Agreement metric - agreement = self._agreement(r.output, sampled, state) - state.confidence[r.id] = ( - state.confidence[r.id] * 0.5 + agreement * 0.5 - ) - - # Check ฮฒโ‚ threshold - if max(state.confidence.values()) >= self.beta_1: - break - - # Phase II: Finality - scores = { - p: state.confidence[p] * state.luminance[p] for p in self.participants - } - state.winner = max(scores, key=lambda p: scores[p]) - - if scores[state.winner] >= self.beta_2: - state.finalized = True - - # Synthesis - if state.responses[state.winner]: - state.synthesis = state.responses[state.winner][-1] - - return state - - def _agreement(self, output: str, sampled: List[str], state: State) -> float: - """Calculate agreement with sampled peers.""" - if not sampled: - return 0.0 - - total = 0.0 - for p in sampled: - if state.responses[p]: - r_words = set(output.lower().split()) - p_words = set(state.responses[p][-1].lower().split()) - if r_words and p_words: - overlap = len(r_words & p_words) / len(r_words | p_words) - total += overlap - - return total / len(sampled) - - -async def run( - prompt: str, - participants: List[str], - execute: Callable[[str, str], Coroutine[Any, Any, Result]], - rounds: int = 3, - k: int = 3, - alpha: float = 0.6, - beta_1: float = 0.5, - beta_2: float = 0.8, -) -> State: - """Run metastable consensus. - - Args: - prompt: Query to reach consensus on - participants: List of participant IDs - execute: Async function (id, prompt) -> Result - rounds: Sampling rounds - k: Sample size per round - alpha: Agreement threshold - beta_1: Preference threshold - beta_2: Decision threshold - - Returns: - Final consensus state - """ - consensus = Consensus( - participants=participants, - execute=execute, - rounds=rounds, - k=k, - alpha=alpha, - beta_1=beta_1, - beta_2=beta_2, - ) - return await consensus.run(prompt) diff --git a/pkg/hanzo-consensus/hanzo_consensus/mcp_mesh.py b/pkg/hanzo-consensus/hanzo_consensus/mcp_mesh.py deleted file mode 100644 index a2dcb7132..000000000 --- a/pkg/hanzo-consensus/hanzo_consensus/mcp_mesh.py +++ /dev/null @@ -1,312 +0,0 @@ -"""MCP mesh for agent-to-agent consensus. - -Each agent in consensus is available as MCP to every other. -Enables N rounds of tool calling/discussion between agents. - -Usage: - from hanzo_consensus import MCPMesh, run_mcp_consensus - - mesh = MCPMesh() - mesh.register("agent1", agent1_server) - mesh.register("agent2", agent2_server) - mesh.register("agent3", agent3_server) - - state = await run_mcp_consensus( - mesh=mesh, - prompt="Discuss the best approach", - rounds=10, - ) -""" - -import asyncio -from typing import Any, Dict, List, Callable, Optional, Coroutine -from dataclasses import field, dataclass - -from .consensus import State, Result, run - - -@dataclass -class MCPAgent: - """An agent in the MCP mesh.""" - - id: str - server: Any # FastMCP server - endpoint: Optional[str] = None # For remote agents - tools: List[str] = field(default_factory=list) - - async def call_tool(self, tool_name: str, **kwargs) -> str: - """Call a tool on this agent.""" - if self.server: - # Local agent - call directly - for tool in self.server._tools.values(): - if tool.name == tool_name: - return await tool.fn(**kwargs) - raise ValueError(f"Tool {tool_name} not found on agent {self.id}") - else: - # Remote agent - would use MCP client - raise NotImplementedError("Remote MCP agents not yet implemented") - - async def discuss(self, prompt: str, context: Dict[str, str]) -> str: - """Have this agent respond to a discussion prompt. - - Args: - prompt: The discussion prompt - context: Previous responses from other agents - - Returns: - Agent's response - """ - # Build context string from other agents' responses - context_str = "\n\n".join( - [f"[{agent_id}]: {response}" for agent_id, response in context.items()] - ) - - full_prompt = f"""Discussion prompt: {prompt} - -Previous responses: -{context_str} - -Please provide your perspective, building on or responding to the above.""" - - # Call the agent's "think" or "respond" tool - for tool_name in ["think", "respond", "discuss", "agent"]: - try: - return await self.call_tool(tool_name, prompt=full_prompt) - except (ValueError, KeyError): - continue - - # Fallback - return a placeholder - return f"[{self.id}] I acknowledge the discussion but have no specific tools to respond." - - -class MCPMesh: - """Mesh network of MCP agents. - - Enables agent-to-agent communication where each agent - can call tools on any other agent in the mesh. - """ - - def __init__(self): - self.agents: Dict[str, MCPAgent] = {} - self._lock = asyncio.Lock() - - def register( - self, - agent_id: str, - server: Any = None, - endpoint: Optional[str] = None, - ) -> MCPAgent: - """Register an agent in the mesh. - - Args: - agent_id: Unique identifier for the agent - server: Local FastMCP server instance - endpoint: Remote MCP endpoint URL - - Returns: - The registered MCPAgent - """ - if not server and not endpoint: - raise ValueError("Must provide either server or endpoint") - - # Extract available tools - tools = [] - if server and hasattr(server, "_tools"): - tools = list(server._tools.keys()) - - agent = MCPAgent( - id=agent_id, - server=server, - endpoint=endpoint, - tools=tools, - ) - self.agents[agent_id] = agent - return agent - - def unregister(self, agent_id: str) -> None: - """Remove an agent from the mesh.""" - self.agents.pop(agent_id, None) - - def get(self, agent_id: str) -> Optional[MCPAgent]: - """Get an agent by ID.""" - return self.agents.get(agent_id) - - @property - def participant_ids(self) -> List[str]: - """Get list of all agent IDs.""" - return list(self.agents.keys()) - - async def call( - self, - from_agent: str, - to_agent: str, - tool_name: str, - **kwargs, - ) -> str: - """Call a tool on one agent from another. - - Args: - from_agent: ID of the calling agent - to_agent: ID of the target agent - tool_name: Name of the tool to call - **kwargs: Tool arguments - - Returns: - Tool result - """ - target = self.agents.get(to_agent) - if not target: - raise ValueError(f"Agent {to_agent} not found in mesh") - - return await target.call_tool(tool_name, **kwargs) - - async def broadcast( - self, - from_agent: str, - tool_name: str, - **kwargs, - ) -> Dict[str, str]: - """Call a tool on all other agents. - - Args: - from_agent: ID of the calling agent - tool_name: Name of the tool to call - **kwargs: Tool arguments - - Returns: - Dict mapping agent_id -> result - """ - results = {} - tasks = [] - - for agent_id in self.agents: - if agent_id != from_agent: - - async def call_agent(aid: str): - try: - result = await self.agents[aid].call_tool(tool_name, **kwargs) - return (aid, result) - except Exception as e: - return (aid, f"Error: {e}") - - tasks.append(call_agent(agent_id)) - - for result in await asyncio.gather(*tasks): - results[result[0]] = result[1] - - return results - - -async def run_mcp_consensus( - mesh: MCPMesh, - prompt: str, - rounds: int = 10, - k: int = 3, - alpha: float = 0.6, - beta_1: float = 0.5, - beta_2: float = 0.8, -) -> State: - """Run Metastable consensus over an MCP mesh. - - Each round involves agents discussing with each other via MCP, - then voting on the best response. - - Args: - mesh: The MCP mesh of agents - prompt: The consensus prompt - rounds: Number of discussion rounds (default: 10) - k: Sample size per round - alpha: Agreement threshold - beta_1: Preference threshold - beta_2: Decision threshold - - Returns: - Consensus state with winner and synthesis - """ - # Track discussion history - discussion_history: List[Dict[str, str]] = [] - - async def execute(agent_id: str, round_prompt: str) -> Result: - """Execute one round of discussion for an agent.""" - import time - - start = time.time() - - agent = mesh.get(agent_id) - if not agent: - return Result( - id=agent_id, - output="", - ok=False, - error=f"Agent {agent_id} not found", - ) - - try: - # Get context from previous rounds - context = {} - if discussion_history: - # Use most recent round's responses - context = discussion_history[-1] - - response = await agent.discuss(round_prompt, context) - ms = int((time.time() - start) * 1000) - - return Result( - id=agent_id, - output=response, - ok=True, - ms=ms, - ) - except Exception as e: - return Result( - id=agent_id, - output="", - ok=False, - error=str(e), - ) - - # Custom round callback to track discussion - async def on_round_complete(round_num: int, responses: Dict[str, str]): - discussion_history.append(responses) - - # Run consensus with the mesh's agents - state = await run( - prompt=prompt, - participants=mesh.participant_ids, - execute=execute, - rounds=rounds, - k=min(k, len(mesh.agents)), - alpha=alpha, - beta_1=beta_1, - beta_2=beta_2, - ) - - # Attach discussion history to state - state.discussion_history = discussion_history - - return state - - -# Convenience function to create mesh from agent configs -def create_mesh( - agents: Dict[str, Any], -) -> MCPMesh: - """Create an MCP mesh from agent configurations. - - Args: - agents: Dict mapping agent_id -> server or endpoint - - Returns: - Configured MCPMesh - """ - mesh = MCPMesh() - - for agent_id, config in agents.items(): - if isinstance(config, str): - # URL endpoint - mesh.register(agent_id, endpoint=config) - else: - # Server instance - mesh.register(agent_id, server=config) - - return mesh diff --git a/pkg/hanzo-consensus/pyproject.toml b/pkg/hanzo-consensus/pyproject.toml deleted file mode 100644 index a8dc531a7..000000000 --- a/pkg/hanzo-consensus/pyproject.toml +++ /dev/null @@ -1,29 +0,0 @@ -[project] -name = "hanzo-consensus" -version = "0.1.0" -description = "Metastable consensus protocol for multi-agent agreement" -readme = "README.md" -requires-python = ">=3.12" -license = "MIT" -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["consensus", "metastable", "multi-agent", "distributed"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", -] -dependencies = [] - -[project.urls] -Homepage = "https://github.com/hanzoai/python-sdk" -Repository = "https://github.com/hanzoai/python-sdk" -Documentation = "https://github.com/luxfi/consensus" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_consensus"] diff --git a/pkg/hanzo-dev/README.md b/pkg/hanzo-dev/README.md deleted file mode 100644 index bc07c05bd..000000000 --- a/pkg/hanzo-dev/README.md +++ /dev/null @@ -1,216 +0,0 @@ -# Hanzo Dev - -Interactive dev environment for Hanzo AI - Like Claude Code in your terminal. - -## Features - -- ๐ŸŽฏ **Direct MCP Access**: All 70+ MCP tools available as Python functions -- ๐Ÿ’ฌ **Integrated Chat**: Chat with AI that can use MCP tools -- ๐Ÿ”ง **IPython Magic**: Advanced features with tab completion and magic commands -- ๐ŸŽจ **Beautiful TUI**: Textual-based terminal UI with syntax highlighting -- ๐Ÿ”„ **Live Editing**: Edit code and see results immediately -- ๐ŸŽค **Voice Mode**: Speak to AI and hear responses (optional) - -## Quick Start - -```bash -# Install -pip install hanzo-dev - -# Start interactive dev environment (recommended) -hanzo-dev - -# Start IPython mode (advanced) -hanzo-dev-ipython - -# Start TUI mode (beautiful interface) -hanzo-dev-tui -``` - -## Usage - -### Basic CLI Usage - -The REPL integrates seamlessly with the Hanzo CLI: - -```bash -# Interactive chat mode -hanzo chat - -# Quick questions -hanzo ask "What files are in the current directory?" - -# With specific model -hanzo ask "Explain this code" --model claude-3-opus -``` - -### REPL Commands - -```python -# Direct tool access ->>> read_file(file_path="README.md") ->>> write_file(file_path="test.py", content="print('Hello')") ->>> search(query="def main", path=".") - -# Chat with AI ->>> chat("Create a Python script that fetches weather data") - -# AI will use tools automatically ->>> chat("Find all TODO comments in the codebase and create a summary") -``` - -### IPython Magic Commands - -```python -# Quick chat -%chat What is 2+2? - -# Multi-line chat -%%ai -Help me refactor this function to be more efficient. -It should handle edge cases better. - -# List tools -%tools - -# Change model -%model claude-3.5-sonnet - -# Execute tool -%tool read_file {"file_path": "config.json"} -``` - -### TUI Mode Features - -- **Split panes**: Code editor, chat, and output -- **Syntax highlighting**: Full language support -- **Tool palette**: Visual tool selection -- **History**: Navigate previous commands -- **Themes**: Dark/light mode support - -## Environment Setup - -Set at least one LLM provider: - -```bash -export ANTHROPIC_API_KEY=your-key # For Claude -export OPENAI_API_KEY=your-key # For GPT -export HANZO_API_KEY=your-key # For Hanzo AI -``` - -## Advanced Features - -### Voice Mode - -Install voice dependencies: - -```bash -pip install hanzo-dev[voice] -``` - -Enable in REPL: - -```python ->>> enable_voice() ->>> chat("Hello") # Speak your message -``` - -### Custom Tools - -Create custom tools on the fly: - -```python -@register_tool -def my_tool(param: str) -> str: - """My custom tool.""" - return f"Processed: {param}" - -# Now available to AI ->>> chat("Use my_tool to process 'hello'") -``` - -### Scripting - -Use the REPL in scripts: - -```python -from hanzo_dev import create_repl - -async def main(): - repl = create_repl() - result = await repl.chat("Analyze the project structure") - print(result) -``` - -## Integration with Hanzo Ecosystem - -### With Hanzo MCP - -All MCP tools are automatically available: - -- File operations -- Code search and analysis -- Process management -- Git operations -- And 60+ more tools - -### With Hanzo Agents - -Create and manage agents: - -```python ->>> agent = create_agent("researcher") ->>> agent.run("Research best practices for API design") -``` - -### With Hanzo Network - -Dispatch to agent networks: - -```python ->>> network.dispatch("Solve this problem", agents=5) -``` - -## Tips - -1. **Tab Completion**: Use Tab to explore available tools and parameters -2. **Help System**: Use `?` after any function for documentation -3. **History**: Use up/down arrows to navigate command history -4. **Shortcuts**: Ctrl+R for reverse search, Ctrl+L to clear screen -5. **Output**: Results are automatically pretty-printed with Rich - -## Troubleshooting - -### No LLM Response - -Ensure you have set API keys: - -```bash -echo $ANTHROPIC_API_KEY # Should show your key -``` - -### Tool Errors - -Check tool permissions: - -```python ->>> mcp.get_allowed_paths() ->>> mcp.add_allowed_path("/path/to/allow") -``` - -### Performance - -For better performance: - -```python ->>> set_model("gpt-3.5-turbo") # Faster model ->>> set_streaming(True) # Stream responses -``` - -## Contributing - -The REPL is part of the Hanzo Python SDK. See the main repository for contribution guidelines. - -## License - -BSD-3-Clause - see LICENSE file for details. \ No newline at end of file diff --git a/pkg/hanzo-dev/pyproject.toml b/pkg/hanzo-dev/pyproject.toml deleted file mode 100644 index 289019c27..000000000 --- a/pkg/hanzo-dev/pyproject.toml +++ /dev/null @@ -1,101 +0,0 @@ -[project] -name = "hanzo-dev" -version = "0.1.0" -description = "Interactive dev environment for Hanzo AI - Like Claude Code in your terminal" -authors = [ - {name = "Hanzo AI", email = "dev@hanzo.ai"}, -] -readme = "README.md" -license = {text = "BSD-3-Clause"} -classifiers = [ - "Development Status :: 4 - Beta", - "Environment :: Console", - "Intended Audience :: Developers", - "License :: OSI Approved :: BSD License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Software Development :: Testing", - "Topic :: Scientific/Engineering :: Artificial Intelligence", -] -keywords = ["dev", "mcp", "ai", "llm", "hanzo", "claude", "interactive"] -requires-python = ">=3.12" - -dependencies = [ - "hanzoai>=2.2.0", - "hanzo-mcp>=0.1.0", - "rich>=13.0.0", - "prompt-toolkit>=3.0.0", - "click>=8.0.0", - "python-dotenv>=1.0.0", - "hanzo-llm>=1.0.0", - "colorama>=0.4.6", - "pygments>=2.17.0", - "ipython>=8.0.0", - "textual>=0.41.0", -] - -[project.urls] -Homepage = "https://hanzo.ai" -Documentation = "https://docs.hanzo.ai/dev" -Repository = "https://github.com/hanzoai/python-sdk" -Issues = "https://github.com/hanzoai/python-sdk/issues" - -[project.optional-dependencies] -lsp = ["hanzo-lsp>=0.1.0"] -hooks = ["hanzo-hooks>=0.1.0"] -sandbox = ["hanzo-sandbox>=0.1.0"] -voice = [ - "speechrecognition>=3.10.0", - "pyttsx3>=2.90", - "sounddevice>=0.4.6", - "numpy>=1.24.0", - # pyaudio is optional and requires system dependencies - # Install with: pip install pyaudio (requires portaudio system library) -] -dev = [ - "pytest>=7.0.0", - "pytest-asyncio>=0.21.0", - "pytest-cov>=4.0.0", - "black>=23.0.0", - "ruff>=0.1.0", - "mypy>=1.0.0", -] - -[project.scripts] -hanzo-dev = "hanzo_dev.cli:main" -hanzo-dev-ipython = "hanzo_dev.ipython_repl:main" -hanzo-dev-tui = "hanzo_dev.textual_repl:main" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build] -include = [ - "src/hanzo_dev", - "README.md", -] - -[tool.hatch.build.targets.wheel] -packages = ["src/hanzo_dev"] - -[tool.ruff] -line-length = 120 -target-version = "py312" - -[tool.ruff.lint] -select = ["E", "F", "I", "B", "UP", "N", "S", "A", "C4", "T20", "RET", "SIM", "ARG"] -ignore = ["E501", "S101"] - -[tool.mypy] -python_version = "3.12" -strict = true -warn_return_any = true -warn_unused_configs = true - -[tool.pytest.ini_options] -testpaths = ["tests"] -pythonpath = ["src"] -asyncio_mode = "auto" \ No newline at end of file diff --git a/pkg/hanzo-dev/src/hanzo_dev/__init__.py b/pkg/hanzo-dev/src/hanzo_dev/__init__.py deleted file mode 100644 index ba79d0de2..000000000 --- a/pkg/hanzo-dev/src/hanzo_dev/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Hanzo REPL - Interactive environment for testing MCP tools and AI integration.""" - -__version__ = "0.1.0" diff --git a/pkg/hanzo-dev/src/hanzo_dev/backends.py b/pkg/hanzo-dev/src/hanzo_dev/backends.py deleted file mode 100644 index db7b73f69..000000000 --- a/pkg/hanzo-dev/src/hanzo_dev/backends.py +++ /dev/null @@ -1,429 +0,0 @@ -"""Backend implementations for different AI CLI tools.""" - -import asyncio -import json -import os -import subprocess # noqa: S404 -from abc import ABC, abstractmethod -from pathlib import Path -from typing import Dict, List, Optional - -from rich.console import Console - - -class Backend(ABC): - """Abstract base class for AI backends.""" - - @abstractmethod - async def chat(self, message: str, tools: Optional[List[Dict]] = None) -> str: - """Send a chat message and get response.""" - pass - - @abstractmethod - def get_config_file(self) -> str: - """Get the configuration file name for this backend.""" - pass - - @abstractmethod - def is_available(self) -> bool: - """Check if this backend is available.""" - pass - - -class ClaudeCodeBackend(Backend): - """Claude Code CLI backend with personal account support.""" - - def __init__(self): - self.cli_path = self._find_claude_code() - self.authenticated = False - - def _find_claude_code(self) -> Optional[str]: - """Find Claude Code CLI executable.""" - # Check common locations - paths = [ - "claude", # In PATH - "/usr/local/bin/claude", - "/opt/homebrew/bin/claude", - os.path.expanduser("~/.local/bin/claude"), - ] - - for path in paths: - try: - result = subprocess.run( # noqa: S603 - [path, "--version"], capture_output=True, text=True - ) # Safe: running known executable with fixed arguments - if result.returncode == 0: - return path - except FileNotFoundError: - continue - - return None - - def is_available(self) -> bool: - """Check if Claude Code is available.""" - if self.cli_path is None: - return False - - # Check if already authenticated - self.authenticated = self._check_auth() - return True - - def _check_auth(self) -> bool: - """Check if Claude Code is authenticated.""" - if not self.cli_path: - return False - - try: - # Claude Code should have an auth status command - result = subprocess.run( # noqa: S603 - [self.cli_path, "auth", "status"], capture_output=True, text=True - ) # Safe: running validated Claude executable with fixed arguments - - # Check if authenticated (Claude Code should indicate this) - return result.returncode == 0 and "authenticated" in result.stdout.lower() - - except Exception: - return False - - async def authenticate(self) -> bool: - """Authenticate with Claude using personal account.""" - if not self.cli_path: - raise RuntimeError("Claude Code CLI not found") - - console = Console() - console.print("[yellow]Opening browser for Claude authentication...[/yellow]") - - # Claude Code auth login command - proc = await asyncio.create_subprocess_exec( - self.cli_path, - "auth", - "login", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - - stdout, stderr = await proc.communicate() - - if proc.returncode == 0: - console.print("[green]Successfully authenticated with Claude![/green]") - self.authenticated = True - return True - console.print(f"[red]Authentication failed: {stderr.decode()}[/red]") - return False - - def get_config_file(self) -> str: - """Get config file for Claude Code.""" - return "CLAUDE.md" - - async def chat(self, message: str, tools: Optional[List[Dict]] = None) -> str: - """Send message to Claude Code.""" - if not self.cli_path: - raise RuntimeError("Claude Code CLI not found") - - # Check authentication - if not self.authenticated and not self._check_auth(): - console = Console() - console.print( - "[yellow]Not authenticated with Claude. Attempting login...[/yellow]" - ) - if not await self.authenticate(): - raise RuntimeError("Failed to authenticate with Claude") - - # Prepare command - Claude Code uses simple message format when authenticated - cmd = [self.cli_path, message] - - # Add any additional flags for MCP tools if needed - if tools: - # Claude Code might handle tools differently when using personal account - # May need to use --mcp flag or similar - cmd.insert(1, "--with-tools") - - # Run command - proc = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - stdin=asyncio.subprocess.PIPE, # For interactive mode if needed - ) - - stdout, stderr = await proc.communicate() - - if proc.returncode != 0: - # Check if it's an auth error - if ( - "unauthorized" in stderr.decode().lower() - or "auth" in stderr.decode().lower() - ): - self.authenticated = False - raise RuntimeError( - "Claude authentication expired. Please re-authenticate." - ) - raise RuntimeError(f"Claude Code error: {stderr.decode()}") - - return stdout.decode() - - async def logout(self) -> None: - """Logout from Claude account.""" - if self.cli_path: - proc = await asyncio.create_subprocess_exec( - self.cli_path, - "auth", - "logout", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - await proc.communicate() - self.authenticated = False - - -class OpenAICodexBackend(Backend): - """OpenAI Codex/GPT CLI backend.""" - - def __init__(self): - self.cli_path = self._find_openai_cli() - - def _find_openai_cli(self) -> Optional[str]: - """Find OpenAI CLI executable.""" - paths = [ - "openai", # In PATH - "gpt", # Alternative name - "/usr/local/bin/openai", - os.path.expanduser("~/.local/bin/openai"), - ] - - for path in paths: - try: - result = subprocess.run( # noqa: S603 - [path, "--version"], capture_output=True, text=True - ) # Safe: running known executable with fixed arguments - if result.returncode == 0: - return path - except FileNotFoundError: - continue - - return None - - def is_available(self) -> bool: - """Check if OpenAI CLI is available.""" - return self.cli_path is not None or os.getenv("OPENAI_API_KEY") - - def get_config_file(self) -> str: - """Get config file for OpenAI.""" - return "AGENTS.md" - - async def chat(self, message: str, tools: Optional[List[Dict]] = None) -> str: - """Send message to OpenAI.""" - if self.cli_path: - # Use CLI - cmd = [self.cli_path, "api", "chat.completions.create"] - - # Build request - request = { - "model": "gpt-4-turbo-preview", - "messages": [{"role": "user", "content": message}], - } - - if tools: - request["tools"] = tools - request["tool_choice"] = "auto" - - cmd.extend(["-g", json.dumps(request)]) - - proc = await asyncio.create_subprocess_exec( - *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE - ) - - stdout, stderr = await proc.communicate() - - if proc.returncode != 0: - raise RuntimeError(f"OpenAI CLI error: {stderr.decode()}") - - # Parse response - response = json.loads(stdout.decode()) - return response["choices"][0]["message"]["content"] - - # Use API directly - import openai - - client = openai.AsyncOpenAI() - - messages = [{"role": "user", "content": message}] - - if tools: - response = await client.chat.completions.create( - model="gpt-4-turbo-preview", - messages=messages, - tools=tools, - tool_choice="auto", - ) - else: - response = await client.chat.completions.create( - model="gpt-4-turbo-preview", messages=messages - ) - - return response.choices[0].message.content - - -class HanzoDevBackend(Backend): - """Hanzo Dev AI backend.""" - - def __init__(self): - self.cli_path = self._find_hanzo_dev() - - def _find_hanzo_dev(self) -> Optional[str]: - """Find hanzo-dev executable.""" - paths = [ - "hanzo-dev", - "/usr/local/bin/hanzo-dev", - os.path.expanduser("~/.local/bin/hanzo-dev"), - # Check in parent directory - os.path.join( - os.path.dirname(os.path.dirname(__file__)), "..", "dev", "hanzo-dev" - ), - ] - - for path in paths: - if os.path.exists(path) and os.access(path, os.X_OK): - return path - - return None - - def is_available(self) -> bool: - """Check if hanzo-dev is available.""" - return self.cli_path is not None - - def get_config_file(self) -> str: - """Get config file for hanzo-dev.""" - return "LLM.md" - - async def chat(self, message: str, tools: Optional[List[Dict]] = None) -> str: - """Send message to hanzo-dev.""" - if not self.cli_path: - raise RuntimeError("hanzo-dev not found") - - cmd = [self.cli_path, "chat", "--message", message] - - if tools: - cmd.extend(["--tools", json.dumps(tools)]) - - proc = await asyncio.create_subprocess_exec( - *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE - ) - - stdout, stderr = await proc.communicate() - - if proc.returncode != 0: - raise RuntimeError(f"hanzo-dev error: {stderr.decode()}") - - return stdout.decode() - - -class EmbeddedBackend(Backend): - """Embedded LLM backend using llm.""" - - def __init__(self, llm_client): - self.llm_client = llm_client - - def is_available(self) -> bool: - """Check if embedded backend is available.""" - return bool(self.llm_client.get_available_providers()) - - def get_config_file(self) -> str: - """Get config file for embedded backend.""" - return "LLM.md" - - async def chat(self, message: str, tools: Optional[List[Dict]] = None) -> str: - """Send message to embedded LLM.""" - messages = [ - { - "role": "system", - "content": "You are a helpful assistant with access to tools.", - }, - {"role": "user", "content": message}, - ] - - response = await self.llm_client.chat( - messages=messages, tools=tools, tool_choice="auto" if tools else None - ) - - return response.choices[0].message.content - - -class BackendManager: - """Manages different AI backends.""" - - def __init__(self, llm_client=None): - self.backends = { - "claude": ClaudeCodeBackend(), - "openai": OpenAICodexBackend(), - "hanzo-dev": HanzoDevBackend(), - "embedded": EmbeddedBackend(llm_client) if llm_client else None, - } - - self.current_backend = None - self._auto_select_backend() - - def _auto_select_backend(self): - """Auto-select the best available backend.""" - # Priority order - priority = ["claude", "openai", "hanzo-dev", "embedded"] - - for name in priority: - backend = self.backends.get(name) - if backend and backend.is_available(): - self.current_backend = name - break - - def set_backend(self, name: str): - """Set the current backend.""" - if name not in self.backends: - raise ValueError(f"Unknown backend: {name}") - - backend = self.backends[name] - if not backend.is_available(): - raise ValueError(f"Backend {name} is not available") - - self.current_backend = name - - def get_backend(self) -> Backend: - """Get the current backend.""" - if not self.current_backend: - raise RuntimeError("No backend available") - - return self.backends[self.current_backend] - - def list_backends(self) -> Dict[str, bool]: - """List all backends and their availability.""" - return { - name: backend.is_available() if backend else False - for name, backend in self.backends.items() - } - - async def load_config(self) -> Optional[str]: - """Load configuration from the appropriate file.""" - backend = self.get_backend() - config_file = backend.get_config_file() - - # Look for config file in current directory and parent directories - paths = [ - Path.cwd() / config_file, - Path.cwd().parent / config_file, - Path.home() / f".config/hanzo/{config_file}", - ] - - for path in paths: - if path.exists(): - return path.read_text() - - return None - - async def chat(self, message: str, tools: Optional[List[Dict]] = None) -> str: - """Send chat message using current backend.""" - backend = self.get_backend() - - # Load and prepend config if available - config = await self.load_config() - if config: - message = f"Configuration:\n{config}\n\nUser: {message}" - - return await backend.chat(message, tools) diff --git a/pkg/hanzo-dev/src/hanzo_dev/cli.py b/pkg/hanzo-dev/src/hanzo_dev/cli.py deleted file mode 100644 index 4d8016891..000000000 --- a/pkg/hanzo-dev/src/hanzo_dev/cli.py +++ /dev/null @@ -1,47 +0,0 @@ -"""CLI entry point for Hanzo REPL.""" - -import asyncio - -import click - -from .ipython_repl import main as ipython_main -from .repl import HanzoREPL - - -@click.command() -@click.option( - "--mode", - default="ipython", - type=click.Choice(["basic", "ipython", "tui"]), - help="REPL mode to use", -) -@click.option("--debug", is_flag=True, help="Enable debug mode") -@click.option("--model", help="LLM model to use") -@click.option( - "--config", - "config_home", - default=None, - help="Path to config home (default: ~/.hanzo)", -) -def main(mode, debug, model, config_home): - """Hanzo REPL - Interactive testing environment.""" - import os - - if config_home: - os.environ["HANZO_CONFIG_HOME"] = config_home - - if mode == "tui": - from .textual_repl import main as tui_main - tui_main() - elif mode == "ipython": - # Use IPython-based REPL (recommended) - ipython_main() - else: - # Use basic REPL - config = {"debug": debug, "model": model} - repl = HanzoREPL(config) - asyncio.run(repl.run()) - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-dev/src/hanzo_dev/command_palette.py b/pkg/hanzo-dev/src/hanzo_dev/command_palette.py deleted file mode 100644 index 27265fca9..000000000 --- a/pkg/hanzo-dev/src/hanzo_dev/command_palette.py +++ /dev/null @@ -1,288 +0,0 @@ -"""Command palette widget for MCP tool selection.""" - -from dataclasses import dataclass -from typing import Any, Dict, List, Optional - -from textual import events -from textual.app import ComposeResult -from textual.containers import Horizontal, Vertical -from textual.message import Message -from textual.reactive import reactive -from textual.widgets import Input, Label, ListItem, ListView, Static - - -@dataclass -class Command: - """Represents a command/tool.""" - - name: str - description: str - category: str - icon: str = "โšก" - usage_count: int = 0 - parameters: Optional[Dict[str, Any]] = None - - -class CommandSelected(Message): - """Message sent when a command is selected.""" - - def __init__(self, command: Command) -> None: - self.command = command - super().__init__() - - -class CommandItem(ListItem): - """A command item in the list.""" - - def __init__(self, command: Command) -> None: - super().__init__() - self.command = command - - def compose(self) -> ComposeResult: - """Create child widgets.""" - with Horizontal(classes="command-item"): - # Icon and category - yield Static( - f"{self.command.icon} [{self.command.category}]", classes="command-icon" - ) - - # Name and description - with Vertical(classes="command-info"): - yield Static(self.command.name, classes="command-name") - yield Static( - self.command.description[:60] + "...", classes="command-desc" - ) - - -class CommandPalette(Vertical): - """Command palette overlay widget.""" - - CSS = """ - CommandPalette { - layer: overlay; - width: 80%; - height: 60%; - background: $panel; - border: thick $primary; - padding: 1; - align: center middle; - offset: 10% 20%; - } - - #palette-input { - dock: top; - height: 3; - margin-bottom: 1; - background: $background; - border: tall $secondary; - padding: 0 1; - } - - #palette-list { - height: 1fr; - background: $background; - border: none; - overflow-y: auto; - } - - .command-item { - padding: 0 1; - height: 3; - } - - .command-item:hover { - background: $boost; - } - - .command-icon { - width: 20; - color: $primary; - } - - .command-info { - width: 1fr; - } - - .command-name { - text-style: bold; - } - - .command-desc { - color: $text-muted; - text-style: italic; - } - - #palette-hint { - dock: bottom; - height: 1; - color: $text-muted; - text-align: center; - margin-top: 1; - } - """ - - commands = reactive([], always_update=True) - filtered_commands = reactive([], always_update=True) - - def __init__(self, tools: Dict[str, Any]) -> None: - super().__init__(id="command-palette") - self.all_commands = self._build_commands(tools) - self.commands = self.all_commands - self.filtered_commands = self.all_commands - - def _build_commands(self, tools: Dict[str, Any]) -> List[Command]: - """Build command list from MCP tools.""" - commands = [] - - # Tool categories with icons - category_icons = { - "file": "๐Ÿ“", - "search": "๐Ÿ”", - "shell": "๐Ÿ’ป", - "agent": "๐Ÿค–", - "llm": "๐Ÿง ", - "editor": "โœ๏ธ", - "todo": "โœ…", - "vector": "๐Ÿ”—", - "database": "๐Ÿ—„๏ธ", - } - - for name, tool in sorted(tools.items()): - # Determine category from tool module - category = "general" - if hasattr(tool, "__module__"): - parts = tool.__module__.split(".") - if len(parts) > 2: - category = parts[-1] - - icon = category_icons.get(category, "โšก") - - commands.append( - Command( - name=name, - description=tool.description, - category=category, - icon=icon, - parameters=( - tool.get_schema() if hasattr(tool, "get_schema") else None - ), - ) - ) - - # Add some special commands - commands.extend( - [ - Command("clear", "Clear the chat history", "system", "๐Ÿ—‘๏ธ"), - Command("help", "Show help and shortcuts", "system", "โ“"), - Command("model", "Change AI model", "system", "๐Ÿ”„"), - Command("theme", "Change theme", "system", "๐ŸŽจ"), - ] - ) - - return commands - - def compose(self) -> ComposeResult: - """Create child widgets.""" - yield Input( - placeholder="Search commands... (fuzzy matching enabled)", - id="palette-input", - ) - yield ListView(id="palette-list") - yield Label( - "โ†‘โ†“ Navigate ยท โŽ Select ยท esc Close ยท โ‡ฅ Complete", id="palette-hint" - ) - - def on_mount(self) -> None: - """Focus input when mounted.""" - self.query_one("#palette-input", Input).focus() - self._update_list() - - def _fuzzy_match(self, query: str, text: str) -> bool: - """Simple fuzzy matching.""" - query = query.lower() - text = text.lower() - - # Direct substring match - if query in text: - return True - - # Character-by-character fuzzy match - query_idx = 0 - for char in text: - if query_idx < len(query) and char == query[query_idx]: - query_idx += 1 - - return query_idx == len(query) - - def on_input_changed(self, event: Input.Changed) -> None: - """Filter commands as user types.""" - query = event.value.strip() - - if not query: - self.filtered_commands = self.all_commands - else: - # Fuzzy filter - filtered = [] - for cmd in self.all_commands: - if ( - self._fuzzy_match(query, cmd.name) - or self._fuzzy_match(query, cmd.description) - or self._fuzzy_match(query, cmd.category) - ): - filtered.append(cmd) - - # Sort by relevance (prefer name matches) - filtered.sort( - key=lambda c: ( - not c.name.lower().startswith(query.lower()), - query.lower() not in c.name.lower(), - -c.usage_count, - ) - ) - - self.filtered_commands = filtered - - self._update_list() - - def _update_list(self) -> None: - """Update the command list.""" - list_view = self.query_one("#palette-list", ListView) - list_view.clear() - - for cmd in self.filtered_commands[:20]: # Limit to 20 items - list_view.append(CommandItem(cmd)) - - def on_list_view_selected(self, event: ListView.Selected) -> None: - """Handle command selection.""" - if isinstance(event.item, CommandItem): - self.post_message(CommandSelected(event.item.command)) - self.remove() - - def on_key(self, event: events.Key) -> None: - """Handle keyboard shortcuts.""" - if event.key == "escape": - self.remove() - elif event.key == "tab": - # Auto-complete to first match - if self.filtered_commands: - input_widget = self.query_one("#palette-input", Input) - input_widget.value = self.filtered_commands[0].name - elif event.key == "enter": - # Execute first match or AI complete - input_widget = self.query_one("#palette-input", Input) - value = input_widget.value.strip() - - if self.filtered_commands: - # Use first match - self.post_message(CommandSelected(self.filtered_commands[0])) - elif value: - # Try AI completion - ai_cmd = Command( - name="ai_complete", - description=f"AI: {value}", - category="ai", - icon="โœจ", - ) - self.post_message(CommandSelected(ai_cmd)) - - self.remove() diff --git a/pkg/hanzo-dev/src/hanzo_dev/command_suggestions.py b/pkg/hanzo-dev/src/hanzo_dev/command_suggestions.py deleted file mode 100644 index 9f47eb4bf..000000000 --- a/pkg/hanzo-dev/src/hanzo_dev/command_suggestions.py +++ /dev/null @@ -1,195 +0,0 @@ -"""Command suggestions widget for slash commands.""" - -from dataclasses import dataclass -from typing import List, Optional - -from rich.text import Text -from textual.app import ComposeResult -from textual.containers import Vertical -from textual.reactive import reactive -from textual.widgets import Static - - -@dataclass -class SlashCommand: - """Represents a slash command.""" - - command: str - description: str - aliases: Optional[List[str]] = None - - def matches(self, query: str) -> bool: - """Check if command matches query.""" - query = query.lower() - if self.command.lower().startswith(query): - return True - if self.aliases: - return any(alias.lower().startswith(query) for alias in self.aliases) - return False - - -class CommandSuggestions(Vertical): - """Command suggestions dropdown widget.""" - - CSS = """ - CommandSuggestions { - layer: overlay; - width: auto; - max-width: 100; - height: auto; - max-height: 20; - background: $panel; - border: tall $primary; - padding: 0 1; - offset-y: -100%; - margin-bottom: 1; - } - - .suggestion-header { - padding: 1 0; - border-bottom: solid $secondary; - margin-bottom: 1; - } - - .suggestion-item { - padding: 0 1; - height: 2; - } - - .suggestion-item.selected { - background: $boost; - } - - .command-name { - color: $primary; - text-style: bold; - } - - .command-desc { - color: $text-muted; - } - """ - - COMMANDS = [ - SlashCommand("/add-dir", "Add a new working directory"), - SlashCommand("/auth", "Authenticate with Claude personal account"), - SlashCommand("/auto", "Execute prompt autonomously with tools"), - SlashCommand("/backend", "Switch AI backend (claude/openai/embedded)"), - SlashCommand("/branch", "List or create git branches"), - SlashCommand("/bug", "Submit feedback about Hanzo Dev"), - SlashCommand("/clear", "Clear session history and start fresh"), - SlashCommand("/code", "Implement with consensus from multiple review passes"), - SlashCommand("/commit", "Commit staged changes"), - SlashCommand("/commit-push-pr", "Commit, push, and create PR"), - SlashCommand("/compact", "Compact session keeping a summary in context"), - SlashCommand("/config", "Show loaded configuration files", ["theme"]), - SlashCommand("/cost", "Show total cost and duration of current session"), - SlashCommand("/diff", "Show working tree diff"), - SlashCommand("/doctor", "Check the health of your Hanzo installation"), - SlashCommand("/exit", "Exit the REPL", ["quit"]), - SlashCommand("/export", "Export conversation to file"), - SlashCommand("/fast", "Toggle fast/standard mode"), - SlashCommand("/file", "Read or open a file"), - SlashCommand("/help", "Show help and available commands"), - SlashCommand("/history", "Show command history"), - SlashCommand("/import", "Import conversation from file"), - SlashCommand("/init", "Create starter CLAUDE.md in cwd"), - SlashCommand("/login", "Authenticate via PKCE flow"), - SlashCommand("/logout", "Logout from Claude account"), - SlashCommand("/loop", "Run prompt on interval (e.g. /loop 10 check status)"), - SlashCommand("/memorize", "Save a snippet for later recall"), - SlashCommand("/memory", "Show loaded instruction files"), - SlashCommand("/model", "Show or switch active AI model"), - SlashCommand("/permissions", "Show/switch permission mode"), - SlashCommand("/plan", "Planning agent - create a detailed plan"), - SlashCommand("/providers", "List available LLM providers"), - SlashCommand("/remote-control", "Start WebSocket server for remote control"), - SlashCommand("/reset", "Reset conversation context"), - SlashCommand("/resume", "Load saved session from JSON file"), - SlashCommand("/run", "Run a shell command"), - SlashCommand("/search", "Search for content using MCP tools"), - SlashCommand("/stash", "Manage git stash (pop/list)"), - SlashCommand("/session", "List or switch saved sessions"), - SlashCommand("/solve", "Race multiple approaches, present best solution"), - SlashCommand("/status", "Model, session info, token usage"), - SlashCommand("/tools", "Show available MCP tools"), - SlashCommand("/version", "Show version info"), - SlashCommand("/voice", "Enable voice mode for bidirectional communication"), - SlashCommand("/worktree", "Manage git worktrees"), - ] - - selected_index = reactive(0) - filtered_commands = reactive(COMMANDS) - - def __init__(self, query: str = ""): - super().__init__(id="command-suggestions") - self.query = query - self._filter_commands() - - def _filter_commands(self) -> None: - """Filter commands based on query.""" - if not self.query or self.query == "/": - self.filtered_commands = self.COMMANDS - else: - query = self.query[1:] if self.query.startswith("/") else self.query - self.filtered_commands = [ - cmd for cmd in self.COMMANDS if cmd.matches(query) - ] - self.selected_index = 0 - - def compose(self) -> ComposeResult: - """Create child widgets.""" - # Header - yield Static( - Text( - "โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ\n" - f"โ”‚ > {self.query:<121}โ”‚\n" - "โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ", - style="dim", - ), - classes="suggestion-header", - ) - - # Commands list - for i, cmd in enumerate(self.filtered_commands[:10]): # Show max 10 - selected = "selected" if i == self.selected_index else "" - - # Format command and description - cmd_text = f"{cmd.command:<20}" - desc_text = cmd.description[:80] - - yield Static( - Text( - f" {cmd_text}{desc_text}", - style="bright_white" if selected else "white", - ), - classes=f"suggestion-item {selected}", - ) - - def on_mount(self) -> None: - """Focus when mounted.""" - self.refresh() - - def update_query(self, query: str) -> None: - """Update the search query.""" - self.query = query - self._filter_commands() - self.refresh() - - def move_selection_up(self) -> None: - """Move selection up.""" - if self.selected_index > 0: - self.selected_index -= 1 - self.refresh() - - def move_selection_down(self) -> None: - """Move selection down.""" - if self.selected_index < len(self.filtered_commands) - 1: - self.selected_index += 1 - self.refresh() - - def get_selected_command(self) -> Optional[str]: - """Get the selected command.""" - if 0 <= self.selected_index < len(self.filtered_commands): - return self.filtered_commands[self.selected_index].command - return None diff --git a/pkg/hanzo-dev/src/hanzo_dev/git_commands.py b/pkg/hanzo-dev/src/hanzo_dev/git_commands.py deleted file mode 100644 index 6c9910f5c..000000000 --- a/pkg/hanzo-dev/src/hanzo_dev/git_commands.py +++ /dev/null @@ -1,149 +0,0 @@ -"""Git slash commands for the Hanzo REPL.""" - -import subprocess -from pathlib import Path -from typing import Optional - - -def _run(args: list[str], cwd: Path) -> subprocess.CompletedProcess[str]: - return subprocess.run(args, cwd=cwd, capture_output=True, text=True) - - -def git_status(cwd: Path) -> str: - """Return git status summary.""" - r = _run(["git", "status", "--short", "--branch"], cwd) - if r.returncode != 0: - return r.stderr.strip() - lines = [l for l in r.stdout.strip().splitlines() if not l.startswith("##")] - if not lines: - return "Nothing to commit, working tree clean." - branch_line = r.stdout.strip().splitlines()[0] if r.stdout.strip() else "" - return (branch_line + "\n" + "\n".join(lines)).strip() - - -def git_branch(args: str, cwd: Path) -> str: - """List branches or create a new branch.""" - if not args.strip(): - r = _run(["git", "branch", "-a"], cwd) - return r.stdout.strip() if r.returncode == 0 else r.stderr.strip() - - branch_name = args.strip().split()[0] - r = _run(["git", "branch", branch_name], cwd) - if r.returncode != 0: - return r.stderr.strip() - return f"Created branch: {branch_name}" - - -def git_commit(message: str, cwd: Path) -> str: - """Stage all changes and commit with the given message.""" - # Stage everything - _run(["git", "add", "-A"], cwd) - - # Check if anything is staged - check = _run(["git", "diff", "--cached", "--quiet"], cwd) - if check.returncode == 0: - return "Nothing to commit, no changes staged." - - r = _run(["git", "commit", "-m", message], cwd) - if r.returncode != 0: - return r.stderr.strip() - return r.stdout.strip() - - -def git_commit_push_pr(message: str, cwd: Path) -> str: - """Commit staged changes, push, and open a pull request via gh.""" - # Commit - commit_out = git_commit(message, cwd) - if "nothing" in commit_out.lower(): - return commit_out - - # Get current branch - branch_r = _run(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd) - if branch_r.returncode != 0: - return f"Committed but failed to get branch: {branch_r.stderr.strip()}" - branch = branch_r.stdout.strip() - - # Push - push_r = _run(["git", "push", "-u", "origin", branch], cwd) - if push_r.returncode != 0: - return f"Committed but push failed: {push_r.stderr.strip()}" - - # Create PR - pr_r = _run(["gh", "pr", "create", "--fill", "--head", branch], cwd) - if pr_r.returncode != 0: - # PR may already exist - if "already exists" in pr_r.stderr.lower(): - return f"Committed and pushed. PR already exists for {branch}." - return f"Committed and pushed but PR creation failed: {pr_r.stderr.strip()}" - - return pr_r.stdout.strip() - - -def git_worktree(args: str, cwd: Path) -> str: - """List, add, or remove git worktrees.""" - parts = args.strip().split() if args.strip() else [] - - if not parts or parts[0] == "list": - r = _run(["git", "worktree", "list"], cwd) - return r.stdout.strip() if r.returncode == 0 else r.stderr.strip() - - if parts[0] == "add" and len(parts) >= 3: - r = _run(["git", "worktree", "add", parts[1], parts[2]], cwd) - if r.returncode != 0: - return r.stderr.strip() - return f"Added worktree at {parts[1]} on branch {parts[2]}" - - if parts[0] == "remove" and len(parts) >= 2: - r = _run(["git", "worktree", "remove", parts[1]], cwd) - if r.returncode != 0: - return r.stderr.strip() - return f"Removed worktree at {parts[1]}" - - return "Usage: worktree [list | add | remove ]" - - -def git_diff(cwd: Path) -> str: - """Return working tree diff.""" - r = _run(["git", "diff"], cwd) - if r.returncode != 0: - return r.stderr.strip() - return r.stdout.strip() - - -def git_stash(args: str, cwd: Path) -> str: - """Stash operations: push (default), pop, list, drop.""" - subcmd = args.strip().split()[0] if args.strip() else "push" - - if subcmd == "list": - r = _run(["git", "stash", "list"], cwd) - return r.stdout.strip() if r.stdout.strip() else "No stashes." - if subcmd == "pop": - r = _run(["git", "stash", "pop"], cwd) - return r.stdout.strip() if r.returncode == 0 else r.stderr.strip() - if subcmd == "drop": - r = _run(["git", "stash", "drop"], cwd) - return r.stdout.strip() if r.returncode == 0 else r.stderr.strip() - - # Default: push - r = _run(["git", "stash", "push"], cwd) - if r.returncode != 0: - return r.stderr.strip() - return r.stdout.strip() if r.stdout.strip() else "No local changes to save." - - -def handle_git_command(command: str, args: str, cwd: Path) -> Optional[str]: - """Dispatch a git slash command. Returns None for unknown commands.""" - dispatch = { - "status": lambda: git_status(cwd), - "branch": lambda: git_branch(args, cwd), - "commit": lambda: git_commit(args, cwd), - "push-pr": lambda: git_commit_push_pr(args, cwd), - "worktree": lambda: git_worktree(args, cwd), - "diff": lambda: git_diff(cwd), - "stash": lambda: git_stash(args, cwd), - } - - handler = dispatch.get(command) - if handler is None: - return None - return handler() diff --git a/pkg/hanzo-dev/src/hanzo_dev/ipython_repl.py b/pkg/hanzo-dev/src/hanzo_dev/ipython_repl.py deleted file mode 100644 index ae8345b45..000000000 --- a/pkg/hanzo-dev/src/hanzo_dev/ipython_repl.py +++ /dev/null @@ -1,291 +0,0 @@ -"""IPython-based REPL for intimate Hanzo MCP interaction.""" - -import asyncio -import contextlib -import os -import sys -from typing import Any, Dict - -from hanzo_mcp.server import HanzoMCPServer -from hanzo_mcp.tools import * # noqa: F403 - Import all tools for direct REPL access -from IPython import get_ipython -from IPython.core.magic import Magics, cell_magic, line_magic, magics_class -from IPython.terminal.embed import InteractiveShellEmbed -from rich.console import Console -from rich.markdown import Markdown -from rich.syntax import Syntax - -from .llm_client import LLMClient -from .tool_executor import ToolExecutor - - -@magics_class -class HanzoMagics(Magics): - """Custom magic commands for Hanzo REPL.""" - - def __init__(self, shell, repl): - super().__init__(shell) - self.repl = repl - self.console = Console() - - @line_magic - def chat(self, line): - """Chat with AI: %chat """ - if not line: - return - - # Run async chat in sync context - loop = asyncio.get_event_loop() - response = loop.run_until_complete( - self.repl.tool_executor.execute_with_tools(line) - ) - self.console.print(Markdown(response)) - - @cell_magic - def ai(self, line, cell): # noqa: ARG002 - IPython magic method signature - """Multi-line AI chat.""" - message = cell.strip() - if not message: - return - - loop = asyncio.get_event_loop() - response = loop.run_until_complete( - self.repl.tool_executor.execute_with_tools(message) - ) - self.console.print(Markdown(response)) - - @line_magic - def tools(self, line): # noqa: ARG002 - IPython magic method signature - """List available MCP tools.""" - self.repl.list_tools() - - @line_magic - def tool(self, line): - """Execute a tool: %tool """ - parts = line.split(maxsplit=1) - if len(parts) < 2: - return - - tool_name = parts[0] - try: - import json - - args = json.loads(parts[1]) - except Exception: - return - - loop = asyncio.get_event_loop() - loop.run_until_complete(self.repl.execute_tool(tool_name, args)) - - @line_magic - def edit_self(self, line): - """Edit the REPL source code: %edit_self """ - if not line: - line = "ipython_repl.py" - - file_path = os.path.join(os.path.dirname(__file__), line) - if os.path.exists(file_path): - # Use IPython's editor - get_ipython().magic(f"edit {file_path}") - - # Reload the module - import importlib - - module_name = f"hanzo_dev.{line.replace('.py', '')}" - if module_name in sys.modules: - importlib.reload(sys.modules[module_name]) - else: - pass - - @line_magic - def model(self, line): - """Set or show LLM model: %model [model_name]""" - if not line: - self.repl.llm_client.get_model_info() - else: - with contextlib.suppress(Exception): - self.repl.llm_client.set_model(line) - - -class HanzoIPythonREPL: - """IPython-based REPL with direct MCP access.""" - - def __init__(self): - self.console = Console() - self.mcp_server = None - self.llm_client = None - self.tool_executor = None - self.tools = {} # Direct tool access - - async def initialize(self): - """Initialize MCP and LLM components.""" - # Initialize MCP server - self.mcp_server = HanzoMCPServer() - await self.mcp_server.initialize() - - # Make tools directly accessible - self.tools = self.mcp_server.tools - - # Initialize LLM client - self.llm_client = LLMClient() - if not self.llm_client.get_available_providers(): - self.console.print("[red]No LLM providers available![/red]") - self.console.print( - "Set OPENAI_API_KEY, ANTHROPIC_API_KEY, or other provider keys" - ) - sys.exit(1) - - # Initialize tool executor - self.tool_executor = ToolExecutor(self.mcp_server, self.llm_client) - - def list_tools(self): - """List available tools with their methods.""" - for _name, tool in sorted(self.tools.items()): - # Show available methods - methods = [ - m - for m in dir(tool) - if not m.startswith("_") and callable(getattr(tool, m)) - ] - for method in methods: - if method not in ["execute", "get_schema"]: - pass - - async def execute_tool(self, tool_name: str, args: Dict[str, Any]) -> Any: - """Execute a tool by name.""" - if tool_name not in self.tools: - raise ValueError(f"Unknown tool: {tool_name}") - - tool = self.tools[tool_name] - return await tool.execute(**args) - - def create_namespace(self) -> Dict[str, Any]: - """Create the namespace for IPython shell.""" - namespace = { - # REPL instance - "repl": self, - "mcp": self.mcp_server, - "llm": self.llm_client, - "executor": self.tool_executor, - # Direct tool access - "tools": self.tools, - # Convenience functions - "chat": self._chat_sync, - "execute": self._execute_sync, - "list_tools": self.list_tools, - # Console for rich output - "console": self.console, - "print_md": lambda x: self.console.print(Markdown(x)), - "print_code": lambda x, lang="python": self.console.print(Syntax(x, lang)), - } - - # Add individual tools to namespace - for name, tool in self.tools.items(): - # Create a wrapper that handles async - namespace[name] = self._create_tool_wrapper(tool) - - return namespace - - def _create_tool_wrapper(self, tool): - """Create a sync wrapper for async tool.""" - - def wrapper(**kwargs): - loop = asyncio.get_event_loop() - return loop.run_until_complete(tool.execute(**kwargs)) - - wrapper.__doc__ = tool.description - wrapper.tool = tool # Keep reference to original tool - return wrapper - - def _chat_sync(self, message: str) -> str: - """Synchronous chat wrapper.""" - loop = asyncio.get_event_loop() - response = loop.run_until_complete( - self.tool_executor.execute_with_tools(message) - ) - self.console.print(Markdown(response)) - return response - - def _execute_sync(self, tool_name: str, **kwargs) -> Any: - """Synchronous tool execution wrapper.""" - loop = asyncio.get_event_loop() - return loop.run_until_complete(self.execute_tool(tool_name, kwargs)) - - def run(self): - """Run the IPython REPL.""" - # Initialize asyncio in the current thread - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - - # Initialize components - loop.run_until_complete(self.initialize()) - - # Create IPython shell - namespace = self.create_namespace() - - # Configure IPython - config = { - "TerminalInteractiveShell": { - "colors": "Linux", - "automagic": True, - "banner1": self._get_banner(), - "banner2": "", - } - } - - shell = InteractiveShellEmbed( - config=config, user_ns=namespace, exit_msg="Goodbye!" - ) - - # Register magic commands - shell.register_magics(HanzoMagics(shell, self)) - - # Add some helpful aliases - shell.alias_manager.define_alias("ls", "ls -la") - shell.alias_manager.define_alias("ll", "ls -la") - - # Print welcome info - self.console.print(f"[green]Model: {self.llm_client.current_model}[/green]") - self.console.print("[dim]Type ? for help, ?? for more details[/dim]") - self.console.print( - "[dim]Use %chat for AI chat, tools. for completion[/dim]" - ) - - # Start the shell - shell() - - def _get_banner(self) -> str: - """Get the REPL banner.""" - return """ -[bold cyan]Hanzo REPL[/bold cyan] - Direct access to Model Context Protocol tools - -Available objects: - โ€ข mcp - MCP server instance - โ€ข tools - Direct tool access (tools.read_file, tools.run_command, etc.) - โ€ข chat(msg) - Chat with AI using MCP tools - โ€ข execute() - Execute a tool by name - -Magic commands: - โ€ข %chat - Single-line AI chat - โ€ข %%ai - Multi-line AI chat - โ€ข %tools - List available tools - โ€ข %tool - Execute a specific tool - โ€ข %edit_self - Edit REPL source code - โ€ข %model - Set/show LLM model - -Examples: - >>> read_file(file_path="/etc/hosts") - >>> chat("What files are in the current directory?") - >>> tools.search(query="def main", path=".") - >>> %chat explain what this code does -""" - - -def main(): - """Main entry point for IPython REPL.""" - repl = HanzoIPythonREPL() - repl.run() - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-dev/src/hanzo_dev/llm_client.py b/pkg/hanzo-dev/src/hanzo_dev/llm_client.py deleted file mode 100644 index 6bd2ba9de..000000000 --- a/pkg/hanzo-dev/src/hanzo_dev/llm_client.py +++ /dev/null @@ -1,233 +0,0 @@ -"""LLM client for interacting with various AI providers.""" - -import os -from typing import Any, Dict, List, Optional, Set - -from llm import acompletion, completion - - -class LLMClient: - """Client for interacting with LLM providers.""" - - # Provider to API key environment variables mapping - PROVIDER_ENV_VARS = { - "openai": ["OPENAI_API_KEY"], - "anthropic": ["ANTHROPIC_API_KEY", "CLAUDE_API_KEY"], - "google": ["GOOGLE_API_KEY", "GEMINI_API_KEY"], - "groq": ["GROQ_API_KEY"], - "mistral": ["MISTRAL_API_KEY"], - "perplexity": ["PERPLEXITY_API_KEY", "PERPLEXITYAI_API_KEY"], - "together": ["TOGETHER_API_KEY", "TOGETHERAI_API_KEY"], - "cohere": ["COHERE_API_KEY"], - "replicate": ["REPLICATE_API_KEY"], - "huggingface": ["HUGGINGFACE_API_KEY", "HF_TOKEN"], - "deepinfra": ["DEEPINFRA_API_KEY"], - "ai21": ["AI21_API_KEY"], - "voyage": ["VOYAGE_API_KEY"], - "anyscale": ["ANYSCALE_API_KEY"], - "palm": ["PALM_API_KEY"], - "nlpcloud": ["NLPCLOUD_API_KEY"], - "aleph_alpha": ["ALEPH_ALPHA_API_KEY"], - "petals": ["PETALS_API_KEY"], - "baseten": ["BASETEN_API_KEY"], - "vllm": ["VLLM_API_KEY"], - "ollama": [], # No API key needed for local Ollama - "bedrock": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"], - "vertex_ai": ["GOOGLE_APPLICATION_CREDENTIALS"], - "sagemaker": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"], - } - - # Default models for each provider - DEFAULT_MODELS = { - "openai": "gpt-4-turbo-preview", - "anthropic": "claude-3-opus-20240229", - "google": "gemini-pro", - "groq": "mixtral-8x7b-32768", - "mistral": "mistral-medium", - "perplexity": "pplx-70b-online", - "together": "mixtral-8x7b-32768", - "cohere": "command-r-plus", - "ollama": "llama2", - } - - def __init__(self): - self.available_providers = self._detect_providers() - self.current_provider = None - self.current_model = None - - # Set default provider and model - if self.available_providers: - # Prefer certain providers in order - preferred_order = ["anthropic", "openai", "groq", "google", "ollama"] - for provider in preferred_order: - if provider in self.available_providers: - self.current_provider = provider - self.current_model = self.DEFAULT_MODELS.get( - provider, "gpt-3.5-turbo" - ) - break - - # If no preferred provider, use the first available - if not self.current_provider: - self.current_provider = list(self.available_providers)[0] - self.current_model = self.DEFAULT_MODELS.get( - self.current_provider, "gpt-3.5-turbo" - ) - - def _detect_providers(self) -> Set[str]: - """Detect which LLM providers have API keys configured.""" - available = set() - - for provider, env_vars in self.PROVIDER_ENV_VARS.items(): - if not env_vars: # No API key needed (e.g., Ollama) - if provider == "ollama": - # Check if Ollama is installed and responsive - try: - import urllib.request - - urllib.request.urlopen( - "http://localhost:11434/api/tags", timeout=1 - ) - available.add(provider) - except Exception: # noqa: S110 - pass # Ollama not running - continue - - # Check if any of the environment variables are set - for env_var in env_vars: - if os.getenv(env_var): - available.add(provider) - break - - return available - - def get_available_providers(self) -> List[str]: - """Get list of available providers.""" - return sorted(self.available_providers) - - def get_available_models(self) -> List[str]: - """Get list of available models across all providers.""" - models = [] - - # Add some common models for each available provider - model_map = { - "openai": [ - "gpt-4-turbo-preview", - "gpt-4", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - ], - "anthropic": [ - "claude-3-opus-20240229", - "claude-3-sonnet-20240229", - "claude-3-haiku-20240307", - "claude-2.1", - "claude-instant-1.2", - ], - "google": [ - "gemini-pro", - "gemini-pro-vision", - "palm-2", - ], - "groq": [ - "mixtral-8x7b-32768", - "llama2-70b-4096", - "gemma-7b-it", - ], - "mistral": [ - "mistral-large-latest", - "mistral-medium", - "mistral-small", - "mistral-tiny", - ], - "ollama": [ - "llama2", - "mistral", - "codellama", - "phi", - "neural-chat", - ], - } - - for provider in self.available_providers: - if provider in model_map: - models.extend(model_map[provider]) - - return models - - def set_model(self, model: str): - """Set the current model to use.""" - # Try to determine provider from model name - provider_prefixes = { - "gpt": "openai", - "claude": "anthropic", - "gemini": "google", - "palm": "google", - "mixtral": "groq", - "llama": "groq", - "mistral": "mistral", - } - - # Check if model is in format provider/model - if "/" in model: - provider, model_name = model.split("/", 1) - if provider in self.available_providers: - self.current_provider = provider - self.current_model = model - return - - # Try to infer provider from model name - for prefix, provider in provider_prefixes.items(): - if ( - model.lower().startswith(prefix) - and provider in self.available_providers - ): - self.current_provider = provider - self.current_model = model - return - - # If can't determine provider, try with current provider - if self.current_provider: - self.current_model = model - else: - raise ValueError(f"Cannot determine provider for model: {model}") - - async def chat( - self, - messages: List[Dict[str, str]], - tools: Optional[List[Dict[str, Any]]] = None, - tool_choice: Optional[str] = None, - **kwargs, - ) -> Dict[str, Any]: - """Send a chat completion request.""" - try: - # Prepare request - request_params = { - "model": self.current_model, - "messages": messages, - **kwargs, - } - - # Add tools if provided - if tools: - request_params["tools"] = tools - if tool_choice: - request_params["tool_choice"] = tool_choice - - # Make async request - return await acompletion(**request_params) - - except Exception: - # Fallback to sync if async fails - try: - return completion(**request_params) - except Exception as e2: - raise Exception(f"LLM request failed: {e2}") from e2 - - def get_model_info(self) -> Dict[str, Any]: - """Get information about the current model.""" - return { - "provider": self.current_provider, - "model": self.current_model, - "available_providers": self.get_available_providers(), - } diff --git a/pkg/hanzo-dev/src/hanzo_dev/repl.py b/pkg/hanzo-dev/src/hanzo_dev/repl.py deleted file mode 100644 index 717998fc1..000000000 --- a/pkg/hanzo-dev/src/hanzo_dev/repl.py +++ /dev/null @@ -1,633 +0,0 @@ -"""Main REPL implementation for Hanzo MCP testing.""" - -import asyncio -import json -import os -import sys -from datetime import datetime -from pathlib import Path -from typing import Any, Dict, Optional - -from hanzo_mcp.server import HanzoMCPServer -from hanzoai.config import ConfigLoader, RuntimeConfig -from hanzoai.protocols import ModelPricing, PermissionMode, PermissionPolicy, UsageTracker -from hanzoai.session import ( - CompactionConfig, - Session as HanzoSession, - compact_session, - estimate_session_tokens, - should_compact, -) -from prompt_toolkit import PromptSession -from prompt_toolkit.auto_suggest import AutoSuggestFromHistory -from prompt_toolkit.completion import WordCompleter -from prompt_toolkit.history import FileHistory -from rich.console import Console -from rich.markdown import Markdown -from rich.panel import Panel -from rich.table import Table - -from .llm_client import LLMClient -from .tool_executor import ToolExecutor - -__version__ = "0.1.0" - -# Default pricing for cost estimates (Claude 3.5 Sonnet tier) -_DEFAULT_PRICING = ModelPricing( - input_price_per_token=3.0e-6, - output_price_per_token=15.0e-6, - cache_creation_price_per_token=3.75e-6, - cache_read_price_per_token=0.3e-6, -) - - -class HanzoREPL: - """Interactive REPL for testing Hanzo MCP tools.""" - - def __init__(self, config: Optional[Dict[str, Any]] = None): - self.console = Console() - self.config = config or {} - self.mcp_server = None - self.llm_client = None - self.tool_executor = None - self.session = None - self.history_file = os.path.expanduser("~/.hanzo_dev_history") - - # Hierarchical config - self.runtime_config: RuntimeConfig = self._load_runtime_config() - - # Session persistence - self._session_path = Path.home() / ".hanzo" / "session.json" - self._sessions_dir = Path.home() / ".hanzo" / "sessions" - self.hanzo_session = HanzoSession() - self.compaction_config = CompactionConfig() - - # Mode flags - self.permission_mode = PermissionMode.Allow - self.fast_mode = False - - # REPL commands - self.commands = { - "/help": self.show_help, - "/tools": self.list_tools, - "/exit": self.exit_repl, - "/quit": self.exit_repl, - "/clear": self.cmd_clear, - "/providers": self.list_providers, - "/model": self.set_model, - "/permissions": self.cmd_permissions, - "/context": self.show_context, - "/reset": self.reset_context, - "/test": self.run_tests, - "/status": self.show_status, - "/cost": self.show_cost, - "/compact": self.run_compact, - "/config": self.show_config, - "/version": self.show_version, - "/login": self.do_login, - "/loop": self.do_loop, - "/resume": self.cmd_resume, - "/memory": self.cmd_memory, - "/init": self.cmd_init, - "/export": self.cmd_export, - "/session": self.cmd_session, - "/plan": self.cmd_plan, - "/solve": self.cmd_solve, - "/code": self.cmd_code, - "/auto": self.cmd_auto, - "/fast": self.cmd_fast, - "/remote-control": self.cmd_remote_control, - } - - def _load_runtime_config(self) -> RuntimeConfig: - """Load hierarchical config via ConfigLoader.""" - loader = ConfigLoader.default_for(Path.cwd()) - return loader.load() - - async def initialize(self): - """Initialize MCP server and LLM client.""" - self.console.print("[bold green]Initializing Hanzo REPL...[/bold green]") - - # Initialize MCP server - self.console.print("Loading MCP server...") - self.mcp_server = HanzoMCPServer() - await self.mcp_server.initialize() - - # Initialize LLM client - self.console.print("Detecting available LLM providers...") - self.llm_client = LLMClient() - - # Apply model from config if present - cfg_model = self.runtime_config.get("model") - if cfg_model and isinstance(cfg_model, str): - try: - self.llm_client.set_model(cfg_model) - self.console.print(f"[green]Model from config: {cfg_model}[/green]") - except ValueError: - pass - - available_providers = self.llm_client.get_available_providers() - - if not available_providers: - self.console.print("[bold red]No LLM API keys detected![/bold red]") - self.console.print("Please set one of the following environment variables:") - self.console.print("- OPENAI_API_KEY") - self.console.print("- ANTHROPIC_API_KEY") - self.console.print("- GROQ_API_KEY") - self.console.print("- etc.") - sys.exit(1) - - self.console.print( - f"[green]Available providers: {', '.join(available_providers)}[/green]" - ) - - # Initialize tool executor with permission policy - self.tool_executor = ToolExecutor( - self.mcp_server, - self.llm_client, - permission_policy=PermissionPolicy(default_mode=PermissionMode.Allow), - ) - - # Connect to MCP servers defined in config - mcp_servers = self.runtime_config.mcp_servers() - for name, cfg in mcp_servers.items(): - if hasattr(cfg, "command"): - cmd = [cfg.command] + list(cfg.args) - count = await self.tool_executor.register_mcp_server( - name, cmd, env=cfg.env or None, - ) - if count: - self.console.print(f"[green]MCP {name}: {count} tools[/green]") - - # Load persisted session if exists - if self._session_path.is_file(): - try: - self.hanzo_session = HanzoSession.load(self._session_path) - est = estimate_session_tokens(self.hanzo_session) - self.console.print( - f"[dim]Restored session: {len(self.hanzo_session.messages)} messages, ~{est:,} tokens[/dim]" - ) - except Exception: - self.hanzo_session = HanzoSession() - - # Initialize prompt session - tool_names = [tool.name for tool in self.mcp_server.tools.values()] - completer = WordCompleter( - list(self.commands.keys()) + tool_names, ignore_case=True - ) - self.session = PromptSession( - history=FileHistory(self.history_file), - auto_suggest=AutoSuggestFromHistory(), - completer=completer, - ) - - self.console.print("[bold green]REPL initialized successfully![/bold green]") - self.console.print(f"Using model: [cyan]{self.llm_client.current_model}[/cyan]") - self.console.print("Type [bold]/help[/bold] for available commands.") - - async def run(self): - """Run the main REPL loop.""" - await self.initialize() - - while True: - try: - # Get user input - user_input = await self.session.prompt_async("hanzo> ", multiline=False) - - if not user_input.strip(): - continue - - # Check for commands - if user_input.startswith("/"): - command = user_input.split()[0] - if command in self.commands: - await self.commands[command](user_input) - else: - self.console.print(f"[red]Unknown command: {command}[/red]") - continue - - # Process as chat with MCP tools - await self.process_chat(user_input) - - except KeyboardInterrupt: - self.console.print("\n[yellow]Use /exit to quit[/yellow]") - except EOFError: - await self.exit_repl("") - except Exception as e: - self.console.print(f"[red]Error: {e}[/red]") - if self.config.get("debug"): - self.console.print_exception() - - async def process_chat(self, message: str): - """Process a chat message with MCP tool support.""" - self.console.print() - - # Send to LLM with available tools - try: - response = await self.tool_executor.execute_with_tools(message) - - # Display response - if isinstance(response, str): - self.console.print(Markdown(response)) - else: - self.console.print(response) - - except Exception as e: - self.console.print(f"[red]Error processing chat: {e}[/red]") - if self.config.get("debug"): - self.console.print_exception() - - async def show_help(self, _): - """Show help information.""" - help_text = """ -# Hanzo REPL Commands - -## Session -- **/clear** - Clear session history (new session) -- **/compact** - Compact session keeping summary -- **/resume [path]** - Load saved session from JSON -- **/session [list|switch id]** - Manage saved sessions -- **/export [file]** - Export conversation to file -- **/memory** - Show loaded instruction files -- **/init** - Create starter CLAUDE.md in cwd - -## Model & Config -- **/model [name]** - Show or switch active model -- **/permissions [mode]** - Show/switch permission mode -- **/config** - Show loaded configuration files -- **/fast** - Toggle fast/standard mode -- **/providers** - List available LLM providers -- **/status** - Model, session info, token usage -- **/cost** - Accumulated cost estimate -- **/version** - Show version info - -## Agents -- **/plan [prompt]** - Planning agent -- **/solve [prompt]** - Multi-approach solver -- **/code [prompt]** - Consensus code review -- **/auto [prompt]** - Autonomous execution - -## Other -- **/login** - Authenticate via PKCE flow -- **/loop ** - Run prompt on interval -- **/tools** - List available MCP tools -- **/context** - Show conversation context -- **/reset** - Reset conversation context -- **/test** - Run MCP tool tests -- **/remote-control** - Start WebSocket remote bridge -- **/exit** or **/quit** - Exit the REPL -""" - self.console.print(Markdown(help_text)) - - async def list_tools(self, _): - """List available MCP tools.""" - table = Table(title="Available MCP Tools") - table.add_column("Tool", style="cyan", no_wrap=True) - table.add_column("Description", style="white") - table.add_column("Category", style="green") - - for tool_name, tool in sorted(self.mcp_server.tools.items()): - category = tool.__class__.__module__.split(".")[-1] - table.add_row(tool_name, tool.description[:60] + "...", category) - - self.console.print(table) - self.console.print(f"\nTotal tools: [bold]{len(self.mcp_server.tools)}[/bold]") - - async def list_providers(self, _): - """List available LLM providers.""" - providers = self.llm_client.get_available_providers() - models = self.llm_client.get_available_models() - - table = Table(title="Available LLM Providers") - table.add_column("Provider", style="cyan") - table.add_column("Models", style="white") - table.add_column("Status", style="green") - - for provider in providers: - provider_models = [m for m in models if m.startswith(provider)] - status = "Active" if provider == self.llm_client.current_provider else "" - table.add_row( - provider, - ", ".join(provider_models[:3]) - + ("..." if len(provider_models) > 3 else ""), - status, - ) - - self.console.print(table) - self.console.print( - f"\nCurrent model: [bold cyan]{self.llm_client.current_model}[/bold cyan]" - ) - - async def set_model(self, command: str): - """Set the LLM model to use.""" - parts = command.split(maxsplit=1) - if len(parts) < 2: - self.console.print("[red]Usage: /model [/red]") - self.console.print("Available models:") - for model in self.llm_client.get_available_models(): - self.console.print(f" - {model}") - return - - model = parts[1] - try: - self.llm_client.set_model(model) - self.console.print(f"[green]Model set to: {model}[/green]") - except ValueError as e: - self.console.print(f"[red]Error: {e}[/red]") - - async def show_context(self, _): - """Show current conversation context.""" - context = self.tool_executor.get_context() - if not context: - self.console.print("[yellow]No conversation context yet[/yellow]") - return - - self.console.print( - Panel( - json.dumps(context, indent=2), - title="Conversation Context", - border_style="blue", - ) - ) - - async def reset_context(self, _): - """Reset conversation context.""" - self.tool_executor.reset_context() - self.console.print("[green]Conversation context reset[/green]") - - async def run_tests(self, _): - """Run MCP tool tests.""" - self.console.print("[bold]Running MCP tool tests...[/bold]") - - # Import and run tests - from .tests import run_tool_tests - - await run_tool_tests(self.console, self.mcp_server, self.tool_executor) - - async def cmd_clear(self, _): - """Clear session history and start fresh.""" - self.hanzo_session = HanzoSession() - self.tool_executor.reset_context() - os.system("cls" if os.name == "nt" else "clear") # noqa: S605 - self.console.print("[green]Session cleared.[/green]") - - async def cmd_permissions(self, command: str): - """Show or switch permission mode.""" - parts = command.split(maxsplit=1) - modes = {"read-only": PermissionMode.Deny, "workspace-write": PermissionMode.Ask, "full-access": PermissionMode.Allow} - if len(parts) < 2: - current = {v: k for k, v in modes.items()}.get(self.permission_mode, "full-access") - self.console.print(f"[cyan]Permission mode:[/cyan] {current}") - self.console.print(f" Options: {', '.join(modes.keys())}") - return - name = parts[1].strip() - if name not in modes: - self.console.print(f"[red]Unknown mode. Use: {', '.join(modes.keys())}[/red]") - return - self.permission_mode = modes[name] - self.tool_executor.permission_policy = PermissionPolicy(default_mode=self.permission_mode) - self.console.print(f"[green]Permission mode: {name}[/green]") - - async def cmd_resume(self, command: str): - """Load saved session from JSON file.""" - parts = command.split(maxsplit=1) - path = Path(parts[1].strip()) if len(parts) > 1 else self._session_path - if not path.is_file(): - self.console.print(f"[red]No session file at {path}[/red]") - return - self.hanzo_session = HanzoSession.load(path) - est = estimate_session_tokens(self.hanzo_session) - self.console.print(f"[green]Loaded: {len(self.hanzo_session.messages)} messages, ~{est:,} tokens[/green]") - - async def cmd_memory(self, _): - """Show loaded instruction files.""" - self.console.print("[cyan]Loaded instruction files:[/cyan]") - for entry in self.runtime_config.loaded_entries: - self.console.print(f" [{entry.source.name}] {entry.path}") - if not self.runtime_config.loaded_entries: - self.console.print(" [dim]None[/dim]") - - async def cmd_init(self, _): - """Create starter CLAUDE.md in cwd.""" - target = Path.cwd() / "CLAUDE.md" - if target.exists(): - self.console.print(f"[yellow]Already exists: {target}[/yellow]") - return - target.write_text("# Project Instructions\n\nAdd project-specific instructions here.\n") - self.console.print(f"[green]Created {target}[/green]") - - async def cmd_export(self, command: str): - """Export conversation to file.""" - parts = command.split(maxsplit=1) - outpath = Path(parts[1].strip()) if len(parts) > 1 else Path("hanzo-export.md") - lines = [f"# Hanzo Session Export ({datetime.now().isoformat()})\n"] - for msg in self.hanzo_session.messages: - role = msg.role.value if hasattr(msg.role, "value") else str(msg.role) - text = "".join(b.text for b in msg.content if hasattr(b, "text")) - lines.append(f"## {role}\n\n{text}\n") - outpath.write_text("\n".join(lines)) - self.console.print(f"[green]Exported {len(self.hanzo_session.messages)} messages to {outpath}[/green]") - - async def cmd_session(self, command: str): - """List or switch saved sessions.""" - parts = command.split(maxsplit=2) - self._sessions_dir.mkdir(parents=True, exist_ok=True) - if len(parts) < 2 or parts[1] == "list": - sessions = sorted(self._sessions_dir.glob("*.json")) - if not sessions: - self.console.print("[yellow]No saved sessions.[/yellow]") - return - for s in sessions: - self.console.print(f" {s.stem}") - return - if parts[1] == "switch" and len(parts) > 2: - target = self._sessions_dir / f"{parts[2]}.json" - if not target.is_file(): - self.console.print(f"[red]Session not found: {parts[2]}[/red]") - return - self.hanzo_session = HanzoSession.load(target) - self.console.print(f"[green]Switched to session: {parts[2]}[/green]") - return - self.console.print("[red]Usage: /session [list|switch ][/red]") - - async def _agent_chat(self, system_prefix: str, command: str, label: str): - """Send a prompt with a system prefix for agent commands.""" - parts = command.split(maxsplit=1) - if len(parts) < 2: - self.console.print(f"[red]Usage: /{label} [/red]") - return - prompt = f"{system_prefix}\n\nUser request: {parts[1]}" - await self.process_chat(prompt) - - async def cmd_plan(self, command: str): - """Planning agent.""" - await self._agent_chat("You are a planning agent. Create a detailed plan.", command, "plan") - - async def cmd_solve(self, command: str): - """Multi-approach solver.""" - await self._agent_chat("Race multiple approaches. Present the best solution.", command, "solve") - - async def cmd_code(self, command: str): - """Consensus code review.""" - await self._agent_chat("Implement this with consensus from multiple review passes.", command, "code") - - async def cmd_auto(self, command: str): - """Autonomous execution.""" - await self._agent_chat("Execute this task autonomously. Use tools as needed.", command, "auto") - - async def cmd_fast(self, _): - """Toggle fast/standard mode.""" - self.fast_mode = not self.fast_mode - state = "fast" if self.fast_mode else "standard" - self.console.print(f"[cyan]Mode: {state}[/cyan]") - - async def cmd_remote_control(self, _): - """Start WebSocket server for remote control.""" - try: - import websockets # noqa: F401 - except ImportError: - self.console.print("[red]Install websockets: pip install websockets[/red]") - return - port = 9229 - self.console.print(f"[cyan]Remote control bridge: ws://localhost:{port}[/cyan]") - self.console.print("[yellow]Send JSON messages: {{\"type\": \"prompt\", \"text\": \"...\"}}[/yellow]") - self.console.print("[dim]Press Ctrl+C to stop.[/dim]") - - async def handler(ws): - async for raw in ws: - try: - msg = json.loads(raw) - if msg.get("type") == "prompt": - await self.process_chat(msg["text"]) - await ws.send(json.dumps({"type": "done"})) - except Exception as e: - await ws.send(json.dumps({"type": "error", "message": str(e)})) - - import websockets - try: - async with websockets.serve(handler, "localhost", port): - await asyncio.Future() # run forever - except KeyboardInterrupt: - self.console.print("[yellow]Remote control stopped.[/yellow]") - - async def show_status(self, _): - """Show model, session info, and token usage.""" - tracker = self.tool_executor.usage_tracker if self.tool_executor else UsageTracker() - cum = tracker.cumulative_usage() - est_tokens = estimate_session_tokens(self.hanzo_session) - - table = Table(title="Status") - table.add_column("Key", style="cyan") - table.add_column("Value", style="white") - table.add_row("Model", self.llm_client.current_model if self.llm_client else "n/a") - table.add_row("Provider", self.llm_client.current_provider if self.llm_client else "n/a") - table.add_row("Turns", str(tracker.turns)) - table.add_row("Input tokens", f"{cum.input_tokens:,}") - table.add_row("Output tokens", f"{cum.output_tokens:,}") - table.add_row("Session tokens (est)", f"{est_tokens:,}") - table.add_row("Session messages", str(len(self.hanzo_session.messages))) - table.add_row("Config files loaded", str(len(self.runtime_config.loaded_entries))) - self.console.print(table) - - async def show_cost(self, _): - """Show accumulated cost estimate.""" - tracker = self.tool_executor.usage_tracker if self.tool_executor else UsageTracker() - cum = tracker.cumulative_usage() - cost = _DEFAULT_PRICING.cost(cum) - self.console.print( - f"[cyan]Cost estimate:[/cyan] ${cost:.6f} " - f"({cum.input_tokens:,} in / {cum.output_tokens:,} out, {tracker.turns} turns)" - ) - - async def run_compact(self, _): - """Compact the session to reclaim context.""" - est = estimate_session_tokens(self.hanzo_session) - result = compact_session(self.hanzo_session, self.compaction_config) - if result.removed_message_count == 0: - self.console.print("[yellow]Session too small to compact.[/yellow]") - return - self.hanzo_session = result.compacted_session - new_est = estimate_session_tokens(self.hanzo_session) - self.console.print( - f"[green]Compacted: removed {result.removed_message_count} messages, " - f"{est:,} -> {new_est:,} est tokens.[/green]" - ) - - async def show_config(self, _): - """Show loaded configuration.""" - if not self.runtime_config.loaded_entries: - self.console.print("[yellow]No config files loaded.[/yellow]") - return - for entry in self.runtime_config.loaded_entries: - self.console.print(f" [{entry.source.name}] {entry.path}") - mcp_servers = self.runtime_config.mcp_servers() - if mcp_servers: - self.console.print(f"\n MCP servers: {', '.join(mcp_servers.keys())}") - - async def show_version(self, _): - """Show version info.""" - from hanzoai._version import __version__ as hanzoai_version - self.console.print(f" hanzo-dev {__version__}") - self.console.print(f" hanzoai {hanzoai_version}") - - async def do_login(self, _): - """Login via PKCE flow.""" - from hanzoai.auth import HanzoAuth - - auth = HanzoAuth() - oauth_cfg = self.runtime_config.oauth() - - kwargs: Dict[str, Any] = {} - if oauth_cfg: - kwargs["authorize_url"] = oauth_cfg.authorize_url - kwargs["token_url"] = oauth_cfg.token_url - kwargs["client_id"] = oauth_cfg.client_id - if oauth_cfg.scopes: - kwargs["scopes"] = oauth_cfg.scopes - if oauth_cfg.callback_port: - kwargs["redirect_port"] = oauth_cfg.callback_port - - self.console.print("[yellow]Starting PKCE login flow...[/yellow]") - try: - token_set = await auth.login_with_pkce(**kwargs) - self.console.print(f"[green]Logged in. Scopes: {', '.join(token_set.scopes)}[/green]") - except Exception as e: - self.console.print(f"[red]Login failed: {e}[/red]") - - async def do_loop(self, command: str): - """Run a prompt on a recurring interval. Usage: /loop """ - parts = command.split(maxsplit=2) - if len(parts) < 3: - self.console.print("[red]Usage: /loop [/red]") - return - try: - interval = int(parts[1]) - except ValueError: - self.console.print("[red]Interval must be an integer (seconds).[/red]") - return - prompt = parts[2] - self.console.print(f"[yellow]Looping every {interval}s: {prompt}[/yellow]") - try: - while True: - await self.process_chat(prompt) - await asyncio.sleep(interval) - except KeyboardInterrupt: - self.console.print("[yellow]Loop stopped.[/yellow]") - - async def exit_repl(self, _): - """Exit the REPL, saving session.""" - # Persist session - self._session_path.parent.mkdir(parents=True, exist_ok=True) - self.hanzo_session.save(self._session_path) - self.console.print("[yellow]Goodbye![/yellow]") - sys.exit(0) - - -async def main(): - """Main entry point.""" - repl = HanzoREPL() - await repl.run() - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-dev/src/hanzo_dev/tests.py b/pkg/hanzo-dev/src/hanzo_dev/tests.py deleted file mode 100644 index d915b7f9b..000000000 --- a/pkg/hanzo-dev/src/hanzo_dev/tests.py +++ /dev/null @@ -1,222 +0,0 @@ -"""Test suite for MCP tools in REPL context.""" - -import tempfile -from pathlib import Path -from typing import Any - -from hanzo_mcp.server import HanzoMCPServer -from rich.console import Console -from rich.table import Table - - -async def run_tool_tests( - console: Console, mcp_server: HanzoMCPServer, tool_executor: Any -): - """Run comprehensive tests for MCP tools.""" - - console.print("\n[bold cyan]Running MCP Tool Tests[/bold cyan]\n") - - test_results = [] - - # Test categories - test_suites = [ - ("File Operations", test_file_operations), - ("Search Operations", test_search_operations), - ("Shell Commands", test_shell_commands), - ("LLM Integration", test_llm_integration), - ("Agent Delegation", test_agent_delegation), - ] - - for suite_name, test_func in test_suites: - console.print(f"\n[bold]{suite_name}[/bold]") - try: - results = await test_func(mcp_server, tool_executor) - for test_name, success, message in results: - test_results.append((suite_name, test_name, success, message)) - status = "[green]โœ“[/green]" if success else "[red]โœ—[/red]" - console.print(f" {status} {test_name}: {message}") - except Exception as e: - console.print(f" [red]Suite failed: {e}[/red]") - - # Summary - console.print("\n[bold]Test Summary[/bold]") - table = Table() - table.add_column("Suite", style="cyan") - table.add_column("Test", style="white") - table.add_column("Status", style="green") - - passed = sum(1 for _, _, success, _ in test_results if success) - total = len(test_results) - - for suite, test, success, _ in test_results: - status = "PASS" if success else "FAIL" - style = "green" if success else "red" - table.add_row(suite, test, f"[{style}]{status}[/{style}]") - - console.print(table) - console.print(f"\nTotal: {passed}/{total} passed ({passed / total * 100:.1f}%)") - - -async def test_file_operations( - mcp_server: HanzoMCPServer, - tool_executor: Any, # noqa: ARG001 -) -> list: - """Test file operations.""" - results = [] - - with tempfile.TemporaryDirectory() as tmpdir: - test_file = Path(tmpdir) / "test.txt" - - # Test 1: Write file - try: - tool = mcp_server.tools.get("write_file") - await tool.execute(file_path=str(test_file), content="Hello, MCP!") - results.append(("Write file", True, "File created successfully")) - except Exception as e: - results.append(("Write file", False, str(e))) - - # Test 2: Read file - try: - tool = mcp_server.tools.get("read_file") - content = await tool.execute(file_path=str(test_file)) - success = "Hello, MCP!" in content - results.append(("Read file", success, f"Content: {content[:50]}...")) - except Exception as e: - results.append(("Read file", False, str(e))) - - # Test 3: Edit file - try: - tool = mcp_server.tools.get("edit_file") - await tool.execute( - file_path=str(test_file), - old_string="Hello, MCP!", - new_string="Hello, Hanzo MCP!", - ) - # Verify edit - read_tool = mcp_server.tools.get("read_file") - content = await read_tool.execute(file_path=str(test_file)) - success = "Hello, Hanzo MCP!" in content - results.append(("Edit file", success, "File edited successfully")) - except Exception as e: - results.append(("Edit file", False, str(e))) - - return results - - -async def test_search_operations( - mcp_server: HanzoMCPServer, - tool_executor: Any, # noqa: ARG001 -) -> list: - """Test search operations.""" - results = [] - - # Test 1: Grep search - try: - tool = mcp_server.tools.get("grep") - result = await tool.execute(pattern="def ", path=".", include="*.py") - success = isinstance(result, (list, str)) - results.append( - ( - "Grep search", - success, - f"Found {len(result) if isinstance(result, list) else 1} matches", - ) - ) - except Exception as e: - results.append(("Grep search", False, str(e))) - - # Test 2: File search - try: - tool = mcp_server.tools.get("search") - result = await tool.execute(query="class", path=".", max_results=5) - success = isinstance(result, (list, dict, str)) - results.append(("File search", success, "Search completed")) - except Exception as e: - results.append(("File search", False, str(e))) - - return results - - -async def test_shell_commands( - mcp_server: HanzoMCPServer, - tool_executor: Any, # noqa: ARG001 -) -> list: - """Test shell command execution.""" - results = [] - - # Test 1: Simple command - try: - tool = mcp_server.tools.get("run_command") - result = await tool.execute(command="echo 'Hello from MCP'") - success = "Hello from MCP" in str(result) - results.append(("Echo command", success, "Command executed")) - except Exception as e: - results.append(("Echo command", False, str(e))) - - # Test 2: Python version - try: - tool = mcp_server.tools.get("run_command") - result = await tool.execute(command="python --version") - success = "Python" in str(result) - results.append(("Python version", success, str(result)[:50])) - except Exception as e: - results.append(("Python version", False, str(e))) - - return results - - -async def test_llm_integration( - mcp_server: HanzoMCPServer, # noqa: ARG001 - tool_executor: Any, -) -> list: - """Test LLM integration.""" - results = [] - - # Test 1: Simple chat - try: - response = await tool_executor.execute_with_tools("What is 2+2?") - success = "4" in response - results.append(("Simple math", success, "LLM responded correctly")) - except Exception as e: - results.append(("Simple math", False, str(e))) - - # Test 2: Tool usage - try: - response = await tool_executor.execute_with_tools( - "Create a file called test_llm.txt with the content 'LLM test'" - ) - # Check if file was created - import os - - success = os.path.exists("test_llm.txt") - if success: - os.remove("test_llm.txt") # Cleanup - results.append(("LLM tool use", success, "LLM used tools correctly")) - except Exception as e: - results.append(("LLM tool use", False, str(e))) - - return results - - -async def test_agent_delegation( - mcp_server: HanzoMCPServer, - tool_executor: Any, # noqa: ARG001 -) -> list: - """Test agent delegation.""" - results = [] - - # Test 1: Agent dispatch - try: - tool = mcp_server.tools.get("dispatch_agent") - if tool: - result = await tool.execute( - instruction="List the current directory contents" - ) - success = result is not None - results.append(("Agent dispatch", success, "Agent completed task")) - else: - results.append(("Agent dispatch", False, "Agent tool not available")) - except Exception as e: - results.append(("Agent dispatch", False, str(e))) - - return results diff --git a/pkg/hanzo-dev/src/hanzo_dev/tests/__init__.py b/pkg/hanzo-dev/src/hanzo_dev/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-dev/src/hanzo_dev/tests/test_vim_mode.py b/pkg/hanzo-dev/src/hanzo_dev/tests/test_vim_mode.py deleted file mode 100644 index d7a0a7d10..000000000 --- a/pkg/hanzo-dev/src/hanzo_dev/tests/test_vim_mode.py +++ /dev/null @@ -1,319 +0,0 @@ -"""Tests for vim keybinding support.""" - -import pytest - -from hanzo_dev.vim_mode import VimMode, VimState - - -class FakeWidget: - """Minimal InputWidget implementation for testing.""" - - def __init__(self, value: str = "", cursor_position: int = 0): - self._value = value - self._cursor_position = cursor_position - - @property - def value(self) -> str: - return self._value - - @value.setter - def value(self, v: str) -> None: - self._value = v - - @property - def cursor_position(self) -> int: - return self._cursor_position - - @cursor_position.setter - def cursor_position(self, v: int) -> None: - self._cursor_position = v - - -# -- Mode transitions -- - -class TestModeTransitions: - def test_starts_in_normal(self): - s = VimState() - assert s.mode == VimMode.Normal - - def test_i_enters_insert(self): - s = VimState() - w = FakeWidget("hello") - s.handle_key("i", w) - assert s.mode == VimMode.Insert - - def test_a_enters_insert_after(self): - s = VimState() - w = FakeWidget("hello", 2) - s.handle_key("a", w) - assert s.mode == VimMode.Insert - assert w.cursor_position == 3 - - def test_I_enters_insert_at_start(self): - s = VimState() - w = FakeWidget("hello", 3) - s.handle_key("I", w) - assert s.mode == VimMode.Insert - assert w.cursor_position == 0 - - def test_A_enters_insert_at_end(self): - s = VimState() - w = FakeWidget("hello", 0) - s.handle_key("A", w) - assert s.mode == VimMode.Insert - assert w.cursor_position == 5 - - def test_o_enters_insert_at_end(self): - s = VimState() - w = FakeWidget("hello", 0) - s.handle_key("o", w) - assert s.mode == VimMode.Insert - assert w.cursor_position == 5 - - def test_O_enters_insert_at_start(self): - s = VimState() - w = FakeWidget("hello", 3) - s.handle_key("O", w) - assert s.mode == VimMode.Insert - assert w.cursor_position == 0 - - def test_escape_from_insert(self): - s = VimState(mode=VimMode.Insert) - w = FakeWidget("hello", 3) - s.handle_key("escape", w) - assert s.mode == VimMode.Normal - assert w.cursor_position == 2 # backs up one - - def test_escape_from_insert_at_zero(self): - s = VimState(mode=VimMode.Insert) - w = FakeWidget("hello", 0) - s.handle_key("escape", w) - assert s.mode == VimMode.Normal - assert w.cursor_position == 0 - - def test_v_enters_visual(self): - s = VimState() - w = FakeWidget("hello", 2) - s.handle_key("v", w) - assert s.mode == VimMode.Visual - assert s.visual_anchor == 2 - - def test_escape_from_visual(self): - s = VimState(mode=VimMode.Visual) - w = FakeWidget("hello") - s.handle_key("escape", w) - assert s.mode == VimMode.Normal - - def test_colon_enters_command(self): - s = VimState() - w = FakeWidget("hello") - s.handle_key(":", w) - assert s.mode == VimMode.Command - - def test_escape_from_command(self): - s = VimState(mode=VimMode.Command) - w = FakeWidget("hello") - s.handle_key("escape", w) - assert s.mode == VimMode.Normal - - -# -- Cursor movement -- - -class TestCursorMovement: - def test_h_moves_left(self): - s = VimState() - w = FakeWidget("hello", 3) - s.handle_key("h", w) - assert w.cursor_position == 2 - - def test_h_clamps_at_zero(self): - s = VimState() - w = FakeWidget("hello", 0) - s.handle_key("h", w) - assert w.cursor_position == 0 - - def test_l_moves_right(self): - s = VimState() - w = FakeWidget("hello", 2) - s.handle_key("l", w) - assert w.cursor_position == 3 - - def test_l_clamps_at_end(self): - s = VimState() - w = FakeWidget("hello", 5) - s.handle_key("l", w) - assert w.cursor_position == 5 - - def test_0_goes_to_start(self): - s = VimState() - w = FakeWidget("hello", 4) - s.handle_key("0", w) - assert w.cursor_position == 0 - - def test_dollar_goes_to_end(self): - s = VimState() - w = FakeWidget("hello", 0) - s.handle_key("$", w) - assert w.cursor_position == 5 - - def test_w_next_word(self): - s = VimState() - w = FakeWidget("hello world", 0) - s.handle_key("w", w) - assert w.cursor_position == 6 - - def test_b_prev_word(self): - s = VimState() - w = FakeWidget("hello world", 8) - s.handle_key("b", w) - assert w.cursor_position == 6 - - def test_gg_goes_to_start(self): - s = VimState() - w = FakeWidget("hello", 4) - s.handle_key("g", w) - s.handle_key("g", w) - assert w.cursor_position == 0 - - def test_j_k_consumed(self): - s = VimState() - w = FakeWidget("hello", 2) - assert s.handle_key("j", w) is True - assert w.cursor_position == 2 - assert s.handle_key("k", w) is True - assert w.cursor_position == 2 - - -# -- Delete / Yank / Paste -- - -class TestEditOperations: - def test_x_deletes_char(self): - s = VimState() - w = FakeWidget("hello", 1) - s.handle_key("x", w) - assert w.value == "hllo" - assert s.register == "e" - - def test_x_at_end_noop(self): - s = VimState() - w = FakeWidget("hi", 2) - s.handle_key("x", w) - assert w.value == "hi" - - def test_dd_deletes_line(self): - s = VimState() - w = FakeWidget("hello world", 3) - s.handle_key("d", w) - s.handle_key("d", w) - assert w.value == "" - assert s.register == "hello world" - - def test_yy_yanks_line(self): - s = VimState() - w = FakeWidget("hello", 2) - s.handle_key("y", w) - s.handle_key("y", w) - assert s.register == "hello" - assert w.value == "hello" # unchanged - - def test_p_pastes_after(self): - s = VimState() - s.register = "XY" - w = FakeWidget("abc", 1) - s.handle_key("p", w) - assert w.value == "abXYc" - - def test_dw_deletes_word(self): - s = VimState() - w = FakeWidget("hello world", 0) - s.handle_key("d", w) - s.handle_key("w", w) - assert w.value == "world" - assert s.register == "hello " - - -# -- Undo -- - -class TestUndo: - def test_undo_restores(self): - s = VimState() - w = FakeWidget("hello", 2) - s.handle_key("x", w) - assert w.value == "helo" - s.handle_key("u", w) - assert w.value == "hello" - assert w.cursor_position == 2 - - def test_undo_empty_stack(self): - s = VimState() - w = FakeWidget("hello", 0) - s.handle_key("u", w) # should not crash - assert w.value == "hello" - - -# -- Visual mode -- - -class TestVisualMode: - def test_visual_yank(self): - s = VimState() - w = FakeWidget("hello", 1) - s.handle_key("v", w) - s.handle_key("l", w) - s.handle_key("l", w) - s.handle_key("y", w) - assert s.register == "ell" - assert s.mode == VimMode.Normal - - def test_visual_delete(self): - s = VimState() - w = FakeWidget("hello", 1) - s.handle_key("v", w) - s.handle_key("l", w) - s.handle_key("d", w) - assert w.value == "hlo" - assert s.register == "el" - assert s.mode == VimMode.Normal - - -# -- Command mode -- - -class TestCommandMode: - def test_accumulate_and_return(self): - s = VimState() - w = FakeWidget() - s.handle_key(":", w) - s.handle_key("w", w) - s.handle_key("q", w) - result = s.handle_key("enter", w) - assert result == "wq" - assert s.mode == VimMode.Normal - - def test_backspace_in_command(self): - s = VimState() - w = FakeWidget() - s.handle_key(":", w) - s.handle_key("a", w) - s.handle_key("b", w) - s.handle_key("backspace", w) - result = s.handle_key("enter", w) - assert result == "a" - - def test_search_prefix(self): - s = VimState() - w = FakeWidget("hello") - s.handle_key("/", w) - assert s.mode == VimMode.Command - assert s.command_buffer == "/" - s.handle_key("h", w) - result = s.handle_key("enter", w) - assert result == "/h" - - -# -- Insert passthrough -- - -class TestInsertMode: - def test_regular_key_returns_false(self): - s = VimState(mode=VimMode.Insert) - w = FakeWidget("hello") - result = s.handle_key("a", w) - assert result is False # not consumed, widget handles it diff --git a/pkg/hanzo-dev/src/hanzo_dev/textual_repl.py b/pkg/hanzo-dev/src/hanzo_dev/textual_repl.py deleted file mode 100644 index ed21445e6..000000000 --- a/pkg/hanzo-dev/src/hanzo_dev/textual_repl.py +++ /dev/null @@ -1,1306 +0,0 @@ -"""Beautiful Textual-based REPL interface for Hanzo.""" - -import contextlib -import json -import time -from datetime import datetime -from pathlib import Path -from typing import Any, Dict, Optional - -from hanzo_mcp.server import HanzoMCPServer -from hanzoai.config import ConfigLoader, RuntimeConfig -from hanzoai.protocols import ( - ModelPricing, - PermissionMode, - PermissionPolicy, - UsageTracker, -) -from hanzoai.session import ( - CompactionConfig, - Session as HanzoSession, - compact_session, - estimate_session_tokens, -) -from rich.console import Console -from rich.markdown import Markdown -from rich.text import Text -from textual.app import App, ComposeResult -from textual.containers import Horizontal, ScrollableContainer, Vertical -from textual.css.query import NoMatches -from textual.reactive import reactive -from textual.widgets import Input, Label, RichLog, Static - -from .backends import BackendManager -from .command_palette import CommandPalette, CommandSelected -from .command_suggestions import CommandSuggestions -from .llm_client import LLMClient -from .tool_executor import ToolExecutor - -# Default pricing for cost display (Claude 3.5 Sonnet tier) -_DEFAULT_PRICING = ModelPricing( - input_price_per_token=3.0e-6, - output_price_per_token=15.0e-6, - cache_creation_price_per_token=3.75e-6, - cache_read_price_per_token=0.3e-6, -) - - -class StatusBar(Static): - """Animated status bar showing thinking state.""" - - elapsed_time = reactive(0) - token_count = reactive(0) - is_thinking = reactive(False) - status_text = reactive("Ready") - - SPINNERS = ["โœฆ", "โœง", "โœถ", "โœท", "โœธ", "โœน", "โœบ", "โœป", "โœผ", "โœฝ"] - - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.start_time = None - self.spinner_index = 0 - - def on_mount(self) -> None: - """Start the timer.""" - self.set_interval(0.1, self.update_status) - - def update_status(self) -> None: - """Update the status display.""" - if self.is_thinking and self.start_time: - self.elapsed_time = int(time.time() - self.start_time) - self.spinner_index = (self.spinner_index + 1) % len(self.SPINNERS) - - def start_thinking(self, text: str = "Bonding") -> None: - """Start the thinking animation.""" - self.is_thinking = True - self.status_text = text - self.start_time = time.time() - self.token_count = 0 - - def stop_thinking(self) -> None: - """Stop the thinking animation.""" - self.is_thinking = False - self.start_time = None - - def update_tokens(self, count: int) -> None: - """Update token count.""" - self.token_count = count - - def render(self) -> Text: - """Render the status bar.""" - if self.is_thinking: - spinner = self.SPINNERS[self.spinner_index] - return Text( - f"{spinner} {self.status_text}โ€ฆ ({self.elapsed_time}s ยท โ†‘ {self.token_count} tokens ยท esc to interrupt)", - style="bright_yellow", - ) - return Text("") - - -class ContextIndicator(Static): - """Shows context usage based on estimate_session_tokens().""" - - context_percent = reactive(100) - - def __init__(self, max_tokens: int = 200_000, **kwargs): - super().__init__(**kwargs) - self.max_tokens = max_tokens - - def update_from_session(self, session: HanzoSession) -> None: - """Recompute percentage from live session token estimate.""" - used = estimate_session_tokens(session) - remaining = max(0, self.max_tokens - used) - self.context_percent = int((remaining / self.max_tokens) * 100) - - def render(self) -> Text: - """Render context indicator.""" - return Text( - f"Context left until auto-compact: {self.context_percent}%", - style="dim yellow", - ) - - -class MessageArea(ScrollableContainer): - """Scrollable area for messages.""" - - def compose(self) -> ComposeResult: - """Create child widgets.""" - yield RichLog(id="messages", wrap=True, markup=True, auto_scroll=True) - - -class HanzoTextualREPL(App): - """Main Textual application for Hanzo REPL.""" - - CSS = """ - Screen { - background: $background; - } - - MessageArea { - height: 1fr; - border: none; - padding: 1 2; - } - - #messages { - background: $background; - scrollbar-size: 1 1; - } - - #input-box { - height: 3; - margin: 0 1; - border: tall $secondary; - background: $panel; - } - - #input { - dock: top; - background: transparent; - border: none; - padding: 0 1; - } - - #status-bar { - dock: top; - height: 1; - padding: 0 2; - background: transparent; - } - - #bottom-bar { - dock: bottom; - height: 1; - background: transparent; - padding: 0 2; - } - - #permissions { - text-align: right; - } - - #hint { - color: $text-muted; - padding: 0 2; - } - """ - - BINDINGS = [ - ("escape", "interrupt", "Interrupt"), - ("ctrl+c", "quit", "Quit"), - ("ctrl+l", "clear", "Clear"), - ("ctrl+k", "command_palette", "Commands"), - ("up", "history_up", "Previous"), - ("down", "history_down", "Next"), - ("ctrl+r", "verbose", "Verbose"), - ("!", "bash_mode", "Bash Mode"), - ("?", "shortcuts", "Shortcuts"), - ("/", "slash_commands", "Commands"), - ("@", "file_complete", "Files"), - ("#", "memorize", "Memorize"), - ] - - def __init__(self): - super().__init__() - self.mcp_server = None - self.llm_client = None - self.backend_manager = None - self.tool_executor = None - self.history = [] - self.history_index = 0 - self.bash_mode = False - self.context_usage = 10 - self.verbose_mode = False - self.memory = {} # For memorized snippets - self.command_suggestions = None - self.fast_mode = False - self.permission_mode = PermissionMode.Allow - - # hanzoai core wiring - loader = ConfigLoader.default_for(Path.cwd()) - self.runtime_config: RuntimeConfig = loader.load() - self.hanzo_session = HanzoSession() - self.compaction_config = CompactionConfig() - self._session_path = Path.home() / ".hanzo" / "session.json" - self._sessions_dir = Path.home() / ".hanzo" / "sessions" - - def compose(self) -> ComposeResult: - """Create child widgets.""" - # Status bar at top - yield StatusBar(id="status-bar") - - # Message area - yield MessageArea() - - # Input area - with Vertical(id="input-box"): - yield Input(placeholder="Press up to edit queued messages", id="input") - - # Hint text - yield Label("? for shortcuts", id="hint") - - # Bottom bar - with Horizontal(id="bottom-bar"): - yield Static(Text("Bypassing Permissions", style="yellow")) - yield ContextIndicator(id="permissions") - - async def on_mount(self) -> None: - """Initialize when app mounts.""" - await self.initialize_services() - - # Focus input - self.query_one("#input", Input).focus() - - # Show welcome message - messages = self.query_one("#messages", RichLog) - messages.write(Text("โ— Welcome to Hanzo REPL", style="bold cyan")) - messages.write(Text("โ— Type '?' for shortcuts, '!' for bash mode", style="dim")) - messages.write("") - - async def initialize_services(self) -> None: - """Initialize MCP and LLM services.""" - try: - # Initialize MCP server - self.mcp_server = HanzoMCPServer() - await self.mcp_server.initialize() - - # Initialize LLM client (for embedded backend) - self.llm_client = LLMClient() - - # Apply model from config if present - cfg_model = self.runtime_config.get("model") - if cfg_model and isinstance(cfg_model, str): - try: - self.llm_client.set_model(cfg_model) - except ValueError: - pass - - # Initialize backend manager - self.backend_manager = BackendManager(self.llm_client) - - # Show backend info - messages = self.query_one("#messages", RichLog) - backend_name = self.backend_manager.current_backend - backend = self.backend_manager.get_backend() - - messages.write(Text(f"โ— Backend: {backend_name}", style="green")) - - # Show specific info based on backend - if backend_name == "claude": - if hasattr(backend, "authenticated") and backend.authenticated: - messages.write( - Text("โ— Using Claude personal account", style="cyan") - ) - else: - messages.write( - Text( - "โ— Claude Code (not authenticated - using API)", - style="yellow", - ) - ) - elif backend_name == "embedded": - messages.write( - Text(f"โ— Model: {self.llm_client.current_model}", style="green") - ) - - # Initialize tool executor with backend + permission policy - self.tool_executor = ToolExecutor( - self.mcp_server, - self.backend_manager, - permission_policy=PermissionPolicy(default_mode=PermissionMode.Allow), - ) - - # Connect to MCP servers from config - mcp_servers = self.runtime_config.mcp_servers() - for name, cfg in mcp_servers.items(): - if hasattr(cfg, "command"): - cmd = [cfg.command] + list(cfg.args) - count = await self.tool_executor.register_mcp_server( - name, cmd, env=cfg.env or None, - ) - if count: - messages.write(Text(f"โ— MCP {name}: {count} tools", style="green")) - - # Load persisted session - if self._session_path.is_file(): - try: - self.hanzo_session = HanzoSession.load(self._session_path) - est = estimate_session_tokens(self.hanzo_session) - messages.write( - Text(f"โ— Restored session: {len(self.hanzo_session.messages)} msgs, ~{est:,} tokens", style="dim") - ) - except Exception: - self.hanzo_session = HanzoSession() - - # Update context indicator from session - context_indicator = self.query_one("#permissions", ContextIndicator) - context_indicator.update_from_session(self.hanzo_session) - - # List available backends - backends = self.backend_manager.list_backends() - available = [name for name, avail in backends.items() if avail] - messages.write( - Text(f"โ— Available backends: {', '.join(available)}", style="dim") - ) - - except Exception as e: - self.show_error(f"Initialization error: {e}") - - def show_message(self, message: str, style: str = "white") -> None: - """Show a message in the chat area.""" - messages = self.query_one("#messages", RichLog) - messages.write(Text(f"โ— {message}", style=style)) - - def show_error(self, message: str) -> None: - """Show an error message.""" - messages = self.query_one("#messages", RichLog) - messages.write(Text(f"โ— {message}", style="red")) - - async def on_input_submitted(self, event: Input.Submitted) -> None: - """Handle input submission.""" - input_widget = self.query_one("#input", Input) - value = event.value.strip() - - # Check if command suggestions are visible and handle selection - try: - suggestions = self.query_one("#command-suggestions", CommandSuggestions) - selected_command = suggestions.get_selected_command() - if selected_command: - # Use selected command - value = selected_command - input_widget.value = "" - # Remove suggestions - suggestions.remove() - except NoMatches: - pass - - if not value: - return - - # Clear input - input_widget.value = "" - - # Add to history - self.history.append(value) - self.history_index = len(self.history) - - # Show user message - self.show_message(value, "bright_white") - - # Handle special input first - if await self.handle_special_input(value): - return - - # Handle special commands - if value.startswith("!"): - # Bash mode - await self.execute_bash(value[1:].strip()) - elif value == "?": - self.action_shortcuts() - else: - # Regular chat mode - await self.process_chat(value) - - async def process_chat(self, message: str) -> None: - """Process a chat message.""" - from hanzoai.session import ConversationMessage, TextBlock - - # Start thinking animation - status = self.query_one("#status-bar", StatusBar) - status.start_thinking() - - try: - # Track user message in session - self.hanzo_session.messages.append(ConversationMessage.user_text(message)) - - # Execute with tools - response = await self.tool_executor.execute_with_tools(message) - - # Track assistant response in session - self.hanzo_session.messages.append( - ConversationMessage.assistant([TextBlock(text=response or "")]) - ) - - # Stop animation - status.stop_thinking() - - # Update token count on status bar - if self.tool_executor: - cum = self.tool_executor.usage_tracker.cumulative_usage() - status.update_tokens(cum.total_tokens()) - - # Show response - msgs_widget = self.query_one("#messages", RichLog) - msgs_widget.write("") - - # Format as markdown - console = Console() - with console.capture() as capture: - console.print(Markdown(response)) - - for line in capture.get().split("\n"): - if line.strip(): - msgs_widget.write(f"โ— {line}") - - msgs_widget.write("") - - # Update context indicator from real session token estimate - context_indicator = self.query_one("#permissions", ContextIndicator) - context_indicator.update_from_session(self.hanzo_session) - - except Exception as e: - status.stop_thinking() - self.show_error(f"Error: {e}") - - async def execute_bash(self, command: str) -> None: - """Execute a bash command.""" - if not command: - self.show_message("Entering bash mode. Type commands to execute.", "yellow") - self.bash_mode = True - return - - # Show command - messages = self.query_one("#messages", RichLog) - messages.write(Text(f"Bash({command})", style="yellow")) - messages.write(Text(" โ””โ”€ Runningโ€ฆ", style="dim")) - - try: - # Execute command - tool = self.mcp_server.tools.get("run_command") - if tool: - result = await tool.execute(command=command) - # Show output - if result: - for line in str(result).split("\n"): - if line.strip(): - messages.write(f" {line}") - messages.write("") - except Exception as e: - self.show_error(f"Command failed: {e}") - - def action_interrupt(self) -> None: - """Interrupt current operation.""" - status = self.query_one("#status-bar", StatusBar) - if status.is_thinking: - status.stop_thinking() - self.show_message("Interrupted", "yellow") - - def action_clear(self) -> None: - """Clear the message area.""" - messages = self.query_one("#messages", RichLog) - messages.clear() - - def action_history_up(self) -> None: - """Navigate to previous command or move selection in suggestions.""" - # Check if command suggestions are visible - try: - suggestions = self.query_one("#command-suggestions", CommandSuggestions) - suggestions.move_selection_up() - return - except NoMatches: - pass - - # Normal history navigation - if self.history and self.history_index > 0: - self.history_index -= 1 - input_widget = self.query_one("#input", Input) - input_widget.value = self.history[self.history_index] - - def action_history_down(self) -> None: - """Navigate to next command or move selection in suggestions.""" - # Check if command suggestions are visible - try: - suggestions = self.query_one("#command-suggestions", CommandSuggestions) - suggestions.move_selection_down() - return - except NoMatches: - pass - - # Normal history navigation - if self.history and self.history_index < len(self.history) - 1: - self.history_index += 1 - input_widget = self.query_one("#input", Input) - input_widget.value = self.history[self.history_index] - elif self.history_index == len(self.history) - 1: - self.history_index = len(self.history) - input_widget = self.query_one("#input", Input) - input_widget.value = "" - - def action_shortcuts(self) -> None: - """Show shortcuts.""" - messages = self.query_one("#messages", RichLog) - messages.write("") - - # Input box with shortcuts - messages.write( - Text( - "โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ", - style="dim", - ) - ) - messages.write( - Text( - "โ”‚ ! โ”‚", - style="dim", - ) - ) - messages.write( - Text( - "โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ", - style="dim", - ) - ) - - # Shortcuts in two columns - shortcuts_left = [ - ("! for bash mode", "Execute shell commands directly"), - ("/ for commands", "Access MCP tool commands"), - ("@ for file paths", "Quick file path completion"), - ("# to memorize", "Save snippet for later recall"), - ] - - shortcuts_right = [ - ("double tap esc to clear input", ""), - ("shift + tab to auto-accept edits", ""), - ("ctrl + r for verbose output", ""), - ("shift + โŽ for newline", ""), - ] - - # Display shortcuts - for left, right in zip(shortcuts_left, shortcuts_right): - left_text = f" {left[0]:<35}" - right_text = right[0] - messages.write(Text(left_text + right_text, style="cyan")) - - messages.write("") - - def action_command_palette(self) -> None: - """Show command palette.""" - if self.mcp_server and self.mcp_server.tools: - palette = CommandPalette(self.mcp_server.tools) - self.mount(palette) - - async def on_command_selected(self, message: CommandSelected) -> None: - """Handle command selection from palette.""" - command = message.command - - # Handle special commands - if command.name == "clear": - self.action_clear() - elif command.name == "help": - self.action_shortcuts() - elif command.name == "model": - await self.show_model_selector() - elif command.name == "ai_complete": - # AI completion of partial command - await self.process_chat(command.description[4:]) # Remove "AI: " prefix - else: - # Execute MCP tool - await self.execute_tool(command.name, command.parameters) - - async def execute_tool( - self, tool_name: str, parameters: Optional[Dict] = None - ) -> None: - """Execute an MCP tool.""" - self.show_message(f"Executing: {tool_name}", "cyan") - - # Start thinking animation - status = self.query_one("#status-bar", StatusBar) - status.start_thinking(f"Running {tool_name}") - - try: - tool = self.mcp_server.tools.get(tool_name) - if not tool: - self.show_error(f"Tool not found: {tool_name}") - return - - # Get parameters if needed - params = {} - if parameters and any( - p.get("required", False) - for p in parameters.get("properties", {}).values() - ): - self.show_message( - "Tool requires parameters - use CLI for full control", "yellow" - ) - - # Execute tool - result = await tool.execute(**params) - - # Stop animation - status.stop_thinking() - - # Show result - messages = self.query_one("#messages", RichLog) - if isinstance(result, str): - for line in result.split("\n"): - if line.strip(): - messages.write(f" {line}") - else: - messages.write(f" Result: {result}") - - messages.write("") - - except Exception as e: - status.stop_thinking() - self.show_error(f"Tool execution failed: {e}") - - def action_slash_commands(self) -> None: - """Show slash commands menu.""" - self.show_message("/ commands:", "cyan") - commands = [ - "/help Show shortcuts", - "/status Model, session info, token usage", - "/cost Accumulated cost estimate", - "/clear Clear session history", - "/compact Compact session, keep summary", - "/model [name] Show or switch AI model", - "/permissions [m] Show/switch permission mode", - "/fast Toggle fast/standard mode", - "/config Show loaded config files", - "/memory Show loaded instruction files", - "/init Create starter CLAUDE.md", - "/resume [path] Load saved session", - "/export [file] Export conversation", - "/session [list|..] Manage saved sessions", - "/plan Planning agent", - "/solve Multi-approach solver", - "/code Consensus code review", - "/auto Autonomous execution", - "/login Authenticate via PKCE", - "/loop Run prompt on interval", - "/backend Switch backend", - "/tools List MCP tools", - "/remote-control WebSocket remote bridge", - "/version Show version info", - "/exit Quit", - ] - messages = self.query_one("#messages", RichLog) - for cmd in commands: - messages.write(f" {cmd}") - messages.write("") - - def action_file_complete(self) -> None: - """File path completion.""" - input_widget = self.query_one("#input", Input) - current_value = input_widget.value - - # Add @ prefix if not present - if not current_value.startswith("@"): - input_widget.value = "@" + current_value - - self.show_message("File completion: Start typing a path after @", "cyan") - - def action_memorize(self) -> None: - """Memorize current input or last message.""" - input_widget = self.query_one("#input", Input) - current_value = input_widget.value - - if current_value: - # Memorize current input - key = f"snippet_{len(self.memory) + 1}" - self.memory[key] = current_value - self.show_message(f"Memorized as {key}: {current_value[:50]}...", "green") - else: - # Show memorized items - if self.memory: - self.show_message("Memorized snippets:", "cyan") - messages = self.query_one("#messages", RichLog) - for key, value in self.memory.items(): - messages.write(f" {key}: {value[:50]}...") - messages.write("") - else: - self.show_message( - "No memorized snippets. Type something and press # to memorize.", - "yellow", - ) - - def action_verbose(self) -> None: - """Toggle verbose mode.""" - self.verbose_mode = not self.verbose_mode - mode = "enabled" if self.verbose_mode else "disabled" - self.show_message(f"Verbose mode {mode}", "cyan") - - async def handle_special_input(self, value: str) -> bool: - """Handle special input patterns.""" - # Voice command - if value == "/voice": - await self.toggle_voice_mode() - return True - - # File path with @ - if value.startswith("@"): - file_path = value[1:].strip() - if file_path: - await self.execute_tool("read_file", {"file_path": file_path}) - return True - - # Search with / - if value.startswith("/") and len(value) > 1: - parts = value[1:].split(maxsplit=1) - command = parts[0] - arg = parts[1] if len(parts) > 1 else "" - - if command == "search" and arg: - await self.execute_tool("search", {"query": arg}) - return True - if command == "model" and arg: - if self.backend_manager.current_backend == "embedded": - self.llm_client.set_model(arg) - self.show_message(f"Model changed to: {arg}", "green") - else: - self.show_message( - "Model selection only available for embedded backend", "yellow" - ) - return True - if command == "model" and not arg: - await self.show_model_selector() - return True - if command == "auth": - await self.handle_auth_command() - return True - if command == "backend": - await self.handle_backend_command(arg) - return True - if command == "logout": - await self.handle_logout_command() - return True - if command == "status": - await self.handle_status_command() - return True - if command == "cost": - await self.handle_cost_command() - return True - if command == "compact": - await self.handle_compact_command() - return True - if command == "config": - await self.handle_config_command() - return True - if command == "version": - await self.handle_version_command() - return True - if command == "login": - await self.handle_login_command() - return True - if command == "loop": - await self.handle_loop_command(arg) - return True - if command == "permissions": - await self.handle_permissions_command(arg) - return True - if command == "resume": - await self.handle_resume_command(arg) - return True - if command == "memory": - await self.handle_memory_command() - return True - if command == "init": - await self.handle_init_command() - return True - if command == "export": - await self.handle_export_command(arg) - return True - if command == "session": - await self.handle_session_command(arg) - return True - if command == "plan": - await self.handle_agent_command("You are a planning agent. Create a detailed plan.", arg, "plan") - return True - if command == "solve": - await self.handle_agent_command("Race multiple approaches. Present the best solution.", arg, "solve") - return True - if command == "code": - await self.handle_agent_command("Implement this with consensus from multiple review passes.", arg, "code") - return True - if command == "auto": - await self.handle_agent_command("Execute this task autonomously. Use tools as needed.", arg, "auto") - return True - if command == "fast": - self.fast_mode = not self.fast_mode - self.show_message(f"Mode: {'fast' if self.fast_mode else 'standard'}", "cyan") - return True - if command == "remote-control": - self.show_message("Remote control: ws://localhost:9229", "cyan") - self.show_message("Install websockets and run from CLI REPL for full support.", "yellow") - return True - if command == "exit" or command == "quit": - # Save session before exit - self._session_path.parent.mkdir(parents=True, exist_ok=True) - self.hanzo_session.save(self._session_path) - self.exit() - return True - if command == "clear": - self.hanzo_session = HanzoSession() - if self.tool_executor: - self.tool_executor.reset_context() - self.action_clear() - self.show_message("Session cleared.", "green") - return True - if command == "help": - self.action_shortcuts() - return True - if command == "tools": - await self.show_tools() - return True - - # Memorize with # - if value.startswith("#"): - content = value[1:].strip() - if content: - key = f"snippet_{len(self.memory) + 1}" - self.memory[key] = content - self.show_message(f"Memorized as {key}", "green") - return True - - return False - - async def toggle_voice_mode(self) -> None: - """Toggle voice mode on/off.""" - try: - from .voice_mode import VOICE_AVAILABLE, VoiceCommands, VoiceMode - - if not VOICE_AVAILABLE: - self.show_error( - "Voice mode not available. Install: pip install speechrecognition pyttsx3 pyaudio" - ) - return - - if not hasattr(self, "voice_mode"): - self.voice_mode = VoiceMode() - self.voice_commands = VoiceCommands() - - if self.voice_mode.is_active: - # Stop voice mode - self.voice_mode.stop() - self.show_message("Voice mode deactivated", "yellow") - else: - # Start voice mode - def on_speech(text: str): - # Process voice input - processed, should_stop = self.voice_commands.process_voice_input( - text - ) - - if should_stop: - self.call_from_thread(self.toggle_voice_mode) - elif processed: - # Show what was heard - self.call_from_thread( - self.show_message, f"Heard: {text}", "dim" - ) - # Process the command - self.call_from_thread(self.process_voice_command, processed) - - self.voice_mode.start(on_speech) - self.show_message( - "Voice mode activated. Say 'Hey Hanzo' followed by your command.", - "green", - ) - - except Exception as e: - self.show_error(f"Voice mode error: {e}") - - async def process_voice_command(self, command: str) -> None: - """Process a voice command.""" - # Simulate input - input_widget = self.query_one("#input", Input) - input_widget.value = command - - # Submit it - await self.on_input_submitted(Input.Submitted(input_widget, command)) - - def on_input_changed(self, event: Input.Changed) -> None: - """Handle input changes to show command suggestions.""" - value = event.value - - # Show command suggestions when typing "/" - if value.startswith("/") and len(value) >= 1: - # Remove existing suggestions if any - with contextlib.suppress(NoMatches): - self.query_one("#command-suggestions").remove() - - # Create and mount suggestions - self.command_suggestions = CommandSuggestions(value) - self.mount(self.command_suggestions, after="#input-box") - else: - # Remove suggestions if not typing a command - with contextlib.suppress(NoMatches): - self.query_one("#command-suggestions").remove() - - # Update existing suggestions - if self.command_suggestions and value.startswith("/"): - self.command_suggestions.update_query(value) - - async def show_model_selector(self) -> None: - """Show model selection dialog.""" - models = self.llm_client.get_available_models() - self.show_message("Available models:", "cyan") - messages = self.query_one("#messages", RichLog) - for i, model in enumerate(models, 1): - current = " (current)" if model == self.llm_client.current_model else "" - messages.write(f" {i}. {model}{current}") - messages.write("") - self.show_message("Use /model to change model", "dim") - - async def handle_auth_command(self) -> None: - """Handle /auth command.""" - backend = self.backend_manager.get_backend() - - if self.backend_manager.current_backend == "claude": - if hasattr(backend, "authenticate"): - self.show_message("Authenticating with Claude...", "yellow") - try: - success = await backend.authenticate() - if success: - self.show_message( - "Successfully authenticated with Claude!", "green" - ) - self.show_message( - "You can now use your personal Claude account without API keys.", - "cyan", - ) - else: - self.show_error("Authentication failed. Please try again.") - except Exception as e: - self.show_error(f"Authentication error: {e}") - else: - self.show_message( - "Claude backend doesn't support authentication", "yellow" - ) - else: - self.show_message( - f"Authentication not available for {self.backend_manager.current_backend} backend", - "yellow", - ) - self.show_message("Use /backend claude to switch to Claude Code", "dim") - - async def handle_backend_command(self, backend_name: str) -> None: - """Handle /backend command.""" - if not backend_name: - # Show available backends - backends = self.backend_manager.list_backends() - self.show_message("Available backends:", "cyan") - messages = self.query_one("#messages", RichLog) - - for name, available in backends.items(): - status = "โœ“" if available else "โœ—" - current = ( - " (current)" if name == self.backend_manager.current_backend else "" - ) - style = "green" if available else "red" - messages.write(Text(f" {status} {name}{current}", style=style)) - - messages.write("") - self.show_message("Use /backend to switch backend", "dim") - else: - # Switch backend - try: - self.backend_manager.set_backend(backend_name) - self.show_message(f"Switched to {backend_name} backend", "green") - - # Reinitialize tool executor with new backend - self.tool_executor = ToolExecutor(self.mcp_server, self.backend_manager) - - # Show backend-specific info - backend = self.backend_manager.get_backend() - if backend_name == "claude" and hasattr(backend, "authenticated"): - if backend.authenticated: - self.show_message("Using Claude personal account", "cyan") - else: - self.show_message( - "Not authenticated. Use /auth to login with personal account", - "yellow", - ) - - except ValueError as e: - self.show_error(str(e)) - - async def handle_logout_command(self) -> None: - """Handle /logout command.""" - backend = self.backend_manager.get_backend() - - if self.backend_manager.current_backend == "claude" and hasattr( - backend, "logout" - ): - await backend.logout() - self.show_message("Logged out from Claude account", "yellow") - else: - self.show_message("No active authentication session", "dim") - - async def handle_status_command(self) -> None: - """Show model, session info, token usage.""" - messages = self.query_one("#messages", RichLog) - tracker = self.tool_executor.usage_tracker if self.tool_executor else UsageTracker() - cum = tracker.cumulative_usage() - est = estimate_session_tokens(self.hanzo_session) - model = self.llm_client.current_model if self.llm_client else "n/a" - provider = self.llm_client.current_provider if self.llm_client else "n/a" - backend = self.backend_manager.current_backend if self.backend_manager else "n/a" - - messages.write(Text("Status:", style="cyan")) - messages.write(f" Backend: {backend}") - messages.write(f" Model: {model}") - messages.write(f" Provider: {provider}") - messages.write(f" Turns: {tracker.turns}") - messages.write(f" Input tokens: {cum.input_tokens:,}") - messages.write(f" Output tokens: {cum.output_tokens:,}") - messages.write(f" Session tokens: ~{est:,}") - messages.write(f" Session messages: {len(self.hanzo_session.messages)}") - messages.write(f" Config files: {len(self.runtime_config.loaded_entries)}") - messages.write("") - - async def handle_cost_command(self) -> None: - """Show accumulated cost estimate.""" - tracker = self.tool_executor.usage_tracker if self.tool_executor else UsageTracker() - cum = tracker.cumulative_usage() - cost = _DEFAULT_PRICING.cost(cum) - self.show_message( - f"Cost: ${cost:.6f} ({cum.input_tokens:,} in / {cum.output_tokens:,} out, {tracker.turns} turns)", - "cyan", - ) - - async def handle_compact_command(self) -> None: - """Compact the session to reclaim context.""" - est_before = estimate_session_tokens(self.hanzo_session) - result = compact_session(self.hanzo_session, self.compaction_config) - if result.removed_message_count == 0: - self.show_message("Session too small to compact.", "yellow") - return - self.hanzo_session = result.compacted_session - est_after = estimate_session_tokens(self.hanzo_session) - self.show_message( - f"Compacted: removed {result.removed_message_count} messages, " - f"{est_before:,} -> {est_after:,} est tokens.", - "green", - ) - # Update context indicator - context_indicator = self.query_one("#permissions", ContextIndicator) - context_indicator.update_from_session(self.hanzo_session) - - async def handle_config_command(self) -> None: - """Show loaded configuration files.""" - messages = self.query_one("#messages", RichLog) - if not self.runtime_config.loaded_entries: - self.show_message("No config files loaded.", "yellow") - return - messages.write(Text("Config:", style="cyan")) - for entry in self.runtime_config.loaded_entries: - messages.write(f" [{entry.source.name}] {entry.path}") - mcp_servers = self.runtime_config.mcp_servers() - if mcp_servers: - messages.write(f" MCP servers: {', '.join(mcp_servers.keys())}") - messages.write("") - - async def handle_version_command(self) -> None: - """Show version info.""" - from hanzoai._version import __version__ as hanzoai_version - from hanzo_dev import __version__ as dev_version - messages = self.query_one("#messages", RichLog) - messages.write(Text("Version:", style="cyan")) - messages.write(f" hanzo-dev {dev_version}") - messages.write(f" hanzoai {hanzoai_version}") - messages.write("") - - async def handle_login_command(self) -> None: - """Login via PKCE flow.""" - from hanzoai.auth import HanzoAuth - - auth = HanzoAuth() - oauth_cfg = self.runtime_config.oauth() - - kwargs: Dict[str, Any] = {} - if oauth_cfg: - kwargs["authorize_url"] = oauth_cfg.authorize_url - kwargs["token_url"] = oauth_cfg.token_url - kwargs["client_id"] = oauth_cfg.client_id - if oauth_cfg.scopes: - kwargs["scopes"] = oauth_cfg.scopes - if oauth_cfg.callback_port: - kwargs["redirect_port"] = oauth_cfg.callback_port - - self.show_message("Starting PKCE login flow...", "yellow") - try: - token_set = await auth.login_with_pkce(**kwargs) - self.show_message(f"Logged in. Scopes: {', '.join(token_set.scopes)}", "green") - except Exception as e: - self.show_error(f"Login failed: {e}") - - async def handle_loop_command(self, arg: str) -> None: - """Run a prompt on a recurring interval. Usage: /loop """ - import asyncio - - parts = arg.split(maxsplit=1) - if len(parts) < 2: - self.show_error("Usage: /loop ") - return - try: - interval = int(parts[0]) - except ValueError: - self.show_error("Interval must be an integer (seconds).") - return - prompt = parts[1] - self.show_message(f"Looping every {interval}s: {prompt}", "yellow") - try: - while True: - await self.process_chat(prompt) - await asyncio.sleep(interval) - except asyncio.CancelledError: - self.show_message("Loop stopped.", "yellow") - - async def handle_permissions_command(self, arg: str) -> None: - """Show or switch permission mode.""" - modes = {"read-only": PermissionMode.Deny, "workspace-write": PermissionMode.Ask, "full-access": PermissionMode.Allow} - if not arg: - current = {v: k for k, v in modes.items()}.get(self.permission_mode, "full-access") - self.show_message(f"Permission mode: {current}", "cyan") - self.show_message(f"Options: {', '.join(modes.keys())}", "dim") - return - if arg not in modes: - self.show_error(f"Unknown mode. Use: {', '.join(modes.keys())}") - return - self.permission_mode = modes[arg] - if self.tool_executor: - self.tool_executor.permission_policy = PermissionPolicy(default_mode=self.permission_mode) - self.show_message(f"Permission mode: {arg}", "green") - - async def handle_resume_command(self, arg: str) -> None: - """Load saved session from JSON file.""" - path = Path(arg.strip()) if arg else self._session_path - if not path.is_file(): - self.show_error(f"No session file at {path}") - return - self.hanzo_session = HanzoSession.load(path) - est = estimate_session_tokens(self.hanzo_session) - self.show_message(f"Loaded: {len(self.hanzo_session.messages)} messages, ~{est:,} tokens", "green") - - async def handle_memory_command(self) -> None: - """Show loaded instruction files.""" - messages = self.query_one("#messages", RichLog) - messages.write(Text("Loaded instruction files:", style="cyan")) - for entry in self.runtime_config.loaded_entries: - messages.write(f" [{entry.source.name}] {entry.path}") - if not self.runtime_config.loaded_entries: - messages.write(" (none)") - messages.write("") - - async def handle_init_command(self) -> None: - """Create starter CLAUDE.md in cwd.""" - target = Path.cwd() / "CLAUDE.md" - if target.exists(): - self.show_message(f"Already exists: {target}", "yellow") - return - target.write_text("# Project Instructions\n\nAdd project-specific instructions here.\n") - self.show_message(f"Created {target}", "green") - - async def handle_export_command(self, arg: str) -> None: - """Export conversation to file.""" - outpath = Path(arg.strip()) if arg else Path("hanzo-export.md") - lines = [f"# Hanzo Session Export ({datetime.now().isoformat()})\n"] - for msg in self.hanzo_session.messages: - role = msg.role.value if hasattr(msg.role, "value") else str(msg.role) - text = "".join(b.text for b in msg.content if hasattr(b, "text")) - lines.append(f"## {role}\n\n{text}\n") - outpath.write_text("\n".join(lines)) - self.show_message(f"Exported {len(self.hanzo_session.messages)} messages to {outpath}", "green") - - async def handle_session_command(self, arg: str) -> None: - """List or switch saved sessions.""" - self._sessions_dir.mkdir(parents=True, exist_ok=True) - parts = arg.split(maxsplit=1) if arg else [] - if not parts or parts[0] == "list": - sessions = sorted(self._sessions_dir.glob("*.json")) - if not sessions: - self.show_message("No saved sessions.", "yellow") - return - messages = self.query_one("#messages", RichLog) - for s in sessions: - messages.write(f" {s.stem}") - messages.write("") - return - if parts[0] == "switch" and len(parts) > 1: - target = self._sessions_dir / f"{parts[1]}.json" - if not target.is_file(): - self.show_error(f"Session not found: {parts[1]}") - return - self.hanzo_session = HanzoSession.load(target) - self.show_message(f"Switched to session: {parts[1]}", "green") - return - self.show_error("Usage: /session [list|switch ]") - - async def handle_agent_command(self, system_prefix: str, arg: str, label: str) -> None: - """Send a prompt with a system prefix for agent commands.""" - if not arg: - self.show_error(f"Usage: /{label} ") - return - prompt = f"{system_prefix}\n\nUser request: {arg}" - await self.process_chat(prompt) - - async def show_backend_status(self) -> None: - """Show current backend status.""" - backend_name = self.backend_manager.current_backend - backend = self.backend_manager.get_backend() - - self.show_message("Backend Status:", "cyan") - messages = self.query_one("#messages", RichLog) - - messages.write(f" Current backend: {backend_name}") - messages.write(f" Config file: {backend.get_config_file()}") - - if backend_name == "claude": - auth_status = ( - "Authenticated" - if hasattr(backend, "authenticated") and backend.authenticated - else "Not authenticated" - ) - messages.write(f" Auth status: {auth_status}") - elif backend_name == "embedded": - messages.write(f" Model: {self.llm_client.current_model}") - messages.write(f" Provider: {self.llm_client.current_provider}") - - # Load config if available - config = await self.backend_manager.load_config() - if config: - messages.write("") - messages.write(" Configuration loaded from: " + backend.get_config_file()) - - messages.write("") - - async def show_tools(self) -> None: - """Show available MCP tools.""" - if not self.mcp_server or not self.mcp_server.tools: - self.show_error("MCP tools not initialized") - return - - from rich.table import Table - - table = Table(title="Available MCP Tools") - table.add_column("Tool", style="cyan", no_wrap=True) - table.add_column("Description", style="white") - table.add_column("Category", style="green") - - for tool_name, tool in sorted(self.mcp_server.tools.items()): - category = tool.__class__.__module__.split(".")[-1] - table.add_row(tool_name, tool.description[:60] + "...", category) - - console = Console() - with console.capture() as capture: - console.print(table) - - messages = self.query_one("#messages", RichLog) - for line in capture.get().split("\n"): - if line.strip(): - messages.write(line) - - messages.write("") - self.show_message(f"Total tools: {len(self.mcp_server.tools)}", "dim") - - -def main(): - """Run the Textual REPL.""" - app = HanzoTextualREPL() - app.run() - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-dev/src/hanzo_dev/tool_executor.py b/pkg/hanzo-dev/src/hanzo_dev/tool_executor.py deleted file mode 100644 index 1358819de..000000000 --- a/pkg/hanzo-dev/src/hanzo_dev/tool_executor.py +++ /dev/null @@ -1,361 +0,0 @@ -"""Tool executor for running MCP tools based on LLM responses.""" - -import json -import logging -from typing import Any, Dict, List, Optional - -from hanzo_mcp.server import HanzoMCPServer -from hanzoai.mcp import MCPClient, MCPClientError -from hanzoai.protocols import ( - PermissionMode, - PermissionOutcome, - PermissionPolicy, - PermissionPrompter, - PermissionRequest, - TokenUsage, - UsageTracker, -) -from rich.console import Console -from rich.panel import Panel - -logger = logging.getLogger(__name__) - - -class _DefaultPrompter: - """Prompter that allows everything (bypass mode).""" - - def decide(self, request: PermissionRequest) -> PermissionOutcome: - return PermissionOutcome.allow() - - -class ToolExecutor: - """Execute MCP tools based on LLM requests.""" - - def __init__( - self, - mcp_server: HanzoMCPServer, - backend, - permission_policy: Optional[PermissionPolicy] = None, - prompter: Optional[PermissionPrompter] = None, - ): - self.mcp_server = mcp_server - self.backend = backend # Can be LLMClient or BackendManager - self.console = Console() - self.conversation_history = [] - self.max_iterations = 10 # Prevent infinite loops - self.permission_policy = permission_policy or PermissionPolicy( - default_mode=PermissionMode.Allow, - ) - self.prompter = prompter or _DefaultPrompter() - self.usage_tracker = UsageTracker() - self._mcp_clients: dict[str, MCPClient] = {} - self._mcp_tools: dict[str, dict[str, Any]] = {} # server_name -> {tool_name: schema} - - # -- MCP client management ------------------------------------------------ - - async def register_mcp_server( - self, - server_name: str, - command: list[str], - env: dict[str, str] | None = None, - ) -> int: - """Connect to an MCP server subprocess via stdio and register its tools. - - Returns the number of tools discovered. - """ - client = MCPClient(server_command=command, env=env) - try: - await client.connect() - except MCPClientError as exc: - logger.warning("Failed to connect to MCP server %s: %s", server_name, exc) - return 0 - - tools = await client.list_tools() - self._mcp_clients[server_name] = client - self._mcp_tools[server_name] = {t["name"]: t for t in tools} - return len(tools) - - async def disconnect_mcp_servers(self) -> None: - """Disconnect all registered MCP server clients.""" - for client in self._mcp_clients.values(): - try: - await client.disconnect() - except Exception: - pass - self._mcp_clients.clear() - self._mcp_tools.clear() - - # -- Context management ---------------------------------------------------- - - def get_context(self) -> List[Dict[str, str]]: - """Get current conversation context.""" - return self.conversation_history.copy() - - def reset_context(self): - """Reset conversation context.""" - self.conversation_history = [] - - def _format_tools_for_llm(self) -> List[Dict[str, Any]]: - """Format MCP tools for LLM consumption (local + remote MCP servers).""" - tools = [] - - for tool_name, tool in self.mcp_server.tools.items(): - # Convert MCP tool to OpenAI function format - tool_spec = { - "type": "function", - "function": { - "name": tool_name, - "description": tool.description, - "parameters": tool.get_schema(), - }, - } - tools.append(tool_spec) - - # Append tools from external MCP servers - for server_name, server_tools in self._mcp_tools.items(): - for tname, tschema in server_tools.items(): - canonical = f"mcp__{server_name}__{tname}" - tool_spec = { - "type": "function", - "function": { - "name": canonical, - "description": tschema.get("description", ""), - "parameters": tschema.get("inputSchema", {}), - }, - } - tools.append(tool_spec) - - return tools - - async def _execute_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Any: - """Execute a single tool after checking permissions.""" - input_str = json.dumps(arguments) - - # Check permission policy before execution - outcome = self.permission_policy.authorize( - tool_name, input_str, self.prompter, - ) - if not outcome.allowed: - msg = f"Tool '{tool_name}' denied: {outcome.reason}" - self.console.print(Panel(f"[bold red]{msg}[/bold red]", border_style="red")) - raise PermissionError(msg) - - # Route to external MCP server if tool name matches mcp__server__tool - if tool_name.startswith("mcp__"): - return await self._execute_mcp_tool(tool_name, arguments) - - if tool_name not in self.mcp_server.tools: - raise ValueError(f"Unknown tool: {tool_name}") - - tool = self.mcp_server.tools[tool_name] - - # Display tool execution - self.console.print( - Panel( - f"[bold cyan]Executing:[/bold cyan] {tool_name}\n" - f"[dim]Arguments:[/dim] {json.dumps(arguments, indent=2)}", - border_style="blue", - ) - ) - - # Execute tool - try: - result = await tool.execute(**arguments) - - # Display result - if isinstance(result, str) and len(result) > 500: - # Truncate long results - display_result = result[:500] + "... (truncated)" - else: - display_result = result - - self.console.print( - Panel( - f"[bold green]Result:[/bold green]\n{display_result}", - border_style="green", - ) - ) - - return result - - except Exception as e: - self.console.print( - Panel(f"[bold red]Error:[/bold red] {str(e)}", border_style="red") - ) - raise - - async def _execute_mcp_tool( - self, canonical_name: str, arguments: Dict[str, Any] - ) -> Any: - """Execute a tool on an external MCP server via its client.""" - # Parse mcp__server__tool - parts = canonical_name.split("__", 2) - if len(parts) != 3: - raise ValueError(f"Invalid MCP tool name: {canonical_name}") - _, server_name, tool_name = parts - - client = self._mcp_clients.get(server_name) - if client is None: - raise ValueError(f"No MCP client for server: {server_name}") - - self.console.print( - Panel( - f"[bold cyan]MCP Call:[/bold cyan] {server_name}/{tool_name}\n" - f"[dim]Arguments:[/dim] {json.dumps(arguments, indent=2)}", - border_style="blue", - ) - ) - - try: - result = await client.call_tool(tool_name, arguments) - content = result.get("content", []) - text_parts = [c.get("text", "") for c in content if c.get("type") == "text"] - output = "\n".join(text_parts) if text_parts else json.dumps(result) - - display = output[:500] + "... (truncated)" if len(output) > 500 else output - self.console.print( - Panel(f"[bold green]Result:[/bold green]\n{display}", border_style="green") - ) - return output - except MCPClientError as e: - self.console.print( - Panel(f"[bold red]MCP Error:[/bold red] {e}", border_style="red") - ) - raise - - async def execute_with_tools(self, user_message: str) -> str: - """Execute a user message with MCP tool support.""" - # Add user message to history - self.conversation_history.append({"role": "user", "content": user_message}) - - # Get available tools - tools = self._format_tools_for_llm() - - # System prompt - system_prompt = """You are a helpful AI assistant with access to various tools via the Model Context Protocol (MCP). -You can use these tools to help users with file operations, code execution, searching, and more. - -When using tools: -1. Be precise with tool arguments -2. Use tools when they would be helpful -3. Explain what you're doing -4. Handle errors gracefully -5. Provide clear, helpful responses - -Available tool categories: -- File operations (read, write, edit, search) -- Shell commands (run_command) -- Code analysis (grep, search) -- Database operations (SQL queries) -- And more... -""" - - messages = [ - {"role": "system", "content": system_prompt}, - *self.conversation_history, - ] - - iterations = 0 - final_response = "" - - while iterations < self.max_iterations: - iterations += 1 - - # Call backend with tools - if hasattr(self.backend, "chat"): - # Direct backend (BackendManager) - response_text = await self.backend.chat( - messages[-1]["content"], # Just the last user message - tools=tools, - ) - # Create a response object that matches expected format - from types import SimpleNamespace - - response = SimpleNamespace( - choices=[ - SimpleNamespace( - message=SimpleNamespace( - content=response_text, tool_calls=None - ) - ) - ] - ) - else: - # Legacy LLMClient - response = await self.backend.chat( - messages=messages, tools=tools, tool_choice="auto" - ) - - # Extract response - message = response.choices[0].message - - # Check if LLM wants to use tools - if hasattr(message, "tool_calls") and message.tool_calls: - # Add assistant message with tool calls - messages.append( - { - "role": "assistant", - "content": message.content or "", - "tool_calls": message.tool_calls, - } - ) - - # Execute each tool call - for tool_call in message.tool_calls: - tool_name = tool_call.function.name - try: - arguments = json.loads(tool_call.function.arguments) - except json.JSONDecodeError: - arguments = {} - - try: - # Execute tool - result = await self._execute_tool(tool_name, arguments) - - # Add tool result to messages - messages.append( - { - "role": "tool", - "tool_call_id": tool_call.id, - "content": ( - json.dumps(result) - if not isinstance(result, str) - else result - ), - } - ) - - except Exception as e: - # Add error to messages - messages.append( - { - "role": "tool", - "tool_call_id": tool_call.id, - "content": f"Error: {str(e)}", - } - ) - - # Continue conversation - continue - - # No tool calls, we have the final response - final_response = message.content - - # Add to history - self.conversation_history.append( - {"role": "assistant", "content": final_response} - ) - - # Track token usage estimate (char_count / 4 heuristic) - est_input = len(str(messages)) // 4 - est_output = len(final_response) // 4 if final_response else 0 - self.usage_tracker.record( - TokenUsage(input_tokens=est_input, output_tokens=est_output) - ) - - break - - if iterations >= self.max_iterations: - final_response = "Maximum iterations reached. Please try a simpler request." - - return final_response diff --git a/pkg/hanzo-dev/src/hanzo_dev/vim_mode.py b/pkg/hanzo-dev/src/hanzo_dev/vim_mode.py deleted file mode 100644 index 6aa640a05..000000000 --- a/pkg/hanzo-dev/src/hanzo_dev/vim_mode.py +++ /dev/null @@ -1,221 +0,0 @@ -"""Vim keybinding support for Textual Input widget.""" - -from dataclasses import dataclass, field -from enum import Enum, auto -from typing import Protocol, runtime_checkable - - -class VimMode(Enum): - Normal = auto() - Insert = auto() - Visual = auto() - Command = auto() - - -@runtime_checkable -class InputWidget(Protocol): - @property - def cursor_position(self) -> int: ... - @cursor_position.setter - def cursor_position(self, value: int) -> None: ... - @property - def value(self) -> str: ... - @value.setter - def value(self, value: str) -> None: ... - - -@dataclass -class VimState: - mode: VimMode = VimMode.Normal - register: str = "" - visual_anchor: int = 0 - command_buffer: str = "" - _pending: str = "" - _undo_stack: list[tuple[str, int]] = field(default_factory=list) - - def handle_key(self, key: str, widget: InputWidget) -> bool | str: - """Handle a keypress. Returns True if consumed, a command string, or False.""" - handler = { - VimMode.Normal: self._normal, VimMode.Insert: self._insert, - VimMode.Visual: self._visual, VimMode.Command: self._command, - }.get(self.mode) - return handler(key, widget) if handler else False - - # -- Normal mode -- - - def _normal(self, key: str, widget: InputWidget) -> bool | str: - val, pos = widget.value, widget.cursor_position - if self._pending: - return self._do_pending(key, widget) - # Motions - if key == "h": - widget.cursor_position = max(0, pos - 1); return True - if key == "l": - widget.cursor_position = min(len(val), pos + 1); return True - if key in ("j", "k"): - return True - if key == "0": - widget.cursor_position = 0; return True - if key == "$": - widget.cursor_position = len(val); return True - if key == "w": - widget.cursor_position = self._next_word(val, pos); return True - if key == "b": - widget.cursor_position = self._prev_word(val, pos); return True - if key == "e": - widget.cursor_position = self._end_word(val, pos); return True - # Insert mode entries - if key == "i": - self._save_undo(widget); self.mode = VimMode.Insert; return True - if key == "a": - self._save_undo(widget); self.mode = VimMode.Insert - widget.cursor_position = min(len(val), pos + 1); return True - if key == "I": - self._save_undo(widget); self.mode = VimMode.Insert - widget.cursor_position = 0; return True - if key == "A": - self._save_undo(widget); self.mode = VimMode.Insert - widget.cursor_position = len(val); return True - if key in ("o", "O"): - self._save_undo(widget); self.mode = VimMode.Insert - widget.cursor_position = len(val) if key == "o" else 0; return True - # Two-char starters - if key in ("d", "y", "g"): - self._pending = key; return True - # Single-char editing - if key == "x": - if pos < len(val): - self._save_undo(widget); self.register = val[pos] - widget.value = val[:pos] + val[pos + 1:] - widget.cursor_position = min(pos, max(0, len(widget.value) - 1)) - return True - if key == "p" and self.register: - self._save_undo(widget); np = pos + 1 - widget.value = val[:np] + self.register + val[np:] - widget.cursor_position = np + len(self.register) - 1; return True - if key == "u": - self._pop_undo(widget); return True - if key == "v": - self.mode = VimMode.Visual; self.visual_anchor = pos; return True - if key == ":": - self.mode = VimMode.Command; self.command_buffer = ""; return True - if key == "/": - self.mode = VimMode.Command; self.command_buffer = "/"; return True - if key == "escape": - return True - if key == "p": - return True - return False - - def _do_pending(self, key: str, widget: InputWidget) -> bool: - seq, self._pending = self._pending + key, "" - val = widget.value - if seq == "dd": - self._save_undo(widget); self.register = val - widget.value = ""; widget.cursor_position = 0; return True - if seq == "yy": - self.register = val; return True - if seq == "gg": - widget.cursor_position = 0; return True - if seq[0] in ("d", "y"): - return self._op_motion(seq[0], key, widget) - return True - - def _op_motion(self, op: str, motion: str, widget: InputWidget) -> bool: - val, pos = widget.value, widget.cursor_position - target = self._motion_target(motion, val, pos) - if target is None: - return True - start, end = (min(pos, target), max(pos, target)) - self.register = val[start:end] - if op == "d": - self._save_undo(widget) - widget.value = val[:start] + val[end:] - widget.cursor_position = start - return True - - def _motion_target(self, m: str, val: str, pos: int) -> int | None: - return {"w": self._next_word, "b": self._prev_word, "e": self._end_word}.get(m, lambda v, p: {"$": len(v), "0": 0}.get(m))(val, pos) - - # -- Insert mode -- - - def _insert(self, key: str, widget: InputWidget) -> bool | str: - if key == "escape": - self.mode = VimMode.Normal - if widget.cursor_position > 0: - widget.cursor_position -= 1 - return True - return False - - # -- Visual mode -- - - def _visual(self, key: str, widget: InputWidget) -> bool | str: - val, pos = widget.value, widget.cursor_position - if key == "escape": - self.mode = VimMode.Normal; return True - if key == "h": - widget.cursor_position = max(0, pos - 1); return True - if key == "l": - widget.cursor_position = min(len(val), pos + 1); return True - if key in ("y", "d"): - start, end = min(self.visual_anchor, pos), max(self.visual_anchor, pos) + 1 - self.register = val[start:end] - if key == "d": - self._save_undo(widget) - widget.value = val[:start] + val[end:] - widget.cursor_position = start - self.mode = VimMode.Normal; return True - return True - - # -- Command mode -- - - def _command(self, key: str, widget: InputWidget) -> bool | str: - if key == "escape": - self.mode = VimMode.Normal; self.command_buffer = ""; return True - if key == "enter": - cmd = self.command_buffer; self.mode = VimMode.Normal - self.command_buffer = ""; return cmd if cmd else True - if key == "backspace": - if self.command_buffer: - self.command_buffer = self.command_buffer[:-1] - else: - self.mode = VimMode.Normal - return True - if len(key) == 1: - self.command_buffer += key - return True - - # -- Word motion helpers -- - - @staticmethod - def _next_word(val: str, pos: int) -> int: - n, i = len(val), pos - while i < n and not val[i].isspace(): i += 1 - while i < n and val[i].isspace(): i += 1 - return i - - @staticmethod - def _prev_word(val: str, pos: int) -> int: - if pos <= 0: return 0 - i = pos - 1 - while i > 0 and val[i].isspace(): i -= 1 - while i > 0 and not val[i - 1].isspace(): i -= 1 - return i - - @staticmethod - def _end_word(val: str, pos: int) -> int: - n = len(val) - if pos >= n - 1: return max(0, n - 1) - i = pos + 1 - while i < n and val[i].isspace(): i += 1 - while i < n - 1 and not val[i + 1].isspace(): i += 1 - return i - - # -- Undo -- - - def _save_undo(self, w: InputWidget) -> None: - self._undo_stack.append((w.value, w.cursor_position)) - - def _pop_undo(self, w: InputWidget) -> None: - if self._undo_stack: - v, p = self._undo_stack.pop(); w.value = v; w.cursor_position = p diff --git a/pkg/hanzo-dev/src/hanzo_dev/voice_mode.py b/pkg/hanzo-dev/src/hanzo_dev/voice_mode.py deleted file mode 100644 index 0c8b6cf2b..000000000 --- a/pkg/hanzo-dev/src/hanzo_dev/voice_mode.py +++ /dev/null @@ -1,215 +0,0 @@ -"""Voice mode for bidirectional communication.""" - -import queue -import threading -import time -from typing import Callable, Optional - -try: - import numpy as np - import pyttsx3 - import sounddevice as sd - import speech_recognition as sr - - VOICE_AVAILABLE = True -except ImportError: - VOICE_AVAILABLE = False - - -class VoiceMode: - """Handles voice input/output for the REPL.""" - - def __init__(self): - if not VOICE_AVAILABLE: - raise ImportError( - "Voice dependencies not installed. Run: pip install speechrecognition pyttsx3 pyaudio" - ) - - # Speech recognition - self.recognizer = sr.Recognizer() - self.microphone = sr.Microphone() - - # Text to speech - self.tts_engine = pyttsx3.init() - self._setup_tts() - - # State - self.is_active = False - self.is_listening = False - self.speech_queue = queue.Queue() - self.stop_event = threading.Event() - - # Callbacks - self.on_speech_recognized = None - self.on_listening_started = None - self.on_listening_stopped = None - - def _setup_tts(self): - """Configure TTS engine.""" - # Set properties - voices = self.tts_engine.getProperty("voices") - - # Try to find a nice voice (prefer female voices) - for voice in voices: - if "female" in voice.name.lower() or "samantha" in voice.name.lower(): - self.tts_engine.setProperty("voice", voice.id) - break - - # Set rate and volume - self.tts_engine.setProperty("rate", 180) # Speed - self.tts_engine.setProperty("volume", 0.9) # Volume - - def start(self, on_speech: Optional[Callable[[str], None]] = None): - """Start voice mode.""" - self.is_active = True - self.on_speech_recognized = on_speech - - # Start listening thread - self.listen_thread = threading.Thread(target=self._listen_loop, daemon=True) - self.listen_thread.start() - - # Play activation sound - self._play_sound("activated") - self.speak("Voice mode activated. I'm listening.") - - def stop(self): - """Stop voice mode.""" - self.is_active = False - self.stop_event.set() - - # Play deactivation sound - self._play_sound("deactivated") - self.speak("Voice mode deactivated.") - - # Wait for thread to stop - if hasattr(self, "listen_thread"): - self.listen_thread.join(timeout=2) - - def _listen_loop(self): - """Main listening loop.""" - with self.microphone as source: - # Adjust for ambient noise - self.recognizer.adjust_for_ambient_noise(source, duration=1) - - while self.is_active and not self.stop_event.is_set(): - try: - # Signal listening started - self.is_listening = True - if self.on_listening_started: - self.on_listening_started() - - # Listen with timeout - audio = self.recognizer.listen( - source, timeout=1, phrase_time_limit=10 - ) - - # Signal listening stopped - self.is_listening = False - if self.on_listening_stopped: - self.on_listening_stopped() - - # Recognize speech - try: - text = self.recognizer.recognize_google(audio) - if text and self.on_speech_recognized: - self.on_speech_recognized(text) - except sr.UnknownValueError: - # Could not understand audio - pass - except sr.RequestError as e: - # API error - if self.on_speech_recognized: - self.on_speech_recognized(f"[Voice Error: {e}]") - - except sr.WaitTimeoutError: - # No speech detected - self.is_listening = False - continue - except Exception as e: - # Other error - self.is_listening = False - if self.on_speech_recognized: - self.on_speech_recognized(f"[Voice Error: {e}]") - time.sleep(0.5) - - def speak(self, text: str, wait: bool = False): - """Convert text to speech.""" - - def _speak(): - self.tts_engine.say(text) - self.tts_engine.runAndWait() - - if wait: - _speak() - else: - # Run in thread to avoid blocking - threading.Thread(target=_speak, daemon=True).start() - - def _play_sound(self, sound_type: str): - """Play UI sounds.""" - # Generate simple beeps using sounddevice - try: - duration = 0.1 - sample_rate = 44100 - - if sound_type == "activated": - # Rising tone - frequency = [440, 880] - elif sound_type == "deactivated": - # Falling tone - frequency = [880, 440] - else: - frequency = [440] - - # Generate and play tones - for freq in frequency: - t = np.linspace(0, duration, int(sample_rate * duration)) - wave = 0.3 * np.sin(2 * np.pi * freq * t) - sd.play(wave, sample_rate) - sd.wait() - - except Exception: # noqa: S110 - Silent fallback to TTS if audio fails - # Fallback to TTS beep - pass - - -class VoiceCommands: - """Voice command processor.""" - - WAKE_WORDS = ["hey hanzo", "hanzo", "computer", "assistant"] - STOP_WORDS = ["stop", "cancel", "never mind", "exit voice"] - - @staticmethod - def process_voice_input(text: str) -> tuple[str, bool]: - """Process voice input and extract commands.""" - text_lower = text.lower() - - # Check for stop commands - for stop_word in VoiceCommands.STOP_WORDS: - if stop_word in text_lower: - return "", True - - # Remove wake words - for wake_word in VoiceCommands.WAKE_WORDS: - if text_lower.startswith(wake_word): - text = text[len(wake_word) :].strip() - break - - # Voice shortcuts - voice_shortcuts = { - "run command": "!", - "bash": "!", - "search for": "/search", - "find file": "@", - "memorize this": "#", - "show commands": "cmd+k", - "clear screen": "ctrl+l", - "what can you do": "?", - } - - for voice_cmd, shortcut in voice_shortcuts.items(): - if text_lower.startswith(voice_cmd): - remainder = text[len(voice_cmd) :].strip() - return f"{shortcut} {remainder}", False - - return text, False diff --git a/pkg/hanzo-dev/tests/__init__.py b/pkg/hanzo-dev/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-dev/tests/test_git_commands.py b/pkg/hanzo-dev/tests/test_git_commands.py deleted file mode 100644 index b519338fd..000000000 --- a/pkg/hanzo-dev/tests/test_git_commands.py +++ /dev/null @@ -1,117 +0,0 @@ -"""Tests for git slash commands using a temporary git repo.""" - -import os -import subprocess -from pathlib import Path - -import pytest - -from hanzo_dev.git_commands import ( - git_branch, - git_commit, - git_diff, - git_stash, - git_status, - git_worktree, - handle_git_command, -) - -_ENV = { - **os.environ, - "GIT_AUTHOR_NAME": "test", - "GIT_AUTHOR_EMAIL": "t@t", - "GIT_COMMITTER_NAME": "test", - "GIT_COMMITTER_EMAIL": "t@t", -} - - -def _run(*args: str, cwd: Path) -> None: - subprocess.run(args, cwd=cwd, capture_output=True, text=True, env=_ENV, check=True) - - -@pytest.fixture() -def git_repo(tmp_path: Path) -> Path: - """Create a temporary git repo with one committed file.""" - _run("git", "init", "-b", "main", cwd=tmp_path) - (tmp_path / "README.md").write_text("# test\n") - _run("git", "add", "README.md", cwd=tmp_path) - _run("git", "commit", "-m", "init", cwd=tmp_path) - return tmp_path - - -class TestGitStatus: - def test_clean_repo(self, git_repo: Path) -> None: - out = git_status(git_repo) - assert "nothing to commit" in out.lower() or "clean" in out.lower() - - def test_dirty_repo(self, git_repo: Path) -> None: - (git_repo / "new.txt").write_text("hello\n") - out = git_status(git_repo) - assert "new.txt" in out - - -class TestGitBranch: - def test_list_branches(self, git_repo: Path) -> None: - out = git_branch("", git_repo) - assert "main" in out - - def test_create_branch(self, git_repo: Path) -> None: - out = git_branch("feature-x", git_repo) - assert "feature-x" in out - listing = git_branch("", git_repo) - assert "feature-x" in listing - - -class TestGitCommit: - def test_commit_staged(self, git_repo: Path) -> None: - (git_repo / "a.txt").write_text("a\n") - out = git_commit("add a.txt", git_repo) - assert "add a.txt" in out or "1 file changed" in out or "create mode" in out - - def test_commit_nothing(self, git_repo: Path) -> None: - out = git_commit("empty", git_repo) - assert "nothing" in out.lower() or "no changes" in out.lower() - - -class TestGitDiff: - def test_no_diff(self, git_repo: Path) -> None: - out = git_diff(git_repo) - assert out.strip() == "" or "no changes" in out.lower() - - def test_has_diff(self, git_repo: Path) -> None: - (git_repo / "README.md").write_text("# changed\n") - out = git_diff(git_repo) - assert "changed" in out - - -class TestGitStash: - def test_stash_and_pop(self, git_repo: Path) -> None: - (git_repo / "README.md").write_text("# stashed\n") - out = git_stash("", git_repo) - assert "saved" in out.lower() or "stash" in out.lower() - - list_out = git_stash("list", git_repo) - assert "stash@" in list_out or "stash" in list_out.lower() - - git_stash("pop", git_repo) - assert "stashed" in (git_repo / "README.md").read_text() - - def test_stash_nothing(self, git_repo: Path) -> None: - out = git_stash("", git_repo) - assert "no local changes" in out.lower() or "nothing" in out.lower() or "no changes" in out.lower() - - -class TestGitWorktree: - def test_list_worktrees(self, git_repo: Path) -> None: - out = git_worktree("", git_repo) - assert str(git_repo) in out or "main" in out - - -class TestDispatcher: - def test_known_command(self, git_repo: Path) -> None: - out = handle_git_command("status", "", git_repo) - assert out is not None - - def test_unknown_command(self, git_repo: Path) -> None: - out = handle_git_command("frobnicate", "", git_repo) - assert out is None diff --git a/pkg/hanzo-flow/README.md b/pkg/hanzo-flow/README.md deleted file mode 100644 index 6203990ec..000000000 --- a/pkg/hanzo-flow/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# hanzo-flow - -Hanzo Flow โ€” visual workflow builder for AI applications. - -## Install - -```bash -pip install hanzo-flow -``` - -## Usage - -```bash -hanzo-flow run --host 0.0.0.0 --port 7860 -``` - -Part of the [Hanzo Python SDK](https://github.com/hanzoai/python-sdk). diff --git a/pkg/hanzo-flow/pyproject.toml b/pkg/hanzo-flow/pyproject.toml deleted file mode 100644 index c04636068..000000000 --- a/pkg/hanzo-flow/pyproject.toml +++ /dev/null @@ -1,20 +0,0 @@ -[project] -name = "hanzo-flow" -version = "1.8.0" -description = "Hanzo Flow โ€” visual workflow builder for AI applications" -license = "MIT" -authors = [{ name = "Hanzo AI", email = "oss@hanzo.ai" }] -requires-python = ">=3.12" -dependencies = [ - "hanzo-flow>=1.8.0", -] - -[project.scripts] -flow = "flow.launcher:main" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["src/hanzo_flow"] diff --git a/pkg/hanzo-flow/src/hanzo_flow/__init__.py b/pkg/hanzo-flow/src/hanzo_flow/__init__.py deleted file mode 100644 index 9f74e9956..000000000 --- a/pkg/hanzo-flow/src/hanzo_flow/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Hanzo Flow โ€” visual workflow builder for AI applications. - -This package re-exports flow and provides the `hanzo-flow` CLI command. -""" -__version__ = "1.8.0" - -# Re-export for convenience -try: - from flow import load_flow_from_json, run_flow # noqa: F401 -except ImportError: - pass diff --git a/pkg/hanzo-hooks/hanzo_hooks/__init__.py b/pkg/hanzo-hooks/hanzo_hooks/__init__.py deleted file mode 100644 index 2f42d8a69..000000000 --- a/pkg/hanzo-hooks/hanzo_hooks/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""hanzo-hooks: shell hook runner for pre/post tool-use lifecycle events.""" - -from .runner import HookRunner -from .types import HookConfig, HookEvent, HookRunResult - -__all__ = ["HookConfig", "HookEvent", "HookRunner", "HookRunResult"] diff --git a/pkg/hanzo-hooks/hanzo_hooks/runner.py b/pkg/hanzo-hooks/hanzo_hooks/runner.py deleted file mode 100644 index 26562d29c..000000000 --- a/pkg/hanzo-hooks/hanzo_hooks/runner.py +++ /dev/null @@ -1,97 +0,0 @@ -"""HookRunner: execute shell commands around tool invocations.""" -from __future__ import annotations - -import json -import os -import subprocess -import sys - -from .types import HookConfig, HookEvent, HookRunResult - - -class HookRunner: - __slots__ = ("_config",) - - def __init__(self, config: HookConfig) -> None: - self._config = config - - @classmethod - def from_settings(cls, path: str) -> HookRunner: - return cls(HookConfig.from_json(path)) - - def run_pre_tool_use(self, tool_name: str, tool_input: str) -> HookRunResult: - return self._run_commands( - HookEvent.PreToolUse, self._config.pre_tool_use, tool_name, tool_input, - ) - - def run_post_tool_use( - self, tool_name: str, tool_input: str, tool_output: str, is_error: bool = False, - ) -> HookRunResult: - return self._run_commands( - HookEvent.PostToolUse, self._config.post_tool_use, - tool_name, tool_input, tool_output=tool_output, is_error=is_error, - ) - - def _run_commands( - self, event: HookEvent, commands: list[str], tool_name: str, tool_input: str, - tool_output: str | None = None, is_error: bool = False, - ) -> HookRunResult: - if not commands: - return HookRunResult.allow() - - try: - parsed_input = json.loads(tool_input) - except (json.JSONDecodeError, TypeError): - parsed_input = {"raw": tool_input} - - payload = json.dumps({ - "hook_event_name": event.value, "tool_name": tool_name, - "tool_input": parsed_input, "tool_input_json": tool_input, - "tool_output": tool_output, "tool_result_is_error": is_error, - }) - env = { - "HOOK_EVENT": event.value, "HOOK_TOOL_NAME": tool_name, - "HOOK_TOOL_INPUT": tool_input, "HOOK_TOOL_IS_ERROR": "1" if is_error else "0", - } - if tool_output is not None: - env["HOOK_TOOL_OUTPUT"] = tool_output - - messages: list[str] = [] - for command in commands: - kind, msg = _run_one(command, event, tool_name, env, payload) - if kind == "allow": - if msg: - messages.append(msg) - elif kind == "deny": - messages.append(msg or f"{event.value} hook denied tool `{tool_name}`") - return HookRunResult(denied=True, messages=messages) - else: - messages.append(msg) - return HookRunResult.allow(messages) - - -def _run_one( - command: str, event: HookEvent, tool_name: str, - env: dict[str, str], payload: str, -) -> tuple[str, str]: - """Returns (outcome_type, message). outcome_type: allow/deny/warn.""" - args = ["cmd", "/C", command] if sys.platform == "win32" else ["sh", "-lc", command] - try: - proc = subprocess.run( - args, input=payload, capture_output=True, text=True, - env={**os.environ, **env}, - ) - except OSError as exc: - return ("warn", f"{event.value} hook `{command}` failed to start for `{tool_name}`: {exc}") - - stdout, stderr = proc.stdout.strip(), proc.stderr.strip() - if proc.returncode == 0: - return ("allow", stdout) - if proc.returncode == 2: - return ("deny", stdout) - msg = f"Hook `{command}` exited with status {proc.returncode}; allowing tool execution to continue" - if stdout: - msg += f": {stdout}" - elif stderr: - msg += f": {stderr}" - return ("warn", msg) diff --git a/pkg/hanzo-hooks/hanzo_hooks/types.py b/pkg/hanzo-hooks/hanzo_hooks/types.py deleted file mode 100644 index e67c87467..000000000 --- a/pkg/hanzo-hooks/hanzo_hooks/types.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Types for the hook runner system.""" - -from __future__ import annotations - -import json -from dataclasses import dataclass, field -from enum import Enum - - -class HookEvent(Enum): - PreToolUse = "PreToolUse" - PostToolUse = "PostToolUse" - - -@dataclass(frozen=True, slots=True) -class HookRunResult: - denied: bool - messages: list[str] - - @classmethod - def allow(cls, messages: list[str] | None = None) -> HookRunResult: - return cls(denied=False, messages=messages or []) - - def to_permission_outcome(self) -> object: - """Convert to hanzoai.protocols.PermissionOutcome if available. - - Returns a duck-typed object with .allowed and .reason when the - hanzoai package is not installed, so callers can use it without - a hard dependency. - """ - try: - from hanzoai.protocols import PermissionOutcome - except ImportError: - PermissionOutcome = None - - if PermissionOutcome is not None: - if self.denied: - return PermissionOutcome.deny("; ".join(self.messages)) - return PermissionOutcome.allow() - - # Fallback: return a simple namespace matching the protocol. - if self.denied: - return _Outcome(allowed=False, reason="; ".join(self.messages)) - return _Outcome(allowed=True, reason="") - - -@dataclass(frozen=True, slots=True) -class _Outcome: - """Minimal stand-in for PermissionOutcome when hanzoai is not installed.""" - allowed: bool - reason: str - - -@dataclass(frozen=True, slots=True) -class HookConfig: - """Loadable from settings.json ``hooks`` key.""" - pre_tool_use: list[str] = field(default_factory=list) - post_tool_use: list[str] = field(default_factory=list) - - @classmethod - def from_dict(cls, d: dict) -> HookConfig: - return cls( - pre_tool_use=list(d.get("pre_tool_use") or d.get("PreToolUse") or []), - post_tool_use=list(d.get("post_tool_use") or d.get("PostToolUse") or []), - ) - - @classmethod - def from_json(cls, path: str) -> HookConfig: - with open(path) as f: - data = json.load(f) - hooks = data.get("hooks", data) - return cls.from_dict(hooks) diff --git a/pkg/hanzo-hooks/pyproject.toml b/pkg/hanzo-hooks/pyproject.toml deleted file mode 100644 index 0fce9c1ef..000000000 --- a/pkg/hanzo-hooks/pyproject.toml +++ /dev/null @@ -1,26 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-hooks" -version = "0.1.0" -description = "Shell hook runner for pre/post tool-use lifecycle events." -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -keywords = ["hanzo", "hooks", "tools", "ai"] -dependencies = [] - -[project.urls] -"Homepage" = "https://github.com/hanzoai/python-sdk" -"Bug Tracker" = "https://github.com/hanzoai/python-sdk/issues" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_hooks*"] diff --git a/pkg/hanzo-hooks/tests/__init__.py b/pkg/hanzo-hooks/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-hooks/tests/test_hooks.py b/pkg/hanzo-hooks/tests/test_hooks.py deleted file mode 100644 index b1c019994..000000000 --- a/pkg/hanzo-hooks/tests/test_hooks.py +++ /dev/null @@ -1,161 +0,0 @@ -"""Tests for hanzo_hooks.""" - -from __future__ import annotations - -import json -import tempfile -from pathlib import Path - -from hanzo_hooks import HookConfig, HookEvent, HookRunner, HookRunResult - - -class TestHookEvent: - def test_values(self): - assert HookEvent.PreToolUse.value == "PreToolUse" - assert HookEvent.PostToolUse.value == "PostToolUse" - - -class TestHookRunResult: - def test_allow(self): - r = HookRunResult.allow() - assert not r.denied - assert r.messages == [] - - def test_allow_with_messages(self): - r = HookRunResult.allow(["msg1", "msg2"]) - assert not r.denied - assert r.messages == ["msg1", "msg2"] - - def test_denied(self): - r = HookRunResult(denied=True, messages=["blocked"]) - assert r.denied - assert r.messages == ["blocked"] - - def test_to_permission_outcome_allow(self): - r = HookRunResult.allow() - outcome = r.to_permission_outcome() - assert outcome.allowed is True - - def test_to_permission_outcome_deny(self): - r = HookRunResult(denied=True, messages=["reason A", "reason B"]) - outcome = r.to_permission_outcome() - assert outcome.allowed is False - assert "reason A" in outcome.reason - assert "reason B" in outcome.reason - - -class TestHookConfig: - def test_from_dict(self): - cfg = HookConfig.from_dict({ - "pre_tool_use": ["echo pre"], - "post_tool_use": ["echo post"], - }) - assert cfg.pre_tool_use == ["echo pre"] - assert cfg.post_tool_use == ["echo post"] - - def test_from_dict_camel_case(self): - cfg = HookConfig.from_dict({ - "PreToolUse": ["echo pre"], - "PostToolUse": ["echo post"], - }) - assert cfg.pre_tool_use == ["echo pre"] - assert cfg.post_tool_use == ["echo post"] - - def test_from_dict_empty(self): - cfg = HookConfig.from_dict({}) - assert cfg.pre_tool_use == [] - assert cfg.post_tool_use == [] - - def test_from_json(self): - with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: - json.dump({"hooks": {"pre_tool_use": ["echo hi"]}}, f) - f.flush() - cfg = HookConfig.from_json(f.name) - assert cfg.pre_tool_use == ["echo hi"] - assert cfg.post_tool_use == [] - Path(f.name).unlink() - - -class TestHookRunner: - def test_no_commands_returns_allow(self): - runner = HookRunner(HookConfig()) - result = runner.run_pre_tool_use("Read", '{"path":"README.md"}') - assert not result.denied - assert result.messages == [] - - def test_exit_zero_captures_stdout(self): - runner = HookRunner(HookConfig(pre_tool_use=["printf 'pre ok'"])) - result = runner.run_pre_tool_use("Read", '{"path":"README.md"}') - assert not result.denied - assert result.messages == ["pre ok"] - - def test_exit_two_denies(self): - runner = HookRunner(HookConfig(pre_tool_use=["printf 'blocked'; exit 2"])) - result = runner.run_pre_tool_use("Bash", '{"command":"pwd"}') - assert result.denied - assert result.messages == ["blocked"] - - def test_exit_two_without_stdout_uses_default_message(self): - runner = HookRunner(HookConfig(pre_tool_use=["exit 2"])) - result = runner.run_pre_tool_use("Bash", '{"command":"pwd"}') - assert result.denied - assert "denied" in result.messages[0] - assert "Bash" in result.messages[0] - - def test_other_exit_code_warns_but_allows(self): - runner = HookRunner(HookConfig(pre_tool_use=["printf 'oops'; exit 1"])) - result = runner.run_pre_tool_use("Edit", '{"file":"lib.py"}') - assert not result.denied - assert len(result.messages) == 1 - assert "allowing tool execution to continue" in result.messages[0] - - def test_short_circuit_on_deny(self): - runner = HookRunner(HookConfig(pre_tool_use=[ - "printf 'first ok'", - "printf 'deny'; exit 2", - "printf 'never reached'", - ])) - result = runner.run_pre_tool_use("Bash", '{}') - assert result.denied - assert result.messages == ["first ok", "deny"] - - def test_post_tool_use(self): - runner = HookRunner(HookConfig(post_tool_use=["printf 'post ok'"])) - result = runner.run_post_tool_use("Read", '{}', "file contents", is_error=False) - assert not result.denied - assert result.messages == ["post ok"] - - def test_env_vars_passed(self): - runner = HookRunner(HookConfig(pre_tool_use=[ - 'printf "%s %s" "$HOOK_EVENT" "$HOOK_TOOL_NAME"' - ])) - result = runner.run_pre_tool_use("Bash", '{"command":"ls"}') - assert not result.denied - assert result.messages == ["PreToolUse Bash"] - - def test_stdin_payload(self): - runner = HookRunner(HookConfig(pre_tool_use=[ - """python3 -c "import sys, json; d=json.load(sys.stdin); print(d['tool_name'])" """ - ])) - result = runner.run_pre_tool_use("Grep", '{"pattern":"foo"}') - assert not result.denied - assert result.messages == ["Grep"] - - def test_multiple_commands_collect_messages(self): - runner = HookRunner(HookConfig(pre_tool_use=[ - "printf 'msg1'", - "printf 'msg2'", - ])) - result = runner.run_pre_tool_use("Read", '{}') - assert not result.denied - assert result.messages == ["msg1", "msg2"] - - def test_from_settings(self): - with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: - json.dump({"hooks": {"pre_tool_use": ["printf 'from settings'"]}}, f) - f.flush() - runner = HookRunner.from_settings(f.name) - result = runner.run_pre_tool_use("Bash", '{}') - assert not result.denied - assert result.messages == ["from settings"] - Path(f.name).unlink() diff --git a/pkg/hanzo-iam/README.md b/pkg/hanzo-iam/README.md deleted file mode 100644 index dee542e7c..000000000 --- a/pkg/hanzo-iam/README.md +++ /dev/null @@ -1,159 +0,0 @@ -# hanzoai-iam - -Identity and Access Management SDK for the Hanzo ecosystem. Built on Casdoor with organization-aware multi-tenancy. - -The PyPI distribution is `hanzoai-iam`; the import path is `hanzo_iam`. - -## Installation - -```bash -pip install hanzoai-iam -``` - -With FastAPI integration: - -```bash -pip install hanzoai-iam[fastapi] -``` - -With KMS support for certificate management: - -```bash -pip install hanzoai-iam[kms] -``` - -## Quick Start - -```python -from hanzo_iam import IAMClient, IAMConfig - -config = IAMConfig( - endpoint="https://iam.hanzo.ai", - client_id="your-client-id", - client_secret="your-client-secret", - org_name="HANZO", -) - -client = IAMClient(config) - -# Get authorization URL for user login -auth_url = client.get_auth_url(redirect_uri="https://yourapp.com/callback") - -# Exchange code for tokens -tokens = client.get_token(code="auth-code-from-callback") - -# Get user info -user = client.get_user_info(access_token=tokens.access_token) -``` - -## Organizations - -| Organization | Endpoint | Description | -|-------------|----------|-------------| -| HANZO | https://iam.hanzo.ai | Hanzo AI platform | -| ZOO | https://iam.zoo.dev | Zoo Labs Foundation | -| LUX | https://iam.lux.network | Lux blockchain network | -| PARS | https://iam.pars.dev | Pars development platform | - -## Environment Variables - -One canonical prefix โ€” `IAM_*`. No upstream-brand aliases, no per-org variants. - -```bash -# Required -IAM_ENDPOINT=https://hanzo.id -IAM_CLIENT_ID=your-client-id -IAM_CLIENT_SECRET=your-client-secret - -# Optional (defaults shown) -IAM_ORG=hanzo -IAM_APP=app -IAM_CERT=path/to/cert.pem # PEM file or inline PEM content -``` - -## FastAPI Integration - -```python -from fastapi import FastAPI, Depends -from hanzo_iam.fastapi import IAMAuth, get_current_user -from hanzo_iam import IAMConfig, User - -app = FastAPI() - -config = IAMConfig.from_env() -auth = IAMAuth(config) - -@app.get("/protected") -async def protected_route(user: User = Depends(auth.require_user)): - return {"user": user.name, "org": user.owner} - -@app.get("/optional") -async def optional_auth(user: User | None = Depends(auth.optional_user)): - if user: - return {"message": f"Hello, {user.name}"} - return {"message": "Hello, anonymous"} -``` - -## Client Credentials Flow - -For service-to-service authentication: - -```python -from hanzo_iam import IAMClient, IAMConfig - -config = IAMConfig( - endpoint="https://iam.hanzo.ai", - client_id="service-client-id", - client_secret="service-client-secret", - org_name="HANZO", -) - -client = IAMClient(config) - -# Get service token -tokens = client.get_client_credentials_token() - -# Use token for API calls -headers = {"Authorization": f"Bearer {tokens.access_token}"} -``` - -## Async Client - -```python -import asyncio -from hanzo_iam import AsyncIAMClient, IAMConfig - -async def main(): - config = IAMConfig.from_env() - client = AsyncIAMClient(config) - - # All methods are async - tokens = await client.get_client_credentials_token() - user = await client.get_user_info(tokens.access_token) - - print(f"Authenticated as: {user.name}") - -asyncio.run(main()) -``` - -## API Reference - -| Method | Description | -|--------|-------------| -| `get_auth_url(redirect_uri, state, scope)` | Generate OAuth authorization URL | -| `get_token(code)` | Exchange authorization code for tokens | -| `refresh_token(refresh_token)` | Refresh access token | -| `get_client_credentials_token()` | Get token via client credentials flow | -| `get_user_info(access_token)` | Get user info from access token | -| `parse_jwt(token)` | Parse and validate JWT claims | -| `get_user(user_id)` | Get user by ID | -| `get_users()` | List all users in organization | -| `create_user(user)` | Create new user | -| `update_user(user)` | Update existing user | -| `delete_user(user_id)` | Delete user | -| `get_organizations()` | List organizations | -| `get_organization(name)` | Get organization by name | - -## License - -Apache-2.0 diff --git a/pkg/hanzo-iam/hanzo_iam/__init__.py b/pkg/hanzo-iam/hanzo_iam/__init__.py deleted file mode 100644 index 4abcaa5d1..000000000 --- a/pkg/hanzo-iam/hanzo_iam/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Hanzo IAM - Identity and Access Management SDK for Hanzo ecosystem.""" - -from hanzo_iam.async_client import AsyncCasdoorSDK, AsyncIAMClient -from hanzo_iam.client import CasdoorSDK, IAMClient -from hanzo_iam.models import ( - Application, - IAMConfig, - JWTClaims, - Organization, - TokenResponse, - User, - UserInfo, -) - -__version__ = "1.1.1" - -__all__ = [ - # Clients - "IAMClient", - "AsyncIAMClient", - # Config - "IAMConfig", - # Models - "Application", - "JWTClaims", - "Organization", - "TokenResponse", - "User", - "UserInfo", - # Aliases for Casdoor compatibility - "CasdoorSDK", - "AsyncCasdoorSDK", -] diff --git a/pkg/hanzo-iam/hanzo_iam/async_client.py b/pkg/hanzo-iam/hanzo_iam/async_client.py deleted file mode 100644 index bfe235aa2..000000000 --- a/pkg/hanzo-iam/hanzo_iam/async_client.py +++ /dev/null @@ -1,1046 +0,0 @@ -"""Asynchronous IAM client for Hanzo IAM (Casdoor-based).""" - -from __future__ import annotations - -import secrets -from typing import TYPE_CHECKING, Any -from urllib.parse import urlencode - -import httpx -import jwt - -from hanzo_iam.models import ( - Application, - JWTClaims, - Organization, - TokenResponse, - User, - UserInfo, -) - -if TYPE_CHECKING: - from jwt import PyJWKClient - - from hanzo_iam.config import IAMConfig - - -class AsyncIAMClient: - """Asynchronous OAuth2/OIDC client for Hanzo IAM. - - Same interface as IAMClient but all I/O methods are async. - - Supports: - - Authorization code flow - - Client credentials flow (M2M) - - Token validation via JWKS - - Token introspection - - User management - - Example: - async with AsyncIAMClient( - client_id="my-app", - client_secret="secret", - org=Organization.HANZO, - ) as client: - # Get tokens - tokens = await client.exchange_code(code, redirect_uri) - - # Get user info - user = await client.get_user_info(tokens.access_token) - """ - - def __init__( - self, - client_id: str | None = None, - client_secret: str | None = None, - org: Organization = Organization.HANZO, - config: IAMConfig | None = None, - bearer_token: str | None = None, - ): - """Initialize async IAM client. - - Args: - client_id: OAuth2 client ID (or from env) - client_secret: OAuth2 client secret (or from env) - org: Organization enum (determines IAM URL) - config: Full configuration (overrides other args) - bearer_token: Bearer token for admin API auth (alternative to client_id/secret) - """ - # Avoid circular import - from hanzo_iam.client import IAMClient - from hanzo_iam.models import IAMConfig as ModelConfig - - if config: - self._config = config - else: - env_config = IAMClient._config_from_env(org) - self._config = ModelConfig( - server_url=env_config.server_url, - client_id=client_id or env_config.client_id, - client_secret=client_secret or env_config.client_secret, - organization=env_config.organization, - application=env_config.application, - certificate=env_config.certificate, - ) - - self._bearer_token = bearer_token - self._http: httpx.AsyncClient | None = None - self._jwks_client: PyJWKClient | None = None - self._openid_config: dict[str, Any] | None = None - - @property - def config(self) -> IAMConfig: - """Get client configuration.""" - return self._config # type: ignore[return-value] - - async def _get_http(self) -> httpx.AsyncClient: - """Get or create async HTTP client.""" - if self._http is None: - self._http = httpx.AsyncClient( - base_url=self._config.server_url.rstrip("/"), - timeout=30.0, - headers={ - "User-Agent": "hanzo-iam-python/1.0", - "Content-Type": "application/json", - }, - ) - return self._http - - # ========================================================================= - # Admin Auth Helpers - # ========================================================================= - - def _admin_params(self) -> dict[str, str]: - """Return query params for admin API auth (empty if using bearer token).""" - if self._bearer_token: - return {} - return { - "clientId": self._config.client_id, - "clientSecret": self._config.client_secret, - } - - def _admin_headers(self) -> dict[str, str]: - """Return extra headers for admin API auth (Authorization if bearer token).""" - if self._bearer_token: - return {"Authorization": f"Bearer {self._bearer_token}"} - return {} - - # ========================================================================= - # OIDC Discovery - # ========================================================================= - - async def get_openid_configuration(self) -> dict[str, Any]: - """Get OpenID Connect discovery document. - - Returns: - OIDC configuration with endpoints, supported features, etc. - """ - if self._openid_config is None: - http = await self._get_http() - response = await http.get("/.well-known/openid-configuration") - response.raise_for_status() - self._openid_config = response.json() - return self._openid_config - - async def get_jwks(self) -> dict[str, Any]: - """Get JSON Web Key Set for token verification. - - Returns: - JWKS with public keys for JWT verification. - """ - http = await self._get_http() - response = await http.get("/.well-known/jwks") - response.raise_for_status() - return response.json() - - # ========================================================================= - # Authorization Code Flow - # ========================================================================= - - def get_authorization_url( - self, - redirect_uri: str, - state: str | None = None, - scope: str = "openid profile email", - response_type: str = "code", - nonce: str | None = None, - code_challenge: str | None = None, - code_challenge_method: str | None = None, - ) -> str: - """Build authorization URL for OAuth2 code flow. - - This method is synchronous since it only builds a URL. - - Args: - redirect_uri: Callback URL after authorization - state: CSRF protection state (generated if not provided) - scope: OAuth2 scopes (default: openid profile email) - response_type: OAuth2 response type (default: code) - nonce: OIDC nonce for ID token validation - code_challenge: PKCE code challenge - code_challenge_method: PKCE method (S256 or plain) - - Returns: - Authorization URL to redirect user to. - """ - if state is None: - state = secrets.token_urlsafe(32) - - params = { - "client_id": self._config.client_id, - "redirect_uri": redirect_uri, - "response_type": response_type, - "scope": scope, - "state": state, - } - - if nonce: - params["nonce"] = nonce - if code_challenge: - params["code_challenge"] = code_challenge - params["code_challenge_method"] = code_challenge_method or "S256" - - base_url = self._config.server_url.rstrip("/") - return f"{base_url}/oauth/authorize?{urlencode(params)}" - - async def exchange_code( - self, - code: str, - redirect_uri: str, - code_verifier: str | None = None, - ) -> TokenResponse: - """Exchange authorization code for tokens. - - Args: - code: Authorization code from callback - redirect_uri: Same redirect_uri used in authorization - code_verifier: PKCE code verifier (if using PKCE) - - Returns: - TokenResponse with access_token, refresh_token, id_token, etc. - """ - data = { - "grant_type": "authorization_code", - "client_id": self._config.client_id, - "client_secret": self._config.client_secret, - "code": code, - "redirect_uri": redirect_uri, - } - - if code_verifier: - data["code_verifier"] = code_verifier - - http = await self._get_http() - response = await http.post( - "/oauth/token", - data=data, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - ) - response.raise_for_status() - return TokenResponse.model_validate(response.json()) - - async def refresh_token(self, refresh_token: str) -> TokenResponse: - """Refresh access token using refresh token. - - Args: - refresh_token: Refresh token from previous token response - - Returns: - New TokenResponse with fresh tokens. - """ - data = { - "grant_type": "refresh_token", - "client_id": self._config.client_id, - "client_secret": self._config.client_secret, - "refresh_token": refresh_token, - } - - http = await self._get_http() - response = await http.post( - "/oauth/token", - data=data, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - ) - response.raise_for_status() - return TokenResponse.model_validate(response.json()) - - # ========================================================================= - # Client Credentials Flow (M2M) - # ========================================================================= - - async def client_credentials(self, scope: str = "openid") -> TokenResponse: - """Get access token using client credentials (machine-to-machine). - - Args: - scope: Requested scopes - - Returns: - TokenResponse with access_token. - """ - data = { - "grant_type": "client_credentials", - "client_id": self._config.client_id, - "client_secret": self._config.client_secret, - "scope": scope, - } - - http = await self._get_http() - response = await http.post( - "/oauth/token", - data=data, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - ) - response.raise_for_status() - return TokenResponse.model_validate(response.json()) - - # ========================================================================= - # Token Validation - # ========================================================================= - - def validate_token( - self, - token: str, - verify_exp: bool = True, - verify_aud: bool = True, - ) -> JWTClaims: - """Validate JWT token using JWKS. - - Note: JWT validation is CPU-bound, so this remains synchronous. - The JWKS client handles caching internally. - - Args: - token: JWT access token or ID token - verify_exp: Verify expiration (default: True) - verify_aud: Verify audience matches client_id (default: True) - - Returns: - JWTClaims with decoded token claims. - - Raises: - jwt.InvalidTokenError: If token is invalid or expired. - """ - if self._jwks_client is None: - jwks_url = f"{self._config.server_url.rstrip('/')}/.well-known/jwks" - self._jwks_client = jwt.PyJWKClient(jwks_url) - - signing_key = self._jwks_client.get_signing_key_from_jwt(token) - - options = { - "verify_exp": verify_exp, - "verify_aud": verify_aud, - } - - audience = self._config.client_id if verify_aud else None - - claims = jwt.decode( - token, - signing_key.key, - algorithms=["RS256", "ES256"], - audience=audience, - options=options, - ) - - return JWTClaims.model_validate(claims) - - def validate_token_with_cert( - self, - token: str, - verify_exp: bool = True, - ) -> JWTClaims: - """Validate JWT token using configured certificate. - - Use this when you have the public certificate configured. - - Args: - token: JWT access token or ID token - verify_exp: Verify expiration (default: True) - - Returns: - JWTClaims with decoded token claims. - """ - if not self._config.certificate: - raise ValueError( - "Certificate not configured. Use validate_token() with JWKS instead." - ) - - options = {"verify_exp": verify_exp} - - claims = jwt.decode( - token, - self._config.certificate, - algorithms=["RS256"], - options=options, - ) - - return JWTClaims.model_validate(claims) - - async def introspect_token(self, token: str) -> dict[str, Any]: - """Introspect token at IAM server. - - Use this for opaque tokens or when you need authoritative validation. - - Args: - token: Token to introspect - - Returns: - Token metadata including active status, scopes, etc. - """ - data = { - "token": token, - "client_id": self._config.client_id, - "client_secret": self._config.client_secret, - } - - http = await self._get_http() - response = await http.post( - "/oauth/introspect", - data=data, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - ) - response.raise_for_status() - return response.json() - - # ========================================================================= - # User Info - # ========================================================================= - - async def get_user_info(self, access_token: str) -> UserInfo: - """Get user info from OIDC userinfo endpoint. - - Args: - access_token: Valid access token - - Returns: - UserInfo with user profile data. - """ - http = await self._get_http() - response = await http.get( - "/api/userinfo", - headers={"Authorization": f"Bearer {access_token}"}, - ) - response.raise_for_status() - return UserInfo.model_validate(response.json()) - - # ========================================================================= - # User Management (Casdoor Admin API) - # ========================================================================= - - async def get_user(self, user_id: str) -> User: - """Get user by ID. - - Args: - user_id: User ID or username - - Returns: - User object with full profile. - """ - params = { - "id": f"{self._config.organization}/{user_id}", - **self._admin_params(), - } - - http = await self._get_http() - response = await http.get( - "/api/get-user", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get user")) - - return User.model_validate(data.get("data", data)) - - async def get_users(self) -> list[User]: - """Get all users in organization. - - Returns: - List of User objects. - """ - params = { - "owner": self._config.organization, - **self._admin_params(), - } - - http = await self._get_http() - response = await http.get( - "/api/get-users", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get users")) - - users_data = data.get("data", data) or [] - return [User.model_validate(u) for u in users_data] - - async def get_user_count( - self, - *, - owner: str | None = None, - is_online: bool | None = None, - ) -> int: - """Get user count in organization. - - Args: - owner: Organization name (defaults to config.organization) - is_online: Filter by online status - - Returns: - Number of users. - """ - params: dict[str, Any] = { - "owner": owner or self._config.organization, - **self._admin_params(), - } - if is_online is not None: - params["isOnline"] = str(is_online).lower() - - http = await self._get_http() - response = await http.get( - "/api/get-user-count", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if isinstance(data, dict) and data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get user count")) - - return int(data.get("data", data) if isinstance(data, dict) else data) - - async def create_user(self, user: User) -> User: - """Create new user. - - Args: - user: User object to create - - Returns: - Created User object. - """ - return await self._modify_user("add-user", user) - - async def update_user(self, user: User) -> User: - """Update existing user. - - Args: - user: User object with updated fields - - Returns: - Updated User object. - """ - return await self._modify_user("update-user", user) - - async def delete_user(self, user: User) -> User: - """Delete user. - - Args: - user: User object to delete - - Returns: - Deleted User object. - """ - return await self._modify_user("delete-user", user) - - async def _modify_user(self, action: str, user: User) -> User: - """Modify user via API.""" - http = await self._get_http() - response = await http.post( - f"/api/{action}", - params=self._admin_params(), - headers=self._admin_headers(), - json=user.model_dump(by_alias=True, exclude_none=True), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", f"Failed to {action}")) - - return User.model_validate(data.get("data", data)) - - async def get_application(self) -> Application: - """Get current application configuration. - - Returns: - Application configuration including OAuth2 settings. - """ - params = { - "id": f"{self._config.organization}/{self._config.application}", - **self._admin_params(), - } - - http = await self._get_http() - response = await http.get( - "/api/get-application", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get application")) - - return Application.model_validate(data.get("data", data)) - - # ========================================================================= - # Organization - # ========================================================================= - - async def get_organizations(self) -> list[dict[str, Any]]: - """Get all organizations. - - Returns: - List of organization data. - """ - params = { - "owner": "admin", - **self._admin_params(), - } - - http = await self._get_http() - response = await http.get( - "/api/get-organizations", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get organizations")) - - return data.get("data", data) or [] - - async def get_organization(self, name: str) -> dict[str, Any]: - """Get organization by name. - - Args: - name: Organization name - - Returns: - Organization data. - """ - params = { - "id": f"admin/{name}", - **self._admin_params(), - } - - http = await self._get_http() - response = await http.get( - "/api/get-organization", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get organization")) - - return data.get("data", data) - - # ========================================================================= - # Permissions / Enforcement - # ========================================================================= - - async def enforce( - self, - permission_id: str, - model_id: str, - resource_id: str, - enforce_id: str, - *, - owner: str | None = None, - request: list[str] | None = None, - ) -> bool: - """Check permission using Casbin. - - Args: - permission_id: Permission identifier - model_id: Casbin model identifier - resource_id: Resource identifier - enforce_id: Enforcement identifier - owner: Organization (defaults to config.organization) - request: Casbin request parameters [sub, obj, act, ...] - - Returns: - True if permitted, False otherwise. - """ - payload: dict[str, Any] = { - "id": permission_id, - "modelId": model_id, - "resourceId": resource_id, - "enforceId": enforce_id, - "owner": owner or self._config.organization, - **self._admin_params(), - } - if request: - payload["casbinRequest"] = request - - http = await self._get_http() - response = await http.post( - "/api/enforce", - json=payload, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if isinstance(data, dict) and data.get("status") == "error": - raise ValueError(data.get("msg", "Enforcement failed")) - - return bool(data.get("data", data) if isinstance(data, dict) else data) - - async def batch_enforce( - self, - permission_id: str, - model_id: str, - enforce_id: str, - *, - owner: str | None = None, - requests: list[list[str]] | None = None, - ) -> list[bool]: - """Batch check permissions using Casbin. - - Args: - permission_id: Permission identifier - model_id: Casbin model identifier - enforce_id: Enforcement identifier - owner: Organization (defaults to config.organization) - requests: List of Casbin requests [[sub, obj, act], ...] - - Returns: - List of permission results. - """ - payload: dict[str, Any] = { - "id": permission_id, - "modelId": model_id, - "enforceId": enforce_id, - "owner": owner or self._config.organization, - **self._admin_params(), - } - if requests: - payload["casbinRequest"] = requests - - http = await self._get_http() - response = await http.post( - "/api/batch-enforce", - json=payload, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if isinstance(data, dict) and data.get("status") == "error": - raise ValueError(data.get("msg", "Batch enforcement failed")) - - results = data.get("data", data) if isinstance(data, dict) else data - return [bool(r) for r in results] - - # ========================================================================= - # Roles - # ========================================================================= - - async def get_roles(self, *, owner: str | None = None) -> list[dict[str, Any]]: - """Get all roles in organization. - - Args: - owner: Organization name (defaults to config.organization) - - Returns: - List of role data. - """ - params = { - "owner": owner or self._config.organization, - **self._admin_params(), - } - - http = await self._get_http() - response = await http.get( - "/api/get-roles", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get roles")) - - return data.get("data", data) or [] - - async def get_role( - self, - role_name: str, - *, - owner: str | None = None, - ) -> dict[str, Any]: - """Get role by name. - - Args: - role_name: Role name - owner: Organization name (defaults to config.organization) - - Returns: - Role data. - """ - org = owner or self._config.organization - params = { - "id": f"{org}/{role_name}", - **self._admin_params(), - } - - http = await self._get_http() - response = await http.get( - "/api/get-role", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get role")) - - return data.get("data", data) - - async def get_user_roles( - self, - username: str, - *, - owner: str | None = None, - ) -> list[dict[str, Any]]: - """Get roles for user. - - Args: - username: Username - owner: Organization name (defaults to config.organization) - - Returns: - List of role data. - """ - org = owner or self._config.organization - params = { - "id": f"{org}/{username}", - **self._admin_params(), - } - - http = await self._get_http() - response = await http.get( - "/api/get-user-roles", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get user roles")) - - return data.get("data", data) or [] - - async def add_role_for_user( - self, - username: str, - role_name: str, - *, - owner: str | None = None, - ) -> bool: - """Add role to user. - - Args: - username: Username - role_name: Role name to add - owner: Organization name (defaults to config.organization) - - Returns: - True if successful. - """ - org = owner or self._config.organization - payload = { - "user": f"{org}/{username}", - "role": f"{org}/{role_name}", - **self._admin_params(), - } - - http = await self._get_http() - response = await http.post( - "/api/add-user-role", - json=payload, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to add role for user")) - - return True - - async def remove_role_from_user( - self, - username: str, - role_name: str, - *, - owner: str | None = None, - ) -> bool: - """Remove role from user. - - Args: - username: Username - role_name: Role name to remove - owner: Organization name (defaults to config.organization) - - Returns: - True if successful. - """ - org = owner or self._config.organization - payload = { - "user": f"{org}/{username}", - "role": f"{org}/{role_name}", - **self._admin_params(), - } - - http = await self._get_http() - response = await http.post( - "/api/delete-user-role", - json=payload, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to remove role from user")) - - return True - - # ========================================================================= - # Password Management - # ========================================================================= - - async def set_password( - self, - user_owner: str, - user_name: str, - new_password: str, - old_password: str = "", - ) -> dict[str, Any]: - """Set or reset a user's password. - - Args: - user_owner: Organization that owns the user. - user_name: Username. - new_password: New password to set. - old_password: Current password (empty for admin reset). - - Returns: - API response data. - """ - payload = { - "userOwner": user_owner, - "userName": user_name, - "oldPassword": old_password, - "newPassword": new_password, - } - - http = await self._get_http() - response = await http.post( - "/api/set-password", - params=self._admin_params(), - headers=self._admin_headers(), - json=payload, - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to set password")) - - return data - - # ========================================================================= - # Application Management - # ========================================================================= - - async def get_applications(self, owner: str = "admin") -> list[Application]: - """Get all applications. - - Args: - owner: Owner of applications (default: admin). - - Returns: - List of Application objects. - """ - params = { - "owner": owner, - **self._admin_params(), - } - - http = await self._get_http() - response = await http.get( - "/api/get-applications", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get applications")) - - apps_data = data.get("data", data) or [] - return [Application.model_validate(a) for a in apps_data] - - async def update_application(self, application: Application) -> dict[str, Any]: - """Update an application. - - Args: - application: Application object with updated fields. - - Returns: - API response data. - """ - http = await self._get_http() - response = await http.post( - "/api/update-application", - params=self._admin_params(), - headers=self._admin_headers(), - json=application.model_dump(by_alias=True, exclude_none=True), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to update application")) - - return data - - # ========================================================================= - # Lifecycle - # ========================================================================= - - async def close(self) -> None: - """Close HTTP client and release resources.""" - if self._http: - await self._http.aclose() - self._http = None - self._jwks_client = None - - async def __aenter__(self) -> AsyncIAMClient: - return self - - async def __aexit__(self, *args: Any) -> None: - await self.close() - - -# Alias for Casdoor SDK compatibility -AsyncCasdoorSDK = AsyncIAMClient diff --git a/pkg/hanzo-iam/hanzo_iam/client.py b/pkg/hanzo-iam/hanzo_iam/client.py deleted file mode 100644 index 060c09a68..000000000 --- a/pkg/hanzo-iam/hanzo_iam/client.py +++ /dev/null @@ -1,1136 +0,0 @@ -""" -Hanzo IAM Client - Sync OAuth2/OIDC client for Hanzo Identity. - -Compatible with Casdoor API. Supports multiple organizations. -""" - -from __future__ import annotations - -import os -import secrets -from typing import TYPE_CHECKING -from urllib.parse import urlencode - -import httpx -import jwt - -from .models import ( - Application, - IAMConfig, - JWTClaims, - Organization, - TokenResponse, - User, - UserInfo, -) - -if TYPE_CHECKING: - from jwt import PyJWKClient - - -class IAMClient: - """ - Sync OAuth2/OIDC client for Hanzo IAM. - - Supports: - - Authorization code flow - - Client credentials flow (M2M) - - Token validation via JWKS - - Token introspection - - User management - - Example: - client = IAMClient( - client_id="my-app", - client_secret="secret", - org=Organization.HANZO, - ) - - # Get authorization URL - url = client.get_authorization_url( - redirect_uri="https://myapp.com/callback", - state="random-state", - ) - - # Exchange code for tokens - tokens = client.exchange_code(code, redirect_uri) - - # Validate token - claims = client.validate_token(tokens.access_token) - """ - - def __init__( - self, - client_id: str | None = None, - client_secret: str | None = None, - org: Organization = Organization.HANZO, - config: IAMConfig | None = None, - bearer_token: str | None = None, - ): - """Initialize IAM client. - - Args: - client_id: OAuth2 client ID (or from env) - client_secret: OAuth2 client secret (or from env) - org: Organization enum (determines IAM URL) - config: Full configuration (overrides other args) - bearer_token: Bearer token for admin API auth (alternative to client_id/secret) - """ - if config: - self._config = config - else: - env_config = self._config_from_env(org) - self._config = IAMConfig( - server_url=env_config.server_url, - client_id=client_id or env_config.client_id, - client_secret=client_secret or env_config.client_secret, - organization=env_config.organization, - application=env_config.application, - certificate=env_config.certificate, - ) - - self._bearer_token = bearer_token - self._http: httpx.Client | None = None - self._jwks_client: PyJWKClient | None = None - self._openid_config: dict | None = None - - @staticmethod - def _config_from_env(org: Organization = Organization.HANZO) -> IAMConfig: - """Read configuration from environment variables. - - The canonical contract is ``IAM_*`` only โ€” no upstream-brand - aliases, no per-org fallbacks (see ~/work/hanzo/iam/CLAUDE.md - "Configuration"). The ``org`` argument seeds the default server - URL when ``IAM_ENDPOINT`` is unset and the default organization - name when ``IAM_ORG`` is unset; it does not influence which env - vars are read. - - Environment variables: - IAM_ENDPOINT - IAM server URL (defaults to ``org.iam_url``) - IAM_CLIENT_ID - OAuth2 client ID - IAM_CLIENT_SECRET - OAuth2 client secret - IAM_ORG - Organization name (defaults to ``org.value``) - IAM_APP - Application name (defaults to ``app``) - IAM_CERT - JWT verification certificate - """ - return IAMConfig( - server_url=os.getenv("IAM_ENDPOINT", org.iam_url), - client_id=os.getenv("IAM_CLIENT_ID", ""), - client_secret=os.getenv("IAM_CLIENT_SECRET", ""), - organization=os.getenv("IAM_ORG", org.value), - application=os.getenv("IAM_APP", "app"), - certificate=os.getenv("IAM_CERT", ""), - ) - - @property - def http(self) -> httpx.Client: - """Get or create HTTP client.""" - if self._http is None: - self._http = httpx.Client( - base_url=self._config.server_url.rstrip("/"), - timeout=30.0, - headers={ - "User-Agent": "hanzo-iam-python/1.0", - "Content-Type": "application/json", - }, - ) - return self._http - - @property - def config(self) -> IAMConfig: - """Get client configuration.""" - return self._config - - # ========================================================================= - # OIDC Discovery - # ========================================================================= - - def get_openid_configuration(self) -> dict: - """Get OpenID Connect discovery document. - - Returns: - OIDC configuration with endpoints, supported features, etc. - """ - if self._openid_config is None: - response = self.http.get("/.well-known/openid-configuration") - response.raise_for_status() - self._openid_config = response.json() - return self._openid_config - - def get_jwks(self) -> dict: - """Get JSON Web Key Set for token verification. - - Returns: - JWKS with public keys for JWT verification. - """ - response = self.http.get("/.well-known/jwks") - response.raise_for_status() - return response.json() - - # ========================================================================= - # Authorization Code Flow - # ========================================================================= - - def get_authorization_url( - self, - redirect_uri: str, - state: str | None = None, - scope: str = "openid profile email", - response_type: str = "code", - nonce: str | None = None, - code_challenge: str | None = None, - code_challenge_method: str | None = None, - ) -> str: - """Build authorization URL for OAuth2 code flow. - - Args: - redirect_uri: Callback URL after authorization - state: CSRF protection state (generated if not provided) - scope: OAuth2 scopes (default: openid profile email) - response_type: OAuth2 response type (default: code) - nonce: OIDC nonce for ID token validation - code_challenge: PKCE code challenge - code_challenge_method: PKCE method (S256 or plain) - - Returns: - Authorization URL to redirect user to. - """ - if state is None: - state = secrets.token_urlsafe(32) - - params = { - "client_id": self._config.client_id, - "redirect_uri": redirect_uri, - "response_type": response_type, - "scope": scope, - "state": state, - } - - if nonce: - params["nonce"] = nonce - if code_challenge: - params["code_challenge"] = code_challenge - params["code_challenge_method"] = code_challenge_method or "S256" - - base_url = self._config.server_url.rstrip("/") - return f"{base_url}/oauth/authorize?{urlencode(params)}" - - def exchange_code( - self, - code: str, - redirect_uri: str, - code_verifier: str | None = None, - ) -> TokenResponse: - """Exchange authorization code for tokens. - - Args: - code: Authorization code from callback - redirect_uri: Same redirect_uri used in authorization - code_verifier: PKCE code verifier (if using PKCE) - - Returns: - TokenResponse with access_token, refresh_token, id_token, etc. - """ - data = { - "grant_type": "authorization_code", - "client_id": self._config.client_id, - "client_secret": self._config.client_secret, - "code": code, - "redirect_uri": redirect_uri, - } - - if code_verifier: - data["code_verifier"] = code_verifier - - response = self.http.post( - "/oauth/token", - data=data, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - ) - response.raise_for_status() - return TokenResponse.model_validate(response.json()) - - def refresh_token(self, refresh_token: str) -> TokenResponse: - """Refresh access token using refresh token. - - Args: - refresh_token: Refresh token from previous token response - - Returns: - New TokenResponse with fresh tokens. - """ - data = { - "grant_type": "refresh_token", - "client_id": self._config.client_id, - "client_secret": self._config.client_secret, - "refresh_token": refresh_token, - } - - response = self.http.post( - "/oauth/token", - data=data, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - ) - response.raise_for_status() - return TokenResponse.model_validate(response.json()) - - # ========================================================================= - # Client Credentials Flow (M2M) - # ========================================================================= - - def client_credentials(self, scope: str = "openid") -> TokenResponse: - """Get access token using client credentials (machine-to-machine). - - Args: - scope: Requested scopes - - Returns: - TokenResponse with access_token. - """ - data = { - "grant_type": "client_credentials", - "client_id": self._config.client_id, - "client_secret": self._config.client_secret, - "scope": scope, - } - - response = self.http.post( - "/oauth/token", - data=data, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - ) - response.raise_for_status() - return TokenResponse.model_validate(response.json()) - - # ========================================================================= - # Token Validation - # ========================================================================= - - def validate_token( - self, - token: str, - verify_exp: bool = True, - verify_aud: bool = True, - ) -> JWTClaims: - """Validate JWT token using JWKS. - - Args: - token: JWT access token or ID token - verify_exp: Verify expiration (default: True) - verify_aud: Verify audience matches client_id (default: True) - - Returns: - JWTClaims with decoded token claims. - - Raises: - jwt.InvalidTokenError: If token is invalid or expired. - """ - # Initialize JWKS client if needed - if self._jwks_client is None: - jwks_url = f"{self._config.server_url.rstrip('/')}/.well-known/jwks" - self._jwks_client = jwt.PyJWKClient(jwks_url) - - # Get signing key from JWKS - signing_key = self._jwks_client.get_signing_key_from_jwt(token) - - # Build verification options - options = { - "verify_exp": verify_exp, - "verify_aud": verify_aud, - } - - audience = self._config.client_id if verify_aud else None - - # Decode and validate - claims = jwt.decode( - token, - signing_key.key, - algorithms=["RS256", "ES256"], - audience=audience, - options=options, - ) - - return JWTClaims.model_validate(claims) - - def validate_token_with_cert( - self, - token: str, - verify_exp: bool = True, - ) -> JWTClaims: - """Validate JWT token using configured certificate. - - Use this when you have the public certificate configured. - - Args: - token: JWT access token or ID token - verify_exp: Verify expiration (default: True) - - Returns: - JWTClaims with decoded token claims. - """ - if not self._config.certificate: - raise ValueError( - "Certificate not configured. Use validate_token() with JWKS instead." - ) - - options = {"verify_exp": verify_exp} - - claims = jwt.decode( - token, - self._config.certificate, - algorithms=["RS256"], - options=options, - ) - - return JWTClaims.model_validate(claims) - - def introspect_token(self, token: str) -> dict: - """Introspect token at IAM server. - - Use this for opaque tokens or when you need authoritative validation. - - Args: - token: Token to introspect - - Returns: - Token metadata including active status, scopes, etc. - """ - data = { - "token": token, - "client_id": self._config.client_id, - "client_secret": self._config.client_secret, - } - - response = self.http.post( - "/oauth/introspect", - data=data, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - ) - response.raise_for_status() - return response.json() - - # ========================================================================= - # User Info - # ========================================================================= - - def get_user_info(self, access_token: str) -> UserInfo: - """Get user info from OIDC userinfo endpoint. - - Args: - access_token: Valid access token - - Returns: - UserInfo with user profile data. - """ - response = self.http.get( - "/api/userinfo", - headers={"Authorization": f"Bearer {access_token}"}, - ) - response.raise_for_status() - return UserInfo.model_validate(response.json()) - - # ========================================================================= - # Admin Auth Helpers - # ========================================================================= - - def _admin_params(self) -> dict: - """Return query params for admin API auth (empty if using bearer token).""" - if self._bearer_token: - return {} - return { - "clientId": self._config.client_id, - "clientSecret": self._config.client_secret, - } - - def _admin_headers(self) -> dict: - """Return extra headers for admin API auth (Authorization if bearer token).""" - if self._bearer_token: - return {"Authorization": f"Bearer {self._bearer_token}"} - return {} - - # ========================================================================= - # User Management (Casdoor Admin API) - # ========================================================================= - - def get_user(self, user_id: str) -> User: - """Get user by ID. - - Args: - user_id: User ID or username - - Returns: - User object with full profile. - """ - params = { - "id": f"{self._config.organization}/{user_id}", - **self._admin_params(), - } - - response = self.http.get( - "/api/get-user", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get user")) - - return User.model_validate(data.get("data", data)) - - def get_users(self) -> list[User]: - """Get all users in organization. - - Returns: - List of User objects. - """ - params = { - "owner": self._config.organization, - **self._admin_params(), - } - - response = self.http.get( - "/api/get-users", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get users")) - - users_data = data.get("data", data) or [] - return [User.model_validate(u) for u in users_data] - - def get_application(self) -> Application: - """Get current application configuration. - - Returns: - Application configuration including OAuth2 settings. - """ - params = { - "id": f"{self._config.organization}/{self._config.application}", - **self._admin_params(), - } - - response = self.http.get( - "/api/get-application", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get application")) - - return Application.model_validate(data.get("data", data)) - - # ========================================================================= - # User Management (Admin API) - # ========================================================================= - - def create_user(self, user: User) -> dict: - """Create a new user. - - Args: - user: User object to create. - - Returns: - API response data. - """ - return self._modify_user("add-user", user) - - def update_user(self, user: User) -> dict: - """Update an existing user. - - Args: - user: User object with updated fields. - - Returns: - API response data. - """ - return self._modify_user("update-user", user) - - def delete_user(self, user: User) -> dict: - """Delete a user. - - Args: - user: User object to delete. - - Returns: - API response data. - """ - return self._modify_user("delete-user", user) - - def _modify_user(self, action: str, user: User) -> dict: - """Modify user via Casdoor admin API.""" - response = self.http.post( - f"/api/{action}", - params=self._admin_params(), - headers=self._admin_headers(), - json=user.model_dump(by_alias=True, exclude_none=True), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", f"Failed to {action}")) - - return data - - # ========================================================================= - # Organization Management - # ========================================================================= - - def get_organizations(self) -> list[dict]: - """Get all organizations. - - Returns: - List of organization dicts. - """ - params = { - "owner": "admin", - **self._admin_params(), - } - - response = self.http.get( - "/api/get-organizations", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get organizations")) - - return data.get("data", data) or [] - - def get_organization(self, name: str) -> dict: - """Get organization by name. - - Args: - name: Organization name. - - Returns: - Organization data dict. - """ - params = { - "id": f"admin/{name}", - **self._admin_params(), - } - - response = self.http.get( - "/api/get-organization", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get organization")) - - return data.get("data", data) - - # ========================================================================= - # Provider Management - # ========================================================================= - - def get_providers(self, owner: str = "admin") -> list[dict]: - """Get all providers. - - Args: - owner: Owner of providers (default: admin). - - Returns: - List of provider dicts. - """ - params = { - "owner": owner, - **self._admin_params(), - } - - response = self.http.get( - "/api/get-providers", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get providers")) - - return data.get("data", data) or [] - - # ========================================================================= - # Role Management - # ========================================================================= - - def get_roles(self, owner: str | None = None) -> list[dict]: - """Get all roles in organization. - - Args: - owner: Organization name (defaults to config.organization). - - Returns: - List of role dicts. - """ - params = { - "owner": owner or self._config.organization, - **self._admin_params(), - } - - response = self.http.get( - "/api/get-roles", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get roles")) - - return data.get("data", data) or [] - - # ========================================================================= - # Password Management - # ========================================================================= - - def set_password( - self, - user_owner: str, - user_name: str, - new_password: str, - old_password: str = "", - ) -> dict: - """Set or reset a user's password. - - Args: - user_owner: Organization that owns the user. - user_name: Username. - new_password: New password to set. - old_password: Current password (empty for admin reset). - - Returns: - API response data. - """ - payload = { - "userOwner": user_owner, - "userName": user_name, - "oldPassword": old_password, - "newPassword": new_password, - } - - response = self.http.post( - "/api/set-password", - params=self._admin_params(), - headers=self._admin_headers(), - json=payload, - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to set password")) - - return data - - # ========================================================================= - # Application Management - # ========================================================================= - - def get_applications(self, owner: str = "admin") -> list[Application]: - """Get all applications. - - Args: - owner: Owner of applications (default: admin). - - Returns: - List of Application objects. - """ - params = { - "owner": owner, - **self._admin_params(), - } - - response = self.http.get( - "/api/get-applications", - params=params, - headers=self._admin_headers(), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get applications")) - - apps_data = data.get("data", data) or [] - return [Application.model_validate(a) for a in apps_data] - - def update_application(self, application: Application) -> dict: - """Update an application. - - Args: - application: Application object with updated fields. - - Returns: - API response data. - """ - response = self.http.post( - "/api/update-application", - params=self._admin_params(), - headers=self._admin_headers(), - json=application.model_dump(by_alias=True, exclude_none=True), - ) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to update application")) - - return data - - # ========================================================================= - # Login (Masquerade) - # ========================================================================= - - def login(self, username: str, password: str) -> dict: - """Login as a user (email/password). - - Args: - username: Email or username. - password: User password. - - Returns: - Login response with access code or token. - """ - payload = { - "type": "code", - "username": username, - "password": password, - "organization": self._config.organization, - "application": self._config.application, - } - - response = self.http.post("/api/login", json=payload) - response.raise_for_status() - data = response.json() - - if data.get("status") != "ok": - raise ValueError(data.get("msg", "Login failed")) - - return data - - # ========================================================================= - # Lifecycle - # ========================================================================= - - def close(self) -> None: - """Close HTTP client and release resources.""" - if self._http: - self._http.close() - self._http = None - self._jwks_client = None - - def __enter__(self) -> IAMClient: - return self - - def __exit__(self, *args) -> None: - self.close() - - -class AsyncIAMClient: - """ - Async OAuth2/OIDC client for Hanzo IAM. - - Same interface as IAMClient but uses async/await. - """ - - def __init__( - self, - client_id: str | None = None, - client_secret: str | None = None, - org: Organization = Organization.HANZO, - config: IAMConfig | None = None, - ): - """Initialize async IAM client.""" - if config: - self._config = config - else: - env_config = IAMClient._config_from_env(org) - self._config = IAMConfig( - server_url=env_config.server_url, - client_id=client_id or env_config.client_id, - client_secret=client_secret or env_config.client_secret, - organization=env_config.organization, - application=env_config.application, - certificate=env_config.certificate, - ) - - self._http: httpx.AsyncClient | None = None - self._jwks_client: PyJWKClient | None = None - self._openid_config: dict | None = None - - @property - def config(self) -> IAMConfig: - """Get client configuration.""" - return self._config - - async def _get_http(self) -> httpx.AsyncClient: - """Get or create async HTTP client.""" - if self._http is None: - self._http = httpx.AsyncClient( - base_url=self._config.server_url.rstrip("/"), - timeout=30.0, - headers={ - "User-Agent": "hanzo-iam-python/1.0", - "Content-Type": "application/json", - }, - ) - return self._http - - async def get_openid_configuration(self) -> dict: - """Get OpenID Connect discovery document.""" - if self._openid_config is None: - http = await self._get_http() - response = await http.get("/.well-known/openid-configuration") - response.raise_for_status() - self._openid_config = response.json() - return self._openid_config - - async def get_jwks(self) -> dict: - """Get JSON Web Key Set.""" - http = await self._get_http() - response = await http.get("/.well-known/jwks") - response.raise_for_status() - return response.json() - - def get_authorization_url( - self, - redirect_uri: str, - state: str | None = None, - scope: str = "openid profile email", - response_type: str = "code", - nonce: str | None = None, - code_challenge: str | None = None, - code_challenge_method: str | None = None, - ) -> str: - """Build authorization URL.""" - if state is None: - state = secrets.token_urlsafe(32) - - params = { - "client_id": self._config.client_id, - "redirect_uri": redirect_uri, - "response_type": response_type, - "scope": scope, - "state": state, - } - - if nonce: - params["nonce"] = nonce - if code_challenge: - params["code_challenge"] = code_challenge - params["code_challenge_method"] = code_challenge_method or "S256" - - base_url = self._config.server_url.rstrip("/") - return f"{base_url}/oauth/authorize?{urlencode(params)}" - - async def exchange_code( - self, - code: str, - redirect_uri: str, - code_verifier: str | None = None, - ) -> TokenResponse: - """Exchange authorization code for tokens.""" - data = { - "grant_type": "authorization_code", - "client_id": self._config.client_id, - "client_secret": self._config.client_secret, - "code": code, - "redirect_uri": redirect_uri, - } - - if code_verifier: - data["code_verifier"] = code_verifier - - http = await self._get_http() - response = await http.post( - "/oauth/token", - data=data, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - ) - response.raise_for_status() - return TokenResponse.model_validate(response.json()) - - async def refresh_token(self, refresh_token: str) -> TokenResponse: - """Refresh access token.""" - data = { - "grant_type": "refresh_token", - "client_id": self._config.client_id, - "client_secret": self._config.client_secret, - "refresh_token": refresh_token, - } - - http = await self._get_http() - response = await http.post( - "/oauth/token", - data=data, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - ) - response.raise_for_status() - return TokenResponse.model_validate(response.json()) - - async def client_credentials(self, scope: str = "openid") -> TokenResponse: - """Get access token using client credentials.""" - data = { - "grant_type": "client_credentials", - "client_id": self._config.client_id, - "client_secret": self._config.client_secret, - "scope": scope, - } - - http = await self._get_http() - response = await http.post( - "/oauth/token", - data=data, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - ) - response.raise_for_status() - return TokenResponse.model_validate(response.json()) - - def validate_token( - self, - token: str, - verify_exp: bool = True, - verify_aud: bool = True, - ) -> JWTClaims: - """Validate JWT token using JWKS (sync operation).""" - if self._jwks_client is None: - jwks_url = f"{self._config.server_url.rstrip('/')}/.well-known/jwks" - self._jwks_client = jwt.PyJWKClient(jwks_url) - - signing_key = self._jwks_client.get_signing_key_from_jwt(token) - - options = { - "verify_exp": verify_exp, - "verify_aud": verify_aud, - } - - audience = self._config.client_id if verify_aud else None - - claims = jwt.decode( - token, - signing_key.key, - algorithms=["RS256", "ES256"], - audience=audience, - options=options, - ) - - return JWTClaims.model_validate(claims) - - async def introspect_token(self, token: str) -> dict: - """Introspect token at IAM server.""" - data = { - "token": token, - "client_id": self._config.client_id, - "client_secret": self._config.client_secret, - } - - http = await self._get_http() - response = await http.post( - "/oauth/introspect", - data=data, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - ) - response.raise_for_status() - return response.json() - - async def get_user_info(self, access_token: str) -> UserInfo: - """Get user info from userinfo endpoint.""" - http = await self._get_http() - response = await http.get( - "/api/userinfo", - headers={"Authorization": f"Bearer {access_token}"}, - ) - response.raise_for_status() - return UserInfo.model_validate(response.json()) - - async def get_user(self, user_id: str) -> User: - """Get user by ID.""" - params = { - "id": f"{self._config.organization}/{user_id}", - "clientId": self._config.client_id, - "clientSecret": self._config.client_secret, - } - - http = await self._get_http() - response = await http.get("/api/get-user", params=params) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get user")) - - return User.model_validate(data.get("data", data)) - - async def get_users(self) -> list[User]: - """Get all users in organization.""" - params = { - "owner": self._config.organization, - "clientId": self._config.client_id, - "clientSecret": self._config.client_secret, - } - - http = await self._get_http() - response = await http.get("/api/get-users", params=params) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get users")) - - users_data = data.get("data", data) or [] - return [User.model_validate(u) for u in users_data] - - async def get_application(self) -> Application: - """Get current application configuration.""" - params = { - "id": f"{self._config.organization}/{self._config.application}", - "clientId": self._config.client_id, - "clientSecret": self._config.client_secret, - } - - http = await self._get_http() - response = await http.get("/api/get-application", params=params) - response.raise_for_status() - data = response.json() - - if data.get("status") == "error": - raise ValueError(data.get("msg", "Failed to get application")) - - return Application.model_validate(data.get("data", data)) - - async def close(self) -> None: - """Close HTTP client.""" - if self._http: - await self._http.aclose() - self._http = None - self._jwks_client = None - - async def __aenter__(self) -> AsyncIAMClient: - return self - - async def __aexit__(self, *args) -> None: - await self.close() - - -# Aliases for Casdoor SDK compatibility -CasdoorSDK = IAMClient -AsyncCasdoorSDK = AsyncIAMClient diff --git a/pkg/hanzo-iam/hanzo_iam/config.py b/pkg/hanzo-iam/hanzo_iam/config.py deleted file mode 100644 index b76727e6e..000000000 --- a/pkg/hanzo-iam/hanzo_iam/config.py +++ /dev/null @@ -1,94 +0,0 @@ -"""Configuration for Hanzo IAM client.""" - -from __future__ import annotations - -import os -from typing import ClassVar - -from pydantic import BaseModel, ConfigDict, Field - - -class IAMConfig(BaseModel): - """Configuration for Hanzo IAM client. - - Can be initialized directly or from environment variables via from_env(). - """ - - model_config = ConfigDict(frozen=True) - - # Canonical environment variable prefix. There is exactly one prefix โ€” - # no upstream-brand aliases, no per-org fallbacks. See - # ~/work/hanzo/iam/CLAUDE.md "Configuration" section. - ENV_PREFIX: ClassVar[str] = "IAM_" - - server_url: str = Field(description="IAM server URL (e.g., https://hanzo.id)") - client_id: str = Field(description="OAuth2 client ID") - client_secret: str = Field(default="", description="OAuth2 client secret") - organization: str = Field(default="hanzo", description="IAM organization name") - application: str = Field(default="app", description="IAM application name") - certificate: str = Field( - default="", description="JWT verification certificate (PEM)" - ) - - @classmethod - def from_env(cls, prefix: str | None = None) -> IAMConfig: - """Create config from environment variables. - - The canonical prefix is ``IAM_`` (no upstream-brand aliases, no - per-org variants). The ``prefix`` argument exists for advanced - callers that scope multiple IAM clients in a single process; new - code should leave it at the default. - - Environment variables (with the default prefix): - IAM_ENDPOINT - IAM server URL (preferred) - IAM_CLIENT_ID - OAuth2 client ID - IAM_CLIENT_SECRET - OAuth2 client secret - IAM_ORG - Organization name - IAM_APP - Application name - IAM_CERT - JWT verification certificate (PEM content or file path) - """ - p = prefix or cls.ENV_PREFIX - - server_url = os.environ.get(f"{p}ENDPOINT", "") - client_id = os.environ.get(f"{p}CLIENT_ID", "") - client_secret = os.environ.get(f"{p}CLIENT_SECRET", "") - organization = os.environ.get(f"{p}ORG", "hanzo") - application = os.environ.get(f"{p}APP", "app") - - # Certificate can be content or file path - cert_val = os.environ.get(f"{p}CERT", "") - if cert_val and not cert_val.startswith("-----BEGIN"): - # Treat as file path - cert_path = os.path.expanduser(cert_val) - if os.path.isfile(cert_path): - with open(cert_path) as f: - cert_val = f.read() - - return cls( - server_url=server_url, - client_id=client_id, - client_secret=client_secret, - organization=organization, - application=application, - certificate=cert_val, - ) - - @property - def token_endpoint(self) -> str: - """OAuth2 token endpoint URL.""" - return f"{self.server_url}/oauth/token" - - @property - def authorize_endpoint(self) -> str: - """OAuth2 authorization endpoint URL.""" - return f"{self.server_url}/oauth/authorize" - - @property - def userinfo_endpoint(self) -> str: - """OIDC UserInfo endpoint URL.""" - return f"{self.server_url}/api/userinfo" - - @property - def api_base(self) -> str: - """Base URL for API calls.""" - return f"{self.server_url}/api" diff --git a/pkg/hanzo-iam/hanzo_iam/fastapi.py b/pkg/hanzo-iam/hanzo_iam/fastapi.py deleted file mode 100644 index d48f4ff56..000000000 --- a/pkg/hanzo-iam/hanzo_iam/fastapi.py +++ /dev/null @@ -1,376 +0,0 @@ -"""FastAPI integration for Hanzo IAM. - -Provides FastAPI dependencies for authentication and authorization -using Hanzo IAM (hanzo.id, zoo.id, lux.id, pars.id). - -Usage: - from hanzo_iam.fastapi import configure, require_auth, get_current_user - - # Configure at startup - configure( - client_id="your-client-id", - client_secret="your-client-secret", - org="hanzo", - ) - - # Use in routes - @app.get("/protected") - async def protected(claims: JWTClaims = Depends(require_auth)): - return {"user": claims.sub} - - @app.get("/user") - async def user_info(user: UserInfo = Depends(get_current_user)): - return {"email": user.email} -""" - -from __future__ import annotations - -import os -from typing import Callable - -import httpx -import jwt -from fastapi import Depends, HTTPException, status -from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer -from jwt import PyJWKClient - -from hanzo_iam.models import IAMConfig, JWTClaims, Organization, UserInfo - -# Global state -_config: IAMConfig | None = None -_jwks_client: PyJWKClient | None = None - -# Security scheme -_bearer = HTTPBearer(auto_error=False) -_bearer_required = HTTPBearer(auto_error=True) - - -def configure( - client_id: str | None = None, - client_secret: str | None = None, - org: str | Organization = Organization.HANZO, -) -> IAMConfig: - """Configure the global IAM client. - - Args: - client_id: OAuth2 client ID (or IAM_CLIENT_ID env var) - client_secret: OAuth2 client secret (or IAM_CLIENT_SECRET env var) - org: Organization (hanzo, zoo, lux, pars) - - Returns: - The configured IAMConfig - - Raises: - ValueError: If client_id is not provided - """ - global _config, _jwks_client - - # Resolve organization - if isinstance(org, str): - org = Organization(org) - - # Get credentials from args or environment (canonical IAM_* only) - resolved_client_id = client_id or os.getenv("IAM_CLIENT_ID", "") - resolved_client_secret = client_secret or os.getenv("IAM_CLIENT_SECRET", "") - - if not resolved_client_id: - raise ValueError("client_id required (or set IAM_CLIENT_ID)") - - _config = IAMConfig( - server_url=os.getenv("IAM_ENDPOINT", org.iam_url), - client_id=resolved_client_id, - client_secret=resolved_client_secret, - organization=os.getenv("IAM_ORG", org.value), - ) - - # Reset JWKS client to pick up new config - _jwks_client = None - - return _config - - -def get_config() -> IAMConfig: - """Get the configured IAM config. - - Returns: - The current IAMConfig - - Raises: - RuntimeError: If configure() has not been called - """ - if _config is None: - raise RuntimeError("IAM not configured. Call configure() first.") - return _config - - -def _get_jwks_client() -> PyJWKClient: - """Get or create the JWKS client.""" - global _jwks_client - - if _jwks_client is None: - config = get_config() - jwks_url = f"{config.server_url}/.well-known/jwks.json" - _jwks_client = PyJWKClient(jwks_url) - - return _jwks_client - - -# ============================================================================= -# Token extraction dependencies -# ============================================================================= - - -async def get_token( - credentials: HTTPAuthorizationCredentials | None = Depends(_bearer), -) -> str | None: - """Extract bearer token from request (optional). - - Returns None if no token is present. - """ - if credentials is None: - return None - return credentials.credentials - - -async def require_token( - credentials: HTTPAuthorizationCredentials = Depends(_bearer_required), -) -> str: - """Require bearer token from request. - - Raises 401 if no token is present. - """ - return credentials.credentials - - -# ============================================================================= -# Token validation dependencies -# ============================================================================= - - -def _validate_token(token: str) -> JWTClaims: - """Validate JWT token and return claims. - - Args: - token: JWT token string - - Returns: - JWTClaims with validated claims - - Raises: - HTTPException: If token is invalid or expired - """ - config = get_config() - jwks_client = _get_jwks_client() - - try: - # Get signing key from JWKS - signing_key = jwks_client.get_signing_key_from_jwt(token) - - # Decode and verify token - payload = jwt.decode( - token, - signing_key.key, - algorithms=["RS256", "ES256"], - audience=config.client_id, - issuer=config.server_url, - ) - - return JWTClaims.model_validate(payload) - - except jwt.ExpiredSignatureError as err: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Token has expired", - headers={"WWW-Authenticate": "Bearer"}, - ) from err - except jwt.InvalidTokenError as err: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail=f"Invalid token: {err}", - headers={"WWW-Authenticate": "Bearer"}, - ) from err - - -async def get_token_claims( - token: str | None = Depends(get_token), -) -> JWTClaims | None: - """Validate token and return claims (optional). - - Returns None if no token is present. - Raises 401 if token is invalid. - """ - if token is None: - return None - return _validate_token(token) - - -async def require_auth( - token: str = Depends(require_token), -) -> JWTClaims: - """Require valid token and return claims. - - Raises 401 if no token or invalid token. - """ - return _validate_token(token) - - -# ============================================================================= -# User info dependencies -# ============================================================================= - - -async def _fetch_user_info(token: str) -> UserInfo: - """Fetch user info from IAM server. - - Args: - token: Access token - - Returns: - UserInfo from the IAM userinfo endpoint - - Raises: - HTTPException: If request fails - """ - config = get_config() - userinfo_url = f"{config.server_url}/api/userinfo" - - async with httpx.AsyncClient() as client: - response = await client.get( - userinfo_url, - headers={"Authorization": f"Bearer {token}"}, - ) - - if response.status_code != 200: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Failed to fetch user info", - headers={"WWW-Authenticate": "Bearer"}, - ) - - return UserInfo.model_validate(response.json()) - - -async def get_current_user( - token: str = Depends(require_token), -) -> UserInfo: - """Get full user info from IAM. - - Requires valid token. Fetches user info from IAM userinfo endpoint. - """ - return await _fetch_user_info(token) - - -async def get_optional_user( - token: str | None = Depends(get_token), -) -> UserInfo | None: - """Get user info if authenticated, None otherwise. - - Does not raise on missing/invalid token. - """ - if token is None: - return None - - try: - _validate_token(token) # Validate first - return await _fetch_user_info(token) - except HTTPException: - return None - - -# ============================================================================= -# Authorization dependencies -# ============================================================================= - - -def require_org(allowed_orgs: list[str | Organization]) -> Callable: - """Create dependency that requires user to be from specific org(s). - - Args: - allowed_orgs: List of allowed organization names or Organization enums - - Returns: - FastAPI dependency that validates org membership - - Usage: - @app.get("/hanzo-only") - async def route(claims: JWTClaims = Depends(require_org(["hanzo"]))): - ... - """ - # Normalize to strings - orgs = [o.value if isinstance(o, Organization) else o for o in allowed_orgs] - - async def _check_org(claims: JWTClaims = Depends(require_auth)) -> JWTClaims: - user_org = claims.owner or "" - if user_org not in orgs: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail=f"Access restricted to organizations: {orgs}", - ) - return claims - - return _check_org - - -async def require_admin( - claims: JWTClaims = Depends(require_auth), -) -> JWTClaims: - """Require user to have admin role. - - Raises 403 if user is not an admin. - """ - if "admin" not in claims.roles: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail="Admin access required", - ) - return claims - - -def require_role(role: str) -> Callable: - """Create dependency that requires specific role. - - Args: - role: Required role name - - Returns: - FastAPI dependency that validates role - - Usage: - @app.get("/moderators") - async def route(claims: JWTClaims = Depends(require_role("moderator"))): - ... - """ - - async def _check_role(claims: JWTClaims = Depends(require_auth)) -> JWTClaims: - if role not in claims.roles: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail=f"Role required: {role}", - ) - return claims - - return _check_role - - -# ============================================================================= -# Exports -# ============================================================================= - -__all__ = [ - # Configuration - "configure", - "get_config", - # Token extraction - "get_token", - "require_token", - # Token validation - "get_token_claims", - "require_auth", - # User info - "get_current_user", - "get_optional_user", - # Authorization - "require_org", - "require_admin", - "require_role", -] diff --git a/pkg/hanzo-iam/hanzo_iam/models.py b/pkg/hanzo-iam/hanzo_iam/models.py deleted file mode 100644 index de00bac51..000000000 --- a/pkg/hanzo-iam/hanzo_iam/models.py +++ /dev/null @@ -1,210 +0,0 @@ -"""Pydantic models for Hanzo IAM (Casdoor-based).""" - -from __future__ import annotations - -from datetime import datetime -from enum import Enum -from typing import Any - -from pydantic import BaseModel, ConfigDict, Field - - -class Organization(str, Enum): - """Hanzo IAM organizations with their identity domains.""" - - HANZO = "hanzo" - ZOO = "zoo" - LUX = "lux" - PARS = "pars" - - @property - def iam_url(self) -> str: - """Return the IAM URL for this organization.""" - return f"https://{self.value}.id" - - -class IAMConfig(BaseModel): - """Configuration for Hanzo IAM client.""" - - model_config = ConfigDict(frozen=True) - - server_url: str = Field(description="IAM server URL (e.g., https://hanzo.id)") - client_id: str = Field(description="OAuth2 client ID") - client_secret: str = Field(default="", description="OAuth2 client secret") - organization: str = Field(default="hanzo", description="IAM organization name") - application: str = Field(default="app", description="IAM application name") - certificate: str = Field( - default="", description="JWT verification certificate (PEM)" - ) - - -class TokenResponse(BaseModel): - """OAuth2 token response.""" - - model_config = ConfigDict(populate_by_name=True) - - access_token: str = Field(alias="access_token") - token_type: str = Field(default="Bearer", alias="token_type") - expires_in: int = Field(alias="expires_in") - refresh_token: str | None = Field(default=None, alias="refresh_token") - id_token: str | None = Field(default=None, alias="id_token") - scope: str | None = Field(default=None) - - -class JWTClaims(BaseModel): - """JWT claims with standard and Hanzo-specific fields.""" - - model_config = ConfigDict(populate_by_name=True, extra="allow") - - # Standard JWT claims - iss: str | None = Field(default=None, description="Issuer") - sub: str | None = Field(default=None, description="Subject (user ID)") - aud: str | list[str] | None = Field(default=None, description="Audience") - exp: int | None = Field( - default=None, description="Expiration time (Unix timestamp)" - ) - iat: int | None = Field(default=None, description="Issued at (Unix timestamp)") - nbf: int | None = Field(default=None, description="Not before (Unix timestamp)") - jti: str | None = Field(default=None, description="JWT ID") - - # Hanzo-specific claims - name: str | None = Field(default=None, description="User display name") - email: str | None = Field(default=None, description="User email") - owner: str | None = Field(default=None, description="Organization owner") - roles: list[str] = Field(default_factory=list, description="User roles") - - @property - def is_expired(self) -> bool: - """Check if token is expired.""" - if self.exp is None: - return False - return datetime.now().timestamp() > self.exp - - -class UserInfo(BaseModel): - """OIDC UserInfo response with Hanzo extensions.""" - - model_config = ConfigDict(populate_by_name=True, extra="allow") - - # Standard OIDC claims - sub: str = Field(description="Subject identifier") - name: str | None = Field(default=None) - given_name: str | None = Field(default=None, alias="given_name") - family_name: str | None = Field(default=None, alias="family_name") - preferred_username: str | None = Field(default=None, alias="preferred_username") - email: str | None = Field(default=None) - email_verified: bool = Field(default=False, alias="email_verified") - picture: str | None = Field(default=None) - locale: str | None = Field(default=None) - updated_at: int | None = Field(default=None, alias="updated_at") - - # Hanzo extensions - owner: str | None = Field(default=None, description="Organization owner") - balance: float = Field(default=0.0, description="Account balance") - is_admin: bool = Field(default=False, alias="isAdmin", description="Admin status") - roles: list[str] = Field(default_factory=list, description="User roles") - - -class User(BaseModel): - """Full user object from IAM API.""" - - model_config = ConfigDict(populate_by_name=True, extra="allow") - - # Identity - id: str | None = Field(default=None) - owner: str = Field(description="Organization owner") - name: str = Field(description="Username") - display_name: str = Field(default="", alias="displayName") - - # Contact - email: str | None = Field(default=None) - phone: str | None = Field(default=None) - country_code: str | None = Field(default=None, alias="countryCode") - - # Profile - avatar: str | None = Field(default=None) - avatar_type: str | None = Field(default=None, alias="avatarType") - bio: str | None = Field(default=None) - location: str | None = Field(default=None) - homepage: str | None = Field(default=None) - - # Status - is_admin: bool = Field(default=False, alias="isAdmin") - is_deleted: bool = Field(default=False, alias="isDeleted") - is_forbidden: bool = Field(default=False, alias="isForbidden") - is_online: bool = Field(default=False, alias="isOnline") - - # Account - type: str | None = Field(default=None) - password: str | None = Field(default=None) - password_salt: str | None = Field(default=None, alias="passwordSalt") - password_type: str | None = Field(default=None, alias="passwordType") - - # Verification - email_verified: bool = Field(default=False, alias="emailVerified") - phone_verified: bool = Field(default=False, alias="phoneVerified") - - # Hanzo extensions - balance: float = Field(default=0.0) - score: int = Field(default=0) - karma: int = Field(default=0) - ranking: int = Field(default=0) - signup_application: str | None = Field(default=None, alias="signupApplication") - - # Permissions - roles: list[str] = Field(default_factory=list) - permissions: list[str] = Field(default_factory=list) - groups: list[str] = Field(default_factory=list) - - # Timestamps - created_time: str | None = Field(default=None, alias="createdTime") - updated_time: str | None = Field(default=None, alias="updatedTime") - - # Additional properties - properties: dict[str, Any] = Field(default_factory=dict) - - -class Application(BaseModel): - """IAM application configuration.""" - - model_config = ConfigDict(populate_by_name=True, extra="allow") - - # Identity - owner: str = Field(description="Organization owner") - name: str = Field(description="Application name") - display_name: str = Field(default="", alias="displayName") - description: str = Field(default="") - logo: str = Field(default="") - homepage_url: str = Field(default="", alias="homepageUrl") - - # OAuth2 configuration - client_id: str = Field(alias="clientId") - client_secret: str = Field(default="", alias="clientSecret") - redirect_uris: list[str] = Field(default_factory=list, alias="redirectUris") - grant_types: list[str] = Field(default_factory=list, alias="grantTypes") - response_types: list[str] = Field(default_factory=list, alias="responseTypes") - - # Token settings - expire_in_hours: int = Field(default=168, alias="expireInHours") - refresh_expire_in_hours: int = Field(default=0, alias="refreshExpireInHours") - - # Providers - providers: list[dict[str, Any]] = Field(default_factory=list) - signup_items: list[dict[str, Any]] = Field( - default_factory=list, alias="signupItems" - ) - - # Features - enable_password: bool = Field(default=True, alias="enablePassword") - enable_signup: bool = Field(default=True, alias="enableSignUp") - enable_signin_session: bool = Field(default=False, alias="enableSigninSession") - enable_code_signin: bool = Field(default=False, alias="enableCodeSignin") - - # Organization - organization: str = Field(default="") - - # Certificate for JWT verification - cert: str = Field(default="") - - # Timestamps - created_time: str | None = Field(default=None, alias="createdTime") diff --git a/pkg/hanzo-iam/pyproject.toml b/pkg/hanzo-iam/pyproject.toml deleted file mode 100644 index 487fffd5d..000000000 --- a/pkg/hanzo-iam/pyproject.toml +++ /dev/null @@ -1,63 +0,0 @@ -[project] -name = "hanzo-iam" -version = "1.30.0" -description = "Hanzo IAM SDK - Identity and Access Management for Python" -readme = "README.md" -license = { text = "MIT" } -requires-python = ">=3.12" -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["oauth2", "oidc", "iam", "hanzo", "authentication", "identity", "access-management"] -classifiers = [ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Topic :: Security", - "Topic :: Software Development :: Libraries :: Python Modules", - "Typing :: Typed", -] - -dependencies = [ - "httpx>=0.25.0", - "pydantic>=2.0.0", - "pyjwt>=2.8.0", -] - -[project.optional-dependencies] -fastapi = ["fastapi>=0.100.0"] -kms = ["hanzo-kms>=1.0.0"] -dev = [ - "pytest>=8.0.0", - "pytest-asyncio>=0.24.0", - "ruff>=0.5.0", - "mypy>=1.10.0", -] - -[project.urls] -Homepage = "https://hanzo.id" -Documentation = "https://docs.hanzo.ai/iam" -Repository = "https://github.com/hanzoai/python-sdk" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_iam"] - -[tool.ruff] -target-version = "py39" -line-length = 100 - -[tool.ruff.lint] -select = ["E", "W", "F", "I", "B", "C4", "UP"] -ignore = ["E501", "B008"] # B008: Depends() in defaults is standard FastAPI pattern - -[tool.mypy] -python_version = "3.9" -strict = true diff --git a/pkg/hanzo-iam/tests/test_env.py b/pkg/hanzo-iam/tests/test_env.py deleted file mode 100644 index 1928ec6b6..000000000 --- a/pkg/hanzo-iam/tests/test_env.py +++ /dev/null @@ -1,145 +0,0 @@ -"""Tests for the canonical IAM_* environment variable contract. - -There is exactly one prefix โ€” ``IAM_``. No upstream-brand aliases, no -per-org variants. See ~/work/hanzo/iam/CLAUDE.md "Configuration". -""" - -from __future__ import annotations - -import os - -import pytest - -from hanzo_iam.client import IAMClient -from hanzo_iam.config import IAMConfig -from hanzo_iam.models import Organization - -_IAM_VARS = ( - "IAM_ENDPOINT", - "IAM_CLIENT_ID", - "IAM_CLIENT_SECRET", - "IAM_ORG", - "IAM_APP", - "IAM_CERT", -) -_LEGACY_VARS = ( - "HANZO_IAM_ENDPOINT", - "HANZO_IAM_CLIENT_ID", - "HANZO_IAM_CLIENT_SECRET", - "HANZO_IAM_ORG", - "HANZO_IAM_APP", - "HANZO_IAM_CERT", - "HANZO_IAM_URL", - "HANZO_IAM_SERVER_URL", - "HANZO_CLIENT_ID", - "HANZO_CLIENT_SECRET", - "HANZO_IAM_ORGANIZATION", - "HANZO_IAM_APP_NAME", - "HANZO_IAM_ORG_NAME", - "HANZO_IAM_CERTIFICATE", - "LUX_IAM_CLIENT_ID", - "ZOO_IAM_CLIENT_ID", - "PARS_IAM_CLIENT_ID", -) - - -@pytest.fixture(autouse=True) -def clean_env(monkeypatch): - """Strip every legacy + canonical IAM env var before each test.""" - for v in (*_IAM_VARS, *_LEGACY_VARS): - monkeypatch.delenv(v, raising=False) - - -class TestCanonicalPrefix: - """IAMConfig.ENV_PREFIX must be 'IAM_' โ€” never an upstream-brand prefix.""" - - def test_env_prefix_is_iam(self): - assert IAMConfig.ENV_PREFIX == "IAM_" - - -class TestConfigFromEnv: - """IAMConfig.from_env reads only IAM_* vars.""" - - def test_reads_iam_vars(self, monkeypatch): - monkeypatch.setenv("IAM_ENDPOINT", "https://iam.example") - monkeypatch.setenv("IAM_CLIENT_ID", "cid") - monkeypatch.setenv("IAM_CLIENT_SECRET", "csec") - monkeypatch.setenv("IAM_ORG", "acme") - monkeypatch.setenv("IAM_APP", "myapp") - - cfg = IAMConfig.from_env() - assert cfg.server_url == "https://iam.example" - assert cfg.client_id == "cid" - assert cfg.client_secret == "csec" - assert cfg.organization == "acme" - assert cfg.application == "myapp" - - def test_ignores_hanzo_iam_legacy(self, monkeypatch): - # Legacy aliases must NOT be honored. - monkeypatch.setenv("HANZO_IAM_CLIENT_ID", "legacy") - monkeypatch.setenv("HANZO_IAM_CLIENT_SECRET", "legacy-secret") - monkeypatch.setenv("HANZO_IAM_ENDPOINT", "https://legacy.example") - - cfg = IAMConfig.from_env() - assert cfg.client_id == "" - assert cfg.client_secret == "" - assert cfg.server_url == "" - - def test_ignores_org_prefixed_legacy(self, monkeypatch): - # {ORG}_IAM_* aliases must NOT be honored. - monkeypatch.setenv("LUX_IAM_CLIENT_ID", "leak") - monkeypatch.setenv("ZOO_IAM_CLIENT_SECRET", "leak2") - - cfg = IAMConfig.from_env() - assert cfg.client_id == "" - assert cfg.client_secret == "" - - def test_defaults_when_empty(self, monkeypatch): - cfg = IAMConfig.from_env() - assert cfg.server_url == "" - assert cfg.client_id == "" - assert cfg.organization == "hanzo" - assert cfg.application == "app" - - -class TestClientConfigFromEnv: - """IAMClient._config_from_env reads only IAM_* vars.""" - - def test_reads_iam_vars(self, monkeypatch): - monkeypatch.setenv("IAM_ENDPOINT", "https://iam.example") - monkeypatch.setenv("IAM_CLIENT_ID", "cid") - monkeypatch.setenv("IAM_CLIENT_SECRET", "csec") - monkeypatch.setenv("IAM_ORG", "acme") - monkeypatch.setenv("IAM_APP", "myapp") - monkeypatch.setenv("IAM_CERT", "") - - cfg = IAMClient._config_from_env(Organization.HANZO) - assert cfg.server_url == "https://iam.example" - assert cfg.client_id == "cid" - assert cfg.client_secret == "csec" - assert cfg.organization == "acme" - assert cfg.application == "myapp" - - def test_endpoint_defaults_to_org_url(self, monkeypatch): - cfg = IAMClient._config_from_env(Organization.ZOO) - assert cfg.server_url == "https://zoo.id" - assert cfg.organization == "zoo" - - def test_ignores_hanzo_iam_legacy(self, monkeypatch): - monkeypatch.setenv("HANZO_IAM_CLIENT_ID", "legacy") - monkeypatch.setenv("HANZO_IAM_URL", "https://legacy.example") - - cfg = IAMClient._config_from_env(Organization.HANZO) - assert cfg.client_id == "" - assert cfg.server_url == "https://hanzo.id" # org default, NOT legacy URL - - def test_ignores_org_prefixed_legacy(self, monkeypatch): - monkeypatch.setenv("LUX_IAM_CLIENT_ID", "leak") - monkeypatch.setenv("ZOO_IAM_CLIENT_ID", "leak2") - - cfg = IAMClient._config_from_env(Organization.LUX) - assert cfg.client_id == "" - - -if __name__ == "__main__": - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-iam/tests/test_fastapi.py b/pkg/hanzo-iam/tests/test_fastapi.py deleted file mode 100644 index 364be2a67..000000000 --- a/pkg/hanzo-iam/tests/test_fastapi.py +++ /dev/null @@ -1,154 +0,0 @@ -"""Tests for FastAPI integration.""" - -import pytest -from fastapi import FastAPI -from fastapi.testclient import TestClient - -from hanzo_iam.fastapi import ( - configure, - get_config, - require_admin, - require_org, - require_role, -) -from hanzo_iam.models import Organization - - -class TestConfigure: - """Tests for configure() function.""" - - def test_configure_with_args(self): - """Configure with explicit arguments.""" - config = configure( - client_id="test-client", - client_secret="test-secret", - org="hanzo", - ) - assert config.client_id == "test-client" - assert config.client_secret == "test-secret" - assert config.organization == "hanzo" - assert config.server_url == "https://hanzo.id" - - def test_configure_with_organization_enum(self): - """Configure with Organization enum.""" - config = configure( - client_id="test-client", - org=Organization.ZOO, - ) - assert config.server_url == "https://zoo.id" - assert config.organization == "zoo" - - def test_configure_missing_client_id_raises(self): - """Missing client_id raises ValueError.""" - import os - - # Ensure env var is not set - os.environ.pop("IAM_CLIENT_ID", None) - - with pytest.raises(ValueError, match="client_id required"): - configure(client_id=None) - - def test_get_config_before_configure_raises(self): - """get_config() before configure() raises RuntimeError.""" - # Reset global state - import hanzo_iam.fastapi as module - - module._config = None - - with pytest.raises(RuntimeError, match="IAM not configured"): - get_config() - - -class TestTokenDependencies: - """Tests for token extraction dependencies.""" - - @pytest.fixture - def app(self): - """Create test FastAPI app.""" - configure(client_id="test-client", org="hanzo") - app = FastAPI() - - @app.get("/optional") - async def optional_route(token: str | None = None): - # Simulating get_token behavior - return {"token": token} - - @app.get("/required") - async def required_route(token: str = ""): - # Simulating require_token behavior - return {"token": token} - - return app - - def test_optional_token_missing(self, app): - """Optional token returns None when missing.""" - client = TestClient(app) - # Test basic route without actual dependency - response = client.get("/optional") - assert response.status_code == 200 - - -class TestRequireOrg: - """Tests for require_org dependency factory.""" - - def test_require_org_normalizes_strings(self): - """require_org normalizes Organization enums to strings.""" - configure(client_id="test-client") - dep = require_org([Organization.HANZO, "zoo"]) - # Verify it's a callable (dependency) - assert callable(dep) - - def test_require_org_accepts_string_list(self): - """require_org accepts list of strings.""" - configure(client_id="test-client") - dep = require_org(["hanzo", "zoo", "lux"]) - assert callable(dep) - - -class TestRequireRole: - """Tests for require_role dependency factory.""" - - def test_require_role_returns_callable(self): - """require_role returns a callable dependency.""" - configure(client_id="test-client") - dep = require_role("moderator") - assert callable(dep) - - -class TestRequireAdmin: - """Tests for require_admin dependency.""" - - def test_require_admin_is_async(self): - """require_admin is an async function.""" - import asyncio - - assert asyncio.iscoroutinefunction(require_admin) - - -class TestModuleExports: - """Tests for module exports.""" - - def test_all_exports_exist(self): - """All __all__ exports exist.""" - from hanzo_iam import fastapi - - expected = [ - "configure", - "get_config", - "get_token", - "require_token", - "get_token_claims", - "require_auth", - "get_current_user", - "get_optional_user", - "require_org", - "require_admin", - "require_role", - ] - - for name in expected: - assert hasattr(fastapi, name), f"Missing export: {name}" - - -if __name__ == "__main__": - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-iam/uv.lock b/pkg/hanzo-iam/uv.lock deleted file mode 100644 index 391b6e476..000000000 --- a/pkg/hanzo-iam/uv.lock +++ /dev/null @@ -1,552 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" -resolution-markers = [ - "python_full_version >= '3.15'", - "python_full_version < '3.15'", -] - -[[package]] -name = "annotated-doc" -version = "0.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, -] - -[[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.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, -] - -[[package]] -name = "ast-serialize" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e2/1f/50f241d4e01fe75f4bba6a209edd4047c4b26acf70992ff885fd161f79cb/ast_serialize-0.4.0.tar.gz", hash = "sha256:74e4e634ab82d1466acf0be27043178570b98ebeaa3165f9240a6fad4c286471", size = 60687, upload-time = "2026-05-14T22:44:38.251Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/85/232631c59b5ca7152c08f026e9a46f47d852298acff74edd04a1fc1d0005/ast_serialize-0.4.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:a6f26937ce0293aafbece0e39019e020369a5a70486ff4088227f0cc888844a9", size = 1182685, upload-time = "2026-05-14T22:43:40.205Z" }, - { url = "https://files.pythonhosted.org/packages/5d/5e/4838d4d3ddc4425555601467d4e2a565e4340899e45feee4e32c80fbc911/ast_serialize-0.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:074032142777e3e6091977dc3c5146a8ca58ae6825b7f64e9a0b604153ddabd8", size = 1173113, upload-time = "2026-05-14T22:43:41.937Z" }, - { url = "https://files.pythonhosted.org/packages/22/fc/d622b19fc1c79a62028ec17f4ad4323177af25b174d32b07c84d61ef9d47/ast_serialize-0.4.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:404f3462b4532e13a70b8849bba241dbd82e30043ff58d98c7e762fd925b116a", size = 1234117, upload-time = "2026-05-14T22:43:43.977Z" }, - { url = "https://files.pythonhosted.org/packages/d5/b5/72f8c8659da0b64562e6d97f852d5c2022c74577df27c922e1e7065039ce/ast_serialize-0.4.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:97c55336e16f5c4ca2bde7be94cca4b8f7d665d64f7008925a82e02707ba14ac", size = 1231703, upload-time = "2026-05-14T22:43:46.064Z" }, - { url = "https://files.pythonhosted.org/packages/7b/98/ccc51ee4f90f97a1ed0a0848bd4c9d77a80969849db8a262b7d2970a6a15/ast_serialize-0.4.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:732b4ef76adcb0f298a7d18c4558336d83b1384f9ae0c7eaa1dc8d031b0a4390", size = 1441574, upload-time = "2026-05-14T22:43:47.784Z" }, - { url = "https://files.pythonhosted.org/packages/6a/ce/668c4efe79e09c9cc97a4d0a1c29e61fe6f78857fe1e57c086772af55f89/ast_serialize-0.4.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b3db87c4772097c0782250bcd550d66b1189a8c889793c7bcf153f4fee70005c", size = 1254040, upload-time = "2026-05-14T22:43:49.879Z" }, - { url = "https://files.pythonhosted.org/packages/3d/be/38b27bc2909b7236939801ca9f0d97cdc6198da4f435a81658e0db506fdb/ast_serialize-0.4.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43729a5e369ebbe7750635c0c206bc616fcd36e703cb9c4497d6b4df0291ee64", size = 1257847, upload-time = "2026-05-14T22:43:51.607Z" }, - { url = "https://files.pythonhosted.org/packages/68/df/360ebccc361235c167a8be2a0476870cb9ef44c42413bf1289b885684052/ast_serialize-0.4.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:91d3786f3929786cdc4eeedfd110abb4603e7f6c1390c5af398f333a947b742d", size = 1298683, upload-time = "2026-05-14T22:43:53.606Z" }, - { url = "https://files.pythonhosted.org/packages/51/5c/7d5e0b4d47aafa1600c19e3670f962f81a9bf3da1bc25a1382529a447cf3/ast_serialize-0.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7fba7315fd4bd87cb5560792709f6e66e0606402d362c0a38dd32dfb66ba6066", size = 1409438, upload-time = "2026-05-14T22:43:55.316Z" }, - { url = "https://files.pythonhosted.org/packages/b5/3d/8875b2f1af3ec1539b88ff193dfbfa5573084ef7fcab27ea4cd09b6dc829/ast_serialize-0.4.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:4db9769d57deb5545ce56ebbbbe3436dcc0ae2688ce14c295cd14e106624ece7", size = 1507922, upload-time = "2026-05-14T22:43:56.959Z" }, - { url = "https://files.pythonhosted.org/packages/55/c3/5ec6927eb493ece7ba64263cdc556be889e0c62a013b1851bbe674a0dcda/ast_serialize-0.4.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:dcd04f85a29deb80400e8987cfaceb9907140f763453cbffdbd6ff36f1b32c12", size = 1502817, upload-time = "2026-05-14T22:43:59.081Z" }, - { url = "https://files.pythonhosted.org/packages/9d/c8/40cb818a08396b1f34d6189c0c42aec917dd331e11fb7c3b870cc61b795a/ast_serialize-0.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:905fc11940831454d93589bd7ce2acb6a5eb01c2936156f751d2a21087c98cd3", size = 1454318, upload-time = "2026-05-14T22:44:01.377Z" }, - { url = "https://files.pythonhosted.org/packages/74/d5/d51494b60cc52f4792be5ddc951631cddb17a2990154634549abdbdbb5bf/ast_serialize-0.4.0-cp314-cp314t-win32.whl", hash = "sha256:3bdde2c4570143791f636aed4e3ef868f5b46eb90a18f8d5c41dd045aab08bef", size = 1060098, upload-time = "2026-05-14T22:44:03.265Z" }, - { url = "https://files.pythonhosted.org/packages/7a/c9/b0086257c79ff95743a3621448a01fc71b234ae359d3d54cda383aa43939/ast_serialize-0.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6551d55b8607b97a7755683d743200b398c61a0b71a11b7f00c89c335a11d0f4", size = 1101015, upload-time = "2026-05-14T22:44:05.055Z" }, - { url = "https://files.pythonhosted.org/packages/3d/6d/3dfddef4990fda47745af6615a3e51c4de711eda56c3a8072a0d8b6181c7/ast_serialize-0.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:7234ff086cb152ea2a3b7ef895b5ebeb6d80779df049d5c6431c8e3536d5b03c", size = 1074495, upload-time = "2026-05-14T22:44:07.186Z" }, - { url = "https://files.pythonhosted.org/packages/be/d5/044c5f995ef75807a0effb56fc288cfdedeeb571222450fb6f7d94fd52f1/ast_serialize-0.4.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dcded5056d9f3d201df7833082c07ebcbc566ffc3d4105c9fc9fe278fa086ecb", size = 1189800, upload-time = "2026-05-14T22:44:09.333Z" }, - { url = "https://files.pythonhosted.org/packages/a9/5a/52163557789d59a8197c10912ab4a1791c9143731ba0e3d9283ac0791db6/ast_serialize-0.4.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bd50d201098aae0d202805fe9606c0545492f69a3ec4403337e32c54ad29fc41", size = 1181713, upload-time = "2026-05-14T22:44:11.286Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c3/678ce3b6cb594b01c361da87f6c5679d26c1dae1583a082a8cd190e7232e/ast_serialize-0.4.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6615b39cd747967c3aabe68bf3f5f26748e823cc6b474ddc1510ed188a824149", size = 1243258, upload-time = "2026-05-14T22:44:13.345Z" }, - { url = "https://files.pythonhosted.org/packages/3d/dd/4810fbeb81c47b7e4e65db15ca65c71330efc59b460bd10c12338dc6012e/ast_serialize-0.4.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91362c0a9fdf1c344b7f50a5b0508b11a0732102998fbd754a191f7187e77031", size = 1239226, upload-time = "2026-05-14T22:44:15.811Z" }, - { url = "https://files.pythonhosted.org/packages/28/38/13a88d90b664c009ed208346ec2ed248b0ab2cb0b582ae467acaa7f44fa4/ast_serialize-0.4.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70d9c5d527bbfa69bd3c7d17dac11fb6781e36186a434a06d7d5892e0b2f88f9", size = 1448867, upload-time = "2026-05-14T22:44:17.99Z" }, - { url = "https://files.pythonhosted.org/packages/4c/19/a069dba1a634b703bf07fb49df8f7e3c04e9ba8ef3f0d9f4495f72630f92/ast_serialize-0.4.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4738790cf54d8b416de992b87ee567056980bc82134d52458bd4985f389d1658", size = 1264135, upload-time = "2026-05-14T22:44:19.8Z" }, - { url = "https://files.pythonhosted.org/packages/2d/4c/76ec4279fecd7e78b60c3c99321f944c43cd11e5ff09c952746f5f9c0f4c/ast_serialize-0.4.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:faa008dccfcb793ae9101325e4d6d026caaa5d845c2182f03749c759834b0a3a", size = 1269060, upload-time = "2026-05-14T22:44:21.894Z" }, - { url = "https://files.pythonhosted.org/packages/33/c5/9230ef7481e5cb63b93a1f7738e959586202b081caf32b8bc5d9f673ef56/ast_serialize-0.4.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1c5245228e65d38cb48e1251f0ca71b0fa417e527141491e8c92f740e8e2d121", size = 1309654, upload-time = "2026-05-14T22:44:23.725Z" }, - { url = "https://files.pythonhosted.org/packages/b9/54/7d7397528d181ad68e476e0c81aa3ceff7d1f1b5c7fa958d6be28628ef16/ast_serialize-0.4.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8f5153e9c44a02e61f4042c5f9249d2e8a759773d621a0b2f445a899e536e181", size = 1418855, upload-time = "2026-05-14T22:44:25.415Z" }, - { url = "https://files.pythonhosted.org/packages/b8/8f/87d6428adaa0986b817404f09329b64f8d2614cfe061ebf4951b4a7e0d19/ast_serialize-0.4.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:1e1fb90def261f6a0db885876f7e1a49ad2dbac38ad9f2f62dba2f9543af16e7", size = 1516040, upload-time = "2026-05-14T22:44:27.535Z" }, - { url = "https://files.pythonhosted.org/packages/b5/bb/5aaa41a21314c8b0d6dee54867b16535682c6660dd28cac64dba1380062d/ast_serialize-0.4.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf2ff7b654c8e95143e20f5d75878cbb78b65b928b26c4d58ef71cdba9d6d981", size = 1511450, upload-time = "2026-05-14T22:44:29.522Z" }, - { url = "https://files.pythonhosted.org/packages/87/16/cc729b5bb4b21da99db1379266cc367512e82ba10f9b3300a6f3e9941325/ast_serialize-0.4.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:90fc5c0d35a22f1a92dd33635508626d50f8fc64deb897c23e78e666a60804c9", size = 1463654, upload-time = "2026-05-14T22:44:31.265Z" }, - { url = "https://files.pythonhosted.org/packages/43/97/7198321b0244d011093387b41affea934d58bda08d59a2adfde72976b6c4/ast_serialize-0.4.0-cp39-abi3-win32.whl", hash = "sha256:9ecd6a1fc1b86f1f4e8ae206759b6319c10019706b3496b01b54d02b9b2cd918", size = 1068636, upload-time = "2026-05-14T22:44:33.189Z" }, - { url = "https://files.pythonhosted.org/packages/10/09/3b868f6d8df4bbe452903a5e0e039ebcec9ea0045f1a77951546205097e8/ast_serialize-0.4.0-cp39-abi3-win_amd64.whl", hash = "sha256:79c8d015c771c8bfdb1208003b227b27c40034790a2c29c09f2317a041825ce2", size = 1107137, upload-time = "2026-05-14T22:44:35.304Z" }, - { url = "https://files.pythonhosted.org/packages/fd/78/9387dffccdc55a12734f83aaccc4a987404a217a2a12a1920d8d4585950b/ast_serialize-0.4.0-cp39-abi3-win_arm64.whl", hash = "sha256:1026f565a7ab846337c630909089b3346a2fe417bf1552b1581ab01852137407", size = 1079199, upload-time = "2026-05-14T22:44:36.816Z" }, -] - -[[package]] -name = "certifi" -version = "2026.4.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, -] - -[[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 = "fastapi" -version = "0.136.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-doc" }, - { name = "pydantic" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5d/45/c130091c2dfa061bbfe3150f2a5091ef1adf149f2a8d2ae769ecaf6e99a2/fastapi-0.136.1.tar.gz", hash = "sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f", size = 397448, upload-time = "2026-04-23T16:49:44.046Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/ff/2e4eca3ade2c22fe1dea7043b8ee9dabe47753349eb1b56a202de8af6349/fastapi-0.136.1-py3-none-any.whl", hash = "sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f", size = 117683, upload-time = "2026-04-23T16:49:42.437Z" }, -] - -[[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 = "hanzo-iam" -version = "1.30.0" -source = { editable = "." } -dependencies = [ - { name = "httpx" }, - { name = "pydantic" }, - { name = "pyjwt" }, -] - -[package.optional-dependencies] -dev = [ - { name = "mypy" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "ruff" }, -] -fastapi = [ - { name = "fastapi" }, -] -kms = [ - { name = "hanzo-kms" }, -] - -[package.metadata] -requires-dist = [ - { name = "fastapi", marker = "extra == 'fastapi'", specifier = ">=0.100.0" }, - { name = "hanzo-kms", marker = "extra == 'kms'", specifier = ">=1.0.0" }, - { name = "httpx", specifier = ">=0.25.0" }, - { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.10.0" }, - { name = "pydantic", specifier = ">=2.0.0" }, - { name = "pyjwt", specifier = ">=2.8.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, - { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24.0" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.5.0" }, -] -provides-extras = ["fastapi", "kms", "dev"] - -[[package]] -name = "hanzo-kms" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "httpx" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/78/c459ae92072e55d94e6b0718d927fb2801c06ea8ef8a5cacc417c237b385/hanzo_kms-1.1.0.tar.gz", hash = "sha256:13242266012dcc2a1b48705b48704504409f21c13ec022c32385f952cf4b9f8b", size = 7553, upload-time = "2026-02-21T06:20:06.542Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/13/77301a5216f4f3c85689061e590f606086f32f26bb0ea7a86ce850223e04/hanzo_kms-1.1.0-py3-none-any.whl", hash = "sha256:19ec34ae131917e153feade770f4aa03366ba43a662fba040d3e31cd78dd2fd3", size = 10672, upload-time = "2026-02-21T06:20:05.56Z" }, -] - -[[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 = "idna" -version = "3.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "librt" -version = "0.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/40/08/9e7f6b5d2b5bed6ad055cdd5925f192bb403a51280f86b56554d9d0699a2/librt-0.11.0.tar.gz", hash = "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1", size = 200139, upload-time = "2026-05-10T18:17:25.138Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/d0/07c77e067f0838949b43bd89232c29d72efebb9d2801a9750184eb706b71/librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46", size = 144147, upload-time = "2026-05-10T18:15:53.227Z" }, - { url = "https://files.pythonhosted.org/packages/7a/24/8493538fa4f62f982686398a5b8f68008138a75086abdea19ade64bf4255/librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3", size = 143614, upload-time = "2026-05-10T18:15:54.657Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1e/f8bad050810d9171f34a1648ed910e56814c2ba61639f2bd53c6377ae24b/librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67", size = 485538, upload-time = "2026-05-10T18:15:56.117Z" }, - { url = "https://files.pythonhosted.org/packages/c0/fe/3594ebfbaf03084ba4b120c9ba5c3183fd938a48725e9bbe6ff0a5159ad8/librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a", size = 479623, upload-time = "2026-05-10T18:15:57.544Z" }, - { url = "https://files.pythonhosted.org/packages/b0/da/5d1876984b3746c85dbd219dbfcb73c85f54ee263fd32e5b2a632ec14571/librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a", size = 513082, upload-time = "2026-05-10T18:15:58.805Z" }, - { url = "https://files.pythonhosted.org/packages/19/6e/55bdf5d5ca00c3e18430690bf2c953d8d3ffd3c337418173d33dec985dc9/librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f", size = 508105, upload-time = "2026-05-10T18:16:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/07/10/f1f23a7c595ee90ece4d35c851e5d104b1311a887ed1b4ac4c35bbd13da8/librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b", size = 522268, upload-time = "2026-05-10T18:16:01.708Z" }, - { url = "https://files.pythonhosted.org/packages/b6/02/5720f5697a7f54b78b3aefbe20df3a48cedcff1276618c4aa481177942ed/librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766", size = 527348, upload-time = "2026-05-10T18:16:03.496Z" }, - { url = "https://files.pythonhosted.org/packages/50/db/b4a47c6f91db4ff76348a0b3dd0cc65e090a078b765a810a62ff9434c3d3/librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d", size = 516294, upload-time = "2026-05-10T18:16:05.173Z" }, - { url = "https://files.pythonhosted.org/packages/9e/58/9384b2f4eb1ed1d273d40948a7c5c4b2360213b402ef3be4641c06299f9c/librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8", size = 553608, upload-time = "2026-05-10T18:16:06.839Z" }, - { url = "https://files.pythonhosted.org/packages/21/7b/5aa8848a7c6a9278c79375146da1812e695754ceec5f005e6043461a7315/librt-0.11.0-cp312-cp312-win32.whl", hash = "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a", size = 101879, upload-time = "2026-05-10T18:16:08.103Z" }, - { url = "https://files.pythonhosted.org/packages/37/33/8a745436944947575b584231750a41417de1a38cf6a2e9251d1065651c09/librt-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9", size = 119831, upload-time = "2026-05-10T18:16:09.174Z" }, - { url = "https://files.pythonhosted.org/packages/59/67/a6739ac96e28b7855808bdb0370e250606104a859750d209e5a0716fe7ab/librt-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c", size = 103470, upload-time = "2026-05-10T18:16:10.369Z" }, - { url = "https://files.pythonhosted.org/packages/82/61/e59168d4d0bf2bf90f4f0caf7a001bfc60254c3af4586013b04dc3ef517b/librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894", size = 144119, upload-time = "2026-05-10T18:16:11.771Z" }, - { url = "https://files.pythonhosted.org/packages/61/fd/caa1d60b12f7dd79ccea23054e06eeaebe266a5f52c40a6b651069200ce5/librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c", size = 143565, upload-time = "2026-05-10T18:16:13.334Z" }, - { url = "https://files.pythonhosted.org/packages/b8/a9/dc744f5c2b4978d48db970be29f22716d3413d28b14ad99740817315cf2c/librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea", size = 485395, upload-time = "2026-05-10T18:16:14.729Z" }, - { url = "https://files.pythonhosted.org/packages/8f/21/7f8e97a1e4dae952a5a95948f6f8507a173bc1e669f54340bba6ca1ca31b/librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230", size = 479383, upload-time = "2026-05-10T18:16:16.321Z" }, - { url = "https://files.pythonhosted.org/packages/a6/6d/d8ee9c114bebf2c50e29ec2aa940826fccb62a645c3e4c18760987d0e16d/librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2", size = 513010, upload-time = "2026-05-10T18:16:17.647Z" }, - { url = "https://files.pythonhosted.org/packages/f0/43/0b5708af2bd30a46400e72ba6bdaa8f066f15fb9a688527e34220e8d6c06/librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3", size = 508433, upload-time = "2026-05-10T18:16:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/4a/50/356187247d09013490481033183b3532b58acf8028bcb34b2b56a375c9b2/librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21", size = 522595, upload-time = "2026-05-10T18:16:20.642Z" }, - { url = "https://files.pythonhosted.org/packages/40/e7/c6ac4240899c7f3248079d5a9900debe0dadb3fdeaf856684c987105ba47/librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930", size = 527255, upload-time = "2026-05-10T18:16:22.352Z" }, - { url = "https://files.pythonhosted.org/packages/eb/b5/a81322dbeedeeaf9c1ee6f001734d28a09d8383ac9e6779bc24bbd0743c6/librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be", size = 516847, upload-time = "2026-05-10T18:16:23.627Z" }, - { url = "https://files.pythonhosted.org/packages/ae/66/6e6323787d592b55204a42595ff1102da5115601b53a7e9ddebc889a6da5/librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e", size = 553920, upload-time = "2026-05-10T18:16:25.025Z" }, - { url = "https://files.pythonhosted.org/packages/9c/21/623f8ca230857102066d9ca8c6c1734995908c4d0d1bee7bb2ef0021cb33/librt-0.11.0-cp313-cp313-win32.whl", hash = "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e", size = 101898, upload-time = "2026-05-10T18:16:26.649Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1d/b4ebd44dd723f768469007515cb92251e0ae286c94c140f374801140fa74/librt-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47", size = 119812, upload-time = "2026-05-10T18:16:27.859Z" }, - { url = "https://files.pythonhosted.org/packages/3b/e4/b2f4ca7965ca373b491cdb4bc25cdb30c1649ca81a8782056a83850292a9/librt-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44", size = 103448, upload-time = "2026-05-10T18:16:29.066Z" }, - { url = "https://files.pythonhosted.org/packages/29/eb/dbce197da4e227779e56b5735f2decc3eb36e55a1cdbf1bd65d6639d76c1/librt-0.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4a017a95e5837dc15a8c5661d60e05daa96b90908b1aa6b7acdf443cd25c8ebd", size = 143345, upload-time = "2026-05-10T18:16:30.674Z" }, - { url = "https://files.pythonhosted.org/packages/76/a3/254bebd0c11c8ba684018efb8006ff22e466abce445215cca6c778e7d9de/librt-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ecbd9819deccc39b7542bf4d2a740d8a620694d39989e58661d3763458f8d4", size = 143131, upload-time = "2026-05-10T18:16:32.037Z" }, - { url = "https://files.pythonhosted.org/packages/f1/3f/f77d6122d21ac7bf6ae8a7dfced1bd2a7ac545d3273ebdcaf8042f6d619f/librt-0.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7da327dacd7be8f8ec36547373550744a3cc0e536d54665cd83f8bcd961200e8", size = 477024, upload-time = "2026-05-10T18:16:33.493Z" }, - { url = "https://files.pythonhosted.org/packages/ac/0a/2c996dadebaa7d9bbbd43ef2d4f3e66b6da545f838a41694ef6172cebec8/librt-0.11.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0dc56b1f8d06e60db362cc3fdae206681817f86ce4725d34511473487f12a34b", size = 474221, upload-time = "2026-05-10T18:16:34.864Z" }, - { url = "https://files.pythonhosted.org/packages/0a/7e/f5d92af8486b8272c23b3e686b46ff72d89c8169585eb61eef01a2ac7147/librt-0.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05fb8fb2ab90e21c8d12ea240d744ad514da9baf381ebfa70d91d20d21713175", size = 505174, upload-time = "2026-05-10T18:16:36.705Z" }, - { url = "https://files.pythonhosted.org/packages/af/1a/cb0734fe86398eb33193ab753b7326255c74cac5eb09e76b9b16536e7adb/librt-0.11.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cae74872be221df4374d10fec61f93ed1513b9546ea84f2c0bf73ab3e9bd0b03", size = 497216, upload-time = "2026-05-10T18:16:38.418Z" }, - { url = "https://files.pythonhosted.org/packages/18/06/094820f91558b66e29943c0ec41c9914f460f48dd51fc503c3101e10842d/librt-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32bcc918c0148eb7e3d57385125bac7e5f9e4359d05f07448b09f6f778c2f31c", size = 513921, upload-time = "2026-05-10T18:16:39.848Z" }, - { url = "https://files.pythonhosted.org/packages/0b/c2/00de9018871a282f530cacb457d5ec0428f6ac7e6fedde9aff7468d9fb04/librt-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f9743fc99135d5f78d2454435615f6dec0473ca507c26ce9d92b10b562a280d3", size = 520850, upload-time = "2026-05-10T18:16:41.471Z" }, - { url = "https://files.pythonhosted.org/packages/51/9d/64631832348fd1834fb3a61b996434edddaaf25a31d03b0a76273159d2cf/librt-0.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5ba067f4aadae8fda802d91d2124c90c42195ff32d9161d3549e6d05cfe26f96", size = 504237, upload-time = "2026-05-10T18:16:43.15Z" }, - { url = "https://files.pythonhosted.org/packages/a5/ec/ae5525eb16edc827a044e7bb8777a455ff95d4bca9379e7e6bddd7383647/librt-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:de3bf945454d032f9e390b85c4072e0a0570bf825421c8be0e71209fa65e1abe", size = 546261, upload-time = "2026-05-10T18:16:44.408Z" }, - { url = "https://files.pythonhosted.org/packages/5a/09/adce371f27ca039411da9659f7430fcc2ba6cd0c7b3e4467a0f091be7fa9/librt-0.11.0-cp314-cp314-win32.whl", hash = "sha256:d2277a05f6dcb9fd13db9566aac4fabd68c3ea1ea46ee5567d4eef8efa495a2f", size = 96965, upload-time = "2026-05-10T18:16:46.039Z" }, - { url = "https://files.pythonhosted.org/packages/d6/ee/8ac720d98548f173c7ce2e632a7ca94673f74cacd5c8162a84af5b35958a/librt-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:ab73e8db5e3f564d812c1f5c3a175930a5f9bc96ccb5e3b22a34d7858b401cf7", size = 115151, upload-time = "2026-05-10T18:16:47.133Z" }, - { url = "https://files.pythonhosted.org/packages/94/20/c900cf14efeb09b6bef2b2dff20779f73464b97fd58d1c6bccc379588ae3/librt-0.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aea3caa317752e3a466fa8af45d91ee0ea8c7fdd96e42b0a8dd9b76a7931eba1", size = 98850, upload-time = "2026-05-10T18:16:48.597Z" }, - { url = "https://files.pythonhosted.org/packages/0c/71/944bfe4b64e12abffcd3c15e1cce07f72f3d55655083786285f4dedeb532/librt-0.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d1b36540d7aaf9b9101b3a6f376c8d8e9f7a9aec93ed05918f2c69d493ffef72", size = 151138, upload-time = "2026-05-10T18:16:49.839Z" }, - { url = "https://files.pythonhosted.org/packages/b6/10/99e64a5c86989357fda078c8143c533389585f6473b7439172dd8f3b3b2d/librt-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:efbb343ab2ce3540f4ecbe6315d677ed70f37cd9a72b1e58066c918ca83acbaa", size = 151976, upload-time = "2026-05-10T18:16:51.062Z" }, - { url = "https://files.pythonhosted.org/packages/21/31/5072ad880946d83e5ea4147d6d018c78eefce85b77819b19bdd0ee229435/librt-0.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0dd688aab3f7914d3e6e5e3554978e0383312fb8e771d84be008a35b9ee548", size = 557927, upload-time = "2026-05-10T18:16:52.632Z" }, - { url = "https://files.pythonhosted.org/packages/5e/8d/70b5fb7cfbab60edbe7381614ab985da58e144fbf465c86d44c95f43cdca/librt-0.11.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f5fb36b8c6c63fdcbb1d526d94c0d1331610d43f4118cc1beb4efef4f3faacb2", size = 539698, upload-time = "2026-05-10T18:16:53.934Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a3/ba3495a0b3edbd24a4cae0d1d3c64f39a9fc45d06e812101289b50c1a619/librt-0.11.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a9a237d13addb93715b6fee74023d5ee3469b53fce527626c0e088aa585805f", size = 577162, upload-time = "2026-05-10T18:16:55.589Z" }, - { url = "https://files.pythonhosted.org/packages/f7/db/36e25fb81f99937ff1b96612a1dc9fd66f039cb9cc3aee12c01fac31aab9/librt-0.11.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ddd17bd87b2c56ddd60e546a7984a2e64c4e8eab92fb4cf3830a48ad5469d51", size = 566494, upload-time = "2026-05-10T18:16:56.975Z" }, - { url = "https://files.pythonhosted.org/packages/33/0d/3f622b47f0b013eeb9cf4cc07ae9bfe378d832a4eec998b2b209fe84244d/librt-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd43992b4473d42f12ff9e68326079f0696d9d4e6000e8f39a0238d482ba6ee2", size = 596858, upload-time = "2026-05-10T18:16:58.374Z" }, - { url = "https://files.pythonhosted.org/packages/a9/02/71b90bc93039c46a2000651f6ad60122b114c8f54c4ad306e0e96f5b75ad/librt-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f8e3e8056dd674e279741485e2e512d6e9a751c7455809d0114e6ebf8d781085", size = 590318, upload-time = "2026-05-10T18:16:59.676Z" }, - { url = "https://files.pythonhosted.org/packages/04/04/418cb3f75621e2b761fb1ab0f017f4d70a1a72a6e7c74ee4f7e8d198c2f3/librt-0.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c1f708d8ae9c56cf38a903c44297243d2ec83fd82b396b977e0144a3e76217e3", size = 575115, upload-time = "2026-05-10T18:17:01.007Z" }, - { url = "https://files.pythonhosted.org/packages/cc/2c/5a2183ac58dd911f26b5d7e7d7d8f1d87fcecdddd99d6c12169a258ff62c/librt-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0add982e0e7b9fc14cf4b33789d5f13f66581889b88c2f58099f6ce8f92617bd", size = 617918, upload-time = "2026-05-10T18:17:02.682Z" }, - { url = "https://files.pythonhosted.org/packages/15/1f/dc6771a52592a4451be6effa200cbfc9cec61e4393d3033d81a9d307961d/librt-0.11.0-cp314-cp314t-win32.whl", hash = "sha256:2b481d846ac894c4e8403c5fd0e87c5d11d6499e404b474602508a224ff531c8", size = 103562, upload-time = "2026-05-10T18:17:03.99Z" }, - { url = "https://files.pythonhosted.org/packages/62/4a/7d1415567027286a75ba1093ec4aca11f073e0f559c530cf3e0a757ad55c/librt-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:28edb433edde181112a908c78907af28f964eabc15f4dd16c9d66c834302677c", size = 124327, upload-time = "2026-05-10T18:17:05.465Z" }, - { url = "https://files.pythonhosted.org/packages/ce/62/b40b382fa0c66fee1478073eb8db352a4a6beda4a1adccf1df911d8c289c/librt-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253", size = 102572, upload-time = "2026-05-10T18:17:06.809Z" }, -] - -[[package]] -name = "mypy" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ast-serialize" }, - { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/82/15/cca9d88503549ed6fedeaa1d448cdddd542ee8a490232d732e278036fbf2/mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633", size = 3898359, upload-time = "2026-05-11T18:37:36.237Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/b1/55861beb5c339b44f9a2ba92df9e2cb1eeb4ae1eee674cdf7772c797778b/mypy-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:244358bf1c0da7722230bce60683d52e8e9fd030554926f15b747a84efb5b3af", size = 14874381, upload-time = "2026-05-11T18:37:31.784Z" }, - { url = "https://files.pythonhosted.org/packages/0b/b3/b7f770114b7d0ac92d0f76e8d93c2780844a70488a90e91821927850da86/mypy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ec7c57657493c7a75534df2751c8ae2cda383c16ecc55d2106c54476b1b16f6", size = 13665501, upload-time = "2026-05-11T18:34:23.063Z" }, - { url = "https://files.pythonhosted.org/packages/b6/f3/8ae2037967e2126689a0c11d99e2b707134a565191e92c60ca2572aec60a/mypy-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8161b6ff4392410023224f0969d17db93e1e154bc3e4ba62598e720723ae211", size = 14045750, upload-time = "2026-05-11T18:31:48.151Z" }, - { url = "https://files.pythonhosted.org/packages/a0/32/615eb5911859e43d054941b0d0a7d06cfa2870eba86529cf385b052b111c/mypy-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf03e12003084a67395184d3eb8cbd6a489dc3655b5664b28c210a9e2403ab0b", size = 15061630, upload-time = "2026-05-11T18:37:06.898Z" }, - { url = "https://files.pythonhosted.org/packages/d4/03/4eafbfff8bfab1b87082741eae6e6a624028c984e6708b73bce2a8570c9d/mypy-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:20509760fd791c51579d573153407d226385ec1f8bcce55d730b354f3336bc22", size = 15288831, upload-time = "2026-05-11T18:31:18.07Z" }, - { url = "https://files.pythonhosted.org/packages/99/ee/919661478e5891a3c96e549c036e467e64563ab85995b10c53c8358e16a3/mypy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:6753d0c1fdd6b1a23b9e4f283ce80b2153b724adcb2653b20b85a8a28ac6436b", size = 11135228, upload-time = "2026-05-11T18:34:31.23Z" }, - { url = "https://files.pythonhosted.org/packages/24/0a/6a12b9782ca0831a553192f351679f4548abc9d19a7cc93bb7feb02084c7/mypy-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:98ebb6589bb3b6d0c6f0c459d53ca55b8091fbc13d277c4041c885392e8195e8", size = 10040684, upload-time = "2026-05-11T18:36:48.199Z" }, - { url = "https://files.pythonhosted.org/packages/6e/dd/c7191469c777f07689c032a8f7326e393ea34c92d6d76eb7ce5ba57ea66d/mypy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35aac3bb114e03888f535d5eb51b8bafbb3266586b599da1940f9b1be3ec5bd5", size = 14852174, upload-time = "2026-05-11T18:31:38.929Z" }, - { url = "https://files.pythonhosted.org/packages/55/8c/aed55408879043d72bb9135f4d0d19a02b886dd569631e113e3d2706cb8d/mypy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8de55a8c861f2a49331f807be98d90caeceeef520bde13d43a160207f8af613e", size = 13651542, upload-time = "2026-05-11T18:36:04.636Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8e/f371a824b1f1fa8ea6e3dbb8703d232977d572be2329554a3bc4d960302f/mypy-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fdf2941a07434af755837d9880f7d7d25f1dacb1af9dcd4b9b66f2220a3024e", size = 14033929, upload-time = "2026-05-11T18:35:55.742Z" }, - { url = "https://files.pythonhosted.org/packages/94/21/f54be870d6dd53a82c674407e0f8eed7174b05ec78d42e5abd7b42e84fd5/mypy-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e195b817c13f02352a9c124301f9f30f078405444679b6753c1b96b6eed37285", size = 15039200, upload-time = "2026-05-11T18:33:10.281Z" }, - { url = "https://files.pythonhosted.org/packages/17/99/bf21748626a40ce59fd29a39386ab46afec88b7bd2f0fa6c3a97c995523f/mypy-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5431d42af987ebd92ba2f71d45c85ed41d8e6ca9f5fd209a69f68f707d2469e5", size = 15272690, upload-time = "2026-05-11T18:32:07.205Z" }, - { url = "https://files.pythonhosted.org/packages/d6/d7/9e90d2cf47100bea550ed2bc7b0d4de3a62181d84d5e37da0003e8462637/mypy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:767fe8c66dc3e01e19e1737d4c38ebefead16125e1b8e58ad421903b376f5c65", size = 11147435, upload-time = "2026-05-11T18:33:56.477Z" }, - { url = "https://files.pythonhosted.org/packages/ec/46/e5c449e858798e35ffc90946282a27c62a77be743fe17480e4977374eb91/mypy-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:ecfe70d43775ab99562ab128ce49854a362044c9f894961f68f898c23cb7429d", size = 10035052, upload-time = "2026-05-11T18:32:30.049Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ca/b279a672e874aedd5498ae25f722dacc8aa86bbffb939b3f97cbb1cf6686/mypy-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7354c5a7f69d9345c3d6e69921d57088eea3ddeeb6b20d34c1b3855b02c36ec2", size = 14848422, upload-time = "2026-05-11T18:35:45.984Z" }, - { url = "https://files.pythonhosted.org/packages/27/e6/3efe56c631d959b9b4454e208b0ac4b7f4f58b404c89f8bec7b49efdfc21/mypy-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:49890d4f76ac9e06ec117f9e09f3174da70a620a0c300953d8595c926e80947f", size = 13677374, upload-time = "2026-05-11T18:36:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/84/7f/8107ea87a44fd1f1b59882442f033c9c3488c127201b1d1d15f1cbd6022e/mypy-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:761be68e023ef5d94678772396a8af1220030f80837a3afd8d0aef3b419666f4", size = 14055743, upload-time = "2026-05-11T18:35:18.361Z" }, - { url = "https://files.pythonhosted.org/packages/51/4d/b6d34db183133b83761b9199a82d31557cdbb70a380d8c3b3438e11882a3/mypy-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c90345fc182dc363b891350457ec69c35140858538f38b4540845afcc32b1aef", size = 15020937, upload-time = "2026-05-11T18:34:59.618Z" }, - { url = "https://files.pythonhosted.org/packages/ff/d7/f08360c691d758acb02f45022c34d98b92892f4ea756644e1000d4b9f3d8/mypy-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b84802e7b5a6daf1f5e15bc9fcd7ddae77be13981ffab037f1c67bb84d67d135", size = 15253371, upload-time = "2026-05-11T18:36:41.081Z" }, - { url = "https://files.pythonhosted.org/packages/67/1b/09460a13719530a19bce27bd3bc8449e83569dd2ba7faf51c9c3c30c0b61/mypy-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:022c771234936ceac541ebaf836fe9e2abeb3f5e09aff21588fe543ff006fe21", size = 11326429, upload-time = "2026-05-11T18:34:13.526Z" }, - { url = "https://files.pythonhosted.org/packages/40/62/75dbf0f82f7b6680340efc614af29dd0b3c17b8a4f1cd09b8bd2fd6bc814/mypy-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:498207db725cec88829a6a5c2fc771205fd043719ef98bc49aba8fb9fc4e6d57", size = 10218799, upload-time = "2026-05-11T18:32:23.491Z" }, - { url = "https://files.pythonhosted.org/packages/b2/66/caca04ed7d972fb6eb6dd1ccd6df1de5c38fae8c5b3dc1c4e8e0d85ee6b9/mypy-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d5e5cad0efeba72b93cd17490cc0d69c5ac9ca132994fe3fb0314808aeeb83e", size = 15923458, upload-time = "2026-05-11T18:35:28.64Z" }, - { url = "https://files.pythonhosted.org/packages/ed/52/2d90cbe49d014b13ed7ff337930c30bad35893fe38a1e4641e756bb62191/mypy-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ff715050c127d724fd260a2e666e7747fdd83511c0c47d449d98238970aef780", size = 14757697, upload-time = "2026-05-11T18:36:14.208Z" }, - { url = "https://files.pythonhosted.org/packages/ac/37/d98f4a14e081b238992d0ed96b6d39c7cc0148c9699eb71eaa68629665ea/mypy-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82208da9e09414d520e912d3e462d454854bed0810b71540bb016dcbca7308fd", size = 15405638, upload-time = "2026-05-11T18:33:48.249Z" }, - { url = "https://files.pythonhosted.org/packages/a3/c2/15c46613b24a84fad2aea1248bf9619b99c2767ae9071fe224c179a0b7d4/mypy-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e79ebc1b904b84f0310dff7469655a9c36c7a68bddb37bdd42b67a332df61d08", size = 16215852, upload-time = "2026-05-11T18:32:50.296Z" }, - { url = "https://files.pythonhosted.org/packages/5c/90/9c16a57f482c76d25f6379762b56bbf65c711d8158cf271fb2802cfb0640/mypy-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e583edc957cfb0deb142079162ae826f58449b116c1d442f2d91c69d9fced081", size = 16452695, upload-time = "2026-05-11T18:33:38.182Z" }, - { url = "https://files.pythonhosted.org/packages/0f/4c/215a4eeb63cacc5f17f516691ea7285d11e249802b942476bff15922a314/mypy-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b33b6cd332695bba180d55e717a79d3038e479a2c49cc5eb3d53603409b9a5d7", size = 12866622, upload-time = "2026-05-11T18:34:39.945Z" }, - { url = "https://files.pythonhosted.org/packages/4b/50/1043e1db5f455ffe4c9ab22747cd8ca2bc492b1e4f4e21b130a44ee2b217/mypy-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:4f910fe825376a7b66ef7ca8c98e5a149e8cd64c19ae71d84047a74ee060d4e6", size = 10610798, upload-time = "2026-05-11T18:36:31.444Z" }, - { url = "https://files.pythonhosted.org/packages/0d/2a/13ca1f292f6db1b98ff495ef3467736b331621c5917cad984b7043e7348d/mypy-2.1.0-py3-none-any.whl", hash = "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289", size = 2693302, upload-time = "2026-05-11T18:31:29.246Z" }, -] - -[[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 = "packaging" -version = "26.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, -] - -[[package]] -name = "pathspec" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, -] - -[[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 = "pydantic" -version = "2.13.4" -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/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, -] - -[[package]] -name = "pydantic-core" -version = "2.46.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, - { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, - { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, - { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, - { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, - { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, - { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, - { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, - { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, - { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, - { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, - { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, - { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, - { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, - { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, - { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, - { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, - { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, - { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, - { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, - { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, - { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, - { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, - { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, - { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, - { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, - { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, - { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, - { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, - { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, - { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, - { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, - { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, - { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, - { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, - { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, - { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, - { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, - { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, - { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, - { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, - { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, - { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, - { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, - { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, - { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, - { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, - { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, - { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, - { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, - { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, - { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, - { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, - { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, - { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, - { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, - { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, - { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, -] - -[[package]] -name = "pygments" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, -] - -[[package]] -name = "pyjwt" -version = "2.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, -] - -[[package]] -name = "pytest" -version = "9.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, -] - -[[package]] -name = "pytest-asyncio" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, -] - -[[package]] -name = "ruff" -version = "0.15.13" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/21/a7d5c126d5b557715ef81098f3db2fe20f622a039ff2e626af28d674ab80/ruff-0.15.13.tar.gz", hash = "sha256:f9d89f17f7ba7fb2ed42921f0df75da797a9a5d71bc39049e2c687cf2baf44b7", size = 4678180, upload-time = "2026-05-14T13:44:37.869Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/61/11d458dc6ac22504fd8e237b29dfd40504c7fbbcc8930402cfe51a8e63ed/ruff-0.15.13-py3-none-linux_armv6l.whl", hash = "sha256:444b580fc72fd6887e650acd3e575e18cdc79dbcf42fb4030b491057921f61f8", size = 10738279, upload-time = "2026-05-14T13:44:18.7Z" }, - { url = "https://files.pythonhosted.org/packages/86/ca/caa871ee7be718c45256fada4e16a218ee3e33f0c4a46b729a60a24912e6/ruff-0.15.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6590d009e7cb7ebf36f83dbdd44a3fa48a0994ff6f1cdc1b08006abe58f98dc7", size = 11124798, upload-time = "2026-05-14T13:44:06.427Z" }, - { url = "https://files.pythonhosted.org/packages/d3/19/43f5f2e568dddde567fc41f8471f9432c09563e19d3e617a48cfa52f8f0a/ruff-0.15.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1c26d2f66163deeb6e08d8b39fbbe983ce3c71cea06a6d7591cfd1421793c629", size = 10460761, upload-time = "2026-05-14T13:44:04.375Z" }, - { url = "https://files.pythonhosted.org/packages/99/df/cf938cd6de3003178f03ad7c1ea2a6c099468c03a35037985070b37e76be/ruff-0.15.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbd6f94b434f896308e4d57fb7bfde0d02b99f7a64b3bdab0fdfa6a864203a5", size = 10804451, upload-time = "2026-05-14T13:44:25.221Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7d/5d0973129b154ded2225729169d7068f26b467760b146493fde138415f23/ruff-0.15.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf3259f3be4d181bda591da5db2571aed6853c6a048157756448020bc6c5cd22", size = 10534285, upload-time = "2026-05-14T13:44:08.888Z" }, - { url = "https://files.pythonhosted.org/packages/1f/e3/6b999bbc66cd51e5f073842bc2a3995e99c5e0e72e16b15e7261f7abf57a/ruff-0.15.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae9c17e5eb4430c154e76abc25d79a318190f5a997f38fb6b114416c5319ffc9", size = 11312063, upload-time = "2026-05-14T13:44:11.274Z" }, - { url = "https://files.pythonhosted.org/packages/af/5a/642639e9f5db04f1e97fbd6e091c6fd20725bdf072fb114d00eefb9e6eb8/ruff-0.15.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e2e39bff6c341f4b577a21b801326fab0b11847f48fcaa83f00a113c9b3cb55", size = 12183079, upload-time = "2026-05-14T13:44:01.634Z" }, - { url = "https://files.pythonhosted.org/packages/19/4c/7585735f6b53b0f12de13618b2f7d250a844f018822efc899df2e7b8295f/ruff-0.15.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e8d9a8e08013542e94d3220bc5b62cc3e5ef87c5f74bff367d3fac14fab013e6", size = 11440833, upload-time = "2026-05-14T13:43:59.043Z" }, - { url = "https://files.pythonhosted.org/packages/e8/31/bf1a0803d077e679cfeee5f2f67290a0fa79c7385b5d9a8c17b9db2c48f0/ruff-0.15.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc411dfebe5eebe55ce041c6ae080eb7668955e866daa2fbb16692a784f1c4ca", size = 11434486, upload-time = "2026-05-14T13:44:27.761Z" }, - { url = "https://files.pythonhosted.org/packages/e1/4e/62c9b999875d4f14db80f277c030578f5e249c9852d65b7ac7ad0b43c041/ruff-0.15.13-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:768494eb08b9cee54e2fd27969966f74db5a57f6eaa7a90fcb3306af34dfc4bd", size = 11385189, upload-time = "2026-05-14T13:44:13.704Z" }, - { url = "https://files.pythonhosted.org/packages/fc/89/7e959047a104df3eb12863447c110140191fc5b6c4f379ea2e803fcdb0e4/ruff-0.15.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fb75f9a3a7e42ffe117d734494e6c5e5cb3565d66e12612cb63d0e572a41a5b6", size = 10781380, upload-time = "2026-05-14T13:43:56.734Z" }, - { url = "https://files.pythonhosted.org/packages/ff/52/5fd18f3b88cab63e88aa11516b3b4e1e5f720e5c330f8dbe5c26210f41f8/ruff-0.15.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8cb74dd33bb2f6613faf7fc03b660053b5ac4f80e706d5788c6335e2a8048d51", size = 10540605, upload-time = "2026-05-14T13:44:20.748Z" }, - { url = "https://files.pythonhosted.org/packages/e8/e0/9e35f338990d3e41a82875ff7053ffe97541dae81c9d02143177f381d572/ruff-0.15.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:7ef823f817fcd191dc934e984be9cf4094f808effa16f2542ad8e821ba02bbf2", size = 11036554, upload-time = "2026-05-14T13:44:16.256Z" }, - { url = "https://files.pythonhosted.org/packages/c2/13/070fb048c24080fba188f66371e2a92785be257ad02242066dc7255ac6e9/ruff-0.15.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f345a13937bd7f09f6f5d19fa0721b0c103e00e7f62bc67089a8e5e037719e0b", size = 11528133, upload-time = "2026-05-14T13:44:22.808Z" }, - { url = "https://files.pythonhosted.org/packages/6b/8c/b1e1666aef7fc6555094d73ae6cd981701781ae85b97ceefc0eebd0b4668/ruff-0.15.13-py3-none-win32.whl", hash = "sha256:4044f94208b3b05ba0fc4a4abd0558cf4d6459bd18325eead7fd8cc66f909b41", size = 10721455, upload-time = "2026-05-14T13:44:35.697Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a6/870a3e8a50590bb92be184ad928c2922f088b00d9dc5c5ec7b924ee08c22/ruff-0.15.13-py3-none-win_amd64.whl", hash = "sha256:7064884d442b7d477b4e7473d12da7f08851d2b1982763c5d3f388a19468a1a4", size = 11900409, upload-time = "2026-05-14T13:44:30.389Z" }, - { url = "https://files.pythonhosted.org/packages/9b/36/9c015cd052fca743dae8cb2aeb16b551444787467db42ceab0fc968865af/ruff-0.15.13-py3-none-win_arm64.whl", hash = "sha256:2471da9bd1068c8c064b5fd9c0c4b6dddffd6369cb1cd68b29993b1709ff1b21", size = 11179336, upload-time = "2026-05-14T13:44:33.026Z" }, -] - -[[package]] -name = "starlette" -version = "1.0.0" -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/81/69/17425771797c36cded50b7fe44e850315d039f28b15901ab44839e70b593/starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149", size = 2655289, upload-time = "2026-03-22T18:29:46.779Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] diff --git a/pkg/hanzo-kms/README.md b/pkg/hanzo-kms/README.md deleted file mode 100644 index 03403c64d..000000000 --- a/pkg/hanzo-kms/README.md +++ /dev/null @@ -1,152 +0,0 @@ -# Hanzo KMS - Python SDK - -Official Python SDK for [Hanzo KMS](https://kms.hanzo.ai) - Secret management for your applications. - -## Installation - -```bash -pip install hanzo-kms -``` - -Or with uv: - -```bash -uv add hanzo-kms -``` - -## Quick Start - -```python -from hanzo_kms import KMSClient, ClientSettings, AuthenticationOptions, UniversalAuthMethod - -# Initialize client -client = KMSClient(ClientSettings( - site_url="https://kms.hanzo.ai", - auth=AuthenticationOptions( - universal_auth=UniversalAuthMethod( - client_id="your-client-id", - client_secret="your-client-secret", - ) - ) -)) - -# List all secrets -secrets = client.list_secrets( - project_id="my-project", - environment="production" -) - -for secret in secrets: - print(f"{secret.secret_key}: {secret.secret_value}") - -# Get a specific secret -db_url = client.get_value( - project_id="my-project", - environment="production", - secret_name="DATABASE_URL" -) - -# Inject all secrets into environment -client.inject_env( - project_id="my-project", - environment="production" -) -``` - -## Environment Variables - -The client can be configured via environment variables: - -```bash -export HANZO_KMS_URL="https://kms.hanzo.ai" -export HANZO_KMS_CLIENT_ID="your-client-id" -export HANZO_KMS_CLIENT_SECRET="your-client-secret" -``` - -Then simply: - -```python -from hanzo_kms import KMSClient - -client = KMSClient() # Uses environment variables -secrets = client.list_secrets("my-project", "production") -``` - -## Authentication Methods - -### Universal Auth (Recommended) - -```python -from hanzo_kms import KMSClient, ClientSettings, AuthenticationOptions, UniversalAuthMethod - -client = KMSClient(ClientSettings( - auth=AuthenticationOptions( - universal_auth=UniversalAuthMethod( - client_id="...", - client_secret="...", - ) - ) -)) -``` - -### Kubernetes Auth - -For workloads running in Kubernetes: - -```python -from hanzo_kms import KMSClient, ClientSettings, AuthenticationOptions, KubernetesAuthMethod - -client = KMSClient(ClientSettings( - auth=AuthenticationOptions( - kubernetes=KubernetesAuthMethod( - identity_id="your-identity-id", - # Uses default service account token path - ) - ) -)) -``` - -### AWS IAM Auth - -```python -from hanzo_kms import KMSClient, ClientSettings, AuthenticationOptions, AWSIamAuthMethod - -client = KMSClient(ClientSettings( - auth=AuthenticationOptions( - aws_iam=AWSIamAuthMethod( - identity_id="your-identity-id", - ) - ) -)) -``` - -## API Reference - -### KMSClient - -| Method | Description | -|--------|-------------| -| `list_secrets(project_id, environment, path="/")` | List all secrets | -| `get_secret(project_id, environment, secret_name)` | Get a single secret | -| `get_value(project_id, environment, secret_name, default=None)` | Get just the value | -| `create_secret(project_id, environment, secret_name, secret_value)` | Create a secret | -| `update_secret(project_id, environment, secret_name, secret_value)` | Update a secret | -| `delete_secret(project_id, environment, secret_name)` | Delete a secret | -| `inject_env(project_id, environment, overwrite=False)` | Inject into os.environ | - -## Compatibility - -This SDK is compatible with: -- Hanzo KMS (https://kms.hanzo.ai) -- Lux KMS (https://kms.lux.network) -- Infisical (https://infisical.com) - -The `InfisicalClient` alias is provided for drop-in compatibility: - -```python -from hanzo_kms import InfisicalClient # Same as KMSClient -``` - -## License - -MIT License - see [LICENSE](LICENSE) for details. diff --git a/pkg/hanzo-kms/hanzo_kms/__init__.py b/pkg/hanzo-kms/hanzo_kms/__init__.py deleted file mode 100644 index 474d995eb..000000000 --- a/pkg/hanzo-kms/hanzo_kms/__init__.py +++ /dev/null @@ -1,85 +0,0 @@ -""" -Hanzo KMS - Secret Management SDK for Python - -A pure Python SDK for Hanzo/Lux KMS (compatible with Infisical API). - -Usage: - from hanzo_kms import KMSClient, ClientSettings, UniversalAuthMethod, AuthenticationOptions - - client = KMSClient(ClientSettings( - site_url="https://kms.hanzo.ai", - auth=AuthenticationOptions( - universal_auth=UniversalAuthMethod( - client_id="your-client-id", - client_secret="your-client-secret", - ) - ) - )) - - # List all secrets - secrets = client.list_secrets(project_id="my-project", environment="production") - - # Get a specific secret - secret = client.get_secret( - project_id="my-project", - environment="production", - secret_name="DATABASE_URL" - ) - print(secret.secret_value) - - # Inject secrets into environment - client.inject_env(project_id="my-project", environment="production") -""" - -__version__ = "1.0.0" - -from .async_client import AsyncKMSClient -from .client import KMSClient -from .models import ( - AuthenticationOptions, - AWSIamAuthMethod, - AzureAuthMethod, - ClientSettings, - CreateSecretOptions, - DeleteSecretOptions, - GCPIamAuthMethod, - GCPIDTokenAuthMethod, - GetSecretOptions, - KubernetesAuthMethod, - ListSecretsOptions, - SecretElement, - TokenAuthMethod, - UniversalAuthMethod, - UpdateSecretOptions, - UserPasswordAuthMethod, -) - -# Aliases for compatibility with infisical-python -InfisicalClient = KMSClient -AsyncInfisicalClient = AsyncKMSClient - -__all__ = [ - # Main clients - "KMSClient", - "AsyncKMSClient", - "InfisicalClient", # Alias for compatibility - "AsyncInfisicalClient", # Async alias - # Settings - "ClientSettings", - "AuthenticationOptions", - # Auth methods - "UniversalAuthMethod", - "AWSIamAuthMethod", - "AzureAuthMethod", - "GCPIamAuthMethod", - "GCPIDTokenAuthMethod", - "KubernetesAuthMethod", - # Options - "GetSecretOptions", - "ListSecretsOptions", - "CreateSecretOptions", - "UpdateSecretOptions", - "DeleteSecretOptions", - # Response types - "SecretElement", -] diff --git a/pkg/hanzo-kms/hanzo_kms/__main__.py b/pkg/hanzo-kms/hanzo_kms/__main__.py deleted file mode 100644 index 322faa399..000000000 --- a/pkg/hanzo-kms/hanzo_kms/__main__.py +++ /dev/null @@ -1,4 +0,0 @@ -"""Allow running as: python -m hanzo_kms""" -from .cli import main - -main() diff --git a/pkg/hanzo-kms/hanzo_kms/async_client.py b/pkg/hanzo-kms/hanzo_kms/async_client.py deleted file mode 100644 index 9eae645e7..000000000 --- a/pkg/hanzo-kms/hanzo_kms/async_client.py +++ /dev/null @@ -1,351 +0,0 @@ -""" -Hanzo KMS Async Client - Async Python implementation - -An async-first KMS client compatible with Infisical API. -""" - -import os -import time -from typing import Optional - -import httpx - -from .models import ( - AuthenticationOptions, - ClientSettings, - CreateSecretOptions, - DeleteSecretOptions, - GetSecretOptions, - ListSecretsOptions, - SecretElement, - SecretsResponse, - TokenResponse, - UpdateSecretOptions, -) - - -class AsyncKMSClient: - """ - Async Hanzo KMS Client for secret management. - - Example: - async with AsyncKMSClient(settings) as client: - secrets = await client.list_secrets("myproject", "production") - """ - - def __init__( - self, - settings: Optional[ClientSettings] = None, - debug: bool = False, - ): - self.settings = settings or self._settings_from_env() - self.debug = debug - self._access_token: Optional[str] = None - self._token_expires_at: float = 0 - self._http_client: Optional[httpx.AsyncClient] = None - - def _settings_from_env(self) -> ClientSettings: - """Create settings from environment variables.""" - from .models import UniversalAuthMethod - - site_url = os.getenv( - "HANZO_KMS_URL", os.getenv("INFISICAL_SITE_URL", "https://kms.hanzo.ai") - ) - organization = os.getenv("HANZO_KMS_ORG", "hanzo") - client_id = os.getenv( - "HANZO_KMS_CLIENT_ID", os.getenv("INFISICAL_CLIENT_ID", "") - ) - client_secret = os.getenv( - "HANZO_KMS_CLIENT_SECRET", os.getenv("INFISICAL_CLIENT_SECRET", "") - ) - - auth = None - if client_id and client_secret: - auth = AuthenticationOptions( - universal_auth=UniversalAuthMethod( - client_id=client_id, - client_secret=client_secret, - ) - ) - - return ClientSettings(site_url=site_url, organization=organization, auth=auth) - - @property - def http(self) -> httpx.AsyncClient: - """Get or create async HTTP client.""" - if self._http_client is None: - self._http_client = httpx.AsyncClient( - base_url=self.settings.site_url.rstrip("/"), - timeout=30.0, - headers={ - "User-Agent": self.settings.user_agent, - "Content-Type": "application/json", - }, - ) - return self._http_client - - async def _get_access_token(self) -> str: - """Get valid access token, refreshing if needed.""" - if self._access_token and time.time() < self._token_expires_at - 60: - return self._access_token - - auth = self.settings.auth - if not auth: - raise ValueError("No authentication configured") - - # Universal Auth - if auth.universal_auth: - response = await self.http.post( - "/api/v1/auth/universal-auth/login", - json={ - "clientId": auth.universal_auth.client_id, - "clientSecret": auth.universal_auth.client_secret, - }, - ) - response.raise_for_status() - data = response.json() - token_data = TokenResponse.model_validate(data) - self._access_token = token_data.access_token - self._token_expires_at = time.time() + token_data.expires_in - return self._access_token - - # Kubernetes Auth - if auth.kubernetes: - token_path = auth.kubernetes.service_account_token_path - if os.path.exists(token_path): - with open(token_path) as f: - k8s_token = f.read().strip() - - response = await self.http.post( - "/api/v1/auth/kubernetes-auth/login", - json={ - "identityId": auth.kubernetes.identity_id, - "jwt": k8s_token, - }, - ) - response.raise_for_status() - data = response.json() - token_data = TokenResponse.model_validate(data) - self._access_token = token_data.access_token - self._token_expires_at = time.time() + token_data.expires_in - return self._access_token - - raise ValueError("No valid authentication method configured") - - async def _auth_headers(self) -> dict[str, str]: - """Get authorization headers including organization context.""" - token = await self._get_access_token() - return { - "Authorization": f"Bearer {token}", - "X-Org-Name": self.settings.organization, - } - - async def get_secret( - self, - project_id: str, - environment: str, - secret_name: str, - path: str = "/", - **kwargs, - ) -> SecretElement: - """Get a single secret by name.""" - options = GetSecretOptions( - project_id=project_id, - environment=environment, - secret_name=secret_name, - path=path, - **kwargs, - ) - - response = await self.http.get( - f"/api/v3/secrets/raw/{options.secret_name}", - params={ - "workspaceId": options.project_id, - "environment": options.environment, - "secretPath": options.path, - "type": options.type, - }, - headers=await self._auth_headers(), - ) - response.raise_for_status() - data = response.json() - return SecretElement.model_validate(data.get("secret", data)) - - async def list_secrets( - self, - project_id: str, - environment: str, - path: str = "/", - attach_to_process_env: bool = False, - **kwargs, - ) -> list[SecretElement]: - """List all secrets in a project/environment.""" - options = ListSecretsOptions( - project_id=project_id, - environment=environment, - path=path, - attach_to_process_env=attach_to_process_env, - **kwargs, - ) - - response = await self.http.get( - "/api/v3/secrets/raw", - params={ - "workspaceId": options.project_id, - "environment": options.environment, - "secretPath": options.path, - "include_imports": str(options.include_imports).lower(), - "recursive": str(options.recursive).lower(), - "expandSecretReferences": str(options.expand_secret_references).lower(), - }, - headers=await self._auth_headers(), - ) - response.raise_for_status() - data = response.json() - secrets_data = SecretsResponse.model_validate(data) - - if options.attach_to_process_env: - for secret in secrets_data.secrets: - if secret.secret_key not in os.environ: - os.environ[secret.secret_key] = secret.secret_value - - return secrets_data.secrets - - async def create_secret( - self, - project_id: str, - environment: str, - secret_name: str, - secret_value: str, - **kwargs, - ) -> SecretElement: - """Create a new secret.""" - options = CreateSecretOptions( - project_id=project_id, - environment=environment, - secret_name=secret_name, - secret_value=secret_value, - **kwargs, - ) - - response = await self.http.post( - f"/api/v3/secrets/raw/{options.secret_name}", - json={ - "workspaceId": options.project_id, - "environment": options.environment, - "secretPath": options.path, - "secretValue": options.secret_value, - "secretComment": options.secret_comment, - "type": options.type, - }, - headers=await self._auth_headers(), - ) - response.raise_for_status() - data = response.json() - return SecretElement.model_validate(data.get("secret", data)) - - async def update_secret( - self, - project_id: str, - environment: str, - secret_name: str, - secret_value: str, - **kwargs, - ) -> SecretElement: - """Update an existing secret.""" - options = UpdateSecretOptions( - project_id=project_id, - environment=environment, - secret_name=secret_name, - secret_value=secret_value, - **kwargs, - ) - - response = await self.http.patch( - f"/api/v3/secrets/raw/{options.secret_name}", - json={ - "workspaceId": options.project_id, - "environment": options.environment, - "secretPath": options.path, - "secretValue": options.secret_value, - }, - headers=await self._auth_headers(), - ) - response.raise_for_status() - data = response.json() - return SecretElement.model_validate(data.get("secret", data)) - - async def delete_secret( - self, - project_id: str, - environment: str, - secret_name: str, - **kwargs, - ) -> SecretElement: - """Delete a secret.""" - options = DeleteSecretOptions( - project_id=project_id, - environment=environment, - secret_name=secret_name, - **kwargs, - ) - - response = await self.http.request( - "DELETE", - f"/api/v3/secrets/raw/{options.secret_name}", - json={ - "workspaceId": options.project_id, - "environment": options.environment, - "secretPath": options.path, - }, - headers=await self._auth_headers(), - ) - response.raise_for_status() - data = response.json() - return SecretElement.model_validate(data.get("secret", data)) - - async def inject_env( - self, - project_id: str, - environment: str, - path: str = "/", - overwrite: bool = False, - ) -> int: - """Inject all secrets into environment variables.""" - secrets = await self.list_secrets(project_id, environment, path) - count = 0 - for secret in secrets: - if overwrite or secret.secret_key not in os.environ: - os.environ[secret.secret_key] = secret.secret_value - count += 1 - return count - - async def get_value( - self, - project_id: str, - environment: str, - secret_name: str, - default: Optional[str] = None, - ) -> Optional[str]: - """Get just the value of a secret.""" - try: - secret = await self.get_secret(project_id, environment, secret_name) - return secret.secret_value - except httpx.HTTPStatusError: - return default - - async def close(self) -> None: - """Close the HTTP client.""" - if self._http_client: - await self._http_client.aclose() - self._http_client = None - - async def __aenter__(self) -> "AsyncKMSClient": - return self - - async def __aexit__(self, *args) -> None: - await self.close() - - -# Alias for compatibility -AsyncInfisicalClient = AsyncKMSClient diff --git a/pkg/hanzo-kms/hanzo_kms/cli.py b/pkg/hanzo-kms/hanzo_kms/cli.py deleted file mode 100644 index bc7b17e8f..000000000 --- a/pkg/hanzo-kms/hanzo_kms/cli.py +++ /dev/null @@ -1,149 +0,0 @@ -""" -Hanzo KMS CLI โ€” fetch secrets non-interactively. - -Usage: - # Via env vars - HANZO_KMS_EMAIL=z@hanzo.ai HANZO_KMS_PASSWORD=... python -m hanzo_kms get SECRET_NAME --project ID --env prod - - # Via flags - python -m hanzo_kms get SECRET_NAME --email z@hanzo.ai --password ... --project ID --env prod - - # List secrets - python -m hanzo_kms list --project ID --env prod - - # With universal auth - HANZO_KMS_CLIENT_ID=... HANZO_KMS_CLIENT_SECRET=... python -m hanzo_kms get SECRET_NAME --project ID --env prod - - # With pre-authenticated token - HANZO_KMS_TOKEN=... python -m hanzo_kms get SECRET_NAME --project ID --env prod -""" - -import argparse -import os -import sys - -from .client import KMSClient -from .models import ( - AuthenticationOptions, - ClientSettings, - TokenAuthMethod, - UniversalAuthMethod, - UserPasswordAuthMethod, -) - - -def _build_client(args: argparse.Namespace) -> KMSClient: - """Build KMS client from CLI args + env vars.""" - site_url = args.url or os.getenv("HANZO_KMS_URL", "https://kms.hanzo.ai") - - # Auth priority: token > universal-auth > user/password > env - token = args.token or os.getenv("HANZO_KMS_TOKEN", os.getenv("INFISICAL_TOKEN", "")) - client_id = args.client_id or os.getenv("HANZO_KMS_CLIENT_ID", "") - client_secret = args.client_secret or os.getenv("HANZO_KMS_CLIENT_SECRET", "") - email = args.email or os.getenv("HANZO_KMS_EMAIL", "") - password = args.password or os.getenv("HANZO_KMS_PASSWORD", "") - - auth = None - if token: - auth = AuthenticationOptions(token=TokenAuthMethod(access_token=token)) - elif client_id and client_secret: - auth = AuthenticationOptions( - universal_auth=UniversalAuthMethod(client_id=client_id, client_secret=client_secret) - ) - elif email and password: - auth = AuthenticationOptions( - user_password=UserPasswordAuthMethod(email=email, password=password) - ) - - if not auth: - # Try env fallback - client = KMSClient(debug=getattr(args, "debug", False)) - if client.settings.auth: - return client - print("Error: No auth configured. Set HANZO_KMS_TOKEN, HANZO_KMS_CLIENT_ID/SECRET, or HANZO_KMS_EMAIL/PASSWORD", file=sys.stderr) - sys.exit(1) - - settings = ClientSettings(site_url=site_url, auth=auth) - return KMSClient(settings=settings, debug=getattr(args, "debug", False)) - - -def main() -> None: - parser = argparse.ArgumentParser(prog="hanzo-kms", description="Hanzo KMS CLI") - parser.add_argument("--url", help="KMS URL (default: https://kms.hanzo.ai)") - parser.add_argument("--token", help="Pre-authenticated access token") - parser.add_argument("--client-id", help="Universal auth client ID") - parser.add_argument("--client-secret", help="Universal auth client secret") - parser.add_argument("--email", help="User email for SRP auth") - parser.add_argument("--password", help="User password for SRP auth") - parser.add_argument("--debug", action="store_true") - - sub = parser.add_subparsers(dest="command") - - # get command - get_p = sub.add_parser("get", help="Get a secret value") - get_p.add_argument("name", help="Secret name") - get_p.add_argument("--project", required=True, help="Project ID") - get_p.add_argument("--env", default="prod", help="Environment (default: prod)") - get_p.add_argument("--path", default="/", help="Secret path") - - # list command - list_p = sub.add_parser("list", help="List secrets") - list_p.add_argument("--project", required=True, help="Project ID") - list_p.add_argument("--env", default="prod", help="Environment (default: prod)") - list_p.add_argument("--path", default="/", help="Secret path") - list_p.add_argument("--keys-only", action="store_true", help="Only print keys") - - # export command - export_p = sub.add_parser("export", help="Export secrets as env vars") - export_p.add_argument("--project", required=True, help="Project ID") - export_p.add_argument("--env", default="prod", help="Environment (default: prod)") - export_p.add_argument("--path", default="/", help="Secret path") - export_p.add_argument("--format", choices=["env", "json"], default="env") - - args = parser.parse_args() - if not args.command: - parser.print_help() - sys.exit(1) - - client = _build_client(args) - - if args.command == "get": - secret = client.get_secret( - project_id=args.project, - environment=args.env, - secret_name=args.name, - path=args.path, - ) - print(secret.secret_value) - - elif args.command == "list": - secrets = client.list_secrets( - project_id=args.project, - environment=args.env, - path=args.path, - ) - for s in secrets: - if args.keys_only: - print(s.secret_key) - else: - print(f"{s.secret_key}={s.secret_value}") - - elif args.command == "export": - import json as _json - - secrets = client.list_secrets( - project_id=args.project, - environment=args.env, - path=args.path, - ) - if args.format == "json": - print(_json.dumps({s.secret_key: s.secret_value for s in secrets}, indent=2)) - else: - for s in secrets: - print(f"{s.secret_key}={s.secret_value}") - - client.close() - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-kms/hanzo_kms/client.py b/pkg/hanzo-kms/hanzo_kms/client.py deleted file mode 100644 index 10bbfaf76..000000000 --- a/pkg/hanzo-kms/hanzo_kms/client.py +++ /dev/null @@ -1,546 +0,0 @@ -""" -Hanzo KMS Client - Pure Python implementation - -A full-featured KMS client compatible with Infisical API. -""" - -import os -import time -from typing import Optional - -import httpx - -from .models import ( - AuthenticationOptions, - ClientSettings, - CreateSecretOptions, - DeleteSecretOptions, - GetSecretOptions, - ListSecretsOptions, - SecretElement, - SecretsResponse, - TokenResponse, - UpdateSecretOptions, -) - - -class KMSClient: - """ - Hanzo KMS Client for secret management. - - Supports multiple authentication methods: - - Universal Auth (client_id/client_secret) - - AWS IAM - - Azure AD - - GCP IAM - - Kubernetes Service Account - - Example: - client = KMSClient(ClientSettings( - site_url="https://kms.hanzo.ai", - auth=AuthenticationOptions( - universal_auth=UniversalAuthMethod( - client_id="your-client-id", - client_secret="your-client-secret", - ) - ) - )) - - secrets = client.list_secrets(project_id="myproject", environment="production") - """ - - def __init__( - self, - settings: Optional[ClientSettings] = None, - debug: bool = False, - ): - """Initialize KMS client. - - Args: - settings: Client configuration (defaults from environment if not provided) - debug: Enable debug logging - """ - self.settings = settings or self._settings_from_env() - self.debug = debug - self._access_token: Optional[str] = None - self._token_expires_at: float = 0 - self._http_client: Optional[httpx.Client] = None - - def _settings_from_env(self) -> ClientSettings: - """Create settings from environment variables.""" - from .models import TokenAuthMethod, UniversalAuthMethod, UserPasswordAuthMethod - - site_url = os.getenv( - "HANZO_KMS_URL", os.getenv("INFISICAL_SITE_URL", "https://kms.hanzo.ai") - ) - organization = os.getenv("HANZO_KMS_ORG", "hanzo") - client_id = os.getenv( - "HANZO_KMS_CLIENT_ID", os.getenv("INFISICAL_CLIENT_ID", "") - ) - client_secret = os.getenv( - "HANZO_KMS_CLIENT_SECRET", os.getenv("INFISICAL_CLIENT_SECRET", "") - ) - email = os.getenv("HANZO_KMS_EMAIL", "") - password = os.getenv("HANZO_KMS_PASSWORD", "") - token = os.getenv("HANZO_KMS_TOKEN", os.getenv("INFISICAL_TOKEN", "")) - - auth = None - if token: - auth = AuthenticationOptions( - token=TokenAuthMethod(access_token=token) - ) - elif client_id and client_secret: - auth = AuthenticationOptions( - universal_auth=UniversalAuthMethod( - client_id=client_id, - client_secret=client_secret, - ) - ) - elif email and password: - auth = AuthenticationOptions( - user_password=UserPasswordAuthMethod( - email=email, - password=password, - ) - ) - - return ClientSettings(site_url=site_url, organization=organization, auth=auth) - - @property - def http(self) -> httpx.Client: - """Get or create HTTP client.""" - if self._http_client is None: - self._http_client = httpx.Client( - base_url=self.settings.site_url.rstrip("/"), - timeout=30.0, - headers={ - "User-Agent": self.settings.user_agent, - "Content-Type": "application/json", - }, - ) - return self._http_client - - def _user_login(self, email: str, password: str) -> str: - """Login with email/password via v3 non-SRP endpoint.""" - response = self.http.post( - "/api/v3/auth/login", - json={"email": email, "password": password}, - ) - response.raise_for_status() - data = response.json() - return data.get("accessToken", data.get("access_token", "")) - - def _get_access_token(self) -> str: - """Get valid access token, refreshing if needed.""" - if self._access_token and time.time() < self._token_expires_at - 60: - return self._access_token - - auth = self.settings.auth - if not auth: - raise ValueError("No authentication configured") - - # Direct token auth - if auth.token: - self._access_token = auth.token.access_token - self._token_expires_at = time.time() + 86400 # assume 24h - return self._access_token - - # User/password auth (v3 non-SRP) - if auth.user_password: - token = self._user_login(auth.user_password.email, auth.user_password.password) - self._access_token = token - self._token_expires_at = time.time() + 7200 # 2h - return self._access_token - - # Universal Auth - if auth.universal_auth: - response = self.http.post( - "/api/v1/auth/universal-auth/login", - json={ - "clientId": auth.universal_auth.client_id, - "clientSecret": auth.universal_auth.client_secret, - }, - ) - response.raise_for_status() - data = response.json() - token_data = TokenResponse.model_validate(data) - self._access_token = token_data.access_token - self._token_expires_at = time.time() + token_data.expires_in - return self._access_token - - # Kubernetes Auth - if auth.kubernetes: - token_path = auth.kubernetes.service_account_token_path - if os.path.exists(token_path): - with open(token_path) as f: - k8s_token = f.read().strip() - - response = self.http.post( - "/api/v1/auth/kubernetes-auth/login", - json={ - "identityId": auth.kubernetes.identity_id, - "jwt": k8s_token, - }, - ) - response.raise_for_status() - data = response.json() - token_data = TokenResponse.model_validate(data) - self._access_token = token_data.access_token - self._token_expires_at = time.time() + token_data.expires_in - return self._access_token - - # AWS IAM Auth - if auth.aws_iam: - # Get AWS credentials from environment/instance metadata - import json - - try: - import boto3 - - session = boto3.Session() - credentials = session.get_credentials() - _region = session.region_name or "us-east-1" - - response = self.http.post( - "/api/v1/auth/aws-auth/login", - json={ - "identityId": auth.aws_iam.identity_id, - "iamHttpRequestMethod": "POST", - "iamRequestBody": "", - "iamRequestHeaders": json.dumps( - { - "X-Amz-Date": credentials.token or "", - } - ), - }, - ) - response.raise_for_status() - data = response.json() - token_data = TokenResponse.model_validate(data) - self._access_token = token_data.access_token - self._token_expires_at = time.time() + token_data.expires_in - return self._access_token - except ImportError as e: - raise ValueError( - "boto3 required for AWS IAM auth: pip install boto3" - ) from e - - raise ValueError("No valid authentication method configured") - - def _auth_headers(self) -> dict[str, str]: - """Get authorization headers including organization context.""" - token = self._get_access_token() - return { - "Authorization": f"Bearer {token}", - "X-Org-Name": self.settings.organization, - } - - def _resolve_project_id(self, project_id: str) -> tuple[str, str]: - """Resolve org-scoped project ID. - - Supports formats: - - "project" -> uses settings.organization - - "org/project" -> uses specified org - - Returns: - Tuple of (organization, project_id) - """ - if "/" in project_id: - org, proj = project_id.split("/", 1) - return (org, proj) - return (self.settings.organization, project_id) - - # ========================================================================= - # Secret Operations - # ========================================================================= - - def get_secret( - self, - project_id: str, - environment: str, - secret_name: str, - path: str = "/", - **kwargs, - ) -> SecretElement: - """Get a single secret by name. - - Args: - project_id: Project ID or slug - environment: Environment slug (e.g., "production") - secret_name: Name of the secret - path: Secret path (default "/") - - Returns: - SecretElement with the secret data - """ - options = GetSecretOptions( - project_id=project_id, - environment=environment, - secret_name=secret_name, - path=path, - **kwargs, - ) - - response = self.http.get( - f"/api/v3/secrets/raw/{options.secret_name}", - params={ - "workspaceId": options.project_id, - "environment": options.environment, - "secretPath": options.path, - "type": options.type, - "include_imports": str(options.include_imports).lower(), - }, - headers=self._auth_headers(), - ) - response.raise_for_status() - data = response.json() - return SecretElement.model_validate(data.get("secret", data)) - - def list_secrets( - self, - project_id: str, - environment: str, - path: str = "/", - attach_to_process_env: bool = False, - **kwargs, - ) -> list[SecretElement]: - """List all secrets in a project/environment. - - Args: - project_id: Project ID or slug - environment: Environment slug - path: Secret path (default "/") - attach_to_process_env: If True, set secrets as environment variables - - Returns: - List of SecretElement - """ - options = ListSecretsOptions( - project_id=project_id, - environment=environment, - path=path, - attach_to_process_env=attach_to_process_env, - **kwargs, - ) - - response = self.http.get( - "/api/v3/secrets/raw", - params={ - "workspaceId": options.project_id, - "environment": options.environment, - "secretPath": options.path, - "include_imports": str(options.include_imports).lower(), - "recursive": str(options.recursive).lower(), - "expandSecretReferences": str(options.expand_secret_references).lower(), - }, - headers=self._auth_headers(), - ) - response.raise_for_status() - data = response.json() - secrets_data = SecretsResponse.model_validate(data) - - # Optionally inject into environment - if options.attach_to_process_env: - for secret in secrets_data.secrets: - if secret.secret_key not in os.environ: - os.environ[secret.secret_key] = secret.secret_value - - return secrets_data.secrets - - def create_secret( - self, - project_id: str, - environment: str, - secret_name: str, - secret_value: str, - **kwargs, - ) -> SecretElement: - """Create a new secret. - - Args: - project_id: Project ID or slug - environment: Environment slug - secret_name: Name of the secret - secret_value: Value of the secret - - Returns: - Created SecretElement - """ - options = CreateSecretOptions( - project_id=project_id, - environment=environment, - secret_name=secret_name, - secret_value=secret_value, - **kwargs, - ) - - payload: dict = { - "workspaceId": options.project_id, - "environment": options.environment, - "secretPath": options.path, - "secretValue": options.secret_value, - "type": options.type, - } - if options.secret_comment is not None: - payload["secretComment"] = options.secret_comment - - response = self.http.post( - f"/api/v3/secrets/raw/{options.secret_name}", - json=payload, - headers=self._auth_headers(), - ) - response.raise_for_status() - data = response.json() - return SecretElement.model_validate(data.get("secret", data)) - - def update_secret( - self, - project_id: str, - environment: str, - secret_name: str, - secret_value: str, - **kwargs, - ) -> SecretElement: - """Update an existing secret. - - Args: - project_id: Project ID or slug - environment: Environment slug - secret_name: Name of the secret - secret_value: New value - - Returns: - Updated SecretElement - """ - options = UpdateSecretOptions( - project_id=project_id, - environment=environment, - secret_name=secret_name, - secret_value=secret_value, - **kwargs, - ) - - response = self.http.patch( - f"/api/v3/secrets/raw/{options.secret_name}", - json={ - "workspaceId": options.project_id, - "environment": options.environment, - "secretPath": options.path, - "secretValue": options.secret_value, - "secretComment": options.secret_comment, - "type": options.type, - }, - headers=self._auth_headers(), - ) - response.raise_for_status() - data = response.json() - return SecretElement.model_validate(data.get("secret", data)) - - def delete_secret( - self, - project_id: str, - environment: str, - secret_name: str, - **kwargs, - ) -> SecretElement: - """Delete a secret. - - Args: - project_id: Project ID or slug - environment: Environment slug - secret_name: Name of the secret - - Returns: - Deleted SecretElement - """ - options = DeleteSecretOptions( - project_id=project_id, - environment=environment, - secret_name=secret_name, - **kwargs, - ) - - response = self.http.request( - "DELETE", - f"/api/v3/secrets/raw/{options.secret_name}", - json={ - "workspaceId": options.project_id, - "environment": options.environment, - "secretPath": options.path, - "type": options.type, - }, - headers=self._auth_headers(), - ) - response.raise_for_status() - data = response.json() - return SecretElement.model_validate(data.get("secret", data)) - - # ========================================================================= - # Convenience Methods - # ========================================================================= - - def inject_env( - self, - project_id: str, - environment: str, - path: str = "/", - overwrite: bool = False, - ) -> int: - """Inject all secrets into environment variables. - - Args: - project_id: Project ID or slug - environment: Environment slug - path: Secret path - overwrite: If True, overwrite existing env vars - - Returns: - Number of secrets injected - """ - secrets = self.list_secrets(project_id, environment, path) - count = 0 - for secret in secrets: - if overwrite or secret.secret_key not in os.environ: - os.environ[secret.secret_key] = secret.secret_value - count += 1 - return count - - def get_value( - self, - project_id: str, - environment: str, - secret_name: str, - default: Optional[str] = None, - ) -> Optional[str]: - """Get just the value of a secret. - - Args: - project_id: Project ID or slug - environment: Environment slug - secret_name: Name of the secret - default: Default value if secret not found - - Returns: - Secret value or default - """ - try: - secret = self.get_secret(project_id, environment, secret_name) - return secret.secret_value - except httpx.HTTPStatusError: - return default - - def close(self) -> None: - """Close the HTTP client.""" - if self._http_client: - self._http_client.close() - self._http_client = None - - def __enter__(self) -> "KMSClient": - return self - - def __exit__(self, *args) -> None: - self.close() - - -# Alias for compatibility -InfisicalClient = KMSClient diff --git a/pkg/hanzo-kms/hanzo_kms/models.py b/pkg/hanzo-kms/hanzo_kms/models.py deleted file mode 100644 index bb6a0d20b..000000000 --- a/pkg/hanzo-kms/hanzo_kms/models.py +++ /dev/null @@ -1,223 +0,0 @@ -""" -Hanzo KMS - Data models for the SDK - -Pydantic models for API requests and responses. -Compatible with Infisical API schema. -""" - -from datetime import datetime -from typing import Any, Optional - -from pydantic import BaseModel, Field - -# ============================================================================= -# Authentication Models -# ============================================================================= - - -class UniversalAuthMethod(BaseModel): - """Universal authentication using client credentials.""" - - client_id: str = Field(..., description="Client ID from KMS") - client_secret: str = Field(..., description="Client secret from KMS") - - -class AWSIamAuthMethod(BaseModel): - """AWS IAM authentication.""" - - identity_id: str = Field(..., description="Identity ID in KMS") - - -class AzureAuthMethod(BaseModel): - """Azure AD authentication.""" - - identity_id: str = Field(..., description="Identity ID in KMS") - resource: Optional[str] = Field(None, description="Azure resource") - - -class GCPIamAuthMethod(BaseModel): - """GCP IAM authentication.""" - - identity_id: str = Field(..., description="Identity ID in KMS") - service_account_key_file_path: str = Field( - ..., description="Path to service account key" - ) - - -class GCPIDTokenAuthMethod(BaseModel): - """GCP ID Token authentication.""" - - identity_id: str = Field(..., description="Identity ID in KMS") - - -class KubernetesAuthMethod(BaseModel): - """Kubernetes service account authentication.""" - - identity_id: str = Field(..., description="Identity ID in KMS") - service_account_token_path: str = Field( - "/var/run/secrets/kubernetes.io/serviceaccount/token", - description="Path to service account token", - ) - - -class UserPasswordAuthMethod(BaseModel): - """User email/password authentication (SRP).""" - - email: str = Field(..., description="User email") - password: str = Field(..., description="User password") - - -class TokenAuthMethod(BaseModel): - """Direct token authentication (pre-authenticated).""" - - access_token: str = Field(..., description="Pre-authenticated access token") - - -class AuthenticationOptions(BaseModel): - """Authentication configuration - use one method.""" - - universal_auth: Optional[UniversalAuthMethod] = None - user_password: Optional[UserPasswordAuthMethod] = None - token: Optional[TokenAuthMethod] = None - aws_iam: Optional[AWSIamAuthMethod] = None - azure: Optional[AzureAuthMethod] = None - gcp_iam: Optional[GCPIamAuthMethod] = None - gcp_id_token: Optional[GCPIDTokenAuthMethod] = None - kubernetes: Optional[KubernetesAuthMethod] = None - - -# ============================================================================= -# Client Settings -# ============================================================================= - - -class ClientSettings(BaseModel): - """Client configuration settings.""" - - site_url: str = Field("https://kms.hanzo.ai", description="KMS API URL") - organization: str = Field( - "hanzo", description="Organization name for multi-tenancy" - ) - auth: Optional[AuthenticationOptions] = Field( - None, description="Authentication options" - ) - user_agent: str = Field("hanzo-kms-python", description="User agent string") - cache_ttl: int = Field(300, description="Cache TTL in seconds") - - -# ============================================================================= -# Secret Models -# ============================================================================= - - -class SecretElement(BaseModel): - """A secret from KMS.""" - - id: str = Field(..., description="Secret ID") - secret_key: str = Field(..., alias="secretKey", description="Secret key/name") - secret_value: str = Field(..., alias="secretValue", description="Secret value") - secret_comment: Optional[str] = Field( - None, alias="secretComment", description="Comment" - ) - version: int = Field(1, description="Secret version") - type: str = Field("shared", description="Secret type") - environment: str = Field(..., description="Environment slug") - workspace: str = Field(..., description="Workspace/project ID") - created_at: Optional[datetime] = Field(None, alias="createdAt") - updated_at: Optional[datetime] = Field(None, alias="updatedAt") - - class Config: - populate_by_name = True - - -# ============================================================================= -# Request Options -# ============================================================================= - - -class GetSecretOptions(BaseModel): - """Options for getting a single secret.""" - - project_id: str = Field(..., description="Project ID or slug") - environment: str = Field(..., description="Environment slug") - secret_name: str = Field(..., description="Secret key/name") - path: str = Field("/", description="Secret path") - type: str = Field("shared", description="Secret type") - include_imports: bool = Field(True, description="Include imported secrets") - - -class ListSecretsOptions(BaseModel): - """Options for listing secrets.""" - - project_id: str = Field(..., description="Project ID or slug") - environment: str = Field(..., description="Environment slug") - path: str = Field("/", description="Secret path") - include_imports: bool = Field(True, description="Include imported secrets") - recursive: bool = Field(False, description="Recursively fetch from subpaths") - expand_secret_references: bool = Field(True, description="Expand ${} references") - attach_to_process_env: bool = Field( - False, description="Set as environment variables" - ) - - -class CreateSecretOptions(BaseModel): - """Options for creating a secret.""" - - project_id: str = Field(..., description="Project ID or slug") - environment: str = Field(..., description="Environment slug") - secret_name: str = Field(..., description="Secret key/name") - secret_value: str = Field(..., description="Secret value") - secret_comment: Optional[str] = Field(None, description="Comment") - path: str = Field("/", description="Secret path") - type: str = Field("shared", description="Secret type") - - -class UpdateSecretOptions(BaseModel): - """Options for updating a secret.""" - - project_id: str = Field(..., description="Project ID or slug") - environment: str = Field(..., description="Environment slug") - secret_name: str = Field(..., description="Secret key/name") - secret_value: str = Field(..., description="New secret value") - secret_comment: Optional[str] = Field(None, description="Comment") - path: str = Field("/", description="Secret path") - type: str = Field("shared", description="Secret type") - - -class DeleteSecretOptions(BaseModel): - """Options for deleting a secret.""" - - project_id: str = Field(..., description="Project ID or slug") - environment: str = Field(..., description="Environment slug") - secret_name: str = Field(..., description="Secret key/name") - path: str = Field("/", description="Secret path") - type: str = Field("shared", description="Secret type") - - -# ============================================================================= -# Response Models -# ============================================================================= - - -class TokenResponse(BaseModel): - """Access token response from authentication.""" - - access_token: str = Field(..., alias="accessToken") - expires_in: int = Field(..., alias="expiresIn") - token_type: str = Field("Bearer", alias="tokenType") - - class Config: - populate_by_name = True - - -class SecretsResponse(BaseModel): - """Response containing list of secrets.""" - - secrets: list[SecretElement] - imports: Optional[list[Any]] = None - - -class SecretResponse(BaseModel): - """Response containing a single secret.""" - - secret: SecretElement diff --git a/pkg/hanzo-kms/hanzo_kms/py.typed b/pkg/hanzo-kms/hanzo_kms/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-kms/pyproject.toml b/pkg/hanzo-kms/pyproject.toml deleted file mode 100644 index 3d8a7f701..000000000 --- a/pkg/hanzo-kms/pyproject.toml +++ /dev/null @@ -1,64 +0,0 @@ -[project] -name = "hanzo-kms" -version = "1.1.0" -description = "Hanzo KMS SDK - Secret management for Python" -readme = "README.md" -license = { text = "MIT" } -requires-python = ">=3.12" -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["secrets", "kms", "hanzo", "lux", "security", "vault"] -classifiers = [ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Topic :: Security", - "Topic :: Software Development :: Libraries :: Python Modules", - "Typing :: Typed", -] - -dependencies = [ - "httpx>=0.25.0", - "pydantic>=2.0.0", -] - -[project.optional-dependencies] -async = ["httpx[http2]>=0.25.0"] -dev = [ - "pytest>=8.0.0", - "pytest-asyncio>=0.24.0", - "ruff>=0.5.0", - "mypy>=1.10.0", -] - -[project.scripts] -hanzo-kms = "hanzo_kms.cli:main" - -[project.urls] -Homepage = "https://kms.hanzo.ai" -Documentation = "https://docs.hanzo.ai/kms" -Repository = "https://github.com/hanzoai/python-sdk" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_kms"] - -[tool.ruff] -target-version = "py39" -line-length = 100 - -[tool.ruff.lint] -select = ["E", "W", "F", "I", "B", "C4", "UP"] -ignore = ["E501"] - -[tool.mypy] -python_version = "3.9" -strict = true diff --git a/pkg/hanzo-kms/uv.lock b/pkg/hanzo-kms/uv.lock deleted file mode 100644 index 2c2668d84..000000000 --- a/pkg/hanzo-kms/uv.lock +++ /dev/null @@ -1,465 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" - -[[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.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, -] - -[[package]] -name = "certifi" -version = "2026.2.25" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, -] - -[[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 = "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 = "h2" -version = "4.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hpack" }, - { name = "hyperframe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026, upload-time = "2025-08-23T18:12:19.778Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779, upload-time = "2025-08-23T18:12:17.779Z" }, -] - -[[package]] -name = "hanzo-kms" -version = "1.1.0" -source = { editable = "." } -dependencies = [ - { name = "httpx" }, - { name = "pydantic" }, -] - -[package.optional-dependencies] -async = [ - { name = "httpx", extra = ["http2"] }, -] -dev = [ - { name = "mypy" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "httpx", specifier = ">=0.25.0" }, - { name = "httpx", extras = ["http2"], marker = "extra == 'async'", specifier = ">=0.25.0" }, - { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.10.0" }, - { name = "pydantic", specifier = ">=2.0.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, - { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24.0" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.5.0" }, -] -provides-extras = ["async", "dev"] - -[[package]] -name = "hpack" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276, upload-time = "2025-01-22T21:44:58.347Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357, upload-time = "2025-01-22T21:44:56.92Z" }, -] - -[[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.optional-dependencies] -http2 = [ - { name = "h2" }, -] - -[[package]] -name = "hyperframe" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566, upload-time = "2025-01-22T21:41:49.302Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "librt" -version = "0.8.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/56/9c/b4b0c54d84da4a94b37bd44151e46d5e583c9534c7e02250b961b1b6d8a8/librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73", size = 177471, upload-time = "2026-02-17T16:13:06.101Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/21/d39b0a87ac52fc98f621fb6f8060efb017a767ebbbac2f99fbcbc9ddc0d7/librt-0.8.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a", size = 66516, upload-time = "2026-02-17T16:11:41.604Z" }, - { url = "https://files.pythonhosted.org/packages/69/f1/46375e71441c43e8ae335905e069f1c54febee63a146278bcee8782c84fd/librt-0.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9", size = 68634, upload-time = "2026-02-17T16:11:43.268Z" }, - { url = "https://files.pythonhosted.org/packages/0a/33/c510de7f93bf1fa19e13423a606d8189a02624a800710f6e6a0a0f0784b3/librt-0.8.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb", size = 198941, upload-time = "2026-02-17T16:11:44.28Z" }, - { url = "https://files.pythonhosted.org/packages/dd/36/e725903416409a533d92398e88ce665476f275081d0d7d42f9c4951999e5/librt-0.8.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d", size = 209991, upload-time = "2026-02-17T16:11:45.462Z" }, - { url = "https://files.pythonhosted.org/packages/30/7a/8d908a152e1875c9f8eac96c97a480df425e657cdb47854b9efaa4998889/librt-0.8.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7", size = 224476, upload-time = "2026-02-17T16:11:46.542Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b8/a22c34f2c485b8903a06f3fe3315341fe6876ef3599792344669db98fcff/librt-0.8.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440", size = 217518, upload-time = "2026-02-17T16:11:47.746Z" }, - { url = "https://files.pythonhosted.org/packages/79/6f/5c6fea00357e4f82ba44f81dbfb027921f1ab10e320d4a64e1c408d035d9/librt-0.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9", size = 225116, upload-time = "2026-02-17T16:11:49.298Z" }, - { url = "https://files.pythonhosted.org/packages/f2/a0/95ced4e7b1267fe1e2720a111685bcddf0e781f7e9e0ce59d751c44dcfe5/librt-0.8.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972", size = 217751, upload-time = "2026-02-17T16:11:50.49Z" }, - { url = "https://files.pythonhosted.org/packages/93/c2/0517281cb4d4101c27ab59472924e67f55e375bc46bedae94ac6dc6e1902/librt-0.8.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921", size = 218378, upload-time = "2026-02-17T16:11:51.783Z" }, - { url = "https://files.pythonhosted.org/packages/43/e8/37b3ac108e8976888e559a7b227d0ceac03c384cfd3e7a1c2ee248dbae79/librt-0.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0", size = 241199, upload-time = "2026-02-17T16:11:53.561Z" }, - { url = "https://files.pythonhosted.org/packages/4b/5b/35812d041c53967fedf551a39399271bbe4257e681236a2cf1a69c8e7fa1/librt-0.8.1-cp312-cp312-win32.whl", hash = "sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a", size = 54917, upload-time = "2026-02-17T16:11:54.758Z" }, - { url = "https://files.pythonhosted.org/packages/de/d1/fa5d5331b862b9775aaf2a100f5ef86854e5d4407f71bddf102f4421e034/librt-0.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444", size = 62017, upload-time = "2026-02-17T16:11:55.748Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7c/c614252f9acda59b01a66e2ddfd243ed1c7e1deab0293332dfbccf862808/librt-0.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d", size = 52441, upload-time = "2026-02-17T16:11:56.801Z" }, - { url = "https://files.pythonhosted.org/packages/c5/3c/f614c8e4eaac7cbf2bbdf9528790b21d89e277ee20d57dc6e559c626105f/librt-0.8.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35", size = 66529, upload-time = "2026-02-17T16:11:57.809Z" }, - { url = "https://files.pythonhosted.org/packages/ab/96/5836544a45100ae411eda07d29e3d99448e5258b6e9c8059deb92945f5c2/librt-0.8.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583", size = 68669, upload-time = "2026-02-17T16:11:58.843Z" }, - { url = "https://files.pythonhosted.org/packages/06/53/f0b992b57af6d5531bf4677d75c44f095f2366a1741fb695ee462ae04b05/librt-0.8.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c", size = 199279, upload-time = "2026-02-17T16:11:59.862Z" }, - { url = "https://files.pythonhosted.org/packages/f3/ad/4848cc16e268d14280d8168aee4f31cea92bbd2b79ce33d3e166f2b4e4fc/librt-0.8.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04", size = 210288, upload-time = "2026-02-17T16:12:00.954Z" }, - { url = "https://files.pythonhosted.org/packages/52/05/27fdc2e95de26273d83b96742d8d3b7345f2ea2bdbd2405cc504644f2096/librt-0.8.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363", size = 224809, upload-time = "2026-02-17T16:12:02.108Z" }, - { url = "https://files.pythonhosted.org/packages/7a/d0/78200a45ba3240cb042bc597d6f2accba9193a2c57d0356268cbbe2d0925/librt-0.8.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0", size = 218075, upload-time = "2026-02-17T16:12:03.631Z" }, - { url = "https://files.pythonhosted.org/packages/af/72/a210839fa74c90474897124c064ffca07f8d4b347b6574d309686aae7ca6/librt-0.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012", size = 225486, upload-time = "2026-02-17T16:12:04.725Z" }, - { url = "https://files.pythonhosted.org/packages/a3/c1/a03cc63722339ddbf087485f253493e2b013039f5b707e8e6016141130fa/librt-0.8.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb", size = 218219, upload-time = "2026-02-17T16:12:05.828Z" }, - { url = "https://files.pythonhosted.org/packages/58/f5/fff6108af0acf941c6f274a946aea0e484bd10cd2dc37610287ce49388c5/librt-0.8.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b", size = 218750, upload-time = "2026-02-17T16:12:07.09Z" }, - { url = "https://files.pythonhosted.org/packages/71/67/5a387bfef30ec1e4b4f30562c8586566faf87e47d696768c19feb49e3646/librt-0.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d", size = 241624, upload-time = "2026-02-17T16:12:08.43Z" }, - { url = "https://files.pythonhosted.org/packages/d4/be/24f8502db11d405232ac1162eb98069ca49c3306c1d75c6ccc61d9af8789/librt-0.8.1-cp313-cp313-win32.whl", hash = "sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a", size = 54969, upload-time = "2026-02-17T16:12:09.633Z" }, - { url = "https://files.pythonhosted.org/packages/5c/73/c9fdf6cb2a529c1a092ce769a12d88c8cca991194dfe641b6af12fa964d2/librt-0.8.1-cp313-cp313-win_amd64.whl", hash = "sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79", size = 62000, upload-time = "2026-02-17T16:12:10.632Z" }, - { url = "https://files.pythonhosted.org/packages/d3/97/68f80ca3ac4924f250cdfa6e20142a803e5e50fca96ef5148c52ee8c10ea/librt-0.8.1-cp313-cp313-win_arm64.whl", hash = "sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0", size = 52495, upload-time = "2026-02-17T16:12:11.633Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6a/907ef6800f7bca71b525a05f1839b21f708c09043b1c6aa77b6b827b3996/librt-0.8.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f", size = 66081, upload-time = "2026-02-17T16:12:12.766Z" }, - { url = "https://files.pythonhosted.org/packages/1b/18/25e991cd5640c9fb0f8d91b18797b29066b792f17bf8493da183bf5caabe/librt-0.8.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c", size = 68309, upload-time = "2026-02-17T16:12:13.756Z" }, - { url = "https://files.pythonhosted.org/packages/a4/36/46820d03f058cfb5a9de5940640ba03165ed8aded69e0733c417bb04df34/librt-0.8.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc", size = 196804, upload-time = "2026-02-17T16:12:14.818Z" }, - { url = "https://files.pythonhosted.org/packages/59/18/5dd0d3b87b8ff9c061849fbdb347758d1f724b9a82241aa908e0ec54ccd0/librt-0.8.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c", size = 206907, upload-time = "2026-02-17T16:12:16.513Z" }, - { url = "https://files.pythonhosted.org/packages/d1/96/ef04902aad1424fd7299b62d1890e803e6ab4018c3044dca5922319c4b97/librt-0.8.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3", size = 221217, upload-time = "2026-02-17T16:12:17.906Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ff/7e01f2dda84a8f5d280637a2e5827210a8acca9a567a54507ef1c75b342d/librt-0.8.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14", size = 214622, upload-time = "2026-02-17T16:12:19.108Z" }, - { url = "https://files.pythonhosted.org/packages/1e/8c/5b093d08a13946034fed57619742f790faf77058558b14ca36a6e331161e/librt-0.8.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7", size = 221987, upload-time = "2026-02-17T16:12:20.331Z" }, - { url = "https://files.pythonhosted.org/packages/d3/cc/86b0b3b151d40920ad45a94ce0171dec1aebba8a9d72bb3fa00c73ab25dd/librt-0.8.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6", size = 215132, upload-time = "2026-02-17T16:12:21.54Z" }, - { url = "https://files.pythonhosted.org/packages/fc/be/8588164a46edf1e69858d952654e216a9a91174688eeefb9efbb38a9c799/librt-0.8.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071", size = 215195, upload-time = "2026-02-17T16:12:23.073Z" }, - { url = "https://files.pythonhosted.org/packages/f5/f2/0b9279bea735c734d69344ecfe056c1ba211694a72df10f568745c899c76/librt-0.8.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78", size = 237946, upload-time = "2026-02-17T16:12:24.275Z" }, - { url = "https://files.pythonhosted.org/packages/e9/cc/5f2a34fbc8aeb35314a3641f9956fa9051a947424652fad9882be7a97949/librt-0.8.1-cp314-cp314-win32.whl", hash = "sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023", size = 50689, upload-time = "2026-02-17T16:12:25.766Z" }, - { url = "https://files.pythonhosted.org/packages/a0/76/cd4d010ab2147339ca2b93e959c3686e964edc6de66ddacc935c325883d7/librt-0.8.1-cp314-cp314-win_amd64.whl", hash = "sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730", size = 57875, upload-time = "2026-02-17T16:12:27.465Z" }, - { url = "https://files.pythonhosted.org/packages/84/0f/2143cb3c3ca48bd3379dcd11817163ca50781927c4537345d608b5045998/librt-0.8.1-cp314-cp314-win_arm64.whl", hash = "sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3", size = 48058, upload-time = "2026-02-17T16:12:28.556Z" }, - { url = "https://files.pythonhosted.org/packages/d2/0e/9b23a87e37baf00311c3efe6b48d6b6c168c29902dfc3f04c338372fd7db/librt-0.8.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1", size = 68313, upload-time = "2026-02-17T16:12:29.659Z" }, - { url = "https://files.pythonhosted.org/packages/db/9a/859c41e5a4f1c84200a7d2b92f586aa27133c8243b6cac9926f6e54d01b9/librt-0.8.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee", size = 70994, upload-time = "2026-02-17T16:12:31.516Z" }, - { url = "https://files.pythonhosted.org/packages/4c/28/10605366ee599ed34223ac2bf66404c6fb59399f47108215d16d5ad751a8/librt-0.8.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7", size = 220770, upload-time = "2026-02-17T16:12:33.294Z" }, - { url = "https://files.pythonhosted.org/packages/af/8d/16ed8fd452dafae9c48d17a6bc1ee3e818fd40ef718d149a8eff2c9f4ea2/librt-0.8.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040", size = 235409, upload-time = "2026-02-17T16:12:35.443Z" }, - { url = "https://files.pythonhosted.org/packages/89/1b/7bdf3e49349c134b25db816e4a3db6b94a47ac69d7d46b1e682c2c4949be/librt-0.8.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e", size = 246473, upload-time = "2026-02-17T16:12:36.656Z" }, - { url = "https://files.pythonhosted.org/packages/4e/8a/91fab8e4fd2a24930a17188c7af5380eb27b203d72101c9cc000dbdfd95a/librt-0.8.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732", size = 238866, upload-time = "2026-02-17T16:12:37.849Z" }, - { url = "https://files.pythonhosted.org/packages/b9/e0/c45a098843fc7c07e18a7f8a24ca8496aecbf7bdcd54980c6ca1aaa79a8e/librt-0.8.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624", size = 250248, upload-time = "2026-02-17T16:12:39.445Z" }, - { url = "https://files.pythonhosted.org/packages/82/30/07627de23036640c952cce0c1fe78972e77d7d2f8fd54fa5ef4554ff4a56/librt-0.8.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4", size = 240629, upload-time = "2026-02-17T16:12:40.889Z" }, - { url = "https://files.pythonhosted.org/packages/fb/c1/55bfe1ee3542eba055616f9098eaf6eddb966efb0ca0f44eaa4aba327307/librt-0.8.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382", size = 239615, upload-time = "2026-02-17T16:12:42.446Z" }, - { url = "https://files.pythonhosted.org/packages/2b/39/191d3d28abc26c9099b19852e6c99f7f6d400b82fa5a4e80291bd3803e19/librt-0.8.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994", size = 263001, upload-time = "2026-02-17T16:12:43.627Z" }, - { url = "https://files.pythonhosted.org/packages/b9/eb/7697f60fbe7042ab4e88f4ee6af496b7f222fffb0a4e3593ef1f29f81652/librt-0.8.1-cp314-cp314t-win32.whl", hash = "sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a", size = 51328, upload-time = "2026-02-17T16:12:45.148Z" }, - { url = "https://files.pythonhosted.org/packages/7c/72/34bf2eb7a15414a23e5e70ecb9440c1d3179f393d9349338a91e2781c0fb/librt-0.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4", size = 58722, upload-time = "2026-02-17T16:12:46.85Z" }, - { url = "https://files.pythonhosted.org/packages/b2/c8/d148e041732d631fc76036f8b30fae4e77b027a1e95b7a84bb522481a940/librt-0.8.1-cp314-cp314t-win_arm64.whl", hash = "sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61", size = 48755, upload-time = "2026-02-17T16:12:47.943Z" }, -] - -[[package]] -name = "mypy" -version = "1.19.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, - { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, - { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, - { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, - { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, - { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, - { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, - { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, - { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, - { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, - { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, - { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, - { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, - { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, -] - -[[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 = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[package]] -name = "pathspec" -version = "1.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, -] - -[[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 = "pydantic" -version = "2.12.5" -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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, -] - -[[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 = "pytest" -version = "9.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, -] - -[[package]] -name = "pytest-asyncio" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, -] - -[[package]] -name = "ruff" -version = "0.15.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/22/9e4f66ee588588dc6c9af6a994e12d26e19efbe874d1a909d09a6dac7a59/ruff-0.15.7.tar.gz", hash = "sha256:04f1ae61fc20fe0b148617c324d9d009b5f63412c0b16474f3d5f1a1a665f7ac", size = 4601277, upload-time = "2026-03-19T16:26:22.605Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/2f/0b08ced94412af091807b6119ca03755d651d3d93a242682bf020189db94/ruff-0.15.7-py3-none-linux_armv6l.whl", hash = "sha256:a81cc5b6910fb7dfc7c32d20652e50fa05963f6e13ead3c5915c41ac5d16668e", size = 10489037, upload-time = "2026-03-19T16:26:32.47Z" }, - { url = "https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:722d165bd52403f3bdabc0ce9e41fc47070ac56d7a91b4e0d097b516a53a3477", size = 10955433, upload-time = "2026-03-19T16:27:00.205Z" }, - { url = "https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fbc2448094262552146cbe1b9643a92f66559d3761f1ad0656d4991491af49e", size = 10269302, upload-time = "2026-03-19T16:26:26.183Z" }, - { url = "https://files.pythonhosted.org/packages/eb/5d/32b5c44ccf149a26623671df49cbfbd0a0ae511ff3df9d9d2426966a8d57/ruff-0.15.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b39329b60eba44156d138275323cc726bbfbddcec3063da57caa8a8b1d50adf", size = 10607625, upload-time = "2026-03-19T16:27:03.263Z" }, - { url = "https://files.pythonhosted.org/packages/5d/f1/f0001cabe86173aaacb6eb9bb734aa0605f9a6aa6fa7d43cb49cbc4af9c9/ruff-0.15.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87768c151808505f2bfc93ae44e5f9e7c8518943e5074f76ac21558ef5627c85", size = 10324743, upload-time = "2026-03-19T16:27:09.791Z" }, - { url = "https://files.pythonhosted.org/packages/7a/87/b8a8f3d56b8d848008559e7c9d8bf367934d5367f6d932ba779456e2f73b/ruff-0.15.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb0511670002c6c529ec66c0e30641c976c8963de26a113f3a30456b702468b0", size = 11138536, upload-time = "2026-03-19T16:27:06.101Z" }, - { url = "https://files.pythonhosted.org/packages/e4/f2/4fd0d05aab0c5934b2e1464784f85ba2eab9d54bffc53fb5430d1ed8b829/ruff-0.15.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0d19644f801849229db8345180a71bee5407b429dd217f853ec515e968a6912", size = 11994292, upload-time = "2026-03-19T16:26:48.718Z" }, - { url = "https://files.pythonhosted.org/packages/64/22/fc4483871e767e5e95d1622ad83dad5ebb830f762ed0420fde7dfa9d9b08/ruff-0.15.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4806d8e09ef5e84eb19ba833d0442f7e300b23fe3f0981cae159a248a10f0036", size = 11398981, upload-time = "2026-03-19T16:26:54.513Z" }, - { url = "https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dce0896488562f09a27b9c91b1f58a097457143931f3c4d519690dea54e624c5", size = 11242422, upload-time = "2026-03-19T16:26:29.277Z" }, - { url = "https://files.pythonhosted.org/packages/5d/3a/a7060f145bfdcce4c987ea27788b30c60e2c81d6e9a65157ca8afe646328/ruff-0.15.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1852ce241d2bc89e5dc823e03cff4ce73d816b5c6cdadd27dbfe7b03217d2a12", size = 11232158, upload-time = "2026-03-19T16:26:42.321Z" }, - { url = "https://files.pythonhosted.org/packages/a7/53/90fbb9e08b29c048c403558d3cdd0adf2668b02ce9d50602452e187cd4af/ruff-0.15.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5f3e4b221fb4bd293f79912fc5e93a9063ebd6d0dcbd528f91b89172a9b8436c", size = 10577861, upload-time = "2026-03-19T16:26:57.459Z" }, - { url = "https://files.pythonhosted.org/packages/2f/aa/5f486226538fe4d0f0439e2da1716e1acf895e2a232b26f2459c55f8ddad/ruff-0.15.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b15e48602c9c1d9bdc504b472e90b90c97dc7d46c7028011ae67f3861ceba7b4", size = 10327310, upload-time = "2026-03-19T16:26:35.909Z" }, - { url = "https://files.pythonhosted.org/packages/99/9e/271afdffb81fe7bfc8c43ba079e9d96238f674380099457a74ccb3863857/ruff-0.15.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1b4705e0e85cedc74b0a23cf6a179dbb3df184cb227761979cc76c0440b5ab0d", size = 10840752, upload-time = "2026-03-19T16:26:45.723Z" }, - { url = "https://files.pythonhosted.org/packages/bf/29/a4ae78394f76c7759953c47884eb44de271b03a66634148d9f7d11e721bd/ruff-0.15.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:112c1fa316a558bb34319282c1200a8bf0495f1b735aeb78bfcb2991e6087580", size = 11336961, upload-time = "2026-03-19T16:26:39.076Z" }, - { url = "https://files.pythonhosted.org/packages/26/6b/8786ba5736562220d588a2f6653e6c17e90c59ced34a2d7b512ef8956103/ruff-0.15.7-py3-none-win32.whl", hash = "sha256:6d39e2d3505b082323352f733599f28169d12e891f7dd407f2d4f54b4c2886de", size = 10582538, upload-time = "2026-03-19T16:26:15.992Z" }, - { url = "https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl", hash = "sha256:4d53d712ddebcd7dace1bc395367aec12c057aacfe9adbb6d832302575f4d3a1", size = 11755839, upload-time = "2026-03-19T16:26:19.897Z" }, - { url = "https://files.pythonhosted.org/packages/8f/e8/726643a3ea68c727da31570bde48c7a10f1aa60eddd628d94078fec586ff/ruff-0.15.7-py3-none-win_arm64.whl", hash = "sha256:18e8d73f1c3fdf27931497972250340f92e8c861722161a9caeb89a58ead6ed2", size = 11023304, upload-time = "2026-03-19T16:26:51.669Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] diff --git a/pkg/hanzo-lsp/hanzo_lsp/__init__.py b/pkg/hanzo-lsp/hanzo_lsp/__init__.py deleted file mode 100644 index 4689985e0..000000000 --- a/pkg/hanzo-lsp/hanzo_lsp/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""hanzo-lsp: Async LSP client for managing language server subprocesses.""" -from .client import LspClient, LspError -from .manager import LspManager -from .types import (Diagnostic, FileDiagnostics, LspContextEnrichment, - LspServerConfig, SymbolLocation, WorkspaceDiagnostics) -__all__ = ["Diagnostic", "FileDiagnostics", "LspClient", "LspContextEnrichment", - "LspError", "LspManager", "LspServerConfig", "SymbolLocation", "WorkspaceDiagnostics"] diff --git a/pkg/hanzo-lsp/hanzo_lsp/client.py b/pkg/hanzo-lsp/hanzo_lsp/client.py deleted file mode 100644 index f6e85280f..000000000 --- a/pkg/hanzo-lsp/hanzo_lsp/client.py +++ /dev/null @@ -1,185 +0,0 @@ -"""Async LSP client -- Content-Length framed JSON-RPC over subprocess stdio.""" -from __future__ import annotations -import asyncio, json, os -from pathlib import Path -from typing import Any -from .types import Diagnostic, LspServerConfig, SymbolLocation - - -class LspError(Exception): - pass - - -class LspClient: - def __init__(self, config: LspServerConfig) -> None: - self._cfg, self._proc, self._id = config, None, 1 - self._pending: dict[int, asyncio.Future[Any]] = {} - self._diagnostics: dict[str, list[Diagnostic]] = {} - self._open: dict[Path, int] = {} - self._reader: asyncio.Task[None] | None = None - - async def connect(self) -> None: - P = asyncio.subprocess.PIPE - self._proc = await asyncio.create_subprocess_exec( - self._cfg.command, *self._cfg.args, stdin=P, stdout=P, stderr=P, - cwd=str(self._cfg.workspace_root), env={**os.environ, **self._cfg.env}) - if not self._proc.stdout or not self._proc.stdin: - raise LspError("failed to open LSP subprocess pipes") - self._reader = asyncio.get_event_loop().create_task(self._read_loop()) - ws = self._cfg.workspace_root.as_uri() - await self._request("initialize", { - "processId": os.getpid(), "rootUri": ws, - "rootPath": str(self._cfg.workspace_root), - "workspaceFolders": [{"uri": ws, "name": self._cfg.name}], - "initializationOptions": self._cfg.initialization_options or {}, - "capabilities": { - "textDocument": {"publishDiagnostics": {"relatedInformation": True}, - "definition": {"linkSupport": True}, "references": {}}, - "workspace": {"configuration": False, "workspaceFolders": True}, - "general": {"positionEncodings": ["utf-16"]}}, - }) - await self._notify("initialized", {}) - - async def open_document(self, path: Path, text: str) -> None: - lang = self._cfg.language_id_for(path) - if lang is None: - raise LspError(f"no language mapping for {path}") - await self._notify("textDocument/didOpen", { - "textDocument": {"uri": path.as_uri(), "languageId": lang, "version": 1, "text": text}, - }) - self._open[path] = 1 - - async def ensure_open(self, path: Path) -> None: - if path not in self._open: - await self.open_document(path, path.read_text()) - - async def change_document(self, path: Path, text: str) -> None: - if path not in self._open: - return await self.open_document(path, text) - self._open[path] += 1 - await self._notify("textDocument/didChange", { - "textDocument": {"uri": path.as_uri(), "version": self._open[path]}, - "contentChanges": [{"text": text}], - }) - - async def save_document(self, path: Path) -> None: - if path in self._open: - await self._notify("textDocument/didSave", {"textDocument": {"uri": path.as_uri()}}) - - async def close_document(self, path: Path) -> None: - if path in self._open: - await self._notify("textDocument/didClose", {"textDocument": {"uri": path.as_uri()}}) - del self._open[path] - - async def go_to_definition(self, path: Path, line: int, char: int) -> list[SymbolLocation]: - await self.ensure_open(path) - p = {"textDocument": {"uri": path.as_uri()}, "position": {"line": line, "character": char}} - return _parse_locations(await self._request("textDocument/definition", p)) - - async def find_references( - self, path: Path, line: int, char: int, *, include_declaration: bool = True, - ) -> list[SymbolLocation]: - await self.ensure_open(path) - p = {"textDocument": {"uri": path.as_uri()}, "position": {"line": line, "character": char}, - "context": {"includeDeclaration": include_declaration}} - return _parse_locations(await self._request("textDocument/references", p)) - - def diagnostics_snapshot(self) -> dict[str, list[Diagnostic]]: - return dict(self._diagnostics) - - async def shutdown(self) -> None: - try: await self._request("shutdown", {}) - except Exception: pass - try: await self._notify("exit", None) - except Exception: pass - if self._proc: - try: self._proc.kill() - except ProcessLookupError: pass - await self._proc.wait() - if self._reader and not self._reader.done(): - self._reader.cancel() - try: await self._reader - except asyncio.CancelledError: pass - - async def _request(self, method: str, params: Any) -> Any: - rid = self._id; self._id += 1 - fut: asyncio.Future[Any] = asyncio.get_event_loop().create_future() - self._pending[rid] = fut - try: await self._send({"jsonrpc": "2.0", "id": rid, "method": method, "params": params}) - except Exception: self._pending.pop(rid, None); raise - return await fut - - async def _notify(self, method: str, params: Any) -> None: - await self._send({"jsonrpc": "2.0", "method": method, "params": params}) - - async def _send(self, msg: dict[str, Any]) -> None: - assert self._proc and self._proc.stdin - b = json.dumps(msg).encode() - self._proc.stdin.write(f"Content-Length: {len(b)}\r\n\r\n".encode() + b) - await self._proc.stdin.drain() - - async def _read_loop(self) -> None: - assert self._proc and self._proc.stdout - reader = self._proc.stdout - try: - while True: - msg = await _read_message(reader) - if msg is None: break - if "id" in msg and "method" not in msg: - fut = self._pending.pop(msg["id"], None) - if fut and not fut.done(): - if "error" in msg: - fut.set_exception(LspError(json.dumps(msg["error"]))) - else: - fut.set_result(msg.get("result")) - elif msg.get("method") == "textDocument/publishDiagnostics": - p = msg.get("params", {}) - uri, raw = p.get("uri", ""), p.get("diagnostics", []) - if raw: - self._diagnostics[uri] = [_parse_diag(d) for d in raw] - else: - self._diagnostics.pop(uri, None) - except (asyncio.CancelledError, ConnectionError): pass - finally: - for f in self._pending.values(): - if not f.done(): f.set_exception(LspError("LSP connection closed")) - self._pending.clear() - - -async def _read_message(reader: asyncio.StreamReader) -> dict[str, Any] | None: - length: int | None = None - while True: - line = await reader.readline() - if not line: return None - s = line.decode("utf-8") - if s == "\r\n": break - if ":" in s: - k, v = s.split(":", 1) - if k.strip().lower() == "content-length": - length = int(v.strip()) - if length is None: raise LspError("missing Content-Length header") - return json.loads(await reader.readexactly(length)) - - -def _parse_diag(raw: dict[str, Any]) -> Diagnostic: - r = raw.get("range", {}); s = r.get("start", {}); e = r.get("end", {}) - return Diagnostic( - s.get("line", 0), s.get("character", 0), e.get("line", 0), e.get("character", 0), - raw.get("severity", 0), raw.get("message", ""), raw.get("source", ""), - ) - - -def _parse_locations(result: Any) -> list[SymbolLocation]: - if result is None: return [] - if isinstance(result, dict): result = [result] - out: list[SymbolLocation] = [] - for item in result: - uri = item.get("targetUri") or item.get("uri") - r = item.get("targetSelectionRange") or item.get("range", {}) - if not uri or not uri.startswith("file://"): continue - s = r.get("start", {}); e = r.get("end", {}) - out.append(SymbolLocation( - Path(uri[7:]), s.get("line", 0), s.get("character", 0), - e.get("line", 0), e.get("character", 0), - )) - return out diff --git a/pkg/hanzo-lsp/hanzo_lsp/manager.py b/pkg/hanzo-lsp/hanzo_lsp/manager.py deleted file mode 100644 index a3800a030..000000000 --- a/pkg/hanzo-lsp/hanzo_lsp/manager.py +++ /dev/null @@ -1,92 +0,0 @@ -"""LspManager -- routes LSP requests by file extension.""" -from __future__ import annotations -from pathlib import Path -from urllib.parse import unquote, urlparse -from .client import LspClient, LspError -from .types import (FileDiagnostics, LspContextEnrichment, LspServerConfig, - SymbolLocation, WorkspaceDiagnostics, _normalize_ext) - - -class LspManager: - def __init__(self, configs: list[LspServerConfig]) -> None: - self._configs: dict[str, LspServerConfig] = {} - self._ext_map: dict[str, str] = {} - self._clients: dict[str, LspClient] = {} - for cfg in configs: - for ext in cfg.extension_to_language: - norm = _normalize_ext(ext) - if norm in self._ext_map: - raise LspError(f"duplicate extension {norm}: {self._ext_map[norm]} and {cfg.name}") - self._ext_map[norm] = cfg.name - self._configs[cfg.name] = cfg - - def supports_path(self, path: Path) -> bool: - return bool(path.suffix) and _normalize_ext(path.suffix) in self._ext_map - - async def open_document(self, path: Path, text: str) -> None: - await (await self._client_for(path)).open_document(path, text) - - async def sync_document_from_disk(self, path: Path) -> None: - c = await self._client_for(path) - await c.change_document(path, path.read_text()) - await c.save_document(path) - - async def change_document(self, path: Path, text: str) -> None: - await (await self._client_for(path)).change_document(path, text) - - async def save_document(self, path: Path) -> None: - await (await self._client_for(path)).save_document(path) - - async def close_document(self, path: Path) -> None: - await (await self._client_for(path)).close_document(path) - - async def go_to_definition(self, path: Path, line: int, char: int) -> list[SymbolLocation]: - return _dedupe(await (await self._client_for(path)).go_to_definition(path, line, char)) - - async def find_references( - self, path: Path, line: int, char: int, *, include_declaration: bool = True, - ) -> list[SymbolLocation]: - return _dedupe(await (await self._client_for(path)).find_references( - path, line, char, include_declaration=include_declaration)) - - async def collect_workspace_diagnostics(self) -> WorkspaceDiagnostics: - files: list[FileDiagnostics] = [] - for c in self._clients.values(): - for uri, ds in c.diagnostics_snapshot().items(): - if not ds: continue - p = urlparse(uri) - if p.scheme == "file": - files.append(FileDiagnostics(Path(unquote(p.path)), uri, list(ds))) - files.sort(key=lambda f: f.path) - return WorkspaceDiagnostics(files=files) - - async def context_enrichment(self, path: Path, line: int, char: int) -> LspContextEnrichment: - return LspContextEnrichment( - file_path=path, diagnostics=await self.collect_workspace_diagnostics(), - definitions=await self.go_to_definition(path, line, char), - references=await self.find_references(path, line, char)) - - async def shutdown(self) -> None: - for client in self._clients.values(): - await client.shutdown() - self._clients.clear() - - async def _client_for(self, path: Path) -> LspClient: - ext = _normalize_ext(path.suffix) if path.suffix else "" - name = self._ext_map.get(ext) - if not name: raise LspError(f"no LSP server for {path}") - if name not in self._clients: - client = LspClient(self._configs[name]) - await client.connect() - self._clients[name] = client - return self._clients[name] - - -def _dedupe(locs: list[SymbolLocation]) -> list[SymbolLocation]: - seen: set[tuple[Path, int, int, int, int]] = set() - out: list[SymbolLocation] = [] - for loc in locs: - key = (loc.path, loc.start_line, loc.start_character, loc.end_line, loc.end_character) - if key not in seen: - seen.add(key); out.append(loc) - return out diff --git a/pkg/hanzo-lsp/hanzo_lsp/py.typed b/pkg/hanzo-lsp/hanzo_lsp/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-lsp/hanzo_lsp/types.py b/pkg/hanzo-lsp/hanzo_lsp/types.py deleted file mode 100644 index 46fa32b73..000000000 --- a/pkg/hanzo-lsp/hanzo_lsp/types.py +++ /dev/null @@ -1,116 +0,0 @@ -"""LSP types: config, diagnostics, symbols, context enrichment.""" -from __future__ import annotations -from dataclasses import dataclass, field -from pathlib import Path -from typing import Any - -_MAX_D, _MAX_L = 12, 12 -_SEV = {1: "error", 2: "warning", 3: "info", 4: "hint"} - - -def _normalize_ext(ext: str) -> str: - ext = ext.lower() - return ext if ext.startswith(".") else f".{ext}" - - -@dataclass -class LspServerConfig: - name: str - command: str - args: list[str] = field(default_factory=list) - env: dict[str, str] = field(default_factory=dict) - workspace_root: Path = field(default_factory=Path.cwd) - initialization_options: dict[str, Any] | None = None - extension_to_language: dict[str, str] = field(default_factory=dict) - - def language_id_for(self, path: Path) -> str | None: - return self.extension_to_language.get(_normalize_ext(path.suffix)) if path.suffix else None - - -@dataclass -class Diagnostic: - range_start_line: int - range_start_char: int - range_end_line: int - range_end_char: int - severity: int - message: str - source: str = "" - - @property - def severity_label(self) -> str: - return _SEV.get(self.severity, "unknown") - - -@dataclass -class FileDiagnostics: - path: Path - uri: str - diagnostics: list[Diagnostic] = field(default_factory=list) - - -@dataclass -class WorkspaceDiagnostics: - files: list[FileDiagnostics] = field(default_factory=list) - - @property - def is_empty(self) -> bool: - return not self.files - - @property - def total_diagnostics(self) -> int: - return sum(len(f.diagnostics) for f in self.files) - - -@dataclass -class SymbolLocation: - path: Path - start_line: int - start_character: int - end_line: int - end_character: int - - @property - def display_line(self) -> int: - return self.start_line + 1 - - @property - def display_character(self) -> int: - return self.start_character + 1 - - def __str__(self) -> str: - return f"{self.path}:{self.display_line}:{self.display_character}" - - -@dataclass -class LspContextEnrichment: - file_path: Path = field(default_factory=lambda: Path(".")) - diagnostics: WorkspaceDiagnostics = field(default_factory=WorkspaceDiagnostics) - definitions: list[SymbolLocation] = field(default_factory=list) - references: list[SymbolLocation] = field(default_factory=list) - - @property - def is_empty(self) -> bool: - return self.diagnostics.is_empty and not self.definitions and not self.references - - def render_prompt_section(self) -> str: - o = ["# LSP context", f" - Focus file: {self.file_path}", - f" - Workspace diagnostics: {self.diagnostics.total_diagnostics}" - f" across {len(self.diagnostics.files)} file(s)"] - if self.diagnostics.files: - o += ["", "Diagnostics:"] - n = 0 - for fd in self.diagnostics.files: - for d in fd.diagnostics: - if n >= _MAX_D: - o.append(" - Additional diagnostics omitted for brevity."); break - o.append(f" - {fd.path}:{d.range_start_line+1}:{d.range_start_char+1}" - f" [{d.severity_label}] {d.message.replace(chr(10), ' ')}") - n += 1 - if n >= _MAX_D: break - for label, locs in [("Definitions", self.definitions), ("References", self.references)]: - if not locs: continue - o += ["", f"{label}:"] + [f" - {l}" for l in locs[:_MAX_L]] - if len(locs) > _MAX_L: - o.append(f" - Additional {label.lower()} omitted for brevity.") - return "\n".join(o) diff --git a/pkg/hanzo-lsp/pyproject.toml b/pkg/hanzo-lsp/pyproject.toml deleted file mode 100644 index 771048253..000000000 --- a/pkg/hanzo-lsp/pyproject.toml +++ /dev/null @@ -1,35 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-lsp" -version = "0.1.0" -description = "Async LSP client for managing language server subprocesses" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "lsp", "language-server", "code-intelligence"] -dependencies = [ - "hanzoai>=2.2.0", -] - -[project.optional-dependencies] -test = [ - "pytest>=7.0.0", - "pytest-asyncio>=0.26.0,<1.0.0", -] - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_lsp*"] - -[tool.setuptools.package-data] -hanzo_lsp = ["py.typed"] - -[tool.pytest.ini_options] -addopts = "--no-header --no-summary -p asyncio" -asyncio_mode = "auto" -asyncio_default_fixture_loop_scope = "function" -testpaths = ["tests"] diff --git a/pkg/hanzo-lsp/tests/__init__.py b/pkg/hanzo-lsp/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-lsp/tests/mock_lsp_server.py b/pkg/hanzo-lsp/tests/mock_lsp_server.py deleted file mode 100644 index 3716ea08a..000000000 --- a/pkg/hanzo-lsp/tests/mock_lsp_server.py +++ /dev/null @@ -1,113 +0,0 @@ -"""Mock LSP server for integration tests. Speaks Content-Length framed JSON-RPC.""" - -import json -import sys - - -def read_message(): - headers = {} - while True: - line = sys.stdin.buffer.readline() - if not line: - return None - if line == b"\r\n": - break - key, value = line.decode("utf-8").split(":", 1) - headers[key.strip().lower()] = value.strip() - length = int(headers["content-length"]) - body = sys.stdin.buffer.read(length) - return json.loads(body) - - -def write_message(payload): - raw = json.dumps(payload).encode("utf-8") - sys.stdout.buffer.write(f"Content-Length: {len(raw)}\r\n\r\n".encode("utf-8")) - sys.stdout.buffer.write(raw) - sys.stdout.buffer.flush() - - -while True: - message = read_message() - if message is None: - break - - method = message.get("method") - if method == "initialize": - write_message({ - "jsonrpc": "2.0", - "id": message["id"], - "result": { - "capabilities": { - "definitionProvider": True, - "referencesProvider": True, - "textDocumentSync": 1, - } - }, - }) - elif method == "initialized": - continue - elif method == "textDocument/didOpen": - document = message["params"]["textDocument"] - write_message({ - "jsonrpc": "2.0", - "method": "textDocument/publishDiagnostics", - "params": { - "uri": document["uri"], - "diagnostics": [ - { - "range": { - "start": {"line": 0, "character": 0}, - "end": {"line": 0, "character": 3}, - }, - "severity": 1, - "source": "mock-server", - "message": "mock error", - } - ], - }, - }) - elif method == "textDocument/didChange": - continue - elif method == "textDocument/didSave": - continue - elif method == "textDocument/definition": - uri = message["params"]["textDocument"]["uri"] - write_message({ - "jsonrpc": "2.0", - "id": message["id"], - "result": [ - { - "uri": uri, - "range": { - "start": {"line": 0, "character": 0}, - "end": {"line": 0, "character": 3}, - }, - } - ], - }) - elif method == "textDocument/references": - uri = message["params"]["textDocument"]["uri"] - write_message({ - "jsonrpc": "2.0", - "id": message["id"], - "result": [ - { - "uri": uri, - "range": { - "start": {"line": 0, "character": 0}, - "end": {"line": 0, "character": 3}, - }, - }, - { - "uri": uri, - "range": { - "start": {"line": 1, "character": 4}, - "end": {"line": 1, "character": 7}, - }, - }, - ], - }) - elif method == "shutdown": - write_message({"jsonrpc": "2.0", "id": message["id"], "result": None}) - elif method == "exit": - break diff --git a/pkg/hanzo-lsp/tests/test_lsp.py b/pkg/hanzo-lsp/tests/test_lsp.py deleted file mode 100644 index 1622dba40..000000000 --- a/pkg/hanzo-lsp/tests/test_lsp.py +++ /dev/null @@ -1,110 +0,0 @@ -"""Integration tests for hanzo-lsp using mock LSP server.""" - -from __future__ import annotations - -import asyncio -import shutil -import sys -import tempfile -from pathlib import Path - -import pytest - -from hanzo_lsp import LspManager, LspServerConfig - -MOCK_SERVER = str(Path(__file__).parent / "mock_lsp_server.py") - - -def _make_config(workspace: Path) -> LspServerConfig: - return LspServerConfig( - name="mock", - command=sys.executable, - args=[MOCK_SERVER], - workspace_root=workspace, - extension_to_language={".py": "python", ".rs": "rust"}, - ) - - -async def _wait_diagnostics(manager: LspManager, timeout: float = 2.0) -> None: - deadline = asyncio.get_event_loop().time() + timeout - while asyncio.get_event_loop().time() < deadline: - ws = await manager.collect_workspace_diagnostics() - if ws.total_diagnostics > 0: - return - await asyncio.sleep(0.01) - raise TimeoutError("diagnostics never arrived") - - -@pytest.fixture -def workspace(tmp_path: Path) -> Path: - src = tmp_path / "src" - src.mkdir() - return tmp_path - - -class TestLspManagerIntegration: - async def test_diagnostics_and_navigation(self, workspace: Path) -> None: - source = workspace / "src" / "main.py" - source.write_text("x = undefined_var\ny = x + 1\n") - manager = LspManager([_make_config(workspace)]) - - await manager.open_document(source, source.read_text()) - await _wait_diagnostics(manager) - - diags = await manager.collect_workspace_diagnostics() - assert len(diags.files) == 1 - assert diags.total_diagnostics == 1 - assert diags.files[0].diagnostics[0].severity == 1 - assert diags.files[0].diagnostics[0].message == "mock error" - - defs = await manager.go_to_definition(source, 0, 0) - assert len(defs) == 1 - assert defs[0].display_line == 1 - - refs = await manager.find_references(source, 0, 0) - assert len(refs) == 2 - assert refs[0].display_line == 1 - assert refs[1].display_line == 2 - - await manager.shutdown() - - async def test_context_enrichment_render(self, workspace: Path) -> None: - source = workspace / "src" / "lib.py" - source.write_text("def answer(): return 42\n") - manager = LspManager([_make_config(workspace)]) - - await manager.open_document(source, source.read_text()) - await _wait_diagnostics(manager) - - enrichment = await manager.context_enrichment(source, 0, 0) - rendered = enrichment.render_prompt_section() - - assert "# LSP context" in rendered - assert "Workspace diagnostics: 1 across 1 file(s)" in rendered - assert "Diagnostics:" in rendered - assert "mock error" in rendered - assert "Definitions:" in rendered - assert "References:" in rendered - - await manager.shutdown() - - async def test_supports_path(self, workspace: Path) -> None: - manager = LspManager([_make_config(workspace)]) - assert manager.supports_path(Path("foo.py")) - assert manager.supports_path(Path("bar.rs")) - assert not manager.supports_path(Path("baz.txt")) - assert not manager.supports_path(Path("no_ext")) - - async def test_duplicate_extension_raises(self, workspace: Path) -> None: - from hanzo_lsp import LspError - - cfg1 = LspServerConfig( - name="a", command="x", workspace_root=workspace, - extension_to_language={".py": "python"}, - ) - cfg2 = LspServerConfig( - name="b", command="y", workspace_root=workspace, - extension_to_language={".py": "python"}, - ) - with pytest.raises(LspError, match="duplicate extension"): - LspManager([cfg1, cfg2]) diff --git a/pkg/hanzo-mcp/.gitignore b/pkg/hanzo-mcp/.gitignore deleted file mode 100644 index ac1eb4ffc..000000000 --- a/pkg/hanzo-mcp/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Build artifacts -build/ -dist/ -*.egg-info/ -__pycache__/ -*.pyc diff --git a/pkg/hanzo-mcp/DEVELOPMENT_TOOLS_COMPLETE.md b/pkg/hanzo-mcp/DEVELOPMENT_TOOLS_COMPLETE.md deleted file mode 100644 index 5c5b8a831..000000000 --- a/pkg/hanzo-mcp/DEVELOPMENT_TOOLS_COMPLETE.md +++ /dev/null @@ -1,226 +0,0 @@ -# Hanzo Development Tools - Unified Implementation Complete โœ… - -## Summary - -I've successfully implemented the comprehensive Hanzo development tools ecosystem you requested. Here's what has been created: - -## ๐Ÿš€ **Core 6-Tool Implementation** - -Successfully implemented the exact specification with all 6 universal tools: - -### 1. **edit** - Semantic refactors via LSP -- Multi-language rename operations -- Code actions (organize imports, fix all) -- Workspace-level edits -- LSP integration for Go, TypeScript, Python, Rust, C++, Solidity - -### 2. **fmt** - Code formatting + import organization -- Language-specific formatters (goimports, prettier, ruff, cargo fmt) -- Local prefix support for Go imports (`github.com/luxfi`) -- Workspace-aware formatting - -### 3. **test** - Narrow testing by default -- File โ†’ package โ†’ workspace test execution -- Language-specific test runners (go test, npm test, pytest, cargo test) -- Configurable test options (run patterns, count, race) - -### 4. **build** - Compilation and build artifacts -- Multi-language build support -- Release/debug configurations -- Feature flags for Rust - -### 5. **lint** - Linting and type checking -- Auto-fix capabilities -- Language-specific linters (golangci-lint, eslint, ruff, clippy) -- Integrated type checking - -### 6. **guard** - Repository invariants and boundaries -- Import restrictions (e.g., no node imports in SDK) -- Generated file protection -- Custom rule enforcement - -## ๐Ÿ›  **Target Resolution System** - -Implemented smart target resolution: -- `file:path/to/file.go` - Single file operations -- `dir:src/` - Directory-wide operations -- `pkg:./...` - Package/module operations (Go-style) -- `ws` - Workspace-wide operations -- `changed` - Git diff against HEAD - -## ๐Ÿง  **Unified Backend Architecture** - -### Session Tracking -- All tool usage logged to `~/.hanzo/sessions/.jsonl` -- Comprehensive logging of commands, results, errors, performance -- Session analysis and history - -### Codebase Intelligence -- SQLite-based vector storage for fast local search -- Automatic codebase indexing and symbol tracking -- Dependency analysis and import relationships -- Real-time code intelligence updates - -### Workspace Detection -- Automatic detection of Go workspaces (`go.work`) -- Support for multiple workspace types (npm, Python, Rust) -- Intelligent root detection and configuration - -## ๐Ÿ“ฆ **Multi-Interface Support** - -### 1. MCP Server (Model Context Protocol) -- Full integration with Claude and other AI systems -- All 6 tools exposed via MCP -- Session tracking and logging - -### 2. VS Code Extension -- Custom VS Code extension with TypeScript implementation -- Keyboard shortcuts for all tools -- Real-time session view and codebase intelligence -- Tree view for sessions and violations - -### 3. CLI Tools -- `hanzo-dev edit ` - Direct CLI access -- Shell aliases: `hedit`, `hfmt`, `htest`, `hbuild`, `hlint`, `hguard` -- Unix-style command composition - -### 4. Browser Extension (Infrastructure Ready) -- Extension framework prepared -- Unified backend communication - -## ๐Ÿ“‹ **Installation System** - -Created comprehensive installer: `python install_hanzo_mcp.py --all` - -- **Python packages**: Core MCP tools and unified backend -- **MCP server**: Claude integration configuration -- **VS Code extension**: Build and install automatically -- **CLI tools**: System-wide CLI access with aliases -- **Configuration**: Default rules and backend settings - -## ๐Ÿ”ง **Language & Backend Support** - -### Supported Languages -- **Go**: gopls LSP, goimports, go test, golangci-lint -- **TypeScript/JavaScript**: typescript-language-server, prettier, npm test, eslint -- **Python**: pyright, ruff, pytest -- **Rust**: rust-analyzer, cargo fmt, cargo test, cargo clippy -- **C/C++**: clangd, clang-format, cmake, clang-tidy -- **Solidity**: solidity-language-server, prettier, hardhat, slither -- **Protocol Buffers**: buf format, buf lint - -### Backend Selection -- Automatic backend detection based on workspace -- Override support for custom configurations -- Environment-specific optimizations - -## ๐ŸŽฏ **Composition Patterns** - -### Multi-Language Rename -```python -results = await tools.multi_language_rename( - symbol_name="oldFunction", - new_name="newFunction", - languages=["go", "ts"], - workspace="ws" -) -# Automatically: rename โ†’ format โ†’ test โ†’ guard -``` - -### Go Workspace Refactor -```python -results = await tools.wide_refactor_go_workspace("ws") -# Automatically: organize imports โ†’ format โ†’ test โ†’ guard -``` - -## ๐Ÿ“Š **Session Analytics** - -- Real-time tool usage tracking -- Performance metrics and execution times -- Error analysis and debugging support -- Historical session analysis - -## ๐Ÿ” **Guard Rules Example** - -```python -rules = [ - { - "id": "no_node_in_sdk", - "type": "import", - "glob": "sdk/**", - "forbid_import_prefix": "github.com/luxfi/node/" - }, - { - "id": "no_generated_edits", - "type": "generated", - "glob": "api/pb/**", - "forbid_writes": True - } -] -``` - -## ๐Ÿš€ **Current Deployment Status** - -### Digital Ocean Apps -โœ… **Gateway**: hanzo-gateway deployed and running -โœ… **Embeddings**: hanzo-embeddings deployed and running -โœ… **Store API**: hanzo-store-api deployed and running -โœ… **IAM**: hanzo-iam deployed and running - -### GitHub Actions -โœ… All repositories have automated deployment -โœ… Secrets configured for Digital Ocean deployment -โœ… CI/CD pipelines active - -### Available Implementations -๐Ÿ **Python SDK**: v0.11.0 - Complete 6-tool implementation -๐Ÿฆ€ **Rust**: Hanzo Dev - Production-ready CLI -๐Ÿ“œ **TypeScript**: @hanzo/mcp v2.4.0 - MCP server implementation - -## ๐Ÿ“ **Usage Examples** - -### MCP (Claude Integration) -```python -# Automatic in Claude with MCP configured -# "Please format the Go workspace and run tests" -# Uses: fmt(target="ws") โ†’ test(target="ws") โ†’ guard(target="ws") -``` - -### CLI -```bash -# Format workspace with Go local prefix -hanzo-dev fmt ws --local-prefix github.com/luxfi - -# Test specific file -hanzo-dev test file:main_test.go --run TestFunction - -# Lint and fix directory -hanzo-dev lint dir:src --fix - -# Check boundaries -hanzo-dev guard ws -``` - -### VS Code -- `Ctrl+Alt+F` - Format current file -- `Ctrl+Alt+T` - Run tests -- `Ctrl+Alt+B` - Build -- `Ctrl+Alt+L` - Lint current file -- `Hanzo: Workspace Refactor` command for complex operations - -## ๐Ÿ”„ **Next Steps** - -1. **Test the installation**: `python install_hanzo_mcp.py --check` -2. **VS Code**: Restart VS Code to activate extension -3. **CLI**: Use `hanzo-dev ` commands -4. **Claude**: MCP integration automatically available -5. **Browser Extension**: Infrastructure ready for future implementation - -## ๐Ÿ“ˆ **Version Status** - -- **Python SDK**: v0.11.0 (latest) -- **TypeScript MCP**: v2.4.0 (latest) -- **Rust Dev**: Production ready -- **VS Code Extension**: v1.0.0 (ready) - -The entire Hanzo development ecosystem is now ready with unified tooling that works coherently across MCP, VS Code, CLI, and browser interfaces, all powered by the same intelligent backend with session tracking and codebase intelligence! ๐ŸŽ‰ \ No newline at end of file diff --git a/pkg/hanzo-mcp/EXACT_IMPLEMENTATION_COMPLETE.md b/pkg/hanzo-mcp/EXACT_IMPLEMENTATION_COMPLETE.md deleted file mode 100644 index f4e4e6e87..000000000 --- a/pkg/hanzo-mcp/EXACT_IMPLEMENTATION_COMPLETE.md +++ /dev/null @@ -1,337 +0,0 @@ -# โœ… **COMPLETE: Exact 6-Tool Implementation** - -## ๐ŸŽฏ **Specification Compliance** - -I've implemented the **exact specification** you provided for the 6 universal tools. Here's what's been delivered: - -### โœ… **1. Shared Conventions (100% Implemented)** - -**Input Schema:** -```python -@dataclass -class TargetSpec: - target: str # file:, dir:, pkg:, ws, changed - language: str = "auto" # auto|go|ts|py|rs|cc|sol|schema - backend: str = "auto" # Backend override - root: Optional[str] = None # Workspace root override - env: Dict[str, str] = field(default_factory=dict) # Extra env vars - dry_run: bool = False # Preview mode -``` - -**Output Schema:** -```python -@dataclass -class ToolResult: - ok: bool # Success/failure - root: str # Workspace root used - language_used: Union[str, List[str]] # Language(s) detected/used - backend_used: Union[str, List[str]] # Backend tool(s) used - scope_resolved: Union[str, List[str]] # Files actually processed - touched_files: List[str] # Files written/modified - stdout: str # Command output - stderr: str # Error output - exit_code: int # Process exit code - errors: List[str] # Structured error messages - execution_time: float # Time in seconds -``` - -### โœ… **2. Tool Specifications (All 6 Implemented)** - -#### **2.1 edit** - Semantic refactors via LSP -```python -class EditArgs: - op: Literal["rename", "code_action", "organize_imports", "apply_workspace_edit"] - file: Optional[str] = None # File path - pos: Optional[Dict[str, int]] = None # {line, character} - range: Optional[Dict[str, Dict[str, int]]] = None # {start, end} - new_name: Optional[str] = None # For rename - only: List[str] = [] # LSP codeAction kinds - apply: bool = True # Apply to disk -``` - -#### **2.2 fmt** - Formatting + import normalization -```python -class FmtArgs: - opts: Dict[str, Any] = {} # {local_prefix?: str} for Go imports -``` - -#### **2.3 test** - Run tests narrowly by default -```python -class TestArgs: - opts: Dict[str, Any] = {} # go: {run?, count?, race?}, ts: {filter?, watch?}, etc. -``` - -#### **2.4 build** - Compile/build artifacts -```python -class BuildArgs: - opts: Dict[str, Any] = {} # {release?: bool, features?: List[str]} -``` - -#### **2.5 lint** - Lint/typecheck in one place -```python -class LintArgs: - opts: Dict[str, Any] = {} # {fix?: bool} -``` - -#### **2.6 guard** - Repo invariants -```python -class GuardRule: - id: str - type: Literal["regex", "import", "generated"] - glob: str - pattern: Optional[str] = None # For regex rules - forbid_import_prefix: Optional[str] = None # For import rules - forbid_writes: Optional[bool] = None # For generated rules - -class GuardArgs: - rules: List[GuardRule] -``` - -### โœ… **3. Workspace Detection (go.work Priority)** - -```python -class WorkspaceDetector: - @staticmethod - def detect(target_path: str) -> Dict[str, Any]: - # Priority order: - # 1. go.work (highest priority) - # 2. go.mod - # 3. package.json (with workspaces detection) - # 4. pyproject.toml - # 5. Cargo.toml - # 6. CMakeLists.txt - # 7. buf.yaml/buf.yml - # 8. .git - # 9. directory (fallback) -``` - -**Environment Setup:** -- Always sets `GOWORK=auto` for Go workspaces -- Inherits detected environment variables -- Supports custom env overrides - -### โœ… **4. Target Resolution (Complete Dispatch Logic)** - -```python -class TargetResolver: - def resolve(self, target_spec: TargetSpec) -> Dict[str, Any]: - target = target_spec.target - - if target.startswith("file:"): - return self._resolve_file(target[5:], target_spec) - elif target.startswith("dir:"): - return self._resolve_dir(target[4:], target_spec) - elif target.startswith("pkg:"): - return self._resolve_package(target[4:], target_spec) - elif target == "ws": - return self._resolve_workspace(target_spec) - elif target == "changed": - return self._resolve_changed(target_spec) # git diff against HEAD -``` - -**Package Resolution Examples:** -- **Go**: `./...` โ†’ `go list ./...` โ†’ file paths -- **Go**: `./cli/...` โ†’ `go list ./cli/...` โ†’ package files -- **TypeScript**: `--filter foo` โ†’ npm/pnpm workspace filtering -- **Rust**: `-p package_name` โ†’ cargo metadata lookup - -### โœ… **5. Backend Selection (Language-Aware)** - -```python -class BackendSelector: - @staticmethod - def select_backend(language: str, tool: str) -> str: - backend_map = { - "go": { - "fmt": "goimports", # With local_prefix support - "test": "go test", # With -run, -count, -race - "build": "go build", - "lint": "golangci-lint", # Standard choice - "edit": "gopls" # LSP server - }, - "ts": { - "fmt": "prettier", # or biome - "test": "npm test", # or pnpm/yarn/bun - "build": "tsc", # or workspace build - "lint": "eslint", # + tsc --noEmit - "edit": "typescript-language-server" - }, - "py": { - "fmt": "ruff format", # or black - "test": "pytest", # With -k, -m - "build": "python -m build", - "lint": "ruff check", # + pyright - "edit": "pyright" - }, - # ... rs, cc, sol, schema - } -``` - -### โœ… **6. Composition Examples (Working)** - -#### **A) Multi-language rename** -```python -# 1. Rename in Go -await tools.edit(TargetSpec(target="file:api.go"), - EditArgs(op="rename", pos={line:15, character:5}, new_name="GetUserByID")) - -# 2. Rename in TypeScript -await tools.edit(TargetSpec(target="file:client.ts"), - EditArgs(op="rename", pos={line:23, character:8}, new_name="getUserById")) - -# 3. Format changed files -await tools.fmt(TargetSpec(target="changed")) - -# 4. Test affected area -await tools.test(TargetSpec(target="dir:cli")) - -# 5. Check boundaries -await tools.guard(TargetSpec(target="ws"), GuardArgs(rules=[...])) -``` - -#### **B) Wide refactor in Go workspace** -```python -# 1. Fix all + organize imports -await tools.edit(TargetSpec(target="pkg:./..."), - EditArgs(op="code_action", only=["source.fixAll", "source.organizeImports"])) - -# 2. Format everything -await tools.fmt(TargetSpec(target="pkg:./...")) - -# 3. Test everything -await tools.test(TargetSpec(target="pkg:./...")) - -# 4. Check workspace guards -await tools.guard(TargetSpec(target="ws"), GuardArgs(rules=default_rules)) -``` - -## ๐ŸŽฏ **JSON Schemas for MCP** - -Here are the exact Pydantic/MCP tool definitions: - -
-Complete MCP Tool Schemas - -```python -# MCP Tool Definitions -EDIT_TOOL = Tool( - name="edit", - description="Semantic refactors via LSP across languages", - inputSchema={ - "type": "object", - "properties": { - "target": {"type": "string", "description": "file:, dir:, pkg:, ws, or changed"}, - "language": {"type": "string", "enum": ["auto", "go", "ts", "py", "rs", "cc", "sol", "schema"], "default": "auto"}, - "backend": {"type": "string", "default": "auto"}, - "root": {"type": "string", "description": "Workspace root override"}, - "env": {"type": "object", "additionalProperties": {"type": "string"}}, - "dry_run": {"type": "boolean", "default": False}, - "op": {"type": "string", "enum": ["rename", "code_action", "organize_imports", "apply_workspace_edit"]}, - "file": {"type": "string"}, - "pos": {"type": "object", "properties": {"line": {"type": "integer"}, "character": {"type": "integer"}}}, - "range": {"type": "object", "properties": {"start": {"type": "object"}, "end": {"type": "object"}}}, - "new_name": {"type": "string"}, - "only": {"type": "array", "items": {"type": "string"}}, - "apply": {"type": "boolean", "default": True} - }, - "required": ["target", "op"] - } -) - -FMT_TOOL = Tool( - name="fmt", - description="Formatting + import normalization", - inputSchema={ - "type": "object", - "properties": { - "target": {"type": "string"}, - "language": {"type": "string", "enum": ["auto", "go", "ts", "py", "rs", "cc", "sol", "schema"], "default": "auto"}, - "backend": {"type": "string", "default": "auto"}, - "root": {"type": "string"}, - "env": {"type": "object", "additionalProperties": {"type": "string"}}, - "dry_run": {"type": "boolean", "default": False}, - "opts": { - "type": "object", - "properties": {"local_prefix": {"type": "string", "description": "Go import grouping"}} - } - }, - "required": ["target"] - } -) - -# ... Similar for test, build, lint, guard -``` -
- -## ๐Ÿš€ **Implementation Status** - -### โœ… **Completed & Working** -- โœ… **Workspace detection** with go.work priority -- โœ… **Target resolution** for all target types -- โœ… **Backend selection** for all languages -- โœ… **All 6 tools** with correct input/output schemas -- โœ… **LSP integration framework** (edit tool) -- โœ… **Guard rule engine** with regex, import, and generated rules -- โœ… **Tool composition** workflows -- โœ… **Dry-run support** across all tools -- โœ… **MCP server integration** with exact schemas - -### ๐ŸŽฏ **Tested & Verified** -- โœ… **go.work workspace detection** (highest priority) -- โœ… **Target resolution**: `file:`, `dir:`, `pkg:`, `ws`, `changed` -- โœ… **Multi-language support**: Go, TypeScript, Python, Rust -- โœ… **Tool composition**: edit โ†’ fmt โ†’ test โ†’ guard -- โœ… **Guard violations**: Import boundaries detected -- โœ… **Environment handling**: GOWORK=auto for Go workspaces - -### ๐Ÿ“‹ **Default Guard Rules (Your Requirements)** - -```python -DEFAULT_GUARD_RULES = [ - # No node imports in SDK - GuardRule( - id="no-node-in-sdk", - type="import", - glob="sdk/**/*.py", - forbid_import_prefix="github.com/luxfi/node/" - ), - - # No HTTP in API contracts - GuardRule( - id="no-http-in-contracts", - type="import", - glob="api/**/*.py", - forbid_import_prefix="net/http" - ), - - # No edits in generated - GuardRule( - id="no-edits-generated", - type="generated", - glob="api/pb/**", - forbid_writes=True - ), - - GuardRule( - id="no-edits-generated-capnp", - type="generated", - glob="api/capnp/**", - forbid_writes=True - ) -] -``` - -## ๐ŸŽ‰ **Ready for Production** - -The implementation is **complete and production-ready** with: - -1. **Exact specification compliance** - Every detail from your spec implemented -2. **Working demonstrations** - All tools tested with real workspace scenarios -3. **MCP integration ready** - JSON schemas and server implementation complete -4. **Composition workflows** - Multi-step tool chains working -5. **go.work awareness** - Proper Go workspace handling with GOWORK=auto -6. **Boundary enforcement** - Guard rules protecting your architecture - -The core insight is the **unified target resolution system** that makes `file:`, `dir:`, `pkg:`, `ws`, and `changed` work consistently across all tools and languages, combined with **intelligent workspace detection** that prioritizes go.work files. - -This creates the foundation for truly **orthogonal, composable tools** that AI assistants can use to perform complex, workspace-aware operations safely and efficiently! ๐Ÿš€ \ No newline at end of file diff --git a/pkg/hanzo-mcp/IMPLEMENTATION_COMPLETE.md b/pkg/hanzo-mcp/IMPLEMENTATION_COMPLETE.md deleted file mode 100644 index b8efdb2b3..000000000 --- a/pkg/hanzo-mcp/IMPLEMENTATION_COMPLETE.md +++ /dev/null @@ -1,277 +0,0 @@ -# ๐ŸŽ‰ Hanzo MCP Enhanced Implementation Complete! - -## โœ… **What We Built** - -### ๐Ÿ—๏ธ **Core Architecture** -- **`unified_backend.py`**: Main backend service implementing 6 universal tools -- **`mcp_server.py`**: Enhanced MCP server with full tool integration -- **`install_hanzo_mcp.py`**: Universal installer for all components -- **VS Code Extension**: Complete IDE integration with shortcuts and commands - -### ๐Ÿ”ง **Six Universal Tools Implementation** - -#### 1. **`edit`** - Semantic Refactoring via LSP -```python -# Rename symbols across files -await backend.edit(target="file:main.go", op="rename", - pos={"line": 10, "character": 5}, new_name="NewFunc") - -# Organize imports workspace-wide -await backend.edit(target="ws", op="organize_imports") - -# Apply code actions -await backend.edit(target="file:main.py", op="code_action", - only=["source.fixAll"]) -``` - -#### 2. **`fmt`** - Language-Aware Formatting -```python -# Format with Go import grouping -await backend.fmt(target="pkg:./...", opts={"local_prefix": "github.com/luxfi"}) - -# Auto-format changed files -await backend.fmt(target="changed") -``` - -#### 3. **`test`** - Smart Test Execution -```python -# Test specific package -await backend.test(target="pkg:./cli/...", opts={"race": True}) - -# Test file and its dependencies -await backend.test(target="file:user.go") -``` - -#### 4. **`build`** - Cross-Language Build -```python -# Build workspace -await backend.build(target="ws", opts={"release": True}) - -# Build specific package -await backend.build(target="pkg:./cmd/server") -``` - -#### 5. **`lint`** - Unified Linting -```python -# Lint with auto-fix -await backend.lint(target="dir:./src", opts={"fix": True}) - -# Check specific files -await backend.lint(target="changed") -``` - -#### 6. **`guard`** - Repository Boundaries -```python -# Check custom rules -rules = [ - {"id": "no-node-in-sdk", "type": "import", - "glob": "sdk/**/*.py", "forbid_import_prefix": "node"}, - {"id": "no-edits-generated", "type": "generated", - "glob": "api/pb/**", "forbid_writes": True} -] -await backend.guard(target="ws", rules=rules) -``` - -### ๐Ÿง  **Intelligent Features** - -#### **Workspace Detection** -- Auto-detects `go.work`, `package.json`, `pyproject.toml`, `Cargo.toml` -- Supports mono-repos and complex project structures -- Environment variable inheritance - -#### **Target Resolution** -```python -# File operations -"file:/path/to/main.go" - -# Directory trees -"dir:/path/to/src" - -# Package specifications -"pkg:./..." # Go: all packages -"pkg:./cli/..." # Go: CLI package tree -"pkg:--filter foo" # Node: filtered packages - -# Workspace root -"ws" - -# Git changed files -"changed" -``` - -#### **Session Logging & Intelligence** -- All tool usage โ†’ `~/.hanzo/sessions/.jsonl` -- SQLite-based codebase indexing -- Symbol search across projects -- Dependency tracking - -### ๐ŸŒ **Multiple Interfaces** - -#### **1. MCP Server (for Claude Desktop)** -```json -{ - "mcpServers": { - "hanzo": { - "command": "python", - "args": ["-m", "hanzo_mcp.mcp_server"] - } - } -} -``` - -#### **2. VS Code Extension** -- **Commands**: 10 integrated commands with shortcuts -- **Context Menus**: Right-click integration -- **Auto-Format**: On-save formatting and import organization -- **Status Bar**: Quick access to workspace refactoring - -#### **3. Python CLI** -```bash -# Direct tool usage -python -m hanzo_mcp.unified_backend fmt ws -python -m hanzo_mcp.unified_backend edit file:main.go --op rename -``` - -#### **4. HTTP API Backend** -```python -# RESTful endpoints for external integration -POST /tools/fmt -POST /tools/edit -POST /tools/test -# etc. -``` - -### ๐Ÿ“ฆ **Installation System** - -#### **One-Command Install** -```bash -python3 install_hanzo_mcp.py --all -``` - -#### **Component Selection** -```bash -# MCP server for Claude -python3 install_hanzo_mcp.py --mcp-server - -# VS Code integration -python3 install_hanzo_mcp.py --vscode - -# Background service -python3 install_hanzo_mcp.py --backend - -# Browser extension -python3 install_hanzo_mcp.py --browser -``` - -#### **Auto-Service Creation** -- **Linux**: Systemd service -- **macOS**: Launchd agent -- **Windows**: Startup integration - -### ๐Ÿ” **LSP Integration** - -#### **Supported Language Servers** -- **Go**: `gopls` -- **TypeScript**: `typescript-language-server` -- **Python**: `pyright` -- **Rust**: `rust-analyzer` -- **C++**: `clangd` -- **Solidity**: `solidity-language-server` - -#### **Unified Operations** -- Cross-file symbol renaming -- Workspace-wide code actions -- Import organization -- Semantic refactoring - -### ๐ŸŽจ **Composition Examples** - -#### **Multi-Language Rename Workflow** -```python -# 1. Rename Go symbol -await mcp.edit("file:api.go", op="rename", ...) - -# 2. Update TypeScript client -await mcp.edit("file:client.ts", op="rename", ...) - -# 3. Format changed files -await mcp.fmt("changed") - -# 4. Run affected tests -await mcp.test("changed") - -# 5. Check boundaries -await mcp.guard("ws") -``` - -#### **VS Code Workspace Refactor** -- **Ctrl+Shift+H R**: Multi-step refactoring wizard -- **Auto-composition**: Organize imports โ†’ Format โ†’ Test โ†’ Guard - -## ๐Ÿš€ **Ready for Production Use** - -### **Immediate Benefits** -1. **AI Assistants** can now perform complex, workspace-aware operations -2. **Developers** get unified tooling across all languages -3. **Teams** benefit from consistent code quality and boundaries -4. **CI/CD** can use the same tools for automated workflows - -### **Extensibility** -- **Plugin System**: Easy to add new tools -- **Language Support**: Straightforward to add new languages -- **Custom Rules**: Flexible guard rule engine -- **Integration**: RESTful API for external tools - -### **Data Intelligence** -- **Session Tracking**: Every operation logged with context -- **Codebase Intelligence**: Automatic symbol and dependency indexing -- **Search & Discovery**: Fast, semantic code search -- **Analytics**: Usage patterns and optimization opportunities - -## ๐ŸŽฏ **What This Enables** - -### **For AI Assistants (Claude, etc.)** -``` -"Rename the UserService class across all Go and TypeScript files, update imports, run tests, and check our API boundary rules" -``` -โ†’ Single command that: -1. Renames in Go via LSP -2. Renames in TypeScript via LSP -3. Organizes imports -4. Formats code -5. Runs affected tests -6. Validates guard rules - -### **For Developers (VS Code)** -- **Intelligent Shortcuts**: `Ctrl+Shift+H F` formats with workspace awareness -- **Cross-Language Operations**: Rename symbols across Go/TS/Python -- **Boundary Enforcement**: Guard rules prevent accidental violations -- **Session Intelligence**: Track and replay complex operations - -### **For Teams** -- **Consistent Tooling**: Same tools across languages and environments -- **Boundary Enforcement**: Automated architecture compliance -- **Knowledge Sharing**: Session logs become team documentation -- **Quality Gates**: Integrated linting and testing - -## ๐ŸŒŸ **Key Innovation Points** - -1. **Universal Target System**: `file:`, `dir:`, `pkg:`, `ws`, `changed` works across all languages -2. **Composition by Design**: Tools are orthogonal and composable -3. **Workspace Intelligence**: Deep understanding of project structure -4. **Session Continuity**: Every operation contributes to codebase knowledge -5. **Multi-Interface**: Same backend powers MCP, VS Code, CLI, and HTTP APIs - ---- - -## ๐Ÿ“‹ **Next Steps for Implementation** - -1. **Complete the remaining tool implementations** (test, build, lint, guard) -2. **Add LSP server management** (auto-start/stop language servers) -3. **Enhance codebase indexing** with language-specific parsers -4. **Create browser extension** for web-based code intelligence -5. **Add team collaboration features** (shared sessions, rule templates) - -This implementation provides the foundation for truly intelligent, AI-powered development workflows that span across languages, tools, and environments. The unified backend architecture ensures consistent behavior whether you're using Claude Desktop, VS Code, or command-line tools. - -**The future of AI-powered development is here! ๐Ÿš€** \ No newline at end of file diff --git a/pkg/hanzo-mcp/MODULAR_ARCHITECTURE.md b/pkg/hanzo-mcp/MODULAR_ARCHITECTURE.md deleted file mode 100644 index 9c99f6ec9..000000000 --- a/pkg/hanzo-mcp/MODULAR_ARCHITECTURE.md +++ /dev/null @@ -1,178 +0,0 @@ -# Modular Plugin Architecture for Hanzo-MCP Memory System - -## Overview -This document outlines a modular plugin architecture for the Hanzo-MCP memory system that allows users to enable specific backends and capabilities as needed, without requiring heavy dependencies by default. - -## Current Architecture -- `hanzo-mcp`: Main MCP server -- `hanzo-tools-memory`: Memory tools package with unified interface -- `hanzo-memory`: Full memory backend implementation with multiple backends - -## Proposed Plugin Architecture - -### 1. Plugin Interface -Define a standard interface for memory backends that can be loaded dynamically: - -```python -from abc import ABC, abstractmethod -from typing import Protocol, Optional, List, Dict, Any - -class MemoryBackendPlugin(Protocol): - """Interface for memory backend plugins.""" - - @property - def name(self) -> str: - """Unique name of the backend.""" - ... - - @property - def capabilities(self) -> List[str]: - """List of capabilities provided by this backend.""" - ... - - async def initialize(self) -> None: - """Initialize the backend.""" - ... - - async def shutdown(self) -> None: - """Shutdown the backend.""" - ... - - async def store_memory(self, content: str, metadata: Dict[str, Any]) -> str: - """Store a memory and return its ID.""" - ... - - async def retrieve_memory(self, query: str, limit: int = 10) -> List[Dict[str, Any]]: - """Retrieve memories based on query.""" - ... -``` - -### 2. Plugin Registry -A central registry to manage available plugins: - -```python -class PluginRegistry: - """Registry for memory backend plugins.""" - - def __init__(self): - self._plugins: Dict[str, MemoryBackendPlugin] = {} - self._active_plugins: List[str] = [] - - def register_plugin(self, plugin: MemoryBackendPlugin): - """Register a new plugin.""" - self._plugins[plugin.name] = plugin - - def enable_plugin(self, name: str) -> bool: - """Enable a registered plugin.""" - if name in self._plugins and name not in self._active_plugins: - self._active_plugins.append(name) - return True - return False - - def get_active_plugins(self) -> List[MemoryBackendPlugin]: - """Get all active plugins.""" - return [self._plugins[name] for name in self._active_plugins] -``` - -### 3. Configuration-Based Loading -Allow users to configure which backends to load via configuration: - -```python -# config.py -from pydantic import BaseModel -from typing import List - -class PluginConfig(BaseModel): - enabled_backends: List[str] = ["sqlite"] # Default to lightweight SQLite - backend_configs: Dict[str, Dict[str, Any]] = { - "sqlite": {"path": "~/.hanzo/memory.db"}, - "lancedb": {"path": "./data/lancedb"}, - "redis": {"url": "redis://localhost:6379"} - } -``` - -### 4. Lazy Loading -Only load plugins when they are actually needed: - -```python -class MemoryService: - """Memory service that uses plugins.""" - - def __init__(self, config: PluginConfig): - self.config = config - self.registry = PluginRegistry() - self._initialized = False - - async def initialize(self): - """Initialize the service and load configured plugins.""" - if self._initialized: - return - - # Register all available plugins - self._register_available_plugins() - - # Enable configured plugins - for backend_name in self.config.enabled_backends: - self.registry.enable_plugin(backend_name) - - # Initialize active plugins - for plugin in self.registry.get_active_plugins(): - await plugin.initialize() - - self._initialized = True - - def _register_available_plugins(self): - """Register all available backend plugins.""" - # Register SQLite plugin - from .backends.sqlite_plugin import SQLiteBackendPlugin - self.registry.register_plugin(SQLiteBackendPlugin()) - - # Conditionally register other plugins if dependencies are available - try: - from .backends.lancedb_plugin import LanceDBBackendPlugin - self.registry.register_plugin(LanceDBBackendPlugin()) - except ImportError: - pass # Skip if dependencies not available - - try: - from .backends.redis_plugin import RedisBackendPlugin - self.registry.register_plugin(RedisBackendPlugin()) - except ImportError: - pass -``` - -### 5. User Experience -Users can enable backends as needed: - -```bash -# Default installation - only lightweight SQLite -pip install hanzo-mcp - -# With full memory capabilities -pip install hanzo-mcp[memory] - -# With specific backends -pip install hanzo-mcp[memory,lancedb,redis] - -# Or configure via config file -echo '{"enabled_backends": ["sqlite", "lancedb"]}' > hanzo_config.json -``` - -## Benefits - -1. **Modularity**: Users only install what they need -2. **Flexibility**: Easy to add new backends -3. **Performance**: Only load necessary components -4. **Scalability**: Different backends for different use cases -5. **Maintainability**: Clear separation of concerns - -## Implementation Steps - -1. Define the plugin interface -2. Create the registry system -3. Implement the lazy loading mechanism -4. Create adapter classes for existing backends -5. Update configuration system -6. Update documentation and examples - -This architecture would allow users to have a lightweight MCP server by default, but enable advanced memory capabilities when needed, without having to rebuild or restart the entire system. \ No newline at end of file diff --git a/pkg/hanzo-mcp/Makefile b/pkg/hanzo-mcp/Makefile deleted file mode 100644 index 851b00234..000000000 --- a/pkg/hanzo-mcp/Makefile +++ /dev/null @@ -1,213 +0,0 @@ -# Hanzo MCP Package Makefile -SHELL := /bin/bash - -# ANSI color codes -GREEN := \033[0;32m -YELLOW := \033[0;33m -RED := \033[0;31m -BLUE := \033[0;34m -RESET := \033[0m - -# Package info -PACKAGE_NAME := hanzo-mcp -CURRENT_VERSION := $(shell grep -E '^version = ' pyproject.toml | sed 's/version = "//g' | sed 's/"//g') - -.PHONY: help -help: ## Show this help message - @printf "$(BLUE)Hanzo MCP Package Commands$(RESET)\n" - @printf "\n" - @printf "$(GREEN)Development:$(RESET)\n" - @printf " make install - Install package in development mode\n" - @printf " make test - Run tests\n" - @printf " make lint - Run linting\n" - @printf " make format - Format code\n" - @printf "\n" - @printf "$(GREEN)Publishing:$(RESET)\n" - @printf " make build - Build distribution packages\n" - @printf " make publish - Publish to PyPI (requires API token)\n" - @printf " make publish-test - Publish to Test PyPI\n" - @printf " make version - Show current version\n" - @printf " make bump-patch - Bump patch version (x.y.Z)\n" - @printf " make bump-minor - Bump minor version (x.Y.0)\n" - @printf " make bump-major - Bump major version (X.0.0)\n" - @printf "\n" - @printf "$(GREEN)Current Version:$(RESET) $(CURRENT_VERSION)\n" - -# ========== Development Commands ========== - -.PHONY: install -install: ## Install package in development mode - @printf "$(GREEN)Installing hanzo-mcp in development mode...$(RESET)\n" - @pip install -e ".[dev,test]" - @printf "$(GREEN)Installation complete!$(RESET)\n" - -.PHONY: test -test: ## Run tests - @printf "$(GREEN)Running tests...$(RESET)\n" - @python -m pytest -v - -.PHONY: test-cov -test-cov: ## Run tests with coverage - @printf "$(GREEN)Running tests with coverage...$(RESET)\n" - @python -m pytest --cov=hanzo_mcp --cov-report=html --cov-report=term - -.PHONY: lint -lint: ## Run linting - @printf "$(GREEN)Running ruff linter...$(RESET)\n" - @ruff check hanzo_mcp/ - -.PHONY: format -format: ## Format code - @printf "$(GREEN)Formatting code...$(RESET)\n" - @ruff format hanzo_mcp/ - @ruff check --fix hanzo_mcp/ - -.PHONY: type-check -type-check: ## Run type checking - @printf "$(GREEN)Running type checks...$(RESET)\n" - @mypy hanzo_mcp/ - -# ========== Build Commands ========== - -.PHONY: clean -clean: ## Clean build artifacts - @printf "$(YELLOW)Cleaning build artifacts...$(RESET)\n" - @rm -rf build/ dist/ *.egg-info .pytest_cache .coverage htmlcov - @find . -type d -name __pycache__ -exec rm -rf {} + - @printf "$(GREEN)Clean complete!$(RESET)\n" - -.PHONY: build -build: clean ## Build distribution packages - @printf "$(GREEN)Building distribution packages...$(RESET)\n" - @python -m build - @printf "$(GREEN)Build complete! Packages in dist/$(RESET)\n" - @ls -la dist/ - -# ========== Version Management ========== - -.PHONY: version -version: ## Show current version - @printf "$(GREEN)Current version: $(CURRENT_VERSION)$(RESET)\n" - -.PHONY: bump-patch -bump-patch: ## Bump patch version (x.y.Z) - @printf "$(YELLOW)Bumping patch version...$(RESET)\n" - @python -c "import re; \ - content = open('pyproject.toml').read(); \ - version = re.search(r'version = \"(.+?)\"', content).group(1); \ - parts = version.split('.'); \ - parts[2] = str(int(parts[2]) + 1); \ - new_version = '.'.join(parts); \ - content = re.sub(r'version = \".+?\"', f'version = \"{new_version}\"', content); \ - open('pyproject.toml', 'w').write(content); \ - print(f'Version bumped from {version} to {new_version}')" - @printf "$(GREEN)Version updated! Don't forget to commit the change.$(RESET)\n" - -.PHONY: bump-minor -bump-minor: ## Bump minor version (x.Y.0) - @printf "$(YELLOW)Bumping minor version...$(RESET)\n" - @python -c "import re; \ - content = open('pyproject.toml').read(); \ - version = re.search(r'version = \"(.+?)\"', content).group(1); \ - parts = version.split('.'); \ - parts[1] = str(int(parts[1]) + 1); \ - parts[2] = '0'; \ - new_version = '.'.join(parts); \ - content = re.sub(r'version = \".+?\"', f'version = \"{new_version}\"', content); \ - open('pyproject.toml', 'w').write(content); \ - print(f'Version bumped from {version} to {new_version}')" - @printf "$(GREEN)Version updated! Don't forget to commit the change.$(RESET)\n" - -.PHONY: bump-major -bump-major: ## Bump major version (X.0.0) - @printf "$(YELLOW)Bumping major version...$(RESET)\n" - @python -c "import re; \ - content = open('pyproject.toml').read(); \ - version = re.search(r'version = \"(.+?)\"', content).group(1); \ - parts = version.split('.'); \ - parts[0] = str(int(parts[0]) + 1); \ - parts[1] = '0'; \ - parts[2] = '0'; \ - new_version = '.'.join(parts); \ - content = re.sub(r'version = \".+?\"', f'version = \"{new_version}\"', content); \ - open('pyproject.toml', 'w').write(content); \ - print(f'Version bumped from {version} to {new_version}')" - @printf "$(GREEN)Version updated! Don't forget to commit the change.$(RESET)\n" - -# ========== Publishing Commands ========== - -.PHONY: check-publish -check-publish: ## Check if ready to publish - @printf "$(YELLOW)Checking publishing requirements...$(RESET)\n" - @which twine > /dev/null || (printf "$(RED)Error: twine not installed. Run: pip install twine$(RESET)\n" && exit 1) - @which python -m build > /dev/null || (printf "$(RED)Error: build not installed. Run: pip install build$(RESET)\n" && exit 1) - @if [ ! -f ~/.pypirc ]; then \ - printf "$(YELLOW)Warning: ~/.pypirc not found. You'll need to enter credentials manually.$(RESET)\n"; \ - fi - @printf "$(GREEN)Publishing tools are installed!$(RESET)\n" - -.PHONY: publish-test -publish-test: check-publish build ## Publish to Test PyPI - @printf "$(YELLOW)Publishing to Test PyPI...$(RESET)\n" - @printf "$(YELLOW)Make sure you have a Test PyPI account and API token$(RESET)\n" - @twine upload --repository testpypi dist/* - @printf "$(GREEN)Published to Test PyPI!$(RESET)\n" - @printf "Install with: pip install -i https://test.pypi.org/simple/ $(PACKAGE_NAME)==$(CURRENT_VERSION)\n" - -.PHONY: publish -publish: check-publish build ## Publish to PyPI - @printf "$(RED)Publishing to PyPI...$(RESET)\n" - @printf "$(YELLOW)This will publish version $(CURRENT_VERSION) to the official PyPI$(RESET)\n" - @read -p "Are you sure? (y/N) " -n 1 -r; \ - echo; \ - if [[ $$REPLY =~ ^[Yy]$$ ]]; then \ - printf "$(YELLOW)Publishing to PyPI...$(RESET)\n"; \ - twine upload dist/*; \ - printf "$(GREEN)Successfully published $(PACKAGE_NAME) $(CURRENT_VERSION) to PyPI!$(RESET)\n"; \ - printf "Install with: pip install $(PACKAGE_NAME)==$(CURRENT_VERSION)\n"; \ - else \ - printf "$(YELLOW)Publishing cancelled.$(RESET)\n"; \ - fi - -.PHONY: release -release: ## Create a new release (runs tests, builds, and publishes) - @printf "$(BLUE)Creating new release for $(PACKAGE_NAME) $(CURRENT_VERSION)...$(RESET)\n" - @make test - @make lint - @make build - @printf "$(GREEN)All checks passed! Ready to publish.$(RESET)\n" - @make publish - -# ========== MCP Specific Commands ========== - -.PHONY: install-desktop -install-desktop: ## Install to Claude Desktop - @printf "$(GREEN)Installing to Claude Desktop...$(RESET)\n" - @python -m hanzo_mcp.cli install-desktop - @printf "$(GREEN)Installation complete! Restart Claude Desktop.$(RESET)\n" - -.PHONY: run-dev -run-dev: ## Run MCP development server - @printf "$(GREEN)Starting MCP development server...$(RESET)\n" - @hanzo-mcp-dev - -.PHONY: test-tools -test-tools: ## Test MCP tools interactively - @printf "$(GREEN)Starting interactive tool testing...$(RESET)\n" - @python -m hanzo_mcp.test_tools - -# ========== Documentation ========== - -.PHONY: docs -docs: ## Build documentation - @printf "$(GREEN)Building documentation...$(RESET)\n" - @cd docs && make html - @printf "$(GREEN)Documentation built! Open docs/_build/html/index.html$(RESET)\n" - -.PHONY: docs-serve -docs-serve: docs ## Build and serve documentation - @printf "$(GREEN)Serving documentation on http://localhost:8000...$(RESET)\n" - @cd docs/_build/html && python -m http.server - -# Default target -.DEFAULT_GOAL := help \ No newline at end of file diff --git a/pkg/hanzo-mcp/README.md b/pkg/hanzo-mcp/README.md deleted file mode 100644 index 124a78228..000000000 --- a/pkg/hanzo-mcp/README.md +++ /dev/null @@ -1,341 +0,0 @@ -# Hanzo Model Context Protocol (MCP) - -[![PyPI](https://img.shields.io/pypi/v/hanzo-mcp.svg)](https://pypi.org/project/hanzo-mcp/) -[![Python Version](https://img.shields.io/pypi/pyversions/hanzo-mcp.svg)](https://pypi.org/project/hanzo-mcp/) - -Model Context Protocol implementation for advanced tool use and context management. - -## Installation - -```bash -pip install hanzo-mcp -``` - -## Features - -- **Tool Management**: Register and manage AI tools -- **File Operations**: Read, write, edit files -- **Code Intelligence**: AST analysis, symbol search -- **Shell Execution**: Run commands safely -- **Agent Delegation**: Recursive agent capabilities -- **Memory Integration**: Persistent context storage -- **Batch Operations**: Execute multiple tools efficiently - -## Quick Start - -### Basic Usage - -```python -from hanzo_mcp import create_mcp_server - -# Create MCP server -server = create_mcp_server() - -# Register tools -server.register_filesystem_tools() -server.register_shell_tools() -server.register_agent_tools() - -# Start server -await server.start() -``` - -### Tool Categories - -#### Filesystem Tools - -```python -# Read file -content = await server.tools.read(file_path="/path/to/file.py") - -# Write file -await server.tools.write( - file_path="/path/to/new.py", - content="print('Hello')" -) - -# Edit file -await server.tools.edit( - file_path="/path/to/file.py", - old_string="old code", - new_string="new code" -) - -# Multi-edit -await server.tools.multi_edit( - file_path="/path/to/file.py", - edits=[ - {"old_string": "foo", "new_string": "bar"}, - {"old_string": "baz", "new_string": "qux"} - ] -) -``` - -#### Search Tools - -```python -# Unified search (grep + AST + semantic) -results = await server.tools.search( - pattern="function_name", - path="/project" -) - -# AST-aware search -results = await server.tools.grep_ast( - pattern="class.*Service", - path="/src" -) - -# Symbol search -symbols = await server.tools.symbols( - pattern="def test_", - path="/tests" -) -``` - -#### Shell Tools - -```python -# Run command -result = await server.tools.bash( - command="ls -la", - cwd="/project" -) - -# Run with auto-backgrounding -result = await server.tools.bash( - command="python server.py", - timeout=120000 # Auto-backgrounds after 2 min -) - -# Manage processes -processes = await server.tools.process(action="list") -logs = await server.tools.process( - action="logs", - id="bash_abc123" -) -``` - -#### Agent Tools - -```python -# Dispatch agent for complex tasks -result = await server.tools.dispatch_agent( - prompt="Analyze the codebase architecture", - path="/project" -) - -# Network of agents -result = await server.tools.network( - task="Implement user authentication", - agents=["architect", "developer", "tester"] -) - -# CLI tool integration -result = await server.tools.claude( - args=["--analyze", "main.py"] -) -``` - -#### Batch Operations - -```python -# Execute multiple tools in parallel -results = await server.tools.batch( - description="Read multiple files", - invocations=[ - {"tool_name": "read", "input": {"file_path": "file1.py"}}, - {"tool_name": "read", "input": {"file_path": "file2.py"}}, - {"tool_name": "grep", "input": {"pattern": "TODO"}} - ] -) -``` - -## Advanced Features - -### Custom Tools - -```python -from hanzo_mcp import Tool - -class MyCustomTool(Tool): - name = "my_tool" - description = "Custom tool" - - async def call(self, ctx, **params): - # Tool implementation - return "Result" - -# Register custom tool -server.register_tool(MyCustomTool()) -``` - -### Permission Management - -```python -from hanzo_mcp import PermissionManager - -# Create permission manager -pm = PermissionManager() - -# Set permission mode -pm.set_mode("review") # review, auto_approve, auto_deny - -# Check permission -allowed = await pm.check_permission( - tool="write", - params={"file_path": "/etc/passwd"} -) -``` - -### Context Management - -```python -from hanzo_mcp import ToolContext - -# Create context -ctx = ToolContext( - cwd="/project", - env={"API_KEY": "secret"}, - timeout=30000 -) - -# Use with tools -result = await tool.call(ctx, **params) -``` - -## Configuration - -### Environment Variables - -```bash -# API keys for agent tools -ANTHROPIC_API_KEY=sk-ant-... -OPENAI_API_KEY=sk-... - -# Tool settings -MCP_PERMISSION_MODE=review -MCP_MAX_FILE_SIZE=10485760 -MCP_TIMEOUT=120000 - -# Search settings -MCP_SEARCH_IGNORE=node_modules,*.pyc -MCP_SEARCH_MAX_RESULTS=100 -``` - -### Configuration File - -```yaml -tools: - filesystem: - enabled: true - max_file_size: 10MB - allowed_paths: - - /home/user/projects - - /tmp - - shell: - enabled: true - timeout: 120000 - auto_background: true - - agent: - enabled: true - models: - - claude-3-opus - - gpt-4 - - search: - ignore_patterns: - - node_modules - - "*.pyc" - - .git - max_results: 100 - -permissions: - mode: review # review, auto_approve, auto_deny - whitelist: - - read - - grep - - search - blacklist: - - rm - - sudo -``` - -## CLI Usage - -### Installation to Claude Desktop - -```bash -# Install to Claude Desktop -hanzo-mcp install-desktop - -# Serve MCP -hanzo-mcp serve --port 3000 -``` - -### Standalone Server - -```bash -# Start MCP server -hanzo-mcp serve - -# With custom config -hanzo-mcp serve --config mcp-config.yaml - -# With specific tools -hanzo-mcp serve --tools filesystem,shell,agent -``` - -## Development - -### Setup - -```bash -cd pkg/hanzo-mcp -uv sync --all-extras -``` - -### Testing - -```bash -# Unit tests -pytest tests/ -v - -# Integration tests -pytest tests/ -m integration - -# With coverage -pytest tests/ --cov=hanzo_mcp -``` - -### Building - -```bash -uv build -``` - -## Architecture - -### Tool Categories - -- **Filesystem**: File operations (read, write, edit) -- **Search**: Code search (grep, AST, semantic) -- **Shell**: Command execution and process management -- **Agent**: AI agent delegation and orchestration -- **Memory**: Context and knowledge persistence -- **Config**: Configuration management -- **LLM**: Direct LLM interactions - -### Security - -- Permission system for dangerous operations -- Path validation and sandboxing -- Command injection protection -- Rate limiting on operations -- Audit logging - -## License - -Apache License 2.0 \ No newline at end of file diff --git a/pkg/hanzo-mcp/SECURITY_PATCH.md b/pkg/hanzo-mcp/SECURITY_PATCH.md deleted file mode 100644 index 9cddfddc0..000000000 --- a/pkg/hanzo-mcp/SECURITY_PATCH.md +++ /dev/null @@ -1,129 +0,0 @@ -# Security Patch for Hanzo MCP v0.8.13 - -## Critical Security Vulnerabilities Found - -### 1. Command Injection (CRITICAL) ๐Ÿšจ -**Location**: `hanzo_mcp/tools/shell/command_executor.py` line 114 -**Issue**: Insufficient shell escaping using basic string replacement -**Risk**: Remote code execution - -**Current Code**: -```python -escaped_command = command.replace('"', '\\"') -``` - -**Fixed Code**: -```python -import shlex -escaped_command = shlex.quote(command) -``` - -### 2. Path Traversal (HIGH) โš ๏ธ -**Location**: `hanzo_mcp/tools/common/permissions.py` -**Issue**: No validation for symlinks and `../` traversal -**Risk**: Unauthorized file access - -**Add validation**: -```python -def validate_path(self, path: str) -> bool: - """Validate path against traversal attacks.""" - resolved = Path(path).resolve() - - # Check for path traversal - if ".." in str(path): - return False - - # Check against allowed paths - for allowed in self.allowed_paths: - try: - resolved.relative_to(allowed) - return True - except ValueError: - continue - - return False -``` - -### 3. Missing Authentication (HIGH) โš ๏ธ -**Location**: `hanzo_mcp/server.py` -**Issue**: MCP server accepts any local connection without auth -**Risk**: Unauthorized access to system - -**Add basic token auth**: -```python -import secrets -import os - -class HanzoMCPServer: - def __init__(self, ...): - self.auth_token = os.environ.get('HANZO_MCP_TOKEN') or secrets.token_urlsafe(32) - - def verify_auth(self, request_token: str) -> bool: - """Verify authentication token.""" - return secrets.compare_digest(request_token, self.auth_token) -``` - -### 4. Subprocess Timeout (MEDIUM) -**Location**: Multiple subprocess.run() calls -**Issue**: Missing consistent timeout enforcement -**Risk**: Resource exhaustion from hanging processes - -**Fix**: -```python -# Always use timeout -result = subprocess.run( - command, - timeout=self.command_timeout, # Default 120s - check=False, - capture_output=True -) -``` - -## Test Results Summary - -- **Total Tests**: 410 collected -- **Test Errors**: 4 collection errors -- **Status**: Tests not fully passing due to environment issues -- **Recommendation**: Fix security issues first, then resolve test environment - -## Immediate Actions Required - -1. **Apply command injection patch** using shlex.quote() -2. **Add path validation** to PermissionManager -3. **Implement token authentication** for MCP server -4. **Add timeout to all subprocess calls** -5. **Add audit logging** for all operations -6. **Implement rate limiting** to prevent abuse - -## Risk Assessment - -| Vulnerability | Severity | Exploitability | Impact | Priority | -|--------------|----------|----------------|---------|----------| -| Command Injection | CRITICAL | High | RCE | P0 | -| Path Traversal | HIGH | Medium | File Access | P1 | -| No Authentication | HIGH | High | Unauthorized Access | P1 | -| Missing Timeouts | MEDIUM | Low | DoS | P2 | - -## Deployment Recommendation - -**DO NOT DEPLOY TO PRODUCTION** until: -1. All CRITICAL and HIGH severity issues are patched -2. Authentication is implemented -3. Test suite is passing (currently has environment issues) -4. Security audit is performed on patched version - -## Next Version Plan (v0.8.14) - -Should include: -- Security patches for all identified vulnerabilities -- Authentication system -- Audit logging -- Rate limiting -- Improved error handling -- Complete test coverage - ---- - -Generated by Code Review Agent -Date: 2024-12-20 -Version Reviewed: hanzo-mcp v0.8.13 \ No newline at end of file diff --git a/pkg/hanzo-mcp/data/lancedb/chat_sessions.lance/_transactions/0-842879eb-3d83-4091-8dc3-4fe207e4b79c.txn b/pkg/hanzo-mcp/data/lancedb/chat_sessions.lance/_transactions/0-842879eb-3d83-4091-8dc3-4fe207e4b79c.txn deleted file mode 100644 index 3a4eaffd6..000000000 --- a/pkg/hanzo-mcp/data/lancedb/chat_sessions.lance/_transactions/0-842879eb-3d83-4091-8dc3-4fe207e4b79c.txn +++ /dev/null @@ -1,7 +0,0 @@ -$842879eb-3d83-4091-8dc3-4fe207e4b79cฒู* -session_id *string8Zdefault)user_id *string8Zdefault, -project_id *string8Zdefault*metadata *string8Zdefault, -created_at *string8Zdefault, -updated_at *string8Zdefault"' -lance.auto_cleanup.older_than14days"! -lance.auto_cleanup.interval20 \ No newline at end of file diff --git a/pkg/hanzo-mcp/data/lancedb/chat_sessions.lance/_versions/1.manifest b/pkg/hanzo-mcp/data/lancedb/chat_sessions.lance/_versions/1.manifest deleted file mode 100644 index b7c967060..000000000 Binary files a/pkg/hanzo-mcp/data/lancedb/chat_sessions.lance/_versions/1.manifest and /dev/null differ diff --git a/pkg/hanzo-mcp/data/lancedb/knowledge_bases.lance/_transactions/0-065b57cf-4b8b-4ea4-b874-dbfe3f60e299.txn b/pkg/hanzo-mcp/data/lancedb/knowledge_bases.lance/_transactions/0-065b57cf-4b8b-4ea4-b874-dbfe3f60e299.txn deleted file mode 100644 index ab8db7f4c..000000000 --- a/pkg/hanzo-mcp/data/lancedb/knowledge_bases.lance/_transactions/0-065b57cf-4b8b-4ea4-b874-dbfe3f60e299.txn +++ /dev/null @@ -1,6 +0,0 @@ -$065b57cf-4b8b-4ea4-b874-dbfe3f60e299ฒŒ1knowledge_base_id *string8Zdefault, -project_id *string8Zdefault&name *string8Zdefault- description *string8Zdefault*metadata *string8Zdefault, -created_at *string8Zdefault, -updated_at *string8Zdefault"' -lance.auto_cleanup.older_than14days"! -lance.auto_cleanup.interval20 \ No newline at end of file diff --git a/pkg/hanzo-mcp/data/lancedb/knowledge_bases.lance/_versions/1.manifest b/pkg/hanzo-mcp/data/lancedb/knowledge_bases.lance/_versions/1.manifest deleted file mode 100644 index f125bdeab..000000000 Binary files a/pkg/hanzo-mcp/data/lancedb/knowledge_bases.lance/_versions/1.manifest and /dev/null differ diff --git a/pkg/hanzo-mcp/data/lancedb/projects.lance/_transactions/0-37711a1a-9eb9-43b5-af6a-bb597b4c5505.txn b/pkg/hanzo-mcp/data/lancedb/projects.lance/_transactions/0-37711a1a-9eb9-43b5-af6a-bb597b4c5505.txn deleted file mode 100644 index 5d91c837a..000000000 --- a/pkg/hanzo-mcp/data/lancedb/projects.lance/_transactions/0-37711a1a-9eb9-43b5-af6a-bb597b4c5505.txn +++ /dev/null @@ -1,6 +0,0 @@ -$37711a1a-9eb9-43b5-af6a-bb597b4c5505ฒ‚* -project_id *string8Zdefault)user_id *string8Zdefault&name *string8Zdefault- description *string8Zdefault*metadata *string8Zdefault, -created_at *string8Zdefault, -updated_at *string8Zdefault"! -lance.auto_cleanup.interval20"' -lance.auto_cleanup.older_than14days \ No newline at end of file diff --git a/pkg/hanzo-mcp/data/lancedb/projects.lance/_versions/1.manifest b/pkg/hanzo-mcp/data/lancedb/projects.lance/_versions/1.manifest deleted file mode 100644 index 5af7d90ea..000000000 Binary files a/pkg/hanzo-mcp/data/lancedb/projects.lance/_versions/1.manifest and /dev/null differ diff --git a/pkg/hanzo-mcp/demo_exact_tools.py b/pkg/hanzo-mcp/demo_exact_tools.py deleted file mode 100644 index 11fe44827..000000000 --- a/pkg/hanzo-mcp/demo_exact_tools.py +++ /dev/null @@ -1,796 +0,0 @@ -#!/usr/bin/env python3 -""" -Demonstration of Exact 6-Tool Implementation (Standalone) -========================================================= - -Core implementation of the 6 universal tools without external dependencies. -""" - -import asyncio -import json -import os -import subprocess -import tempfile -from dataclasses import asdict, dataclass -from pathlib import Path -from typing import Any, Dict, List, Optional, Union - - -@dataclass -class ToolResult: - """Standard result format for all tools""" - - ok: bool - root: str - language_used: Union[str, List[str]] - backend_used: Union[str, List[str]] - scope_resolved: Union[str, List[str]] - touched_files: List[str] - stdout: str - stderr: str - exit_code: int - errors: List[str] - execution_time: float = 0.0 - - -class WorkspaceDetector: - """Intelligent workspace detection with go.work priority""" - - @staticmethod - def detect(target_path: str) -> Dict[str, Any]: - """Detect workspace root, preferring go.work""" - path = Path(target_path).absolute() - - if path.is_file(): - path = path.parent - - # Check for go.work first (highest priority) - for current in [path] + list(path.parents): - if (current / "go.work").exists(): - return { - "root": str(current), - "type": "go_workspace", - "language": "go", - "go_work_file": str(current / "go.work"), - } - - # Then check other workspace types - for current in [path] + list(path.parents): - if (current / "go.mod").exists(): - return {"root": str(current), "type": "go_module", "language": "go"} - elif (current / "package.json").exists(): - return {"root": str(current), "type": "node_project", "language": "ts"} - elif (current / "pyproject.toml").exists(): - return { - "root": str(current), - "type": "python_project", - "language": "py", - } - elif (current / "Cargo.toml").exists(): - return {"root": str(current), "type": "rust_project", "language": "rs"} - - return {"root": str(path), "type": "directory", "language": "auto"} - - -class BackendSelector: - """Selects appropriate backend tools for each language/operation""" - - @staticmethod - def select_backend(language: str, tool: str) -> str: - """Select backend tool for language and operation""" - backend_map = { - "go": { - "fmt": "goimports", - "test": "go test", - "build": "go build", - "lint": "golangci-lint", - "edit": "gopls", - }, - "ts": { - "fmt": "prettier", - "test": "npm test", - "build": "tsc", - "lint": "eslint", - "edit": "typescript-language-server", - }, - "py": { - "fmt": "ruff format", - "test": "pytest", - "build": "python -m build", - "lint": "ruff check", - "edit": "pyright", - }, - "rs": { - "fmt": "cargo fmt", - "test": "cargo test", - "build": "cargo build", - "lint": "cargo clippy", - "edit": "rust-analyzer", - }, - } - - return backend_map.get(language, {}).get(tool, "unknown") - - -class TargetResolver: - """Resolves target specifications to concrete file lists""" - - def __init__(self, workspace_detector: WorkspaceDetector): - self.workspace_detector = workspace_detector - - def resolve( - self, target: str, language: str = "auto", root: Optional[str] = None - ) -> Dict[str, Any]: - """Resolve target specification""" - if target.startswith("file:"): - return self._resolve_file(target[5:], language, root) - elif target.startswith("dir:"): - return self._resolve_dir(target[4:], language, root) - elif target.startswith("pkg:"): - return self._resolve_package(target[4:], language, root) - elif target == "ws": - return self._resolve_workspace(language, root) - elif target == "changed": - return self._resolve_changed(language, root) - else: - raise ValueError(f"Unknown target format: {target}") - - def _resolve_file( - self, file_path: str, language: str, root: Optional[str] - ) -> Dict[str, Any]: - """Resolve single file""" - path = Path(file_path).absolute() - workspace = self.workspace_detector.detect(str(path)) - - return { - "type": "file", - "paths": [str(path)], - "workspace": workspace, - "language": self._infer_language(path, workspace, language), - } - - def _resolve_dir( - self, dir_path: str, language: str, root: Optional[str] - ) -> Dict[str, Any]: - """Resolve directory subtree""" - path = Path(dir_path).absolute() - workspace = self.workspace_detector.detect(str(path)) - - # Find relevant files - extensions = self._get_extensions_for_language(language) - files = [] - for ext in extensions: - files.extend(path.rglob(f"*{ext}")) - - return { - "type": "directory", - "paths": [str(f) for f in files], - "workspace": workspace, - "language": language, - } - - def _resolve_workspace(self, language: str, root: Optional[str]) -> Dict[str, Any]: - """Resolve workspace root""" - workspace = self.workspace_detector.detect(root or ".") - root_path = Path(workspace["root"]) - - extensions = self._get_extensions_for_language(language) - files = [] - for ext in extensions: - files.extend(root_path.rglob(f"*{ext}")) - - return { - "type": "workspace", - "paths": [str(f) for f in files], - "workspace": workspace, - "language": language, - } - - def _resolve_package( - self, pkg_spec: str, language: str, root: Optional[str] - ) -> Dict[str, Any]: - """Resolve package specification (simplified)""" - workspace = self.workspace_detector.detect(root or ".") - - if language == "go" and pkg_spec == "./...": - # All Go packages in workspace - return self._resolve_workspace("go", workspace["root"]) - - # Default to directory resolution - return self._resolve_dir(pkg_spec, language, root) - - def _resolve_changed(self, language: str, root: Optional[str]) -> Dict[str, Any]: - """Resolve git changed files""" - workspace = self.workspace_detector.detect(root or ".") - - try: - result = subprocess.run( - ["git", "diff", "--name-only", "HEAD"], - cwd=workspace["root"], - capture_output=True, - text=True, - check=True, - ) - changed_files = ( - result.stdout.strip().split("\n") if result.stdout.strip() else [] - ) - - # Make paths absolute - root_path = Path(workspace["root"]) - absolute_files = [ - str(root_path / f) for f in changed_files if (root_path / f).exists() - ] - - return { - "type": "changed", - "paths": absolute_files, - "workspace": workspace, - "language": language, - } - except subprocess.CalledProcessError: - return { - "type": "changed", - "paths": [], - "workspace": workspace, - "language": language, - "error": "Not a git repository", - } - - def _infer_language(self, path: Path, workspace: Dict, language_hint: str) -> str: - """Infer language from context""" - if language_hint != "auto": - return language_hint - - if workspace["language"] != "auto": - return workspace["language"] - - # Infer from extension - if path.is_file(): - extension_map = { - ".go": "go", - ".ts": "ts", - ".js": "ts", - ".py": "py", - ".rs": "rs", - } - return extension_map.get(path.suffix, "auto") - - return "auto" - - def _get_extensions_for_language(self, language: str) -> List[str]: - """Get file extensions for language""" - extension_map = { - "go": [".go"], - "ts": [".ts", ".js"], - "py": [".py"], - "rs": [".rs"], - "auto": [".go", ".ts", ".js", ".py", ".rs"], - } - return extension_map.get(language, [".go", ".ts", ".py"]) - - -class SimpleTools: - """Simplified implementation of the 6 universal tools""" - - def __init__(self): - self.workspace_detector = WorkspaceDetector() - self.target_resolver = TargetResolver(self.workspace_detector) - self.backend_selector = BackendSelector() - - def fmt( - self, - target: str, - language: str = "auto", - dry_run: bool = False, - opts: Optional[Dict] = None, - ) -> ToolResult: - """Format tool: formatting + import normalization""" - try: - resolved = self.target_resolver.resolve(target, language) - workspace = resolved["workspace"] - detected_language = resolved["language"] - - backend = self.backend_selector.select_backend(detected_language, "fmt") - - if dry_run: - return ToolResult( - ok=True, - root=workspace["root"], - language_used=detected_language, - backend_used=backend, - scope_resolved=resolved["paths"], - touched_files=[], - stdout=f"Would format {len(resolved['paths'])} files with {backend}", - stderr="", - exit_code=0, - errors=[], - ) - - # Mock formatting (would run actual formatter) - touched_files = resolved["paths"][:3] # Simulate some files being formatted - - return ToolResult( - ok=True, - root=workspace["root"], - language_used=detected_language, - backend_used=backend, - scope_resolved=resolved["paths"], - touched_files=touched_files, - stdout=f"Formatted {len(touched_files)} files", - stderr="", - exit_code=0, - errors=[], - ) - - except Exception as e: - return ToolResult( - ok=False, - root=".", - language_used="unknown", - backend_used="unknown", - scope_resolved=[], - touched_files=[], - stdout="", - stderr=str(e), - exit_code=1, - errors=[str(e)], - ) - - def test( - self, - target: str, - language: str = "auto", - dry_run: bool = False, - opts: Optional[Dict] = None, - ) -> ToolResult: - """Test tool: run tests narrowly by default""" - try: - resolved = self.target_resolver.resolve(target, language) - workspace = resolved["workspace"] - detected_language = resolved["language"] - - backend = self.backend_selector.select_backend(detected_language, "test") - - if dry_run: - return ToolResult( - ok=True, - root=workspace["root"], - language_used=detected_language, - backend_used=backend, - scope_resolved=resolved["paths"], - touched_files=[], - stdout=f"Would run tests with {backend}", - stderr="", - exit_code=0, - errors=[], - ) - - # Mock test run - return ToolResult( - ok=True, - root=workspace["root"], - language_used=detected_language, - backend_used=backend, - scope_resolved=resolved["paths"], - touched_files=[], - stdout=f"Tests passed for {len(resolved['paths'])} files", - stderr="", - exit_code=0, - errors=[], - ) - - except Exception as e: - return ToolResult( - ok=False, - root=".", - language_used="unknown", - backend_used="unknown", - scope_resolved=[], - touched_files=[], - stdout="", - stderr=str(e), - exit_code=1, - errors=[str(e)], - ) - - def edit( - self, - target: str, - op: str, - language: str = "auto", - dry_run: bool = False, - **kwargs, - ) -> ToolResult: - """Edit tool: semantic refactors via LSP""" - try: - resolved = self.target_resolver.resolve(target, language) - workspace = resolved["workspace"] - detected_language = resolved["language"] - - backend = self.backend_selector.select_backend(detected_language, "edit") - - if op == "organize_imports": - touched_files = resolved["paths"] if not dry_run else [] - action_desc = ( - "Would organize imports" if dry_run else "Organized imports" - ) - elif op == "rename": - touched_files = ( - [kwargs.get("file", "")] - if not dry_run and kwargs.get("file") - else [] - ) - new_name = kwargs.get("new_name", "NewName") - action_desc = ( - f"Would rename to {new_name}" - if dry_run - else f"Renamed to {new_name}" - ) - else: - action_desc = f"Would perform {op}" if dry_run else f"Performed {op}" - touched_files = [] - - return ToolResult( - ok=True, - root=workspace["root"], - language_used=detected_language, - backend_used=backend, - scope_resolved=resolved["paths"], - touched_files=touched_files, - stdout=action_desc, - stderr="", - exit_code=0, - errors=[], - ) - - except Exception as e: - return ToolResult( - ok=False, - root=".", - language_used="unknown", - backend_used="unknown", - scope_resolved=[], - touched_files=[], - stdout="", - stderr=str(e), - exit_code=1, - errors=[str(e)], - ) - - def build( - self, - target: str, - language: str = "auto", - dry_run: bool = False, - opts: Optional[Dict] = None, - ) -> ToolResult: - """Build tool: compile/build artifacts""" - try: - resolved = self.target_resolver.resolve(target, language) - workspace = resolved["workspace"] - detected_language = resolved["language"] - - backend = self.backend_selector.select_backend(detected_language, "build") - - action = "Would build" if dry_run else "Built" - - return ToolResult( - ok=True, - root=workspace["root"], - language_used=detected_language, - backend_used=backend, - scope_resolved=resolved["paths"], - touched_files=[], - stdout=f"{action} project with {backend}", - stderr="", - exit_code=0, - errors=[], - ) - - except Exception as e: - return ToolResult( - ok=False, - root=".", - language_used="unknown", - backend_used="unknown", - scope_resolved=[], - touched_files=[], - stdout="", - stderr=str(e), - exit_code=1, - errors=[str(e)], - ) - - def lint( - self, - target: str, - language: str = "auto", - dry_run: bool = False, - opts: Optional[Dict] = None, - ) -> ToolResult: - """Lint tool: lint/typecheck in one place""" - try: - resolved = self.target_resolver.resolve(target, language) - workspace = resolved["workspace"] - detected_language = resolved["language"] - - backend = self.backend_selector.select_backend(detected_language, "lint") - - action = "Would lint" if dry_run else "Linted" - - return ToolResult( - ok=True, - root=workspace["root"], - language_used=detected_language, - backend_used=backend, - scope_resolved=resolved["paths"], - touched_files=[], - stdout=f"{action} {len(resolved['paths'])} files with {backend}", - stderr="", - exit_code=0, - errors=[], - ) - - except Exception as e: - return ToolResult( - ok=False, - root=".", - language_used="unknown", - backend_used="unknown", - scope_resolved=[], - touched_files=[], - stdout="", - stderr=str(e), - exit_code=1, - errors=[str(e)], - ) - - def guard( - self, - target: str, - rules: List[Dict], - language: str = "auto", - dry_run: bool = False, - ) -> ToolResult: - """Guard tool: repo invariants""" - try: - resolved = self.target_resolver.resolve(target, language) - workspace = resolved["workspace"] - - violations = [] - for rule in rules: - # Simplified rule checking - if rule.get("type") == "import" and rule.get("forbid_import_prefix"): - # Mock finding violations - violations.append( - f"Found forbidden import '{rule['forbid_import_prefix']}' in files matching {rule['glob']}" - ) - - return ToolResult( - ok=len(violations) == 0, - root=workspace["root"], - language_used=language, - backend_used="guard", - scope_resolved=resolved["paths"], - touched_files=[], - stdout=f"Checked {len(rules)} rules, found {len(violations)} violations", - stderr="", - exit_code=0 if len(violations) == 0 else 1, - errors=violations, - ) - - except Exception as e: - return ToolResult( - ok=False, - root=".", - language_used="unknown", - backend_used="guard", - scope_resolved=[], - touched_files=[], - stdout="", - stderr=str(e), - exit_code=1, - errors=[str(e)], - ) - - -async def demo_exact_tools(): - """Demonstrate the exact 6-tool implementation""" - print("๐Ÿš€ Demonstrating Exact 6-Tool Implementation\n") - - tools = SimpleTools() - - # Create a temporary workspace - with tempfile.TemporaryDirectory() as temp_dir: - workspace_dir = Path(temp_dir) - - # Create Go workspace structure - print("๐Ÿ“ Creating test Go workspace...") - - # go.work file (highest priority) - go_work = workspace_dir / "go.work" - go_work.write_text("""go 1.21 - -use ( - ./api - ./cli -) -""") - - # API module - api_dir = workspace_dir / "api" - api_dir.mkdir() - - api_mod = api_dir / "go.mod" - api_mod.write_text("module github.com/luxfi/api\n\ngo 1.21\n") - - api_main = api_dir / "main.go" - api_main.write_text("""package main - -import ( - "fmt" - "net/http" -) - -func UserHandler(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello User") -} -""") - - # CLI module - cli_dir = workspace_dir / "cli" - cli_dir.mkdir() - - cli_mod = cli_dir / "go.mod" - cli_mod.write_text("module github.com/luxfi/cli\n\ngo 1.21\n") - - cli_main = cli_dir / "main.go" - cli_main.write_text("""package main - -import "fmt" - -func GreetUser(name string) { - fmt.Printf("Hello %s\\n", name) -} -""") - - print(f" Workspace created at: {workspace_dir}") - print(f" go.work file: {go_work.exists()}") - - # Test workspace detection - print("\n๐Ÿ” Testing workspace detection...") - workspace = tools.workspace_detector.detect(str(workspace_dir)) - print(f" Detected type: {workspace['type']}") - print(f" Root: {workspace['root']}") - print(f" Language: {workspace['language']}") - print(" โœ… go.work detected correctly") - - # Test target resolution - print("\n๐ŸŽฏ Testing target resolution...") - - test_targets = [ - ("ws", "Entire workspace"), - (f"file:{api_main}", "Single file"), - (f"dir:{api_dir}", "Directory"), - ("pkg:./...", "All packages"), - ("changed", "Git changed files"), - ] - - for target, description in test_targets: - try: - resolved = tools.target_resolver.resolve( - target, root=str(workspace_dir) - ) - print( - f" {description}: {len(resolved['paths'])} files, type: {resolved['type']}" - ) - except Exception as e: - print(f" {description}: โŒ {e}") - - # Test all 6 tools - print("\n๐Ÿ”ง Testing all 6 tools (dry-run)...") - - os.chdir(workspace_dir) # Change to workspace for git commands - - # Initialize git repo for 'changed' target to work - subprocess.run(["git", "init"], capture_output=True) - subprocess.run(["git", "add", "."], capture_output=True) - subprocess.run(["git", "commit", "-m", "Initial commit"], capture_output=True) - - # Test each tool - test_cases = [ - ( - "fmt", - tools.fmt, - { - "target": "ws", - "dry_run": True, - "opts": {"local_prefix": "github.com/luxfi"}, - }, - ), - ( - "edit", - tools.edit, - { - "target": "file:" + str(api_main), - "op": "organize_imports", - "dry_run": True, - }, - ), - ("test", tools.test, {"target": "pkg:./...", "dry_run": True}), - ("build", tools.build, {"target": "ws", "dry_run": True}), - ("lint", tools.lint, {"target": "changed", "dry_run": True}), - ( - "guard", - tools.guard, - { - "target": "ws", - "rules": [ - { - "id": "no-net-http", - "type": "import", - "glob": "api/*.go", - "forbid_import_prefix": "net/http", - } - ], - "dry_run": True, - }, - ), - ] - - for tool_name, tool_func, kwargs in test_cases: - try: - result = tool_func(**kwargs) - status = "โœ…" if result.ok else "โŒ" - print( - f" {tool_name}: {status} {result.backend_used} - {result.stdout}" - ) - except Exception as e: - print(f" {tool_name}: โŒ {e}") - - # Test composition workflow - print("\n๐Ÿ”„ Testing composition workflow...") - print(" Workflow: edit(organize_imports) โ†’ fmt โ†’ test โ†’ guard") - - # Step 1: Organize imports - edit_result = tools.edit(target="ws", op="organize_imports", dry_run=True) - print(f" 1. organize_imports: {'โœ…' if edit_result.ok else 'โŒ'}") - - # Step 2: Format (use 'changed' from previous step) - fmt_result = tools.fmt(target="changed", dry_run=True) - print(f" 2. format changed: {'โœ…' if fmt_result.ok else 'โŒ'}") - - # Step 3: Test affected packages - test_result = tools.test(target="pkg:./...", dry_run=True) - print(f" 3. test packages: {'โœ…' if test_result.ok else 'โŒ'}") - - # Step 4: Check guard rules - guard_result = tools.guard( - target="ws", - rules=[ - { - "id": "no-http", - "type": "import", - "glob": "**/*.go", - "forbid_import_prefix": "net/http", - } - ], - dry_run=True, - ) - print( - f" 4. guard check: {'โœ…' if guard_result.ok else 'โŒ'} ({len(guard_result.errors)} violations)" - ) - - print("\n๐ŸŽ‰ Demonstration complete!") - print("\nโœจ Key Features Demonstrated:") - print(" โ€ข Workspace detection with go.work priority") - print(" โ€ข Target resolution (file:, dir:, pkg:, ws, changed)") - print(" โ€ข Backend selection (language-specific tools)") - print(" โ€ข All 6 universal tools (edit, fmt, test, build, lint, guard)") - print(" โ€ข Tool composition workflows") - print(" โ€ข Consistent input/output schemas") - print(" โ€ข Dry-run support") - - -if __name__ == "__main__": - asyncio.run(demo_exact_tools()) diff --git a/pkg/hanzo-mcp/demo_memory_system.py b/pkg/hanzo-mcp/demo_memory_system.py deleted file mode 100644 index dca37b029..000000000 --- a/pkg/hanzo-mcp/demo_memory_system.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env python3 -""" -Demonstration of the Hanzo MCP Memory System with Modular Plugin Architecture. - -This script shows how users can: -1. Install hanzo-mcp with memory support -2. Configure memory backends -3. Use the memory system -""" - -import asyncio -import json -from pathlib import Path - -from hanzo_mcp.config import get_global_config_path, load_config, save_global_config -from hanzo_mcp.memory_service import PluginMemoryService - - -async def demo_memory_system(): - """Demonstrate the memory system usage.""" - print("๐Ÿš€ Hanzo MCP Memory System Demo") - print("=" * 50) - - # Step 1: Show configuration file location - config_path = get_global_config_path() - print(f"๐Ÿ“ Configuration file location: {config_path}") - - # Step 2: Create a sample configuration - print("\n๐Ÿ”ง Creating sample configuration...") - sample_config = { - "enabled_backends": ["sqlite"], # Enable SQLite backend - "backend_configs": { - "sqlite": { - "enabled": True, - "path": str(Path.home() / ".hanzo" / "memory.db"), - "settings": {"auto_migrate": True, "connection_timeout": 30}, - } - }, - "default_user_id": "demo-user", - "default_project_id": "demo-project", - } - - print("๐Ÿ“‹ Sample configuration:") - print(json.dumps(sample_config, indent=2)) - - # Step 3: Save and load configuration - print(f"\n๐Ÿ’พ Saving configuration to {config_path}") - with open(config_path, "w") as f: - json.dump(sample_config, f, indent=2) - - print("๐Ÿ”„ Loading configuration...") - config = load_config() - print(f" Enabled backends: {config.enabled_backends}") - - # Step 4: Initialize memory service - print("\nโšก Initializing memory service...") - service = PluginMemoryService() - await service.initialize(enabled_backends=config.enabled_backends) - print(f" Active backends: {service.get_active_backends()}") - - # Step 5: Demonstrate memory operations - print("\n๐Ÿง  Demonstrating memory operations...") - - # Store a memory - memory_id = await service.store_memory( - content="The Hanzo MCP modular architecture enables flexible backend selection", - metadata={"type": "architecture", "domain": "mcp", "importance": 0.9}, - user_id=config.default_user_id, - project_id=config.default_project_id, - ) - print(f" Stored memory with ID: {memory_id[:8]}...") - - # Store another memory - memory_id2 = await service.store_memory( - content="SQLite backend provides lightweight persistence with vector search", - metadata={"type": "backend", "domain": "storage", "importance": 0.8}, - user_id=config.default_user_id, - project_id=config.default_project_id, - ) - print(f" Stored memory with ID: {memory_id2[:8]}...") - - # Retrieve memories - results = await service.retrieve_memory( - query="architecture", - user_id=config.default_user_id, - project_id=config.default_project_id, - limit=5, - ) - print(f" Retrieved {len(results)} memories for 'architecture'") - - # Step 6: Show available backends and capabilities - print("\n๐Ÿงฉ Available backends and capabilities:") - print(f" Available: {service.get_available_backends()}") - print(f" With persistence: {service.has_capability('persistence')}") - print(f" With vector search: {service.has_capability('vector_search')}") - print(f" With embeddings: {service.has_capability('embeddings')}") - - # Step 7: Demonstrate backend management - print("\nโš™๏ธ Backend management:") - print(f" Current active: {service.get_active_backends()}") - - # Disable and re-enable - service.disable_backend("sqlite") - print(f" After disabling SQLite: {service.get_active_backends()}") - - service.enable_backend("sqlite") - print(f" After re-enabling SQLite: {service.get_active_backends()}") - - # Step 8: Cleanup - print("\n๐Ÿงน Cleaning up...") - await service.shutdown() - - # Remove demo config - if config_path.exists(): - config_path.unlink() - print(f" Removed config file: {config_path}") - - print("\nโœจ Demo completed successfully!") - print("\n๐Ÿ’ก Usage:") - print(" 1. Install with memory support: pip install hanzo-mcp[memory]") - print(" 2. Configure backends in ~/.config/hanzo/mcp-settings.json") - print(" 3. Use the memory system with modular backend support") - print(" 4. Enable/disable backends as needed") - - -if __name__ == "__main__": - asyncio.run(demo_memory_system()) diff --git a/pkg/hanzo-mcp/docs/COMPREHENSIVE_DOCUMENTATION.md b/pkg/hanzo-mcp/docs/COMPREHENSIVE_DOCUMENTATION.md deleted file mode 100644 index 4031cf65e..000000000 --- a/pkg/hanzo-mcp/docs/COMPREHENSIVE_DOCUMENTATION.md +++ /dev/null @@ -1,1045 +0,0 @@ -# ๐Ÿฅท Hanzo MCP - Comprehensive Documentation - -## Table of Contents - -1. [Introduction](#introduction) -2. [Installation Guide](#installation-guide) -3. [Configuration Options](#configuration-options) -4. [API Reference](#api-reference) -5. [Example Use Cases](#example-use-cases) -6. [Troubleshooting Guide](#troubleshooting-guide) -7. [Architecture Overview](#architecture-overview) -8. [Advanced Topics](#advanced-topics) - ---- - -## Introduction - -Hanzo MCP (Model Context Protocol) is a comprehensive ecosystem of interconnected development tools designed for the AI era. It provides a unified interface to orchestrate your entire development workflow through the Model Context Protocol standard. - -### Key Features - -- **70+ Integrated Tools**: From file operations to AI orchestration -- **Multi-Language Support**: Python, JavaScript, Go, R, and more -- **Interactive Development**: Notebooks, REPL, and debugging -- **AI-Native**: Built-in agent systems and LLM integration -- **Extensible**: Add any MCP server or custom tool -- **Quality Focused**: Automated review, testing, and best practices - -### Why Hanzo MCP? - -Traditional development environments suffer from: -- Fragmented tools that don't communicate -- Context switching between interfaces -- No unified workflow orchestration -- Missing tool composition capabilities - -Hanzo MCP solves these problems by providing: -- **Unified Ecosystem**: All tools work together seamlessly -- **Intelligent Orchestration**: Context-aware tool collaboration -- **Interactive Development**: From REPL to debugging in one interface -- **Built-in Quality**: Automated review and testing -- **Extensible Platform**: Add any MCP server or custom tool - ---- - -## Installation Guide - -### Prerequisites - -- Python 3.12 or higher -- Git -- Node.js (optional, for npx support) - -### Quick Installation - -#### Method 1: Using uvx (Recommended) - -```bash -# Install and run Hanzo MCP with one command -uvx hanzo-mcp - -# Note: If uvx is not installed, Hanzo will automatically install it for you -``` - -#### Method 2: Using pip - -```bash -# Install globally -pip install hanzo-mcp - -# Or install in a virtual environment -python -m venv .venv -source .venv/bin/activate # On Windows: .venv\Scripts\activate -pip install hanzo-mcp -``` - -#### Method 3: From Source - -```bash -# Clone the repository -git clone https://github.com/hanzoai/mcp.git -cd mcp - -# Install in development mode -pip install -e . - -# Or use uv for development -uv pip install -e . -``` - -#### Method 4: Desktop Extension (One-Click) - -1. Download the latest `.dxt` file from [releases](https://github.com/hanzoai/mcp/releases) -2. Double-click to install -3. The extension will be available in your desktop environment - -### Post-Installation Setup - -1. **Initialize Configuration**: -```bash -hanzo-mcp --init -``` - -2. **Verify Installation**: -```bash -hanzo-mcp --version -hanzo-mcp --health -``` - -3. **Configure Claude Desktop** (if using): -```bash -# Add to Claude Desktop's config -hanzo-mcp --claude-config -``` - -### Installation Options - -```bash -# Install with specific features -pip install hanzo-mcp[agents] # Agent support -pip install hanzo-mcp[memory] # Memory system -pip install hanzo-mcp[analytics] # Analytics tracking -pip install hanzo-mcp[dev] # Development tools -pip install hanzo-mcp[all] # Everything -``` - ---- - -## Configuration Options - -### Configuration File Locations - -Hanzo MCP looks for configuration in the following order (highest priority first): - -1. CLI arguments -2. Environment variables -3. Project-specific config (`.hanzo/mcp-settings.json`) -4. Global config (`~/.config/hanzo/mcp-settings.json`) -5. Default settings - -### Global Configuration - -Create or edit `~/.config/hanzo/mcp-settings.json`: - -```json -{ - "server": { - "name": "hanzo-mcp", - "host": "127.0.0.1", - "port": 8888, - "transport": "stdio", - "log_level": "INFO", - "command_timeout": 120.0 - }, - - "allowed_paths": [ - "~/projects", - "~/work", - "/tmp" - ], - - "enabled_tools": { - "read": true, - "write": true, - "edit": true, - "search": true, - "agent": true, - "critic": true - }, - - "disabled_tools": [ - "dangerous_tool" - ], - - "agent": { - "enabled": true, - "model": "gpt-4", - "api_key": "your-api-key-here", - "max_iterations": 10, - "max_tool_uses": 30 - }, - - "vector_store": { - "enabled": true, - "provider": "infinity", - "embedding_model": "text-embedding-3-small", - "chunk_size": 1000, - "chunk_overlap": 200 - }, - - "mcp_servers": { - "github": { - "name": "github", - "command": "npx", - "args": ["@modelcontextprotocol/server-github"], - "enabled": true, - "trusted": true, - "description": "GitHub integration" - } - } -} -``` - -### Environment Variables - -All configuration options can be set via environment variables: - -```bash -# Server configuration -export HANZO_MCP_HOST="127.0.0.1" -export HANZO_MCP_PORT="8888" -export HANZO_MCP_LOG_LEVEL="DEBUG" - -# API Keys -export OPENAI_API_KEY="sk-..." -export ANTHROPIC_API_KEY="sk-ant-..." -export HANZO_API_KEY="hanzo-..." - -# Paths -export HANZO_MCP_PROJECT_DIR="/path/to/project" -export HANZO_MCP_ALLOWED_PATHS="/home/user/projects,/tmp" - -# Tool configuration -export HANZO_MCP_DISABLED_TOOLS="dangerous_tool,another_tool" -export HANZO_MCP_AGENT_MODEL="gpt-4" -``` - -### Project-Specific Configuration - -Create `.hanzo/mcp-settings.json` in your project root: - -```json -{ - "project": { - "name": "my-project", - "root_path": ".", - "rules": [ - ".cursorrules", - ".claude/code.md" - ], - "enabled_tools": { - "agent": true, - "critic": true - }, - "disabled_tools": ["rm", "dangerous_operation"], - "mcp_servers": ["github", "linear"] - } -} -``` - -### CLI Configuration - -```bash -# Set configuration via CLI -hanzo-mcp config set agent.model "gpt-4" -hanzo-mcp config set server.port 9999 -hanzo-mcp config add allowed_paths "/new/path" - -# View configuration -hanzo-mcp config show -hanzo-mcp config get agent.model - -# Reset configuration -hanzo-mcp config reset -``` - ---- - -## API Reference - -### Core Tools - -#### File Operations - -##### read -Read file contents with intelligent detection. - -```python -read(file_path: str, limit: int = None, offset: int = None) -``` - -**Parameters:** -- `file_path` (str): Path to file to read -- `limit` (int, optional): Maximum lines to read -- `offset` (int, optional): Starting line number - -**Returns:** -- File contents with line numbers - -##### write -Write or create files. - -```python -write(file_path: str, content: str) -``` - -**Parameters:** -- `file_path` (str): Path to file -- `content` (str): Content to write - -##### edit -Edit files with precise replacements. - -```python -edit(file_path: str, old_string: str, new_string: str, expected_replacements: int = 1) -``` - -**Parameters:** -- `file_path` (str): Path to file -- `old_string` (str): Text to replace -- `new_string` (str): Replacement text -- `expected_replacements` (int): Expected number of replacements - -##### multi_edit -Make multiple edits in one operation. - -```python -multi_edit(file_path: str, edits: List[Dict]) -``` - -**Parameters:** -- `file_path` (str): Path to file -- `edits` (List[Dict]): List of edit operations - -#### Search Tools - -##### search (Unified Search) -Multi-modal search combining text, AST, vector, and symbol search. - -```python -search( - pattern: str, - path: str = ".", - enable_grep: bool = True, - enable_ast: bool = True, - enable_vector: bool = True, - enable_symbol: bool = True, - max_results: int = 50 -) -``` - -**Parameters:** -- `pattern` (str): Search pattern or query -- `path` (str): Directory to search -- `enable_*` (bool): Enable specific search types -- `max_results` (int): Maximum results per search type - -##### grep -Fast pattern matching with ripgrep. - -```python -grep( - pattern: str, - path: str = ".", - glob: str = None, - ignore_case: bool = False, - multiline: bool = False -) -``` - -##### ast -AST-aware code structure search. - -```python -ast( - pattern: str, - path: str, - line_number: bool = False, - ignore_case: bool = False -) -``` - -#### Interactive Development - -##### notebook -Multi-language notebook operations. - -```python -notebook( - action: str, # create, read, write, execute, step, debug - path: str = None, - cell_type: str = None, - content: str = None, - kernel: str = None, - cell_id: str = None -) -``` - -**Actions:** -- `create`: Create new notebook -- `read`: Read notebook or cell -- `write`: Write to cell -- `execute`: Run cell -- `step`: Step through execution -- `debug`: Start debugger - -##### repl -Interactive REPL sessions. - -```python -repl( - languages: List[str], - project_dir: str = None, - share_context: bool = True -) -``` - -##### lsp -Language Server Protocol operations. - -```python -lsp( - action: str, # initialize, definition, references, rename, diagnostics - file: str, - line: int = None, - character: int = None, - new_name: str = None -) -``` - -#### AI Tools - -##### agent -Delegate tasks to AI agents. - -```python -agent( - prompts: List[str], - parallel: bool = False, - model: str = None -) -``` - -##### consensus -Get agreement from multiple LLMs. - -```python -consensus( - prompt: str, - providers: List[str], - threshold: float = 0.8 -) -``` - -##### critic -Automated code review and quality checks. - -```python -critic(analysis: str) -``` - -##### think -Structured reasoning workspace. - -```python -think(thought: str) -``` - -#### System Tools - -##### bash -Execute shell commands. - -```python -bash( - command: str, - cwd: str = None, - env: Dict = None, - timeout: int = None -) -``` - -##### git -Git operations with integrated search. - -```python -git( - *args, # git command arguments - search: bool = False, - pattern: str = None -) -``` - -##### process -Manage background processes. - -```python -process( - action: str, # list, kill, logs - id: str = None, - lines: int = 100 -) -``` - -#### Project Management - -##### todo -Task management. - -```python -todo( - content: str = None, - action: str = "list", # list, add, update, remove - id: str = None, - status: str = None, - priority: str = None -) -``` - -##### rules -Read project preferences. - -```python -rules(path: str = ".") -``` - -### MCP Server Management - -##### mcp -Manage external MCP servers. - -```python -mcp( - action: str, # add, remove, list, stats - url: str = None, - alias: str = None -) -``` - ---- - -## Example Use Cases - -### 1. Interactive Development Session - -```python -# Start a multi-language notebook -notebook(action="create", path="analysis.ipynb", kernels=["python3", "javascript"]) - -# Write and execute Python code -notebook( - action="write", - cell_type="code", - content=""" -import pandas as pd -data = pd.read_csv('data.csv') -print(data.head()) -""", - kernel="python3" -) - -# Execute the cell -notebook(action="execute", cell_id="cell_1") - -# Debug if needed -debugger(notebook="analysis.ipynb", cell_id="cell_1", breakpoint=3) -``` - -### 2. Multi-Agent Code Review - -```python -# Find all Python files -find(pattern="*.py", path="src/") - -# Delegate review to multiple agents -agent( - prompts=[ - "Review src/auth.py for security vulnerabilities", - "Check src/database.py for SQL injection risks", - "Analyze src/api.py for rate limiting implementation" - ], - parallel=True -) - -# Get consensus on critical changes -consensus( - prompt="Should we refactor the authentication system based on the review?", - providers=["openai", "anthropic", "google"], - threshold=0.8 -) - -# Apply critic for final quality check -critic(analysis="Review the proposed authentication refactoring for best practices") -``` - -### 3. Intelligent Code Search and Refactoring - -```python -# Unified search for authentication patterns -results = search("authentication flow", enable_vector=True, enable_ast=True) - -# Find all function definitions -ast(pattern="def authenticate.*", path="src/") - -# Batch refactoring -batch( - invocations=[ - {"tool": "edit", "params": {"file": "src/auth.py", "old": "old_auth", "new": "new_auth"}}, - {"tool": "edit", "params": {"file": "src/api.py", "old": "old_auth", "new": "new_auth"}}, - {"tool": "edit", "params": {"file": "tests/test_auth.py", "old": "old_auth", "new": "new_auth"}} - ] -) - -# Run tests -bash("pytest tests/") -``` - -### 4. Project Setup and Configuration - -```python -# Read project rules -rules() - -# Initialize todo list -todo("Set up authentication system") -todo("Implement rate limiting") -todo("Add comprehensive tests") - -# Configure project-specific settings -mcp(action="add", url="github.com/user/custom-mcp", alias="custom") - -# Set up git hooks -bash("git config core.hooksPath .github/hooks") -``` - -### 5. Debugging Complex Issues - -```python -# Start investigation -think("User reports authentication fails intermittently. Possible causes: race condition, cache invalidation, token expiry") - -# Search for related code -search("token expiry cache", enable_grep=True, enable_ast=True) - -# Check logs -bash("tail -f logs/auth.log", timeout=30) - -# Find recent changes -git("log", "--oneline", "-n", "20", "--", "src/auth/") - -# Interactive debugging -repl(languages=["python"], project_dir=".") -``` - -### 6. Documentation Generation - -```python -# Find all public APIs -ast(pattern="def [^_].*", path="src/api/") - -# Generate documentation -agent( - prompts=[ - "Document all public APIs in src/api/", - "Create usage examples for each endpoint", - "Generate OpenAPI specification" - ] -) - -# Write documentation -write(file_path="API_DOCUMENTATION.md", content=generated_docs) -``` - ---- - -## Troubleshooting Guide - -### Common Issues and Solutions - -#### 1. Installation Issues - -**Problem**: `uvx hanzo-mcp` fails with "command not found" -```bash -# Solution: Install uv first -curl -LsSf https://astral.sh/uv/install.sh | sh - -# Or use pip -pip install hanzo-mcp -``` - -**Problem**: Python version error -```bash -# Solution: Ensure Python 3.12+ -python --version - -# Use pyenv to install newer Python -pyenv install 3.12.0 -pyenv local 3.12.0 -``` - -#### 2. Configuration Issues - -**Problem**: Tools not working as expected -```bash -# Check configuration -hanzo-mcp config show - -# Verify tool is enabled -hanzo-mcp config get enabled_tools.agent - -# Enable specific tool -hanzo-mcp config set enabled_tools.agent true -``` - -**Problem**: Permission denied errors -```bash -# Add path to allowed_paths -hanzo-mcp config add allowed_paths "/path/to/project" - -# Or set via environment -export HANZO_MCP_ALLOWED_PATHS="/home/user/projects,/tmp" -``` - -#### 3. API Key Issues - -**Problem**: Agent tools not working -```bash -# Set API keys -export OPENAI_API_KEY="sk-..." -export ANTHROPIC_API_KEY="sk-ant-..." - -# Or use Hanzo API key -export HANZO_API_KEY="hanzo-..." - -# Verify in config -hanzo-mcp config get agent.api_key -``` - -#### 4. Memory Issues - -**Problem**: High memory usage with large projects -```bash -# Limit search depth -search(pattern="term", max_results=20) - -# Clear vector store cache -hanzo-mcp cache clear - -# Reduce chunk size -hanzo-mcp config set vector_store.chunk_size 500 -``` - -#### 5. Network Issues - -**Problem**: MCP server connection failures -```bash -# Check server status -hanzo-mcp --health - -# Restart server -hanzo-mcp restart - -# Use different port -hanzo-mcp --port 9999 -``` - -#### 6. Tool-Specific Issues - -**LSP not working**: -```bash -# Install language server -npm install -g typescript-language-server -pip install python-lsp-server - -# Reinitialize -lsp(action="initialize", language="python") -``` - -**Git search not finding results**: -```bash -# Ensure git repository -git init - -# Update git index -git add . -git commit -m "Initial commit" -``` - -### Debugging Commands - -```bash -# Enable debug logging -export HANZO_MCP_LOG_LEVEL=DEBUG -hanzo-mcp --debug - -# Check server logs -tail -f ~/.config/hanzo/logs/mcp-server.log - -# Test specific tool -hanzo-mcp test-tool read --file README.md - -# Health check -hanzo-mcp --health - -# Version information -hanzo-mcp --version --verbose -``` - -### Getting Help - -1. **Check Documentation**: https://mcp.hanzo.ai -2. **GitHub Issues**: https://github.com/hanzoai/mcp/issues -3. **Discord Community**: https://discord.gg/hanzoai -4. **Email Support**: support@hanzo.ai - ---- - -## Architecture Overview - -### System Architecture - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Client Applications โ”‚ -โ”‚ (Claude Desktop, VS Code, Custom Clients) โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ”‚ MCP Protocol (JSON-RPC) - โ–ผ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Hanzo MCP Server โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Core Engine โ”‚ โ”‚ -โ”‚ โ”‚ - Request Router โ”‚ โ”‚ -โ”‚ โ”‚ - Tool Registry โ”‚ โ”‚ -โ”‚ โ”‚ - Permission Manager โ”‚ โ”‚ -โ”‚ โ”‚ - Session Manager โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Tool Ecosystem โ”‚ โ”‚ -โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ File โ”‚ โ”‚ Search โ”‚ โ”‚ Agent โ”‚ ... โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ Tools โ”‚ โ”‚ Tools โ”‚ โ”‚ Tools โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ External Services โ”‚ โ”‚ -โ”‚ โ”‚ - LLM Providers (OpenAI, Anthropic) โ”‚ โ”‚ -โ”‚ โ”‚ - Language Servers (LSP) โ”‚ โ”‚ -โ”‚ โ”‚ - Vector Stores โ”‚ โ”‚ -โ”‚ โ”‚ - Other MCP Servers โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -### Tool Categories - -1. **File Operations**: read, write, edit, multi_edit -2. **Search & Intelligence**: search, grep, ast, symbols -3. **Interactive Development**: notebook, repl, debugger, lsp -4. **AI & Automation**: agent, consensus, critic, think -5. **System & Process**: bash, git, process, npx, uvx -6. **Project Management**: todo, rules, palette -7. **Data & Analytics**: vector, sql, graph, stats -8. **MCP Ecosystem**: mcp, batch - -### Security Model - -- **Path-based permissions**: Allowed paths configuration -- **Tool-level permissions**: Enable/disable specific tools -- **Trusted servers**: Whitelist for external MCP servers -- **API key management**: Secure storage and rotation -- **Audit logging**: All operations are logged - ---- - -## Advanced Topics - -### Creating Custom Tools - -```python -# Create custom_tool.py -from hanzo_mcp.tools import register_tool - -@register_tool( - name="my_custom_tool", - description="My custom tool description", - category="custom" -) -async def my_custom_tool(param1: str, param2: int = 10): - """Custom tool implementation.""" - # Tool logic here - return {"result": f"Processed {param1} with {param2}"} - -# Register in config -{ - "custom_tools": { - "my_custom_tool": { - "module": "custom_tool", - "enabled": true - } - } -} -``` - -### Extending with MCP Servers - -```bash -# Add external MCP server -hanzo-mcp mcp add --url github.com/user/their-mcp --alias their - -# Use in workflow -their_tool(action="custom", params={...}) - -# Chain multiple servers -batch( - invocations=[ - {"tool": "hanzo_search", "params": {...}}, - {"tool": "their_tool", "params": {...}}, - {"tool": "another_server_tool", "params": {...}} - ] -) -``` - -### Performance Optimization - -```python -# Parallel execution -batch( - invocations=[...], - parallel=True, - max_workers=8 -) - -# Caching strategies -search( - pattern="term", - cache=True, - cache_ttl=3600 -) - -# Resource limits -{ - "performance": { - "max_file_size": "100MB", - "max_search_results": 100, - "command_timeout": 60, - "max_parallel_tools": 5 - } -} -``` - -### Integration Patterns - -#### VS Code Integration -```json -// .vscode/settings.json -{ - "hanzo.mcp.enabled": true, - "hanzo.mcp.server": "stdio", - "hanzo.mcp.tools": ["read", "write", "search", "lsp"] -} -``` - -#### CI/CD Integration -```yaml -# .github/workflows/hanzo.yml -name: Hanzo MCP Analysis -on: [push, pull_request] -jobs: - analyze: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Run Hanzo Analysis - run: | - uvx hanzo-mcp critic "Review PR changes" - uvx hanzo-mcp test "Run comprehensive tests" -``` - -#### Docker Integration -```dockerfile -FROM python:3.12-slim -RUN pip install hanzo-mcp -COPY . /app -WORKDIR /app -CMD ["hanzo-mcp", "serve", "--host", "0.0.0.0"] -``` - ---- - -## Appendix - -### Environment Variables Reference - -| Variable | Description | Default | -|----------|-------------|---------| -| `HANZO_MCP_HOST` | Server host | 127.0.0.1 | -| `HANZO_MCP_PORT` | Server port | 8888 | -| `HANZO_MCP_LOG_LEVEL` | Logging level | INFO | -| `HANZO_MCP_PROJECT_DIR` | Project directory | . | -| `HANZO_MCP_ALLOWED_PATHS` | Comma-separated allowed paths | ~ | -| `HANZO_MCP_DISABLED_TOOLS` | Comma-separated disabled tools | | -| `OPENAI_API_KEY` | OpenAI API key | | -| `ANTHROPIC_API_KEY` | Anthropic API key | | -| `HANZO_API_KEY` | Hanzo API key | | - -### Tool Compatibility Matrix - -| Tool | Claude | VS Code | Custom Client | Requires API Key | -|------|--------|---------|---------------|------------------| -| read/write/edit | โœ… | โœ… | โœ… | โŒ | -| search/grep/ast | โœ… | โœ… | โœ… | โŒ | -| notebook/repl | โœ… | โœ… | โœ… | โŒ | -| agent/consensus | โœ… | โœ… | โœ… | โœ… | -| critic/think | โœ… | โœ… | โœ… | โš ๏ธ | -| bash/git | โœ… | โœ… | โœ… | โŒ | -| lsp | โœ… | โœ… | โœ… | โŒ | -| vector | โœ… | โœ… | โœ… | โš ๏ธ | - -Legend: โœ… Full support | โš ๏ธ Optional | โŒ Not required - -### Version History - -- **v0.8.0** - Current version with 70+ tools -- **v0.7.0** - Added notebook and repl support -- **v0.6.0** - Introduced agent orchestration -- **v0.5.0** - LSP integration -- **v0.4.0** - Vector store support -- **v0.3.0** - Multi-MCP server support -- **v0.2.0** - Basic tool ecosystem -- **v0.1.0** - Initial release - ---- - -## License - -MIT License - See [LICENSE](https://github.com/hanzoai/mcp/blob/main/LICENSE) for details. - -## Contributing - -We welcome contributions! See [CONTRIBUTING.md](https://github.com/hanzoai/mcp/blob/main/CONTRIBUTING.md) for guidelines. - -## Support - -- Documentation: https://mcp.hanzo.ai -- GitHub: https://github.com/hanzoai/mcp -- Discord: https://discord.gg/hanzoai -- Email: support@hanzo.ai - ---- - -*Built with โค๏ธ by Hanzo Industries Inc.* \ No newline at end of file diff --git a/pkg/hanzo-mcp/docs/SEARCH_GUIDE.md b/pkg/hanzo-mcp/docs/SEARCH_GUIDE.md deleted file mode 100644 index 2c3bb0296..000000000 --- a/pkg/hanzo-mcp/docs/SEARCH_GUIDE.md +++ /dev/null @@ -1,386 +0,0 @@ -# Hanzo MCP Search Guide - -## Overview - -Hanzo MCP provides a powerful unified search system that intelligently combines multiple search modalities to help you find anything in your codebase. The system consists of two primary tools: - -1. **Unified Search** (`search`) - THE primary search interface that finds everything -2. **Find Tool** (`find`) - Fast file and directory discovery - -## Unified Search Tool - -The unified search tool is your universal interface for finding: -- Code patterns and text matches (using ripgrep) -- AST nodes and code structure (using treesitter) -- Symbol definitions and references (using ctags/LSP) -- Files and directories (using find tool) -- Memory and knowledge base entries -- Semantic/conceptual matches (using vector search) - -### Key Features - -- **Intelligent Query Detection**: Automatically determines the best search strategy based on your query -- **Parallel Search**: Runs multiple search types concurrently for speed -- **Deduplication**: Removes duplicate results across search types -- **Relevance Ranking**: Sorts results by relevance and match quality -- **Context Awareness**: Provides surrounding context for matches -- **Pagination**: Handles large result sets efficiently - -### Usage Examples - -#### 1. Find Code Patterns -```python -# Find all error handling code -search("error handling") - -# Find TODOs and FIXMEs (regex) -search("TODO|FIXME") - -# Find async functions -search("async function") -``` - -#### 2. Find Symbols/Definitions -```python -# Find a class definition -search("class UserService") - -# Find a function/method -search("handleRequest") - -# Find a constant -search("MAX_RETRIES") -``` - -#### 3. Find Files -```python -# Find test files -search("test_*.py", search_files=True) - -# Find config files -search("config", search_files=True) -``` - -#### 4. Semantic Search -```python -# Natural language queries -search("how authentication works") -search("database connection logic") -search("error handling patterns") -``` - -#### 5. Memory Search -```python -# Search previous discussions -search("previous discussion about API design") -search("that bug we fixed last week") -``` - -### Advanced Usage - -#### Filtering Results -```python -# Search only in Python files -search("import requests", include="*.py") - -# Exclude test files -search("DatabaseConnection", exclude="*test*") - -# Search in specific directory -search("TODO", path="src/services") -``` - -#### Controlling Search Types -```python -# Force specific search types -search("pattern", - enable_text=True, # Text search (always on) - enable_ast=True, # AST search - enable_vector=True, # Semantic search - enable_symbol=True, # Symbol search - search_files=True, # File name search - search_memory=True # Memory search -) -``` - -#### Pagination -```python -# Get first page of results -result = search("async", page_size=20, page=1) - -# Get next page -if result.data["pagination"]["has_next"]: - result = search("async", page_size=20, page=2) -``` - -### How It Works - -1. **Query Analysis**: The tool analyzes your query to determine intent - - Code syntax โ†’ Text/AST/Symbol search - - Natural language โ†’ Vector/semantic search - - Glob patterns โ†’ File search - -2. **Parallel Execution**: Appropriate search types run concurrently - -3. **Result Processing**: - - Deduplication across search types - - Relevance scoring based on: - - Match type (exact > fuzzy) - - Location (definitions > usage) - - File type (source > test > vendor) - - Context extraction - -4. **Presentation**: Results are formatted with previews and context - -## Find Tool - -The find tool is optimized for quickly finding files and directories by name, pattern, or attributes. - -### Key Features - -- **Lightning Fast**: Uses `ffind` when available for blazing performance -- **Smart Pattern Matching**: Supports glob, regex, and fuzzy matching -- **File Attribute Filtering**: Filter by size, modification time, type -- **Human-Readable**: Sizes and times in readable format -- **Gitignore Aware**: Respects .gitignore by default - -### Usage Examples - -#### Basic File Finding -```python -# Find all Python files -find("*.py") - -# Find files starting with test_ -find("test_", type="file") - -# Find directories named src -find("src", type="dir") -``` - -#### Advanced Filtering -```python -# Find large files (>10MB) -find("*", min_size="10MB") - -# Find recently modified files -find("*", modified_after="1 day ago") - -# Find old log files -find("*.log", modified_before="1 week ago") - -# Find files in size range -find("*", min_size="1MB", max_size="10MB") -``` - -#### Pattern Matching Options -```python -# Regex pattern matching -find(r"test_\w+\.py", regex=True) - -# Fuzzy matching (typo-tolerant) -find("confg", fuzzy=True) # Finds "config" files - -# Case-sensitive search -find("README", case_sensitive=True) -``` - -#### Performance Options -```python -# Limit search depth -find("*.js", max_depth=3) - -# Don't follow symlinks -find("*", follow_symlinks=False) - -# Include gitignored files -find("*", respect_gitignore=False) - -# Sort results -find("*.py", sort_by="size", reverse=True) -``` - -## Integration with Other Tools - -### AST-Aware Multi-Edit - -The new AST-aware multi-edit tool can use the unified search to find all references before making changes: - -```python -# Find and rename a function across the codebase -ast_multi_edit("main.go", [ - { - "old_string": "ProcessData", - "new_string": "ProcessDataWithContext", - "semantic_match": True, # Find all references - "node_types": ["call_expression"] # Only function calls - } -]) -``` - -### LSP Tool - -The LSP tool provides language-specific intelligence: - -```python -# Check if Go LSP is available -lsp("status", file="main.go") - -# Find definition (uses LSP when available) -lsp("definition", file="app.py", line=42, character=10) - -# Find all references -lsp("references", file="service.ts", line=15, character=5) - -# Rename symbol -lsp("rename", file="lib.rs", line=20, character=8, new_name="new_name") -``` - -## Search Strategies - -### For Maximum Coverage - -Use the unified search tool - it automatically runs all appropriate search types: - -```python -# This will search text, AST, symbols, vectors, and more -search("authentication flow") -``` - -### For Speed - -1. **Known file names**: Use the find tool - ```python - find("config.json") - ``` - -2. **Specific text**: Use grep directly - ```python - grep("TODO") - ``` - -3. **Code structure**: Use ast tool (symbols) - ```python - symbols("function.*process") - ``` - -### For Accuracy - -1. **Symbol definitions**: Enable symbol search - ```python - search("MyClass", enable_symbol=True) - ``` - -2. **Semantic meaning**: Enable vector search - ```python - search("how to handle errors", enable_vector=True) - ``` - -3. **Exact matches**: Use regex anchors - ```python - search("^class MyClass$") - ``` - -## Performance Tips - -1. **Use specific paths** when you know the general location: - ```python - search("pattern", path="src/services") - ``` - -2. **Limit results** for faster response: - ```python - search("common_term", max_results_per_type=10) - ``` - -3. **Use appropriate page sizes**: - ```python - # Smaller pages for interactive use - search("pattern", page_size=20) - - # Larger pages for batch processing - search("pattern", page_size=100) - ``` - -4. **Exclude unnecessary files**: - ```python - search("pattern", exclude="node_modules,*.min.js") - ``` - -## Configuration - -### Vector Search Setup - -To enable semantic/vector search: - -1. Install dependencies: - ```bash - pip install chromadb sentence-transformers - ``` - -2. The tool will automatically initialize vector search when available - -3. Build index for better performance: - ```python - # This happens automatically on first use - # But can be triggered manually for large codebases - ``` - -### LSP Configuration - -The LSP tool automatically installs language servers as needed. Supported languages: - -- **Go**: gopls -- **Python**: python-lsp-server -- **TypeScript/JavaScript**: typescript-language-server -- **Rust**: rust-analyzer -- **Java**: jdtls -- **C/C++**: clangd -- **Ruby**: solargraph -- **Lua**: lua-language-server - -## Troubleshooting - -### Search Returns Too Many Results - -1. Be more specific in your query -2. Use filters (include/exclude) -3. Limit search types -4. Reduce max_results_per_type - -### Search is Slow - -1. Use specific paths instead of searching entire codebase -2. Disable vector search for simple text searches -3. Use find tool for file discovery -4. Install ripgrep for faster text search - -### Missing Expected Results - -1. Check if files are gitignored -2. Verify file permissions -3. Try different search types -4. Use more general patterns - -### Vector Search Not Working - -1. Check if chromadb is installed -2. Verify sentence-transformers is available -3. Check for initialization errors in logs -4. Try rebuilding the index - -## Best Practices - -1. **Start with unified search** - it's the most comprehensive -2. **Use natural language** for conceptual searches -3. **Use code syntax** for exact matches -4. **Combine search types** for best results -5. **Review statistics** to understand what was searched -6. **Use pagination** for large result sets -7. **Cache results** when doing multiple related searches - -## Summary - -The Hanzo MCP search system provides a powerful, intelligent way to find anything in your codebase. The unified search tool should be your primary interface, automatically selecting the best search strategies for your queries. The find tool complements this with ultra-fast file discovery. - -Together, these tools ensure you can quickly locate any code, file, symbol, or concept in your project, making development more efficient and enjoyable. \ No newline at end of file diff --git a/pkg/hanzo-mcp/examples/agent_grinding_demo.py b/pkg/hanzo-mcp/examples/agent_grinding_demo.py deleted file mode 100644 index 3fa5e1f5a..000000000 --- a/pkg/hanzo-mcp/examples/agent_grinding_demo.py +++ /dev/null @@ -1,239 +0,0 @@ -#!/usr/bin/env python3 -"""Agent Grinding Pattern Demo. - -This demonstrates the pattern where agents can launch with all normal tools, -read/research/study then decide to edit or not. You can fuzzy match things, -and just throw matches at agent grinder, let them grind through each until -it's all resolved. - -The idea is that agents autonomously: -1. Search for relevant files/patterns -2. Study the code to understand context -3. Decide if changes are needed -4. Make precise edits if necessary -5. Move to the next match - -This is efficient because: -- Agents work in parallel on different files -- Each agent has full context and tools -- They can skip files that don't need changes -- They make surgical edits when needed -""" - -import asyncio -import json - - -# Simulated swarm tool usage -async def run_agent_grinding_demo(): - """Run the agent grinding pattern demo.""" - - print("=== Agent Grinding Pattern Demo ===\n") - - # Example 1: Fix all TODO comments in a codebase - print("Example 1: Resolving TODO comments across codebase") - print("-" * 50) - - todo_grinding_config = { - "query": "Resolve all TODO comments in the codebase", - "agents": [ - { - "id": "todo_finder", - "query": "Find all TODO comments using grep and organize them by file", - "role": "finder", - }, - { - "id": "todo_analyzer_1", - "query": "Analyze TODOs in file group 1 and determine which need fixing", - "role": "analyzer", - "receives_from": ["todo_finder"], - }, - { - "id": "todo_analyzer_2", - "query": "Analyze TODOs in file group 2 and determine which need fixing", - "role": "analyzer", - "receives_from": ["todo_finder"], - }, - { - "id": "todo_fixer_1", - "query": "Fix the TODOs that need fixing in group 1. Read each file, understand context, make precise edits", - "role": "fixer", - "receives_from": ["todo_analyzer_1"], - }, - { - "id": "todo_fixer_2", - "query": "Fix the TODOs that need fixing in group 2. Read each file, understand context, make precise edits", - "role": "fixer", - "receives_from": ["todo_analyzer_2"], - }, - { - "id": "reviewer", - "query": "Review all changes made and ensure they're correct", - "role": "reviewer", - "receives_from": ["todo_fixer_1", "todo_fixer_2"], - }, - ], - } - - print(json.dumps(todo_grinding_config, indent=2)) - print("\nThis would launch 6 agents working in parallel to grind through TODOs\n") - - # Example 2: Update all deprecated API usage - print("\nExample 2: Update deprecated API usage") - print("-" * 50) - - api_update_config = { - "query": "Update all deprecated API calls to new version", - "agents": [ - { - "id": "api_scanner", - "query": "Search for all uses of old_api.* pattern in the codebase", - "role": "scanner", - }, - { - "id": "api_updater_1", - "query": "For files 1-10: Read file, understand usage context, update to new_api if appropriate", - "role": "updater", - "receives_from": ["api_scanner"], - }, - { - "id": "api_updater_2", - "query": "For files 11-20: Read file, understand usage context, update to new_api if appropriate", - "role": "updater", - "receives_from": ["api_scanner"], - }, - { - "id": "api_updater_3", - "query": "For files 21+: Read file, understand usage context, update to new_api if appropriate", - "role": "updater", - "receives_from": ["api_scanner"], - }, - { - "id": "test_runner", - "query": "Run tests on all updated files to ensure changes work", - "role": "tester", - "receives_from": ["api_updater_1", "api_updater_2", "api_updater_3"], - }, - ], - } - - print(json.dumps(api_update_config, indent=2)) - print("\nAgents work in parallel, each handling a subset of files\n") - - # Example 3: Add type hints to untyped functions - print("\nExample 3: Add type hints to Python functions") - print("-" * 50) - - type_hint_config = { - "query": "Add type hints to all Python functions missing them", - "network_type": "pipeline", - "agents": [ - { - "id": "type_finder", - "query": "Use grep_ast to find all Python functions without type hints", - "role": "finder", - }, - { - "id": "type_inferrer", - "query": "For each function, analyze usage and infer appropriate types", - "role": "analyzer", - "receives_from": ["type_finder"], - }, - { - "id": "type_adder", - "query": "Add the inferred type hints to each function signature", - "role": "editor", - "receives_from": ["type_inferrer"], - }, - { - "id": "mypy_checker", - "query": "Run mypy on modified files to verify type correctness", - "role": "validator", - "receives_from": ["type_adder"], - }, - ], - } - - print(json.dumps(type_hint_config, indent=2)) - print("\nPipeline pattern: each stage processes all items before passing to next\n") - - # Example 4: Refactor duplicated code - print("\nExample 4: Refactor duplicated code patterns") - print("-" * 50) - - refactor_config = { - "query": "Find and refactor duplicated code patterns", - "consensus_mode": True, - "agents": [ - { - "id": "dup_finder", - "query": "Find similar code patterns that might be duplicated", - "role": "finder", - }, - { - "id": "refactor_designer", - "query": "Design refactoring approach for each duplication", - "role": "architect", - "receives_from": ["dup_finder"], - }, - { - "id": "consensus_group", - "query": "Discuss and agree on best refactoring approach", - "role": "consensus", - "model": "claude-3-5-sonnet-20241022", - "participants": 3, - "receives_from": ["refactor_designer"], - }, - { - "id": "refactorer", - "query": "Implement the agreed refactoring", - "role": "implementer", - "receives_from": ["consensus_group"], - }, - ], - } - - print(json.dumps(refactor_config, indent=2)) - print("\nConsensus mode: multiple agents discuss before making changes\n") - - # Show the key benefits - print("\n=== Key Benefits of Agent Grinding ===") - print( - "1. Parallel Processing: Multiple agents work on different files simultaneously" - ) - print( - "2. Full Context: Each agent has all tools - can read, search, understand before editing" - ) - print("3. Smart Filtering: Agents skip files that don't need changes") - print("4. Precise Edits: Agents make surgical changes based on understanding") - print("5. Scalable: Add more agents to handle larger codebases") - print( - "6. Flexible: Different patterns (pipeline, parallel, consensus) for different tasks" - ) - - # Show example swarm command - print("\n=== Example Swarm Command ===") - print(""" -swarm_tool( - query="Fix all FIXME comments in the codebase", - agents=[ - {"id": "finder", "query": "grep for all FIXME comments"}, - {"id": "grinder1", "query": "Fix FIXMEs in src/", "receives_from": ["finder"]}, - {"id": "grinder2", "query": "Fix FIXMEs in tests/", "receives_from": ["finder"]}, - {"id": "grinder3", "query": "Fix FIXMEs in docs/", "receives_from": ["finder"]}, - {"id": "reviewer", "query": "Review all fixes", "receives_from": ["grinder1", "grinder2", "grinder3"]} - ] -) -""") - - print("\nThe agents will:") - print("- finder: Search and categorize all FIXMEs") - print("- grinders: Work in parallel on different directories") - print( - "- Each grinder: Read file โ†’ Understand context โ†’ Fix if needed โ†’ Move to next" - ) - print("- reviewer: Ensure all fixes are appropriate") - - -if __name__ == "__main__": - asyncio.run(run_agent_grinding_demo()) diff --git a/pkg/hanzo-mcp/examples/agent_grinding_practical.py b/pkg/hanzo-mcp/examples/agent_grinding_practical.py deleted file mode 100644 index b019cdbf1..000000000 --- a/pkg/hanzo-mcp/examples/agent_grinding_practical.py +++ /dev/null @@ -1,325 +0,0 @@ -#!/usr/bin/env python3 -"""Practical Agent Grinding Examples. - -This shows real-world examples of using the agent grinding pattern -with the swarm tool to solve common development tasks efficiently. -""" - -# Example 1: Fix all linting errors in parallel -fix_lint_errors = """ -# Fix all Python linting errors across the codebase - -swarm( - query="Fix all Python linting errors (ruff/flake8) in the codebase", - agents=[ - { - "id": "lint_runner", - "query": "Run 'ruff check . --output-format=json' to get all linting errors organized by file" - }, - { - "id": "error_distributor", - "query": "Group linting errors by file and distribute to fixers. Create 3 groups for parallel processing", - "receives_from": ["lint_runner"] - }, - { - "id": "lint_fixer_1", - "query": "Fix linting errors in group 1 files. For each file: read it, understand the errors, make precise fixes", - "receives_from": ["error_distributor"] - }, - { - "id": "lint_fixer_2", - "query": "Fix linting errors in group 2 files. For each file: read it, understand the errors, make precise fixes", - "receives_from": ["error_distributor"] - }, - { - "id": "lint_fixer_3", - "query": "Fix linting errors in group 3 files. For each file: read it, understand the errors, make precise fixes", - "receives_from": ["error_distributor"] - }, - { - "id": "lint_verifier", - "query": "Run ruff check again on all modified files to ensure all errors are fixed", - "receives_from": ["lint_fixer_1", "lint_fixer_2", "lint_fixer_3"] - } - ] -) -""" - -# Example 2: Add docstrings to all functions -add_docstrings = """ -# Add missing docstrings to all Python functions - -swarm( - query="Add comprehensive docstrings to all Python functions missing them", - agents=[ - { - "id": "docstring_finder", - "query": "Use grep_ast to find all Python functions without docstrings or with incomplete docstrings" - }, - { - "id": "code_analyzer", - "query": "For each function without docstring, analyze its code to understand what it does, its parameters, and return values", - "receives_from": ["docstring_finder"] - }, - { - "id": "docstring_writer_1", - "query": "Write Google-style docstrings for functions in files A-M. Include description, Args, Returns, Raises sections as appropriate", - "receives_from": ["code_analyzer"] - }, - { - "id": "docstring_writer_2", - "query": "Write Google-style docstrings for functions in files N-Z. Include description, Args, Returns, Raises sections as appropriate", - "receives_from": ["code_analyzer"] - }, - { - "id": "docstring_reviewer", - "query": "Review all added docstrings for accuracy, completeness, and style consistency", - "receives_from": ["docstring_writer_1", "docstring_writer_2"] - } - ] -) -""" - -# Example 3: Upgrade dependency usage -upgrade_dependencies = """ -# Upgrade from requests to httpx across codebase - -swarm( - query="Migrate all code from requests library to httpx with async support", - agents=[ - { - "id": "usage_finder", - "query": "Find all files importing or using requests library. Note different usage patterns (get, post, sessions, etc.)" - }, - { - "id": "migration_planner", - "query": "Create migration plan mapping requests patterns to httpx equivalents. Consider sync vs async contexts", - "receives_from": ["usage_finder"] - }, - { - "id": "simple_migrator", - "query": "Migrate simple requests.get/post calls to httpx. These can be done mechanically", - "receives_from": ["migration_planner"] - }, - { - "id": "session_migrator", - "query": "Migrate requests.Session usage to httpx.Client. Handle context managers properly", - "receives_from": ["migration_planner"] - }, - { - "id": "async_migrator", - "query": "For async functions, migrate to httpx.AsyncClient. Add proper async/await", - "receives_from": ["migration_planner"] - }, - { - "id": "import_updater", - "query": "Update all import statements from requests to httpx. Update requirements files", - "receives_from": ["simple_migrator", "session_migrator", "async_migrator"] - }, - { - "id": "test_runner", - "query": "Run all tests to ensure migration didn't break anything. Note any failures", - "receives_from": ["import_updater"] - } - ] -) -""" - -# Example 4: Security audit and fixes -security_audit = """ -# Security audit and automated fixes - -swarm( - query="Perform security audit and fix common vulnerabilities", - consensus_mode=true, - agents=[ - { - "id": "secret_scanner", - "query": "Search for hardcoded secrets, API keys, passwords using patterns and entropy analysis" - }, - { - "id": "sql_scanner", - "query": "Find potential SQL injection vulnerabilities in database queries" - }, - { - "id": "input_scanner", - "query": "Find user input that isn't properly validated or sanitized" - }, - { - "id": "dependency_scanner", - "query": "Check for known vulnerabilities in dependencies" - }, - { - "id": "security_consensus", - "query": "Review all findings and prioritize fixes. Discuss best approaches", - "receives_from": ["secret_scanner", "sql_scanner", "input_scanner", "dependency_scanner"], - "participants": 3 - }, - { - "id": "secret_fixer", - "query": "Move secrets to environment variables or secure vaults", - "receives_from": ["security_consensus"] - }, - { - "id": "sql_fixer", - "query": "Fix SQL injections using parameterized queries", - "receives_from": ["security_consensus"] - }, - { - "id": "input_fixer", - "query": "Add proper input validation and sanitization", - "receives_from": ["security_consensus"] - }, - { - "id": "security_reviewer", - "query": "Review all security fixes and create security report", - "receives_from": ["secret_fixer", "sql_fixer", "input_fixer"] - } - ] -) -""" - -# Example 5: Performance optimization -performance_optimization = """ -# Find and fix performance bottlenecks - -swarm( - query="Identify and optimize performance bottlenecks in Python code", - agents=[ - { - "id": "profiler", - "query": "Run performance profiling on key code paths. Identify slow functions" - }, - { - "id": "complexity_analyzer", - "query": "Find functions with high algorithmic complexity (nested loops, etc.)" - }, - { - "id": "db_analyzer", - "query": "Find database queries that could be optimized (N+1, missing indexes, etc.)" - }, - { - "id": "optimization_planner", - "query": "Create optimization plan for each bottleneck. Consider tradeoffs", - "receives_from": ["profiler", "complexity_analyzer", "db_analyzer"] - }, - { - "id": "algorithm_optimizer", - "query": "Optimize algorithmic bottlenecks. Use better data structures, reduce complexity", - "receives_from": ["optimization_planner"] - }, - { - "id": "query_optimizer", - "query": "Optimize database queries. Add indexes, use joins, batch operations", - "receives_from": ["optimization_planner"] - }, - { - "id": "cache_implementer", - "query": "Add caching where appropriate. Use functools.lru_cache, Redis, etc.", - "receives_from": ["optimization_planner"] - }, - { - "id": "benchmark_runner", - "query": "Run benchmarks to measure improvements. Create performance report", - "receives_from": ["algorithm_optimizer", "query_optimizer", "cache_implementer"] - } - ] -) -""" - -# Example 6: Test coverage improvement -improve_test_coverage = """ -# Improve test coverage to 90%+ - -swarm( - query="Improve test coverage to at least 90% across all modules", - agents=[ - { - "id": "coverage_runner", - "query": "Run pytest with coverage. Identify files and functions with low coverage" - }, - { - "id": "test_planner", - "query": "For each low-coverage file, plan what tests are needed. Group by complexity", - "receives_from": ["coverage_runner"] - }, - { - "id": "unit_test_writer_1", - "query": "Write unit tests for simple functions (pure functions, clear inputs/outputs)", - "receives_from": ["test_planner"] - }, - { - "id": "unit_test_writer_2", - "query": "Write unit tests for complex functions (side effects, dependencies). Use mocks", - "receives_from": ["test_planner"] - }, - { - "id": "integration_test_writer", - "query": "Write integration tests for components that interact with external services", - "receives_from": ["test_planner"] - }, - { - "id": "edge_case_hunter", - "query": "Add tests for edge cases: empty inputs, large inputs, error conditions", - "receives_from": ["test_planner"] - }, - { - "id": "coverage_validator", - "query": "Run coverage again. Ensure we hit 90%+. Identify any remaining gaps", - "receives_from": ["unit_test_writer_1", "unit_test_writer_2", "integration_test_writer", "edge_case_hunter"] - } - ] -) -""" - - -def print_example(title: str, example: str): - """Print a formatted example.""" - print(f"\n{'=' * 60}") - print(f"{title}") - print("=" * 60) - print(example) - print() - - -if __name__ == "__main__": - print("PRACTICAL AGENT GRINDING EXAMPLES") - print("=================================") - print("\nThese examples show how to use the swarm tool with agent grinding") - print("pattern to solve real development tasks efficiently.\n") - - print_example("Example 1: Fix All Linting Errors", fix_lint_errors) - print_example("Example 2: Add Missing Docstrings", add_docstrings) - print_example("Example 3: Upgrade Dependencies", upgrade_dependencies) - print_example("Example 4: Security Audit & Fixes", security_audit) - print_example("Example 5: Performance Optimization", performance_optimization) - print_example("Example 6: Improve Test Coverage", improve_test_coverage) - - print("\nKEY PATTERNS:") - print("-------------") - print("1. FINDER โ†’ ANALYZER โ†’ FIXERS โ†’ VALIDATOR") - print(" - Common pattern for search-and-fix tasks") - print(" - Finder locates issues, analyzer understands them") - print(" - Multiple fixers work in parallel") - print(" - Validator ensures fixes are correct") - print() - print("2. SCANNER โ†’ PLANNER โ†’ IMPLEMENTERS โ†’ TESTER") - print(" - For complex migrations or refactoring") - print(" - Scanner finds all instances") - print(" - Planner creates strategy") - print(" - Multiple implementers execute in parallel") - print(" - Tester verifies nothing broke") - print() - print("3. MULTI-SCANNER โ†’ CONSENSUS โ†’ TARGETED-FIXERS") - print(" - For security or code quality audits") - print(" - Multiple scanners look for different issues") - print(" - Consensus group prioritizes fixes") - print(" - Specialized fixers handle each issue type") - print() - print("BENEFITS:") - print("---------") - print("โ€ข Parallel processing - multiple agents work simultaneously") - print("โ€ข Specialization - each agent focuses on one task") - print("โ€ข Context preservation - agents read and understand before editing") - print("โ€ข Scalability - add more agents for larger codebases") - print("โ€ข Reliability - validators ensure quality") diff --git a/pkg/hanzo-mcp/examples/agent_swarm_demo.py b/pkg/hanzo-mcp/examples/agent_swarm_demo.py deleted file mode 100644 index e4ed96089..000000000 --- a/pkg/hanzo-mcp/examples/agent_swarm_demo.py +++ /dev/null @@ -1,216 +0,0 @@ -#!/usr/bin/env python3 -"""Agent swarm demo using hanzo/net for local private AI inference.""" - -import asyncio -import sys -from pathlib import Path - -# Add hanzo-network to path -sys.path.insert(0, str(Path(__file__).parent.parent.parent / "hanzo-network" / "src")) - -from hanzo_network import ( - create_local_agent, - create_local_distributed_network, - create_router, - create_tool, -) -from hanzo_network.core.router import RouterArgs -from hanzo_network.llm import HanzoNetProvider - - -async def main(): - """Run agent swarm with local AI inference.""" - - print("๐Ÿ Hanzo Agent Swarm Demo - Local Private AI Inference") - print("=" * 60) - - # Check hanzo/net availability - print("\n๐Ÿ“ก Checking hanzo/net distributed inference...") - provider = HanzoNetProvider("dummy") # Use dummy for demo - is_available = await provider.is_available() - models = await provider.list_models() - - print(f"Status: {'โœ… Available' if is_available else 'โŒ Not Available'}") - print(f"Models: {', '.join(models)}") - print(f"Engine: {provider.engine_type}") - - # Create specialized agents for the swarm - print("\n๐Ÿค– Creating agent swarm...") - - # 1. Research Agent - def search_codebase(query: str) -> str: - return f"Found 10 results for '{query}' in codebase" - - research_agent = create_local_agent( - name="researcher", - description="Searches and analyzes codebase", - system="You are a code research specialist. Search for patterns and analyze code.", - tools=[ - create_tool( - name="search_codebase", - description="Search codebase for patterns", - handler=search_codebase, - ) - ], - local_model="llama3.2", - ) - - # 2. Analyzer Agent - def analyze_complexity(code: str) -> str: - return "Complexity analysis: Low complexity, well-structured" - - analyzer_agent = create_local_agent( - name="analyzer", - description="Analyzes code quality and complexity", - system="You are a code quality analyst. Analyze complexity and suggest improvements.", - tools=[ - create_tool( - name="analyze_complexity", - description="Analyze code complexity", - handler=analyze_complexity, - ) - ], - local_model="llama3.2", - ) - - # 3. Refactor Agent - def suggest_refactoring(code: str) -> str: - return "Suggested refactoring: Extract method, improve naming" - - refactor_agent = create_local_agent( - name="refactorer", - description="Suggests code refactoring", - system="You are a refactoring expert. Suggest clean code improvements.", - tools=[ - create_tool( - name="suggest_refactoring", - description="Suggest refactoring improvements", - handler=suggest_refactoring, - ) - ], - local_model="llama3.2", - ) - - # 4. Test Agent - def generate_tests(function: str) -> str: - return f"Generated 5 unit tests for '{function}'" - - test_agent = create_local_agent( - name="tester", - description="Generates test cases", - system="You are a test engineer. Generate comprehensive test cases.", - tools=[ - create_tool( - name="generate_tests", - description="Generate test cases", - handler=generate_tests, - ) - ], - local_model="llama3.2", - ) - - # Create smart router for the swarm - def swarm_router(args: RouterArgs): - """Intelligent routing for agent swarm.""" - last_output = args.get_last_output() or "" - - # Initial call - start with researcher - if args.call_count == 0: - return research_agent - - # After research, analyze - if args.last_agent and args.last_agent.name == "researcher": - return analyzer_agent - - # After analysis, suggest refactoring - if args.last_agent and args.last_agent.name == "analyzer": - return refactor_agent - - # After refactoring, generate tests - if args.last_agent and args.last_agent.name == "refactorer": - return test_agent - - # Stop after all agents have run - return None - - router = create_router( - handler=swarm_router, - name="swarm_router", - description="Routes tasks through agent swarm", - ) - - # Create distributed network - network = create_local_distributed_network( - agents=[research_agent, analyzer_agent, refactor_agent, test_agent], - name="agent-swarm", - router=router, - listen_port=16200, - broadcast_port=16200, - ) - - # Start the network - print("\n๐ŸŒ Starting agent swarm network...") - await network.start() - - print(f"Network: {network.name}") - print(f"Node ID: {network.node_id}") - print(f"Agents: {len(network.agents)}") - for agent in network.agents: - print(f" - {agent.name}: {agent.description}") - - # Run swarm on different tasks - print("\n๐Ÿš€ Running agent swarm tasks...") - - # Task 1: Code improvement workflow - print("\n๐Ÿ“‹ Task 1: Full code improvement workflow") - result = await network.run("Find and improve the authentication code") - - print("\n๐Ÿ“Š Swarm Results:") - print(f"Agents used: {result.get('agent_count', 0)}") - if "agent_outputs" in result: - for agent_name, output in result["agent_outputs"].items(): - print(f"\n{agent_name}:") - if isinstance(output, dict) and "output" in output: - for item in output["output"]: - if item.get("type") == "text": - print(f" {item['content']}") - - # Task 2: Parallel analysis - print("\n๐Ÿ“‹ Task 2: Analyze multiple components") - components = ["database", "api", "frontend"] - - print("Starting parallel analysis...") - tasks = [] - for component in components: - # Each component gets its own mini-swarm - task = network.run(f"Analyze the {component} module") - tasks.append(task) - - # Wait for all parallel tasks - results = await asyncio.gather(*tasks) - - print("\n๐Ÿ“Š Parallel Analysis Results:") - for component, result in zip(components, results, strict=False): - print(f"\n{component.upper()}:") - agent_count = result.get("agent_count", 0) - print(f" Agents used: {agent_count}") - - # Show network statistics - print("\n๐Ÿ“ˆ Network Statistics:") - status = network.get_network_status() - print(f"Total runs: {status.get('total_runs', 0)}") - print(f"Device: {status.get('device', {}).get('model', 'Unknown')}") - print("Inference: hanzo/net distributed (no API calls)") - - # Stop the network - print("\n๐Ÿ›‘ Stopping agent swarm...") - await network.stop() - - print("\nโœ… Agent swarm demo complete!") - print(" - All inference done locally via hanzo/net") - print(" - No external API calls made") - print(" - Fully private and distributed") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/examples/batch_swarm_pattern.py b/pkg/hanzo-mcp/examples/batch_swarm_pattern.py deleted file mode 100644 index f76da7d5c..000000000 --- a/pkg/hanzo-mcp/examples/batch_swarm_pattern.py +++ /dev/null @@ -1,330 +0,0 @@ -#!/usr/bin/env python3 -"""Example: Batch + Swarm Pattern for 10-100x Performance Gains - -This example demonstrates the powerful pattern of: -1. Using batch tool for rapid parallel analysis -2. Using swarm tool for parallel fixes based on analysis -3. Achieving massive performance gains for complex refactoring -""" - -import asyncio -import json -from typing import Any, Dict, List - -# This example shows the pattern - in real usage you'd import the actual tools -# from hanzo_tools.agent.swarm_tool import SwarmTool -# from hanzo_mcp.tools.common.batch_tool import BatchTool - - -async def batch_analyze_codebase(project_path: str) -> Dict[str, Any]: - """Step 1: Use batch tool for rapid parallel analysis. - - Batch tool runs multiple operations in parallel: - - grep for patterns - - read multiple files - - analyze directory structure - - search for specific code patterns - """ - - # Example batch tasks for analyzing a Go project with import issues - batch_tasks = [ - { - "tool_name": "grep", - "input": { - "pattern": "undefined: (\\w+)", - "path": project_path, - "include": "*.go", - }, - }, - { - "tool_name": "grep", - "input": { - "pattern": "^package (\\w+)", - "path": project_path, - "include": "*.go", - }, - }, - { - "tool_name": "grep", - "input": { - "pattern": "^import \\(", - "path": project_path, - "include": "*.go", - "-A": 10, # Get 10 lines after to see full import block - }, - }, - {"tool_name": "tree", "input": {"path": project_path, "depth": 3}}, - ] - - print(f"Running {len(batch_tasks)} analysis tasks in parallel...") - - # In real usage: - # results = await batch_tool.call(ctx, tasks=batch_tasks) - - # Simulated results for example - results = { - "results": [ - { - "status": "success", - "output": """ -vms/xvm/network/atomic.go:18: undefined: common -vms/xvm/network/network.go:25: undefined: common -vms/xvm/network/gossip.go:55: undefined: common -""", - }, - { - "status": "success", - "output": """ -vms/xvm/network/atomic.go:1: package network -vms/xvm/network/network.go:1: package network -vms/xvm/network/gossip.go:1: package network -""", - }, - {"status": "success", "output": "Import blocks found in 15 files..."}, - {"status": "success", "output": "Directory structure analyzed..."}, - ], - "completed": 4, - "failed": 0, - "total_time": 0.5, # Batch runs all in parallel! - } - - print(f"Analysis complete in {results['total_time']}s") - return results - - -def generate_swarm_tasks_from_analysis(analysis: Dict[str, Any]) -> List[Dict]: - """Step 2: Generate targeted swarm tasks based on batch analysis.""" - - # Parse the analysis results to identify files needing fixes - undefined_errors = analysis["results"][0]["output"] - - files_to_fix = set() - for line in undefined_errors.strip().split("\n"): - if ":" in line and "undefined:" in line: - file_path = line.split(":")[0] - files_to_fix.add(file_path) - - # Generate a swarm task for each file - tasks = [] - for file_path in sorted(files_to_fix): - task = { - "file": file_path, - "instruction": f"""Fix undefined symbol errors in {file_path}: - -1. Read the file to understand current imports -2. Add missing import "github.com/luxfi/node/common" -3. Use multi_edit to: - - Add the import in the correct location - - Ensure proper formatting - - Handle both single import and import block cases -4. Verify the file compiles after changes - -Use multi_edit for atomic changes!""", - } - tasks.append(task) - - return tasks - - -async def execute_parallel_fixes(tasks: List[Dict], max_concurrency: int = 10): - """Step 3: Execute all fixes in parallel using swarm.""" - - print(f"\nExecuting {len(tasks)} file fixes in parallel...") - print(f"Max concurrency: {max_concurrency}") - - # In real usage: - # results = await swarm_tool.call(ctx, tasks=tasks, max_concurrency=max_concurrency) - - # Simulated results for example - start_time = asyncio.get_event_loop().time() - await asyncio.sleep(0.5) # Simulate parallel execution - end_time = asyncio.get_event_loop().time() - - results = { - "results": [ - { - "task_index": i, - "status": "completed", - "agent_output": f"Fixed {tasks[i]['file']}", - } - for i in range(len(tasks)) - ], - "completed": len(tasks), - "failed": 0, - "total_time": end_time - start_time, - } - - return results - - -async def demonstrate_performance_gains(): - """Demonstrate the performance gains from batch + swarm pattern.""" - - print("BATCH + SWARM PATTERN DEMONSTRATION") - print("=" * 60) - - # Simulate a large project - num_files = 50 - - print(f"\nScenario: Fix undefined imports in {num_files} Go files") - print("\nApproach 1: Sequential (Traditional)") - print("-" * 40) - - # Sequential approach - sequential_time_per_file = 2.0 # Read, analyze, fix - sequential_total = num_files * sequential_time_per_file - - print(f"Time per file: {sequential_time_per_file}s") - print(f"Total time: {sequential_total}s ({sequential_total / 60:.1f} minutes)") - - print("\nApproach 2: Batch + Swarm (Parallel)") - print("-" * 40) - - # Parallel approach - batch_analysis_time = 0.5 # All analysis in parallel - swarm_fix_time = 2.0 # All fixes in parallel (limited by slowest) - parallel_total = batch_analysis_time + swarm_fix_time - - print(f"Batch analysis (all files): {batch_analysis_time}s") - print(f"Swarm fixes (all parallel): {swarm_fix_time}s") - print(f"Total time: {parallel_total}s") - - speedup = sequential_total / parallel_total - print(f"\nSPEEDUP: {speedup:.1f}x faster!") - print( - f"Time saved: {sequential_total - parallel_total}s ({(sequential_total - parallel_total) / 60:.1f} minutes)" - ) - - print("\n" + "=" * 60) - print("KEY INSIGHTS:") - print("=" * 60) - print(""" -1. Batch Tool Benefits: - - Runs multiple analysis operations in parallel - - Gathers context from entire codebase quickly - - Identifies patterns and dependencies - -2. Swarm Tool Benefits: - - Fixes multiple files in parallel - - Each agent has focused context (one file) - - Multi-edit ensures atomic changes - -3. Combined Pattern: - - Batch: 0.5s to analyze 50+ files - - Swarm: 2s to fix all files (parallel) - - Total: 2.5s vs 100s sequential = 40x speedup! - -4. Scalability: - - 10 files: ~10x speedup - - 50 files: ~40x speedup - - 100 files: ~50x speedup - - 1000 files: ~100x speedup! -""") - - -async def real_world_example(): - """Show a real-world example of the pattern.""" - - print("\n" + "=" * 60) - print("REAL WORLD EXAMPLE: Refactoring Entire Codebase") - print("=" * 60) - - print(""" -Task: Update all deprecated API calls across 500 files - -Step 1: Batch Analysis (0.5s) -""") - - batch_config = { - "description": "Analyze deprecated API usage", - "invocations": [ - { - "tool_name": "grep", - "input": { - "pattern": "OldAPI\\.\\w+\\(", - "path": "/project", - "include": "*.go", - }, - }, - { - "tool_name": "grep_ast", - "input": {"pattern": "OldAPI", "path": "/project"}, - }, - { - "tool_name": "grep", - "input": {"pattern": "import.*OldAPI", "path": "/project"}, - }, - ], - } - - print(f"Batch tasks: {json.dumps(batch_config, indent=2)}") - - print(""" -Step 2: Generate Swarm Tasks (instant) -- Parse batch results -- Create targeted fix task for each file -- Include specific instructions based on usage pattern - -Step 3: Swarm Execution (2-3s for all files!) -""") - - swarm_tasks_example = [ - { - "file": "user_service.go", - "instruction": "Replace OldAPI.GetUser() with NewAPI.User.Get() using multi_edit", - }, - { - "file": "auth_handler.go", - "instruction": "Replace OldAPI.Authenticate() with NewAPI.Auth.Verify() using multi_edit", - }, - # ... 498 more tasks - ] - - print(f"Swarm tasks (first 2 of 500): {json.dumps(swarm_tasks_example, indent=2)}") - - print(""" -Results: -- Sequential approach: 500 files ร— 3s = 1500s (25 minutes) -- Batch + Swarm: 0.5s + 3s = 3.5s total -- Speedup: 428x faster! -- Developer time saved: 24.9 minutes -""") - - -async def main(): - """Run all examples.""" - - # Example 1: Basic batch + swarm workflow - project_path = "/path/to/project" - - print("EXAMPLE 1: Basic Workflow") - print("=" * 60) - - # Step 1: Analyze - analysis = await batch_analyze_codebase(project_path) - - # Step 2: Generate tasks - tasks = generate_swarm_tasks_from_analysis(analysis) - print(f"\nGenerated {len(tasks)} swarm tasks from analysis") - - # Step 3: Fix in parallel - results = await execute_parallel_fixes(tasks, max_concurrency=10) - print(f"Fixed {results['completed']} files in {results['total_time']}s") - - # Example 2: Performance comparison - await demonstrate_performance_gains() - - # Example 3: Real world use case - await real_world_example() - - -if __name__ == "__main__": - print("Batch + Swarm Pattern for 10-100x Performance Gains") - print("=" * 60) - print("\nThis pattern combines:") - print("- Batch tool for parallel analysis") - print("- Swarm tool for parallel execution") - print("- Result: Massive performance gains!") - print("\nRunning examples...\n") - - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/examples/cli_agents_demo.py b/pkg/hanzo-mcp/examples/cli_agents_demo.py deleted file mode 100644 index 895763a78..000000000 --- a/pkg/hanzo-mcp/examples/cli_agents_demo.py +++ /dev/null @@ -1,236 +0,0 @@ -#!/usr/bin/env python3 -"""Demonstration of CLI agent tools. - -This shows how to use the 4 CLI-based agent tools: -1. Claude Code CLI (claude) -2. OpenAI Codex CLI (openai) -3. Google Gemini CLI (gemini) -4. xAI Grok CLI (grok) - -These can be used individually or composed in swarms. -""" - -import os -import sys - -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from hanzo_tools.agent.claude_cli_tool import ClaudeCLITool -from hanzo_tools.agent.codex_cli_tool import CodexCLITool -from hanzo_tools.agent.gemini_cli_tool import GeminiCLITool -from hanzo_tools.agent.grok_cli_tool import GrokCLITool - -from hanzo_mcp.tools.common.permissions import PermissionManager - - -def check_cli_availability(): - """Check which CLI tools are available.""" - print("=" * 60) - print("CLI AGENT AVAILABILITY CHECK") - print("=" * 60) - - pm = PermissionManager() - - # Check each CLI tool - tools = [ - ("Claude Code", ClaudeCLITool(pm)), - ("OpenAI Codex", CodexCLITool(pm)), - ("Google Gemini", GeminiCLITool(pm)), - ("xAI Grok", GrokCLITool(pm)), - ] - - for name, tool in tools: - print(f"\n{name} ({tool.command_name}):") - - # Check if installed - installed = tool.is_installed() - print(f" Installed: {'โœ“' if installed else 'โœ—'}") - - if installed: - print(f" Command: {tool.command_name}") - else: - print(f" Install: See documentation for {name}") - - # Check API key - has_key = tool.has_api_key() - print(f" API Key: {'โœ“' if has_key else 'โœ—'}") - - if not has_key and tool.env_vars: - print(f" Required: {' or '.join(tool.env_vars)}") - - # Show default model - print(f" Default Model: {tool.default_model}") - - -def show_usage_examples(): - """Show usage examples for each CLI agent.""" - print("\n" + "=" * 60) - print("USAGE EXAMPLES") - print("=" * 60) - - print("\n1. Claude Code CLI:") - print(" claude_cli(prompts='Fix the type errors in main.py')") - print( - " claude_cli(prompts='Refactor this function', model='claude-3-opus-20240229')" - ) - - print("\n2. OpenAI Codex CLI:") - print(" codex_cli(prompts='Generate unit tests for the Calculator class')") - print(" codex_cli(prompts='Optimize this algorithm', model='gpt-4-turbo')") - - print("\n3. Google Gemini CLI:") - print(" gemini_cli(prompts='Create a REST API with FastAPI')") - print( - " gemini_cli(prompts='Analyze security vulnerabilities', model='gemini-1.5-flash')" - ) - - print("\n4. xAI Grok CLI:") - print(" grok_cli(prompts='Explain this regex pattern')") - print(" grok_cli(prompts='Write a web scraper', system_prompt='Be concise')") - - -def show_swarm_examples(): - """Show how to use CLI agents in swarms.""" - print("\n" + "=" * 60) - print("SWARM COMPOSITION EXAMPLES") - print("=" * 60) - - print("\n1. Multi-Agent Code Review:") - print(""" -swarm( - tasks=[ - { - "file_path": "/src/core/engine.py", - "instructions": "Review for architecture and design patterns", - "description": "Claude architectural review" - }, - { - "file_path": "/src/core/engine.py", - "instructions": "Check for performance issues and optimizations", - "description": "GPT-4 performance review" - }, - { - "file_path": "/src/core/engine.py", - "instructions": "Analyze for security vulnerabilities", - "description": "Gemini security audit" - } - ], - common_instructions="Provide specific, actionable feedback" -) -""") - - print("\n2. Parallel Refactoring with Different Agents:") - print(""" -swarm( - tasks=[ - { - "file_path": "/src/api/auth.py", - "instructions": "Refactor to use async/await", - "description": "Claude async refactor" - }, - { - "file_path": "/src/api/database.py", - "instructions": "Add type hints and docstrings", - "description": "Codex type annotations" - }, - { - "file_path": "/src/api/validators.py", - "instructions": "Simplify validation logic", - "description": "Gemini simplification" - } - ] -) -""") - - print("\n3. Consensus-Based Decision Making:") - print(""" -# Get multiple perspectives on the same problem -consensus_prompt = "Should we migrate from REST to GraphQL for our API?" - -# Each agent provides their perspective -claude_response = await claude_cli(prompts=consensus_prompt) -codex_response = await codex_cli(prompts=consensus_prompt) -gemini_response = await gemini_cli(prompts=consensus_prompt) -grok_response = await grok_cli(prompts=consensus_prompt) - -# Synthesize responses for a balanced decision -""") - - -def show_auth_management(): - """Show authentication management features.""" - print("\n" + "=" * 60) - print("AUTHENTICATION MANAGEMENT") - print("=" * 60) - - print("\nManaging API keys and accounts:") - print("1. Create accounts for different projects:") - print(" code_auth create --account work --provider claude") - print(" code_auth create --account personal --provider openai") - print(" code_auth create --account research --provider google") - - print("\n2. Switch between accounts:") - print(" code_auth switch --account work") - print(" code_auth switch --account personal") - - print("\n3. Agent-specific accounts (for swarms):") - print(" code_auth agent --agent_id refactor_agent --parent_account work") - print(" code_auth agent --agent_id review_agent --parent_account work") - - print("\n4. Check status:") - print(" code_auth status") - print(" code_auth list") - - -def show_configuration(): - """Show how to configure CLI agents.""" - print("\n" + "=" * 60) - print("CONFIGURATION OPTIONS") - print("=" * 60) - - print("\n1. Environment Variables:") - for provider, vars in [ - ("Claude", ["ANTHROPIC_API_KEY", "CLAUDE_API_KEY"]), - ("OpenAI", ["OPENAI_API_KEY"]), - ("Google", ["GOOGLE_API_KEY", "GEMINI_API_KEY"]), - ("xAI", ["XAI_API_KEY", "GROK_API_KEY"]), - ]: - print(f"\n {provider}:") - for var in vars: - value = "***" if os.environ.get(var) else "not set" - print(f" {var}: {value}") - - print("\n2. Model Selection:") - print(" - Claude: claude-3-5-sonnet-20241022 (default), claude-3-opus-20240229") - print(" - OpenAI: gpt-4o (default), gpt-4-turbo, gpt-4") - print(" - Gemini: gemini-1.5-pro (default), gemini-1.5-flash") - print(" - Grok: grok-2 (default), grok-1") - - print("\n3. Working Directory:") - print(" All CLI agents support working_dir parameter:") - print(" claude_cli(prompts='...', working_dir='/path/to/project')") - - -def main(): - """Run all demonstrations.""" - check_cli_availability() - show_usage_examples() - show_swarm_examples() - show_auth_management() - show_configuration() - - print("\n" + "=" * 60) - print("KEY FEATURES") - print("=" * 60) - print("โ€ข All 4 CLI agents are now available as MCP tools") - print("โ€ข Can be used individually or composed in swarms") - print("โ€ข Support separate accounts to avoid rate limits") - print("โ€ข Work with existing CLI installations") - print("โ€ข No API keys needed if already logged in to CLIs") - print("โ€ข Claude Code is the default for swarm agents") - print("โ€ข Full programmatic control over AI coding assistants") - print("โ€ข Composable for multi-agent workflows") - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-mcp/examples/distributed_network_example.py b/pkg/hanzo-mcp/examples/distributed_network_example.py deleted file mode 100644 index 361b3b068..000000000 --- a/pkg/hanzo-mcp/examples/distributed_network_example.py +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/env python -"""Example of hanzo-mcp using distributed hanzo-network with local LLM.""" - -import asyncio - -from hanzo_network import ( - check_local_llm_status, - create_local_agent, - create_local_distributed_network, - create_tool, -) - - -# MCP-style tools that could be exposed -async def read_file(path: str) -> str: - """Read a file from the filesystem.""" - try: - with open(path, "r") as f: - return f.read() - except Exception as e: - return f"Error reading file: {str(e)}" - - -async def list_files(directory: str = ".") -> str: - """List files in a directory.""" - import os - - try: - files = os.listdir(directory) - return "\n".join(files) - except Exception as e: - return f"Error listing files: {str(e)}" - - -async def search_files(pattern: str, directory: str = ".") -> str: - """Search for files matching a pattern.""" - import glob - import os - - try: - matches = glob.glob(os.path.join(directory, pattern)) - return "\n".join(matches) if matches else "No matches found" - except Exception as e: - return f"Error searching: {str(e)}" - - -async def main(): - """Demonstrate hanzo-mcp integration with distributed network.""" - print("๐ŸŒ Hanzo MCP + Distributed Network Demo") - print("=" * 50) - - # Check local LLM status - print("\n๐Ÿ“ก Checking local LLM availability...") - ollama_status = await check_local_llm_status("ollama") - - if not ollama_status["available"]: - print("โš ๏ธ Ollama not available. Install and run with:") - print(" brew install ollama") - print(" ollama serve") - print(" ollama pull llama3.2") - print("\nContinuing with mock responses...") - else: - print(f"โœ… Ollama available with models: {ollama_status['models']}") - - # Create MCP-style agents with local LLM - file_agent = create_local_agent( - name="file_agent", - description="Agent that handles file operations", - system="""You are a file system assistant. You have access to tools for: -- read_file: Read file contents -- list_files: List directory contents -- search_files: Search for files by pattern - -Help users explore and understand the filesystem.""", - tools=[ - create_tool( - name="read_file", - description="Read a file from the filesystem", - handler=read_file, - ), - create_tool( - name="list_files", - description="List files in a directory", - handler=list_files, - ), - create_tool( - name="search_files", - description="Search for files matching a pattern", - handler=search_files, - ), - ], - local_model="llama3.2", - ) - - # Create a code analysis agent - code_agent = create_local_agent( - name="code_agent", - description="Agent that analyzes code", - system="""You are a code analysis assistant. When given code or file paths: -- Explain what the code does -- Identify potential issues -- Suggest improvements -Work with the file_agent to read code files.""", - tools=[], # This agent focuses on analysis, not tools - local_model="llama3.2", - ) - - # Create distributed network - network = create_local_distributed_network( - agents=[file_agent, code_agent], - name="mcp-network", - node_id="mcp-node-1", - listen_port=15710, - broadcast_port=15710, - ) - - print("\n๐Ÿš€ Starting MCP-compatible network...") - await network.start(wait_for_peers=0) - - # Network status - status = network.get_network_status() - print("\n๐Ÿ“Š Network Status:") - print(f" Node: {status['node_id']}") - print(f" Agents: {', '.join(status['local_agents'])}") - - # Example 1: List files - print("\n๐Ÿ“ Example 1: List files in current directory") - result = await network.run( - prompt="List all Python files in the current directory", - initial_agent=file_agent, - ) - print(f"Response: {result['final_output']}") - - # Example 2: Read and analyze - print("\n๐Ÿ“– Example 2: Read and analyze a file") - result = await network.run( - prompt="Read the pyproject.toml file and tell me what this project is about" - ) - print(f"Response: {result['final_output']}") - - # Example 3: Multi-agent collaboration - print("\n๐Ÿค Example 3: Multi-agent collaboration") - result = await network.run( - prompt="Find all Python files that might contain the main entry point and analyze them" - ) - print(f"Response: {result['final_output']}") - print(f"Agents used: {result['iterations']}") - - # Simulate multiple nodes - import sys - - if len(sys.argv) > 1 and sys.argv[1] == "--multi": - print("\n๐ŸŒ Starting second node...") - - # Create another agent on a different "node" - search_agent = create_local_agent( - name="search_agent", - description="Agent that searches code", - system="You are a code search specialist.", - tools=[ - create_tool( - name="search_files", - description="Search for files", - handler=search_files, - ) - ], - local_model="llama3.2", - ) - - # Second network node - network2 = create_local_distributed_network( - agents=[search_agent], - name="mcp-network", - node_id="mcp-node-2", - listen_port=15711, - broadcast_port=15710, # Same broadcast port! - ) - - await network2.start(wait_for_peers=1) - - print("\n๐Ÿ” Testing cross-node discovery...") - await asyncio.sleep(2) - - status1 = network.get_network_status() - status2 = network2.get_network_status() - - print(f"Node 1 peers: {status1['peer_count']}") - print(f"Node 2 peers: {status2['peer_count']}") - - print("\nโœ… Demo complete!") - await network.stop() - - -if __name__ == "__main__": - print("\nUsage:") - print(" python distributed_network_example.py # Single node demo") - print(" python distributed_network_example.py --multi # Multi-node demo\n") - - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/examples/fix_go_imports_swarm.py b/pkg/hanzo-mcp/examples/fix_go_imports_swarm.py deleted file mode 100644 index 2a92a6365..000000000 --- a/pkg/hanzo-mcp/examples/fix_go_imports_swarm.py +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/env python3 -"""Example: Fix Go import errors in parallel using swarm and batch tools. - -This example shows how to achieve 10-100x performance gains when fixing -multiple files with similar errors by using the swarm tool for parallel execution. -""" - -import asyncio -import json -import re -from pathlib import Path -from typing import Dict, List, Set - -from hanzo_tools.agent.swarm_tool import SwarmTool - -from hanzo_mcp.tools.common.permissions import PermissionManager - - -class GoImportFixer: - """Fixes Go import errors in parallel using swarm.""" - - def __init__(self, project_root: str): - self.project_root = Path(project_root) - self.permission_manager = PermissionManager(allowed_paths=[project_root]) - self.swarm = SwarmTool(self.permission_manager) - - def parse_go_errors(self, error_output: str) -> Dict[str, Set[str]]: - """Parse Go compiler errors to find undefined symbols per file. - - Returns: - Dict mapping file paths to sets of undefined symbols - """ - file_errors = {} - - # Pattern: filename:line:col: undefined: symbol - pattern = r"([^:]+\.go):\d+:\d+: undefined: (\w+)" - - for match in re.finditer(pattern, error_output): - file_path = match.group(1) - symbol = match.group(2) - - if file_path not in file_errors: - file_errors[file_path] = set() - file_errors[file_path].add(symbol) - - return file_errors - - def generate_swarm_tasks(self, file_errors: Dict[str, Set[str]]) -> List[Dict]: - """Generate swarm tasks for fixing each file in parallel. - - Each task will: - 1. Analyze the file to understand current imports - 2. Determine the correct import path for undefined symbols - 3. Use multi_edit to add all missing imports in one operation - """ - tasks = [] - - for file_path, undefined_symbols in file_errors.items(): - # Create detailed instructions for the agent - symbols_list = ", ".join(sorted(undefined_symbols)) - - instruction = f"""Fix undefined symbols in this Go file: {symbols_list} - -Steps: -1. Read the file to understand its current imports and structure -2. Identify the correct import paths for these undefined symbols: - - If 'common' is undefined, add import "github.com/luxfi/node/common" - - If 'utils' is undefined, add import "github.com/luxfi/node/utils" - - For other symbols, determine the appropriate import based on the project structure -3. Use multi_edit to add all missing imports in a single operation: - - Find the existing import block (or create one after 'package' if none exists) - - Add the new imports in alphabetical order - - Ensure proper formatting with tabs/spaces matching the file's style -4. Verify the changes are correct and the file is still valid Go code - -IMPORTANT: Use multi_edit for efficiency - add all imports in one operation! -""" - - tasks.append({"file": file_path, "instruction": instruction}) - - return tasks - - async def fix_imports_parallel(self, error_output: str, max_concurrency: int = 10): - """Fix all import errors in parallel using swarm. - - Args: - error_output: Go compiler error output - max_concurrency: Maximum number of files to fix simultaneously - """ - # Parse errors - file_errors = self.parse_go_errors(error_output) - - if not file_errors: - print("No undefined symbol errors found.") - return - - print(f"Found {len(file_errors)} files with undefined symbols") - for file_path, symbols in file_errors.items(): - print(f" {file_path}: {', '.join(symbols)}") - - # Generate swarm tasks - tasks = self.generate_swarm_tasks(file_errors) - - print(f"\nLaunching swarm with {len(tasks)} parallel agents...") - print(f"Max concurrency: {max_concurrency}") - - # Execute fixes in parallel - ctx = type("Context", (), {})() # Mock context for example - - start_time = asyncio.get_event_loop().time() - result = await self.swarm.call( - ctx, tasks=tasks, max_concurrency=max_concurrency - ) - end_time = asyncio.get_event_loop().time() - - # Parse and display results - results = json.loads(result) - - print(f"\n{'=' * 60}") - print("SWARM EXECUTION COMPLETE") - print(f"{'=' * 60}") - print(f"Total time: {results['total_time']:.2f}s") - print(f"Actual time: {end_time - start_time:.2f}s") - print(f"Files processed: {len(tasks)}") - print(f"Successful: {results['completed']}") - print(f"Failed: {results['failed']}") - - if results["failed"] > 0: - print("\nFailed tasks:") - for r in results["results"]: - if r["status"] == "failed": - print(f" - {tasks[r['task_index']]['file']}") - print(f" Error: {r.get('error', 'Unknown error')}") - - # Calculate performance gain - sequential_estimate = len(tasks) * 2.0 # Assume 2s per file sequentially - parallel_time = results["total_time"] - speedup = sequential_estimate / parallel_time if parallel_time > 0 else 1 - - print("\nPerformance Analysis:") - print(f" Sequential estimate: {sequential_estimate:.1f}s") - print(f" Parallel actual: {parallel_time:.1f}s") - print(f" Speedup: {speedup:.1f}x") - print( - f" Efficiency: {(speedup / min(max_concurrency, len(tasks))) * 100:.1f}%" - ) - - -async def main(): - """Example usage with the error output from the user's message.""" - - error_output = """ -# github.com/luxfi/node/vms/xvm/network -vms/xvm/network/atomic.go:18:2: undefined: common -vms/xvm/network/atomic.go:20:6: undefined: common -vms/xvm/network/atomic.go:24:23: undefined: common -vms/xvm/network/atomic.go:27:18: undefined: common -vms/xvm/network/atomic.go:54:10: undefined: common -vms/xvm/network/atomic.go:101:10: undefined: common -vms/xvm/network/atomic.go:140:24: undefined: common -vms/xvm/network/network.go:25:4: undefined: common -vms/xvm/network/network.go:35:12: undefined: common -vms/xvm/network/gossip.go:55:13: undefined: common -vms/xvm/network/network.go:25:4: too many errors -""" - - # Initialize fixer with project root - # In real usage, this would be the actual project path - fixer = GoImportFixer("/path/to/luxfi/node") - - # Fix all imports in parallel - # Using max_concurrency=3 since we have 3 files - # Could use higher values for larger projects - await fixer.fix_imports_parallel(error_output, max_concurrency=3) - - print("\n" + "=" * 60) - print("EXAMPLE: Using Batch + Swarm for Complex Analysis") - print("=" * 60) - - # Example of combining batch for analysis + swarm for fixes - print(""" -# Step 1: Use batch tool for rapid analysis across all files -batch_tasks = [ - {"tool": "grep", "args": {"pattern": "undefined: common", "path": "vms/"}}, - {"tool": "grep", "args": {"pattern": "^package", "path": "vms/"}}, - {"tool": "grep", "args": {"pattern": "^import", "path": "vms/"}} -] - -# Step 2: Analyze results to understand import patterns - -# Step 3: Use swarm for parallel fixes with context from batch analysis -swarm_tasks = generate_informed_tasks(batch_results) - -# This combination provides: -# - Batch: Fast whole-codebase analysis (100+ files in seconds) -# - Swarm: Parallel fixes with full context per file -# - Result: 10-100x speedup vs sequential processing -""") - - -if __name__ == "__main__": - print("Go Import Fixer - Swarm Parallel Execution Example") - print("=" * 60) - print("\nThis example demonstrates how to:") - print("1. Parse Go compiler errors") - print("2. Generate parallel fix tasks for each file") - print("3. Use swarm to fix all files simultaneously") - print("4. Achieve 10-100x performance gains") - print("\nKey insight: Each file is independent, so they can be fixed in parallel!") - print("=" * 60) - - # Run the example - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/examples/full_swarm_test.py b/pkg/hanzo-mcp/examples/full_swarm_test.py deleted file mode 100644 index 0028f941d..000000000 --- a/pkg/hanzo-mcp/examples/full_swarm_test.py +++ /dev/null @@ -1,279 +0,0 @@ -#!/usr/bin/env python3 -"""Full end-to-end swarm test with Claude Code. - -This demonstrates: -1. Using batch tool to coordinate multiple operations -2. Parallel editing with swarm tool -3. Consensus/review with multiple agents -4. Proper pagination handling -""" - -import asyncio -import os -import shutil -import tempfile - -from hanzo_tools.agent.agent_tool import AgentTool -from hanzo_tools.agent.swarm_tool import SwarmTool -from hanzo_tools.filesystem import DirectoryTree, Read, Write -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_mcp.tools.common.batch_tool import BatchTool -from hanzo_mcp.tools.common.permissions import PermissionManager - - -async def main(): - """Run full swarm test with batch coordination.""" - # Check for API key - if not os.environ.get("ANTHROPIC_API_KEY") and not os.environ.get("CLAUDE_API_KEY"): - print("Error: No Claude API key found!") - print("Set ANTHROPIC_API_KEY or CLAUDE_API_KEY environment variable") - return - - # Create test project - test_dir = tempfile.mkdtemp(prefix="full_swarm_test_") - print(f"Created test project at: {test_dir}") - - try: - # Create a mini project structure - project_structure = { - "src/models/user.py": """# User model -class User: - def __init__(self, name, email): - self.name = name - self.email = email - self.is_active = True - - def get_display_name(self): - return self.name - - def deactivate(self): - self.is_active = False -""", - "src/models/product.py": """# Product model -class Product: - def __init__(self, name, price): - self.name = name - self.price = price - self.in_stock = True - - def get_display_price(self): - return f"${self.price:.2f}" - - def mark_out_of_stock(self): - self.in_stock = False -""", - "src/models/order.py": """# Order model -from .user import User -from .product import Product - -class Order: - def __init__(self, user, products): - self.user = user - self.products = products - self.status = "pending" - - def calculate_total(self): - return sum(p.price for p in self.products) - - def complete(self): - self.status = "completed" -""", - "src/services/user_service.py": """# User service -from ..models.user import User - -class UserService: - def __init__(self): - self.users = [] - - def create_user(self, name, email): - user = User(name, email) - self.users.append(user) - return user - - def find_user(self, email): - for user in self.users: - if user.email == email: - return user - return None -""", - "tests/test_models.py": """# Model tests -import unittest -from src.models.user import User -from src.models.product import Product - -class TestModels(unittest.TestCase): - def test_user_creation(self): - user = User("John Doe", "john@example.com") - self.assertEqual(user.name, "John Doe") - self.assertTrue(user.is_active) - - def test_product_creation(self): - product = Product("Widget", 19.99) - self.assertEqual(product.get_display_price(), "$19.99") -""", - } - - # Create directory structure and files - for filepath, content in project_structure.items(): - full_path = os.path.join(test_dir, filepath) - os.makedirs(os.path.dirname(full_path), exist_ok=True) - with open(full_path, "w") as f: - f.write(content) - - print(f"Created project structure with {len(project_structure)} files") - - # Set up permissions - pm = PermissionManager() - pm._allowed_paths.add(test_dir) - - # Create tools - swarm = SwarmTool(permission_manager=pm) - agent = AgentTool(permission_manager=pm) - read = Read(pm) - write = Write(pm) - tree = DirectoryTree(pm) - - # Create batch tool - batch = BatchTool( - { - "swarm": swarm, - "agent": agent, - "read": read, - "write": write, - "tree": tree, - } - ) - - # Create context - ctx = MCPContext() - - print("\n" + "=" * 60) - print("PHASE 1: Analyze project structure with batch + agent") - print("=" * 60 + "\n") - - # Use batch to analyze project - analysis_result = await batch.call( - ctx, - description="Analyze project", - invocations=[ - {"tool_name": "tree", "input": {"path": test_dir}}, - { - "tool_name": "agent", - "input": { - "prompts": f"Analyze the project structure in {test_dir} and identify all Python classes that need type hints" - }, - }, - ], - ) - - print( - analysis_result[:1000] + "..." - if len(analysis_result) > 1000 - else analysis_result - ) - - print("\n" + "=" * 60) - print("PHASE 2: Parallel editing with swarm") - print("=" * 60 + "\n") - - # Use swarm for parallel editing - edit_result = await swarm.call( - ctx, - tasks=[ - { - "file_path": os.path.join(test_dir, "src/models/user.py"), - "instructions": "Add complete type hints to all methods and attributes", - "description": "Add type hints to User model", - }, - { - "file_path": os.path.join(test_dir, "src/models/product.py"), - "instructions": "Add complete type hints to all methods and attributes", - "description": "Add type hints to Product model", - }, - { - "file_path": os.path.join(test_dir, "src/models/order.py"), - "instructions": "Add complete type hints to all methods, attributes, and imports", - "description": "Add type hints to Order model", - }, - ], - common_instructions="Use Python 3.9+ style type hints. Add from typing import imports as needed.", - max_concurrent=3, - ) - - print(edit_result[:1500] + "..." if len(edit_result) > 1500 else edit_result) - - print("\n" + "=" * 60) - print("PHASE 3: Consensus review with multiple agents") - print("=" * 60 + "\n") - - # Use swarm for consensus review - review_result = await swarm.call( - ctx, - tasks=[ - { - "file_path": os.path.join(test_dir, "src/models/order.py"), - "instructions": "Review the type hints and suggest any improvements from a best practices perspective", - "description": "Agent 1: Best practices review", - }, - { - "file_path": os.path.join(test_dir, "src/models/order.py"), - "instructions": "Review the code for potential bugs or edge cases that the type hints might not catch", - "description": "Agent 2: Bug and edge case review", - }, - ], - common_instructions="Provide specific, actionable feedback", - max_concurrent=2, - ) - - print( - review_result[:1500] + "..." if len(review_result) > 1500 else review_result - ) - - print("\n" + "=" * 60) - print("PHASE 4: Final verification with batch") - print("=" * 60 + "\n") - - # Final verification - verify_result = await batch.call( - ctx, - description="Verify changes", - invocations=[ - { - "tool_name": "read", - "input": { - "file_path": os.path.join(test_dir, "src/models/user.py") - }, - }, - { - "tool_name": "agent", - "input": { - "prompts": f"Verify that all files in {test_dir}/src/models/ now have proper type hints" - }, - }, - ], - ) - - print( - verify_result[:1000] + "..." if len(verify_result) > 1000 else verify_result - ) - - print("\n" + "=" * 60) - print("TEST COMPLETE!") - print("=" * 60) - print("\nThis test demonstrated:") - print("1. โœ“ Batch tool coordinating multiple operations") - print("2. โœ“ Swarm tool with parallel Claude Code editing") - print("3. โœ“ Consensus review with multiple agents") - print("4. โœ“ Proper pagination for large responses") - print("5. โœ“ All agents defaulting to Claude 3.5 Sonnet") - - finally: - # Cleanup - shutil.rmtree(test_dir) - print(f"\nCleaned up test directory: {test_dir}") - - -if __name__ == "__main__": - # Run the full test - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/examples/hanzo_mcp_swarm_integration.py b/pkg/hanzo-mcp/examples/hanzo_mcp_swarm_integration.py deleted file mode 100644 index c49338d77..000000000 --- a/pkg/hanzo-mcp/examples/hanzo_mcp_swarm_integration.py +++ /dev/null @@ -1,298 +0,0 @@ -#!/usr/bin/env python3 -"""Hanzo MCP + Agent Swarm Integration Demo. - -This demonstrates how hanzo-mcp can launch agent swarms with local private AI inference. -""" - -import asyncio -import sys -from pathlib import Path -from typing import Any, List - -# Add hanzo-network to path -sys.path.insert(0, str(Path(__file__).parent.parent.parent / "hanzo-network" / "src")) - -from hanzo_network import ( - check_local_llm_status, - create_local_agent, - create_local_distributed_network, - create_tool, -) -from hanzo_network.llm import HanzoNetProvider - - -class HanzoMCPSwarmLauncher: - """Launcher for agent swarms via hanzo-mcp.""" - - def __init__(self): - self.networks = {} - self.agents = {} - - async def check_infrastructure(self): - """Check that hanzo/net infrastructure is ready.""" - print("๐Ÿ” Checking hanzo/net infrastructure...") - - # Check local LLM status - status = await check_local_llm_status() - print(f"Local LLM Status: {status}") - - # Check available engines - provider = HanzoNetProvider("dummy") - is_available = await provider.is_available() - models = await provider.list_models() - - print(f"Provider Available: {is_available}") - print(f"Models: {', '.join(models)}") - print(f"Engine: {provider.engine_type}") - - return is_available - - def create_code_analysis_swarm(self) -> List[Any]: - """Create a swarm for code analysis tasks.""" - agents = [] - - # File System Agent (MCP-style) - def read_file(path: str) -> str: - return f"File contents of {path}: [mock file content]" - - def search_files(pattern: str) -> str: - return f"Found 10 files matching '{pattern}'" - - fs_agent = create_local_agent( - name="fs_agent", - description="File system operations agent", - system="You handle file system operations like reading and searching files.", - tools=[ - create_tool( - name="read_file", - description="Read file contents", - handler=read_file, - ), - create_tool( - name="search_files", - description="Search for files", - handler=search_files, - ), - ], - local_model="llama3.2", - ) - agents.append(fs_agent) - - # Code Analysis Agent - def analyze_code(code: str) -> str: - return "Analysis: Clean code, follows best practices, complexity: Low" - - analysis_agent = create_local_agent( - name="analysis_agent", - description="Code analysis and metrics", - system="You analyze code quality and provide metrics.", - tools=[ - create_tool( - name="analyze_code", - description="Analyze code quality", - handler=analyze_code, - ) - ], - local_model="llama3.2", - ) - agents.append(analysis_agent) - - # Refactoring Agent - def suggest_refactor(code: str) -> str: - return "Suggested refactoring: Extract method, improve variable names" - - refactor_agent = create_local_agent( - name="refactor_agent", - description="Code refactoring suggestions", - system="You suggest code refactoring improvements.", - tools=[ - create_tool( - name="suggest_refactor", - description="Suggest refactoring", - handler=suggest_refactor, - ) - ], - local_model="llama3.2", - ) - agents.append(refactor_agent) - - return agents - - def create_development_swarm(self) -> List[Any]: - """Create a swarm for development tasks.""" - agents = [] - - # Code Generator Agent - def generate_code(spec: str) -> str: - return ( - f"Generated code for: {spec}\ndef example():\n return 'Hello World'" - ) - - generator_agent = create_local_agent( - name="generator_agent", - description="Code generation from specifications", - system="You generate code from specifications.", - tools=[ - create_tool( - name="generate_code", - description="Generate code", - handler=generate_code, - ) - ], - local_model="llama3.2", - ) - agents.append(generator_agent) - - # Test Writer Agent - def write_tests(code: str) -> str: - return "Generated tests:\ndef test_example():\n assert example() == 'Hello World'" - - test_agent = create_local_agent( - name="test_agent", - description="Test case generation", - system="You write comprehensive test cases.", - tools=[ - create_tool( - name="write_tests", - description="Write test cases", - handler=write_tests, - ) - ], - local_model="llama3.2", - ) - agents.append(test_agent) - - # Documentation Agent - def write_docs(code: str) -> str: - return "Documentation:\n# Example Function\nReturns a greeting message." - - docs_agent = create_local_agent( - name="docs_agent", - description="Documentation generation", - system="You write clear documentation.", - tools=[ - create_tool( - name="write_docs", - description="Write documentation", - handler=write_docs, - ) - ], - local_model="llama3.2", - ) - agents.append(docs_agent) - - return agents - - async def launch_swarm(self, swarm_type: str, task: str): - """Launch a specific swarm type.""" - print(f"\n๐Ÿš€ Launching {swarm_type} swarm...") - - # Create agents based on swarm type - if swarm_type == "analysis": - agents = self.create_code_analysis_swarm() - elif swarm_type == "development": - agents = self.create_development_swarm() - else: - raise ValueError(f"Unknown swarm type: {swarm_type}") - - # Create distributed network - network = create_local_distributed_network( - agents=agents, - name=f"{swarm_type}-swarm", - listen_port=16300 + len(self.networks), - broadcast_port=16300 + len(self.networks), - ) - - # Start network - await network.start() - self.networks[swarm_type] = network - - print(f"โœ… {swarm_type} swarm launched with {len(agents)} agents") - - # Execute task - print(f"\n๐Ÿ“‹ Executing task: {task}") - result = await network.run(task) - - # Display results - print("\n๐Ÿ“Š Results:") - if "output" in result: - for item in result["output"]: - if item.get("type") == "text": - print(f" {item['content']}") - - return result - - async def coordinate_swarms(self, complex_task: str): - """Coordinate multiple swarms for complex tasks.""" - print(f"\n๐ŸŽฏ Coordinating swarms for: {complex_task}") - - # Phase 1: Analysis - analysis_result = await self.launch_swarm( - "analysis", f"Analyze the codebase for: {complex_task}" - ) - - # Phase 2: Development based on analysis - dev_result = await self.launch_swarm( - "development", f"Based on analysis, implement: {complex_task}" - ) - - return {"analysis": analysis_result, "development": dev_result} - - async def shutdown(self): - """Shutdown all networks.""" - print("\n๐Ÿ›‘ Shutting down swarms...") - for name, network in self.networks.items(): - await network.stop() - print(f" โœ… {name} swarm stopped") - - -async def main(): - """Main demo.""" - print("๐Ÿ Hanzo MCP + Agent Swarm Integration") - print("=" * 60) - print("Demonstrating how hanzo-mcp launches agent swarms") - print("with local private AI inference via hanzo/net") - - # Create launcher - launcher = HanzoMCPSwarmLauncher() - - # Check infrastructure - if not await launcher.check_infrastructure(): - print("โŒ Infrastructure not ready") - return - - # Demo 1: Simple swarm launch - print("\n\n๐Ÿ“‹ Demo 1: Simple Analysis Swarm") - await launcher.launch_swarm( - "analysis", "Find all authentication-related code and analyze security" - ) - - # Demo 2: Development swarm - print("\n\n๐Ÿ“‹ Demo 2: Development Swarm") - await launcher.launch_swarm("development", "Create a new user registration system") - - # Demo 3: Coordinated swarms - print("\n\n๐Ÿ“‹ Demo 3: Coordinated Multi-Swarm Task") - await launcher.coordinate_swarms("Refactor the payment processing module") - - # Show final statistics - print("\n\n๐Ÿ“Š Session Statistics:") - print(f"Total swarms launched: {len(launcher.networks)}") - print( - f"Total agents created: {sum(len(n.agents) for n in launcher.networks.values())}" - ) - print("Inference engine: hanzo/net (local)") - print("External API calls: 0") - print("Privacy: 100% on-device execution") - - # Shutdown - await launcher.shutdown() - - print("\nโœ… Hanzo MCP + Swarm integration complete!") - print(" - All swarms used local private inference") - print(" - No data sent to external services") - print(" - Ready for production deployment") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/examples/hanzo_net_demo.py b/pkg/hanzo-mcp/examples/hanzo_net_demo.py deleted file mode 100644 index 5f82d9f18..000000000 --- a/pkg/hanzo-mcp/examples/hanzo_net_demo.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python -"""Demo of hanzo-mcp using hanzo/net distributed inference.""" - -import asyncio - -from hanzo_network import ( - check_local_llm_status, - create_local_agent, - create_local_distributed_network, - create_tool, -) - - -# Demo tools -async def analyze_code(code: str) -> str: - """Analyze code using hanzo/net inference.""" - return f"Code analysis: {len(code)} characters, appears to be Python code" - - -async def generate_function(description: str) -> str: - """Generate a function based on description.""" - return f"""def generated_function(): - # Generated by hanzo/net - # Description: {description} - print("Function generated via distributed inference") - return True""" - - -async def main(): - """Demonstrate hanzo/net distributed inference.""" - print("๐Ÿš€ Hanzo/Net Distributed Inference Demo") - print("=" * 50) - - # Check hanzo/net status - print("\n๐Ÿ“ก Checking hanzo/net status...") - status = await check_local_llm_status("hanzo") - print(f"Provider: {status['provider']}") - print(f"Engine: {status['engine']}") - print(f"Available: {status['available']}") - print(f"Models: {', '.join(status['models'])}") - if status.get("instructions"): - print(f"Note: {status['instructions']}") - - # Create agents using hanzo/net - print("\n๐Ÿค– Creating agents with hanzo/net inference...") - - analyzer = create_local_agent( - name="code_analyzer", - description="Analyzes code using distributed inference", - system="You are powered by hanzo/net distributed inference. Analyze code efficiently.", - tools=[ - create_tool( - name="analyze_code", description="Analyze code", handler=analyze_code - ) - ], - local_model="llama3.2", # Will use hanzo/net - ) - - generator = create_local_agent( - name="code_generator", - description="Generates code using distributed inference", - system="You are powered by hanzo/net distributed inference. Generate clean code.", - tools=[ - create_tool( - name="generate_function", - description="Generate function", - handler=generate_function, - ) - ], - local_model="llama3.2", # Will use hanzo/net - ) - - # Create distributed network - network = create_local_distributed_network( - agents=[analyzer, generator], - name="hanzo-net-demo", - node_id="hanzo-node-1", - listen_port=15730, - broadcast_port=15730, - ) - - print("\n๐ŸŒ Starting distributed network...") - await network.start(wait_for_peers=0) - - # Network info - status = network.get_network_status() - print("\n๐Ÿ“Š Network Status:") - print(f" Node: {status['node_id']}") - print(f" Agents: {', '.join(status['local_agents'])}") - print(f" Device: {status['device_capabilities']['model']}") - print(" Inference: hanzo/net distributed") - - # Test 1: Code analysis - print("\n๐Ÿ’ป Test 1: Distributed Code Analysis") - result = await network.run( - prompt="Analyze this code: def hello(): return 'Hello from hanzo/net!'", - initial_agent=analyzer, - ) - print(f"Result: {result['final_output']}") - - # Test 2: Code generation - print("\n๐Ÿ”ง Test 2: Distributed Code Generation") - result = await network.run( - prompt="Generate a function that calculates fibonacci numbers", - initial_agent=generator, - ) - print(f"Result: {result['final_output']}") - - # Test 3: Multi-agent collaboration - print("\n๐Ÿค Test 3: Distributed Multi-Agent Collaboration") - result = await network.run( - prompt="First analyze what a sorting algorithm does, then generate a bubble sort function" - ) - print(f"Result: {result['final_output']}") - print(f"Agents used: {result['iterations']}") - - print("\nโœ… Hanzo/net distributed inference demo complete!") - print(" - Using hanzo/net for local LLM inference") - print(" - Distributed across network nodes") - print(" - No external API calls needed") - - await network.stop() - - -if __name__ == "__main__": - print("\nThis demo shows hanzo-mcp using hanzo/net distributed inference") - print("instead of external LLM APIs like OpenAI or Anthropic.\n") - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/examples/parallel_edit_demo.py b/pkg/hanzo-mcp/examples/parallel_edit_demo.py deleted file mode 100644 index 6e9dc19b5..000000000 --- a/pkg/hanzo-mcp/examples/parallel_edit_demo.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env python3 -"""Simple demonstration of parallel editing with Claude Code agents. - -This example shows how to use the swarm tool to edit variables across -multiple files in parallel using Claude 3.5 Sonnet. -""" - -import asyncio -import os -import shutil -import tempfile - -from hanzo_tools.agent.swarm_tool import SwarmTool -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_mcp.tools.common.permissions import PermissionManager - - -async def main(): - """Run parallel editing demonstration.""" - # Check for API key - if not os.environ.get("ANTHROPIC_API_KEY") and not os.environ.get("CLAUDE_API_KEY"): - print("Error: No Claude API key found!") - print("Set ANTHROPIC_API_KEY or CLAUDE_API_KEY environment variable") - return - - # Create temporary project directory - test_dir = tempfile.mkdtemp(prefix="parallel_edit_demo_") - print(f"Created test project at: {test_dir}") - - try: - # Create 3 files with old variable names - files = { - "constants.py": """# Application constants -OLD_VERSION = "1.0.0" -OLD_APP_NAME = "Legacy App" -OLD_MAX_USERS = 100 -OLD_TIMEOUT = 30 - -def get_version(): - return OLD_VERSION -""", - "utils.py": """# Utility functions -from constants import OLD_VERSION, OLD_APP_NAME - -def print_header(): - print(f"{OLD_APP_NAME} v{OLD_VERSION}") - print("=" * 40) - -def validate_app_name(name): - return name == OLD_APP_NAME -""", - "main.py": """# Main application -from constants import OLD_VERSION, OLD_APP_NAME, OLD_MAX_USERS, OLD_TIMEOUT -from utils import print_header - -def main(): - print_header() - print(f"Max users: {OLD_MAX_USERS}") - print(f"Timeout: {OLD_TIMEOUT}s") - print(f"Running {OLD_APP_NAME} version {OLD_VERSION}") - -if __name__ == "__main__": - main() -""", - } - - # Write files - for filename, content in files.items(): - filepath = os.path.join(test_dir, filename) - with open(filepath, "w") as f: - f.write(content) - print(f"Created: {filename}") - - # Set up permissions - pm = PermissionManager() - # Add to allowed paths using the public method - pm.add_allowed_path(test_dir) - - # Create swarm tool (defaults to Claude Sonnet) - swarm = SwarmTool(permission_manager=pm) - - # Create MCP context - ctx = MCPContext() - - # Define parallel editing tasks - tasks = [ - { - "file_path": os.path.join(test_dir, "constants.py"), - "instructions": "Change all variable names from OLD_ prefix to NEW_ prefix. Keep the same values.", - "description": "Update constants.py variables", - }, - { - "file_path": os.path.join(test_dir, "utils.py"), - "instructions": "Update imports and all references to use NEW_ prefix instead of OLD_", - "description": "Update utils.py imports", - }, - { - "file_path": os.path.join(test_dir, "main.py"), - "instructions": "Update imports and all references to use NEW_ prefix instead of OLD_", - "description": "Update main.py imports", - }, - ] - - print("\n" + "=" * 60) - print("Starting parallel editing with 3 Claude agents...") - print("=" * 60 + "\n") - - # Execute parallel edits - result = await swarm.call( - ctx, - tasks=tasks, - common_instructions="Ensure all Python code remains valid. Only change the variable names.", - max_concurrent=3, - ) - - print("\n" + "=" * 60) - print("SWARM EXECUTION RESULT:") - print("=" * 60) - print(result) - - # Show the edited files - print("\n" + "=" * 60) - print("EDITED FILES:") - print("=" * 60) - - for filename in files: - filepath = os.path.join(test_dir, filename) - print(f"\n--- {filename} ---") - with open(filepath, "r") as f: - print(f.read()) - - finally: - # Cleanup - shutil.rmtree(test_dir) - print(f"\nCleaned up test directory: {test_dir}") - - -if __name__ == "__main__": - # Run the demo - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/examples/run_local_agents.py b/pkg/hanzo-mcp/examples/run_local_agents.py deleted file mode 100644 index adbd602c9..000000000 --- a/pkg/hanzo-mcp/examples/run_local_agents.py +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/env python -"""Run agents with local hanzo/net inference (using dummy engine for demo).""" - -import asyncio -import sys -from pathlib import Path - -# Add hanzo-network to path -sys.path.insert(0, str(Path(__file__).parent.parent.parent / "hanzo-network" / "src")) - -from hanzo_network import create_agent, create_distributed_network, create_tool -from hanzo_network.core.agent import ModelConfig, ModelProvider - - -# Create real tools -async def search_code(query: str) -> str: - """Search for code patterns.""" - return f"Found 3 matches for '{query}' in codebase:\n1. main.py:42\n2. utils.py:15\n3. test_main.py:8" - - -async def analyze_function(function_name: str) -> str: - """Analyze a function's implementation.""" - return f"""Analysis of '{function_name}': -- Parameters: 2 (x: int, y: int) -- Returns: int -- Complexity: O(1) -- Purpose: Adds two numbers""" - - -async def generate_test(code: str) -> str: - """Generate unit tests for code.""" - return f"""import pytest - -def test_{code.split()[0].lower()}(): - # Test generated by hanzo/net - assert {code.split()[0]}(2, 3) == 5 - assert {code.split()[0]}(0, 0) == 0 - assert {code.split()[0]}(-1, 1) == 0""" - - -async def explain_concept(concept: str) -> str: - """Explain a programming concept.""" - return f"""Explanation of '{concept}': -{concept} is a fundamental programming concept that involves... -Key points: -1. Definition and purpose -2. Common use cases -3. Best practices""" - - -async def main(): - """Run agents with hanzo/net local inference.""" - print("๐Ÿš€ Running Local Agents with Hanzo/Net") - print("=" * 60) - - # Create specialized agents - print("\n๐Ÿค– Creating specialized agents...") - - # Code Search Agent - search_agent = create_agent( - name="search_agent", - description="Searches through codebase", - model=ModelConfig( - provider=ModelProvider.LOCAL, model="llama3.2", temperature=0.3 - ), - system="""You are a code search specialist. Use the search_code tool to find patterns in code. -Be precise and helpful in locating code elements.""", - tools=[ - create_tool( - name="search_code", - description="Search for code patterns", - handler=search_code, - ) - ], - ) - - # Code Analyzer Agent - analyzer_agent = create_agent( - name="analyzer", - description="Analyzes code structure and quality", - model=ModelConfig( - provider=ModelProvider.LOCAL, model="llama3.2", temperature=0.5 - ), - system="""You are a code analysis expert. Use your tools to analyze functions and provide insights. -Focus on code quality, performance, and best practices.""", - tools=[ - create_tool( - name="analyze_function", - description="Analyze a function", - handler=analyze_function, - ) - ], - ) - - # Test Generator Agent - test_agent = create_agent( - name="test_generator", - description="Generates unit tests", - model=ModelConfig( - provider=ModelProvider.LOCAL, model="llama3.2", temperature=0.7 - ), - system="""You are a test generation specialist. Create comprehensive unit tests for given code. -Follow pytest conventions and ensure good coverage.""", - tools=[ - create_tool( - name="generate_test", - description="Generate unit tests", - handler=generate_test, - ) - ], - ) - - # Teacher Agent - teacher_agent = create_agent( - name="teacher", - description="Explains programming concepts", - model=ModelConfig( - provider=ModelProvider.LOCAL, model="llama3.2", temperature=0.8 - ), - system="""You are a programming teacher. Explain concepts clearly and provide examples. -Make complex topics accessible to learners.""", - tools=[ - create_tool( - name="explain_concept", - description="Explain a concept", - handler=explain_concept, - ) - ], - ) - - # Create distributed network - network = create_distributed_network( - agents=[search_agent, analyzer_agent, test_agent, teacher_agent], - name="dev-network", - listen_port=15750, - broadcast_port=15750, - ) - - print("\n๐ŸŒ Starting distributed network...") - await network.start(wait_for_peers=0) - - # Show network status - status = network.get_network_status() - print("\n๐Ÿ“Š Network Status:") - print(f" Node: {status['node_id']}") - print(f" Agents: {len(status['local_agents'])} agents") - for agent in status["local_agents"]: - print(f" - {agent}") - print(" Inference: hanzo/net (local)") - - # Test 1: Code Search - print("\n๐Ÿ” Test 1: Code Search") - result = await network.run( - prompt="Search for all functions that handle authentication", - initial_agent=search_agent, - ) - print(f"Result: {result['final_output']}") - - # Test 2: Code Analysis - print("\n๐Ÿ“Š Test 2: Code Analysis") - result = await network.run( - prompt="Analyze the add function implementation", initial_agent=analyzer_agent - ) - print(f"Result: {result['final_output']}") - - # Test 3: Test Generation - print("\n๐Ÿงช Test 3: Test Generation") - result = await network.run( - prompt="Generate tests for an add function", initial_agent=test_agent - ) - print(f"Result: {result['final_output']}") - - # Test 4: Concept Explanation - print("\n๐Ÿ“š Test 4: Concept Explanation") - result = await network.run( - prompt="Explain what recursion is", initial_agent=teacher_agent - ) - print(f"Result: {result['final_output']}") - - # Test 5: Multi-Agent Collaboration - print("\n๐Ÿค Test 5: Multi-Agent Collaboration") - result = await network.run( - prompt="First search for sorting algorithms, then analyze bubble sort, and finally generate tests for it" - ) - print(f"Result: {result['final_output']}") - print(f"Agents involved: {result['iterations']}") - - # Test 6: Complex Query - print("\n๐ŸŽฏ Test 6: Complex Development Task") - result = await network.run( - prompt="I need help understanding and testing a binary search implementation" - ) - print(f"Result: {result['final_output']}") - - print("\nโœ… Local agent demo complete!") - print(" - All agents running with hanzo/net local inference") - print(" - No external API calls needed") - print(" - Distributed across network nodes") - - await network.stop() - - -if __name__ == "__main__": - print("\nThis demo shows local agents running with hanzo/net inference.") - print("Using the dummy engine for demonstration purposes.\n") - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/examples/run_real_model.py b/pkg/hanzo-mcp/examples/run_real_model.py deleted file mode 100644 index 909236bb3..000000000 --- a/pkg/hanzo-mcp/examples/run_real_model.py +++ /dev/null @@ -1,175 +0,0 @@ -#!/usr/bin/env python -"""Run agents with real local models using MLX.""" - -import asyncio -import sys -from pathlib import Path - -# Add hanzo-network to path -sys.path.insert(0, str(Path(__file__).parent.parent.parent / "hanzo-network" / "src")) - -from hanzo_network import create_agent, create_distributed_network, create_tool -from hanzo_network.core.agent import ModelConfig, ModelProvider - - -# Create real tools that will use the LLM -async def summarize_text(text: str) -> str: - """Summarize the given text.""" - return f"Summary of {len(text)} characters: {text[:100]}..." - - -async def answer_question(question: str) -> str: - """Answer a question based on knowledge.""" - return f"Answer to '{question}': Based on my analysis..." - - -async def write_code(description: str) -> str: - """Write code based on description.""" - return f"""# Code for: {description} -def solution(): - # Implementation here - pass""" - - -async def main(): - """Run agents with real MLX models.""" - print("๐Ÿค– Running Agents with Real Local Models (MLX)") - print("=" * 60) - - # First, let's try to ensure MLX is working - print("\n๐Ÿ“ก Testing MLX availability...") - try: - import mlx.core as mx - - print("โœ… MLX is available!") - print(f" Device: {mx.default_device()}") - - # Try to load mlx-lm - try: - import mlx_lm - - print("โœ… mlx_lm is available!") - except ImportError: - print("โŒ mlx_lm not found. Installing...") - import subprocess - - subprocess.check_call([sys.executable, "-m", "pip", "install", "mlx-lm"]) - - print("โœ… mlx_lm installed!") - - except Exception as e: - print(f"โŒ MLX error: {e}") - return - - # Download a small model if needed - print("\n๐Ÿ“ฅ Checking for local models...") - model_name = "mlx-community/Qwen2.5-0.5B-Instruct-4bit" # Small 0.5B model - - try: - # Try to load the model - from mlx_lm import load - - print(f"Loading {model_name}...") - model, tokenizer = load(model_name) - print("โœ… Model loaded successfully!") - - # Test generation - from mlx_lm import generate - - test_response = generate(model, tokenizer, prompt="Hello, I am", max_tokens=10) - print(f" Test response: {test_response}") - - except Exception as e: - print(f"โŒ Model loading error: {e}") - print(" Note: The model will be downloaded on first use") - - # Create agents with MLX model - print("\n๐Ÿค– Creating agents with MLX inference...") - - # Agent 1: Assistant - assistant = create_agent( - name="assistant", - description="General purpose assistant using MLX", - model=ModelConfig( - provider=ModelProvider.LOCAL, - model="mlx", # This will trigger MLX engine - temperature=0.7, - max_tokens=100, - ), - system="You are a helpful assistant powered by local MLX inference.", - tools=[ - create_tool( - name="summarize_text", - description="Summarize text", - handler=summarize_text, - ), - create_tool( - name="answer_question", - description="Answer questions", - handler=answer_question, - ), - ], - ) - - # Agent 2: Coder - coder = create_agent( - name="coder", - description="Code writing assistant using MLX", - model=ModelConfig( - provider=ModelProvider.LOCAL, model="mlx", temperature=0.3, max_tokens=200 - ), - system="You are a code writing assistant. Write clean, efficient code.", - tools=[ - create_tool(name="write_code", description="Write code", handler=write_code) - ], - ) - - # Create network - network = create_distributed_network( - agents=[assistant, coder], - name="mlx-network", - listen_port=15740, - broadcast_port=15740, - ) - - print("\n๐ŸŒ Starting network with MLX models...") - await network.start(wait_for_peers=0) - - # Test 1: Simple question - print("\n๐Ÿ’ฌ Test 1: Simple Question") - result = await network.run( - prompt="What is machine learning?", initial_agent=assistant - ) - print(f"Response: {result['final_output']}") - - # Test 2: Summarization - print("\n๐Ÿ“ Test 2: Text Summarization") - result = await network.run( - prompt="Summarize this: Machine learning is a subset of artificial intelligence that enables systems to learn and improve from experience without being explicitly programmed.", - initial_agent=assistant, - ) - print(f"Response: {result['final_output']}") - - # Test 3: Code generation - print("\n๐Ÿ’ป Test 3: Code Generation") - result = await network.run( - prompt="Write a Python function to calculate factorial", initial_agent=coder - ) - print(f"Response: {result['final_output']}") - - # Test 4: Multi-agent - print("\n๐Ÿค Test 4: Multi-Agent Collaboration") - result = await network.run( - prompt="First explain what recursion is, then write a recursive function to calculate fibonacci numbers" - ) - print(f"Response: {result['final_output']}") - print(f"Agents involved: {result['iterations']}") - - print("\nโœ… MLX model test complete!") - await network.stop() - - -if __name__ == "__main__": - print("\nThis demo runs real local models using MLX on Apple Silicon.") - print("The first run will download the model (~500MB).\n") - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/examples/swarm_demo_simple.py b/pkg/hanzo-mcp/examples/swarm_demo_simple.py deleted file mode 100644 index 934dd3749..000000000 --- a/pkg/hanzo-mcp/examples/swarm_demo_simple.py +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env python3 -"""Simple demonstration of Claude Code parallel editing. - -This shows the core concept without full MCP infrastructure. -""" - -import os -import shutil -import tempfile - - -def create_test_files(): - """Create test files for demonstration.""" - test_dir = tempfile.mkdtemp(prefix="claude_edit_") - - files = { - "config.py": """# Configuration -OLD_API_KEY = "sk-old-123" -OLD_DB_URL = "postgres://old" -OLD_TIMEOUT = 30 -""", - "utils.py": """# Utils -from config import OLD_API_KEY, OLD_DB_URL - -def connect(): - print(f"Using {OLD_API_KEY}") - print(f"Connecting to {OLD_DB_URL}") -""", - "main.py": """# Main -from config import OLD_API_KEY, OLD_TIMEOUT -from utils import connect - -def main(): - print(f"API: {OLD_API_KEY}") - print(f"Timeout: {OLD_TIMEOUT}") - connect() - -if __name__ == "__main__": - main() -""", - } - - for name, content in files.items(): - path = os.path.join(test_dir, name) - with open(path, "w") as f: - f.write(content) - - return test_dir, files - - -def show_files(test_dir, files, title): - """Display file contents.""" - print(f"\n{title}") - print("=" * 60) - for name in files: - path = os.path.join(test_dir, name) - print(f"\n--- {name} ---") - with open(path, "r") as f: - print(f.read()) - - -def simulate_parallel_edits(test_dir, files): - """Simulate what parallel Claude agents would do.""" - print("\n๐Ÿค– SIMULATING PARALLEL CLAUDE CODE EDITS") - print("=" * 60) - - # Agent 1: Edit config.py - print("\nโœ… Agent 1 (Claude Sonnet): Editing config.py...") - config_path = os.path.join(test_dir, "config.py") - with open(config_path, "r") as f: - content = f.read() - content = content.replace("OLD_", "NEW_") - content = content.replace("sk-old-123", "sk-new-456") - content = content.replace("postgres://old", "postgres://new") - with open(config_path, "w") as f: - f.write(content) - print(" - Renamed all OLD_ variables to NEW_") - print(" - Updated values") - - # Agent 2: Edit utils.py - print("\nโœ… Agent 2 (Claude Sonnet): Editing utils.py...") - utils_path = os.path.join(test_dir, "utils.py") - with open(utils_path, "r") as f: - content = f.read() - content = content.replace("OLD_", "NEW_") - with open(utils_path, "w") as f: - f.write(content) - print(" - Updated imports to use NEW_ prefix") - print(" - Updated variable references") - - # Agent 3: Edit main.py - print("\nโœ… Agent 3 (Claude Sonnet): Editing main.py...") - main_path = os.path.join(test_dir, "main.py") - with open(main_path, "r") as f: - content = f.read() - content = content.replace("OLD_", "NEW_") - with open(main_path, "w") as f: - f.write(content) - print(" - Updated imports to use NEW_ prefix") - print(" - Updated variable references") - - print("\nโœจ All agents completed successfully!") - - -def main(): - """Run the demonstration.""" - print("CLAUDE CODE PARALLEL EDITING DEMO") - print("=" * 60) - print("This demonstrates how the swarm tool works:") - print("- Defaults to Claude 3.5 Sonnet") - print("- Runs multiple agents in parallel") - print("- Each agent edits a different file") - print("=" * 60) - - # Check for API key - has_key = bool( - os.environ.get("ANTHROPIC_API_KEY") or os.environ.get("CLAUDE_API_KEY") - ) - if has_key: - print("\nโœ… Claude API key detected!") - print(" In real usage, actual Claude agents would edit the files.") - else: - print("\nโš ๏ธ No Claude API key found.") - print(" Set ANTHROPIC_API_KEY or CLAUDE_API_KEY for real agents.") - - # Create test files - test_dir, files = create_test_files() - print(f"\nCreated test project at: {test_dir}") - - try: - # Show original files - show_files(test_dir, files, "ORIGINAL FILES") - - # Simulate parallel edits - simulate_parallel_edits(test_dir, files) - - # Show edited files - show_files(test_dir, files, "EDITED FILES (after parallel Claude edits)") - - # Verify changes - print("\nโœ… VERIFICATION") - print("=" * 60) - all_good = True - for name in files: - path = os.path.join(test_dir, name) - with open(path, "r") as f: - content = f.read() - if "OLD_" in content: - print(f"โŒ {name}: Still contains OLD_ variables") - all_good = False - else: - print(f"โœ… {name}: Successfully updated to NEW_ variables") - - if all_good: - print("\n๐ŸŽ‰ All files successfully edited in parallel!") - - # Show the swarm tool configuration - print("\n๐Ÿ“‹ SWARM TOOL CONFIGURATION") - print("=" * 60) - print("Default model: anthropic/claude-3-5-sonnet-20241022") - print("Max concurrent: 10 (default)") - print("Each agent has full editing capabilities") - print("Automatic pagination for large responses") - - finally: - # Cleanup - shutil.rmtree(test_dir) - print(f"\nโœจ Cleaned up: {test_dir}") - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-mcp/examples/test_local_ai.py b/pkg/hanzo-mcp/examples/test_local_ai.py deleted file mode 100755 index dc2427d48..000000000 --- a/pkg/hanzo-mcp/examples/test_local_ai.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env python -"""Test AI working on distributed network with local LLM.""" - -import asyncio - -from hanzo_network import ( - check_local_llm_status, - create_local_agent, - create_local_distributed_network, - create_tool, -) - - -# Create a simple tool that simulates AI work -async def analyze_text(text: str) -> str: - """Analyze text using AI capabilities.""" - # This would normally use the LLM, but for testing we'll simulate - word_count = len(text.split()) - char_count = len(text) - return f"Analysis: {word_count} words, {char_count} characters. The text appears to be about: {text[:50]}..." - - -async def generate_code(description: str) -> str: - """Generate code based on description.""" - # Simulate code generation - return f"""# Generated code for: {description} -def generated_function(): - # This would be AI-generated code - print("Hello from generated code!") - return True -""" - - -async def main(): - """Test AI working on the distributed network.""" - print("๐Ÿค– Testing AI on Distributed Network with Local LLM") - print("=" * 60) - - # Check LLM status - ollama_status = await check_local_llm_status("ollama") - print( - f"\n๐Ÿ“ก Ollama status: {'โœ… Available' if ollama_status['available'] else 'โŒ Not available'}" - ) - - # Create AI agents - analyzer = create_local_agent( - name="text_analyzer", - description="Analyzes text using AI", - system="You are an AI text analysis agent. Use the analyze_text tool to analyze text content.", - tools=[ - create_tool( - name="analyze_text", - description="Analyze text using AI", - handler=analyze_text, - ) - ], - local_model="llama3.2", - ) - - coder = create_local_agent( - name="code_generator", - description="Generates code using AI", - system="You are an AI code generation agent. Use the generate_code tool to create code.", - tools=[ - create_tool( - name="generate_code", - description="Generate code from description", - handler=generate_code, - ) - ], - local_model="llama3.2", - ) - - # Create network - network = create_local_distributed_network( - agents=[analyzer, coder], - name="ai-test-network", - node_id="ai-node", - listen_port=15720, - broadcast_port=15720, - ) - - print("\n๐Ÿš€ Starting AI network...") - await network.start(wait_for_peers=0) - - # Test 1: Text analysis - print("\n๐Ÿ“ Test 1: AI Text Analysis") - result = await network.run( - prompt="Analyze this text: 'Artificial intelligence is transforming how we build software'", - initial_agent=analyzer, - ) - print(f"Result: {result['final_output']}") - - # Test 2: Code generation - print("\n๐Ÿ’ป Test 2: AI Code Generation") - result = await network.run( - prompt="Generate a Python function that calculates fibonacci numbers", - initial_agent=coder, - ) - print(f"Result: {result['final_output']}") - - # Test 3: Multi-agent collaboration - print("\n๐Ÿค Test 3: AI Collaboration") - result = await network.run( - prompt="First analyze the concept of 'recursive algorithms', then generate code for a recursive factorial function" - ) - print(f"Result: {result['final_output']}") - print(f"Agents involved: {result['iterations']}") - - # Network stats - status = network.get_network_status() - print("\n๐Ÿ“Š Network Stats:") - print(f" Node ID: {status['node_id']}") - print(f" Local agents: {', '.join(status['local_agents'])}") - print(f" Peer count: {status['peer_count']}") - print(f" Device: {status['device_capabilities']['model']}") - print(f" Memory: {status['device_capabilities']['memory'] / 1024:.1f} GB") - - print("\nโœ… AI test complete!") - await network.stop() - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/examples/test_local_inference.py b/pkg/hanzo-mcp/examples/test_local_inference.py deleted file mode 100644 index 17970e83a..000000000 --- a/pkg/hanzo-mcp/examples/test_local_inference.py +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/env python3 -"""Test local private AI inference with hanzo/net.""" - -import asyncio -import sys -from pathlib import Path - -# Add hanzo-network to path -sys.path.insert(0, str(Path(__file__).parent.parent.parent / "hanzo-network" / "src")) - -from hanzo_network import create_local_agent, create_tool -from hanzo_network.core.state import Message -from hanzo_network.llm import HanzoNetProvider - - -async def main(): - """Test local inference directly.""" - - print("๐Ÿงช Testing Local Private AI Inference with hanzo/net") - print("=" * 60) - - # Test 1: Direct provider test - print("\n1๏ธโƒฃ Testing HanzoNetProvider directly...") - provider = HanzoNetProvider("dummy") # Using dummy engine for testing - - # Test availability - is_available = await provider.is_available() - print(f"Provider available: {is_available}") - - # Test model listing - models = await provider.list_models() - print(f"Available models: {models}") - - # Test generation - messages = [ - Message(role="system", content="You are a helpful assistant."), - Message(role="user", content="Write a Python function to add two numbers"), - ] - - response = await provider.generate(messages, model="llama3.2") - print("\nGeneration response:") - print(f"Output: {response['output'][0]['content']}") - print(f"Usage: {response['usage']}") - - # Test 2: Agent with local inference - print("\n\n2๏ธโƒฃ Testing Agent with local inference...") - - def calculate(expression: str) -> str: - """Calculate a mathematical expression.""" - try: - result = eval(expression) - return f"Result: {result}" - except Exception: - return "Error: Invalid expression" - - agent = create_local_agent( - name="calculator", - description="A math calculator agent", - system="You are a calculator. Use the calculate tool to solve math problems.", - tools=[ - create_tool( - name="calculate", - description="Calculate mathematical expressions", - handler=calculate, - ) - ], - local_model="llama3.2", - ) - - # Test agent execution - result = await agent.run("What is 25 + 17?") - print("\nAgent response:") - if "output" in result: - for item in result["output"]: - if item.get("type") == "text": - print(f" {item['content']}") - - # Test 3: Tool calling with local inference - print("\n\n3๏ธโƒฃ Testing tool calling...") - - # Test with tool-aware prompt - messages_with_tools = [ - Message( - role="system", content="You are a helpful assistant with access to tools." - ), - Message(role="user", content="Calculate 100 divided by 4"), - ] - - tools = [{"name": "calculate", "description": "Calculate mathematical expressions"}] - - response = await provider.generate( - messages_with_tools, model="llama3.2", tools=tools - ) - - print("\nTool-aware response:") - print(f"Output: {response['output'][0]['content']}") - - # Test 4: Verify no external API calls - print("\n\n4๏ธโƒฃ Verifying local execution...") - print("โœ… All inference done locally via hanzo/net") - print("โœ… No external API calls made") - print("โœ… Using distributed inference engine: dummy") - print( - "\nNote: In production, this would use MLX (Apple Silicon) or Tinygrad engines" - ) - print(" with actual model weights loaded locally.") - - # Test 5: Concurrent inference - print("\n\n5๏ธโƒฃ Testing concurrent local inference...") - - async def run_inference(prompt: str, id: int): - """Run a single inference.""" - start = asyncio.get_event_loop().time() - response = await provider.generate( - [Message(role="user", content=prompt)], model="llama3.2" - ) - end = asyncio.get_event_loop().time() - return { - "id": id, - "prompt": prompt, - "response": response["output"][0]["content"], - "time": end - start, - } - - # Run multiple inferences concurrently - prompts = [ - "What is AI?", - "Explain machine learning", - "What is deep learning?", - "Define neural networks", - ] - - tasks = [run_inference(prompt, i) for i, prompt in enumerate(prompts)] - results = await asyncio.gather(*tasks) - - print("\nConcurrent inference results:") - for result in results: - print(f" [{result['id']}] {result['prompt'][:20]}... -> {result['time']:.3f}s") - - print("\nโœ… Local private AI inference test complete!") - print(" - hanzo/net distributed inference working") - print(" - Ready for agent swarms") - print(" - Fully private and local execution") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/examples/token_counting_demo.py b/pkg/hanzo-mcp/examples/token_counting_demo.py deleted file mode 100644 index d643c8f65..000000000 --- a/pkg/hanzo-mcp/examples/token_counting_demo.py +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env python3 -"""Demonstration of token counting and authentication features. - -This shows: -1. How token counting works (using tiktoken) -2. Claude Code authentication management -3. Separate agent accounts -""" - -import os -import sys - -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from hanzo_tools.agent.code_auth import CodeAuthManager, get_latest_claude_model - -from hanzo_mcp.tools.common.truncate import estimate_tokens - - -def demonstrate_token_counting(): - """Show how token counting works.""" - print("=" * 60) - print("TOKEN COUNTING DEMONSTRATION") - print("=" * 60) - - # Test strings - test_cases = [ - ("Hello, world!", "Short text"), - ("The quick brown fox jumps over the lazy dog. " * 10, "Medium text"), - ( - "import os\nimport sys\n\ndef main():\n print('Hello')\n" * 50, - "Code snippet", - ), - ("๐Ÿš€ Emoji test ๐ŸŽ‰ Unicode ไฝ ๅฅฝ ะผะธั€", "Unicode and emoji"), - ("a" * 1000, "1000 characters"), - ("word " * 5000, "5000 words"), - ] - - print("\nMCP Token Limit: 25,000 tokens") - print("Safety Buffer: 20,000 tokens (leaving 5k margin)") - print("\nToken counting using tiktoken (cl100k_base encoding):") - print("-" * 60) - - for text, description in test_cases: - tokens = estimate_tokens(text) - chars = len(text) - ratio = tokens / chars if chars > 0 else 0 - - print(f"\n{description}:") - print(f" Characters: {chars:,}") - print(f" Tokens: {tokens:,}") - print(f" Ratio: {ratio:.2f} tokens/char") - - if tokens > 20000: - print(" โš ๏ธ WOULD TRIGGER PAGINATION") - - # Show how much text fits in one page - print("\n" + "-" * 60) - print("Approximate content per page:") - - # Estimate for different content types - avg_chars_per_token = 4 # rough average - max_chars = 20000 * avg_chars_per_token - - print(f" Plain text: ~{max_chars:,} characters") - print(f" Lines of code: ~{max_chars // 80:,} lines (80 chars/line)") - print(f" JSON data: ~{max_chars // 2:,} characters (dense)") - print(f" Natural language: ~{20000 // 1.3:,.0f} words") - - -def demonstrate_auth_management(): - """Show authentication management features.""" - print("\n" + "=" * 60) - print("AUTHENTICATION MANAGEMENT") - print("=" * 60) - - auth_manager = CodeAuthManager() - - # Show current status - current = auth_manager.get_active_account() - print(f"\nCurrent account: {current}") - - # List accounts - accounts = auth_manager.list_accounts() - if accounts: - print("\nConfigured accounts:") - for account in accounts: - info = auth_manager.get_account_info(account) - print(f" - {account}: {info['provider']} ({info.get('model', 'default')})") - else: - print("\nNo accounts configured") - - # Show how to create accounts - print("\nTo create accounts:") - print(" code_auth create --account personal --provider claude") - print(" code_auth create --account work --provider openai") - print(" code_auth create --account test --provider deepseek") - - # Show latest Claude model - latest_model = get_latest_claude_model() - print(f"\nLatest Claude Sonnet model: {latest_model}") - - # Check environment - print("\nEnvironment variables detected:") - providers = { - "claude": ["ANTHROPIC_API_KEY", "CLAUDE_API_KEY"], - "openai": ["OPENAI_API_KEY"], - "google": ["GOOGLE_API_KEY", "GEMINI_API_KEY"], - } - - for provider, env_vars in providers.items(): - for var in env_vars: - if var in os.environ: - print(f" โœ“ {var} (for {provider})") - - -def demonstrate_agent_accounts(): - """Show how agent accounts work.""" - print("\n" + "=" * 60) - print("AGENT ACCOUNT MANAGEMENT") - print("=" * 60) - - print("\nSwarm agents can use separate accounts:") - print("1. Each agent gets a unique identifier") - print("2. Credentials are cloned from parent account") - print("3. Agents run independently with their own auth") - - print("\nExample agent accounts:") - agent_ids = [ - "swarm_0_Update_config_py", - "swarm_1_Fix_imports", - "swarm_2_Add_type_hints", - ] - - for agent_id in agent_ids: - print(f" - agent_{agent_id}") - - print("\nWhen enable_claude_code=True:") - print(" - Each swarm agent gets its own account") - print(" - Prevents rate limit conflicts") - print(" - Allows parallel execution without auth issues") - - -def demonstrate_streaming_tokens(): - """Show how streaming token counting works.""" - print("\n" + "=" * 60) - print("STREAMING TOKEN COUNTING") - print("=" * 60) - - print("\nFor long-running commands:") - print("1. Output streams to disk (no memory usage)") - print("2. Tokens counted as chunks arrive") - print("3. Pagination triggered at 20k tokens") - print("4. Command continues in background") - print("5. Cursor allows resuming from same position") - - # Simulate streaming - print("\nSimulated streaming output:") - total_tokens = 0 - chunk_num = 0 - - while total_tokens < 25000: - chunk_num += 1 - chunk = f"Chunk {chunk_num}: " + "x" * 100 + "\n" - chunk_tokens = estimate_tokens(chunk) - total_tokens += chunk_tokens - - if total_tokens < 20000: - print(f" Chunk {chunk_num}: {chunk_tokens} tokens (total: {total_tokens})") - else: - print(f" Chunk {chunk_num}: PAGINATION TRIGGERED at {total_tokens} tokens") - print(" โ†’ Response includes cursor for continuation") - print(" โ†’ Command continues writing to log file") - print(" โ†’ Next page starts from this position") - break - - -def main(): - """Run all demonstrations.""" - demonstrate_token_counting() - demonstrate_auth_management() - demonstrate_agent_accounts() - demonstrate_streaming_tokens() - - print("\n" + "=" * 60) - print("KEY POINTS:") - print("=" * 60) - print("1. Token counting uses tiktoken (same as OpenAI/Anthropic)") - print("2. MCP limit is 25,000 tokens per response") - print("3. We use 20,000 token buffer for safety") - print("4. Swarm can use separate accounts per agent") - print("5. Streaming output handles unlimited content via pagination") - print("6. Default model: claude-3-5-sonnet-20241022 (latest)") - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-mcp/examples/working_agent_swarm.py b/pkg/hanzo-mcp/examples/working_agent_swarm.py deleted file mode 100644 index 08e2fed5f..000000000 --- a/pkg/hanzo-mcp/examples/working_agent_swarm.py +++ /dev/null @@ -1,247 +0,0 @@ -#!/usr/bin/env python3 -"""Working agent swarm demo with hanzo/net local inference.""" - -import asyncio -import sys -from pathlib import Path - -# Add hanzo-network to path -sys.path.insert(0, str(Path(__file__).parent.parent.parent / "hanzo-network" / "src")) - -from hanzo_network import create_local_agent, create_tool - - -class LocalAgentSwarm: - """A swarm of agents using local hanzo/net inference.""" - - def __init__(self): - self.agents = {} - self.results = {} - - def create_agents(self): - """Create specialized agents for the swarm.""" - - # Code Scanner Agent - def scan_files(pattern: str) -> str: - return ( - f"Scanned files matching '{pattern}': Found 15 Python files, 8 JS files" - ) - - self.agents["scanner"] = create_local_agent( - name="scanner", - description="Scans codebase for files", - system="You scan codebases for specific file patterns.", - tools=[ - create_tool( - name="scan_files", - description="Scan for files matching pattern", - handler=scan_files, - ) - ], - local_model="llama3.2", - ) - - # Vulnerability Detector Agent - def detect_vulnerabilities(code: str) -> str: - return ( - "Found 2 potential issues: SQL injection risk, missing input validation" - ) - - self.agents["detector"] = create_local_agent( - name="detector", - description="Detects security vulnerabilities", - system="You detect security vulnerabilities in code.", - tools=[ - create_tool( - name="detect_vulnerabilities", - description="Detect security issues", - handler=detect_vulnerabilities, - ) - ], - local_model="llama3.2", - ) - - # Code Optimizer Agent - def optimize_code(code: str) -> str: - return "Optimized: Reduced complexity from O(nยฒ) to O(n log n)" - - self.agents["optimizer"] = create_local_agent( - name="optimizer", - description="Optimizes code performance", - system="You optimize code for better performance.", - tools=[ - create_tool( - name="optimize_code", - description="Optimize code performance", - handler=optimize_code, - ) - ], - local_model="llama3.2", - ) - - # Documentation Agent - def generate_docs(code: str) -> str: - return "Generated documentation: 3 classes, 15 methods documented" - - self.agents["documenter"] = create_local_agent( - name="documenter", - description="Generates documentation", - system="You generate comprehensive documentation.", - tools=[ - create_tool( - name="generate_docs", - description="Generate documentation", - handler=generate_docs, - ) - ], - local_model="llama3.2", - ) - - # Test Generator Agent - def generate_tests(code: str) -> str: - return "Generated 12 unit tests with 95% coverage" - - self.agents["test_generator"] = create_local_agent( - name="test_generator", - description="Generates test cases", - system="You generate comprehensive test cases.", - tools=[ - create_tool( - name="generate_tests", - description="Generate test cases", - handler=generate_tests, - ) - ], - local_model="llama3.2", - ) - - async def run_sequential_pipeline(self, task: str): - """Run agents in a sequential pipeline.""" - print(f"\n๐Ÿ“‹ Sequential Pipeline: {task}") - print("-" * 50) - - pipeline = ["scanner", "detector", "optimizer", "documenter", "test_generator"] - previous_output = task - - for agent_name in pipeline: - agent = self.agents[agent_name] - print(f"\nโ–ถ๏ธ Running {agent_name}...") - - result = await agent.run(previous_output) - - if "output" in result: - output_text = result["output"][0]["content"] - print(f" Result: {output_text}") - previous_output = output_text - self.results[agent_name] = output_text - - return self.results - - async def run_parallel_analysis(self, modules: list): - """Run agents in parallel on different modules.""" - print(f"\n๐Ÿ“‹ Parallel Analysis of {len(modules)} modules") - print("-" * 50) - - async def analyze_module(module: str): - """Analyze a single module with multiple agents.""" - tasks = [] - - # Each module gets analyzed by multiple agents - for agent_name in ["scanner", "detector", "optimizer"]: - agent = self.agents[agent_name] - task = agent.run(f"Analyze the {module} module") - tasks.append((agent_name, task)) - - # Wait for all agents to complete - results = {} - for agent_name, task in tasks: - result = await task - if "output" in result: - results[agent_name] = result["output"][0]["content"] - - return module, results - - # Run analysis for all modules in parallel - module_tasks = [analyze_module(module) for module in modules] - module_results = await asyncio.gather(*module_tasks) - - # Display results - for module, results in module_results: - print(f"\n๐Ÿ“ฆ {module.upper()}:") - for agent_name, output in results.items(): - print(f" {agent_name}: {output}") - - return dict(module_results) - - async def run_consensus_decision(self, question: str): - """Multiple agents vote on a decision.""" - print(f"\n๐Ÿ“‹ Consensus Decision: {question}") - print("-" * 50) - - # Ask all agents for their opinion - tasks = [] - for agent_name, agent in self.agents.items(): - task = agent.run(f"Should we {question}? Answer yes or no with reasoning.") - tasks.append((agent_name, task)) - - # Collect votes - votes = {} - for agent_name, task in tasks: - result = await task - if "output" in result: - response = result["output"][0]["content"] - votes[agent_name] = response - print(f"\n{agent_name}: {response}") - - # Count consensus - yes_count = sum(1 for v in votes.values() if "yes" in v.lower()) - no_count = len(votes) - yes_count - - print( - f"\n๐Ÿ—ณ๏ธ Consensus: {'YES' if yes_count > no_count else 'NO'} ({yes_count} yes, {no_count} no)" - ) - - return votes - - -async def main(): - """Run the agent swarm demo.""" - - print("๐Ÿ Hanzo Agent Swarm - Working Demo") - print("=" * 60) - print("Using hanzo/net for local private AI inference") - print("No external API calls - everything runs locally") - - # Create and initialize swarm - swarm = LocalAgentSwarm() - swarm.create_agents() - - print(f"\nโœ… Created {len(swarm.agents)} specialized agents:") - for name, agent in swarm.agents.items(): - print(f" - {name}: {agent.description}") - - # Demo 1: Sequential Pipeline - await swarm.run_sequential_pipeline("Analyze and improve the authentication system") - - # Demo 2: Parallel Analysis - modules = ["database", "api", "frontend", "auth"] - await swarm.run_parallel_analysis(modules) - - # Demo 3: Consensus Decision - await swarm.run_consensus_decision("refactor the entire codebase") - - # Show inference statistics - print("\n\n๐Ÿ“Š Swarm Statistics:") - print(f"Total agents: {len(swarm.agents)}") - print("Inference engine: hanzo/net (dummy)") - print("External API calls: 0") - print("Privacy: 100% local execution") - - print("\nโœ… Agent swarm demo complete!") - print(" - All agents used hanzo/net local inference") - print(" - No data left the device") - print(" - Ready for production use with real models") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/hanzo_mcp/__init__.py b/pkg/hanzo-mcp/hanzo_mcp/__init__.py deleted file mode 100644 index f5a00a332..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Hanzo AI - Implementation of Hanzo capabilities using MCP.""" - -# Polyfill typing.override for Python < 3.12 -try: # pragma: no cover - from typing import override as _override # type: ignore -except Exception: # pragma: no cover - import typing as _typing - - def override(obj): # type: ignore - return obj - - _typing.override = override # type: ignore[attr-defined] - -# Configure FastMCP logging globally for stdio transport -import os -import warnings - -# Suppress llm deprecation warnings about event loop -warnings.filterwarnings( - "ignore", message="There is no current event loop", category=DeprecationWarning -) - -if os.environ.get("HANZO_MCP_TRANSPORT") == "stdio": - try: - from fastmcp.utilities.logging import configure_logging - - configure_logging(level="ERROR") - except ImportError: - pass - -# Version from pyproject.toml (single source of truth) -try: - from importlib.metadata import version as _get_version - - __version__ = _get_version("hanzo-mcp") -except Exception: - __version__ = "0.10.24" # fallback - -# Re-export canonical types from hanzoai core. -# hanzo-mcp works standalone but prefers hanzoai when available. -try: - from hanzoai.config import ConfigLoader, RuntimeConfig - from hanzoai.mcp import MCPClient, mcp_tool_name, normalize_mcp_name - from hanzoai.protocols import PermissionMode, PermissionOutcome, PermissionPolicy - from hanzoai.session import CompactionConfig, Session, compact_session -except ImportError: - pass # hanzoai not installed, MCP server still works standalone diff --git a/pkg/hanzo-mcp/hanzo_mcp/__main__.py b/pkg/hanzo-mcp/hanzo_mcp/__main__.py deleted file mode 100644 index e679e6f10..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/__main__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Main entry point for hanzo-mcp when run as a module.""" - -from hanzo_mcp.cli import main - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-mcp/hanzo_mcp/analytics/__init__.py b/pkg/hanzo-mcp/hanzo_mcp/analytics/__init__.py deleted file mode 100644 index 618935b19..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/analytics/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Analytics module for Hanzo MCP.""" - -from .insights_analytics import ( - Analytics, - InsightsAnalytics, - track_error, - track_event, - track_tool_usage, -) - -__all__ = [ - "Analytics", - "InsightsAnalytics", - "track_event", - "track_tool_usage", - "track_error", -] diff --git a/pkg/hanzo-mcp/hanzo_mcp/analytics/insights_analytics.py b/pkg/hanzo-mcp/hanzo_mcp/analytics/insights_analytics.py deleted file mode 100644 index 27b7e89bc..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/analytics/insights_analytics.py +++ /dev/null @@ -1,375 +0,0 @@ -"""Insights analytics integration for Hanzo MCP. - -This module provides analytics tracking for: -- Tool usage and performance -- Error tracking and debugging -- Feature adoption and user behavior -- A/B testing and feature flags -""" - -import asyncio -import functools -import os -import platform -import time -import traceback -from dataclasses import dataclass -from datetime import datetime -from importlib.metadata import PackageNotFoundError, version -from typing import Any, Callable, Dict, Optional, TypeVar - -# Try to import Insights client (used as backend), but make it optional -try: - from insights import Insights - - INSIGHTS_AVAILABLE = True -except ImportError: - INSIGHTS_AVAILABLE = False - Insights = None - - -F = TypeVar("F", bound=Callable[..., Any]) - - -@dataclass -class AnalyticsConfig: - """Configuration for analytics.""" - - api_key: Optional[str] = None - host: str = "https://insights.hanzo.ai" - enabled: bool = True - debug: bool = False - capture_errors: bool = True - capture_performance: bool = True - distinct_id: Optional[str] = None - - -class InsightsAnalytics: - """Main analytics class for Hanzo MCP.""" - - def __init__(self, config: Optional[AnalyticsConfig] = None): - """Initialize analytics with configuration.""" - self.config = config or AnalyticsConfig() - self._client = None - - # Load from environment if not provided - if not self.config.api_key: - self.config.api_key = os.environ.get("INSIGHTS_API_KEY") - - if not self.config.distinct_id: - # Use machine ID or generate one - self.config.distinct_id = self._get_distinct_id() - - # Initialize backend if available and configured - if INSIGHTS_AVAILABLE and self.config.api_key and self.config.enabled: - self._client = Insights( - self.config.api_key, - host=self.config.host, - debug=self.config.debug, - enable_exception_autocapture=self.config.capture_errors, - ) - - def _get_distinct_id(self) -> str: - """Get a distinct ID for this installation.""" - # Try to get from environment - distinct_id = os.environ.get("HANZO_DISTINCT_ID") - if distinct_id: - return distinct_id - - # Use hostname + username as fallback - import getpass - import socket - - hostname = socket.gethostname() - username = getpass.getuser() - return f"{hostname}:{username}" - - def is_enabled(self) -> bool: - """Check if analytics is enabled.""" - return bool(self._client and self.config.enabled) - - def capture(self, event: str, properties: Optional[Dict[str, Any]] = None) -> None: - """Capture an analytics event.""" - if not self.is_enabled(): - return - - try: - # Add common properties - props = { - "timestamp": datetime.utcnow().isoformat(), - "platform": platform.system(), - "python_version": platform.python_version(), - "mcp_version": self._get_package_version(), - **(properties or {}), - } - - self._client.capture(self.config.distinct_id, event, properties=props) - except Exception as e: - if self.config.debug: - print(f"Analytics error: {e}") - - def identify(self, properties: Optional[Dict[str, Any]] = None) -> None: - """Identify the current user/installation.""" - if not self.is_enabled(): - return - - try: - self._client.identify(self.config.distinct_id, properties=properties or {}) - except Exception as e: - if self.config.debug: - print(f"Analytics identify error: {e}") - - def track_tool_usage( - self, - tool_name: str, - duration_ms: Optional[float] = None, - success: bool = True, - error: Optional[str] = None, - metadata: Optional[Dict[str, Any]] = None, - ) -> None: - """Track tool usage event.""" - properties = {"tool_name": tool_name, "success": success, **(metadata or {})} - - if duration_ms is not None: - properties["duration_ms"] = duration_ms - - if error: - properties["error"] = str(error) - - self.capture("tool_used", properties) - - def track_error( - self, error: Exception, context: Optional[Dict[str, Any]] = None - ) -> None: - """Track an error event.""" - if not self.config.capture_errors: - return - - properties = { - "error_type": type(error).__name__, - "error_message": str(error), - "error_traceback": traceback.format_exc(), - **(context or {}), - } - - self.capture("error_occurred", properties) - - def feature_enabled(self, flag_key: str, default: bool = False) -> bool: - """Check if a feature flag is enabled.""" - if not self.is_enabled(): - return default - - try: - return self._client.feature_enabled( - flag_key, self.config.distinct_id, default=default - ) - except Exception: - return default - - def get_feature_flag(self, flag_key: str, default: Any = None) -> Any: - """Get feature flag value.""" - if not self.is_enabled(): - return default - - try: - return self._client.get_feature_flag( - flag_key, self.config.distinct_id, default=default - ) - except Exception: - return default - - def flush(self) -> None: - """Flush any pending events.""" - if self.is_enabled(): - try: - self._client.flush() - except Exception: - pass - - def _get_package_version(self) -> str: - """Get the current package version.""" - try: - return version("hanzo-mcp") - except PackageNotFoundError: - # Fallback to hardcoded version if package not installed - try: - from hanzo_mcp import __version__ - - return __version__ - except ImportError: - return "0.8.14" - - def shutdown(self) -> None: - """Shutdown analytics client.""" - if self.is_enabled(): - try: - self._client.shutdown() - except Exception: - pass - - -# Backward-compatible alias -Analytics = InsightsAnalytics - -# Global analytics instance -_analytics = None - - -def get_analytics() -> InsightsAnalytics: - """Get or create the global analytics instance.""" - global _analytics - if _analytics is None: - _analytics = InsightsAnalytics() - return _analytics - - -def track_event(event: str, properties: Optional[Dict[str, Any]] = None) -> None: - """Track a custom event.""" - get_analytics().capture(event, properties) - - -def track_tool_usage(tool_name: str, **kwargs) -> None: - """Track tool usage.""" - get_analytics().track_tool_usage(tool_name, **kwargs) - - -def track_error(error: Exception, context: Optional[Dict[str, Any]] = None) -> None: - """Track an error.""" - get_analytics().track_error(error, context) - - -def with_analytics(tool_name: str): - """Decorator to track tool usage with analytics.""" - - def decorator(func: F) -> F: - @functools.wraps(func) - async def async_wrapper(*args, **kwargs): - start_time = time.time() - error = None - try: - result = await func(*args, **kwargs) - return result - except Exception as e: - error = e - track_error(e, {"tool": tool_name}) - raise - finally: - duration_ms = (time.time() - start_time) * 1000 - track_tool_usage( - tool_name, - duration_ms=duration_ms, - success=error is None, - error=str(error) if error else None, - ) - - @functools.wraps(func) - def sync_wrapper(*args, **kwargs): - start_time = time.time() - error = None - try: - result = func(*args, **kwargs) - return result - except Exception as e: - error = e - track_error(e, {"tool": tool_name}) - raise - finally: - duration_ms = (time.time() - start_time) * 1000 - track_tool_usage( - tool_name, - duration_ms=duration_ms, - success=error is None, - error=str(error) if error else None, - ) - - # Return appropriate wrapper based on function type - if asyncio.iscoroutinefunction(func): - return async_wrapper - else: - return sync_wrapper - - return decorator - - -def feature_flag(flag_key: str, default: bool = False): - """Decorator to conditionally enable features based on flags.""" - - def decorator(func: F) -> F: - @functools.wraps(func) - def wrapper(*args, **kwargs): - if get_analytics().feature_enabled(flag_key, default): - return func(*args, **kwargs) - else: - raise NotImplementedError(f"Feature '{flag_key}' is not enabled") - - return wrapper - - return decorator - - -# Tool usage context manager -class ToolUsageTracker: - """Context manager for tracking tool usage.""" - - def __init__(self, tool_name: str, metadata: Optional[Dict[str, Any]] = None): - self.tool_name = tool_name - self.metadata = metadata or {} - self.start_time = None - self.error = None - - def __enter__(self): - self.start_time = time.time() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - duration_ms = (time.time() - self.start_time) * 1000 - success = exc_type is None - - if exc_type: - self.error = str(exc_val) - track_error(exc_val, {"tool": self.tool_name, **self.metadata}) - - track_tool_usage( - self.tool_name, - duration_ms=duration_ms, - success=success, - error=self.error, - metadata=self.metadata, - ) - - # Don't suppress exceptions - return False - - -# Async context manager version -class AsyncToolUsageTracker: - """Async context manager for tracking tool usage.""" - - def __init__(self, tool_name: str, metadata: Optional[Dict[str, Any]] = None): - self.tool_name = tool_name - self.metadata = metadata or {} - self.start_time = None - self.error = None - - async def __aenter__(self): - self.start_time = time.time() - return self - - async def __aexit__(self, exc_type, exc_val, exc_tb): - duration_ms = (time.time() - self.start_time) * 1000 - success = exc_type is None - - if exc_type: - self.error = str(exc_val) - track_error(exc_val, {"tool": self.tool_name, **self.metadata}) - - track_tool_usage( - self.tool_name, - duration_ms=duration_ms, - success=success, - error=self.error, - metadata=self.metadata, - ) - - # Don't suppress exceptions - return False diff --git a/pkg/hanzo-mcp/hanzo_mcp/backends/sqlite_plugin.py b/pkg/hanzo-mcp/hanzo_mcp/backends/sqlite_plugin.py deleted file mode 100644 index 6c4a38e28..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/backends/sqlite_plugin.py +++ /dev/null @@ -1,602 +0,0 @@ -"""SQLite backend plugin implementation with namespace, key, tags, and TTL support.""" - -import json -import sqlite3 -import uuid -from datetime import datetime, timezone -from pathlib import Path -from typing import Any, Dict, List, Optional - -from ..plugin_interface import Capability - - -class SQLiteBackendPlugin: - """SQLite backend plugin implementation. - - Supports: - - Namespace-scoped memory storage - - Key-based exact retrieval and wildcard matching - - Tags for filtering and categorization - - TTL (time-to-live) with ISO date expiry - - Append mode for key-based content concatenation - - Version history per key - - Export/import - """ - - # Sentinel for in-memory database (useful for testing) - IN_MEMORY = ":memory:" - - def __init__(self, db_path=None): - """Initialize the SQLite backend plugin. - - Args: - db_path: Path to SQLite DB file, or ":memory:" for in-memory. - Defaults to ~/.hanzo/memory.db. - """ - if db_path == self.IN_MEMORY: - self._db_path = self.IN_MEMORY - else: - self._db_path = db_path or Path.home() / ".hanzo" / "memory.db" - self._client = None - self._conn: Optional[sqlite3.Connection] = None - self._initialized = False - - @property - def name(self) -> str: - return "sqlite" - - @property - def capabilities(self) -> List[Capability]: - return [ - Capability.PERSISTENCE, - Capability.EMBEDDINGS, - Capability.MARKDOWN_IMPORT, - Capability.STRUCTURED_QUERY, - Capability.VECTOR_SEARCH, - ] - - async def initialize(self) -> None: - """Initialize the backend.""" - if not self._initialized: - if self._db_path == self.IN_MEMORY: - # Pure in-memory โ€” skip hanzo_memory client, use direct sqlite3 - self._conn = sqlite3.connect(":memory:", check_same_thread=False) - self._conn.row_factory = sqlite3.Row - else: - # Use hanzo_memory SQLiteMemoryClient if available - try: - from hanzo_memory.db.sqlite_client import SQLiteMemoryClient - - self._client = SQLiteMemoryClient(db_path=self._db_path) - self._conn = self._client.conn - except Exception: - db_str = str(self._db_path) - self._conn = sqlite3.connect(db_str, check_same_thread=False) - self._conn.row_factory = sqlite3.Row - - self._ensure_schema() - self._initialized = True - - def _ensure_schema(self): - """Ensure the memories table has all required columns.""" - conn = self._conn - if not conn: - return - - # Create the memories table if it doesn't exist (standalone mode) - conn.execute(""" - CREATE TABLE IF NOT EXISTS memories ( - id TEXT PRIMARY KEY, - memory_id TEXT UNIQUE NOT NULL, - project_id TEXT NOT NULL DEFAULT 'default', - user_id TEXT NOT NULL DEFAULT 'default', - content TEXT NOT NULL, - memory_type TEXT DEFAULT 'general', - importance REAL DEFAULT 0.5, - context TEXT, - metadata TEXT, - source TEXT, - embedding BLOB, - timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ); - """) - - # Add namespace column if missing - self._add_column_if_missing("memories", "namespace", "TEXT DEFAULT 'default'") - # Add key column if missing - self._add_column_if_missing("memories", "key", "TEXT") - # Add tags column (JSON array as TEXT) - self._add_column_if_missing("memories", "tags", "TEXT") - # Add ttl column (ISO date string) - self._add_column_if_missing("memories", "ttl", "TEXT") - - # Create indexes for new columns - conn.execute( - "CREATE INDEX IF NOT EXISTS idx_memories_namespace ON memories(namespace);" - ) - conn.execute("CREATE INDEX IF NOT EXISTS idx_memories_key ON memories(key);") - conn.execute( - "CREATE INDEX IF NOT EXISTS idx_memories_ns_key ON memories(namespace, key);" - ) - conn.commit() - - def _add_column_if_missing(self, table: str, column: str, col_type: str): - """Add a column to a table if it doesn't already exist.""" - conn = self._conn - if not conn: - return - try: - conn.execute(f"ALTER TABLE {table} ADD COLUMN {column} {col_type}") - conn.commit() - except sqlite3.OperationalError: - # Column already exists - pass - - async def shutdown(self) -> None: - """Shutdown the backend.""" - if self._client: - self._client.close() - self._client = None - elif self._conn: - self._conn.close() - self._conn = None - self._initialized = False - - # ------------------------------------------------------------------ # - # Core CRUD (backward-compatible signatures + new params) - # ------------------------------------------------------------------ # - - async def store_memory( - self, - content: str, - metadata: Dict[str, Any], - user_id: str = "default", - project_id: str = "default", - namespace: str = "default", - key: Optional[str] = None, - tags: Optional[List[str]] = None, - ttl: Optional[str] = None, - append: bool = False, - ) -> str: - """Store a memory and return its ID.""" - if not self._conn: - raise RuntimeError("Plugin not initialized") - - # Inject namespace/key/tags into metadata for backward compat - metadata = dict(metadata) - metadata["namespace"] = namespace - if key: - metadata["key"] = key - if tags: - metadata["tags"] = tags - if ttl: - metadata["ttl"] = ttl - - # Handle append mode: find existing entry with same key+namespace and concatenate - if append and key: - existing = self._find_by_key(key, namespace) - if existing: - new_content = existing["content"] + content - self._conn.execute( - "UPDATE memories SET content = ?, metadata = ?, updated_at = CURRENT_TIMESTAMP WHERE memory_id = ?", - (new_content, json.dumps(metadata), existing["memory_id"]), - ) - self._conn.commit() - return existing["memory_id"] - - memory_id = str(uuid.uuid4()) - tags_json = json.dumps(tags or []) - metadata_json = json.dumps(metadata) - - self._conn.execute( - """ - INSERT INTO memories - (id, memory_id, user_id, project_id, content, importance, context, - metadata, source, embedding, namespace, key, tags, ttl) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - str(uuid.uuid4()), - memory_id, - user_id, - project_id, - content, - metadata.get("importance", 0.5), - json.dumps({}), - metadata_json, - metadata.get("source", ""), - None, # embedding - namespace, - key, - tags_json, - ttl, - ), - ) - self._conn.commit() - return memory_id - - async def retrieve_memory( - self, - query: str, - user_id: str = "default", - project_id: str = "default", - limit: int = 10, - namespace: Optional[str] = None, - metadata_filter: Optional[Dict[str, Any]] = None, - ) -> List[Dict[str, Any]]: - """Retrieve memories based on query.""" - if not self._conn: - raise RuntimeError("Plugin not initialized") - - where_parts = ["user_id = ?", "project_id = ?"] - params: list = [user_id, project_id] - - if namespace: - where_parts.append("namespace = ?") - params.append(namespace) - - if metadata_filter: - ns = metadata_filter.get("namespace") - if ns: - where_parts.append("namespace = ?") - params.append(ns) - agent = metadata_filter.get("agent") - if agent: - where_parts.append("json_extract(metadata, '$.agent') = ?") - params.append(agent) - mtype = metadata_filter.get("type") - if mtype: - where_parts.append("json_extract(metadata, '$.type') = ?") - params.append(mtype) - - # Content search via LIKE if query provided - if query: - where_parts.append("content LIKE ?") - params.append(f"%{query}%") - - # Filter out expired TTL entries - where_parts.append("(ttl IS NULL OR ttl = '' OR ttl > ?)") - params.append(datetime.now(timezone.utc).isoformat()) - - where_clause = " AND ".join(where_parts) - params.append(limit) - - cursor = self._conn.execute( - f"SELECT * FROM memories WHERE {where_clause} ORDER BY created_at DESC LIMIT ?", - params, - ) - return [self._row_to_dict(row) for row in cursor.fetchall()] - - async def search_memory( - self, - query: str, - user_id: str = "default", - project_id: str = "default", - limit: int = 10, - namespace: Optional[str] = None, - metadata_filter: Optional[Dict[str, Any]] = None, - ) -> List[Dict[str, Any]]: - """Search memories based on query with scoring.""" - return await self.retrieve_memory( - query, user_id, project_id, limit, namespace, metadata_filter - ) - - async def delete_memory( - self, - memory_id: str, - user_id: str = "default", - project_id: str = "default", - ) -> bool: - """Delete a memory by ID.""" - if not self._conn: - raise RuntimeError("Plugin not initialized") - - cursor = self._conn.execute( - "DELETE FROM memories WHERE memory_id = ?", - (memory_id,), - ) - self._conn.commit() - return cursor.rowcount > 0 - - # ------------------------------------------------------------------ # - # New operations for TypeScript parity - # ------------------------------------------------------------------ # - - async def get_by_key( - self, - key: str, - namespace: str = "default", - ): - """Get memory by exact key or wildcard within namespace. - - If key contains '*', returns a list of matches. - Otherwise returns a single dict or None. - """ - if not self._conn: - raise RuntimeError("Plugin not initialized") - - now = datetime.now(timezone.utc).isoformat() - - if "*" in key: - # Wildcard: convert glob to SQL LIKE - like_pattern = key.replace("*", "%") - cursor = self._conn.execute( - """SELECT * FROM memories - WHERE key LIKE ? AND namespace = ? - AND (ttl IS NULL OR ttl = '' OR ttl > ?) - ORDER BY created_at DESC""", - (like_pattern, namespace, now), - ) - rows = cursor.fetchall() - return [self._row_to_dict(r) for r in rows] if rows else [] - else: - row_dict = self._find_by_key(key, namespace) - if row_dict and self._is_expired(row_dict): - return None - return row_dict - - async def list_memories( - self, - namespace: Optional[str] = None, - tag: Optional[str] = None, - limit: int = 50, - ) -> List[Dict[str, Any]]: - """List all memories with optional filters.""" - if not self._conn: - raise RuntimeError("Plugin not initialized") - - where_parts: list = [] - params: list = [] - - if namespace: - where_parts.append("namespace = ?") - params.append(namespace) - - if tag: - # tags is a JSON array stored as TEXT; use json_each to filter - where_parts.append( - "EXISTS (SELECT 1 FROM json_each(tags) WHERE json_each.value = ?)" - ) - params.append(tag) - - # Filter expired - now = datetime.now(timezone.utc).isoformat() - where_parts.append("(ttl IS NULL OR ttl = '' OR ttl > ?)") - params.append(now) - - where_clause = " AND ".join(where_parts) if where_parts else "1=1" - params.append(limit) - - cursor = self._conn.execute( - f"SELECT * FROM memories WHERE {where_clause} ORDER BY created_at DESC LIMIT ?", - params, - ) - return [self._row_to_dict(row) for row in cursor.fetchall()] - - async def namespaces(self) -> Dict[str, int]: - """Return all namespaces with their memory counts (excludes expired).""" - if not self._conn: - raise RuntimeError("Plugin not initialized") - - now = datetime.now(timezone.utc).isoformat() - cursor = self._conn.execute( - """SELECT namespace, COUNT(*) as cnt FROM memories - WHERE (ttl IS NULL OR ttl = '' OR ttl > ?) - GROUP BY namespace""", - (now,), - ) - return {row[0] or "default": row[1] for row in cursor.fetchall()} - - async def stats(self) -> Dict[str, Any]: - """Return count, namespaces, size info.""" - if not self._conn: - raise RuntimeError("Plugin not initialized") - - ns = await self.namespaces() - total = sum(ns.values()) - return { - "count": total, - "namespaces": ns, - } - - async def clear(self, namespace: Optional[str] = None) -> int: - """Clear all or namespace-specific memories. Returns count deleted.""" - if not self._conn: - raise RuntimeError("Plugin not initialized") - - if namespace: - cursor = self._conn.execute( - "DELETE FROM memories WHERE namespace = ?", (namespace,) - ) - else: - cursor = self._conn.execute("DELETE FROM memories") - - self._conn.commit() - return cursor.rowcount - - async def tag_memory(self, memory_id: str, tag: str) -> bool: - """Add a tag to a memory.""" - if not self._conn: - raise RuntimeError("Plugin not initialized") - - cursor = self._conn.execute( - "SELECT tags, metadata FROM memories WHERE memory_id = ?", (memory_id,) - ) - row = cursor.fetchone() - if not row: - return False - - tags = json.loads(row[0]) if row[0] else [] - if tag not in tags: - tags.append(tag) - - # Also update tags in metadata - metadata = json.loads(row[1]) if row[1] else {} - metadata["tags"] = tags - - self._conn.execute( - "UPDATE memories SET tags = ?, metadata = ?, updated_at = CURRENT_TIMESTAMP WHERE memory_id = ?", - (json.dumps(tags), json.dumps(metadata), memory_id), - ) - self._conn.commit() - return True - - async def untag_memory(self, memory_id: str, tag: str) -> bool: - """Remove a tag from a memory.""" - if not self._conn: - raise RuntimeError("Plugin not initialized") - - cursor = self._conn.execute( - "SELECT tags, metadata FROM memories WHERE memory_id = ?", (memory_id,) - ) - row = cursor.fetchone() - if not row: - return False - - tags = json.loads(row[0]) if row[0] else [] - if tag in tags: - tags.remove(tag) - else: - return False - - metadata = json.loads(row[1]) if row[1] else {} - metadata["tags"] = tags - - self._conn.execute( - "UPDATE memories SET tags = ?, metadata = ?, updated_at = CURRENT_TIMESTAMP WHERE memory_id = ?", - (json.dumps(tags), json.dumps(metadata), memory_id), - ) - self._conn.commit() - return True - - async def history( - self, key: str, namespace: str = "default" - ) -> List[Dict[str, Any]]: - """Show all versions/entries for a key within a namespace, ordered by creation time.""" - if not self._conn: - raise RuntimeError("Plugin not initialized") - - cursor = self._conn.execute( - "SELECT * FROM memories WHERE key = ? AND namespace = ? ORDER BY created_at ASC", - (key, namespace), - ) - return [self._row_to_dict(row) for row in cursor.fetchall()] - - async def export_memories( - self, namespace: Optional[str] = None - ) -> List[Dict[str, Any]]: - """Export memories as a list of dicts.""" - if not self._conn: - raise RuntimeError("Plugin not initialized") - - if namespace: - cursor = self._conn.execute( - "SELECT * FROM memories WHERE namespace = ? ORDER BY created_at ASC", - (namespace,), - ) - else: - cursor = self._conn.execute( - "SELECT * FROM memories ORDER BY created_at ASC" - ) - - return [self._row_to_dict(row) for row in cursor.fetchall()] - - async def import_memories(self, data: List[Dict[str, Any]]) -> int: - """Import memories from exported data. Returns count imported.""" - if not self._conn: - raise RuntimeError("Plugin not initialized") - - count = 0 - for entry in data: - memory_id = entry.get("memory_id") or entry.get("id") or str(uuid.uuid4()) - metadata = entry.get("metadata", {}) - namespace = metadata.get("namespace", entry.get("namespace", "default")) - key = metadata.get("key", entry.get("key")) - tags = metadata.get("tags", entry.get("tags", [])) - ttl = metadata.get("ttl", entry.get("ttl")) - - self._conn.execute( - """ - INSERT INTO memories - (id, memory_id, user_id, project_id, content, importance, context, - metadata, source, embedding, namespace, key, tags, ttl) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - str(uuid.uuid4()), - memory_id, - entry.get("user_id", "default"), - entry.get("project_id", "default"), - entry.get("content", ""), - entry.get("importance", 0.5), - json.dumps(entry.get("context", {})), - json.dumps(metadata), - entry.get("source", ""), - None, - namespace, - key, - json.dumps(tags if isinstance(tags, list) else []), - ttl, - ), - ) - count += 1 - - self._conn.commit() - return count - - # ------------------------------------------------------------------ # - # Internal helpers - # ------------------------------------------------------------------ # - - def _find_by_key(self, key: str, namespace: str) -> Optional[Dict[str, Any]]: - """Find a single memory by exact key + namespace.""" - if not self._conn: - return None - cursor = self._conn.execute( - "SELECT * FROM memories WHERE key = ? AND namespace = ? ORDER BY created_at DESC LIMIT 1", - (key, namespace), - ) - row = cursor.fetchone() - return self._row_to_dict(row) if row else None - - def _is_expired(self, memory: Dict[str, Any]) -> bool: - """Check if a memory's TTL has expired.""" - ttl = memory.get("ttl") or (memory.get("metadata", {}).get("ttl")) - if not ttl: - return False - try: - expiry = datetime.fromisoformat(ttl) - if expiry.tzinfo is None: - expiry = expiry.replace(tzinfo=timezone.utc) - return datetime.now(timezone.utc) > expiry - except (ValueError, TypeError): - return False - - def _row_to_dict(self, row) -> Dict[str, Any]: - """Convert a sqlite3.Row to a dict with parsed JSON fields.""" - if row is None: - return {} - - # sqlite3.Row supports both index and key access - d = dict(row) if hasattr(row, "keys") else {} - if not d: - # Fallback for tuple rows - return {} - - # Parse JSON fields - for field in ("metadata", "context"): - if field in d and isinstance(d[field], str): - try: - d[field] = json.loads(d[field]) - except (json.JSONDecodeError, TypeError): - d[field] = {} - - if "tags" in d and isinstance(d["tags"], str): - try: - d["tags"] = json.loads(d["tags"]) - except (json.JSONDecodeError, TypeError): - d["tags"] = [] - - return d diff --git a/pkg/hanzo-mcp/hanzo_mcp/bridge.py b/pkg/hanzo-mcp/hanzo_mcp/bridge.py deleted file mode 100644 index c267fde20..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/bridge.py +++ /dev/null @@ -1,479 +0,0 @@ -"""MCP Bridge for inter-Claude communication. - -This module provides MCP server functionality that allows Claude instances -to communicate with each other, enabling peer-to-peer agent networks. -""" - -import argparse -import asyncio -import logging -import os -from dataclasses import dataclass -from typing import Any, Dict, List, Optional - -from mcp.server.fastmcp import FastMCP -from mcp.server.models import InitializationOptions -from mcp.server.stdio import stdio_server - -logger = logging.getLogger(__name__) - - -@dataclass -class BridgeConfig: - """Configuration for MCP bridge.""" - - target_port: int - instance_id: int - role: str - source_instance: Optional[int] = None - target_instance: Optional[int] = None - - -class ClaudeBridge(FastMCP): - """MCP Bridge server for Claude-to-Claude communication.""" - - def __init__(self, config: BridgeConfig): - """Initialize the bridge. - - Args: - config: Bridge configuration - """ - # Set server name based on target instance - super().__init__(f"claude_instance_{config.instance_id}") - - self.config = config - self.conversation_history: List[Dict[str, Any]] = [] - self.shared_context: Dict[str, Any] = {} - - # Register tools - self._register_tools() - - def _register_tools(self): - """Register MCP tools for inter-Claude communication.""" - - @self.tool() - async def chat_with_claude(message: str, context: Optional[str] = None) -> str: - """Chat with another Claude instance. - - Args: - message: Message to send to the other Claude - context: Optional context to provide - - Returns: - Response from the other Claude instance - """ - logger.info(f"Bridge {self.config.instance_id}: Received chat request") - - # Record in conversation history - self.conversation_history.append( - { - "from": self.config.source_instance, - "to": self.config.target_instance, - "message": message, - "context": context, - } - ) - - # Simulate response (in production, this would make actual API call) - response = await self._forward_to_claude(message, context) - - self.conversation_history.append( - { - "from": self.config.target_instance, - "to": self.config.source_instance, - "response": response, - } - ) - - return response - - @self.tool() - async def ask_claude_to_review( - code: str, description: str, focus_areas: Optional[List[str]] = None - ) -> Dict[str, Any]: - """Ask another Claude to review code. - - Args: - code: Code to review - description: Description of what the code does - focus_areas: Specific areas to focus on (e.g., ["security", "performance"]) - - Returns: - Review feedback from the other Claude - """ - logger.info(f"Bridge {self.config.instance_id}: Code review request") - - review_prompt = self._build_review_prompt(code, description, focus_areas) - review = await self._forward_to_claude(review_prompt) - - return { - "reviewer": f"claude_{self.config.instance_id}", - "role": self.config.role, - "feedback": review, - "focus_areas": focus_areas or ["general"], - } - - @self.tool() - async def delegate_to_claude( - task: str, requirements: List[str], constraints: Optional[List[str]] = None - ) -> Dict[str, Any]: - """Delegate a task to another Claude instance. - - Args: - task: Task description - requirements: List of requirements - constraints: Optional constraints - - Returns: - Task completion result from the other Claude - """ - logger.info(f"Bridge {self.config.instance_id}: Task delegation") - - delegation_prompt = self._build_delegation_prompt( - task, requirements, constraints - ) - result = await self._forward_to_claude(delegation_prompt) - - return { - "delegated_to": f"claude_{self.config.instance_id}", - "role": self.config.role, - "task": task, - "result": result, - "status": "completed", - } - - @self.tool() - async def get_claude_opinion( - question: str, - options: Optional[List[str]] = None, - criteria: Optional[List[str]] = None, - ) -> Dict[str, Any]: - """Get another Claude's opinion on a decision. - - Args: - question: The question or decision to get opinion on - options: Optional list of options to choose from - criteria: Optional evaluation criteria - - Returns: - Opinion and reasoning from the other Claude - """ - logger.info(f"Bridge {self.config.instance_id}: Opinion request") - - opinion_prompt = self._build_opinion_prompt(question, options, criteria) - opinion = await self._forward_to_claude(opinion_prompt) - - return { - "advisor": f"claude_{self.config.instance_id}", - "role": self.config.role, - "question": question, - "opinion": opinion, - "options_considered": options, - "criteria_used": criteria, - } - - @self.tool() - async def share_context_with_claude( - key: str, value: Any, description: Optional[str] = None - ) -> bool: - """Share context with another Claude instance. - - Args: - key: Context key - value: Context value - description: Optional description of the context - - Returns: - Success status - """ - logger.info(f"Bridge {self.config.instance_id}: Sharing context '{key}'") - - self.shared_context[key] = { - "value": value, - "description": description, - "shared_by": self.config.source_instance, - "shared_with": self.config.target_instance, - } - - return True - - @self.tool() - async def get_shared_context(key: Optional[str] = None) -> Dict[str, Any]: - """Get shared context from Claude network. - - Args: - key: Optional specific key to retrieve - - Returns: - Shared context data - """ - if key: - return self.shared_context.get(key, {}) - return self.shared_context - - @self.tool() - async def brainstorm_with_claude( - topic: str, num_ideas: int = 5, constraints: Optional[List[str]] = None - ) -> List[str]: - """Brainstorm ideas with another Claude. - - Args: - topic: Topic to brainstorm about - num_ideas: Number of ideas to generate - constraints: Optional constraints - - Returns: - List of brainstormed ideas - """ - logger.info(f"Bridge {self.config.instance_id}: Brainstorming request") - - brainstorm_prompt = f""" - Please brainstorm {num_ideas} ideas about: {topic} - - {"Constraints: " + ", ".join(constraints) if constraints else ""} - - Provide creative and practical ideas. - """ - - response = await self._forward_to_claude(brainstorm_prompt) - - # Parse response into list (simplified) - ideas = response.split("\n") - ideas = [idea.strip() for idea in ideas if idea.strip()] - - return ideas[:num_ideas] - - @self.tool() - async def get_claude_status() -> Dict[str, Any]: - """Get status of the connected Claude instance. - - Returns: - Status information - """ - return { - "instance_id": self.config.instance_id, - "role": self.config.role, - "status": "available", - "conversation_count": len(self.conversation_history), - "shared_context_keys": list(self.shared_context.keys()), - } - - def _build_review_prompt( - self, code: str, description: str, focus_areas: Optional[List[str]] - ) -> str: - """Build a code review prompt.""" - prompt = f""" - Please review the following code: - - Description: {description} - - Code: - ``` - {code} - ``` - """ - - if focus_areas: - prompt += f"\n\nPlease focus particularly on: {', '.join(focus_areas)}" - - prompt += """ - - Provide constructive feedback on: - 1. Potential bugs or issues - 2. Code quality and best practices - 3. Performance considerations - 4. Security concerns - 5. Suggestions for improvement - """ - - return prompt - - def _build_delegation_prompt( - self, task: str, requirements: List[str], constraints: Optional[List[str]] - ) -> str: - """Build a task delegation prompt.""" - prompt = f""" - Please complete the following task: - - Task: {task} - - Requirements: - {chr(10).join(f"- {req}" for req in requirements)} - """ - - if constraints: - prompt += f""" - - Constraints: - {chr(10).join(f"- {con}" for con in constraints)} - """ - - prompt += """ - - Provide a complete solution that meets all requirements. - """ - - return prompt - - def _build_opinion_prompt( - self, question: str, options: Optional[List[str]], criteria: Optional[List[str]] - ) -> str: - """Build an opinion request prompt.""" - prompt = f""" - I need your opinion on the following: - - Question: {question} - """ - - if options: - prompt += f""" - - Options to consider: - {chr(10).join(f"{i + 1}. {opt}" for i, opt in enumerate(options))} - """ - - if criteria: - prompt += f""" - - Please evaluate based on these criteria: - {chr(10).join(f"- {crit}" for crit in criteria)} - """ - - prompt += """ - - Provide your recommendation with clear reasoning. - """ - - return prompt - - async def _forward_to_claude( - self, prompt: str, context: Optional[str] = None - ) -> str: - """Forward a request to the target Claude instance. - - In production, this would make an actual API call to the Claude instance. - For now, it returns a simulated response. - """ - # Add context if provided - full_prompt = prompt - if context: - full_prompt = f"Context: {context}\n\n{prompt}" - - # Log the forwarding - logger.info( - f"Forwarding from instance {self.config.source_instance} to {self.config.target_instance}" - ) - logger.debug(f"Prompt: {full_prompt[:200]}...") - - # In production, this would: - # 1. Connect to the target Claude instance API - # 2. Send the prompt - # 3. Receive and return the response - - # Simulated response based on role - if self.config.role.startswith("critic"): - return f""" - As {self.config.role}, I've analyzed your request: - - Strengths: - - The approach is logical and well-structured - - Good attention to requirements - - Areas for improvement: - - Consider edge cases more thoroughly - - Add more comprehensive error handling - - Optimize for performance in high-load scenarios - - Recommendation: Proceed with suggested improvements. - """ - else: - return f""" - Response from {self.config.role} (instance {self.config.instance_id}): - - I've processed your request: "{prompt[:100]}..." - - The task has been completed successfully with the following approach: - 1. Analyzed the requirements - 2. Implemented the solution - 3. Validated the results - - The solution meets all specified criteria. - """ - - -async def run_bridge_server(config: BridgeConfig): - """Run the MCP bridge server. - - Args: - config: Bridge configuration - """ - # Configure logging - logging.basicConfig( - level=logging.INFO, - format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", - ) - - logger.info(f"Starting MCP Bridge for Claude instance {config.instance_id}") - logger.info(f"Role: {config.role}") - logger.info(f"Target port: {config.target_port}") - - # Create and run the bridge - bridge = ClaudeBridge(config) - - # Run the stdio server - async with stdio_server() as (read_stream, write_stream): - await bridge.run( - read_stream=read_stream, - write_stream=write_stream, - initialization_options=InitializationOptions( - server_name=bridge.name, - server_version="1.0.0", - capabilities=bridge.get_capabilities(), - ), - ) - - -def main(): - """Main entry point for the bridge.""" - parser = argparse.ArgumentParser( - description="MCP Bridge for Claude-to-Claude communication" - ) - parser.add_argument( - "--target-port", - type=int, - required=True, - help="Port of the target Claude instance", - ) - parser.add_argument( - "--instance-id", - type=int, - required=True, - help="ID of the target Claude instance", - ) - parser.add_argument( - "--role", - type=str, - required=True, - help="Role of the target instance (primary, critic_1, etc.)", - ) - - args = parser.parse_args() - - # Get source/target from environment - source_instance = int(os.environ.get("SOURCE_INSTANCE", "0")) - target_instance = int(os.environ.get("TARGET_INSTANCE", args.instance_id)) - - config = BridgeConfig( - target_port=args.target_port, - instance_id=args.instance_id, - role=args.role, - source_instance=source_instance, - target_instance=target_instance, - ) - - # Run the bridge - asyncio.run(run_bridge_server(config)) - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-mcp/hanzo_mcp/cli.py b/pkg/hanzo-mcp/hanzo_mcp/cli.py deleted file mode 100644 index 526c5e05d..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/cli.py +++ /dev/null @@ -1,658 +0,0 @@ -"""Command-line interface for the Hanzo AI server. - -This module intentionally defers heavy imports (like the server and its -dependencies) until after we determine the transport and configure logging. -This prevents any stdout/stderr noise from imports that would corrupt the -MCP stdio transport used by Claude Desktop and other MCP clients. -""" - -import argparse -import json -import logging -import os -import signal -import sys -from pathlib import Path -from typing import Any, cast - -# Configure uvloop early (before any async imports) for better performance -# This is a no-op on Windows or if uvloop is not installed -try: - from hanzo_mcp.utils.event_loop import configure_event_loop - - configure_event_loop(quiet=True) -except ImportError: - pass - - -# Import timeout parser (deferred to avoid early imports) -def _parse_timeout_arg(timeout_str: str) -> float: - """Parse timeout argument with human-readable format support.""" - try: - from hanzo_mcp.tools.common.timeout_parser import parse_timeout - - return parse_timeout(timeout_str) - except ImportError: - # Fallback if parser not available - try: - return float(timeout_str) - except ValueError: - raise ValueError(f"Invalid timeout format: '{timeout_str}'") - - -def main() -> None: - """Run the CLI for the Hanzo AI server.""" - # Handle 'serve' subcommand for Claude Code compatibility - # Claude Code calls: hanzo-mcp serve --enable-agent - # We support both: hanzo-mcp [serve] [options] - if len(sys.argv) > 1 and sys.argv[1] == "serve": - # Remove 'serve' from argv so argparse treats it as direct invocation - sys.argv = [sys.argv[0]] + sys.argv[2:] - - # Pre-parse arguments to check transport type early, BEFORE importing server - early_parser = argparse.ArgumentParser(add_help=False) - early_parser.add_argument("--transport", choices=["stdio", "sse"], default="stdio") - # Support --enable-agent as alias for --enable-agent-tool (Claude Code compatibility) - early_parser.add_argument( - "--enable-agent", dest="enable_agent", action="store_true" - ) - early_args, _ = early_parser.parse_known_args() - - # Configure logging VERY early based on transport - suppress_stdout = False - original_stdout = sys.stdout - if early_args.transport == "stdio": - # Set environment variable for server to detect stdio mode as early as possible - os.environ["HANZO_MCP_TRANSPORT"] = "stdio" - # Aggressively quiet common dependency loggers/warnings in stdio mode - os.environ.setdefault("PYTHONWARNINGS", "ignore") - os.environ.setdefault("LITELLM_LOG", "ERROR") - os.environ.setdefault("LITELLM_LOGGING_LEVEL", "ERROR") - os.environ.setdefault("FASTMCP_LOG_LEVEL", "ERROR") - - # Suppress FastMCP logging (if available) and all standard logging - try: - from fastmcp.utilities.logging import configure_logging # type: ignore - - configure_logging(level="ERROR") - except Exception: - pass - - logging.basicConfig( - level=logging.ERROR, # Only show errors - handlers=[], # No handlers for stdio to prevent protocol corruption - ) - - # stderr stays attached: the stdio protocol rides stdout only, and MCP - # clients capture stderr for diagnostics. Silencing it hides startup - # tracebacks and turns any crash into an undiagnosable failure. - - # Suppress stdout during potentially noisy imports unless user requested help/version - _stdout_devnull = None - if not any(flag in sys.argv for flag in ("--version", "-h", "--help")): - _stdout_devnull = open(os.devnull, "w") - sys.stdout = _stdout_devnull - suppress_stdout = True - - # Import the server only AFTER transport/logging have been configured to avoid import-time noise - from hanzo_mcp.server import HanzoMCPServer - - # Avoid importing hanzo_mcp package just to get version (it can have side-effects). - try: - from importlib.metadata import version as _pkg_version # py3.8+ - - _version = _pkg_version("hanzo-mcp") - except Exception: - _version = "unknown" - - # Get async backend info - try: - from hanzo_async import using_uvloop - - if using_uvloop(): - try: - import uvloop - - _async_backend = f"uvloop {uvloop.__version__}" - except ImportError: - _async_backend = "uvloop" - else: - _async_backend = "asyncio" - except ImportError: - _async_backend = "asyncio" - - _version_info = f"hanzo-mcp {_version} (async: {_async_backend})" - - parser = argparse.ArgumentParser( - description="MCP server implementing Hanzo AI capabilities" - ) - - parser.add_argument("--version", action="version", version=_version_info) - - _ = parser.add_argument( - "--transport", - choices=["stdio", "sse"], - default="stdio", - help="Transport protocol to use (default: stdio)", - ) - - _ = parser.add_argument( - "--name", - default="hanzo-mcp", - help="Name of the MCP server (default: hanzo-mcp)", - ) - - _ = parser.add_argument( - "--allow-path", - action="append", - dest="allowed_paths", - help="Add an allowed path (can be specified multiple times)", - ) - - _ = parser.add_argument( - "--project", - action="append", - dest="project_paths", - help="Add a project path for prompt generation (can be specified multiple times)", - ) - - _ = parser.add_argument( - "--agent-model", - dest="agent_model", - help="Specify the model name in LiteLLM format (e.g., 'openai/gpt-4o', 'anthropic/claude-4-sonnet')", - ) - - _ = parser.add_argument( - "--agent-max-tokens", - dest="agent_max_tokens", - type=int, - help="Specify the maximum tokens for agent responses", - ) - - _ = parser.add_argument( - "--agent-api-key", - dest="agent_api_key", - help="Specify the API key for the LLM provider (for development/testing only)", - ) - - _ = parser.add_argument( - "--agent-base-url", - dest="agent_base_url", - help="Specify the base URL for the LLM provider API endpoint (e.g., 'http://localhost:1234/v1')", - ) - - _ = parser.add_argument( - "--agent-max-iterations", - dest="agent_max_iterations", - type=int, - default=10, - help="Maximum number of iterations for agent (default: 10)", - ) - - _ = parser.add_argument( - "--agent-max-tool-uses", - dest="agent_max_tool_uses", - type=int, - default=30, - help="Maximum number of total tool uses for agent (default: 30)", - ) - - _ = parser.add_argument( - "--enable-agent-tool", - dest="enable_agent_tool", - action="store_true", - default=False, - help="Enable the agent tool (disabled by default)", - ) - - # Alias for Claude Code compatibility (uses --enable-agent) - _ = parser.add_argument( - "--enable-agent", - dest="enable_agent_tool", - action="store_true", - default=False, - help=argparse.SUPPRESS, # Hidden alias for --enable-agent-tool - ) - - _ = parser.add_argument( - "--command-timeout", - dest="command_timeout", - type=str, - default="45s", - help="Default timeout for command execution (default: 45s). Supports: 2min, 5m, 120s, 30sec, 1.5h", - ) - - _ = parser.add_argument( - "--timeout", - "-t", - dest="tool_timeout", - type=str, - help="Default timeout for MCP tool operations (default: 2min). Supports: 2min, 5m, 120s, 30sec, 1.5h", - ) - - _ = parser.add_argument( - "--search-timeout", - dest="search_timeout", - type=str, - help="Timeout specifically for search operations. Supports: 2min, 5m, 120s, 30sec, 1.5h", - ) - - _ = parser.add_argument( - "--find-timeout", - dest="find_timeout", - type=str, - help="Timeout specifically for find operations. Supports: 2min, 5m, 120s, 30sec, 1.5h", - ) - - _ = parser.add_argument( - "--ast-timeout", - dest="ast_timeout", - type=str, - help="Timeout specifically for AST operations. Supports: 2min, 5m, 120s, 30sec, 1.5h", - ) - - _ = parser.add_argument( - "--shell", - dest="shell", - type=str, - default=None, - help="Shell to expose via MCP. Can be a name (zsh, bash, fish, dash) or path (/opt/homebrew/bin/zsh). " - "By default, only your active shell is exposed. Use --all-shells to expose all.", - ) - - _ = parser.add_argument( - "--force-shell", - dest="force_shell", - choices=["bash", "zsh", "sh", "fish", "dash"], - default=None, - help="[Deprecated: use --shell] Force all shell tools to use this shell", - ) - - _ = parser.add_argument( - "--all-shells", - dest="all_shells", - action="store_true", - default=False, - help="Expose all shell tools (zsh, bash, fish, dash) instead of just your active shell", - ) - - _ = parser.add_argument( - "--disable-write-tools", - dest="disable_write_tools", - action="store_true", - default=False, - help="Disable write tools (edit, write, etc.)", - ) - - _ = parser.add_argument( - "--disable-search-tools", - dest="disable_search_tools", - action="store_true", - default=False, - help="Disable search tools (grep, search_content, etc.)", - ) - - _ = parser.add_argument( - "--host", - dest="host", - default="127.0.0.1", - help="Host for SSE server (default: 127.0.0.1)", - ) - - _ = parser.add_argument( - "--port", - dest="port", - type=int, - default=8888, - help="Port for SSE server (default: 8888)", - ) - - _ = parser.add_argument( - "--log-level", - dest="log_level", - default="INFO", - choices=["DEBUG", "INFO", "WARNING", "ERROR"], - help="Logging level (default: INFO)", - ) - - _ = parser.add_argument( - "--project-dir", - dest="project_dir", - help="Single project directory (alias for --project)", - ) - - _ = parser.add_argument( - "--dev", - action="store_true", - help="Run in development mode with hot reload", - ) - - _ = parser.add_argument( - "--daemon", - action="store_true", - help="Run as daemon process for multiple agent connections", - ) - - _default_socket = ( - os.path.join(os.environ.get("TEMP", ""), "hanzo-mcp.sock") - if sys.platform == "win32" - else "/tmp/hanzo-mcp.sock" - ) - _ = parser.add_argument( - "--socket-path", - dest="socket_path", - default=_default_socket, - help=f"Unix socket path for daemon mode (default: {_default_socket})", - ) - - _ = parser.add_argument( - "--max-connections", - dest="max_connections", - type=int, - default=100, - help="Maximum number of concurrent connections in daemon mode (default: 100)", - ) - - _ = parser.add_argument( - "--install", - action="store_true", - help="Install server configuration in Claude Desktop", - ) - - args = parser.parse_args() - - # Restore stdout after parsing, before any explicit output or server start - if suppress_stdout: - try: - if _stdout_devnull is not None: - _stdout_devnull.close() # Close devnull handle properly - except Exception: - pass - sys.stdout = original_stdout - - # stderr is never redirected: it cannot corrupt the stdio protocol and is - # the only channel where startup failures are visible to MCP clients - - # Parse timeout arguments with human-readable format support - command_timeout = _parse_timeout_arg(str(args.command_timeout)) - - # Set timeout environment variables from CLI args - if hasattr(args, "tool_timeout") and args.tool_timeout: - tool_timeout = _parse_timeout_arg(args.tool_timeout) - os.environ["HANZO_MCP_TOOL_TIMEOUT"] = str(tool_timeout) - - if hasattr(args, "search_timeout") and args.search_timeout: - search_timeout = _parse_timeout_arg(args.search_timeout) - os.environ["HANZO_MCP_SEARCH_TIMEOUT"] = str(search_timeout) - - if hasattr(args, "find_timeout") and args.find_timeout: - find_timeout = _parse_timeout_arg(args.find_timeout) - os.environ["HANZO_MCP_FIND_TIMEOUT"] = str(find_timeout) - - if hasattr(args, "ast_timeout") and args.ast_timeout: - ast_timeout = _parse_timeout_arg(args.ast_timeout) - os.environ["HANZO_MCP_AST_TIMEOUT"] = str(ast_timeout) - - # Set shell environment variables - # --all-shells takes precedence - if hasattr(args, "all_shells") and args.all_shells: - os.environ["HANZO_MCP_ALL_SHELLS"] = "1" - # --shell sets the shell to expose (name or path) - elif hasattr(args, "shell") and args.shell: - shell_arg = args.shell - if "/" in shell_arg: - # It's a path - os.environ["HANZO_MCP_FORCE_SHELL"] = shell_arg - else: - # It's a name - os.environ["HANZO_MCP_SHELL"] = shell_arg - # --force-shell (deprecated) still works - elif hasattr(args, "force_shell") and args.force_shell: - os.environ["HANZO_MCP_SHELL"] = args.force_shell - - # Cast args attributes to appropriate types to avoid 'Any' warnings - name: str = cast(str, args.name) - install: bool = cast(bool, args.install) - dev: bool = cast(bool, args.dev) - daemon: bool = cast(bool, args.daemon) - socket_path: str = cast(str, args.socket_path) - max_connections: int = cast(int, args.max_connections) - transport: str = cast(str, args.transport) - agent_model: str | None = cast(str | None, args.agent_model) - agent_max_tokens: int | None = cast(int | None, args.agent_max_tokens) - agent_api_key: str | None = cast(str | None, args.agent_api_key) - agent_base_url: str | None = cast(str | None, args.agent_base_url) - agent_max_iterations: int = cast(int, args.agent_max_iterations) - agent_max_tool_uses: int = cast(int, args.agent_max_tool_uses) - enable_agent_tool: bool = cast(bool, args.enable_agent_tool) - disable_write_tools: bool = cast(bool, args.disable_write_tools) - disable_search_tools: bool = cast(bool, args.disable_search_tools) - host: str = cast(str, args.host) - port: int = cast(int, args.port) - log_level: str = cast(str, args.log_level) - project_dir: str | None = cast(str | None, args.project_dir) - allowed_paths: list[str] = ( - cast(list[str], args.allowed_paths) if args.allowed_paths else [] - ) - project_paths: list[str] = ( - cast(list[str], args.project_paths) if args.project_paths else [] - ) - - # Handle project_dir parameter (add to both allowed_paths and project_paths) - if project_dir: - if project_dir not in allowed_paths: - allowed_paths.append(project_dir) - if project_dir not in project_paths: - project_paths.append(project_dir) - - if install: - install_claude_desktop_config( - name, allowed_paths, disable_write_tools, disable_search_tools, host, port - ) - return - - # Get logger - logger = logging.getLogger(__name__) - - # Set up signal handler to ensure clean exit - def signal_handler(signum, frame): - if transport != "stdio": - logger.info("\nReceived interrupt signal, shutting down...") - sys.exit(0) - - signal.signal(signal.SIGINT, signal_handler) - signal.signal(signal.SIGTERM, signal_handler) - - # Configure logging based on transport (stdio already configured early) - if transport != "stdio": - # For SSE transport, logging is fine - log_level_map = { - "DEBUG": logging.DEBUG, - "INFO": logging.INFO, - "WARNING": logging.WARNING, - "ERROR": logging.ERROR, - } - logging.basicConfig( - level=log_level_map.get(log_level, logging.INFO), - format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", - ) - - # If no allowed paths are specified, use the home directory - if not allowed_paths: - allowed_paths = [os.path.expanduser("~")] - - # Set daemon mode environment variables - if daemon: - os.environ["HANZO_MCP_DAEMON"] = "true" - os.environ["HANZO_MCP_SOCKET_PATH"] = socket_path - os.environ["HANZO_MCP_MAX_CONNECTIONS"] = str(max_connections) - - if transport != "stdio": - logger.info(f"Starting Hanzo MCP daemon on {socket_path}") - logger.info(f"Max connections: {max_connections}") - - # Ensure only one daemon runs per socket - try: - import fcntl - - lock_file = f"{socket_path}.lock" - with open(lock_file, "w") as f: - fcntl.flock(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) - except (ImportError, IOError): - # Fallback for systems without fcntl or if lock fails - pass - - # Run in dev mode if requested - if dev: - from hanzo_mcp.dev_server import DevServer - - dev_server = DevServer( - name=name, - allowed_paths=allowed_paths, - project_paths=project_paths, - project_dir=project_dir, - agent_model=agent_model, - agent_max_tokens=agent_max_tokens, - agent_api_key=agent_api_key, - agent_base_url=agent_base_url, - agent_max_iterations=agent_max_iterations, - agent_max_tool_uses=agent_max_tool_uses, - enable_agent_tool=enable_agent_tool, - command_timeout=command_timeout, - disable_write_tools=disable_write_tools, - disable_search_tools=disable_search_tools, - host=host, - port=port, - ) - dev_server.run(transport=transport) - return - - # Run the server - server = HanzoMCPServer( - name=name, - allowed_paths=allowed_paths, - project_paths=project_paths, - project_dir=project_dir, - agent_model=agent_model, - agent_max_tokens=agent_max_tokens, - agent_api_key=agent_api_key, - agent_base_url=agent_base_url, - agent_max_iterations=agent_max_iterations, - agent_max_tool_uses=agent_max_tool_uses, - enable_agent_tool=enable_agent_tool, - command_timeout=command_timeout, - disable_write_tools=disable_write_tools, - disable_search_tools=disable_search_tools, - host=host, - port=port, - ) - - try: - # Transport will be automatically cast to Literal['stdio', 'sse'] by the server - server.run(transport=transport) - except KeyboardInterrupt: - if transport != "stdio": - logger.info("\nShutting down...") - sys.exit(0) - except Exception as e: - logger.error(f"Server error: {e}", exc_info=True) - sys.exit(1) - - -def install_claude_desktop_config( - name: str = "hanzo-mcp", - allowed_paths: list[str] | None = None, - disable_write_tools: bool = False, - disable_search_tools: bool = False, - host: str = "127.0.0.1", - port: int = 8888, -) -> None: - """Install the server configuration in Claude Desktop. - - Args: - name: The name to use for the server in the config - allowed_paths: Optional list of paths to allow - disable_write_tools: Whether to disable write tools - disable_search_tools: Whether to disable search tools - host: Host for SSE server - port: Port for SSE server - """ - # Find the Claude Desktop config directory - home: Path = Path.home() - - if sys.platform == "darwin": # macOS - config_dir: Path = home / "Library" / "Application Support" / "Claude" - elif sys.platform == "win32": # Windows - config_dir = Path(os.environ.get("APPDATA", "")) / "Claude" - else: # Linux and others - config_dir = home / ".config" / "claude" - - config_file: Path = config_dir / "claude_desktop_config.json" - - # Create directory if it doesn't exist - config_dir.mkdir(parents=True, exist_ok=True) - - # Get current script path - script_path: Path = Path(sys.executable) - - # Create args array - args: list[str] = ["-m", "hanzo_mcp.cli"] - - # Add allowed paths if specified - if allowed_paths: - for path in allowed_paths: - args.extend(["--allow-path", path]) - else: - # Allow home directory by default - args.extend(["--allow-path", str(home)]) - - # Add tool disable flags if specified - if disable_write_tools: - args.append("--disable-write-tools") - - if disable_search_tools: - args.append("--disable-search-tools") - - # Create config object - config: dict[str, Any] = { - "mcpServers": {name: {"command": script_path.as_posix(), "args": args}} - } - - # Check if the file already exists - if config_file.exists(): - try: - with open(config_file, "r") as f: - existing_config: dict[str, Any] = json.load(f) - - # Update the existing config - if "mcpServers" not in existing_config: - existing_config["mcpServers"] = {} - - existing_config["mcpServers"][name] = config["mcpServers"][name] - config = existing_config - except Exception as e: - logger = logging.getLogger(__name__) - logger.error(f"Error reading existing config: {e}") - logger.info("Creating new config file.") - - # Write the config file - with open(config_file, mode="w") as f: - json.dump(config, f, indent=2) - - logger = logging.getLogger(__name__) - logger.info(f"Successfully installed {name} in Claude Desktop configuration.") - logger.info(f"Config file: {config_file}") - - if allowed_paths: - logger.info("\nAllowed paths:") - for path in allowed_paths: - logger.info(f"- {path}") - else: - logger.info(f"\nDefault allowed path: {home}") - - logger.info("\nYou can modify allowed paths in the config file directly.") - logger.info("Restart Claude Desktop for changes to take effect.") - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-mcp/hanzo_mcp/cli_enhanced.py b/pkg/hanzo-mcp/hanzo_mcp/cli_enhanced.py deleted file mode 100644 index 9445bcc4f..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/cli_enhanced.py +++ /dev/null @@ -1,464 +0,0 @@ -"""Enhanced command-line interface for the Hanzo AI server with full tool configuration.""" - -import argparse -import logging -import os -from typing import Any, Dict - -from hanzo_mcp.config import ( - TOOL_REGISTRY, - HanzoMCPSettings, - load_settings, - save_settings, -) -from hanzo_mcp.server import HanzoMCPServer - - -def create_parser() -> argparse.ArgumentParser: - """Create the argument parser with all tool configuration options.""" - parser = argparse.ArgumentParser( - description="Hanzo AI server with comprehensive tool configuration", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=""" -Tool Configuration: - Each tool can be individually enabled/disabled using CLI flags. - Use --list-tools to see all available tools and their current status. - -Configuration Files: - User: ~/.hanzo/settings.json - Project: .hanzo/settings.json - Local: .hanzo/settings.local.json - -Examples: - # Start server with only read tools - hanzo-mcp --disable-write --disable-edit --disable-multi-edit - - # Enable agent tool with custom model - hanzo-mcp --enable-dispatch-agent --agent-model anthropic/claude-3-sonnet - - # Save current configuration - hanzo-mcp --save-config - """, - ) - - # Basic server options - server_group = parser.add_argument_group("Server Configuration") - server_group.add_argument( - "--name", - default="hanzo-mcp", - help="Name of the MCP server (default: hanzo-mcp)", - ) - server_group.add_argument( - "--transport", - choices=["stdio", "sse"], - default="stdio", - help="Transport protocol to use (default: stdio)", - ) - server_group.add_argument( - "--host", - default="127.0.0.1", - help="Host for SSE server (default: 127.0.0.1)", - ) - server_group.add_argument( - "--port", - type=int, - default=8888, - help="Port for SSE server (default: 8888)", - ) - server_group.add_argument( - "--log-level", - choices=["DEBUG", "INFO", "WARNING", "ERROR"], - default="INFO", - help="Logging level (default: INFO)", - ) - server_group.add_argument( - "--command-timeout", - type=float, - default=120.0, - help="Default timeout for command execution in seconds (default: 120.0)", - ) - - # Path configuration - path_group = parser.add_argument_group("Path Configuration") - path_group.add_argument( - "--allow-path", - action="append", - dest="allowed_paths", - help="Add an allowed path (can be specified multiple times)", - ) - path_group.add_argument( - "--project", - action="append", - dest="project_paths", - help="Add a project path (can be specified multiple times)", - ) - path_group.add_argument( - "--project-dir", - help="Single project directory (added to both allowed and project paths)", - ) - - # Individual tool configuration - tool_group = parser.add_argument_group("Individual Tool Configuration") - - # Add CLI flags for each tool - for tool_name, tool_config in TOOL_REGISTRY.items(): - tool_config.cli_flag.lstrip("-") - help_text = f"{tool_config.description}" - - if tool_config.enabled: - # Tool is enabled by default, add disable flag - tool_group.add_argument( - f"--disable-{tool_name.replace('_', '-')}", - action="store_true", - help=f"Disable {tool_name} tool: {help_text}", - ) - else: - # Tool is disabled by default, add enable flag - tool_group.add_argument( - f"--enable-{tool_name.replace('_', '-')}", - action="store_true", - help=f"Enable {tool_name} tool: {help_text}", - ) - - # Category-level tool configuration (for backward compatibility) - category_group = parser.add_argument_group("Category-level Tool Configuration") - category_group.add_argument( - "--disable-write-tools", - action="store_true", - help="Disable all write tools (write, edit, multi_edit, content_replace)", - ) - category_group.add_argument( - "--disable-search-tools", - action="store_true", - help="Disable all search tools (grep, grep_ast)", - ) - category_group.add_argument( - "--disable-filesystem-tools", - action="store_true", - help="Disable all filesystem tools", - ) - category_group.add_argument( - "--disable-jupyter-tools", - action="store_true", - help="Disable all Jupyter notebook tools", - ) - category_group.add_argument( - "--disable-shell-tools", - action="store_true", - help="Disable shell command execution tools", - ) - category_group.add_argument( - "--disable-todo-tools", - action="store_true", - help="Disable todo management tools", - ) - - # Agent configuration - agent_group = parser.add_argument_group("Agent Tool Configuration") - agent_group.add_argument( - "--agent-model", - help="Model name in LLM format (e.g., 'openai/gpt-4o', 'anthropic/claude-3-sonnet')", - ) - agent_group.add_argument( - "--agent-max-tokens", - type=int, - help="Maximum tokens for agent responses", - ) - agent_group.add_argument( - "--agent-api-key", - help="API key for the LLM provider", - ) - agent_group.add_argument( - "--agent-base-url", - help="Base URL for the LLM provider API endpoint", - ) - agent_group.add_argument( - "--agent-max-iterations", - type=int, - default=10, - help="Maximum iterations for agent (default: 10)", - ) - agent_group.add_argument( - "--agent-max-tool-uses", - type=int, - default=30, - help="Maximum tool uses for agent (default: 30)", - ) - - # Vector store configuration - vector_group = parser.add_argument_group("Vector Store Configuration") - vector_group.add_argument( - "--enable-vector-store", - action="store_true", - help="Enable local vector store (Infinity database)", - ) - vector_group.add_argument( - "--vector-store-path", - help="Path for vector store data (default: ~/.config/hanzo/vector-store)", - ) - vector_group.add_argument( - "--embedding-model", - default="text-embedding-3-small", - help="Embedding model for vector store (default: text-embedding-3-small)", - ) - - # Configuration management - config_group = parser.add_argument_group("Configuration Management") - config_group.add_argument( - "--config-file", - help="Load configuration from specific file", - ) - config_group.add_argument( - "--save-config", - action="store_true", - help="Save current configuration to global config file", - ) - config_group.add_argument( - "--save-project-config", - action="store_true", - help="Save current configuration to project config file", - ) - config_group.add_argument( - "--list-tools", - action="store_true", - help="List all available tools and their status", - ) - - # Installation - install_group = parser.add_argument_group("Installation") - install_group.add_argument( - "--install", - action="store_true", - help="Install server configuration in Claude Desktop", - ) - - return parser - - -def apply_cli_overrides(args: argparse.Namespace) -> Dict[str, Any]: - """Convert CLI arguments to configuration overrides.""" - overrides = {} - - # Server configuration - server_config = {} - if hasattr(args, "name") and args.name != "hanzo-mcp": - server_config["name"] = args.name - if hasattr(args, "host") and args.host != "127.0.0.1": - server_config["host"] = args.host - if hasattr(args, "port") and args.port != 8888: - server_config["port"] = args.port - if hasattr(args, "transport") and args.transport != "stdio": - server_config["transport"] = args.transport - if hasattr(args, "log_level") and args.log_level != "INFO": - server_config["log_level"] = args.log_level - if hasattr(args, "command_timeout") and args.command_timeout != 120.0: - server_config["command_timeout"] = args.command_timeout - - if server_config: - overrides["server"] = server_config - - # Path configuration - if hasattr(args, "allowed_paths") and args.allowed_paths: - overrides["allowed_paths"] = args.allowed_paths - if hasattr(args, "project_paths") and args.project_paths: - overrides["project_paths"] = args.project_paths - if hasattr(args, "project_dir") and args.project_dir: - overrides["project_dir"] = args.project_dir - - # Tool configuration - enabled_tools = {} - - # Handle individual tool flags - for tool_name, tool_config in TOOL_REGISTRY.items(): - tool_name.replace("_", "-") - - if tool_config.enabled: - # Check for disable flag - disable_flag = f"disable_{tool_name}" - if hasattr(args, disable_flag) and getattr(args, disable_flag): - enabled_tools[tool_name] = False - else: - # Check for enable flag - enable_flag = f"enable_{tool_name}" - if hasattr(args, enable_flag) and getattr(args, enable_flag): - enabled_tools[tool_name] = True - - # Handle category-level disables - if hasattr(args, "disable_write_tools") and args.disable_write_tools: - for tool_name in ["write", "edit", "multi_edit", "content_replace"]: - enabled_tools[tool_name] = False - - if hasattr(args, "disable_search_tools") and args.disable_search_tools: - for tool_name in ["grep", "grep_ast"]: - enabled_tools[tool_name] = False - - if hasattr(args, "disable_filesystem_tools") and args.disable_filesystem_tools: - filesystem_tools = [ - "read", - "write", - "edit", - "multi_edit", - "tree", - "grep", - "grep_ast", - "content_replace", - ] - for tool_name in filesystem_tools: - enabled_tools[tool_name] = False - - if hasattr(args, "disable_jupyter_tools") and args.disable_jupyter_tools: - for tool_name in ["notebook_read", "notebook_edit"]: - enabled_tools[tool_name] = False - - if hasattr(args, "disable_shell_tools") and args.disable_shell_tools: - enabled_tools["run_command"] = False - - if hasattr(args, "disable_todo_tools") and args.disable_todo_tools: - for tool_name in ["todo_read", "todo_write"]: - enabled_tools[tool_name] = False - - if enabled_tools: - overrides["enabled_tools"] = enabled_tools - - # Agent configuration - agent_config = {} - if hasattr(args, "agent_model") and args.agent_model: - agent_config["model"] = args.agent_model - agent_config["enabled"] = True - if hasattr(args, "agent_api_key") and args.agent_api_key: - agent_config["api_key"] = args.agent_api_key - if hasattr(args, "agent_base_url") and args.agent_base_url: - agent_config["base_url"] = args.agent_base_url - if hasattr(args, "agent_max_tokens") and args.agent_max_tokens: - agent_config["max_tokens"] = args.agent_max_tokens - if hasattr(args, "agent_max_iterations") and args.agent_max_iterations != 10: - agent_config["max_iterations"] = args.agent_max_iterations - if hasattr(args, "agent_max_tool_uses") and args.agent_max_tool_uses != 30: - agent_config["max_tool_uses"] = args.agent_max_tool_uses - - if agent_config: - overrides["agent"] = agent_config - - # Vector store configuration - vector_config = {} - if hasattr(args, "enable_vector_store") and args.enable_vector_store: - vector_config["enabled"] = True - if hasattr(args, "vector_store_path") and args.vector_store_path: - vector_config["data_path"] = args.vector_store_path - if ( - hasattr(args, "embedding_model") - and args.embedding_model != "text-embedding-3-small" - ): - vector_config["embedding_model"] = args.embedding_model - - if vector_config: - overrides["vector_store"] = vector_config - - return overrides - - -def list_tools(settings: HanzoMCPSettings) -> None: - """List all tools and their current status.""" - logger = logging.getLogger(__name__) - logger.info("Hanzo AI Tools Status:") - logger.info("=" * 50) - - categories = {} - for tool_name, tool_config in TOOL_REGISTRY.items(): - category = tool_config.category.value - if category not in categories: - categories[category] = [] - - enabled = settings.is_tool_enabled(tool_name) - status = "โœ… ENABLED " if enabled else "โŒ DISABLED" - categories[category].append((tool_name, status, tool_config.description)) - - for category, tools in categories.items(): - logger.info(f"\n{category.upper()} TOOLS:") - logger.info("-" * 30) - for tool_name, status, description in tools: - logger.info(f" {status} {tool_name:<15} - {description}") - - logger.info(f"\nTotal: {len(TOOL_REGISTRY)} tools") - enabled_count = len(settings.get_enabled_tools()) - logger.info( - f"Enabled: {enabled_count}, Disabled: {len(TOOL_REGISTRY) - enabled_count}" - ) - - -def main() -> None: - """Run the enhanced CLI for the Hanzo AI server.""" - parser = create_parser() - args = parser.parse_args() - - # Handle list tools command - if hasattr(args, "list_tools") and args.list_tools: - settings = load_settings() - list_tools(settings) - return - - # Load configuration with CLI overrides - config_overrides = apply_cli_overrides(args) - project_dir = getattr(args, "project_dir", None) - settings = load_settings(project_dir=project_dir, config_overrides=config_overrides) - - # Handle configuration saving - logger = logging.getLogger(__name__) - if hasattr(args, "save_config") and args.save_config: - saved_path = save_settings(settings, global_config=True) - logger.info(f"Configuration saved to: {saved_path}") - return - - if hasattr(args, "save_project_config") and args.save_project_config: - saved_path = save_settings(settings, global_config=False) - logger.info(f"Project configuration saved to: {saved_path}") - return - - # Handle installation - if hasattr(args, "install") and args.install: - from hanzo_mcp.cli import install_claude_desktop_config - - install_claude_desktop_config( - settings.server.name, - settings.allowed_paths, - ) - return - - # Set up allowed paths - allowed_paths = settings.allowed_paths[:] - if settings.project_dir and settings.project_dir not in allowed_paths: - allowed_paths.append(settings.project_dir) - - if not allowed_paths: - allowed_paths = [os.getcwd()] - - # Create and run server - server = HanzoMCPServer( - name=settings.server.name, - allowed_paths=allowed_paths, - project_dir=settings.project_dir, - agent_model=settings.agent.model, - agent_max_tokens=settings.agent.max_tokens, - agent_api_key=settings.agent.api_key, - agent_base_url=settings.agent.base_url, - agent_max_iterations=settings.agent.max_iterations, - agent_max_tool_uses=settings.agent.max_tool_uses, - enable_agent_tool=settings.agent.enabled - or settings.is_tool_enabled("dispatch_agent"), - disable_write_tools=not any( - settings.is_tool_enabled(t) - for t in ["write", "edit", "multi_edit", "content_replace"] - ), - disable_search_tools=not any( - settings.is_tool_enabled(t) for t in ["grep", "grep_ast"] - ), - host=settings.server.host, - port=settings.server.port, - enabled_tools=settings.enabled_tools, # Pass individual tool configuration - ) - - server.run(transport=settings.server.transport) - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-mcp/hanzo_mcp/cli_plugin.py b/pkg/hanzo-mcp/hanzo_mcp/cli_plugin.py deleted file mode 100644 index c3c1c09b0..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/cli_plugin.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python3 -"""CLI for managing Hanzo MCP plugins.""" - -import argparse -import sys -from pathlib import Path - -from hanzo_mcp.tools.common.plugin_loader import ( - create_plugin_template, -) - - -def main(): - """Main CLI entry point.""" - parser = argparse.ArgumentParser( - description="Hanzo MCP Plugin Manager", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=""" -Examples: - # Create a new plugin template - hanzo-plugin create mytool - - # List installed plugins - hanzo-plugin list - - # Create plugin in specific directory - hanzo-plugin create mytool --output /path/to/plugins -""", - ) - - subparsers = parser.add_subparsers(dest="command", help="Command to run") - - # Create command - create_parser = subparsers.add_parser("create", help="Create a new plugin template") - create_parser.add_argument("name", help="Name of the tool (e.g., 'mytool')") - create_parser.add_argument( - "--output", - "-o", - type=Path, - default=Path.home() / ".hanzo" / "plugins", - help="Output directory for the plugin (default: ~/.hanzo/plugins)", - ) - - # List command - subparsers.add_parser("list", help="List installed plugins") - - args = parser.parse_args() - - if args.command == "create": - # Create plugin template - output_dir = args.output / args.name - try: - create_plugin_template(output_dir, args.name) - print("\nโœ… Plugin template created successfully!") - print("\nTo use your plugin:") - print( - f"1. Edit the tool implementation in {output_dir / f'{args.name}_tool.py'}" - ) - print("2. Restart Hanzo MCP to load the plugin") - print(f"3. Add '{args.name}' to your mode's tool list") - except Exception as e: - print(f"โŒ Error creating plugin: {e}", file=sys.stderr) - sys.exit(1) - - elif args.command == "list": - # List installed plugins - try: - from hanzo_mcp.tools.common.plugin_loader import load_user_plugins - - plugins = load_user_plugins() - - if not plugins: - print("No plugins installed.") - print("\nPlugin directories:") - print(" ~/.hanzo/plugins/") - print(" ./.hanzo/plugins/") - print(" $HANZO_PLUGIN_PATH") - else: - print(f"Installed plugins ({len(plugins)}):") - for name, plugin in plugins.items(): - print(f"\n {name}:") - print(f" Source: {plugin.source_path}") - if plugin.metadata: - print( - f" Version: {plugin.metadata.get('version', 'unknown')}" - ) - print(f" Author: {plugin.metadata.get('author', 'unknown')}") - print( - f" Description: {plugin.metadata.get('description', '')}" - ) - except Exception as e: - print(f"โŒ Error listing plugins: {e}", file=sys.stderr) - sys.exit(1) - - else: - parser.print_help() - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-mcp/hanzo_mcp/compute_nodes.py b/pkg/hanzo-mcp/hanzo_mcp/compute_nodes.py deleted file mode 100644 index 50e982094..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/compute_nodes.py +++ /dev/null @@ -1,192 +0,0 @@ -"""Compute node detection and management for distributed processing.""" - -import os -import platform -import subprocess -from typing import Any, Dict, List - - -class ComputeNodeDetector: - """Detect available compute nodes (GPUs, WebGPU, CPUs) for distributed work.""" - - @staticmethod - def detect_local_gpus() -> List[Dict[str, Any]]: - """Detect local GPU devices.""" - gpus = [] - - # Try NVIDIA GPUs - try: - result = subprocess.run( - [ - "nvidia-smi", - "--query-gpu=name,memory.total", - "--format=csv,noheader", - ], - capture_output=True, - text=True, - timeout=2, - ) - if result.returncode == 0: - for line in result.stdout.strip().split("\n"): - if line: - name, memory = line.split(", ") - gpus.append( - { - "type": "cuda", - "name": name, - "memory": memory, - "id": f"cuda:{len(gpus)}", - } - ) - except (FileNotFoundError, subprocess.TimeoutExpired): - pass - - # Try Metal GPUs (macOS) - if platform.system() == "Darwin": - try: - # Check for Metal support - result = subprocess.run( - ["system_profiler", "SPDisplaysDataType"], - capture_output=True, - text=True, - timeout=2, - ) - if result.returncode == 0 and "Metal" in result.stdout: - # Parse GPU info from system_profiler - lines = result.stdout.split("\n") - for _i, line in enumerate(lines): - if "Chipset Model:" in line: - gpu_name = line.split(":")[1].strip() - gpus.append( - { - "type": "metal", - "name": gpu_name, - "memory": "Shared", - "id": f"metal:{len(gpus)}", - } - ) - except (FileNotFoundError, subprocess.TimeoutExpired): - pass - - return gpus - - @staticmethod - def detect_webgpu_nodes() -> List[Dict[str, Any]]: - """Detect connected WebGPU nodes (from browsers).""" - webgpu_nodes = [] - - # Check for WebGPU connections (would need actual WebSocket/server to track) - # For now, check if a WebGPU server is running - webgpu_port = os.environ.get("HANZO_WEBGPU_PORT", "8765") - try: - import socket - - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - result = sock.connect_ex(("localhost", int(webgpu_port))) - sock.close() - if result == 0: - webgpu_nodes.append( - { - "type": "webgpu", - "name": "Chrome WebGPU", - "memory": "Browser", - "id": "webgpu:0", - } - ) - except Exception: - pass - - return webgpu_nodes - - @staticmethod - def detect_cpu_nodes() -> List[Dict[str, Any]]: - """Detect CPU compute nodes.""" - import multiprocessing - - return [ - { - "type": "cpu", - "name": f"{platform.processor() or 'CPU'}", - "cores": multiprocessing.cpu_count(), - "id": "cpu:0", - } - ] - - @classmethod - def get_all_nodes(cls) -> List[Dict[str, Any]]: - """Get all available compute nodes.""" - nodes = [] - - # Detect GPUs - gpus = cls.detect_local_gpus() - nodes.extend(gpus) - - # Detect WebGPU connections - webgpu = cls.detect_webgpu_nodes() - nodes.extend(webgpu) - - # If no GPUs/WebGPU, add CPU as compute node - if not nodes: - nodes.extend(cls.detect_cpu_nodes()) - - return nodes - - @classmethod - def get_node_count(cls) -> int: - """Get total number of available compute nodes.""" - return len(cls.get_all_nodes()) - - @classmethod - def get_node_summary(cls) -> str: - """Get a summary string of available nodes.""" - nodes = cls.get_all_nodes() - if not nodes: - return "No compute nodes available" - - count = len(nodes) - node_word = "node" if count == 1 else "nodes" - - # Group by type - types = {} - for node in nodes: - node_type = node["type"] - if node_type not in types: - types[node_type] = 0 - types[node_type] += 1 - - # Build summary - parts = [] - for node_type, type_count in types.items(): - if node_type == "cuda": - parts.append(f"{type_count} CUDA GPU{'s' if type_count > 1 else ''}") - elif node_type == "metal": - parts.append(f"{type_count} Metal GPU{'s' if type_count > 1 else ''}") - elif node_type == "webgpu": - parts.append(f"{type_count} WebGPU") - elif node_type == "cpu": - parts.append(f"{type_count} CPU") - - type_str = ", ".join(parts) - return f"{count} {node_word} available ({type_str})" - - -def print_node_status(): - """Print current node status.""" - detector = ComputeNodeDetector() - nodes = detector.get_all_nodes() - - print(f"\n๐Ÿ–ฅ๏ธ Compute Nodes: {len(nodes)}") - for node in nodes: - if node["type"] in ["cuda", "metal"]: - print(f" โ€ข {node['id']}: {node['name']} ({node['memory']})") - elif node["type"] == "webgpu": - print(f" โ€ข {node['id']}: {node['name']}") - elif node["type"] == "cpu": - print(f" โ€ข {node['id']}: {node['name']} ({node['cores']} cores)") - print() - - -if __name__ == "__main__": - # Test the detector - print_node_status() - print(ComputeNodeDetector.get_node_summary()) diff --git a/pkg/hanzo-mcp/hanzo_mcp/config/__init__.py b/pkg/hanzo-mcp/hanzo_mcp/config/__init__.py deleted file mode 100644 index c3f3817d7..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/config/__init__.py +++ /dev/null @@ -1,322 +0,0 @@ -"""Unified configuration for hanzo-mcp. - -Delegates to hanzoai.config (ConfigLoader, RuntimeConfig) for file discovery -and merging. MCP-specific types (BackendConfig, PluginConfig, HanzoMCPSettings) -live here but are populated FROM the canonical RuntimeConfig. - -Config file locations (handled by ConfigLoader): - User: ~/.hanzo/settings.json - Project: .hanzo/settings.json - Local: .hanzo/settings.local.json -""" - -from __future__ import annotations - -import json -from pathlib import Path -from typing import Any, Dict, List, Optional - -# --- Re-export canonical config types from hanzoai --- -from hanzoai.config import ( - ConfigEntry, - ConfigError, - ConfigLoader, - ConfigSource, - McpClaudeAiProxyConfig, - McpOAuthConfig, - McpRemoteConfig, - McpSdkConfig, - McpServerConfig, - McpStdioConfig, - McpTransport, - McpWsConfig, - OAuthConfig, - RuntimeConfig, -) -from pydantic import BaseModel, Field - -# --- Re-export tool config --- -from .tool_config import ( - TOOL_REGISTRY, - DynamicToolRegistry, - ToolCategory, - ToolConfigEntry, -) - -__all__ = [ - # hanzoai.config re-exports - "ConfigEntry", - "ConfigError", - "ConfigLoader", - "ConfigSource", - "McpClaudeAiProxyConfig", - "McpOAuthConfig", - "McpRemoteConfig", - "McpSdkConfig", - "McpServerConfig", - "McpStdioConfig", - "McpTransport", - "McpWsConfig", - "OAuthConfig", - "RuntimeConfig", - # Tool config - "TOOL_REGISTRY", - "DynamicToolRegistry", - "ToolCategory", - "ToolConfigEntry", - # MCP-specific models - "BackendConfig", - "PluginConfig", - "HanzoMCPSettings", - # Functions - "get_global_config_path", - "get_project_config_path", - "load_config", - "load_settings", - "save_global_config", - "save_settings", - "get_default_config", -] - - -# --------------------------------------------------------------------------- -# MCP-specific Pydantic models -# --------------------------------------------------------------------------- - -class BackendConfig(BaseModel): - """Configuration for a specific MCP backend (e.g. sqlite memory).""" - - enabled: bool = True - path: Optional[str] = None - url: Optional[str] = None - settings: Dict[str, Any] = Field(default_factory=dict) - - -class PluginConfig(BaseModel): - """Plugin system configuration (backends, user/project IDs).""" - - enabled_backends: List[str] = ["sqlite"] - backend_configs: Dict[str, BackendConfig] = Field(default_factory=dict) - default_user_id: str = "default" - default_project_id: str = "default" - - class Config: - extra = "allow" - - -class ServerConfig(BaseModel): - """Server runtime settings.""" - - name: str = "hanzo-mcp" - host: str = "127.0.0.1" - port: int = 8888 - transport: str = "stdio" - log_level: str = "INFO" - command_timeout: float = 120.0 - - -class AgentConfig(BaseModel): - """Agent sub-agent settings.""" - - enabled: bool = False - model: Optional[str] = None - max_tokens: Optional[int] = None - api_key: Optional[str] = None - base_url: Optional[str] = None - max_iterations: int = 10 - max_tool_uses: int = 30 - - -class HanzoMCPSettings(BaseModel): - """Top-level MCP settings. Constructed from RuntimeConfig.merged.""" - - server: ServerConfig = Field(default_factory=ServerConfig) - agent: AgentConfig = Field(default_factory=AgentConfig) - plugins: PluginConfig = Field(default_factory=PluginConfig) - enabled_tools: Dict[str, bool] = Field(default_factory=dict) - allowed_paths: List[str] = Field(default_factory=list) - project_paths: List[str] = Field(default_factory=list) - project_dir: Optional[str] = None - - class Config: - extra = "allow" - - def is_tool_enabled(self, name: str) -> bool: - if name in self.enabled_tools: - return self.enabled_tools[name] - entry = TOOL_REGISTRY.get(name) - if entry is not None: - return entry.enabled - return True - - def get_enabled_tools(self) -> List[str]: - DynamicToolRegistry.initialize() - result = [] - for name in TOOL_REGISTRY.keys(): - if self.is_tool_enabled(name): - result.append(name) - return result - - -# --------------------------------------------------------------------------- -# Path helpers (backwards compat -- these now point to canonical locations) -# --------------------------------------------------------------------------- - -def _config_home() -> Path: - import os - return Path(os.environ.get("HANZO_CONFIG_HOME", Path.home() / ".hanzo")) - - -def get_global_config_path() -> Path: - """Canonical user config: ~/.hanzo/settings.json""" - p = _config_home() - p.mkdir(parents=True, exist_ok=True) - return p / "settings.json" - - -def get_project_config_path() -> Optional[Path]: - """Project config: .hanzo/settings.json in cwd.""" - p = Path.cwd() / ".hanzo" / "settings.json" - if p.exists(): - return p - return None - - -# --------------------------------------------------------------------------- -# Load / save -# --------------------------------------------------------------------------- - -def _runtime_to_plugin_config(rc: RuntimeConfig) -> PluginConfig: - """Extract PluginConfig from the merged runtime dict.""" - plugins_raw = rc.merged.get("plugins", rc.merged.get("mcp", {})) - if not isinstance(plugins_raw, dict): - plugins_raw = {} - - backends_raw = plugins_raw.get("backend_configs", plugins_raw.get("backends", {})) - backend_configs = {} - for name, cfg in (backends_raw if isinstance(backends_raw, dict) else {}).items(): - if isinstance(cfg, dict): - backend_configs[name] = BackendConfig(**cfg) - - return PluginConfig( - enabled_backends=plugins_raw.get("enabled_backends", ["sqlite"]), - backend_configs=backend_configs, - default_user_id=plugins_raw.get("default_user_id", "default"), - default_project_id=plugins_raw.get("default_project_id", "default"), - ) - - -def _runtime_to_settings(rc: RuntimeConfig) -> HanzoMCPSettings: - """Build HanzoMCPSettings from a RuntimeConfig.""" - m = rc.merged - - server_raw = m.get("server", {}) - agent_raw = m.get("agent", {}) - enabled_tools = m.get("enabled_tools", {}) - - return HanzoMCPSettings( - server=ServerConfig(**{k: v for k, v in server_raw.items() if isinstance(server_raw, dict)}), - agent=AgentConfig(**{k: v for k, v in agent_raw.items() if isinstance(agent_raw, dict)}), - plugins=_runtime_to_plugin_config(rc), - enabled_tools=enabled_tools if isinstance(enabled_tools, dict) else {}, - allowed_paths=m.get("allowed_paths", []), - project_paths=m.get("project_paths", []), - project_dir=m.get("project_dir"), - ) - - -def load_config( - global_config_path: Optional[Path] = None, - project_config_path: Optional[Path] = None, -) -> PluginConfig: - """Load PluginConfig using canonical ConfigLoader. - - Accepts legacy path overrides for backwards compatibility but prefers - the standard discovery (User/Project/Local). - """ - loader = ConfigLoader.default_for(Path.cwd()) - rc = loader.load() - - # If caller passed explicit paths, layer them on top - if global_config_path and global_config_path.is_file(): - extra = json.loads(global_config_path.read_text(encoding="utf-8")) - if isinstance(extra, dict): - rc.merged.update(extra) - if project_config_path and project_config_path.is_file(): - extra = json.loads(project_config_path.read_text(encoding="utf-8")) - if isinstance(extra, dict): - rc.merged.update(extra) - - return _runtime_to_plugin_config(rc) - - -def load_settings( - project_dir: Optional[str] = None, - config_overrides: Optional[Dict[str, Any]] = None, -) -> HanzoMCPSettings: - """Load full MCP settings via ConfigLoader, with optional overrides.""" - cwd = Path(project_dir) if project_dir else Path.cwd() - loader = ConfigLoader.default_for(cwd) - rc = loader.load() - - if config_overrides: - from hanzoai.config import _deep_merge - rc.merged = _deep_merge(rc.merged, config_overrides) - - settings = _runtime_to_settings(rc) - if project_dir: - settings.project_dir = project_dir - return settings - - -def save_global_config(config: PluginConfig) -> None: - """Save PluginConfig to ~/.hanzo/settings.json (merges into existing).""" - path = get_global_config_path() - existing: dict = {} - if path.is_file(): - try: - existing = json.loads(path.read_text(encoding="utf-8")) - except (json.JSONDecodeError, OSError): - pass - - existing["plugins"] = config.model_dump() - path.write_text(json.dumps(existing, indent=2), encoding="utf-8") - - -def save_settings(settings: HanzoMCPSettings, global_config: bool = True) -> Path: - """Save HanzoMCPSettings to the appropriate config file. - - Returns the path written to. - """ - data = settings.model_dump(exclude_none=True) - - if global_config: - path = get_global_config_path() - else: - path = Path.cwd() / ".hanzo" / "settings.json" - path.parent.mkdir(parents=True, exist_ok=True) - - existing: dict = {} - if path.is_file(): - try: - existing = json.loads(path.read_text(encoding="utf-8")) - except (json.JSONDecodeError, OSError): - pass - - existing.update(data) - path.write_text(json.dumps(existing, indent=2), encoding="utf-8") - return path - - -def get_default_config() -> PluginConfig: - """Default PluginConfig with sqlite backend.""" - return PluginConfig( - enabled_backends=["sqlite"], - backend_configs={ - "sqlite": BackendConfig( - enabled=True, - path=str(Path.home() / ".hanzo" / "memory.db"), - settings={}, - ) - }, - ) diff --git a/pkg/hanzo-mcp/hanzo_mcp/config/tool_config.py b/pkg/hanzo-mcp/hanzo_mcp/config/tool_config.py deleted file mode 100644 index 668de6414..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/config/tool_config.py +++ /dev/null @@ -1,211 +0,0 @@ -"""Tool configuration registry. - -Discovers tools from hanzo-tools-* entry points and provides a unified -registry for enable/disable, CLI flag generation, and mode filtering. -""" - -from __future__ import annotations - -import logging -from dataclasses import dataclass, field -from enum import Enum -from importlib.metadata import entry_points -from typing import Optional - -logger = logging.getLogger(__name__) - -TOOLS_ENTRY_POINT_GROUP = "hanzo.tools" - - -class ToolCategory(Enum): - filesystem = "filesystem" - shell = "shell" - code = "code" - search = "search" - vcs = "vcs" - reasoning = "reasoning" - memory = "memory" - todo = "todo" - config = "config" - agent = "agent" - browser = "browser" - computer = "computer" - lsp = "lsp" - refactor = "refactor" - net = "net" - llm = "llm" - api = "api" - auth = "auth" - ui = "ui" - other = "other" - - -@dataclass -class ToolConfigEntry: - """Metadata for a single tool.""" - - name: str - description: str = "" - category: ToolCategory = ToolCategory.other - enabled: bool = True - package: Optional[str] = None - cli_flag: str = "" - dependencies: list[str] = field(default_factory=list) - - -# Category guessing from tool name prefix -_PREFIX_TO_CATEGORY: dict[str, ToolCategory] = { - "fs": ToolCategory.filesystem, - "read": ToolCategory.filesystem, - "write": ToolCategory.filesystem, - "edit": ToolCategory.filesystem, - "tree": ToolCategory.filesystem, - "exec": ToolCategory.shell, - "run": ToolCategory.shell, - "zsh": ToolCategory.shell, - "code": ToolCategory.code, - "grep": ToolCategory.search, - "find": ToolCategory.search, - "search": ToolCategory.search, - "git": ToolCategory.vcs, - "think": ToolCategory.reasoning, - "critic": ToolCategory.reasoning, - "memory": ToolCategory.memory, - "tasks": ToolCategory.todo, - "todo": ToolCategory.todo, - "config": ToolCategory.config, - "mode": ToolCategory.config, - "agent": ToolCategory.agent, - "zen": ToolCategory.agent, - "review": ToolCategory.agent, - "browser": ToolCategory.browser, - "computer": ToolCategory.computer, - "lsp": ToolCategory.lsp, - "refactor": ToolCategory.refactor, - "fetch": ToolCategory.net, - "curl": ToolCategory.net, - "wget": ToolCategory.net, - "llm": ToolCategory.llm, - "consensus": ToolCategory.llm, - "api": ToolCategory.api, - "hanzo": ToolCategory.api, - "auth": ToolCategory.auth, - "ui": ToolCategory.ui, -} - - -def _guess_category(name: str) -> ToolCategory: - for prefix, cat in _PREFIX_TO_CATEGORY.items(): - if name == prefix or name.startswith(prefix + "_"): - return cat - return ToolCategory.other - - -class DynamicToolRegistry: - """Discovers tools from entry points and exposes them as ToolConfigEntry.""" - - _entries: dict[str, ToolConfigEntry] = {} - _initialized: bool = False - - @classmethod - def initialize(cls) -> None: - if cls._initialized: - return - cls._initialized = True - cls._entries = {} - try: - eps = entry_points(group=TOOLS_ENTRY_POINT_GROUP) - for ep in eps: - try: - tools_list = ep.load() - if not isinstance(tools_list, list): - continue - for tool_class in tools_list: - name = _extract_tool_name(tool_class) - desc = getattr(tool_class, "description", "") or "" - cls._entries[name] = ToolConfigEntry( - name=name, - description=desc, - category=_guess_category(name), - enabled=True, - package=ep.name, - cli_flag=f"--{name.replace('_', '-')}", - ) - except Exception as e: - logger.debug(f"Failed to load entry point '{ep.name}': {e}") - except Exception as e: - logger.debug(f"Failed to discover entry points: {e}") - - @classmethod - def get(cls, name: str) -> Optional[ToolConfigEntry]: - cls.initialize() - return cls._entries.get(name) - - @classmethod - def list_all(cls) -> dict[str, ToolConfigEntry]: - cls.initialize() - return dict(cls._entries) - - @classmethod - def reset(cls) -> None: - """Reset for testing.""" - cls._entries = {} - cls._initialized = False - - -def _extract_tool_name(tool_class: type) -> str: - """Get name from a tool class, handling @property.""" - for klass in tool_class.__mro__: - if "name" in getattr(klass, "__dict__", {}): - attr = klass.__dict__["name"] - if isinstance(attr, property): - try: - inst = tool_class() - val = getattr(inst, "name", None) - if isinstance(val, str): - return val - except Exception: - pass - return tool_class.__name__.lower().replace("tool", "") - break - name = getattr(tool_class, "name", None) - if isinstance(name, str): - return name - return tool_class.__name__.lower().replace("tool", "") - - -# Module-level convenience -- DynamicToolRegistry doubles as the dict-like TOOL_REGISTRY. -# Code that does `TOOL_REGISTRY.items()` or `TOOL_REGISTRY.keys()` works via __getattr__. -class _RegistryProxy: - """Thin proxy so ``TOOL_REGISTRY.items()`` and ``TOOL_REGISTRY.keys()`` work.""" - - def keys(self): - DynamicToolRegistry.initialize() - return DynamicToolRegistry._entries.keys() - - def values(self): - DynamicToolRegistry.initialize() - return DynamicToolRegistry._entries.values() - - def items(self): - DynamicToolRegistry.initialize() - return DynamicToolRegistry._entries.items() - - def __len__(self): - DynamicToolRegistry.initialize() - return len(DynamicToolRegistry._entries) - - def __iter__(self): - DynamicToolRegistry.initialize() - return iter(DynamicToolRegistry._entries) - - def __getitem__(self, key: str): - DynamicToolRegistry.initialize() - return DynamicToolRegistry._entries[key] - - def get(self, key: str, default=None): - DynamicToolRegistry.initialize() - return DynamicToolRegistry._entries.get(key, default) - - -TOOL_REGISTRY = _RegistryProxy() diff --git a/pkg/hanzo-mcp/hanzo_mcp/coordination.py b/pkg/hanzo-mcp/hanzo_mcp/coordination.py deleted file mode 100644 index dbb06f3aa..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/coordination.py +++ /dev/null @@ -1,116 +0,0 @@ -"""Blue-Red agent coordination protocol via shared memory. - -The blue-red protocol is a structured review cycle: -1. Blue agent analyzes code/system and stores a report -2. Red agent reads Blue's report, finds issues, stores findings -3. Blue reads Red's findings, implements fixes, stores response -4. Red re-reviews Blue's fixes, stores verification result - -All messages are stored in the "blue-red" namespace with typed keys, -enabling deterministic retrieval without semantic search. -""" - -import json -import time -from typing import Any, Dict, List, Optional - - -class BlueRedChannel: - """Shared memory channel for blue-red agent coordination.""" - - NAMESPACE = "blue-red" - - def __init__(self, memory_service): - """Initialize with a PluginMemoryService instance.""" - self.memory = memory_service - - async def blue_report(self, report: dict, scope: str = "") -> str: - """Blue stores its report for Red to read.""" - key = f"blue-report-{int(time.time())}" - await self.memory.store_memory( - content=json.dumps(report), - metadata={ - "agent": "blue", - "type": "report", - "scope": scope, - }, - namespace=self.NAMESPACE, - key=key, - tags=["blue", "report"], - ) - return key - - async def red_report(self, findings: dict, scope: str = "") -> str: - """Red stores findings for Blue to read.""" - key = f"red-report-{int(time.time())}" - await self.memory.store_memory( - content=json.dumps(findings), - metadata={ - "agent": "red", - "type": "findings", - "scope": scope, - }, - namespace=self.NAMESPACE, - key=key, - tags=["red", "findings"], - ) - return key - - async def blue_response(self, fixes: dict) -> str: - """Blue stores fix response for Red re-review.""" - key = f"blue-response-{int(time.time())}" - await self.memory.store_memory( - content=json.dumps(fixes), - metadata={ - "agent": "blue", - "type": "response", - }, - namespace=self.NAMESPACE, - key=key, - tags=["blue", "response", "fixes"], - ) - return key - - async def red_rereview(self, verification: dict) -> str: - """Red stores re-review results.""" - key = f"red-rereview-{int(time.time())}" - await self.memory.store_memory( - content=json.dumps(verification), - metadata={ - "agent": "red", - "type": "rereview", - }, - namespace=self.NAMESPACE, - key=key, - tags=["red", "rereview"], - ) - return key - - async def get_latest_blue_report(self) -> Optional[Dict[str, Any]]: - """Red reads Blue's latest report.""" - results = await self.memory.search_memory( - query="", - metadata_filter={ - "namespace": self.NAMESPACE, - "agent": "blue", - "type": "report", - }, - ) - return results[0] if results else None - - async def get_latest_red_findings(self) -> Optional[Dict[str, Any]]: - """Blue reads Red's latest findings.""" - results = await self.memory.search_memory( - query="", - metadata_filter={ - "namespace": self.NAMESPACE, - "agent": "red", - "type": "findings", - }, - ) - return results[0] if results else None - - async def get_full_cycle(self) -> List[Dict[str, Any]]: - """Get all messages in the blue-red cycle, ordered by creation time.""" - results = await self.memory.list_memories(namespace=self.NAMESPACE) - return sorted(results, key=lambda r: r.get("created_at", "")) diff --git a/pkg/hanzo-mcp/hanzo_mcp/core/base_agent.py b/pkg/hanzo-mcp/hanzo_mcp/core/base_agent.py deleted file mode 100644 index a2ebbed97..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/core/base_agent.py +++ /dev/null @@ -1,539 +0,0 @@ -"""Base Agent - Unified foundation for all AI agent implementations. - -This module provides the single base class for all agent operations, -following DRY principles and ensuring consistent behavior across all agents. -""" - -from __future__ import annotations - -import asyncio -import logging -import os -from abc import ABC, abstractmethod -from dataclasses import dataclass, field -from datetime import datetime -from pathlib import Path -from typing import ( - Any, - Dict, - Generic, - List, - Optional, - Protocol, - TypeVar, - runtime_checkable, -) - -from .model_registry import registry - -logger = logging.getLogger(__name__) - - -# Type variables for generic context -TContext = TypeVar("TContext") -TResult = TypeVar("TResult") - - -@runtime_checkable -class AgentContext(Protocol): - """Protocol for agent execution context.""" - - async def log(self, message: str, level: str = "info") -> None: - """Log a message.""" - pass - - async def progress(self, message: str, percentage: Optional[float] = None) -> None: - """Report progress.""" - pass - - -@dataclass -class AgentConfig: - """Configuration for agent execution.""" - - model: str = "claude-3-5-sonnet-20241022" - timeout: int = 300 - max_retries: int = 3 - working_dir: Optional[Path] = None - environment: Dict[str, str] = field(default_factory=dict) - stream_output: bool = False - use_worktree: bool = False - - def __post_init__(self) -> None: - """Resolve model name and validate configuration.""" - self.model = registry.resolve(self.model) - if self.working_dir and not isinstance(self.working_dir, Path): - self.working_dir = Path(self.working_dir) - - -@dataclass -class AgentResult: - """Result from agent execution.""" - - success: bool - output: Optional[str] = None - error: Optional[str] = None - duration: Optional[float] = None - metadata: Dict[str, Any] = field(default_factory=dict) - - @property - def content(self) -> str: - """Get the primary content (output or error).""" - return self.output if self.success else (self.error or "Unknown error") - - -class BaseAgent(ABC, Generic[TContext]): - """Base class for all AI agents. - - This is the single foundation for all agent implementations, - ensuring consistent behavior and eliminating code duplication. - """ - - def __init__(self, config: Optional[AgentConfig] = None) -> None: - """Initialize agent with configuration. - - Args: - config: Agent configuration - """ - self.config = config or AgentConfig() - self._start_time: Optional[datetime] = None - self._end_time: Optional[datetime] = None - - @property - @abstractmethod - def name(self) -> str: - """Agent name.""" - pass - - @property - @abstractmethod - def description(self) -> str: - """Agent description.""" - pass - - async def execute( - self, - prompt: str, - context: Optional[TContext] = None, - **kwargs: Any, - ) -> AgentResult: - """Execute agent with prompt. - - Args: - prompt: The prompt or task - context: Execution context - **kwargs: Additional parameters - - Returns: - Agent execution result - """ - self._start_time = datetime.now() - - try: - # Setup environment - env = self._prepare_environment() - - # Log start - if context and isinstance(context, AgentContext): - await context.log( - f"Starting {self.name} with model {self.config.model}" - ) - - # Execute with retries - result = await self._execute_with_retries(prompt, context, env, **kwargs) - - # Calculate duration - self._end_time = datetime.now() - duration = (self._end_time - self._start_time).total_seconds() - - return AgentResult( - success=True, - output=result, - duration=duration, - metadata={"model": self.config.model, "agent": self.name}, - ) - - except Exception as e: - self._end_time = datetime.now() - duration = ( - (self._end_time - self._start_time).total_seconds() - if self._start_time - else None - ) - - logger.error(f"Agent {self.name} failed: {e}") - - return AgentResult( - success=False, - error=str(e), - duration=duration, - metadata={"model": self.config.model, "agent": self.name}, - ) - - def _prepare_environment(self) -> Dict[str, str]: - """Prepare environment variables for execution. - - Returns: - Environment variables dictionary - """ - env = os.environ.copy() - - # Add model-specific API key - model_config = registry.get(self.config.model) - if model_config and model_config.api_key_env: - key_var = model_config.api_key_env - if key_var in os.environ: - env[key_var] = os.environ[key_var] - - # Add Hanzo unified auth - if "HANZO_API_KEY" in os.environ: - env["HANZO_API_KEY"] = os.environ["HANZO_API_KEY"] - - # Add custom environment - env.update(self.config.environment) - - return env - - async def _execute_with_retries( - self, - prompt: str, - context: Optional[TContext], - env: Dict[str, str], - **kwargs: Any, - ) -> str: - """Execute with retry logic. - - Args: - prompt: The prompt - context: Execution context - env: Environment variables - **kwargs: Additional parameters - - Returns: - Execution output - - Raises: - Exception: If all retries fail - """ - last_error = None - - for attempt in range(self.config.max_retries): - try: - # Call the implementation - result = await self._execute_impl(prompt, context, env, **kwargs) - return result - - except asyncio.TimeoutError: - last_error = f"Timeout after {self.config.timeout} seconds" - if context and isinstance(context, AgentContext): - await context.log(f"Attempt {attempt + 1} timed out", "warning") - - except Exception as e: - last_error = str(e) - if context and isinstance(context, AgentContext): - await context.log(f"Attempt {attempt + 1} failed: {e}", "warning") - - # Don't retry on certain errors - if "unauthorized" in str(e).lower() or "forbidden" in str(e).lower(): - raise - - # Wait before retry (exponential backoff) - if attempt < self.config.max_retries - 1: - await asyncio.sleep(2**attempt) - - raise Exception( - f"All {self.config.max_retries} attempts failed. Last error: {last_error}" - ) - - @abstractmethod - async def _execute_impl( - self, - prompt: str, - context: Optional[TContext], - env: Dict[str, str], - **kwargs: Any, - ) -> str: - """Implementation-specific execution. - - Args: - prompt: The prompt - context: Execution context - env: Environment variables - **kwargs: Additional parameters - - Returns: - Execution output - """ - pass - - -class CLIAgent(BaseAgent[TContext]): - """Base class for CLI-based agents.""" - - @property - @abstractmethod - def cli_command(self) -> str: - """CLI command to execute.""" - pass - - def build_command(self, prompt: str, **kwargs: Any) -> List[str]: - """Build the CLI command. - - Args: - prompt: The prompt - **kwargs: Additional parameters - - Returns: - Command arguments list - """ - command = [self.cli_command] - - # Add model if specified - model_config = registry.get(self.config.model) - if model_config: - command.extend(["--model", model_config.full_name]) - - # Add prompt - command.append(prompt) - - return command - - async def _execute_impl( - self, - prompt: str, - context: Optional[TContext], - env: Dict[str, str], - **kwargs: Any, - ) -> str: - """Execute CLI command. - - Args: - prompt: The prompt - context: Execution context - env: Environment variables - **kwargs: Additional parameters - - Returns: - Command output - """ - command = self.build_command(prompt, **kwargs) - - # Determine if we need stdin - needs_stdin = self.cli_command in ["claude", "cline"] - - # Execute command - process = await asyncio.create_subprocess_exec( - *command, - stdin=asyncio.subprocess.PIPE if needs_stdin else None, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - cwd=str(self.config.working_dir) if self.config.working_dir else None, - env=env, - ) - - # Handle timeout - try: - stdout, stderr = await asyncio.wait_for( - process.communicate(prompt.encode() if needs_stdin else None), - timeout=self.config.timeout, - ) - except asyncio.TimeoutError: - process.kill() - raise asyncio.TimeoutError( - f"Command timed out after {self.config.timeout} seconds" - ) - - # Check for errors - if process.returncode != 0: - error_msg = stderr.decode() if stderr else "Command failed" - raise Exception(error_msg) - - return stdout.decode() - - -class APIAgent(BaseAgent[TContext]): - """Base class for API-based agents.""" - - async def _execute_impl( - self, - prompt: str, - context: Optional[TContext], - env: Dict[str, str], - **kwargs: Any, - ) -> str: - """Execute via API. - - Args: - prompt: The prompt - context: Execution context - env: Environment variables - **kwargs: Additional parameters - - Returns: - API response - """ - # This would be implemented by specific API agents - # using the appropriate client library - raise NotImplementedError("API agents must implement _execute_impl") - - -class AgentOrchestrator: - """Orchestrator for managing multiple agents.""" - - def __init__(self, default_config: Optional[AgentConfig] = None) -> None: - """Initialize orchestrator. - - Args: - default_config: Default configuration for agents - """ - self.default_config = default_config or AgentConfig() - self._agents: Dict[str, BaseAgent] = {} - self._semaphore: Optional[asyncio.Semaphore] = None - - def register(self, agent: BaseAgent) -> None: - """Register an agent. - - Args: - agent: Agent to register - """ - self._agents[agent.name] = agent - - def get_agent(self, name: str) -> Optional[BaseAgent]: - """Get agent by name. - - Args: - name: Agent name - - Returns: - Agent instance or None - """ - return self._agents.get(name) - - async def execute_single( - self, - agent_name: str, - prompt: str, - context: Optional[Any] = None, - **kwargs: Any, - ) -> AgentResult: - """Execute single agent. - - Args: - agent_name: Name of agent to use - prompt: The prompt - context: Execution context - **kwargs: Additional parameters - - Returns: - Execution result - """ - agent = self.get_agent(agent_name) - if not agent: - return AgentResult( - success=False, - error=f"Agent '{agent_name}' not found", - ) - - return await agent.execute(prompt, context, **kwargs) - - async def execute_parallel( - self, - tasks: List[Dict[str, Any]], - max_concurrent: int = 5, - ) -> List[AgentResult]: - """Execute multiple agents in parallel. - - Args: - tasks: List of task definitions - max_concurrent: Maximum concurrent executions - - Returns: - List of results - """ - self._semaphore = asyncio.Semaphore(max_concurrent) - - async def run_with_semaphore(task: Dict[str, Any]) -> AgentResult: - async with self._semaphore: - return await self.execute_single( - task["agent"], - task["prompt"], - task.get("context"), - **task.get("kwargs", {}), - ) - - return await asyncio.gather( - *[run_with_semaphore(task) for task in tasks], - return_exceptions=False, - ) - - async def execute_consensus( - self, - prompt: str, - agents: List[str], - threshold: float = 0.66, - ) -> Dict[str, Any]: - """Execute consensus operation with multiple agents. - - Args: - prompt: The prompt - agents: List of agent names - threshold: Agreement threshold - - Returns: - Consensus results - """ - # Execute all agents in parallel - tasks = [{"agent": agent, "prompt": prompt} for agent in agents] - results = await self.execute_parallel(tasks) - - # Analyze consensus - successful = [r for r in results if r.success] - agreement = len(successful) / len(results) if results else 0 - - return { - "consensus_reached": agreement >= threshold, - "agreement_score": agreement, - "individual_results": results, - "agents_used": agents, - } - - async def execute_chain( - self, - initial_prompt: str, - agents: List[str], - ) -> List[AgentResult]: - """Execute agents in a chain, passing output forward. - - Args: - initial_prompt: Initial prompt - agents: List of agent names - - Returns: - List of results from each step - """ - results = [] - current_prompt = initial_prompt - - for agent_name in agents: - result = await self.execute_single(agent_name, current_prompt) - results.append(result) - - if result.success and result.output: - # Use output as input for next agent - current_prompt = f"Review and improve:\n{result.output}" - else: - # Chain broken - break - - return results - - -__all__ = [ - "AgentContext", - "AgentConfig", - "AgentResult", - "BaseAgent", - "CLIAgent", - "APIAgent", - "AgentOrchestrator", -] diff --git a/pkg/hanzo-mcp/hanzo_mcp/core/model_registry.py b/pkg/hanzo-mcp/hanzo_mcp/core/model_registry.py deleted file mode 100644 index cb640546b..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/core/model_registry.py +++ /dev/null @@ -1,464 +0,0 @@ -"""Unified Model Registry - Single source of truth for all AI model mappings. - -This module provides a centralized registry for AI model configurations, -eliminating duplication and ensuring consistency across the codebase. -Thread-safe singleton implementation. -""" - -from __future__ import annotations - -import threading -from dataclasses import dataclass, field -from enum import Enum -from typing import Any, Dict, List, Optional, Set - - -class ModelProvider(Enum): - """Enumeration of AI model providers.""" - - ANTHROPIC = "anthropic" - OPENAI = "openai" - GOOGLE = "google" - XAI = "xai" - OLLAMA = "ollama" - DEEPSEEK = "deepseek" - MISTRAL = "mistral" - META = "meta" - HANZO = "hanzo" - - -@dataclass(frozen=True) -class ModelConfig: - """Configuration for a single AI model.""" - - full_name: str - provider: ModelProvider - aliases: Set[str] = field(default_factory=set) - default_params: Dict[str, Any] = field(default_factory=dict) - supports_vision: bool = False - supports_tools: bool = False - supports_streaming: bool = True - context_window: int = 8192 - max_output: int = 4096 - api_key_env: Optional[str] = None - cli_command: Optional[str] = None - - -class ModelRegistry: - """Centralized registry for all AI models. - - Thread-safe singleton implementation ensuring single source of truth - for model configurations across the codebase. - """ - - _instance: Optional[ModelRegistry] = None - _lock = threading.Lock() - _models: Dict[str, ModelConfig] = {} - _initialized = False - - def __new__(cls) -> ModelRegistry: - """Thread-safe singleton pattern.""" - if cls._instance is None: - with cls._lock: - if cls._instance is None: - cls._instance = super().__new__(cls) - return cls._instance - - def __init__(self) -> None: - """Initialize model registry once.""" - if not self._initialized: - with self._lock: - if not self._initialized: - self._initialize_models() - self._initialized = True - - def _initialize_models(self) -> None: - """Initialize all model configurations.""" - # Claude models - self._register( - ModelConfig( - full_name="claude-3-5-sonnet-20241022", - provider=ModelProvider.ANTHROPIC, - aliases={"claude", "cc", "claude-code", "sonnet", "sonnet-4.1"}, - supports_vision=True, - supports_tools=True, - context_window=200000, - max_output=8192, - api_key_env="ANTHROPIC_API_KEY", - cli_command="claude", - ) - ) - - self._register( - ModelConfig( - full_name="claude-opus-4-1-20250805", - provider=ModelProvider.ANTHROPIC, - aliases={"opus", "opus-4.1", "claude-opus"}, - supports_vision=True, - supports_tools=True, - context_window=200000, - max_output=8192, - api_key_env="ANTHROPIC_API_KEY", - cli_command="claude", - ) - ) - - self._register( - ModelConfig( - full_name="claude-3-haiku-20240307", - provider=ModelProvider.ANTHROPIC, - aliases={"haiku", "claude-haiku"}, - supports_vision=True, - supports_tools=True, - context_window=200000, - max_output=4096, - api_key_env="ANTHROPIC_API_KEY", - cli_command="claude", - ) - ) - - # OpenAI models - self._register( - ModelConfig( - full_name="gpt-4-turbo", - provider=ModelProvider.OPENAI, - aliases={"gpt4", "gpt-4", "codex"}, - supports_vision=True, - supports_tools=True, - context_window=128000, - max_output=4096, - api_key_env="OPENAI_API_KEY", - cli_command="openai", - ) - ) - - self._register( - ModelConfig( - full_name="gpt-5-turbo", - provider=ModelProvider.OPENAI, - aliases={"gpt5", "gpt-5"}, - supports_vision=True, - supports_tools=True, - context_window=256000, - max_output=16384, - api_key_env="OPENAI_API_KEY", - cli_command="openai", - ) - ) - - self._register( - ModelConfig( - full_name="o1-preview", - provider=ModelProvider.OPENAI, - aliases={"o1", "openai-o1"}, - supports_vision=False, - supports_tools=False, - context_window=128000, - max_output=32768, - api_key_env="OPENAI_API_KEY", - cli_command="openai", - ) - ) - - # Google models - self._register( - ModelConfig( - full_name="gemini-2.0-flash-exp", - provider=ModelProvider.GOOGLE, - aliases={"gemini-2", "gemini-2.0", "gemini2"}, - supports_vision=True, - supports_tools=True, - context_window=1000000, - max_output=8192, - api_key_env="GEMINI_API_KEY", - cli_command="gemini", - ) - ) - - self._register( - ModelConfig( - full_name="gemini-exp-1206", - provider=ModelProvider.GOOGLE, - aliases={"gemini-2.5", "gemini-2.5-pro", "gemini-pro-2.5"}, - supports_vision=True, - supports_tools=True, - context_window=2000000, - max_output=8192, - api_key_env="GEMINI_API_KEY", - cli_command="gemini", - ) - ) - - self._register( - ModelConfig( - full_name="gemini-1.5-pro", - provider=ModelProvider.GOOGLE, - aliases={"gemini", "gemini-pro", "gemini-1.5"}, - supports_vision=True, - supports_tools=True, - context_window=2000000, - max_output=8192, - api_key_env="GEMINI_API_KEY", - cli_command="gemini", - ) - ) - - self._register( - ModelConfig( - full_name="gemini-1.5-flash", - provider=ModelProvider.GOOGLE, - aliases={"gemini-flash", "flash"}, - supports_vision=True, - supports_tools=True, - context_window=1000000, - max_output=8192, - api_key_env="GEMINI_API_KEY", - cli_command="gemini", - ) - ) - - # xAI models - self._register( - ModelConfig( - full_name="grok-4", - provider=ModelProvider.XAI, - aliases={"grok", "xai-grok", "grok-2"}, # grok-2 for backward compat - supports_vision=True, # Grok-4 supports multimodal - supports_tools=True, - context_window=128000, - max_output=8192, - api_key_env="XAI_API_KEY", - cli_command="grok", - ) - ) - - # Ollama models - self._register( - ModelConfig( - full_name="ollama/llama-3.2-3b", - provider=ModelProvider.OLLAMA, - aliases={"llama", "llama-3.2", "llama3"}, - supports_vision=False, - supports_tools=False, - context_window=128000, - max_output=4096, - api_key_env=None, # Local model - cli_command="ollama", - ) - ) - - self._register( - ModelConfig( - full_name="ollama/mistral:7b", - provider=ModelProvider.MISTRAL, - aliases={"mistral", "mistral-7b"}, - supports_vision=False, - supports_tools=False, - context_window=32000, - max_output=4096, - api_key_env=None, # Local model - cli_command="ollama", - ) - ) - - # DeepSeek models - self._register( - ModelConfig( - full_name="deepseek-coder-v2", - provider=ModelProvider.DEEPSEEK, - aliases={"deepseek", "deepseek-coder"}, - supports_vision=False, - supports_tools=True, - context_window=128000, - max_output=8192, - api_key_env="DEEPSEEK_API_KEY", - cli_command="deepseek", - ) - ) - - def _register(self, config: ModelConfig) -> None: - """Register a model configuration. - - Args: - config: Model configuration to register - """ - # Register by full name - self._models[config.full_name] = config - - # Register all aliases - for alias in config.aliases: - self._models[alias.lower()] = config - - def get(self, model_name: str) -> Optional[ModelConfig]: - """Get model configuration by name or alias. - - Args: - model_name: Model name or alias - - Returns: - Model configuration or None if not found - """ - return self._models.get(model_name.lower()) - - def resolve(self, model_name: str) -> str: - """Resolve model name or alias to full model name. - - Args: - model_name: Model name or alias - - Returns: - Full model name, or original if not found - """ - config = self.get(model_name) - return config.full_name if config else model_name - - def get_by_provider(self, provider: ModelProvider) -> List[ModelConfig]: - """Get all unique models for a specific provider. - - Args: - provider: Model provider - - Returns: - List of unique model configurations - """ - seen_names = set() - results = [] - for config in self._models.values(): - if config.provider == provider and config.full_name not in seen_names: - seen_names.add(config.full_name) - results.append(config) - return results - - def get_models_supporting( - self, - vision: Optional[bool] = None, - tools: Optional[bool] = None, - streaming: Optional[bool] = None, - ) -> List[ModelConfig]: - """Get unique models supporting specific features. - - Args: - vision: Filter by vision support - tools: Filter by tool support - streaming: Filter by streaming support - - Returns: - List of unique matching model configurations - """ - seen_names = set() - results = [] - - for config in self._models.values(): - if config.full_name in seen_names: - continue - - if vision is not None and config.supports_vision != vision: - continue - if tools is not None and config.supports_tools != tools: - continue - if streaming is not None and config.supports_streaming != streaming: - continue - - seen_names.add(config.full_name) - results.append(config) - - return results - - def get_api_key_env(self, model_name: str) -> Optional[str]: - """Get the API key environment variable for a model. - - Args: - model_name: Model name or alias - - Returns: - Environment variable name or None - """ - config = self.get(model_name) - return config.api_key_env if config else None - - def get_cli_command(self, model_name: str) -> Optional[str]: - """Get the CLI command for a model. - - Args: - model_name: Model name or alias - - Returns: - CLI command or None - """ - config = self.get(model_name) - return config.cli_command if config else None - - def list_all_models(self) -> List[str]: - """List all unique model full names. - - Returns: - Sorted list of unique full model names - """ - seen_names = set() - for config in self._models.values(): - seen_names.add(config.full_name) - return sorted(list(seen_names)) - - def list_all_aliases(self) -> Dict[str, str]: - """List all aliases and their full names. - - Returns: - Dictionary mapping aliases to full names - """ - result = {} - for key, config in self._models.items(): - if key != config.full_name: - result[key] = config.full_name - return result - - -# Global singleton instance -registry = ModelRegistry() - - -# Convenience functions -def resolve_model(model_name: str) -> str: - """Resolve model name or alias to full model name. - - Args: - model_name: Model name or alias - - Returns: - Full model name - """ - return registry.resolve(model_name) - - -def get_model_config(model_name: str) -> Optional[ModelConfig]: - """Get model configuration. - - Args: - model_name: Model name or alias - - Returns: - Model configuration or None - """ - return registry.get(model_name) - - -def get_api_key_env(model_name: str) -> Optional[str]: - """Get API key environment variable for model. - - Args: - model_name: Model name or alias - - Returns: - Environment variable name or None - """ - return registry.get_api_key_env(model_name) - - -__all__ = [ - "ModelProvider", - "ModelConfig", - "ModelRegistry", - "registry", - "resolve_model", - "get_model_config", - "get_api_key_env", -] diff --git a/pkg/hanzo-mcp/hanzo_mcp/dev_cli.py b/pkg/hanzo-mcp/hanzo_mcp/dev_cli.py deleted file mode 100644 index cc21d3ce2..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/dev_cli.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env python3 -""" -Main CLI entry point for hanzo-dev command -""" - -import asyncio -import sys - -from hanzo_mcp.dev_tools import DevToolsCore - - -async def main(): - """Main CLI entry point for hanzo-dev""" - tools = DevToolsCore() - - if len(sys.argv) < 2: - print("Usage: hanzo-dev [args]") - print("Tools: edit, fmt, test, build, lint, guard") - print("\nExamples:") - print(" hanzo-dev fmt ws # Format workspace") - print(" hanzo-dev test file:main.py # Test specific file") - print(" hanzo-dev lint dir:src --fix # Lint and fix directory") - print(" hanzo-dev guard ws # Check boundaries") - return 1 - - tool = sys.argv[1] - args = sys.argv[2:] - - try: - if tool == "edit": - if len(args) < 2: - print("Usage: hanzo-dev edit [--new-name NAME]") - return 1 - target, op = args[0], args[1] - new_name = None - if "--new-name" in args: - idx = args.index("--new-name") - if idx + 1 < len(args): - new_name = args[idx + 1] - result = await tools.edit(target=target, op=op, new_name=new_name) - - elif tool == "fmt": - if len(args) < 1: - print("Usage: hanzo-dev fmt [--local-prefix PREFIX]") - return 1 - target = args[0] - local_prefix = None - if "--local-prefix" in args: - idx = args.index("--local-prefix") - if idx + 1 < len(args): - local_prefix = args[idx + 1] - result = await tools.fmt(target=target, local_prefix=local_prefix) - - elif tool == "test": - if len(args) < 1: - print("Usage: hanzo-dev test [--run PATTERN] [--count N]") - return 1 - target = args[0] - run_pattern = None - count = None - if "--run" in args: - idx = args.index("--run") - if idx + 1 < len(args): - run_pattern = args[idx + 1] - if "--count" in args: - idx = args.index("--count") - if idx + 1 < len(args): - count = int(args[idx + 1]) - result = await tools.test(target=target, run=run_pattern, count=count) - - elif tool == "build": - if len(args) < 1: - print("Usage: hanzo-dev build [--release]") - return 1 - target = args[0] - release = "--release" in args - result = await tools.build(target=target, release=release) - - elif tool == "lint": - if len(args) < 1: - print("Usage: hanzo-dev lint [--fix]") - return 1 - target = args[0] - fix = "--fix" in args - result = await tools.lint(target=target, fix=fix) - - elif tool == "guard": - if len(args) < 1: - print("Usage: hanzo-dev guard ") - return 1 - target = args[0] - result = await tools.guard(target=target) - - else: - print(f"Unknown tool: {tool}") - print("Available tools: edit, fmt, test, build, lint, guard") - return 1 - - # Output results - print(f"โœ… Success: {result.ok}") - print(f"๐ŸŒฑ Language: {result.language_used}") - print(f"๐Ÿ› ๏ธ Backend: {result.backend_used}") - print(f"๐Ÿ“‚ Root: {result.root}") - - if result.touched_files: - print(f"๐Ÿ“ Modified files ({len(result.touched_files)}):") - for file in result.touched_files: - print(f" - {file}") - - if result.stdout: - print(f"๐Ÿ“ค Output:\n{result.stdout}") - - if result.stderr: - print(f"โš ๏ธ Error output:\n{result.stderr}") - - if result.violations: - print(f"๐Ÿšจ Violations ({len(result.violations)}):") - for violation in result.violations: - print(f" - {violation}") - - return 0 if result.ok else 1 - - except Exception as e: - print(f"โŒ Error: {e}") - return 1 - - -if __name__ == "__main__": - sys.exit(asyncio.run(main())) diff --git a/pkg/hanzo-mcp/hanzo_mcp/dev_server.py b/pkg/hanzo-mcp/hanzo_mcp/dev_server.py deleted file mode 100644 index ea3386f0d..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/dev_server.py +++ /dev/null @@ -1,262 +0,0 @@ -"""Development server with hot reload for Hanzo AI.""" - -import asyncio -import logging -import time -from pathlib import Path -from typing import Optional, Set - -import watchdog.events -import watchdog.observers -from watchdog.events import FileSystemEventHandler - -from hanzo_mcp.server import HanzoMCPServer - - -class MCPReloadHandler(FileSystemEventHandler): - """Handler for file system events that triggers MCP server reload.""" - - def __init__(self, restart_callback, ignore_patterns: Optional[Set[str]] = None): - """Initialize the reload handler. - - Args: - restart_callback: Function to call when files change - ignore_patterns: Set of patterns to ignore - """ - self.restart_callback = restart_callback - self.ignore_patterns = ignore_patterns or { - "__pycache__", - ".pyc", - ".pyo", - ".git", - ".pytest_cache", - ".mypy_cache", - ".ruff_cache", - ".coverage", - "*.log", - ".env", - ".venv", - "venv", - "node_modules", - } - self.last_reload = 0 - self.reload_delay = 0.5 # Debounce delay in seconds - - def should_ignore(self, path: str) -> bool: - """Check if a path should be ignored.""" - path_obj = Path(path) - - # Check against ignore patterns - for pattern in self.ignore_patterns: - if pattern in str(path_obj): - return True - if path_obj.name.endswith(pattern): - return True - - # Only watch Python files and config files - if path_obj.is_file(): - allowed_extensions = {".py", ".json", ".yaml", ".yml", ".toml"} - if path_obj.suffix not in allowed_extensions: - return True - - return False - - def on_any_event(self, event): - """Handle any file system event.""" - if event.is_directory: - return - - if self.should_ignore(event.src_path): - return - - # Debounce rapid changes - current_time = time.time() - if current_time - self.last_reload < self.reload_delay: - return - - self.last_reload = current_time - - logger = logging.getLogger(__name__) - logger.info(f"\n๐Ÿ”„ File changed: {event.src_path}") - logger.info("๐Ÿ”„ Reloading MCP server...") - - self.restart_callback() - - -class DevServer: - """Development server with hot reload capability.""" - - def __init__( - self, - name: str = "hanzo-dev", - allowed_paths: Optional[list[str]] = None, - project_paths: Optional[list[str]] = None, - project_dir: Optional[str] = None, - **kwargs, - ): - """Initialize the development server. - - Args: - name: Server name - allowed_paths: Allowed paths for the server - project_paths: Project paths - project_dir: Project directory - **kwargs: Additional arguments for HanzoMCPServer - """ - self.name = name - self.allowed_paths = allowed_paths or [] - self.project_paths = project_paths - self.project_dir = project_dir - self.server_kwargs = kwargs - self.server_process = None - self.observer = None - self.running = False - - def create_server(self) -> HanzoMCPServer: - """Create a new MCP server instance.""" - return HanzoMCPServer( - name=self.name, - allowed_paths=self.allowed_paths, - project_paths=self.project_paths, - project_dir=self.project_dir, - **self.server_kwargs, - ) - - def start_file_watcher(self): - """Start watching for file changes.""" - # Watch the hanzo_mcp package directory - package_dir = Path(__file__).parent - - # Create observer and handler - self.observer = watchdog.observers.Observer() - handler = MCPReloadHandler(self.restart_server) - - # Watch the package directory - self.observer.schedule(handler, str(package_dir), recursive=True) - - # Also watch any project directories - if self.project_dir: - self.observer.schedule(handler, self.project_dir, recursive=True) - - for path in self.allowed_paths: - if Path(path).is_dir() and path not in [str(package_dir), self.project_dir]: - self.observer.schedule(handler, path, recursive=True) - - self.observer.start() - logger = logging.getLogger(__name__) - logger.info(f"๐Ÿ‘€ Watching for changes in: {package_dir}") - if self.project_dir: - logger.info(f"๐Ÿ‘€ Also watching: {self.project_dir}") - - def stop_file_watcher(self): - """Stop the file watcher.""" - if self.observer and self.observer.is_alive(): - self.observer.stop() - self.observer.join(timeout=2) - - def restart_server(self): - """Restart the MCP server.""" - # Since MCP servers run in the same process, we need to handle this differently - # For now, we'll log a message indicating a restart is needed - logger = logging.getLogger(__name__) - logger.warning( - "\nโš ๏ธ Server restart required. Please restart the MCP client to reload changes." - ) - logger.info( - "๐Ÿ’ก Tip: In development, consider using the MCP test client for easier reloading." - ) - - async def run_async(self, transport: str = "stdio"): - """Run the development server asynchronously.""" - self.running = True - - logger = logging.getLogger(__name__) - logger.info("\n๐Ÿš€ Starting Hanzo AI in development mode...") - - # Show compute nodes - try: - from hanzo_mcp.compute_nodes import ComputeNodeDetector - - detector = ComputeNodeDetector() - summary = detector.get_node_summary() - logger.info(f"๐Ÿ–ฅ๏ธ {summary}") - except Exception: - # Silently ignore if compute node detection fails - pass - - logger.info("๐Ÿ”ง Hot reload enabled - watching for file changes") - logger.info(f"๐Ÿ“ Project: {self.project_dir or 'current directory'}") - logger.info(f"๐ŸŒ Transport: {transport}\n") - - # Start file watcher - self.start_file_watcher() - - try: - # Create and run server - server = self.create_server() - - # Run the server (this will block) - server.run(transport=transport) - - except KeyboardInterrupt: - logger.info("\n\n๐Ÿ›‘ Shutting down development server...") - finally: - self.running = False - self.stop_file_watcher() - logger.info("๐Ÿ‘‹ Development server stopped") - - def run(self, transport: str = "stdio"): - """Run the development server.""" - try: - # Run the async version - asyncio.run(self.run_async(transport)) - except KeyboardInterrupt: - pass - - -def run_dev_server(): - """Entry point for development server.""" - import argparse - - parser = argparse.ArgumentParser( - description="Run Hanzo AI in development mode with hot reload" - ) - parser.add_argument( - "--name", type=str, default="hanzo-dev", help="Name of the MCP server" - ) - parser.add_argument("--project-dir", type=str, help="Project directory to serve") - parser.add_argument( - "--allowed-path", - type=str, - action="append", - dest="allowed_paths", - help="Additional allowed paths (can be specified multiple times)", - ) - parser.add_argument( - "--transport", - type=str, - default="stdio", - choices=["stdio", "sse"], - help="Transport type (default: stdio)", - ) - parser.add_argument( - "--host", type=str, default="127.0.0.1", help="Host for SSE transport" - ) - parser.add_argument("--port", type=int, default=3000, help="Port for SSE transport") - - args = parser.parse_args() - - # Create and run dev server - dev_server = DevServer( - name=args.name, - allowed_paths=args.allowed_paths, - project_dir=args.project_dir, - host=args.host, - port=args.port, - ) - - dev_server.run(transport=args.transport) - - -if __name__ == "__main__": - run_dev_server() diff --git a/pkg/hanzo-mcp/hanzo_mcp/dev_tools.py b/pkg/hanzo-mcp/hanzo_mcp/dev_tools.py deleted file mode 100644 index 471af0f9c..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/dev_tools.py +++ /dev/null @@ -1,404 +0,0 @@ -#!/usr/bin/env python3 -""" -Hanzo Development Tools - Unified 6-Tool Implementation -====================================================== - -Complete implementation of the 6 universal development tools: -edit, fmt, test, build, lint, guard - -Features: -- Workspace detection (go.work, package.json, pyproject.toml, Cargo.toml, etc.) -- Multi-language support with proper backend selection -- Session tracking and codebase intelligence -- LSP integration for semantic operations -- Target resolution (file:, dir:, pkg:, ws, changed) -- Unified backend with SQLite vector storage -""" - -import asyncio -import logging -import uuid -from dataclasses import asdict -from typing import Any, Dict, List, Literal, Optional - -from .unified_backend import TargetSpec, ToolResult, UnifiedBackend - -logger = logging.getLogger(__name__) - - -class DevToolsCore: - """Core implementation of the 6 universal development tools""" - - def __init__(self): - self.backend = UnifiedBackend() - self.session_id = str(uuid.uuid4()) - - async def edit( - self, - target: str, - op: Literal[ - "rename", "code_action", "organize_imports", "apply_workspace_edit" - ], - file: Optional[str] = None, - pos: Optional[Dict[str, int]] = None, - range_: Optional[Dict[str, Dict[str, int]]] = None, - new_name: Optional[str] = None, - only: List[str] = None, - apply: bool = True, - workspace_edit: Optional[Dict[str, Any]] = None, - **target_opts, - ) -> ToolResult: - """Semantic refactors via LSP across languages""" - target_spec = TargetSpec(target=target, **target_opts) - - args = { - "op": op, - "file": file, - "pos": pos, - "range": range_, - "new_name": new_name, - "only": only or [], - "apply": apply, - "workspace_edit": workspace_edit, - } - - result = await self.backend.execute_edit(target_spec, args) - await self.backend.log_execution("edit", args, result) - return result - - async def fmt( - self, target: str, local_prefix: Optional[str] = None, **target_opts - ) -> ToolResult: - """Format code and organize imports""" - target_spec = TargetSpec(target=target, **target_opts) - - args = {"opts": {"local_prefix": local_prefix} if local_prefix else {}} - - result = await self.backend.execute_fmt(target_spec, args) - await self.backend.log_execution("fmt", args, result) - return result - - async def test( - self, - target: str, - run: Optional[str] = None, - count: Optional[int] = None, - race: Optional[bool] = None, - filter_: Optional[str] = None, - watch: Optional[bool] = None, - **target_opts, - ) -> ToolResult: - """Run tests narrowly by default""" - target_spec = TargetSpec(target=target, **target_opts) - - args = { - "opts": { - k: v - for k, v in { - "run": run, - "count": count, - "race": race, - "filter": filter_, - "watch": watch, - }.items() - if v is not None - } - } - - result = await self.backend.execute_test(target_spec, args) - await self.backend.log_execution("test", args, result) - return result - - async def build( - self, - target: str, - release: Optional[bool] = None, - features: Optional[List[str]] = None, - **target_opts, - ) -> ToolResult: - """Compile/build artifacts narrowly by default""" - target_spec = TargetSpec(target=target, **target_opts) - - args = { - "opts": { - k: v - for k, v in {"release": release, "features": features}.items() - if v is not None - } - } - - result = await self.backend.execute_build(target_spec, args) - await self.backend.log_execution("build", args, result) - return result - - async def lint( - self, target: str, fix: Optional[bool] = None, **target_opts - ) -> ToolResult: - """Lint and typecheck code""" - target_spec = TargetSpec(target=target, **target_opts) - - args = {"opts": {"fix": fix} if fix is not None else {}} - - result = await self.backend.execute_lint(target_spec, args) - await self.backend.log_execution("lint", args, result) - return result - - async def guard( - self, target: str, rules: Optional[List[Dict[str, Any]]] = None, **target_opts - ) -> ToolResult: - """Check repository invariants and boundaries""" - target_spec = TargetSpec(target=target, **target_opts) - - args = {"rules": rules or []} - - result = await self.backend.execute_guard(target_spec, args) - await self.backend.log_execution("guard", args, result) - return result - - # Composition patterns - async def multi_language_rename( - self, - symbol_name: str, - new_name: str, - languages: List[str], - workspace: str = "ws", - ) -> List[ToolResult]: - """Multi-language rename operation""" - results = [] - - for lang in languages: - result = await self.edit( - target=workspace, op="rename", new_name=new_name, language=lang - ) - results.append(result) - - # Format changed files - changed_result = await self.fmt(target="changed") - results.append(changed_result) - - # Run tests - test_result = await self.test(target="ws") - results.append(test_result) - - # Check guards - guard_result = await self.guard(target="ws") - results.append(guard_result) - - return results - - async def wide_refactor_go_workspace( - self, workspace: str = "ws" - ) -> List[ToolResult]: - """Wide refactor in Go workspace""" - results = [] - - # Fix all and organize imports - edit_result = await self.edit( - target="pkg:./...", - op="code_action", - only=["source.fixAll", "source.organizeImports"], - language="go", - ) - results.append(edit_result) - - # Format with local prefix - fmt_result = await self.fmt(target="pkg:./...", local_prefix="github.com/luxfi") - results.append(fmt_result) - - # Test - test_result = await self.test(target="pkg:./...") - results.append(test_result) - - # Guard - guard_result = await self.guard( - target=workspace, - rules=[ - { - "id": "no_node_in_sdk", - "type": "import", - "glob": "sdk/**", - "forbid_import_prefix": "github.com/luxfi/node/", - }, - { - "id": "no_generated_edits", - "type": "generated", - "glob": "api/pb/**", - "forbid_writes": True, - }, - ], - ) - results.append(guard_result) - - return results - - -# MCP Tool wrappers for the 6 universal tools -async def mcp_edit( - target: str, - op: str, - file: Optional[str] = None, - pos: Optional[Dict[str, int]] = None, - range_: Optional[Dict[str, Dict[str, int]]] = None, - new_name: Optional[str] = None, - only: Optional[List[str]] = None, - apply: bool = True, - workspace_edit: Optional[Dict[str, Any]] = None, - language: str = "auto", - backend: str = "auto", - root: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - dry_run: bool = False, -) -> Dict[str, Any]: - """MCP wrapper for edit tool""" - tools = DevToolsCore() - result = await tools.edit( - target=target, - op=op, - file=file, - pos=pos, - range_=range_, - new_name=new_name, - only=only or [], - apply=apply, - workspace_edit=workspace_edit, - language=language, - backend=backend, - root=root, - env=env or {}, - dry_run=dry_run, - ) - return asdict(result) - - -async def mcp_fmt( - target: str, - local_prefix: Optional[str] = None, - language: str = "auto", - backend: str = "auto", - root: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - dry_run: bool = False, -) -> Dict[str, Any]: - """MCP wrapper for fmt tool""" - tools = DevToolsCore() - result = await tools.fmt( - target=target, - local_prefix=local_prefix, - language=language, - backend=backend, - root=root, - env=env or {}, - dry_run=dry_run, - ) - return asdict(result) - - -async def mcp_test( - target: str, - run: Optional[str] = None, - count: Optional[int] = None, - race: Optional[bool] = None, - filter_: Optional[str] = None, - watch: Optional[bool] = None, - language: str = "auto", - backend: str = "auto", - root: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - dry_run: bool = False, -) -> Dict[str, Any]: - """MCP wrapper for test tool""" - tools = DevToolsCore() - result = await tools.test( - target=target, - run=run, - count=count, - race=race, - filter_=filter_, - watch=watch, - language=language, - backend=backend, - root=root, - env=env or {}, - dry_run=dry_run, - ) - return asdict(result) - - -async def mcp_build( - target: str, - release: Optional[bool] = None, - features: Optional[List[str]] = None, - language: str = "auto", - backend: str = "auto", - root: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - dry_run: bool = False, -) -> Dict[str, Any]: - """MCP wrapper for build tool""" - tools = DevToolsCore() - result = await tools.build( - target=target, - release=release, - features=features, - language=language, - backend=backend, - root=root, - env=env or {}, - dry_run=dry_run, - ) - return asdict(result) - - -async def mcp_lint( - target: str, - fix: Optional[bool] = None, - language: str = "auto", - backend: str = "auto", - root: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - dry_run: bool = False, -) -> Dict[str, Any]: - """MCP wrapper for lint tool""" - tools = DevToolsCore() - result = await tools.lint( - target=target, - fix=fix, - language=language, - backend=backend, - root=root, - env=env or {}, - dry_run=dry_run, - ) - return asdict(result) - - -async def mcp_guard( - target: str, - rules: Optional[List[Dict[str, Any]]] = None, - language: str = "auto", - backend: str = "auto", - root: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - dry_run: bool = False, -) -> Dict[str, Any]: - """MCP wrapper for guard tool""" - tools = DevToolsCore() - result = await tools.guard( - target=target, - rules=rules, - language=language, - backend=backend, - root=root, - env=env or {}, - dry_run=dry_run, - ) - return asdict(result) - - -def main(): - """CLI entry point""" - import sys - - from .dev_cli import main as cli_main - - sys.exit(asyncio.run(cli_main())) diff --git a/pkg/hanzo-mcp/hanzo_mcp/exact_mcp_server.py b/pkg/hanzo-mcp/hanzo_mcp/exact_mcp_server.py deleted file mode 100644 index 5f1e8d35c..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/exact_mcp_server.py +++ /dev/null @@ -1,581 +0,0 @@ -#!/usr/bin/env python3 -""" -Enhanced MCP Server with Exact 6-Tool Implementation -==================================================== - -Precise implementation of the 6 universal tools according to specification. -""" - -import asyncio -from typing import List - -from mcp.server import NotificationOptions, Server -from mcp.server.models import InitializationOptions -from mcp.types import ( - TextContent, - Tool, -) - -from .exact_tools import ( - BuildArgs, - EditArgs, - FmtArgs, - GuardArgs, - GuardRule, - LintArgs, - TargetSpec, - TestArgs, - tools, -) - - -class ExactHanzoMCPServer: - """Enhanced MCP server with exact 6-tool specification""" - - def __init__(self): - self.server = Server("hanzo-mcp-exact") - self.setup_handlers() - - def setup_handlers(self): - """Set up MCP server handlers with exact tool specifications""" - - @self.server.list_tools() - async def handle_list_tools() -> List[Tool]: - """List the exact 6 universal tools""" - return [ - Tool( - name="edit", - description="Semantic refactors via LSP across languages", - inputSchema={ - "type": "object", - "properties": { - "target": { - "type": "string", - "description": "Target: file:, dir:, pkg:, ws, or changed", - }, - "language": { - "type": "string", - "enum": [ - "auto", - "go", - "ts", - "py", - "rs", - "cc", - "sol", - "schema", - ], - "default": "auto", - "description": "Language override", - }, - "backend": { - "type": "string", - "default": "auto", - "description": "Backend override", - }, - "root": { - "type": "string", - "description": "Explicit workspace root override", - }, - "env": { - "type": "object", - "additionalProperties": {"type": "string"}, - "description": "Extra environment variables", - }, - "dry_run": { - "type": "boolean", - "default": False, - "description": "Preview mode - no file writes", - }, - "op": { - "type": "string", - "enum": [ - "rename", - "code_action", - "organize_imports", - "apply_workspace_edit", - ], - "description": "Operation to perform", - }, - "file": { - "type": "string", - "description": "File path for rename/code_action operations", - }, - "pos": { - "type": "object", - "properties": { - "line": {"type": "integer"}, - "character": {"type": "integer"}, - }, - "description": "Position for rename/code_action", - }, - "range": { - "type": "object", - "properties": { - "start": { - "type": "object", - "properties": { - "line": {"type": "integer"}, - "character": {"type": "integer"}, - }, - }, - "end": { - "type": "object", - "properties": { - "line": {"type": "integer"}, - "character": {"type": "integer"}, - }, - }, - }, - "description": "Range for code actions", - }, - "new_name": { - "type": "string", - "description": "New name for rename operation", - }, - "only": { - "type": "array", - "items": {"type": "string"}, - "description": "LSP codeAction kinds filter", - }, - "apply": { - "type": "boolean", - "default": True, - "description": "Apply edits to disk", - }, - "workspace_edit": { - "type": "object", - "description": "WorkspaceEdit payload for apply_workspace_edit", - }, - }, - "required": ["target", "op"], - "additionalProperties": False, - }, - ), - Tool( - name="fmt", - description="Formatting + import normalization", - inputSchema={ - "type": "object", - "properties": { - "target": { - "type": "string", - "description": "Target: file:, dir:, pkg:, ws, or changed", - }, - "language": { - "type": "string", - "enum": [ - "auto", - "go", - "ts", - "py", - "rs", - "cc", - "sol", - "schema", - ], - "default": "auto", - }, - "backend": { - "type": "string", - "default": "auto", - "description": "Backend override (goimports, prettier, ruff, etc.)", - }, - "root": { - "type": "string", - "description": "Workspace root override", - }, - "env": { - "type": "object", - "additionalProperties": {"type": "string"}, - "description": "Environment variables", - }, - "dry_run": {"type": "boolean", "default": False}, - "opts": { - "type": "object", - "properties": { - "local_prefix": { - "type": "string", - "description": "Go import grouping prefix (e.g. github.com/luxfi)", - } - }, - "description": "Tool-specific options", - }, - }, - "required": ["target"], - "additionalProperties": False, - }, - ), - Tool( - name="test", - description="Run tests narrowly by default", - inputSchema={ - "type": "object", - "properties": { - "target": { - "type": "string", - "description": "Target: file:, dir:, pkg:, ws, or changed", - }, - "language": { - "type": "string", - "enum": [ - "auto", - "go", - "ts", - "py", - "rs", - "cc", - "sol", - "schema", - ], - "default": "auto", - }, - "backend": { - "type": "string", - "default": "auto", - "description": "Backend override (go, npm, pytest, cargo, etc.)", - }, - "root": { - "type": "string", - "description": "Workspace root override", - }, - "env": { - "type": "object", - "additionalProperties": {"type": "string"}, - }, - "dry_run": {"type": "boolean", "default": False}, - "opts": { - "type": "object", - "properties": { - "run": { - "type": "string", - "description": "Go test filter", - }, - "count": { - "type": "integer", - "description": "Go test count", - }, - "race": { - "type": "boolean", - "description": "Go race detection", - }, - "filter": { - "type": "string", - "description": "TS test filter", - }, - "watch": { - "type": "boolean", - "description": "TS watch mode", - }, - "k": { - "type": "string", - "description": "Python test filter", - }, - "m": { - "type": "string", - "description": "Python test marker", - }, - "p": { - "type": "string", - "description": "Rust package", - }, - "features": { - "type": "array", - "items": {"type": "string"}, - "description": "Rust features", - }, - }, - "description": "Test-specific options", - }, - }, - "required": ["target"], - "additionalProperties": False, - }, - ), - Tool( - name="build", - description="Compile/build artifacts narrowly by default", - inputSchema={ - "type": "object", - "properties": { - "target": { - "type": "string", - "description": "Target: file:, dir:, pkg:, ws, or changed", - }, - "language": { - "type": "string", - "enum": [ - "auto", - "go", - "ts", - "py", - "rs", - "cc", - "sol", - "schema", - ], - "default": "auto", - }, - "backend": { - "type": "string", - "default": "auto", - "description": "Backend override", - }, - "root": { - "type": "string", - "description": "Workspace root override", - }, - "env": { - "type": "object", - "additionalProperties": {"type": "string"}, - }, - "dry_run": {"type": "boolean", "default": False}, - "opts": { - "type": "object", - "properties": { - "release": { - "type": "boolean", - "description": "Release build", - }, - "features": { - "type": "array", - "items": {"type": "string"}, - }, - }, - "description": "Build-specific options", - }, - }, - "required": ["target"], - "additionalProperties": False, - }, - ), - Tool( - name="lint", - description="Lint/typecheck in one place", - inputSchema={ - "type": "object", - "properties": { - "target": { - "type": "string", - "description": "Target: file:, dir:, pkg:, ws, or changed", - }, - "language": { - "type": "string", - "enum": [ - "auto", - "go", - "ts", - "py", - "rs", - "cc", - "sol", - "schema", - ], - "default": "auto", - }, - "backend": { - "type": "string", - "default": "auto", - "description": "Backend override", - }, - "root": { - "type": "string", - "description": "Workspace root override", - }, - "env": { - "type": "object", - "additionalProperties": {"type": "string"}, - }, - "dry_run": {"type": "boolean", "default": False}, - "opts": { - "type": "object", - "properties": { - "fix": { - "type": "boolean", - "description": "Auto-fix issues where possible", - } - }, - "description": "Lint-specific options", - }, - }, - "required": ["target"], - "additionalProperties": False, - }, - ), - Tool( - name="guard", - description="Repo invariants (boundaries, forbidden imports/strings, generated dirs)", - inputSchema={ - "type": "object", - "properties": { - "target": { - "type": "string", - "description": "Target: file:, dir:, pkg:, ws, or changed", - }, - "language": { - "type": "string", - "enum": [ - "auto", - "go", - "ts", - "py", - "rs", - "cc", - "sol", - "schema", - ], - "default": "auto", - }, - "backend": {"type": "string", "default": "auto"}, - "root": { - "type": "string", - "description": "Workspace root override", - }, - "env": { - "type": "object", - "additionalProperties": {"type": "string"}, - }, - "dry_run": {"type": "boolean", "default": False}, - "rules": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": {"type": "string"}, - "type": { - "type": "string", - "enum": ["regex", "import", "generated"], - }, - "glob": {"type": "string"}, - "pattern": {"type": "string"}, - "forbid_import_prefix": {"type": "string"}, - "forbid_writes": {"type": "boolean"}, - }, - "required": ["id", "type", "glob"], - }, - "description": "Guard rules to check", - }, - }, - "required": ["target", "rules"], - "additionalProperties": False, - }, - ), - ] - - @self.server.call_tool() - async def handle_call_tool(name: str, arguments: dict) -> List[TextContent]: - """Handle tool calls with exact specifications""" - try: - # Create target spec from common arguments - target_spec = TargetSpec( - target=arguments["target"], - language=arguments.get("language", "auto"), - backend=arguments.get("backend", "auto"), - root=arguments.get("root"), - env=arguments.get("env", {}), - dry_run=arguments.get("dry_run", False), - ) - - if name == "edit": - edit_args = EditArgs( - op=arguments["op"], - file=arguments.get("file"), - pos=arguments.get("pos"), - range=arguments.get("range"), - new_name=arguments.get("new_name"), - only=arguments.get("only", []), - apply=arguments.get("apply", True), - workspace_edit=arguments.get("workspace_edit"), - ) - result = await tools.edit(target_spec, edit_args) - - elif name == "fmt": - fmt_args = FmtArgs(opts=arguments.get("opts", {})) - result = await tools.fmt(target_spec, fmt_args) - - elif name == "test": - test_args = TestArgs(opts=arguments.get("opts", {})) - result = await tools.test(target_spec, test_args) - - elif name == "build": - build_args = BuildArgs(opts=arguments.get("opts", {})) - result = await tools.build(target_spec, build_args) - - elif name == "lint": - lint_args = LintArgs(opts=arguments.get("opts", {})) - result = await tools.lint(target_spec, lint_args) - - elif name == "guard": - rules = [GuardRule(**rule_data) for rule_data in arguments["rules"]] - guard_args = GuardArgs(rules=rules) - result = await tools.guard(target_spec, guard_args) - - else: - return [TextContent(type="text", text=f"Unknown tool: {name}")] - - return [ - TextContent( - type="text", text=self._format_exact_result(result, name) - ) - ] - - except Exception as e: - return [ - TextContent(type="text", text=f"Error executing {name}: {str(e)}") - ] - - def _format_exact_result(self, result, tool_name: str) -> str: - """Format tool result according to exact specification""" - status = "โœ…" if result.ok else "โŒ" - - output = f"=== {tool_name.upper()} RESULT ===\n" - output += f"Status: {status} {result.ok}\n" - output += f"Root: {result.root}\n" - output += f"Language: {result.language_used}\n" - output += f"Backend: {result.backend_used}\n" - output += f"Scope: {result.scope_resolved}\n" - output += f"Touched files: {len(result.touched_files)}\n" - output += f"Exit code: {result.exit_code}\n" - output += f"Execution time: {result.execution_time:.3f}s\n" - - if result.touched_files: - output += "\nModified files:\n" - for f in result.touched_files: - output += f" - {f}\n" - - if result.stdout: - output += f"\nSTDOUT:\n{result.stdout}\n" - - if result.stderr: - output += f"\nSTDERR:\n{result.stderr}\n" - - if result.errors: - output += "\nErrors:\n" - for error in result.errors: - output += f" - {error}\n" - - return output - - async def run(self, transport_type: str = "stdio"): - """Run the exact MCP server""" - if transport_type == "stdio": - from mcp.server.stdio import stdio_server - - async with stdio_server() as (read_stream, write_stream): - await self.server.run( - read_stream, - write_stream, - InitializationOptions( - server_name="hanzo-mcp-exact", - server_version="1.0.0", - capabilities=self.server.get_capabilities( - notification_options=NotificationOptions(), - experimental_capabilities={}, - ), - ), - ) - - -if __name__ == "__main__": - server = ExactHanzoMCPServer() - asyncio.run(server.run()) diff --git a/pkg/hanzo-mcp/hanzo_mcp/exact_tools.py b/pkg/hanzo-mcp/hanzo_mcp/exact_tools.py deleted file mode 100644 index 689ef52d5..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/exact_tools.py +++ /dev/null @@ -1,1733 +0,0 @@ -#!/usr/bin/env python3 -""" -Hanzo MCP - Exact 6-Tool Implementation -======================================= - -Implements the precise specification for edit, fmt, test, build, lint, guard tools -with proper target resolution, workspace detection, and backend selection. -""" - -import asyncio -import json -import os -import subprocess -from dataclasses import asdict, dataclass, field -from datetime import datetime -from pathlib import Path -from typing import Any, Dict, List, Literal, Optional, Union - -from hanzo_tools.lsp.lsp_tool import LSPTool -from pydantic import BaseModel, Field - -try: - from pydantic import ConfigDict -except ImportError: # pragma: no cover - pydantic v1 - ConfigDict = None - - -class StrictModel(BaseModel): - if ConfigDict is not None: - model_config = ConfigDict(extra="forbid") - else: - - class Config: - extra = "forbid" - - -# Core Types -@dataclass -class ToolResult: - """Standard result format for all tools""" - - ok: bool - root: str - language_used: Union[str, List[str]] - backend_used: Union[str, List[str]] - scope_resolved: Union[str, List[str]] - touched_files: List[str] - stdout: str - stderr: str - exit_code: int - errors: List[str] - violations: List[Dict[str, Any]] = field(default_factory=list) - execution_time: float = 0.0 - - -# Shared Input Schema -class TargetSpec(StrictModel): - target: str = Field( - ..., description="file:, dir:, pkg:, ws, or changed" - ) - language: str = Field(default="auto", description="auto|go|ts|py|rs|cc|sol|schema") - backend: str = Field(default="auto", description="Backend override") - root: Optional[str] = Field(default=None, description="Workspace root override") - env: Dict[str, str] = Field( - default_factory=dict, description="Environment variables" - ) - dry_run: bool = Field(default=False, description="Preview mode") - - -# Tool-specific schemas -class EditArgs(StrictModel): - op: Literal["rename", "code_action", "organize_imports", "apply_workspace_edit"] - file: Optional[str] = None - pos: Optional[Dict[str, int]] = None # {line, character} - range: Optional[Dict[str, Dict[str, int]]] = ( - None # {start: {line, ch}, end: {line, ch}} - ) - new_name: Optional[str] = None - only: List[str] = Field(default_factory=list) # LSP codeAction kinds - apply: bool = True - workspace_edit: Optional[Dict[str, Any]] = None - - -class FmtArgs(StrictModel): - opts: Dict[str, Any] = Field(default_factory=dict) # {local_prefix?: str} - - -class TestArgs(StrictModel): - opts: Dict[str, Any] = Field( - default_factory=dict - ) # go: {run?, count?, race?}, ts: {filter?, watch?}, etc. - - -class BuildArgs(StrictModel): - opts: Dict[str, Any] = Field(default_factory=dict) - - -class LintArgs(StrictModel): - opts: Dict[str, Any] = Field(default_factory=dict) # {fix?: bool} - - -class GuardRule(StrictModel): - id: str - type: Literal["regex", "import", "generated"] - glob: str - pattern: Optional[str] = None - forbid_import_prefix: Optional[str] = None - forbid_writes: Optional[bool] = None - - -class GuardArgs(StrictModel): - rules: List[GuardRule] - - -class GuardViolation(StrictModel): - file: str - line: int - column: int = 1 - import_path: Optional[str] = None - symbol: Optional[str] = None - message: Optional[str] = None - rule_id: str - text: str = "" - - -class WorkspaceDetector: - """Intelligent workspace detection with go.work priority""" - - @staticmethod - def detect(target_path: str, root_hint: Optional[str] = None) -> Dict[str, Any]: - """Detect workspace root and configuration, preferring go.work""" - path = Path(target_path).resolve() - boundary: Optional[Path] = None - if root_hint: - boundary = Path(root_hint).resolve() - if boundary.is_file(): - boundary = boundary.parent - - # If it's a file, start from parent directory - if path.is_file(): - path = path.parent - - # Walk up to find workspace markers, prioritizing go.work - parents = [path] + list(path.parents) - if boundary is not None and boundary in parents: - parents = parents[: parents.index(boundary) + 1] - for current in parents: - # Check for go.work first (highest priority) - if (current / "go.work").is_file(): - return { - "root": str(current), - "type": "go_workspace", - "config": current / "go.work", - "language": "go", - "go_work_file": str(current / "go.work"), - } - - # Then check other workspace types - for current in parents: - if (current / "go.mod").is_file(): - return { - "root": str(current), - "type": "go_module", - "config": current / "go.mod", - "language": "go", - } - elif (current / "package.json").is_file(): - with open(current / "package.json") as f: - pkg_data = json.load(f) - has_workspaces = "workspaces" in pkg_data - return { - "root": str(current), - "type": "node_workspace" if has_workspaces else "node_project", - "config": current / "package.json", - "language": "ts", - } - elif (current / "pyproject.toml").is_file(): - return { - "root": str(current), - "type": "python_workspace", - "config": current / "pyproject.toml", - "language": "py", - } - elif (current / "Cargo.toml").is_file(): - return { - "root": str(current), - "type": "rust_workspace", - "config": current / "Cargo.toml", - "language": "rs", - } - elif (current / "CMakeLists.txt").is_file(): - return { - "root": str(current), - "type": "cmake_project", - "config": current / "CMakeLists.txt", - "language": "cc", - } - elif (current / "buf.yaml").is_file() or (current / "buf.yml").is_file(): - config_file = ( - current / "buf.yaml" - if (current / "buf.yaml").is_file() - else current / "buf.yml" - ) - return { - "root": str(current), - "type": "buf_workspace", - "config": config_file, - "language": "schema", - } - elif (current / ".git").exists(): - return { - "root": str(current), - "type": "git_repository", - "config": current / ".git", - "language": "auto", - } - - # Default to current directory - return { - "root": str(path), - "type": "directory", - "config": None, - "language": "auto", - } - - -class TargetResolver: - """Resolves target specifications to concrete file lists""" - - def __init__(self, workspace_detector: WorkspaceDetector): - self.workspace_detector = workspace_detector - - def resolve(self, target_spec: TargetSpec) -> Dict[str, Any]: - """Resolve target specification to concrete files/paths""" - target = target_spec.target - - if target.startswith("file:"): - return self._resolve_file(target[5:], target_spec) - elif target.startswith("dir:"): - return self._resolve_dir(target[4:], target_spec) - elif target.startswith("pkg:"): - return self._resolve_package(target[4:], target_spec) - elif target == "ws": - return self._resolve_workspace(target_spec) - elif target == "changed": - return self._resolve_changed(target_spec) - else: - raise ValueError(f"Unknown target format: {target}") - - def _resolve_file(self, file_path: str, target_spec: TargetSpec) -> Dict[str, Any]: - """Resolve single file target""" - path = Path(file_path).absolute() - workspace = self.workspace_detector.detect( - str(path), root_hint=target_spec.root - ) - - return { - "type": "file", - "paths": [str(path)], - "workspace": workspace, - "language": self._infer_language(path, workspace, target_spec.language), - } - - def _resolve_dir(self, dir_path: str, target_spec: TargetSpec) -> Dict[str, Any]: - """Resolve directory subtree target""" - path = Path(dir_path).absolute() - workspace = self.workspace_detector.detect( - str(path), root_hint=target_spec.root - ) - language = self._infer_language(path, workspace, target_spec.language) - - # Find relevant files based on language - extensions = self._get_extensions_for_language(language) - files = [] - - for ext in extensions: - files.extend(path.rglob(f"*{ext}")) - - return { - "type": "directory", - "paths": [str(f) for f in files], - "workspace": workspace, - "language": language, - } - - def _resolve_package( - self, pkg_spec: str, target_spec: TargetSpec - ) -> Dict[str, Any]: - """Resolve package specification""" - workspace = self.workspace_detector.detect( - target_spec.root or ".", root_hint=target_spec.root - ) - language = ( - workspace["language"] - if target_spec.language == "auto" - else target_spec.language - ) - - if language == "go": - return self._resolve_go_package(pkg_spec, workspace, target_spec) - elif language == "ts": - return self._resolve_ts_package(pkg_spec, workspace, target_spec) - elif language == "py": - return self._resolve_py_package(pkg_spec, workspace, target_spec) - elif language == "rs": - return self._resolve_rust_package(pkg_spec, workspace, target_spec) - else: - raise ValueError( - f"Package resolution not supported for language: {language}" - ) - - def _resolve_workspace(self, target_spec: TargetSpec) -> Dict[str, Any]: - """Resolve workspace root""" - workspace = self.workspace_detector.detect( - target_spec.root or ".", root_hint=target_spec.root - ) - language = ( - workspace["language"] - if target_spec.language == "auto" - else target_spec.language - ) - - root = Path(workspace["root"]) - extensions = self._get_extensions_for_language(language) - files = [] - - for ext in extensions: - files.extend(root.rglob(f"*{ext}")) - - return { - "type": "workspace", - "paths": [str(f) for f in files], - "workspace": workspace, - "language": language, - } - - def _resolve_changed(self, target_spec: TargetSpec) -> Dict[str, Any]: - """Resolve git changed files""" - workspace = self.workspace_detector.detect( - target_spec.root or ".", root_hint=target_spec.root - ) - - try: - result = subprocess.run( - ["git", "diff", "--name-only", "HEAD"], - cwd=workspace["root"], - capture_output=True, - text=True, - check=True, - ) - changed_files = ( - result.stdout.strip().split("\n") if result.stdout.strip() else [] - ) - - # Make paths absolute and filter existing files - root_path = Path(workspace["root"]) - absolute_files = [] - for file in changed_files: - abs_file = root_path / file - if abs_file.exists(): - absolute_files.append(str(abs_file)) - - return { - "type": "changed", - "paths": absolute_files, - "workspace": workspace, - "language": "auto", # Mixed files - } - except subprocess.CalledProcessError as e: - return { - "type": "changed", - "paths": [], - "workspace": workspace, - "language": "auto", - "error": f"Git command failed: {e}", - } - - def _resolve_go_package( - self, pkg_spec: str, workspace: Dict, target_spec: TargetSpec - ) -> Dict[str, Any]: - """Resolve Go package specification""" - Path(workspace["root"]) - - if pkg_spec == "./...": - # All packages in workspace - cmd = ["go", "list", "./..."] - elif pkg_spec.endswith("/..."): - # Package tree - cmd = ["go", "list", pkg_spec] - else: - # Specific package - cmd = ["go", "list", pkg_spec] - - try: - env = os.environ.copy() - env["GOWORK"] = "auto" # Always use go.work if available - - result = subprocess.run( - cmd, - cwd=workspace["root"], - capture_output=True, - text=True, - env=env, - check=True, - ) - - packages = result.stdout.strip().split("\n") - - # Convert package names to file paths - files = [] - for pkg in packages: - if not pkg: - continue - - # Get package directory - pkg_result = subprocess.run( - ["go", "list", "-f", "{{.Dir}}", pkg], - cwd=workspace["root"], - capture_output=True, - text=True, - env=env, - check=True, - ) - - pkg_dir = Path(pkg_result.stdout.strip()) - if pkg_dir.exists(): - # Add all .go files in package - files.extend(pkg_dir.glob("*.go")) - - return { - "type": "package", - "paths": [str(f) for f in files], - "workspace": workspace, - "language": "go", - "packages": packages, - } - - except subprocess.CalledProcessError as e: - return { - "type": "package", - "paths": [], - "workspace": workspace, - "language": "go", - "error": f"Go list failed: {e}", - } - - def _resolve_ts_package( - self, pkg_spec: str, workspace: Dict, target_spec: TargetSpec - ) -> Dict[str, Any]: - """Resolve TypeScript/Node package specification""" - # Handle npm/pnpm workspace filters - if pkg_spec.startswith("--filter "): - filter_name = pkg_spec[9:] - # Use pnpm/npm workspace commands - return { - "type": "package", - "paths": [], # Would need to query workspace for actual files - "workspace": workspace, - "language": "ts", - "filter": filter_name, - } - - # Default to directory-based resolution - return self._resolve_dir(pkg_spec, target_spec) - - def _resolve_py_package( - self, pkg_spec: str, workspace: Dict, target_spec: TargetSpec - ) -> Dict[str, Any]: - """Resolve Python package specification""" - # Could use importlib or package discovery here - return self._resolve_dir(pkg_spec, target_spec) - - def _resolve_rust_package( - self, pkg_spec: str, workspace: Dict, target_spec: TargetSpec - ) -> Dict[str, Any]: - """Resolve Rust package specification""" - if pkg_spec.startswith("-p "): - package_name = pkg_spec[3:] - # Use cargo metadata to find package - try: - result = subprocess.run( - ["cargo", "metadata", "--format-version", "1"], - cwd=workspace["root"], - capture_output=True, - text=True, - check=True, - ) - - metadata = json.loads(result.stdout) - for pkg in metadata.get("packages", []): - if pkg["name"] == package_name: - manifest_path = Path(pkg["manifest_path"]) - pkg_dir = manifest_path.parent - files = list(pkg_dir.rglob("*.rs")) - - return { - "type": "package", - "paths": [str(f) for f in files], - "workspace": workspace, - "language": "rs", - "package": package_name, - } - - except subprocess.CalledProcessError: - pass - - return self._resolve_dir(pkg_spec, target_spec) - - def _infer_language(self, path: Path, workspace: Dict, language_hint: str) -> str: - """Infer language from file extension and workspace""" - if language_hint != "auto": - return language_hint - - # Use workspace language if available - if workspace["language"] != "auto": - return workspace["language"] - - # Infer from file extension - if path.is_file(): - suffix = path.suffix - extension_map = { - ".go": "go", - ".ts": "ts", - ".tsx": "ts", - ".js": "ts", - ".jsx": "ts", - ".py": "py", - ".rs": "rs", - ".c": "cc", - ".cpp": "cc", - ".cc": "cc", - ".cxx": "cc", - ".h": "cc", - ".hpp": "cc", - ".sol": "sol", - ".proto": "schema", - } - return extension_map.get(suffix, "auto") - - return "auto" - - def _get_extensions_for_language(self, language: str) -> List[str]: - """Get file extensions for language""" - extension_map = { - "go": [".go"], - "ts": [".ts", ".tsx", ".js", ".jsx"], - "py": [".py"], - "rs": [".rs"], - "cc": [".c", ".cpp", ".cc", ".cxx", ".h", ".hpp"], - "sol": [".sol"], - "schema": [".proto"], - "auto": [ - ".go", - ".ts", - ".tsx", - ".js", - ".jsx", - ".py", - ".rs", - ".c", - ".cpp", - ".cc", - ".cxx", - ".h", - ".hpp", - ".sol", - ".proto", - ], - } - return extension_map.get( - language, [".go", ".ts", ".js", ".py", ".rs", ".c", ".cpp"] - ) - - -class BackendSelector: - """Selects appropriate backend tools for each language/operation""" - - @staticmethod - def select_backend(language: str, tool: str, backend_hint: str = "auto") -> str: - """Select backend tool for language and operation""" - if backend_hint != "auto": - return backend_hint - - backend_map = { - "go": { - "fmt": "goimports", - "test": "go", - "build": "go", - "lint": "golangci-lint", - "edit": "gopls", - }, - "ts": { - "fmt": "prettier", - "test": "npm", # or pnpm/yarn - "build": "tsc", - "lint": "eslint", - "edit": "typescript-language-server", - }, - "py": { - "fmt": "ruff", - "test": "pytest", - "build": "build", - "lint": "ruff", - "edit": "pyright", - }, - "rs": { - "fmt": "cargo", - "test": "cargo", - "build": "cargo", - "lint": "cargo", - "edit": "rust-analyzer", - }, - "cc": { - "fmt": "clang-format", - "test": "ctest", - "build": "cmake", - "lint": "clang-tidy", - "edit": "clangd", - }, - "sol": { - "fmt": "prettier", - "test": "hardhat", - "build": "forge", - "lint": "slither", - "edit": "solidity-language-server", - }, - "schema": { - "fmt": "buf", - "test": "buf", - "build": "buf", - "lint": "buf", - "edit": "buf", - }, - } - - return backend_map.get(language, {}).get(tool, "unknown") - - -class LSPBridge: - """LSP client for semantic operations""" - - def __init__(self): - self.tool = LSPTool() - - async def rename_symbol( - self, - file_path: str, - line: int, - character: int, - new_name: str, - workspace_root: str, - language: str, - apply: bool, - ) -> Dict[str, Any]: - """Perform LSP rename operation, return result""" - result = await self.tool.run( - action="rename", - file=file_path, - line=line, - character=character, - new_name=new_name, - apply_edits=apply, - ) - return result.data - - async def code_actions( - self, - file_path: str, - range_spec: Dict, - only: List[str], - workspace_root: str, - language: str, - apply: bool, - ) -> Dict[str, Any]: - """Get and apply code actions, return result""" - result = await self.tool.run( - action="code_action", - file=file_path, - line=range_spec.get("start", {}).get("line", 1), - character=range_spec.get("start", {}).get("character", 0), - only=only, - range=range_spec, - apply_edits=apply, - ) - return result.data - - async def organize_imports( - self, - file_paths: List[str], - workspace_root: str, - language: str, - apply: bool, - ) -> Dict[str, Any]: - """Organize imports for files, return result""" - touched = [] - errors: List[str] = [] - for file_path in file_paths: - result = await self.tool.run( - action="organize_imports", - file=file_path, - apply_edits=apply, - ) - data = result.data - touched.extend(data.get("applied_files", data.get("touched_files", []))) - if "apply_errors" in data: - errors.extend(data["apply_errors"]) - if "error" in data: - errors.append(str(data["error"])) - return {"touched_files": sorted(set(touched)), "errors": errors} - - async def apply_workspace_edit( - self, edit: Dict[str, Any], workspace_root: str - ) -> Dict[str, Any]: - """Apply a WorkspaceEdit directly.""" - applied, errors = self.tool._apply_workspace_edit(edit, workspace_root) - return {"touched_files": applied, "errors": errors} - - -class HanzoTools: - """Implementation of the 6 universal tools""" - - def __init__(self): - self.workspace_detector = WorkspaceDetector() - self.target_resolver = TargetResolver(self.workspace_detector) - self.backend_selector = BackendSelector() - self.lsp_bridge = LSPBridge() - - def _prepare_environment( - self, workspace: Dict, target_spec: TargetSpec - ) -> Dict[str, str]: - """Prepare environment variables for tool execution""" - env = os.environ.copy() - - # Always set GOWORK=auto for Go workspaces - if workspace.get("type") == "go_workspace": - env["GOWORK"] = "auto" - - # Add custom environment variables - env.update(target_spec.env) - - return env - - async def edit(self, target_spec: TargetSpec, args: EditArgs) -> ToolResult: - """Edit tool: semantic refactors via LSP""" - start_time = datetime.utcnow() - - try: - resolved = self.target_resolver.resolve(target_spec) - workspace = resolved["workspace"] - language = resolved["language"] - - backend = self.backend_selector.select_backend( - language, "edit", target_spec.backend - ) - - touched_files = [] - stdout_parts = [] - stderr_parts = [] - errors: List[str] = [] - apply = args.apply and not target_spec.dry_run - - if args.op == "rename": - if not args.file or not args.pos or not args.new_name: - raise ValueError("rename requires file, pos, and new_name") - result = await self.lsp_bridge.rename_symbol( - args.file, - args.pos["line"], - args.pos["character"], - args.new_name, - workspace["root"], - language, - apply=apply, - ) - if "error" in result: - raise RuntimeError(str(result["error"])) - touched_files = result.get( - "applied_files", result.get("touched_files", []) - ) - errors.extend(result.get("apply_errors", [])) - if apply: - stdout_parts.append( - f"Renamed symbol to '{args.new_name}' in {len(touched_files)} files" - ) - else: - stdout_parts.append( - f"Would rename symbol to '{args.new_name}' at {args.file}:{args.pos['line']}:{args.pos['character']}" - ) - - elif args.op == "code_action": - if not args.file: - raise ValueError("code_action requires file") - result = await self.lsp_bridge.code_actions( - args.file, - args.range or {}, - args.only, - workspace["root"], - language, - apply=apply, - ) - if "error" in result: - raise RuntimeError(str(result["error"])) - touched_files = result.get( - "applied_files", result.get("touched_files", []) - ) - errors.extend(result.get("apply_errors", [])) - if apply: - stdout_parts.append( - f"Applied code actions to {len(touched_files)} files" - ) - else: - stdout_parts.append(f"Would apply code actions to {args.file}") - - elif args.op == "organize_imports": - result = await self.lsp_bridge.organize_imports( - resolved["paths"], workspace["root"], language, apply=apply - ) - touched_files = result.get("touched_files", []) - errors.extend(result.get("errors", [])) - if apply: - stdout_parts.append( - f"Organized imports in {len(touched_files)} files" - ) - else: - stdout_parts.append( - f"Would organize imports in {len(resolved['paths'])} files" - ) - - elif args.op == "apply_workspace_edit": - if not args.workspace_edit: - raise ValueError("apply_workspace_edit requires workspace_edit") - result = await self.lsp_bridge.apply_workspace_edit( - args.workspace_edit, workspace["root"] - ) - touched_files = result.get("touched_files", []) - errors.extend(result.get("errors", [])) - if apply: - stdout_parts.append( - f"Applied WorkspaceEdit to {len(touched_files)} files" - ) - else: - stdout_parts.append( - f"Would apply WorkspaceEdit to {len(touched_files)} files" - ) - - return ToolResult( - ok=len(errors) == 0, - root=workspace["root"], - language_used=language, - backend_used=backend, - scope_resolved=resolved["paths"], - touched_files=touched_files, - stdout="\n".join(stdout_parts), - stderr="\n".join(stderr_parts), - exit_code=0 if len(errors) == 0 else 1, - errors=errors, - execution_time=(datetime.utcnow() - start_time).total_seconds(), - ) - - except Exception as e: - return ToolResult( - ok=False, - root=workspace.get("root", "."), - language_used="unknown", - backend_used="unknown", - scope_resolved=[], - touched_files=[], - stdout="", - stderr=str(e), - exit_code=1, - errors=[str(e)], - execution_time=(datetime.utcnow() - start_time).total_seconds(), - ) - - async def fmt(self, target_spec: TargetSpec, args: FmtArgs) -> ToolResult: - """Format tool: formatting + import normalization""" - start_time = datetime.utcnow() - - try: - resolved = self.target_resolver.resolve(target_spec) - workspace = resolved["workspace"] - language = resolved["language"] - - backend = self.backend_selector.select_backend( - language, "fmt", target_spec.backend - ) - env = self._prepare_environment(workspace, target_spec) - - touched_files = [] - stdout_parts = [] - stderr_parts = [] - - for file_path in resolved["paths"]: - if target_spec.dry_run: - stdout_parts.append(f"Would format: {file_path}") - continue - - if language == "go": - cmd = ["goimports", "-w"] - if "local_prefix" in args.opts: - cmd.extend(["-local", args.opts["local_prefix"]]) - cmd.append(file_path) - - elif language == "ts": - cmd = ["prettier", "--write", file_path] - - elif language == "py": - cmd = ["ruff", "format", file_path] - - elif language == "rs": - # For Rust, format the whole package - cmd = ["cargo", "fmt"] - - elif language == "cc": - cmd = ["clang-format", "-i", file_path] - - elif language == "sol": - cmd = ["prettier", "--write", file_path] - - elif language == "schema": - cmd = ["buf", "format", "-w", file_path] - - else: - continue - - try: - result = subprocess.run( - cmd, - cwd=workspace["root"], - env=env, - capture_output=True, - text=True, - timeout=30, - ) - - if result.returncode == 0: - touched_files.append(file_path) - stdout_parts.append(f"Formatted: {file_path}") - else: - stderr_parts.append( - f"Failed to format {file_path}: {result.stderr}" - ) - - except subprocess.TimeoutExpired: - stderr_parts.append(f"Timeout formatting {file_path}") - except FileNotFoundError: - stderr_parts.append(f"Formatter not found for {language}: {cmd[0]}") - - return ToolResult( - ok=len(stderr_parts) == 0, - root=workspace["root"], - language_used=language, - backend_used=backend, - scope_resolved=resolved["paths"], - touched_files=touched_files, - stdout="\n".join(stdout_parts), - stderr="\n".join(stderr_parts), - exit_code=0 if len(stderr_parts) == 0 else 1, - errors=stderr_parts, - execution_time=(datetime.utcnow() - start_time).total_seconds(), - ) - - except Exception as e: - return ToolResult( - ok=False, - root=".", - language_used="unknown", - backend_used="unknown", - scope_resolved=[], - touched_files=[], - stdout="", - stderr=str(e), - exit_code=1, - errors=[str(e)], - execution_time=(datetime.utcnow() - start_time).total_seconds(), - ) - - async def test(self, target_spec: TargetSpec, args: TestArgs) -> ToolResult: - """Test tool: run tests narrowly by default""" - start_time = datetime.utcnow() - - try: - resolved = self.target_resolver.resolve(target_spec) - workspace = resolved["workspace"] - language = resolved["language"] - - backend = self.backend_selector.select_backend( - language, "test", target_spec.backend - ) - env = self._prepare_environment(workspace, target_spec) - - # Build test command based on language and scope - if language == "go": - if resolved["type"] == "package": - # Test specific packages - packages = resolved.get("packages", ["./..."]) - cmd = ["go", "test"] + packages - else: - # Derive package from file - cmd = ["go", "test", "./..."] - - # Add Go-specific options - if "run" in args.opts: - cmd.extend(["-run", args.opts["run"]]) - if "count" in args.opts: - cmd.extend(["-count", str(args.opts["count"])]) - if args.opts.get("race"): - cmd.append("-race") - - elif language == "ts": - # Use npm/pnpm test script - cmd = ["npm", "test"] - if "filter" in args.opts: - cmd.append(f"--filter={args.opts['filter']}") - if not args.opts.get("watch", True): - cmd.append("--no-watch") - - elif language == "py": - cmd = ["pytest"] - if resolved["type"] == "file": - # Test specific file - cmd.extend(resolved["paths"]) - - if "k" in args.opts: - cmd.extend(["-k", args.opts["k"]]) - if "m" in args.opts: - cmd.extend(["-m", args.opts["m"]]) - - elif language == "rs": - cmd = ["cargo", "test"] - if "p" in args.opts: - cmd.extend(["-p", args.opts["p"]]) - if "features" in args.opts: - cmd.extend(["--features", ",".join(args.opts["features"])]) - - else: - raise ValueError(f"Testing not supported for language: {language}") - - if target_spec.dry_run: - return ToolResult( - ok=True, - root=workspace["root"], - language_used=language, - backend_used=backend, - scope_resolved=resolved["paths"], - touched_files=[], - stdout=f"Would run: {' '.join(cmd)}", - stderr="", - exit_code=0, - errors=[], - execution_time=(datetime.utcnow() - start_time).total_seconds(), - ) - - # Execute test command - result = subprocess.run( - cmd, - cwd=workspace["root"], - env=env, - capture_output=True, - text=True, - timeout=300, # 5 minute timeout for tests - ) - - return ToolResult( - ok=result.returncode == 0, - root=workspace["root"], - language_used=language, - backend_used=backend, - scope_resolved=resolved["paths"], - touched_files=[], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - errors=[result.stderr] if result.returncode != 0 else [], - execution_time=(datetime.utcnow() - start_time).total_seconds(), - ) - - except Exception as e: - return ToolResult( - ok=False, - root=".", - language_used="unknown", - backend_used="unknown", - scope_resolved=[], - touched_files=[], - stdout="", - stderr=str(e), - exit_code=1, - errors=[str(e)], - execution_time=(datetime.utcnow() - start_time).total_seconds(), - ) - - async def build(self, target_spec: TargetSpec, args: BuildArgs) -> ToolResult: - """Build tool: compile/build artifacts narrowly by default""" - start_time = datetime.utcnow() - - try: - resolved = self.target_resolver.resolve(target_spec) - workspace = resolved["workspace"] - language = resolved["language"] - - backend = self.backend_selector.select_backend( - language, "build", target_spec.backend - ) - env = self._prepare_environment(workspace, target_spec) - - # Build command based on language - if language == "go": - if resolved["type"] == "package": - packages = resolved.get("packages", ["./..."]) - cmd = ["go", "build"] + packages - else: - cmd = ["go", "build", "./..."] - - elif language == "ts": - cmd = ["tsc"] # or npm run build - - elif language == "py": - cmd = ["python", "-m", "build"] - - elif language == "rs": - cmd = ["cargo", "build"] - if args.opts.get("release"): - cmd.append("--release") - - elif language == "cc": - cmd = ["cmake", "--build", "."] - - elif language == "sol": - cmd = ["forge", "build"] # or hardhat compile - - else: - raise ValueError(f"Build not supported for language: {language}") - - if target_spec.dry_run: - return ToolResult( - ok=True, - root=workspace["root"], - language_used=language, - backend_used=backend, - scope_resolved=resolved["paths"], - touched_files=[], - stdout=f"Would run: {' '.join(cmd)}", - stderr="", - exit_code=0, - errors=[], - execution_time=(datetime.utcnow() - start_time).total_seconds(), - ) - - result = subprocess.run( - cmd, - cwd=workspace["root"], - env=env, - capture_output=True, - text=True, - timeout=300, - ) - - return ToolResult( - ok=result.returncode == 0, - root=workspace["root"], - language_used=language, - backend_used=backend, - scope_resolved=resolved["paths"], - touched_files=[], # Build typically doesn't modify source files - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - errors=[result.stderr] if result.returncode != 0 else [], - execution_time=(datetime.utcnow() - start_time).total_seconds(), - ) - - except Exception as e: - return ToolResult( - ok=False, - root=".", - language_used="unknown", - backend_used="unknown", - scope_resolved=[], - touched_files=[], - stdout="", - stderr=str(e), - exit_code=1, - errors=[str(e)], - execution_time=(datetime.utcnow() - start_time).total_seconds(), - ) - - async def lint(self, target_spec: TargetSpec, args: LintArgs) -> ToolResult: - """Lint tool: lint/typecheck in one place""" - start_time = datetime.utcnow() - - try: - resolved = self.target_resolver.resolve(target_spec) - workspace = resolved["workspace"] - language = resolved["language"] - - backend = self.backend_selector.select_backend( - language, "lint", target_spec.backend - ) - env = self._prepare_environment(workspace, target_spec) - - # Build lint command based on language - if language == "go": - cmd = ["golangci-lint", "run"] - if resolved["type"] == "file": - cmd.extend(resolved["paths"]) - elif resolved["type"] == "package": - cmd.append("./...") - - elif language == "ts": - cmd = ["eslint"] - if resolved["paths"]: - cmd.extend(resolved["paths"]) - else: - cmd.append(".") - - if args.opts.get("fix"): - cmd.append("--fix") - - elif language == "py": - cmd = ["ruff", "check"] - if resolved["paths"]: - cmd.extend(resolved["paths"]) - else: - cmd.append(".") - - if args.opts.get("fix"): - cmd.append("--fix") - - elif language == "rs": - cmd = ["cargo", "clippy"] - - elif language == "cc": - cmd = ["clang-tidy"] + resolved["paths"] - - elif language == "schema": - cmd = ["buf", "lint"] - - else: - raise ValueError(f"Lint not supported for language: {language}") - - if target_spec.dry_run: - return ToolResult( - ok=True, - root=workspace["root"], - language_used=language, - backend_used=backend, - scope_resolved=resolved["paths"], - touched_files=[], - stdout=f"Would run: {' '.join(cmd)}", - stderr="", - exit_code=0, - errors=[], - execution_time=(datetime.utcnow() - start_time).total_seconds(), - ) - - result = subprocess.run( - cmd, - cwd=workspace["root"], - env=env, - capture_output=True, - text=True, - timeout=120, - ) - - # Lint tools often return non-zero for warnings - success = result.returncode == 0 - - return ToolResult( - ok=success, - root=workspace["root"], - language_used=language, - backend_used=backend, - scope_resolved=resolved["paths"], - touched_files=[], # Lint may fix files if --fix is used - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - errors=[result.stderr] if not success else [], - execution_time=(datetime.utcnow() - start_time).total_seconds(), - ) - - except Exception as e: - return ToolResult( - ok=False, - root=".", - language_used="unknown", - backend_used="unknown", - scope_resolved=[], - touched_files=[], - stdout="", - stderr=str(e), - exit_code=1, - errors=[str(e)], - execution_time=(datetime.utcnow() - start_time).total_seconds(), - ) - - async def guard(self, target_spec: TargetSpec, args: GuardArgs) -> ToolResult: - """Guard tool: repo invariants (boundaries, forbidden imports/strings)""" - start_time = datetime.utcnow() - - try: - resolved = self.target_resolver.resolve(target_spec) - workspace = resolved["workspace"] - language = resolved["language"] - - violations = [] - - for rule in args.rules: - rule_violations = await self._check_guard_rule( - rule, resolved["paths"], workspace["root"], language - ) - violations.extend(rule_violations) - - stdout_lines = [] - if violations: - stdout_lines.append(f"Found {len(violations)} guard violations:") - for v in violations: - location = f"{v.file}:{v.line}:{v.column}" - detail = v.import_path or v.text - stdout_lines.append(f" {location} - {v.rule_id}: {detail}") - else: - stdout_lines.append("No guard violations found") - - return ToolResult( - ok=len(violations) == 0, - root=workspace["root"], - language_used="auto", - backend_used="guard", - scope_resolved=resolved["paths"], - touched_files=[], - stdout="\n".join(stdout_lines), - stderr="", - exit_code=0 if len(violations) == 0 else 1, - errors=[ - f"{v.rule_id}: {v.file}:{v.line}:{v.column}" for v in violations - ], - violations=[v.model_dump() for v in violations], - execution_time=(datetime.utcnow() - start_time).total_seconds(), - ) - - except Exception as e: - return ToolResult( - ok=False, - root=".", - language_used="unknown", - backend_used="guard", - scope_resolved=[], - touched_files=[], - stdout="", - stderr=str(e), - exit_code=1, - errors=[str(e)], - execution_time=(datetime.utcnow() - start_time).total_seconds(), - ) - - async def _check_guard_rule( - self, rule: GuardRule, file_paths: List[str], workspace_root: str, language: str - ) -> List[GuardViolation]: - """Check a single guard rule against files""" - violations = [] - - # Match files against glob pattern - import fnmatch - - matched_files = [] - - for file_path in file_paths: - rel_path = os.path.relpath(file_path, workspace_root) - if fnmatch.fnmatch(rel_path, rule.glob): - matched_files.append(file_path) - - if rule.type == "import" and rule.forbid_import_prefix: - wants_go = language in ["go", "auto"] and any( - p.endswith(".go") for p in matched_files - ) - wants_ts = language in ["ts", "js", "auto"] and any( - p.endswith((".ts", ".tsx", ".js", ".jsx")) for p in matched_files - ) - go_index = ( - await self._go_package_index(workspace_root) if wants_go else None - ) - ts_graph = ( - self._build_ts_dependency_graph(workspace_root) if wants_ts else None - ) - else: - go_index = None - ts_graph = None - - for file_path in matched_files: - try: - with open(file_path, "r", encoding="utf-8") as f: - lines = f.readlines() - - if rule.type == "regex": - import re - - pattern = re.compile(rule.pattern) - for line_num, line in enumerate(lines, 1): - if pattern.search(line): - violations.append( - GuardViolation( - file=file_path, - line=line_num, - column=1, - text=line.strip(), - rule_id=rule.id, - ) - ) - - elif rule.type == "import": - if file_path.endswith(".go"): - for imp in self._scan_go_imports(file_path): - if imp["import_path"].startswith(rule.forbid_import_prefix): - violations.append( - GuardViolation( - file=file_path, - line=imp["line"], - column=imp["column"], - import_path=imp["import_path"], - rule_id=rule.id, - message="forbidden direct import", - ) - ) - if go_index and file_path in go_index["file_to_pkg"]: - pkg = go_index["file_to_pkg"][file_path] - deps = go_index["pkg_deps"].get(pkg, set()) - for dep in deps: - if dep.startswith(rule.forbid_import_prefix): - violations.append( - GuardViolation( - file=file_path, - line=1, - column=1, - import_path=dep, - rule_id=rule.id, - message="forbidden transitive import", - ) - ) - break - else: - for imp in self._scan_ts_imports(file_path): - if imp["import_path"].startswith(rule.forbid_import_prefix): - violations.append( - GuardViolation( - file=file_path, - line=imp["line"], - column=imp["column"], - import_path=imp["import_path"], - rule_id=rule.id, - message="forbidden direct import", - ) - ) - if ts_graph: - deps = self._ts_transitive_deps(file_path, ts_graph) - for dep in deps: - if dep.startswith(rule.forbid_import_prefix): - violations.append( - GuardViolation( - file=file_path, - line=1, - column=1, - import_path=dep, - rule_id=rule.id, - message="forbidden transitive import", - ) - ) - break - - elif rule.type == "generated" and rule.forbid_writes: - # Check if file was recently modified (this is a simple check) - violations.append( - GuardViolation( - file=file_path, - line=1, - column=1, - text="Modification of generated file forbidden", - rule_id=rule.id, - ) - ) - - except Exception: - # Skip files that can't be read - continue - - return violations - - def _scan_go_imports(self, file_path: str) -> List[Dict[str, Any]]: - imports: List[Dict[str, Any]] = [] - in_block = False - with open(file_path, "r", encoding="utf-8") as f: - for idx, raw in enumerate(f, 1): - line = raw.strip() - if line.startswith("import ("): - in_block = True - continue - if in_block and line.startswith(")"): - in_block = False - continue - if line.startswith("import "): - in_block = False - line = line[len("import ") :].strip() - if ( - in_block - or line.startswith( - ( - '"', - "_", - ".", - ) - ) - or line.split(" ", 1)[0].isidentifier() - ): - import_path = self._extract_go_import_path(line) - if import_path: - column = raw.find(import_path) + 1 - imports.append( - { - "import_path": import_path, - "line": idx, - "column": max(1, column), - } - ) - return imports - - def _extract_go_import_path(self, line: str) -> Optional[str]: - import re - - match = re.search(r"\"([^\"]+)\"", line) - if not match: - return None - return match.group(1) - - def _scan_ts_imports(self, file_path: str) -> List[Dict[str, Any]]: - import re - - imports: List[Dict[str, Any]] = [] - patterns = [ - re.compile(r"import\s+[^;]*?\s+from\s+['\"]([^'\"]+)['\"]"), - re.compile(r"import\s+['\"]([^'\"]+)['\"]"), - re.compile(r"export\s+[^;]*?\s+from\s+['\"]([^'\"]+)['\"]"), - ] - with open(file_path, "r", encoding="utf-8") as f: - for idx, raw in enumerate(f, 1): - for pattern in patterns: - match = pattern.search(raw) - if match: - module = match.group(1) - column = raw.find(module) + 1 - imports.append( - { - "import_path": module, - "line": idx, - "column": max(1, column), - } - ) - return imports - - def _build_ts_dependency_graph(self, workspace_root: str) -> Dict[str, Any]: - graph: Dict[str, List[str]] = {} - module_deps: Dict[str, List[str]] = {} - for root, _, files in os.walk(workspace_root): - for name in files: - if not name.endswith((".ts", ".tsx", ".js", ".jsx")): - continue - file_path = os.path.join(root, name) - imports = self._scan_ts_imports(file_path) - rel_deps = [] - ext_deps = [] - for imp in imports: - module = imp["import_path"] - if module.startswith("."): - target = self._resolve_ts_relative(file_path, module) - if target: - rel_deps.append(target) - else: - ext_deps.append(module) - graph[file_path] = rel_deps - module_deps[file_path] = ext_deps - return {"graph": graph, "modules": module_deps} - - def _resolve_ts_relative(self, source_file: str, module: str) -> Optional[str]: - base = Path(source_file).parent / module - candidates = [ - base, - base.with_suffix(".ts"), - base.with_suffix(".tsx"), - base.with_suffix(".js"), - base.with_suffix(".jsx"), - base / "index.ts", - base / "index.tsx", - base / "index.js", - base / "index.jsx", - ] - for cand in candidates: - if cand.exists(): - return str(cand.resolve()) - return None - - def _ts_transitive_deps(self, start_file: str, graph: Dict[str, Any]) -> List[str]: - visited = set() - stack = [start_file] - modules = set() - while stack: - current = stack.pop() - if current in visited: - continue - visited.add(current) - modules.update(graph["modules"].get(current, [])) - for neighbor in graph["graph"].get(current, []): - if neighbor not in visited: - stack.append(neighbor) - return sorted(modules) - - async def _go_package_index(self, workspace_root: str) -> Dict[str, Any]: - cmd = ["go", "list", "-deps", "-json", "./..."] - env = os.environ.copy() - env["GOWORK"] = "auto" - try: - result = subprocess.run( - cmd, - cwd=workspace_root, - capture_output=True, - text=True, - check=True, - env=env, - ) - except Exception: - return {"file_to_pkg": {}, "pkg_deps": {}} - - decoder = json.JSONDecoder() - data = result.stdout - idx = 0 - packages = [] - while idx < len(data): - while idx < len(data) and data[idx].isspace(): - idx += 1 - if idx >= len(data): - break - obj, idx = decoder.raw_decode(data, idx) - packages.append(obj) - - file_to_pkg: Dict[str, str] = {} - pkg_deps: Dict[str, set] = {} - for pkg in packages: - import_path = pkg.get("ImportPath", "") - deps = set(pkg.get("Deps", []) or []) - pkg_deps[import_path] = deps - for name in ( - pkg.get("GoFiles", []) - + pkg.get("CgoFiles", []) - + pkg.get("CompiledGoFiles", []) - ): - file_to_pkg[str(Path(pkg.get("Dir", "")) / name)] = import_path - return {"file_to_pkg": file_to_pkg, "pkg_deps": pkg_deps} - - -# Global instance -tools = HanzoTools() - - -# CLI interface for testing -if __name__ == "__main__": - import argparse - import asyncio - import sys - - async def main(): - parser = argparse.ArgumentParser(description="Hanzo MCP Tools") - parser.add_argument( - "tool", choices=["edit", "fmt", "test", "build", "lint", "guard"] - ) - parser.add_argument("target", help="Target specification") - parser.add_argument("--language", default="auto") - parser.add_argument("--backend", default="auto") - parser.add_argument("--dry-run", action="store_true") - parser.add_argument("--op", help="Operation for edit tool") - parser.add_argument("--file", help="File for edit operations") - parser.add_argument("--new-name", help="New name for rename") - parser.add_argument("--local-prefix", help="Local prefix for Go imports") - - args = parser.parse_args() - - target_spec = TargetSpec( - target=args.target, - language=args.language, - backend=args.backend, - dry_run=args.dry_run, - ) - - if args.tool == "edit": - if not args.op: - print("--op required for edit tool") - sys.exit(1) - - edit_args = EditArgs(op=args.op) - if args.file: - edit_args.file = args.file - if args.new_name: - edit_args.new_name = args.new_name - - result = await tools.edit(target_spec, edit_args) - - elif args.tool == "fmt": - fmt_args = FmtArgs() - if args.local_prefix: - fmt_args.opts["local_prefix"] = args.local_prefix - - result = await tools.fmt(target_spec, fmt_args) - - elif args.tool == "test": - result = await tools.test(target_spec, TestArgs()) - - elif args.tool == "build": - result = await tools.build(target_spec, BuildArgs()) - - elif args.tool == "lint": - result = await tools.lint(target_spec, LintArgs()) - - elif args.tool == "guard": - # Example guard rules - example_rules = [ - GuardRule( - id="no-node-in-sdk", - type="import", - glob="sdk/**/*.py", - forbid_import_prefix="node", - ) - ] - result = await tools.guard(target_spec, GuardArgs(rules=example_rules)) - - print(json.dumps(asdict(result), indent=2)) - - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/hanzo_mcp/mcp_server.py b/pkg/hanzo-mcp/hanzo_mcp/mcp_server.py deleted file mode 100644 index 4df9d5c14..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/mcp_server.py +++ /dev/null @@ -1,695 +0,0 @@ -#!/usr/bin/env python3 -""" -Enhanced Hanzo MCP Server -======================== - -MCP server implementation that exposes the 6 universal tools via Model Context Protocol. -Now includes the new unified development tools (edit, fmt, test, build, lint, guard) -plus the existing tool suite. -""" - -import asyncio -from typing import List - -from mcp.server import NotificationOptions, Server -from mcp.server.models import InitializationOptions -from mcp.types import ( - TextContent, - Tool, -) - -from .tools.dev_tools_mcp import dev_tools_server -from .unified_backend import TargetSpec, ToolResult, backend - - -class HanzoMCPServer: - """Enhanced MCP server with 6 universal tools""" - - def __init__(self): - self.server = Server("hanzo-mcp") - self.dev_tools_server = dev_tools_server # New orthogonal dev tools - self.setup_handlers() - - def setup_handlers(self): - """Set up MCP server handlers""" - - @self.server.list_tools() - async def handle_list_tools() -> List[Tool]: - """List available tools""" - return [ - Tool( - name="edit", - description="Semantic refactors via LSP across languages", - inputSchema={ - "type": "object", - "properties": { - "target": { - "type": "string", - "description": "Target: file:, dir:, pkg:, ws, or changed", - }, - "op": { - "type": "string", - "enum": [ - "rename", - "code_action", - "organize_imports", - "apply_workspace_edit", - ], - "description": "Operation to perform", - }, - "language": { - "type": "string", - "default": "auto", - "description": "Language override (auto, go, ts, py, rs, cc, sol)", - }, - "backend": { - "type": "string", - "default": "auto", - "description": "Backend override", - }, - "root": { - "type": "string", - "description": "Workspace root override", - }, - "env": { - "type": "object", - "additionalProperties": {"type": "string"}, - "description": "Environment variables", - }, - "file": { - "type": "string", - "description": "File path for rename/code_action operations", - }, - "pos": { - "type": "object", - "properties": { - "line": {"type": "integer"}, - "character": {"type": "integer"}, - }, - "description": "Position for rename/code_action", - }, - "range": { - "type": "object", - "properties": { - "start": { - "type": "object", - "properties": { - "line": {"type": "integer"}, - "character": {"type": "integer"}, - }, - }, - "end": { - "type": "object", - "properties": { - "line": {"type": "integer"}, - "character": {"type": "integer"}, - }, - }, - }, - "description": "Range for code actions", - }, - "new_name": { - "type": "string", - "description": "New name for rename operation", - }, - "only": { - "type": "array", - "items": {"type": "string"}, - "description": "Code action kinds filter", - }, - "apply": { - "type": "boolean", - "default": True, - "description": "Apply changes (default true)", - }, - "workspace_edit": { - "type": "object", - "description": "WorkspaceEdit payload for apply_workspace_edit", - }, - "dry_run": { - "type": "boolean", - "default": False, - "description": "Preview mode - no file writes", - }, - }, - "required": ["target", "op"], - "additionalProperties": False, - }, - ), - Tool( - name="fmt", - description="Formatting + import normalization", - inputSchema={ - "type": "object", - "properties": { - "target": { - "type": "string", - "description": "Target: file:, dir:, pkg:, ws, or changed", - }, - "language": { - "type": "string", - "default": "auto", - "description": "Language override", - }, - "backend": { - "type": "string", - "default": "auto", - "description": "Backend override", - }, - "root": { - "type": "string", - "description": "Workspace root override", - }, - "env": { - "type": "object", - "additionalProperties": {"type": "string"}, - "description": "Environment variables", - }, - "opts": { - "type": "object", - "properties": { - "local_prefix": { - "type": "string", - "description": "Go import grouping prefix (e.g. github.com/luxfi)", - } - }, - "description": "Tool-specific options", - }, - "dry_run": { - "type": "boolean", - "default": False, - "description": "Preview mode", - }, - }, - "required": ["target"], - "additionalProperties": False, - }, - ), - Tool( - name="test", - description="Run tests narrowly by default", - inputSchema={ - "type": "object", - "properties": { - "target": { - "type": "string", - "description": "Target: file:, dir:, pkg:, ws, or changed", - }, - "language": { - "type": "string", - "default": "auto", - "description": "Language override", - }, - "backend": { - "type": "string", - "default": "auto", - "description": "Backend override", - }, - "root": { - "type": "string", - "description": "Workspace root override", - }, - "env": { - "type": "object", - "additionalProperties": {"type": "string"}, - "description": "Environment variables", - }, - "opts": { - "type": "object", - "properties": { - "run_filter": { - "type": "string", - "description": "Test filter pattern", - }, - "count": { - "type": "integer", - "description": "Number of times to run each test", - }, - "race": { - "type": "boolean", - "description": "Enable race detection (Go)", - }, - "watch": { - "type": "boolean", - "default": False, - "description": "Watch mode", - }, - }, - "description": "Test-specific options", - }, - "dry_run": { - "type": "boolean", - "default": False, - "description": "Preview mode", - }, - }, - "required": ["target"], - "additionalProperties": False, - }, - ), - Tool( - name="build", - description="Compile/build artifacts narrowly by default", - inputSchema={ - "type": "object", - "properties": { - "target": { - "type": "string", - "description": "Target: file:, dir:, pkg:, ws, or changed", - }, - "language": { - "type": "string", - "default": "auto", - "description": "Language override", - }, - "backend": { - "type": "string", - "default": "auto", - "description": "Backend override", - }, - "root": { - "type": "string", - "description": "Workspace root override", - }, - "env": { - "type": "object", - "additionalProperties": {"type": "string"}, - "description": "Environment variables", - }, - "opts": { - "type": "object", - "properties": { - "release": { - "type": "boolean", - "default": False, - "description": "Release build", - }, - "features": { - "type": "array", - "items": {"type": "string"}, - "description": "Rust features to enable", - }, - }, - "description": "Build-specific options", - }, - "dry_run": { - "type": "boolean", - "default": False, - "description": "Preview mode", - }, - }, - "required": ["target"], - "additionalProperties": False, - }, - ), - Tool( - name="lint", - description="Lint/typecheck in one place", - inputSchema={ - "type": "object", - "properties": { - "target": { - "type": "string", - "description": "Target: file:, dir:, pkg:, ws, or changed", - }, - "language": { - "type": "string", - "default": "auto", - "description": "Language override", - }, - "backend": { - "type": "string", - "default": "auto", - "description": "Backend override", - }, - "root": { - "type": "string", - "description": "Workspace root override", - }, - "env": { - "type": "object", - "additionalProperties": {"type": "string"}, - "description": "Environment variables", - }, - "opts": { - "type": "object", - "properties": { - "fix": { - "type": "boolean", - "default": False, - "description": "Auto-fix issues where possible", - } - }, - "description": "Lint-specific options", - }, - "dry_run": { - "type": "boolean", - "default": False, - "description": "Preview mode", - }, - }, - "required": ["target"], - "additionalProperties": False, - }, - ), - Tool( - name="guard", - description="Repo invariants (boundaries, forbidden imports/strings)", - inputSchema={ - "type": "object", - "properties": { - "target": { - "type": "string", - "description": "Target: file:, dir:, pkg:, ws, or changed", - }, - "language": { - "type": "string", - "default": "auto", - "description": "Language override", - }, - "backend": { - "type": "string", - "default": "auto", - "description": "Backend override", - }, - "root": { - "type": "string", - "description": "Workspace root override", - }, - "env": { - "type": "object", - "additionalProperties": {"type": "string"}, - "description": "Environment variables", - }, - "rules": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": {"type": "string"}, - "type": { - "type": "string", - "enum": ["regex", "import", "generated"], - }, - "glob": {"type": "string"}, - "pattern": {"type": "string"}, - "forbid_import_prefix": {"type": "string"}, - "forbid_writes": {"type": "boolean"}, - }, - "required": ["id", "type", "glob"], - }, - "description": "Guard rules to check", - }, - "dry_run": { - "type": "boolean", - "default": False, - "description": "Preview mode", - }, - }, - "required": ["target", "rules"], - "additionalProperties": False, - }, - ), - Tool( - name="search_codebase", - description="Search for symbols, files, or content across codebase", - inputSchema={ - "type": "object", - "properties": { - "query": { - "type": "string", - "description": "Search query (symbol name, file pattern, or content)", - }, - "type": { - "type": "string", - "enum": ["symbols", "files", "content"], - "default": "symbols", - "description": "Search type", - }, - "language": { - "type": "string", - "description": "Filter by language", - }, - "limit": { - "type": "integer", - "default": 50, - "description": "Maximum results", - }, - }, - "required": ["query"], - }, - ), - Tool( - name="get_session_history", - description="Get recent MCP tool usage history", - inputSchema={ - "type": "object", - "properties": { - "limit": { - "type": "integer", - "default": 10, - "description": "Number of recent sessions", - } - }, - }, - ), - ] - - @self.server.call_tool() - async def handle_call_tool(name: str, arguments: dict) -> List[TextContent]: - """Handle tool calls""" - try: - if name == "edit": - target_spec = TargetSpec( - target=arguments["target"], - language=arguments.get("language", "auto"), - backend=arguments.get("backend", "auto"), - root=arguments.get("root"), - env=arguments.get("env", {}), - dry_run=arguments.get("dry_run", False), - ) - - result = await backend.edit( - target_spec, - op=arguments["op"], - file=arguments.get("file"), - pos=arguments.get("pos"), - range=arguments.get("range"), - new_name=arguments.get("new_name"), - only=arguments.get("only", []), - ) - - backend.session_manager.log_tool_execution( - "edit", arguments, result - ) - - return [ - TextContent(type="text", text=self._format_tool_result(result)) - ] - - elif name == "fmt": - target_spec = TargetSpec( - target=arguments["target"], - language=arguments.get("language", "auto"), - backend=arguments.get("backend", "auto"), - root=arguments.get("root"), - env=arguments.get("env", {}), - dry_run=arguments.get("dry_run", False), - ) - - result = await backend.fmt( - target_spec, opts=arguments.get("opts", {}) - ) - - backend.session_manager.log_tool_execution("fmt", arguments, result) - - return [ - TextContent(type="text", text=self._format_tool_result(result)) - ] - - elif name == "test": - target_spec = TargetSpec( - target=arguments["target"], - language=arguments.get("language", "auto"), - backend=arguments.get("backend", "auto"), - root=arguments.get("root"), - env=arguments.get("env", {}), - dry_run=arguments.get("dry_run", False), - ) - - result = await backend.test( - target_spec, opts=arguments.get("opts", {}) - ) - - backend.session_manager.log_tool_execution( - "test", arguments, result - ) - - return [ - TextContent(type="text", text=self._format_tool_result(result)) - ] - - elif name == "build": - target_spec = TargetSpec( - target=arguments["target"], - language=arguments.get("language", "auto"), - backend=arguments.get("backend", "auto"), - root=arguments.get("root"), - env=arguments.get("env", {}), - dry_run=arguments.get("dry_run", False), - ) - - result = await backend.build( - target_spec, opts=arguments.get("opts", {}) - ) - - backend.session_manager.log_tool_execution( - "build", arguments, result - ) - - return [ - TextContent(type="text", text=self._format_tool_result(result)) - ] - - elif name == "lint": - target_spec = TargetSpec( - target=arguments["target"], - language=arguments.get("language", "auto"), - backend=arguments.get("backend", "auto"), - root=arguments.get("root"), - env=arguments.get("env", {}), - dry_run=arguments.get("dry_run", False), - ) - - result = await backend.lint( - target_spec, opts=arguments.get("opts", {}) - ) - - backend.session_manager.log_tool_execution( - "lint", arguments, result - ) - - return [ - TextContent(type="text", text=self._format_tool_result(result)) - ] - - elif name == "guard": - target_spec = TargetSpec( - target=arguments["target"], - language=arguments.get("language", "auto"), - backend=arguments.get("backend", "auto"), - root=arguments.get("root"), - env=arguments.get("env", {}), - dry_run=arguments.get("dry_run", False), - ) - - result = await backend.guard(target_spec, rules=arguments["rules"]) - - backend.session_manager.log_tool_execution( - "guard", arguments, result - ) - - return [ - TextContent(type="text", text=self._format_tool_result(result)) - ] - - elif name == "search_codebase": - query = arguments["query"] - search_type = arguments.get("type", "symbols") - language = arguments.get("language") - limit = arguments.get("limit", 50) - - if search_type == "symbols": - results = backend.indexer.search_symbols(query, language) - results_text = "\n".join( - [ - f"{r['path']}:{r['line']} - {r['kind']} {r['name']}" - for r in results[:limit] - ] - ) - else: - results_text = ( - f"Search type '{search_type}' not implemented yet" - ) - - return [ - TextContent( - type="text", - text=f"Search results for '{query}':\n{results_text}", - ) - ] - - elif name == "get_session_history": - limit = arguments.get("limit", 10) - sessions = backend.session_manager.get_recent_sessions(limit) - - history_text = "Recent MCP Sessions:\n" - for session in sessions: - history_text += f"\nSession {session['session_id'][:8]}... ({session['start_time']})\n" - history_text += f" Tools: {', '.join(session['tools_used'])} ({session['tool_count']} calls)\n" - - return [TextContent(type="text", text=history_text)] - - else: - return [TextContent(type="text", text=f"Unknown tool: {name}")] - - except Exception as e: - return [ - TextContent(type="text", text=f"Error executing {name}: {str(e)}") - ] - - def _format_tool_result(self, result: ToolResult) -> str: - """Format tool result for display""" - status = "โœ… SUCCESS" if result.ok else "โŒ FAILED" - - output = f"{status} ({result.execution_time:.2f}s)\n" - output += f"Root: {result.root}\n" - output += f"Language: {result.language_used}\n" - output += f"Backend: {result.backend_used}\n" - - if result.scope_resolved: - output += f"Scope: {len(result.scope_resolved)} files\n" - - if result.touched_files: - output += f"Modified: {len(result.touched_files)} files\n" - for f in result.touched_files[:5]: # Show first 5 - output += f" - {f}\n" - if len(result.touched_files) > 5: - output += f" ... and {len(result.touched_files) - 5} more\n" - - if result.stdout: - output += f"\nOutput:\n{result.stdout}\n" - - if result.stderr: - output += f"\nErrors:\n{result.stderr}\n" - - if result.errors: - output += "\nIssues:\n" - for error in result.errors: - output += f" - {error}\n" - - return output - - async def run(self, transport_type: str = "stdio"): - """Run the MCP server""" - if transport_type == "stdio": - from mcp.server.stdio import stdio_server - - async with stdio_server() as (read_stream, write_stream): - await self.server.run( - read_stream, - write_stream, - InitializationOptions( - server_name="hanzo-mcp", - server_version="1.0.0", - capabilities=self.server.get_capabilities( - notification_options=NotificationOptions(), - experimental_capabilities={}, - ), - ), - ) - - -if __name__ == "__main__": - server = HanzoMCPServer() - asyncio.run(server.run()) diff --git a/pkg/hanzo-mcp/hanzo_mcp/memory_service.py b/pkg/hanzo-mcp/hanzo_mcp/memory_service.py deleted file mode 100644 index 23d605bd9..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/memory_service.py +++ /dev/null @@ -1,271 +0,0 @@ -"""Memory service with plugin support. - -Provides namespace-scoped, key-addressable memory with tags, TTL, -and full TypeScript parity for the blue-red agent coordination protocol. -""" - -from pathlib import Path -from typing import Any, Dict, List, Optional - -from .backends.sqlite_plugin import SQLiteBackendPlugin -from .plugin_interface import Capability, PluginRegistry - - -class PluginMemoryService: - """Memory service that uses plugins for backend operations. - - Supports: - - namespace: isolate memories into named channels (default: "default") - - key: address individual memories by name for exact retrieval - - tags: categorize and filter memories - - ttl: auto-expire memories after an ISO date - - append: concatenate content to an existing keyed memory - """ - - def __init__(self, config_path: Optional[Path] = None): - """Initialize the service with configuration.""" - self.config_path = config_path - self.registry = PluginRegistry() - self._initialized = False - - # Register available plugins - self._register_available_plugins() - - def _register_available_plugins(self): - """Register all available backend plugins.""" - self.registry.register_plugin(SQLiteBackendPlugin()) - - async def initialize(self, enabled_backends: List[str] = None): - """Initialize the service and load configured plugins.""" - if self._initialized: - return - - if enabled_backends is None: - enabled_backends = ["sqlite"] - - for backend_name in enabled_backends: - if backend_name in self.registry.get_available_plugins(): - self.registry.enable_plugin(backend_name) - else: - print(f"Warning: Backend '{backend_name}' not available, skipping.") - - await self.registry.initialize_all_active() - self._initialized = True - - async def shutdown(self): - """Shutdown the service and all active plugins.""" - await self.registry.shutdown_all_active() - self._initialized = False - - def _get_plugin(self): - """Get the first active plugin, initializing if needed.""" - active = self.registry.get_active_plugins() - if not active: - raise RuntimeError("No active plugins available") - return active[0] - - async def _ensure_init(self): - if not self._initialized: - await self.initialize() - - # ------------------------------------------------------------------ # - # Core CRUD โ€” backward compatible + new params - # ------------------------------------------------------------------ # - - async def store_memory( - self, - content: str, - metadata: Dict[str, Any], - user_id: str = "default", - project_id: str = "default", - namespace: str = "default", - key: Optional[str] = None, - tags: Optional[List[str]] = None, - ttl: Optional[str] = None, - append: bool = False, - ) -> str: - """Store a memory using active plugins. - - Args: - content: The memory content. - metadata: Arbitrary metadata dict. - user_id: Owner user ID. - project_id: Project scope. - namespace: Logical namespace (e.g. "blue-red"). - key: Optional unique key for retrieval. - tags: Optional list of tags. - ttl: Optional ISO date string; memory expires after this time. - append: If True and key exists, concatenate content. - - Returns: - The memory_id of the stored entry. - """ - await self._ensure_init() - plugin = self._get_plugin() - return await plugin.store_memory( - content=content, - metadata=metadata, - user_id=user_id, - project_id=project_id, - namespace=namespace, - key=key, - tags=tags, - ttl=ttl, - append=append, - ) - - async def retrieve_memory( - self, - query: str, - user_id: str = "default", - project_id: str = "default", - limit: int = 10, - namespace: Optional[str] = None, - metadata_filter: Optional[Dict[str, Any]] = None, - ) -> List[Dict[str, Any]]: - """Retrieve memories using active plugins.""" - await self._ensure_init() - plugin = self._get_plugin() - return await plugin.retrieve_memory( - query=query, - user_id=user_id, - project_id=project_id, - limit=limit, - namespace=namespace, - metadata_filter=metadata_filter, - ) - - async def search_memory( - self, - query: str, - user_id: str = "default", - project_id: str = "default", - limit: int = 10, - namespace: Optional[str] = None, - metadata_filter: Optional[Dict[str, Any]] = None, - ) -> List[Dict[str, Any]]: - """Search memories using active plugins.""" - await self._ensure_init() - plugin = self._get_plugin() - return await plugin.search_memory( - query=query, - user_id=user_id, - project_id=project_id, - limit=limit, - namespace=namespace, - metadata_filter=metadata_filter, - ) - - async def delete_memory( - self, - memory_id: str, - user_id: str = "default", - project_id: str = "default", - ) -> bool: - """Delete a memory using active plugins.""" - await self._ensure_init() - plugin = self._get_plugin() - return await plugin.delete_memory( - memory_id=memory_id, - user_id=user_id, - project_id=project_id, - ) - - # ------------------------------------------------------------------ # - # New operations for TypeScript parity - # ------------------------------------------------------------------ # - - async def get_by_key(self, key: str, namespace: str = "default"): - """Get memory by exact key or wildcard within namespace. - - Exact key returns a single dict or None. - Wildcard key (contains '*') returns a list. - """ - await self._ensure_init() - plugin = self._get_plugin() - return await plugin.get_by_key(key=key, namespace=namespace) - - async def list_memories( - self, - namespace: Optional[str] = None, - tag: Optional[str] = None, - limit: int = 50, - ) -> List[Dict[str, Any]]: - """List all memories with optional namespace/tag filters.""" - await self._ensure_init() - plugin = self._get_plugin() - return await plugin.list_memories(namespace=namespace, tag=tag, limit=limit) - - async def stats(self) -> Dict[str, Any]: - """Return count, namespaces, size info.""" - await self._ensure_init() - plugin = self._get_plugin() - return await plugin.stats() - - async def clear(self, namespace: Optional[str] = None) -> int: - """Clear all or namespace-specific memories. Returns count deleted.""" - await self._ensure_init() - plugin = self._get_plugin() - return await plugin.clear(namespace=namespace) - - async def tag_memory(self, memory_id: str, tag: str) -> bool: - """Add a tag to a memory.""" - await self._ensure_init() - plugin = self._get_plugin() - return await plugin.tag_memory(memory_id=memory_id, tag=tag) - - async def untag_memory(self, memory_id: str, tag: str) -> bool: - """Remove a tag from a memory.""" - await self._ensure_init() - plugin = self._get_plugin() - return await plugin.untag_memory(memory_id=memory_id, tag=tag) - - async def namespaces(self) -> Dict[str, int]: - """List all namespaces with counts.""" - await self._ensure_init() - plugin = self._get_plugin() - return await plugin.namespaces() - - async def history( - self, key: str, namespace: str = "default" - ) -> List[Dict[str, Any]]: - """Show all versions of a key.""" - await self._ensure_init() - plugin = self._get_plugin() - return await plugin.history(key=key, namespace=namespace) - - async def export_memories( - self, namespace: Optional[str] = None - ) -> List[Dict[str, Any]]: - """Export memories as a list of dicts.""" - await self._ensure_init() - plugin = self._get_plugin() - return await plugin.export_memories(namespace=namespace) - - async def import_memories(self, data: List[Dict[str, Any]]) -> int: - """Import memories from exported data. Returns count imported.""" - await self._ensure_init() - plugin = self._get_plugin() - return await plugin.import_memories(data=data) - - # ------------------------------------------------------------------ # - # Backend management (unchanged) - # ------------------------------------------------------------------ # - - def get_available_backends(self) -> List[str]: - return self.registry.get_available_plugins() - - def get_active_backends(self) -> List[str]: - return [plugin.name for plugin in self.registry.get_active_plugins()] - - def get_backend_capabilities(self, backend_name: str) -> List[Capability]: - return self.registry.get_plugin_capabilities(backend_name) - - def has_capability(self, capability: Capability) -> List[str]: - return self.registry.has_capability(capability) - - def enable_backend(self, backend_name: str) -> bool: - return self.registry.enable_plugin(backend_name) - - def disable_backend(self, backend_name: str) -> bool: - return self.registry.disable_plugin(backend_name) diff --git a/pkg/hanzo-mcp/hanzo_mcp/plugin_interface.py b/pkg/hanzo-mcp/hanzo_mcp/plugin_interface.py deleted file mode 100644 index 787adb5de..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/plugin_interface.py +++ /dev/null @@ -1,154 +0,0 @@ -"""Plugin interface and registry for memory backends.""" - -from enum import Enum -from typing import Any, Dict, List, Optional, Protocol, runtime_checkable - - -class Capability(str, Enum): - """Memory backend capabilities.""" - - VECTOR_SEARCH = "vector_search" - FULL_TEXT_SEARCH = "full_text_search" - STRUCTURED_QUERY = "structured_query" - PERSISTENCE = "persistence" - EMBEDDINGS = "embeddings" - GRAPH_QUERIES = "graph_queries" - TIME_SERIES = "time_series" - MARKDOWN_IMPORT = "markdown_import" - - -@runtime_checkable -class MemoryBackendPlugin(Protocol): - """Interface for memory backend plugins.""" - - @property - def name(self) -> str: - """Unique name of the backend.""" - ... - - @property - def capabilities(self) -> List[Capability]: - """List of capabilities provided by this backend.""" - ... - - async def initialize(self) -> None: - """Initialize the backend.""" - ... - - async def shutdown(self) -> None: - """Shutdown the backend.""" - ... - - async def store_memory( - self, - content: str, - metadata: Dict[str, Any], - user_id: str = "default", - project_id: str = "default", - ) -> str: - """Store a memory and return its ID.""" - ... - - async def retrieve_memory( - self, - query: str, - user_id: str = "default", - project_id: str = "default", - limit: int = 10, - ) -> List[Dict[str, Any]]: - """Retrieve memories based on query.""" - ... - - async def search_memory( - self, - query: str, - user_id: str = "default", - project_id: str = "default", - limit: int = 10, - ) -> List[Dict[str, Any]]: - """Search memories based on query with scoring.""" - ... - - async def delete_memory( - self, memory_id: str, user_id: str = "default", project_id: str = "default" - ) -> bool: - """Delete a memory by ID.""" - ... - - -class PluginRegistry: - """Registry for memory backend plugins.""" - - def __init__(self) -> None: - self._plugins: Dict[str, MemoryBackendPlugin] = {} - self._active_plugins: List[str] = [] - self._initialized: bool = False - - def register_plugin(self, plugin: MemoryBackendPlugin) -> None: - """Register a new plugin.""" - self._plugins[plugin.name] = plugin - - def unregister_plugin(self, name: str) -> bool: - """Unregister a plugin.""" - if name in self._plugins: - del self._plugins[name] - if name in self._active_plugins: - self._active_plugins.remove(name) - return True - return False - - def enable_plugin(self, name: str) -> bool: - """Enable a registered plugin.""" - if name in self._plugins and name not in self._active_plugins: - self._active_plugins.append(name) - return True - return False - - def disable_plugin(self, name: str) -> bool: - """Disable an active plugin.""" - if name in self._active_plugins: - self._active_plugins.remove(name) - return True - return False - - def get_plugin(self, name: str) -> Optional[MemoryBackendPlugin]: - """Get a specific plugin.""" - return self._plugins.get(name) - - def get_active_plugins(self) -> List[MemoryBackendPlugin]: - """Get all active plugins.""" - return [self._plugins[name] for name in self._active_plugins] - - def get_available_plugins(self) -> List[str]: - """Get names of all registered plugins.""" - return list(self._plugins.keys()) - - def get_plugin_capabilities(self, name: str) -> List[Capability]: - """Get capabilities of a specific plugin.""" - plugin = self.get_plugin(name) - return plugin.capabilities if plugin else [] - - def has_capability(self, capability: Capability) -> List[str]: - """Get all plugins that support a specific capability.""" - result = [] - for name, plugin in self._plugins.items(): - if capability in plugin.capabilities: - result.append(name) - return result - - async def initialize_all_active(self) -> None: - """Initialize all active plugins.""" - if not self._initialized: - for plugin in self.get_active_plugins(): - await plugin.initialize() - self._initialized = True - - async def shutdown_all_active(self) -> None: - """Shutdown all active plugins.""" - for plugin in self.get_active_plugins(): - await plugin.shutdown() - self._initialized = False - - -# Global registry instance -registry = PluginRegistry() diff --git a/pkg/hanzo-mcp/hanzo_mcp/prompts/__init__.py b/pkg/hanzo-mcp/hanzo_mcp/prompts/__init__.py deleted file mode 100644 index 9d184226f..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/prompts/__init__.py +++ /dev/null @@ -1,251 +0,0 @@ -import os - -from hanzoai.session import CompactionConfig, CompactionResult, Session, compact_session -from mcp.server import FastMCP - -from hanzo_mcp.prompts.compact_conversation import ( - COMPACT_CONVERSATION_PROMPT, - compact_or_prompt, -) -from hanzo_mcp.prompts.create_release import CREATE_RELEASE_PROMPT -from hanzo_mcp.prompts.enhanced_prompts import ( - CONFIGURATION_GUIDE_PROMPT, - DATABASE_TOOLS_HELP_PROMPT, - LSP_TOOLS_HELP_PROMPT, - MEMORY_VECTOR_HELP_PROMPT, - NETWORK_AGENT_GUIDE_PROMPT, - PAGINATION_GUIDE_PROMPT, - PERFORMANCE_TIPS_PROMPT, - QUICK_START_PROMPT, - SECURITY_BEST_PRACTICES_PROMPT, - TROUBLESHOOTING_GUIDE_PROMPT, -) -from hanzo_mcp.prompts.project_system import PROJECT_SYSTEM_PROMPT -from hanzo_mcp.prompts.project_todo_reminder import ( - PROJECT_TODO_EMPTY_REMINDER, - get_project_todo_reminder, -) -from hanzo_mcp.prompts.tool_explorer import ( - AGENT_TOOLS_HELP, - BATCH_TOOL_EXAMPLES, - FILESYSTEM_TOOLS_HELP, - SHELL_TOOLS_HELP, - TOOL_EXPLORER_PROMPT, -) -from hanzo_mcp.prompts.utils import ( - get_directory_structure, - get_git_info, - get_os_info, -) - -CONTINUE_FROM_LAST_SESSION_PROMPT = """ -This is a reminder that your todo list is currently empty. DO NOT mention this to the user explicitly because they are already aware. If you are working on tasks that would benefit from a todo list please use the TodoWrite tool to create one. If not, please feel free to ignore. Again do not mention this message to the user. - -""" - - -def create_project_system_prompt(project_path: str): - """Factory function to create a project system prompt function.""" - - def project_system_prompt() -> str: - """ - Summarize the conversation so far for a specific project. - """ - working_directory = project_path - is_git_repo = os.path.isdir(os.path.join(working_directory, ".git")) - platform, _, os_version = get_os_info() - - # Get directory structure - directory_structure = get_directory_structure( - working_directory, max_depth=3, include_filtered=False - ) - - # Get git information - git_info = get_git_info(working_directory) - current_branch = git_info.get("current_branch", "") - main_branch = git_info.get("main_branch", "") - git_status = git_info.get("git_status", "") - recent_commits = git_info.get("recent_commits", "") - - return PROJECT_SYSTEM_PROMPT.format( - working_directory=working_directory, - is_git_repo=is_git_repo, - platform=platform, - os_version=os_version, - directory_structure=directory_structure, - current_branch=current_branch, - main_branch=main_branch, - git_status=git_status, - recent_commits=recent_commits, - ) - - return project_system_prompt - - -def register_all_prompts( - mcp_server: FastMCP, projects: list[str] | None = None -) -> None: - @mcp_server.prompt(name="Compact current conversation") - def compact() -> str: - """ - Summarize the conversation so far. - """ - return COMPACT_CONVERSATION_PROMPT - - @mcp_server.prompt(name="Create a new release") - def create_release() -> str: - """ - Create a new release for my project. - """ - return CREATE_RELEASE_PROMPT - - @mcp_server.prompt(name="Continue todo by session id") - def continue_todo_by_session_id(session_id: str) -> str: - """ - Continue from the last todo list for the current session. - """ - return get_project_todo_reminder(session_id) - - @mcp_server.prompt(name="Continue latest todo") - def continue_latest_todo() -> str: - """ - Continue from the last todo list for the current session. - """ - return get_project_todo_reminder() - - @mcp_server.prompt(name="System prompt") - def manual_project_system_prompt(project_path: str) -> str: - """ - Detailed system prompt include env,git etc information about the specified project. - """ - return create_project_system_prompt(project_path)() - - @mcp_server.prompt(name="Explore all tools") - def explore_tools() -> str: - """ - Comprehensive guide to all available Hanzo MCP tools and how to use them. - """ - return TOOL_EXPLORER_PROMPT - - @mcp_server.prompt(name="Filesystem tools help") - def filesystem_help() -> str: - """ - Detailed guide for filesystem tools (read, write, edit, search, etc). - """ - return FILESYSTEM_TOOLS_HELP - - @mcp_server.prompt(name="Agent tools help") - def agent_help() -> str: - """ - Guide for using agent tools to delegate complex tasks. - """ - return AGENT_TOOLS_HELP - - @mcp_server.prompt(name="Shell tools help") - def shell_help() -> str: - """ - Guide for shell and command execution tools. - """ - return SHELL_TOOLS_HELP - - @mcp_server.prompt(name="Batch tool examples") - def batch_examples() -> str: - """ - Advanced examples of using the batch tool for parallel operations. - """ - return BATCH_TOOL_EXAMPLES - - # Enhanced prompts for better discoverability - @mcp_server.prompt(name="Quick start guide") - def quick_start() -> str: - """ - Common workflows and recipes for getting started quickly. - """ - return QUICK_START_PROMPT - - @mcp_server.prompt(name="Pagination guide") - def pagination_guide() -> str: - """ - How to use pagination for large result sets. - """ - return PAGINATION_GUIDE_PROMPT - - @mcp_server.prompt(name="Memory and vector tools help") - def memory_vector_help() -> str: - """ - Guide for semantic search and memory tools. - """ - return MEMORY_VECTOR_HELP_PROMPT - - @mcp_server.prompt(name="Database tools help") - def database_help() -> str: - """ - SQL and graph database operations guide. - """ - return DATABASE_TOOLS_HELP_PROMPT - - @mcp_server.prompt(name="LSP tools help") - def lsp_help() -> str: - """ - Language Server Protocol features and code intelligence. - """ - return LSP_TOOLS_HELP_PROMPT - - @mcp_server.prompt(name="Configuration guide") - def config_guide() -> str: - """ - How to configure tools, presets, and settings. - """ - return CONFIGURATION_GUIDE_PROMPT - - @mcp_server.prompt(name="Network agent guide") - def network_guide() -> str: - """ - Distributed AI orchestration with network/swarm tools. - """ - return NETWORK_AGENT_GUIDE_PROMPT - - @mcp_server.prompt(name="Performance tips") - def performance_tips() -> str: - """ - Optimization strategies for better performance. - """ - return PERFORMANCE_TIPS_PROMPT - - @mcp_server.prompt(name="Security best practices") - def security_practices() -> str: - """ - Safe usage patterns and security guidelines. - """ - return SECURITY_BEST_PRACTICES_PROMPT - - @mcp_server.prompt(name="Troubleshooting guide") - def troubleshooting() -> str: - """ - Common issues and their solutions. - """ - return TROUBLESHOOTING_GUIDE_PROMPT - - if projects is None: - return - - for project in projects: - # Register the prompt with the factory function - mcp_server.prompt( - name=f"System prompt for {os.path.basename(project)}", - description=f"Detailed system prompt include env,git etc information about {project}", - )(create_project_system_prompt(project)) - - return - - -__all__ = [ - "register_all_prompts", - "get_project_todo_reminder", - "PROJECT_TODO_EMPTY_REMINDER", - "compact_or_prompt", - "compact_session", - "CompactionConfig", - "CompactionResult", - "Session", -] diff --git a/pkg/hanzo-mcp/hanzo_mcp/prompts/compact_conversation.py b/pkg/hanzo-mcp/hanzo_mcp/prompts/compact_conversation.py deleted file mode 100644 index 824eaa039..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/prompts/compact_conversation.py +++ /dev/null @@ -1,116 +0,0 @@ -from __future__ import annotations - -from hanzoai.session import ( - CompactionConfig, - CompactionResult, - Session, - compact_session, - should_compact, -) - -# Re-export for convenience -__all__ = [ - "COMPACT_CONVERSATION_PROMPT", - "compact_or_prompt", - "compact_session", - "should_compact", - "CompactionConfig", - "CompactionResult", - "Session", -] - - -def compact_or_prompt( - session: Session, - config: CompactionConfig | None = None, -) -> CompactionResult | None: - """Try fast local compaction on a session. - - Returns a CompactionResult if the session was compacted, or None if the - session did not exceed the compaction threshold. This is the cheap, - no-LLM-call path. For richer LLM-driven summarization, use - COMPACT_CONVERSATION_PROMPT as an MCP prompt instead. - """ - cfg = config or CompactionConfig() - if not should_compact(session, cfg): - return None - return compact_session(session, cfg) - - -COMPACT_CONVERSATION_PROMPT = """Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and your previous actions. -This summary should be thorough in capturing technical details, code patterns, and architectural decisions that would be essential for continuing development work without losing context. - -Before providing your final summary, wrap your analysis in tags to organize your thoughts and ensure you've covered all necessary points. In your analysis process: - -1. Chronologically analyze each message and section of the conversation. For each section thoroughly identify: - - The user's explicit requests and intents - - Your approach to addressing the user's requests - - Key decisions, technical concepts and code patterns - - Specific details like file names, full code snippets, function signatures, file edits, etc -2. Double-check for technical accuracy and completeness, addressing each required element thoroughly. - -Your summary should include the following sections: - -1. Primary Request and Intent: Capture all of the user's explicit requests and intents in detail -2. Key Technical Concepts: List all important technical concepts, technologies, and frameworks discussed. -3. Files and Code Sections: Enumerate specific files and code sections examined, modified, or created. Pay special attention to the most recent messages and include full code snippets where applicable and include a summary of why this file read or edit is important. -4. Problem Solving: Document problems solved and any ongoing troubleshooting efforts. -5. Pending Tasks: Outline any pending tasks that you have explicitly been asked to work on. -6. Current Work: Describe in detail precisely what was being worked on immediately before this summary request, paying special attention to the most recent messages from both user and assistant. Include file names and code snippets where applicable. -7. Optional Next Step: List the next step that you will take that is related to the most recent work you were doing. IMPORTANT: ensure that this step is DIRECTLY in line with the user's explicit requests, and the task you were working on immediately before this summary request. If your last task was concluded, then only list next steps if they are explicitly in line with the users request. Do not start on tangential requests without confirming with the user first. - If there is a next step, include direct quotes from the most recent conversation showing exactly what task you were working on and where you left off. This should be verbatim to ensure there's no drift in task interpretation. - -Here's an example of how your output should be structured: - - - -[Your thought process, ensuring all points are covered thoroughly and accurately] - - - -1. Primary Request and Intent: - [Detailed description] - -2. Key Technical Concepts: - - [Concept 1] - - [Concept 2] - - [...] - -3. Files and Code Sections: - - [File Name 1] - - [Summary of why this file is important] - - [Summary of the changes made to this file, if any] - - [Important Code Snippet] - - [File Name 2] - - [Important Code Snippet] - - [...] - -4. Problem Solving: - [Description of solved problems and ongoing troubleshooting] - -5. Pending Tasks: - - [Task 1] - - [Task 2] - - [...] - -6. Current Work: - [Precise description of current work] - -7. Optional Next Step: - [Optional Next step to take] - - - - -Please provide your summary based on the conversation so far, following this structure and ensuring precision and thoroughness in your response. - -There may be additional summarization instructions provided in the included context. If so, remember to follow these instructions when creating the above summary. Examples of instructions include: - -## Compact Instructions -When summarizing the conversation focus on typescript code changes and also remember the mistakes you made and how you fixed them. - - - -# Summary instructions -When you are using compact - please focus on test output and code changes. Include file reads verbatim. -""" diff --git a/pkg/hanzo-mcp/hanzo_mcp/prompts/create_release.py b/pkg/hanzo-mcp/hanzo_mcp/prompts/create_release.py deleted file mode 100644 index fc0b82a51..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/prompts/create_release.py +++ /dev/null @@ -1,38 +0,0 @@ -CREATE_RELEASE_PROMPT = """Help me create a new release for my project. Follow these steps: - -## Initial Analysis -1. Examine the project version files (typically `__init__.py`, `package.json`, `pyproject.toml`, etc.) -2. Review the current CHANGELOG.md format and previous releases -3. Check the release workflow configuration (GitHub Actions, CI/CD pipelines) -4. Review commits since the last release tag: - ```bash - git log ..HEAD --pretty=format:"%h %s%n%b" --name-status - ``` - -## Version Update -1. Identify all files containing version numbers -2. Update version numbers consistently across all files -3. Follow semantic versioning guidelines (MAJOR.MINOR.PATCH) - -## Changelog Creation -1. Add a new section at the top of CHANGELOG.md with the new version and today's date -2. Group changes by type: Added, Changed, Fixed, Removed, etc. -3. Include commit hashes in parentheses for reference -4. Write clear, detailed descriptions for each change -5. Follow established project conventions for changelog format - -## Release Commit and Tag -1. Commit the version and changelog updates: - ```bash - git add - git commit -m "chore: bump version to X.Y.Z" - ``` -2. Create an annotated tag: - ```bash - git tag -a "vX.Y.Z" -m "Release vX.Y.Z" - ``` -3. Push the changes and tag: - ```bash - git push origin main - git push origin vX.Y.Z - ```""" diff --git a/pkg/hanzo-mcp/hanzo_mcp/prompts/enhanced_prompts.py b/pkg/hanzo-mcp/hanzo_mcp/prompts/enhanced_prompts.py deleted file mode 100644 index 2de0206a4..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/prompts/enhanced_prompts.py +++ /dev/null @@ -1,800 +0,0 @@ -"""Enhanced prompts for better discoverability and usability.""" - -QUICK_START_PROMPT = """# Hanzo MCP Quick Start Guide - -## Common Workflows - -### 1. Explore a New Codebase -```python -# Get project overview -tree(path=".", depth=3) - -# Find main entry points -find(pattern="main.*|index.*", path=".") - -# Search for key patterns -search(pattern="TODO|FIXME|BUG", path=".") -``` - -### 2. Multi-Agent Code Analysis -```python -# Run parallel analysis with different agents -batch( - description="Comprehensive code analysis", - invocations=[ - {"tool_name": "agent", "input": {"prompt": "Analyze security vulnerabilities"}}, - {"tool_name": "agent", "input": {"prompt": "Find performance bottlenecks"}}, - {"tool_name": "agent", "input": {"prompt": "Review code quality and suggest improvements"}} - ] -) -``` - -### 3. Refactor Code Across Files -```python -# Find all occurrences -search(pattern="oldFunction", path="./src") - -# Review with critic -critic(analysis="Review the usage of oldFunction and suggest refactoring approach") - -# Make changes -content_replace( - pattern="oldFunction", - replacement="newFunction", - path="./src" -) - -# Run tests -bash("npm test") -``` - -### 4. Track Complex Tasks -```python -# Create todo list -todo_write(todos=[ - {"content": "Analyze current implementation", "status": "pending"}, - {"content": "Design new architecture", "status": "pending"}, - {"content": "Implement changes", "status": "pending"}, - {"content": "Write tests", "status": "pending"}, - {"content": "Update documentation", "status": "pending"} -]) - -# Update as you work -todo(action="update", id="task-1", status="completed") -``` - -## Tips -- Use `batch()` for parallel operations -- Use `think()` for complex reasoning -- Use `critic()` for code review -- Use pagination for large result sets""" - -PAGINATION_GUIDE_PROMPT = """# Pagination Guide - -## How Pagination Works - -Most tools support pagination to handle large result sets efficiently. - -### Basic Pagination Parameters -- `page`: Page number (starts at 1) -- `page_size`: Results per page (default: 50-100) -- `max_results`: Total maximum results - -### Examples - -#### Find Tool with Pagination -```python -# Get first page of Python files -find(pattern="*.py", path="/project", page=1, page_size=10) - -# Response includes: -{ - "results": [...], - "pagination": { - "page": 1, - "page_size": 10, - "total_results": 150, - "total_pages": 15, - "has_next": true, - "has_prev": false - } -} - -# Get next page -find(pattern="*.py", path="/project", page=2, page_size=10) -``` - -#### Search with Limits -```python -# Limit total results -search(pattern="TODO", path="/project", max_results=20) -``` - -### Batch Processing Pages -```python -# Process multiple pages in parallel -batch( - description="Get all Python files", - invocations=[ - {"tool_name": "find", "input": {"pattern": "*.py", "page": 1, "page_size": 50}}, - {"tool_name": "find", "input": {"pattern": "*.py", "page": 2, "page_size": 50}}, - {"tool_name": "find", "input": {"pattern": "*.py", "page": 3, "page_size": 50}} - ] -) -``` - -## Best Practices -1. Start with smaller page sizes for testing -2. Use `max_results` to limit total processing -3. Check `has_next` before requesting next page -4. Use batch for parallel page fetching""" - -MEMORY_VECTOR_HELP_PROMPT = """# Memory & Search Tools Guide - -## Search - -### Unified Search -Fast search using text, AST, and symbol matching. - -```python -# Text and code pattern search -search( - pattern="authentication", - path="/project", - enable_ast=true, - enable_symbol=true -) -``` - -### Memory Tools -Store and retrieve context across sessions. - -```python -# Store knowledge -memory_add( - key="project_architecture", - content="The project uses a microservices architecture with..." -) - -# Retrieve knowledge -memory_get(key="project_architecture") - -# Search memory -memory_search(query="architecture decisions") -``` - -## Use Cases -1. **Semantic Code Search**: Find conceptually similar code -2. **Knowledge Base**: Store project understanding -3. **Context Preservation**: Maintain context across sessions -4. **Pattern Discovery**: Find similar implementations - -## Tips -- Index large projects once, search many times -- Combine vector search with traditional search -- Use memory for important discoveries -- Vector search works best with natural language queries""" - -DATABASE_TOOLS_HELP_PROMPT = """# Database Tools Guide - -## SQL Database Operations - -### Query Execution -```python -# Execute SQL query -sql_query( - query="SELECT * FROM users WHERE created_at > '2024-01-01'", - database="myapp.db" -) - -# Search across tables -sql_search( - pattern="john@example.com", - database="myapp.db" -) - -# Get database statistics -sql_stats(database="myapp.db") -``` - -## Graph Database Operations - -### Managing Graph Data -```python -# Add nodes and edges -graph_add( - node_type="User", - node_id="user_123", - properties={"name": "John", "email": "john@example.com"} -) - -graph_add( - edge_type="FOLLOWS", - from_node="user_123", - to_node="user_456" -) - -# Query graph -graph_query( - query="MATCH (u:User)-[:FOLLOWS]->(f:User) RETURN u, f", - database="social.graph" -) - -# Search graph -graph_search( - pattern="John", - node_type="User" -) - -# Graph statistics -graph_stats(database="social.graph") -``` - -## Best Practices -1. Always sanitize inputs to prevent injection -2. Use transactions for multiple operations -3. Index frequently queried fields -4. Monitor query performance with stats tools""" - -LSP_TOOLS_HELP_PROMPT = """# Language Server Protocol (LSP) Tools Guide - -## Code Intelligence Features - -### Basic Operations -```python -# Go to definition -lsp( - action="definition", - file="/src/main.py", - line=42, - character=15 -) - -# Find all references -lsp( - action="references", - file="/src/main.py", - line=42, - character=15 -) - -# Get hover information -lsp( - action="hover", - file="/src/main.py", - line=42, - character=15 -) -``` - -### Refactoring -```python -# Rename symbol across codebase -lsp( - action="rename", - file="/src/main.py", - line=42, - character=15, - new_name="newFunctionName" -) -``` - -### Diagnostics -```python -# Get errors and warnings -lsp( - action="diagnostics", - file="/src/main.py" -) -``` - -### Code Completion -```python -# Get completions at position -lsp( - action="completion", - file="/src/main.py", - line=42, - character=15 -) -``` - -## Supported Languages -- Python (pylsp) -- TypeScript/JavaScript (typescript-language-server) -- Go (gopls) -- Rust (rust-analyzer) -- Java (jdtls) -- C/C++ (clangd) - -## Tips -- LSP servers are installed automatically -- Use for accurate refactoring -- Combine with search tools for comprehensive analysis""" - -CONFIGURATION_GUIDE_PROMPT = """# Configuration Guide - -## Tool Configuration - -### View Current Configuration -```python -# List all enabled tools -tool_list() - -# Check specific tool status -stats() -``` - -### Enable/Disable Tools -```python -# Enable a tool -tool_enable(name="lsp") - -# Disable a tool -tool_disable(name="sql_query") -``` - -## Configuration Presets - -### Available Presets - -1. **minimal** - Essential tools only - - Basic file operations - - Simple commands - -2. **standard** - Common development tools - - File operations - - Search tools - - Process management - -3. **development** - Full development suite - - All standard tools - - Agent tools - - Package managers - - LSP support - -4. **full** - Everything enabled - - All tools available - - Maximum capabilities - -5. **ai_research** - AI/ML focused - - Agent orchestration - - Vector search - - Memory tools - -### Switching Presets -Configure in your launch command: -```bash -# Use development preset -hanzo-mcp --preset development - -# Or set in config file -~/.config/hanzo/mcp-settings.json -``` - -## Environment Variables -```bash -# Agent configuration -export HANZO_AGENT_MODEL="openai/gpt-4" -export HANZO_API_KEY="your-key" - -# Tool settings -export HANZO_COMMAND_TIMEOUT=300 -export HANZO_ENABLE_AGENT_TOOL=true -``` - -## Project-Specific Config -Create `.hanzo/config.json` in your project: -```json -{ - "enabled_tools": ["lsp", "vector_search"], - "disabled_tools": ["sql_query"], - "agent": { - "model": "claude-3-sonnet", - "max_iterations": 15 - } -} -```""" - -NETWORK_AGENT_GUIDE_PROMPT = """# Network Agent Orchestration Guide - -## Distributed AI Processing - -The `network` tool (also accessible as `swarm` for compatibility) enables distributed AI workloads across multiple agents. - -### Basic Multi-Agent Setup -```python -# Launch parallel agents -network( - task="Analyze this codebase for security, performance, and quality", - agents=["security_expert", "performance_analyst", "code_reviewer"], - mode="parallel" -) -``` - -### Execution Modes - -#### 1. Local Mode (Privacy-First) -```python -network( - task="Process sensitive data", - mode="local", # Uses only local compute - agents=["data_processor", "analyzer"] -) -``` - -#### 2. Distributed Mode -```python -network( - task="Large-scale analysis", - mode="distributed", # Uses network resources - agents=["agent1", "agent2", "agent3"] -) -``` - -#### 3. Hybrid Mode (Default) -```python -network( - task="General processing", - mode="hybrid", # Local first, cloud fallback - agents=["primary", "secondary"] -) -``` - -### Routing Strategies - -#### Sequential Processing -```python -network( - task="Multi-step workflow", - agents=["preprocessor", "analyzer", "reporter"], - routing="sequential" # Each agent processes in order -) -``` - -#### Parallel Processing -```python -network( - task="Independent analyses", - agents=["test_runner", "linter", "security_scan"], - routing="parallel" # All agents work simultaneously -) -``` - -#### Consensus Decision -```python -network( - task="Critical decision", - agents=["expert1", "expert2", "expert3"], - routing="consensus" # Agents must agree -) -``` - -### Claude CLI Integration -```python -# Run multiple Claude instances in parallel -batch( - description="Parallel Claude analysis", - invocations=[ - {"tool_name": "claude_cli", "input": {"prompt": "Review architecture"}}, - {"tool_name": "claude_cli", "input": {"prompt": "Analyze performance"}}, - {"tool_name": "claude_cli", "input": {"prompt": "Check security"}} - ] -) -``` - -## Advanced Features -- **MCP Connections**: Agents communicate via MCP protocol -- **State Sharing**: Agents share context and memory -- **Tool Sharing**: Agents can use each other's tools -- **Recursive Calling**: Agents can spawn sub-agents - -## Best Practices -1. Use local mode for sensitive data -2. Use parallel routing for independent tasks -3. Use consensus for critical decisions -4. Monitor agent resource usage""" - -PERFORMANCE_TIPS_PROMPT = """# Performance Optimization Guide - -## Speed Optimization - -### 1. Use Batch for Parallel Operations -```python -# SLOW - Sequential execution -result1 = read(file="/file1.py") -result2 = read(file="/file2.py") -result3 = read(file="/file3.py") - -# FAST - Parallel execution -batch( - description="Read multiple files", - invocations=[ - {"tool_name": "read", "input": {"file_path": "/file1.py"}}, - {"tool_name": "read", "input": {"file_path": "/file2.py"}}, - {"tool_name": "read", "input": {"file_path": "/file3.py"}} - ] -) -``` - -### 2. Use Unified Search -```python -# Combines multiple search methods efficiently -search( - pattern="important_function", - path="/project", - max_results=20 -) -``` - -### 3. Limit Result Sets -```python -# Use pagination -find(pattern="*.py", page=1, page_size=50) - -# Set max results -grep(pattern="TODO", max_results=100) -``` - -### 4. Use Appropriate Tools -```python -# For code structure - use AST search -grep_ast(pattern="class.*Service", path="/src") - -# For exact text - use grep -grep(pattern="ERROR:", path="/logs") - -# For concepts - use vector search -vector_search(query="authentication flow", path="/src") -``` - -## Memory Optimization - -### 1. Process Large Files in Chunks -```python -# Read with offset and limit -read(file="/large_file.log", offset=1000, limit=100) -``` - -### 2. Use Streaming for Long Operations -```python -# Background long-running processes -bash("npm run dev", background=true) - -# Check status separately -process(action="list") -``` - -## Network Optimization - -### 1. Local-First Processing -```python -# Use local compute when possible -network(task="analyze", mode="local") -``` - -### 2. Cache Results -```python -# Index once, search many times -vector_index(path="/project") -# Now searches are fast -vector_search(query="pattern", use_cache=true) -``` - -## Tips -- Profile before optimizing -- Use batch for I/O operations -- Limit data transferred -- Use appropriate search methods -- Cache expensive operations""" - -SECURITY_BEST_PRACTICES_PROMPT = """# Security Best Practices - -## Safe Tool Usage - -### 1. Path Validation -```python -# Always use absolute paths from known locations -read(file="/home/user/project/file.py") # Good -read(file="../../../etc/passwd") # Blocked - -# Tools validate paths against allowed directories -``` - -### 2. Command Injection Prevention -```python -# Use parameterized commands -bash(f"grep {pattern} file.txt") # Risky if pattern is user input - -# Better: Use dedicated tools -grep(pattern=user_input, path="file.txt") # Safe -``` - -### 3. Sensitive Data Handling -```python -# Never log sensitive data -think("Processing user data [REDACTED]") - -# Use local mode for sensitive operations -network( - task="Process PII data", - mode="local", # Stays on device - require_local=true -) -``` - -## Permission Management - -### 1. Tool Permissions -- Read operations are generally safe -- Write operations require confirmation -- System commands are restricted - -### 2. Agent Permissions -```python -# Agents inherit permission restrictions -agent( - prompt="Analyze code", - permissions=["read", "search"] # Limited permissions -) -``` - -## Data Protection - -### 1. Local Processing -```python -# Keep data local -network(mode="local", task="Process confidential data") -``` - -### 2. Secure Communication -- All MCP connections use secure channels -- Agent communications are encrypted -- No data persisted without permission - -## Best Practices -1. Validate all inputs -2. Use least privilege principle -3. Keep sensitive data local -4. Audit tool usage -5. Review agent outputs -6. Don't execute untrusted code -7. Use sandboxed environments for testing""" - -TROUBLESHOOTING_GUIDE_PROMPT = """# Troubleshooting Guide - -## Common Issues and Solutions - -### 1. Tool Not Found -``` -Error: Tool 'X' not found -``` -**Solution:** -```python -# Check if tool is enabled -tool_list() - -# Enable if needed -tool_enable(name="tool_name") -``` - -### 2. Permission Denied -``` -Error: Permission denied for path X -``` -**Solution:** -- Ensure path is in allowed directories -- Launch with: `hanzo-mcp --allow-path /your/path` - -### 3. Timeout Errors -``` -Error: Command timed out -``` -**Solution:** -```python -# Increase timeout -bash("long_command", timeout=300) - -# Or run in background -bash("long_command", background=true) -``` - -### 4. Memory Issues -``` -Error: Result too large -``` -**Solution:** -```python -# Use pagination -find(pattern="*", page=1, page_size=50) - -# Limit results -grep(pattern="text", max_results=100) - -# Read in chunks -read(file="/large.txt", offset=0, limit=1000) -``` - -### 5. Agent Failures -``` -Error: Agent failed to complete task -``` -**Solution:** -```python -# Increase iterations -agent( - prompt="task", - max_iterations=20, - max_tool_uses=50 -) - -# Use simpler prompts -# Break complex tasks into steps -``` - -### 6. Network Issues -``` -Error: Cannot connect to cluster -``` -**Solution:** -```python -# Use local mode -network(mode="local", task="...") - -# Check cluster status -mcp_stats() -``` - -## Debugging Tips - -### 1. Enable Verbose Output -```python -# Get detailed information -stats() - -# Check process status -process(action="list") - -# View logs -process(action="logs", id="process_id") -``` - -### 2. Test Incrementally -```python -# Start simple -read(file="/test.txt") - -# Then expand -batch(invocations=[...]) -``` - -### 3. Check Documentation -```python -# View help for specific category -"/hanzo:Filesystem tools help" -"/hanzo:Agent tools help" - -# Explore all tools -"/hanzo:Explore all tools" -``` - -## Getting Help -1. Check error messages carefully -2. Use `think()` to reason about issues -3. Consult category-specific help -4. Try simpler alternatives -5. Report persistent issues""" - -# Export all prompts -__all__ = [ - "QUICK_START_PROMPT", - "PAGINATION_GUIDE_PROMPT", - "MEMORY_VECTOR_HELP_PROMPT", - "DATABASE_TOOLS_HELP_PROMPT", - "LSP_TOOLS_HELP_PROMPT", - "CONFIGURATION_GUIDE_PROMPT", - "NETWORK_AGENT_GUIDE_PROMPT", - "PERFORMANCE_TIPS_PROMPT", - "SECURITY_BEST_PRACTICES_PROMPT", - "TROUBLESHOOTING_GUIDE_PROMPT", -] diff --git a/pkg/hanzo-mcp/hanzo_mcp/prompts/example_custom_prompt.py b/pkg/hanzo-mcp/hanzo_mcp/prompts/example_custom_prompt.py deleted file mode 100644 index 77f55478a..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/prompts/example_custom_prompt.py +++ /dev/null @@ -1,43 +0,0 @@ -"""Example custom prompt for demonstration.""" - -# This is the actual prompt text that will be sent to Claude -EXAMPLE_GREETING_PROMPT = """You are a friendly assistant. Please greet the user warmly and ask how you can help them today. -Make your greeting personalized based on the time of day if possible.""" - -EXAMPLE_CODE_REVIEW_PROMPT = """Please perform a thorough code review of the most recent code changes in our conversation. -Focus on: -1. Code quality and best practices -2. Potential bugs or edge cases -3. Performance considerations -4. Security implications -5. Suggestions for improvement - -Be constructive and specific in your feedback.""" - - -# You can also create dynamic prompts that take parameters -def create_custom_analysis_prompt( - file_path: str, analysis_type: str = "general" -) -> str: - """Create a dynamic analysis prompt for a specific file.""" - - analysis_types = { - "general": "Provide a general analysis including structure, purpose, and quality", - "security": "Focus on security vulnerabilities and best practices", - "performance": "Analyze performance bottlenecks and optimization opportunities", - "refactor": "Suggest refactoring opportunities to improve code maintainability", - } - - analysis_instruction = analysis_types.get(analysis_type, analysis_types["general"]) - - return f"""Please analyze the file at {file_path}. - -{analysis_instruction} - -Your analysis should include: -1. Overview of the file's purpose and structure -2. Key findings based on the analysis type -3. Specific recommendations with code examples where applicable -4. Priority ranking of any issues found - -Be thorough but concise in your analysis.""" diff --git a/pkg/hanzo-mcp/hanzo_mcp/prompts/project_system.py b/pkg/hanzo-mcp/hanzo_mcp/prompts/project_system.py deleted file mode 100644 index 1ca7436e5..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/prompts/project_system.py +++ /dev/null @@ -1,165 +0,0 @@ -PROJECT_SYSTEM_PROMPT = """Your are assisting me with a project. - -Here is useful information about the environment you are running in: - - -Working directory: {working_directory} (You need cd to this directory by yourself) -Is directory a git repo: {is_git_repo} -Platform: {platform} -OS Version: {os_version} - - - -directoryStructure: Below is a snapshot of this project's file structure at the start of the conversation. This snapshot will NOT update during the conversation. It skips over .gitignore patterns. - -{directory_structure} - -gitStatus: This is the git status at the start of the conversation. Note that this status is a snapshot in time, and will not update during the conversation. - -Current branch: {current_branch} - -Main branch (you will usually use this for PRs): {main_branch} - -Status: - -{git_status} - -Recent commits: - -{recent_commits} - - - -Hanzo AI provides 65+ tools organized by category. Key tools include: - -# File Operations -- read, write, edit, multi_edit: File manipulation -- tree, find: Navigation and discovery - -# Search & Analysis -- grep: Fast text search -- symbols: AST-aware symbol search -- search: Multi-modal intelligent search -- git_search: Git history search -- vector_search: Semantic search - -# Shell & Process -- run_command: Execute commands -- processes, pkill: Process management -- npx, uvx: Run packages directly - -# Development -- jupyter: Notebook operations (read/edit actions) -- todo: Task management (read/write actions) -- agent: Delegate complex tasks -- llm: Query LLMs (query/list/consensus actions) - -# Databases -- sql: SQL operations (query/search/stats actions) -- graph: Graph operations (add/remove/query/search/stats actions) - -# System -- config: Configuration management -- stats: Usage statistics -- tool_enable/disable: Dynamic tool control - -Tools follow the principle of one tool per task with multiple actions where appropriate. - - - -IMPORTANT: Always use the todo_write tool to plan and track tasks throughout the conversation. - -# Code References -When referencing specific functions or pieces of code include the pattern `file_path:line_number` to allow me to easily navigate to the source code location. - - -user: Where are errors from the client handled? -assistant: Clients are marked as failed in the `connectToServer` function in src/services/process.ts:712. - - -Do what has been asked; nothing more, nothing less. -ALWAYS prefer editing an existing file to creating a new one. -NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User. - -# Proactiveness -You are allowed to be proactive, but only when I ask you to do something. You should strive to strike a balance between: -1. Doing the right thing when asked, including taking actions and follow-up actions -2. Not surprising me with actions you take without asking -For example, if I ask you how to approach something, you should do your best to answer their question first, and not immediately jump into taking actions. -3. Do not add additional code explanation summary unless requested by me. After working on a file, just stop, rather than providing an explanation of what you did. - -# Following conventions -When making changes to files, first understand the file's code conventions. Mimic code style, use existing libraries and utilities, and follow existing patterns. -- NEVER assume that a given library is available, even if it is well known. Whenever you write code that uses a library or framework, first check that this codebase already uses the given library. For example, you might look at neighboring files, or check the package.json (or cargo.toml, and so on depending on the language). -- When you create a new component, first look at existing components to see how they're written; then consider framework choice, naming conventions, typing, and other conventions. -- When you edit a piece of code, first look at the code's surrounding context (especially its imports) to understand the code's choice of frameworks and libraries. Then consider how to make the given change in a way that is most idiomatic. -- Always follow security best practices. Never introduce code that exposes or logs secrets and keys. Never commit secrets or keys to the repository. - -# Go workspaces -When working in Go projects: -- Prefer the go.work root if present above the target file; otherwise use the nearest go.mod. If neither exists, error. -- Start gopls with cwd/rootUri/workspaceFolders set to that workspace root. -- Use absolute file URIs for all LSP operations, and apply WorkspaceEdit across the entire workspace. -- LSP positions are UTF-16 code units; convert columns if non-ASCII appears on the line. -- After renames or batch edits, run organize_imports on changed files (then gofmt if available). -- Use filesystem moves (rename/create/delete edits or gomvpkg) for package moves; identifier rename is not a move. - - - - -# Task Management -You have access to the todo tool (actions: read, write) to help you manage and plan tasks. Use this tool VERY frequently to ensure that you are tracking your tasks and giving me visibility into your progress. -This tool is also EXTREMELY helpful for planning tasks, and for breaking down larger complex tasks into smaller steps. If you do not use this tool when planning, you may forget to do important tasks - and that is unacceptable. - -It is critical that you mark todos as completed as soon as you are done with a task. Do not batch up multiple tasks before marking them as completed. - -Examples: - -user: Run the build and fix any type errors -assistant: I'm going to use the todo tool with write action to add the following items to the todo list: -- Run the build -- Fix any type errors - -I'm now going to run the build using Bash. - -Looks like I found 10 type errors. I'm going to use the todo tool with write action to add 10 items to the todo list. - -marking the first todo as in_progress - -Let me start working on the first item... - -The first item has been fixed, let me mark the first todo as completed, and move on to the second item... - - -In the above example, the assistant completes all the tasks, including the 10 error fixes and running the build and fixing all errors. - - -user: Help me write a new feature that allows users to track their usage metrics and export them to various formats - -assistant: I'll help you implement a usage metrics tracking and export feature. Let me first use the todo tool with write action to plan this task. -Adding the following todos to the todo list: -1. Research existing metrics tracking in the codebase -2. Design the metrics collection system -3. Implement core metrics tracking functionality -4. Create export functionality for different formats - -Let me start by researching the existing codebase to understand what metrics we might already be tracking and how we can build on that. - -I'm going to search for any existing metrics or telemetry code in the project. - -I've found some existing telemetry code. Let me mark the first todo as in_progress and start designing our metrics tracking system based on what I've learned... - -[Assistant continues implementing the feature step by step, marking todos as in_progress and completed as they go] - - -# Doing tasks -I will primarily request you perform software engineering tasks. This includes solving bugs, adding new functionality, refactoring code, explaining code, and more. For these tasks the following steps are recommended: -- Use the todo tool with write action to plan the task if required -- Use the available search tools (grep, symbols, search, git_search) to understand the codebase and my query. The 'search' tool intelligently combines multiple search strategies. You are encouraged to use search tools extensively both in parallel and sequentially. -- Implement the solution using all tools available to you -- Verify the solution if possible with tests. NEVER assume specific test framework or test script. Check the README or search codebase to determine the testing approach. -- VERY IMPORTANT: When you have completed a task, you MUST run the lint and typecheck commands (eg. npm run lint, npm run typecheck, ruff, etc.) with Bash if they were provided to you to ensure your code is correct. If you are unable to find the correct command, ask me for the command to run and if they supply it, proactively suggest writing it to CLAUDE.md so that you will know to run it next time. -NEVER commit changes unless I explicitly ask you to. It is VERY IMPORTANT to only commit when explicitly asked, otherwise I will feel that you are being too proactive. - -- Tool results and user messages may include tags. tags contain useful information and reminders. -""" diff --git a/pkg/hanzo-mcp/hanzo_mcp/prompts/project_todo_reminder.py b/pkg/hanzo-mcp/hanzo_mcp/prompts/project_todo_reminder.py deleted file mode 100644 index f1df07a54..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/prompts/project_todo_reminder.py +++ /dev/null @@ -1,110 +0,0 @@ -from typing import Any - -# Import TodoStorage to access todo data -from hanzo_tools.todo.base import TodoStorage - -PROJECT_TODO_EMPTY_REMINDER = """This is a reminder that your todo list is currently empty. DO NOT mention this to me explicitly because i have already aware. If you are working on tasks that would benefit from a todo list please use the todo_write tool to create one. If not, please feel free to ignore.""" - - -PROJECT_TODO_REMINDER = """ -This is a reminder that you have a to-do list for this project. The to-do list session ID is: {session_id}. You can use the todo_write tool to add new to-dos to the list. - -The to-do list is shown below, so you do not need to read it using the todo_read tool before your next time using the todo_write tool: - -{todo_list} - -""" - - -def format_todo_list_concise(todos: list[dict[str, Any]]) -> str: - """Format a todo list in a concise format for inclusion in prompts. - - Args: - todos: List of todo items - - Returns: - Formatted string representation of the todo list - """ - if not todos: - return "No todos found." - - formatted_lines = [] - for todo in todos: - status = todo.get("status", "unknown") - priority = todo.get("priority", "medium") - content = todo.get("content", "No content") - todo_id = todo.get("id", "no-id") - - # Handle empty strings as well as missing values - if not content or not str(content).strip(): - content = "No content" - if not todo_id or not str(todo_id).strip(): - todo_id = "no-id" - - # Create status indicator - status_indicator = { - "pending": "[ ]", - "in_progress": "[~]", - "completed": "[โœ“]", - }.get(status, "[?]") - - # Create priority indicator - priority_indicator = {"high": "๐Ÿ”ด", "medium": "๐ŸŸก", "low": "๐ŸŸข"}.get( - priority, "โšช" - ) - - formatted_lines.append( - f"{status_indicator} {priority_indicator} {content} (id: {todo_id})" - ) - - return "\n".join(formatted_lines) - - -def has_unfinished_todos(todos: list[dict[str, Any]]) -> bool: - """Check if there are any unfinished todos in the list. - - Args: - todos: List of todo items - - Returns: - True if there are unfinished todos, False otherwise - """ - if not todos: - return False - - for todo in todos: - status = todo.get("status", "pending") - if status in ["pending", "in_progress"]: - return True - - return False - - -def get_project_todo_reminder(session_id: str | None = None) -> str: - """Get the appropriate todo reminder for a session. - - Args: - session_id: Session ID to check todos for. If None, finds the latest active session. - - Returns: - Either PROJECT_TODO_EMPTY_REMINDER or PROJECT_TODO_REMINDER with formatted content - """ - # If no session_id provided, try to find the latest active session - if session_id is None: - session_id = TodoStorage.find_latest_active_session() - if session_id is None: - # No active sessions found - return PROJECT_TODO_EMPTY_REMINDER - - # Get todos for the session - todos = TodoStorage.get_todos(session_id) - - # Check if we have unfinished todos - if not has_unfinished_todos(todos): - return PROJECT_TODO_EMPTY_REMINDER - - # Format the todo list and return the reminder with content - formatted_todos = format_todo_list_concise(todos) - return PROJECT_TODO_REMINDER.format( - session_id=session_id, todo_list=formatted_todos - ) diff --git a/pkg/hanzo-mcp/hanzo_mcp/prompts/tool_explorer.py b/pkg/hanzo-mcp/hanzo_mcp/prompts/tool_explorer.py deleted file mode 100644 index b2a24b1ab..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/prompts/tool_explorer.py +++ /dev/null @@ -1,604 +0,0 @@ -"""Tool explorer prompts for discovering and using Hanzo MCP tools.""" - -TOOL_EXPLORER_PROMPT = """# Hanzo MCP Tool Explorer - -You have access to a comprehensive suite of tools through the Hanzo MCP system. These tools can be used individually or combined using the batch tool for powerful multi-agent workflows. - -## Tool Categories - -### ๐Ÿค– Agent Tools -Tools for delegating tasks to specialized AI agents: -- **dispatch_agent**: Launch a specialized agent for file exploration and analysis -- **swarm**: Orchestrate multiple agents working together -- **claude_cli**: Interact with Claude via CLI -- **critic**: Critical analysis and review -- **review**: Code review and feedback -- **zen**: Decision-making guidance using Hanzo Zen - -### ๐Ÿ“ Filesystem Tools -Tools for file and directory operations: -- **read_files**: Read one or multiple files -- **write_file**: Create or overwrite files -- **edit_file**: Make precise edits to files -- **multi_edit**: Multiple edits in one operation -- **tree**: View directory structure -- **find**: Find files by pattern -- **grep**: Search file contents -- **grep_ast**: Search with AST context -- **search_content**: Unified search across multiple methods -- **content_replace**: Replace patterns across files -- **batch_search**: Run multiple searches in parallel - -### ๐Ÿš Shell Tools -Tools for command execution: -- **run_command**: Execute shell commands -- **bash**: Run bash commands with session persistence -- **npx**: Run Node packages -- **uvx**: Run Python packages -- **process**: Manage background processes -- **open**: Open files/URLs in default app - -### ๐Ÿง  AI/LLM Tools -Tools for AI operations: -- **llm**: Query various LLM providers -- **consensus**: Get consensus from multiple models -- **think**: Structured thinking and planning -- **critic**: Critical analysis - -### ๐Ÿ’พ Database Tools -Tools for data operations: -- **sql_query**: Execute SQL queries -- **graph_add**: Add to graph database -- **vector_search**: Semantic search -- **index**: Manage search indices - -### ๐Ÿ““ Jupyter Tools -Tools for notebook operations: -- **notebook_read**: Read Jupyter notebooks -- **notebook_edit**: Edit notebook cells - -### โœ… Todo Tools -Tools for task management: -- **todo**: Manage todo lists -- **todo_read**: Read current todos -- **todo_write**: Update todo items - -### ๐Ÿ”ง Configuration Tools -Tools for settings and configuration: -- **config**: Manage tool configuration -- **mode**: Switch developer modes -- **tool_list**: List available tools -- **tool_enable/disable**: Toggle tools - -### ๐Ÿ” LSP Tools -Language Server Protocol tools: -- **lsp**: Code intelligence operations - -### ๐ŸŒ MCP Tools -Model Context Protocol management: -- **mcp_add**: Add MCP servers -- **mcp_remove**: Remove MCP servers -- **mcp_stats**: View MCP statistics - -## Using Tools with Batch - -The batch tool allows you to run multiple tools in parallel for maximum efficiency: - -```python -batch( - description="Analyze project structure", - invocations=[ - {"tool_name": "tree", "input": {"path": "/project"}}, - {"tool_name": "grep", "input": {"pattern": "TODO", "path": "/project"}}, - {"tool_name": "find", "input": {"pattern": "*.test.js", "path": "/project"}} - ] -) -``` - -## Tool Usage Examples - -### Example 1: Code Analysis Workflow -```python -# First, explore the project structure -tree(path="/project", depth=3) - -# Search for specific patterns -batch( - description="Find all API endpoints", - invocations=[ - {"tool_name": "grep", "input": {"pattern": "app\\.(get|post|put|delete)", "path": "/project/src"}}, - {"tool_name": "grep_ast", "input": {"pattern": "router", "path": "/project/src"}} - ] -) -``` - -### Example 2: Multi-Agent Analysis -```python -# Dispatch specialized agents for different tasks -batch( - description="Comprehensive code analysis", - invocations=[ - {"tool_name": "dispatch_agent", "input": {"prompt": "Analyze security vulnerabilities in /project/src"}}, - {"tool_name": "dispatch_agent", "input": {"prompt": "Find performance bottlenecks in database queries"}}, - {"tool_name": "dispatch_agent", "input": {"prompt": "Review test coverage and suggest improvements"}} - ] -) -``` - -### Example 3: Refactoring Workflow -```python -# Find all instances of a pattern -search_content(pattern="oldFunction", path="/project") - -# Review the code -critic(analysis="Review the usage of oldFunction and suggest refactoring approach") - -# Make the changes -batch( - description="Refactor oldFunction to newFunction", - invocations=[ - {"tool_name": "content_replace", "input": { - "pattern": "oldFunction", - "replacement": "newFunction", - "path": "/project/src" - }}, - {"tool_name": "run_command", "input": {"command": "npm test"}} - ] -) -``` - -## Best Practices - -1. **Use batch for parallel operations**: When you need to run multiple independent operations, use batch to run them concurrently. - -2. **Combine search tools**: Use search for comprehensive results, grep for simple text matching, and grep_ast for code structure understanding. - -3. **Leverage agents for complex tasks**: Use dispatch_agent for tasks requiring deep analysis or multiple steps. - -4. **Track progress with todos**: Use todo tools to manage multi-step workflows. - -5. **Think before acting**: Use the think tool to plan complex operations. - -## Getting Started - -To explore available tools in detail: -1. Use `tool_list()` to see all available tools -2. Each tool has detailed documentation in its implementation -3. Tools can be combined creatively for powerful workflows - -Would you like to explore any specific tool category or see more examples?""" - -# Tool category specific prompts -FILESYSTEM_TOOLS_HELP = """# Filesystem Tools Guide - -## Core File Operations - -### Reading Files -```python -# Read a single file -read_files(paths=["/path/to/file.py"]) - -# Read multiple files at once -read_files(paths=[ - "/project/src/main.py", - "/project/src/utils.py", - "/project/tests/test_main.py" -]) - -# Read with line limits -read_files(paths=["/large/file.py"], lines=100, offset=500) -``` - -### Editing Files -```python -# Simple edit -edit_file( - path="/src/main.py", - edits=[{"oldText": "old code", "newText": "new code"}] -) - -# Multiple edits in one file -multi_edit( - file_path="/src/utils.py", - edits=[ - {"old_string": "import old", "new_string": "import new"}, - {"old_string": "old_function", "new_string": "new_function"} - ] -) -``` - -### Searching -```python -# Find files by pattern -find(pattern="*.test.js", path="/project") - -# Search file contents -grep(pattern="TODO|FIXME", path="/project", include="*.py") - -# Search with AST context -grep_ast(pattern="class.*Controller", path="/project/src") - -# Unified search (combines multiple search methods) -search_content( - pattern="authentication", - path="/project", - enable_ast=True -) -``` - -### Batch Operations -```python -# Search across multiple patterns simultaneously -batch_search( - queries=[ - {"pattern": "login", "type": "text"}, - {"pattern": "authenticate", "type": "semantic"}, - {"pattern": "class.*Auth", "type": "ast"} - ], - path="/project" -) -``` - -## Advanced Features - -### Content Replacement -```python -# Replace across multiple files -content_replace( - pattern="oldAPI", - replacement="newAPI", - path="/project/src", - dry_run=True # Preview changes first -) -``` - -### Directory Exploration -```python -# View directory structure -tree(path="/project", depth=3, include_filtered=False) - -# Find specific file types -find( - pattern="*.py", - path="/project", - min_size="1KB", - max_size="100KB", - modified_after="1 week ago" -) -```""" - -AGENT_TOOLS_HELP = """# Agent Tools Guide - -## Dispatching Agents - -The agent tools allow you to delegate complex tasks to specialized sub-agents that have access to file operations and search capabilities. - -### Basic Agent Dispatch -```python -# Dispatch a single agent for analysis -dispatch_agent( - prompt="Analyze the authentication system in /project/src/auth and identify security vulnerabilities" -) - -# Multiple agents for different aspects -batch( - description="Comprehensive security audit", - invocations=[ - {"tool_name": "dispatch_agent", "input": { - "prompt": "Review authentication implementation for security issues" - }}, - {"tool_name": "dispatch_agent", "input": { - "prompt": "Check for SQL injection vulnerabilities in database queries" - }}, - {"tool_name": "dispatch_agent", "input": { - "prompt": "Analyze API endpoints for authorization bypass risks" - }} - ] -) -``` - -### Swarm Operations -```python -# Create a swarm of agents working together -swarm( - agents=[ - { - "id": "analyzer", - "role": "Code Analyzer", - "goal": "Identify code quality issues", - "backstory": "Expert in clean code principles", - "tools": ["grep_ast", "read_files", "symbols"] - }, - { - "id": "refactorer", - "role": "Code Refactorer", - "goal": "Suggest and implement improvements", - "backstory": "Specialist in code optimization", - "tools": ["edit_file", "multi_edit", "content_replace"] - } - ], - tasks=[ - { - "description": "Find code smells and anti-patterns", - "assigned_to": "analyzer" - }, - { - "description": "Refactor identified issues", - "assigned_to": "refactorer", - "depends_on": ["analyzer"] - } - ] -) -``` - -### Specialized Agents - -#### Critic Agent -```python -# Get critical analysis -critic( - analysis="Review this implementation for potential issues:\\n" + code_snippet -) -``` - -#### Review Agent -```python -# Comprehensive code review -review( - files=["/src/main.py", "/src/utils.py"], - focus_areas=["security", "performance", "maintainability"] -) -``` - -#### Zen Guidance -```python -# Get decision-making guidance -zen( - challenge="Should we refactor the authentication system now or after the release?" -) -``` - -## Agent Capabilities - -Agents dispatched through these tools have access to: -- File reading and searching -- Pattern matching and AST analysis -- Directory exploration -- Comprehensive search capabilities - -They cannot: -- Modify files directly -- Execute shell commands -- Make external API calls - -This makes them safe for exploration and analysis tasks.""" - -SHELL_TOOLS_HELP = """# Shell Tools Guide - -## Command Execution - -### Basic Commands -```python -# Run simple commands -run_command(command="ls -la", cwd="/project") -run_command(command="git status") - -# Run with environment variables -run_command( - command="npm test", - env={"NODE_ENV": "test", "CI": "true"} -) -``` - -### Bash Sessions -```python -# Use bash for session persistence -bash(command="cd /project && npm install") -bash(command="export API_KEY=test123 && npm run dev") -``` - -### Package Runners -```python -# Run Node packages -npx(package="prettier", args="--write src/**/*.js") -npx(package="create-react-app", args="my-app") - -# Run Python packages -uvx(package="ruff", args="check .") -uvx(package="black", args="--check src/") -``` - -### Background Processes -```python -# Start long-running processes -run_command(command="npm run dev", background=True) - -# Manage processes -process(action="list") # List all background processes -process(action="logs", id="npm_abc123") # View logs -process(action="kill", id="npm_abc123") # Stop process -``` - -## Advanced Usage - -### Batch Operations -```python -# Run multiple commands efficiently -batch( - description="Run tests and linting", - invocations=[ - {"tool_name": "run_command", "input": {"command": "npm test"}}, - {"tool_name": "run_command", "input": {"command": "npm run lint"}}, - {"tool_name": "run_command", "input": {"command": "npm audit"}} - ] -) -``` - -### Working with Output -```python -# Capture and process output -result = run_command(command="git log --oneline -10") -# Process result.output for analysis -``` - -### Platform-Specific Commands -```python -# Open files/URLs in default application -open(path="https://github.com/user/repo") -open(path="/path/to/document.pdf") -```""" - -BATCH_TOOL_EXAMPLES = """# Batch Tool Mastery - -The batch tool is one of the most powerful features in Hanzo MCP, allowing parallel execution of multiple tools for maximum efficiency. - -## Basic Batch Usage - -```python -batch( - description="Project setup", - invocations=[ - {"tool_name": "run_command", "input": {"command": "npm install"}}, - {"tool_name": "run_command", "input": {"command": "pip install -r requirements.txt"}}, - {"tool_name": "tree", "input": {"path": ".", "depth": 2}} - ] -) -``` - -## Advanced Patterns - -### 1. Parallel Search Operations -```python -batch( - description="Find all authentication code", - invocations=[ - {"tool_name": "grep", "input": { - "pattern": "login|auth|session", - "path": "/src" - }}, - {"tool_name": "grep_ast", "input": { - "pattern": "class.*Auth", - "path": "/src" - }}, - {"tool_name": "find", "input": { - "pattern": "*auth*.py", - "path": "/src" - }} - ] -) -``` - -### 2. Multi-Agent Analysis -```python -batch( - description="Comprehensive code analysis", - invocations=[ - {"tool_name": "dispatch_agent", "input": { - "prompt": "Analyze code quality in /src/core modules" - }}, - {"tool_name": "dispatch_agent", "input": { - "prompt": "Review test coverage for /src/core modules" - }}, - {"tool_name": "dispatch_agent", "input": { - "prompt": "Identify performance bottlenecks in database operations" - }} - ] -) -``` - -### 3. File Operations -```python -batch( - description="Read configuration files", - invocations=[ - {"tool_name": "read_files", "input": { - "paths": ["package.json", "tsconfig.json", ".env.example"] - }}, - {"tool_name": "read_files", "input": { - "paths": ["src/config/database.js", "src/config/auth.js"] - }} - ] -) -``` - -### 4. Complex Workflows -```python -# Step 1: Analyze -analysis_batch = batch( - description="Analyze codebase", - invocations=[ - {"tool_name": "grep", "input": {"pattern": "TODO|FIXME", "path": "."}}, - {"tool_name": "dispatch_agent", "input": { - "prompt": "Find unused imports and dead code" - }} - ] -) - -# Step 2: Based on analysis, perform fixes -fix_batch = batch( - description="Fix identified issues", - invocations=[ - {"tool_name": "content_replace", "input": { - "pattern": "old_import", - "replacement": "new_import", - "path": "/src" - }}, - {"tool_name": "run_command", "input": {"command": "npm run lint:fix"}} - ] -) -``` - -## Batch Tool Best Practices - -1. **Group Related Operations**: Batch operations that are logically related -2. **Maximize Parallelism**: Independent operations should be in the same batch -3. **Use Descriptive Names**: The description helps track what the batch does -4. **Handle Results**: Each tool result is returned in order - -## Limitations - -- Tools in a batch cannot depend on each other's results -- All tools run in parallel when possible -- Maximum efficiency with truly independent operations - -## Available Tools for Batch - -The following tools can be used in batch operations: -- dispatch_agent -- read_files -- tree -- grep -- grep_ast -- run_command -- notebook_read -- find -- search_content -- ast -- git_search - -Tools NOT available in batch (require state/session): -- write_file -- edit_file -- multi_edit -- think -- todo_write""" - - -def create_tool_category_prompt(category: str, tools: list[str]) -> str: - """Create a dynamic prompt for a specific tool category.""" - - tool_descriptions = { - "filesystem": FILESYSTEM_TOOLS_HELP, - "agent": AGENT_TOOLS_HELP, - "shell": SHELL_TOOLS_HELP, - "batch": BATCH_TOOL_EXAMPLES, - } - - base_prompt = tool_descriptions.get( - category, f"# {category.title()} Tools\n\nAvailable tools in this category:\n" - ) - - if category not in tool_descriptions: - base_prompt += "\n".join(f"- **{tool}**: [Tool description]" for tool in tools) - - return base_prompt diff --git a/pkg/hanzo-mcp/hanzo_mcp/prompts/utils.py b/pkg/hanzo-mcp/hanzo_mcp/prompts/utils.py deleted file mode 100644 index c06b19f83..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/prompts/utils.py +++ /dev/null @@ -1,294 +0,0 @@ -import platform -from pathlib import Path -from typing import Any - -try: - from git import Repo - - GIT_AVAILABLE = True -except ImportError: - GIT_AVAILABLE = False - - -def get_os_info() -> tuple[str, str, str]: - """Get the operating system information. - Returns: - tuple: A tuple containing the system name, release, and version. - """ - system = platform.system() # noqa: F821 - release = platform.release() - version = platform.version() - - if system == "Darwin": - system = "MacOS" - elif system == "Linux": - try: - with open("/etc/os-release") as f: - for line in f: - if line.startswith("NAME="): - name = line.split("=")[1].strip().strip('"') - if "Ubuntu" in name: - system = "Ubuntu" - elif "Debian" in name: - system = "Debian" - elif "Fedora" in name: - system = "Fedora" - elif "CentOS" in name: - system = "CentOS" - elif "Arch Linux" in name: - system = "Arch Linux" - system = name - except FileNotFoundError: - dist = platform.freedesktop_os_release() - if dist and "NAME" in dist: - name = dist["NAME"] - if "Ubuntu" in name: - system = "Ubuntu" - system = name - system = "Linux" - elif system == "Java": - system = "Java" - - return system, release, version - - -def get_directory_structure( - path: str, max_depth: int = 3, include_filtered: bool = False -) -> str: - """Get a directory structure similar to tree tool. - - Args: - path: The directory path to scan - max_depth: Maximum depth to traverse (0 for unlimited) - include_filtered: Whether to include normally filtered directories - - Returns: - Formatted directory structure as a string - """ - try: - dir_path = Path(path) - - if not dir_path.exists() or not dir_path.is_dir(): - return f"Error: {path} is not a valid directory" - - # Define filtered directories (same as tree tool) - FILTERED_DIRECTORIES = { - ".git", - "node_modules", - ".venv", - "venv", - "__pycache__", - ".pytest_cache", - ".idea", - ".vs", - ".vscode", - "dist", - "build", - "target", - ".ruff_cache", - ".llm-context", - } - - def should_filter(current_path: Path) -> bool: - """Check if a directory should be filtered.""" - # Don't filter if it's the explicitly requested path - if str(current_path.absolute()) == str(dir_path.absolute()): - return False - # Filter based on directory name if filtering is enabled - return current_path.name in FILTERED_DIRECTORIES and not include_filtered - - def build_tree(current_path: Path, current_depth: int = 0) -> list[dict]: - """Build directory tree recursively.""" - result = [] - - try: - # Sort entries: directories first, then files alphabetically - entries = sorted( - current_path.iterdir(), key=lambda x: (not x.is_dir(), x.name) - ) - - for entry in entries: - if entry.is_dir(): - entry_data: dict[str, Any] = { - "name": entry.name, - "type": "directory", - } - - # Check if we should filter this directory - if should_filter(entry): - entry_data["skipped"] = "filtered-directory" - result.append(entry_data) - continue - - # Check depth limit (if enabled) - if max_depth > 0 and current_depth >= max_depth: - entry_data["skipped"] = "depth-limit" - result.append(entry_data) - continue - - # Process children recursively - entry_data["children"] = build_tree(entry, current_depth + 1) - result.append(entry_data) - else: - # Add files only if within depth limit - if max_depth <= 0 or current_depth < max_depth: - result.append({"name": entry.name, "type": "file"}) - - except Exception: - # Skip directories we can't read - pass - - return result - - def format_tree(tree_data: list[dict], level: int = 0) -> list[str]: - """Format tree data as indented strings.""" - lines = [] - - for item in tree_data: - # Indentation based on level - indent = " " * level - - # Format based on type - if item["type"] == "directory": - if "skipped" in item: - lines.append( - f"{indent}{item['name']}/ [skipped - {item['skipped']}]" - ) - else: - lines.append(f"{indent}{item['name']}/") - # Add children with increased indentation if present - if "children" in item: - lines.extend(format_tree(item["children"], level + 1)) - else: - # File - lines.append(f"{indent}{item['name']}") - - return lines - - # Build and format the tree - tree_data = build_tree(dir_path) - formatted_lines = format_tree(tree_data) - - # Add the root directory path as a prefix - result = f"- {dir_path}/" - if formatted_lines: - result += "\n" + "\n".join(f" {line}" for line in formatted_lines) - - return result - - except Exception as e: - return f"Error generating directory structure: {str(e)}" - - -def get_git_info(path: str) -> dict[str, str | None]: - """Get git information for a repository. - - Args: - path: Path to the git repository - - Returns: - Dictionary containing git information - """ - if not GIT_AVAILABLE: - return { - "current_branch": None, - "main_branch": None, - "git_status": "GitPython not available", - "recent_commits": "GitPython not available", - } - - try: - repo = Repo(path) - - # Get current branch - try: - current_branch = repo.active_branch.name - except Exception: - current_branch = "HEAD (detached)" - - # Try to determine main branch - main_branch = "main" # default - try: - # Check if 'main' exists - if "origin/main" in [ref.name for ref in repo.refs]: - main_branch = "main" - elif "origin/master" in [ref.name for ref in repo.refs]: - main_branch = "master" - elif "main" in [ref.name for ref in repo.refs]: - main_branch = "main" - elif "master" in [ref.name for ref in repo.refs]: - main_branch = "master" - except Exception: - pass - - # Get git status - try: - status_lines = [] - - # Check for staged changes - staged_files = list(repo.index.diff("HEAD")) - if staged_files: - for item in staged_files[:25]: # Limit to first 25 - change_type = item.change_type - ct = str(change_type)[0].upper() if change_type else "?" - status_lines.append(f"{ct} {item.a_path}") - if len(staged_files) > 25: - status_lines.append( - f"... and {len(staged_files) - 25} more staged files" - ) - - # Check for unstaged changes - unstaged_files = list(repo.index.diff(None)) - if unstaged_files: - for item in unstaged_files[:25]: # Limit to first 25 - status_lines.append(f"M {item.a_path}") - if len(unstaged_files) > 25: - status_lines.append( - f"... and {len(unstaged_files) - 25} more modified files" - ) - - # Check for untracked files - untracked_files = repo.untracked_files - if untracked_files: - for file in untracked_files[:25]: # Limit to first 25 - status_lines.append(f"?? {file}") - if len(untracked_files) > 25: - status_lines.append( - f"... and {len(untracked_files) - 25} more untracked files" - ) - - git_status = ( - "\n".join(status_lines) if status_lines else "Working tree clean" - ) - - except Exception: - git_status = "Unable to get git status" - - # Get recent commits - try: - commits = [] - for commit in repo.iter_commits(max_count=5): - short_hash = commit.hexsha[:7] - msg = commit.message - first_line = ( - msg.decode() if isinstance(msg, bytes) else str(msg) - ).split("\n")[0] - commits.append(f"{short_hash} {first_line}") - recent_commits = "\n".join(commits) - except Exception: - recent_commits = "Unable to get recent commits" - - return { - "current_branch": current_branch, - "main_branch": main_branch, - "git_status": git_status, - "recent_commits": recent_commits, - } - - except Exception as e: - return { - "current_branch": None, - "main_branch": None, - "git_status": f"Error: {str(e)}", - "recent_commits": f"Error: {str(e)}", - } diff --git a/pkg/hanzo-mcp/hanzo_mcp/server.py b/pkg/hanzo-mcp/hanzo_mcp/server.py deleted file mode 100644 index b7dd94edd..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/server.py +++ /dev/null @@ -1,616 +0,0 @@ -"""MCP server implementing Hanzo capabilities. - -IMPORTANT: This module uses lazy imports to ensure fast startup. -Heavy imports happen only when the server is actually created, not at module load time. -""" - -from __future__ import annotations - -import atexit -import logging -import os -import secrets -import signal -import threading -import warnings -from typing import TYPE_CHECKING, Any, Literal, cast, final - -# Type-only imports - don't execute at runtime -if TYPE_CHECKING: - from mcp.server import FastMCP - - -# Suppress llm deprecation warnings about event loop -warnings.filterwarnings( - "ignore", message="There is no current event loop", category=DeprecationWarning -) - -# Cached imports - lazy loaded on first use -_FastMCP = None -_EnhancedFastMCP = None -_PermissionManager = None -_SessionStorage = None -_register_all_tools = None -_register_all_prompts = None - - -def _normalize_mcp_result(result: Any) -> Any: - """Convert FastMCP tool results to JSON-serialisable primitives for ZAP. - - FastMCP returns ``list[mcp.types.TextContent | ImageContent | ...]``. - Pydantic models in that list have ``model_dump()`` (v2) or ``dict()`` - (v1). For TextContent specifically, the underlying text is often itself - a JSON document โ€” collapse single-element TextContent lists to the - parsed JSON when possible to minimise wire shape juggling for clients. - """ - import json as _json - - def _one(item: Any) -> Any: - if isinstance(item, (str, int, float, bool)) or item is None: - return item - if isinstance(item, (list, tuple)): - return [_one(x) for x in item] - if isinstance(item, dict): - return {k: _one(v) for k, v in item.items()} - dump = getattr(item, "model_dump", None) - if callable(dump): - return dump(mode="json") - d = getattr(item, "dict", None) - if callable(d): - return d() - return str(item) - - norm = _one(result) - if ( - isinstance(norm, list) - and len(norm) == 1 - and isinstance(norm[0], dict) - and norm[0].get("type") == "text" - and isinstance(norm[0].get("text"), str) - ): - text = norm[0]["text"] - try: - return _json.loads(text) - except Exception: - return {"type": "text", "text": text} - return norm - - -def _get_fast_mcp(): - """Get FastMCP class lazily.""" - global _FastMCP - if _FastMCP is None: - try: - from fastmcp import FastMCP - - _FastMCP = FastMCP - except ImportError: - try: - from mcp.server import FastMCP - - _FastMCP = FastMCP - except ImportError: - from mcp import FastMCP - - _FastMCP = FastMCP - return _FastMCP - - -def _get_enhanced_fast_mcp(): - """Get EnhancedFastMCP class lazily.""" - global _EnhancedFastMCP - if _EnhancedFastMCP is None: - from hanzo_mcp.server_enhanced import EnhancedFastMCP - - _EnhancedFastMCP = EnhancedFastMCP - return _EnhancedFastMCP - - -def _get_permission_manager(): - """Get PermissionManager class lazily.""" - global _PermissionManager - if _PermissionManager is None: - from hanzo_mcp.tools.common.permissions import PermissionManager - - _PermissionManager = PermissionManager - return _PermissionManager - - -def _get_session_storage(): - """Get SessionStorage class lazily. - - Returns None if session_storage module is not available. - """ - global _SessionStorage - if _SessionStorage is None: - try: - from hanzo_tools.shell.session_storage import SessionStorage - - _SessionStorage = SessionStorage - except ImportError: - # session_storage module not available - return sentinel value - _SessionStorage = False - return _SessionStorage if _SessionStorage else None - - -def _get_register_all_tools(): - """Get register_all_tools function lazily.""" - global _register_all_tools - if _register_all_tools is None: - from hanzo_mcp.tools import register_all_tools - - _register_all_tools = register_all_tools - return _register_all_tools - - -def _get_register_all_prompts(): - """Get register_all_prompts function lazily.""" - global _register_all_prompts - if _register_all_prompts is None: - from hanzo_mcp.prompts import register_all_prompts - - _register_all_prompts = register_all_prompts - return _register_all_prompts - - -@final -class HanzoMCPServer: - """MCP server implementing Hanzo capabilities.""" - - def __init__( - self, - name: str = "hanzo", - allowed_paths: list[str] | None = None, - project_paths: list[str] | None = None, - project_dir: str | None = None, - mcp_instance: FastMCP | None = None, - agent_model: str | None = None, - agent_max_tokens: int | None = None, - agent_api_key: str | None = None, - agent_base_url: str | None = None, - agent_max_iterations: int = 10, - agent_max_tool_uses: int = 30, - enable_agent_tool: bool = False, - command_timeout: float = 120.0, - disable_write_tools: bool = False, - disable_search_tools: bool = False, - host: str = "127.0.0.1", - port: int = 8888, - enabled_tools: dict[str, bool] | None = None, - disabled_tools: list[str] | None = None, - auth_token: str | None = None, - ): - """Initialize the Hanzo AI server. - - Args: - name: The name of the server - allowed_paths: list of paths that the server is allowed to access - project_paths: list of project paths to generate prompts for - project_dir: single project directory (added to allowed_paths and project_paths) - mcp_instance: Optional FastMCP instance for testing - agent_model: Optional model name for agent tool in LLM format - agent_max_tokens: Optional maximum tokens for agent responses - agent_api_key: Optional API key for the LLM provider - agent_base_url: Optional base URL for the LLM provider API endpoint - agent_max_iterations: Maximum number of iterations for agent (default: 10) - agent_max_tool_uses: Maximum number of total tool uses for agent (default: 30) - enable_agent_tool: Whether to enable the agent tool (default: False) - command_timeout: Default timeout for command execution in seconds (default: 120.0) - disable_write_tools: Whether to disable write tools (default: False) - disable_search_tools: Whether to disable search tools (default: False) - host: Host for SSE server (default: 127.0.0.1) - port: Port for SSE server (default: 3000) - enabled_tools: Dictionary of individual tool enable states (default: None) - disabled_tools: List of tool names to disable (default: None) - """ - # Use enhanced server for automatic context normalization - EnhancedFastMCP = _get_enhanced_fast_mcp() - self.mcp = mcp_instance if mcp_instance is not None else EnhancedFastMCP(name) - - # Set hanzo-mcp version on the low-level server (FastMCP doesn't expose this) - try: - from importlib.metadata import version as pkg_version - - self.mcp._mcp_server.version = pkg_version("hanzo-mcp") - except Exception: - self.mcp._mcp_server.version = "0.15.0" - - # Initialize authentication token โ€” check env, then ~/.hanzo/mcp_token - self.auth_token = auth_token or os.environ.get("HANZO_MCP_TOKEN") - if not self.auth_token: - token_path = os.path.expanduser("~/.hanzo/mcp_token") - try: - if os.path.exists(token_path): - with open(token_path) as f: - self.auth_token = f.read().strip() - except OSError: - pass - - if not self.auth_token: - # Generate and persist to ~/.hanzo/mcp_token - self.auth_token = secrets.token_urlsafe(32) - token_path = os.path.expanduser("~/.hanzo/mcp_token") - try: - os.makedirs(os.path.dirname(token_path), exist_ok=True) - with open(token_path, "w") as f: - f.write(self.auth_token) - os.chmod(token_path, 0o600) - except OSError: - pass - logger = logging.getLogger(__name__) - logger.info(f"Auth token persisted to {token_path}") - - # Initialize permissions and command executor - PermissionManager = _get_permission_manager() - self.permission_manager = PermissionManager() - - # Handle project_dir parameter - if project_dir: - if allowed_paths is None: - allowed_paths = [] - if project_dir not in allowed_paths: - allowed_paths.append(project_dir) - if project_paths is None: - project_paths = [] - if project_dir not in project_paths: - project_paths.append(project_dir) - - # Add allowed paths - if allowed_paths: - for path in allowed_paths: - self.permission_manager.add_allowed_path(path) - - # Store paths and options - self.project_paths = project_paths - self.project_dir = project_dir - self.disable_write_tools = disable_write_tools - self.disable_search_tools = disable_search_tools - self.host = host - self.port = port - self.enabled_tools = enabled_tools or {} - self.disabled_tools = disabled_tools or [] - - # Store agent options - self.agent_model = agent_model - self.agent_max_tokens = agent_max_tokens - self.agent_api_key = agent_api_key - self.agent_base_url = agent_base_url - self.agent_max_iterations = agent_max_iterations - self.agent_max_tool_uses = agent_max_tool_uses - self.enable_agent_tool = enable_agent_tool - self.command_timeout = command_timeout - - # Initialize cleanup tracking with thread-safe lock - self._cleanup_thread: threading.Thread | None = None - self._shutdown_event = threading.Event() - self._cleanup_registered = False - self._cleanup_lock = threading.Lock() - - # Apply disabled_tools to enabled_tools - final_enabled_tools = self.enabled_tools.copy() - for tool_name in self.disabled_tools: - final_enabled_tools[tool_name] = False - - # Store the final processed tool configuration - self.enabled_tools = final_enabled_tools - - # Register all tools (lazy import) - register_all_tools = _get_register_all_tools() - register_all_tools( - mcp_server=self.mcp, - permission_manager=self.permission_manager, - agent_model=self.agent_model, - agent_max_tokens=self.agent_max_tokens, - agent_api_key=self.agent_api_key, - agent_base_url=self.agent_base_url, - agent_max_iterations=self.agent_max_iterations, - agent_max_tool_uses=self.agent_max_tool_uses, - enable_agent_tool=self.enable_agent_tool, - disable_write_tools=self.disable_write_tools, - disable_search_tools=self.disable_search_tools, - enabled_tools=final_enabled_tools, - ) - - register_all_prompts = _get_register_all_prompts() - register_all_prompts(mcp_server=self.mcp, projects=self.project_paths) - - def _setup_cleanup_handlers(self) -> None: - """Set up signal handlers and background cleanup thread.""" - # Use lock to prevent race condition in concurrent calls - with self._cleanup_lock: - if self._cleanup_registered: - return - - # Mark as registered first to prevent re-entry - self._cleanup_registered = True - - # Register cleanup on normal exit - atexit.register(self._cleanup_sessions) - - # Register signal handlers for graceful shutdown - def signal_handler(signum, frame): - import sys - - # Only log if not stdio transport - if hasattr(self, "_transport") and self._transport != "stdio": - logger = logging.getLogger(__name__) - logger.info("\nShutting down gracefully...") - self._cleanup_sessions() - self._shutdown_event.set() - sys.exit(0) - - signal.signal(signal.SIGINT, signal_handler) - # SIGTERM works on Windows too (calls TerminateProcess) - signal.signal(signal.SIGTERM, signal_handler) - - # Start background cleanup thread for periodic cleanup - self._cleanup_thread = threading.Thread( - target=self._background_cleanup, daemon=True - ) - self._cleanup_thread.start() - - def _background_cleanup(self) -> None: - """Background thread for periodic session cleanup.""" - SessionStorage = _get_session_storage() - if SessionStorage is None: - # No session storage available, just wait for shutdown - self._shutdown_event.wait() - return - - while not self._shutdown_event.is_set(): - try: - # Clean up expired sessions every 2 minutes - # Using shorter TTL of 5 minutes (300 seconds) - SessionStorage.cleanup_expired_sessions(max_age_seconds=300) - - # Wait for 2 minutes or until shutdown - self._shutdown_event.wait(timeout=120) - except Exception: - # Ignore cleanup errors and continue - pass - - def _cleanup_sessions(self) -> None: - """Clean up all active sessions.""" - try: - SessionStorage = _get_session_storage() - if SessionStorage is None: - return - - cleared_count = SessionStorage.clear_all_sessions() - if cleared_count > 0: - # Only log if not stdio transport - if hasattr(self, "_transport") and self._transport != "stdio": - logger = logging.getLogger(__name__) - logger.info(f"Cleaned up {cleared_count} tmux sessions on shutdown") - except Exception: - # Ignore cleanup errors during shutdown - pass - - def _start_zap_server(self) -> None: - """Start ZAP server in a background thread for browser extension discovery.""" - if os.environ.get("HANZO_NO_ZAP"): - return - - try: - from hanzo_mcp.zap_server import ZapServer - except ImportError: - return - - # Collect tool manifest from registered MCP tools - tool_list: list[dict[str, Any]] = [] - try: - # FastMCP _tool_manager._tools is a dict[str, Tool] - tm = getattr(self.mcp, "_tool_manager", None) - if tm is not None: - tools_dict = getattr(tm, "_tools", {}) - for name, tool in tools_dict.items(): - tool_list.append( - { - "name": name, - "description": getattr(tool, "description", ""), - "inputSchema": getattr(tool, "parameters", {}), - } - ) - except Exception: - pass - - if not tool_list: - # Fallback: try listing via the tool registry - try: - tools = self.mcp.list_tools() - for t in tools: - tool_list.append( - { - "name": t.name, - "description": t.description or "", - "inputSchema": getattr(t, "parameters", {}), - } - ) - except Exception: - pass - - async def call_tool(name: str, args: dict) -> Any: # type: ignore[type-arg] - """Route ZAP tool calls to the MCP server. - - FastMCP returns a list of ``mcp.types.TextContent`` (or other - content) objects. Normalize to JSON-serialisable primitives so - ``zap.protocol.encode`` can serialize the MSG_RESPONSE body. - """ - try: - result = await self.mcp.call_tool(name, args) - except Exception as e: - return {"error": str(e)} - return _normalize_mcp_result(result) - - async def handle_method(method: str, params: Any) -> Any: - """Pass-through for ALL MCP methods โ€” full protocol parity over ZAP. - - Routes resources/*, prompts/*, and any other MCP method to the - underlying FastMCP server, so ZAP clients get the same capabilities - as stdio/SSE MCP clients. - """ - # resources/list - if method == "resources/list": - try: - resources = self.mcp.list_resources() - return { - "resources": [ - { - "uri": r.uri, - "name": r.name, - "mimeType": getattr(r, "mimeType", "text/plain"), - } - for r in resources - ] - } - except Exception: - return {"resources": []} - - # resources/read - if method == "resources/read": - uri = (params or {}).get("uri", "") - try: - content = await self.mcp.read_resource(uri) - return { - "contents": [ - {"uri": uri, "mimeType": "text/plain", "text": str(content)} - ] - } - except Exception as e: - return { - "contents": [ - { - "uri": uri, - "mimeType": "text/plain", - "text": f"Error: {e}", - } - ] - } - - # prompts/list - if method == "prompts/list": - try: - prompts = self.mcp.list_prompts() - return { - "prompts": [ - { - "name": p.name, - "description": getattr(p, "description", ""), - } - for p in prompts - ] - } - except Exception: - return {"prompts": []} - - # prompts/get - if method == "prompts/get": - name = (params or {}).get("name", "") - try: - prompt = await self.mcp.get_prompt( - name, params.get("arguments", {}) - ) - return prompt - except Exception as e: - raise ValueError(f"Prompt error: {e}") - - raise ValueError(f"Unsupported method: {method}") - - def _run_zap_loop(): - import asyncio as _asyncio - - loop = _asyncio.new_event_loop() - _asyncio.set_event_loop(loop) - try: - from hanzo_mcp.zap_server import start_zap_server - - server = loop.run_until_complete( - start_zap_server( - tools=tool_list, - call_tool=call_tool, - handle_method=handle_method, - name=( - self.mcp.name if hasattr(self.mcp, "name") else "hanzo-mcp" - ), - ) - ) - if server: - self._zap_server = server - loop.run_forever() - except Exception as e: - log = logging.getLogger(__name__) - log.debug(f"[ZAP] Failed to start: {e}") - finally: - loop.close() - - self._zap_server = None - zap_thread = threading.Thread(target=_run_zap_loop, daemon=True) - zap_thread.start() - - def run(self, transport: str = "stdio", allowed_paths: list[str] | None = None): - """Run the MCP server. - - Args: - transport: The transport to use (stdio or sse) - allowed_paths: list of paths that the server is allowed to access - """ - # Store transport for later use - self._transport = transport - - # Add allowed paths if provided - allowed_paths_list = allowed_paths or [] - for path in allowed_paths_list: - self.permission_manager.add_allowed_path(path) - - # Show compute nodes only in non-stdio mode (to avoid corrupting protocol) - if transport != "stdio" and not os.environ.get("HANZO_QUIET"): - try: - from hanzo_mcp.compute_nodes import ComputeNodeDetector - - detector = ComputeNodeDetector() - summary = detector.get_node_summary() - logger = logging.getLogger(__name__) - logger.info(f"๐Ÿ–ฅ๏ธ {summary}") - except Exception: - # Silently ignore if compute node detection fails - pass - - # Set up cleanup handlers before running - self._setup_cleanup_handlers() - - # Start ZAP server for browser extension discovery (background thread) - self._start_zap_server() - - # Run the server - transport_type = cast(Literal["stdio", "sse"], transport) - self.mcp.run(transport=transport_type) - - -def create_server( - name: str = "hanzo-mcp", - allowed_paths: list[str] | None = None, - enable_all_tools: bool = False, - **kwargs, -) -> HanzoMCPServer: - """Create a Hanzo MCP server instance. - - Args: - name: Server name - allowed_paths: List of allowed file paths - enable_all_tools: Enable all tools including agent tools - **kwargs: Additional server configuration - - Returns: - HanzoMCPServer instance - """ - if enable_all_tools: - kwargs["enable_agent_tool"] = True - - return HanzoMCPServer(name=name, allowed_paths=allowed_paths, **kwargs) - - -def main(): - """Main entry point for the server.""" - from hanzo_mcp.cli import main as cli_main - - cli_main() diff --git a/pkg/hanzo-mcp/hanzo_mcp/server_enhanced.py b/pkg/hanzo-mcp/hanzo_mcp/server_enhanced.py deleted file mode 100644 index 075d9105c..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/server_enhanced.py +++ /dev/null @@ -1,62 +0,0 @@ -"""Enhanced MCP server with automatic context normalization. - -This module provides an enhanced FastMCP server that automatically -applies context normalization to all registered tools. -""" - -from typing import Callable - -from mcp.server import FastMCP - -from hanzo_mcp.tools.common.decorators import with_context_normalization - - -class EnhancedFastMCP(FastMCP): - """Enhanced FastMCP server with automatic context normalization. - - This server automatically wraps all tool registrations with context - normalization, ensuring that tools work properly when called externally - with serialized context parameters. - """ - - def tool(self, name: str | None = None, description: str | None = None) -> Callable: - """Enhanced tool decorator that includes automatic context normalization. - - Args: - name: Tool name (defaults to function name) - description: Tool description - - Returns: - Decorator function that registers the tool with context normalization - """ - # Get the original decorator from parent class - original_decorator = super().tool(name=name, description=description) - - # Create our enhanced decorator - def enhanced_decorator(func: Callable) -> Callable: - # Apply context normalization first - # Check if function has ctx parameter - import inspect - - sig = inspect.signature(func) - if "ctx" in sig.parameters: - normalized_func = with_context_normalization(func) - else: - normalized_func = func - - # Then apply the original decorator - return original_decorator(normalized_func) - - return enhanced_decorator - - -def create_enhanced_server(name: str = "hanzo") -> EnhancedFastMCP: - """Create an enhanced MCP server with automatic context normalization. - - Args: - name: Server name - - Returns: - Enhanced FastMCP server instance - """ - return EnhancedFastMCP(name) diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/__init__.py b/pkg/hanzo-mcp/hanzo_mcp/tools/__init__.py deleted file mode 100644 index db41f974d..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/__init__.py +++ /dev/null @@ -1,271 +0,0 @@ -"""Tools package for Hanzo AI. - -This package provides dynamic tool loading from hanzo-tools-* packages via entry points. -Tools are discovered and loaded at runtime, enabling: -- Install/uninstall tool packages independently -- Enable/disable individual tools -- Hot-reload without server restart - -IMPORTANT: All tool implementations live in hanzo-tools-* packages. -This module only handles discovery and registration. -""" - -from __future__ import annotations - -import logging -from typing import TYPE_CHECKING, Any - -if TYPE_CHECKING: - from hanzo_tools.core import BaseTool, PermissionManager - from mcp.server import FastMCP - -logger = logging.getLogger(__name__) - - -def register_all_tools( - mcp_server: "FastMCP", - permission_manager: "PermissionManager", - agent_model: str | None = None, - agent_max_tokens: int | None = None, - agent_api_key: str | None = None, - agent_base_url: str | None = None, - agent_max_iterations: int = 10, - agent_max_tool_uses: int = 30, - enable_agent_tool: bool = False, - disable_write_tools: bool = False, - disable_search_tools: bool = False, - enabled_tools: dict[str, bool] | None = None, - vector_config: dict | None = None, - use_mode: bool = True, - force_mode: str | None = None, -) -> list["BaseTool"]: - """Register all Hanzo tools with the MCP server. - - Tools are discovered from installed hanzo-tools-* packages via entry points. - - Args: - mcp_server: The FastMCP server instance - permission_manager: Permission manager for access control - agent_model: Optional model name for agent tool in LLM format - agent_max_tokens: Optional maximum tokens for agent responses - agent_api_key: Optional API key for the LLM provider - agent_base_url: Optional base URL for the LLM provider API endpoint - agent_max_iterations: Maximum number of iterations for agent (default: 10) - agent_max_tool_uses: Maximum number of total tool uses for agent (default: 30) - enable_agent_tool: Whether to enable the agent tool (default: False) - disable_write_tools: Whether to disable write tools (default: False) - disable_search_tools: Whether to disable search tools (default: False) - enabled_tools: Dictionary of individual tool enable/disable states (default: None) - vector_config: Vector store configuration (default: None) - use_mode: Whether to use mode system for tool configuration (default: True) - force_mode: Force a specific mode to be active (default: None) - - Returns: - List of registered BaseTool instances - """ - from hanzo_mcp.tools.common.entrypoint_loader import ( - PACKAGE_TOOL_PREFIXES, - EntryPointToolLoader, - ) - - all_tools: dict[str, "BaseTool"] = {} - tool_config = enabled_tools or {} - - def is_tool_enabled(tool_name: str, category_enabled: bool = True) -> bool: - """Check if a specific tool should be enabled.""" - if tool_name in tool_config: - return tool_config[tool_name] - return category_enabled - - # Apply mode configuration if enabled - if use_mode: - try: - from hanzo_mcp.tools.common.mode import activate_mode_from_env - from hanzo_mcp.tools.common.mode_loader import ModeLoader - - activate_mode_from_env() - tool_config = ModeLoader.get_enabled_tools_from_mode( - base_enabled_tools=enabled_tools, force_mode=force_mode - ) - ModeLoader.apply_environment_from_mode() - except ImportError: - logger.debug("Mode system not available") - - # Build enabled state for each tool based on configuration - resolved_enabled_tools: dict[str, bool] = {} - - # Filesystem tools - for tool in PACKAGE_TOOL_PREFIXES.get("filesystem", []): - if tool in ["write", "edit", "multi_edit"]: - resolved_enabled_tools[tool] = is_tool_enabled( - tool, not disable_write_tools - ) - elif tool in ["ast", "search"]: - resolved_enabled_tools[tool] = is_tool_enabled( - tool, not disable_search_tools - ) - else: - resolved_enabled_tools[tool] = is_tool_enabled(tool, True) - - # Shell tools - always enabled by default - for tool in PACKAGE_TOOL_PREFIXES.get("shell", []): - resolved_enabled_tools[tool] = is_tool_enabled(tool, True) - - # Browser tool - resolved_enabled_tools["browser"] = is_tool_enabled("browser", True) - - # Memory tools - for tool in PACKAGE_TOOL_PREFIXES.get("memory", []): - resolved_enabled_tools[tool] = is_tool_enabled(tool, True) - - # Tasks tools - resolved_enabled_tools["tasks"] = is_tool_enabled("tasks", True) - - # Reasoning tools - resolved_enabled_tools["think"] = is_tool_enabled("think", True) - resolved_enabled_tools["critic"] = is_tool_enabled("critic", True) - - # LSP tool - resolved_enabled_tools["lsp"] = is_tool_enabled("lsp", True) - - # Refactor tool - resolved_enabled_tools["refactor"] = is_tool_enabled("refactor", True) - - # Database tools - for tool in PACKAGE_TOOL_PREFIXES.get("database", []): - resolved_enabled_tools[tool] = is_tool_enabled(tool, True) - - # Agent tools - resolved_enabled_tools["agent"] = enable_agent_tool or is_tool_enabled( - "agent", True - ) - resolved_enabled_tools["swarm"] = is_tool_enabled("swarm", False) - for tool in ["claude", "codex", "gemini", "grok", "code_auth"]: - resolved_enabled_tools[tool] = is_tool_enabled(tool, enable_agent_tool) - - # Editor tools - for tool in PACKAGE_TOOL_PREFIXES.get("editor", []): - resolved_enabled_tools[tool] = is_tool_enabled(tool, True) - - # LLM tools - resolved_enabled_tools["llm"] = is_tool_enabled("llm", True) - resolved_enabled_tools["consensus"] = is_tool_enabled("consensus", True) - - # Vector tools (usually disabled by default unless config provided) - vector_enabled = vector_config is not None - for tool in PACKAGE_TOOL_PREFIXES.get("vector", []): - resolved_enabled_tools[tool] = is_tool_enabled(tool, vector_enabled) - - # Config tools - resolved_enabled_tools["config"] = is_tool_enabled("config", True) - resolved_enabled_tools["mode"] = is_tool_enabled("mode", True) - resolved_enabled_tools["workspace"] = is_tool_enabled("workspace", True) - - # MCP tools - resolved_enabled_tools["mcp"] = is_tool_enabled("mcp", True) - - # Unified Hanzo platform surface. - # No backwards compatibility: expose only `hanzo` and hide legacy per-service tools. - resolved_enabled_tools["hanzo"] = is_tool_enabled("hanzo", True) - for legacy_tool in [ - "api", - "auth", - "billing", - "commerce", - "iam", - "ingress", - "kms", - "mpc", - "paas", - "team", - ]: - resolved_enabled_tools[legacy_tool] = False - - # Jupyter tools - for tool in PACKAGE_TOOL_PREFIXES.get("jupyter", []): - resolved_enabled_tools[tool] = is_tool_enabled(tool, True) - - # Computer tools (screen capture, recording, native control) - for tool in PACKAGE_TOOL_PREFIXES.get("computer", []): - resolved_enabled_tools[tool] = is_tool_enabled(tool, True) - - # UI component registry tool - resolved_enabled_tools["ui"] = is_tool_enabled("ui", True) - - # Create loader and discover packages - loader = EntryPointToolLoader(permission_manager=permission_manager) - discovered = loader.discover_packages() - - if discovered: - logger.info( - f"Discovered {len(discovered)} tool packages: {', '.join(discovered.keys())}" - ) - - # Load all discovered tools - loaded = loader.load_all( - mcp_server, - enabled_tools=resolved_enabled_tools, - # Database package tools currently require explicit db_manager wiring. - # Keep disabled by default unless explicitly enabled. - enabled_packages={"database": is_tool_enabled("database", False)}, - ) - all_tools.update(loaded) - logger.info(f"Loaded {len(loaded)} tools from entry points") - else: - logger.warning("No tool packages discovered via entry points") - - # Register system tools that are always available - _register_system_tools(mcp_server, all_tools) - - return list(all_tools.values()) - - -def _register_system_tools( - mcp_server: "FastMCP", - all_tools: dict[str, "BaseTool"], -) -> None: - """Register built-in system tools that are always available. - - These are core tools that don't come from hanzo-tools-* packages. - """ - # Version tool - try: - from hanzo_mcp.tools.common.version_tool import register_version_tool - - register_version_tool(mcp_server) - except ImportError: - logger.debug("Version tool not available") - - # Unified tool command (replaces tool_install, tool_enable, tool_disable, tool_list) - try: - from hanzo_mcp.tools.common.tool import register_unified_tool - - unified_tools = register_unified_tool(mcp_server) - for tool in unified_tools: - all_tools[tool.name] = tool - logger.info("Registered unified 'tool' command") - except ImportError as e: - logger.debug(f"Unified tool not available: {e}") - # Fallback to legacy tools if unified tool fails - try: - from hanzo_mcp.tools.common.tool_install import register_tool_install - - install_tools = register_tool_install(mcp_server) - for tool in install_tools: - all_tools[tool.name] = tool - except ImportError: - pass - - # Stats tool - try: - from hanzo_mcp.tools.common.stats import StatsTool - - stats_tool = StatsTool() - stats_tool.register(mcp_server) - all_tools[stats_tool.name] = stats_tool - except ImportError: - logger.debug("Stats tool not available") - - -# Re-export for backward compatibility -__all__ = ["register_all_tools"] diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/build_tool.py b/pkg/hanzo-mcp/hanzo_mcp/tools/build_tool.py deleted file mode 100644 index 73c2fe533..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/build_tool.py +++ /dev/null @@ -1,445 +0,0 @@ -""" -Build Tool - Compile/build artifacts narrowly by default -======================================================= - -Purpose: compile/build artifacts with smart scope resolution. - -Same scope logic as test tool: -- file โ†’ derive owning package/project and build it -- dir โ†’ build that subtree -- pkg โ†’ use explicitly -- ws โ†’ workspace-wide - -Backends: -- go: go build -- ts: tsc or workspace build (pnpm build) -- py: optional (python -m build) for packaging -- rs: cargo build -- cc: cmake --build, ninja, make -- sol: forge build / hardhat compile -""" - -from pathlib import Path -from typing import Any, Dict, Optional - -from .dev_tools import DevResult, DevToolBase, create_dev_result - - -class BuildTool(DevToolBase): - """Build/compilation tool""" - - def __init__(self, target: str, **kwargs): - super().__init__(target, **kwargs) - self.opts = kwargs.get("opts", {}) - - async def execute(self) -> DevResult: - """Execute build operation""" - try: - if self.language == "go": - return await self._build_go() - elif self.language == "ts": - return await self._build_typescript() - elif self.language == "py": - return await self._build_python() - elif self.language == "rs": - return await self._build_rust() - elif self.language == "cc": - return await self._build_cpp() - elif self.language == "sol": - return await self._build_solidity() - else: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[f"Build not supported for language: {self.language}"], - ) - except Exception as e: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[str(e)], - ) - - async def _build_go(self) -> DevResult: - """Build Go packages""" - cmd = ["go", "build"] - - # Determine build scope - if self.resolved["type"] == "file": - pkg = self.resolved["package"] - if pkg and pkg != ".": - cmd.append(f"./{pkg}") - else: - cmd.append(".") - elif self.resolved["type"] == "directory": - dir_path = self.resolved["scope"] - cmd.append(f"./{dir_path}/...") - elif self.resolved["type"] == "package": - pkg_spec = self.resolved["scope"] - cmd.append(pkg_spec) - elif self.resolved["type"] == "workspace": - cmd.append("./...") - else: - cmd.append(".") - - # Add build options - if self.opts.get("race"): - cmd.append("-race") - if self.opts.get("tags"): - cmd.extend(["-tags", self.opts["tags"]]) - if self.opts.get("ldflags"): - cmd.extend(["-ldflags", self.opts["ldflags"]]) - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="go", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _build_typescript(self) -> DevResult: - """Build TypeScript/JavaScript""" - # Try different build approaches - if self.backend == "tsc" or self._has_tsconfig(): - return await self._build_with_tsc() - elif self.backend in ["pnpm", "npm", "yarn"]: - return await self._build_with_package_script() - else: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[ - "No build configuration found (tsconfig.json or package.json build script)" - ], - ) - - async def _build_python(self) -> DevResult: - """Build Python packages""" - # Python builds are typically for packaging - if Path(self.workspace["root"], "pyproject.toml").exists(): - cmd = ["python", "-m", "build"] - elif Path(self.workspace["root"], "setup.py").exists(): - cmd = ["python", "setup.py", "build"] - else: - return create_dev_result( - ok=True, - root=self.workspace["root"], - language_used=self.language, - backend_used="python", - scope_resolved=self.resolved["scope"], - stdout="Python doesn't require explicit build - interpret/lint is the main verification", - ) - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="python", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _build_rust(self) -> DevResult: - """Build Rust packages""" - cmd = ["cargo", "build"] - - # Add package filter if building specific package - if self.resolved["type"] == "file": - cargo_toml = self._find_rust_package(self.resolved["scope"]) - if cargo_toml: - package_name = self._get_rust_package_name(cargo_toml) - if package_name: - cmd.extend(["-p", package_name]) - - # Add build options - if self.opts.get("release"): - cmd.append("--release") - if self.opts.get("features"): - cmd.extend(["--features", self.opts["features"]]) - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="cargo", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _build_cpp(self) -> DevResult: - """Build C/C++ projects""" - build_dir = Path(self.workspace["root"]) / "build" - - if self.backend == "cmake": - return await self._build_with_cmake() - elif self.backend == "ninja": - return await self._build_with_ninja() - elif self.backend == "make": - return await self._build_with_make() - else: - # Auto-detect - if (Path(self.workspace["root"]) / "CMakeLists.txt").exists(): - return await self._build_with_cmake() - elif (build_dir / "build.ninja").exists(): - return await self._build_with_ninja() - elif (Path(self.workspace["root"]) / "Makefile").exists(): - return await self._build_with_make() - else: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[ - "No build system detected (CMakeLists.txt, build.ninja, or Makefile)" - ], - ) - - async def _build_solidity(self) -> DevResult: - """Build Solidity contracts""" - if self.backend == "forge" or self._has_forge(): - cmd = ["forge", "build"] - backend = "forge" - elif self.backend == "hardhat" or self._has_hardhat(): - cmd = ["npx", "hardhat", "compile"] - backend = "hardhat" - else: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=["No Solidity build system detected (forge or hardhat)"], - ) - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used=backend, - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _build_with_tsc(self) -> DevResult: - """Build with TypeScript compiler""" - cmd = ["tsc"] - - # Add project file if exists - if self.resolved["type"] != "workspace": - # Look for nearest tsconfig.json - tsconfig = self._find_tsconfig(self.resolved["scope"]) - if tsconfig: - cmd.extend(["-p", str(tsconfig.parent)]) - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="tsc", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _build_with_package_script(self) -> DevResult: - """Build via package.json script""" - if self.backend == "pnpm": - cmd = ["pnpm", "build"] - elif self.backend == "yarn": - cmd = ["yarn", "build"] - else: - cmd = ["npm", "run", "build"] - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _build_with_cmake(self) -> DevResult: - """Build with CMake""" - build_dir = Path(self.workspace["root"]) / "build" - - # Configure if needed - if not build_dir.exists(): - config_result = self._run_command(["cmake", "-B", "build", "-S", "."]) - if config_result.returncode != 0: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used="cmake", - scope_resolved=self.resolved["scope"], - stdout=config_result.stdout, - stderr=config_result.stderr, - exit_code=config_result.returncode, - errors=["CMake configure failed"], - ) - - # Build - result = self._run_command(["cmake", "--build", "build"]) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="cmake", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _build_with_ninja(self) -> DevResult: - """Build with Ninja""" - result = self._run_command( - ["ninja"], cwd=str(Path(self.workspace["root"]) / "build") - ) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="ninja", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _build_with_make(self) -> DevResult: - """Build with Make""" - result = self._run_command(["make"]) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="make", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - def _has_tsconfig(self) -> bool: - """Check if tsconfig.json exists""" - return Path(self.workspace["root"], "tsconfig.json").exists() - - def _find_tsconfig(self, file_path: str) -> Optional[Path]: - """Find nearest tsconfig.json""" - path = Path(file_path) - current = path.parent if path.is_file() else path - - while current >= Path(self.workspace["root"]): - tsconfig = current / "tsconfig.json" - if tsconfig.exists(): - return tsconfig - current = current.parent - - return None - - def _has_forge(self) -> bool: - """Check if Forge is available""" - try: - result = self._run_command(["forge", "--version"]) - return result.returncode == 0 - except (OSError, FileNotFoundError): - return False - - def _has_hardhat(self) -> bool: - """Check if Hardhat is available""" - return ( - Path(self.workspace["root"], "hardhat.config.js").exists() - or Path(self.workspace["root"], "hardhat.config.ts").exists() - ) - - def _find_rust_package(self, file_path: str) -> Optional[str]: - """Find Cargo.toml for given file""" - path = Path(file_path) - current = path.parent if path.is_file() else path - - while current >= Path(self.workspace["root"]): - cargo_toml = current / "Cargo.toml" - if cargo_toml.exists(): - return str(cargo_toml) - current = current.parent - - return None - - def _get_rust_package_name(self, cargo_toml_path: str) -> Optional[str]: - """Extract package name from Cargo.toml""" - try: - import toml - - with open(cargo_toml_path) as f: - data = toml.load(f) - return data.get("package", {}).get("name") - except (OSError, ImportError, ValueError, KeyError): - return None - - -# MCP tool integration -async def build_tool_handler( - target: str, - language: str = "auto", - backend: str = "auto", - root: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - dry_run: bool = False, - opts: Optional[Dict[str, Any]] = None, -) -> Dict[str, Any]: - """MCP handler for build tool""" - - tool = BuildTool( - target=target, - language=language, - backend=backend, - root=root, - env=env, - dry_run=dry_run, - opts=opts or {}, - ) - - result = await tool.execute() - return result.dict() diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/__init__.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/__init__.py deleted file mode 100644 index 62dda4361..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Common utilities for Hanzo MCP tools. - -System tools (always available): -- tool_install: Install, update, reload tools dynamically -- tool_enable/tool_disable: Enable/disable tools at runtime -- tool_list: List available tools -- version: Get hanzo-mcp version info -- stats: Usage statistics - -Base classes and utilities: -- BaseTool: Base class for all tools -- ToolRegistry: Tool registration utilities -- PermissionManager: File access control - -Note: All actual tools (think, critic, dag, read, etc.) come from -hanzo-tools-* packages via entry points. See entrypoint_loader.py. -""" - -from hanzo_mcp.tools.common.base import BaseTool, ToolRegistry -from hanzo_mcp.tools.common.tool_install import ToolInstallTool, register_tool_install - -__all__ = [ - "BaseTool", - "ToolRegistry", - "ToolInstallTool", - "register_tool_install", -] diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/auto_timeout.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/auto_timeout.py deleted file mode 100644 index 01d2bbd6b..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/auto_timeout.py +++ /dev/null @@ -1,281 +0,0 @@ -"""Universal auto-timeout and backgrounding for all MCP tools. - -This module provides automatic timeout and backgrounding for any MCP tool operation -that takes longer than the configured threshold (default: 2 minutes). -""" - -import asyncio -import functools -import json -import os -import time -import uuid -from collections.abc import Awaitable -from pathlib import Path -from typing import Any, Callable, Optional - -from hanzo_async import append_file -from mcp.server.fastmcp import Context as MCPContext - -from .timeout_parser import format_timeout, parse_timeout - - -class MCPToolTimeoutManager: - """Manager for MCP tool timeouts and backgrounding.""" - - # Default timeout before auto-backgrounding (2 minutes) - DEFAULT_TIMEOUT = 120.0 - - # Environment variable to configure timeout - TIMEOUT_ENV_VAR = "HANZO_MCP_TOOL_TIMEOUT" - - def __init__(self, process_manager: Optional[Any] = None): - """Initialize the timeout manager. - - Args: - process_manager: Process manager for tracking background operations - """ - if process_manager is None: - # Lazy import to avoid circular imports - try: - from hanzo_tools.shell.base_process import ProcessManager - - self.process_manager = ProcessManager() - except ImportError: - # If ProcessManager is not available, disable backgrounding - self.process_manager = None - else: - self.process_manager = process_manager - - # Get timeout from environment or use default - env_timeout = os.getenv(self.TIMEOUT_ENV_VAR) - if env_timeout: - try: - self.timeout = parse_timeout(env_timeout) - except ValueError: - self.timeout = self.DEFAULT_TIMEOUT - else: - self.timeout = self.DEFAULT_TIMEOUT - - def _get_timeout_for_tool(self, tool_name: str) -> float: - """Get timeout setting for a specific tool. - - Args: - tool_name: Name of the tool - - Returns: - Timeout in seconds - """ - # Check for tool-specific timeout - env_var = f"HANZO_MCP_{tool_name.upper()}_TIMEOUT" - tool_timeout = os.getenv(env_var) - if tool_timeout: - try: - return parse_timeout(tool_timeout) - except ValueError: - pass - - return self.timeout - - async def _background_tool_execution( - self, - tool_func: Callable, - tool_name: str, - ctx: MCPContext, - process_id: str, - log_file: Path, - **params: Any, - ) -> None: - """Execute tool in background and log results. - - Uses aiofiles for non-blocking file I/O. - - Args: - tool_func: The tool function to execute - tool_name: Name of the tool - ctx: MCP context - process_id: Process identifier - log_file: Log file path - **params: Tool parameters - """ - try: - # Log start (async) - await append_file( - log_file, - f"=== Background execution started for {tool_name} ===\n" - f"Parameters: {json.dumps(params, indent=2, default=str)}\n" - f"Started at: {time.strftime('%Y-%m-%d %H:%M:%S')}\n\n", - ) - - # Execute the tool - result = await tool_func(ctx, **params) - - # Log completion (async) - await append_file( - log_file, - f"\n\n=== Tool execution completed ===\n" - f"Completed at: {time.strftime('%Y-%m-%d %H:%M:%S')}\n" - f"Result length: {len(str(result))} characters\n" - f"\n=== RESULT ===\n" - f"{str(result)}\n" - f"=== END RESULT ===\n", - ) - - # Mark as completed - self.process_manager.mark_completed(process_id, 0) - - except Exception as e: - # Log error (async) - await append_file( - log_file, - f"\n\n=== Tool execution failed ===\n" - f"Failed at: {time.strftime('%Y-%m-%d %H:%M:%S')}\n" - f"Error: {str(e)}\n" - f"Error type: {type(e).__name__}\n", - ) - - self.process_manager.mark_completed(process_id, 1) - - -def with_auto_timeout( - tool_name: str, timeout_manager: Optional[MCPToolTimeoutManager] = None -): - """Decorator to add automatic timeout and backgrounding to MCP tools. - - Args: - tool_name: Name of the tool (for logging and process tracking) - timeout_manager: Optional timeout manager instance - - Returns: - Decorator function - """ - if timeout_manager is None: - timeout_manager = MCPToolTimeoutManager() - - def decorator(func: Callable[..., Awaitable[Any]]) -> Callable[..., Awaitable[Any]]: - @functools.wraps(func) - async def wrapper(*args: Any, **params: Any) -> Any: - # Handle both method calls (with self) and function calls - # For methods: args = (self, ctx), For functions: args = (ctx,) - if len(args) >= 2: - # Method call: self, ctx, **params - self_or_ctx = args[0] - ctx = args[1] - - def call_func(): - return func(self_or_ctx, ctx, **params) - - elif len(args) == 1: - # Function call: ctx, **params - ctx = args[0] - - def call_func(): - return func(ctx, **params) - - else: - raise TypeError(f"Expected at least 1 argument (ctx), got {len(args)}") - - # Fast path for tests - skip timeout logic - if os.getenv("HANZO_MCP_FAST_TESTS") == "1": - return await call_func() - - # Get tool-specific timeout - tool_timeout = timeout_manager._get_timeout_for_tool(tool_name) - - # Create task for the tool execution - tool_task = asyncio.create_task(call_func()) - - try: - # Wait for completion with timeout - result = await asyncio.wait_for(tool_task, timeout=tool_timeout) - return result - - except asyncio.TimeoutError: - # Tool timed out - background it if process manager is available - if timeout_manager.process_manager is None: - # No process manager - just report timeout - timeout_formatted = format_timeout(tool_timeout) - return f"Operation timed out after {timeout_formatted}. Backgrounding unavailable." - - process_id = f"{tool_name}_{uuid.uuid4().hex[:8]}" - log_file = await timeout_manager.process_manager.create_log_file( - process_id - ) - - # Start background execution (need to reconstruct the call) - async def background_call(): - if len(args) >= 2: - return await func(args[0], ctx, **params) - else: - return await func(ctx, **params) - - asyncio.create_task( - timeout_manager._background_tool_execution( - background_call, tool_name, ctx, process_id, log_file, **params - ) - ) - - # Return backgrounding message - timeout_formatted = format_timeout(tool_timeout) - return ( - f"Operation automatically backgrounded after {timeout_formatted}\n" - f"Process ID: {process_id}\n" - f"Log file: {log_file}\n\n" - f"Use 'process --action logs --id {process_id}' to view results\n" - f"Use 'process --action kill --id {process_id}' to cancel\n\n" - f"The {tool_name} operation is continuing in the background..." - ) - - return wrapper - - return decorator - - -# Global timeout manager instance -_global_timeout_manager = None - - -def get_global_timeout_manager() -> MCPToolTimeoutManager: - """Get the global timeout manager instance. - - Returns: - Global timeout manager - """ - global _global_timeout_manager - if _global_timeout_manager is None: - _global_timeout_manager = MCPToolTimeoutManager() - return _global_timeout_manager - - -def set_global_timeout(timeout_seconds: float) -> None: - """Set the global timeout for all MCP tools. - - Args: - timeout_seconds: Timeout in seconds - """ - manager = get_global_timeout_manager() - manager.timeout = timeout_seconds - - -def set_tool_timeout(tool_name: str, timeout_seconds: float) -> None: - """Set timeout for a specific tool via environment variable. - - Args: - tool_name: Name of the tool - timeout_seconds: Timeout in seconds - """ - env_var = f"HANZO_MCP_{tool_name.upper()}_TIMEOUT" - os.environ[env_var] = str(timeout_seconds) - - -# Convenience decorator using global manager -def auto_timeout(tool_name: str): - """Convenience decorator using the global timeout manager. - - Args: - tool_name: Name of the tool - - Returns: - Decorator function - """ - return with_auto_timeout(tool_name, get_global_timeout_manager()) diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/base.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/base.py deleted file mode 100644 index 28b988595..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/base.py +++ /dev/null @@ -1,248 +0,0 @@ -"""Base classes for Hanzo AI tools. - -This module provides abstract base classes that define interfaces and common functionality -for all tools used in Hanzo AI. These abstractions help ensure consistent tool -behavior and provide a foundation for tool registration and management. -""" - -import functools -import inspect -from abc import ABC, abstractmethod -from collections.abc import Awaitable -from typing import Any, Callable, final - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_mcp.tools.common.auto_timeout import auto_timeout -from hanzo_mcp.tools.common.error_logger import log_call_signature_error, log_tool_error -from hanzo_mcp.tools.common.permissions import PermissionManager -from hanzo_mcp.tools.common.validation import ( - ValidationResult, - validate_path_parameter, -) - - -def with_error_logging(tool_name: str) -> Callable: - """Decorator to add comprehensive error logging to tool functions. - - Args: - tool_name: Name of the tool for logging purposes - - Returns: - Decorator function - """ - - def decorator(func: Callable[..., Awaitable[str]]) -> Callable[..., Awaitable[str]]: - @functools.wraps(func) - async def wrapper(*args: Any, **kwargs: Any) -> str: - try: - return await func(*args, **kwargs) - except TypeError as e: - # This often indicates a call signature mismatch - error_msg = str(e) - if "takes" in error_msg and "positional argument" in error_msg: - # Log call signature error - sig = inspect.signature(func) - expected = f"{func.__name__}{sig}" - actual = f"{func.__name__}(*args={args}, **kwargs={kwargs})" - log_call_signature_error(tool_name, expected, actual, e) - - # Log the error - log_tool_error( - tool_name, - e, - params=kwargs, - context="Call signature mismatch or type error", - ) - - # Return user-friendly error message - return ( - f"Error executing tool '{tool_name}': {error_msg}\n\n" - f"This error has been logged to ~/.hanzo/mcp/logs/ for debugging.\n" - f"Check ~/.hanzo/mcp/logs/{tool_name}-errors.log for details." - ) - except Exception as e: - # Log all other errors - log_tool_error(tool_name, e, params=kwargs) - - # Return error message - return ( - f"Error executing tool '{tool_name}': {str(e)}\n\n" - f"This error has been logged to ~/.hanzo/mcp/logs/ for debugging.\n" - f"Check ~/.hanzo/mcp/logs/{tool_name}-errors.log for details." - ) - - return wrapper - - return decorator - - -def handle_connection_errors( - func: Callable[..., Awaitable[str]], -) -> Callable[..., Awaitable[str]]: - """Decorator to handle connection errors in MCP tool functions. - - This decorator wraps tool functions to catch ClosedResourceError and other - connection-related exceptions that occur when the client disconnects. - - Args: - func: The async tool function to wrap - - Returns: - Wrapped function that handles connection errors gracefully - """ - - @functools.wraps(func) - async def wrapper(*args: Any, **kwargs: Any) -> str: - try: - return await func(*args, **kwargs) - except Exception as e: - # Check if this is a connection-related error - error_name = type(e).__name__ - if any( - name in error_name - for name in [ - "ClosedResourceError", - "ConnectionError", - "BrokenPipeError", - ] - ): - # Client has disconnected - log the error but don't crash - # Return a simple error message (though it likely won't be received) - return f"Client disconnected during operation: {error_name}" - else: - # Re-raise non-connection errors - raise - - return wrapper - - -class BaseTool(ABC): - """Abstract base class for all Hanzo AI tools. - - This class defines the core interface that all tools must implement, ensuring - consistency in how tools are registered, documented, and called. - """ - - @property - @abstractmethod - def name(self) -> str: - """Get the tool name. - - Returns: - The tool name as it will appear in the MCP server - """ - pass - - @property - @abstractmethod - def description(self) -> str: - """Get the tool description. - - Returns: - Detailed description of the tool's purpose and usage - """ - pass - - @abstractmethod - @auto_timeout("base") - async def call(self, ctx: MCPContext, **params: Any) -> Any: - """Execute the tool with the given parameters. - - Args: - ctx: MCP context for the tool call - **params: Tool parameters provided by the caller - - Returns: - Tool execution result as a string - """ - pass - - @abstractmethod - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server. - - This method must be implemented by each tool class to create a wrapper function - with explicitly defined parameters that calls this tool's call method. - The wrapper function is then registered with the MCP server. - - Args: - mcp_server: The FastMCP server instance - """ - pass - - -class FileSystemTool(BaseTool, ABC): - """Base class for filesystem-related tools. - - Provides common functionality for working with files and directories, - including permission checking and path validation. - """ - - def __init__(self, permission_manager: PermissionManager) -> None: - """Initialize filesystem tool. - - Args: - permission_manager: Permission manager for access control - """ - self.permission_manager: PermissionManager = permission_manager - - def validate_path(self, path: str, param_name: str = "path") -> ValidationResult: - """Validate a path parameter. - - Args: - path: Path to validate - param_name: Name of the parameter (for error messages) - - Returns: - Validation result containing validation status and error message if any - """ - return validate_path_parameter(path, param_name) - - def is_path_allowed(self, path: str) -> bool: - """Check if a path is allowed according to permission settings. - - Args: - path: Path to check - - Returns: - True if the path is allowed, False otherwise - """ - return self.permission_manager.is_path_allowed(path) - - -@final -class ToolRegistry: - """Registry for Hanzo AI tools. - - Provides functionality for registering tool implementations with an MCP server, - handling the conversion between tool classes and MCP tool functions. - """ - - @staticmethod - def register_tool(mcp_server: FastMCP, tool: BaseTool) -> None: - """Register a tool with the MCP server. - - Args: - mcp_server: The FastMCP server instance - tool: The tool to register - """ - # Check if tool is enabled before registering - # Import here to avoid circular imports - from hanzo_mcp.tools.common.tool_enable import ToolEnableTool - - if ToolEnableTool.is_tool_enabled(tool.name): - # Use the tool's register method which handles all the details - tool.register(mcp_server) - - @staticmethod - def register_tools(mcp_server: FastMCP, tools: list[BaseTool]) -> None: - """Register multiple tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - tools: List of tools to register - """ - for tool in tools: - ToolRegistry.register_tool(mcp_server, tool) diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/batch_tool.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/batch_tool.py deleted file mode 100644 index 0dc039a1e..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/batch_tool.py +++ /dev/null @@ -1,90 +0,0 @@ -"""Backward-compatible batch tool implementation. - -This module restores ``hanzo_mcp.tools.common.batch_tool.BatchTool`` for -existing callers and tests that still import it directly. -""" - -from __future__ import annotations - -import asyncio -from dataclasses import dataclass -from typing import Any - - -@dataclass -class _BatchResult: - index: int - tool_name: str - output: str - - -class BatchTool: - """Execute multiple tool invocations concurrently. - - The current server uses entry-point tools, but legacy code still imports - this class directly. Keep behavior simple and deterministic: - - preserves invocation order in output - - executes in parallel with a concurrency cap - - always returns a human-readable string - """ - - name = "batch" - description = "Run multiple tools in parallel" - - def __init__(self, tools: dict[str, Any], max_concurrency: int = 8): - self.tools = tools - self.max_concurrency = max(1, max_concurrency) - - async def call( - self, - ctx: Any, - description: str, - invocations: list[dict[str, Any]], - max_concurrency: int | None = None, - **_: Any, - ) -> str: - if not invocations: - return "Error: invocations cannot be empty" - - semaphore = asyncio.Semaphore(max_concurrency or self.max_concurrency) - - async def _run(index: int, invocation: dict[str, Any]) -> _BatchResult: - tool_name = str(invocation.get("tool_name", "")).strip() - payload = invocation.get("input", {}) - - if not isinstance(payload, dict): - payload = {} if payload is None else {"input": payload} - - if not tool_name: - return _BatchResult(index, "", "Error: tool_name is required") - - tool = self.tools.get(tool_name) - if tool is None: - return _BatchResult( - index, tool_name, f"Error: Tool '{tool_name}' not found" - ) - - async with semaphore: - try: - # Support both call(ctx, **kwargs) and call(ctx=ctx, **kwargs). - try: - result = await tool.call(ctx=ctx, **payload) - except TypeError: - result = await tool.call(ctx, **payload) - except Exception as exc: # noqa: BLE001 - tool errors should be captured - return _BatchResult(index, tool_name, f"Error: {exc}") - - return _BatchResult(index, tool_name, str(result)) - - tasks = [ - _run(i, invocation) for i, invocation in enumerate(invocations, start=1) - ] - results = await asyncio.gather(*tasks) - results.sort(key=lambda item: item.index) - - lines = [f"Batch: {description}", "results:"] - for item in results: - lines.append(f"Result {item.index}: {item.tool_name}") - lines.append(item.output) - - return "\n".join(lines) diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/cli_tool_factory.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/cli_tool_factory.py deleted file mode 100644 index 849658111..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/cli_tool_factory.py +++ /dev/null @@ -1,343 +0,0 @@ -"""Dynamic CLI tool factory for creating tools from shell commands at runtime. - -Allows exposing any CLI tool to Claude without creating a Python package. - -Usage: - cli_create(name="git", command="git", description="Git version control") - cli_create(name="docker", command="docker", description="Docker container management") - cli_create(name="kubectl", command="kubectl", description="Kubernetes CLI") - - cli_list() # List all dynamic CLI tools - cli_remove(name="git") # Remove a dynamic tool -""" - -from __future__ import annotations - -import asyncio -import json -import logging -import os -from dataclasses import asdict, dataclass, field -from pathlib import Path -from typing import TYPE_CHECKING, Any - -if TYPE_CHECKING: - from mcp.server import FastMCP - -logger = logging.getLogger(__name__) - -# Storage for dynamic CLI tool definitions -CLI_TOOLS_CONFIG = Path.home() / ".hanzo" / "mcp" / "cli_tools.json" - - -@dataclass -class CLIToolDefinition: - """Definition for a dynamic CLI tool.""" - - name: str - command: str - description: str - args_description: str = "Arguments to pass to the command" - timeout: int = 120 - working_dir: str | None = None - env: dict[str, str] = field(default_factory=dict) - enabled: bool = True - - -class CLIToolFactory: - """Factory for creating and managing dynamic CLI tools. - - Tools are persisted to ~/.hanzo/mcp/cli_tools.json and - automatically loaded on startup. - """ - - _instance: "CLIToolFactory | None" = None - - def __init__(self): - self._tools: dict[str, CLIToolDefinition] = {} - self._registered_handlers: dict[str, Any] = {} - self._mcp_server: "FastMCP | None" = None - self._load_config() - - @classmethod - def get_instance(cls) -> "CLIToolFactory": - """Get singleton instance.""" - if cls._instance is None: - cls._instance = CLIToolFactory() - return cls._instance - - def set_mcp_server(self, mcp_server: "FastMCP") -> None: - """Set the MCP server for tool registration.""" - self._mcp_server = mcp_server - # Register all existing tools - for tool_def in self._tools.values(): - if tool_def.enabled: - self._register_tool(tool_def) - - def _load_config(self) -> None: - """Load CLI tool definitions from disk.""" - if CLI_TOOLS_CONFIG.exists(): - try: - with open(CLI_TOOLS_CONFIG) as f: - data = json.load(f) - for name, tool_data in data.get("tools", {}).items(): - self._tools[name] = CLIToolDefinition(**tool_data) - logger.info(f"Loaded {len(self._tools)} dynamic CLI tools") - except Exception as e: - logger.warning(f"Failed to load CLI tools config: {e}") - - def _save_config(self) -> None: - """Save CLI tool definitions to disk.""" - CLI_TOOLS_CONFIG.parent.mkdir(parents=True, exist_ok=True) - data = {"tools": {name: asdict(tool) for name, tool in self._tools.items()}} - with open(CLI_TOOLS_CONFIG, "w") as f: - json.dump(data, f, indent=2) - - def _register_tool(self, tool_def: CLIToolDefinition) -> None: - """Register a CLI tool with the MCP server.""" - if not self._mcp_server: - return - - # Create the async handler - async def cli_handler( - args: str = "", - timeout: int | None = None, - cwd: str | None = None, - ) -> str: - """Execute the CLI command.""" - cmd = tool_def.command - if args: - cmd = f"{cmd} {args}" - - effective_timeout = timeout or tool_def.timeout - effective_cwd = cwd or tool_def.working_dir - - # Build environment - env = os.environ.copy() - env.update(tool_def.env) - - try: - proc = await asyncio.create_subprocess_shell( - cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - cwd=effective_cwd, - env=env, - ) - - try: - stdout, stderr = await asyncio.wait_for( - proc.communicate(), timeout=effective_timeout - ) - except asyncio.TimeoutError: - proc.kill() - await proc.wait() - return f"Command timed out after {effective_timeout}s" - - result = stdout.decode() if stdout else "" - if stderr: - err = stderr.decode() - if err: - result += f"\n[stderr]\n{err}" - - if proc.returncode != 0: - result = f"[exit code: {proc.returncode}]\n{result}" - - return result or "(no output)" - - except Exception as e: - return f"Error executing command: {e}" - - # Register with MCP server - try: - self._mcp_server.tool( - name=tool_def.name, - description=f"{tool_def.description}\n\nCommand: {tool_def.command}", - )(cli_handler) - self._registered_handlers[tool_def.name] = cli_handler - logger.info(f"Registered dynamic CLI tool: {tool_def.name}") - except Exception as e: - logger.error(f"Failed to register CLI tool {tool_def.name}: {e}") - - def create( - self, - name: str, - command: str, - description: str, - args_description: str = "Arguments to pass to the command", - timeout: int = 120, - working_dir: str | None = None, - env: dict[str, str] | None = None, - ) -> dict[str, Any]: - """Create a new dynamic CLI tool. - - Args: - name: Tool name (e.g., "git", "docker") - command: Base command to execute (e.g., "git", "docker") - description: Tool description for Claude - args_description: Description of the args parameter - timeout: Default timeout in seconds - working_dir: Default working directory - env: Additional environment variables - - Returns: - Result dict with success status - """ - if name in self._tools: - return {"success": False, "error": f"Tool '{name}' already exists"} - - tool_def = CLIToolDefinition( - name=name, - command=command, - description=description, - args_description=args_description, - timeout=timeout, - working_dir=working_dir, - env=env or {}, - ) - - self._tools[name] = tool_def - self._save_config() - - # Register if server is available - if self._mcp_server: - self._register_tool(tool_def) - - return { - "success": True, - "name": name, - "command": command, - "message": f"Created CLI tool '{name}'. Use {name}(args='...') to execute.", - } - - def remove(self, name: str) -> dict[str, Any]: - """Remove a dynamic CLI tool.""" - if name not in self._tools: - return {"success": False, "error": f"Tool '{name}' not found"} - - del self._tools[name] - self._registered_handlers.pop(name, None) - self._save_config() - - return {"success": True, "name": name, "message": f"Removed CLI tool '{name}'"} - - def list(self) -> list[dict[str, Any]]: - """List all dynamic CLI tools.""" - return [ - { - "name": tool.name, - "command": tool.command, - "description": tool.description, - "enabled": tool.enabled, - } - for tool in self._tools.values() - ] - - def enable(self, name: str) -> dict[str, Any]: - """Enable a CLI tool.""" - if name not in self._tools: - return {"success": False, "error": f"Tool '{name}' not found"} - - self._tools[name].enabled = True - self._save_config() - - if self._mcp_server and name not in self._registered_handlers: - self._register_tool(self._tools[name]) - - return {"success": True, "name": name} - - def disable(self, name: str) -> dict[str, Any]: - """Disable a CLI tool.""" - if name not in self._tools: - return {"success": False, "error": f"Tool '{name}' not found"} - - self._tools[name].enabled = False - self._save_config() - - # Note: Can't unregister from FastMCP, but tool won't be re-registered on restart - return { - "success": True, - "name": name, - "message": "Tool disabled. Restart server to remove.", - } - - async def get_help(self, name: str) -> str: - """Get help text for a CLI tool by running --help.""" - if name not in self._tools: - return f"Tool '{name}' not found" - - tool = self._tools[name] - - try: - proc = await asyncio.create_subprocess_shell( - f"{tool.command} --help", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=10) - return stdout.decode() or stderr.decode() or "(no help available)" - except Exception as e: - return f"Error getting help: {e}" - - -def register_cli_factory_tools(mcp_server: "FastMCP") -> list: - """Register CLI factory management tools with MCP server.""" - factory = CLIToolFactory.get_instance() - factory.set_mcp_server(mcp_server) - - @mcp_server.tool( - name="cli_create", - description="""Create a new CLI tool that wraps a shell command. - -This allows you to expose any CLI tool to Claude as a first-class tool. - -Examples: - cli_create(name="git", command="git", description="Git version control") - cli_create(name="docker", command="docker", description="Docker containers") - cli_create(name="kubectl", command="kubectl", description="Kubernetes CLI") - cli_create(name="aws", command="aws", description="AWS CLI", timeout=300) -""", - ) - async def cli_create( - name: str, - command: str, - description: str, - timeout: int = 120, - ) -> str: - result = factory.create( - name=name, - command=command, - description=description, - timeout=timeout, - ) - return json.dumps(result, indent=2) - - @mcp_server.tool( - name="cli_list", - description="List all dynamic CLI tools that have been created.", - ) - async def cli_list() -> str: - tools = factory.list() - if not tools: - return "No dynamic CLI tools configured. Use cli_create() to add one." - - lines = ["# Dynamic CLI Tools\n"] - for tool in tools: - status = "โœ“" if tool["enabled"] else "โœ—" - lines.append(f"{status} **{tool['name']}** - `{tool['command']}`") - lines.append(f" {tool['description']}\n") - return "\n".join(lines) - - @mcp_server.tool(name="cli_remove", description="Remove a dynamic CLI tool.") - async def cli_remove(name: str) -> str: - result = factory.remove(name) - return json.dumps(result, indent=2) - - @mcp_server.tool( - name="cli_help", description="Get help text for a CLI tool by running --help." - ) - async def cli_help(name: str) -> str: - return await factory.get_help(name) - - # Return empty list since these are registered directly - return [] diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/context.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/context.py deleted file mode 100644 index 28bbe82a7..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/context.py +++ /dev/null @@ -1,180 +0,0 @@ -"""Enhanced Context for Hanzo AI tools. - -This module provides an enhanced Context class that wraps the MCP Context -and adds additional functionality specific to Hanzo tools. -""" - -from collections.abc import Iterable -from typing import ClassVar, final - -from mcp.server.fastmcp import Context as MCPContext -from mcp.server.lowlevel.helper_types import ReadResourceContents - - -@final -class ToolContext: - """Enhanced context for Hanzo AI tools. - - This class wraps the MCP Context and adds additional functionality - for tracking tool execution, progress reporting, and resource access. - """ - - # Track all active contexts for debugging - _active_contexts: ClassVar[set["ToolContext"]] = set() - - def __init__(self, mcp_context: MCPContext) -> None: - """Initialize the tool context. - - Args: - mcp_context: The underlying MCP Context - """ - self._mcp_context: MCPContext = mcp_context - self._tool_name: str | None = None - self._execution_id: str | None = None - - # Add to active contexts - ToolContext._active_contexts.add(self) - - def __del__(self) -> None: - """Clean up when the context is destroyed.""" - # Remove from active contexts - ToolContext._active_contexts.discard(self) - - @property - def mcp_context(self) -> MCPContext: - """Get the underlying MCP Context. - - Returns: - The MCP Context - """ - return self._mcp_context - - @property - def request_id(self) -> str: - """Get the request ID from the MCP context. - - Returns: - The request ID - """ - return self._mcp_context.request_id - - @property - def client_id(self) -> str | None: - """Get the client ID from the MCP context. - - Returns: - The client ID - """ - return self._mcp_context.client_id - - async def set_tool_info( - self, tool_name: str, execution_id: str | None = None - ) -> None: - """Set information about the currently executing tool. - - Args: - tool_name: The name of the tool being executed - execution_id: Optional unique execution ID - """ - self._tool_name = tool_name - self._execution_id = execution_id - - async def info(self, message: str) -> None: - """Log an informational message. - - Args: - message: The message to log - """ - try: - await self._mcp_context.info(self._format_message(message)) - except Exception: - # Silently ignore errors when client has disconnected - pass - - async def debug(self, message: str) -> None: - """Log a debug message. - - Args: - message: The message to log - """ - try: - await self._mcp_context.debug(self._format_message(message)) - except Exception: - # Silently ignore errors when client has disconnected - pass - - async def warning(self, message: str) -> None: - """Log a warning message. - - Args: - message: The message to log - """ - try: - await self._mcp_context.warning(self._format_message(message)) - except Exception: - # Silently ignore errors when client has disconnected - pass - - async def error(self, message: str) -> None: - """Log an error message. - - Args: - message: The message to log - """ - try: - await self._mcp_context.error(self._format_message(message)) - except Exception: - # Silently ignore errors when client has disconnected - pass - - def _format_message(self, message: str) -> str: - """Format a message with tool information if available. - - Args: - message: The original message - - Returns: - The formatted message - """ - if self._tool_name: - if self._execution_id: - return f"[{self._tool_name}:{self._execution_id}] {message}" - return f"[{self._tool_name}] {message}" - return message - - async def report_progress(self, current: int, total: int) -> None: - """Report progress to the client. - - Args: - current: Current progress value - total: Total progress value - """ - try: - await self._mcp_context.report_progress(current, total) - except Exception: - # Silently ignore errors when client has disconnected - pass - - async def read_resource(self, uri: str) -> Iterable[ReadResourceContents]: - """Read a resource via the MCP protocol. - - Args: - uri: The resource URI - - Returns: - A tuple of (content, mime_type) - """ - return await self._mcp_context.read_resource(uri) - - -# Factory function to create a ToolContext from an MCP Context -def create_tool_context(mcp_context: MCPContext) -> ToolContext: - """Create a ToolContext from an MCP Context. - - Args: - mcp_context: The MCP Context - - Returns: - A new ToolContext - """ - return ToolContext(mcp_context) diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/context_fix.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/context_fix.py deleted file mode 100644 index 51468e914..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/context_fix.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Context handling fix for MCP tools. - -This module provides backward compatibility by re-exporting the -context normalization utilities from the decorators module. - -DEPRECATED: Use hanzo_mcp.tools.common.decorators directly. -""" - -# Re-export for backward compatibility -from hanzo_mcp.tools.common.decorators import ( - MockContext, - with_context_normalization, -) -from hanzo_mcp.tools.common.decorators import ( - _is_valid_context as is_valid_context, -) - - -# Backward compatibility function -def normalize_context(ctx): - """Normalize context - backward compatibility wrapper. - - DEPRECATED: Use decorators.with_context_normalization instead. - """ - if is_valid_context(ctx): - return ctx - return MockContext() - - -__all__ = ["MockContext", "normalize_context", "with_context_normalization"] diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/decorators.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/decorators.py deleted file mode 100644 index 556433faa..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/decorators.py +++ /dev/null @@ -1,210 +0,0 @@ -"""Decorators for MCP tools. - -This module provides decorators that handle common cross-cutting concerns -for MCP tools, such as context normalization and error handling. -""" - -import functools -import inspect -from typing import Any, Callable, TypeVar, cast - -F = TypeVar("F", bound=Callable[..., Any]) - - -class MockContext: - """Mock context for when no real context is available. - - This is used when tools are called externally through the MCP protocol - and the Context parameter is not properly serialized. - """ - - def __init__(self): - self.request_id = "external-request" - self.client_id = "external-client" - - async def info(self, message: str) -> None: - """Mock info logging - no-op for external calls.""" - pass - - async def debug(self, message: str) -> None: - """Mock debug logging - no-op for external calls.""" - pass - - async def warning(self, message: str) -> None: - """Mock warning logging - no-op for external calls.""" - pass - - async def error(self, message: str) -> None: - """Mock error logging - no-op for external calls.""" - pass - - async def report_progress(self, current: int, total: int) -> None: - """Mock progress reporting - no-op for external calls.""" - pass - - async def read_resource(self, uri: str) -> Any: - """Mock resource reading - returns empty result.""" - return [] - - -def with_context_normalization(func: F) -> F: - """Decorator that normalizes the context parameter for MCP tools. - - This decorator intercepts the ctx parameter and ensures it's a valid - MCPContext object, even when called externally where it might be - passed as a string, dict, or None. - - Usage: - @server.tool() - @with_context_normalization - async def my_tool(ctx: MCPContext, param: str) -> str: - # ctx is guaranteed to be a valid context object - await ctx.info("Processing...") - return "result" - - Args: - func: The async function to decorate - - Returns: - The decorated function with context normalization - """ - - @functools.wraps(func) - async def wrapper(*args: Any, **kwargs: Any) -> Any: - # Get function signature to find ctx parameter - sig = inspect.signature(func) - params = list(sig.parameters.keys()) - - # Handle ctx in kwargs - if "ctx" in kwargs: - ctx_value = kwargs["ctx"] - if not _is_valid_context(ctx_value): - kwargs["ctx"] = MockContext() - - # Handle ctx in args (positional) - elif "ctx" in params: - ctx_index = params.index("ctx") - if ctx_index < len(args): - ctx_value = args[ctx_index] - if not _is_valid_context(ctx_value): - args_list = list(args) - args_list[ctx_index] = MockContext() - args = tuple(args_list) - - # Call the original function - return await func(*args, **kwargs) - - return cast(F, wrapper) - - -def _is_valid_context(ctx: Any) -> bool: - """Check if an object is a valid MCPContext. - - Args: - ctx: The object to check - - Returns: - True if ctx is a valid context object - """ - # Check for required context methods - return ( - hasattr(ctx, "info") - and hasattr(ctx, "debug") - and hasattr(ctx, "warning") - and hasattr(ctx, "error") - and hasattr(ctx, "report_progress") - and - # Ensure they're callable - callable(getattr(ctx, "info", None)) - and callable(getattr(ctx, "debug", None)) - ) - - -def mcp_tool( - server: Any, name: str | None = None, description: str | None = None -) -> Callable[[F], F]: - """Enhanced MCP tool decorator that includes context normalization. - - This decorator combines the standard MCP tool registration with - automatic context normalization, providing a single-point solution - for all tools. - - Usage: - @mcp_tool(server, name="my_tool", description="Does something") - async def my_tool(ctx: MCPContext, param: str) -> str: - await ctx.info("Processing...") - return "result" - - Args: - server: The MCP server instance - name: Optional tool name (defaults to function name) - description: Optional tool description - - Returns: - Decorator function - """ - - def decorator(func: F) -> F: - # Apply context normalization first - normalized_func = with_context_normalization(func) - - # Then apply the server's tool decorator - if hasattr(server, "tool"): - # Use the server's tool decorator - server_decorator = server.tool(name=name, description=description) - return server_decorator(normalized_func) - else: - # Fallback if server doesn't have tool method - return normalized_func - - return decorator - - -def create_tool_handler(server: Any, tool: Any) -> Callable[[], None]: - """Create a standardized tool registration handler. - - This function creates a registration method that automatically applies - context normalization to any tool handler registered with the server. - - Usage: - class MyTool(BaseTool): - def register(self, mcp_server): - register = create_tool_handler(mcp_server, self) - register() - - Args: - server: The MCP server instance - tool: The tool instance with name, description, and handler - - Returns: - A function that registers the tool with context normalization - """ - - def register_with_normalization(): - # Get the original register method - original_register = tool.__class__.register - - # Temporarily replace server.tool to wrap with normalization - original_tool_decorator = server.tool - - def normalized_tool_decorator(name=None, description=None): - def decorator(func): - # Apply context normalization - normalized = with_context_normalization(func) - # Apply original decorator - return original_tool_decorator(name=name, description=description)( - normalized - ) - - return decorator - - # Monkey-patch temporarily - server.tool = normalized_tool_decorator - try: - # Call original register - original_register(tool, server) - finally: - # Restore original - server.tool = original_tool_decorator - - return register_with_normalization diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/enhanced_base.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/enhanced_base.py deleted file mode 100644 index 8034afa6a..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/enhanced_base.py +++ /dev/null @@ -1,101 +0,0 @@ -"""Enhanced base classes for MCP tools with automatic context handling. - -This module provides enhanced base classes that automatically handle -context normalization and other cross-cutting concerns, ensuring -consistent behavior across all tools. -""" - -import inspect -from abc import ABC, abstractmethod -from typing import Any, get_type_hints - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_mcp.tools.common.base import BaseTool -from hanzo_mcp.tools.common.decorators import with_context_normalization - - -class EnhancedBaseTool(BaseTool, ABC): - """Enhanced base class for MCP tools with automatic context normalization. - - This base class automatically wraps the tool registration to include - context normalization, ensuring that all tools handle external calls - properly without requiring manual decoration or copy-pasted code. - """ - - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with automatic context normalization. - - This method automatically applies context normalization to the tool - handler, ensuring it works properly when called externally. - - Args: - mcp_server: The FastMCP server instance - """ - # Get the tool method from the subclass - tool_method = self._create_tool_handler() - - # Apply context normalization decorator - normalized_method = with_context_normalization(tool_method) - - # Register with the server - mcp_server.tool(name=self.name, description=self.description)(normalized_method) - - @abstractmethod - def _create_tool_handler(self) -> Any: - """Create the tool handler function. - - Subclasses must implement this to return an async function - that will be registered as the tool handler. - - Returns: - An async function that handles tool calls - """ - pass - - -class AutoRegisterTool(BaseTool, ABC): - """Base class that automatically generates tool handlers from the call method. - - This base class inspects the call method signature and automatically - creates a properly typed tool handler with context normalization. - """ - - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with automatic handler generation. - - This method inspects the call method signature and automatically - creates a tool handler with the correct parameters and types. - - Args: - mcp_server: The FastMCP server instance - """ - # Get the call method signature - call_method = self.call - sig = inspect.signature(call_method) - - # Get type hints for proper typing - get_type_hints(call_method) - - # Create a dynamic handler function - tool_self = self - - # Build the handler dynamically based on the call signature - params = list(sig.parameters.items()) - - # Skip 'self' and 'ctx' parameters - [(name, param) for name, param in params if name not in ("self", "ctx")] - - # Create the handler function dynamically - async def handler(ctx: MCPContext, **kwargs: Any) -> Any: - # Call the tool's call method with the context and parameters - return await tool_self.call(ctx, **kwargs) - - # Apply context normalization - normalized_handler = with_context_normalization(handler) - - # Register with the server - mcp_server.tool(name=self.name, description=self.description)( - normalized_handler - ) diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/entrypoint_loader.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/entrypoint_loader.py deleted file mode 100644 index 1ea6a484a..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/entrypoint_loader.py +++ /dev/null @@ -1,315 +0,0 @@ -"""Entry-point based tool loader for hanzo-mcp. - -Discovers and loads tools from installed hanzo-tools-* packages using -Python entry points. This enables dynamic tool loading without code duplication. - -The entry point group is "hanzo.tools" and each package exports: - [project.entry-points."hanzo.tools"] - package_name = "hanzo_tools.package:TOOLS" - -Where TOOLS is a list of BaseTool subclasses. -""" - -from __future__ import annotations - -import logging -import sys -from importlib.metadata import entry_points -from typing import TYPE_CHECKING, Any - -if TYPE_CHECKING: - from hanzo_tools.core import BaseTool, PermissionManager - from mcp.server import FastMCP - -logger = logging.getLogger(__name__) - -# Entry point group name -TOOLS_ENTRY_POINT_GROUP = "hanzo.tools" - -# Package to tool prefix mapping (for enable/disable) -# HIP-0300 Operator Lattice: 16 tools organized by axis -PACKAGE_TOOL_PREFIXES: dict[str, list[str]] = { - # Core operators (HIP-0300) - "filesystem": ["fs"], # Bytes + Paths axis - "core": ["id"], # Identity axis (hash, uri, ref, verify) - "code": ["code"], # Symbols + Structure axis (parse, transform, summarize) - "shell": ["exec"], # Execution axis - "vcs": ["git"], # History + Diffs axis - "test": ["test"], # Validation axis (check, build, test) - "net": ["fetch"], # Network axis (search, fetch, download, crawl) - "plan": ["plan"], # Orchestration axis (intent, route, compose) - # Control surfaces - "browser": ["browser"], # Web content control (Playwright/extension) - "computer": ["computer"], # OS/desktop control (computer use) - "ui": ["ui"], # UI component registry (browse, search, install) - # Extended operators - "lsp": ["lsp"], # Semantic stream (diagnostics, code_actions) - "memory": ["memory"], # Knowledge persistence - "todo": ["tasks"], # Task tracking - "reasoning": ["think", "critic"], - "refactor": ["refactor"], - "database": ["sql", "graph"], - "agent": ["agent", "zen", "review"], - "jupyter": ["jupyter"], - "editor": ["neovim_edit", "neovim_command", "neovim_session"], - "llm": ["llm", "consensus"], - "vector": ["index", "vector_index", "vector_search"], - "config": ["config", "mode", "workspace"], - "mcp_tools": ["mcp"], - "api": ["api", "hanzo"], - "auth": ["auth"], - "kms": ["kms"], - "paas": ["paas"], - "billing": ["billing"], - "commerce": ["commerce"], - "iam_tools": ["iam"], - "ingress": ["ingress"], - "mpc": ["mpc"], - "team": ["team"], -} - - -class EntryPointToolLoader: - """Loads tools from hanzo-tools-* packages via entry points. - - This loader discovers installed tool packages and dynamically loads - and registers their tools with the MCP server. - """ - - def __init__(self, permission_manager: "PermissionManager" | None = None): - """Initialize the loader. - - Args: - permission_manager: Optional permission manager for file tools - """ - self.permission_manager = permission_manager - self._discovered_packages: dict[str, Any] = {} - self._loaded_tools: dict[str, "BaseTool"] = {} - - def _get_tool_name(self, tool_class: type) -> str: - """Extract tool name from class, handling @property decorators. - - When 'name' is defined as a @property, we need to instantiate - the class to get the actual value. - """ - # Check if name is a property in the class hierarchy - for klass in tool_class.__mro__: - if "name" in getattr(klass, "__dict__", {}): - attr = klass.__dict__["name"] - if isinstance(attr, property): - # Need to instantiate to get property value - try: - instance = tool_class() - name = getattr(instance, "name", None) - if isinstance(name, str): - return name - except Exception: - pass - # Fall back to class name - return tool_class.__name__.lower().replace("tool", "") - break - - # Try class-level attribute - name = getattr(tool_class, "name", None) - if isinstance(name, str): - return name - - # Fall back to class name - return tool_class.__name__.lower().replace("tool", "") - - def discover_packages(self) -> dict[str, list[str]]: - """Discover installed hanzo-tools-* packages. - - Returns: - Dict mapping package name to list of tool names - """ - discovered = {} - - # Get entry points for hanzo.tools group - try: - if sys.version_info >= (3, 10): - eps = entry_points(group=TOOLS_ENTRY_POINT_GROUP) - else: - # Python 3.9 compatibility - eps = entry_points().get(TOOLS_ENTRY_POINT_GROUP, []) - - for ep in eps: - try: - # Load the TOOLS list from the entry point - tools_list = ep.load() - - if isinstance(tools_list, list): - tool_names = [] - for tool_class in tools_list: - name = self._get_tool_name(tool_class) - tool_names.append(name) - - discovered[ep.name] = tool_names - self._discovered_packages[ep.name] = tools_list - logger.debug( - f"Discovered package '{ep.name}' with tools: {tool_names}" - ) - - except Exception as e: - logger.warning(f"Failed to load entry point '{ep.name}': {e}") - - except Exception as e: - logger.error(f"Failed to discover entry points: {e}") - - return discovered - - def load_package( - self, - package_name: str, - mcp_server: "FastMCP", - enabled_tools: dict[str, bool] | None = None, - **kwargs: Any, - ) -> list["BaseTool"]: - """Load tools from a specific package. - - Args: - package_name: Name of the package (e.g., "filesystem", "shell") - mcp_server: The FastMCP server to register tools with - enabled_tools: Dict of tool_name -> enabled state - **kwargs: Additional arguments passed to tool registration - - Returns: - List of registered BaseTool instances - """ - if package_name not in self._discovered_packages: - logger.warning(f"Package '{package_name}' not discovered") - return [] - - tools_list = self._discovered_packages[package_name] - registered = [] - enabled_tools = enabled_tools or {} - - for tool_class in tools_list: - tool_name = self._get_tool_name(tool_class) - - # Check if tool is enabled - if not enabled_tools.get(tool_name, True): - logger.debug(f"Skipping disabled tool: {tool_name}") - continue - - try: - # Try different instantiation patterns - if self.permission_manager and hasattr(tool_class, "__init__"): - # Check if tool accepts permission_manager - import inspect - - sig = inspect.signature(tool_class.__init__) - params = list(sig.parameters.keys()) - - if "permission_manager" in params: - tool = tool_class(permission_manager=self.permission_manager) - else: - tool = tool_class() - else: - tool = tool_class() - - # Register with MCP server - if hasattr(tool, "register"): - tool.register(mcp_server) - - self._loaded_tools[tool_name] = tool - registered.append(tool) - logger.debug(f"Registered tool: {tool_name}") - - except Exception as e: - logger.warning(f"Failed to register tool '{tool_name}': {e}") - - return registered - - def load_all( - self, - mcp_server: "FastMCP", - enabled_tools: dict[str, bool] | None = None, - enabled_packages: dict[str, bool] | None = None, - **kwargs: Any, - ) -> dict[str, "BaseTool"]: - """Load all discovered tools. - - Args: - mcp_server: The FastMCP server to register tools with - enabled_tools: Dict of tool_name -> enabled state - enabled_packages: Dict of package_name -> enabled state - **kwargs: Additional arguments passed to tool registration - - Returns: - Dict mapping tool name to BaseTool instance - """ - if not self._discovered_packages: - self.discover_packages() - - enabled_tools = enabled_tools or {} - enabled_packages = enabled_packages or {} - - for package_name in self._discovered_packages: - # Check if package is enabled - if not enabled_packages.get(package_name, True): - logger.debug(f"Skipping disabled package: {package_name}") - continue - - self.load_package( - package_name, - mcp_server, - # Apply global resolved tool state directly. Package-prefix mapping is - # advisory for UX/grouping but not authoritative for runtime gating. - enabled_tools=enabled_tools, - **kwargs, - ) - - return self._loaded_tools - - def get_tool(self, name: str) -> "BaseTool | None": - """Get a loaded tool by name.""" - return self._loaded_tools.get(name) - - def list_tools(self) -> list[str]: - """List all loaded tool names.""" - return list(self._loaded_tools.keys()) - - def list_packages(self) -> list[str]: - """List all discovered package names.""" - return list(self._discovered_packages.keys()) - - -def discover_tools() -> dict[str, list[str]]: - """Discover all available tools from installed packages. - - Returns: - Dict mapping package name to list of tool names - """ - loader = EntryPointToolLoader() - return loader.discover_packages() - - -def register_tools_from_entrypoints( - mcp_server: "FastMCP", - permission_manager: "PermissionManager" | None = None, - enabled_tools: dict[str, bool] | None = None, - enabled_packages: dict[str, bool] | None = None, - **kwargs: Any, -) -> dict[str, "BaseTool"]: - """Register tools from all discovered hanzo-tools-* packages. - - This is the main entry point for loading tools via entry points. - - Args: - mcp_server: The FastMCP server to register tools with - permission_manager: Optional permission manager for file tools - enabled_tools: Dict of tool_name -> enabled state - enabled_packages: Dict of package_name -> enabled state - **kwargs: Additional arguments passed to tool registration - - Returns: - Dict mapping tool name to BaseTool instance - """ - loader = EntryPointToolLoader(permission_manager=permission_manager) - return loader.load_all( - mcp_server, - enabled_tools=enabled_tools, - enabled_packages=enabled_packages, - **kwargs, - ) diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/error_logger.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/error_logger.py deleted file mode 100644 index e3af13175..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/error_logger.py +++ /dev/null @@ -1,302 +0,0 @@ -"""Centralized error logging for MCP tools. - -This module provides comprehensive error logging for all MCP tool operations, -writing errors to ~/.hanzo/mcp/logs/ for debugging and analysis. -""" - -import json -import logging -import traceback -from datetime import datetime -from pathlib import Path -from typing import Any, Optional - - -class MCPErrorLogger: - """Centralized error logger for MCP tools.""" - - def __init__(self, log_dir: Optional[Path] = None): - """Initialize the error logger. - - Args: - log_dir: Directory for log files (default: ~/.hanzo/mcp/logs/) - """ - if log_dir is None: - log_dir = Path.home() / ".hanzo" / "mcp" / "logs" - - self.log_dir = log_dir - self.log_dir.mkdir(parents=True, exist_ok=True) - - # Create daily log file - today = datetime.now().strftime("%Y-%m-%d") - self.log_file = self.log_dir / f"mcp-errors-{today}.log" - - # Also create a general errors file - self.general_log_file = self.log_dir / "errors.log" - - # Set up Python logging - self._setup_logging() - - def _setup_logging(self): - """Set up Python logging infrastructure.""" - # Create logger - self.logger = logging.getLogger("hanzo_mcp.errors") - self.logger.setLevel(logging.DEBUG) - - # Prevent duplicate handlers - if self.logger.handlers: - return - - # File handler for daily logs - file_handler = logging.FileHandler(self.log_file) - file_handler.setLevel(logging.ERROR) - - # File handler for general log - general_handler = logging.FileHandler(self.general_log_file) - general_handler.setLevel(logging.ERROR) - - # Formatter - formatter = logging.Formatter( - "%(asctime)s - %(name)s - %(levelname)s - %(message)s", - datefmt="%Y-%m-%d %H:%M:%S", - ) - file_handler.setFormatter(formatter) - general_handler.setFormatter(formatter) - - # Add handlers - self.logger.addHandler(file_handler) - self.logger.addHandler(general_handler) - - def log_tool_error( - self, - tool_name: str, - error: Exception, - params: Optional[dict[str, Any]] = None, - context: Optional[str] = None, - ): - """Log a tool execution error. - - Args: - tool_name: Name of the tool that errored - error: The exception that was raised - params: Tool parameters (will be sanitized) - context: Additional context about the error - """ - error_data = { - "timestamp": datetime.now().isoformat(), - "tool_name": tool_name, - "error_type": type(error).__name__, - "error_message": str(error), - "traceback": traceback.format_exc(), - "params": self._sanitize_params(params) if params else None, - "context": context, - } - - # Write to JSON log for structured parsing - json_log_file = ( - self.log_dir / f"tool-errors-{datetime.now().strftime('%Y-%m-%d')}.jsonl" - ) - try: - with open(json_log_file, "a") as f: - json.dump(error_data, f) - f.write("\n") - except Exception as e: - # If JSON logging fails, at least log that - self.logger.error(f"Failed to write JSON log: {e}") - - # Also log to standard logger - self.logger.error( - f"Tool '{tool_name}' error: {type(error).__name__}: {str(error)}", - extra={"tool": tool_name, "params": error_data.get("params")}, - ) - - # Write detailed error to tool-specific file - tool_log_file = self.log_dir / f"{tool_name}-errors.log" - try: - with open(tool_log_file, "a") as f: - f.write(f"\n{'=' * 80}\n") - f.write(f"ERROR at {error_data['timestamp']}\n") - f.write(f"{'=' * 80}\n") - f.write(f"Tool: {tool_name}\n") - f.write(f"Error Type: {error_data['error_type']}\n") - f.write(f"Error Message: {error_data['error_message']}\n") - if context: - f.write(f"Context: {context}\n") - if params: - f.write( - f"\nParameters:\n{json.dumps(error_data['params'], indent=2)}\n" - ) - f.write(f"\nTraceback:\n{error_data['traceback']}\n") - except Exception as e: - self.logger.error(f"Failed to write tool-specific log: {e}") - - def log_call_signature_error( - self, - tool_name: str, - expected_signature: str, - actual_call: str, - error: Exception, - ): - """Log an error related to incorrect tool call signature. - - Args: - tool_name: Name of the tool - expected_signature: Expected function signature - actual_call: How the tool was actually called - error: The exception that was raised - """ - error_data = { - "timestamp": datetime.now().isoformat(), - "tool_name": tool_name, - "error_type": "CallSignatureError", - "expected_signature": expected_signature, - "actual_call": actual_call, - "error_message": str(error), - "traceback": traceback.format_exc(), - } - - # Write to signature errors log - sig_log_file = self.log_dir / "signature-errors.log" - try: - with open(sig_log_file, "a") as f: - f.write(f"\n{'=' * 80}\n") - f.write(f"CALL SIGNATURE ERROR at {error_data['timestamp']}\n") - f.write(f"{'=' * 80}\n") - f.write(f"Tool: {tool_name}\n") - f.write(f"Expected: {expected_signature}\n") - f.write(f"Actual: {actual_call}\n") - f.write(f"Error: {error_data['error_message']}\n") - f.write(f"\nTraceback:\n{error_data['traceback']}\n") - except Exception as e: - self.logger.error(f"Failed to write signature error log: {e}") - - # Also log as regular tool error - self.log_tool_error( - tool_name, - error, - context=f"Call signature mismatch - Expected: {expected_signature}, Got: {actual_call}", - ) - - def _sanitize_params(self, params: dict[str, Any]) -> dict[str, Any]: - """Sanitize parameters to remove sensitive data. - - Args: - params: Parameters to sanitize - - Returns: - Sanitized parameters - """ - sanitized = {} - sensitive_keys = { - "password", - "token", - "key", - "secret", - "api_key", - "auth", - "credential", - "private", - "ssh_key", - "passphrase", - } - - for key, value in params.items(): - # Check if key contains sensitive terms - if any(sensitive in key.lower() for sensitive in sensitive_keys): - sanitized[key] = "[REDACTED]" - # Recursively sanitize nested dicts - elif isinstance(value, dict): - sanitized[key] = self._sanitize_params(value) - # Convert non-serializable types to strings - elif not isinstance(value, (str, int, float, bool, list, dict, type(None))): - sanitized[key] = str(value) - else: - sanitized[key] = value - - return sanitized - - def get_recent_errors( - self, tool_name: Optional[str] = None, limit: int = 10 - ) -> list[dict]: - """Get recent errors from the JSON log. - - Args: - tool_name: Filter by tool name (optional) - limit: Maximum number of errors to return - - Returns: - List of error dictionaries - """ - today = datetime.now().strftime("%Y-%m-%d") - json_log_file = self.log_dir / f"tool-errors-{today}.jsonl" - - if not json_log_file.exists(): - return [] - - errors = [] - try: - with open(json_log_file, "r") as f: - for line in f: - try: - error = json.loads(line) - if tool_name is None or error.get("tool_name") == tool_name: - errors.append(error) - except json.JSONDecodeError: - continue - except Exception as e: - self.logger.error(f"Failed to read error log: {e}") - - # Return most recent errors - return errors[-limit:] - - -# Global error logger instance -_global_error_logger: Optional[MCPErrorLogger] = None - - -def get_error_logger() -> MCPErrorLogger: - """Get the global error logger instance. - - Returns: - Global error logger - """ - global _global_error_logger - if _global_error_logger is None: - _global_error_logger = MCPErrorLogger() - return _global_error_logger - - -def log_tool_error( - tool_name: str, - error: Exception, - params: Optional[dict[str, Any]] = None, - context: Optional[str] = None, -): - """Convenience function to log a tool error using the global logger. - - Args: - tool_name: Name of the tool that errored - error: The exception that was raised - params: Tool parameters - context: Additional context - """ - logger = get_error_logger() - logger.log_tool_error(tool_name, error, params, context) - - -def log_call_signature_error( - tool_name: str, - expected_signature: str, - actual_call: str, - error: Exception, -): - """Convenience function to log a call signature error. - - Args: - tool_name: Name of the tool - expected_signature: Expected function signature - actual_call: How the tool was actually called - error: The exception that was raised - """ - logger = get_error_logger() - logger.log_call_signature_error(tool_name, expected_signature, actual_call, error) diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/fastmcp_pagination.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/fastmcp_pagination.py deleted file mode 100644 index 5b18b152d..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/fastmcp_pagination.py +++ /dev/null @@ -1,366 +0,0 @@ -"""FastMCP-compatible pagination implementation. - -This module provides pagination utilities optimized for FastMCP with minimal latency. -""" - -import base64 -import hashlib -import json -import time -from dataclasses import dataclass, field -from typing import Any, Dict, Generic, List, Optional, TypeVar, Union - -T = TypeVar("T") - - -@dataclass -class CursorData: - """Cursor data structure for efficient pagination.""" - - # Primary cursor fields (indexed) - last_id: Optional[str] = None - last_timestamp: Optional[float] = None - offset: int = 0 - - # Metadata for validation and optimization - page_size: int = 100 - sort_field: str = "id" - sort_order: str = "asc" - - # Security and validation - created_at: float = field(default_factory=time.time) - expires_at: Optional[float] = None - checksum: Optional[str] = None - - def to_cursor(self) -> str: - """Convert to opaque cursor string.""" - data = { - "id": self.last_id, - "ts": self.last_timestamp, - "o": self.offset, - "ps": self.page_size, - "sf": self.sort_field, - "so": self.sort_order, - "ca": self.created_at, - } - if self.expires_at: - data["ea"] = self.expires_at - - # Add checksum for integrity - data_str = json.dumps(data, sort_keys=True, separators=(",", ":")) - data["cs"] = hashlib.md5(data_str.encode()).hexdigest()[:8] - - # Encode as base64 - final_str = json.dumps(data, separators=(",", ":")) - return base64.urlsafe_b64encode(final_str.encode()).decode().rstrip("=") - - @classmethod - def from_cursor(cls, cursor: str) -> Optional["CursorData"]: - """Parse cursor string back to CursorData.""" - try: - # Add padding if needed - padding = 4 - (len(cursor) % 4) - if padding != 4: - cursor += "=" * padding - - decoded = base64.urlsafe_b64decode(cursor.encode()) - data = json.loads(decoded) - - # Validate checksum - checksum = data.pop("cs", None) - if checksum: - data_str = json.dumps( - {k: v for k, v in data.items() if k != "cs"}, - sort_keys=True, - separators=(",", ":"), - ) - expected = hashlib.md5(data_str.encode()).hexdigest()[:8] - if checksum != expected: - return None - - # Check expiration - expires_at = data.get("ea") - if expires_at and time.time() > expires_at: - return None - - return cls( - last_id=data.get("id"), - last_timestamp=data.get("ts"), - offset=data.get("o", 0), - page_size=data.get("ps", 100), - sort_field=data.get("sf", "id"), - sort_order=data.get("so", "asc"), - created_at=data.get("ca", time.time()), - expires_at=expires_at, - ) - except Exception: - return None - - -class FastMCPPaginator(Generic[T]): - """High-performance paginator for FastMCP responses.""" - - def __init__( - self, - page_size: int = 100, - max_page_size: int = 1000, - cursor_ttl: int = 3600, # 1 hour - enable_prefetch: bool = False, - ): - """Initialize the paginator. - - Args: - page_size: Default page size - max_page_size: Maximum allowed page size - cursor_ttl: Cursor time-to-live in seconds - enable_prefetch: Enable prefetching for next page - """ - self.page_size = page_size - self.max_page_size = max_page_size - self.cursor_ttl = cursor_ttl - self.enable_prefetch = enable_prefetch - self._cache: Dict[str, Any] = {} - - def paginate_list( - self, - items: List[T], - cursor: Optional[str] = None, - page_size: Optional[int] = None, - sort_key: Optional[str] = None, - ) -> Dict[str, Any]: - """Paginate a list with optimal performance. - - Args: - items: List to paginate - cursor: Optional cursor from previous request - page_size: Override default page size - sort_key: Sort field for consistent ordering - - Returns: - Dict with items and optional nextCursor - """ - # Parse cursor or create new - cursor_data = CursorData.from_cursor(cursor) if cursor else CursorData() - - # Use provided page size or default - actual_page_size = min( - page_size or cursor_data.page_size or self.page_size, self.max_page_size - ) - - # Get starting position - start_idx = cursor_data.offset - - # Validate bounds - if start_idx >= len(items): - return {"items": [], "hasMore": False} - - # Slice the page - end_idx = min(start_idx + actual_page_size, len(items)) - page_items = items[start_idx:end_idx] - - # Build response - response = { - "items": page_items, - "pageInfo": { - "startIndex": start_idx, - "endIndex": end_idx, - "pageSize": len(page_items), - "totalItems": len(items), - }, - } - - # Create next cursor if more items exist - if end_idx < len(items): - next_cursor_data = CursorData( - offset=end_idx, - page_size=actual_page_size, - expires_at=time.time() + self.cursor_ttl if self.cursor_ttl else None, - ) - response["nextCursor"] = next_cursor_data.to_cursor() - response["hasMore"] = True - else: - response["hasMore"] = False - - return response - - def paginate_query( - self, - query_func, - cursor: Optional[str] = None, - page_size: Optional[int] = None, - **query_params, - ) -> Dict[str, Any]: - """Paginate results from a query function. - - This is optimized for database queries using indexed fields. - - Args: - query_func: Function that accepts (last_id, last_timestamp, limit, **params) - cursor: Optional cursor - page_size: Override page size - **query_params: Additional query parameters - - Returns: - Paginated response - """ - # Parse cursor - cursor_data = CursorData.from_cursor(cursor) if cursor else CursorData() - - # Determine page size - limit = min( - page_size or cursor_data.page_size or self.page_size, self.max_page_size - ) - - # Execute query with cursor position - results = query_func( - last_id=cursor_data.last_id, - last_timestamp=cursor_data.last_timestamp, - limit=limit + 1, # Fetch one extra to detect more - **query_params, - ) - - # Check if there are more results - has_more = len(results) > limit - if has_more: - results = results[:limit] # Remove the extra item - - # Build response - response = { - "items": results, - "pageInfo": {"pageSize": len(results), "hasMore": has_more}, - } - - # Create next cursor if needed - if has_more and results: - last_item = results[-1] - next_cursor_data = CursorData( - last_id=getattr(last_item, "id", None), - last_timestamp=getattr(last_item, "timestamp", None), - page_size=limit, - sort_field=cursor_data.sort_field, - sort_order=cursor_data.sort_order, - expires_at=time.time() + self.cursor_ttl if self.cursor_ttl else None, - ) - response["nextCursor"] = next_cursor_data.to_cursor() - - return response - - -class TokenAwarePaginator: - """Paginator that respects token limits for LLM responses.""" - - def __init__(self, max_tokens: int = 20000): - """Initialize token-aware paginator. - - Args: - max_tokens: Maximum tokens per response - """ - self.max_tokens = max_tokens - self.paginator = FastMCPPaginator() - - def paginate_by_tokens( - self, items: List[Any], cursor: Optional[str] = None, estimate_func=None - ) -> Dict[str, Any]: - """Paginate items based on token count. - - Args: - items: Items to paginate - cursor: Optional cursor - estimate_func: Function to estimate tokens for an item - - Returns: - Paginated response - """ - from hanzo_mcp.tools.common.truncate import estimate_tokens - - # Default token estimation - if not estimate_func: - - def estimate_func(x): - return estimate_tokens(json.dumps(x) if not isinstance(x, str) else x) - - # Parse cursor - cursor_data = CursorData.from_cursor(cursor) if cursor else CursorData() - start_idx = cursor_data.offset - - # Build page respecting token limit - page_items = [] - current_tokens = 100 # Base overhead - current_idx = start_idx - - while current_idx < len(items) and current_tokens < self.max_tokens: - item = items[current_idx] - item_tokens = estimate_func(item) - - # Check if adding this item would exceed limit - if current_tokens + item_tokens > self.max_tokens and page_items: - break - - page_items.append(item) - current_tokens += item_tokens - current_idx += 1 - - # Build response - response = { - "items": page_items, - "pageInfo": { - "itemCount": len(page_items), - "estimatedTokens": current_tokens, - "hasMore": current_idx < len(items), - }, - } - - # Add next cursor if needed - if current_idx < len(items): - next_cursor_data = CursorData(offset=current_idx) - response["nextCursor"] = next_cursor_data.to_cursor() - - return response - - -# FastMCP integration helpers -def create_paginated_response( - items: Union[List[Any], Dict[str, Any], str], - cursor: Optional[str] = None, - page_size: int = 100, - use_token_limit: bool = True, -) -> Dict[str, Any]: - """Create a paginated response compatible with FastMCP. - - Args: - items: The items to paginate - cursor: Optional cursor from request - page_size: Items per page - use_token_limit: Whether to use token-based pagination - - Returns: - FastMCP-compatible paginated response - """ - if use_token_limit: - paginator = TokenAwarePaginator() - - # Convert different types to list - if isinstance(items, str): - # Split string by lines for pagination - items = items.split("\n") - elif isinstance(items, dict): - # Convert dict to list of key-value pairs - items = [{"key": k, "value": v} for k, v in items.items()] - - return paginator.paginate_by_tokens(items, cursor) - else: - paginator = FastMCPPaginator(page_size=page_size) - - # Handle different input types - if isinstance(items, list): - return paginator.paginate_list(items, cursor, page_size) - else: - # Convert to list first - if isinstance(items, str): - items = items.split("\n") - elif isinstance(items, dict): - items = [{"key": k, "value": v} for k, v in items.items()] - else: - items = [items] - - return paginator.paginate_list(items, cursor, page_size) diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/forgiving_edit.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/forgiving_edit.py deleted file mode 100644 index 6129322f0..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/forgiving_edit.py +++ /dev/null @@ -1,258 +0,0 @@ -"""Forgiving edit helper for AI-friendly text matching.""" - -import difflib -import re -from typing import List, Optional, Tuple - - -class ForgivingEditHelper: - """Helper class to make text editing more forgiving for AI usage. - - This helper normalizes whitespace, handles partial matches, and provides - suggestions when exact matches fail. - """ - - @staticmethod - def normalize_whitespace(text: str) -> str: - """Normalize whitespace while preserving structure. - - Args: - text: Text to normalize - - Returns: - Text with normalized whitespace - """ - # Handle the input line by line - lines = [] - for line in text.split("\n"): - # Replace tabs with 4 spaces everywhere in the line - line = line.replace("\t", " ") - - # Split into indentation and content - stripped = line.lstrip() - indent = line[: len(line) - len(stripped)] - - if stripped: - # For content, normalize multiple spaces to single space - content = re.sub(r" {2,}", " ", stripped) - lines.append(indent + content) - else: - lines.append(indent) - - return "\n".join(lines) - - @staticmethod - def find_fuzzy_match( - haystack: str, needle: str, threshold: float = 0.85 - ) -> Optional[Tuple[int, int, str]]: - """Find a fuzzy match for the needle in the haystack. - - Args: - haystack: Text to search in - needle: Text to search for - threshold: Similarity threshold (0-1) - - Returns: - Tuple of (start_pos, end_pos, matched_text) or None - """ - # First try exact match - if needle in haystack: - start = haystack.index(needle) - return (start, start + len(needle), needle) - - # Normalize for comparison - norm_haystack = ForgivingEditHelper.normalize_whitespace(haystack) - norm_needle = ForgivingEditHelper.normalize_whitespace(needle) - - # Try normalized exact match - if norm_needle in norm_haystack: - # Find the match in normalized text - norm_start = norm_haystack.index(norm_needle) - - # Map back to original text - # This is approximate but usually good enough - lines_before = norm_haystack[:norm_start].count("\n") - - # Find corresponding position in original - original_lines = haystack.split("\n") - norm_haystack.split("\n") - - start_pos = sum(len(line) + 1 for line in original_lines[:lines_before]) - - # Find end position by counting lines in needle - needle_line_count = norm_needle.count("\n") + 1 - end_pos = sum( - len(line) + 1 - for line in original_lines[: lines_before + needle_line_count] - ) - - matched = "\n".join( - original_lines[lines_before : lines_before + needle_line_count] - ) - return (start_pos, end_pos - 1, matched) - - # Try fuzzy matching on lines - haystack_lines = haystack.split("\n") - needle_lines = needle.split("\n") - - if len(needle_lines) == 1: - # Single line - find best match - needle_norm = ForgivingEditHelper.normalize_whitespace(needle) - best_ratio: float = 0.0 - best_match = None - - for i, line in enumerate(haystack_lines): - line_norm = ForgivingEditHelper.normalize_whitespace(line) - ratio = difflib.SequenceMatcher(None, line_norm, needle_norm).ratio() - - if ratio > best_ratio and ratio >= threshold: - best_ratio = ratio - start_pos = sum(len(l) + 1 for l in haystack_lines[:i]) - best_match = (start_pos, start_pos + len(line), line) - - return best_match - - else: - # Multi-line - find sequence match - for i in range(len(haystack_lines) - len(needle_lines) + 1): - candidate_lines = haystack_lines[i : i + len(needle_lines)] - candidate = "\n".join(candidate_lines) - candidate_norm = ForgivingEditHelper.normalize_whitespace(candidate) - needle_norm = ForgivingEditHelper.normalize_whitespace(needle) - - ratio = difflib.SequenceMatcher( - None, candidate_norm, needle_norm - ).ratio() - - if ratio >= threshold: - start_pos = sum(len(l) + 1 for l in haystack_lines[:i]) - return (start_pos, start_pos + len(candidate), candidate) - - return None - - @staticmethod - def suggest_matches( - haystack: str, needle: str, max_suggestions: int = 3 - ) -> List[Tuple[float, str]]: - """Suggest possible matches when exact match fails. - - Args: - haystack: Text to search in - needle: Text to search for - max_suggestions: Maximum number of suggestions - - Returns: - List of (similarity_score, text) tuples - """ - suggestions = [] - - # Normalize needle - needle_norm = ForgivingEditHelper.normalize_whitespace(needle) - needle_lines = needle.split("\n") - - if len(needle_lines) == 1: - # Single line - compare with all lines - for line in haystack.split("\n"): - if line.strip(): # Skip empty lines - line_norm = ForgivingEditHelper.normalize_whitespace(line) - ratio = difflib.SequenceMatcher( - None, line_norm, needle_norm - ).ratio() - if ratio > 0.5: # Only reasonably similar lines - suggestions.append((ratio, line)) - - else: - # Multi-line - use sliding window - haystack_lines = haystack.split("\n") - window_size = len(needle_lines) - - for i in range(len(haystack_lines) - window_size + 1): - candidate_lines = haystack_lines[i : i + window_size] - candidate = "\n".join(candidate_lines) - candidate_norm = ForgivingEditHelper.normalize_whitespace(candidate) - - ratio = difflib.SequenceMatcher( - None, candidate_norm, needle_norm - ).ratio() - if ratio > 0.5: - suggestions.append((ratio, candidate)) - - # Sort by similarity and return top matches - suggestions.sort(reverse=True, key=lambda x: x[0]) - return suggestions[:max_suggestions] - - @staticmethod - def create_edit_suggestion( - file_content: str, old_string: str, new_string: str - ) -> dict: - """Create a helpful edit suggestion when match fails. - - Args: - file_content: Current file content - old_string: String that couldn't be found - new_string: Replacement string - - Returns: - Dict with error message and suggestions - """ - # Try fuzzy match - fuzzy_match = ForgivingEditHelper.find_fuzzy_match(file_content, old_string) - - if fuzzy_match: - _, _, matched_text = fuzzy_match - return { - "error": "Exact match not found, but found similar text", - "found": matched_text, - "suggestion": "Use this as old_string instead", - "confidence": "high", - } - - # Get suggestions - suggestions = ForgivingEditHelper.suggest_matches(file_content, old_string) - - if suggestions: - return { - "error": "Could not find exact or fuzzy match", - "suggestions": [ - {"similarity": f"{score:.0%}", "text": text} - for score, text in suggestions - ], - "hint": "Try using one of these suggestions as old_string", - } - - # No good matches - provide general help - return { - "error": "Could not find any matches", - "hints": [ - "Check for whitespace differences (tabs vs spaces)", - "Ensure you're including complete lines", - "Try a smaller, more unique portion of text", - "Use the streaming_command tool to view the file with visible whitespace", - ], - } - - @staticmethod - def prepare_edit_string(text: str) -> str: - """Prepare a string for editing by handling common issues. - - Args: - text: Text to prepare - - Returns: - Cleaned text ready for editing - """ - # Remove any line number prefixes (common in AI copy-paste) - lines = [] - for line in text.split("\n"): - # Remove common line number patterns while preserving indentation - # Match patterns like "1: ", "123: ", "1| ", "1- ", etc. - # But preserve the original indentation after the line number - match = re.match(r"^(\d+[:\|\-])\s(.*)", line) - if match: - # Keep only the content part (group 2) which includes any indentation - lines.append(match.group(2)) - else: - # No line number pattern found, keep the line as-is - lines.append(line) - - return "\n".join(lines) diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/mode.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/mode.py deleted file mode 100644 index ef08bfc22..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/mode.py +++ /dev/null @@ -1,151 +0,0 @@ -"""Mode system for organizing development tools based on programmer personalities.""" - -import os -from dataclasses import dataclass -from typing import Dict, List, Optional, Set - -from hanzo_mcp.tools.common.personality import ( - PersonalityRegistry, - ToolPersonality, - ensure_agent_enabled, - register_default_personalities, -) - - -@dataclass -class Mode(ToolPersonality): - """Development mode combining tool preferences and environment settings.""" - - # Inherits all fields from ToolPersonality - # Adds mode-specific functionality - - @property - def is_active(self) -> bool: - """Check if this mode is currently active.""" - return ModeRegistry.get_active() == self - - -class ModeRegistry: - """Registry for development modes.""" - - _modes: Dict[str, Mode] = {} - _active_mode: Optional[str] = None - - @classmethod - def register(cls, mode: Mode) -> None: - """Register a development mode.""" - # Ensure agent is enabled if API keys present - mode = ensure_agent_enabled(mode) - cls._modes[mode.name] = mode - - @classmethod - def get(cls, name: str) -> Optional[Mode]: - """Get a mode by name.""" - return cls._modes.get(name) - - @classmethod - def list(cls) -> List[Mode]: - """List all registered modes.""" - return list(cls._modes.values()) - - @classmethod - def set_active(cls, name: str) -> None: - """Set the active mode.""" - if name not in cls._modes: - raise ValueError(f"Mode '{name}' not found") - cls._active_mode = name - - # Apply environment variables from the mode - mode = cls._modes[name] - if mode.environment: - for key, value in mode.environment.items(): - os.environ[key] = value - - # Create CLI tools for this mode - if mode.cli_tools: - try: - from hanzo_mcp.tools.common.cli_tool_factory import CLIToolFactory - - factory = CLIToolFactory.get_instance() - for cli_tool in mode.cli_tools: - # Only create if not already exists - if cli_tool.name not in [t["name"] for t in factory.list()]: - factory.create( - name=cli_tool.name, - command=cli_tool.command, - description=cli_tool.description, - timeout=cli_tool.timeout, - ) - except ImportError: - pass # CLI factory not available - - @classmethod - def get_active(cls) -> Optional[Mode]: - """Get the active mode.""" - if cls._active_mode: - return cls._modes.get(cls._active_mode) - return None - - @classmethod - def get_active_tools(cls) -> Set[str]: - """Get the set of tools from the active mode.""" - mode = cls.get_active() - if mode: - return set(mode.tools) - return set() - - @classmethod - def clear(cls) -> None: - """Clear all modes (for testing/reload).""" - cls._modes = {} - cls._active_mode = None - - -def register_default_modes(): - """Register all default development modes from personalities.""" - # First ensure personalities are loaded - register_default_personalities() - - # Convert personalities to modes - for personality in PersonalityRegistry.list(): - mode = Mode( - name=personality.name, - programmer=personality.programmer, - description=personality.description, - tools=personality.tools, - environment=personality.environment, - philosophy=personality.philosophy, - cli_tools=personality.cli_tools, - category=personality.category, - ocean=personality.ocean, - behavioral_traits=personality.behavioral_traits, - cognitive_style=personality.cognitive_style, - social_dynamics=personality.social_dynamics, - communication_patterns=personality.communication_patterns, - work_methodology=personality.work_methodology, - emotional_profile=personality.emotional_profile, - ) - ModeRegistry.register(mode) - - -def get_mode_from_env() -> Optional[str]: - """Get mode name from environment variables.""" - # Check for HANZO_MODE, PERSONALITY, or MODE env vars - return ( - os.environ.get("HANZO_MODE") - or os.environ.get("PERSONALITY") - or os.environ.get("MODE") - ) - - -def activate_mode_from_env(): - """Activate mode based on environment variables.""" - mode_name = get_mode_from_env() - if mode_name: - try: - ModeRegistry.set_active(mode_name) - return True - except ValueError: - # Mode not found, ignore - pass - return False diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/mode_loader.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/mode_loader.py deleted file mode 100644 index 8a3b0f569..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/mode_loader.py +++ /dev/null @@ -1,140 +0,0 @@ -"""Tool mode loader for dynamic tool configuration.""" - -import os -from typing import Dict, Optional, Set - -from hanzo_mcp.tools.common.mode import ( - ModeRegistry, - activate_mode_from_env, - register_default_modes, -) - -# Essential system tools that are ALWAYS enabled regardless of mode -# These are core infrastructure tools that should never be disabled -# NOTE: llm and consensus are NOT essential (heavy llm dependency) -ESSENTIAL_SYSTEM_TOOLS: Set[str] = { - # Reasoning (lightweight) - "think", # Structured thinking - "critic", # Critical analysis - "agent", # Lightweight agent spawning - # Configuration and mode management - "config", # System configuration - "mode", # Mode switching - # Tool management (unified command) - "tool", # Unified tool management (install, enable, disable, list) - # Core system - "version", # Version info - "stats", # Statistics - # Memory (always available) - "memory", # Unified memory operations -} - -# Heavy tools that are disabled by default (opt-in) -# These require large dependencies like llm -HEAVY_TOOLS: Set[str] = { - "llm", # Requires llm (~100MB+ deps) - "consensus", # Requires llm -} - - -class ModeLoader: - """Loads and manages tool modes for dynamic configuration.""" - - @staticmethod - def initialize_modes() -> None: - """Initialize the mode system with defaults.""" - # Initialize modes - register_default_modes() - - # Check for mode from environment - activate_mode_from_env() - - # If no mode set, use default - if not ModeRegistry.get_active(): - default_mode = os.environ.get("HANZO_DEFAULT_MODE", "hanzo") - if ModeRegistry.get(default_mode): - ModeRegistry.set_active(default_mode) - - @staticmethod - def get_enabled_tools_from_mode( - base_enabled_tools: Optional[Dict[str, bool]] = None, - force_mode: Optional[str] = None, - ) -> Dict[str, bool]: - """Get enabled tools configuration from active mode. - - Args: - base_enabled_tools: Base configuration to merge with - force_mode: Force a specific mode (overrides active) - - Returns: - Dictionary of tool enable states - """ - # Initialize if needed - if not ModeRegistry.list(): - ModeLoader.initialize_modes() - - # Get mode to use - tools_list = None - - if force_mode: - # Set and get mode - if ModeRegistry.get(force_mode): - ModeRegistry.set_active(force_mode) - mode = ModeRegistry.get_active() - tools_list = mode.tools if mode else None - else: - # Check active mode - mode = ModeRegistry.get_active() - if mode: - tools_list = mode.tools - - if not tools_list: - # No active mode, return base config - return base_enabled_tools or {} - - # Start with base configuration - result = base_enabled_tools.copy() if base_enabled_tools else {} - - # Get all possible tools from registry - from hanzo_mcp.config.tool_config import TOOL_REGISTRY - - all_possible_tools = set(TOOL_REGISTRY.keys()) - - # Disable all tools first (clean slate for mode) - # EXCEPT essential system tools which are always enabled - for tool in all_possible_tools: - if tool in ESSENTIAL_SYSTEM_TOOLS: - result[tool] = True # Essential tools always enabled - else: - result[tool] = False - - # Enable tools from mode - for tool in tools_list: - result[tool] = True - - # Ensure all essential system tools are enabled (in case mode tried to disable them) - for tool in ESSENTIAL_SYSTEM_TOOLS: - result[tool] = True - - return result - - @staticmethod - def get_environment_from_mode() -> Dict[str, str]: - """Get environment variables from active mode. - - Returns: - Dictionary of environment variables - """ - # Check mode - mode = ModeRegistry.get_active() - if mode and mode.environment: - return mode.environment.copy() - - return {} - - @staticmethod - def apply_environment_from_mode() -> None: - """Apply environment variables from active mode.""" - env_vars = ModeLoader.get_environment_from_mode() - for key, value in env_vars.items(): - os.environ[key] = value diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/paginated_base.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/paginated_base.py deleted file mode 100644 index eea834a74..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/paginated_base.py +++ /dev/null @@ -1,240 +0,0 @@ -"""Enhanced base class with automatic pagination support. - -This module provides a base class that automatically handles pagination -for all tool responses that exceed MCP token limits. -""" - -from abc import abstractmethod -from typing import Any, Dict, Union - -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_mcp.tools.common.auto_timeout import auto_timeout -from hanzo_mcp.tools.common.base import BaseTool, handle_connection_errors -from hanzo_mcp.tools.common.paginated_response import paginate_if_needed -from hanzo_mcp.tools.common.pagination import CursorManager - - -class PaginatedBaseTool(BaseTool): - """Base class for tools with automatic pagination support. - - This base class automatically handles pagination for responses that - exceed MCP token limits, making all tools pagination-aware by default. - """ - - def __init__(self): - """Initialize the paginated base tool.""" - super().__init__() - self._supports_pagination = True - - @abstractmethod - async def execute(self, ctx: MCPContext, **params: Any) -> Any: - """Execute the tool logic and return raw results. - - This method should be implemented by subclasses to perform the - actual tool logic. The base class will handle pagination of - the returned results automatically. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Raw tool results (will be paginated if needed) - """ - pass - - @handle_connection_errors - @auto_timeout("paginated_base") - async def call(self, ctx: MCPContext, **params: Any) -> Union[str, Dict[str, Any]]: - """Execute the tool with automatic pagination support. - - This method wraps the execute() method and automatically handles - pagination if the response exceeds token limits. - - Args: - ctx: MCP context - **params: Tool parameters including optional 'cursor' - - Returns: - Tool result, potentially paginated - """ - # Extract cursor if provided - cursor = params.pop("cursor", None) - - # Validate cursor if provided - if cursor and not CursorManager.parse_cursor(cursor): - return {"error": "Invalid cursor provided", "code": -32602} - - # Check if this is a continuation request - if cursor: - # For continuation, check if we have cached results - cursor_data = CursorManager.parse_cursor(cursor) - if ( - cursor_data - and "tool" in cursor_data - and cursor_data["tool"] != self.name - ): - return {"error": "Cursor is for a different tool", "code": -32602} - - # Execute the tool - try: - result = await self.execute(ctx, **params) - except Exception as e: - # Format errors consistently - return {"error": str(e), "type": type(e).__name__} - - # Handle pagination automatically - if self._supports_pagination: - paginated_result = paginate_if_needed(result, cursor) - - # If pagination occurred, add tool info to help with continuation - if isinstance(paginated_result, dict) and "nextCursor" in paginated_result: - # Enhance the cursor with tool information - if "nextCursor" in paginated_result: - cursor_data = CursorManager.parse_cursor( - paginated_result["nextCursor"] - ) - if cursor_data: - cursor_data["tool"] = self.name - cursor_data["params"] = params # Store params for continuation - paginated_result["nextCursor"] = CursorManager.create_cursor( - cursor_data - ) - - return paginated_result - else: - # Return raw result if pagination is disabled - return result - - def disable_pagination(self): - """Disable automatic pagination for this tool. - - Some tools may want to handle their own pagination logic. - """ - self._supports_pagination = False - - def enable_pagination(self): - """Re-enable automatic pagination for this tool.""" - self._supports_pagination = True - - -class PaginatedFileSystemTool(PaginatedBaseTool): - """Base class for filesystem tools with pagination support.""" - - def __init__(self, permission_manager): - """Initialize filesystem tool with pagination. - - Args: - permission_manager: Permission manager for access control - """ - super().__init__() - self.permission_manager = permission_manager - - def is_path_allowed(self, path: str) -> bool: - """Check if a path is allowed according to permission settings. - - Args: - path: Path to check - - Returns: - True if the path is allowed, False otherwise - """ - return self.permission_manager.is_path_allowed(path) - - -def migrate_tool_to_paginated(tool_class): - """Decorator to migrate existing tools to use pagination. - - This decorator can be applied to existing tool classes to add - automatic pagination support without modifying their code. - - Usage: - @migrate_tool_to_paginated - class MyTool(BaseTool): - ... - """ - - class PaginatedWrapper(PaginatedBaseTool): - def __init__(self, *args, **kwargs): - super().__init__() - self._wrapped_tool = tool_class(*args, **kwargs) - - @property - def name(self): - return self._wrapped_tool.name - - @property - def description(self): - # Add pagination info to description - desc = self._wrapped_tool.description - if "pagination" not in desc.lower(): - desc += "\n\nThis tool supports automatic pagination. If the response is too large, it will be split across multiple requests. Use the returned cursor to continue." - return desc - - async def execute(self, ctx: MCPContext, **params: Any) -> Any: - # Call the wrapped tool's call method - return await self._wrapped_tool.call(ctx, **params) - - def register(self, mcp_server): - # Need to create a new registration that includes cursor parameter - tool_self = self - - # Get the original registration function - original_register = self._wrapped_tool.register - - # Create a new registration that adds cursor support - def register_with_pagination(server): - # First register the original tool - original_register(server) - - # Then override with pagination support - import inspect - - # Get the registered function - tool_func = None - for name, func in server._tools.items(): - if name == self.name: - tool_func = func - break - - if tool_func: - # Get original signature - sig = inspect.signature(tool_func) - params = list(sig.parameters.values()) - - # Add cursor parameter if not present - has_cursor = any(p.name == "cursor" for p in params) - if not has_cursor: - import inspect - from typing import Optional - - # Create new parameter with cursor - cursor_param = inspect.Parameter( - "cursor", - inspect.Parameter.KEYWORD_ONLY, - default=None, - annotation=Optional[str], - ) - - # Insert before ctx parameter - new_params = [] - for p in params: - if p.name == "ctx": - new_params.append(cursor_param) - new_params.append(p) - - # Create wrapper function - async def paginated_wrapper(**kwargs): - return await tool_self.call(kwargs.get("ctx"), **kwargs) - - # Update registration - server._tools[self.name] = paginated_wrapper - - register_with_pagination(mcp_server) - - # Set the class name - PaginatedWrapper.__name__ = f"Paginated{tool_class.__name__}" - PaginatedWrapper.__qualname__ = f"Paginated{tool_class.__qualname__}" - - return PaginatedWrapper diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/paginated_response.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/paginated_response.py deleted file mode 100644 index 1d4a6efab..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/paginated_response.py +++ /dev/null @@ -1,300 +0,0 @@ -"""Automatic pagination response wrapper for MCP tools. - -This module provides utilities to automatically paginate tool responses -when they exceed token limits. -""" - -import json -from typing import Any, Dict, List, Optional, Union - -from hanzo_mcp.tools.common.pagination import CursorManager -from hanzo_mcp.tools.common.truncate import estimate_tokens - - -class AutoPaginatedResponse: - """Automatically paginate responses that exceed token limits.""" - - # MCP token limit with safety buffer - MAX_TOKENS = 20000 # Leave 5k buffer from 25k limit - - @staticmethod - def create_response( - content: Union[str, Dict[str, Any], List[Any]], - cursor: Optional[str] = None, - max_tokens: int = MAX_TOKENS, - ) -> Dict[str, Any]: - """Create a response that automatically paginates if too large. - - Args: - content: The content to return - cursor: Optional cursor from request - max_tokens: Maximum tokens allowed in response - - Returns: - Dict with content and optional nextCursor - """ - # Handle different content types - if isinstance(content, str): - return AutoPaginatedResponse._handle_string_response( - content, cursor, max_tokens - ) - elif isinstance(content, list): - return AutoPaginatedResponse._handle_list_response( - content, cursor, max_tokens - ) - elif isinstance(content, dict): - # If dict already has pagination info, return as-is - if "nextCursor" in content or "cursor" in content: - return content - # Otherwise treat as single item - return AutoPaginatedResponse._handle_dict_response( - content, cursor, max_tokens - ) - else: - # Convert to string for other types - return AutoPaginatedResponse._handle_string_response( - str(content), cursor, max_tokens - ) - - @staticmethod - def _handle_string_response( - content: str, cursor: Optional[str], max_tokens: int - ) -> Dict[str, Any]: - """Handle pagination for string responses.""" - # Parse cursor to get offset - offset = 0 - if cursor: - cursor_data = CursorManager.parse_cursor(cursor) - if cursor_data and "offset" in cursor_data: - offset = cursor_data["offset"] - - # For strings, paginate by lines - lines = content.split("\n") - - if offset >= len(lines): - return {"content": "", "message": "No more content"} - - # Build response line by line, checking tokens - result_lines = [] - current_tokens = 0 - line_index = offset - - # Add header if this is a continuation - if offset > 0: - header = f"[Continued from line {offset + 1}]\n" - current_tokens = estimate_tokens(header) - result_lines.append(header) - - while line_index < len(lines): - line = lines[line_index] - line_tokens = estimate_tokens(line + "\n") - - # Check if adding this line would exceed limit - if current_tokens + line_tokens > max_tokens: - # Need to paginate - if not result_lines: - # Single line too long, truncate it - truncated_line = line[:1000] + "... [line truncated]" - result_lines.append(truncated_line) - line_index += 1 - break - - result_lines.append(line) - current_tokens += line_tokens - line_index += 1 - - # Build response - response = {"content": "\n".join(result_lines)} - - # Add pagination info - if line_index < len(lines): - response["nextCursor"] = CursorManager.create_offset_cursor(line_index) - response["pagination_info"] = { - "current_lines": f"{offset + 1}-{line_index}", - "total_lines": len(lines), - "has_more": True, - } - else: - response["pagination_info"] = { - "current_lines": f"{offset + 1}-{len(lines)}", - "total_lines": len(lines), - "has_more": False, - } - - return response - - @staticmethod - def _handle_list_response( - items: List[Any], cursor: Optional[str], max_tokens: int - ) -> Dict[str, Any]: - """Handle pagination for list responses.""" - # Parse cursor to get offset - offset = 0 - if cursor: - cursor_data = CursorManager.parse_cursor(cursor) - if cursor_data and "offset" in cursor_data: - offset = cursor_data["offset"] - - if offset >= len(items): - return {"items": [], "message": "No more items"} - - # Build response item by item, checking tokens - result_items = [] - current_tokens = 100 # Base overhead - item_index = offset - - # Add header if continuation - header_obj = {} - if offset > 0: - header_obj["continuation_from"] = offset - current_tokens += 50 - - while item_index < len(items): - item = items[item_index] - - # Estimate tokens for this item - item_str = json.dumps(item) if not isinstance(item, str) else item - item_tokens = estimate_tokens(item_str) - - # Check if adding this item would exceed limit - if current_tokens + item_tokens > max_tokens: - if not result_items: - # Single item too large, truncate it - if isinstance(item, str): - truncated = item[:5000] + "... [truncated]" - result_items.append(truncated) - else: - result_items.append( - {"error": "Item too large", "index": item_index} - ) - item_index += 1 - break - - result_items.append(item) - current_tokens += item_tokens - item_index += 1 - - # Build response - response = {"items": result_items} - - if header_obj: - response.update(header_obj) - - # Add pagination info - if item_index < len(items): - response["nextCursor"] = CursorManager.create_offset_cursor(item_index) - response["pagination_info"] = { - "returned_items": len(result_items), - "total_items": len(items), - "has_more": True, - "next_index": item_index, - } - else: - response["pagination_info"] = { - "returned_items": len(result_items), - "total_items": len(items), - "has_more": False, - } - - return response - - @staticmethod - def _handle_dict_response( - content: Dict[str, Any], cursor: Optional[str], max_tokens: int - ) -> Dict[str, Any]: - """Handle pagination for dict responses.""" - # For dicts, check if it's too large as-is - content_str = json.dumps(content, indent=2) - content_tokens = estimate_tokens(content_str) - - if content_tokens <= max_tokens: - # Fits within limit - return content - - # Too large - need to paginate - # Strategy: Convert to key-value pairs and paginate - items = list(content.items()) - offset = 0 - - if cursor: - cursor_data = CursorManager.parse_cursor(cursor) - if cursor_data and "offset" in cursor_data: - offset = cursor_data["offset"] - - if offset >= len(items): - return {"content": {}, "message": "No more content"} - - # Build paginated dict - result = {} - current_tokens = 100 # Base overhead - - for i in range(offset, len(items)): - key, value = items[i] - - # Estimate tokens for this entry - entry_str = json.dumps({key: value}) - entry_tokens = estimate_tokens(entry_str) - - if current_tokens + entry_tokens > max_tokens: - if not result: - # Single entry too large - result[key] = "[Value too large - use specific key access]" - break - - result[key] = value - current_tokens += entry_tokens - - # Wrap in response - response = {"content": result} - - # Add pagination info - processed = offset + len(result) - if processed < len(items): - response["nextCursor"] = CursorManager.create_offset_cursor(processed) - response["pagination_info"] = { - "keys_returned": len(result), - "total_keys": len(items), - "has_more": True, - } - else: - response["pagination_info"] = { - "keys_returned": len(result), - "total_keys": len(items), - "has_more": False, - } - - return response - - -def paginate_if_needed( - response: Any, cursor: Optional[str] = None, force_pagination: bool = False -) -> Union[str, Dict[str, Any]]: - """Wrap a response with automatic pagination if needed. - - Args: - response: The response to potentially paginate - cursor: Optional cursor from request - force_pagination: Force pagination even for small responses - - Returns: - Original response if small enough, otherwise paginated dict - """ - # Quick check - if response is already paginated, return as-is - if isinstance(response, dict) and ( - "nextCursor" in response or "pagination_info" in response - ): - return response - - # For small responses, don't paginate unless forced - if not force_pagination: - try: - response_str = ( - json.dumps(response) if not isinstance(response, str) else response - ) - if len(response_str) < 10000: # Quick heuristic - return response - except Exception: - pass - - # Create paginated response - return AutoPaginatedResponse.create_response(response, cursor) diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/pagination.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/pagination.py deleted file mode 100644 index fbbd35f83..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/pagination.py +++ /dev/null @@ -1,223 +0,0 @@ -"""Pagination utilities for MCP tools. - -This module provides utilities for implementing cursor-based pagination -according to the MCP pagination protocol. -""" - -import base64 -import json -from dataclasses import dataclass -from typing import Any, Dict, Generic, List, Optional, TypeVar - -T = TypeVar("T") - - -@dataclass -class PaginationParams: - """Parameters for pagination.""" - - cursor: Optional[str] = None - page_size: int = 100 # Default page size - - -@dataclass -class PaginatedResponse(Generic[T]): - """A paginated response containing items and optional next cursor.""" - - items: List[T] - next_cursor: Optional[str] = None - - def to_dict(self, items_key: str = "items") -> Dict[str, Any]: - """Convert to dictionary format for MCP response. - - Args: - items_key: The key to use for items in the response - - Returns: - Dictionary with items and optional nextCursor - """ - result = {items_key: self.items} - if self.next_cursor: - result["nextCursor"] = self.next_cursor - return result - - -class CursorManager: - """Manages cursor creation and parsing for pagination.""" - - @staticmethod - def create_cursor(data: Dict[str, Any]) -> str: - """Create an opaque cursor from data. - - Args: - data: Data to encode in the cursor - - Returns: - Base64-encoded cursor string - """ - json_data = json.dumps(data, separators=(",", ":")) - return base64.b64encode(json_data.encode()).decode() - - @staticmethod - def parse_cursor(cursor: str) -> Optional[Dict[str, Any]]: - """Parse a cursor string back to data. - - Args: - cursor: Base64-encoded cursor string - - Returns: - Decoded data or None if invalid - """ - try: - decoded = base64.b64decode(cursor.encode()).decode() - return json.loads(decoded) - except (ValueError, json.JSONDecodeError): - return None - - @staticmethod - def create_offset_cursor(offset: int) -> str: - """Create a cursor for offset-based pagination. - - Args: - offset: The offset for the next page - - Returns: - Cursor string - """ - return CursorManager.create_cursor({"offset": offset}) - - @staticmethod - def parse_offset_cursor(cursor: Optional[str]) -> int: - """Parse an offset cursor. - - Args: - cursor: Cursor string or None - - Returns: - Offset value (0 if cursor is None or invalid) - """ - if not cursor: - return 0 - - data = CursorManager.parse_cursor(cursor) - if not data or "offset" not in data: - return 0 - - return int(data["offset"]) - - -class Paginator(Generic[T]): - """Generic paginator for any list of items.""" - - def __init__(self, items: List[T], page_size: int = 100): - """Initialize paginator. - - Args: - items: List of items to paginate - page_size: Number of items per page - """ - self.items = items - self.page_size = page_size - - def get_page(self, cursor: Optional[str] = None) -> PaginatedResponse[T]: - """Get a page of results. - - Args: - cursor: Optional cursor for the page - - Returns: - Paginated response with items and next cursor - """ - offset = CursorManager.parse_offset_cursor(cursor) - - # Get the page of items - start = offset - end = min(start + self.page_size, len(self.items)) - page_items = self.items[start:end] - - # Create next cursor if there are more items - next_cursor = None - if end < len(self.items): - next_cursor = CursorManager.create_offset_cursor(end) - - return PaginatedResponse(items=page_items, next_cursor=next_cursor) - - -class StreamPaginator(Generic[T]): - """Paginator for streaming/generator-based results.""" - - def __init__(self, page_size: int = 100): - """Initialize stream paginator. - - Args: - page_size: Number of items per page - """ - self.page_size = page_size - - def paginate_stream( - self, stream_generator, cursor: Optional[str] = None - ) -> PaginatedResponse[T]: - """Paginate results from a stream/generator. - - Args: - stream_generator: Generator function that yields items - cursor: Optional cursor for resuming - - Returns: - Paginated response - """ - items = [] - skip_count = 0 - - # Parse cursor to get skip count - if cursor: - cursor_data = CursorManager.parse_cursor(cursor) - if cursor_data and "skip" in cursor_data: - skip_count = cursor_data["skip"] - - # Skip items based on cursor - item_count = 0 - for item in stream_generator(): - if item_count < skip_count: - item_count += 1 - continue - - items.append(item) - if len(items) >= self.page_size: - # We have a full page, create cursor for next page - next_cursor = CursorManager.create_cursor( - {"skip": skip_count + len(items)} - ) - return PaginatedResponse(items=items, next_cursor=next_cursor) - - # No more items - return PaginatedResponse(items=items, next_cursor=None) - - -def paginate_list( - items: List[T], cursor: Optional[str] = None, page_size: int = 100 -) -> PaginatedResponse[T]: - """Convenience function to paginate a list. - - Args: - items: List to paginate - cursor: Optional cursor - page_size: Items per page - - Returns: - Paginated response - """ - paginator = Paginator(items, page_size) - return paginator.get_page(cursor) - - -def validate_cursor(cursor: str) -> bool: - """Validate that a cursor is properly formatted. - - Args: - cursor: Cursor string to validate - - Returns: - True if valid, False otherwise - """ - return CursorManager.parse_cursor(cursor) is not None diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/permissions.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/permissions.py deleted file mode 100644 index db51601d5..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/permissions.py +++ /dev/null @@ -1,405 +0,0 @@ -"""Permission system for the Hanzo AI server. - -Extends the base PermissionManager from hanzo-tools-core with additional -security features and MCP-specific functionality. - -Two complementary layers: -- **Tool-level**: PermissionPolicy from hanzoai.protocols (should I run bash at all?) -- **Path-level**: PermissionManager (can bash access this file?) - -Downstream code should import everything from here -- no need to touch -hanzoai.protocols directly. -""" - -import json -import logging -import os -import sys -import tempfile -from collections.abc import Awaitable, Callable -from pathlib import Path -from typing import Any, TypeVar, final - -# Import base from hanzo-tools-core -from hanzo_tools.core.permissions import PermissionManager as BasePermissionManager - -# Canonical tool-level permission types from hanzoai core SDK. -# Re-exported so downstream only imports from this module. -from hanzoai.protocols import ( - PermissionMode, - PermissionOutcome, - PermissionPolicy, - PermissionPrompter, - PermissionRequest, -) - -logger = logging.getLogger(__name__) - -# Define type variables for better type annotations -T = TypeVar("T") -P = TypeVar("P") - - -@final -class PermissionManager(BasePermissionManager): - """Enhanced permission manager for MCP server. - - Extends the base PermissionManager with: - - Additional security patterns for sensitive files - - Path traversal protection - - Symlink attack protection - - JSON serialization for config persistence - """ - - def __init__( - self, - tool_policy: PermissionPolicy | None = None, - ) -> None: - """Initialize the permission manager with secure defaults. - - Args: - tool_policy: Optional tool-level permission policy. When *None* - a default policy is created that prompts for every tool. - """ - # Initialize with empty allowed paths - we'll add our own - super().__init__(allowed_paths=[], deny_patterns=[]) - - # Tool-level policy (Allow/Deny/Prompt per tool name). - self.tool_policy: PermissionPolicy = tool_policy or PermissionPolicy() - - # Convert to set for O(1) lookups - self.allowed_paths: set[Path] = set() - self.excluded_paths: set[Path] = set() - self.excluded_patterns: list[str] = [] - - # Allowed paths based on platform - if sys.platform == "win32": # Windows - self.allowed_paths.add(Path(tempfile.gettempdir()).resolve()) - else: # Unix/Linux/Mac - self.allowed_paths.add(Path("/tmp").resolve()) - self.allowed_paths.add(Path("/var").resolve()) - - # Also allow user's home directory work folders - home = Path.home() - if home.exists(): - # Add common development directories - work_dir = home / "work" - if work_dir.exists(): - self.allowed_paths.add(work_dir.resolve()) - - # Add default exclusions - self._add_default_exclusions() - - def _add_default_exclusions(self) -> None: - """Add default exclusions for sensitive files and directories.""" - # Sensitive directories - sensitive_dirs: list[str] = [ - ".ssh", - ".gnupg", - "node_modules", - "__pycache__", - ".venv", - "venv", - "env", - ".idea", - ".DS_Store", - ] - self.excluded_patterns.extend(sensitive_dirs) - - # Sensitive file patterns - sensitive_patterns: list[str] = [ - ".env", - "*.key", - "*.pem", - "*.crt", - "*password*", - "*secret*", - "*.sqlite", - "*.db", - "*.sqlite3", - "*.log", - ] - self.excluded_patterns.extend(sensitive_patterns) - - def add_allowed_path(self, path: str) -> None: - """Add a path to the allowed paths. - - Args: - path: The path to allow - """ - resolved_path: Path = Path(path).resolve() - self.allowed_paths.add(resolved_path) - - def remove_allowed_path(self, path: str) -> None: - """Remove a path from the allowed paths. - - Args: - path: The path to remove - """ - resolved_path: Path = Path(path).resolve() - if resolved_path in self.allowed_paths: - self.allowed_paths.remove(resolved_path) - - def exclude_path(self, path: str) -> None: - """Exclude a path from allowed operations. - - Args: - path: The path to exclude - """ - resolved_path: Path = Path(path).resolve() - self.excluded_paths.add(resolved_path) - - def add_exclusion_pattern(self, pattern: str) -> None: - """Add an exclusion pattern. - - Args: - pattern: The pattern to exclude - """ - self.excluded_patterns.append(pattern) - - def is_path_allowed(self, path: str) -> bool: - """Check if a path is allowed with security validation. - - Args: - path: The path to check - - Returns: - True if the path is allowed, False otherwise - """ - # Security check: Reject paths with traversal attempts - if ".." in str(path) or "~" in str(path): - return False - - try: - # Resolve the path (follows symlinks and makes absolute) - resolved_path: Path = Path(path).resolve(strict=False) - - # Security check: Ensure resolved path doesn't escape allowed directories - # by checking if it's actually under an allowed path after resolution - original_path = Path(path) - if original_path.is_absolute() and str(resolved_path) != str( - original_path.resolve(strict=False) - ): - # Path resolution changed the path significantly, might be symlink attack - # Additional check: is the resolved path still under allowed paths? - pass # Continue to normal checks - except (OSError, RuntimeError) as e: - # Path resolution failed, deny access - log for debugging - logger.debug(f"Path resolution failed for '{path}': {e}") - return False - - # Check exclusions first - if self._is_path_excluded(resolved_path): - return False - - # Check if the path is within any allowed path - for allowed_path in self.allowed_paths: - try: - # This will raise ValueError if resolved_path is not under allowed_path - resolved_path.relative_to(allowed_path) - # Additional check: ensure no symlinks are escaping the allowed directory - if resolved_path.exists() and resolved_path.is_symlink(): - link_target = Path(os.readlink(resolved_path)) - if link_target.is_absolute(): - # Absolute symlink - check if it points within allowed paths - if not any( - self._is_subpath(link_target, ap) - for ap in self.allowed_paths - ): - return False - return True - except ValueError: - continue - - return False - - def _is_subpath(self, child: Path, parent: Path) -> bool: - """Check if child is a subpath of parent.""" - try: - child.resolve().relative_to(parent.resolve()) - return True - except ValueError: - return False - - # ------------------------------------------------------------------ - # Tool-level authorization (delegates to hanzoai PermissionPolicy) - # ------------------------------------------------------------------ - - def authorize_tool( - self, - tool_name: str, - input: str, - prompter: PermissionPrompter | None = None, - ) -> PermissionOutcome: - """Check whether *tool_name* is allowed by the tool-level policy. - - This is purely tool-level -- it does NOT check file paths. - Use :meth:`check` when you need both layers. - """ - return self.tool_policy.authorize(tool_name, input, prompter) - - def check( - self, - tool_name: str, - input: str, - path: str | None = None, - prompter: PermissionPrompter | None = None, - ) -> PermissionOutcome: - """Authorize a tool invocation checking BOTH tool-level and path-level. - - 1. Ask the tool_policy whether *tool_name* is allowed at all. - 2. If a *path* is supplied, verify it passes path-based security. - - Returns the first denial, or ``PermissionOutcome.allow()``. - """ - outcome = self.tool_policy.authorize(tool_name, input, prompter) - if not outcome.allowed: - return outcome - - if path is not None and not self.is_path_allowed(path): - return PermissionOutcome.deny( - f"path not allowed: {path}" - ) - - return PermissionOutcome.allow() - - def _is_path_excluded(self, path: Path) -> bool: - """Check if a path is excluded. - - Args: - path: The path to check - - Returns: - True if the path is excluded, False otherwise - """ - - # Check exact excluded paths - if path in self.excluded_paths: - return True - - # Check excluded patterns - path_str: str = str(path) - - # Get path parts to check for exact directory/file name matches - path_parts = path_str.split(os.sep) - - for pattern in self.excluded_patterns: - # Handle wildcard patterns (e.g., "*.log") - if pattern.startswith("*"): - if path_str.endswith(pattern[1:]): - return True - else: - # For non-wildcard patterns, check if any path component matches exactly - if pattern in path_parts: - return True - - return False - - def to_json(self) -> str: - """Convert the permission manager to a JSON string. - - Returns: - A JSON string representation of the permission manager - """ - data: dict[str, Any] = { - "allowed_paths": [str(p) for p in self.allowed_paths], - "excluded_paths": [str(p) for p in self.excluded_paths], - "excluded_patterns": self.excluded_patterns, - } - - return json.dumps(data) - - @classmethod - def from_json(cls, json_str: str) -> "PermissionManager": - """Create a permission manager from a JSON string. - - Args: - json_str: The JSON string - - Returns: - A new PermissionManager instance - """ - data: dict[str, Any] = json.loads(json_str) - - manager = cls() - - for path in data.get("allowed_paths", []): - manager.add_allowed_path(path) - - for path in data.get("excluded_paths", []): - manager.exclude_path(path) - - manager.excluded_patterns = data.get("excluded_patterns", []) - - return manager - - -class PermissibleOperation: - """A decorator for operations that require permission.""" - - def __init__( - self, - permission_manager: PermissionManager, - operation: str, - get_path_fn: Callable[[list[Any], dict[str, Any]], str] | None = None, - ) -> None: - """Initialize the permissible operation. - - Args: - permission_manager: The permission manager - operation: The operation type (read, write, execute, etc.) - get_path_fn: Optional function to extract the path from args and kwargs - """ - self.permission_manager: PermissionManager = permission_manager - self.operation: str = operation - self.get_path_fn: Callable[[list[Any], dict[str, Any]], str] | None = ( - get_path_fn - ) - - def __call__( - self, func: Callable[..., Awaitable[T]] - ) -> Callable[..., Awaitable[T]]: - """Decorate the function. - - Args: - func: The function to decorate - - Returns: - The decorated function - """ - - async def wrapper(*args: Any, **kwargs: Any) -> T: - # Extract the path - if self.get_path_fn: - # Pass args as a list and kwargs as a dict to the path function - path = self.get_path_fn(list(args), kwargs) - else: - # Default to first argument - path = args[0] if args else next(iter(kwargs.values()), None) - - if not isinstance(path, str): - raise ValueError(f"Invalid path type: {type(path)}") - - # Check permission - if not self.permission_manager.is_path_allowed(path): - raise PermissionError( - f"Operation '{self.operation}' not allowed for path: {path}" - ) - - # Call the function - return await func(*args, **kwargs) - - return wrapper - - -__all__ = [ - # This module's own types - "PermissionManager", - "PermissibleOperation", - # Re-exported from hanzoai.protocols so downstream only imports from here - "PermissionMode", - "PermissionOutcome", - "PermissionPolicy", - "PermissionPrompter", - "PermissionRequest", -] diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/persona_adapter.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/persona_adapter.py deleted file mode 100644 index 96c3505c0..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/persona_adapter.py +++ /dev/null @@ -1,232 +0,0 @@ -"""Adapter to load personalities from hanzo-persona package. - -This module bridges the hanzo-persona package (which has rich persona profiles) -with the hanzo-mcp tool personality system. -""" - -import json -import logging -from pathlib import Path -from typing import Any, Dict, List, Optional - -from hanzo_mcp.tools.common.personality import ( - AI_TOOLS, - BUILD_TOOLS, - DATABASE_TOOLS, - ESSENTIAL_TOOLS, - SEARCH_TOOLS, - UNIX_TOOLS, - VECTOR_TOOLS, - PersonalityRegistry, - ToolPersonality, -) - -logger = logging.getLogger(__name__) - - -# Tool mappings for persona categories -CATEGORY_TOOL_MAPPINGS = { - "programmer": ESSENTIAL_TOOLS - + ["symbols", "multi_edit"] - + SEARCH_TOOLS - + BUILD_TOOLS, - "scientist": ESSENTIAL_TOOLS + ["jupyter", "critic"] + AI_TOOLS + VECTOR_TOOLS, - "philosopher": ESSENTIAL_TOOLS + ["critic", "think", "search"], - "artist": ESSENTIAL_TOOLS + ["watch", "jupyter"] + AI_TOOLS, - "leader": ESSENTIAL_TOOLS + ["tasks", "rules", "agent"] + AI_TOOLS, - "writer": ESSENTIAL_TOOLS + ["search", "critic", "tasks"], - "mathematician": ESSENTIAL_TOOLS + ["jupyter", "symbols", "critic"], - "musician": ESSENTIAL_TOOLS + ["watch", "tasks"], - "athlete": ESSENTIAL_TOOLS + ["tasks", "watch", "process"], - "entrepreneur": ESSENTIAL_TOOLS - + ["tasks", "agent", "consensus"] - + BUILD_TOOLS - + DATABASE_TOOLS, - "activist": ESSENTIAL_TOOLS + ["search", "tasks", "rules"], - "religious_leader": ESSENTIAL_TOOLS + ["search", "critic", "think"], - "military_leader": ESSENTIAL_TOOLS + ["tasks", "process", "critic"] + UNIX_TOOLS, - "explorer": ESSENTIAL_TOOLS + ["search", "watch", "tasks"], - "comedian": ESSENTIAL_TOOLS + ["tasks", "watch", "critic"], - "default": ESSENTIAL_TOOLS + AI_TOOLS, -} - - -def persona_to_tool_personality(persona: Dict[str, Any]) -> Optional[ToolPersonality]: - """Convert a hanzo-persona profile to a ToolPersonality. - - Args: - persona: Raw persona dict from hanzo-persona package - - Returns: - ToolPersonality instance or None if invalid - """ - try: - # Get name - could be 'id' or 'name' field - name = persona.get("id") or persona.get("name", "").lower().replace(" ", "_") - if not name: - return None - - # Get programmer name (display name) - programmer = persona.get("programmer") or persona.get("name", name) - - # Get description - description = persona.get("description", "") - - # Get philosophy - philosophy = persona.get("philosophy", "") - - # Determine tools based on category and persona's tool preferences - category = persona.get("category", "default") - base_tools = CATEGORY_TOOL_MAPPINGS.get( - category, CATEGORY_TOOL_MAPPINGS["default"] - ) - - # Add tools from persona's tool preferences - tools_config = persona.get("tools", {}) - if isinstance(tools_config, dict): - essential = tools_config.get("essential", []) - preferred = tools_config.get("preferred", []) - all_tools = list(set(base_tools + essential + preferred)) - elif isinstance(tools_config, list): - all_tools = list(set(base_tools + tools_config)) - else: - all_tools = list(base_tools) - - # Map common tool names to hanzo-mcp tool names - tool_name_map = { - "python": "uvx", - "formatter": "edit", - "testing": "bash", - "documentation": "rules", - "pytest": "bash", - } - all_tools = [tool_name_map.get(t, t) for t in all_tools] - all_tools = list(set(all_tools)) # Dedupe - - # Get environment variables - environment = persona.get("environment", {}) - - # Extract extended fields - ocean = persona.get("ocean") - behavioral_traits = persona.get("behavioral_traits") - cognitive_style = persona.get("cognitive_style") - social_dynamics = persona.get("social_dynamics") - communication_patterns = persona.get("communication_patterns") - work_methodology = persona.get("work_methodology") - emotional_profile = persona.get("emotional_profile") - - return ToolPersonality( - name=name, - programmer=programmer, - description=description, - tools=all_tools, - environment=environment if environment else None, - philosophy=philosophy if philosophy else None, - category=category, - ocean=ocean, - behavioral_traits=behavioral_traits, - cognitive_style=cognitive_style, - social_dynamics=social_dynamics, - communication_patterns=communication_patterns, - work_methodology=work_methodology, - emotional_profile=emotional_profile, - ) - except Exception as e: - logger.warning(f"Failed to convert persona: {e}") - return None - - -def load_personas_from_package() -> int: - """Load all personas from hanzo-persona package. - - Returns: - Number of personas loaded - """ - loaded_count = 0 - - try: - # Try to import hanzo-persona package - from personalities.personality_loader import PERSONA_DIR, PersonalityLoader - - # Load from all_personalities.json if exists - all_personalities_file = PERSONA_DIR / "all_personalities.json" - if all_personalities_file.exists(): - loader = PersonalityLoader(all_personalities_file) - for persona in loader.get_all(): - tp = persona_to_tool_personality(persona) - if tp: - PersonalityRegistry.register(tp) - loaded_count += 1 - logger.info(f"Loaded {loaded_count} personas from all_personalities.json") - return loaded_count - except ImportError: - pass - except Exception as e: - logger.debug(f"Could not load from personalities module: {e}") - - # Try loading from profiles directory - try: - profiles_dir = _find_profiles_dir() - if profiles_dir and profiles_dir.exists(): - loaded_count = _load_from_profiles_dir(profiles_dir) - logger.info(f"Loaded {loaded_count} personas from profiles directory") - return loaded_count - except Exception as e: - logger.debug(f"Could not load from profiles dir: {e}") - - return loaded_count - - -def _find_profiles_dir() -> Optional[Path]: - """Find the hanzo-persona profiles directory.""" - # Check common locations - search_paths = [ - Path.home() / "work" / "hanzo" / "experiments" / "persona" / "profiles", - Path.home() / "work" / "hanzo" / "persona" / "profiles", - Path("/opt/hanzo/persona/profiles"), - ] - - # Check HANZO_PERSONA_DIR environment variable - import os - - env_path = os.environ.get("HANZO_PERSONA_DIR") - if env_path: - search_paths.insert(0, Path(env_path) / "profiles") - - for path in search_paths: - if path.exists(): - return path - - return None - - -def _load_from_profiles_dir(profiles_dir: Path) -> int: - """Load personas from individual JSON files in profiles directory.""" - loaded_count = 0 - - for json_file in profiles_dir.glob("*.json"): - if json_file.name in ["index.json", "categories.json"]: - continue - - try: - with open(json_file, "r", encoding="utf-8") as f: - persona = json.load(f) - - tp = persona_to_tool_personality(persona) - if tp: - PersonalityRegistry.register(tp) - loaded_count += 1 - except Exception as e: - logger.debug(f"Failed to load {json_file}: {e}") - - return loaded_count - - -def get_persona_categories() -> List[str]: - """Get list of available persona categories.""" - return list(CATEGORY_TOOL_MAPPINGS.keys()) - - -def get_personas_by_category(category: str) -> List[ToolPersonality]: - """Get all personas in a specific category.""" - return [p for p in PersonalityRegistry.list() if p.category == category] diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/personality.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/personality.py deleted file mode 100644 index 2320e046e..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/personality.py +++ /dev/null @@ -1,292 +0,0 @@ -"""Tool personality system for organizing development tools based on programmer profiles. - -This module provides the core dataclasses and registry for tool personalities. -Actual personality profiles are loaded from the hanzo-persona package. -""" - -import os -from dataclasses import dataclass -from typing import Dict, List, Optional, Set - - -@dataclass -class CLIToolDef: - """Definition for a CLI tool to be created for a personality.""" - - name: str - command: str - description: str - timeout: int = 120 - - -@dataclass -class ToolPersonality: - """Represents a programmer personality with tool preferences.""" - - name: str - programmer: str - description: str - tools: List[str] - environment: Optional[Dict[str, str]] = None - philosophy: Optional[str] = None - cli_tools: Optional[List[CLIToolDef]] = None # Dynamic CLI tools for this mode - # Extended fields from hanzo-persona - category: Optional[str] = None - ocean: Optional[Dict[str, int]] = None # Big 5 personality traits - behavioral_traits: Optional[Dict] = None - cognitive_style: Optional[Dict] = None - social_dynamics: Optional[Dict] = None - communication_patterns: Optional[Dict] = None - work_methodology: Optional[Dict] = None - emotional_profile: Optional[Dict] = None - - def __post_init__(self): - """Validate personality configuration.""" - if not self.name: - raise ValueError("Personality name is required") - if not self.tools: - raise ValueError("Personality must include at least one tool") - - -class PersonalityRegistry: - """Registry for tool personalities.""" - - _personalities: Dict[str, ToolPersonality] = {} - _active_personality: Optional[str] = None - - @classmethod - def register(cls, personality: ToolPersonality) -> None: - """Register a tool personality.""" - cls._personalities[personality.name] = personality - - @classmethod - def get(cls, name: str) -> Optional[ToolPersonality]: - """Get a personality by name.""" - return cls._personalities.get(name) - - @classmethod - def list(cls) -> List[ToolPersonality]: - """List all registered personalities.""" - return list(cls._personalities.values()) - - @classmethod - def set_active(cls, name: str) -> None: - """Set the active personality.""" - if name not in cls._personalities: - raise ValueError(f"Personality '{name}' not found") - cls._active_personality = name - - @classmethod - def get_active(cls) -> Optional[ToolPersonality]: - """Get the active personality.""" - if cls._active_personality: - return cls._personalities.get(cls._active_personality) - return None - - @classmethod - def get_active_tools(cls) -> Set[str]: - """Get the set of tools from the active personality.""" - personality = cls.get_active() - if personality: - return set(personality.tools) - return set() - - @classmethod - def clear(cls) -> None: - """Clear all personalities (for testing/reload).""" - cls._personalities = {} - cls._active_personality = None - - -# Essential tools โ€” HIP-0300 axis surface, always available. -# One tool per axis: bytes+paths, execution, symbols, history, network, -# orchestration. Action-routed dispatch on top, not split tools. -ESSENTIAL_TOOLS = [ - # HIP-0300 axes - "fs", # Bytes + Paths (read, write, edit, list, stat, apply_patch, search_text) - "exec", # Execution (run, background, ps, kill, logs) - "code", # Symbols + Semantics (parse, search, transform, summarize) - "git", # Diffs + History - "fetch", # Network (get, post, download) - "plan", # Orchestration / intent - # Knowledge persistence - "memory", - # Reasoning (lightweight) - "think", - "critic", - # Agent spawning - "agent", - # System / configuration - "config", - "mode", - "tool", -] - -# Heavy tools (require large dependencies like llm) -# These are opt-in only, not included by default -HEAVY_TOOLS = [ - "llm", - "consensus", -] - -# Common tool sets โ€” kept as aliases for backward compatibility, but each -# now points at the HIP-0300 axis tool that absorbs the legacy split. -UNIX_TOOLS = ["exec"] -BUILD_TOOLS = ["exec"] -VERSION_CONTROL = ["git"] -AI_TOOLS = ["agent", "consensus", "critic", "think", "llm"] -SEARCH_TOOLS = ["code", "fs"] -DATABASE_TOOLS = ["sql_query", "sql_search", "graph_add", "graph_query"] -VECTOR_TOOLS = ["vector_index", "vector_search"] - - -def register_default_personalities() -> None: - """Register personalities from hanzo-persona package and builtin personalities. - - Always registers builtin personalities (hanzo, minimal, fullstack, devops, security) - and optionally loads additional personas from hanzo-persona package. - """ - # Always register builtin personalities first - _register_builtin_personalities() - - # Then load additional personas from hanzo-persona if available - try: - from hanzo_mcp.tools.common.persona_adapter import load_personas_from_package - - loaded = load_personas_from_package() - if loaded > 0: - import logging - - logging.getLogger(__name__).debug( - f"Loaded {loaded} personas from hanzo-persona package" - ) - except ImportError: - pass - except Exception as e: - import logging - - logging.getLogger(__name__).warning( - f"Failed to load personas from package: {e}" - ) - - -def _register_builtin_personalities() -> None: - """Register minimal built-in personalities when hanzo-persona is unavailable.""" - builtin = [ - ToolPersonality( - name="hanzo", - programmer="Hanzo AI Default", - description="Balanced productivity and quality", - philosophy="The Zen of Model Context Protocol.", - tools=list( - set( - ESSENTIAL_TOOLS - + [ - "agent", - "tasks", - "browser", - "computer", - "search", - "find", - "ast", - "jupyter", - "refactor", - "lsp", - "zen", - "review", # Agent sub-tools - ] - + BUILD_TOOLS - ) - ), - environment={"HANZO_MODE": "zen"}, - ), - ToolPersonality( - name="minimal", - programmer="Minimalist", - description="Just the essentials", - philosophy="Less is more.", - tools=list(set(ESSENTIAL_TOOLS)), - environment={"MINIMAL_MODE": "true"}, - ), - ToolPersonality( - name="fullstack", - programmer="Full Stack Developer", - description="Every tool for every job", - philosophy="Jack of all trades, master of... well, all trades.", - tools=list( - set( - ESSENTIAL_TOOLS - + AI_TOOLS - + SEARCH_TOOLS - + DATABASE_TOOLS - + BUILD_TOOLS - + UNIX_TOOLS - + VECTOR_TOOLS - + [ - "tasks", - "rules", - "browser", - "jupyter", - "neovim_edit", - "mcp", - "refactor", - "lsp", - ] - ) - ), - environment={"ALL_TOOLS": "enabled"}, - ), - ToolPersonality( - name="devops", - programmer="DevOps Engineer", - description="Automate everything", - philosophy="You build it, you run it.", - tools=list( - set(ESSENTIAL_TOOLS + BUILD_TOOLS + UNIX_TOOLS + ["tasks", "browser"]) - ), - environment={"CI_CD": "enabled"}, - cli_tools=[ - CLIToolDef("docker", "docker", "Docker container management"), - CLIToolDef("kubectl", "kubectl", "Kubernetes CLI", timeout=60), - CLIToolDef("terraform", "terraform", "Infrastructure as Code"), - CLIToolDef("helm", "helm", "Kubernetes package manager"), - CLIToolDef("aws", "aws", "AWS CLI", timeout=120), - CLIToolDef("gcloud", "gcloud", "Google Cloud CLI", timeout=120), - ], - ), - ToolPersonality( - name="security", - programmer="Security Researcher", - description="Break it to secure it", - philosophy="The only secure system is one that's powered off.", - tools=list(set(ESSENTIAL_TOOLS + UNIX_TOOLS + ["browser", "ast"])), - environment={"SECURITY_MODE": "paranoid"}, - ), - ] - - for personality in builtin: - PersonalityRegistry.register(personality) - - -def ensure_agent_enabled(personality: ToolPersonality) -> ToolPersonality: - """Ensure agent tool is enabled if API keys are present.""" - api_keys_present = any( - os.environ.get(key) - for key in [ - "OPENAI_API_KEY", - "ANTHROPIC_API_KEY", - "GOOGLE_API_KEY", - "HANZO_API_KEY", - "GROQ_API_KEY", - "TOGETHER_API_KEY", - "MISTRAL_API_KEY", - "PERPLEXITY_API_KEY", - ] - ) - - if api_keys_present and "agent" not in personality.tools: - personality.tools.append("agent") - if "consensus" not in personality.tools: - personality.tools.append("consensus") - - return personality diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/plugin_loader.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/plugin_loader.py deleted file mode 100644 index 76609885b..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/plugin_loader.py +++ /dev/null @@ -1,287 +0,0 @@ -"""Plugin loader for custom user tools.""" - -import importlib.util -import inspect -import json -import os -import sys -from dataclasses import dataclass -from pathlib import Path -from typing import Any, Dict, List, Optional, Type - -from .base import BaseTool - - -@dataclass -class ToolPlugin: - """Represents a loaded tool plugin.""" - - name: str - tool_class: Type[BaseTool] - source_path: Path - metadata: Optional[Dict[str, Any]] = None - - -class PluginLoader: - """Loads custom tool plugins from user directories.""" - - def __init__(self): - self.plugins: Dict[str, ToolPlugin] = {} - self.plugin_dirs: List[Path] = [] - self._setup_plugin_directories() - - def _setup_plugin_directories(self): - """Set up standard plugin directories.""" - # User's home directory plugins - home_plugins = Path.home() / ".hanzo" / "plugins" - home_plugins.mkdir(parents=True, exist_ok=True) - self.plugin_dirs.append(home_plugins) - - # Project-local plugins - project_plugins = Path.cwd() / ".hanzo" / "plugins" - if project_plugins.exists(): - self.plugin_dirs.append(project_plugins) - - # Environment variable for additional paths - if custom_paths := os.environ.get("HANZO_PLUGIN_PATH"): - for path in custom_paths.split(":"): - plugin_dir = Path(path) - if plugin_dir.exists(): - self.plugin_dirs.append(plugin_dir) - - def load_plugins(self) -> Dict[str, ToolPlugin]: - """Load all plugins from configured directories.""" - for plugin_dir in self.plugin_dirs: - if not plugin_dir.exists(): - continue - - # Look for Python files - for py_file in plugin_dir.glob("*.py"): - if py_file.name.startswith("_"): - continue - - try: - self._load_plugin_file(py_file) - except Exception as e: - print(f"Failed to load plugin {py_file}: {e}") - - # Look for plugin packages - for package_dir in plugin_dir.iterdir(): - if package_dir.is_dir() and (package_dir / "__init__.py").exists(): - try: - self._load_plugin_package(package_dir) - except Exception as e: - print(f"Failed to load plugin package {package_dir}: {e}") - - return self.plugins - - def _load_plugin_file(self, file_path: Path): - """Load a single plugin file.""" - # Load the module - spec = importlib.util.spec_from_file_location(file_path.stem, file_path) - if not spec or not spec.loader: - return - - module = importlib.util.module_from_spec(spec) - sys.modules[file_path.stem] = module - spec.loader.exec_module(module) - - # Find tool classes - for _name, obj in inspect.getmembers(module): - if ( - inspect.isclass(obj) - and issubclass(obj, BaseTool) - and obj != BaseTool - and hasattr(obj, "name") - ): - # Load metadata if available - metadata = None - metadata_file = file_path.with_suffix(".json") - if metadata_file.exists(): - with open(metadata_file) as f: - metadata = json.load(f) - - plugin = ToolPlugin( - name=obj.name, - tool_class=obj, - source_path=file_path, - metadata=metadata, - ) - self.plugins[obj.name] = plugin - - def _load_plugin_package(self, package_dir: Path): - """Load a plugin package.""" - # Add parent to path temporarily - parent = str(package_dir.parent) - if parent not in sys.path: - sys.path.insert(0, parent) - - try: - # Import the package - module = importlib.import_module(package_dir.name) - - # Look for tools - if hasattr(module, "TOOLS"): - # Package exports TOOLS list - for tool_class in module.TOOLS: - if issubclass(tool_class, BaseTool): - plugin = ToolPlugin( - name=tool_class.name, - tool_class=tool_class, - source_path=package_dir, - ) - self.plugins[tool_class.name] = plugin - else: - # Search for tool classes - for _name, obj in inspect.getmembers(module): - if ( - inspect.isclass(obj) - and issubclass(obj, BaseTool) - and obj != BaseTool - and hasattr(obj, "name") - ): - plugin = ToolPlugin( - name=obj.name, tool_class=obj, source_path=package_dir - ) - self.plugins[obj.name] = plugin - finally: - # Remove from path - if parent in sys.path: - sys.path.remove(parent) - - def get_tool_class(self, name: str) -> Optional[Type[BaseTool]]: - """Get a tool class by name.""" - plugin = self.plugins.get(name) - return plugin.tool_class if plugin else None - - def list_plugins(self) -> List[str]: - """List all loaded plugin names.""" - return list(self.plugins.keys()) - - -# Global plugin loader instance -_plugin_loader = PluginLoader() - - -def load_user_plugins() -> Dict[str, ToolPlugin]: - """Load all user plugins.""" - return _plugin_loader.load_plugins() - - -def get_plugin_tool(name: str) -> Optional[Type[BaseTool]]: - """Get a plugin tool class by name.""" - return _plugin_loader.get_tool_class(name) - - -def list_plugin_tools() -> List[str]: - """List all available plugin tools.""" - return _plugin_loader.list_plugins() - - -def create_plugin_template(output_dir: Path, tool_name: str): - """Create a template for a new plugin tool.""" - output_dir.mkdir(parents=True, exist_ok=True) - - # Create tool file - tool_file = output_dir / f"{tool_name}_tool.py" - tool_content = f'''"""Custom {tool_name} tool plugin.""" - -from hanzo_mcp.tools.common.base import BaseTool -from typing import Dict, Any - - -class {tool_name.title()}Tool(BaseTool): - """Custom {tool_name} tool implementation.""" - - name = "{tool_name}" - description = "Custom {tool_name} tool" - - async def run(self, params: Dict[str, Any], ctx) -> Dict[str, Any]: - """Execute the {tool_name} tool.""" - # Get parameters - action = params.get("action", "default") - - # Implement your tool logic here - if action == "default": - return {{ - "status": "success", - "message": f"Running {tool_name} tool", - "data": {{ - "params": params - }} - }} - - # Add more actions as needed - elif action == "custom_action": - # Your custom logic here - pass - - return {{ - "status": "error", - "message": f"Unknown action: {{action}}" - }} - - -# Optional: Export tools explicitly -TOOLS = [{tool_name.title()}Tool] -''' - - with open(tool_file, "w") as f: - f.write(tool_content) - - # Create metadata file - metadata_file = output_dir / f"{tool_name}_tool.json" - metadata_content = { - "name": tool_name, - "version": "1.0.0", - "author": "Your Name", - "description": f"Custom {tool_name} tool", - "modes": ["custom"], # Modes this tool should be added to - "dependencies": [], - "config": { - # Tool-specific configuration - }, - } - - with open(metadata_file, "w") as f: - json.dump(metadata_content, f, indent=2) - - # Create README - readme_file = output_dir / "README.md" - readme_content = f"""# {tool_name.title()} Tool Plugin - -Custom tool plugin for Hanzo MCP. - -## Installation - -1. Place this directory in one of: - - `~/.hanzo/plugins/` - - `./.hanzo/plugins/` (project-specific) - - Any path in `HANZO_PLUGIN_PATH` environment variable - -2. The tool will be automatically loaded when Hanzo MCP starts. - -## Usage - -The tool will be available as `{tool_name}` in any mode that includes it. - -## Configuration - -Edit the `{tool_name}_tool.json` file to: -- Add the tool to specific modes -- Configure tool-specific settings -- Specify dependencies - -## Development - -Modify `{tool_name}_tool.py` to implement your custom functionality. -""" - - with open(readme_file, "w") as f: - f.write(readme_content) - - print(f"Created plugin template in {output_dir}") - print("Files created:") - print(f" - {tool_file}") - print(f" - {metadata_file}") - print(f" - {readme_file}") diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/stats.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/stats.py deleted file mode 100644 index 380ee04e5..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/stats.py +++ /dev/null @@ -1,281 +0,0 @@ -"""Comprehensive system and MCP statistics.""" - -from datetime import datetime -from pathlib import Path -from typing import TypedDict, Unpack, final, override - -import psutil -from hanzo_tools.database.database_manager import DatabaseManager -from hanzo_tools.shell.run_background import RunBackgroundTool -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_mcp.tools.common.auto_timeout import auto_timeout -from hanzo_mcp.tools.common.base import BaseTool -from hanzo_mcp.tools.common.context import create_tool_context -from hanzo_mcp.tools.mcp.mcp_add import McpAddTool - - -class StatsParams(TypedDict, total=False): - """Parameters for stats tool.""" - - pass - - -@final -class StatsTool(BaseTool): - """Tool for showing comprehensive system and MCP statistics.""" - - def __init__(self, db_manager: DatabaseManager = None): - """Initialize the stats tool. - - Args: - db_manager: Optional database manager for DB stats - """ - self.db_manager = db_manager - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "stats" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Show comprehensive system and Hanzo AI statistics. - -Displays: -- System resources (CPU, memory, disk) -- Running processes -- Database usage -- MCP server status -- Tool usage statistics -- Warnings for high resource usage - -Example: -- stats -""" - - @override - @auto_timeout("stats") - async def call( - self, - ctx: MCPContext, - **params: Unpack[StatsParams], - ) -> str: - """Get comprehensive statistics. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Comprehensive statistics - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - output = [] - warnings = [] - - # Header - output.append("=== Hanzo AI System Statistics ===") - output.append(f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") - output.append("") - - # System Resources - output.append("=== System Resources ===") - - # CPU - cpu_percent = psutil.cpu_percent(interval=1) - cpu_count = psutil.cpu_count() - output.append(f"CPU Usage: {cpu_percent}% ({cpu_count} cores)") - if cpu_percent > 90: - warnings.append(f"โš ๏ธ HIGH CPU USAGE: {cpu_percent}%") - - # Memory - memory = psutil.virtual_memory() - memory_used_gb = memory.used / (1024**3) - memory_total_gb = memory.total / (1024**3) - memory_percent = memory.percent - output.append( - f"Memory: {memory_used_gb:.1f}/{memory_total_gb:.1f} GB ({memory_percent}%)" - ) - if memory_percent > 90: - warnings.append(f"โš ๏ธ HIGH MEMORY USAGE: {memory_percent}%") - - # Disk - disk = psutil.disk_usage("/") - disk_used_gb = disk.used / (1024**3) - disk_total_gb = disk.total / (1024**3) - disk_percent = disk.percent - disk_free_gb = disk.free / (1024**3) - output.append( - f"Disk: {disk_used_gb:.1f}/{disk_total_gb:.1f} GB ({disk_percent}%)" - ) - output.append(f"Free Space: {disk_free_gb:.1f} GB") - if disk_percent > 90: - warnings.append( - f"โš ๏ธ LOW DISK SPACE: Only {disk_free_gb:.1f} GB free ({100 - disk_percent:.1f}% remaining)" - ) - - output.append("") - - # Background Processes - output.append("=== Background Processes ===") - processes = RunBackgroundTool.get_processes() - running_count = 0 - total_memory_mb = 0 - - if processes: - for proc in processes.values(): - if proc.is_running(): - running_count += 1 - try: - ps_proc = psutil.Process(proc.process.pid) - memory_mb = ps_proc.memory_info().rss / (1024**2) - total_memory_mb += memory_mb - except Exception: - pass - - output.append(f"Running Processes: {running_count}") - output.append(f"Total Memory Usage: {total_memory_mb:.1f} MB") - - # List top processes by memory - if running_count > 0: - output.append("\nTop Processes:") - proc_list = [] - for proc_id, proc in processes.items(): - if proc.is_running(): - try: - ps_proc = psutil.Process(proc.process.pid) - memory_mb = ps_proc.memory_info().rss / (1024**2) - cpu = ps_proc.cpu_percent(interval=0.1) - proc_list.append((proc.name, memory_mb, cpu, proc_id)) - except Exception: - proc_list.append((proc.name, 0, 0, proc_id)) - - proc_list.sort(key=lambda x: x[1], reverse=True) - for name, mem, cpu, pid in proc_list[:5]: - output.append(f" - {name} ({pid}): {mem:.1f} MB, {cpu:.1f}% CPU") - else: - output.append("No background processes running") - - output.append("") - - # Database Usage - if self.db_manager: - output.append("=== Database Usage ===") - db_dir = Path.home() / ".hanzo" / "db" - total_db_size = 0 - - if db_dir.exists(): - for db_file in db_dir.rglob("*.db"): - size = db_file.stat().st_size - total_db_size += size - - output.append( - f"Total Database Size: {total_db_size / (1024**2):.1f} MB" - ) - output.append(f"Active Projects: {len(self.db_manager.projects)}") - - # List largest databases - db_sizes = [] - for db_file in db_dir.rglob("*.db"): - size = db_file.stat().st_size / (1024**2) - if size > 0.1: # Only show DBs > 100KB - project = db_file.parent.parent.name - db_type = db_file.stem - db_sizes.append((project, db_type, size)) - - if db_sizes: - db_sizes.sort(key=lambda x: x[2], reverse=True) - output.append("\nLargest Databases:") - for project, db_type, size in db_sizes[:5]: - output.append(f" - {project}/{db_type}: {size:.1f} MB") - else: - output.append("No databases found") - - output.append("") - - # MCP Servers - output.append("=== MCP Servers ===") - mcp_servers = McpAddTool.get_servers() - if mcp_servers: - running_mcp = sum( - 1 for s in mcp_servers.values() if s.get("status") == "running" - ) - total_mcp_tools = sum(len(s.get("tools", [])) for s in mcp_servers.values()) - - output.append(f"Total Servers: {len(mcp_servers)}") - output.append(f"Running: {running_mcp}") - output.append(f"Total Tools Available: {total_mcp_tools}") - else: - output.append("No MCP servers configured") - - output.append("") - - # Hanzo AI Specifics - output.append("=== Hanzo AI ===") - - # Log directory size - log_dir = Path.home() / ".hanzo" / "logs" - if log_dir.exists(): - log_size = sum(f.stat().st_size for f in log_dir.rglob("*") if f.is_file()) - log_count = len(list(log_dir.rglob("*.log"))) - output.append(f"Log Files: {log_count} ({log_size / (1024**2):.1f} MB)") - - if log_size > 100 * 1024**2: # > 100MB - warnings.append( - f"โš ๏ธ Large log directory: {log_size / (1024**2):.1f} MB" - ) - - # Config directory - config_dir = Path.home() / ".hanzo" / "mcp" - if config_dir.exists(): - config_count = len(list(config_dir.rglob("*.json"))) - output.append(f"Config Files: {config_count}") - - # Tool status (if available) - # Note: Tool usage statistics tracking can be added here - output.append("\nTool Categories:") - output.append(" - File Operations: grep, find_files, read, write, edit") - output.append(" - Shell: bash, run_background, processes, pkill") - output.append(" - Database: sql_query, graph_query, vector_search") - output.append(" - Package Runners: uvx, npx, uvx_background, npx_background") - output.append(" - MCP Management: mcp_add, mcp_remove, mcp_stats") - - # Warnings Section - if warnings: - output.append("\n=== โš ๏ธ WARNINGS ===") - for warning in warnings: - output.append(warning) - output.append("") - - # Recommendations - output.append("=== Recommendations ===") - if disk_free_gb < 5: - output.append("- Free up disk space (< 5GB remaining)") - if memory_percent > 80: - output.append("- Close unused applications to free memory") - if running_count > 10: - output.append("- Consider stopping unused background processes") - if log_size > 50 * 1024**2: - output.append("- Clean up old log files in ~/.hanzo/logs") - - if not any( - [ - disk_free_gb < 5, - memory_percent > 80, - running_count > 10, - log_size > 50 * 1024**2, - ] - ): - output.append("โœ… System resources are healthy") - - return "\n".join(output) - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/test_helpers.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/test_helpers.py deleted file mode 100644 index f0422fd75..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/test_helpers.py +++ /dev/null @@ -1,42 +0,0 @@ -"""Test helper classes for MCP tools testing.""" - -from typing import Any, Dict, List, Optional - - -class PaginatedResponseWrapper: - """Wrapper class for paginated responses to support tests.""" - - def __init__( - self, - items: Optional[List[Any]] = None, - next_cursor: Optional[str] = None, - has_more: bool = False, - total_items: Optional[int] = None, - ) -> None: - """Initialize paginated response.""" - self.items: List[Any] = items or [] - self.next_cursor: Optional[str] = next_cursor - self.has_more: bool = has_more - self.total_items: int = total_items or len(self.items) - - def to_json(self) -> Dict[str, Any]: - """Convert to JSON-serializable dict.""" - return { - "items": self.items, - "_meta": { - "next_cursor": self.next_cursor, - "has_more": self.has_more, - "total_items": self.total_items, - }, - } - - -# Export a convenience constructor -def PaginatedResponse( - items: Optional[List[Any]] = None, - next_cursor: Optional[str] = None, - has_more: bool = False, - total_items: Optional[int] = None, -) -> PaginatedResponseWrapper: - """Create a paginated response for testing.""" - return PaginatedResponseWrapper(items, next_cursor, has_more, total_items) diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/timeout_parser.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/timeout_parser.py deleted file mode 100644 index 6d7a09a3c..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/timeout_parser.py +++ /dev/null @@ -1,113 +0,0 @@ -"""Human-readable timeout parsing utilities.""" - -import re -from typing import Union - - -def parse_timeout(timeout_str: Union[str, int, float]) -> float: - """Parse timeout from human-readable string or numeric value. - - Supports formats like: - - "2min", "5m", "120s", "30sec", "1.5h", "0.5hr" - - 120 (seconds as number) - - "120" (seconds as string) - - Args: - timeout_str: Timeout value as string or number - - Returns: - Timeout in seconds as float - - Raises: - ValueError: If format is not recognized - """ - if isinstance(timeout_str, (int, float)): - return float(timeout_str) - - if isinstance(timeout_str, str): - # Handle pure numeric strings - try: - return float(timeout_str) - except ValueError: - pass - - # Handle human-readable formats - timeout_str = timeout_str.lower().strip() - - # Regex patterns for different time units - patterns = [ - # Hours: 1h, 1.5hr, 2hour, 3hours - (r"^(\d*\.?\d+)\s*h(?:r|our|ours)?$", 3600), - # Minutes: 2m, 5min, 10mins, 1.5minute - (r"^(\d*\.?\d+)\s*m(?:in|ins|inute|inutes)?$", 60), - # Seconds: 30s, 120sec, 45secs, 60second, 90seconds - (r"^(\d*\.?\d+)\s*s(?:ec|ecs|econd|econds)?$", 1), - ] - - for pattern, multiplier in patterns: - match = re.match(pattern, timeout_str) - if match: - value = float(match.group(1)) - return value * multiplier - - # If no pattern matches, raise error - raise ValueError( - f"Invalid timeout format: '{timeout_str}'. " - f"Supported formats: 2min, 5m, 120s, 30sec, 1.5h, 0.5hr, or numeric seconds." - ) - - raise ValueError(f"Unsupported timeout type: {type(timeout_str)}") - - -def format_timeout(seconds: float) -> str: - """Format timeout seconds into human-readable string. - - Args: - seconds: Timeout in seconds - - Returns: - Human-readable string like "2m", "90s", "1.5h" - """ - if seconds >= 3600: # >= 1 hour - hours = seconds / 3600 - if hours.is_integer(): - return f"{int(hours)}h" - else: - return f"{hours:.1f}h" - elif seconds >= 60: # >= 1 minute - minutes = seconds / 60 - if minutes.is_integer(): - return f"{int(minutes)}m" - else: - return f"{minutes:.1f}m" - else: # < 1 minute - if seconds.is_integer(): - return f"{int(seconds)}s" - else: - return f"{seconds:.1f}s" - - -# Test the parser -if __name__ == "__main__": - test_cases: list[Union[str, int, float]] = [ - "2min", - "5m", - "120s", - "30sec", - "1.5h", - "0.5hr", - "90", - 120, - 3600.0, - "1hour", - "2hours", - "30seconds", - ] - - for case in test_cases: - try: - result = parse_timeout(case) - formatted = format_timeout(result) - print(f"{case} -> {result}s ({formatted})") - except ValueError as e: - print(f"{case} -> ERROR: {e}") diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool.py deleted file mode 100644 index 98c1c79cf..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool.py +++ /dev/null @@ -1,414 +0,0 @@ -"""Unified tool management command. - -Combines install, uninstall, upgrade, enable, disable, and list into a single tool. -""" - -from pathlib import Path -from typing import Annotated, Any, Literal, Optional, TypedDict, Unpack, final, override - -# Import async I/O utilities -from hanzo_async import mkdir, path_exists, read_json, write_json -from mcp.server.fastmcp import Context as MCPContext -from pydantic import Field - -from hanzo_mcp.tools.common.auto_timeout import auto_timeout -from hanzo_mcp.tools.common.base import BaseTool -from hanzo_mcp.tools.common.context import create_tool_context - -Action = Annotated[ - Literal[ - "install", # Install a tool package - "uninstall", # Remove a tool package - "upgrade", # Upgrade package(s) - "reload", # Hot-reload a package - "enable", # Enable a tool - "disable", # Disable a tool - "list", # List installed tools - "status", # Show tool status - "self_update", # Update hanzo-mcp itself - ], - Field(description="Action to perform"), -] - - -class ToolParams(TypedDict, total=False): - """Parameters for unified tool command.""" - - action: str - name: Optional[str] # Tool or package name - source: str # For install: pypi, git, local - version: Optional[str] # Version constraint - persist: bool # Persist enable/disable changes - category: Optional[str] # Filter list by category - disabled: bool # Show only disabled tools - enabled: bool # Show only enabled tools - - -@final -class UnifiedToolTool(BaseTool): - """Unified tool management. - - Combines package installation, tool enable/disable, and listing - into a single coherent command. - """ - - # Tool states stored here - _tool_states: dict = {} - _config_file = Path.home() / ".hanzo" / "mcp" / "tool_states.json" - _initialized = False - - def __init__(self): - """Initialize the tool.""" - # Note: Async initialization happens on first use via _ensure_initialized - pass - - @classmethod - async def _ensure_initialized(cls): - """Ensure states are loaded (async).""" - if not cls._initialized: - await cls._load_states_async() - cls._initialized = True - - @classmethod - async def _load_states_async(cls): - """Load tool states from config file (async).""" - if await path_exists(cls._config_file): - try: - cls._tool_states = await read_json(cls._config_file) - except Exception: - cls._tool_states = {} - else: - cls._tool_states = {} - - @classmethod - async def _save_states_async(cls): - """Save tool states to config file (async).""" - await mkdir(cls._config_file.parent, parents=True, exist_ok=True) - await write_json(cls._config_file, cls._tool_states) - - @classmethod - def is_tool_enabled(cls, tool_name: str) -> bool: - """Check if a tool is enabled (sync, uses cached state).""" - # Uses cached state - call _ensure_initialized first in async contexts - return cls._tool_states.get(tool_name, True) - - @property - @override - def name(self) -> str: - return "tool" - - @property - @override - def description(self) -> str: - return """Unified tool management command. - -Actions: -- list: List all tools and their status -- enable: Enable a disabled tool -- disable: Disable a tool -- status: Check status of a specific tool -- install: Install a tool package from PyPI/git -- uninstall: Remove a tool package -- upgrade: Upgrade package(s) to latest -- reload: Hot-reload a package without restart -- self_update: Update hanzo-mcp itself - -Examples: - tool list # List all tools - tool list --category=shell # List shell tools - tool list --disabled # List disabled tools - tool enable --name=browser # Enable browser tool - tool disable --name=grep # Disable grep - tool status --name=llm # Check llm status - tool install --name=hanzo-tools-data # Install package - tool upgrade # Upgrade all - tool self_update # Update hanzo-mcp -""" - - @override - @auto_timeout("tool") - async def call( - self, - ctx: MCPContext, - **params: Unpack[ToolParams], - ) -> str: - """Execute tool management action.""" - # Ensure async initialization - await self._ensure_initialized() - - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - action = params.get("action", "list") - name = params.get("name") - source = params.get("source", "pypi") - version = params.get("version") - persist = params.get("persist", True) - category = params.get("category") - show_disabled = params.get("disabled", False) - show_enabled = params.get("enabled", False) - - # Route to appropriate handler - if action == "list": - return await self._handle_list(category, show_disabled, show_enabled) - elif action == "status": - return await self._handle_status(name) - elif action == "enable": - return await self._handle_enable(name, persist) - elif action == "disable": - return await self._handle_disable(name, persist) - elif action == "install": - return await self._handle_install(name, source, version, tool_ctx) - elif action == "uninstall": - return await self._handle_uninstall(name, tool_ctx) - elif action == "upgrade": - return await self._handle_upgrade(name, tool_ctx) - elif action == "reload": - return await self._handle_reload(name, tool_ctx) - elif action == "self_update": - return await self._handle_self_update(tool_ctx) - else: - return f"Unknown action: {action}. Use: list, enable, disable, status, install, uninstall, upgrade, reload, self_update" - - async def _handle_list( - self, - category: Optional[str], - show_disabled: bool, - show_enabled: bool, - ) -> str: - """List all tools.""" - from hanzo_mcp.config.tool_config import DynamicToolRegistry - - DynamicToolRegistry.initialize() - all_tools = DynamicToolRegistry.list_all() - - lines = [] - if show_disabled: - lines.append("=== Disabled Tools ===\n") - elif show_enabled: - lines.append("=== Enabled Tools ===\n") - else: - lines.append("=== All Tools ===\n") - - # Group by category - by_category: dict = {} - for tool_name, config in all_tools.items(): - cat = config.category.value if config.category else "other" - if category and cat != category: - continue - if cat not in by_category: - by_category[cat] = [] - - is_enabled = self.is_tool_enabled(tool_name) - if show_disabled and is_enabled: - continue - if show_enabled and not is_enabled: - continue - - by_category[cat].append((tool_name, config, is_enabled)) - - # Output by category - for cat, tools in sorted(by_category.items()): - if not tools: - continue - lines.append(f"[{cat}]") - for tool_name, config, is_enabled in sorted(tools, key=lambda x: x[0]): - status = "โœ“" if is_enabled else "โ—‹" - desc = ( - config.description[:50] + "..." - if len(config.description) > 50 - else config.description - ) - lines.append(f" {status} {tool_name}: {desc}") - lines.append("") - - # Summary - total = sum(len(t) for t in by_category.values()) - enabled = sum(1 for tools in by_category.values() for _, _, e in tools if e) - lines.append( - f"Total: {total} | Enabled: {enabled} | Disabled: {total - enabled}" - ) - - return "\n".join(lines) - - async def _handle_status(self, name: Optional[str]) -> str: - """Check status of a specific tool.""" - if not name: - return "Error: name required for status action" - - from hanzo_mcp.config.tool_config import DynamicToolRegistry - - DynamicToolRegistry.initialize() - config = DynamicToolRegistry.get(name) - - if not config: - return f"Tool '{name}' not found" - - is_enabled = self.is_tool_enabled(name) - status = "enabled" if is_enabled else "disabled" - - return f"""Tool: {name} -Status: {status} -Category: {config.category.value if config.category else "unknown"} -Package: {config.package or "built-in"} -Description: {config.description}""" - - async def _handle_enable(self, name: Optional[str], persist: bool) -> str: - """Enable a tool.""" - if not name: - return "Error: name required for enable action" - - if self.is_tool_enabled(name): - return f"Tool '{name}' is already enabled" - - self._tool_states[name] = True - if persist: - await self._save_states_async() - - return f"โœ“ Enabled tool '{name}'" + ("" if persist else " (temporary)") - - async def _handle_disable(self, name: Optional[str], persist: bool) -> str: - """Disable a tool.""" - if not name: - return "Error: name required for disable action" - - # Prevent disabling critical tools - critical = {"tool", "version", "stats", "config", "mode", "llm", "consensus"} - if name in critical: - return f"Error: Cannot disable critical tool '{name}'" - - if not self.is_tool_enabled(name): - return f"Tool '{name}' is already disabled" - - self._tool_states[name] = False - if persist: - await self._save_states_async() - - return f"โ—‹ Disabled tool '{name}'" + ("" if persist else " (temporary)") - - async def _handle_install( - self, - name: Optional[str], - source: str, - version: Optional[str], - tool_ctx: Any, - ) -> str: - """Install a tool package.""" - if not name: - return "Error: package name required for install" - - from hanzo_mcp.tools.common.tool_registry import get_registry - - await tool_ctx.info(f"Installing {name} from {source}...") - registry = await get_registry() - result = await registry.install(package=name, source=source, version=version) - - if result["success"]: - tools = result.get("tools", []) - return ( - f"โœ“ Installed {name} v{result.get('version', 'latest')}\n" - f"Tools: {', '.join(tools) if tools else 'none detected'}" - ) - return f"โœ— Failed: {result.get('error', 'unknown error')}" - - async def _handle_uninstall(self, name: Optional[str], tool_ctx: Any) -> str: - """Uninstall a tool package.""" - if not name: - return "Error: package name required for uninstall" - - from hanzo_mcp.tools.common.tool_registry import get_registry - - await tool_ctx.info(f"Uninstalling {name}...") - registry = await get_registry() - result = await registry.uninstall(name) - - if result["success"]: - return f"โœ“ Uninstalled {name}" - return f"โœ— Failed: {result.get('error', 'unknown error')}" - - async def _handle_upgrade(self, name: Optional[str], tool_ctx: Any) -> str: - """Upgrade tool package(s).""" - from hanzo_mcp.tools.common.tool_registry import get_registry - - await tool_ctx.info(f"Upgrading {name or 'all packages'}...") - registry = await get_registry() - result = await registry.upgrade(name) - - if result["success"]: - upgraded = [ - r["package"] for r in result.get("results", []) if r.get("success") - ] - return f"โœ“ Upgraded: {', '.join(upgraded) if upgraded else 'none'}" - return "โœ— Upgrade failed" - - async def _handle_reload(self, name: Optional[str], tool_ctx: Any) -> str: - """Hot-reload a tool package.""" - if not name: - return "Error: package name required for reload" - - from hanzo_mcp.tools.common.tool_registry import get_registry - - await tool_ctx.info(f"Reloading {name}...") - registry = await get_registry() - result = await registry.reload_package(name) - - if result["success"]: - tools = result.get("tools", []) - return f"โœ“ Reloaded {name}\nTools: {', '.join(tools) if tools else 'none'}" - return f"โœ— Failed: {result.get('error', 'unknown error')}" - - async def _handle_self_update(self, tool_ctx: Any) -> str: - """Update hanzo-mcp itself.""" - from hanzo_mcp.tools.common.tool_registry import get_registry - - await tool_ctx.info("Checking for hanzo-mcp updates...") - registry = await get_registry() - result = await registry.self_update() - - if result["success"]: - return f"โœ“ Updated hanzo-mcp from v{result.get('current_version')}\n{result.get('message', '')}" - return f"โœ— Update failed: {result.get('error', 'unknown error')}" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - tool_instance = self - - @mcp_server.tool() - async def tool( - action: Action = "list", - name: Annotated[ - Optional[str], Field(description="Tool or package name") - ] = None, - source: Annotated[ - str, Field(description="Source: pypi, git, local") - ] = "pypi", - version: Annotated[ - Optional[str], Field(description="Version constraint") - ] = None, - persist: Annotated[bool, Field(description="Persist changes")] = True, - category: Annotated[ - Optional[str], Field(description="Filter by category") - ] = None, - disabled: Annotated[bool, Field(description="Show only disabled")] = False, - enabled: Annotated[bool, Field(description="Show only enabled")] = False, - ctx: MCPContext = None, - ) -> str: - """Unified tool management: install, enable, disable, list, upgrade.""" - return await tool_instance.call( - ctx, - action=action, - name=name, - source=source, - version=version, - persist=persist, - category=category, - disabled=disabled, - enabled=enabled, - ) - - -def register_unified_tool(mcp_server) -> list: - """Register the unified tool command.""" - tool = UnifiedToolTool() - tool.register(mcp_server) - return [tool] diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool_disable.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool_disable.py deleted file mode 100644 index 0fba752a6..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool_disable.py +++ /dev/null @@ -1,151 +0,0 @@ -"""Disable tools dynamically.""" - -from typing import Annotated, TypedDict, Unpack, final, override - -from mcp.server.fastmcp import Context as MCPContext -from pydantic import Field - -from hanzo_mcp.tools.common.auto_timeout import auto_timeout -from hanzo_mcp.tools.common.base import BaseTool -from hanzo_mcp.tools.common.context import create_tool_context -from hanzo_mcp.tools.common.tool_enable import ToolEnableTool - -ToolName = Annotated[ - str, - Field( - description="Name of the tool to disable (e.g., 'grep', 'vector_search')", - min_length=1, - ), -] - -Persist = Annotated[ - bool, - Field( - description="Persist the change to config file", - default=True, - ), -] - - -class ToolDisableParams(TypedDict, total=False): - """Parameters for tool disable.""" - - tool: str - persist: bool - - -@final -class ToolDisableTool(BaseTool): - """Tool for disabling other tools dynamically.""" - - def __init__(self): - """Initialize the tool disable tool.""" - # Ensure states are loaded - if not ToolEnableTool._initialized: - ToolEnableTool._load_states() - ToolEnableTool._initialized = True - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "tool_disable" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Disable tools to prevent their use. - -This allows you to temporarily or permanently disable tools. -Useful for testing or when a tool is misbehaving. -Changes are persisted by default. - -Critical tools (tool_enable, tool_disable, tool_list) cannot be disabled. - -Examples: -- tool_disable --tool vector_search -- tool_disable --tool uvx_background -- tool_disable --tool grep --no-persist - -Use 'tool_list' to see all available tools and their status. -Use 'tool_enable' to re-enable disabled tools. -""" - - @override - @auto_timeout("tool_disable") - async def call( - self, - ctx: MCPContext, - **params: Unpack[ToolDisableParams], - ) -> str: - """Disable a tool. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Result of disabling the tool - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - tool_name = params.get("tool") - if not tool_name: - return "Error: tool name is required" - - persist = params.get("persist", True) - - # Prevent disabling critical tools - critical_tools = {"tool_enable", "tool_disable", "tool_list", "stats"} - if tool_name in critical_tools: - return f"Error: Cannot disable critical tool '{tool_name}'. These tools are required for system management." - - # Check current state - was_enabled = ToolEnableTool.is_tool_enabled(tool_name) - - if not was_enabled: - return f"Tool '{tool_name}' is already disabled." - - # Disable the tool - ToolEnableTool._tool_states[tool_name] = False - - # Persist if requested - if persist: - ToolEnableTool._save_states() - await tool_ctx.info(f"Disabled tool '{tool_name}' (persisted)") - else: - await tool_ctx.info(f"Disabled tool '{tool_name}' (temporary)") - - output = [ - f"Successfully disabled tool '{tool_name}'", - "", - "The tool is now unavailable for use.", - f"Use 'tool_enable --tool {tool_name}' to re-enable it.", - ] - - if not persist: - output.append( - "\nNote: This change is temporary and will be lost on restart." - ) - - # Warn about commonly used tools - common_tools = {"grep", "read", "write", "bash", "edit"} - if tool_name in common_tools: - output.append( - f"\nโš ๏ธ Warning: '{tool_name}' is a commonly used tool. Disabling it may affect normal operations." - ) - - # Count disabled tools - disabled_count = sum( - 1 for enabled in ToolEnableTool._tool_states.values() if not enabled - ) - output.append(f"\nTotal disabled tools: {disabled_count}") - - return "\n".join(output) - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool_enable.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool_enable.py deleted file mode 100644 index d99aabf69..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool_enable.py +++ /dev/null @@ -1,182 +0,0 @@ -"""Enable tools dynamically.""" - -from pathlib import Path -from typing import Annotated, TypedDict, Unpack, final, override - -# Import async I/O utilities -from hanzo_async import mkdir, path_exists, read_json, write_json -from mcp.server.fastmcp import Context as MCPContext -from pydantic import Field - -from hanzo_mcp.tools.common.auto_timeout import auto_timeout -from hanzo_mcp.tools.common.base import BaseTool -from hanzo_mcp.tools.common.context import create_tool_context - -ToolName = Annotated[ - str, - Field( - description="Name of the tool to enable (e.g., 'grep', 'vector_search')", - min_length=1, - ), -] - -Persist = Annotated[ - bool, - Field( - description="Persist the change to config file", - default=True, - ), -] - - -class ToolEnableParams(TypedDict, total=False): - """Parameters for tool enable.""" - - tool: str - persist: bool - - -@final -class ToolEnableTool(BaseTool): - """Tool for enabling other tools dynamically.""" - - # Class variable to track enabled/disabled tools - _tool_states = {} - _config_file = Path.home() / ".hanzo" / "mcp" / "tool_states.json" - _initialized = False - - def __init__(self): - """Initialize the tool enable tool.""" - # Async initialization happens on first use - pass - - @classmethod - async def _ensure_initialized(cls): - """Ensure states are loaded (async).""" - if not cls._initialized: - await cls._load_states_async() - cls._initialized = True - - @classmethod - async def _load_states_async(cls): - """Load tool states from config file (async).""" - if await path_exists(cls._config_file): - try: - cls._tool_states = await read_json(cls._config_file) - except Exception: - cls._tool_states = {} - else: - cls._tool_states = {} - - @classmethod - async def _save_states_async(cls): - """Save tool states to config file (async).""" - await mkdir(cls._config_file.parent, parents=True, exist_ok=True) - await write_json(cls._config_file, cls._tool_states) - - @classmethod - def is_tool_enabled(cls, tool_name: str) -> bool: - """Check if a tool is enabled (sync, uses cached state). - - Args: - tool_name: Name of the tool - - Returns: - True if enabled (default), False if explicitly disabled - """ - # Uses cached state - call _ensure_initialized first in async contexts - return cls._tool_states.get(tool_name, True) - - @classmethod - def get_all_states(cls) -> dict: - """Get all tool states (sync, uses cached state).""" - return cls._tool_states.copy() - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "tool_enable" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Enable tools that have been disabled. - -This allows you to re-enable tools that were previously disabled. -Changes are persisted by default. - -Examples: -- tool_enable --tool grep -- tool_enable --tool vector_search -- tool_enable --tool uvx_background --no-persist - -Use 'tool_list' to see all available tools and their status. -""" - - @override - @auto_timeout("tool_enable") - async def call( - self, - ctx: MCPContext, - **params: Unpack[ToolEnableParams], - ) -> str: - """Enable a tool. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Result of enabling the tool - """ - # Ensure async initialization - await self._ensure_initialized() - - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - tool_name = params.get("tool") - if not tool_name: - return "Error: tool name is required" - - persist = params.get("persist", True) - - # Check current state - was_enabled = self.is_tool_enabled(tool_name) - - if was_enabled: - return f"Tool '{tool_name}' is already enabled." - - # Enable the tool - self._tool_states[tool_name] = True - - # Persist if requested (async) - if persist: - await self._save_states_async() - await tool_ctx.info(f"Enabled tool '{tool_name}' (persisted)") - else: - await tool_ctx.info(f"Enabled tool '{tool_name}' (temporary)") - - output = [ - f"Successfully enabled tool '{tool_name}'", - "", - "The tool is now available for use.", - ] - - if not persist: - output.append("Note: This change is temporary and will be lost on restart.") - - # Count enabled/disabled tools - disabled_count = sum(1 for enabled in self._tool_states.values() if not enabled) - if disabled_count > 0: - output.append(f"\nCurrently disabled tools: {disabled_count}") - output.append("Use 'tool_list --disabled' to see them.") - - return "\n".join(output) - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool_install.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool_install.py deleted file mode 100644 index addb0716a..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool_install.py +++ /dev/null @@ -1,236 +0,0 @@ -"""Tool installation and self-update tool. - -Allows the AI to: -- Install new tool packages dynamically -- Update existing tools -- Self-update hanzo-mcp -- Hot-reload without restart -""" - -from typing import Annotated, Literal, TypedDict, Unpack, final, override - -from mcp.server.fastmcp import Context as MCPContext -from pydantic import Field - -from hanzo_mcp.tools.common.auto_timeout import auto_timeout -from hanzo_mcp.tools.common.base import BaseTool -from hanzo_mcp.tools.common.context import create_tool_context - -Action = Annotated[ - Literal[ - "install", # Install a tool package - "uninstall", # Remove a tool package - "upgrade", # Upgrade package(s) - "reload", # Hot-reload a package - "list", # List installed packages - "self_update", # Update hanzo-mcp itself - ], - Field(description="Action to perform"), -] - - -class ToolInstallParams(TypedDict, total=False): - """Parameters for tool install actions.""" - - action: str - package: str - source: str - version: str - - -@final -class ToolInstallTool(BaseTool): - """Dynamic tool installation and management. - - Allows the AI to expand its own capabilities by installing - new tool packages, updating existing tools, and even updating itself. - """ - - @property - @override - def name(self) -> str: - return "tool_install" - - @property - @override - def description(self) -> str: - return """Install, update, and manage tool packages dynamically. - -Actions: -- install: Install a new tool package -- uninstall: Remove a tool package -- upgrade: Upgrade package(s) to latest -- reload: Hot-reload a package without restart -- list: List all installed packages -- self_update: Update hanzo-mcp itself - -Examples: - tool_install(action="install", package="hanzo-tools-browser") - tool_install(action="upgrade", package="hanzo-tools-data") - tool_install(action="self_update") - tool_install(action="list") - tool_install(action="reload", package="hanzo-tools-browser") - -Sources: -- pypi: Install from PyPI (default) -- git: Install from git URL -- local: Install from local path -""" - - @override - @auto_timeout("tool_install") - async def call( - self, - ctx: MCPContext, - **params: Unpack[ToolInstallParams], - ) -> str: - """Execute tool management action.""" - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - action = params.get("action", "list") - package = params.get("package") - source = params.get("source", "pypi") - version = params.get("version") - - # Import registry lazily - from hanzo_mcp.tools.common.tool_registry import get_registry - - registry = await get_registry() - - if action == "install": - if not package: - return "Error: package name required for install" - - await tool_ctx.info(f"Installing {package} from {source}...") - result = await registry.install( - package=package, - source=source, - version=version, - ) - - if result["success"]: - tools = result.get("tools", []) - return ( - f"โœ“ Installed {package} v{result.get('version', 'latest')}\n" - f"Tools available: {', '.join(tools) if tools else 'none detected'}" - ) - else: - return f"โœ— Failed to install {package}: {result.get('error', 'unknown error')}" - - elif action == "uninstall": - if not package: - return "Error: package name required for uninstall" - - await tool_ctx.info(f"Uninstalling {package}...") - result = await registry.uninstall(package) - - if result["success"]: - return f"โœ“ Uninstalled {package}" - else: - return f"โœ— Failed to uninstall {package}: {result.get('error', 'unknown error')}" - - elif action == "upgrade": - await tool_ctx.info(f"Upgrading {package or 'all packages'}...") - result = await registry.upgrade(package) - - if result["success"]: - upgraded = [ - r["package"] for r in result.get("results", []) if r.get("success") - ] - return f"โœ“ Upgraded: {', '.join(upgraded) if upgraded else 'none'}" - else: - errors = [ - f"{r['package']}: {r.get('error')}" - for r in result.get("results", []) - if not r.get("success") - ] - return "โœ— Some upgrades failed:\n" + "\n".join(errors) - - elif action == "reload": - if not package: - return "Error: package name required for reload" - - await tool_ctx.info(f"Reloading {package}...") - result = await registry.reload_package(package) - - if result["success"]: - tools = result.get("tools", []) - return f"โœ“ Reloaded {package}\nTools: {', '.join(tools) if tools else 'none'}" - else: - return f"โœ— Failed to reload {package}: {result.get('error', 'unknown error')}" - - elif action == "list": - packages = registry.list_packages() - - if not packages: - return "No tool packages installed.\n\nUse tool_install(action='install', package='...') to add tools." - - lines = ["Installed tool packages:", ""] - for pkg in packages: - status = "โœ“" if pkg["enabled"] else "โ—‹" - lines.append( - f"{status} {pkg['name']} v{pkg['version']} ({pkg['source']})" - ) - if pkg["tools"]: - lines.append(f" Tools: {', '.join(pkg['tools'])}") - - return "\n".join(lines) - - elif action == "self_update": - await tool_ctx.info("Checking for hanzo-mcp updates...") - result = await registry.self_update() - - if result["success"]: - return f"โœ“ Updated hanzo-mcp from v{result.get('current_version')}\n{result.get('message', '')}" - else: - return ( - f"โœ— Update failed: {result.get('error', 'unknown error')}\n" - f"Current version: v{result.get('current_version', 'unknown')}" - ) - - else: - return f"Unknown action: {action}" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - tool_instance = self - - @mcp_server.tool() - async def tool_install( - action: Action, - package: Annotated[ - str | None, Field(description="Package name or URL") - ] = None, - source: Annotated[ - str, Field(description="Source: pypi, git, or local") - ] = "pypi", - version: Annotated[ - str | None, Field(description="Version constraint") - ] = None, - ctx: MCPContext = None, - ) -> str: - """Install, update, and manage tool packages dynamically. - - The AI can use this to expand its own capabilities by installing - new tools, updating existing ones, or even updating itself. - - Examples: - tool_install(action="install", package="hanzo-tools-browser") - tool_install(action="self_update") - tool_install(action="list") - """ - return await tool_instance.call( - ctx, - action=action, - package=package, - source=source, - version=version, - ) - - -def register_tool_install(mcp_server) -> list: - """Register the tool install tool.""" - tool = ToolInstallTool() - tool.register(mcp_server) - return [tool] diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool_list.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool_list.py deleted file mode 100644 index f64fbddb5..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool_list.py +++ /dev/null @@ -1,274 +0,0 @@ -"""List all available tools and their status.""" - -from typing import Annotated, Optional, TypedDict, Unpack, final, override - -from mcp.server.fastmcp import Context as MCPContext -from pydantic import Field - -from hanzo_mcp.tools.common.auto_timeout import auto_timeout -from hanzo_mcp.tools.common.base import BaseTool -from hanzo_mcp.tools.common.context import create_tool_context -from hanzo_mcp.tools.common.tool_enable import ToolEnableTool - -ShowDisabled = Annotated[ - bool, - Field( - description="Show only disabled tools", - default=False, - ), -] - -ShowEnabled = Annotated[ - bool, - Field( - description="Show only enabled tools", - default=False, - ), -] - -Category = Annotated[ - Optional[str], - Field( - description="Filter by category (filesystem, shell, database, etc.)", - default=None, - ), -] - - -class ToolListParams(TypedDict, total=False): - """Parameters for tool list.""" - - show_disabled: bool - show_enabled: bool - category: Optional[str] - - -@final -class ToolListTool(BaseTool): - """Tool for listing all available tools and their status.""" - - # Tool information organized by category - TOOL_INFO = { - "filesystem": [ - ("read", "Read contents of files"), - ("write", "Write contents to files"), - ("edit", "Edit specific parts of files"), - ("multi_edit", "Make multiple edits to a file"), - ("tree", "Directory tree visualization (Unix-style)"), - ("find", "Find text in files (rg/ag/ack/grep)"), - ("symbols", "Code symbols search with tree-sitter"), - ("search", "Search (parallel grep/symbols/vector/git)"), - ("git_search", "Search git history"), - ("glob", "Find files by name pattern"), - ("content_replace", "Replace content across files"), - ], - "shell": [ - ("run_command", "Execute shell commands (--background option)"), - ("streaming_command", "Run commands with disk-based output streaming"), - ("processes", "List background processes"), - ("pkill", "Kill background processes"), - ("logs", "View process logs"), - ("uvx", "Run Python packages (--background option)"), - ("npx", "Run Node.js packages (--background option)"), - ], - "database": [ - ("sql", "SQLite operations (query/search/schema/stats)"), - ("graph", "Graph database (query/add/remove/search/stats)"), - ("vector", "Semantic search (search/index/stats/clear)"), - ], - "ai": [ - ("llm", "LLM interface (query/consensus/list/models/enable/disable)"), - ("agent", "AI agents (run/start/call/stop/list with A2A support)"), - ("swarm", "Parallel agent execution across multiple files"), - ( - "hierarchical_swarm", - "Hierarchical agent teams with Claude Code integration", - ), - ("mcp", "MCP servers (list/add/remove/enable/disable/restart)"), - ], - "config": [ - ("config", "Git-style configuration (get/set/list/toggle)"), - ("tool_enable", "Enable tools"), - ("tool_disable", "Disable tools"), - ("tool_list", "List all tools (this tool)"), - ], - "productivity": [ - ("tasks", "Task management (list/add/update/remove/clear)"), - ("jupyter", "Jupyter notebooks (read/edit/create/delete/execute)"), - ("think", "Structured thinking space"), - ("ui", "UI component registry (browse/search/install components)"), - ], - "system": [ - ("stats", "System and resource statistics"), - ("batch", "Run multiple tools in parallel"), - ], - "legacy": [ - ("directory_tree", "Legacy: Use 'tree' instead"), - ("grep", "Legacy: Use 'find' instead"), - ("grep_ast", "Legacy: Use 'ast' instead"), - ("batch_search", "Legacy: Use 'search' instead"), - ("find_files", "Legacy: Use 'glob' instead"), - ("run_background", "Legacy: Use 'run_command --background'"), - ("uvx_background", "Legacy: Use 'uvx --background'"), - ("npx_background", "Legacy: Use 'npx --background'"), - ("sql_query", "Legacy: Use 'sql' instead"), - ("sql_search", "Legacy: Use 'sql --action search'"), - ("sql_stats", "Legacy: Use 'sql --action stats'"), - ("graph_add", "Legacy: Use 'graph --action add'"), - ("graph_remove", "Legacy: Use 'graph --action remove'"), - ("graph_query", "Legacy: Use 'graph' instead"), - ("graph_search", "Legacy: Use 'graph --action search'"), - ("graph_stats", "Legacy: Use 'graph --action stats'"), - ("vector_index", "Legacy: Use 'vector --action index'"), - ("vector_search", "Legacy: Use 'vector' instead"), - ("dispatch_agent", "Legacy: Use 'agent' instead"), - ("todo_read", "Legacy: Use 'todo' instead"), - ("todo_write", "Legacy: Use 'todo --action add/update'"), - ("notebook_read", "Legacy: Use 'jupyter' instead"), - ("notebook_edit", "Legacy: Use 'jupyter --action edit'"), - ], - } - - def __init__(self): - """Initialize the tool list tool.""" - pass - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "tool_list" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """List all available tools and their current status. - -Shows: -- Tool names and descriptions -- Whether each tool is enabled or disabled -- Tools organized by category - -Examples: -- tool_list # Show all tools -- tool_list --show-disabled # Show only disabled tools -- tool_list --show-enabled # Show only enabled tools -- tool_list --category shell # Show only shell tools - -Use 'tool_enable' and 'tool_disable' to change tool status. -""" - - @override - @auto_timeout("tool_list") - async def call( - self, - ctx: MCPContext, - **params: Unpack[ToolListParams], - ) -> str: - """List all tools. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - List of tools and their status - """ - tool_ctx = create_tool_context(ctx) - tool_ctx.set_tool_info(self.name) - - # Extract parameters - show_disabled = params.get("show_disabled", False) - show_enabled = params.get("show_enabled", False) - category_filter = params.get("category") - - # Get all tool states - ToolEnableTool.get_all_states() - - output = [] - - # Header - if show_disabled: - output.append("=== Disabled Tools ===") - elif show_enabled: - output.append("=== Enabled Tools ===") - else: - output.append("=== All Available Tools ===") - - if category_filter: - output.append(f"Category: {category_filter}") - - output.append("") - - # Count statistics - total_tools = 0 - disabled_count = 0 - shown_count = 0 - - # Iterate through categories - categories = ( - [category_filter] - if category_filter and category_filter in self.TOOL_INFO - else self.TOOL_INFO.keys() - ) - - for category in categories: - if category not in self.TOOL_INFO: - continue - - category_tools = self.TOOL_INFO[category] - category_shown = [] - - for tool_name, description in category_tools: - total_tools += 1 - is_enabled = ToolEnableTool.is_tool_enabled(tool_name) - - if not is_enabled: - disabled_count += 1 - - # Apply filters - if show_disabled and is_enabled: - continue - if show_enabled and not is_enabled: - continue - - status = "โœ…" if is_enabled else "โŒ" - category_shown.append((tool_name, description, status)) - shown_count += 1 - - # Show category if it has tools - if category_shown: - output.append(f"=== {category.title()} Tools ===") - - # Find max tool name length for alignment - max_name_len = max(len(name) for name, _, _ in category_shown) - - for tool_name, description, status in category_shown: - output.append( - f"{status} {tool_name.ljust(max_name_len)} - {description}" - ) - - output.append("") - - # Summary - if not show_disabled and not show_enabled: - output.append("=== Summary ===") - output.append(f"Total tools: {total_tools}") - output.append(f"Enabled: {total_tools - disabled_count}") - output.append(f"Disabled: {disabled_count}") - else: - output.append(f"Showing {shown_count} tool(s)") - - if disabled_count > 0 and not show_disabled: - output.append("\nUse 'tool_list --show-disabled' to see disabled tools.") - output.append("Use 'tool_enable --tool ' to enable a tool.") - - if show_disabled: - output.append("\nUse 'tool_enable --tool ' to enable these tools.") - - return "\n".join(output) - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool_registry.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool_registry.py deleted file mode 100644 index 8f7bfa236..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/tool_registry.py +++ /dev/null @@ -1,495 +0,0 @@ -"""Dynamic package manager with hot-reload and self-update capabilities. - -Enables: -- Installing tool packages dynamically -- Hot-reloading tools without server restart -- Self-updating the AI's tooling -- Cross-session tool persistence - -Note: This is distinct from: -- tools/common/base.py::ToolRegistry - For registering tools with FastMCP -- config/tool_config.py::DynamicToolRegistry - For discovering tools from entry points -""" - -import asyncio -import importlib -import json -import logging -import sys -from dataclasses import dataclass, field -from datetime import datetime -from pathlib import Path -from typing import Any, Callable, Optional, Type - -logger = logging.getLogger(__name__) - - -@dataclass -class ToolPackage: - """Represents an installed tool package.""" - - name: str - version: str - source: str # "pypi", "git", "local" - installed_at: datetime - tools: list[str] = field(default_factory=list) - enabled: bool = True - - -class PackageManager: - """Centralized registry for dynamic tool management. - - Features: - - Install tools from PyPI/git/local - - Hot-reload without restart - - Track installed packages - - Enable/disable per tool - """ - - _instance: Optional["PackageManager"] = None - _lock = asyncio.Lock() - - def __init__(self): - self._packages: dict[str, ToolPackage] = {} - self._tools: dict[str, Any] = {} # name -> tool instance - self._tool_classes: dict[str, Type] = {} # name -> tool class - self._config_path = Path.home() / ".hanzo" / "mcp" / "registry.json" - self._tools_path = ( - Path.home() / ".hanzo" / "tools" - ) # Tool packages install here - self._reload_callbacks: list[Callable] = [] - self._mcp_server = None - - self._tools_path.mkdir(parents=True, exist_ok=True) - self._load_config() - - @classmethod - async def get_instance(cls) -> "PackageManager": - """Get singleton instance.""" - async with cls._lock: - if cls._instance is None: - cls._instance = PackageManager() - return cls._instance - - def set_mcp_server(self, mcp_server) -> None: - """Set the MCP server for tool registration.""" - self._mcp_server = mcp_server - - def on_reload(self, callback: Callable) -> None: - """Register callback for reload events.""" - self._reload_callbacks.append(callback) - - def _load_config(self) -> None: - """Load registry config from disk.""" - if self._config_path.exists(): - try: - with open(self._config_path) as f: - data = json.load(f) - for name, pkg_data in data.get("packages", {}).items(): - self._packages[name] = ToolPackage( - name=pkg_data["name"], - version=pkg_data["version"], - source=pkg_data["source"], - installed_at=datetime.fromisoformat(pkg_data["installed_at"]), - tools=pkg_data.get("tools", []), - enabled=pkg_data.get("enabled", True), - ) - except Exception as e: - logger.warning(f"Failed to load registry config: {e}") - - def _save_config(self) -> None: - """Save registry config to disk.""" - self._config_path.parent.mkdir(parents=True, exist_ok=True) - data = { - "packages": { - name: { - "name": pkg.name, - "version": pkg.version, - "source": pkg.source, - "installed_at": pkg.installed_at.isoformat(), - "tools": pkg.tools, - "enabled": pkg.enabled, - } - for name, pkg in self._packages.items() - } - } - with open(self._config_path, "w") as f: - json.dump(data, f, indent=2) - - async def install( - self, - package: str, - source: str = "pypi", - version: Optional[str] = None, - upgrade: bool = False, - ) -> dict[str, Any]: - """Install a tool package. - - Args: - package: Package name or git URL - source: "pypi", "git", or "local" - version: Specific version (optional) - upgrade: Upgrade if already installed - - Returns: - Installation result - """ - try: - if source == "pypi": - # Install from PyPI - pkg_spec = f"{package}=={version}" if version else package - cmd = [sys.executable, "-m", "uv", "pip", "install"] - if upgrade: - cmd.append("--upgrade") - cmd.extend(["--target", str(self._tools_path), pkg_spec]) - - result = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await result.communicate() - - if result.returncode != 0: - return { - "success": False, - "error": stderr.decode(), - "package": package, - } - - # Get installed version - installed_version = version or "latest" - - elif source == "git": - # Install from git - cmd = [ - sys.executable, - "-m", - "uv", - "pip", - "install", - "--target", - str(self._tools_path), - f"git+{package}", - ] - - result = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await result.communicate() - - if result.returncode != 0: - return { - "success": False, - "error": stderr.decode(), - "package": package, - } - - installed_version = "git" - - elif source == "local": - # Install from local path - cmd = [ - sys.executable, - "-m", - "uv", - "pip", - "install", - "--target", - str(self._tools_path), - "-e", - package, - ] - - result = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await result.communicate() - - if result.returncode != 0: - return { - "success": False, - "error": stderr.decode(), - "package": package, - } - - installed_version = "local" - else: - return {"success": False, "error": f"Unknown source: {source}"} - - # Record package - pkg_name = package.split("/")[-1] if "/" in package else package - pkg_name = pkg_name.replace(".git", "") - - self._packages[pkg_name] = ToolPackage( - name=pkg_name, - version=installed_version, - source=source, - installed_at=datetime.now(), - enabled=True, - ) - self._save_config() - - # Hot-reload the package - await self.reload_package(pkg_name) - - return { - "success": True, - "package": pkg_name, - "version": installed_version, - "source": source, - "tools": self._packages[pkg_name].tools, - } - - except Exception as e: - logger.exception(f"Failed to install package: {package}") - return {"success": False, "error": str(e), "package": package} - - async def uninstall(self, package: str) -> dict[str, Any]: - """Uninstall a tool package.""" - if package not in self._packages: - return {"success": False, "error": f"Package not found: {package}"} - - try: - cmd = [ - sys.executable, - "-m", - "uv", - "pip", - "uninstall", - "--target", - str(self._tools_path), - "-y", - package, - ] - - result = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - await result.communicate() - - # Remove from registry - pkg = self._packages.pop(package, None) - - # Unregister tools - if pkg: - for tool_name in pkg.tools: - self._tools.pop(tool_name, None) - self._tool_classes.pop(tool_name, None) - - self._save_config() - - return {"success": True, "package": package} - - except Exception as e: - return {"success": False, "error": str(e), "package": package} - - async def upgrade(self, package: Optional[str] = None) -> dict[str, Any]: - """Upgrade package(s) to latest version. - - Args: - package: Specific package or None for all - """ - results = [] - - packages_to_upgrade = [package] if package else list(self._packages.keys()) - - for pkg_name in packages_to_upgrade: - if pkg_name not in self._packages: - results.append({"package": pkg_name, "error": "Not installed"}) - continue - - pkg = self._packages[pkg_name] - result = await self.install( - pkg.name, - source=pkg.source, - upgrade=True, - ) - results.append(result) - - return { - "success": all(r.get("success", False) for r in results), - "results": results, - } - - async def reload_package(self, package: str) -> dict[str, Any]: - """Hot-reload a package without server restart.""" - if package not in self._packages: - return {"success": False, "error": f"Package not found: {package}"} - - try: - # Add tools path to sys.path if not there - tools_path_str = str(self._tools_path) - if tools_path_str not in sys.path: - sys.path.insert(0, tools_path_str) - - # Try to import/reload the package - try: - # Clear existing module from cache - modules_to_remove = [ - mod - for mod in sys.modules - if mod.startswith(package) - or mod.startswith(package.replace("-", "_")) - ] - for mod in modules_to_remove: - del sys.modules[mod] - - # Import the package - pkg_module = importlib.import_module(package.replace("-", "_")) - - # Look for tools - tools_found = [] - - # Check for TOOLS export - if hasattr(pkg_module, "TOOLS"): - for tool_class in pkg_module.TOOLS: - tool_name = getattr(tool_class, "name", tool_class.__name__) - self._tool_classes[tool_name] = tool_class - tools_found.append(tool_name) - - # Register with MCP server if available - if self._mcp_server: - try: - tool_instance = tool_class() - if hasattr(tool_instance, "register"): - tool_instance.register(self._mcp_server) - self._tools[tool_name] = tool_instance - except Exception as e: - logger.warning( - f"Failed to register tool {tool_name}: {e}" - ) - - # Check for register_* functions - for attr_name in dir(pkg_module): - if attr_name.startswith("register_") and callable( - getattr(pkg_module, attr_name) - ): - register_func = getattr(pkg_module, attr_name) - if self._mcp_server: - try: - registered = register_func(self._mcp_server) - if registered: - for tool in registered: - tool_name = getattr(tool, "name", str(tool)) - tools_found.append(tool_name) - except Exception as e: - logger.warning(f"Failed to call {attr_name}: {e}") - - # Update package tools list - self._packages[package].tools = tools_found - self._save_config() - - # Notify callbacks - for callback in self._reload_callbacks: - try: - callback(package, tools_found) - except Exception: - pass - - return { - "success": True, - "package": package, - "tools": tools_found, - } - - except ImportError as e: - return { - "success": False, - "error": f"Import failed: {e}", - "package": package, - } - - except Exception as e: - logger.exception(f"Failed to reload package: {package}") - return {"success": False, "error": str(e), "package": package} - - async def self_update(self) -> dict[str, Any]: - """Update hanzo-mcp itself (the AI updates its own tooling).""" - try: - # Get current version - from hanzo_mcp import __version__ as current_version - - # Check for updates - cmd = [ - sys.executable, - "-m", - "uv", - "pip", - "install", - "--upgrade", - "hanzo-mcp", - ] - - result = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await result.communicate() - - if result.returncode != 0: - return { - "success": False, - "error": stderr.decode(), - "current_version": current_version, - } - - # Check new version (requires restart to take effect) - output = stdout.decode() - - return { - "success": True, - "current_version": current_version, - "message": "Updated successfully. Restart required for changes to take effect.", - "output": output, - } - - except Exception as e: - return {"success": False, "error": str(e)} - - def list_packages(self) -> list[dict[str, Any]]: - """List all installed tool packages.""" - return [ - { - "name": pkg.name, - "version": pkg.version, - "source": pkg.source, - "installed_at": pkg.installed_at.isoformat(), - "tools": pkg.tools, - "enabled": pkg.enabled, - } - for pkg in self._packages.values() - ] - - def list_tools(self) -> list[str]: - """List all registered tools.""" - return list(self._tools.keys()) - - def get_tool(self, name: str) -> Optional[Any]: - """Get a tool instance by name.""" - return self._tools.get(name) - - -# Singleton access -_package_manager: Optional[PackageManager] = None - - -async def get_package_manager() -> PackageManager: - """Get the global package manager instance.""" - global _package_manager - if _package_manager is None: - _package_manager = await PackageManager.get_instance() - return _package_manager - - -# Backwards compatibility alias -async def get_registry() -> PackageManager: - """Deprecated: Use get_package_manager() instead.""" - return await get_package_manager() diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/truncate.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/truncate.py deleted file mode 100644 index 9d3f1cb67..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/truncate.py +++ /dev/null @@ -1,101 +0,0 @@ -"""Response truncation utilities for MCP tools. - -This module provides utilities to ensure MCP tool responses don't exceed token limits. -""" - -import tiktoken - - -def estimate_tokens(text: str, model: str = "gpt-4") -> int: - """Estimate the number of tokens in a text string. - - Args: - text: The text to estimate tokens for - model: The model to use for token estimation (default: gpt-4) - - Returns: - Estimated number of tokens - """ - try: - # Try to get the encoding for the specific model - encoding = tiktoken.encoding_for_model(model) - except KeyError: - # Fall back to cl100k_base which is used by newer models - encoding = tiktoken.get_encoding("cl100k_base") - - return len(encoding.encode(text)) - - -def truncate_response( - response: str, - max_tokens: int = 20000, - truncation_message: str = "\n\n[Response truncated due to length. Please use pagination, filtering, or limit parameters to see more.]", -) -> str: - """Truncate a response to fit within token limits. - - Args: - response: The response text to truncate - max_tokens: Maximum number of tokens allowed (default: 20000) - truncation_message: Message to append when truncating - - Returns: - Truncated response if needed, original response otherwise - """ - # Quick check - if response is short, no need to count tokens - if len(response) < max_tokens * 2: # Rough estimate: 1 token โ‰ˆ 2-4 chars - return response - - # Estimate tokens - token_count = estimate_tokens(response) - - # If within limit, return as-is - if token_count <= max_tokens: - return response - - # Need to truncate - # Binary search to find the right truncation point - left, right = 0, len(response) - truncation_msg_tokens = estimate_tokens(truncation_message) - target_tokens = max_tokens - truncation_msg_tokens - - while left < right - 1: - mid = (left + right) // 2 - mid_tokens = estimate_tokens(response[:mid]) - - if mid_tokens <= target_tokens: - left = mid - else: - right = mid - - # Find a good break point (newline or space) - truncate_at = left - for i in range(min(100, left), -1, -1): - if response[left - i] in "\n ": - truncate_at = left - i - break - - return response[:truncate_at] + truncation_message - - -def truncate_lines( - response: str, - max_lines: int = 1000, - truncation_message: str = "\n\n[Response truncated to {max_lines} lines. Please use pagination or filtering to see more.]", -) -> str: - """Truncate a response by number of lines. - - Args: - response: The response text to truncate - max_lines: Maximum number of lines allowed (default: 1000) - truncation_message: Message template to append when truncating - - Returns: - Truncated response if needed, original response otherwise - """ - lines = response.split("\n") - - if len(lines) <= max_lines: - return response - - truncated = "\n".join(lines[:max_lines]) - return truncated + truncation_message.format(max_lines=max_lines) diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/validation.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/validation.py deleted file mode 100644 index 511d7a1cb..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/validation.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Parameter validation utilities for Hanzo AI tools. - -Re-exports validation utilities from hanzo-tools-core for backwards compatibility. -""" - -# Re-export from hanzo-tools-core -from hanzo_tools.core.validation import ( - ValidationResult, - validate_path_parameter, - validate_string_parameter, -) - -__all__ = [ - "ValidationResult", - "validate_path_parameter", - "validate_string_parameter", -] diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/common/version_tool.py b/pkg/hanzo-mcp/hanzo_mcp/tools/common/version_tool.py deleted file mode 100644 index 1807ac93c..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/common/version_tool.py +++ /dev/null @@ -1,51 +0,0 @@ -"""Version tool for hanzo-mcp.""" - -import sys - -from hanzo_async import using_uvloop -from mcp.server.fastmcp import FastMCP - -import hanzo_mcp - - -def register_version_tool(mcp: FastMCP) -> None: - """Register the version tool with the MCP server.""" - - @mcp.tool() - async def version() -> str: - """Get hanzo-mcp version and environment information. - - Returns version info including: - - hanzo-mcp version - - Python version - - Platform info - - Async backend (uvloop or asyncio) - """ - import platform - - # Get uvloop version if active - async_backend = "asyncio" - if using_uvloop(): - try: - import uvloop - - async_backend = f"uvloop {uvloop.__version__}" - except ImportError: - async_backend = "uvloop" - - info = { - "hanzo_mcp": hanzo_mcp.__version__, - "python": sys.version.split()[0], - "platform": platform.system(), - "arch": platform.machine(), - "async": async_backend, - } - - # Return formatted string for clean display - lines = [ - f"hanzo-mcp: v{info['hanzo_mcp']}", - f"python: {info['python']}", - f"platform: {info['platform']} ({info['arch']})", - f"async: {info['async']}", - ] - return "\n".join(lines) diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/dev_tools.py b/pkg/hanzo-mcp/hanzo_mcp/tools/dev_tools.py deleted file mode 100644 index 27a82604b..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/dev_tools.py +++ /dev/null @@ -1,393 +0,0 @@ -""" -Unified Development Tools - 6 Orthogonal Commands -================================================= - -edit, fmt, test, build, lint, guard - -Each tool is one word, orthogonal, composable, works across languages/backends, -and is go.work aware. -""" - -import os -import subprocess -from pathlib import Path -from typing import Any, Dict, List, Literal, Optional, Union - -from pydantic import BaseModel, Field - -# Common types -LanguageType = Literal["auto", "go", "ts", "py", "rs", "cc", "sol", "schema"] -BackendType = Literal[ - "auto", - "go", - "pnpm", - "yarn", - "npm", - "bun", - "pytest", - "uv", - "poetry", - "cargo", - "cmake", - "ninja", - "make", - "buf", - "capnp", -] - - -class DevResult(BaseModel): - """Common output for all dev tools""" - - ok: bool - root: str - language_used: Union[str, List[str]] - backend_used: Union[str, List[str]] - scope_resolved: Union[str, List[str]] - touched_files: List[str] = Field(default_factory=list) - stdout: str = "" - stderr: str = "" - exit_code: int = 0 - errors: List[str] = Field(default_factory=list) - - -class WorkspaceDetector: - """Detects workspace root and configuration""" - - @staticmethod - def detect(target_path: str) -> Dict[str, Any]: - """Detect workspace root and type from target path""" - path = Path(target_path).resolve() - - # Walk up to find workspace markers - current = path if path.is_dir() else path.parent - - while current != current.parent: - # Check for workspace files in order of preference - if (current / "go.work").exists(): - return { - "root": str(current), - "type": "go", - "config": "go.work", - "primary_language": "go", - } - - if (current / "pnpm-workspace.yaml").exists(): - return { - "root": str(current), - "type": "pnpm", - "config": "pnpm-workspace.yaml", - "primary_language": "ts", - } - - if (current / "package.json").exists(): - return { - "root": str(current), - "type": "npm", - "config": "package.json", - "primary_language": "ts", - } - - if (current / "pyproject.toml").exists(): - return { - "root": str(current), - "type": "python", - "config": "pyproject.toml", - "primary_language": "py", - } - - if (current / "Cargo.toml").exists(): - return { - "root": str(current), - "type": "rust", - "config": "Cargo.toml", - "primary_language": "rs", - } - - current = current.parent - - # Fallback to target directory - return { - "root": str(path.parent if path.is_file() else path), - "type": "unknown", - "config": None, - "primary_language": "auto", - } - - -class TargetResolver: - """Resolves target specifications to file lists and scopes""" - - @staticmethod - def resolve(target: str, workspace: Dict[str, Any]) -> Dict[str, Any]: - """Resolve target to concrete scope""" - - if target.startswith("file:"): - file_path = target[5:] - return { - "type": "file", - "files": [file_path], - "scope": file_path, - "package": TargetResolver._infer_package(file_path, workspace), - } - - if target.startswith("dir:"): - dir_path = target[4:] - return { - "type": "directory", - "files": TargetResolver._scan_directory(dir_path), - "scope": dir_path, - "package": dir_path, - } - - if target.startswith("pkg:"): - pkg_spec = target[4:] - return { - "type": "package", - "files": TargetResolver._resolve_package(pkg_spec, workspace), - "scope": pkg_spec, - "package": pkg_spec, - } - - if target == "ws": - return { - "type": "workspace", - "files": TargetResolver._scan_workspace(workspace), - "scope": "workspace", - "package": ".", - } - - if target == "changed": - return { - "type": "changed", - "files": TargetResolver._get_changed_files(workspace), - "scope": "changed files", - "package": ".", - } - - # Default to file if it's a plain path - return TargetResolver.resolve(f"file:{target}", workspace) - - @staticmethod - def _infer_package(file_path: str, workspace: Dict[str, Any]) -> str: - """Infer package from file path""" - path = Path(file_path) - root = Path(workspace["root"]) - - if workspace["type"] == "go": - # Walk up to find go.mod - current = path.parent - while current >= root: - if (current / "go.mod").exists(): - return str(current.relative_to(root)) or "." - current = current.parent - return "." - - elif workspace["type"] in ["npm", "pnpm"]: - # Walk up to find package.json - current = path.parent - while current >= root: - if (current / "package.json").exists(): - return str(current.relative_to(root)) or "." - current = current.parent - return "." - - return str(path.parent.relative_to(root)) if path.parent >= root else "." - - @staticmethod - def _scan_directory(dir_path: str) -> List[str]: - """Scan directory for relevant files""" - path = Path(dir_path) - files = [] - - # Common source file patterns - patterns = [ - "**/*.go", - "**/*.ts", - "**/*.tsx", - "**/*.js", - "**/*.jsx", - "**/*.py", - "**/*.rs", - ] - - for pattern in patterns: - files.extend([str(f) for f in path.glob(pattern) if f.is_file()]) - - return files - - @staticmethod - def _resolve_package(pkg_spec: str, workspace: Dict[str, Any]) -> List[str]: - """Resolve package specification to files""" - root = Path(workspace["root"]) - - if workspace["type"] == "go": - # Use go list to resolve package - try: - result = subprocess.run( - ["go", "list", "-f", "{{.Dir}}", pkg_spec], - cwd=root, - capture_output=True, - text=True, - env={**os.environ, "GOWORK": "auto"}, - ) - if result.returncode == 0: - pkg_dirs = result.stdout.strip().split("\n") - files = [] - for pkg_dir in pkg_dirs: - files.extend(Path(pkg_dir).glob("*.go")) - return [str(f) for f in files] - except Exception: - pass - - # Fallback to directory scan - pkg_path = root / pkg_spec.replace("./", "").replace("...", "") - return TargetResolver._scan_directory(str(pkg_path)) - - @staticmethod - def _scan_workspace(workspace: Dict[str, Any]) -> List[str]: - """Scan entire workspace""" - return TargetResolver._scan_directory(workspace["root"]) - - @staticmethod - def _get_changed_files(workspace: Dict[str, Any]) -> List[str]: - """Get changed files from git""" - try: - result = subprocess.run( - ["git", "diff", "--name-only", "HEAD"], - cwd=workspace["root"], - capture_output=True, - text=True, - ) - if result.returncode == 0: - files = [f.strip() for f in result.stdout.split("\n") if f.strip()] - return [str(Path(workspace["root"]) / f) for f in files] - except Exception: - pass - - return [] - - -class DevToolBase: - """Base class for development tools""" - - def __init__( - self, - target: str, - language: LanguageType = "auto", - backend: BackendType = "auto", - root: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - dry_run: bool = False, - ): - self.target = target - self.language = language - self.backend = backend - self.env = env or {} - self.dry_run = dry_run - - # Detect workspace - workspace_root = ( - root or TargetResolver.resolve(target, {"root": "."})["package"] - ) - self.workspace = WorkspaceDetector.detect(workspace_root) - - # Resolve target - self.resolved = TargetResolver.resolve(target, self.workspace) - - # Auto-detect language if needed - if language == "auto": - self.language = self._detect_language() - - # Auto-detect backend if needed - if backend == "auto": - self.backend = self._detect_backend() - - def _detect_language(self) -> str: - """Auto-detect language from files and workspace""" - if self.workspace["primary_language"] != "auto": - return self.workspace["primary_language"] - - # Analyze file extensions - files = self.resolved["files"] - extensions = [Path(f).suffix for f in files] - - if any(ext == ".go" for ext in extensions): - return "go" - if any(ext in [".ts", ".tsx", ".js", ".jsx"] for ext in extensions): - return "ts" - if any(ext == ".py" for ext in extensions): - return "py" - if any(ext == ".rs" for ext in extensions): - return "rs" - - return self.workspace["primary_language"] - - def _detect_backend(self) -> str: - """Auto-detect backend from workspace type and language""" - lang = self.language - ws_type = self.workspace["type"] - - backend_map = { - "go": "go", - "ts": "pnpm" if ws_type == "pnpm" else "npm", - "py": ( - "uv" if Path(self.workspace["root"], "uv.lock").exists() else "pytest" - ), - "rs": "cargo", - "cc": "cmake", - "sol": "forge", - "schema": "buf", - } - - return backend_map.get(lang, "auto") - - def _run_command( - self, cmd: List[str], cwd: Optional[str] = None - ) -> subprocess.CompletedProcess: - """Run command with proper environment""" - env = {**os.environ, **self.env} - - # Add go.work environment for Go - if self.language == "go": - env["GOWORK"] = "auto" - - work_dir = cwd or self.workspace["root"] - - if self.dry_run: - return subprocess.CompletedProcess(cmd, 0, f"DRY RUN: {' '.join(cmd)}", "") - - return subprocess.run( - cmd, cwd=work_dir, env=env, capture_output=True, text=True - ) - - -# Individual tool implementations will be in separate files -# This establishes the foundation for edit, fmt, test, build, lint, guard - - -def create_dev_result( - ok: bool, - root: str, - language_used: Union[str, List[str]], - backend_used: Union[str, List[str]], - scope_resolved: Union[str, List[str]], - stdout: str = "", - stderr: str = "", - exit_code: int = 0, - touched_files: List[str] = None, - errors: List[str] = None, -) -> DevResult: - """Helper to create DevResult""" - return DevResult( - ok=ok, - root=root, - language_used=language_used, - backend_used=backend_used, - scope_resolved=scope_resolved, - stdout=stdout, - stderr=stderr, - exit_code=exit_code, - touched_files=touched_files or [], - errors=errors or [], - ) diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/dev_tools_mcp.py b/pkg/hanzo-mcp/hanzo_mcp/tools/dev_tools_mcp.py deleted file mode 100644 index 0dae5a588..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/dev_tools_mcp.py +++ /dev/null @@ -1,543 +0,0 @@ -""" -Unified Development Tools - MCP Integration -=========================================== - -Exposes the 6 orthogonal development tools as MCP tools: -- edit: Semantic refactoring via LSP -- fmt: Code formatting + import normalization -- test: Run tests narrowly by default -- build: Compile/build artifacts -- lint: Static analysis + type checking -- guard: Repository invariants enforcement - -Each tool supports: -- Multi-language detection (go, ts, py, rs, cc, sol, schema) -- Workspace-aware operation (go.work, package.json, etc.) -- Consistent input/output schemas -- Composable workflows -""" - -from typing import Any, Dict, List - -from mcp.server import Server -from mcp.types import TextContent, Tool - -try: - from .build_tool import build_tool_handler - from .edit_tool import edit_tool_handler - from .fmt_tool import fmt_tool_handler - from .guard_tool import guard_tool_handler - from .lint_tool import lint_tool_handler - from .test_tool import test_tool_handler -except ImportError: - # Fallback for testing - print("Warning: Could not import all dev tool handlers") - -# MCP Server instance -dev_tools_server = Server("hanzo-dev-tools") - -# Common parameter schemas -TARGET_PARAM = { - "type": "string", - "description": "Target specification: file:, dir:, pkg:, ws (workspace), or changed (git diff)", -} - -LANGUAGE_PARAM = { - "type": "string", - "enum": ["auto", "go", "ts", "py", "rs", "cc", "sol", "schema"], - "default": "auto", - "description": "Language override (auto-detected by default)", -} - -BACKEND_PARAM = { - "type": "string", - "enum": [ - "auto", - "go", - "pnpm", - "yarn", - "npm", - "bun", - "pytest", - "uv", - "poetry", - "cargo", - "cmake", - "ninja", - "make", - "buf", - "capnp", - ], - "default": "auto", - "description": "Backend/tool override (auto-detected by default)", -} - -COMMON_PARAMS = { - "root": {"type": "string", "description": "Workspace root override"}, - "env": {"type": "object", "description": "Additional environment variables"}, - "dry_run": { - "type": "boolean", - "default": False, - "description": "Show planned operations without executing", - }, -} - - -@dev_tools_server.list_tools() -async def list_tools() -> List[Tool]: - """List all available development tools""" - return [ - Tool( - name="edit", - description="Semantic refactoring via LSP (rename, code_action, organize_imports)", - inputSchema={ - "type": "object", - "properties": { - "target": TARGET_PARAM, - "op": { - "type": "string", - "enum": [ - "rename", - "code_action", - "organize_imports", - "apply_workspace_edit", - ], - "description": "Edit operation to perform", - }, - "file": { - "type": "string", - "description": "File path for rename/code_action operations", - }, - "pos": { - "type": "object", - "properties": { - "line": {"type": "integer"}, - "character": {"type": "integer"}, - }, - "description": "Position for rename/code_action (0-based)", - }, - "range": { - "type": "object", - "properties": { - "start": { - "type": "object", - "properties": { - "line": {"type": "integer"}, - "character": {"type": "integer"}, - }, - }, - "end": { - "type": "object", - "properties": { - "line": {"type": "integer"}, - "character": {"type": "integer"}, - }, - }, - }, - "description": "Range for code actions", - }, - "new_name": { - "type": "string", - "description": "New name for rename operation", - }, - "only": { - "type": "array", - "items": {"type": "string"}, - "description": "Code action kinds to apply", - }, - "apply": { - "type": "boolean", - "default": True, - "description": "Whether to apply edits to disk", - }, - "language": LANGUAGE_PARAM, - "backend": BACKEND_PARAM, - **COMMON_PARAMS, - }, - "required": ["target", "op"], - }, - ), - Tool( - name="fmt", - description="Code formatting + import normalization", - inputSchema={ - "type": "object", - "properties": { - "target": TARGET_PARAM, - "language": LANGUAGE_PARAM, - "backend": BACKEND_PARAM, - "opts": { - "type": "object", - "properties": { - "local_prefix": { - "type": "string", - "description": "Local import prefix for Go (e.g. github.com/luxfi)", - } - }, - "description": "Formatting options", - }, - **COMMON_PARAMS, - }, - "required": ["target"], - }, - ), - Tool( - name="test", - description="Run tests narrowly by default (fileโ†’package, dirโ†’subtree, pkgโ†’explicit, wsโ†’all)", - inputSchema={ - "type": "object", - "properties": { - "target": TARGET_PARAM, - "language": LANGUAGE_PARAM, - "backend": BACKEND_PARAM, - "opts": { - "type": "object", - "properties": { - "run": { - "type": "string", - "description": "Test name pattern (Go -run)", - }, - "count": { - "type": "integer", - "description": "Test count (Go -count)", - }, - "race": { - "type": "boolean", - "description": "Enable race detection (Go -race)", - }, - "filter": { - "type": "string", - "description": "Test filter (JS --filter, Python -k)", - }, - "k": {"type": "string", "description": "Pytest -k filter"}, - "m": {"type": "string", "description": "Pytest -m marker"}, - "p": { - "type": "string", - "description": "Rust package filter", - }, - "features": { - "type": "string", - "description": "Rust features", - }, - }, - "description": "Test options", - }, - **COMMON_PARAMS, - }, - "required": ["target"], - }, - ), - Tool( - name="build", - description="Compile/build artifacts (same scope as test)", - inputSchema={ - "type": "object", - "properties": { - "target": TARGET_PARAM, - "language": LANGUAGE_PARAM, - "backend": BACKEND_PARAM, - "opts": { - "type": "object", - "properties": { - "race": { - "type": "boolean", - "description": "Enable race detection (Go)", - }, - "release": { - "type": "boolean", - "description": "Release build (Rust)", - }, - "tags": { - "type": "string", - "description": "Build tags (Go)", - }, - "ldflags": { - "type": "string", - "description": "Linker flags (Go)", - }, - "features": { - "type": "string", - "description": "Features (Rust)", - }, - }, - "description": "Build options", - }, - **COMMON_PARAMS, - }, - "required": ["target"], - }, - ), - Tool( - name="lint", - description="Static analysis + type checking (language-specific linters + type checkers)", - inputSchema={ - "type": "object", - "properties": { - "target": TARGET_PARAM, - "language": LANGUAGE_PARAM, - "backend": BACKEND_PARAM, - "opts": { - "type": "object", - "properties": { - "fix": { - "type": "boolean", - "default": False, - "description": "Apply fixes where supported", - } - }, - "description": "Lint options", - }, - **COMMON_PARAMS, - }, - "required": ["target"], - }, - ), - Tool( - name="guard", - description="Repository invariant enforcement (boundaries, forbidden imports, generated files)", - inputSchema={ - "type": "object", - "properties": { - "target": TARGET_PARAM, - "rules": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Rule identifier", - }, - "glob": { - "type": "string", - "description": "File glob pattern", - }, - "pattern": { - "type": "string", - "description": "Regex pattern to match (for regex rules)", - }, - "forbid_import_prefix": { - "type": "string", - "description": "Forbidden import prefix (for import rules)", - }, - "forbid_writes": { - "type": "boolean", - "description": "Forbid writes to matched files (for generated rules)", - }, - "message": { - "type": "string", - "description": "Custom violation message", - }, - "description": { - "type": "string", - "description": "Rule description", - }, - }, - "required": ["id", "glob"], - }, - "description": "Custom guard rules", - }, - "use_defaults": { - "type": "boolean", - "default": True, - "description": "Include default Hanzo ecosystem rules", - }, - "language": LANGUAGE_PARAM, - "backend": BACKEND_PARAM, - **COMMON_PARAMS, - }, - "required": ["target"], - }, - ), - ] - - -@dev_tools_server.call_tool() -async def call_tool(name: str, arguments: Dict[str, Any]) -> List[TextContent]: - """Execute development tools""" - - try: - if name == "edit": - result = await edit_tool_handler(**arguments) - elif name == "fmt": - result = await fmt_tool_handler(**arguments) - elif name == "test": - result = await test_tool_handler(**arguments) - elif name == "build": - result = await build_tool_handler(**arguments) - elif name == "lint": - result = await lint_tool_handler(**arguments) - elif name == "guard": - result = await guard_tool_handler(**arguments) - else: - return [TextContent(type="text", text=f"Unknown tool: {name}")] - - # Format result - output_lines = [ - f"{'โœ…' if result['ok'] else 'โŒ'} {name.upper()} - {result['scope_resolved']}", - f"Language: {result['language_used']} | Backend: {result['backend_used']}", - f"Root: {result['root']}", - ] - - if result["touched_files"]: - output_lines.append(f"Modified {len(result['touched_files'])} files:") - for file in result["touched_files"][:10]: # Limit to first 10 - output_lines.append(f" โ€ข {file}") - if len(result["touched_files"]) > 10: - output_lines.append( - f" ... and {len(result['touched_files']) - 10} more" - ) - - if result["stdout"]: - output_lines.append("\n๐Ÿ“ค Output:") - output_lines.append(result["stdout"]) - - if result["stderr"]: - output_lines.append("\nโš ๏ธ Errors:") - output_lines.append(result["stderr"]) - - if result["errors"]: - output_lines.append("\nโŒ Issues:") - for error in result["errors"]: - output_lines.append(f" โ€ข {error}") - - return [TextContent(type="text", text="\n".join(output_lines))] - - except Exception as e: - return [TextContent(type="text", text=f"โŒ Error executing {name}: {str(e)}")] - - -# Composition recipes -@dev_tools_server.call_tool() -async def call_tool_compose(name: str, arguments: Dict[str, Any]) -> List[TextContent]: - """ - Composition recipes for common workflows: - - - rename_and_test: edit(rename) -> fmt(changed) -> test(pkg) -> guard(ws) - - fix_and_verify: fmt(target) -> lint(target, fix=true) -> test(target) - - full_check: fmt(ws) -> lint(ws) -> test(ws) -> build(ws) -> guard(ws) - """ - - if name == "rename_and_test": - return await _compose_rename_and_test(arguments) - elif name == "fix_and_verify": - return await _compose_fix_and_verify(arguments) - elif name == "full_check": - return await _compose_full_check(arguments) - else: - return [TextContent(type="text", text=f"Unknown composition: {name}")] - - -async def _compose_rename_and_test(args: Dict[str, Any]) -> List[TextContent]: - """Compose: rename -> format changed files -> test -> guard""" - results = [] - - # 1. Rename - rename_result = await edit_tool_handler( - target=args["target"], - op="rename", - **{k: v for k, v in args.items() if k not in ["target"]}, - ) - results.append(("rename", rename_result)) - - if not rename_result["ok"]: - return [TextContent(type="text", text="โŒ Rename failed, stopping workflow")] - - # 2. Format changed files - fmt_result = await fmt_tool_handler(target="changed") - results.append(("format", fmt_result)) - - # 3. Test package - pkg = args.get("package", ".") - test_result = await test_tool_handler(target=f"pkg:{pkg}") - results.append(("test", test_result)) - - # 4. Guard workspace - guard_result = await guard_tool_handler(target="ws") - results.append(("guard", guard_result)) - - # Format combined output - output_lines = ["๐Ÿ”„ RENAME AND TEST WORKFLOW"] - for step, result in results: - status = "โœ…" if result["ok"] else "โŒ" - output_lines.append( - f"{status} {step.upper()}: {result.get('stdout', '')[:100]}" - ) - - overall_success = all(result["ok"] for _, result in results) - output_lines.append( - f"\n{'โœ… Workflow completed successfully' if overall_success else 'โŒ Workflow failed'}" - ) - - return [TextContent(type="text", text="\n".join(output_lines))] - - -async def _compose_fix_and_verify(args: Dict[str, Any]) -> List[TextContent]: - """Compose: format -> lint with fix -> test""" - target = args["target"] - results = [] - - # 1. Format - fmt_result = await fmt_tool_handler(target=target) - results.append(("format", fmt_result)) - - # 2. Lint with fix - lint_result = await lint_tool_handler(target=target, opts={"fix": True}) - results.append(("lint", lint_result)) - - # 3. Test - test_result = await test_tool_handler(target=target) - results.append(("test", test_result)) - - output_lines = ["๐Ÿ”ง FIX AND VERIFY WORKFLOW"] - for step, result in results: - status = "โœ…" if result["ok"] else "โŒ" - output_lines.append( - f"{status} {step.upper()}: {result.get('stdout', '')[:100]}" - ) - - overall_success = all(result["ok"] for _, result in results) - output_lines.append( - f"\n{'โœ… All checks passed' if overall_success else 'โŒ Some checks failed'}" - ) - - return [TextContent(type="text", text="\n".join(output_lines))] - - -async def _compose_full_check(args: Dict[str, Any]) -> List[TextContent]: - """Compose: format -> lint -> test -> build -> guard""" - results = [] - - # Run all tools on workspace - tools = [ - ("format", fmt_tool_handler, {"target": "ws"}), - ("lint", lint_tool_handler, {"target": "ws"}), - ("test", test_tool_handler, {"target": "ws"}), - ("build", build_tool_handler, {"target": "ws"}), - ("guard", guard_tool_handler, {"target": "ws"}), - ] - - for step, handler, params in tools: - result = await handler(**params) - results.append((step, result)) - - # Stop on first failure for critical steps - if not result["ok"] and step in ["build", "test"]: - break - - output_lines = ["๐Ÿ” FULL CHECK WORKFLOW"] - for step, result in results: - status = "โœ…" if result["ok"] else "โŒ" - summary = result.get("stdout", "")[:200] - output_lines.append(f"{status} {step.upper()}: {summary}") - - overall_success = all(result["ok"] for _, result in results) - output_lines.append( - f"\n{'๐ŸŽ‰ All checks passed!' if overall_success else 'โš ๏ธ Some checks failed'}" - ) - - return [TextContent(type="text", text="\n".join(output_lines))] - - -# Export server for use in main MCP server -__all__ = ["dev_tools_server"] diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/edit_tool.py b/pkg/hanzo-mcp/hanzo_mcp/tools/edit_tool.py deleted file mode 100644 index a9f520f7c..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/edit_tool.py +++ /dev/null @@ -1,306 +0,0 @@ -""" -Edit Tool - Semantic refactoring via LSP -======================================== - -Purpose: semantic refactors via LSP across languages. - -Operations: -- rename: Rename symbol across workspace -- code_action: Apply LSP code actions -- organize_imports: Organize imports -- apply_workspace_edit: Apply arbitrary WorkspaceEdit - -Supports gopls, tsserver, pyright, rust-analyzer, clangd -""" - -from typing import Any, Dict, List, Optional - -from .dev_tools import DevResult, DevToolBase, create_dev_result - - -class EditTool(DevToolBase): - """LSP-powered semantic editing tool""" - - def __init__(self, target: str, op: str, **kwargs): - super().__init__(target, **kwargs) - self.op = op - self.file = kwargs.get("file") - self.pos = kwargs.get("pos") # {line: int, character: int} - self.range = kwargs.get("range") # {start: {line, ch}, end: {line, ch}} - self.new_name = kwargs.get("new_name") - self.only = kwargs.get("only", []) # code action kinds - self.apply = kwargs.get("apply", True) - - async def execute(self) -> DevResult: - """Execute edit operation""" - try: - if self.op == "rename": - return await self._rename() - elif self.op == "code_action": - return await self._code_action() - elif self.op == "organize_imports": - return await self._organize_imports() - elif self.op == "apply_workspace_edit": - return await self._apply_workspace_edit() - else: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[f"Unknown operation: {self.op}"], - ) - except Exception as e: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[str(e)], - ) - - async def _rename(self) -> DevResult: - """Rename symbol using LSP""" - if not self.file or not self.pos or not self.new_name: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=["rename requires file, pos, and new_name"], - ) - - # Use appropriate LSP client based on language - if self.language == "go": - return await self._gopls_rename() - elif self.language == "ts": - return await self._typescript_rename() - elif self.language == "py": - return await self._pyright_rename() - elif self.language == "rs": - return await self._rust_analyzer_rename() - else: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[f"Rename not supported for language: {self.language}"], - ) - - async def _code_action(self) -> DevResult: - """Apply code actions""" - if self.language == "go": - return await self._gopls_code_action() - elif self.language == "ts": - return await self._typescript_code_action() - elif self.language == "py": - return await self._pyright_code_action() - else: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[f"Code actions not supported for language: {self.language}"], - ) - - async def _organize_imports(self) -> DevResult: - """Organize imports""" - if self.language == "go": - # Use goimports directly - result = self._run_command(["goimports", "-w"] + self.resolved["files"]) - elif self.language == "ts": - # Use TypeScript organize imports - return await self._typescript_organize_imports() - elif self.language == "py": - # Use ruff or isort - result = self._run_command( - ["ruff", "check", "--select", "I", "--fix"] + self.resolved["files"] - ) - else: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[ - f"Organize imports not supported for language: {self.language}" - ], - ) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - touched_files=self.resolved["files"] if result.returncode == 0 else [], - ) - - async def _gopls_rename(self) -> DevResult: - """Rename using gopls""" - # Use gopls command line - cmd = [ - "gopls", - "rename", - f"-w={self.workspace['root']}", - f"{self.file}:{self.pos['line'] + 1}:{self.pos['character'] + 1}", - self.new_name, - ] - - result = self._run_command(cmd) - - # Parse touched files from output if available - touched_files = [] - if result.returncode == 0: - # gopls might output changed files - parse if available - touched_files = self.resolved["files"] - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="gopls", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - touched_files=touched_files, - ) - - async def _typescript_rename(self) -> DevResult: - """Rename using TypeScript Language Server""" - # For now, use a simple approach - could integrate with actual TS LSP - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used="tsserver", - scope_resolved=self.target, - errors=["TypeScript LSP rename not implemented yet - use IDE"], - ) - - async def _pyright_rename(self) -> DevResult: - """Rename using Pyright""" - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used="pyright", - scope_resolved=self.target, - errors=["Pyright rename not implemented yet - use IDE"], - ) - - async def _rust_analyzer_rename(self) -> DevResult: - """Rename using rust-analyzer""" - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used="rust-analyzer", - scope_resolved=self.target, - errors=["rust-analyzer rename not implemented yet - use IDE"], - ) - - async def _gopls_code_action(self) -> DevResult: - """Apply code actions using gopls""" - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used="gopls", - scope_resolved=self.target, - errors=["gopls code actions not implemented yet"], - ) - - async def _typescript_code_action(self) -> DevResult: - """Apply code actions using TypeScript""" - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used="tsserver", - scope_resolved=self.target, - errors=["TypeScript code actions not implemented yet"], - ) - - async def _pyright_code_action(self) -> DevResult: - """Apply code actions using Pyright""" - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used="pyright", - scope_resolved=self.target, - errors=["Pyright code actions not implemented yet"], - ) - - async def _typescript_organize_imports(self) -> DevResult: - """Organize TypeScript imports""" - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used="tsserver", - scope_resolved=self.target, - errors=["TypeScript organize imports not implemented yet"], - ) - - async def _apply_workspace_edit(self) -> DevResult: - """Apply arbitrary workspace edit""" - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=["apply_workspace_edit not implemented yet"], - ) - - -# MCP tool integration -async def edit_tool_handler( - target: str, - op: str, - file: Optional[str] = None, - pos: Optional[Dict[str, int]] = None, - range: Optional[Dict[str, Any]] = None, - new_name: Optional[str] = None, - only: Optional[List[str]] = None, - apply: bool = True, - language: str = "auto", - backend: str = "auto", - root: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - dry_run: bool = False, -) -> Dict[str, Any]: - """MCP handler for edit tool""" - - tool = EditTool( - target=target, - op=op, - file=file, - pos=pos, - range=range, - new_name=new_name, - only=only or [], - apply=apply, - language=language, - backend=backend, - root=root, - env=env, - dry_run=dry_run, - ) - - result = await tool.execute() - return result.dict() diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/fmt_tool.py b/pkg/hanzo-mcp/hanzo_mcp/tools/fmt_tool.py deleted file mode 100644 index 32544e0a2..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/fmt_tool.py +++ /dev/null @@ -1,387 +0,0 @@ -""" -Format Tool - Code formatting + import normalization -=================================================== - -Purpose: formatting + import normalization across languages. - -Backends: -- go: goimports (with local_prefix support) -- ts: prettier or biome -- py: ruff format or black -- rs: cargo fmt -- cc: clang-format -- sol: forge fmt / prettier -- schema: buf format (proto) -""" - -from typing import Any, Dict, List, Optional - -from .dev_tools import DevResult, DevToolBase, create_dev_result - - -class FmtTool(DevToolBase): - """Code formatting tool""" - - def __init__(self, target: str, **kwargs): - super().__init__(target, **kwargs) - self.opts = kwargs.get("opts", {}) - self.local_prefix = self.opts.get("local_prefix") # For Go imports grouping - - async def execute(self) -> DevResult: - """Execute formatting operation""" - try: - if self.language == "go": - return await self._format_go() - elif self.language == "ts": - return await self._format_typescript() - elif self.language == "py": - return await self._format_python() - elif self.language == "rs": - return await self._format_rust() - elif self.language == "cc": - return await self._format_cpp() - elif self.language == "sol": - return await self._format_solidity() - elif self.language == "schema": - return await self._format_protobuf() - else: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[f"Formatting not supported for language: {self.language}"], - ) - except Exception as e: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[str(e)], - ) - - async def _format_go(self) -> DevResult: - """Format Go code using goimports""" - files = self.resolved["files"] - go_files = [f for f in files if f.endswith(".go")] - - if not go_files: - return create_dev_result( - ok=True, - root=self.workspace["root"], - language_used=self.language, - backend_used="goimports", - scope_resolved=self.resolved["scope"], - stdout="No Go files to format", - ) - - cmd = ["goimports", "-w"] - - # Add local prefix if specified - if self.local_prefix: - cmd.extend(["-local", self.local_prefix]) - - cmd.extend(go_files) - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="goimports", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - touched_files=go_files if result.returncode == 0 else [], - ) - - async def _format_typescript(self) -> DevResult: - """Format TypeScript/JavaScript code""" - files = self.resolved["files"] - ts_files = [ - f - for f in files - if any(f.endswith(ext) for ext in [".ts", ".tsx", ".js", ".jsx"]) - ] - - if not ts_files: - return create_dev_result( - ok=True, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.resolved["scope"], - stdout="No TypeScript/JavaScript files to format", - ) - - # Try biome first, then prettier - if self.backend == "biome" or self._has_biome(): - return await self._format_with_biome(ts_files) - else: - return await self._format_with_prettier(ts_files) - - async def _format_python(self) -> DevResult: - """Format Python code""" - files = self.resolved["files"] - py_files = [f for f in files if f.endswith(".py")] - - if not py_files: - return create_dev_result( - ok=True, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.resolved["scope"], - stdout="No Python files to format", - ) - - # Use ruff format if available, otherwise black - if self._has_ruff(): - cmd = ["ruff", "format"] + py_files - backend = "ruff" - else: - cmd = ["black"] + py_files - backend = "black" - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used=backend, - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - touched_files=py_files if result.returncode == 0 else [], - ) - - async def _format_rust(self) -> DevResult: - """Format Rust code using cargo fmt""" - if self.resolved["type"] == "workspace": - cmd = ["cargo", "fmt"] - else: - # Format specific files - files = [f for f in self.resolved["files"] if f.endswith(".rs")] - if not files: - return create_dev_result( - ok=True, - root=self.workspace["root"], - language_used=self.language, - backend_used="cargo", - scope_resolved=self.resolved["scope"], - stdout="No Rust files to format", - ) - cmd = ["rustfmt"] + files - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="cargo", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - touched_files=self.resolved["files"] if result.returncode == 0 else [], - ) - - async def _format_cpp(self) -> DevResult: - """Format C/C++ code using clang-format""" - files = self.resolved["files"] - cpp_files = [ - f - for f in files - if any( - f.endswith(ext) for ext in [".c", ".cpp", ".cc", ".cxx", ".h", ".hpp"] - ) - ] - - if not cpp_files: - return create_dev_result( - ok=True, - root=self.workspace["root"], - language_used=self.language, - backend_used="clang-format", - scope_resolved=self.resolved["scope"], - stdout="No C/C++ files to format", - ) - - cmd = ["clang-format", "-i"] + cpp_files - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="clang-format", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - touched_files=cpp_files if result.returncode == 0 else [], - ) - - async def _format_solidity(self) -> DevResult: - """Format Solidity code""" - files = self.resolved["files"] - sol_files = [f for f in files if f.endswith(".sol")] - - if not sol_files: - return create_dev_result( - ok=True, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.resolved["scope"], - stdout="No Solidity files to format", - ) - - # Try forge fmt first - if self._has_forge(): - cmd = ["forge", "fmt"] + sol_files - backend = "forge" - else: - # Fallback to prettier with solidity plugin - cmd = ["prettier", "--write"] + sol_files - backend = "prettier" - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used=backend, - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - touched_files=sol_files if result.returncode == 0 else [], - ) - - async def _format_protobuf(self) -> DevResult: - """Format Protocol Buffer files using buf""" - files = self.resolved["files"] - proto_files = [f for f in files if f.endswith(".proto")] - - if not proto_files: - return create_dev_result( - ok=True, - root=self.workspace["root"], - language_used=self.language, - backend_used="buf", - scope_resolved=self.resolved["scope"], - stdout="No protobuf files to format", - ) - - cmd = ["buf", "format", "-w"] + proto_files - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="buf", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - touched_files=proto_files if result.returncode == 0 else [], - ) - - async def _format_with_prettier(self, files: List[str]) -> DevResult: - """Format using Prettier""" - cmd = ["prettier", "--write"] + files - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="prettier", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - touched_files=files if result.returncode == 0 else [], - ) - - async def _format_with_biome(self, files: List[str]) -> DevResult: - """Format using Biome""" - cmd = ["biome", "format", "--write"] + files - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="biome", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - touched_files=files if result.returncode == 0 else [], - ) - - def _has_biome(self) -> bool: - """Check if Biome is available""" - try: - result = self._run_command(["biome", "--version"]) - return result.returncode == 0 - except (OSError, FileNotFoundError): - return False - - def _has_ruff(self) -> bool: - """Check if Ruff is available""" - try: - result = self._run_command(["ruff", "--version"]) - return result.returncode == 0 - except (OSError, FileNotFoundError): - return False - - def _has_forge(self) -> bool: - """Check if Forge is available""" - try: - result = self._run_command(["forge", "--version"]) - return result.returncode == 0 - except (OSError, FileNotFoundError): - return False - - -# MCP tool integration -async def fmt_tool_handler( - target: str, - language: str = "auto", - backend: str = "auto", - root: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - dry_run: bool = False, - opts: Optional[Dict[str, Any]] = None, -) -> Dict[str, Any]: - """MCP handler for fmt tool""" - - tool = FmtTool( - target=target, - language=language, - backend=backend, - root=root, - env=env, - dry_run=dry_run, - opts=opts or {}, - ) - - result = await tool.execute() - return result.dict() diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/guard_tool.py b/pkg/hanzo-mcp/hanzo_mcp/tools/guard_tool.py deleted file mode 100644 index 2e73f56fc..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/guard_tool.py +++ /dev/null @@ -1,441 +0,0 @@ -""" -Guard Tool - Repository invariants and boundaries -================================================ - -Purpose: enforce repo invariants (boundaries, forbidden imports/strings, generated dirs). - -Rules: -- regex rule: {id, glob, pattern} - Match regex patterns in files -- import rule: {id, glob, forbid_import_prefix} - Forbidden import prefixes -- generated rule: {id, glob, forbid_writes: true} - Protect generated files - -Example rules for Hanzo ecosystem: -- no node in sdk: forbid import prefix in sdk/** of github.com/luxfi/node/ -- no net/http in api/** contracts -- no edits under generated: api/pb/**, api/capnp/** -""" - -import re -from dataclasses import dataclass -from pathlib import Path -from typing import Any, Dict, List, Optional - -from .dev_tools import DevResult, DevToolBase, create_dev_result - - -@dataclass -class GuardRule: - """Base guard rule""" - - id: str - glob: str - description: str = "" - - -@dataclass -class RegexRule(GuardRule): - """Regex pattern rule""" - - pattern: str - message: str = "Pattern match found" - - -@dataclass -class ImportRule(GuardRule): - """Import prefix rule""" - - forbid_import_prefix: str - message: str = "Forbidden import found" - - -@dataclass -class GeneratedRule(GuardRule): - """Generated file protection rule""" - - forbid_writes: bool = True - message: str = "Modification of generated files forbidden" - - -@dataclass -class Violation: - """Rule violation""" - - file: str - line: int - text: str - rule_id: str - message: str - - -class GuardTool(DevToolBase): - """Repository invariant enforcement tool""" - - def __init__(self, target: str, **kwargs): - super().__init__(target, **kwargs) - self.rules = self._parse_rules(kwargs.get("rules", [])) - - async def execute(self) -> DevResult: - """Execute guard operation""" - try: - violations = [] - - # Get files to check - files = self._get_files_to_check() - - # Run each rule - for rule in self.rules: - rule_violations = await self._check_rule(rule, files) - violations.extend(rule_violations) - - return create_dev_result( - ok=len(violations) == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="guard", - scope_resolved=self.resolved["scope"], - stdout=self._format_violations(violations), - stderr="", - exit_code=0 if len(violations) == 0 else 1, - errors=[f"Found {len(violations)} violations"] if violations else [], - ) - - except Exception as e: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used="guard", - scope_resolved=self.target, - errors=[str(e)], - ) - - def _parse_rules(self, rules_data: List[Dict[str, Any]]) -> List[GuardRule]: - """Parse rule definitions into rule objects""" - rules = [] - - for rule_data in rules_data: - rule_id = rule_data.get("id", "unnamed") - glob_pattern = rule_data.get("glob", "**") - description = rule_data.get("description", "") - - if "pattern" in rule_data: - # Regex rule - rules.append( - RegexRule( - id=rule_id, - glob=glob_pattern, - description=description, - pattern=rule_data["pattern"], - message=rule_data.get("message", "Pattern match found"), - ) - ) - elif "forbid_import_prefix" in rule_data: - # Import rule - rules.append( - ImportRule( - id=rule_id, - glob=glob_pattern, - description=description, - forbid_import_prefix=rule_data["forbid_import_prefix"], - message=rule_data.get("message", "Forbidden import found"), - ) - ) - elif "forbid_writes" in rule_data: - # Generated file rule - rules.append( - GeneratedRule( - id=rule_id, - glob=glob_pattern, - description=description, - forbid_writes=rule_data["forbid_writes"], - message=rule_data.get( - "message", "Modification of generated files forbidden" - ), - ) - ) - - return rules - - def _get_files_to_check(self) -> List[str]: - """Get list of files to check""" - if self.resolved["type"] == "file": - return [self.resolved["scope"]] - elif self.resolved["type"] == "directory": - return self._scan_directory(self.resolved["scope"]) - elif self.resolved["type"] == "workspace": - return self._scan_directory(self.workspace["root"]) - else: - return self.resolved["files"] - - def _scan_directory(self, directory: str) -> List[str]: - """Scan directory for source files""" - dir_path = Path(directory) - files = [] - - # Common source file patterns - patterns = [ - "**/*.go", - "**/*.ts", - "**/*.tsx", - "**/*.js", - "**/*.jsx", - "**/*.py", - "**/*.rs", - "**/*.c", - "**/*.cpp", - "**/*.cc", - "**/*.h", - "**/*.hpp", - "**/*.sol", - "**/*.proto", - ] - - for pattern in patterns: - files.extend([str(f) for f in dir_path.glob(pattern) if f.is_file()]) - - return files - - async def _check_rule(self, rule: GuardRule, files: List[str]) -> List[Violation]: - """Check a rule against files""" - violations = [] - - # Filter files by glob pattern - matching_files = self._filter_files_by_glob(files, rule.glob) - - if isinstance(rule, RegexRule): - violations.extend(await self._check_regex_rule(rule, matching_files)) - elif isinstance(rule, ImportRule): - violations.extend(await self._check_import_rule(rule, matching_files)) - elif isinstance(rule, GeneratedRule): - violations.extend(await self._check_generated_rule(rule, matching_files)) - - return violations - - def _filter_files_by_glob(self, files: List[str], glob_pattern: str) -> List[str]: - """Filter files by glob pattern""" - root_path = Path(self.workspace["root"]) - matching_files = [] - - for file_path in files: - # Make path relative to workspace root for matching - try: - Path(file_path).relative_to(root_path) - if Path(file_path).match(glob_pattern): - matching_files.append(file_path) - except ValueError: - # File outside workspace - continue - - return matching_files - - async def _check_regex_rule( - self, rule: RegexRule, files: List[str] - ) -> List[Violation]: - """Check regex rule against files""" - violations = [] - pattern = re.compile(rule.pattern) - - for file_path in files: - try: - with open(file_path, "r", encoding="utf-8", errors="ignore") as f: - for line_num, line in enumerate(f, 1): - if pattern.search(line): - violations.append( - Violation( - file=file_path, - line=line_num, - text=line.strip(), - rule_id=rule.id, - message=rule.message, - ) - ) - except Exception: - # Skip files that can't be read - continue - - return violations - - async def _check_import_rule( - self, rule: ImportRule, files: List[str] - ) -> List[Violation]: - """Check import rule against files""" - violations = [] - - for file_path in files: - try: - with open(file_path, "r", encoding="utf-8", errors="ignore") as f: - for line_num, line in enumerate(f, 1): - # Check for import statements containing forbidden prefix - if self._line_has_forbidden_import( - line, rule.forbid_import_prefix - ): - violations.append( - Violation( - file=file_path, - line=line_num, - text=line.strip(), - rule_id=rule.id, - message=f"{rule.message}: {rule.forbid_import_prefix}", - ) - ) - except Exception: - continue - - return violations - - async def _check_generated_rule( - self, rule: GeneratedRule, files: List[str] - ) -> List[Violation]: - """Check generated file rule""" - violations = [] - - if not rule.forbid_writes: - return violations - - # Check if any matching files have been modified - for file_path in files: - if self._is_file_modified(file_path): - violations.append( - Violation( - file=file_path, - line=1, - text="[Generated file was modified]", - rule_id=rule.id, - message=rule.message, - ) - ) - - return violations - - def _line_has_forbidden_import(self, line: str, forbidden_prefix: str) -> bool: - """Check if line contains forbidden import""" - line = line.strip() - - # Go imports - if line.startswith("import ") or ('"' in line and "import" in line): - return forbidden_prefix in line - - # Python imports - if line.startswith("import ") or line.startswith("from "): - return forbidden_prefix in line - - # TypeScript/JavaScript imports - if "import" in line and ("from" in line or "require(" in line): - return forbidden_prefix in line - - # Rust use statements - if line.startswith("use "): - return forbidden_prefix in line - - return False - - def _is_file_modified(self, file_path: str) -> bool: - """Check if file has been modified (simplified check)""" - # For now, just check if file exists and is not empty - # In a real implementation, this would check git status, - # modification times, or other indicators - try: - path = Path(file_path) - return path.exists() and path.stat().st_size > 0 - except (OSError, PermissionError): - return False - - def _format_violations(self, violations: List[Violation]) -> str: - """Format violations for output""" - if not violations: - return "โœ… All guard rules passed" - - output = [f"โŒ Found {len(violations)} guard violations:"] - output.append("") - - # Group by rule - by_rule = {} - for violation in violations: - if violation.rule_id not in by_rule: - by_rule[violation.rule_id] = [] - by_rule[violation.rule_id].append(violation) - - for rule_id, rule_violations in by_rule.items(): - output.append(f"Rule: {rule_id}") - for violation in rule_violations: - output.append( - f" {violation.file}:{violation.line} - {violation.message}" - ) - output.append(f" {violation.text}") - output.append("") - - return "\n".join(output) - - -# Default rules for Hanzo ecosystem -HANZO_DEFAULT_RULES = [ - { - "id": "no-node-in-sdk", - "glob": "sdk/**/*.go", - "forbid_import_prefix": "github.com/luxfi/node/", - "description": "SDK packages should not import node internals", - "message": "SDK boundary violation - importing node package", - }, - { - "id": "no-http-in-contracts", - "glob": "api/**/*.go", - "forbid_import_prefix": "net/http", - "description": "API contracts should not import net/http", - "message": "API contract should be transport agnostic", - }, - { - "id": "protect-generated-pb", - "glob": "api/pb/**/*", - "forbid_writes": True, - "description": "Protect generated protobuf files", - "message": "Generated protobuf files should not be manually edited", - }, - { - "id": "protect-generated-capnp", - "glob": "api/capnp/**/*", - "forbid_writes": True, - "description": "Protect generated Cap'n Proto files", - "message": "Generated Cap'n Proto files should not be manually edited", - }, - { - "id": "no-todo-in-main", - "glob": "**/*.go", - "pattern": r"// TODO.*(?:FIXME|HACK|XXX)", - "description": "No TODO/FIXME in production code", - "message": "Remove TODO/FIXME before merging", - }, -] - - -# MCP tool integration -async def guard_tool_handler( - target: str, - rules: Optional[List[Dict[str, Any]]] = None, - language: str = "auto", - backend: str = "auto", - root: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - dry_run: bool = False, - use_defaults: bool = True, -) -> Dict[str, Any]: - """MCP handler for guard tool""" - - # Combine provided rules with defaults if requested - all_rules = [] - if use_defaults: - all_rules.extend(HANZO_DEFAULT_RULES) - if rules: - all_rules.extend(rules) - - tool = GuardTool( - target=target, - language=language, - backend=backend, - root=root, - env=env, - dry_run=dry_run, - rules=all_rules, - ) - - result = await tool.execute() - return result.dict() diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/lint_tool.py b/pkg/hanzo-mcp/hanzo_mcp/tools/lint_tool.py deleted file mode 100644 index 7ab9c0e3b..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/lint_tool.py +++ /dev/null @@ -1,514 +0,0 @@ -""" -Lint Tool - Static analysis and type checking -============================================ - -Purpose: lint/typecheck in one place across languages. - -Backends: -- go: golangci-lint + go vet (configurable) -- ts: eslint + optionally tsc --noEmit -- py: ruff check + optionally pyright/mypy -- rs: cargo clippy -- cc: clang-tidy (optional) -- sol: slither (optional) -- schema: buf lint - -Options: -- fix: Apply fixes where supported -""" - -from pathlib import Path -from typing import Any, Dict, Optional - -from .dev_tools import DevResult, DevToolBase, create_dev_result - - -class LintTool(DevToolBase): - """Static analysis and linting tool""" - - def __init__(self, target: str, **kwargs): - super().__init__(target, **kwargs) - self.opts = kwargs.get("opts", {}) - self.fix = self.opts.get("fix", False) - - async def execute(self) -> DevResult: - """Execute lint operation""" - try: - if self.language == "go": - return await self._lint_go() - elif self.language == "ts": - return await self._lint_typescript() - elif self.language == "py": - return await self._lint_python() - elif self.language == "rs": - return await self._lint_rust() - elif self.language == "cc": - return await self._lint_cpp() - elif self.language == "sol": - return await self._lint_solidity() - elif self.language == "schema": - return await self._lint_protobuf() - else: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[f"Linting not supported for language: {self.language}"], - ) - except Exception as e: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[str(e)], - ) - - async def _lint_go(self) -> DevResult: - """Lint Go code""" - results = [] - - # Run go vet first (always available) - vet_result = await self._run_go_vet() - results.append(("go vet", vet_result)) - - # Run golangci-lint if available - if self._has_golangci_lint(): - lint_result = await self._run_golangci_lint() - results.append(("golangci-lint", lint_result)) - - # Combine results - overall_ok = all(result.returncode == 0 for _, result in results) - combined_stdout = "\n".join( - f"=== {name} ===\n{result.stdout}" for name, result in results - ) - combined_stderr = "\n".join( - f"=== {name} ===\n{result.stderr}" for name, result in results - ) - - return create_dev_result( - ok=overall_ok, - root=self.workspace["root"], - language_used=self.language, - backend_used="go vet + golangci-lint" if len(results) > 1 else "go vet", - scope_resolved=self.resolved["scope"], - stdout=combined_stdout, - stderr=combined_stderr, - exit_code=0 if overall_ok else 1, - ) - - async def _lint_typescript(self) -> DevResult: - """Lint TypeScript/JavaScript code""" - results = [] - - # Run ESLint - eslint_result = await self._run_eslint() - results.append(("eslint", eslint_result)) - - # Run tsc --noEmit for type checking - if self._has_typescript(): - tsc_result = await self._run_tsc_check() - results.append(("tsc", tsc_result)) - - # Combine results - overall_ok = all(result.returncode == 0 for _, result in results) - combined_stdout = "\n".join( - f"=== {name} ===\n{result.stdout}" for name, result in results - ) - combined_stderr = "\n".join( - f"=== {name} ===\n{result.stderr}" for name, result in results - ) - - backend = " + ".join(name for name, _ in results) - - return create_dev_result( - ok=overall_ok, - root=self.workspace["root"], - language_used=self.language, - backend_used=backend, - scope_resolved=self.resolved["scope"], - stdout=combined_stdout, - stderr=combined_stderr, - exit_code=0 if overall_ok else 1, - ) - - async def _lint_python(self) -> DevResult: - """Lint Python code""" - results = [] - - # Run ruff check - if self._has_ruff(): - ruff_result = await self._run_ruff_check() - results.append(("ruff", ruff_result)) - - # Run type checker if available - if self._has_pyright(): - pyright_result = await self._run_pyright() - results.append(("pyright", pyright_result)) - elif self._has_mypy(): - mypy_result = await self._run_mypy() - results.append(("mypy", mypy_result)) - - if not results: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used="none", - scope_resolved=self.target, - errors=["No Python linter found (install ruff, pylint, or flake8)"], - ) - - # Combine results - overall_ok = all(result.returncode == 0 for _, result in results) - combined_stdout = "\n".join( - f"=== {name} ===\n{result.stdout}" for name, result in results - ) - combined_stderr = "\n".join( - f"=== {name} ===\n{result.stderr}" for name, result in results - ) - - backend = " + ".join(name for name, _ in results) - - return create_dev_result( - ok=overall_ok, - root=self.workspace["root"], - language_used=self.language, - backend_used=backend, - scope_resolved=self.resolved["scope"], - stdout=combined_stdout, - stderr=combined_stderr, - exit_code=0 if overall_ok else 1, - ) - - async def _lint_rust(self) -> DevResult: - """Lint Rust code""" - cmd = ["cargo", "clippy"] - - # Add package filter if linting specific package - if self.resolved["type"] == "file": - cargo_toml = self._find_rust_package(self.resolved["scope"]) - if cargo_toml: - package_name = self._get_rust_package_name(cargo_toml) - if package_name: - cmd.extend(["-p", package_name]) - - # Add clippy options - cmd.append("--") - if not self.fix: - cmd.append("-D") - cmd.append("warnings") - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="cargo clippy", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _lint_cpp(self) -> DevResult: - """Lint C/C++ code""" - if not self._has_clang_tidy(): - return create_dev_result( - ok=True, - root=self.workspace["root"], - language_used=self.language, - backend_used="none", - scope_resolved=self.resolved["scope"], - stdout="clang-tidy not available, skipping C++ linting", - ) - - files = [ - f - for f in self.resolved["files"] - if any(f.endswith(ext) for ext in [".c", ".cpp", ".cc", ".cxx"]) - ] - - if not files: - return create_dev_result( - ok=True, - root=self.workspace["root"], - language_used=self.language, - backend_used="clang-tidy", - scope_resolved=self.resolved["scope"], - stdout="No C/C++ files to lint", - ) - - cmd = ["clang-tidy"] + files - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="clang-tidy", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _lint_solidity(self) -> DevResult: - """Lint Solidity code""" - if not self._has_slither(): - return create_dev_result( - ok=True, - root=self.workspace["root"], - language_used=self.language, - backend_used="none", - scope_resolved=self.resolved["scope"], - stdout="slither not available, skipping Solidity linting", - ) - - cmd = ["slither", "."] - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="slither", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _lint_protobuf(self) -> DevResult: - """Lint Protocol Buffer files""" - cmd = ["buf", "lint"] - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="buf", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _run_go_vet(self) -> object: - """Run go vet""" - cmd = ["go", "vet"] - - if self.resolved["type"] == "package": - cmd.append(self.resolved["scope"]) - elif self.resolved["type"] == "workspace": - cmd.append("./...") - else: - cmd.append(".") - - return self._run_command(cmd) - - async def _run_golangci_lint(self) -> object: - """Run golangci-lint""" - cmd = ["golangci-lint", "run"] - - if self.fix: - cmd.append("--fix") - - return self._run_command(cmd) - - async def _run_eslint(self) -> object: - """Run ESLint""" - cmd = ["eslint"] - - if self.fix: - cmd.append("--fix") - - # Add file patterns - if self.resolved["type"] == "file": - cmd.append(self.resolved["scope"]) - elif self.resolved["type"] == "directory": - cmd.append(f"{self.resolved['scope']}/**/*.{{js,jsx,ts,tsx}}") - else: - cmd.extend(["**/*.{js,jsx,ts,tsx}"]) - - return self._run_command(cmd) - - async def _run_tsc_check(self) -> object: - """Run TypeScript compiler for type checking""" - cmd = ["tsc", "--noEmit"] - - # Add project file if specific scope - if self.resolved["type"] != "workspace": - tsconfig = self._find_tsconfig(self.resolved["scope"]) - if tsconfig: - cmd.extend(["-p", str(tsconfig.parent)]) - - return self._run_command(cmd) - - async def _run_ruff_check(self) -> object: - """Run ruff check""" - cmd = ["ruff", "check"] - - if self.fix: - cmd.append("--fix") - - # Add target files - if self.resolved["type"] == "file": - cmd.append(self.resolved["scope"]) - elif self.resolved["type"] == "directory": - cmd.append(self.resolved["scope"]) - else: - cmd.append(".") - - return self._run_command(cmd) - - async def _run_pyright(self) -> object: - """Run Pyright type checker""" - cmd = ["pyright"] - - if self.resolved["type"] == "file": - cmd.append(self.resolved["scope"]) - elif self.resolved["type"] == "directory": - cmd.append(self.resolved["scope"]) - - return self._run_command(cmd) - - async def _run_mypy(self) -> object: - """Run mypy type checker""" - cmd = ["mypy"] - - if self.resolved["type"] == "file": - cmd.append(self.resolved["scope"]) - elif self.resolved["type"] == "directory": - cmd.append(self.resolved["scope"]) - else: - cmd.append(".") - - return self._run_command(cmd) - - def _has_golangci_lint(self) -> bool: - """Check if golangci-lint is available""" - try: - result = self._run_command(["golangci-lint", "--version"]) - return result.returncode == 0 - except (OSError, FileNotFoundError): - return False - - def _has_typescript(self) -> bool: - """Check if TypeScript is available""" - try: - result = self._run_command(["tsc", "--version"]) - return result.returncode == 0 - except (OSError, FileNotFoundError): - return False - - def _has_ruff(self) -> bool: - """Check if Ruff is available""" - try: - result = self._run_command(["ruff", "--version"]) - return result.returncode == 0 - except (OSError, FileNotFoundError): - return False - - def _has_pyright(self) -> bool: - """Check if Pyright is available""" - try: - result = self._run_command(["pyright", "--version"]) - return result.returncode == 0 - except (OSError, FileNotFoundError): - return False - - def _has_mypy(self) -> bool: - """Check if mypy is available""" - try: - result = self._run_command(["mypy", "--version"]) - return result.returncode == 0 - except (OSError, FileNotFoundError): - return False - - def _has_clang_tidy(self) -> bool: - """Check if clang-tidy is available""" - try: - result = self._run_command(["clang-tidy", "--version"]) - return result.returncode == 0 - except (OSError, FileNotFoundError): - return False - - def _has_slither(self) -> bool: - """Check if slither is available""" - try: - result = self._run_command(["slither", "--version"]) - return result.returncode == 0 - except (OSError, FileNotFoundError): - return False - - def _find_tsconfig(self, file_path: str) -> Optional[Path]: - """Find nearest tsconfig.json""" - path = Path(file_path) - current = path.parent if path.is_file() else path - - while current >= Path(self.workspace["root"]): - tsconfig = current / "tsconfig.json" - if tsconfig.exists(): - return tsconfig - current = current.parent - - return None - - def _find_rust_package(self, file_path: str) -> Optional[str]: - """Find Cargo.toml for given file""" - path = Path(file_path) - current = path.parent if path.is_file() else path - - while current >= Path(self.workspace["root"]): - cargo_toml = current / "Cargo.toml" - if cargo_toml.exists(): - return str(cargo_toml) - current = current.parent - - return None - - def _get_rust_package_name(self, cargo_toml_path: str) -> Optional[str]: - """Extract package name from Cargo.toml""" - try: - import toml - - with open(cargo_toml_path) as f: - data = toml.load(f) - return data.get("package", {}).get("name") - except (OSError, ImportError, ValueError, KeyError): - return None - - -# MCP tool integration -async def lint_tool_handler( - target: str, - language: str = "auto", - backend: str = "auto", - root: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - dry_run: bool = False, - opts: Optional[Dict[str, Any]] = None, -) -> Dict[str, Any]: - """MCP handler for lint tool""" - - tool = LintTool( - target=target, - language=language, - backend=backend, - root=root, - env=env, - dry_run=dry_run, - opts=opts or {}, - ) - - result = await tool.execute() - return result.dict() diff --git a/pkg/hanzo-mcp/hanzo_mcp/tools/test_tool.py b/pkg/hanzo-mcp/hanzo_mcp/tools/test_tool.py deleted file mode 100644 index 4c2b1c1e3..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/tools/test_tool.py +++ /dev/null @@ -1,402 +0,0 @@ -""" -Test Tool - Run tests narrowly by default -========================================= - -Purpose: run tests narrowly by default with smart scope resolution. - -Target resolution: -- file โ†’ derive owning package/project and run its tests -- dir โ†’ run tests for that subtree -- pkg โ†’ use explicitly -- ws โ†’ workspace-wide (discouraged unless asked) - -Backends: -- go: go test with -run, -count, -race flags -- ts: test runner with --filter, --watch=false -- py: pytest with -k, -m flags -- rs: cargo test with -p, --features -""" - -from pathlib import Path -from typing import Any, Dict, Optional - -from .dev_tools import DevResult, DevToolBase, create_dev_result - - -class TestTool(DevToolBase): - """Test execution tool""" - - def __init__(self, target: str, **kwargs): - super().__init__(target, **kwargs) - self.opts = kwargs.get("opts", {}) - - async def execute(self) -> DevResult: - """Execute test operation""" - try: - if self.language == "go": - return await self._test_go() - elif self.language == "ts": - return await self._test_typescript() - elif self.language == "py": - return await self._test_python() - elif self.language == "rs": - return await self._test_rust() - else: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[f"Testing not supported for language: {self.language}"], - ) - except Exception as e: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[str(e)], - ) - - async def _test_go(self) -> DevResult: - """Run Go tests""" - cmd = ["go", "test"] - - # Determine test scope - if self.resolved["type"] == "file": - # Get package containing the file - pkg = self.resolved["package"] - if pkg and pkg != ".": - cmd.append(f"./{pkg}") - else: - cmd.append(".") - elif self.resolved["type"] == "directory": - # Test directory subtree - dir_path = self.resolved["scope"] - cmd.append(f"./{dir_path}/...") - elif self.resolved["type"] == "package": - # Test specific package(s) - pkg_spec = self.resolved["scope"] - cmd.append(pkg_spec) - elif self.resolved["type"] == "workspace": - # Test all packages - cmd.append("./...") - else: - cmd.append(".") - - # Add common flags - if self.opts.get("run"): - cmd.extend(["-run", self.opts["run"]]) - if self.opts.get("count"): - cmd.extend(["-count", str(self.opts["count"])]) - if self.opts.get("race"): - cmd.append("-race") - - # Always run verbosely for better output - cmd.append("-v") - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="go", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _test_typescript(self) -> DevResult: - """Run TypeScript/JavaScript tests""" - # Detect test runner - if self.backend == "jest" or self._has_jest(): - return await self._test_with_jest() - elif self.backend == "vitest" or self._has_vitest(): - return await self._test_with_vitest() - elif self.backend in ["pnpm", "npm", "yarn"]: - return await self._test_with_package_script() - else: - return create_dev_result( - ok=False, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.target, - errors=[ - "No test runner detected (jest, vitest, or package.json script)" - ], - ) - - async def _test_python(self) -> DevResult: - """Run Python tests""" - # Use pytest by default - cmd = ["pytest"] - - # Determine test scope - if self.resolved["type"] == "file": - # Test specific file or its test counterpart - file_path = self.resolved["scope"] - test_file = self._find_python_test_file(file_path) - if test_file: - cmd.append(test_file) - else: - # Run tests in same directory - cmd.append(str(Path(file_path).parent)) - elif self.resolved["type"] == "directory": - # Test directory - cmd.append(self.resolved["scope"]) - elif self.resolved["type"] == "workspace": - # Test entire workspace - don't add specific path - pass - else: - cmd.append(".") - - # Add options - if self.opts.get("k"): - cmd.extend(["-k", self.opts["k"]]) - if self.opts.get("m"): - cmd.extend(["-m", self.opts["m"]]) - - # Add verbose output - cmd.append("-v") - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="pytest", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _test_rust(self) -> DevResult: - """Run Rust tests""" - cmd = ["cargo", "test"] - - # Add package filter if testing specific package - if self.resolved["type"] == "file": - # Find Cargo.toml for this file - cargo_toml = self._find_rust_package(self.resolved["scope"]) - if cargo_toml: - package_name = self._get_rust_package_name(cargo_toml) - if package_name: - cmd.extend(["-p", package_name]) - - # Add options - if self.opts.get("p"): - cmd.extend(["-p", self.opts["p"]]) - if self.opts.get("features"): - cmd.extend(["--features", self.opts["features"]]) - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="cargo", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _test_with_jest(self) -> DevResult: - """Run tests with Jest""" - cmd = ["jest"] - - # Add test pattern based on scope - if self.resolved["type"] == "file": - # Test specific file or related tests - file_path = self.resolved["scope"] - test_pattern = self._get_jest_pattern(file_path) - if test_pattern: - cmd.append(test_pattern) - elif self.resolved["type"] == "directory": - cmd.append(self.resolved["scope"]) - - # Add options - if self.opts.get("filter"): - cmd.extend(["--testNamePattern", self.opts["filter"]]) - - # Disable watch mode - cmd.append("--watchAll=false") - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="jest", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _test_with_vitest(self) -> DevResult: - """Run tests with Vitest""" - cmd = ["vitest", "run"] # run mode instead of watch - - # Add test pattern - if self.resolved["type"] == "file": - file_path = self.resolved["scope"] - test_pattern = self._get_vitest_pattern(file_path) - if test_pattern: - cmd.append(test_pattern) - elif self.resolved["type"] == "directory": - cmd.append(self.resolved["scope"]) - - # Add options - if self.opts.get("filter"): - cmd.extend(["-t", self.opts["filter"]]) - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used="vitest", - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - async def _test_with_package_script(self) -> DevResult: - """Run tests via package.json script""" - # Use package manager test script - if self.backend == "pnpm": - cmd = ["pnpm", "test"] - elif self.backend == "yarn": - cmd = ["yarn", "test"] - else: - cmd = ["npm", "test"] - - result = self._run_command(cmd) - - return create_dev_result( - ok=result.returncode == 0, - root=self.workspace["root"], - language_used=self.language, - backend_used=self.backend, - scope_resolved=self.resolved["scope"], - stdout=result.stdout, - stderr=result.stderr, - exit_code=result.returncode, - ) - - def _find_python_test_file(self, file_path: str) -> Optional[str]: - """Find corresponding test file for Python module""" - path = Path(file_path) - - # Common test patterns - test_patterns = [ - path.parent / f"test_{path.stem}.py", - path.parent / f"{path.stem}_test.py", - path.parent / "tests" / f"test_{path.stem}.py", - path.parent.parent / "tests" / f"test_{path.stem}.py", - ] - - for test_path in test_patterns: - if test_path.exists(): - return str(test_path) - - return None - - def _find_rust_package(self, file_path: str) -> Optional[str]: - """Find Cargo.toml for given file""" - path = Path(file_path) - current = path.parent if path.is_file() else path - - while current >= Path(self.workspace["root"]): - cargo_toml = current / "Cargo.toml" - if cargo_toml.exists(): - return str(cargo_toml) - current = current.parent - - return None - - def _get_rust_package_name(self, cargo_toml_path: str) -> Optional[str]: - """Extract package name from Cargo.toml""" - try: - import toml - - with open(cargo_toml_path) as f: - data = toml.load(f) - return data.get("package", {}).get("name") - except (OSError, ImportError, ValueError, KeyError): - return None - - def _get_jest_pattern(self, file_path: str) -> Optional[str]: - """Get Jest test pattern for file""" - path = Path(file_path) - - # If it's already a test file, run it directly - if "test" in path.stem or "spec" in path.stem: - return str(path) - - # Look for corresponding test files - patterns = [ - f"**/*{path.stem}*.test.*", - f"**/*{path.stem}*.spec.*", - f"**/test*{path.stem}*", - ] - - return patterns[0] # Return first pattern as fallback - - def _get_vitest_pattern(self, file_path: str) -> Optional[str]: - """Get Vitest test pattern for file""" - return self._get_jest_pattern(file_path) # Similar patterns - - def _has_jest(self) -> bool: - """Check if Jest is available""" - try: - result = self._run_command(["jest", "--version"]) - return result.returncode == 0 - except (OSError, FileNotFoundError): - return False - - def _has_vitest(self) -> bool: - """Check if Vitest is available""" - try: - result = self._run_command(["vitest", "--version"]) - return result.returncode == 0 - except (OSError, FileNotFoundError): - return False - - -# MCP tool integration -async def test_tool_handler( - target: str, - language: str = "auto", - backend: str = "auto", - root: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - dry_run: bool = False, - opts: Optional[Dict[str, Any]] = None, -) -> Dict[str, Any]: - """MCP handler for test tool""" - - tool = TestTool( - target=target, - language=language, - backend=backend, - root=root, - env=env, - dry_run=dry_run, - opts=opts or {}, - ) - - result = await tool.execute() - return result.dict() diff --git a/pkg/hanzo-mcp/hanzo_mcp/types.py b/pkg/hanzo-mcp/hanzo_mcp/types.py deleted file mode 100644 index bd6060417..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/types.py +++ /dev/null @@ -1,118 +0,0 @@ -"""Type definitions for Hanzo MCP tools.""" - -from dataclasses import dataclass -from typing import Any, Dict, Optional - - -@dataclass -class MCPResourceDocument: - """Resource document returned by MCP tools. - - Output format options: - - to_json_string(): Clean JSON without outer wrapper (default) - - to_readable_string(): Human-readable formatted text - - to_dict(): Full dict structure with data/metadata - """ - - data: Dict[str, Any] - metadata: Optional[Dict[str, Any]] = None - - def to_dict(self) -> Dict[str, Any]: - """Convert to dictionary format with data/metadata structure.""" - result = {"data": self.data} - if self.metadata: - result["metadata"] = self.metadata - return result - - def to_json_string(self) -> str: - """Convert to clean JSON string - just the data, no wrapper.""" - import json - - # Return just the data content directly, no wrapper - return json.dumps(self.data, indent=2) - - def to_readable_string(self) -> str: - """Convert to human-readable formatted string for display.""" - import json - - lines = [] - - # Format the main data - if isinstance(self.data, dict): - # Handle common result structures - if "results" in self.data: - results = self.data["results"] - stats = self.data.get("stats", {}) - pagination = self.data.get("pagination", {}) - - # Header with stats - if stats: - query = stats.get("query", "") - total = stats.get("total", len(results)) - time_ms = stats.get("time_ms", {}) - if time_ms: - total_time = ( - sum(time_ms.values()) - if isinstance(time_ms, dict) - else time_ms - ) - lines.append( - f"# Search: '{query}' ({total} results, {total_time}ms)" - ) - else: - lines.append(f"# Found {total} results") - lines.append("") - - # Results - for i, result in enumerate(results[:50], 1): # Limit display - if isinstance(result, dict): - file_path = result.get("file", result.get("path", "")) - line = result.get("line", "") - match = result.get("match", result.get("text", "")) - rtype = result.get("type", "") - - if file_path: - loc = f"{file_path}:{line}" if line else file_path - lines.append(f"{i}. {loc}") - if match: - # Truncate long matches - match_preview = ( - match[:200] + "..." if len(match) > 200 else match - ) - lines.append(f" {match_preview}") - if rtype: - lines.append(f" [{rtype}]") - else: - lines.append(f"{i}. {json.dumps(result)}") - else: - lines.append(f"{i}. {result}") - - # Pagination info - if pagination: - page = pagination.get("page", 1) - total = pagination.get("total", 0) - has_next = pagination.get("has_next", False) - if has_next: - lines.append( - f"\n... showing page {page} of {(total // 50) + 1}" - ) - else: - # Generic dict - format as key-value pairs - for key, value in self.data.items(): - if isinstance(value, (dict, list)): - lines.append(f"{key}:") - lines.append(json.dumps(value, indent=2)) - else: - lines.append(f"{key}: {value}") - else: - # Non-dict data - just dump as JSON - lines.append(json.dumps(self.data, indent=2)) - - # Add metadata footer if present - if self.metadata: - lines.append("") - lines.append("---") - for key, value in self.metadata.items(): - lines.append(f"{key}: {value}") - - return "\n".join(lines) diff --git a/pkg/hanzo-mcp/hanzo_mcp/unified_backend.py b/pkg/hanzo-mcp/hanzo_mcp/unified_backend.py deleted file mode 100644 index 7a95dc31e..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/unified_backend.py +++ /dev/null @@ -1,604 +0,0 @@ -#!/usr/bin/env python3 -""" -Hanzo Unified MCP Backend -======================== - -Core backend service that powers all MCP interfaces (VS Code, browser, CLI, MCP server). -Implements the 6 universal tools: edit, fmt, test, build, lint, guard. - -Features: -- Unified session logging to ~/.hanzo/sessions/ -- Codebase intelligence with SQLite vector storage -- LSP integration for semantic operations -- Workspace-aware operations with go.work support -- Cross-language tool execution -""" - -import asyncio -import json -import logging -import os -import sqlite3 -import subprocess -import uuid -from dataclasses import asdict, dataclass -from datetime import datetime -from pathlib import Path -from typing import Any, Dict, List, Optional, Union - -from pydantic import BaseModel, Field - - -# Core Types -@dataclass -class ToolResult: - """Standard result format for all tools""" - - ok: bool - root: str - language_used: Union[str, List[str]] - backend_used: Union[str, List[str]] - scope_resolved: Union[str, List[str]] - touched_files: List[str] - stdout: str - stderr: str - exit_code: int - errors: List[str] - execution_time: float - session_id: str - - -class TargetSpec(BaseModel): - """Target specification for tool operations""" - - target: str = Field( - ..., description="file:, dir:, pkg:, ws, or changed" - ) - language: str = Field(default="auto", description="Language override") - backend: str = Field(default="auto", description="Backend override") - root: Optional[str] = Field(default=None, description="Workspace root override") - env: Dict[str, str] = Field( - default_factory=dict, description="Environment variables" - ) - dry_run: bool = Field(default=False, description="Preview mode") - - -class WorkspaceDetector: - """Intelligent workspace detection with go.work support""" - - @staticmethod - def detect(target_path: str) -> Dict[str, Any]: - """Detect workspace root and configuration""" - path = Path(target_path).absolute() - - # Walk up to find workspace markers - for current in [path] + list(path.parents): - # Check for various workspace indicators - if (current / "go.work").exists(): - return { - "root": str(current), - "type": "go_workspace", - "config": current / "go.work", - "language": "go", - } - elif (current / "package.json").exists(): - return { - "root": str(current), - "type": "node_workspace", - "config": current / "package.json", - "language": "ts", - } - elif (current / "pyproject.toml").exists(): - return { - "root": str(current), - "type": "python_workspace", - "config": current / "pyproject.toml", - "language": "py", - } - elif (current / "Cargo.toml").exists(): - return { - "root": str(current), - "type": "rust_workspace", - "config": current / "Cargo.toml", - "language": "rs", - } - elif (current / ".git").exists(): - return { - "root": str(current), - "type": "git_repository", - "config": current / ".git", - "language": "auto", - } - - # Default to current directory - return { - "root": str(path.parent if path.is_file() else path), - "type": "directory", - "config": None, - "language": "auto", - } - - -class SessionManager: - """Manages logging and session tracking""" - - def __init__(self): - self.hanzo_dir = Path.home() / ".hanzo" - self.sessions_dir = self.hanzo_dir / "sessions" - self.sessions_dir.mkdir(parents=True, exist_ok=True) - self.current_session = str(uuid.uuid4()) - self.session_file = self.sessions_dir / f"{self.current_session}.jsonl" - - def log_tool_execution(self, tool_name: str, args: Dict, result: ToolResult): - """Log tool execution to JSONL""" - log_entry = { - "timestamp": datetime.utcnow().isoformat(), - "session_id": self.current_session, - "tool": tool_name, - "args": args, - "result": asdict(result), - "user": os.getenv("USER", "unknown"), - "cwd": os.getcwd(), - } - - with open(self.session_file, "a") as f: - f.write(json.dumps(log_entry) + "\n") - - def get_recent_sessions(self, limit: int = 10) -> List[Dict]: - """Get recent session activity""" - sessions = [] - for session_file in sorted(self.sessions_dir.glob("*.jsonl"), reverse=True)[ - :limit - ]: - with open(session_file) as f: - session_data = [json.loads(line) for line in f] - if session_data: - sessions.append( - { - "session_id": session_file.stem, - "start_time": session_data[0]["timestamp"], - "tool_count": len(session_data), - "tools_used": list( - set(entry["tool"] for entry in session_data) - ), - } - ) - return sessions - - -class CodebaseIndexer: - """SQLite-based codebase intelligence""" - - def __init__(self, hanzo_dir: Path): - self.db_path = hanzo_dir / "codebase.db" - self.init_database() - - def init_database(self): - """Initialize SQLite database with vector storage""" - with sqlite3.connect(self.db_path) as conn: - conn.executescript(""" - CREATE TABLE IF NOT EXISTS files ( - id INTEGER PRIMARY KEY, - path TEXT UNIQUE NOT NULL, - content_hash TEXT NOT NULL, - language TEXT, - size INTEGER, - modified_time REAL, - indexed_time REAL DEFAULT (julianday('now')) - ); - - CREATE TABLE IF NOT EXISTS symbols ( - id INTEGER PRIMARY KEY, - file_id INTEGER REFERENCES files(id), - name TEXT NOT NULL, - kind TEXT NOT NULL, -- function, class, variable, etc - line_start INTEGER, - line_end INTEGER, - definition TEXT, - UNIQUE(file_id, name, line_start) - ); - - CREATE TABLE IF NOT EXISTS imports ( - id INTEGER PRIMARY KEY, - file_id INTEGER REFERENCES files(id), - import_path TEXT NOT NULL, - alias TEXT, - line_number INTEGER - ); - - CREATE TABLE IF NOT EXISTS dependencies ( - id INTEGER PRIMARY KEY, - from_file_id INTEGER REFERENCES files(id), - to_file_id INTEGER REFERENCES files(id), - relationship TEXT NOT NULL, -- imports, calls, extends, etc - UNIQUE(from_file_id, to_file_id, relationship) - ); - - CREATE INDEX IF NOT EXISTS idx_files_path ON files(path); - CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name); - CREATE INDEX IF NOT EXISTS idx_imports_path ON imports(import_path); - """) - - def index_file(self, file_path: str, content: str, language: str): - """Index a single file for intelligent search""" - import hashlib - - content_hash = hashlib.sha256(content.encode()).hexdigest() - file_size = len(content.encode()) - modified_time = os.path.getmtime(file_path) - - with sqlite3.connect(self.db_path) as conn: - # Insert or update file record - conn.execute( - """ - INSERT OR REPLACE INTO files (path, content_hash, language, size, modified_time) - VALUES (?, ?, ?, ?, ?) - """, - (file_path, content_hash, language, file_size, modified_time), - ) - - # Symbol extraction handled by LSP integration in the tools layer - # See hanzo_mcp/tools/common/lsp.py for language-specific parsing - - def search_symbols(self, query: str, language: Optional[str] = None) -> List[Dict]: - """Search for symbols across codebase""" - with sqlite3.connect(self.db_path) as conn: - sql = """ - SELECT f.path, s.name, s.kind, s.line_start, s.definition - FROM symbols s - JOIN files f ON s.file_id = f.id - WHERE s.name LIKE ? - """ - params = [f"%{query}%"] - - if language: - sql += " AND f.language = ?" - params.append(language) - - cursor = conn.execute(sql, params) - return [ - { - "path": row[0], - "name": row[1], - "kind": row[2], - "line": row[3], - "definition": row[4], - } - for row in cursor.fetchall() - ] - - -class LSPBridge: - """Unified LSP client for cross-language operations""" - - LSP_SERVERS = { - "go": "gopls", - "ts": "typescript-language-server", - "py": "pyright", - "rs": "rust-analyzer", - "cc": "clangd", - "sol": "solidity-language-server", - } - - def __init__(self): - self.active_servers = {} - - async def start_server(self, language: str, workspace_root: str): - """Start LSP server for language""" - # Implementation would start LSP server process - # and handle communication via JSON-RPC - pass - - async def rename_symbol( - self, file_path: str, line: int, character: int, new_name: str - ): - """Perform LSP rename operation""" - # Implementation would send LSP rename request - pass - - async def code_actions(self, file_path: str, line_start: int, line_end: int): - """Get available code actions""" - # Implementation would send LSP codeAction request - pass - - -class UnifiedBackend: - """Main backend service implementing the 6 universal tools""" - - def __init__(self): - self.session_manager = SessionManager() - self.indexer = CodebaseIndexer(self.session_manager.hanzo_dir) - self.lsp_bridge = LSPBridge() - self.logger = logging.getLogger(__name__) - - def resolve_target(self, target_spec: TargetSpec) -> Dict[str, Any]: - """Resolve target specification to concrete file/directory list""" - target = target_spec.target - - if target.startswith("file:"): - path = target[5:] - workspace = WorkspaceDetector.detect(path) - return {"type": "file", "paths": [path], "workspace": workspace} - - elif target.startswith("dir:"): - path = target[4:] - workspace = WorkspaceDetector.detect(path) - # Recursively find relevant files - files = [] - for ext in [".py", ".go", ".ts", ".js", ".rs", ".c", ".cpp", ".sol"]: - files.extend(Path(path).rglob(f"*{ext}")) - return { - "type": "directory", - "paths": [str(f) for f in files], - "workspace": workspace, - } - - elif target.startswith("pkg:"): - pkg_spec = target[4:] - workspace = WorkspaceDetector.detect(".") - # Language-specific package resolution - if workspace["language"] == "go": - return self._resolve_go_package(pkg_spec, workspace) - elif workspace["language"] == "ts": - return self._resolve_ts_package(pkg_spec, workspace) - # etc. - - elif target == "ws": - workspace = WorkspaceDetector.detect(".") - # Return all files in workspace - root = Path(workspace["root"]) - files = [] - for ext in [".py", ".go", ".ts", ".js", ".rs", ".c", ".cpp", ".sol"]: - files.extend(root.rglob(f"*{ext}")) - return { - "type": "workspace", - "paths": [str(f) for f in files], - "workspace": workspace, - } - - elif target == "changed": - # Git diff against HEAD - try: - result = subprocess.run( - ["git", "diff", "--name-only", "HEAD"], - capture_output=True, - text=True, - check=True, - ) - changed_files = ( - result.stdout.strip().split("\n") if result.stdout.strip() else [] - ) - workspace = WorkspaceDetector.detect(".") - return { - "type": "changed", - "paths": changed_files, - "workspace": workspace, - } - except subprocess.CalledProcessError: - return { - "type": "changed", - "paths": [], - "workspace": WorkspaceDetector.detect("."), - "error": "Not a git repository or git not available", - } - - def _resolve_go_package(self, pkg_spec: str, workspace: Dict) -> Dict: - """Resolve Go package specification""" - # Implementation for Go package resolution - # Handle ./..., ./cli/..., specific packages - pass - - def _resolve_ts_package(self, pkg_spec: str, workspace: Dict) -> Dict: - """Resolve TypeScript/Node package specification""" - # Implementation for TS package resolution - pass - - async def edit(self, target: TargetSpec, op: str, **kwargs) -> ToolResult: - """Edit tool: semantic refactors via LSP""" - start_time = datetime.utcnow().timestamp() - - try: - resolved = self.resolve_target(target) - workspace = resolved["workspace"] - - result = ToolResult( - ok=True, - root=workspace["root"], - language_used=workspace["language"], - backend_used="lsp", - scope_resolved=resolved["paths"], - touched_files=[], - stdout="", - stderr="", - exit_code=0, - errors=[], - execution_time=0, - session_id=self.session_manager.current_session, - ) - - if op == "rename": - # LSP rename operation - file_path = kwargs.get("file") - pos = kwargs.get("pos", {}) - new_name = kwargs.get("new_name") - - if target.dry_run: - result.stdout = ( - f"Would rename symbol at {file_path}:{pos} to {new_name}" - ) - else: - # Actual LSP rename - await self.lsp_bridge.rename_symbol( - file_path, pos.get("line", 0), pos.get("character", 0), new_name - ) - result.touched_files = [file_path] - - elif op == "code_action": - # LSP code actions - file_path = kwargs.get("file") - range_spec = kwargs.get("range", {}) - kwargs.get("only", []) - - actions = await self.lsp_bridge.code_actions( - file_path, - range_spec.get("start", {}).get("line", 0), - range_spec.get("end", {}).get("line", 0), - ) - result.stdout = f"Available actions: {actions}" - - elif op == "organize_imports": - # Organize imports for all files in scope - for file_path in resolved["paths"]: - if not target.dry_run: - # Implement import organization - pass - result.touched_files.append(file_path) - - result.execution_time = datetime.utcnow().timestamp() - start_time - return result - - except Exception as e: - result = ToolResult( - ok=False, - root=workspace.get("root", "."), - language_used="unknown", - backend_used="lsp", - scope_resolved=[], - touched_files=[], - stdout="", - stderr=str(e), - exit_code=1, - errors=[str(e)], - execution_time=datetime.utcnow().timestamp() - start_time, - session_id=self.session_manager.current_session, - ) - return result - - async def fmt(self, target: TargetSpec, **kwargs) -> ToolResult: - """Format tool: formatting + import normalization""" - start_time = datetime.utcnow().timestamp() - - try: - resolved = self.resolve_target(target) - workspace = resolved["workspace"] - language = workspace["language"] - - # Select formatter based on language - formatters = { - "go": "goimports", - "py": "ruff format", - "ts": "prettier", - "rs": "cargo fmt", - "cc": "clang-format", - "sol": "prettier", - } - - formatter = formatters.get(language, "cat") - backend_used = formatter.split()[0] - - result = ToolResult( - ok=True, - root=workspace["root"], - language_used=language, - backend_used=backend_used, - scope_resolved=resolved["paths"], - touched_files=[], - stdout="", - stderr="", - exit_code=0, - errors=[], - execution_time=0, - session_id=self.session_manager.current_session, - ) - - if not target.dry_run: - for file_path in resolved["paths"]: - # Run formatter on each file - if language == "go": - cmd = ["goimports", "-w"] - local_prefix = kwargs.get("opts", {}).get("local_prefix") - if local_prefix: - cmd.extend(["-local", local_prefix]) - cmd.append(file_path) - - proc_result = subprocess.run( - cmd, capture_output=True, text=True - ) - if proc_result.returncode == 0: - result.touched_files.append(file_path) - else: - result.errors.append( - f"Failed to format {file_path}: {proc_result.stderr}" - ) - - # Add other language formatting logic - - result.execution_time = datetime.utcnow().timestamp() - start_time - result.ok = len(result.errors) == 0 - return result - - except Exception as e: - return ToolResult( - ok=False, - root=".", - language_used="unknown", - backend_used="unknown", - scope_resolved=[], - touched_files=[], - stdout="", - stderr=str(e), - exit_code=1, - errors=[str(e)], - execution_time=datetime.utcnow().timestamp() - start_time, - session_id=self.session_manager.current_session, - ) - - async def test(self, target: TargetSpec, **kwargs) -> ToolResult: - """Test tool: run tests narrowly by default""" - # Implementation for test execution - pass - - async def build(self, target: TargetSpec, **kwargs) -> ToolResult: - """Build tool: compile/build artifacts""" - # Implementation for build execution - pass - - async def lint(self, target: TargetSpec, **kwargs) -> ToolResult: - """Lint tool: lint/typecheck in one place""" - # Implementation for linting - pass - - async def guard( - self, target: TargetSpec, rules: List[Dict], **kwargs - ) -> ToolResult: - """Guard tool: repo invariants""" - # Implementation for guard rules - pass - - -# Global backend instance -backend = UnifiedBackend() - - -if __name__ == "__main__": - # CLI interface for testing - import asyncio - import sys - - async def main(): - if len(sys.argv) < 2: - print("Usage: python unified_backend.py [args...]") - sys.exit(1) - - tool_name = sys.argv[1] - target_spec = TargetSpec(target=sys.argv[2]) - - if tool_name == "fmt": - result = await backend.fmt(target_spec) - elif tool_name == "edit": - result = await backend.edit(target_spec, op="organize_imports") - # Add other tools - - print(json.dumps(asdict(result), indent=2)) - - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/hanzo_mcp/utils/event_loop.py b/pkg/hanzo-mcp/hanzo_mcp/utils/event_loop.py deleted file mode 100644 index c0a87fc1c..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/utils/event_loop.py +++ /dev/null @@ -1,78 +0,0 @@ -"""Event loop configuration with optional uvloop support. - -This module provides utilities for configuring the asyncio event loop -with optional uvloop support for improved performance on Linux/macOS. - -Uses hanzo_async for unified async I/O configuration. -""" - -import asyncio -import sys -from typing import Optional - -from hanzo_async import configure_loop, using_uvloop - - -def configure_event_loop(*, quiet: bool = False) -> Optional[str]: - """Configure the event loop with uvloop if available. - - This should be called early in the application startup, before - any async code runs. - - Args: - quiet: If True, suppress info messages about uvloop status - - Returns: - The name of the event loop policy being used, or None if default - """ - # Use hanzo_async for unified configuration - if configure_loop(): - if not quiet: - import logging - - try: - import uvloop - - logger = logging.getLogger(__name__) - logger.debug(f"Using uvloop {uvloop.__version__} for event loop") - except ImportError: - pass - - try: - import uvloop - - return f"uvloop-{uvloop.__version__}" - except ImportError: - return None - - return None - - -def get_event_loop_info() -> dict: - """Get information about the current event loop configuration. - - Returns: - Dictionary with event loop details - """ - try: - loop = asyncio.get_event_loop() - except RuntimeError: - loop = None - - info = { - "loop_class": type(loop).__name__ if loop else None, - "loop_module": type(loop).__module__ if loop else None, - "platform": sys.platform, - "using_uvloop": using_uvloop(), - } - - # Check if uvloop is available - try: - import uvloop - - info["uvloop_available"] = True - info["uvloop_version"] = uvloop.__version__ - except ImportError: - info["uvloop_available"] = False - - return info diff --git a/pkg/hanzo-mcp/hanzo_mcp/zap_server.py b/pkg/hanzo-mcp/hanzo_mcp/zap_server.py deleted file mode 100644 index 838962545..000000000 --- a/pkg/hanzo-mcp/hanzo_mcp/zap_server.py +++ /dev/null @@ -1,87 +0,0 @@ -"""ZAP server adaptor for hanzo-mcp. - -This module is a thin pass-through to ``hanzo_tools.browser.zap_server``, -which is the single source of truth for the ZAP wire protocol and the -WebSocket server hosted inside every hanzo-mcp process. Discovery is -mDNS-only (HIP-0069); the OS picks the port, mDNS carries it. - -There is one and only one ZapServer implementation in the Python stack โ€” -this module exists purely so existing callers that import -``hanzo_mcp.zap_server`` resolve to the canonical class. -""" - -from __future__ import annotations - -import logging - -from hanzo_tools.browser.zap_server import ( - ZapServer, - get_or_start_server, - get_server, - shutdown_server, -) - -logger = logging.getLogger(__name__) - - -async def start_zap_server( - tools, - call_tool, - handle_method=None, - name: str = "hanzo-mcp", - preferred_port=None, # noqa: ARG001 โ€” kept for legacy callers, ignored -): - """Start (or reuse) the canonical ZAP server and wire MCP routing. - - Args: - tools: List of tool dicts with name/description/inputSchema. - call_tool: Async (name, args) -> result for tools/call dispatch. - handle_method: Optional async (method, params) -> result for - full MCP-method parity beyond tools/list and tools/call. - name: Server name used in the handshake. - preferred_port: Ignored โ€” kept for API parity. Discovery is - mDNS-only; the OS picks the port. - - Returns: - The live ZapServer, or ``None`` if it could not start - (websockets / zap-mdns missing). - """ - server = await get_or_start_server(agent_label=name) - if server is None: - return None - - server.set_tools(list(tools)) - - async def _on_request(method, params): - if handle_method is not None: - try: - return await handle_method(method, params) - except ValueError: - # Fall through to defaults below. - pass - if method == "tools/list": - return {"tools": list(tools)} - if method == "tools/call": - tool_name = (params or {}).get("name", "") - args = (params or {}).get("arguments", {}) or {} - if not tool_name: - raise ValueError("Missing tool name") - return await call_tool(tool_name, args) - if method.startswith("notifications/"): - return {"acknowledged": True} - raise ValueError(f"unsupported method: {method}") - - server.set_request_handler(_on_request) - logger.info( - "hanzo-mcp ZAP transport up on :%d (%d tools)", server.port, len(list(tools)) - ) - return server - - -__all__ = [ - "ZapServer", - "start_zap_server", - "get_or_start_server", - "get_server", - "shutdown_server", -] diff --git a/pkg/hanzo-mcp/install_hanzo_mcp.py b/pkg/hanzo-mcp/install_hanzo_mcp.py deleted file mode 100644 index 2b91f4e5d..000000000 --- a/pkg/hanzo-mcp/install_hanzo_mcp.py +++ /dev/null @@ -1,395 +0,0 @@ -#!/usr/bin/env python3 -""" -Hanzo MCP Installation Script -============================ - -Unified installer for Hanzo MCP tools that provides: -1. MCP server installation -2. VS Code extension setup -3. Browser extension setup (future) -4. CLI tools installation -5. Unified backend configuration - -Usage: - python install_hanzo_mcp.py --all # Install everything - python install_hanzo_mcp.py --mcp # MCP server only - python install_hanzo_mcp.py --vscode # VS Code extension only - python install_hanzo_mcp.py --cli # CLI tools only - python install_hanzo_mcp.py --check # Check installation -""" - -import argparse -import json -import os -import shutil -import subprocess -import sys -from pathlib import Path -from typing import Dict, List, Optional - - -class HanzoMCPInstaller: - """Unified installer for all Hanzo MCP components""" - - def __init__(self): - self.home = Path.home() - self.hanzo_dir = self.home / ".hanzo" - self.sessions_dir = self.hanzo_dir / "sessions" - self.config_file = self.hanzo_dir / "config.json" - - # Create directories - self.hanzo_dir.mkdir(exist_ok=True) - self.sessions_dir.mkdir(exist_ok=True) - - def install_all(self) -> bool: - """Install all components""" - print("๐Ÿš€ Installing Hanzo MCP - Complete Development Environment") - print("=" * 60) - - success = True - success &= self.install_python_packages() - success &= self.setup_mcp_server() - success &= self.install_vscode_extension() - success &= self.install_cli_tools() - success &= self.create_config() - - if success: - print("\nโœ… Hanzo MCP installation completed successfully!") - print("\nNext steps:") - print("1. Restart VS Code to activate the extension") - print("2. Run `hanzo-mcp` to start the development server") - print("3. Check ~/.hanzo/config.json for configuration options") - else: - print("\nโŒ Installation failed. Check errors above.") - - return success - - def install_python_packages(self) -> bool: - """Install Python packages with uv""" - print("๐Ÿ“ฆ Installing Python packages...") - - try: - # Install hanzo-mcp and all tools - subprocess.run( - ["uv", "pip", "install", "-e", ".", "--all-extras"], - check=True, - cwd=Path(__file__).parent, - ) - - print("โœ“ Python packages installed") - return True - except subprocess.CalledProcessError as e: - print(f"โŒ Failed to install Python packages: {e}") - return False - except FileNotFoundError: - print( - "โŒ uv not found. Please install uv first: curl -LsSf https://astral.sh/uv/install.sh | sh" - ) - return False - - def setup_mcp_server(self) -> bool: - """Setup MCP server for Claude/other AI tools""" - print("๐Ÿ”Œ Setting up MCP server...") - - # Create MCP server config - mcp_config = { - "mcpServers": { - "hanzo": { - "command": "hanzo-mcp", - "args": ["--server"], - "env": { - "HANZO_SESSION_DIR": str(self.sessions_dir), - "HANZO_CONFIG": str(self.config_file), - }, - } - } - } - - # Write to Claude MCP config location - claude_config_dir = self.home / ".config" / "claude-desktop" - claude_config_dir.mkdir(parents=True, exist_ok=True) - claude_config_file = claude_config_dir / "claude_desktop_config.json" - - try: - if claude_config_file.exists(): - # Merge with existing config - with open(claude_config_file) as f: - existing = json.load(f) - existing.update(mcp_config) - mcp_config = existing - - with open(claude_config_file, "w") as f: - json.dump(mcp_config, f, indent=2) - - print(f"โœ“ MCP server configured at {claude_config_file}") - return True - except Exception as e: - print(f"โŒ Failed to setup MCP server: {e}") - return False - - def install_vscode_extension(self) -> bool: - """Install and build VS Code extension""" - print("๐Ÿ”ง Installing VS Code extension...") - - extension_dir = Path(__file__).parent / "vscode-extension" - - try: - # Install Node.js dependencies - subprocess.run(["npm", "install"], check=True, cwd=extension_dir) - - # Compile TypeScript - subprocess.run(["npm", "run", "compile"], check=True, cwd=extension_dir) - - # Package extension - subprocess.run(["npx", "vsce", "package"], check=True, cwd=extension_dir) - - # Install extension - vsix_files = list(extension_dir.glob("*.vsix")) - if vsix_files: - subprocess.run( - ["code", "--install-extension", str(vsix_files[0])], check=True - ) - print("โœ“ VS Code extension installed") - return True - else: - print("โŒ No .vsix file found") - return False - - except subprocess.CalledProcessError as e: - print(f"โŒ Failed to install VS Code extension: {e}") - print("Note: Make sure you have Node.js, npm, and VS Code installed") - return False - except FileNotFoundError as e: - print(f"โŒ Required tool not found: {e}") - return False - - def install_cli_tools(self) -> bool: - """Install CLI tools and create shell aliases""" - print("๐Ÿ’ป Installing CLI tools...") - - try: - # Create CLI wrapper scripts - cli_script = """#!/usr/bin/env python3 -import sys -import asyncio -from hanzo_mcp.dev_tools import DevToolsCore - -async def main(): - tools = DevToolsCore() - - if len(sys.argv) < 2: - print("Usage: hanzo-dev ") - print("Tools: edit, fmt, test, build, lint, guard") - return 1 - - tool = sys.argv[1] - args = sys.argv[2:] - - try: - if tool == "edit" and len(args) >= 2: - result = await tools.edit(target=args[0], op=args[1]) - elif tool == "fmt" and len(args) >= 1: - result = await tools.fmt(target=args[0]) - elif tool == "test" and len(args) >= 1: - result = await tools.test(target=args[0]) - elif tool == "build" and len(args) >= 1: - result = await tools.build(target=args[0]) - elif tool == "lint" and len(args) >= 1: - result = await tools.lint(target=args[0]) - elif tool == "guard" and len(args) >= 1: - result = await tools.guard(target=args[0]) - else: - print(f"Invalid usage for {tool}") - return 1 - - print(f"Result: {result.ok}") - if result.stdout: - print(f"Output: {result.stdout}") - if result.stderr: - print(f"Error: {result.stderr}") - if result.touched_files: - print(f"Modified files: {result.touched_files}") - - return 0 if result.ok else 1 - - except Exception as e: - print(f"Error: {e}") - return 1 - -if __name__ == "__main__": - sys.exit(asyncio.run(main())) -""" - - # Install CLI script - cli_bin = Path("/usr/local/bin/hanzo-dev") - with open(cli_bin, "w") as f: - f.write(cli_script) - cli_bin.chmod(0o755) - - # Create shell aliases - aliases = """ -# Hanzo MCP Development Tools -alias hedit='hanzo-dev edit' -alias hfmt='hanzo-dev fmt' -alias htest='hanzo-dev test' -alias hbuild='hanzo-dev build' -alias hlint='hanzo-dev lint' -alias hguard='hanzo-dev guard' -""" - - # Add to shell profiles - for shell_profile in [".bashrc", ".zshrc", ".profile"]: - profile_path = self.home / shell_profile - if profile_path.exists(): - with open(profile_path, "a") as f: - f.write(f"\n# Hanzo MCP aliases\n{aliases}\n") - - print("โœ“ CLI tools installed") - return True - - except Exception as e: - print(f"โŒ Failed to install CLI tools: {e}") - return False - - def create_config(self) -> bool: - """Create default configuration""" - print("โš™๏ธ Creating configuration...") - - config = { - "version": "1.0.0", - "session_tracking": True, - "codebase_indexing": True, - "workspace_detection": "auto", - "default_language": "auto", - "backends": { - "go": {"fmt": "goimports", "local_prefix": "github.com/luxfi"}, - "ts": {"fmt": "prettier", "package_manager": "pnpm"}, - "py": {"fmt": "ruff", "test": "pytest"}, - }, - "guard_rules": [ - { - "id": "no_node_in_sdk", - "type": "import", - "glob": "sdk/**", - "forbid_import_prefix": "github.com/luxfi/node/", - }, - { - "id": "no_generated_edits", - "type": "generated", - "glob": "api/pb/**", - "forbid_writes": True, - }, - ], - } - - try: - with open(self.config_file, "w") as f: - json.dump(config, f, indent=2) - - print(f"โœ“ Configuration created at {self.config_file}") - return True - except Exception as e: - print(f"โŒ Failed to create configuration: {e}") - return False - - def check_installation(self) -> bool: - """Check if installation is working""" - print("๐Ÿ” Checking Hanzo MCP installation...") - - checks = [ - ("Python package", self.check_python_package), - ("MCP server", self.check_mcp_server), - ("CLI tools", self.check_cli_tools), - ("Configuration", self.check_config), - ("Session directory", self.check_session_dir), - ] - - all_good = True - for name, check_fn in checks: - try: - result = check_fn() - status = "โœ“" if result else "โŒ" - print(f"{status} {name}") - all_good &= result - except Exception as e: - print(f"โŒ {name}: {e}") - all_good = False - - return all_good - - def check_python_package(self) -> bool: - """Check if Python package is installed""" - try: - import hanzo_mcp - - return True - except ImportError: - return False - - def check_mcp_server(self) -> bool: - """Check if MCP server is configured""" - claude_config_file = ( - self.home / ".config" / "claude-desktop" / "claude_desktop_config.json" - ) - if not claude_config_file.exists(): - return False - - try: - with open(claude_config_file) as f: - config = json.load(f) - return "hanzo" in config.get("mcpServers", {}) - except (OSError, json.JSONDecodeError, KeyError): - return False - - def check_cli_tools(self) -> bool: - """Check if CLI tools are installed""" - return Path("/usr/local/bin/hanzo-dev").exists() - - def check_config(self) -> bool: - """Check if configuration exists""" - return self.config_file.exists() - - def check_session_dir(self) -> bool: - """Check if session directory exists""" - return self.sessions_dir.exists() - - -def main(): - parser = argparse.ArgumentParser( - description="Install Hanzo MCP Development Environment" - ) - parser.add_argument("--all", action="store_true", help="Install all components") - parser.add_argument("--mcp", action="store_true", help="Install MCP server only") - parser.add_argument( - "--vscode", action="store_true", help="Install VS Code extension only" - ) - parser.add_argument("--cli", action="store_true", help="Install CLI tools only") - parser.add_argument("--check", action="store_true", help="Check installation") - - args = parser.parse_args() - - if not any([args.all, args.mcp, args.vscode, args.cli, args.check]): - args.all = True # Default to installing everything - - installer = HanzoMCPInstaller() - - success = True - - if args.check: - success = installer.check_installation() - else: - if args.all: - success = installer.install_all() - else: - if args.mcp: - success &= installer.setup_mcp_server() - if args.vscode: - success &= installer.install_vscode_extension() - if args.cli: - success &= installer.install_cli_tools() - - sys.exit(0 if success else 1) - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-mcp/migrate_tests.py b/pkg/hanzo-mcp/migrate_tests.py deleted file mode 100644 index 826984c0d..000000000 --- a/pkg/hanzo-mcp/migrate_tests.py +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env python3 -"""Migrate existing tests to use DRY test utilities. - -This script updates test files to use the new test_utils infrastructure. -""" - -import re -from pathlib import Path -from typing import List - -# Patterns to replace -REPLACEMENTS = [ - # Replace result assertions - (r'assert "(.*?)" in result\b', r'tool_helper.assert_in_result("\1", result)'), - (r'assert "(.*?)" in str\(result\)', r'tool_helper.assert_in_result("\1", result)'), - # Replace common tool call patterns - ( - r'result = await tool\.call\(\s*mock_ctx,\s*(.*?)\s*\)\s*if isinstance\(result, dict\) and "output" in result:\s*result = result\["output"\]', - r"result = await tool_helper.call_tool(tool, mock_ctx, \1)", - ), - # Replace mock context creation - (r"mock_ctx = Mock\(\)", r"mock_ctx = create_mock_ctx()"), - # Replace permission manager creation - ( - r"permission_manager = PermissionManager\(\)\s*permission_manager\.add_allowed_path\((.*?)\)", - r"permission_manager = create_permission_manager([\1])", - ), - # Import test utilities - ( - r"from unittest\.mock import (.*)", - r"from unittest.mock import \1\nfrom tests.test_utils import ToolTestHelper, create_mock_ctx, create_permission_manager", - ), -] - - -# Test files to migrate -def find_test_files(test_dir: Path) -> List[Path]: - """Find all test files to migrate.""" - return list(test_dir.rglob("test_*.py")) - - -def migrate_file(file_path: Path, dry_run: bool = False) -> bool: - """Migrate a single test file.""" - try: - with open(file_path, "r") as f: - content = f.read() - except Exception as e: - print(f"Error reading {file_path}: {e}") - return False - - original_content = content - modified = False - - # Apply replacements - for pattern, replacement in REPLACEMENTS: - new_content = re.sub( - pattern, replacement, content, flags=re.MULTILINE | re.DOTALL - ) - if new_content != content: - content = new_content - modified = True - - # Add tool_helper fixture if using it - if "tool_helper" in content and "@pytest.fixture" not in content: - # Find the first test method and add fixture - content = re.sub(r"(def test_\w+\(self,)", r"\1 tool_helper,", content, count=1) - modified = True - - # Ensure imports are at the top - if "from tests.test_utils import" in content: - lines = content.split("\n") - import_lines = [] - other_lines = [] - - for line in lines: - if line.startswith("from tests.test_utils import"): - import_lines.append(line) - else: - other_lines.append(line) - - # Move imports after other imports - new_lines = [] - import_section_done = False - for line in other_lines: - new_lines.append(line) - if ( - not import_section_done - and line.startswith("import ") - or line.startswith("from ") - ): - if not other_lines[other_lines.index(line) + 1].startswith( - ("import ", "from ") - ): - new_lines.extend(import_lines) - import_section_done = True - - content = "\n".join(new_lines) - modified = True - - if modified: - if dry_run: - print(f"Would update: {file_path}") - print("Changes:") - # Show diff - import difflib - - diff = difflib.unified_diff( - original_content.splitlines(keepends=True), - content.splitlines(keepends=True), - fromfile=str(file_path), - tofile=str(file_path), - ) - print( - "".join(diff)[:1000] + "..." - if len("".join(diff)) > 1000 - else "".join(diff) - ) - else: - with open(file_path, "w") as f: - f.write(content) - print(f"Updated: {file_path}") - return True - - return False - - -def main(): - """Main migration function.""" - import argparse - - parser = argparse.ArgumentParser(description="Migrate tests to use DRY utilities") - parser.add_argument( - "--dry-run", - action="store_true", - help="Show what would be changed without modifying files", - ) - parser.add_argument("--file", help="Migrate a specific file only") - args = parser.parse_args() - - test_dir = Path("tests") - - if args.file: - files = [Path(args.file)] - else: - files = find_test_files(test_dir) - - print(f"Found {len(files)} test files to check") - - updated = 0 - for file_path in files: - if file_path.name == "test_utils.py" or file_path.name == "conftest.py": - continue - - if migrate_file(file_path, dry_run=args.dry_run): - updated += 1 - - print(f"\n{'Would update' if args.dry_run else 'Updated'} {updated} files") - - if not args.dry_run and updated > 0: - print("\nNext steps:") - print("1. Run tests to ensure they still pass") - print("2. Add type hints to test files") - print("3. Run mypy to check types") - print("4. Run coverage to ensure 96.3%+ coverage") - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-mcp/pyproject.toml b/pkg/hanzo-mcp/pyproject.toml deleted file mode 100644 index 325d6572e..000000000 --- a/pkg/hanzo-mcp/pyproject.toml +++ /dev/null @@ -1,261 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-mcp" -version = "0.15.8" -description = "The Zen of Hanzo MCP: One server to rule them all. The ultimate MCP that orchestrates all others." -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -keywords = ["mcp", "claude", "hanzo", "code", "agent"] -dependencies = [ - # Hanzo AI core SDK (canonical types, config, session, MCP client) - # 2.2.1 is the first release shipping hanzoai.protocols (permissions.py imports it) - "hanzoai>=2.2.1", - # Core MCP dependencies - "mcp>=1.25.0", - "fastmcp>=2.14.4", - "pydantic>=2.12.5", - "pydantic-settings>=2.12.0", - "typing-extensions>=4.13.0", - # Unified async I/O (aiofiles + uvloop auto-installed on Linux/macOS, graceful fallback) - "hanzo-async>=0.1.3", - "uvloop>=0.22.1; sys_platform != 'win32'", - # Hanzo tool infrastructure (all tools) - "hanzo-tools>=0.3.0", - "hanzo-tools-fs>=0.1.0", - "hanzo-tools-shell>=0.6.1", - "hanzo-tools-browser[playwright]>=0.5.7", # 3 peer tools (browser/cdp/playwright); 0.5.6 was DOA - "hanzo-tools-memory>=0.2.0", - "hanzo-tools-todo>=0.1.0", - "hanzo-tools-reasoning>=0.1.0", - "hanzo-tools-lsp>=0.1.0", - "hanzo-tools-computer>=0.1.0", - # 0.2.1 fixes ConfigTool passing permission_manager positionally into BaseTool.__init__ - "hanzo-tools-config>=0.2.1", - "hanzo-tools-refactor>=0.1.0", - "hanzo-tools-llm>=0.1.0", - "hanzo-tools-code>=0.1.0", - "hanzo-tools-vcs>=0.1.0", - "hanzo-tools-net>=0.1.0", - "hanzo-tools-agent>=0.3.1", - "hanzo-tools-api>=0.3.1", - "hanzo-tools-auth>=0.1.0", - "hanzo-tools-kms>=0.1.0", - "hanzo-tools-paas>=0.1.0", - "hanzo-tools-billing>=0.1.0", - "hanzo-tools-commerce>=0.1.0", - "hanzo-tools-iam>=0.1.0", - "hanzo-tools-ingress>=0.1.0", - "hanzo-tools-mpc>=0.1.0", - "hanzo-tools-team>=0.1.0", - "hanzo-tools-ui>=0.1.0", - "hanzo-persona>=1.0.0", -] - -# For local development, install hanzo-tools-* packages from pkg/ -# pip install -e ../hanzo-tools ../hanzo-tools-fs etc. -# Or when published, add them back as dependencies - -[project.urls] -"Homepage" = "https://github.com/hanzoai/mcp" -"Bug Tracker" = "https://github.com/hanzoai/mcp/issues" -"Documentation" = "https://mcp.hanzo.ai" - -[project.optional-dependencies] -# NOTE: hanzo-tools-* packages are installed separately from pkg/ -# Use: pip install -e ../hanzo-tools -e ../hanzo-tools-fs etc. -# When published to PyPI, these extras will work: -# filesystem = ["hanzo-tools-fs>=0.1.0"] -# shell = ["hanzo-tools-shell>=0.1.0"] -# etc. - -# Memory tools with full backend support -memory = [ - "hanzo-tools-memory[full]>=0.2.2", - "sqlite-vec>=0.1.0", - "fastembed>=0.4.0", -] - -# Interactive REPL with multi-language Jupyter kernels -repl = [ - "hanzo-tools-repl>=0.1.0", - "jupyter-client>=8.6.0", - "ipykernel>=6.29.0", -] - -# IDE integration (VS Code, Cursor, JetBrains) -ide = [ - "hanzo-tools-ide>=0.1.0", -] - -# Full interactive suite (REPL + IDE + Browser) -interactive = [ - "hanzo-mcp[repl,ide]", - "hanzo-tools-browser>=0.2.1", -] - -# Development -dev = [ - "watchdog>=3.0.0", - "pytest>=7.0.0", - "pytest-cov>=4.1.0", - "ruff>=0.14.0", - "black>=23.3.0", - "mypy>=1.10.0", - "types-aiofiles>=23.2.0", - "types-psutil>=5.9.5", - "types-setuptools>=69.5.0", -] -docs = ["sphinx>=8.0.0", "sphinx-rtd-theme>=3.0.0", "myst-parser>=4.0.0", "sphinx-copybutton>=0.5.0"] -test = [ - "pytest>=7.0.0", - "pytest-cov>=4.1.0", - "pytest-mock>=3.10.0", - "pytest-asyncio>=0.26.0,<1.0.0", -] -performance = [ - "ujson>=5.7.0", - "orjson>=3.9.0", -] -publish = ["twine>=4.0.2", "build>=1.0.3"] - -[project.scripts] -hanzo-mcp = "hanzo_mcp.cli:main" -hanzo-mcp-dev = "hanzo_mcp.dev_server:run_dev_server" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_mcp*"] - -[tool.setuptools.package-data] -hanzo_mcp = ["py.typed"] - -[tool.basedpyright] -include = ["hanzo_mcp"] - -[tool.pytest.ini_options] -# Configuration for pytest -addopts = "--no-header --no-summary -p asyncio" -asyncio_mode = "auto" -asyncio_default_fixture_loop_scope = "function" -# Note: --cov-fail-under=96.3 will be added after achieving target coverage -markers = [ - "asyncio: mark test as using asyncio", - "requires_hanzo_agents: mark test as requiring hanzo-agents SDK", - "requires_memory_tools: mark test as requiring hanzo-memory package", - "slow: mark test as slow running", - "integration: mark test as integration test", -] -testpaths = ["tests"] -python_files = ["test_*.py", "*_test.py"] -python_classes = ["Test*"] -python_functions = ["test_*"] - -[tool.pytest_asyncio] -mode = "auto" -default_loop_scope = "function" - -[tool.mypy] -python_version = "3.12" -strict = true -warn_return_any = true -warn_unused_configs = true -disallow_untyped_defs = true -disallow_any_generics = true -check_untyped_defs = true -no_implicit_optional = true -warn_redundant_casts = true -warn_unused_ignores = true -warn_no_return = true -warn_unreachable = true -strict_equality = true -show_error_codes = true -show_column_numbers = true -pretty = true - -# Per-module options -[[tool.mypy.overrides]] -module = [ - "bashlex.*", - "grep_ast.*", - "libtmux.*", - "llm.*", - "nbformat.*", - "ffind.*", - "fastmcp.*", - "mcp.*", - "hanzo_agents.*", - "hanzo_memory.*", - "personalities.*", -] -ignore_missing_imports = true - -[tool.coverage.run] -source = ["hanzo_mcp"] -omit = [ - "*/tests/*", - "*/__pycache__/*", - "*/site-packages/*", - "*/venv/*", - "*/migrations/*", -] - -[tool.coverage.report] -precision = 2 -show_missing = true -skip_covered = false -fail_under = 96.3 -exclude_lines = [ - "pragma: no cover", - "def __repr__", - "if self.debug:", - "if __name__ == .__main__.:", - "raise AssertionError", - "raise NotImplementedError", - "if TYPE_CHECKING:", - "@abstractmethod", -] - -[tool.coverage.html] -directory = "htmlcov" - -[tool.ruff] -exclude = [ - ".git", - ".venv", - "venv", - "__pycache__", - "build", - "dist", - "*.egg-info", - "node_modules", - "vscode-extension", -] - -[tool.ruff.lint] -select = ["E", "F", "I", "B"] -ignore = [ - "E501", # Line too long - "B017", # Blind exception test - "B023", # Function definition loop variable - "B904", # Raise without from in except - "E741", # Ambiguous variable name - "E402", # Module level import not at top - "F401", # Unused imports (handled by tests themselves) - "F821", # Undefined name (tests may have conditional imports) - "F841", # Local variable assigned but never used - "B007", # Loop control variable not used -] - -[tool.ruff.lint.per-file-ignores] -"tests/*" = ["F401", "F821", "F841", "B007"] # Relax rules for test files -"*/__init__.py" = ["F401"] # Allow unused imports in __init__.py diff --git a/pkg/hanzo-mcp/scripts/publish.sh b/pkg/hanzo-mcp/scripts/publish.sh deleted file mode 100755 index 211a32614..000000000 --- a/pkg/hanzo-mcp/scripts/publish.sh +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env bash -set -e - -# ANSI color codes -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -RED='\033[0;31m' -BLUE='\033[0;34m' -RESET='\033[0m' - -# Get current version -CURRENT_VERSION=$(grep -E '^version = ' pyproject.toml | sed 's/version = "//g' | sed 's/"//g') - -echo -e "${BLUE}Hanzo MCP Publishing Script${RESET}" -echo -e "${GREEN}Current version: ${CURRENT_VERSION}${RESET}" -echo "" - -# Check if we're in the right directory -if [ ! -f "pyproject.toml" ] || [ ! -d "hanzo_mcp" ]; then - echo -e "${RED}Error: Must run from pkg/mcp directory${RESET}" - exit 1 -fi - -# Check for uncommitted changes -if ! git diff-index --quiet HEAD --; then - echo -e "${YELLOW}Warning: You have uncommitted changes${RESET}" - read -p "Continue anyway? (y/N) " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - exit 1 - fi -fi - -# Run tests -echo -e "${GREEN}Running tests...${RESET}" -if command -v pytest &> /dev/null; then - pytest -v || { - echo -e "${RED}Tests failed! Fix issues before publishing.${RESET}" - exit 1 - } -else - echo -e "${YELLOW}pytest not found, skipping tests${RESET}" -fi - -# Run linting -echo -e "${GREEN}Running linting...${RESET}" -if command -v ruff &> /dev/null; then - ruff check hanzo_mcp/ || { - echo -e "${RED}Linting failed! Fix issues before publishing.${RESET}" - exit 1 - } -else - echo -e "${YELLOW}ruff not found, skipping linting${RESET}" -fi - -# Clean build artifacts -echo -e "${GREEN}Cleaning build artifacts...${RESET}" -rm -rf build/ dist/ *.egg-info - -# Build packages -echo -e "${GREEN}Building distribution packages...${RESET}" -python -m build || { - echo -e "${RED}Build failed!${RESET}" - echo -e "${YELLOW}Make sure you have 'build' installed: pip install build${RESET}" - exit 1 -} - -# Show built packages -echo -e "${GREEN}Built packages:${RESET}" -ls -la dist/ - -# Check for PyPI credentials -if [ ! -f ~/.pypirc ]; then - echo -e "${YELLOW}Warning: ~/.pypirc not found${RESET}" - echo -e "You can create one with your PyPI API token:" - echo -e "[pypi]" - echo -e " username = __token__" - echo -e " password = " - echo "" -fi - -# Ask which registry to publish to -echo -e "${YELLOW}Where would you like to publish?${RESET}" -echo "1) Test PyPI (recommended for testing)" -echo "2) PyPI (production)" -echo "3) Skip publishing" -read -p "Choose (1-3): " choice - -case $choice in - 1) - echo -e "${GREEN}Publishing to Test PyPI...${RESET}" - twine upload --repository testpypi dist/* || { - echo -e "${RED}Upload failed!${RESET}" - echo -e "${YELLOW}Make sure you have twine installed: pip install twine${RESET}" - exit 1 - } - echo -e "${GREEN}Published to Test PyPI!${RESET}" - echo -e "Install with: pip install -i https://test.pypi.org/simple/ hanzo-mcp==${CURRENT_VERSION}" - ;; - 2) - echo -e "${RED}Publishing to PyPI...${RESET}" - echo -e "${YELLOW}This will publish version ${CURRENT_VERSION} to the official PyPI${RESET}" - read -p "Are you REALLY sure? (yes/no): " confirm - if [ "$confirm" == "yes" ]; then - twine upload dist/* || { - echo -e "${RED}Upload failed!${RESET}" - exit 1 - } - echo -e "${GREEN}Successfully published hanzo-mcp ${CURRENT_VERSION} to PyPI!${RESET}" - echo -e "Install with: pip install hanzo-mcp==${CURRENT_VERSION}" - - # Create git tag - echo -e "${GREEN}Creating git tag...${RESET}" - git tag -a "mcp-v${CURRENT_VERSION}" -m "Release hanzo-mcp ${CURRENT_VERSION}" - echo -e "${YELLOW}Don't forget to push the tag: git push origin mcp-v${CURRENT_VERSION}${RESET}" - else - echo -e "${YELLOW}Publishing cancelled.${RESET}" - fi - ;; - 3) - echo -e "${YELLOW}Skipping publishing. Packages are in dist/${RESET}" - ;; - *) - echo -e "${RED}Invalid choice${RESET}" - exit 1 - ;; -esac - -echo -e "${GREEN}Done!${RESET}" \ No newline at end of file diff --git a/pkg/hanzo-mcp/simple_test.py b/pkg/hanzo-mcp/simple_test.py deleted file mode 100644 index d92c48398..000000000 --- a/pkg/hanzo-mcp/simple_test.py +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/env python3 -""" -Simple test of the exact tool specifications -""" - -import asyncio -import os -import sys -import tempfile -from pathlib import Path - -# Add the package to path -sys.path.insert(0, "/Users/z/work/hanzo/python-sdk/pkg/hanzo-mcp") - - -async def simple_test(): - """Simple test without external dependencies""" - print("๐Ÿ”ง Testing basic functionality...") - - # Test workspace detection - with tempfile.TemporaryDirectory() as temp_dir: - workspace_dir = Path(temp_dir) - - # Create a simple Go project structure - go_mod = workspace_dir / "go.mod" - go_mod.write_text("module test\n\ngo 1.21\n") - - main_go = workspace_dir / "main.go" - main_go.write_text("""package main - -import "fmt" - -func main() { - fmt.Println("Hello World") -} -""") - - # Import and test workspace detection - try: - from hanzo_mcp.exact_tools import WorkspaceDetector - - detector = WorkspaceDetector() - workspace = detector.detect(str(main_go)) - - print(f" Workspace type: {workspace['type']}") - print(f" Root: {workspace['root']}") - print(f" Language: {workspace['language']}") - print(" โœ… Workspace detection working") - - except Exception as e: - print(f" โŒ Workspace detection failed: {e}") - return False - - # Test target resolution - try: - from hanzo_mcp.exact_tools import TargetResolver, TargetSpec - - resolver = TargetResolver(detector) - - # Test file target - target_spec = TargetSpec(target=f"file:{main_go}") - resolved = resolver.resolve(target_spec) - - print(f" File resolution: {len(resolved['paths'])} files") - print(f" Language inferred: {resolved['language']}") - print(" โœ… Target resolution working") - - except Exception as e: - print(f" โŒ Target resolution failed: {e}") - return False - - # Test backend selection - try: - from hanzo_mcp.exact_tools import BackendSelector - - selector = BackendSelector() - - go_fmt_backend = selector.select_backend("go", "fmt") - py_lint_backend = selector.select_backend("py", "lint") - ts_test_backend = selector.select_backend("ts", "test") - - print(f" Go fmt backend: {go_fmt_backend}") - print(f" Python lint backend: {py_lint_backend}") - print(f" TypeScript test backend: {ts_test_backend}") - print(" โœ… Backend selection working") - - except Exception as e: - print(f" โŒ Backend selection failed: {e}") - return False - - return True - - -async def test_tool_schemas(): - """Test tool schema definitions""" - print("๐Ÿ”ง Testing tool schemas...") - - try: - from hanzo_mcp.exact_tools import ( - BuildArgs, - EditArgs, - FmtArgs, - GuardArgs, - GuardRule, - LintArgs, - TargetSpec, - TestArgs, - ) - - # Test target spec - target = TargetSpec(target="ws", language="go", dry_run=True) - print(f" Target spec: {target.target}, {target.language}") - - # Test edit args - edit = EditArgs(op="rename", new_name="NewName") - print(f" Edit args: {edit.op}, {edit.new_name}") - - # Test guard rule - rule = GuardRule( - id="test-rule", - type="import", - glob="**/*.py", - forbid_import_prefix="forbidden", - ) - print(f" Guard rule: {rule.id}, {rule.type}") - - print(" โœ… All schemas working") - return True - - except Exception as e: - print(f" โŒ Schema test failed: {e}") - return False - - -async def main(): - print("๐Ÿš€ Simple Test of Exact 6-Tool Implementation\n") - - success = True - success &= await simple_test() - print() - success &= await test_tool_schemas() - print() - - if success: - print("๐ŸŽ‰ Basic functionality tests passed!") - print("\nโœจ Implementation Features:") - print(" โ€ข Workspace detection (go.work, package.json, pyproject.toml, etc.)") - print(" โ€ข Target resolution (file:, dir:, pkg:, ws, changed)") - print(" โ€ข Backend selection (language-specific tools)") - print(" โ€ข 6 universal tools (edit, fmt, test, build, lint, guard)") - print(" โ€ข LSP integration framework") - print(" โ€ข Guard rule engine") - print(" โ€ข Composition support") - else: - print("โŒ Some tests failed") - return 1 - - return 0 - - -if __name__ == "__main__": - exit_code = asyncio.run(main()) diff --git a/pkg/hanzo-mcp/sitecustomize.py b/pkg/hanzo-mcp/sitecustomize.py deleted file mode 100644 index 8679eb01f..000000000 --- a/pkg/hanzo-mcp/sitecustomize.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Local test bootstrap for hanzo-mcp. - -Ensures third-party pytest plugins aren't auto-loaded during local test runs, -which can cause import-time failures from globally installed packages. - -This file is auto-imported by Python at startup if present on sys.path. -""" - -import os as _os - -# Disable pytest's auto plugin discovery unless already set by the user/CI -_os.environ.setdefault("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "1") diff --git a/pkg/hanzo-mcp/test_complete_system.py b/pkg/hanzo-mcp/test_complete_system.py deleted file mode 100644 index 36acb472b..000000000 --- a/pkg/hanzo-mcp/test_complete_system.py +++ /dev/null @@ -1,147 +0,0 @@ -"""Final test to verify the complete system works as intended.""" - -import asyncio -import json -from pathlib import Path - -from hanzo_mcp.config import get_global_config_path, load_config -from hanzo_mcp.memory_service import PluginMemoryService - - -async def test_complete_system(): - """Test the complete system with all components working together.""" - print("Testing complete system with all components...") - - # Create a global config file with memory backend enabled - config_path = get_global_config_path() - - # Create config with memory backend - memory_config = { - "enabled_backends": ["sqlite"], - "backend_configs": { - "sqlite": { - "enabled": True, - "path": str(Path.home() / ".hanzo" / "memory.db"), - "settings": {"auto_migrate": True, "connection_timeout": 30}, - } - }, - "default_user_id": "hanzo-user", - "default_project_id": "hanzo-project", - } - - # Save the config - with open(config_path, "w") as f: - json.dump(memory_config, f, indent=2) - - print(f"Created config with memory backend at: {config_path}") - - # Load config using our system - loaded_config = load_config() - print(f"โœ“ Loaded config with enabled backends: {loaded_config.enabled_backends}") - - # Test memory service initialization - print("\nInitializing memory service...") - service = PluginMemoryService() - await service.initialize(enabled_backends=loaded_config.enabled_backends) - print(f"โœ“ Active backends: {service.get_active_backends()}") - - # Verify the memory backend is properly configured - print(f"โœ“ Available backends: {service.get_available_backends()}") - print(f"โœ“ Backend capabilities: {service.get_backend_capabilities('sqlite')}") - - # Test memory operations - print("\nTesting memory operations...") - - # Store multiple memories - test_memories = [ - { - "content": "Hanzo MCP modular architecture enables flexible backend selection", - "metadata": {"type": "architecture", "domain": "mcp", "importance": 0.9}, - }, - { - "content": "SQLite backend provides lightweight persistence with vector search", - "metadata": {"type": "backend", "domain": "storage", "importance": 0.8}, - }, - { - "content": "Plugin system allows dynamic loading of memory backends", - "metadata": {"type": "feature", "domain": "modularity", "importance": 0.85}, - }, - ] - - memory_ids = [] - for i, mem in enumerate(test_memories): - memory_id = await service.store_memory( - content=mem["content"], - metadata=mem["metadata"], - user_id=loaded_config.default_user_id, - project_id=loaded_config.default_project_id, - ) - memory_ids.append(memory_id) - print(f"โœ“ Stored memory {i + 1} with ID: {memory_id[:8]}...") - - # Test retrieval - print("\nTesting retrieval...") - retrieved = await service.retrieve_memory( - query="memory backend", - user_id=loaded_config.default_user_id, - project_id=loaded_config.default_project_id, - limit=10, - ) - print(f"โœ“ Retrieved {len(retrieved)} memories matching 'memory backend'") - - # Test search - print("\nTesting search functionality...") - search_results = await service.search_memory( - query="modular architecture", - user_id=loaded_config.default_user_id, - project_id=loaded_config.default_project_id, - limit=5, - ) - print(f"โœ“ Found {len(search_results)} memories for 'modular architecture'") - - # Display search results - for i, result in enumerate(search_results): - print( - f" Result {i + 1}: {result['content'][:60]}... (score: {result.get('similarity_score', 'N/A')})" - ) - - # Test capability queries - print("\nTesting capability queries...") - vector_backends = service.has_capability("vector_search") - persistence_backends = service.has_capability("persistence") - print(f"โœ“ Backends with vector search: {vector_backends}") - print(f"โœ“ Backends with persistence: {persistence_backends}") - - # Test backend management - print("\nTesting backend management...") - print(f"โœ“ Current active backends: {service.get_active_backends()}") - - # Try disabling and re-enabling - was_enabled = service.disable_backend("sqlite") - print(f"โœ“ SQLite disabled: {was_enabled}") - print(f"โœ“ Active backends after disable: {service.get_active_backends()}") - - was_reenabled = service.enable_backend("sqlite") - print(f"โœ“ SQLite re-enabled: {was_reenabled}") - print(f"โœ“ Active backends after re-enable: {service.get_active_backends()}") - - # Cleanup - print("\nCleaning up...") - await service.shutdown() - - # Remove test config - if config_path.exists(): - config_path.unlink() - print(f"โœ“ Removed test config: {config_path}") - - print("\n๐ŸŽ‰ Complete system test passed!") - print("โœ… uvx hanzo-mcp[memory] would work with full memory backend support") - print( - "โœ… Settings management works properly with ~/.config/hanzo/mcp-settings.json" - ) - print("โœ… Modular plugin architecture enables flexible backend selection") - print("โœ… SQLite backend provides lightweight persistence with vector search") - - -if __name__ == "__main__": - asyncio.run(test_complete_system()) diff --git a/pkg/hanzo-mcp/test_exact_tools.py b/pkg/hanzo-mcp/test_exact_tools.py deleted file mode 100644 index bba940d1a..000000000 --- a/pkg/hanzo-mcp/test_exact_tools.py +++ /dev/null @@ -1,428 +0,0 @@ -#!/usr/bin/env python3 -""" -Test Runner for Exact 6-Tool Implementation -=========================================== - -Test the exact tool specifications with real workspace scenarios. -""" - -import asyncio -import json -import shutil -import tempfile -from dataclasses import asdict -from pathlib import Path - -from hanzo_mcp.exact_tools import ( - BuildArgs, - EditArgs, - FmtArgs, - GuardArgs, - GuardRule, - LintArgs, - TargetSpec, - TestArgs, - tools, -) - - -async def test_go_workspace_scenario(): - """Test Go workspace with go.work file""" - print("๐Ÿ”ง Testing Go workspace scenario...") - - # Create temporary Go workspace - with tempfile.TemporaryDirectory() as temp_dir: - workspace_dir = Path(temp_dir) - - # Create go.work file - go_work = workspace_dir / "go.work" - go_work.write_text("""go 1.21 - -use ( - ./api - ./cli -) -""") - - # Create api module - api_dir = workspace_dir / "api" - api_dir.mkdir() - - api_go_mod = api_dir / "go.mod" - api_go_mod.write_text("module github.com/luxfi/api\n\ngo 1.21\n") - - api_main = api_dir / "main.go" - api_main.write_text("""package main - -import ( - "fmt" - "net/http" -) - -func UserHandler(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello User") -} - -func main() { - http.HandleFunc("/user", UserHandler) - http.ListenAndServe(":8080", nil) -} -""") - - # Create cli module - cli_dir = workspace_dir / "cli" - cli_dir.mkdir() - - cli_go_mod = cli_dir / "go.mod" - cli_go_mod.write_text("module github.com/luxfi/cli\n\ngo 1.21\n") - - cli_main = cli_dir / "main.go" - cli_main.write_text("""package main - -import "fmt" - -func GreetUser(name string) { - fmt.Printf("Hello %s\\n", name) -} - -func main() { - GreetUser("World") -} -""") - - # Test workspace detection - target_spec = TargetSpec(target="ws", root=str(workspace_dir)) - - # Test fmt tool - fmt_result = await tools.fmt( - target_spec, FmtArgs(opts={"local_prefix": "github.com/luxfi"}) - ) - print( - f" fmt result: {'โœ…' if fmt_result.ok else 'โŒ'} {fmt_result.language_used}" - ) - print(f" root: {fmt_result.root}") - print(f" backend: {fmt_result.backend_used}") - - # Test edit tool - organize imports - edit_result = await tools.edit(target_spec, EditArgs(op="organize_imports")) - print( - f" edit result: {'โœ…' if edit_result.ok else 'โŒ'} {edit_result.backend_used}" - ) - - # Test guard tool - guard_rules = [ - GuardRule( - id="no-net-http-in-api", - type="import", - glob="api/*.go", - forbid_import_prefix="net/http", - ) - ] - guard_result = await tools.guard(target_spec, GuardArgs(rules=guard_rules)) - print( - f" guard result: {'โœ…' if guard_result.ok else 'โŒ'} violations: {len(guard_result.errors)}" - ) - - # Test specific package - pkg_target = TargetSpec(target="pkg:./cli/...", root=str(workspace_dir)) - - test_result = await tools.test(pkg_target, TestArgs(opts={"dry_run": True})) - print(f" test result: {'โœ…' if test_result.ok else 'โŒ'} {test_result.stdout}") - - -async def test_typescript_project(): - """Test TypeScript project scenario""" - print("๐Ÿ”ง Testing TypeScript project scenario...") - - with tempfile.TemporaryDirectory() as temp_dir: - workspace_dir = Path(temp_dir) - - # Create package.json with workspaces - package_json = workspace_dir / "package.json" - package_json.write_text( - json.dumps( - { - "name": "test-workspace", - "workspaces": ["packages/*"], - "scripts": {"test": "jest", "build": "tsc"}, - "devDependencies": {"typescript": "^5.0.0", "jest": "^29.0.0"}, - }, - indent=2, - ) - ) - - # Create TypeScript config - tsconfig = workspace_dir / "tsconfig.json" - tsconfig.write_text( - json.dumps( - { - "compilerOptions": { - "target": "ES2022", - "module": "commonjs", - "strict": True, - } - }, - indent=2, - ) - ) - - # Create packages - packages_dir = workspace_dir / "packages" - packages_dir.mkdir() - - # Core package - core_dir = packages_dir / "core" - core_dir.mkdir() - - core_package = core_dir / "package.json" - core_package.write_text( - json.dumps({"name": "@test/core", "version": "1.0.0"}, indent=2) - ) - - core_index = core_dir / "index.ts" - core_index.write_text("""export interface User { - id: string; - name: string; -} - -export class UserService { - getUser(id: string): User | null { - return { id, name: 'Test User' }; - } -} -""") - - # Test TypeScript workspace - target_spec = TargetSpec(target="ws", root=str(workspace_dir), language="ts") - - fmt_result = await tools.fmt(target_spec, FmtArgs()) - print( - f" fmt result: {'โœ…' if fmt_result.ok else 'โŒ'} {fmt_result.backend_used}" - ) - - build_result = await tools.build(target_spec, BuildArgs(opts={"dry_run": True})) - print( - f" build result: {'โœ…' if build_result.ok else 'โŒ'} {build_result.stdout}" - ) - - lint_result = await tools.lint(target_spec, LintArgs(opts={"dry_run": True})) - print( - f" lint result: {'โœ…' if lint_result.ok else 'โŒ'} {lint_result.backend_used}" - ) - - -async def test_target_resolution(): - """Test target resolution scenarios""" - print("๐Ÿ”ง Testing target resolution...") - - with tempfile.TemporaryDirectory() as temp_dir: - workspace_dir = Path(temp_dir) - - # Create simple Go project - go_mod = workspace_dir / "go.mod" - go_mod.write_text("module test\n\ngo 1.21\n") - - main_go = workspace_dir / "main.go" - main_go.write_text("""package main - -import "fmt" - -func main() { - fmt.Println("Hello") -} -""") - - src_dir = workspace_dir / "src" - src_dir.mkdir() - - helper_go = src_dir / "helper.go" - helper_go.write_text("""package src - -func Helper() string { - return "helper" -} -""") - - # Test different target types - test_cases = [ - ("file:" + str(main_go), "Single file"), - ("dir:" + str(src_dir), "Directory"), - ("ws", "Workspace"), - ("pkg:.", "Current package"), - ] - - for target, description in test_cases: - target_spec = TargetSpec( - target=target, root=str(workspace_dir), dry_run=True - ) - - result = await tools.fmt(target_spec, FmtArgs()) - print( - f" {description}: {'โœ…' if result.ok else 'โŒ'} scope={len(result.scope_resolved)}" - ) - - -async def test_composition_workflow(): - """Test tool composition workflow""" - print("๐Ÿ”ง Testing composition workflow...") - - with tempfile.TemporaryDirectory() as temp_dir: - workspace_dir = Path(temp_dir) - - # Create Python project - pyproject = workspace_dir / "pyproject.toml" - pyproject.write_text("""[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[project] -name = "test-project" -version = "0.1.0" -""") - - src_dir = workspace_dir / "src" - src_dir.mkdir() - - main_py = src_dir / "main.py" - main_py.write_text("""import os -import sys -from typing import Optional - - -def process_user(user_id: str) -> Optional[str]: - if not user_id: - return None - return f"User: {user_id}" - - -if __name__ == "__main__": - print(process_user("123")) -""") - - # Composition workflow: edit -> fmt -> lint -> test - target_spec = TargetSpec( - target="file:" + str(main_py), root=str(workspace_dir), language="py" - ) - - # 1. Organize imports - print(" Step 1: Organize imports") - edit_result = await tools.edit( - target_spec, EditArgs(op="organize_imports", dry_run=True) - ) - print(f" {'โœ…' if edit_result.ok else 'โŒ'} {edit_result.stdout}") - - # 2. Format code - print(" Step 2: Format code") - fmt_result = await tools.fmt(target_spec, FmtArgs()) - print(f" {'โœ…' if fmt_result.ok else 'โŒ'} {fmt_result.backend_used}") - - # 3. Lint code - print(" Step 3: Lint code") - lint_result = await tools.lint(target_spec, LintArgs(opts={"dry_run": True})) - print(f" {'โœ…' if lint_result.ok else 'โŒ'} {lint_result.backend_used}") - - # 4. Test code - print(" Step 4: Test code") - test_result = await tools.test(target_spec, TestArgs(opts={"dry_run": True})) - print(f" {'โœ…' if test_result.ok else 'โŒ'} {test_result.stdout}") - - -async def test_guard_rules(): - """Test guard rule scenarios""" - print("๐Ÿ”ง Testing guard rules...") - - with tempfile.TemporaryDirectory() as temp_dir: - workspace_dir = Path(temp_dir) - - # Create SDK structure - sdk_dir = workspace_dir / "sdk" - sdk_dir.mkdir() - - # Bad import in SDK - sdk_file = sdk_dir / "bad.py" - sdk_file.write_text("""import node -from node.crypto import hash - -def bad_function(): - return node.process() -""") - - # API contracts - api_dir = workspace_dir / "api" - api_dir.mkdir() - - api_file = api_dir / "contract.py" - api_file.write_text("""import requests -from http.client import HTTPConnection - -def api_call(): - return requests.get("http://example.com") -""") - - # Generated files - generated_dir = workspace_dir / "api" / "pb" - generated_dir.mkdir(parents=True) - - generated_file = generated_dir / "user_pb2.py" - generated_file.write_text("# Generated file - do not edit") - - # Test guard rules - guard_rules = [ - GuardRule( - id="no-node-in-sdk", - type="import", - glob="sdk/**/*.py", - forbid_import_prefix="node", - ), - GuardRule( - id="no-http-in-contracts", - type="import", - glob="api/*.py", - forbid_import_prefix="requests", - ), - GuardRule( - id="no-edits-in-generated", - type="generated", - glob="api/pb/**", - forbid_writes=True, - ), - ] - - target_spec = TargetSpec(target="ws", root=str(workspace_dir)) - - guard_result = await tools.guard(target_spec, GuardArgs(rules=guard_rules)) - print(f" guard result: {'โœ…' if guard_result.ok else 'โŒ'}") - print(f" violations found: {len(guard_result.errors)}") - for error in guard_result.errors: - print(f" - {error}") - - -async def main(): - """Run all tests""" - print("๐Ÿš€ Testing Exact 6-Tool Implementation\n") - - try: - await test_go_workspace_scenario() - print() - - await test_typescript_project() - print() - - await test_target_resolution() - print() - - await test_composition_workflow() - print() - - await test_guard_rules() - print() - - print("๐ŸŽ‰ All tests completed!") - - except Exception as e: - print(f"โŒ Test failed: {e}") - raise - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/test_memory_integration.py b/pkg/hanzo-mcp/test_memory_integration.py deleted file mode 100644 index 748d2423d..000000000 --- a/pkg/hanzo-mcp/test_memory_integration.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python3 -"""Test memory integration in hanzo-mcp.""" - -import asyncio -import sys -from pathlib import Path - -# Add parent to path -sys.path.insert(0, str(Path(__file__).parent)) - - -class Console: - """Simple console replacement.""" - - def print(self, text): - print(text) - - -console = Console() - - -async def test_memory_integration(): - """Test that memory tools work with local storage.""" - - console.print("\n[bold cyan]Testing Hanzo MCP Memory Integration[/bold cyan]\n") - - # Test importing hanzo-memory - try: - import hanzo_memory - - console.print("โœ… hanzo-memory package imported successfully") - except ImportError as e: - console.print(f"โŒ Failed to import hanzo-memory: {e}") - return - - # Test local memory client - try: - from hanzo_memory.db.local_client import LocalMemoryClient - - console.print("โœ… LocalMemoryClient imported successfully") - - # Create client - client = LocalMemoryClient(enable_markdown=True) - await client.initialize() - console.print("โœ… LocalMemoryClient initialized with markdown support") - - # Check for markdown memories - projects = await client.list_projects() - markdown_project = next( - (p for p in projects if p.project_id == "markdown_import"), None - ) - - if markdown_project: - memory_count = len( - [ - m - for m in client.memories.values() - if m.get("project_id") == "markdown_import" - ] - ) - console.print(f"โœ… Found {memory_count} memories from markdown files") - else: - console.print("โ„น๏ธ No markdown memories found (first run)") - - await client.close() - - except Exception as e: - console.print(f"โŒ Error with LocalMemoryClient: {e}") - return - - # Test MCP tools integration - try: - # Check if memory tools can be imported - from hanzo_tools.memory import memory_tools - - console.print("โœ… Memory tools module imported") - - tool_names = [t.name for t in memory_tools.MEMORY_TOOLS] - console.print(f"โœ… Found {len(tool_names)} memory tools: {tool_names[:3]}...") - - except ImportError as e: - console.print(f"โš ๏ธ Memory tools not available (expected): {e}") - console.print( - " This is OK - memory tools are loaded dynamically when MCP server starts" - ) - - console.print( - "\n[bold green]โœ… Memory integration test completed successfully![/bold green]" - ) - console.print( - "\nYour markdown files (LLM.md, CLAUDE.md, etc.) will be automatically loaded into memory when using hanzo-mcp tools." - ) - - -if __name__ == "__main__": - asyncio.run(test_memory_integration()) diff --git a/pkg/hanzo-mcp/test_memory_settings.py b/pkg/hanzo-mcp/test_memory_settings.py deleted file mode 100644 index c8bd89692..000000000 --- a/pkg/hanzo-mcp/test_memory_settings.py +++ /dev/null @@ -1,110 +0,0 @@ -"""Test the memory backend with settings management.""" - -import asyncio -import json -from pathlib import Path - -from hanzo_mcp.config import get_global_config_path, load_config, save_global_config -from hanzo_mcp.memory_service import PluginMemoryService - - -async def test_memory_backend_with_settings(): - """Test the memory backend with settings management.""" - print("Testing memory backend with settings management...") - - # Create a global config file - config_path = get_global_config_path() - print(f"Global config path: {config_path}") - - # Create sample config - sample_config = { - "enabled_backends": ["sqlite"], - "backend_configs": { - "sqlite": { - "enabled": True, - "path": str(Path.home() / ".hanzo" / "memory.db"), - "settings": {"auto_migrate": True, "connection_timeout": 30}, - } - }, - "default_user_id": "test-user", - "default_project_id": "test-project", - } - - # Save the config - with open(config_path, "w") as f: - json.dump(sample_config, f, indent=2) - - print(f"Saved sample config to {config_path}") - - # Load config using our system - loaded_config = load_config() - print(f"Loaded config enabled backends: {loaded_config.enabled_backends}") - print(f"Loaded config default user: {loaded_config.default_user_id}") - print(f"Loaded config default project: {loaded_config.default_project_id}") - - # Test memory service with loaded config - print("\nTesting memory service with loaded config...") - service = PluginMemoryService() - - # Initialize with the loaded config's settings - await service.initialize(enabled_backends=loaded_config.enabled_backends) - print(f"Active backends: {service.get_active_backends()}") - - # Test memory operations - print("\nTesting memory operations...") - - # Store a memory - metadata = {"type": "test", "source": "settings_test", "importance": 0.8} - memory_id = await service.store_memory( - content="This is a test memory stored using the settings-managed memory backend", - metadata=metadata, - user_id=loaded_config.default_user_id, - project_id=loaded_config.default_project_id, - ) - print(f"Stored memory with ID: {memory_id}") - - # Retrieve the memory - results = await service.retrieve_memory( - query="test memory", - user_id=loaded_config.default_user_id, - project_id=loaded_config.default_project_id, - limit=5, - ) - print(f"Retrieved {len(results)} memories") - if results: - print(f"First result content preview: {results[0]['content'][:50]}...") - print(f"Result metadata: {results[0]['metadata']}") - - # Test search functionality - print("\nTesting search functionality...") - search_results = await service.search_memory( - query="settings-managed memory backend", - user_id=loaded_config.default_user_id, - project_id=loaded_config.default_project_id, - limit=5, - ) - print(f"Searched and found {len(search_results)} memories") - - # Test backend management - print("\nTesting backend management...") - print(f"Available backends: {service.get_available_backends()}") - print(f"Active backends: {service.get_active_backends()}") - - # Check capabilities - print(f"Backends with persistence: {service.has_capability('persistence')}") - print(f"Backends with embeddings: {service.has_capability('embeddings')}") - - # Cleanup: shutdown the service - await service.shutdown() - print("\nService shut down successfully") - - # Optionally, remove the test config file - if config_path.exists(): - config_path.unlink() - print(f"Cleaned up config file: {config_path}") - - print("\nโœ… Memory backend with settings management test completed successfully!") - - -if __name__ == "__main__": - asyncio.run(test_memory_backend_with_settings()) diff --git a/pkg/hanzo-mcp/test_modular_architecture.py b/pkg/hanzo-mcp/test_modular_architecture.py deleted file mode 100644 index 00d2a3529..000000000 --- a/pkg/hanzo-mcp/test_modular_architecture.py +++ /dev/null @@ -1,99 +0,0 @@ -"""Test the modular plugin architecture implementation.""" - -import asyncio -import tempfile -from pathlib import Path - -from hanzo_mcp.backends.sqlite_plugin import SQLiteBackendPlugin -from hanzo_mcp.config import get_default_config -from hanzo_mcp.memory_service import PluginMemoryService -from hanzo_mcp.plugin_interface import Capability - - -async def test_modular_architecture(): - """Test the modular plugin architecture.""" - print("Testing modular plugin architecture...") - - # Test 1: Plugin interface and registry - print("\n1. Testing plugin interface and registry...") - - # Create a SQLite plugin - plugin = SQLiteBackendPlugin() - print(f" Plugin name: {plugin.name}") - print(f" Plugin capabilities: {plugin.capabilities}") - - # Initialize the plugin - await plugin.initialize() - print(" Plugin initialized successfully") - - # Test 2: Memory service with plugin support - print("\n2. Testing memory service with plugin support...") - - # Create memory service - service = PluginMemoryService() - print(f" Available backends: {service.get_available_backends()}") - - # Initialize with default backends (SQLite) - await service.initialize() - print(f" Active backends: {service.get_active_backends()}") - - # Test 3: Memory operations - print("\n3. Testing memory operations...") - - # Store a memory - metadata = {"type": "test", "source": "modular_test"} - memory_id = await service.store_memory( - content="This is a test memory for the modular architecture", - metadata=metadata, - user_id="test-user", - project_id="test-project", - ) - print(f" Stored memory with ID: {memory_id}") - - # Retrieve the memory - results = await service.retrieve_memory( - query="test memory", user_id="test-user", project_id="test-project", limit=5 - ) - print(f" Retrieved {len(results)} memories") - if results: - print(f" First result content preview: {results[0]['content'][:50]}...") - - # Test 4: Capability queries - print("\n4. Testing capability queries...") - - backends_with_vector_search = service.has_capability(Capability.VECTOR_SEARCH) - print(f" Backends with vector search: {backends_with_vector_search}") - - backends_with_persistence = service.has_capability(Capability.PERSISTENCE) - print(f" Backends with persistence: {backends_with_persistence}") - - # Test 5: Backend management - print("\n5. Testing backend management...") - - # Try to disable and enable backends - sqlite_disabled = service.disable_backend("sqlite") - print(f" SQLite disabled: {sqlite_disabled}") - print(f" Active backends after disabling SQLite: {service.get_active_backends()}") - - sqlite_enabled = service.enable_backend("sqlite") - print(f" SQLite enabled: {sqlite_enabled}") - print(f" Active backends after enabling SQLite: {service.get_active_backends()}") - - # Test 6: Configuration system - print("\n6. Testing configuration system...") - - config = get_default_config() - print(f" Default enabled backends: {config.enabled_backends}") - print(f" Default user ID: {config.default_user_id}") - print(f" Default project ID: {config.default_project_id}") - - # Test 7: Shutdown - print("\n7. Testing shutdown...") - await service.shutdown() - print(" Service shut down successfully") - - print("\nโœ… All tests passed! Modular plugin architecture is working correctly.") - - -if __name__ == "__main__": - asyncio.run(test_modular_architecture()) diff --git a/pkg/hanzo-mcp/tests/__init__.py b/pkg/hanzo-mcp/tests/__init__.py deleted file mode 100644 index 40298b22a..000000000 --- a/pkg/hanzo-mcp/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Test suite for Hanzo AI.""" diff --git a/pkg/hanzo-mcp/tests/benchmark_search.py b/pkg/hanzo-mcp/tests/benchmark_search.py deleted file mode 100644 index 25f2c4e0a..000000000 --- a/pkg/hanzo-mcp/tests/benchmark_search.py +++ /dev/null @@ -1,592 +0,0 @@ -"""Benchmark suite for search and database storage performance.""" - -import asyncio -import json -import statistics -import sys -import time -from pathlib import Path -from typing import Any, Dict, List - -# Add project root to path for imports -sys.path.insert(0, str(Path(__file__).parent.parent)) - -from hanzo_mcp.tools.vector.ast_analyzer import ASTAnalyzer -from hanzo_mcp.tools.vector.project_manager import ProjectVectorManager -from hanzo_mcp.tools.vector.vector_index import VectorIndexTool -from hanzo_tools.filesystem.search_tool import SearchTool - -from hanzo_mcp.tools.common.permissions import PermissionManager - - -class SearchBenchmark: - """Comprehensive benchmark suite for search performance.""" - - def __init__(self, project_root: str): - """Initialize benchmark with project root.""" - self.project_root = Path(project_root) - self.permission_manager = PermissionManager() - self.permission_manager.add_allowed_path(str(self.project_root)) - - # Database configuration - self.db_config = { - "data_path": str(self.project_root / ".vector_db"), - "embedding_model": "text-embedding-3-small", - "dimension": 1536, - } - - self.results = {} - - def create_test_project(self, size: str = "medium") -> Path: - """Create a test project of specified size.""" - test_dir = self.project_root / f"test_project_{size}" - test_dir.mkdir(exist_ok=True) - - # Size configurations - sizes = { - "small": {"files": 10, "functions_per_file": 5, "lines_per_function": 10}, - "medium": {"files": 50, "functions_per_file": 10, "lines_per_function": 20}, - "large": {"files": 100, "functions_per_file": 15, "lines_per_function": 30}, - } - - config = sizes.get(size, sizes["medium"]) - - for i in range(config["files"]): - file_path = test_dir / f"module_{i:03d}.py" - content = self._generate_file_content(i, config) - file_path.write_text(content) - - # Create LLM.md for project detection - llm_md = test_dir / "LLM.md" - llm_md.write_text(f"""# Test Project ({size}) - -This is a test project for benchmarking search performance. - -## Project Structure -- {config["files"]} Python modules -- {config["functions_per_file"]} functions per module -- {config["lines_per_function"]} lines per function - -## Features -- Error handling patterns -- Data processing functions -- Utility functions -- Configuration management -""") - - return test_dir - - def _generate_file_content(self, file_idx: int, config: Dict[str, int]) -> str: - """Generate realistic Python file content.""" - functions = [] - - for func_idx in range(config["functions_per_file"]): - func_name = f"process_data_{func_idx}" - - if func_idx % 3 == 0: - # Error handling function - func_content = f'''def {func_name}(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {{}} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in {func_name}: {{e}}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in {func_name}: {{e}}") - raise''' - - elif func_idx % 3 == 1: - # Utility function - func_content = f'''def {func_name}(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed''' - - else: - # Regular processing function - func_content = f'''def {func_name}(dataset, parameters): - """Standard data processing function.""" - results = [] - - for record in dataset: - # Validate record - if not validate_record(record): - continue - - # Process record - processed = apply_transformations(record, parameters) - results.append(processed) - - return results''' - - functions.append(func_content) - - # File header and imports - header = f'''"""Module {file_idx:03d} - Auto-generated for benchmarking.""" - -import logging -import json -from typing import List, Dict, Any, Optional - -logger = logging.getLogger(__name__) - -def get_default_config() -> Dict[str, Any]: - """Get default configuration for module {file_idx:03d}.""" - return {{ - "module_id": {file_idx}, - "uppercase": True, - "trim": True, - "validate": True, - }} - -def validate_record(record: Any) -> bool: - """Validate a single record.""" - try: - return record is not None and str(record).strip() != "" - except Exception: - return False - -def apply_transformations(record: Any, params: Dict[str, Any]) -> Any: - """Apply transformations to a record.""" - if params.get("stringify", True): - record = str(record) - - if params.get("normalize", True): - record = record.strip().lower() - - return record - -''' - - return header + "\n\n".join(functions) + "\n" - - async def benchmark_indexing(self, project_path: Path) -> Dict[str, Any]: - """Benchmark vector database indexing performance.""" - print("\\n=== Benchmarking Indexing Performance ===") - print(f"Project: {project_path}") - - # Initialize project manager - project_manager = ProjectVectorManager( - global_db_path=self.db_config["data_path"], - embedding_model=self.db_config["embedding_model"], - dimension=self.db_config["dimension"], - ) - - # Create vector index tool - vector_tool = VectorIndexTool(self.permission_manager, project_manager) - - # Mock context - class MockContext: - def __init__(self): - self.meta = {} - - start_time = time.time() - - # Index the project - try: - result = await vector_tool.call( - MockContext(), - path=str(project_path), - force_reindex=True, - chunk_size=500, - chunk_overlap=50, - ) - - indexing_time = time.time() - start_time - - # Count files and analyze storage - python_files = list(project_path.rglob("*.py")) - total_size = sum(f.stat().st_size for f in python_files) - - # Get database size if it exists - db_path = Path(self.db_config["data_path"]) - db_size = 0 - if db_path.exists(): - for db_file in db_path.rglob("*"): - if db_file.is_file(): - db_size += db_file.stat().st_size - - storage_ratio = db_size / total_size if total_size > 0 else 0 - - benchmark_result = { - "success": "Successfully indexed" in result, - "indexing_time_seconds": indexing_time, - "files_count": len(python_files), - "source_size_bytes": total_size, - "database_size_bytes": db_size, - "storage_ratio": storage_ratio, - "files_per_second": ( - len(python_files) / indexing_time if indexing_time > 0 else 0 - ), - "mb_per_second": ( - (total_size / 1024 / 1024) / indexing_time - if indexing_time > 0 - else 0 - ), - } - - print(f"Indexing completed in {indexing_time:.2f} seconds") - print(f"Files indexed: {len(python_files)}") - print(f"Source size: {total_size / 1024 / 1024:.2f} MB") - print(f"Database size: {db_size / 1024 / 1024:.2f} MB") - print(f"Storage ratio: {storage_ratio:.2f}x") - print( - f"Performance: {len(python_files) / indexing_time:.1f} files/sec, {(total_size / 1024 / 1024) / indexing_time:.1f} MB/sec" - ) - - return benchmark_result - - except Exception as e: - print(f"Indexing failed: {e}") - return { - "success": False, - "error": str(e), - "indexing_time_seconds": time.time() - start_time, - } - - async def benchmark_search_performance( - self, project_path: Path, queries: List[str] - ) -> Dict[str, Any]: - """Benchmark search performance across different query types.""" - print("\\n=== Benchmarking Search Performance ===") - - # Initialize tools - project_manager = ProjectVectorManager( - global_db_path=self.db_config["data_path"], - embedding_model=self.db_config["embedding_model"], - dimension=self.db_config["dimension"], - ) - - unified_tool = SearchTool(self.permission_manager, project_manager) - - class MockContext: - def __init__(self): - self.meta = {} - - search_results = {} - - for query in queries: - print(f"\\nTesting query: '{query}'") - times = [] - result_counts = [] - - # Run each query multiple times for statistical accuracy - for _run in range(3): - start_time = time.time() - - try: - # Mock path validation methods - unified_tool.validate_path = lambda x: type( - "obj", (object,), {"is_error": False} - )() - unified_tool.check_path_allowed = lambda x, y: (True, None) - unified_tool.check_path_exists = lambda x, y: (True, None) - - result = await unified_tool.call( - MockContext(), - pattern=query, - path=str(project_path), - max_results=20, - enable_vector=True, - enable_ast=True, - enable_symbol=True, - include_context=True, - ) - - search_time = time.time() - start_time - times.append(search_time) - - # Count results - result_count = result.count("Result ") if "Result " in result else 0 - result_counts.append(result_count) - - except Exception as e: - print(f"Search failed for '{query}': {e}") - times.append(float("inf")) - result_counts.append(0) - - # Calculate statistics - valid_times = [t for t in times if t != float("inf")] - - search_results[query] = { - "avg_time_seconds": ( - statistics.mean(valid_times) if valid_times else float("inf") - ), - "min_time_seconds": min(valid_times) if valid_times else float("inf"), - "max_time_seconds": max(valid_times) if valid_times else float("inf"), - "avg_results": statistics.mean(result_counts) if result_counts else 0, - "success_rate": len(valid_times) / len(times), - "runs": len(times), - } - - if valid_times: - print(f" Average time: {statistics.mean(valid_times):.3f}s") - print(f" Average results: {statistics.mean(result_counts):.1f}") - print(f" Success rate: {len(valid_times) / len(times) * 100:.1f}%") - - return search_results - - async def benchmark_ast_analysis(self, project_path: Path) -> Dict[str, Any]: - """Benchmark AST analysis performance.""" - print("\\n=== Benchmarking AST Analysis ===") - - analyzer = ASTAnalyzer() - python_files = list(project_path.rglob("*.py")) - - analysis_times = [] - symbol_counts = [] - - start_time = time.time() - - for file_path in python_files: - file_start = time.time() - - try: - file_ast = analyzer.analyze_file(str(file_path)) - file_time = time.time() - file_start - - analysis_times.append(file_time) - symbol_counts.append(len(file_ast.symbols) if file_ast else 0) - - except Exception as e: - print(f"AST analysis failed for {file_path}: {e}") - analysis_times.append(float("inf")) - symbol_counts.append(0) - - total_time = time.time() - start_time - valid_times = [t for t in analysis_times if t != float("inf")] - - return { - "total_files": len(python_files), - "successful_analyses": len(valid_times), - "total_time_seconds": total_time, - "avg_time_per_file": ( - statistics.mean(valid_times) if valid_times else float("inf") - ), - "total_symbols": sum(symbol_counts), - "avg_symbols_per_file": ( - statistics.mean(symbol_counts) if symbol_counts else 0 - ), - "files_per_second": len(valid_times) / total_time if total_time > 0 else 0, - } - - async def run_comprehensive_benchmark(self) -> Dict[str, Any]: - """Run comprehensive benchmark suite.""" - print("๐Ÿš€ Starting Comprehensive Unified Search Benchmark") - print("=" * 60) - - benchmark_results = { - "timestamp": time.time(), - "project_root": str(self.project_root), - "database_config": self.db_config, - "results": {}, - } - - # Test queries of different types - test_queries = [ - # Exact matches - "process_data_0", - "get_default_config", - # Regex patterns - "def.*error", - ".*handling.*", - # Natural language - "error handling functionality", - "data transformation utilities", - # Code patterns - "try.*except", - "logger\\.error", - ] - - # Benchmark different project sizes - for size in ["small", "medium"]: # Skip large for now - print(f"\\n{'=' * 20} Testing {size.upper()} Project {'=' * 20}") - - # Create test project - test_project = self.create_test_project(size) - - size_results = {} - - # 1. Benchmark indexing - size_results["indexing"] = await self.benchmark_indexing(test_project) - - # 2. Benchmark AST analysis - size_results["ast_analysis"] = await self.benchmark_ast_analysis( - test_project - ) - - # 3. Benchmark search performance - size_results[ - "search_performance" - ] = await self.benchmark_search_performance(test_project, test_queries) - - benchmark_results["results"][size] = size_results - - return benchmark_results - - def generate_report(self, results: Dict[str, Any]) -> str: - """Generate a comprehensive benchmark report.""" - report = [] - report.append("# Search Benchmark Report") - report.append(f"Generated at: {time.ctime(results['timestamp'])}") - report.append(f"Project: {results['project_root']}") - report.append("") - - for size, size_results in results["results"].items(): - report.append(f"## {size.upper()} Project Results") - report.append("") - - # Indexing results - indexing = size_results["indexing"] - report.append("### Indexing Performance") - if indexing["success"]: - report.append( - f"- **Indexing Time**: {indexing['indexing_time_seconds']:.2f} seconds" - ) - report.append(f"- **Files Indexed**: {indexing['files_count']}") - report.append( - f"- **Source Size**: {indexing['source_size_bytes'] / 1024 / 1024:.2f} MB" - ) - report.append( - f"- **Database Size**: {indexing['database_size_bytes'] / 1024 / 1024:.2f} MB" - ) - report.append(f"- **Storage Ratio**: {indexing['storage_ratio']:.2f}x") - report.append( - f"- **Performance**: {indexing['files_per_second']:.1f} files/sec, {indexing['mb_per_second']:.1f} MB/sec" - ) - else: - report.append( - f"- **Status**: Failed - {indexing.get('error', 'Unknown error')}" - ) - report.append("") - - # AST Analysis results - ast = size_results["ast_analysis"] - report.append("### AST Analysis Performance") - report.append(f"- **Total Files**: {ast['total_files']}") - report.append(f"- **Successful Analyses**: {ast['successful_analyses']}") - report.append(f"- **Total Time**: {ast['total_time_seconds']:.2f} seconds") - report.append( - f"- **Avg Time per File**: {ast['avg_time_per_file']:.3f} seconds" - ) - report.append(f"- **Total Symbols**: {ast['total_symbols']}") - report.append( - f"- **Avg Symbols per File**: {ast['avg_symbols_per_file']:.1f}" - ) - report.append(f"- **Files per Second**: {ast['files_per_second']:.1f}") - report.append("") - - # Search Performance results - search = size_results["search_performance"] - report.append("### Search Performance") - report.append("| Query | Avg Time (s) | Avg Results | Success Rate |") - report.append("|-------|--------------|-------------|--------------|") - - for query, metrics in search.items(): - avg_time = metrics["avg_time_seconds"] - avg_results = metrics["avg_results"] - success_rate = metrics["success_rate"] * 100 - - if avg_time == float("inf"): - time_str = "Failed" - else: - time_str = f"{avg_time:.3f}" - - report.append( - f"| `{query}` | {time_str} | {avg_results:.1f} | {success_rate:.1f}% |" - ) - - report.append("") - - # Performance summary - report.append("## Performance Summary") - report.append("") - - for size, size_results in results["results"].items(): - indexing = size_results["indexing"] - ast = size_results["ast_analysis"] - - if indexing["success"]: - report.append(f"**{size.upper()} Project:**") - report.append( - f"- Indexed {indexing['files_count']} files in {indexing['indexing_time_seconds']:.2f}s" - ) - report.append( - f"- Database compression: {indexing['storage_ratio']:.2f}x" - ) - report.append( - f"- AST analysis: {ast['files_per_second']:.1f} files/sec" - ) - report.append("") - - return "\\n".join(report) - - -async def main(): - """Run the benchmark suite.""" - import argparse - - parser = argparse.ArgumentParser(description="Benchmark search performance") - parser.add_argument("--project-root", default=".", help="Project root directory") - parser.add_argument("--output", help="Output file for results") - args = parser.parse_args() - - # Initialize benchmark - benchmark = SearchBenchmark(args.project_root) - - try: - # Run comprehensive benchmark - results = await benchmark.run_comprehensive_benchmark() - - # Generate report - report = benchmark.generate_report(results) - - # Output results - if args.output: - with open(args.output, "w") as f: - f.write(report) - print(f"\\nBenchmark report saved to: {args.output}") - else: - print("\\n" + "=" * 60) - print(report) - - # Save raw results as JSON - json_file = Path(args.project_root) / "benchmark_results.json" - with open(json_file, "w") as f: - json.dump(results, f, indent=2) - - print(f"Raw results saved to: {json_file}") - - except Exception as e: - print(f"Benchmark failed: {e}") - import traceback - - traceback.print_exc() - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/tests/benchmark_simple.py b/pkg/hanzo-mcp/tests/benchmark_simple.py deleted file mode 100644 index ade4af8a3..000000000 --- a/pkg/hanzo-mcp/tests/benchmark_simple.py +++ /dev/null @@ -1,171 +0,0 @@ -"""Simplified benchmark script for testing search on this project.""" - -# Add project root to path -import asyncio -import sys -import time -from pathlib import Path - -sys.path.insert(0, str(Path(__file__).parent.parent)) - -from hanzo_mcp.tools.vector.ast_analyzer import ASTAnalyzer -from hanzo_tools.filesystem.ast_tool import ASTTool -from hanzo_tools.filesystem.grep import Grep -from hanzo_tools.filesystem.search_tool import SearchTool - -from hanzo_mcp.tools.common.permissions import PermissionManager - - -async def simple_benchmark(): - """Run a simple benchmark on this MCP project.""" - print("๐Ÿ” Simple Unified Search Benchmark") - print("=" * 50) - - # Setup - project_root = Path(__file__).parent.parent - permission_manager = PermissionManager() - permission_manager.add_allowed_path(str(project_root)) - - # Count project files - python_files = list(project_root.rglob("*.py")) - total_size = sum(f.stat().st_size for f in python_files) - - print(f"Project: {project_root}") - print(f"Python files: {len(python_files)}") - print(f"Total size: {total_size / 1024 / 1024:.2f} MB") - print() - - # Mock context - class MockContext: - def __init__(self): - self.meta = {} - - # Test AST Analysis Performance - print("๐Ÿง  AST Analysis Performance") - print("-" * 30) - - analyzer = ASTAnalyzer() - analysis_times = [] - symbol_counts = [] - - # Test on a sample of files (first 10) - test_files = python_files[:10] - - for file_path in test_files: - start_time = time.time() - try: - file_ast = analyzer.analyze_file(str(file_path)) - analysis_time = time.time() - start_time - - analysis_times.append(analysis_time) - symbol_counts.append(len(file_ast.symbols) if file_ast else 0) - - print( - f" {file_path.name}: {analysis_time:.3f}s, {len(file_ast.symbols) if file_ast else 0} symbols" - ) - - except Exception as e: - print(f" {file_path.name}: FAILED - {e}") - - if analysis_times: - avg_time = sum(analysis_times) / len(analysis_times) - avg_symbols = sum(symbol_counts) / len(symbol_counts) - print( - f"\\nAverage: {avg_time:.3f}s per file, {avg_symbols:.1f} symbols per file" - ) - - print() - - # Test Search Performance - print("๐Ÿ” Search Performance Tests") - print("-" * 30) - - # Initialize tools (without vector search for simplicity) - unified_tool = SearchTool(permission_manager, None) - grep_tool = Grep(permission_manager) - ast_tool = ASTTool(permission_manager) - - # Mock the validation methods - unified_tool.validate_path = lambda x: type("obj", (object,), {"is_error": False})() - unified_tool.check_path_allowed = lambda x, y: asyncio.coroutine( - lambda: (True, None) - )() - unified_tool.check_path_exists = lambda x, y: asyncio.coroutine( - lambda: (True, None) - )() - - # Test queries - queries = [ - "SearchTool", - "def.*search", - "error.handling", - "import.*typing", - "class.*Tool", - ] - - for query in queries: - print(f"\\nQuery: '{query}'") - - # Test grep search - start_time = time.time() - try: - grep_result = await grep_tool.call( - MockContext(), pattern=query, path=str(project_root), include="*.py" - ) - grep_time = time.time() - start_time - grep_matches = ( - grep_result.count("\\n") - if grep_result and "Found" in grep_result - else 0 - ) - - print(f" Grep: {grep_time:.3f}s, ~{grep_matches} matches") - except Exception as e: - print(f" Grep: FAILED - {e}") - - # Test AST search - start_time = time.time() - try: - ast_result = await ast_tool.call( - MockContext(), - pattern=query, - path=str(project_root), - ignore_case=False, - line_number=True, - ) - ast_time = time.time() - start_time - ast_matches = ( - ast_result.count("\\n") - if ast_result and not ast_result.startswith("No matches") - else 0 - ) - - print(f" AST: {ast_time:.3f}s, ~{ast_matches} matches") - except Exception as e: - print(f" AST: FAILED - {e}") - - # Test search (without vector) - start_time = time.time() - try: - unified_result = await unified_tool.call( - MockContext(), - pattern=query, - path=str(project_root), - enable_vector=False, # Disable vector to avoid dependencies - max_results=20, - ) - unified_time = time.time() - start_time - unified_matches = ( - unified_result.count("Result ") if "Result " in unified_result else 0 - ) - - print(f" Unified: {unified_time:.3f}s, {unified_matches} results") - except Exception as e: - print(f" Unified: FAILED - {e}") - - print("\\n" + "=" * 50) - print("โœ… Simple benchmark completed!") - - -if __name__ == "__main__": - asyncio.run(simple_benchmark()) diff --git a/pkg/hanzo-mcp/tests/conftest.py b/pkg/hanzo-mcp/tests/conftest.py deleted file mode 100644 index 835f53754..000000000 --- a/pkg/hanzo-mcp/tests/conftest.py +++ /dev/null @@ -1,331 +0,0 @@ -"""Pytest configuration for the Hanzo AI project. - -This module provides shared fixtures and configuration for all tests. -Uses the test_utils module for DRY test infrastructure. -""" - -import os -import sys -import tempfile - -import pytest - -# pytest-asyncio is auto-discovered, no manual registration needed - -# Add tests directory to path so we can import test_utils -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -from test_utils import ( - FileSystemTestHelper, - TestContext, - TestDataGenerator, - TestEnvironment, - ToolTestHelper, - create_mock_ctx, - create_permission_manager, - create_test_server, -) - -# Set environment variables for testing -os.environ["TEST_MODE"] = "1" -os.environ["HANZO_MCP_FAST_TESTS"] = "1" # Enable fast test mode -os.environ["PYTEST_CURRENT_TEST"] = "1" # Mark as pytest run - - -# Configure pytest -def pytest_configure(config): - """Configure pytest.""" - # Register custom markers - config.addinivalue_line( - "markers", "requires_hanzo_agents: mark test as requiring hanzo-agents SDK" - ) - config.addinivalue_line( - "markers", "requires_memory_tools: mark test as requiring hanzo-memory package" - ) - config.addinivalue_line("markers", "slow: mark test as slow running") - config.addinivalue_line("markers", "integration: mark test as integration test") - - -# --- Basic Fixtures --- - - -@pytest.fixture -def temp_dir(): - """Create a temporary directory for testing.""" - with tempfile.TemporaryDirectory() as tmp_dir: - yield tmp_dir - - -@pytest.fixture -def test_env(): - """Create a test environment manager.""" - with TestEnvironment() as env: - yield env - - -# --- Context and Permission Fixtures --- - - -@pytest.fixture -def mcp_context(): - """Create a mock MCP context for testing.""" - return create_mock_ctx() - - -@pytest.fixture -def mock_ctx(mcp_context): - """Alias for mcp_context for backward compatibility.""" - return mcp_context - - -@pytest.fixture -def test_context(): - """Create a full test context helper.""" - return TestContext() - - -@pytest.fixture -def permission_manager(): - """Create a permission manager for testing.""" - return create_permission_manager(["/"]) # Allow all paths for testing - - -@pytest.fixture -def restricted_permission_manager(temp_dir): - """Create a permission manager restricted to temp directory.""" - return create_permission_manager([temp_dir]) - - -# --- Tool Testing Fixtures --- - - -@pytest.fixture -def tool_helper(): - """Get the tool test helper.""" - return ToolTestHelper - - -@pytest.fixture -def mock_server(): - """Create a mock MCP server for testing.""" - return create_test_server("test-server") - - -# --- File System Fixtures --- - - -@pytest.fixture -def fs_helper(): - """Get the file system test helper.""" - return FileSystemTestHelper - - -@pytest.fixture -def test_file(temp_dir): - """Create a test file in the temporary directory.""" - file_path = os.path.join(temp_dir, "test.txt") - with open(file_path, "w") as f: - f.write("This is a test file content.") - return file_path - - -@pytest.fixture -def test_notebook(temp_dir): - """Create a test notebook in the temporary directory.""" - notebook_path = os.path.join(temp_dir, "test.ipynb") - notebook_content = { - "cells": [ - { - "cell_type": "code", - "execution_count": None, - "metadata": {}, - "outputs": [], - "source": ["# Test cell 1\n", "print('Hello, world!')"], - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": ["## Markdown cell\n", "This is a test."], - }, - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3", - } - }, - "nbformat": 4, - "nbformat_minor": 4, - } - - import json - - with open(notebook_path, "w") as f: - json.dump(notebook_content, f) - - return notebook_path - - -# --- Project Structure Fixtures --- - - -@pytest.fixture -def test_data(): - """Get the test data generator.""" - return TestDataGenerator - - -@pytest.fixture -def project_dir(temp_dir, fs_helper, test_data): - """Create a simple Python project structure for testing.""" - fs_helper.create_test_files(temp_dir, test_data.python_project_files()) - return temp_dir - - -@pytest.fixture -def test_project_dir(project_dir): - """Alias for project_dir fixture.""" - return project_dir - - -@pytest.fixture -def js_project_dir(temp_dir, fs_helper, test_data): - """Create a simple JavaScript project structure for testing.""" - fs_helper.create_test_files(temp_dir, test_data.javascript_project_files()) - return temp_dir - - -# --- Tool-Specific Fixtures --- - - -@pytest.fixture -def command_executor(permission_manager): - """Create a command executor for testing.""" - from hanzo_tools.shell.bash_session_executor import BashSessionExecutor - - return BashSessionExecutor(permission_manager=permission_manager) - - -@pytest.fixture -def tool_context(mcp_context): - """Create a tool context for testing.""" - from hanzo_mcp.tools.common.context import ToolContext - - return ToolContext(mcp_context) - - -@pytest.fixture -def db_manager(permission_manager): - """Create a database manager for testing.""" - from hanzo_tools.database.database_manager import DatabaseManager - - return DatabaseManager(permission_manager) - - -# --- Mock Service Fixtures --- - - -@pytest.fixture -def mock_memory_service(): - """Create a mock memory service.""" - from test_utils import MockServiceHelper - - return MockServiceHelper.mock_memory_service() - - -@pytest.fixture -def mock_llm(): - """Create a mock llm module.""" - from test_utils import MockServiceHelper - - with pytest.mock.patch("hanzo_tools.agent.agent_tool.llm") as mock: - mock.completion = MockServiceHelper.mock_llm_completion() - yield mock - - -# --- Async Fixtures --- - - -@pytest.fixture -def async_helper(): - """Get the async test helper.""" - from test_utils import AsyncTestHelper - - return AsyncTestHelper - - -# --- Autouse Fixtures --- - - -@pytest.fixture(autouse=True) -def reset_environment(): - """Reset environment before each test.""" - # Save current environment - original_env = os.environ.copy() - - yield - - # Restore original environment - os.environ.clear() - os.environ.update(original_env) - - -@pytest.fixture(autouse=True, scope="session") -def mock_slow_operations(): - """Mock slow operations for faster test execution.""" - from unittest.mock import AsyncMock, patch - - # Mock subprocess for LSP tests - with patch("asyncio.create_subprocess_exec") as mock_subprocess: - mock_process = AsyncMock() - mock_process.returncode = 0 - mock_process.communicate = AsyncMock(return_value=(b"", b"")) - mock_subprocess.return_value = mock_process - yield - - -@pytest.fixture(autouse=True) -def cleanup_temp_files(): - """Clean up any temporary files after tests.""" - yield - - # Clean up any stray temp files - import glob - import shutil - - for pattern in ["/tmp/test_*", "/tmp/pytest-*"]: - for path in glob.glob(pattern): - try: - if os.path.isdir(path): - shutil.rmtree(path) - else: - os.unlink(path) - except Exception: - pass # Ignore cleanup errors - - -# --- Pytest Hooks --- - - -def pytest_collection_modifyitems(config, items): - """Modify test collection to add markers based on test names.""" - for item in items: - # Add integration marker to integration tests - if "integration" in item.nodeid: - item.add_marker(pytest.mark.integration) - - # Add slow marker to certain tests - slow_patterns = [ - "performance", - "stress", - "load", - "e2e_", - "swarm_", - "streaming", - "shell_features", - "test_batch_tool_edge_cases", - "test_memory_edge_cases", - ] - if any(pattern in item.nodeid for pattern in slow_patterns): - item.add_marker(pytest.mark.slow) diff --git a/pkg/hanzo-mcp/tests/data/lancedb/chat_sessions.lance/_transactions/0-89562987-e597-4a72-bb11-c37a9408f3f2.txn b/pkg/hanzo-mcp/tests/data/lancedb/chat_sessions.lance/_transactions/0-89562987-e597-4a72-bb11-c37a9408f3f2.txn deleted file mode 100644 index 01e485c60..000000000 --- a/pkg/hanzo-mcp/tests/data/lancedb/chat_sessions.lance/_transactions/0-89562987-e597-4a72-bb11-c37a9408f3f2.txn +++ /dev/null @@ -1,7 +0,0 @@ -$89562987-e597-4a72-bb11-c37a9408f3f2ฒู* -session_id *string8Zdefault)user_id *string8Zdefault, -project_id *string8Zdefault*metadata *string8Zdefault, -created_at *string8Zdefault, -updated_at *string8Zdefault"' -lance.auto_cleanup.older_than14days"! -lance.auto_cleanup.interval20 \ No newline at end of file diff --git a/pkg/hanzo-mcp/tests/data/lancedb/chat_sessions.lance/_versions/1.manifest b/pkg/hanzo-mcp/tests/data/lancedb/chat_sessions.lance/_versions/1.manifest deleted file mode 100644 index dde021e19..000000000 Binary files a/pkg/hanzo-mcp/tests/data/lancedb/chat_sessions.lance/_versions/1.manifest and /dev/null differ diff --git a/pkg/hanzo-mcp/tests/data/lancedb/knowledge_bases.lance/_transactions/0-b736a097-e5cc-49ed-aeb4-db4fc35a04d0.txn b/pkg/hanzo-mcp/tests/data/lancedb/knowledge_bases.lance/_transactions/0-b736a097-e5cc-49ed-aeb4-db4fc35a04d0.txn deleted file mode 100644 index f21c94862..000000000 --- a/pkg/hanzo-mcp/tests/data/lancedb/knowledge_bases.lance/_transactions/0-b736a097-e5cc-49ed-aeb4-db4fc35a04d0.txn +++ /dev/null @@ -1,6 +0,0 @@ -$b736a097-e5cc-49ed-aeb4-db4fc35a04d0ฒŒ1knowledge_base_id *string8Zdefault, -project_id *string8Zdefault&name *string8Zdefault- description *string8Zdefault*metadata *string8Zdefault, -created_at *string8Zdefault, -updated_at *string8Zdefault"' -lance.auto_cleanup.older_than14days"! -lance.auto_cleanup.interval20 \ No newline at end of file diff --git a/pkg/hanzo-mcp/tests/data/lancedb/knowledge_bases.lance/_versions/1.manifest b/pkg/hanzo-mcp/tests/data/lancedb/knowledge_bases.lance/_versions/1.manifest deleted file mode 100644 index a83b3bdfd..000000000 Binary files a/pkg/hanzo-mcp/tests/data/lancedb/knowledge_bases.lance/_versions/1.manifest and /dev/null differ diff --git a/pkg/hanzo-mcp/tests/data/lancedb/projects.lance/_transactions/0-6d4dfb6d-7d5f-4984-8b7d-3ec67f6a237e.txn b/pkg/hanzo-mcp/tests/data/lancedb/projects.lance/_transactions/0-6d4dfb6d-7d5f-4984-8b7d-3ec67f6a237e.txn deleted file mode 100644 index 02d647bc6..000000000 --- a/pkg/hanzo-mcp/tests/data/lancedb/projects.lance/_transactions/0-6d4dfb6d-7d5f-4984-8b7d-3ec67f6a237e.txn +++ /dev/null @@ -1,6 +0,0 @@ -$6d4dfb6d-7d5f-4984-8b7d-3ec67f6a237eฒ‚* -project_id *string8Zdefault)user_id *string8Zdefault&name *string8Zdefault- description *string8Zdefault*metadata *string8Zdefault, -created_at *string8Zdefault, -updated_at *string8Zdefault"' -lance.auto_cleanup.older_than14days"! -lance.auto_cleanup.interval20 \ No newline at end of file diff --git a/pkg/hanzo-mcp/tests/data/lancedb/projects.lance/_versions/1.manifest b/pkg/hanzo-mcp/tests/data/lancedb/projects.lance/_versions/1.manifest deleted file mode 100644 index cd3537929..000000000 Binary files a/pkg/hanzo-mcp/tests/data/lancedb/projects.lance/_versions/1.manifest and /dev/null differ diff --git a/pkg/hanzo-mcp/tests/e2e/__init__.py b/pkg/hanzo-mcp/tests/e2e/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-mcp/tests/e2e/test_llm_integration.py b/pkg/hanzo-mcp/tests/e2e/test_llm_integration.py deleted file mode 100644 index e385997a7..000000000 --- a/pkg/hanzo-mcp/tests/e2e/test_llm_integration.py +++ /dev/null @@ -1,48 +0,0 @@ -"""E2E integration tests for LLM providers. - -These tests require real API keys and make actual API calls. -Only run in CI with proper secrets configured. -""" - -import os - -import llm -import pytest - - -@pytest.mark.skipif( - not os.environ.get("OPENAI_API_KEY"), - reason="OPENAI_API_KEY environment variable not set", -) -@pytest.mark.asyncio -async def test_llm_openai_provider_integration(): - """Integration test: LLM with real OpenAI provider.""" - messages = [{"role": "user", "content": "Hello, how are you?"}] - - try: - response = llm.completion( - model="openai/gpt-3.5-turbo", - messages=messages, - ) - assert response.choices[0].message.content is not None - except Exception as e: - pytest.skip(f"OpenAI API connection failed: {type(e).__name__} - {str(e)}") - - -@pytest.mark.skipif( - not os.environ.get("ANTHROPIC_API_KEY"), - reason="ANTHROPIC_API_KEY environment variable not set", -) -@pytest.mark.asyncio -async def test_llm_anthropic_provider_integration(): - """Integration test: LLM with real Anthropic provider.""" - messages = [{"role": "user", "content": "Hello, how are you?"}] - - try: - response = llm.completion( - model="anthropic/claude-3-haiku-20240307", - messages=messages, - ) - assert response.choices[0].message.content is not None - except Exception as e: - pytest.skip(f"Anthropic API connection failed: {type(e).__name__} - {str(e)}") diff --git a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_000.py b/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_000.py deleted file mode 100644 index 998802c00..000000000 --- a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_000.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Module 000 - Auto-generated for benchmarking.""" - -import logging -from typing import Any, Dict - -logger = logging.getLogger(__name__) - - -def get_default_config() -> Dict[str, Any]: - """Get default configuration for module 000.""" - return { - "module_id": 0, - "uppercase": True, - "trim": True, - "validate": True, - } - - -def validate_record(record: Any) -> bool: - """Validate a single record.""" - try: - return record is not None and str(record).strip() != "" - except Exception: - return False - - -def apply_transformations(record: Any, params: Dict[str, Any]) -> Any: - """Apply transformations to a record.""" - if params.get("stringify", True): - record = str(record) - - if params.get("normalize", True): - record = record.strip().lower() - - return record - - -def process_data_0(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_0: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_0: {e}") - raise - - -def process_data_1(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed - - -def process_data_2(dataset, parameters): - """Standard data processing function.""" - results = [] - - for record in dataset: - # Validate record - if not validate_record(record): - continue - - # Process record - processed = apply_transformations(record, parameters) - results.append(processed) - - return results - - -def process_data_3(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_3: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_3: {e}") - raise - - -def process_data_4(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed diff --git a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_001.py b/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_001.py deleted file mode 100644 index 5a28b9b8c..000000000 --- a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_001.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Module 001 - Auto-generated for benchmarking.""" - -import logging -from typing import Any, Dict - -logger = logging.getLogger(__name__) - - -def get_default_config() -> Dict[str, Any]: - """Get default configuration for module 001.""" - return { - "module_id": 1, - "uppercase": True, - "trim": True, - "validate": True, - } - - -def validate_record(record: Any) -> bool: - """Validate a single record.""" - try: - return record is not None and str(record).strip() != "" - except Exception: - return False - - -def apply_transformations(record: Any, params: Dict[str, Any]) -> Any: - """Apply transformations to a record.""" - if params.get("stringify", True): - record = str(record) - - if params.get("normalize", True): - record = record.strip().lower() - - return record - - -def process_data_0(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_0: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_0: {e}") - raise - - -def process_data_1(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed - - -def process_data_2(dataset, parameters): - """Standard data processing function.""" - results = [] - - for record in dataset: - # Validate record - if not validate_record(record): - continue - - # Process record - processed = apply_transformations(record, parameters) - results.append(processed) - - return results - - -def process_data_3(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_3: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_3: {e}") - raise - - -def process_data_4(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed diff --git a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_002.py b/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_002.py deleted file mode 100644 index c0bb350f8..000000000 --- a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_002.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Module 002 - Auto-generated for benchmarking.""" - -import logging -from typing import Any, Dict - -logger = logging.getLogger(__name__) - - -def get_default_config() -> Dict[str, Any]: - """Get default configuration for module 002.""" - return { - "module_id": 2, - "uppercase": True, - "trim": True, - "validate": True, - } - - -def validate_record(record: Any) -> bool: - """Validate a single record.""" - try: - return record is not None and str(record).strip() != "" - except Exception: - return False - - -def apply_transformations(record: Any, params: Dict[str, Any]) -> Any: - """Apply transformations to a record.""" - if params.get("stringify", True): - record = str(record) - - if params.get("normalize", True): - record = record.strip().lower() - - return record - - -def process_data_0(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_0: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_0: {e}") - raise - - -def process_data_1(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed - - -def process_data_2(dataset, parameters): - """Standard data processing function.""" - results = [] - - for record in dataset: - # Validate record - if not validate_record(record): - continue - - # Process record - processed = apply_transformations(record, parameters) - results.append(processed) - - return results - - -def process_data_3(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_3: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_3: {e}") - raise - - -def process_data_4(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed diff --git a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_003.py b/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_003.py deleted file mode 100644 index 15f5ab367..000000000 --- a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_003.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Module 003 - Auto-generated for benchmarking.""" - -import logging -from typing import Any, Dict - -logger = logging.getLogger(__name__) - - -def get_default_config() -> Dict[str, Any]: - """Get default configuration for module 003.""" - return { - "module_id": 3, - "uppercase": True, - "trim": True, - "validate": True, - } - - -def validate_record(record: Any) -> bool: - """Validate a single record.""" - try: - return record is not None and str(record).strip() != "" - except Exception: - return False - - -def apply_transformations(record: Any, params: Dict[str, Any]) -> Any: - """Apply transformations to a record.""" - if params.get("stringify", True): - record = str(record) - - if params.get("normalize", True): - record = record.strip().lower() - - return record - - -def process_data_0(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_0: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_0: {e}") - raise - - -def process_data_1(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed - - -def process_data_2(dataset, parameters): - """Standard data processing function.""" - results = [] - - for record in dataset: - # Validate record - if not validate_record(record): - continue - - # Process record - processed = apply_transformations(record, parameters) - results.append(processed) - - return results - - -def process_data_3(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_3: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_3: {e}") - raise - - -def process_data_4(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed diff --git a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_004.py b/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_004.py deleted file mode 100644 index 99853a0e1..000000000 --- a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_004.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Module 004 - Auto-generated for benchmarking.""" - -import logging -from typing import Any, Dict - -logger = logging.getLogger(__name__) - - -def get_default_config() -> Dict[str, Any]: - """Get default configuration for module 004.""" - return { - "module_id": 4, - "uppercase": True, - "trim": True, - "validate": True, - } - - -def validate_record(record: Any) -> bool: - """Validate a single record.""" - try: - return record is not None and str(record).strip() != "" - except Exception: - return False - - -def apply_transformations(record: Any, params: Dict[str, Any]) -> Any: - """Apply transformations to a record.""" - if params.get("stringify", True): - record = str(record) - - if params.get("normalize", True): - record = record.strip().lower() - - return record - - -def process_data_0(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_0: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_0: {e}") - raise - - -def process_data_1(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed - - -def process_data_2(dataset, parameters): - """Standard data processing function.""" - results = [] - - for record in dataset: - # Validate record - if not validate_record(record): - continue - - # Process record - processed = apply_transformations(record, parameters) - results.append(processed) - - return results - - -def process_data_3(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_3: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_3: {e}") - raise - - -def process_data_4(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed diff --git a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_005.py b/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_005.py deleted file mode 100644 index eb23275b4..000000000 --- a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_005.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Module 005 - Auto-generated for benchmarking.""" - -import logging -from typing import Any, Dict - -logger = logging.getLogger(__name__) - - -def get_default_config() -> Dict[str, Any]: - """Get default configuration for module 005.""" - return { - "module_id": 5, - "uppercase": True, - "trim": True, - "validate": True, - } - - -def validate_record(record: Any) -> bool: - """Validate a single record.""" - try: - return record is not None and str(record).strip() != "" - except Exception: - return False - - -def apply_transformations(record: Any, params: Dict[str, Any]) -> Any: - """Apply transformations to a record.""" - if params.get("stringify", True): - record = str(record) - - if params.get("normalize", True): - record = record.strip().lower() - - return record - - -def process_data_0(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_0: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_0: {e}") - raise - - -def process_data_1(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed - - -def process_data_2(dataset, parameters): - """Standard data processing function.""" - results = [] - - for record in dataset: - # Validate record - if not validate_record(record): - continue - - # Process record - processed = apply_transformations(record, parameters) - results.append(processed) - - return results - - -def process_data_3(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_3: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_3: {e}") - raise - - -def process_data_4(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed diff --git a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_006.py b/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_006.py deleted file mode 100644 index f600dc4fb..000000000 --- a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_006.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Module 006 - Auto-generated for benchmarking.""" - -import logging -from typing import Any, Dict - -logger = logging.getLogger(__name__) - - -def get_default_config() -> Dict[str, Any]: - """Get default configuration for module 006.""" - return { - "module_id": 6, - "uppercase": True, - "trim": True, - "validate": True, - } - - -def validate_record(record: Any) -> bool: - """Validate a single record.""" - try: - return record is not None and str(record).strip() != "" - except Exception: - return False - - -def apply_transformations(record: Any, params: Dict[str, Any]) -> Any: - """Apply transformations to a record.""" - if params.get("stringify", True): - record = str(record) - - if params.get("normalize", True): - record = record.strip().lower() - - return record - - -def process_data_0(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_0: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_0: {e}") - raise - - -def process_data_1(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed - - -def process_data_2(dataset, parameters): - """Standard data processing function.""" - results = [] - - for record in dataset: - # Validate record - if not validate_record(record): - continue - - # Process record - processed = apply_transformations(record, parameters) - results.append(processed) - - return results - - -def process_data_3(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_3: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_3: {e}") - raise - - -def process_data_4(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed diff --git a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_007.py b/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_007.py deleted file mode 100644 index a294cbe0f..000000000 --- a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_007.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Module 007 - Auto-generated for benchmarking.""" - -import logging -from typing import Any, Dict - -logger = logging.getLogger(__name__) - - -def get_default_config() -> Dict[str, Any]: - """Get default configuration for module 007.""" - return { - "module_id": 7, - "uppercase": True, - "trim": True, - "validate": True, - } - - -def validate_record(record: Any) -> bool: - """Validate a single record.""" - try: - return record is not None and str(record).strip() != "" - except Exception: - return False - - -def apply_transformations(record: Any, params: Dict[str, Any]) -> Any: - """Apply transformations to a record.""" - if params.get("stringify", True): - record = str(record) - - if params.get("normalize", True): - record = record.strip().lower() - - return record - - -def process_data_0(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_0: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_0: {e}") - raise - - -def process_data_1(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed - - -def process_data_2(dataset, parameters): - """Standard data processing function.""" - results = [] - - for record in dataset: - # Validate record - if not validate_record(record): - continue - - # Process record - processed = apply_transformations(record, parameters) - results.append(processed) - - return results - - -def process_data_3(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_3: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_3: {e}") - raise - - -def process_data_4(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed diff --git a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_008.py b/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_008.py deleted file mode 100644 index 15522b0b6..000000000 --- a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_008.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Module 008 - Auto-generated for benchmarking.""" - -import logging -from typing import Any, Dict - -logger = logging.getLogger(__name__) - - -def get_default_config() -> Dict[str, Any]: - """Get default configuration for module 008.""" - return { - "module_id": 8, - "uppercase": True, - "trim": True, - "validate": True, - } - - -def validate_record(record: Any) -> bool: - """Validate a single record.""" - try: - return record is not None and str(record).strip() != "" - except Exception: - return False - - -def apply_transformations(record: Any, params: Dict[str, Any]) -> Any: - """Apply transformations to a record.""" - if params.get("stringify", True): - record = str(record) - - if params.get("normalize", True): - record = record.strip().lower() - - return record - - -def process_data_0(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_0: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_0: {e}") - raise - - -def process_data_1(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed - - -def process_data_2(dataset, parameters): - """Standard data processing function.""" - results = [] - - for record in dataset: - # Validate record - if not validate_record(record): - continue - - # Process record - processed = apply_transformations(record, parameters) - results.append(processed) - - return results - - -def process_data_3(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_3: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_3: {e}") - raise - - -def process_data_4(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed diff --git a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_009.py b/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_009.py deleted file mode 100644 index b472258bb..000000000 --- a/pkg/hanzo-mcp/tests/fixtures/test_project_small/module_009.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Module 009 - Auto-generated for benchmarking.""" - -import logging -from typing import Any, Dict - -logger = logging.getLogger(__name__) - - -def get_default_config() -> Dict[str, Any]: - """Get default configuration for module 009.""" - return { - "module_id": 9, - "uppercase": True, - "trim": True, - "validate": True, - } - - -def validate_record(record: Any) -> bool: - """Validate a single record.""" - try: - return record is not None and str(record).strip() != "" - except Exception: - return False - - -def apply_transformations(record: Any, params: Dict[str, Any]) -> Any: - """Apply transformations to a record.""" - if params.get("stringify", True): - record = str(record) - - if params.get("normalize", True): - record = record.strip().lower() - - return record - - -def process_data_0(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_0: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_0: {e}") - raise - - -def process_data_1(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed - - -def process_data_2(dataset, parameters): - """Standard data processing function.""" - results = [] - - for record in dataset: - # Validate record - if not validate_record(record): - continue - - # Process record - processed = apply_transformations(record, parameters) - results.append(processed) - - return results - - -def process_data_3(data, options=None): - """Process data with comprehensive error handling.""" - if options is None: - options = {} - - try: - result = [] - for item in data: - if not item: - raise ValueError("Empty item encountered") - - processed = item.strip().upper() - result.append(processed) - - return result - except ValueError as e: - logger.error(f"Validation error in process_data_3: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in process_data_3: {e}") - raise - - -def process_data_4(input_data, config=None): - """Utility function for data transformation.""" - config = config or get_default_config() - - transformed = [] - for item in input_data: - # Apply transformation rules - if config.get("uppercase", True): - item = item.upper() - - if config.get("trim", True): - item = item.strip() - - transformed.append(item) - - return transformed diff --git a/pkg/hanzo-mcp/tests/manual/test_filters_manual.py b/pkg/hanzo-mcp/tests/manual/test_filters_manual.py deleted file mode 100644 index 4cf921a75..000000000 --- a/pkg/hanzo-mcp/tests/manual/test_filters_manual.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python3 -"""Test find tool filters.""" - -import asyncio -import os -import sys -import tempfile -from pathlib import Path - -sys.path.insert(0, str(Path(__file__).parent)) - -from hanzo_mcp.tools.search import create_find_tool - - -async def test_filters(): - """Test find tool with filters.""" - find_tool = create_find_tool() - - # Create test directory structure - with tempfile.TemporaryDirectory() as tmpdir: - print(f"Test directory: {tmpdir}") - - # Create some test files - (Path(tmpdir) / "small.txt").write_text("small file") - (Path(tmpdir) / "large.txt").write_text("x" * 10000) # 10KB - (Path(tmpdir) / "test_file.py").write_text("print('test')") - (Path(tmpdir) / "old_file.txt").write_text("old") - - # Make old_file older - old_time = os.path.getmtime(Path(tmpdir) / "old_file.txt") - 86400 # 1 day ago - os.utime(Path(tmpdir) / "old_file.txt", (old_time, old_time)) - - print("\nTest 1: Size filter (min_size=5KB)") - result = await find_tool.run(pattern="*.txt", path=tmpdir, min_size="5KB") - - if result.data: - results = result.data.get("results", []) - print(f"Found {len(results)} files:") - for r in results: - print(f" - {r['name']} ({r['size']} bytes)") - else: - print("No results") - - print("\nTest 2: Time filter (modified_after='12 hours ago')") - result = await find_tool.run( - pattern="*.txt", path=tmpdir, modified_after="12 hours ago" - ) - - if result.data: - results = result.data.get("results", []) - print(f"Found {len(results)} files:") - for r in results: - print(f" - {r['name']} (modified: {r.get('modified', 'unknown')})") - else: - print("No results") - - -if __name__ == "__main__": - asyncio.run(test_filters()) diff --git a/pkg/hanzo-mcp/tests/manual/test_size_parsing_manual.py b/pkg/hanzo-mcp/tests/manual/test_size_parsing_manual.py deleted file mode 100644 index 334f62f77..000000000 --- a/pkg/hanzo-mcp/tests/manual/test_size_parsing_manual.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 -"""Test size parsing.""" - -import sys -from pathlib import Path - -sys.path.insert(0, str(Path(__file__).parent)) - -from hanzo_mcp.tools.search.find_tool import FindTool - - -def test_parse_size(): - """Test size parsing.""" - tool = FindTool() - - test_cases = [ - ("5KB", 5 * 1024), - ("10KB", 10 * 1024), - ("1MB", 1024 * 1024), - ("100", 100), - ("5K", 5 * 1024), - ] - - for size_str, expected in test_cases: - result = tool._parse_size(size_str) - print( - f"'{size_str}' -> {result} bytes (expected: {expected}, match: {result == expected})" - ) - - -if __name__ == "__main__": - test_parse_size() diff --git a/pkg/hanzo-mcp/tests/test_agent/.gitignore b/pkg/hanzo-mcp/tests/test_agent/.gitignore deleted file mode 100644 index d996b060a..000000000 --- a/pkg/hanzo-mcp/tests/test_agent/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Exclude OpenAI API tests that require API key -test_openai_request.py - -# Exclude script that contains API key -run_openai_test.sh diff --git a/pkg/hanzo-mcp/tests/test_agent/__init__.py b/pkg/hanzo-mcp/tests/test_agent/__init__.py deleted file mode 100644 index c7a3f50ec..000000000 --- a/pkg/hanzo-mcp/tests/test_agent/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Agent tools test package.""" diff --git a/pkg/hanzo-mcp/tests/test_agent/openai_api_check.py b/pkg/hanzo-mcp/tests/test_agent/openai_api_check.py deleted file mode 100644 index 1c4dd6628..000000000 --- a/pkg/hanzo-mcp/tests/test_agent/openai_api_check.py +++ /dev/null @@ -1,60 +0,0 @@ -"""Simple script to test OpenAI API connectivity.""" - -import sys - -from openai import OpenAI - -# Use the same API key from the script -api_key = "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" - - -def check_api_connection(): - """Test a simple OpenAI API connection.""" - print("Testing OpenAI API connection...") - - try: - # Initialize the client with the API key - client = OpenAI(api_key=api_key) - - # Make a simple request - response = client.chat.completions.create( - model="gpt-3.5-turbo", # Use a simpler model - messages=[ - {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": "Say hello!"}, - ], - max_tokens=10, # Request minimal tokens for a quick test - ) - - # If we get here, the connection worked - print("SUCCESS: API connection established!") - print(f"Response: {response.choices[0].message.content}") - return True - - except Exception as e: - # Print detailed error information - print(f"ERROR: {type(e).__name__}: {str(e)}") - - # Check for common error types and provide more helpful messages - if "invalid_api_key" in str(e).lower() or "authentication" in str(e).lower(): - print("\nPossible cause: The API key may be invalid or expired.") - print("Solution: Obtain a new API key from the OpenAI dashboard.") - - elif "insufficient_quota" in str(e).lower(): - print("\nPossible cause: Your OpenAI account may be out of credits.") - print( - "Solution: Check your usage and billing information in the OpenAI dashboard." - ) - - elif "connection" in str(e).lower(): - print("\nPossible cause: Network connectivity issue.") - print("Solution: Check your internet connection and firewall settings.") - print(" Some networks block OpenAI API calls.") - print(" Try using a different network or VPN.") - - return False - - -if __name__ == "__main__": - success = check_api_connection() - sys.exit(0 if success else 1) diff --git a/pkg/hanzo-mcp/tests/test_agent/test_agent_tool.py b/pkg/hanzo-mcp/tests/test_agent/test_agent_tool.py deleted file mode 100644 index 30dc7dc2c..000000000 --- a/pkg/hanzo-mcp/tests/test_agent/test_agent_tool.py +++ /dev/null @@ -1,360 +0,0 @@ -"""Tests for the agent tool implementation.""" - -import os -from unittest.mock import AsyncMock, MagicMock, patch - -import pytest -from hanzo_tools.agent.agent_tool import AgentTool - -from hanzo_mcp.tools.common.base import BaseTool -from hanzo_mcp.tools.common.permissions import PermissionManager - - -class TestAgentTool: - """Test cases for the AgentTool.""" - - @pytest.fixture - def permission_manager(self): - """Create a test permission manager.""" - return MagicMock(spec=PermissionManager) - - @pytest.fixture - def mcp_context(self): - """Create a test MCP context.""" - return MagicMock() - - @pytest.fixture - def agent_tool(self, permission_manager): - """Create a test agent tool.""" - # Set environment variable for test - os.environ["OPENAI_API_KEY"] = "test_key" - return AgentTool(permission_manager) - - @pytest.fixture - def agent_tool_with_params(self, permission_manager): - """Create a test agent tool with custom parameters.""" - return AgentTool( - permission_manager=permission_manager, - model="anthropic/claude-3-sonnet", - api_key="test_anthropic_key", - max_tokens=2000, - max_iterations=40, - max_tool_uses=150, - ) - - @pytest.fixture - def mock_tools(self): - """Create a list of mock tools.""" - tools = [] - for name in ["read_files", "search_content", "tree"]: - tool = MagicMock(spec=BaseTool) - tool.name = name - tool.description = f"Description for {name}" - tool.parameters = {"properties": {}, "type": "object"} - tool.required = [] - tools.append(tool) - return tools - - def test_initialization(self, tool_helper, agent_tool): - """Test agent tool initialization.""" - assert agent_tool.name == "agent" - assert "agent" in agent_tool.description.lower() - assert ( - hasattr(agent_tool, "model_override") and agent_tool.model_override is None - ) - assert ( - hasattr(agent_tool, "api_key_override") - and agent_tool.api_key_override is None - ) - assert ( - hasattr(agent_tool, "max_tokens_override") - and agent_tool.max_tokens_override is None - ) - assert hasattr(agent_tool, "max_iterations") and agent_tool.max_iterations == 10 - assert hasattr(agent_tool, "max_tool_uses") and agent_tool.max_tool_uses == 30 - - def test_initialization_with_params(self, tool_helper, agent_tool_with_params): - """Test agent tool initialization with custom parameters.""" - assert agent_tool_with_params.name == "agent" - assert ( - hasattr(agent_tool_with_params, "model_override") - and agent_tool_with_params.model_override == "anthropic/claude-3-sonnet" - ) - assert ( - hasattr(agent_tool_with_params, "api_key_override") - and agent_tool_with_params.api_key_override == "test_anthropic_key" - ) - assert ( - hasattr(agent_tool_with_params, "max_tokens_override") - and agent_tool_with_params.max_tokens_override == 2000 - ) - assert ( - hasattr(agent_tool_with_params, "max_iterations") - and agent_tool_with_params.max_iterations == 40 - ) - assert ( - hasattr(agent_tool_with_params, "max_tool_uses") - and agent_tool_with_params.max_tool_uses == 150 - ) - - # Parameters are not exposed directly in the new interface - # def test_parameters(self, tool_helper, agent_tool): - # """Test agent tool parameters.""" - # # BaseTool doesn't expose parameters property - # pass - - def test_model_and_api_key_override(self, tool_helper, permission_manager): - """Test API key and model override functionality.""" - # Test with antropic model and API key - agent_tool = AgentTool( - permission_manager=permission_manager, - model="anthropic/claude-3-sonnet", - api_key="test_anthropic_key", - ) - - assert agent_tool.model_override == "anthropic/claude-3-sonnet" - assert agent_tool.api_key_override == "test_anthropic_key" - - # Test with openai model and API key - agent_tool = AgentTool( - permission_manager=permission_manager, - model="openai/gpt-4o", - api_key="test_openai_key", - ) - - assert agent_tool.model_override == "openai/gpt-4o" - assert agent_tool.api_key_override == "test_openai_key" - - # Test with no model or API key - agent_tool = AgentTool( - permission_manager=permission_manager, - ) - - assert agent_tool.model_override is None - assert agent_tool.api_key_override is None - - @pytest.mark.asyncio - async def test_call_no_prompt(self, tool_helper, agent_tool, mcp_context): - """Test agent tool call with no prompt.""" - # Mock the tool context - tool_ctx = MagicMock() - tool_ctx.error = AsyncMock() - tool_ctx.info = AsyncMock() - tool_ctx.set_tool_info = AsyncMock() - - with patch( - "hanzo_tools.agent.agent_tool.create_tool_context", - return_value=tool_ctx, - ): - result = await agent_tool.call(ctx=mcp_context) - - tool_helper.assert_in_result("Error", result) - tool_helper.assert_in_result("prompt must be provided", result) - tool_ctx.error.assert_called_once() - - @pytest.mark.asyncio - async def test_call_with_llm_error(self, tool_helper, agent_tool, mcp_context): - """Test agent tool call when llm raises an error.""" - # Mock the tool context - tool_ctx = MagicMock() - tool_ctx.error = AsyncMock() - tool_ctx.info = AsyncMock() - tool_ctx.set_tool_info = AsyncMock() - tool_ctx.get_tools = AsyncMock(return_value=[]) - - # Mock to raise an error - with patch( - "hanzo_tools.agent.agent_tool.create_tool_context", - return_value=tool_ctx, - ): - # Update the test to use a list instead of a string - result = await agent_tool.call(ctx=mcp_context, prompts=["Test prompt"]) - - # We're just making sure an error is returned, the actual error message may vary in tests - tool_helper.assert_in_result("Error", result) - tool_ctx.error.assert_called() - - @pytest.mark.asyncio - async def test_call_with_valid_prompt_string( - self, tool_helper, agent_tool, mcp_context, mock_tools - ): - """Test agent tool call with valid prompt as string - without hanzo-agents SDK.""" - # Mock the tool context - tool_ctx = MagicMock() - tool_ctx.set_tool_info = AsyncMock() - tool_ctx.info = AsyncMock() - tool_ctx.error = AsyncMock() - tool_ctx.mcp_context = mcp_context - - # Mock HANZO_AGENTS_AVAILABLE = False to test fallback behavior - with patch( - "hanzo_tools.agent.agent_tool.HANZO_AGENTS_AVAILABLE", - False, - ): - with patch( - "hanzo_tools.agent.agent_tool.create_tool_context", - return_value=tool_ctx, - ): - # Update the test to use a list instead of a string - result = await agent_tool.call( - ctx=mcp_context, prompts=["Test prompt /home/test/path"] - ) - - tool_helper.assert_in_result("Error", result) - tool_helper.assert_in_result("hanzo-agents SDK is required", result) - tool_ctx.error.assert_called() - - @pytest.mark.asyncio - async def test_call_with_multiple_prompts( - self, tool_helper, agent_tool, mcp_context, mock_tools - ): - """Test agent tool call with multiple prompts - without hanzo-agents SDK.""" - # Mock the tool context - tool_ctx = MagicMock() - tool_ctx.set_tool_info = AsyncMock() - tool_ctx.info = AsyncMock() - tool_ctx.error = AsyncMock() - tool_ctx.mcp_context = mcp_context - - # Create test prompts - test_prompts = [ - "Task 1 /home/test/path1", - "Task 2 /home/test/path2", - "Task 3 /home/test/path3", - ] - - # Mock HANZO_AGENTS_AVAILABLE = False to test fallback behavior - with patch( - "hanzo_tools.agent.agent_tool.HANZO_AGENTS_AVAILABLE", - False, - ): - with patch( - "hanzo_tools.agent.agent_tool.create_tool_context", - return_value=tool_ctx, - ): - result = await agent_tool.call(ctx=mcp_context, prompts=test_prompts) - - tool_helper.assert_in_result("Error", result) - tool_helper.assert_in_result("hanzo-agents SDK is required", result) - tool_ctx.error.assert_called() - - @pytest.mark.asyncio - async def test_call_with_empty_prompt_list( - self, tool_helper, agent_tool, mcp_context - ): - """Test agent tool call with an empty prompt list.""" - # Mock the tool context - tool_ctx = MagicMock() - tool_ctx.set_tool_info = AsyncMock() - tool_ctx.info = AsyncMock() - tool_ctx.error = AsyncMock() - - with patch( - "hanzo_tools.agent.agent_tool.create_tool_context", - return_value=tool_ctx, - ): - # Test with empty list - result = await agent_tool.call(ctx=mcp_context, prompts=[]) - - tool_helper.assert_in_result("Error", result) - tool_helper.assert_in_result("At least one prompt must be provided", result) - tool_ctx.error.assert_called() - - @pytest.mark.asyncio - async def test_call_with_invalid_type(self, tool_helper, agent_tool, mcp_context): - """Test agent tool call with an invalid parameter type.""" - # Mock the tool context - tool_ctx = MagicMock() - tool_ctx.set_tool_info = AsyncMock() - tool_ctx.info = AsyncMock() - tool_ctx.error = AsyncMock() - - with patch( - "hanzo_tools.agent.agent_tool.create_tool_context", - return_value=tool_ctx, - ): - # Test with invalid type (number) - result = await agent_tool.call(ctx=mcp_context, prompts=123) - - # Without hanzo-agents SDK, the tool returns an error - tool_helper.assert_in_result("Error", result) - # The error could be about invalid type or SDK not available - tool_ctx.error.assert_called() - - @pytest.mark.asyncio - async def test_get_agent_class_default_model( - self, tool_helper, agent_tool, mcp_context - ): - """Test _get_agent_class returns appropriate agent class for default model.""" - agent_class = agent_tool._get_agent_class(None, mcp_context) - - # Should return a dynamically created class based on MCPAgent - assert agent_class is not None - assert "DynamicMCPAgent" in agent_class.__name__ - - @pytest.mark.asyncio - async def test_get_agent_class_with_custom_model( - self, tool_helper, agent_tool, mcp_context - ): - """Test _get_agent_class with a custom model string.""" - agent_class = agent_tool._get_agent_class("model://openai/gpt-4", mcp_context) - - # Should return a dynamically created class - assert agent_class is not None - assert agent_class.model == "model://openai/gpt-4" - - @pytest.mark.asyncio - async def test_available_tools_initialized(self, tool_helper, agent_tool): - """Test that available_tools is properly initialized.""" - assert hasattr(agent_tool, "available_tools") - assert isinstance(agent_tool.available_tools, list) - assert len(agent_tool.available_tools) > 0 - - # Check that essential tools are present - tool_names = [t.name for t in agent_tool.available_tools] - assert "edit" in tool_names - assert "multi_edit" in tool_names - - @pytest.mark.asyncio - async def test_mcp_agent_state_serialization(self, tool_helper): - """Test MCPAgentState to_dict and from_dict methods.""" - from hanzo_tools.agent.agent_tool import MCPAgentState - - # Create state - state = MCPAgentState( - prompts=["Task 1 /path/to/file", "Task 2 /another/path"], - context={"key": "value"}, - ) - state.current_prompt_index = 1 - state.results = ["Result 1"] - - # Serialize - state_dict = state.to_dict() - assert state_dict["prompts"] == ["Task 1 /path/to/file", "Task 2 /another/path"] - assert state_dict["context"] == {"key": "value"} - assert state_dict["current_prompt_index"] == 1 - assert state_dict["results"] == ["Result 1"] - - # Deserialize - restored_state = MCPAgentState.from_dict(state_dict) - assert restored_state.prompts == state.prompts - assert restored_state.context == state.context - assert restored_state.current_prompt_index == state.current_prompt_index - assert restored_state.results == state.results - - @pytest.mark.asyncio - async def test_mcp_tool_adapter(self, tool_helper, mcp_context, mock_tools): - """Test MCPToolAdapter wraps MCP tools correctly.""" - from hanzo_tools.agent.agent_tool import MCPToolAdapter - - # Create adapter - mock_tool = mock_tools[0] - adapter = MCPToolAdapter(mock_tool, mcp_context) - - # Check properties - assert adapter.name == mock_tool.name - assert adapter.description == mock_tool.description - - # Check handle method exists (required by Tool ABC) - assert hasattr(adapter, "handle") - assert callable(adapter.handle) diff --git a/pkg/hanzo-mcp/tests/test_agent/test_llm_providers.py b/pkg/hanzo-mcp/tests/test_agent/test_llm_providers.py deleted file mode 100644 index fa88878da..000000000 --- a/pkg/hanzo-mcp/tests/test_agent/test_llm_providers.py +++ /dev/null @@ -1,136 +0,0 @@ -"""Test LLM with different providers.""" - -import llm -import pytest -from hanzo_tools.agent.tool_adapter import convert_tools_to_openai_functions - -from hanzo_mcp.tools.common.base import BaseTool - - -class EchoTool(BaseTool): - """A simple tool that echoes back the input.""" - - @property - def name(self) -> str: - """Get the tool name.""" - return "echo" - - @property - def description(self) -> str: - """Get the tool description.""" - return "Echo back the input message." - - @property - def parameters(self) -> dict: - """Get the parameter specifications for the tool.""" - return { - "properties": { - "message": { - "type": "string", - "description": "Message to echo back", - }, - }, - "required": ["message"], - "type": "object", - } - - @property - def required(self) -> list[str]: - """Get the list of required parameter names.""" - return ["message"] - - def register(self, ctx): - """Register the tool with the context.""" - # This is a required abstract method from BaseTool - pass - - async def call(self, ctx, **params): - """Execute the tool with the given parameters.""" - message = params.get("message", "") - return f"Echo: {message}" - - -@pytest.fixture -def echo_tool(): - """Fixture for the EchoTool.""" - return EchoTool() - - -def test_convert_echo_tool_to_openai_functions(echo_tool): - """Test convert_tools_to_openai_functions with echo_tool.""" - openai_functions = convert_tools_to_openai_functions([echo_tool]) - - assert len(openai_functions) == 1 - assert openai_functions[0]["type"] == "function" - assert openai_functions[0]["function"]["name"] == "echo" - assert ( - openai_functions[0]["function"]["description"] == "Echo back the input message." - ) - assert "parameters" in openai_functions[0]["function"] - - -def test_llm_openai_provider_mocked(): - """Test LLM with OpenAI provider using mocks.""" - from unittest.mock import MagicMock, patch - - messages = [{"role": "user", "content": "Hello, how are you?"}] - - # Create mock response - mock_message = MagicMock() - mock_message.content = "I'm doing well, thank you!" - - mock_choice = MagicMock() - mock_choice.message = mock_message - - mock_response = MagicMock() - mock_response.choices = [mock_choice] - - with patch.object(llm, "completion", return_value=mock_response) as mock_completion: - response = llm.completion( - model="openai/gpt-3.5-turbo", - messages=messages, - ) - - assert response.choices[0].message.content is not None - mock_completion.assert_called_once_with( - model="openai/gpt-3.5-turbo", - messages=messages, - ) - - -def test_llm_anthropic_provider_mocked(): - """Test LLM with Anthropic provider using mocks.""" - from unittest.mock import MagicMock, patch - - messages = [{"role": "user", "content": "Hello, how are you?"}] - - # Create mock response - mock_message = MagicMock() - mock_message.content = "Hello! I'm Claude, and I'm doing well." - - mock_choice = MagicMock() - mock_choice.message = mock_message - - mock_response = MagicMock() - mock_response.choices = [mock_choice] - - with patch.object(llm, "completion", return_value=mock_response) as mock_completion: - response = llm.completion( - model="anthropic/claude-3-haiku-20240307", - messages=messages, - ) - - assert response.choices[0].message.content is not None - mock_completion.assert_called_once_with( - model="anthropic/claude-3-haiku-20240307", - messages=messages, - ) - - -# Integration tests moved to tests/e2e/test_llm_integration.py -# They require real API keys and only run in CI - - -# Only run this test if explicitly requested with pytest -xvs tests/test_agent/test_llm_providers.py -if __name__ == "__main__": - pytest.main(["-xvs", __file__]) diff --git a/pkg/hanzo-mcp/tests/test_agent/test_model_capabilities.py b/pkg/hanzo-mcp/tests/test_agent/test_model_capabilities.py deleted file mode 100644 index 661ead7bf..000000000 --- a/pkg/hanzo-mcp/tests/test_agent/test_model_capabilities.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Tests for model capability checking functions.""" - -from hanzo_tools.agent.tool_adapter import ( - supports_parallel_function_calling, -) - - -class TestModelCapabilities: - """Tests for model capability checking functions.""" - - def test_supports_parallel_function_calling(self): - """Test that supports_parallel_function_calling properly identifies capable models.""" - # Test models that support parallel function calling - assert supports_parallel_function_calling("gpt-4-turbo-preview") is True - assert supports_parallel_function_calling("openai/gpt-4-turbo-preview") is True - assert supports_parallel_function_calling("gpt-4o") is True - assert supports_parallel_function_calling("openai/gpt-4o-mini") is True - assert supports_parallel_function_calling("claude-3-5-sonnet-20241022") is True - assert supports_parallel_function_calling("anthropic/claude-3-opus") is True - - # Test models that don't support parallel function calling - assert supports_parallel_function_calling("gpt-4") is False - assert supports_parallel_function_calling("gpt-3.5") is False - assert supports_parallel_function_calling("text-davinci-003") is False - assert supports_parallel_function_calling("unknown-model") is False diff --git a/pkg/hanzo-mcp/tests/test_agent/test_prompt.py b/pkg/hanzo-mcp/tests/test_agent/test_prompt.py deleted file mode 100644 index bc9f8d5b6..000000000 --- a/pkg/hanzo-mcp/tests/test_agent/test_prompt.py +++ /dev/null @@ -1,154 +0,0 @@ -"""Tests for the agent tool prompt module.""" - -import os -from unittest.mock import MagicMock - -import pytest -from hanzo_tools.agent.prompt import ( - get_allowed_agent_tools, - get_default_model, - get_model_parameters, - get_system_prompt, -) - -from hanzo_mcp.tools.common.base import BaseTool -from hanzo_mcp.tools.common.permissions import PermissionManager - - -class TestPrompt: - """Test cases for the agent tool prompt module.""" - - @pytest.fixture - def permission_manager(self): - """Create a test permission manager.""" - return MagicMock(spec=PermissionManager) - - @pytest.fixture - def mock_tools(self): - """Create a list of mock tools.""" - tools = [] - - # Create a read-only tool - read_tool = MagicMock(spec=BaseTool) - read_tool.name = "read_files" - read_tool.description = "Read files" - read_tool.isReadOnly = MagicMock(return_value=True) - read_tool.needsPermissions = MagicMock(return_value=False) - tools.append(read_tool) - - # Create a non-read-only tool - write_tool = MagicMock(spec=BaseTool) - write_tool.name = "write_file" - write_tool.description = "Write to files" - write_tool.isReadOnly = MagicMock(return_value=False) - write_tool.needsPermissions = MagicMock(return_value=True) - tools.append(write_tool) - - # Create a tool that needs permissions - cmd_tool = MagicMock(spec=BaseTool) - cmd_tool.name = "run_command" - cmd_tool.description = "Run shell commands" - cmd_tool.isReadOnly = MagicMock(return_value=False) - cmd_tool.needsPermissions = MagicMock(return_value=True) - tools.append(cmd_tool) - - # Create an agent tool (should be filtered out to prevent recursion) - agent_tool = MagicMock(spec=BaseTool) - agent_tool.name = "agent" - agent_tool.description = "Launch agent" - agent_tool.isReadOnly = MagicMock(return_value=True) - agent_tool.needsPermissions = MagicMock(return_value=False) - tools.append(agent_tool) - - return tools - - def test_get_allowed_agent_tools(self, tool_helper, mock_tools, permission_manager): - """Test get_allowed_agent_tools only filters out the agent tool.""" - # Get allowed tools - allowed_tools = get_allowed_agent_tools(mock_tools, permission_manager) - - # Should include all tools except for agent - assert len(allowed_tools) == 3 - assert "read_files" in [tool.name for tool in allowed_tools] - assert "write_file" in [tool.name for tool in allowed_tools] - assert "run_command" in [tool.name for tool in allowed_tools] - assert "agent" not in [tool.name for tool in allowed_tools] - - def test_get_system_prompt(self, tool_helper, mock_tools, permission_manager): - """Test get_system_prompt includes all tools except agent.""" - # Get system prompt - system_prompt = get_system_prompt(mock_tools, permission_manager) - - # Should mention all tools except agent - assert "`read_files`" in system_prompt - assert "`write_file`" in system_prompt - assert "`run_command`" in system_prompt - assert "`agent`" not in system_prompt - - # Should mention editing capabilities - assert "FULL read and write access" in system_prompt - assert "can create, edit, and modify files" in system_prompt - - def test_get_default_model(self): - """Test get_default_model.""" - # Test with environment variable - os.environ["AGENT_MODEL"] = "test-model-123" - assert get_default_model() == "test-model-123" - - # Test with model override - explicitly with TEST_MODE to avoid provider prefix - os.environ["TEST_MODE"] = "1" - assert get_default_model("openai/gpt-4o") == "openai/gpt-4o" - assert ( - get_default_model("gpt-4o-mini") == "gpt-4o-mini" - ) # In test mode, no prefix added - assert ( - get_default_model("anthropic/claude-3-sonnet") - == "anthropic/claude-3-sonnet" - ) - - # Test with provider prefixing in non-test mode - del os.environ["TEST_MODE"] - # Set provider to openai for this test - os.environ["AGENT_PROVIDER"] = "openai" - assert get_default_model("gpt-4") == "openai/gpt-4" - # Clean up - del os.environ["AGENT_PROVIDER"] - - # Test default - del os.environ["AGENT_MODEL"] - # Default is now Claude Sonnet - assert get_default_model() == "anthropic/claude-3-5-sonnet-20241022" - - def test_get_model_parameters(self): - """Test get_model_parameters.""" - # Test with environment variables - os.environ["AGENT_TEMPERATURE"] = "0.5" - os.environ["AGENT_API_TIMEOUT"] = "30" - os.environ["AGENT_MAX_TOKENS"] = "2000" - - params = get_model_parameters() - assert params["temperature"] == 0.5 - assert params["timeout"] == 30 - assert params["max_tokens"] == 2000 - - # Test with max_tokens override - params = get_model_parameters(max_tokens=1500) - assert params["temperature"] == 0.5 - assert params["timeout"] == 30 - assert params["max_tokens"] == 1500 # Override takes precedence - - # Test defaults - del os.environ["AGENT_TEMPERATURE"] - del os.environ["AGENT_API_TIMEOUT"] - del os.environ["AGENT_MAX_TOKENS"] - - params = get_model_parameters() - assert params["temperature"] == 0.7 - assert params["timeout"] == 60 - assert "max_tokens" not in params # Not set when not provided - - # Test with only max_tokens override - params = get_model_parameters(max_tokens=1000) - assert params["temperature"] == 0.7 - assert params["timeout"] == 60 - assert params["max_tokens"] == 1000 diff --git a/pkg/hanzo-mcp/tests/test_agent/test_tool_adapter.py b/pkg/hanzo-mcp/tests/test_agent/test_tool_adapter.py deleted file mode 100644 index 259fd13f1..000000000 --- a/pkg/hanzo-mcp/tests/test_agent/test_tool_adapter.py +++ /dev/null @@ -1,104 +0,0 @@ -"""Tests for the agent tool adapter module.""" - -from unittest.mock import MagicMock - -import pytest -from hanzo_tools.agent.tool_adapter import ( - convert_tool_parameters, - convert_tools_to_openai_functions, -) - -from hanzo_mcp.tools.common.base import BaseTool - - -class TestToolAdapter: - """Test cases for the agent tool adapter module.""" - - @pytest.fixture - def mock_tool(self): - """Create a mock tool.""" - tool = MagicMock(spec=BaseTool) - tool.name = "read_files" - tool.description = "Read files from the file system" - tool.parameters = { - "properties": { - "paths": { - "anyOf": [ - {"items": {"type": "string"}, "type": "array"}, - {"type": "string"}, - ], - "title": "Paths", - }, - }, - "required": ["paths"], - "title": "read_filesArguments", - "type": "object", - } - tool.required = ["paths"] - return tool - - @pytest.fixture - def mock_simple_tool(self): - """Create a mock tool with minimal parameters.""" - tool = MagicMock(spec=BaseTool) - tool.name = "think" - tool.description = "Think about something" - tool.parameters = { - "properties": { - "thought": { - "title": "Thought", - "type": "string", - }, - }, - } - tool.required = ["thought"] - return tool - - def test_convert_tools_to_openai_functions( - self, tool_helper, mock_tool, mock_simple_tool - ): - """Test convert_tools_to_openai_functions.""" - # Convert tools - openai_functions = convert_tools_to_openai_functions( - [mock_tool, mock_simple_tool] - ) - - # Verify result - assert len(openai_functions) == 2 - - # Check first tool - assert openai_functions[0]["type"] == "function" - assert openai_functions[0]["function"]["name"] == "read_files" - assert ( - openai_functions[0]["function"]["description"] - == "Read files from the file system" - ) - assert "parameters" in openai_functions[0]["function"] - - # Check second tool - assert openai_functions[1]["type"] == "function" - assert openai_functions[1]["function"]["name"] == "think" - assert openai_functions[1]["function"]["description"] == "Think about something" - assert "parameters" in openai_functions[1]["function"] - - def test_convert_tool_parameters_complete(self, tool_helper, mock_tool): - """Test convert_tool_parameters with complete parameters.""" - # Convert parameters - params = convert_tool_parameters(mock_tool) - - # Verify result - assert params["type"] == "object" - assert "properties" in params - assert "paths" in params["properties"] - assert params["required"] == ["paths"] - - def test_convert_tool_parameters_minimal(self, tool_helper, mock_simple_tool): - """Test convert_tool_parameters with minimal parameters.""" - # Convert parameters - params = convert_tool_parameters(mock_simple_tool) - - # Verify result - assert params["type"] == "object" - assert "properties" in params - assert "thought" in params["properties"] - assert params["required"] == ["thought"] diff --git a/pkg/hanzo-mcp/tests/test_agent_tools_ci.py b/pkg/hanzo-mcp/tests/test_agent_tools_ci.py deleted file mode 100644 index f5a06efe2..000000000 --- a/pkg/hanzo-mcp/tests/test_agent_tools_ci.py +++ /dev/null @@ -1,76 +0,0 @@ -"""CI tests for unified agent tools.""" - -from unittest.mock import Mock - -import pytest -from hanzo_tools.agent import TOOLS, register_tools -from hanzo_tools.agent.agent_tool import AgentTool -from hanzo_tools.agent.review_tool import ReviewTool -from hanzo_tools.agent.zen_tool import ZenTool - - -@pytest.fixture -def mock_mcp_server(): - """Create a mock MCP server.""" - server = Mock() - server.tool = Mock(return_value=lambda f: f) - return server - - -class TestAgentTools: - """Test unified agent tools work correctly.""" - - def test_tools_export(self): - """Test TOOLS exports the correct tools.""" - tool_classes = [t.__name__ for t in TOOLS] - assert "AgentTool" in tool_classes - assert "ZenTool" in tool_classes - assert "ReviewTool" in tool_classes - assert len(TOOLS) == 3 - - def test_agent_tool_creation(self): - """Test AgentTool can be created.""" - tool = AgentTool() - assert tool.name == "agent" - assert tool.description is not None - assert len(tool.description) > 0 - - def test_zen_tool_creation(self): - """Test ZenTool can be created.""" - tool = ZenTool() - assert tool.name == "zen" - desc_lower = tool.description.lower() - assert "zen" in desc_lower or "64" in desc_lower or "guidance" in desc_lower - - def test_review_tool_creation(self): - """Test ReviewTool can be created.""" - tool = ReviewTool() - assert tool.name == "review" - assert "review" in tool.description.lower() - - def test_all_tools_register(self, mock_mcp_server): - """Test all agent tools register correctly.""" - tools = register_tools(mcp_server=mock_mcp_server) - - # Should return list of registered tools - assert len(tools) == 3 - - # Check tool names - tool_names = [t.name for t in tools] - assert "agent" in tool_names - assert "zen" in tool_names - assert "review" in tool_names - - def test_tool_naming_consistency(self): - """Ensure tool naming is consistent.""" - agent = AgentTool() - zen = ZenTool() - review = ReviewTool() - - assert agent.name == "agent" - assert zen.name == "zen" - assert review.name == "review" - - -if __name__ == "__main__": - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_async_support.py b/pkg/hanzo-mcp/tests/test_async_support.py deleted file mode 100644 index 587ec4557..000000000 --- a/pkg/hanzo-mcp/tests/test_async_support.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Basic test for asyncio support.""" - -import asyncio - -import pytest - - -def test_sync(): - """Simple synchronous test to verify basic testing works.""" - assert 1 + 1 == 2 - - -@pytest.mark.asyncio -async def test_async_simple(): - """Simple async test to verify asyncio support.""" - await asyncio.sleep(0.001) - assert 1 + 1 == 2 - - -@pytest.mark.asyncio -async def test_async_manual(): - """Run async code to verify it works.""" - await asyncio.sleep(0.001) - result = 2 - assert result == 2 diff --git a/pkg/hanzo-mcp/tests/test_batch_tool_edge_cases.py b/pkg/hanzo-mcp/tests/test_batch_tool_edge_cases.py deleted file mode 100644 index d9fdb6c29..000000000 --- a/pkg/hanzo-mcp/tests/test_batch_tool_edge_cases.py +++ /dev/null @@ -1,248 +0,0 @@ -"""Test edge cases for the batch tool to prevent errors.""" - -import asyncio -from unittest.mock import AsyncMock, Mock, patch - -import pytest -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_mcp.tools.common.batch_tool import BatchTool - - -class TestBatchToolEdgeCases: - """Test edge cases for the batch tool.""" - - @pytest.fixture - def mock_ctx(self): - """Create a mock MCP context.""" - ctx = Mock(spec=MCPContext) - ctx.meta = {"tool_manager": Mock()} - return ctx - - @pytest.fixture - def batch_tool(self): - """Create a batch tool instance.""" - # Create some mock tools - mock_tool1 = Mock(spec=["name", "call"]) - mock_tool1.name = "tool1" - mock_tool1.call = AsyncMock(return_value="Tool 1 result") - - mock_tool2 = Mock(spec=["name", "call"]) - mock_tool2.name = "tool2" - mock_tool2.call = AsyncMock(return_value="Tool 2 result") - - tools = {"tool1": mock_tool1, "tool2": mock_tool2} - - return BatchTool(tools) - - @pytest.mark.asyncio - async def test_empty_invocations_error(self, tool_helper, batch_tool, mock_ctx): - """Test that empty invocations list raises an error.""" - # Create mock tool context - mock_tool_ctx = Mock() - mock_tool_ctx.set_tool_info = AsyncMock() - mock_tool_ctx.error = AsyncMock() - - with patch( - "hanzo_mcp.tools.common.context.create_tool_context", - return_value=mock_tool_ctx, - ): - result = await batch_tool.call( - ctx=mock_ctx, - description="Test batch", - invocations=[], # Empty list should fail - ) - - # Should return an error message - tool_helper.assert_in_result("Error:", result) - tool_helper.assert_in_result("invocations", result) - tool_helper.assert_in_result("empty", result) - - @pytest.mark.asyncio - async def test_invalid_tool_name(self, tool_helper, batch_tool, mock_ctx): - """Test handling of invalid tool names.""" - # Create mock tool context - mock_tool_ctx = Mock() - mock_tool_ctx.set_tool_info = AsyncMock() - mock_tool_ctx.error = AsyncMock() - mock_tool_ctx.info = AsyncMock() - - with patch( - "hanzo_mcp.tools.common.context.create_tool_context", - return_value=mock_tool_ctx, - ): - result = await batch_tool.call( - ctx=mock_ctx, - description="Test invalid tool", - invocations=[{"tool_name": "nonexistent_tool", "input": {}}], - ) - - # Batch tool returns results as a string - assert isinstance(result, str) - tool_helper.assert_in_result("Error", result) - tool_helper.assert_in_result("not found", result) - tool_helper.assert_in_result("nonexistent_tool", result) - - @pytest.mark.asyncio - async def test_tool_execution_error(self, tool_helper, batch_tool, mock_ctx): - """Test handling of tool execution errors.""" - # Make existing tool1 raise an exception - batch_tool.tools["tool1"].call.side_effect = Exception("Tool execution failed") - - result = await batch_tool.call( - ctx=mock_ctx, - description="Test error handling", - invocations=[{"tool_name": "tool1", "input": {"param": "value"}}], - ) - - # Should capture the error - assert isinstance(result, str) - tool_helper.assert_in_result("Error", result) - tool_helper.assert_in_result("Tool execution failed", result) - - @pytest.mark.asyncio - async def test_mixed_success_and_failure(self, tool_helper, batch_tool, mock_ctx): - """Test batch with both successful and failing tools.""" - # Use existing tools - tool1 succeeds, tool2 fails - batch_tool.tools["tool1"].call.reset_mock() - batch_tool.tools["tool1"].call.side_effect = None - batch_tool.tools["tool1"].call.return_value = {"result": "success"} - - batch_tool.tools["tool2"].call.side_effect = Exception("Failed") - - result = await batch_tool.call( - ctx=mock_ctx, - description="Mixed results", - invocations=[ - {"tool_name": "tool1", "input": {}}, - {"tool_name": "tool2", "input": {}}, - {"tool_name": "tool1", "input": {"param": "2"}}, - ], - ) - - # Check for mixed results in string output - assert isinstance(result, str) - tool_helper.assert_in_result("Result 1: tool1", result) - tool_helper.assert_in_result("Result 2: tool2", result) - tool_helper.assert_in_result("Result 3: tool1", result) - tool_helper.assert_in_result("Error", result) # For the failed tool - tool_helper.assert_in_result("Failed", result) - - @pytest.mark.asyncio - async def test_large_batch_pagination(self, tool_helper, batch_tool, mock_ctx): - """Test pagination with large batch results.""" - # Create mock tool context - mock_tool_ctx = Mock() - mock_tool_ctx.set_tool_info = AsyncMock() - mock_tool_ctx.error = AsyncMock() - mock_tool_ctx.info = AsyncMock() - - # Make tools return large output - batch_tool.tools["tool1"].call = AsyncMock( - return_value="X" * 100000 - ) # 100KB output - - # Create many invocations - invocations = [{"tool_name": "tool1", "input": {"id": i}} for i in range(50)] - - with patch( - "hanzo_mcp.tools.common.context.create_tool_context", - return_value=mock_tool_ctx, - ): - result = await batch_tool.call( - ctx=mock_ctx, description="Large batch", invocations=invocations - ) - - # Should handle pagination - tool_helper.assert_in_result("results", result) - # May have pagination info if output is too large - if "_pagination" in result: - assert "cursor" in result["_pagination"] - - @pytest.mark.asyncio - async def test_concurrent_execution_limit(self, tool_helper, batch_tool, mock_ctx): - """Test that concurrent execution respects limits.""" - execution_times = [] - - async def slow_tool_call(*args, **kwargs): - start = asyncio.get_event_loop().time() - await asyncio.sleep(0.1) # Simulate work - execution_times.append(start) - return {"result": "done"} - - # Use existing tool1 and make it slow - batch_tool.tools["tool1"].call = slow_tool_call - - # Create many invocations - invocations = [{"tool_name": "tool1", "input": {"id": i}} for i in range(20)] - - start_time = asyncio.get_event_loop().time() - await batch_tool.call( - ctx=mock_ctx, description="Concurrent test", invocations=invocations - ) - end_time = asyncio.get_event_loop().time() - - # Check that execution was concurrent but limited - total_time = end_time - start_time - - # If all ran sequentially, would take 2 seconds (20 * 0.1) - # If all ran in parallel with no limit, would take ~0.1 seconds - # With concurrency limit, should be somewhere in between - assert total_time < 2.0 # Confirms some parallelism - # Relax the lower bound as concurrency behavior may vary - assert total_time > 0.05 # At least some execution time - - @pytest.mark.asyncio - async def test_invalid_input_types(self, tool_helper, batch_tool, mock_ctx): - """Test handling of invalid input types.""" - # Use existing tool1 - batch_tool.tools["tool1"].call.reset_mock() - batch_tool.tools["tool1"].call.side_effect = None - batch_tool.tools["tool1"].call.return_value = {"result": "ok"} - - # Test with various invalid input types - test_cases = [ - # String instead of dict - {"tool_name": "tool1", "input": "not a dict"}, - # List instead of dict - {"tool_name": "tool1", "input": ["not", "a", "dict"]}, - # None input - {"tool_name": "tool1", "input": None}, - ] - - for invalid_invocation in test_cases: - # Should handle gracefully or convert - result = await batch_tool.call( - ctx=mock_ctx, - description="Invalid input test", - invocations=[invalid_invocation], - ) - - tool_helper.assert_in_result("results", result) - # Either handles it or returns error - - @pytest.mark.asyncio - async def test_tool_name_normalization(self, tool_helper, batch_tool, mock_ctx): - """Test that tool names are normalized properly.""" - # Batch tool doesn't normalize names - it looks them up exactly - # So all variations will fail to find the tool - - # Test with various name formats - invocations = [ - {"tool_name": "TEST_TOOL", "input": {}}, - {"tool_name": " test_tool ", "input": {}}, - {"tool_name": "Test_Tool", "input": {}}, - ] - - result = await batch_tool.call( - ctx=mock_ctx, description="Name normalization", invocations=invocations - ) - - # Should handle all variations - but batch tool doesn't normalize names - assert isinstance(result, str) - tool_helper.assert_in_result("Result 1", result) - tool_helper.assert_in_result("Result 2", result) - tool_helper.assert_in_result("Result 3", result) - # Since batch tool doesn't normalize names, these will all be "not found" errors - tool_helper.assert_in_result("Error", result) - tool_helper.assert_in_result("not found", result) diff --git a/pkg/hanzo-mcp/tests/test_cli.py b/pkg/hanzo-mcp/tests/test_cli.py deleted file mode 100644 index 6325a32ad..000000000 --- a/pkg/hanzo-mcp/tests/test_cli.py +++ /dev/null @@ -1,644 +0,0 @@ -"""Tests for the CLI module.""" - -import os -import sys -from pathlib import Path -from typing import Callable -from unittest.mock import MagicMock, patch - -import pytest - -from hanzo_mcp.cli import install_claude_desktop_config, main - - -class TestCLI: - """Test the CLI module.""" - - def test_main_server_run(self) -> None: - """Test the main function running the server.""" - with ( - patch("argparse.ArgumentParser.parse_args") as mock_parse_args, - patch("hanzo_mcp.server.HanzoMCPServer") as mock_server_class, - ): - # Mock parsed arguments - mock_args = MagicMock() - mock_args.name = "test-server" - mock_args.transport = "stdio" - mock_args.allowed_paths = ["/test/path"] - mock_args.project_dir = "/test/project" - mock_args.install = False - mock_args.agent_model = "anthropic/claude-3-sonnet" - mock_args.agent_max_tokens = 2000 - mock_args.agent_api_key = "test_api_key" - mock_args.agent_base_url = None - mock_args.agent_max_iterations = 10 - mock_args.agent_max_tool_uses = 30 - mock_args.enable_agent_tool = False - mock_args.disable_write_tools = False - mock_args.disable_search_tools = False - mock_args.log_level = "INFO" - mock_args.host = "127.0.0.1" - mock_args.port = 3000 - mock_args.project_paths = None - mock_args.command_timeout = "120s" - mock_args.dev = False - mock_args.force_shell = None - mock_args.daemon = False - mock_args.socket_path = "/tmp/hanzo-mcp.sock" - mock_args.max_connections = 100 - mock_args.tool_timeout = None - mock_args.search_timeout = None - mock_args.find_timeout = None - mock_args.ast_timeout = None - mock_parse_args.return_value = mock_args - - # Mock server instance - mock_server = MagicMock() - mock_server_class.return_value = mock_server - - # Call main - main() - - # Verify server was created with correct arguments - # Project dir should be added to allowed paths and project_paths - expected_paths = ["/test/path", "/test/project"] - mock_server_class.assert_called_once_with( - name="test-server", - allowed_paths=expected_paths, - project_paths=["/test/project"], - project_dir="/test/project", - agent_model="anthropic/claude-3-sonnet", - agent_max_tokens=2000, - agent_api_key="test_api_key", - agent_base_url=mock_args.agent_base_url, - agent_max_iterations=10, - agent_max_tool_uses=30, - enable_agent_tool=False, - command_timeout=120.0, - disable_write_tools=False, - disable_search_tools=False, - host=mock_args.host, - port=mock_args.port, - ) - mock_server.run.assert_called_once_with(transport="stdio") - - def test_main_with_install(self) -> None: - """Test the main function with install option.""" - with ( - patch("argparse.ArgumentParser.parse_args") as mock_parse_args, - patch("hanzo_mcp.cli.install_claude_desktop_config") as mock_install, - ): - # Mock parsed arguments - mock_args = MagicMock() - mock_args.name = "test-server" - mock_args.install = True - mock_args.allowed_paths = ["/test/path"] - mock_args.project_dir = None - mock_args.disable_write_tools = False - mock_args.disable_search_tools = False - mock_args.log_level = "INFO" - mock_args.host = "127.0.0.1" - mock_args.port = 3000 - mock_args.command_timeout = "120s" - mock_args.dev = False - mock_args.force_shell = None - mock_args.daemon = False - mock_args.socket_path = "/tmp/hanzo-mcp.sock" - mock_args.max_connections = 100 - mock_args.tool_timeout = None - mock_args.search_timeout = None - mock_args.find_timeout = None - mock_args.ast_timeout = None - mock_parse_args.return_value = mock_args - - # Call main - main() - - # Verify install function was called - mock_install.assert_called_once_with( - "test-server", - ["/test/path"], - mock_args.disable_write_tools, - mock_args.disable_search_tools, - mock_args.host, - mock_args.port, - ) - - def test_main_without_allowed_paths(self) -> None: - """Test the main function without specified allowed paths.""" - with ( - patch("argparse.ArgumentParser.parse_args") as mock_parse_args, - patch("hanzo_mcp.server.HanzoMCPServer") as mock_server_class, - patch("os.path.expanduser", return_value="/home/testuser"), - ): - # Mock parsed arguments - mock_args = MagicMock() - mock_args.name = "test-server" - mock_args.transport = "stdio" - mock_args.allowed_paths = None - mock_args.project_dir = None - mock_args.install = False - mock_args.agent_model = None - mock_args.agent_max_tokens = None - mock_args.agent_api_key = None - mock_args.agent_base_url = None - mock_args.agent_max_iterations = 10 - mock_args.agent_max_tool_uses = 30 - mock_args.enable_agent_tool = False - mock_args.disable_write_tools = False - mock_args.disable_search_tools = False - mock_args.log_level = "INFO" - mock_args.host = "127.0.0.1" - mock_args.port = 3000 - mock_args.project_paths = None - mock_args.command_timeout = "120s" - mock_args.dev = False - mock_args.force_shell = None - mock_args.daemon = False - mock_args.socket_path = "/tmp/hanzo-mcp.sock" - mock_args.max_connections = 100 - mock_args.tool_timeout = None - mock_args.search_timeout = None - mock_args.find_timeout = None - mock_args.ast_timeout = None - mock_parse_args.return_value = mock_args - - # Mock server instance - mock_server = MagicMock() - mock_server_class.return_value = mock_server - - # Call main - main() - - # Verify server was created with home directory as allowed path - mock_server_class.assert_called_once_with( - name="test-server", - allowed_paths=["/home/testuser"], - project_paths=[], - project_dir=None, - agent_model=None, - agent_max_tokens=None, - agent_api_key=None, - agent_base_url=None, - agent_max_iterations=10, - agent_max_tool_uses=30, - enable_agent_tool=False, - command_timeout=120.0, - disable_write_tools=False, - disable_search_tools=False, - host=mock_args.host, - port=mock_args.port, - ) - mock_server.run.assert_called_once_with(transport="stdio") - - def test_main_with_disable_write_tools(self) -> None: - """Test the main function with disable_write_tools=True.""" - with ( - patch("argparse.ArgumentParser.parse_args") as mock_parse_args, - patch("hanzo_mcp.server.HanzoMCPServer") as mock_server_class, - ): - # Mock parsed arguments - mock_args = MagicMock() - mock_args.name = "test-server" - mock_args.transport = "stdio" - mock_args.allowed_paths = ["/test/path"] - mock_args.project_dir = "/test/project" - mock_args.install = False - mock_args.agent_model = None - mock_args.agent_max_tokens = None - mock_args.agent_api_key = None - mock_args.agent_base_url = None - mock_args.agent_max_iterations = 10 - mock_args.agent_max_tool_uses = 30 - mock_args.enable_agent_tool = False - mock_args.disable_write_tools = True - mock_args.disable_search_tools = False - mock_args.log_level = "INFO" - mock_args.host = "127.0.0.1" - mock_args.port = 3000 - mock_args.project_paths = None - mock_args.command_timeout = "120s" - mock_args.dev = False - mock_args.force_shell = None - mock_args.daemon = False - mock_args.socket_path = "/tmp/hanzo-mcp.sock" - mock_args.max_connections = 100 - mock_args.tool_timeout = None - mock_args.search_timeout = None - mock_args.find_timeout = None - mock_args.ast_timeout = None - mock_parse_args.return_value = mock_args - - # Mock server instance - mock_server = MagicMock() - mock_server_class.return_value = mock_server - - # Call main - main() - - # Verify server was created with disable_write_tools=True - expected_paths = ["/test/path", "/test/project"] - mock_server_class.assert_called_once_with( - name="test-server", - allowed_paths=expected_paths, - project_paths=["/test/project"], - project_dir="/test/project", - agent_model=None, - agent_max_tokens=None, - agent_api_key=None, - agent_base_url=None, - agent_max_iterations=10, - agent_max_tool_uses=30, - enable_agent_tool=False, - command_timeout=120.0, - disable_write_tools=True, - disable_search_tools=False, - host=mock_args.host, - port=mock_args.port, - ) - mock_server.run.assert_called_once_with(transport="stdio") - - def test_main_with_disable_search_tools(self) -> None: - """Test the main function with disable_search_tools=True.""" - with ( - patch("argparse.ArgumentParser.parse_args") as mock_parse_args, - patch("hanzo_mcp.server.HanzoMCPServer") as mock_server_class, - ): - # Mock parsed arguments - mock_args = MagicMock() - mock_args.name = "test-server" - mock_args.transport = "stdio" - mock_args.allowed_paths = ["/test/path"] - mock_args.project_dir = "/test/project" - mock_args.install = False - mock_args.agent_model = None - mock_args.agent_max_tokens = None - mock_args.agent_api_key = None - mock_args.agent_base_url = None - mock_args.agent_max_iterations = 10 - mock_args.agent_max_tool_uses = 30 - mock_args.enable_agent_tool = False - mock_args.disable_write_tools = False - mock_args.disable_search_tools = True - mock_args.log_level = "INFO" - mock_args.host = "127.0.0.1" - mock_args.port = 3000 - mock_args.project_paths = None - mock_args.command_timeout = "120s" - mock_args.dev = False - mock_args.force_shell = None - mock_args.daemon = False - mock_args.socket_path = "/tmp/hanzo-mcp.sock" - mock_args.max_connections = 100 - mock_args.tool_timeout = None - mock_args.search_timeout = None - mock_args.find_timeout = None - mock_args.ast_timeout = None - mock_parse_args.return_value = mock_args - - # Mock server instance - mock_server = MagicMock() - mock_server_class.return_value = mock_server - - # Call main - main() - - # Verify server was created with disable_search_tools=True - expected_paths = ["/test/path", "/test/project"] - mock_server_class.assert_called_once_with( - name="test-server", - allowed_paths=expected_paths, - project_paths=["/test/project"], - project_dir="/test/project", - agent_model=None, - agent_max_tokens=None, - agent_api_key=None, - agent_base_url=None, - agent_max_iterations=10, - agent_max_tool_uses=30, - enable_agent_tool=False, - command_timeout=120.0, - disable_write_tools=False, - disable_search_tools=True, - host=mock_args.host, - port=mock_args.port, - ) - mock_server.run.assert_called_once_with(transport="stdio") - - -class TestInstallClaudeDesktopConfig: - """Test the install_claude_desktop_config function.""" - - @pytest.fixture - def mock_platform(self, monkeypatch) -> Callable[[str], str]: - """Mock the sys.platform value.""" - original_platform = sys.platform - - def _set_platform(plat): - monkeypatch.setattr(sys, "platform", plat) - return plat - - yield _set_platform - - # Restore original platform - monkeypatch.setattr(sys, "platform", original_platform) - - def test_install_config_macos( - self, mock_platform: Callable[[str], str], tmp_path: Path - ) -> None: - """Test installing config on macOS.""" - # Set platform to macOS - mock_platform("darwin") - - # Mock home directory and config path - with ( - patch("pathlib.Path.home", return_value=Path(tmp_path)), - patch("sys.executable", "/usr/bin/python3"), - patch("json.dump") as mock_json_dump, - patch("builtins.open", create=True) as mock_open, - patch("pathlib.Path.exists", return_value=False), - patch("pathlib.Path.mkdir") as mock_mkdir, - ): - # Construct expected config path - config_dir = tmp_path / "Library" / "Application Support" / "Claude" - config_file = config_dir / "claude_desktop_config.json" - - # Mock file opening - mock_file = MagicMock() - mock_open.return_value.__enter__.return_value = mock_file - - # Call the install function - install_claude_desktop_config("test-server", allowed_paths=["/test/path"]) - - # Verify config directory was created - mock_mkdir.assert_called_once_with(parents=True, exist_ok=True) - - # Verify file was opened correctly - mock_open.assert_called_once() - args, kwargs = mock_open.call_args - assert str(config_file) in str(args[0]) - assert kwargs.get("mode") == "w" - # Note: The mode parameter is set via a positional argument, not a keyword - # so we're not checking it here - - # Verify correct config was written - mock_json_dump.assert_called_once() - config_data = mock_json_dump.call_args[0][0] - assert "mcpServers" in config_data - assert "test-server" in config_data["mcpServers"] - assert ( - "/usr/bin/python3" - in config_data["mcpServers"]["test-server"]["command"] - ) - assert "--allow-path" in str( - config_data["mcpServers"]["test-server"]["args"] - ) - assert "/test/path" in str(config_data["mcpServers"]["test-server"]["args"]) - - def test_install_config_windows( - self, mock_platform: Callable[[str], str], tmp_path: Path - ) -> None: - """Test installing config on Windows.""" - # Set platform to Windows - mock_platform("win32") - - # Mock environment variable - with ( - patch.dict(os.environ, {"APPDATA": str(tmp_path)}), - patch("sys.executable", "C:\\Python\\python.exe"), - patch("json.dump") as mock_json_dump, - patch("builtins.open", create=True) as mock_open, - patch("pathlib.Path.exists", return_value=False), - patch("pathlib.Path.mkdir") as mock_mkdir, - ): - # Construct expected config path - config_dir = Path(tmp_path) / "Claude" - config_file = config_dir / "claude_desktop_config.json" - - # Mock file opening - mock_file = MagicMock() - mock_open.return_value.__enter__.return_value = mock_file - - # Call the install function - install_claude_desktop_config("test-server") - - # Verify config directory was created - mock_mkdir.assert_called_once_with(parents=True, exist_ok=True) - - # Verify file was opened correctly - mock_open.assert_called_once() - args, kwargs = mock_open.call_args - assert str(config_file) in str(args[0]) - - # Verify correct config was written - mock_json_dump.assert_called_once() - config_data = mock_json_dump.call_args[0][0] - assert "mcpServers" in config_data - assert "test-server" in config_data["mcpServers"] - - def test_install_config_merge_existing( - self, mock_platform: Callable[[str], str], tmp_path: Path - ) -> None: - """Test merging with existing config file.""" - # Set platform to Linux - mock_platform("linux") - - # Create a mock existing config - existing_config = { - "mcpServers": { - "existing-server": { - "command": "/usr/bin/python3", - "args": ["-m", "existing_module"], - } - }, - "otherSetting": "value", - } - - # Mock home directory and config path - with ( - patch("pathlib.Path.home", return_value=Path(tmp_path)), - patch("sys.executable", "/usr/bin/python3"), - patch("json.dump") as mock_json_dump, - patch("json.load", return_value=existing_config), - patch("builtins.open", create=True) as mock_open, - patch("pathlib.Path.exists", return_value=True), - patch("pathlib.Path.mkdir") as mock_mkdir, - ): - # Construct expected config path - config_dir = tmp_path / ".config" / "claude" - config_dir / "claude_desktop_config.json" - - # Mock file opening - mock_file = MagicMock() - mock_open.return_value.__enter__.return_value = mock_file - - # Call the install function - install_claude_desktop_config("test-server") - - # Verify config directory was created - mock_mkdir.assert_called_once_with(parents=True, exist_ok=True) - - # Verify file was opened correctly for reading and writing - assert mock_open.call_count == 2 - - # Verify correct config was written - mock_json_dump.assert_called_once() - config_data = mock_json_dump.call_args[0][0] - assert "mcpServers" in config_data - assert "existing-server" in config_data["mcpServers"] - assert "test-server" in config_data["mcpServers"] - assert "otherSetting" in config_data - - def test_install_config_default_paths( - self, mock_platform: Callable[[str], str], tmp_path: Path - ) -> None: - """Test installing config with default allowed paths.""" - # Set platform to macOS - mock_platform("darwin") - - # Mock home directory and config path - with ( - patch("pathlib.Path.home", return_value=Path(tmp_path)), - patch("sys.executable", "/usr/bin/python3"), - patch("json.dump") as mock_json_dump, - patch("builtins.open", create=True) as mock_open, - patch("pathlib.Path.exists", return_value=False), - patch("pathlib.Path.mkdir"), - ): - # Mock file opening - mock_file = MagicMock() - mock_open.return_value.__enter__.return_value = mock_file - - # Call the install function without specifying allowed_paths - install_claude_desktop_config("test-server") - - # Verify correct config was written with home directory as allowed path - mock_json_dump.assert_called_once() - config_data = mock_json_dump.call_args[0][0] - server_args = config_data["mcpServers"]["test-server"]["args"] - - # Verify home directory was added as an allowed path - assert "--allow-path" in server_args - home_path_index = server_args.index("--allow-path") + 1 - assert str(tmp_path) in server_args[home_path_index] - - # Verify --disable-write-tools flag is not present - assert "--disable-write-tools" not in server_args - - def test_install_config_with_disable_write_tools( - self, mock_platform: Callable[[str], str], tmp_path: Path - ) -> None: - """Test installing config with disable_write_tools=True.""" - # Set platform to macOS - mock_platform("darwin") - - # Mock home directory and config path - with ( - patch("pathlib.Path.home", return_value=Path(tmp_path)), - patch("sys.executable", "/usr/bin/python3"), - patch("json.dump") as mock_json_dump, - patch("builtins.open", create=True) as mock_open, - patch("pathlib.Path.exists", return_value=False), - patch("pathlib.Path.mkdir"), - ): - # Mock file opening - mock_file = MagicMock() - mock_open.return_value.__enter__.return_value = mock_file - - # Call the install function with disable_write_tools=True - install_claude_desktop_config( - "test-server", allowed_paths=["/test/path"], disable_write_tools=True - ) - - # Verify correct config was written - mock_json_dump.assert_called_once() - config_data = mock_json_dump.call_args[0][0] - server_args = config_data["mcpServers"]["test-server"]["args"] - - # Verify allowed path was added - assert "--allow-path" in server_args - path_index = server_args.index("--allow-path") + 1 - assert "/test/path" in server_args[path_index] - - # Verify --disable-write-tools flag is present - assert "--disable-write-tools" in server_args - - def test_install_config_with_disable_search_tools( - self, mock_platform: Callable[[str], str], tmp_path: Path - ) -> None: - """Test installing config with disable_search_tools=True.""" - # Set platform to macOS - mock_platform("darwin") - - # Mock home directory and config path - with ( - patch("pathlib.Path.home", return_value=Path(tmp_path)), - patch("sys.executable", "/usr/bin/python3"), - patch("json.dump") as mock_json_dump, - patch("builtins.open", create=True) as mock_open, - patch("pathlib.Path.exists", return_value=False), - patch("pathlib.Path.mkdir"), - ): - # Mock file opening - mock_file = MagicMock() - mock_open.return_value.__enter__.return_value = mock_file - - # Call the install function with disable_search_tools=True - install_claude_desktop_config( - "test-server", allowed_paths=["/test/path"], disable_search_tools=True - ) - - # Verify correct config was written - mock_json_dump.assert_called_once() - config_data = mock_json_dump.call_args[0][0] - server_args = config_data["mcpServers"]["test-server"]["args"] - - # Verify allowed path was added - assert "--allow-path" in server_args - path_index = server_args.index("--allow-path") + 1 - assert "/test/path" in server_args[path_index] - - # Verify --disable-search-tools flag is present - assert "--disable-search-tools" in server_args - - def test_install_config_with_both_flags( - self, mock_platform: Callable[[str], str], tmp_path: Path - ) -> None: - """Test installing config with both disable_write_tools and disable_search_tools set to True.""" - # Set platform to macOS - mock_platform("darwin") - - # Mock home directory and config path - with ( - patch("pathlib.Path.home", return_value=Path(tmp_path)), - patch("sys.executable", "/usr/bin/python3"), - patch("json.dump") as mock_json_dump, - patch("builtins.open", create=True) as mock_open, - patch("pathlib.Path.exists", return_value=False), - patch("pathlib.Path.mkdir"), - ): - # Mock file opening - mock_file = MagicMock() - mock_open.return_value.__enter__.return_value = mock_file - - # Call the install function with both flags set to True - install_claude_desktop_config( - "test-server", - allowed_paths=["/test/path"], - disable_write_tools=True, - disable_search_tools=True, - ) - - # Verify correct config was written - mock_json_dump.assert_called_once() - config_data = mock_json_dump.call_args[0][0] - server_args = config_data["mcpServers"]["test-server"]["args"] - - # Verify allowed path was added - assert "--allow-path" in server_args - path_index = server_args.index("--allow-path") + 1 - assert "/test/path" in server_args[path_index] - - # Verify both flags are present - assert "--disable-write-tools" in server_args - assert "--disable-search-tools" in server_args diff --git a/pkg/hanzo-mcp/tests/test_cli_tools.py b/pkg/hanzo-mcp/tests/test_cli_tools.py deleted file mode 100644 index 3f265c9c1..000000000 --- a/pkg/hanzo-mcp/tests/test_cli_tools.py +++ /dev/null @@ -1,376 +0,0 @@ -"""Test suite for CLI tools in batch operations.""" - -import asyncio -import time -from unittest.mock import AsyncMock, MagicMock, Mock, patch - -import pytest -from hanzo_tools.agent.cli_tools import ( - AiderCLITool, - ClaudeCLITool, - ClaudeCodeCLITool, - ClineCLITool, - CodexCLITool, - GeminiCLITool, - GrokCLITool, - HanzoDevCLITool, - OpenHandsCLITool, - OpenHandsShortCLITool, -) - -from hanzo_mcp.tools.common.batch_tool import BatchTool - - -class TestCLITools: - """Test CLI tool implementations.""" - - @pytest.fixture - def mock_context(self): - """Create a mock MCP context.""" - context = MagicMock() - context.session = MagicMock() - context.session.send_log_message = AsyncMock() - context.session.send_progress = AsyncMock() - return context - - @pytest.fixture - def mock_permission_manager(self): - """Create a mock permission manager.""" - pm = MagicMock() - pm.check_permission = Mock(return_value=True) - return pm - - @pytest.mark.asyncio - async def test_claude_cli_tool(self, mock_context, mock_permission_manager): - """Test Claude CLI tool execution.""" - tool = ClaudeCLITool(mock_permission_manager) - - # Test name and description - assert tool.name == "claude" - assert "Claude CLI" in tool.description - - # Mock subprocess execution - with patch.object(tool, "execute_cli", new_callable=AsyncMock) as mock_exec: - mock_exec.return_value = "Claude response" - - result = await tool.call( - mock_context, - prompt="Test prompt", - model="claude-3-opus-20240229", - timeout=300, - ) - - assert result == "Claude response" - mock_exec.assert_called_once() - - @pytest.mark.asyncio - async def test_claude_code_alias(self, mock_context, mock_permission_manager): - """Test Claude Code (cc) alias tool.""" - tool = ClaudeCodeCLITool(mock_permission_manager) - - # Test alias name - assert tool.name == "cc" - assert "Claude Code" in tool.description - - @pytest.mark.asyncio - async def test_codex_cli_tool(self, mock_context, mock_permission_manager): - """Test Codex/GPT-4 CLI tool execution.""" - tool = CodexCLITool(mock_permission_manager) - - assert tool.name == "codex" - assert "OpenAI" in tool.description or "GPT" in tool.description - - with patch.object(tool, "execute_cli", new_callable=AsyncMock) as mock_exec: - mock_exec.return_value = "GPT-4 response" - - result = await tool.call( - mock_context, prompt="Generate code", model="gpt-4-turbo" - ) - - assert result == "GPT-4 response" - - @pytest.mark.asyncio - async def test_gemini_cli_tool(self, mock_context, mock_permission_manager): - """Test Gemini CLI tool execution.""" - tool = GeminiCLITool(mock_permission_manager) - - assert tool.name == "gemini" - assert "Gemini" in tool.description - - with patch.object(tool, "execute_cli", new_callable=AsyncMock) as mock_exec: - mock_exec.return_value = "Gemini response" - - result = await tool.call( - mock_context, prompt="Analyze image", model="gemini-1.5-pro" - ) - - assert result == "Gemini response" - - @pytest.mark.asyncio - async def test_grok_cli_tool(self, mock_context, mock_permission_manager): - """Test Grok CLI tool execution.""" - tool = GrokCLITool(mock_permission_manager) - - assert tool.name == "grok" - assert "Grok" in tool.description - - with patch.object(tool, "execute_cli", new_callable=AsyncMock) as mock_exec: - mock_exec.return_value = "Grok response" - - result = await tool.call( - mock_context, prompt="Real-time analysis", model="grok-2" - ) - - assert result == "Grok response" - - @pytest.mark.asyncio - async def test_openhands_cli_tool(self, mock_context, mock_permission_manager): - """Test OpenHands CLI tool execution.""" - tool = OpenHandsCLITool(mock_permission_manager) - - assert tool.name == "openhands" - assert "OpenHands" in tool.description or "OpenDevin" in tool.description - - with patch.object(tool, "execute_cli", new_callable=AsyncMock) as mock_exec: - mock_exec.return_value = "OpenHands execution complete" - - result = await tool.call( - mock_context, prompt="Build feature", working_dir="/project" - ) - - assert result == "OpenHands execution complete" - - @pytest.mark.asyncio - async def test_openhands_alias(self, mock_context, mock_permission_manager): - """Test OpenHands (oh) alias tool.""" - tool = OpenHandsShortCLITool(mock_permission_manager) - - assert tool.name == "oh" - assert "OpenHands" in tool.description - - @pytest.mark.asyncio - async def test_hanzo_dev_cli_tool(self, mock_context, mock_permission_manager): - """Test Hanzo Dev CLI tool execution.""" - tool = HanzoDevCLITool(mock_permission_manager) - - assert tool.name == "hanzo_dev" - assert "Hanzo Dev" in tool.description - - with patch.object(tool, "execute_cli", new_callable=AsyncMock) as mock_exec: - mock_exec.return_value = "Hanzo Dev completed task" - - result = await tool.call( - mock_context, - prompt="Implement feature", - model="claude-3-5-sonnet-20241022", - ) - - assert result == "Hanzo Dev completed task" - - @pytest.mark.asyncio - async def test_cline_cli_tool(self, mock_context, mock_permission_manager): - """Test Cline CLI tool execution.""" - tool = ClineCLITool(mock_permission_manager) - - assert tool.name == "cline" - assert "Cline" in tool.description - - with patch.object(tool, "execute_cli", new_callable=AsyncMock) as mock_exec: - mock_exec.return_value = "Cline autonomous coding complete" - - result = await tool.call( - mock_context, prompt="Fix bugs", working_dir="/src" - ) - - assert result == "Cline autonomous coding complete" - - @pytest.mark.asyncio - async def test_aider_cli_tool(self, mock_context, mock_permission_manager): - """Test Aider CLI tool execution.""" - tool = AiderCLITool(mock_permission_manager) - - assert tool.name == "aider" - assert "Aider" in tool.description - - with patch.object(tool, "execute_cli", new_callable=AsyncMock) as mock_exec: - mock_exec.return_value = "Aider pair programming complete" - - result = await tool.call( - mock_context, prompt="Refactor module", model="gpt-4-turbo" - ) - - assert result == "Aider pair programming complete" - - @pytest.mark.asyncio - async def test_cli_tool_auth_env(self, mock_permission_manager): - """Test that CLI tools properly set authentication environment.""" - # Test Claude with Anthropic key - with patch.dict("os.environ", {"ANTHROPIC_API_KEY": "test-anthropic-key"}): - tool = ClaudeCLITool(mock_permission_manager) - env = tool.get_auth_env() - assert env["ANTHROPIC_API_KEY"] == "test-anthropic-key" - - # Test Codex with OpenAI key - with patch.dict("os.environ", {"OPENAI_API_KEY": "test-openai-key"}): - tool = CodexCLITool(mock_permission_manager) - env = tool.get_auth_env() - assert env["OPENAI_API_KEY"] == "test-openai-key" - - # Test unified Hanzo auth - with patch.dict("os.environ", {"HANZO_API_KEY": "test-hanzo-key"}): - tool = OpenHandsCLITool(mock_permission_manager) - env = tool.get_auth_env() - assert env["HANZO_API_KEY"] == "test-hanzo-key" - - @pytest.mark.asyncio - async def test_cli_tool_timeout(self, mock_context, mock_permission_manager): - """Test CLI tool timeout handling.""" - tool = ClaudeCLITool(mock_permission_manager) - - # Mock a timeout - with patch("asyncio.create_subprocess_exec") as mock_subprocess: - mock_process = MagicMock() - mock_process.communicate = AsyncMock(side_effect=asyncio.TimeoutError) - mock_subprocess.return_value = mock_process - - result = await tool.call(mock_context, prompt="Long task", timeout=1) - - assert "timed out" in result.lower() - - @pytest.mark.asyncio - async def test_cli_tool_error_handling(self, mock_context, mock_permission_manager): - """Test CLI tool error handling.""" - tool = CodexCLITool(mock_permission_manager) - - # Mock a subprocess error - with patch("asyncio.create_subprocess_exec") as mock_subprocess: - mock_process = MagicMock() - mock_process.communicate = AsyncMock( - return_value=(b"", b"Error: API key not found") - ) - mock_process.returncode = 1 - mock_subprocess.return_value = mock_process - - result = await tool.call(mock_context, prompt="Generate code") - - assert "Error" in result - assert "API key" in result - - -class TestBatchWithCLITools: - """Test batch tool with CLI tools.""" - - @pytest.fixture - def mock_permission_manager(self): - """Create a mock permission manager.""" - pm = MagicMock() - pm.check_permission = Mock(return_value=True) - return pm - - @pytest.fixture - def mock_tools(self, mock_permission_manager): - """Create mock CLI tools for batch testing.""" - tools = { - "claude": ClaudeCLITool(mock_permission_manager), - "cc": ClaudeCodeCLITool(mock_permission_manager), - "codex": CodexCLITool(mock_permission_manager), - "gemini": GeminiCLITool(mock_permission_manager), - "grok": GrokCLITool(mock_permission_manager), - "openhands": OpenHandsCLITool(mock_permission_manager), - "oh": OpenHandsShortCLITool(mock_permission_manager), - "cline": ClineCLITool(mock_permission_manager), - "aider": AiderCLITool(mock_permission_manager), - } - - # Mock execute_cli for all tools - for tool in tools.values(): - tool.execute_cli = AsyncMock(return_value=f"{tool.name} response") - - return tools - - @pytest.fixture - def batch_tool(self, mock_tools): - """Create batch tool with CLI tools.""" - return BatchTool(mock_tools) - - @pytest.fixture - def mock_context(self): - """Create a mock MCP context.""" - context = MagicMock() - context.session = MagicMock() - context.session.send_log_message = AsyncMock() - context.session.send_progress = AsyncMock() - return context - - @pytest.mark.asyncio - async def test_batch_with_multiple_cli_tools(self, batch_tool, mock_context): - """Test batch execution with multiple CLI tools.""" - invocations = [ - {"tool_name": "claude", "input": {"prompt": "Analyze architecture"}}, - {"tool_name": "codex", "input": {"prompt": "Generate implementation"}}, - {"tool_name": "gemini", "input": {"prompt": "Review code"}}, - ] - - result = await batch_tool.call( - mock_context, description="Multi-AI analysis", invocations=invocations - ) - - # Check that all tools were called - assert "claude response" in result - assert "codex response" in result - assert "gemini response" in result - - @pytest.mark.asyncio - async def test_batch_with_cli_aliases(self, batch_tool, mock_context): - """Test batch execution with CLI tool aliases.""" - invocations = [ - { - "tool_name": "cc", # Claude Code alias - "input": {"prompt": "Quick analysis"}, - }, - { - "tool_name": "oh", # OpenHands alias - "input": {"prompt": "Build feature"}, - }, - ] - - result = await batch_tool.call( - mock_context, description="Test aliases", invocations=invocations - ) - - assert "cc response" in result - assert "oh response" in result - - @pytest.mark.asyncio - async def test_batch_cli_tools_parallel_execution(self, batch_tool, mock_context): - """Test that CLI tools execute in parallel in batch.""" - - # Add delay to mock executions - for tool in batch_tool.tools.values(): - - async def delayed_response(cmd, **kwargs): - await asyncio.sleep(0.1) # 100ms delay - return f"{tool.name} response" - - tool.execute_cli = delayed_response - - invocations = [ - {"tool_name": "claude", "input": {"prompt": "Task 1"}}, - {"tool_name": "codex", "input": {"prompt": "Task 2"}}, - {"tool_name": "gemini", "input": {"prompt": "Task 3"}}, - {"tool_name": "grok", "input": {"prompt": "Task 4"}}, - ] - - start = time.time() - result = await batch_tool.call( - mock_context, description="Parallel test", invocations=invocations - ) - duration = time.time() - start - - # If executed in parallel, should take ~100ms, not 400ms - assert duration < 0.3 # Allow some overhead - assert all(tool in result for tool in ["claude", "codex", "gemini", "grok"]) - - -# Integration tests for CLI tools have been moved to tests/e2e/test_cli_tools_integration.py -# These tests require the actual CLI tools to be installed and API keys to be set diff --git a/pkg/hanzo-mcp/tests/test_common/__init__.py b/pkg/hanzo-mcp/tests/test_common/__init__.py deleted file mode 100644 index 32d3bf7a4..000000000 --- a/pkg/hanzo-mcp/tests/test_common/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Common test package.""" diff --git a/pkg/hanzo-mcp/tests/test_common/test_hidden_files.py b/pkg/hanzo-mcp/tests/test_common/test_hidden_files.py deleted file mode 100644 index 70b944088..000000000 --- a/pkg/hanzo-mcp/tests/test_common/test_hidden_files.py +++ /dev/null @@ -1,177 +0,0 @@ -"""Tests for hidden files (.dot files) in the permissions module.""" - -import os -from pathlib import Path - -from hanzo_mcp.tools.common.permissions import PermissionManager - - -class TestHiddenFilePermissions: - """Test permission handling for hidden files and directories.""" - - def test_dotfile_exclusion_behavior(self, tool_helper, temp_dir: str): - """Test that dotfiles are properly handled in the permission system. - - This test verifies that files with dots in their names are not incorrectly - excluded just because they contain a substring that matches an excluded pattern. - """ - manager = PermissionManager() - manager.add_allowed_path(temp_dir) - - # Create test paths - problem_path = os.path.join(temp_dir, ".github-workflow-example.yml") - actual_github_dir = os.path.join(temp_dir, ".github", "workflows", "ci.yml") - gitignore_file = os.path.join(temp_dir, ".gitignore") - git_related_file = os.path.join(temp_dir, "git-tutorial.md") - - # Test paths with the fixed implementation - # The workflow example and gitignore files should be allowed - assert manager.is_path_allowed(problem_path), ( - "Should allow .github-workflow-example.yml" - ) - assert manager.is_path_allowed(gitignore_file), "Should allow .gitignore" - assert manager.is_path_allowed(git_related_file), "Should allow git-tutorial.md" - - # Since .git is now allowed by default, this should also be allowed - assert manager.is_path_allowed(actual_github_dir), ( - "Should allow actual .github directory" - ) - - def test_various_hidden_files(self, tool_helper, temp_dir: str): - """Test a variety of hidden files and paths to ensure correct behavior.""" - manager = PermissionManager() - manager.add_allowed_path(temp_dir) - - # Files that should be allowed (not matching default exclusions) - allowed_paths = [ - os.path.join(temp_dir, ".hidden_file.txt"), - os.path.join(temp_dir, "subdir", ".config-example.yml"), - os.path.join(temp_dir, ".env-sample"), - os.path.join(temp_dir, ".gitconfig-user"), - os.path.join(temp_dir, ".github-actions-example.json"), - os.path.join(temp_dir, ".git", "config"), # .git now allowed - ] - - # Files that should be excluded (matching default exclusions) - excluded_paths = [ - # os.path.join(temp_dir, ".git", "config"), # .git now allowed - os.path.join(temp_dir, ".env"), - # .vscode is not in default exclusions, so remove it - # os.path.join(temp_dir, ".vscode", "settings.json"), - os.path.join(temp_dir, "logs", "app.log"), - ] - - # Test allowed paths - for path in allowed_paths: - assert manager.is_path_allowed(path), f"Should allow: {path}" - - # Test excluded paths - for path in excluded_paths: - assert not manager.is_path_allowed(path), f"Should exclude: {path}" - - def test_path_component_matching(self, tool_helper, temp_dir: str): - """Test that path component matching works correctly.""" - manager = PermissionManager() - manager.add_allowed_path(temp_dir) - - # Add a custom exclusion pattern - manager.add_exclusion_pattern("exclude_me") - - # Paths with the pattern as a full component (should be excluded) - full_component_paths = [ - os.path.join(temp_dir, "exclude_me"), - os.path.join(temp_dir, "exclude_me", "file.txt"), - os.path.join(temp_dir, "subdir", "exclude_me", "config.json"), - ] - - # Paths with the pattern as part of a component (should be allowed) - partial_component_paths = [ - os.path.join(temp_dir, "exclude_me_not.txt"), - os.path.join(temp_dir, "not_exclude_me", "file.txt"), - os.path.join(temp_dir, "prefix_exclude_me_suffix.json"), - ] - - # Test full component paths (should be excluded) - for path in full_component_paths: - assert not manager.is_path_allowed(path), ( - f"Should exclude full component: {path}" - ) - - # Test partial component paths (should be allowed) - for path in partial_component_paths: - assert manager.is_path_allowed(path), ( - f"Should allow partial component: {path}" - ) - - def test_wildcard_patterns(self, tool_helper, temp_dir: str): - """Test that wildcard patterns work correctly.""" - manager = PermissionManager() - manager.add_allowed_path(temp_dir) - - # Default patterns include "*.log", "*.key", etc. - - # Files matching wildcard patterns (should be excluded) - wildcard_matches = [ - os.path.join(temp_dir, "server.log"), - os.path.join(temp_dir, "private.key"), - os.path.join(temp_dir, "certificate.crt"), - os.path.join(temp_dir, "database.sqlite"), - ] - - # Files not matching wildcard patterns (should be allowed) - wildcard_non_matches = [ - os.path.join(temp_dir, "logfile.txt"), # Doesn't end with .log - os.path.join(temp_dir, "key_material.txt"), # Doesn't end with .key - os.path.join(temp_dir, "log_analysis.py"), # Doesn't end with .log - ] - - # Test wildcard matching paths (should be excluded) - for path in wildcard_matches: - assert not manager.is_path_allowed(path), ( - f"Should exclude wildcard match: {path}" - ) - - # Test non-matching paths (should be allowed) - for path in wildcard_non_matches: - assert manager.is_path_allowed(path), f"Should allow non-matching: {path}" - - def test_real_world_project_paths(self, tool_helper, temp_dir: str): - """Test with realistic project paths that might be problematic.""" - manager = PermissionManager() - base_dir = "/Users/lijie/project/hanzo-mcp" - manager.add_allowed_path(base_dir) - - # These should all be allowed with the fixed implementation - allowed_project_paths = [ - f"{base_dir}/.github-workflow-example.yml", - f"{base_dir}/.gitignore", - f"{base_dir}/.python-version", - f"{base_dir}/.editorconfig", - f"{base_dir}/.pre-commit-config.yaml", - f"{base_dir}/.env.sample", - f"{base_dir}/.devcontainer/config.json", - ] - - # These should still be excluded (matching system exclusions) - excluded_project_paths = [ - # f"{base_dir}/.git/HEAD", # .git now allowed - # f"{base_dir}/.vscode/settings.json", # .vscode not in default exclusions - f"{base_dir}/.env", - f"{base_dir}/logs/debug.log", - f"{base_dir}/__pycache__/module.pyc", - ] - - # Mock the permissions check to avoid actual filesystem access - # This simulates what would happen with the real project paths - def mock_is_allowed(path): - path_obj = Path(path).resolve() - # Skip the actual "is path in allowed_paths" check for testing - return not manager._is_path_excluded(path_obj) - - # Test allowed project paths - for path in allowed_project_paths: - assert mock_is_allowed(path), f"Should allow project path: {path}" - - # Test excluded project paths - for path in excluded_project_paths: - assert not mock_is_allowed(path), f"Should exclude project path: {path}" diff --git a/pkg/hanzo-mcp/tests/test_common/test_permissions.py b/pkg/hanzo-mcp/tests/test_common/test_permissions.py deleted file mode 100644 index b30e4c10d..000000000 --- a/pkg/hanzo-mcp/tests/test_common/test_permissions.py +++ /dev/null @@ -1,166 +0,0 @@ -"""Tests for the permissions module.""" - -import os -from pathlib import Path - -import pytest - -from hanzo_mcp.tools.common.permissions import ( - PermissibleOperation, - PermissionManager, -) - - -class TestPermissionManager: - """Test the PermissionManager class.""" - - def test_add_allowed_path(self, tool_helper, temp_dir: str): - """Test adding an allowed path.""" - manager = PermissionManager() - manager.add_allowed_path(temp_dir) - - assert Path(temp_dir).resolve() in manager.allowed_paths - - def test_remove_allowed_path(self, tool_helper, temp_dir: str): - """Test removing an allowed path.""" - manager = PermissionManager() - manager.add_allowed_path(temp_dir) - manager.remove_allowed_path(temp_dir) - - assert Path(temp_dir).resolve() not in manager.allowed_paths - - def test_exclude_path(self, tool_helper, temp_dir: str): - """Test excluding a path.""" - manager = PermissionManager() - manager.exclude_path(temp_dir) - - assert Path(temp_dir).resolve() in manager.excluded_paths - - def test_add_exclusion_pattern(self): - """Test adding an exclusion pattern.""" - manager = PermissionManager() - pattern = "secret_*" - manager.add_exclusion_pattern(pattern) - - assert pattern in manager.excluded_patterns - - def test_is_path_allowed_with_allowed_path(self, tool_helper, temp_dir: str): - """Test checking if an allowed path is allowed.""" - manager = PermissionManager() - manager.add_allowed_path(temp_dir) - - test_file = os.path.join(temp_dir, "test.txt") - - assert manager.is_path_allowed(test_file) - - def test_is_path_allowed_with_disallowed_path(self, tool_helper, temp_dir: str): - """Test checking if a disallowed path is allowed.""" - manager = PermissionManager() - - assert manager.is_path_allowed(temp_dir) - - def test_is_path_allowed_with_excluded_path(self, tool_helper, temp_dir: str): - """Test checking if an excluded path is allowed.""" - manager = PermissionManager() - manager.add_allowed_path(temp_dir) - manager.exclude_path(temp_dir) - - assert not manager.is_path_allowed(temp_dir) - - def test_is_path_allowed_with_excluded_pattern(self, tool_helper, temp_dir: str): - """Test checking if a path matching an excluded pattern is allowed.""" - manager = PermissionManager() - manager.add_allowed_path(temp_dir) - - secret_file = os.path.join(temp_dir, "secret_data.txt") - manager.add_exclusion_pattern("secret_") - - assert manager.is_path_allowed(secret_file) - - def test_to_json(self, tool_helper, temp_dir: str): - """Test converting the manager to JSON.""" - manager = PermissionManager() - manager.add_allowed_path(temp_dir) - manager.exclude_path(temp_dir + "/excluded") - manager.add_exclusion_pattern("secret_") - - json_str = manager.to_json() - - assert isinstance(json_str, str) - assert temp_dir in json_str - assert "secret_" in json_str - - def test_from_json(self, tool_helper, temp_dir: str): - """Test creating a manager from JSON.""" - original = PermissionManager() - original.add_allowed_path(temp_dir) - original.exclude_path(temp_dir + "/excluded") - original.add_exclusion_pattern("secret_") - - json_str = original.to_json() - reconstructed = PermissionManager.from_json(json_str) - - # Check that the reconstructed manager has the same state - assert len(reconstructed.allowed_paths) == len(original.allowed_paths) - assert len(reconstructed.excluded_paths) == len(original.excluded_paths) - assert reconstructed.excluded_patterns == original.excluded_patterns - - -class TestPermissibleOperation: - """Test the PermissibleOperation decorator.""" - - @pytest.mark.asyncio - async def test_permissible_operation_with_allowed_path( - self, tool_helper, temp_dir: str - ): - """Test the decorator with an allowed path.""" - manager = PermissionManager() - manager.add_allowed_path(temp_dir) - - # Create a decorated function - @PermissibleOperation(manager, "read") - async def test_func(path): - return f"Read {path}" - - # Call the function - result = await test_func(temp_dir) - - assert result == f"Read {temp_dir}" - - @pytest.mark.asyncio - async def test_permissible_operation_with_custom_path_fn( - self, tool_helper, temp_dir: str - ): - """Test the decorator with a custom path function.""" - manager = PermissionManager() - manager.add_allowed_path(temp_dir) - - # Custom path function - def get_path(args, kwargs): - return kwargs.get("filepath", args[0] if args else None) - - # Create a decorated function - @PermissibleOperation(manager, "read", get_path_fn=get_path) - async def test_func(data, filepath=None): - return f"Read {filepath}" - - # Call the function with a kwarg - result = await test_func("dummy", filepath=temp_dir) - - assert result == f"Read {temp_dir}" - - @pytest.mark.asyncio - async def test_permissible_operation_with_invalid_path( - self, tool_helper, temp_dir: str - ): - """Test the decorator with an invalid path type.""" - manager = PermissionManager() - - # Create a decorated function - @PermissibleOperation(manager, "read") - async def test_func(path): - return f"Read {path}" - - # Call the function with an invalid path type - with pytest.raises(ValueError): - await test_func(123) # Not a string diff --git a/pkg/hanzo-mcp/tests/test_e2e_demo.py b/pkg/hanzo-mcp/tests/test_e2e_demo.py deleted file mode 100644 index 0e26620da..000000000 --- a/pkg/hanzo-mcp/tests/test_e2e_demo.py +++ /dev/null @@ -1,222 +0,0 @@ -"""End-to-end test demonstrating hanzo-mcp with hanzo-network using local inference.""" - -import sys -from pathlib import Path - -import pytest - -# Import guard for optional hanzo_network dependency -try: - # Add hanzo-network to path - sys.path.insert( - 0, str(Path(__file__).parent.parent.parent / "hanzo-network" / "src") - ) - - from hanzo_network import ( - check_local_llm_status, - create_local_agent, - create_local_distributed_network, - create_tool, - ) - - HANZO_NETWORK_AVAILABLE = True -except ImportError: - HANZO_NETWORK_AVAILABLE = False - - -# Skip entire module if hanzo_network is not available -pytestmark = pytest.mark.skipif( - not HANZO_NETWORK_AVAILABLE, - reason="hanzo_network package not installed or numpy not available", -) - - -# Test tools -async def echo_message(message: str) -> str: - """Echo a message.""" - return f"Echo: {message}" - - -async def add_numbers(a: int, b: int) -> str: - """Add two numbers.""" - return f"Result: {a + b}" - - -@pytest.mark.asyncio -async def test_e2e_local_inference(): - """Test end-to-end flow with local hanzo/net inference.""" - - # Check hanzo/net status - status = await check_local_llm_status("hanzo") - assert status["available"] is True - assert status["engine"] == "dummy" # Using dummy for CI - assert "hanzo/net" in status["provider"] - - # Create agents with local inference - echo_agent = create_local_agent( - name="echo_agent", - description="Echoes messages", - system="You are an echo agent. Use the echo_message tool when asked to echo.", - tools=[ - create_tool( - name="echo_message", description="Echo a message", handler=echo_message - ) - ], - local_model="llama3.2", - ) - - math_agent = create_local_agent( - name="math_agent", - description="Does math", - system="You are a math agent. Use the add_numbers tool when asked to add.", - tools=[ - create_tool( - name="add_numbers", description="Add numbers", handler=add_numbers - ) - ], - local_model="llama3.2", - ) - - # Create distributed network - network = create_local_distributed_network( - agents=[echo_agent, math_agent], - name="test-network", - listen_port=16000, - broadcast_port=16000, - ) - - # Start network - await network.start(wait_for_peers=0) - assert network.is_running - - # Test network status - status = network.get_network_status() - # Node ID is auto-generated, just check it exists - assert "node_id" in status - assert status["node_id"].startswith("node-") - assert "echo_agent" in status["local_agents"] - assert "math_agent" in status["local_agents"] - - # Test echo agent - result = await network.run( - prompt="Echo the message 'Hello from E2E test'", initial_agent=echo_agent - ) - assert result["success"] - # The dummy model returns generic responses, so just check for success - assert result["final_output"] is not None - assert len(result["final_output"]) > 0 - - # Test math agent - result = await network.run(prompt="Add 5 and 3", initial_agent=math_agent) - assert result["success"] - # The dummy model returns generic responses, so just check for success - assert result["final_output"] is not None - assert len(result["final_output"]) > 0 - - # Stop network - await network.stop() - assert not network.is_running - - -@pytest.mark.asyncio -async def test_e2e_multi_agent_collaboration(): - """Test multi-agent collaboration with local inference.""" - - # Create collaborative agents - researcher = create_local_agent( - name="researcher", - description="Researches topics", - system="You research and gather information.", - tools=[], - local_model="llama3.2", - ) - - writer = create_local_agent( - name="writer", - description="Writes content", - system="You write clear, concise content.", - tools=[], - local_model="llama3.2", - ) - - # Create network - network = create_local_distributed_network( - agents=[researcher, writer], - name="collab-network", - listen_port=16001, - broadcast_port=16001, - ) - - await network.start(wait_for_peers=0) - - # Test collaboration - result = await network.run( - prompt="Research what distributed inference is and write a brief explanation" - ) - assert result["success"] - - await network.stop() - - -@pytest.mark.asyncio -async def test_e2e_mcp_tools_with_local_llm(): - """Test MCP-style tools with local LLM.""" - - # MCP-style file operations - async def read_test_file(path: str) -> str: - """Read a test file.""" - return f"Contents of {path}: This is a test file." - - async def list_test_files(directory: str) -> str: - """List test files.""" - return f"Files in {directory}: test1.py, test2.py, test3.py" - - # Create file system agent - fs_agent = create_local_agent( - name="fs_agent", - description="File system operations", - system="You handle file system operations using the available tools.", - tools=[ - create_tool( - name="read_test_file", description="Read a file", handler=read_test_file - ), - create_tool( - name="list_test_files", - description="List files", - handler=list_test_files, - ), - ], - local_model="llama3.2", - ) - - # Create network - network = create_local_distributed_network( - agents=[fs_agent], - name="mcp-test-network", - listen_port=16002, - broadcast_port=16002, - ) - - await network.start(wait_for_peers=0) - - # Test file operations - result = await network.run( - prompt="List the files in the test directory", initial_agent=fs_agent - ) - assert result["success"] - # The dummy model returns generic responses, so just check for success - assert result["final_output"] is not None - assert len(result["final_output"]) > 0 - - result = await network.run(prompt="Read the test.py file", initial_agent=fs_agent) - assert result["success"] - # The dummy model returns generic responses, so just check for success - assert result["final_output"] is not None - assert len(result["final_output"]) > 0 - - await network.stop() - - -if __name__ == "__main__": - # Run tests - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_e2e_simple.py b/pkg/hanzo-mcp/tests/test_e2e_simple.py deleted file mode 100644 index 322b0cab4..000000000 --- a/pkg/hanzo-mcp/tests/test_e2e_simple.py +++ /dev/null @@ -1,88 +0,0 @@ -"""Simple E2E test for hanzo-mcp with hanzo-network.""" - -import sys -from pathlib import Path - -# Add hanzo-network to path if needed (though it should be installed) -sys.path.insert(0, str(Path(__file__).parent.parent.parent / "hanzo-network" / "src")) - - -def test_imports(): - """Test that all imports work correctly.""" - # Test hanzo-network imports - # Test hanzo-mcp imports - from hanzo_network import __version__ as network_version - - from hanzo_mcp import __version__ as mcp_version - - assert network_version is not None - assert mcp_version is not None - print( - f"โœ… All imports successful! hanzo-network: {network_version}, hanzo-mcp: {mcp_version}" - ) - - -def test_hanzo_net_provider(): - """Test that hanzo/net provider is available.""" - from hanzo_network.llm import HanzoNetProvider - - provider = HanzoNetProvider("dummy") - assert provider is not None - assert provider.engine_type == "dummy" - print("โœ… HanzoNetProvider created successfully") - - -def test_local_agent_creation(): - """Test creating a local agent.""" - from hanzo_network import create_local_agent, create_tool - - def dummy_tool(text: str) -> str: - return f"Processed: {text}" - - agent = create_local_agent( - name="test_agent", - description="Test agent", - system="You are a test agent", - tools=[ - create_tool( - name="dummy_tool", description="A dummy tool", handler=dummy_tool - ) - ], - local_model="llama3.2", - ) - - assert agent.name == "test_agent" - assert agent.model.provider.value == "local" - assert agent.model.model == "llama3.2" - assert len(agent.tools) == 1 - print("โœ… Local agent created successfully") - - -def test_network_config(): - """Test distributed network configuration.""" - from hanzo_network import create_local_agent, create_local_distributed_network - - agent = create_local_agent( - name="test_agent", description="Test agent", local_model="llama3.2" - ) - - network = create_local_distributed_network( - agents=[agent], name="test-network", listen_port=16100, broadcast_port=16100 - ) - - assert network.name == "test-network" - assert network.node_id is not None # node_id is auto-generated - assert network.listen_port == 16100 - assert len(network.agents) == 1 - print("โœ… Distributed network configured successfully") - - -if __name__ == "__main__": - print("Running E2E tests for hanzo-mcp with hanzo-network...\n") - - test_imports() - test_hanzo_net_provider() - test_local_agent_creation() - test_network_config() - - print("\nโœ… All tests passed! E2E integration working correctly.") diff --git a/pkg/hanzo-mcp/tests/test_error_logging.py b/pkg/hanzo-mcp/tests/test_error_logging.py deleted file mode 100644 index 9365cbe6e..000000000 --- a/pkg/hanzo-mcp/tests/test_error_logging.py +++ /dev/null @@ -1,220 +0,0 @@ -"""Tests for MCP error logging functionality.""" - -from datetime import datetime -from pathlib import Path - -import pytest - -from hanzo_mcp.tools.common.error_logger import ( - MCPErrorLogger, - get_error_logger, - log_call_signature_error, - log_tool_error, -) - - -class TestMCPErrorLogger: - """Test suite for MCPErrorLogger.""" - - def test_logger_initialization(self, tmp_path): - """Test that logger initializes correctly.""" - logger = MCPErrorLogger(log_dir=tmp_path) - - assert logger.log_dir == tmp_path - assert logger.log_dir.exists() - - # Check that log files are created with today's date - today = datetime.now().strftime("%Y-%m-%d") - assert logger.log_file == tmp_path / f"mcp-errors-{today}.log" - assert logger.general_log_file == tmp_path / "errors.log" - - def test_log_tool_error(self, tmp_path): - """Test logging a tool error.""" - logger = MCPErrorLogger(log_dir=tmp_path) - - # Create a test error - test_error = ValueError("Test error message") - params = {"file_path": "/test/path", "limit": 100} - - # Log the error - logger.log_tool_error( - tool_name="read", - error=test_error, - params=params, - context="Testing error logging", - ) - - # Check that log files were created - tool_log = tmp_path / "read-errors.log" - assert tool_log.exists() - - # Read the log and verify content - content = tool_log.read_text() - assert "Test error message" in content - assert "ValueError" in content - assert "Testing error logging" in content - - # Check JSON log - today = datetime.now().strftime("%Y-%m-%d") - json_log = tmp_path / f"tool-errors-{today}.jsonl" - assert json_log.exists() - - def test_log_call_signature_error(self, tmp_path): - """Test logging a call signature error.""" - logger = MCPErrorLogger(log_dir=tmp_path) - - # Create a test error - test_error = TypeError("takes 1 positional argument but 2 were given") - - # Log the error - logger.log_call_signature_error( - tool_name="read", - expected_signature="read(ctx, file_path: str)", - actual_call="read(ctx, '/path/to/file')", - error=test_error, - ) - - # Check that signature errors log was created - sig_log = tmp_path / "signature-errors.log" - assert sig_log.exists() - - # Read the log and verify content - content = sig_log.read_text() - assert "CALL SIGNATURE ERROR" in content - assert "read(ctx, file_path: str)" in content - assert "read(ctx, '/path/to/file')" in content - - def test_sanitize_params(self, tmp_path): - """Test that sensitive parameters are sanitized.""" - logger = MCPErrorLogger(log_dir=tmp_path) - - # Create params with sensitive data - params = { - "file_path": "/test/path", - "api_key": "secret-key-123", - "password": "super-secret", - "token": "bearer-token", - "normal_param": "normal-value", - } - - sanitized = logger._sanitize_params(params) - - # Check that sensitive keys are redacted - assert sanitized["api_key"] == "[REDACTED]" - assert sanitized["password"] == "[REDACTED]" - assert sanitized["token"] == "[REDACTED]" - - # Check that normal params are preserved - assert sanitized["file_path"] == "/test/path" - assert sanitized["normal_param"] == "normal-value" - - def test_get_recent_errors(self, tmp_path): - """Test retrieving recent errors.""" - logger = MCPErrorLogger(log_dir=tmp_path) - - # Log multiple errors - for i in range(5): - error = ValueError(f"Error {i}") - logger.log_tool_error( - tool_name=f"tool_{i}", error=error, params={"index": i} - ) - - # Get recent errors - recent = logger.get_recent_errors(limit=3) - assert len(recent) == 3 - - # Check they're the most recent ones - assert "Error 4" in recent[-1]["error_message"] - assert "Error 3" in recent[-2]["error_message"] - - def test_global_logger(self): - """Test global logger singleton.""" - logger1 = get_error_logger() - logger2 = get_error_logger() - - # Should be the same instance - assert logger1 is logger2 - - def test_convenience_functions(self, tmp_path): - """Test convenience logging functions.""" - # Override global logger for testing - from hanzo_mcp.tools.common import error_logger as el_module - - el_module._global_error_logger = MCPErrorLogger(log_dir=tmp_path) - - # Test log_tool_error - error = RuntimeError("Test runtime error") - log_tool_error("test_tool", error, params={"test": "param"}) - - tool_log = tmp_path / "test_tool-errors.log" - assert tool_log.exists() - assert "Test runtime error" in tool_log.read_text() - - # Test log_call_signature_error - sig_error = TypeError("signature mismatch") - log_call_signature_error( - "test_tool", "expected signature", "actual call", sig_error - ) - - sig_log = tmp_path / "signature-errors.log" - assert sig_log.exists() - - -class TestErrorLoggingIntegration: - """Integration tests for error logging with tools.""" - - @pytest.mark.asyncio - async def test_read_tool_with_error_logging(self, tmp_path): - """Test that ReadTool logs errors correctly.""" - from hanzo_tools.filesystem.read import ReadTool - - from hanzo_mcp.tools.common import error_logger as el_module - from hanzo_mcp.tools.common.permissions import PermissionManager - - # Override global logger - el_module._global_error_logger = MCPErrorLogger(log_dir=tmp_path) - - # Create tool - perm_mgr = PermissionManager() - tool = ReadTool(perm_mgr) - - # Create mock context - class MockContext: - async def info(self, msg): - pass - - async def error(self, msg): - pass - - async def warning(self, msg): - pass - - ctx = MockContext() - - # Try to read non-existent file (should log error if call signature is wrong) - # This simulates the original error where LLM called with positional args - try: - # This would cause an error if called incorrectly - result = await tool.call(ctx, file_path="/nonexistent/file.txt") - # Should return error message, not raise - assert "Error" in result or "does not exist" in result - except TypeError as e: - # If there's a type error, it should be logged - log_tool_error("read", e, context="Integration test") - - # Verify error was logged - read_log = tmp_path / "read-errors.log" - assert read_log.exists() - - -def test_error_logger_creates_directory(): - """Test that error logger creates ~/.hanzo/mcp/logs/ directory.""" - logger = get_error_logger() - - expected_dir = Path.home() / ".hanzo" / "mcp" / "logs" - assert logger.log_dir == expected_dir - assert expected_dir.exists() - - -if __name__ == "__main__": - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_exact_tools_contracts.py b/pkg/hanzo-mcp/tests/test_exact_tools_contracts.py deleted file mode 100644 index 8549cd175..000000000 --- a/pkg/hanzo-mcp/tests/test_exact_tools_contracts.py +++ /dev/null @@ -1,127 +0,0 @@ -from pathlib import Path - -import pytest - -from hanzo_mcp.exact_tools import EditArgs, GuardArgs, GuardRule, HanzoTools, TargetSpec - - -@pytest.mark.asyncio -async def test_targetspec_rejects_unknown_keys(): - with pytest.raises(TypeError): - TargetSpec(target="ws", unexpected=123) # type: ignore[arg-type] - - -@pytest.mark.asyncio -async def test_dry_run_envelope_and_no_fs_changes( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch -): - root = tmp_path - (root / "go.work").write_text("go 1.21\n\nuse (\n ./mod\n)\n") - mod_dir = root / "mod" - mod_dir.mkdir() - (mod_dir / "go.mod").write_text("module example.com/mod\n\ngo 1.21\n") - file_path = mod_dir / "main.go" - file_path.write_text("package main\n\nfunc main() {}\n") - - tools = HanzoTools() - - # Patch LSP bridge to avoid external LSP servers - async def fake_rename(*_args, **_kwargs): - return {"touched_files": [str(file_path)]} - - tools.lsp_bridge.rename_symbol = fake_rename # type: ignore[assignment] - - target = TargetSpec(target=f"file:{file_path}", language="go", dry_run=True) - before = file_path.read_text() - - edit_result = await tools.edit( - target, - EditArgs( - op="rename", - file=str(file_path), - pos={"line": 1, "character": 1}, - new_name="Main", - ), - ) - assert hasattr(edit_result, "ok") - assert hasattr(edit_result, "root") - assert hasattr(edit_result, "language_used") - assert hasattr(edit_result, "backend_used") - assert hasattr(edit_result, "scope_resolved") - assert hasattr(edit_result, "touched_files") - assert hasattr(edit_result, "stdout") - assert hasattr(edit_result, "stderr") - assert hasattr(edit_result, "exit_code") - assert hasattr(edit_result, "errors") - - after = file_path.read_text() - assert before == after - assert edit_result.touched_files == [str(file_path)] - - -@pytest.mark.asyncio -async def test_error_exit_contract(tmp_path: Path): - tools = HanzoTools() - target = TargetSpec(target="ws", root=str(tmp_path), dry_run=True) - result = await tools.edit(target, EditArgs(op="rename")) - assert result.ok is False - assert result.exit_code != 0 - assert len(result.errors) > 0 - - -@pytest.mark.asyncio -async def test_workspace_detection_go_work_priority(tmp_path: Path): - root = tmp_path - (root / "go.work").write_text("go 1.21\n\nuse (\n ./a\n ./b\n)\n") - a = root / "a" - b = root / "b" - a.mkdir() - b.mkdir() - (a / "go.mod").write_text("module example.com/a\n\ngo 1.21\n") - (b / "go.mod").write_text("module example.com/b\n\ngo 1.21\n") - - tools = HanzoTools() - resolved = tools.target_resolver.resolve( - TargetSpec(target="ws", root=str(root), language="go", dry_run=True) - ) - assert resolved["workspace"]["root"] == str(root) - - -@pytest.mark.asyncio -async def test_workspace_detection_root_boundary(tmp_path: Path): - outer = tmp_path / "outer" - inner = outer / "inner" - inner.mkdir(parents=True) - (outer / "go.work").write_text("go 1.21\n\nuse (\n ./inner\n)\n") - (inner / "go.mod").write_text("module example.com/inner\n\ngo 1.21\n") - - tools = HanzoTools() - resolved = tools.target_resolver.resolve( - TargetSpec(target="ws", root=str(inner), language="go", dry_run=True) - ) - assert resolved["workspace"]["root"] == str(inner) - - -@pytest.mark.asyncio -async def test_guard_transitive_go_import(tmp_path: Path): - root = tmp_path - (root / "go.mod").write_text("module example.com/root\n\ngo 1.21\n") - (root / "main.go").write_text( - 'package main\n\nimport "net/http"\n\nfunc main() {}\n' - ) - - tools = HanzoTools() - target = TargetSpec(target=f"dir:{root}", language="go", dry_run=True) - guard = GuardArgs( - rules=[ - GuardRule( - id="no-net-http", - type="import", - glob="**/*.go", - forbid_import_prefix="net/http", - ) - ] - ) - result = await tools.guard(target, guard) - assert result.exit_code == 1 - assert len(result.errors) > 0 diff --git a/pkg/hanzo-mcp/tests/test_failure_cases.py b/pkg/hanzo-mcp/tests/test_failure_cases.py deleted file mode 100644 index 07effc3f2..000000000 --- a/pkg/hanzo-mcp/tests/test_failure_cases.py +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env python3 -"""Test failure cases for swarm and agent tools.""" - -import os - - -def test_no_api_key(): - """Test behavior when no API key is present.""" - print("Test: No API Key") - print("-" * 40) - - # Remove API keys from environment - original_keys = {} - for key in ["ANTHROPIC_API_KEY", "CLAUDE_API_KEY", "OPENAI_API_KEY"]: - if key in os.environ: - original_keys[key] = os.environ.pop(key) - - try: - # Try to create swarm without API key - api_key = os.environ.get("ANTHROPIC_API_KEY") or os.environ.get( - "CLAUDE_API_KEY" - ) - - if not api_key: - print("โœ“ Correctly detected missing API key") - print(" Swarm would use model but agent calls would fail") - else: - print("โœ— Unexpectedly found API key") - - finally: - # Restore keys - for key, value in original_keys.items(): - os.environ[key] = value - - print() - - -def test_invalid_task_format(): - """Test invalid task formats.""" - print("Test: Invalid Task Formats") - print("-" * 40) - - invalid_tasks = [ - (None, "None value"), - ("string", "Plain string instead of dict"), - ([], "Empty list"), - ({}, "Empty dict"), - ({"instructions": "No file_path"}, "Missing file_path"), - ({"file_path": "/test.py"}, "Missing instructions"), - ({"file_path": "", "instructions": "Empty path"}, "Empty file_path"), - ({"file_path": "/test.py", "instructions": ""}, "Empty instructions"), - ] - - for task, description in invalid_tasks: - # Validate task - is_valid = ( - isinstance(task, dict) - and "file_path" in task - and "instructions" in task - and task["file_path"] - and task["instructions"] - ) - - if is_valid: - print(f"โœ— {description}: Should be invalid but passed") - else: - print(f"โœ“ {description}: Correctly identified as invalid") - - print() - - -def test_permission_denied(): - """Test file access permission errors.""" - print("Test: Permission Denied") - print("-" * 40) - - # Test paths that should be blocked - blocked_paths = [ - "/etc/passwd", - "/root/.ssh/id_rsa", - "~/.aws/credentials", - "/System/Library/", - "C:\\Windows\\System32\\", - ] - - for path in blocked_paths: - # In real usage, PermissionManager would block these - print(f"โœ“ Would block access to: {path}") - - print() - - -def test_concurrent_limit(): - """Test that concurrent execution limits work.""" - print("Test: Concurrent Execution Limits") - print("-" * 40) - - # Simulate task queue - total_tasks = 10 - max_concurrent = 3 - - print(f"Total tasks: {total_tasks}") - print(f"Max concurrent: {max_concurrent}") - - # Simulate batched execution - batches = [] - for i in range(0, total_tasks, max_concurrent): - batch = list(range(i, min(i + max_concurrent, total_tasks))) - batches.append(batch) - print(f" Batch {len(batches)}: Tasks {batch}") - - expected_batches = (total_tasks + max_concurrent - 1) // max_concurrent - if len(batches) == expected_batches: - print(f"โœ“ Correctly split into {len(batches)} batches") - else: - print(f"โœ— Expected {expected_batches} batches, got {len(batches)}") - - print() - - -def test_large_response_handling(): - """Test handling of responses that exceed token limits.""" - print("Test: Large Response Handling") - print("-" * 40) - - # Simulate a large response - large_text = "x" * 100000 # 100k characters - max_tokens = 25000 - chars_per_token = 4 # Rough estimate - max_chars = max_tokens * chars_per_token - - if len(large_text) > max_chars: - print(f"โœ“ Response ({len(large_text)} chars) exceeds limit ({max_chars} chars)") - print(" Would trigger pagination") - - # Calculate pages needed - pages_needed = (len(large_text) + max_chars - 1) // max_chars - print(f" Would create {pages_needed} pages") - else: - print("โœ— Response fits within limit") - - print() - - -def test_error_recovery(): - """Test error recovery scenarios.""" - print("Test: Error Recovery") - print("-" * 40) - - error_scenarios = [ - ("Network timeout", "Would retry with exponential backoff"), - ("Model overloaded", "Would fallback to alternative model"), - ("Invalid response format", "Would request clarification"), - ("File not found", "Would report error gracefully"), - ("Permission denied", "Would skip file and continue"), - ] - - for error, recovery in error_scenarios: - print(f"โœ“ {error}: {recovery}") - - print() - - -def test_edge_cases(): - """Test various edge cases.""" - print("Test: Edge Cases") - print("-" * 40) - - edge_cases = [ - ("Empty file", "Should handle gracefully"), - ("Binary file", "Should detect and skip"), - ("Symbolic link", "Should resolve or skip"), - ("File with no write permission", "Should report error"), - ("Very long file path", "Should handle up to OS limit"), - ("Unicode in file names", "Should support UTF-8"), - ("Concurrent file access", "Should use file locking"), - ] - - for case, expected in edge_cases: - print(f"โœ“ {case}: {expected}") - - print() - - -def main(): - """Run all failure case tests.""" - print("=" * 60) - print("FAILURE CASE TEST SUITE") - print("=" * 60) - print() - - tests = [ - test_no_api_key, - test_invalid_task_format, - test_permission_denied, - test_concurrent_limit, - test_large_response_handling, - test_error_recovery, - test_edge_cases, - ] - - for test in tests: - try: - test() - except Exception as e: - print(f"โœ— {test.__name__} failed with error: {e}") - print() - - print("=" * 60) - print("All failure cases tested!") - print("=" * 60) - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-mcp/tests/test_filesystem/README.md b/pkg/hanzo-mcp/tests/test_filesystem/README.md deleted file mode 100644 index ff9b59267..000000000 --- a/pkg/hanzo-mcp/tests/test_filesystem/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# Filesystem Tool Tests - -This directory contains tests for the filesystem tools in the Hanzo MCP project. - -## Test Files - -- `test_fs_tools.py`: Tests for the refactored filesystem tools -- `test_file_operations.py`: Tests for various file operations -- `test_write_file.py`: Comprehensive tests for the write file tool - -## Write File Tool Testing - -The `test_write_file.py` file contains comprehensive tests for the write file tool, including: - -### Success Cases -- Writing files with standard content -- Creating nested directories -- Writing files with Unicode content -- Overwriting existing files -- Writing large files - -### Error Cases -- Missing path parameter -- Empty path parameter -- Missing content parameter -- Path not allowed -- Parent directory not allowed -- Write permission issues -- Encoding errors -- I/O errors - -## Running Tests - -To run all filesystem tests: - -```bash -make test TEST_DIR=tests/test_filesystem -``` - -To run a specific test file: - -```bash -make test TEST_DIR=tests/test_filesystem/test_write_file.py -``` - -## Adding New Tests - -When adding new tests for filesystem tools, follow these guidelines: - -1. Use appropriate fixtures for setup and teardown -2. Mock external dependencies and tool context -3. Test both success and error paths -4. Verify file content and filesystem state after operations -5. Clean up temporary files after tests -6. Add tests for various edge cases (permissions, encoding, large files, etc.) - -## Error Handling - -The write_file tool has been enhanced with improved error handling: - -1. Error categorization: Different types of errors are caught and handled appropriately -2. Detailed error messages: Error messages include specific information about what went wrong -3. Logging: All errors are logged with appropriate severity levels -4. Error propagation: Errors are properly returned to the client - -## Debug Logging - -To enable debug logging during tests: - -```bash -make test TEST_DIR=tests/test_filesystem LOG_LEVEL=DEBUG -``` - -This will provide detailed logs of the test execution, including input parameters, error conditions, and results. diff --git a/pkg/hanzo-mcp/tests/test_filesystem/__init__.py b/pkg/hanzo-mcp/tests/test_filesystem/__init__.py deleted file mode 100644 index 71c022b01..000000000 --- a/pkg/hanzo-mcp/tests/test_filesystem/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Filesystem tools test package.""" diff --git a/pkg/hanzo-mcp/tests/test_filesystem/test_ast_simple.py b/pkg/hanzo-mcp/tests/test_filesystem/test_ast_simple.py deleted file mode 100644 index 290bf1f22..000000000 --- a/pkg/hanzo-mcp/tests/test_filesystem/test_ast_simple.py +++ /dev/null @@ -1,14 +0,0 @@ -"""Simple test for the Grep AST tool.""" - -import pytest - - -def test_simple(): - """Simple test to check if test collection works.""" - assert True - - -@pytest.mark.asyncio -async def test_async_simple(): - """Simple async test to check if test collection works.""" - assert True diff --git a/pkg/hanzo-mcp/tests/test_filesystem/test_ast_tool.py b/pkg/hanzo-mcp/tests/test_filesystem/test_ast_tool.py deleted file mode 100644 index 5455fef7f..000000000 --- a/pkg/hanzo-mcp/tests/test_filesystem/test_ast_tool.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Tests for the Symbols tool.""" - -import pytest -from hanzo_tools.filesystem import ASTTool as SymbolsTool - - -def test_symbols_simple(): - """Simple test to verify collection works.""" - assert True - - -@pytest.mark.asyncio -async def test_symbols_import(): - """Test that the SymbolsTool can be imported.""" - assert SymbolsTool is not None diff --git a/pkg/hanzo-mcp/tests/test_filesystem/test_fs_tools.py b/pkg/hanzo-mcp/tests/test_filesystem/test_fs_tools.py deleted file mode 100644 index dc9f50178..000000000 --- a/pkg/hanzo-mcp/tests/test_filesystem/test_fs_tools.py +++ /dev/null @@ -1,152 +0,0 @@ -"""Tests for the refactored filesystem tools.""" - -import os -from typing import TYPE_CHECKING -from unittest.mock import AsyncMock, MagicMock, patch - -import pytest - -if TYPE_CHECKING: - from hanzo_mcp.tools.common.permissions import PermissionManager - -from hanzo_tools.filesystem import Edit, ReadTool, Write, get_filesystem_tools - - -class TestRefactoredFileTools: - """Test the refactored filesystem tools.""" - - @pytest.fixture - def fs_tools( - self, - permission_manager: "PermissionManager", - ): - """Create filesystem tool instances for testing.""" - return get_filesystem_tools(permission_manager) - - @pytest.fixture - def read_files_tool( - self, - permission_manager: "PermissionManager", - ): - """Create a ReadTool instance for testing.""" - return ReadTool(permission_manager) - - @pytest.fixture - def write_file_tool( - self, - permission_manager: "PermissionManager", - ): - """Create a Write instance for testing.""" - return Write(permission_manager) - - @pytest.fixture - def edit_file_tool( - self, - permission_manager: "PermissionManager", - ): - """Create an Edit instance for testing.""" - return Edit(permission_manager) - - @pytest.fixture - def setup_allowed_path( - self, - permission_manager: "PermissionManager", - temp_dir: str, - ): - """Set up an allowed path for testing.""" - permission_manager.add_allowed_path(temp_dir) - return temp_dir - - @pytest.mark.asyncio - async def test_read_files_single_allowed( - self, - tool_helper, - read_files_tool: ReadTool, - setup_allowed_path: str, - test_file: str, - mcp_context: MagicMock, - ): - """Test reading a single allowed file with the refactored tool.""" - # Mock context calls - tool_ctx = AsyncMock() - with patch( - "hanzo_tools.filesystem.base.create_tool_context", - return_value=tool_ctx, - ): - # Call the tool directly - result = await read_files_tool.call(ctx=mcp_context, file_path=test_file) - - # Verify result - tool_helper.assert_in_result("This is a test file content", result) - tool_ctx.info.assert_called() - - @pytest.mark.asyncio - async def test_write_file( - self, - tool_helper, - write_file_tool: Write, - setup_allowed_path: str, - mcp_context: MagicMock, - ): - """Test writing a file with the refactored tool.""" - # Create a test path within allowed path - test_path = os.path.join(setup_allowed_path, "write_test.txt") - test_content = "Test content for writing" - - # Mock context calls - tool_ctx = AsyncMock() - with patch( - "hanzo_tools.filesystem.base.create_tool_context", - return_value=tool_ctx, - ): - # Call the tool directly - result = await write_file_tool.call( - ctx=mcp_context, file_path=test_path, content=test_content - ) - - # Verify result - tool_helper.assert_in_result("Successfully wrote file", result) - tool_ctx.info.assert_called() - - # Verify file was written - assert os.path.exists(test_path) - with open(test_path, "r") as f: - assert f.read() == test_content - - @pytest.mark.asyncio - async def test_edit_file( - self, - tool_helper, - edit_file_tool: Edit, - setup_allowed_path: str, - test_file: str, - mcp_context: MagicMock, - ): - """Test editing a file with the refactored tool.""" - # Set up edit parameters - old_string = "This is a test file content." - new_string = "This is modified content." - - # Mock context calls - tool_ctx = AsyncMock() - with patch( - "hanzo_tools.filesystem.base.create_tool_context", - return_value=tool_ctx, - ): - # Call the tool directly - result = await edit_file_tool.call( - ctx=mcp_context, - file_path=test_file, - old_string=old_string, - new_string=new_string, - expected_replacements=1, - ) - - # Verify result - tool_helper.assert_in_result("Successfully edited file", result) - tool_ctx.info.assert_called() - - # Verify file was modified - with open(test_file, "r") as f: - content = f.read() - assert "This is modified content." in content diff --git a/pkg/hanzo-mcp/tests/test_find_tool_ffind.py b/pkg/hanzo-mcp/tests/test_find_tool_ffind.py deleted file mode 100644 index 70c2a93b6..000000000 --- a/pkg/hanzo-mcp/tests/test_find_tool_ffind.py +++ /dev/null @@ -1,346 +0,0 @@ -"""Test FindTool with ffind for performance.""" - -import tempfile -import time -from pathlib import Path - -import pytest -from hanzo_mcp.tools.search.find_tool import FFIND_AVAILABLE, FindTool - - -class TestFindToolFFind: - """Test FindTool ffind functionality and performance.""" - - @pytest.fixture - def find_tool(self): - """Create FindTool instance.""" - return FindTool() - - @pytest.fixture - def test_directory(self): - """Create a test directory structure with many files.""" - with tempfile.TemporaryDirectory() as tmpdir: - # Create a realistic directory structure - # src/ - # โ”œโ”€โ”€ main.py - # โ”œโ”€โ”€ utils.py - # โ”œโ”€โ”€ config.json - # โ””โ”€โ”€ modules/ - # โ”œโ”€โ”€ auth.py - # โ”œโ”€โ”€ database.py - # โ””โ”€โ”€ api/ - # โ”œโ”€โ”€ routes.py - # โ”œโ”€โ”€ handlers.py - # โ””โ”€โ”€ middleware.py - # tests/ - # โ”œโ”€โ”€ test_main.py - # โ”œโ”€โ”€ test_utils.py - # โ””โ”€โ”€ fixtures/ - # โ””โ”€โ”€ data.json - # docs/ - # โ”œโ”€โ”€ README.md - # โ”œโ”€โ”€ API.md - # โ””โ”€โ”€ images/ - # โ”œโ”€โ”€ logo.png - # โ””โ”€โ”€ diagram.svg - # node_modules/ (should be ignored) - # โ””โ”€โ”€ package/ - # โ””โ”€โ”€ index.js - # .git/ (should be ignored) - # โ””โ”€โ”€ config - - # Create directories - dirs = [ - "src", - "src/modules", - "src/modules/api", - "tests", - "tests/fixtures", - "docs", - "docs/images", - "node_modules", - "node_modules/package", - ".git", - "__pycache__", - ".venv", - ] - - for dir_name in dirs: - Path(tmpdir, dir_name).mkdir(parents=True, exist_ok=True) - - # Create files with different sizes and content - files = { - "src/main.py": "# Main application\nimport sys\n\ndef main():\n pass\n" - * 100, - "src/utils.py": "# Utilities\ndef helper():\n return 'TODO: implement'\n", - "src/config.json": '{"debug": true, "port": 8080}\n', - "src/modules/auth.py": "# Authentication module\nclass Auth:\n pass\n", - "src/modules/database.py": "# Database module\nimport sqlite3\n" * 50, - "src/modules/api/routes.py": "# API routes\nfrom flask import Flask\n", - "src/modules/api/handlers.py": "# Request handlers\ndef handle_request():\n pass\n", - "src/modules/api/middleware.py": "# Middleware\ndef auth_middleware():\n pass\n", - "tests/test_main.py": "# Test main\nimport pytest\ndef test_main():\n assert True\n", - "tests/test_utils.py": "# Test utils\ndef test_helper():\n pass\n", - "tests/fixtures/data.json": '{"test": "data"}\n', - "docs/README.md": "# Project README\n\nTODO: Write documentation\n", - "docs/API.md": "# API Documentation\n\n## Endpoints\n\n### GET /api/users\n", - "docs/images/logo.png": b"PNG fake image data" * 1000, # Binary file - "docs/images/diagram.svg": "TODO: Add diagram\n", - "node_modules/package/index.js": "module.exports = {};\n", - ".git/config": "[core]\nrepositoryformatversion = 0\n", - "__pycache__/cache.pyc": b"Python bytecode", - ".gitignore": "node_modules/\n__pycache__/\n*.pyc\n.venv/\n", - "large_file.dat": "x" * 1024 * 1024, # 1MB file - } - - for file_path, content in files.items(): - full_path = Path(tmpdir, file_path) - if isinstance(content, bytes): - full_path.write_bytes(content) - else: - full_path.write_text(content) - - yield tmpdir - - def test_ffind_availability(self): - """Test if ffind is available.""" - print(f"\nffind available: {FFIND_AVAILABLE}") - # Don't fail if ffind is not installed, just report it - if not FFIND_AVAILABLE: - pytest.skip("ffind not installed - skipping performance tests") - - @pytest.mark.asyncio - async def test_find_all_python_files(self, tool_helper, find_tool, test_directory): - """Test finding all Python files.""" - if not FFIND_AVAILABLE: - pytest.skip("ffind not installed - skipping ffind-specific tests") - - result = await find_tool.run(pattern="*.py", path=test_directory, type="file") - - # Check results - assert "results" in result.data - results = result.data["results"] - - # Should find Python files - py_files = [r["name"] for r in results] - assert "main.py" in py_files - assert "utils.py" in py_files - assert "auth.py" in py_files - assert "test_main.py" in py_files - - # Should not find non-Python files - assert "config.json" not in py_files - assert "README.md" not in py_files - - # Should respect gitignore (no __pycache__) - assert "cache.pyc" not in py_files - - @pytest.mark.asyncio - async def test_find_with_size_filter(self, tool_helper, find_tool, test_directory): - """Test finding files by size.""" - # Find large files (> 500KB) - result = await find_tool.run( - pattern="*", path=test_directory, type="file", min_size="500KB" - ) - - results = result.data["results"] - # Should only find large_file.dat - assert len(results) == 1 - assert results[0]["name"] == "large_file.dat" - - @pytest.mark.asyncio - async def test_find_directories(self, tool_helper, find_tool, test_directory): - """Test finding directories.""" - result = await find_tool.run( - pattern="*", - path=test_directory, - type="dir", - max_depth=0, # Only immediate children - ) - - results = result.data["results"] - dir_names = [r["name"] for r in results] - - # Should find top-level directories - assert "src" in dir_names - assert "tests" in dir_names - assert "docs" in dir_names - - # Should not find nested directories (max_depth=0) - assert "modules" not in dir_names - assert "api" not in dir_names - - @pytest.mark.asyncio - async def test_find_with_pattern_in_content( - self, tool_helper, find_tool, test_directory - ): - """Test finding files containing specific text.""" - result = await find_tool.run( - pattern="TODO", path=test_directory, in_content=True, type="file" - ) - - results = result.data["results"] - file_names = [r["name"] for r in results] - - # Should find files containing "TODO" - assert "utils.py" in file_names # Has TODO in content - assert "README.md" in file_names # Has TODO in content - assert "diagram.svg" in file_names # Has TODO in content - - # Should not find files without TODO - assert "main.py" not in file_names - assert "auth.py" not in file_names - - @pytest.mark.asyncio - async def test_pagination(self, tool_helper, find_tool, test_directory): - """Test pagination functionality.""" - # Get first page - result_page1 = await find_tool.run( - pattern="*", - path=test_directory, - type="file", - page_size=5, - page=1, - sort_by="name", - ) - - page1_data = result_page1.data - assert len(page1_data["results"]) == 5 - assert page1_data["pagination"]["page"] == 1 - assert page1_data["pagination"]["has_next"] - - # Get second page - result_page2 = await find_tool.run( - pattern="*", - path=test_directory, - type="file", - page_size=5, - page=2, - sort_by="name", - ) - - page2_data = result_page2.data - assert page2_data["pagination"]["page"] == 2 - - # Results should be different - page1_names = [r["name"] for r in page1_data["results"]] - page2_names = [r["name"] for r in page2_data["results"]] - assert set(page1_names).isdisjoint(set(page2_names)) - - @pytest.mark.asyncio - async def test_performance_comparison(self, tool_helper, find_tool, test_directory): - """Compare performance with and without ffind.""" - if not FFIND_AVAILABLE: - pytest.skip("ffind not installed - skipping performance comparison") - # Force Python implementation - import hanzo_mcp.tools.search.find_tool as find_module - - original_ffind = find_module.FFIND_AVAILABLE - find_module.FFIND_AVAILABLE = False - find_tool._available_backends = None - - start_time = time.time() - result_python = await find_tool.run( - pattern="*.py", path=test_directory, type="file" - ) - python_time = time.time() - start_time - - # Restore ffind if available - find_module.FFIND_AVAILABLE = original_ffind - find_tool._available_backends = None - - if FFIND_AVAILABLE: - start_time = time.time() - result_ffind = await find_tool.run( - pattern="*.py", path=test_directory, type="file" - ) - ffind_time = time.time() - start_time - - print("\nPerformance comparison:") - print(f"Python implementation: {python_time:.3f}s") - print(f"ffind implementation: {ffind_time:.3f}s") - print(f"Speedup: {python_time / ffind_time:.1f}x") - - # Results should be the same - assert len(result_python.data["results"]) == len( - result_ffind.data["results"] - ) - - @pytest.mark.asyncio - async def test_fuzzy_search(self, tool_helper, find_tool, test_directory): - """Test fuzzy pattern matching.""" - result = await find_tool.run( - pattern="tst", # Fuzzy match for "test" - path=test_directory, - fuzzy=True, - type="file", - ) - - results = result.data["results"] - file_names = [r["name"] for r in results] - - # Should find test files with fuzzy matching - assert any("test" in name for name in file_names) - - @pytest.mark.asyncio - async def test_case_insensitive_search( - self, tool_helper, find_tool, test_directory - ): - """Test case-insensitive search.""" - result = await find_tool.run( - pattern="*.MD", # Uppercase extension - path=test_directory, - case_sensitive=False, - type="file", - ) - - results = result.data["results"] - file_names = [r["name"] for r in results] - - # Should find .md files despite case difference - assert "README.md" in file_names - assert "API.md" in file_names - - @pytest.mark.asyncio - async def test_statistics(self, tool_helper, find_tool, test_directory): - """Test that statistics are included in results.""" - result = await find_tool.run(pattern="*.py", path=test_directory) - - stats = result.data.get("statistics") - assert stats is not None - assert "total_found" in stats - assert "search_time_ms" in stats - assert "search_method" in stats - assert stats["search_method"] in ["ffind", "python"] - assert stats["search_time_ms"] > 0 - - @pytest.mark.asyncio - async def test_gitignore_respect(self, tool_helper, find_tool, test_directory): - """Test that .gitignore patterns are respected.""" - result = await find_tool.run( - pattern="*", path=test_directory, respect_gitignore=True - ) - - results = result.data["results"] - file_paths = [r["path"] for r in results] - - # Should not include gitignored files/dirs - assert not any("node_modules" in path for path in file_paths) - assert not any("__pycache__" in path for path in file_paths) - assert not any(".pyc" in path for path in file_paths) - - # Test with gitignore disabled - result_no_ignore = await find_tool.run( - pattern="*", path=test_directory, respect_gitignore=False - ) - - results_no_ignore = result_no_ignore.data["results"] - file_paths_no_ignore = [r["path"] for r in results_no_ignore] - - # Should include gitignored files when disabled - assert any("node_modules" in path for path in file_paths_no_ignore) - - -if __name__ == "__main__": - # Run tests - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_find_tool_integration.py b/pkg/hanzo-mcp/tests/test_find_tool_integration.py deleted file mode 100644 index e99c30e1a..000000000 --- a/pkg/hanzo-mcp/tests/test_find_tool_integration.py +++ /dev/null @@ -1,311 +0,0 @@ -"""Integration test for FindTool registration and functionality.""" - -import asyncio -import os -import tempfile -import time -from pathlib import Path - -import pytest -from hanzo_mcp.tools.search import create_find_tool - -from hanzo_mcp.server import HanzoMCPServer -from tests.test_utils import ToolTestHelper - - -@pytest.fixture -def tool_helper(): - """Get the tool test helper.""" - return ToolTestHelper - - -async def test_find_tool_direct_usage(tool_helper): - """Test FindTool can be used directly.""" - # Create a temporary directory with test files - with tempfile.TemporaryDirectory() as tmpdir: - # Create test file structure - test_files = { - "readme.md": "# Test Project", - "main.py": "def main():\n pass", - "test.py": "import pytest\n\ndef test_example():\n assert True", - "utils.py": "def helper():\n return 42", - "data.json": '{"key": "value"}', - "config.yaml": "debug: true", - "large.txt": "x" * 100000, # 100KB file - ".hidden": "hidden file", - "subdir/nested.py": "# Nested file", - "subdir/deep/very_deep.txt": "Deep file content", - } - - for filepath, content in test_files.items(): - file_path = Path(tmpdir) / filepath - file_path.parent.mkdir(parents=True, exist_ok=True) - file_path.write_text(content) - - # Test 1: Basic pattern matching - find_tool = create_find_tool() - - result = await find_tool.run(pattern="*.py", path=tmpdir) - - assert result.data is not None - assert "results" in result.data - assert "statistics" in result.data - - py_files = result.data["results"] - py_names = [f["name"] for f in py_files] - assert "main.py" in py_names - assert "test.py" in py_names - assert "utils.py" in py_names - assert "nested.py" in py_names - - # Test 2: Regex pattern - result = await find_tool.run(pattern="^test", path=tmpdir, regex=True) - - test_files = result.data["results"] - test_names = [f["name"] for f in test_files] - assert "test.py" in test_names - assert "main.py" not in test_names - - # Test 3: Size filters - result = await find_tool.run(pattern="*", path=tmpdir, min_size="50KB") - - large_files = result.data["results"] - assert len(large_files) == 1 - assert large_files[0]["name"] == "large.txt" - - # Test 4: Type filter - result = await find_tool.run(pattern="*", path=tmpdir, type="file") - - files = result.data["results"] - assert all(not f.get("is_dir", False) for f in files) - - # Test 5: Fuzzy search - result = await find_tool.run( - pattern="utls", # Misspelled "utils" - path=tmpdir, - fuzzy=True, - ) - - fuzzy_results = result.data["results"] - [f["name"] for f in fuzzy_results] - # TODO: Fix fuzzy search with ffind or use Python implementation - # assert "utils.py" in fuzzy_names - # For now, just check that the search completes without error - assert isinstance(fuzzy_results, list) - - # Test 6: Case sensitivity - result = await find_tool.run(pattern="*.PY", path=tmpdir, case_sensitive=True) - - case_results = result.data["results"] - assert len(case_results) == 0 # No .PY files - - result = await find_tool.run(pattern="*.PY", path=tmpdir, case_sensitive=False) - - case_insensitive = result.data["results"] - assert len(case_insensitive) > 0 # Should find .py files - - # Test 7: Pagination - result = await find_tool.run(pattern="*", path=tmpdir, page_size=3, page=1) - - page1 = result.data["results"] - pagination = result.data["pagination"] - assert len(page1) <= 3 - assert pagination["page"] == 1 - assert pagination["page_size"] == 3 - - if pagination["has_next"]: - result = await find_tool.run(pattern="*", path=tmpdir, page_size=3, page=2) - - page2 = result.data["results"] - assert page2 != page1 # Different results - - -async def test_find_tool_server_integration(tool_helper): - """Test FindTool is properly registered in the server.""" - # Create a test server with a temporary directory - with tempfile.TemporaryDirectory() as tmpdir: - # Create some test files - (Path(tmpdir) / "test1.py").write_text("print('test1')") - (Path(tmpdir) / "test2.txt").write_text("test content") - (Path(tmpdir) / "data.json").write_text('{"test": true}') - - # Create server with the temp directory as allowed path - server = HanzoMCPServer( - name="test-server", - allowed_paths=[tmpdir], - disable_search_tools=False, # Ensure search tools are enabled - ) - - # The server should have registered tools - # We can verify by trying to use the find tool directly - from hanzo_mcp.tools.search import create_find_tool - - # Create the find tool directly to test - find_tool = create_find_tool() - - # Test that we can use it with the allowed path - result = await find_tool.run(pattern="*.py", path=tmpdir) - - assert result.data is not None - assert "results" in result.data - assert len(result.data["results"]) == 1 - assert result.data["results"][0]["name"] == "test1.py" - - # Verify search tools weren't disabled - assert not server.disable_search_tools - - -async def test_find_tool_advanced_filters(tool_helper): - """Test advanced filtering capabilities.""" - with tempfile.TemporaryDirectory() as tmpdir: - # Create files with different timestamps - now = time.time() - old_time = now - (3 * 24 * 60 * 60) # 3 days ago - recent_time = now - (60 * 60) # 1 hour ago - - files = { - "old_file.txt": old_time, - "recent_file.txt": recent_time, - "new_file.txt": now, - } - - for filename, mtime in files.items(): - filepath = Path(tmpdir) / filename - filepath.write_text(f"Content of {filename}") - os.utime(filepath, (mtime, mtime)) - - find_tool = create_find_tool() - - # Test modified_after filter - result = await find_tool.run( - pattern="*.txt", path=tmpdir, modified_after="2 days ago" - ) - - recent_files = result.data["results"] - recent_names = [f["name"] for f in recent_files] - assert "old_file.txt" not in recent_names - assert "recent_file.txt" in recent_names - assert "new_file.txt" in recent_names - - # Test modified_before filter - result = await find_tool.run( - pattern="*.txt", path=tmpdir, modified_before="30 minutes ago" - ) - - older_files = result.data["results"] - older_names = [f["name"] for f in older_files] - assert "new_file.txt" not in older_names - assert "recent_file.txt" in older_names - assert "old_file.txt" in older_names - - # Test sorting - result = await find_tool.run(pattern="*.txt", path=tmpdir, sort_by="modified") - - sorted_files = result.data["results"] - # Should be sorted by modification time (oldest first) - assert sorted_files[0]["name"] == "old_file.txt" - assert sorted_files[-1]["name"] == "new_file.txt" - - # Test reverse sorting - result = await find_tool.run( - pattern="*.txt", path=tmpdir, sort_by="modified", reverse=True - ) - - reverse_sorted = result.data["results"] - assert reverse_sorted[0]["name"] == "new_file.txt" - assert reverse_sorted[-1]["name"] == "old_file.txt" - - -async def test_find_tool_error_handling(tool_helper): - """Test error handling in FindTool.""" - find_tool = create_find_tool() - - # Test with non-existent path - result = await find_tool.run(pattern="*.py", path="/non/existent/path") - - # Should handle gracefully - assert result.data is not None - assert "results" in result.data - assert result.data["results"] == [] - if "statistics" in result.data: - assert result.data["statistics"]["total_found"] == 0 - - # Test with invalid pattern (if regex enabled) - result = await find_tool.run(pattern="[invalid regex", path=".", regex=True) - - # Should handle invalid regex gracefully - assert result.data is not None - - -async def test_find_tool_gitignore_respect(tool_helper): - """Test that FindTool respects .gitignore by default.""" - with tempfile.TemporaryDirectory() as tmpdir: - # Create .gitignore - gitignore_content = """ -*.log -__pycache__/ -node_modules/ -.env -""" - (Path(tmpdir) / ".gitignore").write_text(gitignore_content) - - # Create files that should be ignored - (Path(tmpdir) / "app.log").write_text("log content") - (Path(tmpdir) / ".env").write_text("SECRET=value") - (Path(tmpdir) / "__pycache__").mkdir() - (Path(tmpdir) / "__pycache__" / "module.pyc").write_text("bytecode") - - # Create files that should NOT be ignored - (Path(tmpdir) / "main.py").write_text("print('hello')") - (Path(tmpdir) / "README.md").write_text("# Project") - - find_tool = create_find_tool() - - # Default: respect gitignore - result = await find_tool.run(pattern="*", path=tmpdir, respect_gitignore=True) - - found_names = [f["name"] for f in result.data["results"]] - assert "main.py" in found_names - assert "README.md" in found_names - assert ".gitignore" in found_names # .gitignore itself is included - assert "app.log" not in found_names - assert ".env" not in found_names - assert "__pycache__" not in found_names - - # Test with gitignore disabled - result = await find_tool.run(pattern="*", path=tmpdir, respect_gitignore=False) - - all_names = [f["name"] for f in result.data["results"]] - assert "app.log" in all_names - assert ".env" in all_names - - -if __name__ == "__main__": - # Run the tests - try: - print("Running test_find_tool_direct_usage...") - asyncio.run(test_find_tool_direct_usage(ToolTestHelper)) - print("โœ“ test_find_tool_direct_usage passed") - - print("\nRunning test_find_tool_server_integration...") - asyncio.run(test_find_tool_server_integration(ToolTestHelper)) - print("โœ“ test_find_tool_server_integration passed") - - print("\nRunning test_find_tool_advanced_filters...") - asyncio.run(test_find_tool_advanced_filters(ToolTestHelper)) - print("โœ“ test_find_tool_advanced_filters passed") - - print("\nRunning test_find_tool_error_handling...") - asyncio.run(test_find_tool_error_handling(ToolTestHelper)) - print("โœ“ test_find_tool_error_handling passed") - - print("\nRunning test_find_tool_gitignore_respect...") - asyncio.run(test_find_tool_gitignore_respect(ToolTestHelper)) - print("โœ“ test_find_tool_gitignore_respect passed") - - print("\nโœ… All integration tests passed!") - except Exception as e: - print(f"\nโŒ Test failed: {e}") - import traceback - - traceback.print_exc() diff --git a/pkg/hanzo-mcp/tests/test_find_tool_registration.py b/pkg/hanzo-mcp/tests/test_find_tool_registration.py deleted file mode 100644 index e0e995ee9..000000000 --- a/pkg/hanzo-mcp/tests/test_find_tool_registration.py +++ /dev/null @@ -1,113 +0,0 @@ -"""Test FindTool registration in the MCP server.""" - -import asyncio - -from fastmcp import FastMCP -from hanzo_tools.filesystem import register_filesystem_tools - -from hanzo_mcp.server import HanzoMCPServer -from hanzo_mcp.tools.common.permissions import PermissionManager - - -def test_find_tool_in_filesystem_tools(): - """Test that FindTool is included in filesystem tools.""" - # Create a mock MCP server - mcp = FastMCP("test-server") - pm = PermissionManager() - - # Register filesystem tools - tools = register_filesystem_tools( - mcp_server=mcp, permission_manager=pm, enabled_tools={"find": True} - ) - - # Check that tools were registered - assert len(tools) > 0 - - # Find the find tool - find_tool = None - for tool in tools: - if hasattr(tool, "name") and "find" in str(tool.name).lower(): - find_tool = tool - break - - assert find_tool is not None, "FindTool not found in registered tools" - print("โœ“ FindTool found in filesystem tools") - - -def test_find_tool_in_server(): - """Test that FindTool is registered when creating a server.""" - # Create server with search tools enabled - server = HanzoMCPServer( - name="test-server", disable_search_tools=False, enabled_tools={"find": True} - ) - - # The server registers tools during initialization - # We can't directly access _tool_handlers, but we know tools are registered - print("โœ“ Server created with FindTool enabled") - - # Verify search tools weren't disabled - assert not server.disable_search_tools - assert server.enabled_tools.get("find", True) # Default is True if not specified - - print("โœ“ FindTool registration verified in server") - - -def test_tool_registration_flow(): - """Test the complete tool registration flow.""" - mcp = FastMCP("test-flow") - pm = PermissionManager() - - # Test filesystem tools registration directly - filesystem_tools = register_filesystem_tools( - mcp_server=mcp, permission_manager=pm, enabled_tools={"find": True} - ) - - assert len(filesystem_tools) > 0 - - # Check for find tool in filesystem tools - tool_names = [] - for tool in filesystem_tools: - if hasattr(tool, "__class__"): - tool_names.append(tool.__class__.__name__) - elif hasattr(tool, "name"): - tool_names.append(str(tool.name)) - - print(f"Registered filesystem tools: {tool_names}") - - # FindTool should be in the list - find_tool_registered = any("find" in name.lower() for name in tool_names) - assert find_tool_registered, f"FindTool not found in: {tool_names}" - - print("โœ“ Complete tool registration flow verified") - - -async def test_find_tool_usage(): - """Test that FindTool can be used after registration.""" - from hanzo_mcp.tools.search import create_find_tool - - # Create the tool - find_tool = create_find_tool() - - # Test basic usage - result = await find_tool.run(pattern="*.py", path=".", max_results=5) - - assert result.data is not None - assert "results" in result.data - assert isinstance(result.data["results"], list) - - print( - f"โœ“ FindTool executed successfully, found {len(result.data['results'])} files" - ) - - -if __name__ == "__main__": - print("Testing FindTool registration...\n") - - test_find_tool_in_filesystem_tools() - test_find_tool_in_server() - test_tool_registration_flow() - - print("\nTesting FindTool usage...") - asyncio.run(test_find_tool_usage()) - - print("\nโœ… All registration tests passed!") diff --git a/pkg/hanzo-mcp/tests/test_git_ingestion.py b/pkg/hanzo-mcp/tests/test_git_ingestion.py deleted file mode 100644 index baf5f92be..000000000 --- a/pkg/hanzo-mcp/tests/test_git_ingestion.py +++ /dev/null @@ -1,377 +0,0 @@ -"""Tests for Git history ingestion into vector store.""" - -import subprocess -import tempfile -from datetime import datetime -from pathlib import Path - -import pytest -from hanzo_mcp.tools.vector.infinity_store import InfinityVectorStore -from hanzo_mcp.tools.vector.project_manager import ProjectVectorManager - - -class TestGitIngestion: - """Test suite for ingesting git repositories.""" - - @pytest.fixture - def test_git_repo(self): - """Create a test git repository with history.""" - with tempfile.TemporaryDirectory() as tmpdir: - repo_path = Path(tmpdir) - - # Initialize git repo - subprocess.run(["git", "init"], cwd=repo_path, check=True) - subprocess.run( - ["git", "config", "user.email", "test@example.com"], - cwd=repo_path, - check=True, - ) - subprocess.run( - ["git", "config", "user.name", "Test User"], cwd=repo_path, check=True - ) - - # Create initial commit - readme = repo_path / "README.md" - readme.write_text( - "# Test Project\n\nThis is a test project for git ingestion." - ) - subprocess.run(["git", "add", "README.md"], cwd=repo_path, check=True) - subprocess.run( - ["git", "commit", "-m", "Initial commit"], cwd=repo_path, check=True - ) - - # Add Python file - main_py = repo_path / "main.py" - main_py.write_text('''#!/usr/bin/env python3 -"""Main application file.""" - -def main(): - """Entry point.""" - print("Hello, World!") - -if __name__ == "__main__": - main() -''') - subprocess.run(["git", "add", "main.py"], cwd=repo_path, check=True) - subprocess.run( - ["git", "commit", "-m", "Add main.py"], cwd=repo_path, check=True - ) - - # Add more files and commits - utils_py = repo_path / "utils.py" - utils_py.write_text('''"""Utility functions.""" - -def format_string(s): - """Format a string.""" - return s.strip().title() - -def calculate_sum(numbers): - """Calculate sum of numbers.""" - return sum(numbers) -''') - subprocess.run(["git", "add", "utils.py"], cwd=repo_path, check=True) - subprocess.run( - ["git", "commit", "-m", "Add utility functions"], - cwd=repo_path, - check=True, - ) - - # Modify existing file - main_py.write_text('''#!/usr/bin/env python3 -"""Main application file.""" - -from utils import format_string - -def main(): - """Entry point.""" - message = format_string(" hello, world! ") - print(message) - -def run(): - """Run the application.""" - main() - -if __name__ == "__main__": - run() -''') - subprocess.run(["git", "add", "main.py"], cwd=repo_path, check=True) - subprocess.run( - ["git", "commit", "-m", "Update main.py to use utils"], - cwd=repo_path, - check=True, - ) - - # Create a feature branch - subprocess.run( - ["git", "checkout", "-b", "feature/testing"], cwd=repo_path, check=True - ) - - test_py = repo_path / "test_main.py" - test_py.write_text('''"""Tests for main module.""" - -import unittest -from main import main - -class TestMain(unittest.TestCase): - def test_main(self): - """Test main function.""" - # This is a simple test - self.assertTrue(True) -''') - subprocess.run(["git", "add", "test_main.py"], cwd=repo_path, check=True) - subprocess.run( - ["git", "commit", "-m", "Add tests"], cwd=repo_path, check=True - ) - - # Switch back to main branch - subprocess.run(["git", "checkout", "main"], cwd=repo_path, check=True) - - yield repo_path - - def test_git_log_parsing(self, tool_helper, test_git_repo): - """Test parsing git log output.""" - # Get git log - result = subprocess.run( - ["git", "log", "--pretty=format:%H|%an|%ae|%at|%s", "--name-status"], - cwd=test_git_repo, - capture_output=True, - text=True, - check=True, - ) - - commits = [] - current_commit = None - - for line in result.stdout.strip().split("\n"): - if "|" in line and len(line.split("|")) == 5: - # Commit line - hash_, author, email, timestamp, message = line.split("|") - current_commit = { - "hash": hash_, - "author": author, - "email": email, - "timestamp": int(timestamp), - "message": message, - "files": [], - } - commits.append(current_commit) - elif line and current_commit and "\t" in line: - # File change line - parts = line.split("\t") - if len(parts) >= 2: - status, filename = parts[0], parts[1] - current_commit["files"].append( - {"status": status, "filename": filename} - ) - - assert len(commits) >= 4 # Should have at least 4 commits - assert any(c["message"] == "Initial commit" for c in commits) - assert any(c["message"] == "Add main.py" for c in commits) - assert any("utils" in c["message"] for c in commits) - - def test_git_diff_extraction(self, tool_helper, test_git_repo): - """Test extracting diffs from git history.""" - # Get diff for a specific commit - result = subprocess.run( - ["git", "log", "-1", "-p", "--format=%H"], - cwd=test_git_repo, - capture_output=True, - text=True, - check=True, - ) - - assert "diff --git" in result.stdout - assert "@@" in result.stdout # Diff chunks - - def test_git_blame_integration(self, tool_helper, test_git_repo): - """Test git blame for line-level attribution.""" - # Run git blame on main.py - result = subprocess.run( - ["git", "blame", "--line-porcelain", "main.py"], - cwd=test_git_repo, - capture_output=True, - text=True, - check=True, - ) - - blame_data = {} - current_commit = None - current_line = None - - for line in result.stdout.strip().split("\n"): - if line and not line.startswith("\t"): - parts = line.split(" ") - if len(parts) >= 3 and len(parts[0]) == 40: # SHA-1 hash - current_commit = parts[0] - current_line = int(parts[2]) - elif line.startswith("author "): - author = line[7:] - if current_line: - blame_data[current_line] = { - "commit": current_commit, - "author": author, - } - - assert len(blame_data) > 0 - assert any("Test User" in data["author"] for data in blame_data.values()) - - def test_full_repo_ingestion(self, tool_helper, test_git_repo): - """Test ingesting an entire repository into vector store.""" - with tempfile.TemporaryDirectory() as vector_dir: - store = InfinityVectorStore(data_path=vector_dir) - - # Ingest all Python files - python_files = list(test_git_repo.rglob("*.py")) - assert len(python_files) >= 2 - - total_docs = 0 - for py_file in python_files: - doc_ids = store.add_file(str(py_file), chunk_size=500) - total_docs += len(doc_ids) - - assert total_docs > 0 - - # Search for content - results = store.search("main function", limit=5) - assert len(results) > 0 - - # Search for imports - import_results = store.search("from utils import", limit=5) - assert len(import_results) > 0 - - store.close() - - def test_git_metadata_extraction(self, tool_helper, test_git_repo): - """Test extracting and storing git metadata.""" - # Get file history - result = subprocess.run( - ["git", "log", "--follow", "--pretty=format:%H|%at|%s", "main.py"], - cwd=test_git_repo, - capture_output=True, - text=True, - check=True, - ) - - history = [] - for line in result.stdout.strip().split("\n"): - if line: - parts = line.split("|") - if len(parts) == 3: - history.append( - { - "commit": parts[0], - "timestamp": int(parts[1]), - "message": parts[2], - } - ) - - assert len(history) >= 2 # main.py was modified at least twice - - # Create metadata for vector store - metadata = { - "file_path": "main.py", - "history_count": len(history), - "first_commit": history[-1]["commit"] if history else None, - "last_commit": history[0]["commit"] if history else None, - "last_modified": ( - datetime.fromtimestamp(history[0]["timestamp"]).isoformat() - if history - else None - ), - } - - assert metadata["history_count"] >= 2 - assert metadata["first_commit"] != metadata["last_commit"] - - -class TestGitProjectManager: - """Test project manager with git repositories.""" - - @pytest.fixture - def project_manager(self): - """Create a project manager.""" - with tempfile.TemporaryDirectory() as tmpdir: - manager = ProjectVectorManager( - global_db_path=str(Path(tmpdir) / "global_db") - ) - yield manager, tmpdir - - def test_detect_git_project(self, tool_helper, project_manager): - """Test detecting git projects.""" - manager, base_dir = project_manager - - # Create a test git repo - repo_dir = Path(base_dir) / "test_repo" - repo_dir.mkdir() - - # Initialize git - subprocess.run(["git", "init"], cwd=repo_dir, check=True) - - # Create LLM.md file to mark it as a project - llm_md = repo_dir / "LLM.md" - llm_md.write_text("# Test Project\n\nThis is a test project with git history.") - - # Get project info - this will detect the project - project_info = manager.get_project_for_path(str(repo_dir)) - assert project_info is not None - assert project_info.name == "test_repo" - assert project_info.llm_md_path.name == "LLM.md" - - # Check if .git directory is detected - git_dir = repo_dir / ".git" - assert git_dir.exists() - assert git_dir.is_dir() - - -class TestGitDiffIngestion: - """Test ingesting git diffs for code evolution tracking.""" - - def test_parse_unified_diff(self): - """Test parsing unified diff format.""" - diff_text = """diff --git a/main.py b/main.py -index 1234567..abcdefg 100644 ---- a/main.py -+++ b/main.py -@@ -1,5 +1,6 @@ - def hello(): -- print("Hello") -+ print("Hello, World!") -+ return True - - def goodbye(): - print("Goodbye") -""" - - # Parse diff - changes = [] - current_file = None - - for line in diff_text.strip().split("\n"): - if line.startswith("diff --git"): - parts = line.split() - if len(parts) >= 4: - current_file = parts[2][2:] # Remove 'a/' prefix - elif line.startswith("@@"): - # Parse chunk header - import re - - match = re.match(r"@@ -(\d+),(\d+) \+(\d+),(\d+) @@", line) - if match: - changes.append( - { - "file": current_file, - "old_start": int(match.group(1)), - "old_lines": int(match.group(2)), - "new_start": int(match.group(3)), - "new_lines": int(match.group(4)), - } - ) - - assert len(changes) == 1 - assert changes[0]["file"] == "main.py" - assert changes[0]["old_lines"] == 5 - assert changes[0]["new_lines"] == 6 - - -if __name__ == "__main__": - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_hanzo_agents_integration.py b/pkg/hanzo-mcp/tests/test_hanzo_agents_integration.py deleted file mode 100644 index 2c4e36684..000000000 --- a/pkg/hanzo-mcp/tests/test_hanzo_agents_integration.py +++ /dev/null @@ -1,101 +0,0 @@ -"""Test hanzo-agents integration from within MCP package.""" - -import sys -from pathlib import Path - -# Add agents package to path -agents_path = Path(__file__).parent.parent / "agents" -sys.path.insert(0, str(agents_path)) - -print("Testing Hanzo Agents Web3 Integration") -print("=" * 50) - -# Test basic imports -try: - from hanzo_agents.core.wallet import MockWallet, generate_shared_mnemonic - - print("โœ“ Wallet module imported") - - # Test mnemonic generation - mnemonic = generate_shared_mnemonic() - print(f"โœ“ Generated mnemonic: {' '.join(mnemonic.split()[:3])}...") - - # Test mock wallet - wallet = MockWallet() - print(f"โœ“ Mock wallet address: {wallet.address}") - print(f"โœ“ Mock wallet balance: {wallet.balance} ETH") - -except Exception as e: - print(f"โœ— Wallet test failed: {e}") - -# Test TEE -try: - from hanzo_agents.core.tee import MockTEEExecutor - - print("\nโœ“ TEE module imported") - - executor = MockTEEExecutor() - result = executor.execute("test = 1 + 1", {}) - print(f"โœ“ Mock TEE execution: success={result['success']}") - -except Exception as e: - print(f"\nโœ— TEE test failed: {e}") - -# Test marketplace -try: - from hanzo_agents.core.marketplace import AgentMarketplace, ServiceType - - print("\nโœ“ Marketplace module imported") - - marketplace = AgentMarketplace() - print(f"โœ“ Marketplace created with {len(marketplace.offers)} offers") - print(f"โœ“ Available service types: {[s.value for s in ServiceType]}") - -except Exception as e: - print(f"\nโœ— Marketplace test failed: {e}") - -# Test Web3Agent basics -try: - from hanzo_agents.core.web3_agent import Web3AgentConfig - - print("\nโœ“ Web3Agent config imported") - - config = Web3AgentConfig(wallet_enabled=True, tee_enabled=True, task_price_eth=0.01) - print( - f"โœ“ Created Web3 config: wallet={config.wallet_enabled}, tee={config.tee_enabled}" - ) - -except Exception as e: - print(f"\nโœ— Web3Agent test failed: {e}") - -print("\n" + "=" * 50) -print("Basic components are working!") -print("\nNote: Full agent functionality requires additional dependencies:") -print("- structlog (for logging)") -print("- prometheus_client (for metrics)") -print("- web3 (for real blockchain interaction)") -print("- torch/transformers (for local AI compute)") - -# Test integration with MCP -print("\n" + "=" * 50) -print("Testing MCP Integration") - -try: - # Import MCP swarm tool to verify it can use hanzo-agents - from hanzo_tools.agent.swarm_tool import SwarmTool - - print("โœ“ SwarmTool imports successfully") - - # Check if it references hanzo_agents - import inspect - - source = inspect.getsource(SwarmTool) - if "hanzo_agents" in source or "hanzo-agents" in source: - print("โœ“ SwarmTool uses hanzo-agents SDK") - else: - print("! SwarmTool may need updating to use hanzo-agents") - -except Exception as e: - print(f"โœ— MCP integration check failed: {e}") - -print("\nโœ… Integration test complete!") diff --git a/pkg/hanzo-mcp/tests/test_hanzo_mcp_integration.py b/pkg/hanzo-mcp/tests/test_hanzo_mcp_integration.py deleted file mode 100644 index 996213f61..000000000 --- a/pkg/hanzo-mcp/tests/test_hanzo_mcp_integration.py +++ /dev/null @@ -1,417 +0,0 @@ -"""Integration tests for hanzo-mcp with Claude CLI and basic operations.""" - -import json -import os -import subprocess -import tempfile -from pathlib import Path - -import pytest -from mcp.server.fastmcp import FastMCP - -from hanzo_mcp.server import create_server - - -class TestHanzoMCPIntegration: - """Test hanzo-mcp server functionality and Claude CLI integration.""" - - @pytest.fixture - def temp_dir(self): - """Create a temporary directory for tests.""" - with tempfile.TemporaryDirectory() as tmpdir: - yield Path(tmpdir) - - @pytest.fixture - async def mcp_server(self, temp_dir): - """Create and start an MCP server instance.""" - # Create server with jupyter enabled - server = create_server( - name="test-hanzo-mcp", - allowed_paths=[str(temp_dir)], - enable_all_tools=True, - enabled_tools={"jupyter": True}, - ) - - # Start server (in test mode) - yield server - - async def test_server_startup(self, tool_helper, mcp_server): - """Test that the MCP server starts correctly.""" - assert mcp_server is not None - # mcp_server is a HanzoMCPServer instance that wraps FastMCP - from hanzo_mcp.server import HanzoMCPServer - - assert isinstance(mcp_server, HanzoMCPServer) - assert hasattr(mcp_server, "mcp") - assert isinstance(mcp_server.mcp, FastMCP) - - # Check that tools are registered via the wrapped FastMCP instance - tools = await mcp_server.mcp.list_tools() - assert len(tools) > 0 - - # Check for essential tools - tool_names = [tool.name for tool in tools] - assert "read" in tool_names - assert "write" in tool_names - assert "edit" in tool_names - assert "search" in tool_names - - async def test_file_operations(self, tool_helper, mcp_server, temp_dir): - """Test basic file operations through MCP.""" - # Create a test file - test_file = temp_dir / "test.txt" - test_content = "Hello from hanzo-mcp!" - - # Test write operation - write_result = await mcp_server.mcp.call_tool( - "write", arguments={"file_path": str(test_file), "content": test_content} - ) - # write_result is a tuple (content_list, metadata) - assert test_file.exists() - if isinstance(write_result, tuple) and len(write_result) > 1: - result_text = str(write_result[1]) - assert "success" in result_text.lower() - - # Test read operation - read_result = await mcp_server.mcp.call_tool( - "read", arguments={"file_path": str(test_file)} - ) - # read_result is a tuple (content_list, metadata) - if isinstance(read_result, tuple) and len(read_result) > 0: - content_list = read_result[0] - if content_list and hasattr(content_list[0], "text"): - assert test_content in content_list[0].text - else: - assert test_content in str(read_result) - else: - assert test_content in str(read_result) - - # Test edit operation - await mcp_server.mcp.call_tool( - "edit", - arguments={ - "file_path": str(test_file), - "old_string": "Hello", - "new_string": "Greetings", - }, - ) - - # Verify edit - read_after_edit = await mcp_server.mcp.call_tool( - "read", arguments={"file_path": str(test_file)} - ) - # read_after_edit is a tuple (content_list, metadata) - if isinstance(read_after_edit, tuple) and len(read_after_edit) > 0: - content_list = read_after_edit[0] - if content_list and hasattr(content_list[0], "text"): - assert "Greetings from hanzo-mcp!" in content_list[0].text - else: - assert "Greetings from hanzo-mcp!" in str(read_after_edit) - else: - assert "Greetings from hanzo-mcp!" in str(read_after_edit) - - async def test_search_functionality(self, tool_helper, mcp_server, temp_dir): - """Test the unified search tool.""" - # Create test files with content - for i in range(3): - test_file = temp_dir / f"file{i}.py" - test_file.write_text(f""" -def function_{i}(): - # TODO: Implement this function - return "result_{i}" -""") - - # Test search - search_result = await mcp_server.mcp.call_tool( - "search", arguments={"pattern": "TODO", "path": str(temp_dir)} - ) - - # Handle tuple result from call_tool - if isinstance(search_result, tuple) and len(search_result) > 0: - content_list = search_result[0] - if content_list and hasattr(content_list[0], "text"): - result_text = content_list[0].text - else: - result_text = str(search_result) - else: - result_text = str(search_result) - - # Check results - search tool returns text, not JSON - assert "TODO" in result_text - assert "Total results: 3" in result_text - - # Verify each file was found in the text output - for i in range(3): - expected_file = f"file{i}.py" - assert expected_file in result_text - - @pytest.mark.skipif( - not os.path.exists(os.path.expanduser("~/.claude/bin/claude")), - reason="Claude CLI not installed", - ) - async def test_claude_cli_integration(self, tool_helper, temp_dir): - """Test integration with Claude CLI.""" - # Create a simple test script that uses hanzo-mcp - test_script = temp_dir / "test_claude.py" - test_script.write_text(""" -import subprocess -import json - -# Call Claude with a simple file operation task -result = subprocess.run([ - "claude", - "--mcp-server", "hanzo-mcp", - "--prompt", "Create a file called hello.txt with 'Hello Claude' content" -], capture_output=True, text=True) - -print(result.stdout) -""") - - # Run the test script - result = subprocess.run( - ["python", str(test_script)], - capture_output=True, - text=True, - cwd=str(temp_dir), - ) - - # Check that the file was created - hello_file = temp_dir / "hello.txt" - assert hello_file.exists() or "success" in str(result).lower() - - async def test_multi_tool_workflow(self, tool_helper, mcp_server, temp_dir): - """Test a workflow using multiple tools.""" - # Create a Python file with issues - test_file = temp_dir / "buggy.py" - test_file.write_text(""" -def calculate_sum(a, b): - # TODO: Add type hints - result = a + b - print(f"Sum is: {result}") - return result - -def main(): - # This will fail with strings - result = calculate_sum("10", "20") - print(result) -""") - - # 1. Search for TODOs - search_result = await mcp_server.mcp.call_tool( - "search", arguments={"pattern": "TODO", "path": str(temp_dir)} - ) - # Handle tuple result - if isinstance(search_result, tuple) and len(search_result) > 0: - content_list = search_result[0] - if content_list and hasattr(content_list[0], "text"): - result_text = content_list[0].text - else: - result_text = str(search_result) - else: - result_text = str(search_result) - assert "TODO" in result_text - - # 2. Read the file - content = await mcp_server.mcp.call_tool( - "read", arguments={"file_path": str(test_file)} - ) - # Handle tuple result - if isinstance(content, tuple) and len(content) > 0: - content_list = content[0] - if content_list and hasattr(content_list[0], "text"): - content_text = content_list[0].text - else: - content_text = str(content) - else: - content_text = str(content) - assert "calculate_sum" in content_text - - # 3. Edit to add type hints - await mcp_server.mcp.call_tool( - "edit", - arguments={ - "file_path": str(test_file), - "old_string": "def calculate_sum(a, b):", - "new_string": "def calculate_sum(a: int, b: int) -> int:", - }, - ) - - # 4. Run the critic tool - critic_result = await mcp_server.mcp.call_tool( - "critic", - arguments={ - "analysis": f"Review the code in {test_file} for potential issues" - }, - ) - - # Handle tuple result - if isinstance(critic_result, tuple) and len(critic_result) > 0: - content_list = critic_result[0] - if content_list and hasattr(content_list[0], "text"): - critic_text = content_list[0].text - else: - critic_text = str(critic_result) - else: - critic_text = str(critic_result) - # The critic tool currently just returns a confirmation message - # Check that it returns the expected template response - assert ( - "Critical analysis complete" in critic_text - or "analysis" in critic_text.lower() - ) - - async def test_notebook_operations(self, tool_helper, mcp_server, temp_dir): - """Test notebook read/write operations.""" - notebook_path = temp_dir / "test.ipynb" - - # Create a basic notebook structure first - notebook_content = { - "cells": [ - { - "cell_type": "code", - "source": ["print('Hello from notebook')"], - "metadata": {}, - "outputs": [], - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3", - } - }, - "nbformat": 4, - "nbformat_minor": 5, - } - - # Write the notebook using the write tool - await mcp_server.mcp.call_tool( - "write", - arguments={ - "file_path": str(notebook_path), - "content": json.dumps(notebook_content, indent=2), - }, - ) - - assert notebook_path.exists() - - # Now edit to add a markdown cell using the unified jupyter tool - await mcp_server.mcp.call_tool( - "jupyter", - arguments={ - "action": "edit", - "notebook_path": str(notebook_path), - "source": "# Test Notebook\nThis is a test.", - "edit_mode": "insert", - "cell_type": "markdown", - }, - ) - - # Read the notebook using the unified jupyter tool - read_result = await mcp_server.mcp.call_tool( - "jupyter", arguments={"action": "read", "notebook_path": str(notebook_path)} - ) - - # Handle tuple result - if isinstance(read_result, tuple) and len(read_result) > 0: - content_list = read_result[0] - if content_list and hasattr(content_list[0], "text"): - notebook_text = content_list[0].text - # Parse the JSON from the text - notebook_data = json.loads(notebook_text) - else: - notebook_data = read_result - else: - notebook_data = json.loads(str(read_result)) - - assert "cells" in notebook_data - assert len(notebook_data["cells"]) >= 1 # At least the original code cell - # Check that we have at least one code cell - code_cells = [ - cell for cell in notebook_data["cells"] if cell.get("cell_type") == "code" - ] - assert len(code_cells) >= 1 - - -class TestHanzoMCPStdioServer: - """Test hanzo-mcp as a stdio server (how Claude Desktop uses it).""" - - @pytest.fixture - def server_env(self, tmp_path): - """Environment for the server.""" - return { - "HANZO_ALLOWED_PATHS": str(tmp_path), - "PYTHONPATH": os.environ.get("PYTHONPATH", ""), - } - - async def test_stdio_server_basic(self, tool_helper, tmp_path, server_env): - """Test basic stdio server operations.""" - # Skip test if MCP client imports are not available - try: - from mcp.client import ClientSession - from mcp.client.stdio import stdio_client - except ImportError: - pytest.skip("MCP client imports not available - needs updated dependencies") - return - - # Use the MCP client session to test the server - async with stdio_client( - ["python", "-m", "hanzo_mcp"], env={**os.environ, **server_env} - ) as (read, write): - # Create a client session - async with ClientSession(read, write) as session: - # Initialize the session - await session.initialize() - - # List available tools - tools_response = await session.list_tools() - assert tools_response.tools - assert len(tools_response.tools) > 0 - - # Check some basic tools are available - tool_names = [tool.name for tool in tools_response.tools] - assert "read" in tool_names - assert "write" in tool_names - assert "grep" in tool_names - - # Test a simple tool call - await session.call_tool( - "write", - arguments={ - "file_path": str(tmp_path / "test.txt"), - "content": "Hello from stdio test!", - }, - ) - - # Verify the file was created - test_file = tmp_path / "test.txt" - assert test_file.exists() - assert test_file.read_text() == "Hello from stdio test!" - - -@pytest.mark.asyncio -async def test_hanzo_mcp_cli_tool(): - """Test the hanzo-mcp CLI tool directly.""" - # Test help command - result = subprocess.run( - ["python", "-m", "hanzo_mcp", "--help"], capture_output=True, text=True - ) - - assert result.returncode == 0 - assert "usage" in result.stdout.lower() or "mcp server" in result.stdout.lower() - - # Test version command - result = subprocess.run( - ["python", "-m", "hanzo_mcp", "--version"], capture_output=True, text=True - ) - - assert result.returncode == 0 - assert ( - "0.6" in result.stdout or "0.7" in result.stdout - ) # Should show version 0.6.x or 0.7.x - - -if __name__ == "__main__": - # Run tests - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_hanzo_mcp_local.py b/pkg/hanzo-mcp/tests/test_hanzo_mcp_local.py deleted file mode 100644 index 6d7200766..000000000 --- a/pkg/hanzo-mcp/tests/test_hanzo_mcp_local.py +++ /dev/null @@ -1,327 +0,0 @@ -#!/usr/bin/env python3 -"""Test hanzo-mcp locally to ensure it works.""" - -import asyncio -import json -import re -import subprocess -import sys -import tempfile -from pathlib import Path - -# Add the package to path for local testing -sys.path.insert(0, str(Path(__file__).parent.parent)) - -import pytest - -from hanzo_mcp import __version__ -from hanzo_mcp.server import create_server - - -def _tool_names(server) -> list[str]: - """Extract registered tool names from FastMCP internals.""" - if ( - hasattr(server, "mcp") - and hasattr(server.mcp, "_tool_manager") - and hasattr(server.mcp._tool_manager, "_tools") - ): - return list(server.mcp._tool_manager._tools.keys()) - - raise AssertionError("Cannot access tools from server") - - -def test_basic_import(): - """Test that we can import hanzo-mcp.""" - print(f"โœ“ Successfully imported hanzo-mcp version {__version__}") - assert re.match(r"^\d+\.\d+\.\d+", __version__), ( - f"Unexpected version format: {__version__}" - ) - - -def test_server_creation(): - """Test creating an MCP server.""" - with tempfile.TemporaryDirectory() as tmpdir: - server = create_server( - name="test-server", allowed_paths=[tmpdir], enable_all_tools=True - ) - - print(f"โœ“ Created server: {server.__class__.__name__}") - - tool_names = _tool_names(server) - print(f"โœ“ Found {len(tool_names)} tools") - - # Core tools that must exist in current builds. - essential_tools = ["read", "write", "edit", "search"] - missing_tools = [] - - for tool in essential_tools: - if tool in tool_names: - print(f" โœ“ {tool} tool available") - else: - print(f" โœ— {tool} tool missing") - missing_tools.append(tool) - - # Shell tool name can vary by distribution/version. - shell_aliases = {"bash", "zsh", "run_command", "shell"} - has_shell_tool = any(name in tool_names for name in shell_aliases) - if has_shell_tool: - print(" โœ“ shell tool available") - else: - print(f" โœ— none of shell aliases found: {sorted(shell_aliases)}") - missing_tools.append("shell tool alias") - - assert len(missing_tools) == 0, f"Missing tools: {missing_tools}" - - -@pytest.mark.asyncio -async def test_file_operations(): - """Test basic file operations.""" - with tempfile.TemporaryDirectory() as tmpdir: - server = create_server( - name="test-server", allowed_paths=[tmpdir], enable_all_tools=True - ) - - test_file = Path(tmpdir) / "test.txt" - test_content = "Hello from hanzo-mcp test!" - - # Test write operation using call_tool - await server.mcp.call_tool( - "write", arguments={"file_path": str(test_file), "content": test_content} - ) - - # Verify file was created - assert test_file.exists() - assert test_file.read_text() == test_content - - # Test read operation - read_result = await server.mcp.call_tool( - "read", arguments={"file_path": str(test_file)} - ) - - # Handle tuple result from call_tool - if isinstance(read_result, tuple) and len(read_result) > 0: - content_list = read_result[0] - if content_list and hasattr(content_list[0], "text"): - assert test_content in content_list[0].text - else: - assert test_content in str(read_result) - else: - assert test_content in str(read_result) - - # Test edit operation - await server.mcp.call_tool( - "edit", - arguments={ - "file_path": str(test_file), - "old_string": "Hello", - "new_string": "Greetings", - }, - ) - - # Verify edit worked - edited_content = test_file.read_text() - assert edited_content == "Greetings from hanzo-mcp test!" - - -@pytest.mark.asyncio -async def test_search_functionality(): - """Test search functionality.""" - with tempfile.TemporaryDirectory() as tmpdir: - server = create_server( - name="test-server", allowed_paths=[tmpdir], enable_all_tools=True - ) - - # Create test files before server startup so index-based search backends see them - for i in range(3): - test_file = Path(tmpdir) / f"file{i}.py" - test_file.write_text(f''' -def function_{i}(): - """Function {i} documentation.""" - # TODO: Implement feature {i} - return {i} -''') - - # Search for TODOs using the search tool - search_result = await server.mcp.call_tool( - "search", arguments={"pattern": "TODO", "path": str(tmpdir)} - ) - - # Handle tuple result from call_tool - if isinstance(search_result, tuple) and len(search_result) > 0: - content_list = search_result[0] - if content_list and hasattr(content_list[0], "text"): - result_text = content_list[0].text - else: - result_text = str(search_result) - else: - result_text = str(search_result) - - # Verify search found all TODOs - assert "TODO" in result_text - assert ("Found 3 matches" in result_text) or ("Total results: 3" in result_text) - - # Verify each file was found - for i in range(3): - assert f"file{i}.py" in result_text - - -def test_cli_invocation(): - """Test CLI invocation.""" - print("\n Testing CLI:") - - # Test help - result = subprocess.run( - [sys.executable, "-m", "hanzo_mcp", "--help"], capture_output=True, text=True - ) - - if result.returncode == 0: - print(" โœ“ CLI help works") - else: - print(f" โœ— CLI help failed: {result.stderr}") - - # Test version - result = subprocess.run( - [sys.executable, "-m", "hanzo_mcp", "--version"], capture_output=True, text=True - ) - - normalized_version = __version__.split("+")[0] - if result.returncode == 0 and normalized_version in result.stdout: - print(f" โœ“ CLI version works: {result.stdout.strip()}") - else: - print(f" โœ— CLI version failed: {result.stderr}") - - -@pytest.mark.asyncio -async def test_notebook_operations(): - """Test notebook operations when jupyter tool is available.""" - with tempfile.TemporaryDirectory() as tmpdir: - server = create_server( - name="test-server", allowed_paths=[tmpdir], enable_all_tools=True - ) - - tool_names = _tool_names(server) - if "jupyter" not in tool_names: - pytest.skip("jupyter tool is not available in this build") - - notebook_path = Path(tmpdir) / "test.ipynb" - - # Create a notebook structure - notebook_data = { - "cells": [ - { - "cell_type": "code", - "source": ["print('Hello from notebook')"], - "metadata": {}, - "outputs": [], - }, - { - "cell_type": "markdown", - "source": ["# Test Notebook"], - "metadata": {}, - }, - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3", - } - }, - "nbformat": 4, - "nbformat_minor": 5, - } - - # Write notebook using write tool - await server.mcp.call_tool( - "write", - arguments={ - "file_path": str(notebook_path), - "content": json.dumps(notebook_data, indent=2), - }, - ) - - assert notebook_path.exists() - - # Read notebook using jupyter tool - read_result = await server.mcp.call_tool( - "jupyter", arguments={"action": "read", "notebook_path": str(notebook_path)} - ) - - # Handle tuple result - if isinstance(read_result, tuple) and len(read_result) > 0: - content_list = read_result[0] - if content_list and hasattr(content_list[0], "text"): - notebook_text = content_list[0].text - notebook_content = json.loads(notebook_text) - else: - notebook_content = read_result - else: - notebook_content = json.loads(str(read_result)) - - # Verify notebook structure - assert "cells" in notebook_content - assert len(notebook_content["cells"]) >= 2 - assert notebook_content["cells"][0]["cell_type"] == "code" - assert notebook_content["cells"][1]["cell_type"] == "markdown" - - # Test editing notebook to add a new cell - await server.mcp.call_tool( - "jupyter", - arguments={ - "action": "edit", - "notebook_path": str(notebook_path), - "new_source": "x = 42\nprint(f'The answer is {x}')", - "edit_mode": "insert", - "cell_type": "code", - }, - ) - - # Read again to verify the edit - read_result2 = await server.mcp.call_tool( - "jupyter", arguments={"action": "read", "notebook_path": str(notebook_path)} - ) - - # Handle tuple result - if isinstance(read_result2, tuple) and len(read_result2) > 0: - content_list = read_result2[0] - if content_list and hasattr(content_list[0], "text"): - notebook_text = content_list[0].text - updated_content = json.loads(notebook_text) - else: - updated_content = read_result2 - else: - updated_content = json.loads(str(read_result2)) - - # Verify new cell was added - assert len(updated_content["cells"]) >= 3 - - -async def run_all_tests(): - """Run all tests.""" - print("=" * 60) - print("Testing hanzo-mcp locally") - print("=" * 60) - - try: - test_basic_import() - test_server_creation() - await test_file_operations() - await test_search_functionality() - test_cli_invocation() - await test_notebook_operations() - - print("\n" + "=" * 60) - print("โœ… All tests passed!") - print("=" * 60) - - except Exception as e: - print(f"\nโŒ Test failed: {e}") - import traceback - - traceback.print_exc() - sys.exit(1) - - -if __name__ == "__main__": - # Run the tests - asyncio.run(run_all_tests()) diff --git a/pkg/hanzo-mcp/tests/test_hanzo_mcp_simple.py b/pkg/hanzo-mcp/tests/test_hanzo_mcp_simple.py deleted file mode 100644 index 46ad3db3a..000000000 --- a/pkg/hanzo-mcp/tests/test_hanzo_mcp_simple.py +++ /dev/null @@ -1,201 +0,0 @@ -#!/usr/bin/env python3 -"""Simple test to verify hanzo-mcp works locally.""" - -import asyncio -import os -import subprocess -import sys -import tempfile -from pathlib import Path - -# Add the package to path for local testing -sys.path.insert(0, str(Path(__file__).parent.parent)) - - -def test_cli_help(): - """Test that CLI help works.""" - result = subprocess.run( - [sys.executable, "-m", "hanzo_mcp", "--help"], capture_output=True, text=True - ) - - print(f"Return code: {result.returncode}") - print(f"STDOUT: {result.stdout[:200]}") - print(f"STDERR: {result.stderr[:200]}") - - assert result.returncode == 0, ( - f"Command failed with code {result.returncode}\nSTDOUT: {result.stdout}\nSTDERR: {result.stderr}" - ) - assert "hanzo" in result.stdout.lower() or "mcp" in result.stdout.lower() - print("โœ“ CLI help works") - - -def test_cli_version(): - """Test version command.""" - result = subprocess.run( - [sys.executable, "-m", "hanzo_mcp", "--version"], capture_output=True, text=True - ) - - print(f"Version output: {result.stdout}") - # Version command might not be supported, so just check if command doesn't crash - if result.returncode == 0 or "usage" in result.stdout + result.stderr: - print("โœ“ Version check works") - else: - print(f"โœ— Version check failed: {result.stderr}") - - -async def test_stdio_server(): - """Test the stdio server with a simple interaction.""" - import asyncio - import json - - with tempfile.TemporaryDirectory() as tmpdir: - # Start the server process - env = os.environ.copy() - env["HANZO_ALLOWED_PATHS"] = tmpdir - - proc = await asyncio.create_subprocess_exec( - sys.executable, - "-m", - "hanzo_mcp", - "--transport", - "stdio", - "--allow-path", - tmpdir, - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - env=env, - ) - - try: - # Send initialize request - init_request = { - "jsonrpc": "2.0", - "id": 1, - "method": "initialize", - "params": {"capabilities": {}}, - } - - proc.stdin.write((json.dumps(init_request) + "\n").encode()) - await proc.stdin.drain() - - # Read response with timeout - try: - response_line = await asyncio.wait_for( - proc.stdout.readline(), timeout=5.0 - ) - response = json.loads(response_line.decode()) - - print(f"Initialize response: {response}") - assert response["id"] == 1 - assert "result" in response - print("โœ“ Server initialization works") - - # List tools - list_request = { - "jsonrpc": "2.0", - "id": 2, - "method": "tools/list", - "params": {}, - } - - proc.stdin.write((json.dumps(list_request) + "\n").encode()) - await proc.stdin.drain() - - response_line = await asyncio.wait_for( - proc.stdout.readline(), timeout=5.0 - ) - response = json.loads(response_line.decode()) - - print(f"Found {len(response['result']['tools'])} tools") - assert len(response["result"]["tools"]) > 10 - print("โœ“ Tool listing works") - - except asyncio.TimeoutError: - print("โœ— Server response timeout") - stderr = await proc.stderr.read() - print(f"Stderr: {stderr.decode()}") - - finally: - proc.terminate() - await proc.wait() - - -def test_import_tools(): - """Test that we can import tools directly.""" - try: - from mcp.server.fastmcp import FastMCP - - from hanzo_mcp.tools import register_all_tools - from hanzo_mcp.tools.common.permissions import PermissionManager - - # Create a test server - server = FastMCP("test-server") - pm = PermissionManager() - pm.add_allowed_path("/tmp") - - # Register tools - tools = register_all_tools(server, pm) - - print(f"โœ“ Registered {len(tools)} tools") - - # Check some essential tools - tool_names = [getattr(t, "name", str(t)) for t in tools] - print(f"Sample tools: {tool_names[:5]}") - - except Exception as e: - print(f"โœ— Import failed: {e}") - raise - - -def run_tests(): - """Run all tests.""" - print("=" * 60) - print("Testing hanzo-mcp") - print("=" * 60) - - tests_passed = 0 - tests_failed = 0 - - # Test 1: CLI Help - try: - test_cli_help() - tests_passed += 1 - except Exception as e: - print(f"โœ— CLI help test failed: {e}") - tests_failed += 1 - - # Test 2: Version - try: - test_cli_version() - tests_passed += 1 - except Exception as e: - print(f"โœ— Version test failed: {e}") - tests_failed += 1 - - # Test 3: Import tools - try: - test_import_tools() - tests_passed += 1 - except Exception as e: - print(f"โœ— Import test failed: {e}") - tests_failed += 1 - - # Test 4: Stdio server - try: - asyncio.run(test_stdio_server()) - tests_passed += 1 - except Exception as e: - print(f"โœ— Stdio server test failed: {e}") - tests_failed += 1 - - print("\n" + "=" * 60) - print(f"Results: {tests_passed} passed, {tests_failed} failed") - print("=" * 60) - - return tests_failed == 0 - - -if __name__ == "__main__": - success = run_tests() - sys.exit(0 if success else 1) diff --git a/pkg/hanzo-mcp/tests/test_hanzo_network_integration.py b/pkg/hanzo-mcp/tests/test_hanzo_network_integration.py deleted file mode 100644 index 143c6ad32..000000000 --- a/pkg/hanzo-mcp/tests/test_hanzo_network_integration.py +++ /dev/null @@ -1,550 +0,0 @@ -"""Integration tests for hanzo-network multi-agent orchestration.""" - -import tempfile -from pathlib import Path -from typing import Any, Dict - -import pytest - -# Try to import hanzo-network components -try: - from hanzo_network import ( - Agent, - LocalComputeNode, - LocalComputeOrchestrator, - Network, - NetworkConfig, - State, - Tool, - create_agent_network, - ) - from hanzo_network.tools import MemoryTool, create_memory_tool - - HANZO_NETWORK_AVAILABLE = True -except ImportError: - HANZO_NETWORK_AVAILABLE = False - -# Try to import hanzo-agents -try: - from hanzo_agents import ( - Agent as HanzoAgent, - ) - from hanzo_agents import ( - Network as HanzoNetwork, - ) - from hanzo_agents import ( - create_agent, - create_network, - ) - - HANZO_AGENTS_AVAILABLE = True -except ImportError: - HANZO_AGENTS_AVAILABLE = False - - -class TestHanzoNetworkIntegration: - """Test hanzo-network multi-agent orchestration capabilities.""" - - @pytest.fixture - def temp_dir(self): - """Create a temporary directory for tests.""" - with tempfile.TemporaryDirectory() as tmpdir: - yield Path(tmpdir) - - @pytest.fixture - async def basic_network(self): - """Create a basic agent network.""" - # Define network configuration - config = NetworkConfig( - name="test-network", - description="Test multi-agent network", - max_agents=5, - enable_memory=True, - ) - - # Create network - network = await create_agent_network(config) - yield network - - # Cleanup - await network.shutdown() - - async def test_network_creation(self, tool_helper, basic_network): - """Test that a network can be created successfully.""" - assert basic_network is not None - assert basic_network.name == "test-network" - assert basic_network.max_agents == 5 - assert basic_network.is_running - - async def test_agent_addition(self, tool_helper, basic_network): - """Test adding agents to the network.""" - # Create agents with different roles - architect = Agent( - name="architect", - role="System Architect", - capabilities=["design", "planning", "architecture"], - model="gpt-4", - ) - - developer = Agent( - name="developer", - role="Software Developer", - capabilities=["coding", "testing", "debugging"], - model="claude-3-sonnet", - ) - - # Add agents to network - await basic_network.add_agent(architect) - await basic_network.add_agent(developer) - - # Verify agents were added - assert len(basic_network.agents) == 2 - assert "architect" in basic_network.agents - assert "developer" in basic_network.agents - - async def test_agent_communication(self, tool_helper, basic_network): - """Test agent-to-agent communication.""" - # Create communicating agents - agent1 = Agent( - name="agent1", role="Coordinator", capabilities=["coordination", "planning"] - ) - - agent2 = Agent( - name="agent2", role="Worker", capabilities=["execution", "reporting"] - ) - - # Add message handler to agent2 - messages_received = [] - - async def handle_message(message: Dict[str, Any]): - messages_received.append(message) - return { - "status": "received", - "content": f"Acknowledged: {message['content']}", - } - - agent2.on_message = handle_message - - # Add agents to network - await basic_network.add_agent(agent1) - await basic_network.add_agent(agent2) - - # Send message from agent1 to agent2 - response = await basic_network.send_message( - from_agent="agent1", to_agent="agent2", content="Please execute task X" - ) - - # Verify communication - assert len(messages_received) == 1 - assert messages_received[0]["content"] == "Please execute task X" - assert response["status"] == "received" - - async def test_tool_sharing(self, tool_helper, basic_network, temp_dir): - """Test tool sharing between agents.""" - - # Create a file tool - class FileTool(Tool): - def __init__(self, base_path: Path): - super().__init__(name="file_tool", description="Read and write files") - self.base_path = base_path - - async def execute(self, action: str, **kwargs) -> Dict[str, Any]: - if action == "write": - path = self.base_path / kwargs["filename"] - path.write_text(kwargs["content"]) - return {"status": "success", "path": str(path)} - elif action == "read": - path = self.base_path / kwargs["filename"] - content = path.read_text() if path.exists() else None - return {"status": "success", "content": content} - - # Create agents - writer = Agent(name="writer", role="Content Writer") - reader = Agent(name="reader", role="Content Reader") - - # Create and share tool - file_tool = FileTool(temp_dir) - await basic_network.add_shared_tool(file_tool) - - # Add agents - await basic_network.add_agent(writer) - await basic_network.add_agent(reader) - - # Writer uses tool to create file - write_result = await basic_network.execute_tool( - agent_name="writer", - tool_name="file_tool", - action="write", - filename="shared.txt", - content="This is shared content", - ) - - assert write_result["status"] == "success" - - # Reader uses tool to read file - read_result = await basic_network.execute_tool( - agent_name="reader", - tool_name="file_tool", - action="read", - filename="shared.txt", - ) - - assert read_result["status"] == "success" - assert read_result["content"] == "This is shared content" - - async def test_memory_sharing(self, tool_helper, basic_network): - """Test shared memory between agents.""" - # Create memory tool - memory_tool = create_memory_tool("test-memory") - await basic_network.add_shared_tool(memory_tool) - - # Create agents - learner = Agent(name="learner", role="Knowledge Collector") - teacher = Agent(name="teacher", role="Knowledge Provider") - - await basic_network.add_agent(learner) - await basic_network.add_agent(teacher) - - # Teacher stores knowledge - await basic_network.execute_tool( - agent_name="teacher", - tool_name="test-memory", - action="store", - key="python_tip", - value="Use list comprehensions for cleaner code", - ) - - # Learner retrieves knowledge - result = await basic_network.execute_tool( - agent_name="learner", - tool_name="test-memory", - action="retrieve", - key="python_tip", - ) - - assert result["value"] == "Use list comprehensions for cleaner code" - - async def test_orchestrated_workflow(self, tool_helper, basic_network, temp_dir): - """Test a complete orchestrated workflow with multiple agents.""" - # Create specialized agents - pm = Agent( - name="project_manager", - role="Project Manager", - capabilities=["planning", "coordination"], - model="gpt-4", - ) - - architect = Agent( - name="architect", - role="Software Architect", - capabilities=["design", "architecture"], - model="claude-3-sonnet", - ) - - developer = Agent( - name="developer", - role="Developer", - capabilities=["coding", "implementation"], - model="gpt-3.5-turbo", - ) - - tester = Agent( - name="tester", - role="QA Engineer", - capabilities=["testing", "validation"], - model="gpt-3.5-turbo", - ) - - # Add all agents - for agent in [pm, architect, developer, tester]: - await basic_network.add_agent(agent) - - # Define workflow - workflow = { - "name": "feature_development", - "steps": [ - { - "agent": "project_manager", - "task": "Define requirements for user authentication feature", - "output": "requirements", - }, - { - "agent": "architect", - "task": "Design system architecture based on {requirements}", - "depends_on": ["requirements"], - "output": "architecture", - }, - { - "agent": "developer", - "task": "Implement authentication based on {architecture}", - "depends_on": ["architecture"], - "output": "implementation", - }, - { - "agent": "tester", - "task": "Test the {implementation}", - "depends_on": ["implementation"], - "output": "test_results", - }, - ], - } - - # Execute workflow - results = await basic_network.execute_workflow(workflow) - - # Verify workflow completed - assert "requirements" in results - assert "architecture" in results - assert "implementation" in results - assert "test_results" in results - - # Each step should have produced output - for step_output in results.values(): - assert step_output is not None - assert "status" in step_output or "content" in step_output - - async def test_consensus_decision(self, tool_helper, basic_network): - """Test consensus-based decision making.""" - # Create decision-making agents - agents = [] - for i in range(3): - agent = Agent( - name=f"advisor_{i}", - role=f"Technical Advisor {i}", - model="gpt-3.5-turbo", - ) - agents.append(agent) - await basic_network.add_agent(agent) - - # Define decision question - question = "Should we use microservices architecture for this project?" - - # Get consensus - consensus_result = await basic_network.get_consensus( - question=question, - agents=["advisor_0", "advisor_1", "advisor_2"], - threshold=0.66, # 2 out of 3 must agree - ) - - # Verify consensus result - assert "decision" in consensus_result - assert "confidence" in consensus_result - assert "votes" in consensus_result - assert len(consensus_result["votes"]) == 3 - - async def test_local_compute_orchestration(self, tool_helper, basic_network): - """Test local compute orchestration for cost optimization.""" - # Create local compute orchestrator - orchestrator = LocalComputeOrchestrator( - preferred_local_model="llama2:7b", - cost_threshold=0.01, # Use local for tasks under 1 cent - ) - - # Attach to network - basic_network.set_orchestrator(orchestrator) - - # Create mixed agents (some local, some API) - local_agent = Agent( - name="local_helper", - role="Local Assistant", - model="llama2:7b", - is_local=True, - ) - - api_agent = Agent( - name="api_expert", role="Expert Consultant", model="gpt-4", is_local=False - ) - - await basic_network.add_agent(local_agent) - await basic_network.add_agent(api_agent) - - # Simple task (should go to local) - simple_result = await basic_network.delegate_task( - task='Format this JSON: {"name":"test"}', complexity="simple" - ) - - assert simple_result["agent"] == "local_helper" - assert simple_result["cost"] < 0.01 - - # Complex task (should go to API) - complex_result = await basic_network.delegate_task( - task="Design a distributed system for handling 1M requests/second", - complexity="complex", - ) - - assert complex_result["agent"] == "api_expert" - - async def test_agent_network_persistence( - self, tool_helper, basic_network, temp_dir - ): - """Test saving and loading agent network state.""" - # Add some agents and state - agent1 = Agent(name="persistent_agent", role="Keeper") - await basic_network.add_agent(agent1) - - # Add shared memory - await basic_network.execute_tool( - agent_name="persistent_agent", - tool_name="memory", - action="store", - key="important_data", - value="This must persist", - ) - - # Save network state - state_file = temp_dir / "network_state.json" - await basic_network.save_state(state_file) - - assert state_file.exists() - - # Create new network and load state - new_network = await create_agent_network(NetworkConfig(name="restored-network")) - - await new_network.load_state(state_file) - - # Verify state was restored - assert "persistent_agent" in new_network.agents - - # Check memory was restored - memory_result = await new_network.execute_tool( - agent_name="persistent_agent", - tool_name="memory", - action="retrieve", - key="important_data", - ) - - assert memory_result["value"] == "This must persist" - - -@pytest.mark.skipif( - not (HANZO_NETWORK_AVAILABLE and HANZO_AGENTS_AVAILABLE), - reason="Both hanzo-network and hanzo-agents required", -) -class TestHanzoNetworkMCPIntegration: - """Test hanzo-network integration with MCP tools and servers.""" - - async def test_mcp_tool_integration(self, tool_helper, temp_dir): - """Test agents using MCP tools through hanzo-network.""" - # Create network with MCP support - network = await create_agent_network( - NetworkConfig( - name="mcp-network", - enable_mcp_tools=True, - mcp_allowed_paths=[str(temp_dir)], - ) - ) - - # Create agent with MCP access - mcp_agent = Agent( - name="mcp_agent", - role="MCP Tool User", - capabilities=["file_operations", "search"], - has_mcp_access=True, - ) - - await network.add_agent(mcp_agent) - - # Use MCP write tool through agent - write_result = await network.execute_mcp_tool( - agent_name="mcp_agent", - tool_name="write", - arguments={ - "path": str(temp_dir / "mcp_test.txt"), - "content": "Written through MCP", - }, - ) - - assert "success" in str(write_result).lower() - assert (temp_dir / "mcp_test.txt").read_text() == "Written through MCP" - - # Use MCP search tool - search_result = await network.execute_mcp_tool( - agent_name="mcp_agent", - tool_name="search", - arguments={"pattern": "MCP", "path": str(temp_dir)}, - ) - - assert "results" in search_result - - async def test_multi_agent_mcp_workflow(self, tool_helper, temp_dir): - """Test multiple agents collaborating through MCP tools.""" - # Create network - network = await create_agent_network( - NetworkConfig( - name="collaborative-mcp", - enable_mcp_tools=True, - mcp_allowed_paths=[str(temp_dir)], - ) - ) - - # Create specialized agents - analyst = Agent(name="analyst", role="Code Analyst", has_mcp_access=True) - - refactorer = Agent( - name="refactorer", role="Code Refactorer", has_mcp_access=True - ) - - reviewer = Agent(name="reviewer", role="Code Reviewer", has_mcp_access=True) - - for agent in [analyst, refactorer, reviewer]: - await network.add_agent(agent) - - # Create a file with code to analyze - code_file = temp_dir / "legacy_code.py" - code_file.write_text(""" -def calculate(x, y): - # TODO: Add error handling - result = x + y - print(result) - return result - -def process_data(data): - # Complex function that needs refactoring - output = [] - for i in range(len(data)): - if data[i] > 0: - output.append(data[i] * 2) - return output -""") - - # Workflow: Analyze -> Refactor -> Review - workflow_result = await network.execute_collaborative_task( - task="Improve the code quality in legacy_code.py", - steps=[ - { - "agent": "analyst", - "action": "analyze", - "mcp_tools": ["read", "search"], - "focus": "Identify code smells and TODOs", - }, - { - "agent": "refactorer", - "action": "refactor", - "mcp_tools": ["read", "edit", "multi_edit"], - "focus": "Improve code based on analysis", - }, - { - "agent": "reviewer", - "action": "review", - "mcp_tools": ["read", "critic"], - "focus": "Review changes and ensure quality", - }, - ], - ) - - # Verify workflow completed - assert workflow_result["status"] == "completed" - assert len(workflow_result["steps"]) == 3 - - # Check that code was actually modified - modified_code = code_file.read_text() - assert modified_code != code_file.read_text() # Should be different - - # Should have better error handling and cleaner list comprehension - assert "try:" in modified_code or "except:" in modified_code - assert "[" in modified_code and "for" in modified_code # List comprehension - - -if __name__ == "__main__": - # Run the tests - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_integration.py.skip b/pkg/hanzo-mcp/tests/test_integration.py.skip deleted file mode 100644 index 05f547b21..000000000 --- a/pkg/hanzo-mcp/tests/test_integration.py.skip +++ /dev/null @@ -1,515 +0,0 @@ -"""Integration tests for MCP tools working together.""" - -import asyncio -import os -import tempfile -from unittest.mock import Mock, patch, MagicMock -from tests.test_utils import ToolTestHelper, create_mock_ctx, create_permission_manager - -import pytest - -from hanzo_mcp.tools.common.permissions import PermissionManager -from hanzo_mcp.tools.filesystem.read import ReadTool -from hanzo_mcp.tools.filesystem.write import Write as WriteTool -from hanzo_mcp.tools.filesystem.edit import Edit as EditTool -from hanzo_mcp.tools.filesystem.grep import Grep as GrepTool -from hanzo_mcp.tools.shell.bash_tool import BashTool -from hanzo_mcp.tools.todo.todo import TodoTool -from hanzo_mcp.tools.common.batch_tool import BatchTool -from hanzo_mcp.tools.common.thinking_tool import ThinkingTool - - -class TestFileSystemAndShellIntegration: - """Test filesystem tools working with shell commands.""" - - def test_create_edit_and_verify_workflow(self, tool_helper): - """Test creating, editing, and verifying files.""" - with tempfile.TemporaryDirectory() as tmpdir: - pm = PermissionManager() - pm.add_allowed_path(tmpdir) - - write_tool = WriteTool(pm) - edit_tool = EditTool(pm) - read_tool = ReadTool(pm) - bash_tool = BashTool(pm) - mock_ctx = create_mock_ctx() - - # 1. Create a Python file - filepath = os.path.join(tmpdir, "test_script.py") - initial_content = '''def greet(name): - return f"Hello, {name}!" - -def main(): - print(greet("World")) - -if __name__ == "__main__": - main() -''' - - result = asyncio.run(write_tool.call( - mock_ctx, - file_path=filepath, - content=initial_content - )) - assert "successfully" in result.lower() - - # 2. Edit the file to add a new function - edit_result = asyncio.run(edit_tool.call( - mock_ctx, - file_path=filepath, - old_string='def main():\n print(greet("World"))', - new_string='def goodbye(name):\n return f"Goodbye, {name}!"\n\ndef main():\n print(greet("World"))\n print(goodbye("World"))' - )) - assert "successfully" in edit_result.lower() - - # 3. Read the file to verify - read_result = asyncio.run(read_tool.call(mock_ctx, file_path=filepath)) - assert "def goodbye(name):" in read_result - assert "Goodbye, {name}!" in read_result - - # 4. Run the script - run_result = asyncio.run(bash_tool.call( - mock_ctx, - command=f"cd {tmpdir} && python test_script.py" - )) - assert "Hello, World!" in run_result - assert "Goodbye, World!" in run_result - - def test_grep_edit_workflow(self, tool_helper): - """Test finding patterns and editing them.""" - with tempfile.TemporaryDirectory() as tmpdir: - pm = PermissionManager() - pm.add_allowed_path(tmpdir) - - write_tool = WriteTool(pm) - grep_tool = GrepTool(pm) - edit_tool = EditTool(pm) - mock_ctx = create_mock_ctx() - - # Create multiple files with TODOs - for i in range(3): - filepath = os.path.join(tmpdir, f"module_{i}.py") - content = f'''# Module {i} - -def function_{i}(): - # TODO: Implement this function - pass - -def helper_{i}(): - # TODO: Add error handling - return None -''' - asyncio.run(write_tool.call(mock_ctx, file_path=filepath, content=content)) - - # Find all TODOs - grep_result = asyncio.run(grep_tool.call( - mock_ctx, - pattern="TODO", - path=tmpdir, - output_mode="content", - line_numbers=True - )) - - assert "TODO: Implement this function" in grep_result - assert "TODO: Add error handling" in grep_result - assert "module_0.py" in grep_result - assert "module_1.py" in grep_result - assert "module_2.py" in grep_result - - # Edit one of the TODOs - edit_result = asyncio.run(edit_tool.call( - mock_ctx, - file_path=os.path.join(tmpdir, "module_0.py"), - old_string=" # TODO: Implement this function\n pass", - new_string=" # Function implemented\n return 'Module 0 implementation'" - )) - assert "successfully" in edit_result.lower() - - # Verify the edit - grep_after = asyncio.run(grep_tool.call( - mock_ctx, - pattern="TODO", - path=os.path.join(tmpdir, "module_0.py"), - output_mode="count" - )) - # Should have one less TODO in module_0.py - assert "1" in grep_after # Only one TODO left in module_0 - - -class TestBatchAndTodoIntegration: - """Test batch operations with todo tracking.""" - - def test_batch_file_operations_with_todos(self, tool_helper): - """Test using batch tool for multiple operations with todo tracking.""" - with tempfile.TemporaryDirectory() as tmpdir: - pm = PermissionManager() - pm.add_allowed_path(tmpdir) - - # Create tools - write_tool = WriteTool(pm) - read_tool = ReadTool(pm) - todo_tool = TodoTool() - - # Create batch tool with our tools - tools = { - "write": write_tool, - "read": read_tool, - "todo": todo_tool - } - batch_tool = BatchTool(tools) - mock_ctx = create_mock_ctx() - - # Create batch operations - invocations = [ - # First, create a todo list - { - "tool": "todo", - "parameters": { - "operation": "add", - "items": [ - "Create configuration file", - "Create main script", - "Create README" - ] - } - }, - # Create the files - { - "tool": "write", - "parameters": { - "file_path": os.path.join(tmpdir, "config.json"), - "content": '{"version": "1.0", "debug": true}' - } - }, - { - "tool": "write", - "parameters": { - "file_path": os.path.join(tmpdir, "main.py"), - "content": 'print("Hello from main!")' - } - }, - { - "tool": "write", - "parameters": { - "file_path": os.path.join(tmpdir, "README.md"), - "content": '# Test Project\n\nThis is a test.' - } - }, - # Mark todos as complete - { - "tool": "todo", - "parameters": { - "operation": "complete", - "indices": [0, 1, 2] - } - }, - # Get final todo status - { - "tool": "todo", - "parameters": { - "operation": "list" - } - } - ] - - # Execute batch - result = asyncio.run(batch_tool.call( - mock_ctx, - description="Create project files with todo tracking", - invocations=invocations - )) - - # Parse results - tool_helper.assert_in_result("results", result) - results_data = eval(result.split("results:")[1].strip()) # Simple parsing - - # Verify files were created - assert os.path.exists(os.path.join(tmpdir, "config.json")) - assert os.path.exists(os.path.join(tmpdir, "main.py")) - assert os.path.exists(os.path.join(tmpdir, "README.md")) - - -class TestMemorySearchIntegration: - """Test memory tools with search integration.""" - - @patch('hanzo_memory.services.memory.get_memory_service') - def test_memory_and_code_context_workflow(self, tool_helper, mock_get_service): - """Test storing code context in memory and recalling it.""" - from hanzo_mcp.tools.memory.memory_tools import CreateMemoriesTool, RecallMemoriesTool - from hanzo_mcp.tools.memory.knowledge_tools import StoreFactsTool, RecallFactsTool - - # Mock memory service - mock_service = Mock() - memories_db = {} - - def mock_create(user_id, project_id, content, metadata=None, **kwargs): - mem_id = f"mem_{len(memories_db)}" - memory = Mock( - memory_id=mem_id, - user_id=user_id, - project_id=project_id, - content=content, - metadata=metadata or {} - ) - memories_db[mem_id] = memory - return memory - - def mock_search(user_id, query, project_id=None, **kwargs): - results = [] - for memory in memories_db.values(): - if user_id == memory.user_id: - # Simple search - check if query terms in content - if any(term.lower() in memory.content.lower() for term in query.split()): - results.append(Mock( - **memory.__dict__, - similarity_score=0.9 - )) - return results - - mock_service.create_memory = mock_create - mock_service.search_memories = mock_search - mock_get_service.return_value = mock_service - - mock_ctx = create_mock_ctx() - - # 1. Store facts about code patterns - facts_tool = StoreFactsTool(user_id="dev", project_id="myproject") - facts_result = asyncio.run(facts_tool.call( - mock_ctx, - facts=[ - "Always use async/await for I/O operations", - "Error handling should use try/except blocks", - "All functions need type hints" - ], - kb_name="coding_standards" - )) - assert "Successfully stored 3 facts" in facts_result - - # 2. Store memory about specific implementation - memory_tool = CreateMemoriesTool(user_id="dev", project_id="myproject") - memory_result = asyncio.run(memory_tool.call( - mock_ctx, - statements=[ - "The database connection uses PostgreSQL with pgvector", - "Authentication is handled by Clerk", - "The main API uses FastAPI framework" - ] - )) - assert "Successfully created 3 new memories" in memory_result - - # 3. Recall relevant information - recall_tool = RecallMemoriesTool(user_id="dev", project_id="myproject") - - # Search for database info - db_result = asyncio.run(recall_tool.call( - mock_ctx, - queries=["database", "PostgreSQL"] - )) - assert "PostgreSQL with pgvector" in db_result - - # Search for framework info - framework_result = asyncio.run(recall_tool.call( - mock_ctx, - queries=["API", "framework"] - )) - assert "FastAPI framework" in framework_result - - -class TestAgentSwarmIntegration: - """Test agent swarm with other tools.""" - - @patch('hanzo_mcp.tools.agent.swarm_tool.dispatch_to_model') - def test_swarm_with_file_operations(self, tool_helper, mock_dispatch): - """Test swarm agents working with files.""" - from hanzo_mcp.tools.agent.swarm_tool import SwarmTool - - with tempfile.TemporaryDirectory() as tmpdir: - pm = PermissionManager() - pm.add_allowed_path(tmpdir) - - # Mock agent responses - agent_responses = { - "scanner": f"Found 3 Python files in {tmpdir} with TODO comments", - "analyzer": "TODOs are: 1) Add error handling, 2) Implement cache, 3) Write tests", - "implementer": "Fixed TODOs: Added try/except, implemented LRU cache, created test file", - "validator": "All changes verified. Tests pass. No TODOs remaining." - } - - def mock_agent_dispatch(messages, model=None, **kwargs): - # Extract agent ID from messages - for msg in messages: - if "You are agent" in msg.get("content", ""): - agent_id = msg["content"].split("agent ")[1].split(" ")[0] - return agent_responses.get(agent_id, "Unknown agent") - return "No response" - - mock_dispatch.side_effect = mock_agent_dispatch - - swarm_tool = SwarmTool() - mock_ctx = create_mock_ctx() - - # Run swarm to process TODOs - result = asyncio.run(swarm_tool.call( - mock_ctx, - query="Find and fix all TODO comments in the codebase", - agents=[ - { - "id": "scanner", - "query": "Scan all Python files for TODO comments", - "role": "scanner" - }, - { - "id": "analyzer", - "query": "Analyze the TODOs and categorize them", - "role": "analyzer", - "receives_from": ["scanner"] - }, - { - "id": "implementer", - "query": "Implement fixes for each TODO", - "role": "developer", - "receives_from": ["analyzer"] - }, - { - "id": "validator", - "query": "Verify all TODOs are resolved and tests pass", - "role": "tester", - "receives_from": ["implementer"] - } - ] - )) - - # Verify workflow completed - tool_helper.assert_in_result("scanner", result) - tool_helper.assert_in_result("Found 3 Python files", result) - tool_helper.assert_in_result("analyzer", result) - tool_helper.assert_in_result("implementer", result) - tool_helper.assert_in_result("validator", result) - tool_helper.assert_in_result("All changes verified", result) - - -class TestThinkingToolIntegration: - """Test thinking tool with other operations.""" - - def test_think_plan_execute_workflow(self, tool_helper): - """Test using thinking tool to plan before execution.""" - thinking_tool = ThinkingTool() - mock_ctx = create_mock_ctx() - - # 1. Think about the problem - think_result = asyncio.run(thinking_tool.call( - mock_ctx, - content="""Problem: Need to refactor a large function that does too many things. - -The function currently: -- Validates input -- Queries database -- Processes results -- Sends notifications -- Updates cache - -How should I approach this refactoring?""" - )) - - # Should contain structured thinking - assert "```thinking" in think_result - assert "```" in think_result - - # 2. With batch tool, could execute the plan - # (Not shown here but demonstrates the workflow) - - -class TestStreamingAndPaginationIntegration: - """Test streaming commands with pagination.""" - - def test_streaming_large_output_with_pagination(self, tool_helper): - """Test streaming command that produces paginated output.""" - from hanzo_mcp.tools.shell.streaming_command import StreamingCommandTool - - with tempfile.TemporaryDirectory() as tmpdir: - pm = PermissionManager() - pm.add_allowed_path(tmpdir) - - streaming_tool = StreamingCommandTool(pm) - mock_ctx = create_mock_ctx() - - # Create a large file - large_file = os.path.join(tmpdir, "large.txt") - with open(large_file, 'w') as f: - for i in range(10000): - f.write(f"Line {i}: " + "x" * 100 + "\n") - - # Stream reading the file - result = asyncio.run(streaming_tool.call( - mock_ctx, - command=f"cat {large_file}", - stream_to_file=True, - session_id="test_session" - )) - - # Should handle large output - tool_helper.assert_in_result("Line 0:", result) - # May be truncated or have pagination info - if "next_cursor" in result or "truncated" in result: - assert True # Pagination handled - - # Check session file was created - session_file = os.path.expanduser(f"~/.hanzo/sessions/test_session/output.log") - if os.path.exists(session_file): - # Full output should be in session file - with open(session_file, 'r') as f: - full_output = f.read() - assert "Line 9999:" in full_output - - -class TestErrorRecoveryIntegration: - """Test error recovery across tools.""" - - def test_error_handling_in_batch(self, tool_helper): - """Test how batch tool handles errors in individual tools.""" - # Create tools where some will fail - def failing_tool_call(*args, **kwargs): - raise Exception("Tool failed!") - - def working_tool_call(*args, **kwargs): - return "Success" - - failing_tool = Mock() - failing_tool.name = "failing" - failing_tool.call = Mock(side_effect=failing_tool_call) - - working_tool = Mock() - working_tool.name = "working" - working_tool.call = Mock(side_effect=working_tool_call) - - tools = { - "failing": failing_tool, - "working": working_tool - } - - batch_tool = BatchTool(tools) - mock_ctx = create_mock_ctx() - - # Mix of failing and working tools - invocations = [ - {"tool": "working", "parameters": {}}, - {"tool": "failing", "parameters": {}}, - {"tool": "working", "parameters": {}}, - {"tool": "failing", "parameters": {}}, - ] - - result = asyncio.run(batch_tool.call( - mock_ctx, - description="Test error handling", - invocations=invocations - )) - - # Should complete despite errors - tool_helper.assert_in_result("results", result) - tool_helper.assert_in_result("Success", result) - assert "error" in result.lower() - tool_helper.assert_in_result("Tool failed!", result) - - -if __name__ == "__main__": - pytest.main([__file__, "-v"]) \ No newline at end of file diff --git a/pkg/hanzo-mcp/tests/test_integration/__init__.py b/pkg/hanzo-mcp/tests/test_integration/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-mcp/tests/test_integration/test_agent_clarification_critic.py b/pkg/hanzo-mcp/tests/test_integration/test_agent_clarification_critic.py deleted file mode 100644 index 7f36947b9..000000000 --- a/pkg/hanzo-mcp/tests/test_integration/test_agent_clarification_critic.py +++ /dev/null @@ -1,533 +0,0 @@ -"""Integration test for agent clarification and critic features.""" - -import json -from unittest.mock import Mock, patch - -import pytest -from hanzo_tools.agent.agent_tool import AgentTool -from hanzo_tools.agent.clarification_protocol import ClarificationType -from hanzo_tools.agent.critic_tool import ReviewType - -from hanzo_mcp.tools.common.permissions import PermissionManager - - -@pytest.fixture -def mock_context(): - """Create a mock MCP context.""" - ctx = Mock() - ctx.session_id = "test-session" - return ctx - - -@pytest.fixture -def test_project(tmp_path): - """Create a test project structure.""" - project_dir = tmp_path / "test_project" - project_dir.mkdir() - - # Create a test file with import issues - test_file = project_dir / "main.go" - test_file.write_text("""package main - -import ( - "fmt" -) - -func main() { - logger := common.GetLogger() - logger.Info("Starting application") - - config := common.LoadConfig() - fmt.Printf("Config: %v\\n", config) -} -""") - - return project_dir - - -@pytest.mark.asyncio -async def test_agent_clarification_flow(test_project, mock_context): - """Test that agents can request clarification from main loop.""" - permission_manager = PermissionManager(allowed_paths=[str(test_project)]) - - # Create an agent tool with test model - agent = AgentTool( - permission_manager=permission_manager, model="test-model", max_iterations=5 - ) - - # Mock the llm completion to simulate agent requesting clarification - with patch("llm.completion") as mock_completion: - # First call - agent reads file and asks for clarification - mock_completion.side_effect = [ - # First iteration - read file and request clarification - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock( - name="read", - arguments=json.dumps( - {"file_path": str(test_project / "main.go")} - ), - ), - id="call_1", - ) - ], - ) - ) - ] - ), - # Second iteration - process file content and request clarification - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock( - name="request_clarification", - arguments=json.dumps( - { - "type": "MISSING_CONTEXT", - "question": "What is the correct import path for the common package?", - "context": { - "file_path": str( - test_project / "main.go" - ), - "undefined_symbols": ["common"], - }, - "options": [ - "github.com/project/common", - "github.com/company/common", - "../common", - ], - } - ), - ), - id="call_2", - ) - ], - ) - ) - ] - ), - # Third iteration - use clarification to fix the file - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock( - name="multi_edit", - arguments=json.dumps( - { - "file_path": str( - test_project / "main.go" - ), - "edits": [ - { - "old_string": 'import (\n "fmt"\n)', - "new_string": 'import (\n "fmt"\n "github.com/project/common"\n)', - } - ], - } - ), - ), - id="call_3", - ) - ], - ) - ) - ] - ), - # Final response - Mock( - choices=[ - Mock( - message=Mock( - content="I've successfully added the missing import for the common package based on the clarification received.", - tool_calls=None, - ) - ) - ] - ), - ] - - # Execute the agent - result = await agent.call( - mock_context, - prompts=f"Fix the undefined 'common' import in {test_project / 'main.go'}", - ) - - # Verify the result - tool_helper.assert_in_result("successfully added the missing import", result) - assert "clarification" in result.lower() - - # Verify the agent made the expected tool calls - assert mock_completion.call_count == 4 - - # Check file was modified - content = (test_project / "main.go").read_text() - assert "github.com/project/common" in content - - -@pytest.mark.asyncio -async def test_agent_critic_flow(test_project, mock_context): - """Test that agents can request critical review.""" - permission_manager = PermissionManager(allowed_paths=[str(test_project)]) - - # Create an agent tool - agent = AgentTool( - permission_manager=permission_manager, model="test-model", max_iterations=5 - ) - - # Mock the llm completion to simulate agent requesting critic review - with patch("llm.completion") as mock_completion: - mock_completion.side_effect = [ - # First iteration - read file - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock( - name="read", - arguments=json.dumps( - {"file_path": str(test_project / "main.go")} - ), - ), - id="call_1", - ) - ], - ) - ) - ] - ), - # Second iteration - make initial fix - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock( - name="multi_edit", - arguments=json.dumps( - { - "file_path": str( - test_project / "main.go" - ), - "edits": [ - { - "old_string": 'import (\n "fmt"\n)', - "new_string": 'import (\n "fmt"\n "github.com/project/common"\n)', - } - ], - } - ), - ), - id="call_2", - ) - ], - ) - ) - ] - ), - # Third iteration - request critic review - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock( - name="critic", - arguments=json.dumps( - { - "review_type": "CODE_QUALITY", - "work_description": "Added missing import for common package", - "code_snippets": [ - 'import (\n "fmt"\n "github.com/project/common"\n)' - ], - "file_paths": [ - str(test_project / "main.go") - ], - "specific_concerns": "Is the import in the correct format and position?", - } - ), - ), - id="call_3", - ) - ], - ) - ) - ] - ), - # Final response after critic review - Mock( - choices=[ - Mock( - message=Mock( - content="Fixed the import issue. The critic confirmed the import is properly formatted and in the correct position.", - tool_calls=None, - ) - ) - ] - ), - ] - - # Execute the agent - result = await agent.call( - mock_context, - prompts=f"Fix the undefined 'common' import in {test_project / 'main.go'} and verify the fix with critic review", - ) - - # Verify the result - assert "critic confirmed" in result.lower() - assert mock_completion.call_count == 4 - - -@pytest.mark.asyncio -async def test_clarification_limits(test_project, mock_context): - """Test that clarification requests are limited.""" - permission_manager = PermissionManager(allowed_paths=[str(test_project)]) - - agent = AgentTool( - permission_manager=permission_manager, model="test-model", max_iterations=5 - ) - - # Test that only one clarification is allowed - with patch("llm.completion") as mock_completion: - mock_completion.side_effect = [ - # First clarification request - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock( - name="request_clarification", - arguments=json.dumps( - { - "type": "MISSING_CONTEXT", - "question": "First question", - "context": {}, - } - ), - ), - id="call_1", - ) - ], - ) - ) - ] - ), - # Try second clarification (should fail) - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock( - name="request_clarification", - arguments=json.dumps( - { - "type": "MISSING_CONTEXT", - "question": "Second question", - "context": {}, - } - ), - ), - id="call_2", - ) - ], - ) - ) - ] - ), - # Final response - Mock( - choices=[ - Mock( - message=Mock( - content="Completed with clarification limit error.", - tool_calls=None, - ) - ) - ] - ), - ] - - result = await agent.call(mock_context, prompts="Test clarification limits") - - # The second clarification should fail - assert "limit" in result.lower() - - -@pytest.mark.asyncio -async def test_critic_limits(test_project, mock_context): - """Test that critic reviews are limited.""" - permission_manager = PermissionManager(allowed_paths=[str(test_project)]) - - agent = AgentTool( - permission_manager=permission_manager, model="test-model", max_iterations=6 - ) - - # Test that only two critic reviews are allowed - with patch("llm.completion") as mock_completion: - review_args = json.dumps( - { - "review_type": "GENERAL", - "work_description": "Test work", - "code_snippets": None, - "file_paths": None, - "specific_concerns": None, - } - ) - - mock_completion.side_effect = [ - # First review - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock(name="critic", arguments=review_args), - id="call_1", - ) - ], - ) - ) - ] - ), - # Second review - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock(name="critic", arguments=review_args), - id="call_2", - ) - ], - ) - ) - ] - ), - # Third review (should fail) - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock(name="critic", arguments=review_args), - id="call_3", - ) - ], - ) - ) - ] - ), - # Final response - Mock( - choices=[ - Mock( - message=Mock( - content="Completed with review limit exceeded.", - tool_calls=None, - ) - ) - ] - ), - ] - - result = await agent.call(mock_context, prompts="Test critic review limits") - - # The third review should fail - assert "limit exceeded" in result.lower() - - -@pytest.mark.asyncio -async def test_clarification_protocol_types(): - """Test all clarification types work correctly.""" - from hanzo_tools.agent.clarification_protocol import ClarificationHandler - - handler = ClarificationHandler() - - # Test each clarification type - types_to_test = [ - (ClarificationType.AMBIGUOUS_INSTRUCTION, {"file_path": "test.go"}), - (ClarificationType.MISSING_CONTEXT, {}), - (ClarificationType.MULTIPLE_OPTIONS, {}), - (ClarificationType.CONFIRMATION_NEEDED, {}), - (ClarificationType.ADDITIONAL_INFO, {}), - ] - - for clarification_type, context in types_to_test: - request_id = handler.create_request( - agent_id="test-agent", - request_type=clarification_type, - question=f"Test question for {clarification_type.value}", - context=context, - options=( - ["option1", "option2"] - if clarification_type == ClarificationType.MULTIPLE_OPTIONS - else None - ), - ) - - assert request_id.startswith("clarify_") - assert len(handler.pending_requests) > 0 - - # Get the request and handle it - request = handler.pending_requests[request_id] - response = handler.handle_request(request) - - assert response.answer - assert len(response.answer) > 0 - - -@pytest.mark.asyncio -async def test_critic_review_types(): - """Test all review types work correctly.""" - from hanzo_tools.agent.critic_tool import AutoCritic - - critic = AutoCritic() - - # Test each review type - for review_type in ReviewType: - review = critic.review( - review_type=review_type, - work_description="Test work for review", - code_snippets=["import fmt", "if err != nil { return err }"], - file_paths=["test.go"], - specific_concerns="Is this correct?", - ) - - assert review - assert "REVIEW" in review - assert len(review) > 50 # Ensure substantial feedback - - -if __name__ == "__main__": - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_integration/test_all_agent_tools.py b/pkg/hanzo-mcp/tests/test_integration/test_all_agent_tools.py deleted file mode 100644 index b9cdb648f..000000000 --- a/pkg/hanzo-mcp/tests/test_integration/test_all_agent_tools.py +++ /dev/null @@ -1,402 +0,0 @@ -"""Integration test for all agent tools working together.""" - -import json -import sys -from types import ModuleType -from unittest.mock import Mock, patch - -import pytest -from hanzo_tools.agent.agent_tool import AgentTool - -# Ensure 'llm' module is available for mocking even if not installed -if "llm" not in sys.modules: - _mock_llm = ModuleType("llm") - _mock_llm.completion = Mock() - sys.modules["llm"] = _mock_llm - - -@pytest.fixture -def mock_context(): - """Create a mock MCP context.""" - ctx = Mock() - ctx.session_id = "test-session" - return ctx - - -@pytest.fixture -def test_project(tmp_path): - """Create a test project structure.""" - project_dir = tmp_path / "test_project" - project_dir.mkdir() - - # Create a test file with a challenging problem - test_file = project_dir / "complex_algorithm.py" - test_file.write_text("""def find_optimal_path(graph, start, end): - # TODO: Implement this complex pathfinding algorithm - # Should handle weighted graphs, cycles, and negative edges - pass -""") - - return project_dir - - -@pytest.mark.asyncio -@pytest.mark.skip(reason="Requires CLI agent backends (claude/codex/grok) installed") -async def test_agent_uses_all_tools(test_project, mock_context): - """Test that an agent can use clarification, critic, review, and zen tools.""" - # Create an agent tool - agent = AgentTool() - - # Mock the llm completion to simulate agent using all tools - with patch("llm.completion") as mock_completion: - mock_completion.side_effect = [ - # 1. Read the file - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock( - name="read", - arguments=json.dumps( - { - "file_path": str( - test_project - / "complex_algorithm.py" - ) - } - ), - ), - id="call_1", - ) - ], - ) - ) - ] - ), - # 2. Use zen guidance for creative direction - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock( - name="zen", - arguments=json.dumps( - { - "challenge": "How should I approach implementing this complex pathfinding algorithm?" - } - ), - ), - id="call_2", - ) - ], - ) - ) - ] - ), - # 3. Request clarification about requirements - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock( - name="request_clarification", - arguments=json.dumps( - { - "type": "MISSING_CONTEXT", - "question": "Should the algorithm optimize for shortest path or lowest cost?", - "context": { - "algorithm": "pathfinding", - "requirements": "unclear", - }, - "options": [ - "shortest_distance", - "lowest_cost", - "balanced", - ], - } - ), - ), - id="call_3", - ) - ], - ) - ) - ] - ), - # 4. Implement initial solution - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock( - name="multi_edit", - arguments=json.dumps( - { - "file_path": str( - test_project - / "complex_algorithm.py" - ), - "edits": [ - { - "old_string": """def find_optimal_path(graph, start, end): - # TODO: Implement this complex pathfinding algorithm - # Should handle weighted graphs, cycles, and negative edges - pass""", - "new_string": """def find_optimal_path(graph, start, end): - \"\"\"Find optimal path using modified Dijkstra's algorithm. - - Handles weighted graphs, cycles, and uses Bellman-Ford for negative edges. - \"\"\" - # Implementation here - return [] # Placeholder""", - } - ], - } - ), - ), - id="call_4", - ) - ], - ) - ) - ] - ), - # 5. Request balanced review - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock( - name="review", - arguments=json.dumps( - { - "focus": "FUNCTIONALITY", - "work_description": "Implemented pathfinding algorithm with Dijkstra/Bellman-Ford hybrid", - "code_snippets": [ - 'def find_optimal_path(graph, start, end):\n """Find optimal path"""' - ], - "file_paths": [ - str( - test_project - / "complex_algorithm.py" - ) - ], - "context": "This needs to handle various graph types efficiently", - } - ), - ), - id="call_5", - ) - ], - ) - ) - ] - ), - # 6. Request critic review (devil's advocate) - Mock( - choices=[ - Mock( - message=Mock( - content=None, - tool_calls=[ - Mock( - function=Mock( - name="critic", - arguments=json.dumps( - { - "review_type": "PERFORMANCE", - "work_description": "Pathfinding algorithm implementation", - "code_snippets": [ - "def find_optimal_path(graph, start, end):" - ], - "file_paths": [ - str( - test_project - / "complex_algorithm.py" - ) - ], - "specific_concerns": "Will this scale to large graphs?", - } - ), - ), - id="call_6", - ) - ], - ) - ) - ] - ), - # 7. Final response incorporating all feedback - Mock( - choices=[ - Mock( - message=Mock( - content="""I've implemented the pathfinding algorithm with guidance from multiple perspectives: - -1. **Zen Guidance**: Provided creative insights about balancing simplicity and power -2. **Clarification**: Confirmed we should optimize for lowest cost -3. **Implementation**: Created a hybrid Dijkstra/Bellman-Ford approach -4. **Balanced Review**: Highlighted good structure and suggested edge case handling -5. **Critic Review**: Challenged performance assumptions and pushed for optimization - -The final implementation addresses all concerns raised.""", - tool_calls=None, - ) - ) - ] - ), - ] - - # Execute the agent - result = await agent.call( - mock_context, - prompt=f"Implement the complex pathfinding algorithm in {test_project / 'complex_algorithm.py'} using all available tools for guidance", - ) - - # Verify the result mentions all tools - assert "zen" in result.lower() - assert "clarification" in result.lower() - assert "review" in result.lower() - assert "critic" in result.lower() - - # Verify all tool calls were made - assert mock_completion.call_count == 7 - - -@pytest.mark.asyncio -async def test_zen_tool_directly(): - """Test the zen tool directly.""" - from hanzo_tools.agent.zen_tool import ZenTool - - tool = ZenTool() - ctx = Mock() - - # Test with different challenges - challenges = [ - "How should I scale this microservice architecture?", - "What's the best approach to refactor legacy code?", - "How do I improve team collaboration?", - "Should I optimize for performance or maintainability?", - ] - - for challenge in challenges: - result = await tool.call(ctx, challenge) - if isinstance(result, dict) and "output" in result: - result = result["output"] - - # Verify result structure - assert "ZEN GUIDANCE" in result - assert "Hexagram Cast" in result - assert "Hanzo Principles" in result - assert "Synthesized Approach" in result - assert "The Way Forward" in result - - # Verify it selected relevant principles - if "scale" in challenge.lower(): - assert any( - word in result for word in ["Scalable", "Exponentiality", "Growth"] - ) - elif "refactor" in challenge.lower(): - assert any( - word in result for word in ["Simplicity", "Clarity", "Composable"] - ) - elif "team" in challenge.lower(): - assert any( - word in result for word in ["Autonomy", "Balance", "Collaboration"] - ) - - -@pytest.mark.asyncio -async def test_review_vs_critic_difference(): - """Test that review and critic provide different styles of feedback.""" - from hanzo_tools.agent.critic_tool import CriticProtocol - from hanzo_tools.agent.review_tool import ReviewProtocol - - critic = CriticProtocol() - reviewer = ReviewProtocol() - - work_description = "Implemented user authentication with JWT tokens" - code_snippet = [ - "func authenticate(token string) (User, error) { return User{}, nil }" - ] - - # Get critic feedback (harsh) - critic_feedback = critic.request_review( - review_type="SECURITY", - work_description=work_description, - code_snippets=code_snippet, - ) - - # Get review feedback (balanced) - use GENERAL since ReviewFocus has no SECURITY - review_feedback = reviewer.request_review( - focus="GENERAL", work_description=work_description, code_snippets=code_snippet - ) - - # Critic should be harsher - assert "โŒ" in critic_feedback or "โš ๏ธ" in critic_feedback - assert "!" in critic_feedback # Exclamation marks indicate urgency - - # Review should be more balanced - assert "Positive Aspects" in review_feedback or "โœ“" in review_feedback - assert "Suggestions" in review_feedback - - # Critic should mention security (it has SECURITY review type) - assert "security" in critic_feedback.lower() - - -@pytest.mark.asyncio -async def test_tool_limits(): - """Test that tools respect their usage limits.""" - from hanzo_tools.agent.clarification_protocol import ClarificationHandler - from hanzo_tools.agent.critic_tool import CriticProtocol - from hanzo_tools.agent.review_tool import ReviewProtocol - - # Test clarification limit (1) - clarifier = ClarificationHandler() - clarifier.clarification_count = 0 - clarifier.max_clarifications = 1 - - # First should work - try: - await clarifier.request_clarification( - ClarificationType.MISSING_CONTEXT, "Question 1", {} - ) - except AttributeError: - # Handler doesn't have async request_clarification, that's ok - pass - - # Test critic limit (2) - critic = CriticProtocol() - assert critic.max_reviews == 2 - - # Test review limit (3) - reviewer = ReviewProtocol() - assert reviewer.max_reviews == 3 - - # Test exceeding limits - critic.review_count = 2 - result = critic.request_review("GENERAL", "test", None, None, None) - assert "limit exceeded" in result.lower() - - reviewer.review_count = 3 - result = reviewer.request_review("GENERAL", "test", None, None, None) - assert "limit reached" in result.lower() - - -if __name__ == "__main__": - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_jupyter/__init__.py b/pkg/hanzo-mcp/tests/test_jupyter/__init__.py deleted file mode 100644 index e7e947e65..000000000 --- a/pkg/hanzo-mcp/tests/test_jupyter/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Jupyter notebook test package.""" diff --git a/pkg/hanzo-mcp/tests/test_jupyter/test_unified_jupyter.py b/pkg/hanzo-mcp/tests/test_jupyter/test_unified_jupyter.py deleted file mode 100644 index 655d2e32a..000000000 --- a/pkg/hanzo-mcp/tests/test_jupyter/test_unified_jupyter.py +++ /dev/null @@ -1,229 +0,0 @@ -"""Test unified Jupyter tool implementation.""" - -import json - -import pytest -from hanzo_tools.jupyter.jupyter import JupyterTool -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_mcp.tools.common.permissions import PermissionManager - - -@pytest.fixture -def permission_manager(tmp_path): - """Create a permission manager with tmp_path as allowed.""" - pm = PermissionManager() - pm.add_allowed_path(str(tmp_path)) - return pm - - -@pytest.fixture -def jupyter_tool(permission_manager): - """Create JupyterTool instance.""" - return JupyterTool(permission_manager) - - -@pytest.fixture -def sample_notebook(tmp_path): - """Create a sample notebook for testing.""" - notebook_path = tmp_path / "test.ipynb" - notebook_content = { - "cells": [ - { - "cell_type": "code", - "source": "print('Hello, World!')", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": "Hello, World!\n", - } - ], - "execution_count": 1, - }, - { - "cell_type": "markdown", - "source": "# Test Notebook\nThis is a test.", - "metadata": {}, - }, - ], - "metadata": {"language_info": {"name": "python"}}, - "nbformat": 4, - "nbformat_minor": 5, - } - - with open(notebook_path, "w") as f: - json.dump(notebook_content, f) - - return notebook_path - - -@pytest.mark.asyncio -class TestUnifiedJupyterTool: - """Test the unified Jupyter tool.""" - - @pytest.mark.asyncio - async def test_read_action(self, jupyter_tool, sample_notebook): - """Test reading a notebook.""" - ctx = MCPContext() - - # Read entire notebook - result = await jupyter_tool.call( - ctx, action="read", notebook_path=str(sample_notebook) - ) - - assert "Notebook with 2 cells" in result - assert "Hello, World!" in result - assert "Test Notebook" in result - assert "[stdout]: Hello, World!" in result - - # Read specific cell by index - result = await jupyter_tool.call( - ctx, action="read", notebook_path=str(sample_notebook), cell_index=0 - ) - - assert "Cell 0 (code)" in result - assert "print('Hello, World!')" in result - - @pytest.mark.asyncio - async def test_create_action(self, jupyter_tool, tmp_path): - """Test creating a new notebook.""" - ctx = MCPContext() - new_notebook = tmp_path / "new.ipynb" - - result = await jupyter_tool.call( - ctx, action="create", notebook_path=str(new_notebook) - ) - - assert "Successfully created notebook" in result - assert new_notebook.exists() - - # Verify it's a valid notebook - with open(new_notebook) as f: - nb = json.load(f) - assert nb["nbformat"] == 4 - assert "cells" in nb - - @pytest.mark.asyncio - async def test_edit_action_replace(self, jupyter_tool, sample_notebook): - """Test editing a cell (replace mode).""" - ctx = MCPContext() - - result = await jupyter_tool.call( - ctx, - action="edit", - notebook_path=str(sample_notebook), - cell_index=0, - source="print('Modified!')", - edit_mode="replace", - ) - - assert "Successfully updated cell at index 0" in result - - # Verify the change - with open(sample_notebook) as f: - nb = json.load(f) - # nbformat stores source as list in JSON - source = nb["cells"][0]["source"] - if isinstance(source, list): - source = "".join(source) - assert source == "print('Modified!')" - - @pytest.mark.asyncio - async def test_edit_action_insert(self, jupyter_tool, sample_notebook): - """Test inserting a new cell.""" - ctx = MCPContext() - - result = await jupyter_tool.call( - ctx, - action="edit", - notebook_path=str(sample_notebook), - cell_index=1, - source="x = 42", - cell_type="code", - edit_mode="insert", - ) - - assert "Successfully inserted new cell at index 1" in result - - # Verify the notebook now has 3 cells - with open(sample_notebook) as f: - nb = json.load(f) - assert len(nb["cells"]) == 3 - # nbformat stores source as list in JSON - source = nb["cells"][1]["source"] - if isinstance(source, list): - source = "".join(source) - assert source == "x = 42" - assert nb["cells"][1]["cell_type"] == "code" - - @pytest.mark.asyncio - async def test_edit_action_delete(self, jupyter_tool, sample_notebook): - """Test deleting a cell.""" - ctx = MCPContext() - - # First check we have 2 cells - with open(sample_notebook) as f: - nb = json.load(f) - assert len(nb["cells"]) == 2 - - result = await jupyter_tool.call( - ctx, - action="edit", - notebook_path=str(sample_notebook), - cell_index=1, - edit_mode="delete", - ) - - assert "Successfully deleted cell at index 1" in result - - # Verify we now have 1 cell - with open(sample_notebook) as f: - nb = json.load(f) - assert len(nb["cells"]) == 1 - - @pytest.mark.asyncio - async def test_delete_action_notebook(self, jupyter_tool, sample_notebook): - """Test deleting entire notebook.""" - ctx = MCPContext() - - result = await jupyter_tool.call( - ctx, action="delete", notebook_path=str(sample_notebook) - ) - - assert "Successfully deleted notebook" in result - assert not sample_notebook.exists() - - @pytest.mark.asyncio - async def test_error_handling(self, jupyter_tool, tmp_path): - """Test error handling.""" - ctx = MCPContext() - - # Non-existent file - result = await jupyter_tool.call( - ctx, action="read", notebook_path=str(tmp_path / "nonexistent.ipynb") - ) - assert "Error:" in result and "does not exist" in result - - # Invalid action - result = await jupyter_tool.call( - ctx, action="invalid", notebook_path=str(tmp_path / "test.ipynb") - ) - assert "Error: Unknown action" in result - - # Missing required params for edit (need to test insert mode) - # First create a notebook to edit - test_nb = tmp_path / "test_edit.ipynb" - await jupyter_tool.call(ctx, action="create", notebook_path=str(test_nb)) - - # Try to insert without source - result = await jupyter_tool.call( - ctx, - action="edit", - notebook_path=str(test_nb), - cell_index=0, - edit_mode="insert", - cell_type="code", # Required for insert - ) - assert "Error: source is required" in result diff --git a/pkg/hanzo-mcp/tests/test_llm_warnings.py b/pkg/hanzo-mcp/tests/test_llm_warnings.py deleted file mode 100644 index 93cb39699..000000000 --- a/pkg/hanzo-mcp/tests/test_llm_warnings.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python -"""Test that llm deprecation warnings are properly suppressed.""" - -import subprocess -import sys - - -def test_no_pydantic_warnings(): - """Test that running uvx hanzo-mcp doesn't show Pydantic deprecation warnings.""" - # Run the command and capture stderr - result = subprocess.run( - [sys.executable, "-m", "hanzo_mcp.cli", "--help"], - capture_output=True, - text=True, - ) - - # Check for deprecation warnings in stderr - assert "PydanticDeprecatedSince20" not in result.stderr, ( - f"Pydantic deprecation warning found in stderr: {result.stderr}" - ) - - # Check that the command succeeded - assert result.returncode == 0, ( - f"Command failed with return code {result.returncode}" - ) - - # Check that help text is shown - assert "MCP server implementing Hanzo AI capabilities" in result.stdout - - -def test_agent_tool_no_warnings(): - """Test that importing agent tools doesn't produce Pydantic deprecation warnings. - - We specifically check for PydanticDeprecatedSince20 warnings from llm, - not all deprecation warnings (which may come from other packages in CI). - """ - import warnings - - # Capture warnings during import - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - - # Import agent tools (which imports llm) - # This import happens in the test process directly - from hanzo_tools.agent import register_tools # noqa: F401 - - # Check specifically for Pydantic deprecation warnings - # Other packages may produce warnings that we don't control - pydantic_warnings = [ - warning - for warning in w - if "pydantic" in str(warning.message).lower() - or "PydanticDeprecatedSince20" in str(warning.category.__name__) - ] - - # We don't fail on pydantic warnings since they come from upstream llm - # and we've already configured the warnings filter to suppress them - # This test just documents that we're aware of them - if pydantic_warnings: - # Log but don't fail - these are upstream issues - for warning in pydantic_warnings: - print(f" Note: {warning.category.__name__}: {warning.message}") - - # The test passes as long as we can import without errors - assert True, "Agent tools imported successfully" - - -if __name__ == "__main__": - test_no_pydantic_warnings() - test_agent_tool_no_warnings() - print("โœ… All llm warning tests passed!") diff --git a/pkg/hanzo-mcp/tests/test_lsp_tool.py b/pkg/hanzo-mcp/tests/test_lsp_tool.py deleted file mode 100644 index a4c990ad9..000000000 --- a/pkg/hanzo-mcp/tests/test_lsp_tool.py +++ /dev/null @@ -1,202 +0,0 @@ -"""Test LSP tool functionality.""" - -import asyncio -import tempfile -from pathlib import Path -from unittest.mock import AsyncMock, patch - -import pytest -from hanzo_tools.lsp import create_lsp_tool - - -@pytest.mark.asyncio -async def test_lsp_tool_status(): - """Test LSP tool status check with mocking.""" - lsp_tool = create_lsp_tool() - - # Create a test Go file - with tempfile.NamedTemporaryFile(suffix=".go", mode="w", delete=False) as f: - f.write("""package main - -import "fmt" - -func main() { - fmt.Println("Hello, World!") -} - -func greet(name string) string { - return fmt.Sprintf("Hello, %s!", name) -} -""") - go_file = f.name - - try: - # Mock the subprocess calls to prevent hanging - with patch("asyncio.create_subprocess_exec") as mock_subprocess: - # Mock successful check for gopls - mock_process = AsyncMock() - mock_process.returncode = 0 - mock_process.communicate = AsyncMock(return_value=(b"", b"")) - mock_subprocess.return_value = mock_process - - # Check status for Go - result = await lsp_tool.run(action="status", file=go_file) - - assert result.data is not None - assert "language" in result.data - assert result.data["language"] == "go" - assert "lsp_server" in result.data - assert result.data["lsp_server"] == "gopls" - assert "capabilities" in result.data - assert "definition" in result.data["capabilities"] - - finally: - Path(go_file).unlink() - - -@pytest.mark.asyncio -async def test_lsp_tool_unsupported_file(): - """Test LSP tool with unsupported file type.""" - lsp_tool = create_lsp_tool() - - # Test with unsupported file - result = await lsp_tool.run(action="status", file="test.unknown") - - assert result.data is not None - assert "error" in result.data - assert "Unsupported file type" in result.data["error"] - assert "supported_languages" in result.data - - -@pytest.mark.asyncio -async def test_lsp_tool_invalid_action(): - """Test LSP tool with invalid action.""" - lsp_tool = create_lsp_tool() - - # Test with invalid action - result = await lsp_tool.run(action="invalid_action", file="test.py") - - assert result.data is not None - assert "error" in result.data - assert "Invalid action" in result.data["error"] - - -@pytest.mark.asyncio -async def test_lsp_tool_definition_placeholder(): - """Test LSP tool definition action (placeholder) with mocking.""" - lsp_tool = create_lsp_tool() - - with tempfile.NamedTemporaryFile(suffix=".py", mode="w", delete=False) as f: - f.write("""def hello(): - return "Hello" - -result = hello() -""") - py_file = f.name - - try: - # Mock the subprocess calls - with patch("asyncio.create_subprocess_exec") as mock_subprocess: - # Mock successful check for pylsp - mock_process = AsyncMock() - mock_process.returncode = 0 - mock_process.communicate = AsyncMock(return_value=(b"", b"")) - mock_subprocess.return_value = mock_process - - # Test definition lookup - result = await lsp_tool.run( - action="definition", - file=py_file, - line=4, - character=9, # Position of 'hello' in 'hello()' - ) - - assert result.data is not None - assert "action" in result.data - assert result.data["action"] == "definition" - assert "note" in result.data - assert "fallback" in result.data - - finally: - Path(py_file).unlink() - - -@pytest.mark.asyncio -async def test_lsp_tool_python_status(): - """Test LSP status for Python files with mocking.""" - lsp_tool = create_lsp_tool() - - # Mock the subprocess calls - with patch("asyncio.create_subprocess_exec") as mock_subprocess: - # Mock successful check for pylsp - mock_process = AsyncMock() - mock_process.returncode = 0 - mock_process.communicate = AsyncMock(return_value=(b"", b"")) - mock_subprocess.return_value = mock_process - - # Check Python LSP status - result = await lsp_tool.run(action="status", file="test.py") - - assert result.data is not None - assert result.data["language"] == "python" - assert result.data["lsp_server"] == "pylsp" - assert "definition" in result.data["capabilities"] - assert "hover" in result.data["capabilities"] - - -@pytest.mark.asyncio -async def test_lsp_tool_typescript_status(): - """Test LSP status for TypeScript files with mocking.""" - lsp_tool = create_lsp_tool() - - # Mock the subprocess calls - with patch("asyncio.create_subprocess_exec") as mock_subprocess: - # Mock successful check for typescript-language-server - mock_process = AsyncMock() - mock_process.returncode = 0 - mock_process.communicate = AsyncMock(return_value=(b"", b"")) - mock_subprocess.return_value = mock_process - - # Check TypeScript LSP status - result = await lsp_tool.run(action="status", file="app.ts") - - assert result.data is not None - assert result.data["language"] == "typescript" - assert result.data["lsp_server"] == "typescript-language-server" - assert "completion" in result.data["capabilities"] - - -@pytest.mark.asyncio -async def test_lsp_tool_rename_placeholder(): - """Test LSP rename action (placeholder) with mocking.""" - lsp_tool = create_lsp_tool() - - # Mock the subprocess calls - with patch("asyncio.create_subprocess_exec") as mock_subprocess: - # Mock successful check - mock_process = AsyncMock() - mock_process.returncode = 0 - mock_process.communicate = AsyncMock(return_value=(b"", b"")) - mock_subprocess.return_value = mock_process - - # Test rename - result = await lsp_tool.run( - action="rename", - file="test.go", - line=10, - character=5, - new_name="newFunctionName", - ) - - assert result.data is not None - assert result.data["action"] == "rename" - assert result.data["new_name"] == "newFunctionName" - assert "fallback" in result.data - - -if __name__ == "__main__": - # Run basic tests - asyncio.run(test_lsp_tool_status()) - asyncio.run(test_lsp_tool_unsupported_file()) - asyncio.run(test_lsp_tool_invalid_action()) - print("LSP tool tests passed!") diff --git a/pkg/hanzo-mcp/tests/test_manual.py b/pkg/hanzo-mcp/tests/test_manual.py deleted file mode 100644 index f5c1583a6..000000000 --- a/pkg/hanzo-mcp/tests/test_manual.py +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env python3 -"""Manual test script for Hanzo AI functionality.""" - -import asyncio -import os -import tempfile -from pathlib import Path - -from hanzo_tools.filesystem.diff import create_diff_tool -from hanzo_tools.filesystem.read import ReadTool - -# from hanzo_mcp.tools.common.palette import PaletteRegistry # Module doesn't exist -from hanzo_tools.shell.bash_tool import bash_tool - -from hanzo_mcp.server import HanzoMCPServer -from hanzo_mcp.tools.common.permissions import PermissionManager - - -async def test_basic_functionality(): - """Test basic MCP functionality.""" - print("๐Ÿงช Testing Hanzo AI Basic Functionality\n") - - # Create a temporary directory for testing - with tempfile.TemporaryDirectory() as temp_dir: - temp_path = Path(temp_dir) - print(f"๐Ÿ“ Using temp directory: {temp_path}\n") - - # Test 1: Permission Manager - print("1๏ธโƒฃ Testing Permission Manager...") - pm = PermissionManager() - pm.add_allowed_path(temp_dir) - print("โœ… Permission manager created\n") - - # Test 2: Palette System (Skipped - module not found) - print("2๏ธโƒฃ Testing Palette System...") - print("โš ๏ธ Skipping palette test - module not available") - print() - - # Test 3: Shell Tool (works with your zsh) - print("3๏ธโƒฃ Testing Shell Tool...") - try: - # Create mock context - class MockContext: - pass - - ctx = MockContext() - - # Test shell detection - interpreter = bash_tool.get_interpreter() - tool_name = bash_tool.get_tool_name() - print(f"โœ… Shell detected: {interpreter} (tool: {tool_name})") - - # Test simple command - result = await bash_tool.execute_sync( - "echo 'Hello from Hanzo AI!'", timeout=5 - ) - print(f"โœ… Command result: {result.strip()}") - except Exception as e: - print(f"โŒ Shell test failed: {e}") - print() - - # Test 4: File Operations - print("4๏ธโƒฃ Testing File Operations...") - try: - # Create test files - file1 = temp_path / "test1.txt" - file2 = temp_path / "test2.txt" - - file1.write_text("Hello\nWorld\nFrom\nHanzo\n") - file2.write_text("Hello\nUniverse\nFrom\nHanzo\nMCP\n") - - # Test read tool - read_tool = ReadTool(pm) - content = await read_tool.run(ctx, str(file1)) - print(f"โœ… Read tool works - got {len(content.split())} words") - - # Test diff tool - diff_tool = create_diff_tool(pm) - diff_result = await diff_tool.run(ctx, str(file1), str(file2)) - print("โœ… Diff tool works - found differences:") - diff_lines = diff_result.split("\n") - for line in diff_lines[-3:]: # Show last 3 lines (summary) - if line.strip(): - print(f" {line}") - - except Exception as e: - print(f"โŒ File operations test failed: {e}") - print() - - # Test 5: Server Creation - print("5๏ธโƒฃ Testing Server Creation...") - try: - HanzoMCPServer( - name="test-server", - allowed_paths=[temp_dir], - use_palette=True, - force_palette="python", - ) - print("โœ… Server created successfully") - print("โœ… Python palette applied") - except Exception as e: - print(f"โŒ Server creation failed: {e}") - print() - - -def test_cloudflare_tools(): - """Test Cloudflare tools configuration.""" - print("โ˜๏ธ Testing Cloudflare Configuration\n") - - # Test environment variables - cf_token = os.environ.get("CLOUDFLARE_API_TOKEN") - cf_account = os.environ.get("CLOUDFLARE_ACCOUNT_ID") - - if cf_token: - print(f"โœ… Cloudflare API token configured (ends with: ...{cf_token[-8:]})") - else: - print("โš ๏ธ No Cloudflare API token found in environment") - - if cf_account: - print(f"โœ… Cloudflare Account ID: {cf_account}") - else: - print("โš ๏ธ No Cloudflare Account ID found in environment") - - print() - - # Check if tools exist - tools_dir = Path("tools/hanzoai-mcp-server-cloudflare") - if tools_dir.exists(): - print("โœ… Cloudflare MCP server repository found") - tunnels_dir = tools_dir / "apps" / "cloudflare-tunnels" - if tunnels_dir.exists(): - print("โœ… Cloudflare Tunnels app found") - else: - print("โŒ Cloudflare Tunnels app not found") - else: - print("โŒ Cloudflare MCP server repository not found") - print() - - -def test_dev_mode(): - """Test development mode setup.""" - print("๐Ÿ”ง Testing Development Mode\n") - - try: - from hanzo_mcp.dev_server import DevServer - - DevServer( - name="test-dev", - allowed_paths=["/tmp"], - ) - print("โœ… DevServer created successfully") - print("โœ… Hot reload functionality available") - - # Test watchdog import - import watchdog - - print(f"โœ… Watchdog version: {watchdog.__version__}") - - except ImportError as e: - print(f"โŒ Development mode import failed: {e}") - except Exception as e: - print(f"โŒ Development mode test failed: {e}") - print() - - -def main(): - """Run all manual tests.""" - print("๐Ÿš€ Hanzo AI Manual Test Suite") - print("=" * 50) - print() - - # Run async tests - asyncio.run(test_basic_functionality()) - - # Run sync tests - test_cloudflare_tools() - test_dev_mode() - - print("๐ŸŽ‰ Manual test suite completed!") - print("\nNext steps:") - print("1. Restart Claude Desktop to load new MCP servers") - print("2. Test Cloudflare authentication in Claude") - print("3. Try palette commands: 'palette --action list'") - print("4. Test shell integration with your zsh config") - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-mcp/tests/test_memory_base.py b/pkg/hanzo-mcp/tests/test_memory_base.py deleted file mode 100644 index ab39b8253..000000000 --- a/pkg/hanzo-mcp/tests/test_memory_base.py +++ /dev/null @@ -1,135 +0,0 @@ -"""Base test class for memory-related tests to reduce redundancy.""" - -from datetime import datetime -from unittest.mock import Mock, patch - -import pytest -from conftest import ToolTestHelper, create_mock_ctx -from fastmcp import FastMCP - -# Import guard for optional hanzo_memory dependency -try: - from hanzo_memory.models import Memory - - HANZO_MEMORY_AVAILABLE = True -except ImportError: - HANZO_MEMORY_AVAILABLE = False - Memory = None # type: ignore - -# Skip entire module if hanzo_memory is not available -pytestmark = pytest.mark.skipif( - not HANZO_MEMORY_AVAILABLE, reason="hanzo_memory package not installed" -) - -# Only import these if hanzo_memory is available -if HANZO_MEMORY_AVAILABLE: - from hanzo_tools.memory import ( - CreateMemoriesTool, - DeleteMemoriesTool, - RecallMemoriesTool, - UpdateMemoriesTool, - ) - - -class MemoryTestBase: - """Base class for memory test cases with common fixtures and utilities.""" - - @pytest.fixture - def tool_helper(self): - """Provide ToolTestHelper for tests.""" - return ToolTestHelper - - @pytest.fixture - def mock_ctx(self): - """Create mock context for tool calls.""" - return create_mock_ctx() - - @pytest.fixture - def mock_memory(self): - """Create a standard mock memory object.""" - return Memory( - memory_id="test_123", - user_id="test_user", - project_id="test_project", - content="Test memory content", - metadata={"type": "statement"}, - importance=1.0, - created_at=datetime.fromisoformat("2024-01-01T00:00:00"), - updated_at=datetime.fromisoformat("2024-01-01T00:00:00"), - embedding=[0.1] * 1536, - ) - - @pytest.fixture - def mock_memory_service(self): - """Create a mock memory service with standard responses.""" - with patch( - "hanzo_memory.services.memory.get_memory_service" - ) as mock_get_service: - mock_service = Mock() - - # Set up standard responses - mock_service.create_memory.return_value = Mock( - memory_id="mem_123", - user_id="test_user", - project_id="test_project", - content="Created memory", - metadata={}, - importance=1.0, - ) - - mock_service.update_memory.return_value = Mock( - memory_id="mem_123", content="Updated memory" - ) - - mock_service.delete_memory.return_value = None - - mock_service.search_memories.return_value = [ - Mock( - memory_id="mem_123", - content="Found memory", - importance=0.9, - metadata={}, - ) - ] - - mock_get_service.return_value = mock_service - yield mock_service - - @pytest.fixture - def mcp_server(self): - """Create a FastMCP server instance.""" - return FastMCP("test-server") - - @pytest.fixture - def permission_manager(self): - """Create a permission manager with /tmp allowed.""" - from hanzo_mcp.security.permissions import PermissionManager - - pm = PermissionManager() - pm.add_allowed_path("/tmp") - return pm - - def assert_tool_registration(self, tools, expected_count=9): - """Assert that the expected number of memory tools are registered.""" - assert len(tools) == expected_count - tool_types = {type(tool) for tool in tools} - expected_types = { - CreateMemoriesTool, - UpdateMemoriesTool, - DeleteMemoriesTool, - RecallMemoriesTool, - } - # Check that at least the core tools are present - assert expected_types.issubset(tool_types) - - def create_memory_tool_params(self, **overrides): - """Create standard parameters for memory tool calls.""" - params = { - "user_id": "test_user", - "project_id": "test_project", - "content": "Test content", - "metadata": {"type": "test"}, - "importance": 1.0, - } - params.update(overrides) - return params diff --git a/pkg/hanzo-mcp/tests/test_memory_basic.py b/pkg/hanzo-mcp/tests/test_memory_basic.py deleted file mode 100644 index 42997a852..000000000 --- a/pkg/hanzo-mcp/tests/test_memory_basic.py +++ /dev/null @@ -1,71 +0,0 @@ -"""Basic memory test to debug issues.""" - -import asyncio -from datetime import datetime -from unittest.mock import AsyncMock, Mock, patch - - -def test_basic_memory(): - """Basic test without pytest complexity.""" - with patch("hanzo_memory.services.memory.get_memory_service") as mock_get_service: - with patch( - "hanzo_tools.memory.memory_tools.create_tool_context" - ) as mock_create_tool_context: - # Mock the tool context - mock_tool_ctx = Mock() - mock_tool_ctx.set_tool_info = AsyncMock() - mock_tool_ctx.info = AsyncMock() - mock_tool_ctx.send_completion_ping = AsyncMock() - mock_create_tool_context.return_value = mock_tool_ctx - - # Mock the memory service - mock_service = Mock() - mock_get_service.return_value = mock_service - - # Mock memory creation - from hanzo_memory.models.memory import Memory - - mock_memory = Memory( - memory_id="test_123", - user_id="test_user", - project_id="test_project", - content="Test memory content", - metadata={"type": "statement"}, - importance=1.0, - created_at=datetime.fromisoformat("2024-01-01T00:00:00"), - updated_at=datetime.fromisoformat("2024-01-01T00:00:00"), - embedding=[0.1] * 1536, - ) - mock_service.create_memory.return_value = mock_memory - - # Create and test the tool - from hanzo_tools.memory.memory_tools import CreateMemoriesTool - - tool = CreateMemoriesTool(user_id="test_user", project_id="test_project") - - # Mock context - mock_ctx = Mock() - mock_ctx.request_id = "test-request-id" - - # Call the tool - result = asyncio.run( - tool.call( - mock_ctx, - statements=["This is a test memory", "This is another test"], - ) - ) - - print(f"Result: {result}") - print( - f"create_memory called: {mock_service.create_memory.call_count} times" - ) - - # Check result - assert "Successfully created 2 new memories" in str(result) - assert mock_service.create_memory.call_count == 2 - - print("Test passed!") - - -if __name__ == "__main__": - test_basic_memory() diff --git a/pkg/hanzo-mcp/tests/test_memory_consolidated.py b/pkg/hanzo-mcp/tests/test_memory_consolidated.py deleted file mode 100644 index 066f7791d..000000000 --- a/pkg/hanzo-mcp/tests/test_memory_consolidated.py +++ /dev/null @@ -1,233 +0,0 @@ -"""Consolidated memory tests using parametrization to reduce redundancy.""" - -import pytest - -# Import guard for optional hanzo_memory dependency -try: - from hanzo_memory.models import Memory - - HANZO_MEMORY_AVAILABLE = True -except ImportError: - HANZO_MEMORY_AVAILABLE = False - -# Skip entire module if hanzo_memory is not available -pytestmark = pytest.mark.skipif( - not HANZO_MEMORY_AVAILABLE, reason="hanzo_memory package not installed" -) - -# Only import these if hanzo_memory is available -if HANZO_MEMORY_AVAILABLE: - from hanzo_tools.memory import ( - CreateMemoriesTool, - DeleteMemoriesTool, - RecallMemoriesTool, - UpdateMemoriesTool, - register_memory_tools, - ) - from test_memory_base import MemoryTestBase -else: - # Dummy class to prevent NameError when module is skipped - class MemoryTestBase: - pass - - -class TestMemoryToolsConsolidated(MemoryTestBase): - """Consolidated memory tool tests using parametrization.""" - - @pytest.mark.parametrize( - "tool_class,method_name,params,expected_result", - ( - [ - ( - CreateMemoriesTool, - "create_memory", - { - "content": "Test memory content", - "metadata": {"type": "test"}, - "importance": 1.0, - }, - "Successfully created memory mem_123", - ), - ( - UpdateMemoriesTool, - "update_memory", - { - "memory_id": "mem_123", - "content": "Updated content", - "metadata": {"type": "updated"}, - }, - "Would update memory mem_123", - ), - ( - DeleteMemoriesTool, - "delete_memory", - {"memory_id": "mem_123"}, - "Successfully deleted memory mem_123", - ), - ( - RecallMemoriesTool, - "search_memories", - {"query": "test query", "limit": 5}, - "Found 1 relevant memories", - ), - ] - if HANZO_MEMORY_AVAILABLE - else [] - ), - ) - async def test_memory_operations( - self, - tool_class, - method_name, - params, - expected_result, - mock_memory_service, - mock_ctx, - tool_helper, - ): - """Test various memory operations with parametrization.""" - # Create tool instance - tool = tool_class(user_id="test_user", project_id="test_project") - - # Execute tool - result = await tool_helper.run_tool(tool, params, mock_ctx) - - # Verify service method was called - service_method = getattr(mock_memory_service, method_name) - assert service_method.called - - # Verify result contains expected message - assert expected_result in str(result) - - @pytest.mark.parametrize( - "error_type,error_message,params", - [ - ( - ValueError, - "Memory not found", - {"memory_id": "nonexistent", "content": "update"}, - ), - ( - ConnectionError, - "Database connection failed", - {"content": "test", "metadata": {}}, - ), - ( - PermissionError, - "Insufficient permissions", - {"memory_id": "protected", "content": "hack"}, - ), - ], - ) - async def test_memory_error_handling( - self, - error_type, - error_message, - params, - mock_memory_service, - mock_ctx, - tool_helper, - ): - """Test error handling for memory operations.""" - # Configure service to raise error - mock_memory_service.update_memory.side_effect = error_type(error_message) - mock_memory_service.create_memory.side_effect = error_type(error_message) - - # Create tool (use UpdateMemoriesTool as example) - tool = UpdateMemoriesTool(user_id="test_user", project_id="test_project") - - # Execute and expect error - with pytest.raises(error_type, match=error_message): - await tool_helper.run_tool(tool, params, mock_ctx) - - def test_memory_tools_registration(self, mcp_server, permission_manager): - """Test that all memory tools are properly registered.""" - tools = register_memory_tools( - mcp_server, - permission_manager, - user_id="test_user", - project_id="test_project", - ) - - # Verify tools are registered - self.assert_tool_registration(tools) - - # Verify each tool has correct configuration - for tool in tools: - if isinstance( - tool, - ( - CreateMemoriesTool, - UpdateMemoriesTool, - DeleteMemoriesTool, - RecallMemoriesTool, - ), - ): - assert tool.user_id == "test_user" - assert tool.project_id == "test_project" - - @pytest.mark.parametrize( - "batch_size,expected_calls", - [ - (1, 1), - (5, 5), - (10, 10), - ], - ) - async def test_batch_memory_operations( - self, - batch_size, - expected_calls, - mock_memory_service, - mock_ctx, - tool_helper, - ): - """Test batch memory operations with different sizes.""" - tool = CreateMemoriesTool(user_id="test_user", project_id="test_project") - - # Execute multiple operations - for i in range(batch_size): - params = { - "content": f"Batch memory {i}", - "metadata": {"index": i}, - "importance": 0.5 + (i * 0.1), - } - await tool_helper.run_tool(tool, params, mock_ctx) - - # Verify correct number of service calls - assert mock_memory_service.create_memory.call_count == expected_calls - - @pytest.mark.parametrize( - "memory_type,metadata,importance", - [ - ("statement", {"type": "statement", "source": "user"}, 1.0), - ("question", {"type": "question", "context": "test"}, 0.5), - ("fact", {"type": "fact", "verified": True}, 0.9), - ("emotion", {"type": "emotion", "sentiment": "positive"}, 0.7), - ], - ) - async def test_memory_types( - self, - memory_type, - metadata, - importance, - mock_memory_service, - mock_ctx, - tool_helper, - ): - """Test different memory types and metadata.""" - tool = CreateMemoriesTool(user_id="test_user", project_id="test_project") - - params = { - "content": f"Test {memory_type} memory", - "metadata": metadata, - "importance": importance, - } - - await tool_helper.run_tool(tool, params, mock_ctx) - - # Verify service was called with correct parameters - mock_memory_service.create_memory.assert_called_once() - call_args = mock_memory_service.create_memory.call_args[1] - assert call_args["metadata"] == metadata - assert call_args["importance"] == importance diff --git a/pkg/hanzo-mcp/tests/test_memory_namespace.py b/pkg/hanzo-mcp/tests/test_memory_namespace.py deleted file mode 100644 index 6bb3aac2d..000000000 --- a/pkg/hanzo-mcp/tests/test_memory_namespace.py +++ /dev/null @@ -1,396 +0,0 @@ -"""Tests for memory service namespace, key-based retrieval, tags, ttl, and coordination.""" - -import asyncio -import json -import time -from datetime import datetime, timedelta, timezone - -import pytest - - -@pytest.fixture -async def memory_service(): - """Create a PluginMemoryService with in-memory SQLite for testing.""" - from hanzo_mcp.backends.sqlite_plugin import SQLiteBackendPlugin - from hanzo_mcp.memory_service import PluginMemoryService - - svc = PluginMemoryService() - # Use true in-memory db โ€” each test gets a fresh database - plugin = SQLiteBackendPlugin(db_path=SQLiteBackendPlugin.IN_MEMORY) - svc.registry._plugins = {"sqlite": plugin} - svc.registry._active_plugins = ["sqlite"] - await svc.registry.initialize_all_active() - svc._initialized = True - yield svc - await svc.shutdown() - - -class TestNamespaceSupport: - """Test namespace parameter on memory operations.""" - - @pytest.mark.asyncio - async def test_store_with_namespace(self, memory_service): - mid = await memory_service.store_memory( - content="blue agent report", - metadata={"type": "report"}, - namespace="blue-red", - ) - assert mid - - @pytest.mark.asyncio - async def test_store_default_namespace(self, memory_service): - mid = await memory_service.store_memory( - content="plain memory", - metadata={}, - ) - assert mid - - @pytest.mark.asyncio - async def test_list_memories_filters_by_namespace(self, memory_service): - await memory_service.store_memory(content="ns1", metadata={}, namespace="ns1") - await memory_service.store_memory(content="ns2", metadata={}, namespace="ns2") - await memory_service.store_memory(content="ns1-b", metadata={}, namespace="ns1") - - results = await memory_service.list_memories(namespace="ns1") - assert len(results) == 2 - for r in results: - assert r["metadata"].get("namespace") == "ns1" - - @pytest.mark.asyncio - async def test_list_memories_no_filter(self, memory_service): - await memory_service.store_memory(content="a", metadata={}, namespace="x") - await memory_service.store_memory(content="b", metadata={}, namespace="y") - results = await memory_service.list_memories() - assert len(results) == 2 - - @pytest.mark.asyncio - async def test_namespaces_returns_counts(self, memory_service): - await memory_service.store_memory(content="a", metadata={}, namespace="alpha") - await memory_service.store_memory(content="b", metadata={}, namespace="alpha") - await memory_service.store_memory(content="c", metadata={}, namespace="beta") - - ns = await memory_service.namespaces() - assert ns["alpha"] == 2 - assert ns["beta"] == 1 - - @pytest.mark.asyncio - async def test_clear_namespace(self, memory_service): - await memory_service.store_memory(content="a", metadata={}, namespace="temp") - await memory_service.store_memory(content="b", metadata={}, namespace="keep") - - deleted = await memory_service.clear(namespace="temp") - assert deleted >= 1 - - results = await memory_service.list_memories() - assert len(results) == 1 - assert results[0]["metadata"].get("namespace") == "keep" - - @pytest.mark.asyncio - async def test_clear_all(self, memory_service): - await memory_service.store_memory(content="a", metadata={}, namespace="x") - await memory_service.store_memory(content="b", metadata={}, namespace="y") - - deleted = await memory_service.clear() - assert deleted >= 2 - - results = await memory_service.list_memories() - assert len(results) == 0 - - -class TestKeyBasedRetrieval: - """Test key-based store and get_by_key.""" - - @pytest.mark.asyncio - async def test_store_with_key(self, memory_service): - mid = await memory_service.store_memory( - content="keyed content", - metadata={"extra": "data"}, - key="my-key-1", - namespace="test", - ) - assert mid - - @pytest.mark.asyncio - async def test_get_by_key_exact(self, memory_service): - await memory_service.store_memory( - content="target", metadata={}, key="exact-key", namespace="ns" - ) - await memory_service.store_memory( - content="other", metadata={}, key="other-key", namespace="ns" - ) - - result = await memory_service.get_by_key(key="exact-key", namespace="ns") - assert result is not None - assert result["content"] == "target" - - @pytest.mark.asyncio - async def test_get_by_key_wildcard(self, memory_service): - await memory_service.store_memory( - content="report-1", metadata={}, key="blue-report-100", namespace="blue-red" - ) - await memory_service.store_memory( - content="report-2", metadata={}, key="blue-report-200", namespace="blue-red" - ) - await memory_service.store_memory( - content="other", metadata={}, key="red-report-100", namespace="blue-red" - ) - - results = await memory_service.get_by_key( - key="blue-report-*", namespace="blue-red" - ) - assert isinstance(results, list) - assert len(results) == 2 - - @pytest.mark.asyncio - async def test_get_by_key_not_found(self, memory_service): - result = await memory_service.get_by_key(key="nonexistent", namespace="ns") - assert result is None - - @pytest.mark.asyncio - async def test_append_mode(self, memory_service): - await memory_service.store_memory( - content="line1", metadata={}, key="append-key", namespace="test" - ) - await memory_service.store_memory( - content="\nline2", - metadata={}, - key="append-key", - namespace="test", - append=True, - ) - - result = await memory_service.get_by_key(key="append-key", namespace="test") - assert "line1" in result["content"] - assert "line2" in result["content"] - - -class TestTagsSupport: - """Test tags parameter on memory operations.""" - - @pytest.mark.asyncio - async def test_store_with_tags(self, memory_service): - mid = await memory_service.store_memory( - content="tagged memory", - metadata={}, - tags=["blue", "report"], - ) - assert mid - - @pytest.mark.asyncio - async def test_list_memories_filter_by_tag(self, memory_service): - await memory_service.store_memory(content="a", metadata={}, tags=["blue"]) - await memory_service.store_memory(content="b", metadata={}, tags=["red"]) - await memory_service.store_memory( - content="c", metadata={}, tags=["blue", "red"] - ) - - results = await memory_service.list_memories(tag="blue") - assert len(results) == 2 - - @pytest.mark.asyncio - async def test_tag_memory(self, memory_service): - mid = await memory_service.store_memory(content="taggable", metadata={}) - ok = await memory_service.tag_memory(mid, "new-tag") - assert ok - - results = await memory_service.list_memories(tag="new-tag") - assert len(results) == 1 - - @pytest.mark.asyncio - async def test_untag_memory(self, memory_service): - mid = await memory_service.store_memory( - content="untaggable", metadata={}, tags=["remove-me"] - ) - ok = await memory_service.untag_memory(mid, "remove-me") - assert ok - - results = await memory_service.list_memories(tag="remove-me") - assert len(results) == 0 - - -class TestTTLSupport: - """Test TTL (time-to-live) on memories.""" - - @pytest.mark.asyncio - async def test_store_with_ttl(self, memory_service): - # TTL in the past = already expired - past = (datetime.now(timezone.utc) - timedelta(hours=1)).isoformat() - mid = await memory_service.store_memory( - content="ephemeral", metadata={}, ttl=past - ) - assert mid - - # Should not appear in list (expired) - results = await memory_service.list_memories() - assert len(results) == 0 - - @pytest.mark.asyncio - async def test_store_with_future_ttl(self, memory_service): - future = (datetime.now(timezone.utc) + timedelta(hours=1)).isoformat() - mid = await memory_service.store_memory( - content="long-lived", metadata={}, ttl=future - ) - - results = await memory_service.list_memories() - assert len(results) == 1 - - -class TestStats: - """Test stats method.""" - - @pytest.mark.asyncio - async def test_stats_empty(self, memory_service): - s = await memory_service.stats() - assert s["count"] == 0 - assert s["namespaces"] == {} - - @pytest.mark.asyncio - async def test_stats_with_data(self, memory_service): - await memory_service.store_memory(content="a", metadata={}, namespace="ns1") - await memory_service.store_memory(content="b", metadata={}, namespace="ns1") - await memory_service.store_memory(content="c", metadata={}, namespace="ns2") - - s = await memory_service.stats() - assert s["count"] == 3 - assert s["namespaces"]["ns1"] == 2 - assert s["namespaces"]["ns2"] == 1 - - -class TestHistory: - """Test version history for a key.""" - - @pytest.mark.asyncio - async def test_history_shows_versions(self, memory_service): - await memory_service.store_memory( - content="v1", metadata={}, key="versioned", namespace="test" - ) - # Store again with same key but NOT append โ€” creates new entry - await memory_service.store_memory( - content="v2", metadata={}, key="versioned", namespace="test" - ) - - versions = await memory_service.history(key="versioned", namespace="test") - assert len(versions) == 2 - - -class TestExportImport: - """Test export and import.""" - - @pytest.mark.asyncio - async def test_export_import_round_trip(self, memory_service): - await memory_service.store_memory( - content="exportable", metadata={"x": 1}, namespace="exp" - ) - - data = await memory_service.export_memories(namespace="exp") - assert len(data) == 1 - assert data[0]["content"] == "exportable" - - # Clear and re-import - await memory_service.clear() - imported = await memory_service.import_memories(data) - assert imported == 1 - - results = await memory_service.list_memories(namespace="exp") - assert len(results) == 1 - - -class TestBackwardCompatibility: - """Verify existing callers still work.""" - - @pytest.mark.asyncio - async def test_store_memory_original_signature(self, memory_service): - mid = await memory_service.store_memory( - content="old style", - metadata={"type": "general"}, - user_id="u1", - project_id="p1", - ) - assert mid - - @pytest.mark.asyncio - async def test_retrieve_memory_original_signature(self, memory_service): - await memory_service.store_memory( - content="findme", metadata={}, user_id="u1", project_id="p1" - ) - results = await memory_service.retrieve_memory( - query="findme", user_id="u1", project_id="p1" - ) - assert isinstance(results, list) - - @pytest.mark.asyncio - async def test_delete_memory_original_signature(self, memory_service): - mid = await memory_service.store_memory( - content="deleteme", metadata={}, user_id="u1", project_id="p1" - ) - ok = await memory_service.delete_memory( - memory_id=mid, user_id="u1", project_id="p1" - ) - assert ok - - -class TestBlueRedChannel: - """Test the coordination convenience class.""" - - @pytest.mark.asyncio - async def test_blue_report(self, memory_service): - from hanzo_mcp.coordination import BlueRedChannel - - channel = BlueRedChannel(memory_service) - key = await channel.blue_report({"analysis": "code looks good"}) - assert key.startswith("blue-report-") - - @pytest.mark.asyncio - async def test_red_report(self, memory_service): - from hanzo_mcp.coordination import BlueRedChannel - - channel = BlueRedChannel(memory_service) - key = await channel.red_report({"issues": ["bug in auth"]}) - assert key.startswith("red-report-") - - @pytest.mark.asyncio - async def test_blue_red_cycle(self, memory_service): - from hanzo_mcp.coordination import BlueRedChannel - - channel = BlueRedChannel(memory_service) - - # Blue reports - await channel.blue_report({"analysis": "reviewed auth module"}, scope="auth") - - # Red reads and reports findings - blue_report = await channel.get_latest_blue_report() - assert blue_report is not None - assert "reviewed auth module" in blue_report["content"] - - await channel.red_report({"issues": ["SQL injection risk"]}, scope="auth") - - # Blue reads red findings - red_findings = await channel.get_latest_red_findings() - assert red_findings is not None - assert "SQL injection risk" in red_findings["content"] - - # Blue responds with fixes - await channel.blue_response({"fixes": ["added parameterized queries"]}) - - # Red re-reviews - await channel.red_rereview({"verdict": "resolved"}) - - # Get full cycle - cycle = await channel.get_full_cycle() - assert len(cycle) == 4 - - @pytest.mark.asyncio - async def test_get_latest_blue_report_empty(self, memory_service): - from hanzo_mcp.coordination import BlueRedChannel - - channel = BlueRedChannel(memory_service) - result = await channel.get_latest_blue_report() - assert result is None - - @pytest.mark.asyncio - async def test_get_latest_red_findings_empty(self, memory_service): - from hanzo_mcp.coordination import BlueRedChannel - - channel = BlueRedChannel(memory_service) - result = await channel.get_latest_red_findings() - assert result is None diff --git a/pkg/hanzo-mcp/tests/test_memory_simple.py b/pkg/hanzo-mcp/tests/test_memory_simple.py deleted file mode 100644 index 4d66b449d..000000000 --- a/pkg/hanzo-mcp/tests/test_memory_simple.py +++ /dev/null @@ -1,46 +0,0 @@ -"""Simple memory test to verify functionality.""" - -import pytest - - -def test_memory_registration(): - """Test that memory tools can be registered.""" - from mcp.server.fastmcp import FastMCP - - from hanzo_mcp.tools.common.permissions import PermissionManager - - # Skip if memory not available - try: - from hanzo_tools.memory import register_memory_tools - except ImportError: - pytest.skip("hanzo-memory not available") - - mcp_server = FastMCP("test-server") - permission_manager = PermissionManager() - permission_manager.add_allowed_path("/tmp") - - tools = register_memory_tools( - mcp_server, permission_manager, user_id="test_user", project_id="test_project" - ) - - assert len(tools) == 9 - print(f"Successfully registered {len(tools)} memory tools") - - -def test_memory_descriptions(): - """Test memory tool descriptions.""" - try: - from hanzo_tools.memory.memory_tools import CreateMemoriesTool - except ImportError: - pytest.skip("hanzo-memory not available") - - tool = CreateMemoriesTool() - assert "save" in tool.description.lower() - assert "memory" in tool.description.lower() - print(f"CreateMemoriesTool description OK: {tool.description[:50]}...") - - -if __name__ == "__main__": - test_memory_registration() - test_memory_descriptions() - print("\nAll simple tests passed!") diff --git a/pkg/hanzo-mcp/tests/test_memory_utils.py b/pkg/hanzo-mcp/tests/test_memory_utils.py deleted file mode 100644 index 52baf2cdf..000000000 --- a/pkg/hanzo-mcp/tests/test_memory_utils.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Test utilities specific to memory tests.""" - -from unittest.mock import AsyncMock, Mock, patch - -from hanzo_mcp.tools.common.context import ToolContext - - -def create_memory_mock_ctx(): - """Create a mock context that works with memory tools.""" - mock_ctx = Mock() - mock_ctx.request_id = "test-request-id" - mock_ctx.client_id = "test-client-id" - - # Create a mock that returns a proper tool context - mock_tool_ctx = Mock(spec=ToolContext) - mock_tool_ctx.set_tool_info = AsyncMock() - mock_tool_ctx.info = AsyncMock() - mock_tool_ctx.debug = AsyncMock() - mock_tool_ctx.warning = AsyncMock() - mock_tool_ctx.error = AsyncMock() - mock_tool_ctx.send_completion_ping = AsyncMock() - - # Patch create_tool_context to return our mock - with patch( - "hanzo_tools.memory.memory_tools.create_tool_context", - return_value=mock_tool_ctx, - ): - yield mock_ctx, mock_tool_ctx diff --git a/pkg/hanzo-mcp/tests/test_new_tools.py b/pkg/hanzo-mcp/tests/test_new_tools.py deleted file mode 100644 index cf4adf224..000000000 --- a/pkg/hanzo-mcp/tests/test_new_tools.py +++ /dev/null @@ -1,435 +0,0 @@ -"""Tests for new tools added to Hanzo AI.""" - -import os -import tempfile -from pathlib import Path -from unittest.mock import MagicMock, Mock, patch - -import pytest -from hanzo_mcp.tools.mcp.mcp_add import McpAddTool -from hanzo_mcp.tools.mcp.mcp_remove import McpRemoveTool -from hanzo_mcp.tools.mcp.mcp_stats import McpStatsTool -from hanzo_tools.database.database_manager import DatabaseManager -from hanzo_tools.database.graph_add import GraphAddTool -from hanzo_tools.database.graph_query import GraphQueryTool -from hanzo_tools.database.graph_remove import GraphRemoveTool -from hanzo_tools.database.graph_search import GraphSearchTool -from hanzo_tools.database.graph_stats import GraphStatsTool -from hanzo_tools.filesystem.find_files import FindFilesTool -from hanzo_tools.shell.npx import NpxTool -from hanzo_tools.shell.uvx import UvxTool -from hanzo_tools.shell.uvx_background import UvxBackgroundTool - -from hanzo_mcp.tools.common.permissions import PermissionManager -from hanzo_mcp.tools.common.stats import StatsTool -from hanzo_mcp.tools.common.tool_disable import ToolDisableTool -from hanzo_mcp.tools.common.tool_enable import ToolEnableTool - - -@pytest.fixture -def mock_ctx(): - """Create a mock MCP context.""" - ctx = Mock() - ctx.client = Mock() - ctx.client.notify_progress = Mock() - return ctx - - -@pytest.fixture -def permission_manager(): - """Create a permission manager.""" - pm = PermissionManager() - pm.add_allowed_path(os.getcwd()) - return pm - - -@pytest.fixture -def db_manager(permission_manager): - """Create a database manager.""" - return DatabaseManager(permission_manager) - - -class TestGraphTools: - """Test graph database tools.""" - - @pytest.mark.asyncio - async def test_graph_add_node( - self, tool_helper, mock_ctx, permission_manager, db_manager - ): - """Test adding a node to the graph.""" - tool = GraphAddTool(permission_manager, db_manager) - - result = await tool_helper.call_tool( - tool, - mock_ctx, - node_id="test_node", - node_type="file", - properties={"size": 1024}, - ) - - tool_helper.assert_in_result("Successfully added node 'test_node'", result) - - @pytest.mark.asyncio - async def test_graph_add_edge( - self, tool_helper, mock_ctx, permission_manager, db_manager - ): - """Test adding an edge to the graph.""" - tool = GraphAddTool(permission_manager, db_manager) - - # Add nodes first - await tool.call(mock_ctx, node_id="node1", node_type="file") - await tool.call(mock_ctx, node_id="node2", node_type="file") - - # Add edge - result = await tool.call( - mock_ctx, source="node1", target="node2", relationship="imports" - ) - if isinstance(result, dict) and "output" in result: - result = result["output"] - - tool_helper.assert_in_result("Successfully added edge", result) - - @pytest.mark.asyncio - async def test_graph_remove_node( - self, tool_helper, mock_ctx, permission_manager, db_manager - ): - """Test removing a node from the graph.""" - add_tool = GraphAddTool(permission_manager, db_manager) - remove_tool = GraphRemoveTool(permission_manager, db_manager) - - # Add a node - await add_tool.call(mock_ctx, node_id="test_node", node_type="file") - - # Remove it - result = await remove_tool.call(mock_ctx, node_id="test_node") - - tool_helper.assert_in_result("Successfully removed node 'test_node'", result) - - @pytest.mark.asyncio - async def test_graph_query_neighbors( - self, tool_helper, mock_ctx, permission_manager, db_manager - ): - """Test querying neighbors in the graph.""" - add_tool = GraphAddTool(permission_manager, db_manager) - query_tool = GraphQueryTool(permission_manager, db_manager) - - # Create a simple graph - await add_tool.call(mock_ctx, node_id="A", node_type="file") - await add_tool.call(mock_ctx, node_id="B", node_type="file") - await add_tool.call(mock_ctx, node_id="C", node_type="file") - await add_tool.call(mock_ctx, source="A", target="B", relationship="imports") - await add_tool.call(mock_ctx, source="A", target="C", relationship="imports") - - # Query neighbors - result = await query_tool.call(mock_ctx, query="neighbors", node_id="A") - - tool_helper.assert_in_result("Neighbors of 'A'", result) - tool_helper.assert_in_result("B", result) - tool_helper.assert_in_result("C", result) - - @pytest.mark.asyncio - async def test_graph_search( - self, tool_helper, mock_ctx, permission_manager, db_manager - ): - """Test searching in the graph.""" - add_tool = GraphAddTool(permission_manager, db_manager) - search_tool = GraphSearchTool(permission_manager, db_manager) - - # Add nodes with properties - await add_tool.call( - mock_ctx, - node_id="main.py", - node_type="file", - properties={"description": "Main entry point"}, - ) - - # Search - result = await search_tool.call(mock_ctx, pattern="main%") - - tool_helper.assert_in_result("Found", result) - tool_helper.assert_in_result("main.py", result) - - @pytest.mark.skip( - reason="Graph stats test needs database isolation - stale data causes false failures" - ) - @pytest.mark.asyncio - async def test_graph_stats( - self, tool_helper, mock_ctx, permission_manager, db_manager - ): - """Test graph statistics.""" - add_tool = GraphAddTool(permission_manager, db_manager) - stats_tool = GraphStatsTool(permission_manager, db_manager) - - # Add some data - await add_tool.call(mock_ctx, node_id="A", node_type="file") - await add_tool.call(mock_ctx, node_id="B", node_type="class") - await add_tool.call(mock_ctx, source="A", target="B", relationship="contains") - - # Get stats - result = await stats_tool.call(mock_ctx) - - tool_helper.assert_in_result("Total Nodes: 2", result) - tool_helper.assert_in_result("Total Edges: 1", result) - - -class TestFindFilesTool: - """Test find files tool.""" - - @pytest.mark.asyncio - async def test_find_files_basic(self, tool_helper, mock_ctx, permission_manager): - """Test basic file finding.""" - tool = FindFilesTool(permission_manager) - - with tempfile.TemporaryDirectory() as tmpdir: - # Create test files - Path(tmpdir, "test1.py").touch() - Path(tmpdir, "test2.py").touch() - Path(tmpdir, "data.txt").touch() - - # Allow access - permission_manager.add_allowed_path(tmpdir) - - # Find Python files - result = await tool.call(mock_ctx, pattern="*.py", path=tmpdir) - if isinstance(result, dict) and "output" in result: - result = result["output"] - - tool_helper.assert_in_result("Found 2 file(s)", result) - tool_helper.assert_in_result("test1.py", result) - tool_helper.assert_in_result("test2.py", result) - assert "data.txt" not in result - - @pytest.mark.asyncio - async def test_find_files_recursive( - self, tool_helper, mock_ctx, permission_manager - ): - """Test recursive file finding.""" - tool = FindFilesTool(permission_manager) - - with tempfile.TemporaryDirectory() as tmpdir: - # Create nested structure - subdir = Path(tmpdir, "subdir") - subdir.mkdir() - Path(tmpdir, "top.txt").touch() - Path(subdir, "nested.txt").touch() - - permission_manager.add_allowed_path(tmpdir) - - # Find all txt files - result = await tool.call( - mock_ctx, pattern="*.txt", path=tmpdir, recursive=True - ) - if isinstance(result, dict) and "output" in result: - result = result["output"] - - tool_helper.assert_in_result("Found 2 file(s)", result) - tool_helper.assert_in_result("top.txt", result) - tool_helper.assert_in_result("subdir/nested.txt", result) - - -class TestPackageRunnerTools: - """Test uvx and npx tools.""" - - @pytest.mark.asyncio - async def test_uvx_basic(self, tool_helper, mock_ctx, permission_manager): - """Test basic uvx execution.""" - tool = UvxTool(permission_manager) - - with patch("subprocess.run") as mock_run: - mock_run.return_value = MagicMock( - stdout="Package output", stderr="", returncode=0 - ) - - with patch("shutil.which", return_value="/usr/bin/uvx"): - result = await tool.call(mock_ctx, package="ruff", args="check .") - if isinstance(result, dict) and "output" in result: - result = result["output"] - - tool_helper.assert_in_result("Package output", result) - mock_run.assert_called_once() - - @pytest.mark.asyncio - async def test_npx_basic(self, tool_helper, mock_ctx, permission_manager): - """Test basic npx execution.""" - tool = NpxTool(permission_manager) - - with patch("subprocess.run") as mock_run: - mock_run.return_value = MagicMock( - stdout="Package output", stderr="", returncode=0 - ) - - with patch("shutil.which", return_value="/usr/bin/npx"): - result = await tool.call(mock_ctx, package="eslint", args="--version") - if isinstance(result, dict) and "output" in result: - result = result["output"] - - tool_helper.assert_in_result("Package output", result) - mock_run.assert_called_once() - - @pytest.mark.skip(reason="UvxBackgroundTool is deprecated - use UvxTool instead") - @pytest.mark.asyncio - async def test_uvx_background(self, tool_helper, mock_ctx, permission_manager): - """Test uvx background execution.""" - tool = UvxBackgroundTool(permission_manager) - - with patch("subprocess.Popen") as mock_popen: - mock_process = MagicMock() - mock_process.pid = 12345 - mock_popen.return_value = mock_process - - with patch("shutil.which", return_value="/usr/bin/uvx"): - result = await tool.call( - mock_ctx, package="streamlit", args="run app.py", name="test-app" - ) - if isinstance(result, dict) and "output" in result: - result = result["output"] - - tool_helper.assert_in_result("Started uvx background process", result) - tool_helper.assert_in_result("PID: 12345", result) - mock_popen.assert_called_once() - - -class TestMcpManagementTools: - """Test MCP management tools.""" - - @pytest.mark.skip( - reason="McpAddTool test modifies user's MCP config - run manually" - ) - @pytest.mark.asyncio - async def test_mcp_add(self, tool_helper, mock_ctx): - """Test adding an MCP server.""" - tool = McpAddTool() - - result = await tool.call( - mock_ctx, command="uvx mcp-server-git", name="git-server" - ) - if isinstance(result, dict) and "output" in result: - result = result["output"] - - tool_helper.assert_in_result( - "Successfully added MCP server 'git-server'", result - ) - - # Check it was saved - servers = McpAddTool.get_servers() - assert "git-server" in servers - - @pytest.mark.asyncio - async def test_mcp_remove(self, tool_helper, mock_ctx): - """Test removing an MCP server.""" - # First add a server - add_tool = McpAddTool() - await add_tool.call(mock_ctx, command="uvx test-server", name="test-server") - - # Then remove it - remove_tool = McpRemoveTool() - result = await remove_tool.call(mock_ctx, name="test-server") - - tool_helper.assert_in_result( - "Successfully removed MCP server 'test-server'", result - ) - - # Check it was removed - servers = McpAddTool.get_servers() - assert "test-server" not in servers - - @pytest.mark.asyncio - async def test_mcp_stats(self, tool_helper, mock_ctx): - """Test MCP stats.""" - tool = McpStatsTool() - - # Add some servers first - McpAddTool._mcp_servers = { - "server1": { - "type": "python", - "status": "running", - "command": ["uvx", "server1"], - "tools": ["tool1", "tool2"], - }, - "server2": { - "type": "node", - "status": "stopped", - "command": ["npx", "server2"], - "tools": [], - }, - } - - result = await tool.call(mock_ctx) - if isinstance(result, dict) and "output" in result: - result = result["output"] - - tool_helper.assert_in_result("Total Servers: 2", result) - tool_helper.assert_in_result("server1", result) - tool_helper.assert_in_result("server2", result) - - -class TestSystemTools: - """Test system management tools.""" - - @pytest.mark.asyncio - async def test_stats_tool(self, tool_helper, mock_ctx, db_manager): - """Test comprehensive stats tool.""" - tool = StatsTool(db_manager) - - with patch("psutil.cpu_percent", return_value=50.0): - with patch("psutil.cpu_count", return_value=4): - with patch("psutil.virtual_memory") as mock_mem: - mock_mem.return_value = MagicMock( - total=8 * 1024**3, used=4 * 1024**3, percent=50.0 - ) - - with patch("psutil.disk_usage") as mock_disk: - mock_disk.return_value = MagicMock( - total=100 * 1024**3, - used=50 * 1024**3, - free=50 * 1024**3, - percent=50.0, - ) - - result = await tool.call(mock_ctx) - if isinstance(result, dict) and "output" in result: - result = result["output"] - - tool_helper.assert_in_result("CPU Usage: 50.0%", result) - tool_helper.assert_in_result("Memory: 4.0/8.0 GB", result) - tool_helper.assert_in_result("Disk: 50.0/100.0 GB", result) - tool_helper.assert_in_result("System resources are healthy", result) - - @pytest.mark.asyncio - async def test_tool_enable_disable(self, tool_helper, mock_ctx): - """Test tool enable/disable functionality.""" - enable_tool = ToolEnableTool() - disable_tool = ToolDisableTool() - - # Disable a tool - result = await disable_tool.call(mock_ctx, tool="vector_search") - tool_helper.assert_in_result( - "Successfully disabled tool 'vector_search'", result - ) - - # Check it's disabled - assert not ToolEnableTool.is_tool_enabled("vector_search") - - # Re-enable it - result = await enable_tool.call(mock_ctx, tool="vector_search") - tool_helper.assert_in_result( - "Successfully enabled tool 'vector_search'", result - ) - - # Check it's enabled - assert ToolEnableTool.is_tool_enabled("vector_search") - - @pytest.mark.asyncio - async def test_tool_disable_critical(self, tool_helper, mock_ctx): - """Test that critical tools cannot be disabled.""" - tool = ToolDisableTool() - - result = await tool.call(mock_ctx, tool="tool_enable") - if isinstance(result, dict) and "output" in result: - result = result["output"] - - tool_helper.assert_in_result("Error: Cannot disable critical tool", result) - - -if __name__ == "__main__": - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_no_stubs.py b/pkg/hanzo-mcp/tests/test_no_stubs.py deleted file mode 100644 index e33c135ca..000000000 --- a/pkg/hanzo-mcp/tests/test_no_stubs.py +++ /dev/null @@ -1,431 +0,0 @@ -"""Test to ensure no stub/fake/incomplete code exists in production.""" - -import ast -import re -from pathlib import Path -from typing import List, Tuple - -import pytest - - -class StubDetector(ast.NodeVisitor): - """AST visitor to detect stub implementations.""" - - def __init__(self, filepath: str): - self.filepath = filepath - self.issues: List[Tuple[int, str]] = [] - self.in_test_file = "test" in filepath or "mock" in filepath.lower() - self.in_except_handler = False # Track if we're inside an except block - - def visit_ExceptHandler(self, node: ast.ExceptHandler) -> None: - """Track that we're inside an except handler (fallback stubs are OK).""" - old_in_except = self.in_except_handler - self.in_except_handler = True - self.generic_visit(node) - self.in_except_handler = old_in_except - - def visit_FunctionDef(self, node: ast.FunctionDef) -> None: - """Check function definitions for stub patterns.""" - # Skip test files for certain checks - if self.in_test_file and node.name.startswith("test_"): - self.generic_visit(node) - return - - # Skip functions defined in except handlers (these are legitimate fallbacks) - if self.in_except_handler: - self.generic_visit(node) - return - - # Skip dunder methods (like __init__) that may be empty placeholders - if node.name.startswith("__") and node.name.endswith("__"): - self.generic_visit(node) - return - - # Check for empty functions with just pass (but allow if has docstring) - if len(node.body) == 1 and isinstance(node.body[0], ast.Pass): - self.issues.append( - (node.lineno, f"Function '{node.name}' contains only 'pass' statement") - ) - elif len(node.body) == 2: - # Check for docstring + pass (also a stub) - if ( - isinstance(node.body[0], ast.Expr) - and isinstance(node.body[0].value, ast.Constant) - and isinstance(node.body[0].value.value, str) - and isinstance(node.body[1], ast.Pass) - ): - # Has docstring + pass - this is a documented stub, skip it - pass - - # Check for functions that just raise NotImplementedError - # But allow if it's an abstract method (has docstring explaining it) - if len(node.body) == 1 and isinstance(node.body[0], ast.Raise): - if isinstance(node.body[0].exc, ast.Call): - if ( - hasattr(node.body[0].exc.func, "id") - and node.body[0].exc.func.id == "NotImplementedError" - ): - # This is a legitimate abstract method pattern, skip it - pass - elif len(node.body) == 2: - # Docstring + NotImplementedError - legitimate abstract method - if ( - isinstance(node.body[0], ast.Expr) - and isinstance(node.body[0].value, ast.Constant) - and isinstance(node.body[0].value.value, str) - and isinstance(node.body[1], ast.Raise) - ): - # Has docstring + raise, this is a documented abstract method - pass - - # Check for functions with only ellipsis - if len(node.body) == 1 and isinstance(node.body[0], ast.Expr): - if isinstance(node.body[0].value, ast.Constant): - if node.body[0].value.value is Ellipsis: - self.issues.append( - (node.lineno, f"Function '{node.name}' contains only ellipsis") - ) - - self.generic_visit(node) - - def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None: - """Check async function definitions.""" - # Treat async functions same as regular functions - self.visit_FunctionDef(node) - - -def find_stub_patterns(filepath: Path) -> List[Tuple[int, str, str]]: - """Find stub patterns in a Python file.""" - issues = [] - - # Skip test files for most checks - is_test_file = "test" in filepath.name or "mock" in filepath.name.lower() - - try: - content = filepath.read_text(encoding="utf-8") - except Exception: - return issues - - # Regex patterns to find stub indicators (with case sensitivity flag) - # Note: We're looking for TODO/FIXME comments, not tool names containing "todo" - # Format: (pattern, message, case_insensitive) - patterns = [ - # Match TODO/FIXME comments (case insensitive for comments) - ( - r"#\s*(TODO|FIXME|STUB|FAKE|UNFINISHED|HACK|XXX)\s*:", - "contains {0} comment", - True, - ), - ( - r'assert\s+False,?\s*["\']Not implemented', - 'has "Not implemented" assertion', - True, - ), - ] - - # Additional patterns for non-test files - if not is_test_file: - patterns.extend( - [ - (r"pass\s*#\s*(stub|fake)", "has stub/fake comment after pass", True), - # Only match uppercase "TODO"/"STUB" strings (case sensitive to avoid "todo" tool name) - (r'return\s+["\']TODO["\']', "returns TODO string", False), - (r'return\s+["\']STUB["\']', "returns STUB string", False), - ( - r"return\s+None\s*#\s*(STUB|FAKE)", - "returns None with stub comment", - True, - ), - ] - ) - - lines = content.split("\n") - for line_num, line in enumerate(lines, 1): - for pattern, message, case_insensitive in patterns: - flags = re.IGNORECASE if case_insensitive else 0 - if match := re.search(pattern, line, flags): - keyword = match.group(1) if match.groups() else "stub pattern" - issues.append((line_num, message.format(keyword), filepath.name)) - - # Parse AST for deeper inspection - try: - tree = ast.parse(content) - detector = StubDetector(str(filepath)) - detector.visit(tree) - for line_num, message in detector.issues: - issues.append((line_num, message, filepath.name)) - except SyntaxError: - pass # Ignore files with syntax errors - - return issues - - -def get_python_files(root_dir: Path, exclude_dirs: set = None) -> List[Path]: - """Get all Python files in directory, excluding certain directories.""" - if exclude_dirs is None: - exclude_dirs = { - "__pycache__", - ".git", - ".tox", - ".pytest_cache", - "build", - "dist", - "*.egg-info", - ".venv", - "venv", - "node_modules", - ".mypy_cache", - } - - python_files = [] - for path in root_dir.rglob("*.py"): - # Skip excluded directories - if any(excluded in path.parts for excluded in exclude_dirs): - continue - python_files.append(path) - - return python_files - - -class TestNoStubs: - """Test suite to ensure no stub implementations exist.""" - - def test_no_stub_functions_in_source(self): - """Ensure no stub functions exist in source code.""" - # Get the package root - package_root = Path(__file__).parent.parent / "hanzo_mcp" - - if not package_root.exists(): - pytest.skip(f"Package root {package_root} does not exist") - - all_issues = [] - python_files = get_python_files(package_root) - - for filepath in python_files: - issues = find_stub_patterns(filepath) - for line_num, message, _filename in issues: - all_issues.append( - f"{filepath.relative_to(package_root.parent)}:{line_num} - {message}" - ) - - if all_issues: - report = "\n".join(all_issues) - pytest.fail( - f"Found {len(all_issues)} stub/incomplete implementations:\n{report}" - ) - - def test_critical_functions_implemented(self): - """Ensure critical functions are actually implemented.""" - package_root = Path(__file__).parent.parent / "hanzo_mcp" - - # Critical modules and functions that must be implemented - critical_checks = [ - ("tools/__init__.py", "register_all_tools"), - ("server.py", "__init__"), - ("server.py", "run"), - ("cli.py", "main"), - ] - - for module_path, function_name in critical_checks: - filepath = package_root / module_path - if not filepath.exists(): - pytest.fail(f"Critical module {module_path} does not exist") - - content = filepath.read_text() - - # Check function exists - use a more flexible pattern - # Match 'def function_name(' with optional type hints - function_pattern = rf"def {function_name}\s*\(" - if not re.search(function_pattern, content): - pytest.fail(f"Function {function_name} not found in {module_path}") - - # Find the function body and check it's not a stub - # Use AST for accurate parsing - try: - tree = ast.parse(content) - for node in ast.walk(tree): - if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): - if node.name == function_name: - # Check if function body is just pass, ellipsis, or NotImplementedError - if len(node.body) == 1: - body = node.body[0] - if isinstance(body, ast.Pass): - pytest.fail( - f"Function {function_name} in {module_path} contains only 'pass'" - ) - if isinstance(body, ast.Expr) and isinstance( - body.value, ast.Constant - ): - if body.value.value is Ellipsis: - pytest.fail( - f"Function {function_name} in {module_path} contains only '...'" - ) - if isinstance(body, ast.Raise) and isinstance( - body.exc, ast.Call - ): - if ( - hasattr(body.exc.func, "id") - and body.exc.func.id == "NotImplementedError" - ): - pytest.fail( - f"Function {function_name} in {module_path} raises NotImplementedError" - ) - break - except SyntaxError: - pass # If we can't parse, skip AST check - - def test_no_pytest_skip_in_non_test_files(self): - """Ensure pytest.skip is only used in test files.""" - package_root = Path(__file__).parent.parent / "hanzo_mcp" - - for filepath in get_python_files(package_root): - # Skip test directories - if "test" in str(filepath): - continue - - content = filepath.read_text() - if "pytest.skip" in content or "@pytest.mark.skip" in content: - pytest.fail(f"Found pytest.skip in non-test file: {filepath}") - - def test_no_mock_implementations_in_production(self): - """Ensure no mock implementations exist in production code.""" - package_root = Path(__file__).parent.parent / "hanzo_mcp" - - for filepath in get_python_files(package_root): - # Skip test directories, legitimate mock modules, and fallback implementations - if "test" in str(filepath) or "mock" in filepath.name.lower(): - continue - - content = filepath.read_text() - - # Check for mock-related imports in production code - # Note: class Mock/Fake are OK if they're for fallback functionality - # (like MockContext for when Context is not serialized over MCP) - mock_patterns = [ - r"from unittest\.mock import", - r"import unittest\.mock", - # Only flag clearly test-oriented mock patterns - r"def fake_\w+\s*\(", - r"def mock_\w+\s*\(", - r'return\s+["\']fake\w*["\']', - r'return\s+["\']mock\w*["\']', - ] - - for pattern in mock_patterns: - if re.search(pattern, content, re.IGNORECASE): - pytest.fail( - f"Found mock/fake pattern '{pattern}' in production file: {filepath}" - ) - - def test_all_tool_classes_have_run_method(self): - """Ensure all tool classes have a proper run or call method.""" - package_root = Path(__file__).parent.parent / "hanzo_mcp" / "tools" - - if not package_root.exists(): - pytest.skip("Tools directory does not exist") - - issues = [] - for filepath in get_python_files(package_root): - if "test" in str(filepath) or "__pycache__" in str(filepath): - continue - - content = filepath.read_text() - - try: - tree = ast.parse(content) - except SyntaxError: - continue - - # Find all top-level Tool classes (not nested) and check for run/call methods - for node in tree.body: - if isinstance(node, ast.ClassDef): - class_name = node.name - # Only check classes that end with Tool - if not class_name.endswith("Tool"): - continue - # Skip Params/Config classes that are just type definitions - if "Params" in class_name or "Config" in class_name: - continue - # Skip abstract base classes - if "Base" in class_name or "Abstract" in class_name: - continue - # Skip adapter classes - if "Adapter" in class_name: - continue - - # Check if class has run() or call() method (or execute as alias) - has_run_or_call = False - for item in node.body: - if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)): - if item.name in ("run", "call", "execute"): - has_run_or_call = True - break - # Also check for method aliases like "call = execute" - if isinstance(item, ast.Assign): - for target in item.targets: - if isinstance(target, ast.Name) and target.id in ( - "run", - "call", - ): - has_run_or_call = True - break - - if not has_run_or_call: - # If it inherits from any base class, the base likely provides call() - # Only flag classes with no inheritance or with Object-only inheritance - inherits_from_something_meaningful = len(node.bases) > 0 - if inherits_from_something_meaningful: - # Check all base class names - for base in node.bases: - base_name = "" - if isinstance(base, ast.Name): - base_name = base.id - elif isinstance(base, ast.Attribute): - base_name = base.attr - # If any base ends with Tool, Base, Mixin it's likely OK - if any( - base_name.endswith(suffix) - for suffix in ("Tool", "Base", "Mixin") - ): - inherits_from_something_meaningful = True - break - else: - # No bases - this is a standalone Tool class that needs run/call - issues.append( - f"Tool class {class_name} in {filepath.name} missing run() or call() method" - ) - - if issues: - pytest.fail( - f"Found {len(issues)} tool classes without run()/call() method:\n" - + "\n".join(issues[:10]) - ) - - def test_no_debug_prints_in_production(self): - """Ensure no debug print statements in production code.""" - package_root = Path(__file__).parent.parent / "hanzo_mcp" - - for filepath in get_python_files(package_root): - # Skip test files - if "test" in str(filepath): - continue - - content = filepath.read_text() - - # Check for debug patterns - debug_patterns = [ - (r"print\s*\([^)]*#\s*DEBUG", "debug print statement"), - (r"print\s*\([^)]*#\s*TODO", "TODO print statement"), - (r"print\s*\([^)]*#\s*REMOVE", "REMOVE print statement"), - (r"console\.log", "console.log statement"), - (r"debugger;?", "debugger statement"), - ] - - for pattern, description in debug_patterns: - if re.search(pattern, content, re.IGNORECASE): - pytest.fail(f"Found {description} in production file: {filepath}") - - -if __name__ == "__main__": - # Run tests directly - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_performance.py b/pkg/hanzo-mcp/tests/test_performance.py deleted file mode 100644 index 757e109d5..000000000 --- a/pkg/hanzo-mcp/tests/test_performance.py +++ /dev/null @@ -1,416 +0,0 @@ -"""Performance tests for MCP tools.""" - -import asyncio -import os -import tempfile -import time -from unittest.mock import Mock, patch - -import pytest - -from tests.test_utils import create_mock_ctx - -# Try to import memory tools, skip tests if not available -try: - from hanzo_tools.memory.memory_tools import ( - CreateMemoriesTool, - RecallMemoriesTool, - ) - - MEMORY_TOOLS_AVAILABLE = True -except ImportError: - MEMORY_TOOLS_AVAILABLE = False - RecallMemoriesTool = None - CreateMemoriesTool = None - -from hanzo_tools.agent.swarm_tool import SwarmTool -from hanzo_tools.filesystem.search_tool import SearchTool as UnifiedSearchTool - -from hanzo_mcp.tools.common.batch_tool import BatchTool - - -class TestMemoryPerformance: - """Performance tests for memory tools.""" - - @pytest.mark.skipif(not MEMORY_TOOLS_AVAILABLE, reason="Memory tools not available") - @patch("hanzo_memory.services.memory.get_memory_service") - def test_bulk_memory_creation_performance(self, mock_get_service): - """Test performance of bulk memory creation.""" - mock_service = Mock() - created_count = 0 - - def mock_create(*args, **kwargs): - nonlocal created_count - created_count += 1 - return Mock(memory_id=f"mem_{created_count}") - - mock_service.create_memory = mock_create - mock_get_service.return_value = mock_service - - tool = CreateMemoriesTool() - mock_ctx = create_mock_ctx() - - # Create 1000 memories - memories = [f"Memory {i}" for i in range(1000)] - - start_time = time.time() - result = asyncio.run(tool.call(mock_ctx, statements=memories)) - elapsed = time.time() - start_time - - assert "1000" in str(result) or created_count == 1000 - assert created_count == 1000 - assert elapsed < 5.0 # Should complete within 5 seconds - - print(f"Created 1000 memories in {elapsed:.2f} seconds") - - @pytest.mark.skip(reason="Test uses deprecated API - needs update") - def test_concurrent_memory_operations(self): - """Test concurrent memory operations.""" - mock_service = Mock() - operation_times = [] - - async def mock_operation(*args, **kwargs): - start = time.time() - await asyncio.sleep(0.01) # Simulate work - operation_times.append(time.time() - start) - return Mock(memory_id=f"mem_{len(operation_times)}") - - mock_service.create_memory = Mock(side_effect=mock_operation) - mock_service.search_memories = Mock( - side_effect=lambda *a, **k: asyncio.create_task(mock_operation(*a, **k)) - ) - mock_get_service.return_value = mock_service - - async def run_concurrent_operations(): - """Run multiple operations concurrently.""" - create_tool = CreateMemoriesTool() - recall_tool = RecallMemoriesTool() - mock_ctx = create_mock_ctx() - - # Run 10 operations concurrently - tasks = [] - for i in range(5): - tasks.append(create_tool.call(mock_ctx, statements=[f"Memory {i}"])) - tasks.append(recall_tool.call(mock_ctx, queries=[f"Query {i}"])) - - start_time = time.time() - await asyncio.gather(*tasks) - total_time = time.time() - start_time - - return total_time, operation_times - - total_time, op_times = asyncio.run(run_concurrent_operations()) - - # Should be faster than sequential execution - sequential_time = sum(op_times) - assert total_time < sequential_time * 0.5 # At least 2x speedup - - print( - f"Concurrent: {total_time:.2f}s, Sequential would be: {sequential_time:.2f}s" - ) - - -class TestSearchPerformance: - """Performance tests for search tools.""" - - def test_large_directory_search(self, tool_helper): - """Test search performance in large directory structure.""" - # Create temporary directory with many files - with tempfile.TemporaryDirectory() as tmpdir: - # Create 1000 files in nested structure - for i in range(10): - subdir = os.path.join(tmpdir, f"dir_{i}") - os.makedirs(subdir) - for j in range(100): - filepath = os.path.join(subdir, f"file_{j}.txt") - with open(filepath, "w") as f: - f.write(f"Content of file {i}_{j}\n") - if j % 10 == 0: - f.write("SPECIAL_PATTERN\n") - - # Mock permission manager - from hanzo_mcp.tools.common.permissions import PermissionManager - - pm = PermissionManager() - pm.add_allowed_path(tmpdir) - - # Create search tool - with patch("hanzo_tools.filesystem.search_tool.ProjectVectorManager"): - tool = UnifiedSearchTool(permission_manager=pm) - mock_ctx = create_mock_ctx() - - # Search for pattern - start_time = time.time() - result = asyncio.run( - tool.call( - mock_ctx, pattern="SPECIAL_PATTERN", path=tmpdir, max_results=50 - ) - ) - elapsed = time.time() - start_time - - # Should find matches quickly - tool_helper.assert_in_result("SPECIAL_PATTERN", result) - assert elapsed < 2.0 # Should complete within 2 seconds - - print(f"Searched 1000 files in {elapsed:.2f} seconds") - - -class TestBatchToolPerformance: - """Performance tests for batch tool.""" - - def test_large_batch_processing(self): - """Test processing large batches of tool calls.""" - # Create mock tools - mock_tools = {} - call_times = [] - - async def mock_tool_call(*args, **kwargs): - start = time.time() - await asyncio.sleep(0.01) # Simulate work - call_times.append(time.time() - start) - return f"Result {len(call_times)}" - - for i in range(10): - tool = Mock() - tool.name = f"tool_{i}" - tool.call = Mock(side_effect=mock_tool_call) - mock_tools[tool.name] = tool - - batch_tool = BatchTool(mock_tools) - mock_ctx = create_mock_ctx() - - # Create 50 invocations - invocations = [] - for i in range(50): - invocations.append({"tool_name": f"tool_{i % 10}", "input": {"param": i}}) - - # Process batch - start_time = time.time() - result = asyncio.run( - batch_tool.call( - mock_ctx, description="Large batch test", invocations=invocations - ) - ) - elapsed = time.time() - start_time - - # Should process efficiently - assert "error" not in result.lower() - - # Should be much faster than sequential - sequential_time = len(invocations) * 0.01 - assert elapsed < sequential_time * 0.5 # At least 2x speedup - - print(f"Processed 50 tool calls in {elapsed:.2f} seconds") - - -class TestSwarmPerformance: - """Performance tests for swarm tool.""" - - @pytest.mark.skip( - reason="Test uses deprecated dispatch_to_model API - swarm now uses hanzo-agents SDK" - ) - def test_parallel_agent_execution(self): - """Test parallel execution of swarm agents.""" - execution_times = [] - - async def mock_agent_execution(*args, **kwargs): - start = time.time() - await asyncio.sleep(0.1) # Simulate agent work - execution_times.append(time.time() - start) - return f"Agent result {len(execution_times)}" - - mock_dispatch.side_effect = mock_agent_execution - - tool = SwarmTool() - mock_ctx = create_mock_ctx() - - # Create parallel agent network - agents = [] - for i in range(10): - agents.append({"id": f"agent_{i}", "query": f"Task {i}", "role": "worker"}) - - # Add a final reviewer that depends on all - agents.append( - { - "id": "reviewer", - "query": "Review all results", - "role": "reviewer", - "receives_from": [f"agent_{i}" for i in range(10)], - } - ) - - # Execute swarm - start_time = time.time() - asyncio.run( - tool.call(mock_ctx, query="Parallel processing test", agents=agents) - ) - elapsed = time.time() - start_time - - # Should execute workers in parallel - # Sequential would be 11 * 0.1 = 1.1 seconds - # Parallel should be ~0.2 seconds (2 phases) - assert elapsed < 0.6 # Allow some overhead - - print(f"Executed {len(agents)} agents in {elapsed:.2f} seconds") - - -class TestPaginationPerformance: - """Test pagination system performance.""" - - def test_large_output_pagination(self): - """Test pagination with very large outputs.""" - from hanzo_mcp.tools.common.paginated_response import AutoPaginatedResponse - from hanzo_mcp.tools.common.truncate import estimate_tokens - - # Create large dataset - large_data = [] - for i in range(10000): - large_data.append( - { - "id": i, - "content": f"This is item {i} with some content that makes it larger", - "metadata": {"category": i % 10, "priority": i % 5}, - } - ) - - # Test token estimation performance - start_time = time.time() - total_tokens = 0 - for item in large_data[:1000]: # Test first 1000 - total_tokens += estimate_tokens(str(item)) - estimation_time = time.time() - start_time - - assert estimation_time < 1.0 # Should be fast - print(f"Estimated tokens for 1000 items in {estimation_time:.2f} seconds") - - # Test pagination creation with new API - start_time = time.time() - - # Use the new AutoPaginatedResponse API - content_str = "\n".join([str(item) for item in large_data]) - response = AutoPaginatedResponse.create_response(content_str) - - pagination_time = time.time() - start_time - - assert "content" in response # New API returns content dict - assert pagination_time < 0.5 # Should be very fast - - print(f"Created paginated response in {pagination_time:.2f} seconds") - - -class TestMemoryStressTest: - """Stress tests for memory system.""" - - @pytest.mark.skip(reason="Test uses deprecated API - needs update") - def test_memory_system_under_load(self): - """Test memory system under heavy load.""" - mock_service = Mock() - - # Track all operations - operations = [] - - def track_operation(op_type): - def wrapper(*args, **kwargs): - operations.append((op_type, time.time())) - return Mock(memory_id=f"mem_{len(operations)}") - - return wrapper - - mock_service.create_memory = track_operation("create") - mock_service.search_memories = Mock(side_effect=lambda *a, **k: []) - mock_service.delete_memory = Mock(side_effect=lambda *a, **k: True) - mock_get_service.return_value = mock_service - - async def stress_test(): - """Run many operations concurrently.""" - create_tool = CreateMemoriesTool() - recall_tool = RecallMemoriesTool() - mock_ctx = create_mock_ctx() - - tasks = [] - - # Create 100 concurrent operations - for i in range(100): - if i % 3 == 0: - tasks.append(create_tool.call(mock_ctx, statements=[f"Memory {i}"])) - else: - tasks.append(recall_tool.call(mock_ctx, queries=[f"Query {i}"])) - - start_time = time.time() - await asyncio.gather(*tasks, return_exceptions=True) - return time.time() - start_time - - elapsed = asyncio.run(stress_test()) - - # Should handle load without crashing - assert len(operations) > 30 # At least the creates - assert elapsed < 5.0 # Should complete reasonably fast - - print( - f"Completed {len(operations)} operations under load in {elapsed:.2f} seconds" - ) - - -class TestConcurrentFileOperations: - """Test concurrent file operations.""" - - def test_concurrent_file_access(self): - """Test multiple tools accessing files concurrently.""" - from hanzo_tools.filesystem.read import ReadTool - from hanzo_tools.filesystem.write import Write as WriteTool - - from hanzo_mcp.tools.common.permissions import PermissionManager - - with tempfile.TemporaryDirectory() as tmpdir: - pm = PermissionManager() - pm.add_allowed_path(tmpdir) - - # Create test files - test_files = [] - for i in range(20): - filepath = os.path.join(tmpdir, f"test_{i}.txt") - with open(filepath, "w") as f: - f.write(f"Initial content {i}\n" * 100) - test_files.append(filepath) - - read_tool = ReadTool(pm) - write_tool = WriteTool(pm) - - async def concurrent_operations(): - """Run concurrent read/write operations.""" - tasks = [] - mock_ctx = create_mock_ctx() - - # Mix of reads and writes - for i, filepath in enumerate(test_files): - if i % 3 == 0: - # Write operation - tasks.append( - write_tool.call( - mock_ctx, - file_path=filepath, - content=f"Updated content {i}\n" * 100, - ) - ) - else: - # Read operation - tasks.append(read_tool.call(mock_ctx, file_path=filepath)) - - start_time = time.time() - results = await asyncio.gather(*tasks, return_exceptions=True) - return time.time() - start_time, results - - elapsed, results = asyncio.run(concurrent_operations()) - - # Should complete without errors - errors = [r for r in results if isinstance(r, Exception)] - assert len(errors) == 0 - assert elapsed < 2.0 # Should be fast even with 20 operations - - print( - f"Completed {len(results)} concurrent file operations in {elapsed:.2f} seconds" - ) - - -if __name__ == "__main__": - pytest.main([__file__, "-v", "-s"]) # -s to see print statements diff --git a/pkg/hanzo-mcp/tests/test_refactor_tool.py b/pkg/hanzo-mcp/tests/test_refactor_tool.py deleted file mode 100644 index 0e10c6864..000000000 --- a/pkg/hanzo-mcp/tests/test_refactor_tool.py +++ /dev/null @@ -1,492 +0,0 @@ -"""Tests for the refactor tool.""" - -import os -import tempfile - -import pytest -from hanzo_tools.refactor import RefactorTool, create_refactor_tool - - -class TestRefactorTool: - """Tests for RefactorTool class.""" - - @pytest.fixture - def tool(self): - """Create a refactor tool instance.""" - return create_refactor_tool() - - @pytest.fixture - def temp_python_file(self): - """Create a temporary Python file for testing.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: - f.write("""def old_function(x, y): - result = x + y - return result - -def caller(): - value = old_function(1, 2) - return value - -class MyClass: - def method(self): - return old_function(3, 4) -""") - temp_path = f.name - - yield temp_path - - # Cleanup - if os.path.exists(temp_path): - os.unlink(temp_path) - - @pytest.fixture - def temp_js_file(self): - """Create a temporary JavaScript file for testing.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".js", delete=False) as f: - f.write("""function oldFunction(x, y) { - const result = x + y; - return result; -} - -function caller() { - const value = oldFunction(1, 2); - return value; -} -""") - temp_path = f.name - - yield temp_path - - if os.path.exists(temp_path): - os.unlink(temp_path) - - def test_tool_creation(self, tool): - """Test that the tool can be created.""" - assert tool is not None - assert tool.name == "refactor" - - def test_tool_has_description(self, tool): - """Test that the tool has a description.""" - assert tool.description is not None - assert "refactor" in tool.description.lower() - - @pytest.mark.asyncio - async def test_invalid_action(self, tool): - """Test error handling for invalid action.""" - result = await tool.run(action="invalid_action", file="test.py") - assert result.data["error"] is not None - assert "Invalid action" in result.data["error"] - - @pytest.mark.asyncio - async def test_file_not_found(self, tool): - """Test error handling for non-existent file.""" - result = await tool.run( - action="rename", - file="/nonexistent/file.py", - line=1, - column=0, - new_name="new", - ) - assert "error" in result.data - assert "not found" in result.data["error"].lower() - - @pytest.mark.asyncio - async def test_find_references_missing_args(self, tool, temp_python_file): - """Test find_references without required arguments.""" - result = await tool.run(action="find_references", file=temp_python_file) - assert result.data["success"] is False - assert "required" in result.data["errors"][0].lower() - - @pytest.mark.asyncio - async def test_find_references(self, tool, temp_python_file): - """Test finding references to a symbol.""" - result = await tool.run( - action="find_references", - file=temp_python_file, - line=1, - column=4, # 'old_function' - ) - assert result.data["success"] is True - assert result.data["action"] == "find_references" - assert len(result.data["changes"]) > 0 - - @pytest.mark.asyncio - async def test_rename_preview(self, tool, temp_python_file): - """Test rename in preview mode.""" - result = await tool.run( - action="rename", - file=temp_python_file, - line=1, - column=4, # 'old_function' - new_name="new_function", - preview=True, - ) - assert result.data["success"] is True - assert result.data["action"] == "rename" - assert "preview" in result.data - assert len(result.data["preview"]) > 0 - - # Verify file wasn't modified - with open(temp_python_file, "r") as f: - content = f.read() - assert "old_function" in content - assert "new_function" not in content - - @pytest.mark.asyncio - async def test_rename_apply(self, tool, temp_python_file): - """Test rename with actual application.""" - result = await tool.run( - action="rename", - file=temp_python_file, - line=1, - column=4, # 'old_function' - new_name="new_function", - preview=False, - ) - assert result.data["success"] is True - assert result.data["action"] == "rename" - assert result.data["changes_applied"] > 0 - - # Verify file was modified - with open(temp_python_file, "r") as f: - content = f.read() - assert "new_function" in content - # All occurrences should be renamed - assert "old_function" not in content - - @pytest.mark.asyncio - async def test_extract_function_missing_name(self, tool, temp_python_file): - """Test extract_function without new_name.""" - result = await tool.run( - action="extract_function", - file=temp_python_file, - start_line=2, - end_line=3, - ) - assert result.data["success"] is False - assert "new_name" in result.data["errors"][0].lower() - - @pytest.mark.asyncio - async def test_extract_function_preview(self, tool, temp_python_file): - """Test extract_function in preview mode.""" - result = await tool.run( - action="extract_function", - file=temp_python_file, - start_line=2, - end_line=3, - new_name="compute_result", - preview=True, - ) - assert result.data["success"] is True - assert result.data["action"] == "extract_function" - assert "preview" in result.data - assert len(result.data["preview"]) > 0 - - @pytest.mark.asyncio - async def test_extract_variable_missing_args(self, tool, temp_python_file): - """Test extract_variable without required arguments.""" - result = await tool.run( - action="extract_variable", - file=temp_python_file, - ) - assert result.data["success"] is False - - @pytest.mark.asyncio - async def test_inline_missing_args(self, tool, temp_python_file): - """Test inline without required arguments.""" - result = await tool.run( - action="inline", - file=temp_python_file, - ) - assert result.data["success"] is False - - @pytest.mark.asyncio - async def test_move_missing_target(self, tool, temp_python_file): - """Test move without target_file.""" - result = await tool.run( - action="move", - file=temp_python_file, - line=1, - ) - assert result.data["success"] is False - assert "target_file" in result.data["errors"][0].lower() - - @pytest.mark.asyncio - async def test_organize_imports_python(self, tool): - """Test organize imports for Python file.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: - f.write("""import os -from typing import List -import sys -from collections import defaultdict -import json - -def main(): - pass -""") - temp_path = f.name - - try: - result = await tool.run( - action="organize_imports", - file=temp_path, - preview=False, - ) - assert result.data["success"] is True - - with open(temp_path, "r") as f: - content = f.read() - - # Imports should be sorted - lines = content.split("\n") - import_lines = [l for l in lines if l.startswith("import ")] - from_lines = [l for l in lines if l.startswith("from ")] - - # Check imports are sorted - assert import_lines == sorted(import_lines, key=str.lower) - assert from_lines == sorted(from_lines, key=str.lower) - - finally: - if os.path.exists(temp_path): - os.unlink(temp_path) - - @pytest.mark.asyncio - async def test_change_signature_missing_args(self, tool, temp_python_file): - """Test change_signature without required arguments.""" - result = await tool.run(action="change_signature") - assert result.data["success"] is False - assert "required" in result.data["errors"][0].lower() - - @pytest.mark.asyncio - async def test_change_signature_add_parameter_preview(self, tool): - """Test adding a parameter with preview.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: - f.write("""def greet(name): - return f"Hello, {name}!" - -def main(): - msg = greet("World") - print(msg) -""") - temp_path = f.name - - try: - result = await tool.run( - action="change_signature", - file=temp_path, - line=1, - add_parameter={"name": "greeting", "default": "'Hello'"}, - preview=True, - ) - assert result.data["success"] is True - assert result.data["action"] == "change_signature" - assert len(result.data["preview"]) > 0 - - # Verify file wasn't modified - with open(temp_path, "r") as f: - content = f.read() - assert "greeting" not in content - - finally: - if os.path.exists(temp_path): - os.unlink(temp_path) - - @pytest.mark.asyncio - async def test_change_signature_rename_parameter(self, tool): - """Test renaming a parameter.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: - f.write("""def compute(x, y): - return x + y - -result = compute(1, 2) -""") - temp_path = f.name - - try: - result = await tool.run( - action="change_signature", - file=temp_path, - line=1, - rename_parameter={"old": "x", "new": "first"}, - preview=True, - ) - assert result.data["success"] is True - assert "first" in str(result.data["preview"]) - - finally: - if os.path.exists(temp_path): - os.unlink(temp_path) - - @pytest.mark.asyncio - async def test_change_signature_no_function(self, tool): - """Test change_signature when no function at line.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: - f.write("""x = 10 -y = 20 -""") - temp_path = f.name - - try: - result = await tool.run( - action="change_signature", - file=temp_path, - line=1, - add_parameter={"name": "z"}, - ) - assert result.data["success"] is False - assert "function signature" in result.data["errors"][0].lower() - - finally: - if os.path.exists(temp_path): - os.unlink(temp_path) - - -class TestRefactorToolHelpers: - """Tests for RefactorTool helper methods.""" - - @pytest.fixture - def tool(self): - """Create a refactor tool instance.""" - return RefactorTool() - - def test_get_identifier_at_basic(self, tool): - """Test getting identifier at position.""" - line = "def my_function(arg1, arg2):" - assert tool._get_identifier_at(line, 4) == "my_function" - assert tool._get_identifier_at(line, 16) == "arg1" - assert tool._get_identifier_at(line, 23) == "arg2" - - def test_get_identifier_at_edge_cases(self, tool): - """Test edge cases for identifier detection.""" - # Empty line - assert tool._get_identifier_at("", 0) is None - - # Column out of range - assert tool._get_identifier_at("hello", 100) == "hello" - - # No identifier at position - assert tool._get_identifier_at(" ", 2) is None - - def test_get_indentation(self, tool): - """Test indentation detection.""" - assert tool._get_indentation("def foo():") == "" - assert tool._get_indentation(" return x") == " " - assert tool._get_indentation("\t\treturn y") == "\t\t" - assert tool._get_indentation("") == "" - - def test_get_language(self, tool): - """Test language detection from file extension.""" - assert tool._get_language("test.py") == "python" - assert tool._get_language("test.js") == "javascript" - assert tool._get_language("test.ts") == "typescript" - assert tool._get_language("test.go") == "go" - assert tool._get_language("test.rs") == "rust" - assert tool._get_language("test.unknown") == "unknown" - - def test_find_used_variables(self, tool): - """Test finding used variables in code.""" - code = "result = x + y" - vars = tool._find_used_variables(code, "python") - assert "x" in vars - assert "y" in vars - assert "result" in vars - - def test_find_defined_variables_python(self, tool): - """Test finding defined variables in Python.""" - code = "x = 10\ny = 20" - vars = tool._find_defined_variables(code, "python") - assert "x" in vars - assert "y" in vars - - def test_find_defined_variables_javascript(self, tool): - """Test finding defined variables in JavaScript.""" - code = "const x = 10;\nlet y = 20;" - vars = tool._find_defined_variables(code, "javascript") - assert "x" in vars - assert "y" in vars - - def test_build_function_python(self, tool): - """Test building Python function.""" - func = tool._build_function("my_func", ["x", "y"], "return x + y", "python", "") - assert "def my_func(x, y):" in func - assert "return x + y" in func - - def test_build_function_javascript(self, tool): - """Test building JavaScript function.""" - func = tool._build_function( - "myFunc", ["x", "y"], "return x + y;", "javascript", "" - ) - assert "function myFunc(x, y)" in func - assert "return x + y;" in func - - def test_build_variable_declaration_python(self, tool): - """Test building Python variable declaration.""" - decl = tool._build_variable_declaration("x", "10 + 20", "python", " ") - assert decl == " x = 10 + 20" - - def test_build_variable_declaration_javascript(self, tool): - """Test building JavaScript variable declaration.""" - decl = tool._build_variable_declaration("x", "10 + 20", "javascript", " ") - assert decl == " const x = 10 + 20;" - - def test_parse_python_params(self, tool): - """Test parsing Python parameters.""" - params = tool._parse_python_params("x, y: int, z: str = 'hello'") - assert len(params) == 3 - assert params[0]["name"] == "x" - assert params[1]["name"] == "y" - assert params[1]["type"] == "int" - assert params[2]["name"] == "z" - assert params[2]["type"] == "str" - assert params[2]["default"] == "'hello'" - - def test_parse_python_params_empty(self, tool): - """Test parsing empty parameters.""" - params = tool._parse_python_params("") - assert params == [] - - def test_parse_python_params_complex_default(self, tool): - """Test parsing parameters with complex defaults.""" - params = tool._parse_python_params("items: List[int] = [1, 2, 3]") - assert len(params) == 1 - assert params[0]["name"] == "items" - assert params[0]["type"] == "List[int]" - assert params[0]["default"] == "[1, 2, 3]" - - def test_build_signature_python(self, tool): - """Test building Python function signature.""" - params = [ - {"name": "x", "type": "int", "default": None}, - {"name": "y", "type": "str", "default": "'default'"}, - ] - sig = tool._build_signature("my_func", params, "python") - assert "def my_func(x: int, y: str = 'default'):" in sig - - def test_build_signature_javascript(self, tool): - """Test building JavaScript function signature.""" - params = [ - {"name": "x", "type": None, "default": None}, - {"name": "y", "type": None, "default": "10"}, - ] - sig = tool._build_signature("myFunc", params, "javascript") - assert "function myFunc(x, y = 10) {" in sig - - def test_parse_call_arguments(self, tool): - """Test parsing function call arguments.""" - args = tool._parse_call_arguments("myFunc(1, 2, 'hello')", "myFunc") - assert args == ["1", "2", "'hello'"] - - def test_parse_call_arguments_nested(self, tool): - """Test parsing nested function call arguments.""" - args = tool._parse_call_arguments("foo(bar(1, 2), [3, 4])", "foo") - assert len(args) == 2 - assert args[0] == "bar(1, 2)" - assert args[1] == "[3, 4]" - - -# Test factory function -def test_create_refactor_tool(): - """Test the factory function.""" - tool = create_refactor_tool() - assert isinstance(tool, RefactorTool) - assert tool.name == "refactor" diff --git a/pkg/hanzo-mcp/tests/test_search.py b/pkg/hanzo-mcp/tests/test_search.py deleted file mode 100644 index 489ad11ea..000000000 --- a/pkg/hanzo-mcp/tests/test_search.py +++ /dev/null @@ -1,635 +0,0 @@ -"""Comprehensive test suite for search functionality.""" - -import tempfile -from pathlib import Path -from unittest.mock import AsyncMock, MagicMock, patch - -import pytest -from hanzo_tools.filesystem.search_tool import SearchResult, SearchTool, SearchType - -from hanzo_mcp.tools.common.permissions import PermissionManager -from tests.test_utils import create_permission_manager - - -class TestSearchTool: - """Test suite for the SearchTool.""" - - @pytest.fixture - def permission_manager(self): - """Create a permission manager for testing.""" - pm = PermissionManager() - pm.add_allowed_path("/tmp") - pm.add_allowed_path(".") - return pm - - @pytest.fixture - def mock_project_manager(self): - """Create a mock project manager.""" - mock_pm = MagicMock() - mock_pm.search_all_projects = AsyncMock(return_value={}) - mock_pm.get_project_for_path = MagicMock(return_value=None) - mock_pm._get_global_store = MagicMock() - mock_pm.projects = {} - return mock_pm - - @pytest.fixture - def search_tool(self, permission_manager, mock_project_manager): - """Create a search tool instance.""" - return SearchTool(permission_manager, mock_project_manager) - - @pytest.fixture - def mock_context(self): - """Create a mock MCP context.""" - ctx = MagicMock() - ctx.meta = MagicMock() - return ctx - - @pytest.fixture - def test_files(self): - """Create temporary test files.""" - with tempfile.TemporaryDirectory() as tmpdir: - test_dir = Path(tmpdir) - - # Create test Python file - python_file = test_dir / "test_module.py" - python_file.write_text(""" -def hello_world(): - '''Say hello to the world.''' - print("Hello, world!") - return "greeting" - -class TestClass: - '''A test class for demonstration.''' - - def test_method(self): - '''Test method with error handling.''' - try: - result = hello_world() - return result - except Exception as e: - print(f"Error occurred: {e}") - raise - -def complex_function(data, options=None): - '''Complex function for testing search.''' - if options is None: - options = {} - - # Process data with error handling - processed = [] - for item in data: - try: - processed.append(item.upper()) - except AttributeError: - processed.append(str(item)) - - return processed -""") - - # Create test JavaScript file - js_file = test_dir / "test_script.js" - js_file.write_text(""" -function helloWorld() { - console.log("Hello, world!"); - return "greeting"; -} - -class TestClass { - constructor() { - this.name = "test"; - } - - testMethod() { - try { - return helloWorld(); - } catch (error) { - console.error("Error occurred:", error); - throw error; - } - } -} - -function complexFunction(data, options = {}) { - const processed = data.map(item => { - try { - return item.toUpperCase(); - } catch (error) { - return String(item); - } - }); - - return processed; -} -""") - - # Create test documentation file - md_file = test_dir / "README.md" - md_file.write_text(""" -# Test Project - -This is a test project for demonstrating search capabilities. - -## Features - -- Hello world functionality -- Error handling -- Complex data processing -- Multi-language support - -## Usage - -```python -from test_module import hello_world -result = hello_world() -``` - -## Error Handling - -The project includes comprehensive error handling throughout. -""") - - yield { - "dir": test_dir, - "python": python_file, - "javascript": js_file, - "markdown": md_file, - } - - def test_search_intent_detection(self, tool_helper, search_tool): - """Test the search intent detection logic.""" - # Regex pattern should disable vector search - use_vector, use_ast, use_symbol = search_tool._detect_search_intent(".*error") - assert not use_vector - assert use_ast - assert use_symbol - - # Function name should enable symbol and AST - use_vector, use_ast, use_symbol = search_tool._detect_search_intent( - "hello_world" - ) - assert use_vector # Could be semantic - assert use_ast - assert use_symbol - - # Natural language should enable vector search - use_vector, use_ast, use_symbol = search_tool._detect_search_intent( - "error handling functionality" - ) - assert use_vector - assert use_ast - assert use_symbol - - @pytest.mark.asyncio - async def test_grep_search( - self, tool_helper, search_tool, test_files, mock_context - ): - """Test grep search functionality.""" - tool_ctx = MagicMock() - tool_ctx.info = AsyncMock() - tool_ctx.error = AsyncMock() - tool_ctx.mcp_context = mock_context - - with patch.object(search_tool, "create_tool_context", return_value=tool_ctx): - with patch.object(search_tool.grep_tool, "call") as mock_grep: - mock_grep.return_value = """Found 2 matches in 1 file: - -test_module.py:3: def hello_world(): -test_module.py:15: result = hello_world()""" - - results = await search_tool._run_grep_search( - "hello_world", str(test_files["dir"]), "*", tool_ctx, 10 - ) - - assert len(results) == 2 - assert all(r.search_type == SearchType.GREP for r in results) - assert results[0].file_path == "test_module.py" - assert results[0].line_number == 3 - assert "def hello_world():" in results[0].content - - @pytest.mark.asyncio - async def test_ast_search(self, tool_helper, search_tool, test_files, mock_context): - """Test AST search functionality.""" - tool_ctx = MagicMock() - tool_ctx.info = AsyncMock() - tool_ctx.error = AsyncMock() - tool_ctx.mcp_context = mock_context - - with patch.object(search_tool, "create_tool_context", return_value=tool_ctx): - with patch.object(search_tool.grep_ast_tool, "call") as mock_ast: - mock_ast.return_value = f""" -{test_files["python"]}: -3: def hello_world(): -4: '''Say hello to the world.''' -5: print("Hello, world!") -6: return "greeting" -""" - - results = await search_tool._run_ast_search( - "hello_world", str(test_files["dir"]), "*", tool_ctx, 10 - ) - - assert len(results) > 0 - assert all(r.search_type == SearchType.AST for r in results) - - @pytest.mark.asyncio - async def test_symbol_search( - self, tool_helper, search_tool, test_files, mock_context - ): - """Test symbol search functionality.""" - tool_ctx = MagicMock() - tool_ctx.info = AsyncMock() - tool_ctx.error = AsyncMock() - - with patch.object(search_tool, "create_tool_context", return_value=tool_ctx): - # Mock the AST analyzer - with patch.object(search_tool.ast_analyzer, "analyze_file") as mock_analyze: - from hanzo_mcp.tools.vector.ast_analyzer import FileAST, Symbol - - # Create mock symbols - symbol = Symbol( - name="hello_world", - type="function", - file_path=str(test_files["python"]), - line_start=3, - line_end=6, - column_start=0, - column_end=20, - scope="global", - signature="hello_world()", - ) - - mock_ast = FileAST( - file_path=str(test_files["python"]), - file_hash="test_hash", - language="python", - symbols=[symbol], - ast_nodes=[], - imports=[], - exports=[], - dependencies=[], - ) - - mock_analyze.return_value = mock_ast - - results = await search_tool._run_symbol_search( - "hello_world", str(test_files["dir"]), tool_ctx, 10 - ) - - assert len(results) > 0 - assert all(r.search_type == SearchType.SYMBOL for r in results) - assert results[0].symbol_info is not None - assert results[0].symbol_info.name == "hello_world" - - @pytest.mark.asyncio - async def test_vector_search( - self, tool_helper, search_tool, test_files, mock_context - ): - """Test vector search functionality.""" - tool_ctx = MagicMock() - tool_ctx.info = AsyncMock() - tool_ctx.error = AsyncMock() - tool_ctx.mcp_context = mock_context - - # Mock vector tool - search_tool.vector_tool = MagicMock() - search_tool.vector_tool.call = AsyncMock() - search_tool.vector_tool.call.return_value = """Found 1 results for query: 'error handling' - -Result 1 (Score: 85.5%) - Project: test -test_module.py [Chunk 0] -Content: -Test method with error handling. -try: - result = hello_world() - return result -except Exception as e: - print(f"Error occurred: {e}")""" - - with patch.object(search_tool, "create_tool_context", return_value=tool_ctx): - results = await search_tool._run_vector_search( - "error handling", str(test_files["dir"]), tool_ctx, 10 - ) - - assert len(results) > 0 - assert all(r.search_type == SearchType.VECTOR for r in results) - - @pytest.mark.asyncio - async def test_full_search( - self, tool_helper, search_tool, test_files, mock_context - ): - """Test complete search functionality.""" - with patch.object(search_tool, "validate_path") as mock_validate: - mock_validate.return_value = MagicMock(is_error=False) - - with patch.object(search_tool, "check_path_allowed") as mock_allowed: - mock_allowed.return_value = (True, None) - - with patch.object(search_tool, "check_path_exists") as mock_exists: - mock_exists.return_value = (True, None) - - with patch.object( - search_tool, "create_tool_context" - ) as mock_tool_ctx: - tool_ctx = MagicMock() - tool_ctx.info = AsyncMock() - tool_ctx.error = AsyncMock() - tool_ctx.mcp_context = mock_context - mock_tool_ctx.return_value = tool_ctx - - # Mock all search methods - with patch.object(search_tool, "_run_grep_search") as mock_grep: - with patch.object( - search_tool, "_run_ast_search" - ) as mock_ast: - with patch.object( - search_tool, "_run_symbol_search" - ) as mock_symbol: - # Setup mock results - grep_result = SearchResult( - file_path="test.py", - line_number=1, - content="def hello_world():", - search_type=SearchType.GREP, - score=1.0, - ) - - ast_result = SearchResult( - file_path="test.py", - line_number=1, - content="def hello_world():", - search_type=SearchType.AST, - score=0.9, - context="function definition", - ) - - symbol_result = SearchResult( - file_path="test.py", - line_number=1, - content="function hello_world", - search_type=SearchType.SYMBOL, - score=0.95, - ) - - mock_grep.return_value = [grep_result] - mock_ast.return_value = [ast_result] - mock_symbol.return_value = [symbol_result] - - # Execute search - result = await search_tool.call( - mock_context, - pattern="hello_world", - path=str(test_files["dir"]), - max_results=10, - ) - - tool_helper.assert_in_result( - "Unified Search Results", result - ) - tool_helper.assert_in_result("hello_world", result) - tool_helper.assert_in_result("Found", result) - - def test_result_combination_and_ranking(self, tool_helper, search_tool): - """Test result combination and ranking logic.""" - # Create test results - grep_result = SearchResult( - file_path="test.py", - line_number=1, - content="def hello_world():", - search_type=SearchType.GREP, - score=1.0, - ) - - ast_result = SearchResult( - file_path="test.py", - line_number=1, - content="def hello_world():", - search_type=SearchType.AST, - score=0.9, - context="function definition", - ) - - symbol_result = SearchResult( - file_path="test.py", - line_number=1, - content="function hello_world", - search_type=SearchType.SYMBOL, - score=0.95, - ) - - vector_result = SearchResult( - file_path="other.py", - line_number=5, - content="call hello_world function", - search_type=SearchType.VECTOR, - score=0.8, - ) - - results_by_type = { - SearchType.GREP: [grep_result], - SearchType.AST: [ast_result], - SearchType.SYMBOL: [symbol_result], - SearchType.VECTOR: [vector_result], - } - - combined = search_tool._combine_and_rank_results(results_by_type) - - # Should have 2 unique results (3 duplicates merged into 1) - assert len(combined) == 2 - - # Should be sorted by score (symbol score should win for duplicates) - assert combined[0].score == 0.95 # Symbol result wins - assert combined[1].score == 0.8 # Vector result - - def test_search_result_serialization(self): - """Test SearchResult and UnifiedSearchResults serialization.""" - from hanzo_mcp.tools.vector.ast_analyzer import Symbol - - symbol = Symbol( - name="test_func", - type="function", - file_path="test.py", - line_start=1, - line_end=5, - column_start=0, - column_end=20, - scope="global", - ) - - result = SearchResult( - file_path="test.py", - line_number=1, - content="def test_func():", - search_type=SearchType.GREP, - score=1.0, - context="function definition", - symbol_info=symbol, - ) - - # Test serialization - result_dict = result.to_dict() - assert result_dict["file_path"] == "test.py" - assert result_dict["search_type"] == "grep" - assert result_dict["symbol_info"]["name"] == "test_func" - - # Test UnifiedSearchResults - unified_results = UnifiedSearchResults( - query="test", - total_results=1, - results_by_type={SearchType.GREP: [result]}, - combined_results=[result], - search_time_ms=100.5, - ) - - unified_dict = unified_results.to_dict() - assert unified_dict["query"] == "test" - assert unified_dict["total_results"] == 1 - assert unified_dict["search_time_ms"] == 100.5 - - -class TestUnifiedSearchIntegration: - """Integration tests for search with real file operations.""" - - @pytest.fixture - def real_test_environment(self): - """Create a real test environment with actual files.""" - with tempfile.TemporaryDirectory() as tmpdir: - test_dir = Path(tmpdir) - - # Create a small Python project - (test_dir / "__init__.py").touch() - - main_file = test_dir / "main.py" - main_file.write_text(""" -#!/usr/bin/env python3 -'''Main module for testing search.''' - -import logging -from typing import List, Optional - -logger = logging.getLogger(__name__) - -class DataProcessor: - '''Processes data with error handling.''' - - def __init__(self, config: Optional[dict] = None): - self.config = config or {} - logger.info("DataProcessor initialized") - - def process_items(self, items: List[str]) -> List[str]: - '''Process a list of items with error handling.''' - processed = [] - - for item in items: - try: - result = self._process_single_item(item) - processed.append(result) - except ValueError as e: - logger.error(f"Error processing item {item}: {e}") - continue - except Exception as e: - logger.critical(f"Unexpected error: {e}") - raise - - return processed - - def _process_single_item(self, item: str) -> str: - '''Process a single item.''' - if not item.strip(): - raise ValueError("Empty item") - - return item.upper().strip() - -def main(): - '''Main function with comprehensive error handling.''' - try: - processor = DataProcessor() - test_data = ["hello", "world", "", "test"] - - results = processor.process_items(test_data) - print(f"Processed {len(results)} items successfully") - - return results - except Exception as e: - logger.error(f"Main execution failed: {e}") - return [] - -if __name__ == "__main__": - main() -""") - - utils_file = test_dir / "utils.py" - utils_file.write_text(""" -'''Utility functions for the test project.''' - -import re -from typing import Union, Optional - -def validate_input(data: Union[str, int, float]) -> bool: - '''Validate input data with error handling.''' - try: - if isinstance(data, str): - return bool(data.strip()) - elif isinstance(data, (int, float)): - return not (data is None or data != data) # Check for NaN - else: - return False - except Exception: - return False - -def format_error_message(error: Exception, context: Optional[str] = None) -> str: - '''Format error messages consistently.''' - base_msg = f"{type(error).__name__}: {str(error)}" - - if context: - return f"[{context}] {base_msg}" - - return base_msg - -def extract_function_names(code: str) -> List[str]: - '''Extract function names from Python code.''' - pattern = r'def\\s+([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(' - matches = re.findall(pattern, code) - return matches -""") - - yield {"dir": test_dir, "main": main_file, "utils": utils_file} - - @pytest.mark.asyncio - async def test_real_search(self, tool_helper, real_test_environment): - """Test search on real files.""" - permission_manager = create_permission_manager( - [str(real_test_environment["dir"])] - ) - - # Test without vector search (no project manager) - unified_tool = SearchTool(permission_manager, None) - - # Mock context - ctx = MagicMock() - - # Test search for "error handling" - with patch.object(unified_tool, "validate_path") as mock_validate: - mock_validate.return_value = MagicMock(is_error=False) - - with patch.object(unified_tool, "check_path_allowed") as mock_allowed: - mock_allowed.return_value = (True, None) - - with patch.object(unified_tool, "check_path_exists") as mock_exists: - mock_exists.return_value = (True, None) - - result = await unified_tool.call( - ctx, - pattern="error.handling", - path=str(real_test_environment["dir"]), - enable_vector=False, # Disable vector search - max_results=20, - ) - - # Should find multiple instances of error handling - tool_helper.assert_in_result("Unified Search Results", result) - assert "error" in result.lower() - - -if __name__ == "__main__": - # Run tests - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_search_quality.py b/pkg/hanzo-mcp/tests/test_search_quality.py deleted file mode 100644 index 348765dee..000000000 --- a/pkg/hanzo-mcp/tests/test_search_quality.py +++ /dev/null @@ -1,661 +0,0 @@ -"""Test search result quality and relevance scoring for search.""" - -import asyncio -import sys -import tempfile -from pathlib import Path - -import pytest - -from tests.test_utils import create_permission_manager - -sys.path.insert(0, str(Path(__file__).parent.parent)) - -from hanzo_mcp.tools.vector.ast_analyzer import ASTAnalyzer - - -class TestSearchQuality: - """Test suite for search result quality and relevance.""" - - @pytest.fixture - def test_codebase(self): - """Create a realistic test codebase.""" - with tempfile.TemporaryDirectory() as tmpdir: - test_dir = Path(tmpdir) - - # Create a realistic Python project - - # Main application file - main_py = test_dir / "main.py" - main_py.write_text(''' -"""Main application with error handling.""" - -import logging -from typing import List, Optional -from data_processor import DataProcessor - -logger = logging.getLogger(__name__) - -class ApplicationError(Exception): - """Custom application error.""" - pass - -def main(): - """Main function with comprehensive error handling.""" - try: - processor = DataProcessor() - data = ["item1", "item2", "item3"] - - results = processor.process_data(data) - logger.info(f"Processed {len(results)} items successfully") - - return results - except ApplicationError as e: - logger.error(f"Application error: {e}") - return [] - except Exception as e: - logger.critical(f"Unexpected error in main: {e}") - raise - -def validate_input(data: List[str]) -> bool: - """Validate input data with error handling.""" - try: - return all(isinstance(item, str) and item.strip() for item in data) - except (TypeError, AttributeError): - return False - -if __name__ == "__main__": - main() -''') - - # Data processor module - processor_py = test_dir / "data_processor.py" - processor_py.write_text(''' -"""Data processing module with error handling.""" - -import logging -from typing import List, Dict, Any, Optional - -logger = logging.getLogger(__name__) - -class DataProcessor: - """Process data with comprehensive error handling.""" - - def __init__(self, config: Optional[Dict[str, Any]] = None): - """Initialize processor with configuration.""" - self.config = config or {} - logger.info("DataProcessor initialized") - - def process_data(self, items: List[str]) -> List[str]: - """Process a list of items with error handling.""" - processed = [] - - for item in items: - try: - result = self._process_single_item(item) - processed.append(result) - except ValueError as e: - logger.error(f"Error processing item {item}: {e}") - continue - except Exception as e: - logger.critical(f"Unexpected processing error: {e}") - raise - - return processed - - def _process_single_item(self, item: str) -> str: - """Process a single item with validation.""" - if not item or not item.strip(): - raise ValueError("Empty or whitespace-only item") - - # Transform the item - return item.upper().strip() - - def batch_process(self, data_batches: List[List[str]]) -> Dict[str, List[str]]: - """Process multiple batches of data.""" - results = {} - - for i, batch in enumerate(data_batches): - batch_key = f"batch_{i}" - try: - batch_results = self.process_data(batch) - results[batch_key] = batch_results - except Exception as e: - logger.error(f"Error processing {batch_key}: {e}") - results[batch_key] = [] - - return results -''') - - # Utility module - utils_py = test_dir / "utils.py" - utils_py.write_text(''' -"""Utility functions with error handling.""" - -import re -from typing import Union, Optional, List - -def validate_email(email: str) -> bool: - """Validate email address with error handling.""" - try: - pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$' - return bool(re.match(pattern, email)) - except (TypeError, re.error): - return False - -def safe_divide(a: Union[int, float], b: Union[int, float]) -> Optional[float]: - """Safely divide two numbers with error handling.""" - try: - if b == 0: - raise ValueError("Division by zero") - return float(a) / float(b) - except (TypeError, ValueError) as e: - print(f"Division error: {e}") - return None - -def format_error_message(error: Exception, context: str = "") -> str: - """Format error messages consistently.""" - error_type = type(error).__name__ - error_msg = str(error) - - if context: - return f"[{context}] {error_type}: {error_msg}" - else: - return f"{error_type}: {error_msg}" - -def extract_function_names(code: str) -> List[str]: - """Extract function names from Python code.""" - try: - pattern = r'def\\s+([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(' - matches = re.findall(pattern, code) - return matches - except re.error: - return [] - -class ErrorHandler: - """Centralized error handling class.""" - - def __init__(self, log_errors: bool = True): - self.log_errors = log_errors - self.error_count = 0 - - def handle_error(self, error: Exception, context: str = "") -> str: - """Handle an error and return formatted message.""" - self.error_count += 1 - message = format_error_message(error, context) - - if self.log_errors: - print(f"Error #{self.error_count}: {message}") - - return message -''') - - # Test file - test_py = test_dir / "test_main.py" - test_py.write_text(''' -"""Test file for the main application.""" - -import unittest -from unittest.mock import patch, MagicMock -from main import main, validate_input, ApplicationError -from data_processor import DataProcessor - -class TestMain(unittest.TestCase): - """Test cases for main application.""" - - def test_main_success(self): - """Test successful main execution.""" - with patch('main.DataProcessor') as mock_processor: - mock_instance = MagicMock() - mock_instance.process_data.return_value = ["ITEM1", "ITEM2", "ITEM3"] - mock_processor.return_value = mock_instance - - result = main() - self.assertEqual(result, ["ITEM1", "ITEM2", "ITEM3"]) - - def test_main_application_error(self): - """Test main with application error.""" - with patch('main.DataProcessor') as mock_processor: - mock_instance = MagicMock() - mock_instance.process_data.side_effect = ApplicationError("Test error") - mock_processor.return_value = mock_instance - - result = main() - self.assertEqual(result, []) - - def test_validate_input_valid(self): - """Test input validation with valid data.""" - data = ["item1", "item2", "item3"] - self.assertTrue(validate_input(data)) - - def test_validate_input_invalid(self): - """Test input validation with invalid data.""" - self.assertFalse(validate_input(["", "item2"])) - self.assertFalse(validate_input([None, "item2"])) - self.assertFalse(validate_input("not a list")) - -class TestDataProcessor(unittest.TestCase): - """Test cases for data processor.""" - - def setUp(self): - """Set up test processor.""" - self.processor = DataProcessor() - - def test_process_data_success(self): - """Test successful data processing.""" - data = ["item1", "item2", "item3"] - result = self.processor.process_data(data) - expected = ["ITEM1", "ITEM2", "ITEM3"] - self.assertEqual(result, expected) - - def test_process_data_with_empty_items(self): - """Test processing with empty items.""" - data = ["item1", "", "item3"] - result = self.processor.process_data(data) - # Empty item should be skipped - expected = ["ITEM1", "ITEM3"] - self.assertEqual(result, expected) - -if __name__ == "__main__": - unittest.main() -''') - - yield { - "dir": test_dir, - "main": main_py, - "processor": processor_py, - "utils": utils_py, - "test": test_py, - } - - def test_search_relevance_scoring(self, tool_helper, test_codebase): - """Test that search results are properly scored for relevance.""" - - # Test different search scenarios and expected relevance order - test_cases = [ - { - "query": "error handling", - "expected_files": ["main.py", "data_processor.py", "utils.py"], - "description": "Natural language query should find relevant files", - }, - { - "query": "DataProcessor", - "expected_files": ["data_processor.py", "main.py", "test_main.py"], - "description": "Class name should prioritize definition file", - }, - { - "query": "def process_data", - "expected_files": ["data_processor.py"], - "description": "Function definition should find exact matches", - }, - { - "query": "ApplicationError", - "expected_files": ["main.py", "test_main.py"], - "description": "Custom exception should find definition and usage", - }, - ] - - permission_manager = create_permission_manager([str(test_codebase["dir"])]) - - # Test with different search types - search_tools = { - "grep": lambda: self._test_grep_relevance( - test_codebase, test_cases, permission_manager - ), - "ast": lambda: self._test_ast_relevance( - test_codebase, test_cases, permission_manager - ), - "symbol": lambda: self._test_symbol_relevance( - test_codebase, test_cases, permission_manager - ), - } - - results = {} - for tool_name, test_func in search_tools.items(): - print(f"\\n=== Testing {tool_name.upper()} Search Relevance ===") - results[tool_name] = test_func() - - return results - - def _test_grep_relevance(self, test_codebase, test_cases, permission_manager): - """Test grep search relevance.""" - from hanzo_tools.filesystem.grep import Grep - - grep_tool = Grep(permission_manager) - results = {} - - class MockContext: - def __init__(self): - self.meta = {} - - for case in test_cases: - query = case["query"] - expected_files = case["expected_files"] - - try: - # Use asyncio.run for individual calls - result = asyncio.run( - grep_tool.call( - MockContext(), - pattern=query, - path=str(test_codebase["dir"]), - include="*.py", - ) - ) - - # Extract found files - found_files = set() - if "Found" in result: - lines = result.split("\\n") - for line in lines: - if ":" in line and line.strip(): - try: - file_path = line.split(":")[0] - if file_path: - found_files.add(Path(file_path).name) - except Exception: - continue - - # Calculate relevance score - expected_set = set(expected_files) - precision = ( - len(found_files & expected_set) / len(found_files) - if found_files - else 0 - ) - recall = ( - len(found_files & expected_set) / len(expected_set) - if expected_set - else 0 - ) - f1_score = ( - 2 * (precision * recall) / (precision + recall) - if (precision + recall) > 0 - else 0 - ) - - results[query] = { - "found_files": list(found_files), - "expected_files": expected_files, - "precision": precision, - "recall": recall, - "f1_score": f1_score, - "success": True, - } - - print(f"Query: '{query}'") - print(f" Found: {sorted(found_files)}") - print(f" Expected: {expected_files}") - print(f" F1 Score: {f1_score:.3f}") - - except Exception as e: - results[query] = {"success": False, "error": str(e)} - print(f"Query: '{query}' - FAILED: {e}") - - return results - - def _test_ast_relevance(self, test_codebase, test_cases, permission_manager): - """Test AST search relevance.""" - from hanzo_tools.filesystem.symbols import SymbolsTool - - ast_tool = SymbolsTool(permission_manager) - results = {} - - class MockContext: - def __init__(self): - self.meta = {} - - for case in test_cases: - query = case["query"] - expected_files = case["expected_files"] - - try: - result = asyncio.run( - ast_tool.call( - MockContext(), - pattern=query, - path=str(test_codebase["dir"]), - ignore_case=False, - line_number=True, - ) - ) - - # Extract found files from AST results - found_files = set() - if result and not result.startswith("No matches"): - lines = result.split("\\n") - for line in lines: - if line.endswith(":") and "/" in line: - file_path = line[:-1] - found_files.add(Path(file_path).name) - - # Calculate relevance metrics - expected_set = set(expected_files) - precision = ( - len(found_files & expected_set) / len(found_files) - if found_files - else 0 - ) - recall = ( - len(found_files & expected_set) / len(expected_set) - if expected_set - else 0 - ) - f1_score = ( - 2 * (precision * recall) / (precision + recall) - if (precision + recall) > 0 - else 0 - ) - - results[query] = { - "found_files": list(found_files), - "expected_files": expected_files, - "precision": precision, - "recall": recall, - "f1_score": f1_score, - "success": True, - } - - print(f"Query: '{query}'") - print(f" Found: {sorted(found_files)}") - print(f" Expected: {expected_files}") - print(f" F1 Score: {f1_score:.3f}") - - except Exception as e: - results[query] = {"success": False, "error": str(e)} - print(f"Query: '{query}' - FAILED: {e}") - - return results - - def _test_symbol_relevance(self, test_codebase, test_cases, permission_manager): - """Test symbol search relevance.""" - analyzer = ASTAnalyzer() - results = {} - - # Analyze all files first - file_symbols = {} - for file_path in test_codebase["dir"].rglob("*.py"): - try: - file_ast = analyzer.analyze_file(str(file_path)) - if file_ast: - file_symbols[file_path.name] = file_ast.symbols - except Exception as e: - print(f"Failed to analyze {file_path}: {e}") - - for case in test_cases: - query = case["query"] - expected_files = case["expected_files"] - - try: - # Search for symbols matching the query - found_files = set() - - for file_name, symbols in file_symbols.items(): - for symbol in symbols: - if query.lower() in symbol.name.lower(): - found_files.add(file_name) - break - - # Calculate relevance metrics - expected_set = set(expected_files) - precision = ( - len(found_files & expected_set) / len(found_files) - if found_files - else 0 - ) - recall = ( - len(found_files & expected_set) / len(expected_set) - if expected_set - else 0 - ) - f1_score = ( - 2 * (precision * recall) / (precision + recall) - if (precision + recall) > 0 - else 0 - ) - - results[query] = { - "found_files": list(found_files), - "expected_files": expected_files, - "precision": precision, - "recall": recall, - "f1_score": f1_score, - "success": True, - } - - print(f"Query: '{query}'") - print(f" Found: {sorted(found_files)}") - print(f" Expected: {expected_files}") - print(f" F1 Score: {f1_score:.3f}") - - except Exception as e: - results[query] = {"success": False, "error": str(e)} - print(f"Query: '{query}' - FAILED: {e}") - - return results - - def test_search_performance_comparison(self, tool_helper, test_codebase): - """Compare performance across different search methods.""" - import time - - permission_manager = create_permission_manager([str(test_codebase["dir"])]) - - queries = ["error", "DataProcessor", "def.*process", "import.*typing"] - - from hanzo_tools.filesystem.grep import Grep - from hanzo_tools.filesystem.symbols import SymbolsTool - - grep_tool = Grep(permission_manager) - ast_tool = SymbolsTool(permission_manager) - - class MockContext: - def __init__(self): - self.meta = {} - - performance_results = {} - - for query in queries: - query_results = {} - - # Test grep performance - start_time = time.time() - try: - result = asyncio.run( - grep_tool.call( - MockContext(), - pattern=query, - path=str(test_codebase["dir"]), - include="*.py", - ) - ) - grep_time = time.time() - start_time - grep_matches = ( - result.count("\\n") if result and "Found" in result else 0 - ) - - query_results["grep"] = { - "time": grep_time, - "matches": grep_matches, - "success": True, - } - except Exception as e: - query_results["grep"] = { - "time": time.time() - start_time, - "matches": 0, - "success": False, - "error": str(e), - } - - # Test AST performance - start_time = time.time() - try: - result = asyncio.run( - ast_tool.call( - MockContext(), - pattern=query, - path=str(test_codebase["dir"]), - ignore_case=False, - line_number=True, - ) - ) - ast_time = time.time() - start_time - ast_matches = ( - result.count("\\n") - if result and not result.startswith("No matches") - else 0 - ) - - query_results["ast"] = { - "time": ast_time, - "matches": ast_matches, - "success": True, - } - except Exception as e: - query_results["ast"] = { - "time": time.time() - start_time, - "matches": 0, - "success": False, - "error": str(e), - } - - performance_results[query] = query_results - - # Print performance comparison - print("\\n=== Performance Comparison ===") - print(f"{'Query':<20} {'Grep Time':<12} {'AST Time':<12} {'Speedup':<10}") - print("-" * 60) - - for query, results in performance_results.items(): - grep_time = results["grep"]["time"] - ast_time = results["ast"]["time"] - speedup = ast_time / grep_time if grep_time > 0 else float("inf") - - print(f"{query:<20} {grep_time:<12.3f} {ast_time:<12.3f} {speedup:<10.1f}x") - - return performance_results - - -if __name__ == "__main__": - # Run the search quality tests - tester = TestSearchQuality() - - # Create test codebase - test_codebase_gen = tester.create_test_codebase() - test_codebase = next(test_codebase_gen) - - try: - print("๐ŸŽฏ Search Quality and Relevance Testing") - print("=" * 50) - - # Test search relevance - relevance_results = tester.test_search_relevance_scoring(test_codebase) - - # Test performance comparison - print("\\n" + "=" * 50) - performance_results = tester.test_search_performance_comparison(test_codebase) - - print("\\nโœ… Search quality testing completed!") - - finally: - # Cleanup handled by context manager - pass diff --git a/pkg/hanzo-mcp/tests/test_search_tool.py b/pkg/hanzo-mcp/tests/test_search_tool.py deleted file mode 100644 index a5e84ba2d..000000000 --- a/pkg/hanzo-mcp/tests/test_search_tool.py +++ /dev/null @@ -1,221 +0,0 @@ -"""Test search tool functionality.""" - -import asyncio -import os -import tempfile -from pathlib import Path - -import pytest -from hanzo_mcp.tools.search import create_find_tool, create_search_tool - - -@pytest.mark.asyncio -async def test_search_basic(): - """Test basic search functionality.""" - # Create search tool - search_tool = create_search_tool() - - # Test with current directory - result = await search_tool.run( - pattern="SearchTool", path=".", max_results_per_type=5 - ) - - assert result.data is not None - assert "results" in result.data - assert "statistics" in result.data - - # Should find the class definition - results = result.data["results"] - assert len(results) > 0 - - # Check statistics - stats = result.data["statistics"] - assert "query" in stats - assert stats["query"] == "SearchTool" - assert "search_types_used" in stats - assert "text" in stats["search_types_used"] # Should use text search - - -@pytest.mark.asyncio -async def test_search_auto_detection(): - """Test automatic search type detection.""" - search_tool = create_search_tool() - - # Test natural language query (should trigger vector search if available) - result = await search_tool.run( - pattern="how does search work in this codebase", max_results_per_type=5 - ) - - assert result.data is not None - stats = result.data["statistics"] - - # Should detect this as a natural language query - if "vector" in stats["search_types_used"]: - # Vector search was used - assert True - else: - # At minimum, text search should be used - assert "text" in stats["search_types_used"] - - -@pytest.mark.asyncio -async def test_search_code_patterns(): - """Test code pattern search.""" - search_tool = create_search_tool() - - # Test AST pattern detection - result = await search_tool.run(pattern="class SearchResult", max_results_per_type=5) - - assert result.data is not None - stats = result.data["statistics"] - - # Should use AST search for class patterns - if "ast" in stats["search_types_used"]: - assert True - else: - # At minimum text search - assert "text" in stats["search_types_used"] - - -@pytest.mark.asyncio -async def test_search_with_files(): - """Test file search integration.""" - search_tool = create_search_tool() - - # Test file search - result = await search_tool.run( - pattern="*.py", search_files=True, max_results_per_type=10 - ) - - assert result.data is not None - stats = result.data["statistics"] - - # Should include file search - assert "files" in stats["search_types_used"] - - # Check for file results - results = result.data["results"] - file_results = [r for r in results if r["type"] == "file"] - assert len(file_results) > 0 - - -@pytest.mark.asyncio -async def test_find_tool_basic(): - """Test basic find tool functionality.""" - find_tool = create_find_tool() - - # Find Python files - result = await find_tool.run(pattern="*.py", path=".", max_results=10) - - assert result.data is not None - assert "results" in result.data - assert "statistics" in result.data - - # Should find Python files - results = result.data["results"] - assert len(results) > 0 - assert all(r["extension"] == ".py" for r in results) - - -@pytest.mark.asyncio -async def test_find_tool_with_filters(): - """Test find tool with filters.""" - find_tool = create_find_tool() - - # Create test directory structure - with tempfile.TemporaryDirectory() as tmpdir: - # Create some test files - (Path(tmpdir) / "small.txt").write_text("small file") - (Path(tmpdir) / "large.txt").write_text("x" * 10000) # 10KB - (Path(tmpdir) / "test_file.py").write_text("print('test')") - (Path(tmpdir) / "old_file.txt").write_text("old") - - # Make old_file older - old_time = os.path.getmtime(Path(tmpdir) / "old_file.txt") - 86400 # 1 day ago - os.utime(Path(tmpdir) / "old_file.txt", (old_time, old_time)) - - # Test size filter - result = await find_tool.run(pattern="*.txt", path=tmpdir, min_size="5KB") - - assert result.data is not None - results = result.data["results"] - assert len(results) == 1 - assert results[0]["name"] == "large.txt" - - # Test time filter - result = await find_tool.run( - pattern="*.txt", path=tmpdir, modified_after="12 hours ago" - ) - - results = result.data["results"] - assert len(results) == 2 # small.txt and large.txt - assert "old_file.txt" not in [r["name"] for r in results] - - -@pytest.mark.asyncio -async def test_find_tool_fuzzy_search(): - """Test fuzzy file name matching.""" - find_tool = create_find_tool() - - # Test fuzzy matching - result = await find_tool.run( - pattern="srchtl", # Misspelled "search_tool" - fuzzy=True, - max_results=5, - ) - - assert result.data is not None - # Fuzzy search might find "search_tool.py" - # But results depend on actual files in directory - - -@pytest.mark.asyncio -async def test_search_pagination(): - """Test pagination in search.""" - search_tool = create_search_tool() - - # First page - result1 = await search_tool.run(pattern="def", page_size=5, page=1) - - assert result1.data is not None - assert "pagination" in result1.data - - pagination = result1.data["pagination"] - assert pagination["page"] == 1 - assert pagination["page_size"] == 5 - - if pagination["has_next"]: - # Get second page - result2 = await search_tool.run(pattern="def", page_size=5, page=2) - - assert result2.data is not None - assert result2.data["pagination"]["page"] == 2 - - # Results should be different - results1 = result1.data["results"] - results2 = result2.data["results"] - - # Check that we got different results - files1 = set(r["file"] for r in results1) - files2 = set(r["file"] for r in results2) - - # Some overlap is OK but shouldn't be identical - assert files1 != files2 or len(files1) == 0 or len(files2) == 0 - - -@pytest.mark.asyncio -async def test_search_no_vector(): - """Test that search tool is lightweight without vector/ML dependencies.""" - # Vector search has been removed - tool should be fast and lightweight - search_tool = create_search_tool() - # No vector-related attributes should exist - assert not hasattr(search_tool, "_enable_vector_index") - assert not hasattr(search_tool, "embedder") - assert not hasattr(search_tool, "vector_db") - - -if __name__ == "__main__": - # Run tests - asyncio.run(test_search_basic()) - asyncio.run(test_find_tool_basic()) - print("Basic tests passed!") diff --git a/pkg/hanzo-mcp/tests/test_shell/__init__.py b/pkg/hanzo-mcp/tests/test_shell/__init__.py deleted file mode 100644 index ff4677655..000000000 --- a/pkg/hanzo-mcp/tests/test_shell/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Shell command test package.""" diff --git a/pkg/hanzo-mcp/tests/test_shell/test_command_executor.py b/pkg/hanzo-mcp/tests/test_shell/test_command_executor.py deleted file mode 100644 index 597b5173e..000000000 --- a/pkg/hanzo-mcp/tests/test_shell/test_command_executor.py +++ /dev/null @@ -1,304 +0,0 @@ -"""Tests for the command executor module.""" - -import os -from typing import TYPE_CHECKING -from unittest.mock import AsyncMock, patch - -import pytest - -if TYPE_CHECKING: - from hanzo_mcp.tools.common.permissions import PermissionManager - - -from hanzo_tools.shell.command_executor import CommandExecutor, CommandResult - - -class TestCommandResult: - """Test the CommandResult class.""" - - def test_initialization(self) -> None: - """Test initializing a CommandResult.""" - result = CommandResult( - return_code=0, - stdout="Standard output", - stderr="Standard error", - error_message=None, - ) - - assert result.return_code == 0 - assert result.stdout == "Standard output" - assert result.stderr == "Standard error" - assert result.error_message is None - - def test_is_success(self) -> None: - """Test the is_success property.""" - # Success case - success = CommandResult(return_code=0) - assert success.is_success - - # Failure case - failure = CommandResult(return_code=1) - assert not failure.is_success - - def test_format_output_success(self) -> None: - """Test formatting output for successful commands.""" - result = CommandResult(return_code=0, stdout="Command output", stderr="") - - formatted = result.format_output() - assert "Exit code: 0" in formatted - assert "Command output" in formatted - - def test_format_output_failure(self) -> None: - """Test formatting output for failed commands.""" - result = CommandResult( - return_code=1, - stdout="Command output", - stderr="Error message", - error_message="Execution failed", - ) - - formatted = result.format_output() - assert "Error: Execution failed" in formatted - assert "Command output" in formatted - assert "Error message" in formatted - - def test_format_output_without_exit_code(self) -> None: - """Test formatting output without including exit code.""" - result = CommandResult(return_code=0, stdout="Command output", stderr="") - - formatted = result.format_output(include_exit_code=False) - assert "Exit code: 0" not in formatted - assert "Command output" in formatted - - -class TestCommandExecutor: - """Test the CommandExecutor class.""" - - @pytest.fixture - def executor(self, permission_manager: "PermissionManager") -> CommandExecutor: - """Create a CommandExecutor instance for testing.""" - return CommandExecutor(permission_manager) - - def test_initialization( - self, tool_helper, permission_manager: "PermissionManager" - ) -> None: - """Test initializing CommandExecutor.""" - executor = CommandExecutor(permission_manager) - - assert executor.permission_manager is permission_manager - assert not executor.verbose - assert isinstance(executor.excluded_commands, list) - - def test_deny_command(self, tool_helper, executor: CommandExecutor) -> None: - """Test denying a command.""" - # Add a new command to denied list - executor.deny_command("custom_command") - - # Verify command is excluded - assert "custom_command" in executor.excluded_commands - - def test_is_command_allowed(self, tool_helper, executor: CommandExecutor) -> None: - """Test checking if a command is allowed.""" - # Allowed command - assert executor.is_command_allowed("echo Hello") - - # Excluded base command - assert not executor.is_command_allowed("rm -rf /") - - # Command with excluded pattern - assert executor.is_command_allowed("ls | grep test") - - # Empty command - assert not executor.is_command_allowed("") - - @pytest.mark.asyncio - async def test_execute_command_allowed( - self, executor: CommandExecutor, temp_dir: str - ) -> None: - """Test executing an allowed command.""" - # Create a test file - test_file = os.path.join(temp_dir, "test_exec.txt") - with open(test_file, "w") as f: - f.write("test content") - - # Execute a command - result: CommandResult = await executor.execute_command( - f"cat {test_file}", cwd=temp_dir - ) - - # Verify result - assert result.is_success - assert "test content" in result.stdout - assert result.stderr == "" - - @pytest.mark.asyncio - async def test_execute_command_not_allowed( - self, tool_helper, executor: CommandExecutor - ) -> None: - """Test executing a command that is not allowed.""" - # Try an excluded command - result = await executor.execute_command("rm test.txt") - - # Verify result - assert not result.is_success - assert "Command not allowed" in result.error_message - - @pytest.mark.asyncio - async def test_execute_command_with_invalid_cwd( - self, executor: CommandExecutor - ) -> None: - """Test executing a command with an invalid working directory.""" - # Try with non-existent directory - result = await executor.execute_command("ls", cwd="/nonexistent/dir") - - # Verify result - assert not result.is_success - assert "Working directory does not exist" in result.error_message - - @pytest.mark.asyncio - async def test_execute_command_with_timeout( - self, executor: CommandExecutor - ) -> None: - """Test command execution with timeout.""" - # Execute a command that sleeps - result = await executor.execute_command("sleep 5", timeout=0.1) - - # Verify result - assert not result.is_success - assert "Command timed out" in result.error_message - - @pytest.mark.asyncio - async def test_execute_script( - self, executor: CommandExecutor, temp_dir: str - ) -> None: - """Test executing a script.""" - # Mock the _execute_script_with_stdin method - with patch.object(executor, "_execute_script_with_stdin") as mock_execute: - mock_execute.return_value = CommandResult(0, "Script output", "") - - # Execute script - script = "echo 'test'" - result = await executor.execute_script(script, "bash", cwd=temp_dir) - - # Verify some method was called to execute the script - mock_execute.assert_called_once() - - # Verify result - assert result.is_success - assert "Script output" in result.stdout - - @pytest.mark.asyncio - async def test_handle_fish_script( - self, executor: CommandExecutor, temp_dir: str - ) -> None: - """Test special handling for Fish shell scripts.""" - # Patch asyncio.create_subprocess_shell - with patch("asyncio.create_subprocess_shell") as mock_subprocess: - # Setup mock process - mock_process = AsyncMock() - mock_process.returncode = 0 - mock_process.communicate = AsyncMock(return_value=(b"Fish output", b"")) - mock_subprocess.return_value = mock_process - - # Execute Fish script - script = "echo 'test'" - result = await executor._handle_fish_script("fish", script, temp_dir) - - # Verify subprocess was called - mock_subprocess.assert_called_once() - assert "fish" in mock_subprocess.call_args[0][0] - - # Verify result - assert result.is_success - assert "Fish output" in result.stdout - - @pytest.mark.asyncio - async def test_execute_script_from_file( - self, executor: CommandExecutor, temp_dir: str - ) -> None: - """Test executing a script from a temporary file.""" - # Patch asyncio.create_subprocess_exec - with patch("asyncio.create_subprocess_shell") as mock_subprocess: - # Setup mock process - mock_process = AsyncMock() - mock_process.returncode = 0 - mock_process.communicate = AsyncMock(return_value=(b"Python output", b"")) - mock_subprocess.return_value = mock_process - - # Execute Python script - script = "print('Hello, world!')" - result = await executor.execute_script_from_file( - script=script, language="python", cwd=temp_dir - ) - - # Verify subprocess was called with python - mock_subprocess.assert_called_once() - assert "python" in mock_subprocess.call_args[0][0] - - # Verify result - assert result.is_success - assert "Python output" in result.stdout - - def test_get_available_languages( - self, tool_helper, executor: CommandExecutor - ) -> None: - """Test getting available script languages.""" - languages = executor.get_available_languages() - - assert isinstance(languages, list) - assert "python" in languages - assert "javascript" in languages - assert "bash" in languages - - @pytest.mark.asyncio - async def test_execute_command_with_cd( - self, executor: CommandExecutor, temp_dir: str - ) -> None: - """Test executing a command that combines cd with another command.""" - # Create a test file in the temp directory - test_file = os.path.join(temp_dir, "test_exec.txt") - with open(test_file, "w") as f: - f.write("test content") - - # The command string that combines cd and cat - combined_command = f"cd {temp_dir} && cat test_exec.txt" - - # Execute the command - result: CommandResult = await executor.execute_command(combined_command) - - # Verify result - assert result.is_success - assert "test content" in result.stdout - assert result.stderr == "" - - # Test with a non-existent directory - bad_command = "cd /nonexistent/dir && ls" - result = await executor.execute_command(bad_command) - - # Command should fail because of the cd to non-existent directory - assert not result.is_success - assert result.return_code != 0 # Specific error code depends on the shell - - @pytest.mark.asyncio - async def test_execute_command_with_env_vars( - self, executor: CommandExecutor - ) -> None: - """Test executing a command with environment variables.""" - # Execute a command that echoes an environment variable - result: CommandResult = await executor.execute_command("echo $PATH") - - # Verify result - $PATH should be expanded - assert result.is_success - # PATH should contain directories separated by colons - assert ":" in result.stdout - # The output should not just be the literal string "$PATH" - assert result.stdout.strip() != "$PATH" - - # CommandExecutor no longer has register_tools method in new architecture - # Tools are now registered through register_shell_tools function - # @pytest.mark.asyncio - # async def test_register_tools(self, tool_helper, executor: CommandExecutor) -> None: - # """Test registering command execution tools.""" - # # This test is no longer applicable as tools are registered - # # through the hanzo_tools.shell.register_shell_tools function - # pass diff --git a/pkg/hanzo-mcp/tests/test_shell_features.py b/pkg/hanzo-mcp/tests/test_shell_features.py deleted file mode 100644 index 7b4e379f2..000000000 --- a/pkg/hanzo-mcp/tests/test_shell_features.py +++ /dev/null @@ -1,279 +0,0 @@ -"""Test shell features including auto-backgrounding and shell detection.""" - -import os -from pathlib import Path -from unittest.mock import MagicMock, patch - -import pytest -from hanzo_tools.shell.base_process import ProcessManager -from hanzo_tools.shell.ps_tool import PsTool -from hanzo_tools.shell.shell_detect import ( - SUPPORTED_SHELLS, - clear_shell_cache, - detect_shells, - get_active_shell, - get_shell_tool_class, -) -from hanzo_tools.shell.shell_tools import ( - BashTool, - DashTool, - FishTool, - ShellTool, - ZshTool, -) - -from hanzo_mcp.tools.common.permissions import PermissionManager - - -@pytest.fixture -def mock_ctx(): - """Create a mock MCP context.""" - ctx = MagicMock() - return ctx - - -@pytest.fixture -def permission_manager(): - """Create a permission manager.""" - pm = PermissionManager() - pm.add_allowed_path("/tmp") - pm.add_allowed_path(str(Path.home())) - return pm - - -@pytest.fixture -def bash_tool(permission_manager): - """Create a bash tool instance.""" - tool = BashTool() - tool.permission_manager = permission_manager - return tool - - -@pytest.fixture -def zsh_tool(permission_manager): - """Create a zsh tool instance.""" - tool = ZshTool() - tool.permission_manager = permission_manager - return tool - - -@pytest.fixture -def shell_tool(permission_manager): - """Create a shell tool instance (smart shell detection).""" - tool = ShellTool() - tool.permission_manager = permission_manager - return tool - - -@pytest.fixture -def ps_tool_instance(): - """Create a ps tool instance.""" - return PsTool() - - -class TestShellDetection: - """Test shell detection functionality.""" - - def test_detect_shells_returns_shell_info(self): - """Test detect_shells returns proper ShellInfo.""" - info = detect_shells() - assert hasattr(info, "login_shell") - assert hasattr(info, "invoking_shell") - assert hasattr(info, "env_shell") - assert hasattr(info, "evidence") - - def test_get_active_shell_returns_tuple(self): - """Test get_active_shell returns (name, path) tuple.""" - name, path = get_active_shell() - assert isinstance(name, str) - assert isinstance(path, str) - assert name in SUPPORTED_SHELLS or name == "sh" - - def test_supported_shells_contains_expected(self): - """Test SUPPORTED_SHELLS contains expected shells.""" - assert "zsh" in SUPPORTED_SHELLS - assert "bash" in SUPPORTED_SHELLS - assert "fish" in SUPPORTED_SHELLS - assert "dash" in SUPPORTED_SHELLS - - def test_get_shell_tool_class_returns_correct_class(self): - """Test get_shell_tool_class returns the right tool class.""" - assert get_shell_tool_class("zsh") == ZshTool - assert get_shell_tool_class("bash") == BashTool - assert get_shell_tool_class("fish") == FishTool - assert get_shell_tool_class("dash") == DashTool - assert get_shell_tool_class("unknown") is None - - def test_shell_env_override(self): - """Test HANZO_MCP_SHELL environment variable override.""" - clear_shell_cache() - with patch.dict(os.environ, {"HANZO_MCP_SHELL": "bash"}, clear=False): - name, path = get_active_shell() - assert name == "bash" - clear_shell_cache() - - def test_force_shell_path_override(self): - """Test HANZO_MCP_FORCE_SHELL environment variable override.""" - clear_shell_cache() - with patch.dict( - os.environ, {"HANZO_MCP_FORCE_SHELL": "/bin/bash"}, clear=False - ): - name, path = get_active_shell() - assert name == "bash" - assert path == "/bin/bash" - clear_shell_cache() - - def test_bash_tool_name(self, bash_tool): - """Test BashTool has correct name.""" - assert bash_tool.name == "bash" - - def test_zsh_tool_name(self, zsh_tool): - """Test ZshTool has correct name.""" - assert zsh_tool.name == "zsh" - - def test_shell_tool_name(self, shell_tool): - """Test ShellTool has correct name.""" - assert shell_tool.name == "shell" - - -class TestAutoBackgrounding: - """Test auto-backgrounding functionality.""" - - @pytest.mark.asyncio - async def test_quick_command_completes(self, bash_tool, mock_ctx): - """Test that quick commands complete normally.""" - result = await bash_tool.call(mock_ctx, command="echo 'Hello World'") - assert "Hello World" in result - assert "backgrounded" not in result.lower() - - @pytest.mark.asyncio - async def test_command_output(self, zsh_tool, mock_ctx): - """Test command output is captured.""" - result = await zsh_tool.call(mock_ctx, command="echo 'test output'") - assert "test output" in result - - -class TestProcessManagement: - """Test process management functionality.""" - - @pytest.fixture - def process_manager(self): - """Get process manager instance.""" - return ProcessManager() - - def test_process_tracking(self, process_manager): - """Test process tracking functionality.""" - # Create mock process (asyncio.subprocess.Process uses returncode, not poll()) - mock_process = MagicMock() - mock_process.pid = 12345 - mock_process.returncode = ( - None # Still running (asyncio.subprocess.Process style) - ) - - # Add process - process_manager.add_process("test_123", mock_process, "/tmp/test.log") - - # Check it's tracked - assert process_manager.get_process("test_123") == mock_process - - # List processes - processes = process_manager.list_processes() - assert "test_123" in processes - assert processes["test_123"]["pid"] == 12345 - assert processes["test_123"]["running"] is True - - @pytest.mark.asyncio - async def test_ps_list_command(self, ps_tool_instance, mock_ctx): - """Test ps list command.""" - result = await ps_tool_instance.call(mock_ctx) - assert isinstance(result, str) - - -class TestToolsListWithShellDetection: - """Test that TOOLS list respects shell detection.""" - - def test_default_tools_list_has_one_shell(self): - """Test default TOOLS list only includes detected shell.""" - clear_shell_cache() - # Re-import to get fresh TOOLS list - from hanzo_tools.shell import TOOLS, get_cached_active_shell - - shell_name, _ = get_cached_active_shell() - - # Count shell tools - shell_tools = [ - t for t in TOOLS if hasattr(t, "name") and t.name in SUPPORTED_SHELLS - ] - - # Should have exactly one shell tool (the detected one) - assert len(shell_tools) == 1 - assert shell_tools[0].name == shell_name - - def test_all_shells_mode(self): - """Test HANZO_MCP_ALL_SHELLS=1 exposes all shells.""" - clear_shell_cache() - with patch.dict(os.environ, {"HANZO_MCP_ALL_SHELLS": "1"}, clear=False): - # Re-import to get fresh TOOLS list - import importlib - - import hanzo_tools.shell - - importlib.reload(hanzo_tools.shell) - from hanzo_tools.shell import TOOLS - - # Count shell tools - shell_tools = [ - t for t in TOOLS if hasattr(t, "name") and t.name in SUPPORTED_SHELLS - ] - - # Should have all 4 shell tools - assert len(shell_tools) == 4 - shell_names = {t.name for t in shell_tools} - assert shell_names == SUPPORTED_SHELLS - - # Cleanup - clear_shell_cache() - if "HANZO_MCP_ALL_SHELLS" in os.environ: - del os.environ["HANZO_MCP_ALL_SHELLS"] - import importlib - - import hanzo_tools.shell - - importlib.reload(hanzo_tools.shell) - - -class TestIntegration: - """Integration tests for shell features.""" - - @pytest.mark.asyncio - async def test_bash_command_execution(self, bash_tool, mock_ctx): - """Test bash command execution.""" - result = await bash_tool.call(mock_ctx, command="echo 'integration test'") - assert "integration test" in result - - @pytest.mark.asyncio - async def test_zsh_command_execution(self, zsh_tool, mock_ctx): - """Test zsh command execution.""" - result = await zsh_tool.call(mock_ctx, command="echo 'zsh test'") - assert "zsh test" in result - - def test_tool_names_are_correct(self, bash_tool, zsh_tool, shell_tool): - """Test all shell tools have correct names.""" - assert bash_tool.name == "bash" - assert zsh_tool.name == "zsh" - assert shell_tool.name == "shell" - - -@pytest.mark.asyncio -async def test_end_to_end_workflow(): - """Test complete workflow with real commands.""" - pm = PermissionManager() - pm.add_allowed_path("/tmp") - - bash = BashTool() - bash.permission_manager = pm - - ps = PsTool() - - assert bash.name == "bash" - assert ps.name == "ps" diff --git a/pkg/hanzo-mcp/tests/test_shell_tools.py b/pkg/hanzo-mcp/tests/test_shell_tools.py deleted file mode 100644 index 6bcd6fe02..000000000 --- a/pkg/hanzo-mcp/tests/test_shell_tools.py +++ /dev/null @@ -1,254 +0,0 @@ -"""Test suite for shell tools including zsh and smart shell selection.""" - -import os -import platform -import shutil -import sys -from pathlib import Path -from unittest.mock import AsyncMock, patch - -import pytest - -# Add parent directory to path for imports -sys.path.insert(0, str(Path(__file__).parent.parent)) - -from hanzo_tools.shell.shell_tools import BashTool, ShellTool, ZshTool - - -class MockContext: - """Mock MCP context for testing.""" - - pass - - -class TestBashTool: - """Test bash tool functionality.""" - - def test_bash_tool_properties(self): - """Test bash tool basic properties.""" - tool = BashTool() - - assert tool.name == "bash" - assert tool.get_tool_name() == "bash" - assert "bash" in tool.description.lower() - - # On Unix-like systems, should always return bash - if platform.system() != "Windows": - assert tool.get_interpreter() == "bash" - - @pytest.mark.asyncio - async def test_bash_execution(self): - """Test bash command execution.""" - tool = BashTool() - ctx = MockContext() - - # Mock execute_sync to avoid actual command execution - with patch.object(tool, "execute_sync", new_callable=AsyncMock) as mock_exec: - mock_exec.return_value = "test output" - - result = await tool.run(ctx, "echo test") - - assert result == "test output" - mock_exec.assert_called_once() - - def test_bash_flags(self): - """Test bash interpreter flags.""" - tool = BashTool() - - if platform.system() == "Windows": - # On Windows, might be /c or -c depending on shell - flags = tool.get_script_flags() - assert flags in [["/c"], ["-c"]] - else: - assert tool.get_script_flags() == ["-c"] - - -class TestZshTool: - """Test zsh tool functionality.""" - - def test_zsh_tool_properties(self): - """Test zsh tool basic properties.""" - tool = ZshTool() - - assert tool.name == "zsh" - assert tool.get_tool_name() == "zsh" - assert "zsh" in tool.description.lower() - assert "enhanced features" in tool.description.lower() - - def test_zsh_interpreter_detection(self): - """Test zsh interpreter detection.""" - tool = ZshTool() - - if platform.system() != "Windows": - # Check if zsh is available - if shutil.which("zsh"): - interpreter = tool.get_interpreter() - assert "zsh" in interpreter or interpreter == "bash" - else: - # Should fall back to bash if zsh not found - assert tool.get_interpreter() == "bash" - - @pytest.mark.asyncio - async def test_zsh_execution(self): - """Test zsh command execution.""" - tool = ZshTool() - ctx = MockContext() - - # Mock execute_sync - with patch.object(tool, "execute_sync", new_callable=AsyncMock) as mock_exec: - mock_exec.return_value = "zsh output" - - result = await tool.run(ctx, "echo $ZSH_VERSION") - - assert result == "zsh output" - mock_exec.assert_called_once() - - @pytest.mark.asyncio - async def test_zsh_not_installed(self): - """Test error when zsh is not installed.""" - tool = ZshTool() - ctx = MockContext() - - # Mock shutil.which to return None (zsh not found) - with patch("shutil.which", return_value=None): - if platform.system() != "Windows": - result = await tool.run(ctx, "echo test") - assert "not installed" in result.lower() - - -class TestShellTool: - """Test smart shell tool functionality.""" - - def test_shell_tool_properties(self): - """Test shell tool basic properties.""" - tool = ShellTool() - - assert tool.name == "shell" - assert tool.get_tool_name() == "shell" - assert "best available shell" in tool.description.lower() - - def test_shell_detection(self): - """Test smart shell detection.""" - tool = ShellTool() - - # Should have detected a shell - assert tool._best_shell is not None - assert tool._best_shell in ["zsh", "bash"] or Path(tool._best_shell).exists() - - def test_shell_preference_order(self): - """Test shell preference order.""" - # Test with fresh instance each time - - # Mock different scenarios - with patch("shutil.which") as mock_which: - with patch.object(Path, "exists") as mock_exists: - # Scenario 1: zsh available with .zshrc - mock_which.return_value = "/usr/bin/zsh" - mock_exists.return_value = True - tool = ShellTool() - assert "zsh" in tool._best_shell - - # Scenario 2: zsh not available, use bash - mock_which.return_value = None - mock_exists.return_value = False - tool = ShellTool() - assert tool._best_shell == "bash" - - @pytest.mark.asyncio - async def test_shell_execution_with_info(self): - """Test shell execution with shell info.""" - tool = ShellTool() - ctx = MockContext() - - # Mock execute_sync - with patch.object(tool, "execute_sync", new_callable=AsyncMock) as mock_exec: - mock_exec.return_value = "" # Empty output - - result = await tool.run(ctx, "true") # Command with no output - - # Should mention which shell was used - assert "completed successfully" in result.lower() - if "zsh" in tool._best_shell: - assert "zsh" in result.lower() - else: - assert "bash" in result.lower() or "shell" in result.lower() - - def test_shell_description_dynamic(self): - """Test that shell description shows current shell.""" - tool = ShellTool() - - description = tool.description - shell_name = os.path.basename(tool._best_shell) - - # Description should mention the current shell - assert f"currently: {shell_name}" in description.lower() - - -class TestShellIntegration: - """Integration tests for shell tools.""" - - @pytest.mark.asyncio - @pytest.mark.integration - async def test_real_shell_execution(self): - """Test real shell execution (integration test).""" - # This test actually runs commands - skip in CI - if os.environ.get("CI"): - pytest.skip("Skipping real shell execution in CI") - - ctx = MockContext() - - # Test each tool - bash_tool = BashTool() - result = await bash_tool.run(ctx, "echo 'bash works'") - assert "bash works" in result - - # Test zsh if available - if shutil.which("zsh"): - zsh_tool = ZshTool() - result = await zsh_tool.run(ctx, "echo 'zsh works'") - assert "zsh works" in result - - # Test smart shell - shell_tool = ShellTool() - result = await shell_tool.run(ctx, "echo 'shell works'") - assert "shell works" in result or "completed successfully" in result - - @pytest.mark.asyncio - async def test_shell_tools_registration(self): - """Test that shell tools can be registered.""" - from hanzo_tools.shell import get_shell_tools - - from hanzo_mcp.tools.common.permissions import PermissionManager - - pm = PermissionManager() - tools = get_shell_tools(pm) - - # Should have our core tools - tool_names = [tool.name for tool in tools] - assert "cmd" in tool_names # Primary command execution - assert "zsh" in tool_names - assert "bash" in tool_names - - # cmd should be first (primary) - assert tools[0].name == "cmd" - - def test_shell_tool_ordering(self): - """Test that shell tools are returned in correct order.""" - from hanzo_tools.shell import get_shell_tools - - from hanzo_mcp.tools.common.permissions import PermissionManager - - pm = PermissionManager() - tools = get_shell_tools(pm) - - # Find shell-related tools - shell_tools = [t for t in tools if t.name in ["cmd", "zsh", "bash"]] - - # Order should be: cmd (primary), zsh, bash - assert shell_tools[0].name == "cmd" - assert shell_tools[1].name == "zsh" - assert shell_tools[2].name == "bash" - - -if __name__ == "__main__": - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_simple.py b/pkg/hanzo-mcp/tests/test_simple.py deleted file mode 100644 index 514d9d6d9..000000000 --- a/pkg/hanzo-mcp/tests/test_simple.py +++ /dev/null @@ -1,255 +0,0 @@ -#!/usr/bin/env python3 -"""Simple test script for core Hanzo AI functionality.""" - -import asyncio -import os -import tempfile -from pathlib import Path - - -def test_imports(): - """Test that core modules can be imported.""" - print("๐Ÿงช Testing Core Imports\n") - - try: - print("โœ… Permission manager imported") - - print("โœ… Read tool imported") - - print("โœ… Diff tool imported") - - print("โœ… Context normalization imported") - - print("โœ… Enhanced server imported") - - return True - - except Exception as e: - print(f"โŒ Import failed: {e}") - return False - - -async def test_file_operations(): - """Test file operations.""" - print("\n๐Ÿ“ Testing File Operations\n") - - try: - from hanzo_tools.filesystem.diff import create_diff_tool - from hanzo_tools.filesystem.read import ReadTool - - from hanzo_mcp.tools.common.permissions import PermissionManager - - # Create temp files - with tempfile.TemporaryDirectory() as temp_dir: - temp_path = Path(temp_dir) - - # Set up permission manager - pm = PermissionManager() - pm.add_allowed_path(temp_dir) - - # Create test files - file1 = temp_path / "test1.txt" - file2 = temp_path / "test2.txt" - - file1.write_text("Hello\nWorld\nFrom\nHanzo") - file2.write_text("Hello\nUniverse\nFrom\nHanzo\nMCP") - - print(f"๐Ÿ“ Created test files in: {temp_path}") - - # Test read tool - read_tool = ReadTool(pm) - - class MockContext: - pass - - ctx = MockContext() - - content = await read_tool.run(ctx, str(file1)) - print(f"โœ… Read tool: got {len(content)} characters") - - # Test diff tool - diff_tool = create_diff_tool(pm) - diff_result = await diff_tool.run(ctx, str(file1), str(file2)) - print("โœ… Diff tool: found differences") - - # Show summary line - lines = diff_result.split("\n") - summary_line = [line for line in lines if "Summary:" in line] - if summary_line: - print(f" {summary_line[0]}") - - return True - - except Exception as e: - print(f"โŒ File operations failed: {e}") - return False - - -def test_shell_detection(): - """Test shell detection.""" - print("\n๐Ÿš Testing Shell Detection\n") - - try: - # Test shell detection without creating the tool - import os - import platform - from pathlib import Path - - if platform.system() == "Windows": - expected_shell = "cmd.exe" - else: - shell = os.environ.get("SHELL", "/bin/bash") - shell_name = os.path.basename(shell) - - if shell_name == "zsh": - zshrc_path = Path.home() / ".zshrc" - if zshrc_path.exists(): - expected_shell = shell - print(f"โœ… Found .zshrc at: {zshrc_path}") - else: - expected_shell = "bash" - else: - expected_shell = "bash" - - print(f"โœ… Shell detection: {expected_shell}") - print(f"โœ… User's SHELL: {os.environ.get('SHELL', 'not set')}") - - return True - - except Exception as e: - print(f"โŒ Shell detection failed: {e}") - return False - - -def test_cloudflare_config(): - """Test Cloudflare configuration.""" - print("\nโ˜๏ธ Testing Cloudflare Configuration\n") - - # Check environment variables - cf_token = os.environ.get("CLOUDFLARE_API_TOKEN") - cf_account = os.environ.get("CLOUDFLARE_ACCOUNT_ID") - - if cf_token: - print(f"โœ… CLOUDFLARE_API_TOKEN configured (ends with: ...{cf_token[-8:]})") - else: - print("โš ๏ธ CLOUDFLARE_API_TOKEN not found in environment") - - if cf_account: - print(f"โœ… CLOUDFLARE_ACCOUNT_ID: {cf_account}") - else: - print("โš ๏ธ CLOUDFLARE_ACCOUNT_ID not found in environment") - - # Check Claude Desktop config - claude_config = ( - Path.home() - / "Library" - / "Application Support" - / "Claude" - / "claude_desktop_config.json" - ) - if claude_config.exists(): - print(f"โœ… Claude Desktop config found: {claude_config}") - try: - import json - - with open(claude_config) as f: - config = json.load(f) - - servers = config.get("mcpServers", {}) - print(f"โœ… MCP servers configured: {list(servers.keys())}") - - if "cloudflare-bindings" in servers: - print("โœ… Cloudflare Bindings server configured") - if "cloudflare-tunnels" in servers: - print("โœ… Cloudflare Tunnels server configured") - - except Exception as e: - print(f"โš ๏ธ Could not parse Claude config: {e}") - else: - print("โš ๏ธ Claude Desktop config not found") - - # Check tunnels repo - tunnels_repo = Path("tools/hanzoai-mcp-server-cloudflare") - if tunnels_repo.exists(): - print(f"โœ… Cloudflare MCP server repo found: {tunnels_repo}") - tunnels_app = tunnels_repo / "apps" / "cloudflare-tunnels" - if tunnels_app.exists(): - print("โœ… Cloudflare Tunnels app found") - else: - print("โŒ Cloudflare Tunnels app not found") - else: - print("โš ๏ธ Cloudflare MCP server repo not found locally") - - -def test_dev_mode(): - """Test development mode availability.""" - print("\n๐Ÿ”ง Testing Development Mode\n") - - try: - # Test watchdog import - import watchdog - - try: - version = watchdog.__version__ - print(f"โœ… Watchdog available: v{version}") - except AttributeError: - print("โœ… Watchdog available (version unknown)") - - # Test dev server availability - from hanzo_mcp.dev_server import DevServer - - print("โœ… DevServer class available") - - # Check if CLI supports --dev - from hanzo_mcp import cli - - print("โœ… CLI module available") - - return True - - except ImportError as e: - print(f"โŒ Development mode dependency missing: {e}") - return False - except Exception as e: - print(f"โŒ Development mode test failed: {e}") - return False - - -async def main(): - """Run all tests.""" - print("๐Ÿš€ Hanzo AI Simple Test Suite") - print("=" * 50) - - results = [] - - # Run tests - results.append(test_imports()) - results.append(await test_file_operations()) - results.append(test_shell_detection()) - results.append(test_dev_mode()) - - # Cloudflare config test (always runs) - test_cloudflare_config() - - # Summary - passed = sum(results) - total = len(results) - - print(f"\n๐Ÿ“Š Test Results: {passed}/{total} passed") - - if passed == total: - print("๐ŸŽ‰ All core tests passed!") - print("\n๐Ÿ”ง Next Steps:") - print("1. Restart Claude Desktop/Code to load new MCP servers") - print("2. Test Cloudflare tools: 'List my Cloudflare Tunnels'") - print("3. Test palette system: 'palette --action list'") - print("4. Test shell: 'bash \"echo $SHELL\"'") - print("5. Test dev mode: 'hanzo-mcp --dev --project-dir .'") - else: - print("โš ๏ธ Some tests failed. Check the output above.") - - return passed == total - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-mcp/tests/test_startup_performance.py b/pkg/hanzo-mcp/tests/test_startup_performance.py deleted file mode 100644 index c7c4e4bb6..000000000 --- a/pkg/hanzo-mcp/tests/test_startup_performance.py +++ /dev/null @@ -1,238 +0,0 @@ -#!/usr/bin/env python3 -"""Tests for MCP startup performance. - -These tests ensure that MCP server startup remains fast by checking -import times. Slow imports cause MCP connections to timeout and hang. - -IMPORTANT: If these tests fail, it means imports have become slow again. -The fix is to make heavy imports LAZY - only import when actually needed, -not at module load time. - -Common culprits for slow imports: -- sentence_transformers (2.5+ seconds) -- llm (1+ second) -- hanzo_memory embedding services (3+ seconds) -""" - -import subprocess -import sys -import time - -import pytest - -# Maximum allowed import times (in seconds) -# These are intentionally generous to avoid flaky tests -MAX_MODULE_IMPORT_TIME = 0.7 # hanzo_mcp.tools module (generous for CI variance) -MAX_TOTAL_IMPORT_TIME = 1.0 # All core imports combined -MAX_CLI_STARTUP_TIME = 3.0 # CLI --help should respond quickly - - -class TestImportPerformance: - """Test that imports are fast enough for MCP to work.""" - - def test_tools_module_import_is_fast(self): - """Test that hanzo_mcp.tools imports quickly. - - This is critical because slow imports cause MCP to hang. - The tools module must NOT import heavy dependencies at load time. - """ - code = """ -import time -start = time.time() -import hanzo_mcp.tools -elapsed = time.time() - start -print(f"ELAPSED:{elapsed:.3f}") -""" - result = subprocess.run( - [sys.executable, "-c", code], - capture_output=True, - text=True, - timeout=30, - ) - - assert result.returncode == 0, f"Import failed: {result.stderr}" - - # Extract elapsed time - for line in result.stdout.split("\n"): - if line.startswith("ELAPSED:"): - elapsed = float(line.split(":")[1]) - break - else: - pytest.fail(f"Could not find elapsed time in output: {result.stdout}") - - assert elapsed < MAX_MODULE_IMPORT_TIME, ( - f"hanzo_mcp.tools import took {elapsed:.2f}s (max: {MAX_MODULE_IMPORT_TIME}s). " - "This is too slow! Check for heavy imports at module load time. " - "Common culprits: sentence_transformers, llm, hanzo_memory" - ) - - def test_no_heavy_imports_at_module_level(self): - """Test that heavy packages are NOT imported at module load time.""" - code = """ -import sys -# Clear any cached modules -for mod in list(sys.modules.keys()): - if any(x in mod for x in ['hanzo', 'sentence', 'llm']): - del sys.modules[mod] - -# Import the tools module -import hanzo_mcp.tools - -# Check what got imported -heavy_modules = [] -for name in ['sentence_transformers', 'llm', 'hanzo_memory']: - if name in sys.modules: - heavy_modules.append(name) - -if heavy_modules: - print(f"FAIL:Heavy modules imported at load time: {heavy_modules}") - sys.exit(1) -else: - print("PASS:No heavy modules imported") -""" - result = subprocess.run( - [sys.executable, "-c", code], - capture_output=True, - text=True, - timeout=30, - ) - - if "FAIL:" in result.stdout: - pytest.fail(result.stdout.split("FAIL:")[1].strip()) - - assert "PASS:" in result.stdout, f"Unexpected output: {result.stdout}" - - def test_total_import_time(self): - """Test that all core imports combined are fast.""" - code = """ -import time -start = time.time() -import hanzo_mcp -import hanzo_mcp.tools -from hanzo_mcp.server import create_server -elapsed = time.time() - start -print(f"ELAPSED:{elapsed:.3f}") -""" - result = subprocess.run( - [sys.executable, "-c", code], - capture_output=True, - text=True, - timeout=30, - ) - - assert result.returncode == 0, f"Import failed: {result.stderr}" - - for line in result.stdout.split("\n"): - if line.startswith("ELAPSED:"): - elapsed = float(line.split(":")[1]) - break - else: - pytest.fail(f"Could not find elapsed time in output: {result.stdout}") - - assert elapsed < MAX_TOTAL_IMPORT_TIME, ( - f"Total imports took {elapsed:.2f}s (max: {MAX_TOTAL_IMPORT_TIME}s). " - "Imports are too slow! MCP connections will timeout." - ) - - -class TestCLIPerformance: - """Test that CLI starts quickly.""" - - def test_cli_help_is_fast(self): - """Test that --help responds quickly.""" - start = time.time() - result = subprocess.run( - [sys.executable, "-m", "hanzo_mcp", "--help"], - capture_output=True, - text=True, - timeout=30, - ) - elapsed = time.time() - start - - assert result.returncode == 0, f"CLI help failed: {result.stderr}" - assert elapsed < MAX_CLI_STARTUP_TIME, ( - f"CLI --help took {elapsed:.2f}s (max: {MAX_CLI_STARTUP_TIME}s). This is too slow for MCP connections." - ) - - def test_cli_does_not_hang(self): - """Test that CLI starts without hanging. - - This was the original issue - MCP tools would hang for 4+ hours - because imports were too slow. - """ - try: - result = subprocess.run( - [sys.executable, "-m", "hanzo_mcp", "--help"], - capture_output=True, - text=True, - timeout=10, # Should respond within 10 seconds max - ) - assert "hanzo" in result.stdout.lower() or "mcp" in result.stdout.lower() - except subprocess.TimeoutExpired: - pytest.fail( - "CLI hung for more than 10 seconds! This indicates slow imports causing MCP to hang." - ) - - -class TestLazyImportPattern: - """Test that lazy import patterns are correctly implemented.""" - - def test_memory_tools_use_lazy_import(self): - """Test that memory tools use lazy imports.""" - code = """ -import sys - -# Import the memory tools module -from hanzo_tools.memory import memory_tools - -# Check that hanzo_memory was NOT imported -if 'hanzo_memory' in sys.modules: - print("FAIL:hanzo_memory imported at module load") - sys.exit(1) -print("PASS:hanzo_memory not imported at module load") -""" - result = subprocess.run( - [sys.executable, "-c", code], - capture_output=True, - text=True, - timeout=30, - ) - - if "FAIL:" in result.stdout: - pytest.fail( - "memory_tools.py imports hanzo_memory at module load time. Use lazy imports with TYPE_CHECKING pattern." - ) - - def test_search_tool_no_heavy_deps(self): - """Test that search tool does not import heavy ML dependencies. - - Vector search with sentence_transformers has been removed to keep MCP lightweight. - If semantic search is needed, use external services (hanzo-node, hanzo desktop). - """ - code = """ -import sys - -# Import the search tool module -from hanzo_mcp.tools.search import search_tool - -# Check that sentence_transformers was NOT imported (should be removed entirely) -if 'sentence_transformers' in sys.modules: - print("FAIL:sentence_transformers imported - should be removed from search_tool") - sys.exit(1) -print("PASS:no heavy ML dependencies in search_tool") -""" - result = subprocess.run( - [sys.executable, "-c", code], - capture_output=True, - text=True, - timeout=30, - ) - - if "FAIL:" in result.stdout: - pytest.fail( - "search_tool.py imports sentence_transformers. Vector search has been removed - keep MCP lightweight." - ) - - -if __name__ == "__main__": - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_stdio_protocol.py b/pkg/hanzo-mcp/tests/test_stdio_protocol.py deleted file mode 100644 index 618bf3957..000000000 --- a/pkg/hanzo-mcp/tests/test_stdio_protocol.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env python -"""Final comprehensive test for stdio protocol integrity.""" - -import json -import select -import subprocess -import sys -import time - - -def test_stdio_protocol(): - """Test that stdio transport produces only valid JSON output.""" - print("๐Ÿงช Testing stdio protocol integrity...\n") - - # Start the server - proc = subprocess.Popen( - [sys.executable, "-m", "hanzo_mcp.cli", "--transport", "stdio"], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - bufsize=0, - ) - - # Test cases - test_cases = [ - # 1. Initialize - { - "name": "Initialize", - "request": { - "jsonrpc": "2.0", - "id": 1, - "method": "initialize", - "params": { - "protocolVersion": "0.1.0", - "capabilities": {}, - "clientInfo": {"name": "test-client", "version": "1.0.0"}, - }, - }, - }, - # 2. List tools (might trigger logging) - { - "name": "List tools", - "request": {"jsonrpc": "2.0", "id": 2, "method": "tools/list"}, - }, - # 3. Read file (successful) - { - "name": "Read file (success)", - "request": { - "jsonrpc": "2.0", - "id": 3, - "method": "tools/call", - "params": {"name": "read", "arguments": {"file_path": "/etc/hosts"}}, - }, - }, - # 4. Read file (error - should not break protocol) - { - "name": "Read file (error)", - "request": { - "jsonrpc": "2.0", - "id": 4, - "method": "tools/call", - "params": { - "name": "read", - "arguments": {"file_path": "/does/not/exist.txt"}, - }, - }, - }, - # 5. Execute command - { - "name": "Execute command", - "request": { - "jsonrpc": "2.0", - "id": 5, - "method": "tools/call", - "params": { - "name": "bash", - "arguments": {"command": "echo 'Test output'"}, - }, - }, - }, - ] - - # Track results - responses = [] - violations = [] - - # Run tests - for test in test_cases: - print(f"โ†’ Test: {test['name']}") - - # Send request - request_str = json.dumps(test["request"]) + "\n" - proc.stdin.write(request_str) - proc.stdin.flush() - - # Read response with timeout - start_time = time.time() - response_found = False - - while time.time() - start_time < 5: # 5 second timeout per test - # Check for stdout data - readable, _, _ = select.select([proc.stdout], [], [], 0.1) - if proc.stdout in readable: - line = proc.stdout.readline() - if line: - line = line.strip() - if line: - try: - msg = json.loads(line) - responses.append(msg) - response_found = True - print(" โœ“ Valid JSON response received") - break - except json.JSONDecodeError: - violations.append( - {"test": test["name"], "output": line[:200]} - ) - print(f" โŒ PROTOCOL VIOLATION: {line[:100]}") - - if not response_found: - print(" โฑ๏ธ Timeout - no response received") - - # Cleanup - proc.terminate() - try: - proc.wait(timeout=2) - except subprocess.TimeoutExpired: - proc.kill() - proc.wait() - - # Summary - print("\n" + "=" * 60) - print("๐Ÿ“Š TEST RESULTS:") - print(f"Total tests: {len(test_cases)}") - print(f"Valid JSON responses: {len(responses)}") - print(f"Protocol violations: {len(violations)}") - - if violations: - print("\nโŒ PROTOCOL VIOLATIONS DETECTED:") - for v in violations: - print(f" Test: {v['test']}") - print(f" Output: {v['output']}") - else: - print("\nโœ… All tests passed! No protocol violations detected.") - - # Also check stderr was silent - stderr_output = proc.stderr.read() - if stderr_output: - print("\nโš ๏ธ stderr output detected (should be empty for stdio):") - print(stderr_output[:500]) - - return len(violations) == 0 and not stderr_output - - -if __name__ == "__main__": - success = test_stdio_protocol() - sys.exit(0 if success else 1) diff --git a/pkg/hanzo-mcp/tests/test_stdio_simple.py b/pkg/hanzo-mcp/tests/test_stdio_simple.py deleted file mode 100644 index 19532d116..000000000 --- a/pkg/hanzo-mcp/tests/test_stdio_simple.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python -"""Simple test to check if stdio mode starts without logging interference.""" - -import json -import subprocess -import sys -import time - - -def test_stdio_simple(): - """Test stdio mode for logging interference.""" - # Start the server - proc = subprocess.Popen( - [sys.executable, "-m", "hanzo_mcp.cli", "--transport", "stdio"], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - bufsize=0, - ) - - # Send initialize request - request = { - "jsonrpc": "2.0", - "id": 1, - "method": "initialize", - "params": { - "protocolVersion": "0.1.0", - "capabilities": {}, - "clientInfo": {"name": "test-client", "version": "1.0.0"}, - }, - } - - print("Sending initialize request...") - proc.stdin.write(json.dumps(request) + "\n") - proc.stdin.flush() - - # Read response for 5 seconds - start_time = time.time() - output_lines = [] - error_lines = [] - - while time.time() - start_time < 5: - # Check for stdout - try: - proc.stdout.flush() - line = proc.stdout.readline() - if line: - output_lines.append(line.strip()) - except Exception: - pass - - # Check for stderr - try: - proc.stderr.flush() - line = proc.stderr.readline() - if line: - error_lines.append(line.strip()) - except Exception: - pass - - time.sleep(0.1) - - # Kill the process - proc.terminate() - proc.wait() - - # Analyze results - print("\n" + "=" * 60) - print("SUMMARY:") - print(f"Total stdout lines: {len(output_lines)}") - print(f"Total stderr lines: {len(error_lines)}") - - # Check each stdout line for valid JSON - violations = 0 - for i, line in enumerate(output_lines): - try: - data = json.loads(line) - print( - f"โœ“ Valid JSON response: {data.get('method', data.get('result', 'response'))}" - ) - except json.JSONDecodeError: - print(f"โœ— Line {i + 1} is not valid JSON: {line}") - violations += 1 - - # Show stderr if any - if error_lines: - print("\nSTDERR OUTPUT:") - for line in error_lines: - print(f" {line}") - - if violations == 0 and output_lines: - print("โœ… All stdout output is valid JSON!") - else: - print(f"โŒ Found {violations} protocol violations") - - return violations == 0 - - -if __name__ == "__main__": - success = test_stdio_simple() - sys.exit(0 if success else 1) diff --git a/pkg/hanzo-mcp/tests/test_streaming_command.py b/pkg/hanzo-mcp/tests/test_streaming_command.py deleted file mode 100644 index b3ae43a42..000000000 --- a/pkg/hanzo-mcp/tests/test_streaming_command.py +++ /dev/null @@ -1,297 +0,0 @@ -"""Test cases for the streaming command tool.""" - -import asyncio -import json -import tempfile -from pathlib import Path -from unittest.mock import patch - -import pytest -from hanzo_tools.shell.streaming_command import StreamingCommandTool - - -class TestStreamingCommandTool: - """Test cases for StreamingCommandTool.""" - - @pytest.fixture - async def tool(self): - """Create a test instance of StreamingCommandTool.""" - # Use a temporary directory for testing - with tempfile.TemporaryDirectory() as temp_dir: - with patch.object(StreamingCommandTool, "SESSION_BASE_DIR", Path(temp_dir)): - tool = StreamingCommandTool() - yield tool - - @pytest.mark.asyncio - async def test_execute_simple_command(self, tool_helper, tool): - """Test executing a simple command.""" - result = await tool.run(command="echo 'Hello, World!'") - - tool_helper.assert_in_result("command_id", result) - tool_helper.assert_in_result("short_id", result) - assert result["command"] == "echo 'Hello, World!'" - assert "Hello, World!" in result["output"] - assert result["status"] in ["running", "completed"] - - @pytest.mark.asyncio - async def test_command_aliases(self, tool_helper, tool): - """Test that command aliases work.""" - # Test 'cmd' alias - result1 = await tool.run(cmd="echo 'test1'") - assert "test1" in result1["output"] - - # Test 'cwd' alias - result2 = await tool.run(command="pwd", cwd="/tmp") - assert result2["status"] in ["running", "completed"] - - @pytest.mark.asyncio - async def test_continue_reading(self, tool_helper, tool): - """Test continuing to read from a command.""" - # Generate some output - result1 = await tool.run( - command="for i in {1..100}; do echo Line $i; done", - chunk_size=500, # Small chunk to ensure pagination - ) - - assert result1["has_more"] is True - assert "continue_hints" in result1 - - # Continue reading - result2 = await tool.run(continue_from=result1["short_id"]) - assert "output" in result2 - assert result2["command_id"] == result1["command_id"] - - @pytest.mark.asyncio - async def test_resume_aliases(self, tool_helper, tool): - """Test resume aliases work.""" - # Run a command - result1 = await tool.run(command="echo 'test'") - - # Test 'resume' alias - result2 = await tool.run(resume=result1["short_id"]) - assert result2["command_id"] == result1["command_id"] - - # Test 'last' keyword - result3 = await tool.run(continue_from="last") - assert result3["command_id"] == result1["command_id"] - - @pytest.mark.asyncio - async def test_string_number_conversion(self, tool_helper, tool): - """Test that string numbers are converted properly.""" - result = await tool.run( - command="echo 'test'", - timeout="30", # String instead of int - chunk_size="1000", # String instead of int - ) - - tool_helper.assert_in_result("command_id", result) - assert result["status"] in ["running", "completed"] - - @pytest.mark.asyncio - async def test_list_commands(self, tool_helper, tool): - """Test listing recent commands.""" - # Run a few commands - await tool.run(command="echo 'test1'") - await tool.run(command="echo 'test2'") - - # List commands - result = await tool.list() - - tool_helper.assert_in_result("commands", result) - assert len(result["commands"]) >= 2 - assert result["session_id"] == tool.session_id - - @pytest.mark.asyncio - async def test_tail_command(self, tool_helper, tool): - """Test tailing command output.""" - # Run a command with multiple lines - result1 = await tool.run(command="for i in {1..20}; do echo Line $i; done") - - # Tail the output - result2 = await tool.tail(ref=result1["short_id"], lines=5) - - assert "output" in result2 - assert "Line 20" in result2["output"] - assert result2["lines"] == 5 - - @pytest.mark.asyncio - async def test_error_handling(self, tool_helper, tool): - """Test error handling for invalid commands.""" - result = await tool.run(command="nonexistent_command_12345") - - tool_helper.assert_in_result("command_id", result) - # Command should still execute and capture error output - - @pytest.mark.asyncio - async def test_no_command_error(self, tool_helper, tool): - """Test helpful error when no command provided.""" - result = await tool.run() - - tool_helper.assert_in_result("error", result) - tool_helper.assert_in_result("hint", result) - tool_helper.assert_in_result("recent_commands", result) - - @pytest.mark.asyncio - async def test_session_persistence(self, tool_helper, tool): - """Test that session data persists to disk.""" - session_dir = tool.session_dir - commands_dir = tool.commands_dir - - # Run a command - result = await tool.run(command="echo 'persistent'") - cmd_id = result["command_id"] - - # Check files exist - assert session_dir.exists() - assert commands_dir.exists() - assert (commands_dir / cmd_id).exists() - assert (commands_dir / cmd_id / "output.log").exists() - assert (commands_dir / cmd_id / "metadata.json").exists() - - # Check metadata content - with open(commands_dir / cmd_id / "metadata.json", "r") as f: - metadata = json.load(f) - assert metadata["command"] == "echo 'persistent'" - assert metadata["command_id"] == cmd_id - - @pytest.mark.asyncio - async def test_normalize_command_ref(self, tool_helper, tool): - """Test command reference normalization.""" - # Run a command - result = await tool.run(command="echo 'test'") - cmd_id = result["command_id"] - short_id = result["short_id"] - - # Test different reference formats - assert tool._normalize_command_ref(cmd_id) == cmd_id - assert tool._normalize_command_ref(short_id) == cmd_id - assert tool._normalize_command_ref("1") == cmd_id # First command - assert tool._normalize_command_ref("last") == cmd_id - assert tool._normalize_command_ref("latest") == cmd_id - - @pytest.mark.asyncio - async def test_long_running_command(self, tool_helper, tool): - """Test handling of long-running commands.""" - # Start a command that takes time - result1 = await tool.run( - command="sleep 2 && echo 'done'", - timeout=1, # Timeout before completion - ) - - # Should still get initial status - assert result1["status"] == "running" - assert "command_id" in result1 - - # Wait and check again - await asyncio.sleep(3) - result2 = await tool.run(continue_from=result1["short_id"]) - - # Now it should be completed - assert "done" in result2["output"] or result2["status"] == "completed" - - @pytest.mark.asyncio - async def test_streaming_to_disk(self, tool_helper, tool): - """Test that output is streamed directly to disk.""" - # Generate large output - result = await tool.run( - command="for i in {1..1000}; do echo 'This is a long line of text to test streaming'; done", - chunk_size=1000, # Small chunk - ) - - # Check that file exists and is larger than chunk - cmd_dir = tool.commands_dir / result["command_id"] - output_file = cmd_dir / "output.log" - - assert output_file.exists() - assert output_file.stat().st_size > 1000 - assert result["has_more"] is True - assert len(result["output"]) <= 1000 - - -class TestForgivingEditHelper: - """Test cases for the forgiving edit helper.""" - - def test_normalize_whitespace(self): - """Test whitespace normalization.""" - from hanzo_mcp.tools.common.forgiving_edit import ForgivingEditHelper - - # Test tab to space conversion - text = "\tindented\twith\ttabs" - normalized = ForgivingEditHelper.normalize_whitespace(text) - assert "\t" not in normalized - # The function normalizes multiple spaces to single spaces in content - assert " indented with tabs" in normalized - - # Test multiple space normalization - text = "multiple spaces between" - normalized = ForgivingEditHelper.normalize_whitespace(text) - assert "multiple spaces between" in normalized - - # Test preserving indentation - text = " indented line\n more indented" - normalized = ForgivingEditHelper.normalize_whitespace(text) - assert normalized.startswith(" ") - assert "\n " in normalized - - def test_find_fuzzy_match(self): - """Test fuzzy matching.""" - from hanzo_mcp.tools.common.forgiving_edit import ForgivingEditHelper - - haystack = """ -def hello(): - print("Hello, World!") - return True -""" - - # Exact match - match = ForgivingEditHelper.find_fuzzy_match(haystack, 'print("Hello, World!")') - assert match is not None - start, end, text = match - assert 'print("Hello, World!")' in text - - # Whitespace difference - match = ForgivingEditHelper.find_fuzzy_match( - haystack, - 'print("Hello, World!")', # Different quotes/spaces - ) - assert match is not None - - # No match with low threshold - match = ForgivingEditHelper.find_fuzzy_match( - haystack, "completely different text", threshold=0.9 - ) - assert match is None - - def test_suggest_matches(self): - """Test match suggestions.""" - from hanzo_mcp.tools.common.forgiving_edit import ForgivingEditHelper - - haystack = """ -def add(a, b): - return a + b - -def subtract(a, b): - return a - b -""" - - suggestions = ForgivingEditHelper.suggest_matches( - haystack, "def multiply(a, b):" - ) - - assert len(suggestions) > 0 - # Should suggest similar function definitions - assert any("def" in text for _, text in suggestions) - - def test_prepare_edit_string(self): - """Test preparing strings for editing.""" - from hanzo_mcp.tools.common.forgiving_edit import ForgivingEditHelper - - # Test removing line numbers - text = """1: def hello(): -2: print("test") -3: return True""" - - prepared = ForgivingEditHelper.prepare_edit_string(text) - assert "1:" not in prepared - assert "def hello():" in prepared - assert ' print("test")' in prepared diff --git a/pkg/hanzo-mcp/tests/test_swarm/__init__.py b/pkg/hanzo-mcp/tests/test_swarm/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-mcp/tests/test_swarm/conftest.py b/pkg/hanzo-mcp/tests/test_swarm/conftest.py deleted file mode 100644 index c15bf3593..000000000 --- a/pkg/hanzo-mcp/tests/test_swarm/conftest.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Fixtures for swarm tests.""" - -import pytest - -from tests.test_utils import ToolTestHelper - - -@pytest.fixture -def tool_helper(): - """Provide ToolTestHelper instance for tests.""" - return ToolTestHelper() diff --git a/pkg/hanzo-mcp/tests/test_swarm/test_claude_code_parallel.py b/pkg/hanzo-mcp/tests/test_swarm/test_claude_code_parallel.py deleted file mode 100644 index 7a07343c4..000000000 --- a/pkg/hanzo-mcp/tests/test_swarm/test_claude_code_parallel.py +++ /dev/null @@ -1,349 +0,0 @@ -"""Test parallel editing with Claude Code (Sonnet) agents. - -This test demonstrates: -1. Multiple Claude agents editing different files in parallel -2. Automatic pagination for large responses -3. Consensus mode with multiple agents reviewing code -""" - -import os -import shutil -import tempfile - -import pytest -from hanzo_tools.agent.agent_tool import AgentTool -from hanzo_tools.agent.swarm_tool import SwarmTool -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_mcp.tools.common.permissions import PermissionManager - - -class TestClaudeCodeParallel: - """Test Claude Code parallel editing capabilities.""" - - @pytest.fixture - def test_project(self): - """Create a test project with files that need refactoring.""" - test_dir = tempfile.mkdtemp(prefix="claude_test_") - - # Create a simple project structure - project_files = { - "main.py": '''#!/usr/bin/env python -"""Main application entry point.""" - -import sys -from config import CONFIG_API_KEY, CONFIG_DATABASE_URL, CONFIG_CACHE_SIZE -from database import Database -from api import APIClient - -def main(): - # Initialize database - db = Database(CONFIG_DATABASE_URL, CONFIG_CACHE_SIZE) - - # Initialize API client - api = APIClient(CONFIG_API_KEY) - - # Main application logic - print("Starting application...") - if db.connect(): - print("Database connected") - api.test_connection() - else: - print("Failed to connect to database") - sys.exit(1) - -if __name__ == "__main__": - main() -''', - "config.py": '''"""Configuration module.""" - -# Old configuration variables that need updating -CONFIG_API_KEY = "sk-old-api-key-12345" -CONFIG_DATABASE_URL = "postgres://localhost:5432/olddb" -CONFIG_CACHE_SIZE = 1000 -CONFIG_TIMEOUT = 30 -CONFIG_RETRIES = 3 - -# Feature flags -CONFIG_ENABLE_CACHE = True -CONFIG_ENABLE_LOGGING = False -CONFIG_DEBUG_MODE = True -''', - "database.py": '''"""Database connection module.""" - -from config import CONFIG_DATABASE_URL, CONFIG_CACHE_SIZE, CONFIG_TIMEOUT - -class Database: - """Database connection handler.""" - - def __init__(self, url=CONFIG_DATABASE_URL, cache_size=CONFIG_CACHE_SIZE): - self.url = url - self.cache_size = cache_size - self.timeout = CONFIG_TIMEOUT - self.connection = None - - def connect(self): - """Connect to the database.""" - print(f"Connecting to database: {CONFIG_DATABASE_URL}") - print(f"Cache size: {CONFIG_CACHE_SIZE}") - print(f"Timeout: {CONFIG_TIMEOUT}") - # Simulate connection - self.connection = f"Connection to {self.url}" - return True - - def disconnect(self): - """Disconnect from the database.""" - if self.connection: - print("Disconnecting from database") - self.connection = None -''', - "api.py": '''"""API client module.""" - -from config import CONFIG_API_KEY, CONFIG_RETRIES, CONFIG_TIMEOUT - -class APIClient: - """API client for external services.""" - - def __init__(self, api_key=CONFIG_API_KEY): - self.api_key = api_key - self.retries = CONFIG_RETRIES - self.timeout = CONFIG_TIMEOUT - self.session = None - - def test_connection(self): - """Test API connection.""" - print(f"Testing API with key: {CONFIG_API_KEY[:10]}...") - print(f"Retries: {CONFIG_RETRIES}, Timeout: {CONFIG_TIMEOUT}") - return True - - def make_request(self, endpoint, data=None): - """Make an API request.""" - headers = { - "Authorization": f"Bearer {CONFIG_API_KEY}", - "Content-Type": "application/json" - } - print(f"Making request to {endpoint}") - return {"status": "success", "data": data} -''', - } - - # Write all files - for filename, content in project_files.items(): - file_path = os.path.join(test_dir, filename) - with open(file_path, "w") as f: - f.write(content) - - yield test_dir - - # Cleanup - shutil.rmtree(test_dir) - - @pytest.fixture - def permission_manager(self, test_project): - """Create permission manager for test project.""" - pm = PermissionManager() - pm._allowed_paths.add(test_project) - return pm - - @pytest.mark.asyncio - async def test_parallel_variable_renaming( - self, tool_helper, test_project, permission_manager - ): - """Test parallel editing of multiple files to rename variables.""" - # Skip if no API key - if not os.environ.get("ANTHROPIC_API_KEY") and not os.environ.get( - "CLAUDE_API_KEY" - ): - pytest.skip("No Claude API key found") - - # Create swarm tool (will default to Claude Sonnet) - swarm = SwarmTool(permission_manager=permission_manager) - - # Create context - ctx = MCPContext() - - # Define parallel editing tasks - tasks = [ - { - "file_path": os.path.join(test_project, "config.py"), - "instructions": """Rename all CONFIG_ prefixed variables to use a modern Settings class pattern: - 1. Change CONFIG_API_KEY to SETTINGS_API_KEY - 2. Change CONFIG_DATABASE_URL to SETTINGS_DATABASE_URL - 3. Change CONFIG_CACHE_SIZE to SETTINGS_CACHE_SIZE - 4. Change CONFIG_TIMEOUT to SETTINGS_TIMEOUT - 5. Change CONFIG_RETRIES to SETTINGS_RETRIES - 6. Update all other CONFIG_ variables similarly - Keep the values the same, just rename the variables.""", - "description": "Modernize config.py variables", - }, - { - "file_path": os.path.join(test_project, "database.py"), - "instructions": """Update all imports and usages to use the new SETTINGS_ prefix instead of CONFIG_: - 1. Update the import statement - 2. Update all references to CONFIG_ variables to use SETTINGS_ instead - 3. Ensure the code still works correctly""", - "description": "Update database.py imports and usage", - }, - { - "file_path": os.path.join(test_project, "api.py"), - "instructions": """Update all imports and usages to use the new SETTINGS_ prefix instead of CONFIG_: - 1. Update the import statement - 2. Update all references to CONFIG_ variables to use SETTINGS_ instead - 3. Ensure the code still works correctly""", - "description": "Update api.py imports and usage", - }, - ] - - # Execute parallel edits - print("\n=== Starting parallel Claude Code editing ===") - result = await swarm.call( - ctx, - tasks=tasks, - common_instructions="Ensure all Python code remains valid and properly formatted. Maintain existing functionality.", - max_concurrent=3, # Run all 3 in parallel - ) - - print("\n=== Swarm Result ===") - print(result) - - # Verify the edits were made - with open(os.path.join(test_project, "config.py"), "r") as f: - config_content = f.read() - # Check that at least some renaming happened - assert "SETTINGS_" in config_content or "CONFIG_" not in config_content - - @pytest.mark.asyncio - async def test_consensus_code_review( - self, tool_helper, test_project, permission_manager - ): - """Test consensus mode with multiple agents reviewing code.""" - # Skip if no API key - if not os.environ.get("ANTHROPIC_API_KEY") and not os.environ.get( - "CLAUDE_API_KEY" - ): - pytest.skip("No Claude API key found") - - # Create swarm tool - swarm = SwarmTool(permission_manager=permission_manager) - - # Create context - ctx = MCPContext() - - # Define consensus review tasks - multiple agents review the same file - tasks = [ - { - "file_path": os.path.join(test_project, "database.py"), - "instructions": """Review this database module from an architecture perspective: - 1. Evaluate the design patterns used - 2. Suggest improvements to the class structure - 3. Comment on error handling - 4. Propose better abstraction if needed""", - "description": "Agent 1: Architecture Review", - }, - { - "file_path": os.path.join(test_project, "database.py"), - "instructions": """Review this database module from a security perspective: - 1. Identify potential security vulnerabilities - 2. Check for SQL injection risks - 3. Evaluate credential handling - 4. Suggest security improvements""", - "description": "Agent 2: Security Review", - }, - { - "file_path": os.path.join(test_project, "database.py"), - "instructions": """Review this database module from a performance perspective: - 1. Identify potential performance bottlenecks - 2. Suggest caching improvements - 3. Evaluate connection pooling needs - 4. Recommend optimization strategies""", - "description": "Agent 3: Performance Review", - }, - ] - - # Execute consensus review - print("\n=== Starting consensus code review ===") - result = await swarm.call( - ctx, - tasks=tasks, - common_instructions="Provide specific, actionable feedback. Focus on practical improvements.", - max_concurrent=3, # Run all reviewers in parallel - ) - - print("\n=== Consensus Review Result ===") - print(result) - - # Check that we got reviews from multiple agents - assert "Agent 1:" in result or "Task 1" in result - assert "Agent 2:" in result or "Task 2" in result - assert "Agent 3:" in result or "Task 3" in result - - @pytest.mark.asyncio - async def test_large_response_pagination( - self, tool_helper, test_project, permission_manager - ): - """Test that large responses are properly paginated.""" - # Skip if no API key - if not os.environ.get("ANTHROPIC_API_KEY") and not os.environ.get( - "CLAUDE_API_KEY" - ): - pytest.skip("No Claude API key found") - - # Create a large file that will produce a big response - large_file = os.path.join(test_project, "large_module.py") - with open(large_file, "w") as f: - # Write a large file with many functions - f.write('"""Large module for testing pagination."""\n\n') - for i in range(100): - f.write(f'''def function_{i}(param1, param2): - """Function {i} that needs documentation. - - This function currently lacks proper documentation, - type hints, and error handling. - """ - result = param1 + param2 - print(f"Function {i} result: {{result}}") - return result - -''') - - # Create agent directly to test pagination - agent = AgentTool( - permission_manager=permission_manager, - model="anthropic/claude-3-5-sonnet-20241022", - ) - - ctx = MCPContext() - - # Task that will produce large output - prompt = f"""Analyze every function in {large_file} and provide: -1. A detailed review of each function -2. Specific improvements for each function -3. Type hints that should be added -4. Error handling recommendations -Be very detailed and thorough for each function.""" - - print("\n=== Testing large response pagination ===") - result = await agent.call(ctx, prompts=prompt) - - # The response should be reasonable size due to pagination - print(f"\nResponse length: {len(result)} characters") - assert len(result) > 0 - - # Check for pagination indicators if response was large - if "To continue" in result or "cursor" in result: - print("Pagination detected in response") - - -def test_claude_code_defaults(): - """Test that swarm tool defaults to Claude Code.""" - pm = PermissionManager() - swarm = SwarmTool(permission_manager=pm) - - # Check that it defaults to Claude Sonnet - assert swarm.model == "anthropic/claude-3-5-sonnet-20241022" - print(f"โœ“ Swarm tool defaults to: {swarm.model}") - - -if __name__ == "__main__": - # Run specific test - pytest.main([__file__, "-v", "-k", "test_parallel_variable_renaming"]) diff --git a/pkg/hanzo-mcp/tests/test_swarm/test_parallel_editing.py b/pkg/hanzo-mcp/tests/test_swarm/test_parallel_editing.py deleted file mode 100644 index bf53cf394..000000000 --- a/pkg/hanzo-mcp/tests/test_swarm/test_parallel_editing.py +++ /dev/null @@ -1,346 +0,0 @@ -"""Test case for parallel AI editing with swarm and batch tools. - -This test demonstrates: -1. Multiple agents editing different files in parallel -2. Using batch tool for concurrent operations -3. Proper pagination for large responses -4. Claude Code compatibility -""" - -import os -import shutil -import tempfile - -import pytest -from hanzo_tools.agent.agent_tool import AgentTool -from hanzo_tools.agent.swarm_tool import SwarmTool -from hanzo_tools.filesystem import Write -from mcp.server.fastmcp import Context as MCPContext -from mcp.server.fastmcp import FastMCP - -from hanzo_mcp.tools.common.batch_tool import BatchTool -from hanzo_mcp.tools.common.permissions import PermissionManager - - -class TestParallelEditing: - """Test parallel editing capabilities with multiple agents.""" - - @pytest.fixture - def test_dir(self): - """Create a temporary directory with test files.""" - test_dir = tempfile.mkdtemp() - - # Create test files that need editing - files_content = { - "config.py": """# Configuration file -OLD_API_KEY = "sk-old-key-12345" -OLD_DATABASE_URL = "postgres://old-host:5432/db" -OLD_CACHE_SIZE = 1000 - -class Config: - def __init__(self): - self.api_key = OLD_API_KEY - self.database_url = OLD_DATABASE_URL - self.cache_size = OLD_CACHE_SIZE -""", - "database.py": """# Database module -from config import OLD_DATABASE_URL, OLD_CACHE_SIZE - -class Database: - def __init__(self): - self.url = OLD_DATABASE_URL - self.cache = OLD_CACHE_SIZE - - def connect(self): - print(f"Connecting to {OLD_DATABASE_URL}") - return True -""", - "api.py": """# API module -from config import OLD_API_KEY, Config - -class APIClient: - def __init__(self): - self.config = Config() - self.api_key = OLD_API_KEY - - def make_request(self, endpoint): - headers = {"Authorization": f"Bearer {OLD_API_KEY}"} - print(f"Making request with key: {self.api_key}") - return {"status": "ok"} -""", - } - - # Write test files - for filename, content in files_content.items(): - file_path = os.path.join(test_dir, filename) - with open(file_path, "w") as f: - f.write(content) - - yield test_dir - - # Cleanup - shutil.rmtree(test_dir) - - @pytest.fixture - def permission_manager(self, test_dir): - """Create permission manager allowing access to test directory.""" - pm = PermissionManager() - # Allow access to test directory - pm._allowed_paths.add(test_dir) - return pm - - @pytest.fixture - def mcp_server(self): - """Create MCP server for testing.""" - return FastMCP("test-server") - - @pytest.mark.asyncio - async def test_swarm_parallel_file_editing( - self, tool_helper, test_dir, permission_manager, mcp_server - ): - """Test editing multiple files in parallel with swarm tool.""" - # Create swarm tool with default model (should use Sonnet) - swarm_tool = SwarmTool( - permission_manager=permission_manager, - model="anthropic/claude-3-5-sonnet-20241022", # Explicitly use Sonnet - max_concurrent=3, # Run 3 agents in parallel - ) - - # Create context - ctx = MCPContext() - - # Define tasks for each file - tasks = [ - { - "file_path": os.path.join(test_dir, "config.py"), - "instructions": """Replace all occurrences of 'OLD_' prefix with 'NEW_' in variable names. - Update the values as follows: - - API key should be 'sk-new-key-67890' - - Database URL should be 'postgres://new-host:5432/newdb' - - Cache size should be 5000 - Make sure to update both the variable definitions and their usage.""", - "description": "Update configuration variables", - }, - { - "file_path": os.path.join(test_dir, "database.py"), - "instructions": """Update all imports to use the new variable names (NEW_ prefix instead of OLD_). - Update all references to use the new variable names. - Make sure the code still works correctly.""", - "description": "Update database module imports", - }, - { - "file_path": os.path.join(test_dir, "api.py"), - "instructions": """Update all imports to use the new variable names (NEW_ prefix instead of OLD_). - Update all references to use the new variable names. - Make sure the code still works correctly.""", - "description": "Update API module imports", - }, - ] - - # Execute swarm - result = await swarm_tool.call( - ctx, - tasks=tasks, - common_instructions="Ensure all Python code remains valid and properly formatted. Maintain the existing code structure.", - max_concurrent=3, - ) - - # Verify results - tool_helper.assert_in_result("Swarm Execution Summary:", result) - assert ( - "Successful: 3" in result or "Successful: 2" in result - ) # Allow for some failures in test - - # Check if files were actually modified - with open(os.path.join(test_dir, "config.py"), "r") as f: - config_content = f.read() - # Should have NEW_ variables now - assert ( - "NEW_API_KEY" in config_content or "OLD_API_KEY" in config_content - ) # Allow for partial success - - print("Swarm execution result:") - print(result) - - @pytest.mark.asyncio - async def test_batch_tool_with_agents( - self, tool_helper, test_dir, permission_manager, mcp_server - ): - """Test using batch tool to launch multiple agents.""" - # Create tools - agent_tool = AgentTool( - permission_manager=permission_manager, - model="anthropic/claude-3-5-sonnet-20241022", - ) - read_tool = Read(permission_manager) - write_tool = Write(permission_manager) - - # Create batch tool - batch_tool = BatchTool( - { - "agent": agent_tool, - "read": read_tool, - "write": write_tool, - } - ) - - # Create context - ctx = MCPContext() - - # Test batch execution with multiple agent calls - invocations = [ - { - "tool_name": "agent", - "input": { - "prompts": f"Search for all occurrences of 'OLD_' prefix in {os.path.join(test_dir, 'config.py')} and list them" - }, - }, - { - "tool_name": "agent", - "input": { - "prompts": f"Search for all imports from config module in {test_dir} and list the files" - }, - }, - { - "tool_name": "read", - "input": {"file_path": os.path.join(test_dir, "api.py")}, - }, - ] - - # Execute batch - result = await batch_tool.call( - ctx, description="Analyze codebase", invocations=invocations - ) - - # Check results - tool_helper.assert_in_result("Batch operation: Analyze codebase", result) - tool_helper.assert_in_result("Result 1: agent", result) - tool_helper.assert_in_result("Result 2: agent", result) - tool_helper.assert_in_result("Result 3: read", result) - - print("Batch execution result:") - print(result) - - @pytest.mark.asyncio - async def test_pagination_with_large_agent_response( - self, tool_helper, test_dir, permission_manager - ): - """Test that large agent responses are properly paginated.""" - # Create a large file that will produce a big response - large_content = "\n".join( - [ - f"LINE_{i}: This is a test line with some content that needs to be analyzed" - for i in range(1000) - ] - ) - large_file = os.path.join(test_dir, "large_file.py") - with open(large_file, "w") as f: - f.write(large_content) - - # Create agent - agent = AgentTool( - permission_manager=permission_manager, - model="anthropic/claude-3-5-sonnet-20241022", - ) - - # Create context - ctx = MCPContext() - - # Execute agent with task that will produce large output - result = await agent.call( - ctx, - prompts=f"Read the entire file at {large_file} and list every single line with its line number. Be very detailed.", - ) - - # Check that response is reasonable size (pagination should have kicked in) - assert len(result) < 100000 # Should be paginated if too large - - print(f"Agent response length: {len(result)} characters") - - @pytest.mark.asyncio - async def test_claude_code_compatibility( - self, tool_helper, test_dir, permission_manager - ): - """Test that agent tool works with Claude Code style prompts.""" - # Create agent configured for Claude Code - agent = AgentTool( - permission_manager=permission_manager, - model="anthropic/claude-3-5-sonnet-20241022", # Claude Sonnet - max_iterations=15, # Higher for more complex tasks - max_tool_uses=50, # Higher for more operations - ) - - # Create context - ctx = MCPContext() - - # Claude Code style prompt with multiple operations - claude_code_prompt = f"""I need you to refactor the codebase in {test_dir}: - -1. First, analyze all Python files to understand the structure -2. Identify all variables with 'OLD_' prefix -3. Create a refactoring plan -4. Update all files to use 'NEW_' prefix instead -5. Ensure all imports and references are updated -6. Verify the changes are correct - -Please be thorough and handle this like Claude Code would - read files first, plan changes, then execute them systematically.""" - - # Execute - result = await agent.call(ctx, prompts=claude_code_prompt) - - # Should have executed multiple operations - tool_helper.assert_in_result("AGENT RESPONSE:", result) - assert len(result) > 100 # Should have substantial output - - print("Claude Code style execution:") - print(result[:500] + "..." if len(result) > 500 else result) - - -# Example of how this would be used in practice -async def example_usage(): - """Example of using swarm for parallel editing.""" - # Setup - permission_manager = PermissionManager() - permission_manager._allowed_paths.add("/path/to/project") - - swarm = SwarmTool( - permission_manager=permission_manager, - model="anthropic/claude-3-5-sonnet-20241022", # Use Sonnet for all agents - max_concurrent=5, # Run up to 5 agents in parallel - ) - - # Define editing tasks - tasks = [ - { - "file_path": "/path/to/project/src/module1.py", - "instructions": "Update all class names from CamelCase to snake_case", - "description": "Rename classes in module1", - }, - { - "file_path": "/path/to/project/src/module2.py", - "instructions": "Update all class names from CamelCase to snake_case", - "description": "Rename classes in module2", - }, - { - "file_path": "/path/to/project/src/module3.py", - "instructions": "Update all class names from CamelCase to snake_case", - "description": "Rename classes in module3", - }, - ] - - # Execute all edits in parallel - ctx = MCPContext() - result = await swarm.call( - ctx, - tasks=tasks, - common_instructions="Ensure all Python code remains valid. Update imports as needed.", - max_concurrent=3, - enable_claude_code=True, # Future: spawn actual Claude Code instances - ) - - print(result) - - -if __name__ == "__main__": - # Run the tests - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_swarm/test_swarm_basic.py b/pkg/hanzo-mcp/tests/test_swarm/test_swarm_basic.py deleted file mode 100644 index 6ff40d2c1..000000000 --- a/pkg/hanzo-mcp/tests/test_swarm/test_swarm_basic.py +++ /dev/null @@ -1,244 +0,0 @@ -"""Basic swarm tool tests with success and failure cases.""" - -import os -import shutil -import sys -import tempfile -from unittest.mock import AsyncMock, Mock, patch - -import pytest - -# Add the parent directory to the path for imports -sys.path.insert( - 0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -) - -from hanzo_tools.agent.swarm_tool import SwarmTool -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_mcp.tools.common.permissions import PermissionManager - - -class TestSwarmBasic: - """Basic tests for swarm tool functionality.""" - - def test_swarm_defaults_to_claude_sonnet(self): - """Test that swarm tool defaults to Claude Sonnet.""" - pm = PermissionManager() - swarm = SwarmTool(permission_manager=pm) - - assert swarm.model == "anthropic/claude-3-5-sonnet-20241022" - print("โœ“ Swarm tool correctly defaults to Claude 3.5 Sonnet") - - def test_swarm_detects_api_keys(self): - """Test that swarm tool detects API keys from environment.""" - pm = PermissionManager() - - # Test with ANTHROPIC_API_KEY - with patch.dict(os.environ, {"ANTHROPIC_API_KEY": "test-key-123"}): - swarm = SwarmTool(permission_manager=pm) - assert swarm.api_key == "test-key-123" - print("โœ“ Swarm detects ANTHROPIC_API_KEY") - - # Test with CLAUDE_API_KEY - with patch.dict(os.environ, {"CLAUDE_API_KEY": "test-key-456"}, clear=True): - swarm = SwarmTool(permission_manager=pm) - print(f"Expected: test-key-456, Got: {swarm.api_key}") - assert swarm.api_key == "test-key-456" - print("โœ“ Swarm detects CLAUDE_API_KEY") - - # Test priority (ANTHROPIC_API_KEY takes precedence) - with patch.dict( - os.environ, - {"ANTHROPIC_API_KEY": "primary-key", "CLAUDE_API_KEY": "fallback-key"}, - ): - swarm = SwarmTool(permission_manager=pm) - assert swarm.api_key == "primary-key" - print("โœ“ ANTHROPIC_API_KEY takes precedence over CLAUDE_API_KEY") - - @pytest.mark.asyncio - async def test_swarm_fails_without_agents(self): - """Test that swarm fails gracefully without agents.""" - pm = PermissionManager() - swarm = SwarmTool(permission_manager=pm) - ctx = Mock(spec=MCPContext) - ctx.meta = {"tool_manager": Mock()} - - # Mock tool context - mock_tool_ctx = Mock() - mock_tool_ctx.set_tool_info = AsyncMock() - mock_tool_ctx.error = AsyncMock() - mock_tool_ctx.info = AsyncMock() - - with patch( - "hanzo_mcp.tools.common.context.create_tool_context", - return_value=mock_tool_ctx, - ): - # Call with no agents in config - result = await swarm.call(ctx, config={"agents": {}}) - - assert "Error:" in result - assert "at least one agent" in result.lower() - print("โœ“ Swarm correctly fails when no agents provided") - - @pytest.mark.asyncio - async def test_swarm_validates_agent_format(self): - """Test that swarm validates agent format.""" - pm = PermissionManager() - swarm = SwarmTool(permission_manager=pm) - ctx = Mock(spec=MCPContext) - ctx.meta = {"tool_manager": Mock()} - - # Mock tool context - mock_tool_ctx = Mock() - mock_tool_ctx.set_tool_info = AsyncMock() - mock_tool_ctx.error = AsyncMock() - mock_tool_ctx.info = AsyncMock() - - with patch( - "hanzo_mcp.tools.common.context.create_tool_context", - return_value=mock_tool_ctx, - ): - # Valid agent format - v2 interface uses agents with query - result = await swarm.call( - ctx, - config={ - "agents": { - "agent1": {"query": "Analyze the code", "role": "Code analyzer"} - } - }, - query="Analyze this project", - ) - - # Should handle gracefully - assert isinstance(result, str) - print("โœ“ Swarm handles agent format") - - @pytest.mark.asyncio - async def test_swarm_respects_max_concurrent(self): - """Test that swarm respects max_concurrent setting.""" - pm = PermissionManager() - swarm = SwarmTool(permission_manager=pm) - ctx = Mock(spec=MCPContext) - ctx.meta = {"tool_manager": Mock()} - - # Create dummy agents with v2 config - test_dir = tempfile.mkdtemp() - try: - # Create test files - for i in range(5): - with open(os.path.join(test_dir, f"file{i}.txt"), "w") as f: - f.write(f"Content {i}") - - # Allow access - pm.add_allowed_path(test_dir) - - # Create multiple agents - agents = {} - for i in range(5): - agents[f"agent{i}"] = { - "query": f"Analyze file{i}.txt", - "file_path": os.path.join(test_dir, f"file{i}.txt"), - "role": f"Analyzer {i}", - } - - # Mock execute to track concurrency - concurrent_count = 0 - max_observed = 0 - - async def mock_execute(*args, **kwargs): - nonlocal concurrent_count, max_observed - concurrent_count += 1 - max_observed = max(max_observed, concurrent_count) - # Simulate work - import asyncio - - await asyncio.sleep(0.01) - concurrent_count -= 1 - return "Mock result" - - # Test with max_concurrent=2 - with patch.object(swarm, "_execute_agent", mock_execute): - await swarm.call( - ctx, - config={"agents": agents}, - query="Analyze all files", - max_concurrent=2, - ) - - # Max observed should not exceed 2 - assert max_observed <= 2 - print(f"โœ“ Swarm respects max_concurrent (observed max: {max_observed})") - - finally: - shutil.rmtree(test_dir) - - @pytest.mark.asyncio - async def test_swarm_handles_agent_failures(self): - """Test that swarm handles individual agent failures gracefully.""" - pm = PermissionManager() - swarm = SwarmTool(permission_manager=pm) - ctx = Mock(spec=MCPContext) - ctx.meta = {"tool_manager": Mock()} - - # Create agents where one will fail - agents = { - "agent1": {"query": "Task 1", "role": "Worker 1"}, - "agent2": {"query": "Task 2", "role": "Worker 2"}, - "agent3": {"query": "Task 3", "role": "Worker 3"}, - } - - call_count = 0 - - async def mock_execute(agent_id, *args, **kwargs): - nonlocal call_count - call_count += 1 - if agent_id == "agent2": - raise Exception("Agent 2 failed!") - return f"Result from {agent_id}" - - # Mock the execution - with patch.object(swarm, "_execute_agent", mock_execute): - result = await swarm.call( - ctx, config={"agents": agents}, query="Execute all tasks" - ) - - # Should complete despite one failure - assert isinstance(result, str) - assert call_count == 3 # All agents should be attempted - print("โœ“ Swarm handles agent failures gracefully") - - @pytest.mark.asyncio - async def test_swarm_summary_format(self): - """Test that swarm returns properly formatted summary.""" - pm = PermissionManager() - swarm = SwarmTool(permission_manager=pm) - ctx = Mock(spec=MCPContext) - ctx.meta = {"tool_manager": Mock()} - - # Create simple agents - agents = { - "analyzer": { - "query": "Analyze the code structure", - "role": "Code Analyzer", - }, - "reviewer": { - "query": "Review the analysis", - "role": "Code Reviewer", - "receives_from": ["analyzer"], - }, - } - - async def mock_execute(agent_id, *args, **kwargs): - return f"Mock result from {agent_id}" - - with patch.object(swarm, "_execute_agent", mock_execute): - result = await swarm.call( - ctx, - config={"agents": agents, "entry_point": "analyzer"}, - query="Analyze and review code", - ) - - # Should return a string result - assert isinstance(result, str) - print("โœ“ Swarm returns formatted summary") diff --git a/pkg/hanzo-mcp/tests/test_swarm/test_swarm_parallel_edit.py b/pkg/hanzo-mcp/tests/test_swarm/test_swarm_parallel_edit.py deleted file mode 100644 index 87b97c41e..000000000 --- a/pkg/hanzo-mcp/tests/test_swarm/test_swarm_parallel_edit.py +++ /dev/null @@ -1,244 +0,0 @@ -"""Test swarm tool for parallel file editing. - -This test ensures the swarm tool can edit multiple files in parallel correctly. -""" - -import asyncio -from pathlib import Path - -import pytest -from hanzo_tools.agent.swarm_tool import SwarmTool - -from hanzo_mcp.tools.common.permissions import PermissionManager - - -class TestSwarmParallelEdit: - """Test swarm tool for parallel file editing.""" - - @pytest.fixture - def temp_project(self, tmp_path): - """Create a temporary project with multiple files.""" - # Create 5 test files - files = {} - for i in range(1, 6): - file_path = tmp_path / f"file{i}.py" - content = f"""# File {i} -import old_module - -class TestClass{i}: - def __init__(self): - self.value = {i} - self.old_attribute = "old_value" - - def old_method(self): - return self.value * 2 - -def old_function(): - return "This is file {i}" -""" - file_path.write_text(content) - files[f"file{i}.py"] = file_path - - return tmp_path, files - - @pytest.fixture - def mock_mcp_context(self): - """Create a mock MCP context.""" - - class MockContext: - def __init__(self): - self.logs = [] - - async def log(self, level, message): - self.logs.append((level, message)) - - return MockContext() - - @pytest.mark.asyncio - async def test_swarm_parallel_file_edits( - self, tool_helper, temp_project, mock_mcp_context, monkeypatch - ): - """Test that swarm can edit multiple files in parallel.""" - project_dir, files = temp_project - - # Mock the agent tool to simulate edits - class MockAgentTool: - def __init__(self, *args, **kwargs): - pass - - async def call(self, ctx, prompts): - # Extract file path from prompt - for line in prompts.split("\n"): - if line.startswith("File:"): - file_path = Path(line.replace("File:", "").strip()) - break - else: - return "Error: No file path found in prompt" - - # Simulate editing the file - if file_path.exists(): - content = file_path.read_text() - # Replace old_module with new_module - content = content.replace("import old_module", "import new_module") - # Replace old_method with new_method - content = content.replace("def old_method(", "def new_method(") - # Replace old_function with new_function - content = content.replace("def old_function(", "def new_function(") - # Replace old_attribute with new_attribute - content = content.replace( - "self.old_attribute", "self.new_attribute" - ) - - file_path.write_text(content) - return f"Successfully updated {file_path.name}: replaced old references with new ones" - else: - return f"Error: File {file_path} not found" - - # Patch AgentTool - monkeypatch.setattr("hanzo_tools.agent.swarm_tool.AgentTool", MockAgentTool) - - # Create permission manager - permission_manager = PermissionManager(allowed_paths=[str(project_dir)]) - - # Create swarm tool - swarm_tool = SwarmTool( - permission_manager=permission_manager, - model="test-model", # Use test model - ) - - # Create tasks for all 5 files - tasks = [] - for filename, filepath in files.items(): - tasks.append( - { - "file_path": str(filepath), - "instructions": "Update all imports from old_module to new_module, rename old_method to new_method, rename old_function to new_function, and rename old_attribute to new_attribute", - "description": f"Update {filename}", - } - ) - - # Execute swarm - result = await swarm_tool.call( - mock_mcp_context, - tasks=tasks, - common_instructions="Ensure all changes maintain Python syntax", - max_concurrent=3, # Test concurrency limit - ) - - # Verify all files were updated - tool_helper.assert_in_result("Successful: 5", result) - tool_helper.assert_in_result("Failed: 0", result) - - # Check each file was actually modified - for filename, filepath in files.items(): - content = filepath.read_text() - assert "import new_module" in content - assert "import old_module" not in content - assert "def new_method(" in content - assert "def old_method(" not in content - assert "def new_function(" in content - assert "def old_function(" not in content - assert "self.new_attribute" in content - assert "self.old_attribute" not in content - - @pytest.mark.asyncio - async def test_swarm_handles_errors( - self, tool_helper, temp_project, mock_mcp_context, monkeypatch - ): - """Test that swarm handles errors gracefully.""" - project_dir, files = temp_project - - # Mock agent to simulate some failures - class MockAgentWithErrors: - def __init__(self, *args, **kwargs): - self.call_count = 0 - - async def call(self, ctx, prompts): - self.call_count += 1 - # Make every other task fail - if self.call_count % 2 == 0: - return "Error: Simulated failure" - - # Extract file path and succeed - for line in prompts.split("\n"): - if line.startswith("File:"): - file_path = Path(line.replace("File:", "").strip()) - return f"Successfully processed {file_path.name}" - - return "Error: No file path found" - - monkeypatch.setattr( - "hanzo_tools.agent.swarm_tool.AgentTool", MockAgentWithErrors - ) - - permission_manager = PermissionManager(allowed_paths=[str(project_dir)]) - swarm_tool = SwarmTool(permission_manager=permission_manager) - - # Create tasks - tasks = [] - for i in range(1, 5): - tasks.append( - { - "file_path": str(project_dir / f"file{i}.py"), - "instructions": "Test task", - } - ) - - # Execute swarm - result = await swarm_tool.call(mock_mcp_context, tasks=tasks, max_concurrent=2) - - # Should handle partial failures - tool_helper.assert_in_result("Successful: 2", result) - tool_helper.assert_in_result("Failed: 2", result) - tool_helper.assert_in_result("โŒ", result) # Error indicator - tool_helper.assert_in_result("โœ…", result) # Success indicator - - @pytest.mark.asyncio - async def test_swarm_respects_concurrency_limit( - self, tool_helper, temp_project, mock_mcp_context, monkeypatch - ): - """Test that swarm respects max_concurrent setting.""" - project_dir, files = temp_project - - # Track concurrent executions - concurrent_count = 0 - max_concurrent_seen = 0 - - class MockAgentConcurrency: - def __init__(self, *args, **kwargs): - pass - - async def call(self, ctx, prompts): - nonlocal concurrent_count, max_concurrent_seen - - concurrent_count += 1 - max_concurrent_seen = max(max_concurrent_seen, concurrent_count) - - # Simulate some work - await asyncio.sleep(0.1) - - concurrent_count -= 1 - return "Success" - - monkeypatch.setattr( - "hanzo_tools.agent.swarm_tool.AgentTool", MockAgentConcurrency - ) - - permission_manager = PermissionManager(allowed_paths=[str(project_dir)]) - swarm_tool = SwarmTool(permission_manager=permission_manager) - - # Create many tasks - tasks = [] - for i in range(10): - tasks.append( - { - "file_path": str(project_dir / f"file{i}.py"), - "instructions": "Test task", - } - ) - - # Execute with low concurrency limit - await swarm_tool.call(mock_mcp_context, tasks=tasks, max_concurrent=2) - - # Should never exceed the limit - assert max_concurrent_seen <= 2 diff --git a/pkg/hanzo-mcp/tests/test_swarm/test_swarm_parallel_fix.py b/pkg/hanzo-mcp/tests/test_swarm/test_swarm_parallel_fix.py deleted file mode 100644 index 71606aa3d..000000000 --- a/pkg/hanzo-mcp/tests/test_swarm/test_swarm_parallel_fix.py +++ /dev/null @@ -1,348 +0,0 @@ -"""Test case for swarm parallel file editing with complex Go fixes. - -This test demonstrates how the swarm tool can fix multiple Go files in parallel, -achieving 10-100x performance gains for complex refactoring tasks. -""" - -import json - -import pytest -from hanzo_tools.agent.swarm_tool import SwarmTool - -from hanzo_mcp.tools.common.permissions import PermissionManager - - -class TestSwarmParallelFix: - """Test swarm parallel file editing with complex Go fixes.""" - - @pytest.fixture - def test_project(self, tool_helper, tmp_path): - """Create a test Go project with undefined common imports.""" - # Create directory structure - project_dir = tmp_path / "luxfi-node" - vms_dir = project_dir / "vms" / "xvm" / "network" - vms_dir.mkdir(parents=True) - - # Create Go files with undefined common imports - - # atomic.go - atomic_content = """package network - -import ( - "context" - "fmt" -) - -type AtomicTx struct { - ID common.ID - Inputs []common.Input - Outputs []common.Output -} - -func (a *AtomicTx) Verify() error { - hash := common.Hash(a.ID) - if !common.IsValidID(a.ID) { - return fmt.Errorf("invalid ID") - } - return nil -} - -func (a *AtomicTx) Accept() error { - state := common.GetState() - return state.Apply(a) -} - -func (a *AtomicTx) Reject() error { - logger := common.GetLogger() - logger.Info("transaction rejected", "id", a.ID) - return nil -} -""" - (vms_dir / "atomic.go").write_text(atomic_content) - - # network.go - network_content = """package network - -import ( - "sync" -) - -type Network struct { - mu sync.Mutex - chainID common.ChainID - handlers map[common.MessageType]Handler -} - -func NewNetwork(chainID common.ChainID) *Network { - return &Network{ - chainID: chainID, - handlers: make(map[common.MessageType]Handler), - } -} - -func (n *Network) SendMessage(msg common.Message) error { - handler, ok := n.handlers[msg.Type()] - if !ok { - return common.ErrUnknownMessageType - } - return handler.Handle(msg) -} -""" - (vms_dir / "network.go").write_text(network_content) - - # gossip.go - gossip_content = """package network - -import ( - "time" -) - -type Gossiper struct { - network *Network - peers []common.Peer - interval time.Duration -} - -func NewGossiper(network *Network) *Gossiper { - return &Gossiper{ - network: network, - interval: common.DefaultGossipInterval, - } -} - -func (g *Gossiper) Start() { - ticker := time.NewTicker(g.interval) - defer ticker.Stop() - - for range ticker.C { - for _, peer := range g.peers { - msg := common.NewGossipMessage() - peer.Send(msg) - } - } -} -""" - (vms_dir / "gossip.go").write_text(gossip_content) - - # Create a common package that should be imported - common_dir = project_dir / "common" - common_dir.mkdir(parents=True) - - common_content = """package common - -import ( - "time" - "errors" -) - -type ID string -type ChainID string -type MessageType int - -type Input struct { - ID ID - Amount uint64 -} - -type Output struct { - ID ID - Amount uint64 -} - -type Message interface { - Type() MessageType -} - -type Peer interface { - Send(Message) error -} - -type Handler interface { - Handle(Message) error -} - -var ( - ErrUnknownMessageType = errors.New("unknown message type") - DefaultGossipInterval = 30 * time.Second -) - -func Hash(id ID) string { - return string(id) -} - -func IsValidID(id ID) bool { - return len(id) > 0 -} - -func GetState() *State { - return &State{} -} - -func GetLogger() *Logger { - return &Logger{} -} - -func NewGossipMessage() Message { - return &gossipMessage{} -} - -type State struct{} - -func (s *State) Apply(tx interface{}) error { - return nil -} - -type Logger struct{} - -func (l *Logger) Info(msg string, args ...interface{}) {} - -type gossipMessage struct{} - -func (g *gossipMessage) Type() MessageType { - return 1 -} -""" - (common_dir / "common.go").write_text(common_content) - - return project_dir - - @pytest.mark.asyncio - async def test_swarm_parallel_go_fixes(self, tool_helper, test_project): - """Test fixing multiple Go files in parallel using swarm.""" - # Initialize swarm tool - permission_manager = PermissionManager(allowed_paths=[str(test_project)]) - swarm = SwarmTool(permission_manager) - - # Create task list for fixing each file - tasks = [ - { - "file": str(test_project / "vms" / "xvm" / "network" / "atomic.go"), - "instruction": "Add the missing import for the common package. The import should be 'github.com/luxfi/node/common'. Use multi_edit to add the import in one operation.", - }, - { - "file": str(test_project / "vms" / "xvm" / "network" / "network.go"), - "instruction": "Add the missing import for the common package. The import should be 'github.com/luxfi/node/common'. Use multi_edit to add the import in one operation.", - }, - { - "file": str(test_project / "vms" / "xvm" / "network" / "gossip.go"), - "instruction": "Add the missing import for the common package. The import should be 'github.com/luxfi/node/common'. Use multi_edit to add the import in one operation.", - }, - ] - - # Run swarm to fix all files in parallel - ctx = type("Context", (), {})() # Mock context - - result = await swarm.call( - ctx, - tasks=tasks, - max_concurrency=3, # Fix all 3 files in parallel - ) - - # Parse results - results = json.loads(result) - - # Verify all tasks completed successfully - assert len(results["results"]) == 3 - assert all(r["status"] == "completed" for r in results["results"]) - - # Verify the imports were added correctly - for go_file in ["atomic.go", "network.go", "gossip.go"]: - content = (test_project / "vms" / "xvm" / "network" / go_file).read_text() - assert "github.com/luxfi/node/common" in content - # Verify the import is in the correct format - assert ( - "import (\n" in content - or 'import "github.com/luxfi/node/common"' in content - ) - - # Print performance metrics - print("\nPerformance Metrics:") - print(f"Total time: {results['total_time']:.2f}s") - print(f"Files fixed: {results['completed']}") - print( - f"Parallel speedup: ~{results['completed']}x (all files fixed simultaneously)" - ) - - @pytest.mark.asyncio - async def test_swarm_with_batch_analysis(self, tool_helper, test_project): - """Test using swarm with batch tool for initial analysis.""" - # This demonstrates the pattern of: - # 1. Use batch tool to analyze all files quickly - # 2. Use swarm to fix them in parallel - - permission_manager = PermissionManager(allowed_paths=[str(test_project)]) - swarm = SwarmTool(permission_manager) - - # Create a more complex task that requires analysis first - analysis_task = { - "file": str(test_project / "vms" / "xvm" / "network"), - "instruction": """ - 1. First use grep to find all Go files with undefined 'common' references - 2. Analyze each file to determine the exact import needed - 3. Use multi_edit to add the import statement to each file - 4. Ensure the import is added in the correct location in the imports block - """, - } - - # This would typically be done with batch tool first for analysis - # Then swarm for parallel fixes - - ctx = type("Context", (), {})() - - # For this test, we'll just verify the swarm structure works - result = await swarm.call(ctx, tasks=[analysis_task], max_concurrency=1) - - results = json.loads(result) - assert results["completed"] >= 0 # At least attempted - - def test_swarm_task_generation(self): - """Test generating swarm tasks from error output.""" - # This shows how to convert compiler errors to swarm tasks - - error_output = """ -vms/xvm/network/atomic.go:18:2: undefined: common -vms/xvm/network/atomic.go:20:6: undefined: common -vms/xvm/network/network.go:25:4: undefined: common -vms/xvm/network/gossip.go:55:13: undefined: common -""" - - # Parse errors to find unique files - files_with_errors = set() - for line in error_output.strip().split("\n"): - if ":" in line and "undefined: common" in line: - file_path = line.split(":")[0] - files_with_errors.add(file_path) - - # Generate swarm tasks - tasks = [] - for file_path in files_with_errors: - tasks.append( - { - "file": file_path, - "instruction": "Add import 'github.com/luxfi/node/common' to fix undefined common references. Use multi_edit for efficiency.", - } - ) - - assert len(tasks) == 3 - assert all( - "atomic.go" in t["file"] - or "network.go" in t["file"] - or "gossip.go" in t["file"] - for t in tasks - ) - - -if __name__ == "__main__": - # Example of how this would be used in practice - print("Swarm Parallel Fix Test Case") - print("=" * 50) - print("\nThis test demonstrates:") - print("1. Parsing compiler errors to identify files needing fixes") - print("2. Creating parallel tasks for each file") - print("3. Using swarm to fix all files simultaneously") - print("4. Achieving 10-100x speedup vs sequential fixes") - print("\nKey benefits:") - print("- All files fixed in parallel") - print("- Each agent has focused context (one file)") - print("- Multi-edit ensures atomic changes") - print("- Batch analysis can identify patterns across files") diff --git a/pkg/hanzo-mcp/tests/test_swarm/test_swarm_v2_comprehensive.py b/pkg/hanzo-mcp/tests/test_swarm/test_swarm_v2_comprehensive.py deleted file mode 100644 index 7b0461787..000000000 --- a/pkg/hanzo-mcp/tests/test_swarm/test_swarm_v2_comprehensive.py +++ /dev/null @@ -1,550 +0,0 @@ -"""Comprehensive test for swarm_tool_v2.py functionality. - -This test demonstrates multiple Claude agents collaborating to refactor code: -1. Creates a simple Python file -2. Uses multiple agents to refactor it: - - One agent adds docstrings - - Another adds type hints - - Another optimizes the code -3. Shows before/after comparison -""" - -import os -import shutil -import sys -import tempfile -from unittest.mock import AsyncMock, Mock, patch - -import pytest - -# Add the parent directory to the path for imports -sys.path.insert( - 0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -) - -from hanzo_tools.agent.swarm_tool import SwarmTool -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_mcp.tools.common.permissions import PermissionManager - - -class TestSwarmV2Comprehensive: - """Comprehensive tests for swarm_tool_v2 functionality.""" - - @pytest.fixture - def setup_test_env(self): - """Set up test environment with temporary directory.""" - test_dir = tempfile.mkdtemp() - pm = PermissionManager() - pm.add_allowed_path(test_dir) - - yield test_dir, pm - - # Cleanup - shutil.rmtree(test_dir) - - def create_test_file(self, test_dir: str) -> str: - """Create a simple Python file for testing.""" - file_path = os.path.join(test_dir, "calculator.py") - - # Create a simple calculator module without docstrings or type hints - content = """# Simple calculator module - -def add(a, b): - return a + b - -def subtract(a, b): - return a - b - -def multiply(a, b): - result = 0 - for i in range(b): - result = result + a - return result - -def divide(a, b): - if b == 0: - return None - return a / b - -class Calculator: - def __init__(self): - self.memory = 0 - - def add_to_memory(self, value): - self.memory = self.memory + value - - def clear_memory(self): - self.memory = 0 - - def get_memory(self): - return self.memory - - def calculate(self, operation, a, b): - if operation == "add": - return add(a, b) - elif operation == "subtract": - return subtract(a, b) - elif operation == "multiply": - return multiply(a, b) - elif operation == "divide": - return divide(a, b) - else: - return None -""" - - with open(file_path, "w") as f: - f.write(content) - - return file_path - - @pytest.mark.asyncio - async def test_swarm_v2_with_hanzo_agents(self, tool_helper, setup_test_env): - """Test swarm_tool_v2 when hanzo-agents is available.""" - test_dir, pm = setup_test_env - file_path = self.create_test_file(test_dir) - - # Read original content - with open(file_path, "r") as f: - original_content = f.read() - - print("\n=== BEFORE REFACTORING ===") - print(original_content) - print("=" * 60) - - # Create swarm tool - swarm = SwarmTool(permission_manager=pm) - ctx = MCPContext() - - # Define agent network for refactoring - config = { - "agents": { - "docstring_agent": { - "query": "Add comprehensive docstrings to all functions and classes in the file", - "role": "Documentation specialist", - "model": "claude-3-5-sonnet", - "file_path": file_path, - "connections": ["type_hint_agent"], - }, - "type_hint_agent": { - "query": "Add proper type hints to all function parameters and return values", - "role": "Type annotation expert", - "model": "claude-3-5-sonnet", - "file_path": file_path, - "receives_from": ["docstring_agent"], - "connections": ["optimizer_agent"], - }, - "optimizer_agent": { - "query": "Optimize the code for better performance and readability. The multiply function is inefficient - use the * operator instead", - "role": "Code optimization expert", - "model": "claude-3-5-sonnet", - "file_path": file_path, - "receives_from": ["type_hint_agent"], - "connections": ["reviewer"], - }, - "reviewer": { - "query": "Review all changes made by previous agents and ensure code quality", - "role": "Senior code reviewer", - "model": "claude-3-5-sonnet", - "file_path": file_path, - "receives_from": ["optimizer_agent"], - }, - }, - "entry_point": "docstring_agent", - "topology": "pipeline", - } - - # Check if we need to mock (for testing purposes) - mock_agents = os.environ.get("MOCK_HANZO_AGENTS", "false").lower() == "true" - if mock_agents: - print("Using mock implementation for testing") - - # Mock successful agent responses - mock_responses = { - "docstring_agent": "Added comprehensive docstrings to all functions and classes", - "type_hint_agent": "Added type hints to all functions", - "optimizer_agent": "Optimized multiply function and improved code structure", - "reviewer": "All changes reviewed and approved. Code quality is excellent.", - } - - # Simulate file changes - refactored_content = '''"""Simple calculator module with basic arithmetic operations.""" - -from typing import Optional, Union - - -def add(a: float, b: float) -> float: - """Add two numbers together. - - Args: - a: First number - b: Second number - - Returns: - Sum of a and b - """ - return a + b - - -def subtract(a: float, b: float) -> float: - """Subtract second number from first. - - Args: - a: First number - b: Number to subtract - - Returns: - Difference of a and b - """ - return a - b - - -def multiply(a: float, b: float) -> float: - """Multiply two numbers. - - Args: - a: First number - b: Second number - - Returns: - Product of a and b - """ - return a * b # Optimized from loop - - -def divide(a: float, b: float) -> Optional[float]: - """Divide first number by second. - - Args: - a: Dividend - b: Divisor - - Returns: - Quotient of a and b, or None if b is 0 - """ - if b == 0: - return None - return a / b - - -class Calculator: - """A simple calculator with memory functionality.""" - - def __init__(self) -> None: - """Initialize calculator with zero memory.""" - self.memory: float = 0 - - def add_to_memory(self, value: float) -> None: - """Add a value to memory. - - Args: - value: Value to add to memory - """ - self.memory += value # Optimized - - def clear_memory(self) -> None: - """Clear the memory to zero.""" - self.memory = 0 - - def get_memory(self) -> float: - """Get current memory value. - - Returns: - Current value in memory - """ - return self.memory - - def calculate(self, operation: str, a: float, b: float) -> Optional[float]: - """Perform a calculation based on the operation. - - Args: - operation: Type of operation ('add', 'subtract', 'multiply', 'divide') - a: First operand - b: Second operand - - Returns: - Result of the operation, or None if operation is invalid - """ - operations = { - "add": add, - "subtract": subtract, - "multiply": multiply, - "divide": divide - } - - if operation in operations: - return operations[operation](a, b) - return None -''' - - # Write the refactored content - with open(file_path, "w") as f: - f.write(refactored_content) - - # Create mock result - result = f"""Agent Network Execution Results (hanzo-agents SDK) -================================================================================ -Total agents: 4 -Completed: 4 -Failed: 0 -Entry point: docstring_agent - -Execution Order: docstring_agent โ†’ type_hint_agent โ†’ optimizer_agent โ†’ reviewer ----------------------------------------- - -Detailed Results: -================================================================================ - -### docstring_agent (Documentation specialist) [claude-3-5-sonnet] ----------------------------------------- -{mock_responses["docstring_agent"]} - -### type_hint_agent (Type annotation expert) [claude-3-5-sonnet] ----------------------------------------- -{mock_responses["type_hint_agent"]} - -### optimizer_agent (Code optimization expert) [claude-3-5-sonnet] ----------------------------------------- -{mock_responses["optimizer_agent"]} - -### reviewer (Senior code reviewer) [claude-3-5-sonnet] ----------------------------------------- -{mock_responses["reviewer"]}""" - - else: - # Execute with real hanzo-agents - result = await swarm.call( - ctx, - config=config, - query="Refactor the calculator.py file with proper documentation, type hints, and optimizations", - context="This is a simple calculator module that needs improvement", - ) - - print("\n=== SWARM EXECUTION RESULT ===") - print(result) - print("=" * 60) - - # Read refactored content - with open(file_path, "r") as f: - refactored_content = f.read() - - print("\n=== AFTER REFACTORING ===") - print(refactored_content) - print("=" * 60) - - # Verify improvements - assert ( - '"""' in refactored_content or "'''" in refactored_content - ) # Has docstrings - assert "->" in refactored_content # Has type hints - assert ( - "a * b" in refactored_content or "return a * b" in refactored_content - ) # Optimized multiply - assert ( - "typing" in refactored_content or "Optional" in refactored_content - ) # Uses typing module - - print("\nโœ“ All improvements verified:") - print(" - Docstrings added") - print(" - Type hints added") - print(" - Code optimized") - print(" - Code reviewed") - - @pytest.mark.asyncio - async def test_swarm_v2_fallback_mode(self, tool_helper, setup_test_env): - """Test swarm_tool_v2 fallback when hanzo-agents is not available.""" - test_dir, pm = setup_test_env - - # Create test file - file_path = os.path.join(test_dir, "test.py") - with open(file_path, "w") as f: - f.write("def hello():\n print('Hello')\n") - - # Create swarm tool - swarm = SwarmTool(permission_manager=pm) - ctx = Mock(spec=MCPContext) - - # Mock the tool context creation - mock_tool_ctx = Mock() - mock_tool_ctx.set_tool_info = AsyncMock() - mock_tool_ctx.error = AsyncMock() - mock_tool_ctx.warning = AsyncMock() - mock_tool_ctx.info = AsyncMock() - - # Since hanzo-agents is already imported at module level, - # we need to test the fallback behavior by patching inside the call method - # The swarm_tool_v2 imports from swarm_tool during runtime in the call method - - # Create a mock for the fallback SwarmTool - mock_original_class = Mock() - mock_original_instance = Mock() - mock_original_instance.call = AsyncMock( - return_value="Fallback result from original SwarmTool" - ) - mock_original_class.return_value = mock_original_instance - - # Patch where the import happens inside the call method - with patch( - "hanzo_mcp.tools.common.context.create_tool_context", - return_value=mock_tool_ctx, - ): - with patch.dict( - "sys.modules", - {"hanzo_tools.agent.swarm_tool": Mock(SwarmTool=mock_original_class)}, - ): - with patch.dict(os.environ, {"MOCK_HANZO_AGENTS": "true"}): - result = await swarm.call( - ctx, - config={"agents": {"test": {"query": "test"}}}, - query="test query", - ) - - # The result should come from the fallback - tool_helper.assert_in_result( - "Fallback result from original SwarmTool", result - ) - print("โœ“ Fallback to original SwarmTool works correctly") - - @pytest.mark.asyncio - async def test_swarm_v2_error_handling(self, tool_helper, setup_test_env): - """Test error handling in swarm_tool_v2.""" - test_dir, pm = setup_test_env - - swarm = SwarmTool(permission_manager=pm) - ctx = MCPContext() - - # Test with no agents - result = await swarm.call(ctx, config={"agents": {}}, query="test") - - tool_helper.assert_in_result("Error:", result) - assert "at least one agent" in result.lower() - print("โœ“ Proper error handling for empty agent config") - - @pytest.mark.asyncio - async def test_swarm_v2_network_topologies(self, tool_helper, setup_test_env): - """Test different network topologies.""" - test_dir, pm = setup_test_env - - # Create multiple test files - files = {} - for name in ["module1.py", "module2.py", "module3.py"]: - file_path = os.path.join(test_dir, name) - with open(file_path, "w") as f: - f.write(f"# {name}\ndef func():\n pass\n") - files[name] = file_path - - swarm = SwarmTool(permission_manager=pm) - ctx = MCPContext() - - # Test star topology (coordinator pattern) - star_config = { - "agents": { - "coordinator": { - "query": "Coordinate refactoring of all modules", - "role": "Lead architect", - "connections": ["module1_agent", "module2_agent", "module3_agent"], - }, - "module1_agent": { - "query": "Refactor module1.py", - "role": "Module 1 specialist", - "file_path": files["module1.py"], - "receives_from": ["coordinator"], - "connections": ["final_reviewer"], - }, - "module2_agent": { - "query": "Refactor module2.py", - "role": "Module 2 specialist", - "file_path": files["module2.py"], - "receives_from": ["coordinator"], - "connections": ["final_reviewer"], - }, - "module3_agent": { - "query": "Refactor module3.py", - "role": "Module 3 specialist", - "file_path": files["module3.py"], - "receives_from": ["coordinator"], - "connections": ["final_reviewer"], - }, - "final_reviewer": { - "query": "Review all module changes", - "role": "Final reviewer", - "receives_from": [ - "module1_agent", - "module2_agent", - "module3_agent", - ], - }, - }, - "entry_point": "coordinator", - "topology": "star", - } - - # Mock execution for testing - mock_agents = os.environ.get("MOCK_HANZO_AGENTS", "false").lower() == "true" - if mock_agents: - result = "Agent Network Execution Results (hanzo-agents SDK)\n" - result += "Star topology test completed" - else: - result = await swarm.call( - ctx, - config=star_config, - query="Refactor all modules with consistent style", - ) - - print("\n=== STAR TOPOLOGY TEST ===") - print(result) - tool_helper.assert_in_result("Agent Network Execution Results", result) - print("โœ“ Star topology configuration works") - - -def run_comprehensive_tests(): - """Run comprehensive tests without pytest.""" - print("\n" + "=" * 80) - print("COMPREHENSIVE SWARM V2 TESTS") - print("=" * 80) - - # Create test instance - test = TestSwarmV2Comprehensive() - - # Set up test environment manually - test_dir = tempfile.mkdtemp() - pm = PermissionManager() - pm.add_allowed_path(test_dir) - - try: - # Test 1: Create test file - print("\n1. Creating test file...") - file_path = test.create_test_file(test_dir) - print(f"โœ“ Created test file: {file_path}") - - # Test 2: Check swarm availability - print("\n2. Checking hanzo-agents availability...") - print("hanzo-agents available: True") # Always available now - - # Test 3: Create swarm instance - print("\n3. Creating SwarmTool instance...") - swarm = SwarmTool(permission_manager=pm) - print(f"โœ“ SwarmTool created with model: {swarm.model}") - - # Test 4: Verify tool registration - print("\n4. Verifying tool properties...") - print(f"Tool name: {swarm.name}") - print(f"Tool description preview: {swarm.description[:100]}...") - - print("\n" + "=" * 80) - print("Basic tests completed successfully!") - print("Run with pytest for full async tests including agent execution.") - - finally: - # Cleanup - shutil.rmtree(test_dir) - - -if __name__ == "__main__": - # Run basic synchronous tests - run_comprehensive_tests() - - print("\n" + "=" * 80) - print("To run full async tests with agent execution:") - print("pytest -xvs tests/test_swarm/test_swarm_v2_comprehensive.py") - print("=" * 80) diff --git a/pkg/hanzo-mcp/tests/test_swarm_simple.py b/pkg/hanzo-mcp/tests/test_swarm_simple.py deleted file mode 100644 index 1816ea40a..000000000 --- a/pkg/hanzo-mcp/tests/test_swarm_simple.py +++ /dev/null @@ -1,275 +0,0 @@ -#!/usr/bin/env python3 -"""Simple standalone test for swarm tool functionality.""" - -import os -import shutil -import sys -import tempfile - - -# Test 1: Check swarm defaults -def test_defaults(): - """Test that swarm defaults to Claude Sonnet.""" - print("Test 1: Checking swarm defaults...") - - # Simulate the swarm tool initialization - model = None - api_key = None - - # Default behavior - model = model or "anthropic/claude-3-5-sonnet-20241022" - api_key = ( - api_key - or os.environ.get("ANTHROPIC_API_KEY") - or os.environ.get("CLAUDE_API_KEY") - ) - - assert model == "anthropic/claude-3-5-sonnet-20241022", ( - f"Expected Claude Sonnet, got {model}" - ) - print("โœ“ Swarm defaults to Claude 3.5 Sonnet") - - # Check API key detection - if api_key: - print(f"โœ“ API key detected: {api_key[:10]}...") - else: - print("โœ— No API key found (set ANTHROPIC_API_KEY or CLAUDE_API_KEY)") - - return True - - -# Test 2: Test task validation -def test_task_validation(): - """Test task validation logic.""" - print("\nTest 2: Task validation...") - - # Valid task - valid_task = { - "file_path": "/path/to/file.py", - "instructions": "Update imports", - "description": "Optional description", - } - - # Invalid tasks - invalid_tasks = [ - {"instructions": "Missing file_path"}, - {"file_path": "/path/to/file.py"}, # Missing instructions - "Not a dictionary", - None, - [], - ] - - # Simulate validation - def validate_task(task): - if not isinstance(task, dict): - return False, "Task must be a dictionary" - if "file_path" not in task: - return False, "Task must have 'file_path'" - if "instructions" not in task: - return False, "Task must have 'instructions'" - return True, "Valid" - - # Test valid task - is_valid, msg = validate_task(valid_task) - assert is_valid, f"Valid task failed: {msg}" - print("โœ“ Valid task passes validation") - - # Test invalid tasks - for i, task in enumerate(invalid_tasks): - is_valid, msg = validate_task(task) - assert not is_valid, f"Invalid task {i} should have failed" - print(f"โœ“ Invalid task {i} correctly rejected: {msg}") - - return True - - -# Test 3: Test parallel execution simulation -def test_parallel_execution(): - """Test parallel execution behavior.""" - print("\nTest 3: Parallel execution simulation...") - - import asyncio - import time - - async def simulate_agent_task(task_id, duration=0.1): - """Simulate an agent task.""" - start = time.time() - await asyncio.sleep(duration) - end = time.time() - return task_id, end - start - - async def run_parallel_test(): - # Create 5 tasks - max_concurrent = 3 - - # Semaphore to limit concurrency - semaphore = asyncio.Semaphore(max_concurrent) - - async def limited_task(task_id): - async with semaphore: - return await simulate_agent_task(task_id) - - # Run all tasks - start = time.time() - await asyncio.gather(*[limited_task(i) for i in range(5)]) - total_time = time.time() - start - - print(f"โœ“ Ran 5 tasks with max_concurrent=3 in {total_time:.2f}s") - - # With max_concurrent=3 and 0.1s per task, should take ~0.2s (2 batches) - assert total_time < 0.6, f"Parallel execution too slow: {total_time}s" - print("โœ“ Parallel execution completed efficiently") - - return True - - # Run async test - return asyncio.run(run_parallel_test()) - - -# Test 4: Test failure handling -def test_failure_handling(): - """Test handling of failures.""" - print("\nTest 4: Failure handling...") - - # Simulate task results - results = [ - ("Task 1", "Success: Completed edit"), - ("Task 2", "Error: File not found"), - ("Task 3", "Success: Updated imports"), - ("Task 4", "Error: Permission denied"), - ("Task 5", "Success: Added type hints"), - ] - - # Count successes and failures - successful = sum(1 for _, result in results if not result.startswith("Error:")) - failed = len(results) - successful - - assert successful == 3, f"Expected 3 successful, got {successful}" - assert failed == 2, f"Expected 2 failed, got {failed}" - print(f"โœ“ Correctly counted {successful} successful and {failed} failed tasks") - - # Format summary (like swarm does) - summary = f"""=== Swarm Execution Summary === -Total tasks: {len(results)} -Successful: {successful} -Failed: {failed} -""" - - print("โœ“ Summary formatting works correctly") - print(summary) - - return True - - -# Test 5: Test file operations -def test_file_operations(): - """Test file creation and editing simulation.""" - print("\nTest 5: File operations simulation...") - - test_dir = tempfile.mkdtemp(prefix="swarm_test_") - try: - # Create test files - files = { - "config.py": "OLD_VALUE = 123", - "utils.py": "from config import OLD_VALUE", - "main.py": "print(OLD_VALUE)", - } - - for filename, content in files.items(): - path = os.path.join(test_dir, filename) - with open(path, "w") as f: - f.write(content) - print(f"โœ“ Created {filename}") - - # Simulate edits - edits = { - "config.py": "NEW_VALUE = 123", - "utils.py": "from config import NEW_VALUE", - "main.py": "print(NEW_VALUE)", - } - - for filename, new_content in edits.items(): - path = os.path.join(test_dir, filename) - with open(path, "w") as f: - f.write(new_content) - print(f"โœ“ Edited {filename}") - - # Verify edits - for filename in files: - path = os.path.join(test_dir, filename) - with open(path, "r") as f: - content = f.read() - assert "NEW_VALUE" in content, f"{filename} not properly edited" - - print("โœ“ All files edited successfully") - return True - - finally: - shutil.rmtree(test_dir) - print("โœ“ Cleaned up test directory") - - -# Main test runner -def main(): - """Run all tests.""" - print("=" * 60) - print("SWARM TOOL TEST SUITE") - print("=" * 60) - - tests = [ - test_defaults, - test_task_validation, - test_parallel_execution, - test_failure_handling, - test_file_operations, - ] - - passed = 0 - failed = 0 - - for test in tests: - try: - if test(): - passed += 1 - else: - failed += 1 - print(f"โœ— {test.__name__} failed") - except Exception as e: - failed += 1 - print(f"โœ— {test.__name__} crashed: {e}") - - print("\n" + "=" * 60) - print(f"RESULTS: {passed} passed, {failed} failed") - print("=" * 60) - - # Also test the actual demo scripts if API key is available - if os.environ.get("ANTHROPIC_API_KEY") or os.environ.get("CLAUDE_API_KEY"): - print("\nAPI key detected! Running live demos...") - - # Run parallel edit demo - demo_path = os.path.join( - os.path.dirname(__file__), "..", "examples", "parallel_edit_demo.py" - ) - if os.path.exists(demo_path): - print("\nRunning parallel_edit_demo.py...") - import subprocess - - result = subprocess.run( - [sys.executable, demo_path], capture_output=True, text=True - ) - if result.returncode == 0: - print("โœ“ Parallel edit demo completed successfully") - if "NEW_VERSION" in result.stdout: - print("โœ“ Variables were successfully renamed") - else: - print(f"โœ— Parallel edit demo failed: {result.stderr}") - else: - print("\nNo API key found - skipping live demos") - print("Set ANTHROPIC_API_KEY or CLAUDE_API_KEY to run live tests") - - return failed == 0 - - -if __name__ == "__main__": - success = main() - sys.exit(0 if success else 1) diff --git a/pkg/hanzo-mcp/tests/test_tools_suite.py b/pkg/hanzo-mcp/tests/test_tools_suite.py deleted file mode 100644 index 970ecf296..000000000 --- a/pkg/hanzo-mcp/tests/test_tools_suite.py +++ /dev/null @@ -1,877 +0,0 @@ -"""Comprehensive test suite for all MCP tools. - -Following Guido van Rossum's Python philosophy: -- 'Testing shows the presence, not the absence of bugs' -- 'Practicality beats purity' -- 'Errors should never pass silently' -- 'In the face of ambiguity, refuse the temptation to guess' -""" - -import asyncio -from unittest.mock import Mock, patch - -import pytest -from mcp.server.fastmcp import FastMCP - -from hanzo_mcp.tools import register_all_tools -from hanzo_mcp.tools.common.fastmcp_pagination import FastMCPPaginator -from hanzo_mcp.tools.common.tool_list import ToolListTool -from hanzo_mcp.tools.common.truncate import truncate_response - -# Property-based testing imports -try: - from hypothesis import assume, example, given, settings - from hypothesis import strategies as st - - HYPOTHESIS_AVAILABLE = True -except ImportError: - HYPOTHESIS_AVAILABLE = False - - # Create stubs so tests still run - def given(*args, **kwargs): - def decorator(func): - return func - - return decorator - - class st: - @staticmethod - def text(*args, **kwargs): - return None - - @staticmethod - def integers(*args, **kwargs): - return None - - @staticmethod - def lists(*args, **kwargs): - return None - - @staticmethod - def booleans(): - return None - - def settings(*args, **kwargs): - def decorator(func): - return func - - return decorator - - def assume(*args): - pass - - def example(*args, **kwargs): - def decorator(func): - return func - - return decorator - - -try: - # Try to import test helper version first - from hanzo_mcp.tools.common.test_helpers import PaginatedResponse -except ImportError: - # Fall back to real implementation - from hanzo_mcp.tools.common.paginated_response import ( - AutoPaginatedResponse as PaginatedResponse, - ) - -from tests.test_utils import create_mock_ctx, create_permission_manager - - -class TestToolRegistration: - """Test tool registration and configuration.""" - - def test_register_all_tools_default(self): - """Test registering all tools with default settings.""" - mcp_server = FastMCP("test-server") - permission_manager = create_permission_manager(["/tmp"]) - - # Register all tools - register_all_tools( - mcp_server, - permission_manager, - use_mode=False, # Disable mode system for predictable testing - ) - - # Check that tools are registered - # Note: We can't directly check mcp_server's internal state, - # but we can verify no exceptions were raised - assert True # Registration completed - - def test_register_tools_with_disabled_categories(self): - """Test disabling entire categories of tools.""" - mcp_server = FastMCP("test-server") - permission_manager = create_permission_manager(["/tmp"]) - - # Register with write tools disabled - register_all_tools( - mcp_server, - permission_manager, - disable_write_tools=True, - disable_search_tools=True, - use_mode=False, - ) - - # Tools should still register, just with some disabled - assert True - - def test_register_tools_with_individual_config(self): - """Test enabling/disabling individual tools.""" - mcp_server = FastMCP("test-server") - permission_manager = create_permission_manager(["/tmp"]) - - # Enable only specific tools - enabled_tools = { - "read": True, - "write": False, - "grep": True, - "run_command": False, - "think": True, - "agent": False, - } - - register_all_tools( - mcp_server, permission_manager, enabled_tools=enabled_tools, use_mode=False - ) - - assert True - - @patch("hanzo_tools.agent.AgentTool") - def test_agent_tool_configuration(self, mock_agent_tool): - """Test agent tool configuration.""" - mcp_server = FastMCP("test-server") - permission_manager = create_permission_manager(["/tmp"]) - - # Mock agent tool to verify configuration - mock_instance = Mock() - mock_agent_tool.return_value = mock_instance - - register_all_tools( - mcp_server, - permission_manager, - enable_agent_tool=True, - agent_model="claude-3-5-sonnet-20241022", - agent_max_tokens=8192, - agent_api_key="test_key", - agent_base_url="https://api.example.com", - agent_max_iterations=15, - agent_max_tool_uses=50, - use_mode=False, - ) - - # Verify agent tool was configured - mock_agent_tool.assert_called_once() - call_kwargs = mock_agent_tool.call_args.kwargs - # Check keyword args - assert call_kwargs["permission_manager"] == permission_manager - assert call_kwargs["model"] == "claude-3-5-sonnet-20241022" - assert call_kwargs["max_tokens"] == 8192 - - -class TestPaginationSystem: - """Test the pagination system for large outputs.""" - - def test_truncate_response(self): - """Test output truncation.""" - # Test small output (no truncation) - small_output = "Small output" - result = truncate_response(small_output, max_tokens=1000) - assert result == small_output - - # Test large output (truncation) - large_output = "x" * 100000 # Very large output - result = truncate_response(large_output, max_tokens=100) - assert len(result) < len(large_output) - assert "truncated" in result.lower() - - def test_paginated_response(self): - """Test paginated response creation.""" - # Create test data - items = [f"Item {i}" for i in range(100)] - - # Create paginated response using wrapper - response = PaginatedResponse( - items=items[:10], next_cursor="cursor_10", has_more=True, total_items=100 - ) - - # Check response attributes - assert len(response.items) == 10 - assert response.next_cursor == "cursor_10" - assert response.has_more is True - assert response.total_items == 100 - - # Test JSON serialization - json_data = response.to_json() - assert json_data["items"] == items[:10] - assert json_data["_meta"]["next_cursor"] == "cursor_10" - - def test_fastmcp_paginator(self): - """Test FastMCP paginator.""" - paginator = FastMCPPaginator(page_size=10) - - # Test paginating a list - items = [f"item_{i}" for i in range(100)] - - # Get first page - result = paginator.paginate_list(items, cursor=None, page_size=10) - - assert result is not None - assert "items" in result - assert len(result["items"]) == 10 - assert result["items"][0] == "item_0" - - # Check if there's a next cursor - if "nextCursor" in result: - # Get next page using cursor - next_result = paginator.paginate_list( - items, cursor=result["nextCursor"], page_size=10 - ) - assert next_result is not None - assert "items" in next_result - - -class TestToolListFunctionality: - """Test tool listing functionality.""" - - def test_tool_list_basic(self): - """Test basic tool listing.""" - tool = ToolListTool() - - # Mock context - mock_ctx = create_mock_ctx() - mock_ctx.meta = {"disabled_tools": set()} - - # Get tool list - result = asyncio.run(tool.call(mock_ctx)) - - # Should return a formatted list - assert "Available Tools" in str(result) or "Available tools:" in str(result) - assert "Total tools:" in str(result) or "Enabled:" in str(result) - - def test_tool_list_with_disabled(self): - """Test tool list with disabled tools.""" - tool = ToolListTool() - - # Mock context with disabled tools - mock_ctx = create_mock_ctx() - mock_ctx.meta = {"disabled_tools": {"write", "edit"}} - - # Get tool list - result = asyncio.run(tool.call(mock_ctx)) - - # Should show disabled tools or summary - assert "Disabled:" in str(result) or "disabled_tools" in str(mock_ctx.meta) - # Note: Actual disabled tools depend on what's registered - - -class TestCLIAgentTools: - """Test CLI-based agent tools.""" - - def test_claude_cli_tool(self): - """Test Claude CLI tool.""" - from hanzo_tools.agent.claude_cli_tool import ClaudeCLITool - - permission_manager = create_permission_manager(["/tmp"]) - tool = ClaudeCLITool(permission_manager) - assert tool.name == "claude_cli" - assert tool.command_name == "claude" - assert "Claude Code" in tool.provider_name - - def test_codex_cli_tool(self): - """Test Codex CLI tool.""" - from hanzo_tools.agent.codex_cli_tool import CodexCLITool - - permission_manager = create_permission_manager(["/tmp"]) - tool = CodexCLITool(permission_manager) - assert tool.name == "codex_cli" - assert tool.command_name == "openai" - assert "OpenAI" in tool.provider_name - - def test_gemini_cli_tool(self): - """Test Gemini CLI tool.""" - from hanzo_tools.agent.gemini_cli_tool import GeminiCLITool - - permission_manager = create_permission_manager(["/tmp"]) - tool = GeminiCLITool(permission_manager) - assert tool.name == "gemini_cli" - assert tool.command_name == "gemini" - assert "Google Gemini" in tool.provider_name - - def test_grok_cli_tool(self): - """Test Grok CLI tool.""" - from hanzo_tools.agent.grok_cli_tool import GrokCLITool - - permission_manager = create_permission_manager(["/tmp"]) - tool = GrokCLITool(permission_manager) - assert tool.name == "grok_cli" - assert tool.command_name == "grok" - assert "xAI Grok" in tool.provider_name - - -class TestSwarmTool: - """Test swarm tool functionality.""" - - def test_swarm_basic_configuration(self): - """Test basic swarm configuration.""" - from hanzo_tools.agent.swarm_tool import SwarmTool - - permission_manager = create_permission_manager(["/tmp"]) - tool = SwarmTool(permission_manager) - - # Test basic properties - assert tool.name == "swarm" - assert "network of AI agents" in tool.description - - # Test that tool can be instantiated - assert tool is not None - - -class TestMemoryIntegration: - """Test memory tools integration.""" - - def test_memory_tools_available(self): - """Test that memory tools registration works correctly. - - Following Guido's principle: 'Practicality beats purity.' - We test that the memory tools module handles missing dependencies gracefully. - """ - # Test that memory tools handle missing dependencies gracefully - try: - from hanzo_tools.memory import memory_tools - - # Check that module has expected lazy-loading infrastructure - assert hasattr(memory_tools, "MEMORY_AVAILABLE") - assert hasattr(memory_tools, "_check_memory_available") - - # Call the check function to initialize the lazy state - is_available = memory_tools._check_memory_available() - # After calling, MEMORY_AVAILABLE should be set (True or False, not None) - assert memory_tools.MEMORY_AVAILABLE is not None - assert memory_tools.MEMORY_AVAILABLE == is_available - - # Test that we can access the base class - assert hasattr(memory_tools, "MemoryToolBase") - - # Test that we can register tools - from hanzo_tools.memory import register_memory_tools - - mcp_server = FastMCP("test-server") - permission_manager = create_permission_manager(["/tmp"]) - tools = register_memory_tools( - mcp_server, permission_manager, user_id="test", project_id="test" - ) - assert isinstance(tools, list) - - except ImportError as e: - # This is the expected path when hanzo_memory is not installed - error_msg = str(e) - if ( - "hanzo-memory package is required" in error_msg - or "hanzo_memory" in error_msg - ): - # This is expected and valid - memory tools require the package - print( - f"Memory tools correctly require hanzo-memory package: {error_msg}" - ) - assert True # Test passes - correct behavior - else: - # Some other import error - this is not expected - raise - - -class TestNetworkPackage: - """Test hanzo-network package integration.""" - - def test_network_imports(self): - """Test that network package can be imported. - - Guido's Zen: 'In the face of ambiguity, refuse the temptation to guess.' - We explicitly mock what we need for reliable testing. - """ - - # Create mock classes that behave like the real ones - class MockAgent: - def __init__(self, id=None, instructions=None, **kwargs): - self.id = id - self.instructions = instructions - self.__dict__.update(kwargs) - - class MockTool: - def __init__(self, name=None, **kwargs): - self.name = name - self.__dict__.update(kwargs) - - class MockRouter: - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - - class MockNetwork: - def __init__(self, agents=None, **kwargs): - self.agents = agents or [] - self.__dict__.update(kwargs) - - class MockNetworkState: - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - - # Mock the hanzo_network module - mock_network_module = Mock() - mock_network_module.Agent = MockAgent - mock_network_module.Tool = MockTool - mock_network_module.Router = MockRouter - mock_network_module.Network = MockNetwork - mock_network_module.NetworkState = MockNetworkState - - with patch.dict("sys.modules", {"hanzo_network": mock_network_module}): - from hanzo_network import Agent, Network, NetworkState, Router, Tool - - # Test that all classes are available and functional - assert Agent is not None - assert Network is not None - assert Router is not None - assert NetworkState is not None - assert Tool is not None - - # Test instantiation - test_agent = Agent(id="test", instructions="test") - assert test_agent.id == "test" - - test_network = Network(agents=[test_agent]) - assert len(test_network.agents) == 1 - - def test_network_agent_creation(self): - """Test creating a network agent. - - Guido's philosophy: 'Simple is better than complex.' - Test the interface, not the implementation. - """ - - # Mock Agent class with expected interface - class MockAgent: - def __init__(self, id, instructions, **kwargs): - self.id = id - self.instructions = instructions - self.tools = kwargs.get("tools", []) - self.model = kwargs.get("model", "gpt-4") - - mock_network_module = Mock() - mock_network_module.Agent = MockAgent - - with patch.dict("sys.modules", {"hanzo_network": mock_network_module}): - from hanzo_network import Agent - - # Test basic agent creation - agent = Agent(id="test_agent", instructions="Test instructions") - assert agent.id == "test_agent" - assert agent.instructions == "Test instructions" - - # Test agent with additional parameters - agent_with_tools = Agent( - id="advanced_agent", - instructions="Advanced instructions", - tools=["tool1", "tool2"], - model="claude-3", - ) - assert agent_with_tools.id == "advanced_agent" - assert len(agent_with_tools.tools) == 2 - assert agent_with_tools.model == "claude-3" - - -class TestAutoBackgrounding: - """Test auto-backgrounding functionality. - - Guido's principle: 'Errors should never pass silently.' - Test error conditions explicitly. - """ - - def test_auto_background_timeout(self): - """Test that long-running processes auto-background.""" - from hanzo_tools.shell.auto_background import AutoBackgroundExecutor - from hanzo_tools.shell.base_process import ProcessManager - - process_manager = ProcessManager() - executor = AutoBackgroundExecutor( - process_manager, timeout=0.1 - ) # Very short timeout - - # Test that executor is created properly - assert executor is not None - assert executor.default_timeout == 0.1 - assert executor.process_manager == process_manager - - # Test has the expected method - assert hasattr(executor, "execute_with_auto_background") - - def test_auto_background_edge_cases(self): - """Test edge cases for auto-backgrounding. - - Guido: 'Special cases aren't special enough to break the rules.' - """ - from hanzo_tools.shell.auto_background import AutoBackgroundExecutor - from hanzo_tools.shell.base_process import ProcessManager - - process_manager = ProcessManager() - - # Test with zero timeout - executor_zero = AutoBackgroundExecutor(process_manager, timeout=0) - assert executor_zero.default_timeout == 0 - - # Test with very large timeout - executor_large = AutoBackgroundExecutor(process_manager, timeout=float("inf")) - assert executor_large.default_timeout == float("inf") - - # Test with negative timeout (should handle gracefully) - executor_negative = AutoBackgroundExecutor(process_manager, timeout=-1) - assert ( - executor_negative.default_timeout == -1 - ) # Should accept but handle internally - - def test_process_manager_singleton(self): - """Test that ProcessManager is a proper singleton. - - Guido: 'There should be one-- and preferably only one --obvious way to do it.' - ProcessManager uses the singleton pattern for global process tracking. - """ - from hanzo_tools.shell.base_process import ProcessManager - - pm1 = ProcessManager() - pm2 = ProcessManager() - - # Singleton pattern: instances should be the same - assert pm1 is pm2 - - # Test that both share the same state - test_id = "test_process_123" - pm1.add_process(test_id, Mock(), "/tmp/test.log") - - # Should be accessible from pm2 - assert pm2.get_process(test_id) is not None - - # Clean up - pm1.remove_process(test_id) - assert pm2.get_process(test_id) is None - - -class TestCriticAndReviewTools: - """Test critic and review tools.""" - - def test_critic_tool_basic(self): - """Test critic tool basic functionality.""" - from hanzo_mcp.tools.common.critic_tool import CriticTool - - tool = CriticTool() - mock_ctx = create_mock_ctx() - - # Test with analysis parameter - result = asyncio.run( - tool.call( - mock_ctx, - analysis="Review this function: def add(a, b): return a + b", - ) - ) - - # Should return analysis result - assert result is not None - assert isinstance(result, str) - assert len(result) > 0 - - def test_review_tool_basic(self): - """Test review tool basic functionality.""" - from hanzo_tools.agent.review_tool import ReviewTool - - tool = ReviewTool() - mock_ctx = create_mock_ctx() - - # Test review with call method - result = asyncio.run( - tool.call( - mock_ctx, - focus="general", - work_description="Test code implementation", - code_snippets=["def test(): pass"], - ) - ) - - # Should return review result - assert result is not None - assert isinstance(result, str) - assert len(result) > 0 - - -class TestStreamingCommand: - """Test streaming command functionality.""" - - def test_streaming_command_basic(self): - """Test basic streaming command.""" - from hanzo_tools.shell.streaming_command import StreamingCommandTool - - # Test that the abstract class exists and has expected properties - assert StreamingCommandTool is not None - assert hasattr(StreamingCommandTool, "__abstractmethods__") - - # Create a concrete implementation for testing - class ConcreteStreamingCommand(StreamingCommandTool): - @property - def name(self): - return "test_streaming" - - @property - def description(self): - return "Test streaming command" - - def register(self, server): - pass - - # Test the concrete implementation (without permission_manager) - tool = ConcreteStreamingCommand() - assert tool.name == "test_streaming" - assert tool.description == "Test streaming command" - - -class TestBatchTool: - """Test batch tool with pagination.""" - - def test_batch_tool_pagination(self): - """Test that batch tool handles pagination correctly.""" - from hanzo_mcp.tools.common.batch_tool import BatchTool - - # Create mock tools that return large outputs - mock_tools = {} - for i in range(5): - tool = Mock() - tool.name = f"tool_{i}" - # Large output that would exceed token limit - tool.call = Mock(return_value="x" * 10000) - mock_tools[f"tool_{i}"] = tool - - batch_tool = BatchTool(mock_tools) - mock_ctx = create_mock_ctx() - - # Execute batch with multiple tools - invocations = [{"tool": f"tool_{i}", "parameters": {}} for i in range(5)] - - result = asyncio.run( - batch_tool.call(mock_ctx, description="Test batch", invocations=invocations) - ) - - # Should handle without error - assert "results" in result or "error" in result - - -class TestPropertyBasedTruncation: - """Property-based tests for output truncation. - - Guido: 'Testing shows the presence, not the absence of bugs.' - We test with various edge cases to ensure robustness. - """ - - def test_truncation_never_exceeds_limit(self): - """Test that truncation never exceeds the specified limit.""" - # Test with various sizes - test_cases = [ - ("x" * 100000, 1000), - ("", 100), # Empty string - ("a", 1), # Single char - ("๐Ÿš€" * 10000, 500), # Unicode - ] - - for text, max_tokens in test_cases: - result = truncate_response(text, max_tokens=max_tokens) - - # Result should be reasonably sized - # Note: We can't guarantee exact token count, but should be reasonable - assert isinstance(result, str) - - # If original was very large, result should be truncated - if len(text) > 10000: - assert len(result) < len(text) - assert "truncated" in result.lower() or "..." in result - - def test_truncation_handles_all_text(self): - """Test that truncation handles any valid text input.""" - test_texts = [ - "test" * 100, - "", # Empty - "a", # Single - "๐Ÿš€" * 1000, # Unicode - "Hello\nWorld\n", # Newlines - "\t\t \n\n", # Whitespace - ] - - for text in test_texts: - # Should never raise an exception - result = truncate_response(text, max_tokens=100) - assert isinstance(result, str) - - -class TestPropertyBasedPagination: - """Property-based tests for pagination. - - Guido: 'Explicit is better than implicit.' - Test that pagination behavior is explicit and predictable. - """ - - def test_pagination_consistency(self): - """Test that pagination is consistent across different data sizes.""" - test_cases = [ - (0, 10), # No items - (5, 10), # Less than one page - (10, 10), # Exactly one page - (25, 10), # Multiple pages - (100, 7), # Odd page size - (1000, 50), # Large dataset - ] - - for num_items, page_size in test_cases: - paginator = FastMCPPaginator(page_size=page_size) - items = [f"item_{i}" for i in range(num_items)] - - # Collect all pages - all_retrieved = [] - cursor = None - pages_retrieved = 0 - max_pages = (num_items + page_size - 1) // page_size + 1 # Safety limit - - while pages_retrieved < max_pages: - result = paginator.paginate_list( - items, cursor=cursor, page_size=page_size - ) - if not result or "items" not in result: - break - - all_retrieved.extend(result["items"]) - pages_retrieved += 1 - - # Check for next cursor - if "nextCursor" not in result or not result["nextCursor"]: - break - cursor = result["nextCursor"] - - # Should retrieve all items exactly once - assert len(all_retrieved) == num_items - if num_items > 0: - assert all_retrieved == items - - -class TestPropertyBasedToolConfig: - """Property-based tests for tool configuration. - - Guido: 'There should be one-- and preferably only one --obvious way to do it.' - """ - - def test_tool_registration_combinations(self): - """Test that any combination of tool settings works.""" - # Test various combinations - test_cases = [ - (True, True, False, 1024), # All enabled except agent - (False, False, True, 8192), # Only agent enabled - (True, False, False, 4096), # Only write enabled - (False, True, False, 2048), # Only search enabled - (True, True, True, 16384), # Everything enabled - (False, False, False, 512), # Nothing enabled - ] - - for enable_write, enable_search, enable_agent, max_tokens in test_cases: - mcp_server = FastMCP( - f"test-server-{enable_write}-{enable_search}-{enable_agent}" - ) - permission_manager = create_permission_manager(["/tmp"]) - - # Should never raise an exception - register_all_tools( - mcp_server, - permission_manager, - disable_write_tools=not enable_write, - disable_search_tools=not enable_search, - enable_agent_tool=enable_agent, - agent_max_tokens=max_tokens, - use_mode=False, - ) - - # Registration should always succeed - assert True - - -class TestEdgeCasesAndRobustness: - """Test edge cases and robustness. - - Guido: 'Errors should never pass silently.' - """ - - def test_unicode_handling(self): - """Test that all tools handle Unicode correctly.""" - unicode_strings = [ - "Hello ไธ–็•Œ ๐ŸŒ", - "Emoji test: ๐Ÿš€๐Ÿ”ฅ๐Ÿ’ป", - "Math symbols: โˆ‘โˆโˆซโˆž", - "Accents: cafรฉ, naรฏve, rรฉsumรฉ", - "RTL text: ู…ุฑุญุจุง ุจุงู„ุนุงู„ู…", - "Zero-width: test\u200btest", - "Combining: รฉ (e + ฬ)", - ] - - for text in unicode_strings: - # Test truncation - result = truncate_response(text, max_tokens=100) - assert isinstance(result, str) - - # Test in paginated response - response = PaginatedResponse(items=[text], total_items=1) - json_data = response.to_json() - assert json_data["items"][0] == text - - def test_extreme_values(self): - """Test extreme values that might break assumptions.""" - # Test very large pagination - paginator = FastMCPPaginator(page_size=1000000) - result = paginator.paginate_list(["item"], page_size=1000000) - assert result["items"] == ["item"] - - # Test zero page size (should handle gracefully) - FastMCPPaginator(page_size=0) - # Should either handle or use default - - # Test negative values in auto-backgrounding - from hanzo_tools.shell.auto_background import AutoBackgroundExecutor - from hanzo_tools.shell.base_process import ProcessManager - - pm = ProcessManager() - # These should not crash - AutoBackgroundExecutor(pm, timeout=-999) - AutoBackgroundExecutor(pm, timeout=float("inf")) - # Note: float('nan') might cause issues, skip for now - - def test_concurrent_access(self): - """Test that singleton ProcessManager handles concurrent access. - - Guido: 'If the implementation is hard to explain, it's a bad idea.' - The singleton should be simple and thread-safe. - """ - import threading - - from hanzo_tools.shell.base_process import ProcessManager - - results = [] - - def get_manager(): - pm = ProcessManager() - results.append(id(pm)) - - # Create multiple threads - threads = [threading.Thread(target=get_manager) for _ in range(10)] - - # Start all threads - for t in threads: - t.start() - - # Wait for completion - for t in threads: - t.join() - - # All should get the same instance - assert len(set(results)) == 1, "ProcessManager singleton not thread-safe" - - -if __name__ == "__main__": - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_utils.py b/pkg/hanzo-mcp/tests/test_utils.py deleted file mode 100644 index 6454b8c30..000000000 --- a/pkg/hanzo-mcp/tests/test_utils.py +++ /dev/null @@ -1,402 +0,0 @@ -"""Common test utilities to DRY up the test suite. - -This module provides shared utilities, fixtures, and helpers to make tests -more maintainable and consistent. -""" - -import asyncio -import json -import os -import tempfile -from pathlib import Path -from typing import Any, Dict, List, Optional, Union -from unittest.mock import AsyncMock, MagicMock, Mock, patch - -import pytest - -try: - from fastmcp import FastMCP # type: ignore -except Exception: # pragma: no cover - test fallback - - class FastMCP: # minimal stub for tests when dependency is unavailable - def __init__(self, *args, **kwargs): - pass - - -# Provide a minimal stub for `mcp.server.FastMCP` if `mcp` is unavailable -try: # pragma: no cover - import guard for test runtime - import mcp # type: ignore -except Exception: # pragma: no cover - import sys - import types - - mcp = types.ModuleType("mcp") - server_mod = types.ModuleType("mcp.server") - fastmcp_mod = types.ModuleType("mcp.server.fastmcp") - lowlevel_pkg = types.ModuleType("mcp.server.lowlevel") - helper_types_mod = types.ModuleType("mcp.server.lowlevel.helper_types") - - class _FastMCP: # minimal placeholder - def __init__(self, *args, **kwargs): - pass - - server_mod.FastMCP = _FastMCP - - class _Context: - def __init__(self, *args, **kwargs): - pass - - fastmcp_mod.Context = _Context - mcp.server = server_mod # type: ignore[attr-defined] - sys.modules["mcp"] = mcp - sys.modules["mcp.server"] = server_mod - sys.modules["mcp.server.fastmcp"] = fastmcp_mod - - class _ReadResourceContents: # placeholder - pass - - helper_types_mod.ReadResourceContents = _ReadResourceContents - sys.modules["mcp.server.lowlevel"] = lowlevel_pkg - sys.modules["mcp.server.lowlevel.helper_types"] = helper_types_mod - - -# Create a mock context type for testing -class MCPContext: - """Mock MCP Context for testing.""" - - def __init__(self, **kwargs): - for k, v in kwargs.items(): - setattr(self, k, v) - - -try: - from hanzo_mcp.tools.common.base import BaseTool # type: ignore -except Exception: # pragma: no cover - fallback typing only - BaseTool = object # type: ignore - - -# Minimal local PermissionManager stub to avoid importing the full package in CI -class PermissionManager: # pragma: no cover - lightweight test stub - def __init__(self): - self._allowed_paths = set() - - def add_allowed_path(self, path: str) -> None: - self._allowed_paths.add(Path(path).resolve()) - - @property - def allowed_paths(self): - return self._allowed_paths - - def is_path_allowed(self, path: str) -> bool: - return True - - -# Common test markers -requires_hanzo_agents = pytest.mark.skipif( - "HANZO_AGENTS_AVAILABLE" not in globals() - or not globals()["HANZO_AGENTS_AVAILABLE"], - reason="hanzo-agents SDK not available", -) - -requires_memory_tools = pytest.mark.skipif( - "MEMORY_TOOLS_AVAILABLE" not in globals() - or not globals()["MEMORY_TOOLS_AVAILABLE"], - reason="hanzo-memory package not installed", -) - - -class TestContext: - """Enhanced MCP context for testing.""" - - def __init__(self): - self.mock = MagicMock(spec=MCPContext) - self.mock.request_id = "test-request-id" - self.mock.client_id = "test-client-id" - self.mock.info = AsyncMock() - self.mock.debug = AsyncMock() - self.mock.warning = AsyncMock() - self.mock.error = AsyncMock() - self.mock.report_progress = AsyncMock() - self.mock.read_resource = AsyncMock() - self.mock.get_tools = AsyncMock(return_value=[]) - self.mock.meta = {"disabled_tools": set()} - # Add tool context methods - self.mock.set_tool_info = AsyncMock() - self.mock.send_completion_ping = AsyncMock() - - @property - def ctx(self): - return self.mock - - -class ToolTestHelper: - """Helper for testing tools consistently.""" - - @staticmethod - def normalize_result(result: Any) -> str: - """Normalize tool results to string for testing. - - Handles: - - Dict results with 'output' key - - Dict results with 'content' key - - String results - - Other types converted to string - """ - if isinstance(result, dict): - # Check common output keys - for key in ["output", "content", "result", "data"]: - if key in result: - return str(result[key]) - # If no known key, stringify the whole dict - return json.dumps(result, default=str) - return str(result) - - @staticmethod - async def call_tool(tool: BaseTool, ctx: Any, **kwargs) -> str: - """Call a tool and normalize the result.""" - result = await tool.call(ctx, **kwargs) - return ToolTestHelper.normalize_result(result) - - @staticmethod - def assert_in_result(expected: str, result: Any, message: str = None): - """Assert that expected string is in the normalized result.""" - normalized = ToolTestHelper.normalize_result(result) - if message: - assert expected in normalized, f"{message}. Got: {normalized}" - else: - assert expected in normalized, ( - f"Expected '{expected}' in result. Got: {normalized}" - ) - - @staticmethod - def assert_success(result: Any): - """Assert that the result indicates success.""" - normalized = ToolTestHelper.normalize_result(result) - error_indicators = ["error", "failed", "exception", "Error:", "Failed:"] - for indicator in error_indicators: - assert indicator.lower() not in normalized.lower(), ( - f"Result indicates error: {normalized}" - ) - - -class FileSystemTestHelper: - """Helper for file system tests.""" - - @staticmethod - def create_test_directory(files: Dict[str, str]) -> tempfile.TemporaryDirectory: - """Create a temporary directory with test files. - - Args: - files: Dict mapping file paths to content - - Returns: - TemporaryDirectory context manager - """ - temp_dir = tempfile.TemporaryDirectory() - base_path = Path(temp_dir.name) - - for file_path, content in files.items(): - full_path = base_path / file_path - full_path.parent.mkdir(parents=True, exist_ok=True) - full_path.write_text(content) - - return temp_dir - - @staticmethod - def create_test_files(base_dir: Union[str, Path], files: Dict[str, str]): - """Create test files in an existing directory.""" - base_path = Path(base_dir) - - for file_path, content in files.items(): - full_path = base_path / file_path - full_path.parent.mkdir(parents=True, exist_ok=True) - full_path.write_text(content) - - -class MockServiceHelper: - """Helper for mocking external services.""" - - @staticmethod - def mock_memory_service(): - """Create a mock memory service.""" - mock_service = Mock() - mock_service.search_memories = Mock(return_value=[]) - mock_service.create_memory = Mock(return_value=Mock(memory_id="test-id")) - mock_service.delete_memory = Mock(return_value=True) - return mock_service - - @staticmethod - def mock_llm_completion(response: str = "Test response"): - """Create a mock llm completion.""" - mock_response = Mock() - mock_response.choices = [Mock(message=Mock(content=response))] - return Mock(return_value=mock_response) - - @staticmethod - def mock_subprocess_run(stdout: str = "", stderr: str = "", returncode: int = 0): - """Create a mock subprocess.run result.""" - return Mock(stdout=stdout, stderr=stderr, returncode=returncode) - - -class AsyncTestHelper: - """Helper for async testing.""" - - @staticmethod - def run_async(coro): - """Run an async coroutine in tests.""" - return asyncio.run(coro) - - @staticmethod - async def gather_results(tools: List[BaseTool], ctx: Any, **kwargs) -> List[str]: - """Run multiple tools in parallel and gather results.""" - tasks = [ToolTestHelper.call_tool(tool, ctx, **kwargs) for tool in tools] - results = await asyncio.gather(*tasks) - return results - - -# Fixture factories -def create_mock_ctx(): - """Create a mock MCP context.""" - return TestContext().ctx - - -def create_permission_manager(allowed_paths: Optional[List[str]] = None): - """Create a permission manager with optional allowed paths.""" - pm = PermissionManager() - if allowed_paths: - for path in allowed_paths: - pm.add_allowed_path(path) - else: - # Default to temp directory - pm.add_allowed_path("/tmp") - return pm - - -def create_test_server(name: str = "test-server"): - """Create a test MCP server.""" - return FastMCP(name) - - -# Common test patterns as decorators -def with_temp_dir(test_func): - """Decorator to provide a temporary directory to a test.""" - - def wrapper(*args, **kwargs): - with tempfile.TemporaryDirectory() as temp_dir: - return test_func(*args, temp_dir=temp_dir, **kwargs) - - return wrapper - - -def with_mock_service(service_name: str, mock_factory): - """Decorator to mock a service for a test.""" - - def decorator(test_func): - def wrapper(*args, **kwargs): - with patch(service_name, mock_factory()): - return test_func(*args, **kwargs) - - return wrapper - - return decorator - - -# Test data generators -class TestDataGenerator: - """Generate common test data.""" - - @staticmethod - def python_project_files() -> Dict[str, str]: - """Generate a simple Python project structure.""" - return { - "main.py": "def main():\n print('Hello, world!')\n\nif __name__ == '__main__':\n main()", - "requirements.txt": "requests==2.31.0\npytest==7.3.1\n", - "setup.py": "from setuptools import setup\n\nsetup(name='test-project', version='0.1.0')", - "src/__init__.py": "# Test package", - "src/utils.py": "def helper():\n return 42", - "tests/test_main.py": "def test_main():\n assert True", - } - - @staticmethod - def javascript_project_files() -> Dict[str, str]: - """Generate a simple JavaScript project structure.""" - return { - "index.js": "console.log('Hello, world!');", - "package.json": '{"name": "test-project", "version": "1.0.0", "main": "index.js"}', - "src/app.js": "export function app() { return 'app'; }", - "test/app.test.js": "import { app } from '../src/app.js';\ntest('app', () => expect(app()).toBe('app'));", - } - - @staticmethod - def mixed_files() -> Dict[str, str]: - """Generate mixed file types for testing.""" - return { - "readme.md": "# Test Project", - "data.json": '{"key": "value"}', - "config.yaml": "debug: true\nport: 3000", - ".env": "API_KEY=secret", - "script.sh": "#!/bin/bash\necho 'Hello'", - } - - -# Environment setup helpers -class TestEnvironment: - """Manage test environment setup and teardown.""" - - def __init__(self): - self.original_env = {} - self.temp_dirs = [] - - def set_env(self, key: str, value: str): - """Set an environment variable, saving the original.""" - if key not in self.original_env: - self.original_env[key] = os.environ.get(key) - os.environ[key] = value - - def restore_env(self): - """Restore original environment variables.""" - for key, value in self.original_env.items(): - if value is None: - os.environ.pop(key, None) - else: - os.environ[key] = value - - def create_temp_dir(self) -> Path: - """Create a temporary directory that will be cleaned up.""" - temp_dir = tempfile.mkdtemp() - self.temp_dirs.append(temp_dir) - return Path(temp_dir) - - def cleanup(self): - """Clean up all resources.""" - self.restore_env() - for temp_dir in self.temp_dirs: - if os.path.exists(temp_dir): - import shutil - - shutil.rmtree(temp_dir) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.cleanup() - - -# Export commonly used items -__all__ = [ - "TestContext", - "ToolTestHelper", - "FileSystemTestHelper", - "MockServiceHelper", - "AsyncTestHelper", - "TestDataGenerator", - "TestEnvironment", - "create_mock_ctx", - "create_permission_manager", - "create_test_server", - "with_temp_dir", - "with_mock_service", - "requires_hanzo_agents", - "requires_memory_tools", -] diff --git a/pkg/hanzo-mcp/tests/test_vector_store.py b/pkg/hanzo-mcp/tests/test_vector_store.py deleted file mode 100644 index 13d76dde3..000000000 --- a/pkg/hanzo-mcp/tests/test_vector_store.py +++ /dev/null @@ -1,347 +0,0 @@ -"""Comprehensive tests for InfinityVectorStore functionality.""" - -import tempfile -from pathlib import Path - -import pytest -from hanzo_mcp.tools.vector.ast_analyzer import Symbol -from hanzo_mcp.tools.vector.infinity_store import ( - InfinityVectorStore, - SearchResult, - UnifiedSearchResult, -) - - -class TestInfinityVectorStore: - """Test suite for InfinityVectorStore.""" - - @pytest.fixture - def temp_store(self): - """Create a temporary vector store.""" - with tempfile.TemporaryDirectory() as tmpdir: - store = InfinityVectorStore(data_path=tmpdir) - yield store - store.close() - - def test_initialization(self, tool_helper, temp_store): - """Test store initialization.""" - assert temp_store is not None - assert temp_store.dimension == 1536 # Default OpenAI dimension - assert temp_store.embedding_model == "text-embedding-3-small" - - def test_add_document(self, tool_helper, temp_store): - """Test adding a single document.""" - content = "This is a test document about Python programming" - metadata = {"language": "python", "type": "tutorial"} - - doc_id = temp_store.add_document(content, metadata) - - assert doc_id is not None - assert isinstance(doc_id, str) - assert len(doc_id) > 0 - - def test_add_file(self, tool_helper, temp_store): - """Test adding a file with chunking.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: - f.write( - """ -def hello_world(): - '''A simple hello world function.''' - print("Hello, World!") - return "Hello" - -class Calculator: - '''A simple calculator class.''' - - def add(self, a, b): - '''Add two numbers.''' - return a + b - - def subtract(self, a, b): - '''Subtract b from a.''' - return a - b - -# This is a long comment to test chunking behavior -# when files are larger than the chunk size -# and need to be split into multiple documents -""" - * 50 - ) # Make it long enough to require chunking - f.flush() - - doc_ids = temp_store.add_file( - f.name, chunk_size=500, chunk_overlap=50, metadata={"project": "test"} - ) - - assert len(doc_ids) > 1 # Should be chunked - assert all(isinstance(doc_id, str) for doc_id in doc_ids) - - Path(f.name).unlink() - - def test_search_basic(self, tool_helper, temp_store): - """Test basic search functionality.""" - # Add test documents - docs = [ - ("Python is a great programming language", {"type": "opinion"}), - ("JavaScript is used for web development", {"type": "fact"}), - ("Machine learning with Python is powerful", {"type": "tutorial"}), - ] - - for content, metadata in docs: - temp_store.add_document(content, metadata) - - # Search for Python-related content - results = temp_store.search("Python programming", limit=2) - - assert len(results) <= 2 - assert all(isinstance(r, SearchResult) for r in results) - if results: - assert results[0].score >= 0.0 - assert results[0].document.content is not None - - def test_symbol_storage_and_search(self, tool_helper, temp_store): - """Test symbol storage and searching.""" - # Create test symbols - symbols = [ - Symbol( - name="calculate_average", - type="function", - file_path="/test/math.py", - line_start=10, - line_end=15, - column_start=0, - column_end=50, - scope="module", - signature="def calculate_average(numbers: List[float]) -> float", - docstring="Calculate the average of a list of numbers", - ), - Symbol( - name="DataProcessor", - type="class", - file_path="/test/processor.py", - line_start=20, - line_end=100, - column_start=0, - column_end=80, - scope="module", - docstring="Process various types of data", - ), - ] - - # Store symbols - temp_store._store_symbols(symbols) - - # Search for function - func_results = temp_store.search_symbols( - "calculate average numbers", symbol_type="function" - ) - - assert len(func_results) > 0 - assert any(r.symbol.name == "calculate_average" for r in func_results) - - # Search for class - class_results = temp_store.search_symbols( - "data processing", symbol_type="class" - ) - - assert len(class_results) > 0 - assert any(r.symbol.name == "DataProcessor" for r in class_results) - - def test_file_deletion(self, tool_helper, temp_store): - """Test deleting all documents from a file.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f: - f.write("Test content for deletion") - f.flush() - - # Add file - doc_ids = temp_store.add_file(f.name) - assert len(doc_ids) > 0 - - # Delete file documents - deleted_count = temp_store.delete_file(f.name) - assert deleted_count == len(doc_ids) - - # Verify deletion by searching - results = temp_store.search("Test content for deletion") - file_results = [r for r in results if r.document.file_path == f.name] - assert len(file_results) == 0 - - Path(f.name).unlink() - - def test_list_files(self, tool_helper, temp_store): - """Test listing indexed files.""" - # Add multiple files - test_files = [] - for i in range(3): - with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: - f.write(f"Test file {i} content") - f.flush() - test_files.append(f.name) - temp_store.add_file(f.name) - - # List files - indexed_files = temp_store.list_files() - indexed_paths = [f["file_path"] for f in indexed_files] - - assert len(indexed_files) >= 3 - for test_file in test_files: - assert test_file in indexed_paths - Path(test_file).unlink() - - def test_ast_storage(self, tool_helper, temp_store): - """Test AST storage and retrieval.""" - from hanzo_mcp.tools.vector.ast_analyzer import FileAST - - # Create a mock FileAST - file_ast = FileAST( - file_path="/test/example.py", - file_hash="abc123", - language="python", - symbols=[ - Symbol( - name="main", - type="function", - file_path="/test/example.py", - line_start=1, - line_end=5, - column_start=0, - column_end=50, - scope="module", - ) - ], - ast_nodes=[], - imports=["os", "sys"], - exports=["main"], - dependencies=["os", "sys"], - ) - - # Store AST - temp_store._store_file_ast(file_ast) - - # Retrieve AST - retrieved_ast = temp_store.search_ast_nodes("/test/example.py") - - assert retrieved_ast is not None - assert retrieved_ast.file_path == file_ast.file_path - assert retrieved_ast.file_hash == file_ast.file_hash - assert len(retrieved_ast.symbols) == 1 - assert retrieved_ast.symbols[0].name == "main" - - def test_file_references(self, tool_helper, temp_store): - """Test cross-file reference tracking.""" - from hanzo_mcp.tools.vector.ast_analyzer import FileAST - - # Create FileAST with dependencies - file_ast = FileAST( - file_path="/test/module_a.py", - file_hash="def456", - language="python", - symbols=[], - ast_nodes=[], - imports=["module_b", "module_c"], - exports=[], - dependencies=["module_b.py", "module_c.py"], - ) - - # Store references - temp_store._store_references(file_ast) - - # Get references to module_b - refs = temp_store.get_file_references("module_b.py") - - assert len(refs) > 0 - assert any(ref["source_file"] == "/test/module_a.py" for ref in refs) - - def test_chunking_algorithm(self, tool_helper, temp_store): - """Test text chunking algorithm.""" - # Create text with clear sentence boundaries - text = "First sentence. Second sentence. Third sentence.\n" * 10 - - chunks = temp_store._chunk_text(text, chunk_size=50, overlap=10) - - assert len(chunks) > 1 - assert all(len(chunk) <= 50 for chunk in chunks) - - # Check overlap exists between consecutive chunks - if len(chunks) > 1: - # The chunking algorithm should create some overlap - # Just verify chunks are created - exact overlap depends on algorithm - assert len(chunks[0]) > 0 - assert len(chunks[-1]) > 0 - - def test_embedding_generation(self, tool_helper, temp_store): - """Test embedding generation (mock).""" - text = "Test embedding generation" - embedding = temp_store._generate_embedding(text) - - assert isinstance(embedding, list) - assert len(embedding) == temp_store.dimension - assert all(isinstance(val, float) for val in embedding) - assert all( - 0 <= val <= 1 for val in embedding - ) # Mock embeddings use random [0,1] - - -class TestVectorStoreIntegration: - """Integration tests for vector store with other components.""" - - @pytest.fixture - def integrated_store(self): - """Create a vector store with realistic data.""" - with tempfile.TemporaryDirectory() as tmpdir: - store = InfinityVectorStore(data_path=tmpdir) - - # Add various types of content - # Code files - store.add_document( - "def process_data(data): return [d.upper() for d in data]", - {"type": "code", "language": "python", "file": "processor.py"}, - ) - - # Documentation - store.add_document( - "The process_data function transforms input data to uppercase", - {"type": "docs", "file": "README.md"}, - ) - - # Comments/docstrings - store.add_document( - "Process the input data and return uppercase version", - {"type": "docstring", "function": "process_data"}, - ) - - yield store - store.close() - - def test_semantic_code_search(self, tool_helper, integrated_store): - """Test semantic search across code and docs.""" - # Search for functionality - results = integrated_store.search("transform text to capital letters", limit=5) - - assert len(results) > 0 - # Should find both code and documentation - result_types = {r.document.metadata.get("type") for r in results} - assert "code" in result_types or "docs" in result_types - - def test_search_results(self, tool_helper, integrated_store): - """Test creating search results.""" - # This would integrate with the SearchTool - # but we can test the data structure - result = UnifiedSearchResult( - type="document", - content="test content", - file_path="/test/file.py", - line_start=1, - line_end=5, - score=0.95, - search_type="vector", - metadata={"test": True}, - ) - - assert result.type == "document" - assert result.score == 0.95 - assert result.search_type == "vector" - - -if __name__ == "__main__": - pytest.main([__file__, "-v"]) diff --git a/pkg/hanzo-mcp/tests/test_web3_integration.py b/pkg/hanzo-mcp/tests/test_web3_integration.py deleted file mode 100644 index abb405bf5..000000000 --- a/pkg/hanzo-mcp/tests/test_web3_integration.py +++ /dev/null @@ -1,393 +0,0 @@ -"""Test Web3 integration with hanzo-agents SDK and local compute.""" - -import pytest - -# Import hanzo-agents components -try: - from hanzo_agents import ( - WEB3_AVAILABLE, - Agent, - AgentWallet, - ConfidentialAgent, - Network, - State, - TEEConfig, - TEEProvider, - Tool, - WalletConfig, - generate_shared_mnemonic, - ) - from hanzo_agents.core.marketplace import AgentMarketplace, ServiceType - from hanzo_agents.core.web3_agent import Web3Agent, Web3AgentConfig - from hanzo_agents.core.web3_network import Web3Network, create_web3_network - - HANZO_AGENTS_AVAILABLE = True -except ImportError: - HANZO_AGENTS_AVAILABLE = False - -# hanzo-agents is now always available - -# Import hanzo-network components -try: - from hanzo_network import ( - LOCAL_COMPUTE_AVAILABLE, - InferenceRequest, - LocalComputeNode, - LocalComputeOrchestrator, - ModelConfig, - ModelProvider, - ) - - HANZO_NETWORK_AVAILABLE = True -except ImportError: - HANZO_NETWORK_AVAILABLE = False - - -class TestWeb3Integration: - """Test Web3 capabilities in hanzo-agents SDK.""" - - def test_wallet_creation(self): - """Test agent wallet creation.""" - # Generate shared mnemonic - mnemonic = generate_shared_mnemonic() - assert len(mnemonic.split()) == 12 - - # Create wallet config - config = WalletConfig( - mnemonic=mnemonic, account_index=0, network_rpc="mock://localhost" - ) - - # Create wallet - wallet = AgentWallet(config) - assert wallet.address is not None - assert wallet.balance >= 0 - - def test_web3_agent_creation(self): - """Test Web3Agent creation.""" - # Create Web3 config - web3_config = Web3AgentConfig( - wallet_enabled=True, tee_enabled=True, task_price_eth=0.01 - ) - - # Create agent - agent = Web3Agent( - name="test_agent", description="Test Web3 agent", web3_config=web3_config - ) - - assert agent.name == "test_agent" - assert agent.wallet is not None - assert agent.confidential_agent is not None - assert agent.balance_eth >= 0 - - @pytest.mark.asyncio - async def test_agent_payment(self): - """Test payment between agents.""" - # Create two agents with wallets - agent1 = Web3Agent( - name="payer", - description="Agent that pays", - web3_config=Web3AgentConfig(wallet_enabled=True), - ) - - agent2 = Web3Agent( - name="payee", - description="Agent that receives payment", - web3_config=Web3AgentConfig(wallet_enabled=True), - ) - - # Give agent1 some balance - agent1.earnings = 10.0 - - # Request payment - payment_request = await agent2.request_payment( - from_address=agent1.address, amount_eth=1.0, task_description="Test service" - ) - - assert payment_request["to"] == agent2.address - assert payment_request["amount_eth"] == 1.0 - - # Make payment (mock) - if agent1.wallet: - tx = await agent1.pay_agent( - to_address=agent2.address, amount_eth=1.0, reason="Test payment" - ) - assert tx is not None - - def test_tee_execution(self): - """Test TEE confidential execution.""" - agent = Web3Agent( - name="tee_agent", - description="Agent with TEE", - web3_config=Web3AgentConfig(tee_enabled=True), - ) - - # Execute confidential task - task_code = """ -result = {"sum": inputs["a"] + inputs["b"]} -""" - - result = agent.confidential_agent.execute_confidential( - task_code, {"a": 5, "b": 3} - ) - - assert result["success"] is True - assert result["result"]["sum"] == 8 - tool_helper.assert_in_result("attestation", result) - - def test_marketplace_interaction(self): - """Test agent marketplace.""" - marketplace = AgentMarketplace() - - # Create provider agent - provider = Web3Agent( - name="provider", - description="Service provider", - web3_config=Web3AgentConfig(wallet_enabled=True), - ) - - # Post offer - offer_id = marketplace.post_offer( - agent=provider, - service_type=ServiceType.COMPUTE, - description="GPU compute for AI", - price_eth=0.1, - requires_tee=True, - ) - - assert offer_id.startswith("offer_") - assert len(marketplace.offers) == 1 - - # Create requester agent - requester = Web3Agent( - name="requester", - description="Service requester", - web3_config=Web3AgentConfig(wallet_enabled=True), - ) - - # Post request - marketplace.post_request( - agent=requester, - service_type=ServiceType.COMPUTE, - description="Need GPU for training", - max_price_eth=0.2, - ) - - # Should auto-match - assert len(marketplace.matches) == 1 - match = list(marketplace.matches.values())[0] - assert match.offer.agent_name == "provider" - assert match.request.requester_name == "requester" - - -@pytest.mark.skipif( - not (HANZO_AGENTS_AVAILABLE and HANZO_NETWORK_AVAILABLE), - reason="Both hanzo-agents and hanzo-network required", -) -class TestLocalComputeIntegration: - """Test local compute with agent networks.""" - - @pytest.mark.asyncio - async def test_local_compute_node(self): - """Test local compute node creation.""" - node = LocalComputeNode( - node_id="test_node", - wallet_address="0x1234567890123456789012345678901234567890", - ) - - # List models - models = node.list_models() - assert len(models) > 0 - assert models[0]["name"] == "hanzo-nano" - - # Create inference request - request = InferenceRequest( - request_id="test_001", - prompt="Hello, world!", - max_tokens=10, - max_price_eth=0.001, - ) - - # Process request - result = await node.process_request(request) - assert result.request_id == "test_001" - assert len(result.text) > 0 - - @pytest.mark.asyncio - async def test_compute_marketplace_integration(self): - """Test compute marketplace with agents.""" - # Create compute node - node = LocalComputeNode(node_id="gpu_node") - - # Create agent that provides compute - compute_agent = Web3Agent( - name="compute_provider", - description="Provides local AI compute", - web3_config=Web3AgentConfig(wallet_enabled=True, tee_enabled=True), - ) - - # Link node to agent - compute_agent.compute_node = node - - # Create marketplace - marketplace = AgentMarketplace() - - # Agent posts compute offer - marketplace.post_offer( - agent=compute_agent, - service_type=ServiceType.COMPUTE, - description="Local GPU inference - Mistral 7B", - price_eth=0.0001, # Per inference - metadata={ - "model": "hanzo-nano", - "tokens_per_second": 20, - "max_tokens": 1000, - }, - ) - - # Another agent requests compute - user_agent = Web3Agent(name="user", description="Needs AI inference") - - marketplace.post_request( - agent=user_agent, - service_type=ServiceType.COMPUTE, - description="Need to run inference on prompt", - max_price_eth=0.001, - metadata={"prompt": "What is the meaning of life?", "max_tokens": 100}, - ) - - # Should match - assert len(marketplace.matches) == 1 - - -class TestDeterministicExecution: - """Test deterministic network execution.""" - - @pytest.mark.asyncio - async def test_deterministic_network(self): - """Test deterministic execution of agent network.""" - - # Create agents - class Agent1(Agent): - name = "agent1" - - async def run(self, state, history, network): - state["step1"] = "completed" - return InferenceResult(agent=self.name, content="Step 1 done") - - class Agent2(Agent): - name = "agent2" - - async def run(self, state, history, network): - state["step2"] = "completed" - return InferenceResult(agent=self.name, content="Step 2 done") - - # Create network - network = create_web3_network( - agents=[Agent1(), Agent2()], - task="Test deterministic execution", - deterministic=True, - ) - - # Run network - await network.run() - - # Get execution hash - hash1 = network.execution_hash - assert hash1 is not None - - # Run again with same config - network2 = create_web3_network( - agents=[Agent1(), Agent2()], - task="Test deterministic execution", - deterministic=True, - ) - - await network2.run() - hash2 = network2.execution_hash - - # Should produce same hash - assert hash1 == hash2 - - # Verify execution - assert network2.verify_execution(hash1) - - -@pytest.mark.asyncio -async def test_full_integration(): - """Test full integration of all components.""" - if not (HANZO_AGENTS_AVAILABLE and HANZO_NETWORK_AVAILABLE): - pytest.skip("Full integration requires all components") - - # 1. Create shared mnemonic for network - mnemonic = generate_shared_mnemonic() - - # 2. Create compute nodes - compute_orchestrator = LocalComputeOrchestrator() - - node1 = LocalComputeNode(node_id="node_001") - node2 = LocalComputeNode(node_id="node_002") - - compute_orchestrator.register_node(node1) - compute_orchestrator.register_node(node2) - - # 3. Create marketplace - marketplace = AgentMarketplace() - - # 4. Create Web3 agents - data_agent = Web3Agent( - name="data_provider", - description="Provides training data", - web3_config=Web3AgentConfig( - wallet_enabled=True, - wallet_config=WalletConfig(mnemonic=mnemonic, account_index=0), - ), - ) - - compute_agent = Web3Agent( - name="compute_provider", - description="Provides GPU compute", - web3_config=Web3AgentConfig( - wallet_enabled=True, - tee_enabled=True, - wallet_config=WalletConfig(mnemonic=mnemonic, account_index=1), - ), - ) - - orchestrator_agent = Web3Agent( - name="orchestrator", - description="Orchestrates the workflow", - web3_config=Web3AgentConfig( - wallet_enabled=True, - wallet_config=WalletConfig(mnemonic=mnemonic, account_index=2), - ), - ) - - # 5. Create network - from hanzo_agents.core.router import sequential_router - - network = Web3Network( - state=State({"task": "Train a small AI model"}), - agents=[orchestrator_agent, data_agent, compute_agent], - router=sequential_router(["orchestrator", "data_provider", "compute_provider"]), - shared_mnemonic=mnemonic, - marketplace=marketplace, - ) - - # 6. Run network - await network.run() - - # 7. Check results - stats = network.get_network_stats() - print("\nNetwork execution complete!") - print(f"Total steps: {stats['total_steps']}") - print(f"Treasury: {stats['treasury_balance']:.4f}") - print(f"Marketplace activity: {stats['marketplace_stats']}") - - # Verify some execution occurred - assert stats["total_steps"] > 0 - assert network.execution_hash is not None - - -# if __name__ == "__main__": -# # Run integration test -# asyncio.run(test_full_integration()) diff --git a/pkg/hanzo-mcp/tests/test_workflows/test_linear_planning_flow.py b/pkg/hanzo-mcp/tests/test_workflows/test_linear_planning_flow.py deleted file mode 100644 index 732e400dc..000000000 --- a/pkg/hanzo-mcp/tests/test_workflows/test_linear_planning_flow.py +++ /dev/null @@ -1,237 +0,0 @@ -"""End-to-end workflow sanity tests for planningโ†’tasksโ†’Linearโ†’worktrees. - -These tests validate that hanzo-mcp can orchestrate the requested flow using -existing tools without requiring networked services: -- Architecture proposal via LLM (mocked) -- Task generation from proposal via LLM (mocked) -- Linear MCP server registration via mcp_add + visibility in mcp_stats -- Per-task git worktree creation via bash - -Network calls are not performed. LLM calls are mocked via llm patching. -""" - -from __future__ import annotations - -import json -import os -import subprocess - -# Ensure typing.override exists on Python < 3.12 -import typing as _typing -from pathlib import Path -from typing import Dict - -import pytest - -if not hasattr(_typing, "override"): - - def _override(obj): # type: ignore - return obj - - _typing.override = _override # type: ignore[attr-defined] - -from hanzo_mcp.tools.llm.llm_tool import LLMTool - -# Import MCP tools after making sure typing.override is available -from hanzo_mcp.tools.mcp.mcp_add import McpAddTool -from hanzo_mcp.tools.mcp.mcp_stats import McpStatsTool -from hanzo_tools.shell.bash_tool import BashTool -from mcp.server.fastmcp import Context as MCPContext -from test_utils import PermissionManager - - -def _mock_acompletion_factory(responses: Dict[str, str]): - """Create a llm.acompletion mock that returns responses by keyword. - - The first matching keyword in the prompt determines the returned text. - Defaults to a simple echo if nothing matches. - """ - - class _MockResponse: - def __init__(self, text: str): - self.choices = [ - type("C", (), {"message": type("M", (), {"content": text})()}) - ] - - async def _acompletion(**kwargs): # type: ignore - prompt = "".join( - part - for part in ( - kwargs.get("messages", [{}])[-1].get("content"), - kwargs.get("prompt"), - ) - if part - ) - for key, value in responses.items(): - if key.lower() in (prompt or "").lower(): - return _MockResponse(value) - return _MockResponse("OK") - - async def _astream(**kwargs): # pragma: no cover - not used - return - - return _acompletion, _astream - - -@pytest.mark.asyncio -async def test_architecture_and_task_gen_with_consensus_mock( - monkeypatch, tmp_path: Path -): - """Architecture โ†’ tasks flow using LLMTool with llm mocked. - - - Generates an architecture.md file - - Generates JSON tasks from the architecture - """ - - # LLM responses will be generated by LLMTool fast-test mode - - # Tools and context - ctx = MCPContext() - # Isolate server registry by scoping HOME to tmp_path - os.environ["HOME"] = str(tmp_path) - # Ensure provider check passes - os.environ["OPENAI_API_KEY"] = "test-key" - permission_manager = PermissionManager() - permission_manager._allowed_paths.add(str(tmp_path)) - llm = LLMTool() - - # 1) Architecture proposal - arch_file = tmp_path / "architecture.md" - result1 = await llm.call( - ctx, - action="query", - prompt="Create architecture for service (architecture)", - model="gpt-4o", - ) - arch_file.write_text(str(result1)) - assert arch_file.read_text().startswith("Architecture Proposal") - - # 2) Task generation from architecture doc - result2 = await llm.call( - ctx, - action="query", - prompt=f"Read {arch_file} and generate tasks as JSON (generate tasks)", - model="gpt-4o", - ) - data = json.loads(str(result2)) - assert "tasks" in data and len(data["tasks"]) >= 2 - - -@pytest.mark.asyncio -async def test_register_linear_mcp_and_list_in_stats(tmp_path: Path): - """Add a Linear MCP server and confirm it shows in stats output.""" - - ctx = MCPContext() - # Patch tool context to have async set_tool_info - import hanzo_mcp.tools.mcp.mcp_add as mcp_add_mod - import hanzo_mcp.tools.mcp.mcp_stats as mcp_stats_mod - - from hanzo_mcp.tools.common import context as ctx_mod - - class _StubToolCtx: - async def set_tool_info(self, *args, **kwargs): - return None - - async def info(self, *args, **kwargs): - return None - - async def error(self, *args, **kwargs): - return None - - ctx_mod.create_tool_context = lambda _ctx: _StubToolCtx() # type: ignore - mcp_add_mod.create_tool_context = lambda _ctx: _StubToolCtx() # type: ignore - mcp_stats_mod.create_tool_context = lambda _ctx: _StubToolCtx() # type: ignore - # Ensure clean registry for this test - from hanzo_mcp.tools.mcp.mcp_add import McpAddTool as _M - - _M._mcp_servers.clear() - # Remove any persisted registry - cfg = _M._config_file - try: - if cfg.exists(): - cfg.unlink() - except Exception: - pass - add = McpAddTool() - stats = McpStatsTool() - - server_name = f"linear-{tmp_path.name}" - res = await add.call( - ctx, - command="npx @modelcontextprotocol/server-linear", - name=server_name, - args="--help", - auto_start=False, - ) - assert f"Successfully added MCP server '{server_name}'" in res - - out = await stats.call(ctx) - # Stats include common servers hint; ensure linear appears in server details hints - assert "@modelcontextprotocol/server-linear" in out - - -@pytest.mark.asyncio -async def test_git_worktree_per_task(monkeypatch, tmp_path: Path): - """Create per-task git worktrees using the bash tool in a temp repo.""" - - # Initialize a git repo - subprocess.run(["git", "init", "-q", str(tmp_path)], check=True) - (tmp_path / "README.md").write_text("demo") - subprocess.run(["git", "-C", str(tmp_path), "add", "README.md"], check=True) - subprocess.run( - [ - "git", - "-C", - str(tmp_path), - "commit", - "-qm", - "chore: initial", - ], - check=True, - ) - - # Prepare bash tool - ctx = MCPContext() - pm = PermissionManager() - pm._allowed_paths.add(str(tmp_path)) - bash = BashTool(permission_manager=pm) - - # Restore real subprocess execution for this test (override auto background mock) - async def _exec_now(**kwargs): - cmd_args = kwargs.get("cmd_args") - cwd = kwargs.get("cwd") - env = kwargs.get("env") - proc = subprocess.run( - cmd_args, - cwd=str(cwd) if cwd else None, - env=env, - capture_output=True, - text=True, - ) - if proc.returncode != 0: - return ( - f"Command failed with exit code {proc.returncode}:\n{proc.stderr}", - False, - None, - ) - return (proc.stdout or "Exit Code: 0", False, None) - - bash.auto_background_executor.execute_with_auto_background = _exec_now # type: ignore - - # Create two worktrees for two tasks - tasks = ["init-repo", "add-ci"] - for t in tasks: - worktree_dir = tmp_path.parent / f"{tmp_path.name}-{t}" - cmd = f"git -C {tmp_path} worktree add -b {t} {worktree_dir}" - await bash.call(ctx, command=cmd) - assert worktree_dir.exists() - assert worktree_dir.exists() - - # Verify branches exist - branches = subprocess.run( - ["git", "-C", str(tmp_path), "branch"], - capture_output=True, - text=True, - check=True, - ).stdout - assert "init-repo" in branches and "add-ci" in branches diff --git a/pkg/hanzo-mcp/uv.lock b/pkg/hanzo-mcp/uv.lock deleted file mode 100644 index 8be321ba8..000000000 --- a/pkg/hanzo-mcp/uv.lock +++ /dev/null @@ -1,6360 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" -resolution-markers = [ - "python_full_version >= '3.14'", - "python_full_version == '3.13.*'", - "python_full_version < '3.13'", -] - -[[package]] -name = "aiocache" -version = "0.12.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7a/64/b945b8025a9d1e6e2138845f4022165d3b337f55f50984fbc6a4c0a1e355/aiocache-0.12.3.tar.gz", hash = "sha256:f528b27bf4d436b497a1d0d1a8f59a542c153ab1e37c3621713cb376d44c4713", size = 132196, upload-time = "2024-09-25T13:20:23.823Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/37/d7/15d67e05b235d1ed8c3ce61688fe4d84130e72af1657acadfaac3479f4cf/aiocache-0.12.3-py2.py3-none-any.whl", hash = "sha256:889086fc24710f431937b87ad3720a289f7fc31c4fd8b68e9f918b9bacd8270d", size = 28199, upload-time = "2024-09-25T13:20:22.688Z" }, -] - -[[package]] -name = "aiofiles" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/c3/534eac40372d8ee36ef40df62ec129bee4fdb5ad9706e58a29be53b2c970/aiofiles-25.1.0.tar.gz", hash = "sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2", size = 46354, upload-time = "2025-10-09T20:51:04.358Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/8a/340a1555ae33d7354dbca4faa54948d76d89a27ceef032c8c3bc661d003e/aiofiles-25.1.0-py3-none-any.whl", hash = "sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695", size = 14668, upload-time = "2025-10-09T20:51:03.174Z" }, -] - -[[package]] -name = "aiohappyeyeballs" -version = "2.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, -] - -[[package]] -name = "aiohttp" -version = "3.13.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohappyeyeballs" }, - { name = "aiosignal" }, - { name = "attrs" }, - { name = "frozenlist" }, - { name = "multidict" }, - { name = "propcache" }, - { name = "yarl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732, upload-time = "2026-01-03T17:30:14.23Z" }, - { url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293, upload-time = "2026-01-03T17:30:15.96Z" }, - { url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533, upload-time = "2026-01-03T17:30:17.431Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839, upload-time = "2026-01-03T17:30:19.422Z" }, - { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932, upload-time = "2026-01-03T17:30:21.756Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906, upload-time = "2026-01-03T17:30:23.932Z" }, - { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020, upload-time = "2026-01-03T17:30:26Z" }, - { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181, upload-time = "2026-01-03T17:30:27.554Z" }, - { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794, upload-time = "2026-01-03T17:30:29.254Z" }, - { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900, upload-time = "2026-01-03T17:30:31.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239, upload-time = "2026-01-03T17:30:32.703Z" }, - { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527, upload-time = "2026-01-03T17:30:34.695Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489, upload-time = "2026-01-03T17:30:36.864Z" }, - { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852, upload-time = "2026-01-03T17:30:39.433Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379, upload-time = "2026-01-03T17:30:41.081Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4a/1a3fee7c21350cac78e5c5cef711bac1b94feca07399f3d406972e2d8fcd/aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", size = 428253, upload-time = "2026-01-03T17:30:42.644Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b7/76175c7cb4eb73d91ad63c34e29fc4f77c9386bba4a65b53ba8e05ee3c39/aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", size = 455407, upload-time = "2026-01-03T17:30:44.195Z" }, - { url = "https://files.pythonhosted.org/packages/97/8a/12ca489246ca1faaf5432844adbfce7ff2cc4997733e0af120869345643a/aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", size = 734190, upload-time = "2026-01-03T17:30:45.832Z" }, - { url = "https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", size = 491783, upload-time = "2026-01-03T17:30:47.466Z" }, - { url = "https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", size = 490704, upload-time = "2026-01-03T17:30:49.373Z" }, - { url = "https://files.pythonhosted.org/packages/6d/40/a46b03ca03936f832bc7eaa47cfbb1ad012ba1be4790122ee4f4f8cba074/aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", size = 1720652, upload-time = "2026-01-03T17:30:50.974Z" }, - { url = "https://files.pythonhosted.org/packages/f7/7e/917fe18e3607af92657e4285498f500dca797ff8c918bd7d90b05abf6c2a/aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", size = 1692014, upload-time = "2026-01-03T17:30:52.729Z" }, - { url = "https://files.pythonhosted.org/packages/71/b6/cefa4cbc00d315d68973b671cf105b21a609c12b82d52e5d0c9ae61d2a09/aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", size = 1759777, upload-time = "2026-01-03T17:30:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e3/e06ee07b45e59e6d81498b591fc589629be1553abb2a82ce33efe2a7b068/aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", size = 1861276, upload-time = "2026-01-03T17:30:56.512Z" }, - { url = "https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", size = 1743131, upload-time = "2026-01-03T17:30:58.256Z" }, - { url = "https://files.pythonhosted.org/packages/04/98/3d21dde21889b17ca2eea54fdcff21b27b93f45b7bb94ca029c31ab59dc3/aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", size = 1556863, upload-time = "2026-01-03T17:31:00.445Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/da0c3ab1192eaf64782b03971ab4055b475d0db07b17eff925e8c93b3aa5/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", size = 1682793, upload-time = "2026-01-03T17:31:03.024Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0f/5802ada182f575afa02cbd0ec5180d7e13a402afb7c2c03a9aa5e5d49060/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", size = 1716676, upload-time = "2026-01-03T17:31:04.842Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8c/714d53bd8b5a4560667f7bbbb06b20c2382f9c7847d198370ec6526af39c/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", size = 1733217, upload-time = "2026-01-03T17:31:06.868Z" }, - { url = "https://files.pythonhosted.org/packages/7d/79/e2176f46d2e963facea939f5be2d26368ce543622be6f00a12844d3c991f/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", size = 1552303, upload-time = "2026-01-03T17:31:08.958Z" }, - { url = "https://files.pythonhosted.org/packages/ab/6a/28ed4dea1759916090587d1fe57087b03e6c784a642b85ef48217b0277ae/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", size = 1763673, upload-time = "2026-01-03T17:31:10.676Z" }, - { url = "https://files.pythonhosted.org/packages/e8/35/4a3daeb8b9fab49240d21c04d50732313295e4bd813a465d840236dd0ce1/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", size = 1721120, upload-time = "2026-01-03T17:31:12.575Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9f/d643bb3c5fb99547323e635e251c609fbbc660d983144cfebec529e09264/aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", size = 427383, upload-time = "2026-01-03T17:31:14.382Z" }, - { url = "https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", size = 453899, upload-time = "2026-01-03T17:31:15.958Z" }, - { url = "https://files.pythonhosted.org/packages/99/36/5b6514a9f5d66f4e2597e40dea2e3db271e023eb7a5d22defe96ba560996/aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808", size = 737238, upload-time = "2026-01-03T17:31:17.909Z" }, - { url = "https://files.pythonhosted.org/packages/f7/49/459327f0d5bcd8c6c9ca69e60fdeebc3622861e696490d8674a6d0cb90a6/aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415", size = 492292, upload-time = "2026-01-03T17:31:19.919Z" }, - { url = "https://files.pythonhosted.org/packages/e8/0b/b97660c5fd05d3495b4eb27f2d0ef18dc1dc4eff7511a9bf371397ff0264/aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f", size = 493021, upload-time = "2026-01-03T17:31:21.636Z" }, - { url = "https://files.pythonhosted.org/packages/54/d4/438efabdf74e30aeceb890c3290bbaa449780583b1270b00661126b8aae4/aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6", size = 1717263, upload-time = "2026-01-03T17:31:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/71/f2/7bddc7fd612367d1459c5bcf598a9e8f7092d6580d98de0e057eb42697ad/aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687", size = 1669107, upload-time = "2026-01-03T17:31:25.334Z" }, - { url = "https://files.pythonhosted.org/packages/00/5a/1aeaecca40e22560f97610a329e0e5efef5e0b5afdf9f857f0d93839ab2e/aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26", size = 1760196, upload-time = "2026-01-03T17:31:27.394Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f8/0ff6992bea7bd560fc510ea1c815f87eedd745fe035589c71ce05612a19a/aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a", size = 1843591, upload-time = "2026-01-03T17:31:29.238Z" }, - { url = "https://files.pythonhosted.org/packages/e3/d1/e30e537a15f53485b61f5be525f2157da719819e8377298502aebac45536/aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1", size = 1720277, upload-time = "2026-01-03T17:31:31.053Z" }, - { url = "https://files.pythonhosted.org/packages/84/45/23f4c451d8192f553d38d838831ebbc156907ea6e05557f39563101b7717/aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25", size = 1548575, upload-time = "2026-01-03T17:31:32.87Z" }, - { url = "https://files.pythonhosted.org/packages/6a/ed/0a42b127a43712eda7807e7892c083eadfaf8429ca8fb619662a530a3aab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603", size = 1679455, upload-time = "2026-01-03T17:31:34.76Z" }, - { url = "https://files.pythonhosted.org/packages/2e/b5/c05f0c2b4b4fe2c9d55e73b6d3ed4fd6c9dc2684b1d81cbdf77e7fad9adb/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a", size = 1687417, upload-time = "2026-01-03T17:31:36.699Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6b/915bc5dad66aef602b9e459b5a973529304d4e89ca86999d9d75d80cbd0b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926", size = 1729968, upload-time = "2026-01-03T17:31:38.622Z" }, - { url = "https://files.pythonhosted.org/packages/11/3b/e84581290a9520024a08640b63d07673057aec5ca548177a82026187ba73/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba", size = 1545690, upload-time = "2026-01-03T17:31:40.57Z" }, - { url = "https://files.pythonhosted.org/packages/f5/04/0c3655a566c43fd647c81b895dfe361b9f9ad6d58c19309d45cff52d6c3b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c", size = 1746390, upload-time = "2026-01-03T17:31:42.857Z" }, - { url = "https://files.pythonhosted.org/packages/1f/53/71165b26978f719c3419381514c9690bd5980e764a09440a10bb816ea4ab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43", size = 1702188, upload-time = "2026-01-03T17:31:44.984Z" }, - { url = "https://files.pythonhosted.org/packages/29/a7/cbe6c9e8e136314fa1980da388a59d2f35f35395948a08b6747baebb6aa6/aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1", size = 433126, upload-time = "2026-01-03T17:31:47.463Z" }, - { url = "https://files.pythonhosted.org/packages/de/56/982704adea7d3b16614fc5936014e9af85c0e34b58f9046655817f04306e/aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984", size = 459128, upload-time = "2026-01-03T17:31:49.2Z" }, - { url = "https://files.pythonhosted.org/packages/6c/2a/3c79b638a9c3d4658d345339d22070241ea341ed4e07b5ac60fb0f418003/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c", size = 769512, upload-time = "2026-01-03T17:31:51.134Z" }, - { url = "https://files.pythonhosted.org/packages/29/b9/3e5014d46c0ab0db8707e0ac2711ed28c4da0218c358a4e7c17bae0d8722/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592", size = 506444, upload-time = "2026-01-03T17:31:52.85Z" }, - { url = "https://files.pythonhosted.org/packages/90/03/c1d4ef9a054e151cd7839cdc497f2638f00b93cbe8043983986630d7a80c/aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f", size = 510798, upload-time = "2026-01-03T17:31:54.91Z" }, - { url = "https://files.pythonhosted.org/packages/ea/76/8c1e5abbfe8e127c893fe7ead569148a4d5a799f7cf958d8c09f3eedf097/aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29", size = 1868835, upload-time = "2026-01-03T17:31:56.733Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ac/984c5a6f74c363b01ff97adc96a3976d9c98940b8969a1881575b279ac5d/aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc", size = 1720486, upload-time = "2026-01-03T17:31:58.65Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9a/b7039c5f099c4eb632138728828b33428585031a1e658d693d41d07d89d1/aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2", size = 1847951, upload-time = "2026-01-03T17:32:00.989Z" }, - { url = "https://files.pythonhosted.org/packages/3c/02/3bec2b9a1ba3c19ff89a43a19324202b8eb187ca1e928d8bdac9bbdddebd/aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587", size = 1941001, upload-time = "2026-01-03T17:32:03.122Z" }, - { url = "https://files.pythonhosted.org/packages/37/df/d879401cedeef27ac4717f6426c8c36c3091c6e9f08a9178cc87549c537f/aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8", size = 1797246, upload-time = "2026-01-03T17:32:05.255Z" }, - { url = "https://files.pythonhosted.org/packages/8d/15/be122de1f67e6953add23335c8ece6d314ab67c8bebb3f181063010795a7/aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632", size = 1627131, upload-time = "2026-01-03T17:32:07.607Z" }, - { url = "https://files.pythonhosted.org/packages/12/12/70eedcac9134cfa3219ab7af31ea56bc877395b1ac30d65b1bc4b27d0438/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64", size = 1795196, upload-time = "2026-01-03T17:32:09.59Z" }, - { url = "https://files.pythonhosted.org/packages/32/11/b30e1b1cd1f3054af86ebe60df96989c6a414dd87e27ad16950eee420bea/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0", size = 1782841, upload-time = "2026-01-03T17:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/88/0d/d98a9367b38912384a17e287850f5695c528cff0f14f791ce8ee2e4f7796/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56", size = 1795193, upload-time = "2026-01-03T17:32:13.705Z" }, - { url = "https://files.pythonhosted.org/packages/43/a5/a2dfd1f5ff5581632c7f6a30e1744deda03808974f94f6534241ef60c751/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72", size = 1621979, upload-time = "2026-01-03T17:32:15.965Z" }, - { url = "https://files.pythonhosted.org/packages/fa/f0/12973c382ae7c1cccbc4417e129c5bf54c374dfb85af70893646e1f0e749/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df", size = 1822193, upload-time = "2026-01-03T17:32:18.219Z" }, - { url = "https://files.pythonhosted.org/packages/3c/5f/24155e30ba7f8c96918af1350eb0663e2430aad9e001c0489d89cd708ab1/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", size = 1769801, upload-time = "2026-01-03T17:32:20.25Z" }, - { url = "https://files.pythonhosted.org/packages/eb/f8/7314031ff5c10e6ece114da79b338ec17eeff3a079e53151f7e9f43c4723/aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", size = 466523, upload-time = "2026-01-03T17:32:22.215Z" }, - { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694, upload-time = "2026-01-03T17:32:24.546Z" }, -] - -[[package]] -name = "aiosignal" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "frozenlist" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, -] - -[[package]] -name = "alabaster" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, -] - -[[package]] -name = "annotated-doc" -version = "0.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, -] - -[[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.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, -] - -[[package]] -name = "appnope" -version = "0.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, -] - -[[package]] -name = "asttokens" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, -] - -[[package]] -name = "attrs" -version = "25.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, -] - -[[package]] -name = "authlib" -version = "1.6.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/9b/b1661026ff24bc641b76b78c5222d614776b0c085bcfdac9bd15a1cb4b35/authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e", size = 164894, upload-time = "2025-12-12T08:01:41.464Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/51/321e821856452f7386c4e9df866f196720b1ad0c5ea1623ea7399969ae3b/authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd", size = 244005, upload-time = "2025-12-12T08:01:40.209Z" }, -] - -[[package]] -name = "babel" -version = "2.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, -] - -[[package]] -name = "backoff" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" }, -] - -[[package]] -name = "bcrypt" -version = "5.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/36/3329e2518d70ad8e2e5817d5a4cac6bba05a47767ec416c7d020a965f408/bcrypt-5.0.0.tar.gz", hash = "sha256:f748f7c2d6fd375cc93d3fba7ef4a9e3a092421b8dbf34d8d4dc06be9492dfdd", size = 25386, upload-time = "2025-09-25T19:50:47.829Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/85/3e65e01985fddf25b64ca67275bb5bdb4040bd1a53b66d355c6c37c8a680/bcrypt-5.0.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f3c08197f3039bec79cee59a606d62b96b16669cff3949f21e74796b6e3cd2be", size = 481806, upload-time = "2025-09-25T19:49:05.102Z" }, - { url = "https://files.pythonhosted.org/packages/44/dc/01eb79f12b177017a726cbf78330eb0eb442fae0e7b3dfd84ea2849552f3/bcrypt-5.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:200af71bc25f22006f4069060c88ed36f8aa4ff7f53e67ff04d2ab3f1e79a5b2", size = 268626, upload-time = "2025-09-25T19:49:06.723Z" }, - { url = "https://files.pythonhosted.org/packages/8c/cf/e82388ad5959c40d6afd94fb4743cc077129d45b952d46bdc3180310e2df/bcrypt-5.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:baade0a5657654c2984468efb7d6c110db87ea63ef5a4b54732e7e337253e44f", size = 271853, upload-time = "2025-09-25T19:49:08.028Z" }, - { url = "https://files.pythonhosted.org/packages/ec/86/7134b9dae7cf0efa85671651341f6afa695857fae172615e960fb6a466fa/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c58b56cdfb03202b3bcc9fd8daee8e8e9b6d7e3163aa97c631dfcfcc24d36c86", size = 269793, upload-time = "2025-09-25T19:49:09.727Z" }, - { url = "https://files.pythonhosted.org/packages/cc/82/6296688ac1b9e503d034e7d0614d56e80c5d1a08402ff856a4549cb59207/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4bfd2a34de661f34d0bda43c3e4e79df586e4716ef401fe31ea39d69d581ef23", size = 289930, upload-time = "2025-09-25T19:49:11.204Z" }, - { url = "https://files.pythonhosted.org/packages/d1/18/884a44aa47f2a3b88dd09bc05a1e40b57878ecd111d17e5bba6f09f8bb77/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ed2e1365e31fc73f1825fa830f1c8f8917ca1b3ca6185773b349c20fd606cec2", size = 272194, upload-time = "2025-09-25T19:49:12.524Z" }, - { url = "https://files.pythonhosted.org/packages/0e/8f/371a3ab33c6982070b674f1788e05b656cfbf5685894acbfef0c65483a59/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:83e787d7a84dbbfba6f250dd7a5efd689e935f03dd83b0f919d39349e1f23f83", size = 269381, upload-time = "2025-09-25T19:49:14.308Z" }, - { url = "https://files.pythonhosted.org/packages/b1/34/7e4e6abb7a8778db6422e88b1f06eb07c47682313997ee8a8f9352e5a6f1/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:137c5156524328a24b9fac1cb5db0ba618bc97d11970b39184c1d87dc4bf1746", size = 271750, upload-time = "2025-09-25T19:49:15.584Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1b/54f416be2499bd72123c70d98d36c6cd61a4e33d9b89562c22481c81bb30/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:38cac74101777a6a7d3b3e3cfefa57089b5ada650dce2baf0cbdd9d65db22a9e", size = 303757, upload-time = "2025-09-25T19:49:17.244Z" }, - { url = "https://files.pythonhosted.org/packages/13/62/062c24c7bcf9d2826a1a843d0d605c65a755bc98002923d01fd61270705a/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:d8d65b564ec849643d9f7ea05c6d9f0cd7ca23bdd4ac0c2dbef1104ab504543d", size = 306740, upload-time = "2025-09-25T19:49:18.693Z" }, - { url = "https://files.pythonhosted.org/packages/d5/c8/1fdbfc8c0f20875b6b4020f3c7dc447b8de60aa0be5faaf009d24242aec9/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:741449132f64b3524e95cd30e5cd3343006ce146088f074f31ab26b94e6c75ba", size = 334197, upload-time = "2025-09-25T19:49:20.523Z" }, - { url = "https://files.pythonhosted.org/packages/a6/c1/8b84545382d75bef226fbc6588af0f7b7d095f7cd6a670b42a86243183cd/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:212139484ab3207b1f0c00633d3be92fef3c5f0af17cad155679d03ff2ee1e41", size = 352974, upload-time = "2025-09-25T19:49:22.254Z" }, - { url = "https://files.pythonhosted.org/packages/10/a6/ffb49d4254ed085e62e3e5dd05982b4393e32fe1e49bb1130186617c29cd/bcrypt-5.0.0-cp313-cp313t-win32.whl", hash = "sha256:9d52ed507c2488eddd6a95bccee4e808d3234fa78dd370e24bac65a21212b861", size = 148498, upload-time = "2025-09-25T19:49:24.134Z" }, - { url = "https://files.pythonhosted.org/packages/48/a9/259559edc85258b6d5fc5471a62a3299a6aa37a6611a169756bf4689323c/bcrypt-5.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f6984a24db30548fd39a44360532898c33528b74aedf81c26cf29c51ee47057e", size = 145853, upload-time = "2025-09-25T19:49:25.702Z" }, - { url = "https://files.pythonhosted.org/packages/2d/df/9714173403c7e8b245acf8e4be8876aac64a209d1b392af457c79e60492e/bcrypt-5.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9fffdb387abe6aa775af36ef16f55e318dcda4194ddbf82007a6f21da29de8f5", size = 139626, upload-time = "2025-09-25T19:49:26.928Z" }, - { url = "https://files.pythonhosted.org/packages/f8/14/c18006f91816606a4abe294ccc5d1e6f0e42304df5a33710e9e8e95416e1/bcrypt-5.0.0-cp314-cp314t-macosx_10_12_universal2.whl", hash = "sha256:4870a52610537037adb382444fefd3706d96d663ac44cbb2f37e3919dca3d7ef", size = 481862, upload-time = "2025-09-25T19:49:28.365Z" }, - { url = "https://files.pythonhosted.org/packages/67/49/dd074d831f00e589537e07a0725cf0e220d1f0d5d8e85ad5bbff251c45aa/bcrypt-5.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48f753100931605686f74e27a7b49238122aa761a9aefe9373265b8b7aa43ea4", size = 268544, upload-time = "2025-09-25T19:49:30.39Z" }, - { url = "https://files.pythonhosted.org/packages/f5/91/50ccba088b8c474545b034a1424d05195d9fcbaaf802ab8bfe2be5a4e0d7/bcrypt-5.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f70aadb7a809305226daedf75d90379c397b094755a710d7014b8b117df1ebbf", size = 271787, upload-time = "2025-09-25T19:49:32.144Z" }, - { url = "https://files.pythonhosted.org/packages/aa/e7/d7dba133e02abcda3b52087a7eea8c0d4f64d3e593b4fffc10c31b7061f3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:744d3c6b164caa658adcb72cb8cc9ad9b4b75c7db507ab4bc2480474a51989da", size = 269753, upload-time = "2025-09-25T19:49:33.885Z" }, - { url = "https://files.pythonhosted.org/packages/33/fc/5b145673c4b8d01018307b5c2c1fc87a6f5a436f0ad56607aee389de8ee3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a28bc05039bdf3289d757f49d616ab3efe8cf40d8e8001ccdd621cd4f98f4fc9", size = 289587, upload-time = "2025-09-25T19:49:35.144Z" }, - { url = "https://files.pythonhosted.org/packages/27/d7/1ff22703ec6d4f90e62f1a5654b8867ef96bafb8e8102c2288333e1a6ca6/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:7f277a4b3390ab4bebe597800a90da0edae882c6196d3038a73adf446c4f969f", size = 272178, upload-time = "2025-09-25T19:49:36.793Z" }, - { url = "https://files.pythonhosted.org/packages/c8/88/815b6d558a1e4d40ece04a2f84865b0fef233513bd85fd0e40c294272d62/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:79cfa161eda8d2ddf29acad370356b47f02387153b11d46042e93a0a95127493", size = 269295, upload-time = "2025-09-25T19:49:38.164Z" }, - { url = "https://files.pythonhosted.org/packages/51/8c/e0db387c79ab4931fc89827d37608c31cc57b6edc08ccd2386139028dc0d/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a5393eae5722bcef046a990b84dff02b954904c36a194f6cfc817d7dca6c6f0b", size = 271700, upload-time = "2025-09-25T19:49:39.917Z" }, - { url = "https://files.pythonhosted.org/packages/06/83/1570edddd150f572dbe9fc00f6203a89fc7d4226821f67328a85c330f239/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f4c94dec1b5ab5d522750cb059bb9409ea8872d4494fd152b53cca99f1ddd8c", size = 334034, upload-time = "2025-09-25T19:49:41.227Z" }, - { url = "https://files.pythonhosted.org/packages/c9/f2/ea64e51a65e56ae7a8a4ec236c2bfbdd4b23008abd50ac33fbb2d1d15424/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0cae4cb350934dfd74c020525eeae0a5f79257e8a201c0c176f4b84fdbf2a4b4", size = 352766, upload-time = "2025-09-25T19:49:43.08Z" }, - { url = "https://files.pythonhosted.org/packages/d7/d4/1a388d21ee66876f27d1a1f41287897d0c0f1712ef97d395d708ba93004c/bcrypt-5.0.0-cp314-cp314t-win32.whl", hash = "sha256:b17366316c654e1ad0306a6858e189fc835eca39f7eb2cafd6aaca8ce0c40a2e", size = 152449, upload-time = "2025-09-25T19:49:44.971Z" }, - { url = "https://files.pythonhosted.org/packages/3f/61/3291c2243ae0229e5bca5d19f4032cecad5dfb05a2557169d3a69dc0ba91/bcrypt-5.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:92864f54fb48b4c718fc92a32825d0e42265a627f956bc0361fe869f1adc3e7d", size = 149310, upload-time = "2025-09-25T19:49:46.162Z" }, - { url = "https://files.pythonhosted.org/packages/3e/89/4b01c52ae0c1a681d4021e5dd3e45b111a8fb47254a274fa9a378d8d834b/bcrypt-5.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dd19cf5184a90c873009244586396a6a884d591a5323f0e8a5922560718d4993", size = 143761, upload-time = "2025-09-25T19:49:47.345Z" }, - { url = "https://files.pythonhosted.org/packages/84/29/6237f151fbfe295fe3e074ecc6d44228faa1e842a81f6d34a02937ee1736/bcrypt-5.0.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:fc746432b951e92b58317af8e0ca746efe93e66555f1b40888865ef5bf56446b", size = 494553, upload-time = "2025-09-25T19:49:49.006Z" }, - { url = "https://files.pythonhosted.org/packages/45/b6/4c1205dde5e464ea3bd88e8742e19f899c16fa8916fb8510a851fae985b5/bcrypt-5.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c2388ca94ffee269b6038d48747f4ce8df0ffbea43f31abfa18ac72f0218effb", size = 275009, upload-time = "2025-09-25T19:49:50.581Z" }, - { url = "https://files.pythonhosted.org/packages/3b/71/427945e6ead72ccffe77894b2655b695ccf14ae1866cd977e185d606dd2f/bcrypt-5.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:560ddb6ec730386e7b3b26b8b4c88197aaed924430e7b74666a586ac997249ef", size = 278029, upload-time = "2025-09-25T19:49:52.533Z" }, - { url = "https://files.pythonhosted.org/packages/17/72/c344825e3b83c5389a369c8a8e58ffe1480b8a699f46c127c34580c4666b/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d79e5c65dcc9af213594d6f7f1fa2c98ad3fc10431e7aa53c176b441943efbdd", size = 275907, upload-time = "2025-09-25T19:49:54.709Z" }, - { url = "https://files.pythonhosted.org/packages/0b/7e/d4e47d2df1641a36d1212e5c0514f5291e1a956a7749f1e595c07a972038/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2b732e7d388fa22d48920baa267ba5d97cca38070b69c0e2d37087b381c681fd", size = 296500, upload-time = "2025-09-25T19:49:56.013Z" }, - { url = "https://files.pythonhosted.org/packages/0f/c3/0ae57a68be2039287ec28bc463b82e4b8dc23f9d12c0be331f4782e19108/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0c8e093ea2532601a6f686edbc2c6b2ec24131ff5c52f7610dd64fa4553b5464", size = 278412, upload-time = "2025-09-25T19:49:57.356Z" }, - { url = "https://files.pythonhosted.org/packages/45/2b/77424511adb11e6a99e3a00dcc7745034bee89036ad7d7e255a7e47be7d8/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5b1589f4839a0899c146e8892efe320c0fa096568abd9b95593efac50a87cb75", size = 275486, upload-time = "2025-09-25T19:49:59.116Z" }, - { url = "https://files.pythonhosted.org/packages/43/0a/405c753f6158e0f3f14b00b462d8bca31296f7ecfc8fc8bc7919c0c7d73a/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:89042e61b5e808b67daf24a434d89bab164d4de1746b37a8d173b6b14f3db9ff", size = 277940, upload-time = "2025-09-25T19:50:00.869Z" }, - { url = "https://files.pythonhosted.org/packages/62/83/b3efc285d4aadc1fa83db385ec64dcfa1707e890eb42f03b127d66ac1b7b/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:e3cf5b2560c7b5a142286f69bde914494b6d8f901aaa71e453078388a50881c4", size = 310776, upload-time = "2025-09-25T19:50:02.393Z" }, - { url = "https://files.pythonhosted.org/packages/95/7d/47ee337dacecde6d234890fe929936cb03ebc4c3a7460854bbd9c97780b8/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f632fd56fc4e61564f78b46a2269153122db34988e78b6be8b32d28507b7eaeb", size = 312922, upload-time = "2025-09-25T19:50:04.232Z" }, - { url = "https://files.pythonhosted.org/packages/d6/3a/43d494dfb728f55f4e1cf8fd435d50c16a2d75493225b54c8d06122523c6/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:801cad5ccb6b87d1b430f183269b94c24f248dddbbc5c1f78b6ed231743e001c", size = 341367, upload-time = "2025-09-25T19:50:05.559Z" }, - { url = "https://files.pythonhosted.org/packages/55/ab/a0727a4547e383e2e22a630e0f908113db37904f58719dc48d4622139b5c/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3cf67a804fc66fc217e6914a5635000259fbbbb12e78a99488e4d5ba445a71eb", size = 359187, upload-time = "2025-09-25T19:50:06.916Z" }, - { url = "https://files.pythonhosted.org/packages/1b/bb/461f352fdca663524b4643d8b09e8435b4990f17fbf4fea6bc2a90aa0cc7/bcrypt-5.0.0-cp38-abi3-win32.whl", hash = "sha256:3abeb543874b2c0524ff40c57a4e14e5d3a66ff33fb423529c88f180fd756538", size = 153752, upload-time = "2025-09-25T19:50:08.515Z" }, - { url = "https://files.pythonhosted.org/packages/41/aa/4190e60921927b7056820291f56fc57d00d04757c8b316b2d3c0d1d6da2c/bcrypt-5.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:35a77ec55b541e5e583eb3436ffbbf53b0ffa1fa16ca6782279daf95d146dcd9", size = 150881, upload-time = "2025-09-25T19:50:09.742Z" }, - { url = "https://files.pythonhosted.org/packages/54/12/cd77221719d0b39ac0b55dbd39358db1cd1246e0282e104366ebbfb8266a/bcrypt-5.0.0-cp38-abi3-win_arm64.whl", hash = "sha256:cde08734f12c6a4e28dc6755cd11d3bdfea608d93d958fffbe95a7026ebe4980", size = 144931, upload-time = "2025-09-25T19:50:11.016Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ba/2af136406e1c3839aea9ecadc2f6be2bcd1eff255bd451dd39bcf302c47a/bcrypt-5.0.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0c418ca99fd47e9c59a301744d63328f17798b5947b0f791e9af3c1c499c2d0a", size = 495313, upload-time = "2025-09-25T19:50:12.309Z" }, - { url = "https://files.pythonhosted.org/packages/ac/ee/2f4985dbad090ace5ad1f7dd8ff94477fe089b5fab2040bd784a3d5f187b/bcrypt-5.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddb4e1500f6efdd402218ffe34d040a1196c072e07929b9820f363a1fd1f4191", size = 275290, upload-time = "2025-09-25T19:50:13.673Z" }, - { url = "https://files.pythonhosted.org/packages/e4/6e/b77ade812672d15cf50842e167eead80ac3514f3beacac8902915417f8b7/bcrypt-5.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7aeef54b60ceddb6f30ee3db090351ecf0d40ec6e2abf41430997407a46d2254", size = 278253, upload-time = "2025-09-25T19:50:15.089Z" }, - { url = "https://files.pythonhosted.org/packages/36/c4/ed00ed32f1040f7990dac7115f82273e3c03da1e1a1587a778d8cea496d8/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f0ce778135f60799d89c9693b9b398819d15f1921ba15fe719acb3178215a7db", size = 276084, upload-time = "2025-09-25T19:50:16.699Z" }, - { url = "https://files.pythonhosted.org/packages/e7/c4/fa6e16145e145e87f1fa351bbd54b429354fd72145cd3d4e0c5157cf4c70/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a71f70ee269671460b37a449f5ff26982a6f2ba493b3eabdd687b4bf35f875ac", size = 297185, upload-time = "2025-09-25T19:50:18.525Z" }, - { url = "https://files.pythonhosted.org/packages/24/b4/11f8a31d8b67cca3371e046db49baa7c0594d71eb40ac8121e2fc0888db0/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8429e1c410b4073944f03bd778a9e066e7fad723564a52ff91841d278dfc822", size = 278656, upload-time = "2025-09-25T19:50:19.809Z" }, - { url = "https://files.pythonhosted.org/packages/ac/31/79f11865f8078e192847d2cb526e3fa27c200933c982c5b2869720fa5fce/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:edfcdcedd0d0f05850c52ba3127b1fce70b9f89e0fe5ff16517df7e81fa3cbb8", size = 275662, upload-time = "2025-09-25T19:50:21.567Z" }, - { url = "https://files.pythonhosted.org/packages/d4/8d/5e43d9584b3b3591a6f9b68f755a4da879a59712981ef5ad2a0ac1379f7a/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:611f0a17aa4a25a69362dcc299fda5c8a3d4f160e2abb3831041feb77393a14a", size = 278240, upload-time = "2025-09-25T19:50:23.305Z" }, - { url = "https://files.pythonhosted.org/packages/89/48/44590e3fc158620f680a978aafe8f87a4c4320da81ed11552f0323aa9a57/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:db99dca3b1fdc3db87d7c57eac0c82281242d1eabf19dcb8a6b10eb29a2e72d1", size = 311152, upload-time = "2025-09-25T19:50:24.597Z" }, - { url = "https://files.pythonhosted.org/packages/5f/85/e4fbfc46f14f47b0d20493669a625da5827d07e8a88ee460af6cd9768b44/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:5feebf85a9cefda32966d8171f5db7e3ba964b77fdfe31919622256f80f9cf42", size = 313284, upload-time = "2025-09-25T19:50:26.268Z" }, - { url = "https://files.pythonhosted.org/packages/25/ae/479f81d3f4594456a01ea2f05b132a519eff9ab5768a70430fa1132384b1/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3ca8a166b1140436e058298a34d88032ab62f15aae1c598580333dc21d27ef10", size = 341643, upload-time = "2025-09-25T19:50:28.02Z" }, - { url = "https://files.pythonhosted.org/packages/df/d2/36a086dee1473b14276cd6ea7f61aef3b2648710b5d7f1c9e032c29b859f/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:61afc381250c3182d9078551e3ac3a41da14154fbff647ddf52a769f588c4172", size = 359698, upload-time = "2025-09-25T19:50:31.347Z" }, - { url = "https://files.pythonhosted.org/packages/c0/f6/688d2cd64bfd0b14d805ddb8a565e11ca1fb0fd6817175d58b10052b6d88/bcrypt-5.0.0-cp39-abi3-win32.whl", hash = "sha256:64d7ce196203e468c457c37ec22390f1a61c85c6f0b8160fd752940ccfb3a683", size = 153725, upload-time = "2025-09-25T19:50:34.384Z" }, - { url = "https://files.pythonhosted.org/packages/9f/b9/9d9a641194a730bda138b3dfe53f584d61c58cd5230e37566e83ec2ffa0d/bcrypt-5.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:64ee8434b0da054d830fa8e89e1c8bf30061d539044a39524ff7dec90481e5c2", size = 150912, upload-time = "2025-09-25T19:50:35.69Z" }, - { url = "https://files.pythonhosted.org/packages/27/44/d2ef5e87509158ad2187f4dd0852df80695bb1ee0cfe0a684727b01a69e0/bcrypt-5.0.0-cp39-abi3-win_arm64.whl", hash = "sha256:f2347d3534e76bf50bca5500989d6c1d05ed64b440408057a37673282c654927", size = 144953, upload-time = "2025-09-25T19:50:37.32Z" }, -] - -[[package]] -name = "beartype" -version = "0.22.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, -] - -[[package]] -name = "black" -version = "26.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "mypy-extensions" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "platformdirs" }, - { name = "pytokens" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/13/88/560b11e521c522440af991d46848a2bde64b5f7202ec14e1f46f9509d328/black-26.1.0.tar.gz", hash = "sha256:d294ac3340eef9c9eb5d29288e96dc719ff269a88e27b396340459dd85da4c58", size = 658785, upload-time = "2026-01-18T04:50:11.993Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/13/710298938a61f0f54cdb4d1c0baeb672c01ff0358712eddaf29f76d32a0b/black-26.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6eeca41e70b5f5c84f2f913af857cf2ce17410847e1d54642e658e078da6544f", size = 1878189, upload-time = "2026-01-18T04:59:30.682Z" }, - { url = "https://files.pythonhosted.org/packages/79/a6/5179beaa57e5dbd2ec9f1c64016214057b4265647c62125aa6aeffb05392/black-26.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd39eef053e58e60204f2cdf059e2442e2eb08f15989eefe259870f89614c8b6", size = 1700178, upload-time = "2026-01-18T04:59:32.387Z" }, - { url = "https://files.pythonhosted.org/packages/8c/04/c96f79d7b93e8f09d9298b333ca0d31cd9b2ee6c46c274fd0f531de9dc61/black-26.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9459ad0d6cd483eacad4c6566b0f8e42af5e8b583cee917d90ffaa3778420a0a", size = 1777029, upload-time = "2026-01-18T04:59:33.767Z" }, - { url = "https://files.pythonhosted.org/packages/49/f9/71c161c4c7aa18bdda3776b66ac2dc07aed62053c7c0ff8bbda8c2624fe2/black-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a19915ec61f3a8746e8b10adbac4a577c6ba9851fa4a9e9fbfbcf319887a5791", size = 1406466, upload-time = "2026-01-18T04:59:35.177Z" }, - { url = "https://files.pythonhosted.org/packages/4a/8b/a7b0f974e473b159d0ac1b6bcefffeb6bec465898a516ee5cc989503cbc7/black-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:643d27fb5facc167c0b1b59d0315f2674a6e950341aed0fc05cf307d22bf4954", size = 1216393, upload-time = "2026-01-18T04:59:37.18Z" }, - { url = "https://files.pythonhosted.org/packages/79/04/fa2f4784f7237279332aa735cdfd5ae2e7730db0072fb2041dadda9ae551/black-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ba1d768fbfb6930fc93b0ecc32a43d8861ded16f47a40f14afa9bb04ab93d304", size = 1877781, upload-time = "2026-01-18T04:59:39.054Z" }, - { url = "https://files.pythonhosted.org/packages/cf/ad/5a131b01acc0e5336740a039628c0ab69d60cf09a2c87a4ec49f5826acda/black-26.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b807c240b64609cb0e80d2200a35b23c7df82259f80bef1b2c96eb422b4aac9", size = 1699670, upload-time = "2026-01-18T04:59:41.005Z" }, - { url = "https://files.pythonhosted.org/packages/da/7c/b05f22964316a52ab6b4265bcd52c0ad2c30d7ca6bd3d0637e438fc32d6e/black-26.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1de0f7d01cc894066a1153b738145b194414cc6eeaad8ef4397ac9abacf40f6b", size = 1775212, upload-time = "2026-01-18T04:59:42.545Z" }, - { url = "https://files.pythonhosted.org/packages/a6/a3/e8d1526bea0446e040193185353920a9506eab60a7d8beb062029129c7d2/black-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:91a68ae46bf07868963671e4d05611b179c2313301bd756a89ad4e3b3db2325b", size = 1409953, upload-time = "2026-01-18T04:59:44.357Z" }, - { url = "https://files.pythonhosted.org/packages/c7/5a/d62ebf4d8f5e3a1daa54adaab94c107b57be1b1a2f115a0249b41931e188/black-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:be5e2fe860b9bd9edbf676d5b60a9282994c03fbbd40fe8f5e75d194f96064ca", size = 1217707, upload-time = "2026-01-18T04:59:45.719Z" }, - { url = "https://files.pythonhosted.org/packages/6a/83/be35a175aacfce4b05584ac415fd317dd6c24e93a0af2dcedce0f686f5d8/black-26.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9dc8c71656a79ca49b8d3e2ce8103210c9481c57798b48deeb3a8bb02db5f115", size = 1871864, upload-time = "2026-01-18T04:59:47.586Z" }, - { url = "https://files.pythonhosted.org/packages/a5/f5/d33696c099450b1274d925a42b7a030cd3ea1f56d72e5ca8bbed5f52759c/black-26.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b22b3810451abe359a964cc88121d57f7bce482b53a066de0f1584988ca36e79", size = 1701009, upload-time = "2026-01-18T04:59:49.443Z" }, - { url = "https://files.pythonhosted.org/packages/1b/87/670dd888c537acb53a863bc15abbd85b22b429237d9de1b77c0ed6b79c42/black-26.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53c62883b3f999f14e5d30b5a79bd437236658ad45b2f853906c7cbe79de00af", size = 1767806, upload-time = "2026-01-18T04:59:50.769Z" }, - { url = "https://files.pythonhosted.org/packages/fe/9c/cd3deb79bfec5bcf30f9d2100ffeec63eecce826eb63e3961708b9431ff1/black-26.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:f016baaadc423dc960cdddf9acae679e71ee02c4c341f78f3179d7e4819c095f", size = 1433217, upload-time = "2026-01-18T04:59:52.218Z" }, - { url = "https://files.pythonhosted.org/packages/4e/29/f3be41a1cf502a283506f40f5d27203249d181f7a1a2abce1c6ce188035a/black-26.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:66912475200b67ef5a0ab665011964bf924745103f51977a78b4fb92a9fc1bf0", size = 1245773, upload-time = "2026-01-18T04:59:54.457Z" }, - { url = "https://files.pythonhosted.org/packages/e4/3d/51bdb3ecbfadfaf825ec0c75e1de6077422b4afa2091c6c9ba34fbfc0c2d/black-26.1.0-py3-none-any.whl", hash = "sha256:1054e8e47ebd686e078c0bb0eaf31e6ce69c966058d122f2c0c950311f9f3ede", size = 204010, upload-time = "2026-01-18T04:50:09.978Z" }, -] - -[[package]] -name = "build" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "os_name == 'nt'" }, - { name = "packaging" }, - { name = "pyproject-hooks" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/18/94eaffda7b329535d91f00fe605ab1f1e5cd68b2074d03f255c7d250687d/build-1.4.0.tar.gz", hash = "sha256:f1b91b925aa322be454f8330c6fb48b465da993d1e7e7e6fa35027ec49f3c936", size = 50054, upload-time = "2026-01-08T16:41:47.696Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl", hash = "sha256:6a07c1b8eb6f2b311b96fcbdbce5dab5fe637ffda0fd83c9cac622e927501596", size = 24141, upload-time = "2026-01-08T16:41:46.453Z" }, -] - -[[package]] -name = "cachetools" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/af/df70e9b65bc77a1cbe0768c0aa4617147f30f8306ded98c1744bcdc0ae1e/cachetools-7.0.0.tar.gz", hash = "sha256:a9abf18ff3b86c7d05b27ead412e235e16ae045925e531fae38d5fada5ed5b08", size = 35796, upload-time = "2026-02-01T18:59:47.411Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/df/2dd32cce20cbcf6f2ec456b58d44368161ad28320729f64e5e1d5d7bd0ae/cachetools-7.0.0-py3-none-any.whl", hash = "sha256:d52fef60e6e964a1969cfb61ccf6242a801b432790fe520d78720d757c81cbd2", size = 13487, upload-time = "2026-02-01T18:59:45.981Z" }, -] - -[[package]] -name = "certifi" -version = "2026.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, - { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, - { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, - { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, - { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, - { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, - { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, - { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, - { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, -] - -[[package]] -name = "chromadb" -version = "1.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "bcrypt" }, - { name = "build" }, - { name = "grpcio" }, - { name = "httpx" }, - { name = "importlib-resources" }, - { name = "jsonschema" }, - { name = "kubernetes" }, - { name = "mmh3" }, - { name = "numpy" }, - { name = "onnxruntime" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-otlp-proto-grpc" }, - { name = "opentelemetry-sdk" }, - { name = "orjson" }, - { name = "overrides" }, - { name = "posthog" }, - { name = "pybase64" }, - { name = "pydantic" }, - { name = "pypika" }, - { name = "pyyaml" }, - { name = "rich" }, - { name = "tenacity" }, - { name = "tokenizers" }, - { name = "tqdm" }, - { name = "typer" }, - { name = "typing-extensions" }, - { name = "uvicorn", extra = ["standard"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/03/35/24479ac00e74b86e388854a573a9ebe6d41c51c37e03d00864bb967d861f/chromadb-1.4.1.tar.gz", hash = "sha256:3cceb83e0a7a3c2db0752ebf62e9cfe652da657594c093fe07e74022581a58eb", size = 2226347, upload-time = "2026-01-14T19:18:15.189Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/f0/7c815bb80a2aaa349757ed0c743fa7e85bbe16f612057b25cf1809456a32/chromadb-1.4.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:05d98ffe4a9a5549c9a78eee7624277f9d99c53200a01f1176ecb1d31ea3c819", size = 20313209, upload-time = "2026-01-14T19:18:12.111Z" }, - { url = "https://files.pythonhosted.org/packages/a1/4b/c16236d56bf6bf144edbe5a03c431b59ba089bd6f86baefa8ebc288bf8b8/chromadb-1.4.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:38336431c01562cffdb3ef693f22f7a88df5304f942e01ed66ee0bbaf08f35da", size = 19634405, upload-time = "2026-01-14T19:18:08.264Z" }, - { url = "https://files.pythonhosted.org/packages/70/9c/33c6c3036e30632c2b64d333e92af3972e6bef423a8285e0edc5f487d322/chromadb-1.4.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaaf9c7d4ddbbdc74bd7cac45d9729032020cc6e65a2b8f313257e6c949beed", size = 20276410, upload-time = "2026-01-14T19:18:00.226Z" }, - { url = "https://files.pythonhosted.org/packages/29/bc/0c6a6255cd55fe384c1bda6bebb47b5ff9d5c535d993fd3451e4a3fbe42f/chromadb-1.4.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad50fbb5799dcaef5ae7613be583a06b44b637283db066396490863266f48623", size = 21082323, upload-time = "2026-01-14T19:18:04.604Z" }, - { url = "https://files.pythonhosted.org/packages/79/be/5092571f87ddf08022a3d9434d3374d3f5aa20ebad1c75d63107c0c046d6/chromadb-1.4.1-cp39-abi3-win_amd64.whl", hash = "sha256:cedc9941dad1081eb9be89a7f5f66374715d4f99f731f1eb9da900636c501330", size = 21376957, upload-time = "2026-01-14T19:18:16.95Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, -] - -[[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 = "coloredlogs" -version = "15.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "humanfriendly" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" }, -] - -[[package]] -name = "comm" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, -] - -[[package]] -name = "coverage" -version = "7.13.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ad/49/349848445b0e53660e258acbcc9b0d014895b6739237920886672240f84b/coverage-7.13.2.tar.gz", hash = "sha256:044c6951ec37146b72a50cc81ef02217d27d4c3640efd2640311393cbbf143d3", size = 826523, upload-time = "2026-01-25T13:00:04.889Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/39/e92a35f7800222d3f7b2cbb7bbc3b65672ae8d501cb31801b2d2bd7acdf1/coverage-7.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f106b2af193f965d0d3234f3f83fc35278c7fb935dfbde56ae2da3dd2c03b84d", size = 219142, upload-time = "2026-01-25T12:58:00.448Z" }, - { url = "https://files.pythonhosted.org/packages/45/7a/8bf9e9309c4c996e65c52a7c5a112707ecdd9fbaf49e10b5a705a402bbb4/coverage-7.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f45d21dc4d5d6bd29323f0320089ef7eae16e4bef712dff79d184fa7330af3", size = 219503, upload-time = "2026-01-25T12:58:02.451Z" }, - { url = "https://files.pythonhosted.org/packages/87/93/17661e06b7b37580923f3f12406ac91d78aeed293fb6da0b69cc7957582f/coverage-7.13.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fae91dfecd816444c74531a9c3d6ded17a504767e97aa674d44f638107265b99", size = 251006, upload-time = "2026-01-25T12:58:04.059Z" }, - { url = "https://files.pythonhosted.org/packages/12/f0/f9e59fb8c310171497f379e25db060abef9fa605e09d63157eebec102676/coverage-7.13.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:264657171406c114787b441484de620e03d8f7202f113d62fcd3d9688baa3e6f", size = 253750, upload-time = "2026-01-25T12:58:05.574Z" }, - { url = "https://files.pythonhosted.org/packages/e5/b1/1935e31add2232663cf7edd8269548b122a7d100047ff93475dbaaae673e/coverage-7.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae47d8dcd3ded0155afbb59c62bd8ab07ea0fd4902e1c40567439e6db9dcaf2f", size = 254862, upload-time = "2026-01-25T12:58:07.647Z" }, - { url = "https://files.pythonhosted.org/packages/af/59/b5e97071ec13df5f45da2b3391b6cdbec78ba20757bc92580a5b3d5fa53c/coverage-7.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a0b33e9fd838220b007ce8f299114d406c1e8edb21336af4c97a26ecfd185aa", size = 251420, upload-time = "2026-01-25T12:58:09.309Z" }, - { url = "https://files.pythonhosted.org/packages/3f/75/9495932f87469d013dc515fb0ce1aac5fa97766f38f6b1a1deb1ee7b7f3a/coverage-7.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b3becbea7f3ce9a2d4d430f223ec15888e4deb31395840a79e916368d6004cce", size = 252786, upload-time = "2026-01-25T12:58:10.909Z" }, - { url = "https://files.pythonhosted.org/packages/6a/59/af550721f0eb62f46f7b8cb7e6f1860592189267b1c411a4e3a057caacee/coverage-7.13.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f819c727a6e6eeb8711e4ce63d78c620f69630a2e9d53bc95ca5379f57b6ba94", size = 250928, upload-time = "2026-01-25T12:58:12.449Z" }, - { url = "https://files.pythonhosted.org/packages/9b/b1/21b4445709aae500be4ab43bbcfb4e53dc0811c3396dcb11bf9f23fd0226/coverage-7.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:4f7b71757a3ab19f7ba286e04c181004c1d61be921795ee8ba6970fd0ec91da5", size = 250496, upload-time = "2026-01-25T12:58:14.047Z" }, - { url = "https://files.pythonhosted.org/packages/ba/b1/0f5d89dfe0392990e4f3980adbde3eb34885bc1effb2dc369e0bf385e389/coverage-7.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b7fc50d2afd2e6b4f6f2f403b70103d280a8e0cb35320cbbe6debcda02a1030b", size = 252373, upload-time = "2026-01-25T12:58:15.976Z" }, - { url = "https://files.pythonhosted.org/packages/01/c9/0cf1a6a57a9968cc049a6b896693faa523c638a5314b1fc374eb2b2ac904/coverage-7.13.2-cp312-cp312-win32.whl", hash = "sha256:292250282cf9bcf206b543d7608bda17ca6fc151f4cbae949fc7e115112fbd41", size = 221696, upload-time = "2026-01-25T12:58:17.517Z" }, - { url = "https://files.pythonhosted.org/packages/4d/05/d7540bf983f09d32803911afed135524570f8c47bb394bf6206c1dc3a786/coverage-7.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:eeea10169fac01549a7921d27a3e517194ae254b542102267bef7a93ed38c40e", size = 222504, upload-time = "2026-01-25T12:58:19.115Z" }, - { url = "https://files.pythonhosted.org/packages/15/8b/1a9f037a736ced0a12aacf6330cdaad5008081142a7070bc58b0f7930cbc/coverage-7.13.2-cp312-cp312-win_arm64.whl", hash = "sha256:2a5b567f0b635b592c917f96b9a9cb3dbd4c320d03f4bf94e9084e494f2e8894", size = 221120, upload-time = "2026-01-25T12:58:21.334Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f0/3d3eac7568ab6096ff23791a526b0048a1ff3f49d0e236b2af6fb6558e88/coverage-7.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ed75de7d1217cf3b99365d110975f83af0528c849ef5180a12fd91b5064df9d6", size = 219168, upload-time = "2026-01-25T12:58:23.376Z" }, - { url = "https://files.pythonhosted.org/packages/a3/a6/f8b5cfeddbab95fdef4dcd682d82e5dcff7a112ced57a959f89537ee9995/coverage-7.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97e596de8fa9bada4d88fde64a3f4d37f1b6131e4faa32bad7808abc79887ddc", size = 219537, upload-time = "2026-01-25T12:58:24.932Z" }, - { url = "https://files.pythonhosted.org/packages/7b/e6/8d8e6e0c516c838229d1e41cadcec91745f4b1031d4db17ce0043a0423b4/coverage-7.13.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:68c86173562ed4413345410c9480a8d64864ac5e54a5cda236748031e094229f", size = 250528, upload-time = "2026-01-25T12:58:26.567Z" }, - { url = "https://files.pythonhosted.org/packages/8e/78/befa6640f74092b86961f957f26504c8fba3d7da57cc2ab7407391870495/coverage-7.13.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7be4d613638d678b2b3773b8f687537b284d7074695a43fe2fbbfc0e31ceaed1", size = 253132, upload-time = "2026-01-25T12:58:28.251Z" }, - { url = "https://files.pythonhosted.org/packages/9d/10/1630db1edd8ce675124a2ee0f7becc603d2bb7b345c2387b4b95c6907094/coverage-7.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7f63ce526a96acd0e16c4af8b50b64334239550402fb1607ce6a584a6d62ce9", size = 254374, upload-time = "2026-01-25T12:58:30.294Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1d/0d9381647b1e8e6d310ac4140be9c428a0277330991e0c35bdd751e338a4/coverage-7.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:406821f37f864f968e29ac14c3fccae0fec9fdeba48327f0341decf4daf92d7c", size = 250762, upload-time = "2026-01-25T12:58:32.036Z" }, - { url = "https://files.pythonhosted.org/packages/43/e4/5636dfc9a7c871ee8776af83ee33b4c26bc508ad6cee1e89b6419a366582/coverage-7.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ee68e5a4e3e5443623406b905db447dceddffee0dceb39f4e0cd9ec2a35004b5", size = 252502, upload-time = "2026-01-25T12:58:33.961Z" }, - { url = "https://files.pythonhosted.org/packages/02/2a/7ff2884d79d420cbb2d12fed6fff727b6d0ef27253140d3cdbbd03187ee0/coverage-7.13.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2ee0e58cca0c17dd9c6c1cdde02bb705c7b3fbfa5f3b0b5afeda20d4ebff8ef4", size = 250463, upload-time = "2026-01-25T12:58:35.529Z" }, - { url = "https://files.pythonhosted.org/packages/91/c0/ba51087db645b6c7261570400fc62c89a16278763f36ba618dc8657a187b/coverage-7.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e5bbb5018bf76a56aabdb64246b5288d5ae1b7d0dd4d0534fe86df2c2992d1c", size = 250288, upload-time = "2026-01-25T12:58:37.226Z" }, - { url = "https://files.pythonhosted.org/packages/03/07/44e6f428551c4d9faf63ebcefe49b30e5c89d1be96f6a3abd86a52da9d15/coverage-7.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a55516c68ef3e08e134e818d5e308ffa6b1337cc8b092b69b24287bf07d38e31", size = 252063, upload-time = "2026-01-25T12:58:38.821Z" }, - { url = "https://files.pythonhosted.org/packages/c2/67/35b730ad7e1859dd57e834d1bc06080d22d2f87457d53f692fce3f24a5a9/coverage-7.13.2-cp313-cp313-win32.whl", hash = "sha256:5b20211c47a8abf4abc3319d8ce2464864fa9f30c5fcaf958a3eed92f4f1fef8", size = 221716, upload-time = "2026-01-25T12:58:40.484Z" }, - { url = "https://files.pythonhosted.org/packages/0d/82/e5fcf5a97c72f45fc14829237a6550bf49d0ab882ac90e04b12a69db76b4/coverage-7.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:14f500232e521201cf031549fb1ebdfc0a40f401cf519157f76c397e586c3beb", size = 222522, upload-time = "2026-01-25T12:58:43.247Z" }, - { url = "https://files.pythonhosted.org/packages/b1/f1/25d7b2f946d239dd2d6644ca2cc060d24f97551e2af13b6c24c722ae5f97/coverage-7.13.2-cp313-cp313-win_arm64.whl", hash = "sha256:9779310cb5a9778a60c899f075a8514c89fa6d10131445c2207fc893e0b14557", size = 221145, upload-time = "2026-01-25T12:58:45Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f7/080376c029c8f76fadfe43911d0daffa0cbdc9f9418a0eead70c56fb7f4b/coverage-7.13.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e64fa5a1e41ce5df6b547cbc3d3699381c9e2c2c369c67837e716ed0f549d48e", size = 219861, upload-time = "2026-01-25T12:58:46.586Z" }, - { url = "https://files.pythonhosted.org/packages/42/11/0b5e315af5ab35f4c4a70e64d3314e4eec25eefc6dec13be3a7d5ffe8ac5/coverage-7.13.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b01899e82a04085b6561eb233fd688474f57455e8ad35cd82286463ba06332b7", size = 220207, upload-time = "2026-01-25T12:58:48.277Z" }, - { url = "https://files.pythonhosted.org/packages/b2/0c/0874d0318fb1062117acbef06a09cf8b63f3060c22265adaad24b36306b7/coverage-7.13.2-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:838943bea48be0e2768b0cf7819544cdedc1bbb2f28427eabb6eb8c9eb2285d3", size = 261504, upload-time = "2026-01-25T12:58:49.904Z" }, - { url = "https://files.pythonhosted.org/packages/83/5e/1cd72c22ecb30751e43a72f40ba50fcef1b7e93e3ea823bd9feda8e51f9a/coverage-7.13.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:93d1d25ec2b27e90bcfef7012992d1f5121b51161b8bffcda756a816cf13c2c3", size = 263582, upload-time = "2026-01-25T12:58:51.582Z" }, - { url = "https://files.pythonhosted.org/packages/9b/da/8acf356707c7a42df4d0657020308e23e5a07397e81492640c186268497c/coverage-7.13.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93b57142f9621b0d12349c43fc7741fe578e4bc914c1e5a54142856cfc0bf421", size = 266008, upload-time = "2026-01-25T12:58:53.234Z" }, - { url = "https://files.pythonhosted.org/packages/41/41/ea1730af99960309423c6ea8d6a4f1fa5564b2d97bd1d29dda4b42611f04/coverage-7.13.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f06799ae1bdfff7ccb8665d75f8291c69110ba9585253de254688aa8a1ccc6c5", size = 260762, upload-time = "2026-01-25T12:58:55.372Z" }, - { url = "https://files.pythonhosted.org/packages/22/fa/02884d2080ba71db64fdc127b311db60e01fe6ba797d9c8363725e39f4d5/coverage-7.13.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f9405ab4f81d490811b1d91c7a20361135a2df4c170e7f0b747a794da5b7f23", size = 263571, upload-time = "2026-01-25T12:58:57.52Z" }, - { url = "https://files.pythonhosted.org/packages/d2/6b/4083aaaeba9b3112f55ac57c2ce7001dc4d8fa3fcc228a39f09cc84ede27/coverage-7.13.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f9ab1d5b86f8fbc97a5b3cd6280a3fd85fef3b028689d8a2c00918f0d82c728c", size = 261200, upload-time = "2026-01-25T12:58:59.255Z" }, - { url = "https://files.pythonhosted.org/packages/e9/d2/aea92fa36d61955e8c416ede9cf9bf142aa196f3aea214bb67f85235a050/coverage-7.13.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:f674f59712d67e841525b99e5e2b595250e39b529c3bda14764e4f625a3fa01f", size = 260095, upload-time = "2026-01-25T12:59:01.066Z" }, - { url = "https://files.pythonhosted.org/packages/0d/ae/04ffe96a80f107ea21b22b2367175c621da920063260a1c22f9452fd7866/coverage-7.13.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c6cadac7b8ace1ba9144feb1ae3cb787a6065ba6d23ffc59a934b16406c26573", size = 262284, upload-time = "2026-01-25T12:59:02.802Z" }, - { url = "https://files.pythonhosted.org/packages/1c/7a/6f354dcd7dfc41297791d6fb4e0d618acb55810bde2c1fd14b3939e05c2b/coverage-7.13.2-cp313-cp313t-win32.whl", hash = "sha256:14ae4146465f8e6e6253eba0cccd57423e598a4cb925958b240c805300918343", size = 222389, upload-time = "2026-01-25T12:59:04.563Z" }, - { url = "https://files.pythonhosted.org/packages/8d/d5/080ad292a4a3d3daf411574be0a1f56d6dee2c4fdf6b005342be9fac807f/coverage-7.13.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9074896edd705a05769e3de0eac0a8388484b503b68863dd06d5e473f874fd47", size = 223450, upload-time = "2026-01-25T12:59:06.677Z" }, - { url = "https://files.pythonhosted.org/packages/88/96/df576fbacc522e9fb8d1c4b7a7fc62eb734be56e2cba1d88d2eabe08ea3f/coverage-7.13.2-cp313-cp313t-win_arm64.whl", hash = "sha256:69e526e14f3f854eda573d3cf40cffd29a1a91c684743d904c33dbdcd0e0f3e7", size = 221707, upload-time = "2026-01-25T12:59:08.363Z" }, - { url = "https://files.pythonhosted.org/packages/55/53/1da9e51a0775634b04fcc11eb25c002fc58ee4f92ce2e8512f94ac5fc5bf/coverage-7.13.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:387a825f43d680e7310e6f325b2167dd093bc8ffd933b83e9aa0983cf6e0a2ef", size = 219213, upload-time = "2026-01-25T12:59:11.909Z" }, - { url = "https://files.pythonhosted.org/packages/46/35/b3caac3ebbd10230fea5a33012b27d19e999a17c9285c4228b4b2e35b7da/coverage-7.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f0d7fea9d8e5d778cd5a9e8fc38308ad688f02040e883cdc13311ef2748cb40f", size = 219549, upload-time = "2026-01-25T12:59:13.638Z" }, - { url = "https://files.pythonhosted.org/packages/76/9c/e1cf7def1bdc72c1907e60703983a588f9558434a2ff94615747bd73c192/coverage-7.13.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e080afb413be106c95c4ee96b4fffdc9e2fa56a8bbf90b5c0918e5c4449412f5", size = 250586, upload-time = "2026-01-25T12:59:15.808Z" }, - { url = "https://files.pythonhosted.org/packages/ba/49/f54ec02ed12be66c8d8897270505759e057b0c68564a65c429ccdd1f139e/coverage-7.13.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7fc042ba3c7ce25b8a9f097eb0f32a5ce1ccdb639d9eec114e26def98e1f8a4", size = 253093, upload-time = "2026-01-25T12:59:17.491Z" }, - { url = "https://files.pythonhosted.org/packages/fb/5e/aaf86be3e181d907e23c0f61fccaeb38de8e6f6b47aed92bf57d8fc9c034/coverage-7.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0ba505e021557f7f8173ee8cd6b926373d8653e5ff7581ae2efce1b11ef4c27", size = 254446, upload-time = "2026-01-25T12:59:19.752Z" }, - { url = "https://files.pythonhosted.org/packages/28/c8/a5fa01460e2d75b0c853b392080d6829d3ca8b5ab31e158fa0501bc7c708/coverage-7.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7de326f80e3451bd5cc7239ab46c73ddb658fe0b7649476bc7413572d36cd548", size = 250615, upload-time = "2026-01-25T12:59:21.928Z" }, - { url = "https://files.pythonhosted.org/packages/86/0b/6d56315a55f7062bb66410732c24879ccb2ec527ab6630246de5fe45a1df/coverage-7.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:abaea04f1e7e34841d4a7b343904a3f59481f62f9df39e2cd399d69a187a9660", size = 252452, upload-time = "2026-01-25T12:59:23.592Z" }, - { url = "https://files.pythonhosted.org/packages/30/19/9bc550363ebc6b0ea121977ee44d05ecd1e8bf79018b8444f1028701c563/coverage-7.13.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9f93959ee0c604bccd8e0697be21de0887b1f73efcc3aa73a3ec0fd13feace92", size = 250418, upload-time = "2026-01-25T12:59:25.392Z" }, - { url = "https://files.pythonhosted.org/packages/1f/53/580530a31ca2f0cc6f07a8f2ab5460785b02bb11bdf815d4c4d37a4c5169/coverage-7.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:13fe81ead04e34e105bf1b3c9f9cdf32ce31736ee5d90a8d2de02b9d3e1bcb82", size = 250231, upload-time = "2026-01-25T12:59:27.888Z" }, - { url = "https://files.pythonhosted.org/packages/e2/42/dd9093f919dc3088cb472893651884bd675e3df3d38a43f9053656dca9a2/coverage-7.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d6d16b0f71120e365741bca2cb473ca6fe38930bc5431c5e850ba949f708f892", size = 251888, upload-time = "2026-01-25T12:59:29.636Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a6/0af4053e6e819774626e133c3d6f70fae4d44884bfc4b126cb647baee8d3/coverage-7.13.2-cp314-cp314-win32.whl", hash = "sha256:9b2f4714bb7d99ba3790ee095b3b4ac94767e1347fe424278a0b10acb3ff04fe", size = 221968, upload-time = "2026-01-25T12:59:31.424Z" }, - { url = "https://files.pythonhosted.org/packages/c4/cc/5aff1e1f80d55862442855517bb8ad8ad3a68639441ff6287dde6a58558b/coverage-7.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:e4121a90823a063d717a96e0a0529c727fb31ea889369a0ee3ec00ed99bf6859", size = 222783, upload-time = "2026-01-25T12:59:33.118Z" }, - { url = "https://files.pythonhosted.org/packages/de/20/09abafb24f84b3292cc658728803416c15b79f9ee5e68d25238a895b07d9/coverage-7.13.2-cp314-cp314-win_arm64.whl", hash = "sha256:6873f0271b4a15a33e7590f338d823f6f66f91ed147a03938d7ce26efd04eee6", size = 221348, upload-time = "2026-01-25T12:59:34.939Z" }, - { url = "https://files.pythonhosted.org/packages/b6/60/a3820c7232db63be060e4019017cd3426751c2699dab3c62819cdbcea387/coverage-7.13.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f61d349f5b7cd95c34017f1927ee379bfbe9884300d74e07cf630ccf7a610c1b", size = 219950, upload-time = "2026-01-25T12:59:36.624Z" }, - { url = "https://files.pythonhosted.org/packages/fd/37/e4ef5975fdeb86b1e56db9a82f41b032e3d93a840ebaf4064f39e770d5c5/coverage-7.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a43d34ce714f4ca674c0d90beb760eb05aad906f2c47580ccee9da8fe8bfb417", size = 220209, upload-time = "2026-01-25T12:59:38.339Z" }, - { url = "https://files.pythonhosted.org/packages/54/df/d40e091d00c51adca1e251d3b60a8b464112efa3004949e96a74d7c19a64/coverage-7.13.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bff1b04cb9d4900ce5c56c4942f047dc7efe57e2608cb7c3c8936e9970ccdbee", size = 261576, upload-time = "2026-01-25T12:59:40.446Z" }, - { url = "https://files.pythonhosted.org/packages/c5/44/5259c4bed54e3392e5c176121af9f71919d96dde853386e7730e705f3520/coverage-7.13.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6ae99e4560963ad8e163e819e5d77d413d331fd00566c1e0856aa252303552c1", size = 263704, upload-time = "2026-01-25T12:59:42.346Z" }, - { url = "https://files.pythonhosted.org/packages/16/bd/ae9f005827abcbe2c70157459ae86053971c9fa14617b63903abbdce26d9/coverage-7.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e79a8c7d461820257d9aa43716c4efc55366d7b292e46b5b37165be1d377405d", size = 266109, upload-time = "2026-01-25T12:59:44.073Z" }, - { url = "https://files.pythonhosted.org/packages/a2/c0/8e279c1c0f5b1eaa3ad9b0fb7a5637fc0379ea7d85a781c0fe0bb3cfc2ab/coverage-7.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:060ee84f6a769d40c492711911a76811b4befb6fba50abb450371abb720f5bd6", size = 260686, upload-time = "2026-01-25T12:59:45.804Z" }, - { url = "https://files.pythonhosted.org/packages/b2/47/3a8112627e9d863e7cddd72894171c929e94491a597811725befdcd76bce/coverage-7.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bca209d001fd03ea2d978f8a4985093240a355c93078aee3f799852c23f561a", size = 263568, upload-time = "2026-01-25T12:59:47.929Z" }, - { url = "https://files.pythonhosted.org/packages/92/bc/7ea367d84afa3120afc3ce6de294fd2dcd33b51e2e7fbe4bbfd200f2cb8c/coverage-7.13.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6b8092aa38d72f091db61ef83cb66076f18f02da3e1a75039a4f218629600e04", size = 261174, upload-time = "2026-01-25T12:59:49.717Z" }, - { url = "https://files.pythonhosted.org/packages/33/b7/f1092dcecb6637e31cc2db099581ee5c61a17647849bae6b8261a2b78430/coverage-7.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4a3158dc2dcce5200d91ec28cd315c999eebff355437d2765840555d765a6e5f", size = 260017, upload-time = "2026-01-25T12:59:51.463Z" }, - { url = "https://files.pythonhosted.org/packages/2b/cd/f3d07d4b95fbe1a2ef0958c15da614f7e4f557720132de34d2dc3aa7e911/coverage-7.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3973f353b2d70bd9796cc12f532a05945232ccae966456c8ed7034cb96bbfd6f", size = 262337, upload-time = "2026-01-25T12:59:53.407Z" }, - { url = "https://files.pythonhosted.org/packages/e0/db/b0d5b2873a07cb1e06a55d998697c0a5a540dcefbf353774c99eb3874513/coverage-7.13.2-cp314-cp314t-win32.whl", hash = "sha256:79f6506a678a59d4ded048dc72f1859ebede8ec2b9a2d509ebe161f01c2879d3", size = 222749, upload-time = "2026-01-25T12:59:56.316Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2f/838a5394c082ac57d85f57f6aba53093b30d9089781df72412126505716f/coverage-7.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:196bfeabdccc5a020a57d5a368c681e3a6ceb0447d153aeccc1ab4d70a5032ba", size = 223857, upload-time = "2026-01-25T12:59:58.201Z" }, - { url = "https://files.pythonhosted.org/packages/44/d4/b608243e76ead3a4298824b50922b89ef793e50069ce30316a65c1b4d7ef/coverage-7.13.2-cp314-cp314t-win_arm64.whl", hash = "sha256:69269ab58783e090bfbf5b916ab3d188126e22d6070bbfc93098fdd474ef937c", size = 221881, upload-time = "2026-01-25T13:00:00.449Z" }, - { url = "https://files.pythonhosted.org/packages/d2/db/d291e30fdf7ea617a335531e72294e0c723356d7fdde8fba00610a76bda9/coverage-7.13.2-py3-none-any.whl", hash = "sha256:40ce1ea1e25125556d8e76bd0b61500839a07944cc287ac21d5626f3e620cad5", size = 210943, upload-time = "2026-01-25T13:00:02.388Z" }, -] - -[[package]] -name = "cryptography" -version = "46.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/19/f748958276519adf6a0c1e79e7b8860b4830dda55ccdf29f2719b5fc499c/cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59", size = 749301, upload-time = "2026-01-28T00:24:37.379Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/99/157aae7949a5f30d51fcb1a9851e8ebd5c74bf99b5285d8bb4b8b9ee641e/cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485", size = 7173686, upload-time = "2026-01-28T00:23:07.515Z" }, - { url = "https://files.pythonhosted.org/packages/87/91/874b8910903159043b5c6a123b7e79c4559ddd1896e38967567942635778/cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc", size = 4275871, upload-time = "2026-01-28T00:23:09.439Z" }, - { url = "https://files.pythonhosted.org/packages/c0/35/690e809be77896111f5b195ede56e4b4ed0435b428c2f2b6d35046fbb5e8/cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0", size = 4423124, upload-time = "2026-01-28T00:23:11.529Z" }, - { url = "https://files.pythonhosted.org/packages/1a/5b/a26407d4f79d61ca4bebaa9213feafdd8806dc69d3d290ce24996d3cfe43/cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa", size = 4277090, upload-time = "2026-01-28T00:23:13.123Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d8/4bb7aec442a9049827aa34cee1aa83803e528fa55da9a9d45d01d1bb933e/cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81", size = 4947652, upload-time = "2026-01-28T00:23:14.554Z" }, - { url = "https://files.pythonhosted.org/packages/2b/08/f83e2e0814248b844265802d081f2fac2f1cbe6cd258e72ba14ff006823a/cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255", size = 4455157, upload-time = "2026-01-28T00:23:16.443Z" }, - { url = "https://files.pythonhosted.org/packages/0a/05/19d849cf4096448779d2dcc9bb27d097457dac36f7273ffa875a93b5884c/cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e", size = 3981078, upload-time = "2026-01-28T00:23:17.838Z" }, - { url = "https://files.pythonhosted.org/packages/e6/89/f7bac81d66ba7cde867a743ea5b37537b32b5c633c473002b26a226f703f/cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c", size = 4276213, upload-time = "2026-01-28T00:23:19.257Z" }, - { url = "https://files.pythonhosted.org/packages/da/9f/7133e41f24edd827020ad21b068736e792bc68eecf66d93c924ad4719fb3/cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32", size = 4912190, upload-time = "2026-01-28T00:23:21.244Z" }, - { url = "https://files.pythonhosted.org/packages/a6/f7/6d43cbaddf6f65b24816e4af187d211f0bc536a29961f69faedc48501d8e/cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616", size = 4454641, upload-time = "2026-01-28T00:23:22.866Z" }, - { url = "https://files.pythonhosted.org/packages/9e/4f/ebd0473ad656a0ac912a16bd07db0f5d85184924e14fc88feecae2492834/cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0", size = 4405159, upload-time = "2026-01-28T00:23:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d1/f7/7923886f32dc47e27adeff8246e976d77258fd2aa3efdd1754e4e323bf49/cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0", size = 4666059, upload-time = "2026-01-28T00:23:26.766Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a7/0fca0fd3591dffc297278a61813d7f661a14243dd60f499a7a5b48acb52a/cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5", size = 3026378, upload-time = "2026-01-28T00:23:28.317Z" }, - { url = "https://files.pythonhosted.org/packages/2d/12/652c84b6f9873f0909374864a57b003686c642ea48c84d6c7e2c515e6da5/cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b", size = 3478614, upload-time = "2026-01-28T00:23:30.275Z" }, - { url = "https://files.pythonhosted.org/packages/b9/27/542b029f293a5cce59349d799d4d8484b3b1654a7b9a0585c266e974a488/cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908", size = 7116417, upload-time = "2026-01-28T00:23:31.958Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f5/559c25b77f40b6bf828eabaf988efb8b0e17b573545edb503368ca0a2a03/cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da", size = 4264508, upload-time = "2026-01-28T00:23:34.264Z" }, - { url = "https://files.pythonhosted.org/packages/49/a1/551fa162d33074b660dc35c9bc3616fefa21a0e8c1edd27b92559902e408/cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829", size = 4409080, upload-time = "2026-01-28T00:23:35.793Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/4d8d129a755f5d6df1bbee69ea2f35ebfa954fa1847690d1db2e8bca46a5/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2", size = 4270039, upload-time = "2026-01-28T00:23:37.263Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f5/ed3fcddd0a5e39321e595e144615399e47e7c153a1fb8c4862aec3151ff9/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085", size = 4926748, upload-time = "2026-01-28T00:23:38.884Z" }, - { url = "https://files.pythonhosted.org/packages/43/ae/9f03d5f0c0c00e85ecb34f06d3b79599f20630e4db91b8a6e56e8f83d410/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b", size = 4442307, upload-time = "2026-01-28T00:23:40.56Z" }, - { url = "https://files.pythonhosted.org/packages/8b/22/e0f9f2dae8040695103369cf2283ef9ac8abe4d51f68710bec2afd232609/cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd", size = 3959253, upload-time = "2026-01-28T00:23:42.827Z" }, - { url = "https://files.pythonhosted.org/packages/01/5b/6a43fcccc51dae4d101ac7d378a8724d1ba3de628a24e11bf2f4f43cba4d/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2", size = 4269372, upload-time = "2026-01-28T00:23:44.655Z" }, - { url = "https://files.pythonhosted.org/packages/17/b7/0f6b8c1dd0779df2b526e78978ff00462355e31c0a6f6cff8a3e99889c90/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e", size = 4891908, upload-time = "2026-01-28T00:23:46.48Z" }, - { url = "https://files.pythonhosted.org/packages/83/17/259409b8349aa10535358807a472c6a695cf84f106022268d31cea2b6c97/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f", size = 4441254, upload-time = "2026-01-28T00:23:48.403Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fe/e4a1b0c989b00cee5ffa0764401767e2d1cf59f45530963b894129fd5dce/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82", size = 4396520, upload-time = "2026-01-28T00:23:50.26Z" }, - { url = "https://files.pythonhosted.org/packages/b3/81/ba8fd9657d27076eb40d6a2f941b23429a3c3d2f56f5a921d6b936a27bc9/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c", size = 4651479, upload-time = "2026-01-28T00:23:51.674Z" }, - { url = "https://files.pythonhosted.org/packages/00/03/0de4ed43c71c31e4fe954edd50b9d28d658fef56555eba7641696370a8e2/cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061", size = 3001986, upload-time = "2026-01-28T00:23:53.485Z" }, - { url = "https://files.pythonhosted.org/packages/5c/70/81830b59df7682917d7a10f833c4dab2a5574cd664e86d18139f2b421329/cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7", size = 3468288, upload-time = "2026-01-28T00:23:55.09Z" }, - { url = "https://files.pythonhosted.org/packages/56/f7/f648fdbb61d0d45902d3f374217451385edc7e7768d1b03ff1d0e5ffc17b/cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab", size = 7169583, upload-time = "2026-01-28T00:23:56.558Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cc/8f3224cbb2a928de7298d6ed4790f5ebc48114e02bdc9559196bfb12435d/cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef", size = 4275419, upload-time = "2026-01-28T00:23:58.364Z" }, - { url = "https://files.pythonhosted.org/packages/17/43/4a18faa7a872d00e4264855134ba82d23546c850a70ff209e04ee200e76f/cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d", size = 4419058, upload-time = "2026-01-28T00:23:59.867Z" }, - { url = "https://files.pythonhosted.org/packages/ee/64/6651969409821d791ba12346a124f55e1b76f66a819254ae840a965d4b9c/cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973", size = 4278151, upload-time = "2026-01-28T00:24:01.731Z" }, - { url = "https://files.pythonhosted.org/packages/20/0b/a7fce65ee08c3c02f7a8310cc090a732344066b990ac63a9dfd0a655d321/cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4", size = 4939441, upload-time = "2026-01-28T00:24:03.175Z" }, - { url = "https://files.pythonhosted.org/packages/db/a7/20c5701e2cd3e1dfd7a19d2290c522a5f435dd30957d431dcb531d0f1413/cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af", size = 4451617, upload-time = "2026-01-28T00:24:05.403Z" }, - { url = "https://files.pythonhosted.org/packages/00/dc/3e16030ea9aa47b63af6524c354933b4fb0e352257c792c4deeb0edae367/cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263", size = 3977774, upload-time = "2026-01-28T00:24:06.851Z" }, - { url = "https://files.pythonhosted.org/packages/42/c8/ad93f14118252717b465880368721c963975ac4b941b7ef88f3c56bf2897/cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095", size = 4277008, upload-time = "2026-01-28T00:24:08.926Z" }, - { url = "https://files.pythonhosted.org/packages/00/cf/89c99698151c00a4631fbfcfcf459d308213ac29e321b0ff44ceeeac82f1/cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b", size = 4903339, upload-time = "2026-01-28T00:24:12.009Z" }, - { url = "https://files.pythonhosted.org/packages/03/c3/c90a2cb358de4ac9309b26acf49b2a100957e1ff5cc1e98e6c4996576710/cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019", size = 4451216, upload-time = "2026-01-28T00:24:13.975Z" }, - { url = "https://files.pythonhosted.org/packages/96/2c/8d7f4171388a10208671e181ca43cdc0e596d8259ebacbbcfbd16de593da/cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4", size = 4404299, upload-time = "2026-01-28T00:24:16.169Z" }, - { url = "https://files.pythonhosted.org/packages/e9/23/cbb2036e450980f65c6e0a173b73a56ff3bccd8998965dea5cc9ddd424a5/cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b", size = 4664837, upload-time = "2026-01-28T00:24:17.629Z" }, - { url = "https://files.pythonhosted.org/packages/0a/21/f7433d18fe6d5845329cbdc597e30caf983229c7a245bcf54afecc555938/cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc", size = 3009779, upload-time = "2026-01-28T00:24:20.198Z" }, - { url = "https://files.pythonhosted.org/packages/3a/6a/bd2e7caa2facffedf172a45c1a02e551e6d7d4828658c9a245516a598d94/cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976", size = 3466633, upload-time = "2026-01-28T00:24:21.851Z" }, -] - -[[package]] -name = "cuda-bindings" -version = "12.9.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-pathfinder" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/c1/dabe88f52c3e3760d861401bb994df08f672ec893b8f7592dc91626adcf3/cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8", size = 12151019, upload-time = "2025-10-21T14:51:43.167Z" }, - { url = "https://files.pythonhosted.org/packages/63/56/e465c31dc9111be3441a9ba7df1941fe98f4aa6e71e8788a3fb4534ce24d/cuda_bindings-12.9.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:32bdc5a76906be4c61eb98f546a6786c5773a881f3b166486449b5d141e4a39f", size = 11906628, upload-time = "2025-10-21T14:51:49.905Z" }, - { url = "https://files.pythonhosted.org/packages/a3/84/1e6be415e37478070aeeee5884c2022713c1ecc735e6d82d744de0252eee/cuda_bindings-12.9.4-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56e0043c457a99ac473ddc926fe0dc4046694d99caef633e92601ab52cbe17eb", size = 11925991, upload-time = "2025-10-21T14:51:56.535Z" }, - { url = "https://files.pythonhosted.org/packages/d1/af/6dfd8f2ed90b1d4719bc053ff8940e494640fe4212dc3dd72f383e4992da/cuda_bindings-12.9.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b72ee72a9cc1b531db31eebaaee5c69a8ec3500e32c6933f2d3b15297b53686", size = 11922703, upload-time = "2025-10-21T14:52:03.585Z" }, - { url = "https://files.pythonhosted.org/packages/6c/19/90ac264acc00f6df8a49378eedec9fd2db3061bf9263bf9f39fd3d8377c3/cuda_bindings-12.9.4-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d80bffc357df9988dca279734bc9674c3934a654cab10cadeed27ce17d8635ee", size = 11924658, upload-time = "2025-10-21T14:52:10.411Z" }, -] - -[[package]] -name = "cuda-pathfinder" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/02/4dbe7568a42e46582248942f54dc64ad094769532adbe21e525e4edf7bc4/cuda_pathfinder-1.3.3-py3-none-any.whl", hash = "sha256:9984b664e404f7c134954a771be8775dfd6180ea1e1aef4a5a37d4be05d9bbb1", size = 27154, upload-time = "2025-12-04T22:35:08.996Z" }, -] - -[[package]] -name = "cyclopts" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "docstring-parser" }, - { name = "rich" }, - { name = "rich-rst" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d4/93/6085aa89c3fff78a5180987354538d72e43b0db27e66a959302d0c07821a/cyclopts-4.5.1.tar.gz", hash = "sha256:fadc45304763fd9f5d6033727f176898d17a1778e194436964661a005078a3dd", size = 162075, upload-time = "2026-01-25T15:23:54.07Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/7c/996760c30f1302704af57c66ff2d723f7d656d0d0b93563b5528a51484bb/cyclopts-4.5.1-py3-none-any.whl", hash = "sha256:0642c93601e554ca6b7b9abd81093847ea4448b2616280f2a0952416574e8c7a", size = 199807, upload-time = "2026-01-25T15:23:55.219Z" }, -] - -[[package]] -name = "debugpy" -version = "1.8.20" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/b7/cd8080344452e4874aae67c40d8940e2b4d47b01601a8fd9f44786c757c7/debugpy-1.8.20.tar.gz", hash = "sha256:55bc8701714969f1ab89a6d5f2f3d40c36f91b2cbe2f65d98bf8196f6a6a2c33", size = 1645207, upload-time = "2026-01-29T23:03:28.199Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/57/7f34f4736bfb6e00f2e4c96351b07805d83c9a7b33d28580ae01374430f7/debugpy-1.8.20-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:4ae3135e2089905a916909ef31922b2d733d756f66d87345b3e5e52b7a55f13d", size = 2550686, upload-time = "2026-01-29T23:03:42.023Z" }, - { url = "https://files.pythonhosted.org/packages/ab/78/b193a3975ca34458f6f0e24aaf5c3e3da72f5401f6054c0dfd004b41726f/debugpy-1.8.20-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:88f47850a4284b88bd2bfee1f26132147d5d504e4e86c22485dfa44b97e19b4b", size = 4310588, upload-time = "2026-01-29T23:03:43.314Z" }, - { url = "https://files.pythonhosted.org/packages/c1/55/f14deb95eaf4f30f07ef4b90a8590fc05d9e04df85ee379712f6fb6736d7/debugpy-1.8.20-cp312-cp312-win32.whl", hash = "sha256:4057ac68f892064e5f98209ab582abfee3b543fb55d2e87610ddc133a954d390", size = 5331372, upload-time = "2026-01-29T23:03:45.526Z" }, - { url = "https://files.pythonhosted.org/packages/a1/39/2bef246368bd42f9bd7cba99844542b74b84dacbdbea0833e610f384fee8/debugpy-1.8.20-cp312-cp312-win_amd64.whl", hash = "sha256:a1a8f851e7cf171330679ef6997e9c579ef6dd33c9098458bd9986a0f4ca52e3", size = 5372835, upload-time = "2026-01-29T23:03:47.245Z" }, - { url = "https://files.pythonhosted.org/packages/15/e2/fc500524cc6f104a9d049abc85a0a8b3f0d14c0a39b9c140511c61e5b40b/debugpy-1.8.20-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:5dff4bb27027821fdfcc9e8f87309a28988231165147c31730128b1c983e282a", size = 2539560, upload-time = "2026-01-29T23:03:48.738Z" }, - { url = "https://files.pythonhosted.org/packages/90/83/fb33dcea789ed6018f8da20c5a9bc9d82adc65c0c990faed43f7c955da46/debugpy-1.8.20-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:84562982dd7cf5ebebfdea667ca20a064e096099997b175fe204e86817f64eaf", size = 4293272, upload-time = "2026-01-29T23:03:50.169Z" }, - { url = "https://files.pythonhosted.org/packages/a6/25/b1e4a01bfb824d79a6af24b99ef291e24189080c93576dfd9b1a2815cd0f/debugpy-1.8.20-cp313-cp313-win32.whl", hash = "sha256:da11dea6447b2cadbf8ce2bec59ecea87cc18d2c574980f643f2d2dfe4862393", size = 5331208, upload-time = "2026-01-29T23:03:51.547Z" }, - { url = "https://files.pythonhosted.org/packages/13/f7/a0b368ce54ffff9e9028c098bd2d28cfc5b54f9f6c186929083d4c60ba58/debugpy-1.8.20-cp313-cp313-win_amd64.whl", hash = "sha256:eb506e45943cab2efb7c6eafdd65b842f3ae779f020c82221f55aca9de135ed7", size = 5372930, upload-time = "2026-01-29T23:03:53.585Z" }, - { url = "https://files.pythonhosted.org/packages/33/2e/f6cb9a8a13f5058f0a20fe09711a7b726232cd5a78c6a7c05b2ec726cff9/debugpy-1.8.20-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:9c74df62fc064cd5e5eaca1353a3ef5a5d50da5eb8058fcef63106f7bebe6173", size = 2538066, upload-time = "2026-01-29T23:03:54.999Z" }, - { url = "https://files.pythonhosted.org/packages/c5/56/6ddca50b53624e1ca3ce1d1e49ff22db46c47ea5fb4c0cc5c9b90a616364/debugpy-1.8.20-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:077a7447589ee9bc1ff0cdf443566d0ecf540ac8aa7333b775ebcb8ce9f4ecad", size = 4269425, upload-time = "2026-01-29T23:03:56.518Z" }, - { url = "https://files.pythonhosted.org/packages/c5/d9/d64199c14a0d4c476df46c82470a3ce45c8d183a6796cfb5e66533b3663c/debugpy-1.8.20-cp314-cp314-win32.whl", hash = "sha256:352036a99dd35053b37b7803f748efc456076f929c6a895556932eaf2d23b07f", size = 5331407, upload-time = "2026-01-29T23:03:58.481Z" }, - { url = "https://files.pythonhosted.org/packages/e0/d9/1f07395b54413432624d61524dfd98c1a7c7827d2abfdb8829ac92638205/debugpy-1.8.20-cp314-cp314-win_amd64.whl", hash = "sha256:a98eec61135465b062846112e5ecf2eebb855305acc1dfbae43b72903b8ab5be", size = 5372521, upload-time = "2026-01-29T23:03:59.864Z" }, - { url = "https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl", hash = "sha256:5be9bed9ae3be00665a06acaa48f8329d2b9632f15fd09f6a9a8c8d9907e54d7", size = 5337658, upload-time = "2026-01-29T23:04:17.404Z" }, -] - -[[package]] -name = "decorator" -version = "5.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, -] - -[[package]] -name = "deprecation" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/d3/8ae2869247df154b64c1884d7346d412fed0c49df84db635aab2d1c40e62/deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", size = 173788, upload-time = "2020-04-20T14:23:38.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", size = 11178, upload-time = "2020-04-20T14:23:36.581Z" }, -] - -[[package]] -name = "diskcache" -version = "5.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, -] - -[[package]] -name = "distro" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, -] - -[[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.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, -] - -[[package]] -name = "durationpy" -version = "0.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/a4/e44218c2b394e31a6dd0d6b095c4e1f32d0be54c2a4b250032d717647bab/durationpy-0.10.tar.gz", hash = "sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba", size = 3335, upload-time = "2025-05-17T13:52:37.26Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/0d/9feae160378a3553fa9a339b0e9c1a048e147a4127210e286ef18b730f03/durationpy-0.10-py3-none-any.whl", hash = "sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286", size = 3922, upload-time = "2025-05-17T13:52:36.463Z" }, -] - -[[package]] -name = "ecdsa" -version = "0.19.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/1f/924e3caae75f471eae4b26bd13b698f6af2c44279f67af317439c2f4c46a/ecdsa-0.19.1.tar.gz", hash = "sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61", size = 201793, upload-time = "2025-03-13T11:52:43.25Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/a3/460c57f094a4a165c84a1341c373b0a4f5ec6ac244b998d5021aade89b77/ecdsa-0.19.1-py2.py3-none-any.whl", hash = "sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3", size = 150607, upload-time = "2025-03-13T11:52:41.757Z" }, -] - -[[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.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "executing" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, -] - -[[package]] -name = "fakeredis" -version = "2.33.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "redis" }, - { name = "sortedcontainers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/f9/57464119936414d60697fcbd32f38909bb5688b616ae13de6e98384433e0/fakeredis-2.33.0.tar.gz", hash = "sha256:d7bc9a69d21df108a6451bbffee23b3eba432c21a654afc7ff2d295428ec5770", size = 175187, upload-time = "2025-12-16T19:45:52.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/78/a850fed8aeef96d4a99043c90b818b2ed5419cd5b24a4049fd7cfb9f1471/fakeredis-2.33.0-py3-none-any.whl", hash = "sha256:de535f3f9ccde1c56672ab2fdd6a8efbc4f2619fc2f1acc87b8737177d71c965", size = 119605, upload-time = "2025-12-16T19:45:51.08Z" }, -] - -[package.optional-dependencies] -lua = [ - { name = "lupa" }, -] - -[[package]] -name = "fastapi" -version = "0.128.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-doc" }, - { name = "pydantic" }, - { name = "starlette" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/52/08/8c8508db6c7b9aae8f7175046af41baad690771c9bcde676419965e338c7/fastapi-0.128.0.tar.gz", hash = "sha256:1cc179e1cef10a6be60ffe429f79b829dce99d8de32d7acb7e6c8dfdf7f2645a", size = 365682, upload-time = "2025-12-27T15:21:13.714Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/05/5cbb59154b093548acd0f4c7c474a118eda06da25aa75c616b72d8fcd92a/fastapi-0.128.0-py3-none-any.whl", hash = "sha256:aebd93f9716ee3b4f4fcfe13ffb7cf308d99c9f3ab5622d8877441072561582d", size = 103094, upload-time = "2025-12-27T15:21:12.154Z" }, -] - -[[package]] -name = "fastembed" -version = "0.7.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, - { name = "loguru" }, - { name = "mmh3" }, - { name = "numpy" }, - { name = "onnxruntime" }, - { name = "pillow" }, - { name = "py-rust-stemmers" }, - { name = "requests" }, - { name = "tokenizers" }, - { name = "tqdm" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4c/c2/9c708680de1b54480161e0505f9d6d3d8eb47a1dc1a1f7f3c5106ba355d2/fastembed-0.7.4.tar.gz", hash = "sha256:8b8a4ea860ca295002f4754e8f5820a636e1065a9444959e18d5988d7f27093b", size = 68807, upload-time = "2025-12-05T12:08:10.447Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/3b/8da01492bc8b69184257d0c951bf0e77aec8ce110f06d8ce16c6ed9084f7/fastembed-0.7.4-py3-none-any.whl", hash = "sha256:79250a775f70bd6addb0e054204df042b5029ecae501e40e5bbd08e75844ad83", size = 108491, upload-time = "2025-12-05T12:08:09.059Z" }, -] - -[[package]] -name = "fastmcp" -version = "2.14.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "authlib" }, - { name = "cyclopts" }, - { name = "exceptiongroup" }, - { name = "httpx" }, - { name = "jsonref" }, - { name = "jsonschema-path" }, - { name = "mcp" }, - { name = "openapi-pydantic" }, - { name = "packaging" }, - { name = "platformdirs" }, - { name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] }, - { name = "pydantic", extra = ["email"] }, - { name = "pydocket" }, - { name = "pyperclip" }, - { name = "python-dotenv" }, - { name = "rich" }, - { name = "uvicorn" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/a9/a57d5e5629ebd4ef82b495a7f8e346ce29ef80cc86b15c8c40570701b94d/fastmcp-2.14.4.tar.gz", hash = "sha256:c01f19845c2adda0a70d59525c9193be64a6383014c8d40ce63345ac664053ff", size = 8302239, upload-time = "2026-01-22T17:29:37.024Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/41/c4d407e2218fd60d84acb6cc5131d28ff876afecf325e3fd9d27b8318581/fastmcp-2.14.4-py3-none-any.whl", hash = "sha256:5858cff5e4c8ea8107f9bca2609d71d6256e0fce74495912f6e51625e466c49a", size = 417788, upload-time = "2026-01-22T17:29:35.159Z" }, -] - -[[package]] -name = "fastuuid" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232, upload-time = "2025-10-19T22:19:22.402Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/a2/e78fcc5df65467f0d207661b7ef86c5b7ac62eea337c0c0fcedbeee6fb13/fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77e94728324b63660ebf8adb27055e92d2e4611645bf12ed9d88d30486471d0a", size = 510164, upload-time = "2025-10-19T22:31:45.635Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b3/c846f933f22f581f558ee63f81f29fa924acd971ce903dab1a9b6701816e/fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:caa1f14d2102cb8d353096bc6ef6c13b2c81f347e6ab9d6fbd48b9dea41c153d", size = 261837, upload-time = "2025-10-19T22:38:38.53Z" }, - { url = "https://files.pythonhosted.org/packages/54/ea/682551030f8c4fa9a769d9825570ad28c0c71e30cf34020b85c1f7ee7382/fastuuid-0.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d23ef06f9e67163be38cece704170486715b177f6baae338110983f99a72c070", size = 251370, upload-time = "2025-10-19T22:40:26.07Z" }, - { url = "https://files.pythonhosted.org/packages/14/dd/5927f0a523d8e6a76b70968e6004966ee7df30322f5fc9b6cdfb0276646a/fastuuid-0.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c9ec605ace243b6dbe3bd27ebdd5d33b00d8d1d3f580b39fdd15cd96fd71796", size = 277766, upload-time = "2025-10-19T22:37:23.779Z" }, - { url = "https://files.pythonhosted.org/packages/16/6e/c0fb547eef61293153348f12e0f75a06abb322664b34a1573a7760501336/fastuuid-0.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:808527f2407f58a76c916d6aa15d58692a4a019fdf8d4c32ac7ff303b7d7af09", size = 278105, upload-time = "2025-10-19T22:26:56.821Z" }, - { url = "https://files.pythonhosted.org/packages/2d/b1/b9c75e03b768f61cf2e84ee193dc18601aeaf89a4684b20f2f0e9f52b62c/fastuuid-0.14.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fb3c0d7fef6674bbeacdd6dbd386924a7b60b26de849266d1ff6602937675c8", size = 301564, upload-time = "2025-10-19T22:30:31.604Z" }, - { url = "https://files.pythonhosted.org/packages/fc/fa/f7395fdac07c7a54f18f801744573707321ca0cee082e638e36452355a9d/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab3f5d36e4393e628a4df337c2c039069344db5f4b9d2a3c9cea48284f1dd741", size = 459659, upload-time = "2025-10-19T22:31:32.341Z" }, - { url = "https://files.pythonhosted.org/packages/66/49/c9fd06a4a0b1f0f048aacb6599e7d96e5d6bc6fa680ed0d46bf111929d1b/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b9a0ca4f03b7e0b01425281ffd44e99d360e15c895f1907ca105854ed85e2057", size = 478430, upload-time = "2025-10-19T22:26:22.962Z" }, - { url = "https://files.pythonhosted.org/packages/be/9c/909e8c95b494e8e140e8be6165d5fc3f61fdc46198c1554df7b3e1764471/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3acdf655684cc09e60fb7e4cf524e8f42ea760031945aa8086c7eae2eeeabeb8", size = 450894, upload-time = "2025-10-19T22:27:01.647Z" }, - { url = "https://files.pythonhosted.org/packages/90/eb/d29d17521976e673c55ef7f210d4cdd72091a9ec6755d0fd4710d9b3c871/fastuuid-0.14.0-cp312-cp312-win32.whl", hash = "sha256:9579618be6280700ae36ac42c3efd157049fe4dd40ca49b021280481c78c3176", size = 154374, upload-time = "2025-10-19T22:29:19.879Z" }, - { url = "https://files.pythonhosted.org/packages/cc/fc/f5c799a6ea6d877faec0472d0b27c079b47c86b1cdc577720a5386483b36/fastuuid-0.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:d9e4332dc4ba054434a9594cbfaf7823b57993d7d8e7267831c3e059857cf397", size = 156550, upload-time = "2025-10-19T22:27:49.658Z" }, - { url = "https://files.pythonhosted.org/packages/a5/83/ae12dd39b9a39b55d7f90abb8971f1a5f3c321fd72d5aa83f90dc67fe9ed/fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77a09cb7427e7af74c594e409f7731a0cf887221de2f698e1ca0ebf0f3139021", size = 510720, upload-time = "2025-10-19T22:42:34.633Z" }, - { url = "https://files.pythonhosted.org/packages/53/b0/a4b03ff5d00f563cc7546b933c28cb3f2a07344b2aec5834e874f7d44143/fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:9bd57289daf7b153bfa3e8013446aa144ce5e8c825e9e366d455155ede5ea2dc", size = 262024, upload-time = "2025-10-19T22:30:25.482Z" }, - { url = "https://files.pythonhosted.org/packages/9c/6d/64aee0a0f6a58eeabadd582e55d0d7d70258ffdd01d093b30c53d668303b/fastuuid-0.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ac60fc860cdf3c3f327374db87ab8e064c86566ca8c49d2e30df15eda1b0c2d5", size = 251679, upload-time = "2025-10-19T22:36:14.096Z" }, - { url = "https://files.pythonhosted.org/packages/60/f5/a7e9cda8369e4f7919d36552db9b2ae21db7915083bc6336f1b0082c8b2e/fastuuid-0.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab32f74bd56565b186f036e33129da77db8be09178cd2f5206a5d4035fb2a23f", size = 277862, upload-time = "2025-10-19T22:36:23.302Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d3/8ce11827c783affffd5bd4d6378b28eb6cc6d2ddf41474006b8d62e7448e/fastuuid-0.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e678459cf4addaedd9936bbb038e35b3f6b2061330fd8f2f6a1d80414c0f87", size = 278278, upload-time = "2025-10-19T22:29:43.809Z" }, - { url = "https://files.pythonhosted.org/packages/a2/51/680fb6352d0bbade04036da46264a8001f74b7484e2fd1f4da9e3db1c666/fastuuid-0.14.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1e3cc56742f76cd25ecb98e4b82a25f978ccffba02e4bdce8aba857b6d85d87b", size = 301788, upload-time = "2025-10-19T22:36:06.825Z" }, - { url = "https://files.pythonhosted.org/packages/fa/7c/2014b5785bd8ebdab04ec857635ebd84d5ee4950186a577db9eff0fb8ff6/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cb9a030f609194b679e1660f7e32733b7a0f332d519c5d5a6a0a580991290022", size = 459819, upload-time = "2025-10-19T22:35:31.623Z" }, - { url = "https://files.pythonhosted.org/packages/01/d2/524d4ceeba9160e7a9bc2ea3e8f4ccf1ad78f3bde34090ca0c51f09a5e91/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:09098762aad4f8da3a888eb9ae01c84430c907a297b97166b8abc07b640f2995", size = 478546, upload-time = "2025-10-19T22:26:03.023Z" }, - { url = "https://files.pythonhosted.org/packages/bc/17/354d04951ce114bf4afc78e27a18cfbd6ee319ab1829c2d5fb5e94063ac6/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1383fff584fa249b16329a059c68ad45d030d5a4b70fb7c73a08d98fd53bcdab", size = 450921, upload-time = "2025-10-19T22:31:02.151Z" }, - { url = "https://files.pythonhosted.org/packages/fb/be/d7be8670151d16d88f15bb121c5b66cdb5ea6a0c2a362d0dcf30276ade53/fastuuid-0.14.0-cp313-cp313-win32.whl", hash = "sha256:a0809f8cc5731c066c909047f9a314d5f536c871a7a22e815cc4967c110ac9ad", size = 154559, upload-time = "2025-10-19T22:36:36.011Z" }, - { url = "https://files.pythonhosted.org/packages/22/1d/5573ef3624ceb7abf4a46073d3554e37191c868abc3aecd5289a72f9810a/fastuuid-0.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:0df14e92e7ad3276327631c9e7cec09e32572ce82089c55cb1bb8df71cf394ed", size = 156539, upload-time = "2025-10-19T22:33:35.898Z" }, - { url = "https://files.pythonhosted.org/packages/16/c9/8c7660d1fe3862e3f8acabd9be7fc9ad71eb270f1c65cce9a2b7a31329ab/fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b852a870a61cfc26c884af205d502881a2e59cc07076b60ab4a951cc0c94d1ad", size = 510600, upload-time = "2025-10-19T22:43:44.17Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f4/a989c82f9a90d0ad995aa957b3e572ebef163c5299823b4027986f133dfb/fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c7502d6f54cd08024c3ea9b3514e2d6f190feb2f46e6dbcd3747882264bb5f7b", size = 262069, upload-time = "2025-10-19T22:43:38.38Z" }, - { url = "https://files.pythonhosted.org/packages/da/6c/a1a24f73574ac995482b1326cf7ab41301af0fabaa3e37eeb6b3df00e6e2/fastuuid-0.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ca61b592120cf314cfd66e662a5b54a578c5a15b26305e1b8b618a6f22df714", size = 251543, upload-time = "2025-10-19T22:32:22.537Z" }, - { url = "https://files.pythonhosted.org/packages/1a/20/2a9b59185ba7a6c7b37808431477c2d739fcbdabbf63e00243e37bd6bf49/fastuuid-0.14.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa75b6657ec129d0abded3bec745e6f7ab642e6dba3a5272a68247e85f5f316f", size = 277798, upload-time = "2025-10-19T22:33:53.821Z" }, - { url = "https://files.pythonhosted.org/packages/ef/33/4105ca574f6ded0af6a797d39add041bcfb468a1255fbbe82fcb6f592da2/fastuuid-0.14.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8a0dfea3972200f72d4c7df02c8ac70bad1bb4c58d7e0ec1e6f341679073a7f", size = 278283, upload-time = "2025-10-19T22:29:02.812Z" }, - { url = "https://files.pythonhosted.org/packages/fe/8c/fca59f8e21c4deb013f574eae05723737ddb1d2937ce87cb2a5d20992dc3/fastuuid-0.14.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1bf539a7a95f35b419f9ad105d5a8a35036df35fdafae48fb2fd2e5f318f0d75", size = 301627, upload-time = "2025-10-19T22:35:54.985Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e2/f78c271b909c034d429218f2798ca4e89eeda7983f4257d7865976ddbb6c/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:9a133bf9cc78fdbd1179cb58a59ad0100aa32d8675508150f3658814aeefeaa4", size = 459778, upload-time = "2025-10-19T22:28:00.999Z" }, - { url = "https://files.pythonhosted.org/packages/1e/f0/5ff209d865897667a2ff3e7a572267a9ced8f7313919f6d6043aed8b1caa/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_i686.whl", hash = "sha256:f54d5b36c56a2d5e1a31e73b950b28a0d83eb0c37b91d10408875a5a29494bad", size = 478605, upload-time = "2025-10-19T22:36:21.764Z" }, - { url = "https://files.pythonhosted.org/packages/e0/c8/2ce1c78f983a2c4987ea865d9516dbdfb141a120fd3abb977ae6f02ba7ca/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:ec27778c6ca3393ef662e2762dba8af13f4ec1aaa32d08d77f71f2a70ae9feb8", size = 450837, upload-time = "2025-10-19T22:34:37.178Z" }, - { url = "https://files.pythonhosted.org/packages/df/60/dad662ec9a33b4a5fe44f60699258da64172c39bd041da2994422cdc40fe/fastuuid-0.14.0-cp314-cp314-win32.whl", hash = "sha256:e23fc6a83f112de4be0cc1990e5b127c27663ae43f866353166f87df58e73d06", size = 154532, upload-time = "2025-10-19T22:35:18.217Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/da4db31001e854025ffd26bc9ba0740a9cbba2c3259695f7c5834908b336/fastuuid-0.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:df61342889d0f5e7a32f7284e55ef95103f2110fee433c2ae7c2c0956d76ac8a", size = 156457, upload-time = "2025-10-19T22:33:44.579Z" }, -] - -[[package]] -name = "ffind" -version = "1.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/03/97fca9e84aa4f4e484884a8ca21fd4e2d07a7906a2228044f80fa28c1a21/ffind-1.6.1.tar.gz", hash = "sha256:1715b6b718eb53ec0b7e9877399b894a48ace1faa2f6a3d772376b7b0a181feb", size = 10234, upload-time = "2025-03-22T17:23:02.76Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/59/ae/5b13349f7f5f9977f2f3cbc26ba09098db2b1242b715a67df4fa2a75e372/ffind-1.6.1-py3-none-any.whl", hash = "sha256:6d79c604087f53fe0e1e3dc4d75a4cf9d4a424a7143289b5f2b9ea061b9a266a", size = 8689, upload-time = "2025-03-22T17:23:01.885Z" }, -] - -[[package]] -name = "filelock" -version = "3.20.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, -] - -[[package]] -name = "flatbuffers" -version = "25.12.19" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" }, -] - -[[package]] -name = "frozenlist" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, - { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, - { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, - { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, - { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, - { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, - { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, - { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, - { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, - { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, - { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, - { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, - { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, - { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, - { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, - { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, - { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, - { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, - { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, - { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, - { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, - { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, - { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, - { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, - { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, - { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, - { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, - { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, - { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, - { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, - { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, - { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, - { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, - { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, - { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, - { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, - { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, - { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, - { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, - { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, - { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, - { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, - { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, - { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, - { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, - { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, - { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, - { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, - { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, - { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, - { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, - { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, - { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, - { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, - { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, - { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, - { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, - { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, - { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, - { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, - { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, - { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, - { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, -] - -[[package]] -name = "fsspec" -version = "2026.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d5/7d/5df2650c57d47c57232af5ef4b4fdbff182070421e405e0d62c6cdbfaa87/fsspec-2026.1.0.tar.gz", hash = "sha256:e987cb0496a0d81bba3a9d1cee62922fb395e7d4c3b575e57f547953334fe07b", size = 310496, upload-time = "2026-01-09T15:21:35.562Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/c9/97cc5aae1648dcb851958a3ddf73ccd7dbe5650d95203ecb4d7720b4cdbf/fsspec-2026.1.0-py3-none-any.whl", hash = "sha256:cb76aa913c2285a3b49bdd5fc55b1d7c708d7208126b60f2eb8194fe1b4cbdcc", size = 201838, upload-time = "2026-01-09T15:21:34.041Z" }, -] - -[[package]] -name = "googleapis-common-protos" -version = "1.72.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e5/7b/adfd75544c415c487b33061fe7ae526165241c1ea133f9a9125a56b39fd8/googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5", size = 147433, upload-time = "2025-11-06T18:29:24.087Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515, upload-time = "2025-11-06T18:29:13.14Z" }, -] - -[[package]] -name = "greenlet" -version = "3.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a3/51/1664f6b78fc6ebbd98019a1fd730e83fa78f2db7058f72b1463d3612b8db/greenlet-3.3.2.tar.gz", hash = "sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2", size = 188267, upload-time = "2026-02-20T20:54:15.531Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/ab/1608e5a7578e62113506740b88066bf09888322a311cff602105e619bd87/greenlet-3.3.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd", size = 280358, upload-time = "2026-02-20T20:17:43.971Z" }, - { url = "https://files.pythonhosted.org/packages/a5/23/0eae412a4ade4e6623ff7626e38998cb9b11e9ff1ebacaa021e4e108ec15/greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd", size = 601217, upload-time = "2026-02-20T20:47:31.462Z" }, - { url = "https://files.pythonhosted.org/packages/f8/16/5b1678a9c07098ecb9ab2dd159fafaf12e963293e61ee8d10ecb55273e5e/greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac", size = 611792, upload-time = "2026-02-20T20:55:58.423Z" }, - { url = "https://files.pythonhosted.org/packages/5c/c5/cc09412a29e43406eba18d61c70baa936e299bc27e074e2be3806ed29098/greenlet-3.3.2-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb", size = 626250, upload-time = "2026-02-20T21:02:46.596Z" }, - { url = "https://files.pythonhosted.org/packages/50/1f/5155f55bd71cabd03765a4aac9ac446be129895271f73872c36ebd4b04b6/greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070", size = 613875, upload-time = "2026-02-20T20:21:01.102Z" }, - { url = "https://files.pythonhosted.org/packages/fc/dd/845f249c3fcd69e32df80cdab059b4be8b766ef5830a3d0aa9d6cad55beb/greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79", size = 1571467, upload-time = "2026-02-20T20:49:33.495Z" }, - { url = "https://files.pythonhosted.org/packages/2a/50/2649fe21fcc2b56659a452868e695634722a6655ba245d9f77f5656010bf/greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395", size = 1640001, upload-time = "2026-02-20T20:21:09.154Z" }, - { url = "https://files.pythonhosted.org/packages/9b/40/cc802e067d02af8b60b6771cea7d57e21ef5e6659912814babb42b864713/greenlet-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:34308836d8370bddadb41f5a7ce96879b72e2fdfb4e87729330c6ab52376409f", size = 231081, upload-time = "2026-02-20T20:17:28.121Z" }, - { url = "https://files.pythonhosted.org/packages/58/2e/fe7f36ff1982d6b10a60d5e0740c759259a7d6d2e1dc41da6d96de32fff6/greenlet-3.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:d3a62fa76a32b462a97198e4c9e99afb9ab375115e74e9a83ce180e7a496f643", size = 230331, upload-time = "2026-02-20T20:17:23.34Z" }, - { url = "https://files.pythonhosted.org/packages/ac/48/f8b875fa7dea7dd9b33245e37f065af59df6a25af2f9561efa8d822fde51/greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4", size = 279120, upload-time = "2026-02-20T20:19:01.9Z" }, - { url = "https://files.pythonhosted.org/packages/49/8d/9771d03e7a8b1ee456511961e1b97a6d77ae1dea4a34a5b98eee706689d3/greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986", size = 603238, upload-time = "2026-02-20T20:47:32.873Z" }, - { url = "https://files.pythonhosted.org/packages/59/0e/4223c2bbb63cd5c97f28ffb2a8aee71bdfb30b323c35d409450f51b91e3e/greenlet-3.3.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92", size = 614219, upload-time = "2026-02-20T20:55:59.817Z" }, - { url = "https://files.pythonhosted.org/packages/94/2b/4d012a69759ac9d77210b8bfb128bc621125f5b20fc398bce3940d036b1c/greenlet-3.3.2-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd", size = 628268, upload-time = "2026-02-20T21:02:48.024Z" }, - { url = "https://files.pythonhosted.org/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab", size = 616774, upload-time = "2026-02-20T20:21:02.454Z" }, - { url = "https://files.pythonhosted.org/packages/0a/03/996c2d1689d486a6e199cb0f1cf9e4aa940c500e01bdf201299d7d61fa69/greenlet-3.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a", size = 1571277, upload-time = "2026-02-20T20:49:34.795Z" }, - { url = "https://files.pythonhosted.org/packages/d9/c4/2570fc07f34a39f2caf0bf9f24b0a1a0a47bc2e8e465b2c2424821389dfc/greenlet-3.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b", size = 1640455, upload-time = "2026-02-20T20:21:10.261Z" }, - { url = "https://files.pythonhosted.org/packages/91/39/5ef5aa23bc545aa0d31e1b9b55822b32c8da93ba657295840b6b34124009/greenlet-3.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:a7945dd0eab63ded0a48e4dcade82939783c172290a7903ebde9e184333ca124", size = 230961, upload-time = "2026-02-20T20:16:58.461Z" }, - { url = "https://files.pythonhosted.org/packages/62/6b/a89f8456dcb06becff288f563618e9f20deed8dd29beea14f9a168aef64b/greenlet-3.3.2-cp313-cp313-win_arm64.whl", hash = "sha256:394ead29063ee3515b4e775216cb756b2e3b4a7e55ae8fd884f17fa579e6b327", size = 230221, upload-time = "2026-02-20T20:17:37.152Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ae/8bffcbd373b57a5992cd077cbe8858fff39110480a9d50697091faea6f39/greenlet-3.3.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab", size = 279650, upload-time = "2026-02-20T20:18:00.783Z" }, - { url = "https://files.pythonhosted.org/packages/d1/c0/45f93f348fa49abf32ac8439938726c480bd96b2a3c6f4d949ec0124b69f/greenlet-3.3.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082", size = 650295, upload-time = "2026-02-20T20:47:34.036Z" }, - { url = "https://files.pythonhosted.org/packages/b3/de/dd7589b3f2b8372069ab3e4763ea5329940fc7ad9dcd3e272a37516d7c9b/greenlet-3.3.2-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9", size = 662163, upload-time = "2026-02-20T20:56:01.295Z" }, - { url = "https://files.pythonhosted.org/packages/cd/ac/85804f74f1ccea31ba518dcc8ee6f14c79f73fe36fa1beba38930806df09/greenlet-3.3.2-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9", size = 675371, upload-time = "2026-02-20T21:02:49.664Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d8/09bfa816572a4d83bccd6750df1926f79158b1c36c5f73786e26dbe4ee38/greenlet-3.3.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506", size = 664160, upload-time = "2026-02-20T20:21:04.015Z" }, - { url = "https://files.pythonhosted.org/packages/48/cf/56832f0c8255d27f6c35d41b5ec91168d74ec721d85f01a12131eec6b93c/greenlet-3.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce", size = 1619181, upload-time = "2026-02-20T20:49:36.052Z" }, - { url = "https://files.pythonhosted.org/packages/0a/23/b90b60a4aabb4cec0796e55f25ffbfb579a907c3898cd2905c8918acaa16/greenlet-3.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5", size = 1687713, upload-time = "2026-02-20T20:21:11.684Z" }, - { url = "https://files.pythonhosted.org/packages/f3/ca/2101ca3d9223a1dc125140dbc063644dca76df6ff356531eb27bc267b446/greenlet-3.3.2-cp314-cp314-win_amd64.whl", hash = "sha256:8c4dd0f3997cf2512f7601563cc90dfb8957c0cff1e3a1b23991d4ea1776c492", size = 232034, upload-time = "2026-02-20T20:20:08.186Z" }, - { url = "https://files.pythonhosted.org/packages/f6/4a/ecf894e962a59dea60f04877eea0fd5724618da89f1867b28ee8b91e811f/greenlet-3.3.2-cp314-cp314-win_arm64.whl", hash = "sha256:cd6f9e2bbd46321ba3bbb4c8a15794d32960e3b0ae2cc4d49a1a53d314805d71", size = 231437, upload-time = "2026-02-20T20:18:59.722Z" }, - { url = "https://files.pythonhosted.org/packages/98/6d/8f2ef704e614bcf58ed43cfb8d87afa1c285e98194ab2cfad351bf04f81e/greenlet-3.3.2-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54", size = 286617, upload-time = "2026-02-20T20:19:29.856Z" }, - { url = "https://files.pythonhosted.org/packages/5e/0d/93894161d307c6ea237a43988f27eba0947b360b99ac5239ad3fe09f0b47/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4", size = 655189, upload-time = "2026-02-20T20:47:35.742Z" }, - { url = "https://files.pythonhosted.org/packages/f5/2c/d2d506ebd8abcb57386ec4f7ba20f4030cbe56eae541bc6fd6ef399c0b41/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff", size = 658225, upload-time = "2026-02-20T20:56:02.527Z" }, - { url = "https://files.pythonhosted.org/packages/d1/67/8197b7e7e602150938049d8e7f30de1660cfb87e4c8ee349b42b67bdb2e1/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf", size = 666581, upload-time = "2026-02-20T21:02:51.526Z" }, - { url = "https://files.pythonhosted.org/packages/8e/30/3a09155fbf728673a1dea713572d2d31159f824a37c22da82127056c44e4/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4", size = 657907, upload-time = "2026-02-20T20:21:05.259Z" }, - { url = "https://files.pythonhosted.org/packages/f3/fd/d05a4b7acd0154ed758797f0a43b4c0962a843bedfe980115e842c5b2d08/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727", size = 1618857, upload-time = "2026-02-20T20:49:37.309Z" }, - { url = "https://files.pythonhosted.org/packages/6f/e1/50ee92a5db521de8f35075b5eff060dd43d39ebd46c2181a2042f7070385/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e", size = 1680010, upload-time = "2026-02-20T20:21:13.427Z" }, - { url = "https://files.pythonhosted.org/packages/29/4b/45d90626aef8e65336bed690106d1382f7a43665e2249017e9527df8823b/greenlet-3.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c04c5e06ec3e022cbfe2cd4a846e1d4e50087444f875ff6d2c2ad8445495cf1a", size = 237086, upload-time = "2026-02-20T20:20:45.786Z" }, -] - -[[package]] -name = "grep-ast" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pathspec" }, - { name = "tree-sitter-language-pack" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/67/82/a87079945a7c15d242cb586ae22e17952132439eaa9c878ec5fbdc61c54d/grep_ast-0.9.0.tar.gz", hash = "sha256:620a242a4493e6721338d1c9a6c234ae651f8774f4924a6dcf90f6865d4b2ee3", size = 14125, upload-time = "2025-05-08T01:08:28.371Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/79/29f1373b2ce1eec37c03aefbc17194c2470d8b61ede288e5043231825999/grep_ast-0.9.0-py3-none-any.whl", hash = "sha256:a3973dca99f1abc026a01bbbc70e00a63860c8ff94a56182ff18b089836826d7", size = 13918, upload-time = "2025-05-08T01:08:27.481Z" }, -] - -[[package]] -name = "grpcio" -version = "1.76.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b6/e0/318c1ce3ae5a17894d5791e87aea147587c9e702f24122cc7a5c8bbaeeb1/grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73", size = 12785182, upload-time = "2025-10-21T16:23:12.106Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/05/8e29121994b8d959ffa0afd28996d452f291b48cfc0875619de0bde2c50c/grpcio-1.76.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:81fd9652b37b36f16138611c7e884eb82e0cec137c40d3ef7c3f9b3ed00f6ed8", size = 5799718, upload-time = "2025-10-21T16:21:17.939Z" }, - { url = "https://files.pythonhosted.org/packages/d9/75/11d0e66b3cdf998c996489581bdad8900db79ebd83513e45c19548f1cba4/grpcio-1.76.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:04bbe1bfe3a68bbfd4e52402ab7d4eb59d72d02647ae2042204326cf4bbad280", size = 11825627, upload-time = "2025-10-21T16:21:20.466Z" }, - { url = "https://files.pythonhosted.org/packages/28/50/2f0aa0498bc188048f5d9504dcc5c2c24f2eb1a9337cd0fa09a61a2e75f0/grpcio-1.76.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d388087771c837cdb6515539f43b9d4bf0b0f23593a24054ac16f7a960be16f4", size = 6359167, upload-time = "2025-10-21T16:21:23.122Z" }, - { url = "https://files.pythonhosted.org/packages/66/e5/bbf0bb97d29ede1d59d6588af40018cfc345b17ce979b7b45424628dc8bb/grpcio-1.76.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f8f757bebaaea112c00dba718fc0d3260052ce714e25804a03f93f5d1c6cc11", size = 7044267, upload-time = "2025-10-21T16:21:25.995Z" }, - { url = "https://files.pythonhosted.org/packages/f5/86/f6ec2164f743d9609691115ae8ece098c76b894ebe4f7c94a655c6b03e98/grpcio-1.76.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:980a846182ce88c4f2f7e2c22c56aefd515daeb36149d1c897f83cf57999e0b6", size = 6573963, upload-time = "2025-10-21T16:21:28.631Z" }, - { url = "https://files.pythonhosted.org/packages/60/bc/8d9d0d8505feccfdf38a766d262c71e73639c165b311c9457208b56d92ae/grpcio-1.76.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f92f88e6c033db65a5ae3d97905c8fea9c725b63e28d5a75cb73b49bda5024d8", size = 7164484, upload-time = "2025-10-21T16:21:30.837Z" }, - { url = "https://files.pythonhosted.org/packages/67/e6/5d6c2fc10b95edf6df9b8f19cf10a34263b7fd48493936fffd5085521292/grpcio-1.76.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4baf3cbe2f0be3289eb68ac8ae771156971848bb8aaff60bad42005539431980", size = 8127777, upload-time = "2025-10-21T16:21:33.577Z" }, - { url = "https://files.pythonhosted.org/packages/3f/c8/dce8ff21c86abe025efe304d9e31fdb0deaaa3b502b6a78141080f206da0/grpcio-1.76.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:615ba64c208aaceb5ec83bfdce7728b80bfeb8be97562944836a7a0a9647d882", size = 7594014, upload-time = "2025-10-21T16:21:41.882Z" }, - { url = "https://files.pythonhosted.org/packages/e0/42/ad28191ebf983a5d0ecef90bab66baa5a6b18f2bfdef9d0a63b1973d9f75/grpcio-1.76.0-cp312-cp312-win32.whl", hash = "sha256:45d59a649a82df5718fd9527ce775fd66d1af35e6d31abdcdc906a49c6822958", size = 3984750, upload-time = "2025-10-21T16:21:44.006Z" }, - { url = "https://files.pythonhosted.org/packages/9e/00/7bd478cbb851c04a48baccaa49b75abaa8e4122f7d86da797500cccdd771/grpcio-1.76.0-cp312-cp312-win_amd64.whl", hash = "sha256:c088e7a90b6017307f423efbb9d1ba97a22aa2170876223f9709e9d1de0b5347", size = 4704003, upload-time = "2025-10-21T16:21:46.244Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ed/71467ab770effc9e8cef5f2e7388beb2be26ed642d567697bb103a790c72/grpcio-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:26ef06c73eb53267c2b319f43e6634c7556ea37672029241a056629af27c10e2", size = 5807716, upload-time = "2025-10-21T16:21:48.475Z" }, - { url = "https://files.pythonhosted.org/packages/2c/85/c6ed56f9817fab03fa8a111ca91469941fb514e3e3ce6d793cb8f1e1347b/grpcio-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:45e0111e73f43f735d70786557dc38141185072d7ff8dc1829d6a77ac1471468", size = 11821522, upload-time = "2025-10-21T16:21:51.142Z" }, - { url = "https://files.pythonhosted.org/packages/ac/31/2b8a235ab40c39cbc141ef647f8a6eb7b0028f023015a4842933bc0d6831/grpcio-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83d57312a58dcfe2a3a0f9d1389b299438909a02db60e2f2ea2ae2d8034909d3", size = 6362558, upload-time = "2025-10-21T16:21:54.213Z" }, - { url = "https://files.pythonhosted.org/packages/bd/64/9784eab483358e08847498ee56faf8ff6ea8e0a4592568d9f68edc97e9e9/grpcio-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3e2a27c89eb9ac3d81ec8835e12414d73536c6e620355d65102503064a4ed6eb", size = 7049990, upload-time = "2025-10-21T16:21:56.476Z" }, - { url = "https://files.pythonhosted.org/packages/2b/94/8c12319a6369434e7a184b987e8e9f3b49a114c489b8315f029e24de4837/grpcio-1.76.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61f69297cba3950a524f61c7c8ee12e55c486cb5f7db47ff9dcee33da6f0d3ae", size = 6575387, upload-time = "2025-10-21T16:21:59.051Z" }, - { url = "https://files.pythonhosted.org/packages/15/0f/f12c32b03f731f4a6242f771f63039df182c8b8e2cf8075b245b409259d4/grpcio-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a15c17af8839b6801d554263c546c69c4d7718ad4321e3166175b37eaacca77", size = 7166668, upload-time = "2025-10-21T16:22:02.049Z" }, - { url = "https://files.pythonhosted.org/packages/ff/2d/3ec9ce0c2b1d92dd59d1c3264aaec9f0f7c817d6e8ac683b97198a36ed5a/grpcio-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25a18e9810fbc7e7f03ec2516addc116a957f8cbb8cbc95ccc80faa072743d03", size = 8124928, upload-time = "2025-10-21T16:22:04.984Z" }, - { url = "https://files.pythonhosted.org/packages/1a/74/fd3317be5672f4856bcdd1a9e7b5e17554692d3db9a3b273879dc02d657d/grpcio-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:931091142fd8cc14edccc0845a79248bc155425eee9a98b2db2ea4f00a235a42", size = 7589983, upload-time = "2025-10-21T16:22:07.881Z" }, - { url = "https://files.pythonhosted.org/packages/45/bb/ca038cf420f405971f19821c8c15bcbc875505f6ffadafe9ffd77871dc4c/grpcio-1.76.0-cp313-cp313-win32.whl", hash = "sha256:5e8571632780e08526f118f74170ad8d50fb0a48c23a746bef2a6ebade3abd6f", size = 3984727, upload-time = "2025-10-21T16:22:10.032Z" }, - { url = "https://files.pythonhosted.org/packages/41/80/84087dc56437ced7cdd4b13d7875e7439a52a261e3ab4e06488ba6173b0a/grpcio-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:f9f7bd5faab55f47231ad8dba7787866b69f5e93bc306e3915606779bbfb4ba8", size = 4702799, upload-time = "2025-10-21T16:22:12.709Z" }, - { url = "https://files.pythonhosted.org/packages/b4/46/39adac80de49d678e6e073b70204091e76631e03e94928b9ea4ecf0f6e0e/grpcio-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:ff8a59ea85a1f2191a0ffcc61298c571bc566332f82e5f5be1b83c9d8e668a62", size = 5808417, upload-time = "2025-10-21T16:22:15.02Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f5/a4531f7fb8b4e2a60b94e39d5d924469b7a6988176b3422487be61fe2998/grpcio-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06c3d6b076e7b593905d04fdba6a0525711b3466f43b3400266f04ff735de0cd", size = 11828219, upload-time = "2025-10-21T16:22:17.954Z" }, - { url = "https://files.pythonhosted.org/packages/4b/1c/de55d868ed7a8bd6acc6b1d6ddc4aa36d07a9f31d33c912c804adb1b971b/grpcio-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd5ef5932f6475c436c4a55e4336ebbe47bd3272be04964a03d316bbf4afbcbc", size = 6367826, upload-time = "2025-10-21T16:22:20.721Z" }, - { url = "https://files.pythonhosted.org/packages/59/64/99e44c02b5adb0ad13ab3adc89cb33cb54bfa90c74770f2607eea629b86f/grpcio-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b331680e46239e090f5b3cead313cc772f6caa7d0fc8de349337563125361a4a", size = 7049550, upload-time = "2025-10-21T16:22:23.637Z" }, - { url = "https://files.pythonhosted.org/packages/43/28/40a5be3f9a86949b83e7d6a2ad6011d993cbe9b6bd27bea881f61c7788b6/grpcio-1.76.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2229ae655ec4e8999599469559e97630185fdd53ae1e8997d147b7c9b2b72cba", size = 6575564, upload-time = "2025-10-21T16:22:26.016Z" }, - { url = "https://files.pythonhosted.org/packages/4b/a9/1be18e6055b64467440208a8559afac243c66a8b904213af6f392dc2212f/grpcio-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:490fa6d203992c47c7b9e4a9d39003a0c2bcc1c9aa3c058730884bbbb0ee9f09", size = 7176236, upload-time = "2025-10-21T16:22:28.362Z" }, - { url = "https://files.pythonhosted.org/packages/0f/55/dba05d3fcc151ce6e81327541d2cc8394f442f6b350fead67401661bf041/grpcio-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:479496325ce554792dba6548fae3df31a72cef7bad71ca2e12b0e58f9b336bfc", size = 8125795, upload-time = "2025-10-21T16:22:31.075Z" }, - { url = "https://files.pythonhosted.org/packages/4a/45/122df922d05655f63930cf42c9e3f72ba20aadb26c100ee105cad4ce4257/grpcio-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c9b93f79f48b03ada57ea24725d83a30284a012ec27eab2cf7e50a550cbbbcc", size = 7592214, upload-time = "2025-10-21T16:22:33.831Z" }, - { url = "https://files.pythonhosted.org/packages/4a/6e/0b899b7f6b66e5af39e377055fb4a6675c9ee28431df5708139df2e93233/grpcio-1.76.0-cp314-cp314-win32.whl", hash = "sha256:747fa73efa9b8b1488a95d0ba1039c8e2dca0f741612d80415b1e1c560febf4e", size = 4062961, upload-time = "2025-10-21T16:22:36.468Z" }, - { url = "https://files.pythonhosted.org/packages/19/41/0b430b01a2eb38ee887f88c1f07644a1df8e289353b78e82b37ef988fb64/grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e", size = 4834462, upload-time = "2025-10-21T16:22:39.772Z" }, -] - -[[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 = "hanzo-async" -version = "0.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiofiles" }, - { name = "uvloop", marker = "sys_platform != 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ba/2c/fd628adf5bce8a36559f8b867f35876d3938b9869b9c5e410072f36d09ae/hanzo_async-0.1.3.tar.gz", hash = "sha256:47180370268cd1f3be3f3b8f20f4de38f3123c5031f1dcdba114a610c4fb8840", size = 8634, upload-time = "2026-03-10T04:12:17.093Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d8/dc/6f30fdefa0ef99b89d8edb7c1af3f1d480d2c4dbaffd705235de1105547d/hanzo_async-0.1.3-py3-none-any.whl", hash = "sha256:1c5b17469b612b9994a04d986295600b8cc7b22ea3e7fc9ff34107a20ba1f0c1", size = 8675, upload-time = "2026-03-10T04:12:15.914Z" }, -] - -[[package]] -name = "hanzo-iam" -version = "1.29.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "cryptography" }, - { name = "pyjwt" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3e/4b/11d440a3a99e5b7967ae1a9d56f4bee7c9879f7e2a56a9a1398a3d931064/hanzo_iam-1.29.0.tar.gz", hash = "sha256:5979db89b791be181c259d103822424f389be5d82bff677bee9fad3f213f2578", size = 25123, upload-time = "2025-04-09T18:51:53.64Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/26/bc5dbd90e5fd0f2666646c2b4831cc4fef6867bbad8091da496851fe600c/hanzo_iam-1.29.0-py2.py3-none-any.whl", hash = "sha256:22aba50d91d642843570fd73853783cc26bad7ac618c778d2df49e54bd43bed9", size = 47149, upload-time = "2025-04-09T18:51:52.134Z" }, -] - -[[package]] -name = "hanzo-kms" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "httpx" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/78/c459ae92072e55d94e6b0718d927fb2801c06ea8ef8a5cacc417c237b385/hanzo_kms-1.1.0.tar.gz", hash = "sha256:13242266012dcc2a1b48705b48704504409f21c13ec022c32385f952cf4b9f8b", size = 7553, upload-time = "2026-02-21T06:20:06.542Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/13/77301a5216f4f3c85689061e590f606086f32f26bb0ea7a86ce850223e04/hanzo_kms-1.1.0-py3-none-any.whl", hash = "sha256:19ec34ae131917e153feade770f4aa03366ba43a662fba040d3e31cd78dd2fd3", size = 10672, upload-time = "2026-02-21T06:20:05.56Z" }, -] - -[[package]] -name = "hanzo-mcp" -version = "0.15.0" -source = { editable = "." } -dependencies = [ - { name = "fastmcp" }, - { name = "hanzo-async" }, - { name = "hanzo-persona" }, - { name = "hanzo-tools" }, - { name = "hanzo-tools-agent" }, - { name = "hanzo-tools-api" }, - { name = "hanzo-tools-auth" }, - { name = "hanzo-tools-billing" }, - { name = "hanzo-tools-browser", extra = ["playwright"] }, - { name = "hanzo-tools-code" }, - { name = "hanzo-tools-commerce" }, - { name = "hanzo-tools-computer" }, - { name = "hanzo-tools-config" }, - { name = "hanzo-tools-fs" }, - { name = "hanzo-tools-iam" }, - { name = "hanzo-tools-ingress" }, - { name = "hanzo-tools-kms" }, - { name = "hanzo-tools-llm" }, - { name = "hanzo-tools-lsp" }, - { name = "hanzo-tools-memory" }, - { name = "hanzo-tools-mpc" }, - { name = "hanzo-tools-net" }, - { name = "hanzo-tools-paas" }, - { name = "hanzo-tools-reasoning" }, - { name = "hanzo-tools-refactor" }, - { name = "hanzo-tools-shell" }, - { name = "hanzo-tools-team" }, - { name = "hanzo-tools-todo" }, - { name = "hanzo-tools-vcs" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "typing-extensions" }, - { name = "uvloop", marker = "sys_platform != 'win32'" }, - { name = "zap-protocol" }, -] - -[package.optional-dependencies] -dev = [ - { name = "black" }, - { name = "mypy" }, - { name = "pytest" }, - { name = "pytest-cov" }, - { name = "ruff" }, - { name = "types-aiofiles" }, - { name = "types-psutil" }, - { name = "types-setuptools" }, - { name = "watchdog" }, -] -docs = [ - { name = "myst-parser" }, - { name = "sphinx" }, - { name = "sphinx-copybutton" }, - { name = "sphinx-rtd-theme" }, -] -ide = [ - { name = "hanzo-tools-ide" }, -] -interactive = [ - { name = "hanzo-tools-browser" }, - { name = "hanzo-tools-ide" }, - { name = "hanzo-tools-repl" }, - { name = "ipykernel" }, - { name = "jupyter-client" }, -] -memory = [ - { name = "fastembed" }, - { name = "hanzo-tools-memory", extra = ["full"] }, - { name = "sqlite-vec" }, -] -performance = [ - { name = "orjson" }, - { name = "ujson" }, -] -publish = [ - { name = "build" }, - { name = "twine" }, -] -repl = [ - { name = "hanzo-tools-repl" }, - { name = "ipykernel" }, - { name = "jupyter-client" }, -] -test = [ - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "pytest-mock" }, -] - -[package.metadata] -requires-dist = [ - { name = "black", marker = "extra == 'dev'", specifier = ">=23.3.0" }, - { name = "build", marker = "extra == 'publish'", specifier = ">=1.0.3" }, - { name = "fastembed", marker = "extra == 'memory'", specifier = ">=0.4.0" }, - { name = "fastmcp", specifier = ">=2.14.4" }, - { name = "hanzo-async", specifier = ">=0.1.3" }, - { name = "hanzo-mcp", extras = ["repl", "ide"], marker = "extra == 'interactive'" }, - { name = "hanzo-persona", specifier = ">=1.0.0" }, - { name = "hanzo-tools", specifier = ">=0.3.0" }, - { name = "hanzo-tools-agent", specifier = ">=0.3.1" }, - { name = "hanzo-tools-api", specifier = ">=0.3.1" }, - { name = "hanzo-tools-auth", specifier = ">=0.1.0" }, - { name = "hanzo-tools-billing", specifier = ">=0.1.0" }, - { name = "hanzo-tools-browser", marker = "extra == 'interactive'", specifier = ">=0.2.1" }, - { name = "hanzo-tools-browser", extras = ["playwright"], specifier = ">=0.4.5" }, - { name = "hanzo-tools-code", specifier = ">=0.1.0" }, - { name = "hanzo-tools-commerce", specifier = ">=0.1.0" }, - { name = "hanzo-tools-computer", specifier = ">=0.1.0" }, - { name = "hanzo-tools-config", specifier = ">=0.1.0" }, - { name = "hanzo-tools-fs", specifier = ">=0.1.0" }, - { name = "hanzo-tools-iam", specifier = ">=0.1.0" }, - { name = "hanzo-tools-ide", marker = "extra == 'ide'", specifier = ">=0.1.0" }, - { name = "hanzo-tools-ingress", specifier = ">=0.1.0" }, - { name = "hanzo-tools-kms", specifier = ">=0.1.0" }, - { name = "hanzo-tools-llm", specifier = ">=0.1.0" }, - { name = "hanzo-tools-lsp", specifier = ">=0.1.0" }, - { name = "hanzo-tools-memory", specifier = ">=0.2.0" }, - { name = "hanzo-tools-memory", extras = ["full"], marker = "extra == 'memory'", specifier = ">=0.2.2" }, - { name = "hanzo-tools-mpc", specifier = ">=0.1.0" }, - { name = "hanzo-tools-net", specifier = ">=0.1.0" }, - { name = "hanzo-tools-paas", specifier = ">=0.1.0" }, - { name = "hanzo-tools-reasoning", specifier = ">=0.1.0" }, - { name = "hanzo-tools-refactor", specifier = ">=0.1.0" }, - { name = "hanzo-tools-repl", marker = "extra == 'repl'", specifier = ">=0.1.0" }, - { name = "hanzo-tools-shell", specifier = ">=0.6.1" }, - { name = "hanzo-tools-team", specifier = ">=0.1.0" }, - { name = "hanzo-tools-todo", specifier = ">=0.1.0" }, - { name = "hanzo-tools-vcs", specifier = ">=0.1.0" }, - { name = "ipykernel", marker = "extra == 'repl'", specifier = ">=6.29.0" }, - { name = "jupyter-client", marker = "extra == 'repl'", specifier = ">=8.6.0" }, - { name = "mcp", specifier = ">=1.25.0" }, - { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.10.0" }, - { name = "myst-parser", marker = "extra == 'docs'", specifier = ">=4.0.0" }, - { name = "orjson", marker = "extra == 'performance'", specifier = ">=3.9.0" }, - { name = "pydantic", specifier = ">=2.12.5" }, - { name = "pydantic-settings", specifier = ">=2.12.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0" }, - { name = "pytest", marker = "extra == 'test'", specifier = ">=7.0.0" }, - { name = "pytest-asyncio", marker = "extra == 'test'", specifier = ">=0.26.0,<1.0.0" }, - { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.1.0" }, - { name = "pytest-cov", marker = "extra == 'test'", specifier = ">=4.1.0" }, - { name = "pytest-mock", marker = "extra == 'test'", specifier = ">=3.10.0" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.14.0" }, - { name = "sphinx", marker = "extra == 'docs'", specifier = ">=8.0.0" }, - { name = "sphinx-copybutton", marker = "extra == 'docs'", specifier = ">=0.5.0" }, - { name = "sphinx-rtd-theme", marker = "extra == 'docs'", specifier = ">=3.0.0" }, - { name = "sqlite-vec", marker = "extra == 'memory'", specifier = ">=0.1.0" }, - { name = "twine", marker = "extra == 'publish'", specifier = ">=4.0.2" }, - { name = "types-aiofiles", marker = "extra == 'dev'", specifier = ">=23.2.0" }, - { name = "types-psutil", marker = "extra == 'dev'", specifier = ">=5.9.5" }, - { name = "types-setuptools", marker = "extra == 'dev'", specifier = ">=69.5.0" }, - { name = "typing-extensions", specifier = ">=4.13.0" }, - { name = "ujson", marker = "extra == 'performance'", specifier = ">=5.7.0" }, - { name = "uvloop", marker = "sys_platform != 'win32'", specifier = ">=0.22.1" }, - { name = "watchdog", marker = "extra == 'dev'", specifier = ">=3.0.0" }, - { name = "zap-protocol", specifier = ">=0.3.0" }, -] -provides-extras = ["memory", "repl", "ide", "interactive", "dev", "docs", "test", "performance", "publish"] - -[[package]] -name = "hanzo-memory" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiocache" }, - { name = "chromadb" }, - { name = "fastapi" }, - { name = "fastembed" }, - { name = "httpx" }, - { name = "lancedb" }, - { name = "litellm" }, - { name = "mcp" }, - { name = "numpy" }, - { name = "orjson" }, - { name = "passlib", extra = ["bcrypt"] }, - { name = "polars" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "python-jose", extra = ["cryptography"] }, - { name = "python-multipart" }, - { name = "redis" }, - { name = "rich" }, - { name = "scikit-learn" }, - { name = "sentence-transformers" }, - { name = "structlog" }, - { name = "tenacity" }, - { name = "tiktoken" }, - { name = "uvicorn", extra = ["standard"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9c/8a/9cbcff33dcbc3c05d8de53120c4b039bf5d41dfcf0d73c3846c15dcba936/hanzo_memory-1.0.1.tar.gz", hash = "sha256:8b31210c967cfb5fe2b109d804f67017bb2771dec508543e2de02563e898c3f0", size = 44623, upload-time = "2025-09-17T22:11:52.775Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/12/223b4c3a0c3fe336421a1b7dc4345bd6ef2d1b9fe1db2abe6de896aa0f44/hanzo_memory-1.0.1-py3-none-any.whl", hash = "sha256:fd0fea34a63d38b7e5dcc83bff23086d295bec8b0cec4c449d6338a8d0a17f09", size = 40932, upload-time = "2025-09-17T22:11:51.316Z" }, -] - -[[package]] -name = "hanzo-persona" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/15/15ecb8b1825d614adaad211a7902cbc05c185ff94160c6445d67e73bb473/hanzo_persona-1.0.0.tar.gz", hash = "sha256:a74f58788bb72623697fbd72e7848c3d6ad306f630ae7007ddfcde7dfc0ef7a5", size = 13305, upload-time = "2025-12-26T18:46:38.055Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/50/c7/53bdab3e003e839a7d574c8532d686c60642e85818ea861926a08b0f7822/hanzo_persona-1.0.0-py3-none-any.whl", hash = "sha256:e3c5e2d214d79af5f6dc728fdef35c7cad180ff8d3bcc6ba9fca30cbba96b068", size = 14857, upload-time = "2025-12-26T18:46:36.731Z" }, -] - -[[package]] -name = "hanzo-tools" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/3e/2d94dc54e202bdb11f6e4597dd68eebc554d2b92fffb4f6918cdf3f91fe2/hanzo_tools-0.3.0.tar.gz", hash = "sha256:d00cb3212a707e22f9bb5a21f0f9eb34a74f22ff2b5f24e2f8b6321f9880e2fb", size = 10929, upload-time = "2025-12-27T18:56:38.843Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/07/6ebbcf371aafa5f2d171de2916ef92c73978b927b34a8863af53e1b1a80b/hanzo_tools-0.3.0-py3-none-any.whl", hash = "sha256:c7b0f6f7c3089f06329bc1aaca39fbce4b7108fdbd048e2bbc450a3aff9941f2", size = 11928, upload-time = "2025-12-27T18:56:37.528Z" }, -] - -[[package]] -name = "hanzo-tools-agent" -version = "0.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "hanzo-async" }, - { name = "hanzo-tools" }, - { name = "hanzo-tools-shell" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/db/a66da27139016535a58cad1b874dc757ddd6426726fca100bb98d07fed19/hanzo_tools_agent-0.3.1.tar.gz", hash = "sha256:ede2273174bb2d41791d823f0eb5c1d1f8c106a6ad1da4abf40418092d5d7624", size = 71757, upload-time = "2026-01-05T14:51:13.575Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/cf/ec37667cc32b5fa48c9fc1be37de0c8865f5392ffad5598c956684e81d8d/hanzo_tools_agent-0.3.1-py3-none-any.whl", hash = "sha256:ef0a7d7481047f61dc7dec1c2f0a92f9b63d9c29069138d400c77c18a5bde205", size = 84867, upload-time = "2026-01-05T14:51:12.593Z" }, -] - -[[package]] -name = "hanzo-tools-api" -version = "0.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiofiles" }, - { name = "hanzo-tools-core" }, - { name = "httpx" }, - { name = "pydantic" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b7/a9/4277bf9752f043e4b42831468b1ae4e85a628612b6d6e9ecdcece8f98e63/hanzo_tools_api-0.3.1.tar.gz", hash = "sha256:bab061526849f61234e0a2bdf1e8c02092374566125ad721abd322c3cf506b45", size = 204438, upload-time = "2026-01-20T07:12:31.863Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/5d/6f00bbd79ecc3b0313cf7bd52d7c986fe7a8fa74d3f0052216b9bfbc5476/hanzo_tools_api-0.3.1-py3-none-any.whl", hash = "sha256:3f9a26f3e195b6ec1c4ec049b7fb9003183a0717f8a217bf14156da43d6d6d15", size = 134747, upload-time = "2026-01-20T07:12:30.152Z" }, -] - -[[package]] -name = "hanzo-tools-auth" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-iam" }, - { name = "hanzo-tools-core" }, - { name = "httpx" }, - { name = "pyjwt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/07/0c8a54dae8052ebe7880a7a482142a885738f03b830fae05f7930181dea2/hanzo_tools_auth-0.1.0.tar.gz", hash = "sha256:27fa0d7efeda058cf1c6e4ad593f1da73bf3d1e501f59685178b37da28cab1ad", size = 6150, upload-time = "2026-02-25T05:57:54.598Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/63/6b82a88840210518ce9a3eefa9f96dd6febb9656d49c68c5734a863aed6a/hanzo_tools_auth-0.1.0-py3-none-any.whl", hash = "sha256:38e75848892179dd5e3da184605d0dc7488070833cad21872a012e651a1b9f70", size = 7386, upload-time = "2026-02-25T05:57:51.519Z" }, -] - -[[package]] -name = "hanzo-tools-billing" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-auth" }, - { name = "hanzo-tools-core" }, - { name = "httpx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/44/09/cff246b0d60fd80957bbe5f0d4b44efc8307677f1c5958e70aab96e9be72/hanzo_tools_billing-0.1.0.tar.gz", hash = "sha256:308bef7e148561ad147bfc0186b7afcc7de089cf6fcb1e44a5a7533f3c93661b", size = 4052, upload-time = "2026-03-02T04:50:34.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/2e/2db6833ddc12085b9435490165deeb21e3ea844c8759dc38b300d7351eb0/hanzo_tools_billing-0.1.0-py3-none-any.whl", hash = "sha256:43513c2c1408a1a7148914ce4237dadce410eb73f74b2025120e1edfd2ab6c54", size = 4748, upload-time = "2026-03-02T04:50:33.673Z" }, -] - -[[package]] -name = "hanzo-tools-browser" -version = "0.4.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "hanzo-tools" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/76/28/9ff0fb5b2ca66e2d92f1ca8648dd620d78846a5562f771a29b3ad63924b1/hanzo_tools_browser-0.4.5.tar.gz", hash = "sha256:768d8e0a53d5a57b05a65f4994b0ef3778b93dc061b3945575f1f4881eb64279", size = 29440, upload-time = "2026-03-10T17:37:39.185Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/4a/f792e0ae823f1e84328db635510790e9272711d513ff1cc4c0eb7033abb0/hanzo_tools_browser-0.4.5-py3-none-any.whl", hash = "sha256:2bf0344bb54301629d542217c949f740afb75f8bb7e891b822bb833f21d1d07b", size = 29862, upload-time = "2026-03-10T17:37:37.262Z" }, -] - -[package.optional-dependencies] -playwright = [ - { name = "playwright" }, -] - -[[package]] -name = "hanzo-tools-code" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-core" }, - { name = "mcp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/25/e5/2c9ec04d3e7d8382649dd89d81888cc2fbd3f2b06ad37d47198bae3891af/hanzo_tools_code-0.1.0.tar.gz", hash = "sha256:88e51b7448d556ddb02df5aaca76d61f42730c6567383dc8a6e5f8578684e646", size = 7703, upload-time = "2026-03-10T00:40:52.504Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/fc/407d3b57491f7e1349da47ca636d03933448b8983d9ecc71a9c02f13bf7e/hanzo_tools_code-0.1.0-py3-none-any.whl", hash = "sha256:34a2a40f3d23e663c948e4abad307402ec84a5422f3c8a133d0a74366b052d1f", size = 8405, upload-time = "2026-03-10T00:40:51.369Z" }, -] - -[[package]] -name = "hanzo-tools-commerce" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-auth" }, - { name = "hanzo-tools-core" }, - { name = "httpx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3e/da/bd05c7ef454c5cb7a87a2c3e4fef5d3f65b340bf71b1e1afb74dada2408e/hanzo_tools_commerce-0.1.0.tar.gz", hash = "sha256:cf3fbe7577bd3684af28df6b07379283212f21092b86b13b46a5ef0f71b3d652", size = 3618, upload-time = "2026-03-02T04:50:40.128Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/80/5af45745f0cb088597618c796cebe81638b432469f2b19e288004c73ada5/hanzo_tools_commerce-0.1.0-py3-none-any.whl", hash = "sha256:8311d4c0fa08c25bf75b1ede42e18c6b93c5b024ac1aa2e39ab053ffbdceca02", size = 4325, upload-time = "2026-03-02T04:50:38.92Z" }, -] - -[[package]] -name = "hanzo-tools-computer" -version = "0.5.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools" }, - { name = "mcp" }, - { name = "numpy" }, - { name = "pillow" }, - { name = "pyautogui" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/bd/00d4e47a49c33b057ff443f33b8b836b7b0ccbeb10972d3d2944664bf63f/hanzo_tools_computer-0.5.2.tar.gz", hash = "sha256:cdec76dad07d01587ed5358ef1581ddcf83cb462b30e8eff2a2884905acef404", size = 109353, upload-time = "2026-01-12T04:45:45.22Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/90/143881ff12f14e10f87ae33a5c19087df74e9a71ecf131caa5159fd84403/hanzo_tools_computer-0.5.2-py3-none-any.whl", hash = "sha256:88baadcec8463d585cf7adc06cfa61acf13a08d967e81fd9a99805adb401c5d2", size = 31281, upload-time = "2026-01-12T04:45:43.86Z" }, -] - -[[package]] -name = "hanzo-tools-config" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "hanzo-tools-core" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/60/d8/a013cb956850f404f6e306fabe974ff9a7462d936f19f569ce7587fe70f1/hanzo_tools_config-0.2.0.tar.gz", hash = "sha256:fdbc63abc6e588e361a8c20e3b6b5947387161d168e945769b0efbe757128eaf", size = 8665, upload-time = "2025-12-26T18:29:59.387Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/eb/83e34ef9480890805bbf0e99795afbfa0816bc527c4f72104101d70f0f93/hanzo_tools_config-0.2.0-py3-none-any.whl", hash = "sha256:b130d13d5491ebea0a64230960c51c20dfaffafcd97dc84ab7e1a38a7da8d469", size = 9927, upload-time = "2025-12-26T18:29:58.532Z" }, -] - -[[package]] -name = "hanzo-tools-core" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a9/67/eabf2d355819b9c948a9898ed537fa014a5de0422de66e0ea4203faae6be/hanzo_tools_core-0.2.0.tar.gz", hash = "sha256:ab5352056d3db1d42aadd94d81635eb835476194562b07b497f327f6d334c058", size = 11441, upload-time = "2025-12-26T14:46:12.281Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/16/c1a705434afc5936156eadedcc29e1200fb4871e7a1b83d5efeebefbfcda/hanzo_tools_core-0.2.0-py3-none-any.whl", hash = "sha256:e07cdc3692003e30a40082be3750a0670b2adf612e3e7a56dd741d3b532b8090", size = 11026, upload-time = "2025-12-26T14:46:11.514Z" }, -] - -[[package]] -name = "hanzo-tools-fs" -version = "0.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "ffind" }, - { name = "grep-ast" }, - { name = "hanzo-async" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "watchdog" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6b/af/d5735a32339d6bf783a708bd9b294388d59003820a2f4648e006d4213112/hanzo_tools_fs-0.3.1.tar.gz", hash = "sha256:3751fa060ec8cccc26eb3dd2f9ed1771bea3a38373e7b84dac95c4a1067b37c5", size = 10968, upload-time = "2026-01-04T01:40:46.164Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/b2/4418ab22f8854992c786b145f87277a0a4ea2798b865af592a2725495348/hanzo_tools_fs-0.3.1-py3-none-any.whl", hash = "sha256:6892bf6ba2559cad90876276b7022bfc45923d86866ac08e83bbe86e0771081f", size = 14541, upload-time = "2026-01-04T01:40:44.447Z" }, -] - -[[package]] -name = "hanzo-tools-iam" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-auth" }, - { name = "hanzo-tools-core" }, - { name = "httpx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5d/b3/a0ae35cf9d859341982b2e9b87e63c51f12b6db8d0e69099afd54ebd40d5/hanzo_tools_iam-0.1.0.tar.gz", hash = "sha256:e901af6afcf5cf728fdf7cb099082e901a2870aacfc8b7613f6257a738c470d4", size = 5639, upload-time = "2026-03-02T04:50:44.574Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/ca/7fe1e7dd334bd7204007a712aeb380a016464f8f0513e1fbb4e8c535f490/hanzo_tools_iam-0.1.0-py3-none-any.whl", hash = "sha256:94b9c267fe0e80d3b00b93cf9921e071fd6cdf5ed4bafd5cf34e8b7f8eaf7ac7", size = 6287, upload-time = "2026-03-02T04:50:43.63Z" }, -] - -[[package]] -name = "hanzo-tools-ide" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "hanzo-tools-core" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/10/ca700250dfc63987635c339ab4caad7b6bde1fa79ac97f212b3a34b291e5/hanzo_tools_ide-0.1.0.tar.gz", hash = "sha256:6737bb9c9e65157eb2660d6ddc363a653561f838cc94b60d40c864b747d40f52", size = 8132, upload-time = "2026-01-22T23:48:39.571Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/01/dc6ffba6b34403d8295a7225af55c2d891ca5db16cfb9420e76264145f75/hanzo_tools_ide-0.1.0-py3-none-any.whl", hash = "sha256:8b1a7fd381c6be5a265e1711b67bd007e1bc2ae5dc61e60bcbbc8d8e6a7d4bb7", size = 7984, upload-time = "2026-01-22T23:48:38.153Z" }, -] - -[[package]] -name = "hanzo-tools-ingress" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-auth" }, - { name = "hanzo-tools-core" }, - { name = "httpx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d2/94/d942ab541b7a8d029ff1c3171f34827b539a5a8360277ab34bbeee8d6888/hanzo_tools_ingress-0.1.0.tar.gz", hash = "sha256:26ed087f9f5ec10b8159c86b623f052d06b2f6320aada917221a0545bb02bfc5", size = 3737, upload-time = "2026-03-02T04:50:54.997Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/7b/0672340573d51ad915163f74043bfed7357cf9093d4e1927f3cf18f7f7c7/hanzo_tools_ingress-0.1.0-py3-none-any.whl", hash = "sha256:d57647444a6733d0dc2a9d97cb3041a75de6157010e5a37890e5ec4ca99ccdc2", size = 4423, upload-time = "2026-03-02T04:50:53.957Z" }, -] - -[[package]] -name = "hanzo-tools-kms" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-kms" }, - { name = "hanzo-tools-core" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/52/7c395f6425d1d76464031d7636a9a60fc4ff72e419613e2c7daa1fffece8/hanzo_tools_kms-0.1.0.tar.gz", hash = "sha256:d3e77a58f9b3d545f971dba75df42a5045a9036160b73136476e21d10f9e9ced", size = 3687, upload-time = "2026-02-25T05:57:56.217Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/43/b9a301144ecc000d13d5e69ae02fb58ef82c73c3ec1e8042f0ea4775755b/hanzo_tools_kms-0.1.0-py3-none-any.whl", hash = "sha256:fcfc8eee546ff17ffe11b15450be21a2d3696caa5565f242c66ad1e99a590ad8", size = 4306, upload-time = "2026-02-25T05:57:52.606Z" }, -] - -[[package]] -name = "hanzo-tools-llm" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "hanzo-tools-core" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/e7/d90c92abfbb4ed5884e5183eda1e6ab65d9f7bd22d9a0cbcf2d5d3f321ed/hanzo_tools_llm-0.2.0.tar.gz", hash = "sha256:4ef06e5f8ab91e59b4404bd5b7a35657fb55c7ce62efa524ce99c5e320424703", size = 16839, upload-time = "2025-12-26T18:30:01.148Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/8a/42849884339e7b8a65b6db1ea421554bb50ee4e4abdb4fa40a9acc0e31b1/hanzo_tools_llm-0.2.0-py3-none-any.whl", hash = "sha256:4c57d77ce18358327647643f354f5489365b699789320dac3deb025c441509cb", size = 20882, upload-time = "2025-12-26T18:29:58.592Z" }, -] - -[[package]] -name = "hanzo-tools-lsp" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-core" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7e/aa/5a8f9e3310fb1ed1ec5ea48b2054e413ac9b62be6f5316b0303e7212c5e8/hanzo_tools_lsp-0.2.0.tar.gz", hash = "sha256:f19a52e9025a5f4ba96dc98a7072d117fa94e9f5a64c4055a5353c7841b639c0", size = 7913, upload-time = "2025-12-26T14:46:58.64Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/61/7771fa35a4013c123c289cd31cf9e599b014990fb39a8a3f3abee7673a8b/hanzo_tools_lsp-0.2.0-py3-none-any.whl", hash = "sha256:2e2926787efdd52d613823491e52dc599d3505a6954d1ea69ec0fd2a3dd4a371", size = 8468, upload-time = "2025-12-26T14:46:57.761Z" }, -] - -[[package]] -name = "hanzo-tools-memory" -version = "0.2.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ed/c6/ddd1a702d436b5abc1c6b0aaf46a6f97036c06c2cb951fd31b1d6b65abd0/hanzo_tools_memory-0.2.2.tar.gz", hash = "sha256:4d73ae44c47ba02783588dde96d68c4d463cb299de253b93f948b04ccf5594dd", size = 12005, upload-time = "2026-03-06T10:42:16.408Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/f6/c4b380ec0b372b0c81820e2d71f303f248b55f37840fe20743de784c2964/hanzo_tools_memory-0.2.2-py3-none-any.whl", hash = "sha256:3b5d3a1cc19461d42d1cfcd12caaa6fa9235dfd79465bb31810dea22280433f2", size = 11415, upload-time = "2026-03-06T10:42:15.368Z" }, -] - -[package.optional-dependencies] -full = [ - { name = "hanzo-memory" }, -] - -[[package]] -name = "hanzo-tools-mpc" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-auth" }, - { name = "hanzo-tools-core" }, - { name = "httpx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b7/89/da067ccc7549fa1f9fd21bd81962b5eb1d67266ab6003a1f7838d9847032/hanzo_tools_mpc-0.1.0.tar.gz", hash = "sha256:96a46d149594bf7ae33cfb89f24248fe2591daa1752b7c147a746e06d9f7ce38", size = 5082, upload-time = "2026-03-02T04:50:59.895Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/38/0265de9384a500e224841183d494a706f32ddfc5d9bfd20d1a90a8ca0adc/hanzo_tools_mpc-0.1.0-py3-none-any.whl", hash = "sha256:d16a41502e424fafd66503bee098239116ba4c1a830bec49776033baa6015877", size = 5692, upload-time = "2026-03-02T04:50:58.502Z" }, -] - -[[package]] -name = "hanzo-tools-net" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-core" }, - { name = "httpx" }, - { name = "mcp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/20/ab/840bb2ac99af207c85fbb7621a91ac9d1b359a3104d5fb8d8faddd1bf3a9/hanzo_tools_net-0.1.0.tar.gz", hash = "sha256:e34aeb74ad5d37a4c2b00bd882f0dfc3f7d38ad8f53883da7981da12c1c17952", size = 6042, upload-time = "2026-03-10T00:40:41.178Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/77/f051ec31c7e96c05ef09a6c215ec0c590755c82228f323e78ccd74be8a65/hanzo_tools_net-0.1.0-py3-none-any.whl", hash = "sha256:714124b149d6c5ebad08875461c5d37a89b6e0f6be618d34cb17f159f68bdc92", size = 6755, upload-time = "2026-03-10T00:40:39.718Z" }, -] - -[[package]] -name = "hanzo-tools-paas" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-iam" }, - { name = "hanzo-tools-auth" }, - { name = "hanzo-tools-core" }, - { name = "httpx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4c/a9/7be54abddbd34f7c8353095ab2cd2e1485fbb29c673052531b726949aaa0/hanzo_tools_paas-0.1.0.tar.gz", hash = "sha256:74ec3dd1098198a9571af00c200e1925abdd94ed10c741e4ae20419081585937", size = 3854, upload-time = "2026-02-25T06:05:03.07Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/4a/6ca908f31fab52b41c7bfa75cc3500c320818bc9980f617bb56ada718425/hanzo_tools_paas-0.1.0-py3-none-any.whl", hash = "sha256:b1e74f42d2df8e1966d3a5768fde034c82c4ed8b7ff3429b142f9e72763a5f36", size = 4491, upload-time = "2026-02-25T06:05:02.024Z" }, -] - -[[package]] -name = "hanzo-tools-reasoning" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-core" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/90/c9/59ffa443cb3daeaf1d2009d417d3bf1cddb3cf2d7cede6610f9c7ae04162/hanzo_tools_reasoning-0.2.0.tar.gz", hash = "sha256:4d54a248ea92c42cc1c41195073d20fe3f65b2526d19c51b2f8dd84d25b0e8b2", size = 5381, upload-time = "2025-12-26T14:46:37.417Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/d4/1126584049f18085012f9d1fac3e44c81b43f4fb0ea2ea5c4971c5434d6e/hanzo_tools_reasoning-0.2.0-py3-none-any.whl", hash = "sha256:9d9ed9160bc778b087c30c6afdf0252cddeb9dd772f07b2542527629ab4871ad", size = 7209, upload-time = "2025-12-26T14:46:36.314Z" }, -] - -[[package]] -name = "hanzo-tools-refactor" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "hanzo-tools-core" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/31/c8317babfb160e8b002c047de65206bf30607f1eadb341d2d377cbf358e8/hanzo_tools_refactor-0.2.0.tar.gz", hash = "sha256:283b1bfc036de7dbba1e44c8332df9285385885af6d5f5f81d4b7ae1902c7f3f", size = 19653, upload-time = "2025-12-26T14:46:59.372Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/f4/8efcdcdb398e3c16242331c4a2321750ca3ea1dd51077811b4034215eb5f/hanzo_tools_refactor-0.2.0-py3-none-any.whl", hash = "sha256:b7cd5963ff1ad2b418397202ac6134dd1ad4b079563ff3755f9c935d6556cb51", size = 20019, upload-time = "2025-12-26T14:46:57.835Z" }, -] - -[[package]] -name = "hanzo-tools-repl" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-core" }, - { name = "jupyter-client" }, - { name = "jupyter-core" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/e9/046be03d0b877307c447a623d51820bae260850c682275087a450b335521/hanzo_tools_repl-0.1.0.tar.gz", hash = "sha256:58e9ef1712e8a4e317812c7dd46267d2dcd3dc6f7aaa55de85a3bcae3d59f915", size = 7117, upload-time = "2026-01-22T23:48:34.983Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/93/0f0bf1e1d2c1114dffb8fd50b8de3782ef9df535cdc5784d4bf93fe6af54/hanzo_tools_repl-0.1.0-py3-none-any.whl", hash = "sha256:fdddc005232f29c75f3788f5bcaff0377ff347d06537fccac3060d749469e49d", size = 7362, upload-time = "2026-01-22T23:48:33.51Z" }, -] - -[[package]] -name = "hanzo-tools-shell" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-async" }, - { name = "hanzo-tools" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "tiktoken" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f9/06/5d8c57757f2f13fc879a7db2b6be43d57d908212b4bbdaf587c23470cc1d/hanzo_tools_shell-0.6.1.tar.gz", hash = "sha256:f437cf204d276bb8543dbc659d152d77ef9d04d634d1d551e216a4a4cf42d334", size = 39564, upload-time = "2026-01-12T03:21:55.948Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/65/db/fe26bc4cace2900e000644d61cb2a37502312a4e20082d85cde11e419ba0/hanzo_tools_shell-0.6.1-py3-none-any.whl", hash = "sha256:d0a7eef3385744dba2e5dd0a268a44884a3276ef2ea4d8c96cc06e43c226e866", size = 46161, upload-time = "2026-01-12T03:21:55.028Z" }, -] - -[[package]] -name = "hanzo-tools-team" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-auth" }, - { name = "hanzo-tools-core" }, - { name = "httpx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/a4/ec214fd59735abc9d84303300b2548c8e841a098013efde0a3b27c7fe3e2/hanzo_tools_team-0.1.0.tar.gz", hash = "sha256:ebec2d8fd13d95b8e820d025e832460bad233f0212356e34611e8fe0a6aa27c6", size = 3952, upload-time = "2026-03-02T04:50:50.169Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/66/eb60ebb86ab73e03feec61548a0c4be95c21f95547d7b9c20817dface691/hanzo_tools_team-0.1.0-py3-none-any.whl", hash = "sha256:1ac1c76b3c0a06bcc937171cf3f7cf5c149bc936ce47f7ee4f1e09d755c6fd58", size = 4583, upload-time = "2026-03-02T04:50:48.513Z" }, -] - -[[package]] -name = "hanzo-tools-todo" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-core" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d0/ca/81e888e55326c218863290e9c9b4a52b2f3512aefe2e38e89595f58c45e4/hanzo_tools_todo-0.2.0.tar.gz", hash = "sha256:85cfca809bb12bb9b6f7c913888acc5d6e9d5e1891aac1b26ef7c5679f160d03", size = 7180, upload-time = "2025-12-26T14:46:38.189Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/dc/3a34128bddba3ad5a0bae1824d123d9d64f3aba305d1637a6c89e230dd1b/hanzo_tools_todo-0.2.0-py3-none-any.whl", hash = "sha256:6a6a47decef663f1737cc447454da560212bdc3c4a9b7e331b9961f32f96a572", size = 8086, upload-time = "2025-12-26T14:46:36.459Z" }, -] - -[[package]] -name = "hanzo-tools-vcs" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-core" }, - { name = "mcp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/7d/a1243bb8a18314e42b7e492662de2c9c988eacb8c4955ea400912e36076f/hanzo_tools_vcs-0.1.0.tar.gz", hash = "sha256:adb657fd9eeb496e1131c1f646e4fba458b50886a27d3b5a272dfba76ef38f4b", size = 5241, upload-time = "2026-03-10T00:40:48.704Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/42/c029f04ee81c09fe390b47170448b08c563252611a6ab7eff92dd87bd53f/hanzo_tools_vcs-0.1.0-py3-none-any.whl", hash = "sha256:991bda97b058e3fc00fc95bee8140557cc3356f1d007fdd7107478bf2c37e2e0", size = 5904, upload-time = "2026-03-10T00:40:49.835Z" }, -] - -[[package]] -name = "hf-xet" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020, upload-time = "2025-10-24T19:04:32.129Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/a5/85ef910a0aa034a2abcfadc360ab5ac6f6bc4e9112349bd40ca97551cff0/hf_xet-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:ceeefcd1b7aed4956ae8499e2199607765fbd1c60510752003b6cc0b8413b649", size = 2861870, upload-time = "2025-10-24T19:04:11.422Z" }, - { url = "https://files.pythonhosted.org/packages/ea/40/e2e0a7eb9a51fe8828ba2d47fe22a7e74914ea8a0db68a18c3aa7449c767/hf_xet-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b70218dd548e9840224df5638fdc94bd033552963cfa97f9170829381179c813", size = 2717584, upload-time = "2025-10-24T19:04:09.586Z" }, - { url = "https://files.pythonhosted.org/packages/a5/7d/daf7f8bc4594fdd59a8a596f9e3886133fdc68e675292218a5e4c1b7e834/hf_xet-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d40b18769bb9a8bc82a9ede575ce1a44c75eb80e7375a01d76259089529b5dc", size = 3315004, upload-time = "2025-10-24T19:04:00.314Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ba/45ea2f605fbf6d81c8b21e4d970b168b18a53515923010c312c06cd83164/hf_xet-1.2.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd3a6027d59cfb60177c12d6424e31f4b5ff13d8e3a1247b3a584bf8977e6df5", size = 3222636, upload-time = "2025-10-24T19:03:58.111Z" }, - { url = "https://files.pythonhosted.org/packages/4a/1d/04513e3cab8f29ab8c109d309ddd21a2705afab9d52f2ba1151e0c14f086/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6de1fc44f58f6dd937956c8d304d8c2dea264c80680bcfa61ca4a15e7b76780f", size = 3408448, upload-time = "2025-10-24T19:04:20.951Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7c/60a2756d7feec7387db3a1176c632357632fbe7849fce576c5559d4520c7/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f182f264ed2acd566c514e45da9f2119110e48a87a327ca271027904c70c5832", size = 3503401, upload-time = "2025-10-24T19:04:22.549Z" }, - { url = "https://files.pythonhosted.org/packages/4e/64/48fffbd67fb418ab07451e4ce641a70de1c40c10a13e25325e24858ebe5a/hf_xet-1.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:293a7a3787e5c95d7be1857358a9130694a9c6021de3f27fa233f37267174382", size = 2900866, upload-time = "2025-10-24T19:04:33.461Z" }, - { url = "https://files.pythonhosted.org/packages/e2/51/f7e2caae42f80af886db414d4e9885fac959330509089f97cccb339c6b87/hf_xet-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:10bfab528b968c70e062607f663e21e34e2bba349e8038db546646875495179e", size = 2861861, upload-time = "2025-10-24T19:04:19.01Z" }, - { url = "https://files.pythonhosted.org/packages/6e/1d/a641a88b69994f9371bd347f1dd35e5d1e2e2460a2e350c8d5165fc62005/hf_xet-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a212e842647b02eb6a911187dc878e79c4aa0aa397e88dd3b26761676e8c1f8", size = 2717699, upload-time = "2025-10-24T19:04:17.306Z" }, - { url = "https://files.pythonhosted.org/packages/df/e0/e5e9bba7d15f0318955f7ec3f4af13f92e773fbb368c0b8008a5acbcb12f/hf_xet-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30e06daccb3a7d4c065f34fc26c14c74f4653069bb2b194e7f18f17cbe9939c0", size = 3314885, upload-time = "2025-10-24T19:04:07.642Z" }, - { url = "https://files.pythonhosted.org/packages/21/90/b7fe5ff6f2b7b8cbdf1bd56145f863c90a5807d9758a549bf3d916aa4dec/hf_xet-1.2.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:29c8fc913a529ec0a91867ce3d119ac1aac966e098cf49501800c870328cc090", size = 3221550, upload-time = "2025-10-24T19:04:05.55Z" }, - { url = "https://files.pythonhosted.org/packages/6f/cb/73f276f0a7ce46cc6a6ec7d6c7d61cbfe5f2e107123d9bbd0193c355f106/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e159cbfcfbb29f920db2c09ed8b660eb894640d284f102ada929b6e3dc410a", size = 3408010, upload-time = "2025-10-24T19:04:28.598Z" }, - { url = "https://files.pythonhosted.org/packages/b8/1e/d642a12caa78171f4be64f7cd9c40e3ca5279d055d0873188a58c0f5fbb9/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c91d5ae931510107f148874e9e2de8a16052b6f1b3ca3c1b12f15ccb491390f", size = 3503264, upload-time = "2025-10-24T19:04:30.397Z" }, - { url = "https://files.pythonhosted.org/packages/17/b5/33764714923fa1ff922770f7ed18c2daae034d21ae6e10dbf4347c854154/hf_xet-1.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:210d577732b519ac6ede149d2f2f34049d44e8622bf14eb3d63bbcd2d4b332dc", size = 2901071, upload-time = "2025-10-24T19:04:37.463Z" }, - { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099, upload-time = "2025-10-24T19:04:15.366Z" }, - { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178, upload-time = "2025-10-24T19:04:13.695Z" }, - { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214, upload-time = "2025-10-24T19:04:03.596Z" }, - { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054, upload-time = "2025-10-24T19:04:01.949Z" }, - { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812, upload-time = "2025-10-24T19:04:24.585Z" }, - { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920, upload-time = "2025-10-24T19:04:26.927Z" }, - { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735, upload-time = "2025-10-24T19:04:35.928Z" }, -] - -[[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 = "httptools" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/7f/403e5d787dc4942316e515e949b0c8a013d84078a915910e9f391ba9b3ed/httptools-0.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:38e0c83a2ea9746ebbd643bdfb521b9aa4a91703e2cd705c20443405d2fd16a5", size = 206280, upload-time = "2025-10-10T03:54:39.274Z" }, - { url = "https://files.pythonhosted.org/packages/2a/0d/7f3fd28e2ce311ccc998c388dd1c53b18120fda3b70ebb022b135dc9839b/httptools-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f25bbaf1235e27704f1a7b86cd3304eabc04f569c828101d94a0e605ef7205a5", size = 110004, upload-time = "2025-10-10T03:54:40.403Z" }, - { url = "https://files.pythonhosted.org/packages/84/a6/b3965e1e146ef5762870bbe76117876ceba51a201e18cc31f5703e454596/httptools-0.7.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c15f37ef679ab9ecc06bfc4e6e8628c32a8e4b305459de7cf6785acd57e4d03", size = 517655, upload-time = "2025-10-10T03:54:41.347Z" }, - { url = "https://files.pythonhosted.org/packages/11/7d/71fee6f1844e6fa378f2eddde6c3e41ce3a1fb4b2d81118dd544e3441ec0/httptools-0.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fe6e96090df46b36ccfaf746f03034e5ab723162bc51b0a4cf58305324036f2", size = 511440, upload-time = "2025-10-10T03:54:42.452Z" }, - { url = "https://files.pythonhosted.org/packages/22/a5/079d216712a4f3ffa24af4a0381b108aa9c45b7a5cc6eb141f81726b1823/httptools-0.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362", size = 495186, upload-time = "2025-10-10T03:54:43.937Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9e/025ad7b65278745dee3bd0ebf9314934c4592560878308a6121f7f812084/httptools-0.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e99c7b90a29fd82fea9ef57943d501a16f3404d7b9ee81799d41639bdaae412c", size = 499192, upload-time = "2025-10-10T03:54:45.003Z" }, - { url = "https://files.pythonhosted.org/packages/6d/de/40a8f202b987d43afc4d54689600ff03ce65680ede2f31df348d7f368b8f/httptools-0.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3e14f530fefa7499334a79b0cf7e7cd2992870eb893526fb097d51b4f2d0f321", size = 86694, upload-time = "2025-10-10T03:54:45.923Z" }, - { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889, upload-time = "2025-10-10T03:54:47.089Z" }, - { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180, upload-time = "2025-10-10T03:54:48.052Z" }, - { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596, upload-time = "2025-10-10T03:54:48.919Z" }, - { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268, upload-time = "2025-10-10T03:54:49.993Z" }, - { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517, upload-time = "2025-10-10T03:54:51.066Z" }, - { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337, upload-time = "2025-10-10T03:54:52.196Z" }, - { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743, upload-time = "2025-10-10T03:54:53.448Z" }, - { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" }, - { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" }, - { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" }, - { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" }, - { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" }, - { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" }, - { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" }, -] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, -] - -[[package]] -name = "huggingface-hub" -version = "1.3.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "fsspec" }, - { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, - { name = "httpx" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "shellingham" }, - { name = "tqdm" }, - { name = "typer-slim" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/3f/352efd52136bfd8aa9280c6d4a445869226ae2ccd49ddad4f62e90cfd168/huggingface_hub-1.3.7.tar.gz", hash = "sha256:5f86cd48f27131cdbf2882699cbdf7a67dd4cbe89a81edfdc31211f42e4a5fd1", size = 627537, upload-time = "2026-02-02T10:40:10.61Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/89/bfbfde252d649fae8d5f09b14a2870e5672ed160c1a6629301b3e5302621/huggingface_hub-1.3.7-py3-none-any.whl", hash = "sha256:8155ce937038fa3d0cb4347d752708079bc85e6d9eb441afb44c84bcf48620d2", size = 536728, upload-time = "2026-02-02T10:40:08.274Z" }, -] - -[[package]] -name = "humanfriendly" -version = "10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyreadline3", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" }, -] - -[[package]] -name = "id" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/22/11/102da08f88412d875fa2f1a9a469ff7ad4c874b0ca6fed0048fe385bdb3d/id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d", size = 15237, upload-time = "2024-12-04T19:53:05.575Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/cb/18326d2d89ad3b0dd143da971e77afd1e6ca6674f1b1c3df4b6bec6279fc/id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658", size = 13611, upload-time = "2024-12-04T19:53:03.02Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "imagesize" -version = "1.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026, upload-time = "2022-07-01T12:21:05.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769, upload-time = "2022-07-01T12:21:02.467Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - -[[package]] -name = "importlib-resources" -version = "6.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "ipykernel" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "appnope", marker = "sys_platform == 'darwin'" }, - { name = "comm" }, - { name = "debugpy" }, - { name = "ipython" }, - { name = "jupyter-client" }, - { name = "jupyter-core" }, - { name = "matplotlib-inline" }, - { name = "nest-asyncio" }, - { name = "packaging" }, - { name = "psutil" }, - { name = "pyzmq" }, - { name = "tornado" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/a4/4948be6eb88628505b83a1f2f40d90254cab66abf2043b3c40fa07dfce0f/ipykernel-7.1.0.tar.gz", hash = "sha256:58a3fc88533d5930c3546dc7eac66c6d288acde4f801e2001e65edc5dc9cf0db", size = 174579, upload-time = "2025-10-27T09:46:39.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/17/20c2552266728ceba271967b87919664ecc0e33efca29c3efc6baf88c5f9/ipykernel-7.1.0-py3-none-any.whl", hash = "sha256:763b5ec6c5b7776f6a8d7ce09b267693b4e5ce75cb50ae696aaefb3c85e1ea4c", size = 117968, upload-time = "2025-10-27T09:46:37.805Z" }, -] - -[[package]] -name = "ipython" -version = "9.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "decorator" }, - { name = "ipython-pygments-lexers" }, - { name = "jedi" }, - { name = "matplotlib-inline" }, - { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit" }, - { name = "pygments" }, - { name = "stack-data" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a6/60/2111715ea11f39b1535bed6024b7dec7918b71e5e5d30855a5b503056b50/ipython-9.10.0.tar.gz", hash = "sha256:cd9e656be97618a0676d058134cd44e6dc7012c0e5cb36a9ce96a8c904adaf77", size = 4426526, upload-time = "2026-02-02T10:00:33.594Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/aa/898dec789a05731cd5a9f50605b7b44a72bd198fd0d4528e11fc610177cc/ipython-9.10.0-py3-none-any.whl", hash = "sha256:c6ab68cc23bba8c7e18e9b932797014cc61ea7fd6f19de180ab9ba73e65ee58d", size = 622774, upload-time = "2026-02-02T10:00:31.503Z" }, -] - -[[package]] -name = "ipython-pygments-lexers" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, -] - -[[package]] -name = "jaraco-context" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" }, -] - -[[package]] -name = "jaraco-functools" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, -] - -[[package]] -name = "jedi" -version = "0.19.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "parso" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, -] - -[[package]] -name = "jiter" -version = "0.13.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/5e/4ec91646aee381d01cdb9974e30882c9cd3b8c5d1079d6b5ff4af522439a/jiter-0.13.0.tar.gz", hash = "sha256:f2839f9c2c7e2dffc1bc5929a510e14ce0a946be9365fd1219e7ef342dae14f4", size = 164847, upload-time = "2026-02-02T12:37:56.441Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/30/7687e4f87086829955013ca12a9233523349767f69653ebc27036313def9/jiter-0.13.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0a2bd69fc1d902e89925fc34d1da51b2128019423d7b339a45d9e99c894e0663", size = 307958, upload-time = "2026-02-02T12:35:57.165Z" }, - { url = "https://files.pythonhosted.org/packages/c3/27/e57f9a783246ed95481e6749cc5002a8a767a73177a83c63ea71f0528b90/jiter-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f917a04240ef31898182f76a332f508f2cc4b57d2b4d7ad2dbfebbfe167eb505", size = 318597, upload-time = "2026-02-02T12:35:58.591Z" }, - { url = "https://files.pythonhosted.org/packages/cf/52/e5719a60ac5d4d7c5995461a94ad5ef962a37c8bf5b088390e6fad59b2ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e2b199f446d3e82246b4fd9236d7cb502dc2222b18698ba0d986d2fecc6152", size = 348821, upload-time = "2026-02-02T12:36:00.093Z" }, - { url = "https://files.pythonhosted.org/packages/61/db/c1efc32b8ba4c740ab3fc2d037d8753f67685f475e26b9d6536a4322bcdd/jiter-0.13.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04670992b576fa65bd056dbac0c39fe8bd67681c380cb2b48efa885711d9d726", size = 364163, upload-time = "2026-02-02T12:36:01.937Z" }, - { url = "https://files.pythonhosted.org/packages/55/8a/fb75556236047c8806995671a18e4a0ad646ed255276f51a20f32dceaeec/jiter-0.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a1aff1fbdb803a376d4d22a8f63f8e7ccbce0b4890c26cc7af9e501ab339ef0", size = 483709, upload-time = "2026-02-02T12:36:03.41Z" }, - { url = "https://files.pythonhosted.org/packages/7e/16/43512e6ee863875693a8e6f6d532e19d650779d6ba9a81593ae40a9088ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b3fb8c2053acaef8580809ac1d1f7481a0a0bdc012fd7f5d8b18fb696a5a089", size = 370480, upload-time = "2026-02-02T12:36:04.791Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4c/09b93e30e984a187bc8aaa3510e1ec8dcbdcd71ca05d2f56aac0492453aa/jiter-0.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdaba7d87e66f26a2c45d8cbadcbfc4bf7884182317907baf39cfe9775bb4d93", size = 360735, upload-time = "2026-02-02T12:36:06.994Z" }, - { url = "https://files.pythonhosted.org/packages/1a/1b/46c5e349019874ec5dfa508c14c37e29864ea108d376ae26d90bee238cd7/jiter-0.13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b88d649135aca526da172e48083da915ec086b54e8e73a425ba50999468cc08", size = 391814, upload-time = "2026-02-02T12:36:08.368Z" }, - { url = "https://files.pythonhosted.org/packages/15/9e/26184760e85baee7162ad37b7912797d2077718476bf91517641c92b3639/jiter-0.13.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e404ea551d35438013c64b4f357b0474c7abf9f781c06d44fcaf7a14c69ff9e2", size = 513990, upload-time = "2026-02-02T12:36:09.993Z" }, - { url = "https://files.pythonhosted.org/packages/e9/34/2c9355247d6debad57a0a15e76ab1566ab799388042743656e566b3b7de1/jiter-0.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f4748aad1b4a93c8bdd70f604d0f748cdc0e8744c5547798acfa52f10e79228", size = 548021, upload-time = "2026-02-02T12:36:11.376Z" }, - { url = "https://files.pythonhosted.org/packages/ac/4a/9f2c23255d04a834398b9c2e0e665382116911dc4d06b795710503cdad25/jiter-0.13.0-cp312-cp312-win32.whl", hash = "sha256:0bf670e3b1445fc4d31612199f1744f67f889ee1bbae703c4b54dc097e5dd394", size = 203024, upload-time = "2026-02-02T12:36:12.682Z" }, - { url = "https://files.pythonhosted.org/packages/09/ee/f0ae675a957ae5a8f160be3e87acea6b11dc7b89f6b7ab057e77b2d2b13a/jiter-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:15db60e121e11fe186c0b15236bd5d18381b9ddacdcf4e659feb96fc6c969c92", size = 205424, upload-time = "2026-02-02T12:36:13.93Z" }, - { url = "https://files.pythonhosted.org/packages/1b/02/ae611edf913d3cbf02c97cdb90374af2082c48d7190d74c1111dde08bcdd/jiter-0.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:41f92313d17989102f3cb5dd533a02787cdb99454d494344b0361355da52fcb9", size = 186818, upload-time = "2026-02-02T12:36:15.308Z" }, - { url = "https://files.pythonhosted.org/packages/91/9c/7ee5a6ff4b9991e1a45263bfc46731634c4a2bde27dfda6c8251df2d958c/jiter-0.13.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1f8a55b848cbabf97d861495cd65f1e5c590246fabca8b48e1747c4dfc8f85bf", size = 306897, upload-time = "2026-02-02T12:36:16.748Z" }, - { url = "https://files.pythonhosted.org/packages/7c/02/be5b870d1d2be5dd6a91bdfb90f248fbb7dcbd21338f092c6b89817c3dbf/jiter-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f556aa591c00f2c45eb1b89f68f52441a016034d18b65da60e2d2875bbbf344a", size = 317507, upload-time = "2026-02-02T12:36:18.351Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/b25d2ec333615f5f284f3a4024f7ce68cfa0604c322c6808b2344c7f5d2b/jiter-0.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7e1d61da332ec412350463891923f960c3073cf1aae93b538f0bb4c8cd46efb", size = 350560, upload-time = "2026-02-02T12:36:19.746Z" }, - { url = "https://files.pythonhosted.org/packages/be/ec/74dcb99fef0aca9fbe56b303bf79f6bd839010cb18ad41000bf6cc71eec0/jiter-0.13.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3097d665a27bc96fd9bbf7f86178037db139f319f785e4757ce7ccbf390db6c2", size = 363232, upload-time = "2026-02-02T12:36:21.243Z" }, - { url = "https://files.pythonhosted.org/packages/1b/37/f17375e0bb2f6a812d4dd92d7616e41917f740f3e71343627da9db2824ce/jiter-0.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d01ecc3a8cbdb6f25a37bd500510550b64ddf9f7d64a107d92f3ccb25035d0f", size = 483727, upload-time = "2026-02-02T12:36:22.688Z" }, - { url = "https://files.pythonhosted.org/packages/77/d2/a71160a5ae1a1e66c1395b37ef77da67513b0adba73b993a27fbe47eb048/jiter-0.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed9bbc30f5d60a3bdf63ae76beb3f9db280d7f195dfcfa61af792d6ce912d159", size = 370799, upload-time = "2026-02-02T12:36:24.106Z" }, - { url = "https://files.pythonhosted.org/packages/01/99/ed5e478ff0eb4e8aa5fd998f9d69603c9fd3f32de3bd16c2b1194f68361c/jiter-0.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fbafb6e88256f4454de33c1f40203d09fc33ed19162a68b3b257b29ca7f663", size = 359120, upload-time = "2026-02-02T12:36:25.519Z" }, - { url = "https://files.pythonhosted.org/packages/16/be/7ffd08203277a813f732ba897352797fa9493faf8dc7995b31f3d9cb9488/jiter-0.13.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5467696f6b827f1116556cb0db620440380434591e93ecee7fd14d1a491b6daa", size = 390664, upload-time = "2026-02-02T12:36:26.866Z" }, - { url = "https://files.pythonhosted.org/packages/d1/84/e0787856196d6d346264d6dcccb01f741e5f0bd014c1d9a2ebe149caf4f3/jiter-0.13.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2d08c9475d48b92892583df9da592a0e2ac49bcd41fae1fec4f39ba6cf107820", size = 513543, upload-time = "2026-02-02T12:36:28.217Z" }, - { url = "https://files.pythonhosted.org/packages/65/50/ecbd258181c4313cf79bca6c88fb63207d04d5bf5e4f65174114d072aa55/jiter-0.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:aed40e099404721d7fcaf5b89bd3b4568a4666358bcac7b6b15c09fb6252ab68", size = 547262, upload-time = "2026-02-02T12:36:29.678Z" }, - { url = "https://files.pythonhosted.org/packages/27/da/68f38d12e7111d2016cd198161b36e1f042bd115c169255bcb7ec823a3bf/jiter-0.13.0-cp313-cp313-win32.whl", hash = "sha256:36ebfbcffafb146d0e6ffb3e74d51e03d9c35ce7c625c8066cdbfc7b953bdc72", size = 200630, upload-time = "2026-02-02T12:36:31.808Z" }, - { url = "https://files.pythonhosted.org/packages/25/65/3bd1a972c9a08ecd22eb3b08a95d1941ebe6938aea620c246cf426ae09c2/jiter-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:8d76029f077379374cf0dbc78dbe45b38dec4a2eb78b08b5194ce836b2517afc", size = 202602, upload-time = "2026-02-02T12:36:33.679Z" }, - { url = "https://files.pythonhosted.org/packages/15/fe/13bd3678a311aa67686bb303654792c48206a112068f8b0b21426eb6851e/jiter-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:bb7613e1a427cfcb6ea4544f9ac566b93d5bf67e0d48c787eca673ff9c9dff2b", size = 185939, upload-time = "2026-02-02T12:36:35.065Z" }, - { url = "https://files.pythonhosted.org/packages/49/19/a929ec002ad3228bc97ca01dbb14f7632fffdc84a95ec92ceaf4145688ae/jiter-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fa476ab5dd49f3bf3a168e05f89358c75a17608dbabb080ef65f96b27c19ab10", size = 316616, upload-time = "2026-02-02T12:36:36.579Z" }, - { url = "https://files.pythonhosted.org/packages/52/56/d19a9a194afa37c1728831e5fb81b7722c3de18a3109e8f282bfc23e587a/jiter-0.13.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade8cb6ff5632a62b7dbd4757d8c5573f7a2e9ae285d6b5b841707d8363205ef", size = 346850, upload-time = "2026-02-02T12:36:38.058Z" }, - { url = "https://files.pythonhosted.org/packages/36/4a/94e831c6bf287754a8a019cb966ed39ff8be6ab78cadecf08df3bb02d505/jiter-0.13.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9950290340acc1adaded363edd94baebcee7dabdfa8bee4790794cd5cfad2af6", size = 358551, upload-time = "2026-02-02T12:36:39.417Z" }, - { url = "https://files.pythonhosted.org/packages/a2/ec/a4c72c822695fa80e55d2b4142b73f0012035d9fcf90eccc56bc060db37c/jiter-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2b4972c6df33731aac0742b64fd0d18e0a69bc7d6e03108ce7d40c85fd9e3e6d", size = 201950, upload-time = "2026-02-02T12:36:40.791Z" }, - { url = "https://files.pythonhosted.org/packages/b6/00/393553ec27b824fbc29047e9c7cd4a3951d7fbe4a76743f17e44034fa4e4/jiter-0.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:701a1e77d1e593c1b435315ff625fd071f0998c5f02792038a5ca98899261b7d", size = 185852, upload-time = "2026-02-02T12:36:42.077Z" }, - { url = "https://files.pythonhosted.org/packages/6e/f5/f1997e987211f6f9bd71b8083047b316208b4aca0b529bb5f8c96c89ef3e/jiter-0.13.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:cc5223ab19fe25e2f0bf2643204ad7318896fe3729bf12fde41b77bfc4fafff0", size = 308804, upload-time = "2026-02-02T12:36:43.496Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8f/5482a7677731fd44881f0204981ce2d7175db271f82cba2085dd2212e095/jiter-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9776ebe51713acf438fd9b4405fcd86893ae5d03487546dae7f34993217f8a91", size = 318787, upload-time = "2026-02-02T12:36:45.071Z" }, - { url = "https://files.pythonhosted.org/packages/f3/b9/7257ac59778f1cd025b26a23c5520a36a424f7f1b068f2442a5b499b7464/jiter-0.13.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:879e768938e7b49b5e90b7e3fecc0dbec01b8cb89595861fb39a8967c5220d09", size = 353880, upload-time = "2026-02-02T12:36:47.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/87/719eec4a3f0841dad99e3d3604ee4cba36af4419a76f3cb0b8e2e691ad67/jiter-0.13.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:682161a67adea11e3aae9038c06c8b4a9a71023228767477d683f69903ebc607", size = 366702, upload-time = "2026-02-02T12:36:48.871Z" }, - { url = "https://files.pythonhosted.org/packages/d2/65/415f0a75cf6921e43365a1bc227c565cb949caca8b7532776e430cbaa530/jiter-0.13.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a13b68cd1cd8cc9de8f244ebae18ccb3e4067ad205220ef324c39181e23bbf66", size = 486319, upload-time = "2026-02-02T12:36:53.006Z" }, - { url = "https://files.pythonhosted.org/packages/54/a2/9e12b48e82c6bbc6081fd81abf915e1443add1b13d8fc586e1d90bb02bb8/jiter-0.13.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87ce0f14c6c08892b610686ae8be350bf368467b6acd5085a5b65441e2bf36d2", size = 372289, upload-time = "2026-02-02T12:36:54.593Z" }, - { url = "https://files.pythonhosted.org/packages/4e/c1/e4693f107a1789a239c759a432e9afc592366f04e901470c2af89cfd28e1/jiter-0.13.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c365005b05505a90d1c47856420980d0237adf82f70c4aff7aebd3c1cc143ad", size = 360165, upload-time = "2026-02-02T12:36:56.112Z" }, - { url = "https://files.pythonhosted.org/packages/17/08/91b9ea976c1c758240614bd88442681a87672eebc3d9a6dde476874e706b/jiter-0.13.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1317fdffd16f5873e46ce27d0e0f7f4f90f0cdf1d86bf6abeaea9f63ca2c401d", size = 389634, upload-time = "2026-02-02T12:36:57.495Z" }, - { url = "https://files.pythonhosted.org/packages/18/23/58325ef99390d6d40427ed6005bf1ad54f2577866594bcf13ce55675f87d/jiter-0.13.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c05b450d37ba0c9e21c77fef1f205f56bcee2330bddca68d344baebfc55ae0df", size = 514933, upload-time = "2026-02-02T12:36:58.909Z" }, - { url = "https://files.pythonhosted.org/packages/5b/25/69f1120c7c395fd276c3996bb8adefa9c6b84c12bb7111e5c6ccdcd8526d/jiter-0.13.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:775e10de3849d0631a97c603f996f518159272db00fdda0a780f81752255ee9d", size = 548842, upload-time = "2026-02-02T12:37:00.433Z" }, - { url = "https://files.pythonhosted.org/packages/18/05/981c9669d86850c5fbb0d9e62bba144787f9fba84546ba43d624ee27ef29/jiter-0.13.0-cp314-cp314-win32.whl", hash = "sha256:632bf7c1d28421c00dd8bbb8a3bac5663e1f57d5cd5ed962bce3c73bf62608e6", size = 202108, upload-time = "2026-02-02T12:37:01.718Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/cdcf54dd0b0341db7d25413229888a346c7130bd20820530905fdb65727b/jiter-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:f22ef501c3f87ede88f23f9b11e608581c14f04db59b6a801f354397ae13739f", size = 204027, upload-time = "2026-02-02T12:37:03.075Z" }, - { url = "https://files.pythonhosted.org/packages/fb/f9/724bcaaab7a3cd727031fe4f6995cb86c4bd344909177c186699c8dec51a/jiter-0.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:07b75fe09a4ee8e0c606200622e571e44943f47254f95e2436c8bdcaceb36d7d", size = 187199, upload-time = "2026-02-02T12:37:04.414Z" }, - { url = "https://files.pythonhosted.org/packages/62/92/1661d8b9fd6a3d7a2d89831db26fe3c1509a287d83ad7838831c7b7a5c7e/jiter-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:964538479359059a35fb400e769295d4b315ae61e4105396d355a12f7fef09f0", size = 318423, upload-time = "2026-02-02T12:37:05.806Z" }, - { url = "https://files.pythonhosted.org/packages/4f/3b/f77d342a54d4ebcd128e520fc58ec2f5b30a423b0fd26acdfc0c6fef8e26/jiter-0.13.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e104da1db1c0991b3eaed391ccd650ae8d947eab1480c733e5a3fb28d4313e40", size = 351438, upload-time = "2026-02-02T12:37:07.189Z" }, - { url = "https://files.pythonhosted.org/packages/76/b3/ba9a69f0e4209bd3331470c723c2f5509e6f0482e416b612431a5061ed71/jiter-0.13.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e3a5f0cde8ff433b8e88e41aa40131455420fb3649a3c7abdda6145f8cb7202", size = 364774, upload-time = "2026-02-02T12:37:08.579Z" }, - { url = "https://files.pythonhosted.org/packages/b3/16/6cdb31fa342932602458dbb631bfbd47f601e03d2e4950740e0b2100b570/jiter-0.13.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57aab48f40be1db920a582b30b116fe2435d184f77f0e4226f546794cedd9cf0", size = 487238, upload-time = "2026-02-02T12:37:10.066Z" }, - { url = "https://files.pythonhosted.org/packages/ed/b1/956cc7abaca8d95c13aa8d6c9b3f3797241c246cd6e792934cc4c8b250d2/jiter-0.13.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7772115877c53f62beeb8fd853cab692dbc04374ef623b30f997959a4c0e7e95", size = 372892, upload-time = "2026-02-02T12:37:11.656Z" }, - { url = "https://files.pythonhosted.org/packages/26/c4/97ecde8b1e74f67b8598c57c6fccf6df86ea7861ed29da84629cdbba76c4/jiter-0.13.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1211427574b17b633cfceba5040de8081e5abf114f7a7602f73d2e16f9fdaa59", size = 360309, upload-time = "2026-02-02T12:37:13.244Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d7/eabe3cf46715854ccc80be2cd78dd4c36aedeb30751dbf85a1d08c14373c/jiter-0.13.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7beae3a3d3b5212d3a55d2961db3c292e02e302feb43fce6a3f7a31b90ea6dfe", size = 389607, upload-time = "2026-02-02T12:37:14.881Z" }, - { url = "https://files.pythonhosted.org/packages/df/2d/03963fc0804e6109b82decfb9974eb92df3797fe7222428cae12f8ccaa0c/jiter-0.13.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e5562a0f0e90a6223b704163ea28e831bd3a9faa3512a711f031611e6b06c939", size = 514986, upload-time = "2026-02-02T12:37:16.326Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/8c83b45eb3eb1c1e18d841fe30b4b5bc5619d781267ca9bc03e005d8fd0a/jiter-0.13.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:6c26a424569a59140fb51160a56df13f438a2b0967365e987889186d5fc2f6f9", size = 548756, upload-time = "2026-02-02T12:37:17.736Z" }, - { url = "https://files.pythonhosted.org/packages/47/66/eea81dfff765ed66c68fd2ed8c96245109e13c896c2a5015c7839c92367e/jiter-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:24dc96eca9f84da4131cdf87a95e6ce36765c3b156fc9ae33280873b1c32d5f6", size = 201196, upload-time = "2026-02-02T12:37:19.101Z" }, - { url = "https://files.pythonhosted.org/packages/ff/32/4ac9c7a76402f8f00d00842a7f6b83b284d0cf7c1e9d4227bc95aa6d17fa/jiter-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0a8d76c7524087272c8ae913f5d9d608bd839154b62c4322ef65723d2e5bb0b8", size = 204215, upload-time = "2026-02-02T12:37:20.495Z" }, - { url = "https://files.pythonhosted.org/packages/f9/8e/7def204fea9f9be8b3c21a6f2dd6c020cf56c7d5ff753e0e23ed7f9ea57e/jiter-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2c26cf47e2cad140fa23b6d58d435a7c0161f5c514284802f25e87fddfe11024", size = 187152, upload-time = "2026-02-02T12:37:22.124Z" }, - { url = "https://files.pythonhosted.org/packages/80/60/e50fa45dd7e2eae049f0ce964663849e897300433921198aef94b6ffa23a/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:3d744a6061afba08dd7ae375dcde870cffb14429b7477e10f67e9e6d68772a0a", size = 305169, upload-time = "2026-02-02T12:37:50.376Z" }, - { url = "https://files.pythonhosted.org/packages/d2/73/a009f41c5eed71c49bec53036c4b33555afcdee70682a18c6f66e396c039/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:ff732bd0a0e778f43d5009840f20b935e79087b4dc65bd36f1cd0f9b04b8ff7f", size = 303808, upload-time = "2026-02-02T12:37:52.092Z" }, - { url = "https://files.pythonhosted.org/packages/c4/10/528b439290763bff3d939268085d03382471b442f212dca4ff5f12802d43/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab44b178f7981fcaea7e0a5df20e773c663d06ffda0198f1a524e91b2fde7e59", size = 337384, upload-time = "2026-02-02T12:37:53.582Z" }, - { url = "https://files.pythonhosted.org/packages/67/8a/a342b2f0251f3dac4ca17618265d93bf244a2a4d089126e81e4c1056ac50/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb00b6d26db67a05fe3e12c76edc75f32077fb51deed13822dc648fa373bc19", size = 343768, upload-time = "2026-02-02T12:37:55.055Z" }, -] - -[[package]] -name = "joblib" -version = "1.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, -] - -[[package]] -name = "jsonref" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[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.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "jupyter-client" -version = "8.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-core" }, - { name = "python-dateutil" }, - { name = "pyzmq" }, - { name = "tornado" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/05/e4/ba649102a3bc3fbca54e7239fb924fd434c766f855693d86de0b1f2bec81/jupyter_client-8.8.0.tar.gz", hash = "sha256:d556811419a4f2d96c869af34e854e3f059b7cc2d6d01a9cd9c85c267691be3e", size = 348020, upload-time = "2026-01-08T13:55:47.938Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a", size = 107371, upload-time = "2026-01-08T13:55:45.562Z" }, -] - -[[package]] -name = "jupyter-core" -version = "5.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "platformdirs" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814, upload-time = "2025-10-16T19:19:18.444Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, -] - -[[package]] -name = "keyring" -version = "25.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, -] - -[[package]] -name = "kubernetes" -version = "35.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "durationpy" }, - { name = "python-dateutil" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "requests-oauthlib" }, - { name = "six" }, - { name = "urllib3" }, - { name = "websocket-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2c/8f/85bf51ad4150f64e8c665daf0d9dfe9787ae92005efb9a4d1cba592bd79d/kubernetes-35.0.0.tar.gz", hash = "sha256:3d00d344944239821458b9efd484d6df9f011da367ecb155dadf9513f05f09ee", size = 1094642, upload-time = "2026-01-16T01:05:27.76Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/70/05b685ea2dffcb2adbf3cdcea5d8865b7bc66f67249084cf845012a0ff13/kubernetes-35.0.0-py2.py3-none-any.whl", hash = "sha256:39e2b33b46e5834ef6c3985ebfe2047ab39135d41de51ce7641a7ca5b372a13d", size = 2017602, upload-time = "2026-01-16T01:05:25.991Z" }, -] - -[[package]] -name = "lance-namespace" -version = "0.4.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "lance-namespace-urllib3-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b4/b5/0c3c55cf336b1e90392c2e24ac833551659e8bb3c61644b2d94825eb31bd/lance_namespace-0.4.5.tar.gz", hash = "sha256:0aee0abed3a1fa762c2955c7d12bb3004cea5c82ba28f6fcb9fe79d0cc19e317", size = 9827, upload-time = "2026-01-07T19:20:23.005Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/88/173687dad72baf819223e3b506898e386bc88c26ff8da5e8013291e02daf/lance_namespace-0.4.5-py3-none-any.whl", hash = "sha256:cd1a4f789de03ba23a0c16f100b1464cca572a5d04e428917a54d09db912d548", size = 11703, upload-time = "2026-01-07T19:20:25.394Z" }, -] - -[[package]] -name = "lance-namespace-urllib3-client" -version = "0.4.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dateutil" }, - { name = "typing-extensions" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/a9/4e527c2f05704565618b239b0965f829d1a194837f01234af3f8e2f33d92/lance_namespace_urllib3_client-0.4.5.tar.gz", hash = "sha256:184deda8cf8700926d994618187053c644eb1f2866a4479e7b80843cacc92b1c", size = 159726, upload-time = "2026-01-07T19:20:24.025Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/86/0adee7190408a28dcc5a0562c674537457e3de59ee51d1c724ecdc4a9930/lance_namespace_urllib3_client-0.4.5-py3-none-any.whl", hash = "sha256:2ee154d616ba4721f0bfdf043d33c4fef2e79d380653e2f263058ab00fb4adf4", size = 277969, upload-time = "2026-01-07T19:20:26.597Z" }, -] - -[[package]] -name = "lancedb" -version = "0.27.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "deprecation" }, - { name = "lance-namespace" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pyarrow" }, - { name = "pydantic" }, - { name = "tqdm" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/35/135ee7e3de58389074ad49b389adb8f431dc3f0034afbed1a9122c223c68/lancedb-0.27.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8aea87c3002850e98e4ac095c165dd819edd69f7c50e418f13f5917d1b9e0dcb", size = 43540316, upload-time = "2026-01-26T23:56:19.228Z" }, - { url = "https://files.pythonhosted.org/packages/16/cf/ea458fa50ef29c1a0653e1af6ea0599e532180267f49ca0bcf0049b0d8e3/lancedb-0.27.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:382666cddfb8b87d1efef4797bbc92cb1c3263b9b40894e5194ed5ed4e4486d4", size = 45409178, upload-time = "2026-01-27T03:25:09.932Z" }, - { url = "https://files.pythonhosted.org/packages/ee/cd/30714b878ec876eda3ce88637d6ef8da44484a065ec050dcfba3ad888465/lancedb-0.27.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7835e84d92631ddc7e269c8a18691ec16f24fe32f0fd14138d76951f530c28b9", size = 48484253, upload-time = "2026-01-27T03:28:16.048Z" }, - { url = "https://files.pythonhosted.org/packages/69/c2/19c1b8b7b36a0445e31fa532619bb75c4e76a91fff0514439dea2c4194d6/lancedb-0.27.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5996f7e36ae4cf580693fae33f560a21f29640b1ae0e923dcd8efea65ee8a78e", size = 45427415, upload-time = "2026-01-27T03:23:26.822Z" }, - { url = "https://files.pythonhosted.org/packages/eb/f1/794e9bc8d2adc9130c55695979afb66b0121c9d2abacdd19ce112e201879/lancedb-0.27.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:37e80565729555f6fc390a623da4f26392c463ba35e7634b2e706c1f9ac77e47", size = 48531937, upload-time = "2026-01-27T03:27:57.455Z" }, - { url = "https://files.pythonhosted.org/packages/3d/96/fa3cb37a6ffe7b81073d8c74f7cb95204d0922ac1668b264685aa34add20/lancedb-0.27.1-cp39-abi3-win_amd64.whl", hash = "sha256:f2150a66758ce6fe3cff226ac1ffcac2d5f5e2c9b35bc4c2d5923abcebef98cc", size = 53374010, upload-time = "2026-01-27T03:57:13.434Z" }, -] - -[[package]] -name = "librt" -version = "0.7.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/24/5f3646ff414285e0f7708fa4e946b9bf538345a41d1c375c439467721a5e/librt-0.7.8.tar.gz", hash = "sha256:1a4ede613941d9c3470b0368be851df6bb78ab218635512d0370b27a277a0862", size = 148323, upload-time = "2026-01-14T12:56:16.876Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/04/79d8fcb43cae376c7adbab7b2b9f65e48432c9eced62ac96703bcc16e09b/librt-0.7.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b6943885b2d49c48d0cff23b16be830ba46b0152d98f62de49e735c6e655a63", size = 57472, upload-time = "2026-01-14T12:55:08.528Z" }, - { url = "https://files.pythonhosted.org/packages/b4/ba/60b96e93043d3d659da91752689023a73981336446ae82078cddf706249e/librt-0.7.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46ef1f4b9b6cc364b11eea0ecc0897314447a66029ee1e55859acb3dd8757c93", size = 58986, upload-time = "2026-01-14T12:55:09.466Z" }, - { url = "https://files.pythonhosted.org/packages/7c/26/5215e4cdcc26e7be7eee21955a7e13cbf1f6d7d7311461a6014544596fac/librt-0.7.8-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:907ad09cfab21e3c86e8f1f87858f7049d1097f77196959c033612f532b4e592", size = 168422, upload-time = "2026-01-14T12:55:10.499Z" }, - { url = "https://files.pythonhosted.org/packages/0f/84/e8d1bc86fa0159bfc24f3d798d92cafd3897e84c7fea7fe61b3220915d76/librt-0.7.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2991b6c3775383752b3ca0204842743256f3ad3deeb1d0adc227d56b78a9a850", size = 177478, upload-time = "2026-01-14T12:55:11.577Z" }, - { url = "https://files.pythonhosted.org/packages/57/11/d0268c4b94717a18aa91df1100e767b010f87b7ae444dafaa5a2d80f33a6/librt-0.7.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03679b9856932b8c8f674e87aa3c55ea11c9274301f76ae8dc4d281bda55cf62", size = 192439, upload-time = "2026-01-14T12:55:12.7Z" }, - { url = "https://files.pythonhosted.org/packages/8d/56/1e8e833b95fe684f80f8894ae4d8b7d36acc9203e60478fcae599120a975/librt-0.7.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3968762fec1b2ad34ce57458b6de25dbb4142713e9ca6279a0d352fa4e9f452b", size = 191483, upload-time = "2026-01-14T12:55:13.838Z" }, - { url = "https://files.pythonhosted.org/packages/17/48/f11cf28a2cb6c31f282009e2208312aa84a5ee2732859f7856ee306176d5/librt-0.7.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bb7a7807523a31f03061288cc4ffc065d684c39db7644c676b47d89553c0d714", size = 185376, upload-time = "2026-01-14T12:55:15.017Z" }, - { url = "https://files.pythonhosted.org/packages/b8/6a/d7c116c6da561b9155b184354a60a3d5cdbf08fc7f3678d09c95679d13d9/librt-0.7.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad64a14b1e56e702e19b24aae108f18ad1bf7777f3af5fcd39f87d0c5a814449", size = 206234, upload-time = "2026-01-14T12:55:16.571Z" }, - { url = "https://files.pythonhosted.org/packages/61/de/1975200bb0285fc921c5981d9978ce6ce11ae6d797df815add94a5a848a3/librt-0.7.8-cp312-cp312-win32.whl", hash = "sha256:0241a6ed65e6666236ea78203a73d800dbed896cf12ae25d026d75dc1fcd1dac", size = 44057, upload-time = "2026-01-14T12:55:18.077Z" }, - { url = "https://files.pythonhosted.org/packages/8e/cd/724f2d0b3461426730d4877754b65d39f06a41ac9d0a92d5c6840f72b9ae/librt-0.7.8-cp312-cp312-win_amd64.whl", hash = "sha256:6db5faf064b5bab9675c32a873436b31e01d66ca6984c6f7f92621656033a708", size = 50293, upload-time = "2026-01-14T12:55:19.179Z" }, - { url = "https://files.pythonhosted.org/packages/bd/cf/7e899acd9ee5727ad8160fdcc9994954e79fab371c66535c60e13b968ffc/librt-0.7.8-cp312-cp312-win_arm64.whl", hash = "sha256:57175aa93f804d2c08d2edb7213e09276bd49097611aefc37e3fa38d1fb99ad0", size = 43574, upload-time = "2026-01-14T12:55:20.185Z" }, - { url = "https://files.pythonhosted.org/packages/a1/fe/b1f9de2829cf7fc7649c1dcd202cfd873837c5cc2fc9e526b0e7f716c3d2/librt-0.7.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4c3995abbbb60b3c129490fa985dfe6cac11d88fc3c36eeb4fb1449efbbb04fc", size = 57500, upload-time = "2026-01-14T12:55:21.219Z" }, - { url = "https://files.pythonhosted.org/packages/eb/d4/4a60fbe2e53b825f5d9a77325071d61cd8af8506255067bf0c8527530745/librt-0.7.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44e0c2cbc9bebd074cf2cdbe472ca185e824be4e74b1c63a8e934cea674bebf2", size = 59019, upload-time = "2026-01-14T12:55:22.256Z" }, - { url = "https://files.pythonhosted.org/packages/6a/37/61ff80341ba5159afa524445f2d984c30e2821f31f7c73cf166dcafa5564/librt-0.7.8-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d2f1e492cae964b3463a03dc77a7fe8742f7855d7258c7643f0ee32b6651dd3", size = 169015, upload-time = "2026-01-14T12:55:23.24Z" }, - { url = "https://files.pythonhosted.org/packages/1c/86/13d4f2d6a93f181ebf2fc953868826653ede494559da8268023fe567fca3/librt-0.7.8-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:451e7ffcef8f785831fdb791bd69211f47e95dc4c6ddff68e589058806f044c6", size = 178161, upload-time = "2026-01-14T12:55:24.826Z" }, - { url = "https://files.pythonhosted.org/packages/88/26/e24ef01305954fc4d771f1f09f3dd682f9eb610e1bec188ffb719374d26e/librt-0.7.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3469e1af9f1380e093ae06bedcbdd11e407ac0b303a56bbe9afb1d6824d4982d", size = 193015, upload-time = "2026-01-14T12:55:26.04Z" }, - { url = "https://files.pythonhosted.org/packages/88/a0/92b6bd060e720d7a31ed474d046a69bd55334ec05e9c446d228c4b806ae3/librt-0.7.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f11b300027ce19a34f6d24ebb0a25fd0e24a9d53353225a5c1e6cadbf2916b2e", size = 192038, upload-time = "2026-01-14T12:55:27.208Z" }, - { url = "https://files.pythonhosted.org/packages/06/bb/6f4c650253704279c3a214dad188101d1b5ea23be0606628bc6739456624/librt-0.7.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4adc73614f0d3c97874f02f2c7fd2a27854e7e24ad532ea6b965459c5b757eca", size = 186006, upload-time = "2026-01-14T12:55:28.594Z" }, - { url = "https://files.pythonhosted.org/packages/dc/00/1c409618248d43240cadf45f3efb866837fa77e9a12a71481912135eb481/librt-0.7.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:60c299e555f87e4c01b2eca085dfccda1dde87f5a604bb45c2906b8305819a93", size = 206888, upload-time = "2026-01-14T12:55:30.214Z" }, - { url = "https://files.pythonhosted.org/packages/d9/83/b2cfe8e76ff5c1c77f8a53da3d5de62d04b5ebf7cf913e37f8bca43b5d07/librt-0.7.8-cp313-cp313-win32.whl", hash = "sha256:b09c52ed43a461994716082ee7d87618096851319bf695d57ec123f2ab708951", size = 44126, upload-time = "2026-01-14T12:55:31.44Z" }, - { url = "https://files.pythonhosted.org/packages/a9/0b/c59d45de56a51bd2d3a401fc63449c0ac163e4ef7f523ea8b0c0dee86ec5/librt-0.7.8-cp313-cp313-win_amd64.whl", hash = "sha256:f8f4a901a3fa28969d6e4519deceab56c55a09d691ea7b12ca830e2fa3461e34", size = 50262, upload-time = "2026-01-14T12:55:33.01Z" }, - { url = "https://files.pythonhosted.org/packages/fc/b9/973455cec0a1ec592395250c474164c4a58ebf3e0651ee920fef1a2623f1/librt-0.7.8-cp313-cp313-win_arm64.whl", hash = "sha256:43d4e71b50763fcdcf64725ac680d8cfa1706c928b844794a7aa0fa9ac8e5f09", size = 43600, upload-time = "2026-01-14T12:55:34.054Z" }, - { url = "https://files.pythonhosted.org/packages/1a/73/fa8814c6ce2d49c3827829cadaa1589b0bf4391660bd4510899393a23ebc/librt-0.7.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:be927c3c94c74b05128089a955fba86501c3b544d1d300282cc1b4bd370cb418", size = 57049, upload-time = "2026-01-14T12:55:35.056Z" }, - { url = "https://files.pythonhosted.org/packages/53/fe/f6c70956da23ea235fd2e3cc16f4f0b4ebdfd72252b02d1164dd58b4e6c3/librt-0.7.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7b0803e9008c62a7ef79058233db7ff6f37a9933b8f2573c05b07ddafa226611", size = 58689, upload-time = "2026-01-14T12:55:36.078Z" }, - { url = "https://files.pythonhosted.org/packages/1f/4d/7a2481444ac5fba63050d9abe823e6bc16896f575bfc9c1e5068d516cdce/librt-0.7.8-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:79feb4d00b2a4e0e05c9c56df707934f41fcb5fe53fd9efb7549068d0495b758", size = 166808, upload-time = "2026-01-14T12:55:37.595Z" }, - { url = "https://files.pythonhosted.org/packages/ac/3c/10901d9e18639f8953f57c8986796cfbf4c1c514844a41c9197cf87cb707/librt-0.7.8-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9122094e3f24aa759c38f46bd8863433820654927370250f460ae75488b66ea", size = 175614, upload-time = "2026-01-14T12:55:38.756Z" }, - { url = "https://files.pythonhosted.org/packages/db/01/5cbdde0951a5090a80e5ba44e6357d375048123c572a23eecfb9326993a7/librt-0.7.8-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e03bea66af33c95ce3addf87a9bf1fcad8d33e757bc479957ddbc0e4f7207ac", size = 189955, upload-time = "2026-01-14T12:55:39.939Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b4/e80528d2f4b7eaf1d437fcbd6fc6ba4cbeb3e2a0cb9ed5a79f47c7318706/librt-0.7.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f1ade7f31675db00b514b98f9ab9a7698c7282dad4be7492589109471852d398", size = 189370, upload-time = "2026-01-14T12:55:41.057Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ab/938368f8ce31a9787ecd4becb1e795954782e4312095daf8fd22420227c8/librt-0.7.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a14229ac62adcf1b90a15992f1ab9c69ae8b99ffb23cb64a90878a6e8a2f5b81", size = 183224, upload-time = "2026-01-14T12:55:42.328Z" }, - { url = "https://files.pythonhosted.org/packages/3c/10/559c310e7a6e4014ac44867d359ef8238465fb499e7eb31b6bfe3e3f86f5/librt-0.7.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5bcaaf624fd24e6a0cb14beac37677f90793a96864c67c064a91458611446e83", size = 203541, upload-time = "2026-01-14T12:55:43.501Z" }, - { url = "https://files.pythonhosted.org/packages/f8/db/a0db7acdb6290c215f343835c6efda5b491bb05c3ddc675af558f50fdba3/librt-0.7.8-cp314-cp314-win32.whl", hash = "sha256:7aa7d5457b6c542ecaed79cec4ad98534373c9757383973e638ccced0f11f46d", size = 40657, upload-time = "2026-01-14T12:55:44.668Z" }, - { url = "https://files.pythonhosted.org/packages/72/e0/4f9bdc2a98a798511e81edcd6b54fe82767a715e05d1921115ac70717f6f/librt-0.7.8-cp314-cp314-win_amd64.whl", hash = "sha256:3d1322800771bee4a91f3b4bd4e49abc7d35e65166821086e5afd1e6c0d9be44", size = 46835, upload-time = "2026-01-14T12:55:45.655Z" }, - { url = "https://files.pythonhosted.org/packages/f9/3d/59c6402e3dec2719655a41ad027a7371f8e2334aa794ed11533ad5f34969/librt-0.7.8-cp314-cp314-win_arm64.whl", hash = "sha256:5363427bc6a8c3b1719f8f3845ea53553d301382928a86e8fab7984426949bce", size = 39885, upload-time = "2026-01-14T12:55:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/4e/9c/2481d80950b83085fb14ba3c595db56330d21bbc7d88a19f20165f3538db/librt-0.7.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ca916919793a77e4a98d4a1701e345d337ce53be4a16620f063191f7322ac80f", size = 59161, upload-time = "2026-01-14T12:55:48.45Z" }, - { url = "https://files.pythonhosted.org/packages/96/79/108df2cfc4e672336765d54e3ff887294c1cc36ea4335c73588875775527/librt-0.7.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:54feb7b4f2f6706bb82325e836a01be805770443e2400f706e824e91f6441dde", size = 61008, upload-time = "2026-01-14T12:55:49.527Z" }, - { url = "https://files.pythonhosted.org/packages/46/f2/30179898f9994a5637459d6e169b6abdc982012c0a4b2d4c26f50c06f911/librt-0.7.8-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:39a4c76fee41007070f872b648cc2f711f9abf9a13d0c7162478043377b52c8e", size = 187199, upload-time = "2026-01-14T12:55:50.587Z" }, - { url = "https://files.pythonhosted.org/packages/b4/da/f7563db55cebdc884f518ba3791ad033becc25ff68eb70902b1747dc0d70/librt-0.7.8-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac9c8a458245c7de80bc1b9765b177055efff5803f08e548dd4bb9ab9a8d789b", size = 198317, upload-time = "2026-01-14T12:55:51.991Z" }, - { url = "https://files.pythonhosted.org/packages/b3/6c/4289acf076ad371471fa86718c30ae353e690d3de6167f7db36f429272f1/librt-0.7.8-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b67aa7eff150f075fda09d11f6bfb26edffd300f6ab1666759547581e8f666", size = 210334, upload-time = "2026-01-14T12:55:53.682Z" }, - { url = "https://files.pythonhosted.org/packages/4a/7f/377521ac25b78ac0a5ff44127a0360ee6d5ddd3ce7327949876a30533daa/librt-0.7.8-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:535929b6eff670c593c34ff435d5440c3096f20fa72d63444608a5aef64dd581", size = 211031, upload-time = "2026-01-14T12:55:54.827Z" }, - { url = "https://files.pythonhosted.org/packages/c5/b1/e1e96c3e20b23d00cf90f4aad48f0deb4cdfec2f0ed8380d0d85acf98bbf/librt-0.7.8-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:63937bd0f4d1cb56653dc7ae900d6c52c41f0015e25aaf9902481ee79943b33a", size = 204581, upload-time = "2026-01-14T12:55:56.811Z" }, - { url = "https://files.pythonhosted.org/packages/43/71/0f5d010e92ed9747e14bef35e91b6580533510f1e36a8a09eb79ee70b2f0/librt-0.7.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf243da9e42d914036fd362ac3fa77d80a41cadcd11ad789b1b5eec4daaf67ca", size = 224731, upload-time = "2026-01-14T12:55:58.175Z" }, - { url = "https://files.pythonhosted.org/packages/22/f0/07fb6ab5c39a4ca9af3e37554f9d42f25c464829254d72e4ebbd81da351c/librt-0.7.8-cp314-cp314t-win32.whl", hash = "sha256:171ca3a0a06c643bd0a2f62a8944e1902c94aa8e5da4db1ea9a8daf872685365", size = 41173, upload-time = "2026-01-14T12:55:59.315Z" }, - { url = "https://files.pythonhosted.org/packages/24/d4/7e4be20993dc6a782639625bd2f97f3c66125c7aa80c82426956811cfccf/librt-0.7.8-cp314-cp314t-win_amd64.whl", hash = "sha256:445b7304145e24c60288a2f172b5ce2ca35c0f81605f5299f3fa567e189d2e32", size = 47668, upload-time = "2026-01-14T12:56:00.261Z" }, - { url = "https://files.pythonhosted.org/packages/fc/85/69f92b2a7b3c0f88ffe107c86b952b397004b5b8ea5a81da3d9c04c04422/librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06", size = 40550, upload-time = "2026-01-14T12:56:01.542Z" }, -] - -[[package]] -name = "litellm" -version = "1.81.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "click" }, - { name = "fastuuid" }, - { name = "httpx" }, - { name = "importlib-metadata" }, - { name = "jinja2" }, - { name = "jsonschema" }, - { name = "openai" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "tiktoken" }, - { name = "tokenizers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2e/f3/194a2dca6cb3eddb89f4bc2920cf5e27542256af907c23be13c61fe7e021/litellm-1.81.6.tar.gz", hash = "sha256:f02b503dfb7d66d1c939f82e4db21aeec1d6e2ed1fe3f5cd02aaec3f792bc4ae", size = 13878107, upload-time = "2026-02-01T04:02:27.36Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/05/3516cc7386b220d388aa0bd833308c677e94eceb82b2756dd95e06f6a13f/litellm-1.81.6-py3-none-any.whl", hash = "sha256:573206ba194d49a1691370ba33f781671609ac77c35347f8a0411d852cf6341a", size = 12224343, upload-time = "2026-02-01T04:02:23.704Z" }, -] - -[[package]] -name = "loguru" -version = "0.7.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "win32-setctime", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, -] - -[[package]] -name = "lupa" -version = "2.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/1c/191c3e6ec6502e3dbe25a53e27f69a5daeac3e56de1f73c0138224171ead/lupa-2.6.tar.gz", hash = "sha256:9a770a6e89576be3447668d7ced312cd6fd41d3c13c2462c9dc2c2ab570e45d9", size = 7240282, upload-time = "2025-10-24T07:20:29.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/86/ce243390535c39d53ea17ccf0240815e6e457e413e40428a658ea4ee4b8d/lupa-2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47ce718817ef1cc0c40d87c3d5ae56a800d61af00fbc0fad1ca9be12df2f3b56", size = 951707, upload-time = "2025-10-24T07:18:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/cedea5e6cbeb54396fdcc55f6b741696f3f036d23cfaf986d50d680446da/lupa-2.6-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:7aba985b15b101495aa4b07112cdc08baa0c545390d560ad5cfde2e9e34f4d58", size = 1916703, upload-time = "2025-10-24T07:18:05.6Z" }, - { url = "https://files.pythonhosted.org/packages/24/be/3d6b5f9a8588c01a4d88129284c726017b2089f3a3fd3ba8bd977292fea0/lupa-2.6-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:b766f62f95b2739f2248977d29b0722e589dcf4f0ccfa827ccbd29f0148bd2e5", size = 985152, upload-time = "2025-10-24T07:18:08.561Z" }, - { url = "https://files.pythonhosted.org/packages/eb/23/9f9a05beee5d5dce9deca4cb07c91c40a90541fc0a8e09db4ee670da550f/lupa-2.6-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:00a934c23331f94cb51760097ebfab14b005d55a6b30a2b480e3c53dd2fa290d", size = 1159599, upload-time = "2025-10-24T07:18:10.346Z" }, - { url = "https://files.pythonhosted.org/packages/40/4e/e7c0583083db9d7f1fd023800a9767d8e4391e8330d56c2373d890ac971b/lupa-2.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21de9f38bd475303e34a042b7081aabdf50bd9bafd36ce4faea2f90fd9f15c31", size = 1038686, upload-time = "2025-10-24T07:18:12.112Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/5a4f7d959d4feba5e203ff0c31889e74d1ca3153122be4a46dca7d92bf7c/lupa-2.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf3bda96d3fc41237e964a69c23647d50d4e28421111360274d4799832c560e9", size = 2071956, upload-time = "2025-10-24T07:18:14.572Z" }, - { url = "https://files.pythonhosted.org/packages/92/34/2f4f13ca65d01169b1720176aedc4af17bc19ee834598c7292db232cb6dc/lupa-2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a76ead245da54801a81053794aa3975f213221f6542d14ec4b859ee2e7e0323", size = 1057199, upload-time = "2025-10-24T07:18:16.379Z" }, - { url = "https://files.pythonhosted.org/packages/35/2a/5f7d2eebec6993b0dcd428e0184ad71afb06a45ba13e717f6501bfed1da3/lupa-2.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8dd0861741caa20886ddbda0a121d8e52fb9b5bb153d82fa9bba796962bf30e8", size = 1173693, upload-time = "2025-10-24T07:18:18.153Z" }, - { url = "https://files.pythonhosted.org/packages/e4/29/089b4d2f8e34417349af3904bb40bec40b65c8731f45e3fd8d497ca573e5/lupa-2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:239e63948b0b23023f81d9a19a395e768ed3da6a299f84e7963b8f813f6e3f9c", size = 2164394, upload-time = "2025-10-24T07:18:20.403Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1b/79c17b23c921f81468a111cad843b076a17ef4b684c4a8dff32a7969c3f0/lupa-2.6-cp312-cp312-win32.whl", hash = "sha256:325894e1099499e7a6f9c351147661a2011887603c71086d36fe0f964d52d1ce", size = 1420647, upload-time = "2025-10-24T07:18:23.368Z" }, - { url = "https://files.pythonhosted.org/packages/b8/15/5121e68aad3584e26e1425a5c9a79cd898f8a152292059e128c206ee817c/lupa-2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c735a1ce8ee60edb0fe71d665f1e6b7c55c6021f1d340eb8c865952c602cd36f", size = 1688529, upload-time = "2025-10-24T07:18:25.523Z" }, - { url = "https://files.pythonhosted.org/packages/28/1d/21176b682ca5469001199d8b95fa1737e29957a3d185186e7a8b55345f2e/lupa-2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:663a6e58a0f60e7d212017d6678639ac8df0119bc13c2145029dcba084391310", size = 947232, upload-time = "2025-10-24T07:18:27.878Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4c/d327befb684660ca13cf79cd1f1d604331808f9f1b6fb6bf57832f8edf80/lupa-2.6-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:d1f5afda5c20b1f3217a80e9bc1b77037f8a6eb11612fd3ada19065303c8f380", size = 1908625, upload-time = "2025-10-24T07:18:29.944Z" }, - { url = "https://files.pythonhosted.org/packages/66/8e/ad22b0a19454dfd08662237a84c792d6d420d36b061f239e084f29d1a4f3/lupa-2.6-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:26f2b3c085fe76e9119e48c1013c1cccdc1f51585d456858290475aa38e7089e", size = 981057, upload-time = "2025-10-24T07:18:31.553Z" }, - { url = "https://files.pythonhosted.org/packages/5c/48/74859073ab276bd0566c719f9ca0108b0cfc1956ca0d68678d117d47d155/lupa-2.6-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:60d2f902c7b96fb8ab98493dcff315e7bb4d0b44dc9dd76eb37de575025d5685", size = 1156227, upload-time = "2025-10-24T07:18:33.981Z" }, - { url = "https://files.pythonhosted.org/packages/09/6c/0e9ded061916877253c2266074060eb71ed99fb21d73c8c114a76725bce2/lupa-2.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a02d25dee3a3250967c36590128d9220ae02f2eda166a24279da0b481519cbff", size = 1035752, upload-time = "2025-10-24T07:18:36.32Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ef/f8c32e454ef9f3fe909f6c7d57a39f950996c37a3deb7b391fec7903dab7/lupa-2.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eae1ee16b886b8914ff292dbefbf2f48abfbdee94b33a88d1d5475e02423203", size = 2069009, upload-time = "2025-10-24T07:18:38.072Z" }, - { url = "https://files.pythonhosted.org/packages/53/dc/15b80c226a5225815a890ee1c11f07968e0aba7a852df41e8ae6fe285063/lupa-2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0edd5073a4ee74ab36f74fe61450148e6044f3952b8d21248581f3c5d1a58be", size = 1056301, upload-time = "2025-10-24T07:18:40.165Z" }, - { url = "https://files.pythonhosted.org/packages/31/14/2086c1425c985acfb30997a67e90c39457122df41324d3c179d6ee2292c6/lupa-2.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c53ee9f22a8a17e7d4266ad48e86f43771951797042dd51d1494aaa4f5f3f0a", size = 1170673, upload-time = "2025-10-24T07:18:42.426Z" }, - { url = "https://files.pythonhosted.org/packages/10/e5/b216c054cf86576c0191bf9a9f05de6f7e8e07164897d95eea0078dca9b2/lupa-2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de7c0f157a9064a400d828789191a96da7f4ce889969a588b87ec80de9b14772", size = 2162227, upload-time = "2025-10-24T07:18:46.112Z" }, - { url = "https://files.pythonhosted.org/packages/59/2f/33ecb5bedf4f3bc297ceacb7f016ff951331d352f58e7e791589609ea306/lupa-2.6-cp313-cp313-win32.whl", hash = "sha256:ee9523941ae0a87b5b703417720c5d78f72d2f5bc23883a2ea80a949a3ed9e75", size = 1419558, upload-time = "2025-10-24T07:18:48.371Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b4/55e885834c847ea610e111d87b9ed4768f0afdaeebc00cd46810f25029f6/lupa-2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b1335a5835b0a25ebdbc75cf0bda195e54d133e4d994877ef025e218c2e59db9", size = 1683424, upload-time = "2025-10-24T07:18:50.976Z" }, - { url = "https://files.pythonhosted.org/packages/66/9d/d9427394e54d22a35d1139ef12e845fd700d4872a67a34db32516170b746/lupa-2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dcb6d0a3264873e1653bc188499f48c1fb4b41a779e315eba45256cfe7bc33c1", size = 953818, upload-time = "2025-10-24T07:18:53.378Z" }, - { url = "https://files.pythonhosted.org/packages/10/41/27bbe81953fb2f9ecfced5d9c99f85b37964cfaf6aa8453bb11283983721/lupa-2.6-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:a37e01f2128f8c36106726cb9d360bac087d58c54b4522b033cc5691c584db18", size = 1915850, upload-time = "2025-10-24T07:18:55.259Z" }, - { url = "https://files.pythonhosted.org/packages/a3/98/f9ff60db84a75ba8725506bbf448fb085bc77868a021998ed2a66d920568/lupa-2.6-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:458bd7e9ff3c150b245b0fcfbb9bd2593d1152ea7f0a7b91c1d185846da033fe", size = 982344, upload-time = "2025-10-24T07:18:57.05Z" }, - { url = "https://files.pythonhosted.org/packages/41/f7/f39e0f1c055c3b887d86b404aaf0ca197b5edfd235a8b81b45b25bac7fc3/lupa-2.6-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:052ee82cac5206a02df77119c325339acbc09f5ce66967f66a2e12a0f3211cad", size = 1156543, upload-time = "2025-10-24T07:18:59.251Z" }, - { url = "https://files.pythonhosted.org/packages/9e/9c/59e6cffa0d672d662ae17bd7ac8ecd2c89c9449dee499e3eb13ca9cd10d9/lupa-2.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96594eca3c87dd07938009e95e591e43d554c1dbd0385be03c100367141db5a8", size = 1047974, upload-time = "2025-10-24T07:19:01.449Z" }, - { url = "https://files.pythonhosted.org/packages/23/c6/a04e9cef7c052717fcb28fb63b3824802488f688391895b618e39be0f684/lupa-2.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8faddd9d198688c8884091173a088a8e920ecc96cda2ffed576a23574c4b3f6", size = 2073458, upload-time = "2025-10-24T07:19:03.369Z" }, - { url = "https://files.pythonhosted.org/packages/e6/10/824173d10f38b51fc77785228f01411b6ca28826ce27404c7c912e0e442c/lupa-2.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:daebb3a6b58095c917e76ba727ab37b27477fb926957c825205fbda431552134", size = 1067683, upload-time = "2025-10-24T07:19:06.2Z" }, - { url = "https://files.pythonhosted.org/packages/b6/dc/9692fbcf3c924d9c4ece2d8d2f724451ac2e09af0bd2a782db1cef34e799/lupa-2.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f3154e68972befe0f81564e37d8142b5d5d79931a18309226a04ec92487d4ea3", size = 1171892, upload-time = "2025-10-24T07:19:08.544Z" }, - { url = "https://files.pythonhosted.org/packages/84/ff/e318b628d4643c278c96ab3ddea07fc36b075a57383c837f5b11e537ba9d/lupa-2.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e4dadf77b9fedc0bfa53417cc28dc2278a26d4cbd95c29f8927ad4d8fe0a7ef9", size = 2166641, upload-time = "2025-10-24T07:19:10.485Z" }, - { url = "https://files.pythonhosted.org/packages/12/f7/a6f9ec2806cf2d50826980cdb4b3cffc7691dc6f95e13cc728846d5cb793/lupa-2.6-cp314-cp314-win32.whl", hash = "sha256:cb34169c6fa3bab3e8ac58ca21b8a7102f6a94b6a5d08d3636312f3f02fafd8f", size = 1456857, upload-time = "2025-10-24T07:19:37.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/de/df71896f25bdc18360fdfa3b802cd7d57d7fede41a0e9724a4625b412c85/lupa-2.6-cp314-cp314-win_amd64.whl", hash = "sha256:b74f944fe46c421e25d0f8692aef1e842192f6f7f68034201382ac440ef9ea67", size = 1731191, upload-time = "2025-10-24T07:19:40.281Z" }, - { url = "https://files.pythonhosted.org/packages/47/3c/a1f23b01c54669465f5f4c4083107d496fbe6fb45998771420e9aadcf145/lupa-2.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0e21b716408a21ab65723f8841cf7f2f37a844b7a965eeabb785e27fca4099cf", size = 999343, upload-time = "2025-10-24T07:19:12.519Z" }, - { url = "https://files.pythonhosted.org/packages/c5/6d/501994291cb640bfa2ccf7f554be4e6914afa21c4026bd01bff9ca8aac57/lupa-2.6-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:589db872a141bfff828340079bbdf3e9a31f2689f4ca0d88f97d9e8c2eae6142", size = 2000730, upload-time = "2025-10-24T07:19:14.869Z" }, - { url = "https://files.pythonhosted.org/packages/53/a5/457ffb4f3f20469956c2d4c4842a7675e884efc895b2f23d126d23e126cc/lupa-2.6-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:cd852a91a4a9d4dcbb9a58100f820a75a425703ec3e3f049055f60b8533b7953", size = 1021553, upload-time = "2025-10-24T07:19:17.123Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/36bb5a5d0960f2a5c7c700e0819abb76fd9bf9c1d8a66e5106416d6e9b14/lupa-2.6-cp314-cp314t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:0334753be028358922415ca97a64a3048e4ed155413fc4eaf87dd0a7e2752983", size = 1133275, upload-time = "2025-10-24T07:19:20.51Z" }, - { url = "https://files.pythonhosted.org/packages/19/86/202ff4429f663013f37d2229f6176ca9f83678a50257d70f61a0a97281bf/lupa-2.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:661d895cd38c87658a34780fac54a690ec036ead743e41b74c3fb81a9e65a6aa", size = 1038441, upload-time = "2025-10-24T07:19:22.509Z" }, - { url = "https://files.pythonhosted.org/packages/a7/42/d8125f8e420714e5b52e9c08d88b5329dfb02dcca731b4f21faaee6cc5b5/lupa-2.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aa58454ccc13878cc177c62529a2056be734da16369e451987ff92784994ca7", size = 2058324, upload-time = "2025-10-24T07:19:24.979Z" }, - { url = "https://files.pythonhosted.org/packages/2b/2c/47bf8b84059876e877a339717ddb595a4a7b0e8740bacae78ba527562e1c/lupa-2.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1425017264e470c98022bba8cff5bd46d054a827f5df6b80274f9cc71dafd24f", size = 1060250, upload-time = "2025-10-24T07:19:27.262Z" }, - { url = "https://files.pythonhosted.org/packages/c2/06/d88add2b6406ca1bdec99d11a429222837ca6d03bea42ca75afa169a78cb/lupa-2.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:224af0532d216e3105f0a127410f12320f7c5f1aa0300bdf9646b8d9afb0048c", size = 1151126, upload-time = "2025-10-24T07:19:29.522Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a0/89e6a024c3b4485b89ef86881c9d55e097e7cb0bdb74efb746f2fa6a9a76/lupa-2.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9abb98d5a8fd27c8285302e82199f0e56e463066f88f619d6594a450bf269d80", size = 2153693, upload-time = "2025-10-24T07:19:31.379Z" }, - { url = "https://files.pythonhosted.org/packages/b6/36/a0f007dc58fc1bbf51fb85dcc82fcb1f21b8c4261361de7dab0e3d8521ef/lupa-2.6-cp314-cp314t-win32.whl", hash = "sha256:1849efeba7a8f6fb8aa2c13790bee988fd242ae404bd459509640eeea3d1e291", size = 1590104, upload-time = "2025-10-24T07:19:33.514Z" }, - { url = "https://files.pythonhosted.org/packages/7d/5e/db903ce9cf82c48d6b91bf6d63ae4c8d0d17958939a4e04ba6b9f38b8643/lupa-2.6-cp314-cp314t-win_amd64.whl", hash = "sha256:fc1498d1a4fc028bc521c26d0fad4ca00ed63b952e32fb95949bda76a04bad52", size = 1913818, upload-time = "2025-10-24T07:19:36.039Z" }, -] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, - { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, - { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, - { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, - { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, - { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, - { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, - { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, - { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, - { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, - { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, - { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, - { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, - { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, - { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, - { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, - { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, - { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, - { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, - { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, - { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, - { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, - { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, - { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, - { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, - { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, - { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, - { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, - { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, - { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, - { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, - { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, - { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, - { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, - { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, - { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, - { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, - { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, - { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, - { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, - { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, - { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, - { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, - { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, - { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, -] - -[[package]] -name = "matplotlib-inline" -version = "0.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, -] - -[[package]] -name = "mcp" -version = "1.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyjwt", extra = ["crypto"] }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, -] - -[[package]] -name = "mdit-py-plugins" -version = "0.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b2/fd/a756d36c0bfba5f6e39a1cdbdbfdd448dc02692467d83816dff4592a1ebc/mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6", size = 44655, upload-time = "2025-08-11T07:25:49.083Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f", size = 57205, upload-time = "2025-08-11T07:25:47.597Z" }, -] - -[[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 = "mmh3" -version = "5.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/af/f28c2c2f51f31abb4725f9a64bc7863d5f491f6539bd26aee2a1d21a649e/mmh3-5.2.0.tar.gz", hash = "sha256:1efc8fec8478e9243a78bb993422cf79f8ff85cb4cf6b79647480a31e0d950a8", size = 33582, upload-time = "2025-07-29T07:43:48.49Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/6a/d5aa7edb5c08e0bd24286c7d08341a0446f9a2fbbb97d96a8a6dd81935ee/mmh3-5.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:384eda9361a7bf83a85e09447e1feafe081034af9dd428893701b959230d84be", size = 56141, upload-time = "2025-07-29T07:42:13.456Z" }, - { url = "https://files.pythonhosted.org/packages/08/49/131d0fae6447bc4a7299ebdb1a6fb9d08c9f8dcf97d75ea93e8152ddf7ab/mmh3-5.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2c9da0d568569cc87315cb063486d761e38458b8ad513fedd3dc9263e1b81bcd", size = 40681, upload-time = "2025-07-29T07:42:14.306Z" }, - { url = "https://files.pythonhosted.org/packages/8f/6f/9221445a6bcc962b7f5ff3ba18ad55bba624bacdc7aa3fc0a518db7da8ec/mmh3-5.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86d1be5d63232e6eb93c50881aea55ff06eb86d8e08f9b5417c8c9b10db9db96", size = 40062, upload-time = "2025-07-29T07:42:15.08Z" }, - { url = "https://files.pythonhosted.org/packages/1e/d4/6bb2d0fef81401e0bb4c297d1eb568b767de4ce6fc00890bc14d7b51ecc4/mmh3-5.2.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bf7bee43e17e81671c447e9c83499f53d99bf440bc6d9dc26a841e21acfbe094", size = 97333, upload-time = "2025-07-29T07:42:16.436Z" }, - { url = "https://files.pythonhosted.org/packages/44/e0/ccf0daff8134efbb4fbc10a945ab53302e358c4b016ada9bf97a6bdd50c1/mmh3-5.2.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7aa18cdb58983ee660c9c400b46272e14fa253c675ed963d3812487f8ca42037", size = 103310, upload-time = "2025-07-29T07:42:17.796Z" }, - { url = "https://files.pythonhosted.org/packages/02/63/1965cb08a46533faca0e420e06aff8bbaf9690a6f0ac6ae6e5b2e4544687/mmh3-5.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9d032488fcec32d22be6542d1a836f00247f40f320844dbb361393b5b22773", size = 106178, upload-time = "2025-07-29T07:42:19.281Z" }, - { url = "https://files.pythonhosted.org/packages/c2/41/c883ad8e2c234013f27f92061200afc11554ea55edd1bcf5e1accd803a85/mmh3-5.2.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1861fb6b1d0453ed7293200139c0a9011eeb1376632e048e3766945b13313c5", size = 113035, upload-time = "2025-07-29T07:42:20.356Z" }, - { url = "https://files.pythonhosted.org/packages/df/b5/1ccade8b1fa625d634a18bab7bf08a87457e09d5ec8cf83ca07cbea9d400/mmh3-5.2.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:99bb6a4d809aa4e528ddfe2c85dd5239b78b9dd14be62cca0329db78505e7b50", size = 120784, upload-time = "2025-07-29T07:42:21.377Z" }, - { url = "https://files.pythonhosted.org/packages/77/1c/919d9171fcbdcdab242e06394464ccf546f7d0f3b31e0d1e3a630398782e/mmh3-5.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1f8d8b627799f4e2fcc7c034fed8f5f24dc7724ff52f69838a3d6d15f1ad4765", size = 99137, upload-time = "2025-07-29T07:42:22.344Z" }, - { url = "https://files.pythonhosted.org/packages/66/8a/1eebef5bd6633d36281d9fc83cf2e9ba1ba0e1a77dff92aacab83001cee4/mmh3-5.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b5995088dd7023d2d9f310a0c67de5a2b2e06a570ecfd00f9ff4ab94a67cde43", size = 98664, upload-time = "2025-07-29T07:42:23.269Z" }, - { url = "https://files.pythonhosted.org/packages/13/41/a5d981563e2ee682b21fb65e29cc0f517a6734a02b581359edd67f9d0360/mmh3-5.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1a5f4d2e59d6bba8ef01b013c472741835ad961e7c28f50c82b27c57748744a4", size = 106459, upload-time = "2025-07-29T07:42:24.238Z" }, - { url = "https://files.pythonhosted.org/packages/24/31/342494cd6ab792d81e083680875a2c50fa0c5df475ebf0b67784f13e4647/mmh3-5.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fd6e6c3d90660d085f7e73710eab6f5545d4854b81b0135a3526e797009dbda3", size = 110038, upload-time = "2025-07-29T07:42:25.629Z" }, - { url = "https://files.pythonhosted.org/packages/28/44/efda282170a46bb4f19c3e2b90536513b1d821c414c28469a227ca5a1789/mmh3-5.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c4a2f3d83879e3de2eb8cbf562e71563a8ed15ee9b9c2e77ca5d9f73072ac15c", size = 97545, upload-time = "2025-07-29T07:42:27.04Z" }, - { url = "https://files.pythonhosted.org/packages/68/8f/534ae319c6e05d714f437e7206f78c17e66daca88164dff70286b0e8ea0c/mmh3-5.2.0-cp312-cp312-win32.whl", hash = "sha256:2421b9d665a0b1ad724ec7332fb5a98d075f50bc51a6ff854f3a1882bd650d49", size = 40805, upload-time = "2025-07-29T07:42:28.032Z" }, - { url = "https://files.pythonhosted.org/packages/b8/f6/f6abdcfefcedab3c964868048cfe472764ed358c2bf6819a70dd4ed4ed3a/mmh3-5.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:72d80005b7634a3a2220f81fbeb94775ebd12794623bb2e1451701ea732b4aa3", size = 41597, upload-time = "2025-07-29T07:42:28.894Z" }, - { url = "https://files.pythonhosted.org/packages/15/fd/f7420e8cbce45c259c770cac5718badf907b302d3a99ec587ba5ce030237/mmh3-5.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:3d6bfd9662a20c054bc216f861fa330c2dac7c81e7fb8307b5e32ab5b9b4d2e0", size = 39350, upload-time = "2025-07-29T07:42:29.794Z" }, - { url = "https://files.pythonhosted.org/packages/d8/fa/27f6ab93995ef6ad9f940e96593c5dd24744d61a7389532b0fec03745607/mmh3-5.2.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:e79c00eba78f7258e5b354eccd4d7907d60317ced924ea4a5f2e9d83f5453065", size = 40874, upload-time = "2025-07-29T07:42:30.662Z" }, - { url = "https://files.pythonhosted.org/packages/11/9c/03d13bcb6a03438bc8cac3d2e50f80908d159b31a4367c2e1a7a077ded32/mmh3-5.2.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:956127e663d05edbeec54df38885d943dfa27406594c411139690485128525de", size = 42012, upload-time = "2025-07-29T07:42:31.539Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/0865d9765408a7d504f1789944e678f74e0888b96a766d578cb80b040999/mmh3-5.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:c3dca4cb5b946ee91b3d6bb700d137b1cd85c20827f89fdf9c16258253489044", size = 39197, upload-time = "2025-07-29T07:42:32.374Z" }, - { url = "https://files.pythonhosted.org/packages/3e/12/76c3207bd186f98b908b6706c2317abb73756d23a4e68ea2bc94825b9015/mmh3-5.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:e651e17bfde5840e9e4174b01e9e080ce49277b70d424308b36a7969d0d1af73", size = 39840, upload-time = "2025-07-29T07:42:33.227Z" }, - { url = "https://files.pythonhosted.org/packages/5d/0d/574b6cce5555c9f2b31ea189ad44986755eb14e8862db28c8b834b8b64dc/mmh3-5.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:9f64bf06f4bf623325fda3a6d02d36cd69199b9ace99b04bb2d7fd9f89688504", size = 40644, upload-time = "2025-07-29T07:42:34.099Z" }, - { url = "https://files.pythonhosted.org/packages/52/82/3731f8640b79c46707f53ed72034a58baad400be908c87b0088f1f89f986/mmh3-5.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ddc63328889bcaee77b743309e5c7d2d52cee0d7d577837c91b6e7cc9e755e0b", size = 56153, upload-time = "2025-07-29T07:42:35.031Z" }, - { url = "https://files.pythonhosted.org/packages/4f/34/e02dca1d4727fd9fdeaff9e2ad6983e1552804ce1d92cc796e5b052159bb/mmh3-5.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bb0fdc451fb6d86d81ab8f23d881b8d6e37fc373a2deae1c02d27002d2ad7a05", size = 40684, upload-time = "2025-07-29T07:42:35.914Z" }, - { url = "https://files.pythonhosted.org/packages/8f/36/3dee40767356e104967e6ed6d102ba47b0b1ce2a89432239b95a94de1b89/mmh3-5.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b29044e1ffdb84fe164d0a7ea05c7316afea93c00f8ed9449cf357c36fc4f814", size = 40057, upload-time = "2025-07-29T07:42:36.755Z" }, - { url = "https://files.pythonhosted.org/packages/31/58/228c402fccf76eb39a0a01b8fc470fecf21965584e66453b477050ee0e99/mmh3-5.2.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:58981d6ea9646dbbf9e59a30890cbf9f610df0e4a57dbfe09215116fd90b0093", size = 97344, upload-time = "2025-07-29T07:42:37.675Z" }, - { url = "https://files.pythonhosted.org/packages/34/82/fc5ce89006389a6426ef28e326fc065b0fbaaed230373b62d14c889f47ea/mmh3-5.2.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e5634565367b6d98dc4aa2983703526ef556b3688ba3065edb4b9b90ede1c54", size = 103325, upload-time = "2025-07-29T07:42:38.591Z" }, - { url = "https://files.pythonhosted.org/packages/09/8c/261e85777c6aee1ebd53f2f17e210e7481d5b0846cd0b4a5c45f1e3761b8/mmh3-5.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0271ac12415afd3171ab9a3c7cbfc71dee2c68760a7dc9d05bf8ed6ddfa3a7a", size = 106240, upload-time = "2025-07-29T07:42:39.563Z" }, - { url = "https://files.pythonhosted.org/packages/70/73/2f76b3ad8a3d431824e9934403df36c0ddacc7831acf82114bce3c4309c8/mmh3-5.2.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:45b590e31bc552c6f8e2150ff1ad0c28dd151e9f87589e7eaf508fbdd8e8e908", size = 113060, upload-time = "2025-07-29T07:42:40.585Z" }, - { url = "https://files.pythonhosted.org/packages/9f/b9/7ea61a34e90e50a79a9d87aa1c0b8139a7eaf4125782b34b7d7383472633/mmh3-5.2.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bdde97310d59604f2a9119322f61b31546748499a21b44f6715e8ced9308a6c5", size = 120781, upload-time = "2025-07-29T07:42:41.618Z" }, - { url = "https://files.pythonhosted.org/packages/0f/5b/ae1a717db98c7894a37aeedbd94b3f99e6472a836488f36b6849d003485b/mmh3-5.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc9c5f280438cf1c1a8f9abb87dc8ce9630a964120cfb5dd50d1e7ce79690c7a", size = 99174, upload-time = "2025-07-29T07:42:42.587Z" }, - { url = "https://files.pythonhosted.org/packages/e3/de/000cce1d799fceebb6d4487ae29175dd8e81b48e314cba7b4da90bcf55d7/mmh3-5.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c903e71fd8debb35ad2a4184c1316b3cb22f64ce517b4e6747f25b0a34e41266", size = 98734, upload-time = "2025-07-29T07:42:43.996Z" }, - { url = "https://files.pythonhosted.org/packages/79/19/0dc364391a792b72fbb22becfdeacc5add85cc043cd16986e82152141883/mmh3-5.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:eed4bba7ff8a0d37106ba931ab03bdd3915fbb025bcf4e1f0aa02bc8114960c5", size = 106493, upload-time = "2025-07-29T07:42:45.07Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b1/bc8c28e4d6e807bbb051fefe78e1156d7f104b89948742ad310612ce240d/mmh3-5.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1fdb36b940e9261aff0b5177c5b74a36936b902f473180f6c15bde26143681a9", size = 110089, upload-time = "2025-07-29T07:42:46.122Z" }, - { url = "https://files.pythonhosted.org/packages/3b/a2/d20f3f5c95e9c511806686c70d0a15479cc3941c5f322061697af1c1ff70/mmh3-5.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7303aab41e97adcf010a09efd8f1403e719e59b7705d5e3cfed3dd7571589290", size = 97571, upload-time = "2025-07-29T07:42:47.18Z" }, - { url = "https://files.pythonhosted.org/packages/7b/23/665296fce4f33488deec39a750ffd245cfc07aafb0e3ef37835f91775d14/mmh3-5.2.0-cp313-cp313-win32.whl", hash = "sha256:03e08c6ebaf666ec1e3d6ea657a2d363bb01effd1a9acfe41f9197decaef0051", size = 40806, upload-time = "2025-07-29T07:42:48.166Z" }, - { url = "https://files.pythonhosted.org/packages/59/b0/92e7103f3b20646e255b699e2d0327ce53a3f250e44367a99dc8be0b7c7a/mmh3-5.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:7fddccd4113e7b736706e17a239a696332360cbaddf25ae75b57ba1acce65081", size = 41600, upload-time = "2025-07-29T07:42:49.371Z" }, - { url = "https://files.pythonhosted.org/packages/99/22/0b2bd679a84574647de538c5b07ccaa435dbccc37815067fe15b90fe8dad/mmh3-5.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa0c966ee727aad5406d516375593c5f058c766b21236ab8985693934bb5085b", size = 39349, upload-time = "2025-07-29T07:42:50.268Z" }, - { url = "https://files.pythonhosted.org/packages/f7/ca/a20db059a8a47048aaf550da14a145b56e9c7386fb8280d3ce2962dcebf7/mmh3-5.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e5015f0bb6eb50008bed2d4b1ce0f2a294698a926111e4bb202c0987b4f89078", size = 39209, upload-time = "2025-07-29T07:42:51.559Z" }, - { url = "https://files.pythonhosted.org/packages/98/dd/e5094799d55c7482d814b979a0fd608027d0af1b274bfb4c3ea3e950bfd5/mmh3-5.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:e0f3ed828d709f5b82d8bfe14f8856120718ec4bd44a5b26102c3030a1e12501", size = 39843, upload-time = "2025-07-29T07:42:52.536Z" }, - { url = "https://files.pythonhosted.org/packages/f4/6b/7844d7f832c85400e7cc89a1348e4e1fdd38c5a38415bb5726bbb8fcdb6c/mmh3-5.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:f35727c5118aba95f0397e18a1a5b8405425581bfe53e821f0fb444cbdc2bc9b", size = 40648, upload-time = "2025-07-29T07:42:53.392Z" }, - { url = "https://files.pythonhosted.org/packages/1f/bf/71f791f48a21ff3190ba5225807cbe4f7223360e96862c376e6e3fb7efa7/mmh3-5.2.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bc244802ccab5220008cb712ca1508cb6a12f0eb64ad62997156410579a1770", size = 56164, upload-time = "2025-07-29T07:42:54.267Z" }, - { url = "https://files.pythonhosted.org/packages/70/1f/f87e3d34d83032b4f3f0f528c6d95a98290fcacf019da61343a49dccfd51/mmh3-5.2.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ff3d50dc3fe8a98059f99b445dfb62792b5d006c5e0b8f03c6de2813b8376110", size = 40692, upload-time = "2025-07-29T07:42:55.234Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e2/db849eaed07117086f3452feca8c839d30d38b830ac59fe1ce65af8be5ad/mmh3-5.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:37a358cc881fe796e099c1db6ce07ff757f088827b4e8467ac52b7a7ffdca647", size = 40068, upload-time = "2025-07-29T07:42:56.158Z" }, - { url = "https://files.pythonhosted.org/packages/df/6b/209af927207af77425b044e32f77f49105a0b05d82ff88af6971d8da4e19/mmh3-5.2.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b9a87025121d1c448f24f27ff53a5fe7b6ef980574b4a4f11acaabe702420d63", size = 97367, upload-time = "2025-07-29T07:42:57.037Z" }, - { url = "https://files.pythonhosted.org/packages/ca/e0/78adf4104c425606a9ce33fb351f790c76a6c2314969c4a517d1ffc92196/mmh3-5.2.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ba55d6ca32eeef8b2625e1e4bfc3b3db52bc63014bd7e5df8cc11bf2b036b12", size = 103306, upload-time = "2025-07-29T07:42:58.522Z" }, - { url = "https://files.pythonhosted.org/packages/a3/79/c2b89f91b962658b890104745b1b6c9ce38d50a889f000b469b91eeb1b9e/mmh3-5.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9ff37ba9f15637e424c2ab57a1a590c52897c845b768e4e0a4958084ec87f22", size = 106312, upload-time = "2025-07-29T07:42:59.552Z" }, - { url = "https://files.pythonhosted.org/packages/4b/14/659d4095528b1a209be90934778c5ffe312177d51e365ddcbca2cac2ec7c/mmh3-5.2.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a094319ec0db52a04af9fdc391b4d39a1bc72bc8424b47c4411afb05413a44b5", size = 113135, upload-time = "2025-07-29T07:43:00.745Z" }, - { url = "https://files.pythonhosted.org/packages/8d/6f/cd7734a779389a8a467b5c89a48ff476d6f2576e78216a37551a97e9e42a/mmh3-5.2.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c5584061fd3da584659b13587f26c6cad25a096246a481636d64375d0c1f6c07", size = 120775, upload-time = "2025-07-29T07:43:02.124Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ca/8256e3b96944408940de3f9291d7e38a283b5761fe9614d4808fcf27bd62/mmh3-5.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecbfc0437ddfdced5e7822d1ce4855c9c64f46819d0fdc4482c53f56c707b935", size = 99178, upload-time = "2025-07-29T07:43:03.182Z" }, - { url = "https://files.pythonhosted.org/packages/8a/32/39e2b3cf06b6e2eb042c984dab8680841ac2a0d3ca6e0bea30db1f27b565/mmh3-5.2.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:7b986d506a8e8ea345791897ba5d8ba0d9d8820cd4fc3e52dbe6de19388de2e7", size = 98738, upload-time = "2025-07-29T07:43:04.207Z" }, - { url = "https://files.pythonhosted.org/packages/61/d3/7bbc8e0e8cf65ebbe1b893ffa0467b7ecd1bd07c3bbf6c9db4308ada22ec/mmh3-5.2.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:38d899a156549da8ef6a9f1d6f7ef231228d29f8f69bce2ee12f5fba6d6fd7c5", size = 106510, upload-time = "2025-07-29T07:43:05.656Z" }, - { url = "https://files.pythonhosted.org/packages/10/99/b97e53724b52374e2f3859046f0eb2425192da356cb19784d64bc17bb1cf/mmh3-5.2.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d86651fa45799530885ba4dab3d21144486ed15285e8784181a0ab37a4552384", size = 110053, upload-time = "2025-07-29T07:43:07.204Z" }, - { url = "https://files.pythonhosted.org/packages/ac/62/3688c7d975ed195155671df68788c83fed6f7909b6ec4951724c6860cb97/mmh3-5.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c463d7c1c4cfc9d751efeaadd936bbba07b5b0ed81a012b3a9f5a12f0872bd6e", size = 97546, upload-time = "2025-07-29T07:43:08.226Z" }, - { url = "https://files.pythonhosted.org/packages/ca/3b/c6153250f03f71a8b7634cded82939546cdfba02e32f124ff51d52c6f991/mmh3-5.2.0-cp314-cp314-win32.whl", hash = "sha256:bb4fe46bdc6104fbc28db7a6bacb115ee6368ff993366bbd8a2a7f0076e6f0c0", size = 41422, upload-time = "2025-07-29T07:43:09.216Z" }, - { url = "https://files.pythonhosted.org/packages/74/01/a27d98bab083a435c4c07e9d1d720d4c8a578bf4c270bae373760b1022be/mmh3-5.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:7c7f0b342fd06044bedd0b6e72177ddc0076f54fd89ee239447f8b271d919d9b", size = 42135, upload-time = "2025-07-29T07:43:10.183Z" }, - { url = "https://files.pythonhosted.org/packages/cb/c9/dbba5507e95429b8b380e2ba091eff5c20a70a59560934dff0ad8392b8c8/mmh3-5.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:3193752fc05ea72366c2b63ff24b9a190f422e32d75fdeae71087c08fff26115", size = 39879, upload-time = "2025-07-29T07:43:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/b5/d1/c8c0ef839c17258b9de41b84f663574fabcf8ac2007b7416575e0f65ff6e/mmh3-5.2.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:69fc339d7202bea69ef9bd7c39bfdf9fdabc8e6822a01eba62fb43233c1b3932", size = 57696, upload-time = "2025-07-29T07:43:11.989Z" }, - { url = "https://files.pythonhosted.org/packages/2f/55/95e2b9ff201e89f9fe37036037ab61a6c941942b25cdb7b6a9df9b931993/mmh3-5.2.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:12da42c0a55c9d86ab566395324213c319c73ecb0c239fad4726324212b9441c", size = 41421, upload-time = "2025-07-29T07:43:13.269Z" }, - { url = "https://files.pythonhosted.org/packages/77/79/9be23ad0b7001a4b22752e7693be232428ecc0a35068a4ff5c2f14ef8b20/mmh3-5.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f7f9034c7cf05ddfaac8d7a2e63a3c97a840d4615d0a0e65ba8bdf6f8576e3be", size = 40853, upload-time = "2025-07-29T07:43:14.888Z" }, - { url = "https://files.pythonhosted.org/packages/ac/1b/96b32058eda1c1dee8264900c37c359a7325c1f11f5ff14fd2be8e24eff9/mmh3-5.2.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:11730eeb16dfcf9674fdea9bb6b8e6dd9b40813b7eb839bc35113649eef38aeb", size = 109694, upload-time = "2025-07-29T07:43:15.816Z" }, - { url = "https://files.pythonhosted.org/packages/8d/6f/a2ae44cd7dad697b6dea48390cbc977b1e5ca58fda09628cbcb2275af064/mmh3-5.2.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:932a6eec1d2e2c3c9e630d10f7128d80e70e2d47fe6b8c7ea5e1afbd98733e65", size = 117438, upload-time = "2025-07-29T07:43:16.865Z" }, - { url = "https://files.pythonhosted.org/packages/a0/08/bfb75451c83f05224a28afeaf3950c7b793c0b71440d571f8e819cfb149a/mmh3-5.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ca975c51c5028947bbcfc24966517aac06a01d6c921e30f7c5383c195f87991", size = 120409, upload-time = "2025-07-29T07:43:18.207Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ea/8b118b69b2ff8df568f742387d1a159bc654a0f78741b31437dd047ea28e/mmh3-5.2.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5b0b58215befe0f0e120b828f7645e97719bbba9f23b69e268ed0ac7adde8645", size = 125909, upload-time = "2025-07-29T07:43:19.39Z" }, - { url = "https://files.pythonhosted.org/packages/3e/11/168cc0b6a30650032e351a3b89b8a47382da541993a03af91e1ba2501234/mmh3-5.2.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29c2b9ce61886809d0492a274a5a53047742dea0f703f9c4d5d223c3ea6377d3", size = 135331, upload-time = "2025-07-29T07:43:20.435Z" }, - { url = "https://files.pythonhosted.org/packages/31/05/e3a9849b1c18a7934c64e831492c99e67daebe84a8c2f2c39a7096a830e3/mmh3-5.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a367d4741ac0103f8198c82f429bccb9359f543ca542b06a51f4f0332e8de279", size = 110085, upload-time = "2025-07-29T07:43:21.92Z" }, - { url = "https://files.pythonhosted.org/packages/d9/d5/a96bcc306e3404601418b2a9a370baec92af84204528ba659fdfe34c242f/mmh3-5.2.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:5a5dba98e514fb26241868f6eb90a7f7ca0e039aed779342965ce24ea32ba513", size = 111195, upload-time = "2025-07-29T07:43:23.066Z" }, - { url = "https://files.pythonhosted.org/packages/af/29/0fd49801fec5bff37198684e0849b58e0dab3a2a68382a357cfffb0fafc3/mmh3-5.2.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:941603bfd75a46023807511c1ac2f1b0f39cccc393c15039969806063b27e6db", size = 116919, upload-time = "2025-07-29T07:43:24.178Z" }, - { url = "https://files.pythonhosted.org/packages/2d/04/4f3c32b0a2ed762edca45d8b46568fc3668e34f00fb1e0a3b5451ec1281c/mmh3-5.2.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:132dd943451a7c7546978863d2f5a64977928410782e1a87d583cb60eb89e667", size = 123160, upload-time = "2025-07-29T07:43:25.26Z" }, - { url = "https://files.pythonhosted.org/packages/91/76/3d29eaa38821730633d6a240d36fa8ad2807e9dfd432c12e1a472ed211eb/mmh3-5.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f698733a8a494466432d611a8f0d1e026f5286dee051beea4b3c3146817e35d5", size = 110206, upload-time = "2025-07-29T07:43:26.699Z" }, - { url = "https://files.pythonhosted.org/packages/44/1c/ccf35892684d3a408202e296e56843743e0b4fb1629e59432ea88cdb3909/mmh3-5.2.0-cp314-cp314t-win32.whl", hash = "sha256:6d541038b3fc360ec538fc116de87462627944765a6750308118f8b509a8eec7", size = 41970, upload-time = "2025-07-29T07:43:27.666Z" }, - { url = "https://files.pythonhosted.org/packages/75/b2/b9e4f1e5adb5e21eb104588fcee2cd1eaa8308255173481427d5ecc4284e/mmh3-5.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e912b19cf2378f2967d0c08e86ff4c6c360129887f678e27e4dde970d21b3f4d", size = 43063, upload-time = "2025-07-29T07:43:28.582Z" }, - { url = "https://files.pythonhosted.org/packages/6a/fc/0e61d9a4e29c8679356795a40e48f647b4aad58d71bfc969f0f8f56fb912/mmh3-5.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:e7884931fe5e788163e7b3c511614130c2c59feffdc21112290a194487efb2e9", size = 40455, upload-time = "2025-07-29T07:43:29.563Z" }, -] - -[[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 = "mouseinfo" -version = "0.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyperclip" }, - { name = "python3-xlib", marker = "sys_platform == 'linux'" }, - { name = "rubicon-objc", marker = "sys_platform == 'darwin'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/fa/b2ba8229b9381e8f6381c1dcae6f4159a7f72349e414ed19cfbbd1817173/MouseInfo-0.1.3.tar.gz", hash = "sha256:2c62fb8885062b8e520a3cce0a297c657adcc08c60952eb05bc8256ef6f7f6e7", size = 10850, upload-time = "2020-03-27T21:20:10.136Z" } - -[[package]] -name = "mpmath" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, -] - -[[package]] -name = "multidict" -version = "6.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, - { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, - { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, - { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, - { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, - { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, - { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, - { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, - { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, - { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, - { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, - { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, - { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, - { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, - { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, - { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, - { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, - { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, - { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, - { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, - { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, - { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, - { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, - { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, - { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, - { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, - { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, - { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, - { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, - { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, - { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, - { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, - { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, - { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, - { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, - { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, - { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, - { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, - { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, - { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, - { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, - { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, - { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, - { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, - { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, - { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, - { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, - { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, - { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, - { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, - { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, - { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, - { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, - { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, - { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, - { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, - { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, - { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, - { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, - { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, - { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, - { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, - { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, - { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, - { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, - { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, - { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, - { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, - { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, - { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, - { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, - { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, - { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, - { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, - { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, - { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, - { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, -] - -[[package]] -name = "mypy" -version = "1.19.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, - { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, - { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, - { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, - { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, - { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, - { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, - { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, - { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, - { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, - { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, - { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, - { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, - { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, -] - -[[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 = "myst-parser" -version = "5.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "jinja2" }, - { name = "markdown-it-py" }, - { name = "mdit-py-plugins" }, - { name = "pyyaml" }, - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/33/fa/7b45eef11b7971f0beb29d27b7bfe0d747d063aa29e170d9edd004733c8a/myst_parser-5.0.0.tar.gz", hash = "sha256:f6f231452c56e8baa662cc352c548158f6a16fcbd6e3800fc594978002b94f3a", size = 98535, upload-time = "2026-01-15T09:08:18.036Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/ac/686789b9145413f1a61878c407210e41bfdb097976864e0913078b24098c/myst_parser-5.0.0-py3-none-any.whl", hash = "sha256:ab31e516024918296e169139072b81592336f2fef55b8986aa31c9f04b5f7211", size = 84533, upload-time = "2026-01-15T09:08:16.788Z" }, -] - -[[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 = "networkx" -version = "3.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, -] - -[[package]] -name = "nh3" -version = "0.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/a5/34c26015d3a434409f4d2a1cd8821a06c05238703f49283ffeb937bef093/nh3-0.3.2.tar.gz", hash = "sha256:f394759a06df8b685a4ebfb1874fb67a9cbfd58c64fc5ed587a663c0e63ec376", size = 19288, upload-time = "2025-10-30T11:17:45.948Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/01/a1eda067c0ba823e5e2bb033864ae4854549e49fb6f3407d2da949106bfb/nh3-0.3.2-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d18957a90806d943d141cc5e4a0fefa1d77cf0d7a156878bf9a66eed52c9cc7d", size = 1419839, upload-time = "2025-10-30T11:17:09.956Z" }, - { url = "https://files.pythonhosted.org/packages/30/57/07826ff65d59e7e9cc789ef1dc405f660cabd7458a1864ab58aefa17411b/nh3-0.3.2-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45c953e57028c31d473d6b648552d9cab1efe20a42ad139d78e11d8f42a36130", size = 791183, upload-time = "2025-10-30T11:17:11.99Z" }, - { url = "https://files.pythonhosted.org/packages/af/2f/e8a86f861ad83f3bb5455f596d5c802e34fcdb8c53a489083a70fd301333/nh3-0.3.2-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c9850041b77a9147d6bbd6dbbf13eeec7009eb60b44e83f07fcb2910075bf9b", size = 829127, upload-time = "2025-10-30T11:17:13.192Z" }, - { url = "https://files.pythonhosted.org/packages/d8/97/77aef4daf0479754e8e90c7f8f48f3b7b8725a3b8c0df45f2258017a6895/nh3-0.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:403c11563e50b915d0efdb622866d1d9e4506bce590ef7da57789bf71dd148b5", size = 997131, upload-time = "2025-10-30T11:17:14.677Z" }, - { url = "https://files.pythonhosted.org/packages/41/ee/fd8140e4df9d52143e89951dd0d797f5546004c6043285289fbbe3112293/nh3-0.3.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:0dca4365db62b2d71ff1620ee4f800c4729849906c5dd504ee1a7b2389558e31", size = 1068783, upload-time = "2025-10-30T11:17:15.861Z" }, - { url = "https://files.pythonhosted.org/packages/87/64/bdd9631779e2d588b08391f7555828f352e7f6427889daf2fa424bfc90c9/nh3-0.3.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0fe7ee035dd7b2290715baf29cb27167dddd2ff70ea7d052c958dbd80d323c99", size = 994732, upload-time = "2025-10-30T11:17:17.155Z" }, - { url = "https://files.pythonhosted.org/packages/79/66/90190033654f1f28ca98e3d76b8be1194505583f9426b0dcde782a3970a2/nh3-0.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a40202fd58e49129764f025bbaae77028e420f1d5b3c8e6f6fd3a6490d513868", size = 975997, upload-time = "2025-10-30T11:17:18.77Z" }, - { url = "https://files.pythonhosted.org/packages/34/30/ebf8e2e8d71fdb5a5d5d8836207177aed1682df819cbde7f42f16898946c/nh3-0.3.2-cp314-cp314t-win32.whl", hash = "sha256:1f9ba555a797dbdcd844b89523f29cdc90973d8bd2e836ea6b962cf567cadd93", size = 583364, upload-time = "2025-10-30T11:17:20.286Z" }, - { url = "https://files.pythonhosted.org/packages/94/ae/95c52b5a75da429f11ca8902c2128f64daafdc77758d370e4cc310ecda55/nh3-0.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:dce4248edc427c9b79261f3e6e2b3ecbdd9b88c267012168b4a7b3fc6fd41d13", size = 589982, upload-time = "2025-10-30T11:17:21.384Z" }, - { url = "https://files.pythonhosted.org/packages/b4/bd/c7d862a4381b95f2469704de32c0ad419def0f4a84b7a138a79532238114/nh3-0.3.2-cp314-cp314t-win_arm64.whl", hash = "sha256:019ecbd007536b67fdf76fab411b648fb64e2257ca3262ec80c3425c24028c80", size = 577126, upload-time = "2025-10-30T11:17:22.755Z" }, - { url = "https://files.pythonhosted.org/packages/b6/3e/f5a5cc2885c24be13e9b937441bd16a012ac34a657fe05e58927e8af8b7a/nh3-0.3.2-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7064ccf5ace75825bd7bf57859daaaf16ed28660c1c6b306b649a9eda4b54b1e", size = 1431980, upload-time = "2025-10-30T11:17:25.457Z" }, - { url = "https://files.pythonhosted.org/packages/7f/f7/529a99324d7ef055de88b690858f4189379708abae92ace799365a797b7f/nh3-0.3.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8745454cdd28bbbc90861b80a0111a195b0e3961b9fa2e672be89eb199fa5d8", size = 820805, upload-time = "2025-10-30T11:17:26.98Z" }, - { url = "https://files.pythonhosted.org/packages/3d/62/19b7c50ccd1fa7d0764822d2cea8f2a320f2fd77474c7a1805cb22cf69b0/nh3-0.3.2-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72d67c25a84579f4a432c065e8b4274e53b7cf1df8f792cf846abfe2c3090866", size = 803527, upload-time = "2025-10-30T11:17:28.284Z" }, - { url = "https://files.pythonhosted.org/packages/4a/ca/f022273bab5440abff6302731a49410c5ef66b1a9502ba3fbb2df998d9ff/nh3-0.3.2-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:13398e676a14d6233f372c75f52d5ae74f98210172991f7a3142a736bd92b131", size = 1051674, upload-time = "2025-10-30T11:17:29.909Z" }, - { url = "https://files.pythonhosted.org/packages/fa/f7/5728e3b32a11daf5bd21cf71d91c463f74305938bc3eb9e0ac1ce141646e/nh3-0.3.2-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03d617e5c8aa7331bd2659c654e021caf9bba704b109e7b2b28b039a00949fe5", size = 1004737, upload-time = "2025-10-30T11:17:31.205Z" }, - { url = "https://files.pythonhosted.org/packages/53/7f/f17e0dba0a99cee29e6cee6d4d52340ef9cb1f8a06946d3a01eb7ec2fb01/nh3-0.3.2-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f55c4d2d5a207e74eefe4d828067bbb01300e06e2a7436142f915c5928de07", size = 911745, upload-time = "2025-10-30T11:17:32.945Z" }, - { url = "https://files.pythonhosted.org/packages/42/0f/c76bf3dba22c73c38e9b1113b017cf163f7696f50e003404ec5ecdb1e8a6/nh3-0.3.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb18403f02b655a1bbe4e3a4696c2ae1d6ae8f5991f7cacb684b1ae27e6c9f7", size = 797184, upload-time = "2025-10-30T11:17:34.226Z" }, - { url = "https://files.pythonhosted.org/packages/08/a1/73d8250f888fb0ddf1b119b139c382f8903d8bb0c5bd1f64afc7e38dad1d/nh3-0.3.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d66f41672eb4060cf87c037f760bdbc6847852ca9ef8e9c5a5da18f090abf87", size = 838556, upload-time = "2025-10-30T11:17:35.875Z" }, - { url = "https://files.pythonhosted.org/packages/d1/09/deb57f1fb656a7a5192497f4a287b0ade5a2ff6b5d5de4736d13ef6d2c1f/nh3-0.3.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f97f8b25cb2681d25e2338148159447e4d689aafdccfcf19e61ff7db3905768a", size = 1006695, upload-time = "2025-10-30T11:17:37.071Z" }, - { url = "https://files.pythonhosted.org/packages/b6/61/8f4d41c4ccdac30e4b1a4fa7be4b0f9914d8314a5058472f84c8e101a418/nh3-0.3.2-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:2ab70e8c6c7d2ce953d2a58102eefa90c2d0a5ed7aa40c7e29a487bc5e613131", size = 1075471, upload-time = "2025-10-30T11:17:38.225Z" }, - { url = "https://files.pythonhosted.org/packages/b0/c6/966aec0cb4705e69f6c3580422c239205d5d4d0e50fac380b21e87b6cf1b/nh3-0.3.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:1710f3901cd6440ca92494ba2eb6dc260f829fa8d9196b659fa10de825610ce0", size = 1002439, upload-time = "2025-10-30T11:17:39.553Z" }, - { url = "https://files.pythonhosted.org/packages/e2/c8/97a2d5f7a314cce2c5c49f30c6f161b7f3617960ade4bfc2fd1ee092cb20/nh3-0.3.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:91e9b001101fb4500a2aafe3e7c92928d85242d38bf5ac0aba0b7480da0a4cd6", size = 987439, upload-time = "2025-10-30T11:17:40.81Z" }, - { url = "https://files.pythonhosted.org/packages/0d/95/2d6fc6461687d7a171f087995247dec33e8749a562bfadd85fb5dbf37a11/nh3-0.3.2-cp38-abi3-win32.whl", hash = "sha256:169db03df90da63286e0560ea0efa9b6f3b59844a9735514a1d47e6bb2c8c61b", size = 589826, upload-time = "2025-10-30T11:17:42.239Z" }, - { url = "https://files.pythonhosted.org/packages/64/9a/1a1c154f10a575d20dd634e5697805e589bbdb7673a0ad00e8da90044ba7/nh3-0.3.2-cp38-abi3-win_amd64.whl", hash = "sha256:562da3dca7a17f9077593214a9781a94b8d76de4f158f8c895e62f09573945fe", size = 596406, upload-time = "2025-10-30T11:17:43.773Z" }, - { url = "https://files.pythonhosted.org/packages/9e/7e/a96255f63b7aef032cbee8fc4d6e37def72e3aaedc1f72759235e8f13cb1/nh3-0.3.2-cp38-abi3-win_arm64.whl", hash = "sha256:cf5964d54edd405e68583114a7cba929468bcd7db5e676ae38ee954de1cfc104", size = 584162, upload-time = "2025-10-30T11:17:44.96Z" }, -] - -[[package]] -name = "numpy" -version = "2.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/fd/0005efbd0af48e55eb3c7208af93f2862d4b1a56cd78e84309a2d959208d/numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae", size = 20723651, upload-time = "2026-01-31T23:13:10.135Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/6e/6f394c9c77668153e14d4da83bcc247beb5952f6ead7699a1a2992613bea/numpy-2.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:21982668592194c609de53ba4933a7471880ccbaadcc52352694a59ecc860b3a", size = 16667963, upload-time = "2026-01-31T23:10:52.147Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f8/55483431f2b2fd015ae6ed4fe62288823ce908437ed49db5a03d15151678/numpy-2.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40397bda92382fcec844066efb11f13e1c9a3e2a8e8f318fb72ed8b6db9f60f1", size = 14693571, upload-time = "2026-01-31T23:10:54.789Z" }, - { url = "https://files.pythonhosted.org/packages/2f/20/18026832b1845cdc82248208dd929ca14c9d8f2bac391f67440707fff27c/numpy-2.4.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b3a24467af63c67829bfaa61eecf18d5432d4f11992688537be59ecd6ad32f5e", size = 5203469, upload-time = "2026-01-31T23:10:57.343Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/2eb97c8a77daaba34eaa3fa7241a14ac5f51c46a6bd5911361b644c4a1e2/numpy-2.4.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:805cc8de9fd6e7a22da5aed858e0ab16be5a4db6c873dde1d7451c541553aa27", size = 6550820, upload-time = "2026-01-31T23:10:59.429Z" }, - { url = "https://files.pythonhosted.org/packages/b1/91/b97fdfd12dc75b02c44e26c6638241cc004d4079a0321a69c62f51470c4c/numpy-2.4.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d82351358ffbcdcd7b686b90742a9b86632d6c1c051016484fa0b326a0a1548", size = 15663067, upload-time = "2026-01-31T23:11:01.291Z" }, - { url = "https://files.pythonhosted.org/packages/f5/c6/a18e59f3f0b8071cc85cbc8d80cd02d68aa9710170b2553a117203d46936/numpy-2.4.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e35d3e0144137d9fdae62912e869136164534d64a169f86438bc9561b6ad49f", size = 16619782, upload-time = "2026-01-31T23:11:03.669Z" }, - { url = "https://files.pythonhosted.org/packages/b7/83/9751502164601a79e18847309f5ceec0b1446d7b6aa12305759b72cf98b2/numpy-2.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adb6ed2ad29b9e15321d167d152ee909ec73395901b70936f029c3bc6d7f4460", size = 17013128, upload-time = "2026-01-31T23:11:05.913Z" }, - { url = "https://files.pythonhosted.org/packages/61/c4/c4066322256ec740acc1c8923a10047818691d2f8aec254798f3dd90f5f2/numpy-2.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8906e71fd8afcb76580404e2a950caef2685df3d2a57fe82a86ac8d33cc007ba", size = 18345324, upload-time = "2026-01-31T23:11:08.248Z" }, - { url = "https://files.pythonhosted.org/packages/ab/af/6157aa6da728fa4525a755bfad486ae7e3f76d4c1864138003eb84328497/numpy-2.4.2-cp312-cp312-win32.whl", hash = "sha256:ec055f6dae239a6299cace477b479cca2fc125c5675482daf1dd886933a1076f", size = 5960282, upload-time = "2026-01-31T23:11:10.497Z" }, - { url = "https://files.pythonhosted.org/packages/92/0f/7ceaaeaacb40567071e94dbf2c9480c0ae453d5bb4f52bea3892c39dc83c/numpy-2.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:209fae046e62d0ce6435fcfe3b1a10537e858249b3d9b05829e2a05218296a85", size = 12314210, upload-time = "2026-01-31T23:11:12.176Z" }, - { url = "https://files.pythonhosted.org/packages/2f/a3/56c5c604fae6dd40fa2ed3040d005fca97e91bd320d232ac9931d77ba13c/numpy-2.4.2-cp312-cp312-win_arm64.whl", hash = "sha256:fbde1b0c6e81d56f5dccd95dd4a711d9b95df1ae4009a60887e56b27e8d903fa", size = 10220171, upload-time = "2026-01-31T23:11:14.684Z" }, - { url = "https://files.pythonhosted.org/packages/a1/22/815b9fe25d1d7ae7d492152adbc7226d3eff731dffc38fe970589fcaaa38/numpy-2.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25f2059807faea4b077a2b6837391b5d830864b3543627f381821c646f31a63c", size = 16663696, upload-time = "2026-01-31T23:11:17.516Z" }, - { url = "https://files.pythonhosted.org/packages/09/f0/817d03a03f93ba9c6c8993de509277d84e69f9453601915e4a69554102a1/numpy-2.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bd3a7a9f5847d2fb8c2c6d1c862fa109c31a9abeca1a3c2bd5a64572955b2979", size = 14688322, upload-time = "2026-01-31T23:11:19.883Z" }, - { url = "https://files.pythonhosted.org/packages/da/b4/f805ab79293c728b9a99438775ce51885fd4f31b76178767cfc718701a39/numpy-2.4.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8e4549f8a3c6d13d55041925e912bfd834285ef1dd64d6bc7d542583355e2e98", size = 5198157, upload-time = "2026-01-31T23:11:22.375Z" }, - { url = "https://files.pythonhosted.org/packages/74/09/826e4289844eccdcd64aac27d13b0fd3f32039915dd5b9ba01baae1f436c/numpy-2.4.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:aea4f66ff44dfddf8c2cffd66ba6538c5ec67d389285292fe428cb2c738c8aef", size = 6546330, upload-time = "2026-01-31T23:11:23.958Z" }, - { url = "https://files.pythonhosted.org/packages/19/fb/cbfdbfa3057a10aea5422c558ac57538e6acc87ec1669e666d32ac198da7/numpy-2.4.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3cd545784805de05aafe1dde61752ea49a359ccba9760c1e5d1c88a93bbf2b7", size = 15660968, upload-time = "2026-01-31T23:11:25.713Z" }, - { url = "https://files.pythonhosted.org/packages/04/dc/46066ce18d01645541f0186877377b9371b8fa8017fa8262002b4ef22612/numpy-2.4.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0d9b7c93578baafcbc5f0b83eaf17b79d345c6f36917ba0c67f45226911d499", size = 16607311, upload-time = "2026-01-31T23:11:28.117Z" }, - { url = "https://files.pythonhosted.org/packages/14/d9/4b5adfc39a43fa6bf918c6d544bc60c05236cc2f6339847fc5b35e6cb5b0/numpy-2.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f74f0f7779cc7ae07d1810aab8ac6b1464c3eafb9e283a40da7309d5e6e48fbb", size = 17012850, upload-time = "2026-01-31T23:11:30.888Z" }, - { url = "https://files.pythonhosted.org/packages/b7/20/adb6e6adde6d0130046e6fdfb7675cc62bc2f6b7b02239a09eb58435753d/numpy-2.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7ac672d699bf36275c035e16b65539931347d68b70667d28984c9fb34e07fa7", size = 18334210, upload-time = "2026-01-31T23:11:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/78/0e/0a73b3dff26803a8c02baa76398015ea2a5434d9b8265a7898a6028c1591/numpy-2.4.2-cp313-cp313-win32.whl", hash = "sha256:8e9afaeb0beff068b4d9cd20d322ba0ee1cecfb0b08db145e4ab4dd44a6b5110", size = 5958199, upload-time = "2026-01-31T23:11:35.385Z" }, - { url = "https://files.pythonhosted.org/packages/43/bc/6352f343522fcb2c04dbaf94cb30cca6fd32c1a750c06ad6231b4293708c/numpy-2.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:7df2de1e4fba69a51c06c28f5a3de36731eb9639feb8e1cf7e4a7b0daf4cf622", size = 12310848, upload-time = "2026-01-31T23:11:38.001Z" }, - { url = "https://files.pythonhosted.org/packages/6e/8d/6da186483e308da5da1cc6918ce913dcfe14ffde98e710bfeff2a6158d4e/numpy-2.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:0fece1d1f0a89c16b03442eae5c56dc0be0c7883b5d388e0c03f53019a4bfd71", size = 10221082, upload-time = "2026-01-31T23:11:40.392Z" }, - { url = "https://files.pythonhosted.org/packages/25/a1/9510aa43555b44781968935c7548a8926274f815de42ad3997e9e83680dd/numpy-2.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5633c0da313330fd20c484c78cdd3f9b175b55e1a766c4a174230c6b70ad8262", size = 14815866, upload-time = "2026-01-31T23:11:42.495Z" }, - { url = "https://files.pythonhosted.org/packages/36/30/6bbb5e76631a5ae46e7923dd16ca9d3f1c93cfa8d4ed79a129814a9d8db3/numpy-2.4.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d9f64d786b3b1dd742c946c42d15b07497ed14af1a1f3ce840cce27daa0ce913", size = 5325631, upload-time = "2026-01-31T23:11:44.7Z" }, - { url = "https://files.pythonhosted.org/packages/46/00/3a490938800c1923b567b3a15cd17896e68052e2145d8662aaf3e1ffc58f/numpy-2.4.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:b21041e8cb6a1eb5312dd1d2f80a94d91efffb7a06b70597d44f1bd2dfc315ab", size = 6646254, upload-time = "2026-01-31T23:11:46.341Z" }, - { url = "https://files.pythonhosted.org/packages/d3/e9/fac0890149898a9b609caa5af7455a948b544746e4b8fe7c212c8edd71f8/numpy-2.4.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00ab83c56211a1d7c07c25e3217ea6695e50a3e2f255053686b081dc0b091a82", size = 15720138, upload-time = "2026-01-31T23:11:48.082Z" }, - { url = "https://files.pythonhosted.org/packages/ea/5c/08887c54e68e1e28df53709f1893ce92932cc6f01f7c3d4dc952f61ffd4e/numpy-2.4.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fb882da679409066b4603579619341c6d6898fc83a8995199d5249f986e8e8f", size = 16655398, upload-time = "2026-01-31T23:11:50.293Z" }, - { url = "https://files.pythonhosted.org/packages/4d/89/253db0fa0e66e9129c745e4ef25631dc37d5f1314dad2b53e907b8538e6d/numpy-2.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66cb9422236317f9d44b67b4d18f44efe6e9c7f8794ac0462978513359461554", size = 17079064, upload-time = "2026-01-31T23:11:52.927Z" }, - { url = "https://files.pythonhosted.org/packages/2a/d5/cbade46ce97c59c6c3da525e8d95b7abe8a42974a1dc5c1d489c10433e88/numpy-2.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0f01dcf33e73d80bd8dc0f20a71303abbafa26a19e23f6b68d1aa9990af90257", size = 18379680, upload-time = "2026-01-31T23:11:55.22Z" }, - { url = "https://files.pythonhosted.org/packages/40/62/48f99ae172a4b63d981babe683685030e8a3df4f246c893ea5c6ef99f018/numpy-2.4.2-cp313-cp313t-win32.whl", hash = "sha256:52b913ec40ff7ae845687b0b34d8d93b60cb66dcee06996dd5c99f2fc9328657", size = 6082433, upload-time = "2026-01-31T23:11:58.096Z" }, - { url = "https://files.pythonhosted.org/packages/07/38/e054a61cfe48ad9f1ed0d188e78b7e26859d0b60ef21cd9de4897cdb5326/numpy-2.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5eea80d908b2c1f91486eb95b3fb6fab187e569ec9752ab7d9333d2e66bf2d6b", size = 12451181, upload-time = "2026-01-31T23:11:59.782Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a4/a05c3a6418575e185dd84d0b9680b6bb2e2dc3e4202f036b7b4e22d6e9dc/numpy-2.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fd49860271d52127d61197bb50b64f58454e9f578cb4b2c001a6de8b1f50b0b1", size = 10290756, upload-time = "2026-01-31T23:12:02.438Z" }, - { url = "https://files.pythonhosted.org/packages/18/88/b7df6050bf18fdcfb7046286c6535cabbdd2064a3440fca3f069d319c16e/numpy-2.4.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b", size = 16663092, upload-time = "2026-01-31T23:12:04.521Z" }, - { url = "https://files.pythonhosted.org/packages/25/7a/1fee4329abc705a469a4afe6e69b1ef7e915117747886327104a8493a955/numpy-2.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000", size = 14698770, upload-time = "2026-01-31T23:12:06.96Z" }, - { url = "https://files.pythonhosted.org/packages/fb/0b/f9e49ba6c923678ad5bc38181c08ac5e53b7a5754dbca8e581aa1a56b1ff/numpy-2.4.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1", size = 5208562, upload-time = "2026-01-31T23:12:09.632Z" }, - { url = "https://files.pythonhosted.org/packages/7d/12/d7de8f6f53f9bb76997e5e4c069eda2051e3fe134e9181671c4391677bb2/numpy-2.4.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74", size = 6543710, upload-time = "2026-01-31T23:12:11.969Z" }, - { url = "https://files.pythonhosted.org/packages/09/63/c66418c2e0268a31a4cf8a8b512685748200f8e8e8ec6c507ce14e773529/numpy-2.4.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a", size = 15677205, upload-time = "2026-01-31T23:12:14.33Z" }, - { url = "https://files.pythonhosted.org/packages/5d/6c/7f237821c9642fb2a04d2f1e88b4295677144ca93285fd76eff3bcba858d/numpy-2.4.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325", size = 16611738, upload-time = "2026-01-31T23:12:16.525Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a7/39c4cdda9f019b609b5c473899d87abff092fc908cfe4d1ecb2fcff453b0/numpy-2.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909", size = 17028888, upload-time = "2026-01-31T23:12:19.306Z" }, - { url = "https://files.pythonhosted.org/packages/da/b3/e84bb64bdfea967cc10950d71090ec2d84b49bc691df0025dddb7c26e8e3/numpy-2.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a", size = 18339556, upload-time = "2026-01-31T23:12:21.816Z" }, - { url = "https://files.pythonhosted.org/packages/88/f5/954a291bc1192a27081706862ac62bb5920fbecfbaa302f64682aa90beed/numpy-2.4.2-cp314-cp314-win32.whl", hash = "sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a", size = 6006899, upload-time = "2026-01-31T23:12:24.14Z" }, - { url = "https://files.pythonhosted.org/packages/05/cb/eff72a91b2efdd1bc98b3b8759f6a1654aa87612fc86e3d87d6fe4f948c4/numpy-2.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75", size = 12443072, upload-time = "2026-01-31T23:12:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/37/75/62726948db36a56428fce4ba80a115716dc4fad6a3a4352487f8bb950966/numpy-2.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05", size = 10494886, upload-time = "2026-01-31T23:12:28.488Z" }, - { url = "https://files.pythonhosted.org/packages/36/2f/ee93744f1e0661dc267e4b21940870cabfae187c092e1433b77b09b50ac4/numpy-2.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308", size = 14818567, upload-time = "2026-01-31T23:12:30.709Z" }, - { url = "https://files.pythonhosted.org/packages/a7/24/6535212add7d76ff938d8bdc654f53f88d35cddedf807a599e180dcb8e66/numpy-2.4.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef", size = 5328372, upload-time = "2026-01-31T23:12:32.962Z" }, - { url = "https://files.pythonhosted.org/packages/5e/9d/c48f0a035725f925634bf6b8994253b43f2047f6778a54147d7e213bc5a7/numpy-2.4.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d", size = 6649306, upload-time = "2026-01-31T23:12:34.797Z" }, - { url = "https://files.pythonhosted.org/packages/81/05/7c73a9574cd4a53a25907bad38b59ac83919c0ddc8234ec157f344d57d9a/numpy-2.4.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8", size = 15722394, upload-time = "2026-01-31T23:12:36.565Z" }, - { url = "https://files.pythonhosted.org/packages/35/fa/4de10089f21fc7d18442c4a767ab156b25c2a6eaf187c0db6d9ecdaeb43f/numpy-2.4.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5", size = 16653343, upload-time = "2026-01-31T23:12:39.188Z" }, - { url = "https://files.pythonhosted.org/packages/b8/f9/d33e4ffc857f3763a57aa85650f2e82486832d7492280ac21ba9efda80da/numpy-2.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e", size = 17078045, upload-time = "2026-01-31T23:12:42.041Z" }, - { url = "https://files.pythonhosted.org/packages/c8/b8/54bdb43b6225badbea6389fa038c4ef868c44f5890f95dd530a218706da3/numpy-2.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a", size = 18380024, upload-time = "2026-01-31T23:12:44.331Z" }, - { url = "https://files.pythonhosted.org/packages/a5/55/6e1a61ded7af8df04016d81b5b02daa59f2ea9252ee0397cb9f631efe9e5/numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443", size = 6153937, upload-time = "2026-01-31T23:12:47.229Z" }, - { url = "https://files.pythonhosted.org/packages/45/aa/fa6118d1ed6d776b0983f3ceac9b1a5558e80df9365b1c3aa6d42bf9eee4/numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236", size = 12631844, upload-time = "2026-01-31T23:12:48.997Z" }, - { url = "https://files.pythonhosted.org/packages/32/0a/2ec5deea6dcd158f254a7b372fb09cfba5719419c8d66343bab35237b3fb/numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181", size = 10565379, upload-time = "2026-01-31T23:12:51.345Z" }, -] - -[[package]] -name = "nvidia-cublas-cu12" -version = "12.8.4.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, -] - -[[package]] -name = "nvidia-cuda-cupti-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, -] - -[[package]] -name = "nvidia-cuda-nvrtc-cu12" -version = "12.8.93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, -] - -[[package]] -name = "nvidia-cuda-runtime-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, -] - -[[package]] -name = "nvidia-cudnn-cu12" -version = "9.10.2.21" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, -] - -[[package]] -name = "nvidia-cufft-cu12" -version = "11.3.3.83" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, -] - -[[package]] -name = "nvidia-cufile-cu12" -version = "1.13.1.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, -] - -[[package]] -name = "nvidia-curand-cu12" -version = "10.3.9.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, -] - -[[package]] -name = "nvidia-cusolver-cu12" -version = "11.7.3.90" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12" }, - { name = "nvidia-cusparse-cu12" }, - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, -] - -[[package]] -name = "nvidia-cusparse-cu12" -version = "12.5.8.93" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, -] - -[[package]] -name = "nvidia-cusparselt-cu12" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, -] - -[[package]] -name = "nvidia-nccl-cu12" -version = "2.27.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, -] - -[[package]] -name = "nvidia-nvjitlink-cu12" -version = "12.8.93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, -] - -[[package]] -name = "nvidia-nvshmem-cu12" -version = "3.4.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095, upload-time = "2025-09-06T00:32:31.266Z" }, -] - -[[package]] -name = "nvidia-nvtx-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, -] - -[[package]] -name = "oauthlib" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" }, -] - -[[package]] -name = "onnxruntime" -version = "1.23.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coloredlogs" }, - { name = "flatbuffers" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "sympy" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/9e/f748cd64161213adeef83d0cb16cb8ace1e62fa501033acdd9f9341fff57/onnxruntime-1.23.2-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:b8f029a6b98d3cf5be564d52802bb50a8489ab73409fa9db0bf583eabb7c2321", size = 17195929, upload-time = "2025-10-22T03:47:36.24Z" }, - { url = "https://files.pythonhosted.org/packages/91/9d/a81aafd899b900101988ead7fb14974c8a58695338ab6a0f3d6b0100f30b/onnxruntime-1.23.2-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:218295a8acae83905f6f1aed8cacb8e3eb3bd7513a13fe4ba3b2664a19fc4a6b", size = 19157705, upload-time = "2025-10-22T03:46:40.415Z" }, - { url = "https://files.pythonhosted.org/packages/3c/35/4e40f2fba272a6698d62be2cd21ddc3675edfc1a4b9ddefcc4648f115315/onnxruntime-1.23.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76ff670550dc23e58ea9bc53b5149b99a44e63b34b524f7b8547469aaa0dcb8c", size = 15226915, upload-time = "2025-10-22T03:46:27.773Z" }, - { url = "https://files.pythonhosted.org/packages/ef/88/9cc25d2bafe6bc0d4d3c1db3ade98196d5b355c0b273e6a5dc09c5d5d0d5/onnxruntime-1.23.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f9b4ae77f8e3c9bee50c27bc1beede83f786fe1d52e99ac85aa8d65a01e9b77", size = 17382649, upload-time = "2025-10-22T03:47:02.782Z" }, - { url = "https://files.pythonhosted.org/packages/c0/b4/569d298f9fc4d286c11c45e85d9ffa9e877af12ace98af8cab52396e8f46/onnxruntime-1.23.2-cp312-cp312-win_amd64.whl", hash = "sha256:25de5214923ce941a3523739d34a520aac30f21e631de53bba9174dc9c004435", size = 13470528, upload-time = "2025-10-22T03:47:28.106Z" }, - { url = "https://files.pythonhosted.org/packages/3d/41/fba0cabccecefe4a1b5fc8020c44febb334637f133acefc7ec492029dd2c/onnxruntime-1.23.2-cp313-cp313-macosx_13_0_arm64.whl", hash = "sha256:2ff531ad8496281b4297f32b83b01cdd719617e2351ffe0dba5684fb283afa1f", size = 17196337, upload-time = "2025-10-22T03:46:35.168Z" }, - { url = "https://files.pythonhosted.org/packages/fe/f9/2d49ca491c6a986acce9f1d1d5fc2099108958cc1710c28e89a032c9cfe9/onnxruntime-1.23.2-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:162f4ca894ec3de1a6fd53589e511e06ecdc3ff646849b62a9da7489dee9ce95", size = 19157691, upload-time = "2025-10-22T03:46:43.518Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a1/428ee29c6eaf09a6f6be56f836213f104618fb35ac6cc586ff0f477263eb/onnxruntime-1.23.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45d127d6e1e9b99d1ebeae9bcd8f98617a812f53f46699eafeb976275744826b", size = 15226898, upload-time = "2025-10-22T03:46:30.039Z" }, - { url = "https://files.pythonhosted.org/packages/f2/2b/b57c8a2466a3126dbe0a792f56ad7290949b02f47b86216cd47d857e4b77/onnxruntime-1.23.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8bace4e0d46480fbeeb7bbe1ffe1f080e6663a42d1086ff95c1551f2d39e7872", size = 17382518, upload-time = "2025-10-22T03:47:05.407Z" }, - { url = "https://files.pythonhosted.org/packages/4a/93/aba75358133b3a941d736816dd392f687e7eab77215a6e429879080b76b6/onnxruntime-1.23.2-cp313-cp313-win_amd64.whl", hash = "sha256:1f9cc0a55349c584f083c1c076e611a7c35d5b867d5d6e6d6c823bf821978088", size = 13470276, upload-time = "2025-10-22T03:47:31.193Z" }, - { url = "https://files.pythonhosted.org/packages/7c/3d/6830fa61c69ca8e905f237001dbfc01689a4e4ab06147020a4518318881f/onnxruntime-1.23.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9d2385e774f46ac38f02b3a91a91e30263d41b2f1f4f26ae34805b2a9ddef466", size = 15229610, upload-time = "2025-10-22T03:46:32.239Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ca/862b1e7a639460f0ca25fd5b6135fb42cf9deea86d398a92e44dfda2279d/onnxruntime-1.23.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2b9233c4947907fd1818d0e581c049c41ccc39b2856cc942ff6d26317cee145", size = 17394184, upload-time = "2025-10-22T03:47:08.127Z" }, -] - -[[package]] -name = "openai" -version = "2.16.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/6c/e4c964fcf1d527fdf4739e7cc940c60075a4114d50d03871d5d5b1e13a88/openai-2.16.0.tar.gz", hash = "sha256:42eaa22ca0d8ded4367a77374104d7a2feafee5bd60a107c3c11b5243a11cd12", size = 629649, upload-time = "2026-01-27T23:28:02.579Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/16/83/0315bf2cfd75a2ce8a7e54188e9456c60cec6c0cf66728ed07bd9859ff26/openai-2.16.0-py3-none-any.whl", hash = "sha256:5f46643a8f42899a84e80c38838135d7038e7718333ce61396994f887b09a59b", size = 1068612, upload-time = "2026-01-27T23:28:00.356Z" }, -] - -[[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 = "opentelemetry-api" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, -] - -[[package]] -name = "opentelemetry-exporter-otlp-proto-common" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-proto" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e9/9d/22d241b66f7bbde88a3bfa6847a351d2c46b84de23e71222c6aae25c7050/opentelemetry_exporter_otlp_proto_common-1.39.1.tar.gz", hash = "sha256:763370d4737a59741c89a67b50f9e39271639ee4afc999dadfe768541c027464", size = 20409, upload-time = "2025-12-11T13:32:40.885Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/02/ffc3e143d89a27ac21fd557365b98bd0653b98de8a101151d5805b5d4c33/opentelemetry_exporter_otlp_proto_common-1.39.1-py3-none-any.whl", hash = "sha256:08f8a5862d64cc3435105686d0216c1365dc5701f86844a8cd56597d0c764fde", size = 18366, upload-time = "2025-12-11T13:32:20.2Z" }, -] - -[[package]] -name = "opentelemetry-exporter-otlp-proto-grpc" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "googleapis-common-protos" }, - { name = "grpcio" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-otlp-proto-common" }, - { name = "opentelemetry-proto" }, - { name = "opentelemetry-sdk" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/53/48/b329fed2c610c2c32c9366d9dc597202c9d1e58e631c137ba15248d8850f/opentelemetry_exporter_otlp_proto_grpc-1.39.1.tar.gz", hash = "sha256:772eb1c9287485d625e4dbe9c879898e5253fea111d9181140f51291b5fec3ad", size = 24650, upload-time = "2025-12-11T13:32:41.429Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/a3/cc9b66575bd6597b98b886a2067eea2693408d2d5f39dad9ab7fc264f5f3/opentelemetry_exporter_otlp_proto_grpc-1.39.1-py3-none-any.whl", hash = "sha256:fa1c136a05c7e9b4c09f739469cbdb927ea20b34088ab1d959a849b5cc589c18", size = 19766, upload-time = "2025-12-11T13:32:21.027Z" }, -] - -[[package]] -name = "opentelemetry-exporter-prometheus" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-sdk" }, - { name = "prometheus-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/14/39/7dafa6fff210737267bed35a8855b6ac7399b9e582b8cf1f25f842517012/opentelemetry_exporter_prometheus-0.60b1.tar.gz", hash = "sha256:a4011b46906323f71724649d301b4dc188aaa068852e814f4df38cc76eac616b", size = 14976, upload-time = "2025-12-11T13:32:42.944Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/0d/4be6bf5477a3eb3d917d2f17d3c0b6720cd6cb97898444a61d43cc983f5c/opentelemetry_exporter_prometheus-0.60b1-py3-none-any.whl", hash = "sha256:49f59178de4f4590e3cef0b8b95cf6e071aae70e1f060566df5546fad773b8fd", size = 13019, upload-time = "2025-12-11T13:32:23.974Z" }, -] - -[[package]] -name = "opentelemetry-instrumentation" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "packaging" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/0f/7e6b713ac117c1f5e4e3300748af699b9902a2e5e34c9cf443dde25a01fa/opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a", size = 31706, upload-time = "2025-12-11T13:36:42.515Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096, upload-time = "2025-12-11T13:35:33.067Z" }, -] - -[[package]] -name = "opentelemetry-proto" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/1d/f25d76d8260c156c40c97c9ed4511ec0f9ce353f8108ca6e7561f82a06b2/opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8", size = 46152, upload-time = "2025-12-11T13:32:48.681Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/95/b40c96a7b5203005a0b03d8ce8cd212ff23f1793d5ba289c87a097571b18/opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007", size = 72535, upload-time = "2025-12-11T13:32:33.866Z" }, -] - -[[package]] -name = "opentelemetry-sdk" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, -] - -[[package]] -name = "orjson" -version = "3.11.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/45/b268004f745ede84e5798b48ee12b05129d19235d0e15267aa57dcdb400b/orjson-3.11.7.tar.gz", hash = "sha256:9b1a67243945819ce55d24a30b59d6a168e86220452d2c96f4d1f093e71c0c49", size = 6144992, upload-time = "2026-02-02T15:38:49.29Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/bf/76f4f1665f6983385938f0e2a5d7efa12a58171b8456c252f3bae8a4cf75/orjson-3.11.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bd03ea7606833655048dab1a00734a2875e3e86c276e1d772b2a02556f0d895f", size = 228545, upload-time = "2026-02-02T15:37:46.376Z" }, - { url = "https://files.pythonhosted.org/packages/79/53/6c72c002cb13b5a978a068add59b25a8bdf2800ac1c9c8ecdb26d6d97064/orjson-3.11.7-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:89e440ebc74ce8ab5c7bc4ce6757b4a6b1041becb127df818f6997b5c71aa60b", size = 125224, upload-time = "2026-02-02T15:37:47.697Z" }, - { url = "https://files.pythonhosted.org/packages/2c/83/10e48852865e5dd151bdfe652c06f7da484578ed02c5fca938e3632cb0b8/orjson-3.11.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ede977b5fe5ac91b1dffc0a517ca4542d2ec8a6a4ff7b2652d94f640796342a", size = 128154, upload-time = "2026-02-02T15:37:48.954Z" }, - { url = "https://files.pythonhosted.org/packages/6e/52/a66e22a2b9abaa374b4a081d410edab6d1e30024707b87eab7c734afe28d/orjson-3.11.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b7b1dae39230a393df353827c855a5f176271c23434cfd2db74e0e424e693e10", size = 123548, upload-time = "2026-02-02T15:37:50.187Z" }, - { url = "https://files.pythonhosted.org/packages/de/38/605d371417021359f4910c496f764c48ceb8997605f8c25bf1dfe58c0ebe/orjson-3.11.7-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed46f17096e28fb28d2975834836a639af7278aa87c84f68ab08fbe5b8bd75fa", size = 129000, upload-time = "2026-02-02T15:37:51.426Z" }, - { url = "https://files.pythonhosted.org/packages/44/98/af32e842b0ffd2335c89714d48ca4e3917b42f5d6ee5537832e069a4b3ac/orjson-3.11.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3726be79e36e526e3d9c1aceaadbfb4a04ee80a72ab47b3f3c17fefb9812e7b8", size = 141686, upload-time = "2026-02-02T15:37:52.607Z" }, - { url = "https://files.pythonhosted.org/packages/96/0b/fc793858dfa54be6feee940c1463370ece34b3c39c1ca0aa3845f5ba9892/orjson-3.11.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0724e265bc548af1dedebd9cb3d24b4e1c1e685a343be43e87ba922a5c5fff2f", size = 130812, upload-time = "2026-02-02T15:37:53.944Z" }, - { url = "https://files.pythonhosted.org/packages/dc/91/98a52415059db3f374757d0b7f0f16e3b5cd5976c90d1c2b56acaea039e6/orjson-3.11.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7745312efa9e11c17fbd3cb3097262d079da26930ae9ae7ba28fb738367cbad", size = 133440, upload-time = "2026-02-02T15:37:55.615Z" }, - { url = "https://files.pythonhosted.org/packages/dc/b6/cb540117bda61791f46381f8c26c8f93e802892830a6055748d3bb1925ab/orjson-3.11.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f904c24bdeabd4298f7a977ef14ca2a022ca921ed670b92ecd16ab6f3d01f867", size = 138386, upload-time = "2026-02-02T15:37:56.814Z" }, - { url = "https://files.pythonhosted.org/packages/63/1a/50a3201c334a7f17c231eee5f841342190723794e3b06293f26e7cf87d31/orjson-3.11.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b9fc4d0f81f394689e0814617aadc4f2ea0e8025f38c226cbf22d3b5ddbf025d", size = 408853, upload-time = "2026-02-02T15:37:58.291Z" }, - { url = "https://files.pythonhosted.org/packages/87/cd/8de1c67d0be44fdc22701e5989c0d015a2adf391498ad42c4dc589cd3013/orjson-3.11.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:849e38203e5be40b776ed2718e587faf204d184fc9a008ae441f9442320c0cab", size = 144130, upload-time = "2026-02-02T15:38:00.163Z" }, - { url = "https://files.pythonhosted.org/packages/0f/fe/d605d700c35dd55f51710d159fc54516a280923cd1b7e47508982fbb387d/orjson-3.11.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4682d1db3bcebd2b64757e0ddf9e87ae5f00d29d16c5cdf3a62f561d08cc3dd2", size = 134818, upload-time = "2026-02-02T15:38:01.507Z" }, - { url = "https://files.pythonhosted.org/packages/e4/e4/15ecc67edb3ddb3e2f46ae04475f2d294e8b60c1825fbe28a428b93b3fbd/orjson-3.11.7-cp312-cp312-win32.whl", hash = "sha256:f4f7c956b5215d949a1f65334cf9d7612dde38f20a95f2315deef167def91a6f", size = 127923, upload-time = "2026-02-02T15:38:02.75Z" }, - { url = "https://files.pythonhosted.org/packages/34/70/2e0855361f76198a3965273048c8e50a9695d88cd75811a5b46444895845/orjson-3.11.7-cp312-cp312-win_amd64.whl", hash = "sha256:bf742e149121dc5648ba0a08ea0871e87b660467ef168a3a5e53bc1fbd64bb74", size = 125007, upload-time = "2026-02-02T15:38:04.032Z" }, - { url = "https://files.pythonhosted.org/packages/68/40/c2051bd19fc467610fed469dc29e43ac65891571138f476834ca192bc290/orjson-3.11.7-cp312-cp312-win_arm64.whl", hash = "sha256:26c3b9132f783b7d7903bf1efb095fed8d4a3a85ec0d334ee8beff3d7a4749d5", size = 126089, upload-time = "2026-02-02T15:38:05.297Z" }, - { url = "https://files.pythonhosted.org/packages/89/25/6e0e52cac5aab51d7b6dcd257e855e1dec1c2060f6b28566c509b4665f62/orjson-3.11.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1d98b30cc1313d52d4af17d9c3d307b08389752ec5f2e5febdfada70b0f8c733", size = 228390, upload-time = "2026-02-02T15:38:06.8Z" }, - { url = "https://files.pythonhosted.org/packages/a5/29/a77f48d2fc8a05bbc529e5ff481fb43d914f9e383ea2469d4f3d51df3d00/orjson-3.11.7-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:d897e81f8d0cbd2abb82226d1860ad2e1ab3ff16d7b08c96ca00df9d45409ef4", size = 125189, upload-time = "2026-02-02T15:38:08.181Z" }, - { url = "https://files.pythonhosted.org/packages/89/25/0a16e0729a0e6a1504f9d1a13cdd365f030068aab64cec6958396b9969d7/orjson-3.11.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:814be4b49b228cfc0b3c565acf642dd7d13538f966e3ccde61f4f55be3e20785", size = 128106, upload-time = "2026-02-02T15:38:09.41Z" }, - { url = "https://files.pythonhosted.org/packages/66/da/a2e505469d60666a05ab373f1a6322eb671cb2ba3a0ccfc7d4bc97196787/orjson-3.11.7-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d06e5c5fed5caedd2e540d62e5b1c25e8c82431b9e577c33537e5fa4aa909539", size = 123363, upload-time = "2026-02-02T15:38:10.73Z" }, - { url = "https://files.pythonhosted.org/packages/23/bf/ed73f88396ea35c71b38961734ea4a4746f7ca0768bf28fd551d37e48dd0/orjson-3.11.7-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31c80ce534ac4ea3739c5ee751270646cbc46e45aea7576a38ffec040b4029a1", size = 129007, upload-time = "2026-02-02T15:38:12.138Z" }, - { url = "https://files.pythonhosted.org/packages/73/3c/b05d80716f0225fc9008fbf8ab22841dcc268a626aa550561743714ce3bf/orjson-3.11.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f50979824bde13d32b4320eedd513431c921102796d86be3eee0b58e58a3ecd1", size = 141667, upload-time = "2026-02-02T15:38:13.398Z" }, - { url = "https://files.pythonhosted.org/packages/61/e8/0be9b0addd9bf86abfc938e97441dcd0375d494594b1c8ad10fe57479617/orjson-3.11.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e54f3808e2b6b945078c41aa8d9b5834b28c50843846e97807e5adb75fa9705", size = 130832, upload-time = "2026-02-02T15:38:14.698Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ec/c68e3b9021a31d9ec15a94931db1410136af862955854ed5dd7e7e4f5bff/orjson-3.11.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12b80df61aab7b98b490fe9e4879925ba666fccdfcd175252ce4d9035865ace", size = 133373, upload-time = "2026-02-02T15:38:16.109Z" }, - { url = "https://files.pythonhosted.org/packages/d2/45/f3466739aaafa570cc8e77c6dbb853c48bf56e3b43738020e2661e08b0ac/orjson-3.11.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:996b65230271f1a97026fd0e6a753f51fbc0c335d2ad0c6201f711b0da32693b", size = 138307, upload-time = "2026-02-02T15:38:17.453Z" }, - { url = "https://files.pythonhosted.org/packages/e1/84/9f7f02288da1ffb31405c1be07657afd1eecbcb4b64ee2817b6fe0f785fa/orjson-3.11.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ab49d4b2a6a1d415ddb9f37a21e02e0d5dbfe10b7870b21bf779fc21e9156157", size = 408695, upload-time = "2026-02-02T15:38:18.831Z" }, - { url = "https://files.pythonhosted.org/packages/18/07/9dd2f0c0104f1a0295ffbe912bc8d63307a539b900dd9e2c48ef7810d971/orjson-3.11.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:390a1dce0c055ddf8adb6aa94a73b45a4a7d7177b5c584b8d1c1947f2ba60fb3", size = 144099, upload-time = "2026-02-02T15:38:20.28Z" }, - { url = "https://files.pythonhosted.org/packages/a5/66/857a8e4a3292e1f7b1b202883bcdeb43a91566cf59a93f97c53b44bd6801/orjson-3.11.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1eb80451a9c351a71dfaf5b7ccc13ad065405217726b59fdbeadbcc544f9d223", size = 134806, upload-time = "2026-02-02T15:38:22.186Z" }, - { url = "https://files.pythonhosted.org/packages/0a/5b/6ebcf3defc1aab3a338ca777214966851e92efb1f30dc7fc8285216e6d1b/orjson-3.11.7-cp313-cp313-win32.whl", hash = "sha256:7477aa6a6ec6139c5cb1cc7b214643592169a5494d200397c7fc95d740d5fcf3", size = 127914, upload-time = "2026-02-02T15:38:23.511Z" }, - { url = "https://files.pythonhosted.org/packages/00/04/c6f72daca5092e3117840a1b1e88dfc809cc1470cf0734890d0366b684a1/orjson-3.11.7-cp313-cp313-win_amd64.whl", hash = "sha256:b9f95dcdea9d4f805daa9ddf02617a89e484c6985fa03055459f90e87d7a0757", size = 124986, upload-time = "2026-02-02T15:38:24.836Z" }, - { url = "https://files.pythonhosted.org/packages/03/ba/077a0f6f1085d6b806937246860fafbd5b17f3919c70ee3f3d8d9c713f38/orjson-3.11.7-cp313-cp313-win_arm64.whl", hash = "sha256:800988273a014a0541483dc81021247d7eacb0c845a9d1a34a422bc718f41539", size = 126045, upload-time = "2026-02-02T15:38:26.216Z" }, - { url = "https://files.pythonhosted.org/packages/e9/1e/745565dca749813db9a093c5ebc4bac1a9475c64d54b95654336ac3ed961/orjson-3.11.7-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:de0a37f21d0d364954ad5de1970491d7fbd0fb1ef7417d4d56a36dc01ba0c0a0", size = 228391, upload-time = "2026-02-02T15:38:27.757Z" }, - { url = "https://files.pythonhosted.org/packages/46/19/e40f6225da4d3aa0c8dc6e5219c5e87c2063a560fe0d72a88deb59776794/orjson-3.11.7-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:c2428d358d85e8da9d37cba18b8c4047c55222007a84f97156a5b22028dfbfc0", size = 125188, upload-time = "2026-02-02T15:38:29.241Z" }, - { url = "https://files.pythonhosted.org/packages/9d/7e/c4de2babef2c0817fd1f048fd176aa48c37bec8aef53d2fa932983032cce/orjson-3.11.7-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c4bc6c6ac52cdaa267552544c73e486fecbd710b7ac09bc024d5a78555a22f6", size = 128097, upload-time = "2026-02-02T15:38:30.618Z" }, - { url = "https://files.pythonhosted.org/packages/eb/74/233d360632bafd2197f217eee7fb9c9d0229eac0c18128aee5b35b0014fe/orjson-3.11.7-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd0d68edd7dfca1b2eca9361a44ac9f24b078de3481003159929a0573f21a6bf", size = 123364, upload-time = "2026-02-02T15:38:32.363Z" }, - { url = "https://files.pythonhosted.org/packages/79/51/af79504981dd31efe20a9e360eb49c15f06df2b40e7f25a0a52d9ae888e8/orjson-3.11.7-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:623ad1b9548ef63886319c16fa317848e465a21513b31a6ad7b57443c3e0dcf5", size = 129076, upload-time = "2026-02-02T15:38:33.68Z" }, - { url = "https://files.pythonhosted.org/packages/67/e2/da898eb68b72304f8de05ca6715870d09d603ee98d30a27e8a9629abc64b/orjson-3.11.7-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6e776b998ac37c0396093d10290e60283f59cfe0fc3fccbd0ccc4bd04dd19892", size = 141705, upload-time = "2026-02-02T15:38:34.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/89/15364d92acb3d903b029e28d834edb8780c2b97404cbf7929aa6b9abdb24/orjson-3.11.7-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:652c6c3af76716f4a9c290371ba2e390ede06f6603edb277b481daf37f6f464e", size = 130855, upload-time = "2026-02-02T15:38:36.379Z" }, - { url = "https://files.pythonhosted.org/packages/c2/8b/ecdad52d0b38d4b8f514be603e69ccd5eacf4e7241f972e37e79792212ec/orjson-3.11.7-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a56df3239294ea5964adf074c54bcc4f0ccd21636049a2cf3ca9cf03b5d03cf1", size = 133386, upload-time = "2026-02-02T15:38:37.704Z" }, - { url = "https://files.pythonhosted.org/packages/b9/0e/45e1dcf10e17d0924b7c9162f87ec7b4ca79e28a0548acf6a71788d3e108/orjson-3.11.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bda117c4148e81f746655d5a3239ae9bd00cb7bc3ca178b5fc5a5997e9744183", size = 138295, upload-time = "2026-02-02T15:38:39.096Z" }, - { url = "https://files.pythonhosted.org/packages/63/d7/4d2e8b03561257af0450f2845b91fbd111d7e526ccdf737267108075e0ba/orjson-3.11.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:23d6c20517a97a9daf1d48b580fcdc6f0516c6f4b5038823426033690b4d2650", size = 408720, upload-time = "2026-02-02T15:38:40.634Z" }, - { url = "https://files.pythonhosted.org/packages/78/cf/d45343518282108b29c12a65892445fc51f9319dc3c552ceb51bb5905ed2/orjson-3.11.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8ff206156006da5b847c9304b6308a01e8cdbc8cce824e2779a5ba71c3def141", size = 144152, upload-time = "2026-02-02T15:38:42.262Z" }, - { url = "https://files.pythonhosted.org/packages/a9/3a/d6001f51a7275aacd342e77b735c71fa04125a3f93c36fee4526bc8c654e/orjson-3.11.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:962d046ee1765f74a1da723f4b33e3b228fe3a48bd307acce5021dfefe0e29b2", size = 134814, upload-time = "2026-02-02T15:38:43.627Z" }, - { url = "https://files.pythonhosted.org/packages/1d/d3/f19b47ce16820cc2c480f7f1723e17f6d411b3a295c60c8ad3aa9ff1c96a/orjson-3.11.7-cp314-cp314-win32.whl", hash = "sha256:89e13dd3f89f1c38a9c9eba5fbf7cdc2d1feca82f5f290864b4b7a6aac704576", size = 127997, upload-time = "2026-02-02T15:38:45.06Z" }, - { url = "https://files.pythonhosted.org/packages/12/df/172771902943af54bf661a8d102bdf2e7f932127968080632bda6054b62c/orjson-3.11.7-cp314-cp314-win_amd64.whl", hash = "sha256:845c3e0d8ded9c9271cd79596b9b552448b885b97110f628fb687aee2eed11c1", size = 124985, upload-time = "2026-02-02T15:38:46.388Z" }, - { url = "https://files.pythonhosted.org/packages/6f/1c/f2a8d8a1b17514660a614ce5f7aac74b934e69f5abc2700cc7ced882a009/orjson-3.11.7-cp314-cp314-win_arm64.whl", hash = "sha256:4a2e9c5be347b937a2e0203866f12bba36082e89b402ddb9e927d5822e43088d", size = 126038, upload-time = "2026-02-02T15:38:47.703Z" }, -] - -[[package]] -name = "overrides" -version = "7.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812, upload-time = "2024-01-27T21:01:33.423Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832, upload-time = "2024-01-27T21:01:31.393Z" }, -] - -[[package]] -name = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[package]] -name = "parso" -version = "0.8.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" }, -] - -[[package]] -name = "passlib" -version = "1.7.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/06/9da9ee59a67fae7761aab3ccc84fa4f3f33f125b370f1ccdb915bf967c11/passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04", size = 689844, upload-time = "2020-10-08T19:00:52.121Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/a4/ab6b7589382ca3df236e03faa71deac88cae040af60c071a78d254a62172/passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1", size = 525554, upload-time = "2020-10-08T19:00:49.856Z" }, -] - -[package.optional-dependencies] -bcrypt = [ - { name = "bcrypt" }, -] - -[[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 = "1.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, -] - -[[package]] -name = "pathvalidate" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262, upload-time = "2025-06-15T09:07:20.736Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305, upload-time = "2025-06-15T09:07:19.117Z" }, -] - -[[package]] -name = "pexpect" -version = "4.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ptyprocess" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, -] - -[[package]] -name = "pillow" -version = "11.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800, upload-time = "2025-07-01T09:14:17.648Z" }, - { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296, upload-time = "2025-07-01T09:14:19.828Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726, upload-time = "2025-07-03T13:10:04.448Z" }, - { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652, upload-time = "2025-07-03T13:10:10.391Z" }, - { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787, upload-time = "2025-07-01T09:14:21.63Z" }, - { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236, upload-time = "2025-07-01T09:14:23.321Z" }, - { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950, upload-time = "2025-07-01T09:14:25.237Z" }, - { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", size = 6723358, upload-time = "2025-07-01T09:14:27.053Z" }, - { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", size = 6275079, upload-time = "2025-07-01T09:14:30.104Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", size = 6986324, upload-time = "2025-07-01T09:14:31.899Z" }, - { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", size = 2423067, upload-time = "2025-07-01T09:14:33.709Z" }, - { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328, upload-time = "2025-07-01T09:14:35.276Z" }, - { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652, upload-time = "2025-07-01T09:14:37.203Z" }, - { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443, upload-time = "2025-07-01T09:14:39.344Z" }, - { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474, upload-time = "2025-07-01T09:14:41.843Z" }, - { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038, upload-time = "2025-07-01T09:14:44.008Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407, upload-time = "2025-07-03T13:10:15.628Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094, upload-time = "2025-07-03T13:10:21.857Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503, upload-time = "2025-07-01T09:14:45.698Z" }, - { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574, upload-time = "2025-07-01T09:14:47.415Z" }, - { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060, upload-time = "2025-07-01T09:14:49.636Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407, upload-time = "2025-07-01T09:14:51.962Z" }, - { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841, upload-time = "2025-07-01T09:14:54.142Z" }, - { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450, upload-time = "2025-07-01T09:14:56.436Z" }, - { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055, upload-time = "2025-07-01T09:14:58.072Z" }, - { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110, upload-time = "2025-07-01T09:14:59.79Z" }, - { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547, upload-time = "2025-07-01T09:15:01.648Z" }, - { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554, upload-time = "2025-07-03T13:10:27.018Z" }, - { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132, upload-time = "2025-07-03T13:10:33.01Z" }, - { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001, upload-time = "2025-07-01T09:15:03.365Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814, upload-time = "2025-07-01T09:15:05.655Z" }, - { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124, upload-time = "2025-07-01T09:15:07.358Z" }, - { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186, upload-time = "2025-07-01T09:15:09.317Z" }, - { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546, upload-time = "2025-07-01T09:15:11.311Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102, upload-time = "2025-07-01T09:15:13.164Z" }, - { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" }, - { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" }, - { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" }, - { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" }, - { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" }, - { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" }, - { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" }, - { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" }, - { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" }, - { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" }, - { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" }, - { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" }, - { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" }, - { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" }, - { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" }, - { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" }, - { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" }, - { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" }, - { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, -] - -[[package]] -name = "playwright" -version = "1.58.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "greenlet" }, - { name = "pyee" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/c9/9c6061d5703267f1baae6a4647bfd1862e386fbfdb97d889f6f6ae9e3f64/playwright-1.58.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:96e3204aac292ee639edbfdef6298b4be2ea0a55a16b7068df91adac077cc606", size = 42251098, upload-time = "2026-01-30T15:09:24.028Z" }, - { url = "https://files.pythonhosted.org/packages/e0/40/59d34a756e02f8c670f0fee987d46f7ee53d05447d43cd114ca015cb168c/playwright-1.58.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:70c763694739d28df71ed578b9c8202bb83e8fe8fb9268c04dd13afe36301f71", size = 41039625, upload-time = "2026-01-30T15:09:27.558Z" }, - { url = "https://files.pythonhosted.org/packages/e1/ee/3ce6209c9c74a650aac9028c621f357a34ea5cd4d950700f8e2c4b7fe2c4/playwright-1.58.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:185e0132578733d02802dfddfbbc35f42be23a45ff49ccae5081f25952238117", size = 42251098, upload-time = "2026-01-30T15:09:30.461Z" }, - { url = "https://files.pythonhosted.org/packages/f1/af/009958cbf23fac551a940d34e3206e6c7eed2b8c940d0c3afd1feb0b0589/playwright-1.58.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:c95568ba1eda83812598c1dc9be60b4406dffd60b149bc1536180ad108723d6b", size = 46235268, upload-time = "2026-01-30T15:09:33.787Z" }, - { url = "https://files.pythonhosted.org/packages/d9/a6/0e66ad04b6d3440dae73efb39540c5685c5fc95b17c8b29340b62abbd952/playwright-1.58.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f9999948f1ab541d98812de25e3a8c410776aa516d948807140aff797b4bffa", size = 45964214, upload-time = "2026-01-30T15:09:36.751Z" }, - { url = "https://files.pythonhosted.org/packages/0e/4b/236e60ab9f6d62ed0fd32150d61f1f494cefbf02304c0061e78ed80c1c32/playwright-1.58.0-py3-none-win32.whl", hash = "sha256:1e03be090e75a0fabbdaeab65ce17c308c425d879fa48bb1d7986f96bfad0b99", size = 36815998, upload-time = "2026-01-30T15:09:39.627Z" }, - { url = "https://files.pythonhosted.org/packages/41/f8/5ec599c5e59d2f2f336a05b4f318e733077cd5044f24adb6f86900c3e6a7/playwright-1.58.0-py3-none-win_amd64.whl", hash = "sha256:a2bf639d0ce33b3ba38de777e08697b0d8f3dc07ab6802e4ac53fb65e3907af8", size = 36816005, upload-time = "2026-01-30T15:09:42.449Z" }, - { url = "https://files.pythonhosted.org/packages/c8/c4/cc0229fea55c87d6c9c67fe44a21e2cd28d1d558a5478ed4d617e9fb0c93/playwright-1.58.0-py3-none-win_arm64.whl", hash = "sha256:32ffe5c303901a13a0ecab91d1c3f74baf73b84f4bedbb6b935f5bc11cc98e1b", size = 33085919, upload-time = "2026-01-30T15:09:45.71Z" }, -] - -[[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 = "polars" -version = "1.37.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "polars-runtime-32" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/84/ae/dfebf31b9988c20998140b54d5b521f64ce08879f2c13d9b4d44d7c87e32/polars-1.37.1.tar.gz", hash = "sha256:0309e2a4633e712513401964b4d95452f124ceabf7aec6db50affb9ced4a274e", size = 715572, upload-time = "2026-01-12T23:27:03.267Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/75/ec73e38812bca7c2240aff481b9ddff20d1ad2f10dee4b3353f5eeaacdab/polars-1.37.1-py3-none-any.whl", hash = "sha256:377fed8939a2f1223c1563cfabdc7b4a3d6ff846efa1f2ddeb8644fafd9b1aff", size = 805749, upload-time = "2026-01-12T23:25:48.595Z" }, -] - -[[package]] -name = "polars-runtime-32" -version = "1.37.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/40/0b/addabe5e8d28a5a4c9887a08907be7ddc3fce892dc38f37d14b055438a57/polars_runtime_32-1.37.1.tar.gz", hash = "sha256:68779d4a691da20a5eb767d74165a8f80a2bdfbde4b54acf59af43f7fa028d8f", size = 2818945, upload-time = "2026-01-12T23:27:04.653Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/a2/e828ea9f845796de02d923edb790e408ca0b560cd68dbd74bb99a1b3c461/polars_runtime_32-1.37.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0b8d4d73ea9977d3731927740e59d814647c5198bdbe359bcf6a8bfce2e79771", size = 43499912, upload-time = "2026-01-12T23:25:51.182Z" }, - { url = "https://files.pythonhosted.org/packages/7e/46/81b71b7aa9e3703ee6e4ef1f69a87e40f58ea7c99212bf49a95071e99c8c/polars_runtime_32-1.37.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:c682bf83f5f352e5e02f5c16c652c48ca40442f07b236f30662b22217320ce76", size = 39695707, upload-time = "2026-01-12T23:25:54.289Z" }, - { url = "https://files.pythonhosted.org/packages/81/2e/20009d1fde7ee919e24040f5c87cb9d0e4f8e3f109b74ba06bc10c02459c/polars_runtime_32-1.37.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc82b5bbe70ca1a4b764eed1419f6336752d6ba9fc1245388d7f8b12438afa2c", size = 41467034, upload-time = "2026-01-12T23:25:56.925Z" }, - { url = "https://files.pythonhosted.org/packages/eb/21/9b55bea940524324625b1e8fd96233290303eb1bf2c23b54573487bbbc25/polars_runtime_32-1.37.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8362d11ac5193b994c7e9048ffe22ccfb976699cfbf6e128ce0302e06728894", size = 45142711, upload-time = "2026-01-12T23:26:00.817Z" }, - { url = "https://files.pythonhosted.org/packages/8c/25/c5f64461aeccdac6834a89f826d051ccd3b4ce204075e562c87a06ed2619/polars_runtime_32-1.37.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:04f5d5a2f013dca7391b7d8e7672fa6d37573a87f1d45d3dd5f0d9b5565a4b0f", size = 41638564, upload-time = "2026-01-12T23:26:04.186Z" }, - { url = "https://files.pythonhosted.org/packages/35/af/509d3cf6c45e764ccf856beaae26fc34352f16f10f94a7839b1042920a73/polars_runtime_32-1.37.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fbfde7c0ca8209eeaed546e4a32cca1319189aa61c5f0f9a2b4494262bd0c689", size = 44721136, upload-time = "2026-01-12T23:26:07.088Z" }, - { url = "https://files.pythonhosted.org/packages/af/d1/5c0a83a625f72beef59394bebc57d12637997632a4f9d3ab2ffc2cc62bbf/polars_runtime_32-1.37.1-cp310-abi3-win_amd64.whl", hash = "sha256:da3d3642ae944e18dd17109d2a3036cb94ce50e5495c5023c77b1599d4c861bc", size = 44948288, upload-time = "2026-01-12T23:26:10.214Z" }, - { url = "https://files.pythonhosted.org/packages/10/f3/061bb702465904b6502f7c9081daee34b09ccbaa4f8c94cf43a2a3b6dd6f/polars_runtime_32-1.37.1-cp310-abi3-win_arm64.whl", hash = "sha256:55f2c4847a8d2e267612f564de7b753a4bde3902eaabe7b436a0a4abf75949a0", size = 41001914, upload-time = "2026-01-12T23:26:12.997Z" }, -] - -[[package]] -name = "posthog" -version = "5.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "backoff" }, - { name = "distro" }, - { name = "python-dateutil" }, - { name = "requests" }, - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/48/20/60ae67bb9d82f00427946218d49e2e7e80fb41c15dc5019482289ec9ce8d/posthog-5.4.0.tar.gz", hash = "sha256:701669261b8d07cdde0276e5bc096b87f9e200e3b9589c5ebff14df658c5893c", size = 88076, upload-time = "2025-06-20T23:19:23.485Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/98/e480cab9a08d1c09b1c59a93dade92c1bb7544826684ff2acbfd10fcfbd4/posthog-5.4.0-py3-none-any.whl", hash = "sha256:284dfa302f64353484420b52d4ad81ff5c2c2d1d607c4e2db602ac72761831bd", size = 105364, upload-time = "2025-06-20T23:19:22.001Z" }, -] - -[[package]] -name = "prometheus-client" -version = "0.24.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616, upload-time = "2026-01-14T15:26:26.965Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057, upload-time = "2026-01-14T15:26:24.42Z" }, -] - -[[package]] -name = "prompt-toolkit" -version = "3.0.52" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wcwidth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, -] - -[[package]] -name = "propcache" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, - { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, - { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, - { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, - { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, - { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, - { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, - { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, - { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, - { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, - { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, - { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, - { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, - { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, - { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, - { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, - { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, - { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, - { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, - { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, - { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, - { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, - { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, - { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, - { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, - { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, - { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, - { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, - { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, - { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, - { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, - { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, - { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, - { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, - { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, - { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, - { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, - { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, - { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, - { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, - { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, - { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, - { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, - { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, - { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, - { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, - { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, - { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, - { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, - { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, - { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, - { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, - { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, - { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, - { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, -] - -[[package]] -name = "protobuf" -version = "6.33.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/25/7c72c307aafc96fa87062aa6291d9f7c94836e43214d43722e86037aac02/protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c", size = 444465, upload-time = "2026-01-29T21:51:33.494Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/79/af92d0a8369732b027e6d6084251dd8e782c685c72da161bd4a2e00fbabb/protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b", size = 425769, upload-time = "2026-01-29T21:51:21.751Z" }, - { url = "https://files.pythonhosted.org/packages/55/75/bb9bc917d10e9ee13dee8607eb9ab963b7cf8be607c46e7862c748aa2af7/protobuf-6.33.5-cp310-abi3-win_amd64.whl", hash = "sha256:3093804752167bcab3998bec9f1048baae6e29505adaf1afd14a37bddede533c", size = 437118, upload-time = "2026-01-29T21:51:24.022Z" }, - { url = "https://files.pythonhosted.org/packages/a2/6b/e48dfc1191bc5b52950246275bf4089773e91cb5ba3592621723cdddca62/protobuf-6.33.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:a5cb85982d95d906df1e2210e58f8e4f1e3cdc088e52c921a041f9c9a0386de5", size = 427766, upload-time = "2026-01-29T21:51:25.413Z" }, - { url = "https://files.pythonhosted.org/packages/4e/b1/c79468184310de09d75095ed1314b839eb2f72df71097db9d1404a1b2717/protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190", size = 324638, upload-time = "2026-01-29T21:51:26.423Z" }, - { url = "https://files.pythonhosted.org/packages/c5/f5/65d838092fd01c44d16037953fd4c2cc851e783de9b8f02b27ec4ffd906f/protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd", size = 339411, upload-time = "2026-01-29T21:51:27.446Z" }, - { url = "https://files.pythonhosted.org/packages/9b/53/a9443aa3ca9ba8724fdfa02dd1887c1bcd8e89556b715cfbacca6b63dbec/protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0", size = 323465, upload-time = "2026-01-29T21:51:28.925Z" }, - { url = "https://files.pythonhosted.org/packages/57/bf/2086963c69bdac3d7cff1cc7ff79b8ce5ea0bec6797a017e1be338a46248/protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02", size = 170687, upload-time = "2026-01-29T21:51:32.557Z" }, -] - -[[package]] -name = "psutil" -version = "7.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, - { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, - { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, - { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, - { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, - { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, - { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, - { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, - { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, - { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, - { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, - { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, - { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, - { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, - { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, - { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, - { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, - { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, - { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, -] - -[[package]] -name = "ptyprocess" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, -] - -[[package]] -name = "pure-eval" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, -] - -[[package]] -name = "py-key-value-aio" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "py-key-value-shared" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/ce/3136b771dddf5ac905cc193b461eb67967cf3979688c6696e1f2cdcde7ea/py_key_value_aio-0.3.0.tar.gz", hash = "sha256:858e852fcf6d696d231266da66042d3355a7f9871650415feef9fca7a6cd4155", size = 50801, upload-time = "2025-11-17T16:50:04.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/10/72f6f213b8f0bce36eff21fda0a13271834e9eeff7f9609b01afdc253c79/py_key_value_aio-0.3.0-py3-none-any.whl", hash = "sha256:1c781915766078bfd608daa769fefb97e65d1d73746a3dfb640460e322071b64", size = 96342, upload-time = "2025-11-17T16:50:03.801Z" }, -] - -[package.optional-dependencies] -disk = [ - { name = "diskcache" }, - { name = "pathvalidate" }, -] -keyring = [ - { name = "keyring" }, -] -memory = [ - { name = "cachetools" }, -] -redis = [ - { name = "redis" }, -] - -[[package]] -name = "py-key-value-shared" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/e4/1971dfc4620a3a15b4579fe99e024f5edd6e0967a71154771a059daff4db/py_key_value_shared-0.3.0.tar.gz", hash = "sha256:8fdd786cf96c3e900102945f92aa1473138ebe960ef49da1c833790160c28a4b", size = 11666, upload-time = "2025-11-17T16:50:06.849Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e4/b8b0a03ece72f47dce2307d36e1c34725b7223d209fc679315ffe6a4e2c3/py_key_value_shared-0.3.0-py3-none-any.whl", hash = "sha256:5b0efba7ebca08bb158b1e93afc2f07d30b8f40c2fc12ce24a4c0d84f42f9298", size = 19560, upload-time = "2025-11-17T16:50:05.954Z" }, -] - -[[package]] -name = "py-rust-stemmers" -version = "0.1.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/63/4fbc14810c32d2a884e2e94e406a7d5bf8eee53e1103f558433817230342/py_rust_stemmers-0.1.5.tar.gz", hash = "sha256:e9c310cfb5c2470d7c7c8a0484725965e7cab8b1237e106a0863d5741da3e1f7", size = 9388, upload-time = "2025-02-19T13:56:28.708Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e1/ea8ac92454a634b1bb1ee0a89c2f75a4e6afec15a8412527e9bbde8c6b7b/py_rust_stemmers-0.1.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:29772837126a28263bf54ecd1bc709dd569d15a94d5e861937813ce51e8a6df4", size = 286085, upload-time = "2025-02-19T13:55:23.871Z" }, - { url = "https://files.pythonhosted.org/packages/cb/32/fe1cc3d36a19c1ce39792b1ed151ddff5ee1d74c8801f0e93ff36e65f885/py_rust_stemmers-0.1.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d62410ada44a01e02974b85d45d82f4b4c511aae9121e5f3c1ba1d0bea9126b", size = 272021, upload-time = "2025-02-19T13:55:25.685Z" }, - { url = "https://files.pythonhosted.org/packages/0a/38/b8f94e5e886e7ab181361a0911a14fb923b0d05b414de85f427e773bf445/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b28ef729a4c83c7d9418be3c23c0372493fcccc67e86783ff04596ef8a208cdf", size = 310547, upload-time = "2025-02-19T13:55:26.891Z" }, - { url = "https://files.pythonhosted.org/packages/a9/08/62e97652d359b75335486f4da134a6f1c281f38bd3169ed6ecfb276448c3/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a979c3f4ff7ad94a0d4cf566ca7bfecebb59e66488cc158e64485cf0c9a7879f", size = 315237, upload-time = "2025-02-19T13:55:28.116Z" }, - { url = "https://files.pythonhosted.org/packages/1c/b9/fc0278432f288d2be4ee4d5cc80fd8013d604506b9b0503e8b8cae4ba1c3/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c3593d895453fa06bf70a7b76d6f00d06def0f91fc253fe4260920650c5e078", size = 324419, upload-time = "2025-02-19T13:55:29.211Z" }, - { url = "https://files.pythonhosted.org/packages/6b/5b/74e96eaf622fe07e83c5c389d101540e305e25f76a6d0d6fb3d9e0506db8/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:96ccc7fd042ffc3f7f082f2223bb7082ed1423aa6b43d5d89ab23e321936c045", size = 324792, upload-time = "2025-02-19T13:55:30.948Z" }, - { url = "https://files.pythonhosted.org/packages/4f/f7/b76816d7d67166e9313915ad486c21d9e7da0ac02703e14375bb1cb64b5a/py_rust_stemmers-0.1.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef18cfced2c9c676e0d7d172ba61c3fab2aa6969db64cc8f5ca33a7759efbefe", size = 488014, upload-time = "2025-02-19T13:55:32.066Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ed/7d9bed02f78d85527501f86a867cd5002d97deb791b9a6b1b45b00100010/py_rust_stemmers-0.1.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:541d4b5aa911381e3d37ec483abb6a2cf2351b4f16d5e8d77f9aa2722956662a", size = 575582, upload-time = "2025-02-19T13:55:34.005Z" }, - { url = "https://files.pythonhosted.org/packages/93/40/eafd1b33688e8e8ae946d1ef25c4dc93f5b685bd104b9c5573405d7e1d30/py_rust_stemmers-0.1.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ffd946a36e9ac17ca96821963663012e04bc0ee94d21e8b5ae034721070b436c", size = 493267, upload-time = "2025-02-19T13:55:35.294Z" }, - { url = "https://files.pythonhosted.org/packages/2f/6a/15135b69e4fd28369433eb03264d201b1b0040ba534b05eddeb02a276684/py_rust_stemmers-0.1.5-cp312-none-win_amd64.whl", hash = "sha256:6ed61e1207f3b7428e99b5d00c055645c6415bb75033bff2d06394cbe035fd8e", size = 209395, upload-time = "2025-02-19T13:55:36.519Z" }, - { url = "https://files.pythonhosted.org/packages/80/b8/030036311ec25952bf3083b6c105be5dee052a71aa22d5fbeb857ebf8c1c/py_rust_stemmers-0.1.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:398b3a843a9cd4c5d09e726246bc36f66b3d05b0a937996814e91f47708f5db5", size = 286086, upload-time = "2025-02-19T13:55:37.581Z" }, - { url = "https://files.pythonhosted.org/packages/ed/be/0465dcb3a709ee243d464e89231e3da580017f34279d6304de291d65ccb0/py_rust_stemmers-0.1.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4e308fc7687901f0c73603203869908f3156fa9c17c4ba010a7fcc98a7a1c5f2", size = 272019, upload-time = "2025-02-19T13:55:39.183Z" }, - { url = "https://files.pythonhosted.org/packages/ab/b6/76ca5b1f30cba36835938b5d9abee0c130c81833d51b9006264afdf8df3c/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f9efc4da5e734bdd00612e7506de3d0c9b7abc4b89d192742a0569d0d1fe749", size = 310545, upload-time = "2025-02-19T13:55:40.339Z" }, - { url = "https://files.pythonhosted.org/packages/56/8f/5be87618cea2fe2e70e74115a20724802bfd06f11c7c43514b8288eb6514/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cc2cc8d2b36bc05b8b06506199ac63d437360ae38caefd98cd19e479d35afd42", size = 315236, upload-time = "2025-02-19T13:55:41.55Z" }, - { url = "https://files.pythonhosted.org/packages/00/02/ea86a316aee0f0a9d1449ad4dbffff38f4cf0a9a31045168ae8b95d8bdf8/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a231dc6f0b2a5f12a080dfc7abd9e6a4ea0909290b10fd0a4620e5a0f52c3d17", size = 324419, upload-time = "2025-02-19T13:55:42.693Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fd/1612c22545dcc0abe2f30fc08f30a2332f2224dd536fa1508444a9ca0e39/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5845709d48afc8b29e248f42f92431155a3d8df9ba30418301c49c6072b181b0", size = 324794, upload-time = "2025-02-19T13:55:43.896Z" }, - { url = "https://files.pythonhosted.org/packages/66/18/8a547584d7edac9e7ac9c7bdc53228d6f751c0f70a317093a77c386c8ddc/py_rust_stemmers-0.1.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e48bfd5e3ce9d223bfb9e634dc1425cf93ee57eef6f56aa9a7120ada3990d4be", size = 488014, upload-time = "2025-02-19T13:55:45.088Z" }, - { url = "https://files.pythonhosted.org/packages/3b/87/4619c395b325e26048a6e28a365afed754614788ba1f49b2eefb07621a03/py_rust_stemmers-0.1.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:35d32f6e7bdf6fd90e981765e32293a8be74def807147dea9fdc1f65d6ce382f", size = 575582, upload-time = "2025-02-19T13:55:46.436Z" }, - { url = "https://files.pythonhosted.org/packages/98/6e/214f1a889142b7df6d716e7f3fea6c41e87bd6c29046aa57e175d452b104/py_rust_stemmers-0.1.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:191ea8bf922c984631ffa20bf02ef0ad7eec0465baeaed3852779e8f97c7e7a3", size = 493269, upload-time = "2025-02-19T13:55:49.057Z" }, - { url = "https://files.pythonhosted.org/packages/e1/b9/c5185df277576f995ae34418eb2b2ac12f30835412270f9e05c52face521/py_rust_stemmers-0.1.5-cp313-none-win_amd64.whl", hash = "sha256:e564c9efdbe7621704e222b53bac265b0e4fbea788f07c814094f0ec6b80adcf", size = 209397, upload-time = "2025-02-19T13:55:50.853Z" }, -] - -[[package]] -name = "pyarrow" -version = "23.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/33/ffd9c3eb087fa41dd79c3cf20c4c0ae3cdb877c4f8e1107a446006344924/pyarrow-23.0.0.tar.gz", hash = "sha256:180e3150e7edfcd182d3d9afba72f7cf19839a497cc76555a8dce998a8f67615", size = 1167185, upload-time = "2026-01-18T16:19:42.218Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/bd/c861d020831ee57609b73ea721a617985ece817684dc82415b0bc3e03ac3/pyarrow-23.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5961a9f646c232697c24f54d3419e69b4261ba8a8b66b0ac54a1851faffcbab8", size = 34189116, upload-time = "2026-01-18T16:15:28.054Z" }, - { url = "https://files.pythonhosted.org/packages/8c/23/7725ad6cdcbaf6346221391e7b3eecd113684c805b0a95f32014e6fa0736/pyarrow-23.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:632b3e7c3d232f41d64e1a4a043fb82d44f8a349f339a1188c6a0dd9d2d47d8a", size = 35803831, upload-time = "2026-01-18T16:15:33.798Z" }, - { url = "https://files.pythonhosted.org/packages/57/06/684a421543455cdc2944d6a0c2cc3425b028a4c6b90e34b35580c4899743/pyarrow-23.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:76242c846db1411f1d6c2cc3823be6b86b40567ee24493344f8226ba34a81333", size = 44436452, upload-time = "2026-01-18T16:15:41.598Z" }, - { url = "https://files.pythonhosted.org/packages/c6/6f/8f9eb40c2328d66e8b097777ddcf38494115ff9f1b5bc9754ba46991191e/pyarrow-23.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b73519f8b52ae28127000986bf228fda781e81d3095cd2d3ece76eb5cf760e1b", size = 47557396, upload-time = "2026-01-18T16:15:51.252Z" }, - { url = "https://files.pythonhosted.org/packages/10/6e/f08075f1472e5159553501fde2cc7bc6700944bdabe49a03f8a035ee6ccd/pyarrow-23.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:068701f6823449b1b6469120f399a1239766b117d211c5d2519d4ed5861f75de", size = 48147129, upload-time = "2026-01-18T16:16:00.299Z" }, - { url = "https://files.pythonhosted.org/packages/7d/82/d5a680cd507deed62d141cc7f07f7944a6766fc51019f7f118e4d8ad0fb8/pyarrow-23.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1801ba947015d10e23bca9dd6ef5d0e9064a81569a89b6e9a63b59224fd060df", size = 50596642, upload-time = "2026-01-18T16:16:08.502Z" }, - { url = "https://files.pythonhosted.org/packages/a9/26/4f29c61b3dce9fa7780303b86895ec6a0917c9af927101daaaf118fbe462/pyarrow-23.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:52265266201ec25b6839bf6bd4ea918ca6d50f31d13e1cf200b4261cd11dc25c", size = 27660628, upload-time = "2026-01-18T16:16:15.28Z" }, - { url = "https://files.pythonhosted.org/packages/66/34/564db447d083ec7ff93e0a883a597d2f214e552823bfc178a2d0b1f2c257/pyarrow-23.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:ad96a597547af7827342ffb3c503c8316e5043bb09b47a84885ce39394c96e00", size = 34184630, upload-time = "2026-01-18T16:16:22.141Z" }, - { url = "https://files.pythonhosted.org/packages/aa/3a/3999daebcb5e6119690c92a621c4d78eef2ffba7a0a1b56386d2875fcd77/pyarrow-23.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:b9edf990df77c2901e79608f08c13fbde60202334a4fcadb15c1f57bf7afee43", size = 35796820, upload-time = "2026-01-18T16:16:29.441Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ee/39195233056c6a8d0976d7d1ac1cd4fe21fb0ec534eca76bc23ef3f60e11/pyarrow-23.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:36d1b5bc6ddcaff0083ceec7e2561ed61a51f49cce8be079ee8ed406acb6fdef", size = 44438735, upload-time = "2026-01-18T16:16:38.79Z" }, - { url = "https://files.pythonhosted.org/packages/2c/41/6a7328ee493527e7afc0c88d105ecca69a3580e29f2faaeac29308369fd7/pyarrow-23.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4292b889cd224f403304ddda8b63a36e60f92911f89927ec8d98021845ea21be", size = 47557263, upload-time = "2026-01-18T16:16:46.248Z" }, - { url = "https://files.pythonhosted.org/packages/c6/ee/34e95b21ee84db494eae60083ddb4383477b31fb1fd19fd866d794881696/pyarrow-23.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dfd9e133e60eaa847fd80530a1b89a052f09f695d0b9c34c235ea6b2e0924cf7", size = 48153529, upload-time = "2026-01-18T16:16:53.412Z" }, - { url = "https://files.pythonhosted.org/packages/52/88/8a8d83cea30f4563efa1b7bf51d241331ee5cd1b185a7e063f5634eca415/pyarrow-23.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832141cc09fac6aab1cd3719951d23301396968de87080c57c9a7634e0ecd068", size = 50598851, upload-time = "2026-01-18T16:17:01.133Z" }, - { url = "https://files.pythonhosted.org/packages/c6/4c/2929c4be88723ba025e7b3453047dc67e491c9422965c141d24bab6b5962/pyarrow-23.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:7a7d067c9a88faca655c71bcc30ee2782038d59c802d57950826a07f60d83c4c", size = 27577747, upload-time = "2026-01-18T16:18:02.413Z" }, - { url = "https://files.pythonhosted.org/packages/64/52/564a61b0b82d72bd68ec3aef1adda1e3eba776f89134b9ebcb5af4b13cb6/pyarrow-23.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:ce9486e0535a843cf85d990e2ec5820a47918235183a5c7b8b97ed7e92c2d47d", size = 34446038, upload-time = "2026-01-18T16:17:07.861Z" }, - { url = "https://files.pythonhosted.org/packages/cc/c9/232d4f9855fd1de0067c8a7808a363230d223c83aeee75e0fe6eab851ba9/pyarrow-23.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:075c29aeaa685fd1182992a9ed2499c66f084ee54eea47da3eb76e125e06064c", size = 35921142, upload-time = "2026-01-18T16:17:15.401Z" }, - { url = "https://files.pythonhosted.org/packages/96/f2/60af606a3748367b906bb82d41f0032e059f075444445d47e32a7ff1df62/pyarrow-23.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:799965a5379589510d888be3094c2296efd186a17ca1cef5b77703d4d5121f53", size = 44490374, upload-time = "2026-01-18T16:17:23.93Z" }, - { url = "https://files.pythonhosted.org/packages/ff/2d/7731543050a678ea3a413955a2d5d80d2a642f270aa57a3cb7d5a86e3f46/pyarrow-23.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ef7cac8fe6fccd8b9e7617bfac785b0371a7fe26af59463074e4882747145d40", size = 47527896, upload-time = "2026-01-18T16:17:33.393Z" }, - { url = "https://files.pythonhosted.org/packages/5a/90/f3342553b7ac9879413aed46500f1637296f3c8222107523a43a1c08b42a/pyarrow-23.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15a414f710dc927132dd67c361f78c194447479555af57317066ee5116b90e9e", size = 48210401, upload-time = "2026-01-18T16:17:42.012Z" }, - { url = "https://files.pythonhosted.org/packages/f3/da/9862ade205ecc46c172b6ce5038a74b5151c7401e36255f15975a45878b2/pyarrow-23.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e0d2e6915eca7d786be6a77bf227fbc06d825a75b5b5fe9bcbef121dec32685", size = 50579677, upload-time = "2026-01-18T16:17:50.241Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4c/f11f371f5d4740a5dafc2e11c76bcf42d03dfdb2d68696da97de420b6963/pyarrow-23.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4b317ea6e800b5704e5e5929acb6e2dc13e9276b708ea97a39eb8b345aa2658b", size = 27631889, upload-time = "2026-01-18T16:17:56.55Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/15aec78bcf43a0c004067bd33eb5352836a29a49db8581fc56f2b6ca88b7/pyarrow-23.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:20b187ed9550d233a872074159f765f52f9d92973191cd4b93f293a19efbe377", size = 34213265, upload-time = "2026-01-18T16:18:07.904Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/deb2c594bbba41c37c5d9aa82f510376998352aa69dfcb886cb4b18ad80f/pyarrow-23.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:18ec84e839b493c3886b9b5e06861962ab4adfaeb79b81c76afbd8d84c7d5fda", size = 35819211, upload-time = "2026-01-18T16:18:13.94Z" }, - { url = "https://files.pythonhosted.org/packages/e0/e5/ee82af693cb7b5b2b74f6524cdfede0e6ace779d7720ebca24d68b57c36b/pyarrow-23.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:e438dd3f33894e34fd02b26bd12a32d30d006f5852315f611aa4add6c7fab4bc", size = 44502313, upload-time = "2026-01-18T16:18:20.367Z" }, - { url = "https://files.pythonhosted.org/packages/9c/86/95c61ad82236495f3c31987e85135926ba3ec7f3819296b70a68d8066b49/pyarrow-23.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:a244279f240c81f135631be91146d7fa0e9e840e1dfed2aba8483eba25cd98e6", size = 47585886, upload-time = "2026-01-18T16:18:27.544Z" }, - { url = "https://files.pythonhosted.org/packages/bb/6e/a72d901f305201802f016d015de1e05def7706fff68a1dedefef5dc7eff7/pyarrow-23.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c4692e83e42438dba512a570c6eaa42be2f8b6c0f492aea27dec54bdc495103a", size = 48207055, upload-time = "2026-01-18T16:18:35.425Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e5/5de029c537630ca18828db45c30e2a78da03675a70ac6c3528203c416fe3/pyarrow-23.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae7f30f898dfe44ea69654a35c93e8da4cef6606dc4c72394068fd95f8e9f54a", size = 50619812, upload-time = "2026-01-18T16:18:43.553Z" }, - { url = "https://files.pythonhosted.org/packages/59/8d/2af846cd2412e67a087f5bda4a8e23dfd4ebd570f777db2e8686615dafc1/pyarrow-23.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:5b86bb649e4112fb0614294b7d0a175c7513738876b89655605ebb87c804f861", size = 28263851, upload-time = "2026-01-18T16:19:38.567Z" }, - { url = "https://files.pythonhosted.org/packages/7b/7f/caab863e587041156f6786c52e64151b7386742c8c27140f637176e9230e/pyarrow-23.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:ebc017d765d71d80a3f8584ca0566b53e40464586585ac64176115baa0ada7d3", size = 34463240, upload-time = "2026-01-18T16:18:49.755Z" }, - { url = "https://files.pythonhosted.org/packages/c9/fa/3a5b8c86c958e83622b40865e11af0857c48ec763c11d472c87cd518283d/pyarrow-23.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:0800cc58a6d17d159df823f87ad66cefebf105b982493d4bad03ee7fab84b993", size = 35935712, upload-time = "2026-01-18T16:18:55.626Z" }, - { url = "https://files.pythonhosted.org/packages/c5/08/17a62078fc1a53decb34a9aa79cf9009efc74d63d2422e5ade9fed2f99e3/pyarrow-23.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:3a7c68c722da9bb5b0f8c10e3eae71d9825a4b429b40b32709df5d1fa55beb3d", size = 44503523, upload-time = "2026-01-18T16:19:03.958Z" }, - { url = "https://files.pythonhosted.org/packages/cc/70/84d45c74341e798aae0323d33b7c39194e23b1abc439ceaf60a68a7a969a/pyarrow-23.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:bd5556c24622df90551063ea41f559b714aa63ca953db884cfb958559087a14e", size = 47542490, upload-time = "2026-01-18T16:19:11.208Z" }, - { url = "https://files.pythonhosted.org/packages/61/d9/d1274b0e6f19e235de17441e53224f4716574b2ca837022d55702f24d71d/pyarrow-23.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:54810f6e6afc4ffee7c2e0051b61722fbea9a4961b46192dcfae8ea12fa09059", size = 48233605, upload-time = "2026-01-18T16:19:19.544Z" }, - { url = "https://files.pythonhosted.org/packages/39/07/e4e2d568cb57543d84482f61e510732820cddb0f47c4bb7df629abfed852/pyarrow-23.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:14de7d48052cf4b0ed174533eafa3cfe0711b8076ad70bede32cf59f744f0d7c", size = 50603979, upload-time = "2026-01-18T16:19:26.717Z" }, - { url = "https://files.pythonhosted.org/packages/72/9c/47693463894b610f8439b2e970b82ef81e9599c757bf2049365e40ff963c/pyarrow-23.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:427deac1f535830a744a4f04a6ac183a64fcac4341b3f618e693c41b7b98d2b0", size = 28338905, upload-time = "2026-01-18T16:19:32.93Z" }, -] - -[[package]] -name = "pyasn1" -version = "0.6.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/b6/6e630dff89739fcd427e3f72b3d905ce0acb85a45d4ec3e2678718a3487f/pyasn1-0.6.2.tar.gz", hash = "sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b", size = 146586, upload-time = "2026-01-16T18:04:18.534Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/b5/a96872e5184f354da9c84ae119971a0a4c221fe9b27a4d94bd43f2596727/pyasn1-0.6.2-py3-none-any.whl", hash = "sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf", size = 83371, upload-time = "2026-01-16T18:04:17.174Z" }, -] - -[[package]] -name = "pyautogui" -version = "0.9.54" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mouseinfo" }, - { name = "pygetwindow" }, - { name = "pymsgbox" }, - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, - { name = "pyscreeze" }, - { name = "python3-xlib", marker = "sys_platform == 'linux'" }, - { name = "pytweening" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/65/ff/cdae0a8c2118a0de74b6cf4cbcdcaf8fd25857e6c3f205ce4b1794b27814/PyAutoGUI-0.9.54.tar.gz", hash = "sha256:dd1d29e8fd118941cb193f74df57e5c6ff8e9253b99c7b04f39cfc69f3ae04b2", size = 61236, upload-time = "2023-05-24T20:11:32.972Z" } - -[[package]] -name = "pybase64" -version = "1.4.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/b8/4ed5c7ad5ec15b08d35cc79ace6145d5c1ae426e46435f4987379439dfea/pybase64-1.4.3.tar.gz", hash = "sha256:c2ed274c9e0ba9c8f9c4083cfe265e66dd679126cd9c2027965d807352f3f053", size = 137272, upload-time = "2025-12-06T13:27:04.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/a7/efcaa564f091a2af7f18a83c1c4875b1437db56ba39540451dc85d56f653/pybase64-1.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:18d85e5ab8b986bb32d8446aca6258ed80d1bafe3603c437690b352c648f5967", size = 38167, upload-time = "2025-12-06T13:23:16.821Z" }, - { url = "https://files.pythonhosted.org/packages/db/c7/c7ad35adff2d272bf2930132db2b3eea8c44bb1b1f64eb9b2b8e57cde7b4/pybase64-1.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3f5791a3491d116d0deaf4d83268f48792998519698f8751efb191eac84320e9", size = 31673, upload-time = "2025-12-06T13:23:17.835Z" }, - { url = "https://files.pythonhosted.org/packages/43/1b/9a8cab0042b464e9a876d5c65fe5127445a2436da36fda64899b119b1a1b/pybase64-1.4.3-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f0b3f200c3e06316f6bebabd458b4e4bcd4c2ca26af7c0c766614d91968dee27", size = 68210, upload-time = "2025-12-06T13:23:18.813Z" }, - { url = "https://files.pythonhosted.org/packages/62/f7/965b79ff391ad208b50e412b5d3205ccce372a2d27b7218ae86d5295b105/pybase64-1.4.3-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb632edfd132b3eaf90c39c89aa314beec4e946e210099b57d40311f704e11d4", size = 71599, upload-time = "2025-12-06T13:23:20.195Z" }, - { url = "https://files.pythonhosted.org/packages/03/4b/a3b5175130b3810bbb8ccfa1edaadbd3afddb9992d877c8a1e2f274b476e/pybase64-1.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:356ef1d74648ce997f5a777cf8f1aefecc1c0b4fe6201e0ef3ec8a08170e1b54", size = 59922, upload-time = "2025-12-06T13:23:21.487Z" }, - { url = "https://files.pythonhosted.org/packages/da/5d/c38d1572027fc601b62d7a407721688b04b4d065d60ca489912d6893e6cf/pybase64-1.4.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:c48361f90db32bacaa5518419d4eb9066ba558013aaf0c7781620279ecddaeb9", size = 56712, upload-time = "2025-12-06T13:23:22.77Z" }, - { url = "https://files.pythonhosted.org/packages/e7/d4/4e04472fef485caa8f561d904d4d69210a8f8fc1608ea15ebd9012b92655/pybase64-1.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:702bcaa16ae02139d881aeaef5b1c8ffb4a3fae062fe601d1e3835e10310a517", size = 59300, upload-time = "2025-12-06T13:23:24.543Z" }, - { url = "https://files.pythonhosted.org/packages/86/e7/16e29721b86734b881d09b7e23dfd7c8408ad01a4f4c7525f3b1088e25ec/pybase64-1.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:53d0ffe1847b16b647c6413d34d1de08942b7724273dd57e67dcbdb10c574045", size = 60278, upload-time = "2025-12-06T13:23:25.608Z" }, - { url = "https://files.pythonhosted.org/packages/b1/02/18515f211d7c046be32070709a8efeeef8a0203de4fd7521e6b56404731b/pybase64-1.4.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:9a1792e8b830a92736dae58f0c386062eb038dfe8004fb03ba33b6083d89cd43", size = 54817, upload-time = "2025-12-06T13:23:26.633Z" }, - { url = "https://files.pythonhosted.org/packages/e7/be/14e29d8e1a481dbff151324c96dd7b5d2688194bb65dc8a00ca0e1ad1e86/pybase64-1.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1d468b1b1ac5ad84875a46eaa458663c3721e8be5f155ade356406848d3701f6", size = 58611, upload-time = "2025-12-06T13:23:27.684Z" }, - { url = "https://files.pythonhosted.org/packages/b4/8a/a2588dfe24e1bbd742a554553778ab0d65fdf3d1c9a06d10b77047d142aa/pybase64-1.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e97b7bdbd62e71898cd542a6a9e320d9da754ff3ebd02cb802d69087ee94d468", size = 52404, upload-time = "2025-12-06T13:23:28.714Z" }, - { url = "https://files.pythonhosted.org/packages/27/fc/afcda7445bebe0cbc38cafdd7813234cdd4fc5573ff067f1abf317bb0cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b33aeaa780caaa08ffda87fc584d5eab61e3d3bbb5d86ead02161dc0c20d04bc", size = 68817, upload-time = "2025-12-06T13:23:30.079Z" }, - { url = "https://files.pythonhosted.org/packages/d3/3a/87c3201e555ed71f73e961a787241a2438c2bbb2ca8809c29ddf938a3157/pybase64-1.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c0efcf78f11cf866bed49caa7b97552bc4855a892f9cc2372abcd3ed0056f0d", size = 57854, upload-time = "2025-12-06T13:23:31.17Z" }, - { url = "https://files.pythonhosted.org/packages/fd/7d/931c2539b31a7b375e7d595b88401eeb5bd6c5ce1059c9123f9b608aaa14/pybase64-1.4.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:66e3791f2ed725a46593f8bd2761ff37d01e2cdad065b1dceb89066f476e50c6", size = 54333, upload-time = "2025-12-06T13:23:32.422Z" }, - { url = "https://files.pythonhosted.org/packages/de/5e/537601e02cc01f27e9d75f440f1a6095b8df44fc28b1eef2cd739aea8cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:72bb0b6bddadab26e1b069bb78e83092711a111a80a0d6b9edcb08199ad7299b", size = 56492, upload-time = "2025-12-06T13:23:33.515Z" }, - { url = "https://files.pythonhosted.org/packages/96/97/2a2e57acf8f5c9258d22aba52e71f8050e167b29ed2ee1113677c1b600c1/pybase64-1.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5b3365dbcbcdb0a294f0f50af0c0a16b27a232eddeeb0bceeefd844ef30d2a23", size = 70974, upload-time = "2025-12-06T13:23:36.27Z" }, - { url = "https://files.pythonhosted.org/packages/75/2e/a9e28941c6dab6f06e6d3f6783d3373044be9b0f9a9d3492c3d8d2260ac0/pybase64-1.4.3-cp312-cp312-win32.whl", hash = "sha256:7bca1ed3a5df53305c629ca94276966272eda33c0d71f862d2d3d043f1e1b91a", size = 33686, upload-time = "2025-12-06T13:23:37.848Z" }, - { url = "https://files.pythonhosted.org/packages/83/e3/507ab649d8c3512c258819c51d25c45d6e29d9ca33992593059e7b646a33/pybase64-1.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:9f2da8f56d9b891b18b4daf463a0640eae45a80af548ce435be86aa6eff3603b", size = 35833, upload-time = "2025-12-06T13:23:38.877Z" }, - { url = "https://files.pythonhosted.org/packages/bc/8a/6eba66cd549a2fc74bb4425fd61b839ba0ab3022d3c401b8a8dc2cc00c7a/pybase64-1.4.3-cp312-cp312-win_arm64.whl", hash = "sha256:0631d8a2d035de03aa9bded029b9513e1fee8ed80b7ddef6b8e9389ffc445da0", size = 31185, upload-time = "2025-12-06T13:23:39.908Z" }, - { url = "https://files.pythonhosted.org/packages/3a/50/b7170cb2c631944388fe2519507fe3835a4054a6a12a43f43781dae82be1/pybase64-1.4.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:ea4b785b0607d11950b66ce7c328f452614aefc9c6d3c9c28bae795dc7f072e1", size = 33901, upload-time = "2025-12-06T13:23:40.951Z" }, - { url = "https://files.pythonhosted.org/packages/48/8b/69f50578e49c25e0a26e3ee72c39884ff56363344b79fc3967f5af420ed6/pybase64-1.4.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:6a10b6330188c3026a8b9c10e6b9b3f2e445779cf16a4c453d51a072241c65a2", size = 40807, upload-time = "2025-12-06T13:23:42.006Z" }, - { url = "https://files.pythonhosted.org/packages/5c/8d/20b68f11adfc4c22230e034b65c71392e3e338b413bf713c8945bd2ccfb3/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:27fdff227a0c0e182e0ba37a99109645188978b920dfb20d8b9c17eeee370d0d", size = 30932, upload-time = "2025-12-06T13:23:43.348Z" }, - { url = "https://files.pythonhosted.org/packages/f7/79/b1b550ac6bff51a4880bf6e089008b2e1ca16f2c98db5e039a08ac3ad157/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2a8204f1fdfec5aa4184249b51296c0de95445869920c88123978304aad42df1", size = 31394, upload-time = "2025-12-06T13:23:44.317Z" }, - { url = "https://files.pythonhosted.org/packages/82/70/b5d7c5932bf64ee1ec5da859fbac981930b6a55d432a603986c7f509c838/pybase64-1.4.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:874fc2a3777de6baf6aa921a7aa73b3be98295794bea31bd80568a963be30767", size = 38078, upload-time = "2025-12-06T13:23:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/56/fe/e66fe373bce717c6858427670736d54297938dad61c5907517ab4106bd90/pybase64-1.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2dc64a94a9d936b8e3449c66afabbaa521d3cc1a563d6bbaaa6ffa4535222e4b", size = 38158, upload-time = "2025-12-06T13:23:46.872Z" }, - { url = "https://files.pythonhosted.org/packages/80/a9/b806ed1dcc7aed2ea3dd4952286319e6f3a8b48615c8118f453948e01999/pybase64-1.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e48f86de1c145116ccf369a6e11720ce696c2ec02d285f440dfb57ceaa0a6cb4", size = 31672, upload-time = "2025-12-06T13:23:47.88Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c9/24b3b905cf75e23a9a4deaf203b35ffcb9f473ac0e6d8257f91a05dfce62/pybase64-1.4.3-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1d45c8fe8fe82b65c36b227bb4a2cf623d9ada16bed602ce2d3e18c35285b72a", size = 68244, upload-time = "2025-12-06T13:23:49.026Z" }, - { url = "https://files.pythonhosted.org/packages/f8/cd/d15b0c3e25e5859fab0416dc5b96d34d6bd2603c1c96a07bb2202b68ab92/pybase64-1.4.3-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ad70c26ba091d8f5167e9d4e1e86a0483a5414805cdb598a813db635bd3be8b8", size = 71620, upload-time = "2025-12-06T13:23:50.081Z" }, - { url = "https://files.pythonhosted.org/packages/0d/31/4ca953cc3dcde2b3711d6bfd70a6f4ad2ca95a483c9698076ba605f1520f/pybase64-1.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e98310b7c43145221e7194ac9fa7fffc84763c87bfc5e2f59f9f92363475bdc1", size = 59930, upload-time = "2025-12-06T13:23:51.68Z" }, - { url = "https://files.pythonhosted.org/packages/60/55/e7f7bdcd0fd66e61dda08db158ffda5c89a306bbdaaf5a062fbe4e48f4a1/pybase64-1.4.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:398685a76034e91485a28aeebcb49e64cd663212fd697b2497ac6dfc1df5e671", size = 56425, upload-time = "2025-12-06T13:23:52.732Z" }, - { url = "https://files.pythonhosted.org/packages/cb/65/b592c7f921e51ca1aca3af5b0d201a98666d0a36b930ebb67e7c2ed27395/pybase64-1.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7e46400a6461187ccb52ed75b0045d937529e801a53a9cd770b350509f9e4d50", size = 59327, upload-time = "2025-12-06T13:23:53.856Z" }, - { url = "https://files.pythonhosted.org/packages/23/95/1613d2fb82dbb1548595ad4179f04e9a8451bfa18635efce18b631eabe3f/pybase64-1.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1b62b9f2f291d94f5e0b76ab499790b7dcc78a009d4ceea0b0428770267484b6", size = 60294, upload-time = "2025-12-06T13:23:54.937Z" }, - { url = "https://files.pythonhosted.org/packages/9d/73/40431f37f7d1b3eab4673e7946ff1e8f5d6bd425ec257e834dae8a6fc7b0/pybase64-1.4.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:f30ceb5fa4327809dede614be586efcbc55404406d71e1f902a6fdcf322b93b2", size = 54858, upload-time = "2025-12-06T13:23:56.031Z" }, - { url = "https://files.pythonhosted.org/packages/a7/84/f6368bcaf9f743732e002a9858646fd7a54f428490d427dd6847c5cfe89e/pybase64-1.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0d5f18ed53dfa1d4cf8b39ee542fdda8e66d365940e11f1710989b3cf4a2ed66", size = 58629, upload-time = "2025-12-06T13:23:57.12Z" }, - { url = "https://files.pythonhosted.org/packages/43/75/359532f9adb49c6b546cafc65c46ed75e2ccc220d514ba81c686fbd83965/pybase64-1.4.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:119d31aa4b58b85a8ebd12b63c07681a138c08dfc2fe5383459d42238665d3eb", size = 52448, upload-time = "2025-12-06T13:23:58.298Z" }, - { url = "https://files.pythonhosted.org/packages/92/6c/ade2ba244c3f33ed920a7ed572ad772eb0b5f14480b72d629d0c9e739a40/pybase64-1.4.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3cf0218b0e2f7988cf7d738a73b6a1d14f3be6ce249d7c0f606e768366df2cce", size = 68841, upload-time = "2025-12-06T13:23:59.886Z" }, - { url = "https://files.pythonhosted.org/packages/a0/51/b345139cd236be382f2d4d4453c21ee6299e14d2f759b668e23080f8663f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:12f4ee5e988bc5c0c1106b0d8fc37fb0508f12dab76bac1b098cb500d148da9d", size = 57910, upload-time = "2025-12-06T13:24:00.994Z" }, - { url = "https://files.pythonhosted.org/packages/1a/b8/9f84bdc4f1c4f0052489396403c04be2f9266a66b70c776001eaf0d78c1f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:937826bc7b6b95b594a45180e81dd4d99bd4dd4814a443170e399163f7ff3fb6", size = 54335, upload-time = "2025-12-06T13:24:02.046Z" }, - { url = "https://files.pythonhosted.org/packages/d0/c7/be63b617d284de46578a366da77ede39c8f8e815ed0d82c7c2acca560fab/pybase64-1.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:88995d1460971ef80b13e3e007afbe4b27c62db0508bc7250a2ab0a0b4b91362", size = 56486, upload-time = "2025-12-06T13:24:03.141Z" }, - { url = "https://files.pythonhosted.org/packages/5e/96/f252c8f9abd6ded3ef1ccd3cdbb8393a33798007f761b23df8de1a2480e6/pybase64-1.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:72326fe163385ed3e1e806dd579d47fde5d8a59e51297a60fc4e6cbc1b4fc4ed", size = 70978, upload-time = "2025-12-06T13:24:04.221Z" }, - { url = "https://files.pythonhosted.org/packages/af/51/0f5714af7aeef96e30f968e4371d75ad60558aaed3579d7c6c8f1c43c18a/pybase64-1.4.3-cp313-cp313-win32.whl", hash = "sha256:b1623730c7892cf5ed0d6355e375416be6ef8d53ab9b284f50890443175c0ac3", size = 33684, upload-time = "2025-12-06T13:24:05.29Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ad/0cea830a654eb08563fb8214150ef57546ece1cc421c09035f0e6b0b5ea9/pybase64-1.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:8369887590f1646a5182ca2fb29252509da7ae31d4923dbb55d3e09da8cc4749", size = 35832, upload-time = "2025-12-06T13:24:06.35Z" }, - { url = "https://files.pythonhosted.org/packages/b4/0d/eec2a8214989c751bc7b4cad1860eb2c6abf466e76b77508c0f488c96a37/pybase64-1.4.3-cp313-cp313-win_arm64.whl", hash = "sha256:860b86bca71e5f0237e2ab8b2d9c4c56681f3513b1bf3e2117290c1963488390", size = 31175, upload-time = "2025-12-06T13:24:07.419Z" }, - { url = "https://files.pythonhosted.org/packages/db/c9/e23463c1a2913686803ef76b1a5ae7e6fac868249a66e48253d17ad7232c/pybase64-1.4.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eb51db4a9c93215135dccd1895dca078e8785c357fabd983c9f9a769f08989a9", size = 38497, upload-time = "2025-12-06T13:24:08.873Z" }, - { url = "https://files.pythonhosted.org/packages/71/83/343f446b4b7a7579bf6937d2d013d82f1a63057cf05558e391ab6039d7db/pybase64-1.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a03ef3f529d85fd46b89971dfb00c634d53598d20ad8908fb7482955c710329d", size = 32076, upload-time = "2025-12-06T13:24:09.975Z" }, - { url = "https://files.pythonhosted.org/packages/46/fc/cb64964c3b29b432f54d1bce5e7691d693e33bbf780555151969ffd95178/pybase64-1.4.3-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:2e745f2ce760c6cf04d8a72198ef892015ddb89f6ceba489e383518ecbdb13ab", size = 72317, upload-time = "2025-12-06T13:24:11.129Z" }, - { url = "https://files.pythonhosted.org/packages/0a/b7/fab2240da6f4e1ad46f71fa56ec577613cf5df9dce2d5b4cfaa4edd0e365/pybase64-1.4.3-cp313-cp313t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fac217cd9de8581a854b0ac734c50fd1fa4b8d912396c1fc2fce7c230efe3a7", size = 75534, upload-time = "2025-12-06T13:24:12.433Z" }, - { url = "https://files.pythonhosted.org/packages/91/3b/3e2f2b6e68e3d83ddb9fa799f3548fb7449765daec9bbd005a9fbe296d7f/pybase64-1.4.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:da1ee8fa04b283873de2d6e8fa5653e827f55b86bdf1a929c5367aaeb8d26f8a", size = 65399, upload-time = "2025-12-06T13:24:13.928Z" }, - { url = "https://files.pythonhosted.org/packages/6b/08/476ac5914c3b32e0274a2524fc74f01cbf4f4af4513d054e41574eb018f6/pybase64-1.4.3-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:b0bf8e884ee822ca7b1448eeb97fa131628fe0ff42f60cae9962789bd562727f", size = 60487, upload-time = "2025-12-06T13:24:15.177Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b8/618a92915330cc9cba7880299b546a1d9dab1a21fd6c0292ee44a4fe608c/pybase64-1.4.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1bf749300382a6fd1f4f255b183146ef58f8e9cb2f44a077b3a9200dfb473a77", size = 63959, upload-time = "2025-12-06T13:24:16.854Z" }, - { url = "https://files.pythonhosted.org/packages/a5/52/af9d8d051652c3051862c442ec3861259c5cdb3fc69774bc701470bd2a59/pybase64-1.4.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:153a0e42329b92337664cfc356f2065248e6c9a1bd651bbcd6dcaf15145d3f06", size = 64874, upload-time = "2025-12-06T13:24:18.328Z" }, - { url = "https://files.pythonhosted.org/packages/e4/51/5381a7adf1f381bd184d33203692d3c57cf8ae9f250f380c3fecbdbe554b/pybase64-1.4.3-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:86ee56ac7f2184ca10217ed1c655c1a060273e233e692e9086da29d1ae1768db", size = 58572, upload-time = "2025-12-06T13:24:19.417Z" }, - { url = "https://files.pythonhosted.org/packages/e0/f0/578ee4ffce5818017de4fdf544e066c225bc435e73eb4793cde28a689d0b/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0e71a4db76726bf830b47477e7d830a75c01b2e9b01842e787a0836b0ba741e3", size = 63636, upload-time = "2025-12-06T13:24:20.497Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ad/8ae94814bf20159ea06310b742433e53d5820aa564c9fdf65bf2d79f8799/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2ba7799ec88540acd9861b10551d24656ca3c2888ecf4dba2ee0a71544a8923f", size = 56193, upload-time = "2025-12-06T13:24:21.559Z" }, - { url = "https://files.pythonhosted.org/packages/d1/31/6438cfcc3d3f0fa84d229fa125c243d5094e72628e525dfefadf3bcc6761/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2860299e4c74315f5951f0cf3e72ba0f201c3356c8a68f95a3ab4e620baf44e9", size = 72655, upload-time = "2025-12-06T13:24:22.673Z" }, - { url = "https://files.pythonhosted.org/packages/a3/0d/2bbc9e9c3fc12ba8a6e261482f03a544aca524f92eae0b4908c0a10ba481/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:bb06015db9151f0c66c10aae8e3603adab6b6cd7d1f7335a858161d92fc29618", size = 62471, upload-time = "2025-12-06T13:24:23.8Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0b/34d491e7f49c1dbdb322ea8da6adecda7c7cd70b6644557c6e4ca5c6f7c7/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:242512a070817272865d37c8909059f43003b81da31f616bb0c391ceadffe067", size = 58119, upload-time = "2025-12-06T13:24:24.994Z" }, - { url = "https://files.pythonhosted.org/packages/ce/17/c21d0cde2a6c766923ae388fc1f78291e1564b0d38c814b5ea8a0e5e081c/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5d8277554a12d3e3eed6180ebda62786bf9fc8d7bb1ee00244258f4a87ca8d20", size = 60791, upload-time = "2025-12-06T13:24:26.046Z" }, - { url = "https://files.pythonhosted.org/packages/92/b2/eaa67038916a48de12b16f4c384bcc1b84b7ec731b23613cb05f27673294/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f40b7ddd698fc1e13a4b64fbe405e4e0e1279e8197e37050e24154655f5f7c4e", size = 74701, upload-time = "2025-12-06T13:24:27.466Z" }, - { url = "https://files.pythonhosted.org/packages/42/10/abb7757c330bb869ebb95dab0c57edf5961ffbd6c095c8209cbbf75d117d/pybase64-1.4.3-cp313-cp313t-win32.whl", hash = "sha256:46d75c9387f354c5172582a9eaae153b53a53afeb9c19fcf764ea7038be3bd8b", size = 33965, upload-time = "2025-12-06T13:24:28.548Z" }, - { url = "https://files.pythonhosted.org/packages/63/a0/2d4e5a59188e9e6aed0903d580541aaea72dcbbab7bf50fb8b83b490b6c3/pybase64-1.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:d7344625591d281bec54e85cbfdab9e970f6219cac1570f2aa140b8c942ccb81", size = 36207, upload-time = "2025-12-06T13:24:29.646Z" }, - { url = "https://files.pythonhosted.org/packages/1f/05/95b902e8f567b4d4b41df768ccc438af618f8d111e54deaf57d2df46bd76/pybase64-1.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:28a3c60c55138e0028313f2eccd321fec3c4a0be75e57a8d3eb883730b1b0880", size = 31505, upload-time = "2025-12-06T13:24:30.687Z" }, - { url = "https://files.pythonhosted.org/packages/e4/80/4bd3dff423e5a91f667ca41982dc0b79495b90ec0c0f5d59aca513e50f8c/pybase64-1.4.3-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:015bb586a1ea1467f69d57427abe587469392215f59db14f1f5c39b52fdafaf5", size = 33835, upload-time = "2025-12-06T13:24:31.767Z" }, - { url = "https://files.pythonhosted.org/packages/45/60/a94d94cc1e3057f602e0b483c9ebdaef40911d84a232647a2fe593ab77bb/pybase64-1.4.3-cp314-cp314-android_24_x86_64.whl", hash = "sha256:d101e3a516f837c3dcc0e5a0b7db09582ebf99ed670865223123fb2e5839c6c0", size = 40673, upload-time = "2025-12-06T13:24:32.82Z" }, - { url = "https://files.pythonhosted.org/packages/e3/71/cf62b261d431857e8e054537a5c3c24caafa331de30daede7b2c6c558501/pybase64-1.4.3-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8f183ac925a48046abe047360fe3a1b28327afb35309892132fe1915d62fb282", size = 30939, upload-time = "2025-12-06T13:24:34.001Z" }, - { url = "https://files.pythonhosted.org/packages/24/3e/d12f92a3c1f7c6ab5d53c155bff9f1084ba997a37a39a4f781ccba9455f3/pybase64-1.4.3-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30bf3558e24dcce4da5248dcf6d73792adfcf4f504246967e9db155be4c439ad", size = 31401, upload-time = "2025-12-06T13:24:35.11Z" }, - { url = "https://files.pythonhosted.org/packages/9b/3d/9c27440031fea0d05146f8b70a460feb95d8b4e3d9ca8f45c972efb4c3d3/pybase64-1.4.3-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a674b419de318d2ce54387dd62646731efa32b4b590907800f0bd40675c1771d", size = 38075, upload-time = "2025-12-06T13:24:36.53Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d4/6c0e0cf0efd53c254173fbcd84a3d8fcbf5e0f66622473da425becec32a5/pybase64-1.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:720104fd7303d07bac302be0ff8f7f9f126f2f45c1edb4f48fdb0ff267e69fe1", size = 38257, upload-time = "2025-12-06T13:24:38.049Z" }, - { url = "https://files.pythonhosted.org/packages/50/eb/27cb0b610d5cd70f5ad0d66c14ad21c04b8db930f7139818e8fbdc14df4d/pybase64-1.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:83f1067f73fa5afbc3efc0565cecc6ed53260eccddef2ebe43a8ce2b99ea0e0a", size = 31685, upload-time = "2025-12-06T13:24:40.327Z" }, - { url = "https://files.pythonhosted.org/packages/db/26/b136a4b65e5c94ff06217f7726478df3f31ab1c777c2c02cf698e748183f/pybase64-1.4.3-cp314-cp314-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:b51204d349a4b208287a8aa5b5422be3baa88abf6cc8ff97ccbda34919bbc857", size = 68460, upload-time = "2025-12-06T13:24:41.735Z" }, - { url = "https://files.pythonhosted.org/packages/68/6d/84ce50e7ee1ae79984d689e05a9937b2460d4efa1e5b202b46762fb9036c/pybase64-1.4.3-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:30f2fd53efecbdde4bdca73a872a68dcb0d1bf8a4560c70a3e7746df973e1ef3", size = 71688, upload-time = "2025-12-06T13:24:42.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/57/6743e420416c3ff1b004041c85eb0ebd9c50e9cf05624664bfa1dc8b5625/pybase64-1.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0932b0c5cfa617091fd74f17d24549ce5de3628791998c94ba57be808078eeaf", size = 60040, upload-time = "2025-12-06T13:24:44.37Z" }, - { url = "https://files.pythonhosted.org/packages/3b/68/733324e28068a89119af2921ce548e1c607cc5c17d354690fc51c302e326/pybase64-1.4.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:acb61f5ab72bec808eb0d4ce8b87ec9f38d7d750cb89b1371c35eb8052a29f11", size = 56478, upload-time = "2025-12-06T13:24:45.815Z" }, - { url = "https://files.pythonhosted.org/packages/b5/9e/f3f4aa8cfe3357a3cdb0535b78eb032b671519d3ecc08c58c4c6b72b5a91/pybase64-1.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:2bc2d5bc15168f5c04c53bdfe5a1e543b2155f456ed1e16d7edce9ce73842021", size = 59463, upload-time = "2025-12-06T13:24:46.938Z" }, - { url = "https://files.pythonhosted.org/packages/aa/d1/53286038e1f0df1cf58abcf4a4a91b0f74ab44539c2547b6c31001ddd054/pybase64-1.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:8a7bc3cd23880bdca59758bcdd6f4ef0674f2393782763910a7466fab35ccb98", size = 60360, upload-time = "2025-12-06T13:24:48.039Z" }, - { url = "https://files.pythonhosted.org/packages/00/9a/5cc6ce95db2383d27ff4d790b8f8b46704d360d701ab77c4f655bcfaa6a7/pybase64-1.4.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ad15acf618880d99792d71e3905b0e2508e6e331b76a1b34212fa0f11e01ad28", size = 54999, upload-time = "2025-12-06T13:24:49.547Z" }, - { url = "https://files.pythonhosted.org/packages/64/e7/c3c1d09c3d7ae79e3aa1358c6d912d6b85f29281e47aa94fc0122a415a2f/pybase64-1.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448158d417139cb4851200e5fee62677ae51f56a865d50cda9e0d61bda91b116", size = 58736, upload-time = "2025-12-06T13:24:50.641Z" }, - { url = "https://files.pythonhosted.org/packages/db/d5/0baa08e3d8119b15b588c39f0d39fd10472f0372e3c54ca44649cbefa256/pybase64-1.4.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:9058c49b5a2f3e691b9db21d37eb349e62540f9f5fc4beabf8cbe3c732bead86", size = 52298, upload-time = "2025-12-06T13:24:51.791Z" }, - { url = "https://files.pythonhosted.org/packages/00/87/fc6f11474a1de7e27cd2acbb8d0d7508bda3efa73dfe91c63f968728b2a3/pybase64-1.4.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ce561724f6522907a66303aca27dce252d363fcd85884972d348f4403ba3011a", size = 69049, upload-time = "2025-12-06T13:24:53.253Z" }, - { url = "https://files.pythonhosted.org/packages/69/9d/7fb5566f669ac18b40aa5fc1c438e24df52b843c1bdc5da47d46d4c1c630/pybase64-1.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:63316560a94ac449fe86cb8b9e0a13714c659417e92e26a5cbf085cd0a0c838d", size = 57952, upload-time = "2025-12-06T13:24:54.342Z" }, - { url = "https://files.pythonhosted.org/packages/de/cc/ceb949232dbbd3ec4ee0190d1df4361296beceee9840390a63df8bc31784/pybase64-1.4.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7ecd796f2ac0be7b73e7e4e232b8c16422014de3295d43e71d2b19fd4a4f5368", size = 54484, upload-time = "2025-12-06T13:24:55.774Z" }, - { url = "https://files.pythonhosted.org/packages/a7/69/659f3c8e6a5d7b753b9c42a4bd9c42892a0f10044e9c7351a4148d413a33/pybase64-1.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d01e102a12fb2e1ed3dc11611c2818448626637857ec3994a9cf4809dfd23477", size = 56542, upload-time = "2025-12-06T13:24:57Z" }, - { url = "https://files.pythonhosted.org/packages/85/2c/29c9e6c9c82b72025f9676f9e82eb1fd2339ad038cbcbf8b9e2ac02798fc/pybase64-1.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ebff797a93c2345f22183f454fd8607a34d75eca5a3a4a969c1c75b304cee39d", size = 71045, upload-time = "2025-12-06T13:24:58.179Z" }, - { url = "https://files.pythonhosted.org/packages/b9/84/5a3dce8d7a0040a5c0c14f0fe1311cd8db872913fa04438071b26b0dac04/pybase64-1.4.3-cp314-cp314-win32.whl", hash = "sha256:28b2a1bb0828c0595dc1ea3336305cd97ff85b01c00d81cfce4f92a95fb88f56", size = 34200, upload-time = "2025-12-06T13:24:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/57/bc/ce7427c12384adee115b347b287f8f3cf65860b824d74fe2c43e37e81c1f/pybase64-1.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:33338d3888700ff68c3dedfcd49f99bfc3b887570206130926791e26b316b029", size = 36323, upload-time = "2025-12-06T13:25:01.708Z" }, - { url = "https://files.pythonhosted.org/packages/9a/1b/2b8ffbe9a96eef7e3f6a5a7be75995eebfb6faaedc85b6da6b233e50c778/pybase64-1.4.3-cp314-cp314-win_arm64.whl", hash = "sha256:62725669feb5acb186458da2f9353e88ae28ef66bb9c4c8d1568b12a790dfa94", size = 31584, upload-time = "2025-12-06T13:25:02.801Z" }, - { url = "https://files.pythonhosted.org/packages/ac/d8/6824c2e6fb45b8fa4e7d92e3c6805432d5edc7b855e3e8e1eedaaf6efb7c/pybase64-1.4.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:153fe29be038948d9372c3e77ae7d1cab44e4ba7d9aaf6f064dbeea36e45b092", size = 38601, upload-time = "2025-12-06T13:25:04.222Z" }, - { url = "https://files.pythonhosted.org/packages/ea/e5/10d2b3a4ad3a4850be2704a2f70cd9c0cf55725c8885679872d3bc846c67/pybase64-1.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f7fe3decaa7c4a9e162327ec7bd81ce183d2b16f23c6d53b606649c6e0203e9e", size = 32078, upload-time = "2025-12-06T13:25:05.362Z" }, - { url = "https://files.pythonhosted.org/packages/43/04/8b15c34d3c2282f1c1b0850f1113a249401b618a382646a895170bc9b5e7/pybase64-1.4.3-cp314-cp314t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:a5ae04ea114c86eb1da1f6e18d75f19e3b5ae39cb1d8d3cd87c29751a6a22780", size = 72474, upload-time = "2025-12-06T13:25:06.434Z" }, - { url = "https://files.pythonhosted.org/packages/42/00/f34b4d11278f8fdc68bc38f694a91492aa318f7c6f1bd7396197ac0f8b12/pybase64-1.4.3-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1755b3dce3a2a5c7d17ff6d4115e8bee4a1d5aeae74469db02e47c8f477147da", size = 75706, upload-time = "2025-12-06T13:25:07.636Z" }, - { url = "https://files.pythonhosted.org/packages/bb/5d/71747d4ad7fe16df4c4c852bdbdeb1f2cf35677b48d7c34d3011a7a6ad3a/pybase64-1.4.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb852f900e27ffc4ec1896817535a0fa19610ef8875a096b59f21d0aa42ff172", size = 65589, upload-time = "2025-12-06T13:25:08.809Z" }, - { url = "https://files.pythonhosted.org/packages/49/b1/d1e82bd58805bb5a3a662864800bab83a83a36ba56e7e3b1706c708002a5/pybase64-1.4.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:9cf21ea8c70c61eddab3421fbfce061fac4f2fb21f7031383005a1efdb13d0b9", size = 60670, upload-time = "2025-12-06T13:25:10.04Z" }, - { url = "https://files.pythonhosted.org/packages/15/67/16c609b7a13d1d9fc87eca12ba2dce5e67f949eeaab61a41bddff843cbb0/pybase64-1.4.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:afff11b331fdc27692fc75e85ae083340a35105cea1a3c4552139e2f0e0d174f", size = 64194, upload-time = "2025-12-06T13:25:11.48Z" }, - { url = "https://files.pythonhosted.org/packages/3c/11/37bc724e42960f0106c2d33dc957dcec8f760c91a908cc6c0df7718bc1a8/pybase64-1.4.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9a5143df542c1ce5c1f423874b948c4d689b3f05ec571f8792286197a39ba02", size = 64984, upload-time = "2025-12-06T13:25:12.645Z" }, - { url = "https://files.pythonhosted.org/packages/6e/66/b2b962a6a480dd5dae3029becf03ea1a650d326e39bf1c44ea3db78bb010/pybase64-1.4.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:d62e9861019ad63624b4a7914dff155af1cc5d6d79df3be14edcaedb5fdad6f9", size = 58750, upload-time = "2025-12-06T13:25:13.848Z" }, - { url = "https://files.pythonhosted.org/packages/2b/15/9b6d711035e29b18b2e1c03d47f41396d803d06ef15b6c97f45b75f73f04/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:84cfd4d92668ef5766cc42a9c9474b88960ac2b860767e6e7be255c6fddbd34a", size = 63816, upload-time = "2025-12-06T13:25:15.356Z" }, - { url = "https://files.pythonhosted.org/packages/b4/21/e2901381ed0df62e2308380f30d9c4d87d6b74e33a84faed3478d33a7197/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:60fc025437f9a7c2cc45e0c19ed68ed08ba672be2c5575fd9d98bdd8f01dd61f", size = 56348, upload-time = "2025-12-06T13:25:16.559Z" }, - { url = "https://files.pythonhosted.org/packages/c4/16/3d788388a178a0407aa814b976fe61bfa4af6760d9aac566e59da6e4a8b4/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:edc8446196f04b71d3af76c0bd1fe0a45066ac5bffecca88adb9626ee28c266f", size = 72842, upload-time = "2025-12-06T13:25:18.055Z" }, - { url = "https://files.pythonhosted.org/packages/a6/63/c15b1f8bd47ea48a5a2d52a4ec61f037062932ea6434ab916107b58e861e/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e99f6fa6509c037794da57f906ade271f52276c956d00f748e5b118462021d48", size = 62651, upload-time = "2025-12-06T13:25:19.191Z" }, - { url = "https://files.pythonhosted.org/packages/bd/b8/f544a2e37c778d59208966d4ef19742a0be37c12fc8149ff34483c176616/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d94020ef09f624d841aa9a3a6029df8cf65d60d7a6d5c8687579fa68bd679b65", size = 58295, upload-time = "2025-12-06T13:25:20.822Z" }, - { url = "https://files.pythonhosted.org/packages/03/99/1fae8a3b7ac181e36f6e7864a62d42d5b1f4fa7edf408c6711e28fba6b4d/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f64ce70d89942a23602dee910dec9b48e5edf94351e1b378186b74fcc00d7f66", size = 60960, upload-time = "2025-12-06T13:25:22.099Z" }, - { url = "https://files.pythonhosted.org/packages/9d/9e/cd4c727742345ad8384569a4466f1a1428f4e5cc94d9c2ab2f53d30be3fe/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8ea99f56e45c469818b9781903be86ba4153769f007ba0655fa3b46dc332803d", size = 74863, upload-time = "2025-12-06T13:25:23.442Z" }, - { url = "https://files.pythonhosted.org/packages/28/86/a236ecfc5b494e1e922da149689f690abc84248c7c1358f5605b8c9fdd60/pybase64-1.4.3-cp314-cp314t-win32.whl", hash = "sha256:343b1901103cc72362fd1f842524e3bb24978e31aea7ff11e033af7f373f66ab", size = 34513, upload-time = "2025-12-06T13:25:24.592Z" }, - { url = "https://files.pythonhosted.org/packages/56/ce/ca8675f8d1352e245eb012bfc75429ee9cf1f21c3256b98d9a329d44bf0f/pybase64-1.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:57aff6f7f9dea6705afac9d706432049642de5b01080d3718acc23af87c5af76", size = 36702, upload-time = "2025-12-06T13:25:25.72Z" }, - { url = "https://files.pythonhosted.org/packages/3b/30/4a675864877397179b09b720ee5fcb1cf772cf7bebc831989aff0a5f79c1/pybase64-1.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:e906aa08d4331e799400829e0f5e4177e76a3281e8a4bc82ba114c6b30e405c9", size = 31904, upload-time = "2025-12-06T13:25:26.826Z" }, - { url = "https://files.pythonhosted.org/packages/17/45/92322aec1b6979e789b5710f73c59f2172bc37c8ce835305434796824b7b/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:2baaa092f3475f3a9c87ac5198023918ea8b6c125f4c930752ab2cbe3cd1d520", size = 38746, upload-time = "2025-12-06T13:26:25.869Z" }, - { url = "https://files.pythonhosted.org/packages/11/94/f1a07402870388fdfc2ecec0c718111189732f7d0f2d7fe1386e19e8fad0/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:cde13c0764b1af07a631729f26df019070dad759981d6975527b7e8ecb465b6c", size = 32573, upload-time = "2025-12-06T13:26:27.792Z" }, - { url = "https://files.pythonhosted.org/packages/fa/8f/43c3bb11ca9bacf81cb0b7a71500bb65b2eda6d5fe07433c09b543de97f3/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5c29a582b0ea3936d02bd6fe9bf674ab6059e6e45ab71c78404ab2c913224414", size = 43461, upload-time = "2025-12-06T13:26:28.906Z" }, - { url = "https://files.pythonhosted.org/packages/2d/4c/2a5258329200be57497d3972b5308558c6de42e3749c6cc2aa1cbe34b25a/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b6b664758c804fa919b4f1257aa8cf68e95db76fc331de5f70bfc3a34655afe1", size = 36058, upload-time = "2025-12-06T13:26:30.092Z" }, - { url = "https://files.pythonhosted.org/packages/ea/6d/41faa414cde66ec023b0ca8402a8f11cb61731c3dc27c082909cbbd1f929/pybase64-1.4.3-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:f7537fa22ae56a0bf51e4b0ffc075926ad91c618e1416330939f7ef366b58e3b", size = 36231, upload-time = "2025-12-06T13:26:31.656Z" }, -] - -[[package]] -name = "pycapnp" -version = "2.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/7b/b2f356bc24220068beffc03e94062e8059a1383addb837303794398aec36/pycapnp-2.2.2.tar.gz", hash = "sha256:7f6c23c2283173a3cb6f1a5086dd0114779d508a7cd1b138d25a6357857d02b6", size = 730142, upload-time = "2026-01-21T01:22:13.73Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/76/f8f81d32ddf950e934ec144facbc112e5acbef31a63ba5be0c5f34a00fd5/pycapnp-2.2.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b86cb8ea5b8011b562c4e022325a826a62f91196ceb5aa33a766c0bea0b8fd3", size = 1605194, upload-time = "2026-01-21T01:20:29.604Z" }, - { url = "https://files.pythonhosted.org/packages/50/dd/a31be782d56a8648fef899f39aeeab867cf544a6b170871e3f4cbfc58af6/pycapnp-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2353531cfa669e3eeb99be9f993573341650276abec46676d687cc12b3e6b6d9", size = 1486613, upload-time = "2026-01-21T01:20:31.415Z" }, - { url = "https://files.pythonhosted.org/packages/aa/bf/8da830dda94eb7327c6508d6c26fbd964897d742f8c1c0ec48623f0c515b/pycapnp-2.2.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ee27bdc78c7ccd8eaa0fe31e09f0ec4ef31deda3f475fc9373bb4b0de8083053", size = 5186701, upload-time = "2026-01-21T01:20:32.836Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a1/13d0baa2f337f4f6fe8c2142646ba437a26b9c433f5d7ce016a912bad052/pycapnp-2.2.2-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:a8ded808911d1d7a9a2197626c09eea6e269e74dc1276760789538b1efcf6cd5", size = 5239464, upload-time = "2026-01-21T01:20:34.793Z" }, - { url = "https://files.pythonhosted.org/packages/82/76/0451c64b5f0132e4b75a0afe8cec957c8bf8fa981264a7c0b264cb94663e/pycapnp-2.2.2-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:59e92e1db40041d82a95eab0bd8de2676ce50c6b97c1457e2edde4d134b6d046", size = 5542887, upload-time = "2026-01-21T01:20:36.463Z" }, - { url = "https://files.pythonhosted.org/packages/04/00/d025d68d9a5330d55cbe2d018091cacfef0835c3ad422eb6778c4525041f/pycapnp-2.2.2-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:ee1e9ac2f0b80fa892b922b60e36efc925d072ecf1204ba3e59d8d9ac7c3dc83", size = 5659696, upload-time = "2026-01-21T01:20:38.069Z" }, - { url = "https://files.pythonhosted.org/packages/58/b7/28f7c539a5f4cbaa12e55ec27d081d11473464230f2e801e4714606d3453/pycapnp-2.2.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:53273b385be78ed8ac997ff8697f2a4c760e93c190b509822a937de5531f4861", size = 5413827, upload-time = "2026-01-21T01:20:39.781Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a7/83bc13d90675f0cee8a38d4ad8401bb2f8662c543b3a6622aeffb7b56b1e/pycapnp-2.2.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:812cbdd002bc542b63f969b85c6b9041dfdaf4185613635a6d4feea84c9092fa", size = 6046815, upload-time = "2026-01-21T01:20:42.172Z" }, - { url = "https://files.pythonhosted.org/packages/0d/8a/80f46baa1684bbcc4754ce22c5a44693a1209a64de6df2b256b85b8b8a97/pycapnp-2.2.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9c330218a44bd649b96f565dbf5326d183fdd20f9887bdedfeabd73f0366c2e1", size = 6367625, upload-time = "2026-01-21T01:20:44.004Z" }, - { url = "https://files.pythonhosted.org/packages/02/00/60e82eaf6b4e78d887157bf9f18234c852771cc575355e63d1114c4a5d79/pycapnp-2.2.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:796aa0ba18bcd4e6b2815471bbed059ad7ee8a815a30e81ac8a9aa030ec7818d", size = 6487265, upload-time = "2026-01-21T01:20:46.137Z" }, - { url = "https://files.pythonhosted.org/packages/57/6e/2dedd8f95dc22357c50a775ee2b8711b3d711f30344d244141e0e1962c3e/pycapnp-2.2.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:251a6abdd64b9b11d2a8e16fc365b922ef6ba6c968959b72a3a3d9d8ec8cc8d7", size = 6576699, upload-time = "2026-01-21T01:20:47.987Z" }, - { url = "https://files.pythonhosted.org/packages/2f/53/f7f69ed1d11ea30ea4f0f6d8319fbc18bc8781c480c118005e0a394492a7/pycapnp-2.2.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6aab811e0fcc27ae8bf5f04dedaa7e0af47e0d4db51d9c85ab0d2dad26a46bd7", size = 6344114, upload-time = "2026-01-21T01:20:50.367Z" }, - { url = "https://files.pythonhosted.org/packages/ab/78/ab78ee42797ff44c7e1fc0d1aa9396c6742cb05ff01a7cdf9c8f19e0defe/pycapnp-2.2.2-cp312-cp312-win32.whl", hash = "sha256:5061c85dd8f843b2656720ca6976d2a9b418845580c6f6d9602f7119fc2208d5", size = 1047207, upload-time = "2026-01-21T01:20:51.842Z" }, - { url = "https://files.pythonhosted.org/packages/4c/fb/6edf56d5144c476270fa8b2e6a660ef5a188fb0097193e342618fbcb0210/pycapnp-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:700eb8c77405222903af3fb5a371c0d766f86139c3d51f4bff41ccd6403b51f9", size = 1185178, upload-time = "2026-01-21T01:20:53.429Z" }, - { url = "https://files.pythonhosted.org/packages/ba/70/376c3f1be4ba453584bc96a9e6a7372486ce920cb9c0869c06066e77d626/pycapnp-2.2.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:90138fceca1e85ea3eaa0de6656e33c4bef1c8da3c191db0a5b5bacc969f7889", size = 1603886, upload-time = "2026-01-21T01:20:55.383Z" }, - { url = "https://files.pythonhosted.org/packages/b6/11/2728b563f3f25d826024136cd3aab39f5d1727195de5c90a2ba3d232e897/pycapnp-2.2.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cd0549036bfddb003f8e371c3c4ed3f56ac847953eb57cdd371100bb52afa64e", size = 1485731, upload-time = "2026-01-21T01:20:57.587Z" }, - { url = "https://files.pythonhosted.org/packages/77/1b/ab9bb376e7314b92dde24843fcf3d6459f10e48d95eb54d25912f973f5d7/pycapnp-2.2.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:7c5a8f6d96017a7bb7e202fa8920fcdad119deab0d761f9aca1e6a4755376cef", size = 5209046, upload-time = "2026-01-21T01:20:59.049Z" }, - { url = "https://files.pythonhosted.org/packages/5f/62/303548df0316740caad513e4b81b18b2db1990785f3f01c36fd19889e7d4/pycapnp-2.2.2-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:bba31a8ba8ec32a04c5a14a5e469df7e1f1b85e169f49f7c2edfdfb78ec5075f", size = 5235782, upload-time = "2026-01-21T01:21:00.676Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ec/df320105d17e118207f01208af1e505d48981856c07fe10b04a8feab39d9/pycapnp-2.2.2-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:2decdaeb517120e152f7d9ddede393a830087ac3f1024c5224f2ec58e1735abd", size = 5544647, upload-time = "2026-01-21T01:21:02.812Z" }, - { url = "https://files.pythonhosted.org/packages/03/47/bc3ea9b0d71cc3e0694993a5907ac56aab2c1ad803697be068fff55a0e1b/pycapnp-2.2.2-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:e879127ac9580005efcd51ec9ae903f1dd98954fb4ea88db9cf534e9a4afb379", size = 5596651, upload-time = "2026-01-21T01:21:04.504Z" }, - { url = "https://files.pythonhosted.org/packages/84/82/c500220d4eccca845a06f2f9d0747ad8043f8b893ef5278e2019cbc906cc/pycapnp-2.2.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:7092c2393191221b8ce1c03ddc1343d1ff26d36129a831017d2371867e9c09bb", size = 5393131, upload-time = "2026-01-21T01:21:06.459Z" }, - { url = "https://files.pythonhosted.org/packages/ed/61/be35f0b8d81cf107a9d0329d180f1fd8f5d6d75e117fdf7fcad779443f17/pycapnp-2.2.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:682812bf9ba4a60309b7150763cd5abed9341a31375398f3d2f60fee26143e13", size = 6071761, upload-time = "2026-01-21T01:21:08.238Z" }, - { url = "https://files.pythonhosted.org/packages/54/c0/d09da26ebb1bf0a130650a940b8e32f41219241c8d5f9c0891f15343dd9c/pycapnp-2.2.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6f321dbf33767bc2fd77dd66265177284697089f9e45b6dee7fd462b2b5cf918", size = 6369216, upload-time = "2026-01-21T01:21:10.338Z" }, - { url = "https://files.pythonhosted.org/packages/3d/58/a651de950437d8ef9fa91c3cce84ecd068e05011d421a6cf288f930f331c/pycapnp-2.2.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1d443d38f1cbeaec5b20b420117885fd5de812c5687e3bc8456d2e2c5cba371e", size = 6488273, upload-time = "2026-01-21T01:21:12.127Z" }, - { url = "https://files.pythonhosted.org/packages/4b/28/254f511272fde9fea1220807d759bcbdb47ecdff64ac860499be9aeddeaa/pycapnp-2.2.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:20a31fedb7ef30ec53d5d2d951a7cec8f639c954557093a3ba3d11aba5d174b4", size = 6542078, upload-time = "2026-01-21T01:21:14.581Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b3/eea39216b7b59cefd21e6783e38a4d78db7e58c4f8f1f45e8351057432b7/pycapnp-2.2.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fbefb388464501899233d0b7d27256edd22c3cebb5a312da5d9f246671e9455e", size = 6333374, upload-time = "2026-01-21T01:21:16.522Z" }, - { url = "https://files.pythonhosted.org/packages/be/08/42ab52a3e7e5381d8fd7ae227ae75f54454f9e339d214ebe92763bb78115/pycapnp-2.2.2-cp313-cp313-win32.whl", hash = "sha256:a25e3b3ef40d430309acc7c024aa62b1ee6b2e7a85b341a8b7a8a6f8e29d4133", size = 1046173, upload-time = "2026-01-21T01:21:18.074Z" }, - { url = "https://files.pythonhosted.org/packages/34/d4/fec2023c4709d3631fe24e09dae2badc23d613f1288b4a6b398302945984/pycapnp-2.2.2-cp313-cp313-win_amd64.whl", hash = "sha256:db65c9ec9d69bac6d19092db43b5e1f3cb2c680810418fd1e02316f7ef157fc1", size = 1184414, upload-time = "2026-01-21T01:21:19.334Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[package.optional-dependencies] -email = [ - { name = "email-validator" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, -] - -[[package]] -name = "pydantic-settings" -version = "2.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, -] - -[[package]] -name = "pydocket" -version = "0.16.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cloudpickle" }, - { name = "fakeredis", extra = ["lua"] }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-prometheus" }, - { name = "opentelemetry-instrumentation" }, - { name = "prometheus-client" }, - { name = "py-key-value-aio", extra = ["memory", "redis"] }, - { name = "python-json-logger" }, - { name = "redis" }, - { name = "rich" }, - { name = "typer" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/00/26befe5f58df7cd1aeda4a8d10bc7d1908ffd86b80fd995e57a2a7b3f7bd/pydocket-0.16.6.tar.gz", hash = "sha256:b96c96ad7692827214ed4ff25fcf941ec38371314db5dcc1ae792b3e9d3a0294", size = 299054, upload-time = "2026-01-09T22:09:15.405Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/3f/7483e5a6dc6326b6e0c640619b5c5bd1d6e3c20e54d58f5fb86267cef00e/pydocket-0.16.6-py3-none-any.whl", hash = "sha256:683d21e2e846aa5106274e7d59210331b242d7fb0dce5b08d3b82065663ed183", size = 67697, upload-time = "2026-01-09T22:09:13.436Z" }, -] - -[[package]] -name = "pyee" -version = "13.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/04/e7c1fe4dc78a6fdbfd6c337b1c3732ff543b8a397683ab38378447baa331/pyee-13.0.1.tar.gz", hash = "sha256:0b931f7c14535667ed4c7e0d531716368715e860b988770fc7eb8578d1f67fc8", size = 31655, upload-time = "2026-02-14T21:12:28.044Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c4/b4d4827c93ef43c01f599ef31453ccc1c132b353284fc6c87d535c233129/pyee-13.0.1-py3-none-any.whl", hash = "sha256:af2f8fede4171ef667dfded53f96e2ed0d6e6bd7ee3bb46437f77e3b57689228", size = 15659, upload-time = "2026-02-14T21:12:26.263Z" }, -] - -[[package]] -name = "pygetwindow" -version = "0.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyrect" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e1/70/c7a4f46dbf06048c6d57d9489b8e0f9c4c3d36b7479f03c5ca97eaa2541d/PyGetWindow-0.0.9.tar.gz", hash = "sha256:17894355e7d2b305cd832d717708384017c1698a90ce24f6f7fbf0242dd0a688", size = 9699, upload-time = "2020-10-04T02:12:50.806Z" } - -[[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 = "pyjwt" -version = "2.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, -] - -[package.optional-dependencies] -crypto = [ - { name = "cryptography" }, -] - -[[package]] -name = "pymsgbox" -version = "2.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/6a/e80da7594ee598a776972d09e2813df2b06b3bc29218f440631dfa7c78a8/pymsgbox-2.0.1.tar.gz", hash = "sha256:98d055c49a511dcc10fa08c3043e7102d468f5e4b3a83c6d3c61df722c7d798d", size = 20768, upload-time = "2025-09-09T00:38:56.863Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/3e/08c8cac81b2b2f7502746e6b9c8e5b0ec6432cd882c605560fc409aaf087/pymsgbox-2.0.1-py3-none-any.whl", hash = "sha256:5de8ec19bca2ca7e6c09d39c817c83f17c75cee80275235f43a9931db699f73b", size = 9994, upload-time = "2025-09-09T00:38:55.672Z" }, -] - -[[package]] -name = "pyobjc-core" -version = "12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/b6/d5612eb40be4fd5ef88c259339e6313f46ba67577a95d86c3470b951fce0/pyobjc_core-12.1.tar.gz", hash = "sha256:2bb3903f5387f72422145e1466b3ac3f7f0ef2e9960afa9bcd8961c5cbf8bd21", size = 1000532, upload-time = "2025-11-14T10:08:28.292Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/5a/6b15e499de73050f4a2c88fff664ae154307d25dc04da8fb38998a428358/pyobjc_core-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:818bcc6723561f207e5b5453efe9703f34bc8781d11ce9b8be286bb415eb4962", size = 678335, upload-time = "2025-11-14T09:32:20.107Z" }, - { url = "https://files.pythonhosted.org/packages/f4/d2/29e5e536adc07bc3d33dd09f3f7cf844bf7b4981820dc2a91dd810f3c782/pyobjc_core-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:01c0cf500596f03e21c23aef9b5f326b9fb1f8f118cf0d8b66749b6cf4cbb37a", size = 677370, upload-time = "2025-11-14T09:33:05.273Z" }, - { url = "https://files.pythonhosted.org/packages/1b/f0/4b4ed8924cd04e425f2a07269943018d43949afad1c348c3ed4d9d032787/pyobjc_core-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:177aaca84bb369a483e4961186704f64b2697708046745f8167e818d968c88fc", size = 719586, upload-time = "2025-11-14T09:33:53.302Z" }, - { url = "https://files.pythonhosted.org/packages/25/98/9f4ed07162de69603144ff480be35cd021808faa7f730d082b92f7ebf2b5/pyobjc_core-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:844515f5d86395b979d02152576e7dee9cc679acc0b32dc626ef5bda315eaa43", size = 670164, upload-time = "2025-11-14T09:34:37.458Z" }, - { url = "https://files.pythonhosted.org/packages/62/50/dc076965c96c7f0de25c0a32b7f8aa98133ed244deaeeacfc758783f1f30/pyobjc_core-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:453b191df1a4b80e756445b935491b974714456ae2cbae816840bd96f86db882", size = 712204, upload-time = "2025-11-14T09:35:24.148Z" }, -] - -[[package]] -name = "pyobjc-framework-cocoa" -version = "12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyobjc-core" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/02/a3/16ca9a15e77c061a9250afbae2eae26f2e1579eb8ca9462ae2d2c71e1169/pyobjc_framework_cocoa-12.1.tar.gz", hash = "sha256:5556c87db95711b985d5efdaaf01c917ddd41d148b1e52a0c66b1a2e2c5c1640", size = 2772191, upload-time = "2025-11-14T10:13:02.069Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/bf/ee4f27ec3920d5c6fc63c63e797c5b2cc4e20fe439217085d01ea5b63856/pyobjc_framework_cocoa-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:547c182837214b7ec4796dac5aee3aa25abc665757b75d7f44f83c994bcb0858", size = 384590, upload-time = "2025-11-14T09:41:17.336Z" }, - { url = "https://files.pythonhosted.org/packages/ad/31/0c2e734165abb46215797bd830c4bdcb780b699854b15f2b6240515edcc6/pyobjc_framework_cocoa-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5a3dcd491cacc2f5a197142b3c556d8aafa3963011110102a093349017705118", size = 384689, upload-time = "2025-11-14T09:41:41.478Z" }, - { url = "https://files.pythonhosted.org/packages/23/3b/b9f61be7b9f9b4e0a6db18b3c35c4c4d589f2d04e963e2174d38c6555a92/pyobjc_framework_cocoa-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:914b74328c22d8ca261d78c23ef2befc29776e0b85555973927b338c5734ca44", size = 388843, upload-time = "2025-11-14T09:42:05.719Z" }, - { url = "https://files.pythonhosted.org/packages/59/bb/f777cc9e775fc7dae77b569254570fe46eb842516b3e4fe383ab49eab598/pyobjc_framework_cocoa-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:03342a60fc0015bcdf9b93ac0b4f457d3938e9ef761b28df9564c91a14f0129a", size = 384932, upload-time = "2025-11-14T09:42:29.771Z" }, - { url = "https://files.pythonhosted.org/packages/58/27/b457b7b37089cad692c8aada90119162dfb4c4a16f513b79a8b2b022b33b/pyobjc_framework_cocoa-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6ba1dc1bfa4da42d04e93d2363491275fb2e2be5c20790e561c8a9e09b8cf2cc", size = 388970, upload-time = "2025-11-14T09:42:53.964Z" }, -] - -[[package]] -name = "pyobjc-framework-quartz" -version = "12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/94/18/cc59f3d4355c9456fc945eae7fe8797003c4da99212dd531ad1b0de8a0c6/pyobjc_framework_quartz-12.1.tar.gz", hash = "sha256:27f782f3513ac88ec9b6c82d9767eef95a5cf4175ce88a1e5a65875fee799608", size = 3159099, upload-time = "2025-11-14T10:21:24.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/9b/780f057e5962f690f23fdff1083a4cfda5a96d5b4d3bb49505cac4f624f2/pyobjc_framework_quartz-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7730cdce46c7e985535b5a42c31381af4aa6556e5642dc55b5e6597595e57a16", size = 218798, upload-time = "2025-11-14T10:00:01.236Z" }, - { url = "https://files.pythonhosted.org/packages/ba/2d/e8f495328101898c16c32ac10e7b14b08ff2c443a756a76fd1271915f097/pyobjc_framework_quartz-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:629b7971b1b43a11617f1460cd218bd308dfea247cd4ee3842eb40ca6f588860", size = 219206, upload-time = "2025-11-14T10:00:15.623Z" }, - { url = "https://files.pythonhosted.org/packages/67/43/b1f0ad3b842ab150a7e6b7d97f6257eab6af241b4c7d14cb8e7fde9214b8/pyobjc_framework_quartz-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:53b84e880c358ba1ddcd7e8d5ea0407d760eca58b96f0d344829162cda5f37b3", size = 224317, upload-time = "2025-11-14T10:00:30.703Z" }, - { url = "https://files.pythonhosted.org/packages/4a/00/96249c5c7e5aaca5f688ca18b8d8ad05cd7886ebd639b3c71a6a4cadbe75/pyobjc_framework_quartz-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:42d306b07f05ae7d155984503e0fb1b701fecd31dcc5c79fe8ab9790ff7e0de0", size = 219558, upload-time = "2025-11-14T10:00:45.476Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a6/708a55f3ff7a18c403b30a29a11dccfed0410485a7548c60a4b6d4cc0676/pyobjc_framework_quartz-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0cc08fddb339b2760df60dea1057453557588908e42bdc62184b6396ce2d6e9a", size = 224580, upload-time = "2025-11-14T10:01:00.091Z" }, -] - -[[package]] -name = "pyperclip" -version = "1.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, -] - -[[package]] -name = "pypika" -version = "0.51.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/9b/76b931b449fee149359bda6ffc3bf711a7a2a2e9bfd7a32c2668e2069018/pypika-0.51.0.tar.gz", hash = "sha256:ba71a4e4f320221727619401b49b93491c589d794d5347a97bf1e8dfaf8676bb", size = 80932, upload-time = "2026-02-01T18:18:44.103Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/1c/54b7a741a5e1bdd366d6767c28d74421c7191b2e2109d2b773d28d49ecc6/pypika-0.51.0-py2.py3-none-any.whl", hash = "sha256:219f14f2dcf3c0047e25bd47227d43e227fc59170ea9bb7d14f4e0945442ce3e", size = 60581, upload-time = "2026-02-01T18:18:42.187Z" }, -] - -[[package]] -name = "pyproject-hooks" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, -] - -[[package]] -name = "pyreadline3" -version = "3.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839, upload-time = "2024-09-19T02:40:10.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178, upload-time = "2024-09-19T02:40:08.598Z" }, -] - -[[package]] -name = "pyrect" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/04/2ba023d5f771b645f7be0c281cdacdcd939fe13d1deb331fc5ed1a6b3a98/PyRect-0.2.0.tar.gz", hash = "sha256:f65155f6df9b929b67caffbd57c0947c5ae5449d3b580d178074bffb47a09b78", size = 17219, upload-time = "2022-03-16T04:45:52.36Z" } - -[[package]] -name = "pyscreeze" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/f0/cb456ac4f1a73723d5b866933b7986f02bacea27516629c00f8e7da94c2d/pyscreeze-1.0.1.tar.gz", hash = "sha256:cf1662710f1b46aa5ff229ee23f367da9e20af4a78e6e365bee973cad0ead4be", size = 27826, upload-time = "2024-08-20T23:03:07.291Z" } - -[[package]] -name = "pytest" -version = "8.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -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 = "0.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8e/c4/453c52c659521066969523e87d85d54139bbd17b78f09532fb8eb8cdb58e/pytest_asyncio-0.26.0.tar.gz", hash = "sha256:c4df2a697648241ff39e7f0e4a73050b03f123f760673956cf0d72a4990e312f", size = 54156, upload-time = "2025-03-25T06:22:28.883Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/7f/338843f449ace853647ace35870874f69a764d251872ed1b4de9f234822c/pytest_asyncio-0.26.0-py3-none-any.whl", hash = "sha256:7b51ed894f4fbea1340262bdae5135797ebbe21d8638978e35d31c6d19f72fb0", size = 19694, upload-time = "2025-03-25T06:22:27.807Z" }, -] - -[[package]] -name = "pytest-cov" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coverage" }, - { name = "pluggy" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, -] - -[[package]] -name = "pytest-mock" -version = "3.15.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, -] - -[[package]] -name = "python-jose" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ecdsa" }, - { name = "pyasn1" }, - { name = "rsa" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c6/77/3a1c9039db7124eb039772b935f2244fbb73fc8ee65b9acf2375da1c07bf/python_jose-3.5.0.tar.gz", hash = "sha256:fb4eaa44dbeb1c26dcc69e4bd7ec54a1cb8dd64d3b4d81ef08d90ff453f2b01b", size = 92726, upload-time = "2025-05-28T17:31:54.288Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/c3/0bd11992072e6a1c513b16500a5d07f91a24017c5909b02c72c62d7ad024/python_jose-3.5.0-py2.py3-none-any.whl", hash = "sha256:abd1202f23d34dfad2c3d28cb8617b90acf34132c7afd60abd0b0b7d3cb55771", size = 34624, upload-time = "2025-05-28T17:31:52.802Z" }, -] - -[package.optional-dependencies] -cryptography = [ - { name = "cryptography" }, -] - -[[package]] -name = "python-json-logger" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, -] - -[[package]] -name = "python3-xlib" -version = "0.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ef/c6/2c5999de3bb1533521f1101e8fe56fd9c266732f4d48011c7c69b29d12ae/python3-xlib-0.15.tar.gz", hash = "sha256:dc4245f3ae4aa5949c1d112ee4723901ade37a96721ba9645f2bfa56e5b383f8", size = 132828, upload-time = "2014-05-31T12:28:59.603Z" } - -[[package]] -name = "pytokens" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, - { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, - { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, - { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, - { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, - { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, - { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, - { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, - { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, - { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, - { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, - { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, - { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, - { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, - { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, - { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, - { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, - { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, - { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, - { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, - { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, -] - -[[package]] -name = "pytweening" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/0c/c16bc93ac2755bac0066a8ecbd2a2931a1735a6fffd99a2b9681c7e83e90/pytweening-1.2.0.tar.gz", hash = "sha256:243318b7736698066c5f362ec5c2b6434ecf4297c3c8e7caa8abfe6af4cac71b", size = 171241, upload-time = "2024-02-20T03:37:56.809Z" } - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { 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 = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "pyzmq" -version = "27.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "implementation_name == 'pypy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" }, - { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" }, - { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" }, - { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" }, - { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" }, - { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" }, - { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" }, - { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" }, - { url = "https://files.pythonhosted.org/packages/60/cb/84a13459c51da6cec1b7b1dc1a47e6db6da50b77ad7fd9c145842750a011/pyzmq-27.1.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:93ad4b0855a664229559e45c8d23797ceac03183c7b6f5b4428152a6b06684a5", size = 1122436, upload-time = "2025-09-08T23:08:20.801Z" }, - { url = "https://files.pythonhosted.org/packages/dc/b6/94414759a69a26c3dd674570a81813c46a078767d931a6c70ad29fc585cb/pyzmq-27.1.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:fbb4f2400bfda24f12f009cba62ad5734148569ff4949b1b6ec3b519444342e6", size = 1156301, upload-time = "2025-09-08T23:08:22.47Z" }, - { url = "https://files.pythonhosted.org/packages/a5/ad/15906493fd40c316377fd8a8f6b1f93104f97a752667763c9b9c1b71d42d/pyzmq-27.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:e343d067f7b151cfe4eb3bb796a7752c9d369eed007b91231e817071d2c2fec7", size = 1341197, upload-time = "2025-09-08T23:08:24.286Z" }, - { url = "https://files.pythonhosted.org/packages/14/1d/d343f3ce13db53a54cb8946594e567410b2125394dafcc0268d8dda027e0/pyzmq-27.1.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:08363b2011dec81c354d694bdecaef4770e0ae96b9afea70b3f47b973655cc05", size = 897275, upload-time = "2025-09-08T23:08:26.063Z" }, - { url = "https://files.pythonhosted.org/packages/69/2d/d83dd6d7ca929a2fc67d2c3005415cdf322af7751d773524809f9e585129/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d54530c8c8b5b8ddb3318f481297441af102517602b569146185fa10b63f4fa9", size = 660469, upload-time = "2025-09-08T23:08:27.623Z" }, - { url = "https://files.pythonhosted.org/packages/3e/cd/9822a7af117f4bc0f1952dbe9ef8358eb50a24928efd5edf54210b850259/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3afa12c392f0a44a2414056d730eebc33ec0926aae92b5ad5cf26ebb6cc128", size = 847961, upload-time = "2025-09-08T23:08:29.672Z" }, - { url = "https://files.pythonhosted.org/packages/9a/12/f003e824a19ed73be15542f172fd0ec4ad0b60cf37436652c93b9df7c585/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c65047adafe573ff023b3187bb93faa583151627bc9c51fc4fb2c561ed689d39", size = 1650282, upload-time = "2025-09-08T23:08:31.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4a/e82d788ed58e9a23995cee70dbc20c9aded3d13a92d30d57ec2291f1e8a3/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:90e6e9441c946a8b0a667356f7078d96411391a3b8f80980315455574177ec97", size = 2024468, upload-time = "2025-09-08T23:08:33.543Z" }, - { url = "https://files.pythonhosted.org/packages/d9/94/2da0a60841f757481e402b34bf4c8bf57fa54a5466b965de791b1e6f747d/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:add071b2d25f84e8189aaf0882d39a285b42fa3853016ebab234a5e78c7a43db", size = 1885394, upload-time = "2025-09-08T23:08:35.51Z" }, - { url = "https://files.pythonhosted.org/packages/4f/6f/55c10e2e49ad52d080dc24e37adb215e5b0d64990b57598abc2e3f01725b/pyzmq-27.1.0-cp313-cp313t-win32.whl", hash = "sha256:7ccc0700cfdf7bd487bea8d850ec38f204478681ea02a582a8da8171b7f90a1c", size = 574964, upload-time = "2025-09-08T23:08:37.178Z" }, - { url = "https://files.pythonhosted.org/packages/87/4d/2534970ba63dd7c522d8ca80fb92777f362c0f321900667c615e2067cb29/pyzmq-27.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8085a9fba668216b9b4323be338ee5437a235fe275b9d1610e422ccc279733e2", size = 641029, upload-time = "2025-09-08T23:08:40.595Z" }, - { url = "https://files.pythonhosted.org/packages/f6/fa/f8aea7a28b0641f31d40dea42d7ef003fded31e184ef47db696bc74cd610/pyzmq-27.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6bb54ca21bcfe361e445256c15eedf083f153811c37be87e0514934d6913061e", size = 561541, upload-time = "2025-09-08T23:08:42.668Z" }, - { url = "https://files.pythonhosted.org/packages/87/45/19efbb3000956e82d0331bafca5d9ac19ea2857722fa2caacefb6042f39d/pyzmq-27.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a", size = 1341197, upload-time = "2025-09-08T23:08:44.973Z" }, - { url = "https://files.pythonhosted.org/packages/48/43/d72ccdbf0d73d1343936296665826350cb1e825f92f2db9db3e61c2162a2/pyzmq-27.1.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea", size = 897175, upload-time = "2025-09-08T23:08:46.601Z" }, - { url = "https://files.pythonhosted.org/packages/2f/2e/a483f73a10b65a9ef0161e817321d39a770b2acf8bcf3004a28d90d14a94/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96", size = 660427, upload-time = "2025-09-08T23:08:48.187Z" }, - { url = "https://files.pythonhosted.org/packages/f5/d2/5f36552c2d3e5685abe60dfa56f91169f7a2d99bbaf67c5271022ab40863/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d", size = 847929, upload-time = "2025-09-08T23:08:49.76Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2a/404b331f2b7bf3198e9945f75c4c521f0c6a3a23b51f7a4a401b94a13833/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146", size = 1650193, upload-time = "2025-09-08T23:08:51.7Z" }, - { url = "https://files.pythonhosted.org/packages/1c/0b/f4107e33f62a5acf60e3ded67ed33d79b4ce18de432625ce2fc5093d6388/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd", size = 2024388, upload-time = "2025-09-08T23:08:53.393Z" }, - { url = "https://files.pythonhosted.org/packages/0d/01/add31fe76512642fd6e40e3a3bd21f4b47e242c8ba33efb6809e37076d9b/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a", size = 1885316, upload-time = "2025-09-08T23:08:55.702Z" }, - { url = "https://files.pythonhosted.org/packages/c4/59/a5f38970f9bf07cee96128de79590bb354917914a9be11272cfc7ff26af0/pyzmq-27.1.0-cp314-cp314t-win32.whl", hash = "sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92", size = 587472, upload-time = "2025-09-08T23:08:58.18Z" }, - { url = "https://files.pythonhosted.org/packages/70/d8/78b1bad170f93fcf5e3536e70e8fadac55030002275c9a29e8f5719185de/pyzmq-27.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0", size = 661401, upload-time = "2025-09-08T23:08:59.802Z" }, - { url = "https://files.pythonhosted.org/packages/81/d6/4bfbb40c9a0b42fc53c7cf442f6385db70b40f74a783130c5d0a5aa62228/pyzmq-27.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7", size = 575170, upload-time = "2025-09-08T23:09:01.418Z" }, -] - -[[package]] -name = "readme-renderer" -version = "44.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "nh3" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/a9/104ec9234c8448c4379768221ea6df01260cd6c2ce13182d4eac531c8342/readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", size = 32056, upload-time = "2024-07-08T15:00:57.805Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", size = 13310, upload-time = "2024-07-08T15:00:56.577Z" }, -] - -[[package]] -name = "redis" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/c8/983d5c6579a411d8a99bc5823cc5712768859b5ce2c8afe1a65b37832c81/redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c", size = 4796669, upload-time = "2025-11-19T15:54:39.961Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/f0/8956f8a86b20d7bb9d6ac0187cf4cd54d8065bc9a1a09eb8011d4d326596/redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b", size = 354159, upload-time = "2025-11-19T15:54:38.064Z" }, -] - -[[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 = "regex" -version = "2026.1.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/86/07d5056945f9ec4590b518171c4254a5925832eb727b56d3c38a7476f316/regex-2026.1.15.tar.gz", hash = "sha256:164759aa25575cbc0651bef59a0b18353e54300d79ace8084c818ad8ac72b7d5", size = 414811, upload-time = "2026-01-14T23:18:02.775Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/81/10d8cf43c807d0326efe874c1b79f22bfb0fb226027b0b19ebc26d301408/regex-2026.1.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4c8fcc5793dde01641a35905d6731ee1548f02b956815f8f1cab89e515a5bdf1", size = 489398, upload-time = "2026-01-14T23:14:43.741Z" }, - { url = "https://files.pythonhosted.org/packages/90/b0/7c2a74e74ef2a7c32de724658a69a862880e3e4155cba992ba04d1c70400/regex-2026.1.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bfd876041a956e6a90ad7cdb3f6a630c07d491280bfeed4544053cd434901681", size = 291339, upload-time = "2026-01-14T23:14:45.183Z" }, - { url = "https://files.pythonhosted.org/packages/19/4d/16d0773d0c818417f4cc20aa0da90064b966d22cd62a8c46765b5bd2d643/regex-2026.1.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9250d087bc92b7d4899ccd5539a1b2334e44eee85d848c4c1aef8e221d3f8c8f", size = 289003, upload-time = "2026-01-14T23:14:47.25Z" }, - { url = "https://files.pythonhosted.org/packages/c6/e4/1fc4599450c9f0863d9406e944592d968b8d6dfd0d552a7d569e43bceada/regex-2026.1.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8a154cf6537ebbc110e24dabe53095e714245c272da9c1be05734bdad4a61aa", size = 798656, upload-time = "2026-01-14T23:14:48.77Z" }, - { url = "https://files.pythonhosted.org/packages/b2/e6/59650d73a73fa8a60b3a590545bfcf1172b4384a7df2e7fe7b9aab4e2da9/regex-2026.1.15-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8050ba2e3ea1d8731a549e83c18d2f0999fbc99a5f6bd06b4c91449f55291804", size = 864252, upload-time = "2026-01-14T23:14:50.528Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ab/1d0f4d50a1638849a97d731364c9a80fa304fec46325e48330c170ee8e80/regex-2026.1.15-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf065240704cb8951cc04972cf107063917022511273e0969bdb34fc173456c", size = 912268, upload-time = "2026-01-14T23:14:52.952Z" }, - { url = "https://files.pythonhosted.org/packages/dd/df/0d722c030c82faa1d331d1921ee268a4e8fb55ca8b9042c9341c352f17fa/regex-2026.1.15-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c32bef3e7aeee75746748643667668ef941d28b003bfc89994ecf09a10f7a1b5", size = 803589, upload-time = "2026-01-14T23:14:55.182Z" }, - { url = "https://files.pythonhosted.org/packages/66/23/33289beba7ccb8b805c6610a8913d0131f834928afc555b241caabd422a9/regex-2026.1.15-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d5eaa4a4c5b1906bd0d2508d68927f15b81821f85092e06f1a34a4254b0e1af3", size = 775700, upload-time = "2026-01-14T23:14:56.707Z" }, - { url = "https://files.pythonhosted.org/packages/e7/65/bf3a42fa6897a0d3afa81acb25c42f4b71c274f698ceabd75523259f6688/regex-2026.1.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:86c1077a3cc60d453d4084d5b9649065f3bf1184e22992bd322e1f081d3117fb", size = 787928, upload-time = "2026-01-14T23:14:58.312Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f5/13bf65864fc314f68cdd6d8ca94adcab064d4d39dbd0b10fef29a9da48fc/regex-2026.1.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:2b091aefc05c78d286657cd4db95f2e6313375ff65dcf085e42e4c04d9c8d410", size = 858607, upload-time = "2026-01-14T23:15:00.657Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/040e589834d7a439ee43fb0e1e902bc81bd58a5ba81acffe586bb3321d35/regex-2026.1.15-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:57e7d17f59f9ebfa9667e6e5a1c0127b96b87cb9cede8335482451ed00788ba4", size = 763729, upload-time = "2026-01-14T23:15:02.248Z" }, - { url = "https://files.pythonhosted.org/packages/9b/84/6921e8129687a427edf25a34a5594b588b6d88f491320b9de5b6339a4fcb/regex-2026.1.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c6c4dcdfff2c08509faa15d36ba7e5ef5fcfab25f1e8f85a0c8f45bc3a30725d", size = 850697, upload-time = "2026-01-14T23:15:03.878Z" }, - { url = "https://files.pythonhosted.org/packages/8a/87/3d06143d4b128f4229158f2de5de6c8f2485170c7221e61bf381313314b2/regex-2026.1.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf8ff04c642716a7f2048713ddc6278c5fd41faa3b9cab12607c7abecd012c22", size = 789849, upload-time = "2026-01-14T23:15:06.102Z" }, - { url = "https://files.pythonhosted.org/packages/77/69/c50a63842b6bd48850ebc7ab22d46e7a2a32d824ad6c605b218441814639/regex-2026.1.15-cp312-cp312-win32.whl", hash = "sha256:82345326b1d8d56afbe41d881fdf62f1926d7264b2fc1537f99ae5da9aad7913", size = 266279, upload-time = "2026-01-14T23:15:07.678Z" }, - { url = "https://files.pythonhosted.org/packages/f2/36/39d0b29d087e2b11fd8191e15e81cce1b635fcc845297c67f11d0d19274d/regex-2026.1.15-cp312-cp312-win_amd64.whl", hash = "sha256:4def140aa6156bc64ee9912383d4038f3fdd18fee03a6f222abd4de6357ce42a", size = 277166, upload-time = "2026-01-14T23:15:09.257Z" }, - { url = "https://files.pythonhosted.org/packages/28/32/5b8e476a12262748851fa8ab1b0be540360692325975b094e594dfebbb52/regex-2026.1.15-cp312-cp312-win_arm64.whl", hash = "sha256:c6c565d9a6e1a8d783c1948937ffc377dd5771e83bd56de8317c450a954d2056", size = 270415, upload-time = "2026-01-14T23:15:10.743Z" }, - { url = "https://files.pythonhosted.org/packages/f8/2e/6870bb16e982669b674cce3ee9ff2d1d46ab80528ee6bcc20fb2292efb60/regex-2026.1.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e69d0deeb977ffe7ed3d2e4439360089f9c3f217ada608f0f88ebd67afb6385e", size = 489164, upload-time = "2026-01-14T23:15:13.962Z" }, - { url = "https://files.pythonhosted.org/packages/dc/67/9774542e203849b0286badf67199970a44ebdb0cc5fb739f06e47ada72f8/regex-2026.1.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3601ffb5375de85a16f407854d11cca8fe3f5febbe3ac78fb2866bb220c74d10", size = 291218, upload-time = "2026-01-14T23:15:15.647Z" }, - { url = "https://files.pythonhosted.org/packages/b2/87/b0cda79f22b8dee05f774922a214da109f9a4c0eca5da2c9d72d77ea062c/regex-2026.1.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4c5ef43b5c2d4114eb8ea424bb8c9cec01d5d17f242af88b2448f5ee81caadbc", size = 288895, upload-time = "2026-01-14T23:15:17.788Z" }, - { url = "https://files.pythonhosted.org/packages/3b/6a/0041f0a2170d32be01ab981d6346c83a8934277d82c780d60b127331f264/regex-2026.1.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:968c14d4f03e10b2fd960f1d5168c1f0ac969381d3c1fcc973bc45fb06346599", size = 798680, upload-time = "2026-01-14T23:15:19.342Z" }, - { url = "https://files.pythonhosted.org/packages/58/de/30e1cfcdbe3e891324aa7568b7c968771f82190df5524fabc1138cb2d45a/regex-2026.1.15-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56a5595d0f892f214609c9f76b41b7428bed439d98dc961efafdd1354d42baae", size = 864210, upload-time = "2026-01-14T23:15:22.005Z" }, - { url = "https://files.pythonhosted.org/packages/64/44/4db2f5c5ca0ccd40ff052ae7b1e9731352fcdad946c2b812285a7505ca75/regex-2026.1.15-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf650f26087363434c4e560011f8e4e738f6f3e029b85d4904c50135b86cfa5", size = 912358, upload-time = "2026-01-14T23:15:24.569Z" }, - { url = "https://files.pythonhosted.org/packages/79/b6/e6a5665d43a7c42467138c8a2549be432bad22cbd206f5ec87162de74bd7/regex-2026.1.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18388a62989c72ac24de75f1449d0fb0b04dfccd0a1a7c1c43af5eb503d890f6", size = 803583, upload-time = "2026-01-14T23:15:26.526Z" }, - { url = "https://files.pythonhosted.org/packages/e7/53/7cd478222169d85d74d7437e74750005e993f52f335f7c04ff7adfda3310/regex-2026.1.15-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d220a2517f5893f55daac983bfa9fe998a7dbcaee4f5d27a88500f8b7873788", size = 775782, upload-time = "2026-01-14T23:15:29.352Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b5/75f9a9ee4b03a7c009fe60500fe550b45df94f0955ca29af16333ef557c5/regex-2026.1.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9c08c2fbc6120e70abff5d7f28ffb4d969e14294fb2143b4b5c7d20e46d1714", size = 787978, upload-time = "2026-01-14T23:15:31.295Z" }, - { url = "https://files.pythonhosted.org/packages/72/b3/79821c826245bbe9ccbb54f6eadb7879c722fd3e0248c17bfc90bf54e123/regex-2026.1.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7ef7d5d4bd49ec7364315167a4134a015f61e8266c6d446fc116a9ac4456e10d", size = 858550, upload-time = "2026-01-14T23:15:33.558Z" }, - { url = "https://files.pythonhosted.org/packages/4a/85/2ab5f77a1c465745bfbfcb3ad63178a58337ae8d5274315e2cc623a822fa/regex-2026.1.15-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e42844ad64194fa08d5ccb75fe6a459b9b08e6d7296bd704460168d58a388f3", size = 763747, upload-time = "2026-01-14T23:15:35.206Z" }, - { url = "https://files.pythonhosted.org/packages/6d/84/c27df502d4bfe2873a3e3a7cf1bdb2b9cc10284d1a44797cf38bed790470/regex-2026.1.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cfecdaa4b19f9ca534746eb3b55a5195d5c95b88cac32a205e981ec0a22b7d31", size = 850615, upload-time = "2026-01-14T23:15:37.523Z" }, - { url = "https://files.pythonhosted.org/packages/7d/b7/658a9782fb253680aa8ecb5ccbb51f69e088ed48142c46d9f0c99b46c575/regex-2026.1.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:08df9722d9b87834a3d701f3fca570b2be115654dbfd30179f30ab2f39d606d3", size = 789951, upload-time = "2026-01-14T23:15:39.582Z" }, - { url = "https://files.pythonhosted.org/packages/fc/2a/5928af114441e059f15b2f63e188bd00c6529b3051c974ade7444b85fcda/regex-2026.1.15-cp313-cp313-win32.whl", hash = "sha256:d426616dae0967ca225ab12c22274eb816558f2f99ccb4a1d52ca92e8baf180f", size = 266275, upload-time = "2026-01-14T23:15:42.108Z" }, - { url = "https://files.pythonhosted.org/packages/4f/16/5bfbb89e435897bff28cf0352a992ca719d9e55ebf8b629203c96b6ce4f7/regex-2026.1.15-cp313-cp313-win_amd64.whl", hash = "sha256:febd38857b09867d3ed3f4f1af7d241c5c50362e25ef43034995b77a50df494e", size = 277145, upload-time = "2026-01-14T23:15:44.244Z" }, - { url = "https://files.pythonhosted.org/packages/56/c1/a09ff7392ef4233296e821aec5f78c51be5e91ffde0d163059e50fd75835/regex-2026.1.15-cp313-cp313-win_arm64.whl", hash = "sha256:8e32f7896f83774f91499d239e24cebfadbc07639c1494bb7213983842348337", size = 270411, upload-time = "2026-01-14T23:15:45.858Z" }, - { url = "https://files.pythonhosted.org/packages/3c/38/0cfd5a78e5c6db00e6782fdae70458f89850ce95baa5e8694ab91d89744f/regex-2026.1.15-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ec94c04149b6a7b8120f9f44565722c7ae31b7a6d2275569d2eefa76b83da3be", size = 492068, upload-time = "2026-01-14T23:15:47.616Z" }, - { url = "https://files.pythonhosted.org/packages/50/72/6c86acff16cb7c959c4355826bbf06aad670682d07c8f3998d9ef4fee7cd/regex-2026.1.15-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40c86d8046915bb9aeb15d3f3f15b6fd500b8ea4485b30e1bbc799dab3fe29f8", size = 292756, upload-time = "2026-01-14T23:15:49.307Z" }, - { url = "https://files.pythonhosted.org/packages/4e/58/df7fb69eadfe76526ddfce28abdc0af09ffe65f20c2c90932e89d705153f/regex-2026.1.15-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:726ea4e727aba21643205edad8f2187ec682d3305d790f73b7a51c7587b64bdd", size = 291114, upload-time = "2026-01-14T23:15:51.484Z" }, - { url = "https://files.pythonhosted.org/packages/ed/6c/a4011cd1cf96b90d2cdc7e156f91efbd26531e822a7fbb82a43c1016678e/regex-2026.1.15-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cb740d044aff31898804e7bf1181cc72c03d11dfd19932b9911ffc19a79070a", size = 807524, upload-time = "2026-01-14T23:15:53.102Z" }, - { url = "https://files.pythonhosted.org/packages/1d/25/a53ffb73183f69c3e9f4355c4922b76d2840aee160af6af5fac229b6201d/regex-2026.1.15-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05d75a668e9ea16f832390d22131fe1e8acc8389a694c8febc3e340b0f810b93", size = 873455, upload-time = "2026-01-14T23:15:54.956Z" }, - { url = "https://files.pythonhosted.org/packages/66/0b/8b47fc2e8f97d9b4a851736f3890a5f786443aa8901061c55f24c955f45b/regex-2026.1.15-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d991483606f3dbec93287b9f35596f41aa2e92b7c2ebbb935b63f409e243c9af", size = 915007, upload-time = "2026-01-14T23:15:57.041Z" }, - { url = "https://files.pythonhosted.org/packages/c2/fa/97de0d681e6d26fabe71968dbee06dd52819e9a22fdce5dac7256c31ed84/regex-2026.1.15-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:194312a14819d3e44628a44ed6fea6898fdbecb0550089d84c403475138d0a09", size = 812794, upload-time = "2026-01-14T23:15:58.916Z" }, - { url = "https://files.pythonhosted.org/packages/22/38/e752f94e860d429654aa2b1c51880bff8dfe8f084268258adf9151cf1f53/regex-2026.1.15-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe2fda4110a3d0bc163c2e0664be44657431440722c5c5315c65155cab92f9e5", size = 781159, upload-time = "2026-01-14T23:16:00.817Z" }, - { url = "https://files.pythonhosted.org/packages/e9/a7/d739ffaef33c378fc888302a018d7f81080393d96c476b058b8c64fd2b0d/regex-2026.1.15-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:124dc36c85d34ef2d9164da41a53c1c8c122cfb1f6e1ec377a1f27ee81deb794", size = 795558, upload-time = "2026-01-14T23:16:03.267Z" }, - { url = "https://files.pythonhosted.org/packages/3e/c4/542876f9a0ac576100fc73e9c75b779f5c31e3527576cfc9cb3009dcc58a/regex-2026.1.15-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1774cd1981cd212506a23a14dba7fdeaee259f5deba2df6229966d9911e767a", size = 868427, upload-time = "2026-01-14T23:16:05.646Z" }, - { url = "https://files.pythonhosted.org/packages/fc/0f/d5655bea5b22069e32ae85a947aa564912f23758e112cdb74212848a1a1b/regex-2026.1.15-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:b5f7d8d2867152cdb625e72a530d2ccb48a3d199159144cbdd63870882fb6f80", size = 769939, upload-time = "2026-01-14T23:16:07.542Z" }, - { url = "https://files.pythonhosted.org/packages/20/06/7e18a4fa9d326daeda46d471a44ef94201c46eaa26dbbb780b5d92cbfdda/regex-2026.1.15-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:492534a0ab925d1db998defc3c302dae3616a2fc3fe2e08db1472348f096ddf2", size = 854753, upload-time = "2026-01-14T23:16:10.395Z" }, - { url = "https://files.pythonhosted.org/packages/3b/67/dc8946ef3965e166f558ef3b47f492bc364e96a265eb4a2bb3ca765c8e46/regex-2026.1.15-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c661fc820cfb33e166bf2450d3dadbda47c8d8981898adb9b6fe24e5e582ba60", size = 799559, upload-time = "2026-01-14T23:16:12.347Z" }, - { url = "https://files.pythonhosted.org/packages/a5/61/1bba81ff6d50c86c65d9fd84ce9699dd106438ee4cdb105bf60374ee8412/regex-2026.1.15-cp313-cp313t-win32.whl", hash = "sha256:99ad739c3686085e614bf77a508e26954ff1b8f14da0e3765ff7abbf7799f952", size = 268879, upload-time = "2026-01-14T23:16:14.049Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5e/cef7d4c5fb0ea3ac5c775fd37db5747f7378b29526cc83f572198924ff47/regex-2026.1.15-cp313-cp313t-win_amd64.whl", hash = "sha256:32655d17905e7ff8ba5c764c43cb124e34a9245e45b83c22e81041e1071aee10", size = 280317, upload-time = "2026-01-14T23:16:15.718Z" }, - { url = "https://files.pythonhosted.org/packages/b4/52/4317f7a5988544e34ab57b4bde0f04944c4786128c933fb09825924d3e82/regex-2026.1.15-cp313-cp313t-win_arm64.whl", hash = "sha256:b2a13dd6a95e95a489ca242319d18fc02e07ceb28fa9ad146385194d95b3c829", size = 271551, upload-time = "2026-01-14T23:16:17.533Z" }, - { url = "https://files.pythonhosted.org/packages/52/0a/47fa888ec7cbbc7d62c5f2a6a888878e76169170ead271a35239edd8f0e8/regex-2026.1.15-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:d920392a6b1f353f4aa54328c867fec3320fa50657e25f64abf17af054fc97ac", size = 489170, upload-time = "2026-01-14T23:16:19.835Z" }, - { url = "https://files.pythonhosted.org/packages/ac/c4/d000e9b7296c15737c9301708e9e7fbdea009f8e93541b6b43bdb8219646/regex-2026.1.15-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b5a28980a926fa810dbbed059547b02783952e2efd9c636412345232ddb87ff6", size = 291146, upload-time = "2026-01-14T23:16:21.541Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b6/921cc61982e538682bdf3bdf5b2c6ab6b34368da1f8e98a6c1ddc503c9cf/regex-2026.1.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:621f73a07595d83f28952d7bd1e91e9d1ed7625fb7af0064d3516674ec93a2a2", size = 288986, upload-time = "2026-01-14T23:16:23.381Z" }, - { url = "https://files.pythonhosted.org/packages/ca/33/eb7383dde0bbc93f4fb9d03453aab97e18ad4024ac7e26cef8d1f0a2cff0/regex-2026.1.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d7d92495f47567a9b1669c51fc8d6d809821849063d168121ef801bbc213846", size = 799098, upload-time = "2026-01-14T23:16:25.088Z" }, - { url = "https://files.pythonhosted.org/packages/27/56/b664dccae898fc8d8b4c23accd853f723bde0f026c747b6f6262b688029c/regex-2026.1.15-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8dd16fba2758db7a3780a051f245539c4451ca20910f5a5e6ea1c08d06d4a76b", size = 864980, upload-time = "2026-01-14T23:16:27.297Z" }, - { url = "https://files.pythonhosted.org/packages/16/40/0999e064a170eddd237bae9ccfcd8f28b3aa98a38bf727a086425542a4fc/regex-2026.1.15-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1e1808471fbe44c1a63e5f577a1d5f02fe5d66031dcbdf12f093ffc1305a858e", size = 911607, upload-time = "2026-01-14T23:16:29.235Z" }, - { url = "https://files.pythonhosted.org/packages/07/78/c77f644b68ab054e5a674fb4da40ff7bffb2c88df58afa82dbf86573092d/regex-2026.1.15-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0751a26ad39d4f2ade8fe16c59b2bf5cb19eb3d2cd543e709e583d559bd9efde", size = 803358, upload-time = "2026-01-14T23:16:31.369Z" }, - { url = "https://files.pythonhosted.org/packages/27/31/d4292ea8566eaa551fafc07797961c5963cf5235c797cc2ae19b85dfd04d/regex-2026.1.15-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0f0c7684c7f9ca241344ff95a1de964f257a5251968484270e91c25a755532c5", size = 775833, upload-time = "2026-01-14T23:16:33.141Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b2/cff3bf2fea4133aa6fb0d1e370b37544d18c8350a2fa118c7e11d1db0e14/regex-2026.1.15-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:74f45d170a21df41508cb67165456538425185baaf686281fa210d7e729abc34", size = 788045, upload-time = "2026-01-14T23:16:35.005Z" }, - { url = "https://files.pythonhosted.org/packages/8d/99/2cb9b69045372ec877b6f5124bda4eb4253bc58b8fe5848c973f752bc52c/regex-2026.1.15-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f1862739a1ffb50615c0fde6bae6569b5efbe08d98e59ce009f68a336f64da75", size = 859374, upload-time = "2026-01-14T23:16:36.919Z" }, - { url = "https://files.pythonhosted.org/packages/09/16/710b0a5abe8e077b1729a562d2f297224ad079f3a66dce46844c193416c8/regex-2026.1.15-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:453078802f1b9e2b7303fb79222c054cb18e76f7bdc220f7530fdc85d319f99e", size = 763940, upload-time = "2026-01-14T23:16:38.685Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/7585c8e744e40eb3d32f119191969b91de04c073fca98ec14299041f6e7e/regex-2026.1.15-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:a30a68e89e5a218b8b23a52292924c1f4b245cb0c68d1cce9aec9bbda6e2c160", size = 850112, upload-time = "2026-01-14T23:16:40.646Z" }, - { url = "https://files.pythonhosted.org/packages/af/d6/43e1dd85df86c49a347aa57c1f69d12c652c7b60e37ec162e3096194a278/regex-2026.1.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9479cae874c81bf610d72b85bb681a94c95722c127b55445285fb0e2c82db8e1", size = 789586, upload-time = "2026-01-14T23:16:42.799Z" }, - { url = "https://files.pythonhosted.org/packages/93/38/77142422f631e013f316aaae83234c629555729a9fbc952b8a63ac91462a/regex-2026.1.15-cp314-cp314-win32.whl", hash = "sha256:d639a750223132afbfb8f429c60d9d318aeba03281a5f1ab49f877456448dcf1", size = 271691, upload-time = "2026-01-14T23:16:44.671Z" }, - { url = "https://files.pythonhosted.org/packages/4a/a9/ab16b4649524ca9e05213c1cdbb7faa85cc2aa90a0230d2f796cbaf22736/regex-2026.1.15-cp314-cp314-win_amd64.whl", hash = "sha256:4161d87f85fa831e31469bfd82c186923070fc970b9de75339b68f0c75b51903", size = 280422, upload-time = "2026-01-14T23:16:46.607Z" }, - { url = "https://files.pythonhosted.org/packages/be/2a/20fd057bf3521cb4791f69f869635f73e0aaf2b9ad2d260f728144f9047c/regex-2026.1.15-cp314-cp314-win_arm64.whl", hash = "sha256:91c5036ebb62663a6b3999bdd2e559fd8456d17e2b485bf509784cd31a8b1705", size = 273467, upload-time = "2026-01-14T23:16:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/ad/77/0b1e81857060b92b9cad239104c46507dd481b3ff1fa79f8e7f865aae38a/regex-2026.1.15-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ee6854c9000a10938c79238de2379bea30c82e4925a371711af45387df35cab8", size = 492073, upload-time = "2026-01-14T23:16:51.154Z" }, - { url = "https://files.pythonhosted.org/packages/70/f3/f8302b0c208b22c1e4f423147e1913fd475ddd6230565b299925353de644/regex-2026.1.15-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c2b80399a422348ce5de4fe40c418d6299a0fa2803dd61dc0b1a2f28e280fcf", size = 292757, upload-time = "2026-01-14T23:16:53.08Z" }, - { url = "https://files.pythonhosted.org/packages/bf/f0/ef55de2460f3b4a6da9d9e7daacd0cb79d4ef75c64a2af316e68447f0df0/regex-2026.1.15-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:dca3582bca82596609959ac39e12b7dad98385b4fefccb1151b937383cec547d", size = 291122, upload-time = "2026-01-14T23:16:55.383Z" }, - { url = "https://files.pythonhosted.org/packages/cf/55/bb8ccbacabbc3a11d863ee62a9f18b160a83084ea95cdfc5d207bfc3dd75/regex-2026.1.15-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef71d476caa6692eea743ae5ea23cde3260677f70122c4d258ca952e5c2d4e84", size = 807761, upload-time = "2026-01-14T23:16:57.251Z" }, - { url = "https://files.pythonhosted.org/packages/8f/84/f75d937f17f81e55679a0509e86176e29caa7298c38bd1db7ce9c0bf6075/regex-2026.1.15-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c243da3436354f4af6c3058a3f81a97d47ea52c9bd874b52fd30274853a1d5df", size = 873538, upload-time = "2026-01-14T23:16:59.349Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d9/0da86327df70349aa8d86390da91171bd3ca4f0e7c1d1d453a9c10344da3/regex-2026.1.15-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8355ad842a7c7e9e5e55653eade3b7d1885ba86f124dd8ab1f722f9be6627434", size = 915066, upload-time = "2026-01-14T23:17:01.607Z" }, - { url = "https://files.pythonhosted.org/packages/2a/5e/f660fb23fc77baa2a61aa1f1fe3a4eea2bbb8a286ddec148030672e18834/regex-2026.1.15-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f192a831d9575271a22d804ff1a5355355723f94f31d9eef25f0d45a152fdc1a", size = 812938, upload-time = "2026-01-14T23:17:04.366Z" }, - { url = "https://files.pythonhosted.org/packages/69/33/a47a29bfecebbbfd1e5cd3f26b28020a97e4820f1c5148e66e3b7d4b4992/regex-2026.1.15-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:166551807ec20d47ceaeec380081f843e88c8949780cd42c40f18d16168bed10", size = 781314, upload-time = "2026-01-14T23:17:06.378Z" }, - { url = "https://files.pythonhosted.org/packages/65/ec/7ec2bbfd4c3f4e494a24dec4c6943a668e2030426b1b8b949a6462d2c17b/regex-2026.1.15-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f9ca1cbdc0fbfe5e6e6f8221ef2309988db5bcede52443aeaee9a4ad555e0dac", size = 795652, upload-time = "2026-01-14T23:17:08.521Z" }, - { url = "https://files.pythonhosted.org/packages/46/79/a5d8651ae131fe27d7c521ad300aa7f1c7be1dbeee4d446498af5411b8a9/regex-2026.1.15-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b30bcbd1e1221783c721483953d9e4f3ab9c5d165aa709693d3f3946747b1aea", size = 868550, upload-time = "2026-01-14T23:17:10.573Z" }, - { url = "https://files.pythonhosted.org/packages/06/b7/25635d2809664b79f183070786a5552dd4e627e5aedb0065f4e3cf8ee37d/regex-2026.1.15-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2a8d7b50c34578d0d3bf7ad58cde9652b7d683691876f83aedc002862a35dc5e", size = 769981, upload-time = "2026-01-14T23:17:12.871Z" }, - { url = "https://files.pythonhosted.org/packages/16/8b/fc3fcbb2393dcfa4a6c5ffad92dc498e842df4581ea9d14309fcd3c55fb9/regex-2026.1.15-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9d787e3310c6a6425eb346be4ff2ccf6eece63017916fd77fe8328c57be83521", size = 854780, upload-time = "2026-01-14T23:17:14.837Z" }, - { url = "https://files.pythonhosted.org/packages/d0/38/dde117c76c624713c8a2842530be9c93ca8b606c0f6102d86e8cd1ce8bea/regex-2026.1.15-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:619843841e220adca114118533a574a9cd183ed8a28b85627d2844c500a2b0db", size = 799778, upload-time = "2026-01-14T23:17:17.369Z" }, - { url = "https://files.pythonhosted.org/packages/e3/0d/3a6cfa9ae99606afb612d8fb7a66b245a9d5ff0f29bb347c8a30b6ad561b/regex-2026.1.15-cp314-cp314t-win32.whl", hash = "sha256:e90b8db97f6f2c97eb045b51a6b2c5ed69cedd8392459e0642d4199b94fabd7e", size = 274667, upload-time = "2026-01-14T23:17:19.301Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b2/297293bb0742fd06b8d8e2572db41a855cdf1cae0bf009b1cb74fe07e196/regex-2026.1.15-cp314-cp314t-win_amd64.whl", hash = "sha256:5ef19071f4ac9f0834793af85bd04a920b4407715624e40cb7a0631a11137cdf", size = 284386, upload-time = "2026-01-14T23:17:21.231Z" }, - { url = "https://files.pythonhosted.org/packages/95/e4/a3b9480c78cf8ee86626cb06f8d931d74d775897d44201ccb813097ae697/regex-2026.1.15-cp314-cp314t-win_arm64.whl", hash = "sha256:ca89c5e596fc05b015f27561b3793dc2fa0917ea0d7507eebb448efd35274a70", size = 274837, upload-time = "2026-01-14T23:17:23.146Z" }, -] - -[[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 = "requests-oauthlib" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "oauthlib" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, -] - -[[package]] -name = "requests-toolbelt" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } -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 = "rfc3986" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", size = 49026, upload-time = "2022-01-10T00:52:30.832Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", size = 31326, upload-time = "2022-01-10T00:52:29.594Z" }, -] - -[[package]] -name = "rich" -version = "14.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" }, -] - -[[package]] -name = "rich-rst" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, -] - -[[package]] -name = "roman-numerals" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/f9/41dc953bbeb056c17d5f7a519f50fdf010bd0553be2d630bc69d1e022703/roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2", size = 9077, upload-time = "2025-12-17T18:25:34.381Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/54/6f679c435d28e0a568d8e8a7c0a93a09010818634c3c3907fc98d8983770/roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7", size = 7676, upload-time = "2025-12-17T18:25:33.098Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, -] - -[[package]] -name = "rsa" -version = "4.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyasn1" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, -] - -[[package]] -name = "rubicon-objc" -version = "0.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/d2/d39ecd205661a5c14c90dbd92a722a203848a3621785c9783716341de427/rubicon_objc-0.5.3.tar.gz", hash = "sha256:74c25920c5951a05db9d3a1aac31d23816ec7dacc841a5b124d911b99ea71b9a", size = 171512, upload-time = "2025-12-03T03:51:10.264Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/ab/e834c01138c272fb2e37d2f3c7cba708bc694dbc7b3f03b743f29ceb92d5/rubicon_objc-0.5.3-py3-none-any.whl", hash = "sha256:31dedcda9be38435f5ec067906e1eea5d0ddb790330e98a22e94ff424758b415", size = 64414, upload-time = "2025-12-03T03:51:09.082Z" }, -] - -[[package]] -name = "ruff" -version = "0.14.14" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/06/f71e3a86b2df0dfa2d2f72195941cd09b44f87711cb7fa5193732cb9a5fc/ruff-0.14.14.tar.gz", hash = "sha256:2d0f819c9a90205f3a867dbbd0be083bee9912e170fd7d9704cc8ae45824896b", size = 4515732, upload-time = "2026-01-22T22:30:17.527Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/89/20a12e97bc6b9f9f68343952da08a8099c57237aef953a56b82711d55edd/ruff-0.14.14-py3-none-linux_armv6l.whl", hash = "sha256:7cfe36b56e8489dee8fbc777c61959f60ec0f1f11817e8f2415f429552846aed", size = 10467650, upload-time = "2026-01-22T22:30:08.578Z" }, - { url = "https://files.pythonhosted.org/packages/a3/b1/c5de3fd2d5a831fcae21beda5e3589c0ba67eec8202e992388e4b17a6040/ruff-0.14.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6006a0082336e7920b9573ef8a7f52eec837add1265cc74e04ea8a4368cd704c", size = 10883245, upload-time = "2026-01-22T22:30:04.155Z" }, - { url = "https://files.pythonhosted.org/packages/b8/7c/3c1db59a10e7490f8f6f8559d1db8636cbb13dccebf18686f4e3c9d7c772/ruff-0.14.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:026c1d25996818f0bf498636686199d9bd0d9d6341c9c2c3b62e2a0198b758de", size = 10231273, upload-time = "2026-01-22T22:30:34.642Z" }, - { url = "https://files.pythonhosted.org/packages/a1/6e/5e0e0d9674be0f8581d1f5e0f0a04761203affce3232c1a1189d0e3b4dad/ruff-0.14.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f666445819d31210b71e0a6d1c01e24447a20b85458eea25a25fe8142210ae0e", size = 10585753, upload-time = "2026-01-22T22:30:31.781Z" }, - { url = "https://files.pythonhosted.org/packages/23/09/754ab09f46ff1884d422dc26d59ba18b4e5d355be147721bb2518aa2a014/ruff-0.14.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c0f18b922c6d2ff9a5e6c3ee16259adc513ca775bcf82c67ebab7cbd9da5bc8", size = 10286052, upload-time = "2026-01-22T22:30:24.827Z" }, - { url = "https://files.pythonhosted.org/packages/c8/cc/e71f88dd2a12afb5f50733851729d6b571a7c3a35bfdb16c3035132675a0/ruff-0.14.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1629e67489c2dea43e8658c3dba659edbfd87361624b4040d1df04c9740ae906", size = 11043637, upload-time = "2026-01-22T22:30:13.239Z" }, - { url = "https://files.pythonhosted.org/packages/67/b2/397245026352494497dac935d7f00f1468c03a23a0c5db6ad8fc49ca3fb2/ruff-0.14.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:27493a2131ea0f899057d49d303e4292b2cae2bb57253c1ed1f256fbcd1da480", size = 12194761, upload-time = "2026-01-22T22:30:22.542Z" }, - { url = "https://files.pythonhosted.org/packages/5b/06/06ef271459f778323112c51b7587ce85230785cd64e91772034ddb88f200/ruff-0.14.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ff589aab3f5b539e35db38425da31a57521efd1e4ad1ae08fc34dbe30bd7df", size = 12005701, upload-time = "2026-01-22T22:30:20.499Z" }, - { url = "https://files.pythonhosted.org/packages/41/d6/99364514541cf811ccc5ac44362f88df66373e9fec1b9d1c4cc830593fe7/ruff-0.14.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc12d74eef0f29f51775f5b755913eb523546b88e2d733e1d701fe65144e89b", size = 11282455, upload-time = "2026-01-22T22:29:59.679Z" }, - { url = "https://files.pythonhosted.org/packages/ca/71/37daa46f89475f8582b7762ecd2722492df26421714a33e72ccc9a84d7a5/ruff-0.14.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb8481604b7a9e75eff53772496201690ce2687067e038b3cc31aaf16aa0b974", size = 11215882, upload-time = "2026-01-22T22:29:57.032Z" }, - { url = "https://files.pythonhosted.org/packages/2c/10/a31f86169ec91c0705e618443ee74ede0bdd94da0a57b28e72db68b2dbac/ruff-0.14.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:14649acb1cf7b5d2d283ebd2f58d56b75836ed8c6f329664fa91cdea19e76e66", size = 11180549, upload-time = "2026-01-22T22:30:27.175Z" }, - { url = "https://files.pythonhosted.org/packages/fd/1e/c723f20536b5163adf79bdd10c5f093414293cdf567eed9bdb7b83940f3f/ruff-0.14.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8058d2145566510790eab4e2fad186002e288dec5e0d343a92fe7b0bc1b3e13", size = 10543416, upload-time = "2026-01-22T22:30:01.964Z" }, - { url = "https://files.pythonhosted.org/packages/3e/34/8a84cea7e42c2d94ba5bde1d7a4fae164d6318f13f933d92da6d7c2041ff/ruff-0.14.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e651e977a79e4c758eb807f0481d673a67ffe53cfa92209781dfa3a996cf8412", size = 10285491, upload-time = "2026-01-22T22:30:29.51Z" }, - { url = "https://files.pythonhosted.org/packages/55/ef/b7c5ea0be82518906c978e365e56a77f8de7678c8bb6651ccfbdc178c29f/ruff-0.14.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cc8b22da8d9d6fdd844a68ae937e2a0adf9b16514e9a97cc60355e2d4b219fc3", size = 10733525, upload-time = "2026-01-22T22:30:06.499Z" }, - { url = "https://files.pythonhosted.org/packages/6a/5b/aaf1dfbcc53a2811f6cc0a1759de24e4b03e02ba8762daabd9b6bd8c59e3/ruff-0.14.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:16bc890fb4cc9781bb05beb5ab4cd51be9e7cb376bf1dd3580512b24eb3fda2b", size = 11315626, upload-time = "2026-01-22T22:30:36.848Z" }, - { url = "https://files.pythonhosted.org/packages/2c/aa/9f89c719c467dfaf8ad799b9bae0df494513fb21d31a6059cb5870e57e74/ruff-0.14.14-py3-none-win32.whl", hash = "sha256:b530c191970b143375b6a68e6f743800b2b786bbcf03a7965b06c4bf04568167", size = 10502442, upload-time = "2026-01-22T22:30:38.93Z" }, - { url = "https://files.pythonhosted.org/packages/87/44/90fa543014c45560cae1fffc63ea059fb3575ee6e1cb654562197e5d16fb/ruff-0.14.14-py3-none-win_amd64.whl", hash = "sha256:3dde1435e6b6fe5b66506c1dff67a421d0b7f6488d466f651c07f4cab3bf20fd", size = 11630486, upload-time = "2026-01-22T22:30:10.852Z" }, - { url = "https://files.pythonhosted.org/packages/9e/6a/40fee331a52339926a92e17ae748827270b288a35ef4a15c9c8f2ec54715/ruff-0.14.14-py3-none-win_arm64.whl", hash = "sha256:56e6981a98b13a32236a72a8da421d7839221fa308b223b9283312312e5ac76c", size = 10920448, upload-time = "2026-01-22T22:30:15.417Z" }, -] - -[[package]] -name = "safetensors" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, - { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, - { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, - { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, - { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, - { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, - { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, - { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, - { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, - { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, - { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, -] - -[[package]] -name = "scikit-learn" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "joblib" }, - { name = "numpy" }, - { name = "scipy" }, - { name = "threadpoolctl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/74/e6a7cc4b820e95cc38cf36cd74d5aa2b42e8ffc2d21fe5a9a9c45c1c7630/scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e", size = 8548242, upload-time = "2025-12-10T07:07:51.568Z" }, - { url = "https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76", size = 8079075, upload-time = "2025-12-10T07:07:53.697Z" }, - { url = "https://files.pythonhosted.org/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492, upload-time = "2025-12-10T07:07:55.574Z" }, - { url = "https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904, upload-time = "2025-12-10T07:07:57.666Z" }, - { url = "https://files.pythonhosted.org/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809", size = 8019359, upload-time = "2025-12-10T07:07:59.838Z" }, - { url = "https://files.pythonhosted.org/packages/24/90/344a67811cfd561d7335c1b96ca21455e7e472d281c3c279c4d3f2300236/scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb", size = 7641898, upload-time = "2025-12-10T07:08:01.36Z" }, - { url = "https://files.pythonhosted.org/packages/03/aa/e22e0768512ce9255eba34775be2e85c2048da73da1193e841707f8f039c/scikit_learn-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a", size = 8513770, upload-time = "2025-12-10T07:08:03.251Z" }, - { url = "https://files.pythonhosted.org/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e", size = 8044458, upload-time = "2025-12-10T07:08:05.336Z" }, - { url = "https://files.pythonhosted.org/packages/2d/5a/3f1caed8765f33eabb723596666da4ebbf43d11e96550fb18bdec42b467b/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57", size = 8610341, upload-time = "2025-12-10T07:08:07.732Z" }, - { url = "https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e", size = 8900022, upload-time = "2025-12-10T07:08:09.862Z" }, - { url = "https://files.pythonhosted.org/packages/1c/f9/9b7563caf3ec8873e17a31401858efab6b39a882daf6c1bfa88879c0aa11/scikit_learn-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271", size = 7989409, upload-time = "2025-12-10T07:08:12.028Z" }, - { url = "https://files.pythonhosted.org/packages/49/bd/1f4001503650e72c4f6009ac0c4413cb17d2d601cef6f71c0453da2732fc/scikit_learn-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3", size = 7619760, upload-time = "2025-12-10T07:08:13.688Z" }, - { url = "https://files.pythonhosted.org/packages/d2/7d/a630359fc9dcc95496588c8d8e3245cc8fd81980251079bc09c70d41d951/scikit_learn-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735", size = 8826045, upload-time = "2025-12-10T07:08:15.215Z" }, - { url = "https://files.pythonhosted.org/packages/cc/56/a0c86f6930cfcd1c7054a2bc417e26960bb88d32444fe7f71d5c2cfae891/scikit_learn-1.8.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd", size = 8420324, upload-time = "2025-12-10T07:08:17.561Z" }, - { url = "https://files.pythonhosted.org/packages/46/1e/05962ea1cebc1cf3876667ecb14c283ef755bf409993c5946ade3b77e303/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e", size = 8680651, upload-time = "2025-12-10T07:08:19.952Z" }, - { url = "https://files.pythonhosted.org/packages/fe/56/a85473cd75f200c9759e3a5f0bcab2d116c92a8a02ee08ccd73b870f8bb4/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb", size = 8925045, upload-time = "2025-12-10T07:08:22.11Z" }, - { url = "https://files.pythonhosted.org/packages/cc/b7/64d8cfa896c64435ae57f4917a548d7ac7a44762ff9802f75a79b77cb633/scikit_learn-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702", size = 8507994, upload-time = "2025-12-10T07:08:23.943Z" }, - { url = "https://files.pythonhosted.org/packages/5e/37/e192ea709551799379958b4c4771ec507347027bb7c942662c7fbeba31cb/scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde", size = 7869518, upload-time = "2025-12-10T07:08:25.71Z" }, - { url = "https://files.pythonhosted.org/packages/24/05/1af2c186174cc92dcab2233f327336058c077d38f6fe2aceb08e6ab4d509/scikit_learn-1.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3", size = 8528667, upload-time = "2025-12-10T07:08:27.541Z" }, - { url = "https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7", size = 8066524, upload-time = "2025-12-10T07:08:29.822Z" }, - { url = "https://files.pythonhosted.org/packages/be/ce/a0623350aa0b68647333940ee46fe45086c6060ec604874e38e9ab7d8e6c/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6", size = 8657133, upload-time = "2025-12-10T07:08:31.865Z" }, - { url = "https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4", size = 8923223, upload-time = "2025-12-10T07:08:34.166Z" }, - { url = "https://files.pythonhosted.org/packages/76/18/a8def8f91b18cd1ba6e05dbe02540168cb24d47e8dcf69e8d00b7da42a08/scikit_learn-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6", size = 8096518, upload-time = "2025-12-10T07:08:36.339Z" }, - { url = "https://files.pythonhosted.org/packages/d1/77/482076a678458307f0deb44e29891d6022617b2a64c840c725495bee343f/scikit_learn-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242", size = 7754546, upload-time = "2025-12-10T07:08:38.128Z" }, - { url = "https://files.pythonhosted.org/packages/2d/d1/ef294ca754826daa043b2a104e59960abfab4cf653891037d19dd5b6f3cf/scikit_learn-1.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7", size = 8848305, upload-time = "2025-12-10T07:08:41.013Z" }, - { url = "https://files.pythonhosted.org/packages/5b/e2/b1f8b05138ee813b8e1a4149f2f0d289547e60851fd1bb268886915adbda/scikit_learn-1.8.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9", size = 8432257, upload-time = "2025-12-10T07:08:42.873Z" }, - { url = "https://files.pythonhosted.org/packages/26/11/c32b2138a85dcb0c99f6afd13a70a951bfdff8a6ab42d8160522542fb647/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f", size = 8678673, upload-time = "2025-12-10T07:08:45.362Z" }, - { url = "https://files.pythonhosted.org/packages/c7/57/51f2384575bdec454f4fe4e7a919d696c9ebce914590abf3e52d47607ab8/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9", size = 8922467, upload-time = "2025-12-10T07:08:47.408Z" }, - { url = "https://files.pythonhosted.org/packages/35/4d/748c9e2872637a57981a04adc038dacaa16ba8ca887b23e34953f0b3f742/scikit_learn-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2", size = 8774395, upload-time = "2025-12-10T07:08:49.337Z" }, - { url = "https://files.pythonhosted.org/packages/60/22/d7b2ebe4704a5e50790ba089d5c2ae308ab6bb852719e6c3bd4f04c3a363/scikit_learn-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c", size = 8002647, upload-time = "2025-12-10T07:08:51.601Z" }, -] - -[[package]] -name = "scipy" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/56/3e/9cca699f3486ce6bc12ff46dc2031f1ec8eb9ccc9a320fdaf925f1417426/scipy-1.17.0.tar.gz", hash = "sha256:2591060c8e648d8b96439e111ac41fd8342fdeff1876be2e19dea3fe8930454e", size = 30396830, upload-time = "2026-01-10T21:34:23.009Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/11/7241a63e73ba5a516f1930ac8d5b44cbbfabd35ac73a2d08ca206df007c4/scipy-1.17.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:0d5018a57c24cb1dd828bcf51d7b10e65986d549f52ef5adb6b4d1ded3e32a57", size = 31364580, upload-time = "2026-01-10T21:25:25.717Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1d/5057f812d4f6adc91a20a2d6f2ebcdb517fdbc87ae3acc5633c9b97c8ba5/scipy-1.17.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:88c22af9e5d5a4f9e027e26772cc7b5922fab8bcc839edb3ae33de404feebd9e", size = 27969012, upload-time = "2026-01-10T21:25:30.921Z" }, - { url = "https://files.pythonhosted.org/packages/e3/21/f6ec556c1e3b6ec4e088da667d9987bb77cc3ab3026511f427dc8451187d/scipy-1.17.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f3cd947f20fe17013d401b64e857c6b2da83cae567adbb75b9dcba865abc66d8", size = 20140691, upload-time = "2026-01-10T21:25:34.802Z" }, - { url = "https://files.pythonhosted.org/packages/7a/fe/5e5ad04784964ba964a96f16c8d4676aa1b51357199014dce58ab7ec5670/scipy-1.17.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e8c0b331c2c1f531eb51f1b4fc9ba709521a712cce58f1aa627bc007421a5306", size = 22463015, upload-time = "2026-01-10T21:25:39.277Z" }, - { url = "https://files.pythonhosted.org/packages/4a/69/7c347e857224fcaf32a34a05183b9d8a7aca25f8f2d10b8a698b8388561a/scipy-1.17.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5194c445d0a1c7a6c1a4a4681b6b7c71baad98ff66d96b949097e7513c9d6742", size = 32724197, upload-time = "2026-01-10T21:25:44.084Z" }, - { url = "https://files.pythonhosted.org/packages/d1/fe/66d73b76d378ba8cc2fe605920c0c75092e3a65ae746e1e767d9d020a75a/scipy-1.17.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9eeb9b5f5997f75507814ed9d298ab23f62cf79f5a3ef90031b1ee2506abdb5b", size = 35009148, upload-time = "2026-01-10T21:25:50.591Z" }, - { url = "https://files.pythonhosted.org/packages/af/07/07dec27d9dc41c18d8c43c69e9e413431d20c53a0339c388bcf72f353c4b/scipy-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:40052543f7bbe921df4408f46003d6f01c6af109b9e2c8a66dd1cf6cf57f7d5d", size = 34798766, upload-time = "2026-01-10T21:25:59.41Z" }, - { url = "https://files.pythonhosted.org/packages/81/61/0470810c8a093cdacd4ba7504b8a218fd49ca070d79eca23a615f5d9a0b0/scipy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0cf46c8013fec9d3694dc572f0b54100c28405d55d3e2cb15e2895b25057996e", size = 37405953, upload-time = "2026-01-10T21:26:07.75Z" }, - { url = "https://files.pythonhosted.org/packages/92/ce/672ed546f96d5d41ae78c4b9b02006cedd0b3d6f2bf5bb76ea455c320c28/scipy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:0937a0b0d8d593a198cededd4c439a0ea216a3f36653901ea1f3e4be949056f8", size = 36328121, upload-time = "2026-01-10T21:26:16.509Z" }, - { url = "https://files.pythonhosted.org/packages/9d/21/38165845392cae67b61843a52c6455d47d0cc2a40dd495c89f4362944654/scipy-1.17.0-cp312-cp312-win_arm64.whl", hash = "sha256:f603d8a5518c7426414d1d8f82e253e454471de682ce5e39c29adb0df1efb86b", size = 24314368, upload-time = "2026-01-10T21:26:23.087Z" }, - { url = "https://files.pythonhosted.org/packages/0c/51/3468fdfd49387ddefee1636f5cf6d03ce603b75205bf439bbf0e62069bfd/scipy-1.17.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:65ec32f3d32dfc48c72df4291345dae4f048749bc8d5203ee0a3f347f96c5ce6", size = 31344101, upload-time = "2026-01-10T21:26:30.25Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9a/9406aec58268d437636069419e6977af953d1e246df941d42d3720b7277b/scipy-1.17.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:1f9586a58039d7229ce77b52f8472c972448cded5736eaf102d5658bbac4c269", size = 27950385, upload-time = "2026-01-10T21:26:36.801Z" }, - { url = "https://files.pythonhosted.org/packages/4f/98/e7342709e17afdfd1b26b56ae499ef4939b45a23a00e471dfb5375eea205/scipy-1.17.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9fad7d3578c877d606b1150135c2639e9de9cecd3705caa37b66862977cc3e72", size = 20122115, upload-time = "2026-01-10T21:26:42.107Z" }, - { url = "https://files.pythonhosted.org/packages/fd/0e/9eeeb5357a64fd157cbe0302c213517c541cc16b8486d82de251f3c68ede/scipy-1.17.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:423ca1f6584fc03936972b5f7c06961670dbba9f234e71676a7c7ccf938a0d61", size = 22442402, upload-time = "2026-01-10T21:26:48.029Z" }, - { url = "https://files.pythonhosted.org/packages/c9/10/be13397a0e434f98e0c79552b2b584ae5bb1c8b2be95db421533bbca5369/scipy-1.17.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe508b5690e9eaaa9467fc047f833af58f1152ae51a0d0aed67aa5801f4dd7d6", size = 32696338, upload-time = "2026-01-10T21:26:55.521Z" }, - { url = "https://files.pythonhosted.org/packages/63/1e/12fbf2a3bb240161651c94bb5cdd0eae5d4e8cc6eaeceb74ab07b12a753d/scipy-1.17.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6680f2dfd4f6182e7d6db161344537da644d1cf85cf293f015c60a17ecf08752", size = 34977201, upload-time = "2026-01-10T21:27:03.501Z" }, - { url = "https://files.pythonhosted.org/packages/19/5b/1a63923e23ccd20bd32156d7dd708af5bbde410daa993aa2500c847ab2d2/scipy-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eec3842ec9ac9de5917899b277428886042a93db0b227ebbe3a333b64ec7643d", size = 34777384, upload-time = "2026-01-10T21:27:11.423Z" }, - { url = "https://files.pythonhosted.org/packages/39/22/b5da95d74edcf81e540e467202a988c50fef41bd2011f46e05f72ba07df6/scipy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d7425fcafbc09a03731e1bc05581f5fad988e48c6a861f441b7ab729a49a55ea", size = 37379586, upload-time = "2026-01-10T21:27:20.171Z" }, - { url = "https://files.pythonhosted.org/packages/b9/b6/8ac583d6da79e7b9e520579f03007cb006f063642afd6b2eeb16b890bf93/scipy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:87b411e42b425b84777718cc41516b8a7e0795abfa8e8e1d573bf0ef014f0812", size = 36287211, upload-time = "2026-01-10T21:28:43.122Z" }, - { url = "https://files.pythonhosted.org/packages/55/fb/7db19e0b3e52f882b420417644ec81dd57eeef1bd1705b6f689d8ff93541/scipy-1.17.0-cp313-cp313-win_arm64.whl", hash = "sha256:357ca001c6e37601066092e7c89cca2f1ce74e2a520ca78d063a6d2201101df2", size = 24312646, upload-time = "2026-01-10T21:28:49.893Z" }, - { url = "https://files.pythonhosted.org/packages/20/b6/7feaa252c21cc7aff335c6c55e1b90ab3e3306da3f048109b8b639b94648/scipy-1.17.0-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:ec0827aa4d36cb79ff1b81de898e948a51ac0b9b1c43e4a372c0508c38c0f9a3", size = 31693194, upload-time = "2026-01-10T21:27:27.454Z" }, - { url = "https://files.pythonhosted.org/packages/76/bb/bbb392005abce039fb7e672cb78ac7d158700e826b0515cab6b5b60c26fb/scipy-1.17.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:819fc26862b4b3c73a60d486dbb919202f3d6d98c87cf20c223511429f2d1a97", size = 28365415, upload-time = "2026-01-10T21:27:34.26Z" }, - { url = "https://files.pythonhosted.org/packages/37/da/9d33196ecc99fba16a409c691ed464a3a283ac454a34a13a3a57c0d66f3a/scipy-1.17.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:363ad4ae2853d88ebcde3ae6ec46ccca903ea9835ee8ba543f12f575e7b07e4e", size = 20537232, upload-time = "2026-01-10T21:27:40.306Z" }, - { url = "https://files.pythonhosted.org/packages/56/9d/f4b184f6ddb28e9a5caea36a6f98e8ecd2a524f9127354087ce780885d83/scipy-1.17.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:979c3a0ff8e5ba254d45d59ebd38cde48fce4f10b5125c680c7a4bfe177aab07", size = 22791051, upload-time = "2026-01-10T21:27:46.539Z" }, - { url = "https://files.pythonhosted.org/packages/9b/9d/025cccdd738a72140efc582b1641d0dd4caf2e86c3fb127568dc80444e6e/scipy-1.17.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:130d12926ae34399d157de777472bf82e9061c60cc081372b3118edacafe1d00", size = 32815098, upload-time = "2026-01-10T21:27:54.389Z" }, - { url = "https://files.pythonhosted.org/packages/48/5f/09b879619f8bca15ce392bfc1894bd9c54377e01d1b3f2f3b595a1b4d945/scipy-1.17.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e886000eb4919eae3a44f035e63f0fd8b651234117e8f6f29bad1cd26e7bc45", size = 35031342, upload-time = "2026-01-10T21:28:03.012Z" }, - { url = "https://files.pythonhosted.org/packages/f2/9a/f0f0a9f0aa079d2f106555b984ff0fbb11a837df280f04f71f056ea9c6e4/scipy-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:13c4096ac6bc31d706018f06a49abe0485f96499deb82066b94d19b02f664209", size = 34893199, upload-time = "2026-01-10T21:28:10.832Z" }, - { url = "https://files.pythonhosted.org/packages/90/b8/4f0f5cf0c5ea4d7548424e6533e6b17d164f34a6e2fb2e43ffebb6697b06/scipy-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cacbaddd91fcffde703934897c5cd2c7cb0371fac195d383f4e1f1c5d3f3bd04", size = 37438061, upload-time = "2026-01-10T21:28:19.684Z" }, - { url = "https://files.pythonhosted.org/packages/f9/cc/2bd59140ed3b2fa2882fb15da0a9cb1b5a6443d67cfd0d98d4cec83a57ec/scipy-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:edce1a1cf66298cccdc48a1bdf8fb10a3bf58e8b58d6c3883dd1530e103f87c0", size = 36328593, upload-time = "2026-01-10T21:28:28.007Z" }, - { url = "https://files.pythonhosted.org/packages/13/1b/c87cc44a0d2c7aaf0f003aef2904c3d097b422a96c7e7c07f5efd9073c1b/scipy-1.17.0-cp313-cp313t-win_arm64.whl", hash = "sha256:30509da9dbec1c2ed8f168b8d8aa853bc6723fede1dbc23c7d43a56f5ab72a67", size = 24625083, upload-time = "2026-01-10T21:28:35.188Z" }, - { url = "https://files.pythonhosted.org/packages/1a/2d/51006cd369b8e7879e1c630999a19d1fbf6f8b5ed3e33374f29dc87e53b3/scipy-1.17.0-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:c17514d11b78be8f7e6331b983a65a7f5ca1fd037b95e27b280921fe5606286a", size = 31346803, upload-time = "2026-01-10T21:28:57.24Z" }, - { url = "https://files.pythonhosted.org/packages/d6/2e/2349458c3ce445f53a6c93d4386b1c4c5c0c540917304c01222ff95ff317/scipy-1.17.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:4e00562e519c09da34c31685f6acc3aa384d4d50604db0f245c14e1b4488bfa2", size = 27967182, upload-time = "2026-01-10T21:29:04.107Z" }, - { url = "https://files.pythonhosted.org/packages/5e/7c/df525fbfa77b878d1cfe625249529514dc02f4fd5f45f0f6295676a76528/scipy-1.17.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f7df7941d71314e60a481e02d5ebcb3f0185b8d799c70d03d8258f6c80f3d467", size = 20139125, upload-time = "2026-01-10T21:29:10.179Z" }, - { url = "https://files.pythonhosted.org/packages/33/11/fcf9d43a7ed1234d31765ec643b0515a85a30b58eddccc5d5a4d12b5f194/scipy-1.17.0-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:aabf057c632798832f071a8dde013c2e26284043934f53b00489f1773b33527e", size = 22443554, upload-time = "2026-01-10T21:29:15.888Z" }, - { url = "https://files.pythonhosted.org/packages/80/5c/ea5d239cda2dd3d31399424967a24d556cf409fbea7b5b21412b0fd0a44f/scipy-1.17.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a38c3337e00be6fd8a95b4ed66b5d988bac4ec888fd922c2ea9fe5fb1603dd67", size = 32757834, upload-time = "2026-01-10T21:29:23.406Z" }, - { url = "https://files.pythonhosted.org/packages/b8/7e/8c917cc573310e5dc91cbeead76f1b600d3fb17cf0969db02c9cf92e3cfa/scipy-1.17.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00fb5f8ec8398ad90215008d8b6009c9db9fa924fd4c7d6be307c6f945f9cd73", size = 34995775, upload-time = "2026-01-10T21:29:31.915Z" }, - { url = "https://files.pythonhosted.org/packages/c5/43/176c0c3c07b3f7df324e7cdd933d3e2c4898ca202b090bd5ba122f9fe270/scipy-1.17.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f2a4942b0f5f7c23c7cd641a0ca1955e2ae83dedcff537e3a0259096635e186b", size = 34841240, upload-time = "2026-01-10T21:29:39.995Z" }, - { url = "https://files.pythonhosted.org/packages/44/8c/d1f5f4b491160592e7f084d997de53a8e896a3ac01cd07e59f43ca222744/scipy-1.17.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:dbf133ced83889583156566d2bdf7a07ff89228fe0c0cb727f777de92092ec6b", size = 37394463, upload-time = "2026-01-10T21:29:48.723Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ec/42a6657f8d2d087e750e9a5dde0b481fd135657f09eaf1cf5688bb23c338/scipy-1.17.0-cp314-cp314-win_amd64.whl", hash = "sha256:3625c631a7acd7cfd929e4e31d2582cf00f42fcf06011f59281271746d77e061", size = 37053015, upload-time = "2026-01-10T21:30:51.418Z" }, - { url = "https://files.pythonhosted.org/packages/27/58/6b89a6afd132787d89a362d443a7bddd511b8f41336a1ae47f9e4f000dc4/scipy-1.17.0-cp314-cp314-win_arm64.whl", hash = "sha256:9244608d27eafe02b20558523ba57f15c689357c85bdcfe920b1828750aa26eb", size = 24951312, upload-time = "2026-01-10T21:30:56.771Z" }, - { url = "https://files.pythonhosted.org/packages/e9/01/f58916b9d9ae0112b86d7c3b10b9e685625ce6e8248df139d0fcb17f7397/scipy-1.17.0-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:2b531f57e09c946f56ad0b4a3b2abee778789097871fc541e267d2eca081cff1", size = 31706502, upload-time = "2026-01-10T21:29:56.326Z" }, - { url = "https://files.pythonhosted.org/packages/59/8e/2912a87f94a7d1f8b38aabc0faf74b82d3b6c9e22be991c49979f0eceed8/scipy-1.17.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:13e861634a2c480bd237deb69333ac79ea1941b94568d4b0efa5db5e263d4fd1", size = 28380854, upload-time = "2026-01-10T21:30:01.554Z" }, - { url = "https://files.pythonhosted.org/packages/bd/1c/874137a52dddab7d5d595c1887089a2125d27d0601fce8c0026a24a92a0b/scipy-1.17.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:eb2651271135154aa24f6481cbae5cc8af1f0dd46e6533fb7b56aa9727b6a232", size = 20552752, upload-time = "2026-01-10T21:30:05.93Z" }, - { url = "https://files.pythonhosted.org/packages/3f/f0/7518d171cb735f6400f4576cf70f756d5b419a07fe1867da34e2c2c9c11b/scipy-1.17.0-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:c5e8647f60679790c2f5c76be17e2e9247dc6b98ad0d3b065861e082c56e078d", size = 22803972, upload-time = "2026-01-10T21:30:10.651Z" }, - { url = "https://files.pythonhosted.org/packages/7c/74/3498563a2c619e8a3ebb4d75457486c249b19b5b04a30600dfd9af06bea5/scipy-1.17.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fb10d17e649e1446410895639f3385fd2bf4c3c7dfc9bea937bddcbc3d7b9ba", size = 32829770, upload-time = "2026-01-10T21:30:16.359Z" }, - { url = "https://files.pythonhosted.org/packages/48/d1/7b50cedd8c6c9d6f706b4b36fa8544d829c712a75e370f763b318e9638c1/scipy-1.17.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8547e7c57f932e7354a2319fab613981cde910631979f74c9b542bb167a8b9db", size = 35051093, upload-time = "2026-01-10T21:30:22.987Z" }, - { url = "https://files.pythonhosted.org/packages/e2/82/a2d684dfddb87ba1b3ea325df7c3293496ee9accb3a19abe9429bce94755/scipy-1.17.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33af70d040e8af9d5e7a38b5ed3b772adddd281e3062ff23fec49e49681c38cf", size = 34909905, upload-time = "2026-01-10T21:30:28.704Z" }, - { url = "https://files.pythonhosted.org/packages/ef/5e/e565bd73991d42023eb82bb99e51c5b3d9e2c588ca9d4b3e2cc1d3ca62a6/scipy-1.17.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb55bb97d00f8b7ab95cb64f873eb0bf54d9446264d9f3609130381233483f", size = 37457743, upload-time = "2026-01-10T21:30:34.819Z" }, - { url = "https://files.pythonhosted.org/packages/58/a8/a66a75c3d8f1fb2b83f66007d6455a06a6f6cf5618c3dc35bc9b69dd096e/scipy-1.17.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1ff269abf702f6c7e67a4b7aad981d42871a11b9dd83c58d2d2ea624efbd1088", size = 37098574, upload-time = "2026-01-10T21:30:40.782Z" }, - { url = "https://files.pythonhosted.org/packages/56/a5/df8f46ef7da168f1bc52cd86e09a9de5c6f19cc1da04454d51b7d4f43408/scipy-1.17.0-cp314-cp314t-win_arm64.whl", hash = "sha256:031121914e295d9791319a1875444d55079885bbae5bdc9c5e0f2ee5f09d34ff", size = 25246266, upload-time = "2026-01-10T21:30:45.923Z" }, -] - -[[package]] -name = "secretstorage" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, -] - -[[package]] -name = "sentence-transformers" -version = "5.2.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, - { name = "numpy" }, - { name = "scikit-learn" }, - { name = "scipy" }, - { name = "torch" }, - { name = "tqdm" }, - { name = "transformers" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a6/bc/0bc9c0ec1cf83ab2ec6e6f38667d167349b950fff6dd2086b79bd360eeca/sentence_transformers-5.2.2.tar.gz", hash = "sha256:7033ee0a24bc04c664fd490abf2ef194d387b3a58a97adcc528783ff505159fa", size = 381607, upload-time = "2026-01-27T11:11:02.658Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/21/7e925890636791386e81b52878134f114d63072e79fffe14cdcc5e7a5e6a/sentence_transformers-5.2.2-py3-none-any.whl", hash = "sha256:280ac54bffb84c110726b4d8848ba7b7c60813b9034547f8aea6e9a345cd1c23", size = 494106, upload-time = "2026-01-27T11:11:00.983Z" }, -] - -[[package]] -name = "setuptools" -version = "80.10.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/76/95/faf61eb8363f26aa7e1d762267a8d602a1b26d4f3a1e758e92cb3cb8b054/setuptools-80.10.2.tar.gz", hash = "sha256:8b0e9d10c784bf7d262c4e5ec5d4ec94127ce206e8738f29a437945fbc219b70", size = 1200343, upload-time = "2026-01-25T22:38:17.252Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/b8/f1f62a5e3c0ad2ff1d189590bfa4c46b4f3b6e49cef6f26c6ee4e575394d/setuptools-80.10.2-py3-none-any.whl", hash = "sha256:95b30ddfb717250edb492926c92b5221f7ef3fbcc2b07579bcd4a27da21d0173", size = 1064234, upload-time = "2026-01-25T22:38:15.216Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[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 = "snowballstemmer" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, -] - -[[package]] -name = "sortedcontainers" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, -] - -[[package]] -name = "sphinx" -version = "9.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "alabaster" }, - { name = "babel" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "docutils" }, - { name = "imagesize" }, - { name = "jinja2" }, - { name = "packaging" }, - { name = "pygments" }, - { name = "requests" }, - { name = "roman-numerals" }, - { name = "snowballstemmer" }, - { name = "sphinxcontrib-applehelp" }, - { name = "sphinxcontrib-devhelp" }, - { name = "sphinxcontrib-htmlhelp" }, - { name = "sphinxcontrib-jsmath" }, - { name = "sphinxcontrib-qthelp" }, - { name = "sphinxcontrib-serializinghtml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/73/f7/b1884cb3188ab181fc81fa00c266699dab600f927a964df02ec3d5d1916a/sphinx-9.1.0-py3-none-any.whl", hash = "sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978", size = 3921742, upload-time = "2025-12-31T15:09:25.561Z" }, -] - -[[package]] -name = "sphinx-copybutton" -version = "0.5.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039, upload-time = "2023-04-14T08:10:22.998Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343, upload-time = "2023-04-14T08:10:20.844Z" }, -] - -[[package]] -name = "sphinx-rtd-theme" -version = "3.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "sphinx" }, - { name = "sphinxcontrib-jquery" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/84/68/a1bfbf38c0f7bccc9b10bbf76b94606f64acb1552ae394f0b8285bfaea25/sphinx_rtd_theme-3.1.0.tar.gz", hash = "sha256:b44276f2c276e909239a4f6c955aa667aaafeb78597923b1c60babc76db78e4c", size = 7620915, upload-time = "2026-01-12T16:03:31.17Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/c7/b5c8015d823bfda1a346adb2c634a2101d50bb75d421eb6dcb31acd25ebc/sphinx_rtd_theme-3.1.0-py2.py3-none-any.whl", hash = "sha256:1785824ae8e6632060490f67cf3a72d404a85d2d9fc26bce3619944de5682b89", size = 7655617, upload-time = "2026-01-12T16:03:28.101Z" }, -] - -[[package]] -name = "sphinxcontrib-applehelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, -] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, -] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, -] - -[[package]] -name = "sphinxcontrib-jquery" -version = "4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331, upload-time = "2023-03-14T15:01:01.944Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae", size = 121104, upload-time = "2023-03-14T15:01:00.356Z" }, -] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, -] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, -] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, -] - -[[package]] -name = "sqlite-vec" -version = "0.1.6" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/ed/aabc328f29ee6814033d008ec43e44f2c595447d9cccd5f2aabe60df2933/sqlite_vec-0.1.6-py3-none-macosx_10_6_x86_64.whl", hash = "sha256:77491bcaa6d496f2acb5cc0d0ff0b8964434f141523c121e313f9a7d8088dee3", size = 164075, upload-time = "2024-11-20T16:40:29.847Z" }, - { url = "https://files.pythonhosted.org/packages/a7/57/05604e509a129b22e303758bfa062c19afb020557d5e19b008c64016704e/sqlite_vec-0.1.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fdca35f7ee3243668a055255d4dee4dea7eed5a06da8cad409f89facf4595361", size = 165242, upload-time = "2024-11-20T16:40:31.206Z" }, - { url = "https://files.pythonhosted.org/packages/f2/48/dbb2cc4e5bad88c89c7bb296e2d0a8df58aab9edc75853728c361eefc24f/sqlite_vec-0.1.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b0519d9cd96164cd2e08e8eed225197f9cd2f0be82cb04567692a0a4be02da3", size = 103704, upload-time = "2024-11-20T16:40:33.729Z" }, - { url = "https://files.pythonhosted.org/packages/80/76/97f33b1a2446f6ae55e59b33869bed4eafaf59b7f4c662c8d9491b6a714a/sqlite_vec-0.1.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux1_x86_64.whl", hash = "sha256:823b0493add80d7fe82ab0fe25df7c0703f4752941aee1c7b2b02cec9656cb24", size = 151556, upload-time = "2024-11-20T16:40:35.387Z" }, - { url = "https://files.pythonhosted.org/packages/6a/98/e8bc58b178266eae2fcf4c9c7a8303a8d41164d781b32d71097924a6bebe/sqlite_vec-0.1.6-py3-none-win_amd64.whl", hash = "sha256:c65bcfd90fa2f41f9000052bcb8bb75d38240b2dae49225389eca6c3136d3f0c", size = 281540, upload-time = "2024-11-20T16:40:37.296Z" }, -] - -[[package]] -name = "sse-starlette" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "starlette" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/00d280c03ffd39aaee0e86ec81e2d3b9253036a0f93f51d10503adef0e65/sse_starlette-3.2.0.tar.gz", hash = "sha256:8127594edfb51abe44eac9c49e59b0b01f1039d0c7461c6fd91d4e03b70da422", size = 27253, upload-time = "2026-01-17T13:11:05.62Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/7f/832f015020844a8b8f7a9cbc103dd76ba8e3875004c41e08440ea3a2b41a/sse_starlette-3.2.0-py3-none-any.whl", hash = "sha256:5876954bd51920fc2cd51baee47a080eb88a37b5b784e615abb0b283f801cdbf", size = 12763, upload-time = "2026-01-17T13:11:03.775Z" }, -] - -[[package]] -name = "stack-data" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asttokens" }, - { name = "executing" }, - { name = "pure-eval" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, -] - -[[package]] -name = "starlette" -version = "0.50.0" -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/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, -] - -[[package]] -name = "structlog" -version = "25.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ef/52/9ba0f43b686e7f3ddfeaa78ac3af750292662284b3661e91ad5494f21dbc/structlog-25.5.0.tar.gz", hash = "sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98", size = 1460830, upload-time = "2025-10-27T08:28:23.028Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/45/a132b9074aa18e799b891b91ad72133c98d8042c70f6240e4c5f9dabee2f/structlog-25.5.0-py3-none-any.whl", hash = "sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f", size = 72510, upload-time = "2025-10-27T08:28:21.535Z" }, -] - -[[package]] -name = "sympy" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mpmath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, -] - -[[package]] -name = "tenacity" -version = "9.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, -] - -[[package]] -name = "threadpoolctl" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, -] - -[[package]] -name = "tiktoken" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "regex" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806, upload-time = "2025-10-06T20:22:45.419Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/85/be65d39d6b647c79800fd9d29241d081d4eeb06271f383bb87200d74cf76/tiktoken-0.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b97f74aca0d78a1ff21b8cd9e9925714c15a9236d6ceacf5c7327c117e6e21e8", size = 1050728, upload-time = "2025-10-06T20:21:52.756Z" }, - { url = "https://files.pythonhosted.org/packages/4a/42/6573e9129bc55c9bf7300b3a35bef2c6b9117018acca0dc760ac2d93dffe/tiktoken-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b90f5ad190a4bb7c3eb30c5fa32e1e182ca1ca79f05e49b448438c3e225a49b", size = 994049, upload-time = "2025-10-06T20:21:53.782Z" }, - { url = "https://files.pythonhosted.org/packages/66/c5/ed88504d2f4a5fd6856990b230b56d85a777feab84e6129af0822f5d0f70/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:65b26c7a780e2139e73acc193e5c63ac754021f160df919add909c1492c0fb37", size = 1129008, upload-time = "2025-10-06T20:21:54.832Z" }, - { url = "https://files.pythonhosted.org/packages/f4/90/3dae6cc5436137ebd38944d396b5849e167896fc2073da643a49f372dc4f/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:edde1ec917dfd21c1f2f8046b86348b0f54a2c0547f68149d8600859598769ad", size = 1152665, upload-time = "2025-10-06T20:21:56.129Z" }, - { url = "https://files.pythonhosted.org/packages/a3/fe/26df24ce53ffde419a42f5f53d755b995c9318908288c17ec3f3448313a3/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:35a2f8ddd3824608b3d650a000c1ef71f730d0c56486845705a8248da00f9fe5", size = 1194230, upload-time = "2025-10-06T20:21:57.546Z" }, - { url = "https://files.pythonhosted.org/packages/20/cc/b064cae1a0e9fac84b0d2c46b89f4e57051a5f41324e385d10225a984c24/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83d16643edb7fa2c99eff2ab7733508aae1eebb03d5dfc46f5565862810f24e3", size = 1254688, upload-time = "2025-10-06T20:21:58.619Z" }, - { url = "https://files.pythonhosted.org/packages/81/10/b8523105c590c5b8349f2587e2fdfe51a69544bd5a76295fc20f2374f470/tiktoken-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffc5288f34a8bc02e1ea7047b8d041104791d2ddbf42d1e5fa07822cbffe16bd", size = 878694, upload-time = "2025-10-06T20:21:59.876Z" }, - { url = "https://files.pythonhosted.org/packages/00/61/441588ee21e6b5cdf59d6870f86beb9789e532ee9718c251b391b70c68d6/tiktoken-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3", size = 1050802, upload-time = "2025-10-06T20:22:00.96Z" }, - { url = "https://files.pythonhosted.org/packages/1f/05/dcf94486d5c5c8d34496abe271ac76c5b785507c8eae71b3708f1ad9b45a/tiktoken-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160", size = 993995, upload-time = "2025-10-06T20:22:02.788Z" }, - { url = "https://files.pythonhosted.org/packages/a0/70/5163fe5359b943f8db9946b62f19be2305de8c3d78a16f629d4165e2f40e/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa", size = 1128948, upload-time = "2025-10-06T20:22:03.814Z" }, - { url = "https://files.pythonhosted.org/packages/0c/da/c028aa0babf77315e1cef357d4d768800c5f8a6de04d0eac0f377cb619fa/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be", size = 1151986, upload-time = "2025-10-06T20:22:05.173Z" }, - { url = "https://files.pythonhosted.org/packages/a0/5a/886b108b766aa53e295f7216b509be95eb7d60b166049ce2c58416b25f2a/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a", size = 1194222, upload-time = "2025-10-06T20:22:06.265Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f8/4db272048397636ac7a078d22773dd2795b1becee7bc4922fe6207288d57/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3", size = 1255097, upload-time = "2025-10-06T20:22:07.403Z" }, - { url = "https://files.pythonhosted.org/packages/8e/32/45d02e2e0ea2be3a9ed22afc47d93741247e75018aac967b713b2941f8ea/tiktoken-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697", size = 879117, upload-time = "2025-10-06T20:22:08.418Z" }, - { url = "https://files.pythonhosted.org/packages/ce/76/994fc868f88e016e6d05b0da5ac24582a14c47893f4474c3e9744283f1d5/tiktoken-0.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16", size = 1050309, upload-time = "2025-10-06T20:22:10.939Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b8/57ef1456504c43a849821920d582a738a461b76a047f352f18c0b26c6516/tiktoken-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a", size = 993712, upload-time = "2025-10-06T20:22:12.115Z" }, - { url = "https://files.pythonhosted.org/packages/72/90/13da56f664286ffbae9dbcfadcc625439142675845baa62715e49b87b68b/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27", size = 1128725, upload-time = "2025-10-06T20:22:13.541Z" }, - { url = "https://files.pythonhosted.org/packages/05/df/4f80030d44682235bdaecd7346c90f67ae87ec8f3df4a3442cb53834f7e4/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb", size = 1151875, upload-time = "2025-10-06T20:22:14.559Z" }, - { url = "https://files.pythonhosted.org/packages/22/1f/ae535223a8c4ef4c0c1192e3f9b82da660be9eb66b9279e95c99288e9dab/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e", size = 1194451, upload-time = "2025-10-06T20:22:15.545Z" }, - { url = "https://files.pythonhosted.org/packages/78/a7/f8ead382fce0243cb625c4f266e66c27f65ae65ee9e77f59ea1653b6d730/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25", size = 1253794, upload-time = "2025-10-06T20:22:16.624Z" }, - { url = "https://files.pythonhosted.org/packages/93/e0/6cc82a562bc6365785a3ff0af27a2a092d57c47d7a81d9e2295d8c36f011/tiktoken-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f", size = 878777, upload-time = "2025-10-06T20:22:18.036Z" }, - { url = "https://files.pythonhosted.org/packages/72/05/3abc1db5d2c9aadc4d2c76fa5640134e475e58d9fbb82b5c535dc0de9b01/tiktoken-0.12.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a90388128df3b3abeb2bfd1895b0681412a8d7dc644142519e6f0a97c2111646", size = 1050188, upload-time = "2025-10-06T20:22:19.563Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7b/50c2f060412202d6c95f32b20755c7a6273543b125c0985d6fa9465105af/tiktoken-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:da900aa0ad52247d8794e307d6446bd3cdea8e192769b56276695d34d2c9aa88", size = 993978, upload-time = "2025-10-06T20:22:20.702Z" }, - { url = "https://files.pythonhosted.org/packages/14/27/bf795595a2b897e271771cd31cb847d479073497344c637966bdf2853da1/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:285ba9d73ea0d6171e7f9407039a290ca77efcdb026be7769dccc01d2c8d7fff", size = 1129271, upload-time = "2025-10-06T20:22:22.06Z" }, - { url = "https://files.pythonhosted.org/packages/f5/de/9341a6d7a8f1b448573bbf3425fa57669ac58258a667eb48a25dfe916d70/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:d186a5c60c6a0213f04a7a802264083dea1bbde92a2d4c7069e1a56630aef830", size = 1151216, upload-time = "2025-10-06T20:22:23.085Z" }, - { url = "https://files.pythonhosted.org/packages/75/0d/881866647b8d1be4d67cb24e50d0c26f9f807f994aa1510cb9ba2fe5f612/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:604831189bd05480f2b885ecd2d1986dc7686f609de48208ebbbddeea071fc0b", size = 1194860, upload-time = "2025-10-06T20:22:24.602Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1e/b651ec3059474dab649b8d5b69f5c65cd8fcd8918568c1935bd4136c9392/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8f317e8530bb3a222547b85a58583238c8f74fd7a7408305f9f63246d1a0958b", size = 1254567, upload-time = "2025-10-06T20:22:25.671Z" }, - { url = "https://files.pythonhosted.org/packages/80/57/ce64fd16ac390fafde001268c364d559447ba09b509181b2808622420eec/tiktoken-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:399c3dd672a6406719d84442299a490420b458c44d3ae65516302a99675888f3", size = 921067, upload-time = "2025-10-06T20:22:26.753Z" }, - { url = "https://files.pythonhosted.org/packages/ac/a4/72eed53e8976a099539cdd5eb36f241987212c29629d0a52c305173e0a68/tiktoken-0.12.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2c714c72bc00a38ca969dae79e8266ddec999c7ceccd603cc4f0d04ccd76365", size = 1050473, upload-time = "2025-10-06T20:22:27.775Z" }, - { url = "https://files.pythonhosted.org/packages/e6/d7/0110b8f54c008466b19672c615f2168896b83706a6611ba6e47313dbc6e9/tiktoken-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cbb9a3ba275165a2cb0f9a83f5d7025afe6b9d0ab01a22b50f0e74fee2ad253e", size = 993855, upload-time = "2025-10-06T20:22:28.799Z" }, - { url = "https://files.pythonhosted.org/packages/5f/77/4f268c41a3957c418b084dd576ea2fad2e95da0d8e1ab705372892c2ca22/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:dfdfaa5ffff8993a3af94d1125870b1d27aed7cb97aa7eb8c1cefdbc87dbee63", size = 1129022, upload-time = "2025-10-06T20:22:29.981Z" }, - { url = "https://files.pythonhosted.org/packages/4e/2b/fc46c90fe5028bd094cd6ee25a7db321cb91d45dc87531e2bdbb26b4867a/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:584c3ad3d0c74f5269906eb8a659c8bfc6144a52895d9261cdaf90a0ae5f4de0", size = 1150736, upload-time = "2025-10-06T20:22:30.996Z" }, - { url = "https://files.pythonhosted.org/packages/28/c0/3c7a39ff68022ddfd7d93f3337ad90389a342f761c4d71de99a3ccc57857/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:54c891b416a0e36b8e2045b12b33dd66fb34a4fe7965565f1b482da50da3e86a", size = 1194908, upload-time = "2025-10-06T20:22:32.073Z" }, - { url = "https://files.pythonhosted.org/packages/ab/0d/c1ad6f4016a3968c048545f5d9b8ffebf577774b2ede3e2e352553b685fe/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5edb8743b88d5be814b1a8a8854494719080c28faaa1ccbef02e87354fe71ef0", size = 1253706, upload-time = "2025-10-06T20:22:33.385Z" }, - { url = "https://files.pythonhosted.org/packages/af/df/c7891ef9d2712ad774777271d39fdef63941ffba0a9d59b7ad1fd2765e57/tiktoken-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f61c0aea5565ac82e2ec50a05e02a6c44734e91b51c10510b084ea1b8e633a71", size = 920667, upload-time = "2025-10-06T20:22:34.444Z" }, -] - -[[package]] -name = "tokenizers" -version = "0.22.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, - { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, - { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, - { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, - { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, - { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, - { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, - { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, - { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, - { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, - { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, - { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, - { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, - { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, -] - -[[package]] -name = "torch" -version = "2.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-bindings", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "setuptools" }, - { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "typing-extensions" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/54/a2ba279afcca44bbd320d4e73675b282fcee3d81400ea1b53934efca6462/torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:13ec4add8c3faaed8d13e0574f5cd4a323c11655546f91fbe6afa77b57423574", size = 79498202, upload-time = "2026-02-10T21:44:52.603Z" }, - { url = "https://files.pythonhosted.org/packages/ec/23/2c9fe0c9c27f7f6cb865abcea8a4568f29f00acaeadfc6a37f6801f84cb4/torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:e521c9f030a3774ed770a9c011751fb47c4d12029a3d6522116e48431f2ff89e", size = 79498254, upload-time = "2026-02-10T21:44:44.095Z" }, - { url = "https://files.pythonhosted.org/packages/b3/7a/abada41517ce0011775f0f4eacc79659bc9bc6c361e6bfe6f7052a6b9363/torch-2.10.0-3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:98c01b8bb5e3240426dcde1446eed6f40c778091c8544767ef1168fc663a05a6", size = 915622781, upload-time = "2026-03-11T14:17:11.354Z" }, - { url = "https://files.pythonhosted.org/packages/ab/c6/4dfe238342ffdcec5aef1c96c457548762d33c40b45a1ab7033bb26d2ff2/torch-2.10.0-3-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:80b1b5bfe38eb0e9f5ff09f206dcac0a87aadd084230d4a36eea5ec5232c115b", size = 915627275, upload-time = "2026-03-11T14:16:11.325Z" }, - { url = "https://files.pythonhosted.org/packages/d8/f0/72bf18847f58f877a6a8acf60614b14935e2f156d942483af1ffc081aea0/torch-2.10.0-3-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:46b3574d93a2a8134b3f5475cfb98e2eb46771794c57015f6ad1fb795ec25e49", size = 915523474, upload-time = "2026-03-11T14:17:44.422Z" }, - { url = "https://files.pythonhosted.org/packages/f4/39/590742415c3030551944edc2ddc273ea1fdfe8ffb2780992e824f1ebee98/torch-2.10.0-3-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b1d5e2aba4eb7f8e87fbe04f86442887f9167a35f092afe4c237dfcaaef6e328", size = 915632474, upload-time = "2026-03-11T14:15:13.666Z" }, - { url = "https://files.pythonhosted.org/packages/b6/8e/34949484f764dde5b222b7fe3fede43e4a6f0da9d7f8c370bb617d629ee2/torch-2.10.0-3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:0228d20b06701c05a8f978357f657817a4a63984b0c90745def81c18aedfa591", size = 915523882, upload-time = "2026-03-11T14:14:46.311Z" }, - { url = "https://files.pythonhosted.org/packages/cc/af/758e242e9102e9988969b5e621d41f36b8f258bb4a099109b7a4b4b50ea4/torch-2.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5fd4117d89ffd47e3dcc71e71a22efac24828ad781c7e46aaaf56bf7f2796acf", size = 145996088, upload-time = "2026-01-21T16:24:44.171Z" }, - { url = "https://files.pythonhosted.org/packages/23/8e/3c74db5e53bff7ed9e34c8123e6a8bfef718b2450c35eefab85bb4a7e270/torch-2.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:787124e7db3b379d4f1ed54dd12ae7c741c16a4d29b49c0226a89bea50923ffb", size = 915711952, upload-time = "2026-01-21T16:23:53.503Z" }, - { url = "https://files.pythonhosted.org/packages/6e/01/624c4324ca01f66ae4c7cd1b74eb16fb52596dce66dbe51eff95ef9e7a4c/torch-2.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2c66c61f44c5f903046cc696d088e21062644cbe541c7f1c4eaae88b2ad23547", size = 113757972, upload-time = "2026-01-21T16:24:39.516Z" }, - { url = "https://files.pythonhosted.org/packages/c9/5c/dee910b87c4d5c0fcb41b50839ae04df87c1cfc663cf1b5fca7ea565eeaa/torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:6d3707a61863d1c4d6ebba7be4ca320f42b869ee657e9b2c21c736bf17000294", size = 79498198, upload-time = "2026-01-21T16:24:34.704Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6f/f2e91e34e3fcba2e3fc8d8f74e7d6c22e74e480bbd1db7bc8900fdf3e95c/torch-2.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5c4d217b14741e40776dd7074d9006fd28b8a97ef5654db959d8635b2fe5f29b", size = 146004247, upload-time = "2026-01-21T16:24:29.335Z" }, - { url = "https://files.pythonhosted.org/packages/98/fb/5160261aeb5e1ee12ee95fe599d0541f7c976c3701d607d8fc29e623229f/torch-2.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6b71486353fce0f9714ca0c9ef1c850a2ae766b409808acd58e9678a3edb7738", size = 915716445, upload-time = "2026-01-21T16:22:45.353Z" }, - { url = "https://files.pythonhosted.org/packages/6a/16/502fb1b41e6d868e8deb5b0e3ae926bbb36dab8ceb0d1b769b266ad7b0c3/torch-2.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2ee399c644dc92ef7bc0d4f7e74b5360c37cdbe7c5ba11318dda49ffac2bc57", size = 113757050, upload-time = "2026-01-21T16:24:19.204Z" }, - { url = "https://files.pythonhosted.org/packages/1a/0b/39929b148f4824bc3ad6f9f72a29d4ad865bcf7ebfc2fa67584773e083d2/torch-2.10.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:3202429f58309b9fa96a614885eace4b7995729f44beb54d3e4a47773649d382", size = 79851305, upload-time = "2026-01-21T16:24:09.209Z" }, - { url = "https://files.pythonhosted.org/packages/d8/14/21fbce63bc452381ba5f74a2c0a959fdf5ad5803ccc0c654e752e0dbe91a/torch-2.10.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:aae1b29cd68e50a9397f5ee897b9c24742e9e306f88a807a27d617f07adb3bd8", size = 146005472, upload-time = "2026-01-21T16:22:29.022Z" }, - { url = "https://files.pythonhosted.org/packages/54/fd/b207d1c525cb570ef47f3e9f836b154685011fce11a2f444ba8a4084d042/torch-2.10.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6021db85958db2f07ec94e1bc77212721ba4920c12a18dc552d2ae36a3eb163f", size = 915612644, upload-time = "2026-01-21T16:21:47.019Z" }, - { url = "https://files.pythonhosted.org/packages/36/53/0197f868c75f1050b199fe58f9bf3bf3aecac9b4e85cc9c964383d745403/torch-2.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff43db38af76fda183156153983c9a096fc4c78d0cd1e07b14a2314c7f01c2c8", size = 113997015, upload-time = "2026-01-21T16:23:00.767Z" }, - { url = "https://files.pythonhosted.org/packages/0e/13/e76b4d9c160e89fff48bf16b449ea324bda84745d2ab30294c37c2434c0d/torch-2.10.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:cdf2a523d699b70d613243211ecaac14fe9c5df8a0b0a9c02add60fb2a413e0f", size = 79498248, upload-time = "2026-01-21T16:23:09.315Z" }, - { url = "https://files.pythonhosted.org/packages/4f/93/716b5ac0155f1be70ed81bacc21269c3ece8dba0c249b9994094110bfc51/torch-2.10.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:bf0d9ff448b0218e0433aeb198805192346c4fd659c852370d5cc245f602a06a", size = 79464992, upload-time = "2026-01-21T16:23:05.162Z" }, - { url = "https://files.pythonhosted.org/packages/69/2b/51e663ff190c9d16d4a8271203b71bc73a16aa7619b9f271a69b9d4a936b/torch-2.10.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:233aed0659a2503b831d8a67e9da66a62c996204c0bba4f4c442ccc0c68a3f60", size = 146018567, upload-time = "2026-01-21T16:22:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/5e/cd/4b95ef7f293b927c283db0b136c42be91c8ec6845c44de0238c8c23bdc80/torch-2.10.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:682497e16bdfa6efeec8cde66531bc8d1fbbbb4d8788ec6173c089ed3cc2bfe5", size = 915721646, upload-time = "2026-01-21T16:21:16.983Z" }, - { url = "https://files.pythonhosted.org/packages/56/97/078a007208f8056d88ae43198833469e61a0a355abc0b070edd2c085eb9a/torch-2.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:6528f13d2a8593a1a412ea07a99812495bec07e9224c28b2a25c0a30c7da025c", size = 113752373, upload-time = "2026-01-21T16:22:13.471Z" }, - { url = "https://files.pythonhosted.org/packages/d8/94/71994e7d0d5238393df9732fdab607e37e2b56d26a746cb59fdb415f8966/torch-2.10.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f5ab4ba32383061be0fb74bda772d470140a12c1c3b58a0cfbf3dae94d164c28", size = 79850324, upload-time = "2026-01-21T16:22:09.494Z" }, - { url = "https://files.pythonhosted.org/packages/e2/65/1a05346b418ea8ccd10360eef4b3e0ce688fba544e76edec26913a8d0ee0/torch-2.10.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:716b01a176c2a5659c98f6b01bf868244abdd896526f1c692712ab36dbaf9b63", size = 146006482, upload-time = "2026-01-21T16:22:18.42Z" }, - { url = "https://files.pythonhosted.org/packages/1d/b9/5f6f9d9e859fc3235f60578fa64f52c9c6e9b4327f0fe0defb6de5c0de31/torch-2.10.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d8f5912ba938233f86361e891789595ff35ca4b4e2ac8fe3670895e5976731d6", size = 915613050, upload-time = "2026-01-21T16:20:49.035Z" }, - { url = "https://files.pythonhosted.org/packages/66/4d/35352043ee0eaffdeff154fad67cd4a31dbed7ff8e3be1cc4549717d6d51/torch-2.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:71283a373f0ee2c89e0f0d5f446039bdabe8dbc3c9ccf35f0f784908b0acd185", size = 113995816, upload-time = "2026-01-21T16:22:05.312Z" }, -] - -[[package]] -name = "tornado" -version = "6.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/37/1d/0a336abf618272d53f62ebe274f712e213f5a03c0b2339575430b8362ef2/tornado-6.5.4.tar.gz", hash = "sha256:a22fa9047405d03260b483980635f0b041989d8bcc9a313f8fe18b411d84b1d7", size = 513632, upload-time = "2025-12-15T19:21:03.836Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/a9/e94a9d5224107d7ce3cc1fab8d5dc97f5ea351ccc6322ee4fb661da94e35/tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d6241c1a16b1c9e4cc28148b1cda97dd1c6cb4fb7068ac1bedc610768dff0ba9", size = 443909, upload-time = "2025-12-15T19:20:48.382Z" }, - { url = "https://files.pythonhosted.org/packages/db/7e/f7b8d8c4453f305a51f80dbb49014257bb7d28ccb4bbb8dd328ea995ecad/tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2d50f63dda1d2cac3ae1fa23d254e16b5e38153758470e9956cbc3d813d40843", size = 442163, upload-time = "2025-12-15T19:20:49.791Z" }, - { url = "https://files.pythonhosted.org/packages/ba/b5/206f82d51e1bfa940ba366a8d2f83904b15942c45a78dd978b599870ab44/tornado-6.5.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1cf66105dc6acb5af613c054955b8137e34a03698aa53272dbda4afe252be17", size = 445746, upload-time = "2025-12-15T19:20:51.491Z" }, - { url = "https://files.pythonhosted.org/packages/8e/9d/1a3338e0bd30ada6ad4356c13a0a6c35fbc859063fa7eddb309183364ac1/tornado-6.5.4-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50ff0a58b0dc97939d29da29cd624da010e7f804746621c78d14b80238669335", size = 445083, upload-time = "2025-12-15T19:20:52.778Z" }, - { url = "https://files.pythonhosted.org/packages/50/d4/e51d52047e7eb9a582da59f32125d17c0482d065afd5d3bc435ff2120dc5/tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fb5e04efa54cf0baabdd10061eb4148e0be137166146fff835745f59ab9f7f", size = 445315, upload-time = "2025-12-15T19:20:53.996Z" }, - { url = "https://files.pythonhosted.org/packages/27/07/2273972f69ca63dbc139694a3fc4684edec3ea3f9efabf77ed32483b875c/tornado-6.5.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9c86b1643b33a4cd415f8d0fe53045f913bf07b4a3ef646b735a6a86047dda84", size = 446003, upload-time = "2025-12-15T19:20:56.101Z" }, - { url = "https://files.pythonhosted.org/packages/d1/83/41c52e47502bf7260044413b6770d1a48dda2f0246f95ee1384a3cd9c44a/tornado-6.5.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:6eb82872335a53dd063a4f10917b3efd28270b56a33db69009606a0312660a6f", size = 445412, upload-time = "2025-12-15T19:20:57.398Z" }, - { url = "https://files.pythonhosted.org/packages/10/c7/bc96917f06cbee182d44735d4ecde9c432e25b84f4c2086143013e7b9e52/tornado-6.5.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6076d5dda368c9328ff41ab5d9dd3608e695e8225d1cd0fd1e006f05da3635a8", size = 445392, upload-time = "2025-12-15T19:20:58.692Z" }, - { url = "https://files.pythonhosted.org/packages/0c/1a/d7592328d037d36f2d2462f4bc1fbb383eec9278bc786c1b111cbbd44cfa/tornado-6.5.4-cp39-abi3-win32.whl", hash = "sha256:1768110f2411d5cd281bac0a090f707223ce77fd110424361092859e089b38d1", size = 446481, upload-time = "2025-12-15T19:21:00.008Z" }, - { url = "https://files.pythonhosted.org/packages/d6/6d/c69be695a0a64fd37a97db12355a035a6d90f79067a3cf936ec2b1dc38cd/tornado-6.5.4-cp39-abi3-win_amd64.whl", hash = "sha256:fa07d31e0cd85c60713f2b995da613588aa03e1303d75705dca6af8babc18ddc", size = 446886, upload-time = "2025-12-15T19:21:01.287Z" }, - { url = "https://files.pythonhosted.org/packages/50/49/8dc3fd90902f70084bd2cd059d576ddb4f8bb44c2c7c0e33a11422acb17e/tornado-6.5.4-cp39-abi3-win_arm64.whl", hash = "sha256:053e6e16701eb6cbe641f308f4c1a9541f91b6261991160391bfc342e8a551a1", size = 445910, upload-time = "2025-12-15T19:21:02.571Z" }, -] - -[[package]] -name = "tqdm" -version = "4.67.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/27/89/4b0001b2dab8df0a5ee2787dcbe771de75ded01f18f1f8d53dedeea2882b/tqdm-4.67.2.tar.gz", hash = "sha256:649aac53964b2cb8dec76a14b405a4c0d13612cb8933aae547dd144eacc99653", size = 169514, upload-time = "2026-01-30T23:12:06.555Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/e2/31eac96de2915cf20ccaed0225035db149dfb9165a9ed28d4b252ef3f7f7/tqdm-4.67.2-py3-none-any.whl", hash = "sha256:9a12abcbbff58b6036b2167d9d3853042b9d436fe7330f06ae047867f2f8e0a7", size = 78354, upload-time = "2026-01-30T23:12:04.368Z" }, -] - -[[package]] -name = "traitlets" -version = "5.14.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, -] - -[[package]] -name = "transformers" -version = "5.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "huggingface-hub" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "regex" }, - { name = "safetensors" }, - { name = "tokenizers" }, - { name = "tqdm" }, - { name = "typer-slim" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/79/845941711811789c85fb7e2599cea425a14a07eda40f50896b9d3fda7492/transformers-5.0.0.tar.gz", hash = "sha256:5f5634efed6cf76ad068cc5834c7adbc32db78bbd6211fb70df2325a9c37dec8", size = 8424830, upload-time = "2026-01-26T10:46:46.813Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/f3/ac976fa8e305c9e49772527e09fbdc27cc6831b8a2f6b6063406626be5dd/transformers-5.0.0-py3-none-any.whl", hash = "sha256:587086f249ce64c817213cf36afdb318d087f790723e9b3d4500b97832afd52d", size = 10142091, upload-time = "2026-01-26T10:46:43.88Z" }, -] - -[[package]] -name = "tree-sitter" -version = "0.25.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/7c/0350cfc47faadc0d3cf7d8237a4e34032b3014ddf4a12ded9933e1648b55/tree-sitter-0.25.2.tar.gz", hash = "sha256:fe43c158555da46723b28b52e058ad444195afd1db3ca7720c59a254544e9c20", size = 177961, upload-time = "2025-09-25T17:37:59.751Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/9e/20c2a00a862f1c2897a436b17edb774e831b22218083b459d0d081c9db33/tree_sitter-0.25.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ddabfff809ffc983fc9963455ba1cecc90295803e06e140a4c83e94c1fa3d960", size = 146941, upload-time = "2025-09-25T17:37:34.813Z" }, - { url = "https://files.pythonhosted.org/packages/ef/04/8512e2062e652a1016e840ce36ba1cc33258b0dcc4e500d8089b4054afec/tree_sitter-0.25.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c0c0ab5f94938a23fe81928a21cc0fac44143133ccc4eb7eeb1b92f84748331c", size = 137699, upload-time = "2025-09-25T17:37:36.349Z" }, - { url = "https://files.pythonhosted.org/packages/47/8a/d48c0414db19307b0fb3bb10d76a3a0cbe275bb293f145ee7fba2abd668e/tree_sitter-0.25.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd12d80d91d4114ca097626eb82714618dcdfacd6a5e0955216c6485c350ef99", size = 607125, upload-time = "2025-09-25T17:37:37.725Z" }, - { url = "https://files.pythonhosted.org/packages/39/d1/b95f545e9fc5001b8a78636ef942a4e4e536580caa6a99e73dd0a02e87aa/tree_sitter-0.25.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b43a9e4c89d4d0839de27cd4d6902d33396de700e9ff4c5ab7631f277a85ead9", size = 635418, upload-time = "2025-09-25T17:37:38.922Z" }, - { url = "https://files.pythonhosted.org/packages/de/4d/b734bde3fb6f3513a010fa91f1f2875442cdc0382d6a949005cd84563d8f/tree_sitter-0.25.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbb1706407c0e451c4f8cc016fec27d72d4b211fdd3173320b1ada7a6c74c3ac", size = 631250, upload-time = "2025-09-25T17:37:40.039Z" }, - { url = "https://files.pythonhosted.org/packages/46/f2/5f654994f36d10c64d50a192239599fcae46677491c8dd53e7579c35a3e3/tree_sitter-0.25.2-cp312-cp312-win_amd64.whl", hash = "sha256:6d0302550bbe4620a5dc7649517c4409d74ef18558276ce758419cf09e578897", size = 127156, upload-time = "2025-09-25T17:37:41.132Z" }, - { url = "https://files.pythonhosted.org/packages/67/23/148c468d410efcf0a9535272d81c258d840c27b34781d625f1f627e2e27d/tree_sitter-0.25.2-cp312-cp312-win_arm64.whl", hash = "sha256:0c8b6682cac77e37cfe5cf7ec388844957f48b7bd8d6321d0ca2d852994e10d5", size = 113984, upload-time = "2025-09-25T17:37:42.074Z" }, - { url = "https://files.pythonhosted.org/packages/8c/67/67492014ce32729b63d7ef318a19f9cfedd855d677de5773476caf771e96/tree_sitter-0.25.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0628671f0de69bb279558ef6b640bcfc97864fe0026d840f872728a86cd6b6cd", size = 146926, upload-time = "2025-09-25T17:37:43.041Z" }, - { url = "https://files.pythonhosted.org/packages/4e/9c/a278b15e6b263e86c5e301c82a60923fa7c59d44f78d7a110a89a413e640/tree_sitter-0.25.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f5ddcd3e291a749b62521f71fc953f66f5fd9743973fd6dd962b092773569601", size = 137712, upload-time = "2025-09-25T17:37:44.039Z" }, - { url = "https://files.pythonhosted.org/packages/54/9a/423bba15d2bf6473ba67846ba5244b988cd97a4b1ea2b146822162256794/tree_sitter-0.25.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd88fbb0f6c3a0f28f0a68d72df88e9755cf5215bae146f5a1bdc8362b772053", size = 607873, upload-time = "2025-09-25T17:37:45.477Z" }, - { url = "https://files.pythonhosted.org/packages/ed/4c/b430d2cb43f8badfb3a3fa9d6cd7c8247698187b5674008c9d67b2a90c8e/tree_sitter-0.25.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b878e296e63661c8e124177cc3084b041ba3f5936b43076d57c487822426f614", size = 636313, upload-time = "2025-09-25T17:37:46.68Z" }, - { url = "https://files.pythonhosted.org/packages/9d/27/5f97098dbba807331d666a0997662e82d066e84b17d92efab575d283822f/tree_sitter-0.25.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d77605e0d353ba3fe5627e5490f0fbfe44141bafa4478d88ef7954a61a848dae", size = 631370, upload-time = "2025-09-25T17:37:47.993Z" }, - { url = "https://files.pythonhosted.org/packages/d4/3c/87caaed663fabc35e18dc704cd0e9800a0ee2f22bd18b9cbe7c10799895d/tree_sitter-0.25.2-cp313-cp313-win_amd64.whl", hash = "sha256:463c032bd02052d934daa5f45d183e0521ceb783c2548501cf034b0beba92c9b", size = 127157, upload-time = "2025-09-25T17:37:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/d5/23/f8467b408b7988aff4ea40946a4bd1a2c1a73d17156a9d039bbaff1e2ceb/tree_sitter-0.25.2-cp313-cp313-win_arm64.whl", hash = "sha256:b3f63a1796886249bd22c559a5944d64d05d43f2be72961624278eff0dcc5cb8", size = 113975, upload-time = "2025-09-25T17:37:49.922Z" }, - { url = "https://files.pythonhosted.org/packages/07/e3/d9526ba71dfbbe4eba5e51d89432b4b333a49a1e70712aa5590cd22fc74f/tree_sitter-0.25.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:65d3c931013ea798b502782acab986bbf47ba2c452610ab0776cf4a8ef150fc0", size = 146776, upload-time = "2025-09-25T17:37:50.898Z" }, - { url = "https://files.pythonhosted.org/packages/42/97/4bd4ad97f85a23011dd8a535534bb1035c4e0bac1234d58f438e15cff51f/tree_sitter-0.25.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bda059af9d621918efb813b22fb06b3fe00c3e94079c6143fcb2c565eb44cb87", size = 137732, upload-time = "2025-09-25T17:37:51.877Z" }, - { url = "https://files.pythonhosted.org/packages/b6/19/1e968aa0b1b567988ed522f836498a6a9529a74aab15f09dd9ac1e41f505/tree_sitter-0.25.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eac4e8e4c7060c75f395feec46421eb61212cb73998dbe004b7384724f3682ab", size = 609456, upload-time = "2025-09-25T17:37:52.925Z" }, - { url = "https://files.pythonhosted.org/packages/48/b6/cf08f4f20f4c9094006ef8828555484e842fc468827ad6e56011ab668dbd/tree_sitter-0.25.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:260586381b23be33b6191a07cea3d44ecbd6c01aa4c6b027a0439145fcbc3358", size = 636772, upload-time = "2025-09-25T17:37:54.647Z" }, - { url = "https://files.pythonhosted.org/packages/57/e2/d42d55bf56360987c32bc7b16adb06744e425670b823fb8a5786a1cea991/tree_sitter-0.25.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7d2ee1acbacebe50ba0f85fff1bc05e65d877958f00880f49f9b2af38dce1af0", size = 631522, upload-time = "2025-09-25T17:37:55.833Z" }, - { url = "https://files.pythonhosted.org/packages/03/87/af9604ebe275a9345d88c3ace0cf2a1341aa3f8ef49dd9fc11662132df8a/tree_sitter-0.25.2-cp314-cp314-win_amd64.whl", hash = "sha256:4973b718fcadfb04e59e746abfbb0288694159c6aeecd2add59320c03368c721", size = 130864, upload-time = "2025-09-25T17:37:57.453Z" }, - { url = "https://files.pythonhosted.org/packages/a6/6e/e64621037357acb83d912276ffd30a859ef117f9c680f2e3cb955f47c680/tree_sitter-0.25.2-cp314-cp314-win_arm64.whl", hash = "sha256:b8d4429954a3beb3e844e2872610d2a4800ba4eb42bb1990c6a4b1949b18459f", size = 117470, upload-time = "2025-09-25T17:37:58.431Z" }, -] - -[[package]] -name = "tree-sitter-c-sharp" -version = "0.23.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/22/85/a61c782afbb706a47d990eaee6977e7c2bd013771c5bf5c81c617684f286/tree_sitter_c_sharp-0.23.1.tar.gz", hash = "sha256:322e2cfd3a547a840375276b2aea3335fa6458aeac082f6c60fec3f745c967eb", size = 1317728, upload-time = "2024-11-11T05:25:32.535Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/04/f6c2df4c53a588ccd88d50851155945cff8cd887bd70c175e00aaade7edf/tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2b612a6e5bd17bb7fa2aab4bb6fc1fba45c94f09cb034ab332e45603b86e32fd", size = 372235, upload-time = "2024-11-11T05:25:19.424Z" }, - { url = "https://files.pythonhosted.org/packages/99/10/1aa9486f1e28fc22810fa92cbdc54e1051e7f5536a5e5b5e9695f609b31e/tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a8b98f62bc53efcd4d971151950c9b9cd5cbe3bacdb0cd69fdccac63350d83e", size = 419046, upload-time = "2024-11-11T05:25:20.679Z" }, - { url = "https://files.pythonhosted.org/packages/0f/21/13df29f8fcb9ba9f209b7b413a4764b673dfd58989a0dd67e9c7e19e9c2e/tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:986e93d845a438ec3c4416401aa98e6a6f6631d644bbbc2e43fcb915c51d255d", size = 415999, upload-time = "2024-11-11T05:25:22.359Z" }, - { url = "https://files.pythonhosted.org/packages/ca/72/fc6846795bcdae2f8aa94cc8b1d1af33d634e08be63e294ff0d6794b1efc/tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8024e466b2f5611c6dc90321f232d8584893c7fb88b75e4a831992f877616d2", size = 402830, upload-time = "2024-11-11T05:25:24.198Z" }, - { url = "https://files.pythonhosted.org/packages/fe/3a/b6028c5890ce6653807d5fa88c72232c027c6ceb480dbeb3b186d60e5971/tree_sitter_c_sharp-0.23.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7f9bf876866835492281d336b9e1f9626ab668737f74e914c31d285261507da7", size = 397880, upload-time = "2024-11-11T05:25:25.937Z" }, - { url = "https://files.pythonhosted.org/packages/47/d2/4facaa34b40f8104d8751746d0e1cd2ddf0beb9f1404b736b97f372bd1f3/tree_sitter_c_sharp-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:ae9a9e859e8f44e2b07578d44f9a220d3fa25b688966708af6aa55d42abeebb3", size = 377562, upload-time = "2024-11-11T05:25:27.539Z" }, - { url = "https://files.pythonhosted.org/packages/d8/88/3cf6bd9959d94d1fec1e6a9c530c5f08ff4115a474f62aedb5fedb0f7241/tree_sitter_c_sharp-0.23.1-cp39-abi3-win_arm64.whl", hash = "sha256:c81548347a93347be4f48cb63ec7d60ef4b0efa91313330e69641e49aa5a08c5", size = 375157, upload-time = "2024-11-11T05:25:30.839Z" }, -] - -[[package]] -name = "tree-sitter-embedded-template" -version = "0.25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/a7/77729fefab8b1b5690cfc54328f2f629d1c076d16daf32c96ba39d3a3a3a/tree_sitter_embedded_template-0.25.0.tar.gz", hash = "sha256:7d72d5e8a1d1d501a7c90e841b51f1449a90cc240be050e4fb85c22dab991d50", size = 14114, upload-time = "2025-08-29T00:42:51.078Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/9d/3e3c8ee0c019d3bace728300a1ca807c03df39e66cc51e9a5e7c9d1e1909/tree_sitter_embedded_template-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fa0d06467199aeb33fb3d6fa0665bf9b7d5a32621ffdaf37fd8249f8a8050649", size = 10266, upload-time = "2025-08-29T00:42:44.148Z" }, - { url = "https://files.pythonhosted.org/packages/e8/ab/6d4e43b736b2a895d13baea3791dc8ce7245bedf4677df9e7deb22e23a2a/tree_sitter_embedded_template-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc7aacbc2985a5d7e7fe7334f44dffe24c38fb0a8295c4188a04cf21a3d64a73", size = 10650, upload-time = "2025-08-29T00:42:45.147Z" }, - { url = "https://files.pythonhosted.org/packages/9f/97/ea3d1ea4b320fe66e0468b9f6602966e544c9fe641882484f9105e50ee0c/tree_sitter_embedded_template-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7c88c3dd8b94b3c9efe8ae071ff6b1b936a27ac5f6e651845c3b9631fa4c1c2", size = 18268, upload-time = "2025-08-29T00:42:46.03Z" }, - { url = "https://files.pythonhosted.org/packages/64/40/0f42ca894a8f7c298cf336080046ccc14c10e8f4ea46d455f640193181b2/tree_sitter_embedded_template-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:025f7ca84218dcd8455efc901bdbcc2689fb694f3a636c0448e322a23d4bc96b", size = 19068, upload-time = "2025-08-29T00:42:46.699Z" }, - { url = "https://files.pythonhosted.org/packages/d0/2a/0b720bcae7c2dd0a44889c09e800a2f8eb08c496dede9f2b97683506c4c3/tree_sitter_embedded_template-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b5dc1aef6ffa3fae621fe037d85dd98948b597afba20df29d779c426be813ee5", size = 18518, upload-time = "2025-08-29T00:42:47.694Z" }, - { url = "https://files.pythonhosted.org/packages/14/8a/d745071afa5e8bdf5b381cf84c4dc6be6c79dee6af8e0ff07476c3d8e4aa/tree_sitter_embedded_template-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d0a35cfe634c44981a516243bc039874580e02a2990669313730187ce83a5bc6", size = 18267, upload-time = "2025-08-29T00:42:48.635Z" }, - { url = "https://files.pythonhosted.org/packages/5d/74/728355e594fca140f793f234fdfec195366b6956b35754d00ea97ca18b21/tree_sitter_embedded_template-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:3e05a4ac013d54505e75ae48e1a0e9db9aab19949fe15d9f4c7345b11a84a069", size = 13049, upload-time = "2025-08-29T00:42:49.589Z" }, - { url = "https://files.pythonhosted.org/packages/d8/de/afac475e694d0e626b0808f3c86339c349cd15c5163a6a16a53cc11cf892/tree_sitter_embedded_template-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:2751d402179ac0e83f2065b249d8fe6df0718153f1636bcb6a02bde3e5730db9", size = 11978, upload-time = "2025-08-29T00:42:50.226Z" }, -] - -[[package]] -name = "tree-sitter-language-pack" -version = "0.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tree-sitter" }, - { name = "tree-sitter-c-sharp" }, - { name = "tree-sitter-embedded-template" }, - { name = "tree-sitter-yaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c1/83/d1bc738d6f253f415ee54a8afb99640f47028871436f53f2af637c392c4f/tree_sitter_language_pack-0.13.0.tar.gz", hash = "sha256:032034c5e27b1f6e00730b9e7c2dbc8203b4700d0c681fd019d6defcf61183ec", size = 51353370, upload-time = "2025-11-26T14:01:04.586Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/38/aec1f450ae5c4796de8345442f297fcf8912c7d2e00a66d3236ff0f825ed/tree_sitter_language_pack-0.13.0-cp310-abi3-macosx_10_15_universal2.whl", hash = "sha256:0e7eae812b40a2dc8a12eb2f5c55e130eb892706a0bee06215dd76affeb00d07", size = 32991857, upload-time = "2025-11-26T14:00:51.459Z" }, - { url = "https://files.pythonhosted.org/packages/90/09/11f51c59ede786dccddd2d348d5d24a1d99c54117d00f88b477f5fae4bd5/tree_sitter_language_pack-0.13.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:7fdacf383418a845b20772118fcb53ad245f9c5d409bd07dae16acec65151756", size = 20092989, upload-time = "2025-11-26T14:00:54.202Z" }, - { url = "https://files.pythonhosted.org/packages/72/9d/644db031047ab1a70fc5cb6a79a4d4067080fac628375b2320752d2d7b58/tree_sitter_language_pack-0.13.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:0d4f261fce387ae040dae7e4d1c1aca63d84c88320afcc0961c123bec0be8377", size = 19952029, upload-time = "2025-11-26T14:00:56.699Z" }, - { url = "https://files.pythonhosted.org/packages/48/92/5fd749bbb3f5e4538492c77de7bc51a5e479fec6209464ddc25be9153b13/tree_sitter_language_pack-0.13.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:78f369dc4d456c5b08d659939e662c2f9b9fba8c0ec5538a1f973e01edfcf04d", size = 19944614, upload-time = "2025-11-26T14:00:59.381Z" }, - { url = "https://files.pythonhosted.org/packages/97/59/2287f07723c063475d6657babed0d5569f4b499e393ab51354d529c3e7b5/tree_sitter_language_pack-0.13.0-cp310-abi3-win_amd64.whl", hash = "sha256:1cdbc88a03dacd47bec69e56cc20c48eace1fbb6f01371e89c3ee6a2e8f34db1", size = 16896852, upload-time = "2025-11-26T14:01:01.788Z" }, -] - -[[package]] -name = "tree-sitter-yaml" -version = "0.7.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/b6/941d356ac70c90b9d2927375259e3a4204f38f7499ec6e7e8a95b9664689/tree_sitter_yaml-0.7.2.tar.gz", hash = "sha256:756db4c09c9d9e97c81699e8f941cb8ce4e51104927f6090eefe638ee567d32c", size = 84882, upload-time = "2025-10-07T14:40:36.071Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/29/c0b8dbff302c49ff4284666ffb6f2f21145006843bb4c3a9a85d0ec0b7ae/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:7e269ddcfcab8edb14fbb1f1d34eed1e1e26888f78f94eedfe7cc98c60f8bc9f", size = 43898, upload-time = "2025-10-07T14:40:29.486Z" }, - { url = "https://files.pythonhosted.org/packages/18/0d/15a5add06b3932b5e4ce5f5e8e179197097decfe82a0ef000952c8b98216/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:0807b7966e23ddf7dddc4545216e28b5a58cdadedcecca86b8d8c74271a07870", size = 44691, upload-time = "2025-10-07T14:40:30.369Z" }, - { url = "https://files.pythonhosted.org/packages/72/92/c4b896c90d08deb8308fadbad2210fdcc4c66c44ab4292eac4e80acb4b61/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1a5c60c98b6c4c037aae023569f020d0c489fad8dc26fdfd5510363c9c29a41", size = 91430, upload-time = "2025-10-07T14:40:31.16Z" }, - { url = "https://files.pythonhosted.org/packages/89/59/61f1fed31eb6d46ff080b8c0d53658cf29e10263f41ef5fe34768908037a/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88636d19d0654fd24f4f242eaaafa90f6f5ebdba8a62e4b32d251ed156c51a2a", size = 92428, upload-time = "2025-10-07T14:40:31.954Z" }, - { url = "https://files.pythonhosted.org/packages/e3/62/a33a04d19b7f9a0ded780b9c9fcc6279e37c5d00b89b00425bb807a22cc2/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1d2e8f0bb14aa4537320952d0f9607eef3021d5aada8383c34ebeece17db1e06", size = 90580, upload-time = "2025-10-07T14:40:33.037Z" }, - { url = "https://files.pythonhosted.org/packages/6c/e7/9525defa7b30792623f56b1fba9bbba361752348875b165b8975b87398fd/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:74ca712c50fc9d7dbc68cb36b4a7811d6e67a5466b5a789f19bf8dd6084ef752", size = 90455, upload-time = "2025-10-07T14:40:33.778Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d6/8d1e1ace03db3b02e64e91daf21d1347941d1bbecc606a5473a1a605250d/tree_sitter_yaml-0.7.2-cp310-abi3-win_amd64.whl", hash = "sha256:7587b5ca00fc4f9a548eff649697a3b395370b2304b399ceefa2087d8a6c9186", size = 45514, upload-time = "2025-10-07T14:40:34.562Z" }, - { url = "https://files.pythonhosted.org/packages/d8/c7/dcf3ea1c4f5da9b10353b9af4455d756c92d728a8f58f03c480d3ef0ead5/tree_sitter_yaml-0.7.2-cp310-abi3-win_arm64.whl", hash = "sha256:f63c227b18e7ce7587bce124578f0bbf1f890ac63d3e3cd027417574273642c4", size = 44065, upload-time = "2025-10-07T14:40:35.337Z" }, -] - -[[package]] -name = "triton" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850, upload-time = "2026-01-20T16:00:43.041Z" }, - { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450, upload-time = "2026-01-20T16:00:49.136Z" }, - { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296, upload-time = "2026-01-20T16:00:56.042Z" }, - { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" }, - { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" }, -] - -[[package]] -name = "twine" -version = "6.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "id" }, - { name = "keyring", marker = "platform_machine != 'ppc64le' and platform_machine != 's390x'" }, - { name = "packaging" }, - { name = "readme-renderer" }, - { name = "requests" }, - { name = "requests-toolbelt" }, - { name = "rfc3986" }, - { name = "rich" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e0/a8/949edebe3a82774c1ec34f637f5dd82d1cf22c25e963b7d63771083bbee5/twine-6.2.0.tar.gz", hash = "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf", size = 172262, upload-time = "2025-09-04T15:43:17.255Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/7a/882d99539b19b1490cac5d77c67338d126e4122c8276bf640e411650c830/twine-6.2.0-py3-none-any.whl", hash = "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8", size = 42727, upload-time = "2025-09-04T15:43:15.994Z" }, -] - -[[package]] -name = "typer" -version = "0.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, -] - -[[package]] -name = "typer-slim" -version = "0.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/17/d4/064570dec6358aa9049d4708e4a10407d74c99258f8b2136bb8702303f1a/typer_slim-0.21.1.tar.gz", hash = "sha256:73495dd08c2d0940d611c5a8c04e91c2a0a98600cbd4ee19192255a233b6dbfd", size = 110478, upload-time = "2026-01-06T11:21:11.176Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/0a/4aca634faf693e33004796b6cee0ae2e1dba375a800c16ab8d3eff4bb800/typer_slim-0.21.1-py3-none-any.whl", hash = "sha256:6e6c31047f171ac93cc5a973c9e617dbc5ab2bddc4d0a3135dc161b4e2020e0d", size = 47444, upload-time = "2026-01-06T11:21:12.441Z" }, -] - -[[package]] -name = "types-aiofiles" -version = "25.1.0.20251011" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/84/6c/6d23908a8217e36704aa9c79d99a620f2fdd388b66a4b7f72fbc6b6ff6c6/types_aiofiles-25.1.0.20251011.tar.gz", hash = "sha256:1c2b8ab260cb3cd40c15f9d10efdc05a6e1e6b02899304d80dfa0410e028d3ff", size = 14535, upload-time = "2025-10-11T02:44:51.237Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/0f/76917bab27e270bb6c32addd5968d69e558e5b6f7fb4ac4cbfa282996a96/types_aiofiles-25.1.0.20251011-py3-none-any.whl", hash = "sha256:8ff8de7f9d42739d8f0dadcceeb781ce27cd8d8c4152d4a7c52f6b20edb8149c", size = 14338, upload-time = "2025-10-11T02:44:50.054Z" }, -] - -[[package]] -name = "types-psutil" -version = "7.2.2.20260130" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/14/fc5fb0a6ddfadf68c27e254a02ececd4d5c7fdb0efcb7e7e917a183497fb/types_psutil-7.2.2.20260130.tar.gz", hash = "sha256:15b0ab69c52841cf9ce3c383e8480c620a4d13d6a8e22b16978ebddac5590950", size = 26535, upload-time = "2026-01-30T03:58:14.116Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/d7/60974b7e31545d3768d1770c5fe6e093182c3bfd819429b33133ba6b3e89/types_psutil-7.2.2.20260130-py3-none-any.whl", hash = "sha256:15523a3caa7b3ff03ac7f9b78a6470a59f88f48df1d74a39e70e06d2a99107da", size = 32876, upload-time = "2026-01-30T03:58:13.172Z" }, -] - -[[package]] -name = "types-setuptools" -version = "80.10.0.20260124" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/7e/116539b9610585e34771611e33c88a4c706491fa3565500f5a63139f8731/types_setuptools-80.10.0.20260124.tar.gz", hash = "sha256:1b86d9f0368858663276a0cbe5fe5a9722caf94b5acde8aba0399a6e90680f20", size = 43299, upload-time = "2026-01-24T03:18:39.527Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/7f/016dc5cc718ec6ccaa84fb73ed409ef1c261793fd5e637cdfaa18beb40a9/types_setuptools-80.10.0.20260124-py3-none-any.whl", hash = "sha256:efed7e044f01adb9c2806c7a8e1b6aa3656b8e382379b53d5f26ee3db24d4c01", size = 64333, upload-time = "2026-01-24T03:18:38.344Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "ujson" -version = "5.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/d9/3f17e3c5773fb4941c68d9a37a47b1a79c9649d6c56aefbed87cc409d18a/ujson-5.11.0.tar.gz", hash = "sha256:e204ae6f909f099ba6b6b942131cee359ddda2b6e4ea39c12eb8b991fe2010e0", size = 7156583, upload-time = "2025-08-20T11:57:02.452Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/ef/a9cb1fce38f699123ff012161599fb9f2ff3f8d482b4b18c43a2dc35073f/ujson-5.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7895f0d2d53bd6aea11743bd56e3cb82d729980636cd0ed9b89418bf66591702", size = 55434, upload-time = "2025-08-20T11:55:34.987Z" }, - { url = "https://files.pythonhosted.org/packages/b1/05/dba51a00eb30bd947791b173766cbed3492269c150a7771d2750000c965f/ujson-5.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12b5e7e22a1fe01058000d1b317d3b65cc3daf61bd2ea7a2b76721fe160fa74d", size = 53190, upload-time = "2025-08-20T11:55:36.384Z" }, - { url = "https://files.pythonhosted.org/packages/03/3c/fd11a224f73fbffa299fb9644e425f38b38b30231f7923a088dd513aabb4/ujson-5.11.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0180a480a7d099082501cad1fe85252e4d4bf926b40960fb3d9e87a3a6fbbc80", size = 57600, upload-time = "2025-08-20T11:55:37.692Z" }, - { url = "https://files.pythonhosted.org/packages/55/b9/405103cae24899df688a3431c776e00528bd4799e7d68820e7ebcf824f92/ujson-5.11.0-cp312-cp312-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:fa79fdb47701942c2132a9dd2297a1a85941d966d8c87bfd9e29b0cf423f26cc", size = 59791, upload-time = "2025-08-20T11:55:38.877Z" }, - { url = "https://files.pythonhosted.org/packages/17/7b/2dcbc2bbfdbf68f2368fb21ab0f6735e872290bb604c75f6e06b81edcb3f/ujson-5.11.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8254e858437c00f17cb72e7a644fc42dad0ebb21ea981b71df6e84b1072aaa7c", size = 57356, upload-time = "2025-08-20T11:55:40.036Z" }, - { url = "https://files.pythonhosted.org/packages/d1/71/fea2ca18986a366c750767b694430d5ded6b20b6985fddca72f74af38a4c/ujson-5.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1aa8a2ab482f09f6c10fba37112af5f957689a79ea598399c85009f2f29898b5", size = 1036313, upload-time = "2025-08-20T11:55:41.408Z" }, - { url = "https://files.pythonhosted.org/packages/a3/bb/d4220bd7532eac6288d8115db51710fa2d7d271250797b0bfba9f1e755af/ujson-5.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a638425d3c6eed0318df663df44480f4a40dc87cc7c6da44d221418312f6413b", size = 1195782, upload-time = "2025-08-20T11:55:43.357Z" }, - { url = "https://files.pythonhosted.org/packages/80/47/226e540aa38878ce1194454385701d82df538ccb5ff8db2cf1641dde849a/ujson-5.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7e3cff632c1d78023b15f7e3a81c3745cd3f94c044d1e8fa8efbd6b161997bbc", size = 1088817, upload-time = "2025-08-20T11:55:45.262Z" }, - { url = "https://files.pythonhosted.org/packages/7e/81/546042f0b23c9040d61d46ea5ca76f0cc5e0d399180ddfb2ae976ebff5b5/ujson-5.11.0-cp312-cp312-win32.whl", hash = "sha256:be6b0eaf92cae8cdee4d4c9e074bde43ef1c590ed5ba037ea26c9632fb479c88", size = 39757, upload-time = "2025-08-20T11:55:46.522Z" }, - { url = "https://files.pythonhosted.org/packages/44/1b/27c05dc8c9728f44875d74b5bfa948ce91f6c33349232619279f35c6e817/ujson-5.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:b7b136cc6abc7619124fd897ef75f8e63105298b5ca9bdf43ebd0e1fa0ee105f", size = 43859, upload-time = "2025-08-20T11:55:47.987Z" }, - { url = "https://files.pythonhosted.org/packages/22/2d/37b6557c97c3409c202c838aa9c960ca3896843b4295c4b7bb2bbd260664/ujson-5.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:6cd2df62f24c506a0ba322d5e4fe4466d47a9467b57e881ee15a31f7ecf68ff6", size = 38361, upload-time = "2025-08-20T11:55:49.122Z" }, - { url = "https://files.pythonhosted.org/packages/1c/ec/2de9dd371d52c377abc05d2b725645326c4562fc87296a8907c7bcdf2db7/ujson-5.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:109f59885041b14ee9569bf0bb3f98579c3fa0652317b355669939e5fc5ede53", size = 55435, upload-time = "2025-08-20T11:55:50.243Z" }, - { url = "https://files.pythonhosted.org/packages/5b/a4/f611f816eac3a581d8a4372f6967c3ed41eddbae4008d1d77f223f1a4e0a/ujson-5.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a31c6b8004438e8c20fc55ac1c0e07dad42941db24176fe9acf2815971f8e752", size = 53193, upload-time = "2025-08-20T11:55:51.373Z" }, - { url = "https://files.pythonhosted.org/packages/e9/c5/c161940967184de96f5cbbbcce45b562a4bf851d60f4c677704b1770136d/ujson-5.11.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78c684fb21255b9b90320ba7e199780f653e03f6c2528663768965f4126a5b50", size = 57603, upload-time = "2025-08-20T11:55:52.583Z" }, - { url = "https://files.pythonhosted.org/packages/2b/d6/c7b2444238f5b2e2d0e3dab300b9ddc3606e4b1f0e4bed5a48157cebc792/ujson-5.11.0-cp313-cp313-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:4c9f5d6a27d035dd90a146f7761c2272cf7103de5127c9ab9c4cd39ea61e878a", size = 59794, upload-time = "2025-08-20T11:55:53.69Z" }, - { url = "https://files.pythonhosted.org/packages/fe/a3/292551f936d3d02d9af148f53e1bc04306b00a7cf1fcbb86fa0d1c887242/ujson-5.11.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:837da4d27fed5fdc1b630bd18f519744b23a0b5ada1bbde1a36ba463f2900c03", size = 57363, upload-time = "2025-08-20T11:55:54.843Z" }, - { url = "https://files.pythonhosted.org/packages/90/a6/82cfa70448831b1a9e73f882225980b5c689bf539ec6400b31656a60ea46/ujson-5.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:787aff4a84da301b7f3bac09bc696e2e5670df829c6f8ecf39916b4e7e24e701", size = 1036311, upload-time = "2025-08-20T11:55:56.197Z" }, - { url = "https://files.pythonhosted.org/packages/84/5c/96e2266be50f21e9b27acaee8ca8f23ea0b85cb998c33d4f53147687839b/ujson-5.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6dd703c3e86dc6f7044c5ac0b3ae079ed96bf297974598116aa5fb7f655c3a60", size = 1195783, upload-time = "2025-08-20T11:55:58.081Z" }, - { url = "https://files.pythonhosted.org/packages/8d/20/78abe3d808cf3bb3e76f71fca46cd208317bf461c905d79f0d26b9df20f1/ujson-5.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3772e4fe6b0c1e025ba3c50841a0ca4786825a4894c8411bf8d3afe3a8061328", size = 1088822, upload-time = "2025-08-20T11:55:59.469Z" }, - { url = "https://files.pythonhosted.org/packages/d8/50/8856e24bec5e2fc7f775d867aeb7a3f137359356200ac44658f1f2c834b2/ujson-5.11.0-cp313-cp313-win32.whl", hash = "sha256:8fa2af7c1459204b7a42e98263b069bd535ea0cd978b4d6982f35af5a04a4241", size = 39753, upload-time = "2025-08-20T11:56:01.345Z" }, - { url = "https://files.pythonhosted.org/packages/5b/d8/1baee0f4179a4d0f5ce086832147b6cc9b7731c24ca08e14a3fdb8d39c32/ujson-5.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:34032aeca4510a7c7102bd5933f59a37f63891f30a0706fb46487ab6f0edf8f0", size = 43866, upload-time = "2025-08-20T11:56:02.552Z" }, - { url = "https://files.pythonhosted.org/packages/a9/8c/6d85ef5be82c6d66adced3ec5ef23353ed710a11f70b0b6a836878396334/ujson-5.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:ce076f2df2e1aa62b685086fbad67f2b1d3048369664b4cdccc50707325401f9", size = 38363, upload-time = "2025-08-20T11:56:03.688Z" }, - { url = "https://files.pythonhosted.org/packages/28/08/4518146f4984d112764b1dfa6fb7bad691c44a401adadaa5e23ccd930053/ujson-5.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:65724738c73645db88f70ba1f2e6fb678f913281804d5da2fd02c8c5839af302", size = 55462, upload-time = "2025-08-20T11:56:04.873Z" }, - { url = "https://files.pythonhosted.org/packages/29/37/2107b9a62168867a692654d8766b81bd2fd1e1ba13e2ec90555861e02b0c/ujson-5.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29113c003ca33ab71b1b480bde952fbab2a0b6b03a4ee4c3d71687cdcbd1a29d", size = 53246, upload-time = "2025-08-20T11:56:06.054Z" }, - { url = "https://files.pythonhosted.org/packages/9b/f8/25583c70f83788edbe3ca62ce6c1b79eff465d78dec5eb2b2b56b3e98b33/ujson-5.11.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c44c703842024d796b4c78542a6fcd5c3cb948b9fc2a73ee65b9c86a22ee3638", size = 57631, upload-time = "2025-08-20T11:56:07.374Z" }, - { url = "https://files.pythonhosted.org/packages/ed/ca/19b3a632933a09d696f10dc1b0dfa1d692e65ad507d12340116ce4f67967/ujson-5.11.0-cp314-cp314-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:e750c436fb90edf85585f5c62a35b35082502383840962c6983403d1bd96a02c", size = 59877, upload-time = "2025-08-20T11:56:08.534Z" }, - { url = "https://files.pythonhosted.org/packages/55/7a/4572af5324ad4b2bfdd2321e898a527050290147b4ea337a79a0e4e87ec7/ujson-5.11.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f278b31a7c52eb0947b2db55a5133fbc46b6f0ef49972cd1a80843b72e135aba", size = 57363, upload-time = "2025-08-20T11:56:09.758Z" }, - { url = "https://files.pythonhosted.org/packages/7b/71/a2b8c19cf4e1efe53cf439cdf7198ac60ae15471d2f1040b490c1f0f831f/ujson-5.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ab2cb8351d976e788669c8281465d44d4e94413718af497b4e7342d7b2f78018", size = 1036394, upload-time = "2025-08-20T11:56:11.168Z" }, - { url = "https://files.pythonhosted.org/packages/7a/3e/7b98668cba3bb3735929c31b999b374ebc02c19dfa98dfebaeeb5c8597ca/ujson-5.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:090b4d11b380ae25453100b722d0609d5051ffe98f80ec52853ccf8249dfd840", size = 1195837, upload-time = "2025-08-20T11:56:12.6Z" }, - { url = "https://files.pythonhosted.org/packages/a1/ea/8870f208c20b43571a5c409ebb2fe9b9dba5f494e9e60f9314ac01ea8f78/ujson-5.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:80017e870d882d5517d28995b62e4e518a894f932f1e242cbc802a2fd64d365c", size = 1088837, upload-time = "2025-08-20T11:56:14.15Z" }, - { url = "https://files.pythonhosted.org/packages/63/b6/c0e6607e37fa47929920a685a968c6b990a802dec65e9c5181e97845985d/ujson-5.11.0-cp314-cp314-win32.whl", hash = "sha256:1d663b96eb34c93392e9caae19c099ec4133ba21654b081956613327f0e973ac", size = 41022, upload-time = "2025-08-20T11:56:15.509Z" }, - { url = "https://files.pythonhosted.org/packages/4e/56/f4fe86b4c9000affd63e9219e59b222dc48b01c534533093e798bf617a7e/ujson-5.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:849e65b696f0d242833f1df4182096cedc50d414215d1371fca85c541fbff629", size = 45111, upload-time = "2025-08-20T11:56:16.597Z" }, - { url = "https://files.pythonhosted.org/packages/0a/f3/669437f0280308db4783b12a6d88c00730b394327d8334cc7a32ef218e64/ujson-5.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:e73df8648c9470af2b6a6bf5250d4744ad2cf3d774dcf8c6e31f018bdd04d764", size = 39682, upload-time = "2025-08-20T11:56:17.763Z" }, - { url = "https://files.pythonhosted.org/packages/6e/cd/e9809b064a89fe5c4184649adeb13c1b98652db3f8518980b04227358574/ujson-5.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:de6e88f62796372fba1de973c11138f197d3e0e1d80bcb2b8aae1e826096d433", size = 55759, upload-time = "2025-08-20T11:56:18.882Z" }, - { url = "https://files.pythonhosted.org/packages/1b/be/ae26a6321179ebbb3a2e2685b9007c71bcda41ad7a77bbbe164005e956fc/ujson-5.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:49e56ef8066f11b80d620985ae36869a3ff7e4b74c3b6129182ec5d1df0255f3", size = 53634, upload-time = "2025-08-20T11:56:20.012Z" }, - { url = "https://files.pythonhosted.org/packages/ae/e9/fb4a220ee6939db099f4cfeeae796ecb91e7584ad4d445d4ca7f994a9135/ujson-5.11.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1a325fd2c3a056cf6c8e023f74a0c478dd282a93141356ae7f16d5309f5ff823", size = 58547, upload-time = "2025-08-20T11:56:21.175Z" }, - { url = "https://files.pythonhosted.org/packages/bd/f8/fc4b952b8f5fea09ea3397a0bd0ad019e474b204cabcb947cead5d4d1ffc/ujson-5.11.0-cp314-cp314t-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:a0af6574fc1d9d53f4ff371f58c96673e6d988ed2b5bf666a6143c782fa007e9", size = 60489, upload-time = "2025-08-20T11:56:22.342Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e5/af5491dfda4f8b77e24cf3da68ee0d1552f99a13e5c622f4cef1380925c3/ujson-5.11.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10f29e71ecf4ecd93a6610bd8efa8e7b6467454a363c3d6416db65de883eb076", size = 58035, upload-time = "2025-08-20T11:56:23.92Z" }, - { url = "https://files.pythonhosted.org/packages/c4/09/0945349dd41f25cc8c38d78ace49f14c5052c5bbb7257d2f466fa7bdb533/ujson-5.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1a0a9b76a89827a592656fe12e000cf4f12da9692f51a841a4a07aa4c7ecc41c", size = 1037212, upload-time = "2025-08-20T11:56:25.274Z" }, - { url = "https://files.pythonhosted.org/packages/49/44/8e04496acb3d5a1cbee3a54828d9652f67a37523efa3d3b18a347339680a/ujson-5.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b16930f6a0753cdc7d637b33b4e8f10d5e351e1fb83872ba6375f1e87be39746", size = 1196500, upload-time = "2025-08-20T11:56:27.517Z" }, - { url = "https://files.pythonhosted.org/packages/64/ae/4bc825860d679a0f208a19af2f39206dfd804ace2403330fdc3170334a2f/ujson-5.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:04c41afc195fd477a59db3a84d5b83a871bd648ef371cf8c6f43072d89144eef", size = 1089487, upload-time = "2025-08-20T11:56:29.07Z" }, - { url = "https://files.pythonhosted.org/packages/30/ed/5a057199fb0a5deabe0957073a1c1c1c02a3e99476cd03daee98ea21fa57/ujson-5.11.0-cp314-cp314t-win32.whl", hash = "sha256:aa6d7a5e09217ff93234e050e3e380da62b084e26b9f2e277d2606406a2fc2e5", size = 41859, upload-time = "2025-08-20T11:56:30.495Z" }, - { url = "https://files.pythonhosted.org/packages/aa/03/b19c6176bdf1dc13ed84b886e99677a52764861b6cc023d5e7b6ebda249d/ujson-5.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:48055e1061c1bb1f79e75b4ac39e821f3f35a9b82de17fce92c3140149009bec", size = 46183, upload-time = "2025-08-20T11:56:31.574Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ca/a0413a3874b2dc1708b8796ca895bf363292f9c70b2e8ca482b7dbc0259d/ujson-5.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:1194b943e951092db611011cb8dbdb6cf94a3b816ed07906e14d3bc6ce0e90ab", size = 40264, upload-time = "2025-08-20T11:56:32.773Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.40.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, -] - -[package.optional-dependencies] -standard = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "httptools" }, - { name = "python-dotenv" }, - { name = "pyyaml" }, - { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, - { name = "watchfiles" }, - { name = "websockets" }, -] - -[[package]] -name = "uvloop" -version = "0.22.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" }, - { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" }, - { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" }, - { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" }, - { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" }, - { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" }, - { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, - { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, - { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, - { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, - { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, - { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, - { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, - { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, - { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, - { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, - { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, - { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, - { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, - { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, - { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, - { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, -] - -[[package]] -name = "watchdog" -version = "6.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, - { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, - { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, - { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, - { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, -] - -[[package]] -name = "watchfiles" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" }, - { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" }, - { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" }, - { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" }, - { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" }, - { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" }, - { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" }, - { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" }, - { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" }, - { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" }, - { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, - { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, - { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, - { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, - { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, - { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, - { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, - { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, - { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, - { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, - { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, - { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, - { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, - { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, - { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, - { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, - { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, - { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, - { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, - { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, - { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, - { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, - { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, - { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, - { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, - { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, - { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, - { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, - { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, - { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, - { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, -] - -[[package]] -name = "wcwidth" -version = "0.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c2/62/a7c072fbfefb2980a00f99ca994279cb9ecf310cb2e6b2a4d2a28fe192b3/wcwidth-0.5.3.tar.gz", hash = "sha256:53123b7af053c74e9fe2e92ac810301f6139e64379031f7124574212fb3b4091", size = 157587, upload-time = "2026-01-31T03:52:10.92Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/c1/d73f12f8cdb1891334a2ccf7389eed244d3941e74d80dd220badb937f3fb/wcwidth-0.5.3-py3-none-any.whl", hash = "sha256:d584eff31cd4753e1e5ff6c12e1edfdb324c995713f75d26c29807bb84bf649e", size = 92981, upload-time = "2026-01-31T03:52:09.14Z" }, -] - -[[package]] -name = "websocket-client" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576, upload-time = "2025-10-07T21:16:36.495Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" }, -] - -[[package]] -name = "websockets" -version = "16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, - { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, - { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, - { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, - { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, - { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, - { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, - { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, - { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, - { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, -] - -[[package]] -name = "win32-setctime" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, -] - -[[package]] -name = "wrapt" -version = "1.17.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998, upload-time = "2025-08-12T05:51:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020, upload-time = "2025-08-12T05:51:35.906Z" }, - { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098, upload-time = "2025-08-12T05:51:57.474Z" }, - { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036, upload-time = "2025-08-12T05:52:34.784Z" }, - { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156, upload-time = "2025-08-12T05:52:13.599Z" }, - { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102, upload-time = "2025-08-12T05:52:14.56Z" }, - { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732, upload-time = "2025-08-12T05:52:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705, upload-time = "2025-08-12T05:53:07.123Z" }, - { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877, upload-time = "2025-08-12T05:53:05.436Z" }, - { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885, upload-time = "2025-08-12T05:52:54.367Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, - { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, - { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, - { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, - { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, - { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, - { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, - { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, - { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, - { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, - { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, - { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, - { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, - { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, - { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, - { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, - { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, - { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, - { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, -] - -[[package]] -name = "yarl" -version = "1.22.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "multidict" }, - { name = "propcache" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" }, - { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" }, - { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" }, - { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" }, - { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" }, - { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" }, - { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" }, - { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" }, - { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" }, - { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" }, - { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" }, - { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" }, - { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" }, - { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" }, - { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" }, - { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" }, - { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" }, - { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" }, - { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" }, - { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" }, - { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" }, - { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" }, - { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" }, - { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" }, - { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" }, - { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" }, - { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" }, - { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" }, - { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" }, - { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" }, - { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" }, - { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" }, - { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" }, - { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" }, - { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" }, - { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" }, - { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" }, - { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" }, - { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" }, - { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" }, - { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" }, - { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" }, - { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" }, - { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" }, - { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" }, - { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" }, - { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" }, - { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" }, - { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" }, - { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" }, - { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" }, - { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" }, - { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" }, - { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" }, - { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" }, - { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" }, - { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" }, - { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" }, - { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" }, - { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" }, - { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" }, - { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" }, - { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" }, - { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" }, - { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" }, - { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" }, - { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" }, - { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" }, - { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" }, - { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" }, - { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" }, - { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, - { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, - { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, - { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, -] - -[[package]] -name = "zap-protocol" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zap-schema" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/21/d2/c21faa12aaba9f9e74a07d71a8f87b468deb150f7f0a1601c9fda0aae58d/zap_protocol-1.0.0.tar.gz", hash = "sha256:1f9a30226f15aef5777ff2ab96523e8ece43a6f88bbb230e313b86775d322aea", size = 1513, upload-time = "2026-01-26T19:19:59.934Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/71/bad51bd4ba2fb0ee5d3f6ed504231697d45324ad0abc2633cf75c3894295/zap_protocol-1.0.0-py3-none-any.whl", hash = "sha256:b33bed8c5c42d15f6f42e74ab3a6f7a5f18471b48292a395ea9c3bb2fedb24fe", size = 1816, upload-time = "2026-01-26T19:19:58.599Z" }, -] - -[[package]] -name = "zap-schema" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "pycapnp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7a/f9/54d8d2df784cd7a1b06fdecc5805ea33e06d216df01a352557c2fdf9301b/zap_schema-1.0.0.tar.gz", hash = "sha256:451b1ce05b6ae9c1b016e86fdbc04d62a78a0d720a970a7e61b0579954fb687d", size = 57177, upload-time = "2026-01-26T19:19:36.951Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/4f/db0331ed6c785d37b6965d43eec8fc49e06f9b4033ea8e1a144ad3c5e8de/zap_schema-1.0.0-py3-none-any.whl", hash = "sha256:3102e813fc02d12ef2dc6379bc4c79baf1f752939d146a9bae1c6b125ada62b7", size = 18789, upload-time = "2026-01-26T19:19:35.685Z" }, -] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] diff --git a/pkg/hanzo-mcp/vscode-extension/package-lock.json b/pkg/hanzo-mcp/vscode-extension/package-lock.json deleted file mode 100644 index e2b2cb3fc..000000000 --- a/pkg/hanzo-mcp/vscode-extension/package-lock.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "name": "hanzo-mcp", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "hanzo-mcp", - "version": "1.0.0", - "dependencies": { - "node-pty": "^0.10.1", - "ws": "^8.14.2" - }, - "devDependencies": { - "@types/node": "16.x", - "@types/vscode": "^1.74.0", - "typescript": "^4.9.4" - }, - "engines": { - "vscode": "^1.74.0" - } - }, - "node_modules/@types/node": { - "version": "16.18.126", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.126.tgz", - "integrity": "sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/vscode": { - "version": "1.108.1", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.108.1.tgz", - "integrity": "sha512-DerV0BbSzt87TbrqmZ7lRDIYaMiqvP8tmJTzW2p49ZBVtGUnGAu2RGQd1Wv4XMzEVUpaHbsemVM5nfuQJj7H6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/nan": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", - "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", - "license": "MIT" - }, - "node_modules/node-pty": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-0.10.1.tgz", - "integrity": "sha512-JTdtUS0Im/yRsWJSx7yiW9rtpfmxqxolrtnyKwPLI+6XqTAPW/O2MjS8FYL4I5TsMbH2lVgDb2VMjp+9LoQGNg==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "nan": "^2.14.0" - } - }, - "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - } - } -} diff --git a/pkg/hanzo-mcp/vscode-extension/package.json b/pkg/hanzo-mcp/vscode-extension/package.json deleted file mode 100644 index ef4cec4d1..000000000 --- a/pkg/hanzo-mcp/vscode-extension/package.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "name": "hanzo-mcp", - "displayName": "Hanzo MCP", - "description": "AI intelligent workspace with unified development tools", - "version": "1.0.0", - "publisher": "hanzoai", - "engines": { - "vscode": "^1.74.0" - }, - "categories": [ - "Other", - "AI", - "Formatters", - "Linters" - ], - "keywords": [ - "ai", - "mcp", - "development", - "tools", - "hanzo", - "lsp", - "refactoring", - "workspace" - ], - "activationEvents": [ - "*" - ], - "main": "./out/extension.js", - "contributes": { - "commands": [ - { - "command": "hanzo-mcp.edit", - "title": "Hanzo: Edit (semantic refactor)" - }, - { - "command": "hanzo-mcp.fmt", - "title": "Hanzo: Format" - }, - { - "command": "hanzo-mcp.test", - "title": "Hanzo: Test" - }, - { - "command": "hanzo-mcp.build", - "title": "Hanzo: Build" - }, - { - "command": "hanzo-mcp.lint", - "title": "Hanzo: Lint" - }, - { - "command": "hanzo-mcp.guard", - "title": "Hanzo: Guard (check boundaries)" - }, - { - "command": "hanzo-mcp.workspace.refactor", - "title": "Hanzo: Workspace Refactor" - }, - { - "command": "hanzo-mcp.sessions.view", - "title": "Hanzo: View Sessions" - }, - { - "command": "hanzo-mcp.codebase.index", - "title": "Hanzo: Index Codebase" - } - ], - "keybindings": [ - { - "command": "hanzo-mcp.fmt", - "key": "ctrl+alt+f", - "mac": "cmd+alt+f", - "when": "editorTextFocus" - }, - { - "command": "hanzo-mcp.test", - "key": "ctrl+alt+t", - "mac": "cmd+alt+t" - }, - { - "command": "hanzo-mcp.build", - "key": "ctrl+alt+b", - "mac": "cmd+alt+b" - }, - { - "command": "hanzo-mcp.lint", - "key": "ctrl+alt+l", - "mac": "cmd+alt+l", - "when": "editorTextFocus" - } - ], - "configuration": { - "type": "object", - "title": "Hanzo MCP Configuration", - "properties": { - "hanzo-mcp.sessionTracking": { - "type": "boolean", - "default": true, - "description": "Enable session tracking to ~/.hanzo/sessions" - }, - "hanzo-mcp.codebaseIndexing": { - "type": "boolean", - "default": true, - "description": "Enable automatic codebase indexing" - }, - "hanzo-mcp.workspaceDetection": { - "type": "string", - "enum": ["auto", "go.work", "package.json", "pyproject.toml", "Cargo.toml"], - "default": "auto", - "description": "Workspace detection method" - }, - "hanzo-mcp.defaultLanguage": { - "type": "string", - "enum": ["auto", "go", "ts", "py", "rs", "cc", "sol", "schema"], - "default": "auto", - "description": "Default language for tools" - }, - "hanzo-mcp.goLocalPrefix": { - "type": "string", - "default": "github.com/luxfi", - "description": "Go import local prefix for formatting" - } - } - }, - "views": { - "explorer": [ - { - "id": "hanzo-mcp-sessions", - "name": "Hanzo Sessions", - "when": "hanzo-mcp.sessionTracking" - }, - { - "id": "hanzo-mcp-codebase", - "name": "Codebase Intelligence", - "when": "hanzo-mcp.codebaseIndexing" - } - ] - } - }, - "scripts": { - "vscode:prepublish": "npm run compile", - "compile": "tsc -p ./", - "watch": "tsc -watch -p ./" - }, - "devDependencies": { - "@types/vscode": "^1.74.0", - "@types/node": "16.x", - "typescript": "^4.9.4" - }, - "dependencies": { - "node-pty": "^0.10.1", - "ws": "^8.14.2" - } -} \ No newline at end of file diff --git a/pkg/hanzo-mcp/vscode-extension/src/extension.ts b/pkg/hanzo-mcp/vscode-extension/src/extension.ts deleted file mode 100644 index 5a75da518..000000000 --- a/pkg/hanzo-mcp/vscode-extension/src/extension.ts +++ /dev/null @@ -1,395 +0,0 @@ -import * as vscode from 'vscode'; -import * as path from 'path'; -import * as os from 'os'; -import { spawn, ChildProcess } from 'child_process'; -import { WebSocketServer, WebSocket } from 'ws'; - -interface HanzoSession { - session_id: string; - start_time: string; - tool_count: number; - tools_used: string[]; -} - -class HanzoMCPProvider implements vscode.TreeDataProvider { - private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); - readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; - - private sessions: HanzoSession[] = []; - private mcpProcess: ChildProcess | null = null; - private wsServer: WebSocketServer | null = null; - - constructor(private context: vscode.ExtensionContext) { - this.startMCPBackend(); - this.refreshSessions(); - } - - refresh(): void { - this.refreshSessions(); - this._onDidChangeTreeData.fire(); - } - - getTreeItem(element: HanzoSession): vscode.TreeItem { - const item = new vscode.TreeItem( - `Session ${element.session_id.substring(0, 8)}`, - vscode.TreeItemCollapsibleState.None - ); - item.description = `${element.tool_count} tools, ${element.tools_used.join(', ')}`; - item.tooltip = `Started: ${element.start_time}`; - return item; - } - - getChildren(element?: HanzoSession): Thenable { - if (!element) { - return Promise.resolve(this.sessions); - } - return Promise.resolve([]); - } - - private startMCPBackend() { - const config = vscode.workspace.getConfiguration('hanzo-mcp'); - - // Start the unified MCP backend - this.mcpProcess = spawn('python', ['-m', 'hanzo_mcp.unified_backend'], { - stdio: ['pipe', 'pipe', 'pipe'], - env: { ...process.env, PYTHONPATH: process.env.PYTHONPATH } - }); - - this.mcpProcess.on('error', (err) => { - vscode.window.showErrorMessage(`Hanzo MCP backend error: ${err.message}`); - }); - - // Start WebSocket server for real-time communication - this.wsServer = new WebSocketServer({ port: 8765 }); - this.wsServer.on('connection', (ws: WebSocket) => { - ws.on('message', (data: Buffer) => { - const message = JSON.parse(data.toString()); - this.handleMCPMessage(message); - }); - }); - } - - private handleMCPMessage(message: any) { - if (message.type === 'tool_executed') { - this.refreshSessions(); - - // Show notification for important operations - if (message.data.tool === 'guard' && !message.data.result.ok) { - vscode.window.showWarningMessage( - `Guard violations found: ${message.data.result.violations.length} issues` - ); - } - } - } - - private async refreshSessions() { - try { - const result = await this.executeMCPCommand('get_sessions'); - this.sessions = result.sessions || []; - } catch (err) { - console.error('Failed to refresh sessions:', err); - } - } - - private async executeMCPCommand(command: string, args: any = {}): Promise { - return new Promise((resolve, reject) => { - if (!this.mcpProcess) { - reject(new Error('MCP backend not running')); - return; - } - - const request = JSON.stringify({ command, args, id: Date.now() }); - this.mcpProcess.stdin?.write(request + '\n'); - - const timeout = setTimeout(() => { - reject(new Error('Command timeout')); - }, 10000); - - const handler = (data: Buffer) => { - try { - const response = JSON.parse(data.toString()); - if (response.id === JSON.parse(request).id) { - clearTimeout(timeout); - this.mcpProcess?.stdout?.off('data', handler); - if (response.error) { - reject(new Error(response.error)); - } else { - resolve(response.result); - } - } - } catch (err) { - // Ignore parsing errors, might be partial data - } - }; - - this.mcpProcess.stdout?.on('data', handler); - }); - } - - async executeEdit(operation: string) { - const editor = vscode.window.activeTextEditor; - if (!editor) { - vscode.window.showErrorMessage('No active editor'); - return; - } - - const document = editor.document; - const position = editor.selection.active; - - try { - const result = await this.executeMCPCommand('edit', { - target: `file:${document.fileName}`, - op: operation, - file: document.fileName, - pos: { line: position.line, character: position.character } - }); - - if (result.touched_files?.length > 0) { - // Reload affected files - for (const file of result.touched_files) { - const uri = vscode.Uri.file(file); - const doc = await vscode.workspace.openTextDocument(uri); - await vscode.window.showTextDocument(doc); - } - vscode.window.showInformationMessage(`Edit completed: ${result.touched_files.length} files modified`); - } - } catch (err) { - vscode.window.showErrorMessage(`Edit failed: ${err}`); - } - } - - async executeFormat() { - const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - if (!workspaceFolder) { - vscode.window.showErrorMessage('No workspace folder'); - return; - } - - const config = vscode.workspace.getConfiguration('hanzo-mcp'); - const localPrefix = config.get('goLocalPrefix', 'github.com/luxfi'); - - try { - const result = await this.executeMCPCommand('fmt', { - target: 'changed', // Format only changed files - local_prefix: localPrefix - }); - - if (result.ok) { - vscode.window.showInformationMessage(`Formatted ${result.touched_files.length} files`); - } else { - vscode.window.showErrorMessage(`Format failed: ${result.errors.join(', ')}`); - } - } catch (err) { - vscode.window.showErrorMessage(`Format failed: ${err}`); - } - } - - async executeTest() { - const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - if (!workspaceFolder) { - vscode.window.showErrorMessage('No workspace folder'); - return; - } - - try { - const result = await this.executeMCPCommand('test', { - target: 'ws' // Test entire workspace - }); - - const output = vscode.window.createOutputChannel('Hanzo Test'); - output.appendLine(result.stdout); - if (result.stderr) { - output.appendLine('STDERR:'); - output.appendLine(result.stderr); - } - output.show(); - - if (result.ok) { - vscode.window.showInformationMessage('Tests passed'); - } else { - vscode.window.showErrorMessage(`Tests failed (exit code: ${result.exit_code})`); - } - } catch (err) { - vscode.window.showErrorMessage(`Test failed: ${err}`); - } - } - - async executeBuild() { - const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - if (!workspaceFolder) { - vscode.window.showErrorMessage('No workspace folder'); - return; - } - - try { - const result = await this.executeMCPCommand('build', { - target: 'ws' - }); - - const output = vscode.window.createOutputChannel('Hanzo Build'); - output.appendLine(result.stdout); - if (result.stderr) { - output.appendLine('STDERR:'); - output.appendLine(result.stderr); - } - output.show(); - - if (result.ok) { - vscode.window.showInformationMessage('Build succeeded'); - } else { - vscode.window.showErrorMessage(`Build failed (exit code: ${result.exit_code})`); - } - } catch (err) { - vscode.window.showErrorMessage(`Build failed: ${err}`); - } - } - - async executeLint(fix: boolean = false) { - const editor = vscode.window.activeTextEditor; - const target = editor ? `file:${editor.document.fileName}` : 'ws'; - - try { - const result = await this.executeMCPCommand('lint', { - target, - fix - }); - - if (result.ok) { - if (fix && result.touched_files.length > 0) { - vscode.window.showInformationMessage(`Lint fixes applied to ${result.touched_files.length} files`); - // Reload the files - for (const file of result.touched_files) { - const uri = vscode.Uri.file(file); - const doc = await vscode.workspace.openTextDocument(uri); - await vscode.window.showTextDocument(doc); - } - } else { - vscode.window.showInformationMessage('Lint check passed'); - } - } else { - vscode.window.showErrorMessage(`Lint failed: ${result.errors.join(', ')}`); - } - } catch (err) { - vscode.window.showErrorMessage(`Lint failed: ${err}`); - } - } - - async executeGuard() { - const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - if (!workspaceFolder) { - vscode.window.showErrorMessage('No workspace folder'); - return; - } - - try { - const result = await this.executeMCPCommand('guard', { - target: 'ws', - rules: [ - { - id: 'no_node_in_sdk', - type: 'import', - glob: 'sdk/**', - forbid_import_prefix: 'github.com/luxfi/node/' - }, - { - id: 'no_generated_edits', - type: 'generated', - glob: 'api/pb/**', - forbid_writes: true - } - ] - }); - - if (result.violations && result.violations.length > 0) { - const violationsText = result.violations.map((v: any) => - `${v.file}:${v.line} - ${v.rule_id}: ${v.text}` - ).join('\n'); - - const output = vscode.window.createOutputChannel('Hanzo Guard'); - output.appendLine('Guard Violations:'); - output.appendLine(violationsText); - output.show(); - - vscode.window.showWarningMessage(`${result.violations.length} guard violations found`); - } else { - vscode.window.showInformationMessage('All guard checks passed'); - } - } catch (err) { - vscode.window.showErrorMessage(`Guard check failed: ${err}`); - } - } - - dispose() { - this.mcpProcess?.kill(); - this.wsServer?.close(); - } -} - -export function activate(context: vscode.ExtensionContext) { - const provider = new HanzoMCPProvider(context); - - // Register tree view - vscode.window.createTreeView('hanzo-mcp-sessions', { - treeDataProvider: provider, - showCollapseAll: true - }); - - // Register commands - const commands = [ - vscode.commands.registerCommand('hanzo-mcp.edit', () => provider.executeEdit('organize_imports')), - vscode.commands.registerCommand('hanzo-mcp.fmt', () => provider.executeFormat()), - vscode.commands.registerCommand('hanzo-mcp.test', () => provider.executeTest()), - vscode.commands.registerCommand('hanzo-mcp.build', () => provider.executeBuild()), - vscode.commands.registerCommand('hanzo-mcp.lint', () => provider.executeLint(false)), - vscode.commands.registerCommand('hanzo-mcp.guard', () => provider.executeGuard()), - vscode.commands.registerCommand('hanzo-mcp.sessions.view', () => provider.refresh()), - vscode.commands.registerCommand('hanzo-mcp.workspace.refactor', async () => { - const choice = await vscode.window.showQuickPick([ - 'Multi-language rename', - 'Go workspace refactor', - 'Format all', - 'Test all', - 'Lint all' - ], { placeHolder: 'Choose refactoring operation' }); - - switch (choice) { - case 'Multi-language rename': - const symbolName = await vscode.window.showInputBox({ - prompt: 'Enter symbol to rename' - }); - const newName = await vscode.window.showInputBox({ - prompt: 'Enter new name' - }); - if (symbolName && newName) { - // Implementation for multi-language rename - } - break; - case 'Go workspace refactor': - // Execute wide Go refactor - break; - case 'Format all': - await provider.executeFormat(); - break; - case 'Test all': - await provider.executeTest(); - break; - case 'Lint all': - await provider.executeLint(true); - break; - } - }), - vscode.commands.registerCommand('hanzo-mcp.codebase.index', async () => { - vscode.window.showInformationMessage('Indexing codebase...'); - try { - await provider.executeMCPCommand('index_codebase'); - vscode.window.showInformationMessage('Codebase indexing completed'); - } catch (err) { - vscode.window.showErrorMessage(`Indexing failed: ${err}`); - } - }) - ]; - - context.subscriptions.push(...commands, provider); -} - -export function deactivate() {} \ No newline at end of file diff --git a/pkg/hanzo-mcp/vscode-extension/tsconfig.json b/pkg/hanzo-mcp/vscode-extension/tsconfig.json deleted file mode 100644 index ab3279cab..000000000 --- a/pkg/hanzo-mcp/vscode-extension/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "ES2020", - "outDir": "out", - "lib": [ - "ES2020" - ], - "sourceMap": true, - "rootDir": "src", - "strict": true, - "moduleResolution": "node", - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true - }, - "exclude": [ - "node_modules", - ".vscode-test" - ] -} \ No newline at end of file diff --git a/pkg/hanzo-memory/CHANGELOG.md b/pkg/hanzo-memory/CHANGELOG.md deleted file mode 100644 index f8ba90f7c..000000000 --- a/pkg/hanzo-memory/CHANGELOG.md +++ /dev/null @@ -1,37 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [0.1.1] - 2025-07-23 - -### Fixed -- Fixed type checking errors throughout the codebase -- Fixed Settings initialization in config.py -- Added missing return type annotations -- Fixed FastAPI parameter ordering in endpoints -- Changed error responses from JSONResponse to HTTPException for consistency - -### Added -- Added comprehensive test coverage for authentication module -- Added tests for CLI commands -- Added edge case tests for various modules - -### Changed -- Improved test coverage from 81% to 86% -- Updated all dependencies to latest versions - -## [0.1.0] - 2025-07-23 - -### Added -- Initial release of Hanzo Memory Service -- FastAPI server with memory and knowledge management APIs -- Model Context Protocol (MCP) server support -- InfinityDB embedded vector database integration -- FastEmbed for local ONNX-based embeddings -- LiteLLM for universal LLM interface -- Authentication support with API keys -- CLI interface for server management -- Comprehensive test suite with pytest \ No newline at end of file diff --git a/pkg/hanzo-memory/DOCS.md b/pkg/hanzo-memory/DOCS.md deleted file mode 100644 index 432b9bf06..000000000 --- a/pkg/hanzo-memory/DOCS.md +++ /dev/null @@ -1,396 +0,0 @@ -# Memory API Documentation - -## Introduction - -Memory API provides long-term memory and contextual knowledge capabilities for AI applications, enabling systems to: - -* **Remember past interactions** with users -* **Maintain context** across sessions -* **Retrieve relevant information** from previous conversations or a dedicated knowledge base - -These capabilities support more personalized, contextually aware, and human-like AI experiences. - -### Authentication - -* **Production**: All endpoints require an API key provided via the `x-api-key` HTTP header or set in the `HANZO_API_KEY` environment variable. -* **Local Development**: Authentication can be disabled when running locally by setting `DISABLE_AUTH=true` or omitting the header. - -> **Note:** All endpoints are RESTful and expect JSON request bodies with **lowercase** key names (e.g., `userid`, `messagecontent`). The `userid` parameter is **mandatory** for most endpoints; it partitions data per user to ensure multi-tenancy and security. - -## Core Memory API - -### 1. POST /v1/remember - -**Retrieve & Store Memories** - -* **Description:** Retrieves relevant memories for the incoming message, enqueues the message for storage, and optionally filters results via an LLM. -* **Authentication:** API key required in header `Authorization: Bearer ` or JSON field `apikey`. - -#### Request Parameters - -| Name | Type | Required | Description | -| ------------------- | ------- | -------- | ------------------------------------------------------------ | -| `apikey` | string | Yes | Your API key. | -| `userid` | string | Yes | Unique user identifier. | -| `messagecontent` | string | Yes | Message text for retrieval and storage. | -| `additionalcontext` | string | No | Extra context to improve retrieval or LLM filtering. | -| `strippii` | boolean | No | Anonymize PII during storage (default: false). | -| `filterresults` | boolean | No | Use LLM to filter to top 3 memories (default: false). | -| `includememoryid` | boolean | No | Return objects with `content` & `memoryId` (default: false). | - -#### Example Request - -```json -POST /v1/remember -Authorization: Bearer YOUR_API_KEY -Content-Type: application/json - -{ - "userid": "user-123", - "messagecontent": "I prefer dark mode interfaces", - "filterresults": true, - "includememoryid": true -} -``` - -#### Example Response - -```json -HTTP/1.1 200 OK -Content-Type: application/json - -{ - "userid": "user-123", - "relevant_memories": [ - {"content": "User previously chose dark theme.", "memory_id": "mem_abc123"} - ], - "memory_stored": true, - "usage_info": {"current": 15, "limit": 1000} -} -``` - ---- - -### 2. POST /v1/memories/add - -**Add Explicit Memories** - -* **Description:** Directly adds one or more memory strings, bypassing importance analysis. - -#### Request Parameters - -| Name | Type | Required | Description | -| --------------- | ------------ | -------- | ----------------------------------------- | -| `apikey` | string | Yes | Your API key. | -| `userid` | string | Yes | Unique user identifier. | -| `memoriestoadd` | string/array | Yes | Single string or array of strings to add. | - -#### Example Request - -```json -POST /v1/memories/add -{ - "apikey": "YOUR_API_KEY", - "userid": "user-123", - "memoriestoadd": [ - "User likes chocolate ice cream", - "User is allergic to nuts" - ] -} -``` - -#### Example Response - -```json -HTTP/1.1 200 OK -{ - "userid": "user-123", - "added_count": 2, - "memory_ids": ["mem_001", "mem_002"], - "usage_info": {"current": 17, "limit": 1000} -} -``` - ---- - -### 3. POST /v1/memories/get - -**Retrieve Stored Memories** - -* **Description:** Fetches a single memory by ID or a paginated list for a user. - -#### Request Parameters - -| Name | Type | Required | Description | -| ------------ | ------- | -------- | -------------------------------------------------- | -| `apikey` | string | Yes | Your API key. | -| `userid` | string | Yes | Unique user identifier. | -| `memoryid` | string | No | Specific memory ID (ignores `limit`/`startafter`). | -| `limit` | integer | No | Maximum memories to return (default: 50). | -| `startafter` | string | No | Memory ID to start after (for pagination). | - -#### Example Response (List) - -```json -HTTP/1.1 200 OK -{ - "userid": "user-123", - "memories": [ - { - "memory_id": "mem_001", - "content": "User likes chocolate ice cream", - "timestamp": "2025-07-22T14:23:01Z" - } - ], - "pagination": {"has_more": false, "last_id": "mem_001"}, - "usage_info": {"current": 20, "limit": 1000} -} -``` - ---- - -### 4. POST /v1/memories/delete - -**Delete a Specific Memory** - -* **Description:** Removes a single memory by its ID. - -#### Request Parameters - -| Name | Type | Required | Description | -| ---------- | ------ | -------- | --------------------------- | -| `apikey` | string | Yes | Your API key. | -| `userid` | string | Yes | Unique user identifier. | -| `memoryid` | string | Yes | ID of the memory to delete. | - -#### Example Response - -```json -HTTP/1.1 200 OK -{ - "message": "Memory deleted successfully", - "memory_id": "mem_001", - "userid": "user-123" -} -``` - ---- - -### 5. POST /v1/user/delete - -**Delete All Memories for a User** - -* **Description:** Permanently deletes all memories for a given user. Requires explicit confirmation. - -#### Request Parameters - -| Name | Type | Required | Description | -| --------------- | ------- | -------- | ----------------------------------- | -| `apikey` | string | Yes | Your API key. | -| `userid` | string | Yes | Unique user identifier. | -| `confirmdelete` | boolean | Yes | Must be `true` to confirm deletion. | - -#### Example Response - -```json -HTTP/1.1 200 OK -{ - "message": "All user memories deleted", - "userid": "user-123", - "deleted_count": 42 -} -``` - ---- - -## MCP Server API - -The MCP Server API exposes Memory and Fact operations via the Model Context Protocol. Use your dedicated MCP URL (including `:userid`) and authenticate with a Bearer token or `x-api-key`. - -### POST /v1/mcp/\:userid - -**All Tools Endpoint**: Access both Core Memory and Fact APIs, plus Knowledge tools - -#### Available MCP Tools - -* `addMemories`, `getMemories`, `deleteMemory` -* `addKnowledge`, `getKnowledge`, `deleteKnowledge` -* `addFacts`, `getFacts`, `deleteFact` - -#### Example Request - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "method": "tools/call", - "params": { - "name": "addMemories", - "arguments": {"memoriesToAdd": ["dark mode", "vegan user"]} - } -} -``` - ---- - -### POST /v1/mcp/memory/\:userid - -**Memory-Only Tools**: `addMemories`, `getMemories`, `deleteMemory` - ---- - -### POST /v1/mcp/knowledge/\:userid - -**Knowledge-Only Tools**: `addKnowledge`, `getKnowledge`, `deleteKnowledge`, `listSources` - ---- - -## Knowledge API (Multi-Base Graph) - -The Knowledge API handles structured knowledgeโ€”individual factsโ€”organized into one or more **knowledge bases** per user. Each knowledge base is a separate namespace, and within each, facts are stored as nodes in a directed graph in a SQL backend. Facts can be linked via parentโ€“child relationships to model hierarchies or networks. - -### Data Model - -A typical SQL schema: - -```sql --- Knowledge bases (namespaces) -CREATE TABLE knowledge_bases ( - kb_id VARCHAR PRIMARY KEY, - userid VARCHAR NOT NULL, - name TEXT, - metadata JSONB, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- Fact nodes -CREATE TABLE facts ( - fact_id VARCHAR PRIMARY KEY, - kb_id VARCHAR REFERENCES knowledge_bases(kb_id), - content TEXT NOT NULL, - metadata JSONB, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- Relationships between facts -CREATE TABLE fact_relations ( - parent_fact_id VARCHAR REFERENCES facts(fact_id), - child_fact_id VARCHAR REFERENCES facts(fact_id), - relation_type VARCHAR, - PRIMARY KEY(parent_fact_id, child_fact_id) -); -``` - -* **knowledge\_bases**: Namespaces for facts. A user can own multiple bases. -* **facts**: Individual fact nodes within a base (`kb_id`). -* **fact\_relations**: Directed edges modeling parent โ†’ child links. - -### Key Concepts - -* **Multiple Knowledge Bases**: Create, list, and delete bases to partition knowledge. -* **Fact Nodes**: Store `content` plus optional JSON `metadata` per fact. -* **Edges**: Build rich fact networks via `fact_relations`. -* **Recursive Traversal**: Use SQL CTEs for subtree queries. - -### Endpoints - -All endpoints require `userid` and either the `x-api-key` header or `HANZO_API_KEY` environment variable (unless `DISABLE_AUTH=true`). - -#### 1. POST /v1/knowledge/bases/create - -**Create a new knowledge base** - -| Field | Type | Required | Description | -| ------- | ------ | -------- | -------------------------------------- | -| `name` | string | Yes | Display name for the base. | -| `kb_id` | string | No | Custom ID (auto-generated if omitted). | - -**Response:** - -```json -{ "success": true, "kb_id": "kb_123", "name": "Engineering Knowledge" } -``` - -#### 2. POST /v1/knowledge/bases/list - -**List knowledge bases for a user** - -| Field | Type | Required | Description | -| -------- | ------ | -------- | ---------------- | -| `userid` | string | Yes | User identifier. | - -**Response:** - -```json -{ "bases": [ { "kb_id": "kb_123", "name": "Engineering Knowledge", "created_at": "2025-07-22T..." } ] } -``` - -#### 3. POST /v1/knowledge/add - -**Add facts to a knowledge base** - -| Field | Type | Required | Description | -| -------- | ------------ | -------- | -------------------------------------------------------------------- | -| `userid` | string | Yes | User identifier. | -| `kb_id` | string | Yes | Target knowledge base. | -| `facts` | array of obj | Yes | List of facts: `{ fact_id?(auto), content, metadata?, parent_id? }`. | - -**Behavior:** Inserts into `facts` and, if `parent_id` provided, into `fact_relations`. - -**Response:** - -```json -{ "success": true, "inserted": 5 } -``` - -#### 4. POST /v1/knowledge/get - -**Retrieve facts** - -| Field | Type | Required | Description | -| --------- | ------- | -------- | ---------------------------------------------- | -| `userid` | string | Yes | User identifier. | -| `kb_id` | string | Yes | Knowledge base ID. | -| `fact_id` | string | No | Single fact ID to fetch. | -| `subtree` | boolean | No | If true, fetch this fact plus all descendants. | -| `query` | string | No | Full-text search on `content`. | -| `limit` | integer | No | Max facts to return (default: 50). | - -**Behavior:** - -* With `fact_id` + `subtree=true`, uses a SQL recursive CTE to traverse descendants. -* Otherwise, returns matching nodes. - -**Response:** - -```json -{ "facts": [ { "fact_id":"f_1","content":"...","metadata":{} } ], "pagination": {...} } -``` - -#### 5. POST /v1/knowledge/delete - -**Delete facts** - -| Field | Type | Required | Description | -| --------- | ------- | -------- | ------------------------------------------ | -| `userid` | string | Yes | User identifier. | -| `kb_id` | string | Yes | Knowledge base ID. | -| `fact_id` | string | Yes | Fact node to delete. | -| `cascade` | boolean | No | If true, also delete all descendant facts. | - -#### 6. POST /v1/knowledge/ingest - -**Configure GCS ingestion** - -| Field | Type | Required | Description | -| ------------- | --------------- | -------- | -------------------------------------------- | -| `userid` | string | Yes | User identifier. | -| `kb_id` | string | Yes | Target knowledge base. | -| `details` | object | Yes | Must include `bucketUri` (e.g., `gs://...`). | -| `projecttags` | array of string | No | Tags applied to all ingested facts. | - -**Behavior:** Sets up nightly sync from GCS into the specified base. - ---- - -For more information, see the online docs at [https://your-api-domain.com/docs](https://your-api-domain.com/docs) diff --git a/pkg/hanzo-memory/Dockerfile b/pkg/hanzo-memory/Dockerfile deleted file mode 100644 index 5a0fff7a5..000000000 --- a/pkg/hanzo-memory/Dockerfile +++ /dev/null @@ -1,57 +0,0 @@ -# Build stage -FROM python:3.11-slim as builder - -# Install build dependencies -RUN apt-get update && apt-get install -y \ - build-essential \ - curl \ - && rm -rf /var/lib/apt/lists/* - -# Install uv -RUN curl -LsSf https://astral.sh/uv/install.sh | sh -ENV PATH="/root/.cargo/bin:$PATH" - -# Set working directory -WORKDIR /app - -# Copy project files -COPY pyproject.toml . -COPY src/ src/ - -# Build the package -RUN uv pip install --system -e . - -# Runtime stage -FROM python:3.11-slim - -# Install runtime dependencies -RUN apt-get update && apt-get install -y \ - curl \ - && rm -rf /var/lib/apt/lists/* - -# Create non-root user -RUN useradd -m -u 1000 hanzo - -# Set working directory -WORKDIR /app - -# Copy from builder -COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages -COPY --from=builder /usr/local/bin /usr/local/bin -COPY --from=builder /app/src /app/src - -# Create data directory -RUN mkdir -p /app/data && chown -R hanzo:hanzo /app - -# Switch to non-root user -USER hanzo - -# Expose port -EXPOSE 4000 - -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ - CMD curl -f http://localhost:4000/health || exit 1 - -# Run the server -CMD ["python", "-m", "uvicorn", "hanzo_memory.server:app", "--host", "0.0.0.0", "--port", "4000"] \ No newline at end of file diff --git a/pkg/hanzo-memory/LICENSE b/pkg/hanzo-memory/LICENSE deleted file mode 100644 index a18ce54b2..000000000 --- a/pkg/hanzo-memory/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD License - -Copyright (c) 2025-present, Hanzo Industries Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name Hanzo nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pkg/hanzo-memory/LLM.md b/pkg/hanzo-memory/LLM.md deleted file mode 100644 index 4798719c9..000000000 --- a/pkg/hanzo-memory/LLM.md +++ /dev/null @@ -1,173 +0,0 @@ -# Hanzo Memory Service - LLM Context - -## Project Overview - -Hanzo Memory Service is a high-performance AI memory and knowledge management system built with: -- **InfinityDB**: Embedded vector database for fast similarity search -- **FastEmbed**: Local embedding generation (no API calls) -- **LiteLLM**: Universal LLM interface supporting 100+ providers -- **FastAPI**: Modern async web framework - -## Key Architecture Decisions - -### 1. InfinityDB over PostgreSQL/pgvector -- Embedded database (no separate server process) -- Optimized for vector similarity search -- Lightweight deployment with file-based storage -- Direct support for multimodal embeddings - -### 2. FastEmbed for Local Embeddings -- No dependency on external embedding APIs -- Fast local inference using ONNX runtime -- Default model: BAAI/bge-small-en-v1.5 (384 dimensions) -- Supports custom models via model registry - -### 3. LiteLLM for LLM Flexibility -- Single interface for all LLM providers -- Supports OpenAI, Anthropic, Ollama, Azure, etc. -- Easy switching between cloud and local models -- Automatic retry and fallback handling - -## Core Components - -### Database Schema (InfinityDB Tables) - -1. **Projects Database** - - `projects` table: User projects with metadata - -2. **Memories Database** - - `memories_{user_id}` tables: Per-user memory storage - - Columns: memory_id, content, embedding, metadata, importance - -3. **Knowledge Database** - - `knowledge_bases` table: Knowledge base definitions - - `facts_{kb_id}` tables: Facts with parent-child relationships - -4. **Chats Database** - - `chats_{user_id}` tables: Conversation history with embeddings - -### Service Layer - -1. **EmbeddingService** - - Manages FastEmbed model lifecycle - - Batch processing for efficiency - - Similarity computation (cosine, dot, euclidean) - -2. **LLMService** - - LiteLLM integration for all LLM operations - - Summarization with knowledge extraction - - JSON-mode for structured outputs - - PII stripping capabilities - -3. **MemoryService** - - Memory CRUD operations - - Semantic search with optional LLM filtering - - Importance scoring and metadata management - -## API Design Patterns - -### Authentication -- Bearer token or x-hanzo-api-key header -- Optional apikey field in JSON body (legacy support) -- DISABLE_AUTH environment variable for development - -### Request/Response Models -- Lowercase field names for compatibility (userid, messagecontent) -- Pydantic models for validation and documentation -- Consistent error responses with status codes - -### Unified Search -- Query embeddings generated locally -- Vector similarity search in InfinityDB -- Optional LLM re-ranking for relevance -- Project and session-based filtering - -## Testing Strategy - -### Unit Tests -- Service layer testing with mocked dependencies -- Embedding generation and similarity tests -- LLM response parsing and error handling - -### Integration Tests -- FastAPI TestClient for endpoint testing -- InfinityDB operations with temporary databases -- End-to-end memory storage and retrieval - -### Test Fixtures -- Temporary database paths -- Sample embeddings and content -- Mock LLM responses for deterministic tests - -## Performance Optimizations - -1. **Local Operations** - - Embeddings generated in-process - - No network latency for vector operations - - Batch processing where possible - -2. **Caching Strategy** - - Optional Redis integration - - Embedding cache for repeated content - - LLM response caching for common queries - -3. **Async Operations** - - FastAPI async endpoints - - Non-blocking database operations - - Concurrent request handling - -## Deployment Considerations - -### Docker Deployment -- Multi-stage build for smaller images -- Non-root user for security -- Health checks for orchestration -- Volume mounting for data persistence - -### Configuration Management -- Environment variables with HANZO_ prefix -- .env file support for local development -- Sensible defaults for all settings -- Model selection via environment - -### Scaling Options -1. **Vertical Scaling**: Larger instances for more memory/CPU -2. **Horizontal Scaling**: Multiple instances with shared storage -3. **Edge Deployment**: Fully offline operation with local models - -## Future Enhancements - -1. **MCP Server Implementation** - - Full Model Context Protocol support - - Tool definitions for memory/knowledge operations - - Integration with Claude Desktop and other MCP clients - -2. **Advanced Features** - - Multi-modal embeddings (images, audio) - - Incremental learning and memory consolidation - - Federated memory sharing between instances - - Advanced graph operations for knowledge bases - -3. **Performance Improvements** - - GPU acceleration for embeddings - - Streaming responses for large result sets - - Distributed vector indices - -## Common Patterns - -### Adding New Endpoints -1. Define Pydantic models in `models/` -2. Implement service logic in `services/` -3. Add FastAPI endpoint in `server.py` -4. Write tests in `tests/` - -### Extending Embedding Support -1. Update FastEmbed model in config -2. Adjust embedding dimensions -3. Regenerate existing embeddings if needed - -### Custom LLM Integration -1. Configure LiteLLM model string -2. Set appropriate API base/key -3. Adjust temperature and token limits -4. Test JSON mode compatibility \ No newline at end of file diff --git a/pkg/hanzo-memory/Makefile b/pkg/hanzo-memory/Makefile deleted file mode 100644 index f1a867dd3..000000000 --- a/pkg/hanzo-memory/Makefile +++ /dev/null @@ -1,208 +0,0 @@ -# Hanzo Memory Service Makefile - -# Colors -BLUE := \033[0;34m -GREEN := \033[0;32m -YELLOW := \033[0;33m -RED := \033[0;31m -NC := \033[0m # No Color - -# Python version -PYTHON_VERSION := 3.11 - -# Project paths -PROJECT_ROOT := $(shell pwd) -SRC_DIR := src -TEST_DIR := tests -DATA_DIR := data -INFINITY_DB_PATH := $(DATA_DIR)/infinity_db - -# Default target -.DEFAULT_GOAL := help - -# Help -.PHONY: help -help: ## Show this help message - @echo "$(BLUE)Hanzo Memory Service$(NC)" - @echo "$(GREEN)Available commands:$(NC)" - @awk 'BEGIN {FS = ":.*##"; printf "\n"} /^[a-zA-Z_-]+:.*?##/ { printf " $(YELLOW)%-15s$(NC) %s\n", $$1, $$2 } /^##@/ { printf "\n$(BLUE)%s$(NC)\n", substr($$0, 5) }' $(MAKEFILE_LIST) - -##@ Setup - -.PHONY: install-python -install-python: ## Install Python using uv - @echo "$(BLUE)Installing Python $(PYTHON_VERSION)...$(NC)" - @command -v uv >/dev/null 2>&1 || (echo "Installing uv..." && curl -LsSf https://astral.sh/uv/install.sh | sh) - uv python install $(PYTHON_VERSION) - -.PHONY: venv -venv: ## Create virtual environment using uv - @echo "$(BLUE)Creating virtual environment...$(NC)" - uv venv - -.PHONY: install -install: ## Install dependencies - @echo "$(BLUE)Installing dependencies...$(NC)" - uv pip install -e ".[dev,test,docs]" - -.PHONY: setup -setup: venv install dirs ## Complete project setup - @echo "$(GREEN)Setup complete!$(NC)" - -.PHONY: dirs -dirs: ## Create necessary directories - @echo "$(BLUE)Creating directories...$(NC)" - @mkdir -p $(DATA_DIR) - @mkdir -p $(INFINITY_DB_PATH) - @mkdir -p logs - @mkdir -p $(SRC_DIR)/hanzo_memory - @mkdir -p $(TEST_DIR) - -##@ Development - -.PHONY: dev -dev: ## Run development server - @echo "$(BLUE)Starting development server...$(NC)" - uv run uvicorn hanzo_memory.server:app --reload --host 0.0.0.0 --port 4000 - -.PHONY: mcp -mcp: ## Run MCP server - @echo "$(BLUE)Starting MCP server...$(NC)" - uv run hanzo-memory-mcp - -.PHONY: install-mcp -install-mcp: ## Install MCP server to Claude Desktop - @echo "$(BLUE)Installing MCP server to Claude Desktop...$(NC)" - @echo "Add the following to your Claude Desktop config:" - @echo ' "mcpServers": {' - @echo ' "hanzo-memory": {' - @echo ' "command": "uv",' - @echo ' "args": ["run", "hanzo-memory-mcp"],' - @echo ' "cwd": "$(shell pwd)"' - @echo ' }' - @echo ' }' - -.PHONY: run -run: ## Run production server - @echo "$(BLUE)Starting production server...$(NC)" - uv run uvicorn hanzo_memory.server:app --host 0.0.0.0 --port 4000 - -##@ Testing - -.PHONY: test -test: ## Run tests - @echo "$(BLUE)Running tests...$(NC)" - uv run pytest -v - -.PHONY: test-cov -test-cov: ## Run tests with coverage - @echo "$(BLUE)Running tests with coverage...$(NC)" - uv run pytest --cov=hanzo_memory --cov-report=html --cov-report=term - -.PHONY: test-watch -test-watch: ## Run tests in watch mode - @echo "$(BLUE)Running tests in watch mode...$(NC)" - uv run pytest-watch - -##@ Code Quality - -.PHONY: lint -lint: ## Run linting - @echo "$(BLUE)Running ruff linter...$(NC)" - uv run ruff check $(SRC_DIR) $(TEST_DIR) - -.PHONY: format -format: ## Format code - @echo "$(BLUE)Formatting code...$(NC)" - uv run ruff format $(SRC_DIR) $(TEST_DIR) - -.PHONY: type-check -type-check: ## Run type checking - @echo "$(BLUE)Running type checker...$(NC)" - uv run mypy $(SRC_DIR) - -.PHONY: check -check: lint type-check test ## Run all checks - -##@ Build & Package - -.PHONY: build -build: clean check ## Build package - @echo "$(BLUE)Building package...$(NC)" - uv build - -.PHONY: install-local -install-local: ## Install package locally with uv - @echo "$(BLUE)Installing package locally...$(NC)" - uv pip install . - -.PHONY: publish -publish: build ## Build and publish package to PyPI - @echo "$(BLUE)Publishing package to PyPI...$(NC)" - uv run twine upload dist/* - -##@ Database - -.PHONY: db-init -db-init: ## Initialize InfinityDB - @echo "$(BLUE)Initializing InfinityDB...$(NC)" - uv run python -m hanzo_memory.db.init - -.PHONY: db-reset -db-reset: ## Reset InfinityDB (WARNING: destroys all data) - @echo "$(RED)WARNING: This will destroy all data!$(NC)" - @read -p "Are you sure? [y/N] " -n 1 -r; \ - echo; \ - if [[ $$REPLY =~ ^[Yy]$$ ]]; then \ - rm -rf $(INFINITY_DB_PATH)/*; \ - $(MAKE) db-init; \ - fi - -##@ Documentation - -.PHONY: docs -docs: ## Build documentation - @echo "$(BLUE)Building documentation...$(NC)" - uv run mkdocs build - -.PHONY: docs-serve -docs-serve: ## Serve documentation locally - @echo "$(BLUE)Serving documentation...$(NC)" - uv run mkdocs serve - -##@ Cleanup - -.PHONY: clean -clean: ## Clean build artifacts - @echo "$(BLUE)Cleaning build artifacts...$(NC)" - @rm -rf build dist *.egg-info - @rm -rf .pytest_cache .ruff_cache .mypy_cache - @rm -rf htmlcov .coverage coverage.xml - @find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true - @find . -type f -name "*.pyc" -delete - -.PHONY: clean-all -clean-all: clean ## Clean everything including data - @echo "$(RED)Cleaning all data...$(NC)" - @rm -rf $(DATA_DIR) - @rm -rf logs - -##@ Docker (Optional) - -.PHONY: docker-build -docker-build: ## Build Docker image - @echo "$(BLUE)Building Docker image...$(NC)" - docker build -t hanzo-memory:latest . - -.PHONY: docker-run -docker-run: ## Run Docker container - @echo "$(BLUE)Running Docker container...$(NC)" - docker run -p 4000:4000 -v $(PWD)/data:/app/data hanzo-memory:latest - -##@ Default target - -.PHONY: all -all: setup build test ## Run setup, build, and test - -# Make default target -.DEFAULT: help \ No newline at end of file diff --git a/pkg/hanzo-memory/README.md b/pkg/hanzo-memory/README.md deleted file mode 100644 index 9eaf8fa17..000000000 --- a/pkg/hanzo-memory/README.md +++ /dev/null @@ -1,245 +0,0 @@ -# Hanzo Memory Service - -## Add memory to any AI application! - -A high-performance FastAPI service that provides memory and knowledge management capabilities for AI applications. Built with LanceDB vector database (works on all platforms including browsers via WASM), local embeddings, and LiteLLM for flexible LLM integration. - -## Features - -- **๐Ÿง  Intelligent Memory Management**: Store and retrieve contextual memories with semantic search -- **๐Ÿ“š Knowledge Base System**: Organize facts in hierarchical knowledge bases with parent-child relationships -- **๐Ÿ’ฌ Chat History**: Store and search conversation history with de-duplication -- **๐Ÿ” Unified Search API**: Fast semantic search using FastEmbed embeddings -- **๐Ÿค– Flexible LLM Support**: Use any LLM via LiteLLM (OpenAI, Anthropic, Ollama, etc.) -- **๐Ÿ” Multi-tenancy**: Secure user and project-based data isolation -- **๐Ÿš€ High Performance**: Local embeddings and efficient vector storage -- **๐Ÿ—„๏ธ Cross-Platform Database**: LanceDB works everywhere - Linux, macOS, Windows, and even browsers -- **๐Ÿ”Œ MCP Support**: Model Context Protocol server for AI tool integration -- **๐Ÿ“ฆ Easy Deployment**: Docker support and uvx compatibility - -## Architecture - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ FastAPI โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ Vector DB โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ Embeddings โ”‚ -โ”‚ Server โ”‚ โ”‚ (LanceDB/ โ”‚ โ”‚ (FastEmbed/ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ InfinityDB) โ”‚ โ”‚ LanceDB) โ”‚ - โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ–ผ โ”‚ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ LiteLLM โ”‚ โ”‚ Local Models โ”‚ -โ”‚ (LLM Bridge) โ”‚ โ”‚ (BGE, etc.) โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## Quick Start - -### Install with uvx - -```bash -# Install and run directly with uvx -uvx hanzo-memory - -# Or install globally -uvx install hanzo-memory -``` - -### Install from source - -```bash -# Clone the repository -git clone https://github.com/hanzoai/memory -cd memory - -# Install with uv -make setup - -# Run the server -make dev -``` - -### Docker - -```bash -# Using docker-compose -docker-compose up - -# Or build and run manually -docker build -t hanzo-memory . -docker run -p 4000:4000 -v $(pwd)/data:/app/data hanzo-memory -``` - -## Configuration - -Create a `.env` file (see `.env.example`): - -```env -# API Authentication -HANZO_API_KEY=your-api-key-here -HANZO_DISABLE_AUTH=false # Set to true for local development - -# LLM Configuration (choose one) -# OpenAI -HANZO_LLM_MODEL=gpt-4o-mini -OPENAI_API_KEY=your-openai-key - -# Anthropic -HANZO_LLM_MODEL=claude-3-haiku-20240307 -ANTHROPIC_API_KEY=your-anthropic-key - -# Local Models (Ollama) -HANZO_LLM_MODEL=ollama/llama3.2 -HANZO_LLM_API_BASE=http://localhost:11434 - -# Embedding Model -HANZO_EMBEDDING_MODEL=BAAI/bge-small-en-v1.5 - -# Database Backend (optional, defaults to lancedb) -HANZO_DB_BACKEND=lancedb -HANZO_LANCEDB_PATH=data/lancedb -``` - -## Database Backends - -Hanzo Memory supports multiple vector database backends: - -### LanceDB (Default) -- Modern embedded vector database that works on ALL platforms -- Cross-platform: Linux, macOS, Windows, ARM, and even browsers (via WASM) -- Built-in support for FastEmbed and sentence-transformers -- Efficient columnar storage format (Apache Arrow/Parquet) -- Native vector similarity search -- Can be embedded in Python, JavaScript/TypeScript, Rust applications - -### InfinityDB (Alternative, Linux/Windows only) -- High-performance embedded vector database -- Not available on macOS -- Optimized for production workloads -- Built-in vector indexing - -To configure the database backend: - -```env -# Use LanceDB -HANZO_DB_BACKEND=lancedb -HANZO_LANCEDB_PATH=data/lancedb - -# Use InfinityDB -HANZO_DB_BACKEND=infinity -HANZO_INFINITY_DB_PATH=data/infinity_db -``` - -## API Documentation - -For complete API documentation including all endpoints, request/response formats, and examples, see [docs/API.md](docs/API.md). - -### Quick API Overview - -- **Memory Management**: `/v1/remember`, `/v1/memories/*` -- **Knowledge Bases**: `/v1/kb/*`, `/v1/kb/facts/*` -- **Chat Sessions**: `/v1/chat/sessions/*`, `/v1/chat/messages/*` -- **Search**: Unified semantic search across all data types -- **MCP Server**: Model Context Protocol integration for AI tools - -### LLM Features - -The service can: -- **Summarize content** for knowledge extraction -- **Generate knowledge update instructions** in JSON format -- **Filter search results** for relevance -- **Strip PII** from stored content - -Example summarization request: -```python -llm_service.summarize_for_knowledge( - content="Long document...", - skip_summarization=False, # Set to True to skip - provided_summary="Optional pre-made summary" -) -``` - -Returns: -```json -{ - "summary": "Concise summary of content", - "knowledge_instructions": { - "action": "add_fact", - "facts": [{"content": "Extracted fact", "metadata": {...}}], - "reasoning": "Why these facts are important" - } -} -``` - -## Development - -### Running Tests - -```bash -# Run all tests -make test - -# Run with coverage -make test-cov - -# Run specific test -uvx pytest tests/test_memory_api.py -v -``` - -### Code Quality - -```bash -# Format code -make format - -# Run linter -make lint - -# Type checking -make type-check -``` - -### Project Structure - -``` -memory/ -โ”œโ”€โ”€ src/hanzo_memory/ -โ”‚ โ”œโ”€โ”€ api/ # API authentication -โ”‚ โ”œโ”€โ”€ db/ # InfinityDB client -โ”‚ โ”œโ”€โ”€ models/ # Pydantic models -โ”‚ โ”œโ”€โ”€ services/ # Business logic -โ”‚ โ”œโ”€โ”€ config.py # Settings -โ”‚ โ””โ”€โ”€ server.py # FastAPI app -โ”œโ”€โ”€ tests/ # Pytest tests -โ”œโ”€โ”€ Makefile # Build automation -โ””โ”€โ”€ pyproject.toml # Project config -``` - -## Deployment - -### Production Checklist - -1. Set strong `HANZO_API_KEY` -2. Configure appropriate LLM model and API keys -3. Set `HANZO_DISABLE_AUTH=false` -4. Configure data persistence volume -5. Set up monitoring and logging -6. Configure rate limiting if needed - -### Scaling Considerations - -- InfinityDB embedded runs in-process (no separate DB server) -- FastEmbed generates embeddings locally (no API calls) -- LLM calls can be directed to local models for full offline operation -- Use Redis for caching in high-traffic scenarios - -## Contributing - -Pull requests are welcome! Please: -1. Write tests for new features -2. Follow existing code style -3. Update documentation as needed -4. Run `make check` before submitting - -## License - -BSD License - see LICENSE file for details. diff --git a/pkg/hanzo-memory/USAGE.md b/pkg/hanzo-memory/USAGE.md deleted file mode 100644 index 1a4964984..000000000 --- a/pkg/hanzo-memory/USAGE.md +++ /dev/null @@ -1,1318 +0,0 @@ -# Hanzo Memory SDK - Complete Usage Guide - -## Table of Contents -1. [Overview](#overview) -2. [Installation](#installation) -3. [Quick Start](#quick-start) -4. [Core Concepts](#core-concepts) -5. [Memory Management](#memory-management) -6. [Knowledge Bases](#knowledge-bases) -7. [Chat History](#chat-history) -8. [Search Capabilities](#search-capabilities) -9. [LLM Integration](#llm-integration) -10. [Vector Databases](#vector-databases) -11. [API Client Usage](#api-client-usage) -12. [MCP Integration](#mcp-integration) -13. [Production Deployment](#production-deployment) -14. [Examples](#examples) -15. [API Reference](#api-reference) - -## Overview - -Hanzo Memory is a high-performance memory and knowledge management service for AI applications. It provides: - -- **Semantic Memory**: Store and retrieve contextual memories using vector similarity -- **Knowledge Management**: Hierarchical knowledge bases with facts and relationships -- **Chat History**: Persistent conversation storage with semantic search -- **Vector Search**: Fast similarity search using local embeddings -- **LLM Integration**: Flexible LLM support via LiteLLM -- **Multi-tenancy**: User and project isolation -- **Cross-platform**: Works on Linux, macOS, Windows, and browsers - -## Installation - -### Install as a Service - -```bash -# Install with uvx -uvx install hanzo-memory - -# Run the service -hanzo-memory - -# Or run directly without installing -uvx hanzo-memory -``` - -### Install as a Python Package - -```bash -# Basic installation -pip install hanzo-memory - -# With all features -pip install hanzo-memory[all] - -# For development -pip install hanzo-memory[dev] -``` - -### Docker Installation - -```bash -# Using docker-compose -docker-compose up - -# Or with Docker directly -docker build -t hanzo-memory . -docker run -p 4000:4000 -v $(pwd)/data:/app/data hanzo-memory -``` - -## Quick Start - -### Starting the Service - -```python -# Start the memory service -import subprocess -service = subprocess.Popen(["hanzo-memory"]) - -# Or use programmatically -from hanzo_memory import MemoryService -service = MemoryService() -await service.start() -``` - -### Basic Client Usage - -```python -from hanzo_memory.client import MemoryClient - -# Initialize client -client = MemoryClient( - base_url="http://localhost:4000", - api_key="your-api-key" -) - -# Store a memory -memory = await client.remember( - content="The user prefers dark mode interfaces", - user_id="user123", - metadata={"category": "preferences"} -) - -# Search memories -results = await client.search_memories( - query="user interface preferences", - user_id="user123", - limit=10 -) - -# Use knowledge base -kb = await client.create_knowledge_base( - name="Product Documentation", - description="Internal product knowledge" -) - -fact = await client.add_fact( - kb_id=kb.id, - content="The application supports OAuth2 authentication", - metadata={"section": "auth", "version": "2.0"} -) -``` - -## Core Concepts - -### Memory Types - -1. **Episodic Memory**: Event-based memories with temporal context -2. **Semantic Memory**: Facts and knowledge without specific temporal context -3. **Working Memory**: Short-term context for active conversations - -### Data Model - -```python -from hanzo_memory.models import Memory, KnowledgeBase, Fact, ChatMessage -from datetime import datetime -from typing import Optional, Dict, Any - -# Memory model -class Memory: - id: str - content: str - embedding: Optional[list[float]] - user_id: str - project_id: Optional[str] - metadata: Dict[str, Any] - created_at: datetime - accessed_at: datetime - access_count: int - -# Knowledge base model -class KnowledgeBase: - id: str - name: str - description: Optional[str] - parent_id: Optional[str] # Hierarchical KBs - metadata: Dict[str, Any] - created_at: datetime - -# Fact model -class Fact: - id: str - kb_id: str - content: str - embedding: Optional[list[float]] - confidence: float = 1.0 - metadata: Dict[str, Any] - created_at: datetime - -# Chat message model -class ChatMessage: - id: str - session_id: str - role: str # "user", "assistant", "system" - content: str - embedding: Optional[list[float]] - metadata: Dict[str, Any] - created_at: datetime -``` - -## Memory Management - -### Storing Memories - -```python -# Simple memory storage -memory = await client.remember( - content="Important information to remember", - user_id="user123" -) - -# With metadata and project context -memory = await client.remember( - content="Customer prefers email communication", - user_id="user123", - project_id="project456", - metadata={ - "type": "preference", - "customer_id": "cust789", - "confidence": 0.9 - } -) - -# Batch memory storage -memories = await client.remember_batch([ - { - "content": "Meeting scheduled for 3pm", - "metadata": {"type": "event", "date": "2024-01-15"} - }, - { - "content": "Project deadline is next Friday", - "metadata": {"type": "deadline", "project": "Alpha"} - } -], user_id="user123") -``` - -### Retrieving Memories - -```python -# Get specific memory -memory = await client.get_memory(memory_id="mem_123") - -# List memories with filters -memories = await client.list_memories( - user_id="user123", - project_id="project456", - limit=50, - offset=0 -) - -# Semantic search -results = await client.search_memories( - query="communication preferences", - user_id="user123", - threshold=0.7, # Similarity threshold - limit=10 -) - -# Advanced search with filters -results = await client.search_memories( - query="project deadlines", - user_id="user123", - filters={ - "metadata.type": "deadline", - "created_at": {"$gte": "2024-01-01"} - }, - limit=20 -) -``` - -### Memory Operations - -```python -# Update memory -updated = await client.update_memory( - memory_id="mem_123", - content="Updated information", - metadata={"edited": True, "editor": "user123"} -) - -# Delete memory -await client.delete_memory(memory_id="mem_123") - -# Forget memories (bulk delete) -deleted_count = await client.forget_memories( - user_id="user123", - filters={"metadata.type": "temporary"} -) - -# Memory consolidation -consolidated = await client.consolidate_memories( - user_id="user123", - strategy="summarize", # or "merge", "deduplicate" - time_window="7d" -) -``` - -## Knowledge Bases - -### Creating Knowledge Bases - -```python -# Create root knowledge base -kb = await client.create_knowledge_base( - name="Company Knowledge", - description="Central repository of company information" -) - -# Create child knowledge base -product_kb = await client.create_knowledge_base( - name="Product Documentation", - description="Product-specific knowledge", - parent_id=kb.id, - metadata={"version": "2.0", "public": True} -) - -# Hierarchical structure -departments = await client.create_knowledge_base( - name="Departments", - parent_id=kb.id -) - -engineering = await client.create_knowledge_base( - name="Engineering", - parent_id=departments.id -) -``` - -### Managing Facts - -```python -# Add fact to knowledge base -fact = await client.add_fact( - kb_id=engineering.id, - content="The API uses REST architecture with JSON responses", - metadata={ - "category": "architecture", - "importance": "high", - "last_updated": "2024-01-15" - } -) - -# Batch add facts -facts = await client.add_facts_batch( - kb_id=product_kb.id, - facts=[ - { - "content": "Feature X improves performance by 40%", - "metadata": {"feature": "X", "metric": "performance"} - }, - { - "content": "Feature Y reduces memory usage", - "metadata": {"feature": "Y", "metric": "memory"} - } - ] -) - -# Update fact -updated_fact = await client.update_fact( - fact_id=fact.id, - content="The API uses REST architecture with JSON and XML responses", - metadata={"revised": True} -) - -# Search facts -results = await client.search_facts( - kb_id=engineering.id, - query="API architecture", - include_children=True, # Search child KBs too - limit=10 -) -``` - -### Knowledge Base Operations - -```python -# List knowledge bases -kbs = await client.list_knowledge_bases( - parent_id=None, # Get root KBs - include_children=True -) - -# Get KB with facts -kb_details = await client.get_knowledge_base( - kb_id=kb.id, - include_facts=True, - include_children=True -) - -# Update KB -updated_kb = await client.update_knowledge_base( - kb_id=kb.id, - name="Company Knowledge Base v2", - metadata={"last_review": "2024-01-15"} -) - -# Delete KB (and optionally its facts) -await client.delete_knowledge_base( - kb_id=kb.id, - cascade=True # Delete all facts and child KBs -) - -# Export KB -export_data = await client.export_knowledge_base( - kb_id=kb.id, - format="json", # or "markdown", "yaml" - include_embeddings=False -) - -# Import KB -imported_kb = await client.import_knowledge_base( - name="Imported Knowledge", - data=export_data, - parent_id=None -) -``` - -## Chat History - -### Managing Chat Sessions - -```python -# Create chat session -session = await client.create_chat_session( - user_id="user123", - metadata={ - "platform": "web", - "version": "2.0" - } -) - -# Add messages to session -user_msg = await client.add_chat_message( - session_id=session.id, - role="user", - content="What's the weather like?" -) - -assistant_msg = await client.add_chat_message( - session_id=session.id, - role="assistant", - content="I'd be happy to help with weather information. Could you tell me your location?" -) - -# Get session history -messages = await client.get_chat_history( - session_id=session.id, - limit=50, - include_system_messages=True -) - -# Search across sessions -results = await client.search_chat_history( - user_id="user123", - query="weather information", - limit=20 -) -``` - -### Advanced Chat Features - -```python -# Summarize conversation -summary = await client.summarize_chat_session( - session_id=session.id, - style="bullet_points" # or "paragraph", "key_points" -) - -# Extract insights from chat -insights = await client.extract_chat_insights( - session_id=session.id, - insight_types=["preferences", "issues", "questions"] -) - -# Find similar conversations -similar = await client.find_similar_conversations( - session_id=session.id, - user_id="user123", - threshold=0.8, - limit=5 -) - -# Chat analytics -analytics = await client.get_chat_analytics( - user_id="user123", - time_range="30d", - metrics=["message_count", "session_duration", "topics"] -) -``` - -## Search Capabilities - -### Unified Search - -```python -# Search across all data types -results = await client.unified_search( - query="authentication methods", - user_id="user123", - search_types=["memories", "facts", "chats"], - limit=30 -) - -# Structured results -for result in results: - print(f"Type: {result.type}") - print(f"Content: {result.content}") - print(f"Score: {result.score}") - print(f"Metadata: {result.metadata}") -``` - -### Advanced Search Features - -```python -# Faceted search -results = await client.faceted_search( - query="API documentation", - facets={ - "type": ["fact", "memory"], - "metadata.category": ["architecture", "authentication"], - "created_at": { - "ranges": [ - {"from": "2024-01-01", "to": "2024-06-30"}, - {"from": "2024-07-01", "to": "2024-12-31"} - ] - } - } -) - -# Hybrid search (keyword + semantic) -results = await client.hybrid_search( - keyword_query="REST API", - semantic_query="how to authenticate users", - keyword_weight=0.3, - semantic_weight=0.7 -) - -# Search with reranking -results = await client.search_with_rerank( - query="best practices for API design", - initial_results=50, - rerank_top_k=10, - rerank_model="cross-encoder" -) -``` - -## LLM Integration - -### Content Processing - -```python -# Summarize content before storing -summary_result = await client.process_content( - content="Long document text...", - operations=["summarize", "extract_facts", "remove_pii"] -) - -memory = await client.remember( - content=summary_result.summary, - original_content=content, - metadata={ - "facts": summary_result.facts, - "pii_removed": summary_result.pii_removed - } -) - -# Generate knowledge from content -knowledge = await client.extract_knowledge( - content="Technical documentation...", - kb_id=kb.id, - auto_add_facts=True -) - -# Smart deduplication -deduplicated = await client.smart_deduplicate( - user_id="user123", - similarity_threshold=0.9, - use_llm_verification=True -) -``` - -### LLM-Enhanced Search - -```python -# Query expansion -expanded_results = await client.search_with_expansion( - query="auth", - expand_synonyms=True, - expand_related=True, - max_expansions=5 -) - -# Contextual search -contextual_results = await client.contextual_search( - query="How do I implement this?", - context="Previous conversation about OAuth2", - user_id="user123" -) - -# Answer generation -answer = await client.generate_answer( - question="What are the authentication methods?", - kb_ids=[kb.id], - include_sources=True, - max_facts_to_use=10 -) -``` - -## Vector Databases - -### LanceDB Configuration - -```python -from hanzo_memory.db import LanceDBConfig - -# Configure LanceDB -config = LanceDBConfig( - path="data/lancedb", - embedding_model="BAAI/bge-small-en-v1.5", - distance_metric="cosine", # or "l2", "dot" - index_type="IVF_PQ", # or "FLAT", "HNSW" - nprobe=20, # for IVF index - refine_factor=10 -) - -# Initialize with config -service = MemoryService(db_config=config) -``` - -### InfinityDB Configuration - -```python -from hanzo_memory.db import InfinityDBConfig - -# Configure InfinityDB (Linux/Windows only) -config = InfinityDBConfig( - path="data/infinity_db", - embedding_dimension=384, - distance_type="cosine", - index_type="HNSW", - ef_construction=200, - ef_search=100 -) -``` - -### Custom Embeddings - -```python -from hanzo_memory.embeddings import EmbeddingService - -# Use custom embedding model -embedding_service = EmbeddingService( - model_name="sentence-transformers/all-mpnet-base-v2", - device="cuda", # or "cpu" - batch_size=32 -) - -# Generate embeddings -embeddings = await embedding_service.embed_batch([ - "Text to embed 1", - "Text to embed 2" -]) - -# Use with client -client = MemoryClient( - base_url="http://localhost:4000", - embedding_service=embedding_service -) -``` - -## API Client Usage - -### Async Client - -```python -import asyncio -from hanzo_memory.client import AsyncMemoryClient - -async def main(): - async with AsyncMemoryClient( - base_url="http://localhost:4000", - api_key="your-api-key" - ) as client: - # All operations are async - memory = await client.remember("Important info") - results = await client.search_memories("important") - - # Batch operations - memories = await client.remember_batch([ - {"content": "Memory 1"}, - {"content": "Memory 2"} - ]) - -asyncio.run(main()) -``` - -### Sync Client - -```python -from hanzo_memory.client import SyncMemoryClient - -# Synchronous client for non-async code -client = SyncMemoryClient( - base_url="http://localhost:4000", - api_key="your-api-key" -) - -# All operations are synchronous -memory = client.remember("Important info") -results = client.search_memories("important") -``` - -### Client Configuration - -```python -from hanzo_memory.client import MemoryClient, ClientConfig - -# Advanced configuration -config = ClientConfig( - base_url="http://localhost:4000", - api_key="your-api-key", - timeout=30, # seconds - max_retries=3, - retry_backoff=2.0, - verify_ssl=True, - proxy="http://proxy.example.com:8080" -) - -client = MemoryClient(config=config) - -# With custom headers -client = MemoryClient( - base_url="http://localhost:4000", - api_key="your-api-key", - headers={ - "X-User-ID": "user123", - "X-Project-ID": "project456" - } -) -``` - -## MCP Integration - -### MCP Server Mode - -```python -# Run as MCP server -from hanzo_memory.mcp import MemoryMCPServer - -server = MemoryMCPServer( - name="hanzo-memory", - version="1.0.0" -) - -# Register tools -server.register_tools([ - "remember", - "search_memories", - "manage_knowledge_base", - "chat_history" -]) - -# Start server -await server.start() -``` - -### Using with Claude Desktop - -```json -// claude_desktop_config.json -{ - "mcpServers": { - "hanzo-memory": { - "command": "hanzo-memory", - "args": ["--mcp"], - "env": { - "HANZO_API_KEY": "your-api-key" - } - } - } -} -``` - -### MCP Tools - -```python -# Available MCP tools -tools = { - "remember": { - "description": "Store information in memory", - "parameters": { - "content": "string", - "metadata": "object" - } - }, - "search_memories": { - "description": "Search stored memories", - "parameters": { - "query": "string", - "limit": "number" - } - }, - "add_fact": { - "description": "Add fact to knowledge base", - "parameters": { - "kb_name": "string", - "content": "string" - } - } -} -``` - -## Production Deployment - -### Environment Configuration - -```bash -# Production .env file -# API Configuration -HANZO_API_KEY=strong-random-key -HANZO_DISABLE_AUTH=false -HANZO_CORS_ORIGINS=["https://app.example.com"] - -# Database -HANZO_DB_BACKEND=lancedb -HANZO_LANCEDB_PATH=/data/lancedb -HANZO_DB_BACKUP_ENABLED=true -HANZO_DB_BACKUP_INTERVAL=3600 - -# LLM Configuration -HANZO_LLM_MODEL=gpt-4o-mini -HANZO_LLM_TEMPERATURE=0.3 -HANZO_LLM_MAX_TOKENS=2000 -OPENAI_API_KEY=your-openai-key - -# Embedding Configuration -HANZO_EMBEDDING_MODEL=BAAI/bge-small-en-v1.5 -HANZO_EMBEDDING_BATCH_SIZE=100 -HANZO_EMBEDDING_DEVICE=cuda - -# Performance -HANZO_WORKERS=4 -HANZO_MAX_CONNECTIONS=1000 -HANZO_CACHE_ENABLED=true -HANZO_CACHE_TTL=3600 - -# Monitoring -HANZO_METRICS_ENABLED=true -HANZO_METRICS_PORT=9090 -HANZO_LOG_LEVEL=INFO -``` - -### Docker Compose Production - -```yaml -version: '3.8' - -services: - hanzo-memory: - image: hanzo/memory:latest - ports: - - "4000:4000" - - "9090:9090" # Metrics - volumes: - - ./data:/app/data - - ./logs:/app/logs - environment: - - HANZO_API_KEY=${HANZO_API_KEY} - - HANZO_DB_BACKEND=lancedb - - HANZO_WORKERS=4 - deploy: - resources: - limits: - cpus: '4' - memory: 8G - reservations: - cpus: '2' - memory: 4G - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:4000/health"] - interval: 30s - timeout: 10s - retries: 3 - - redis: - image: redis:7-alpine - volumes: - - redis_data:/data - command: redis-server --appendonly yes - - prometheus: - image: prom/prometheus - volumes: - - ./prometheus.yml:/etc/prometheus/prometheus.yml - - prometheus_data:/prometheus - ports: - - "9091:9090" - -volumes: - redis_data: - prometheus_data: -``` - -### Kubernetes Deployment - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hanzo-memory -spec: - replicas: 3 - selector: - matchLabels: - app: hanzo-memory - template: - metadata: - labels: - app: hanzo-memory - spec: - containers: - - name: hanzo-memory - image: hanzo/memory:latest - ports: - - containerPort: 4000 - - containerPort: 9090 - env: - - name: HANZO_API_KEY - valueFrom: - secretKeyRef: - name: hanzo-secrets - key: api-key - - name: OPENAI_API_KEY - valueFrom: - secretKeyRef: - name: hanzo-secrets - key: openai-key - volumeMounts: - - name: data - mountPath: /app/data - resources: - requests: - memory: "4Gi" - cpu: "2" - limits: - memory: "8Gi" - cpu: "4" - livenessProbe: - httpGet: - path: /health - port: 4000 - initialDelaySeconds: 30 - periodSeconds: 30 - volumes: - - name: data - persistentVolumeClaim: - claimName: hanzo-memory-data -``` - -### Monitoring Setup - -```yaml -# prometheus.yml -global: - scrape_interval: 15s - -scrape_configs: - - job_name: 'hanzo-memory' - static_configs: - - targets: ['hanzo-memory:9090'] - metrics_path: '/metrics' -``` - -### Backup Strategy - -```python -from hanzo_memory.backup import BackupService - -# Configure automatic backups -backup_service = BackupService( - source_path="/data/lancedb", - backup_path="/backups", - retention_days=30, - compression="gzip" -) - -# Schedule backups -backup_service.schedule_daily(hour=2, minute=0) - -# Manual backup -backup_path = await backup_service.backup_now( - description="Pre-deployment backup" -) - -# Restore from backup -await backup_service.restore( - backup_path=backup_path, - target_path="/data/lancedb_restored" -) -``` - -## Examples - -### AI Assistant with Memory - -```python -class MemoryAgent: - def __init__(self, memory_client, user_id): - self.memory = memory_client - self.user_id = user_id - self.session_id = None - - async def start_session(self): - session = await self.memory.create_chat_session( - user_id=self.user_id - ) - self.session_id = session.id - - async def process_message(self, message: str): - # Store user message - await self.memory.add_chat_message( - session_id=self.session_id, - role="user", - content=message - ) - - # Search relevant memories - memories = await self.memory.search_memories( - query=message, - user_id=self.user_id, - limit=5 - ) - - # Search knowledge base - facts = await self.memory.search_facts( - query=message, - limit=5 - ) - - # Generate response with context - context = self._build_context(memories, facts) - response = await self._generate_response(message, context) - - # Store assistant response - await self.memory.add_chat_message( - session_id=self.session_id, - role="assistant", - content=response - ) - - # Extract and store any new information - await self._extract_and_store_info(message, response) - - return response - - async def _extract_and_store_info(self, user_msg: str, assistant_msg: str): - # Extract important information - extraction = await self.memory.extract_knowledge( - content=f"User: {user_msg}\nAssistant: {assistant_msg}", - auto_add_facts=False - ) - - # Store as memories - for fact in extraction.facts: - if fact.confidence > 0.7: - await self.memory.remember( - content=fact.content, - user_id=self.user_id, - metadata={"source": "conversation", "confidence": fact.confidence} - ) -``` - -### Knowledge Base Builder - -```python -class KnowledgeBuilder: - def __init__(self, memory_client): - self.memory = memory_client - self.kb_cache = {} - - async def build_from_documents(self, documents: List[str], kb_name: str): - # Create knowledge base - kb = await self.memory.create_knowledge_base( - name=kb_name, - description=f"Knowledge extracted from {len(documents)} documents" - ) - - # Process each document - for i, doc in enumerate(documents): - print(f"Processing document {i+1}/{len(documents)}") - - # Extract knowledge - knowledge = await self.memory.extract_knowledge( - content=doc, - kb_id=kb.id, - auto_add_facts=True - ) - - # Store document as memory too - await self.memory.remember( - content=knowledge.summary, - metadata={ - "type": "document", - "kb_id": kb.id, - "doc_index": i - } - ) - - # Build relationships - await self._build_fact_relationships(kb.id) - - return kb - - async def _build_fact_relationships(self, kb_id: str): - # Get all facts - facts = await self.memory.list_facts(kb_id=kb_id, limit=1000) - - # Find related facts - for fact in facts: - similar = await self.memory.search_facts( - kb_id=kb_id, - query=fact.content, - exclude_ids=[fact.id], - limit=5, - threshold=0.8 - ) - - # Update fact with relationships - if similar: - await self.memory.update_fact( - fact_id=fact.id, - metadata={ - **fact.metadata, - "related_facts": [f.id for f in similar] - } - ) -``` - -### Memory-Augmented RAG System - -```python -class MemoryRAG: - def __init__(self, memory_client, llm_service): - self.memory = memory_client - self.llm = llm_service - - async def query(self, question: str, user_id: str, kb_ids: List[str] = None): - # Multi-stage retrieval - stage1_results = await self._broad_retrieval(question, user_id, kb_ids) - stage2_results = await self._focused_retrieval(question, stage1_results) - - # Build context - context = await self._build_augmented_context( - question, - stage2_results, - user_id - ) - - # Generate answer - answer = await self.llm.generate( - prompt=self._build_prompt(question, context), - temperature=0.3 - ) - - # Store Q&A as memory - await self.memory.remember( - content=f"Q: {question}\nA: {answer}", - user_id=user_id, - metadata={ - "type": "qa", - "sources": [r.id for r in stage2_results] - } - ) - - return { - "answer": answer, - "sources": stage2_results, - "confidence": self._calculate_confidence(stage2_results) - } - - async def _broad_retrieval(self, query: str, user_id: str, kb_ids: List[str]): - results = [] - - # Search memories - memories = await self.memory.search_memories( - query=query, - user_id=user_id, - limit=20 - ) - results.extend(memories) - - # Search knowledge bases - if kb_ids: - for kb_id in kb_ids: - facts = await self.memory.search_facts( - kb_id=kb_id, - query=query, - limit=20 - ) - results.extend(facts) - - return results - - async def _focused_retrieval(self, query: str, initial_results: List): - # Rerank using cross-encoder - reranked = await self.memory.rerank_results( - query=query, - results=initial_results, - top_k=10 - ) - - # Expand with related content - expanded = [] - for result in reranked[:5]: - if hasattr(result, 'metadata') and 'related_facts' in result.metadata: - related = await self.memory.get_facts(result.metadata['related_facts']) - expanded.extend(related) - - return reranked + expanded -``` - -## API Reference - -### Client Methods - -```python -# Memory operations -async def remember(content: str, user_id: str, **kwargs) -> Memory -async def get_memory(memory_id: str) -> Memory -async def search_memories(query: str, user_id: str, **kwargs) -> List[Memory] -async def update_memory(memory_id: str, **kwargs) -> Memory -async def delete_memory(memory_id: str) -> None -async def forget_memories(user_id: str, **kwargs) -> int - -# Knowledge base operations -async def create_knowledge_base(name: str, **kwargs) -> KnowledgeBase -async def get_knowledge_base(kb_id: str, **kwargs) -> KnowledgeBase -async def list_knowledge_bases(**kwargs) -> List[KnowledgeBase] -async def update_knowledge_base(kb_id: str, **kwargs) -> KnowledgeBase -async def delete_knowledge_base(kb_id: str, cascade: bool = False) -> None - -# Fact operations -async def add_fact(kb_id: str, content: str, **kwargs) -> Fact -async def get_fact(fact_id: str) -> Fact -async def search_facts(kb_id: str, query: str, **kwargs) -> List[Fact] -async def update_fact(fact_id: str, **kwargs) -> Fact -async def delete_fact(fact_id: str) -> None - -# Chat operations -async def create_chat_session(user_id: str, **kwargs) -> ChatSession -async def add_chat_message(session_id: str, role: str, content: str, **kwargs) -> ChatMessage -async def get_chat_history(session_id: str, **kwargs) -> List[ChatMessage] -async def search_chat_history(user_id: str, query: str, **kwargs) -> List[ChatMessage] - -# Advanced operations -async def unified_search(query: str, **kwargs) -> List[SearchResult] -async def extract_knowledge(content: str, **kwargs) -> KnowledgeExtraction -async def generate_answer(question: str, kb_ids: List[str], **kwargs) -> AnswerResult -``` - -### REST API Endpoints - -``` -# Memory endpoints -POST /v1/remember -GET /v1/memories/{memory_id} -GET /v1/memories -POST /v1/memories/search -PUT /v1/memories/{memory_id} -DELETE /v1/memories/{memory_id} -POST /v1/memories/forget - -# Knowledge base endpoints -POST /v1/kb -GET /v1/kb/{kb_id} -GET /v1/kb -PUT /v1/kb/{kb_id} -DELETE /v1/kb/{kb_id} - -# Fact endpoints -POST /v1/kb/{kb_id}/facts -GET /v1/facts/{fact_id} -POST /v1/kb/{kb_id}/facts/search -PUT /v1/facts/{fact_id} -DELETE /v1/facts/{fact_id} - -# Chat endpoints -POST /v1/chat/sessions -POST /v1/chat/sessions/{session_id}/messages -GET /v1/chat/sessions/{session_id}/messages -POST /v1/chat/search - -# Advanced endpoints -POST /v1/search -POST /v1/extract -POST /v1/answer -``` - -## Best Practices - -1. **Memory Hygiene**: Regularly consolidate and deduplicate memories -2. **Knowledge Organization**: Use hierarchical KBs for better organization -3. **Embedding Caching**: Cache embeddings for frequently accessed content -4. **Batch Operations**: Use batch APIs for better performance -5. **Security**: Always use API keys in production -6. **Monitoring**: Track memory usage and query performance -7. **Backup**: Regular backups of vector database -8. **Privacy**: Implement PII removal for sensitive data - -## Troubleshooting - -### Common Issues - -1. **Out of Memory**: Reduce embedding batch size or use smaller models -2. **Slow Searches**: Create indexes on vector columns -3. **API Timeouts**: Increase client timeout or use async operations -4. **Embedding Errors**: Check model compatibility and dimensions -5. **Database Corruption**: Restore from backup and check disk space - -### Debug Mode - -```python -# Enable debug logging -import logging -logging.basicConfig(level=logging.DEBUG) - -# Client with debug mode -client = MemoryClient( - base_url="http://localhost:4000", - api_key="your-api-key", - debug=True -) - -# Service with debug mode -HANZO_LOG_LEVEL=DEBUG hanzo-memory -``` - -For more help, see our [GitHub issues](https://github.com/hanzoai/memory/issues). \ No newline at end of file diff --git a/pkg/hanzo-memory/compose.yml b/pkg/hanzo-memory/compose.yml deleted file mode 100644 index d99c2c0c3..000000000 --- a/pkg/hanzo-memory/compose.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: '3.8' - -services: - memory: - build: . - ports: - - "4000:4000" - environment: - - HANZO_DISABLE_AUTH=true - - HANZO_LLM_MODEL=${HANZO_LLM_MODEL:-gpt-3.5-turbo} - - OPENAI_API_KEY=${OPENAI_API_KEY} - - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - - HANZO_LLM_API_BASE=${HANZO_LLM_API_BASE} - - HANZO_LOG_LEVEL=${HANZO_LOG_LEVEL:-INFO} - volumes: - - ./data:/app/data - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:4000/health"] - interval: 30s - timeout: 10s - retries: 3 - - # Optional: Redis for caching - redis: - image: redis:7-alpine - ports: - - "6379:6379" - volumes: - - redis_data:/data - profiles: - - with-cache - -volumes: - redis_data: \ No newline at end of file diff --git a/pkg/hanzo-memory/coverage.xml b/pkg/hanzo-memory/coverage.xml deleted file mode 100644 index 58b48c920..000000000 --- a/pkg/hanzo-memory/coverage.xml +++ /dev/null @@ -1,1276 +0,0 @@ - - - - - - /Users/z/work/hanzo/memory - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pkg/hanzo-memory/data/lancedb/chat_sessions.lance/_transactions/0-f3882b81-b95b-443e-b9a5-9d49b91103e8.txn b/pkg/hanzo-memory/data/lancedb/chat_sessions.lance/_transactions/0-f3882b81-b95b-443e-b9a5-9d49b91103e8.txn deleted file mode 100644 index 5f9736b77..000000000 --- a/pkg/hanzo-memory/data/lancedb/chat_sessions.lance/_transactions/0-f3882b81-b95b-443e-b9a5-9d49b91103e8.txn +++ /dev/null @@ -1,7 +0,0 @@ -$f3882b81-b95b-443e-b9a5-9d49b91103e8ฒู* -session_id *string8Zdefault)user_id *string8Zdefault, -project_id *string8Zdefault*metadata *string8Zdefault, -created_at *string8Zdefault, -updated_at *string8Zdefault"! -lance.auto_cleanup.interval20"' -lance.auto_cleanup.older_than14days \ No newline at end of file diff --git a/pkg/hanzo-memory/data/lancedb/chat_sessions.lance/_transactions/1-e69b8089-3525-4828-bf18-ee15618d37f7.txn b/pkg/hanzo-memory/data/lancedb/chat_sessions.lance/_transactions/1-e69b8089-3525-4828-bf18-ee15618d37f7.txn deleted file mode 100644 index 39701d1a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/chat_sessions.lance/_transactions/1-e69b8089-3525-4828-bf18-ee15618d37f7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/chat_sessions.lance/_versions/1.manifest b/pkg/hanzo-memory/data/lancedb/chat_sessions.lance/_versions/1.manifest deleted file mode 100644 index 5a2053522..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/chat_sessions.lance/_versions/1.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/chat_sessions.lance/_versions/2.manifest b/pkg/hanzo-memory/data/lancedb/chat_sessions.lance/_versions/2.manifest deleted file mode 100644 index 1c9c854a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/chat_sessions.lance/_versions/2.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/chat_sessions.lance/data/ddc760d8-98cc-4860-943d-0bc0fd522ab6.lance b/pkg/hanzo-memory/data/lancedb/chat_sessions.lance/data/ddc760d8-98cc-4860-943d-0bc0fd522ab6.lance deleted file mode 100644 index 9799e21ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/chat_sessions.lance/data/ddc760d8-98cc-4860-943d-0bc0fd522ab6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_transactions/.!74461!0-002f914c-1c88-41c5-8037-2b2bc4352372.txn b/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_transactions/.!74461!0-002f914c-1c88-41c5-8037-2b2bc4352372.txn deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_transactions/.!74831!0-002f914c-1c88-41c5-8037-2b2bc4352372.txn b/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_transactions/.!74831!0-002f914c-1c88-41c5-8037-2b2bc4352372.txn deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_transactions/0-002f914c-1c88-41c5-8037-2b2bc4352372.txn b/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_transactions/0-002f914c-1c88-41c5-8037-2b2bc4352372.txn deleted file mode 100644 index b4bdc8776..000000000 --- a/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_transactions/0-002f914c-1c88-41c5-8037-2b2bc4352372.txn +++ /dev/null @@ -1,6 +0,0 @@ -$002f914c-1c88-41c5-8037-2b2bc4352372ฒอ'fact_id *string8Zdefault3knowledge_base_id *string8Zdefault)content *string8Zdefault*metadata *string8Zdefault, -confidence *double8Zdefault, -created_at *string8Zdefault, -updated_at *string8Zdefault@ embedding *fixed_size_list:float:38408Zdefault"' -lance.auto_cleanup.older_than14days"! -lance.auto_cleanup.interval20 \ No newline at end of file diff --git a/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_transactions/1-a91bf279-3d49-41ce-8764-a6d55212b823.txn b/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_transactions/1-a91bf279-3d49-41ce-8764-a6d55212b823.txn deleted file mode 100644 index 838431d4a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_transactions/1-a91bf279-3d49-41ce-8764-a6d55212b823.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_versions/1.manifest b/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_versions/1.manifest deleted file mode 100644 index ab6fe2811..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_versions/1.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_versions/2.manifest b/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_versions/2.manifest deleted file mode 100644 index 80f698b16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/_versions/2.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/data/533da356-3fd0-4ef6-b8fa-d4af18be0df7.lance b/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/data/533da356-3fd0-4ef6-b8fa-d4af18be0df7.lance deleted file mode 100644 index 000e84b85..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/facts_test_kb.lance/data/533da356-3fd0-4ef6-b8fa-d4af18be0df7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/knowledge_bases.lance/_transactions/0-8bdfdbef-fdac-41f2-8700-608b84f2c9a4.txn b/pkg/hanzo-memory/data/lancedb/knowledge_bases.lance/_transactions/0-8bdfdbef-fdac-41f2-8700-608b84f2c9a4.txn deleted file mode 100644 index 8056e55bd..000000000 --- a/pkg/hanzo-memory/data/lancedb/knowledge_bases.lance/_transactions/0-8bdfdbef-fdac-41f2-8700-608b84f2c9a4.txn +++ /dev/null @@ -1,6 +0,0 @@ -$8bdfdbef-fdac-41f2-8700-608b84f2c9a4ฒŒ1knowledge_base_id *string8Zdefault, -project_id *string8Zdefault&name *string8Zdefault- description *string8Zdefault*metadata *string8Zdefault, -created_at *string8Zdefault, -updated_at *string8Zdefault"! -lance.auto_cleanup.interval20"' -lance.auto_cleanup.older_than14days \ No newline at end of file diff --git a/pkg/hanzo-memory/data/lancedb/knowledge_bases.lance/_transactions/1-7fb86500-ee9a-4631-bd2f-351d94f008bc.txn b/pkg/hanzo-memory/data/lancedb/knowledge_bases.lance/_transactions/1-7fb86500-ee9a-4631-bd2f-351d94f008bc.txn deleted file mode 100644 index ee14bc3f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/knowledge_bases.lance/_transactions/1-7fb86500-ee9a-4631-bd2f-351d94f008bc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/knowledge_bases.lance/_versions/1.manifest b/pkg/hanzo-memory/data/lancedb/knowledge_bases.lance/_versions/1.manifest deleted file mode 100644 index aa0a7313e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/knowledge_bases.lance/_versions/1.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/knowledge_bases.lance/_versions/2.manifest b/pkg/hanzo-memory/data/lancedb/knowledge_bases.lance/_versions/2.manifest deleted file mode 100644 index 427201e98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/knowledge_bases.lance/_versions/2.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/knowledge_bases.lance/data/00c6dc97-c886-40c5-92ab-631035e2fac5.lance b/pkg/hanzo-memory/data/lancedb/knowledge_bases.lance/data/00c6dc97-c886-40c5-92ab-631035e2fac5.lance deleted file mode 100644 index e3ede4197..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/knowledge_bases.lance/data/00c6dc97-c886-40c5-92ab-631035e2fac5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/0-1217a29a-e38a-4079-9b7c-9383336e366e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/0-1217a29a-e38a-4079-9b7c-9383336e366e.txn deleted file mode 100644 index d49360ebb..000000000 --- a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/0-1217a29a-e38a-4079-9b7c-9383336e366e.txn +++ /dev/null @@ -1,8 +0,0 @@ -$1217a29a-e38a-4079-9b7c-9383336e366eฒ๗) memory_id *string8Zdefault)user_id *string8Zdefault, -project_id *string8Zdefault)content *string8Zdefault*metadata *string8Zdefault, -importance *double8Zdefault- memory_type *string8Zdefault)context *string8Zdefault(source *string8Zdefault, -created_at *string8Zdefault, -updated_at - *string8Zdefault@ embedding *fixed_size_list:float:38408Zdefault"' -lance.auto_cleanup.older_than14days"! -lance.auto_cleanup.interval20 \ No newline at end of file diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1-df8169e0-5906-49cc-bcf6-26ffa57b3417.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1-df8169e0-5906-49cc-bcf6-26ffa57b3417.txn deleted file mode 100644 index 39f918ef0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1-df8169e0-5906-49cc-bcf6-26ffa57b3417.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/10-9caca930-27a8-43a7-9706-9d2d36a8124c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/10-9caca930-27a8-43a7-9706-9d2d36a8124c.txn deleted file mode 100644 index f454f0657..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/10-9caca930-27a8-43a7-9706-9d2d36a8124c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/100-cdb89f00-fec9-4f1e-940b-9ab1538c94b7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/100-cdb89f00-fec9-4f1e-940b-9ab1538c94b7.txn deleted file mode 100644 index 45790c387..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/100-cdb89f00-fec9-4f1e-940b-9ab1538c94b7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1000-29e9102d-a0bb-49af-8eeb-5a90e162ed73.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1000-29e9102d-a0bb-49af-8eeb-5a90e162ed73.txn deleted file mode 100644 index f90767bcf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1000-29e9102d-a0bb-49af-8eeb-5a90e162ed73.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1001-74ade769-c52a-4e1f-864d-6a60c50ea64d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1001-74ade769-c52a-4e1f-864d-6a60c50ea64d.txn deleted file mode 100644 index 88cafd6bf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1001-74ade769-c52a-4e1f-864d-6a60c50ea64d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1002-f6d496fa-5782-42ec-8ac6-d955e36d9224.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1002-f6d496fa-5782-42ec-8ac6-d955e36d9224.txn deleted file mode 100644 index 57b9d0efa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1002-f6d496fa-5782-42ec-8ac6-d955e36d9224.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1003-d85833e0-2047-4d35-97e9-b60d39e31106.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1003-d85833e0-2047-4d35-97e9-b60d39e31106.txn deleted file mode 100644 index 338d85709..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1003-d85833e0-2047-4d35-97e9-b60d39e31106.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1004-14c35ada-8447-4e29-afa3-2907ef7481e8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1004-14c35ada-8447-4e29-afa3-2907ef7481e8.txn deleted file mode 100644 index 709c133f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1004-14c35ada-8447-4e29-afa3-2907ef7481e8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1005-0d59eee9-74e3-469b-8314-9b7c34071050.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1005-0d59eee9-74e3-469b-8314-9b7c34071050.txn deleted file mode 100644 index 5b21ae4bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1005-0d59eee9-74e3-469b-8314-9b7c34071050.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1006-4325edbf-58d8-415e-aed2-cfe61d84f1d0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1006-4325edbf-58d8-415e-aed2-cfe61d84f1d0.txn deleted file mode 100644 index ae082bfe5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1006-4325edbf-58d8-415e-aed2-cfe61d84f1d0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1007-2b994ec2-2306-429e-856d-f56997f81ae2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1007-2b994ec2-2306-429e-856d-f56997f81ae2.txn deleted file mode 100644 index c12662a3d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1007-2b994ec2-2306-429e-856d-f56997f81ae2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1008-3b9ce1eb-fb67-4a8e-8c80-c239fce5c17a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1008-3b9ce1eb-fb67-4a8e-8c80-c239fce5c17a.txn deleted file mode 100644 index ea750bc04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1008-3b9ce1eb-fb67-4a8e-8c80-c239fce5c17a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1009-687fb88d-1582-4655-bc49-6c8060aaf194.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1009-687fb88d-1582-4655-bc49-6c8060aaf194.txn deleted file mode 100644 index 8c96fa12a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1009-687fb88d-1582-4655-bc49-6c8060aaf194.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/101-18874460-ca31-4633-a741-aaa3efa56724.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/101-18874460-ca31-4633-a741-aaa3efa56724.txn deleted file mode 100644 index c5226bdeb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/101-18874460-ca31-4633-a741-aaa3efa56724.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1010-a14f442b-ee46-4465-bee2-e81f4e3a3d8b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1010-a14f442b-ee46-4465-bee2-e81f4e3a3d8b.txn deleted file mode 100644 index 78177e07f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1010-a14f442b-ee46-4465-bee2-e81f4e3a3d8b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1011-713e4683-0226-4089-a2d4-c19f05776fda.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1011-713e4683-0226-4089-a2d4-c19f05776fda.txn deleted file mode 100644 index 8733669d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1011-713e4683-0226-4089-a2d4-c19f05776fda.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1012-f2ea1525-856e-46ef-b8a4-7516edb8a4e1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1012-f2ea1525-856e-46ef-b8a4-7516edb8a4e1.txn deleted file mode 100644 index 198a27a84..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1012-f2ea1525-856e-46ef-b8a4-7516edb8a4e1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1013-d2ceb82c-1f3d-4851-b958-852f548ba1d9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1013-d2ceb82c-1f3d-4851-b958-852f548ba1d9.txn deleted file mode 100644 index df653ce45..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1013-d2ceb82c-1f3d-4851-b958-852f548ba1d9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1014-5c996dad-90bf-4af9-98f9-0344cc16e5f1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1014-5c996dad-90bf-4af9-98f9-0344cc16e5f1.txn deleted file mode 100644 index 6102116ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1014-5c996dad-90bf-4af9-98f9-0344cc16e5f1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1015-d43c3223-2c9f-446a-a447-07db3c40d1ca.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1015-d43c3223-2c9f-446a-a447-07db3c40d1ca.txn deleted file mode 100644 index 8389489e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1015-d43c3223-2c9f-446a-a447-07db3c40d1ca.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1016-fab0a8ff-3f04-4c63-846d-523b66e3db30.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1016-fab0a8ff-3f04-4c63-846d-523b66e3db30.txn deleted file mode 100644 index 79be5627e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1016-fab0a8ff-3f04-4c63-846d-523b66e3db30.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1017-e3316571-d087-4868-8e1a-cb94e8b1eaf2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1017-e3316571-d087-4868-8e1a-cb94e8b1eaf2.txn deleted file mode 100644 index a6d00e29a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1017-e3316571-d087-4868-8e1a-cb94e8b1eaf2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1018-7766a6e5-76dd-41a6-a828-b17d706fde3a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1018-7766a6e5-76dd-41a6-a828-b17d706fde3a.txn deleted file mode 100644 index b87fb15cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1018-7766a6e5-76dd-41a6-a828-b17d706fde3a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1019-4686007e-2d24-4db2-94c1-4133ad92db16.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1019-4686007e-2d24-4db2-94c1-4133ad92db16.txn deleted file mode 100644 index b59829710..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1019-4686007e-2d24-4db2-94c1-4133ad92db16.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/102-cdd5b815-3901-489a-84de-c12c9033851c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/102-cdd5b815-3901-489a-84de-c12c9033851c.txn deleted file mode 100644 index 99671d024..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/102-cdd5b815-3901-489a-84de-c12c9033851c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1020-f8ad273c-7d4c-417d-8e99-a7fa79dcc1a6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1020-f8ad273c-7d4c-417d-8e99-a7fa79dcc1a6.txn deleted file mode 100644 index 6c31b762a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1020-f8ad273c-7d4c-417d-8e99-a7fa79dcc1a6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1021-3523d4a4-2fc8-4540-8e5a-90379679b59e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1021-3523d4a4-2fc8-4540-8e5a-90379679b59e.txn deleted file mode 100644 index 1d165776f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1021-3523d4a4-2fc8-4540-8e5a-90379679b59e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1022-c7f1ff08-d78a-4b3f-b965-9a9971792c03.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1022-c7f1ff08-d78a-4b3f-b965-9a9971792c03.txn deleted file mode 100644 index 32c707fef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1022-c7f1ff08-d78a-4b3f-b965-9a9971792c03.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1023-321f8586-3ea2-4552-8e2e-71138266bba9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1023-321f8586-3ea2-4552-8e2e-71138266bba9.txn deleted file mode 100644 index 573add81c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1023-321f8586-3ea2-4552-8e2e-71138266bba9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1024-b580bf01-b490-4da7-80d7-c9c67861c68b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1024-b580bf01-b490-4da7-80d7-c9c67861c68b.txn deleted file mode 100644 index e88f39907..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1024-b580bf01-b490-4da7-80d7-c9c67861c68b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1025-945a155d-973b-499e-80b7-e378c7c3eda1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1025-945a155d-973b-499e-80b7-e378c7c3eda1.txn deleted file mode 100644 index b87f01ae7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1025-945a155d-973b-499e-80b7-e378c7c3eda1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1026-a49f716a-8511-44b0-8c4d-8bb686a668c1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1026-a49f716a-8511-44b0-8c4d-8bb686a668c1.txn deleted file mode 100644 index 9fca03834..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1026-a49f716a-8511-44b0-8c4d-8bb686a668c1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1027-0b34b501-c363-4def-b9df-3aebd25dea45.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1027-0b34b501-c363-4def-b9df-3aebd25dea45.txn deleted file mode 100644 index ffd5b444d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1027-0b34b501-c363-4def-b9df-3aebd25dea45.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1028-a25ac407-bda9-4724-9a5c-7bf5a7605353.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1028-a25ac407-bda9-4724-9a5c-7bf5a7605353.txn deleted file mode 100644 index 9d2b2e437..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1028-a25ac407-bda9-4724-9a5c-7bf5a7605353.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1029-8538653a-3798-46a1-a166-22320705cfc1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1029-8538653a-3798-46a1-a166-22320705cfc1.txn deleted file mode 100644 index 88fc55535..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1029-8538653a-3798-46a1-a166-22320705cfc1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/103-b9bb3163-f4e0-4e39-b30a-43aa5b4888b3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/103-b9bb3163-f4e0-4e39-b30a-43aa5b4888b3.txn deleted file mode 100644 index 708433c7b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/103-b9bb3163-f4e0-4e39-b30a-43aa5b4888b3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1030-e5852d8c-35f5-410c-b18b-dca6d4543069.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1030-e5852d8c-35f5-410c-b18b-dca6d4543069.txn deleted file mode 100644 index 8fd071e5c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1030-e5852d8c-35f5-410c-b18b-dca6d4543069.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1031-8780637b-7dc7-4f04-9516-b5c22c6e5422.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1031-8780637b-7dc7-4f04-9516-b5c22c6e5422.txn deleted file mode 100644 index c3d4b65c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1031-8780637b-7dc7-4f04-9516-b5c22c6e5422.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1032-c060ab79-622a-4bc6-a33d-bdde6b9a1ffc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1032-c060ab79-622a-4bc6-a33d-bdde6b9a1ffc.txn deleted file mode 100644 index c627bc434..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1032-c060ab79-622a-4bc6-a33d-bdde6b9a1ffc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1033-103b14df-ae5f-49ba-b492-21ed4a3dbb4b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1033-103b14df-ae5f-49ba-b492-21ed4a3dbb4b.txn deleted file mode 100644 index 848edc247..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1033-103b14df-ae5f-49ba-b492-21ed4a3dbb4b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1034-56832b3d-1144-446b-b7d5-9386192748c5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1034-56832b3d-1144-446b-b7d5-9386192748c5.txn deleted file mode 100644 index 8e658e478..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1034-56832b3d-1144-446b-b7d5-9386192748c5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1035-3ddc56cb-b9a0-42ec-a630-2c36e5eea287.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1035-3ddc56cb-b9a0-42ec-a630-2c36e5eea287.txn deleted file mode 100644 index 8c205f321..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1035-3ddc56cb-b9a0-42ec-a630-2c36e5eea287.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1036-e5a8a495-09cf-4e29-91c1-36716f5e2552.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1036-e5a8a495-09cf-4e29-91c1-36716f5e2552.txn deleted file mode 100644 index 53bdca371..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1036-e5a8a495-09cf-4e29-91c1-36716f5e2552.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1037-be8295f8-cc5e-443e-a8ce-76df4fe97eaa.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1037-be8295f8-cc5e-443e-a8ce-76df4fe97eaa.txn deleted file mode 100644 index dce91c4e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1037-be8295f8-cc5e-443e-a8ce-76df4fe97eaa.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1038-7679e06a-9818-4aba-b109-f11d9ae0b878.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1038-7679e06a-9818-4aba-b109-f11d9ae0b878.txn deleted file mode 100644 index 7d29f3c6b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1038-7679e06a-9818-4aba-b109-f11d9ae0b878.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1039-be1772ee-2a43-4bee-905f-71a6f44f302d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1039-be1772ee-2a43-4bee-905f-71a6f44f302d.txn deleted file mode 100644 index c403cd3c3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1039-be1772ee-2a43-4bee-905f-71a6f44f302d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/104-3bc0958f-0777-42bf-a82d-32cf7f6da5d1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/104-3bc0958f-0777-42bf-a82d-32cf7f6da5d1.txn deleted file mode 100644 index 9c1d9280c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/104-3bc0958f-0777-42bf-a82d-32cf7f6da5d1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1040-47a795c7-aff7-4234-b28a-63d69bef95fe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1040-47a795c7-aff7-4234-b28a-63d69bef95fe.txn deleted file mode 100644 index a55c9b565..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1040-47a795c7-aff7-4234-b28a-63d69bef95fe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1041-e3f36ab2-27c4-4197-a872-2ceb93897c3f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1041-e3f36ab2-27c4-4197-a872-2ceb93897c3f.txn deleted file mode 100644 index bdcba6605..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1041-e3f36ab2-27c4-4197-a872-2ceb93897c3f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1042-e33a1e68-7f6c-474b-bb3b-158a60662fc6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1042-e33a1e68-7f6c-474b-bb3b-158a60662fc6.txn deleted file mode 100644 index 40face203..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1042-e33a1e68-7f6c-474b-bb3b-158a60662fc6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1043-1aacc80a-020d-4a69-8d4a-e18fd0f12fc3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1043-1aacc80a-020d-4a69-8d4a-e18fd0f12fc3.txn deleted file mode 100644 index 63209894f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1043-1aacc80a-020d-4a69-8d4a-e18fd0f12fc3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1044-c3407fee-064f-4115-9767-bdefa6eda7eb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1044-c3407fee-064f-4115-9767-bdefa6eda7eb.txn deleted file mode 100644 index eeeae4370..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1044-c3407fee-064f-4115-9767-bdefa6eda7eb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1045-0251f082-e47b-4b4d-be18-322579bccb5d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1045-0251f082-e47b-4b4d-be18-322579bccb5d.txn deleted file mode 100644 index 6a49fb708..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1045-0251f082-e47b-4b4d-be18-322579bccb5d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1046-9abe5820-ad06-4183-a64d-9d73c4ace2ee.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1046-9abe5820-ad06-4183-a64d-9d73c4ace2ee.txn deleted file mode 100644 index d42e8264a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1046-9abe5820-ad06-4183-a64d-9d73c4ace2ee.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1047-f77406b1-9190-4239-ac65-a8c4e9a6c1a7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1047-f77406b1-9190-4239-ac65-a8c4e9a6c1a7.txn deleted file mode 100644 index 8e36c6954..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1047-f77406b1-9190-4239-ac65-a8c4e9a6c1a7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1048-c4897808-54da-4186-9ffb-284de0218b05.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1048-c4897808-54da-4186-9ffb-284de0218b05.txn deleted file mode 100644 index 5f625b3b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1048-c4897808-54da-4186-9ffb-284de0218b05.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1049-290bd52a-5b40-446a-86ca-436ed9ad8c7a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1049-290bd52a-5b40-446a-86ca-436ed9ad8c7a.txn deleted file mode 100644 index 4b89b9b06..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1049-290bd52a-5b40-446a-86ca-436ed9ad8c7a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/105-f0f753d9-cbfc-46d0-9c37-1710e945a44e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/105-f0f753d9-cbfc-46d0-9c37-1710e945a44e.txn deleted file mode 100644 index 8aad16ec0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/105-f0f753d9-cbfc-46d0-9c37-1710e945a44e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1050-0526aee4-b9e3-4dd1-a794-797ce781b6ab.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1050-0526aee4-b9e3-4dd1-a794-797ce781b6ab.txn deleted file mode 100644 index e9a20a674..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1050-0526aee4-b9e3-4dd1-a794-797ce781b6ab.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1051-a6224c98-d695-411e-b7f8-2dfc81dee0ce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1051-a6224c98-d695-411e-b7f8-2dfc81dee0ce.txn deleted file mode 100644 index db038fb25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1051-a6224c98-d695-411e-b7f8-2dfc81dee0ce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1052-50d7bb61-e671-491d-993c-8208d31d381a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1052-50d7bb61-e671-491d-993c-8208d31d381a.txn deleted file mode 100644 index 712d01c81..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1052-50d7bb61-e671-491d-993c-8208d31d381a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1053-a232d0c0-6ddf-4078-b36a-5c3d7084f515.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1053-a232d0c0-6ddf-4078-b36a-5c3d7084f515.txn deleted file mode 100644 index 09f9f3330..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1053-a232d0c0-6ddf-4078-b36a-5c3d7084f515.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1054-a68a6779-420b-4f05-958c-1c3f3e35b6e9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1054-a68a6779-420b-4f05-958c-1c3f3e35b6e9.txn deleted file mode 100644 index e3f5ea809..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1054-a68a6779-420b-4f05-958c-1c3f3e35b6e9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1055-eb6f049a-e77a-447f-8501-1bb715e66de1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1055-eb6f049a-e77a-447f-8501-1bb715e66de1.txn deleted file mode 100644 index dc14e2b68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1055-eb6f049a-e77a-447f-8501-1bb715e66de1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1056-20d40360-9db6-4b31-b91b-a31c84f0a052.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1056-20d40360-9db6-4b31-b91b-a31c84f0a052.txn deleted file mode 100644 index 6651b5daf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1056-20d40360-9db6-4b31-b91b-a31c84f0a052.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1057-3a7e2e48-f0f3-4ef8-823e-6c34c344902a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1057-3a7e2e48-f0f3-4ef8-823e-6c34c344902a.txn deleted file mode 100644 index 350187b51..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1057-3a7e2e48-f0f3-4ef8-823e-6c34c344902a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1058-607e3c10-435e-4c89-b439-16ba16721489.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1058-607e3c10-435e-4c89-b439-16ba16721489.txn deleted file mode 100644 index 5c4f398bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1058-607e3c10-435e-4c89-b439-16ba16721489.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1059-0be13b69-6dee-4e64-8f6c-95b4e2b17afe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1059-0be13b69-6dee-4e64-8f6c-95b4e2b17afe.txn deleted file mode 100644 index e0539c4db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1059-0be13b69-6dee-4e64-8f6c-95b4e2b17afe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/106-b879a5a8-f853-484a-b814-af7bdc3731e7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/106-b879a5a8-f853-484a-b814-af7bdc3731e7.txn deleted file mode 100644 index d09d0244a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/106-b879a5a8-f853-484a-b814-af7bdc3731e7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1060-0e9c0f9c-89f6-4eb8-ab29-a8dd8b2102e7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1060-0e9c0f9c-89f6-4eb8-ab29-a8dd8b2102e7.txn deleted file mode 100644 index b5c13f273..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1060-0e9c0f9c-89f6-4eb8-ab29-a8dd8b2102e7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1061-2dfb07bc-6726-44f7-b8de-cc91ef801326.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1061-2dfb07bc-6726-44f7-b8de-cc91ef801326.txn deleted file mode 100644 index b2c776885..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1061-2dfb07bc-6726-44f7-b8de-cc91ef801326.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1062-10774856-5c72-459b-9f72-140cc712cac7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1062-10774856-5c72-459b-9f72-140cc712cac7.txn deleted file mode 100644 index 56de8c6a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1062-10774856-5c72-459b-9f72-140cc712cac7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1063-d7d3334c-ca63-4e02-9382-34097ff8d58a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1063-d7d3334c-ca63-4e02-9382-34097ff8d58a.txn deleted file mode 100644 index da60859c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1063-d7d3334c-ca63-4e02-9382-34097ff8d58a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1064-7ad490da-7901-43ba-bdfd-56134c7d1370.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1064-7ad490da-7901-43ba-bdfd-56134c7d1370.txn deleted file mode 100644 index f415a54cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1064-7ad490da-7901-43ba-bdfd-56134c7d1370.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1065-518f272a-a107-42cc-bf66-9dc15027921d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1065-518f272a-a107-42cc-bf66-9dc15027921d.txn deleted file mode 100644 index de2e1fb8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1065-518f272a-a107-42cc-bf66-9dc15027921d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1066-13186ef6-9333-4108-a11e-5c81e12848c5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1066-13186ef6-9333-4108-a11e-5c81e12848c5.txn deleted file mode 100644 index 3ddb23c0c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1066-13186ef6-9333-4108-a11e-5c81e12848c5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1067-b0053e84-6abb-4c53-a59e-d661275e23d4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1067-b0053e84-6abb-4c53-a59e-d661275e23d4.txn deleted file mode 100644 index a62cc5232..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1067-b0053e84-6abb-4c53-a59e-d661275e23d4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1068-2a0dff3b-d79c-4cc1-8d69-e83f4950dfb5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1068-2a0dff3b-d79c-4cc1-8d69-e83f4950dfb5.txn deleted file mode 100644 index cf59f7fa1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1068-2a0dff3b-d79c-4cc1-8d69-e83f4950dfb5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1069-87aa9221-6392-469e-b02e-2abb7b94657a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1069-87aa9221-6392-469e-b02e-2abb7b94657a.txn deleted file mode 100644 index 857ca514d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1069-87aa9221-6392-469e-b02e-2abb7b94657a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/107-99fb663a-8789-4c02-9a51-bb46e53df1a6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/107-99fb663a-8789-4c02-9a51-bb46e53df1a6.txn deleted file mode 100644 index 8fb7a342f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/107-99fb663a-8789-4c02-9a51-bb46e53df1a6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1070-10bc3a94-2e45-4361-ae35-ea179591fc34.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1070-10bc3a94-2e45-4361-ae35-ea179591fc34.txn deleted file mode 100644 index d6a7676f4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1070-10bc3a94-2e45-4361-ae35-ea179591fc34.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1071-54dc0574-777b-42bb-beca-49962314532f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1071-54dc0574-777b-42bb-beca-49962314532f.txn deleted file mode 100644 index fcc79850f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1071-54dc0574-777b-42bb-beca-49962314532f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1072-296cc46a-3d09-4430-b54a-3ac420255a9b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1072-296cc46a-3d09-4430-b54a-3ac420255a9b.txn deleted file mode 100644 index 28c61e3e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1072-296cc46a-3d09-4430-b54a-3ac420255a9b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1073-30e29ca3-7976-4595-adaf-fc242a3bbd22.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1073-30e29ca3-7976-4595-adaf-fc242a3bbd22.txn deleted file mode 100644 index 25894977b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1073-30e29ca3-7976-4595-adaf-fc242a3bbd22.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1074-7967e786-889b-4ce2-831f-2a3e8864cf99.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1074-7967e786-889b-4ce2-831f-2a3e8864cf99.txn deleted file mode 100644 index 3decc2afb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1074-7967e786-889b-4ce2-831f-2a3e8864cf99.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1075-5fd0d767-191d-4014-aecc-d22ec7c3e76c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1075-5fd0d767-191d-4014-aecc-d22ec7c3e76c.txn deleted file mode 100644 index f4e63b08b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1075-5fd0d767-191d-4014-aecc-d22ec7c3e76c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1076-447505d9-d6e1-4978-9be8-60643b7ad622.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1076-447505d9-d6e1-4978-9be8-60643b7ad622.txn deleted file mode 100644 index dcd8b342d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1076-447505d9-d6e1-4978-9be8-60643b7ad622.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1077-93a289b1-65f4-48b6-99d2-37d5c785b0df.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1077-93a289b1-65f4-48b6-99d2-37d5c785b0df.txn deleted file mode 100644 index c363ca31b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1077-93a289b1-65f4-48b6-99d2-37d5c785b0df.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1078-84e37d8c-cddf-4caa-8460-6895548f5cdd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1078-84e37d8c-cddf-4caa-8460-6895548f5cdd.txn deleted file mode 100644 index 4120c480d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1078-84e37d8c-cddf-4caa-8460-6895548f5cdd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1079-ebe5ae24-97e3-4c4b-b664-6045d1f25fd7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1079-ebe5ae24-97e3-4c4b-b664-6045d1f25fd7.txn deleted file mode 100644 index 357fef621..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1079-ebe5ae24-97e3-4c4b-b664-6045d1f25fd7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/108-836c23f8-637b-4d09-84cc-7755111d7f8b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/108-836c23f8-637b-4d09-84cc-7755111d7f8b.txn deleted file mode 100644 index f35ffba04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/108-836c23f8-637b-4d09-84cc-7755111d7f8b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1080-efb350c2-139f-4605-93de-761ff2ae75ba.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1080-efb350c2-139f-4605-93de-761ff2ae75ba.txn deleted file mode 100644 index f07d472e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1080-efb350c2-139f-4605-93de-761ff2ae75ba.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1081-c95d19ce-d1ba-4565-8d16-e3ddda1d8015.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1081-c95d19ce-d1ba-4565-8d16-e3ddda1d8015.txn deleted file mode 100644 index 5684e7aa5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1081-c95d19ce-d1ba-4565-8d16-e3ddda1d8015.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1082-fcc4d155-fd2f-41d9-85b3-b972343551ce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1082-fcc4d155-fd2f-41d9-85b3-b972343551ce.txn deleted file mode 100644 index c9aab6cfd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1082-fcc4d155-fd2f-41d9-85b3-b972343551ce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1083-0abf6350-1231-4c6c-9076-09c3ca6635de.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1083-0abf6350-1231-4c6c-9076-09c3ca6635de.txn deleted file mode 100644 index 52a13d6b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1083-0abf6350-1231-4c6c-9076-09c3ca6635de.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1084-f280b791-03e0-4362-b963-c575392f1e88.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1084-f280b791-03e0-4362-b963-c575392f1e88.txn deleted file mode 100644 index e47d16e8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1084-f280b791-03e0-4362-b963-c575392f1e88.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1085-2e138750-f5aa-4a83-a00f-0c3c60faa231.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1085-2e138750-f5aa-4a83-a00f-0c3c60faa231.txn deleted file mode 100644 index 55cb90c5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1085-2e138750-f5aa-4a83-a00f-0c3c60faa231.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1086-6ec8a329-48fa-4f99-95cc-0dd377c31cfb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1086-6ec8a329-48fa-4f99-95cc-0dd377c31cfb.txn deleted file mode 100644 index 408e5a43c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1086-6ec8a329-48fa-4f99-95cc-0dd377c31cfb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1087-6e187d41-0136-42be-bdec-44615348b380.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1087-6e187d41-0136-42be-bdec-44615348b380.txn deleted file mode 100644 index 929c2b53c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1087-6e187d41-0136-42be-bdec-44615348b380.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1088-7e4816bf-e53b-443a-a4d5-681d69361c00.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1088-7e4816bf-e53b-443a-a4d5-681d69361c00.txn deleted file mode 100644 index f6d370ce9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1088-7e4816bf-e53b-443a-a4d5-681d69361c00.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1089-fb4984e9-05e0-40d5-b1fd-c4742058d544.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1089-fb4984e9-05e0-40d5-b1fd-c4742058d544.txn deleted file mode 100644 index adb16969c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1089-fb4984e9-05e0-40d5-b1fd-c4742058d544.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/109-79a2b742-66d1-4e03-b34e-5a644a0240ec.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/109-79a2b742-66d1-4e03-b34e-5a644a0240ec.txn deleted file mode 100644 index 9ab713ab5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/109-79a2b742-66d1-4e03-b34e-5a644a0240ec.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1090-ee071568-3dc9-45c3-953f-acade0a2fade.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1090-ee071568-3dc9-45c3-953f-acade0a2fade.txn deleted file mode 100644 index 1a10e9088..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1090-ee071568-3dc9-45c3-953f-acade0a2fade.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1091-c3978fb1-f4d7-4c4b-98e2-da14635d40e4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1091-c3978fb1-f4d7-4c4b-98e2-da14635d40e4.txn deleted file mode 100644 index 8934a04ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1091-c3978fb1-f4d7-4c4b-98e2-da14635d40e4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1092-5e35ebf6-f1f0-4ad8-82d1-685fc9a8d544.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1092-5e35ebf6-f1f0-4ad8-82d1-685fc9a8d544.txn deleted file mode 100644 index 9c2cd479e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1092-5e35ebf6-f1f0-4ad8-82d1-685fc9a8d544.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1093-8051a0ff-217e-4b78-a4d2-96fb26fe463f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1093-8051a0ff-217e-4b78-a4d2-96fb26fe463f.txn deleted file mode 100644 index af4f1b233..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1093-8051a0ff-217e-4b78-a4d2-96fb26fe463f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1094-014628c6-6c49-4c01-8fbd-297f80c5afa0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1094-014628c6-6c49-4c01-8fbd-297f80c5afa0.txn deleted file mode 100644 index a76c5b52c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1094-014628c6-6c49-4c01-8fbd-297f80c5afa0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1095-dcb8d3e2-545a-435e-942d-27ab2847c4ac.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1095-dcb8d3e2-545a-435e-942d-27ab2847c4ac.txn deleted file mode 100644 index 89d2d2592..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1095-dcb8d3e2-545a-435e-942d-27ab2847c4ac.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1096-b8dd265f-af24-4211-b157-48fce89916f3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1096-b8dd265f-af24-4211-b157-48fce89916f3.txn deleted file mode 100644 index a9d129ccd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1096-b8dd265f-af24-4211-b157-48fce89916f3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1097-a83ff28d-e18f-40df-87f6-2e2ed16c2ad8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1097-a83ff28d-e18f-40df-87f6-2e2ed16c2ad8.txn deleted file mode 100644 index e72bb7f94..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1097-a83ff28d-e18f-40df-87f6-2e2ed16c2ad8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1098-0d4ef567-b9a7-4118-a956-faf6f25287e2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1098-0d4ef567-b9a7-4118-a956-faf6f25287e2.txn deleted file mode 100644 index 4ae868104..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1098-0d4ef567-b9a7-4118-a956-faf6f25287e2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1099-fc3ace49-2066-4fd8-81f0-9d23528e822d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1099-fc3ace49-2066-4fd8-81f0-9d23528e822d.txn deleted file mode 100644 index 7c67dc04a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1099-fc3ace49-2066-4fd8-81f0-9d23528e822d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/11-4778a03c-229d-40be-b381-2d11ff7e772b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/11-4778a03c-229d-40be-b381-2d11ff7e772b.txn deleted file mode 100644 index aa611a93b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/11-4778a03c-229d-40be-b381-2d11ff7e772b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/110-f4f7d8ff-e7ab-4742-921e-0c45d9261e65.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/110-f4f7d8ff-e7ab-4742-921e-0c45d9261e65.txn deleted file mode 100644 index 3d8ab997b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/110-f4f7d8ff-e7ab-4742-921e-0c45d9261e65.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1100-f6b15c83-aee6-4a70-b1a9-3763c0593090.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1100-f6b15c83-aee6-4a70-b1a9-3763c0593090.txn deleted file mode 100644 index 273847f04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1100-f6b15c83-aee6-4a70-b1a9-3763c0593090.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1101-fb55af6b-9b46-4196-9061-06ea1f7f45d6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1101-fb55af6b-9b46-4196-9061-06ea1f7f45d6.txn deleted file mode 100644 index 4c54a9744..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1101-fb55af6b-9b46-4196-9061-06ea1f7f45d6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1102-cfcd5167-74fc-45eb-8502-a9825b346b09.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1102-cfcd5167-74fc-45eb-8502-a9825b346b09.txn deleted file mode 100644 index 20e6d46a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1102-cfcd5167-74fc-45eb-8502-a9825b346b09.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1103-52ecbd94-5424-426b-acc2-4bd2ef3e993e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1103-52ecbd94-5424-426b-acc2-4bd2ef3e993e.txn deleted file mode 100644 index 2610b742e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1103-52ecbd94-5424-426b-acc2-4bd2ef3e993e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1104-a5c192b5-367d-4687-88f6-123fb220ae45.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1104-a5c192b5-367d-4687-88f6-123fb220ae45.txn deleted file mode 100644 index 8aa86f523..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1104-a5c192b5-367d-4687-88f6-123fb220ae45.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1105-7014c2df-047e-47a6-b10f-422d3a55ece3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1105-7014c2df-047e-47a6-b10f-422d3a55ece3.txn deleted file mode 100644 index 3af289d14..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1105-7014c2df-047e-47a6-b10f-422d3a55ece3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1106-5f827c44-769a-4aed-8019-abbeb65cb29c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1106-5f827c44-769a-4aed-8019-abbeb65cb29c.txn deleted file mode 100644 index 91490304f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1106-5f827c44-769a-4aed-8019-abbeb65cb29c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1107-78123593-075c-4c53-956b-c7a01ac31c24.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1107-78123593-075c-4c53-956b-c7a01ac31c24.txn deleted file mode 100644 index b8fbd58a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1107-78123593-075c-4c53-956b-c7a01ac31c24.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1108-4caaa323-f2a2-447f-b058-069c2d6f0a81.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1108-4caaa323-f2a2-447f-b058-069c2d6f0a81.txn deleted file mode 100644 index 92e07fab1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1108-4caaa323-f2a2-447f-b058-069c2d6f0a81.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1109-0b0bbd5f-6c02-4666-a908-fc75c41a02a2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1109-0b0bbd5f-6c02-4666-a908-fc75c41a02a2.txn deleted file mode 100644 index aa1cf9b81..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1109-0b0bbd5f-6c02-4666-a908-fc75c41a02a2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/111-ec5371c3-2710-4d99-ba4a-1982e21eec99.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/111-ec5371c3-2710-4d99-ba4a-1982e21eec99.txn deleted file mode 100644 index f2d8d3f74..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/111-ec5371c3-2710-4d99-ba4a-1982e21eec99.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1110-3856d271-98d9-4140-9956-5993776c10d8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1110-3856d271-98d9-4140-9956-5993776c10d8.txn deleted file mode 100644 index 4c27abb3a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1110-3856d271-98d9-4140-9956-5993776c10d8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1111-a44ba445-c6f8-41a8-a24d-7f98cdce6d0e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1111-a44ba445-c6f8-41a8-a24d-7f98cdce6d0e.txn deleted file mode 100644 index 51c224f9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1111-a44ba445-c6f8-41a8-a24d-7f98cdce6d0e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1112-2b5d2303-b7c1-4827-a00f-d07aca60863a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1112-2b5d2303-b7c1-4827-a00f-d07aca60863a.txn deleted file mode 100644 index eaaf54458..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1112-2b5d2303-b7c1-4827-a00f-d07aca60863a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1113-df7adf58-cbfe-49ba-9b60-4d57717fbec4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1113-df7adf58-cbfe-49ba-9b60-4d57717fbec4.txn deleted file mode 100644 index 81501152c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1113-df7adf58-cbfe-49ba-9b60-4d57717fbec4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1114-c20477fc-babf-4ef3-a9f0-d150bd022517.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1114-c20477fc-babf-4ef3-a9f0-d150bd022517.txn deleted file mode 100644 index dc95745ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1114-c20477fc-babf-4ef3-a9f0-d150bd022517.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1115-ce0052a2-f2cc-4c71-b1a5-6fe9767e240f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1115-ce0052a2-f2cc-4c71-b1a5-6fe9767e240f.txn deleted file mode 100644 index a0c524179..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1115-ce0052a2-f2cc-4c71-b1a5-6fe9767e240f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1116-147d10dd-8a4c-4ff9-9368-6aaafef57ffc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1116-147d10dd-8a4c-4ff9-9368-6aaafef57ffc.txn deleted file mode 100644 index 67a201fd7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1116-147d10dd-8a4c-4ff9-9368-6aaafef57ffc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1117-92a32146-76bd-4c43-8b7c-0997c4ded14e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1117-92a32146-76bd-4c43-8b7c-0997c4ded14e.txn deleted file mode 100644 index 1ed96141b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1117-92a32146-76bd-4c43-8b7c-0997c4ded14e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1118-43aea352-de75-42bc-946a-2661904d30b5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1118-43aea352-de75-42bc-946a-2661904d30b5.txn deleted file mode 100644 index c6d2a8715..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1118-43aea352-de75-42bc-946a-2661904d30b5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1119-9ab95ee4-081a-43d7-8877-a661eaeedcf5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1119-9ab95ee4-081a-43d7-8877-a661eaeedcf5.txn deleted file mode 100644 index 0c9cf64be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1119-9ab95ee4-081a-43d7-8877-a661eaeedcf5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/112-48b22878-aeec-4549-bc61-f6a45a5da75b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/112-48b22878-aeec-4549-bc61-f6a45a5da75b.txn deleted file mode 100644 index bf906c0ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/112-48b22878-aeec-4549-bc61-f6a45a5da75b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1120-ae375768-4738-4fb3-a32a-027bdf1a6426.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1120-ae375768-4738-4fb3-a32a-027bdf1a6426.txn deleted file mode 100644 index 32d90064e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1120-ae375768-4738-4fb3-a32a-027bdf1a6426.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1121-6956c8a0-2f84-4dd0-afc3-74770dd69f7f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1121-6956c8a0-2f84-4dd0-afc3-74770dd69f7f.txn deleted file mode 100644 index c676c76ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1121-6956c8a0-2f84-4dd0-afc3-74770dd69f7f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1122-e36e8c93-81b6-4aec-a373-1a3146ae7b99.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1122-e36e8c93-81b6-4aec-a373-1a3146ae7b99.txn deleted file mode 100644 index 397d66d2e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1122-e36e8c93-81b6-4aec-a373-1a3146ae7b99.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1123-7a36852c-2eb7-40c0-ae19-cdbe50f11a64.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1123-7a36852c-2eb7-40c0-ae19-cdbe50f11a64.txn deleted file mode 100644 index 900d1bd4a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1123-7a36852c-2eb7-40c0-ae19-cdbe50f11a64.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1124-83ba9a6c-afe0-4cc7-94b8-b50825540e2c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1124-83ba9a6c-afe0-4cc7-94b8-b50825540e2c.txn deleted file mode 100644 index 7198e4963..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1124-83ba9a6c-afe0-4cc7-94b8-b50825540e2c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1125-9f94f7ff-73ce-40d0-a4f9-a8a8232908de.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1125-9f94f7ff-73ce-40d0-a4f9-a8a8232908de.txn deleted file mode 100644 index a2ebe6a4a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1125-9f94f7ff-73ce-40d0-a4f9-a8a8232908de.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1126-6a3a34a5-460e-4edf-bfb4-a4bdf785ff43.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1126-6a3a34a5-460e-4edf-bfb4-a4bdf785ff43.txn deleted file mode 100644 index eb0caef8f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1126-6a3a34a5-460e-4edf-bfb4-a4bdf785ff43.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1127-d26f3eb9-0345-49ce-8127-f2ff56ed37bb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1127-d26f3eb9-0345-49ce-8127-f2ff56ed37bb.txn deleted file mode 100644 index 2c96999f5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1127-d26f3eb9-0345-49ce-8127-f2ff56ed37bb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1128-a55401f9-326e-41c5-acae-814c9df8cf41.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1128-a55401f9-326e-41c5-acae-814c9df8cf41.txn deleted file mode 100644 index 43245ea57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1128-a55401f9-326e-41c5-acae-814c9df8cf41.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1129-91a34dac-6027-4241-82b0-152c82935340.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1129-91a34dac-6027-4241-82b0-152c82935340.txn deleted file mode 100644 index ecc6d136e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1129-91a34dac-6027-4241-82b0-152c82935340.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/113-a4a0311c-62b8-4a41-b594-201fbbb06141.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/113-a4a0311c-62b8-4a41-b594-201fbbb06141.txn deleted file mode 100644 index af2289e70..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/113-a4a0311c-62b8-4a41-b594-201fbbb06141.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1130-441194a5-3b37-45e2-bfe2-cb3c6dabc2cf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1130-441194a5-3b37-45e2-bfe2-cb3c6dabc2cf.txn deleted file mode 100644 index fd9debfc3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1130-441194a5-3b37-45e2-bfe2-cb3c6dabc2cf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1131-e0f418ba-1a9d-4f6a-ac98-b84c5c503027.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1131-e0f418ba-1a9d-4f6a-ac98-b84c5c503027.txn deleted file mode 100644 index 906167974..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1131-e0f418ba-1a9d-4f6a-ac98-b84c5c503027.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1132-db95829d-6d6b-4b6c-af09-06ecc26508b1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1132-db95829d-6d6b-4b6c-af09-06ecc26508b1.txn deleted file mode 100644 index 6588ffbbb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1132-db95829d-6d6b-4b6c-af09-06ecc26508b1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1133-2ec2688e-0e03-4cee-9ddb-558f160c6c6c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1133-2ec2688e-0e03-4cee-9ddb-558f160c6c6c.txn deleted file mode 100644 index 241348620..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1133-2ec2688e-0e03-4cee-9ddb-558f160c6c6c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1134-128fa4b8-c09b-431b-9bde-b729370ffa91.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1134-128fa4b8-c09b-431b-9bde-b729370ffa91.txn deleted file mode 100644 index e4ed641a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1134-128fa4b8-c09b-431b-9bde-b729370ffa91.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1135-ad2c51b0-d730-4dea-aec4-c3f8aebb5e44.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1135-ad2c51b0-d730-4dea-aec4-c3f8aebb5e44.txn deleted file mode 100644 index 591446177..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1135-ad2c51b0-d730-4dea-aec4-c3f8aebb5e44.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1136-e8603dd2-94bf-47e6-b5bd-e8285d1ce737.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1136-e8603dd2-94bf-47e6-b5bd-e8285d1ce737.txn deleted file mode 100644 index ffeddf99b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1136-e8603dd2-94bf-47e6-b5bd-e8285d1ce737.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1137-d952bda8-6053-4d14-bfd5-1666f28325ba.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1137-d952bda8-6053-4d14-bfd5-1666f28325ba.txn deleted file mode 100644 index 6869f09ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1137-d952bda8-6053-4d14-bfd5-1666f28325ba.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1138-03926684-3c53-40c4-a0c8-2a65c00005f9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1138-03926684-3c53-40c4-a0c8-2a65c00005f9.txn deleted file mode 100644 index 72ae76514..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1138-03926684-3c53-40c4-a0c8-2a65c00005f9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1139-2821acee-0a03-4326-932e-b7244368b09d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1139-2821acee-0a03-4326-932e-b7244368b09d.txn deleted file mode 100644 index d52b85bdb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1139-2821acee-0a03-4326-932e-b7244368b09d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/114-57e83a01-83dc-40ea-81f1-d0276aabf0cf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/114-57e83a01-83dc-40ea-81f1-d0276aabf0cf.txn deleted file mode 100644 index 5ddeaab75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/114-57e83a01-83dc-40ea-81f1-d0276aabf0cf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1140-51f869f5-e430-45e9-97cf-a46e18844267.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1140-51f869f5-e430-45e9-97cf-a46e18844267.txn deleted file mode 100644 index 1687258c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1140-51f869f5-e430-45e9-97cf-a46e18844267.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1141-878f4026-1ba2-481a-a1da-dea8d97aae8e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1141-878f4026-1ba2-481a-a1da-dea8d97aae8e.txn deleted file mode 100644 index cac563c75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1141-878f4026-1ba2-481a-a1da-dea8d97aae8e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1142-1d731e8d-d34e-444e-9bab-363e0e357c04.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1142-1d731e8d-d34e-444e-9bab-363e0e357c04.txn deleted file mode 100644 index ade511cf0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1142-1d731e8d-d34e-444e-9bab-363e0e357c04.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1143-b8549503-4458-4f25-b643-048eb67bde1c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1143-b8549503-4458-4f25-b643-048eb67bde1c.txn deleted file mode 100644 index 115012b95..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1143-b8549503-4458-4f25-b643-048eb67bde1c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1144-cacb3b07-7a7f-490f-9d48-540a7739abd8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1144-cacb3b07-7a7f-490f-9d48-540a7739abd8.txn deleted file mode 100644 index 07683a5a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1144-cacb3b07-7a7f-490f-9d48-540a7739abd8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1145-6f69f750-b979-40b8-8185-3d4d749c9c28.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1145-6f69f750-b979-40b8-8185-3d4d749c9c28.txn deleted file mode 100644 index 37e82a6c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1145-6f69f750-b979-40b8-8185-3d4d749c9c28.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1146-69c34194-3783-4dc5-951e-296e4837821e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1146-69c34194-3783-4dc5-951e-296e4837821e.txn deleted file mode 100644 index 3fefb5961..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1146-69c34194-3783-4dc5-951e-296e4837821e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1147-dbb85c84-59df-4e79-a935-f2bda9a06b2c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1147-dbb85c84-59df-4e79-a935-f2bda9a06b2c.txn deleted file mode 100644 index 3c55a75b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1147-dbb85c84-59df-4e79-a935-f2bda9a06b2c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1148-965dc6b5-c923-4578-be84-f3f49d2dc96f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1148-965dc6b5-c923-4578-be84-f3f49d2dc96f.txn deleted file mode 100644 index 7b98ab5aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1148-965dc6b5-c923-4578-be84-f3f49d2dc96f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1149-5795959b-dfcd-49d0-b941-2e2beaf35a97.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1149-5795959b-dfcd-49d0-b941-2e2beaf35a97.txn deleted file mode 100644 index 31eb91b68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1149-5795959b-dfcd-49d0-b941-2e2beaf35a97.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/115-1376bc0d-8fd7-49fe-8460-e5a1576fc1f0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/115-1376bc0d-8fd7-49fe-8460-e5a1576fc1f0.txn deleted file mode 100644 index ef04ec638..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/115-1376bc0d-8fd7-49fe-8460-e5a1576fc1f0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1150-94383110-808d-4588-8f2d-ff815b929a86.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1150-94383110-808d-4588-8f2d-ff815b929a86.txn deleted file mode 100644 index 323de3ecc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1150-94383110-808d-4588-8f2d-ff815b929a86.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1151-1f92cd9f-f662-45d5-86e8-ed76c4c5dae8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1151-1f92cd9f-f662-45d5-86e8-ed76c4c5dae8.txn deleted file mode 100644 index c885383c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1151-1f92cd9f-f662-45d5-86e8-ed76c4c5dae8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1152-bca9d0cd-5822-4cfa-9b8b-562eda6db76d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1152-bca9d0cd-5822-4cfa-9b8b-562eda6db76d.txn deleted file mode 100644 index c9c86f695..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1152-bca9d0cd-5822-4cfa-9b8b-562eda6db76d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1153-19bdcb1c-812e-449f-9562-f6ed7a5503c1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1153-19bdcb1c-812e-449f-9562-f6ed7a5503c1.txn deleted file mode 100644 index 8230b6a9b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1153-19bdcb1c-812e-449f-9562-f6ed7a5503c1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1154-ed919204-58c0-4b42-b75e-ecee2c7a1861.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1154-ed919204-58c0-4b42-b75e-ecee2c7a1861.txn deleted file mode 100644 index 32a447a9d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1154-ed919204-58c0-4b42-b75e-ecee2c7a1861.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1155-43b0b752-c072-4540-abf5-5654d503f803.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1155-43b0b752-c072-4540-abf5-5654d503f803.txn deleted file mode 100644 index 15d21f8c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1155-43b0b752-c072-4540-abf5-5654d503f803.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1156-1c85ae7b-2a5a-49fc-8632-24ae5baca3e4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1156-1c85ae7b-2a5a-49fc-8632-24ae5baca3e4.txn deleted file mode 100644 index 0467184ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1156-1c85ae7b-2a5a-49fc-8632-24ae5baca3e4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1157-c32e753f-2414-4b38-8779-4dec757e6435.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1157-c32e753f-2414-4b38-8779-4dec757e6435.txn deleted file mode 100644 index f1ea1bc2f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1157-c32e753f-2414-4b38-8779-4dec757e6435.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1158-03e18d6c-abcb-4519-a792-830c6459358b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1158-03e18d6c-abcb-4519-a792-830c6459358b.txn deleted file mode 100644 index 8f3c6da80..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1158-03e18d6c-abcb-4519-a792-830c6459358b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1159-4b18a29a-6599-4464-bdd7-84702d004dc5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1159-4b18a29a-6599-4464-bdd7-84702d004dc5.txn deleted file mode 100644 index 27950aefe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1159-4b18a29a-6599-4464-bdd7-84702d004dc5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/116-247306a7-917b-452d-b46d-beb2f8276fc2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/116-247306a7-917b-452d-b46d-beb2f8276fc2.txn deleted file mode 100644 index 5593a9077..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/116-247306a7-917b-452d-b46d-beb2f8276fc2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1160-b602ab31-7398-4a12-a42b-26c4f1951cff.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1160-b602ab31-7398-4a12-a42b-26c4f1951cff.txn deleted file mode 100644 index ab8afa11b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1160-b602ab31-7398-4a12-a42b-26c4f1951cff.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1161-84bd5d95-fffa-48ed-8b9f-fc5aee56323e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1161-84bd5d95-fffa-48ed-8b9f-fc5aee56323e.txn deleted file mode 100644 index 83c857f6e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1161-84bd5d95-fffa-48ed-8b9f-fc5aee56323e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1162-0589e9f3-d732-489e-882c-3b0042102b9d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1162-0589e9f3-d732-489e-882c-3b0042102b9d.txn deleted file mode 100644 index 335833f91..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1162-0589e9f3-d732-489e-882c-3b0042102b9d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1163-257fd943-280b-41ca-82ca-11450a8b7ae8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1163-257fd943-280b-41ca-82ca-11450a8b7ae8.txn deleted file mode 100644 index 6573b0dc6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1163-257fd943-280b-41ca-82ca-11450a8b7ae8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1164-0b1e5e27-9822-4c1f-8cd3-99fcd62d8e61.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1164-0b1e5e27-9822-4c1f-8cd3-99fcd62d8e61.txn deleted file mode 100644 index 8061eac18..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1164-0b1e5e27-9822-4c1f-8cd3-99fcd62d8e61.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1165-3bcba96e-0d69-4990-8a64-057c439917ce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1165-3bcba96e-0d69-4990-8a64-057c439917ce.txn deleted file mode 100644 index ac2ae675b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1165-3bcba96e-0d69-4990-8a64-057c439917ce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1166-a5fe5540-1f3e-458f-be49-7a8e8ad7a838.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1166-a5fe5540-1f3e-458f-be49-7a8e8ad7a838.txn deleted file mode 100644 index 866a674a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1166-a5fe5540-1f3e-458f-be49-7a8e8ad7a838.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1167-fbf6e215-20e6-4805-a953-13991f99e376.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1167-fbf6e215-20e6-4805-a953-13991f99e376.txn deleted file mode 100644 index 1d729b76c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1167-fbf6e215-20e6-4805-a953-13991f99e376.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1168-6a436e15-a3c8-4a74-b9df-a21d0ab779ad.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1168-6a436e15-a3c8-4a74-b9df-a21d0ab779ad.txn deleted file mode 100644 index eca24de40..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1168-6a436e15-a3c8-4a74-b9df-a21d0ab779ad.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1169-383e42b3-6c9e-454b-abd5-593f9ab92477.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1169-383e42b3-6c9e-454b-abd5-593f9ab92477.txn deleted file mode 100644 index 8639e691c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1169-383e42b3-6c9e-454b-abd5-593f9ab92477.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/117-8a02aa35-b6bc-4f9d-afc8-ff26d6a58d4e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/117-8a02aa35-b6bc-4f9d-afc8-ff26d6a58d4e.txn deleted file mode 100644 index 9981acbcc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/117-8a02aa35-b6bc-4f9d-afc8-ff26d6a58d4e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1170-e5ce46b4-9ae6-4b73-aabe-ee8fd1737089.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1170-e5ce46b4-9ae6-4b73-aabe-ee8fd1737089.txn deleted file mode 100644 index aad324a11..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1170-e5ce46b4-9ae6-4b73-aabe-ee8fd1737089.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1171-2d82d53b-5616-4b01-b7d0-6dda5b8c5dcc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1171-2d82d53b-5616-4b01-b7d0-6dda5b8c5dcc.txn deleted file mode 100644 index 9488c3241..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1171-2d82d53b-5616-4b01-b7d0-6dda5b8c5dcc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1172-97d89802-8064-4939-a12b-1ddc58aea875.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1172-97d89802-8064-4939-a12b-1ddc58aea875.txn deleted file mode 100644 index b64e8dbac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1172-97d89802-8064-4939-a12b-1ddc58aea875.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1173-f1372e1a-819b-4461-be17-a696f0188ba8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1173-f1372e1a-819b-4461-be17-a696f0188ba8.txn deleted file mode 100644 index d6aebafae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1173-f1372e1a-819b-4461-be17-a696f0188ba8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1174-e5e6baf2-3107-4f04-9055-5d76b6ce9a66.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1174-e5e6baf2-3107-4f04-9055-5d76b6ce9a66.txn deleted file mode 100644 index ab335106a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1174-e5e6baf2-3107-4f04-9055-5d76b6ce9a66.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1175-1367e8d5-c32c-4c1b-b0c7-60253116bb65.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1175-1367e8d5-c32c-4c1b-b0c7-60253116bb65.txn deleted file mode 100644 index 7fc85640f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1175-1367e8d5-c32c-4c1b-b0c7-60253116bb65.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1176-8a1eefdc-adb2-4487-940f-bca012d2a743.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1176-8a1eefdc-adb2-4487-940f-bca012d2a743.txn deleted file mode 100644 index ccadfad00..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1176-8a1eefdc-adb2-4487-940f-bca012d2a743.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1177-cd4986e4-3e0f-4767-a80c-1bb6daf29d45.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1177-cd4986e4-3e0f-4767-a80c-1bb6daf29d45.txn deleted file mode 100644 index 9ade97d21..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1177-cd4986e4-3e0f-4767-a80c-1bb6daf29d45.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1178-9bd96ca9-0f45-4d23-aad9-35aa5f988a7c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1178-9bd96ca9-0f45-4d23-aad9-35aa5f988a7c.txn deleted file mode 100644 index 165d5298a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1178-9bd96ca9-0f45-4d23-aad9-35aa5f988a7c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1179-5d5cf440-cd7d-4609-9cd2-5a4e178622cd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1179-5d5cf440-cd7d-4609-9cd2-5a4e178622cd.txn deleted file mode 100644 index fb097f575..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1179-5d5cf440-cd7d-4609-9cd2-5a4e178622cd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/118-fe97c0a7-8757-4b81-953f-57092a3f9f97.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/118-fe97c0a7-8757-4b81-953f-57092a3f9f97.txn deleted file mode 100644 index 37e279dd6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/118-fe97c0a7-8757-4b81-953f-57092a3f9f97.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1180-0f0890d8-3302-4c98-98fb-61e7ded2f041.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1180-0f0890d8-3302-4c98-98fb-61e7ded2f041.txn deleted file mode 100644 index 377dc8300..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1180-0f0890d8-3302-4c98-98fb-61e7ded2f041.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1181-01a495bf-0a10-4885-bff6-7d657f1a8191.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1181-01a495bf-0a10-4885-bff6-7d657f1a8191.txn deleted file mode 100644 index 2e4edf95e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1181-01a495bf-0a10-4885-bff6-7d657f1a8191.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1182-06137285-4095-4940-838d-150d00b3c0ba.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1182-06137285-4095-4940-838d-150d00b3c0ba.txn deleted file mode 100644 index 532ad772c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1182-06137285-4095-4940-838d-150d00b3c0ba.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1183-0ea45ea3-dd6b-4ef4-bfc7-ef715c80dd56.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1183-0ea45ea3-dd6b-4ef4-bfc7-ef715c80dd56.txn deleted file mode 100644 index b1ad93576..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1183-0ea45ea3-dd6b-4ef4-bfc7-ef715c80dd56.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1184-7e2f63c4-5b0a-4f5e-bf19-6701419350bc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1184-7e2f63c4-5b0a-4f5e-bf19-6701419350bc.txn deleted file mode 100644 index cb866e5e0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1184-7e2f63c4-5b0a-4f5e-bf19-6701419350bc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1185-237aacd9-9e7d-4796-9b8e-ef65870ab4f9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1185-237aacd9-9e7d-4796-9b8e-ef65870ab4f9.txn deleted file mode 100644 index 806b2b562..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1185-237aacd9-9e7d-4796-9b8e-ef65870ab4f9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1186-0ef65d4d-3f03-4673-bd88-6241972bec73.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1186-0ef65d4d-3f03-4673-bd88-6241972bec73.txn deleted file mode 100644 index 9f44d3878..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1186-0ef65d4d-3f03-4673-bd88-6241972bec73.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1187-9555dafb-91d4-428b-aeae-e96b17dc4883.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1187-9555dafb-91d4-428b-aeae-e96b17dc4883.txn deleted file mode 100644 index 9f45c5828..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1187-9555dafb-91d4-428b-aeae-e96b17dc4883.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1188-5d56391a-67f0-491a-a426-ca2bdb624286.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1188-5d56391a-67f0-491a-a426-ca2bdb624286.txn deleted file mode 100644 index 5b648fbcf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1188-5d56391a-67f0-491a-a426-ca2bdb624286.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1189-7186219c-1d6c-4c96-8ffd-04cc498fd4d0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1189-7186219c-1d6c-4c96-8ffd-04cc498fd4d0.txn deleted file mode 100644 index fea8a7f14..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1189-7186219c-1d6c-4c96-8ffd-04cc498fd4d0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/119-0ddf362d-fdb7-41cb-a768-a2e09b826592.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/119-0ddf362d-fdb7-41cb-a768-a2e09b826592.txn deleted file mode 100644 index f7cfeb21d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/119-0ddf362d-fdb7-41cb-a768-a2e09b826592.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1190-8a0b352b-18b7-4b05-aaf7-da0dabd4fdf8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1190-8a0b352b-18b7-4b05-aaf7-da0dabd4fdf8.txn deleted file mode 100644 index df5e1f77a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1190-8a0b352b-18b7-4b05-aaf7-da0dabd4fdf8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1191-31c3c5c8-a267-4923-905d-d4c49e904d92.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1191-31c3c5c8-a267-4923-905d-d4c49e904d92.txn deleted file mode 100644 index 21e280a53..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1191-31c3c5c8-a267-4923-905d-d4c49e904d92.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1192-97a3167e-9060-416b-8a88-91c133da6fe3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1192-97a3167e-9060-416b-8a88-91c133da6fe3.txn deleted file mode 100644 index deb35dbe0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1192-97a3167e-9060-416b-8a88-91c133da6fe3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1193-ff4f7e75-0561-4bcf-b14e-72aa8f8e28c9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1193-ff4f7e75-0561-4bcf-b14e-72aa8f8e28c9.txn deleted file mode 100644 index 4bceefed2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1193-ff4f7e75-0561-4bcf-b14e-72aa8f8e28c9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1194-25926b48-14ec-4f6c-8f29-3d0d9f3964db.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1194-25926b48-14ec-4f6c-8f29-3d0d9f3964db.txn deleted file mode 100644 index 2e1ba6787..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1194-25926b48-14ec-4f6c-8f29-3d0d9f3964db.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1195-8e1d4be7-6bfc-410e-b874-fd7a908dc31f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1195-8e1d4be7-6bfc-410e-b874-fd7a908dc31f.txn deleted file mode 100644 index 10498c5f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1195-8e1d4be7-6bfc-410e-b874-fd7a908dc31f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1196-e211c4dc-e060-4f64-8086-df2e8c238c00.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1196-e211c4dc-e060-4f64-8086-df2e8c238c00.txn deleted file mode 100644 index 651a7d0a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1196-e211c4dc-e060-4f64-8086-df2e8c238c00.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1197-af16cfab-f00f-448a-ab6b-61ea5e16c4e5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1197-af16cfab-f00f-448a-ab6b-61ea5e16c4e5.txn deleted file mode 100644 index f4ee6ffbe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1197-af16cfab-f00f-448a-ab6b-61ea5e16c4e5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1198-070ac920-4251-4dbc-b4f0-90045358a9e8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1198-070ac920-4251-4dbc-b4f0-90045358a9e8.txn deleted file mode 100644 index 27390e59c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1198-070ac920-4251-4dbc-b4f0-90045358a9e8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1199-b0b59fa0-bae4-4b6a-9e5c-3908a8bb35db.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1199-b0b59fa0-bae4-4b6a-9e5c-3908a8bb35db.txn deleted file mode 100644 index 7b48f1d0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1199-b0b59fa0-bae4-4b6a-9e5c-3908a8bb35db.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/12-f7cf5b57-b458-4be0-9de7-d91683d9651a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/12-f7cf5b57-b458-4be0-9de7-d91683d9651a.txn deleted file mode 100644 index c30888d6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/12-f7cf5b57-b458-4be0-9de7-d91683d9651a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/120-63de3e88-11f4-4742-a6fc-338701fccd1a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/120-63de3e88-11f4-4742-a6fc-338701fccd1a.txn deleted file mode 100644 index 10f9002de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/120-63de3e88-11f4-4742-a6fc-338701fccd1a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1200-2493f6ab-717a-48a9-9aa8-3ad7b635b2fd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1200-2493f6ab-717a-48a9-9aa8-3ad7b635b2fd.txn deleted file mode 100644 index 2a03c38a0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1200-2493f6ab-717a-48a9-9aa8-3ad7b635b2fd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1201-54d6189b-0675-40cc-ab7c-47f35697fb70.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1201-54d6189b-0675-40cc-ab7c-47f35697fb70.txn deleted file mode 100644 index b7438394e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1201-54d6189b-0675-40cc-ab7c-47f35697fb70.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1202-4b8b1694-22a3-408c-90e6-a6081ebb5378.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1202-4b8b1694-22a3-408c-90e6-a6081ebb5378.txn deleted file mode 100644 index 02716f76b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1202-4b8b1694-22a3-408c-90e6-a6081ebb5378.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1203-8a9a681d-5664-4985-9357-366c4f6e2d33.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1203-8a9a681d-5664-4985-9357-366c4f6e2d33.txn deleted file mode 100644 index d83244274..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1203-8a9a681d-5664-4985-9357-366c4f6e2d33.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1204-93f4530e-7d9a-490c-8040-2ba049234761.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1204-93f4530e-7d9a-490c-8040-2ba049234761.txn deleted file mode 100644 index a228d3f06..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1204-93f4530e-7d9a-490c-8040-2ba049234761.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1205-5e2f15f7-f435-4f88-802f-5251bece61c7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1205-5e2f15f7-f435-4f88-802f-5251bece61c7.txn deleted file mode 100644 index 6e6d85db0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1205-5e2f15f7-f435-4f88-802f-5251bece61c7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1206-95e811a7-eed1-4ffd-a5c4-1f56dfd3f494.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1206-95e811a7-eed1-4ffd-a5c4-1f56dfd3f494.txn deleted file mode 100644 index e146ee45a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1206-95e811a7-eed1-4ffd-a5c4-1f56dfd3f494.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1207-b0780c5b-ef9e-4289-9765-1ee926e28bd5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1207-b0780c5b-ef9e-4289-9765-1ee926e28bd5.txn deleted file mode 100644 index eb005c5ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1207-b0780c5b-ef9e-4289-9765-1ee926e28bd5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1208-bbbf5831-317f-4c7f-9f0f-bcbec4e193e8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1208-bbbf5831-317f-4c7f-9f0f-bcbec4e193e8.txn deleted file mode 100644 index e30c7c794..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1208-bbbf5831-317f-4c7f-9f0f-bcbec4e193e8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1209-5e870639-ad37-446a-88be-71dc0cb0755d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1209-5e870639-ad37-446a-88be-71dc0cb0755d.txn deleted file mode 100644 index b04b46afe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1209-5e870639-ad37-446a-88be-71dc0cb0755d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/121-c197c21f-de7e-4cf1-9ad8-94e6eed1f6e8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/121-c197c21f-de7e-4cf1-9ad8-94e6eed1f6e8.txn deleted file mode 100644 index 5e59d4ab5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/121-c197c21f-de7e-4cf1-9ad8-94e6eed1f6e8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1210-33d06a34-81a2-40c7-b4eb-c6e71ce8ceb0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1210-33d06a34-81a2-40c7-b4eb-c6e71ce8ceb0.txn deleted file mode 100644 index 7dcfa7dbf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1210-33d06a34-81a2-40c7-b4eb-c6e71ce8ceb0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1211-716f6899-4270-4ab9-981b-eccd0ac37f76.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1211-716f6899-4270-4ab9-981b-eccd0ac37f76.txn deleted file mode 100644 index 61775d416..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1211-716f6899-4270-4ab9-981b-eccd0ac37f76.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1212-6355cb49-616a-49b3-8d5e-0a6b96380e8e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1212-6355cb49-616a-49b3-8d5e-0a6b96380e8e.txn deleted file mode 100644 index 26fa1fd25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1212-6355cb49-616a-49b3-8d5e-0a6b96380e8e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1213-9f053b69-f3f3-4653-b56d-387458d7501e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1213-9f053b69-f3f3-4653-b56d-387458d7501e.txn deleted file mode 100644 index 5521042a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1213-9f053b69-f3f3-4653-b56d-387458d7501e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1214-e70b0433-663e-4c55-9604-8dc3af752521.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1214-e70b0433-663e-4c55-9604-8dc3af752521.txn deleted file mode 100644 index dfcb4ddcb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1214-e70b0433-663e-4c55-9604-8dc3af752521.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1215-f3018793-000a-4f79-a2b7-02d6544dccb1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1215-f3018793-000a-4f79-a2b7-02d6544dccb1.txn deleted file mode 100644 index a29b1fbfd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1215-f3018793-000a-4f79-a2b7-02d6544dccb1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1216-c2286e81-4cdf-4a26-be21-c442e4a75f6d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1216-c2286e81-4cdf-4a26-be21-c442e4a75f6d.txn deleted file mode 100644 index fb20971e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1216-c2286e81-4cdf-4a26-be21-c442e4a75f6d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1217-49d8f158-3423-4d97-a50c-c1f05cfecb82.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1217-49d8f158-3423-4d97-a50c-c1f05cfecb82.txn deleted file mode 100644 index 139ca500e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1217-49d8f158-3423-4d97-a50c-c1f05cfecb82.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1218-60d3112b-b288-47f9-b3fd-1f18ec1a42bf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1218-60d3112b-b288-47f9-b3fd-1f18ec1a42bf.txn deleted file mode 100644 index f019033f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1218-60d3112b-b288-47f9-b3fd-1f18ec1a42bf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1219-da3743ac-a03f-4a16-9ed8-bd200eb51c3b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1219-da3743ac-a03f-4a16-9ed8-bd200eb51c3b.txn deleted file mode 100644 index 5088bd4f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1219-da3743ac-a03f-4a16-9ed8-bd200eb51c3b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/122-e964dd51-1ae8-409f-ba1a-b1e1baad2816.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/122-e964dd51-1ae8-409f-ba1a-b1e1baad2816.txn deleted file mode 100644 index 3e46a1042..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/122-e964dd51-1ae8-409f-ba1a-b1e1baad2816.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1220-28030aa1-55d1-454b-9825-3a3ad6394bda.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1220-28030aa1-55d1-454b-9825-3a3ad6394bda.txn deleted file mode 100644 index 6db43a024..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1220-28030aa1-55d1-454b-9825-3a3ad6394bda.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1221-874a267e-1407-4fb1-bb27-a9fb325bba2c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1221-874a267e-1407-4fb1-bb27-a9fb325bba2c.txn deleted file mode 100644 index 08585f9d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1221-874a267e-1407-4fb1-bb27-a9fb325bba2c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1222-16a9ce70-0450-4239-ad1c-0e4c3c0617ae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1222-16a9ce70-0450-4239-ad1c-0e4c3c0617ae.txn deleted file mode 100644 index ad03f8b1c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1222-16a9ce70-0450-4239-ad1c-0e4c3c0617ae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1223-4b7c3901-8bbf-4156-9f44-09ae21c77868.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1223-4b7c3901-8bbf-4156-9f44-09ae21c77868.txn deleted file mode 100644 index 528a292e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1223-4b7c3901-8bbf-4156-9f44-09ae21c77868.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1224-53edde8b-ae79-486e-8177-bd970dc7db0d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1224-53edde8b-ae79-486e-8177-bd970dc7db0d.txn deleted file mode 100644 index 00a74bb44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1224-53edde8b-ae79-486e-8177-bd970dc7db0d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1225-414cbe25-b524-47c3-bce8-3cfb49688152.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1225-414cbe25-b524-47c3-bce8-3cfb49688152.txn deleted file mode 100644 index 3a75463d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1225-414cbe25-b524-47c3-bce8-3cfb49688152.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1226-8f1713b0-beb2-4de6-8286-c4d394bf1853.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1226-8f1713b0-beb2-4de6-8286-c4d394bf1853.txn deleted file mode 100644 index 6a291cbb8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1226-8f1713b0-beb2-4de6-8286-c4d394bf1853.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1227-7d2d2f23-44e0-40bb-b2de-2d87c9947226.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1227-7d2d2f23-44e0-40bb-b2de-2d87c9947226.txn deleted file mode 100644 index 4dacb7595..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1227-7d2d2f23-44e0-40bb-b2de-2d87c9947226.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1228-bded1c03-b8cf-4f4f-a8a6-420edd713f9f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1228-bded1c03-b8cf-4f4f-a8a6-420edd713f9f.txn deleted file mode 100644 index 8ba763b12..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1228-bded1c03-b8cf-4f4f-a8a6-420edd713f9f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1229-298d3cfe-5f76-476a-8866-f9c99f68c1a8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1229-298d3cfe-5f76-476a-8866-f9c99f68c1a8.txn deleted file mode 100644 index d05f7ccfb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1229-298d3cfe-5f76-476a-8866-f9c99f68c1a8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/123-219a3626-5718-442b-b213-79398f89d9ba.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/123-219a3626-5718-442b-b213-79398f89d9ba.txn deleted file mode 100644 index 5343541ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/123-219a3626-5718-442b-b213-79398f89d9ba.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1230-626cb58f-8ddd-4163-9fa8-7f93ef47c9d5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1230-626cb58f-8ddd-4163-9fa8-7f93ef47c9d5.txn deleted file mode 100644 index db526a0d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1230-626cb58f-8ddd-4163-9fa8-7f93ef47c9d5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1231-7c5f47ba-ba8f-479b-bcfa-f6d2e6a5babe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1231-7c5f47ba-ba8f-479b-bcfa-f6d2e6a5babe.txn deleted file mode 100644 index e134c98fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1231-7c5f47ba-ba8f-479b-bcfa-f6d2e6a5babe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1232-f4b33967-f4c6-491c-a7c7-ed4e2b5b12ae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1232-f4b33967-f4c6-491c-a7c7-ed4e2b5b12ae.txn deleted file mode 100644 index d1c988636..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1232-f4b33967-f4c6-491c-a7c7-ed4e2b5b12ae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1233-2292b642-2771-41ee-bcdf-00c6e143cb03.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1233-2292b642-2771-41ee-bcdf-00c6e143cb03.txn deleted file mode 100644 index d6f9b430b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1233-2292b642-2771-41ee-bcdf-00c6e143cb03.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1234-c1302ea0-a4ea-4d41-9297-1cecf5f1e9ac.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1234-c1302ea0-a4ea-4d41-9297-1cecf5f1e9ac.txn deleted file mode 100644 index 707231ef1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1234-c1302ea0-a4ea-4d41-9297-1cecf5f1e9ac.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1235-ea921ecf-a04a-475c-b269-1833424725fe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1235-ea921ecf-a04a-475c-b269-1833424725fe.txn deleted file mode 100644 index 7a6e8a9ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1235-ea921ecf-a04a-475c-b269-1833424725fe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1236-86c697b4-3a47-4055-818f-787f92f4f7fd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1236-86c697b4-3a47-4055-818f-787f92f4f7fd.txn deleted file mode 100644 index d96e39b5c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1236-86c697b4-3a47-4055-818f-787f92f4f7fd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1237-33ea4c3b-c655-4f72-88fb-70fc2ce0cd0a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1237-33ea4c3b-c655-4f72-88fb-70fc2ce0cd0a.txn deleted file mode 100644 index aeefe7b2c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1237-33ea4c3b-c655-4f72-88fb-70fc2ce0cd0a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1238-ca3a5477-718e-486c-901d-000c4d0f48c9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1238-ca3a5477-718e-486c-901d-000c4d0f48c9.txn deleted file mode 100644 index 854f52af2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1238-ca3a5477-718e-486c-901d-000c4d0f48c9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1239-7e5a0912-6ed5-4961-a0ba-ec7e112bd9e5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1239-7e5a0912-6ed5-4961-a0ba-ec7e112bd9e5.txn deleted file mode 100644 index 77c35d9c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1239-7e5a0912-6ed5-4961-a0ba-ec7e112bd9e5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/124-1bc8042c-a759-4306-b9fb-a9d41d2abacb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/124-1bc8042c-a759-4306-b9fb-a9d41d2abacb.txn deleted file mode 100644 index 97f33da28..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/124-1bc8042c-a759-4306-b9fb-a9d41d2abacb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1240-f99931b3-db6d-4081-975c-7bd01b20f45f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1240-f99931b3-db6d-4081-975c-7bd01b20f45f.txn deleted file mode 100644 index 2f4b0d575..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1240-f99931b3-db6d-4081-975c-7bd01b20f45f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1241-9929214f-b69a-47b7-90a7-a7693ee55085.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1241-9929214f-b69a-47b7-90a7-a7693ee55085.txn deleted file mode 100644 index fc259426d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1241-9929214f-b69a-47b7-90a7-a7693ee55085.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1242-1c76fb64-9676-4a5f-9c78-3331b0dc29af.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1242-1c76fb64-9676-4a5f-9c78-3331b0dc29af.txn deleted file mode 100644 index 6294e1beb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1242-1c76fb64-9676-4a5f-9c78-3331b0dc29af.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1243-dc1b642d-d199-40db-b922-fafc1a0631a3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1243-dc1b642d-d199-40db-b922-fafc1a0631a3.txn deleted file mode 100644 index 060c25884..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1243-dc1b642d-d199-40db-b922-fafc1a0631a3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1244-654ba2a3-f657-4409-8bb2-c7a049ad1d39.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1244-654ba2a3-f657-4409-8bb2-c7a049ad1d39.txn deleted file mode 100644 index 8def5d7b2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1244-654ba2a3-f657-4409-8bb2-c7a049ad1d39.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1245-057a244f-9bfc-4799-92d7-a768ef67b7f1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1245-057a244f-9bfc-4799-92d7-a768ef67b7f1.txn deleted file mode 100644 index 388227447..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1245-057a244f-9bfc-4799-92d7-a768ef67b7f1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1246-e805cc17-e744-4523-9b9e-47b45da0fe1b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1246-e805cc17-e744-4523-9b9e-47b45da0fe1b.txn deleted file mode 100644 index 19d54e7f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1246-e805cc17-e744-4523-9b9e-47b45da0fe1b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1247-1231fcad-3724-422d-b367-72efa4367d5e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1247-1231fcad-3724-422d-b367-72efa4367d5e.txn deleted file mode 100644 index 072299a4b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1247-1231fcad-3724-422d-b367-72efa4367d5e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1248-a8d2763c-8b0f-49f6-a38c-66cddf51e265.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1248-a8d2763c-8b0f-49f6-a38c-66cddf51e265.txn deleted file mode 100644 index 927996c62..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1248-a8d2763c-8b0f-49f6-a38c-66cddf51e265.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1249-5690eac6-6a39-4b17-9987-bf58f345993a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1249-5690eac6-6a39-4b17-9987-bf58f345993a.txn deleted file mode 100644 index 7635ac22f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1249-5690eac6-6a39-4b17-9987-bf58f345993a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/125-8061f2b2-47e4-4125-9df9-bcb58e3d0741.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/125-8061f2b2-47e4-4125-9df9-bcb58e3d0741.txn deleted file mode 100644 index d36ae3689..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/125-8061f2b2-47e4-4125-9df9-bcb58e3d0741.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1250-ea0f065f-85ed-4ec2-9b35-47ffa58c299b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1250-ea0f065f-85ed-4ec2-9b35-47ffa58c299b.txn deleted file mode 100644 index 3ab9e5c0a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1250-ea0f065f-85ed-4ec2-9b35-47ffa58c299b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1251-b00a8bbd-a4e1-4f31-b9d5-0a08ae68a8a1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1251-b00a8bbd-a4e1-4f31-b9d5-0a08ae68a8a1.txn deleted file mode 100644 index 934ea20f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1251-b00a8bbd-a4e1-4f31-b9d5-0a08ae68a8a1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1252-bcb69ecd-357b-46d1-b119-36fc0ecae6d4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1252-bcb69ecd-357b-46d1-b119-36fc0ecae6d4.txn deleted file mode 100644 index 73f18724f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1252-bcb69ecd-357b-46d1-b119-36fc0ecae6d4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1253-25d062ec-b841-4c41-ab65-9bd3bf8d0397.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1253-25d062ec-b841-4c41-ab65-9bd3bf8d0397.txn deleted file mode 100644 index 9949ed794..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1253-25d062ec-b841-4c41-ab65-9bd3bf8d0397.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1254-cc219816-d164-44ef-bf2d-7f62e03813d7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1254-cc219816-d164-44ef-bf2d-7f62e03813d7.txn deleted file mode 100644 index adb7fbf82..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1254-cc219816-d164-44ef-bf2d-7f62e03813d7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1255-91c0bd03-2375-4e05-a5a6-19875a1fe9c6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1255-91c0bd03-2375-4e05-a5a6-19875a1fe9c6.txn deleted file mode 100644 index d151b2364..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1255-91c0bd03-2375-4e05-a5a6-19875a1fe9c6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1256-0c5aa0d3-1f82-46a4-a06f-a0065c586b0f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1256-0c5aa0d3-1f82-46a4-a06f-a0065c586b0f.txn deleted file mode 100644 index 4c184b73f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1256-0c5aa0d3-1f82-46a4-a06f-a0065c586b0f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1257-80d64c41-76bb-4d01-95dc-1a4fcd623c95.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1257-80d64c41-76bb-4d01-95dc-1a4fcd623c95.txn deleted file mode 100644 index d0ac16409..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1257-80d64c41-76bb-4d01-95dc-1a4fcd623c95.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1258-d2692c55-435c-4717-8af4-2784e0df97e6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1258-d2692c55-435c-4717-8af4-2784e0df97e6.txn deleted file mode 100644 index 5be39cb9b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1258-d2692c55-435c-4717-8af4-2784e0df97e6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1259-2016c301-f7d9-4f31-aa4d-647a9af7da5c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1259-2016c301-f7d9-4f31-aa4d-647a9af7da5c.txn deleted file mode 100644 index eaf8a410b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1259-2016c301-f7d9-4f31-aa4d-647a9af7da5c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/126-ea6e873b-469e-4799-b633-893eb3fec09c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/126-ea6e873b-469e-4799-b633-893eb3fec09c.txn deleted file mode 100644 index 8c985c597..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/126-ea6e873b-469e-4799-b633-893eb3fec09c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1260-218e93ce-30a8-4ebc-a54b-64e6b68f5646.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1260-218e93ce-30a8-4ebc-a54b-64e6b68f5646.txn deleted file mode 100644 index 8f0f67b44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1260-218e93ce-30a8-4ebc-a54b-64e6b68f5646.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1261-f8a36106-042f-4771-8e05-140ff03caf1c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1261-f8a36106-042f-4771-8e05-140ff03caf1c.txn deleted file mode 100644 index d4b0393f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1261-f8a36106-042f-4771-8e05-140ff03caf1c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1262-9b4c77ee-2de6-48bd-a5a2-959181c1818e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1262-9b4c77ee-2de6-48bd-a5a2-959181c1818e.txn deleted file mode 100644 index b68c9c670..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1262-9b4c77ee-2de6-48bd-a5a2-959181c1818e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1263-cfea44a5-29cd-45e6-896a-dc57c3354177.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1263-cfea44a5-29cd-45e6-896a-dc57c3354177.txn deleted file mode 100644 index 4845b8153..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1263-cfea44a5-29cd-45e6-896a-dc57c3354177.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1264-a2bc29ef-bc92-40fc-85f3-dfd9596fc3bf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1264-a2bc29ef-bc92-40fc-85f3-dfd9596fc3bf.txn deleted file mode 100644 index 7b2ad666b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1264-a2bc29ef-bc92-40fc-85f3-dfd9596fc3bf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1265-16379108-f3b8-4239-a8d8-7665c17ede48.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1265-16379108-f3b8-4239-a8d8-7665c17ede48.txn deleted file mode 100644 index b04d74b01..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1265-16379108-f3b8-4239-a8d8-7665c17ede48.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1266-1b5386e2-7249-44a0-95e7-977877b750cc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1266-1b5386e2-7249-44a0-95e7-977877b750cc.txn deleted file mode 100644 index c405e133b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1266-1b5386e2-7249-44a0-95e7-977877b750cc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1267-1b0f0fe1-fc44-4c8c-ad25-153e01b346e5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1267-1b0f0fe1-fc44-4c8c-ad25-153e01b346e5.txn deleted file mode 100644 index 370b68340..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1267-1b0f0fe1-fc44-4c8c-ad25-153e01b346e5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1268-171c2928-2d06-4eee-8f64-a790ecfac5e8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1268-171c2928-2d06-4eee-8f64-a790ecfac5e8.txn deleted file mode 100644 index 8b1eaf247..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1268-171c2928-2d06-4eee-8f64-a790ecfac5e8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1269-d1879e25-9072-4fe2-9a49-392fa99587ce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1269-d1879e25-9072-4fe2-9a49-392fa99587ce.txn deleted file mode 100644 index 7892c55d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1269-d1879e25-9072-4fe2-9a49-392fa99587ce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/127-ed32973e-58cf-4781-96bb-98b73e765fad.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/127-ed32973e-58cf-4781-96bb-98b73e765fad.txn deleted file mode 100644 index 81cb236af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/127-ed32973e-58cf-4781-96bb-98b73e765fad.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1270-a0843ca2-4d90-4d49-bb5e-56c32e0510b2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1270-a0843ca2-4d90-4d49-bb5e-56c32e0510b2.txn deleted file mode 100644 index 088ff5d8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1270-a0843ca2-4d90-4d49-bb5e-56c32e0510b2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1271-29c491ca-0265-4452-9b6c-7b4dfefbb5d3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1271-29c491ca-0265-4452-9b6c-7b4dfefbb5d3.txn deleted file mode 100644 index 9079cb96a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1271-29c491ca-0265-4452-9b6c-7b4dfefbb5d3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1272-32918682-c543-4b8d-996a-3309c11ebe5e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1272-32918682-c543-4b8d-996a-3309c11ebe5e.txn deleted file mode 100644 index 9cc9efffb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1272-32918682-c543-4b8d-996a-3309c11ebe5e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1273-c7386787-73f6-488a-9559-0f3c6b4f1736.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1273-c7386787-73f6-488a-9559-0f3c6b4f1736.txn deleted file mode 100644 index b4ec8a1cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1273-c7386787-73f6-488a-9559-0f3c6b4f1736.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1274-a65cafa7-a4a6-4290-a3b9-7edcaa4f582f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1274-a65cafa7-a4a6-4290-a3b9-7edcaa4f582f.txn deleted file mode 100644 index cbc714e44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1274-a65cafa7-a4a6-4290-a3b9-7edcaa4f582f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1275-39d9de5e-1d2e-4ea8-b857-0f9da80fdce8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1275-39d9de5e-1d2e-4ea8-b857-0f9da80fdce8.txn deleted file mode 100644 index 66ab44cbd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1275-39d9de5e-1d2e-4ea8-b857-0f9da80fdce8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1276-d7d0b9ac-9ac9-4d52-a9b5-f5a56adb8b90.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1276-d7d0b9ac-9ac9-4d52-a9b5-f5a56adb8b90.txn deleted file mode 100644 index 29d06fda6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1276-d7d0b9ac-9ac9-4d52-a9b5-f5a56adb8b90.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1277-6416858a-a878-4482-9f67-3448e839859c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1277-6416858a-a878-4482-9f67-3448e839859c.txn deleted file mode 100644 index 5e1434018..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1277-6416858a-a878-4482-9f67-3448e839859c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1278-d963c97d-94fc-4732-9475-feb736e75763.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1278-d963c97d-94fc-4732-9475-feb736e75763.txn deleted file mode 100644 index f3601894e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1278-d963c97d-94fc-4732-9475-feb736e75763.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1279-38fbe45b-7663-458c-9ff0-3178d7a8c417.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1279-38fbe45b-7663-458c-9ff0-3178d7a8c417.txn deleted file mode 100644 index f3bd29524..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1279-38fbe45b-7663-458c-9ff0-3178d7a8c417.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/128-3ebf3bc3-a186-44a5-a15a-5b54218624b7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/128-3ebf3bc3-a186-44a5-a15a-5b54218624b7.txn deleted file mode 100644 index 60aef9a2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/128-3ebf3bc3-a186-44a5-a15a-5b54218624b7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1280-b3542419-539a-4344-a03c-811980b0f9bd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1280-b3542419-539a-4344-a03c-811980b0f9bd.txn deleted file mode 100644 index d2b5c1614..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1280-b3542419-539a-4344-a03c-811980b0f9bd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1281-2b47dc3e-f507-4b13-89d4-7a4a06876a79.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1281-2b47dc3e-f507-4b13-89d4-7a4a06876a79.txn deleted file mode 100644 index 84ebcbed4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1281-2b47dc3e-f507-4b13-89d4-7a4a06876a79.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1282-8730f91b-ea64-4ad8-bb67-91eafb502bff.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1282-8730f91b-ea64-4ad8-bb67-91eafb502bff.txn deleted file mode 100644 index 70eb7f251..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1282-8730f91b-ea64-4ad8-bb67-91eafb502bff.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1283-e1c4cc85-4dc6-4b23-8f41-251b9e728f4a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1283-e1c4cc85-4dc6-4b23-8f41-251b9e728f4a.txn deleted file mode 100644 index 812435b66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1283-e1c4cc85-4dc6-4b23-8f41-251b9e728f4a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1284-93543d11-aaad-4408-b902-33b64d8b14b7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1284-93543d11-aaad-4408-b902-33b64d8b14b7.txn deleted file mode 100644 index 7a21a8058..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1284-93543d11-aaad-4408-b902-33b64d8b14b7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1285-128d12a1-ba24-4376-a8cf-e76e1f49983a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1285-128d12a1-ba24-4376-a8cf-e76e1f49983a.txn deleted file mode 100644 index 031146ad5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1285-128d12a1-ba24-4376-a8cf-e76e1f49983a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1286-08fdd016-9bff-4b4e-a443-d3190a7748c1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1286-08fdd016-9bff-4b4e-a443-d3190a7748c1.txn deleted file mode 100644 index e3403cfec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1286-08fdd016-9bff-4b4e-a443-d3190a7748c1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1287-f376e941-488a-44db-9d48-667e1dab48c0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1287-f376e941-488a-44db-9d48-667e1dab48c0.txn deleted file mode 100644 index f4b4647b7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1287-f376e941-488a-44db-9d48-667e1dab48c0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1288-6e3a3caf-6ead-4fc2-913b-d37f28c8d90e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1288-6e3a3caf-6ead-4fc2-913b-d37f28c8d90e.txn deleted file mode 100644 index 951ae416f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1288-6e3a3caf-6ead-4fc2-913b-d37f28c8d90e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1289-a44766a0-e92e-4dfb-a590-f4736bdf3392.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1289-a44766a0-e92e-4dfb-a590-f4736bdf3392.txn deleted file mode 100644 index 1e09ea3dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1289-a44766a0-e92e-4dfb-a590-f4736bdf3392.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/129-2d5d5509-db09-4ab2-acc9-8067072626cb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/129-2d5d5509-db09-4ab2-acc9-8067072626cb.txn deleted file mode 100644 index fccea3778..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/129-2d5d5509-db09-4ab2-acc9-8067072626cb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1290-e94eafe3-421a-4fe8-b4de-cf04c4f52cc8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1290-e94eafe3-421a-4fe8-b4de-cf04c4f52cc8.txn deleted file mode 100644 index 7c52ed8b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1290-e94eafe3-421a-4fe8-b4de-cf04c4f52cc8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1291-f5c1dbfd-3427-4e68-9ba8-224e424a6318.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1291-f5c1dbfd-3427-4e68-9ba8-224e424a6318.txn deleted file mode 100644 index 5fdc0eaf1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1291-f5c1dbfd-3427-4e68-9ba8-224e424a6318.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1292-be453b2c-5d98-49e5-a900-2d3d8cb79a76.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1292-be453b2c-5d98-49e5-a900-2d3d8cb79a76.txn deleted file mode 100644 index 3500dce16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1292-be453b2c-5d98-49e5-a900-2d3d8cb79a76.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1293-e3bcc800-de93-4696-b749-d9abcb19a722.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1293-e3bcc800-de93-4696-b749-d9abcb19a722.txn deleted file mode 100644 index 9bb5fbd3f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1293-e3bcc800-de93-4696-b749-d9abcb19a722.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1294-236797bf-c610-40d9-92fb-7eada7f5f3a0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1294-236797bf-c610-40d9-92fb-7eada7f5f3a0.txn deleted file mode 100644 index c8b2da102..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1294-236797bf-c610-40d9-92fb-7eada7f5f3a0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1295-f481f636-c54d-472c-b117-e16a11967c5f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1295-f481f636-c54d-472c-b117-e16a11967c5f.txn deleted file mode 100644 index c6e9bc416..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1295-f481f636-c54d-472c-b117-e16a11967c5f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1296-b545420d-7f7f-4959-83f2-ac6c59f08ba9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1296-b545420d-7f7f-4959-83f2-ac6c59f08ba9.txn deleted file mode 100644 index 41de80757..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1296-b545420d-7f7f-4959-83f2-ac6c59f08ba9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1297-282b839e-0301-44b0-b7a5-9d7c7fa79c12.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1297-282b839e-0301-44b0-b7a5-9d7c7fa79c12.txn deleted file mode 100644 index a677fa6ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1297-282b839e-0301-44b0-b7a5-9d7c7fa79c12.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1298-afea5806-ee39-41c5-b8a8-0892a224e6bf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1298-afea5806-ee39-41c5-b8a8-0892a224e6bf.txn deleted file mode 100644 index 076ba5f95..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1298-afea5806-ee39-41c5-b8a8-0892a224e6bf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1299-7ed6a45f-aad3-4c35-8551-945aecdd831e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1299-7ed6a45f-aad3-4c35-8551-945aecdd831e.txn deleted file mode 100644 index de813e71b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1299-7ed6a45f-aad3-4c35-8551-945aecdd831e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/13-11ba2cc1-758b-4956-ac3b-b05d0c78ce4f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/13-11ba2cc1-758b-4956-ac3b-b05d0c78ce4f.txn deleted file mode 100644 index e95df8026..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/13-11ba2cc1-758b-4956-ac3b-b05d0c78ce4f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/130-d8a8050d-b745-49ea-b428-619e19dd16a0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/130-d8a8050d-b745-49ea-b428-619e19dd16a0.txn deleted file mode 100644 index 8d8b5fa8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/130-d8a8050d-b745-49ea-b428-619e19dd16a0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1300-a8f4d67e-aa80-404d-833c-b5af18c6d50e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1300-a8f4d67e-aa80-404d-833c-b5af18c6d50e.txn deleted file mode 100644 index f40248f5c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1300-a8f4d67e-aa80-404d-833c-b5af18c6d50e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1301-3d265d0c-7c4d-40c4-86d2-661da7ffe6c8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1301-3d265d0c-7c4d-40c4-86d2-661da7ffe6c8.txn deleted file mode 100644 index 32d929983..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1301-3d265d0c-7c4d-40c4-86d2-661da7ffe6c8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1302-6342aa1a-ffb6-4046-8372-9a654b73f3db.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1302-6342aa1a-ffb6-4046-8372-9a654b73f3db.txn deleted file mode 100644 index caa7322f5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1302-6342aa1a-ffb6-4046-8372-9a654b73f3db.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1303-de5a4136-3d32-4c43-b1be-c97bf1c81d21.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1303-de5a4136-3d32-4c43-b1be-c97bf1c81d21.txn deleted file mode 100644 index 3674cde6a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1303-de5a4136-3d32-4c43-b1be-c97bf1c81d21.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1304-dc8848ef-ca03-4c05-833b-fb8874fc8a9c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1304-dc8848ef-ca03-4c05-833b-fb8874fc8a9c.txn deleted file mode 100644 index 00db61f64..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1304-dc8848ef-ca03-4c05-833b-fb8874fc8a9c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1305-33f58b8c-3e40-4379-8de5-950dd02b383e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1305-33f58b8c-3e40-4379-8de5-950dd02b383e.txn deleted file mode 100644 index 6c3cc64ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1305-33f58b8c-3e40-4379-8de5-950dd02b383e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1306-61b9fd15-f140-415e-803c-86d37bbc744b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1306-61b9fd15-f140-415e-803c-86d37bbc744b.txn deleted file mode 100644 index 85fb137f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1306-61b9fd15-f140-415e-803c-86d37bbc744b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1307-3fe23881-6c9c-45b2-9ac3-3a52f70a16e0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1307-3fe23881-6c9c-45b2-9ac3-3a52f70a16e0.txn deleted file mode 100644 index 5b41d3898..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1307-3fe23881-6c9c-45b2-9ac3-3a52f70a16e0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1308-676c2442-8b74-4697-b08e-8260939d471c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1308-676c2442-8b74-4697-b08e-8260939d471c.txn deleted file mode 100644 index 1ce2f6477..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1308-676c2442-8b74-4697-b08e-8260939d471c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1309-2aeecd4e-8de2-4125-8221-d01234cff6c2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1309-2aeecd4e-8de2-4125-8221-d01234cff6c2.txn deleted file mode 100644 index f5ed0224b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1309-2aeecd4e-8de2-4125-8221-d01234cff6c2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/131-812bf2ed-3d6d-486f-8958-dff8e402d0e9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/131-812bf2ed-3d6d-486f-8958-dff8e402d0e9.txn deleted file mode 100644 index 66155d951..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/131-812bf2ed-3d6d-486f-8958-dff8e402d0e9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1310-360b41d5-6f95-4483-b330-7da36ebb9116.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1310-360b41d5-6f95-4483-b330-7da36ebb9116.txn deleted file mode 100644 index b6f78fc7d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1310-360b41d5-6f95-4483-b330-7da36ebb9116.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1311-8aec623e-0110-4037-8bd2-bef891a56c4e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1311-8aec623e-0110-4037-8bd2-bef891a56c4e.txn deleted file mode 100644 index ea6fc2f00..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1311-8aec623e-0110-4037-8bd2-bef891a56c4e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1312-fa0f0e4e-fb58-41b3-9b83-0673eda0d617.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1312-fa0f0e4e-fb58-41b3-9b83-0673eda0d617.txn deleted file mode 100644 index b8b9b9c75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1312-fa0f0e4e-fb58-41b3-9b83-0673eda0d617.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1313-b4cfaa6c-791e-4439-8dbb-7ea776ffc22c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1313-b4cfaa6c-791e-4439-8dbb-7ea776ffc22c.txn deleted file mode 100644 index 0863f99bf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1313-b4cfaa6c-791e-4439-8dbb-7ea776ffc22c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1314-837f2654-eef1-41b1-829f-424ba102925a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1314-837f2654-eef1-41b1-829f-424ba102925a.txn deleted file mode 100644 index 303fa5fdd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1314-837f2654-eef1-41b1-829f-424ba102925a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1315-1bc46e51-0d12-4841-b4ec-528b7fc15dbf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1315-1bc46e51-0d12-4841-b4ec-528b7fc15dbf.txn deleted file mode 100644 index 9478b1bc8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1315-1bc46e51-0d12-4841-b4ec-528b7fc15dbf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1316-afbedc3e-4b74-432d-874a-87d6808a433f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1316-afbedc3e-4b74-432d-874a-87d6808a433f.txn deleted file mode 100644 index 9e593a161..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1316-afbedc3e-4b74-432d-874a-87d6808a433f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1317-8937bfc0-9d13-488b-9c41-4cb1ff789803.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1317-8937bfc0-9d13-488b-9c41-4cb1ff789803.txn deleted file mode 100644 index b03deba8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1317-8937bfc0-9d13-488b-9c41-4cb1ff789803.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1318-f362ffe2-d0f9-411d-be4b-af6b22ee9953.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1318-f362ffe2-d0f9-411d-be4b-af6b22ee9953.txn deleted file mode 100644 index 975a5055e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1318-f362ffe2-d0f9-411d-be4b-af6b22ee9953.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1319-de352c0e-0d35-4ec9-bd4c-266c66c50019.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1319-de352c0e-0d35-4ec9-bd4c-266c66c50019.txn deleted file mode 100644 index e83ddd909..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1319-de352c0e-0d35-4ec9-bd4c-266c66c50019.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/132-b4f6888e-ae72-419e-a9ac-c39d26a9725d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/132-b4f6888e-ae72-419e-a9ac-c39d26a9725d.txn deleted file mode 100644 index 662d4fe29..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/132-b4f6888e-ae72-419e-a9ac-c39d26a9725d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1320-f5f2a508-cac2-4fdd-8fc5-f6a26e2b1a10.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1320-f5f2a508-cac2-4fdd-8fc5-f6a26e2b1a10.txn deleted file mode 100644 index 699008ef9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1320-f5f2a508-cac2-4fdd-8fc5-f6a26e2b1a10.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1321-ffe00643-0f68-4f85-a54e-2257f939a4c7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1321-ffe00643-0f68-4f85-a54e-2257f939a4c7.txn deleted file mode 100644 index af8c4b2a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1321-ffe00643-0f68-4f85-a54e-2257f939a4c7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1322-c0e66708-b48a-4837-8637-47fdec871398.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1322-c0e66708-b48a-4837-8637-47fdec871398.txn deleted file mode 100644 index e76a47f39..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1322-c0e66708-b48a-4837-8637-47fdec871398.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1323-37d31f74-6feb-42fa-8a47-41ccd6f723e0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1323-37d31f74-6feb-42fa-8a47-41ccd6f723e0.txn deleted file mode 100644 index fdb536ac0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1323-37d31f74-6feb-42fa-8a47-41ccd6f723e0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1324-e9a8144b-b027-4b01-9021-ffc054a841ae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1324-e9a8144b-b027-4b01-9021-ffc054a841ae.txn deleted file mode 100644 index 00e7a7863..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1324-e9a8144b-b027-4b01-9021-ffc054a841ae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1325-a826268e-9b6e-4f95-9b21-f7ea331210d6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1325-a826268e-9b6e-4f95-9b21-f7ea331210d6.txn deleted file mode 100644 index a93e0bddc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1325-a826268e-9b6e-4f95-9b21-f7ea331210d6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1326-f2e19871-b843-4e7b-8206-66a7cb43198a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1326-f2e19871-b843-4e7b-8206-66a7cb43198a.txn deleted file mode 100644 index ddb763afb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1326-f2e19871-b843-4e7b-8206-66a7cb43198a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1327-34f4d7aa-e11b-4531-b677-ce190d765950.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1327-34f4d7aa-e11b-4531-b677-ce190d765950.txn deleted file mode 100644 index 8d03d6033..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1327-34f4d7aa-e11b-4531-b677-ce190d765950.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1328-b282557e-9b96-4eb6-afb4-ee6b9ea2e8e9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1328-b282557e-9b96-4eb6-afb4-ee6b9ea2e8e9.txn deleted file mode 100644 index 80fda9188..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1328-b282557e-9b96-4eb6-afb4-ee6b9ea2e8e9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1329-256f5c1f-62f8-4151-925d-1cbe35977246.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1329-256f5c1f-62f8-4151-925d-1cbe35977246.txn deleted file mode 100644 index 048a94183..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1329-256f5c1f-62f8-4151-925d-1cbe35977246.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/133-0a930e37-2508-4707-bdc3-bce728607030.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/133-0a930e37-2508-4707-bdc3-bce728607030.txn deleted file mode 100644 index a44b1ad23..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/133-0a930e37-2508-4707-bdc3-bce728607030.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1330-a12391a8-fbba-4dd6-9734-1aecf0e2fbbb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1330-a12391a8-fbba-4dd6-9734-1aecf0e2fbbb.txn deleted file mode 100644 index 067aa4984..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1330-a12391a8-fbba-4dd6-9734-1aecf0e2fbbb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1331-6cebd7fe-40c9-4e6e-a1c3-1485df5f99e7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1331-6cebd7fe-40c9-4e6e-a1c3-1485df5f99e7.txn deleted file mode 100644 index 849e7d205..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1331-6cebd7fe-40c9-4e6e-a1c3-1485df5f99e7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1332-ee0a46b1-f1f5-4506-b38f-decf5c8447d2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1332-ee0a46b1-f1f5-4506-b38f-decf5c8447d2.txn deleted file mode 100644 index ba902624b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1332-ee0a46b1-f1f5-4506-b38f-decf5c8447d2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1333-4f975c8d-709e-4330-b5bc-6915ee536ea1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1333-4f975c8d-709e-4330-b5bc-6915ee536ea1.txn deleted file mode 100644 index c73e3bbd5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1333-4f975c8d-709e-4330-b5bc-6915ee536ea1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1334-917e160a-1d8c-44b3-83df-feae1b7dcc78.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1334-917e160a-1d8c-44b3-83df-feae1b7dcc78.txn deleted file mode 100644 index 8e1d15e8f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1334-917e160a-1d8c-44b3-83df-feae1b7dcc78.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1335-40cc5fa3-9aeb-41be-9192-18ad83c0c62a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1335-40cc5fa3-9aeb-41be-9192-18ad83c0c62a.txn deleted file mode 100644 index 82e2ce054..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1335-40cc5fa3-9aeb-41be-9192-18ad83c0c62a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1336-b151860c-553f-444e-85f3-3c8a24aa0201.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1336-b151860c-553f-444e-85f3-3c8a24aa0201.txn deleted file mode 100644 index c1f21c7a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1336-b151860c-553f-444e-85f3-3c8a24aa0201.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1337-5056fcc6-9887-4968-a08f-9c9b113df2f5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1337-5056fcc6-9887-4968-a08f-9c9b113df2f5.txn deleted file mode 100644 index b689a50e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1337-5056fcc6-9887-4968-a08f-9c9b113df2f5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1338-01fcb473-fd40-4245-a0f5-d976d75cb30b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1338-01fcb473-fd40-4245-a0f5-d976d75cb30b.txn deleted file mode 100644 index 26cd1fbaf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1338-01fcb473-fd40-4245-a0f5-d976d75cb30b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1339-1f69f058-052d-4f9a-889b-c390ce4136f1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1339-1f69f058-052d-4f9a-889b-c390ce4136f1.txn deleted file mode 100644 index 2047b4a61..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1339-1f69f058-052d-4f9a-889b-c390ce4136f1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/134-2decf5ef-5c3a-4947-877a-ab66a7a7affb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/134-2decf5ef-5c3a-4947-877a-ab66a7a7affb.txn deleted file mode 100644 index cfe5f4097..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/134-2decf5ef-5c3a-4947-877a-ab66a7a7affb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1340-38cdf811-3a2d-4b88-b4f9-c1de91506945.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1340-38cdf811-3a2d-4b88-b4f9-c1de91506945.txn deleted file mode 100644 index 9986f86cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1340-38cdf811-3a2d-4b88-b4f9-c1de91506945.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1341-bd5d627d-8f99-4a12-b7f7-bf2980d9a261.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1341-bd5d627d-8f99-4a12-b7f7-bf2980d9a261.txn deleted file mode 100644 index 64c387c30..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1341-bd5d627d-8f99-4a12-b7f7-bf2980d9a261.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1342-054948a6-bd9e-4448-bc64-d0bc7264696f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1342-054948a6-bd9e-4448-bc64-d0bc7264696f.txn deleted file mode 100644 index 021810f48..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1342-054948a6-bd9e-4448-bc64-d0bc7264696f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1343-90afef09-5264-47d7-a232-00bfcd884fa7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1343-90afef09-5264-47d7-a232-00bfcd884fa7.txn deleted file mode 100644 index 540bc32f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1343-90afef09-5264-47d7-a232-00bfcd884fa7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1344-64d18fd2-b1ee-4f6f-bbfd-124ce364d0fe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1344-64d18fd2-b1ee-4f6f-bbfd-124ce364d0fe.txn deleted file mode 100644 index b87d337ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1344-64d18fd2-b1ee-4f6f-bbfd-124ce364d0fe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1345-abc19bec-732f-4d0a-b46c-678e038e9127.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1345-abc19bec-732f-4d0a-b46c-678e038e9127.txn deleted file mode 100644 index 5bb0c56f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1345-abc19bec-732f-4d0a-b46c-678e038e9127.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1346-98718a56-0d57-473f-a456-e189ee304c3f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1346-98718a56-0d57-473f-a456-e189ee304c3f.txn deleted file mode 100644 index b87fa05c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1346-98718a56-0d57-473f-a456-e189ee304c3f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1347-c144f31e-2def-426b-9ae0-3a2f7dad71b3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1347-c144f31e-2def-426b-9ae0-3a2f7dad71b3.txn deleted file mode 100644 index 0906c9924..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1347-c144f31e-2def-426b-9ae0-3a2f7dad71b3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1348-a5a2d61e-8b5a-49e2-876b-448fdebbe2da.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1348-a5a2d61e-8b5a-49e2-876b-448fdebbe2da.txn deleted file mode 100644 index e95fea718..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1348-a5a2d61e-8b5a-49e2-876b-448fdebbe2da.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1349-1a5c61e8-e2e2-49ed-bac9-8f380a6ab45b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1349-1a5c61e8-e2e2-49ed-bac9-8f380a6ab45b.txn deleted file mode 100644 index 63ed9ddea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1349-1a5c61e8-e2e2-49ed-bac9-8f380a6ab45b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/135-b575fee3-390d-44af-ace9-bd0a150d3cd0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/135-b575fee3-390d-44af-ace9-bd0a150d3cd0.txn deleted file mode 100644 index ede7adcff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/135-b575fee3-390d-44af-ace9-bd0a150d3cd0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1350-1dcdb1b2-6657-497b-97b1-df5f6effd01c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1350-1dcdb1b2-6657-497b-97b1-df5f6effd01c.txn deleted file mode 100644 index a71b87dc6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1350-1dcdb1b2-6657-497b-97b1-df5f6effd01c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1351-4d8f6336-b0c4-4c5b-923c-0045ef91c77b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1351-4d8f6336-b0c4-4c5b-923c-0045ef91c77b.txn deleted file mode 100644 index b92d02747..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1351-4d8f6336-b0c4-4c5b-923c-0045ef91c77b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1352-fc9991e0-e3bc-497b-9e6e-be740d3358c5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1352-fc9991e0-e3bc-497b-9e6e-be740d3358c5.txn deleted file mode 100644 index d3d63b3d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1352-fc9991e0-e3bc-497b-9e6e-be740d3358c5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1353-17ce10aa-3641-48bb-9b2a-4e29a42cc29f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1353-17ce10aa-3641-48bb-9b2a-4e29a42cc29f.txn deleted file mode 100644 index e0dd086d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1353-17ce10aa-3641-48bb-9b2a-4e29a42cc29f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1354-6ce40efd-a84b-4550-81d9-e08da77f6ff4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1354-6ce40efd-a84b-4550-81d9-e08da77f6ff4.txn deleted file mode 100644 index 30cd123ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1354-6ce40efd-a84b-4550-81d9-e08da77f6ff4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1355-7aeaf256-3311-4459-b0aa-aadc868abcba.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1355-7aeaf256-3311-4459-b0aa-aadc868abcba.txn deleted file mode 100644 index f5561d873..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1355-7aeaf256-3311-4459-b0aa-aadc868abcba.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1356-dc2f9f24-e9d5-427e-862b-4c25d57bfa9d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1356-dc2f9f24-e9d5-427e-862b-4c25d57bfa9d.txn deleted file mode 100644 index c1a2039ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1356-dc2f9f24-e9d5-427e-862b-4c25d57bfa9d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1357-0ca42959-7331-4e2e-98cb-7029907c303e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1357-0ca42959-7331-4e2e-98cb-7029907c303e.txn deleted file mode 100644 index 84575dd43..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1357-0ca42959-7331-4e2e-98cb-7029907c303e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1358-1f164cb1-21b7-49a4-9008-cc006c60a411.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1358-1f164cb1-21b7-49a4-9008-cc006c60a411.txn deleted file mode 100644 index 92fd89c40..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1358-1f164cb1-21b7-49a4-9008-cc006c60a411.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1359-8fb766d0-f0e6-4f49-9694-6ec4e1a40de2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1359-8fb766d0-f0e6-4f49-9694-6ec4e1a40de2.txn deleted file mode 100644 index a5e98129d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1359-8fb766d0-f0e6-4f49-9694-6ec4e1a40de2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/136-1817ee80-a474-4b66-89ef-16f9802c0f58.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/136-1817ee80-a474-4b66-89ef-16f9802c0f58.txn deleted file mode 100644 index f7e5a22fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/136-1817ee80-a474-4b66-89ef-16f9802c0f58.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1360-7ccc734c-46e1-4ab0-b637-87bd3e7d62c9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1360-7ccc734c-46e1-4ab0-b637-87bd3e7d62c9.txn deleted file mode 100644 index 4d8edf010..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1360-7ccc734c-46e1-4ab0-b637-87bd3e7d62c9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1361-9961c8d4-cf5c-4476-9755-76b5394c9a80.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1361-9961c8d4-cf5c-4476-9755-76b5394c9a80.txn deleted file mode 100644 index 9fd414679..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1361-9961c8d4-cf5c-4476-9755-76b5394c9a80.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1362-6947463c-9be0-44b4-b8ee-71a20a5fbc42.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1362-6947463c-9be0-44b4-b8ee-71a20a5fbc42.txn deleted file mode 100644 index cf740b9ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1362-6947463c-9be0-44b4-b8ee-71a20a5fbc42.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1363-e83e8518-a0bc-4647-a085-98593a11c92f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1363-e83e8518-a0bc-4647-a085-98593a11c92f.txn deleted file mode 100644 index cc1ab593a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1363-e83e8518-a0bc-4647-a085-98593a11c92f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1364-ac095ae4-6d04-4df0-9a96-019ccee0d299.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1364-ac095ae4-6d04-4df0-9a96-019ccee0d299.txn deleted file mode 100644 index a9cb7c83c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1364-ac095ae4-6d04-4df0-9a96-019ccee0d299.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1365-3830ff0a-a118-47f1-ac7e-f19cdc6d9141.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1365-3830ff0a-a118-47f1-ac7e-f19cdc6d9141.txn deleted file mode 100644 index 95d174b1c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1365-3830ff0a-a118-47f1-ac7e-f19cdc6d9141.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1366-1919d7e7-9b12-40a3-b932-715ffc179951.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1366-1919d7e7-9b12-40a3-b932-715ffc179951.txn deleted file mode 100644 index 5009df9d6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1366-1919d7e7-9b12-40a3-b932-715ffc179951.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1367-84346348-c2e6-41db-adc4-7c122125a1c5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1367-84346348-c2e6-41db-adc4-7c122125a1c5.txn deleted file mode 100644 index c5ab8a17e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1367-84346348-c2e6-41db-adc4-7c122125a1c5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1368-09aa8bc9-c26a-4597-a359-71eaf639ada7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1368-09aa8bc9-c26a-4597-a359-71eaf639ada7.txn deleted file mode 100644 index 2ec292b9b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1368-09aa8bc9-c26a-4597-a359-71eaf639ada7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1369-9ad20795-c95d-4e0c-9470-af0fc1a76d57.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1369-9ad20795-c95d-4e0c-9470-af0fc1a76d57.txn deleted file mode 100644 index dffbc8d84..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1369-9ad20795-c95d-4e0c-9470-af0fc1a76d57.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/137-6e1fc4cc-0a48-4ef1-a2dd-d414afd8aa3a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/137-6e1fc4cc-0a48-4ef1-a2dd-d414afd8aa3a.txn deleted file mode 100644 index e4e5dc27a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/137-6e1fc4cc-0a48-4ef1-a2dd-d414afd8aa3a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1370-56495a7a-137c-4a01-987d-7aa1372494f5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1370-56495a7a-137c-4a01-987d-7aa1372494f5.txn deleted file mode 100644 index 589b4f8e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1370-56495a7a-137c-4a01-987d-7aa1372494f5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1371-d9c37236-4c05-4774-8d2c-6c2b9e6715f8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1371-d9c37236-4c05-4774-8d2c-6c2b9e6715f8.txn deleted file mode 100644 index ae809df1b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1371-d9c37236-4c05-4774-8d2c-6c2b9e6715f8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1372-f85aef9c-91fe-4345-8e37-b682ac4b7462.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1372-f85aef9c-91fe-4345-8e37-b682ac4b7462.txn deleted file mode 100644 index c2556f8ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1372-f85aef9c-91fe-4345-8e37-b682ac4b7462.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1373-fe94ea7c-c67d-40b6-9d53-62aa401b35f5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1373-fe94ea7c-c67d-40b6-9d53-62aa401b35f5.txn deleted file mode 100644 index 3277e318d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1373-fe94ea7c-c67d-40b6-9d53-62aa401b35f5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1374-95d3ce3a-d3cc-4b89-83f1-bd063f2e89af.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1374-95d3ce3a-d3cc-4b89-83f1-bd063f2e89af.txn deleted file mode 100644 index a193c41c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1374-95d3ce3a-d3cc-4b89-83f1-bd063f2e89af.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1375-c813dce9-bbbd-4301-b160-69f23f22af12.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1375-c813dce9-bbbd-4301-b160-69f23f22af12.txn deleted file mode 100644 index 4086da9fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1375-c813dce9-bbbd-4301-b160-69f23f22af12.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1376-7a7dd8dc-41ce-4903-8884-e2cc97749b39.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1376-7a7dd8dc-41ce-4903-8884-e2cc97749b39.txn deleted file mode 100644 index 4ea3e7131..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1376-7a7dd8dc-41ce-4903-8884-e2cc97749b39.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1377-6baa025a-2aec-400e-affe-1f96c68cad24.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1377-6baa025a-2aec-400e-affe-1f96c68cad24.txn deleted file mode 100644 index f592226d6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1377-6baa025a-2aec-400e-affe-1f96c68cad24.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1378-fcde78d2-b2ec-4fe3-8fb5-61c2dbf273aa.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1378-fcde78d2-b2ec-4fe3-8fb5-61c2dbf273aa.txn deleted file mode 100644 index f4419c08e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1378-fcde78d2-b2ec-4fe3-8fb5-61c2dbf273aa.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1379-27fef782-279f-4f7c-aeee-67487975aad8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1379-27fef782-279f-4f7c-aeee-67487975aad8.txn deleted file mode 100644 index 09a0d43b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1379-27fef782-279f-4f7c-aeee-67487975aad8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/138-2537504c-4e86-4bff-9935-d305a46826ab.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/138-2537504c-4e86-4bff-9935-d305a46826ab.txn deleted file mode 100644 index 8c6920ecb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/138-2537504c-4e86-4bff-9935-d305a46826ab.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1380-3b375ddd-a4d7-4525-8582-bf6e13fffdcc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1380-3b375ddd-a4d7-4525-8582-bf6e13fffdcc.txn deleted file mode 100644 index 02864503b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1380-3b375ddd-a4d7-4525-8582-bf6e13fffdcc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1381-8737c1de-99df-4f47-a94e-67f06516adc4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1381-8737c1de-99df-4f47-a94e-67f06516adc4.txn deleted file mode 100644 index 6be529c21..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1381-8737c1de-99df-4f47-a94e-67f06516adc4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1382-61cf0c72-2dc1-4f86-ac1c-80baada760ce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1382-61cf0c72-2dc1-4f86-ac1c-80baada760ce.txn deleted file mode 100644 index 182cd1cad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1382-61cf0c72-2dc1-4f86-ac1c-80baada760ce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1383-2b5da0de-ffb5-4c3b-b3ff-611dfcbb7ff9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1383-2b5da0de-ffb5-4c3b-b3ff-611dfcbb7ff9.txn deleted file mode 100644 index 755935b8a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1383-2b5da0de-ffb5-4c3b-b3ff-611dfcbb7ff9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1384-aac4a1ea-d87f-4e8f-bd15-0c19c5bcf9be.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1384-aac4a1ea-d87f-4e8f-bd15-0c19c5bcf9be.txn deleted file mode 100644 index c790d9481..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1384-aac4a1ea-d87f-4e8f-bd15-0c19c5bcf9be.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1385-01b4a01e-1ad8-450a-84f8-51ac5891fa0f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1385-01b4a01e-1ad8-450a-84f8-51ac5891fa0f.txn deleted file mode 100644 index 53a19f943..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1385-01b4a01e-1ad8-450a-84f8-51ac5891fa0f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1386-920d1a45-deff-43c4-a948-19b9f9ce465a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1386-920d1a45-deff-43c4-a948-19b9f9ce465a.txn deleted file mode 100644 index de667ba03..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1386-920d1a45-deff-43c4-a948-19b9f9ce465a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1387-b840bc69-4685-4255-b797-0a0181a05be9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1387-b840bc69-4685-4255-b797-0a0181a05be9.txn deleted file mode 100644 index 2537b341c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1387-b840bc69-4685-4255-b797-0a0181a05be9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1388-d8f2bdb1-33f9-4515-933e-c1f58a453800.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1388-d8f2bdb1-33f9-4515-933e-c1f58a453800.txn deleted file mode 100644 index 247e9798b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1388-d8f2bdb1-33f9-4515-933e-c1f58a453800.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1389-727d2d7f-907f-444b-bd2b-933a5d7905b7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1389-727d2d7f-907f-444b-bd2b-933a5d7905b7.txn deleted file mode 100644 index 66a4681f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1389-727d2d7f-907f-444b-bd2b-933a5d7905b7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/139-95ff8c08-5ace-4697-a1e3-c53496691cbb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/139-95ff8c08-5ace-4697-a1e3-c53496691cbb.txn deleted file mode 100644 index 8a8767956..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/139-95ff8c08-5ace-4697-a1e3-c53496691cbb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1390-da3186ea-dc90-4c2f-947a-a397e751b211.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1390-da3186ea-dc90-4c2f-947a-a397e751b211.txn deleted file mode 100644 index 95ca8fdf7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1390-da3186ea-dc90-4c2f-947a-a397e751b211.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1391-0fcf2633-72ef-4996-9350-610696fa0edf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1391-0fcf2633-72ef-4996-9350-610696fa0edf.txn deleted file mode 100644 index 8b284d6d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1391-0fcf2633-72ef-4996-9350-610696fa0edf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1392-db86377a-61f4-4bf9-93ed-cd7c2326e2e9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1392-db86377a-61f4-4bf9-93ed-cd7c2326e2e9.txn deleted file mode 100644 index c4966fba9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1392-db86377a-61f4-4bf9-93ed-cd7c2326e2e9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1393-670bca91-2af2-4cbc-a3db-f4ab5b616c36.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1393-670bca91-2af2-4cbc-a3db-f4ab5b616c36.txn deleted file mode 100644 index 927dd2ceb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1393-670bca91-2af2-4cbc-a3db-f4ab5b616c36.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1394-c301f288-2814-46b0-a66d-d539d3b95765.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1394-c301f288-2814-46b0-a66d-d539d3b95765.txn deleted file mode 100644 index de76e36db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1394-c301f288-2814-46b0-a66d-d539d3b95765.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1395-c97eed77-ea6b-4d89-8522-c1a5a37b2120.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1395-c97eed77-ea6b-4d89-8522-c1a5a37b2120.txn deleted file mode 100644 index a3b565570..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1395-c97eed77-ea6b-4d89-8522-c1a5a37b2120.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1396-de3fc0a8-84ba-4ee1-aa44-d2db92392be8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1396-de3fc0a8-84ba-4ee1-aa44-d2db92392be8.txn deleted file mode 100644 index 2b2365202..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1396-de3fc0a8-84ba-4ee1-aa44-d2db92392be8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1397-0b8d331d-d447-4e72-98cc-d835c3e89785.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1397-0b8d331d-d447-4e72-98cc-d835c3e89785.txn deleted file mode 100644 index 1d4c3daae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1397-0b8d331d-d447-4e72-98cc-d835c3e89785.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1398-e1f04b03-2672-4cd8-91f6-d54b4e459088.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1398-e1f04b03-2672-4cd8-91f6-d54b4e459088.txn deleted file mode 100644 index 4e68d429d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1398-e1f04b03-2672-4cd8-91f6-d54b4e459088.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1399-30a323a5-ea88-4463-99ff-4e89d7e66471.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1399-30a323a5-ea88-4463-99ff-4e89d7e66471.txn deleted file mode 100644 index 4e0d40d0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1399-30a323a5-ea88-4463-99ff-4e89d7e66471.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/14-bc7c31d7-e124-4d44-846e-7cd8c1c80868.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/14-bc7c31d7-e124-4d44-846e-7cd8c1c80868.txn deleted file mode 100644 index a91f29608..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/14-bc7c31d7-e124-4d44-846e-7cd8c1c80868.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/140-a199bfb5-9c2d-4581-b102-c1cc6ab4e0e6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/140-a199bfb5-9c2d-4581-b102-c1cc6ab4e0e6.txn deleted file mode 100644 index 2562c6eec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/140-a199bfb5-9c2d-4581-b102-c1cc6ab4e0e6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1400-2bcb51d5-b14f-4af8-a7a6-b4d139c175fd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1400-2bcb51d5-b14f-4af8-a7a6-b4d139c175fd.txn deleted file mode 100644 index 9c84c25cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1400-2bcb51d5-b14f-4af8-a7a6-b4d139c175fd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1401-5a5aa16e-9f21-451d-ac18-8b1224df7588.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1401-5a5aa16e-9f21-451d-ac18-8b1224df7588.txn deleted file mode 100644 index b3c83e55a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1401-5a5aa16e-9f21-451d-ac18-8b1224df7588.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1402-77b5258d-eb78-4ee6-afaa-be5816fcaf77.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1402-77b5258d-eb78-4ee6-afaa-be5816fcaf77.txn deleted file mode 100644 index bb8f9c477..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1402-77b5258d-eb78-4ee6-afaa-be5816fcaf77.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1403-f0fad314-1075-4c29-8dee-cd653df97f1b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1403-f0fad314-1075-4c29-8dee-cd653df97f1b.txn deleted file mode 100644 index ebd4072ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1403-f0fad314-1075-4c29-8dee-cd653df97f1b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1404-a9bc9910-d345-43b9-a5fa-af1206c5dae2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1404-a9bc9910-d345-43b9-a5fa-af1206c5dae2.txn deleted file mode 100644 index b85dd6629..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1404-a9bc9910-d345-43b9-a5fa-af1206c5dae2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1405-ad67d5cc-56cf-4ca0-be01-ab668ef89cf2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1405-ad67d5cc-56cf-4ca0-be01-ab668ef89cf2.txn deleted file mode 100644 index 901e8cb18..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1405-ad67d5cc-56cf-4ca0-be01-ab668ef89cf2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1406-a9483a8a-ded6-4d19-b0cf-8bcfdc37e6fe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1406-a9483a8a-ded6-4d19-b0cf-8bcfdc37e6fe.txn deleted file mode 100644 index 8389e30bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1406-a9483a8a-ded6-4d19-b0cf-8bcfdc37e6fe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1407-1865612e-4d78-4932-a1a0-85e9414c47cc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1407-1865612e-4d78-4932-a1a0-85e9414c47cc.txn deleted file mode 100644 index 4701b9989..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1407-1865612e-4d78-4932-a1a0-85e9414c47cc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1408-d0106a84-6946-40b2-98b2-2bb99aca7448.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1408-d0106a84-6946-40b2-98b2-2bb99aca7448.txn deleted file mode 100644 index dae796521..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1408-d0106a84-6946-40b2-98b2-2bb99aca7448.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1409-4d5769b3-06df-42e4-a49d-840308f3609e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1409-4d5769b3-06df-42e4-a49d-840308f3609e.txn deleted file mode 100644 index f9f0334c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1409-4d5769b3-06df-42e4-a49d-840308f3609e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/141-2f9f6c20-aaf2-4c6b-90b5-13543cffe95f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/141-2f9f6c20-aaf2-4c6b-90b5-13543cffe95f.txn deleted file mode 100644 index e35b49dbb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/141-2f9f6c20-aaf2-4c6b-90b5-13543cffe95f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1410-732b8eeb-7585-47e9-92ec-4f7084e46262.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1410-732b8eeb-7585-47e9-92ec-4f7084e46262.txn deleted file mode 100644 index b13dd05c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1410-732b8eeb-7585-47e9-92ec-4f7084e46262.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1411-f657128b-5a95-4ac5-8b48-9109da387abc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1411-f657128b-5a95-4ac5-8b48-9109da387abc.txn deleted file mode 100644 index af9c937a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1411-f657128b-5a95-4ac5-8b48-9109da387abc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1412-0a103c40-563f-4fe6-844a-933e82ce62e2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1412-0a103c40-563f-4fe6-844a-933e82ce62e2.txn deleted file mode 100644 index f556385fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1412-0a103c40-563f-4fe6-844a-933e82ce62e2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1413-44cad22c-f2b3-473b-b9ec-b6e335452fc4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1413-44cad22c-f2b3-473b-b9ec-b6e335452fc4.txn deleted file mode 100644 index eeab3317e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1413-44cad22c-f2b3-473b-b9ec-b6e335452fc4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1414-860ee29d-cab0-400c-9fbc-c124fe3b9e51.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1414-860ee29d-cab0-400c-9fbc-c124fe3b9e51.txn deleted file mode 100644 index 53bf1cad7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1414-860ee29d-cab0-400c-9fbc-c124fe3b9e51.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1415-615abc11-3745-4138-9ccb-bc14977945dd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1415-615abc11-3745-4138-9ccb-bc14977945dd.txn deleted file mode 100644 index 9f4a335eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1415-615abc11-3745-4138-9ccb-bc14977945dd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1416-0fea5758-fd3a-478e-b930-4c815692c9ea.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1416-0fea5758-fd3a-478e-b930-4c815692c9ea.txn deleted file mode 100644 index d3e2edb47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1416-0fea5758-fd3a-478e-b930-4c815692c9ea.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1417-d2133bdd-9a94-4313-a10c-6b5cb70619c6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1417-d2133bdd-9a94-4313-a10c-6b5cb70619c6.txn deleted file mode 100644 index af3653578..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1417-d2133bdd-9a94-4313-a10c-6b5cb70619c6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1418-8a649c35-54af-45fd-a625-45d618e53865.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1418-8a649c35-54af-45fd-a625-45d618e53865.txn deleted file mode 100644 index d6a21a884..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1418-8a649c35-54af-45fd-a625-45d618e53865.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1419-9720a800-90a0-4a78-bb04-ea10357d36fd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1419-9720a800-90a0-4a78-bb04-ea10357d36fd.txn deleted file mode 100644 index 22107697d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1419-9720a800-90a0-4a78-bb04-ea10357d36fd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/142-6a8dce98-8797-4bdd-a692-4f07e3a4c756.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/142-6a8dce98-8797-4bdd-a692-4f07e3a4c756.txn deleted file mode 100644 index 8953f59c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/142-6a8dce98-8797-4bdd-a692-4f07e3a4c756.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1420-6d25bb73-8935-4f75-9441-1884a7cd1d11.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1420-6d25bb73-8935-4f75-9441-1884a7cd1d11.txn deleted file mode 100644 index b690c5765..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1420-6d25bb73-8935-4f75-9441-1884a7cd1d11.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1421-75e69368-09f6-47ff-9faa-4ef2192504d5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1421-75e69368-09f6-47ff-9faa-4ef2192504d5.txn deleted file mode 100644 index 01e085c11..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1421-75e69368-09f6-47ff-9faa-4ef2192504d5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1422-cc440fdf-4af9-4058-aece-5486a73d23ca.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1422-cc440fdf-4af9-4058-aece-5486a73d23ca.txn deleted file mode 100644 index 62f7e35d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1422-cc440fdf-4af9-4058-aece-5486a73d23ca.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1423-5dd7f859-8247-4c04-b2e8-60bc181e9c91.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1423-5dd7f859-8247-4c04-b2e8-60bc181e9c91.txn deleted file mode 100644 index 860d17846..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1423-5dd7f859-8247-4c04-b2e8-60bc181e9c91.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1424-9ca6f053-36f5-47dc-bff5-b4608009f768.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1424-9ca6f053-36f5-47dc-bff5-b4608009f768.txn deleted file mode 100644 index 64c3b6cd5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1424-9ca6f053-36f5-47dc-bff5-b4608009f768.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1425-ec517512-9d3b-4e4a-a3ca-687311c8ff88.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1425-ec517512-9d3b-4e4a-a3ca-687311c8ff88.txn deleted file mode 100644 index 5fc6fe971..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1425-ec517512-9d3b-4e4a-a3ca-687311c8ff88.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1426-db5d7ade-2ded-40d4-b578-4b323ca8cd8e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1426-db5d7ade-2ded-40d4-b578-4b323ca8cd8e.txn deleted file mode 100644 index 5b0f6f5dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1426-db5d7ade-2ded-40d4-b578-4b323ca8cd8e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1427-ad20d214-bba3-4c59-89c5-d56232a36ef0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1427-ad20d214-bba3-4c59-89c5-d56232a36ef0.txn deleted file mode 100644 index 4681feb61..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1427-ad20d214-bba3-4c59-89c5-d56232a36ef0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1428-e4964be4-553a-4d25-bfac-6c8afa1b5ab9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1428-e4964be4-553a-4d25-bfac-6c8afa1b5ab9.txn deleted file mode 100644 index 595528951..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1428-e4964be4-553a-4d25-bfac-6c8afa1b5ab9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1429-e3bff7d3-397d-4c1f-8b75-b76ccfb339dc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1429-e3bff7d3-397d-4c1f-8b75-b76ccfb339dc.txn deleted file mode 100644 index 1d869bcbd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1429-e3bff7d3-397d-4c1f-8b75-b76ccfb339dc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/143-46ae4dde-3338-48dd-86c9-b3bb665dee97.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/143-46ae4dde-3338-48dd-86c9-b3bb665dee97.txn deleted file mode 100644 index e1ede178c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/143-46ae4dde-3338-48dd-86c9-b3bb665dee97.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1430-c7f0065d-4462-4a58-a167-8780a197e2fe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1430-c7f0065d-4462-4a58-a167-8780a197e2fe.txn deleted file mode 100644 index c5102d485..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1430-c7f0065d-4462-4a58-a167-8780a197e2fe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1431-7c0986a4-d6db-44ab-ab90-aceffd2d9ffd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1431-7c0986a4-d6db-44ab-ab90-aceffd2d9ffd.txn deleted file mode 100644 index d9357ab0b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1431-7c0986a4-d6db-44ab-ab90-aceffd2d9ffd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1432-60d5123f-6b74-4ec3-a02e-47d6774c4796.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1432-60d5123f-6b74-4ec3-a02e-47d6774c4796.txn deleted file mode 100644 index f5c8f1ca1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1432-60d5123f-6b74-4ec3-a02e-47d6774c4796.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1433-9cc80213-f380-4077-bb5f-70cc0806330d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1433-9cc80213-f380-4077-bb5f-70cc0806330d.txn deleted file mode 100644 index cdf3d68fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1433-9cc80213-f380-4077-bb5f-70cc0806330d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1434-8c104f12-63c0-49da-aebe-509e1a7ab90e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1434-8c104f12-63c0-49da-aebe-509e1a7ab90e.txn deleted file mode 100644 index e454efdb2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1434-8c104f12-63c0-49da-aebe-509e1a7ab90e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1435-f0efd76c-b27f-4c08-9dbb-e157ff0d35a0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1435-f0efd76c-b27f-4c08-9dbb-e157ff0d35a0.txn deleted file mode 100644 index ce4bd17e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1435-f0efd76c-b27f-4c08-9dbb-e157ff0d35a0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1436-35174597-360c-4fa6-b51f-df568def40dd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1436-35174597-360c-4fa6-b51f-df568def40dd.txn deleted file mode 100644 index 350a77736..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1436-35174597-360c-4fa6-b51f-df568def40dd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1437-7afae383-b3f5-4336-90e3-1f43e7f09a78.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1437-7afae383-b3f5-4336-90e3-1f43e7f09a78.txn deleted file mode 100644 index 548f20885..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1437-7afae383-b3f5-4336-90e3-1f43e7f09a78.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1438-7790473a-2b25-42af-9f56-95fb9f6d6fd5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1438-7790473a-2b25-42af-9f56-95fb9f6d6fd5.txn deleted file mode 100644 index 5698daf33..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1438-7790473a-2b25-42af-9f56-95fb9f6d6fd5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1439-fca4a4f9-13c2-4d15-afaa-cfaa3c1975ca.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1439-fca4a4f9-13c2-4d15-afaa-cfaa3c1975ca.txn deleted file mode 100644 index 36a5ed076..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1439-fca4a4f9-13c2-4d15-afaa-cfaa3c1975ca.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/144-68a8d73c-9871-4175-8307-dc0c982c048a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/144-68a8d73c-9871-4175-8307-dc0c982c048a.txn deleted file mode 100644 index 2da4d078d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/144-68a8d73c-9871-4175-8307-dc0c982c048a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1440-5108a9aa-ffc3-46e5-b851-b40a55f07d66.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1440-5108a9aa-ffc3-46e5-b851-b40a55f07d66.txn deleted file mode 100644 index 3c31e0999..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1440-5108a9aa-ffc3-46e5-b851-b40a55f07d66.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1441-f5db6841-66ab-4e5d-89f7-b7301683debc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1441-f5db6841-66ab-4e5d-89f7-b7301683debc.txn deleted file mode 100644 index 079d7dfa8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1441-f5db6841-66ab-4e5d-89f7-b7301683debc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1442-55017ee0-e77f-4b44-b5f2-4f81d6f14e90.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1442-55017ee0-e77f-4b44-b5f2-4f81d6f14e90.txn deleted file mode 100644 index 434124f72..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1442-55017ee0-e77f-4b44-b5f2-4f81d6f14e90.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1443-075c9493-898a-4203-9ec7-f7a57b211d16.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1443-075c9493-898a-4203-9ec7-f7a57b211d16.txn deleted file mode 100644 index 1ade4b9ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1443-075c9493-898a-4203-9ec7-f7a57b211d16.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1444-4b973291-1904-477f-a900-e6c8c21322ac.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1444-4b973291-1904-477f-a900-e6c8c21322ac.txn deleted file mode 100644 index a77d4b358..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1444-4b973291-1904-477f-a900-e6c8c21322ac.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1445-faefc724-b7b6-4506-9587-4f30740a6269.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1445-faefc724-b7b6-4506-9587-4f30740a6269.txn deleted file mode 100644 index 486ed25bd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1445-faefc724-b7b6-4506-9587-4f30740a6269.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1446-27e28f53-383b-46c1-baeb-108775c5279a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1446-27e28f53-383b-46c1-baeb-108775c5279a.txn deleted file mode 100644 index 495e2499f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1446-27e28f53-383b-46c1-baeb-108775c5279a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1447-7c2155ea-2267-4b3c-a80f-9102e5974b92.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1447-7c2155ea-2267-4b3c-a80f-9102e5974b92.txn deleted file mode 100644 index 385a20271..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1447-7c2155ea-2267-4b3c-a80f-9102e5974b92.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1448-2b3bb924-3bed-446c-b6a4-3e7617b931d1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1448-2b3bb924-3bed-446c-b6a4-3e7617b931d1.txn deleted file mode 100644 index fd1a4caa9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1448-2b3bb924-3bed-446c-b6a4-3e7617b931d1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1449-6ddbb1c2-6074-442f-98aa-e1e2e75c6d5f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1449-6ddbb1c2-6074-442f-98aa-e1e2e75c6d5f.txn deleted file mode 100644 index 138d95fce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1449-6ddbb1c2-6074-442f-98aa-e1e2e75c6d5f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/145-795f06e9-d89c-40a1-8353-8d3da5a80893.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/145-795f06e9-d89c-40a1-8353-8d3da5a80893.txn deleted file mode 100644 index 2953b0fe0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/145-795f06e9-d89c-40a1-8353-8d3da5a80893.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1450-6a6381e6-d665-44b2-9b44-3c36e7da95e1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1450-6a6381e6-d665-44b2-9b44-3c36e7da95e1.txn deleted file mode 100644 index 13cf5a253..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1450-6a6381e6-d665-44b2-9b44-3c36e7da95e1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1451-b9b0590e-7d14-4d3e-b9c0-70378adbfe79.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1451-b9b0590e-7d14-4d3e-b9c0-70378adbfe79.txn deleted file mode 100644 index 07a872920..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1451-b9b0590e-7d14-4d3e-b9c0-70378adbfe79.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1452-0816bd51-ff5d-4a79-8ae1-2437a0fbd152.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1452-0816bd51-ff5d-4a79-8ae1-2437a0fbd152.txn deleted file mode 100644 index d9ca52c1c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1452-0816bd51-ff5d-4a79-8ae1-2437a0fbd152.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1453-06db90e5-26c4-4a69-979f-afbf05531db6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1453-06db90e5-26c4-4a69-979f-afbf05531db6.txn deleted file mode 100644 index af3a6dcaf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1453-06db90e5-26c4-4a69-979f-afbf05531db6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1454-96da7d8b-77f6-4d32-b104-4baea0035c1d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1454-96da7d8b-77f6-4d32-b104-4baea0035c1d.txn deleted file mode 100644 index e5c9f4c95..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1454-96da7d8b-77f6-4d32-b104-4baea0035c1d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1455-b5d1be6c-f61c-4873-9d5b-b95e3f891519.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1455-b5d1be6c-f61c-4873-9d5b-b95e3f891519.txn deleted file mode 100644 index 8da496149..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1455-b5d1be6c-f61c-4873-9d5b-b95e3f891519.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1456-5c7c1896-f00c-4377-a8f9-c63cc91c8bba.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1456-5c7c1896-f00c-4377-a8f9-c63cc91c8bba.txn deleted file mode 100644 index 24fa05117..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1456-5c7c1896-f00c-4377-a8f9-c63cc91c8bba.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1457-dc3c17e2-6e67-4b64-b457-7cd93b5e57c3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1457-dc3c17e2-6e67-4b64-b457-7cd93b5e57c3.txn deleted file mode 100644 index 6ff4725a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1457-dc3c17e2-6e67-4b64-b457-7cd93b5e57c3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1458-a0748cc8-8b3c-401e-839a-367edd24a081.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1458-a0748cc8-8b3c-401e-839a-367edd24a081.txn deleted file mode 100644 index cfc949298..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1458-a0748cc8-8b3c-401e-839a-367edd24a081.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1459-b2127466-d64a-4c06-aa00-da1351c22e15.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1459-b2127466-d64a-4c06-aa00-da1351c22e15.txn deleted file mode 100644 index 990694621..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1459-b2127466-d64a-4c06-aa00-da1351c22e15.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/146-c04b9037-a875-4865-a287-eee4e79aad79.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/146-c04b9037-a875-4865-a287-eee4e79aad79.txn deleted file mode 100644 index 3434a6471..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/146-c04b9037-a875-4865-a287-eee4e79aad79.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1460-6e2292bc-8b2c-48e7-9f0e-fc2083ce180f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1460-6e2292bc-8b2c-48e7-9f0e-fc2083ce180f.txn deleted file mode 100644 index e9ba2d18e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1460-6e2292bc-8b2c-48e7-9f0e-fc2083ce180f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1461-1fb1f87e-2a02-4b28-a9d1-757bc0e8cf9a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1461-1fb1f87e-2a02-4b28-a9d1-757bc0e8cf9a.txn deleted file mode 100644 index 8e8886e80..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1461-1fb1f87e-2a02-4b28-a9d1-757bc0e8cf9a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1462-9f7f33d7-821e-4feb-8fae-75a78c8aca94.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1462-9f7f33d7-821e-4feb-8fae-75a78c8aca94.txn deleted file mode 100644 index 5a5b86cb6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1462-9f7f33d7-821e-4feb-8fae-75a78c8aca94.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1463-f7d075ee-05a4-4d01-be32-345ca8f81419.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1463-f7d075ee-05a4-4d01-be32-345ca8f81419.txn deleted file mode 100644 index e790df4bd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1463-f7d075ee-05a4-4d01-be32-345ca8f81419.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1464-4765cf9b-23b7-49dd-a992-b3ebd2ce5c78.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1464-4765cf9b-23b7-49dd-a992-b3ebd2ce5c78.txn deleted file mode 100644 index 620027229..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1464-4765cf9b-23b7-49dd-a992-b3ebd2ce5c78.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1465-ed709e34-e258-4b8b-8f9c-cc4bd216e95c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1465-ed709e34-e258-4b8b-8f9c-cc4bd216e95c.txn deleted file mode 100644 index 587481219..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1465-ed709e34-e258-4b8b-8f9c-cc4bd216e95c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1466-d51c934a-4b2e-4fe2-9d9b-8617b4e514b9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1466-d51c934a-4b2e-4fe2-9d9b-8617b4e514b9.txn deleted file mode 100644 index 87a068da7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1466-d51c934a-4b2e-4fe2-9d9b-8617b4e514b9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1467-306b5d9a-d9a3-4c1f-9776-593ab21f8f0c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1467-306b5d9a-d9a3-4c1f-9776-593ab21f8f0c.txn deleted file mode 100644 index f9191f844..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1467-306b5d9a-d9a3-4c1f-9776-593ab21f8f0c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1468-cff79730-fd4d-4385-bbbb-96ec08fa28a5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1468-cff79730-fd4d-4385-bbbb-96ec08fa28a5.txn deleted file mode 100644 index 8f4d5e553..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1468-cff79730-fd4d-4385-bbbb-96ec08fa28a5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1469-4770ba5a-869b-422c-a8e6-10252690d07d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1469-4770ba5a-869b-422c-a8e6-10252690d07d.txn deleted file mode 100644 index e5d329e8f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1469-4770ba5a-869b-422c-a8e6-10252690d07d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/147-e3fb3814-bb34-4c4d-b881-ae907640a258.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/147-e3fb3814-bb34-4c4d-b881-ae907640a258.txn deleted file mode 100644 index 74daa0b9c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/147-e3fb3814-bb34-4c4d-b881-ae907640a258.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1470-e71a365a-c344-4118-ba07-ee71c341749c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1470-e71a365a-c344-4118-ba07-ee71c341749c.txn deleted file mode 100644 index b15e9d8a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1470-e71a365a-c344-4118-ba07-ee71c341749c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1471-e8b2cae9-81ee-4365-9c9e-747e04a4ed7d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1471-e8b2cae9-81ee-4365-9c9e-747e04a4ed7d.txn deleted file mode 100644 index 53c26b0d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1471-e8b2cae9-81ee-4365-9c9e-747e04a4ed7d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1472-5a66880b-be44-4064-be28-ed691042be47.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1472-5a66880b-be44-4064-be28-ed691042be47.txn deleted file mode 100644 index dfaa776db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1472-5a66880b-be44-4064-be28-ed691042be47.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1473-41d6d9cc-a604-42e1-b4e2-6fdcb9bddddc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1473-41d6d9cc-a604-42e1-b4e2-6fdcb9bddddc.txn deleted file mode 100644 index f06866388..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1473-41d6d9cc-a604-42e1-b4e2-6fdcb9bddddc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1474-1ddfd806-8b03-4c14-96d7-d5c1a4387522.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1474-1ddfd806-8b03-4c14-96d7-d5c1a4387522.txn deleted file mode 100644 index db580bf55..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1474-1ddfd806-8b03-4c14-96d7-d5c1a4387522.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1475-1856bec8-e3fa-456b-980b-a50409641d47.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1475-1856bec8-e3fa-456b-980b-a50409641d47.txn deleted file mode 100644 index 7b05135b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1475-1856bec8-e3fa-456b-980b-a50409641d47.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1476-38eb0d67-01fc-453c-b37e-43feaaaf0184.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1476-38eb0d67-01fc-453c-b37e-43feaaaf0184.txn deleted file mode 100644 index 30c9a1854..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1476-38eb0d67-01fc-453c-b37e-43feaaaf0184.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1477-644ebdff-d842-4a5e-a832-299aa7889b80.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1477-644ebdff-d842-4a5e-a832-299aa7889b80.txn deleted file mode 100644 index 50116de63..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1477-644ebdff-d842-4a5e-a832-299aa7889b80.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1478-0e5a261c-e306-4295-82bf-111272e65d12.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1478-0e5a261c-e306-4295-82bf-111272e65d12.txn deleted file mode 100644 index e24f586f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1478-0e5a261c-e306-4295-82bf-111272e65d12.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1479-ca3bdf47-c2f1-4c5d-979e-49c498af0226.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1479-ca3bdf47-c2f1-4c5d-979e-49c498af0226.txn deleted file mode 100644 index d086a7ca1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1479-ca3bdf47-c2f1-4c5d-979e-49c498af0226.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/148-0df17192-0b7d-4495-bcbb-036eae896b14.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/148-0df17192-0b7d-4495-bcbb-036eae896b14.txn deleted file mode 100644 index 05a2f393f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/148-0df17192-0b7d-4495-bcbb-036eae896b14.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1480-98dabf22-1d21-43b4-a0f6-6b64ebf75319.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1480-98dabf22-1d21-43b4-a0f6-6b64ebf75319.txn deleted file mode 100644 index 20d1fd85c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1480-98dabf22-1d21-43b4-a0f6-6b64ebf75319.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1481-8fdd7592-ad08-46c9-9b8d-df590eb9bdfe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1481-8fdd7592-ad08-46c9-9b8d-df590eb9bdfe.txn deleted file mode 100644 index 7680bee03..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1481-8fdd7592-ad08-46c9-9b8d-df590eb9bdfe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1482-9995ca9c-40c7-480e-b34c-93f825c216b7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1482-9995ca9c-40c7-480e-b34c-93f825c216b7.txn deleted file mode 100644 index c996c1d51..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1482-9995ca9c-40c7-480e-b34c-93f825c216b7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1483-9c06c8d0-b179-485e-8986-15505d625f34.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1483-9c06c8d0-b179-485e-8986-15505d625f34.txn deleted file mode 100644 index 2deecc89d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1483-9c06c8d0-b179-485e-8986-15505d625f34.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1484-a36dbc62-ffd1-4556-b7cc-23a13ea2fa63.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1484-a36dbc62-ffd1-4556-b7cc-23a13ea2fa63.txn deleted file mode 100644 index 3dc45d8c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1484-a36dbc62-ffd1-4556-b7cc-23a13ea2fa63.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1485-677bc34b-15c6-4c7d-b493-20e5340fec80.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1485-677bc34b-15c6-4c7d-b493-20e5340fec80.txn deleted file mode 100644 index ae974e192..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1485-677bc34b-15c6-4c7d-b493-20e5340fec80.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1486-945673ec-54ce-4a3b-a4e4-1a8b7c94bf09.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1486-945673ec-54ce-4a3b-a4e4-1a8b7c94bf09.txn deleted file mode 100644 index 6ab1fca16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1486-945673ec-54ce-4a3b-a4e4-1a8b7c94bf09.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1487-0accb063-1bf5-4fe1-8a5b-2d48fac2d2db.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1487-0accb063-1bf5-4fe1-8a5b-2d48fac2d2db.txn deleted file mode 100644 index 747dcdac7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1487-0accb063-1bf5-4fe1-8a5b-2d48fac2d2db.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1488-03eb046e-fa7f-4244-8ae5-be69f149c0d8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1488-03eb046e-fa7f-4244-8ae5-be69f149c0d8.txn deleted file mode 100644 index e89cb26c3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1488-03eb046e-fa7f-4244-8ae5-be69f149c0d8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1489-cf0b2828-ed52-4eb0-8f13-09aee840fb8f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1489-cf0b2828-ed52-4eb0-8f13-09aee840fb8f.txn deleted file mode 100644 index 78cc1e86c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1489-cf0b2828-ed52-4eb0-8f13-09aee840fb8f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/149-5f955451-eb2d-4cc0-94ce-7d997a93efa1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/149-5f955451-eb2d-4cc0-94ce-7d997a93efa1.txn deleted file mode 100644 index bb9b7a4ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/149-5f955451-eb2d-4cc0-94ce-7d997a93efa1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1490-040605c2-1e35-4cc6-8b23-82b9495c44b7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1490-040605c2-1e35-4cc6-8b23-82b9495c44b7.txn deleted file mode 100644 index fea535088..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1490-040605c2-1e35-4cc6-8b23-82b9495c44b7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1491-7c557171-ecbe-476f-b9e3-6c444f18869e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1491-7c557171-ecbe-476f-b9e3-6c444f18869e.txn deleted file mode 100644 index 5eb30fa69..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1491-7c557171-ecbe-476f-b9e3-6c444f18869e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1492-3ed9ea73-4ec7-4747-9c0f-096f7ef8a28f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1492-3ed9ea73-4ec7-4747-9c0f-096f7ef8a28f.txn deleted file mode 100644 index 15ea5eab2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1492-3ed9ea73-4ec7-4747-9c0f-096f7ef8a28f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1493-27ddd74d-28c1-4791-aa3b-7f868e18a40f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1493-27ddd74d-28c1-4791-aa3b-7f868e18a40f.txn deleted file mode 100644 index cc5e14b65..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1493-27ddd74d-28c1-4791-aa3b-7f868e18a40f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1494-71944a96-6413-4f11-b5e2-506e2fe8a1ad.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1494-71944a96-6413-4f11-b5e2-506e2fe8a1ad.txn deleted file mode 100644 index 0bc8fbfa8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1494-71944a96-6413-4f11-b5e2-506e2fe8a1ad.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1495-f276de80-01c6-48b5-a0f0-fa11301df8cf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1495-f276de80-01c6-48b5-a0f0-fa11301df8cf.txn deleted file mode 100644 index 9fc0a1b16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1495-f276de80-01c6-48b5-a0f0-fa11301df8cf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1496-fb30ecbc-ba88-4674-8ec0-3c7fbd63c518.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1496-fb30ecbc-ba88-4674-8ec0-3c7fbd63c518.txn deleted file mode 100644 index 88a1724f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1496-fb30ecbc-ba88-4674-8ec0-3c7fbd63c518.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1497-abc2e58c-0095-49a0-a3f1-bc1adf069316.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1497-abc2e58c-0095-49a0-a3f1-bc1adf069316.txn deleted file mode 100644 index 873b7f85e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1497-abc2e58c-0095-49a0-a3f1-bc1adf069316.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1498-75767a09-0b3e-4878-b921-e9b34f43e548.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1498-75767a09-0b3e-4878-b921-e9b34f43e548.txn deleted file mode 100644 index 93835e50d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1498-75767a09-0b3e-4878-b921-e9b34f43e548.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1499-ec2c603e-1076-4672-8184-d414315e985d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1499-ec2c603e-1076-4672-8184-d414315e985d.txn deleted file mode 100644 index 67eb33ce5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1499-ec2c603e-1076-4672-8184-d414315e985d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/15-9c436436-6dab-4549-87ce-8d50d26541aa.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/15-9c436436-6dab-4549-87ce-8d50d26541aa.txn deleted file mode 100644 index 86081ff48..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/15-9c436436-6dab-4549-87ce-8d50d26541aa.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/150-547beb39-1d00-42d2-b770-f777b0e31739.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/150-547beb39-1d00-42d2-b770-f777b0e31739.txn deleted file mode 100644 index bde6ef89c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/150-547beb39-1d00-42d2-b770-f777b0e31739.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1500-aeba34e8-b4d0-44c7-b764-86742f85924d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1500-aeba34e8-b4d0-44c7-b764-86742f85924d.txn deleted file mode 100644 index 62088a4f4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1500-aeba34e8-b4d0-44c7-b764-86742f85924d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1501-7704f7e3-a16c-465f-a39a-15c8db99cea5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1501-7704f7e3-a16c-465f-a39a-15c8db99cea5.txn deleted file mode 100644 index e608a2c6c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1501-7704f7e3-a16c-465f-a39a-15c8db99cea5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1502-8f9a1b55-06b8-4f6c-a5d6-199706eed9fc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1502-8f9a1b55-06b8-4f6c-a5d6-199706eed9fc.txn deleted file mode 100644 index 68886e47a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1502-8f9a1b55-06b8-4f6c-a5d6-199706eed9fc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1503-e5d09829-f46b-4d38-ba06-bcea88854d12.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1503-e5d09829-f46b-4d38-ba06-bcea88854d12.txn deleted file mode 100644 index d823bf195..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1503-e5d09829-f46b-4d38-ba06-bcea88854d12.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1504-3fb5fa5d-9e27-4791-9d71-00332fda5d7e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1504-3fb5fa5d-9e27-4791-9d71-00332fda5d7e.txn deleted file mode 100644 index cd04b7a30..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1504-3fb5fa5d-9e27-4791-9d71-00332fda5d7e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1505-17cdfb1e-939e-4e62-abdb-8c95653277d3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1505-17cdfb1e-939e-4e62-abdb-8c95653277d3.txn deleted file mode 100644 index 5f8670e0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1505-17cdfb1e-939e-4e62-abdb-8c95653277d3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1506-5cf7bbe8-73b2-4723-ad08-daf582a94b0a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1506-5cf7bbe8-73b2-4723-ad08-daf582a94b0a.txn deleted file mode 100644 index 4e8e7d5ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1506-5cf7bbe8-73b2-4723-ad08-daf582a94b0a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1507-ab2604ac-586a-41af-bdb7-3e9089c47024.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1507-ab2604ac-586a-41af-bdb7-3e9089c47024.txn deleted file mode 100644 index d3a5b10b6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1507-ab2604ac-586a-41af-bdb7-3e9089c47024.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1508-27d95829-f679-4a6b-b83c-d7a94d869893.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1508-27d95829-f679-4a6b-b83c-d7a94d869893.txn deleted file mode 100644 index bb2b137e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1508-27d95829-f679-4a6b-b83c-d7a94d869893.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1509-51c97e78-91e5-455c-a5cb-98e2ba697f12.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1509-51c97e78-91e5-455c-a5cb-98e2ba697f12.txn deleted file mode 100644 index 96493a5f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1509-51c97e78-91e5-455c-a5cb-98e2ba697f12.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/151-00baea92-e493-463d-be73-3907b0415d07.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/151-00baea92-e493-463d-be73-3907b0415d07.txn deleted file mode 100644 index e0ed9eea4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/151-00baea92-e493-463d-be73-3907b0415d07.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1510-edac7d30-f9d4-4df5-beb2-7bb894d79d7c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1510-edac7d30-f9d4-4df5-beb2-7bb894d79d7c.txn deleted file mode 100644 index 27fc2a4cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1510-edac7d30-f9d4-4df5-beb2-7bb894d79d7c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1511-7b5ca279-0084-459f-b16e-d163759af8e8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1511-7b5ca279-0084-459f-b16e-d163759af8e8.txn deleted file mode 100644 index 8164c038e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1511-7b5ca279-0084-459f-b16e-d163759af8e8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1512-7e1459a8-fdb0-4005-bb5c-c920a2c78636.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1512-7e1459a8-fdb0-4005-bb5c-c920a2c78636.txn deleted file mode 100644 index bbdad09f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1512-7e1459a8-fdb0-4005-bb5c-c920a2c78636.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1513-c3f957dc-ae96-420a-abad-c1b829e24e96.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1513-c3f957dc-ae96-420a-abad-c1b829e24e96.txn deleted file mode 100644 index 261a6ab28..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1513-c3f957dc-ae96-420a-abad-c1b829e24e96.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1514-c4abb9f5-cc2a-4b59-b7ae-75effba2588f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1514-c4abb9f5-cc2a-4b59-b7ae-75effba2588f.txn deleted file mode 100644 index ae61ffdc4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1514-c4abb9f5-cc2a-4b59-b7ae-75effba2588f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1515-b5f0adc1-cb3b-425f-ac5d-11b91ea907e3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1515-b5f0adc1-cb3b-425f-ac5d-11b91ea907e3.txn deleted file mode 100644 index 3f501275f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1515-b5f0adc1-cb3b-425f-ac5d-11b91ea907e3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1516-32828fdf-3aea-4a48-ac79-572ea2286bae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1516-32828fdf-3aea-4a48-ac79-572ea2286bae.txn deleted file mode 100644 index c86ce4a0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1516-32828fdf-3aea-4a48-ac79-572ea2286bae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1517-433b97f4-5a36-4ce4-b77c-b2661036b175.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1517-433b97f4-5a36-4ce4-b77c-b2661036b175.txn deleted file mode 100644 index d43c1369a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1517-433b97f4-5a36-4ce4-b77c-b2661036b175.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1518-4159b4eb-e5e0-4af5-9070-550495b3a44e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1518-4159b4eb-e5e0-4af5-9070-550495b3a44e.txn deleted file mode 100644 index 8f3eb29e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1518-4159b4eb-e5e0-4af5-9070-550495b3a44e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1519-78edb532-f8c1-429f-82a4-11adc9df0d30.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1519-78edb532-f8c1-429f-82a4-11adc9df0d30.txn deleted file mode 100644 index 17f64a8e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1519-78edb532-f8c1-429f-82a4-11adc9df0d30.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/152-d349a0d7-18e6-470b-87c5-c09d720e348a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/152-d349a0d7-18e6-470b-87c5-c09d720e348a.txn deleted file mode 100644 index 08f53cdc3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/152-d349a0d7-18e6-470b-87c5-c09d720e348a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1520-6d193c46-b043-4946-8f02-b0ec6d0f432a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1520-6d193c46-b043-4946-8f02-b0ec6d0f432a.txn deleted file mode 100644 index 773e6ad46..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1520-6d193c46-b043-4946-8f02-b0ec6d0f432a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1521-6f31deee-a324-45f1-af8c-244053c2c75a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1521-6f31deee-a324-45f1-af8c-244053c2c75a.txn deleted file mode 100644 index eaf5d8536..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1521-6f31deee-a324-45f1-af8c-244053c2c75a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1522-f3ae60c2-5277-46f6-b933-bf3afd72cf15.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1522-f3ae60c2-5277-46f6-b933-bf3afd72cf15.txn deleted file mode 100644 index a05fd02d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1522-f3ae60c2-5277-46f6-b933-bf3afd72cf15.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1523-0c1d4b9c-51ea-4ead-8a66-f789f4494452.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1523-0c1d4b9c-51ea-4ead-8a66-f789f4494452.txn deleted file mode 100644 index 2c45797a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1523-0c1d4b9c-51ea-4ead-8a66-f789f4494452.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1524-d298c89f-05c6-4528-817c-603cc78fbfd8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1524-d298c89f-05c6-4528-817c-603cc78fbfd8.txn deleted file mode 100644 index 0c1093780..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1524-d298c89f-05c6-4528-817c-603cc78fbfd8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1525-80a726f5-505c-45b5-8e82-cb5e2dba5cee.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1525-80a726f5-505c-45b5-8e82-cb5e2dba5cee.txn deleted file mode 100644 index be16a9d8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1525-80a726f5-505c-45b5-8e82-cb5e2dba5cee.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1526-4957ce02-f634-488e-ba6f-d17cce0335b0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1526-4957ce02-f634-488e-ba6f-d17cce0335b0.txn deleted file mode 100644 index 93611a638..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1526-4957ce02-f634-488e-ba6f-d17cce0335b0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1527-972b4e99-8ba1-4f32-878f-a99b25fa4d4c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1527-972b4e99-8ba1-4f32-878f-a99b25fa4d4c.txn deleted file mode 100644 index bec63952d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1527-972b4e99-8ba1-4f32-878f-a99b25fa4d4c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1528-5bd198a8-ab76-4f58-b163-1b9d9d7ec89a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1528-5bd198a8-ab76-4f58-b163-1b9d9d7ec89a.txn deleted file mode 100644 index 15a84de1f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1528-5bd198a8-ab76-4f58-b163-1b9d9d7ec89a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1529-6a753702-3eb1-4100-8340-cccd6d898fe8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1529-6a753702-3eb1-4100-8340-cccd6d898fe8.txn deleted file mode 100644 index 6591c0597..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1529-6a753702-3eb1-4100-8340-cccd6d898fe8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/153-0fd9a695-5a77-4ab8-9d0b-41cc864d7050.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/153-0fd9a695-5a77-4ab8-9d0b-41cc864d7050.txn deleted file mode 100644 index 5e7c55502..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/153-0fd9a695-5a77-4ab8-9d0b-41cc864d7050.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1530-bae99653-167d-4ae5-a23b-ebb8206f1c50.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1530-bae99653-167d-4ae5-a23b-ebb8206f1c50.txn deleted file mode 100644 index 4f08adcff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1530-bae99653-167d-4ae5-a23b-ebb8206f1c50.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1531-9ece85cf-f320-421f-bb11-5bb8ba1f4444.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1531-9ece85cf-f320-421f-bb11-5bb8ba1f4444.txn deleted file mode 100644 index 1f61d607c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1531-9ece85cf-f320-421f-bb11-5bb8ba1f4444.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1532-9f63e6d4-73b3-4c3b-9400-29b2f1a70380.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1532-9f63e6d4-73b3-4c3b-9400-29b2f1a70380.txn deleted file mode 100644 index 9c7ad1213..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1532-9f63e6d4-73b3-4c3b-9400-29b2f1a70380.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1533-f1eaf66a-4511-44b6-9aa9-5ad641082bb4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1533-f1eaf66a-4511-44b6-9aa9-5ad641082bb4.txn deleted file mode 100644 index 637423d1a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1533-f1eaf66a-4511-44b6-9aa9-5ad641082bb4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1534-d547aa69-bf39-48b4-b6ca-43fbbd0bb327.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1534-d547aa69-bf39-48b4-b6ca-43fbbd0bb327.txn deleted file mode 100644 index da99f502d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1534-d547aa69-bf39-48b4-b6ca-43fbbd0bb327.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1535-29a5ba7d-b084-40fa-80ce-6ee63474bfc8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1535-29a5ba7d-b084-40fa-80ce-6ee63474bfc8.txn deleted file mode 100644 index 3e05ceb7d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1535-29a5ba7d-b084-40fa-80ce-6ee63474bfc8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1536-5e97af89-8e0a-4a5b-be9d-0141a6c28a36.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1536-5e97af89-8e0a-4a5b-be9d-0141a6c28a36.txn deleted file mode 100644 index 270daff53..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1536-5e97af89-8e0a-4a5b-be9d-0141a6c28a36.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1537-619dcd61-e268-4bfb-8589-412796fe5dd8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1537-619dcd61-e268-4bfb-8589-412796fe5dd8.txn deleted file mode 100644 index 5cf7cf9fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1537-619dcd61-e268-4bfb-8589-412796fe5dd8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1538-9f665ac3-6e3b-449e-a209-9ab45a5e76a1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1538-9f665ac3-6e3b-449e-a209-9ab45a5e76a1.txn deleted file mode 100644 index 070fadd07..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1538-9f665ac3-6e3b-449e-a209-9ab45a5e76a1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1539-cc4eacb8-8927-4571-9250-086f911a3bd4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1539-cc4eacb8-8927-4571-9250-086f911a3bd4.txn deleted file mode 100644 index 18b52c882..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1539-cc4eacb8-8927-4571-9250-086f911a3bd4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/154-305bd8f1-3f4e-43a0-939e-5a19726262f0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/154-305bd8f1-3f4e-43a0-939e-5a19726262f0.txn deleted file mode 100644 index 729242b31..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/154-305bd8f1-3f4e-43a0-939e-5a19726262f0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1540-2f37712d-79e1-489b-bfc2-1b631bf0c51f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1540-2f37712d-79e1-489b-bfc2-1b631bf0c51f.txn deleted file mode 100644 index 0519e2583..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1540-2f37712d-79e1-489b-bfc2-1b631bf0c51f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1541-e7d169e8-5e7e-4ccb-a02d-0521e5b95a71.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1541-e7d169e8-5e7e-4ccb-a02d-0521e5b95a71.txn deleted file mode 100644 index a4b2dff6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1541-e7d169e8-5e7e-4ccb-a02d-0521e5b95a71.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1542-0c2f3ed5-bcc8-4742-bb1f-5889e85eebcd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1542-0c2f3ed5-bcc8-4742-bb1f-5889e85eebcd.txn deleted file mode 100644 index 93a5f9362..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1542-0c2f3ed5-bcc8-4742-bb1f-5889e85eebcd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1543-2be37af6-4eaa-40ee-b591-4447ee7a4dd0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1543-2be37af6-4eaa-40ee-b591-4447ee7a4dd0.txn deleted file mode 100644 index 0418270ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1543-2be37af6-4eaa-40ee-b591-4447ee7a4dd0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1544-9c16b2f2-a64f-429c-b304-30a940ca5d14.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1544-9c16b2f2-a64f-429c-b304-30a940ca5d14.txn deleted file mode 100644 index 28ac05df4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1544-9c16b2f2-a64f-429c-b304-30a940ca5d14.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1545-bb3b5dd9-4d6c-4a11-9d5c-91d8c51d1264.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1545-bb3b5dd9-4d6c-4a11-9d5c-91d8c51d1264.txn deleted file mode 100644 index 19a732df6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1545-bb3b5dd9-4d6c-4a11-9d5c-91d8c51d1264.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1546-dc471fd1-8581-4def-b9ae-c7a2d6ed8308.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1546-dc471fd1-8581-4def-b9ae-c7a2d6ed8308.txn deleted file mode 100644 index 71481d088..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1546-dc471fd1-8581-4def-b9ae-c7a2d6ed8308.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1547-5f010286-c037-4399-8c56-4710b8ea4e96.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1547-5f010286-c037-4399-8c56-4710b8ea4e96.txn deleted file mode 100644 index a696634e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1547-5f010286-c037-4399-8c56-4710b8ea4e96.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1548-cea02893-849f-40aa-8ee9-8b128f780fd1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1548-cea02893-849f-40aa-8ee9-8b128f780fd1.txn deleted file mode 100644 index 62e5dbf37..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1548-cea02893-849f-40aa-8ee9-8b128f780fd1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1549-e2bcf726-905e-4572-87aa-cd2f0585aeb1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1549-e2bcf726-905e-4572-87aa-cd2f0585aeb1.txn deleted file mode 100644 index 6f369e7ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1549-e2bcf726-905e-4572-87aa-cd2f0585aeb1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/155-0190b416-4622-492a-87bb-c7aa2b1c0094.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/155-0190b416-4622-492a-87bb-c7aa2b1c0094.txn deleted file mode 100644 index fe93149a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/155-0190b416-4622-492a-87bb-c7aa2b1c0094.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1550-50dd7eec-cb22-4454-a931-648943c0e745.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1550-50dd7eec-cb22-4454-a931-648943c0e745.txn deleted file mode 100644 index cf3979610..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1550-50dd7eec-cb22-4454-a931-648943c0e745.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1551-36b4373f-f7c3-4fee-8c4c-648e003b51e1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1551-36b4373f-f7c3-4fee-8c4c-648e003b51e1.txn deleted file mode 100644 index 5e286bb16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1551-36b4373f-f7c3-4fee-8c4c-648e003b51e1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1552-8ce4a201-6338-4313-9bce-917d30ecf722.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1552-8ce4a201-6338-4313-9bce-917d30ecf722.txn deleted file mode 100644 index dc880340d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1552-8ce4a201-6338-4313-9bce-917d30ecf722.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1553-eb57cf0a-ce33-4020-876a-04b1ff5dd00c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1553-eb57cf0a-ce33-4020-876a-04b1ff5dd00c.txn deleted file mode 100644 index e4282544d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1553-eb57cf0a-ce33-4020-876a-04b1ff5dd00c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1554-1966c49e-723d-4a4a-80ad-a1beb319e7fb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1554-1966c49e-723d-4a4a-80ad-a1beb319e7fb.txn deleted file mode 100644 index 1d970ae5f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1554-1966c49e-723d-4a4a-80ad-a1beb319e7fb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1555-3415ec02-a5a9-49ae-99e9-e46224ed9123.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1555-3415ec02-a5a9-49ae-99e9-e46224ed9123.txn deleted file mode 100644 index 5fb7fb6e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1555-3415ec02-a5a9-49ae-99e9-e46224ed9123.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1556-307fa458-f2a6-4a3b-968d-df9fb96a6b3b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1556-307fa458-f2a6-4a3b-968d-df9fb96a6b3b.txn deleted file mode 100644 index 1e93a457e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1556-307fa458-f2a6-4a3b-968d-df9fb96a6b3b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1557-e40137c0-9554-4555-add7-506f93eb4f72.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1557-e40137c0-9554-4555-add7-506f93eb4f72.txn deleted file mode 100644 index fa025a141..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1557-e40137c0-9554-4555-add7-506f93eb4f72.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1558-21164033-4b2a-4bc7-ab56-3277b3bfeaf1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1558-21164033-4b2a-4bc7-ab56-3277b3bfeaf1.txn deleted file mode 100644 index 262346e01..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1558-21164033-4b2a-4bc7-ab56-3277b3bfeaf1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1559-8469f983-1703-464a-8017-2dfda24d5204.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1559-8469f983-1703-464a-8017-2dfda24d5204.txn deleted file mode 100644 index 255239685..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1559-8469f983-1703-464a-8017-2dfda24d5204.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/156-bd9b314e-5d23-4b56-9c61-35dd763c1525.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/156-bd9b314e-5d23-4b56-9c61-35dd763c1525.txn deleted file mode 100644 index 9efa3ee23..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/156-bd9b314e-5d23-4b56-9c61-35dd763c1525.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1560-4cadbfad-0071-4a04-b7a2-f26ed21646b7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1560-4cadbfad-0071-4a04-b7a2-f26ed21646b7.txn deleted file mode 100644 index e1506572b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1560-4cadbfad-0071-4a04-b7a2-f26ed21646b7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1561-b64c0b3c-c242-477d-8f03-8d3eb55c37cb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1561-b64c0b3c-c242-477d-8f03-8d3eb55c37cb.txn deleted file mode 100644 index d1adc815a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1561-b64c0b3c-c242-477d-8f03-8d3eb55c37cb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1562-960e8e46-f12f-4b84-bf7e-627ef150e0b4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1562-960e8e46-f12f-4b84-bf7e-627ef150e0b4.txn deleted file mode 100644 index 327a47b10..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1562-960e8e46-f12f-4b84-bf7e-627ef150e0b4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1563-3bb9ca7e-8617-4b5d-9aab-91ff66a00a00.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1563-3bb9ca7e-8617-4b5d-9aab-91ff66a00a00.txn deleted file mode 100644 index 1641fe05c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1563-3bb9ca7e-8617-4b5d-9aab-91ff66a00a00.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1564-2bca5326-e8fc-4ae2-87d4-4ffb42db03a9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1564-2bca5326-e8fc-4ae2-87d4-4ffb42db03a9.txn deleted file mode 100644 index 6e0485d26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1564-2bca5326-e8fc-4ae2-87d4-4ffb42db03a9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1565-94d8cd6e-81cf-4d3b-9bd5-f5d9670cb84d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1565-94d8cd6e-81cf-4d3b-9bd5-f5d9670cb84d.txn deleted file mode 100644 index 08151bf3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1565-94d8cd6e-81cf-4d3b-9bd5-f5d9670cb84d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1566-b3d608e2-5404-4583-916c-218cbdb85818.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1566-b3d608e2-5404-4583-916c-218cbdb85818.txn deleted file mode 100644 index 75822e960..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1566-b3d608e2-5404-4583-916c-218cbdb85818.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1567-8a24469e-3166-4090-9bd5-33d9881d900d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1567-8a24469e-3166-4090-9bd5-33d9881d900d.txn deleted file mode 100644 index 8cc3181d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1567-8a24469e-3166-4090-9bd5-33d9881d900d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1568-2a018615-7efc-4c46-bfc1-25f1d5c3a251.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1568-2a018615-7efc-4c46-bfc1-25f1d5c3a251.txn deleted file mode 100644 index 1029f2560..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1568-2a018615-7efc-4c46-bfc1-25f1d5c3a251.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1569-e4cd4a88-8d63-4435-9dea-a27d9a8098cb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1569-e4cd4a88-8d63-4435-9dea-a27d9a8098cb.txn deleted file mode 100644 index 6b151f9b2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1569-e4cd4a88-8d63-4435-9dea-a27d9a8098cb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/157-c661aaba-9be0-4174-a43a-2f07dd30bba7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/157-c661aaba-9be0-4174-a43a-2f07dd30bba7.txn deleted file mode 100644 index b4b8314f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/157-c661aaba-9be0-4174-a43a-2f07dd30bba7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1570-af129c34-7db9-4fa3-be5b-dd771ac8e601.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1570-af129c34-7db9-4fa3-be5b-dd771ac8e601.txn deleted file mode 100644 index cb9c28482..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1570-af129c34-7db9-4fa3-be5b-dd771ac8e601.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1571-8ee77591-f101-4b15-b0ce-213df71f5110.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1571-8ee77591-f101-4b15-b0ce-213df71f5110.txn deleted file mode 100644 index 73a55d23f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1571-8ee77591-f101-4b15-b0ce-213df71f5110.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1572-ec7095f0-2740-475a-94f1-13dd08ffd973.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1572-ec7095f0-2740-475a-94f1-13dd08ffd973.txn deleted file mode 100644 index c9b4ddc5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1572-ec7095f0-2740-475a-94f1-13dd08ffd973.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1573-8e1f8c0a-9e2a-4f25-91ea-713732d41efb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1573-8e1f8c0a-9e2a-4f25-91ea-713732d41efb.txn deleted file mode 100644 index ec8780f16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1573-8e1f8c0a-9e2a-4f25-91ea-713732d41efb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1574-b011d57f-6d34-45d3-99b6-672910c1c1f0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1574-b011d57f-6d34-45d3-99b6-672910c1c1f0.txn deleted file mode 100644 index d9a074843..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1574-b011d57f-6d34-45d3-99b6-672910c1c1f0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1575-74b3ad2f-a1b3-4ab7-aa5e-bcb3ecfca8de.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1575-74b3ad2f-a1b3-4ab7-aa5e-bcb3ecfca8de.txn deleted file mode 100644 index 86c84e7a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1575-74b3ad2f-a1b3-4ab7-aa5e-bcb3ecfca8de.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1576-a03878a9-d739-4963-8b62-f9b78ffb2420.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1576-a03878a9-d739-4963-8b62-f9b78ffb2420.txn deleted file mode 100644 index e4d4d924e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1576-a03878a9-d739-4963-8b62-f9b78ffb2420.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1577-610e3890-3bf6-43cb-9c7b-68b3bfa625fe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1577-610e3890-3bf6-43cb-9c7b-68b3bfa625fe.txn deleted file mode 100644 index 6eaba1c6c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1577-610e3890-3bf6-43cb-9c7b-68b3bfa625fe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1578-ea1f22ca-abdb-4ff1-8480-43c7c411fafd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1578-ea1f22ca-abdb-4ff1-8480-43c7c411fafd.txn deleted file mode 100644 index d21c17159..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1578-ea1f22ca-abdb-4ff1-8480-43c7c411fafd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1579-6dcae6a4-bd58-491e-a897-0a2f0705ac9f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1579-6dcae6a4-bd58-491e-a897-0a2f0705ac9f.txn deleted file mode 100644 index 87f08e00f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1579-6dcae6a4-bd58-491e-a897-0a2f0705ac9f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/158-6ab2d35a-44c8-4435-a527-b9afc4265548.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/158-6ab2d35a-44c8-4435-a527-b9afc4265548.txn deleted file mode 100644 index d37b66db3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/158-6ab2d35a-44c8-4435-a527-b9afc4265548.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1580-fa96d03f-492a-414c-b558-4dad7a894333.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1580-fa96d03f-492a-414c-b558-4dad7a894333.txn deleted file mode 100644 index c42c60fd9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1580-fa96d03f-492a-414c-b558-4dad7a894333.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1581-6f2491ac-8366-4f2e-bfac-79eaae9db40a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1581-6f2491ac-8366-4f2e-bfac-79eaae9db40a.txn deleted file mode 100644 index 4b5918b61..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1581-6f2491ac-8366-4f2e-bfac-79eaae9db40a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1582-483a90f2-6f03-46bd-a256-e0f37cab89fc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1582-483a90f2-6f03-46bd-a256-e0f37cab89fc.txn deleted file mode 100644 index f0228ff75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1582-483a90f2-6f03-46bd-a256-e0f37cab89fc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1583-d7cc7f05-ea59-4353-b907-f38f6c333e73.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1583-d7cc7f05-ea59-4353-b907-f38f6c333e73.txn deleted file mode 100644 index 236445a9b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1583-d7cc7f05-ea59-4353-b907-f38f6c333e73.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1584-8088ac91-bec1-40f2-9017-b8777c79a9d4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1584-8088ac91-bec1-40f2-9017-b8777c79a9d4.txn deleted file mode 100644 index be44ce227..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1584-8088ac91-bec1-40f2-9017-b8777c79a9d4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1585-f66d1d4f-9eb9-4950-adb3-b0cbbd841280.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1585-f66d1d4f-9eb9-4950-adb3-b0cbbd841280.txn deleted file mode 100644 index 3464b6d4b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1585-f66d1d4f-9eb9-4950-adb3-b0cbbd841280.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1586-e8851ab4-6df2-489a-8456-903c716e64de.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1586-e8851ab4-6df2-489a-8456-903c716e64de.txn deleted file mode 100644 index 70e359e89..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1586-e8851ab4-6df2-489a-8456-903c716e64de.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1587-eeb8d3e5-2e11-4a10-a1a9-5f24287a547c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1587-eeb8d3e5-2e11-4a10-a1a9-5f24287a547c.txn deleted file mode 100644 index 9800b9363..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1587-eeb8d3e5-2e11-4a10-a1a9-5f24287a547c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1588-fc13ab4f-48a5-43e2-9bd5-d55bc9500d17.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1588-fc13ab4f-48a5-43e2-9bd5-d55bc9500d17.txn deleted file mode 100644 index b03ed5a16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1588-fc13ab4f-48a5-43e2-9bd5-d55bc9500d17.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1589-8021bd1a-cff8-447e-bca3-d3c0cfc78547.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1589-8021bd1a-cff8-447e-bca3-d3c0cfc78547.txn deleted file mode 100644 index 87174f562..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1589-8021bd1a-cff8-447e-bca3-d3c0cfc78547.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/159-77d3d1d4-f624-41dd-9ff8-22a0b31f097a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/159-77d3d1d4-f624-41dd-9ff8-22a0b31f097a.txn deleted file mode 100644 index 1a123725f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/159-77d3d1d4-f624-41dd-9ff8-22a0b31f097a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1590-026090e2-37c7-4548-84ad-542e4131a9d6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1590-026090e2-37c7-4548-84ad-542e4131a9d6.txn deleted file mode 100644 index 6a42d0db7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1590-026090e2-37c7-4548-84ad-542e4131a9d6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1591-04a8f821-be5a-47e0-8937-71a294016584.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1591-04a8f821-be5a-47e0-8937-71a294016584.txn deleted file mode 100644 index dc0e1226c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1591-04a8f821-be5a-47e0-8937-71a294016584.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1592-3189bd3a-a7d4-429a-be51-54746c9bedfc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1592-3189bd3a-a7d4-429a-be51-54746c9bedfc.txn deleted file mode 100644 index b89197d3a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1592-3189bd3a-a7d4-429a-be51-54746c9bedfc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1593-10ec3b60-6932-48ff-a67c-7dda552de5b8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1593-10ec3b60-6932-48ff-a67c-7dda552de5b8.txn deleted file mode 100644 index 7d5d03e8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1593-10ec3b60-6932-48ff-a67c-7dda552de5b8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1594-cf00deea-918c-4e08-bd57-e039b826d198.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1594-cf00deea-918c-4e08-bd57-e039b826d198.txn deleted file mode 100644 index fdad9ed26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1594-cf00deea-918c-4e08-bd57-e039b826d198.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1595-57646122-6f53-4618-865d-0e59363ba914.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1595-57646122-6f53-4618-865d-0e59363ba914.txn deleted file mode 100644 index 2c0f755a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1595-57646122-6f53-4618-865d-0e59363ba914.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1596-44d8efb2-5df0-4e7e-b452-c309a5169af2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1596-44d8efb2-5df0-4e7e-b452-c309a5169af2.txn deleted file mode 100644 index 48d42637a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1596-44d8efb2-5df0-4e7e-b452-c309a5169af2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1597-63b08cc3-48d2-48d6-97aa-cdd87ee3dc59.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1597-63b08cc3-48d2-48d6-97aa-cdd87ee3dc59.txn deleted file mode 100644 index 8985a3f77..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1597-63b08cc3-48d2-48d6-97aa-cdd87ee3dc59.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1598-d0ca2da6-4561-4bbf-bac1-b0e6da3fcbd3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1598-d0ca2da6-4561-4bbf-bac1-b0e6da3fcbd3.txn deleted file mode 100644 index 4aafa4361..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1598-d0ca2da6-4561-4bbf-bac1-b0e6da3fcbd3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1599-1d7b7b5b-23fe-475d-992a-259e7fed9df3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1599-1d7b7b5b-23fe-475d-992a-259e7fed9df3.txn deleted file mode 100644 index 649d0b00a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1599-1d7b7b5b-23fe-475d-992a-259e7fed9df3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/16-b1fd8386-c711-448c-86d4-9b1d98f9896c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/16-b1fd8386-c711-448c-86d4-9b1d98f9896c.txn deleted file mode 100644 index 967f4ca37..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/16-b1fd8386-c711-448c-86d4-9b1d98f9896c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/160-27556914-5e4a-4d51-8b53-d855f1cf065e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/160-27556914-5e4a-4d51-8b53-d855f1cf065e.txn deleted file mode 100644 index 9e32ce507..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/160-27556914-5e4a-4d51-8b53-d855f1cf065e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1600-acd932ef-83f4-4b09-b43b-33316eaa170e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1600-acd932ef-83f4-4b09-b43b-33316eaa170e.txn deleted file mode 100644 index cbccefe1b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1600-acd932ef-83f4-4b09-b43b-33316eaa170e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1601-b6f042bf-da6a-4ccc-8e20-97cc4bb1c0ef.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1601-b6f042bf-da6a-4ccc-8e20-97cc4bb1c0ef.txn deleted file mode 100644 index 2ebc39f4b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1601-b6f042bf-da6a-4ccc-8e20-97cc4bb1c0ef.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1602-c3cbf6f1-ba77-48dd-9620-b45d953b5a44.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1602-c3cbf6f1-ba77-48dd-9620-b45d953b5a44.txn deleted file mode 100644 index 74a138c9b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1602-c3cbf6f1-ba77-48dd-9620-b45d953b5a44.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1603-61e7aab9-df39-4075-816f-3425139b76e2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1603-61e7aab9-df39-4075-816f-3425139b76e2.txn deleted file mode 100644 index 1db044a08..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1603-61e7aab9-df39-4075-816f-3425139b76e2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1604-9ccd9116-7c47-4852-97cb-63fffedfca24.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1604-9ccd9116-7c47-4852-97cb-63fffedfca24.txn deleted file mode 100644 index fe610823e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1604-9ccd9116-7c47-4852-97cb-63fffedfca24.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1605-90cb79b6-c9ae-4d6d-9cc6-bfa8e5981e72.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1605-90cb79b6-c9ae-4d6d-9cc6-bfa8e5981e72.txn deleted file mode 100644 index 2e496cf56..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1605-90cb79b6-c9ae-4d6d-9cc6-bfa8e5981e72.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1606-faf964a2-0360-4278-aef0-3a8427ea18ed.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1606-faf964a2-0360-4278-aef0-3a8427ea18ed.txn deleted file mode 100644 index a4aba5c31..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1606-faf964a2-0360-4278-aef0-3a8427ea18ed.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1607-5e7d3b5b-94fa-47ac-b15e-5bc3d88d15b1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1607-5e7d3b5b-94fa-47ac-b15e-5bc3d88d15b1.txn deleted file mode 100644 index 7c8fa6866..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1607-5e7d3b5b-94fa-47ac-b15e-5bc3d88d15b1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1608-9e6efb62-75c4-4595-8956-ab55adf38dc7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1608-9e6efb62-75c4-4595-8956-ab55adf38dc7.txn deleted file mode 100644 index 3b15a8239..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1608-9e6efb62-75c4-4595-8956-ab55adf38dc7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1609-ab9ee13d-58cb-4de5-8317-dd74246320f3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1609-ab9ee13d-58cb-4de5-8317-dd74246320f3.txn deleted file mode 100644 index 7944c3f96..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1609-ab9ee13d-58cb-4de5-8317-dd74246320f3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/161-7e9812f3-1ed2-4d69-99fc-2329ae9bcf43.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/161-7e9812f3-1ed2-4d69-99fc-2329ae9bcf43.txn deleted file mode 100644 index 06e5fe526..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/161-7e9812f3-1ed2-4d69-99fc-2329ae9bcf43.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1610-49ab262a-db02-4366-9bb5-628b0dd6627f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1610-49ab262a-db02-4366-9bb5-628b0dd6627f.txn deleted file mode 100644 index ddb25c81f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1610-49ab262a-db02-4366-9bb5-628b0dd6627f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1611-ccb17bd2-f4b0-475f-b27a-4191e8e3a063.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1611-ccb17bd2-f4b0-475f-b27a-4191e8e3a063.txn deleted file mode 100644 index 114c8a7e0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1611-ccb17bd2-f4b0-475f-b27a-4191e8e3a063.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1612-e658f2f5-4047-4e72-939b-316d8ae0bcbb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1612-e658f2f5-4047-4e72-939b-316d8ae0bcbb.txn deleted file mode 100644 index c7b0ab5d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1612-e658f2f5-4047-4e72-939b-316d8ae0bcbb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1613-5a10d9d2-7985-40f6-b890-c752ac774cd2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1613-5a10d9d2-7985-40f6-b890-c752ac774cd2.txn deleted file mode 100644 index 439f2e949..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1613-5a10d9d2-7985-40f6-b890-c752ac774cd2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1614-e0959adb-734a-4adf-ab73-f4d570d02fe9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1614-e0959adb-734a-4adf-ab73-f4d570d02fe9.txn deleted file mode 100644 index 6baccd94a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1614-e0959adb-734a-4adf-ab73-f4d570d02fe9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1615-5e97ef0f-4265-4f8b-92b6-1790419e8050.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1615-5e97ef0f-4265-4f8b-92b6-1790419e8050.txn deleted file mode 100644 index 00e993b0f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1615-5e97ef0f-4265-4f8b-92b6-1790419e8050.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1616-ab0d845d-09ee-40e5-979f-72b6403e0a93.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1616-ab0d845d-09ee-40e5-979f-72b6403e0a93.txn deleted file mode 100644 index 7c8f89c16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1616-ab0d845d-09ee-40e5-979f-72b6403e0a93.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1617-20fbfdc5-56a0-4e1e-844b-b4d1c30ff5d8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1617-20fbfdc5-56a0-4e1e-844b-b4d1c30ff5d8.txn deleted file mode 100644 index ce9a8c001..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1617-20fbfdc5-56a0-4e1e-844b-b4d1c30ff5d8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1618-837b89e3-c212-49d1-a3a7-05af855b4bc5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1618-837b89e3-c212-49d1-a3a7-05af855b4bc5.txn deleted file mode 100644 index 115dc95b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1618-837b89e3-c212-49d1-a3a7-05af855b4bc5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1619-d7547d88-670c-4de5-b709-4db2daf5e9e5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1619-d7547d88-670c-4de5-b709-4db2daf5e9e5.txn deleted file mode 100644 index 2c92d33eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1619-d7547d88-670c-4de5-b709-4db2daf5e9e5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/162-e74b937a-ee6a-4614-96b6-03543729c920.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/162-e74b937a-ee6a-4614-96b6-03543729c920.txn deleted file mode 100644 index 11168890b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/162-e74b937a-ee6a-4614-96b6-03543729c920.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1620-2d683c1d-8585-4589-95b7-ecba569a4c44.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1620-2d683c1d-8585-4589-95b7-ecba569a4c44.txn deleted file mode 100644 index ba76ba0fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1620-2d683c1d-8585-4589-95b7-ecba569a4c44.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1621-4b3122ef-1b04-4bfe-886a-ef72df179329.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1621-4b3122ef-1b04-4bfe-886a-ef72df179329.txn deleted file mode 100644 index a13bc401f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1621-4b3122ef-1b04-4bfe-886a-ef72df179329.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1622-ebb51844-761e-4382-a134-6c3f410d7099.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1622-ebb51844-761e-4382-a134-6c3f410d7099.txn deleted file mode 100644 index f029e93c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1622-ebb51844-761e-4382-a134-6c3f410d7099.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1623-20045d12-b6b2-480e-96b8-5d8dc1c0ae64.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1623-20045d12-b6b2-480e-96b8-5d8dc1c0ae64.txn deleted file mode 100644 index e4c4cf27a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1623-20045d12-b6b2-480e-96b8-5d8dc1c0ae64.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1624-cd6fc100-c335-4eb5-8dab-74ca29223f99.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1624-cd6fc100-c335-4eb5-8dab-74ca29223f99.txn deleted file mode 100644 index 42371d495..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1624-cd6fc100-c335-4eb5-8dab-74ca29223f99.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1625-95e076a6-29c8-4759-89bb-7acdbb7321bc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1625-95e076a6-29c8-4759-89bb-7acdbb7321bc.txn deleted file mode 100644 index 150aebd6f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1625-95e076a6-29c8-4759-89bb-7acdbb7321bc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1626-747205b5-d7ed-49e4-add5-21c047717804.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1626-747205b5-d7ed-49e4-add5-21c047717804.txn deleted file mode 100644 index a875ac5ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1626-747205b5-d7ed-49e4-add5-21c047717804.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1627-b4dcf89c-32d9-4210-b709-c1f4499a75aa.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1627-b4dcf89c-32d9-4210-b709-c1f4499a75aa.txn deleted file mode 100644 index 607bc1565..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1627-b4dcf89c-32d9-4210-b709-c1f4499a75aa.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1628-818988cb-bec6-4b9b-ab96-6366a05fe0ef.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1628-818988cb-bec6-4b9b-ab96-6366a05fe0ef.txn deleted file mode 100644 index f00b9b94e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1628-818988cb-bec6-4b9b-ab96-6366a05fe0ef.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1629-68b0626e-b2bf-4aee-a745-c77f340b7f73.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1629-68b0626e-b2bf-4aee-a745-c77f340b7f73.txn deleted file mode 100644 index f9c62b92e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1629-68b0626e-b2bf-4aee-a745-c77f340b7f73.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/163-6b2f0ee1-c69e-4b20-ad2c-77fc2764ad07.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/163-6b2f0ee1-c69e-4b20-ad2c-77fc2764ad07.txn deleted file mode 100644 index 3ba3452d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/163-6b2f0ee1-c69e-4b20-ad2c-77fc2764ad07.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1630-2afe0b5d-78c7-4182-8a64-292f41482e34.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1630-2afe0b5d-78c7-4182-8a64-292f41482e34.txn deleted file mode 100644 index 6a1d820e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1630-2afe0b5d-78c7-4182-8a64-292f41482e34.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1631-6339db39-3fad-4420-9b84-ae433989a29c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1631-6339db39-3fad-4420-9b84-ae433989a29c.txn deleted file mode 100644 index 334ada907..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1631-6339db39-3fad-4420-9b84-ae433989a29c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1632-81092f86-484f-4194-9a94-db1017066a3c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1632-81092f86-484f-4194-9a94-db1017066a3c.txn deleted file mode 100644 index 2c4985d30..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1632-81092f86-484f-4194-9a94-db1017066a3c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1633-ef605ace-0119-40c2-afb3-92d6c1e2d839.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1633-ef605ace-0119-40c2-afb3-92d6c1e2d839.txn deleted file mode 100644 index f947eceee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1633-ef605ace-0119-40c2-afb3-92d6c1e2d839.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1634-07185f93-5c9e-4b62-ad1c-d264b7b0138c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1634-07185f93-5c9e-4b62-ad1c-d264b7b0138c.txn deleted file mode 100644 index d7f687a5c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1634-07185f93-5c9e-4b62-ad1c-d264b7b0138c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1635-db2d3bd0-9834-43b4-8ea7-209be37931d4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1635-db2d3bd0-9834-43b4-8ea7-209be37931d4.txn deleted file mode 100644 index 576d1fcc7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1635-db2d3bd0-9834-43b4-8ea7-209be37931d4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1636-9990a9f4-8ffa-4ded-bb77-a21626f275de.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1636-9990a9f4-8ffa-4ded-bb77-a21626f275de.txn deleted file mode 100644 index aebedfcaf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1636-9990a9f4-8ffa-4ded-bb77-a21626f275de.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1637-26e5c556-3765-41f7-b30e-b072a64f5329.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1637-26e5c556-3765-41f7-b30e-b072a64f5329.txn deleted file mode 100644 index 06b1f648b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1637-26e5c556-3765-41f7-b30e-b072a64f5329.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1638-8888a18f-b1c1-47ae-bba3-a6af65225e89.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1638-8888a18f-b1c1-47ae-bba3-a6af65225e89.txn deleted file mode 100644 index 8c73b1b3a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1638-8888a18f-b1c1-47ae-bba3-a6af65225e89.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1639-c6fd294c-07c1-4fe0-beb5-ef5432945ff8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1639-c6fd294c-07c1-4fe0-beb5-ef5432945ff8.txn deleted file mode 100644 index a36eba3e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1639-c6fd294c-07c1-4fe0-beb5-ef5432945ff8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/164-371eaa1e-2eaa-4839-9c10-197d94950db4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/164-371eaa1e-2eaa-4839-9c10-197d94950db4.txn deleted file mode 100644 index faf4283ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/164-371eaa1e-2eaa-4839-9c10-197d94950db4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1640-83d98668-4b69-4736-9e1a-4b51757fce4c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1640-83d98668-4b69-4736-9e1a-4b51757fce4c.txn deleted file mode 100644 index b3a503b4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1640-83d98668-4b69-4736-9e1a-4b51757fce4c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1641-45c990fe-cd79-4456-a166-44d9d418aa46.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1641-45c990fe-cd79-4456-a166-44d9d418aa46.txn deleted file mode 100644 index 824eedc6c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1641-45c990fe-cd79-4456-a166-44d9d418aa46.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1642-606b1305-4535-4194-a95c-c572d94eb62a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1642-606b1305-4535-4194-a95c-c572d94eb62a.txn deleted file mode 100644 index 9884785c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1642-606b1305-4535-4194-a95c-c572d94eb62a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1643-e0fdbb99-6b5b-4761-a55a-a4a7c2962a00.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1643-e0fdbb99-6b5b-4761-a55a-a4a7c2962a00.txn deleted file mode 100644 index 85144db13..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1643-e0fdbb99-6b5b-4761-a55a-a4a7c2962a00.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1644-54e7d824-bd96-4d45-9a19-4ca4d0b5b257.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1644-54e7d824-bd96-4d45-9a19-4ca4d0b5b257.txn deleted file mode 100644 index 623e75a72..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1644-54e7d824-bd96-4d45-9a19-4ca4d0b5b257.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1645-97ce97ab-bb17-4e31-9c6a-b4dd21c500a7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1645-97ce97ab-bb17-4e31-9c6a-b4dd21c500a7.txn deleted file mode 100644 index 0d53a0df8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1645-97ce97ab-bb17-4e31-9c6a-b4dd21c500a7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1646-ffe64b16-e543-4789-a17b-2fd941c77cbb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1646-ffe64b16-e543-4789-a17b-2fd941c77cbb.txn deleted file mode 100644 index 2c8d2372d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1646-ffe64b16-e543-4789-a17b-2fd941c77cbb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1647-59aa72d7-d84f-401c-97b2-858ddc01ee2f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1647-59aa72d7-d84f-401c-97b2-858ddc01ee2f.txn deleted file mode 100644 index 31b64753c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1647-59aa72d7-d84f-401c-97b2-858ddc01ee2f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1648-739d245d-ec62-4a65-a817-b4381a1c5e56.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1648-739d245d-ec62-4a65-a817-b4381a1c5e56.txn deleted file mode 100644 index a1d9968a6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1648-739d245d-ec62-4a65-a817-b4381a1c5e56.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1649-245df782-a225-476e-a16b-0097b238e1e7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1649-245df782-a225-476e-a16b-0097b238e1e7.txn deleted file mode 100644 index fe2704e98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1649-245df782-a225-476e-a16b-0097b238e1e7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/165-c242bfce-6ec8-40fd-8133-2f990286ab15.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/165-c242bfce-6ec8-40fd-8133-2f990286ab15.txn deleted file mode 100644 index 3f1dd3a95..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/165-c242bfce-6ec8-40fd-8133-2f990286ab15.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1650-35600c79-2cf5-4f41-820b-cd8e1e57503a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1650-35600c79-2cf5-4f41-820b-cd8e1e57503a.txn deleted file mode 100644 index e9a1a9e88..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1650-35600c79-2cf5-4f41-820b-cd8e1e57503a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1651-e84dbde6-5f04-430e-812b-48186499262f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1651-e84dbde6-5f04-430e-812b-48186499262f.txn deleted file mode 100644 index faa16aff2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1651-e84dbde6-5f04-430e-812b-48186499262f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1652-e42c8d14-f76c-4e64-8920-a28269dac7ee.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1652-e42c8d14-f76c-4e64-8920-a28269dac7ee.txn deleted file mode 100644 index 7f88808ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1652-e42c8d14-f76c-4e64-8920-a28269dac7ee.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1653-dfe0c70a-6930-46c3-80d8-642c63acbf6f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1653-dfe0c70a-6930-46c3-80d8-642c63acbf6f.txn deleted file mode 100644 index ce0334c9b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1653-dfe0c70a-6930-46c3-80d8-642c63acbf6f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1654-b3da21ca-c583-4c4b-95ae-34767006cc9a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1654-b3da21ca-c583-4c4b-95ae-34767006cc9a.txn deleted file mode 100644 index 85a303f44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1654-b3da21ca-c583-4c4b-95ae-34767006cc9a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1655-62cb93b8-b975-4e3e-bc6a-8d7f511f36c0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1655-62cb93b8-b975-4e3e-bc6a-8d7f511f36c0.txn deleted file mode 100644 index 301f65348..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1655-62cb93b8-b975-4e3e-bc6a-8d7f511f36c0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1656-af31fce8-34b7-4917-83d9-bef7b02715e3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1656-af31fce8-34b7-4917-83d9-bef7b02715e3.txn deleted file mode 100644 index 183df12dd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1656-af31fce8-34b7-4917-83d9-bef7b02715e3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1657-e2efd409-6152-4bd8-84ad-fdb63ae35101.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1657-e2efd409-6152-4bd8-84ad-fdb63ae35101.txn deleted file mode 100644 index 8d3032572..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1657-e2efd409-6152-4bd8-84ad-fdb63ae35101.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1658-e7e826b0-e104-4aea-9bf9-0b79e33803a2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1658-e7e826b0-e104-4aea-9bf9-0b79e33803a2.txn deleted file mode 100644 index 9a24b7808..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1658-e7e826b0-e104-4aea-9bf9-0b79e33803a2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1659-fe332dc3-57cc-4a4b-942b-63969840bde9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1659-fe332dc3-57cc-4a4b-942b-63969840bde9.txn deleted file mode 100644 index 445e6271b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1659-fe332dc3-57cc-4a4b-942b-63969840bde9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/166-06829c1f-b39f-41a2-9887-4cfe9a60b3a7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/166-06829c1f-b39f-41a2-9887-4cfe9a60b3a7.txn deleted file mode 100644 index c8ba5917b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/166-06829c1f-b39f-41a2-9887-4cfe9a60b3a7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1660-990e55d7-4573-4bcd-b9bc-a0009f13da94.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1660-990e55d7-4573-4bcd-b9bc-a0009f13da94.txn deleted file mode 100644 index 79bf7ea82..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1660-990e55d7-4573-4bcd-b9bc-a0009f13da94.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1661-6c0f7dd8-42ac-461d-8880-16752609295e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1661-6c0f7dd8-42ac-461d-8880-16752609295e.txn deleted file mode 100644 index 22558399c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1661-6c0f7dd8-42ac-461d-8880-16752609295e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1662-700dc858-905c-40e1-8c21-7ab20118d3ed.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1662-700dc858-905c-40e1-8c21-7ab20118d3ed.txn deleted file mode 100644 index d438714fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1662-700dc858-905c-40e1-8c21-7ab20118d3ed.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1663-d296652b-ad4f-4a4f-b269-9221590d7507.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1663-d296652b-ad4f-4a4f-b269-9221590d7507.txn deleted file mode 100644 index f513589fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1663-d296652b-ad4f-4a4f-b269-9221590d7507.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1664-c845f353-7b1b-4977-ad38-601b6de25dff.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1664-c845f353-7b1b-4977-ad38-601b6de25dff.txn deleted file mode 100644 index f21d650a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1664-c845f353-7b1b-4977-ad38-601b6de25dff.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1665-7716542c-fc3d-4ff6-acaf-0aa2b50258f0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1665-7716542c-fc3d-4ff6-acaf-0aa2b50258f0.txn deleted file mode 100644 index 0cb7ea036..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1665-7716542c-fc3d-4ff6-acaf-0aa2b50258f0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1666-43444e3e-7344-4bda-a727-d60cffd209f0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1666-43444e3e-7344-4bda-a727-d60cffd209f0.txn deleted file mode 100644 index eb71ccb8a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1666-43444e3e-7344-4bda-a727-d60cffd209f0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1667-f7a70ff5-9cdd-42f8-9899-1ee480af1074.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1667-f7a70ff5-9cdd-42f8-9899-1ee480af1074.txn deleted file mode 100644 index 960551668..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1667-f7a70ff5-9cdd-42f8-9899-1ee480af1074.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1668-b2fa89ea-ed18-4228-9bed-7407ad5cf7c4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1668-b2fa89ea-ed18-4228-9bed-7407ad5cf7c4.txn deleted file mode 100644 index 2c3859d83..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1668-b2fa89ea-ed18-4228-9bed-7407ad5cf7c4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1669-3d800c6e-4ec7-48b5-a507-977808a56790.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1669-3d800c6e-4ec7-48b5-a507-977808a56790.txn deleted file mode 100644 index 4359cc9b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1669-3d800c6e-4ec7-48b5-a507-977808a56790.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/167-9a91c373-9d60-4afb-ac07-f6e3aad6c255.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/167-9a91c373-9d60-4afb-ac07-f6e3aad6c255.txn deleted file mode 100644 index cc5613400..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/167-9a91c373-9d60-4afb-ac07-f6e3aad6c255.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1670-3fe4e29b-5769-445a-8e92-1948851c8de6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1670-3fe4e29b-5769-445a-8e92-1948851c8de6.txn deleted file mode 100644 index a7a6ec177..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1670-3fe4e29b-5769-445a-8e92-1948851c8de6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1671-03a57f32-a60b-4466-abf5-a84ca61dce2b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1671-03a57f32-a60b-4466-abf5-a84ca61dce2b.txn deleted file mode 100644 index a68b83d49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1671-03a57f32-a60b-4466-abf5-a84ca61dce2b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1672-fbabd2d8-d9b5-43d3-934d-cc23ddcc4a2a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1672-fbabd2d8-d9b5-43d3-934d-cc23ddcc4a2a.txn deleted file mode 100644 index 22ebfaab2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1672-fbabd2d8-d9b5-43d3-934d-cc23ddcc4a2a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1673-f9da6c1c-4a7a-4b88-8709-58d1d4396aa1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1673-f9da6c1c-4a7a-4b88-8709-58d1d4396aa1.txn deleted file mode 100644 index e96b57c78..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1673-f9da6c1c-4a7a-4b88-8709-58d1d4396aa1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1674-14c1c681-36db-4cae-b44e-abd2659e6a1d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1674-14c1c681-36db-4cae-b44e-abd2659e6a1d.txn deleted file mode 100644 index d2cce8521..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1674-14c1c681-36db-4cae-b44e-abd2659e6a1d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1675-f976b72a-023b-4a7f-82bc-a5436bbaea05.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1675-f976b72a-023b-4a7f-82bc-a5436bbaea05.txn deleted file mode 100644 index 7ec7554af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1675-f976b72a-023b-4a7f-82bc-a5436bbaea05.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1676-f93140d4-ffe0-4aa4-a493-99abf218024b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1676-f93140d4-ffe0-4aa4-a493-99abf218024b.txn deleted file mode 100644 index 461442488..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1676-f93140d4-ffe0-4aa4-a493-99abf218024b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1677-8822cad8-247d-48fc-adf6-aca43e0e34d6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1677-8822cad8-247d-48fc-adf6-aca43e0e34d6.txn deleted file mode 100644 index 6623558a6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1677-8822cad8-247d-48fc-adf6-aca43e0e34d6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1678-4bd283b0-b57c-4a4f-9ac4-f1f50bbba968.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1678-4bd283b0-b57c-4a4f-9ac4-f1f50bbba968.txn deleted file mode 100644 index 7145b5e41..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1678-4bd283b0-b57c-4a4f-9ac4-f1f50bbba968.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1679-3c95d224-d4c7-453d-b647-2f6250d07791.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1679-3c95d224-d4c7-453d-b647-2f6250d07791.txn deleted file mode 100644 index 0560000e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1679-3c95d224-d4c7-453d-b647-2f6250d07791.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/168-d1e84fd8-bb8e-49b7-8bbb-f885ec8d8bbe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/168-d1e84fd8-bb8e-49b7-8bbb-f885ec8d8bbe.txn deleted file mode 100644 index a06fe13c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/168-d1e84fd8-bb8e-49b7-8bbb-f885ec8d8bbe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1680-50454c31-89e2-42c1-8ce5-cd8e8f82801d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1680-50454c31-89e2-42c1-8ce5-cd8e8f82801d.txn deleted file mode 100644 index d6c732773..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1680-50454c31-89e2-42c1-8ce5-cd8e8f82801d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1681-e52617d8-9b86-4583-9c15-8314a0656001.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1681-e52617d8-9b86-4583-9c15-8314a0656001.txn deleted file mode 100644 index f5d001f87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1681-e52617d8-9b86-4583-9c15-8314a0656001.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1682-4cadec6c-7e0e-46a9-83cd-7b2cff7a2e1e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1682-4cadec6c-7e0e-46a9-83cd-7b2cff7a2e1e.txn deleted file mode 100644 index 2e7e4045d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1682-4cadec6c-7e0e-46a9-83cd-7b2cff7a2e1e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1683-170cb24e-b251-4fb3-9478-7de37b500779.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1683-170cb24e-b251-4fb3-9478-7de37b500779.txn deleted file mode 100644 index 9f5c121ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1683-170cb24e-b251-4fb3-9478-7de37b500779.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1684-4253da39-f260-44f7-8e4b-65f6fdd067a0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1684-4253da39-f260-44f7-8e4b-65f6fdd067a0.txn deleted file mode 100644 index 5e823d7de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1684-4253da39-f260-44f7-8e4b-65f6fdd067a0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1685-d6352aaf-db46-45fc-b4ce-a9ad880648d5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1685-d6352aaf-db46-45fc-b4ce-a9ad880648d5.txn deleted file mode 100644 index ac58ecdb1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1685-d6352aaf-db46-45fc-b4ce-a9ad880648d5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1686-439d407c-7696-4cd0-a0a3-ad8934a5494f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1686-439d407c-7696-4cd0-a0a3-ad8934a5494f.txn deleted file mode 100644 index d42dd4188..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1686-439d407c-7696-4cd0-a0a3-ad8934a5494f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1687-7ed9bc81-3dec-4067-929b-9d3acb23f6dc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1687-7ed9bc81-3dec-4067-929b-9d3acb23f6dc.txn deleted file mode 100644 index 3a4a9d0b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1687-7ed9bc81-3dec-4067-929b-9d3acb23f6dc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1688-226bcc68-bc8c-4e33-8c70-2673b19a9196.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1688-226bcc68-bc8c-4e33-8c70-2673b19a9196.txn deleted file mode 100644 index 1ee5f9727..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1688-226bcc68-bc8c-4e33-8c70-2673b19a9196.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1689-e8685283-e863-49fa-a2b0-36a78f8cd605.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1689-e8685283-e863-49fa-a2b0-36a78f8cd605.txn deleted file mode 100644 index 1314390c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1689-e8685283-e863-49fa-a2b0-36a78f8cd605.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/169-02fa6fb2-69b9-47af-bfcb-fc78848b9fdc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/169-02fa6fb2-69b9-47af-bfcb-fc78848b9fdc.txn deleted file mode 100644 index 30212f81f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/169-02fa6fb2-69b9-47af-bfcb-fc78848b9fdc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1690-4a875299-75d3-4232-98e2-0a02ed981a7f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1690-4a875299-75d3-4232-98e2-0a02ed981a7f.txn deleted file mode 100644 index a90ea78e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1690-4a875299-75d3-4232-98e2-0a02ed981a7f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1691-fdf97055-c46d-4ac2-8f87-f92260aac94e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1691-fdf97055-c46d-4ac2-8f87-f92260aac94e.txn deleted file mode 100644 index a57d616e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1691-fdf97055-c46d-4ac2-8f87-f92260aac94e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1692-b4079c28-b07c-4b1f-830a-2d24a992f1e5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1692-b4079c28-b07c-4b1f-830a-2d24a992f1e5.txn deleted file mode 100644 index a38c77b36..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1692-b4079c28-b07c-4b1f-830a-2d24a992f1e5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1693-2075a7b5-7cb9-4af2-948a-ae5eb4ea9ef8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1693-2075a7b5-7cb9-4af2-948a-ae5eb4ea9ef8.txn deleted file mode 100644 index f29e6b993..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1693-2075a7b5-7cb9-4af2-948a-ae5eb4ea9ef8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1694-474816fe-685e-424b-b10e-972022a1794f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1694-474816fe-685e-424b-b10e-972022a1794f.txn deleted file mode 100644 index fc787c6c2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1694-474816fe-685e-424b-b10e-972022a1794f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1695-a4bd57e5-107c-483b-9ea0-cb154331857c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1695-a4bd57e5-107c-483b-9ea0-cb154331857c.txn deleted file mode 100644 index 8342fd23c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1695-a4bd57e5-107c-483b-9ea0-cb154331857c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1696-69b50213-c41d-4e8e-a882-2b5d6b75b72a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1696-69b50213-c41d-4e8e-a882-2b5d6b75b72a.txn deleted file mode 100644 index cbbace43e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1696-69b50213-c41d-4e8e-a882-2b5d6b75b72a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1697-33ab8db9-8fc7-4a8f-9a6c-89a279b7fc41.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1697-33ab8db9-8fc7-4a8f-9a6c-89a279b7fc41.txn deleted file mode 100644 index 1d6a90c13..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1697-33ab8db9-8fc7-4a8f-9a6c-89a279b7fc41.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1698-bd008072-6d47-45bb-a814-9577cd53f950.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1698-bd008072-6d47-45bb-a814-9577cd53f950.txn deleted file mode 100644 index ed7bfde35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1698-bd008072-6d47-45bb-a814-9577cd53f950.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1699-25722161-f20b-41e3-b1a6-74ba6d15b8d2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1699-25722161-f20b-41e3-b1a6-74ba6d15b8d2.txn deleted file mode 100644 index 3693a363d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1699-25722161-f20b-41e3-b1a6-74ba6d15b8d2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/17-1bcd9412-537f-4997-989a-39ccd5b21832.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/17-1bcd9412-537f-4997-989a-39ccd5b21832.txn deleted file mode 100644 index 508e82c63..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/17-1bcd9412-537f-4997-989a-39ccd5b21832.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/170-5b9461a8-1985-4e86-b626-1ce2b014c13b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/170-5b9461a8-1985-4e86-b626-1ce2b014c13b.txn deleted file mode 100644 index 41c2a6c69..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/170-5b9461a8-1985-4e86-b626-1ce2b014c13b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1700-ac184528-9865-4c5f-84af-4376eaf88642.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1700-ac184528-9865-4c5f-84af-4376eaf88642.txn deleted file mode 100644 index 80fb3560d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1700-ac184528-9865-4c5f-84af-4376eaf88642.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1701-f9af9b53-62ba-470e-9012-7a954b39a29e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1701-f9af9b53-62ba-470e-9012-7a954b39a29e.txn deleted file mode 100644 index 368b59b06..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1701-f9af9b53-62ba-470e-9012-7a954b39a29e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1702-f61df274-8c1a-4ff4-b645-278f5128fd46.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1702-f61df274-8c1a-4ff4-b645-278f5128fd46.txn deleted file mode 100644 index 93ca275b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1702-f61df274-8c1a-4ff4-b645-278f5128fd46.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1703-de28e8b9-cd72-4b2a-a27c-04c5e00dc040.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1703-de28e8b9-cd72-4b2a-a27c-04c5e00dc040.txn deleted file mode 100644 index 685273984..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1703-de28e8b9-cd72-4b2a-a27c-04c5e00dc040.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1704-1a7c2b0c-6388-41c6-84df-99767d76fa85.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1704-1a7c2b0c-6388-41c6-84df-99767d76fa85.txn deleted file mode 100644 index f2ea36518..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1704-1a7c2b0c-6388-41c6-84df-99767d76fa85.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1705-98cc3e19-8872-4c30-a82c-ced154ff4da0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1705-98cc3e19-8872-4c30-a82c-ced154ff4da0.txn deleted file mode 100644 index 1ff6a6398..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1705-98cc3e19-8872-4c30-a82c-ced154ff4da0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1706-5627e674-0d9a-4fcd-aac5-f780f2006045.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1706-5627e674-0d9a-4fcd-aac5-f780f2006045.txn deleted file mode 100644 index 96afb2b1c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1706-5627e674-0d9a-4fcd-aac5-f780f2006045.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1707-cd19c79c-9cfc-4a69-bc8e-0d558a56328c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1707-cd19c79c-9cfc-4a69-bc8e-0d558a56328c.txn deleted file mode 100644 index 8063023e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1707-cd19c79c-9cfc-4a69-bc8e-0d558a56328c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1708-f9d16522-33de-498f-8556-f6c71151d107.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1708-f9d16522-33de-498f-8556-f6c71151d107.txn deleted file mode 100644 index af7d5c08a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1708-f9d16522-33de-498f-8556-f6c71151d107.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1709-71487e54-4bbd-4d5e-8e39-06d9dbad70e3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1709-71487e54-4bbd-4d5e-8e39-06d9dbad70e3.txn deleted file mode 100644 index 413c3638e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1709-71487e54-4bbd-4d5e-8e39-06d9dbad70e3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/171-a4037420-fd43-4642-89f3-44416c17b60c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/171-a4037420-fd43-4642-89f3-44416c17b60c.txn deleted file mode 100644 index f5d00aed1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/171-a4037420-fd43-4642-89f3-44416c17b60c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1710-cba297fa-fc6e-4869-aa90-b4ca1f5f37a4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1710-cba297fa-fc6e-4869-aa90-b4ca1f5f37a4.txn deleted file mode 100644 index 87ca839ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1710-cba297fa-fc6e-4869-aa90-b4ca1f5f37a4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1711-46b7d30c-39b8-437d-a4be-bf929ffb84bc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1711-46b7d30c-39b8-437d-a4be-bf929ffb84bc.txn deleted file mode 100644 index 7d205791d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1711-46b7d30c-39b8-437d-a4be-bf929ffb84bc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1712-f0366ba6-7630-4d34-babc-503798a85a63.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1712-f0366ba6-7630-4d34-babc-503798a85a63.txn deleted file mode 100644 index 43d51e8b7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1712-f0366ba6-7630-4d34-babc-503798a85a63.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1713-ee4bdb16-f4ed-4365-9839-56a7be1898a2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1713-ee4bdb16-f4ed-4365-9839-56a7be1898a2.txn deleted file mode 100644 index bfd195687..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1713-ee4bdb16-f4ed-4365-9839-56a7be1898a2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1714-fbc837c2-7d0f-41d5-8c50-bc9caf3504bc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1714-fbc837c2-7d0f-41d5-8c50-bc9caf3504bc.txn deleted file mode 100644 index 94be2843b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1714-fbc837c2-7d0f-41d5-8c50-bc9caf3504bc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1715-51959f9a-9c4e-4d8d-ac73-ff99e8c3ba7f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1715-51959f9a-9c4e-4d8d-ac73-ff99e8c3ba7f.txn deleted file mode 100644 index b03efec6b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1715-51959f9a-9c4e-4d8d-ac73-ff99e8c3ba7f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1716-61d245a8-49c0-4c56-9602-300442029d1d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1716-61d245a8-49c0-4c56-9602-300442029d1d.txn deleted file mode 100644 index 25a785a5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1716-61d245a8-49c0-4c56-9602-300442029d1d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1717-fafec397-ec48-495e-a709-7a84e23fe18c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1717-fafec397-ec48-495e-a709-7a84e23fe18c.txn deleted file mode 100644 index fd4873f6c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1717-fafec397-ec48-495e-a709-7a84e23fe18c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1718-8b333d43-3122-4cfd-9480-9c6c52435f2b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1718-8b333d43-3122-4cfd-9480-9c6c52435f2b.txn deleted file mode 100644 index 0fbfd9386..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1718-8b333d43-3122-4cfd-9480-9c6c52435f2b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1719-ccd9e5a0-d2a5-42e9-ba52-600bdf400e74.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1719-ccd9e5a0-d2a5-42e9-ba52-600bdf400e74.txn deleted file mode 100644 index 2f5f818c2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1719-ccd9e5a0-d2a5-42e9-ba52-600bdf400e74.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/172-64138bec-fef9-4b84-9f2f-dba0c7f54d27.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/172-64138bec-fef9-4b84-9f2f-dba0c7f54d27.txn deleted file mode 100644 index a00268b72..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/172-64138bec-fef9-4b84-9f2f-dba0c7f54d27.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1720-0a7ce60c-a253-420f-9922-3e43bf38b5b0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1720-0a7ce60c-a253-420f-9922-3e43bf38b5b0.txn deleted file mode 100644 index ca0ebdf0f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1720-0a7ce60c-a253-420f-9922-3e43bf38b5b0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1721-87541a64-ac51-47e6-8772-662a205ee79e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1721-87541a64-ac51-47e6-8772-662a205ee79e.txn deleted file mode 100644 index 446142869..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1721-87541a64-ac51-47e6-8772-662a205ee79e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1722-ef773cf6-7340-497e-8cb5-00f21ccc0020.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1722-ef773cf6-7340-497e-8cb5-00f21ccc0020.txn deleted file mode 100644 index 28f29501e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1722-ef773cf6-7340-497e-8cb5-00f21ccc0020.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1723-d5bc84bf-4f05-41c0-b131-9b2b27b61acf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1723-d5bc84bf-4f05-41c0-b131-9b2b27b61acf.txn deleted file mode 100644 index 0da996d12..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1723-d5bc84bf-4f05-41c0-b131-9b2b27b61acf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1724-cb8578d7-2a24-4ffe-8713-2e3d555972c6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1724-cb8578d7-2a24-4ffe-8713-2e3d555972c6.txn deleted file mode 100644 index e0d4d76f4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1724-cb8578d7-2a24-4ffe-8713-2e3d555972c6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1725-88351a76-5c2c-483a-9784-186fbe84ef48.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1725-88351a76-5c2c-483a-9784-186fbe84ef48.txn deleted file mode 100644 index b1f30bef8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1725-88351a76-5c2c-483a-9784-186fbe84ef48.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1726-9cc8809e-d8ac-4bd0-b5eb-19462bc20bbf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1726-9cc8809e-d8ac-4bd0-b5eb-19462bc20bbf.txn deleted file mode 100644 index 814face46..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1726-9cc8809e-d8ac-4bd0-b5eb-19462bc20bbf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1727-134d92f8-347e-4e1d-83dd-e0b322a5059b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1727-134d92f8-347e-4e1d-83dd-e0b322a5059b.txn deleted file mode 100644 index 48ab51296..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1727-134d92f8-347e-4e1d-83dd-e0b322a5059b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1728-ed45fef6-c57a-4281-8450-bfd92b6cfeff.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1728-ed45fef6-c57a-4281-8450-bfd92b6cfeff.txn deleted file mode 100644 index b2a7f42ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1728-ed45fef6-c57a-4281-8450-bfd92b6cfeff.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1729-dd7c3a45-0787-49bc-b8cd-f8e23555e2e6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1729-dd7c3a45-0787-49bc-b8cd-f8e23555e2e6.txn deleted file mode 100644 index 436352560..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1729-dd7c3a45-0787-49bc-b8cd-f8e23555e2e6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/173-959ba9c3-c9a3-4945-a89b-e6dfefe1327a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/173-959ba9c3-c9a3-4945-a89b-e6dfefe1327a.txn deleted file mode 100644 index 96670ce3c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/173-959ba9c3-c9a3-4945-a89b-e6dfefe1327a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1730-e57636a2-7077-4d20-b83a-7642ac4dd8a4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1730-e57636a2-7077-4d20-b83a-7642ac4dd8a4.txn deleted file mode 100644 index 1b03be917..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1730-e57636a2-7077-4d20-b83a-7642ac4dd8a4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1731-920c07cf-e3f9-4a91-ad52-bcae10abfae7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1731-920c07cf-e3f9-4a91-ad52-bcae10abfae7.txn deleted file mode 100644 index 2b3857b1a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1731-920c07cf-e3f9-4a91-ad52-bcae10abfae7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1732-142e5c61-82b9-4789-ba4d-08b1556e9bc2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1732-142e5c61-82b9-4789-ba4d-08b1556e9bc2.txn deleted file mode 100644 index 15e313b69..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1732-142e5c61-82b9-4789-ba4d-08b1556e9bc2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1733-98805dfb-356b-43ed-95ee-0cfc38d32294.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1733-98805dfb-356b-43ed-95ee-0cfc38d32294.txn deleted file mode 100644 index edb559b2b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1733-98805dfb-356b-43ed-95ee-0cfc38d32294.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1734-165023a1-14ef-4792-8360-756b7c516dff.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1734-165023a1-14ef-4792-8360-756b7c516dff.txn deleted file mode 100644 index 34aafd4f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1734-165023a1-14ef-4792-8360-756b7c516dff.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1735-fcfbdd34-e23f-4bc9-ba85-e80a894ad198.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1735-fcfbdd34-e23f-4bc9-ba85-e80a894ad198.txn deleted file mode 100644 index 860da66fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1735-fcfbdd34-e23f-4bc9-ba85-e80a894ad198.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1736-80eade0d-5caa-46d2-adf5-fc7f04d4e317.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1736-80eade0d-5caa-46d2-adf5-fc7f04d4e317.txn deleted file mode 100644 index b096ac3de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1736-80eade0d-5caa-46d2-adf5-fc7f04d4e317.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1737-26aff810-76fe-46d6-9709-69c595e74b2c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1737-26aff810-76fe-46d6-9709-69c595e74b2c.txn deleted file mode 100644 index b0ed95931..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1737-26aff810-76fe-46d6-9709-69c595e74b2c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1738-a00f5e74-dcef-440b-9ab9-57b393960ae5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1738-a00f5e74-dcef-440b-9ab9-57b393960ae5.txn deleted file mode 100644 index 83549d877..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1738-a00f5e74-dcef-440b-9ab9-57b393960ae5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1739-e3f30bc9-348c-4f85-8f47-197cc959706a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1739-e3f30bc9-348c-4f85-8f47-197cc959706a.txn deleted file mode 100644 index 6e25670ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1739-e3f30bc9-348c-4f85-8f47-197cc959706a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/174-e13ef14a-d2c2-49ed-8106-c29dc9be083f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/174-e13ef14a-d2c2-49ed-8106-c29dc9be083f.txn deleted file mode 100644 index b5fff6cb6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/174-e13ef14a-d2c2-49ed-8106-c29dc9be083f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1740-9d3bfff4-365a-4697-8ef8-d7272784f38d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1740-9d3bfff4-365a-4697-8ef8-d7272784f38d.txn deleted file mode 100644 index 8c31ecf94..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1740-9d3bfff4-365a-4697-8ef8-d7272784f38d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1741-a135079b-068a-4aa9-b7a4-f860a766fbd2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1741-a135079b-068a-4aa9-b7a4-f860a766fbd2.txn deleted file mode 100644 index c4ab9d617..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1741-a135079b-068a-4aa9-b7a4-f860a766fbd2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1742-55e10126-4b0c-4492-b22f-9828ceba5011.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1742-55e10126-4b0c-4492-b22f-9828ceba5011.txn deleted file mode 100644 index 71895b941..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1742-55e10126-4b0c-4492-b22f-9828ceba5011.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1743-1103cf9d-6f96-455a-81fd-5d6b20483d3e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1743-1103cf9d-6f96-455a-81fd-5d6b20483d3e.txn deleted file mode 100644 index e8557c1ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1743-1103cf9d-6f96-455a-81fd-5d6b20483d3e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1744-eb85000a-bda3-4ed2-8eb5-bacddf8bf941.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1744-eb85000a-bda3-4ed2-8eb5-bacddf8bf941.txn deleted file mode 100644 index 84e2c004a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1744-eb85000a-bda3-4ed2-8eb5-bacddf8bf941.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1745-44fd4d67-20d0-4f7d-b659-db6fe65d7313.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1745-44fd4d67-20d0-4f7d-b659-db6fe65d7313.txn deleted file mode 100644 index d9b2b8ec0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1745-44fd4d67-20d0-4f7d-b659-db6fe65d7313.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1746-e9923934-8954-4358-b17c-1b01486c7d7a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1746-e9923934-8954-4358-b17c-1b01486c7d7a.txn deleted file mode 100644 index afc53179b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1746-e9923934-8954-4358-b17c-1b01486c7d7a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1747-817d67df-0495-400f-84d4-9885815126a5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1747-817d67df-0495-400f-84d4-9885815126a5.txn deleted file mode 100644 index 338a44d00..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1747-817d67df-0495-400f-84d4-9885815126a5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1748-e0568f78-b619-4148-a961-0e52e21d1c73.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1748-e0568f78-b619-4148-a961-0e52e21d1c73.txn deleted file mode 100644 index a13d8353f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1748-e0568f78-b619-4148-a961-0e52e21d1c73.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1749-a464d75c-3024-4ca7-8a6e-830c3e75688a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1749-a464d75c-3024-4ca7-8a6e-830c3e75688a.txn deleted file mode 100644 index 50fffd97d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1749-a464d75c-3024-4ca7-8a6e-830c3e75688a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/175-862105d7-a2c5-4661-95fe-e6abb95c1a07.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/175-862105d7-a2c5-4661-95fe-e6abb95c1a07.txn deleted file mode 100644 index c1ef6b310..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/175-862105d7-a2c5-4661-95fe-e6abb95c1a07.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1750-4c62757e-45e7-481b-8bfb-ab7065c46559.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1750-4c62757e-45e7-481b-8bfb-ab7065c46559.txn deleted file mode 100644 index 5c9d7ebea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1750-4c62757e-45e7-481b-8bfb-ab7065c46559.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1751-84833297-f073-4c80-b6ac-a65a3292433c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1751-84833297-f073-4c80-b6ac-a65a3292433c.txn deleted file mode 100644 index 3ee4b027a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1751-84833297-f073-4c80-b6ac-a65a3292433c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1752-cb3d39b9-6c86-4115-8698-c144154bfa77.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1752-cb3d39b9-6c86-4115-8698-c144154bfa77.txn deleted file mode 100644 index d1d504d49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1752-cb3d39b9-6c86-4115-8698-c144154bfa77.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1753-4de861f0-fb0d-4265-b553-4fd9098d7f81.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1753-4de861f0-fb0d-4265-b553-4fd9098d7f81.txn deleted file mode 100644 index 15889aa25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1753-4de861f0-fb0d-4265-b553-4fd9098d7f81.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1754-6f27dcaf-ef95-46a0-be71-cd559980ff05.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1754-6f27dcaf-ef95-46a0-be71-cd559980ff05.txn deleted file mode 100644 index a19225bb4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1754-6f27dcaf-ef95-46a0-be71-cd559980ff05.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1755-d587792c-175a-40fa-a4d7-e0ff2375dd69.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1755-d587792c-175a-40fa-a4d7-e0ff2375dd69.txn deleted file mode 100644 index ebc835649..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1755-d587792c-175a-40fa-a4d7-e0ff2375dd69.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1756-a334de2d-caa6-460a-86fb-0f4a5a03edd7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1756-a334de2d-caa6-460a-86fb-0f4a5a03edd7.txn deleted file mode 100644 index 46fd100e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1756-a334de2d-caa6-460a-86fb-0f4a5a03edd7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1757-ea68cfae-4f31-4149-b801-a010db9763f3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1757-ea68cfae-4f31-4149-b801-a010db9763f3.txn deleted file mode 100644 index 5be50a14b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1757-ea68cfae-4f31-4149-b801-a010db9763f3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1758-244e556e-ba61-49f6-b87a-39202bacd28c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1758-244e556e-ba61-49f6-b87a-39202bacd28c.txn deleted file mode 100644 index 1a7592e2e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1758-244e556e-ba61-49f6-b87a-39202bacd28c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1759-2be19be7-a601-4d26-8e15-734114bb28fd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1759-2be19be7-a601-4d26-8e15-734114bb28fd.txn deleted file mode 100644 index 5385e113a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1759-2be19be7-a601-4d26-8e15-734114bb28fd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/176-8664ded5-da64-4e65-9bc1-9a624fa8d5d7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/176-8664ded5-da64-4e65-9bc1-9a624fa8d5d7.txn deleted file mode 100644 index d2fe53b64..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/176-8664ded5-da64-4e65-9bc1-9a624fa8d5d7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1760-69adc34d-042f-4d2c-a6f8-41eea5f8b763.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1760-69adc34d-042f-4d2c-a6f8-41eea5f8b763.txn deleted file mode 100644 index 61324c611..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1760-69adc34d-042f-4d2c-a6f8-41eea5f8b763.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1761-06638de3-4cdb-4145-be54-bb4a4605c382.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1761-06638de3-4cdb-4145-be54-bb4a4605c382.txn deleted file mode 100644 index 65ca4d12f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1761-06638de3-4cdb-4145-be54-bb4a4605c382.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1762-275b639e-5460-4f14-abf0-306fac57b513.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1762-275b639e-5460-4f14-abf0-306fac57b513.txn deleted file mode 100644 index f7794b703..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1762-275b639e-5460-4f14-abf0-306fac57b513.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1763-5173eb13-662d-44b3-bdba-74b564cdd3ce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1763-5173eb13-662d-44b3-bdba-74b564cdd3ce.txn deleted file mode 100644 index 8a7b6d211..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1763-5173eb13-662d-44b3-bdba-74b564cdd3ce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1764-b26ebcef-fcf3-4c2a-813b-da565b3e8dc7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1764-b26ebcef-fcf3-4c2a-813b-da565b3e8dc7.txn deleted file mode 100644 index b9393ae2b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1764-b26ebcef-fcf3-4c2a-813b-da565b3e8dc7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1765-e6f35478-fa1b-415a-835a-e226334fe71b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1765-e6f35478-fa1b-415a-835a-e226334fe71b.txn deleted file mode 100644 index 7356ad1bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1765-e6f35478-fa1b-415a-835a-e226334fe71b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1766-7d31c1bc-56df-4ea3-9fa6-150a646a2a8c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1766-7d31c1bc-56df-4ea3-9fa6-150a646a2a8c.txn deleted file mode 100644 index dd1bd5b49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1766-7d31c1bc-56df-4ea3-9fa6-150a646a2a8c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1767-523b59a3-bf8a-460a-9851-cd935f18d44a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1767-523b59a3-bf8a-460a-9851-cd935f18d44a.txn deleted file mode 100644 index 1fdcffade..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1767-523b59a3-bf8a-460a-9851-cd935f18d44a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1768-c0d45db7-f9e2-4f44-bcba-3348fcc5505e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1768-c0d45db7-f9e2-4f44-bcba-3348fcc5505e.txn deleted file mode 100644 index 11ccdbeed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1768-c0d45db7-f9e2-4f44-bcba-3348fcc5505e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1769-f503cd04-4475-4863-8bf2-0f460a6c7a04.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1769-f503cd04-4475-4863-8bf2-0f460a6c7a04.txn deleted file mode 100644 index d87c83b4d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1769-f503cd04-4475-4863-8bf2-0f460a6c7a04.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/177-2bad26c9-68e6-4dcb-b05f-78d5b1c07393.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/177-2bad26c9-68e6-4dcb-b05f-78d5b1c07393.txn deleted file mode 100644 index 61125e70d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/177-2bad26c9-68e6-4dcb-b05f-78d5b1c07393.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1770-341fac2d-41df-45ed-afc5-d784093a0fd4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1770-341fac2d-41df-45ed-afc5-d784093a0fd4.txn deleted file mode 100644 index 0cc5cafd2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1770-341fac2d-41df-45ed-afc5-d784093a0fd4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1771-84926400-6617-46aa-b94a-1f592e26e5b6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1771-84926400-6617-46aa-b94a-1f592e26e5b6.txn deleted file mode 100644 index 940a3a1ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1771-84926400-6617-46aa-b94a-1f592e26e5b6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1772-c50df425-2e41-4c68-b728-80d2536083ff.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1772-c50df425-2e41-4c68-b728-80d2536083ff.txn deleted file mode 100644 index f18bb2972..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1772-c50df425-2e41-4c68-b728-80d2536083ff.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1773-1e5f22f3-196b-4b15-99dc-9a569aee0f6e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1773-1e5f22f3-196b-4b15-99dc-9a569aee0f6e.txn deleted file mode 100644 index 030056306..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1773-1e5f22f3-196b-4b15-99dc-9a569aee0f6e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1774-a4e181a9-4081-478c-8f45-c240ee13c298.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1774-a4e181a9-4081-478c-8f45-c240ee13c298.txn deleted file mode 100644 index e102dff7d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1774-a4e181a9-4081-478c-8f45-c240ee13c298.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1775-81016741-3921-4ede-ab89-524152cafe71.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1775-81016741-3921-4ede-ab89-524152cafe71.txn deleted file mode 100644 index a87159c3f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1775-81016741-3921-4ede-ab89-524152cafe71.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1776-ed541496-842b-438f-9c97-431b63576cb5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1776-ed541496-842b-438f-9c97-431b63576cb5.txn deleted file mode 100644 index 537606c32..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1776-ed541496-842b-438f-9c97-431b63576cb5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1777-8cbd9859-577b-48c5-9448-b5754b97d779.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1777-8cbd9859-577b-48c5-9448-b5754b97d779.txn deleted file mode 100644 index 908fe19e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1777-8cbd9859-577b-48c5-9448-b5754b97d779.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1778-3f1c9271-1d90-485d-943b-2a47aab1eb61.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1778-3f1c9271-1d90-485d-943b-2a47aab1eb61.txn deleted file mode 100644 index 1a2134909..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1778-3f1c9271-1d90-485d-943b-2a47aab1eb61.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1779-bd03030b-6e8c-4a40-ba24-9d44853d5a97.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1779-bd03030b-6e8c-4a40-ba24-9d44853d5a97.txn deleted file mode 100644 index 37c4c52c3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1779-bd03030b-6e8c-4a40-ba24-9d44853d5a97.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/178-64f05aa1-1eba-48ea-94ac-38ba26446324.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/178-64f05aa1-1eba-48ea-94ac-38ba26446324.txn deleted file mode 100644 index 25697a8ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/178-64f05aa1-1eba-48ea-94ac-38ba26446324.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1780-13eb3c4c-dc88-4901-8bfb-b302043f8dd6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1780-13eb3c4c-dc88-4901-8bfb-b302043f8dd6.txn deleted file mode 100644 index f336c0a96..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1780-13eb3c4c-dc88-4901-8bfb-b302043f8dd6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1781-d7065862-ddbf-4d55-b41c-160de8742c0f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1781-d7065862-ddbf-4d55-b41c-160de8742c0f.txn deleted file mode 100644 index adabc29a0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1781-d7065862-ddbf-4d55-b41c-160de8742c0f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1782-64b4acea-2c34-4813-929a-d226141c9b29.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1782-64b4acea-2c34-4813-929a-d226141c9b29.txn deleted file mode 100644 index 29e55c344..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1782-64b4acea-2c34-4813-929a-d226141c9b29.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1783-90dbc74f-e498-448b-af0b-6c742b0a4569.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1783-90dbc74f-e498-448b-af0b-6c742b0a4569.txn deleted file mode 100644 index 77d023db4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1783-90dbc74f-e498-448b-af0b-6c742b0a4569.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1784-d21b92ca-8eb9-4b4e-9f9e-67331cf3c6a8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1784-d21b92ca-8eb9-4b4e-9f9e-67331cf3c6a8.txn deleted file mode 100644 index d0cd85495..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1784-d21b92ca-8eb9-4b4e-9f9e-67331cf3c6a8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1785-4b4086b2-8271-410b-8498-2c845292a570.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1785-4b4086b2-8271-410b-8498-2c845292a570.txn deleted file mode 100644 index 4af2e1a4e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1785-4b4086b2-8271-410b-8498-2c845292a570.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1786-b000b5ee-0d3a-4542-957a-343fc29cbb31.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1786-b000b5ee-0d3a-4542-957a-343fc29cbb31.txn deleted file mode 100644 index 1a753bc14..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1786-b000b5ee-0d3a-4542-957a-343fc29cbb31.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1787-4adb721e-0fe0-46c9-9f6b-10c7396679d0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1787-4adb721e-0fe0-46c9-9f6b-10c7396679d0.txn deleted file mode 100644 index 108fd86e0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1787-4adb721e-0fe0-46c9-9f6b-10c7396679d0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1788-938da1aa-35f6-4c79-ba1a-e52419111369.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1788-938da1aa-35f6-4c79-ba1a-e52419111369.txn deleted file mode 100644 index 2d202f4fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1788-938da1aa-35f6-4c79-ba1a-e52419111369.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1789-b6d655e4-0e5d-4db2-916f-94663e394a0a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1789-b6d655e4-0e5d-4db2-916f-94663e394a0a.txn deleted file mode 100644 index 1c3beac20..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1789-b6d655e4-0e5d-4db2-916f-94663e394a0a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/179-ef5881e7-eafc-48ab-b41a-38ccab6cd5e7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/179-ef5881e7-eafc-48ab-b41a-38ccab6cd5e7.txn deleted file mode 100644 index 60437984f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/179-ef5881e7-eafc-48ab-b41a-38ccab6cd5e7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1790-f515841f-6a0e-4c13-ac68-98a5ac4e7bc7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1790-f515841f-6a0e-4c13-ac68-98a5ac4e7bc7.txn deleted file mode 100644 index 19c0f0cb9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1790-f515841f-6a0e-4c13-ac68-98a5ac4e7bc7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1791-14441cfa-cbab-430b-8613-c4cf8d042b24.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1791-14441cfa-cbab-430b-8613-c4cf8d042b24.txn deleted file mode 100644 index 1290346a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1791-14441cfa-cbab-430b-8613-c4cf8d042b24.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1792-a651a82f-1c34-41fd-a95a-541b250f3eee.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1792-a651a82f-1c34-41fd-a95a-541b250f3eee.txn deleted file mode 100644 index 05f0e0716..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1792-a651a82f-1c34-41fd-a95a-541b250f3eee.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1793-03cb4cec-4546-4d3f-8aba-660a7bf82608.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1793-03cb4cec-4546-4d3f-8aba-660a7bf82608.txn deleted file mode 100644 index 044149598..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1793-03cb4cec-4546-4d3f-8aba-660a7bf82608.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1794-2b8daaaa-0537-4f06-81dc-1d54b903033c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1794-2b8daaaa-0537-4f06-81dc-1d54b903033c.txn deleted file mode 100644 index 4bbb214fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1794-2b8daaaa-0537-4f06-81dc-1d54b903033c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1795-37757263-2b4f-4bf3-a6bb-759e86b84c14.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1795-37757263-2b4f-4bf3-a6bb-759e86b84c14.txn deleted file mode 100644 index 43c15cbf7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1795-37757263-2b4f-4bf3-a6bb-759e86b84c14.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1796-a2516110-ea26-4e05-ac99-51e99ee49068.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1796-a2516110-ea26-4e05-ac99-51e99ee49068.txn deleted file mode 100644 index 51561e3c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1796-a2516110-ea26-4e05-ac99-51e99ee49068.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1797-e28a2c72-e3c6-48a1-8b3e-1f8dc408354e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1797-e28a2c72-e3c6-48a1-8b3e-1f8dc408354e.txn deleted file mode 100644 index c14754116..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1797-e28a2c72-e3c6-48a1-8b3e-1f8dc408354e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1798-abbda44c-2b31-4c10-9c26-e3ef08b12246.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1798-abbda44c-2b31-4c10-9c26-e3ef08b12246.txn deleted file mode 100644 index d4d9e7719..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1798-abbda44c-2b31-4c10-9c26-e3ef08b12246.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1799-d4a33cd5-515c-4ca8-9b0f-88c5e12b4ac6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1799-d4a33cd5-515c-4ca8-9b0f-88c5e12b4ac6.txn deleted file mode 100644 index 9d463656a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1799-d4a33cd5-515c-4ca8-9b0f-88c5e12b4ac6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/18-90361836-8c28-451d-99df-42f3a36a23f3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/18-90361836-8c28-451d-99df-42f3a36a23f3.txn deleted file mode 100644 index b86f80481..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/18-90361836-8c28-451d-99df-42f3a36a23f3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/180-408fca5d-08ec-4290-ad13-3dd0cec8db96.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/180-408fca5d-08ec-4290-ad13-3dd0cec8db96.txn deleted file mode 100644 index 8f90b0273..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/180-408fca5d-08ec-4290-ad13-3dd0cec8db96.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1800-d8e1d365-6b40-4320-8f95-7c5966f6c5fe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1800-d8e1d365-6b40-4320-8f95-7c5966f6c5fe.txn deleted file mode 100644 index 66ae2db90..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1800-d8e1d365-6b40-4320-8f95-7c5966f6c5fe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1801-e96c789d-0465-4d85-b61a-3824ee8d3c4f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1801-e96c789d-0465-4d85-b61a-3824ee8d3c4f.txn deleted file mode 100644 index cf74b5116..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1801-e96c789d-0465-4d85-b61a-3824ee8d3c4f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1802-5af8be8f-79e4-4589-b2cb-176180db5824.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1802-5af8be8f-79e4-4589-b2cb-176180db5824.txn deleted file mode 100644 index 549b0cb35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1802-5af8be8f-79e4-4589-b2cb-176180db5824.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1803-4583312d-ce40-4fb7-b57f-80b52d829b87.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1803-4583312d-ce40-4fb7-b57f-80b52d829b87.txn deleted file mode 100644 index 6a41c9195..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1803-4583312d-ce40-4fb7-b57f-80b52d829b87.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1804-df77d127-07b8-4b4c-9a57-6300b7913c05.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1804-df77d127-07b8-4b4c-9a57-6300b7913c05.txn deleted file mode 100644 index a4f5e98fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1804-df77d127-07b8-4b4c-9a57-6300b7913c05.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1805-71ec77ea-2389-45bf-aad8-7c8a20daf916.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1805-71ec77ea-2389-45bf-aad8-7c8a20daf916.txn deleted file mode 100644 index edb9dbbf8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1805-71ec77ea-2389-45bf-aad8-7c8a20daf916.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1806-93aa808f-71ca-471d-be72-96007e2ea7d3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1806-93aa808f-71ca-471d-be72-96007e2ea7d3.txn deleted file mode 100644 index c5b8600bf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1806-93aa808f-71ca-471d-be72-96007e2ea7d3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1807-8c68097d-ea0b-4ff4-a1cc-b8ddba39a05c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1807-8c68097d-ea0b-4ff4-a1cc-b8ddba39a05c.txn deleted file mode 100644 index b0d5fac5d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1807-8c68097d-ea0b-4ff4-a1cc-b8ddba39a05c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1808-c957dc29-68a1-4f76-9d69-8ead61157d15.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1808-c957dc29-68a1-4f76-9d69-8ead61157d15.txn deleted file mode 100644 index 44b1bb062..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1808-c957dc29-68a1-4f76-9d69-8ead61157d15.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1809-793b90a7-4aca-4b3c-ace8-f74a5bcfaffe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1809-793b90a7-4aca-4b3c-ace8-f74a5bcfaffe.txn deleted file mode 100644 index 163555f04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1809-793b90a7-4aca-4b3c-ace8-f74a5bcfaffe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/181-813a98d1-f2d5-4d36-994e-e35189bce1c5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/181-813a98d1-f2d5-4d36-994e-e35189bce1c5.txn deleted file mode 100644 index 101b65e1a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/181-813a98d1-f2d5-4d36-994e-e35189bce1c5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1810-02f3af28-e51c-4469-b3d6-39f675d407b8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1810-02f3af28-e51c-4469-b3d6-39f675d407b8.txn deleted file mode 100644 index ca543abaf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1810-02f3af28-e51c-4469-b3d6-39f675d407b8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1811-a6438b93-ff0e-4f2d-8ccb-09fd6ee2ab42.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1811-a6438b93-ff0e-4f2d-8ccb-09fd6ee2ab42.txn deleted file mode 100644 index 0c69e9e33..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1811-a6438b93-ff0e-4f2d-8ccb-09fd6ee2ab42.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1812-468ca68e-88a1-4265-8842-6a6c0e04df13.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1812-468ca68e-88a1-4265-8842-6a6c0e04df13.txn deleted file mode 100644 index 1fa3accff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1812-468ca68e-88a1-4265-8842-6a6c0e04df13.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1813-d77e563a-81dc-4cf0-89d4-f80536609ec8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1813-d77e563a-81dc-4cf0-89d4-f80536609ec8.txn deleted file mode 100644 index 3e3bae56e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1813-d77e563a-81dc-4cf0-89d4-f80536609ec8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1814-d2814b37-760f-489e-b836-34964368d6c1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1814-d2814b37-760f-489e-b836-34964368d6c1.txn deleted file mode 100644 index 3adf9c97f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1814-d2814b37-760f-489e-b836-34964368d6c1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1815-4c144b32-1c88-43b5-97aa-6239b94e0dad.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1815-4c144b32-1c88-43b5-97aa-6239b94e0dad.txn deleted file mode 100644 index 8f1e1fcdf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1815-4c144b32-1c88-43b5-97aa-6239b94e0dad.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1816-62dcd0ef-4370-4b40-a762-62f33c33db63.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1816-62dcd0ef-4370-4b40-a762-62f33c33db63.txn deleted file mode 100644 index 4d0d8fda8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1816-62dcd0ef-4370-4b40-a762-62f33c33db63.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1817-1a3fe5ef-423c-4952-a670-bede3a2dc1ca.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1817-1a3fe5ef-423c-4952-a670-bede3a2dc1ca.txn deleted file mode 100644 index 652351db2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1817-1a3fe5ef-423c-4952-a670-bede3a2dc1ca.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1818-59cb8baa-10ba-4dbe-80a9-9dba6c583c18.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1818-59cb8baa-10ba-4dbe-80a9-9dba6c583c18.txn deleted file mode 100644 index 817c76f66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1818-59cb8baa-10ba-4dbe-80a9-9dba6c583c18.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1819-1264c745-2241-40bf-9300-1fa0e4f140cc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1819-1264c745-2241-40bf-9300-1fa0e4f140cc.txn deleted file mode 100644 index 8fac37a56..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1819-1264c745-2241-40bf-9300-1fa0e4f140cc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/182-859aee08-8933-440e-857d-a491c6f90ea0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/182-859aee08-8933-440e-857d-a491c6f90ea0.txn deleted file mode 100644 index 04e55f132..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/182-859aee08-8933-440e-857d-a491c6f90ea0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1820-3d86e053-1211-410b-af6d-f5f426e8395c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1820-3d86e053-1211-410b-af6d-f5f426e8395c.txn deleted file mode 100644 index d2328de2f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1820-3d86e053-1211-410b-af6d-f5f426e8395c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1821-bf4145e7-e015-4ce6-ab6c-fd611c0ebb1f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1821-bf4145e7-e015-4ce6-ab6c-fd611c0ebb1f.txn deleted file mode 100644 index 86d09730a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1821-bf4145e7-e015-4ce6-ab6c-fd611c0ebb1f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1822-4601b2f6-8fe2-40e7-b446-5389baa996ab.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1822-4601b2f6-8fe2-40e7-b446-5389baa996ab.txn deleted file mode 100644 index 4af674819..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1822-4601b2f6-8fe2-40e7-b446-5389baa996ab.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1823-e4713fb9-81ef-4f76-8fda-8a0be0c05966.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1823-e4713fb9-81ef-4f76-8fda-8a0be0c05966.txn deleted file mode 100644 index dd33aa8c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1823-e4713fb9-81ef-4f76-8fda-8a0be0c05966.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1824-7848970f-ff80-43a5-b84d-1ac2eed42fab.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1824-7848970f-ff80-43a5-b84d-1ac2eed42fab.txn deleted file mode 100644 index 23d77b144..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1824-7848970f-ff80-43a5-b84d-1ac2eed42fab.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1825-7abf688f-b716-4a6e-91b3-28cf230a66bb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1825-7abf688f-b716-4a6e-91b3-28cf230a66bb.txn deleted file mode 100644 index a70d2b587..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1825-7abf688f-b716-4a6e-91b3-28cf230a66bb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1826-358a1db4-4b16-4810-b9c4-b750a071961e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1826-358a1db4-4b16-4810-b9c4-b750a071961e.txn deleted file mode 100644 index 69653070f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1826-358a1db4-4b16-4810-b9c4-b750a071961e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1827-d40c589c-38bb-486a-8b19-1f9a5d78fdcd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1827-d40c589c-38bb-486a-8b19-1f9a5d78fdcd.txn deleted file mode 100644 index 80cacb5b7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1827-d40c589c-38bb-486a-8b19-1f9a5d78fdcd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1828-9af51e1d-91c4-4e60-9998-dd2f91fa5980.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1828-9af51e1d-91c4-4e60-9998-dd2f91fa5980.txn deleted file mode 100644 index a45260bfc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1828-9af51e1d-91c4-4e60-9998-dd2f91fa5980.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1829-ef9d0d61-470c-4fa2-86f5-3297f621bbaa.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1829-ef9d0d61-470c-4fa2-86f5-3297f621bbaa.txn deleted file mode 100644 index 74cb35d05..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1829-ef9d0d61-470c-4fa2-86f5-3297f621bbaa.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/183-6fcde7bb-fb29-428c-a5cc-839034a6dc5e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/183-6fcde7bb-fb29-428c-a5cc-839034a6dc5e.txn deleted file mode 100644 index cb4b8d380..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/183-6fcde7bb-fb29-428c-a5cc-839034a6dc5e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1830-88354ad6-945f-4166-aec0-06b9787765d8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1830-88354ad6-945f-4166-aec0-06b9787765d8.txn deleted file mode 100644 index d258b684c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1830-88354ad6-945f-4166-aec0-06b9787765d8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1831-1f315591-0f25-47f5-95e1-8e61f1cf05c2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1831-1f315591-0f25-47f5-95e1-8e61f1cf05c2.txn deleted file mode 100644 index 73c4703dd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1831-1f315591-0f25-47f5-95e1-8e61f1cf05c2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1832-e2fc3f41-e281-4fa8-be44-ebbd41b624a9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1832-e2fc3f41-e281-4fa8-be44-ebbd41b624a9.txn deleted file mode 100644 index e6b4434b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1832-e2fc3f41-e281-4fa8-be44-ebbd41b624a9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1833-bd567406-8faa-4c0c-a35d-c4191cded7fc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1833-bd567406-8faa-4c0c-a35d-c4191cded7fc.txn deleted file mode 100644 index fe3acf973..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1833-bd567406-8faa-4c0c-a35d-c4191cded7fc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1834-1fa79e4f-0e20-4181-ae7c-89c4849294db.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1834-1fa79e4f-0e20-4181-ae7c-89c4849294db.txn deleted file mode 100644 index 947351dae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1834-1fa79e4f-0e20-4181-ae7c-89c4849294db.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1835-be48c73f-e7b6-4f83-8975-799ff2603571.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1835-be48c73f-e7b6-4f83-8975-799ff2603571.txn deleted file mode 100644 index 8979de517..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1835-be48c73f-e7b6-4f83-8975-799ff2603571.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1836-21ae6c5f-06c7-4c8d-84d1-0a9b3cea09c4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1836-21ae6c5f-06c7-4c8d-84d1-0a9b3cea09c4.txn deleted file mode 100644 index 03148816e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1836-21ae6c5f-06c7-4c8d-84d1-0a9b3cea09c4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1837-d2b9d0bb-2465-4478-86fb-8c27b87533cd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1837-d2b9d0bb-2465-4478-86fb-8c27b87533cd.txn deleted file mode 100644 index 552096a31..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1837-d2b9d0bb-2465-4478-86fb-8c27b87533cd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1838-48df8cd8-c46e-459f-ba51-bbf62c92c9fa.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1838-48df8cd8-c46e-459f-ba51-bbf62c92c9fa.txn deleted file mode 100644 index 93d249940..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1838-48df8cd8-c46e-459f-ba51-bbf62c92c9fa.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1839-7c4de2ab-6be8-4195-8929-4e1ff8805df1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1839-7c4de2ab-6be8-4195-8929-4e1ff8805df1.txn deleted file mode 100644 index 6d3fcf13e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1839-7c4de2ab-6be8-4195-8929-4e1ff8805df1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/184-8222726e-3572-4bcc-821c-2f4592a46ee2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/184-8222726e-3572-4bcc-821c-2f4592a46ee2.txn deleted file mode 100644 index 17fdebd39..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/184-8222726e-3572-4bcc-821c-2f4592a46ee2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1840-b3675a74-e28c-4c2e-8c9f-13e19b23db41.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1840-b3675a74-e28c-4c2e-8c9f-13e19b23db41.txn deleted file mode 100644 index 34006988b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1840-b3675a74-e28c-4c2e-8c9f-13e19b23db41.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1841-0aa70407-d81e-4370-8561-e24f19b47c61.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1841-0aa70407-d81e-4370-8561-e24f19b47c61.txn deleted file mode 100644 index 580f8a264..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1841-0aa70407-d81e-4370-8561-e24f19b47c61.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1842-3cd40a77-ebb3-459f-b050-8139d6f8ad16.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1842-3cd40a77-ebb3-459f-b050-8139d6f8ad16.txn deleted file mode 100644 index c2dd03c2e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1842-3cd40a77-ebb3-459f-b050-8139d6f8ad16.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1843-5d06b64a-e0be-4bc0-81e3-6c9cf64b3715.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1843-5d06b64a-e0be-4bc0-81e3-6c9cf64b3715.txn deleted file mode 100644 index cbad09d70..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1843-5d06b64a-e0be-4bc0-81e3-6c9cf64b3715.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1844-bc76bc77-cb7c-4e60-916b-9ed7b2b00f62.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1844-bc76bc77-cb7c-4e60-916b-9ed7b2b00f62.txn deleted file mode 100644 index 247653ad1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1844-bc76bc77-cb7c-4e60-916b-9ed7b2b00f62.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1845-ff75206a-1624-49c7-9645-e398515bd379.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1845-ff75206a-1624-49c7-9645-e398515bd379.txn deleted file mode 100644 index 7186d8464..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1845-ff75206a-1624-49c7-9645-e398515bd379.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1846-6282a4b6-8af6-4802-8193-beb57de17bee.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1846-6282a4b6-8af6-4802-8193-beb57de17bee.txn deleted file mode 100644 index bb8c89862..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1846-6282a4b6-8af6-4802-8193-beb57de17bee.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1847-daa49c48-6628-477f-9f86-5cb0f96b7eb5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1847-daa49c48-6628-477f-9f86-5cb0f96b7eb5.txn deleted file mode 100644 index e32dcbcdd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1847-daa49c48-6628-477f-9f86-5cb0f96b7eb5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1848-18b13596-47d8-4ab4-8528-71a4e51e01f6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1848-18b13596-47d8-4ab4-8528-71a4e51e01f6.txn deleted file mode 100644 index d1b8e504f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1848-18b13596-47d8-4ab4-8528-71a4e51e01f6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1849-5ca4c0db-78e5-4ca4-805e-37b8923d1034.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1849-5ca4c0db-78e5-4ca4-805e-37b8923d1034.txn deleted file mode 100644 index 576dbd449..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1849-5ca4c0db-78e5-4ca4-805e-37b8923d1034.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/185-e1890bb6-67d7-4887-a0b1-5366892f8d25.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/185-e1890bb6-67d7-4887-a0b1-5366892f8d25.txn deleted file mode 100644 index 90a915b8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/185-e1890bb6-67d7-4887-a0b1-5366892f8d25.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1850-7c3cdaeb-64ee-4ad2-9884-a07000f8a497.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1850-7c3cdaeb-64ee-4ad2-9884-a07000f8a497.txn deleted file mode 100644 index 03b7c55f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1850-7c3cdaeb-64ee-4ad2-9884-a07000f8a497.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1851-189a2f34-f38c-4638-9625-1a9134969dad.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1851-189a2f34-f38c-4638-9625-1a9134969dad.txn deleted file mode 100644 index f195ec846..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1851-189a2f34-f38c-4638-9625-1a9134969dad.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1852-8c46569f-363d-40e2-84f6-eae8195cad84.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1852-8c46569f-363d-40e2-84f6-eae8195cad84.txn deleted file mode 100644 index a395c5e43..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1852-8c46569f-363d-40e2-84f6-eae8195cad84.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1853-83b829fb-d38a-4dac-a117-d78fe7cd3d2c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1853-83b829fb-d38a-4dac-a117-d78fe7cd3d2c.txn deleted file mode 100644 index 4bc4e6a85..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1853-83b829fb-d38a-4dac-a117-d78fe7cd3d2c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1854-e2536d25-41e7-4cc4-a80c-64e2d39eed9f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1854-e2536d25-41e7-4cc4-a80c-64e2d39eed9f.txn deleted file mode 100644 index 71a4584fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1854-e2536d25-41e7-4cc4-a80c-64e2d39eed9f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1855-5190b37c-0b0e-42ba-aa89-c00f11ea3da9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1855-5190b37c-0b0e-42ba-aa89-c00f11ea3da9.txn deleted file mode 100644 index c62462d0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1855-5190b37c-0b0e-42ba-aa89-c00f11ea3da9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1856-cfb65df2-1dce-4b87-a05e-88f7308b9393.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1856-cfb65df2-1dce-4b87-a05e-88f7308b9393.txn deleted file mode 100644 index 6f35c5c79..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1856-cfb65df2-1dce-4b87-a05e-88f7308b9393.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1857-ad383381-8f06-4c53-861f-2090f2faecaf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1857-ad383381-8f06-4c53-861f-2090f2faecaf.txn deleted file mode 100644 index ba9572a64..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1857-ad383381-8f06-4c53-861f-2090f2faecaf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1858-c8f9ccbb-9703-4e2d-bbda-e926459657aa.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1858-c8f9ccbb-9703-4e2d-bbda-e926459657aa.txn deleted file mode 100644 index e16620938..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1858-c8f9ccbb-9703-4e2d-bbda-e926459657aa.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1859-5024c2f1-ab8e-4249-8f58-ab1666f7bf82.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1859-5024c2f1-ab8e-4249-8f58-ab1666f7bf82.txn deleted file mode 100644 index 28876054f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1859-5024c2f1-ab8e-4249-8f58-ab1666f7bf82.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/186-f6695da0-3b9a-4857-9eb1-a22d5352da54.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/186-f6695da0-3b9a-4857-9eb1-a22d5352da54.txn deleted file mode 100644 index bf0a989f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/186-f6695da0-3b9a-4857-9eb1-a22d5352da54.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1860-e67c0840-265e-46e0-8fac-21f4887a3eed.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1860-e67c0840-265e-46e0-8fac-21f4887a3eed.txn deleted file mode 100644 index 100b8dcc4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1860-e67c0840-265e-46e0-8fac-21f4887a3eed.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1861-63b8b23b-ec1d-42d9-bfff-c14558f9cc82.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1861-63b8b23b-ec1d-42d9-bfff-c14558f9cc82.txn deleted file mode 100644 index c53d1b5b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1861-63b8b23b-ec1d-42d9-bfff-c14558f9cc82.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1862-d480479c-8407-46b7-a62f-fbb26f4afc32.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1862-d480479c-8407-46b7-a62f-fbb26f4afc32.txn deleted file mode 100644 index 1fd7d17b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1862-d480479c-8407-46b7-a62f-fbb26f4afc32.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1863-618b60ad-d5d6-4017-8d02-078990ec64eb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1863-618b60ad-d5d6-4017-8d02-078990ec64eb.txn deleted file mode 100644 index dab5c4c18..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1863-618b60ad-d5d6-4017-8d02-078990ec64eb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1864-6cff4fd4-856a-4f33-b0c4-c9906ce221b7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1864-6cff4fd4-856a-4f33-b0c4-c9906ce221b7.txn deleted file mode 100644 index fdfd6f857..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1864-6cff4fd4-856a-4f33-b0c4-c9906ce221b7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1865-d9cd7158-617d-40f8-bf83-823619a6613b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1865-d9cd7158-617d-40f8-bf83-823619a6613b.txn deleted file mode 100644 index 4e810da99..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1865-d9cd7158-617d-40f8-bf83-823619a6613b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1866-e2d2fcc4-65ae-4e32-aa86-4ca145335846.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1866-e2d2fcc4-65ae-4e32-aa86-4ca145335846.txn deleted file mode 100644 index 267e3b278..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1866-e2d2fcc4-65ae-4e32-aa86-4ca145335846.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1867-d6915fbd-d04a-4927-937c-309ec7a02d3c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1867-d6915fbd-d04a-4927-937c-309ec7a02d3c.txn deleted file mode 100644 index ad00d0a98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1867-d6915fbd-d04a-4927-937c-309ec7a02d3c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1868-2318bb5f-e92d-421a-9ec6-ecca37439ffd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1868-2318bb5f-e92d-421a-9ec6-ecca37439ffd.txn deleted file mode 100644 index 2d8976358..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1868-2318bb5f-e92d-421a-9ec6-ecca37439ffd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1869-30663e4b-bafc-4e6e-819d-196c28d1e2ff.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1869-30663e4b-bafc-4e6e-819d-196c28d1e2ff.txn deleted file mode 100644 index 1f71888cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1869-30663e4b-bafc-4e6e-819d-196c28d1e2ff.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/187-4b4574cb-28ba-4774-8300-a4b68540517a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/187-4b4574cb-28ba-4774-8300-a4b68540517a.txn deleted file mode 100644 index 57b487fb2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/187-4b4574cb-28ba-4774-8300-a4b68540517a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1870-0b4a460c-0555-4071-831d-3cbad102d0c6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1870-0b4a460c-0555-4071-831d-3cbad102d0c6.txn deleted file mode 100644 index 17e79ec51..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1870-0b4a460c-0555-4071-831d-3cbad102d0c6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1871-192737f5-5302-4769-8e64-2f8c3a5931fd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1871-192737f5-5302-4769-8e64-2f8c3a5931fd.txn deleted file mode 100644 index 00e030c8b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1871-192737f5-5302-4769-8e64-2f8c3a5931fd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1872-fda2ca27-d9f1-410c-ac80-6a81df87a912.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1872-fda2ca27-d9f1-410c-ac80-6a81df87a912.txn deleted file mode 100644 index b1bffee77..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1872-fda2ca27-d9f1-410c-ac80-6a81df87a912.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1873-7ee44af8-d409-44a4-b6e7-c61832d671ce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1873-7ee44af8-d409-44a4-b6e7-c61832d671ce.txn deleted file mode 100644 index 74042c8bd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1873-7ee44af8-d409-44a4-b6e7-c61832d671ce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1874-e917e80d-148b-418d-9c96-0c97db4bc9cb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1874-e917e80d-148b-418d-9c96-0c97db4bc9cb.txn deleted file mode 100644 index 9a3590fc3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1874-e917e80d-148b-418d-9c96-0c97db4bc9cb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1875-6c3f045d-976a-46db-9431-f825ebaf00e0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1875-6c3f045d-976a-46db-9431-f825ebaf00e0.txn deleted file mode 100644 index 7cf954dea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1875-6c3f045d-976a-46db-9431-f825ebaf00e0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1876-8de4287e-c649-49bc-89fd-a895d37cbdba.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1876-8de4287e-c649-49bc-89fd-a895d37cbdba.txn deleted file mode 100644 index a901e3e98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1876-8de4287e-c649-49bc-89fd-a895d37cbdba.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1877-ca35e8d5-ac2c-4bb8-a881-82be86460b43.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1877-ca35e8d5-ac2c-4bb8-a881-82be86460b43.txn deleted file mode 100644 index a552b6958..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1877-ca35e8d5-ac2c-4bb8-a881-82be86460b43.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1878-e22dbe8b-f20c-4eb1-aee2-f65c2abd36cc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1878-e22dbe8b-f20c-4eb1-aee2-f65c2abd36cc.txn deleted file mode 100644 index bd77a9025..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1878-e22dbe8b-f20c-4eb1-aee2-f65c2abd36cc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1879-cb3e640f-3adc-4436-b9ba-829c84b523cc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1879-cb3e640f-3adc-4436-b9ba-829c84b523cc.txn deleted file mode 100644 index 2433ab722..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1879-cb3e640f-3adc-4436-b9ba-829c84b523cc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/188-0220da04-71ce-492a-8c3a-15aec49cd9f4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/188-0220da04-71ce-492a-8c3a-15aec49cd9f4.txn deleted file mode 100644 index dfa607329..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/188-0220da04-71ce-492a-8c3a-15aec49cd9f4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1880-7f13b081-b4b8-4af3-8a48-9303331c20c1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1880-7f13b081-b4b8-4af3-8a48-9303331c20c1.txn deleted file mode 100644 index a884b50bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1880-7f13b081-b4b8-4af3-8a48-9303331c20c1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1881-2deb623c-587d-4c65-afdf-51dd0a040985.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1881-2deb623c-587d-4c65-afdf-51dd0a040985.txn deleted file mode 100644 index bde53186b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1881-2deb623c-587d-4c65-afdf-51dd0a040985.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1882-078cc855-0be0-49f1-98a1-dd0a29bee760.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1882-078cc855-0be0-49f1-98a1-dd0a29bee760.txn deleted file mode 100644 index 663cae88f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1882-078cc855-0be0-49f1-98a1-dd0a29bee760.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1883-b8545424-ac3a-400e-bf58-2e70bfe5fe1c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1883-b8545424-ac3a-400e-bf58-2e70bfe5fe1c.txn deleted file mode 100644 index d6a948f47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1883-b8545424-ac3a-400e-bf58-2e70bfe5fe1c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1884-f6f92879-6beb-4084-b32d-c8e66f37a8c4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1884-f6f92879-6beb-4084-b32d-c8e66f37a8c4.txn deleted file mode 100644 index 833fb9227..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1884-f6f92879-6beb-4084-b32d-c8e66f37a8c4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1885-0da12ce9-e6a6-477b-8214-c5a01e43b155.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1885-0da12ce9-e6a6-477b-8214-c5a01e43b155.txn deleted file mode 100644 index ae5a1e37d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1885-0da12ce9-e6a6-477b-8214-c5a01e43b155.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1886-c4467994-490f-4817-b1d9-dd1ffd43a07b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1886-c4467994-490f-4817-b1d9-dd1ffd43a07b.txn deleted file mode 100644 index dd3f8624b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1886-c4467994-490f-4817-b1d9-dd1ffd43a07b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1887-70627a53-fcf8-4973-99c8-eaddf7d7b0c1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1887-70627a53-fcf8-4973-99c8-eaddf7d7b0c1.txn deleted file mode 100644 index da3572f03..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1887-70627a53-fcf8-4973-99c8-eaddf7d7b0c1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1888-7ce160e9-1d6b-43b6-a8fe-f22033bff18a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1888-7ce160e9-1d6b-43b6-a8fe-f22033bff18a.txn deleted file mode 100644 index d3bad0e97..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1888-7ce160e9-1d6b-43b6-a8fe-f22033bff18a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1889-99770d94-af86-4bf0-ba4b-8fafe0f90795.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1889-99770d94-af86-4bf0-ba4b-8fafe0f90795.txn deleted file mode 100644 index 835bb84f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1889-99770d94-af86-4bf0-ba4b-8fafe0f90795.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/189-3e17aaed-d532-4ff2-ad2e-3eb6aa161984.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/189-3e17aaed-d532-4ff2-ad2e-3eb6aa161984.txn deleted file mode 100644 index d748b5975..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/189-3e17aaed-d532-4ff2-ad2e-3eb6aa161984.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1890-67e1f60e-f106-43c5-b0f7-270b128d3d79.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1890-67e1f60e-f106-43c5-b0f7-270b128d3d79.txn deleted file mode 100644 index 3d6cbc480..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1890-67e1f60e-f106-43c5-b0f7-270b128d3d79.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1891-2e6fe8a6-fb82-4a1d-9ba5-2d4e2c9bce15.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1891-2e6fe8a6-fb82-4a1d-9ba5-2d4e2c9bce15.txn deleted file mode 100644 index ab9c505f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1891-2e6fe8a6-fb82-4a1d-9ba5-2d4e2c9bce15.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1892-80d4082a-0634-4c04-b025-d5ff492d82e3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1892-80d4082a-0634-4c04-b025-d5ff492d82e3.txn deleted file mode 100644 index 48e7b8d62..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1892-80d4082a-0634-4c04-b025-d5ff492d82e3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1893-1aff10ab-0026-4a9d-9fa1-4e9eed47594a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1893-1aff10ab-0026-4a9d-9fa1-4e9eed47594a.txn deleted file mode 100644 index e53ec32d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1893-1aff10ab-0026-4a9d-9fa1-4e9eed47594a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1894-b3a3bc49-e999-4e3f-afaf-bdf6932764bc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1894-b3a3bc49-e999-4e3f-afaf-bdf6932764bc.txn deleted file mode 100644 index 333e37e75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1894-b3a3bc49-e999-4e3f-afaf-bdf6932764bc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1895-297145e3-7c6c-4e79-abaf-938c17a02521.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1895-297145e3-7c6c-4e79-abaf-938c17a02521.txn deleted file mode 100644 index 1f83d5e98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1895-297145e3-7c6c-4e79-abaf-938c17a02521.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1896-c45dd456-d3b2-4fce-8077-08723cdea7fe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1896-c45dd456-d3b2-4fce-8077-08723cdea7fe.txn deleted file mode 100644 index 4828d525f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1896-c45dd456-d3b2-4fce-8077-08723cdea7fe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1897-c5dd6747-90a5-44bd-9247-bb03dcdfc123.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1897-c5dd6747-90a5-44bd-9247-bb03dcdfc123.txn deleted file mode 100644 index cdbf46027..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1897-c5dd6747-90a5-44bd-9247-bb03dcdfc123.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1898-4ecb9615-ba50-4b2e-8ed0-9490ac2364d3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1898-4ecb9615-ba50-4b2e-8ed0-9490ac2364d3.txn deleted file mode 100644 index fce77f488..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1898-4ecb9615-ba50-4b2e-8ed0-9490ac2364d3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1899-2645e56e-e122-4c97-9897-a245cfaa7e4b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1899-2645e56e-e122-4c97-9897-a245cfaa7e4b.txn deleted file mode 100644 index 70f66a59c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1899-2645e56e-e122-4c97-9897-a245cfaa7e4b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/19-12f83e26-11f8-451e-932e-c3a218b9a684.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/19-12f83e26-11f8-451e-932e-c3a218b9a684.txn deleted file mode 100644 index 3a476c7b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/19-12f83e26-11f8-451e-932e-c3a218b9a684.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/190-3cbf4aa4-fefb-43af-99d7-32ee8e1ead91.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/190-3cbf4aa4-fefb-43af-99d7-32ee8e1ead91.txn deleted file mode 100644 index ac52d4d1c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/190-3cbf4aa4-fefb-43af-99d7-32ee8e1ead91.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1900-8f55a688-0f36-4e38-9289-4c541914aa3a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1900-8f55a688-0f36-4e38-9289-4c541914aa3a.txn deleted file mode 100644 index eb0a85413..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1900-8f55a688-0f36-4e38-9289-4c541914aa3a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1901-e4a5afec-6b4e-4a71-ab66-7f0031cff1c4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1901-e4a5afec-6b4e-4a71-ab66-7f0031cff1c4.txn deleted file mode 100644 index 18556f670..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1901-e4a5afec-6b4e-4a71-ab66-7f0031cff1c4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1902-4cc9a92e-3ed6-4b30-b90f-6539c72aefc4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1902-4cc9a92e-3ed6-4b30-b90f-6539c72aefc4.txn deleted file mode 100644 index fcb2f0317..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1902-4cc9a92e-3ed6-4b30-b90f-6539c72aefc4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1903-d03391e4-24d5-4f8f-8232-94e685190906.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1903-d03391e4-24d5-4f8f-8232-94e685190906.txn deleted file mode 100644 index 0042ef995..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1903-d03391e4-24d5-4f8f-8232-94e685190906.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1904-5c91eb8f-1d20-49ed-87f0-c9304ef2a4a3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1904-5c91eb8f-1d20-49ed-87f0-c9304ef2a4a3.txn deleted file mode 100644 index c1e6507db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1904-5c91eb8f-1d20-49ed-87f0-c9304ef2a4a3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1905-ecb8dc97-848b-428e-ba53-c84f3879401c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1905-ecb8dc97-848b-428e-ba53-c84f3879401c.txn deleted file mode 100644 index 78da5798d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1905-ecb8dc97-848b-428e-ba53-c84f3879401c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1906-ae741da0-82b1-4423-a568-a871e524e74a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1906-ae741da0-82b1-4423-a568-a871e524e74a.txn deleted file mode 100644 index 316d3b43c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1906-ae741da0-82b1-4423-a568-a871e524e74a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1907-ceaf9292-ef35-4ee1-bb5b-73964d4fa644.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1907-ceaf9292-ef35-4ee1-bb5b-73964d4fa644.txn deleted file mode 100644 index 14b3cab96..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1907-ceaf9292-ef35-4ee1-bb5b-73964d4fa644.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1908-933eca5b-9bd2-4e48-a9a7-e8037adc95b1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1908-933eca5b-9bd2-4e48-a9a7-e8037adc95b1.txn deleted file mode 100644 index b86d1252e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1908-933eca5b-9bd2-4e48-a9a7-e8037adc95b1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1909-55248424-ae2b-49a6-93e9-2a5a8fa59be2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1909-55248424-ae2b-49a6-93e9-2a5a8fa59be2.txn deleted file mode 100644 index d0712f455..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1909-55248424-ae2b-49a6-93e9-2a5a8fa59be2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/191-849a5804-31c0-4cf4-baa8-60bdfc363371.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/191-849a5804-31c0-4cf4-baa8-60bdfc363371.txn deleted file mode 100644 index 1fafd9952..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/191-849a5804-31c0-4cf4-baa8-60bdfc363371.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1910-463da7c2-90b1-4301-8d11-c338f8102142.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1910-463da7c2-90b1-4301-8d11-c338f8102142.txn deleted file mode 100644 index 95571d77c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1910-463da7c2-90b1-4301-8d11-c338f8102142.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1911-d271008c-e473-4a6f-9d9a-fe3e25111a65.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1911-d271008c-e473-4a6f-9d9a-fe3e25111a65.txn deleted file mode 100644 index a26bc7c01..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1911-d271008c-e473-4a6f-9d9a-fe3e25111a65.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1912-82abb79c-8a60-43f6-9bc0-2c60709a72ae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1912-82abb79c-8a60-43f6-9bc0-2c60709a72ae.txn deleted file mode 100644 index ac9a3cd9b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1912-82abb79c-8a60-43f6-9bc0-2c60709a72ae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1913-d9b6dafa-7399-4ff2-8e6a-809856fbdfaf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1913-d9b6dafa-7399-4ff2-8e6a-809856fbdfaf.txn deleted file mode 100644 index 3ac0f0837..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1913-d9b6dafa-7399-4ff2-8e6a-809856fbdfaf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1914-0f963180-027a-4526-a707-2218b54a707e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1914-0f963180-027a-4526-a707-2218b54a707e.txn deleted file mode 100644 index 52ca2050c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1914-0f963180-027a-4526-a707-2218b54a707e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1915-00ee721c-68af-49cf-91ae-b5dbef137f1f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1915-00ee721c-68af-49cf-91ae-b5dbef137f1f.txn deleted file mode 100644 index 8c8040e4a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1915-00ee721c-68af-49cf-91ae-b5dbef137f1f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1916-a3bf17c4-9556-416a-a250-a611a3f650d8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1916-a3bf17c4-9556-416a-a250-a611a3f650d8.txn deleted file mode 100644 index 09f58e563..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1916-a3bf17c4-9556-416a-a250-a611a3f650d8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1917-cdf2b095-d46f-45f0-9658-1c4c31aeea56.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1917-cdf2b095-d46f-45f0-9658-1c4c31aeea56.txn deleted file mode 100644 index ea2df3532..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1917-cdf2b095-d46f-45f0-9658-1c4c31aeea56.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1918-eb3d9a3d-f48c-48aa-82fc-5b1f89508072.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1918-eb3d9a3d-f48c-48aa-82fc-5b1f89508072.txn deleted file mode 100644 index 03205f5d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1918-eb3d9a3d-f48c-48aa-82fc-5b1f89508072.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1919-19ae6b8e-8b1d-4a9a-a82b-953d7dcd3d76.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1919-19ae6b8e-8b1d-4a9a-a82b-953d7dcd3d76.txn deleted file mode 100644 index d904ec36b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1919-19ae6b8e-8b1d-4a9a-a82b-953d7dcd3d76.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/192-cc9a1bc2-3e28-487d-b31d-c677c00f5a11.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/192-cc9a1bc2-3e28-487d-b31d-c677c00f5a11.txn deleted file mode 100644 index 75c190348..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/192-cc9a1bc2-3e28-487d-b31d-c677c00f5a11.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1920-03faf6f6-c572-4def-b6d3-2c3f1398c4bf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1920-03faf6f6-c572-4def-b6d3-2c3f1398c4bf.txn deleted file mode 100644 index 28fef06a6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1920-03faf6f6-c572-4def-b6d3-2c3f1398c4bf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1921-36e41c73-2235-4ca0-9136-9ad23f3d170b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1921-36e41c73-2235-4ca0-9136-9ad23f3d170b.txn deleted file mode 100644 index d15004627..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1921-36e41c73-2235-4ca0-9136-9ad23f3d170b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1922-5f78beb0-51f7-41f4-af1c-e0a1d2d70a41.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1922-5f78beb0-51f7-41f4-af1c-e0a1d2d70a41.txn deleted file mode 100644 index af5834ead..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1922-5f78beb0-51f7-41f4-af1c-e0a1d2d70a41.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1923-f4d8e22c-7e07-4fe5-bdec-ebbeda3d4e2a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1923-f4d8e22c-7e07-4fe5-bdec-ebbeda3d4e2a.txn deleted file mode 100644 index 2f98c32ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1923-f4d8e22c-7e07-4fe5-bdec-ebbeda3d4e2a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1924-fb9dc41f-f2cf-438c-bc9b-7fcfe28cf132.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1924-fb9dc41f-f2cf-438c-bc9b-7fcfe28cf132.txn deleted file mode 100644 index 0ceb2c9c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1924-fb9dc41f-f2cf-438c-bc9b-7fcfe28cf132.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1925-fa2859d7-1a1a-4ecb-a437-8dab6f89f518.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1925-fa2859d7-1a1a-4ecb-a437-8dab6f89f518.txn deleted file mode 100644 index 1c4c5cd8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1925-fa2859d7-1a1a-4ecb-a437-8dab6f89f518.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1926-9b0135c6-d6bd-4d1c-9583-4ee46179093b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1926-9b0135c6-d6bd-4d1c-9583-4ee46179093b.txn deleted file mode 100644 index f2074530c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1926-9b0135c6-d6bd-4d1c-9583-4ee46179093b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1927-f716e404-51d4-4895-99d4-22f8a42b643d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1927-f716e404-51d4-4895-99d4-22f8a42b643d.txn deleted file mode 100644 index b714f38e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1927-f716e404-51d4-4895-99d4-22f8a42b643d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1928-7027a083-f409-44ee-9d6c-5d8d7a00c8f5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1928-7027a083-f409-44ee-9d6c-5d8d7a00c8f5.txn deleted file mode 100644 index 129ceac3d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1928-7027a083-f409-44ee-9d6c-5d8d7a00c8f5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1929-b01a5e6f-ee15-4175-995a-3320a8919195.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1929-b01a5e6f-ee15-4175-995a-3320a8919195.txn deleted file mode 100644 index e3bb28a5f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1929-b01a5e6f-ee15-4175-995a-3320a8919195.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/193-106ee3c4-043f-460c-b5db-3db576bdcc44.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/193-106ee3c4-043f-460c-b5db-3db576bdcc44.txn deleted file mode 100644 index 8876d5539..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/193-106ee3c4-043f-460c-b5db-3db576bdcc44.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1930-41838f9e-8eaf-40cd-aad2-f973c2c2df0f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1930-41838f9e-8eaf-40cd-aad2-f973c2c2df0f.txn deleted file mode 100644 index 21bd310ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1930-41838f9e-8eaf-40cd-aad2-f973c2c2df0f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1931-0d310b41-0680-44be-8495-2b1425688d6c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1931-0d310b41-0680-44be-8495-2b1425688d6c.txn deleted file mode 100644 index 06d097a3c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1931-0d310b41-0680-44be-8495-2b1425688d6c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1932-bc2010a7-5295-460c-b3ff-b2a67cc4cc5f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1932-bc2010a7-5295-460c-b3ff-b2a67cc4cc5f.txn deleted file mode 100644 index c5a4f7c36..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1932-bc2010a7-5295-460c-b3ff-b2a67cc4cc5f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1933-c28ab8fa-ea8d-4606-848d-346004f5e9f4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1933-c28ab8fa-ea8d-4606-848d-346004f5e9f4.txn deleted file mode 100644 index ff3639a3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1933-c28ab8fa-ea8d-4606-848d-346004f5e9f4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1934-0252ccc6-557c-43ae-b6ed-978f853f54cf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1934-0252ccc6-557c-43ae-b6ed-978f853f54cf.txn deleted file mode 100644 index b49817a57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1934-0252ccc6-557c-43ae-b6ed-978f853f54cf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1935-08c72910-a0c1-4075-9e54-cfa171f69e57.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1935-08c72910-a0c1-4075-9e54-cfa171f69e57.txn deleted file mode 100644 index ef863bfee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1935-08c72910-a0c1-4075-9e54-cfa171f69e57.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1936-23fe78eb-861e-4af6-9428-ce0d388a0306.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1936-23fe78eb-861e-4af6-9428-ce0d388a0306.txn deleted file mode 100644 index 3fa21c381..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1936-23fe78eb-861e-4af6-9428-ce0d388a0306.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1937-9360282b-2ff4-40ea-9515-97c494b91401.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1937-9360282b-2ff4-40ea-9515-97c494b91401.txn deleted file mode 100644 index 37021f034..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1937-9360282b-2ff4-40ea-9515-97c494b91401.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1938-d78545c6-5e1a-44c9-b5cd-57024d90456e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1938-d78545c6-5e1a-44c9-b5cd-57024d90456e.txn deleted file mode 100644 index d646b1cfb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1938-d78545c6-5e1a-44c9-b5cd-57024d90456e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1939-4c9eb5f2-4794-47e5-a4e2-caa8df6b5554.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1939-4c9eb5f2-4794-47e5-a4e2-caa8df6b5554.txn deleted file mode 100644 index e52bc2a06..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1939-4c9eb5f2-4794-47e5-a4e2-caa8df6b5554.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/194-da952333-e1a9-4765-a170-22636496b9ff.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/194-da952333-e1a9-4765-a170-22636496b9ff.txn deleted file mode 100644 index 4ff93072a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/194-da952333-e1a9-4765-a170-22636496b9ff.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1940-aed4694e-f626-4249-865a-7856fb4fea90.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1940-aed4694e-f626-4249-865a-7856fb4fea90.txn deleted file mode 100644 index 2a1a2eee0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1940-aed4694e-f626-4249-865a-7856fb4fea90.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1941-28df8745-e269-4bee-a0c9-e1c78638ec97.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1941-28df8745-e269-4bee-a0c9-e1c78638ec97.txn deleted file mode 100644 index 0faf7c2cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1941-28df8745-e269-4bee-a0c9-e1c78638ec97.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1942-50163060-add8-4c8c-85a9-adcff871f32f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1942-50163060-add8-4c8c-85a9-adcff871f32f.txn deleted file mode 100644 index 92c189861..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1942-50163060-add8-4c8c-85a9-adcff871f32f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1943-7b357259-88d7-4834-8e43-e4d5f5f98f21.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1943-7b357259-88d7-4834-8e43-e4d5f5f98f21.txn deleted file mode 100644 index 0a5791806..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1943-7b357259-88d7-4834-8e43-e4d5f5f98f21.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1944-a9dc5283-b6cd-47ea-8795-bf570adfa372.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1944-a9dc5283-b6cd-47ea-8795-bf570adfa372.txn deleted file mode 100644 index 7acbe31d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1944-a9dc5283-b6cd-47ea-8795-bf570adfa372.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1945-a453445b-4343-4d13-b7fc-214517f57c3c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1945-a453445b-4343-4d13-b7fc-214517f57c3c.txn deleted file mode 100644 index ef0700ce1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1945-a453445b-4343-4d13-b7fc-214517f57c3c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1946-30916d69-35b4-4d5c-807a-4ab90fb30c09.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1946-30916d69-35b4-4d5c-807a-4ab90fb30c09.txn deleted file mode 100644 index 0bba9d40f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1946-30916d69-35b4-4d5c-807a-4ab90fb30c09.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1947-087e5676-3d45-40cd-a838-143a0ed115ac.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1947-087e5676-3d45-40cd-a838-143a0ed115ac.txn deleted file mode 100644 index 0fc02accf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1947-087e5676-3d45-40cd-a838-143a0ed115ac.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1948-0860045e-e536-4ac2-a20e-4384eb0ce6e9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1948-0860045e-e536-4ac2-a20e-4384eb0ce6e9.txn deleted file mode 100644 index 3c213dfd6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1948-0860045e-e536-4ac2-a20e-4384eb0ce6e9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1949-b9a64124-a9d7-4844-b491-1d06f158d662.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1949-b9a64124-a9d7-4844-b491-1d06f158d662.txn deleted file mode 100644 index 4ca41b366..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1949-b9a64124-a9d7-4844-b491-1d06f158d662.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/195-14812a25-5a25-4674-81c8-b44da1b30f9b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/195-14812a25-5a25-4674-81c8-b44da1b30f9b.txn deleted file mode 100644 index f80bd3e33..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/195-14812a25-5a25-4674-81c8-b44da1b30f9b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1950-febf2f45-6560-4da1-a617-96e209e32cdb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1950-febf2f45-6560-4da1-a617-96e209e32cdb.txn deleted file mode 100644 index cfced25d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1950-febf2f45-6560-4da1-a617-96e209e32cdb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1951-3d697d8c-b841-41fe-9f2e-4b6fc930e8c8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1951-3d697d8c-b841-41fe-9f2e-4b6fc930e8c8.txn deleted file mode 100644 index 1772f7cf4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1951-3d697d8c-b841-41fe-9f2e-4b6fc930e8c8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1952-f1d545b7-73f3-4bde-9f20-97fe9282c996.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1952-f1d545b7-73f3-4bde-9f20-97fe9282c996.txn deleted file mode 100644 index a3e1f2d4d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1952-f1d545b7-73f3-4bde-9f20-97fe9282c996.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1953-3af50435-7ec5-409e-a283-b93219a86542.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1953-3af50435-7ec5-409e-a283-b93219a86542.txn deleted file mode 100644 index 1454b465a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1953-3af50435-7ec5-409e-a283-b93219a86542.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1954-2c66a1f4-8935-4634-ae9e-48b78172f7d1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1954-2c66a1f4-8935-4634-ae9e-48b78172f7d1.txn deleted file mode 100644 index 609f28a42..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1954-2c66a1f4-8935-4634-ae9e-48b78172f7d1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1955-8d0695dc-cfcf-4462-a903-e2317072f0fd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1955-8d0695dc-cfcf-4462-a903-e2317072f0fd.txn deleted file mode 100644 index d048a3dd0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1955-8d0695dc-cfcf-4462-a903-e2317072f0fd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1956-97b33ee5-313f-4339-8f5f-d44e5ad8da4e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1956-97b33ee5-313f-4339-8f5f-d44e5ad8da4e.txn deleted file mode 100644 index fb7f6f139..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1956-97b33ee5-313f-4339-8f5f-d44e5ad8da4e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1957-506ba696-75c1-47e8-be1d-bc31f423b1f4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1957-506ba696-75c1-47e8-be1d-bc31f423b1f4.txn deleted file mode 100644 index a5e6bf83c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1957-506ba696-75c1-47e8-be1d-bc31f423b1f4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1958-c27995e1-1c84-4007-9e4b-04f982f854f3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1958-c27995e1-1c84-4007-9e4b-04f982f854f3.txn deleted file mode 100644 index a45978704..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1958-c27995e1-1c84-4007-9e4b-04f982f854f3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1959-db635590-07b2-4c9d-8ca8-894e11377b76.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1959-db635590-07b2-4c9d-8ca8-894e11377b76.txn deleted file mode 100644 index e1e814f4b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1959-db635590-07b2-4c9d-8ca8-894e11377b76.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/196-ebd3905b-90c8-474b-94f7-14766332de2d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/196-ebd3905b-90c8-474b-94f7-14766332de2d.txn deleted file mode 100644 index 30766bf17..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/196-ebd3905b-90c8-474b-94f7-14766332de2d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1960-5cec35f1-69e9-4578-9e10-b39e7d9561f9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1960-5cec35f1-69e9-4578-9e10-b39e7d9561f9.txn deleted file mode 100644 index 8190fc591..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1960-5cec35f1-69e9-4578-9e10-b39e7d9561f9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1961-828a2752-98bc-4228-add9-6a3806701f89.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1961-828a2752-98bc-4228-add9-6a3806701f89.txn deleted file mode 100644 index 7d4248538..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1961-828a2752-98bc-4228-add9-6a3806701f89.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1962-041e058c-6295-4732-95e0-fada8f4cf374.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1962-041e058c-6295-4732-95e0-fada8f4cf374.txn deleted file mode 100644 index ef7cd748e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1962-041e058c-6295-4732-95e0-fada8f4cf374.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1963-36787107-562f-4eed-8dfe-6b5f68e804b7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1963-36787107-562f-4eed-8dfe-6b5f68e804b7.txn deleted file mode 100644 index 77abfcd94..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1963-36787107-562f-4eed-8dfe-6b5f68e804b7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1964-944064a7-8d6e-4133-867d-f32a1f2e654d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1964-944064a7-8d6e-4133-867d-f32a1f2e654d.txn deleted file mode 100644 index b42e00baa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1964-944064a7-8d6e-4133-867d-f32a1f2e654d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1965-f0ea497c-3df7-4f84-9d5c-097c81c49eeb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1965-f0ea497c-3df7-4f84-9d5c-097c81c49eeb.txn deleted file mode 100644 index 30ba9cc39..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1965-f0ea497c-3df7-4f84-9d5c-097c81c49eeb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1966-772d3be3-8781-4fed-8cd2-814645b1c54d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1966-772d3be3-8781-4fed-8cd2-814645b1c54d.txn deleted file mode 100644 index 247ab5715..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1966-772d3be3-8781-4fed-8cd2-814645b1c54d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1967-f6cd5e26-0962-47be-b13e-13f6a47cf4e5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1967-f6cd5e26-0962-47be-b13e-13f6a47cf4e5.txn deleted file mode 100644 index 9ca6741a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1967-f6cd5e26-0962-47be-b13e-13f6a47cf4e5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1968-169a1c6c-b4be-44f2-84af-4f9d62d20986.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1968-169a1c6c-b4be-44f2-84af-4f9d62d20986.txn deleted file mode 100644 index 30e82fc39..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1968-169a1c6c-b4be-44f2-84af-4f9d62d20986.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1969-b7c3da04-65bc-492b-8d95-72a72320e7de.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1969-b7c3da04-65bc-492b-8d95-72a72320e7de.txn deleted file mode 100644 index 16e7ee420..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1969-b7c3da04-65bc-492b-8d95-72a72320e7de.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/197-cb9f7b33-2e1e-42d8-bf03-abdd684537cf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/197-cb9f7b33-2e1e-42d8-bf03-abdd684537cf.txn deleted file mode 100644 index 72b619b17..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/197-cb9f7b33-2e1e-42d8-bf03-abdd684537cf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1970-93bc2c5d-d879-4b9e-b37e-fe11163bfe2a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1970-93bc2c5d-d879-4b9e-b37e-fe11163bfe2a.txn deleted file mode 100644 index 41ca21a18..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1970-93bc2c5d-d879-4b9e-b37e-fe11163bfe2a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1971-15bfba57-8604-4458-90f3-417f2111946b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1971-15bfba57-8604-4458-90f3-417f2111946b.txn deleted file mode 100644 index 6fdc65f4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1971-15bfba57-8604-4458-90f3-417f2111946b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1972-4cc109df-35f3-4f1b-9571-d74f90cff81c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1972-4cc109df-35f3-4f1b-9571-d74f90cff81c.txn deleted file mode 100644 index 3d9704b92..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1972-4cc109df-35f3-4f1b-9571-d74f90cff81c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1973-4f55cdb3-cd38-4bdc-8207-858538d57f54.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1973-4f55cdb3-cd38-4bdc-8207-858538d57f54.txn deleted file mode 100644 index cd1c85e49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1973-4f55cdb3-cd38-4bdc-8207-858538d57f54.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1974-7528259f-b179-48c9-a3d4-fcdc3f820a86.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1974-7528259f-b179-48c9-a3d4-fcdc3f820a86.txn deleted file mode 100644 index ef4a0e901..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1974-7528259f-b179-48c9-a3d4-fcdc3f820a86.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1975-4852b288-9e8a-4001-9cb2-ccb8d50a42f7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1975-4852b288-9e8a-4001-9cb2-ccb8d50a42f7.txn deleted file mode 100644 index d50f821b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1975-4852b288-9e8a-4001-9cb2-ccb8d50a42f7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1976-3508ac5a-b053-46f3-8083-ec03fc67956d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1976-3508ac5a-b053-46f3-8083-ec03fc67956d.txn deleted file mode 100644 index 28f493375..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1976-3508ac5a-b053-46f3-8083-ec03fc67956d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1977-362d266d-058f-460c-bb25-7ea5c2059591.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1977-362d266d-058f-460c-bb25-7ea5c2059591.txn deleted file mode 100644 index 7901ed9f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1977-362d266d-058f-460c-bb25-7ea5c2059591.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1978-ee48375e-df98-4331-b233-a42c0c407960.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1978-ee48375e-df98-4331-b233-a42c0c407960.txn deleted file mode 100644 index 62d7db3d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1978-ee48375e-df98-4331-b233-a42c0c407960.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1979-9e725d7f-2301-498b-a1e8-2d40139bfac7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1979-9e725d7f-2301-498b-a1e8-2d40139bfac7.txn deleted file mode 100644 index 3af1e1889..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1979-9e725d7f-2301-498b-a1e8-2d40139bfac7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/198-3d5bf8ac-8d29-4f68-bb61-a3ecc0156821.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/198-3d5bf8ac-8d29-4f68-bb61-a3ecc0156821.txn deleted file mode 100644 index c071ccc47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/198-3d5bf8ac-8d29-4f68-bb61-a3ecc0156821.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1980-775ea62f-a82b-4eea-8c3f-6c126330756c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1980-775ea62f-a82b-4eea-8c3f-6c126330756c.txn deleted file mode 100644 index 0ec2d31b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1980-775ea62f-a82b-4eea-8c3f-6c126330756c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1981-0fd6e94b-06c9-4819-9b42-5a4bc5aafafd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1981-0fd6e94b-06c9-4819-9b42-5a4bc5aafafd.txn deleted file mode 100644 index f6b5efe5d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1981-0fd6e94b-06c9-4819-9b42-5a4bc5aafafd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1982-6f34080e-eea8-4b62-8b9d-29c284cd62ab.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1982-6f34080e-eea8-4b62-8b9d-29c284cd62ab.txn deleted file mode 100644 index 516548b22..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1982-6f34080e-eea8-4b62-8b9d-29c284cd62ab.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1983-3e16576a-3a72-43da-ab7b-708fb4949aa0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1983-3e16576a-3a72-43da-ab7b-708fb4949aa0.txn deleted file mode 100644 index 78d646f8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1983-3e16576a-3a72-43da-ab7b-708fb4949aa0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1984-97c28e2c-ae2d-4047-a40d-d26cec934153.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1984-97c28e2c-ae2d-4047-a40d-d26cec934153.txn deleted file mode 100644 index f747890cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1984-97c28e2c-ae2d-4047-a40d-d26cec934153.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1985-5f9b7083-5367-4bef-86cc-7b2322bdb40f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1985-5f9b7083-5367-4bef-86cc-7b2322bdb40f.txn deleted file mode 100644 index ca26c1254..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1985-5f9b7083-5367-4bef-86cc-7b2322bdb40f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1986-93616816-310e-4da5-a801-d20913dd48a0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1986-93616816-310e-4da5-a801-d20913dd48a0.txn deleted file mode 100644 index bd4b9521d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1986-93616816-310e-4da5-a801-d20913dd48a0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1987-1d75d23c-f924-4709-b460-e00b1fd5bb89.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1987-1d75d23c-f924-4709-b460-e00b1fd5bb89.txn deleted file mode 100644 index e04a6850e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1987-1d75d23c-f924-4709-b460-e00b1fd5bb89.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1988-459adbc1-cd85-4001-aa41-fc686f92d239.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1988-459adbc1-cd85-4001-aa41-fc686f92d239.txn deleted file mode 100644 index 7892db5ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1988-459adbc1-cd85-4001-aa41-fc686f92d239.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1989-d02699e1-e961-4992-8baf-708d6362cd5d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1989-d02699e1-e961-4992-8baf-708d6362cd5d.txn deleted file mode 100644 index 29f7ba0c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1989-d02699e1-e961-4992-8baf-708d6362cd5d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/199-cc6e34cd-0278-4436-ac95-47f8e7651f07.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/199-cc6e34cd-0278-4436-ac95-47f8e7651f07.txn deleted file mode 100644 index f7a320da8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/199-cc6e34cd-0278-4436-ac95-47f8e7651f07.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1990-68c686fe-5342-4b25-8a3c-a36174695dc4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1990-68c686fe-5342-4b25-8a3c-a36174695dc4.txn deleted file mode 100644 index 884ef7319..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1990-68c686fe-5342-4b25-8a3c-a36174695dc4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1991-e8f06f08-22ee-45e4-a232-412d3ecce330.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1991-e8f06f08-22ee-45e4-a232-412d3ecce330.txn deleted file mode 100644 index 2d3495e20..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1991-e8f06f08-22ee-45e4-a232-412d3ecce330.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1992-a3829020-eb43-485f-9fa6-678a33bfef49.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1992-a3829020-eb43-485f-9fa6-678a33bfef49.txn deleted file mode 100644 index f394894f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1992-a3829020-eb43-485f-9fa6-678a33bfef49.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1993-6d879a17-1f09-4be1-a6af-70b5df4645e6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1993-6d879a17-1f09-4be1-a6af-70b5df4645e6.txn deleted file mode 100644 index 9ccf8e459..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1993-6d879a17-1f09-4be1-a6af-70b5df4645e6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1994-d0169250-01e4-4b28-85f5-c419d987807c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1994-d0169250-01e4-4b28-85f5-c419d987807c.txn deleted file mode 100644 index f07af345f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1994-d0169250-01e4-4b28-85f5-c419d987807c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1995-ba5b5015-1ca9-47f9-b8fe-6360e203f2c9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1995-ba5b5015-1ca9-47f9-b8fe-6360e203f2c9.txn deleted file mode 100644 index d7af3b5e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1995-ba5b5015-1ca9-47f9-b8fe-6360e203f2c9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1996-a03aaf06-eea2-450e-9f3b-513079f90a29.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1996-a03aaf06-eea2-450e-9f3b-513079f90a29.txn deleted file mode 100644 index 86518854f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1996-a03aaf06-eea2-450e-9f3b-513079f90a29.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1997-a119e316-6566-4fa8-a0f9-610795ff91bc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1997-a119e316-6566-4fa8-a0f9-610795ff91bc.txn deleted file mode 100644 index 373bb7dbd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1997-a119e316-6566-4fa8-a0f9-610795ff91bc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1998-ba42768c-9142-4928-9152-7729aa8ef5b6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1998-ba42768c-9142-4928-9152-7729aa8ef5b6.txn deleted file mode 100644 index 7092db86f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1998-ba42768c-9142-4928-9152-7729aa8ef5b6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1999-5660e5ad-6404-4070-9c5a-f72415f85e7c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1999-5660e5ad-6404-4070-9c5a-f72415f85e7c.txn deleted file mode 100644 index ad767749c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/1999-5660e5ad-6404-4070-9c5a-f72415f85e7c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2-00b92763-51dd-441d-a7cb-19810b960b50.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2-00b92763-51dd-441d-a7cb-19810b960b50.txn deleted file mode 100644 index ed90512e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2-00b92763-51dd-441d-a7cb-19810b960b50.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/20-8bcb9833-8e94-43ac-a7f3-aa2786e8fd8a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/20-8bcb9833-8e94-43ac-a7f3-aa2786e8fd8a.txn deleted file mode 100644 index 6d85ff7f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/20-8bcb9833-8e94-43ac-a7f3-aa2786e8fd8a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/200-599b06c1-eb7f-4a72-9793-77b8d7e98b26.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/200-599b06c1-eb7f-4a72-9793-77b8d7e98b26.txn deleted file mode 100644 index 9695214a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/200-599b06c1-eb7f-4a72-9793-77b8d7e98b26.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2000-9cb2d667-6d89-4e78-a745-34951b28ee5f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2000-9cb2d667-6d89-4e78-a745-34951b28ee5f.txn deleted file mode 100644 index e51e1a39e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2000-9cb2d667-6d89-4e78-a745-34951b28ee5f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2001-fccb566a-619a-40b7-9d5d-81980903f933.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2001-fccb566a-619a-40b7-9d5d-81980903f933.txn deleted file mode 100644 index 5cc532e0a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2001-fccb566a-619a-40b7-9d5d-81980903f933.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2002-265d8ea0-cb34-4aa6-81af-6ddbca9af89f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2002-265d8ea0-cb34-4aa6-81af-6ddbca9af89f.txn deleted file mode 100644 index d56197a39..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2002-265d8ea0-cb34-4aa6-81af-6ddbca9af89f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2003-6e9f0f92-4967-48f6-b8ed-80d308fec442.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2003-6e9f0f92-4967-48f6-b8ed-80d308fec442.txn deleted file mode 100644 index ba9fce70f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2003-6e9f0f92-4967-48f6-b8ed-80d308fec442.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2004-759ce460-347d-4273-8e42-ccb5561f7285.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2004-759ce460-347d-4273-8e42-ccb5561f7285.txn deleted file mode 100644 index 63f85cece..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2004-759ce460-347d-4273-8e42-ccb5561f7285.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2005-fe8dacb5-76a0-483c-88d1-9a4263b91481.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2005-fe8dacb5-76a0-483c-88d1-9a4263b91481.txn deleted file mode 100644 index f1f238e1e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2005-fe8dacb5-76a0-483c-88d1-9a4263b91481.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2006-dff386d9-b897-4b5c-b9a0-871e9523e297.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2006-dff386d9-b897-4b5c-b9a0-871e9523e297.txn deleted file mode 100644 index 84e828ceb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2006-dff386d9-b897-4b5c-b9a0-871e9523e297.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2007-74d79f1c-bb0a-4209-bd35-14c11d6466f0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2007-74d79f1c-bb0a-4209-bd35-14c11d6466f0.txn deleted file mode 100644 index 80c85adae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2007-74d79f1c-bb0a-4209-bd35-14c11d6466f0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2008-c9055491-b0c5-4cc1-b5cd-9a54c456eac3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2008-c9055491-b0c5-4cc1-b5cd-9a54c456eac3.txn deleted file mode 100644 index e5818955f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2008-c9055491-b0c5-4cc1-b5cd-9a54c456eac3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2009-6b617520-234b-41c6-8081-f4edfed574b4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2009-6b617520-234b-41c6-8081-f4edfed574b4.txn deleted file mode 100644 index 3bfed8036..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2009-6b617520-234b-41c6-8081-f4edfed574b4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/201-34302f69-ab13-4556-8cb2-fd16e63eb845.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/201-34302f69-ab13-4556-8cb2-fd16e63eb845.txn deleted file mode 100644 index 53de0fcc8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/201-34302f69-ab13-4556-8cb2-fd16e63eb845.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2010-7505bc98-1b59-46fe-aec4-b4e3adf79487.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2010-7505bc98-1b59-46fe-aec4-b4e3adf79487.txn deleted file mode 100644 index 5aaee1b3f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2010-7505bc98-1b59-46fe-aec4-b4e3adf79487.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2011-a6dc8ede-3536-473d-bb03-d5d8fad22b4d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2011-a6dc8ede-3536-473d-bb03-d5d8fad22b4d.txn deleted file mode 100644 index b54725762..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2011-a6dc8ede-3536-473d-bb03-d5d8fad22b4d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2012-f3b8585b-4577-466f-83de-4ced6fe9d03f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2012-f3b8585b-4577-466f-83de-4ced6fe9d03f.txn deleted file mode 100644 index 9da7dd95e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2012-f3b8585b-4577-466f-83de-4ced6fe9d03f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2013-23c01737-240e-4031-8f5a-48d0ee29dfb2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2013-23c01737-240e-4031-8f5a-48d0ee29dfb2.txn deleted file mode 100644 index e799565d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2013-23c01737-240e-4031-8f5a-48d0ee29dfb2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2014-e3967069-bfab-4c02-9801-e9f8b5130c7a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2014-e3967069-bfab-4c02-9801-e9f8b5130c7a.txn deleted file mode 100644 index e45648f10..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2014-e3967069-bfab-4c02-9801-e9f8b5130c7a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2015-5bda8132-1c19-45c8-a9f6-0a639628afb5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2015-5bda8132-1c19-45c8-a9f6-0a639628afb5.txn deleted file mode 100644 index a848847c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2015-5bda8132-1c19-45c8-a9f6-0a639628afb5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2016-4bd0180e-a3fe-43e5-b913-44e8c7a57422.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2016-4bd0180e-a3fe-43e5-b913-44e8c7a57422.txn deleted file mode 100644 index afd23b0c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2016-4bd0180e-a3fe-43e5-b913-44e8c7a57422.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2017-2f65c4e2-bc2c-45bb-bbb0-a10d7954dcfe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2017-2f65c4e2-bc2c-45bb-bbb0-a10d7954dcfe.txn deleted file mode 100644 index 79b523e0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2017-2f65c4e2-bc2c-45bb-bbb0-a10d7954dcfe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2018-8a33ae7f-1d20-4529-bf1a-77bede0a1c8e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2018-8a33ae7f-1d20-4529-bf1a-77bede0a1c8e.txn deleted file mode 100644 index 48e6a0034..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2018-8a33ae7f-1d20-4529-bf1a-77bede0a1c8e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2019-d2ea84e3-72a6-4d3e-b461-155658cdbf0d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2019-d2ea84e3-72a6-4d3e-b461-155658cdbf0d.txn deleted file mode 100644 index 69470f334..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2019-d2ea84e3-72a6-4d3e-b461-155658cdbf0d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/202-cf7b3fcb-097d-40bd-a846-29211deb5a83.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/202-cf7b3fcb-097d-40bd-a846-29211deb5a83.txn deleted file mode 100644 index 437887c2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/202-cf7b3fcb-097d-40bd-a846-29211deb5a83.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2020-e7ba24cf-48a9-420c-8896-ecd79ce2c769.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2020-e7ba24cf-48a9-420c-8896-ecd79ce2c769.txn deleted file mode 100644 index 15d247a4b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2020-e7ba24cf-48a9-420c-8896-ecd79ce2c769.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2021-05114acf-d814-4691-acfb-f7e367648845.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2021-05114acf-d814-4691-acfb-f7e367648845.txn deleted file mode 100644 index 72181af1d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2021-05114acf-d814-4691-acfb-f7e367648845.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2022-5f36bb3e-4dd0-4b3a-a2c3-d00e4e961922.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2022-5f36bb3e-4dd0-4b3a-a2c3-d00e4e961922.txn deleted file mode 100644 index c91adb74d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2022-5f36bb3e-4dd0-4b3a-a2c3-d00e4e961922.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2023-50121a69-dd97-4c7e-aac5-bf9c7b722c61.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2023-50121a69-dd97-4c7e-aac5-bf9c7b722c61.txn deleted file mode 100644 index 6cb772bc5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2023-50121a69-dd97-4c7e-aac5-bf9c7b722c61.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2024-74f7cd4b-94e5-43b9-98d7-9e886eeac92b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2024-74f7cd4b-94e5-43b9-98d7-9e886eeac92b.txn deleted file mode 100644 index 98e391724..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2024-74f7cd4b-94e5-43b9-98d7-9e886eeac92b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2025-7aa56cdf-b6d9-4242-a833-927c3a1b533e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2025-7aa56cdf-b6d9-4242-a833-927c3a1b533e.txn deleted file mode 100644 index 6f73795ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2025-7aa56cdf-b6d9-4242-a833-927c3a1b533e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2026-f62db449-3798-40c2-be9a-5034d678e22a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2026-f62db449-3798-40c2-be9a-5034d678e22a.txn deleted file mode 100644 index f71f71656..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2026-f62db449-3798-40c2-be9a-5034d678e22a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2027-4d0f7288-f6be-4e7a-9f23-782c64f61a7e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2027-4d0f7288-f6be-4e7a-9f23-782c64f61a7e.txn deleted file mode 100644 index 3a129c3b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2027-4d0f7288-f6be-4e7a-9f23-782c64f61a7e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2028-762a64c3-c07c-4b7d-9864-3df624df3632.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2028-762a64c3-c07c-4b7d-9864-3df624df3632.txn deleted file mode 100644 index 68db93b1f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2028-762a64c3-c07c-4b7d-9864-3df624df3632.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2029-886bf096-145f-4ede-84e9-893d9dcf2a6d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2029-886bf096-145f-4ede-84e9-893d9dcf2a6d.txn deleted file mode 100644 index 89308677c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2029-886bf096-145f-4ede-84e9-893d9dcf2a6d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/203-ad1bcfa5-933e-4fcb-8b58-15299819268d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/203-ad1bcfa5-933e-4fcb-8b58-15299819268d.txn deleted file mode 100644 index a8a6e4382..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/203-ad1bcfa5-933e-4fcb-8b58-15299819268d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2030-56311af5-dc2c-45a4-a925-fde24ef9c8ac.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2030-56311af5-dc2c-45a4-a925-fde24ef9c8ac.txn deleted file mode 100644 index 74f9f09fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2030-56311af5-dc2c-45a4-a925-fde24ef9c8ac.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2031-0f8b231c-bb9d-40e3-a29a-49f655d6df05.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2031-0f8b231c-bb9d-40e3-a29a-49f655d6df05.txn deleted file mode 100644 index 1fdf5e655..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2031-0f8b231c-bb9d-40e3-a29a-49f655d6df05.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2032-09d13002-b60f-4994-befc-3b9650e87bde.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2032-09d13002-b60f-4994-befc-3b9650e87bde.txn deleted file mode 100644 index 8b4fd1452..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2032-09d13002-b60f-4994-befc-3b9650e87bde.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2033-536b83c0-9d0e-423c-9377-72811657e66d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2033-536b83c0-9d0e-423c-9377-72811657e66d.txn deleted file mode 100644 index 1f85fa0af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2033-536b83c0-9d0e-423c-9377-72811657e66d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2034-82702893-b774-4ce1-9aca-86ef9c1968c4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2034-82702893-b774-4ce1-9aca-86ef9c1968c4.txn deleted file mode 100644 index 61bd7962c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2034-82702893-b774-4ce1-9aca-86ef9c1968c4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2035-31c7bdec-ac41-48a8-ba76-5082801e89f0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2035-31c7bdec-ac41-48a8-ba76-5082801e89f0.txn deleted file mode 100644 index 2b472c886..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2035-31c7bdec-ac41-48a8-ba76-5082801e89f0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2036-afd36c40-b1ad-400b-a866-1f25d296f1e5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2036-afd36c40-b1ad-400b-a866-1f25d296f1e5.txn deleted file mode 100644 index dc32028b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2036-afd36c40-b1ad-400b-a866-1f25d296f1e5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2037-080d08b6-18de-414f-9f7f-aa4073b89608.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2037-080d08b6-18de-414f-9f7f-aa4073b89608.txn deleted file mode 100644 index 2ce16da39..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2037-080d08b6-18de-414f-9f7f-aa4073b89608.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2038-94be606f-d586-48ff-88de-fb2e870c3ba4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2038-94be606f-d586-48ff-88de-fb2e870c3ba4.txn deleted file mode 100644 index 703e6909a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2038-94be606f-d586-48ff-88de-fb2e870c3ba4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2039-177ece32-713b-41e8-a9d0-62c34f5526f4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2039-177ece32-713b-41e8-a9d0-62c34f5526f4.txn deleted file mode 100644 index 9e979c0eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2039-177ece32-713b-41e8-a9d0-62c34f5526f4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/204-c76e53d1-1233-4173-baae-bbebb95b3cae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/204-c76e53d1-1233-4173-baae-bbebb95b3cae.txn deleted file mode 100644 index 084de51d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/204-c76e53d1-1233-4173-baae-bbebb95b3cae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2040-d1250a82-9a17-4e6d-8c87-92839dc05383.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2040-d1250a82-9a17-4e6d-8c87-92839dc05383.txn deleted file mode 100644 index f9c121700..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2040-d1250a82-9a17-4e6d-8c87-92839dc05383.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2041-8e633043-b58f-4262-9d1c-aaf7c2585b29.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2041-8e633043-b58f-4262-9d1c-aaf7c2585b29.txn deleted file mode 100644 index d76513ab1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2041-8e633043-b58f-4262-9d1c-aaf7c2585b29.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2042-dd4857f5-955f-4bea-b05e-988a06c8d4db.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2042-dd4857f5-955f-4bea-b05e-988a06c8d4db.txn deleted file mode 100644 index 65a7e7439..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2042-dd4857f5-955f-4bea-b05e-988a06c8d4db.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2043-03cb6ff9-ca50-4c6f-a77a-7dd1dc47246b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2043-03cb6ff9-ca50-4c6f-a77a-7dd1dc47246b.txn deleted file mode 100644 index 17c3178d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2043-03cb6ff9-ca50-4c6f-a77a-7dd1dc47246b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2044-9672a8b4-613c-422e-9677-46475d210375.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2044-9672a8b4-613c-422e-9677-46475d210375.txn deleted file mode 100644 index 585115bd5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2044-9672a8b4-613c-422e-9677-46475d210375.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2045-b2db8093-cd4f-4c86-b80a-d976aa41af6e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2045-b2db8093-cd4f-4c86-b80a-d976aa41af6e.txn deleted file mode 100644 index c1a4ccb3a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2045-b2db8093-cd4f-4c86-b80a-d976aa41af6e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2046-f29850c2-3e8c-47ad-87a6-c9a7ec38434f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2046-f29850c2-3e8c-47ad-87a6-c9a7ec38434f.txn deleted file mode 100644 index c60d39bb8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2046-f29850c2-3e8c-47ad-87a6-c9a7ec38434f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2047-7544ae46-baae-4c6e-8e3e-6b068db9076a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2047-7544ae46-baae-4c6e-8e3e-6b068db9076a.txn deleted file mode 100644 index b808eb206..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2047-7544ae46-baae-4c6e-8e3e-6b068db9076a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2048-857b35e9-5e1f-4026-a310-145e3da37900.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2048-857b35e9-5e1f-4026-a310-145e3da37900.txn deleted file mode 100644 index e0343fc0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2048-857b35e9-5e1f-4026-a310-145e3da37900.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2049-9a7a2cfe-29be-436e-b63f-155451dd302f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2049-9a7a2cfe-29be-436e-b63f-155451dd302f.txn deleted file mode 100644 index 7fcc4467b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2049-9a7a2cfe-29be-436e-b63f-155451dd302f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/205-02776062-252e-4030-b8d2-dc0e65b293a8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/205-02776062-252e-4030-b8d2-dc0e65b293a8.txn deleted file mode 100644 index 609ff952f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/205-02776062-252e-4030-b8d2-dc0e65b293a8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2050-ece302f9-d1a8-41ab-93c0-477f41e187ab.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2050-ece302f9-d1a8-41ab-93c0-477f41e187ab.txn deleted file mode 100644 index 0e9a0af9a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2050-ece302f9-d1a8-41ab-93c0-477f41e187ab.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2051-a2c991f1-7a63-4f3f-912a-36fc0f082c5e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2051-a2c991f1-7a63-4f3f-912a-36fc0f082c5e.txn deleted file mode 100644 index cc3fce54e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2051-a2c991f1-7a63-4f3f-912a-36fc0f082c5e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2052-e21354f1-c235-4099-a3b9-3ee8dfd0f71c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2052-e21354f1-c235-4099-a3b9-3ee8dfd0f71c.txn deleted file mode 100644 index e0e229969..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2052-e21354f1-c235-4099-a3b9-3ee8dfd0f71c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2053-56dd2aa7-b499-4182-9f7d-e633b2949cb7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2053-56dd2aa7-b499-4182-9f7d-e633b2949cb7.txn deleted file mode 100644 index e1b8ee624..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2053-56dd2aa7-b499-4182-9f7d-e633b2949cb7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2054-e9a533e1-8aa9-4e2b-8dfd-cc07aaf3a7e9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2054-e9a533e1-8aa9-4e2b-8dfd-cc07aaf3a7e9.txn deleted file mode 100644 index c6932ffdb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2054-e9a533e1-8aa9-4e2b-8dfd-cc07aaf3a7e9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2055-28426a55-e49c-4a59-a90e-55c531d43218.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2055-28426a55-e49c-4a59-a90e-55c531d43218.txn deleted file mode 100644 index 19f07aedc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2055-28426a55-e49c-4a59-a90e-55c531d43218.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2056-ccd500f4-5178-48dc-88dd-6a60a1ebcfb8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2056-ccd500f4-5178-48dc-88dd-6a60a1ebcfb8.txn deleted file mode 100644 index e9de5f911..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2056-ccd500f4-5178-48dc-88dd-6a60a1ebcfb8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2057-6045e3aa-1c21-4ed4-b65e-a2810bd48258.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2057-6045e3aa-1c21-4ed4-b65e-a2810bd48258.txn deleted file mode 100644 index 14e84d71d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2057-6045e3aa-1c21-4ed4-b65e-a2810bd48258.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2058-46e338ea-5a46-4372-b18d-b2b6e93563fe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2058-46e338ea-5a46-4372-b18d-b2b6e93563fe.txn deleted file mode 100644 index a6f6804de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2058-46e338ea-5a46-4372-b18d-b2b6e93563fe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2059-3236b14c-723a-4440-bbbb-204dc6887595.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2059-3236b14c-723a-4440-bbbb-204dc6887595.txn deleted file mode 100644 index ea5fc12e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2059-3236b14c-723a-4440-bbbb-204dc6887595.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/206-68f328c9-586d-4ecd-8d4f-702a99db0f37.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/206-68f328c9-586d-4ecd-8d4f-702a99db0f37.txn deleted file mode 100644 index 45478f815..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/206-68f328c9-586d-4ecd-8d4f-702a99db0f37.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2060-4a0b1d50-e60e-4e30-aaa3-9022b821e45e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2060-4a0b1d50-e60e-4e30-aaa3-9022b821e45e.txn deleted file mode 100644 index b82d4cf84..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2060-4a0b1d50-e60e-4e30-aaa3-9022b821e45e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2061-a3aebcf8-9d50-44fb-b671-777e7d8c23b8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2061-a3aebcf8-9d50-44fb-b671-777e7d8c23b8.txn deleted file mode 100644 index 806ea044b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2061-a3aebcf8-9d50-44fb-b671-777e7d8c23b8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2062-5781eb33-45e7-4cf1-b5db-8321ab892de6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2062-5781eb33-45e7-4cf1-b5db-8321ab892de6.txn deleted file mode 100644 index 9fb5c2702..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2062-5781eb33-45e7-4cf1-b5db-8321ab892de6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2063-db463e17-f87c-428c-9c59-ccada66be216.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2063-db463e17-f87c-428c-9c59-ccada66be216.txn deleted file mode 100644 index 47caa2942..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2063-db463e17-f87c-428c-9c59-ccada66be216.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2064-4dda37d2-fba7-472f-82a1-7b67b4b50f75.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2064-4dda37d2-fba7-472f-82a1-7b67b4b50f75.txn deleted file mode 100644 index 08c3ddc1f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2064-4dda37d2-fba7-472f-82a1-7b67b4b50f75.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2065-1cb87218-9782-4c94-b63e-ec2635baa4ba.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2065-1cb87218-9782-4c94-b63e-ec2635baa4ba.txn deleted file mode 100644 index daa01820e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2065-1cb87218-9782-4c94-b63e-ec2635baa4ba.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2066-2957b30d-64bb-4943-bb37-d335795969c8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2066-2957b30d-64bb-4943-bb37-d335795969c8.txn deleted file mode 100644 index 677021fd2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2066-2957b30d-64bb-4943-bb37-d335795969c8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2067-4446b77e-a018-4f1e-b7ce-2a94ac39ba9e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2067-4446b77e-a018-4f1e-b7ce-2a94ac39ba9e.txn deleted file mode 100644 index 7fe346757..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2067-4446b77e-a018-4f1e-b7ce-2a94ac39ba9e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2068-d054f3e0-9c1e-4e66-9cdf-0627d4315242.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2068-d054f3e0-9c1e-4e66-9cdf-0627d4315242.txn deleted file mode 100644 index 234ea21b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2068-d054f3e0-9c1e-4e66-9cdf-0627d4315242.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2069-ad4696e1-9292-438d-8e96-8a9559db565d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2069-ad4696e1-9292-438d-8e96-8a9559db565d.txn deleted file mode 100644 index 7a86105ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2069-ad4696e1-9292-438d-8e96-8a9559db565d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/207-aa2cbb8e-3da1-46c7-a874-6f6d0210d224.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/207-aa2cbb8e-3da1-46c7-a874-6f6d0210d224.txn deleted file mode 100644 index 6623ff294..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/207-aa2cbb8e-3da1-46c7-a874-6f6d0210d224.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2070-ffb7e611-e716-431c-b29e-e79e1fdd2a77.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2070-ffb7e611-e716-431c-b29e-e79e1fdd2a77.txn deleted file mode 100644 index 4e7cccfb6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2070-ffb7e611-e716-431c-b29e-e79e1fdd2a77.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2071-de0972fc-2663-4272-bb22-19d789df8714.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2071-de0972fc-2663-4272-bb22-19d789df8714.txn deleted file mode 100644 index fb605102f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2071-de0972fc-2663-4272-bb22-19d789df8714.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2072-28045d8a-bbb1-46b2-a214-9a877d4ffee4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2072-28045d8a-bbb1-46b2-a214-9a877d4ffee4.txn deleted file mode 100644 index 150a0fa8d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2072-28045d8a-bbb1-46b2-a214-9a877d4ffee4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2073-b36f96d1-d101-4300-91a4-1d8105f06bac.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2073-b36f96d1-d101-4300-91a4-1d8105f06bac.txn deleted file mode 100644 index 63365b833..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2073-b36f96d1-d101-4300-91a4-1d8105f06bac.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2074-ff80af49-ff30-421b-b29a-947309261bbf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2074-ff80af49-ff30-421b-b29a-947309261bbf.txn deleted file mode 100644 index 1fbd6f1d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2074-ff80af49-ff30-421b-b29a-947309261bbf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2075-bdadabcd-4d8f-41f0-9471-a8656b5ea2d6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2075-bdadabcd-4d8f-41f0-9471-a8656b5ea2d6.txn deleted file mode 100644 index af79a054a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2075-bdadabcd-4d8f-41f0-9471-a8656b5ea2d6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2076-c8295e7b-edc0-45ee-8954-dcefe390d4d4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2076-c8295e7b-edc0-45ee-8954-dcefe390d4d4.txn deleted file mode 100644 index 7639a5e68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2076-c8295e7b-edc0-45ee-8954-dcefe390d4d4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2077-3c484d52-febf-41ff-9f4f-5d6c3f3abecf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2077-3c484d52-febf-41ff-9f4f-5d6c3f3abecf.txn deleted file mode 100644 index 237b1b8e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2077-3c484d52-febf-41ff-9f4f-5d6c3f3abecf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2078-9e895e8c-7f90-419d-b4d0-2c7d6a56b10e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2078-9e895e8c-7f90-419d-b4d0-2c7d6a56b10e.txn deleted file mode 100644 index 955fc54ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2078-9e895e8c-7f90-419d-b4d0-2c7d6a56b10e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2079-c25bdba4-12e3-4256-8b73-bad18aeffd98.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2079-c25bdba4-12e3-4256-8b73-bad18aeffd98.txn deleted file mode 100644 index f74718331..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2079-c25bdba4-12e3-4256-8b73-bad18aeffd98.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/208-60a20156-0ae8-4a8b-b32b-ba9335c712df.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/208-60a20156-0ae8-4a8b-b32b-ba9335c712df.txn deleted file mode 100644 index af5101bad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/208-60a20156-0ae8-4a8b-b32b-ba9335c712df.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2080-8726c5e3-dd31-46dc-966c-477c3e02ab1b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2080-8726c5e3-dd31-46dc-966c-477c3e02ab1b.txn deleted file mode 100644 index 85b68c4a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2080-8726c5e3-dd31-46dc-966c-477c3e02ab1b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2081-4dbfde1d-1b45-4260-ae64-9fc76c175e75.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2081-4dbfde1d-1b45-4260-ae64-9fc76c175e75.txn deleted file mode 100644 index f9b38d97a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2081-4dbfde1d-1b45-4260-ae64-9fc76c175e75.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2082-39ce122a-802a-41be-9006-4e246fe83c39.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2082-39ce122a-802a-41be-9006-4e246fe83c39.txn deleted file mode 100644 index 31d3b7c51..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2082-39ce122a-802a-41be-9006-4e246fe83c39.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2083-d151fb58-bc4d-4620-8f37-398b9703d227.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2083-d151fb58-bc4d-4620-8f37-398b9703d227.txn deleted file mode 100644 index ce2e387d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2083-d151fb58-bc4d-4620-8f37-398b9703d227.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2084-5cff01d9-6769-41d6-a357-4355da4b7245.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2084-5cff01d9-6769-41d6-a357-4355da4b7245.txn deleted file mode 100644 index 8dc43f557..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2084-5cff01d9-6769-41d6-a357-4355da4b7245.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2085-c13957ff-eb65-470e-b2ea-789b036a73a1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2085-c13957ff-eb65-470e-b2ea-789b036a73a1.txn deleted file mode 100644 index a219cf183..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2085-c13957ff-eb65-470e-b2ea-789b036a73a1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2086-420818bb-7801-463b-985b-cd233c4f341a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2086-420818bb-7801-463b-985b-cd233c4f341a.txn deleted file mode 100644 index 8982ded68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2086-420818bb-7801-463b-985b-cd233c4f341a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2087-21310c02-b6ae-47c6-ad47-a9d04aaa564c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2087-21310c02-b6ae-47c6-ad47-a9d04aaa564c.txn deleted file mode 100644 index 4204d33ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2087-21310c02-b6ae-47c6-ad47-a9d04aaa564c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2088-c634150a-b394-4c11-98ff-957a4df55db4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2088-c634150a-b394-4c11-98ff-957a4df55db4.txn deleted file mode 100644 index e1b36cbcc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2088-c634150a-b394-4c11-98ff-957a4df55db4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2089-efd9e04c-fd69-4963-93cd-98809358692f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2089-efd9e04c-fd69-4963-93cd-98809358692f.txn deleted file mode 100644 index 911afa06c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2089-efd9e04c-fd69-4963-93cd-98809358692f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/209-44dbb56b-a36a-4537-bb7a-695f9b6f6570.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/209-44dbb56b-a36a-4537-bb7a-695f9b6f6570.txn deleted file mode 100644 index fbc045020..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/209-44dbb56b-a36a-4537-bb7a-695f9b6f6570.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2090-1469fc0d-ff09-468f-b0d2-255338fffb19.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2090-1469fc0d-ff09-468f-b0d2-255338fffb19.txn deleted file mode 100644 index de08a383c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2090-1469fc0d-ff09-468f-b0d2-255338fffb19.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2091-e5e68f0a-31e8-4aae-879d-2c9443cebac2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2091-e5e68f0a-31e8-4aae-879d-2c9443cebac2.txn deleted file mode 100644 index 2a4147430..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2091-e5e68f0a-31e8-4aae-879d-2c9443cebac2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2092-5e025412-e952-4fa0-acbe-30d8e9891ee7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2092-5e025412-e952-4fa0-acbe-30d8e9891ee7.txn deleted file mode 100644 index 2d9a757ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2092-5e025412-e952-4fa0-acbe-30d8e9891ee7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2093-d01ff5ee-80a5-450c-934c-1849196c0a97.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2093-d01ff5ee-80a5-450c-934c-1849196c0a97.txn deleted file mode 100644 index 895c1aa71..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2093-d01ff5ee-80a5-450c-934c-1849196c0a97.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2094-b0807826-407d-4fc1-9efc-5a1451d085b9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2094-b0807826-407d-4fc1-9efc-5a1451d085b9.txn deleted file mode 100644 index 38be137e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2094-b0807826-407d-4fc1-9efc-5a1451d085b9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2095-3711669b-72c4-4657-b342-d54084984820.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2095-3711669b-72c4-4657-b342-d54084984820.txn deleted file mode 100644 index 68697cb8d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2095-3711669b-72c4-4657-b342-d54084984820.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2096-92be452e-60fa-4dfe-a061-25127091a053.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2096-92be452e-60fa-4dfe-a061-25127091a053.txn deleted file mode 100644 index 24ff93137..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2096-92be452e-60fa-4dfe-a061-25127091a053.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2097-4aa21484-2ccd-44a3-af75-4836326e3b41.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2097-4aa21484-2ccd-44a3-af75-4836326e3b41.txn deleted file mode 100644 index 101d5f5ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2097-4aa21484-2ccd-44a3-af75-4836326e3b41.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2098-ecae6a21-267c-4249-ad0a-5a8b6c720e39.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2098-ecae6a21-267c-4249-ad0a-5a8b6c720e39.txn deleted file mode 100644 index f0b601c3b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2098-ecae6a21-267c-4249-ad0a-5a8b6c720e39.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2099-972c88bd-8438-4342-ae3e-fe08aa4ed1b8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2099-972c88bd-8438-4342-ae3e-fe08aa4ed1b8.txn deleted file mode 100644 index 155439102..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2099-972c88bd-8438-4342-ae3e-fe08aa4ed1b8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/21-6f24b3ac-03a3-4334-8504-b312f1137c48.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/21-6f24b3ac-03a3-4334-8504-b312f1137c48.txn deleted file mode 100644 index f0ebb7911..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/21-6f24b3ac-03a3-4334-8504-b312f1137c48.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/210-15d04b13-bb25-419c-96c5-e460e94c2abb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/210-15d04b13-bb25-419c-96c5-e460e94c2abb.txn deleted file mode 100644 index e73bfa968..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/210-15d04b13-bb25-419c-96c5-e460e94c2abb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2100-d0f71596-03f0-4cc6-8422-3a40d4c71659.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2100-d0f71596-03f0-4cc6-8422-3a40d4c71659.txn deleted file mode 100644 index bf40780d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2100-d0f71596-03f0-4cc6-8422-3a40d4c71659.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2101-abe8a4b1-61f3-41d4-a0bd-9dec53f3f262.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2101-abe8a4b1-61f3-41d4-a0bd-9dec53f3f262.txn deleted file mode 100644 index a5dea947c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2101-abe8a4b1-61f3-41d4-a0bd-9dec53f3f262.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2102-cf6306ca-0798-4c3a-9552-a8f8b757f85b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2102-cf6306ca-0798-4c3a-9552-a8f8b757f85b.txn deleted file mode 100644 index 62c9c7148..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2102-cf6306ca-0798-4c3a-9552-a8f8b757f85b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2103-a33068f9-22ef-40e1-95d6-6d0c9328e248.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2103-a33068f9-22ef-40e1-95d6-6d0c9328e248.txn deleted file mode 100644 index edfd02a86..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2103-a33068f9-22ef-40e1-95d6-6d0c9328e248.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2104-b323824e-5232-4e11-bdca-85e2a4e75ddb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2104-b323824e-5232-4e11-bdca-85e2a4e75ddb.txn deleted file mode 100644 index dea84d5c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2104-b323824e-5232-4e11-bdca-85e2a4e75ddb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2105-c931bd25-d58f-455a-8afd-929efecf6eff.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2105-c931bd25-d58f-455a-8afd-929efecf6eff.txn deleted file mode 100644 index 3e31e37e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2105-c931bd25-d58f-455a-8afd-929efecf6eff.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2106-26e7d0e9-a635-4418-8484-cf8fe8cab5cb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2106-26e7d0e9-a635-4418-8484-cf8fe8cab5cb.txn deleted file mode 100644 index 628b3c594..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2106-26e7d0e9-a635-4418-8484-cf8fe8cab5cb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2107-258f65cb-ef74-43e5-b13d-7eb4923af0af.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2107-258f65cb-ef74-43e5-b13d-7eb4923af0af.txn deleted file mode 100644 index 01f27b235..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2107-258f65cb-ef74-43e5-b13d-7eb4923af0af.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2108-f396567f-a70b-41ce-87a6-770ab33a8f55.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2108-f396567f-a70b-41ce-87a6-770ab33a8f55.txn deleted file mode 100644 index 8644640b6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2108-f396567f-a70b-41ce-87a6-770ab33a8f55.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2109-e2e258ab-5edb-4bc5-bca2-37d86f8fe73c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2109-e2e258ab-5edb-4bc5-bca2-37d86f8fe73c.txn deleted file mode 100644 index 4834d4632..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2109-e2e258ab-5edb-4bc5-bca2-37d86f8fe73c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/211-eea10a0b-ee25-4606-855c-550fe58aa562.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/211-eea10a0b-ee25-4606-855c-550fe58aa562.txn deleted file mode 100644 index f2e0c5aa7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/211-eea10a0b-ee25-4606-855c-550fe58aa562.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2110-1f375f05-92a4-41ed-b81f-a90c198290ae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2110-1f375f05-92a4-41ed-b81f-a90c198290ae.txn deleted file mode 100644 index f7fb9638a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2110-1f375f05-92a4-41ed-b81f-a90c198290ae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2111-381a2239-2a48-484d-b33f-a47aeabd7caf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2111-381a2239-2a48-484d-b33f-a47aeabd7caf.txn deleted file mode 100644 index 55a25328a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2111-381a2239-2a48-484d-b33f-a47aeabd7caf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2112-4401cee8-f204-4dc0-b039-0877d132696b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2112-4401cee8-f204-4dc0-b039-0877d132696b.txn deleted file mode 100644 index 53c21caaf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2112-4401cee8-f204-4dc0-b039-0877d132696b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2113-1ffde82c-5571-4ab6-a747-1bcd22c3c2d9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2113-1ffde82c-5571-4ab6-a747-1bcd22c3c2d9.txn deleted file mode 100644 index fcc38a001..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2113-1ffde82c-5571-4ab6-a747-1bcd22c3c2d9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2114-7291d4eb-425e-4591-98a0-04cfd3d68864.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2114-7291d4eb-425e-4591-98a0-04cfd3d68864.txn deleted file mode 100644 index 4218e68d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2114-7291d4eb-425e-4591-98a0-04cfd3d68864.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2115-2f5bb1f8-eb19-4da5-baff-f3af397be8e2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2115-2f5bb1f8-eb19-4da5-baff-f3af397be8e2.txn deleted file mode 100644 index ec28e0d4d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2115-2f5bb1f8-eb19-4da5-baff-f3af397be8e2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2116-ae4fa813-1599-4c07-8036-f26eec1efb14.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2116-ae4fa813-1599-4c07-8036-f26eec1efb14.txn deleted file mode 100644 index 22089fd7d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2116-ae4fa813-1599-4c07-8036-f26eec1efb14.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2117-4fe18a10-a162-4aab-b5f6-e1a2773d5658.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2117-4fe18a10-a162-4aab-b5f6-e1a2773d5658.txn deleted file mode 100644 index 3c1c7aacb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2117-4fe18a10-a162-4aab-b5f6-e1a2773d5658.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2118-758ea561-508c-47c6-9505-18feea591e78.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2118-758ea561-508c-47c6-9505-18feea591e78.txn deleted file mode 100644 index 145451741..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2118-758ea561-508c-47c6-9505-18feea591e78.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2119-473d613b-014a-4d91-9524-f37cb9beaddb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2119-473d613b-014a-4d91-9524-f37cb9beaddb.txn deleted file mode 100644 index ba528cbb9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2119-473d613b-014a-4d91-9524-f37cb9beaddb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/212-4a9692e2-094d-46a1-b2f2-ba916c654bca.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/212-4a9692e2-094d-46a1-b2f2-ba916c654bca.txn deleted file mode 100644 index d79927897..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/212-4a9692e2-094d-46a1-b2f2-ba916c654bca.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2120-c9b6a8a6-f296-4f0a-868a-e8bab6ce4ff9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2120-c9b6a8a6-f296-4f0a-868a-e8bab6ce4ff9.txn deleted file mode 100644 index a2c2bfea6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2120-c9b6a8a6-f296-4f0a-868a-e8bab6ce4ff9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2121-e0ffc052-e409-48ab-8811-b180a898d38a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2121-e0ffc052-e409-48ab-8811-b180a898d38a.txn deleted file mode 100644 index 03de6ad69..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2121-e0ffc052-e409-48ab-8811-b180a898d38a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2122-0d385d72-1989-45e4-83cf-116641bee66b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2122-0d385d72-1989-45e4-83cf-116641bee66b.txn deleted file mode 100644 index 5ee859589..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2122-0d385d72-1989-45e4-83cf-116641bee66b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2123-9f12288a-e8aa-4b32-bdc6-e63065b25dda.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2123-9f12288a-e8aa-4b32-bdc6-e63065b25dda.txn deleted file mode 100644 index 7921c5412..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2123-9f12288a-e8aa-4b32-bdc6-e63065b25dda.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2124-ec081144-a8bf-473b-b651-5a0f44c49eea.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2124-ec081144-a8bf-473b-b651-5a0f44c49eea.txn deleted file mode 100644 index 27da8534e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2124-ec081144-a8bf-473b-b651-5a0f44c49eea.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2125-1be2d293-003b-4e69-8a33-f454bddaf6b0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2125-1be2d293-003b-4e69-8a33-f454bddaf6b0.txn deleted file mode 100644 index 5bb985ff7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2125-1be2d293-003b-4e69-8a33-f454bddaf6b0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2126-1d321a2a-9109-4b5f-afdb-1b0e32f021b8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2126-1d321a2a-9109-4b5f-afdb-1b0e32f021b8.txn deleted file mode 100644 index 416e44583..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2126-1d321a2a-9109-4b5f-afdb-1b0e32f021b8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2127-700bfde3-689f-4e60-ab7c-9bd5a4d2730e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2127-700bfde3-689f-4e60-ab7c-9bd5a4d2730e.txn deleted file mode 100644 index 3ff9cc265..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2127-700bfde3-689f-4e60-ab7c-9bd5a4d2730e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2128-c73bf9d0-3704-480c-ac4f-f2917689977d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2128-c73bf9d0-3704-480c-ac4f-f2917689977d.txn deleted file mode 100644 index 2db869e78..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2128-c73bf9d0-3704-480c-ac4f-f2917689977d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2129-d90ca1e2-f567-4b25-8ba5-63d7a7b27ea4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2129-d90ca1e2-f567-4b25-8ba5-63d7a7b27ea4.txn deleted file mode 100644 index 44e2fe24a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2129-d90ca1e2-f567-4b25-8ba5-63d7a7b27ea4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/213-28e6cf44-4356-4008-bfb9-26b364e49c8c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/213-28e6cf44-4356-4008-bfb9-26b364e49c8c.txn deleted file mode 100644 index e75b198f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/213-28e6cf44-4356-4008-bfb9-26b364e49c8c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2130-46367f5f-e406-4fb9-a652-6046624210e4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2130-46367f5f-e406-4fb9-a652-6046624210e4.txn deleted file mode 100644 index 2ec3b2dac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2130-46367f5f-e406-4fb9-a652-6046624210e4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2131-1651eacd-8272-4efb-a22e-a4aa627ea659.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2131-1651eacd-8272-4efb-a22e-a4aa627ea659.txn deleted file mode 100644 index 894219a09..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2131-1651eacd-8272-4efb-a22e-a4aa627ea659.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2132-1933a495-2026-4f27-a1c6-f599071f092f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2132-1933a495-2026-4f27-a1c6-f599071f092f.txn deleted file mode 100644 index 227602c02..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2132-1933a495-2026-4f27-a1c6-f599071f092f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2133-773bb491-c0b9-4882-91cf-86ca832a7639.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2133-773bb491-c0b9-4882-91cf-86ca832a7639.txn deleted file mode 100644 index a8ff9cea0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2133-773bb491-c0b9-4882-91cf-86ca832a7639.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2134-242eb9af-397e-40db-aa17-c339a1a44650.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2134-242eb9af-397e-40db-aa17-c339a1a44650.txn deleted file mode 100644 index 69e5e88d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2134-242eb9af-397e-40db-aa17-c339a1a44650.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2135-053c9c97-953d-460e-9279-1fdff1b6724d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2135-053c9c97-953d-460e-9279-1fdff1b6724d.txn deleted file mode 100644 index 62791bfeb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2135-053c9c97-953d-460e-9279-1fdff1b6724d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2136-b0c782c0-5ba5-4999-8904-32d994858579.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2136-b0c782c0-5ba5-4999-8904-32d994858579.txn deleted file mode 100644 index d742d1ce4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2136-b0c782c0-5ba5-4999-8904-32d994858579.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2137-b5152ac5-12fb-4c12-8e37-c1f9e86037a7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2137-b5152ac5-12fb-4c12-8e37-c1f9e86037a7.txn deleted file mode 100644 index ca65261d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2137-b5152ac5-12fb-4c12-8e37-c1f9e86037a7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2138-31b11fbd-046a-4930-91ba-24c00503ee78.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2138-31b11fbd-046a-4930-91ba-24c00503ee78.txn deleted file mode 100644 index 5ad5b5730..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2138-31b11fbd-046a-4930-91ba-24c00503ee78.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2139-b5bdf711-4cfe-442a-9480-903f8064f06a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2139-b5bdf711-4cfe-442a-9480-903f8064f06a.txn deleted file mode 100644 index 6866732f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2139-b5bdf711-4cfe-442a-9480-903f8064f06a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/214-7f9ac863-6126-4c14-918a-4339680a1657.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/214-7f9ac863-6126-4c14-918a-4339680a1657.txn deleted file mode 100644 index c6d635589..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/214-7f9ac863-6126-4c14-918a-4339680a1657.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2140-a7e2ddfe-f0e6-4ad2-9bfb-fdf027234d34.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2140-a7e2ddfe-f0e6-4ad2-9bfb-fdf027234d34.txn deleted file mode 100644 index bc1e8c7aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2140-a7e2ddfe-f0e6-4ad2-9bfb-fdf027234d34.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2141-128cacc9-b7e0-4047-9fda-e330afca1aa7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2141-128cacc9-b7e0-4047-9fda-e330afca1aa7.txn deleted file mode 100644 index 25dfa18b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2141-128cacc9-b7e0-4047-9fda-e330afca1aa7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2142-baa5a135-34ff-4aaf-8f13-727b9734345f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2142-baa5a135-34ff-4aaf-8f13-727b9734345f.txn deleted file mode 100644 index ef3874a90..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2142-baa5a135-34ff-4aaf-8f13-727b9734345f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2143-900d228c-68aa-4476-836d-2c77acecce3a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2143-900d228c-68aa-4476-836d-2c77acecce3a.txn deleted file mode 100644 index c67092e51..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2143-900d228c-68aa-4476-836d-2c77acecce3a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2144-a5965231-9b70-4f9d-b68a-45b8909fa99e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2144-a5965231-9b70-4f9d-b68a-45b8909fa99e.txn deleted file mode 100644 index 6e576762b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2144-a5965231-9b70-4f9d-b68a-45b8909fa99e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2145-5b3fd558-754e-467a-8c52-0b5144bc1fbe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2145-5b3fd558-754e-467a-8c52-0b5144bc1fbe.txn deleted file mode 100644 index a74f7c113..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2145-5b3fd558-754e-467a-8c52-0b5144bc1fbe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2146-bfd98fa2-3fb1-4113-95bb-e4af1127bed5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2146-bfd98fa2-3fb1-4113-95bb-e4af1127bed5.txn deleted file mode 100644 index a721dba64..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2146-bfd98fa2-3fb1-4113-95bb-e4af1127bed5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2147-54d2b61f-7226-4ea0-bcc4-8ea5bc8bcb4b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2147-54d2b61f-7226-4ea0-bcc4-8ea5bc8bcb4b.txn deleted file mode 100644 index 62f4b71c1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2147-54d2b61f-7226-4ea0-bcc4-8ea5bc8bcb4b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2148-d9ae8ade-220f-471b-a0f1-309677aea215.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2148-d9ae8ade-220f-471b-a0f1-309677aea215.txn deleted file mode 100644 index b2aa21432..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2148-d9ae8ade-220f-471b-a0f1-309677aea215.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2149-1a871957-9b98-4550-a3c8-c297e3b23aa1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2149-1a871957-9b98-4550-a3c8-c297e3b23aa1.txn deleted file mode 100644 index 7983a791e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2149-1a871957-9b98-4550-a3c8-c297e3b23aa1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/215-f0954482-da7f-40e8-bc5a-77c758a02e79.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/215-f0954482-da7f-40e8-bc5a-77c758a02e79.txn deleted file mode 100644 index 96b07a90b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/215-f0954482-da7f-40e8-bc5a-77c758a02e79.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2150-99fa2779-89a6-40a0-9d8b-2dc49dac58b7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2150-99fa2779-89a6-40a0-9d8b-2dc49dac58b7.txn deleted file mode 100644 index da25f3535..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2150-99fa2779-89a6-40a0-9d8b-2dc49dac58b7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2151-c9a36245-2102-4897-8966-f512db99a110.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2151-c9a36245-2102-4897-8966-f512db99a110.txn deleted file mode 100644 index dfd36dd3f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2151-c9a36245-2102-4897-8966-f512db99a110.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2152-4c80ca7f-c5ae-4499-be74-d3288957c669.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2152-4c80ca7f-c5ae-4499-be74-d3288957c669.txn deleted file mode 100644 index 19f94f07f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2152-4c80ca7f-c5ae-4499-be74-d3288957c669.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2153-374d0ff3-dd05-490e-954c-79837b58f735.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2153-374d0ff3-dd05-490e-954c-79837b58f735.txn deleted file mode 100644 index 612916f37..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2153-374d0ff3-dd05-490e-954c-79837b58f735.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2154-50a7271d-e9a6-4050-96e0-e11ed375772a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2154-50a7271d-e9a6-4050-96e0-e11ed375772a.txn deleted file mode 100644 index d7f01a397..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2154-50a7271d-e9a6-4050-96e0-e11ed375772a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2155-c2547c37-1775-42ff-843f-2c3a9552029f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2155-c2547c37-1775-42ff-843f-2c3a9552029f.txn deleted file mode 100644 index 35e5560fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2155-c2547c37-1775-42ff-843f-2c3a9552029f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2156-c2572ae4-7834-4774-ad50-dc9ebc6f99f3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2156-c2572ae4-7834-4774-ad50-dc9ebc6f99f3.txn deleted file mode 100644 index d5b3a43ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2156-c2572ae4-7834-4774-ad50-dc9ebc6f99f3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2157-c79deb1d-3b56-46da-bba2-a6c57d2fd318.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2157-c79deb1d-3b56-46da-bba2-a6c57d2fd318.txn deleted file mode 100644 index bcfece661..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2157-c79deb1d-3b56-46da-bba2-a6c57d2fd318.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2158-59f2e0ba-eeed-43a0-936b-c63e77f16d90.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2158-59f2e0ba-eeed-43a0-936b-c63e77f16d90.txn deleted file mode 100644 index c664a2427..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2158-59f2e0ba-eeed-43a0-936b-c63e77f16d90.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2159-fe4fced1-07be-4a21-8f88-dbeb77ddbd1f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2159-fe4fced1-07be-4a21-8f88-dbeb77ddbd1f.txn deleted file mode 100644 index 39c65ce60..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2159-fe4fced1-07be-4a21-8f88-dbeb77ddbd1f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/216-c934b201-eeef-4046-b105-408197fe3325.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/216-c934b201-eeef-4046-b105-408197fe3325.txn deleted file mode 100644 index 5425ffdef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/216-c934b201-eeef-4046-b105-408197fe3325.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2160-db74015f-8480-4cbf-aefa-9bea635c98d4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2160-db74015f-8480-4cbf-aefa-9bea635c98d4.txn deleted file mode 100644 index 66910df1b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2160-db74015f-8480-4cbf-aefa-9bea635c98d4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2161-76b1db7a-f0f6-4002-819e-00ec6d361ac4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2161-76b1db7a-f0f6-4002-819e-00ec6d361ac4.txn deleted file mode 100644 index 7ca638bfa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2161-76b1db7a-f0f6-4002-819e-00ec6d361ac4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2162-0484a78c-9010-4416-bfa3-eac1e7ba4fc5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2162-0484a78c-9010-4416-bfa3-eac1e7ba4fc5.txn deleted file mode 100644 index a09332b81..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2162-0484a78c-9010-4416-bfa3-eac1e7ba4fc5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2163-6540fc0b-cfa0-46e0-8e02-7ac5d9691504.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2163-6540fc0b-cfa0-46e0-8e02-7ac5d9691504.txn deleted file mode 100644 index 7d20ec0aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2163-6540fc0b-cfa0-46e0-8e02-7ac5d9691504.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2164-c690b3c1-0eaa-4532-bd6e-37ac5e2ef9dd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2164-c690b3c1-0eaa-4532-bd6e-37ac5e2ef9dd.txn deleted file mode 100644 index 53822f9c1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2164-c690b3c1-0eaa-4532-bd6e-37ac5e2ef9dd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2165-eef46109-7f73-4ca7-b791-02ffa32db98d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2165-eef46109-7f73-4ca7-b791-02ffa32db98d.txn deleted file mode 100644 index 3fec44ae5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2165-eef46109-7f73-4ca7-b791-02ffa32db98d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2166-52ae9492-db1d-4322-8fd7-74758c9747e5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2166-52ae9492-db1d-4322-8fd7-74758c9747e5.txn deleted file mode 100644 index edd6b756b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2166-52ae9492-db1d-4322-8fd7-74758c9747e5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2167-922c72cf-b188-46b2-8ab3-23da0565893c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2167-922c72cf-b188-46b2-8ab3-23da0565893c.txn deleted file mode 100644 index cb18d248a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2167-922c72cf-b188-46b2-8ab3-23da0565893c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2168-7004ff1e-ca8b-460c-b0d7-bd60831bf3a9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2168-7004ff1e-ca8b-460c-b0d7-bd60831bf3a9.txn deleted file mode 100644 index cf056ea8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2168-7004ff1e-ca8b-460c-b0d7-bd60831bf3a9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2169-2b481c6d-f672-4e29-8f92-bf4aab89cd6d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2169-2b481c6d-f672-4e29-8f92-bf4aab89cd6d.txn deleted file mode 100644 index f4a5b9c58..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2169-2b481c6d-f672-4e29-8f92-bf4aab89cd6d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/217-4b34c329-1439-4bee-b7be-4772f9762668.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/217-4b34c329-1439-4bee-b7be-4772f9762668.txn deleted file mode 100644 index 8d3ee7f5b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/217-4b34c329-1439-4bee-b7be-4772f9762668.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2170-43d35d42-a4ae-4159-a3a6-77da58c5d6c4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2170-43d35d42-a4ae-4159-a3a6-77da58c5d6c4.txn deleted file mode 100644 index cb8ac5fbc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2170-43d35d42-a4ae-4159-a3a6-77da58c5d6c4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2171-a8da65ee-a13b-431f-9f85-60add0939b05.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2171-a8da65ee-a13b-431f-9f85-60add0939b05.txn deleted file mode 100644 index 9287639c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2171-a8da65ee-a13b-431f-9f85-60add0939b05.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2172-b8a6b530-3992-45bc-a47a-75b48a511271.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2172-b8a6b530-3992-45bc-a47a-75b48a511271.txn deleted file mode 100644 index 276645b03..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2172-b8a6b530-3992-45bc-a47a-75b48a511271.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2173-18368fa0-286b-4bc1-8b23-d637fd37b5f0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2173-18368fa0-286b-4bc1-8b23-d637fd37b5f0.txn deleted file mode 100644 index 063b82cd2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2173-18368fa0-286b-4bc1-8b23-d637fd37b5f0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2174-614ab06d-4e3a-4d44-8cff-8415c2a0aa84.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2174-614ab06d-4e3a-4d44-8cff-8415c2a0aa84.txn deleted file mode 100644 index 250837f97..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2174-614ab06d-4e3a-4d44-8cff-8415c2a0aa84.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2175-b0e2bcba-45d8-41ff-9ff8-12952baf3aee.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2175-b0e2bcba-45d8-41ff-9ff8-12952baf3aee.txn deleted file mode 100644 index dca392d4d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2175-b0e2bcba-45d8-41ff-9ff8-12952baf3aee.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2176-6106e4a5-b4dc-4dfd-a85c-4b3a7de93d7b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2176-6106e4a5-b4dc-4dfd-a85c-4b3a7de93d7b.txn deleted file mode 100644 index a426b46a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2176-6106e4a5-b4dc-4dfd-a85c-4b3a7de93d7b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2177-067adefa-4601-4d7b-8701-1d8740438b08.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2177-067adefa-4601-4d7b-8701-1d8740438b08.txn deleted file mode 100644 index 693d7539b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2177-067adefa-4601-4d7b-8701-1d8740438b08.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2178-7527bad8-215d-4396-9419-68dd52085b8c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2178-7527bad8-215d-4396-9419-68dd52085b8c.txn deleted file mode 100644 index 3a3864127..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2178-7527bad8-215d-4396-9419-68dd52085b8c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2179-9c37ea10-9f4a-497a-b3c2-f2405a134498.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2179-9c37ea10-9f4a-497a-b3c2-f2405a134498.txn deleted file mode 100644 index 1da5e84ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2179-9c37ea10-9f4a-497a-b3c2-f2405a134498.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/218-e399d7d9-6642-48fb-ac8f-21483c8c0658.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/218-e399d7d9-6642-48fb-ac8f-21483c8c0658.txn deleted file mode 100644 index a271293b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/218-e399d7d9-6642-48fb-ac8f-21483c8c0658.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2180-990efcd5-2615-4631-863c-99a3acdb9c9f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2180-990efcd5-2615-4631-863c-99a3acdb9c9f.txn deleted file mode 100644 index d7fbf90cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2180-990efcd5-2615-4631-863c-99a3acdb9c9f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2181-6c274105-c849-4225-b1e7-27d22c81c01a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2181-6c274105-c849-4225-b1e7-27d22c81c01a.txn deleted file mode 100644 index 1adab78e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2181-6c274105-c849-4225-b1e7-27d22c81c01a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2182-647e68e8-3d58-4e95-8d4d-c739fc59e513.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2182-647e68e8-3d58-4e95-8d4d-c739fc59e513.txn deleted file mode 100644 index 5d698242d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2182-647e68e8-3d58-4e95-8d4d-c739fc59e513.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2183-8e7495ba-b1da-4da4-8415-c8cb72660ee2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2183-8e7495ba-b1da-4da4-8415-c8cb72660ee2.txn deleted file mode 100644 index af2906244..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2183-8e7495ba-b1da-4da4-8415-c8cb72660ee2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2184-2b228942-04ea-4701-8ac2-16f91ee8fc06.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2184-2b228942-04ea-4701-8ac2-16f91ee8fc06.txn deleted file mode 100644 index a373d9a2d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2184-2b228942-04ea-4701-8ac2-16f91ee8fc06.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2185-c195d0c8-7d9c-4ea0-97b9-70c4d93fcbc2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2185-c195d0c8-7d9c-4ea0-97b9-70c4d93fcbc2.txn deleted file mode 100644 index e412270f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2185-c195d0c8-7d9c-4ea0-97b9-70c4d93fcbc2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2186-983011a9-1978-476a-8a15-f50434b29d26.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2186-983011a9-1978-476a-8a15-f50434b29d26.txn deleted file mode 100644 index 05fe30a63..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2186-983011a9-1978-476a-8a15-f50434b29d26.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2187-74549ab6-9f22-421a-914c-9d82133ecbe9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2187-74549ab6-9f22-421a-914c-9d82133ecbe9.txn deleted file mode 100644 index f899aefb2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2187-74549ab6-9f22-421a-914c-9d82133ecbe9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2188-32f6c4eb-f031-4200-97c3-a53337907a07.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2188-32f6c4eb-f031-4200-97c3-a53337907a07.txn deleted file mode 100644 index 7f90d29fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2188-32f6c4eb-f031-4200-97c3-a53337907a07.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2189-2f0a425f-f3ac-4105-8401-4950dff2e6f4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2189-2f0a425f-f3ac-4105-8401-4950dff2e6f4.txn deleted file mode 100644 index ed444034b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2189-2f0a425f-f3ac-4105-8401-4950dff2e6f4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/219-6a35391f-857e-4926-b249-892418c12e60.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/219-6a35391f-857e-4926-b249-892418c12e60.txn deleted file mode 100644 index 79c0cc19a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/219-6a35391f-857e-4926-b249-892418c12e60.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2190-7c310ad0-a0e4-467a-acb2-9bfa77a3f642.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2190-7c310ad0-a0e4-467a-acb2-9bfa77a3f642.txn deleted file mode 100644 index 092e6dd84..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2190-7c310ad0-a0e4-467a-acb2-9bfa77a3f642.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2191-9679ad32-c314-4da1-973c-24694351e4e3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2191-9679ad32-c314-4da1-973c-24694351e4e3.txn deleted file mode 100644 index 7c36ed65b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2191-9679ad32-c314-4da1-973c-24694351e4e3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2192-5f3d8585-6031-44cc-be12-6db8f593542c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2192-5f3d8585-6031-44cc-be12-6db8f593542c.txn deleted file mode 100644 index c3156180d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2192-5f3d8585-6031-44cc-be12-6db8f593542c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2193-f034fe35-ff65-4ab4-8d20-6c16ec98ef5a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2193-f034fe35-ff65-4ab4-8d20-6c16ec98ef5a.txn deleted file mode 100644 index 4c3d201c2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2193-f034fe35-ff65-4ab4-8d20-6c16ec98ef5a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2194-65106870-382a-41ff-b130-68121678f4a5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2194-65106870-382a-41ff-b130-68121678f4a5.txn deleted file mode 100644 index 731500f5d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2194-65106870-382a-41ff-b130-68121678f4a5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2195-fc727617-9163-499d-9c9b-139a33a527a5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2195-fc727617-9163-499d-9c9b-139a33a527a5.txn deleted file mode 100644 index 2f83ae3ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2195-fc727617-9163-499d-9c9b-139a33a527a5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2196-8ef19999-0655-4f04-b8b8-b6abd0f25558.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2196-8ef19999-0655-4f04-b8b8-b6abd0f25558.txn deleted file mode 100644 index f041220a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2196-8ef19999-0655-4f04-b8b8-b6abd0f25558.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2197-e3ef18dd-942f-4e30-8be7-a2de91df41e1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2197-e3ef18dd-942f-4e30-8be7-a2de91df41e1.txn deleted file mode 100644 index b7f7837a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2197-e3ef18dd-942f-4e30-8be7-a2de91df41e1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2198-68f89774-851c-474b-9137-989faad25a09.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2198-68f89774-851c-474b-9137-989faad25a09.txn deleted file mode 100644 index d03fb0208..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2198-68f89774-851c-474b-9137-989faad25a09.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2199-3e239151-4ced-4cce-a8f8-dc899350baa1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2199-3e239151-4ced-4cce-a8f8-dc899350baa1.txn deleted file mode 100644 index f983d96d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2199-3e239151-4ced-4cce-a8f8-dc899350baa1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/22-8b5f39ef-b06f-497b-9669-01e72ebfa1c0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/22-8b5f39ef-b06f-497b-9669-01e72ebfa1c0.txn deleted file mode 100644 index c781898c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/22-8b5f39ef-b06f-497b-9669-01e72ebfa1c0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/220-7880c0b8-f3c5-4993-ae93-7cc270e4bd48.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/220-7880c0b8-f3c5-4993-ae93-7cc270e4bd48.txn deleted file mode 100644 index da2c8b46a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/220-7880c0b8-f3c5-4993-ae93-7cc270e4bd48.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2200-6becf3bc-e9e1-455f-9cf8-ac514fc8ffe1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2200-6becf3bc-e9e1-455f-9cf8-ac514fc8ffe1.txn deleted file mode 100644 index 126a122f5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2200-6becf3bc-e9e1-455f-9cf8-ac514fc8ffe1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2201-39985d0c-711b-4f17-8c29-65bfc31c4520.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2201-39985d0c-711b-4f17-8c29-65bfc31c4520.txn deleted file mode 100644 index 189d40fd4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2201-39985d0c-711b-4f17-8c29-65bfc31c4520.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2202-8d6adadf-2f94-4691-a059-e094a3bc13c4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2202-8d6adadf-2f94-4691-a059-e094a3bc13c4.txn deleted file mode 100644 index 80f6fcf08..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2202-8d6adadf-2f94-4691-a059-e094a3bc13c4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2203-9c046306-4ff2-4dbc-83a4-a394fa66ac64.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2203-9c046306-4ff2-4dbc-83a4-a394fa66ac64.txn deleted file mode 100644 index 27de13e50..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2203-9c046306-4ff2-4dbc-83a4-a394fa66ac64.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2204-38002bb2-7a1f-4521-b413-43b164f59e90.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2204-38002bb2-7a1f-4521-b413-43b164f59e90.txn deleted file mode 100644 index d4c93623b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2204-38002bb2-7a1f-4521-b413-43b164f59e90.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2205-36730b29-ccd1-45e2-bfbf-ce41d38df76c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2205-36730b29-ccd1-45e2-bfbf-ce41d38df76c.txn deleted file mode 100644 index 5c947845d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2205-36730b29-ccd1-45e2-bfbf-ce41d38df76c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2206-f1c0f1d4-877d-4c22-8599-8b762d48caa6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2206-f1c0f1d4-877d-4c22-8599-8b762d48caa6.txn deleted file mode 100644 index 8978cf37c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2206-f1c0f1d4-877d-4c22-8599-8b762d48caa6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2207-f8af5ca8-7553-481e-9c9a-8210276887fe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2207-f8af5ca8-7553-481e-9c9a-8210276887fe.txn deleted file mode 100644 index 4e3505b08..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2207-f8af5ca8-7553-481e-9c9a-8210276887fe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2208-eb324275-bbf3-4835-980f-f26121aadc1f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2208-eb324275-bbf3-4835-980f-f26121aadc1f.txn deleted file mode 100644 index 787540647..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2208-eb324275-bbf3-4835-980f-f26121aadc1f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2209-cd806b85-6d1f-46d1-b4fd-3be7a69ca93a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2209-cd806b85-6d1f-46d1-b4fd-3be7a69ca93a.txn deleted file mode 100644 index 6e30a1b57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2209-cd806b85-6d1f-46d1-b4fd-3be7a69ca93a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/221-a5444137-34e4-48f2-9308-51b02ab4d694.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/221-a5444137-34e4-48f2-9308-51b02ab4d694.txn deleted file mode 100644 index dec177505..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/221-a5444137-34e4-48f2-9308-51b02ab4d694.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2210-6bbc874f-90d3-4803-b1de-5b397abf2919.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2210-6bbc874f-90d3-4803-b1de-5b397abf2919.txn deleted file mode 100644 index cec907d68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2210-6bbc874f-90d3-4803-b1de-5b397abf2919.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2211-18c2745c-39d3-44dd-b44b-81e73c320118.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2211-18c2745c-39d3-44dd-b44b-81e73c320118.txn deleted file mode 100644 index b11a9209c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2211-18c2745c-39d3-44dd-b44b-81e73c320118.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2212-15ee2a5d-298e-4f36-9425-bb92f70b7bd5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2212-15ee2a5d-298e-4f36-9425-bb92f70b7bd5.txn deleted file mode 100644 index fbfff4fe8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2212-15ee2a5d-298e-4f36-9425-bb92f70b7bd5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2213-7fcf1c2c-9c33-431d-9277-fa542598cb99.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2213-7fcf1c2c-9c33-431d-9277-fa542598cb99.txn deleted file mode 100644 index 679fb155a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2213-7fcf1c2c-9c33-431d-9277-fa542598cb99.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2214-0a424823-aed7-4383-8f7d-442aa3d276c4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2214-0a424823-aed7-4383-8f7d-442aa3d276c4.txn deleted file mode 100644 index 8add92c95..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2214-0a424823-aed7-4383-8f7d-442aa3d276c4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2215-522e4cc5-d7e7-4d26-b6f4-a2a0999a0c82.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2215-522e4cc5-d7e7-4d26-b6f4-a2a0999a0c82.txn deleted file mode 100644 index 325d37bbd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2215-522e4cc5-d7e7-4d26-b6f4-a2a0999a0c82.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2216-d0825251-1ee7-40cb-aa52-e0e439dbd627.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2216-d0825251-1ee7-40cb-aa52-e0e439dbd627.txn deleted file mode 100644 index 15d152996..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2216-d0825251-1ee7-40cb-aa52-e0e439dbd627.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2217-0252a285-686a-42e9-a826-2540f38afe64.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2217-0252a285-686a-42e9-a826-2540f38afe64.txn deleted file mode 100644 index 2b0f1a177..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2217-0252a285-686a-42e9-a826-2540f38afe64.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2218-73eeb8a3-a7dd-4ff4-9362-0f920f851fb7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2218-73eeb8a3-a7dd-4ff4-9362-0f920f851fb7.txn deleted file mode 100644 index e61031be7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2218-73eeb8a3-a7dd-4ff4-9362-0f920f851fb7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2219-1027a2cc-d958-46d0-8e03-11c837b7267a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2219-1027a2cc-d958-46d0-8e03-11c837b7267a.txn deleted file mode 100644 index 92b6f1cd9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2219-1027a2cc-d958-46d0-8e03-11c837b7267a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/222-ca9076ff-6557-4488-9f13-d4fd60141b66.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/222-ca9076ff-6557-4488-9f13-d4fd60141b66.txn deleted file mode 100644 index 209346cef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/222-ca9076ff-6557-4488-9f13-d4fd60141b66.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2220-987b5e4f-3f8f-409c-8d0e-73db3f1c6aea.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2220-987b5e4f-3f8f-409c-8d0e-73db3f1c6aea.txn deleted file mode 100644 index 10f535e1e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2220-987b5e4f-3f8f-409c-8d0e-73db3f1c6aea.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2221-8c2beb51-b574-4208-b754-268c0a0a3e79.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2221-8c2beb51-b574-4208-b754-268c0a0a3e79.txn deleted file mode 100644 index 937cccd07..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2221-8c2beb51-b574-4208-b754-268c0a0a3e79.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2222-b5819320-0d4c-41f3-9266-5d0643fc1bc2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2222-b5819320-0d4c-41f3-9266-5d0643fc1bc2.txn deleted file mode 100644 index 361f858e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2222-b5819320-0d4c-41f3-9266-5d0643fc1bc2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2223-ab89f08c-6358-479d-971e-a0409ee9d008.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2223-ab89f08c-6358-479d-971e-a0409ee9d008.txn deleted file mode 100644 index 594144a69..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2223-ab89f08c-6358-479d-971e-a0409ee9d008.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2224-885ca332-ea73-44c7-b600-49899f0fcd03.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2224-885ca332-ea73-44c7-b600-49899f0fcd03.txn deleted file mode 100644 index f37b17fc6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2224-885ca332-ea73-44c7-b600-49899f0fcd03.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2225-6a56a7d1-ba3c-45dc-8a96-1da4cdb4bad4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2225-6a56a7d1-ba3c-45dc-8a96-1da4cdb4bad4.txn deleted file mode 100644 index 2e39b4fb7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2225-6a56a7d1-ba3c-45dc-8a96-1da4cdb4bad4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2226-f13b8d3d-d686-4920-aee4-60ae1f1de386.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2226-f13b8d3d-d686-4920-aee4-60ae1f1de386.txn deleted file mode 100644 index c51498992..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2226-f13b8d3d-d686-4920-aee4-60ae1f1de386.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2227-465bc2c8-01cd-44b5-b4d9-e6ad9698e836.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2227-465bc2c8-01cd-44b5-b4d9-e6ad9698e836.txn deleted file mode 100644 index 95e5da1b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2227-465bc2c8-01cd-44b5-b4d9-e6ad9698e836.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2228-d7c33c3d-7e5c-4a70-bb33-9f51ae7e7d1a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2228-d7c33c3d-7e5c-4a70-bb33-9f51ae7e7d1a.txn deleted file mode 100644 index baee2d865..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2228-d7c33c3d-7e5c-4a70-bb33-9f51ae7e7d1a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2229-e521ff62-ff0c-4b10-bf72-f9e442a8d095.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2229-e521ff62-ff0c-4b10-bf72-f9e442a8d095.txn deleted file mode 100644 index f6c523155..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2229-e521ff62-ff0c-4b10-bf72-f9e442a8d095.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/223-bb6e783b-fcb2-41e2-989e-a5f12caef876.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/223-bb6e783b-fcb2-41e2-989e-a5f12caef876.txn deleted file mode 100644 index 7986d46d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/223-bb6e783b-fcb2-41e2-989e-a5f12caef876.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2230-0ed8f7ff-62b3-46f2-8e6b-0fdec316caaa.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2230-0ed8f7ff-62b3-46f2-8e6b-0fdec316caaa.txn deleted file mode 100644 index 0510bfbb9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2230-0ed8f7ff-62b3-46f2-8e6b-0fdec316caaa.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2231-2c9978f3-a1db-40a3-9ac9-4e5109ad2b26.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2231-2c9978f3-a1db-40a3-9ac9-4e5109ad2b26.txn deleted file mode 100644 index e2512dc7f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2231-2c9978f3-a1db-40a3-9ac9-4e5109ad2b26.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2232-d48803f6-7d74-4cc1-bc07-51ea62e3af7b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2232-d48803f6-7d74-4cc1-bc07-51ea62e3af7b.txn deleted file mode 100644 index f210ae912..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2232-d48803f6-7d74-4cc1-bc07-51ea62e3af7b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2233-268cb72c-c747-47cc-9f2d-6bdd3b48bff9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2233-268cb72c-c747-47cc-9f2d-6bdd3b48bff9.txn deleted file mode 100644 index efa5abab5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2233-268cb72c-c747-47cc-9f2d-6bdd3b48bff9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2234-d0586923-a043-4b0c-b27d-fa7e309bc34a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2234-d0586923-a043-4b0c-b27d-fa7e309bc34a.txn deleted file mode 100644 index f3e76b216..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2234-d0586923-a043-4b0c-b27d-fa7e309bc34a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2235-dcd93db4-fad2-4b53-8164-900781591540.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2235-dcd93db4-fad2-4b53-8164-900781591540.txn deleted file mode 100644 index 8d6bc2c52..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2235-dcd93db4-fad2-4b53-8164-900781591540.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2236-982cc2fa-9198-49a4-90f3-42ac62c556fb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2236-982cc2fa-9198-49a4-90f3-42ac62c556fb.txn deleted file mode 100644 index b867e1adc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2236-982cc2fa-9198-49a4-90f3-42ac62c556fb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2237-fe5f1044-f0ea-4368-8885-7927471a20d8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2237-fe5f1044-f0ea-4368-8885-7927471a20d8.txn deleted file mode 100644 index f8f1e1c3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2237-fe5f1044-f0ea-4368-8885-7927471a20d8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2238-e87b9bb5-ff56-4fa7-a097-b2709657596f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2238-e87b9bb5-ff56-4fa7-a097-b2709657596f.txn deleted file mode 100644 index 7ad42bc5d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2238-e87b9bb5-ff56-4fa7-a097-b2709657596f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2239-d7dbfeaf-2768-4986-9392-0b051a25dbdd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2239-d7dbfeaf-2768-4986-9392-0b051a25dbdd.txn deleted file mode 100644 index 2b95286f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2239-d7dbfeaf-2768-4986-9392-0b051a25dbdd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/224-7d11f397-853b-4c50-aaf5-0ec48ca48416.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/224-7d11f397-853b-4c50-aaf5-0ec48ca48416.txn deleted file mode 100644 index bb3e91da3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/224-7d11f397-853b-4c50-aaf5-0ec48ca48416.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2240-93410752-05e1-4f47-896c-063effd7f5f5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2240-93410752-05e1-4f47-896c-063effd7f5f5.txn deleted file mode 100644 index ca078b98c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2240-93410752-05e1-4f47-896c-063effd7f5f5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2241-61ee4663-792e-4150-8dd4-00e13eb79c1f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2241-61ee4663-792e-4150-8dd4-00e13eb79c1f.txn deleted file mode 100644 index 67c5f929e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2241-61ee4663-792e-4150-8dd4-00e13eb79c1f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2242-b7b70041-2baf-4d82-a1c2-2a6548fd2051.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2242-b7b70041-2baf-4d82-a1c2-2a6548fd2051.txn deleted file mode 100644 index 533b87eab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2242-b7b70041-2baf-4d82-a1c2-2a6548fd2051.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2243-1fb49c72-9ea4-4a51-8ab1-5456b1e6ad31.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2243-1fb49c72-9ea4-4a51-8ab1-5456b1e6ad31.txn deleted file mode 100644 index a7bfa4d9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2243-1fb49c72-9ea4-4a51-8ab1-5456b1e6ad31.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2244-bd252ad9-4f78-4b34-ae7a-bcc9ae9ebc87.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2244-bd252ad9-4f78-4b34-ae7a-bcc9ae9ebc87.txn deleted file mode 100644 index 2f313daa8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2244-bd252ad9-4f78-4b34-ae7a-bcc9ae9ebc87.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2245-ca1ff848-4546-4fa1-a493-0bfd96203ff6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2245-ca1ff848-4546-4fa1-a493-0bfd96203ff6.txn deleted file mode 100644 index ce71ccf35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2245-ca1ff848-4546-4fa1-a493-0bfd96203ff6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2246-000d2417-ca19-485e-ad0f-4c68d83dec91.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2246-000d2417-ca19-485e-ad0f-4c68d83dec91.txn deleted file mode 100644 index fa328cb11..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2246-000d2417-ca19-485e-ad0f-4c68d83dec91.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2247-3153b6d0-5d2e-4860-9034-dd90a585b22b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2247-3153b6d0-5d2e-4860-9034-dd90a585b22b.txn deleted file mode 100644 index 3db1f17e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2247-3153b6d0-5d2e-4860-9034-dd90a585b22b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2248-7eba06f4-8cd6-4fd1-a5ef-837f67f1e96f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2248-7eba06f4-8cd6-4fd1-a5ef-837f67f1e96f.txn deleted file mode 100644 index 8b60dc253..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2248-7eba06f4-8cd6-4fd1-a5ef-837f67f1e96f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2249-a5bf7e24-a31f-4072-86f0-9b4abc4a97e4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2249-a5bf7e24-a31f-4072-86f0-9b4abc4a97e4.txn deleted file mode 100644 index c0c9128ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2249-a5bf7e24-a31f-4072-86f0-9b4abc4a97e4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/225-edd25f2b-b46d-4e9f-81be-73c524b019ad.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/225-edd25f2b-b46d-4e9f-81be-73c524b019ad.txn deleted file mode 100644 index 689f91bba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/225-edd25f2b-b46d-4e9f-81be-73c524b019ad.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2250-269a9dc6-02bd-49e5-b316-38f57cfed45d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2250-269a9dc6-02bd-49e5-b316-38f57cfed45d.txn deleted file mode 100644 index 5f72cfe9d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2250-269a9dc6-02bd-49e5-b316-38f57cfed45d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2251-8109344e-5f94-4a07-b3f2-961989843441.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2251-8109344e-5f94-4a07-b3f2-961989843441.txn deleted file mode 100644 index c476b3d11..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2251-8109344e-5f94-4a07-b3f2-961989843441.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2252-7f3ce699-464c-4b9b-b5fc-6d3c6589dfe9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2252-7f3ce699-464c-4b9b-b5fc-6d3c6589dfe9.txn deleted file mode 100644 index f52ba9d39..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2252-7f3ce699-464c-4b9b-b5fc-6d3c6589dfe9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2253-7e7fdcb6-d76f-4f00-803f-8607e744d9f8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2253-7e7fdcb6-d76f-4f00-803f-8607e744d9f8.txn deleted file mode 100644 index e73dafe47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2253-7e7fdcb6-d76f-4f00-803f-8607e744d9f8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2254-48104d7f-f7d1-4665-a8b7-5f534dc8812d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2254-48104d7f-f7d1-4665-a8b7-5f534dc8812d.txn deleted file mode 100644 index 8f2e78452..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2254-48104d7f-f7d1-4665-a8b7-5f534dc8812d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2255-e7235c1d-f13d-48ab-be6d-3dd6a5544f24.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2255-e7235c1d-f13d-48ab-be6d-3dd6a5544f24.txn deleted file mode 100644 index 9b4cf5218..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2255-e7235c1d-f13d-48ab-be6d-3dd6a5544f24.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2256-3d664498-b904-4719-af5d-6ac07ffeaa28.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2256-3d664498-b904-4719-af5d-6ac07ffeaa28.txn deleted file mode 100644 index de699111d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2256-3d664498-b904-4719-af5d-6ac07ffeaa28.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2257-58e92843-a9e9-4afc-aa01-ca4e8cc9215f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2257-58e92843-a9e9-4afc-aa01-ca4e8cc9215f.txn deleted file mode 100644 index 92155239f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2257-58e92843-a9e9-4afc-aa01-ca4e8cc9215f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2258-74756f48-e74e-4a29-8fac-05cc05f80fb7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2258-74756f48-e74e-4a29-8fac-05cc05f80fb7.txn deleted file mode 100644 index edf92ee46..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2258-74756f48-e74e-4a29-8fac-05cc05f80fb7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2259-c79d7213-40c0-4c46-aa9f-a23ffe74e44f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2259-c79d7213-40c0-4c46-aa9f-a23ffe74e44f.txn deleted file mode 100644 index 93b6b0e0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2259-c79d7213-40c0-4c46-aa9f-a23ffe74e44f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/226-274f67de-216f-48b0-a389-9cb51ca522de.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/226-274f67de-216f-48b0-a389-9cb51ca522de.txn deleted file mode 100644 index a484989b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/226-274f67de-216f-48b0-a389-9cb51ca522de.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2260-1d8259de-b9ef-46e0-a285-e9a4d9402d5f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2260-1d8259de-b9ef-46e0-a285-e9a4d9402d5f.txn deleted file mode 100644 index 6fddec09e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2260-1d8259de-b9ef-46e0-a285-e9a4d9402d5f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2261-9bc3499a-e699-416e-b992-9f6ea44ace30.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2261-9bc3499a-e699-416e-b992-9f6ea44ace30.txn deleted file mode 100644 index f5af4ea10..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2261-9bc3499a-e699-416e-b992-9f6ea44ace30.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2262-b15ee451-aeb0-4e95-94c3-536ab3eddbb1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2262-b15ee451-aeb0-4e95-94c3-536ab3eddbb1.txn deleted file mode 100644 index d18c32dcf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2262-b15ee451-aeb0-4e95-94c3-536ab3eddbb1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2263-cd4aaddc-eb4c-41c3-86eb-a0595b451ef2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2263-cd4aaddc-eb4c-41c3-86eb-a0595b451ef2.txn deleted file mode 100644 index 4e38ece9a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2263-cd4aaddc-eb4c-41c3-86eb-a0595b451ef2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2264-1789ca3d-2abb-44ca-bc76-6af21a7c86f1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2264-1789ca3d-2abb-44ca-bc76-6af21a7c86f1.txn deleted file mode 100644 index 4d2ea648c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2264-1789ca3d-2abb-44ca-bc76-6af21a7c86f1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2265-85e7df47-6740-44ce-bcaf-29805cc29051.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2265-85e7df47-6740-44ce-bcaf-29805cc29051.txn deleted file mode 100644 index cfb178d36..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2265-85e7df47-6740-44ce-bcaf-29805cc29051.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2266-6e2d6a5a-6bc9-44e4-a387-b5033ca8ca81.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2266-6e2d6a5a-6bc9-44e4-a387-b5033ca8ca81.txn deleted file mode 100644 index 46daf10bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2266-6e2d6a5a-6bc9-44e4-a387-b5033ca8ca81.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2267-9b8bc4f3-747a-4f91-8ba0-31cd1e8cbeee.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2267-9b8bc4f3-747a-4f91-8ba0-31cd1e8cbeee.txn deleted file mode 100644 index 1e6a62309..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2267-9b8bc4f3-747a-4f91-8ba0-31cd1e8cbeee.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2268-c7fb6d24-0162-4814-b70c-d163f05a0503.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2268-c7fb6d24-0162-4814-b70c-d163f05a0503.txn deleted file mode 100644 index 898189514..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2268-c7fb6d24-0162-4814-b70c-d163f05a0503.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2269-74e75d4f-217e-46b1-aee3-b093674108d4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2269-74e75d4f-217e-46b1-aee3-b093674108d4.txn deleted file mode 100644 index 41275653f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2269-74e75d4f-217e-46b1-aee3-b093674108d4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/227-cb149396-70ff-493b-ae63-9d91cebfd0bd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/227-cb149396-70ff-493b-ae63-9d91cebfd0bd.txn deleted file mode 100644 index 90190ac6c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/227-cb149396-70ff-493b-ae63-9d91cebfd0bd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2270-02bb3acb-cafb-458d-b782-d51cdcefb6ef.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2270-02bb3acb-cafb-458d-b782-d51cdcefb6ef.txn deleted file mode 100644 index e51b20f17..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2270-02bb3acb-cafb-458d-b782-d51cdcefb6ef.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2271-08b71f4d-7c7a-4c9e-b3c0-6b8a906d64e8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2271-08b71f4d-7c7a-4c9e-b3c0-6b8a906d64e8.txn deleted file mode 100644 index a11bb2e0f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2271-08b71f4d-7c7a-4c9e-b3c0-6b8a906d64e8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2272-633f8a63-6fcc-4e64-968d-37cb280a296f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2272-633f8a63-6fcc-4e64-968d-37cb280a296f.txn deleted file mode 100644 index d3b0e5677..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2272-633f8a63-6fcc-4e64-968d-37cb280a296f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2273-9bd7cfcd-b927-4755-8135-572b2c0cb6ec.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2273-9bd7cfcd-b927-4755-8135-572b2c0cb6ec.txn deleted file mode 100644 index 1ac6a29d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2273-9bd7cfcd-b927-4755-8135-572b2c0cb6ec.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2274-4380bfa6-4162-4c11-b7b2-bb51ad133321.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2274-4380bfa6-4162-4c11-b7b2-bb51ad133321.txn deleted file mode 100644 index 22dacd600..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2274-4380bfa6-4162-4c11-b7b2-bb51ad133321.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2275-9a83fdd5-2904-448f-aa0f-ab7ae4ab8fc2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2275-9a83fdd5-2904-448f-aa0f-ab7ae4ab8fc2.txn deleted file mode 100644 index c0342557c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2275-9a83fdd5-2904-448f-aa0f-ab7ae4ab8fc2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2276-e7b826c4-5ba0-4b0d-9720-81d3b1fcaa85.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2276-e7b826c4-5ba0-4b0d-9720-81d3b1fcaa85.txn deleted file mode 100644 index b2e35991f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2276-e7b826c4-5ba0-4b0d-9720-81d3b1fcaa85.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2277-2d1d5f54-6f38-4efe-9bc4-d29ef76d5b21.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2277-2d1d5f54-6f38-4efe-9bc4-d29ef76d5b21.txn deleted file mode 100644 index e720a4b1a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2277-2d1d5f54-6f38-4efe-9bc4-d29ef76d5b21.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2278-85aa9c85-eef9-4ecf-8a9b-42315a9e9799.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2278-85aa9c85-eef9-4ecf-8a9b-42315a9e9799.txn deleted file mode 100644 index 497d57de2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2278-85aa9c85-eef9-4ecf-8a9b-42315a9e9799.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2279-298cd58a-a371-4657-a3be-e7e90625f6db.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2279-298cd58a-a371-4657-a3be-e7e90625f6db.txn deleted file mode 100644 index 57eba654c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2279-298cd58a-a371-4657-a3be-e7e90625f6db.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/228-55a4bb0a-9f48-422d-9259-591265fb88f4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/228-55a4bb0a-9f48-422d-9259-591265fb88f4.txn deleted file mode 100644 index c5effe273..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/228-55a4bb0a-9f48-422d-9259-591265fb88f4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2280-e6cdb34f-f2f8-4735-ab07-06507146d7df.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2280-e6cdb34f-f2f8-4735-ab07-06507146d7df.txn deleted file mode 100644 index c82e0490c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2280-e6cdb34f-f2f8-4735-ab07-06507146d7df.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2281-190b4a73-3a0f-4465-a462-87b965fa1169.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2281-190b4a73-3a0f-4465-a462-87b965fa1169.txn deleted file mode 100644 index a4e7e1442..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2281-190b4a73-3a0f-4465-a462-87b965fa1169.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2282-bf1e7dde-e688-487a-8dc1-f19cbcfb22c5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2282-bf1e7dde-e688-487a-8dc1-f19cbcfb22c5.txn deleted file mode 100644 index 3bc4225af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2282-bf1e7dde-e688-487a-8dc1-f19cbcfb22c5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2283-c5e87616-ee53-4f62-8b93-f370e4f8f342.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2283-c5e87616-ee53-4f62-8b93-f370e4f8f342.txn deleted file mode 100644 index c9ab4a7a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2283-c5e87616-ee53-4f62-8b93-f370e4f8f342.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2284-6e3f6dd6-7ceb-4c9f-9dbc-b86aa1eb136e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2284-6e3f6dd6-7ceb-4c9f-9dbc-b86aa1eb136e.txn deleted file mode 100644 index 7fd8ecb66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2284-6e3f6dd6-7ceb-4c9f-9dbc-b86aa1eb136e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2285-a1554b2f-0381-40ac-bfe3-24b94e79cfab.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2285-a1554b2f-0381-40ac-bfe3-24b94e79cfab.txn deleted file mode 100644 index 993ff61f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2285-a1554b2f-0381-40ac-bfe3-24b94e79cfab.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2286-f4898c62-2f7f-4197-825b-2cd7cde2fd2c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2286-f4898c62-2f7f-4197-825b-2cd7cde2fd2c.txn deleted file mode 100644 index 076b9b34a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2286-f4898c62-2f7f-4197-825b-2cd7cde2fd2c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2287-418728c8-4c99-42f6-b619-f6e0aa627021.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2287-418728c8-4c99-42f6-b619-f6e0aa627021.txn deleted file mode 100644 index 5ab417242..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2287-418728c8-4c99-42f6-b619-f6e0aa627021.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2288-060e2296-1f58-4c86-8a5e-ae271dc081b9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2288-060e2296-1f58-4c86-8a5e-ae271dc081b9.txn deleted file mode 100644 index 3a9bca5df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2288-060e2296-1f58-4c86-8a5e-ae271dc081b9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2289-6df00aae-2b23-4ff4-88b5-c68291135fc6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2289-6df00aae-2b23-4ff4-88b5-c68291135fc6.txn deleted file mode 100644 index ed04a04ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2289-6df00aae-2b23-4ff4-88b5-c68291135fc6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/229-09f6ef7c-cb44-4a14-8913-5260dbbc1142.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/229-09f6ef7c-cb44-4a14-8913-5260dbbc1142.txn deleted file mode 100644 index 36b5c24ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/229-09f6ef7c-cb44-4a14-8913-5260dbbc1142.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2290-1d3e12fc-1a54-44a4-a646-4d917f7bbbe7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2290-1d3e12fc-1a54-44a4-a646-4d917f7bbbe7.txn deleted file mode 100644 index 7de6ee0ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2290-1d3e12fc-1a54-44a4-a646-4d917f7bbbe7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2291-24e454b0-d58d-4ce3-803a-46d9007c6e9d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2291-24e454b0-d58d-4ce3-803a-46d9007c6e9d.txn deleted file mode 100644 index d45df3ee7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2291-24e454b0-d58d-4ce3-803a-46d9007c6e9d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2292-6a4b6c8b-b880-4f87-815a-46d9898e7996.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2292-6a4b6c8b-b880-4f87-815a-46d9898e7996.txn deleted file mode 100644 index d7896fdd7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2292-6a4b6c8b-b880-4f87-815a-46d9898e7996.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2293-024cdf10-3abb-4e0b-b7f0-fc6f279f49e8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2293-024cdf10-3abb-4e0b-b7f0-fc6f279f49e8.txn deleted file mode 100644 index 34ab9a1d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2293-024cdf10-3abb-4e0b-b7f0-fc6f279f49e8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2294-6fcccd91-33f3-43cd-b43e-6c484fabb889.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2294-6fcccd91-33f3-43cd-b43e-6c484fabb889.txn deleted file mode 100644 index 223241d2d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2294-6fcccd91-33f3-43cd-b43e-6c484fabb889.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2295-435035a6-8696-433a-8bdc-fc5b54a3df15.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2295-435035a6-8696-433a-8bdc-fc5b54a3df15.txn deleted file mode 100644 index 7338d1dfd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2295-435035a6-8696-433a-8bdc-fc5b54a3df15.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2296-67cc98e2-81fb-4382-8096-dad457f3764f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2296-67cc98e2-81fb-4382-8096-dad457f3764f.txn deleted file mode 100644 index 103dc3f8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2296-67cc98e2-81fb-4382-8096-dad457f3764f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2297-978f9d09-e668-4fe9-8c11-e7fc817d68c2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2297-978f9d09-e668-4fe9-8c11-e7fc817d68c2.txn deleted file mode 100644 index 121af4fec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2297-978f9d09-e668-4fe9-8c11-e7fc817d68c2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2298-00a24dc9-549b-47ea-8846-e278b4cea952.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2298-00a24dc9-549b-47ea-8846-e278b4cea952.txn deleted file mode 100644 index ec0178a6f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2298-00a24dc9-549b-47ea-8846-e278b4cea952.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2299-f3f6ba3b-825d-4b16-af1f-03ded9ff6f60.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2299-f3f6ba3b-825d-4b16-af1f-03ded9ff6f60.txn deleted file mode 100644 index 8b919b4ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2299-f3f6ba3b-825d-4b16-af1f-03ded9ff6f60.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/23-0c736817-2bb2-4059-a2f9-0b81bc0d1e96.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/23-0c736817-2bb2-4059-a2f9-0b81bc0d1e96.txn deleted file mode 100644 index bddf6b4ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/23-0c736817-2bb2-4059-a2f9-0b81bc0d1e96.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/230-811c3393-adf5-4bde-81e9-14a9148a07fc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/230-811c3393-adf5-4bde-81e9-14a9148a07fc.txn deleted file mode 100644 index 63c810ad3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/230-811c3393-adf5-4bde-81e9-14a9148a07fc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2300-27d91881-99b9-48cd-ae43-86b55031bdc9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2300-27d91881-99b9-48cd-ae43-86b55031bdc9.txn deleted file mode 100644 index 8dbdea83a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2300-27d91881-99b9-48cd-ae43-86b55031bdc9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2301-26b188e7-d0a3-4d58-a078-a883cb76f6af.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2301-26b188e7-d0a3-4d58-a078-a883cb76f6af.txn deleted file mode 100644 index 71b3f3e0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2301-26b188e7-d0a3-4d58-a078-a883cb76f6af.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2302-082d3002-ed2f-4922-81a7-57986b23ab38.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2302-082d3002-ed2f-4922-81a7-57986b23ab38.txn deleted file mode 100644 index 7c9ca7efc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2302-082d3002-ed2f-4922-81a7-57986b23ab38.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2303-01bc328c-dd08-42ac-a0f5-c14bf4c72e24.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2303-01bc328c-dd08-42ac-a0f5-c14bf4c72e24.txn deleted file mode 100644 index 38cdd3740..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2303-01bc328c-dd08-42ac-a0f5-c14bf4c72e24.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2304-707317c5-128e-415f-9d9d-df48e8065a96.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2304-707317c5-128e-415f-9d9d-df48e8065a96.txn deleted file mode 100644 index add5d00e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2304-707317c5-128e-415f-9d9d-df48e8065a96.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2305-3b65fbb8-55b8-452b-a84e-9acced38783b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2305-3b65fbb8-55b8-452b-a84e-9acced38783b.txn deleted file mode 100644 index 57280a331..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2305-3b65fbb8-55b8-452b-a84e-9acced38783b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2306-5a62e59d-e879-487d-b6bc-5345c79b688a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2306-5a62e59d-e879-487d-b6bc-5345c79b688a.txn deleted file mode 100644 index 45b1982c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2306-5a62e59d-e879-487d-b6bc-5345c79b688a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2307-a2063b0e-8134-4ed2-9a4d-f8995379b11d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2307-a2063b0e-8134-4ed2-9a4d-f8995379b11d.txn deleted file mode 100644 index d4ac1aa50..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2307-a2063b0e-8134-4ed2-9a4d-f8995379b11d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2308-2fd12b8c-71e8-4097-a2d8-fd676524f6a0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2308-2fd12b8c-71e8-4097-a2d8-fd676524f6a0.txn deleted file mode 100644 index 4d74d9f1e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2308-2fd12b8c-71e8-4097-a2d8-fd676524f6a0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2309-988ddffd-c6d1-4c88-a60f-cfda63b800af.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2309-988ddffd-c6d1-4c88-a60f-cfda63b800af.txn deleted file mode 100644 index 3268120cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2309-988ddffd-c6d1-4c88-a60f-cfda63b800af.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/231-75ddc331-8dd4-4aaf-972a-e202e7f8bb48.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/231-75ddc331-8dd4-4aaf-972a-e202e7f8bb48.txn deleted file mode 100644 index 630cce19c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/231-75ddc331-8dd4-4aaf-972a-e202e7f8bb48.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2310-03413759-55b4-453f-b21f-34a8894cfedd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2310-03413759-55b4-453f-b21f-34a8894cfedd.txn deleted file mode 100644 index 394fa24df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2310-03413759-55b4-453f-b21f-34a8894cfedd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2311-a8c38855-a7d3-45e1-9139-87146274a62d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2311-a8c38855-a7d3-45e1-9139-87146274a62d.txn deleted file mode 100644 index 3fa3a88f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2311-a8c38855-a7d3-45e1-9139-87146274a62d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2312-7dbd9713-9195-4a00-8363-55a25f7c51bc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2312-7dbd9713-9195-4a00-8363-55a25f7c51bc.txn deleted file mode 100644 index 4530bd3b2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2312-7dbd9713-9195-4a00-8363-55a25f7c51bc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2313-4008780f-171f-4eee-8e4e-b3f89ba246d6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2313-4008780f-171f-4eee-8e4e-b3f89ba246d6.txn deleted file mode 100644 index 90cf73a86..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2313-4008780f-171f-4eee-8e4e-b3f89ba246d6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2314-f42d379b-7d16-4ed8-858c-ac7378bbb10d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2314-f42d379b-7d16-4ed8-858c-ac7378bbb10d.txn deleted file mode 100644 index ab394c28d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2314-f42d379b-7d16-4ed8-858c-ac7378bbb10d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2315-31f5d921-ffd2-4c7f-801b-1f2c2d7d494f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2315-31f5d921-ffd2-4c7f-801b-1f2c2d7d494f.txn deleted file mode 100644 index 5f5cecea5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2315-31f5d921-ffd2-4c7f-801b-1f2c2d7d494f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2316-497b1315-e176-4ab1-8e56-8c631e8a1e65.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2316-497b1315-e176-4ab1-8e56-8c631e8a1e65.txn deleted file mode 100644 index 3d541bdd0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2316-497b1315-e176-4ab1-8e56-8c631e8a1e65.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2317-8e0d3b21-f5d8-4310-a528-62e773b960a3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2317-8e0d3b21-f5d8-4310-a528-62e773b960a3.txn deleted file mode 100644 index a71c1e804..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2317-8e0d3b21-f5d8-4310-a528-62e773b960a3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2318-97c5bfd1-5164-4f21-b7cb-80e8f437f4f8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2318-97c5bfd1-5164-4f21-b7cb-80e8f437f4f8.txn deleted file mode 100644 index 9180ed395..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2318-97c5bfd1-5164-4f21-b7cb-80e8f437f4f8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2319-89754966-09d7-4731-bbe6-60f6691f1add.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2319-89754966-09d7-4731-bbe6-60f6691f1add.txn deleted file mode 100644 index 8181c61ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2319-89754966-09d7-4731-bbe6-60f6691f1add.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/232-db2de2ee-df4a-47c9-8611-31dfb1729dbc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/232-db2de2ee-df4a-47c9-8611-31dfb1729dbc.txn deleted file mode 100644 index effb687d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/232-db2de2ee-df4a-47c9-8611-31dfb1729dbc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2320-3585fe88-2c76-4661-af67-6f0c923ab2d2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2320-3585fe88-2c76-4661-af67-6f0c923ab2d2.txn deleted file mode 100644 index 37aeca17f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2320-3585fe88-2c76-4661-af67-6f0c923ab2d2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2321-f3aed55c-8662-4f10-a64f-1c193f7b2865.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2321-f3aed55c-8662-4f10-a64f-1c193f7b2865.txn deleted file mode 100644 index 5aabb7ef8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2321-f3aed55c-8662-4f10-a64f-1c193f7b2865.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2322-796f3645-4bc1-4c22-a763-8f3ffe20b4eb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2322-796f3645-4bc1-4c22-a763-8f3ffe20b4eb.txn deleted file mode 100644 index 5e3f4cf07..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2322-796f3645-4bc1-4c22-a763-8f3ffe20b4eb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2323-c8f7934b-d2b4-4969-b3a1-f1b5bf7f0991.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2323-c8f7934b-d2b4-4969-b3a1-f1b5bf7f0991.txn deleted file mode 100644 index 2d50e8b0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2323-c8f7934b-d2b4-4969-b3a1-f1b5bf7f0991.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2324-2f2d8a85-33a1-4acd-9f0c-1944e3661aaa.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2324-2f2d8a85-33a1-4acd-9f0c-1944e3661aaa.txn deleted file mode 100644 index a0ac2aac1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2324-2f2d8a85-33a1-4acd-9f0c-1944e3661aaa.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2325-572e6ae3-e812-4078-aa43-69d508310023.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2325-572e6ae3-e812-4078-aa43-69d508310023.txn deleted file mode 100644 index 000ebdd0c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2325-572e6ae3-e812-4078-aa43-69d508310023.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2326-83ed99d9-e71b-4e89-8459-6e9bee20dab8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2326-83ed99d9-e71b-4e89-8459-6e9bee20dab8.txn deleted file mode 100644 index 01277e739..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2326-83ed99d9-e71b-4e89-8459-6e9bee20dab8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2327-d66f08d5-ac3e-4491-8088-7513757c2799.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2327-d66f08d5-ac3e-4491-8088-7513757c2799.txn deleted file mode 100644 index 11ee25664..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2327-d66f08d5-ac3e-4491-8088-7513757c2799.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2328-4c770d8c-dbaf-4d9e-9092-69bbde5773ae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2328-4c770d8c-dbaf-4d9e-9092-69bbde5773ae.txn deleted file mode 100644 index 911c0f0f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2328-4c770d8c-dbaf-4d9e-9092-69bbde5773ae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2329-12917417-bcfd-4e48-8a9a-5b2b06f0458a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2329-12917417-bcfd-4e48-8a9a-5b2b06f0458a.txn deleted file mode 100644 index 7a6cf1405..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2329-12917417-bcfd-4e48-8a9a-5b2b06f0458a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/233-fa1dac30-6c0d-4933-8966-5ac9c316b27b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/233-fa1dac30-6c0d-4933-8966-5ac9c316b27b.txn deleted file mode 100644 index f649d87d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/233-fa1dac30-6c0d-4933-8966-5ac9c316b27b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2330-64295ecc-a482-4dbb-8845-2662e4325cea.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2330-64295ecc-a482-4dbb-8845-2662e4325cea.txn deleted file mode 100644 index 971c4cad2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2330-64295ecc-a482-4dbb-8845-2662e4325cea.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2331-d4e284fa-f68a-4d64-b6ea-178b6137561c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2331-d4e284fa-f68a-4d64-b6ea-178b6137561c.txn deleted file mode 100644 index 2556ee28c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2331-d4e284fa-f68a-4d64-b6ea-178b6137561c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2332-4a62ea0f-4e31-4795-a463-9ce2efcb65ea.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2332-4a62ea0f-4e31-4795-a463-9ce2efcb65ea.txn deleted file mode 100644 index 1b80d2678..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2332-4a62ea0f-4e31-4795-a463-9ce2efcb65ea.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2333-72e6c7e8-92f5-41fa-8b4c-976713c4ccf1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2333-72e6c7e8-92f5-41fa-8b4c-976713c4ccf1.txn deleted file mode 100644 index d22ba92e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2333-72e6c7e8-92f5-41fa-8b4c-976713c4ccf1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2334-471f3e5d-8118-4dd9-b1c6-a644930ef09c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2334-471f3e5d-8118-4dd9-b1c6-a644930ef09c.txn deleted file mode 100644 index 51ee3fd7a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2334-471f3e5d-8118-4dd9-b1c6-a644930ef09c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2335-160e149e-358f-40b1-ae22-d43c468b5c24.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2335-160e149e-358f-40b1-ae22-d43c468b5c24.txn deleted file mode 100644 index 66dcf05d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2335-160e149e-358f-40b1-ae22-d43c468b5c24.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2336-2874226c-5ac9-4fa6-9615-42d5b227994a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2336-2874226c-5ac9-4fa6-9615-42d5b227994a.txn deleted file mode 100644 index 14a03c924..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2336-2874226c-5ac9-4fa6-9615-42d5b227994a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2337-1adbd9c5-c0eb-498f-a8e2-6056a3b881ae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2337-1adbd9c5-c0eb-498f-a8e2-6056a3b881ae.txn deleted file mode 100644 index 781a9fef9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2337-1adbd9c5-c0eb-498f-a8e2-6056a3b881ae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2338-40a61b24-9345-4331-b017-bbfb45a9f709.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2338-40a61b24-9345-4331-b017-bbfb45a9f709.txn deleted file mode 100644 index 53282b136..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2338-40a61b24-9345-4331-b017-bbfb45a9f709.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2339-0f3b5b58-c6c8-4ffc-ba46-a9bac56243ab.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2339-0f3b5b58-c6c8-4ffc-ba46-a9bac56243ab.txn deleted file mode 100644 index 6f35e2b9c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2339-0f3b5b58-c6c8-4ffc-ba46-a9bac56243ab.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/234-b36a5904-e237-4923-9d55-99886b36f600.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/234-b36a5904-e237-4923-9d55-99886b36f600.txn deleted file mode 100644 index 36d412714..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/234-b36a5904-e237-4923-9d55-99886b36f600.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2340-7abad3a6-994d-4b01-be6a-d103280f7ff4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2340-7abad3a6-994d-4b01-be6a-d103280f7ff4.txn deleted file mode 100644 index 8bdda4c09..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2340-7abad3a6-994d-4b01-be6a-d103280f7ff4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2341-0439856d-a593-47de-8c30-e1179d9beb31.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2341-0439856d-a593-47de-8c30-e1179d9beb31.txn deleted file mode 100644 index 98f1beaf4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2341-0439856d-a593-47de-8c30-e1179d9beb31.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2342-da4956f7-cd3f-4122-be03-8e5c04606519.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2342-da4956f7-cd3f-4122-be03-8e5c04606519.txn deleted file mode 100644 index 764e572a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2342-da4956f7-cd3f-4122-be03-8e5c04606519.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2343-82bf2753-cff3-45f2-a538-c309d98a8e3d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2343-82bf2753-cff3-45f2-a538-c309d98a8e3d.txn deleted file mode 100644 index 2a557b599..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2343-82bf2753-cff3-45f2-a538-c309d98a8e3d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2344-c54941db-787f-4324-b429-891292b56d41.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2344-c54941db-787f-4324-b429-891292b56d41.txn deleted file mode 100644 index f2f8c5ce1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2344-c54941db-787f-4324-b429-891292b56d41.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2345-96dc6056-6971-463d-8af3-de1bd22411c5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2345-96dc6056-6971-463d-8af3-de1bd22411c5.txn deleted file mode 100644 index 6f2db6516..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2345-96dc6056-6971-463d-8af3-de1bd22411c5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2346-bd4a60eb-49f2-4c6b-a860-4d580224aaf4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2346-bd4a60eb-49f2-4c6b-a860-4d580224aaf4.txn deleted file mode 100644 index 63a40fbcd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2346-bd4a60eb-49f2-4c6b-a860-4d580224aaf4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2347-b8b1d2ba-27b1-4b87-8fcc-8e986dab83af.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2347-b8b1d2ba-27b1-4b87-8fcc-8e986dab83af.txn deleted file mode 100644 index 112f8d80f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2347-b8b1d2ba-27b1-4b87-8fcc-8e986dab83af.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2348-aaabd840-47f1-4f24-be2f-a30c647c56ef.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2348-aaabd840-47f1-4f24-be2f-a30c647c56ef.txn deleted file mode 100644 index fd3f640cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2348-aaabd840-47f1-4f24-be2f-a30c647c56ef.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2349-765afd3d-e747-4d54-93ae-aec0ce0e06be.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2349-765afd3d-e747-4d54-93ae-aec0ce0e06be.txn deleted file mode 100644 index c36b204eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2349-765afd3d-e747-4d54-93ae-aec0ce0e06be.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/235-4840af10-93eb-4ddc-822c-b4c2eba9e25d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/235-4840af10-93eb-4ddc-822c-b4c2eba9e25d.txn deleted file mode 100644 index f6c3082bd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/235-4840af10-93eb-4ddc-822c-b4c2eba9e25d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2350-d4323ac2-dbdb-4bff-bdbc-1527829ccd3e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2350-d4323ac2-dbdb-4bff-bdbc-1527829ccd3e.txn deleted file mode 100644 index 9ee6f214b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2350-d4323ac2-dbdb-4bff-bdbc-1527829ccd3e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2351-6358c231-5d9f-4ee9-8884-9b0e051a5f4f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2351-6358c231-5d9f-4ee9-8884-9b0e051a5f4f.txn deleted file mode 100644 index 8e0d7bbf1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2351-6358c231-5d9f-4ee9-8884-9b0e051a5f4f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2352-b4122dd5-cd25-444d-9724-1fa9cba84222.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2352-b4122dd5-cd25-444d-9724-1fa9cba84222.txn deleted file mode 100644 index 203eb10ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2352-b4122dd5-cd25-444d-9724-1fa9cba84222.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2353-9a3df2ea-b9e7-4e08-a1b1-16d7477b7289.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2353-9a3df2ea-b9e7-4e08-a1b1-16d7477b7289.txn deleted file mode 100644 index 29adbf418..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2353-9a3df2ea-b9e7-4e08-a1b1-16d7477b7289.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2354-f32fec2c-bf88-4b92-8407-b058ad8b1246.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2354-f32fec2c-bf88-4b92-8407-b058ad8b1246.txn deleted file mode 100644 index f3a587229..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2354-f32fec2c-bf88-4b92-8407-b058ad8b1246.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2355-efc80743-5f2b-4f5f-bfdb-56d699c846ce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2355-efc80743-5f2b-4f5f-bfdb-56d699c846ce.txn deleted file mode 100644 index 12044820d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2355-efc80743-5f2b-4f5f-bfdb-56d699c846ce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2356-2e91cfdd-b551-48e3-92e9-3e030935abb2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2356-2e91cfdd-b551-48e3-92e9-3e030935abb2.txn deleted file mode 100644 index ce20cc376..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2356-2e91cfdd-b551-48e3-92e9-3e030935abb2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2357-dfd2f33a-e973-42ba-8fa8-779d679a25c3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2357-dfd2f33a-e973-42ba-8fa8-779d679a25c3.txn deleted file mode 100644 index 0db23a5dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2357-dfd2f33a-e973-42ba-8fa8-779d679a25c3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2358-349c8e53-e28c-484b-8d15-4560382f56eb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2358-349c8e53-e28c-484b-8d15-4560382f56eb.txn deleted file mode 100644 index a1cb0f6b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2358-349c8e53-e28c-484b-8d15-4560382f56eb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2359-5f730344-6f37-4c63-8ca8-dad1ec162a4b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2359-5f730344-6f37-4c63-8ca8-dad1ec162a4b.txn deleted file mode 100644 index 73d9854a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2359-5f730344-6f37-4c63-8ca8-dad1ec162a4b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/236-b888909b-d2da-4a43-917a-dcb183c62f01.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/236-b888909b-d2da-4a43-917a-dcb183c62f01.txn deleted file mode 100644 index 500de1748..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/236-b888909b-d2da-4a43-917a-dcb183c62f01.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2360-719fa953-02ee-4305-9a5b-79626c7812c9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2360-719fa953-02ee-4305-9a5b-79626c7812c9.txn deleted file mode 100644 index a6bdc434f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2360-719fa953-02ee-4305-9a5b-79626c7812c9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2361-7e0fee7a-3c9f-4020-bf00-5e278e395514.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2361-7e0fee7a-3c9f-4020-bf00-5e278e395514.txn deleted file mode 100644 index 0461176d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2361-7e0fee7a-3c9f-4020-bf00-5e278e395514.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2362-2e91db04-47d4-4838-bc0f-7f1473f4f371.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2362-2e91db04-47d4-4838-bc0f-7f1473f4f371.txn deleted file mode 100644 index c7f3900d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2362-2e91db04-47d4-4838-bc0f-7f1473f4f371.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2363-bb9982cf-05e4-403f-aa2e-26251ee933e2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2363-bb9982cf-05e4-403f-aa2e-26251ee933e2.txn deleted file mode 100644 index 8e9b036e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2363-bb9982cf-05e4-403f-aa2e-26251ee933e2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2364-7d2d71c1-53e7-4bf5-8f1b-db79b5caf49c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2364-7d2d71c1-53e7-4bf5-8f1b-db79b5caf49c.txn deleted file mode 100644 index e549f5e87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2364-7d2d71c1-53e7-4bf5-8f1b-db79b5caf49c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2365-255717bc-6e6c-4a3e-9c75-ea1d8c8f5e68.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2365-255717bc-6e6c-4a3e-9c75-ea1d8c8f5e68.txn deleted file mode 100644 index 7436e968b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2365-255717bc-6e6c-4a3e-9c75-ea1d8c8f5e68.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2366-f78c0f00-fe1c-4fc3-a0ed-ce05fca9bb1a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2366-f78c0f00-fe1c-4fc3-a0ed-ce05fca9bb1a.txn deleted file mode 100644 index d7c244a75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2366-f78c0f00-fe1c-4fc3-a0ed-ce05fca9bb1a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2367-29c35419-1a9f-4be3-a649-79aaa85568da.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2367-29c35419-1a9f-4be3-a649-79aaa85568da.txn deleted file mode 100644 index 93ce906f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2367-29c35419-1a9f-4be3-a649-79aaa85568da.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2368-0812a200-51ae-4e06-87d3-e927df3dfd52.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2368-0812a200-51ae-4e06-87d3-e927df3dfd52.txn deleted file mode 100644 index dac9d7cdc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2368-0812a200-51ae-4e06-87d3-e927df3dfd52.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2369-22bdbb96-3eae-47be-b699-71a56858ea67.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2369-22bdbb96-3eae-47be-b699-71a56858ea67.txn deleted file mode 100644 index 607e58b75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2369-22bdbb96-3eae-47be-b699-71a56858ea67.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/237-1ffe317e-7c3d-479d-8695-1c17cb7540cc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/237-1ffe317e-7c3d-479d-8695-1c17cb7540cc.txn deleted file mode 100644 index 0a97afaee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/237-1ffe317e-7c3d-479d-8695-1c17cb7540cc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2370-06478980-adfa-4bb2-be4b-30bdacab830b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2370-06478980-adfa-4bb2-be4b-30bdacab830b.txn deleted file mode 100644 index 4f30ee534..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2370-06478980-adfa-4bb2-be4b-30bdacab830b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2371-9adc944c-3342-4a2e-ae3c-4cfe5f29bd40.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2371-9adc944c-3342-4a2e-ae3c-4cfe5f29bd40.txn deleted file mode 100644 index 08c0c0cd4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2371-9adc944c-3342-4a2e-ae3c-4cfe5f29bd40.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2372-bdcde32b-37da-4914-84ae-3adaf34e2430.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2372-bdcde32b-37da-4914-84ae-3adaf34e2430.txn deleted file mode 100644 index 8a45f2af6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2372-bdcde32b-37da-4914-84ae-3adaf34e2430.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2373-6c947595-d427-4bad-a695-8bebcc8dbdea.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2373-6c947595-d427-4bad-a695-8bebcc8dbdea.txn deleted file mode 100644 index da14edde9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2373-6c947595-d427-4bad-a695-8bebcc8dbdea.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2374-bc5d36b5-d5d3-412d-b00b-56e74798b7f8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2374-bc5d36b5-d5d3-412d-b00b-56e74798b7f8.txn deleted file mode 100644 index 9ee5b988e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2374-bc5d36b5-d5d3-412d-b00b-56e74798b7f8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2375-e01fe386-9714-4ef3-bf76-b28bad17bc0b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2375-e01fe386-9714-4ef3-bf76-b28bad17bc0b.txn deleted file mode 100644 index 475fb28d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2375-e01fe386-9714-4ef3-bf76-b28bad17bc0b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2376-593a7999-ac0d-4e73-9124-33f9e4354a8d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2376-593a7999-ac0d-4e73-9124-33f9e4354a8d.txn deleted file mode 100644 index 7f249c025..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2376-593a7999-ac0d-4e73-9124-33f9e4354a8d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2377-be03c192-3bff-4444-a88c-1dc98ad2ff02.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2377-be03c192-3bff-4444-a88c-1dc98ad2ff02.txn deleted file mode 100644 index b4ceabf8b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2377-be03c192-3bff-4444-a88c-1dc98ad2ff02.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2378-25979678-9f79-42f7-99bc-ab5d04030026.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2378-25979678-9f79-42f7-99bc-ab5d04030026.txn deleted file mode 100644 index ed1a652f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2378-25979678-9f79-42f7-99bc-ab5d04030026.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2379-c7b82141-2ccf-4fc7-af1e-b015ce9ae4eb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2379-c7b82141-2ccf-4fc7-af1e-b015ce9ae4eb.txn deleted file mode 100644 index 0d08aa7a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2379-c7b82141-2ccf-4fc7-af1e-b015ce9ae4eb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/238-f7fd1f81-0548-435f-baf4-c594e343b032.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/238-f7fd1f81-0548-435f-baf4-c594e343b032.txn deleted file mode 100644 index 1e77177d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/238-f7fd1f81-0548-435f-baf4-c594e343b032.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2380-4fa96b65-239b-4e3f-99d8-50f872734b1c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2380-4fa96b65-239b-4e3f-99d8-50f872734b1c.txn deleted file mode 100644 index 062f33a57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2380-4fa96b65-239b-4e3f-99d8-50f872734b1c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2381-d6849544-8d57-4d96-8194-6ec0e1ae167f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2381-d6849544-8d57-4d96-8194-6ec0e1ae167f.txn deleted file mode 100644 index 2361a2f92..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2381-d6849544-8d57-4d96-8194-6ec0e1ae167f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2382-e30ff20e-6a0f-441a-99b7-c4a94a4c37b9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2382-e30ff20e-6a0f-441a-99b7-c4a94a4c37b9.txn deleted file mode 100644 index aef24ec3a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2382-e30ff20e-6a0f-441a-99b7-c4a94a4c37b9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2383-0dcfa532-35af-423f-ab9c-fbf2f7a64540.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2383-0dcfa532-35af-423f-ab9c-fbf2f7a64540.txn deleted file mode 100644 index 996c8793e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2383-0dcfa532-35af-423f-ab9c-fbf2f7a64540.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2384-8b2d484e-4b71-4874-91d4-7beb4da5a8f6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2384-8b2d484e-4b71-4874-91d4-7beb4da5a8f6.txn deleted file mode 100644 index b124de1bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2384-8b2d484e-4b71-4874-91d4-7beb4da5a8f6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2385-28998731-3542-496b-ac74-4db3d3b21399.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2385-28998731-3542-496b-ac74-4db3d3b21399.txn deleted file mode 100644 index 024fa66aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2385-28998731-3542-496b-ac74-4db3d3b21399.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2386-9b1c6796-39d1-4a69-9746-ca9a2c9385e8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2386-9b1c6796-39d1-4a69-9746-ca9a2c9385e8.txn deleted file mode 100644 index 3f23c71d6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2386-9b1c6796-39d1-4a69-9746-ca9a2c9385e8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2387-1cc140b4-2800-42b6-b08d-dece9551332e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2387-1cc140b4-2800-42b6-b08d-dece9551332e.txn deleted file mode 100644 index b7c114219..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2387-1cc140b4-2800-42b6-b08d-dece9551332e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2388-0b508116-378b-4fe5-a940-d8bb90fe12ab.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2388-0b508116-378b-4fe5-a940-d8bb90fe12ab.txn deleted file mode 100644 index cdd8e9f88..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2388-0b508116-378b-4fe5-a940-d8bb90fe12ab.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2389-44735b0f-87f7-4356-b3ab-184230242b05.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2389-44735b0f-87f7-4356-b3ab-184230242b05.txn deleted file mode 100644 index 901f3197e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2389-44735b0f-87f7-4356-b3ab-184230242b05.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/239-06e29753-11b0-4a43-98ec-8afae90a5e90.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/239-06e29753-11b0-4a43-98ec-8afae90a5e90.txn deleted file mode 100644 index b0a6ee99c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/239-06e29753-11b0-4a43-98ec-8afae90a5e90.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2390-5206c876-d77d-404c-82ca-10b407b720cb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2390-5206c876-d77d-404c-82ca-10b407b720cb.txn deleted file mode 100644 index f16b7a1ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2390-5206c876-d77d-404c-82ca-10b407b720cb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2391-1ae500d6-b30b-4f35-a381-cca4cb9d814e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2391-1ae500d6-b30b-4f35-a381-cca4cb9d814e.txn deleted file mode 100644 index bdbdc2741..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2391-1ae500d6-b30b-4f35-a381-cca4cb9d814e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2392-11c5b07e-cd49-40d3-bea6-d00a9777a568.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2392-11c5b07e-cd49-40d3-bea6-d00a9777a568.txn deleted file mode 100644 index 066eafa3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2392-11c5b07e-cd49-40d3-bea6-d00a9777a568.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2393-a108e96b-b780-49c4-9ee5-acce0febf74a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2393-a108e96b-b780-49c4-9ee5-acce0febf74a.txn deleted file mode 100644 index 6a6503d13..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2393-a108e96b-b780-49c4-9ee5-acce0febf74a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2394-860fbd8f-cf87-4fca-908e-874863e72a48.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2394-860fbd8f-cf87-4fca-908e-874863e72a48.txn deleted file mode 100644 index d6348f461..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2394-860fbd8f-cf87-4fca-908e-874863e72a48.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2395-5892738c-2577-4691-8fd1-deab93094a73.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2395-5892738c-2577-4691-8fd1-deab93094a73.txn deleted file mode 100644 index 0ce3426d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2395-5892738c-2577-4691-8fd1-deab93094a73.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2396-be2497ab-8d36-40b2-bfee-7e4fe2de5822.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2396-be2497ab-8d36-40b2-bfee-7e4fe2de5822.txn deleted file mode 100644 index 6be202bbf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2396-be2497ab-8d36-40b2-bfee-7e4fe2de5822.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2397-c7edf959-6118-4ed6-8ac0-6c577d7dbd3d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2397-c7edf959-6118-4ed6-8ac0-6c577d7dbd3d.txn deleted file mode 100644 index 629eaa255..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2397-c7edf959-6118-4ed6-8ac0-6c577d7dbd3d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2398-4cf1a180-5a36-4ee0-a885-c83432d34e45.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2398-4cf1a180-5a36-4ee0-a885-c83432d34e45.txn deleted file mode 100644 index e79a2d074..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2398-4cf1a180-5a36-4ee0-a885-c83432d34e45.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2399-88fc0674-ac41-42f1-926b-77fb2ec51bc2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2399-88fc0674-ac41-42f1-926b-77fb2ec51bc2.txn deleted file mode 100644 index 3292862dd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2399-88fc0674-ac41-42f1-926b-77fb2ec51bc2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/24-b6c23287-b3a1-4515-88da-af596fb43c2a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/24-b6c23287-b3a1-4515-88da-af596fb43c2a.txn deleted file mode 100644 index 83ba7b2b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/24-b6c23287-b3a1-4515-88da-af596fb43c2a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/240-03c63a2a-1b51-4ac0-896d-cb333b044113.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/240-03c63a2a-1b51-4ac0-896d-cb333b044113.txn deleted file mode 100644 index b48c049f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/240-03c63a2a-1b51-4ac0-896d-cb333b044113.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2400-6ec3ed8c-3f92-4935-888b-54bf5101431c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2400-6ec3ed8c-3f92-4935-888b-54bf5101431c.txn deleted file mode 100644 index 9bfcd1ec9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2400-6ec3ed8c-3f92-4935-888b-54bf5101431c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2401-95a5966b-1c0f-49db-a258-ca955b4a0bf0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2401-95a5966b-1c0f-49db-a258-ca955b4a0bf0.txn deleted file mode 100644 index 404cf1625..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2401-95a5966b-1c0f-49db-a258-ca955b4a0bf0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2402-93c0c0eb-2eb5-4cb7-8c1d-19560da0ec20.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2402-93c0c0eb-2eb5-4cb7-8c1d-19560da0ec20.txn deleted file mode 100644 index e4b01bb1d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2402-93c0c0eb-2eb5-4cb7-8c1d-19560da0ec20.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2403-a171ad30-0927-408a-bc1a-5eb3db2e6533.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2403-a171ad30-0927-408a-bc1a-5eb3db2e6533.txn deleted file mode 100644 index 72eab8604..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2403-a171ad30-0927-408a-bc1a-5eb3db2e6533.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2404-5a0d7b70-bbb3-4cef-884d-315eb650c6f1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2404-5a0d7b70-bbb3-4cef-884d-315eb650c6f1.txn deleted file mode 100644 index 45de62ffc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2404-5a0d7b70-bbb3-4cef-884d-315eb650c6f1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2405-a9518954-b7b8-4745-a5b9-3cdc75ed3e4b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2405-a9518954-b7b8-4745-a5b9-3cdc75ed3e4b.txn deleted file mode 100644 index 3eab267a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2405-a9518954-b7b8-4745-a5b9-3cdc75ed3e4b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2406-3e0ad35f-f24d-4809-9cd9-922b4ece23f1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2406-3e0ad35f-f24d-4809-9cd9-922b4ece23f1.txn deleted file mode 100644 index 107f40441..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2406-3e0ad35f-f24d-4809-9cd9-922b4ece23f1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2407-7809a547-1f58-4014-b130-ef65bccecd5b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2407-7809a547-1f58-4014-b130-ef65bccecd5b.txn deleted file mode 100644 index 07f427622..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2407-7809a547-1f58-4014-b130-ef65bccecd5b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2408-855ee967-3a3a-4278-9fed-c1f80cd6cd55.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2408-855ee967-3a3a-4278-9fed-c1f80cd6cd55.txn deleted file mode 100644 index 04e806383..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2408-855ee967-3a3a-4278-9fed-c1f80cd6cd55.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2409-af65b0ed-6583-4d3a-bd1e-8f12a922f16c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2409-af65b0ed-6583-4d3a-bd1e-8f12a922f16c.txn deleted file mode 100644 index a93b4dd0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2409-af65b0ed-6583-4d3a-bd1e-8f12a922f16c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/241-c0ad8009-d214-4ca0-b746-f367ee9125ec.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/241-c0ad8009-d214-4ca0-b746-f367ee9125ec.txn deleted file mode 100644 index 139aedd5a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/241-c0ad8009-d214-4ca0-b746-f367ee9125ec.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2410-b71ee267-5f0f-4e7a-a937-d50d82ff8817.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2410-b71ee267-5f0f-4e7a-a937-d50d82ff8817.txn deleted file mode 100644 index ff06bb36b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2410-b71ee267-5f0f-4e7a-a937-d50d82ff8817.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2411-953b3a83-675c-4ae5-b4c2-ec8bac5a31ab.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2411-953b3a83-675c-4ae5-b4c2-ec8bac5a31ab.txn deleted file mode 100644 index e94b396d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2411-953b3a83-675c-4ae5-b4c2-ec8bac5a31ab.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2412-577d5d1b-f244-4c81-8f68-2e8819fd816b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2412-577d5d1b-f244-4c81-8f68-2e8819fd816b.txn deleted file mode 100644 index 1803101c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2412-577d5d1b-f244-4c81-8f68-2e8819fd816b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2413-b809a5bd-014c-4ae5-99b5-cb761433f23d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2413-b809a5bd-014c-4ae5-99b5-cb761433f23d.txn deleted file mode 100644 index 8649cc0ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2413-b809a5bd-014c-4ae5-99b5-cb761433f23d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2414-e1f460c9-206c-45df-8aab-c284ce1e5ba1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2414-e1f460c9-206c-45df-8aab-c284ce1e5ba1.txn deleted file mode 100644 index 1de0d3f23..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2414-e1f460c9-206c-45df-8aab-c284ce1e5ba1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2415-728e93f2-bcca-45cd-8058-af99b34b5359.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2415-728e93f2-bcca-45cd-8058-af99b34b5359.txn deleted file mode 100644 index c3f05f3a6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2415-728e93f2-bcca-45cd-8058-af99b34b5359.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2416-00b3fab6-be9f-4050-9ca8-c9f424e9cb27.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2416-00b3fab6-be9f-4050-9ca8-c9f424e9cb27.txn deleted file mode 100644 index 24178b63a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2416-00b3fab6-be9f-4050-9ca8-c9f424e9cb27.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2417-d029f40c-f75d-455e-bcca-b46d90319c16.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2417-d029f40c-f75d-455e-bcca-b46d90319c16.txn deleted file mode 100644 index e24538165..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2417-d029f40c-f75d-455e-bcca-b46d90319c16.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2418-9ffe15b9-ab1b-49ee-961f-c33935194241.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2418-9ffe15b9-ab1b-49ee-961f-c33935194241.txn deleted file mode 100644 index 3db92dd5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2418-9ffe15b9-ab1b-49ee-961f-c33935194241.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2419-8cb9f831-30d3-4e27-bc3b-68de8577a05f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2419-8cb9f831-30d3-4e27-bc3b-68de8577a05f.txn deleted file mode 100644 index b18f74484..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2419-8cb9f831-30d3-4e27-bc3b-68de8577a05f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/242-9f2995dd-9d59-456d-bebc-123c4439d46b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/242-9f2995dd-9d59-456d-bebc-123c4439d46b.txn deleted file mode 100644 index c243aca07..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/242-9f2995dd-9d59-456d-bebc-123c4439d46b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2420-6355fe5a-23f1-420e-b073-3c57ca6fa911.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2420-6355fe5a-23f1-420e-b073-3c57ca6fa911.txn deleted file mode 100644 index 07d7f3e0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2420-6355fe5a-23f1-420e-b073-3c57ca6fa911.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2421-98fd63a5-b3d4-4d0b-8ef5-f990685183ed.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2421-98fd63a5-b3d4-4d0b-8ef5-f990685183ed.txn deleted file mode 100644 index e0f657dd9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2421-98fd63a5-b3d4-4d0b-8ef5-f990685183ed.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2422-aa69b625-3e40-4f57-a13f-e34372dfcd48.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2422-aa69b625-3e40-4f57-a13f-e34372dfcd48.txn deleted file mode 100644 index 73cecb94a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2422-aa69b625-3e40-4f57-a13f-e34372dfcd48.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2423-03131ca0-33bd-4a9d-9bfc-9c487e76089a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2423-03131ca0-33bd-4a9d-9bfc-9c487e76089a.txn deleted file mode 100644 index c17cf0c27..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2423-03131ca0-33bd-4a9d-9bfc-9c487e76089a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2424-f32c5c78-25a3-4e0f-aa4c-871780939226.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2424-f32c5c78-25a3-4e0f-aa4c-871780939226.txn deleted file mode 100644 index 8d16c2deb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2424-f32c5c78-25a3-4e0f-aa4c-871780939226.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2425-bfbed356-1213-4649-a1de-76448918c603.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2425-bfbed356-1213-4649-a1de-76448918c603.txn deleted file mode 100644 index 80a2c3ab8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2425-bfbed356-1213-4649-a1de-76448918c603.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2426-04020491-6273-40a5-8ba3-b6df912072c2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2426-04020491-6273-40a5-8ba3-b6df912072c2.txn deleted file mode 100644 index f75ae8049..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2426-04020491-6273-40a5-8ba3-b6df912072c2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2427-41d3a5c0-dc36-4df5-9b9d-a3ace0696bb7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2427-41d3a5c0-dc36-4df5-9b9d-a3ace0696bb7.txn deleted file mode 100644 index 5bd386bd0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2427-41d3a5c0-dc36-4df5-9b9d-a3ace0696bb7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2428-6d5fb074-c885-4c52-81b6-4c7d7d9577f3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2428-6d5fb074-c885-4c52-81b6-4c7d7d9577f3.txn deleted file mode 100644 index 5c240b388..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2428-6d5fb074-c885-4c52-81b6-4c7d7d9577f3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2429-a268cc0f-93c5-4e61-aef4-05619bff2e47.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2429-a268cc0f-93c5-4e61-aef4-05619bff2e47.txn deleted file mode 100644 index d81004981..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2429-a268cc0f-93c5-4e61-aef4-05619bff2e47.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/243-0fb7f863-5948-4a42-8ec2-d0fb745e748c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/243-0fb7f863-5948-4a42-8ec2-d0fb745e748c.txn deleted file mode 100644 index 1bd08048f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/243-0fb7f863-5948-4a42-8ec2-d0fb745e748c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2430-88e5fd32-ec97-4cf7-8b1a-327af091bdea.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2430-88e5fd32-ec97-4cf7-8b1a-327af091bdea.txn deleted file mode 100644 index 4c9f7b187..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2430-88e5fd32-ec97-4cf7-8b1a-327af091bdea.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2431-8e74160e-466e-4c76-b37a-bdf22ca4327c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2431-8e74160e-466e-4c76-b37a-bdf22ca4327c.txn deleted file mode 100644 index d502fbe22..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2431-8e74160e-466e-4c76-b37a-bdf22ca4327c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2432-a5846676-27c5-4bdd-89a9-87abe3557fc4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2432-a5846676-27c5-4bdd-89a9-87abe3557fc4.txn deleted file mode 100644 index e0b0d0557..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2432-a5846676-27c5-4bdd-89a9-87abe3557fc4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2433-ea65b660-4fad-49a8-b57a-ca675a6d5c04.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2433-ea65b660-4fad-49a8-b57a-ca675a6d5c04.txn deleted file mode 100644 index 2404c64c3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2433-ea65b660-4fad-49a8-b57a-ca675a6d5c04.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2434-d8d24197-d4ac-4a2e-a0d0-a0a3cd69be09.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2434-d8d24197-d4ac-4a2e-a0d0-a0a3cd69be09.txn deleted file mode 100644 index debcdb411..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2434-d8d24197-d4ac-4a2e-a0d0-a0a3cd69be09.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2435-03d3e9af-8738-483c-a2eb-bad87e03ed25.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2435-03d3e9af-8738-483c-a2eb-bad87e03ed25.txn deleted file mode 100644 index 58b8bfa06..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2435-03d3e9af-8738-483c-a2eb-bad87e03ed25.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2436-14bef9a0-c6ca-4998-9902-95d6be4a652a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2436-14bef9a0-c6ca-4998-9902-95d6be4a652a.txn deleted file mode 100644 index d49800503..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2436-14bef9a0-c6ca-4998-9902-95d6be4a652a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2437-34d9bdaf-2e65-4978-9572-f7c33f81255b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2437-34d9bdaf-2e65-4978-9572-f7c33f81255b.txn deleted file mode 100644 index ff8d8f81e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2437-34d9bdaf-2e65-4978-9572-f7c33f81255b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2438-ef961617-534e-4210-a7ac-7542df9bec38.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2438-ef961617-534e-4210-a7ac-7542df9bec38.txn deleted file mode 100644 index 6ab283ee8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2438-ef961617-534e-4210-a7ac-7542df9bec38.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2439-28700f6b-56fe-4d19-aeff-4f2ae4802c76.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2439-28700f6b-56fe-4d19-aeff-4f2ae4802c76.txn deleted file mode 100644 index 387b263a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2439-28700f6b-56fe-4d19-aeff-4f2ae4802c76.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/244-2f000b9d-0846-4a7a-a8e4-ca0934b0ffa7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/244-2f000b9d-0846-4a7a-a8e4-ca0934b0ffa7.txn deleted file mode 100644 index 4d7f0f786..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/244-2f000b9d-0846-4a7a-a8e4-ca0934b0ffa7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2440-2c93e317-282c-4db1-ba02-c1e4eb278bf3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2440-2c93e317-282c-4db1-ba02-c1e4eb278bf3.txn deleted file mode 100644 index 90659d4d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2440-2c93e317-282c-4db1-ba02-c1e4eb278bf3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2441-3abccb36-9451-4cc8-a7b7-07d144da422d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2441-3abccb36-9451-4cc8-a7b7-07d144da422d.txn deleted file mode 100644 index 510c634f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2441-3abccb36-9451-4cc8-a7b7-07d144da422d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2442-c90ccc7f-7cd3-4756-a91b-a8fc7837cb10.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2442-c90ccc7f-7cd3-4756-a91b-a8fc7837cb10.txn deleted file mode 100644 index 1dcd2b4fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2442-c90ccc7f-7cd3-4756-a91b-a8fc7837cb10.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2443-b6d77146-07a0-494b-907b-ed70b34c8d68.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2443-b6d77146-07a0-494b-907b-ed70b34c8d68.txn deleted file mode 100644 index 923fe1fe1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2443-b6d77146-07a0-494b-907b-ed70b34c8d68.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2444-6def6547-d8d8-47d5-b7ba-8a4f7f2ab6a7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2444-6def6547-d8d8-47d5-b7ba-8a4f7f2ab6a7.txn deleted file mode 100644 index 4e94c693b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2444-6def6547-d8d8-47d5-b7ba-8a4f7f2ab6a7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2445-0e2ec7c6-4899-4044-9aad-1ef0c23f4114.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2445-0e2ec7c6-4899-4044-9aad-1ef0c23f4114.txn deleted file mode 100644 index efd5ed23b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2445-0e2ec7c6-4899-4044-9aad-1ef0c23f4114.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2446-ce0f7b03-8dfb-4230-8992-fe2979798773.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2446-ce0f7b03-8dfb-4230-8992-fe2979798773.txn deleted file mode 100644 index 7056e73ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2446-ce0f7b03-8dfb-4230-8992-fe2979798773.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2447-d8aa92f9-7e7d-49e2-a2dc-babbc8399393.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2447-d8aa92f9-7e7d-49e2-a2dc-babbc8399393.txn deleted file mode 100644 index 02e8d7bb3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2447-d8aa92f9-7e7d-49e2-a2dc-babbc8399393.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2448-f04acc67-603e-4a16-bc45-38bfa8dd65f9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2448-f04acc67-603e-4a16-bc45-38bfa8dd65f9.txn deleted file mode 100644 index f68d47796..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2448-f04acc67-603e-4a16-bc45-38bfa8dd65f9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2449-615cd02c-75c4-4a80-b6de-d30299eb9eb6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2449-615cd02c-75c4-4a80-b6de-d30299eb9eb6.txn deleted file mode 100644 index 4b01f71a0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2449-615cd02c-75c4-4a80-b6de-d30299eb9eb6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/245-db71a386-0118-4868-9786-06183546040c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/245-db71a386-0118-4868-9786-06183546040c.txn deleted file mode 100644 index 0af344b1b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/245-db71a386-0118-4868-9786-06183546040c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2450-197d06bf-5148-4069-a89d-5249bb9b099b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2450-197d06bf-5148-4069-a89d-5249bb9b099b.txn deleted file mode 100644 index 50a12628c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2450-197d06bf-5148-4069-a89d-5249bb9b099b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2451-60640df8-9509-4fbd-a2a1-67cb208a4b52.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2451-60640df8-9509-4fbd-a2a1-67cb208a4b52.txn deleted file mode 100644 index ab37565a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2451-60640df8-9509-4fbd-a2a1-67cb208a4b52.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2452-8bf695cf-d31a-4dae-b052-b8b8c68c6026.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2452-8bf695cf-d31a-4dae-b052-b8b8c68c6026.txn deleted file mode 100644 index 61de9ee2d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2452-8bf695cf-d31a-4dae-b052-b8b8c68c6026.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2453-8b29c2d6-4061-4ca8-b08d-be4a9f86cce1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2453-8b29c2d6-4061-4ca8-b08d-be4a9f86cce1.txn deleted file mode 100644 index 4257ffee7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2453-8b29c2d6-4061-4ca8-b08d-be4a9f86cce1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2454-98f6b013-20b6-40dd-9f1a-9151ba1394a9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2454-98f6b013-20b6-40dd-9f1a-9151ba1394a9.txn deleted file mode 100644 index d25338647..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2454-98f6b013-20b6-40dd-9f1a-9151ba1394a9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2455-9a1501ed-843d-4765-ae01-504f7eb0c8e6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2455-9a1501ed-843d-4765-ae01-504f7eb0c8e6.txn deleted file mode 100644 index 68980f071..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2455-9a1501ed-843d-4765-ae01-504f7eb0c8e6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2456-2f26f8cf-25f2-4600-8f3f-24596591ed4c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2456-2f26f8cf-25f2-4600-8f3f-24596591ed4c.txn deleted file mode 100644 index eaa4fe9c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2456-2f26f8cf-25f2-4600-8f3f-24596591ed4c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2457-8c654048-4d6c-42a7-a9d8-c07ec921f333.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2457-8c654048-4d6c-42a7-a9d8-c07ec921f333.txn deleted file mode 100644 index 27c6c9a94..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2457-8c654048-4d6c-42a7-a9d8-c07ec921f333.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2458-dd986c67-899f-4555-9eff-10ae5d4254af.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2458-dd986c67-899f-4555-9eff-10ae5d4254af.txn deleted file mode 100644 index f17f59932..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2458-dd986c67-899f-4555-9eff-10ae5d4254af.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2459-a9550ff7-584c-487e-b583-c79c50fcebb4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2459-a9550ff7-584c-487e-b583-c79c50fcebb4.txn deleted file mode 100644 index 4c79c73c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2459-a9550ff7-584c-487e-b583-c79c50fcebb4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/246-3a0f9ad9-cc25-425d-85a2-d6c43b9b7a97.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/246-3a0f9ad9-cc25-425d-85a2-d6c43b9b7a97.txn deleted file mode 100644 index 9142e1136..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/246-3a0f9ad9-cc25-425d-85a2-d6c43b9b7a97.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2460-5bb80470-a6e0-4d6d-81b4-f7872e603d4d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2460-5bb80470-a6e0-4d6d-81b4-f7872e603d4d.txn deleted file mode 100644 index ef576513b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2460-5bb80470-a6e0-4d6d-81b4-f7872e603d4d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2461-bedac126-e81a-432d-97f6-e781550ff60f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2461-bedac126-e81a-432d-97f6-e781550ff60f.txn deleted file mode 100644 index 57a83912d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2461-bedac126-e81a-432d-97f6-e781550ff60f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2462-5c546627-1e94-44e3-9867-93b4dca794aa.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2462-5c546627-1e94-44e3-9867-93b4dca794aa.txn deleted file mode 100644 index 6f98b3b26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2462-5c546627-1e94-44e3-9867-93b4dca794aa.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2463-ed647ca1-689e-49bc-8405-48f027d9e3ca.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2463-ed647ca1-689e-49bc-8405-48f027d9e3ca.txn deleted file mode 100644 index 6ffcef9ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2463-ed647ca1-689e-49bc-8405-48f027d9e3ca.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2464-ec5a8703-bf88-4259-ab26-5e762fdf5ad0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2464-ec5a8703-bf88-4259-ab26-5e762fdf5ad0.txn deleted file mode 100644 index 979cdb41b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2464-ec5a8703-bf88-4259-ab26-5e762fdf5ad0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2465-07379040-ae6c-4d06-a117-7ba25f156317.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2465-07379040-ae6c-4d06-a117-7ba25f156317.txn deleted file mode 100644 index 7993dc5f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2465-07379040-ae6c-4d06-a117-7ba25f156317.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2466-24501f6d-1246-4066-b990-9e65c276d4cf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2466-24501f6d-1246-4066-b990-9e65c276d4cf.txn deleted file mode 100644 index 92578a55a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2466-24501f6d-1246-4066-b990-9e65c276d4cf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2467-646078f4-3e0c-4097-ab4d-c5fa3b875791.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2467-646078f4-3e0c-4097-ab4d-c5fa3b875791.txn deleted file mode 100644 index 935ccb56b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2467-646078f4-3e0c-4097-ab4d-c5fa3b875791.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2468-22531945-25ce-4170-a6dc-c768312eba1a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2468-22531945-25ce-4170-a6dc-c768312eba1a.txn deleted file mode 100644 index 65bd14aba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2468-22531945-25ce-4170-a6dc-c768312eba1a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2469-834bc0fe-c4bf-4a4a-9324-dd8a04345b99.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2469-834bc0fe-c4bf-4a4a-9324-dd8a04345b99.txn deleted file mode 100644 index 177597c09..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2469-834bc0fe-c4bf-4a4a-9324-dd8a04345b99.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/247-f425b8a0-c9f1-47c2-aabb-bd6dc111d6d5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/247-f425b8a0-c9f1-47c2-aabb-bd6dc111d6d5.txn deleted file mode 100644 index a8c0fb7c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/247-f425b8a0-c9f1-47c2-aabb-bd6dc111d6d5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2470-aeff8977-c94a-4c48-bee5-9f9176061b5c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2470-aeff8977-c94a-4c48-bee5-9f9176061b5c.txn deleted file mode 100644 index 0f8ef51c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2470-aeff8977-c94a-4c48-bee5-9f9176061b5c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2471-a54e5c98-9214-4c23-b006-b5cb016b92c9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2471-a54e5c98-9214-4c23-b006-b5cb016b92c9.txn deleted file mode 100644 index c16266ce3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2471-a54e5c98-9214-4c23-b006-b5cb016b92c9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2472-eda7596b-34db-48c2-a029-2592d3191031.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2472-eda7596b-34db-48c2-a029-2592d3191031.txn deleted file mode 100644 index cfbd90ad7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2472-eda7596b-34db-48c2-a029-2592d3191031.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2473-c4e17dd0-83be-46e0-952d-f526687c6df0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2473-c4e17dd0-83be-46e0-952d-f526687c6df0.txn deleted file mode 100644 index 90eb5248a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2473-c4e17dd0-83be-46e0-952d-f526687c6df0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2474-d34efca4-9ac0-4e8d-9842-ac958c5f59b0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2474-d34efca4-9ac0-4e8d-9842-ac958c5f59b0.txn deleted file mode 100644 index 341dc56bd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2474-d34efca4-9ac0-4e8d-9842-ac958c5f59b0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2475-8d2973bf-a85a-4709-a8a8-7d680097ec30.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2475-8d2973bf-a85a-4709-a8a8-7d680097ec30.txn deleted file mode 100644 index 4da1390a6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2475-8d2973bf-a85a-4709-a8a8-7d680097ec30.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2476-db1ce6a8-8d86-4d26-981e-a00ad0ff3108.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2476-db1ce6a8-8d86-4d26-981e-a00ad0ff3108.txn deleted file mode 100644 index ebe3f6586..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2476-db1ce6a8-8d86-4d26-981e-a00ad0ff3108.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2477-d9d7f0a2-84ef-4daf-bd13-b950c002ade1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2477-d9d7f0a2-84ef-4daf-bd13-b950c002ade1.txn deleted file mode 100644 index 4f780cb7b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2477-d9d7f0a2-84ef-4daf-bd13-b950c002ade1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2478-5a6602de-61f3-4605-9a57-7b44f1f90750.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2478-5a6602de-61f3-4605-9a57-7b44f1f90750.txn deleted file mode 100644 index b48f54e1f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2478-5a6602de-61f3-4605-9a57-7b44f1f90750.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2479-f8e45bfc-cf8d-40d4-8da4-cd07e61a3a6f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2479-f8e45bfc-cf8d-40d4-8da4-cd07e61a3a6f.txn deleted file mode 100644 index 5eb7a2648..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2479-f8e45bfc-cf8d-40d4-8da4-cd07e61a3a6f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/248-d16fbc1d-7b89-4632-af1d-14c8309b49b3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/248-d16fbc1d-7b89-4632-af1d-14c8309b49b3.txn deleted file mode 100644 index 5dda717dd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/248-d16fbc1d-7b89-4632-af1d-14c8309b49b3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2480-2f4bc34f-8656-4057-b5c2-22385cf92439.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2480-2f4bc34f-8656-4057-b5c2-22385cf92439.txn deleted file mode 100644 index 1e67fec48..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2480-2f4bc34f-8656-4057-b5c2-22385cf92439.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2481-281c767e-352f-4c67-96f4-defa7355d219.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2481-281c767e-352f-4c67-96f4-defa7355d219.txn deleted file mode 100644 index 5977aee62..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2481-281c767e-352f-4c67-96f4-defa7355d219.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2482-782be707-e2ac-4ee2-a92b-321739678954.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2482-782be707-e2ac-4ee2-a92b-321739678954.txn deleted file mode 100644 index 7c7e06f40..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2482-782be707-e2ac-4ee2-a92b-321739678954.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2483-475f36c1-6866-47df-bd17-bb4268ffe2df.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2483-475f36c1-6866-47df-bd17-bb4268ffe2df.txn deleted file mode 100644 index 1e2347de7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2483-475f36c1-6866-47df-bd17-bb4268ffe2df.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2484-e411e5dc-2e86-4f34-aeb5-1d3dbd691339.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2484-e411e5dc-2e86-4f34-aeb5-1d3dbd691339.txn deleted file mode 100644 index f16bd8d7b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2484-e411e5dc-2e86-4f34-aeb5-1d3dbd691339.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2485-a877e663-56b5-4686-b63f-233836b0e29f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2485-a877e663-56b5-4686-b63f-233836b0e29f.txn deleted file mode 100644 index c8e208665..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2485-a877e663-56b5-4686-b63f-233836b0e29f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2486-712599a2-e805-4488-a709-f1a3c9c5d0ed.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2486-712599a2-e805-4488-a709-f1a3c9c5d0ed.txn deleted file mode 100644 index c036d165c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2486-712599a2-e805-4488-a709-f1a3c9c5d0ed.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2487-de23edc1-0e75-4e9b-9052-1b305594958b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2487-de23edc1-0e75-4e9b-9052-1b305594958b.txn deleted file mode 100644 index 7ec91de88..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2487-de23edc1-0e75-4e9b-9052-1b305594958b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2488-184af59b-cd22-4a7c-be1a-d771bc3d58bb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2488-184af59b-cd22-4a7c-be1a-d771bc3d58bb.txn deleted file mode 100644 index e26dabf6b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2488-184af59b-cd22-4a7c-be1a-d771bc3d58bb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2489-5aa99266-6daf-4d0f-a624-2c089c830b16.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2489-5aa99266-6daf-4d0f-a624-2c089c830b16.txn deleted file mode 100644 index 2b02d65a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2489-5aa99266-6daf-4d0f-a624-2c089c830b16.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/249-b713479c-7083-42d8-9340-2f57df4e3536.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/249-b713479c-7083-42d8-9340-2f57df4e3536.txn deleted file mode 100644 index 567c0e19d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/249-b713479c-7083-42d8-9340-2f57df4e3536.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2490-770a12ba-f118-4a06-857a-eaddfc7b8765.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2490-770a12ba-f118-4a06-857a-eaddfc7b8765.txn deleted file mode 100644 index 413ad3712..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2490-770a12ba-f118-4a06-857a-eaddfc7b8765.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2491-dc8de51e-5855-4cda-b566-1f7ad1abb7d9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2491-dc8de51e-5855-4cda-b566-1f7ad1abb7d9.txn deleted file mode 100644 index d46304707..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2491-dc8de51e-5855-4cda-b566-1f7ad1abb7d9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2492-d5824387-3eba-4b9d-9bae-fafdc2cdb7a8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2492-d5824387-3eba-4b9d-9bae-fafdc2cdb7a8.txn deleted file mode 100644 index 773129671..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2492-d5824387-3eba-4b9d-9bae-fafdc2cdb7a8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2493-110b84fa-02b9-41b5-86f9-fbfccfa0cc35.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2493-110b84fa-02b9-41b5-86f9-fbfccfa0cc35.txn deleted file mode 100644 index 9fdcd459c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2493-110b84fa-02b9-41b5-86f9-fbfccfa0cc35.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2494-625ead1b-e49d-4462-9ec9-a9588864c955.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2494-625ead1b-e49d-4462-9ec9-a9588864c955.txn deleted file mode 100644 index 8c58b52d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2494-625ead1b-e49d-4462-9ec9-a9588864c955.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2495-ae2dbf91-d2d1-45c7-a5b5-5cad6f132674.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2495-ae2dbf91-d2d1-45c7-a5b5-5cad6f132674.txn deleted file mode 100644 index e1d16ca66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2495-ae2dbf91-d2d1-45c7-a5b5-5cad6f132674.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2496-a6b0e427-eebd-4433-b69d-914e18166edc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2496-a6b0e427-eebd-4433-b69d-914e18166edc.txn deleted file mode 100644 index 8ec77ab91..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2496-a6b0e427-eebd-4433-b69d-914e18166edc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2497-4e09a2ce-f1f0-43ed-b36e-894888c9f7be.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2497-4e09a2ce-f1f0-43ed-b36e-894888c9f7be.txn deleted file mode 100644 index 33dfe3724..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2497-4e09a2ce-f1f0-43ed-b36e-894888c9f7be.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2498-531a74a4-0089-4960-a6b0-c89871ccade6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2498-531a74a4-0089-4960-a6b0-c89871ccade6.txn deleted file mode 100644 index 3ba4edb87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2498-531a74a4-0089-4960-a6b0-c89871ccade6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2499-e0f1dd2a-d102-4ea1-9705-6c011a2e989b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2499-e0f1dd2a-d102-4ea1-9705-6c011a2e989b.txn deleted file mode 100644 index c05f290b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2499-e0f1dd2a-d102-4ea1-9705-6c011a2e989b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/25-a2d103d8-fb96-4a98-8eda-f91d16663426.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/25-a2d103d8-fb96-4a98-8eda-f91d16663426.txn deleted file mode 100644 index 2402bfda7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/25-a2d103d8-fb96-4a98-8eda-f91d16663426.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/250-f836b302-6db5-429e-aa58-bb2a802634d3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/250-f836b302-6db5-429e-aa58-bb2a802634d3.txn deleted file mode 100644 index 390abe58e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/250-f836b302-6db5-429e-aa58-bb2a802634d3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2500-33e8799d-3c45-4d88-8ce7-f60f59849141.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2500-33e8799d-3c45-4d88-8ce7-f60f59849141.txn deleted file mode 100644 index 65344b774..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2500-33e8799d-3c45-4d88-8ce7-f60f59849141.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2501-712d0e8e-1431-4fc2-8169-14b3380022e9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2501-712d0e8e-1431-4fc2-8169-14b3380022e9.txn deleted file mode 100644 index bc554b479..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2501-712d0e8e-1431-4fc2-8169-14b3380022e9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2502-e813cdd9-6cb3-447e-bd4f-29425c516293.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2502-e813cdd9-6cb3-447e-bd4f-29425c516293.txn deleted file mode 100644 index 1c663002e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2502-e813cdd9-6cb3-447e-bd4f-29425c516293.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2503-54f50ed0-0a29-47b6-b8e8-c0383f010aac.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2503-54f50ed0-0a29-47b6-b8e8-c0383f010aac.txn deleted file mode 100644 index f97a39d1f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2503-54f50ed0-0a29-47b6-b8e8-c0383f010aac.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2504-22f99705-270d-4d33-a04e-36e6740e0b71.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2504-22f99705-270d-4d33-a04e-36e6740e0b71.txn deleted file mode 100644 index db6e889fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2504-22f99705-270d-4d33-a04e-36e6740e0b71.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2505-004c7075-91a4-4733-a7f2-9fd154766c12.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2505-004c7075-91a4-4733-a7f2-9fd154766c12.txn deleted file mode 100644 index 1bd04547d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2505-004c7075-91a4-4733-a7f2-9fd154766c12.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2506-10afc7e5-b2dd-4394-8f23-130d2fb0ebc5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2506-10afc7e5-b2dd-4394-8f23-130d2fb0ebc5.txn deleted file mode 100644 index 89d56992d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2506-10afc7e5-b2dd-4394-8f23-130d2fb0ebc5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2507-1ea5bbbb-4435-437f-81ac-f24968cb07c4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2507-1ea5bbbb-4435-437f-81ac-f24968cb07c4.txn deleted file mode 100644 index 6af43f2ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2507-1ea5bbbb-4435-437f-81ac-f24968cb07c4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2508-ec6fc2b9-81aa-4ac8-b60a-1a2bedc1aad9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2508-ec6fc2b9-81aa-4ac8-b60a-1a2bedc1aad9.txn deleted file mode 100644 index 3918d3919..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2508-ec6fc2b9-81aa-4ac8-b60a-1a2bedc1aad9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2509-7c1a0d77-1af4-42d9-9c0d-f5aeb87c8de2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2509-7c1a0d77-1af4-42d9-9c0d-f5aeb87c8de2.txn deleted file mode 100644 index 8f6f45722..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2509-7c1a0d77-1af4-42d9-9c0d-f5aeb87c8de2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/251-73d6a31b-9fd5-4ef3-b7ef-af0e7f5cea89.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/251-73d6a31b-9fd5-4ef3-b7ef-af0e7f5cea89.txn deleted file mode 100644 index f1c80487b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/251-73d6a31b-9fd5-4ef3-b7ef-af0e7f5cea89.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2510-b57fc186-97d5-40bb-bebf-ca12499f6c42.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2510-b57fc186-97d5-40bb-bebf-ca12499f6c42.txn deleted file mode 100644 index fcf2ee0e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2510-b57fc186-97d5-40bb-bebf-ca12499f6c42.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2511-aca76c39-a88e-47e2-8059-4157b6740e5c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2511-aca76c39-a88e-47e2-8059-4157b6740e5c.txn deleted file mode 100644 index a9a07ff80..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2511-aca76c39-a88e-47e2-8059-4157b6740e5c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2512-1cc4dbf3-565d-44ba-9a4d-286861ee70be.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2512-1cc4dbf3-565d-44ba-9a4d-286861ee70be.txn deleted file mode 100644 index f443378c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2512-1cc4dbf3-565d-44ba-9a4d-286861ee70be.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2513-94a05f32-5a41-471e-8a42-0304ba84e9dd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2513-94a05f32-5a41-471e-8a42-0304ba84e9dd.txn deleted file mode 100644 index 810121473..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2513-94a05f32-5a41-471e-8a42-0304ba84e9dd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2514-a3cd9282-f821-468b-8132-3f4bf6f83694.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2514-a3cd9282-f821-468b-8132-3f4bf6f83694.txn deleted file mode 100644 index 27b97d672..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2514-a3cd9282-f821-468b-8132-3f4bf6f83694.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2515-faea0ee8-76b5-43eb-8959-fc049d389d80.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2515-faea0ee8-76b5-43eb-8959-fc049d389d80.txn deleted file mode 100644 index 0eeb81e4d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2515-faea0ee8-76b5-43eb-8959-fc049d389d80.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2516-4efd60c7-31ab-469b-9213-e9132190ebae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2516-4efd60c7-31ab-469b-9213-e9132190ebae.txn deleted file mode 100644 index 4a94cdf4e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2516-4efd60c7-31ab-469b-9213-e9132190ebae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2517-03e1e58c-c578-460a-bd10-f97300411276.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2517-03e1e58c-c578-460a-bd10-f97300411276.txn deleted file mode 100644 index e407f0a6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2517-03e1e58c-c578-460a-bd10-f97300411276.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2518-ff64c092-3106-4317-8037-f065ed3b4d14.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2518-ff64c092-3106-4317-8037-f065ed3b4d14.txn deleted file mode 100644 index 5d557bc33..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2518-ff64c092-3106-4317-8037-f065ed3b4d14.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2519-065b85f2-1d98-4be1-9d5b-bb5b9b3bd6db.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2519-065b85f2-1d98-4be1-9d5b-bb5b9b3bd6db.txn deleted file mode 100644 index d6349c898..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2519-065b85f2-1d98-4be1-9d5b-bb5b9b3bd6db.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/252-c716b9d0-d6de-434c-b237-d3331cab60e9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/252-c716b9d0-d6de-434c-b237-d3331cab60e9.txn deleted file mode 100644 index 8ef7f7319..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/252-c716b9d0-d6de-434c-b237-d3331cab60e9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2520-9a60c648-7ae5-444b-9825-095a26c7d864.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2520-9a60c648-7ae5-444b-9825-095a26c7d864.txn deleted file mode 100644 index 0634402cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2520-9a60c648-7ae5-444b-9825-095a26c7d864.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2521-ceb41e0c-2bdd-4177-8c87-07b8c0d72784.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2521-ceb41e0c-2bdd-4177-8c87-07b8c0d72784.txn deleted file mode 100644 index 95d107f29..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2521-ceb41e0c-2bdd-4177-8c87-07b8c0d72784.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2522-d5a46812-b51c-4018-bf56-d758451cefeb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2522-d5a46812-b51c-4018-bf56-d758451cefeb.txn deleted file mode 100644 index 9724f19cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2522-d5a46812-b51c-4018-bf56-d758451cefeb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2523-6b0a626d-98ed-45a3-a221-60191b904ba1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2523-6b0a626d-98ed-45a3-a221-60191b904ba1.txn deleted file mode 100644 index abe799d90..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2523-6b0a626d-98ed-45a3-a221-60191b904ba1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2524-a4e2bae9-b81c-43bc-b3ed-a5799bdacfe9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2524-a4e2bae9-b81c-43bc-b3ed-a5799bdacfe9.txn deleted file mode 100644 index b48decbba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2524-a4e2bae9-b81c-43bc-b3ed-a5799bdacfe9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2525-c5381d81-980e-4bfa-933b-9c59a7fde7bd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2525-c5381d81-980e-4bfa-933b-9c59a7fde7bd.txn deleted file mode 100644 index 73fd693c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2525-c5381d81-980e-4bfa-933b-9c59a7fde7bd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2526-bf22e703-f965-45d7-9265-cce91c1117a7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2526-bf22e703-f965-45d7-9265-cce91c1117a7.txn deleted file mode 100644 index 1d26ab39b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2526-bf22e703-f965-45d7-9265-cce91c1117a7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2527-ccfcd92b-3aba-4b3d-b4dc-fbfa6878113d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2527-ccfcd92b-3aba-4b3d-b4dc-fbfa6878113d.txn deleted file mode 100644 index f9ea22128..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2527-ccfcd92b-3aba-4b3d-b4dc-fbfa6878113d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2528-90744050-e4d6-4b79-92a7-5b96323f9a1e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2528-90744050-e4d6-4b79-92a7-5b96323f9a1e.txn deleted file mode 100644 index 32aac7f45..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2528-90744050-e4d6-4b79-92a7-5b96323f9a1e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2529-1e4a4380-d48c-4307-8fff-03bc7f3396ef.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2529-1e4a4380-d48c-4307-8fff-03bc7f3396ef.txn deleted file mode 100644 index f00aa7db6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2529-1e4a4380-d48c-4307-8fff-03bc7f3396ef.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/253-d4096e5a-c598-4f5a-b113-25a62acce6e1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/253-d4096e5a-c598-4f5a-b113-25a62acce6e1.txn deleted file mode 100644 index c05c26e07..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/253-d4096e5a-c598-4f5a-b113-25a62acce6e1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2530-21c5c6f0-3a56-42a9-989d-6911bf2c2301.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2530-21c5c6f0-3a56-42a9-989d-6911bf2c2301.txn deleted file mode 100644 index 958a36ee7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2530-21c5c6f0-3a56-42a9-989d-6911bf2c2301.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2531-5e776aaa-0e3a-4da9-8b16-501fc2a8bc54.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2531-5e776aaa-0e3a-4da9-8b16-501fc2a8bc54.txn deleted file mode 100644 index 8b47ee366..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2531-5e776aaa-0e3a-4da9-8b16-501fc2a8bc54.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2532-5d3b020b-d4c1-4980-8bdd-4ab74b04fd11.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2532-5d3b020b-d4c1-4980-8bdd-4ab74b04fd11.txn deleted file mode 100644 index 9ab6dc8d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2532-5d3b020b-d4c1-4980-8bdd-4ab74b04fd11.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2533-ebfb8262-842c-4bd7-a37f-2f02448b34ec.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2533-ebfb8262-842c-4bd7-a37f-2f02448b34ec.txn deleted file mode 100644 index be9e94923..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2533-ebfb8262-842c-4bd7-a37f-2f02448b34ec.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2534-5af7eae3-5294-48ff-bf86-b5c1afd47130.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2534-5af7eae3-5294-48ff-bf86-b5c1afd47130.txn deleted file mode 100644 index c3c7e3844..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2534-5af7eae3-5294-48ff-bf86-b5c1afd47130.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2535-603ca010-ee33-40c1-99cf-778f7fb6839b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2535-603ca010-ee33-40c1-99cf-778f7fb6839b.txn deleted file mode 100644 index 68ff60000..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2535-603ca010-ee33-40c1-99cf-778f7fb6839b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2536-3b65c96f-49e9-4bed-8683-6441132cbc90.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2536-3b65c96f-49e9-4bed-8683-6441132cbc90.txn deleted file mode 100644 index eb17f99bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2536-3b65c96f-49e9-4bed-8683-6441132cbc90.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2537-b802bcb3-0b04-4adc-bc8f-f2a90863295d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2537-b802bcb3-0b04-4adc-bc8f-f2a90863295d.txn deleted file mode 100644 index 9272451b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2537-b802bcb3-0b04-4adc-bc8f-f2a90863295d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2538-9e4d3875-fd19-4dc2-9204-1cfbeea58029.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2538-9e4d3875-fd19-4dc2-9204-1cfbeea58029.txn deleted file mode 100644 index 37373cf75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2538-9e4d3875-fd19-4dc2-9204-1cfbeea58029.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2539-47c12987-3cec-4b01-a34e-952fab4e80ee.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2539-47c12987-3cec-4b01-a34e-952fab4e80ee.txn deleted file mode 100644 index 122ac4bb0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2539-47c12987-3cec-4b01-a34e-952fab4e80ee.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/254-77aa9aa2-f0d3-4723-9fae-8f86f5972324.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/254-77aa9aa2-f0d3-4723-9fae-8f86f5972324.txn deleted file mode 100644 index e2e99554c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/254-77aa9aa2-f0d3-4723-9fae-8f86f5972324.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2540-8b92241f-d4aa-4dd6-a870-eb6d6f2b52a2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2540-8b92241f-d4aa-4dd6-a870-eb6d6f2b52a2.txn deleted file mode 100644 index 7b13d4a6e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2540-8b92241f-d4aa-4dd6-a870-eb6d6f2b52a2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2541-4156715b-020a-46cf-b455-71a56bd3ad54.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2541-4156715b-020a-46cf-b455-71a56bd3ad54.txn deleted file mode 100644 index 57b1bbf93..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2541-4156715b-020a-46cf-b455-71a56bd3ad54.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2542-d835e7fb-16bb-4cdb-b7ef-6d1f9cca15fe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2542-d835e7fb-16bb-4cdb-b7ef-6d1f9cca15fe.txn deleted file mode 100644 index 4a35f76b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2542-d835e7fb-16bb-4cdb-b7ef-6d1f9cca15fe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2543-4d328ba1-d385-4a15-a647-5400fbb8b9c1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2543-4d328ba1-d385-4a15-a647-5400fbb8b9c1.txn deleted file mode 100644 index 44b17a786..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2543-4d328ba1-d385-4a15-a647-5400fbb8b9c1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2544-1cfc03fc-4452-4f9c-ae7c-9edda2eeafd0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2544-1cfc03fc-4452-4f9c-ae7c-9edda2eeafd0.txn deleted file mode 100644 index e8c62e288..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2544-1cfc03fc-4452-4f9c-ae7c-9edda2eeafd0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2545-14328423-04af-40c3-be7e-fe49e4e477da.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2545-14328423-04af-40c3-be7e-fe49e4e477da.txn deleted file mode 100644 index 8fdf0d365..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2545-14328423-04af-40c3-be7e-fe49e4e477da.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2546-906d7f01-8668-4a62-8c8c-3bd442bc5e93.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2546-906d7f01-8668-4a62-8c8c-3bd442bc5e93.txn deleted file mode 100644 index 396192f62..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2546-906d7f01-8668-4a62-8c8c-3bd442bc5e93.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2547-4af8f7b1-6b40-4908-a508-ffec006ed4c8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2547-4af8f7b1-6b40-4908-a508-ffec006ed4c8.txn deleted file mode 100644 index 37b2a8fe6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2547-4af8f7b1-6b40-4908-a508-ffec006ed4c8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2548-9258a8e8-9e18-4797-88c3-f4c0d67fc4db.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2548-9258a8e8-9e18-4797-88c3-f4c0d67fc4db.txn deleted file mode 100644 index 7942c22b0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2548-9258a8e8-9e18-4797-88c3-f4c0d67fc4db.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2549-d9547743-719b-4531-9f84-f9531b93a58d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2549-d9547743-719b-4531-9f84-f9531b93a58d.txn deleted file mode 100644 index 1cd12a3dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2549-d9547743-719b-4531-9f84-f9531b93a58d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/255-029fa057-9166-4782-aa3f-ea7bec5d5e4c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/255-029fa057-9166-4782-aa3f-ea7bec5d5e4c.txn deleted file mode 100644 index e3d197fbc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/255-029fa057-9166-4782-aa3f-ea7bec5d5e4c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2550-14b6ea4c-d266-4edf-b6e3-e723645d8493.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2550-14b6ea4c-d266-4edf-b6e3-e723645d8493.txn deleted file mode 100644 index 8a2c06f40..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2550-14b6ea4c-d266-4edf-b6e3-e723645d8493.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2551-dfb8f11c-eef4-4f63-a72a-7bd06c0afb45.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2551-dfb8f11c-eef4-4f63-a72a-7bd06c0afb45.txn deleted file mode 100644 index 0a7c1b436..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2551-dfb8f11c-eef4-4f63-a72a-7bd06c0afb45.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2552-cc94d617-8a98-4533-9bbe-ae8b92ff5358.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2552-cc94d617-8a98-4533-9bbe-ae8b92ff5358.txn deleted file mode 100644 index 9f1febc90..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2552-cc94d617-8a98-4533-9bbe-ae8b92ff5358.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2553-aafa1882-07c7-494c-9fb7-479f017c3fd5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2553-aafa1882-07c7-494c-9fb7-479f017c3fd5.txn deleted file mode 100644 index a75cd498b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2553-aafa1882-07c7-494c-9fb7-479f017c3fd5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2554-3985e309-e6f4-4154-9a7b-831b8b2d9f8c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2554-3985e309-e6f4-4154-9a7b-831b8b2d9f8c.txn deleted file mode 100644 index 0d1e10d78..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2554-3985e309-e6f4-4154-9a7b-831b8b2d9f8c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2555-4603d42c-a2a7-4990-9e41-15d3e8ba1165.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2555-4603d42c-a2a7-4990-9e41-15d3e8ba1165.txn deleted file mode 100644 index 3335ffac0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2555-4603d42c-a2a7-4990-9e41-15d3e8ba1165.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2556-db4f5b82-ab97-469d-b401-fdbb2fe00d6a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2556-db4f5b82-ab97-469d-b401-fdbb2fe00d6a.txn deleted file mode 100644 index d419c1008..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2556-db4f5b82-ab97-469d-b401-fdbb2fe00d6a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2557-3425333d-0921-4f0d-b7c2-1380947aab8e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2557-3425333d-0921-4f0d-b7c2-1380947aab8e.txn deleted file mode 100644 index 1b4ceba3b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2557-3425333d-0921-4f0d-b7c2-1380947aab8e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2558-64e60a5a-6e2d-4f08-b022-720706fd99be.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2558-64e60a5a-6e2d-4f08-b022-720706fd99be.txn deleted file mode 100644 index 8b72ff561..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2558-64e60a5a-6e2d-4f08-b022-720706fd99be.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2559-a0ac8ac9-5634-44ce-bb00-824eb5279f2b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2559-a0ac8ac9-5634-44ce-bb00-824eb5279f2b.txn deleted file mode 100644 index 03a009411..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2559-a0ac8ac9-5634-44ce-bb00-824eb5279f2b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/256-5c966336-f08f-402f-a57d-c9e3b55b9eaf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/256-5c966336-f08f-402f-a57d-c9e3b55b9eaf.txn deleted file mode 100644 index 87420770b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/256-5c966336-f08f-402f-a57d-c9e3b55b9eaf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2560-aa1e590f-4900-47f2-adfd-032e3b84a8d0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2560-aa1e590f-4900-47f2-adfd-032e3b84a8d0.txn deleted file mode 100644 index f63718c9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2560-aa1e590f-4900-47f2-adfd-032e3b84a8d0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2561-56750f08-5db2-4181-a486-d3de4856b667.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2561-56750f08-5db2-4181-a486-d3de4856b667.txn deleted file mode 100644 index b4e7c77b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2561-56750f08-5db2-4181-a486-d3de4856b667.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2562-5bdada26-3e09-48ec-afb3-3946c1a0a377.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2562-5bdada26-3e09-48ec-afb3-3946c1a0a377.txn deleted file mode 100644 index fc1986284..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2562-5bdada26-3e09-48ec-afb3-3946c1a0a377.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2563-9901d2e2-99b9-4196-ac11-59386ab5be7b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2563-9901d2e2-99b9-4196-ac11-59386ab5be7b.txn deleted file mode 100644 index a8914aa40..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2563-9901d2e2-99b9-4196-ac11-59386ab5be7b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2564-2e93c1b0-7a6f-4439-a0af-fef505ca3592.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2564-2e93c1b0-7a6f-4439-a0af-fef505ca3592.txn deleted file mode 100644 index eccdbf828..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2564-2e93c1b0-7a6f-4439-a0af-fef505ca3592.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2565-2c21b82a-b303-4126-a7e1-c3cd8cedb04c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2565-2c21b82a-b303-4126-a7e1-c3cd8cedb04c.txn deleted file mode 100644 index 4fe21785e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2565-2c21b82a-b303-4126-a7e1-c3cd8cedb04c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2566-46894553-7268-4377-8fbf-4a849f7505fc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2566-46894553-7268-4377-8fbf-4a849f7505fc.txn deleted file mode 100644 index 03efd2872..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2566-46894553-7268-4377-8fbf-4a849f7505fc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2567-f8289127-d49b-4d2e-9aff-698e56ded0f3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2567-f8289127-d49b-4d2e-9aff-698e56ded0f3.txn deleted file mode 100644 index 3a191deb1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2567-f8289127-d49b-4d2e-9aff-698e56ded0f3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2568-d7678d96-8837-4e71-8daa-0f04a8e335d8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2568-d7678d96-8837-4e71-8daa-0f04a8e335d8.txn deleted file mode 100644 index 75ca973df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2568-d7678d96-8837-4e71-8daa-0f04a8e335d8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2569-85de529a-fa92-436b-8422-42a3338ac435.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2569-85de529a-fa92-436b-8422-42a3338ac435.txn deleted file mode 100644 index b29519782..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2569-85de529a-fa92-436b-8422-42a3338ac435.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/257-39b29358-4382-4dbf-b24b-efe1bc842f6d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/257-39b29358-4382-4dbf-b24b-efe1bc842f6d.txn deleted file mode 100644 index 2bbb3e4c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/257-39b29358-4382-4dbf-b24b-efe1bc842f6d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2570-c74b4e1f-0a15-44cc-902b-181b6cb155d5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2570-c74b4e1f-0a15-44cc-902b-181b6cb155d5.txn deleted file mode 100644 index 8d572bce8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2570-c74b4e1f-0a15-44cc-902b-181b6cb155d5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2571-490e72e6-8962-486f-9f4b-93f00c2d9265.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2571-490e72e6-8962-486f-9f4b-93f00c2d9265.txn deleted file mode 100644 index 7602d19bd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2571-490e72e6-8962-486f-9f4b-93f00c2d9265.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2572-515f4575-9b8e-4c81-bb01-3d02113be9c0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2572-515f4575-9b8e-4c81-bb01-3d02113be9c0.txn deleted file mode 100644 index 95286f7bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2572-515f4575-9b8e-4c81-bb01-3d02113be9c0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2573-fe5ae7cf-1080-49f1-a519-748b29a39498.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2573-fe5ae7cf-1080-49f1-a519-748b29a39498.txn deleted file mode 100644 index 63ef6c36b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2573-fe5ae7cf-1080-49f1-a519-748b29a39498.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2574-d66394ac-2846-4850-90ca-3f6138e83b79.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2574-d66394ac-2846-4850-90ca-3f6138e83b79.txn deleted file mode 100644 index 78b462ac7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2574-d66394ac-2846-4850-90ca-3f6138e83b79.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2575-9bc33d56-8c9f-4e22-ba6f-fbb254cab635.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2575-9bc33d56-8c9f-4e22-ba6f-fbb254cab635.txn deleted file mode 100644 index f84f64a44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2575-9bc33d56-8c9f-4e22-ba6f-fbb254cab635.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2576-f2820b1f-d525-430e-81a1-5c6090b2bd9d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2576-f2820b1f-d525-430e-81a1-5c6090b2bd9d.txn deleted file mode 100644 index d877ba677..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2576-f2820b1f-d525-430e-81a1-5c6090b2bd9d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2577-a12790bd-4e27-459d-9dea-0e3cfbabcdc7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2577-a12790bd-4e27-459d-9dea-0e3cfbabcdc7.txn deleted file mode 100644 index d6e01c6c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2577-a12790bd-4e27-459d-9dea-0e3cfbabcdc7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2578-b6ae3133-a69e-4cc8-8ed6-fd9f7b402b3c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2578-b6ae3133-a69e-4cc8-8ed6-fd9f7b402b3c.txn deleted file mode 100644 index 812da2c12..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2578-b6ae3133-a69e-4cc8-8ed6-fd9f7b402b3c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2579-0e911248-326f-445e-b122-ca60d8413466.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2579-0e911248-326f-445e-b122-ca60d8413466.txn deleted file mode 100644 index 50287bbf8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2579-0e911248-326f-445e-b122-ca60d8413466.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/258-9d4b43e0-7262-4121-842e-5355aff69880.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/258-9d4b43e0-7262-4121-842e-5355aff69880.txn deleted file mode 100644 index 2c47334bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/258-9d4b43e0-7262-4121-842e-5355aff69880.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2580-09768f1e-c853-4089-9749-7542abd9564e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2580-09768f1e-c853-4089-9749-7542abd9564e.txn deleted file mode 100644 index 57a38b1f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2580-09768f1e-c853-4089-9749-7542abd9564e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2581-a7f7f7e7-c072-471f-a3db-03c9ee6434aa.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2581-a7f7f7e7-c072-471f-a3db-03c9ee6434aa.txn deleted file mode 100644 index 94969c875..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2581-a7f7f7e7-c072-471f-a3db-03c9ee6434aa.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2582-cbc291aa-e632-4a54-ae86-08821ac5ec8b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2582-cbc291aa-e632-4a54-ae86-08821ac5ec8b.txn deleted file mode 100644 index 1fb03fabd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2582-cbc291aa-e632-4a54-ae86-08821ac5ec8b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2583-3c6d23da-a336-4f42-aa08-bf4e10222f6d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2583-3c6d23da-a336-4f42-aa08-bf4e10222f6d.txn deleted file mode 100644 index cda3e96a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2583-3c6d23da-a336-4f42-aa08-bf4e10222f6d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2584-dff17bd0-8c66-41f7-916e-12ed54cc9529.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2584-dff17bd0-8c66-41f7-916e-12ed54cc9529.txn deleted file mode 100644 index 0889d400e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2584-dff17bd0-8c66-41f7-916e-12ed54cc9529.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2585-95e477d8-4e6d-4f3e-8c2e-bb4b9590b88e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2585-95e477d8-4e6d-4f3e-8c2e-bb4b9590b88e.txn deleted file mode 100644 index b62809fa4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2585-95e477d8-4e6d-4f3e-8c2e-bb4b9590b88e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2586-e27b2deb-02a8-4277-b324-acc7c3536231.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2586-e27b2deb-02a8-4277-b324-acc7c3536231.txn deleted file mode 100644 index c1f13b64f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2586-e27b2deb-02a8-4277-b324-acc7c3536231.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2587-3ebe457e-3dcf-4035-8230-1ff7d97f73d3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2587-3ebe457e-3dcf-4035-8230-1ff7d97f73d3.txn deleted file mode 100644 index 48ae6502a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2587-3ebe457e-3dcf-4035-8230-1ff7d97f73d3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2588-b5867153-e2a8-48fe-b910-c424b93ebd56.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2588-b5867153-e2a8-48fe-b910-c424b93ebd56.txn deleted file mode 100644 index 55d769670..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2588-b5867153-e2a8-48fe-b910-c424b93ebd56.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2589-48f31cb6-dff0-4c41-b8c4-a1f59ef13c62.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2589-48f31cb6-dff0-4c41-b8c4-a1f59ef13c62.txn deleted file mode 100644 index 6675e0594..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2589-48f31cb6-dff0-4c41-b8c4-a1f59ef13c62.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/259-47980eed-ae81-412f-9deb-c50cab22cd0f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/259-47980eed-ae81-412f-9deb-c50cab22cd0f.txn deleted file mode 100644 index c80fcf970..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/259-47980eed-ae81-412f-9deb-c50cab22cd0f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2590-19bd55c8-1d1f-413f-a315-dd70a08ec119.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2590-19bd55c8-1d1f-413f-a315-dd70a08ec119.txn deleted file mode 100644 index 851c5657d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2590-19bd55c8-1d1f-413f-a315-dd70a08ec119.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2591-c02a5011-ac1e-4637-a9e8-6eb7ed792f32.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2591-c02a5011-ac1e-4637-a9e8-6eb7ed792f32.txn deleted file mode 100644 index 2690bb195..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2591-c02a5011-ac1e-4637-a9e8-6eb7ed792f32.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2592-15904bad-7743-4897-813c-1a439251b464.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2592-15904bad-7743-4897-813c-1a439251b464.txn deleted file mode 100644 index 5b63657e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2592-15904bad-7743-4897-813c-1a439251b464.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2593-23ce6225-a34e-4b30-b883-d6b3751f2c46.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2593-23ce6225-a34e-4b30-b883-d6b3751f2c46.txn deleted file mode 100644 index 05853678b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2593-23ce6225-a34e-4b30-b883-d6b3751f2c46.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2594-63acf5d7-1e56-4f71-ad94-703edef3a6b7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2594-63acf5d7-1e56-4f71-ad94-703edef3a6b7.txn deleted file mode 100644 index 75b5178fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2594-63acf5d7-1e56-4f71-ad94-703edef3a6b7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2595-39f3be5f-c02b-4422-ab7d-c52a07e3698d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2595-39f3be5f-c02b-4422-ab7d-c52a07e3698d.txn deleted file mode 100644 index 37ee79286..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2595-39f3be5f-c02b-4422-ab7d-c52a07e3698d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2596-ab42b7a6-b7e8-4f28-8bfc-b36d93d7e23d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2596-ab42b7a6-b7e8-4f28-8bfc-b36d93d7e23d.txn deleted file mode 100644 index fbfe6fc22..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2596-ab42b7a6-b7e8-4f28-8bfc-b36d93d7e23d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2597-2ecafe0f-f2ee-4868-8314-6c14dc98f5b4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2597-2ecafe0f-f2ee-4868-8314-6c14dc98f5b4.txn deleted file mode 100644 index cb131d623..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2597-2ecafe0f-f2ee-4868-8314-6c14dc98f5b4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2598-db911610-1f85-4942-9fcb-17f33d141b59.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2598-db911610-1f85-4942-9fcb-17f33d141b59.txn deleted file mode 100644 index 2919a971d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2598-db911610-1f85-4942-9fcb-17f33d141b59.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2599-f7a744d1-4f0d-41ef-bf92-384782339ae3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2599-f7a744d1-4f0d-41ef-bf92-384782339ae3.txn deleted file mode 100644 index c7969815b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2599-f7a744d1-4f0d-41ef-bf92-384782339ae3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/26-f37f62b9-4e2e-468d-823a-b456e04bc3c3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/26-f37f62b9-4e2e-468d-823a-b456e04bc3c3.txn deleted file mode 100644 index 809f640a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/26-f37f62b9-4e2e-468d-823a-b456e04bc3c3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/260-aa2f1ce2-975d-4c2d-9805-3de55c1b5c83.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/260-aa2f1ce2-975d-4c2d-9805-3de55c1b5c83.txn deleted file mode 100644 index 45370e673..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/260-aa2f1ce2-975d-4c2d-9805-3de55c1b5c83.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2600-cb6fb171-03b8-40ad-86ea-b8130fa57da1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2600-cb6fb171-03b8-40ad-86ea-b8130fa57da1.txn deleted file mode 100644 index 4eb88e4c2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2600-cb6fb171-03b8-40ad-86ea-b8130fa57da1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2601-09775aa7-7319-446b-ad0f-188523d903b8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2601-09775aa7-7319-446b-ad0f-188523d903b8.txn deleted file mode 100644 index 27571848c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2601-09775aa7-7319-446b-ad0f-188523d903b8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2602-528fb15c-82f2-4933-8b3d-fd8b31e05384.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2602-528fb15c-82f2-4933-8b3d-fd8b31e05384.txn deleted file mode 100644 index 26726908f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2602-528fb15c-82f2-4933-8b3d-fd8b31e05384.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2603-0f065e07-d2f9-4502-a935-976d542f5344.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2603-0f065e07-d2f9-4502-a935-976d542f5344.txn deleted file mode 100644 index f96c508f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2603-0f065e07-d2f9-4502-a935-976d542f5344.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2604-6d68ce9a-232f-4790-8926-746c432e2e1b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2604-6d68ce9a-232f-4790-8926-746c432e2e1b.txn deleted file mode 100644 index 97c377ffd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2604-6d68ce9a-232f-4790-8926-746c432e2e1b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2605-2ed99c16-19a4-4c15-8828-141e93c38853.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2605-2ed99c16-19a4-4c15-8828-141e93c38853.txn deleted file mode 100644 index 5a2dbf020..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2605-2ed99c16-19a4-4c15-8828-141e93c38853.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2606-8176f42e-9acb-4b78-9193-3195ec629b4a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2606-8176f42e-9acb-4b78-9193-3195ec629b4a.txn deleted file mode 100644 index 29528fdfc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2606-8176f42e-9acb-4b78-9193-3195ec629b4a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2607-46837949-d787-4d9c-8452-bf0c2bb2562b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2607-46837949-d787-4d9c-8452-bf0c2bb2562b.txn deleted file mode 100644 index 7768832ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2607-46837949-d787-4d9c-8452-bf0c2bb2562b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2608-d828bdbb-5711-415c-80c6-c476405cc2bb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2608-d828bdbb-5711-415c-80c6-c476405cc2bb.txn deleted file mode 100644 index e621d4054..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2608-d828bdbb-5711-415c-80c6-c476405cc2bb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2609-efa59015-fdc4-4cf2-9d53-ce4c53a2da36.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2609-efa59015-fdc4-4cf2-9d53-ce4c53a2da36.txn deleted file mode 100644 index 5dc59ba5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2609-efa59015-fdc4-4cf2-9d53-ce4c53a2da36.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/261-28b63c0c-5f65-47b2-b39a-846bfa61adcd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/261-28b63c0c-5f65-47b2-b39a-846bfa61adcd.txn deleted file mode 100644 index 541443b29..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/261-28b63c0c-5f65-47b2-b39a-846bfa61adcd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2610-ffc55995-4b5f-489e-a65e-9ab7fcb81203.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2610-ffc55995-4b5f-489e-a65e-9ab7fcb81203.txn deleted file mode 100644 index 3c1fc47df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2610-ffc55995-4b5f-489e-a65e-9ab7fcb81203.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2611-372692e1-faed-4143-83d0-f7e11619c6c9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2611-372692e1-faed-4143-83d0-f7e11619c6c9.txn deleted file mode 100644 index 337d1a784..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2611-372692e1-faed-4143-83d0-f7e11619c6c9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2612-1404a902-5a91-4343-abb3-1a315af7b6ec.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2612-1404a902-5a91-4343-abb3-1a315af7b6ec.txn deleted file mode 100644 index 610036c27..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2612-1404a902-5a91-4343-abb3-1a315af7b6ec.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2613-4290855c-a0ee-41a2-ae89-967e54783ade.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2613-4290855c-a0ee-41a2-ae89-967e54783ade.txn deleted file mode 100644 index 729d32500..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2613-4290855c-a0ee-41a2-ae89-967e54783ade.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2614-2d395db1-910e-4ddc-b7a1-556cb6832801.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2614-2d395db1-910e-4ddc-b7a1-556cb6832801.txn deleted file mode 100644 index 7fd5b0475..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2614-2d395db1-910e-4ddc-b7a1-556cb6832801.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2615-0ea8a317-c4ea-4867-8eb3-980274079c0a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2615-0ea8a317-c4ea-4867-8eb3-980274079c0a.txn deleted file mode 100644 index 2b6e68694..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2615-0ea8a317-c4ea-4867-8eb3-980274079c0a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2616-4b9f7f4d-f1a9-4ab4-bae8-eaa1825d27ae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2616-4b9f7f4d-f1a9-4ab4-bae8-eaa1825d27ae.txn deleted file mode 100644 index c599422c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2616-4b9f7f4d-f1a9-4ab4-bae8-eaa1825d27ae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2617-4d69040e-0dcd-49c5-8335-e37c8582f535.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2617-4d69040e-0dcd-49c5-8335-e37c8582f535.txn deleted file mode 100644 index 621673dbf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2617-4d69040e-0dcd-49c5-8335-e37c8582f535.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2618-8f642528-7ee0-490c-a34d-26ffe0e216d4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2618-8f642528-7ee0-490c-a34d-26ffe0e216d4.txn deleted file mode 100644 index 778455860..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2618-8f642528-7ee0-490c-a34d-26ffe0e216d4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2619-c33981f6-c04a-48f1-bd25-b8fc2a7d96f8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2619-c33981f6-c04a-48f1-bd25-b8fc2a7d96f8.txn deleted file mode 100644 index 3273e7700..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2619-c33981f6-c04a-48f1-bd25-b8fc2a7d96f8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/262-db153e69-d446-4040-97e6-e8ddcb16d4fb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/262-db153e69-d446-4040-97e6-e8ddcb16d4fb.txn deleted file mode 100644 index c8b390876..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/262-db153e69-d446-4040-97e6-e8ddcb16d4fb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2620-03365730-7484-444e-a0eb-1df3c6a825ab.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2620-03365730-7484-444e-a0eb-1df3c6a825ab.txn deleted file mode 100644 index 22018d801..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2620-03365730-7484-444e-a0eb-1df3c6a825ab.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2621-a4393e61-fbe4-4fbf-aa59-e04fcc08caee.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2621-a4393e61-fbe4-4fbf-aa59-e04fcc08caee.txn deleted file mode 100644 index 4624c926d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2621-a4393e61-fbe4-4fbf-aa59-e04fcc08caee.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2622-f52883bc-9223-4db3-b18f-9ddab34d4f23.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2622-f52883bc-9223-4db3-b18f-9ddab34d4f23.txn deleted file mode 100644 index f8d7a28d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2622-f52883bc-9223-4db3-b18f-9ddab34d4f23.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2623-5d4d8e64-8a75-4f99-869a-7bb22a427bda.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2623-5d4d8e64-8a75-4f99-869a-7bb22a427bda.txn deleted file mode 100644 index f62d5d199..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2623-5d4d8e64-8a75-4f99-869a-7bb22a427bda.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2624-8cabcb0f-c9fa-4a35-9a18-a3cb8a749669.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2624-8cabcb0f-c9fa-4a35-9a18-a3cb8a749669.txn deleted file mode 100644 index e16b34d5c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2624-8cabcb0f-c9fa-4a35-9a18-a3cb8a749669.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2625-10efc6db-f7d5-4e4b-9086-d11209a16130.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2625-10efc6db-f7d5-4e4b-9086-d11209a16130.txn deleted file mode 100644 index 8dcb38dce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2625-10efc6db-f7d5-4e4b-9086-d11209a16130.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2626-0cca2d07-06b5-48a7-a277-d2fb0469e25b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2626-0cca2d07-06b5-48a7-a277-d2fb0469e25b.txn deleted file mode 100644 index 2def365aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2626-0cca2d07-06b5-48a7-a277-d2fb0469e25b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2627-1923641c-5ca3-4171-92a9-18ee38f6a82a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2627-1923641c-5ca3-4171-92a9-18ee38f6a82a.txn deleted file mode 100644 index 91ce9a227..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2627-1923641c-5ca3-4171-92a9-18ee38f6a82a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2628-1487f43a-e79a-4e4e-9414-30fb0128429f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2628-1487f43a-e79a-4e4e-9414-30fb0128429f.txn deleted file mode 100644 index 6252db322..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2628-1487f43a-e79a-4e4e-9414-30fb0128429f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2629-429f93bf-2b9c-4c3d-9e4e-d60442d9c298.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2629-429f93bf-2b9c-4c3d-9e4e-d60442d9c298.txn deleted file mode 100644 index d77c6ec2f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2629-429f93bf-2b9c-4c3d-9e4e-d60442d9c298.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/263-491fc8eb-4255-4d91-a2b2-9a899818f5e2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/263-491fc8eb-4255-4d91-a2b2-9a899818f5e2.txn deleted file mode 100644 index 25b2ac279..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/263-491fc8eb-4255-4d91-a2b2-9a899818f5e2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2630-f70339d4-f609-4172-a8a7-17640060114a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2630-f70339d4-f609-4172-a8a7-17640060114a.txn deleted file mode 100644 index 211f0139e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2630-f70339d4-f609-4172-a8a7-17640060114a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2631-28879928-9208-4df9-8be5-efa75216f77d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2631-28879928-9208-4df9-8be5-efa75216f77d.txn deleted file mode 100644 index f7c79d311..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2631-28879928-9208-4df9-8be5-efa75216f77d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2632-c32a6698-7cf5-471b-bf03-00c330a151ae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2632-c32a6698-7cf5-471b-bf03-00c330a151ae.txn deleted file mode 100644 index 57d773691..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2632-c32a6698-7cf5-471b-bf03-00c330a151ae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2633-97d1a948-4dd4-44a0-8b58-e0940f68b6e6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2633-97d1a948-4dd4-44a0-8b58-e0940f68b6e6.txn deleted file mode 100644 index 03b67e846..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2633-97d1a948-4dd4-44a0-8b58-e0940f68b6e6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2634-8e30a216-3483-427c-a577-0cd6e865cd7d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2634-8e30a216-3483-427c-a577-0cd6e865cd7d.txn deleted file mode 100644 index f702893c1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2634-8e30a216-3483-427c-a577-0cd6e865cd7d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2635-d2dcb931-d479-49cf-9c1d-75588fe1a653.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2635-d2dcb931-d479-49cf-9c1d-75588fe1a653.txn deleted file mode 100644 index 82463fbb1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2635-d2dcb931-d479-49cf-9c1d-75588fe1a653.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2636-262c9ac2-25c7-4020-a997-03954c55d15c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2636-262c9ac2-25c7-4020-a997-03954c55d15c.txn deleted file mode 100644 index a18ccb26d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2636-262c9ac2-25c7-4020-a997-03954c55d15c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2637-4db5d2cc-c9f6-4e88-b3a6-c00f12257e4e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2637-4db5d2cc-c9f6-4e88-b3a6-c00f12257e4e.txn deleted file mode 100644 index b2af9cfa9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2637-4db5d2cc-c9f6-4e88-b3a6-c00f12257e4e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2638-8c1ea219-f887-477e-b941-38bfa7a22cbf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2638-8c1ea219-f887-477e-b941-38bfa7a22cbf.txn deleted file mode 100644 index a9a13091b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2638-8c1ea219-f887-477e-b941-38bfa7a22cbf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2639-fd4b5bfa-123f-489f-827a-283158e6a257.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2639-fd4b5bfa-123f-489f-827a-283158e6a257.txn deleted file mode 100644 index 88bcc51fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2639-fd4b5bfa-123f-489f-827a-283158e6a257.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/264-4384a0bc-7c1e-4f50-aa85-70d51a7a336c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/264-4384a0bc-7c1e-4f50-aa85-70d51a7a336c.txn deleted file mode 100644 index 17b3bc1d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/264-4384a0bc-7c1e-4f50-aa85-70d51a7a336c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2640-25aa09b4-eb61-438d-bbc3-7729e9d50969.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2640-25aa09b4-eb61-438d-bbc3-7729e9d50969.txn deleted file mode 100644 index 5da7c0388..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2640-25aa09b4-eb61-438d-bbc3-7729e9d50969.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2641-8163fc0a-aea6-489e-8449-6a015b70de87.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2641-8163fc0a-aea6-489e-8449-6a015b70de87.txn deleted file mode 100644 index f2b5ddf35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2641-8163fc0a-aea6-489e-8449-6a015b70de87.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2642-a07d259b-6c34-431b-82df-aa87aa5f70c7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2642-a07d259b-6c34-431b-82df-aa87aa5f70c7.txn deleted file mode 100644 index 6206ce974..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2642-a07d259b-6c34-431b-82df-aa87aa5f70c7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2643-21563e63-0dfc-44ae-a46f-1fbff9c4a90f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2643-21563e63-0dfc-44ae-a46f-1fbff9c4a90f.txn deleted file mode 100644 index 9b4e96e1f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2643-21563e63-0dfc-44ae-a46f-1fbff9c4a90f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2644-58bc62d1-4b60-4d2e-80a5-4bf539ef9ddb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2644-58bc62d1-4b60-4d2e-80a5-4bf539ef9ddb.txn deleted file mode 100644 index e20620a1d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2644-58bc62d1-4b60-4d2e-80a5-4bf539ef9ddb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2645-f4f39769-8914-46cd-827a-48cda2d5184e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2645-f4f39769-8914-46cd-827a-48cda2d5184e.txn deleted file mode 100644 index b3c5d5771..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2645-f4f39769-8914-46cd-827a-48cda2d5184e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2646-343b3c97-7b69-4887-a813-1eb1f3a3d6aa.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2646-343b3c97-7b69-4887-a813-1eb1f3a3d6aa.txn deleted file mode 100644 index 9a8a71bd7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2646-343b3c97-7b69-4887-a813-1eb1f3a3d6aa.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2647-452061e8-61f1-4063-9ee0-757b0399970f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2647-452061e8-61f1-4063-9ee0-757b0399970f.txn deleted file mode 100644 index 1a0456a95..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2647-452061e8-61f1-4063-9ee0-757b0399970f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2648-bbc30a98-86e1-4c91-8b2e-df251cd5fb78.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2648-bbc30a98-86e1-4c91-8b2e-df251cd5fb78.txn deleted file mode 100644 index 9387aef79..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2648-bbc30a98-86e1-4c91-8b2e-df251cd5fb78.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2649-875075b8-aa96-4e98-86a9-b39cdd0a7404.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2649-875075b8-aa96-4e98-86a9-b39cdd0a7404.txn deleted file mode 100644 index 9da245baa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2649-875075b8-aa96-4e98-86a9-b39cdd0a7404.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/265-d4e969b7-f5ae-41b2-aad7-5ccb2dced187.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/265-d4e969b7-f5ae-41b2-aad7-5ccb2dced187.txn deleted file mode 100644 index 0512ea23e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/265-d4e969b7-f5ae-41b2-aad7-5ccb2dced187.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2650-36d35f6a-77e5-49d2-bff5-29220a5817d6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2650-36d35f6a-77e5-49d2-bff5-29220a5817d6.txn deleted file mode 100644 index 611918aa1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2650-36d35f6a-77e5-49d2-bff5-29220a5817d6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2651-bb152275-6817-42cc-bb8c-bb18447141c9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2651-bb152275-6817-42cc-bb8c-bb18447141c9.txn deleted file mode 100644 index fd64dd3d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2651-bb152275-6817-42cc-bb8c-bb18447141c9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2652-518bde11-14ab-4d70-974a-77e416bec415.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2652-518bde11-14ab-4d70-974a-77e416bec415.txn deleted file mode 100644 index cd66c9f9b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2652-518bde11-14ab-4d70-974a-77e416bec415.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2653-c04e3a02-d10b-4450-a78d-d4591a52c5dd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2653-c04e3a02-d10b-4450-a78d-d4591a52c5dd.txn deleted file mode 100644 index f5c428b0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2653-c04e3a02-d10b-4450-a78d-d4591a52c5dd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2654-3814b354-a3cb-48d3-b4cf-025d527fd181.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2654-3814b354-a3cb-48d3-b4cf-025d527fd181.txn deleted file mode 100644 index 32fcb4039..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2654-3814b354-a3cb-48d3-b4cf-025d527fd181.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2655-b150ad34-a706-4095-80a8-d3422550e7cd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2655-b150ad34-a706-4095-80a8-d3422550e7cd.txn deleted file mode 100644 index af833bbf2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2655-b150ad34-a706-4095-80a8-d3422550e7cd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2656-0cc7732d-0039-4058-b1ea-4e2141341567.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2656-0cc7732d-0039-4058-b1ea-4e2141341567.txn deleted file mode 100644 index 1a458c22b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2656-0cc7732d-0039-4058-b1ea-4e2141341567.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2657-69703034-1286-4b30-8ea5-ee552788cfff.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2657-69703034-1286-4b30-8ea5-ee552788cfff.txn deleted file mode 100644 index 69cff8fb1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2657-69703034-1286-4b30-8ea5-ee552788cfff.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2658-27a547a7-8ca0-434f-981d-b26197334d1d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2658-27a547a7-8ca0-434f-981d-b26197334d1d.txn deleted file mode 100644 index 0f11f196a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2658-27a547a7-8ca0-434f-981d-b26197334d1d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2659-b37b5db1-50fd-4f5d-a99c-ec391b9971f3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2659-b37b5db1-50fd-4f5d-a99c-ec391b9971f3.txn deleted file mode 100644 index 5c47e012c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2659-b37b5db1-50fd-4f5d-a99c-ec391b9971f3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/266-ad815ff2-e324-4901-bb57-001c7b5e8c6d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/266-ad815ff2-e324-4901-bb57-001c7b5e8c6d.txn deleted file mode 100644 index 193ba6c4c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/266-ad815ff2-e324-4901-bb57-001c7b5e8c6d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2660-2b15e9c9-26ce-47ef-b757-eca3ac9b1343.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2660-2b15e9c9-26ce-47ef-b757-eca3ac9b1343.txn deleted file mode 100644 index 786029666..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2660-2b15e9c9-26ce-47ef-b757-eca3ac9b1343.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2661-685b2a92-1bfc-4122-b97d-2940a321a835.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2661-685b2a92-1bfc-4122-b97d-2940a321a835.txn deleted file mode 100644 index cc8b2f70b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2661-685b2a92-1bfc-4122-b97d-2940a321a835.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2662-22968a22-072a-4512-abf5-28217718e108.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2662-22968a22-072a-4512-abf5-28217718e108.txn deleted file mode 100644 index c010faa8d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2662-22968a22-072a-4512-abf5-28217718e108.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2663-b38fdf52-3e29-4e52-a960-961470eef28c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2663-b38fdf52-3e29-4e52-a960-961470eef28c.txn deleted file mode 100644 index 4a892c719..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2663-b38fdf52-3e29-4e52-a960-961470eef28c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2664-8384e6eb-4d8d-4b0b-b9b9-e7b8c50dd04b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2664-8384e6eb-4d8d-4b0b-b9b9-e7b8c50dd04b.txn deleted file mode 100644 index afa9eb682..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2664-8384e6eb-4d8d-4b0b-b9b9-e7b8c50dd04b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2665-0715084c-b3fb-44d2-95c1-60679a29f313.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2665-0715084c-b3fb-44d2-95c1-60679a29f313.txn deleted file mode 100644 index 4eda43a23..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2665-0715084c-b3fb-44d2-95c1-60679a29f313.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2666-57e49777-b4fd-49dd-8e4b-acba5f0de921.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2666-57e49777-b4fd-49dd-8e4b-acba5f0de921.txn deleted file mode 100644 index f0b11f588..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2666-57e49777-b4fd-49dd-8e4b-acba5f0de921.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2667-4f7c2332-361b-4b52-b6a5-e8af2c7994e8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2667-4f7c2332-361b-4b52-b6a5-e8af2c7994e8.txn deleted file mode 100644 index cb04782fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2667-4f7c2332-361b-4b52-b6a5-e8af2c7994e8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2668-6708e0b9-15de-48ef-997e-ed1b5cdf7f96.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2668-6708e0b9-15de-48ef-997e-ed1b5cdf7f96.txn deleted file mode 100644 index 06bab67f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2668-6708e0b9-15de-48ef-997e-ed1b5cdf7f96.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2669-9a32a89d-bb00-4be5-9158-9c4d8af4c9c1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2669-9a32a89d-bb00-4be5-9158-9c4d8af4c9c1.txn deleted file mode 100644 index 8800fa082..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2669-9a32a89d-bb00-4be5-9158-9c4d8af4c9c1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/267-83895a38-c174-4537-af7b-bc3d10afd6f1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/267-83895a38-c174-4537-af7b-bc3d10afd6f1.txn deleted file mode 100644 index 6c9275ac6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/267-83895a38-c174-4537-af7b-bc3d10afd6f1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2670-d6a4c1b6-d9d1-4af7-987a-e35b3b73f968.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2670-d6a4c1b6-d9d1-4af7-987a-e35b3b73f968.txn deleted file mode 100644 index 59d359cca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2670-d6a4c1b6-d9d1-4af7-987a-e35b3b73f968.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2671-d706637c-77a4-4b04-a2a1-1824fec72323.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2671-d706637c-77a4-4b04-a2a1-1824fec72323.txn deleted file mode 100644 index c358ca43e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2671-d706637c-77a4-4b04-a2a1-1824fec72323.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2672-c23b5b6c-03af-4deb-8ad4-054caa2911a7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2672-c23b5b6c-03af-4deb-8ad4-054caa2911a7.txn deleted file mode 100644 index 918bbe42a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2672-c23b5b6c-03af-4deb-8ad4-054caa2911a7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2673-98ac5364-24e5-4259-9ca5-02493b3222e8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2673-98ac5364-24e5-4259-9ca5-02493b3222e8.txn deleted file mode 100644 index 9512268eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2673-98ac5364-24e5-4259-9ca5-02493b3222e8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2674-14570dd2-686f-4408-b7bf-100d74fe1d72.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2674-14570dd2-686f-4408-b7bf-100d74fe1d72.txn deleted file mode 100644 index 53268bd5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2674-14570dd2-686f-4408-b7bf-100d74fe1d72.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2675-dc2a6199-8f90-464c-ac50-0a994df2192d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2675-dc2a6199-8f90-464c-ac50-0a994df2192d.txn deleted file mode 100644 index e52c8ac93..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2675-dc2a6199-8f90-464c-ac50-0a994df2192d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2676-7b99e663-bbbb-49aa-b8f9-9926f603375a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2676-7b99e663-bbbb-49aa-b8f9-9926f603375a.txn deleted file mode 100644 index ec82a234d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2676-7b99e663-bbbb-49aa-b8f9-9926f603375a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2677-d96a491c-8025-4273-b745-82d3d138c097.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2677-d96a491c-8025-4273-b745-82d3d138c097.txn deleted file mode 100644 index a56e2851b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2677-d96a491c-8025-4273-b745-82d3d138c097.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2678-1e606f09-6d67-4cf2-b920-1d307378aa96.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2678-1e606f09-6d67-4cf2-b920-1d307378aa96.txn deleted file mode 100644 index 464ac6375..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2678-1e606f09-6d67-4cf2-b920-1d307378aa96.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2679-30c643b7-cc97-40d4-a1c7-8bed78f7761b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2679-30c643b7-cc97-40d4-a1c7-8bed78f7761b.txn deleted file mode 100644 index f8e26fb49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2679-30c643b7-cc97-40d4-a1c7-8bed78f7761b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/268-768f4867-b1ce-4272-a7f8-a00862a11ba9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/268-768f4867-b1ce-4272-a7f8-a00862a11ba9.txn deleted file mode 100644 index 528006077..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/268-768f4867-b1ce-4272-a7f8-a00862a11ba9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2680-69631fee-4f65-46d8-9601-d27001f922e5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2680-69631fee-4f65-46d8-9601-d27001f922e5.txn deleted file mode 100644 index 729b1b8c1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2680-69631fee-4f65-46d8-9601-d27001f922e5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2681-3f5ce13b-0025-41a2-921e-bf6d162fbd23.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2681-3f5ce13b-0025-41a2-921e-bf6d162fbd23.txn deleted file mode 100644 index 3458443fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2681-3f5ce13b-0025-41a2-921e-bf6d162fbd23.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2682-e1d10a18-1ad0-458f-8ccf-f001d065034a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2682-e1d10a18-1ad0-458f-8ccf-f001d065034a.txn deleted file mode 100644 index 8e58c8565..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2682-e1d10a18-1ad0-458f-8ccf-f001d065034a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2683-fd3c42ca-1a05-480b-bac8-8c15ab6493b7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2683-fd3c42ca-1a05-480b-bac8-8c15ab6493b7.txn deleted file mode 100644 index 78f8b78cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2683-fd3c42ca-1a05-480b-bac8-8c15ab6493b7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2684-a33e55d8-ce87-4c84-9254-6694e42876f6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2684-a33e55d8-ce87-4c84-9254-6694e42876f6.txn deleted file mode 100644 index dc6234a71..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2684-a33e55d8-ce87-4c84-9254-6694e42876f6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2685-5a08d19f-4ce6-473f-8ac9-cbf8fc92dd9c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2685-5a08d19f-4ce6-473f-8ac9-cbf8fc92dd9c.txn deleted file mode 100644 index 4292517cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2685-5a08d19f-4ce6-473f-8ac9-cbf8fc92dd9c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2686-4ce1cb1b-7863-4739-9241-5b367dc34852.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2686-4ce1cb1b-7863-4739-9241-5b367dc34852.txn deleted file mode 100644 index 95c9e96a0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2686-4ce1cb1b-7863-4739-9241-5b367dc34852.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2687-5ddab77f-db63-4b80-85ba-3ca9ed02af16.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2687-5ddab77f-db63-4b80-85ba-3ca9ed02af16.txn deleted file mode 100644 index f29319768..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2687-5ddab77f-db63-4b80-85ba-3ca9ed02af16.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2688-e0cd8fd6-a027-4920-8ec3-1476fc4bc6ad.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2688-e0cd8fd6-a027-4920-8ec3-1476fc4bc6ad.txn deleted file mode 100644 index 84d43bc36..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2688-e0cd8fd6-a027-4920-8ec3-1476fc4bc6ad.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2689-19c687c6-300e-438e-bfd9-f375591382fb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2689-19c687c6-300e-438e-bfd9-f375591382fb.txn deleted file mode 100644 index c77c9f759..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2689-19c687c6-300e-438e-bfd9-f375591382fb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/269-8f6148e2-7d9a-49da-93ce-1ace0d2012c8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/269-8f6148e2-7d9a-49da-93ce-1ace0d2012c8.txn deleted file mode 100644 index c239344c3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/269-8f6148e2-7d9a-49da-93ce-1ace0d2012c8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2690-f90af1e2-770a-4e04-8f54-1eae3da4cfbd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2690-f90af1e2-770a-4e04-8f54-1eae3da4cfbd.txn deleted file mode 100644 index 95a42e458..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2690-f90af1e2-770a-4e04-8f54-1eae3da4cfbd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2691-7bc2ece4-53e3-4ed5-9731-170cf280cb2e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2691-7bc2ece4-53e3-4ed5-9731-170cf280cb2e.txn deleted file mode 100644 index 78c039760..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2691-7bc2ece4-53e3-4ed5-9731-170cf280cb2e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2692-1d8b93a6-fcbb-46e8-97eb-c508b2b7990a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2692-1d8b93a6-fcbb-46e8-97eb-c508b2b7990a.txn deleted file mode 100644 index 95d1f78cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2692-1d8b93a6-fcbb-46e8-97eb-c508b2b7990a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2693-9f44467a-999b-43ba-8db7-c7d12d8be297.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2693-9f44467a-999b-43ba-8db7-c7d12d8be297.txn deleted file mode 100644 index f424f9065..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2693-9f44467a-999b-43ba-8db7-c7d12d8be297.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2694-21c7a477-4f68-4643-8acf-d50f8c9b21e9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2694-21c7a477-4f68-4643-8acf-d50f8c9b21e9.txn deleted file mode 100644 index 3af87f3d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2694-21c7a477-4f68-4643-8acf-d50f8c9b21e9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2695-978e166c-d906-4d72-8f73-ff5b2988b41c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2695-978e166c-d906-4d72-8f73-ff5b2988b41c.txn deleted file mode 100644 index a037b9c23..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2695-978e166c-d906-4d72-8f73-ff5b2988b41c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2696-809d4902-d5c7-4528-acab-bc43238be3d4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2696-809d4902-d5c7-4528-acab-bc43238be3d4.txn deleted file mode 100644 index 576389a6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2696-809d4902-d5c7-4528-acab-bc43238be3d4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2697-07c84d8a-bbb4-4285-9db1-824aa96e66cc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2697-07c84d8a-bbb4-4285-9db1-824aa96e66cc.txn deleted file mode 100644 index 7815ec3ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2697-07c84d8a-bbb4-4285-9db1-824aa96e66cc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2698-d57d9b27-e185-4240-b3e8-f16adf7284b2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2698-d57d9b27-e185-4240-b3e8-f16adf7284b2.txn deleted file mode 100644 index 30e2bf054..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2698-d57d9b27-e185-4240-b3e8-f16adf7284b2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2699-68d7ebfa-92cc-49ee-845f-824fa7798d27.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2699-68d7ebfa-92cc-49ee-845f-824fa7798d27.txn deleted file mode 100644 index 9fedd33bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2699-68d7ebfa-92cc-49ee-845f-824fa7798d27.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/27-acec31d3-0fcd-4996-90b9-bb023aac00cf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/27-acec31d3-0fcd-4996-90b9-bb023aac00cf.txn deleted file mode 100644 index ab5896a5f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/27-acec31d3-0fcd-4996-90b9-bb023aac00cf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/270-250593cd-477a-4cc1-94fc-e69994e7492a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/270-250593cd-477a-4cc1-94fc-e69994e7492a.txn deleted file mode 100644 index 65728afb0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/270-250593cd-477a-4cc1-94fc-e69994e7492a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2700-a9cd448c-b5cd-4158-a629-5d07d6913635.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2700-a9cd448c-b5cd-4158-a629-5d07d6913635.txn deleted file mode 100644 index 35a4ee41e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2700-a9cd448c-b5cd-4158-a629-5d07d6913635.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2701-9066b1a1-7623-48e5-8806-7412826509c9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2701-9066b1a1-7623-48e5-8806-7412826509c9.txn deleted file mode 100644 index 032608b16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2701-9066b1a1-7623-48e5-8806-7412826509c9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2702-8e257dd8-310d-4407-b7ab-5c7d2a0bef71.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2702-8e257dd8-310d-4407-b7ab-5c7d2a0bef71.txn deleted file mode 100644 index dbbb6743f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2702-8e257dd8-310d-4407-b7ab-5c7d2a0bef71.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2703-afc3e5d7-500e-44a2-b861-a1b6a2ac4ccd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2703-afc3e5d7-500e-44a2-b861-a1b6a2ac4ccd.txn deleted file mode 100644 index 609ae3dd0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2703-afc3e5d7-500e-44a2-b861-a1b6a2ac4ccd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2704-c4a673ba-6aa2-4d8d-a161-0330539f4505.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2704-c4a673ba-6aa2-4d8d-a161-0330539f4505.txn deleted file mode 100644 index e3d2abc45..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2704-c4a673ba-6aa2-4d8d-a161-0330539f4505.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2705-e3cac95a-27bb-4db2-adcf-85953ccdee63.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2705-e3cac95a-27bb-4db2-adcf-85953ccdee63.txn deleted file mode 100644 index 249ef8dd5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2705-e3cac95a-27bb-4db2-adcf-85953ccdee63.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2706-31e94724-2958-454b-801c-ecbedb70565b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2706-31e94724-2958-454b-801c-ecbedb70565b.txn deleted file mode 100644 index 7548e3cc6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2706-31e94724-2958-454b-801c-ecbedb70565b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2707-ad249b54-38c6-4d79-af89-9ccfd129b9eb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2707-ad249b54-38c6-4d79-af89-9ccfd129b9eb.txn deleted file mode 100644 index 4e2543eb2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2707-ad249b54-38c6-4d79-af89-9ccfd129b9eb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2708-c6c425f1-4410-4f67-abe5-a6e0d17fba45.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2708-c6c425f1-4410-4f67-abe5-a6e0d17fba45.txn deleted file mode 100644 index b322b530b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2708-c6c425f1-4410-4f67-abe5-a6e0d17fba45.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2709-fced3b6e-f1e6-4651-b275-8b64d13d89f5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2709-fced3b6e-f1e6-4651-b275-8b64d13d89f5.txn deleted file mode 100644 index f66a56a21..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2709-fced3b6e-f1e6-4651-b275-8b64d13d89f5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/271-c520c56a-e575-4280-8adf-76c7d3aed2d7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/271-c520c56a-e575-4280-8adf-76c7d3aed2d7.txn deleted file mode 100644 index 6374c18be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/271-c520c56a-e575-4280-8adf-76c7d3aed2d7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2710-d1eb5f32-212c-4193-b13a-7d26f7d881d6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2710-d1eb5f32-212c-4193-b13a-7d26f7d881d6.txn deleted file mode 100644 index 4dd8930f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2710-d1eb5f32-212c-4193-b13a-7d26f7d881d6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2711-27c56164-09b7-47ac-b6c4-2471ed8d62d8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2711-27c56164-09b7-47ac-b6c4-2471ed8d62d8.txn deleted file mode 100644 index 1f4a1eab2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2711-27c56164-09b7-47ac-b6c4-2471ed8d62d8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2712-ba203f16-cc9a-419c-ab06-f66c4f6d811c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2712-ba203f16-cc9a-419c-ab06-f66c4f6d811c.txn deleted file mode 100644 index 4497a516a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2712-ba203f16-cc9a-419c-ab06-f66c4f6d811c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2713-e1f0a1b3-515a-4730-82ee-b803e1256aa6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2713-e1f0a1b3-515a-4730-82ee-b803e1256aa6.txn deleted file mode 100644 index 2a23abb53..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2713-e1f0a1b3-515a-4730-82ee-b803e1256aa6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2714-090b7470-7770-4630-a315-f380d6e72f37.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2714-090b7470-7770-4630-a315-f380d6e72f37.txn deleted file mode 100644 index 30a1b0675..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2714-090b7470-7770-4630-a315-f380d6e72f37.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2715-35574d95-a8be-4c99-b916-73a491b43155.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2715-35574d95-a8be-4c99-b916-73a491b43155.txn deleted file mode 100644 index 471d6d2a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2715-35574d95-a8be-4c99-b916-73a491b43155.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2716-f2ad01d2-8f5f-4684-ae13-4eba9a091f98.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2716-f2ad01d2-8f5f-4684-ae13-4eba9a091f98.txn deleted file mode 100644 index 8c20834b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2716-f2ad01d2-8f5f-4684-ae13-4eba9a091f98.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2717-2d04d0a7-4820-4e96-994c-bf409f9d3da4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2717-2d04d0a7-4820-4e96-994c-bf409f9d3da4.txn deleted file mode 100644 index de1cb0828..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2717-2d04d0a7-4820-4e96-994c-bf409f9d3da4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2718-96792b1b-7caa-46dd-bacf-44da5599a781.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2718-96792b1b-7caa-46dd-bacf-44da5599a781.txn deleted file mode 100644 index 6c7677489..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2718-96792b1b-7caa-46dd-bacf-44da5599a781.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2719-34b65ed6-7b95-4268-ba40-2b8e2cddf5c4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2719-34b65ed6-7b95-4268-ba40-2b8e2cddf5c4.txn deleted file mode 100644 index 44ff596c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/2719-34b65ed6-7b95-4268-ba40-2b8e2cddf5c4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/272-b6423af2-194f-445a-aeb3-2b5cac73decc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/272-b6423af2-194f-445a-aeb3-2b5cac73decc.txn deleted file mode 100644 index 11a9f0289..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/272-b6423af2-194f-445a-aeb3-2b5cac73decc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/273-3af8e2ea-76c9-4465-b797-d1200b355ab7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/273-3af8e2ea-76c9-4465-b797-d1200b355ab7.txn deleted file mode 100644 index 6914d83f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/273-3af8e2ea-76c9-4465-b797-d1200b355ab7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/274-16206e81-4561-4784-abc1-3b5aba185bbe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/274-16206e81-4561-4784-abc1-3b5aba185bbe.txn deleted file mode 100644 index 3c3f78e7a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/274-16206e81-4561-4784-abc1-3b5aba185bbe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/275-a64b79fa-4999-4a0d-9ca4-744c4b803502.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/275-a64b79fa-4999-4a0d-9ca4-744c4b803502.txn deleted file mode 100644 index 0f3c295a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/275-a64b79fa-4999-4a0d-9ca4-744c4b803502.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/276-21a59c37-8df0-4e50-b7f0-59b80be6b0ce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/276-21a59c37-8df0-4e50-b7f0-59b80be6b0ce.txn deleted file mode 100644 index 9d5f8c6f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/276-21a59c37-8df0-4e50-b7f0-59b80be6b0ce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/277-c58e58b0-5a01-4a66-bca3-5bf582e532d2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/277-c58e58b0-5a01-4a66-bca3-5bf582e532d2.txn deleted file mode 100644 index 02c126fa8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/277-c58e58b0-5a01-4a66-bca3-5bf582e532d2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/278-93b66adc-c90a-409a-87df-1e6664db04ba.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/278-93b66adc-c90a-409a-87df-1e6664db04ba.txn deleted file mode 100644 index f68e6a537..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/278-93b66adc-c90a-409a-87df-1e6664db04ba.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/279-b137fd39-0912-48f9-b8b4-2226348945d2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/279-b137fd39-0912-48f9-b8b4-2226348945d2.txn deleted file mode 100644 index 962f36561..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/279-b137fd39-0912-48f9-b8b4-2226348945d2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/28-70a4a787-ccf9-4d10-8072-5359a596f80d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/28-70a4a787-ccf9-4d10-8072-5359a596f80d.txn deleted file mode 100644 index ac62f400e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/28-70a4a787-ccf9-4d10-8072-5359a596f80d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/280-2a7fde4e-2b14-4696-b33f-ae84e6736930.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/280-2a7fde4e-2b14-4696-b33f-ae84e6736930.txn deleted file mode 100644 index 6c909dbcc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/280-2a7fde4e-2b14-4696-b33f-ae84e6736930.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/281-0caf42fa-8693-4fa6-bc53-cf6b9d35bc1e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/281-0caf42fa-8693-4fa6-bc53-cf6b9d35bc1e.txn deleted file mode 100644 index f380a3fd4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/281-0caf42fa-8693-4fa6-bc53-cf6b9d35bc1e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/282-0d377925-3052-46c0-8945-8c74b466498a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/282-0d377925-3052-46c0-8945-8c74b466498a.txn deleted file mode 100644 index b6ae49761..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/282-0d377925-3052-46c0-8945-8c74b466498a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/283-8d8a3874-d2cc-4772-bad9-846f420e95d4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/283-8d8a3874-d2cc-4772-bad9-846f420e95d4.txn deleted file mode 100644 index f287a4381..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/283-8d8a3874-d2cc-4772-bad9-846f420e95d4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/284-c5ad867d-6c3e-4d01-a585-908d2191196e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/284-c5ad867d-6c3e-4d01-a585-908d2191196e.txn deleted file mode 100644 index ed9123054..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/284-c5ad867d-6c3e-4d01-a585-908d2191196e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/285-099abd60-6b5b-4798-8542-9599c71ad911.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/285-099abd60-6b5b-4798-8542-9599c71ad911.txn deleted file mode 100644 index 3e414cdeb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/285-099abd60-6b5b-4798-8542-9599c71ad911.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/286-edaf3284-58f1-475a-ade6-e26971c7a2a9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/286-edaf3284-58f1-475a-ade6-e26971c7a2a9.txn deleted file mode 100644 index 18b3c73bf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/286-edaf3284-58f1-475a-ade6-e26971c7a2a9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/287-b1278013-4723-4c39-8de4-6b477e80d204.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/287-b1278013-4723-4c39-8de4-6b477e80d204.txn deleted file mode 100644 index 4a5225284..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/287-b1278013-4723-4c39-8de4-6b477e80d204.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/288-97ad236e-9845-4ea9-9e15-dffc1fb906e2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/288-97ad236e-9845-4ea9-9e15-dffc1fb906e2.txn deleted file mode 100644 index 626f43d8a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/288-97ad236e-9845-4ea9-9e15-dffc1fb906e2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/289-49d4a453-66ce-4413-b937-8251a1c7eb1c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/289-49d4a453-66ce-4413-b937-8251a1c7eb1c.txn deleted file mode 100644 index f0402a03d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/289-49d4a453-66ce-4413-b937-8251a1c7eb1c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/29-0e6870b3-4ea5-40a3-bff6-1ea20e8a949d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/29-0e6870b3-4ea5-40a3-bff6-1ea20e8a949d.txn deleted file mode 100644 index 6a6843e1b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/29-0e6870b3-4ea5-40a3-bff6-1ea20e8a949d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/290-0fa2d52b-9dff-46fe-9179-760ec8be63a4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/290-0fa2d52b-9dff-46fe-9179-760ec8be63a4.txn deleted file mode 100644 index 10bbcf7a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/290-0fa2d52b-9dff-46fe-9179-760ec8be63a4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/291-f99ddbd5-75a4-40d9-b5ba-3d8823a0edbe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/291-f99ddbd5-75a4-40d9-b5ba-3d8823a0edbe.txn deleted file mode 100644 index 7307e2ed6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/291-f99ddbd5-75a4-40d9-b5ba-3d8823a0edbe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/292-271f07ca-a9fb-417e-bb5e-c9efde79b92a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/292-271f07ca-a9fb-417e-bb5e-c9efde79b92a.txn deleted file mode 100644 index e8ce189a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/292-271f07ca-a9fb-417e-bb5e-c9efde79b92a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/293-b5a41808-e857-4b82-86a7-00076a8265db.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/293-b5a41808-e857-4b82-86a7-00076a8265db.txn deleted file mode 100644 index 7b484e695..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/293-b5a41808-e857-4b82-86a7-00076a8265db.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/294-51798b6f-4b08-46f4-b03e-c9107bf74d61.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/294-51798b6f-4b08-46f4-b03e-c9107bf74d61.txn deleted file mode 100644 index d7813a2c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/294-51798b6f-4b08-46f4-b03e-c9107bf74d61.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/295-5090aa33-5637-497c-bd04-36a50ec41c4b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/295-5090aa33-5637-497c-bd04-36a50ec41c4b.txn deleted file mode 100644 index f7f80ee0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/295-5090aa33-5637-497c-bd04-36a50ec41c4b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/296-2be5c8d0-8a1b-4f93-a95e-716d0b88b878.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/296-2be5c8d0-8a1b-4f93-a95e-716d0b88b878.txn deleted file mode 100644 index 70dc90f99..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/296-2be5c8d0-8a1b-4f93-a95e-716d0b88b878.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/297-380f740b-eed6-4230-8cb9-598585781036.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/297-380f740b-eed6-4230-8cb9-598585781036.txn deleted file mode 100644 index aede10875..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/297-380f740b-eed6-4230-8cb9-598585781036.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/298-3960de89-89bd-4d4b-b99e-5fde9c241105.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/298-3960de89-89bd-4d4b-b99e-5fde9c241105.txn deleted file mode 100644 index 50650ff3b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/298-3960de89-89bd-4d4b-b99e-5fde9c241105.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/299-9f8f9d08-2c1c-4292-aeee-f6518717a62f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/299-9f8f9d08-2c1c-4292-aeee-f6518717a62f.txn deleted file mode 100644 index ac476f7ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/299-9f8f9d08-2c1c-4292-aeee-f6518717a62f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/3-3aad2054-9873-4b40-8340-946adadafef7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/3-3aad2054-9873-4b40-8340-946adadafef7.txn deleted file mode 100644 index 63be620d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/3-3aad2054-9873-4b40-8340-946adadafef7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/30-3872ce0d-fd63-4a8d-a22e-8e17d6e682d6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/30-3872ce0d-fd63-4a8d-a22e-8e17d6e682d6.txn deleted file mode 100644 index bb41ac731..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/30-3872ce0d-fd63-4a8d-a22e-8e17d6e682d6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/300-a29a0ad8-3a71-4031-8d1e-c3610838e7ae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/300-a29a0ad8-3a71-4031-8d1e-c3610838e7ae.txn deleted file mode 100644 index 11373de20..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/300-a29a0ad8-3a71-4031-8d1e-c3610838e7ae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/301-a55a23bc-b9e0-44e5-a670-c9fdecec3b83.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/301-a55a23bc-b9e0-44e5-a670-c9fdecec3b83.txn deleted file mode 100644 index 3e78199d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/301-a55a23bc-b9e0-44e5-a670-c9fdecec3b83.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/302-8768fcd1-5974-453f-b1cc-50c4c6179d72.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/302-8768fcd1-5974-453f-b1cc-50c4c6179d72.txn deleted file mode 100644 index 4ebecfefe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/302-8768fcd1-5974-453f-b1cc-50c4c6179d72.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/303-d5b77a2b-220c-40b0-8255-6fb88bfce80d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/303-d5b77a2b-220c-40b0-8255-6fb88bfce80d.txn deleted file mode 100644 index 8b3ffe593..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/303-d5b77a2b-220c-40b0-8255-6fb88bfce80d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/304-157acb1e-b894-4eaf-ae42-7755d486055f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/304-157acb1e-b894-4eaf-ae42-7755d486055f.txn deleted file mode 100644 index 33f5099be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/304-157acb1e-b894-4eaf-ae42-7755d486055f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/305-44e951a5-fd1b-498f-ab8a-99adcfb19d26.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/305-44e951a5-fd1b-498f-ab8a-99adcfb19d26.txn deleted file mode 100644 index df3e1922c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/305-44e951a5-fd1b-498f-ab8a-99adcfb19d26.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/306-522b80ae-6681-4c23-a52f-a4ec8f48a872.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/306-522b80ae-6681-4c23-a52f-a4ec8f48a872.txn deleted file mode 100644 index 9a82f62b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/306-522b80ae-6681-4c23-a52f-a4ec8f48a872.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/307-c759ac48-3e48-40fa-893d-63779d69e14a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/307-c759ac48-3e48-40fa-893d-63779d69e14a.txn deleted file mode 100644 index e1d6aa276..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/307-c759ac48-3e48-40fa-893d-63779d69e14a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/308-d644c34f-6a14-466c-9e30-3dd88598f7fb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/308-d644c34f-6a14-466c-9e30-3dd88598f7fb.txn deleted file mode 100644 index 212c3ea5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/308-d644c34f-6a14-466c-9e30-3dd88598f7fb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/309-b4e7e415-39cc-4a38-8e9f-d1034f7c5f18.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/309-b4e7e415-39cc-4a38-8e9f-d1034f7c5f18.txn deleted file mode 100644 index b1b5591ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/309-b4e7e415-39cc-4a38-8e9f-d1034f7c5f18.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/31-1b9e7928-e0e3-45c9-bfb8-158fd56b0287.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/31-1b9e7928-e0e3-45c9-bfb8-158fd56b0287.txn deleted file mode 100644 index 8fcd72c26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/31-1b9e7928-e0e3-45c9-bfb8-158fd56b0287.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/310-42f21455-22d7-4c38-b666-ccb7f53fc41f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/310-42f21455-22d7-4c38-b666-ccb7f53fc41f.txn deleted file mode 100644 index 278633c79..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/310-42f21455-22d7-4c38-b666-ccb7f53fc41f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/311-49d1f276-80e8-4018-8642-368bdae188b6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/311-49d1f276-80e8-4018-8642-368bdae188b6.txn deleted file mode 100644 index e14b2627b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/311-49d1f276-80e8-4018-8642-368bdae188b6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/312-f7058858-6683-4505-be02-df3150c2a913.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/312-f7058858-6683-4505-be02-df3150c2a913.txn deleted file mode 100644 index a5bb79ce3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/312-f7058858-6683-4505-be02-df3150c2a913.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/313-28cebd3b-62f1-41fe-a283-8f4dba7ec0f6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/313-28cebd3b-62f1-41fe-a283-8f4dba7ec0f6.txn deleted file mode 100644 index f59c15138..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/313-28cebd3b-62f1-41fe-a283-8f4dba7ec0f6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/314-7e7fdcd3-3003-4ed2-90bb-b7a428577c31.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/314-7e7fdcd3-3003-4ed2-90bb-b7a428577c31.txn deleted file mode 100644 index 7e17a3145..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/314-7e7fdcd3-3003-4ed2-90bb-b7a428577c31.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/315-1a73ad99-dca3-49bf-8827-d6f3a769bfb8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/315-1a73ad99-dca3-49bf-8827-d6f3a769bfb8.txn deleted file mode 100644 index 4de4a3801..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/315-1a73ad99-dca3-49bf-8827-d6f3a769bfb8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/316-bbaede11-9f7e-4cd1-81e5-1290c935a210.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/316-bbaede11-9f7e-4cd1-81e5-1290c935a210.txn deleted file mode 100644 index d237b1b8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/316-bbaede11-9f7e-4cd1-81e5-1290c935a210.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/317-e7eacd73-b4da-4f64-89c2-013b4ef3dc58.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/317-e7eacd73-b4da-4f64-89c2-013b4ef3dc58.txn deleted file mode 100644 index 57d0cb25c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/317-e7eacd73-b4da-4f64-89c2-013b4ef3dc58.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/318-5e1d22aa-838c-4fe0-9e05-b3421cc083e2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/318-5e1d22aa-838c-4fe0-9e05-b3421cc083e2.txn deleted file mode 100644 index 6dd2b368d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/318-5e1d22aa-838c-4fe0-9e05-b3421cc083e2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/319-51764b24-7d25-4e3e-823a-7b4b2fb125fe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/319-51764b24-7d25-4e3e-823a-7b4b2fb125fe.txn deleted file mode 100644 index b6df75108..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/319-51764b24-7d25-4e3e-823a-7b4b2fb125fe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/32-128e3ceb-79b6-404a-a715-7d18f9a4ac74.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/32-128e3ceb-79b6-404a-a715-7d18f9a4ac74.txn deleted file mode 100644 index 2da3e8fdd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/32-128e3ceb-79b6-404a-a715-7d18f9a4ac74.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/320-7c37930c-0c05-44e8-a970-9eaf3ed6bd12.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/320-7c37930c-0c05-44e8-a970-9eaf3ed6bd12.txn deleted file mode 100644 index 466250db4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/320-7c37930c-0c05-44e8-a970-9eaf3ed6bd12.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/321-37900f50-e2cd-4330-83f4-2fc25d4128d0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/321-37900f50-e2cd-4330-83f4-2fc25d4128d0.txn deleted file mode 100644 index 73ca9bf48..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/321-37900f50-e2cd-4330-83f4-2fc25d4128d0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/322-d904bff0-4726-4f10-8184-13ac0705590a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/322-d904bff0-4726-4f10-8184-13ac0705590a.txn deleted file mode 100644 index d00100b68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/322-d904bff0-4726-4f10-8184-13ac0705590a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/323-bbf2c870-5b18-4af6-a8ec-602c3d3ce757.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/323-bbf2c870-5b18-4af6-a8ec-602c3d3ce757.txn deleted file mode 100644 index 6701b6f8a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/323-bbf2c870-5b18-4af6-a8ec-602c3d3ce757.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/324-eb0a7654-89d1-4ab8-9491-ce21eadc5d0b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/324-eb0a7654-89d1-4ab8-9491-ce21eadc5d0b.txn deleted file mode 100644 index 60828aab7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/324-eb0a7654-89d1-4ab8-9491-ce21eadc5d0b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/325-d406bd86-4d17-4d22-b75c-5478c58051a2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/325-d406bd86-4d17-4d22-b75c-5478c58051a2.txn deleted file mode 100644 index 6af438b16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/325-d406bd86-4d17-4d22-b75c-5478c58051a2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/326-07c49416-8108-4c8c-9ce0-f86217b0cadb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/326-07c49416-8108-4c8c-9ce0-f86217b0cadb.txn deleted file mode 100644 index a6870dbdc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/326-07c49416-8108-4c8c-9ce0-f86217b0cadb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/327-b5ca0689-f2b3-4659-9aa8-5be1bc9a2842.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/327-b5ca0689-f2b3-4659-9aa8-5be1bc9a2842.txn deleted file mode 100644 index 440782e84..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/327-b5ca0689-f2b3-4659-9aa8-5be1bc9a2842.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/328-d2a1a559-c63e-4329-b99d-47ce975c3bc6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/328-d2a1a559-c63e-4329-b99d-47ce975c3bc6.txn deleted file mode 100644 index a33b8cd50..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/328-d2a1a559-c63e-4329-b99d-47ce975c3bc6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/329-79b48971-246a-493d-89ce-669cbc84fbec.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/329-79b48971-246a-493d-89ce-669cbc84fbec.txn deleted file mode 100644 index ae3c11845..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/329-79b48971-246a-493d-89ce-669cbc84fbec.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/33-e1b2360a-24cc-422d-ba47-7dbdf00b28c8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/33-e1b2360a-24cc-422d-ba47-7dbdf00b28c8.txn deleted file mode 100644 index 206c19001..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/33-e1b2360a-24cc-422d-ba47-7dbdf00b28c8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/330-6aa3595c-b635-4a7d-9c87-d4c5afcfb9a1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/330-6aa3595c-b635-4a7d-9c87-d4c5afcfb9a1.txn deleted file mode 100644 index fdb382722..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/330-6aa3595c-b635-4a7d-9c87-d4c5afcfb9a1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/331-580f9339-c88d-4d10-91c2-2dc08f09b437.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/331-580f9339-c88d-4d10-91c2-2dc08f09b437.txn deleted file mode 100644 index b1cb3c6ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/331-580f9339-c88d-4d10-91c2-2dc08f09b437.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/332-ba4ee59c-a4f6-4cc8-b827-dc5ef6f93923.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/332-ba4ee59c-a4f6-4cc8-b827-dc5ef6f93923.txn deleted file mode 100644 index f590a0632..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/332-ba4ee59c-a4f6-4cc8-b827-dc5ef6f93923.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/333-282f6772-4c19-4f63-8c80-f4d6957eaa14.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/333-282f6772-4c19-4f63-8c80-f4d6957eaa14.txn deleted file mode 100644 index b39838369..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/333-282f6772-4c19-4f63-8c80-f4d6957eaa14.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/334-4dbeb20f-c03c-41f3-9556-28e2e278d07f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/334-4dbeb20f-c03c-41f3-9556-28e2e278d07f.txn deleted file mode 100644 index e860f9858..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/334-4dbeb20f-c03c-41f3-9556-28e2e278d07f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/335-bba9447c-7513-43df-897e-4d15298d6df2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/335-bba9447c-7513-43df-897e-4d15298d6df2.txn deleted file mode 100644 index fdfb7cf71..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/335-bba9447c-7513-43df-897e-4d15298d6df2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/336-c4f52dcd-392d-4636-afb8-2baf90677573.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/336-c4f52dcd-392d-4636-afb8-2baf90677573.txn deleted file mode 100644 index d95e81e93..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/336-c4f52dcd-392d-4636-afb8-2baf90677573.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/337-58b8cdb7-0509-49b2-87e7-e5550fd1f51c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/337-58b8cdb7-0509-49b2-87e7-e5550fd1f51c.txn deleted file mode 100644 index 5d50fc72b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/337-58b8cdb7-0509-49b2-87e7-e5550fd1f51c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/338-27f9e921-4568-4e83-b630-f6a62537a3bd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/338-27f9e921-4568-4e83-b630-f6a62537a3bd.txn deleted file mode 100644 index cf7125fb5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/338-27f9e921-4568-4e83-b630-f6a62537a3bd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/339-12c20fb0-023c-40f9-8a76-05372d7e92ce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/339-12c20fb0-023c-40f9-8a76-05372d7e92ce.txn deleted file mode 100644 index 161d832f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/339-12c20fb0-023c-40f9-8a76-05372d7e92ce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/34-d92c03c5-f1d4-45d5-9993-e20c9f695e86.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/34-d92c03c5-f1d4-45d5-9993-e20c9f695e86.txn deleted file mode 100644 index ad2fa95a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/34-d92c03c5-f1d4-45d5-9993-e20c9f695e86.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/340-fdc84c53-2ff6-401b-90a5-d4b8c6bf6497.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/340-fdc84c53-2ff6-401b-90a5-d4b8c6bf6497.txn deleted file mode 100644 index 48f3725c2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/340-fdc84c53-2ff6-401b-90a5-d4b8c6bf6497.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/341-80348251-b62e-45be-848c-cd76ae81c20b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/341-80348251-b62e-45be-848c-cd76ae81c20b.txn deleted file mode 100644 index 45e0de073..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/341-80348251-b62e-45be-848c-cd76ae81c20b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/342-f4f54a9a-9da1-45cd-8bb4-9e811eea27ec.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/342-f4f54a9a-9da1-45cd-8bb4-9e811eea27ec.txn deleted file mode 100644 index bca080fc7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/342-f4f54a9a-9da1-45cd-8bb4-9e811eea27ec.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/343-b3da24e5-6319-4fda-9964-a606cfefd58e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/343-b3da24e5-6319-4fda-9964-a606cfefd58e.txn deleted file mode 100644 index 0bfd8ed5f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/343-b3da24e5-6319-4fda-9964-a606cfefd58e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/344-3fc624c3-fa54-41a0-81ae-eb12e8897863.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/344-3fc624c3-fa54-41a0-81ae-eb12e8897863.txn deleted file mode 100644 index 48480be3a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/344-3fc624c3-fa54-41a0-81ae-eb12e8897863.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/345-c6c3cbc3-ea33-4e7e-91d9-c9171a494d23.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/345-c6c3cbc3-ea33-4e7e-91d9-c9171a494d23.txn deleted file mode 100644 index a3d76a0bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/345-c6c3cbc3-ea33-4e7e-91d9-c9171a494d23.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/346-d2c94906-550f-4f80-8533-d3a765cdf372.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/346-d2c94906-550f-4f80-8533-d3a765cdf372.txn deleted file mode 100644 index 84253619c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/346-d2c94906-550f-4f80-8533-d3a765cdf372.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/347-b11b6afb-aba6-40aa-83aa-ef9968d5ed70.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/347-b11b6afb-aba6-40aa-83aa-ef9968d5ed70.txn deleted file mode 100644 index e147d2518..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/347-b11b6afb-aba6-40aa-83aa-ef9968d5ed70.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/348-3115d3e4-a4b5-4645-9fbf-1466bd378217.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/348-3115d3e4-a4b5-4645-9fbf-1466bd378217.txn deleted file mode 100644 index 0d3b58114..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/348-3115d3e4-a4b5-4645-9fbf-1466bd378217.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/349-2d03e3f7-4cc5-4903-b657-e20dafc7c146.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/349-2d03e3f7-4cc5-4903-b657-e20dafc7c146.txn deleted file mode 100644 index 35158d269..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/349-2d03e3f7-4cc5-4903-b657-e20dafc7c146.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/35-94959a15-9728-4de8-9ab5-7be0550e5d40.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/35-94959a15-9728-4de8-9ab5-7be0550e5d40.txn deleted file mode 100644 index 6ddf5d868..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/35-94959a15-9728-4de8-9ab5-7be0550e5d40.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/350-a9249b36-3b7e-4a1d-96e1-d541554b799e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/350-a9249b36-3b7e-4a1d-96e1-d541554b799e.txn deleted file mode 100644 index fce2eb66b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/350-a9249b36-3b7e-4a1d-96e1-d541554b799e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/351-de3579d0-ca8a-4421-abb0-1d08654d92e9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/351-de3579d0-ca8a-4421-abb0-1d08654d92e9.txn deleted file mode 100644 index 8e502a64c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/351-de3579d0-ca8a-4421-abb0-1d08654d92e9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/352-f620f947-1d6d-49cd-a2db-530c95da238e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/352-f620f947-1d6d-49cd-a2db-530c95da238e.txn deleted file mode 100644 index f2b3bc7b6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/352-f620f947-1d6d-49cd-a2db-530c95da238e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/353-88617d10-9fe6-4ecb-8e7a-2d87b5b5e5b1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/353-88617d10-9fe6-4ecb-8e7a-2d87b5b5e5b1.txn deleted file mode 100644 index c6c107f31..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/353-88617d10-9fe6-4ecb-8e7a-2d87b5b5e5b1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/354-0bfd4f43-481d-4c9d-8cc0-c94f05e34092.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/354-0bfd4f43-481d-4c9d-8cc0-c94f05e34092.txn deleted file mode 100644 index 70a0fa7a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/354-0bfd4f43-481d-4c9d-8cc0-c94f05e34092.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/355-22bb9b28-81d7-4353-b51f-2c95ee0e67b0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/355-22bb9b28-81d7-4353-b51f-2c95ee0e67b0.txn deleted file mode 100644 index 6b5a567ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/355-22bb9b28-81d7-4353-b51f-2c95ee0e67b0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/356-b4c8e90a-30c1-439f-b3de-90eb9afd0477.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/356-b4c8e90a-30c1-439f-b3de-90eb9afd0477.txn deleted file mode 100644 index 8ab22560a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/356-b4c8e90a-30c1-439f-b3de-90eb9afd0477.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/357-31b5dbe4-56eb-4fd3-8a12-975e6393fd49.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/357-31b5dbe4-56eb-4fd3-8a12-975e6393fd49.txn deleted file mode 100644 index e5ebb0351..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/357-31b5dbe4-56eb-4fd3-8a12-975e6393fd49.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/358-435f316c-8c5f-4eb2-88d3-3e89644add9b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/358-435f316c-8c5f-4eb2-88d3-3e89644add9b.txn deleted file mode 100644 index e58646eed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/358-435f316c-8c5f-4eb2-88d3-3e89644add9b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/359-fda3b631-c2d2-4fe1-9a50-ea51c0b979be.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/359-fda3b631-c2d2-4fe1-9a50-ea51c0b979be.txn deleted file mode 100644 index f1f2c12a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/359-fda3b631-c2d2-4fe1-9a50-ea51c0b979be.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/36-21eeb337-2348-437c-a26e-5d3792829a25.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/36-21eeb337-2348-437c-a26e-5d3792829a25.txn deleted file mode 100644 index 4986fb5a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/36-21eeb337-2348-437c-a26e-5d3792829a25.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/360-378e5850-ca66-4f9c-91f6-ab5c5ad2068e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/360-378e5850-ca66-4f9c-91f6-ab5c5ad2068e.txn deleted file mode 100644 index cfb846c0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/360-378e5850-ca66-4f9c-91f6-ab5c5ad2068e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/361-39ac0cc9-f8ca-4be3-849a-fb326956ea15.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/361-39ac0cc9-f8ca-4be3-849a-fb326956ea15.txn deleted file mode 100644 index b5316f578..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/361-39ac0cc9-f8ca-4be3-849a-fb326956ea15.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/362-fac1ad67-ec84-419b-ac35-6fabd88b34bb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/362-fac1ad67-ec84-419b-ac35-6fabd88b34bb.txn deleted file mode 100644 index 7095e5a2b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/362-fac1ad67-ec84-419b-ac35-6fabd88b34bb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/363-e54d398e-274e-4c62-a68f-f28afaddb24a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/363-e54d398e-274e-4c62-a68f-f28afaddb24a.txn deleted file mode 100644 index dbb1a2a3d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/363-e54d398e-274e-4c62-a68f-f28afaddb24a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/364-981c6636-5454-453f-9060-05499b2d59ec.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/364-981c6636-5454-453f-9060-05499b2d59ec.txn deleted file mode 100644 index c560c90be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/364-981c6636-5454-453f-9060-05499b2d59ec.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/365-304f5477-e0d6-42a2-8a96-d79529de5e86.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/365-304f5477-e0d6-42a2-8a96-d79529de5e86.txn deleted file mode 100644 index dd47ca8f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/365-304f5477-e0d6-42a2-8a96-d79529de5e86.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/366-1815359c-a52f-4aeb-ac92-67205d2eac73.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/366-1815359c-a52f-4aeb-ac92-67205d2eac73.txn deleted file mode 100644 index 28add0c0a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/366-1815359c-a52f-4aeb-ac92-67205d2eac73.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/367-9d40d130-da2a-4922-a826-da4aa2872803.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/367-9d40d130-da2a-4922-a826-da4aa2872803.txn deleted file mode 100644 index 15590c78c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/367-9d40d130-da2a-4922-a826-da4aa2872803.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/368-83b34a87-350f-4a40-9d89-b092fb4ce330.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/368-83b34a87-350f-4a40-9d89-b092fb4ce330.txn deleted file mode 100644 index 070b3530f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/368-83b34a87-350f-4a40-9d89-b092fb4ce330.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/369-e38cdd49-939e-497d-8937-f42d15f9f4cd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/369-e38cdd49-939e-497d-8937-f42d15f9f4cd.txn deleted file mode 100644 index 82cd0d373..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/369-e38cdd49-939e-497d-8937-f42d15f9f4cd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/37-71d05501-662e-44f9-b4fc-5f32796ad764.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/37-71d05501-662e-44f9-b4fc-5f32796ad764.txn deleted file mode 100644 index 2c89d42ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/37-71d05501-662e-44f9-b4fc-5f32796ad764.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/370-e90ece0b-264d-4f2b-8c59-ae722b3995e7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/370-e90ece0b-264d-4f2b-8c59-ae722b3995e7.txn deleted file mode 100644 index 992d6915e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/370-e90ece0b-264d-4f2b-8c59-ae722b3995e7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/371-86b71d8d-3ef2-4e3b-a465-ce04042c28ce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/371-86b71d8d-3ef2-4e3b-a465-ce04042c28ce.txn deleted file mode 100644 index 91583b91a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/371-86b71d8d-3ef2-4e3b-a465-ce04042c28ce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/372-ca28001b-3b19-4a66-ae61-6b6dd4d1a774.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/372-ca28001b-3b19-4a66-ae61-6b6dd4d1a774.txn deleted file mode 100644 index 8af5c8ec5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/372-ca28001b-3b19-4a66-ae61-6b6dd4d1a774.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/373-cf8ec21f-916b-43b9-8f23-89675b9ed0cf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/373-cf8ec21f-916b-43b9-8f23-89675b9ed0cf.txn deleted file mode 100644 index 23b7c3953..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/373-cf8ec21f-916b-43b9-8f23-89675b9ed0cf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/374-813df37c-e9c3-421b-bc46-3c4e0f64f4c4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/374-813df37c-e9c3-421b-bc46-3c4e0f64f4c4.txn deleted file mode 100644 index 05971bdcc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/374-813df37c-e9c3-421b-bc46-3c4e0f64f4c4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/375-a9f48e0a-c748-4a7b-8fa7-7673e83e9f7e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/375-a9f48e0a-c748-4a7b-8fa7-7673e83e9f7e.txn deleted file mode 100644 index 080968b4a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/375-a9f48e0a-c748-4a7b-8fa7-7673e83e9f7e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/376-2ea34f03-5e0a-47e2-8d59-c949194f6bc1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/376-2ea34f03-5e0a-47e2-8d59-c949194f6bc1.txn deleted file mode 100644 index f9f2fd6c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/376-2ea34f03-5e0a-47e2-8d59-c949194f6bc1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/377-211bc7c6-b9d8-4acd-b396-25802e782ee3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/377-211bc7c6-b9d8-4acd-b396-25802e782ee3.txn deleted file mode 100644 index 503244a41..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/377-211bc7c6-b9d8-4acd-b396-25802e782ee3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/378-6047cde2-c834-4c40-aea5-220c10a819ab.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/378-6047cde2-c834-4c40-aea5-220c10a819ab.txn deleted file mode 100644 index 16312cedf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/378-6047cde2-c834-4c40-aea5-220c10a819ab.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/379-f4e42fe5-a4d8-4ead-911d-7cb4ecf6a931.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/379-f4e42fe5-a4d8-4ead-911d-7cb4ecf6a931.txn deleted file mode 100644 index 9f81f5f7f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/379-f4e42fe5-a4d8-4ead-911d-7cb4ecf6a931.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/38-e2df9c21-3fa5-458d-a650-d4d6d84c7e82.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/38-e2df9c21-3fa5-458d-a650-d4d6d84c7e82.txn deleted file mode 100644 index 79dba404e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/38-e2df9c21-3fa5-458d-a650-d4d6d84c7e82.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/380-bf301c85-6d53-46f3-bc0c-fe758acdca07.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/380-bf301c85-6d53-46f3-bc0c-fe758acdca07.txn deleted file mode 100644 index 2d0e027f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/380-bf301c85-6d53-46f3-bc0c-fe758acdca07.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/381-b49ac327-eca6-4b1b-85d3-0969065c1a2a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/381-b49ac327-eca6-4b1b-85d3-0969065c1a2a.txn deleted file mode 100644 index d02c55b13..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/381-b49ac327-eca6-4b1b-85d3-0969065c1a2a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/382-a54d2fff-569e-43a9-96b0-6bfb2a7927bc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/382-a54d2fff-569e-43a9-96b0-6bfb2a7927bc.txn deleted file mode 100644 index db9aa7ce3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/382-a54d2fff-569e-43a9-96b0-6bfb2a7927bc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/383-4f1450f8-94f9-4822-80fb-479c0e6d8fe0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/383-4f1450f8-94f9-4822-80fb-479c0e6d8fe0.txn deleted file mode 100644 index c7394904a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/383-4f1450f8-94f9-4822-80fb-479c0e6d8fe0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/384-136db1e7-3c14-4892-b1d1-773cd9ce79a1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/384-136db1e7-3c14-4892-b1d1-773cd9ce79a1.txn deleted file mode 100644 index b44939c2f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/384-136db1e7-3c14-4892-b1d1-773cd9ce79a1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/385-aa9555d1-a571-413d-a96e-b2607749d849.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/385-aa9555d1-a571-413d-a96e-b2607749d849.txn deleted file mode 100644 index 32a448dc4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/385-aa9555d1-a571-413d-a96e-b2607749d849.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/386-44f55e80-e5e8-4758-9313-da1f8cccef32.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/386-44f55e80-e5e8-4758-9313-da1f8cccef32.txn deleted file mode 100644 index 3d320adf5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/386-44f55e80-e5e8-4758-9313-da1f8cccef32.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/387-c98de435-dd7f-4aa5-a3a8-75c6bfef53ae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/387-c98de435-dd7f-4aa5-a3a8-75c6bfef53ae.txn deleted file mode 100644 index 0c3893607..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/387-c98de435-dd7f-4aa5-a3a8-75c6bfef53ae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/388-8495012e-c325-4701-84b8-387505ed7f1f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/388-8495012e-c325-4701-84b8-387505ed7f1f.txn deleted file mode 100644 index 5faf01d43..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/388-8495012e-c325-4701-84b8-387505ed7f1f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/389-2415719e-0d81-4da1-aed6-9864fd2a6f50.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/389-2415719e-0d81-4da1-aed6-9864fd2a6f50.txn deleted file mode 100644 index 9053b55e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/389-2415719e-0d81-4da1-aed6-9864fd2a6f50.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/39-975e902b-6c8e-4739-9329-5591c4a2f449.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/39-975e902b-6c8e-4739-9329-5591c4a2f449.txn deleted file mode 100644 index 547b36988..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/39-975e902b-6c8e-4739-9329-5591c4a2f449.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/390-b87a5da9-8b1e-4328-93f2-d8e664a0cff9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/390-b87a5da9-8b1e-4328-93f2-d8e664a0cff9.txn deleted file mode 100644 index b625caca7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/390-b87a5da9-8b1e-4328-93f2-d8e664a0cff9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/391-7ce08be1-4c39-418e-b826-b00b339dbbf2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/391-7ce08be1-4c39-418e-b826-b00b339dbbf2.txn deleted file mode 100644 index f6c8cd895..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/391-7ce08be1-4c39-418e-b826-b00b339dbbf2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/392-a4eb86c6-2623-4929-9c4b-0e1cfb2a114f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/392-a4eb86c6-2623-4929-9c4b-0e1cfb2a114f.txn deleted file mode 100644 index 90364a7d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/392-a4eb86c6-2623-4929-9c4b-0e1cfb2a114f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/393-b4fa3506-f373-42e1-b922-fa459f7fca66.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/393-b4fa3506-f373-42e1-b922-fa459f7fca66.txn deleted file mode 100644 index fc206062e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/393-b4fa3506-f373-42e1-b922-fa459f7fca66.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/394-47b162d4-abf0-47ed-9704-a0c4d63bae8c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/394-47b162d4-abf0-47ed-9704-a0c4d63bae8c.txn deleted file mode 100644 index 9e86ecdb7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/394-47b162d4-abf0-47ed-9704-a0c4d63bae8c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/395-204055bb-bf1f-4a6a-862a-b9a5e1a67d10.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/395-204055bb-bf1f-4a6a-862a-b9a5e1a67d10.txn deleted file mode 100644 index b336219d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/395-204055bb-bf1f-4a6a-862a-b9a5e1a67d10.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/396-6530b02e-eaa4-44a0-bae7-8e404e9a293a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/396-6530b02e-eaa4-44a0-bae7-8e404e9a293a.txn deleted file mode 100644 index aa1033a70..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/396-6530b02e-eaa4-44a0-bae7-8e404e9a293a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/397-23e1e290-02c1-4ff4-983d-12bd463ae60b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/397-23e1e290-02c1-4ff4-983d-12bd463ae60b.txn deleted file mode 100644 index f8861e7c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/397-23e1e290-02c1-4ff4-983d-12bd463ae60b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/398-547a0cc8-8d48-4878-b64b-06f4271620fd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/398-547a0cc8-8d48-4878-b64b-06f4271620fd.txn deleted file mode 100644 index 854f1f3ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/398-547a0cc8-8d48-4878-b64b-06f4271620fd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/399-12647add-22a2-4e26-9a04-a59a85ca2b3d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/399-12647add-22a2-4e26-9a04-a59a85ca2b3d.txn deleted file mode 100644 index ae7810958..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/399-12647add-22a2-4e26-9a04-a59a85ca2b3d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/4-cf282a0b-c423-4340-b770-9b3f54e40153.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/4-cf282a0b-c423-4340-b770-9b3f54e40153.txn deleted file mode 100644 index aa1f20c6a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/4-cf282a0b-c423-4340-b770-9b3f54e40153.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/40-bc7e3b49-bdaa-4798-851e-71bb5c01a379.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/40-bc7e3b49-bdaa-4798-851e-71bb5c01a379.txn deleted file mode 100644 index 4c799d341..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/40-bc7e3b49-bdaa-4798-851e-71bb5c01a379.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/400-8d857cb0-1543-4122-a3bb-53c04deaafa2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/400-8d857cb0-1543-4122-a3bb-53c04deaafa2.txn deleted file mode 100644 index 124a7daab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/400-8d857cb0-1543-4122-a3bb-53c04deaafa2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/401-72a6220c-4f8a-4eb1-9dd6-aab21d47f1df.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/401-72a6220c-4f8a-4eb1-9dd6-aab21d47f1df.txn deleted file mode 100644 index 2debc226d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/401-72a6220c-4f8a-4eb1-9dd6-aab21d47f1df.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/402-a1254b5e-4b4a-42fc-91ea-d27b9a55c420.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/402-a1254b5e-4b4a-42fc-91ea-d27b9a55c420.txn deleted file mode 100644 index d0958b61e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/402-a1254b5e-4b4a-42fc-91ea-d27b9a55c420.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/403-405b82d7-8fdc-42e0-a83d-5b8a3f5cf126.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/403-405b82d7-8fdc-42e0-a83d-5b8a3f5cf126.txn deleted file mode 100644 index 6f9a1833e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/403-405b82d7-8fdc-42e0-a83d-5b8a3f5cf126.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/404-32ab1e43-1143-4d3e-9788-983f8751ec6f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/404-32ab1e43-1143-4d3e-9788-983f8751ec6f.txn deleted file mode 100644 index 8e3fcbe7d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/404-32ab1e43-1143-4d3e-9788-983f8751ec6f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/405-84e72321-e398-40f2-911e-2468684d0d4c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/405-84e72321-e398-40f2-911e-2468684d0d4c.txn deleted file mode 100644 index f564c97ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/405-84e72321-e398-40f2-911e-2468684d0d4c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/406-c0a1d81a-6829-43a7-95e4-1f83217cd82e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/406-c0a1d81a-6829-43a7-95e4-1f83217cd82e.txn deleted file mode 100644 index 940d26659..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/406-c0a1d81a-6829-43a7-95e4-1f83217cd82e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/407-fcc19eee-213f-4d84-94f6-bc2b0f339e17.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/407-fcc19eee-213f-4d84-94f6-bc2b0f339e17.txn deleted file mode 100644 index 80ce6561a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/407-fcc19eee-213f-4d84-94f6-bc2b0f339e17.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/408-7693d29f-bdba-49a3-a746-a5ec9742399a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/408-7693d29f-bdba-49a3-a746-a5ec9742399a.txn deleted file mode 100644 index 92996f828..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/408-7693d29f-bdba-49a3-a746-a5ec9742399a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/409-9a3cc17a-2c25-4fcb-bd8c-cf37abdc6413.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/409-9a3cc17a-2c25-4fcb-bd8c-cf37abdc6413.txn deleted file mode 100644 index 3d8ad407d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/409-9a3cc17a-2c25-4fcb-bd8c-cf37abdc6413.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/41-dcc4e632-f3a8-4998-b94a-12c769eacd31.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/41-dcc4e632-f3a8-4998-b94a-12c769eacd31.txn deleted file mode 100644 index 1844db6aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/41-dcc4e632-f3a8-4998-b94a-12c769eacd31.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/410-fbbc3200-f1e4-43e6-ab41-96deba625ba1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/410-fbbc3200-f1e4-43e6-ab41-96deba625ba1.txn deleted file mode 100644 index 4b4ffd7be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/410-fbbc3200-f1e4-43e6-ab41-96deba625ba1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/411-bc216812-8fe7-4c57-9e07-332b7683a4ea.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/411-bc216812-8fe7-4c57-9e07-332b7683a4ea.txn deleted file mode 100644 index 2a6af0895..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/411-bc216812-8fe7-4c57-9e07-332b7683a4ea.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/412-4d81f032-921d-4953-bf0d-8eda05c25958.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/412-4d81f032-921d-4953-bf0d-8eda05c25958.txn deleted file mode 100644 index f6cff8e06..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/412-4d81f032-921d-4953-bf0d-8eda05c25958.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/413-75806e6e-a852-4e2a-a499-e12eb73d0069.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/413-75806e6e-a852-4e2a-a499-e12eb73d0069.txn deleted file mode 100644 index 662ebeb90..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/413-75806e6e-a852-4e2a-a499-e12eb73d0069.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/414-707665a7-3386-450a-9c33-69fd653a70d3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/414-707665a7-3386-450a-9c33-69fd653a70d3.txn deleted file mode 100644 index d99e075ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/414-707665a7-3386-450a-9c33-69fd653a70d3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/415-1914686e-fc3c-4d96-9208-79406364a478.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/415-1914686e-fc3c-4d96-9208-79406364a478.txn deleted file mode 100644 index a172161ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/415-1914686e-fc3c-4d96-9208-79406364a478.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/416-940ae7bd-d169-4c15-8b9a-b6fb78a2240d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/416-940ae7bd-d169-4c15-8b9a-b6fb78a2240d.txn deleted file mode 100644 index 26b808115..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/416-940ae7bd-d169-4c15-8b9a-b6fb78a2240d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/417-bbca6154-428d-402b-a800-895813e92801.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/417-bbca6154-428d-402b-a800-895813e92801.txn deleted file mode 100644 index 76cbc5197..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/417-bbca6154-428d-402b-a800-895813e92801.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/418-2d1d73f1-ad5a-4b8f-b31d-4eceef505485.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/418-2d1d73f1-ad5a-4b8f-b31d-4eceef505485.txn deleted file mode 100644 index be683df1a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/418-2d1d73f1-ad5a-4b8f-b31d-4eceef505485.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/419-a7170726-cd81-481d-8e57-89d79069ebdc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/419-a7170726-cd81-481d-8e57-89d79069ebdc.txn deleted file mode 100644 index 4e0b6a1f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/419-a7170726-cd81-481d-8e57-89d79069ebdc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/42-56654994-ea97-426b-ac1b-a95143a53006.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/42-56654994-ea97-426b-ac1b-a95143a53006.txn deleted file mode 100644 index 65954fd9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/42-56654994-ea97-426b-ac1b-a95143a53006.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/420-12815831-7128-4c38-b306-b849a9d9df89.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/420-12815831-7128-4c38-b306-b849a9d9df89.txn deleted file mode 100644 index 7fd34c313..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/420-12815831-7128-4c38-b306-b849a9d9df89.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/421-702a4b98-1bc5-4e6d-a37d-b15dec7f93eb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/421-702a4b98-1bc5-4e6d-a37d-b15dec7f93eb.txn deleted file mode 100644 index c93e09925..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/421-702a4b98-1bc5-4e6d-a37d-b15dec7f93eb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/422-e4cb39a9-111d-42d7-b100-f0c3aef70b37.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/422-e4cb39a9-111d-42d7-b100-f0c3aef70b37.txn deleted file mode 100644 index 63bd24984..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/422-e4cb39a9-111d-42d7-b100-f0c3aef70b37.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/423-6d4402dd-604a-47d2-91d2-eecee8f878c7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/423-6d4402dd-604a-47d2-91d2-eecee8f878c7.txn deleted file mode 100644 index bedf0df2e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/423-6d4402dd-604a-47d2-91d2-eecee8f878c7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/424-05b494e4-028e-435d-bce5-52841951361f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/424-05b494e4-028e-435d-bce5-52841951361f.txn deleted file mode 100644 index 950d2203e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/424-05b494e4-028e-435d-bce5-52841951361f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/425-26351934-e450-4931-b79e-ed7ad895de24.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/425-26351934-e450-4931-b79e-ed7ad895de24.txn deleted file mode 100644 index 0a54c2237..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/425-26351934-e450-4931-b79e-ed7ad895de24.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/426-39d9b717-4835-4ea0-9bdc-631a6df0cdeb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/426-39d9b717-4835-4ea0-9bdc-631a6df0cdeb.txn deleted file mode 100644 index bdbcf888f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/426-39d9b717-4835-4ea0-9bdc-631a6df0cdeb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/427-c5b5ed10-cea4-44d4-9a46-d96c01f8c96a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/427-c5b5ed10-cea4-44d4-9a46-d96c01f8c96a.txn deleted file mode 100644 index f7a60fb49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/427-c5b5ed10-cea4-44d4-9a46-d96c01f8c96a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/428-9c7190ef-b325-4029-b3fd-933261b1e3f4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/428-9c7190ef-b325-4029-b3fd-933261b1e3f4.txn deleted file mode 100644 index bef09a06b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/428-9c7190ef-b325-4029-b3fd-933261b1e3f4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/429-ca49c88a-bfca-4260-a017-6a2fbd059e63.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/429-ca49c88a-bfca-4260-a017-6a2fbd059e63.txn deleted file mode 100644 index 16f7ff0d6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/429-ca49c88a-bfca-4260-a017-6a2fbd059e63.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/43-b215bceb-66fb-495d-83aa-4aae7eac65ee.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/43-b215bceb-66fb-495d-83aa-4aae7eac65ee.txn deleted file mode 100644 index 03a599d02..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/43-b215bceb-66fb-495d-83aa-4aae7eac65ee.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/430-d029d902-267f-4bb5-bd6d-c1c66f074f31.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/430-d029d902-267f-4bb5-bd6d-c1c66f074f31.txn deleted file mode 100644 index 44dbd07c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/430-d029d902-267f-4bb5-bd6d-c1c66f074f31.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/431-63eef162-ad7d-4d9f-88ad-e6f7a84c2f54.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/431-63eef162-ad7d-4d9f-88ad-e6f7a84c2f54.txn deleted file mode 100644 index eaf49ef25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/431-63eef162-ad7d-4d9f-88ad-e6f7a84c2f54.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/432-da29a6ac-2a76-4323-baf9-5e319813311b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/432-da29a6ac-2a76-4323-baf9-5e319813311b.txn deleted file mode 100644 index 6a20a0796..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/432-da29a6ac-2a76-4323-baf9-5e319813311b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/433-9e2fea45-7a00-4bae-b1c3-82e2188eb616.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/433-9e2fea45-7a00-4bae-b1c3-82e2188eb616.txn deleted file mode 100644 index 8893fe50f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/433-9e2fea45-7a00-4bae-b1c3-82e2188eb616.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/434-f7fea386-9445-4202-9b47-037381bef3b6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/434-f7fea386-9445-4202-9b47-037381bef3b6.txn deleted file mode 100644 index a4b5c9305..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/434-f7fea386-9445-4202-9b47-037381bef3b6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/435-5772a9ef-ea43-4f23-9c89-f2de4606ef75.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/435-5772a9ef-ea43-4f23-9c89-f2de4606ef75.txn deleted file mode 100644 index 8f361e8bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/435-5772a9ef-ea43-4f23-9c89-f2de4606ef75.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/436-dcfff698-6b05-41a0-ade2-4e5d1e1d282b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/436-dcfff698-6b05-41a0-ade2-4e5d1e1d282b.txn deleted file mode 100644 index 72d4f19b0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/436-dcfff698-6b05-41a0-ade2-4e5d1e1d282b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/437-6bdc6d4a-53af-48e9-a4fc-24fcb2629750.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/437-6bdc6d4a-53af-48e9-a4fc-24fcb2629750.txn deleted file mode 100644 index d3bfb5cc3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/437-6bdc6d4a-53af-48e9-a4fc-24fcb2629750.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/438-cb737859-a6de-41ac-acfe-88f53163364f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/438-cb737859-a6de-41ac-acfe-88f53163364f.txn deleted file mode 100644 index 682ff1e9b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/438-cb737859-a6de-41ac-acfe-88f53163364f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/439-0373a3ae-4564-4740-81e3-1b6ca829eda3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/439-0373a3ae-4564-4740-81e3-1b6ca829eda3.txn deleted file mode 100644 index f5c90836a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/439-0373a3ae-4564-4740-81e3-1b6ca829eda3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/44-1dc62a81-700d-4baa-9bc9-cab031118ffd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/44-1dc62a81-700d-4baa-9bc9-cab031118ffd.txn deleted file mode 100644 index 85244e09a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/44-1dc62a81-700d-4baa-9bc9-cab031118ffd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/440-45183dbd-e779-487e-9ced-6239b4f5168b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/440-45183dbd-e779-487e-9ced-6239b4f5168b.txn deleted file mode 100644 index b997d449d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/440-45183dbd-e779-487e-9ced-6239b4f5168b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/441-1f8ba222-3b60-4e37-9e74-7403bc7250f8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/441-1f8ba222-3b60-4e37-9e74-7403bc7250f8.txn deleted file mode 100644 index 3aacc0dbf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/441-1f8ba222-3b60-4e37-9e74-7403bc7250f8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/442-3b826b56-054d-4f5b-980b-ea156cd1b6cc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/442-3b826b56-054d-4f5b-980b-ea156cd1b6cc.txn deleted file mode 100644 index b4542bee8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/442-3b826b56-054d-4f5b-980b-ea156cd1b6cc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/443-70c0cef8-708c-4636-9d4d-54187772f3d3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/443-70c0cef8-708c-4636-9d4d-54187772f3d3.txn deleted file mode 100644 index 947889256..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/443-70c0cef8-708c-4636-9d4d-54187772f3d3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/444-07d71cb7-18af-4de4-a525-95db96fc14a0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/444-07d71cb7-18af-4de4-a525-95db96fc14a0.txn deleted file mode 100644 index 258b552b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/444-07d71cb7-18af-4de4-a525-95db96fc14a0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/445-8bfa8266-39eb-467c-902b-44108b2c847e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/445-8bfa8266-39eb-467c-902b-44108b2c847e.txn deleted file mode 100644 index bcedf71eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/445-8bfa8266-39eb-467c-902b-44108b2c847e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/446-53c0600b-2210-4211-8547-9dce6468e6d5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/446-53c0600b-2210-4211-8547-9dce6468e6d5.txn deleted file mode 100644 index feb8af1b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/446-53c0600b-2210-4211-8547-9dce6468e6d5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/447-b576a091-cf6c-4ce0-87be-435ec11bf296.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/447-b576a091-cf6c-4ce0-87be-435ec11bf296.txn deleted file mode 100644 index 0a627a7d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/447-b576a091-cf6c-4ce0-87be-435ec11bf296.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/448-d15f6c55-97d2-4450-a249-6972ad3097df.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/448-d15f6c55-97d2-4450-a249-6972ad3097df.txn deleted file mode 100644 index 7aeda699f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/448-d15f6c55-97d2-4450-a249-6972ad3097df.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/449-a378ac66-bf66-47ca-a23c-bde4dda7ce9d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/449-a378ac66-bf66-47ca-a23c-bde4dda7ce9d.txn deleted file mode 100644 index e31be5676..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/449-a378ac66-bf66-47ca-a23c-bde4dda7ce9d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/45-c200c1bb-ac19-481a-866c-0b769f128ace.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/45-c200c1bb-ac19-481a-866c-0b769f128ace.txn deleted file mode 100644 index 1d9847c4c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/45-c200c1bb-ac19-481a-866c-0b769f128ace.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/450-54a99253-aa18-4143-8b13-f426f1ccff50.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/450-54a99253-aa18-4143-8b13-f426f1ccff50.txn deleted file mode 100644 index 76350be58..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/450-54a99253-aa18-4143-8b13-f426f1ccff50.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/451-6001e5d9-e85c-4489-9a3a-5fbf5fca7161.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/451-6001e5d9-e85c-4489-9a3a-5fbf5fca7161.txn deleted file mode 100644 index a02d49bbf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/451-6001e5d9-e85c-4489-9a3a-5fbf5fca7161.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/452-43c47d2f-aaa0-4096-8024-1360c868eafc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/452-43c47d2f-aaa0-4096-8024-1360c868eafc.txn deleted file mode 100644 index b757bb58c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/452-43c47d2f-aaa0-4096-8024-1360c868eafc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/453-0c7fad51-ea64-4c0d-b591-1fc12487bf14.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/453-0c7fad51-ea64-4c0d-b591-1fc12487bf14.txn deleted file mode 100644 index 5a55b3901..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/453-0c7fad51-ea64-4c0d-b591-1fc12487bf14.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/454-73863da5-c2f0-4ab8-bf9d-cf1c7d3c75e7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/454-73863da5-c2f0-4ab8-bf9d-cf1c7d3c75e7.txn deleted file mode 100644 index ba4d409bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/454-73863da5-c2f0-4ab8-bf9d-cf1c7d3c75e7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/455-d2ca070f-c052-40d0-a213-b260874ae4b3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/455-d2ca070f-c052-40d0-a213-b260874ae4b3.txn deleted file mode 100644 index 3cca2076b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/455-d2ca070f-c052-40d0-a213-b260874ae4b3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/456-1f1aed10-97dc-4dd5-a0c1-469090b02549.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/456-1f1aed10-97dc-4dd5-a0c1-469090b02549.txn deleted file mode 100644 index 4be581d01..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/456-1f1aed10-97dc-4dd5-a0c1-469090b02549.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/457-0c5ceb38-6f60-4963-bdab-7045154ebd6d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/457-0c5ceb38-6f60-4963-bdab-7045154ebd6d.txn deleted file mode 100644 index 5fca23bca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/457-0c5ceb38-6f60-4963-bdab-7045154ebd6d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/458-176ba61b-6033-4b2e-a183-02d5a9a42b16.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/458-176ba61b-6033-4b2e-a183-02d5a9a42b16.txn deleted file mode 100644 index b35f001e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/458-176ba61b-6033-4b2e-a183-02d5a9a42b16.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/459-ae803fe6-5367-40cf-868e-f2971a8c4805.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/459-ae803fe6-5367-40cf-868e-f2971a8c4805.txn deleted file mode 100644 index 0f6c1e09d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/459-ae803fe6-5367-40cf-868e-f2971a8c4805.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/46-6e02f8a8-b9f3-437b-ba32-3e1b1ca5b0b3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/46-6e02f8a8-b9f3-437b-ba32-3e1b1ca5b0b3.txn deleted file mode 100644 index 11ab1beef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/46-6e02f8a8-b9f3-437b-ba32-3e1b1ca5b0b3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/460-aff6a095-0db5-4fab-8e0d-3ee2af70b173.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/460-aff6a095-0db5-4fab-8e0d-3ee2af70b173.txn deleted file mode 100644 index 4699df66a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/460-aff6a095-0db5-4fab-8e0d-3ee2af70b173.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/461-7fc52b43-594e-434d-b917-e67b9176c80b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/461-7fc52b43-594e-434d-b917-e67b9176c80b.txn deleted file mode 100644 index 688fa4674..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/461-7fc52b43-594e-434d-b917-e67b9176c80b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/462-813353bd-64f9-4829-a8e1-1e4784ae34de.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/462-813353bd-64f9-4829-a8e1-1e4784ae34de.txn deleted file mode 100644 index d33f235e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/462-813353bd-64f9-4829-a8e1-1e4784ae34de.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/463-93f4357e-a99b-491e-83c7-c2239b8cb7fa.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/463-93f4357e-a99b-491e-83c7-c2239b8cb7fa.txn deleted file mode 100644 index 17fd540a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/463-93f4357e-a99b-491e-83c7-c2239b8cb7fa.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/464-6932c63f-12d9-456c-95aa-7403515f8b13.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/464-6932c63f-12d9-456c-95aa-7403515f8b13.txn deleted file mode 100644 index 22eea7b24..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/464-6932c63f-12d9-456c-95aa-7403515f8b13.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/465-7dece8e1-b66a-4d36-9f3e-3f590d328d9c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/465-7dece8e1-b66a-4d36-9f3e-3f590d328d9c.txn deleted file mode 100644 index 97f6e4088..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/465-7dece8e1-b66a-4d36-9f3e-3f590d328d9c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/466-eb135b56-564a-4194-a6f1-6bc28f8a611e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/466-eb135b56-564a-4194-a6f1-6bc28f8a611e.txn deleted file mode 100644 index 2f04deea7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/466-eb135b56-564a-4194-a6f1-6bc28f8a611e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/467-0cb119e2-d98d-41fe-ab3b-372670194e4d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/467-0cb119e2-d98d-41fe-ab3b-372670194e4d.txn deleted file mode 100644 index 8273fa623..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/467-0cb119e2-d98d-41fe-ab3b-372670194e4d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/468-06da03d2-a6c1-4983-b112-3896c924c27c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/468-06da03d2-a6c1-4983-b112-3896c924c27c.txn deleted file mode 100644 index 7a6af5ba5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/468-06da03d2-a6c1-4983-b112-3896c924c27c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/469-a2671a74-eff8-41d3-bcd7-fde0f18a387b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/469-a2671a74-eff8-41d3-bcd7-fde0f18a387b.txn deleted file mode 100644 index 3785972fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/469-a2671a74-eff8-41d3-bcd7-fde0f18a387b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/47-113d2fb8-445e-4779-b86b-ca28fa4e47f7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/47-113d2fb8-445e-4779-b86b-ca28fa4e47f7.txn deleted file mode 100644 index 83cb01520..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/47-113d2fb8-445e-4779-b86b-ca28fa4e47f7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/470-602452c5-7fb0-42b9-8381-ff729235fc32.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/470-602452c5-7fb0-42b9-8381-ff729235fc32.txn deleted file mode 100644 index c464b7f22..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/470-602452c5-7fb0-42b9-8381-ff729235fc32.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/471-d7b81dfc-4375-4ca7-b48b-dea5dbaddeda.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/471-d7b81dfc-4375-4ca7-b48b-dea5dbaddeda.txn deleted file mode 100644 index 9acbf77df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/471-d7b81dfc-4375-4ca7-b48b-dea5dbaddeda.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/472-1af7b0c7-85a5-4efd-b67b-7de7f12081ea.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/472-1af7b0c7-85a5-4efd-b67b-7de7f12081ea.txn deleted file mode 100644 index a48d67ccd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/472-1af7b0c7-85a5-4efd-b67b-7de7f12081ea.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/473-6afc31f2-d924-4142-bc23-2c7da0bbab1a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/473-6afc31f2-d924-4142-bc23-2c7da0bbab1a.txn deleted file mode 100644 index 577fa41fe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/473-6afc31f2-d924-4142-bc23-2c7da0bbab1a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/474-434c2475-0fda-4f39-8dfd-96bbf59cd0f8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/474-434c2475-0fda-4f39-8dfd-96bbf59cd0f8.txn deleted file mode 100644 index c819e8e30..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/474-434c2475-0fda-4f39-8dfd-96bbf59cd0f8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/475-dcbf98cf-99e8-4b39-9bfc-a129a89c003f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/475-dcbf98cf-99e8-4b39-9bfc-a129a89c003f.txn deleted file mode 100644 index 33cb4727f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/475-dcbf98cf-99e8-4b39-9bfc-a129a89c003f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/476-196d494c-c103-40a5-8a73-6704d0be4c87.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/476-196d494c-c103-40a5-8a73-6704d0be4c87.txn deleted file mode 100644 index d559a89cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/476-196d494c-c103-40a5-8a73-6704d0be4c87.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/477-bf6e444b-ce69-481c-a55f-1a72cde43127.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/477-bf6e444b-ce69-481c-a55f-1a72cde43127.txn deleted file mode 100644 index 65c714f72..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/477-bf6e444b-ce69-481c-a55f-1a72cde43127.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/478-ab90d321-6dc6-479d-abf7-2536ece16594.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/478-ab90d321-6dc6-479d-abf7-2536ece16594.txn deleted file mode 100644 index e0137fd25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/478-ab90d321-6dc6-479d-abf7-2536ece16594.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/479-aa565cb4-4d10-4157-9b1c-8d94c8fcb421.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/479-aa565cb4-4d10-4157-9b1c-8d94c8fcb421.txn deleted file mode 100644 index fafaf7edb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/479-aa565cb4-4d10-4157-9b1c-8d94c8fcb421.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/48-f4c5f875-065f-4400-9164-6e0cdd67c618.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/48-f4c5f875-065f-4400-9164-6e0cdd67c618.txn deleted file mode 100644 index b241add82..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/48-f4c5f875-065f-4400-9164-6e0cdd67c618.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/480-ce9d3bf6-9ef0-43d4-a15c-6e3c56e83490.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/480-ce9d3bf6-9ef0-43d4-a15c-6e3c56e83490.txn deleted file mode 100644 index c6b410912..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/480-ce9d3bf6-9ef0-43d4-a15c-6e3c56e83490.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/481-0940840e-8fb1-4e92-9ffe-1acfcb0b6def.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/481-0940840e-8fb1-4e92-9ffe-1acfcb0b6def.txn deleted file mode 100644 index 835799cad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/481-0940840e-8fb1-4e92-9ffe-1acfcb0b6def.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/482-9950d2e3-000a-4d40-ba24-3ee0d3733618.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/482-9950d2e3-000a-4d40-ba24-3ee0d3733618.txn deleted file mode 100644 index dc7faa0e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/482-9950d2e3-000a-4d40-ba24-3ee0d3733618.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/483-146a8993-1598-4161-97e7-5d4d8d76d91e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/483-146a8993-1598-4161-97e7-5d4d8d76d91e.txn deleted file mode 100644 index 70b80e321..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/483-146a8993-1598-4161-97e7-5d4d8d76d91e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/484-b28578e3-4808-4b53-8fd9-2c3402bbda4d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/484-b28578e3-4808-4b53-8fd9-2c3402bbda4d.txn deleted file mode 100644 index ecd9697d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/484-b28578e3-4808-4b53-8fd9-2c3402bbda4d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/485-176ac532-5bc3-403a-ba68-cb7220a717d6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/485-176ac532-5bc3-403a-ba68-cb7220a717d6.txn deleted file mode 100644 index afa049976..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/485-176ac532-5bc3-403a-ba68-cb7220a717d6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/486-bb6e4e1c-2014-4557-8004-52e1ebf347c1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/486-bb6e4e1c-2014-4557-8004-52e1ebf347c1.txn deleted file mode 100644 index 16d1f747d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/486-bb6e4e1c-2014-4557-8004-52e1ebf347c1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/487-6ee5cae4-4f45-4f3d-9dab-9e5eb747d48b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/487-6ee5cae4-4f45-4f3d-9dab-9e5eb747d48b.txn deleted file mode 100644 index e9d722e4e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/487-6ee5cae4-4f45-4f3d-9dab-9e5eb747d48b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/488-f2493728-a16d-44bb-a9de-d6e217619aa3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/488-f2493728-a16d-44bb-a9de-d6e217619aa3.txn deleted file mode 100644 index 37909a180..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/488-f2493728-a16d-44bb-a9de-d6e217619aa3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/489-d0df0506-10a5-426d-bf02-d6b5fdc313b0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/489-d0df0506-10a5-426d-bf02-d6b5fdc313b0.txn deleted file mode 100644 index 3fa50c36e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/489-d0df0506-10a5-426d-bf02-d6b5fdc313b0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/49-d7fed5d9-5333-4a63-a487-bc0e6b5be5c2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/49-d7fed5d9-5333-4a63-a487-bc0e6b5be5c2.txn deleted file mode 100644 index a93118f4b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/49-d7fed5d9-5333-4a63-a487-bc0e6b5be5c2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/490-779d5401-33b6-4cee-bc4e-facff4d8d42e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/490-779d5401-33b6-4cee-bc4e-facff4d8d42e.txn deleted file mode 100644 index e9f728cb6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/490-779d5401-33b6-4cee-bc4e-facff4d8d42e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/491-a00325c4-e2e9-4ab3-a980-c1d4060dfe53.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/491-a00325c4-e2e9-4ab3-a980-c1d4060dfe53.txn deleted file mode 100644 index 32b543ae0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/491-a00325c4-e2e9-4ab3-a980-c1d4060dfe53.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/492-1c88dbe8-8565-4ac6-968b-06773fd21ebd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/492-1c88dbe8-8565-4ac6-968b-06773fd21ebd.txn deleted file mode 100644 index f685ffe7e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/492-1c88dbe8-8565-4ac6-968b-06773fd21ebd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/493-bca87df6-8d30-499b-abd4-82e80b002068.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/493-bca87df6-8d30-499b-abd4-82e80b002068.txn deleted file mode 100644 index fe303da18..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/493-bca87df6-8d30-499b-abd4-82e80b002068.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/494-7c390963-e873-49b6-ae38-6eb407e1f170.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/494-7c390963-e873-49b6-ae38-6eb407e1f170.txn deleted file mode 100644 index e76b5b38b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/494-7c390963-e873-49b6-ae38-6eb407e1f170.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/495-c122bd52-a311-4f44-bc60-6656d8a145b0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/495-c122bd52-a311-4f44-bc60-6656d8a145b0.txn deleted file mode 100644 index 09e3c619c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/495-c122bd52-a311-4f44-bc60-6656d8a145b0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/496-01078fec-9254-4201-a5ff-ceecff499d64.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/496-01078fec-9254-4201-a5ff-ceecff499d64.txn deleted file mode 100644 index 0cbb66b60..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/496-01078fec-9254-4201-a5ff-ceecff499d64.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/497-9d6fe29c-1555-43bb-8412-8926df142271.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/497-9d6fe29c-1555-43bb-8412-8926df142271.txn deleted file mode 100644 index 22c582b3d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/497-9d6fe29c-1555-43bb-8412-8926df142271.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/498-d4970259-3627-4890-b9f8-12b0584fd591.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/498-d4970259-3627-4890-b9f8-12b0584fd591.txn deleted file mode 100644 index c650083c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/498-d4970259-3627-4890-b9f8-12b0584fd591.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/499-014a83db-6656-4604-92a9-667c7441d954.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/499-014a83db-6656-4604-92a9-667c7441d954.txn deleted file mode 100644 index 79b053abc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/499-014a83db-6656-4604-92a9-667c7441d954.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/5-2f822629-489a-4c0e-aeac-8e9324fa9679.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/5-2f822629-489a-4c0e-aeac-8e9324fa9679.txn deleted file mode 100644 index b40f9d899..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/5-2f822629-489a-4c0e-aeac-8e9324fa9679.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/50-81b9e520-b590-4373-93ab-0927f12e8f20.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/50-81b9e520-b590-4373-93ab-0927f12e8f20.txn deleted file mode 100644 index 0086a1328..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/50-81b9e520-b590-4373-93ab-0927f12e8f20.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/500-26f97779-f961-422d-9ead-676a0a8ecc0f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/500-26f97779-f961-422d-9ead-676a0a8ecc0f.txn deleted file mode 100644 index 497f82a84..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/500-26f97779-f961-422d-9ead-676a0a8ecc0f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/501-51550a30-86d5-45ae-a4db-aa08b2ff7fd4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/501-51550a30-86d5-45ae-a4db-aa08b2ff7fd4.txn deleted file mode 100644 index 9b0931d75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/501-51550a30-86d5-45ae-a4db-aa08b2ff7fd4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/502-2b68a529-0dd8-44bd-8b2b-cdb853c8ff12.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/502-2b68a529-0dd8-44bd-8b2b-cdb853c8ff12.txn deleted file mode 100644 index df26dbcab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/502-2b68a529-0dd8-44bd-8b2b-cdb853c8ff12.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/503-e111fba3-0e70-40a1-bffe-c37aa4ad5875.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/503-e111fba3-0e70-40a1-bffe-c37aa4ad5875.txn deleted file mode 100644 index a81038efd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/503-e111fba3-0e70-40a1-bffe-c37aa4ad5875.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/504-5943685a-8bb4-4e8f-9c8b-bb6546bbe473.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/504-5943685a-8bb4-4e8f-9c8b-bb6546bbe473.txn deleted file mode 100644 index f4f46a6fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/504-5943685a-8bb4-4e8f-9c8b-bb6546bbe473.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/505-e3bbfa92-54e3-430e-9c66-2dda67ae50b3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/505-e3bbfa92-54e3-430e-9c66-2dda67ae50b3.txn deleted file mode 100644 index 8ed4a4b9b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/505-e3bbfa92-54e3-430e-9c66-2dda67ae50b3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/506-6327e50f-7d5b-421d-8882-8331013fbbd1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/506-6327e50f-7d5b-421d-8882-8331013fbbd1.txn deleted file mode 100644 index 3e0af8478..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/506-6327e50f-7d5b-421d-8882-8331013fbbd1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/507-9d5de49d-bc6b-4f5e-9d87-e0e910151f0c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/507-9d5de49d-bc6b-4f5e-9d87-e0e910151f0c.txn deleted file mode 100644 index b367b67af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/507-9d5de49d-bc6b-4f5e-9d87-e0e910151f0c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/508-9118e310-b9b4-4bfb-830d-d6d45c27dac9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/508-9118e310-b9b4-4bfb-830d-d6d45c27dac9.txn deleted file mode 100644 index 24125d4d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/508-9118e310-b9b4-4bfb-830d-d6d45c27dac9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/509-4a12b923-6104-4ddc-ac17-1e5f72e8975f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/509-4a12b923-6104-4ddc-ac17-1e5f72e8975f.txn deleted file mode 100644 index 6ee3ab6cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/509-4a12b923-6104-4ddc-ac17-1e5f72e8975f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/51-822f1435-4ef2-4302-8a98-8f205ea95988.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/51-822f1435-4ef2-4302-8a98-8f205ea95988.txn deleted file mode 100644 index fe373482d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/51-822f1435-4ef2-4302-8a98-8f205ea95988.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/510-0ef356a5-cf8c-4477-937e-1c52c8019cf6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/510-0ef356a5-cf8c-4477-937e-1c52c8019cf6.txn deleted file mode 100644 index ec0230475..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/510-0ef356a5-cf8c-4477-937e-1c52c8019cf6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/511-4e0b2c2b-b1b6-4ee9-b04b-deab16f03464.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/511-4e0b2c2b-b1b6-4ee9-b04b-deab16f03464.txn deleted file mode 100644 index 5a6b4401b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/511-4e0b2c2b-b1b6-4ee9-b04b-deab16f03464.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/512-3d9d056b-df37-403c-aed0-20d1e505f095.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/512-3d9d056b-df37-403c-aed0-20d1e505f095.txn deleted file mode 100644 index 8b7fcccbb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/512-3d9d056b-df37-403c-aed0-20d1e505f095.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/513-d83a1b19-1e19-4316-b043-a74983f3be37.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/513-d83a1b19-1e19-4316-b043-a74983f3be37.txn deleted file mode 100644 index 6c823408f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/513-d83a1b19-1e19-4316-b043-a74983f3be37.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/514-08c4afc3-4e55-481e-9516-9163334a6b0d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/514-08c4afc3-4e55-481e-9516-9163334a6b0d.txn deleted file mode 100644 index 8c6396c7a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/514-08c4afc3-4e55-481e-9516-9163334a6b0d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/515-f702fbea-e3b1-45a1-bdb7-bd9de8c22596.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/515-f702fbea-e3b1-45a1-bdb7-bd9de8c22596.txn deleted file mode 100644 index 7a927e8e0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/515-f702fbea-e3b1-45a1-bdb7-bd9de8c22596.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/516-77f50333-c406-4463-acbd-aaaf9dc44cbe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/516-77f50333-c406-4463-acbd-aaaf9dc44cbe.txn deleted file mode 100644 index 8c95a937f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/516-77f50333-c406-4463-acbd-aaaf9dc44cbe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/517-936acc92-c995-41f3-a580-5466363195ce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/517-936acc92-c995-41f3-a580-5466363195ce.txn deleted file mode 100644 index 4d23aac97..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/517-936acc92-c995-41f3-a580-5466363195ce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/518-e33e5c01-d51b-4638-bbe2-1c6a5690b2a4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/518-e33e5c01-d51b-4638-bbe2-1c6a5690b2a4.txn deleted file mode 100644 index 0f7be8262..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/518-e33e5c01-d51b-4638-bbe2-1c6a5690b2a4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/519-6fd4e621-487b-4a6b-9b84-d7bb3643744a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/519-6fd4e621-487b-4a6b-9b84-d7bb3643744a.txn deleted file mode 100644 index 02bbcf29e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/519-6fd4e621-487b-4a6b-9b84-d7bb3643744a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/52-68c20541-0091-4135-bb64-e85c1c40235d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/52-68c20541-0091-4135-bb64-e85c1c40235d.txn deleted file mode 100644 index 35e5b9102..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/52-68c20541-0091-4135-bb64-e85c1c40235d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/520-d79bf125-cb01-4746-93c1-e3564e8d0d6b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/520-d79bf125-cb01-4746-93c1-e3564e8d0d6b.txn deleted file mode 100644 index 4a4094341..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/520-d79bf125-cb01-4746-93c1-e3564e8d0d6b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/521-6be039cd-1594-482d-a55d-5a0865cb0ba4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/521-6be039cd-1594-482d-a55d-5a0865cb0ba4.txn deleted file mode 100644 index 58a8d3fd3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/521-6be039cd-1594-482d-a55d-5a0865cb0ba4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/522-fd4789d0-bba6-48e7-81ea-ed6665b38ec1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/522-fd4789d0-bba6-48e7-81ea-ed6665b38ec1.txn deleted file mode 100644 index 7bfd580d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/522-fd4789d0-bba6-48e7-81ea-ed6665b38ec1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/523-e36e8a42-5107-4cec-a38d-4641ace13e87.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/523-e36e8a42-5107-4cec-a38d-4641ace13e87.txn deleted file mode 100644 index f81cc0766..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/523-e36e8a42-5107-4cec-a38d-4641ace13e87.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/524-c41aa123-803f-4247-ab88-d6137e0c4ef0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/524-c41aa123-803f-4247-ab88-d6137e0c4ef0.txn deleted file mode 100644 index 91c71b288..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/524-c41aa123-803f-4247-ab88-d6137e0c4ef0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/525-05535849-9d85-42ab-a4c7-1f8dd68e4829.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/525-05535849-9d85-42ab-a4c7-1f8dd68e4829.txn deleted file mode 100644 index e8eb9edaa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/525-05535849-9d85-42ab-a4c7-1f8dd68e4829.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/526-d111cfdc-3903-438e-bf24-f0eba24756a3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/526-d111cfdc-3903-438e-bf24-f0eba24756a3.txn deleted file mode 100644 index 071fe1481..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/526-d111cfdc-3903-438e-bf24-f0eba24756a3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/527-d587305c-51a5-4d8d-bf48-040e2e74c3d7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/527-d587305c-51a5-4d8d-bf48-040e2e74c3d7.txn deleted file mode 100644 index 1957f0363..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/527-d587305c-51a5-4d8d-bf48-040e2e74c3d7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/528-c2c0cf8a-c718-45e4-b435-2486b8d48307.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/528-c2c0cf8a-c718-45e4-b435-2486b8d48307.txn deleted file mode 100644 index 6bf60008d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/528-c2c0cf8a-c718-45e4-b435-2486b8d48307.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/529-dd19388d-d95b-46b9-82b3-e819e87fc7d6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/529-dd19388d-d95b-46b9-82b3-e819e87fc7d6.txn deleted file mode 100644 index d881f67d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/529-dd19388d-d95b-46b9-82b3-e819e87fc7d6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/53-993bab44-de8b-4ab7-a3e5-91f003120d8c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/53-993bab44-de8b-4ab7-a3e5-91f003120d8c.txn deleted file mode 100644 index 71b13f2e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/53-993bab44-de8b-4ab7-a3e5-91f003120d8c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/530-d56a687d-de71-4614-be6c-295955971324.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/530-d56a687d-de71-4614-be6c-295955971324.txn deleted file mode 100644 index bf82a9d65..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/530-d56a687d-de71-4614-be6c-295955971324.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/531-f35d86ec-f6af-42ac-b9e1-670eeec1fecb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/531-f35d86ec-f6af-42ac-b9e1-670eeec1fecb.txn deleted file mode 100644 index 844140cff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/531-f35d86ec-f6af-42ac-b9e1-670eeec1fecb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/532-c3ce58b1-997b-4308-9dbe-097d4dca606b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/532-c3ce58b1-997b-4308-9dbe-097d4dca606b.txn deleted file mode 100644 index 592c04dc2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/532-c3ce58b1-997b-4308-9dbe-097d4dca606b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/533-cd197794-7ec5-4ceb-a495-7d4a4aa124ee.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/533-cd197794-7ec5-4ceb-a495-7d4a4aa124ee.txn deleted file mode 100644 index 3acb12f36..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/533-cd197794-7ec5-4ceb-a495-7d4a4aa124ee.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/534-19a3efb0-8a51-4d6a-a2f7-18b3a27edd5a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/534-19a3efb0-8a51-4d6a-a2f7-18b3a27edd5a.txn deleted file mode 100644 index 5c726e585..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/534-19a3efb0-8a51-4d6a-a2f7-18b3a27edd5a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/535-7e3437fe-be9d-4be6-b903-826434b2cf2a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/535-7e3437fe-be9d-4be6-b903-826434b2cf2a.txn deleted file mode 100644 index 1063aae30..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/535-7e3437fe-be9d-4be6-b903-826434b2cf2a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/536-d04af373-8e80-4c50-a52a-4e32f366949d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/536-d04af373-8e80-4c50-a52a-4e32f366949d.txn deleted file mode 100644 index 41f076af3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/536-d04af373-8e80-4c50-a52a-4e32f366949d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/537-218445a4-c357-470b-8030-5d5f0bc71cf5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/537-218445a4-c357-470b-8030-5d5f0bc71cf5.txn deleted file mode 100644 index c3468c8a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/537-218445a4-c357-470b-8030-5d5f0bc71cf5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/538-d4a8a53f-638b-4087-9f83-ff96406f6bb9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/538-d4a8a53f-638b-4087-9f83-ff96406f6bb9.txn deleted file mode 100644 index e6a00db51..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/538-d4a8a53f-638b-4087-9f83-ff96406f6bb9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/539-87745821-4859-4a9a-ab9f-dcc39b4aa506.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/539-87745821-4859-4a9a-ab9f-dcc39b4aa506.txn deleted file mode 100644 index 7d507c1d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/539-87745821-4859-4a9a-ab9f-dcc39b4aa506.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/54-a7c98f21-d470-46a1-a16f-22f043bdae49.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/54-a7c98f21-d470-46a1-a16f-22f043bdae49.txn deleted file mode 100644 index c79029e21..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/54-a7c98f21-d470-46a1-a16f-22f043bdae49.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/540-26b2d5cc-2f2d-47ba-ac82-be4234f6763f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/540-26b2d5cc-2f2d-47ba-ac82-be4234f6763f.txn deleted file mode 100644 index c36101d24..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/540-26b2d5cc-2f2d-47ba-ac82-be4234f6763f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/541-55e513fe-8369-4c64-9f17-8ac8e91d51d6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/541-55e513fe-8369-4c64-9f17-8ac8e91d51d6.txn deleted file mode 100644 index e687c8b1b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/541-55e513fe-8369-4c64-9f17-8ac8e91d51d6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/542-0e339541-b564-4b4a-bf55-1f48c12e0f45.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/542-0e339541-b564-4b4a-bf55-1f48c12e0f45.txn deleted file mode 100644 index b411cce57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/542-0e339541-b564-4b4a-bf55-1f48c12e0f45.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/543-c193a7a2-b063-4d43-b2b2-62fc328e4d9d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/543-c193a7a2-b063-4d43-b2b2-62fc328e4d9d.txn deleted file mode 100644 index 7119c7717..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/543-c193a7a2-b063-4d43-b2b2-62fc328e4d9d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/544-d9bf409a-2122-4e6f-9047-0e96284b64f1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/544-d9bf409a-2122-4e6f-9047-0e96284b64f1.txn deleted file mode 100644 index 933344008..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/544-d9bf409a-2122-4e6f-9047-0e96284b64f1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/545-f9ace105-8c61-4a66-87d3-386a4edec87e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/545-f9ace105-8c61-4a66-87d3-386a4edec87e.txn deleted file mode 100644 index 28878379e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/545-f9ace105-8c61-4a66-87d3-386a4edec87e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/546-3dac396a-8675-47ca-b761-f43f159ba5ef.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/546-3dac396a-8675-47ca-b761-f43f159ba5ef.txn deleted file mode 100644 index defd79671..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/546-3dac396a-8675-47ca-b761-f43f159ba5ef.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/547-a65dbf16-3ee5-414f-9869-51647c304c26.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/547-a65dbf16-3ee5-414f-9869-51647c304c26.txn deleted file mode 100644 index f972752a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/547-a65dbf16-3ee5-414f-9869-51647c304c26.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/548-7b90fd64-2941-4cd5-aeac-5bc115957970.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/548-7b90fd64-2941-4cd5-aeac-5bc115957970.txn deleted file mode 100644 index 7ae54f23d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/548-7b90fd64-2941-4cd5-aeac-5bc115957970.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/549-7a2893b4-7978-4e96-8be1-e45124386b0d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/549-7a2893b4-7978-4e96-8be1-e45124386b0d.txn deleted file mode 100644 index cba416bf1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/549-7a2893b4-7978-4e96-8be1-e45124386b0d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/55-e25f162f-2a43-419c-8d33-af04dd573b6b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/55-e25f162f-2a43-419c-8d33-af04dd573b6b.txn deleted file mode 100644 index 368b6a357..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/55-e25f162f-2a43-419c-8d33-af04dd573b6b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/550-0f9830f3-4736-4b2d-bfe1-2d7041db020a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/550-0f9830f3-4736-4b2d-bfe1-2d7041db020a.txn deleted file mode 100644 index 0a602e77f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/550-0f9830f3-4736-4b2d-bfe1-2d7041db020a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/551-348e11be-675e-490a-b491-dac80e4ca73e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/551-348e11be-675e-490a-b491-dac80e4ca73e.txn deleted file mode 100644 index fea0a10a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/551-348e11be-675e-490a-b491-dac80e4ca73e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/552-decb8773-58d4-4788-bc07-f5735086e270.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/552-decb8773-58d4-4788-bc07-f5735086e270.txn deleted file mode 100644 index ff8f819cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/552-decb8773-58d4-4788-bc07-f5735086e270.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/553-c374c5d3-e34a-425c-b92a-f0b65e9947c3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/553-c374c5d3-e34a-425c-b92a-f0b65e9947c3.txn deleted file mode 100644 index bc85d0084..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/553-c374c5d3-e34a-425c-b92a-f0b65e9947c3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/554-7e189609-9546-4e52-9bb7-63ae1208e245.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/554-7e189609-9546-4e52-9bb7-63ae1208e245.txn deleted file mode 100644 index f3ba3dfba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/554-7e189609-9546-4e52-9bb7-63ae1208e245.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/555-bbcf0fe4-2626-49be-b976-3c906db4dc4a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/555-bbcf0fe4-2626-49be-b976-3c906db4dc4a.txn deleted file mode 100644 index 9b33f9343..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/555-bbcf0fe4-2626-49be-b976-3c906db4dc4a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/556-3de3ed17-71eb-4867-8545-77c6402cc84f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/556-3de3ed17-71eb-4867-8545-77c6402cc84f.txn deleted file mode 100644 index a16fdf8c1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/556-3de3ed17-71eb-4867-8545-77c6402cc84f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/557-f8e63914-ad68-4505-befd-06ebe8225161.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/557-f8e63914-ad68-4505-befd-06ebe8225161.txn deleted file mode 100644 index c7182bf7e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/557-f8e63914-ad68-4505-befd-06ebe8225161.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/558-3fbdee85-2a3a-4628-8c8e-03a98d26fbe8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/558-3fbdee85-2a3a-4628-8c8e-03a98d26fbe8.txn deleted file mode 100644 index 5fde97302..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/558-3fbdee85-2a3a-4628-8c8e-03a98d26fbe8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/559-46680794-3cf2-4d23-bb55-3a282311705e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/559-46680794-3cf2-4d23-bb55-3a282311705e.txn deleted file mode 100644 index e1d77286b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/559-46680794-3cf2-4d23-bb55-3a282311705e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/56-379583da-e1ac-4ddc-ad83-e1dff3f7c7e0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/56-379583da-e1ac-4ddc-ad83-e1dff3f7c7e0.txn deleted file mode 100644 index df4ad3ec7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/56-379583da-e1ac-4ddc-ad83-e1dff3f7c7e0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/560-7b59307b-cefa-438c-9b60-3f3dc9ccec92.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/560-7b59307b-cefa-438c-9b60-3f3dc9ccec92.txn deleted file mode 100644 index 0d68ac6dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/560-7b59307b-cefa-438c-9b60-3f3dc9ccec92.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/561-d65e071c-054c-4c02-ae6b-4a155dc8a0ac.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/561-d65e071c-054c-4c02-ae6b-4a155dc8a0ac.txn deleted file mode 100644 index b6ac394a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/561-d65e071c-054c-4c02-ae6b-4a155dc8a0ac.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/562-c4ec26ea-043d-45b5-8fb7-e68d4a09910a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/562-c4ec26ea-043d-45b5-8fb7-e68d4a09910a.txn deleted file mode 100644 index cef60f66b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/562-c4ec26ea-043d-45b5-8fb7-e68d4a09910a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/563-03507af8-d8b4-493e-a28a-ca661e9ac4c1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/563-03507af8-d8b4-493e-a28a-ca661e9ac4c1.txn deleted file mode 100644 index ddf8376a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/563-03507af8-d8b4-493e-a28a-ca661e9ac4c1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/564-6f50ae42-6875-4b3a-9841-c970a3827d64.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/564-6f50ae42-6875-4b3a-9841-c970a3827d64.txn deleted file mode 100644 index b0e6416ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/564-6f50ae42-6875-4b3a-9841-c970a3827d64.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/565-86515ecb-e38c-4e4b-9f67-7c64f622282c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/565-86515ecb-e38c-4e4b-9f67-7c64f622282c.txn deleted file mode 100644 index 3c2cac530..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/565-86515ecb-e38c-4e4b-9f67-7c64f622282c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/566-85497fb9-7bdf-446a-84c5-d3704528409e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/566-85497fb9-7bdf-446a-84c5-d3704528409e.txn deleted file mode 100644 index 4deb55c66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/566-85497fb9-7bdf-446a-84c5-d3704528409e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/567-243a0a2e-9fa5-4869-b219-a471001bca37.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/567-243a0a2e-9fa5-4869-b219-a471001bca37.txn deleted file mode 100644 index 902b1fcfe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/567-243a0a2e-9fa5-4869-b219-a471001bca37.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/568-994a74e6-04c9-4913-99f4-3f006029df49.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/568-994a74e6-04c9-4913-99f4-3f006029df49.txn deleted file mode 100644 index 88f671235..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/568-994a74e6-04c9-4913-99f4-3f006029df49.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/569-59e6116a-dd9a-49a5-929d-688a9d167207.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/569-59e6116a-dd9a-49a5-929d-688a9d167207.txn deleted file mode 100644 index 9c79ad02b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/569-59e6116a-dd9a-49a5-929d-688a9d167207.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/57-adedfa74-6384-482b-bbc3-810b6f3c9781.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/57-adedfa74-6384-482b-bbc3-810b6f3c9781.txn deleted file mode 100644 index 6ed41a364..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/57-adedfa74-6384-482b-bbc3-810b6f3c9781.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/570-f99198c3-319f-4959-81e1-0be73bc19930.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/570-f99198c3-319f-4959-81e1-0be73bc19930.txn deleted file mode 100644 index c398e6ed8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/570-f99198c3-319f-4959-81e1-0be73bc19930.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/571-636ff982-fd9d-47ce-b31c-3bd0e075fee7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/571-636ff982-fd9d-47ce-b31c-3bd0e075fee7.txn deleted file mode 100644 index 7a4eb764d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/571-636ff982-fd9d-47ce-b31c-3bd0e075fee7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/572-19a57469-43d8-4db1-a050-a233ab6cb48b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/572-19a57469-43d8-4db1-a050-a233ab6cb48b.txn deleted file mode 100644 index 907026d5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/572-19a57469-43d8-4db1-a050-a233ab6cb48b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/573-30210eb5-ba5f-4f0d-9b38-8ba10a50a903.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/573-30210eb5-ba5f-4f0d-9b38-8ba10a50a903.txn deleted file mode 100644 index b83174a36..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/573-30210eb5-ba5f-4f0d-9b38-8ba10a50a903.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/574-5fa39964-c5d2-416e-940c-afddbb9a913b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/574-5fa39964-c5d2-416e-940c-afddbb9a913b.txn deleted file mode 100644 index 631c97eae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/574-5fa39964-c5d2-416e-940c-afddbb9a913b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/575-5205de35-5caa-4c02-a499-5075b1548844.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/575-5205de35-5caa-4c02-a499-5075b1548844.txn deleted file mode 100644 index 2f6558964..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/575-5205de35-5caa-4c02-a499-5075b1548844.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/576-ddf9201c-6d21-443e-8363-fa91401d7b48.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/576-ddf9201c-6d21-443e-8363-fa91401d7b48.txn deleted file mode 100644 index 384998622..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/576-ddf9201c-6d21-443e-8363-fa91401d7b48.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/577-1481f84f-318c-4ff2-8bb8-2fd9b2406f56.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/577-1481f84f-318c-4ff2-8bb8-2fd9b2406f56.txn deleted file mode 100644 index 6785877d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/577-1481f84f-318c-4ff2-8bb8-2fd9b2406f56.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/578-e9d0ed00-d166-4362-9bf7-de455715ef58.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/578-e9d0ed00-d166-4362-9bf7-de455715ef58.txn deleted file mode 100644 index 6c1b1d1e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/578-e9d0ed00-d166-4362-9bf7-de455715ef58.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/579-c596b483-b599-47af-bd08-68b6da05fc19.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/579-c596b483-b599-47af-bd08-68b6da05fc19.txn deleted file mode 100644 index 8e6627b5f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/579-c596b483-b599-47af-bd08-68b6da05fc19.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/58-f16c84cc-d75d-4a53-b3c3-2bfe71da39b2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/58-f16c84cc-d75d-4a53-b3c3-2bfe71da39b2.txn deleted file mode 100644 index 92ef0e603..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/58-f16c84cc-d75d-4a53-b3c3-2bfe71da39b2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/580-353aaedf-982e-4a16-874a-e88a0fdf16fb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/580-353aaedf-982e-4a16-874a-e88a0fdf16fb.txn deleted file mode 100644 index e0ec4913e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/580-353aaedf-982e-4a16-874a-e88a0fdf16fb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/581-c36ca97b-7104-4477-a72b-cb11f26d8163.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/581-c36ca97b-7104-4477-a72b-cb11f26d8163.txn deleted file mode 100644 index 11f3ef309..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/581-c36ca97b-7104-4477-a72b-cb11f26d8163.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/582-3e754e5a-f571-43e2-bdf7-cd9dd117499d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/582-3e754e5a-f571-43e2-bdf7-cd9dd117499d.txn deleted file mode 100644 index 544ba304d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/582-3e754e5a-f571-43e2-bdf7-cd9dd117499d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/583-e7ab512e-18d2-4640-9650-63ad350daada.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/583-e7ab512e-18d2-4640-9650-63ad350daada.txn deleted file mode 100644 index 3a3ac1d56..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/583-e7ab512e-18d2-4640-9650-63ad350daada.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/584-390d7425-c99f-4c6e-9994-de4bec2a35a8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/584-390d7425-c99f-4c6e-9994-de4bec2a35a8.txn deleted file mode 100644 index 2f7595509..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/584-390d7425-c99f-4c6e-9994-de4bec2a35a8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/585-17523eeb-1b61-4a50-ac78-a770b2a02bce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/585-17523eeb-1b61-4a50-ac78-a770b2a02bce.txn deleted file mode 100644 index 1505f3013..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/585-17523eeb-1b61-4a50-ac78-a770b2a02bce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/586-c23f1226-2a1f-4342-b074-17e0ca6bc591.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/586-c23f1226-2a1f-4342-b074-17e0ca6bc591.txn deleted file mode 100644 index 6c69b89b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/586-c23f1226-2a1f-4342-b074-17e0ca6bc591.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/587-d50d3b0f-0803-4cfd-8abb-990c2e78f111.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/587-d50d3b0f-0803-4cfd-8abb-990c2e78f111.txn deleted file mode 100644 index e1d422f0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/587-d50d3b0f-0803-4cfd-8abb-990c2e78f111.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/588-2ff407e1-babc-4d62-83f2-4e062f05b541.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/588-2ff407e1-babc-4d62-83f2-4e062f05b541.txn deleted file mode 100644 index ab6800998..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/588-2ff407e1-babc-4d62-83f2-4e062f05b541.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/589-6ad061f3-3038-41ab-ae23-db172296e8e5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/589-6ad061f3-3038-41ab-ae23-db172296e8e5.txn deleted file mode 100644 index a1ea7eb3b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/589-6ad061f3-3038-41ab-ae23-db172296e8e5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/59-e5495ea6-2123-439e-a316-39eade345cad.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/59-e5495ea6-2123-439e-a316-39eade345cad.txn deleted file mode 100644 index 3daa92e9c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/59-e5495ea6-2123-439e-a316-39eade345cad.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/590-1c268376-a875-4e2e-9134-2a46f4ef0c24.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/590-1c268376-a875-4e2e-9134-2a46f4ef0c24.txn deleted file mode 100644 index af30d592c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/590-1c268376-a875-4e2e-9134-2a46f4ef0c24.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/591-18308888-93ad-4f04-b502-111dd12ea1f9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/591-18308888-93ad-4f04-b502-111dd12ea1f9.txn deleted file mode 100644 index 6543e6e8d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/591-18308888-93ad-4f04-b502-111dd12ea1f9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/592-c4716a88-f54e-4eb9-a3af-1444eb17ed5f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/592-c4716a88-f54e-4eb9-a3af-1444eb17ed5f.txn deleted file mode 100644 index 7313b027a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/592-c4716a88-f54e-4eb9-a3af-1444eb17ed5f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/593-ac7a9571-894a-44c0-b922-c6195cd26ba9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/593-ac7a9571-894a-44c0-b922-c6195cd26ba9.txn deleted file mode 100644 index 185d71a11..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/593-ac7a9571-894a-44c0-b922-c6195cd26ba9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/594-03af9f5b-4aff-4205-ba09-24fff1feabe1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/594-03af9f5b-4aff-4205-ba09-24fff1feabe1.txn deleted file mode 100644 index 247a29db9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/594-03af9f5b-4aff-4205-ba09-24fff1feabe1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/595-acf0e01b-8ae6-4008-9dbc-df3f347727f1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/595-acf0e01b-8ae6-4008-9dbc-df3f347727f1.txn deleted file mode 100644 index 32f6635b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/595-acf0e01b-8ae6-4008-9dbc-df3f347727f1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/596-ac59a3b6-f864-4a52-9ef2-542578a76cf8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/596-ac59a3b6-f864-4a52-9ef2-542578a76cf8.txn deleted file mode 100644 index 427679edb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/596-ac59a3b6-f864-4a52-9ef2-542578a76cf8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/597-8aa12125-0069-4288-9acb-ad04a0217b07.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/597-8aa12125-0069-4288-9acb-ad04a0217b07.txn deleted file mode 100644 index 3174ed68f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/597-8aa12125-0069-4288-9acb-ad04a0217b07.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/598-3277227a-9f71-4c2e-a7a3-69151c657cb9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/598-3277227a-9f71-4c2e-a7a3-69151c657cb9.txn deleted file mode 100644 index 7203c3bf8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/598-3277227a-9f71-4c2e-a7a3-69151c657cb9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/599-ba34d85c-8699-41a9-8bf8-17630f856856.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/599-ba34d85c-8699-41a9-8bf8-17630f856856.txn deleted file mode 100644 index 73b2fabae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/599-ba34d85c-8699-41a9-8bf8-17630f856856.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/6-fa4a50f9-d028-43bf-9880-48884a31d5d0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/6-fa4a50f9-d028-43bf-9880-48884a31d5d0.txn deleted file mode 100644 index 07b6e908a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/6-fa4a50f9-d028-43bf-9880-48884a31d5d0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/60-f9a46689-3aec-4a94-b856-27601c5508b5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/60-f9a46689-3aec-4a94-b856-27601c5508b5.txn deleted file mode 100644 index f7435f3cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/60-f9a46689-3aec-4a94-b856-27601c5508b5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/600-b54e7bec-7c00-446e-9d51-ae2cb06c209a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/600-b54e7bec-7c00-446e-9d51-ae2cb06c209a.txn deleted file mode 100644 index 52259d95f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/600-b54e7bec-7c00-446e-9d51-ae2cb06c209a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/601-bd61a66a-13ca-4056-850f-e966c555a055.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/601-bd61a66a-13ca-4056-850f-e966c555a055.txn deleted file mode 100644 index 86f0244ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/601-bd61a66a-13ca-4056-850f-e966c555a055.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/602-eeaea992-870e-40b2-8ace-619a4fbf5b37.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/602-eeaea992-870e-40b2-8ace-619a4fbf5b37.txn deleted file mode 100644 index fd60ce5a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/602-eeaea992-870e-40b2-8ace-619a4fbf5b37.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/603-1729e75c-1c50-4fa5-9382-f6efccbe492a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/603-1729e75c-1c50-4fa5-9382-f6efccbe492a.txn deleted file mode 100644 index a4671ee89..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/603-1729e75c-1c50-4fa5-9382-f6efccbe492a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/604-85ec731d-ed74-4597-ac79-bb7d3a3b87c4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/604-85ec731d-ed74-4597-ac79-bb7d3a3b87c4.txn deleted file mode 100644 index 7e7426d8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/604-85ec731d-ed74-4597-ac79-bb7d3a3b87c4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/605-23ef3cca-2208-4328-8110-a0ef0e9bfb17.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/605-23ef3cca-2208-4328-8110-a0ef0e9bfb17.txn deleted file mode 100644 index 03449bbbf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/605-23ef3cca-2208-4328-8110-a0ef0e9bfb17.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/606-11b58b75-1b3e-443b-a1a5-618d99c03773.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/606-11b58b75-1b3e-443b-a1a5-618d99c03773.txn deleted file mode 100644 index 632d023db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/606-11b58b75-1b3e-443b-a1a5-618d99c03773.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/607-3bc00d93-17ab-46b7-bcbd-4005e7ca7432.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/607-3bc00d93-17ab-46b7-bcbd-4005e7ca7432.txn deleted file mode 100644 index d500b79e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/607-3bc00d93-17ab-46b7-bcbd-4005e7ca7432.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/608-1d3ed668-51f0-4b17-a708-636b7aa7a9a6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/608-1d3ed668-51f0-4b17-a708-636b7aa7a9a6.txn deleted file mode 100644 index ceac32088..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/608-1d3ed668-51f0-4b17-a708-636b7aa7a9a6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/609-574d994a-ad80-417a-9eae-09ca6db7c084.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/609-574d994a-ad80-417a-9eae-09ca6db7c084.txn deleted file mode 100644 index 76e096f7e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/609-574d994a-ad80-417a-9eae-09ca6db7c084.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/61-79921ab7-0a6c-4618-aaf9-692af71c3f3f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/61-79921ab7-0a6c-4618-aaf9-692af71c3f3f.txn deleted file mode 100644 index de9d784ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/61-79921ab7-0a6c-4618-aaf9-692af71c3f3f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/610-f8cea471-6d00-406b-906e-ab6bb36ca709.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/610-f8cea471-6d00-406b-906e-ab6bb36ca709.txn deleted file mode 100644 index 91faae6a6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/610-f8cea471-6d00-406b-906e-ab6bb36ca709.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/611-56a50505-7490-4222-af78-b29c07b2c680.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/611-56a50505-7490-4222-af78-b29c07b2c680.txn deleted file mode 100644 index c9bde3b3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/611-56a50505-7490-4222-af78-b29c07b2c680.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/612-71c41ebc-21e6-4ef4-95f6-b3209834e2b7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/612-71c41ebc-21e6-4ef4-95f6-b3209834e2b7.txn deleted file mode 100644 index 5321d71e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/612-71c41ebc-21e6-4ef4-95f6-b3209834e2b7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/613-e6dda8ed-bc17-46b8-85d6-847946486e7a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/613-e6dda8ed-bc17-46b8-85d6-847946486e7a.txn deleted file mode 100644 index cd7b29c8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/613-e6dda8ed-bc17-46b8-85d6-847946486e7a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/614-3bed9c24-f387-45b1-8a6f-b717506052e7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/614-3bed9c24-f387-45b1-8a6f-b717506052e7.txn deleted file mode 100644 index 111eeec65..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/614-3bed9c24-f387-45b1-8a6f-b717506052e7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/615-147c0fc8-07f0-4374-8d70-c5ad46ae93e2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/615-147c0fc8-07f0-4374-8d70-c5ad46ae93e2.txn deleted file mode 100644 index 90c0ce1cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/615-147c0fc8-07f0-4374-8d70-c5ad46ae93e2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/616-fe6789c1-6ec1-4718-8161-8c797354c252.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/616-fe6789c1-6ec1-4718-8161-8c797354c252.txn deleted file mode 100644 index aa09c1e95..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/616-fe6789c1-6ec1-4718-8161-8c797354c252.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/617-25907aae-7734-46b3-ab4b-2682a5c0242f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/617-25907aae-7734-46b3-ab4b-2682a5c0242f.txn deleted file mode 100644 index 67f954e31..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/617-25907aae-7734-46b3-ab4b-2682a5c0242f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/618-b823ce6c-613d-4d0a-a693-7d53c5b9b979.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/618-b823ce6c-613d-4d0a-a693-7d53c5b9b979.txn deleted file mode 100644 index d09f169fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/618-b823ce6c-613d-4d0a-a693-7d53c5b9b979.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/619-ff2eb54c-fd0e-4492-9c3a-de151b571d8a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/619-ff2eb54c-fd0e-4492-9c3a-de151b571d8a.txn deleted file mode 100644 index 4a5d8d227..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/619-ff2eb54c-fd0e-4492-9c3a-de151b571d8a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/62-bd28b81e-c9e9-4a7b-9c3e-96468b491425.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/62-bd28b81e-c9e9-4a7b-9c3e-96468b491425.txn deleted file mode 100644 index 61f9f7f8b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/62-bd28b81e-c9e9-4a7b-9c3e-96468b491425.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/620-f82e361b-626d-44e8-b58c-e12dea4d3572.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/620-f82e361b-626d-44e8-b58c-e12dea4d3572.txn deleted file mode 100644 index 0b2ded5d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/620-f82e361b-626d-44e8-b58c-e12dea4d3572.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/621-c30b9c22-79b2-45d2-a9da-ee52e2676b6a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/621-c30b9c22-79b2-45d2-a9da-ee52e2676b6a.txn deleted file mode 100644 index 47da3b38e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/621-c30b9c22-79b2-45d2-a9da-ee52e2676b6a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/622-0f00cf5c-2c54-402d-adfe-07ef0569f1c7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/622-0f00cf5c-2c54-402d-adfe-07ef0569f1c7.txn deleted file mode 100644 index 875c47b39..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/622-0f00cf5c-2c54-402d-adfe-07ef0569f1c7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/623-4b923fac-49fd-42ee-8011-80975266d30c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/623-4b923fac-49fd-42ee-8011-80975266d30c.txn deleted file mode 100644 index f0eac6042..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/623-4b923fac-49fd-42ee-8011-80975266d30c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/624-b53681e6-b082-495d-a532-eb08c43d7b64.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/624-b53681e6-b082-495d-a532-eb08c43d7b64.txn deleted file mode 100644 index e20c293ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/624-b53681e6-b082-495d-a532-eb08c43d7b64.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/625-28736081-24c7-4076-93e6-715d086e4408.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/625-28736081-24c7-4076-93e6-715d086e4408.txn deleted file mode 100644 index 23f9d5f1f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/625-28736081-24c7-4076-93e6-715d086e4408.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/626-f6d5b2b6-0a7e-4fa4-8882-5bc0d6b60f09.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/626-f6d5b2b6-0a7e-4fa4-8882-5bc0d6b60f09.txn deleted file mode 100644 index 28033f40e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/626-f6d5b2b6-0a7e-4fa4-8882-5bc0d6b60f09.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/627-ca9293bf-7596-4ade-abf7-603ce3330c8a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/627-ca9293bf-7596-4ade-abf7-603ce3330c8a.txn deleted file mode 100644 index ac1ad3207..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/627-ca9293bf-7596-4ade-abf7-603ce3330c8a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/628-d29af08d-0868-4c9a-999b-5b2da856bcb9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/628-d29af08d-0868-4c9a-999b-5b2da856bcb9.txn deleted file mode 100644 index 0f784cddb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/628-d29af08d-0868-4c9a-999b-5b2da856bcb9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/629-7929ac43-6891-4b19-8fb9-6b17e852e71e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/629-7929ac43-6891-4b19-8fb9-6b17e852e71e.txn deleted file mode 100644 index d13a808d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/629-7929ac43-6891-4b19-8fb9-6b17e852e71e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/63-75a90e1c-9d8e-4e2f-8cfb-b4efd6cbc9af.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/63-75a90e1c-9d8e-4e2f-8cfb-b4efd6cbc9af.txn deleted file mode 100644 index 4784c66a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/63-75a90e1c-9d8e-4e2f-8cfb-b4efd6cbc9af.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/630-04096e22-de37-4fa4-ad2f-6382614b0460.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/630-04096e22-de37-4fa4-ad2f-6382614b0460.txn deleted file mode 100644 index e7cd4d134..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/630-04096e22-de37-4fa4-ad2f-6382614b0460.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/631-0d5c4907-9e13-48f9-8beb-9d4a4268d864.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/631-0d5c4907-9e13-48f9-8beb-9d4a4268d864.txn deleted file mode 100644 index b9d311fe6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/631-0d5c4907-9e13-48f9-8beb-9d4a4268d864.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/632-cc26e335-2cdd-4593-a018-8fd088828003.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/632-cc26e335-2cdd-4593-a018-8fd088828003.txn deleted file mode 100644 index e1cfa5a9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/632-cc26e335-2cdd-4593-a018-8fd088828003.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/633-e76073a7-703b-4dbc-ad61-89eaad68975a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/633-e76073a7-703b-4dbc-ad61-89eaad68975a.txn deleted file mode 100644 index e32a1213a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/633-e76073a7-703b-4dbc-ad61-89eaad68975a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/634-f94a9289-43c1-426b-927e-baddf36e51f3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/634-f94a9289-43c1-426b-927e-baddf36e51f3.txn deleted file mode 100644 index 31ad39ab9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/634-f94a9289-43c1-426b-927e-baddf36e51f3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/635-7a82ea81-e0aa-4ac2-beab-2f9bd9711089.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/635-7a82ea81-e0aa-4ac2-beab-2f9bd9711089.txn deleted file mode 100644 index 8ddeaca9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/635-7a82ea81-e0aa-4ac2-beab-2f9bd9711089.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/636-6af55621-753e-42a4-b9ac-e763a32655ed.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/636-6af55621-753e-42a4-b9ac-e763a32655ed.txn deleted file mode 100644 index 79e8642d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/636-6af55621-753e-42a4-b9ac-e763a32655ed.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/637-9b1c38d3-9a66-4065-8da4-56338526ba7c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/637-9b1c38d3-9a66-4065-8da4-56338526ba7c.txn deleted file mode 100644 index be42d7664..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/637-9b1c38d3-9a66-4065-8da4-56338526ba7c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/638-755abbbd-9ab9-43b4-888a-a8f9633031e6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/638-755abbbd-9ab9-43b4-888a-a8f9633031e6.txn deleted file mode 100644 index fd3cce7fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/638-755abbbd-9ab9-43b4-888a-a8f9633031e6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/639-91732298-d51c-4e0c-a62c-92ff7bca9575.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/639-91732298-d51c-4e0c-a62c-92ff7bca9575.txn deleted file mode 100644 index 132eec8ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/639-91732298-d51c-4e0c-a62c-92ff7bca9575.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/64-181893f5-055f-4436-bd2c-78f454ef20b9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/64-181893f5-055f-4436-bd2c-78f454ef20b9.txn deleted file mode 100644 index 65731f43d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/64-181893f5-055f-4436-bd2c-78f454ef20b9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/640-a7316a77-66f5-4d4e-a725-f16e3d6c1644.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/640-a7316a77-66f5-4d4e-a725-f16e3d6c1644.txn deleted file mode 100644 index c4c81dec4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/640-a7316a77-66f5-4d4e-a725-f16e3d6c1644.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/641-91932ada-42a4-4085-82a5-424482e033f5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/641-91932ada-42a4-4085-82a5-424482e033f5.txn deleted file mode 100644 index b447913ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/641-91932ada-42a4-4085-82a5-424482e033f5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/642-65166749-8d2a-43b2-8d72-cd14f02bdaff.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/642-65166749-8d2a-43b2-8d72-cd14f02bdaff.txn deleted file mode 100644 index cd4785438..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/642-65166749-8d2a-43b2-8d72-cd14f02bdaff.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/643-184a5167-5e0f-4cc2-9d34-c8011e119bbd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/643-184a5167-5e0f-4cc2-9d34-c8011e119bbd.txn deleted file mode 100644 index d5acf5cbc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/643-184a5167-5e0f-4cc2-9d34-c8011e119bbd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/644-ff2ef246-95b7-489e-9b87-b784e130edd6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/644-ff2ef246-95b7-489e-9b87-b784e130edd6.txn deleted file mode 100644 index db7a2e109..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/644-ff2ef246-95b7-489e-9b87-b784e130edd6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/645-6ef14e2e-097f-47ce-85cc-65ee65532c53.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/645-6ef14e2e-097f-47ce-85cc-65ee65532c53.txn deleted file mode 100644 index 0d7da8e35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/645-6ef14e2e-097f-47ce-85cc-65ee65532c53.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/646-27034a50-b737-4358-b22c-d29a7c647b4c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/646-27034a50-b737-4358-b22c-d29a7c647b4c.txn deleted file mode 100644 index 5240e076c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/646-27034a50-b737-4358-b22c-d29a7c647b4c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/647-348dd783-cad2-480e-8ec2-aff679a5fee5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/647-348dd783-cad2-480e-8ec2-aff679a5fee5.txn deleted file mode 100644 index aed34249f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/647-348dd783-cad2-480e-8ec2-aff679a5fee5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/648-e05b898f-314f-4798-9d3a-f7d970bce7ca.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/648-e05b898f-314f-4798-9d3a-f7d970bce7ca.txn deleted file mode 100644 index d59e74e45..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/648-e05b898f-314f-4798-9d3a-f7d970bce7ca.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/649-3ce78367-0522-4904-81cb-b0ceed20026a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/649-3ce78367-0522-4904-81cb-b0ceed20026a.txn deleted file mode 100644 index 0a67e01bf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/649-3ce78367-0522-4904-81cb-b0ceed20026a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/65-33bf0514-1e7a-417b-a7f4-5d7f3e34425d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/65-33bf0514-1e7a-417b-a7f4-5d7f3e34425d.txn deleted file mode 100644 index ef9109c47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/65-33bf0514-1e7a-417b-a7f4-5d7f3e34425d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/650-8173babe-bda8-43d1-a602-ab0e3dddee2d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/650-8173babe-bda8-43d1-a602-ab0e3dddee2d.txn deleted file mode 100644 index 1358ec796..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/650-8173babe-bda8-43d1-a602-ab0e3dddee2d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/651-65640d51-aef2-4e85-b0fb-3b5cec6e8573.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/651-65640d51-aef2-4e85-b0fb-3b5cec6e8573.txn deleted file mode 100644 index 3bfbd3084..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/651-65640d51-aef2-4e85-b0fb-3b5cec6e8573.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/652-314f1692-4673-444a-ba4a-88f3c9ba109d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/652-314f1692-4673-444a-ba4a-88f3c9ba109d.txn deleted file mode 100644 index 4fa3dc1eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/652-314f1692-4673-444a-ba4a-88f3c9ba109d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/653-4949d30b-9f61-4a8c-a0b4-8bc99945b395.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/653-4949d30b-9f61-4a8c-a0b4-8bc99945b395.txn deleted file mode 100644 index df7f59331..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/653-4949d30b-9f61-4a8c-a0b4-8bc99945b395.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/654-5399e1f5-7169-468b-8db4-465e9a0546fa.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/654-5399e1f5-7169-468b-8db4-465e9a0546fa.txn deleted file mode 100644 index de80b5bf7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/654-5399e1f5-7169-468b-8db4-465e9a0546fa.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/655-43fc7eaa-17ef-44be-b93c-dbf13e1d6564.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/655-43fc7eaa-17ef-44be-b93c-dbf13e1d6564.txn deleted file mode 100644 index 9509b37fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/655-43fc7eaa-17ef-44be-b93c-dbf13e1d6564.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/656-f9a30210-d345-4e13-8a44-e9adab076845.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/656-f9a30210-d345-4e13-8a44-e9adab076845.txn deleted file mode 100644 index da119ceb1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/656-f9a30210-d345-4e13-8a44-e9adab076845.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/657-a086eb0e-056d-4be1-b144-3bbb674173f6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/657-a086eb0e-056d-4be1-b144-3bbb674173f6.txn deleted file mode 100644 index cbf5aba49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/657-a086eb0e-056d-4be1-b144-3bbb674173f6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/658-6639f734-072a-464f-97a5-f18215950af1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/658-6639f734-072a-464f-97a5-f18215950af1.txn deleted file mode 100644 index bc8c4f0db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/658-6639f734-072a-464f-97a5-f18215950af1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/659-cf689c67-81b4-4c6d-873e-aa423e172a96.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/659-cf689c67-81b4-4c6d-873e-aa423e172a96.txn deleted file mode 100644 index 4f21c9e76..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/659-cf689c67-81b4-4c6d-873e-aa423e172a96.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/66-e13edc7a-de7a-41b6-ad6f-c776df41845c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/66-e13edc7a-de7a-41b6-ad6f-c776df41845c.txn deleted file mode 100644 index 96d13bdaf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/66-e13edc7a-de7a-41b6-ad6f-c776df41845c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/660-b00264af-8092-4ed7-a397-85378323f54f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/660-b00264af-8092-4ed7-a397-85378323f54f.txn deleted file mode 100644 index 1937d3390..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/660-b00264af-8092-4ed7-a397-85378323f54f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/661-5fc295d2-3662-4068-9113-6d0d39590d84.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/661-5fc295d2-3662-4068-9113-6d0d39590d84.txn deleted file mode 100644 index 4ad454dee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/661-5fc295d2-3662-4068-9113-6d0d39590d84.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/662-b9ed4301-9564-45e4-97e3-f1485aed0484.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/662-b9ed4301-9564-45e4-97e3-f1485aed0484.txn deleted file mode 100644 index 136288123..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/662-b9ed4301-9564-45e4-97e3-f1485aed0484.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/663-d8ad43d1-c942-4990-904f-784978b2cced.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/663-d8ad43d1-c942-4990-904f-784978b2cced.txn deleted file mode 100644 index 001ebd99e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/663-d8ad43d1-c942-4990-904f-784978b2cced.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/664-37889063-36b7-4a70-adab-10e7ddb278ad.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/664-37889063-36b7-4a70-adab-10e7ddb278ad.txn deleted file mode 100644 index fdfb1b50f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/664-37889063-36b7-4a70-adab-10e7ddb278ad.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/665-f01746c1-c6e0-4f5c-93f4-8294f2fa3fa6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/665-f01746c1-c6e0-4f5c-93f4-8294f2fa3fa6.txn deleted file mode 100644 index 92f5d9546..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/665-f01746c1-c6e0-4f5c-93f4-8294f2fa3fa6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/666-84b9fe57-0746-4fd2-bd8c-c739bbc797e6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/666-84b9fe57-0746-4fd2-bd8c-c739bbc797e6.txn deleted file mode 100644 index f8085b855..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/666-84b9fe57-0746-4fd2-bd8c-c739bbc797e6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/667-ae765bd5-7672-4d82-a6d1-0a26167bcfb3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/667-ae765bd5-7672-4d82-a6d1-0a26167bcfb3.txn deleted file mode 100644 index 034e731d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/667-ae765bd5-7672-4d82-a6d1-0a26167bcfb3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/668-08b73cb8-564c-44dd-8800-d395ec77fef2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/668-08b73cb8-564c-44dd-8800-d395ec77fef2.txn deleted file mode 100644 index a0fc43efd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/668-08b73cb8-564c-44dd-8800-d395ec77fef2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/669-3269ebef-4ef5-46b8-bc53-29e0a059865d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/669-3269ebef-4ef5-46b8-bc53-29e0a059865d.txn deleted file mode 100644 index 79af9364e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/669-3269ebef-4ef5-46b8-bc53-29e0a059865d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/67-456d5fb8-2928-495b-bcf7-8f9e32167449.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/67-456d5fb8-2928-495b-bcf7-8f9e32167449.txn deleted file mode 100644 index 1d35011fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/67-456d5fb8-2928-495b-bcf7-8f9e32167449.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/670-dbda3643-7ec4-4816-9943-4979c8c40153.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/670-dbda3643-7ec4-4816-9943-4979c8c40153.txn deleted file mode 100644 index 749ccdeb1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/670-dbda3643-7ec4-4816-9943-4979c8c40153.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/671-a9317e1a-00bc-4cc8-8378-096fd9f5cb5e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/671-a9317e1a-00bc-4cc8-8378-096fd9f5cb5e.txn deleted file mode 100644 index 8795282a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/671-a9317e1a-00bc-4cc8-8378-096fd9f5cb5e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/672-58c13279-0fb4-4b82-b96a-17282a9e3f6c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/672-58c13279-0fb4-4b82-b96a-17282a9e3f6c.txn deleted file mode 100644 index 2d4d66474..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/672-58c13279-0fb4-4b82-b96a-17282a9e3f6c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/673-b62efac9-d8eb-4afb-a086-beaf41f9ab8b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/673-b62efac9-d8eb-4afb-a086-beaf41f9ab8b.txn deleted file mode 100644 index 2100cd3cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/673-b62efac9-d8eb-4afb-a086-beaf41f9ab8b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/674-2bf18aac-20b8-4630-bd67-0a5dacc5af23.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/674-2bf18aac-20b8-4630-bd67-0a5dacc5af23.txn deleted file mode 100644 index 2ea3d53ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/674-2bf18aac-20b8-4630-bd67-0a5dacc5af23.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/675-19b23fa2-fd0e-444c-a09f-a55831f09090.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/675-19b23fa2-fd0e-444c-a09f-a55831f09090.txn deleted file mode 100644 index 3a68144f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/675-19b23fa2-fd0e-444c-a09f-a55831f09090.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/676-71c1abfe-774f-47d6-9db7-adcadbcacb80.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/676-71c1abfe-774f-47d6-9db7-adcadbcacb80.txn deleted file mode 100644 index 31e801631..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/676-71c1abfe-774f-47d6-9db7-adcadbcacb80.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/677-b70005aa-8c29-45a5-a5a6-ae75ce2db399.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/677-b70005aa-8c29-45a5-a5a6-ae75ce2db399.txn deleted file mode 100644 index 7951dd51a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/677-b70005aa-8c29-45a5-a5a6-ae75ce2db399.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/678-06501541-2727-4a02-a156-2ce407345893.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/678-06501541-2727-4a02-a156-2ce407345893.txn deleted file mode 100644 index ae7d864c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/678-06501541-2727-4a02-a156-2ce407345893.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/679-c4b8dccd-e48c-4d44-aab2-114f5fa531c6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/679-c4b8dccd-e48c-4d44-aab2-114f5fa531c6.txn deleted file mode 100644 index 3da26e691..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/679-c4b8dccd-e48c-4d44-aab2-114f5fa531c6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/68-26d0b36d-485c-4c05-bc1b-9a05a8238bda.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/68-26d0b36d-485c-4c05-bc1b-9a05a8238bda.txn deleted file mode 100644 index 1bb0baf1d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/68-26d0b36d-485c-4c05-bc1b-9a05a8238bda.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/680-4106ee7e-385c-4285-92be-45e112d8d843.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/680-4106ee7e-385c-4285-92be-45e112d8d843.txn deleted file mode 100644 index 362a38ae0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/680-4106ee7e-385c-4285-92be-45e112d8d843.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/681-9302f5da-c9bc-4eb2-871f-03b9876833e7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/681-9302f5da-c9bc-4eb2-871f-03b9876833e7.txn deleted file mode 100644 index beb2e4ac7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/681-9302f5da-c9bc-4eb2-871f-03b9876833e7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/682-92466078-16a6-4a94-921e-4722427aa24c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/682-92466078-16a6-4a94-921e-4722427aa24c.txn deleted file mode 100644 index 8a3b9bd06..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/682-92466078-16a6-4a94-921e-4722427aa24c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/683-5c5979da-127b-40ea-b4a8-2240f2f31c8a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/683-5c5979da-127b-40ea-b4a8-2240f2f31c8a.txn deleted file mode 100644 index 9509dfd27..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/683-5c5979da-127b-40ea-b4a8-2240f2f31c8a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/684-18c5d977-dd28-4092-84af-c9df975a4be3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/684-18c5d977-dd28-4092-84af-c9df975a4be3.txn deleted file mode 100644 index 9081056dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/684-18c5d977-dd28-4092-84af-c9df975a4be3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/685-81643fe5-77d0-4cc4-bc76-f7e8d0af1eae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/685-81643fe5-77d0-4cc4-bc76-f7e8d0af1eae.txn deleted file mode 100644 index 2a453f362..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/685-81643fe5-77d0-4cc4-bc76-f7e8d0af1eae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/686-1ac47e59-77a7-4b2f-b09d-be43e5ef4a6e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/686-1ac47e59-77a7-4b2f-b09d-be43e5ef4a6e.txn deleted file mode 100644 index 97ce86240..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/686-1ac47e59-77a7-4b2f-b09d-be43e5ef4a6e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/687-1cdef4e3-b24a-4190-bb4d-9b71452f859f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/687-1cdef4e3-b24a-4190-bb4d-9b71452f859f.txn deleted file mode 100644 index 92a2d7505..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/687-1cdef4e3-b24a-4190-bb4d-9b71452f859f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/688-fdde0eee-e64c-4f3e-81b9-5798a33f381a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/688-fdde0eee-e64c-4f3e-81b9-5798a33f381a.txn deleted file mode 100644 index 46a7b464c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/688-fdde0eee-e64c-4f3e-81b9-5798a33f381a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/689-6b606097-ccf4-453d-bcb3-dbaa800d4729.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/689-6b606097-ccf4-453d-bcb3-dbaa800d4729.txn deleted file mode 100644 index b471f131d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/689-6b606097-ccf4-453d-bcb3-dbaa800d4729.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/69-87cc995a-4ad8-4bba-b725-06b679595198.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/69-87cc995a-4ad8-4bba-b725-06b679595198.txn deleted file mode 100644 index a2c9a4eed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/69-87cc995a-4ad8-4bba-b725-06b679595198.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/690-c32d66a4-45dc-4fa1-9f0c-4338259097a9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/690-c32d66a4-45dc-4fa1-9f0c-4338259097a9.txn deleted file mode 100644 index 95a5e5be9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/690-c32d66a4-45dc-4fa1-9f0c-4338259097a9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/691-7813f6d2-bce1-47bc-a051-140b90d9fcc0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/691-7813f6d2-bce1-47bc-a051-140b90d9fcc0.txn deleted file mode 100644 index 0b6f6574b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/691-7813f6d2-bce1-47bc-a051-140b90d9fcc0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/692-c09185fa-b7bf-47c3-bad6-a421626dbbcc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/692-c09185fa-b7bf-47c3-bad6-a421626dbbcc.txn deleted file mode 100644 index b5dd08e7f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/692-c09185fa-b7bf-47c3-bad6-a421626dbbcc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/693-4bfd2609-2145-4413-bb66-9a230de62da5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/693-4bfd2609-2145-4413-bb66-9a230de62da5.txn deleted file mode 100644 index 76d6c4cb9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/693-4bfd2609-2145-4413-bb66-9a230de62da5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/694-78a596ce-8df2-41a7-95ea-1cc6f7b16850.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/694-78a596ce-8df2-41a7-95ea-1cc6f7b16850.txn deleted file mode 100644 index e66c0a1e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/694-78a596ce-8df2-41a7-95ea-1cc6f7b16850.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/695-33e23f37-f44f-4d62-90a0-06444d89f7a4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/695-33e23f37-f44f-4d62-90a0-06444d89f7a4.txn deleted file mode 100644 index e1ba3b10f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/695-33e23f37-f44f-4d62-90a0-06444d89f7a4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/696-8bef2270-3a08-4fe3-8d4c-b5aeb6f22ef2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/696-8bef2270-3a08-4fe3-8d4c-b5aeb6f22ef2.txn deleted file mode 100644 index a5204d818..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/696-8bef2270-3a08-4fe3-8d4c-b5aeb6f22ef2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/697-63bc362f-ec94-4334-96f3-fbbdb7acc67c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/697-63bc362f-ec94-4334-96f3-fbbdb7acc67c.txn deleted file mode 100644 index b27d54d5c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/697-63bc362f-ec94-4334-96f3-fbbdb7acc67c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/698-e4b361d6-dc0c-4a30-918c-1904716799aa.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/698-e4b361d6-dc0c-4a30-918c-1904716799aa.txn deleted file mode 100644 index 55528ebfa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/698-e4b361d6-dc0c-4a30-918c-1904716799aa.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/699-f9a6c309-5130-4c75-8100-7c9f9f28b1e8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/699-f9a6c309-5130-4c75-8100-7c9f9f28b1e8.txn deleted file mode 100644 index 9d785dce6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/699-f9a6c309-5130-4c75-8100-7c9f9f28b1e8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/7-e5128e38-5be0-400f-8473-c050cf696fe3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/7-e5128e38-5be0-400f-8473-c050cf696fe3.txn deleted file mode 100644 index 79d6782f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/7-e5128e38-5be0-400f-8473-c050cf696fe3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/70-b7139d3b-d961-4565-b5b7-37b50945eae1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/70-b7139d3b-d961-4565-b5b7-37b50945eae1.txn deleted file mode 100644 index f2c232cc0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/70-b7139d3b-d961-4565-b5b7-37b50945eae1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/700-7d6945ee-de8f-4984-8b70-bb8a2cdba2f7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/700-7d6945ee-de8f-4984-8b70-bb8a2cdba2f7.txn deleted file mode 100644 index a30fdb0bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/700-7d6945ee-de8f-4984-8b70-bb8a2cdba2f7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/701-65a0df91-45b4-45a4-8e72-dbd274017ed8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/701-65a0df91-45b4-45a4-8e72-dbd274017ed8.txn deleted file mode 100644 index 6a970428f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/701-65a0df91-45b4-45a4-8e72-dbd274017ed8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/702-aeb12a39-afc6-42b3-b8bd-040fe0cc8dff.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/702-aeb12a39-afc6-42b3-b8bd-040fe0cc8dff.txn deleted file mode 100644 index a604a7d5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/702-aeb12a39-afc6-42b3-b8bd-040fe0cc8dff.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/703-703267de-8b4b-49da-845d-ad8a19fe3bc5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/703-703267de-8b4b-49da-845d-ad8a19fe3bc5.txn deleted file mode 100644 index 22d807f2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/703-703267de-8b4b-49da-845d-ad8a19fe3bc5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/704-ed492ded-2520-4236-9026-f090cad3887e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/704-ed492ded-2520-4236-9026-f090cad3887e.txn deleted file mode 100644 index 9ae7ce3cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/704-ed492ded-2520-4236-9026-f090cad3887e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/705-38b74b5d-98c7-4b67-8e68-c36bc6d4103f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/705-38b74b5d-98c7-4b67-8e68-c36bc6d4103f.txn deleted file mode 100644 index a03cde957..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/705-38b74b5d-98c7-4b67-8e68-c36bc6d4103f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/706-53c49766-9c2b-4df3-9983-5b2aea62da24.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/706-53c49766-9c2b-4df3-9983-5b2aea62da24.txn deleted file mode 100644 index be355dccb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/706-53c49766-9c2b-4df3-9983-5b2aea62da24.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/707-a0ea68f3-cfcd-4493-9459-4989d0c29300.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/707-a0ea68f3-cfcd-4493-9459-4989d0c29300.txn deleted file mode 100644 index 0c28c7fbc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/707-a0ea68f3-cfcd-4493-9459-4989d0c29300.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/708-cfd29cd4-7995-469b-b99b-69f0a4b26c27.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/708-cfd29cd4-7995-469b-b99b-69f0a4b26c27.txn deleted file mode 100644 index d64d0ae09..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/708-cfd29cd4-7995-469b-b99b-69f0a4b26c27.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/709-a9eaade3-0d6e-42d1-93c6-e2454eddd96a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/709-a9eaade3-0d6e-42d1-93c6-e2454eddd96a.txn deleted file mode 100644 index 2c5a389fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/709-a9eaade3-0d6e-42d1-93c6-e2454eddd96a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/71-df5c533c-3051-4a7b-a0bc-81a35afc9c3a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/71-df5c533c-3051-4a7b-a0bc-81a35afc9c3a.txn deleted file mode 100644 index 366e5a88b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/71-df5c533c-3051-4a7b-a0bc-81a35afc9c3a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/710-077439c2-001c-4caa-9582-838e0eb86293.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/710-077439c2-001c-4caa-9582-838e0eb86293.txn deleted file mode 100644 index 4e3dd8220..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/710-077439c2-001c-4caa-9582-838e0eb86293.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/711-3e879e0e-530a-40f2-ae58-16c3958ea6c3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/711-3e879e0e-530a-40f2-ae58-16c3958ea6c3.txn deleted file mode 100644 index 5cc45bbb3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/711-3e879e0e-530a-40f2-ae58-16c3958ea6c3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/712-14512071-9344-46e8-99df-fa5de25632dd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/712-14512071-9344-46e8-99df-fa5de25632dd.txn deleted file mode 100644 index f10136190..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/712-14512071-9344-46e8-99df-fa5de25632dd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/713-8bc638c9-9f4a-4e41-a8e3-ea37ba0b964e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/713-8bc638c9-9f4a-4e41-a8e3-ea37ba0b964e.txn deleted file mode 100644 index 59e74c995..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/713-8bc638c9-9f4a-4e41-a8e3-ea37ba0b964e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/714-f6dea0f3-b695-4431-8932-ec1aeed82ec8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/714-f6dea0f3-b695-4431-8932-ec1aeed82ec8.txn deleted file mode 100644 index c5085ae1c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/714-f6dea0f3-b695-4431-8932-ec1aeed82ec8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/715-c1748db1-454b-441b-a527-960e2ef28ff8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/715-c1748db1-454b-441b-a527-960e2ef28ff8.txn deleted file mode 100644 index 45ee99ea8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/715-c1748db1-454b-441b-a527-960e2ef28ff8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/716-583b390c-ab74-4c64-afb6-9af50bb313bb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/716-583b390c-ab74-4c64-afb6-9af50bb313bb.txn deleted file mode 100644 index 9ae4fe5db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/716-583b390c-ab74-4c64-afb6-9af50bb313bb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/717-6fae9466-0b05-4587-8170-dbc25a0aea14.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/717-6fae9466-0b05-4587-8170-dbc25a0aea14.txn deleted file mode 100644 index a277df2c1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/717-6fae9466-0b05-4587-8170-dbc25a0aea14.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/718-6882ed5f-b622-46ec-83a9-0d0818726a94.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/718-6882ed5f-b622-46ec-83a9-0d0818726a94.txn deleted file mode 100644 index 1bb8712c2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/718-6882ed5f-b622-46ec-83a9-0d0818726a94.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/719-2f6e4f91-24c7-4711-9129-101319111278.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/719-2f6e4f91-24c7-4711-9129-101319111278.txn deleted file mode 100644 index 135d9622f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/719-2f6e4f91-24c7-4711-9129-101319111278.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/72-a077aef8-40d7-42f3-9695-d872ed137759.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/72-a077aef8-40d7-42f3-9695-d872ed137759.txn deleted file mode 100644 index 597b1a380..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/72-a077aef8-40d7-42f3-9695-d872ed137759.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/720-b407c53b-9eda-4187-82b0-5a4abeb45658.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/720-b407c53b-9eda-4187-82b0-5a4abeb45658.txn deleted file mode 100644 index c871110ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/720-b407c53b-9eda-4187-82b0-5a4abeb45658.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/721-43ea7741-2705-4887-ad91-fb2d2bc47bca.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/721-43ea7741-2705-4887-ad91-fb2d2bc47bca.txn deleted file mode 100644 index 452097732..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/721-43ea7741-2705-4887-ad91-fb2d2bc47bca.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/722-e8db2679-8a8f-49bd-b488-09e14b22502c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/722-e8db2679-8a8f-49bd-b488-09e14b22502c.txn deleted file mode 100644 index 8073f64a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/722-e8db2679-8a8f-49bd-b488-09e14b22502c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/723-003340cb-3c32-4d3c-9832-0939bfcc8880.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/723-003340cb-3c32-4d3c-9832-0939bfcc8880.txn deleted file mode 100644 index ece9748ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/723-003340cb-3c32-4d3c-9832-0939bfcc8880.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/724-0ba8f110-1626-49e3-bb8f-ab5b144f9c45.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/724-0ba8f110-1626-49e3-bb8f-ab5b144f9c45.txn deleted file mode 100644 index 26c050bff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/724-0ba8f110-1626-49e3-bb8f-ab5b144f9c45.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/725-efeb02c8-77f9-4d38-8847-da5590f20e91.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/725-efeb02c8-77f9-4d38-8847-da5590f20e91.txn deleted file mode 100644 index 3e1c2049e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/725-efeb02c8-77f9-4d38-8847-da5590f20e91.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/726-0a5c8984-3679-448c-8b0c-230175852888.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/726-0a5c8984-3679-448c-8b0c-230175852888.txn deleted file mode 100644 index 8fd628e52..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/726-0a5c8984-3679-448c-8b0c-230175852888.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/727-6d601ac7-40d3-4974-b21b-0847fd34a98b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/727-6d601ac7-40d3-4974-b21b-0847fd34a98b.txn deleted file mode 100644 index c41929cc0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/727-6d601ac7-40d3-4974-b21b-0847fd34a98b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/728-da44b8b6-6cbb-4702-b6d7-b14bb81099e8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/728-da44b8b6-6cbb-4702-b6d7-b14bb81099e8.txn deleted file mode 100644 index 10842899b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/728-da44b8b6-6cbb-4702-b6d7-b14bb81099e8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/729-fac9514f-efdc-442e-9de1-3ac0595f6d9f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/729-fac9514f-efdc-442e-9de1-3ac0595f6d9f.txn deleted file mode 100644 index 3faede7cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/729-fac9514f-efdc-442e-9de1-3ac0595f6d9f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/73-a291c5af-e404-41ae-af8f-1cfaef9d1239.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/73-a291c5af-e404-41ae-af8f-1cfaef9d1239.txn deleted file mode 100644 index 55e2b6c7f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/73-a291c5af-e404-41ae-af8f-1cfaef9d1239.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/730-513674e5-df6f-455f-a8c5-c580444e49f2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/730-513674e5-df6f-455f-a8c5-c580444e49f2.txn deleted file mode 100644 index b116f5824..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/730-513674e5-df6f-455f-a8c5-c580444e49f2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/731-56becab5-8b92-4d67-bcd7-864ab1b7c20f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/731-56becab5-8b92-4d67-bcd7-864ab1b7c20f.txn deleted file mode 100644 index 1d33df300..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/731-56becab5-8b92-4d67-bcd7-864ab1b7c20f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/732-ffb6cc6f-7952-45a2-8d6b-69bdedffd81f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/732-ffb6cc6f-7952-45a2-8d6b-69bdedffd81f.txn deleted file mode 100644 index e4d48899e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/732-ffb6cc6f-7952-45a2-8d6b-69bdedffd81f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/733-feb2ed02-8a76-480c-847f-5d390116730e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/733-feb2ed02-8a76-480c-847f-5d390116730e.txn deleted file mode 100644 index b152fd288..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/733-feb2ed02-8a76-480c-847f-5d390116730e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/734-1b004578-61d4-4129-84eb-2b15d7a53f14.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/734-1b004578-61d4-4129-84eb-2b15d7a53f14.txn deleted file mode 100644 index e666d915c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/734-1b004578-61d4-4129-84eb-2b15d7a53f14.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/735-057155f6-998f-4314-8ada-8ce8a8e6b3c1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/735-057155f6-998f-4314-8ada-8ce8a8e6b3c1.txn deleted file mode 100644 index 8ebcca00d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/735-057155f6-998f-4314-8ada-8ce8a8e6b3c1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/736-5ddf954d-0e25-409b-adf5-4cec55d54cce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/736-5ddf954d-0e25-409b-adf5-4cec55d54cce.txn deleted file mode 100644 index 0ae2441aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/736-5ddf954d-0e25-409b-adf5-4cec55d54cce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/737-e573ab5b-ec95-49fd-8fac-1e2d772d75b9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/737-e573ab5b-ec95-49fd-8fac-1e2d772d75b9.txn deleted file mode 100644 index 02782b486..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/737-e573ab5b-ec95-49fd-8fac-1e2d772d75b9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/738-a619eaa2-4570-4222-922a-f6c175ad5786.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/738-a619eaa2-4570-4222-922a-f6c175ad5786.txn deleted file mode 100644 index bdf04b81d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/738-a619eaa2-4570-4222-922a-f6c175ad5786.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/739-a4c73c5b-2eed-4ae0-8efc-5a537e0e03c3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/739-a4c73c5b-2eed-4ae0-8efc-5a537e0e03c3.txn deleted file mode 100644 index 494c41072..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/739-a4c73c5b-2eed-4ae0-8efc-5a537e0e03c3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/74-540704cc-7b0f-4c9e-b14c-69fc802aa599.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/74-540704cc-7b0f-4c9e-b14c-69fc802aa599.txn deleted file mode 100644 index da7578f44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/74-540704cc-7b0f-4c9e-b14c-69fc802aa599.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/740-6d62b475-b2f5-4904-9daa-0e9fa2f490bb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/740-6d62b475-b2f5-4904-9daa-0e9fa2f490bb.txn deleted file mode 100644 index bb4fc6728..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/740-6d62b475-b2f5-4904-9daa-0e9fa2f490bb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/741-1f858b9d-7935-4578-94bf-41b5361fc9f5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/741-1f858b9d-7935-4578-94bf-41b5361fc9f5.txn deleted file mode 100644 index f4b6a9e05..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/741-1f858b9d-7935-4578-94bf-41b5361fc9f5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/742-97fba06e-da07-4fbc-9365-c643df08df2c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/742-97fba06e-da07-4fbc-9365-c643df08df2c.txn deleted file mode 100644 index fb21a6ae0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/742-97fba06e-da07-4fbc-9365-c643df08df2c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/743-5a4eb913-7d76-44b0-96ae-194763ca4238.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/743-5a4eb913-7d76-44b0-96ae-194763ca4238.txn deleted file mode 100644 index c5dd70db3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/743-5a4eb913-7d76-44b0-96ae-194763ca4238.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/744-3d6f7a36-9f7f-41e9-803d-598dddc36bd5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/744-3d6f7a36-9f7f-41e9-803d-598dddc36bd5.txn deleted file mode 100644 index 3097427f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/744-3d6f7a36-9f7f-41e9-803d-598dddc36bd5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/745-653ba250-83ab-464c-a7e4-c21082368314.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/745-653ba250-83ab-464c-a7e4-c21082368314.txn deleted file mode 100644 index e8fa90e02..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/745-653ba250-83ab-464c-a7e4-c21082368314.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/746-d4d10ca6-496f-4eaf-9c61-6311fa43aed7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/746-d4d10ca6-496f-4eaf-9c61-6311fa43aed7.txn deleted file mode 100644 index 1a184d8fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/746-d4d10ca6-496f-4eaf-9c61-6311fa43aed7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/747-c9697bbd-4238-4dc0-9e4d-ab079c73f430.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/747-c9697bbd-4238-4dc0-9e4d-ab079c73f430.txn deleted file mode 100644 index 8200f7560..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/747-c9697bbd-4238-4dc0-9e4d-ab079c73f430.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/748-72108698-2869-4a3d-9fc0-6757b7562b0a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/748-72108698-2869-4a3d-9fc0-6757b7562b0a.txn deleted file mode 100644 index a24e26f6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/748-72108698-2869-4a3d-9fc0-6757b7562b0a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/749-06a92ad5-6afa-4098-800d-044c1970f321.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/749-06a92ad5-6afa-4098-800d-044c1970f321.txn deleted file mode 100644 index 5bf54ef7d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/749-06a92ad5-6afa-4098-800d-044c1970f321.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/75-250f4136-9e3e-4a35-9e4a-5750faab21a7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/75-250f4136-9e3e-4a35-9e4a-5750faab21a7.txn deleted file mode 100644 index 108f91fff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/75-250f4136-9e3e-4a35-9e4a-5750faab21a7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/750-a43edc22-132d-4a71-b4dc-6413be2eb2a9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/750-a43edc22-132d-4a71-b4dc-6413be2eb2a9.txn deleted file mode 100644 index db5209938..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/750-a43edc22-132d-4a71-b4dc-6413be2eb2a9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/751-c6c814cb-279e-4750-afc4-a9a2112f7fdc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/751-c6c814cb-279e-4750-afc4-a9a2112f7fdc.txn deleted file mode 100644 index 59cdfe66d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/751-c6c814cb-279e-4750-afc4-a9a2112f7fdc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/752-beb248f4-5878-4349-8d2a-1a74239dc787.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/752-beb248f4-5878-4349-8d2a-1a74239dc787.txn deleted file mode 100644 index 4198db87c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/752-beb248f4-5878-4349-8d2a-1a74239dc787.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/753-3f9667d6-1b26-4d4a-9817-69e7f4681f94.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/753-3f9667d6-1b26-4d4a-9817-69e7f4681f94.txn deleted file mode 100644 index 4d2f76e87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/753-3f9667d6-1b26-4d4a-9817-69e7f4681f94.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/754-a6f86bdd-60da-4547-a59b-15add14c8bce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/754-a6f86bdd-60da-4547-a59b-15add14c8bce.txn deleted file mode 100644 index 97826a70a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/754-a6f86bdd-60da-4547-a59b-15add14c8bce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/755-62862d44-dd80-4e63-b4d1-f4aa145baff8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/755-62862d44-dd80-4e63-b4d1-f4aa145baff8.txn deleted file mode 100644 index 958af6293..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/755-62862d44-dd80-4e63-b4d1-f4aa145baff8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/756-aa161daa-e51f-4c85-9a06-69b7e23eb452.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/756-aa161daa-e51f-4c85-9a06-69b7e23eb452.txn deleted file mode 100644 index 2fa8324cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/756-aa161daa-e51f-4c85-9a06-69b7e23eb452.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/757-102187df-444f-47f2-8217-a84ac0727d4a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/757-102187df-444f-47f2-8217-a84ac0727d4a.txn deleted file mode 100644 index 9817c3213..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/757-102187df-444f-47f2-8217-a84ac0727d4a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/758-4547364c-cf3c-44cf-9dbb-1159163b4b94.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/758-4547364c-cf3c-44cf-9dbb-1159163b4b94.txn deleted file mode 100644 index 7cec92678..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/758-4547364c-cf3c-44cf-9dbb-1159163b4b94.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/759-2f49ca42-fbde-406f-bc9c-78e3ce821556.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/759-2f49ca42-fbde-406f-bc9c-78e3ce821556.txn deleted file mode 100644 index 0077621b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/759-2f49ca42-fbde-406f-bc9c-78e3ce821556.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/76-96e84fcb-631a-4bc0-adcc-82d97aaf68d0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/76-96e84fcb-631a-4bc0-adcc-82d97aaf68d0.txn deleted file mode 100644 index 445fcedae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/76-96e84fcb-631a-4bc0-adcc-82d97aaf68d0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/760-799c9514-82aa-4fea-ac64-cf9f6d2483a2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/760-799c9514-82aa-4fea-ac64-cf9f6d2483a2.txn deleted file mode 100644 index a8f763d8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/760-799c9514-82aa-4fea-ac64-cf9f6d2483a2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/761-6f24c397-23c1-4a8f-9791-2cc7861852f6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/761-6f24c397-23c1-4a8f-9791-2cc7861852f6.txn deleted file mode 100644 index a5623bb55..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/761-6f24c397-23c1-4a8f-9791-2cc7861852f6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/762-339a4169-ae34-4d5f-b824-82a0d2bc60e2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/762-339a4169-ae34-4d5f-b824-82a0d2bc60e2.txn deleted file mode 100644 index ca41d103e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/762-339a4169-ae34-4d5f-b824-82a0d2bc60e2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/763-f98e4748-0051-4559-bd7b-27091b0836e8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/763-f98e4748-0051-4559-bd7b-27091b0836e8.txn deleted file mode 100644 index d2bf52d74..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/763-f98e4748-0051-4559-bd7b-27091b0836e8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/764-78749026-e3ad-4b18-837f-4d0d1e71dc87.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/764-78749026-e3ad-4b18-837f-4d0d1e71dc87.txn deleted file mode 100644 index 25ca74c88..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/764-78749026-e3ad-4b18-837f-4d0d1e71dc87.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/765-efbc2140-da99-4065-8dd7-8b5e8ef1d093.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/765-efbc2140-da99-4065-8dd7-8b5e8ef1d093.txn deleted file mode 100644 index 233579e31..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/765-efbc2140-da99-4065-8dd7-8b5e8ef1d093.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/766-bc41d0cf-9d49-4b73-a851-3f8ad368dadc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/766-bc41d0cf-9d49-4b73-a851-3f8ad368dadc.txn deleted file mode 100644 index d1d57ac7a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/766-bc41d0cf-9d49-4b73-a851-3f8ad368dadc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/767-33492f4c-6bd1-4cca-9e14-ebd2c7d3503c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/767-33492f4c-6bd1-4cca-9e14-ebd2c7d3503c.txn deleted file mode 100644 index f07b0a8ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/767-33492f4c-6bd1-4cca-9e14-ebd2c7d3503c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/768-2881fd0d-a1f4-4382-9466-9f53157ec727.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/768-2881fd0d-a1f4-4382-9466-9f53157ec727.txn deleted file mode 100644 index 0942d14d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/768-2881fd0d-a1f4-4382-9466-9f53157ec727.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/769-abe98e1c-f261-4ccc-ba2d-6d704cb26223.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/769-abe98e1c-f261-4ccc-ba2d-6d704cb26223.txn deleted file mode 100644 index 0abf826a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/769-abe98e1c-f261-4ccc-ba2d-6d704cb26223.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/77-54845238-8e74-419f-b5cd-cea15f875e63.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/77-54845238-8e74-419f-b5cd-cea15f875e63.txn deleted file mode 100644 index 2a06ee898..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/77-54845238-8e74-419f-b5cd-cea15f875e63.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/770-3126548f-35c5-4171-97ed-721a6231fe7f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/770-3126548f-35c5-4171-97ed-721a6231fe7f.txn deleted file mode 100644 index 9a84241ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/770-3126548f-35c5-4171-97ed-721a6231fe7f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/771-937c40d9-fa4f-4186-99bd-5edcd7b77a61.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/771-937c40d9-fa4f-4186-99bd-5edcd7b77a61.txn deleted file mode 100644 index 57087e146..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/771-937c40d9-fa4f-4186-99bd-5edcd7b77a61.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/772-614bf238-94a5-4d5b-a3f1-79db5354ae67.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/772-614bf238-94a5-4d5b-a3f1-79db5354ae67.txn deleted file mode 100644 index 5af14237b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/772-614bf238-94a5-4d5b-a3f1-79db5354ae67.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/773-f669485f-1e92-4c14-83f8-e46dd29e6901.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/773-f669485f-1e92-4c14-83f8-e46dd29e6901.txn deleted file mode 100644 index 44322ed8b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/773-f669485f-1e92-4c14-83f8-e46dd29e6901.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/774-e503e4c3-389e-49aa-9778-3bfdf996f4b2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/774-e503e4c3-389e-49aa-9778-3bfdf996f4b2.txn deleted file mode 100644 index 1aa5afa5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/774-e503e4c3-389e-49aa-9778-3bfdf996f4b2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/775-d6eaf834-bc63-4785-959f-b079dcae3ab9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/775-d6eaf834-bc63-4785-959f-b079dcae3ab9.txn deleted file mode 100644 index 91297882f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/775-d6eaf834-bc63-4785-959f-b079dcae3ab9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/776-20e3c360-cc46-4d0c-bfe6-e2079009ff38.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/776-20e3c360-cc46-4d0c-bfe6-e2079009ff38.txn deleted file mode 100644 index a47c1df04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/776-20e3c360-cc46-4d0c-bfe6-e2079009ff38.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/777-7e05f29c-d2e1-44e2-a11a-6923f965e4f2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/777-7e05f29c-d2e1-44e2-a11a-6923f965e4f2.txn deleted file mode 100644 index 343376c12..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/777-7e05f29c-d2e1-44e2-a11a-6923f965e4f2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/778-6b9b35f4-95b4-4481-a681-d26f0267011b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/778-6b9b35f4-95b4-4481-a681-d26f0267011b.txn deleted file mode 100644 index 7ba5a9a76..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/778-6b9b35f4-95b4-4481-a681-d26f0267011b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/779-e3093fec-b98b-4a25-b2b3-7582314f6452.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/779-e3093fec-b98b-4a25-b2b3-7582314f6452.txn deleted file mode 100644 index 1aebdd230..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/779-e3093fec-b98b-4a25-b2b3-7582314f6452.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/78-53c8093a-498f-4564-8f12-f0c020fea0a5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/78-53c8093a-498f-4564-8f12-f0c020fea0a5.txn deleted file mode 100644 index 216ee9a23..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/78-53c8093a-498f-4564-8f12-f0c020fea0a5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/780-02bd214d-6c49-4223-85b9-370d490260bd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/780-02bd214d-6c49-4223-85b9-370d490260bd.txn deleted file mode 100644 index fd788c44b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/780-02bd214d-6c49-4223-85b9-370d490260bd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/781-5c632882-eaf3-42ac-92c4-bb5fc6abcafc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/781-5c632882-eaf3-42ac-92c4-bb5fc6abcafc.txn deleted file mode 100644 index dc1999d6a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/781-5c632882-eaf3-42ac-92c4-bb5fc6abcafc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/782-9a37d7db-edb9-4378-8a67-24c740d069b7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/782-9a37d7db-edb9-4378-8a67-24c740d069b7.txn deleted file mode 100644 index 474457f87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/782-9a37d7db-edb9-4378-8a67-24c740d069b7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/783-e803c3a9-63bb-4bce-a632-c1c2fc70eb33.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/783-e803c3a9-63bb-4bce-a632-c1c2fc70eb33.txn deleted file mode 100644 index e00de06e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/783-e803c3a9-63bb-4bce-a632-c1c2fc70eb33.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/784-9d7a146e-c795-408d-b631-e7390edc97a9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/784-9d7a146e-c795-408d-b631-e7390edc97a9.txn deleted file mode 100644 index f0ac218b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/784-9d7a146e-c795-408d-b631-e7390edc97a9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/785-f90ce52a-973e-457f-bef7-06afbce74f42.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/785-f90ce52a-973e-457f-bef7-06afbce74f42.txn deleted file mode 100644 index c78bb63ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/785-f90ce52a-973e-457f-bef7-06afbce74f42.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/786-f5b03634-61a2-4a42-a123-354d5f27a97b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/786-f5b03634-61a2-4a42-a123-354d5f27a97b.txn deleted file mode 100644 index 2408eb60d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/786-f5b03634-61a2-4a42-a123-354d5f27a97b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/787-31c8ce4a-45ec-4698-8492-5a85f36163c4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/787-31c8ce4a-45ec-4698-8492-5a85f36163c4.txn deleted file mode 100644 index 73d966ad4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/787-31c8ce4a-45ec-4698-8492-5a85f36163c4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/788-790f592f-2bc2-409a-bd5a-ba2f92e332ce.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/788-790f592f-2bc2-409a-bd5a-ba2f92e332ce.txn deleted file mode 100644 index 2cbcb5f7c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/788-790f592f-2bc2-409a-bd5a-ba2f92e332ce.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/789-76f0f235-46a4-44c8-8bad-6e72ccc939c5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/789-76f0f235-46a4-44c8-8bad-6e72ccc939c5.txn deleted file mode 100644 index a3352ca6e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/789-76f0f235-46a4-44c8-8bad-6e72ccc939c5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/79-220b55f9-7e4d-4b13-b7c8-0c165a96430a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/79-220b55f9-7e4d-4b13-b7c8-0c165a96430a.txn deleted file mode 100644 index 70c7e3f9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/79-220b55f9-7e4d-4b13-b7c8-0c165a96430a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/790-2c2d9405-0e15-4be8-bd74-56000ea63cea.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/790-2c2d9405-0e15-4be8-bd74-56000ea63cea.txn deleted file mode 100644 index ef9feafc3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/790-2c2d9405-0e15-4be8-bd74-56000ea63cea.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/791-e1199eb3-c0d5-4edc-90e1-72b62b943f60.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/791-e1199eb3-c0d5-4edc-90e1-72b62b943f60.txn deleted file mode 100644 index e9d7253fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/791-e1199eb3-c0d5-4edc-90e1-72b62b943f60.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/792-1d9bcc45-badf-41d2-afdf-fcd6f2ac22d4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/792-1d9bcc45-badf-41d2-afdf-fcd6f2ac22d4.txn deleted file mode 100644 index 89cd835c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/792-1d9bcc45-badf-41d2-afdf-fcd6f2ac22d4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/793-3a222459-3fdb-45a1-8797-0f9d9f3144c3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/793-3a222459-3fdb-45a1-8797-0f9d9f3144c3.txn deleted file mode 100644 index feef3c1c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/793-3a222459-3fdb-45a1-8797-0f9d9f3144c3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/794-14bfb644-e967-42c3-8a26-1eab6592c1ca.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/794-14bfb644-e967-42c3-8a26-1eab6592c1ca.txn deleted file mode 100644 index 655728614..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/794-14bfb644-e967-42c3-8a26-1eab6592c1ca.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/795-264219eb-d2fa-49ce-895c-08a8aac21cf5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/795-264219eb-d2fa-49ce-895c-08a8aac21cf5.txn deleted file mode 100644 index ecb7b420f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/795-264219eb-d2fa-49ce-895c-08a8aac21cf5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/796-dab4694b-b5fd-460b-9d49-e79421ad2082.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/796-dab4694b-b5fd-460b-9d49-e79421ad2082.txn deleted file mode 100644 index 630c2b6c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/796-dab4694b-b5fd-460b-9d49-e79421ad2082.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/797-abb166e8-2463-4cfb-b961-537050afb5af.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/797-abb166e8-2463-4cfb-b961-537050afb5af.txn deleted file mode 100644 index c0c34bebe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/797-abb166e8-2463-4cfb-b961-537050afb5af.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/798-6f06b444-b8e4-4a21-93a5-92b8fc94b29f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/798-6f06b444-b8e4-4a21-93a5-92b8fc94b29f.txn deleted file mode 100644 index 13a7d4e5a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/798-6f06b444-b8e4-4a21-93a5-92b8fc94b29f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/799-25f1d617-745b-4e75-abde-57cd1de2341b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/799-25f1d617-745b-4e75-abde-57cd1de2341b.txn deleted file mode 100644 index 637281277..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/799-25f1d617-745b-4e75-abde-57cd1de2341b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/8-99b69a08-7f7f-4331-8408-4106b9740ab9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/8-99b69a08-7f7f-4331-8408-4106b9740ab9.txn deleted file mode 100644 index cf31eefbb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/8-99b69a08-7f7f-4331-8408-4106b9740ab9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/80-aebf5c7a-d503-44ec-9fcb-6c9fc1e6fe9c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/80-aebf5c7a-d503-44ec-9fcb-6c9fc1e6fe9c.txn deleted file mode 100644 index fdfb590bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/80-aebf5c7a-d503-44ec-9fcb-6c9fc1e6fe9c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/800-a72986e8-2689-472e-b44f-e633c78153a6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/800-a72986e8-2689-472e-b44f-e633c78153a6.txn deleted file mode 100644 index ff2a6d976..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/800-a72986e8-2689-472e-b44f-e633c78153a6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/801-91cd5430-ec43-4d8d-b1eb-74c65c5ecfe8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/801-91cd5430-ec43-4d8d-b1eb-74c65c5ecfe8.txn deleted file mode 100644 index f19d79fa1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/801-91cd5430-ec43-4d8d-b1eb-74c65c5ecfe8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/802-ad755a5c-fe97-4ddc-b159-90ed922c5651.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/802-ad755a5c-fe97-4ddc-b159-90ed922c5651.txn deleted file mode 100644 index 6be2fe45a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/802-ad755a5c-fe97-4ddc-b159-90ed922c5651.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/803-ea4dcc3b-646b-4678-9397-32a034f0bd24.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/803-ea4dcc3b-646b-4678-9397-32a034f0bd24.txn deleted file mode 100644 index b11e396a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/803-ea4dcc3b-646b-4678-9397-32a034f0bd24.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/804-f68b7e6e-08e5-44c6-9ae8-c0eaae844a8b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/804-f68b7e6e-08e5-44c6-9ae8-c0eaae844a8b.txn deleted file mode 100644 index cce0985e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/804-f68b7e6e-08e5-44c6-9ae8-c0eaae844a8b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/805-8c70d8b5-7cb0-4b56-b26a-ccbb396c4db1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/805-8c70d8b5-7cb0-4b56-b26a-ccbb396c4db1.txn deleted file mode 100644 index 23f258c65..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/805-8c70d8b5-7cb0-4b56-b26a-ccbb396c4db1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/806-e04c8e37-88f4-42ea-9eb4-d8ec987d1ed3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/806-e04c8e37-88f4-42ea-9eb4-d8ec987d1ed3.txn deleted file mode 100644 index 6c94fbdf8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/806-e04c8e37-88f4-42ea-9eb4-d8ec987d1ed3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/807-6e4b41a8-b4f7-4471-8b4f-584a202be12d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/807-6e4b41a8-b4f7-4471-8b4f-584a202be12d.txn deleted file mode 100644 index 8ff1eb621..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/807-6e4b41a8-b4f7-4471-8b4f-584a202be12d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/808-f70c53ff-c1ad-4734-9a12-c36f5db4ff32.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/808-f70c53ff-c1ad-4734-9a12-c36f5db4ff32.txn deleted file mode 100644 index c7021cbd2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/808-f70c53ff-c1ad-4734-9a12-c36f5db4ff32.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/809-57425639-63d6-4c2e-9ce7-56c74c642577.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/809-57425639-63d6-4c2e-9ce7-56c74c642577.txn deleted file mode 100644 index aeac56de7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/809-57425639-63d6-4c2e-9ce7-56c74c642577.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/81-34347027-3b6b-4a07-a342-a21c614cd064.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/81-34347027-3b6b-4a07-a342-a21c614cd064.txn deleted file mode 100644 index ba26e0be2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/81-34347027-3b6b-4a07-a342-a21c614cd064.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/810-6fbf5c9b-a7ff-445e-b3e1-d44553240998.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/810-6fbf5c9b-a7ff-445e-b3e1-d44553240998.txn deleted file mode 100644 index 59c7e6718..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/810-6fbf5c9b-a7ff-445e-b3e1-d44553240998.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/811-27759b78-b957-4909-b077-2699d6031f9a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/811-27759b78-b957-4909-b077-2699d6031f9a.txn deleted file mode 100644 index c1c1dc2f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/811-27759b78-b957-4909-b077-2699d6031f9a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/812-49d2b6be-0f4a-40e7-b3cc-2b6b6ce4f09f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/812-49d2b6be-0f4a-40e7-b3cc-2b6b6ce4f09f.txn deleted file mode 100644 index 56219e740..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/812-49d2b6be-0f4a-40e7-b3cc-2b6b6ce4f09f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/813-99147b2c-366c-4fda-bf3d-337173f7b77d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/813-99147b2c-366c-4fda-bf3d-337173f7b77d.txn deleted file mode 100644 index c3e03a2e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/813-99147b2c-366c-4fda-bf3d-337173f7b77d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/814-c2c0b713-c73c-47e5-8440-915aa40b9782.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/814-c2c0b713-c73c-47e5-8440-915aa40b9782.txn deleted file mode 100644 index 906cb261e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/814-c2c0b713-c73c-47e5-8440-915aa40b9782.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/815-676994d4-2944-4e58-97bc-874f6eb34555.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/815-676994d4-2944-4e58-97bc-874f6eb34555.txn deleted file mode 100644 index 6bb71af3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/815-676994d4-2944-4e58-97bc-874f6eb34555.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/816-be59aa19-8a8b-4ab2-a72d-4a2493cf9c10.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/816-be59aa19-8a8b-4ab2-a72d-4a2493cf9c10.txn deleted file mode 100644 index 14c058892..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/816-be59aa19-8a8b-4ab2-a72d-4a2493cf9c10.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/817-6d4418f2-8fa8-4575-b1cb-4418ccaae56d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/817-6d4418f2-8fa8-4575-b1cb-4418ccaae56d.txn deleted file mode 100644 index b43e1aa16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/817-6d4418f2-8fa8-4575-b1cb-4418ccaae56d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/818-9f8962cd-d66f-4cb9-8130-bf5fae332405.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/818-9f8962cd-d66f-4cb9-8130-bf5fae332405.txn deleted file mode 100644 index e42145a63..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/818-9f8962cd-d66f-4cb9-8130-bf5fae332405.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/819-719e754d-4913-492c-ab29-16488f29f736.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/819-719e754d-4913-492c-ab29-16488f29f736.txn deleted file mode 100644 index e8d233dfa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/819-719e754d-4913-492c-ab29-16488f29f736.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/82-30febd78-2dfa-47cf-8877-eb0ad09bdac2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/82-30febd78-2dfa-47cf-8877-eb0ad09bdac2.txn deleted file mode 100644 index 0151b46cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/82-30febd78-2dfa-47cf-8877-eb0ad09bdac2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/820-e4a8c61c-a30b-4ee0-aed0-6f04f704399a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/820-e4a8c61c-a30b-4ee0-aed0-6f04f704399a.txn deleted file mode 100644 index 6b0607fac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/820-e4a8c61c-a30b-4ee0-aed0-6f04f704399a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/821-0835d2ed-c7ae-49c1-b39b-486ff04e01c5.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/821-0835d2ed-c7ae-49c1-b39b-486ff04e01c5.txn deleted file mode 100644 index ac65758af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/821-0835d2ed-c7ae-49c1-b39b-486ff04e01c5.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/822-72e4c3fa-3ae5-4180-8510-9275edc85530.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/822-72e4c3fa-3ae5-4180-8510-9275edc85530.txn deleted file mode 100644 index 6f48b6a71..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/822-72e4c3fa-3ae5-4180-8510-9275edc85530.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/823-dcc01f71-855a-4a27-83ce-8228c38d838a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/823-dcc01f71-855a-4a27-83ce-8228c38d838a.txn deleted file mode 100644 index 3f6e48068..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/823-dcc01f71-855a-4a27-83ce-8228c38d838a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/824-c629696a-0b86-4069-8160-2116bb2567a7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/824-c629696a-0b86-4069-8160-2116bb2567a7.txn deleted file mode 100644 index e0b7a3cd8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/824-c629696a-0b86-4069-8160-2116bb2567a7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/825-8650604e-80ad-4911-b47b-38a1cebc2b70.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/825-8650604e-80ad-4911-b47b-38a1cebc2b70.txn deleted file mode 100644 index 70c22a796..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/825-8650604e-80ad-4911-b47b-38a1cebc2b70.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/826-67fd31f6-7cf4-4777-855b-d5a9e476efa6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/826-67fd31f6-7cf4-4777-855b-d5a9e476efa6.txn deleted file mode 100644 index fa868af86..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/826-67fd31f6-7cf4-4777-855b-d5a9e476efa6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/827-5eebbde9-3b6f-4974-b9e3-5aa7006759de.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/827-5eebbde9-3b6f-4974-b9e3-5aa7006759de.txn deleted file mode 100644 index 59af1877c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/827-5eebbde9-3b6f-4974-b9e3-5aa7006759de.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/828-d192cfbd-5028-4d9d-a93b-306a3787adbf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/828-d192cfbd-5028-4d9d-a93b-306a3787adbf.txn deleted file mode 100644 index 510e3e68e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/828-d192cfbd-5028-4d9d-a93b-306a3787adbf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/829-f9433cd7-fdf4-4b01-af39-2bace114deca.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/829-f9433cd7-fdf4-4b01-af39-2bace114deca.txn deleted file mode 100644 index 39da9d3a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/829-f9433cd7-fdf4-4b01-af39-2bace114deca.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/83-162260e9-5dcd-4d35-ac0d-21303bd1310b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/83-162260e9-5dcd-4d35-ac0d-21303bd1310b.txn deleted file mode 100644 index 875af847e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/83-162260e9-5dcd-4d35-ac0d-21303bd1310b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/830-bd44f70e-843b-41c0-b60b-5eb6f4bbe77d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/830-bd44f70e-843b-41c0-b60b-5eb6f4bbe77d.txn deleted file mode 100644 index 6c8fbd992..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/830-bd44f70e-843b-41c0-b60b-5eb6f4bbe77d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/831-205bc87e-e7d3-44ee-bd43-98e650289c15.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/831-205bc87e-e7d3-44ee-bd43-98e650289c15.txn deleted file mode 100644 index b16548e4e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/831-205bc87e-e7d3-44ee-bd43-98e650289c15.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/832-71bdf6df-6b47-4294-b940-2dd0ea9b8f00.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/832-71bdf6df-6b47-4294-b940-2dd0ea9b8f00.txn deleted file mode 100644 index d96ebacb9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/832-71bdf6df-6b47-4294-b940-2dd0ea9b8f00.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/833-9f33902e-95a4-43ea-82a5-3bbc7925b0be.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/833-9f33902e-95a4-43ea-82a5-3bbc7925b0be.txn deleted file mode 100644 index ad2ac135e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/833-9f33902e-95a4-43ea-82a5-3bbc7925b0be.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/834-eea5b216-90f2-4dac-aade-6927b1b7c5a7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/834-eea5b216-90f2-4dac-aade-6927b1b7c5a7.txn deleted file mode 100644 index 93c22cb11..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/834-eea5b216-90f2-4dac-aade-6927b1b7c5a7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/835-5ed7a349-f6d9-4075-b061-01270e9f7599.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/835-5ed7a349-f6d9-4075-b061-01270e9f7599.txn deleted file mode 100644 index 3e2987aee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/835-5ed7a349-f6d9-4075-b061-01270e9f7599.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/836-47ab4d20-6a46-4295-8808-1fc655dda451.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/836-47ab4d20-6a46-4295-8808-1fc655dda451.txn deleted file mode 100644 index 5919a15d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/836-47ab4d20-6a46-4295-8808-1fc655dda451.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/837-36d21432-44f6-4491-958d-df681141effd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/837-36d21432-44f6-4491-958d-df681141effd.txn deleted file mode 100644 index d32c8bc2d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/837-36d21432-44f6-4491-958d-df681141effd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/838-c0c4561f-a0e3-4304-80f3-28b5fa72943e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/838-c0c4561f-a0e3-4304-80f3-28b5fa72943e.txn deleted file mode 100644 index f6626e32e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/838-c0c4561f-a0e3-4304-80f3-28b5fa72943e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/839-88d9c901-1f7e-4c6b-93c2-09a2c496e3ba.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/839-88d9c901-1f7e-4c6b-93c2-09a2c496e3ba.txn deleted file mode 100644 index 5d06b1d42..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/839-88d9c901-1f7e-4c6b-93c2-09a2c496e3ba.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/84-804316f8-734b-4d6e-88fc-a81eef3fd277.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/84-804316f8-734b-4d6e-88fc-a81eef3fd277.txn deleted file mode 100644 index 9366cfd35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/84-804316f8-734b-4d6e-88fc-a81eef3fd277.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/840-dad044e3-d7a5-4cb3-bd97-d36acf7d1711.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/840-dad044e3-d7a5-4cb3-bd97-d36acf7d1711.txn deleted file mode 100644 index bd5470e88..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/840-dad044e3-d7a5-4cb3-bd97-d36acf7d1711.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/841-0f3da709-d8ed-4f11-bc19-f5e83d5e0812.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/841-0f3da709-d8ed-4f11-bc19-f5e83d5e0812.txn deleted file mode 100644 index fea965080..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/841-0f3da709-d8ed-4f11-bc19-f5e83d5e0812.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/842-6eded277-29b3-4606-9635-5a27087f5406.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/842-6eded277-29b3-4606-9635-5a27087f5406.txn deleted file mode 100644 index 164d1de66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/842-6eded277-29b3-4606-9635-5a27087f5406.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/843-d18f6f7d-e475-48de-ab58-c8ed72a6e51a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/843-d18f6f7d-e475-48de-ab58-c8ed72a6e51a.txn deleted file mode 100644 index 735d1b6ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/843-d18f6f7d-e475-48de-ab58-c8ed72a6e51a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/844-702337d6-0b13-4729-9f47-7f558cd4ce5d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/844-702337d6-0b13-4729-9f47-7f558cd4ce5d.txn deleted file mode 100644 index e90f6c513..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/844-702337d6-0b13-4729-9f47-7f558cd4ce5d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/845-f07b1a6e-e2f8-4080-80e9-459fddf2b5c0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/845-f07b1a6e-e2f8-4080-80e9-459fddf2b5c0.txn deleted file mode 100644 index a95316a44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/845-f07b1a6e-e2f8-4080-80e9-459fddf2b5c0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/846-fa54a4da-7ec0-45be-94b4-b25ed2a1e293.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/846-fa54a4da-7ec0-45be-94b4-b25ed2a1e293.txn deleted file mode 100644 index 92a1079cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/846-fa54a4da-7ec0-45be-94b4-b25ed2a1e293.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/847-f8de16e3-d707-4431-ad5d-f3d4e9240168.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/847-f8de16e3-d707-4431-ad5d-f3d4e9240168.txn deleted file mode 100644 index b00f43158..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/847-f8de16e3-d707-4431-ad5d-f3d4e9240168.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/848-37e3adb9-a8aa-4438-bf5b-f3a9b607f366.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/848-37e3adb9-a8aa-4438-bf5b-f3a9b607f366.txn deleted file mode 100644 index 151b64145..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/848-37e3adb9-a8aa-4438-bf5b-f3a9b607f366.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/849-811eb91a-deb7-457c-8bd5-d744022ae831.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/849-811eb91a-deb7-457c-8bd5-d744022ae831.txn deleted file mode 100644 index 1bf881cfa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/849-811eb91a-deb7-457c-8bd5-d744022ae831.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/85-351375f3-7e55-4abd-9fb8-81cc850b37d9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/85-351375f3-7e55-4abd-9fb8-81cc850b37d9.txn deleted file mode 100644 index cada212f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/85-351375f3-7e55-4abd-9fb8-81cc850b37d9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/850-744817b7-1d66-4d22-b2e4-d0916b7c7204.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/850-744817b7-1d66-4d22-b2e4-d0916b7c7204.txn deleted file mode 100644 index 69c6153ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/850-744817b7-1d66-4d22-b2e4-d0916b7c7204.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/851-add8350a-3db5-4654-b4dd-61a6c6260b67.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/851-add8350a-3db5-4654-b4dd-61a6c6260b67.txn deleted file mode 100644 index f06b78f9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/851-add8350a-3db5-4654-b4dd-61a6c6260b67.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/852-1d6c816c-9806-4f1f-909b-637499248cb8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/852-1d6c816c-9806-4f1f-909b-637499248cb8.txn deleted file mode 100644 index 130c5cd72..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/852-1d6c816c-9806-4f1f-909b-637499248cb8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/853-0528f76b-2914-45d0-81c4-17c6699b7652.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/853-0528f76b-2914-45d0-81c4-17c6699b7652.txn deleted file mode 100644 index ce08117b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/853-0528f76b-2914-45d0-81c4-17c6699b7652.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/854-2612a5f6-dd92-4fc5-ad3f-6e4e7c887ea6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/854-2612a5f6-dd92-4fc5-ad3f-6e4e7c887ea6.txn deleted file mode 100644 index 33988c0af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/854-2612a5f6-dd92-4fc5-ad3f-6e4e7c887ea6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/855-e0161a27-dc25-43d0-a2ac-7579252c3ed1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/855-e0161a27-dc25-43d0-a2ac-7579252c3ed1.txn deleted file mode 100644 index 78d2fc5c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/855-e0161a27-dc25-43d0-a2ac-7579252c3ed1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/856-6d1cad2b-bc67-4744-8376-c8a6e11d3dd9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/856-6d1cad2b-bc67-4744-8376-c8a6e11d3dd9.txn deleted file mode 100644 index c4d754837..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/856-6d1cad2b-bc67-4744-8376-c8a6e11d3dd9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/857-6bdbbe08-e2a9-47d7-a31d-d71a14a90203.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/857-6bdbbe08-e2a9-47d7-a31d-d71a14a90203.txn deleted file mode 100644 index 7b22ebce4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/857-6bdbbe08-e2a9-47d7-a31d-d71a14a90203.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/858-f2d4091f-9c6a-4d5c-9d21-dea4f145d251.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/858-f2d4091f-9c6a-4d5c-9d21-dea4f145d251.txn deleted file mode 100644 index fa5eaaf05..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/858-f2d4091f-9c6a-4d5c-9d21-dea4f145d251.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/859-0e516b79-8f8c-4e8d-8cf7-bdfec43dae14.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/859-0e516b79-8f8c-4e8d-8cf7-bdfec43dae14.txn deleted file mode 100644 index edb1bde16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/859-0e516b79-8f8c-4e8d-8cf7-bdfec43dae14.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/86-4e654fb8-b241-4124-bcb5-58c2f3f51cae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/86-4e654fb8-b241-4124-bcb5-58c2f3f51cae.txn deleted file mode 100644 index ffb3ab182..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/86-4e654fb8-b241-4124-bcb5-58c2f3f51cae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/860-6d673a5d-06f3-4f18-bf32-2d4d24bebfd3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/860-6d673a5d-06f3-4f18-bf32-2d4d24bebfd3.txn deleted file mode 100644 index f00f9bc42..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/860-6d673a5d-06f3-4f18-bf32-2d4d24bebfd3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/861-e60f6f36-8190-42f8-98a8-274553e59e93.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/861-e60f6f36-8190-42f8-98a8-274553e59e93.txn deleted file mode 100644 index cadb4167f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/861-e60f6f36-8190-42f8-98a8-274553e59e93.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/862-c90d35cd-4f55-43c9-b9ff-e9862a317784.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/862-c90d35cd-4f55-43c9-b9ff-e9862a317784.txn deleted file mode 100644 index 918414e46..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/862-c90d35cd-4f55-43c9-b9ff-e9862a317784.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/863-b3d3f29a-3e08-4c52-94e4-89c527591f3f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/863-b3d3f29a-3e08-4c52-94e4-89c527591f3f.txn deleted file mode 100644 index 4e8403aa7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/863-b3d3f29a-3e08-4c52-94e4-89c527591f3f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/864-6782b722-fffc-47c2-b89a-5cd8aa1a336b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/864-6782b722-fffc-47c2-b89a-5cd8aa1a336b.txn deleted file mode 100644 index 178aa4d3a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/864-6782b722-fffc-47c2-b89a-5cd8aa1a336b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/865-f2162993-b9b0-48b5-b90d-604c76202382.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/865-f2162993-b9b0-48b5-b90d-604c76202382.txn deleted file mode 100644 index 841435c2f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/865-f2162993-b9b0-48b5-b90d-604c76202382.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/866-462b9ad5-7332-4834-acf6-6cec18153dcc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/866-462b9ad5-7332-4834-acf6-6cec18153dcc.txn deleted file mode 100644 index 60f09a8e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/866-462b9ad5-7332-4834-acf6-6cec18153dcc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/867-5f064a51-008a-405e-9ace-e2756633ec10.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/867-5f064a51-008a-405e-9ace-e2756633ec10.txn deleted file mode 100644 index 89973dad3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/867-5f064a51-008a-405e-9ace-e2756633ec10.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/868-01a657c1-439a-4dcf-8f35-281e6fd65fff.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/868-01a657c1-439a-4dcf-8f35-281e6fd65fff.txn deleted file mode 100644 index f21402127..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/868-01a657c1-439a-4dcf-8f35-281e6fd65fff.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/869-27335d10-1727-40a3-925a-994e543fde4a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/869-27335d10-1727-40a3-925a-994e543fde4a.txn deleted file mode 100644 index 38fd7cb8b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/869-27335d10-1727-40a3-925a-994e543fde4a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/87-e3306fb7-ece1-40f5-a941-baef79504f38.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/87-e3306fb7-ece1-40f5-a941-baef79504f38.txn deleted file mode 100644 index 2b4ea77e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/87-e3306fb7-ece1-40f5-a941-baef79504f38.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/870-9c8aeb11-332d-44a0-9134-c87b6c02e07e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/870-9c8aeb11-332d-44a0-9134-c87b6c02e07e.txn deleted file mode 100644 index e15fb88da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/870-9c8aeb11-332d-44a0-9134-c87b6c02e07e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/871-f388f4fb-efb3-46b4-8f4a-5f4cd0ad801d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/871-f388f4fb-efb3-46b4-8f4a-5f4cd0ad801d.txn deleted file mode 100644 index a107fc25f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/871-f388f4fb-efb3-46b4-8f4a-5f4cd0ad801d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/872-ece9fd36-3038-4c18-9dfd-57c2011c746d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/872-ece9fd36-3038-4c18-9dfd-57c2011c746d.txn deleted file mode 100644 index 9eaa8c741..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/872-ece9fd36-3038-4c18-9dfd-57c2011c746d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/873-0d32ca1f-b5ef-440e-8efe-a51d48e1445b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/873-0d32ca1f-b5ef-440e-8efe-a51d48e1445b.txn deleted file mode 100644 index db470b6b2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/873-0d32ca1f-b5ef-440e-8efe-a51d48e1445b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/874-41ff694b-45ba-4caa-b6d7-958f2d27fe1b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/874-41ff694b-45ba-4caa-b6d7-958f2d27fe1b.txn deleted file mode 100644 index 24a92d3be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/874-41ff694b-45ba-4caa-b6d7-958f2d27fe1b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/875-e3f9187d-6550-46a0-8f1b-5a07600ebf3c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/875-e3f9187d-6550-46a0-8f1b-5a07600ebf3c.txn deleted file mode 100644 index b17ebd116..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/875-e3f9187d-6550-46a0-8f1b-5a07600ebf3c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/876-b21c397e-26ce-4027-9b1a-4ca4642cc15a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/876-b21c397e-26ce-4027-9b1a-4ca4642cc15a.txn deleted file mode 100644 index edcc1c3b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/876-b21c397e-26ce-4027-9b1a-4ca4642cc15a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/877-b7cca824-62b2-413e-a1d8-69e6e60f9cad.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/877-b7cca824-62b2-413e-a1d8-69e6e60f9cad.txn deleted file mode 100644 index d14aec712..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/877-b7cca824-62b2-413e-a1d8-69e6e60f9cad.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/878-270454b5-4d5c-430b-9e33-894e6f71fba6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/878-270454b5-4d5c-430b-9e33-894e6f71fba6.txn deleted file mode 100644 index f96259a0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/878-270454b5-4d5c-430b-9e33-894e6f71fba6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/879-57820986-f9d0-44e0-b8a1-b4ac12e1189e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/879-57820986-f9d0-44e0-b8a1-b4ac12e1189e.txn deleted file mode 100644 index 0e0054bcc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/879-57820986-f9d0-44e0-b8a1-b4ac12e1189e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/88-13413d8b-dd29-4921-a0ec-c8d9df368830.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/88-13413d8b-dd29-4921-a0ec-c8d9df368830.txn deleted file mode 100644 index 5f8db19a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/88-13413d8b-dd29-4921-a0ec-c8d9df368830.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/880-fe3a1f40-0c26-4515-977b-c17db26d1569.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/880-fe3a1f40-0c26-4515-977b-c17db26d1569.txn deleted file mode 100644 index 6110940f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/880-fe3a1f40-0c26-4515-977b-c17db26d1569.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/881-4ca10fff-1b3a-4c02-bcd0-588ce88d1d40.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/881-4ca10fff-1b3a-4c02-bcd0-588ce88d1d40.txn deleted file mode 100644 index f26634523..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/881-4ca10fff-1b3a-4c02-bcd0-588ce88d1d40.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/882-30b9bbdb-05dc-42c6-9ac7-93419cf3d701.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/882-30b9bbdb-05dc-42c6-9ac7-93419cf3d701.txn deleted file mode 100644 index 1f448946e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/882-30b9bbdb-05dc-42c6-9ac7-93419cf3d701.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/883-cfcaf217-54ab-41bc-9e19-7cb62b741d1d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/883-cfcaf217-54ab-41bc-9e19-7cb62b741d1d.txn deleted file mode 100644 index 9f4cac82a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/883-cfcaf217-54ab-41bc-9e19-7cb62b741d1d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/884-b8327465-686b-4866-80fb-54728dbffc8a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/884-b8327465-686b-4866-80fb-54728dbffc8a.txn deleted file mode 100644 index 64d250665..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/884-b8327465-686b-4866-80fb-54728dbffc8a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/885-3c3e4bf4-12d5-46bf-898f-880a689f6a3e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/885-3c3e4bf4-12d5-46bf-898f-880a689f6a3e.txn deleted file mode 100644 index 6d5ea4be4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/885-3c3e4bf4-12d5-46bf-898f-880a689f6a3e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/886-1a7addc8-9c73-4ecf-a9c8-d309498bc407.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/886-1a7addc8-9c73-4ecf-a9c8-d309498bc407.txn deleted file mode 100644 index 75382eae8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/886-1a7addc8-9c73-4ecf-a9c8-d309498bc407.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/887-443d6666-ed0b-49f5-b59e-f4711a962d71.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/887-443d6666-ed0b-49f5-b59e-f4711a962d71.txn deleted file mode 100644 index f27e5af3b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/887-443d6666-ed0b-49f5-b59e-f4711a962d71.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/888-eb900e33-5a03-4b8d-a289-41009473efe6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/888-eb900e33-5a03-4b8d-a289-41009473efe6.txn deleted file mode 100644 index 6724b919f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/888-eb900e33-5a03-4b8d-a289-41009473efe6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/889-9b493755-a90a-43d0-a310-0051abd08a65.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/889-9b493755-a90a-43d0-a310-0051abd08a65.txn deleted file mode 100644 index eae2ba0e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/889-9b493755-a90a-43d0-a310-0051abd08a65.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/89-aa3d0c56-e4ce-4c7f-9c98-f9edca877187.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/89-aa3d0c56-e4ce-4c7f-9c98-f9edca877187.txn deleted file mode 100644 index 8a3f8831f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/89-aa3d0c56-e4ce-4c7f-9c98-f9edca877187.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/890-edc2f3c7-d26f-4962-8fac-a85dca69f37c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/890-edc2f3c7-d26f-4962-8fac-a85dca69f37c.txn deleted file mode 100644 index fb498d0c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/890-edc2f3c7-d26f-4962-8fac-a85dca69f37c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/891-2b2e154d-0dc9-43e4-bccd-e28a182e9833.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/891-2b2e154d-0dc9-43e4-bccd-e28a182e9833.txn deleted file mode 100644 index 8eb2355bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/891-2b2e154d-0dc9-43e4-bccd-e28a182e9833.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/892-c988073f-0bca-4ee4-b480-00176046b4f6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/892-c988073f-0bca-4ee4-b480-00176046b4f6.txn deleted file mode 100644 index af1f30f8d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/892-c988073f-0bca-4ee4-b480-00176046b4f6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/893-78898786-45d1-417b-8af9-d1e8a8f33452.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/893-78898786-45d1-417b-8af9-d1e8a8f33452.txn deleted file mode 100644 index e2675ceae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/893-78898786-45d1-417b-8af9-d1e8a8f33452.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/894-64597b33-6649-4244-a952-69900c346a6a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/894-64597b33-6649-4244-a952-69900c346a6a.txn deleted file mode 100644 index 20192f997..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/894-64597b33-6649-4244-a952-69900c346a6a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/895-fd1a29d4-aa81-4506-b556-510473e1f2d2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/895-fd1a29d4-aa81-4506-b556-510473e1f2d2.txn deleted file mode 100644 index 9acd61f27..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/895-fd1a29d4-aa81-4506-b556-510473e1f2d2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/896-0efcaf8a-6c50-4e34-841a-824182ebd78f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/896-0efcaf8a-6c50-4e34-841a-824182ebd78f.txn deleted file mode 100644 index 53520c491..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/896-0efcaf8a-6c50-4e34-841a-824182ebd78f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/897-55879ad5-d3a8-4416-90d8-bd4391bf983f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/897-55879ad5-d3a8-4416-90d8-bd4391bf983f.txn deleted file mode 100644 index 776a3b3d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/897-55879ad5-d3a8-4416-90d8-bd4391bf983f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/898-4c7ddc6c-0c02-4cca-80cb-06126130e297.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/898-4c7ddc6c-0c02-4cca-80cb-06126130e297.txn deleted file mode 100644 index 2b6d8d070..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/898-4c7ddc6c-0c02-4cca-80cb-06126130e297.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/899-8f62d610-3685-467e-bdbd-dfb177a3a39b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/899-8f62d610-3685-467e-bdbd-dfb177a3a39b.txn deleted file mode 100644 index 9062a6244..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/899-8f62d610-3685-467e-bdbd-dfb177a3a39b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/9-772bc45c-d87c-4bf7-b2ab-20aa1c4bd534.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/9-772bc45c-d87c-4bf7-b2ab-20aa1c4bd534.txn deleted file mode 100644 index bcbe59744..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/9-772bc45c-d87c-4bf7-b2ab-20aa1c4bd534.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/90-7cb8dd54-5d7b-4e74-85da-859db4581f82.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/90-7cb8dd54-5d7b-4e74-85da-859db4581f82.txn deleted file mode 100644 index 6ff33a101..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/90-7cb8dd54-5d7b-4e74-85da-859db4581f82.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/900-57b6b41a-32f3-4302-a260-63885df96a9a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/900-57b6b41a-32f3-4302-a260-63885df96a9a.txn deleted file mode 100644 index d83954ac0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/900-57b6b41a-32f3-4302-a260-63885df96a9a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/901-d23a265e-9edd-4dbf-af7c-95aba919b950.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/901-d23a265e-9edd-4dbf-af7c-95aba919b950.txn deleted file mode 100644 index b756b1d17..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/901-d23a265e-9edd-4dbf-af7c-95aba919b950.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/902-bbbe3150-e184-43cc-a8d8-bcb5e9529d3f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/902-bbbe3150-e184-43cc-a8d8-bcb5e9529d3f.txn deleted file mode 100644 index e0dee8e18..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/902-bbbe3150-e184-43cc-a8d8-bcb5e9529d3f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/903-4e291d74-c6a8-4bd2-8aef-9eabfcb00d54.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/903-4e291d74-c6a8-4bd2-8aef-9eabfcb00d54.txn deleted file mode 100644 index e2790ebfc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/903-4e291d74-c6a8-4bd2-8aef-9eabfcb00d54.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/904-2a355047-bc64-4959-af32-20842a5c9ae8.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/904-2a355047-bc64-4959-af32-20842a5c9ae8.txn deleted file mode 100644 index 69a883076..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/904-2a355047-bc64-4959-af32-20842a5c9ae8.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/905-f35890dd-ef34-4762-b713-2a7b1ffa83ff.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/905-f35890dd-ef34-4762-b713-2a7b1ffa83ff.txn deleted file mode 100644 index e5b4b42a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/905-f35890dd-ef34-4762-b713-2a7b1ffa83ff.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/906-7dbec6a2-8cc6-43ba-8797-ac08974c9a09.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/906-7dbec6a2-8cc6-43ba-8797-ac08974c9a09.txn deleted file mode 100644 index 18a22584d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/906-7dbec6a2-8cc6-43ba-8797-ac08974c9a09.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/907-24ce0856-745e-413e-84e5-3aaa5d985359.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/907-24ce0856-745e-413e-84e5-3aaa5d985359.txn deleted file mode 100644 index 567ea80bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/907-24ce0856-745e-413e-84e5-3aaa5d985359.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/908-e3688259-26f6-448e-a720-df6d50f89960.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/908-e3688259-26f6-448e-a720-df6d50f89960.txn deleted file mode 100644 index 3e3ea0de1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/908-e3688259-26f6-448e-a720-df6d50f89960.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/909-a687760f-4d23-41f7-a798-1f0beceb713b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/909-a687760f-4d23-41f7-a798-1f0beceb713b.txn deleted file mode 100644 index b8af5934d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/909-a687760f-4d23-41f7-a798-1f0beceb713b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/91-37672ec8-b33a-4e4f-bca7-ebe9b8387282.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/91-37672ec8-b33a-4e4f-bca7-ebe9b8387282.txn deleted file mode 100644 index 5c2118f3f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/91-37672ec8-b33a-4e4f-bca7-ebe9b8387282.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/910-02518984-f5fc-4220-9966-ed2b686ada4e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/910-02518984-f5fc-4220-9966-ed2b686ada4e.txn deleted file mode 100644 index 2f09f2764..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/910-02518984-f5fc-4220-9966-ed2b686ada4e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/911-2cb03af3-39f7-495d-ba29-39fb707d4edb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/911-2cb03af3-39f7-495d-ba29-39fb707d4edb.txn deleted file mode 100644 index e56e74aa0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/911-2cb03af3-39f7-495d-ba29-39fb707d4edb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/912-f92d8b50-bdd6-444e-895f-134523c6b653.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/912-f92d8b50-bdd6-444e-895f-134523c6b653.txn deleted file mode 100644 index 624fa1ecd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/912-f92d8b50-bdd6-444e-895f-134523c6b653.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/913-988fb634-db4c-42df-a12b-bdb66b566cf1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/913-988fb634-db4c-42df-a12b-bdb66b566cf1.txn deleted file mode 100644 index 93114ded3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/913-988fb634-db4c-42df-a12b-bdb66b566cf1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/914-eaf771aa-1278-4c6a-8066-427a494dce33.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/914-eaf771aa-1278-4c6a-8066-427a494dce33.txn deleted file mode 100644 index acc150a29..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/914-eaf771aa-1278-4c6a-8066-427a494dce33.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/915-3da23233-2725-4457-b1dc-f2dfa330648f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/915-3da23233-2725-4457-b1dc-f2dfa330648f.txn deleted file mode 100644 index aaa2d276b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/915-3da23233-2725-4457-b1dc-f2dfa330648f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/916-9bd0c39e-8e8e-4d75-bf77-4da1feb77ae9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/916-9bd0c39e-8e8e-4d75-bf77-4da1feb77ae9.txn deleted file mode 100644 index 14bf814c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/916-9bd0c39e-8e8e-4d75-bf77-4da1feb77ae9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/917-5f984230-f697-4b4a-9e23-81f95fdeac16.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/917-5f984230-f697-4b4a-9e23-81f95fdeac16.txn deleted file mode 100644 index 55fda8615..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/917-5f984230-f697-4b4a-9e23-81f95fdeac16.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/918-939c9593-fda4-4a13-8621-822eead0a613.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/918-939c9593-fda4-4a13-8621-822eead0a613.txn deleted file mode 100644 index 8d1d31b84..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/918-939c9593-fda4-4a13-8621-822eead0a613.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/919-b4a34710-90f3-46e9-bd84-298bb480e81b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/919-b4a34710-90f3-46e9-bd84-298bb480e81b.txn deleted file mode 100644 index d99fbbbaa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/919-b4a34710-90f3-46e9-bd84-298bb480e81b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/92-0674f31c-cc73-48fe-bcd4-377f26cf082a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/92-0674f31c-cc73-48fe-bcd4-377f26cf082a.txn deleted file mode 100644 index 0e10f3534..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/92-0674f31c-cc73-48fe-bcd4-377f26cf082a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/920-e98b1180-132c-4717-8ebc-1d6ad112b26b.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/920-e98b1180-132c-4717-8ebc-1d6ad112b26b.txn deleted file mode 100644 index c8e0adbf7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/920-e98b1180-132c-4717-8ebc-1d6ad112b26b.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/921-c44886a4-9dde-4d3e-af26-ed15f433c397.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/921-c44886a4-9dde-4d3e-af26-ed15f433c397.txn deleted file mode 100644 index 09bd8f7d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/921-c44886a4-9dde-4d3e-af26-ed15f433c397.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/922-ec905203-39ff-4dbe-88d4-a8340a68692c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/922-ec905203-39ff-4dbe-88d4-a8340a68692c.txn deleted file mode 100644 index fa80d0d70..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/922-ec905203-39ff-4dbe-88d4-a8340a68692c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/923-93090432-d96a-4e02-ae1e-3b7a7f97877a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/923-93090432-d96a-4e02-ae1e-3b7a7f97877a.txn deleted file mode 100644 index 0f018cadd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/923-93090432-d96a-4e02-ae1e-3b7a7f97877a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/924-fa64cb45-364f-4853-8faf-d25146b2d505.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/924-fa64cb45-364f-4853-8faf-d25146b2d505.txn deleted file mode 100644 index 2906e941b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/924-fa64cb45-364f-4853-8faf-d25146b2d505.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/925-f126aac2-a8f1-46e1-bc56-0b6548667a43.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/925-f126aac2-a8f1-46e1-bc56-0b6548667a43.txn deleted file mode 100644 index a679e16b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/925-f126aac2-a8f1-46e1-bc56-0b6548667a43.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/926-783770a6-9829-47d8-b67e-d6bf8e340d58.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/926-783770a6-9829-47d8-b67e-d6bf8e340d58.txn deleted file mode 100644 index 093c7aee5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/926-783770a6-9829-47d8-b67e-d6bf8e340d58.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/927-2173c425-a9e6-49fb-8a11-f5205afc1bde.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/927-2173c425-a9e6-49fb-8a11-f5205afc1bde.txn deleted file mode 100644 index ea5ce977d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/927-2173c425-a9e6-49fb-8a11-f5205afc1bde.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/928-7cc38ae7-ed72-4858-b542-ac3f4f1c525e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/928-7cc38ae7-ed72-4858-b542-ac3f4f1c525e.txn deleted file mode 100644 index c42f1385f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/928-7cc38ae7-ed72-4858-b542-ac3f4f1c525e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/929-9abd61ab-8c30-4c4f-8323-0549f4d5b16e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/929-9abd61ab-8c30-4c4f-8323-0549f4d5b16e.txn deleted file mode 100644 index 2da77552c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/929-9abd61ab-8c30-4c4f-8323-0549f4d5b16e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/93-4f8c96b6-8c0c-4549-9d1d-2ab832ee7107.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/93-4f8c96b6-8c0c-4549-9d1d-2ab832ee7107.txn deleted file mode 100644 index 4d5d39ed1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/93-4f8c96b6-8c0c-4549-9d1d-2ab832ee7107.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/930-b8e65a1d-e13d-4f7c-aea1-6940a4a077dd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/930-b8e65a1d-e13d-4f7c-aea1-6940a4a077dd.txn deleted file mode 100644 index f9911ad46..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/930-b8e65a1d-e13d-4f7c-aea1-6940a4a077dd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/931-f7e4c9ba-66cd-4e20-8a4c-2103fed7b05f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/931-f7e4c9ba-66cd-4e20-8a4c-2103fed7b05f.txn deleted file mode 100644 index c739dff52..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/931-f7e4c9ba-66cd-4e20-8a4c-2103fed7b05f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/932-a360630f-8c07-4846-9973-0a67d44530cf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/932-a360630f-8c07-4846-9973-0a67d44530cf.txn deleted file mode 100644 index 15f56aed1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/932-a360630f-8c07-4846-9973-0a67d44530cf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/933-343d8c38-d372-486b-ad96-862e6eacc5e3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/933-343d8c38-d372-486b-ad96-862e6eacc5e3.txn deleted file mode 100644 index e31e09851..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/933-343d8c38-d372-486b-ad96-862e6eacc5e3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/934-f3699c51-866f-4c3f-8400-f6845446696a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/934-f3699c51-866f-4c3f-8400-f6845446696a.txn deleted file mode 100644 index 44a53fb40..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/934-f3699c51-866f-4c3f-8400-f6845446696a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/935-eec3540a-4a2c-4a50-86a1-70074b87983d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/935-eec3540a-4a2c-4a50-86a1-70074b87983d.txn deleted file mode 100644 index d3690ad36..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/935-eec3540a-4a2c-4a50-86a1-70074b87983d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/936-f23a7558-3002-44d5-bd14-11498184ea70.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/936-f23a7558-3002-44d5-bd14-11498184ea70.txn deleted file mode 100644 index 9b14d3260..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/936-f23a7558-3002-44d5-bd14-11498184ea70.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/937-d2c3106e-4c20-4d01-bd67-167e06e0fe21.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/937-d2c3106e-4c20-4d01-bd67-167e06e0fe21.txn deleted file mode 100644 index e9ce48ad4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/937-d2c3106e-4c20-4d01-bd67-167e06e0fe21.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/938-16bcde73-a0ca-4c6b-be8f-7f866cc948b3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/938-16bcde73-a0ca-4c6b-be8f-7f866cc948b3.txn deleted file mode 100644 index 188806e9d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/938-16bcde73-a0ca-4c6b-be8f-7f866cc948b3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/939-a85047d3-fc39-4fef-bc6d-695d7740c0eb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/939-a85047d3-fc39-4fef-bc6d-695d7740c0eb.txn deleted file mode 100644 index 048547986..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/939-a85047d3-fc39-4fef-bc6d-695d7740c0eb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/94-aba6e7fa-ebae-4c15-b64e-e7c8593691bc.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/94-aba6e7fa-ebae-4c15-b64e-e7c8593691bc.txn deleted file mode 100644 index b944b5194..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/94-aba6e7fa-ebae-4c15-b64e-e7c8593691bc.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/940-d6880bd7-6058-4578-9514-dccd60b9a3b6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/940-d6880bd7-6058-4578-9514-dccd60b9a3b6.txn deleted file mode 100644 index 37a7593fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/940-d6880bd7-6058-4578-9514-dccd60b9a3b6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/941-9ddbf37d-10ea-433c-865f-f7cbeeca944c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/941-9ddbf37d-10ea-433c-865f-f7cbeeca944c.txn deleted file mode 100644 index a3230a9e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/941-9ddbf37d-10ea-433c-865f-f7cbeeca944c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/942-2c45c984-89d3-4d84-a7e1-d3b0b93950f3.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/942-2c45c984-89d3-4d84-a7e1-d3b0b93950f3.txn deleted file mode 100644 index dbd9289fe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/942-2c45c984-89d3-4d84-a7e1-d3b0b93950f3.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/943-242d73ac-01ae-4cc1-b782-325cf24f1f42.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/943-242d73ac-01ae-4cc1-b782-325cf24f1f42.txn deleted file mode 100644 index 098ae1c7b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/943-242d73ac-01ae-4cc1-b782-325cf24f1f42.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/944-41e7325d-53d8-4ccf-a7ac-5c622c27dacf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/944-41e7325d-53d8-4ccf-a7ac-5c622c27dacf.txn deleted file mode 100644 index 727cae43f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/944-41e7325d-53d8-4ccf-a7ac-5c622c27dacf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/945-39c08b3a-2b3f-470c-a2f7-4881282b16eb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/945-39c08b3a-2b3f-470c-a2f7-4881282b16eb.txn deleted file mode 100644 index bd9f00f40..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/945-39c08b3a-2b3f-470c-a2f7-4881282b16eb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/946-4be1c02c-db8f-49c4-8c73-6ff00b1a8e32.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/946-4be1c02c-db8f-49c4-8c73-6ff00b1a8e32.txn deleted file mode 100644 index 4b94dbcc7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/946-4be1c02c-db8f-49c4-8c73-6ff00b1a8e32.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/947-2074b7e2-1a02-42a3-ae90-e7b170868e75.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/947-2074b7e2-1a02-42a3-ae90-e7b170868e75.txn deleted file mode 100644 index 4f7a05b9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/947-2074b7e2-1a02-42a3-ae90-e7b170868e75.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/948-60580d58-29ff-4df0-99fc-fcd78123eab0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/948-60580d58-29ff-4df0-99fc-fcd78123eab0.txn deleted file mode 100644 index a2a6d20e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/948-60580d58-29ff-4df0-99fc-fcd78123eab0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/949-3c8a4b5d-8c06-44fe-96bf-bd28311de341.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/949-3c8a4b5d-8c06-44fe-96bf-bd28311de341.txn deleted file mode 100644 index 52c85be11..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/949-3c8a4b5d-8c06-44fe-96bf-bd28311de341.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/95-96eb44d4-d567-4e79-90b3-df21a3277902.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/95-96eb44d4-d567-4e79-90b3-df21a3277902.txn deleted file mode 100644 index 092ab2ee1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/95-96eb44d4-d567-4e79-90b3-df21a3277902.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/950-0de80b5f-e4e7-44ac-8fbd-c9c7aa342684.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/950-0de80b5f-e4e7-44ac-8fbd-c9c7aa342684.txn deleted file mode 100644 index f2050b3d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/950-0de80b5f-e4e7-44ac-8fbd-c9c7aa342684.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/951-b67964ce-042d-4047-abba-a9eeacb41380.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/951-b67964ce-042d-4047-abba-a9eeacb41380.txn deleted file mode 100644 index 986179e44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/951-b67964ce-042d-4047-abba-a9eeacb41380.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/952-f3b2138f-c1c4-44fd-91a1-af5148fee755.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/952-f3b2138f-c1c4-44fd-91a1-af5148fee755.txn deleted file mode 100644 index b838bde48..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/952-f3b2138f-c1c4-44fd-91a1-af5148fee755.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/953-c5308788-ac3b-409c-ac30-c1c95faa9457.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/953-c5308788-ac3b-409c-ac30-c1c95faa9457.txn deleted file mode 100644 index 1ef612f7a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/953-c5308788-ac3b-409c-ac30-c1c95faa9457.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/954-1d19dc9d-a08e-40b0-b03e-83bc4c7b4bfd.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/954-1d19dc9d-a08e-40b0-b03e-83bc4c7b4bfd.txn deleted file mode 100644 index a6d61868c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/954-1d19dc9d-a08e-40b0-b03e-83bc4c7b4bfd.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/955-85b9ff78-a4fc-447c-b26b-1fb275ccdfb2.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/955-85b9ff78-a4fc-447c-b26b-1fb275ccdfb2.txn deleted file mode 100644 index 6796c7e03..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/955-85b9ff78-a4fc-447c-b26b-1fb275ccdfb2.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/956-15e3ca68-5339-476b-b87b-2bda035128ed.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/956-15e3ca68-5339-476b-b87b-2bda035128ed.txn deleted file mode 100644 index 448fcbcdd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/956-15e3ca68-5339-476b-b87b-2bda035128ed.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/957-bd1f19eb-11e1-4a7a-87db-3e3f372636ad.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/957-bd1f19eb-11e1-4a7a-87db-3e3f372636ad.txn deleted file mode 100644 index 3b012f61e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/957-bd1f19eb-11e1-4a7a-87db-3e3f372636ad.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/958-48c90b4e-0ce7-4539-8723-205e70123c68.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/958-48c90b4e-0ce7-4539-8723-205e70123c68.txn deleted file mode 100644 index 56908084e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/958-48c90b4e-0ce7-4539-8723-205e70123c68.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/959-3064557c-6c0d-4265-98fb-e5c2533f1be7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/959-3064557c-6c0d-4265-98fb-e5c2533f1be7.txn deleted file mode 100644 index 395e88c67..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/959-3064557c-6c0d-4265-98fb-e5c2533f1be7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/96-46cc73d4-2c81-472e-aff8-564a374bcc3e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/96-46cc73d4-2c81-472e-aff8-564a374bcc3e.txn deleted file mode 100644 index 067b50ecf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/96-46cc73d4-2c81-472e-aff8-564a374bcc3e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/960-f4744ae2-2398-4430-8db6-82892f5f9395.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/960-f4744ae2-2398-4430-8db6-82892f5f9395.txn deleted file mode 100644 index 000fac8ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/960-f4744ae2-2398-4430-8db6-82892f5f9395.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/961-4f15ff1e-3530-47d8-9a65-fb5d24b4aaef.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/961-4f15ff1e-3530-47d8-9a65-fb5d24b4aaef.txn deleted file mode 100644 index 5e2fd3cfe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/961-4f15ff1e-3530-47d8-9a65-fb5d24b4aaef.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/962-c68978fd-8190-4c4e-995d-3b6751f4f5e1.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/962-c68978fd-8190-4c4e-995d-3b6751f4f5e1.txn deleted file mode 100644 index eba6e8e81..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/962-c68978fd-8190-4c4e-995d-3b6751f4f5e1.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/963-a986e7f0-fdcd-40e8-82ee-3e1ba4ea5d35.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/963-a986e7f0-fdcd-40e8-82ee-3e1ba4ea5d35.txn deleted file mode 100644 index 1df338c96..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/963-a986e7f0-fdcd-40e8-82ee-3e1ba4ea5d35.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/964-897cad0c-2b81-4785-b428-c3399ac1dfef.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/964-897cad0c-2b81-4785-b428-c3399ac1dfef.txn deleted file mode 100644 index 410e5d6a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/964-897cad0c-2b81-4785-b428-c3399ac1dfef.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/965-31f51df6-bea9-4721-89af-a7033c34a31e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/965-31f51df6-bea9-4721-89af-a7033c34a31e.txn deleted file mode 100644 index 12a1adfc5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/965-31f51df6-bea9-4721-89af-a7033c34a31e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/966-2d9e0995-6135-44cb-8a49-e3df95ed6686.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/966-2d9e0995-6135-44cb-8a49-e3df95ed6686.txn deleted file mode 100644 index 9c8d40ade..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/966-2d9e0995-6135-44cb-8a49-e3df95ed6686.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/967-87fd47d2-7e7d-456b-a5df-9c2bb4265a3c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/967-87fd47d2-7e7d-456b-a5df-9c2bb4265a3c.txn deleted file mode 100644 index 311346fea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/967-87fd47d2-7e7d-456b-a5df-9c2bb4265a3c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/968-81c791a6-a2f5-4297-853f-25654430a27d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/968-81c791a6-a2f5-4297-853f-25654430a27d.txn deleted file mode 100644 index 599636e10..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/968-81c791a6-a2f5-4297-853f-25654430a27d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/969-b5b6ee94-e9ab-4451-ad51-fb641e627edf.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/969-b5b6ee94-e9ab-4451-ad51-fb641e627edf.txn deleted file mode 100644 index e1226577e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/969-b5b6ee94-e9ab-4451-ad51-fb641e627edf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/97-28de973d-2281-4c59-9ff3-ba43242b9a9e.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/97-28de973d-2281-4c59-9ff3-ba43242b9a9e.txn deleted file mode 100644 index 30a8ea653..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/97-28de973d-2281-4c59-9ff3-ba43242b9a9e.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/970-5a3a0c11-cd86-4ec8-9015-9917a7927b2a.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/970-5a3a0c11-cd86-4ec8-9015-9917a7927b2a.txn deleted file mode 100644 index 557d05cbd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/970-5a3a0c11-cd86-4ec8-9015-9917a7927b2a.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/971-5ee4e269-fc7c-44ca-beac-10f973919920.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/971-5ee4e269-fc7c-44ca-beac-10f973919920.txn deleted file mode 100644 index 64e276a96..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/971-5ee4e269-fc7c-44ca-beac-10f973919920.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/972-b270101c-d7b7-4e2c-81cb-9847abc153eb.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/972-b270101c-d7b7-4e2c-81cb-9847abc153eb.txn deleted file mode 100644 index c3dfff48b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/972-b270101c-d7b7-4e2c-81cb-9847abc153eb.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/973-e27c9bfd-bf03-4d0f-89bf-c7bbfb4c005f.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/973-e27c9bfd-bf03-4d0f-89bf-c7bbfb4c005f.txn deleted file mode 100644 index ef5935a75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/973-e27c9bfd-bf03-4d0f-89bf-c7bbfb4c005f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/974-a419bfe6-4e6d-48ce-8f65-1a64816ccc87.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/974-a419bfe6-4e6d-48ce-8f65-1a64816ccc87.txn deleted file mode 100644 index aab246891..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/974-a419bfe6-4e6d-48ce-8f65-1a64816ccc87.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/975-a040ce80-3a5c-4d76-8a35-2265f3330b20.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/975-a040ce80-3a5c-4d76-8a35-2265f3330b20.txn deleted file mode 100644 index 2fe152984..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/975-a040ce80-3a5c-4d76-8a35-2265f3330b20.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/976-584d0c0b-d874-4bff-8e69-55be832b0171.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/976-584d0c0b-d874-4bff-8e69-55be832b0171.txn deleted file mode 100644 index 6b0a00290..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/976-584d0c0b-d874-4bff-8e69-55be832b0171.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/977-72a789e4-aaa0-42eb-85c3-4d1164b9e11d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/977-72a789e4-aaa0-42eb-85c3-4d1164b9e11d.txn deleted file mode 100644 index 12a1b8629..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/977-72a789e4-aaa0-42eb-85c3-4d1164b9e11d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/978-5feee9f6-1b48-4234-b3cd-121e4a38bf88.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/978-5feee9f6-1b48-4234-b3cd-121e4a38bf88.txn deleted file mode 100644 index d19c36d88..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/978-5feee9f6-1b48-4234-b3cd-121e4a38bf88.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/979-67cda94d-3157-4b70-bfc9-21feabd5970c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/979-67cda94d-3157-4b70-bfc9-21feabd5970c.txn deleted file mode 100644 index 6a9ae6eac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/979-67cda94d-3157-4b70-bfc9-21feabd5970c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/98-40d40c8a-ab28-405d-8cc8-ce6361bdb035.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/98-40d40c8a-ab28-405d-8cc8-ce6361bdb035.txn deleted file mode 100644 index 59776481a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/98-40d40c8a-ab28-405d-8cc8-ce6361bdb035.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/980-f65874fb-8c81-4741-b3d6-0632cfc02720.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/980-f65874fb-8c81-4741-b3d6-0632cfc02720.txn deleted file mode 100644 index b65cdd344..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/980-f65874fb-8c81-4741-b3d6-0632cfc02720.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/981-3df8da2e-bb21-459a-906e-7fa11d7c33c4.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/981-3df8da2e-bb21-459a-906e-7fa11d7c33c4.txn deleted file mode 100644 index 4937e94ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/981-3df8da2e-bb21-459a-906e-7fa11d7c33c4.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/982-93426665-c7dc-4b37-a81b-8490828ff2f7.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/982-93426665-c7dc-4b37-a81b-8490828ff2f7.txn deleted file mode 100644 index 1fd22c56c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/982-93426665-c7dc-4b37-a81b-8490828ff2f7.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/983-b2cfd2da-baec-421c-ab4c-f6567c04ffbe.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/983-b2cfd2da-baec-421c-ab4c-f6567c04ffbe.txn deleted file mode 100644 index 93db27e0b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/983-b2cfd2da-baec-421c-ab4c-f6567c04ffbe.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/984-0779b984-7719-41bc-bfba-610b1dc31ed0.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/984-0779b984-7719-41bc-bfba-610b1dc31ed0.txn deleted file mode 100644 index 2203dbd84..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/984-0779b984-7719-41bc-bfba-610b1dc31ed0.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/985-c90c3ce9-9713-4809-8b4e-34c6ed6c6eae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/985-c90c3ce9-9713-4809-8b4e-34c6ed6c6eae.txn deleted file mode 100644 index ec4a90433..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/985-c90c3ce9-9713-4809-8b4e-34c6ed6c6eae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/986-0959b6cc-b439-47d5-beda-acf11744adda.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/986-0959b6cc-b439-47d5-beda-acf11744adda.txn deleted file mode 100644 index 0b309b726..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/986-0959b6cc-b439-47d5-beda-acf11744adda.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/987-7d738ff5-0631-40d5-8faa-9853402e3779.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/987-7d738ff5-0631-40d5-8faa-9853402e3779.txn deleted file mode 100644 index faa86b4e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/987-7d738ff5-0631-40d5-8faa-9853402e3779.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/988-51072ea7-6881-415e-8940-53a2af3a99d6.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/988-51072ea7-6881-415e-8940-53a2af3a99d6.txn deleted file mode 100644 index f81eea2e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/988-51072ea7-6881-415e-8940-53a2af3a99d6.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/989-b40252d4-5a99-41af-901b-de06124c8fae.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/989-b40252d4-5a99-41af-901b-de06124c8fae.txn deleted file mode 100644 index bc566b6d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/989-b40252d4-5a99-41af-901b-de06124c8fae.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/99-933b8e82-c9fe-442b-89a2-6019e99406ea.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/99-933b8e82-c9fe-442b-89a2-6019e99406ea.txn deleted file mode 100644 index d86f4f47a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/99-933b8e82-c9fe-442b-89a2-6019e99406ea.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/990-65621c51-8c65-43ac-ac7b-a76b454afe5c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/990-65621c51-8c65-43ac-ac7b-a76b454afe5c.txn deleted file mode 100644 index 65c7890f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/990-65621c51-8c65-43ac-ac7b-a76b454afe5c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/991-bbd41e1c-a256-4bda-ac15-0d49786400ca.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/991-bbd41e1c-a256-4bda-ac15-0d49786400ca.txn deleted file mode 100644 index 83c991414..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/991-bbd41e1c-a256-4bda-ac15-0d49786400ca.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/992-f42b9bec-63e1-4f98-914c-2afd971484da.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/992-f42b9bec-63e1-4f98-914c-2afd971484da.txn deleted file mode 100644 index 0efa5dbf0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/992-f42b9bec-63e1-4f98-914c-2afd971484da.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/993-ea8d3e4e-59a2-452e-9b47-27bac8a7d49d.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/993-ea8d3e4e-59a2-452e-9b47-27bac8a7d49d.txn deleted file mode 100644 index 40f6a3663..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/993-ea8d3e4e-59a2-452e-9b47-27bac8a7d49d.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/994-693ff788-fe3b-4979-91a3-95831911ca95.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/994-693ff788-fe3b-4979-91a3-95831911ca95.txn deleted file mode 100644 index 0d4060678..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/994-693ff788-fe3b-4979-91a3-95831911ca95.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/995-67977d6c-00fd-4d13-bbb8-25ce9c3c2209.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/995-67977d6c-00fd-4d13-bbb8-25ce9c3c2209.txn deleted file mode 100644 index 97abe536e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/995-67977d6c-00fd-4d13-bbb8-25ce9c3c2209.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/996-c52a7d36-c92c-4184-b5c4-23046e40b842.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/996-c52a7d36-c92c-4184-b5c4-23046e40b842.txn deleted file mode 100644 index 50d76097d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/996-c52a7d36-c92c-4184-b5c4-23046e40b842.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/997-bc7b0240-9c43-444d-a207-4642cf326a4c.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/997-bc7b0240-9c43-444d-a207-4642cf326a4c.txn deleted file mode 100644 index 1512e4edf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/997-bc7b0240-9c43-444d-a207-4642cf326a4c.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/998-124bb9a4-e913-40ee-9cf9-4d10c1d9f7b9.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/998-124bb9a4-e913-40ee-9cf9-4d10c1d9f7b9.txn deleted file mode 100644 index 19aae70b6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/998-124bb9a4-e913-40ee-9cf9-4d10c1d9f7b9.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/999-09361d1c-fbd1-40ce-a503-f8406ff59085.txn b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/999-09361d1c-fbd1-40ce-a503-f8406ff59085.txn deleted file mode 100644 index 63e27574d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_transactions/999-09361d1c-fbd1-40ce-a503-f8406ff59085.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1.manifest deleted file mode 100644 index 4477355c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/10.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/10.manifest deleted file mode 100644 index f6f56cf76..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/10.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/100.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/100.manifest deleted file mode 100644 index 059f9dfec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/100.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1000.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1000.manifest deleted file mode 100644 index 79ff68292..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1000.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1001.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1001.manifest deleted file mode 100644 index f5e49f624..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1001.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1002.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1002.manifest deleted file mode 100644 index 8a38f57f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1002.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1003.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1003.manifest deleted file mode 100644 index c75275283..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1003.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1004.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1004.manifest deleted file mode 100644 index b82f840d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1004.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1005.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1005.manifest deleted file mode 100644 index 08b7cf72d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1005.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1006.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1006.manifest deleted file mode 100644 index c4f311b67..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1006.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1007.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1007.manifest deleted file mode 100644 index 8601b1df3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1007.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1008.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1008.manifest deleted file mode 100644 index 30a6640ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1008.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1009.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1009.manifest deleted file mode 100644 index f1aed0a0f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1009.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/101.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/101.manifest deleted file mode 100644 index a612bc3b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/101.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1010.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1010.manifest deleted file mode 100644 index 7809cf9fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1010.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1011.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1011.manifest deleted file mode 100644 index 7756bab0b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1011.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1012.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1012.manifest deleted file mode 100644 index 7449986a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1012.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1013.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1013.manifest deleted file mode 100644 index 8b6c964f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1013.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1014.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1014.manifest deleted file mode 100644 index bb519f8c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1014.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1015.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1015.manifest deleted file mode 100644 index a9a735477..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1015.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1016.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1016.manifest deleted file mode 100644 index 5747578b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1016.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1017.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1017.manifest deleted file mode 100644 index 98fd0c7eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1017.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1018.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1018.manifest deleted file mode 100644 index 6f79c389c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1018.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1019.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1019.manifest deleted file mode 100644 index 1fe83ebcb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1019.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/102.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/102.manifest deleted file mode 100644 index 153141a4d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/102.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1020.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1020.manifest deleted file mode 100644 index 7b4648976..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1020.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1021.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1021.manifest deleted file mode 100644 index df0c601d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1021.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1022.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1022.manifest deleted file mode 100644 index 0a3c2d079..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1022.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1023.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1023.manifest deleted file mode 100644 index a910b51c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1023.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1024.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1024.manifest deleted file mode 100644 index da7666525..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1024.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1025.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1025.manifest deleted file mode 100644 index 67b305bea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1025.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1026.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1026.manifest deleted file mode 100644 index d5a695ffd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1026.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1027.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1027.manifest deleted file mode 100644 index 8c0ce8953..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1027.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1028.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1028.manifest deleted file mode 100644 index 735b2c585..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1028.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1029.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1029.manifest deleted file mode 100644 index e6b6dfd6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1029.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/103.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/103.manifest deleted file mode 100644 index 8efb11e4b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/103.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1030.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1030.manifest deleted file mode 100644 index deb57e5e0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1030.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1031.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1031.manifest deleted file mode 100644 index 213fd8943..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1031.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1032.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1032.manifest deleted file mode 100644 index 62baf474b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1032.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1033.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1033.manifest deleted file mode 100644 index 110c593b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1033.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1034.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1034.manifest deleted file mode 100644 index e6fbf0ade..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1034.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1035.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1035.manifest deleted file mode 100644 index 4ef11bbe1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1035.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1036.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1036.manifest deleted file mode 100644 index ceb7ba96a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1036.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1037.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1037.manifest deleted file mode 100644 index aaedbebbe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1037.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1038.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1038.manifest deleted file mode 100644 index c201c411e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1038.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1039.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1039.manifest deleted file mode 100644 index fb973a889..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1039.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/104.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/104.manifest deleted file mode 100644 index c40680846..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/104.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1040.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1040.manifest deleted file mode 100644 index 4a5592f82..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1040.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1041.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1041.manifest deleted file mode 100644 index ce475a0d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1041.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1042.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1042.manifest deleted file mode 100644 index 4d35abf06..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1042.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1043.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1043.manifest deleted file mode 100644 index af26671ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1043.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1044.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1044.manifest deleted file mode 100644 index 21dbc8e58..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1044.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1045.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1045.manifest deleted file mode 100644 index 88d8bb7d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1045.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1046.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1046.manifest deleted file mode 100644 index 2fd317c34..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1046.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1047.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1047.manifest deleted file mode 100644 index 9e74fd9ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1047.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1048.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1048.manifest deleted file mode 100644 index 06448f397..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1048.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1049.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1049.manifest deleted file mode 100644 index 2daff1295..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1049.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/105.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/105.manifest deleted file mode 100644 index f85d22754..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/105.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1050.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1050.manifest deleted file mode 100644 index 91503f263..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1050.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1051.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1051.manifest deleted file mode 100644 index 38af95359..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1051.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1052.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1052.manifest deleted file mode 100644 index 3f0a3ec64..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1052.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1053.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1053.manifest deleted file mode 100644 index 8c867f862..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1053.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1054.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1054.manifest deleted file mode 100644 index 9a6430622..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1054.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1055.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1055.manifest deleted file mode 100644 index cc6b43ccd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1055.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1056.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1056.manifest deleted file mode 100644 index 57ac2e073..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1056.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1057.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1057.manifest deleted file mode 100644 index a9619e6fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1057.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1058.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1058.manifest deleted file mode 100644 index b7d2e6fef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1058.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1059.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1059.manifest deleted file mode 100644 index 2083a8a86..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1059.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/106.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/106.manifest deleted file mode 100644 index 16381eb40..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/106.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1060.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1060.manifest deleted file mode 100644 index 50323db01..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1060.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1061.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1061.manifest deleted file mode 100644 index 48472469b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1061.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1062.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1062.manifest deleted file mode 100644 index 32edaab8f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1062.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1063.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1063.manifest deleted file mode 100644 index 862f4260e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1063.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1064.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1064.manifest deleted file mode 100644 index 5cb544d8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1064.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1065.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1065.manifest deleted file mode 100644 index cf75fa82b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1065.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1066.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1066.manifest deleted file mode 100644 index 9f7345936..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1066.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1067.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1067.manifest deleted file mode 100644 index bea6ff10b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1067.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1068.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1068.manifest deleted file mode 100644 index 59aa357ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1068.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1069.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1069.manifest deleted file mode 100644 index 945de838d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1069.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/107.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/107.manifest deleted file mode 100644 index 4ca22bac6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/107.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1070.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1070.manifest deleted file mode 100644 index 327e82b55..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1070.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1071.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1071.manifest deleted file mode 100644 index 7867b10fe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1071.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1072.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1072.manifest deleted file mode 100644 index 58e7738a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1072.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1073.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1073.manifest deleted file mode 100644 index 074192a84..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1073.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1074.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1074.manifest deleted file mode 100644 index 9eba2175f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1074.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1075.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1075.manifest deleted file mode 100644 index 75b91ee7d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1075.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1076.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1076.manifest deleted file mode 100644 index 037dbd479..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1076.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1077.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1077.manifest deleted file mode 100644 index f18be1446..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1077.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1078.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1078.manifest deleted file mode 100644 index 5b6f3e101..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1078.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1079.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1079.manifest deleted file mode 100644 index 0b93b4121..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1079.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/108.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/108.manifest deleted file mode 100644 index efec6f3dd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/108.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1080.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1080.manifest deleted file mode 100644 index 05128dfb3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1080.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1081.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1081.manifest deleted file mode 100644 index 71a304617..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1081.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1082.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1082.manifest deleted file mode 100644 index 38b8394d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1082.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1083.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1083.manifest deleted file mode 100644 index d0049357d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1083.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1084.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1084.manifest deleted file mode 100644 index 49ae710d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1084.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1085.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1085.manifest deleted file mode 100644 index 8f5d0514a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1085.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1086.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1086.manifest deleted file mode 100644 index abf3d50c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1086.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1087.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1087.manifest deleted file mode 100644 index afccf2d1e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1087.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1088.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1088.manifest deleted file mode 100644 index d6a8f22ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1088.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1089.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1089.manifest deleted file mode 100644 index 235f39e0f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1089.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/109.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/109.manifest deleted file mode 100644 index 1fc662f6f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/109.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1090.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1090.manifest deleted file mode 100644 index c15658539..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1090.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1091.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1091.manifest deleted file mode 100644 index 8009727de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1091.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1092.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1092.manifest deleted file mode 100644 index 34b8eb7e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1092.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1093.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1093.manifest deleted file mode 100644 index 4c88e02f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1093.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1094.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1094.manifest deleted file mode 100644 index 153abef20..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1094.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1095.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1095.manifest deleted file mode 100644 index 8db83574f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1095.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1096.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1096.manifest deleted file mode 100644 index ed7b6bf6c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1096.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1097.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1097.manifest deleted file mode 100644 index e4915f153..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1097.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1098.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1098.manifest deleted file mode 100644 index 653c4b60d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1098.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1099.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1099.manifest deleted file mode 100644 index 832cbbf85..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1099.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/11.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/11.manifest deleted file mode 100644 index 8a586ac5a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/11.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/110.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/110.manifest deleted file mode 100644 index 505789dc3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/110.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1100.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1100.manifest deleted file mode 100644 index e2dddc887..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1100.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1101.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1101.manifest deleted file mode 100644 index 520e30dee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1101.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1102.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1102.manifest deleted file mode 100644 index ff8fe474e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1102.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1103.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1103.manifest deleted file mode 100644 index 93e3efee5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1103.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1104.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1104.manifest deleted file mode 100644 index c3db622a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1104.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1105.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1105.manifest deleted file mode 100644 index 4fbd9260f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1105.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1106.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1106.manifest deleted file mode 100644 index 4657df48e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1106.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1107.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1107.manifest deleted file mode 100644 index bff20429f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1107.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1108.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1108.manifest deleted file mode 100644 index 03e2a6318..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1108.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1109.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1109.manifest deleted file mode 100644 index f9a40468e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1109.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/111.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/111.manifest deleted file mode 100644 index ef28dada5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/111.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1110.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1110.manifest deleted file mode 100644 index a86fcffb3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1110.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1111.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1111.manifest deleted file mode 100644 index 60fbfbc61..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1111.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1112.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1112.manifest deleted file mode 100644 index 64cfd3ca4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1112.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1113.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1113.manifest deleted file mode 100644 index 54fdcdc88..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1113.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1114.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1114.manifest deleted file mode 100644 index 5107d03ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1114.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1115.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1115.manifest deleted file mode 100644 index 7ccaf83e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1115.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1116.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1116.manifest deleted file mode 100644 index 238936652..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1116.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1117.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1117.manifest deleted file mode 100644 index 2314f3717..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1117.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1118.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1118.manifest deleted file mode 100644 index c1f07060b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1118.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1119.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1119.manifest deleted file mode 100644 index 218f25331..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1119.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/112.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/112.manifest deleted file mode 100644 index b28d2a527..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/112.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1120.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1120.manifest deleted file mode 100644 index 2659e2955..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1120.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1121.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1121.manifest deleted file mode 100644 index d0e1b0887..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1121.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1122.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1122.manifest deleted file mode 100644 index 5f10352d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1122.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1123.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1123.manifest deleted file mode 100644 index 30af56273..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1123.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1124.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1124.manifest deleted file mode 100644 index 764e3997a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1124.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1125.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1125.manifest deleted file mode 100644 index c048d2b9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1125.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1126.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1126.manifest deleted file mode 100644 index ed05f1bcc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1126.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1127.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1127.manifest deleted file mode 100644 index 1031d1be6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1127.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1128.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1128.manifest deleted file mode 100644 index f3bf84e58..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1128.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1129.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1129.manifest deleted file mode 100644 index b6e1ffcb0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1129.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/113.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/113.manifest deleted file mode 100644 index 2a5c62f5c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/113.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1130.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1130.manifest deleted file mode 100644 index 4f6ed7f3a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1130.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1131.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1131.manifest deleted file mode 100644 index 4cad00739..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1131.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1132.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1132.manifest deleted file mode 100644 index b5a87f4e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1132.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1133.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1133.manifest deleted file mode 100644 index 08b53ae18..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1133.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1134.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1134.manifest deleted file mode 100644 index 68e731f68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1134.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1135.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1135.manifest deleted file mode 100644 index a49616692..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1135.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1136.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1136.manifest deleted file mode 100644 index 24f3c617d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1136.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1137.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1137.manifest deleted file mode 100644 index f331694c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1137.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1138.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1138.manifest deleted file mode 100644 index bb9d0d591..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1138.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1139.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1139.manifest deleted file mode 100644 index a0dd71037..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1139.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/114.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/114.manifest deleted file mode 100644 index 842de9679..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/114.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1140.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1140.manifest deleted file mode 100644 index 60ea06fab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1140.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1141.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1141.manifest deleted file mode 100644 index b41070099..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1141.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1142.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1142.manifest deleted file mode 100644 index a95a1b8c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1142.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1143.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1143.manifest deleted file mode 100644 index 3f4bea32d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1143.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1144.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1144.manifest deleted file mode 100644 index f436a543c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1144.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1145.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1145.manifest deleted file mode 100644 index 3e7b95f6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1145.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1146.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1146.manifest deleted file mode 100644 index 79f85eed1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1146.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1147.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1147.manifest deleted file mode 100644 index f57c37482..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1147.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1148.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1148.manifest deleted file mode 100644 index dc646151c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1148.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1149.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1149.manifest deleted file mode 100644 index 1e4c2f155..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1149.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/115.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/115.manifest deleted file mode 100644 index 1476be2a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/115.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1150.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1150.manifest deleted file mode 100644 index 1fa92af04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1150.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1151.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1151.manifest deleted file mode 100644 index 2f4605c44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1151.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1152.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1152.manifest deleted file mode 100644 index f727cd8ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1152.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1153.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1153.manifest deleted file mode 100644 index b0b54e9f4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1153.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1154.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1154.manifest deleted file mode 100644 index 927b84562..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1154.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1155.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1155.manifest deleted file mode 100644 index ca7586548..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1155.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1156.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1156.manifest deleted file mode 100644 index 028480363..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1156.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1157.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1157.manifest deleted file mode 100644 index d445d61e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1157.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1158.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1158.manifest deleted file mode 100644 index 2ed7cfac8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1158.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1159.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1159.manifest deleted file mode 100644 index 5effaab22..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1159.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/116.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/116.manifest deleted file mode 100644 index 0c5d41a94..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/116.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1160.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1160.manifest deleted file mode 100644 index a11460248..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1160.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1161.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1161.manifest deleted file mode 100644 index 2e9eab566..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1161.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1162.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1162.manifest deleted file mode 100644 index 4e5944df2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1162.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1163.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1163.manifest deleted file mode 100644 index ad1259457..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1163.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1164.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1164.manifest deleted file mode 100644 index 681867311..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1164.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1165.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1165.manifest deleted file mode 100644 index 555852dac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1165.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1166.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1166.manifest deleted file mode 100644 index 2d844fb72..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1166.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1167.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1167.manifest deleted file mode 100644 index c0ca115d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1167.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1168.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1168.manifest deleted file mode 100644 index fac9a0be4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1168.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1169.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1169.manifest deleted file mode 100644 index 0be4b7aec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1169.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/117.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/117.manifest deleted file mode 100644 index 3213bf234..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/117.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1170.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1170.manifest deleted file mode 100644 index 5ba951a41..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1170.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1171.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1171.manifest deleted file mode 100644 index e6a493276..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1171.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1172.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1172.manifest deleted file mode 100644 index 486e59f93..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1172.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1173.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1173.manifest deleted file mode 100644 index 5cd0baec2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1173.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1174.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1174.manifest deleted file mode 100644 index a8ab851ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1174.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1175.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1175.manifest deleted file mode 100644 index 93487e888..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1175.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1176.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1176.manifest deleted file mode 100644 index 13cb77ffe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1176.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1177.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1177.manifest deleted file mode 100644 index 623c21b52..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1177.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1178.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1178.manifest deleted file mode 100644 index f4d7ed019..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1178.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1179.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1179.manifest deleted file mode 100644 index 7f601eead..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1179.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/118.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/118.manifest deleted file mode 100644 index 4b7adc589..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/118.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1180.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1180.manifest deleted file mode 100644 index 48d4ba860..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1180.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1181.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1181.manifest deleted file mode 100644 index feb52f204..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1181.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1182.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1182.manifest deleted file mode 100644 index 59783e887..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1182.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1183.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1183.manifest deleted file mode 100644 index d80b5881b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1183.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1184.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1184.manifest deleted file mode 100644 index f3ac278e0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1184.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1185.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1185.manifest deleted file mode 100644 index afcc69ce5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1185.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1186.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1186.manifest deleted file mode 100644 index 0d1930abb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1186.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1187.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1187.manifest deleted file mode 100644 index b03e05d37..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1187.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1188.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1188.manifest deleted file mode 100644 index 4e661d913..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1188.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1189.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1189.manifest deleted file mode 100644 index 4ce9b181b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1189.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/119.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/119.manifest deleted file mode 100644 index c3f3297f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/119.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1190.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1190.manifest deleted file mode 100644 index 2f956221b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1190.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1191.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1191.manifest deleted file mode 100644 index f167da8fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1191.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1192.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1192.manifest deleted file mode 100644 index 73005f3e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1192.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1193.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1193.manifest deleted file mode 100644 index 2bf73d253..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1193.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1194.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1194.manifest deleted file mode 100644 index c6933d4ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1194.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1195.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1195.manifest deleted file mode 100644 index d725a40a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1195.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1196.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1196.manifest deleted file mode 100644 index 668412e26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1196.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1197.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1197.manifest deleted file mode 100644 index 23c8be793..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1197.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1198.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1198.manifest deleted file mode 100644 index aca2c73c2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1198.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1199.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1199.manifest deleted file mode 100644 index de54b7675..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1199.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/12.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/12.manifest deleted file mode 100644 index 1b869782f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/12.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/120.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/120.manifest deleted file mode 100644 index 875a60a6b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/120.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1200.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1200.manifest deleted file mode 100644 index 3c1b575a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1200.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1201.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1201.manifest deleted file mode 100644 index 93a6ed854..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1201.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1202.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1202.manifest deleted file mode 100644 index 22744df1a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1202.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1203.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1203.manifest deleted file mode 100644 index 7d9f4b152..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1203.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1204.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1204.manifest deleted file mode 100644 index e4799c072..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1204.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1205.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1205.manifest deleted file mode 100644 index 248f5d10f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1205.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1206.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1206.manifest deleted file mode 100644 index abfa677a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1206.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1207.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1207.manifest deleted file mode 100644 index f08a025a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1207.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1208.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1208.manifest deleted file mode 100644 index 5a1c3eeef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1208.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1209.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1209.manifest deleted file mode 100644 index d363385b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1209.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/121.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/121.manifest deleted file mode 100644 index 1f728ae16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/121.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1210.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1210.manifest deleted file mode 100644 index 888868a72..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1210.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1211.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1211.manifest deleted file mode 100644 index 1ec54f7ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1211.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1212.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1212.manifest deleted file mode 100644 index dd382ceb7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1212.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1213.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1213.manifest deleted file mode 100644 index 9a91f0fcb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1213.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1214.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1214.manifest deleted file mode 100644 index a524084c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1214.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1215.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1215.manifest deleted file mode 100644 index 59b8d5934..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1215.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1216.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1216.manifest deleted file mode 100644 index edeb4ea78..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1216.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1217.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1217.manifest deleted file mode 100644 index 403342100..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1217.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1218.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1218.manifest deleted file mode 100644 index 55a0a2b0a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1218.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1219.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1219.manifest deleted file mode 100644 index 9e36f9c0f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1219.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/122.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/122.manifest deleted file mode 100644 index ed7009efc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/122.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1220.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1220.manifest deleted file mode 100644 index 23aec0f71..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1220.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1221.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1221.manifest deleted file mode 100644 index 8b5adf3eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1221.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1222.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1222.manifest deleted file mode 100644 index fc8305f9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1222.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1223.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1223.manifest deleted file mode 100644 index 86dc439f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1223.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1224.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1224.manifest deleted file mode 100644 index bff980058..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1224.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1225.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1225.manifest deleted file mode 100644 index 09700a7c1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1225.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1226.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1226.manifest deleted file mode 100644 index 77e0d1384..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1226.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1227.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1227.manifest deleted file mode 100644 index 9d3ce21dd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1227.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1228.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1228.manifest deleted file mode 100644 index d5e54c34c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1228.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1229.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1229.manifest deleted file mode 100644 index d2f0e5735..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1229.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/123.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/123.manifest deleted file mode 100644 index f91c0c315..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/123.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1230.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1230.manifest deleted file mode 100644 index cbe11ba46..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1230.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1231.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1231.manifest deleted file mode 100644 index 5fccac20a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1231.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1232.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1232.manifest deleted file mode 100644 index 6893f3338..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1232.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1233.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1233.manifest deleted file mode 100644 index a529c9b28..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1233.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1234.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1234.manifest deleted file mode 100644 index 0526658dd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1234.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1235.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1235.manifest deleted file mode 100644 index de3d5d600..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1235.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1236.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1236.manifest deleted file mode 100644 index e162d6a6c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1236.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1237.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1237.manifest deleted file mode 100644 index 1b18bd399..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1237.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1238.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1238.manifest deleted file mode 100644 index ad8baffe1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1238.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1239.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1239.manifest deleted file mode 100644 index 42727d62c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1239.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/124.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/124.manifest deleted file mode 100644 index 696be6b07..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/124.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1240.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1240.manifest deleted file mode 100644 index ab13e6d04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1240.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1241.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1241.manifest deleted file mode 100644 index ae4f83605..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1241.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1242.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1242.manifest deleted file mode 100644 index 2604be705..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1242.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1243.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1243.manifest deleted file mode 100644 index 4691aefb2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1243.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1244.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1244.manifest deleted file mode 100644 index cb1504212..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1244.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1245.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1245.manifest deleted file mode 100644 index 9493a7d19..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1245.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1246.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1246.manifest deleted file mode 100644 index be24b8735..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1246.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1247.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1247.manifest deleted file mode 100644 index e88237faa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1247.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1248.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1248.manifest deleted file mode 100644 index 7d94861d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1248.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1249.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1249.manifest deleted file mode 100644 index ebe4d717d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1249.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/125.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/125.manifest deleted file mode 100644 index a10c63447..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/125.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1250.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1250.manifest deleted file mode 100644 index 354921e17..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1250.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1251.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1251.manifest deleted file mode 100644 index 64eb10ba8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1251.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1252.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1252.manifest deleted file mode 100644 index 6e6d1cac9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1252.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1253.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1253.manifest deleted file mode 100644 index aaa2b3636..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1253.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1254.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1254.manifest deleted file mode 100644 index d4a286b75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1254.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1255.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1255.manifest deleted file mode 100644 index c4cd11afc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1255.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1256.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1256.manifest deleted file mode 100644 index 126a4c2c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1256.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1257.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1257.manifest deleted file mode 100644 index 29cade734..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1257.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1258.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1258.manifest deleted file mode 100644 index e3b2cb32f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1258.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1259.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1259.manifest deleted file mode 100644 index 5b9760af6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1259.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/126.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/126.manifest deleted file mode 100644 index 23c81fe62..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/126.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1260.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1260.manifest deleted file mode 100644 index 72dca62c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1260.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1261.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1261.manifest deleted file mode 100644 index 6ac7155fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1261.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1262.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1262.manifest deleted file mode 100644 index 9880854c2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1262.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1263.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1263.manifest deleted file mode 100644 index e60536c6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1263.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1264.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1264.manifest deleted file mode 100644 index 554b4465b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1264.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1265.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1265.manifest deleted file mode 100644 index 7128b80ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1265.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1266.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1266.manifest deleted file mode 100644 index c52cf7943..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1266.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1267.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1267.manifest deleted file mode 100644 index 8d7ea88a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1267.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1268.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1268.manifest deleted file mode 100644 index 097cf9c04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1268.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1269.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1269.manifest deleted file mode 100644 index d386536ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1269.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/127.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/127.manifest deleted file mode 100644 index d1bd47724..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/127.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1270.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1270.manifest deleted file mode 100644 index d89a5d4a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1270.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1271.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1271.manifest deleted file mode 100644 index dc5e2c8ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1271.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1272.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1272.manifest deleted file mode 100644 index b3df2b835..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1272.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1273.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1273.manifest deleted file mode 100644 index e7e93440d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1273.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1274.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1274.manifest deleted file mode 100644 index 29985c892..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1274.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1275.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1275.manifest deleted file mode 100644 index e7a71f8d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1275.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1276.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1276.manifest deleted file mode 100644 index f4dd75cbc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1276.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1277.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1277.manifest deleted file mode 100644 index 66056c995..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1277.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1278.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1278.manifest deleted file mode 100644 index 002237246..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1278.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1279.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1279.manifest deleted file mode 100644 index 8c719fd64..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1279.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/128.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/128.manifest deleted file mode 100644 index a7a9de9cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/128.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1280.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1280.manifest deleted file mode 100644 index d26dfba0f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1280.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1281.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1281.manifest deleted file mode 100644 index b79119fdf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1281.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1282.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1282.manifest deleted file mode 100644 index a7d8c7d3b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1282.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1283.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1283.manifest deleted file mode 100644 index c7fd8e91d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1283.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1284.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1284.manifest deleted file mode 100644 index 0e609d53b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1284.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1285.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1285.manifest deleted file mode 100644 index e2bda3ed9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1285.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1286.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1286.manifest deleted file mode 100644 index 32e839c99..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1286.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1287.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1287.manifest deleted file mode 100644 index f7b568907..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1287.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1288.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1288.manifest deleted file mode 100644 index 0762228ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1288.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1289.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1289.manifest deleted file mode 100644 index 706ab94d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1289.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/129.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/129.manifest deleted file mode 100644 index c41164a4d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/129.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1290.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1290.manifest deleted file mode 100644 index f970e887a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1290.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1291.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1291.manifest deleted file mode 100644 index b1dc2df0a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1291.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1292.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1292.manifest deleted file mode 100644 index 02d1d9034..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1292.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1293.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1293.manifest deleted file mode 100644 index 99ed3e176..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1293.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1294.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1294.manifest deleted file mode 100644 index f95266f49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1294.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1295.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1295.manifest deleted file mode 100644 index 1adbbd033..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1295.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1296.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1296.manifest deleted file mode 100644 index 8dfa119bd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1296.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1297.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1297.manifest deleted file mode 100644 index 4ee8daccd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1297.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1298.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1298.manifest deleted file mode 100644 index 3e07898a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1298.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1299.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1299.manifest deleted file mode 100644 index 0b9cff530..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1299.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/13.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/13.manifest deleted file mode 100644 index 7f0efbab3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/13.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/130.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/130.manifest deleted file mode 100644 index 0732c8943..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/130.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1300.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1300.manifest deleted file mode 100644 index 27aea8205..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1300.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1301.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1301.manifest deleted file mode 100644 index 25d9e3c30..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1301.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1302.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1302.manifest deleted file mode 100644 index 790dea5a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1302.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1303.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1303.manifest deleted file mode 100644 index b48b99d06..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1303.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1304.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1304.manifest deleted file mode 100644 index 00c7edc0a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1304.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1305.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1305.manifest deleted file mode 100644 index 49572cbe1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1305.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1306.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1306.manifest deleted file mode 100644 index 774192278..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1306.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1307.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1307.manifest deleted file mode 100644 index 48d8c3ebd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1307.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1308.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1308.manifest deleted file mode 100644 index 5d510235b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1308.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1309.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1309.manifest deleted file mode 100644 index 3324f3576..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1309.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/131.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/131.manifest deleted file mode 100644 index 7f9b2d30f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/131.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1310.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1310.manifest deleted file mode 100644 index 0460a28f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1310.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1311.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1311.manifest deleted file mode 100644 index 80ce8163f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1311.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1312.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1312.manifest deleted file mode 100644 index c678dcfaa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1312.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1313.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1313.manifest deleted file mode 100644 index ac21cbcb3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1313.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1314.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1314.manifest deleted file mode 100644 index fec7af036..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1314.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1315.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1315.manifest deleted file mode 100644 index 4eed9a570..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1315.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1316.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1316.manifest deleted file mode 100644 index 6f5b3dba3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1316.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1317.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1317.manifest deleted file mode 100644 index fa6cce448..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1317.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1318.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1318.manifest deleted file mode 100644 index 2fca0e096..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1318.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1319.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1319.manifest deleted file mode 100644 index 1168d6088..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1319.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/132.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/132.manifest deleted file mode 100644 index c994977d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/132.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1320.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1320.manifest deleted file mode 100644 index a385edc21..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1320.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1321.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1321.manifest deleted file mode 100644 index c4485446b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1321.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1322.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1322.manifest deleted file mode 100644 index 91471b0ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1322.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1323.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1323.manifest deleted file mode 100644 index 3144fe941..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1323.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1324.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1324.manifest deleted file mode 100644 index 9f363f490..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1324.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1325.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1325.manifest deleted file mode 100644 index 9b28c8352..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1325.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1326.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1326.manifest deleted file mode 100644 index 34ef64381..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1326.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1327.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1327.manifest deleted file mode 100644 index 58dcaf013..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1327.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1328.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1328.manifest deleted file mode 100644 index 289ec5f71..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1328.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1329.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1329.manifest deleted file mode 100644 index aa0f1e04a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1329.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/133.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/133.manifest deleted file mode 100644 index 5791975da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/133.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1330.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1330.manifest deleted file mode 100644 index d44150aad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1330.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1331.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1331.manifest deleted file mode 100644 index 980464624..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1331.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1332.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1332.manifest deleted file mode 100644 index 4381ce76b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1332.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1333.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1333.manifest deleted file mode 100644 index 2b2b43f17..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1333.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1334.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1334.manifest deleted file mode 100644 index 0fd89293e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1334.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1335.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1335.manifest deleted file mode 100644 index 3a38abc99..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1335.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1336.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1336.manifest deleted file mode 100644 index 9517b99c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1336.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1337.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1337.manifest deleted file mode 100644 index b67cf1f1a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1337.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1338.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1338.manifest deleted file mode 100644 index be3ef56f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1338.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1339.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1339.manifest deleted file mode 100644 index 1bd52da9d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1339.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/134.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/134.manifest deleted file mode 100644 index cb24dfbe4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/134.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1340.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1340.manifest deleted file mode 100644 index 67221f182..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1340.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1341.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1341.manifest deleted file mode 100644 index c66c9c7ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1341.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1342.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1342.manifest deleted file mode 100644 index 6caebc8de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1342.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1343.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1343.manifest deleted file mode 100644 index 650bd71fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1343.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1344.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1344.manifest deleted file mode 100644 index 55e6929ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1344.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1345.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1345.manifest deleted file mode 100644 index d00029fa3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1345.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1346.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1346.manifest deleted file mode 100644 index b4b880929..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1346.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1347.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1347.manifest deleted file mode 100644 index 69d132da3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1347.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1348.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1348.manifest deleted file mode 100644 index 5b7b3a16f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1348.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1349.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1349.manifest deleted file mode 100644 index e7ebcb8e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1349.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/135.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/135.manifest deleted file mode 100644 index 07718b89c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/135.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1350.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1350.manifest deleted file mode 100644 index 236c2c52f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1350.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1351.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1351.manifest deleted file mode 100644 index 3702598e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1351.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1352.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1352.manifest deleted file mode 100644 index a8503a4ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1352.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1353.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1353.manifest deleted file mode 100644 index 6a327c4c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1353.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1354.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1354.manifest deleted file mode 100644 index e1ed8fda8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1354.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1355.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1355.manifest deleted file mode 100644 index 13018ce97..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1355.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1356.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1356.manifest deleted file mode 100644 index b3da802e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1356.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1357.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1357.manifest deleted file mode 100644 index 41238469f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1357.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1358.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1358.manifest deleted file mode 100644 index 20b7fe807..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1358.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1359.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1359.manifest deleted file mode 100644 index 9e90912b0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1359.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/136.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/136.manifest deleted file mode 100644 index 74d4c3a08..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/136.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1360.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1360.manifest deleted file mode 100644 index b7163dbba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1360.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1361.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1361.manifest deleted file mode 100644 index 2ff7e5164..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1361.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1362.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1362.manifest deleted file mode 100644 index 20c1de5cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1362.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1363.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1363.manifest deleted file mode 100644 index 25c4ecbff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1363.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1364.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1364.manifest deleted file mode 100644 index 26c3ba404..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1364.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1365.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1365.manifest deleted file mode 100644 index 634ca1d5c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1365.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1366.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1366.manifest deleted file mode 100644 index b50546be3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1366.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1367.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1367.manifest deleted file mode 100644 index 05ca6482c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1367.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1368.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1368.manifest deleted file mode 100644 index b32772164..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1368.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1369.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1369.manifest deleted file mode 100644 index daaf1ec92..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1369.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/137.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/137.manifest deleted file mode 100644 index 1704990a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/137.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1370.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1370.manifest deleted file mode 100644 index 89be0db44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1370.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1371.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1371.manifest deleted file mode 100644 index e80735abf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1371.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1372.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1372.manifest deleted file mode 100644 index 81d13a5c3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1372.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1373.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1373.manifest deleted file mode 100644 index 4d98e9e8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1373.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1374.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1374.manifest deleted file mode 100644 index adde3e23a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1374.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1375.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1375.manifest deleted file mode 100644 index 9aab55e88..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1375.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1376.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1376.manifest deleted file mode 100644 index 27e108239..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1376.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1377.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1377.manifest deleted file mode 100644 index cbcd6940a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1377.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1378.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1378.manifest deleted file mode 100644 index 8c7377c01..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1378.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1379.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1379.manifest deleted file mode 100644 index 9f23fb8ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1379.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/138.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/138.manifest deleted file mode 100644 index afeb2214f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/138.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1380.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1380.manifest deleted file mode 100644 index d294f9c94..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1380.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1381.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1381.manifest deleted file mode 100644 index 48f269a42..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1381.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1382.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1382.manifest deleted file mode 100644 index 70990ad26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1382.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1383.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1383.manifest deleted file mode 100644 index ff4a9956e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1383.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1384.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1384.manifest deleted file mode 100644 index 754d3eb78..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1384.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1385.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1385.manifest deleted file mode 100644 index 8eb06f6de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1385.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1386.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1386.manifest deleted file mode 100644 index 049de13dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1386.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1387.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1387.manifest deleted file mode 100644 index a067e5df6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1387.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1388.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1388.manifest deleted file mode 100644 index ac4bb2d47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1388.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1389.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1389.manifest deleted file mode 100644 index 365eab0f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1389.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/139.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/139.manifest deleted file mode 100644 index 1909876c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/139.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1390.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1390.manifest deleted file mode 100644 index 8ff084959..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1390.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1391.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1391.manifest deleted file mode 100644 index 1d5a0a76e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1391.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1392.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1392.manifest deleted file mode 100644 index 983fc1d70..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1392.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1393.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1393.manifest deleted file mode 100644 index cfb47a862..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1393.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1394.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1394.manifest deleted file mode 100644 index 0a15c2424..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1394.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1395.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1395.manifest deleted file mode 100644 index b7205aa74..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1395.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1396.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1396.manifest deleted file mode 100644 index ff27209c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1396.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1397.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1397.manifest deleted file mode 100644 index 7efdaf5f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1397.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1398.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1398.manifest deleted file mode 100644 index c3c26b6df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1398.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1399.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1399.manifest deleted file mode 100644 index ab8ce2d7a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1399.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/14.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/14.manifest deleted file mode 100644 index 97aafee0b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/14.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/140.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/140.manifest deleted file mode 100644 index cbd276c23..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/140.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1400.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1400.manifest deleted file mode 100644 index b54b0891c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1400.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1401.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1401.manifest deleted file mode 100644 index a49b4a608..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1401.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1402.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1402.manifest deleted file mode 100644 index 2c59aa08e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1402.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1403.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1403.manifest deleted file mode 100644 index 93380d783..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1403.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1404.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1404.manifest deleted file mode 100644 index 6dca06a42..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1404.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1405.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1405.manifest deleted file mode 100644 index a24662d00..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1405.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1406.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1406.manifest deleted file mode 100644 index 7e7b0195a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1406.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1407.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1407.manifest deleted file mode 100644 index 70fcd820d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1407.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1408.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1408.manifest deleted file mode 100644 index 07fedb389..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1408.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1409.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1409.manifest deleted file mode 100644 index 5182c61eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1409.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/141.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/141.manifest deleted file mode 100644 index a9e49df9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/141.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1410.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1410.manifest deleted file mode 100644 index 574116be7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1410.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1411.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1411.manifest deleted file mode 100644 index d6c08878b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1411.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1412.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1412.manifest deleted file mode 100644 index cdd147c78..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1412.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1413.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1413.manifest deleted file mode 100644 index 5eee520e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1413.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1414.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1414.manifest deleted file mode 100644 index 0d24109fe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1414.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1415.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1415.manifest deleted file mode 100644 index d393346d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1415.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1416.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1416.manifest deleted file mode 100644 index 59d818183..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1416.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1417.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1417.manifest deleted file mode 100644 index de3b6b124..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1417.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1418.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1418.manifest deleted file mode 100644 index d6732c0bf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1418.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1419.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1419.manifest deleted file mode 100644 index 6f8ff9a4a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1419.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/142.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/142.manifest deleted file mode 100644 index 2ef8a5c14..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/142.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1420.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1420.manifest deleted file mode 100644 index 5bae8b611..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1420.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1421.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1421.manifest deleted file mode 100644 index 0ee6ef1c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1421.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1422.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1422.manifest deleted file mode 100644 index eda543a3b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1422.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1423.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1423.manifest deleted file mode 100644 index 2ed28c37f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1423.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1424.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1424.manifest deleted file mode 100644 index 900e5c9f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1424.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1425.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1425.manifest deleted file mode 100644 index d4ad86976..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1425.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1426.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1426.manifest deleted file mode 100644 index 8227211c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1426.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1427.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1427.manifest deleted file mode 100644 index 101f35001..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1427.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1428.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1428.manifest deleted file mode 100644 index 93d54ecfb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1428.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1429.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1429.manifest deleted file mode 100644 index fe1762a5c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1429.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/143.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/143.manifest deleted file mode 100644 index 1f607316e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/143.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1430.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1430.manifest deleted file mode 100644 index 27de66bd5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1430.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1431.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1431.manifest deleted file mode 100644 index 57b283c68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1431.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1432.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1432.manifest deleted file mode 100644 index ec730613d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1432.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1433.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1433.manifest deleted file mode 100644 index 89eba9cac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1433.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1434.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1434.manifest deleted file mode 100644 index db8d4bbae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1434.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1435.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1435.manifest deleted file mode 100644 index 71a53733e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1435.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1436.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1436.manifest deleted file mode 100644 index 550f5385e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1436.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1437.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1437.manifest deleted file mode 100644 index 1c7a77d4e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1437.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1438.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1438.manifest deleted file mode 100644 index b7acee91e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1438.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1439.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1439.manifest deleted file mode 100644 index 520cbda92..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1439.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/144.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/144.manifest deleted file mode 100644 index 0c79ba57a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/144.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1440.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1440.manifest deleted file mode 100644 index c605fb459..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1440.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1441.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1441.manifest deleted file mode 100644 index 38c1f8e8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1441.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1442.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1442.manifest deleted file mode 100644 index a73fa92f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1442.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1443.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1443.manifest deleted file mode 100644 index 68bfa8191..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1443.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1444.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1444.manifest deleted file mode 100644 index 4a6546c21..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1444.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1445.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1445.manifest deleted file mode 100644 index d2ddab561..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1445.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1446.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1446.manifest deleted file mode 100644 index aa63470e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1446.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1447.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1447.manifest deleted file mode 100644 index 029012176..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1447.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1448.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1448.manifest deleted file mode 100644 index 7a5100b25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1448.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1449.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1449.manifest deleted file mode 100644 index 083e7ea3f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1449.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/145.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/145.manifest deleted file mode 100644 index fb4af90ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/145.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1450.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1450.manifest deleted file mode 100644 index aea6c948e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1450.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1451.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1451.manifest deleted file mode 100644 index 10d4b029b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1451.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1452.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1452.manifest deleted file mode 100644 index 4e79cdcde..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1452.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1453.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1453.manifest deleted file mode 100644 index 155c1cf16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1453.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1454.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1454.manifest deleted file mode 100644 index 0e247d004..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1454.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1455.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1455.manifest deleted file mode 100644 index be3e6209e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1455.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1456.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1456.manifest deleted file mode 100644 index 0e0fd58ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1456.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1457.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1457.manifest deleted file mode 100644 index 6822d36aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1457.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1458.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1458.manifest deleted file mode 100644 index b77d696d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1458.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1459.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1459.manifest deleted file mode 100644 index 18d238b8f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1459.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/146.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/146.manifest deleted file mode 100644 index 35193578a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/146.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1460.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1460.manifest deleted file mode 100644 index ab9487496..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1460.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1461.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1461.manifest deleted file mode 100644 index 79f15df25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1461.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1462.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1462.manifest deleted file mode 100644 index 589a6ffaa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1462.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1463.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1463.manifest deleted file mode 100644 index 73854d9bf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1463.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1464.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1464.manifest deleted file mode 100644 index e58d4ba17..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1464.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1465.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1465.manifest deleted file mode 100644 index debc7f95b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1465.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1466.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1466.manifest deleted file mode 100644 index 03d143055..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1466.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1467.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1467.manifest deleted file mode 100644 index 068b54056..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1467.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1468.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1468.manifest deleted file mode 100644 index f5f62e535..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1468.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1469.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1469.manifest deleted file mode 100644 index cd821159e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1469.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/147.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/147.manifest deleted file mode 100644 index 35772d095..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/147.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1470.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1470.manifest deleted file mode 100644 index 25bd26426..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1470.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1471.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1471.manifest deleted file mode 100644 index 68352adf2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1471.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1472.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1472.manifest deleted file mode 100644 index feec72b74..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1472.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1473.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1473.manifest deleted file mode 100644 index 1a7b634e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1473.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1474.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1474.manifest deleted file mode 100644 index 754a9780b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1474.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1475.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1475.manifest deleted file mode 100644 index 16fb1d923..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1475.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1476.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1476.manifest deleted file mode 100644 index fa4ed2ee5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1476.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1477.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1477.manifest deleted file mode 100644 index ece58c945..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1477.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1478.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1478.manifest deleted file mode 100644 index 8858f08c1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1478.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1479.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1479.manifest deleted file mode 100644 index 98003b16b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1479.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/148.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/148.manifest deleted file mode 100644 index 4b3dcdbc3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/148.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1480.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1480.manifest deleted file mode 100644 index 0c5414b6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1480.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1481.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1481.manifest deleted file mode 100644 index 17bbcbb73..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1481.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1482.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1482.manifest deleted file mode 100644 index e4ad8a4bf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1482.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1483.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1483.manifest deleted file mode 100644 index ed045d9de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1483.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1484.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1484.manifest deleted file mode 100644 index 91e356132..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1484.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1485.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1485.manifest deleted file mode 100644 index ec2b28d02..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1485.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1486.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1486.manifest deleted file mode 100644 index 8c48684c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1486.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1487.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1487.manifest deleted file mode 100644 index 26cb1071e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1487.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1488.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1488.manifest deleted file mode 100644 index 888422e34..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1488.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1489.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1489.manifest deleted file mode 100644 index f0c293c04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1489.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/149.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/149.manifest deleted file mode 100644 index d610691d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/149.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1490.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1490.manifest deleted file mode 100644 index e59ef6068..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1490.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1491.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1491.manifest deleted file mode 100644 index f6f85218f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1491.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1492.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1492.manifest deleted file mode 100644 index ff5832583..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1492.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1493.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1493.manifest deleted file mode 100644 index 5c778e60c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1493.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1494.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1494.manifest deleted file mode 100644 index 6ccfe8e0c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1494.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1495.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1495.manifest deleted file mode 100644 index dec208be2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1495.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1496.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1496.manifest deleted file mode 100644 index 84046f1d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1496.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1497.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1497.manifest deleted file mode 100644 index e83462265..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1497.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1498.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1498.manifest deleted file mode 100644 index 5ced77f27..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1498.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1499.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1499.manifest deleted file mode 100644 index 4e5df8325..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1499.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/15.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/15.manifest deleted file mode 100644 index 2f76f93f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/15.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/150.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/150.manifest deleted file mode 100644 index 672e2d8cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/150.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1500.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1500.manifest deleted file mode 100644 index 9c97c0791..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1500.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1501.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1501.manifest deleted file mode 100644 index 1228a6bfd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1501.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1502.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1502.manifest deleted file mode 100644 index ae75f187a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1502.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1503.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1503.manifest deleted file mode 100644 index 9a58855de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1503.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1504.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1504.manifest deleted file mode 100644 index 77f851614..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1504.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1505.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1505.manifest deleted file mode 100644 index 4905a3dc6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1505.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1506.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1506.manifest deleted file mode 100644 index dd4446a9d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1506.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1507.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1507.manifest deleted file mode 100644 index 1012bf196..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1507.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1508.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1508.manifest deleted file mode 100644 index 50cee9ca2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1508.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1509.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1509.manifest deleted file mode 100644 index 659a357a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1509.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/151.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/151.manifest deleted file mode 100644 index f160c6d43..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/151.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1510.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1510.manifest deleted file mode 100644 index 968683b89..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1510.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1511.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1511.manifest deleted file mode 100644 index 5def97adb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1511.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1512.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1512.manifest deleted file mode 100644 index 79263550f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1512.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1513.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1513.manifest deleted file mode 100644 index b348b7a6e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1513.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1514.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1514.manifest deleted file mode 100644 index 0b234eb23..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1514.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1515.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1515.manifest deleted file mode 100644 index 6ea6c57c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1515.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1516.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1516.manifest deleted file mode 100644 index f6cf75c8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1516.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1517.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1517.manifest deleted file mode 100644 index 270fb99a0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1517.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1518.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1518.manifest deleted file mode 100644 index c40af391d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1518.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1519.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1519.manifest deleted file mode 100644 index 7170d3b27..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1519.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/152.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/152.manifest deleted file mode 100644 index 9f53c427c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/152.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1520.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1520.manifest deleted file mode 100644 index cb728a5c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1520.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1521.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1521.manifest deleted file mode 100644 index 0f22965c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1521.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1522.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1522.manifest deleted file mode 100644 index 6155382b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1522.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1523.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1523.manifest deleted file mode 100644 index db94668bf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1523.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1524.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1524.manifest deleted file mode 100644 index bee1ac298..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1524.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1525.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1525.manifest deleted file mode 100644 index 03188d158..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1525.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1526.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1526.manifest deleted file mode 100644 index 3c43fb109..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1526.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1527.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1527.manifest deleted file mode 100644 index 10a911027..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1527.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1528.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1528.manifest deleted file mode 100644 index a211c3ffe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1528.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1529.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1529.manifest deleted file mode 100644 index da9145e67..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1529.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/153.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/153.manifest deleted file mode 100644 index aef441ffd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/153.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1530.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1530.manifest deleted file mode 100644 index 88a9ffe25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1530.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1531.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1531.manifest deleted file mode 100644 index 89249becb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1531.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1532.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1532.manifest deleted file mode 100644 index 17d3fda22..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1532.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1533.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1533.manifest deleted file mode 100644 index 83eabbb9c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1533.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1534.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1534.manifest deleted file mode 100644 index cf1f56de1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1534.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1535.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1535.manifest deleted file mode 100644 index 9f2cc9560..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1535.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1536.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1536.manifest deleted file mode 100644 index cdf9b6e0b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1536.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1537.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1537.manifest deleted file mode 100644 index c432b8f43..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1537.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1538.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1538.manifest deleted file mode 100644 index 51dee070e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1538.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1539.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1539.manifest deleted file mode 100644 index 15f07da66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1539.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/154.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/154.manifest deleted file mode 100644 index 9fef79881..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/154.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1540.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1540.manifest deleted file mode 100644 index ccc5aa376..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1540.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1541.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1541.manifest deleted file mode 100644 index 79fc4209e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1541.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1542.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1542.manifest deleted file mode 100644 index c61856efa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1542.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1543.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1543.manifest deleted file mode 100644 index df5ab0a9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1543.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1544.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1544.manifest deleted file mode 100644 index 7e5242301..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1544.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1545.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1545.manifest deleted file mode 100644 index 2f38ce1ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1545.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1546.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1546.manifest deleted file mode 100644 index d235c5d32..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1546.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1547.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1547.manifest deleted file mode 100644 index 003639d03..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1547.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1548.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1548.manifest deleted file mode 100644 index 2288e6f27..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1548.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1549.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1549.manifest deleted file mode 100644 index 8fefd6142..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1549.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/155.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/155.manifest deleted file mode 100644 index bc896bd7c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/155.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1550.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1550.manifest deleted file mode 100644 index 83ebcb034..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1550.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1551.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1551.manifest deleted file mode 100644 index faa30a27e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1551.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1552.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1552.manifest deleted file mode 100644 index 5cc617cf3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1552.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1553.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1553.manifest deleted file mode 100644 index b32540418..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1553.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1554.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1554.manifest deleted file mode 100644 index 98ba81173..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1554.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1555.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1555.manifest deleted file mode 100644 index 2a97a787d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1555.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1556.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1556.manifest deleted file mode 100644 index 68ccb2d61..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1556.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1557.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1557.manifest deleted file mode 100644 index 9d81000fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1557.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1558.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1558.manifest deleted file mode 100644 index e068f87e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1558.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1559.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1559.manifest deleted file mode 100644 index d94fe50e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1559.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/156.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/156.manifest deleted file mode 100644 index 9e30fbdb7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/156.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1560.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1560.manifest deleted file mode 100644 index 586bd1b9a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1560.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1561.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1561.manifest deleted file mode 100644 index 64bc93fd4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1561.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1562.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1562.manifest deleted file mode 100644 index 3378832ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1562.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1563.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1563.manifest deleted file mode 100644 index e8502fccc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1563.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1564.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1564.manifest deleted file mode 100644 index fb275dbf2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1564.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1565.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1565.manifest deleted file mode 100644 index 45a9a06df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1565.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1566.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1566.manifest deleted file mode 100644 index 29bfd3aa2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1566.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1567.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1567.manifest deleted file mode 100644 index 37f3e9a7e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1567.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1568.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1568.manifest deleted file mode 100644 index f0ea44b68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1568.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1569.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1569.manifest deleted file mode 100644 index 42d09125b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1569.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/157.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/157.manifest deleted file mode 100644 index 808fbfea1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/157.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1570.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1570.manifest deleted file mode 100644 index 5d7d7a44b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1570.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1571.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1571.manifest deleted file mode 100644 index d19fde9ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1571.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1572.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1572.manifest deleted file mode 100644 index 906006f45..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1572.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1573.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1573.manifest deleted file mode 100644 index a10b295d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1573.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1574.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1574.manifest deleted file mode 100644 index 5b3ed4de6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1574.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1575.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1575.manifest deleted file mode 100644 index 3cceea852..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1575.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1576.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1576.manifest deleted file mode 100644 index 8227e9d3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1576.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1577.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1577.manifest deleted file mode 100644 index 62a884ea8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1577.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1578.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1578.manifest deleted file mode 100644 index cc9344442..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1578.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1579.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1579.manifest deleted file mode 100644 index adb4f48ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1579.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/158.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/158.manifest deleted file mode 100644 index bbd7e059e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/158.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1580.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1580.manifest deleted file mode 100644 index 14c8fce15..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1580.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1581.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1581.manifest deleted file mode 100644 index 203018003..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1581.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1582.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1582.manifest deleted file mode 100644 index 455cc4adb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1582.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1583.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1583.manifest deleted file mode 100644 index 292bb71ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1583.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1584.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1584.manifest deleted file mode 100644 index 74af4a218..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1584.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1585.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1585.manifest deleted file mode 100644 index d503f1484..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1585.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1586.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1586.manifest deleted file mode 100644 index 5a0a13032..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1586.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1587.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1587.manifest deleted file mode 100644 index 906265ee2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1587.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1588.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1588.manifest deleted file mode 100644 index 45116bc89..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1588.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1589.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1589.manifest deleted file mode 100644 index e88350fc0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1589.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/159.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/159.manifest deleted file mode 100644 index d11aa0e74..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/159.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1590.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1590.manifest deleted file mode 100644 index f3aac9f65..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1590.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1591.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1591.manifest deleted file mode 100644 index d3cf69ec2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1591.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1592.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1592.manifest deleted file mode 100644 index aff71f197..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1592.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1593.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1593.manifest deleted file mode 100644 index 64e971446..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1593.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1594.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1594.manifest deleted file mode 100644 index dfca77cee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1594.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1595.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1595.manifest deleted file mode 100644 index 51258953c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1595.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1596.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1596.manifest deleted file mode 100644 index 62c82010a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1596.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1597.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1597.manifest deleted file mode 100644 index 14df1a1a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1597.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1598.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1598.manifest deleted file mode 100644 index 3dd200698..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1598.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1599.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1599.manifest deleted file mode 100644 index be4e8ac0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1599.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/16.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/16.manifest deleted file mode 100644 index 8bc1b2d80..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/16.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/160.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/160.manifest deleted file mode 100644 index a097a129c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/160.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1600.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1600.manifest deleted file mode 100644 index c9d8af8cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1600.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1601.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1601.manifest deleted file mode 100644 index 39f7da160..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1601.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1602.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1602.manifest deleted file mode 100644 index 25bee4cff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1602.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1603.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1603.manifest deleted file mode 100644 index 3c4d791ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1603.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1604.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1604.manifest deleted file mode 100644 index e2b9b257c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1604.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1605.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1605.manifest deleted file mode 100644 index ffc9cea0c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1605.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1606.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1606.manifest deleted file mode 100644 index 82a7c5b16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1606.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1607.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1607.manifest deleted file mode 100644 index 5753a2087..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1607.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1608.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1608.manifest deleted file mode 100644 index b05bf8ebb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1608.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1609.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1609.manifest deleted file mode 100644 index e15428cf6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1609.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/161.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/161.manifest deleted file mode 100644 index 825433674..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/161.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1610.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1610.manifest deleted file mode 100644 index 76f954913..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1610.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1611.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1611.manifest deleted file mode 100644 index a55b96167..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1611.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1612.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1612.manifest deleted file mode 100644 index d3ad84601..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1612.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1613.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1613.manifest deleted file mode 100644 index a3d6b2162..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1613.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1614.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1614.manifest deleted file mode 100644 index ea334eeba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1614.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1615.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1615.manifest deleted file mode 100644 index 2aeba1988..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1615.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1616.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1616.manifest deleted file mode 100644 index 2c0a22a94..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1616.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1617.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1617.manifest deleted file mode 100644 index 850cf2e14..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1617.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1618.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1618.manifest deleted file mode 100644 index 253d29207..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1618.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1619.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1619.manifest deleted file mode 100644 index 0e8396b89..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1619.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/162.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/162.manifest deleted file mode 100644 index c98cd9089..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/162.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1620.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1620.manifest deleted file mode 100644 index 8f9773293..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1620.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1621.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1621.manifest deleted file mode 100644 index 337d924f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1621.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1622.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1622.manifest deleted file mode 100644 index 6380646b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1622.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1623.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1623.manifest deleted file mode 100644 index 2954f9209..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1623.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1624.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1624.manifest deleted file mode 100644 index 6d946b453..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1624.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1625.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1625.manifest deleted file mode 100644 index eff57646a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1625.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1626.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1626.manifest deleted file mode 100644 index ed88086fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1626.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1627.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1627.manifest deleted file mode 100644 index 20c0bafff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1627.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1628.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1628.manifest deleted file mode 100644 index 8c7d9bba9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1628.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1629.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1629.manifest deleted file mode 100644 index b7fe280c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1629.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/163.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/163.manifest deleted file mode 100644 index c1e669b74..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/163.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1630.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1630.manifest deleted file mode 100644 index 50895d781..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1630.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1631.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1631.manifest deleted file mode 100644 index 4610d86dd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1631.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1632.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1632.manifest deleted file mode 100644 index 1bbec78d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1632.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1633.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1633.manifest deleted file mode 100644 index 5a43cc304..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1633.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1634.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1634.manifest deleted file mode 100644 index 23cbffe47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1634.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1635.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1635.manifest deleted file mode 100644 index a5e0b8ce9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1635.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1636.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1636.manifest deleted file mode 100644 index 5f141592e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1636.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1637.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1637.manifest deleted file mode 100644 index ce44c8119..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1637.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1638.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1638.manifest deleted file mode 100644 index 7ca1732dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1638.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1639.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1639.manifest deleted file mode 100644 index 689cbe565..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1639.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/164.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/164.manifest deleted file mode 100644 index 4c8f48b25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/164.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1640.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1640.manifest deleted file mode 100644 index f016c1d15..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1640.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1641.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1641.manifest deleted file mode 100644 index ad68a5026..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1641.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1642.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1642.manifest deleted file mode 100644 index 4ba7f98c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1642.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1643.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1643.manifest deleted file mode 100644 index f16488d26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1643.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1644.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1644.manifest deleted file mode 100644 index 83f52f5a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1644.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1645.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1645.manifest deleted file mode 100644 index ed1108454..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1645.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1646.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1646.manifest deleted file mode 100644 index 6d4d6c6d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1646.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1647.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1647.manifest deleted file mode 100644 index 4e628f677..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1647.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1648.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1648.manifest deleted file mode 100644 index ea115e413..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1648.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1649.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1649.manifest deleted file mode 100644 index 9c3c5accf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1649.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/165.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/165.manifest deleted file mode 100644 index b820789c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/165.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1650.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1650.manifest deleted file mode 100644 index 58d5a37e0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1650.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1651.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1651.manifest deleted file mode 100644 index 0e62ed5cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1651.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1652.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1652.manifest deleted file mode 100644 index 9e228b006..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1652.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1653.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1653.manifest deleted file mode 100644 index 4cd59167c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1653.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1654.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1654.manifest deleted file mode 100644 index eaaa1fdb6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1654.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1655.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1655.manifest deleted file mode 100644 index e59b1448d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1655.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1656.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1656.manifest deleted file mode 100644 index 3577edce4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1656.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1657.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1657.manifest deleted file mode 100644 index 5c0b0ff8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1657.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1658.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1658.manifest deleted file mode 100644 index 5d5b39c08..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1658.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1659.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1659.manifest deleted file mode 100644 index 1c154ab61..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1659.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/166.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/166.manifest deleted file mode 100644 index b5f8d7dbf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/166.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1660.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1660.manifest deleted file mode 100644 index a9526b3f5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1660.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1661.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1661.manifest deleted file mode 100644 index 458f8daca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1661.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1662.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1662.manifest deleted file mode 100644 index 6a9f071f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1662.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1663.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1663.manifest deleted file mode 100644 index 46b209d65..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1663.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1664.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1664.manifest deleted file mode 100644 index 0352b8fee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1664.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1665.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1665.manifest deleted file mode 100644 index 6058712a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1665.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1666.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1666.manifest deleted file mode 100644 index 35406b720..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1666.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1667.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1667.manifest deleted file mode 100644 index 72e149bf8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1667.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1668.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1668.manifest deleted file mode 100644 index 6bf27610e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1668.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1669.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1669.manifest deleted file mode 100644 index f3b0989e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1669.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/167.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/167.manifest deleted file mode 100644 index b884a7aae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/167.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1670.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1670.manifest deleted file mode 100644 index 27d45f5ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1670.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1671.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1671.manifest deleted file mode 100644 index 4554d86ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1671.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1672.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1672.manifest deleted file mode 100644 index 4278108d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1672.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1673.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1673.manifest deleted file mode 100644 index ccab97b1f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1673.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1674.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1674.manifest deleted file mode 100644 index 4d673dc14..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1674.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1675.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1675.manifest deleted file mode 100644 index fd95786e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1675.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1676.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1676.manifest deleted file mode 100644 index 09912ffc3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1676.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1677.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1677.manifest deleted file mode 100644 index 6d731f8eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1677.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1678.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1678.manifest deleted file mode 100644 index f8deaf634..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1678.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1679.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1679.manifest deleted file mode 100644 index f93bf0e4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1679.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/168.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/168.manifest deleted file mode 100644 index 5c91b4029..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/168.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1680.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1680.manifest deleted file mode 100644 index bbbb31e3c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1680.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1681.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1681.manifest deleted file mode 100644 index b7db34eef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1681.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1682.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1682.manifest deleted file mode 100644 index 979f570bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1682.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1683.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1683.manifest deleted file mode 100644 index 16f929a1a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1683.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1684.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1684.manifest deleted file mode 100644 index 632136821..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1684.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1685.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1685.manifest deleted file mode 100644 index 068dee753..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1685.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1686.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1686.manifest deleted file mode 100644 index 237a5cf8a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1686.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1687.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1687.manifest deleted file mode 100644 index 96475422e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1687.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1688.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1688.manifest deleted file mode 100644 index 91e9330fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1688.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1689.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1689.manifest deleted file mode 100644 index 1db410067..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1689.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/169.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/169.manifest deleted file mode 100644 index 8e948f663..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/169.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1690.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1690.manifest deleted file mode 100644 index 1f11f51ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1690.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1691.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1691.manifest deleted file mode 100644 index 49ee25f08..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1691.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1692.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1692.manifest deleted file mode 100644 index ba4296249..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1692.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1693.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1693.manifest deleted file mode 100644 index bd6408b00..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1693.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1694.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1694.manifest deleted file mode 100644 index c6be4b0a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1694.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1695.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1695.manifest deleted file mode 100644 index 4678560fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1695.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1696.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1696.manifest deleted file mode 100644 index 1a7d1cf9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1696.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1697.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1697.manifest deleted file mode 100644 index 9c05ce52d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1697.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1698.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1698.manifest deleted file mode 100644 index 5e91f7e45..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1698.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1699.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1699.manifest deleted file mode 100644 index 9f96374e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1699.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/17.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/17.manifest deleted file mode 100644 index e47792d94..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/17.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/170.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/170.manifest deleted file mode 100644 index 178c5cbcb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/170.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1700.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1700.manifest deleted file mode 100644 index 12e8d28f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1700.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1701.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1701.manifest deleted file mode 100644 index 7130c40ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1701.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1702.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1702.manifest deleted file mode 100644 index 41284eab8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1702.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1703.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1703.manifest deleted file mode 100644 index fc04d436b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1703.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1704.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1704.manifest deleted file mode 100644 index 67765edc5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1704.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1705.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1705.manifest deleted file mode 100644 index 149453272..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1705.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1706.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1706.manifest deleted file mode 100644 index 6413cfce4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1706.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1707.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1707.manifest deleted file mode 100644 index f38cfc5c1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1707.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1708.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1708.manifest deleted file mode 100644 index 85d0a9809..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1708.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1709.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1709.manifest deleted file mode 100644 index 62747c42e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1709.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/171.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/171.manifest deleted file mode 100644 index 258267efe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/171.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1710.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1710.manifest deleted file mode 100644 index 875deb9f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1710.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1711.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1711.manifest deleted file mode 100644 index e691f7800..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1711.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1712.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1712.manifest deleted file mode 100644 index 1666ef860..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1712.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1713.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1713.manifest deleted file mode 100644 index b58bf08e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1713.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1714.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1714.manifest deleted file mode 100644 index d3f6b5c85..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1714.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1715.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1715.manifest deleted file mode 100644 index 2d4d25e8d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1715.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1716.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1716.manifest deleted file mode 100644 index e4dd4af77..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1716.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1717.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1717.manifest deleted file mode 100644 index ef895e016..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1717.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1718.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1718.manifest deleted file mode 100644 index 0e8851570..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1718.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1719.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1719.manifest deleted file mode 100644 index 873eb87e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1719.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/172.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/172.manifest deleted file mode 100644 index 816a9c43c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/172.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1720.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1720.manifest deleted file mode 100644 index f87c29043..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1720.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1721.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1721.manifest deleted file mode 100644 index 6ddbeeada..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1721.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1722.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1722.manifest deleted file mode 100644 index f658b2a28..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1722.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1723.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1723.manifest deleted file mode 100644 index 38b8b98b7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1723.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1724.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1724.manifest deleted file mode 100644 index 01a3962e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1724.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1725.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1725.manifest deleted file mode 100644 index 06047a1fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1725.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1726.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1726.manifest deleted file mode 100644 index 35f03a3da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1726.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1727.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1727.manifest deleted file mode 100644 index d2c934601..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1727.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1728.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1728.manifest deleted file mode 100644 index 7f21fab2c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1728.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1729.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1729.manifest deleted file mode 100644 index 0a46a44b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1729.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/173.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/173.manifest deleted file mode 100644 index f8c9afaeb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/173.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1730.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1730.manifest deleted file mode 100644 index 3f15fd195..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1730.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1731.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1731.manifest deleted file mode 100644 index 9f53d4af4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1731.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1732.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1732.manifest deleted file mode 100644 index b812f3d3f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1732.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1733.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1733.manifest deleted file mode 100644 index b271cd273..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1733.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1734.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1734.manifest deleted file mode 100644 index feaae48c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1734.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1735.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1735.manifest deleted file mode 100644 index 8c5284f32..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1735.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1736.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1736.manifest deleted file mode 100644 index f94915a00..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1736.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1737.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1737.manifest deleted file mode 100644 index 54b63c95d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1737.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1738.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1738.manifest deleted file mode 100644 index 259700dbe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1738.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1739.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1739.manifest deleted file mode 100644 index a24b5cba4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1739.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/174.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/174.manifest deleted file mode 100644 index 4f0f818e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/174.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1740.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1740.manifest deleted file mode 100644 index bbb529b81..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1740.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1741.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1741.manifest deleted file mode 100644 index fed2800e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1741.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1742.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1742.manifest deleted file mode 100644 index 97cd8ed46..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1742.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1743.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1743.manifest deleted file mode 100644 index aa1094ba1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1743.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1744.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1744.manifest deleted file mode 100644 index a55be3154..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1744.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1745.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1745.manifest deleted file mode 100644 index 5a12babe7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1745.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1746.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1746.manifest deleted file mode 100644 index bc72bbc8f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1746.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1747.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1747.manifest deleted file mode 100644 index 21ea3d9aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1747.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1748.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1748.manifest deleted file mode 100644 index 06a1224ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1748.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1749.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1749.manifest deleted file mode 100644 index 4ae872903..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1749.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/175.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/175.manifest deleted file mode 100644 index e7c514bd2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/175.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1750.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1750.manifest deleted file mode 100644 index 6531bfe11..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1750.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1751.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1751.manifest deleted file mode 100644 index 940ff158a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1751.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1752.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1752.manifest deleted file mode 100644 index 8c09b2119..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1752.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1753.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1753.manifest deleted file mode 100644 index 2a0746077..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1753.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1754.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1754.manifest deleted file mode 100644 index 7a6d175ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1754.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1755.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1755.manifest deleted file mode 100644 index 56bfd9533..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1755.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1756.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1756.manifest deleted file mode 100644 index da6069af2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1756.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1757.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1757.manifest deleted file mode 100644 index 6513453d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1757.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1758.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1758.manifest deleted file mode 100644 index 3d6aa93aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1758.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1759.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1759.manifest deleted file mode 100644 index 2ad5e69f4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1759.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/176.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/176.manifest deleted file mode 100644 index ea1fc9661..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/176.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1760.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1760.manifest deleted file mode 100644 index a3f482640..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1760.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1761.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1761.manifest deleted file mode 100644 index 20c681390..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1761.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1762.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1762.manifest deleted file mode 100644 index e39252728..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1762.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1763.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1763.manifest deleted file mode 100644 index 06ab55ab1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1763.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1764.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1764.manifest deleted file mode 100644 index fcdf7f504..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1764.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1765.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1765.manifest deleted file mode 100644 index 025217d86..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1765.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1766.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1766.manifest deleted file mode 100644 index a704d32da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1766.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1767.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1767.manifest deleted file mode 100644 index 7bd738aec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1767.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1768.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1768.manifest deleted file mode 100644 index 109531aad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1768.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1769.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1769.manifest deleted file mode 100644 index 27e534a24..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1769.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/177.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/177.manifest deleted file mode 100644 index f58eaed0a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/177.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1770.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1770.manifest deleted file mode 100644 index 43e4f6048..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1770.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1771.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1771.manifest deleted file mode 100644 index 5c8260861..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1771.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1772.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1772.manifest deleted file mode 100644 index b05b50fad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1772.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1773.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1773.manifest deleted file mode 100644 index 286242c42..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1773.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1774.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1774.manifest deleted file mode 100644 index f5353a22b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1774.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1775.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1775.manifest deleted file mode 100644 index b2e604bb2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1775.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1776.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1776.manifest deleted file mode 100644 index a5aa72a4c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1776.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1777.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1777.manifest deleted file mode 100644 index 18655a000..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1777.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1778.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1778.manifest deleted file mode 100644 index 5e5dac731..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1778.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1779.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1779.manifest deleted file mode 100644 index ff814fed9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1779.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/178.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/178.manifest deleted file mode 100644 index c5a3928b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/178.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1780.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1780.manifest deleted file mode 100644 index cb6e2f1ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1780.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1781.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1781.manifest deleted file mode 100644 index 73388eca4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1781.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1782.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1782.manifest deleted file mode 100644 index d340be80d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1782.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1783.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1783.manifest deleted file mode 100644 index 71f8fba48..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1783.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1784.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1784.manifest deleted file mode 100644 index 76c2938e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1784.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1785.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1785.manifest deleted file mode 100644 index bb83187f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1785.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1786.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1786.manifest deleted file mode 100644 index 7a26f0421..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1786.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1787.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1787.manifest deleted file mode 100644 index 3a7f5e6cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1787.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1788.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1788.manifest deleted file mode 100644 index f6e0a1fdf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1788.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1789.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1789.manifest deleted file mode 100644 index 11c7eb111..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1789.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/179.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/179.manifest deleted file mode 100644 index 454e37abe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/179.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1790.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1790.manifest deleted file mode 100644 index 298d55e7e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1790.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1791.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1791.manifest deleted file mode 100644 index 108f17aae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1791.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1792.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1792.manifest deleted file mode 100644 index 1056ecb1a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1792.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1793.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1793.manifest deleted file mode 100644 index 4fa5c9be0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1793.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1794.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1794.manifest deleted file mode 100644 index 2e9a869a0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1794.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1795.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1795.manifest deleted file mode 100644 index 711a70230..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1795.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1796.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1796.manifest deleted file mode 100644 index 3e219af7f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1796.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1797.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1797.manifest deleted file mode 100644 index 37eced798..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1797.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1798.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1798.manifest deleted file mode 100644 index 65d648509..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1798.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1799.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1799.manifest deleted file mode 100644 index 08bc77156..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1799.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/18.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/18.manifest deleted file mode 100644 index a471de63f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/18.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/180.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/180.manifest deleted file mode 100644 index 25c493b72..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/180.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1800.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1800.manifest deleted file mode 100644 index 06ca4bc56..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1800.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1801.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1801.manifest deleted file mode 100644 index bbc255c58..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1801.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1802.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1802.manifest deleted file mode 100644 index 764b65776..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1802.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1803.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1803.manifest deleted file mode 100644 index 73b30a3d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1803.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1804.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1804.manifest deleted file mode 100644 index 360f1ca2d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1804.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1805.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1805.manifest deleted file mode 100644 index c0fc23322..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1805.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1806.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1806.manifest deleted file mode 100644 index 90dde120b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1806.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1807.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1807.manifest deleted file mode 100644 index ab2c919c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1807.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1808.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1808.manifest deleted file mode 100644 index 943e4c568..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1808.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1809.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1809.manifest deleted file mode 100644 index 2b560aaec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1809.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/181.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/181.manifest deleted file mode 100644 index 39ac22d19..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/181.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1810.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1810.manifest deleted file mode 100644 index e477a2faa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1810.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1811.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1811.manifest deleted file mode 100644 index 5446662b2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1811.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1812.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1812.manifest deleted file mode 100644 index 1a34b86dd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1812.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1813.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1813.manifest deleted file mode 100644 index 306296040..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1813.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1814.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1814.manifest deleted file mode 100644 index 290d84ad4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1814.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1815.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1815.manifest deleted file mode 100644 index e4e31b7fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1815.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1816.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1816.manifest deleted file mode 100644 index 952ab0a8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1816.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1817.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1817.manifest deleted file mode 100644 index a8121c160..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1817.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1818.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1818.manifest deleted file mode 100644 index 8491851a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1818.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1819.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1819.manifest deleted file mode 100644 index baafdaa61..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1819.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/182.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/182.manifest deleted file mode 100644 index 1dca1679e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/182.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1820.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1820.manifest deleted file mode 100644 index 440332b4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1820.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1821.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1821.manifest deleted file mode 100644 index 2820ae3da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1821.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1822.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1822.manifest deleted file mode 100644 index 95cda1e9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1822.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1823.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1823.manifest deleted file mode 100644 index 3488c2499..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1823.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1824.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1824.manifest deleted file mode 100644 index 1e65e957f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1824.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1825.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1825.manifest deleted file mode 100644 index f7d03ac47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1825.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1826.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1826.manifest deleted file mode 100644 index 2f89dbedc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1826.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1827.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1827.manifest deleted file mode 100644 index ae2905ed4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1827.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1828.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1828.manifest deleted file mode 100644 index 331ce423f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1828.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1829.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1829.manifest deleted file mode 100644 index bd5fce3be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1829.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/183.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/183.manifest deleted file mode 100644 index c6a1333ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/183.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1830.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1830.manifest deleted file mode 100644 index 243872a9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1830.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1831.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1831.manifest deleted file mode 100644 index 2bef42aa1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1831.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1832.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1832.manifest deleted file mode 100644 index 67a93ef96..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1832.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1833.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1833.manifest deleted file mode 100644 index 494e6608f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1833.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1834.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1834.manifest deleted file mode 100644 index fca8d8cce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1834.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1835.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1835.manifest deleted file mode 100644 index 763cf2bf1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1835.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1836.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1836.manifest deleted file mode 100644 index eaca6ab11..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1836.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1837.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1837.manifest deleted file mode 100644 index af019db35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1837.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1838.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1838.manifest deleted file mode 100644 index 9a81c93f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1838.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1839.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1839.manifest deleted file mode 100644 index c9f1c6f7e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1839.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/184.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/184.manifest deleted file mode 100644 index 97536542c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/184.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1840.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1840.manifest deleted file mode 100644 index b2a04a76a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1840.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1841.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1841.manifest deleted file mode 100644 index 0d8968038..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1841.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1842.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1842.manifest deleted file mode 100644 index 4999ad047..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1842.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1843.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1843.manifest deleted file mode 100644 index 27e8e8745..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1843.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1844.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1844.manifest deleted file mode 100644 index 2c991d245..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1844.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1845.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1845.manifest deleted file mode 100644 index ea8881d57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1845.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1846.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1846.manifest deleted file mode 100644 index 2fbd5d97c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1846.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1847.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1847.manifest deleted file mode 100644 index 1693d222e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1847.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1848.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1848.manifest deleted file mode 100644 index c61cdbb98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1848.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1849.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1849.manifest deleted file mode 100644 index 8da46ce37..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1849.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/185.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/185.manifest deleted file mode 100644 index d76657fc3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/185.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1850.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1850.manifest deleted file mode 100644 index 4c4a1cf9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1850.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1851.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1851.manifest deleted file mode 100644 index ea688dbad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1851.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1852.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1852.manifest deleted file mode 100644 index a7cc9fc77..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1852.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1853.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1853.manifest deleted file mode 100644 index 4151ca23d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1853.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1854.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1854.manifest deleted file mode 100644 index 41aa05a5f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1854.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1855.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1855.manifest deleted file mode 100644 index bdad2f632..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1855.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1856.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1856.manifest deleted file mode 100644 index 8e7fcb328..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1856.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1857.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1857.manifest deleted file mode 100644 index c6531a802..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1857.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1858.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1858.manifest deleted file mode 100644 index 89f433515..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1858.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1859.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1859.manifest deleted file mode 100644 index f684e4c31..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1859.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/186.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/186.manifest deleted file mode 100644 index d06b1c651..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/186.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1860.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1860.manifest deleted file mode 100644 index 8d4d41e16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1860.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1861.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1861.manifest deleted file mode 100644 index 47286594d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1861.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1862.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1862.manifest deleted file mode 100644 index 6af20289f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1862.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1863.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1863.manifest deleted file mode 100644 index 86a573668..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1863.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1864.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1864.manifest deleted file mode 100644 index 597802da1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1864.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1865.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1865.manifest deleted file mode 100644 index e39eca8cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1865.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1866.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1866.manifest deleted file mode 100644 index 98115d00d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1866.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1867.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1867.manifest deleted file mode 100644 index dae7070d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1867.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1868.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1868.manifest deleted file mode 100644 index bf4bf6ad0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1868.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1869.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1869.manifest deleted file mode 100644 index d224c9bfa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1869.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/187.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/187.manifest deleted file mode 100644 index 1012fb4f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/187.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1870.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1870.manifest deleted file mode 100644 index d2100b540..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1870.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1871.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1871.manifest deleted file mode 100644 index 446fb1d98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1871.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1872.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1872.manifest deleted file mode 100644 index 68a463020..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1872.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1873.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1873.manifest deleted file mode 100644 index 376c38c65..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1873.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1874.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1874.manifest deleted file mode 100644 index 64d000b7c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1874.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1875.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1875.manifest deleted file mode 100644 index f33da1df1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1875.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1876.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1876.manifest deleted file mode 100644 index a3d790fd6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1876.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1877.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1877.manifest deleted file mode 100644 index 298758c60..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1877.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1878.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1878.manifest deleted file mode 100644 index 55b98f574..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1878.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1879.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1879.manifest deleted file mode 100644 index 2cdec6b2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1879.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/188.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/188.manifest deleted file mode 100644 index 9fc7d4e12..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/188.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1880.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1880.manifest deleted file mode 100644 index 6f0b2a015..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1880.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1881.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1881.manifest deleted file mode 100644 index 64387991a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1881.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1882.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1882.manifest deleted file mode 100644 index 584f934b6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1882.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1883.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1883.manifest deleted file mode 100644 index bb3ab947a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1883.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1884.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1884.manifest deleted file mode 100644 index 2139659c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1884.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1885.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1885.manifest deleted file mode 100644 index 2c779106a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1885.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1886.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1886.manifest deleted file mode 100644 index 3431dd9b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1886.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1887.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1887.manifest deleted file mode 100644 index bbf4bc8f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1887.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1888.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1888.manifest deleted file mode 100644 index 517880a42..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1888.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1889.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1889.manifest deleted file mode 100644 index 11506e8d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1889.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/189.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/189.manifest deleted file mode 100644 index 726910219..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/189.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1890.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1890.manifest deleted file mode 100644 index 92a239ee2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1890.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1891.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1891.manifest deleted file mode 100644 index 80ff9a74d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1891.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1892.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1892.manifest deleted file mode 100644 index e1774df2d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1892.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1893.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1893.manifest deleted file mode 100644 index 48e60dfea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1893.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1894.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1894.manifest deleted file mode 100644 index 4312b7637..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1894.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1895.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1895.manifest deleted file mode 100644 index 97d765dcb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1895.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1896.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1896.manifest deleted file mode 100644 index e4b2c41e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1896.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1897.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1897.manifest deleted file mode 100644 index 3703d4960..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1897.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1898.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1898.manifest deleted file mode 100644 index 9ce8e9411..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1898.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1899.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1899.manifest deleted file mode 100644 index f74c5f0c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1899.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/19.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/19.manifest deleted file mode 100644 index 2299de3f5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/19.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/190.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/190.manifest deleted file mode 100644 index af9b179c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/190.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1900.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1900.manifest deleted file mode 100644 index 443a5ad0a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1900.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1901.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1901.manifest deleted file mode 100644 index a2afa97a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1901.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1902.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1902.manifest deleted file mode 100644 index 1d01a0586..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1902.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1903.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1903.manifest deleted file mode 100644 index f26e3b9cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1903.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1904.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1904.manifest deleted file mode 100644 index 643c05856..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1904.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1905.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1905.manifest deleted file mode 100644 index 5b5bbc2da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1905.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1906.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1906.manifest deleted file mode 100644 index 45f694726..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1906.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1907.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1907.manifest deleted file mode 100644 index b4f9b2c0f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1907.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1908.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1908.manifest deleted file mode 100644 index e0bdf06a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1908.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1909.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1909.manifest deleted file mode 100644 index 11c2bf2c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1909.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/191.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/191.manifest deleted file mode 100644 index 21df2196b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/191.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1910.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1910.manifest deleted file mode 100644 index f361e8fa7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1910.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1911.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1911.manifest deleted file mode 100644 index 4121cb0cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1911.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1912.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1912.manifest deleted file mode 100644 index 58d7d22ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1912.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1913.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1913.manifest deleted file mode 100644 index c504e8676..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1913.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1914.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1914.manifest deleted file mode 100644 index fe2f3b21d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1914.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1915.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1915.manifest deleted file mode 100644 index ffaa0493b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1915.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1916.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1916.manifest deleted file mode 100644 index 0c11972a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1916.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1917.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1917.manifest deleted file mode 100644 index 485c96bd7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1917.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1918.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1918.manifest deleted file mode 100644 index 3c830b5fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1918.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1919.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1919.manifest deleted file mode 100644 index e314febf0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1919.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/192.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/192.manifest deleted file mode 100644 index e47af5af5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/192.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1920.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1920.manifest deleted file mode 100644 index 9f736ce8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1920.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1921.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1921.manifest deleted file mode 100644 index b0cd12911..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1921.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1922.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1922.manifest deleted file mode 100644 index cb39778fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1922.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1923.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1923.manifest deleted file mode 100644 index bcdbdd500..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1923.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1924.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1924.manifest deleted file mode 100644 index c6c5acae7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1924.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1925.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1925.manifest deleted file mode 100644 index 0a60238d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1925.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1926.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1926.manifest deleted file mode 100644 index 2fd32e70e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1926.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1927.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1927.manifest deleted file mode 100644 index 8d42cc1a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1927.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1928.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1928.manifest deleted file mode 100644 index b93720b76..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1928.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1929.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1929.manifest deleted file mode 100644 index 4b03e2be5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1929.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/193.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/193.manifest deleted file mode 100644 index 515c5d6d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/193.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1930.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1930.manifest deleted file mode 100644 index 9ba7fbb65..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1930.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1931.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1931.manifest deleted file mode 100644 index be5c5d5c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1931.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1932.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1932.manifest deleted file mode 100644 index f8f49cd9c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1932.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1933.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1933.manifest deleted file mode 100644 index a819459fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1933.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1934.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1934.manifest deleted file mode 100644 index 04cf8b953..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1934.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1935.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1935.manifest deleted file mode 100644 index e7176e135..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1935.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1936.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1936.manifest deleted file mode 100644 index 3e96bb7ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1936.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1937.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1937.manifest deleted file mode 100644 index b70192f25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1937.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1938.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1938.manifest deleted file mode 100644 index 7bb6da3bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1938.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1939.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1939.manifest deleted file mode 100644 index 65bb8c216..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1939.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/194.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/194.manifest deleted file mode 100644 index cfffacc5a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/194.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1940.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1940.manifest deleted file mode 100644 index 20405db67..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1940.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1941.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1941.manifest deleted file mode 100644 index 51e4d6800..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1941.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1942.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1942.manifest deleted file mode 100644 index 669e2ed4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1942.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1943.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1943.manifest deleted file mode 100644 index cd363194a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1943.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1944.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1944.manifest deleted file mode 100644 index 61306fbc7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1944.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1945.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1945.manifest deleted file mode 100644 index 94e4889ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1945.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1946.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1946.manifest deleted file mode 100644 index b9a02e5a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1946.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1947.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1947.manifest deleted file mode 100644 index 207d5eb4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1947.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1948.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1948.manifest deleted file mode 100644 index c76305abf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1948.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1949.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1949.manifest deleted file mode 100644 index 04f0a3654..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1949.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/195.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/195.manifest deleted file mode 100644 index 09c2cc860..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/195.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1950.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1950.manifest deleted file mode 100644 index b70422905..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1950.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1951.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1951.manifest deleted file mode 100644 index 6766ef644..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1951.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1952.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1952.manifest deleted file mode 100644 index f40a52ae4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1952.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1953.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1953.manifest deleted file mode 100644 index d4867be87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1953.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1954.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1954.manifest deleted file mode 100644 index 5c42c02af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1954.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1955.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1955.manifest deleted file mode 100644 index 3828ea2fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1955.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1956.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1956.manifest deleted file mode 100644 index 9b047a048..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1956.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1957.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1957.manifest deleted file mode 100644 index faf011329..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1957.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1958.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1958.manifest deleted file mode 100644 index 67214bdab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1958.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1959.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1959.manifest deleted file mode 100644 index 9f8290f26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1959.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/196.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/196.manifest deleted file mode 100644 index cee104623..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/196.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1960.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1960.manifest deleted file mode 100644 index d3f823611..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1960.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1961.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1961.manifest deleted file mode 100644 index 716a127cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1961.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1962.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1962.manifest deleted file mode 100644 index bbf4815c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1962.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1963.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1963.manifest deleted file mode 100644 index c28229799..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1963.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1964.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1964.manifest deleted file mode 100644 index 3c196a3c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1964.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1965.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1965.manifest deleted file mode 100644 index d898f3f71..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1965.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1966.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1966.manifest deleted file mode 100644 index d7d625c0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1966.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1967.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1967.manifest deleted file mode 100644 index 3628b3571..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1967.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1968.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1968.manifest deleted file mode 100644 index 0c87358d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1968.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1969.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1969.manifest deleted file mode 100644 index 5fa29d752..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1969.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/197.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/197.manifest deleted file mode 100644 index 57fa19bd7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/197.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1970.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1970.manifest deleted file mode 100644 index 135a33813..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1970.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1971.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1971.manifest deleted file mode 100644 index f7cdba97e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1971.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1972.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1972.manifest deleted file mode 100644 index c75a93c83..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1972.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1973.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1973.manifest deleted file mode 100644 index 4c1c5d967..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1973.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1974.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1974.manifest deleted file mode 100644 index e7e9fd4bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1974.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1975.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1975.manifest deleted file mode 100644 index dc0ac3472..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1975.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1976.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1976.manifest deleted file mode 100644 index 434b93fc8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1976.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1977.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1977.manifest deleted file mode 100644 index bbb975ccf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1977.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1978.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1978.manifest deleted file mode 100644 index 325ad1fcc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1978.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1979.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1979.manifest deleted file mode 100644 index d17982e16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1979.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/198.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/198.manifest deleted file mode 100644 index b01837c9a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/198.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1980.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1980.manifest deleted file mode 100644 index b9dcfb548..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1980.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1981.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1981.manifest deleted file mode 100644 index e9bd9b1bd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1981.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1982.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1982.manifest deleted file mode 100644 index 5bf84e895..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1982.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1983.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1983.manifest deleted file mode 100644 index 8cf7ac023..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1983.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1984.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1984.manifest deleted file mode 100644 index 563448252..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1984.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1985.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1985.manifest deleted file mode 100644 index d3d8cfbdb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1985.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1986.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1986.manifest deleted file mode 100644 index c5352eaba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1986.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1987.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1987.manifest deleted file mode 100644 index 3f3da339d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1987.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1988.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1988.manifest deleted file mode 100644 index bfefc988a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1988.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1989.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1989.manifest deleted file mode 100644 index 4b39d89f5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1989.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/199.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/199.manifest deleted file mode 100644 index b69d2e638..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/199.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1990.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1990.manifest deleted file mode 100644 index b9c53cacc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1990.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1991.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1991.manifest deleted file mode 100644 index 978913ea0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1991.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1992.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1992.manifest deleted file mode 100644 index ba2283184..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1992.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1993.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1993.manifest deleted file mode 100644 index 40ed5bda7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1993.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1994.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1994.manifest deleted file mode 100644 index feaf9c393..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1994.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1995.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1995.manifest deleted file mode 100644 index e6c139bb0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1995.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1996.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1996.manifest deleted file mode 100644 index 7d7a6ed28..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1996.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1997.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1997.manifest deleted file mode 100644 index b826fc4e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1997.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1998.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1998.manifest deleted file mode 100644 index d1c446984..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1998.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1999.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1999.manifest deleted file mode 100644 index 756c8c0fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/1999.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2.manifest deleted file mode 100644 index fe52e63f4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/20.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/20.manifest deleted file mode 100644 index 67b27624d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/20.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/200.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/200.manifest deleted file mode 100644 index d13a0486a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/200.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2000.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2000.manifest deleted file mode 100644 index e31ce7e5f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2000.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2001.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2001.manifest deleted file mode 100644 index 76c0ab3d6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2001.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2002.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2002.manifest deleted file mode 100644 index be3cb96c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2002.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2003.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2003.manifest deleted file mode 100644 index b4bf3dfd7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2003.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2004.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2004.manifest deleted file mode 100644 index 31b1998af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2004.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2005.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2005.manifest deleted file mode 100644 index 6d3b5af90..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2005.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2006.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2006.manifest deleted file mode 100644 index 5cd4a17d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2006.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2007.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2007.manifest deleted file mode 100644 index 31f37a96c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2007.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2008.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2008.manifest deleted file mode 100644 index be4255a27..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2008.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2009.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2009.manifest deleted file mode 100644 index f7ab3e309..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2009.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/201.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/201.manifest deleted file mode 100644 index 3ffa3c625..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/201.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2010.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2010.manifest deleted file mode 100644 index b9de3d7fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2010.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2011.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2011.manifest deleted file mode 100644 index f6bd4a9ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2011.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2012.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2012.manifest deleted file mode 100644 index f73994e77..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2012.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2013.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2013.manifest deleted file mode 100644 index ea3fac438..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2013.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2014.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2014.manifest deleted file mode 100644 index 0415c6028..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2014.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2015.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2015.manifest deleted file mode 100644 index d663bab22..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2015.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2016.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2016.manifest deleted file mode 100644 index 565bbd0df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2016.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2017.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2017.manifest deleted file mode 100644 index 5adecd95a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2017.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2018.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2018.manifest deleted file mode 100644 index 61add55d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2018.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2019.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2019.manifest deleted file mode 100644 index 310992309..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2019.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/202.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/202.manifest deleted file mode 100644 index cff56e6de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/202.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2020.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2020.manifest deleted file mode 100644 index c59cd411f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2020.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2021.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2021.manifest deleted file mode 100644 index 7f5ce473a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2021.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2022.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2022.manifest deleted file mode 100644 index 7e05c0077..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2022.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2023.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2023.manifest deleted file mode 100644 index 45c85cb57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2023.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2024.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2024.manifest deleted file mode 100644 index 8b180cd59..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2024.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2025.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2025.manifest deleted file mode 100644 index 4ed54cb0c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2025.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2026.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2026.manifest deleted file mode 100644 index 30f26eac1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2026.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2027.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2027.manifest deleted file mode 100644 index 68c1f0e16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2027.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2028.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2028.manifest deleted file mode 100644 index c11dc1dca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2028.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2029.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2029.manifest deleted file mode 100644 index a8a706867..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2029.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/203.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/203.manifest deleted file mode 100644 index 9074439f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/203.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2030.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2030.manifest deleted file mode 100644 index bf82c900f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2030.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2031.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2031.manifest deleted file mode 100644 index 7b589291f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2031.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2032.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2032.manifest deleted file mode 100644 index 00d8be56d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2032.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2033.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2033.manifest deleted file mode 100644 index 64c9c55ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2033.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2034.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2034.manifest deleted file mode 100644 index 9210285dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2034.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2035.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2035.manifest deleted file mode 100644 index 411e2c9b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2035.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2036.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2036.manifest deleted file mode 100644 index d26ef346f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2036.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2037.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2037.manifest deleted file mode 100644 index 638638c0a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2037.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2038.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2038.manifest deleted file mode 100644 index 117bf2fbb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2038.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2039.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2039.manifest deleted file mode 100644 index 8e191a409..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2039.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/204.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/204.manifest deleted file mode 100644 index 6cba9d610..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/204.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2040.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2040.manifest deleted file mode 100644 index 36ccfb34a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2040.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2041.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2041.manifest deleted file mode 100644 index e1a20f7cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2041.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2042.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2042.manifest deleted file mode 100644 index 7250d9e68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2042.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2043.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2043.manifest deleted file mode 100644 index d15c0eefe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2043.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2044.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2044.manifest deleted file mode 100644 index 4fbc9b2b0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2044.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2045.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2045.manifest deleted file mode 100644 index 4e438a5d6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2045.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2046.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2046.manifest deleted file mode 100644 index 0113d04e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2046.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2047.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2047.manifest deleted file mode 100644 index eceaa62cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2047.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2048.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2048.manifest deleted file mode 100644 index c7b414d19..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2048.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2049.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2049.manifest deleted file mode 100644 index 2e5d7221c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2049.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/205.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/205.manifest deleted file mode 100644 index 9b5b08706..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/205.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2050.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2050.manifest deleted file mode 100644 index 26ff6bdd2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2050.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2051.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2051.manifest deleted file mode 100644 index 2293976f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2051.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2052.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2052.manifest deleted file mode 100644 index 1b4ec3f44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2052.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2053.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2053.manifest deleted file mode 100644 index 8b6cc286f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2053.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2054.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2054.manifest deleted file mode 100644 index bb681f733..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2054.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2055.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2055.manifest deleted file mode 100644 index 924eafd7b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2055.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2056.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2056.manifest deleted file mode 100644 index 083386c96..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2056.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2057.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2057.manifest deleted file mode 100644 index 3fdbf7870..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2057.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2058.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2058.manifest deleted file mode 100644 index fb8fd0743..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2058.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2059.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2059.manifest deleted file mode 100644 index e0417220c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2059.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/206.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/206.manifest deleted file mode 100644 index 6968738f5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/206.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2060.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2060.manifest deleted file mode 100644 index 46592fe29..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2060.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2061.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2061.manifest deleted file mode 100644 index 31c47e93f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2061.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2062.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2062.manifest deleted file mode 100644 index ceafd6a79..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2062.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2063.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2063.manifest deleted file mode 100644 index 92e147749..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2063.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2064.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2064.manifest deleted file mode 100644 index bcda82118..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2064.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2065.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2065.manifest deleted file mode 100644 index 0bee2eace..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2065.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2066.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2066.manifest deleted file mode 100644 index 9f8177273..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2066.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2067.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2067.manifest deleted file mode 100644 index 9fe99850e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2067.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2068.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2068.manifest deleted file mode 100644 index 291e4e125..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2068.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2069.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2069.manifest deleted file mode 100644 index f4a3d5745..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2069.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/207.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/207.manifest deleted file mode 100644 index ea9235f32..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/207.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2070.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2070.manifest deleted file mode 100644 index 46d432dd1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2070.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2071.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2071.manifest deleted file mode 100644 index 85203d739..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2071.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2072.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2072.manifest deleted file mode 100644 index e6f40834c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2072.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2073.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2073.manifest deleted file mode 100644 index d96c14345..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2073.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2074.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2074.manifest deleted file mode 100644 index de7e96286..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2074.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2075.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2075.manifest deleted file mode 100644 index 3620f9e26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2075.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2076.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2076.manifest deleted file mode 100644 index 635a6e8ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2076.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2077.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2077.manifest deleted file mode 100644 index 8e96fab7c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2077.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2078.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2078.manifest deleted file mode 100644 index 20ff0d4e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2078.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2079.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2079.manifest deleted file mode 100644 index 7f938720e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2079.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/208.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/208.manifest deleted file mode 100644 index 6ba0d9adc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/208.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2080.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2080.manifest deleted file mode 100644 index 26d6686ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2080.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2081.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2081.manifest deleted file mode 100644 index 14ddbf4af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2081.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2082.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2082.manifest deleted file mode 100644 index 50f2933ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2082.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2083.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2083.manifest deleted file mode 100644 index 83dc969d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2083.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2084.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2084.manifest deleted file mode 100644 index 62a775591..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2084.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2085.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2085.manifest deleted file mode 100644 index 8b85595ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2085.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2086.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2086.manifest deleted file mode 100644 index 5771bf869..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2086.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2087.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2087.manifest deleted file mode 100644 index cc148f39e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2087.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2088.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2088.manifest deleted file mode 100644 index 6f3557928..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2088.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2089.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2089.manifest deleted file mode 100644 index 0073db31a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2089.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/209.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/209.manifest deleted file mode 100644 index b4aaa9e78..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/209.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2090.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2090.manifest deleted file mode 100644 index 6b0f84161..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2090.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2091.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2091.manifest deleted file mode 100644 index befd62ea6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2091.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2092.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2092.manifest deleted file mode 100644 index 2081fc6b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2092.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2093.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2093.manifest deleted file mode 100644 index 8ef125feb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2093.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2094.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2094.manifest deleted file mode 100644 index 889050484..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2094.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2095.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2095.manifest deleted file mode 100644 index 784d2df7a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2095.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2096.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2096.manifest deleted file mode 100644 index 1d6d9aec2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2096.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2097.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2097.manifest deleted file mode 100644 index d111bc5ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2097.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2098.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2098.manifest deleted file mode 100644 index 27aee4ae6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2098.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2099.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2099.manifest deleted file mode 100644 index 20fa2f06b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2099.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/21.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/21.manifest deleted file mode 100644 index faec8f871..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/21.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/210.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/210.manifest deleted file mode 100644 index 5292a4c8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/210.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2100.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2100.manifest deleted file mode 100644 index b171e1be4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2100.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2101.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2101.manifest deleted file mode 100644 index 4b9e18b27..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2101.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2102.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2102.manifest deleted file mode 100644 index 6dd14daa1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2102.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2103.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2103.manifest deleted file mode 100644 index 34b0cf3ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2103.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2104.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2104.manifest deleted file mode 100644 index f1d836525..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2104.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2105.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2105.manifest deleted file mode 100644 index bee17f31c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2105.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2106.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2106.manifest deleted file mode 100644 index bb32cbe2e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2106.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2107.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2107.manifest deleted file mode 100644 index e29e4bcb9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2107.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2108.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2108.manifest deleted file mode 100644 index 7626c333a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2108.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2109.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2109.manifest deleted file mode 100644 index 7a938eb4c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2109.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/211.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/211.manifest deleted file mode 100644 index 09e44c59e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/211.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2110.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2110.manifest deleted file mode 100644 index 68203057d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2110.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2111.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2111.manifest deleted file mode 100644 index c50283d84..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2111.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2112.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2112.manifest deleted file mode 100644 index bb6e93082..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2112.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2113.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2113.manifest deleted file mode 100644 index 8c758b2c3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2113.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2114.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2114.manifest deleted file mode 100644 index 1286cc4f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2114.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2115.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2115.manifest deleted file mode 100644 index 6e670d339..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2115.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2116.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2116.manifest deleted file mode 100644 index 828d0dba3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2116.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2117.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2117.manifest deleted file mode 100644 index 4f47a1dfe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2117.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2118.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2118.manifest deleted file mode 100644 index 21d6b0bed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2118.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2119.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2119.manifest deleted file mode 100644 index c067b854a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2119.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/212.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/212.manifest deleted file mode 100644 index 50ab4a93f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/212.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2120.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2120.manifest deleted file mode 100644 index 7056cf60b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2120.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2121.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2121.manifest deleted file mode 100644 index e567f70ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2121.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2122.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2122.manifest deleted file mode 100644 index 5d8cec72a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2122.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2123.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2123.manifest deleted file mode 100644 index f7838ebc2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2123.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2124.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2124.manifest deleted file mode 100644 index f7316087a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2124.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2125.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2125.manifest deleted file mode 100644 index 8f80b92a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2125.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2126.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2126.manifest deleted file mode 100644 index 305bbdc43..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2126.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2127.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2127.manifest deleted file mode 100644 index f0300d4aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2127.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2128.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2128.manifest deleted file mode 100644 index 5a969461a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2128.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2129.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2129.manifest deleted file mode 100644 index 3f1b91b10..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2129.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/213.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/213.manifest deleted file mode 100644 index 794336a51..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/213.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2130.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2130.manifest deleted file mode 100644 index 80b321953..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2130.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2131.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2131.manifest deleted file mode 100644 index 79da037a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2131.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2132.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2132.manifest deleted file mode 100644 index b3569ac41..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2132.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2133.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2133.manifest deleted file mode 100644 index 966f410c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2133.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2134.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2134.manifest deleted file mode 100644 index 365a73a73..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2134.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2135.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2135.manifest deleted file mode 100644 index 03585a896..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2135.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2136.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2136.manifest deleted file mode 100644 index b3433e124..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2136.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2137.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2137.manifest deleted file mode 100644 index 14e454103..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2137.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2138.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2138.manifest deleted file mode 100644 index 114cbf5f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2138.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2139.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2139.manifest deleted file mode 100644 index 0650bec20..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2139.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/214.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/214.manifest deleted file mode 100644 index 0ddba8b10..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/214.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2140.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2140.manifest deleted file mode 100644 index f69d13edc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2140.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2141.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2141.manifest deleted file mode 100644 index d1ee04da1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2141.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2142.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2142.manifest deleted file mode 100644 index 0ec12bc8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2142.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2143.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2143.manifest deleted file mode 100644 index 42f29c5bd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2143.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2144.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2144.manifest deleted file mode 100644 index 9621653fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2144.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2145.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2145.manifest deleted file mode 100644 index 772a7f9ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2145.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2146.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2146.manifest deleted file mode 100644 index b8130ada5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2146.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2147.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2147.manifest deleted file mode 100644 index 7b19d5dfd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2147.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2148.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2148.manifest deleted file mode 100644 index 54f968aef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2148.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2149.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2149.manifest deleted file mode 100644 index 1039cdb9b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2149.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/215.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/215.manifest deleted file mode 100644 index 8a5311957..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/215.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2150.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2150.manifest deleted file mode 100644 index 091776740..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2150.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2151.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2151.manifest deleted file mode 100644 index aab3fe741..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2151.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2152.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2152.manifest deleted file mode 100644 index 3aac56054..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2152.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2153.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2153.manifest deleted file mode 100644 index 8d5e53e70..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2153.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2154.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2154.manifest deleted file mode 100644 index a2ca79137..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2154.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2155.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2155.manifest deleted file mode 100644 index 4a84c9508..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2155.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2156.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2156.manifest deleted file mode 100644 index f6982534f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2156.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2157.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2157.manifest deleted file mode 100644 index 5dde1b012..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2157.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2158.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2158.manifest deleted file mode 100644 index 094cb55bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2158.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2159.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2159.manifest deleted file mode 100644 index 7cb572ab3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2159.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/216.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/216.manifest deleted file mode 100644 index a575706da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/216.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2160.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2160.manifest deleted file mode 100644 index 02a63677e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2160.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2161.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2161.manifest deleted file mode 100644 index 1596c8232..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2161.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2162.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2162.manifest deleted file mode 100644 index 1725cc9ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2162.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2163.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2163.manifest deleted file mode 100644 index 458eac3e0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2163.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2164.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2164.manifest deleted file mode 100644 index abae246c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2164.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2165.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2165.manifest deleted file mode 100644 index 3cb5afd68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2165.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2166.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2166.manifest deleted file mode 100644 index aec8d3c27..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2166.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2167.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2167.manifest deleted file mode 100644 index c4da636fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2167.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2168.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2168.manifest deleted file mode 100644 index 4d209e8c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2168.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2169.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2169.manifest deleted file mode 100644 index 8041c3e93..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2169.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/217.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/217.manifest deleted file mode 100644 index 7a5242368..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/217.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2170.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2170.manifest deleted file mode 100644 index 33e67c0c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2170.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2171.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2171.manifest deleted file mode 100644 index 7628f8c5b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2171.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2172.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2172.manifest deleted file mode 100644 index 88471133c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2172.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2173.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2173.manifest deleted file mode 100644 index 56a491019..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2173.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2174.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2174.manifest deleted file mode 100644 index f077d06ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2174.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2175.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2175.manifest deleted file mode 100644 index e997419fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2175.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2176.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2176.manifest deleted file mode 100644 index 5c809e35c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2176.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2177.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2177.manifest deleted file mode 100644 index a711f7842..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2177.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2178.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2178.manifest deleted file mode 100644 index 5dba55438..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2178.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2179.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2179.manifest deleted file mode 100644 index 764e0b4f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2179.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/218.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/218.manifest deleted file mode 100644 index 8fecdb89e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/218.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2180.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2180.manifest deleted file mode 100644 index 8600a4a39..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2180.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2181.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2181.manifest deleted file mode 100644 index 71000f652..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2181.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2182.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2182.manifest deleted file mode 100644 index 756afec3b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2182.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2183.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2183.manifest deleted file mode 100644 index 6e7d3616c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2183.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2184.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2184.manifest deleted file mode 100644 index 907d713df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2184.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2185.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2185.manifest deleted file mode 100644 index 220d3e2a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2185.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2186.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2186.manifest deleted file mode 100644 index 0efbeafb0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2186.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2187.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2187.manifest deleted file mode 100644 index bdb207c67..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2187.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2188.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2188.manifest deleted file mode 100644 index d1d2038ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2188.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2189.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2189.manifest deleted file mode 100644 index 51fc8bfd1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2189.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/219.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/219.manifest deleted file mode 100644 index a5dfbc587..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/219.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2190.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2190.manifest deleted file mode 100644 index 3667b1217..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2190.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2191.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2191.manifest deleted file mode 100644 index 3726fd071..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2191.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2192.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2192.manifest deleted file mode 100644 index b40b69ade..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2192.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2193.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2193.manifest deleted file mode 100644 index b377bc8f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2193.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2194.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2194.manifest deleted file mode 100644 index 96b2a8c5d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2194.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2195.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2195.manifest deleted file mode 100644 index a706edd92..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2195.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2196.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2196.manifest deleted file mode 100644 index 95d8c8385..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2196.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2197.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2197.manifest deleted file mode 100644 index d2a4f7bf0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2197.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2198.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2198.manifest deleted file mode 100644 index afa326420..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2198.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2199.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2199.manifest deleted file mode 100644 index 92276bcde..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2199.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/22.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/22.manifest deleted file mode 100644 index f5ed01663..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/22.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/220.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/220.manifest deleted file mode 100644 index 9e3527a40..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/220.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2200.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2200.manifest deleted file mode 100644 index d9d7c7780..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2200.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2201.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2201.manifest deleted file mode 100644 index 1d05dad57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2201.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2202.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2202.manifest deleted file mode 100644 index 715cb4a92..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2202.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2203.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2203.manifest deleted file mode 100644 index 51b15baa4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2203.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2204.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2204.manifest deleted file mode 100644 index 2b3045e1f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2204.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2205.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2205.manifest deleted file mode 100644 index d9b2737d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2205.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2206.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2206.manifest deleted file mode 100644 index 79a5256c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2206.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2207.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2207.manifest deleted file mode 100644 index 25ce59e4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2207.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2208.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2208.manifest deleted file mode 100644 index 0907adc2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2208.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2209.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2209.manifest deleted file mode 100644 index aa7adbe1e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2209.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/221.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/221.manifest deleted file mode 100644 index 40e0841f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/221.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2210.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2210.manifest deleted file mode 100644 index fb17f4efa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2210.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2211.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2211.manifest deleted file mode 100644 index 1272abd9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2211.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2212.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2212.manifest deleted file mode 100644 index d656bcfdf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2212.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2213.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2213.manifest deleted file mode 100644 index c11987f5a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2213.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2214.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2214.manifest deleted file mode 100644 index 01ca00d44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2214.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2215.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2215.manifest deleted file mode 100644 index 02628edf5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2215.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2216.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2216.manifest deleted file mode 100644 index bf322529e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2216.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2217.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2217.manifest deleted file mode 100644 index dd31d733e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2217.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2218.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2218.manifest deleted file mode 100644 index a92c576cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2218.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2219.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2219.manifest deleted file mode 100644 index a78fe3212..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2219.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/222.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/222.manifest deleted file mode 100644 index 834f616cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/222.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2220.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2220.manifest deleted file mode 100644 index ae4113f4a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2220.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2221.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2221.manifest deleted file mode 100644 index 6eb9e42b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2221.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2222.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2222.manifest deleted file mode 100644 index e98aa3b68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2222.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2223.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2223.manifest deleted file mode 100644 index 83ede2a59..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2223.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2224.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2224.manifest deleted file mode 100644 index 46c99bb93..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2224.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2225.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2225.manifest deleted file mode 100644 index 9c74993e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2225.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2226.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2226.manifest deleted file mode 100644 index f188a4c45..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2226.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2227.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2227.manifest deleted file mode 100644 index d0edb2a4e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2227.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2228.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2228.manifest deleted file mode 100644 index 5d7c42a68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2228.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2229.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2229.manifest deleted file mode 100644 index 0d080623c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2229.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/223.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/223.manifest deleted file mode 100644 index 4fa713217..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/223.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2230.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2230.manifest deleted file mode 100644 index b92bb5ec9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2230.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2231.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2231.manifest deleted file mode 100644 index 94f365a04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2231.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2232.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2232.manifest deleted file mode 100644 index bf0cec356..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2232.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2233.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2233.manifest deleted file mode 100644 index 8098ee51a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2233.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2234.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2234.manifest deleted file mode 100644 index b72279df4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2234.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2235.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2235.manifest deleted file mode 100644 index a5feee488..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2235.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2236.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2236.manifest deleted file mode 100644 index e54a39233..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2236.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2237.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2237.manifest deleted file mode 100644 index 86d33a1b2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2237.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2238.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2238.manifest deleted file mode 100644 index 0003769b6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2238.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2239.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2239.manifest deleted file mode 100644 index b86b40169..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2239.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/224.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/224.manifest deleted file mode 100644 index 3edd1b57b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/224.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2240.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2240.manifest deleted file mode 100644 index 38caa1fa9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2240.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2241.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2241.manifest deleted file mode 100644 index 64a594d35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2241.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2242.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2242.manifest deleted file mode 100644 index 1585a1ccf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2242.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2243.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2243.manifest deleted file mode 100644 index cb0bd9bbc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2243.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2244.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2244.manifest deleted file mode 100644 index ae817fe53..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2244.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2245.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2245.manifest deleted file mode 100644 index 88f9c5a36..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2245.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2246.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2246.manifest deleted file mode 100644 index 96e151992..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2246.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2247.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2247.manifest deleted file mode 100644 index e20eb09c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2247.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2248.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2248.manifest deleted file mode 100644 index 1bd4fe985..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2248.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2249.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2249.manifest deleted file mode 100644 index c5d632824..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2249.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/225.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/225.manifest deleted file mode 100644 index 5199f442d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/225.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2250.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2250.manifest deleted file mode 100644 index 300a607ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2250.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2251.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2251.manifest deleted file mode 100644 index 2f902976c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2251.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2252.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2252.manifest deleted file mode 100644 index 815675d8b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2252.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2253.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2253.manifest deleted file mode 100644 index 1e81055fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2253.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2254.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2254.manifest deleted file mode 100644 index 43d73f9b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2254.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2255.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2255.manifest deleted file mode 100644 index 9226743a6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2255.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2256.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2256.manifest deleted file mode 100644 index 35300e94c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2256.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2257.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2257.manifest deleted file mode 100644 index 940cbcf90..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2257.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2258.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2258.manifest deleted file mode 100644 index bd38fdd73..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2258.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2259.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2259.manifest deleted file mode 100644 index 4058f194a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2259.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/226.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/226.manifest deleted file mode 100644 index df3e8b150..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/226.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2260.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2260.manifest deleted file mode 100644 index 8fa6e3694..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2260.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2261.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2261.manifest deleted file mode 100644 index e1c7322eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2261.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2262.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2262.manifest deleted file mode 100644 index 94d7dc921..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2262.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2263.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2263.manifest deleted file mode 100644 index 34b30f70d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2263.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2264.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2264.manifest deleted file mode 100644 index fd090f6ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2264.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2265.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2265.manifest deleted file mode 100644 index 8a0bfb1dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2265.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2266.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2266.manifest deleted file mode 100644 index d94067e13..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2266.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2267.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2267.manifest deleted file mode 100644 index 903b706bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2267.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2268.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2268.manifest deleted file mode 100644 index 0d49a486f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2268.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2269.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2269.manifest deleted file mode 100644 index 94275bc43..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2269.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/227.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/227.manifest deleted file mode 100644 index 17147a481..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/227.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2270.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2270.manifest deleted file mode 100644 index 497fc3a09..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2270.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2271.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2271.manifest deleted file mode 100644 index 7b12de2f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2271.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2272.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2272.manifest deleted file mode 100644 index afd4923d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2272.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2273.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2273.manifest deleted file mode 100644 index 3d857c607..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2273.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2274.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2274.manifest deleted file mode 100644 index d2a516c8f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2274.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2275.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2275.manifest deleted file mode 100644 index e173aad55..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2275.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2276.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2276.manifest deleted file mode 100644 index ba63b2c80..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2276.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2277.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2277.manifest deleted file mode 100644 index ed4c2f6e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2277.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2278.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2278.manifest deleted file mode 100644 index 0e09adbdd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2278.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2279.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2279.manifest deleted file mode 100644 index 7865c986e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2279.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/228.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/228.manifest deleted file mode 100644 index 09543adf0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/228.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2280.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2280.manifest deleted file mode 100644 index a9efb2318..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2280.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2281.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2281.manifest deleted file mode 100644 index e86fc513a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2281.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2282.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2282.manifest deleted file mode 100644 index 998b95e2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2282.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2283.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2283.manifest deleted file mode 100644 index 68e9904d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2283.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2284.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2284.manifest deleted file mode 100644 index 112d73605..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2284.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2285.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2285.manifest deleted file mode 100644 index 34c16f2a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2285.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2286.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2286.manifest deleted file mode 100644 index 722eb6272..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2286.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2287.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2287.manifest deleted file mode 100644 index 15390a64c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2287.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2288.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2288.manifest deleted file mode 100644 index c67a3ef0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2288.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2289.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2289.manifest deleted file mode 100644 index 735c8a255..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2289.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/229.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/229.manifest deleted file mode 100644 index 8ceb9a572..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/229.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2290.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2290.manifest deleted file mode 100644 index 41656179d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2290.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2291.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2291.manifest deleted file mode 100644 index 8ae2d8c5b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2291.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2292.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2292.manifest deleted file mode 100644 index 17fe874bd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2292.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2293.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2293.manifest deleted file mode 100644 index a8c4920f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2293.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2294.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2294.manifest deleted file mode 100644 index 7b7df9369..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2294.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2295.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2295.manifest deleted file mode 100644 index 64321d999..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2295.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2296.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2296.manifest deleted file mode 100644 index f47ed665c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2296.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2297.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2297.manifest deleted file mode 100644 index 1eec50d63..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2297.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2298.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2298.manifest deleted file mode 100644 index da4f8d601..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2298.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2299.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2299.manifest deleted file mode 100644 index 508b46349..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2299.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/23.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/23.manifest deleted file mode 100644 index e0bebde02..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/23.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/230.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/230.manifest deleted file mode 100644 index 6893ed2d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/230.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2300.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2300.manifest deleted file mode 100644 index 476215484..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2300.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2301.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2301.manifest deleted file mode 100644 index 0c094c798..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2301.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2302.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2302.manifest deleted file mode 100644 index 3b9f31559..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2302.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2303.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2303.manifest deleted file mode 100644 index 44275dec7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2303.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2304.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2304.manifest deleted file mode 100644 index f48dd16b6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2304.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2305.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2305.manifest deleted file mode 100644 index f9a77d110..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2305.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2306.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2306.manifest deleted file mode 100644 index 324811c5b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2306.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2307.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2307.manifest deleted file mode 100644 index 7cb6cb81c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2307.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2308.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2308.manifest deleted file mode 100644 index d6dea04d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2308.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2309.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2309.manifest deleted file mode 100644 index a880e6d04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2309.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/231.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/231.manifest deleted file mode 100644 index 285bd3d2c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/231.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2310.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2310.manifest deleted file mode 100644 index 1a4d228f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2310.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2311.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2311.manifest deleted file mode 100644 index 629ebd732..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2311.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2312.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2312.manifest deleted file mode 100644 index 97bad8537..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2312.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2313.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2313.manifest deleted file mode 100644 index de70e16cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2313.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2314.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2314.manifest deleted file mode 100644 index 15ee05dbf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2314.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2315.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2315.manifest deleted file mode 100644 index 967c89985..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2315.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2316.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2316.manifest deleted file mode 100644 index 29a251191..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2316.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2317.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2317.manifest deleted file mode 100644 index d926bae8a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2317.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2318.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2318.manifest deleted file mode 100644 index 89bf74b7c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2318.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2319.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2319.manifest deleted file mode 100644 index b8f8a959c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2319.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/232.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/232.manifest deleted file mode 100644 index 28313baa4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/232.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2320.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2320.manifest deleted file mode 100644 index bef3e231d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2320.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2321.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2321.manifest deleted file mode 100644 index 79b39ad62..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2321.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2322.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2322.manifest deleted file mode 100644 index c412f8cb6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2322.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2323.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2323.manifest deleted file mode 100644 index 026cd257c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2323.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2324.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2324.manifest deleted file mode 100644 index 006d366b2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2324.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2325.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2325.manifest deleted file mode 100644 index 6ee8514bd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2325.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2326.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2326.manifest deleted file mode 100644 index aca03be2c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2326.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2327.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2327.manifest deleted file mode 100644 index d81bf75b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2327.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2328.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2328.manifest deleted file mode 100644 index 3c1f81298..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2328.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2329.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2329.manifest deleted file mode 100644 index a99fbc214..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2329.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/233.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/233.manifest deleted file mode 100644 index 18967a9d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/233.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2330.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2330.manifest deleted file mode 100644 index 3b2c63d15..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2330.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2331.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2331.manifest deleted file mode 100644 index a7274aebc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2331.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2332.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2332.manifest deleted file mode 100644 index 75eda9e27..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2332.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2333.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2333.manifest deleted file mode 100644 index 3581061e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2333.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2334.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2334.manifest deleted file mode 100644 index 8e1cba8df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2334.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2335.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2335.manifest deleted file mode 100644 index 8c8ae4761..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2335.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2336.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2336.manifest deleted file mode 100644 index 5b20dc453..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2336.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2337.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2337.manifest deleted file mode 100644 index 4d995c55b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2337.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2338.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2338.manifest deleted file mode 100644 index ee07f8562..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2338.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2339.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2339.manifest deleted file mode 100644 index 0869d32ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2339.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/234.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/234.manifest deleted file mode 100644 index 7d369cc94..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/234.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2340.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2340.manifest deleted file mode 100644 index 8534d8602..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2340.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2341.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2341.manifest deleted file mode 100644 index eba50ddee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2341.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2342.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2342.manifest deleted file mode 100644 index 799e5cfac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2342.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2343.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2343.manifest deleted file mode 100644 index 110bab0d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2343.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2344.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2344.manifest deleted file mode 100644 index 9c369b775..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2344.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2345.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2345.manifest deleted file mode 100644 index 4366d3073..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2345.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2346.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2346.manifest deleted file mode 100644 index 593fa4eed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2346.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2347.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2347.manifest deleted file mode 100644 index d7f068296..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2347.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2348.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2348.manifest deleted file mode 100644 index cdfc4b699..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2348.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2349.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2349.manifest deleted file mode 100644 index b77af4173..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2349.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/235.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/235.manifest deleted file mode 100644 index 09eac1f5c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/235.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2350.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2350.manifest deleted file mode 100644 index b3e01b882..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2350.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2351.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2351.manifest deleted file mode 100644 index 6d0451273..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2351.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2352.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2352.manifest deleted file mode 100644 index 12f60df9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2352.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2353.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2353.manifest deleted file mode 100644 index 8cb210c54..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2353.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2354.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2354.manifest deleted file mode 100644 index e4f71ce2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2354.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2355.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2355.manifest deleted file mode 100644 index 8d2d2fec5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2355.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2356.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2356.manifest deleted file mode 100644 index 1e382cbde..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2356.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2357.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2357.manifest deleted file mode 100644 index 797fe69b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2357.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2358.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2358.manifest deleted file mode 100644 index 488b0e71d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2358.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2359.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2359.manifest deleted file mode 100644 index c6efaad7c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2359.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/236.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/236.manifest deleted file mode 100644 index 59a6123ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/236.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2360.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2360.manifest deleted file mode 100644 index 17b166142..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2360.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2361.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2361.manifest deleted file mode 100644 index a314e00cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2361.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2362.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2362.manifest deleted file mode 100644 index 4475b5921..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2362.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2363.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2363.manifest deleted file mode 100644 index 4b2a4fe2b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2363.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2364.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2364.manifest deleted file mode 100644 index 30dbf35ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2364.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2365.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2365.manifest deleted file mode 100644 index e4cc5818e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2365.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2366.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2366.manifest deleted file mode 100644 index e8c6e2f63..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2366.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2367.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2367.manifest deleted file mode 100644 index b305692f5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2367.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2368.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2368.manifest deleted file mode 100644 index 0ce7d6b7f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2368.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2369.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2369.manifest deleted file mode 100644 index b06ba9d28..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2369.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/237.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/237.manifest deleted file mode 100644 index a2903c1e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/237.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2370.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2370.manifest deleted file mode 100644 index 3ddfe167f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2370.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2371.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2371.manifest deleted file mode 100644 index f45aa49fe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2371.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2372.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2372.manifest deleted file mode 100644 index a9a5a1d53..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2372.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2373.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2373.manifest deleted file mode 100644 index eb84ee493..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2373.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2374.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2374.manifest deleted file mode 100644 index e7e1ee874..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2374.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2375.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2375.manifest deleted file mode 100644 index 25ac198ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2375.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2376.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2376.manifest deleted file mode 100644 index 199771f4b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2376.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2377.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2377.manifest deleted file mode 100644 index 45e884166..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2377.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2378.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2378.manifest deleted file mode 100644 index c614628d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2378.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2379.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2379.manifest deleted file mode 100644 index 6a5dd8705..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2379.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/238.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/238.manifest deleted file mode 100644 index 1cbe8dea2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/238.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2380.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2380.manifest deleted file mode 100644 index d97340d21..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2380.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2381.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2381.manifest deleted file mode 100644 index d0e96f9c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2381.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2382.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2382.manifest deleted file mode 100644 index 55ae1d9e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2382.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2383.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2383.manifest deleted file mode 100644 index 32d0c9ac1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2383.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2384.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2384.manifest deleted file mode 100644 index be0339d49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2384.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2385.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2385.manifest deleted file mode 100644 index b08e24b37..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2385.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2386.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2386.manifest deleted file mode 100644 index 9b0f61660..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2386.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2387.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2387.manifest deleted file mode 100644 index 37268fd02..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2387.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2388.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2388.manifest deleted file mode 100644 index 66ab90776..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2388.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2389.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2389.manifest deleted file mode 100644 index e9f85cf98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2389.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/239.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/239.manifest deleted file mode 100644 index d8c304602..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/239.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2390.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2390.manifest deleted file mode 100644 index 1140eb563..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2390.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2391.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2391.manifest deleted file mode 100644 index 43e758119..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2391.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2392.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2392.manifest deleted file mode 100644 index 699857794..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2392.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2393.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2393.manifest deleted file mode 100644 index 75d78def9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2393.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2394.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2394.manifest deleted file mode 100644 index 96c0d4d27..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2394.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2395.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2395.manifest deleted file mode 100644 index 5ac1803d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2395.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2396.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2396.manifest deleted file mode 100644 index d57b268d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2396.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2397.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2397.manifest deleted file mode 100644 index 0e555aff5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2397.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2398.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2398.manifest deleted file mode 100644 index 93b43e7bf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2398.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2399.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2399.manifest deleted file mode 100644 index dcf90bbda..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2399.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/24.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/24.manifest deleted file mode 100644 index 5b8ba6daa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/24.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/240.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/240.manifest deleted file mode 100644 index da60be651..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/240.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2400.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2400.manifest deleted file mode 100644 index cf072f339..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2400.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2401.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2401.manifest deleted file mode 100644 index bbe924d8f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2401.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2402.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2402.manifest deleted file mode 100644 index 1e7edb708..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2402.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2403.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2403.manifest deleted file mode 100644 index f1b01b510..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2403.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2404.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2404.manifest deleted file mode 100644 index bae2dc493..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2404.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2405.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2405.manifest deleted file mode 100644 index f90fe8b85..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2405.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2406.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2406.manifest deleted file mode 100644 index 94761c7bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2406.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2407.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2407.manifest deleted file mode 100644 index fd438b0b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2407.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2408.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2408.manifest deleted file mode 100644 index 76ca0a70b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2408.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2409.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2409.manifest deleted file mode 100644 index 5282efa0a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2409.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/241.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/241.manifest deleted file mode 100644 index d2217b611..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/241.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2410.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2410.manifest deleted file mode 100644 index e21a02335..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2410.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2411.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2411.manifest deleted file mode 100644 index 0a8d6b813..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2411.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2412.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2412.manifest deleted file mode 100644 index f931072b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2412.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2413.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2413.manifest deleted file mode 100644 index 30ae22078..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2413.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2414.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2414.manifest deleted file mode 100644 index 7e3f46dff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2414.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2415.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2415.manifest deleted file mode 100644 index b71166b5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2415.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2416.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2416.manifest deleted file mode 100644 index 384e6cbde..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2416.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2417.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2417.manifest deleted file mode 100644 index 9c19e6484..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2417.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2418.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2418.manifest deleted file mode 100644 index f8bce21f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2418.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2419.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2419.manifest deleted file mode 100644 index 837fa3ab8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2419.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/242.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/242.manifest deleted file mode 100644 index 6a395a4d6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/242.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2420.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2420.manifest deleted file mode 100644 index a54042072..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2420.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2421.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2421.manifest deleted file mode 100644 index 8c4a8e912..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2421.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2422.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2422.manifest deleted file mode 100644 index d83e992f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2422.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2423.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2423.manifest deleted file mode 100644 index 02945ad5c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2423.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2424.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2424.manifest deleted file mode 100644 index 2ca9fdef5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2424.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2425.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2425.manifest deleted file mode 100644 index a957843d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2425.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2426.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2426.manifest deleted file mode 100644 index 22b3b12a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2426.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2427.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2427.manifest deleted file mode 100644 index 073d358dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2427.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2428.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2428.manifest deleted file mode 100644 index 60a28420e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2428.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2429.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2429.manifest deleted file mode 100644 index b9e99a4fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2429.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/243.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/243.manifest deleted file mode 100644 index 9c0b727dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/243.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2430.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2430.manifest deleted file mode 100644 index af7b47262..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2430.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2431.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2431.manifest deleted file mode 100644 index 49298c041..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2431.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2432.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2432.manifest deleted file mode 100644 index e37e3bdd8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2432.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2433.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2433.manifest deleted file mode 100644 index 9b48cdfa3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2433.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2434.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2434.manifest deleted file mode 100644 index 4fdf75e99..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2434.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2435.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2435.manifest deleted file mode 100644 index 424476361..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2435.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2436.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2436.manifest deleted file mode 100644 index c54ecf7d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2436.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2437.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2437.manifest deleted file mode 100644 index 14bfe30d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2437.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2438.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2438.manifest deleted file mode 100644 index 4d78aeb85..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2438.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2439.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2439.manifest deleted file mode 100644 index 1c847c425..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2439.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/244.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/244.manifest deleted file mode 100644 index 61196ce46..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/244.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2440.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2440.manifest deleted file mode 100644 index 4b11a79d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2440.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2441.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2441.manifest deleted file mode 100644 index f7c1fa9e0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2441.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2442.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2442.manifest deleted file mode 100644 index 2124443e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2442.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2443.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2443.manifest deleted file mode 100644 index 0c14b6981..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2443.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2444.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2444.manifest deleted file mode 100644 index 95e310a30..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2444.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2445.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2445.manifest deleted file mode 100644 index bc69b7687..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2445.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2446.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2446.manifest deleted file mode 100644 index 90e9bd977..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2446.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2447.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2447.manifest deleted file mode 100644 index 2b455c819..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2447.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2448.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2448.manifest deleted file mode 100644 index 1b43f6b71..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2448.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2449.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2449.manifest deleted file mode 100644 index 13a79d8db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2449.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/245.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/245.manifest deleted file mode 100644 index da2c64ba1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/245.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2450.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2450.manifest deleted file mode 100644 index 60b73193e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2450.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2451.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2451.manifest deleted file mode 100644 index 0224fe1f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2451.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2452.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2452.manifest deleted file mode 100644 index 95dcd6d7c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2452.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2453.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2453.manifest deleted file mode 100644 index 15db6fc22..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2453.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2454.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2454.manifest deleted file mode 100644 index 04c0c73dd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2454.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2455.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2455.manifest deleted file mode 100644 index e03586231..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2455.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2456.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2456.manifest deleted file mode 100644 index 7bfb92128..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2456.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2457.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2457.manifest deleted file mode 100644 index d3112a481..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2457.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2458.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2458.manifest deleted file mode 100644 index 612074432..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2458.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2459.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2459.manifest deleted file mode 100644 index 275dc4498..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2459.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/246.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/246.manifest deleted file mode 100644 index 5d5ce6ef4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/246.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2460.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2460.manifest deleted file mode 100644 index 44bd2e7eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2460.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2461.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2461.manifest deleted file mode 100644 index 6bd87a954..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2461.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2462.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2462.manifest deleted file mode 100644 index ec06600cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2462.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2463.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2463.manifest deleted file mode 100644 index ed2ad040a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2463.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2464.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2464.manifest deleted file mode 100644 index af56621de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2464.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2465.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2465.manifest deleted file mode 100644 index cda224625..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2465.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2466.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2466.manifest deleted file mode 100644 index 54de0c282..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2466.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2467.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2467.manifest deleted file mode 100644 index 8a2c9eb6c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2467.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2468.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2468.manifest deleted file mode 100644 index 362726e86..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2468.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2469.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2469.manifest deleted file mode 100644 index 0fba57c5c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2469.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/247.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/247.manifest deleted file mode 100644 index eacb79d64..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/247.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2470.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2470.manifest deleted file mode 100644 index 8ac973a25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2470.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2471.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2471.manifest deleted file mode 100644 index 7173475da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2471.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2472.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2472.manifest deleted file mode 100644 index ee86fcd78..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2472.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2473.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2473.manifest deleted file mode 100644 index 7ef337c98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2473.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2474.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2474.manifest deleted file mode 100644 index cc89b18c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2474.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2475.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2475.manifest deleted file mode 100644 index 2a64caf4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2475.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2476.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2476.manifest deleted file mode 100644 index 2fd849964..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2476.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2477.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2477.manifest deleted file mode 100644 index 44633868d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2477.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2478.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2478.manifest deleted file mode 100644 index c1a209512..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2478.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2479.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2479.manifest deleted file mode 100644 index 862e3adde..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2479.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/248.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/248.manifest deleted file mode 100644 index dad9019d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/248.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2480.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2480.manifest deleted file mode 100644 index b6346acfd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2480.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2481.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2481.manifest deleted file mode 100644 index d67b0dd26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2481.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2482.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2482.manifest deleted file mode 100644 index aab36f328..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2482.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2483.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2483.manifest deleted file mode 100644 index 57b2a86f4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2483.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2484.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2484.manifest deleted file mode 100644 index 02d98c8ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2484.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2485.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2485.manifest deleted file mode 100644 index ac22502ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2485.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2486.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2486.manifest deleted file mode 100644 index 29b2b50be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2486.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2487.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2487.manifest deleted file mode 100644 index f9fd2050f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2487.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2488.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2488.manifest deleted file mode 100644 index e351e983d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2488.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2489.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2489.manifest deleted file mode 100644 index 59ea80fef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2489.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/249.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/249.manifest deleted file mode 100644 index c60f742bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/249.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2490.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2490.manifest deleted file mode 100644 index a7a2033bf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2490.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2491.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2491.manifest deleted file mode 100644 index d29b024da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2491.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2492.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2492.manifest deleted file mode 100644 index 4452cfde1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2492.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2493.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2493.manifest deleted file mode 100644 index d1fc98671..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2493.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2494.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2494.manifest deleted file mode 100644 index b9f0e11f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2494.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2495.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2495.manifest deleted file mode 100644 index f53b17842..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2495.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2496.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2496.manifest deleted file mode 100644 index 9a6d8534b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2496.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2497.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2497.manifest deleted file mode 100644 index 6e478d26e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2497.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2498.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2498.manifest deleted file mode 100644 index 17804e4c3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2498.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2499.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2499.manifest deleted file mode 100644 index 617d51676..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2499.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/25.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/25.manifest deleted file mode 100644 index 9c0e08984..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/25.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/250.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/250.manifest deleted file mode 100644 index 41a0f0b14..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/250.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2500.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2500.manifest deleted file mode 100644 index ddc3291ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2500.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2501.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2501.manifest deleted file mode 100644 index 5634aa65c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2501.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2502.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2502.manifest deleted file mode 100644 index 01816983b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2502.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2503.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2503.manifest deleted file mode 100644 index 70364ca03..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2503.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2504.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2504.manifest deleted file mode 100644 index 05fc20185..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2504.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2505.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2505.manifest deleted file mode 100644 index 5838daf1c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2505.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2506.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2506.manifest deleted file mode 100644 index 52e71a615..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2506.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2507.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2507.manifest deleted file mode 100644 index c56c1daa8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2507.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2508.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2508.manifest deleted file mode 100644 index 806a0cc19..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2508.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2509.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2509.manifest deleted file mode 100644 index 760d5d42f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2509.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/251.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/251.manifest deleted file mode 100644 index 0e1491717..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/251.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2510.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2510.manifest deleted file mode 100644 index e4bc32f87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2510.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2511.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2511.manifest deleted file mode 100644 index 6d6a8dd3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2511.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2512.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2512.manifest deleted file mode 100644 index def9786af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2512.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2513.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2513.manifest deleted file mode 100644 index b292a8fef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2513.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2514.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2514.manifest deleted file mode 100644 index eaad3b83b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2514.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2515.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2515.manifest deleted file mode 100644 index 36701104f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2515.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2516.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2516.manifest deleted file mode 100644 index f71be1529..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2516.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2517.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2517.manifest deleted file mode 100644 index ee71747a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2517.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2518.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2518.manifest deleted file mode 100644 index 0f0e7732d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2518.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2519.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2519.manifest deleted file mode 100644 index 4e11fa268..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2519.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/252.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/252.manifest deleted file mode 100644 index 33b81f1d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/252.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2520.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2520.manifest deleted file mode 100644 index c840cb92f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2520.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2521.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2521.manifest deleted file mode 100644 index 4110e9729..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2521.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2522.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2522.manifest deleted file mode 100644 index 9e8ba1773..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2522.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2523.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2523.manifest deleted file mode 100644 index 908e5b160..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2523.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2524.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2524.manifest deleted file mode 100644 index 560efb49c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2524.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2525.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2525.manifest deleted file mode 100644 index 5ce357f7e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2525.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2526.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2526.manifest deleted file mode 100644 index 948b1d7c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2526.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2527.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2527.manifest deleted file mode 100644 index b7f956dc5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2527.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2528.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2528.manifest deleted file mode 100644 index d0206b187..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2528.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2529.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2529.manifest deleted file mode 100644 index 9ed1adf67..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2529.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/253.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/253.manifest deleted file mode 100644 index 4286661ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/253.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2530.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2530.manifest deleted file mode 100644 index b2d35ce4c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2530.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2531.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2531.manifest deleted file mode 100644 index 786f56a33..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2531.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2532.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2532.manifest deleted file mode 100644 index 3b03c372b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2532.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2533.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2533.manifest deleted file mode 100644 index b8dc22729..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2533.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2534.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2534.manifest deleted file mode 100644 index 3b476b1af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2534.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2535.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2535.manifest deleted file mode 100644 index 8ce2bfd9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2535.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2536.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2536.manifest deleted file mode 100644 index c0312d4ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2536.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2537.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2537.manifest deleted file mode 100644 index 253a84840..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2537.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2538.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2538.manifest deleted file mode 100644 index 14db3da9d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2538.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2539.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2539.manifest deleted file mode 100644 index e174a71fe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2539.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/254.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/254.manifest deleted file mode 100644 index 1dca14c70..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/254.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2540.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2540.manifest deleted file mode 100644 index 0a75fcecb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2540.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2541.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2541.manifest deleted file mode 100644 index 8ab7371bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2541.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2542.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2542.manifest deleted file mode 100644 index 011b0fc2d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2542.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2543.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2543.manifest deleted file mode 100644 index ad815524d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2543.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2544.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2544.manifest deleted file mode 100644 index f30d7acd1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2544.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2545.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2545.manifest deleted file mode 100644 index 6b5eff16a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2545.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2546.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2546.manifest deleted file mode 100644 index e5d5a47a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2546.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2547.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2547.manifest deleted file mode 100644 index a87315527..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2547.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2548.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2548.manifest deleted file mode 100644 index 35d9eaa83..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2548.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2549.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2549.manifest deleted file mode 100644 index 929e4137f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2549.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/255.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/255.manifest deleted file mode 100644 index 20f9f3bd6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/255.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2550.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2550.manifest deleted file mode 100644 index 46290910d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2550.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2551.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2551.manifest deleted file mode 100644 index 0e9445c42..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2551.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2552.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2552.manifest deleted file mode 100644 index f982610f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2552.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2553.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2553.manifest deleted file mode 100644 index 278f1bc17..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2553.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2554.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2554.manifest deleted file mode 100644 index f5bf92c6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2554.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2555.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2555.manifest deleted file mode 100644 index 82e3cc1cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2555.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2556.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2556.manifest deleted file mode 100644 index c2aeb5951..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2556.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2557.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2557.manifest deleted file mode 100644 index 62db12f20..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2557.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2558.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2558.manifest deleted file mode 100644 index 4864543fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2558.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2559.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2559.manifest deleted file mode 100644 index 122503dbc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2559.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/256.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/256.manifest deleted file mode 100644 index 08b077a1d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/256.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2560.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2560.manifest deleted file mode 100644 index c924c8e36..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2560.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2561.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2561.manifest deleted file mode 100644 index 15308de79..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2561.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2562.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2562.manifest deleted file mode 100644 index 3b039aa4e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2562.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2563.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2563.manifest deleted file mode 100644 index 485bd7c76..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2563.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2564.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2564.manifest deleted file mode 100644 index c46808555..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2564.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2565.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2565.manifest deleted file mode 100644 index a96accf35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2565.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2566.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2566.manifest deleted file mode 100644 index 28e5e1f43..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2566.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2567.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2567.manifest deleted file mode 100644 index 8b7647e66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2567.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2568.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2568.manifest deleted file mode 100644 index 6844e2eaa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2568.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2569.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2569.manifest deleted file mode 100644 index 664b50723..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2569.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/257.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/257.manifest deleted file mode 100644 index f027b5d47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/257.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2570.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2570.manifest deleted file mode 100644 index eb65fec4d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2570.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2571.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2571.manifest deleted file mode 100644 index feaea6bbb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2571.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2572.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2572.manifest deleted file mode 100644 index d1d37de07..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2572.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2573.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2573.manifest deleted file mode 100644 index 501676325..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2573.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2574.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2574.manifest deleted file mode 100644 index cd9f029ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2574.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2575.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2575.manifest deleted file mode 100644 index 08ed25476..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2575.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2576.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2576.manifest deleted file mode 100644 index 67c6a552e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2576.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2577.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2577.manifest deleted file mode 100644 index 32cc7201e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2577.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2578.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2578.manifest deleted file mode 100644 index c5a3d72d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2578.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2579.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2579.manifest deleted file mode 100644 index 3fc1c7ea9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2579.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/258.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/258.manifest deleted file mode 100644 index 18e0c82fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/258.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2580.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2580.manifest deleted file mode 100644 index 1ad471181..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2580.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2581.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2581.manifest deleted file mode 100644 index adb136e84..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2581.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2582.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2582.manifest deleted file mode 100644 index 8f116c3c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2582.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2583.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2583.manifest deleted file mode 100644 index 3ec3766fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2583.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2584.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2584.manifest deleted file mode 100644 index a67a30fb9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2584.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2585.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2585.manifest deleted file mode 100644 index 7a8f00859..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2585.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2586.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2586.manifest deleted file mode 100644 index 14cf6dc4c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2586.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2587.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2587.manifest deleted file mode 100644 index b2b15869f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2587.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2588.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2588.manifest deleted file mode 100644 index 960316686..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2588.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2589.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2589.manifest deleted file mode 100644 index a52decfac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2589.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/259.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/259.manifest deleted file mode 100644 index 996d31f32..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/259.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2590.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2590.manifest deleted file mode 100644 index d09114f10..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2590.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2591.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2591.manifest deleted file mode 100644 index 9da17b28a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2591.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2592.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2592.manifest deleted file mode 100644 index 1d833ab8d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2592.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2593.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2593.manifest deleted file mode 100644 index cdd97ec5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2593.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2594.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2594.manifest deleted file mode 100644 index 59dd240a0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2594.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2595.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2595.manifest deleted file mode 100644 index 9834f1bf3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2595.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2596.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2596.manifest deleted file mode 100644 index d03ec6dde..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2596.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2597.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2597.manifest deleted file mode 100644 index e465d874c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2597.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2598.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2598.manifest deleted file mode 100644 index 9d0e49d66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2598.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2599.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2599.manifest deleted file mode 100644 index aea807fe1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2599.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/26.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/26.manifest deleted file mode 100644 index 806a12878..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/26.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/260.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/260.manifest deleted file mode 100644 index 364e65def..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/260.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2600.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2600.manifest deleted file mode 100644 index fd1adfab8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2600.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2601.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2601.manifest deleted file mode 100644 index f57180e00..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2601.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2602.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2602.manifest deleted file mode 100644 index 9250b0ffd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2602.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2603.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2603.manifest deleted file mode 100644 index ad979f0f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2603.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2604.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2604.manifest deleted file mode 100644 index 5f1ce3cd3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2604.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2605.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2605.manifest deleted file mode 100644 index d7fedf63a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2605.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2606.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2606.manifest deleted file mode 100644 index b7e41bfc4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2606.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2607.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2607.manifest deleted file mode 100644 index e3bef6420..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2607.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2608.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2608.manifest deleted file mode 100644 index a39d2b5dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2608.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2609.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2609.manifest deleted file mode 100644 index 877ff1ed8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2609.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/261.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/261.manifest deleted file mode 100644 index 64889a8be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/261.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2610.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2610.manifest deleted file mode 100644 index 2daee2787..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2610.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2611.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2611.manifest deleted file mode 100644 index ed9a2a3f4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2611.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2612.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2612.manifest deleted file mode 100644 index ca02457ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2612.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2613.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2613.manifest deleted file mode 100644 index b8eda5ce9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2613.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2614.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2614.manifest deleted file mode 100644 index fb8352a0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2614.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2615.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2615.manifest deleted file mode 100644 index 9c05533a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2615.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2616.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2616.manifest deleted file mode 100644 index 21b90b10e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2616.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2617.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2617.manifest deleted file mode 100644 index a34db0d83..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2617.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2618.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2618.manifest deleted file mode 100644 index 178e2d3ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2618.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2619.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2619.manifest deleted file mode 100644 index 0f74e7950..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2619.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/262.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/262.manifest deleted file mode 100644 index 39d3290d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/262.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2620.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2620.manifest deleted file mode 100644 index 770a282e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2620.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2621.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2621.manifest deleted file mode 100644 index c4fbf282f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2621.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2622.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2622.manifest deleted file mode 100644 index e160af469..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2622.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2623.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2623.manifest deleted file mode 100644 index 9ed98e7f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2623.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2624.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2624.manifest deleted file mode 100644 index b713f4694..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2624.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2625.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2625.manifest deleted file mode 100644 index f099ffaa4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2625.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2626.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2626.manifest deleted file mode 100644 index a8acc8838..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2626.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2627.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2627.manifest deleted file mode 100644 index 3313da64f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2627.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2628.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2628.manifest deleted file mode 100644 index b4030ff3f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2628.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2629.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2629.manifest deleted file mode 100644 index 6513c6aca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2629.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/263.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/263.manifest deleted file mode 100644 index c4be4a3d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/263.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2630.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2630.manifest deleted file mode 100644 index 71315c6aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2630.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2631.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2631.manifest deleted file mode 100644 index 47b5a2c57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2631.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2632.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2632.manifest deleted file mode 100644 index 2a88c6e1c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2632.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2633.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2633.manifest deleted file mode 100644 index e8cf7dc22..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2633.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2634.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2634.manifest deleted file mode 100644 index 860eee8c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2634.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2635.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2635.manifest deleted file mode 100644 index 6b11f97bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2635.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2636.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2636.manifest deleted file mode 100644 index 4006ec23d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2636.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2637.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2637.manifest deleted file mode 100644 index 8f8e38381..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2637.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2638.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2638.manifest deleted file mode 100644 index 7d10ef409..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2638.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2639.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2639.manifest deleted file mode 100644 index 30babf2ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2639.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/264.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/264.manifest deleted file mode 100644 index 093374506..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/264.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2640.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2640.manifest deleted file mode 100644 index e117e28f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2640.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2641.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2641.manifest deleted file mode 100644 index f06171322..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2641.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2642.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2642.manifest deleted file mode 100644 index 02c9842d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2642.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2643.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2643.manifest deleted file mode 100644 index ca716c7a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2643.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2644.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2644.manifest deleted file mode 100644 index 2cb0ea76b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2644.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2645.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2645.manifest deleted file mode 100644 index 1bd045d67..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2645.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2646.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2646.manifest deleted file mode 100644 index 8b6231a90..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2646.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2647.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2647.manifest deleted file mode 100644 index 81b10a5f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2647.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2648.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2648.manifest deleted file mode 100644 index 476199aaf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2648.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2649.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2649.manifest deleted file mode 100644 index e5816e0c3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2649.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/265.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/265.manifest deleted file mode 100644 index ed0384324..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/265.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2650.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2650.manifest deleted file mode 100644 index c0fa0a94b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2650.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2651.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2651.manifest deleted file mode 100644 index 0b4f7f5f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2651.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2652.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2652.manifest deleted file mode 100644 index 406fd1f23..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2652.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2653.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2653.manifest deleted file mode 100644 index 32e41f416..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2653.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2654.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2654.manifest deleted file mode 100644 index 55d8bb2b0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2654.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2655.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2655.manifest deleted file mode 100644 index 2bf93542b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2655.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2656.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2656.manifest deleted file mode 100644 index 1ce75cc6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2656.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2657.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2657.manifest deleted file mode 100644 index 49ea8fed7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2657.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2658.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2658.manifest deleted file mode 100644 index 1120d88f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2658.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2659.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2659.manifest deleted file mode 100644 index acbe039a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2659.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/266.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/266.manifest deleted file mode 100644 index 5820e5217..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/266.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2660.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2660.manifest deleted file mode 100644 index 27cf88f98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2660.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2661.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2661.manifest deleted file mode 100644 index bc1f4718f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2661.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2662.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2662.manifest deleted file mode 100644 index ceda886b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2662.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2663.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2663.manifest deleted file mode 100644 index 3379f61bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2663.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2664.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2664.manifest deleted file mode 100644 index c5870557c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2664.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2665.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2665.manifest deleted file mode 100644 index 36eba56d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2665.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2666.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2666.manifest deleted file mode 100644 index 20f4cebc5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2666.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2667.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2667.manifest deleted file mode 100644 index 7d6f4bf52..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2667.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2668.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2668.manifest deleted file mode 100644 index 151792707..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2668.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2669.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2669.manifest deleted file mode 100644 index 3347c0b3f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2669.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/267.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/267.manifest deleted file mode 100644 index 50e0f93cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/267.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2670.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2670.manifest deleted file mode 100644 index 7c6c08306..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2670.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2671.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2671.manifest deleted file mode 100644 index 2386df4bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2671.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2672.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2672.manifest deleted file mode 100644 index 3a1591bd4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2672.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2673.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2673.manifest deleted file mode 100644 index ea4d2e89a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2673.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2674.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2674.manifest deleted file mode 100644 index b956ff31b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2674.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2675.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2675.manifest deleted file mode 100644 index 15e94dc18..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2675.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2676.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2676.manifest deleted file mode 100644 index ff1b435ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2676.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2677.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2677.manifest deleted file mode 100644 index dcb17b712..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2677.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2678.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2678.manifest deleted file mode 100644 index 0e14e98f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2678.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2679.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2679.manifest deleted file mode 100644 index 290ce6627..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2679.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/268.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/268.manifest deleted file mode 100644 index dcc0d06a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/268.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2680.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2680.manifest deleted file mode 100644 index ae719eaba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2680.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2681.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2681.manifest deleted file mode 100644 index f31f432b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2681.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2682.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2682.manifest deleted file mode 100644 index 34ed3981e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2682.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2683.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2683.manifest deleted file mode 100644 index 93517a578..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2683.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2684.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2684.manifest deleted file mode 100644 index 952d6dfb5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2684.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2685.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2685.manifest deleted file mode 100644 index 5d3ef226b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2685.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2686.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2686.manifest deleted file mode 100644 index 32a755a87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2686.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2687.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2687.manifest deleted file mode 100644 index e791d9a1b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2687.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2688.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2688.manifest deleted file mode 100644 index 3e2b219e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2688.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2689.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2689.manifest deleted file mode 100644 index 9f5e89a99..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2689.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/269.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/269.manifest deleted file mode 100644 index 53b84eeb8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/269.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2690.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2690.manifest deleted file mode 100644 index b7b3c865e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2690.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2691.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2691.manifest deleted file mode 100644 index 04b47f4ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2691.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2692.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2692.manifest deleted file mode 100644 index 1db9ce9dd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2692.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2693.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2693.manifest deleted file mode 100644 index 49a8a0de3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2693.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2694.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2694.manifest deleted file mode 100644 index 3ba99252b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2694.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2695.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2695.manifest deleted file mode 100644 index 3573c27f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2695.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2696.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2696.manifest deleted file mode 100644 index 64b03f75a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2696.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2697.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2697.manifest deleted file mode 100644 index 8d9fa88b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2697.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2698.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2698.manifest deleted file mode 100644 index d56373429..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2698.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2699.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2699.manifest deleted file mode 100644 index a1f398370..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2699.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/27.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/27.manifest deleted file mode 100644 index 88410abfd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/27.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/270.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/270.manifest deleted file mode 100644 index 2b4a52435..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/270.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2700.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2700.manifest deleted file mode 100644 index 07ebd752c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2700.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2701.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2701.manifest deleted file mode 100644 index 8700279eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2701.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2702.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2702.manifest deleted file mode 100644 index f0f175aa8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2702.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2703.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2703.manifest deleted file mode 100644 index 985b4563b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2703.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2704.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2704.manifest deleted file mode 100644 index 61771c5fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2704.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2705.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2705.manifest deleted file mode 100644 index 5a3bbe866..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2705.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2706.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2706.manifest deleted file mode 100644 index a5bec17cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2706.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2707.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2707.manifest deleted file mode 100644 index ae37cc0da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2707.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2708.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2708.manifest deleted file mode 100644 index 736ca0171..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2708.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2709.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2709.manifest deleted file mode 100644 index 2e81a481d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2709.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/271.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/271.manifest deleted file mode 100644 index 1c7104410..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/271.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2710.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2710.manifest deleted file mode 100644 index c8a2b0e42..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2710.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2711.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2711.manifest deleted file mode 100644 index 0dee73473..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2711.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2712.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2712.manifest deleted file mode 100644 index 0cda42b23..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2712.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2713.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2713.manifest deleted file mode 100644 index c115d2653..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2713.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2714.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2714.manifest deleted file mode 100644 index 42f836964..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2714.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2715.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2715.manifest deleted file mode 100644 index 5fb5d3bc9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2715.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2716.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2716.manifest deleted file mode 100644 index 941659621..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2716.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2717.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2717.manifest deleted file mode 100644 index 03c703fb7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2717.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2718.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2718.manifest deleted file mode 100644 index f28cdaebe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2718.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2719.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2719.manifest deleted file mode 100644 index 8c5206e79..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2719.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/272.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/272.manifest deleted file mode 100644 index 9c2bb769c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/272.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2720.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2720.manifest deleted file mode 100644 index 64ce24acf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/2720.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/273.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/273.manifest deleted file mode 100644 index 86ebbabc5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/273.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/274.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/274.manifest deleted file mode 100644 index 85201aff6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/274.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/275.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/275.manifest deleted file mode 100644 index a2b216ef9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/275.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/276.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/276.manifest deleted file mode 100644 index bdca193d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/276.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/277.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/277.manifest deleted file mode 100644 index c093e61aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/277.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/278.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/278.manifest deleted file mode 100644 index 3b95cd371..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/278.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/279.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/279.manifest deleted file mode 100644 index a09e7a019..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/279.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/28.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/28.manifest deleted file mode 100644 index e7c37abbe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/28.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/280.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/280.manifest deleted file mode 100644 index 3d3d722ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/280.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/281.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/281.manifest deleted file mode 100644 index 8d4439a53..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/281.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/282.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/282.manifest deleted file mode 100644 index 752ac1d9c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/282.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/283.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/283.manifest deleted file mode 100644 index 6b8e38556..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/283.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/284.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/284.manifest deleted file mode 100644 index e91687420..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/284.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/285.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/285.manifest deleted file mode 100644 index e7f346ccd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/285.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/286.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/286.manifest deleted file mode 100644 index 7ee2b5839..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/286.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/287.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/287.manifest deleted file mode 100644 index d7c17769c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/287.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/288.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/288.manifest deleted file mode 100644 index bbf226b72..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/288.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/289.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/289.manifest deleted file mode 100644 index 55bd144d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/289.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/29.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/29.manifest deleted file mode 100644 index 663fdaffe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/29.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/290.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/290.manifest deleted file mode 100644 index 0b38a9dca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/290.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/291.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/291.manifest deleted file mode 100644 index ac028aed6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/291.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/292.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/292.manifest deleted file mode 100644 index c1274ab82..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/292.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/293.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/293.manifest deleted file mode 100644 index 751a50580..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/293.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/294.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/294.manifest deleted file mode 100644 index c522a165a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/294.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/295.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/295.manifest deleted file mode 100644 index 0e478c498..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/295.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/296.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/296.manifest deleted file mode 100644 index c48ecec78..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/296.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/297.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/297.manifest deleted file mode 100644 index ead2a1280..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/297.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/298.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/298.manifest deleted file mode 100644 index 627688f3c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/298.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/299.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/299.manifest deleted file mode 100644 index fd19088cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/299.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/3.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/3.manifest deleted file mode 100644 index ccf27f580..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/3.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/30.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/30.manifest deleted file mode 100644 index c805c5304..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/30.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/300.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/300.manifest deleted file mode 100644 index 7d6eef5af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/300.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/301.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/301.manifest deleted file mode 100644 index b5df84fd2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/301.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/302.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/302.manifest deleted file mode 100644 index ffdaece49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/302.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/303.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/303.manifest deleted file mode 100644 index 370ee13cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/303.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/304.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/304.manifest deleted file mode 100644 index 0765dc4b6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/304.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/305.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/305.manifest deleted file mode 100644 index fbf293006..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/305.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/306.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/306.manifest deleted file mode 100644 index e1994cc64..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/306.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/307.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/307.manifest deleted file mode 100644 index 908458d35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/307.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/308.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/308.manifest deleted file mode 100644 index 4b471eefc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/308.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/309.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/309.manifest deleted file mode 100644 index f6b5038cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/309.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/31.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/31.manifest deleted file mode 100644 index c5f87d090..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/31.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/310.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/310.manifest deleted file mode 100644 index a6c554c86..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/310.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/311.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/311.manifest deleted file mode 100644 index 8dfc3ca2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/311.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/312.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/312.manifest deleted file mode 100644 index 796f6bcfa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/312.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/313.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/313.manifest deleted file mode 100644 index 4773416b6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/313.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/314.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/314.manifest deleted file mode 100644 index 879ef09d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/314.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/315.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/315.manifest deleted file mode 100644 index a0354eb37..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/315.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/316.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/316.manifest deleted file mode 100644 index 55eff1e1f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/316.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/317.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/317.manifest deleted file mode 100644 index 742ae84be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/317.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/318.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/318.manifest deleted file mode 100644 index 9af074014..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/318.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/319.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/319.manifest deleted file mode 100644 index 4d139ed75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/319.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/32.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/32.manifest deleted file mode 100644 index 8f166051e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/32.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/320.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/320.manifest deleted file mode 100644 index 5b38c867e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/320.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/321.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/321.manifest deleted file mode 100644 index 8675ff128..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/321.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/322.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/322.manifest deleted file mode 100644 index 9df4fd9f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/322.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/323.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/323.manifest deleted file mode 100644 index 0562577bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/323.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/324.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/324.manifest deleted file mode 100644 index 913e96437..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/324.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/325.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/325.manifest deleted file mode 100644 index 095de0441..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/325.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/326.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/326.manifest deleted file mode 100644 index 22daee6b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/326.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/327.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/327.manifest deleted file mode 100644 index 160e9bf95..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/327.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/328.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/328.manifest deleted file mode 100644 index c7507d5cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/328.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/329.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/329.manifest deleted file mode 100644 index d7f73220c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/329.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/33.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/33.manifest deleted file mode 100644 index 4b191e0f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/33.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/330.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/330.manifest deleted file mode 100644 index d36b2dab9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/330.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/331.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/331.manifest deleted file mode 100644 index 6c151728b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/331.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/332.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/332.manifest deleted file mode 100644 index 6ca3d7577..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/332.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/333.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/333.manifest deleted file mode 100644 index dc0a9e3a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/333.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/334.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/334.manifest deleted file mode 100644 index 6fba4a670..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/334.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/335.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/335.manifest deleted file mode 100644 index fdcf7d26a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/335.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/336.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/336.manifest deleted file mode 100644 index 1212aca76..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/336.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/337.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/337.manifest deleted file mode 100644 index fe9a25fc3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/337.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/338.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/338.manifest deleted file mode 100644 index 14b18c387..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/338.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/339.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/339.manifest deleted file mode 100644 index 29b76db6b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/339.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/34.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/34.manifest deleted file mode 100644 index 7ecec6820..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/34.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/340.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/340.manifest deleted file mode 100644 index e5ea5402e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/340.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/341.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/341.manifest deleted file mode 100644 index 73d5661fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/341.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/342.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/342.manifest deleted file mode 100644 index 7bf598f47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/342.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/343.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/343.manifest deleted file mode 100644 index 76aa97dfb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/343.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/344.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/344.manifest deleted file mode 100644 index 4eaa38aef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/344.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/345.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/345.manifest deleted file mode 100644 index a35a12cea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/345.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/346.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/346.manifest deleted file mode 100644 index d08b416e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/346.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/347.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/347.manifest deleted file mode 100644 index b483d8496..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/347.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/348.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/348.manifest deleted file mode 100644 index ce3176b1b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/348.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/349.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/349.manifest deleted file mode 100644 index c001f2427..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/349.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/35.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/35.manifest deleted file mode 100644 index 8b2b0d1a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/35.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/350.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/350.manifest deleted file mode 100644 index 8d0089f18..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/350.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/351.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/351.manifest deleted file mode 100644 index 488c60c24..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/351.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/352.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/352.manifest deleted file mode 100644 index 851983394..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/352.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/353.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/353.manifest deleted file mode 100644 index 53e4f0096..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/353.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/354.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/354.manifest deleted file mode 100644 index cdc16d52b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/354.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/355.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/355.manifest deleted file mode 100644 index 889946920..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/355.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/356.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/356.manifest deleted file mode 100644 index 79a569b39..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/356.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/357.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/357.manifest deleted file mode 100644 index b8721a5bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/357.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/358.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/358.manifest deleted file mode 100644 index 3a8922fe9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/358.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/359.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/359.manifest deleted file mode 100644 index b0460ca10..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/359.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/36.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/36.manifest deleted file mode 100644 index 2e68d1656..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/36.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/360.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/360.manifest deleted file mode 100644 index 89f218c94..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/360.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/361.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/361.manifest deleted file mode 100644 index c8de8a85f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/361.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/362.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/362.manifest deleted file mode 100644 index 6023727b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/362.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/363.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/363.manifest deleted file mode 100644 index 7e2ed6825..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/363.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/364.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/364.manifest deleted file mode 100644 index 61b6ba9af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/364.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/365.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/365.manifest deleted file mode 100644 index 28771cab4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/365.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/366.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/366.manifest deleted file mode 100644 index 5b0fb5920..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/366.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/367.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/367.manifest deleted file mode 100644 index 3df41c933..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/367.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/368.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/368.manifest deleted file mode 100644 index 0549b614e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/368.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/369.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/369.manifest deleted file mode 100644 index aff14394b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/369.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/37.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/37.manifest deleted file mode 100644 index 092284c72..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/37.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/370.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/370.manifest deleted file mode 100644 index b9d61bf51..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/370.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/371.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/371.manifest deleted file mode 100644 index 3117a7cde..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/371.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/372.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/372.manifest deleted file mode 100644 index 79ba6d5a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/372.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/373.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/373.manifest deleted file mode 100644 index 75d8cdb41..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/373.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/374.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/374.manifest deleted file mode 100644 index 1e2e8ed1c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/374.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/375.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/375.manifest deleted file mode 100644 index b1ea0a276..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/375.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/376.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/376.manifest deleted file mode 100644 index a6474cfe1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/376.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/377.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/377.manifest deleted file mode 100644 index 04a2d43bd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/377.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/378.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/378.manifest deleted file mode 100644 index 7ce3dd57a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/378.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/379.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/379.manifest deleted file mode 100644 index 3230bce75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/379.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/38.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/38.manifest deleted file mode 100644 index 58b259582..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/38.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/380.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/380.manifest deleted file mode 100644 index fd2733f9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/380.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/381.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/381.manifest deleted file mode 100644 index 790efae95..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/381.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/382.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/382.manifest deleted file mode 100644 index e9a515b64..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/382.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/383.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/383.manifest deleted file mode 100644 index e38203f20..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/383.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/384.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/384.manifest deleted file mode 100644 index d3df7db98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/384.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/385.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/385.manifest deleted file mode 100644 index 2950b5864..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/385.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/386.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/386.manifest deleted file mode 100644 index 57e230fd3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/386.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/387.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/387.manifest deleted file mode 100644 index 10ff1236b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/387.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/388.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/388.manifest deleted file mode 100644 index 458c47038..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/388.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/389.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/389.manifest deleted file mode 100644 index a8fff63a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/389.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/39.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/39.manifest deleted file mode 100644 index 4d0515464..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/39.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/390.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/390.manifest deleted file mode 100644 index 127dc8eeb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/390.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/391.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/391.manifest deleted file mode 100644 index 784107fcf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/391.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/392.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/392.manifest deleted file mode 100644 index a670454f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/392.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/393.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/393.manifest deleted file mode 100644 index de872b47c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/393.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/394.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/394.manifest deleted file mode 100644 index 3ed4d6795..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/394.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/395.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/395.manifest deleted file mode 100644 index 3b824354d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/395.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/396.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/396.manifest deleted file mode 100644 index c1804e907..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/396.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/397.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/397.manifest deleted file mode 100644 index 180b66de5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/397.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/398.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/398.manifest deleted file mode 100644 index a0b4b857d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/398.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/399.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/399.manifest deleted file mode 100644 index 4f434ea3c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/399.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/4.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/4.manifest deleted file mode 100644 index dbd463bc1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/4.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/40.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/40.manifest deleted file mode 100644 index 82a36de90..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/40.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/400.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/400.manifest deleted file mode 100644 index bafa2dc51..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/400.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/401.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/401.manifest deleted file mode 100644 index 6f9b7d49d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/401.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/402.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/402.manifest deleted file mode 100644 index 28ef91c71..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/402.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/403.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/403.manifest deleted file mode 100644 index 129caa33d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/403.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/404.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/404.manifest deleted file mode 100644 index c09f6b204..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/404.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/405.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/405.manifest deleted file mode 100644 index 739c8d2a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/405.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/406.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/406.manifest deleted file mode 100644 index ad18fba63..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/406.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/407.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/407.manifest deleted file mode 100644 index 4f9e5e8af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/407.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/408.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/408.manifest deleted file mode 100644 index 17169f76f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/408.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/409.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/409.manifest deleted file mode 100644 index 8970aa251..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/409.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/41.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/41.manifest deleted file mode 100644 index 1ef287ef1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/41.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/410.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/410.manifest deleted file mode 100644 index dcc907bfb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/410.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/411.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/411.manifest deleted file mode 100644 index b6d2a4e79..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/411.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/412.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/412.manifest deleted file mode 100644 index 8fc450354..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/412.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/413.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/413.manifest deleted file mode 100644 index 99173be86..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/413.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/414.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/414.manifest deleted file mode 100644 index 894c86933..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/414.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/415.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/415.manifest deleted file mode 100644 index 2459f416a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/415.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/416.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/416.manifest deleted file mode 100644 index 08e972ffd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/416.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/417.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/417.manifest deleted file mode 100644 index 6bd940f8b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/417.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/418.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/418.manifest deleted file mode 100644 index 13ffa55a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/418.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/419.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/419.manifest deleted file mode 100644 index 66b8aa0a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/419.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/42.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/42.manifest deleted file mode 100644 index 3b844ad49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/42.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/420.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/420.manifest deleted file mode 100644 index ee74b899e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/420.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/421.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/421.manifest deleted file mode 100644 index 11387e011..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/421.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/422.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/422.manifest deleted file mode 100644 index 6ff30e489..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/422.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/423.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/423.manifest deleted file mode 100644 index e1ed4a6f5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/423.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/424.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/424.manifest deleted file mode 100644 index da2f09e49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/424.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/425.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/425.manifest deleted file mode 100644 index 36c693d76..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/425.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/426.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/426.manifest deleted file mode 100644 index e8535d62f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/426.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/427.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/427.manifest deleted file mode 100644 index 4ed3c5fe0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/427.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/428.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/428.manifest deleted file mode 100644 index 7c325a04a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/428.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/429.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/429.manifest deleted file mode 100644 index 81e24350f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/429.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/43.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/43.manifest deleted file mode 100644 index 01fa0b539..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/43.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/430.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/430.manifest deleted file mode 100644 index 0ae9e603a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/430.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/431.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/431.manifest deleted file mode 100644 index 7a75fb34c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/431.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/432.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/432.manifest deleted file mode 100644 index 085799255..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/432.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/433.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/433.manifest deleted file mode 100644 index 7f1977721..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/433.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/434.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/434.manifest deleted file mode 100644 index a536e461d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/434.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/435.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/435.manifest deleted file mode 100644 index fad2d8670..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/435.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/436.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/436.manifest deleted file mode 100644 index a7717ca3b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/436.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/437.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/437.manifest deleted file mode 100644 index a026c6058..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/437.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/438.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/438.manifest deleted file mode 100644 index c6ba7bb20..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/438.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/439.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/439.manifest deleted file mode 100644 index 74aed2d88..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/439.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/44.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/44.manifest deleted file mode 100644 index f86924036..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/44.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/440.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/440.manifest deleted file mode 100644 index 4ff6da54b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/440.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/441.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/441.manifest deleted file mode 100644 index d16ad38bf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/441.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/442.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/442.manifest deleted file mode 100644 index 204a32382..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/442.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/443.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/443.manifest deleted file mode 100644 index e4e5ab342..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/443.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/444.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/444.manifest deleted file mode 100644 index da5dfa052..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/444.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/445.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/445.manifest deleted file mode 100644 index 106444c48..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/445.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/446.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/446.manifest deleted file mode 100644 index 5ee8047e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/446.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/447.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/447.manifest deleted file mode 100644 index 222ccc344..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/447.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/448.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/448.manifest deleted file mode 100644 index ab998d6b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/448.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/449.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/449.manifest deleted file mode 100644 index 30a431485..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/449.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/45.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/45.manifest deleted file mode 100644 index 3c100845c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/45.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/450.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/450.manifest deleted file mode 100644 index 90158f858..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/450.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/451.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/451.manifest deleted file mode 100644 index f386fa104..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/451.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/452.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/452.manifest deleted file mode 100644 index 32c81ac10..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/452.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/453.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/453.manifest deleted file mode 100644 index 55aecce31..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/453.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/454.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/454.manifest deleted file mode 100644 index d184435a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/454.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/455.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/455.manifest deleted file mode 100644 index 0fcb81c66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/455.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/456.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/456.manifest deleted file mode 100644 index 60b626207..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/456.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/457.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/457.manifest deleted file mode 100644 index 95694494a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/457.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/458.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/458.manifest deleted file mode 100644 index 51c5c33c1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/458.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/459.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/459.manifest deleted file mode 100644 index 78ae6c23a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/459.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/46.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/46.manifest deleted file mode 100644 index 7389bc649..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/46.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/460.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/460.manifest deleted file mode 100644 index 835b62367..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/460.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/461.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/461.manifest deleted file mode 100644 index ad18c3008..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/461.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/462.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/462.manifest deleted file mode 100644 index 18ee60734..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/462.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/463.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/463.manifest deleted file mode 100644 index 0622d3fba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/463.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/464.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/464.manifest deleted file mode 100644 index dbb784510..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/464.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/465.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/465.manifest deleted file mode 100644 index 73a332083..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/465.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/466.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/466.manifest deleted file mode 100644 index 7e222faff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/466.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/467.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/467.manifest deleted file mode 100644 index 76e6710bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/467.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/468.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/468.manifest deleted file mode 100644 index a93aaed47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/468.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/469.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/469.manifest deleted file mode 100644 index 616db7d38..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/469.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/47.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/47.manifest deleted file mode 100644 index b33546245..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/47.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/470.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/470.manifest deleted file mode 100644 index 17a58f39b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/470.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/471.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/471.manifest deleted file mode 100644 index 5356ea2ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/471.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/472.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/472.manifest deleted file mode 100644 index 23eceba24..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/472.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/473.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/473.manifest deleted file mode 100644 index 643bd5470..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/473.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/474.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/474.manifest deleted file mode 100644 index c51b595ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/474.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/475.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/475.manifest deleted file mode 100644 index 3eba63501..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/475.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/476.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/476.manifest deleted file mode 100644 index 21079c336..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/476.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/477.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/477.manifest deleted file mode 100644 index 28b488e6c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/477.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/478.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/478.manifest deleted file mode 100644 index cf220b955..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/478.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/479.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/479.manifest deleted file mode 100644 index b5b6dc297..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/479.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/48.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/48.manifest deleted file mode 100644 index 0eed529e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/48.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/480.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/480.manifest deleted file mode 100644 index 10662ef2b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/480.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/481.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/481.manifest deleted file mode 100644 index 658f3d15a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/481.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/482.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/482.manifest deleted file mode 100644 index 7a80e01a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/482.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/483.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/483.manifest deleted file mode 100644 index 096a867b7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/483.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/484.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/484.manifest deleted file mode 100644 index 1ef4104e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/484.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/485.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/485.manifest deleted file mode 100644 index 4c6592ccf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/485.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/486.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/486.manifest deleted file mode 100644 index 43d832445..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/486.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/487.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/487.manifest deleted file mode 100644 index 480172e45..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/487.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/488.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/488.manifest deleted file mode 100644 index c21d626c1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/488.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/489.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/489.manifest deleted file mode 100644 index 0b77f4254..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/489.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/49.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/49.manifest deleted file mode 100644 index 63c446e87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/49.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/490.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/490.manifest deleted file mode 100644 index cb82580e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/490.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/491.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/491.manifest deleted file mode 100644 index 8ca47a798..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/491.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/492.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/492.manifest deleted file mode 100644 index 1c4358a75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/492.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/493.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/493.manifest deleted file mode 100644 index 8c27122eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/493.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/494.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/494.manifest deleted file mode 100644 index 9933c9a12..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/494.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/495.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/495.manifest deleted file mode 100644 index fae8b0ae1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/495.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/496.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/496.manifest deleted file mode 100644 index 5ccbfbb78..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/496.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/497.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/497.manifest deleted file mode 100644 index a746e04d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/497.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/498.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/498.manifest deleted file mode 100644 index 19cb64aea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/498.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/499.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/499.manifest deleted file mode 100644 index e72440eaa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/499.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/5.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/5.manifest deleted file mode 100644 index 0969c6e1b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/5.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/50.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/50.manifest deleted file mode 100644 index a06db2ff0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/50.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/500.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/500.manifest deleted file mode 100644 index 3707aedb3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/500.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/501.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/501.manifest deleted file mode 100644 index 33173412c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/501.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/502.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/502.manifest deleted file mode 100644 index 2e67b623d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/502.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/503.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/503.manifest deleted file mode 100644 index 572c79377..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/503.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/504.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/504.manifest deleted file mode 100644 index a70858e99..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/504.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/505.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/505.manifest deleted file mode 100644 index f95127ec0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/505.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/506.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/506.manifest deleted file mode 100644 index f2421b30e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/506.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/507.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/507.manifest deleted file mode 100644 index 9ce16a6c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/507.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/508.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/508.manifest deleted file mode 100644 index 280b7a3d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/508.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/509.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/509.manifest deleted file mode 100644 index fd35370e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/509.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/51.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/51.manifest deleted file mode 100644 index fb0b872bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/51.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/510.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/510.manifest deleted file mode 100644 index c1ecd9827..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/510.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/511.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/511.manifest deleted file mode 100644 index 4269e21d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/511.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/512.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/512.manifest deleted file mode 100644 index 3eb4675cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/512.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/513.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/513.manifest deleted file mode 100644 index 92842fe26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/513.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/514.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/514.manifest deleted file mode 100644 index ecd6648e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/514.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/515.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/515.manifest deleted file mode 100644 index dd5b87c69..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/515.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/516.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/516.manifest deleted file mode 100644 index c32eab780..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/516.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/517.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/517.manifest deleted file mode 100644 index 1aa0c1b2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/517.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/518.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/518.manifest deleted file mode 100644 index 330c1f129..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/518.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/519.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/519.manifest deleted file mode 100644 index dafe9b71b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/519.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/52.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/52.manifest deleted file mode 100644 index 08e458a68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/52.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/520.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/520.manifest deleted file mode 100644 index ac6cbb49d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/520.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/521.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/521.manifest deleted file mode 100644 index 8d3493457..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/521.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/522.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/522.manifest deleted file mode 100644 index eb33d2c64..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/522.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/523.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/523.manifest deleted file mode 100644 index e0a32e508..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/523.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/524.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/524.manifest deleted file mode 100644 index 566ee947d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/524.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/525.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/525.manifest deleted file mode 100644 index 071aae3d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/525.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/526.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/526.manifest deleted file mode 100644 index 57b9e6f3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/526.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/527.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/527.manifest deleted file mode 100644 index 0b3688afd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/527.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/528.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/528.manifest deleted file mode 100644 index a04f00566..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/528.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/529.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/529.manifest deleted file mode 100644 index 3863e5353..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/529.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/53.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/53.manifest deleted file mode 100644 index c3faaccbc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/53.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/530.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/530.manifest deleted file mode 100644 index e875d1047..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/530.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/531.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/531.manifest deleted file mode 100644 index 0b35849eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/531.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/532.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/532.manifest deleted file mode 100644 index 3689054a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/532.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/533.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/533.manifest deleted file mode 100644 index fbf302290..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/533.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/534.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/534.manifest deleted file mode 100644 index 0fd875d73..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/534.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/535.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/535.manifest deleted file mode 100644 index aa86b829a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/535.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/536.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/536.manifest deleted file mode 100644 index e8d2bce74..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/536.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/537.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/537.manifest deleted file mode 100644 index 6c4a76dad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/537.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/538.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/538.manifest deleted file mode 100644 index e2cc3fe78..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/538.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/539.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/539.manifest deleted file mode 100644 index cea1ef3d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/539.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/54.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/54.manifest deleted file mode 100644 index 0f7344fac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/54.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/540.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/540.manifest deleted file mode 100644 index 193fa8914..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/540.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/541.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/541.manifest deleted file mode 100644 index d1f3407cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/541.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/542.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/542.manifest deleted file mode 100644 index e480a172e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/542.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/543.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/543.manifest deleted file mode 100644 index ed225880f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/543.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/544.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/544.manifest deleted file mode 100644 index 2b7b8baf6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/544.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/545.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/545.manifest deleted file mode 100644 index 623ab2bf4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/545.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/546.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/546.manifest deleted file mode 100644 index 641d352ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/546.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/547.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/547.manifest deleted file mode 100644 index 165a6abbc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/547.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/548.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/548.manifest deleted file mode 100644 index 61e2d8f96..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/548.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/549.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/549.manifest deleted file mode 100644 index ca994d961..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/549.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/55.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/55.manifest deleted file mode 100644 index 9db3d3cde..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/55.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/550.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/550.manifest deleted file mode 100644 index 17147bc8b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/550.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/551.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/551.manifest deleted file mode 100644 index a2a216cd6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/551.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/552.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/552.manifest deleted file mode 100644 index 1d82dee0b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/552.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/553.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/553.manifest deleted file mode 100644 index 8164c3e33..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/553.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/554.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/554.manifest deleted file mode 100644 index 3d786fb96..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/554.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/555.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/555.manifest deleted file mode 100644 index f829180a6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/555.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/556.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/556.manifest deleted file mode 100644 index 9f1039621..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/556.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/557.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/557.manifest deleted file mode 100644 index 41351f554..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/557.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/558.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/558.manifest deleted file mode 100644 index b899ce7b2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/558.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/559.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/559.manifest deleted file mode 100644 index dd989c309..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/559.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/56.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/56.manifest deleted file mode 100644 index 291f21b82..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/56.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/560.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/560.manifest deleted file mode 100644 index 20250bea9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/560.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/561.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/561.manifest deleted file mode 100644 index 79c2d7138..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/561.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/562.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/562.manifest deleted file mode 100644 index 81b04a482..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/562.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/563.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/563.manifest deleted file mode 100644 index 95f58a3d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/563.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/564.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/564.manifest deleted file mode 100644 index 725c10f79..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/564.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/565.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/565.manifest deleted file mode 100644 index 992c73a01..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/565.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/566.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/566.manifest deleted file mode 100644 index 31ab5c0de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/566.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/567.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/567.manifest deleted file mode 100644 index 5256f3f76..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/567.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/568.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/568.manifest deleted file mode 100644 index d9792c466..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/568.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/569.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/569.manifest deleted file mode 100644 index 7c9261d82..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/569.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/57.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/57.manifest deleted file mode 100644 index efce6f8a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/57.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/570.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/570.manifest deleted file mode 100644 index 9d1e07993..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/570.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/571.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/571.manifest deleted file mode 100644 index 01543b982..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/571.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/572.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/572.manifest deleted file mode 100644 index 7a0c3b185..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/572.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/573.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/573.manifest deleted file mode 100644 index bc4c6fe63..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/573.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/574.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/574.manifest deleted file mode 100644 index 505995c6b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/574.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/575.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/575.manifest deleted file mode 100644 index 8f6d3ff15..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/575.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/576.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/576.manifest deleted file mode 100644 index b71ae7c90..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/576.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/577.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/577.manifest deleted file mode 100644 index 048acb4c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/577.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/578.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/578.manifest deleted file mode 100644 index 6b806d1c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/578.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/579.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/579.manifest deleted file mode 100644 index cb3394521..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/579.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/58.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/58.manifest deleted file mode 100644 index eb321798a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/58.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/580.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/580.manifest deleted file mode 100644 index 7601d7456..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/580.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/581.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/581.manifest deleted file mode 100644 index 4324fa006..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/581.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/582.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/582.manifest deleted file mode 100644 index e78a5d654..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/582.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/583.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/583.manifest deleted file mode 100644 index 47222737f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/583.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/584.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/584.manifest deleted file mode 100644 index d116a2cff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/584.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/585.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/585.manifest deleted file mode 100644 index 6f92f1572..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/585.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/586.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/586.manifest deleted file mode 100644 index bc3d456f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/586.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/587.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/587.manifest deleted file mode 100644 index a4f2e31c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/587.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/588.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/588.manifest deleted file mode 100644 index 3816a1fa5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/588.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/589.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/589.manifest deleted file mode 100644 index b0c0e3831..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/589.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/59.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/59.manifest deleted file mode 100644 index 3b08ad1af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/59.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/590.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/590.manifest deleted file mode 100644 index 370123082..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/590.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/591.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/591.manifest deleted file mode 100644 index a3d20ee26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/591.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/592.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/592.manifest deleted file mode 100644 index 9df9061e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/592.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/593.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/593.manifest deleted file mode 100644 index 49b9f15aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/593.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/594.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/594.manifest deleted file mode 100644 index f24fe3218..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/594.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/595.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/595.manifest deleted file mode 100644 index b6e4b2e78..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/595.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/596.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/596.manifest deleted file mode 100644 index 78e895b71..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/596.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/597.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/597.manifest deleted file mode 100644 index d1115d228..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/597.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/598.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/598.manifest deleted file mode 100644 index 0923f102f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/598.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/599.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/599.manifest deleted file mode 100644 index 2af88263d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/599.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/6.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/6.manifest deleted file mode 100644 index 8ca9bdfde..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/6.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/60.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/60.manifest deleted file mode 100644 index 63503b852..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/60.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/600.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/600.manifest deleted file mode 100644 index 8eba901d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/600.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/601.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/601.manifest deleted file mode 100644 index fb4d53054..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/601.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/602.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/602.manifest deleted file mode 100644 index aa9ad81cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/602.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/603.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/603.manifest deleted file mode 100644 index 0d847b9fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/603.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/604.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/604.manifest deleted file mode 100644 index 7642f2bef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/604.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/605.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/605.manifest deleted file mode 100644 index 9d213f03b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/605.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/606.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/606.manifest deleted file mode 100644 index 7c690e735..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/606.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/607.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/607.manifest deleted file mode 100644 index 64987ef37..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/607.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/608.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/608.manifest deleted file mode 100644 index 218ea6764..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/608.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/609.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/609.manifest deleted file mode 100644 index a3a87041d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/609.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/61.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/61.manifest deleted file mode 100644 index 3d0384965..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/61.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/610.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/610.manifest deleted file mode 100644 index a6a23c2e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/610.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/611.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/611.manifest deleted file mode 100644 index 1eb379c2b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/611.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/612.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/612.manifest deleted file mode 100644 index 3612ebd09..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/612.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/613.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/613.manifest deleted file mode 100644 index 124fec26b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/613.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/614.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/614.manifest deleted file mode 100644 index b311ef32f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/614.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/615.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/615.manifest deleted file mode 100644 index 63dbbc5b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/615.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/616.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/616.manifest deleted file mode 100644 index 99163c46f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/616.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/617.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/617.manifest deleted file mode 100644 index 871da832b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/617.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/618.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/618.manifest deleted file mode 100644 index eb306b026..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/618.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/619.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/619.manifest deleted file mode 100644 index afe0d14b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/619.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/62.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/62.manifest deleted file mode 100644 index 3c7fb8e21..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/62.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/620.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/620.manifest deleted file mode 100644 index 0d5a66f35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/620.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/621.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/621.manifest deleted file mode 100644 index 32de1ab8f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/621.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/622.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/622.manifest deleted file mode 100644 index 64dd82f8f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/622.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/623.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/623.manifest deleted file mode 100644 index 3cc86bfca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/623.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/624.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/624.manifest deleted file mode 100644 index a4a1b7dcd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/624.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/625.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/625.manifest deleted file mode 100644 index 78a4d1c5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/625.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/626.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/626.manifest deleted file mode 100644 index 150f8a679..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/626.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/627.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/627.manifest deleted file mode 100644 index 552e92643..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/627.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/628.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/628.manifest deleted file mode 100644 index 5b04122df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/628.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/629.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/629.manifest deleted file mode 100644 index 1475982e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/629.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/63.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/63.manifest deleted file mode 100644 index 45e5e48bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/63.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/630.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/630.manifest deleted file mode 100644 index ce743eb4d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/630.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/631.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/631.manifest deleted file mode 100644 index d5b8982e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/631.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/632.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/632.manifest deleted file mode 100644 index 2a0b62181..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/632.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/633.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/633.manifest deleted file mode 100644 index e0b69146f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/633.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/634.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/634.manifest deleted file mode 100644 index 5e2e01318..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/634.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/635.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/635.manifest deleted file mode 100644 index 18dcdd0ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/635.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/636.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/636.manifest deleted file mode 100644 index 74003805b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/636.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/637.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/637.manifest deleted file mode 100644 index f9b26eff8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/637.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/638.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/638.manifest deleted file mode 100644 index 085437011..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/638.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/639.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/639.manifest deleted file mode 100644 index 952197715..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/639.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/64.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/64.manifest deleted file mode 100644 index 22313b04a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/64.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/640.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/640.manifest deleted file mode 100644 index 7161d90ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/640.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/641.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/641.manifest deleted file mode 100644 index e469805fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/641.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/642.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/642.manifest deleted file mode 100644 index 8ec0d8e60..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/642.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/643.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/643.manifest deleted file mode 100644 index edf3db6cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/643.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/644.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/644.manifest deleted file mode 100644 index 02fbbbbc8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/644.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/645.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/645.manifest deleted file mode 100644 index 2711048d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/645.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/646.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/646.manifest deleted file mode 100644 index f10353cb2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/646.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/647.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/647.manifest deleted file mode 100644 index c5a7015d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/647.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/648.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/648.manifest deleted file mode 100644 index 9c28cf8ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/648.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/649.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/649.manifest deleted file mode 100644 index b8af9e04d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/649.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/65.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/65.manifest deleted file mode 100644 index d47072463..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/65.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/650.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/650.manifest deleted file mode 100644 index 0170a5657..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/650.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/651.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/651.manifest deleted file mode 100644 index 28eb7cf09..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/651.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/652.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/652.manifest deleted file mode 100644 index b0cca32c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/652.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/653.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/653.manifest deleted file mode 100644 index b93dac49d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/653.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/654.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/654.manifest deleted file mode 100644 index f3529e9d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/654.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/655.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/655.manifest deleted file mode 100644 index 676bbc579..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/655.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/656.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/656.manifest deleted file mode 100644 index 1c6537663..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/656.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/657.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/657.manifest deleted file mode 100644 index a67fc7090..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/657.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/658.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/658.manifest deleted file mode 100644 index 55396a310..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/658.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/659.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/659.manifest deleted file mode 100644 index 5165b253b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/659.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/66.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/66.manifest deleted file mode 100644 index dc36f8df1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/66.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/660.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/660.manifest deleted file mode 100644 index 02974b8f4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/660.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/661.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/661.manifest deleted file mode 100644 index 384f15c0c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/661.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/662.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/662.manifest deleted file mode 100644 index 42cf14bff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/662.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/663.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/663.manifest deleted file mode 100644 index 41b7b8c19..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/663.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/664.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/664.manifest deleted file mode 100644 index e291934fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/664.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/665.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/665.manifest deleted file mode 100644 index e44d220f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/665.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/666.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/666.manifest deleted file mode 100644 index d6718f439..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/666.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/667.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/667.manifest deleted file mode 100644 index 8dce5f9d6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/667.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/668.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/668.manifest deleted file mode 100644 index 484e413cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/668.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/669.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/669.manifest deleted file mode 100644 index f63db62f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/669.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/67.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/67.manifest deleted file mode 100644 index 3b4485b7c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/67.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/670.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/670.manifest deleted file mode 100644 index 2a2c35c9a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/670.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/671.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/671.manifest deleted file mode 100644 index 418a4983b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/671.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/672.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/672.manifest deleted file mode 100644 index e6530a55a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/672.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/673.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/673.manifest deleted file mode 100644 index 2843fde54..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/673.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/674.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/674.manifest deleted file mode 100644 index a381db407..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/674.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/675.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/675.manifest deleted file mode 100644 index 6c9cfafeb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/675.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/676.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/676.manifest deleted file mode 100644 index c9bdf2299..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/676.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/677.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/677.manifest deleted file mode 100644 index 4bfeffefb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/677.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/678.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/678.manifest deleted file mode 100644 index 11f595c4a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/678.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/679.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/679.manifest deleted file mode 100644 index 44689b76a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/679.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/68.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/68.manifest deleted file mode 100644 index 0a6c57db6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/68.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/680.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/680.manifest deleted file mode 100644 index 4c86fb034..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/680.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/681.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/681.manifest deleted file mode 100644 index 8190eff35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/681.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/682.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/682.manifest deleted file mode 100644 index 5d0a22e5f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/682.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/683.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/683.manifest deleted file mode 100644 index 0325787b0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/683.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/684.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/684.manifest deleted file mode 100644 index 61abc50e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/684.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/685.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/685.manifest deleted file mode 100644 index 8f20572f5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/685.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/686.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/686.manifest deleted file mode 100644 index e6fbef06c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/686.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/687.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/687.manifest deleted file mode 100644 index 634242d6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/687.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/688.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/688.manifest deleted file mode 100644 index d7e19ce05..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/688.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/689.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/689.manifest deleted file mode 100644 index c98bc60ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/689.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/69.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/69.manifest deleted file mode 100644 index c0e519677..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/69.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/690.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/690.manifest deleted file mode 100644 index d54e0eb98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/690.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/691.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/691.manifest deleted file mode 100644 index 2f8de135f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/691.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/692.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/692.manifest deleted file mode 100644 index 7bb675133..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/692.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/693.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/693.manifest deleted file mode 100644 index 18e42f2ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/693.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/694.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/694.manifest deleted file mode 100644 index 32351f2be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/694.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/695.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/695.manifest deleted file mode 100644 index a756c72f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/695.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/696.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/696.manifest deleted file mode 100644 index 9b1338e2d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/696.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/697.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/697.manifest deleted file mode 100644 index 13013591a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/697.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/698.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/698.manifest deleted file mode 100644 index 3e435ae26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/698.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/699.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/699.manifest deleted file mode 100644 index 1b93bbd16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/699.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/7.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/7.manifest deleted file mode 100644 index 6e2f90450..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/7.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/70.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/70.manifest deleted file mode 100644 index da44b318d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/70.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/700.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/700.manifest deleted file mode 100644 index 277414286..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/700.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/701.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/701.manifest deleted file mode 100644 index ff588478a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/701.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/702.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/702.manifest deleted file mode 100644 index 45e3ea5f4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/702.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/703.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/703.manifest deleted file mode 100644 index 2acf782a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/703.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/704.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/704.manifest deleted file mode 100644 index 1df59bb8a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/704.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/705.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/705.manifest deleted file mode 100644 index c40b6e7ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/705.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/706.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/706.manifest deleted file mode 100644 index e6dc7843e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/706.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/707.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/707.manifest deleted file mode 100644 index 2e0ebb9c1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/707.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/708.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/708.manifest deleted file mode 100644 index 9f9e2d7ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/708.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/709.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/709.manifest deleted file mode 100644 index 77ca7ed00..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/709.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/71.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/71.manifest deleted file mode 100644 index 80d97412f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/71.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/710.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/710.manifest deleted file mode 100644 index b8b2f5ce1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/710.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/711.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/711.manifest deleted file mode 100644 index 94f455652..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/711.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/712.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/712.manifest deleted file mode 100644 index e9bcf276e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/712.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/713.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/713.manifest deleted file mode 100644 index a29c0696f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/713.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/714.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/714.manifest deleted file mode 100644 index 2099464d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/714.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/715.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/715.manifest deleted file mode 100644 index 82448d1cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/715.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/716.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/716.manifest deleted file mode 100644 index ef08d3496..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/716.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/717.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/717.manifest deleted file mode 100644 index e9bb87872..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/717.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/718.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/718.manifest deleted file mode 100644 index c35f30d9d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/718.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/719.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/719.manifest deleted file mode 100644 index ae4ea9b67..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/719.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/72.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/72.manifest deleted file mode 100644 index 6a01f5a6f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/72.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/720.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/720.manifest deleted file mode 100644 index 2c2f28391..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/720.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/721.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/721.manifest deleted file mode 100644 index 72533ee32..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/721.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/722.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/722.manifest deleted file mode 100644 index c6f96c7d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/722.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/723.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/723.manifest deleted file mode 100644 index a8ae51f6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/723.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/724.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/724.manifest deleted file mode 100644 index ff07ebe22..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/724.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/725.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/725.manifest deleted file mode 100644 index a55f0cfb3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/725.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/726.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/726.manifest deleted file mode 100644 index 8ed978da8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/726.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/727.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/727.manifest deleted file mode 100644 index cd6c7e12e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/727.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/728.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/728.manifest deleted file mode 100644 index 07879eb9a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/728.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/729.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/729.manifest deleted file mode 100644 index 477535ab9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/729.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/73.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/73.manifest deleted file mode 100644 index 7acbdba2c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/73.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/730.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/730.manifest deleted file mode 100644 index b96cccdfe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/730.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/731.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/731.manifest deleted file mode 100644 index 9fbc43753..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/731.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/732.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/732.manifest deleted file mode 100644 index 04062c1fe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/732.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/733.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/733.manifest deleted file mode 100644 index ad68b16df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/733.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/734.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/734.manifest deleted file mode 100644 index 61cd41812..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/734.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/735.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/735.manifest deleted file mode 100644 index cff6beaf8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/735.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/736.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/736.manifest deleted file mode 100644 index 7960db08e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/736.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/737.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/737.manifest deleted file mode 100644 index 9e213c760..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/737.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/738.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/738.manifest deleted file mode 100644 index 67eabe56b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/738.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/739.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/739.manifest deleted file mode 100644 index 19995323f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/739.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/74.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/74.manifest deleted file mode 100644 index e4491ed86..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/74.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/740.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/740.manifest deleted file mode 100644 index 833c2472a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/740.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/741.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/741.manifest deleted file mode 100644 index f94b0cde9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/741.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/742.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/742.manifest deleted file mode 100644 index a916607ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/742.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/743.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/743.manifest deleted file mode 100644 index c281190be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/743.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/744.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/744.manifest deleted file mode 100644 index cfa56a3f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/744.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/745.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/745.manifest deleted file mode 100644 index bfe2cbd94..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/745.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/746.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/746.manifest deleted file mode 100644 index b9c487c79..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/746.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/747.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/747.manifest deleted file mode 100644 index f73fa9662..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/747.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/748.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/748.manifest deleted file mode 100644 index 5e5d9bf61..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/748.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/749.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/749.manifest deleted file mode 100644 index 039ce2fd4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/749.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/75.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/75.manifest deleted file mode 100644 index 44d1eee94..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/75.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/750.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/750.manifest deleted file mode 100644 index 9b15d1576..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/750.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/751.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/751.manifest deleted file mode 100644 index c12b426ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/751.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/752.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/752.manifest deleted file mode 100644 index 793b6b87f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/752.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/753.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/753.manifest deleted file mode 100644 index 7334e181a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/753.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/754.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/754.manifest deleted file mode 100644 index 01ef95d0c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/754.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/755.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/755.manifest deleted file mode 100644 index 74d8c200f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/755.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/756.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/756.manifest deleted file mode 100644 index bf737f45d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/756.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/757.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/757.manifest deleted file mode 100644 index 4c3856d1e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/757.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/758.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/758.manifest deleted file mode 100644 index 68bac2b4b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/758.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/759.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/759.manifest deleted file mode 100644 index ea1d73880..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/759.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/76.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/76.manifest deleted file mode 100644 index 9dca3e6d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/76.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/760.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/760.manifest deleted file mode 100644 index ccc48eeec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/760.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/761.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/761.manifest deleted file mode 100644 index b44d24114..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/761.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/762.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/762.manifest deleted file mode 100644 index 7c72decd8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/762.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/763.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/763.manifest deleted file mode 100644 index c772a87de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/763.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/764.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/764.manifest deleted file mode 100644 index 5a7caa059..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/764.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/765.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/765.manifest deleted file mode 100644 index 17d6c063b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/765.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/766.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/766.manifest deleted file mode 100644 index 62098ef15..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/766.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/767.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/767.manifest deleted file mode 100644 index f4eb4d697..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/767.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/768.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/768.manifest deleted file mode 100644 index 44d1f604f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/768.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/769.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/769.manifest deleted file mode 100644 index bf0c6308d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/769.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/77.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/77.manifest deleted file mode 100644 index 231e6151e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/77.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/770.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/770.manifest deleted file mode 100644 index 61e846629..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/770.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/771.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/771.manifest deleted file mode 100644 index 9b0221f36..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/771.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/772.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/772.manifest deleted file mode 100644 index 4d70d037c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/772.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/773.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/773.manifest deleted file mode 100644 index 1c2c38893..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/773.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/774.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/774.manifest deleted file mode 100644 index 5e6c0b886..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/774.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/775.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/775.manifest deleted file mode 100644 index 92437e450..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/775.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/776.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/776.manifest deleted file mode 100644 index fb1f350df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/776.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/777.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/777.manifest deleted file mode 100644 index f3331785c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/777.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/778.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/778.manifest deleted file mode 100644 index 93149526c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/778.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/779.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/779.manifest deleted file mode 100644 index d96a96b6c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/779.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/78.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/78.manifest deleted file mode 100644 index d3a9162af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/78.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/780.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/780.manifest deleted file mode 100644 index 03ffb5b8a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/780.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/781.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/781.manifest deleted file mode 100644 index 20f8c52e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/781.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/782.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/782.manifest deleted file mode 100644 index 7bde80b5f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/782.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/783.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/783.manifest deleted file mode 100644 index fa17ffccf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/783.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/784.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/784.manifest deleted file mode 100644 index bfce9c35c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/784.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/785.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/785.manifest deleted file mode 100644 index e6f06aec3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/785.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/786.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/786.manifest deleted file mode 100644 index 3c49f40e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/786.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/787.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/787.manifest deleted file mode 100644 index 960e80853..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/787.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/788.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/788.manifest deleted file mode 100644 index 7cf724ee3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/788.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/789.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/789.manifest deleted file mode 100644 index 70019db1b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/789.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/79.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/79.manifest deleted file mode 100644 index 40eaf0b5b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/79.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/790.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/790.manifest deleted file mode 100644 index a995adcf5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/790.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/791.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/791.manifest deleted file mode 100644 index 530dc7aa4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/791.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/792.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/792.manifest deleted file mode 100644 index 9c4c1025e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/792.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/793.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/793.manifest deleted file mode 100644 index 0a0f64ab2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/793.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/794.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/794.manifest deleted file mode 100644 index 67fe3f37c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/794.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/795.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/795.manifest deleted file mode 100644 index 4131e27ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/795.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/796.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/796.manifest deleted file mode 100644 index 609e721f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/796.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/797.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/797.manifest deleted file mode 100644 index a35eadedf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/797.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/798.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/798.manifest deleted file mode 100644 index 20377811f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/798.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/799.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/799.manifest deleted file mode 100644 index 8d2ec25cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/799.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/8.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/8.manifest deleted file mode 100644 index fd8facdde..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/8.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/80.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/80.manifest deleted file mode 100644 index 2e42a804d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/80.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/800.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/800.manifest deleted file mode 100644 index 9104e13d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/800.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/801.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/801.manifest deleted file mode 100644 index e370e0ddb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/801.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/802.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/802.manifest deleted file mode 100644 index 44349f24d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/802.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/803.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/803.manifest deleted file mode 100644 index d69d241bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/803.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/804.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/804.manifest deleted file mode 100644 index 770178405..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/804.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/805.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/805.manifest deleted file mode 100644 index 83fe38534..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/805.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/806.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/806.manifest deleted file mode 100644 index 04f55aa62..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/806.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/807.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/807.manifest deleted file mode 100644 index e6f4c93ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/807.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/808.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/808.manifest deleted file mode 100644 index d1f0747d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/808.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/809.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/809.manifest deleted file mode 100644 index 3c4970716..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/809.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/81.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/81.manifest deleted file mode 100644 index aa0181ed1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/81.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/810.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/810.manifest deleted file mode 100644 index 42b6e67e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/810.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/811.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/811.manifest deleted file mode 100644 index 87105fc47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/811.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/812.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/812.manifest deleted file mode 100644 index 607bb088c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/812.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/813.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/813.manifest deleted file mode 100644 index 287d3aa71..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/813.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/814.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/814.manifest deleted file mode 100644 index e1a797161..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/814.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/815.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/815.manifest deleted file mode 100644 index 41dcf4a59..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/815.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/816.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/816.manifest deleted file mode 100644 index 5b156dc6e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/816.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/817.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/817.manifest deleted file mode 100644 index ff96da38f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/817.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/818.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/818.manifest deleted file mode 100644 index 6c4d264b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/818.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/819.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/819.manifest deleted file mode 100644 index 212b667f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/819.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/82.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/82.manifest deleted file mode 100644 index 3456c6e9c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/82.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/820.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/820.manifest deleted file mode 100644 index 5c35fe185..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/820.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/821.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/821.manifest deleted file mode 100644 index c6bf60533..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/821.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/822.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/822.manifest deleted file mode 100644 index 0ae7e1b50..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/822.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/823.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/823.manifest deleted file mode 100644 index 9d336321f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/823.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/824.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/824.manifest deleted file mode 100644 index 79276fca8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/824.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/825.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/825.manifest deleted file mode 100644 index 28c64a6e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/825.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/826.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/826.manifest deleted file mode 100644 index 63d2a86e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/826.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/827.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/827.manifest deleted file mode 100644 index fd21f77da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/827.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/828.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/828.manifest deleted file mode 100644 index 2a196a7cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/828.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/829.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/829.manifest deleted file mode 100644 index 3a87fd773..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/829.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/83.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/83.manifest deleted file mode 100644 index da2e713ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/83.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/830.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/830.manifest deleted file mode 100644 index 6e02f3ed2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/830.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/831.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/831.manifest deleted file mode 100644 index 6679bb700..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/831.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/832.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/832.manifest deleted file mode 100644 index 21119dfaf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/832.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/833.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/833.manifest deleted file mode 100644 index 95b5a0a2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/833.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/834.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/834.manifest deleted file mode 100644 index 836e87e3a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/834.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/835.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/835.manifest deleted file mode 100644 index 2cb0169e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/835.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/836.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/836.manifest deleted file mode 100644 index 42dc77cac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/836.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/837.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/837.manifest deleted file mode 100644 index b67f4c902..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/837.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/838.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/838.manifest deleted file mode 100644 index 79a6cc1a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/838.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/839.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/839.manifest deleted file mode 100644 index 971daca4b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/839.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/84.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/84.manifest deleted file mode 100644 index 6ae254e93..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/84.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/840.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/840.manifest deleted file mode 100644 index fff49635b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/840.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/841.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/841.manifest deleted file mode 100644 index 80ecdd982..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/841.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/842.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/842.manifest deleted file mode 100644 index a73d09cf3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/842.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/843.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/843.manifest deleted file mode 100644 index 8cb8901ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/843.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/844.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/844.manifest deleted file mode 100644 index 3b66f6566..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/844.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/845.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/845.manifest deleted file mode 100644 index b8cba4479..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/845.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/846.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/846.manifest deleted file mode 100644 index 93bc737e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/846.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/847.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/847.manifest deleted file mode 100644 index 633c17301..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/847.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/848.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/848.manifest deleted file mode 100644 index 5be3fc46a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/848.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/849.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/849.manifest deleted file mode 100644 index 1ddc4d029..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/849.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/85.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/85.manifest deleted file mode 100644 index 42383f84c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/85.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/850.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/850.manifest deleted file mode 100644 index 1602d9323..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/850.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/851.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/851.manifest deleted file mode 100644 index 557f84215..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/851.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/852.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/852.manifest deleted file mode 100644 index 714aae82e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/852.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/853.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/853.manifest deleted file mode 100644 index a0472c126..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/853.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/854.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/854.manifest deleted file mode 100644 index 6117d8f0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/854.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/855.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/855.manifest deleted file mode 100644 index 6d99514cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/855.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/856.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/856.manifest deleted file mode 100644 index 127bd9e06..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/856.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/857.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/857.manifest deleted file mode 100644 index 6abc2739d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/857.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/858.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/858.manifest deleted file mode 100644 index a473c6e5b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/858.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/859.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/859.manifest deleted file mode 100644 index eb5eaf5a0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/859.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/86.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/86.manifest deleted file mode 100644 index c99b30687..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/86.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/860.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/860.manifest deleted file mode 100644 index c329ea983..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/860.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/861.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/861.manifest deleted file mode 100644 index aea063846..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/861.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/862.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/862.manifest deleted file mode 100644 index 0d706ca02..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/862.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/863.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/863.manifest deleted file mode 100644 index dd3ebe0d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/863.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/864.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/864.manifest deleted file mode 100644 index 49c858efb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/864.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/865.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/865.manifest deleted file mode 100644 index 307a0cea6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/865.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/866.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/866.manifest deleted file mode 100644 index fb9d62461..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/866.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/867.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/867.manifest deleted file mode 100644 index ddbde36b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/867.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/868.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/868.manifest deleted file mode 100644 index 4af62fa43..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/868.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/869.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/869.manifest deleted file mode 100644 index 9b52e79bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/869.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/87.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/87.manifest deleted file mode 100644 index 0614e23ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/87.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/870.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/870.manifest deleted file mode 100644 index 8552a539e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/870.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/871.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/871.manifest deleted file mode 100644 index 72ef4d911..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/871.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/872.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/872.manifest deleted file mode 100644 index ab1f21a57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/872.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/873.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/873.manifest deleted file mode 100644 index e8415c4dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/873.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/874.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/874.manifest deleted file mode 100644 index 50483a156..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/874.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/875.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/875.manifest deleted file mode 100644 index dddfbee46..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/875.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/876.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/876.manifest deleted file mode 100644 index b72b5d2fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/876.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/877.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/877.manifest deleted file mode 100644 index e18d65eb4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/877.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/878.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/878.manifest deleted file mode 100644 index 327c4cebe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/878.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/879.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/879.manifest deleted file mode 100644 index 585bc5e88..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/879.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/88.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/88.manifest deleted file mode 100644 index 9e9d32165..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/88.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/880.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/880.manifest deleted file mode 100644 index 6fa593e51..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/880.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/881.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/881.manifest deleted file mode 100644 index 2063f59a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/881.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/882.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/882.manifest deleted file mode 100644 index 9afaad6b7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/882.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/883.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/883.manifest deleted file mode 100644 index 012de73ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/883.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/884.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/884.manifest deleted file mode 100644 index 7239ffe21..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/884.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/885.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/885.manifest deleted file mode 100644 index bd2f194dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/885.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/886.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/886.manifest deleted file mode 100644 index 6ea202b34..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/886.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/887.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/887.manifest deleted file mode 100644 index a5214d900..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/887.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/888.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/888.manifest deleted file mode 100644 index 4e0d80688..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/888.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/889.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/889.manifest deleted file mode 100644 index a082ff3ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/889.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/89.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/89.manifest deleted file mode 100644 index a02fd982e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/89.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/890.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/890.manifest deleted file mode 100644 index 012535beb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/890.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/891.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/891.manifest deleted file mode 100644 index 8ed0d3b8d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/891.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/892.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/892.manifest deleted file mode 100644 index 3e146b63d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/892.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/893.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/893.manifest deleted file mode 100644 index 1353fa81f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/893.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/894.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/894.manifest deleted file mode 100644 index 0af556777..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/894.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/895.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/895.manifest deleted file mode 100644 index 44632522c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/895.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/896.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/896.manifest deleted file mode 100644 index f0e4a026e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/896.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/897.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/897.manifest deleted file mode 100644 index 6d13f5589..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/897.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/898.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/898.manifest deleted file mode 100644 index f465b40fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/898.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/899.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/899.manifest deleted file mode 100644 index 9211c3469..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/899.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/9.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/9.manifest deleted file mode 100644 index e15fcc58c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/9.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/90.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/90.manifest deleted file mode 100644 index 38488e9d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/90.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/900.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/900.manifest deleted file mode 100644 index aa201511b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/900.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/901.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/901.manifest deleted file mode 100644 index 8d6148183..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/901.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/902.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/902.manifest deleted file mode 100644 index cfd9972e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/902.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/903.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/903.manifest deleted file mode 100644 index d1a0c1291..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/903.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/904.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/904.manifest deleted file mode 100644 index 9f59a4e02..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/904.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/905.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/905.manifest deleted file mode 100644 index be0143bb9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/905.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/906.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/906.manifest deleted file mode 100644 index 6dd036645..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/906.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/907.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/907.manifest deleted file mode 100644 index 5d973e99d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/907.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/908.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/908.manifest deleted file mode 100644 index 23bbf28d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/908.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/909.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/909.manifest deleted file mode 100644 index 4df78720c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/909.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/91.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/91.manifest deleted file mode 100644 index 7821535d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/91.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/910.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/910.manifest deleted file mode 100644 index 5a6d23258..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/910.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/911.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/911.manifest deleted file mode 100644 index f5821d31c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/911.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/912.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/912.manifest deleted file mode 100644 index c23d7ded5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/912.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/913.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/913.manifest deleted file mode 100644 index e8775cfe9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/913.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/914.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/914.manifest deleted file mode 100644 index 3518c5737..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/914.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/915.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/915.manifest deleted file mode 100644 index ff700ba1f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/915.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/916.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/916.manifest deleted file mode 100644 index de4547eb1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/916.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/917.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/917.manifest deleted file mode 100644 index 6ce129e8f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/917.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/918.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/918.manifest deleted file mode 100644 index d75bb2642..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/918.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/919.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/919.manifest deleted file mode 100644 index 708f0aa09..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/919.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/92.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/92.manifest deleted file mode 100644 index 27d763993..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/92.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/920.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/920.manifest deleted file mode 100644 index 21f510df1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/920.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/921.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/921.manifest deleted file mode 100644 index e0a5aa23e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/921.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/922.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/922.manifest deleted file mode 100644 index e8cce3685..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/922.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/923.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/923.manifest deleted file mode 100644 index d28c6730a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/923.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/924.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/924.manifest deleted file mode 100644 index 211a8873c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/924.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/925.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/925.manifest deleted file mode 100644 index 2c3b44149..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/925.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/926.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/926.manifest deleted file mode 100644 index 47e804814..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/926.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/927.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/927.manifest deleted file mode 100644 index 189adea63..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/927.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/928.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/928.manifest deleted file mode 100644 index 9bd918471..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/928.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/929.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/929.manifest deleted file mode 100644 index 46e15c96f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/929.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/93.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/93.manifest deleted file mode 100644 index 14f9457fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/93.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/930.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/930.manifest deleted file mode 100644 index 7bb6d7a29..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/930.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/931.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/931.manifest deleted file mode 100644 index aca25867f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/931.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/932.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/932.manifest deleted file mode 100644 index a07a621fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/932.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/933.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/933.manifest deleted file mode 100644 index 957b07f12..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/933.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/934.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/934.manifest deleted file mode 100644 index 70e3708bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/934.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/935.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/935.manifest deleted file mode 100644 index 157171f54..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/935.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/936.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/936.manifest deleted file mode 100644 index df4577a9b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/936.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/937.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/937.manifest deleted file mode 100644 index 9414cddf7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/937.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/938.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/938.manifest deleted file mode 100644 index 087f68aa9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/938.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/939.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/939.manifest deleted file mode 100644 index ae685c4cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/939.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/94.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/94.manifest deleted file mode 100644 index 3c001768a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/94.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/940.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/940.manifest deleted file mode 100644 index 7636e4b83..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/940.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/941.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/941.manifest deleted file mode 100644 index a070fece6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/941.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/942.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/942.manifest deleted file mode 100644 index bc25f59f4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/942.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/943.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/943.manifest deleted file mode 100644 index d83e94c93..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/943.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/944.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/944.manifest deleted file mode 100644 index 4b7527acd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/944.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/945.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/945.manifest deleted file mode 100644 index 64e56dc86..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/945.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/946.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/946.manifest deleted file mode 100644 index fd5b1513c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/946.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/947.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/947.manifest deleted file mode 100644 index c17133164..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/947.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/948.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/948.manifest deleted file mode 100644 index 04375a8b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/948.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/949.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/949.manifest deleted file mode 100644 index 6677dc7de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/949.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/95.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/95.manifest deleted file mode 100644 index 24f945c0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/95.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/950.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/950.manifest deleted file mode 100644 index b729cf188..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/950.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/951.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/951.manifest deleted file mode 100644 index c227e365f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/951.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/952.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/952.manifest deleted file mode 100644 index f9e402aee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/952.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/953.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/953.manifest deleted file mode 100644 index 200617a81..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/953.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/954.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/954.manifest deleted file mode 100644 index b0d8ec62b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/954.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/955.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/955.manifest deleted file mode 100644 index afc7fd842..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/955.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/956.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/956.manifest deleted file mode 100644 index 9ec4af146..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/956.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/957.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/957.manifest deleted file mode 100644 index 74ae62a5c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/957.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/958.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/958.manifest deleted file mode 100644 index 89325ac92..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/958.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/959.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/959.manifest deleted file mode 100644 index a6664a4b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/959.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/96.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/96.manifest deleted file mode 100644 index defd9ca35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/96.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/960.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/960.manifest deleted file mode 100644 index daa950fa1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/960.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/961.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/961.manifest deleted file mode 100644 index 5679f395e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/961.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/962.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/962.manifest deleted file mode 100644 index 6e349b972..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/962.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/963.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/963.manifest deleted file mode 100644 index 5da232b4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/963.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/964.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/964.manifest deleted file mode 100644 index eb00e8abf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/964.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/965.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/965.manifest deleted file mode 100644 index 27a389d93..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/965.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/966.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/966.manifest deleted file mode 100644 index 466e36ebd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/966.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/967.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/967.manifest deleted file mode 100644 index 138945fda..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/967.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/968.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/968.manifest deleted file mode 100644 index 6c30569cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/968.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/969.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/969.manifest deleted file mode 100644 index 776e3b4c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/969.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/97.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/97.manifest deleted file mode 100644 index 4060ebd78..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/97.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/970.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/970.manifest deleted file mode 100644 index 9ca63e7b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/970.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/971.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/971.manifest deleted file mode 100644 index 568f347a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/971.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/972.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/972.manifest deleted file mode 100644 index c43f60824..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/972.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/973.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/973.manifest deleted file mode 100644 index 53a557b0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/973.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/974.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/974.manifest deleted file mode 100644 index 8177cf8c1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/974.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/975.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/975.manifest deleted file mode 100644 index 608d8e8c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/975.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/976.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/976.manifest deleted file mode 100644 index e2b1f42d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/976.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/977.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/977.manifest deleted file mode 100644 index efd09d1b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/977.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/978.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/978.manifest deleted file mode 100644 index de831d6a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/978.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/979.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/979.manifest deleted file mode 100644 index 937d04837..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/979.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/98.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/98.manifest deleted file mode 100644 index f617b14a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/98.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/980.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/980.manifest deleted file mode 100644 index 76fd70a00..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/980.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/981.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/981.manifest deleted file mode 100644 index 10978935c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/981.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/982.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/982.manifest deleted file mode 100644 index 2bba074fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/982.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/983.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/983.manifest deleted file mode 100644 index 2a143c7ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/983.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/984.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/984.manifest deleted file mode 100644 index 996283b04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/984.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/985.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/985.manifest deleted file mode 100644 index 107755c59..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/985.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/986.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/986.manifest deleted file mode 100644 index 90f91c1c2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/986.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/987.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/987.manifest deleted file mode 100644 index af1c290e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/987.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/988.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/988.manifest deleted file mode 100644 index 7f21b9506..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/988.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/989.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/989.manifest deleted file mode 100644 index ea1640f7c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/989.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/99.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/99.manifest deleted file mode 100644 index 1b5ebb543..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/99.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/990.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/990.manifest deleted file mode 100644 index fe5cb8489..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/990.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/991.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/991.manifest deleted file mode 100644 index 1829beb02..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/991.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/992.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/992.manifest deleted file mode 100644 index df856dc8a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/992.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/993.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/993.manifest deleted file mode 100644 index 931ddafa4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/993.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/994.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/994.manifest deleted file mode 100644 index 42947e03e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/994.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/995.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/995.manifest deleted file mode 100644 index de4fb4620..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/995.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/996.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/996.manifest deleted file mode 100644 index 70a691460..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/996.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/997.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/997.manifest deleted file mode 100644 index f197f2c5d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/997.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/998.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/998.manifest deleted file mode 100644 index 9782bd74c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/998.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/999.manifest b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/999.manifest deleted file mode 100644 index e9454a9fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/_versions/999.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0045bf62-0648-4ed1-9a3d-00fe5ba8993b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0045bf62-0648-4ed1-9a3d-00fe5ba8993b.lance deleted file mode 100644 index 64b13ad7c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0045bf62-0648-4ed1-9a3d-00fe5ba8993b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/005634d5-0649-4302-9b0d-224bb2a9a4a6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/005634d5-0649-4302-9b0d-224bb2a9a4a6.lance deleted file mode 100644 index 281b52893..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/005634d5-0649-4302-9b0d-224bb2a9a4a6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0078ff05-70a2-4c50-94ef-6bee56d449e3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0078ff05-70a2-4c50-94ef-6bee56d449e3.lance deleted file mode 100644 index 80b7affff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0078ff05-70a2-4c50-94ef-6bee56d449e3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/007fd991-9923-416a-a9f9-acd348dca8f0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/007fd991-9923-416a-a9f9-acd348dca8f0.lance deleted file mode 100644 index 8a65c001f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/007fd991-9923-416a-a9f9-acd348dca8f0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/008f01b0-ae6f-471e-a498-0205ff362de4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/008f01b0-ae6f-471e-a498-0205ff362de4.lance deleted file mode 100644 index f6b93f839..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/008f01b0-ae6f-471e-a498-0205ff362de4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0091033e-0b41-415d-a83f-b87b57ee0e05.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0091033e-0b41-415d-a83f-b87b57ee0e05.lance deleted file mode 100644 index e1d9584b6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0091033e-0b41-415d-a83f-b87b57ee0e05.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/009d667f-9579-4448-b855-6ed1974250fd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/009d667f-9579-4448-b855-6ed1974250fd.lance deleted file mode 100644 index 76a49d427..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/009d667f-9579-4448-b855-6ed1974250fd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00af3bfb-4ec8-4b90-9b3e-971b2e2d2ca9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00af3bfb-4ec8-4b90-9b3e-971b2e2d2ca9.lance deleted file mode 100644 index 07fa57faa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00af3bfb-4ec8-4b90-9b3e-971b2e2d2ca9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00bf473a-913d-4977-b7e0-22a9d079f4ef.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00bf473a-913d-4977-b7e0-22a9d079f4ef.lance deleted file mode 100644 index 84fc84181..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00bf473a-913d-4977-b7e0-22a9d079f4ef.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00c00259-1004-4000-97a7-d680c6376e31.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00c00259-1004-4000-97a7-d680c6376e31.lance deleted file mode 100644 index e962c93e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00c00259-1004-4000-97a7-d680c6376e31.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00d49304-fa46-44d6-982c-48cff7923d5f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00d49304-fa46-44d6-982c-48cff7923d5f.lance deleted file mode 100644 index b2243a3cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00d49304-fa46-44d6-982c-48cff7923d5f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00ed57c1-3966-4e43-a5b5-c13f8982604c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00ed57c1-3966-4e43-a5b5-c13f8982604c.lance deleted file mode 100644 index 247ef75ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00ed57c1-3966-4e43-a5b5-c13f8982604c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00f703ad-0311-402e-a8e5-e89edc9b50dd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00f703ad-0311-402e-a8e5-e89edc9b50dd.lance deleted file mode 100644 index 3e65d411e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/00f703ad-0311-402e-a8e5-e89edc9b50dd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/01247723-902d-4659-bde4-9cf54942682a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/01247723-902d-4659-bde4-9cf54942682a.lance deleted file mode 100644 index 30bfaeafe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/01247723-902d-4659-bde4-9cf54942682a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/013ecc2c-ae79-4466-8f23-1664ad2f19b4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/013ecc2c-ae79-4466-8f23-1664ad2f19b4.lance deleted file mode 100644 index d6e22e7d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/013ecc2c-ae79-4466-8f23-1664ad2f19b4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/016c9659-e8c2-4738-9b13-032e0a428ebd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/016c9659-e8c2-4738-9b13-032e0a428ebd.lance deleted file mode 100644 index 09b7db92c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/016c9659-e8c2-4738-9b13-032e0a428ebd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/017988e5-78bb-4fac-8e7a-25fe84ef7a25.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/017988e5-78bb-4fac-8e7a-25fe84ef7a25.lance deleted file mode 100644 index 11fa138bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/017988e5-78bb-4fac-8e7a-25fe84ef7a25.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/019b3907-3534-45e4-89d6-9bb112271124.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/019b3907-3534-45e4-89d6-9bb112271124.lance deleted file mode 100644 index af5d6c572..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/019b3907-3534-45e4-89d6-9bb112271124.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/01be4091-237d-4191-aeb1-4ab4e55e08da.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/01be4091-237d-4191-aeb1-4ab4e55e08da.lance deleted file mode 100644 index d9cbec746..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/01be4091-237d-4191-aeb1-4ab4e55e08da.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/01d14c55-5342-403c-a1f3-11637c1424ca.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/01d14c55-5342-403c-a1f3-11637c1424ca.lance deleted file mode 100644 index 6719d64f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/01d14c55-5342-403c-a1f3-11637c1424ca.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/01f920ef-cdf4-4498-98fc-66656f7ace16.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/01f920ef-cdf4-4498-98fc-66656f7ace16.lance deleted file mode 100644 index 58566b1d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/01f920ef-cdf4-4498-98fc-66656f7ace16.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/01fc44e8-27f0-4b84-b1e9-f3298272ec06.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/01fc44e8-27f0-4b84-b1e9-f3298272ec06.lance deleted file mode 100644 index 1861af68e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/01fc44e8-27f0-4b84-b1e9-f3298272ec06.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0206ebaa-1a5b-4263-b127-fcc03e949c36.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0206ebaa-1a5b-4263-b127-fcc03e949c36.lance deleted file mode 100644 index 2c4fef72c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0206ebaa-1a5b-4263-b127-fcc03e949c36.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0213e9a6-2bb4-4894-9608-524918e6d20d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0213e9a6-2bb4-4894-9608-524918e6d20d.lance deleted file mode 100644 index a1f3905a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0213e9a6-2bb4-4894-9608-524918e6d20d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/023d8f9b-23eb-48f7-b25d-c9467998e73e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/023d8f9b-23eb-48f7-b25d-c9467998e73e.lance deleted file mode 100644 index c4d7d8cd0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/023d8f9b-23eb-48f7-b25d-c9467998e73e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0243b524-3c6b-40f1-b6b8-2b51af9d7a05.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0243b524-3c6b-40f1-b6b8-2b51af9d7a05.lance deleted file mode 100644 index a14432e13..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0243b524-3c6b-40f1-b6b8-2b51af9d7a05.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/02ab6161-0c91-4119-9b25-21fe314d230f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/02ab6161-0c91-4119-9b25-21fe314d230f.lance deleted file mode 100644 index 612e693af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/02ab6161-0c91-4119-9b25-21fe314d230f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/02adb2a4-3ff5-4bb5-b8aa-8e118be23f18.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/02adb2a4-3ff5-4bb5-b8aa-8e118be23f18.lance deleted file mode 100644 index 801f1a7c3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/02adb2a4-3ff5-4bb5-b8aa-8e118be23f18.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/02c99db3-ac61-4082-88f5-75ddb0ea0111.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/02c99db3-ac61-4082-88f5-75ddb0ea0111.lance deleted file mode 100644 index 8e946c39e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/02c99db3-ac61-4082-88f5-75ddb0ea0111.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/02f7ffe2-ae82-402d-af03-c2f97e4b6a23.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/02f7ffe2-ae82-402d-af03-c2f97e4b6a23.lance deleted file mode 100644 index 1fcee1e22..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/02f7ffe2-ae82-402d-af03-c2f97e4b6a23.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0316f5ee-2c8b-4b4e-a998-d3b069661b37.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0316f5ee-2c8b-4b4e-a998-d3b069661b37.lance deleted file mode 100644 index 29a7ecc2f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0316f5ee-2c8b-4b4e-a998-d3b069661b37.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/033c3f27-71ce-4752-897d-a9bc8f6e1e9e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/033c3f27-71ce-4752-897d-a9bc8f6e1e9e.lance deleted file mode 100644 index 02282102e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/033c3f27-71ce-4752-897d-a9bc8f6e1e9e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/03419874-a858-408a-8847-0d3ac7ed89ec.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/03419874-a858-408a-8847-0d3ac7ed89ec.lance deleted file mode 100644 index 70203bcc7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/03419874-a858-408a-8847-0d3ac7ed89ec.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/038843f4-5308-4147-b9ed-75ec67690263.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/038843f4-5308-4147-b9ed-75ec67690263.lance deleted file mode 100644 index 6a0c41dad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/038843f4-5308-4147-b9ed-75ec67690263.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/038beaac-4312-4da0-b650-bd95033d6522.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/038beaac-4312-4da0-b650-bd95033d6522.lance deleted file mode 100644 index d01e9f0d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/038beaac-4312-4da0-b650-bd95033d6522.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/03a688d2-8bc6-4665-b943-d247fa22623d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/03a688d2-8bc6-4665-b943-d247fa22623d.lance deleted file mode 100644 index a44ccec0b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/03a688d2-8bc6-4665-b943-d247fa22623d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/03a709c7-d1bd-40ee-a917-5461f23c89dd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/03a709c7-d1bd-40ee-a917-5461f23c89dd.lance deleted file mode 100644 index 29999235c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/03a709c7-d1bd-40ee-a917-5461f23c89dd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/049aa613-a68f-410d-beab-40032c33ed24.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/049aa613-a68f-410d-beab-40032c33ed24.lance deleted file mode 100644 index 79db0933d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/049aa613-a68f-410d-beab-40032c33ed24.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/04d19593-20ef-4c01-ac1f-04366a5cf7c3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/04d19593-20ef-4c01-ac1f-04366a5cf7c3.lance deleted file mode 100644 index 4776e497c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/04d19593-20ef-4c01-ac1f-04366a5cf7c3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/04e8cb28-324a-4ac6-866c-ad9676ad7e9a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/04e8cb28-324a-4ac6-866c-ad9676ad7e9a.lance deleted file mode 100644 index c422796b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/04e8cb28-324a-4ac6-866c-ad9676ad7e9a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/04ec26ae-3168-4f9b-85df-b497e051dbc3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/04ec26ae-3168-4f9b-85df-b497e051dbc3.lance deleted file mode 100644 index 0478fb590..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/04ec26ae-3168-4f9b-85df-b497e051dbc3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/04f2e6ee-0c07-43e2-a042-1ef6cef6187a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/04f2e6ee-0c07-43e2-a042-1ef6cef6187a.lance deleted file mode 100644 index 4a19ded6c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/04f2e6ee-0c07-43e2-a042-1ef6cef6187a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/04f5528d-bda2-4cf4-9118-b2103a091f04.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/04f5528d-bda2-4cf4-9118-b2103a091f04.lance deleted file mode 100644 index 71bd806ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/04f5528d-bda2-4cf4-9118-b2103a091f04.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05245632-6c29-4671-8d6a-10e0eb2371e1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05245632-6c29-4671-8d6a-10e0eb2371e1.lance deleted file mode 100644 index dd7fee522..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05245632-6c29-4671-8d6a-10e0eb2371e1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0539896b-9994-4a47-ba9b-7296048bbf35.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0539896b-9994-4a47-ba9b-7296048bbf35.lance deleted file mode 100644 index a1f40c18b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0539896b-9994-4a47-ba9b-7296048bbf35.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0570db58-c01c-4f2e-9e11-9ceb11431f22.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0570db58-c01c-4f2e-9e11-9ceb11431f22.lance deleted file mode 100644 index dc4afa571..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0570db58-c01c-4f2e-9e11-9ceb11431f22.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05880a39-8566-4cf4-93e9-676901f6f172.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05880a39-8566-4cf4-93e9-676901f6f172.lance deleted file mode 100644 index 42ff90c43..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05880a39-8566-4cf4-93e9-676901f6f172.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/059c31d4-55ad-4bf3-9f75-59b3eb0bd0bc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/059c31d4-55ad-4bf3-9f75-59b3eb0bd0bc.lance deleted file mode 100644 index fa376593e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/059c31d4-55ad-4bf3-9f75-59b3eb0bd0bc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05a17b06-6972-4660-b7fa-315ae918bd50.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05a17b06-6972-4660-b7fa-315ae918bd50.lance deleted file mode 100644 index 69bb4f96c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05a17b06-6972-4660-b7fa-315ae918bd50.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05ad97e0-49ed-43c8-9a44-5ef9d1b612e6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05ad97e0-49ed-43c8-9a44-5ef9d1b612e6.lance deleted file mode 100644 index 321b5ba9d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05ad97e0-49ed-43c8-9a44-5ef9d1b612e6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05b59547-e9b4-4ee2-a461-26ba91ba2f4d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05b59547-e9b4-4ee2-a461-26ba91ba2f4d.lance deleted file mode 100644 index 387eede1a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05b59547-e9b4-4ee2-a461-26ba91ba2f4d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05b628ea-6cc3-450a-86d0-b7bb6d84f7be.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05b628ea-6cc3-450a-86d0-b7bb6d84f7be.lance deleted file mode 100644 index 991102b66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05b628ea-6cc3-450a-86d0-b7bb6d84f7be.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05dff068-92b0-46ce-8f07-3b0747f80182.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05dff068-92b0-46ce-8f07-3b0747f80182.lance deleted file mode 100644 index 7bfc60490..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05dff068-92b0-46ce-8f07-3b0747f80182.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05ed5245-5b63-404b-b9ee-1e5d95317de5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05ed5245-5b63-404b-b9ee-1e5d95317de5.lance deleted file mode 100644 index 9f7e05a29..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05ed5245-5b63-404b-b9ee-1e5d95317de5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05f7fe36-e489-4af8-a954-f8a5801dfdf1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05f7fe36-e489-4af8-a954-f8a5801dfdf1.lance deleted file mode 100644 index e5901b320..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/05f7fe36-e489-4af8-a954-f8a5801dfdf1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0603330f-7fee-4217-bcc4-ee5fb88d8e66.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0603330f-7fee-4217-bcc4-ee5fb88d8e66.lance deleted file mode 100644 index d42cd49f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0603330f-7fee-4217-bcc4-ee5fb88d8e66.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0617defa-14ad-40b5-90cb-9cf25f641955.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0617defa-14ad-40b5-90cb-9cf25f641955.lance deleted file mode 100644 index 7f30b5e64..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0617defa-14ad-40b5-90cb-9cf25f641955.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/06214038-eed2-48a7-ac90-c9e9e34df0bb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/06214038-eed2-48a7-ac90-c9e9e34df0bb.lance deleted file mode 100644 index 9e1f2dee8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/06214038-eed2-48a7-ac90-c9e9e34df0bb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/06249b34-52a7-4e00-834b-fb89149d9361.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/06249b34-52a7-4e00-834b-fb89149d9361.lance deleted file mode 100644 index ab119e218..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/06249b34-52a7-4e00-834b-fb89149d9361.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/067e055a-eeb3-481c-928c-5fa8aa8e49f5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/067e055a-eeb3-481c-928c-5fa8aa8e49f5.lance deleted file mode 100644 index 08e26194f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/067e055a-eeb3-481c-928c-5fa8aa8e49f5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0709f472-dba0-4584-9333-1e3c61859f17.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0709f472-dba0-4584-9333-1e3c61859f17.lance deleted file mode 100644 index 43b278bf3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0709f472-dba0-4584-9333-1e3c61859f17.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07307af4-1ea4-42f1-86c9-0e13df94dd40.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07307af4-1ea4-42f1-86c9-0e13df94dd40.lance deleted file mode 100644 index a43033d76..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07307af4-1ea4-42f1-86c9-0e13df94dd40.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/073450fa-0c8d-41a3-b928-b55bd9ed4f97.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/073450fa-0c8d-41a3-b928-b55bd9ed4f97.lance deleted file mode 100644 index 42397daaa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/073450fa-0c8d-41a3-b928-b55bd9ed4f97.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0745af37-fc7a-44b2-9cc5-465eb560f614.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0745af37-fc7a-44b2-9cc5-465eb560f614.lance deleted file mode 100644 index 176e13f81..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0745af37-fc7a-44b2-9cc5-465eb560f614.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07468791-4bfd-4d61-adaa-30e2dc87f07c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07468791-4bfd-4d61-adaa-30e2dc87f07c.lance deleted file mode 100644 index 105e51467..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07468791-4bfd-4d61-adaa-30e2dc87f07c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/074bfd43-6d42-4bb1-a319-57b9f2be5c53.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/074bfd43-6d42-4bb1-a319-57b9f2be5c53.lance deleted file mode 100644 index d063bba52..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/074bfd43-6d42-4bb1-a319-57b9f2be5c53.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07749964-d52d-4239-bcb7-c3ec7ad27513.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07749964-d52d-4239-bcb7-c3ec7ad27513.lance deleted file mode 100644 index 4192d7b7f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07749964-d52d-4239-bcb7-c3ec7ad27513.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07826cc8-51a3-42ce-b11b-1043b67c3ec5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07826cc8-51a3-42ce-b11b-1043b67c3ec5.lance deleted file mode 100644 index 70191c345..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07826cc8-51a3-42ce-b11b-1043b67c3ec5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07abea1e-689b-45d3-9c3f-4d377212d7af.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07abea1e-689b-45d3-9c3f-4d377212d7af.lance deleted file mode 100644 index 39ef861c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07abea1e-689b-45d3-9c3f-4d377212d7af.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07b8560e-8972-46c8-adb8-e7e7f7c19d20.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07b8560e-8972-46c8-adb8-e7e7f7c19d20.lance deleted file mode 100644 index d65f9d9b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/07b8560e-8972-46c8-adb8-e7e7f7c19d20.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/081b9ebb-c02d-423a-9928-35200ac4e093.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/081b9ebb-c02d-423a-9928-35200ac4e093.lance deleted file mode 100644 index 8261903a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/081b9ebb-c02d-423a-9928-35200ac4e093.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/081c9df5-f8da-4c65-8ca0-faf49c150cc6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/081c9df5-f8da-4c65-8ca0-faf49c150cc6.lance deleted file mode 100644 index 4ea64b1db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/081c9df5-f8da-4c65-8ca0-faf49c150cc6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/082f00ca-e201-4c02-889f-6c36638fee6f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/082f00ca-e201-4c02-889f-6c36638fee6f.lance deleted file mode 100644 index 58ef35fde..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/082f00ca-e201-4c02-889f-6c36638fee6f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/08391452-dc06-4c82-a1c4-60aa61b93063.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/08391452-dc06-4c82-a1c4-60aa61b93063.lance deleted file mode 100644 index 898671a35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/08391452-dc06-4c82-a1c4-60aa61b93063.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/083e26d9-a3ec-4b11-9503-6b4f0cc7cb6a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/083e26d9-a3ec-4b11-9503-6b4f0cc7cb6a.lance deleted file mode 100644 index 0f57b1cbf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/083e26d9-a3ec-4b11-9503-6b4f0cc7cb6a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/08a1bfd3-dad0-4413-9ba8-1c606cd700ad.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/08a1bfd3-dad0-4413-9ba8-1c606cd700ad.lance deleted file mode 100644 index 44e2a5d68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/08a1bfd3-dad0-4413-9ba8-1c606cd700ad.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/08a56a60-4674-45b7-802e-e023e40d72bc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/08a56a60-4674-45b7-802e-e023e40d72bc.lance deleted file mode 100644 index 2f8d21e8f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/08a56a60-4674-45b7-802e-e023e40d72bc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/08ccf584-0294-4f92-b898-67b9753dda2f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/08ccf584-0294-4f92-b898-67b9753dda2f.lance deleted file mode 100644 index 5bdc83855..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/08ccf584-0294-4f92-b898-67b9753dda2f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/08db613d-0dd6-4c0a-889b-3b88bd5f4e02.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/08db613d-0dd6-4c0a-889b-3b88bd5f4e02.lance deleted file mode 100644 index 27d185afa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/08db613d-0dd6-4c0a-889b-3b88bd5f4e02.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/091a42c7-ee47-434f-ae61-b9f9bb2ec0ac.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/091a42c7-ee47-434f-ae61-b9f9bb2ec0ac.lance deleted file mode 100644 index c8ce8ea98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/091a42c7-ee47-434f-ae61-b9f9bb2ec0ac.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0920fd72-73b4-4629-9366-a4a3ba3eb447.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0920fd72-73b4-4629-9366-a4a3ba3eb447.lance deleted file mode 100644 index 7e1454151..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0920fd72-73b4-4629-9366-a4a3ba3eb447.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/096b61cc-ad49-443f-8d31-f94a960511f0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/096b61cc-ad49-443f-8d31-f94a960511f0.lance deleted file mode 100644 index 427d95913..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/096b61cc-ad49-443f-8d31-f94a960511f0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/09887a8d-09ef-4381-b30e-9ac36dabe0cd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/09887a8d-09ef-4381-b30e-9ac36dabe0cd.lance deleted file mode 100644 index 42bfa1d8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/09887a8d-09ef-4381-b30e-9ac36dabe0cd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/099089b1-e8db-4269-baef-248c5c0c82b3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/099089b1-e8db-4269-baef-248c5c0c82b3.lance deleted file mode 100644 index 508310264..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/099089b1-e8db-4269-baef-248c5c0c82b3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/09be84c7-3ebf-4f05-99f1-eac7902139a6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/09be84c7-3ebf-4f05-99f1-eac7902139a6.lance deleted file mode 100644 index a5b181c11..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/09be84c7-3ebf-4f05-99f1-eac7902139a6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a111e8e-f310-4a53-8a0b-3c12a21161c2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a111e8e-f310-4a53-8a0b-3c12a21161c2.lance deleted file mode 100644 index 6f5e66957..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a111e8e-f310-4a53-8a0b-3c12a21161c2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a1c8b94-6df6-4988-b343-89e698b04eb8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a1c8b94-6df6-4988-b343-89e698b04eb8.lance deleted file mode 100644 index aaaf64ec2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a1c8b94-6df6-4988-b343-89e698b04eb8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a2cdd87-39af-4451-a216-5012c8d72fba.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a2cdd87-39af-4451-a216-5012c8d72fba.lance deleted file mode 100644 index 8988fb63e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a2cdd87-39af-4451-a216-5012c8d72fba.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a36485f-e163-4d67-9e05-71640c29e304.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a36485f-e163-4d67-9e05-71640c29e304.lance deleted file mode 100644 index 9c6e22119..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a36485f-e163-4d67-9e05-71640c29e304.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a50a26c-b7a1-49bf-84b2-4da8c00633b6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a50a26c-b7a1-49bf-84b2-4da8c00633b6.lance deleted file mode 100644 index e558b639f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a50a26c-b7a1-49bf-84b2-4da8c00633b6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a75a0ff-77bb-4396-aefd-c54995d07a34.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a75a0ff-77bb-4396-aefd-c54995d07a34.lance deleted file mode 100644 index e55f0a45f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0a75a0ff-77bb-4396-aefd-c54995d07a34.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0ac7780a-7279-4798-a2ae-c1a0c36b0d73.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0ac7780a-7279-4798-a2ae-c1a0c36b0d73.lance deleted file mode 100644 index 88f9a543b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0ac7780a-7279-4798-a2ae-c1a0c36b0d73.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0add901a-2c2f-4dda-91c5-5af73ac8c7b7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0add901a-2c2f-4dda-91c5-5af73ac8c7b7.lance deleted file mode 100644 index 589cadbb4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0add901a-2c2f-4dda-91c5-5af73ac8c7b7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b11f498-e89b-41db-bc48-442a55b3dc76.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b11f498-e89b-41db-bc48-442a55b3dc76.lance deleted file mode 100644 index 7c89fb48e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b11f498-e89b-41db-bc48-442a55b3dc76.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b2de82d-3731-4eee-8a5a-ccfe088a59fb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b2de82d-3731-4eee-8a5a-ccfe088a59fb.lance deleted file mode 100644 index 48201a59b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b2de82d-3731-4eee-8a5a-ccfe088a59fb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b33a94d-bf67-4664-9996-7aa80b77c02d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b33a94d-bf67-4664-9996-7aa80b77c02d.lance deleted file mode 100644 index 7e83b2d41..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b33a94d-bf67-4664-9996-7aa80b77c02d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b4e2807-a364-4246-93f1-b0958f896f3a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b4e2807-a364-4246-93f1-b0958f896f3a.lance deleted file mode 100644 index dc66ad3c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b4e2807-a364-4246-93f1-b0958f896f3a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b54b234-e6bf-471d-98c1-484bc1807734.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b54b234-e6bf-471d-98c1-484bc1807734.lance deleted file mode 100644 index a458b059c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b54b234-e6bf-471d-98c1-484bc1807734.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b610376-f92e-44c2-9168-ec52dd04ce12.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b610376-f92e-44c2-9168-ec52dd04ce12.lance deleted file mode 100644 index d357cf10d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b610376-f92e-44c2-9168-ec52dd04ce12.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b611d52-2e82-4e6b-8fa5-b9ffc7a32d6c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b611d52-2e82-4e6b-8fa5-b9ffc7a32d6c.lance deleted file mode 100644 index 891dbbb4d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b611d52-2e82-4e6b-8fa5-b9ffc7a32d6c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b7ea3f3-8d17-456f-8a9f-8954f245b48f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b7ea3f3-8d17-456f-8a9f-8954f245b48f.lance deleted file mode 100644 index d4ba55465..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b7ea3f3-8d17-456f-8a9f-8954f245b48f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b87b1e2-18ad-4856-ba7e-d088d1579ee5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b87b1e2-18ad-4856-ba7e-d088d1579ee5.lance deleted file mode 100644 index 29ae9a26e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b87b1e2-18ad-4856-ba7e-d088d1579ee5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b9478e9-9d30-4b31-bb7c-293db672d2db.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b9478e9-9d30-4b31-bb7c-293db672d2db.lance deleted file mode 100644 index c8213eea6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0b9478e9-9d30-4b31-bb7c-293db672d2db.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0bcb21b0-97ef-433c-913f-8220b906fce0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0bcb21b0-97ef-433c-913f-8220b906fce0.lance deleted file mode 100644 index c475adb0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0bcb21b0-97ef-433c-913f-8220b906fce0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0bda755b-8d68-43f8-b124-1825d6f923dd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0bda755b-8d68-43f8-b124-1825d6f923dd.lance deleted file mode 100644 index b993e3b7d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0bda755b-8d68-43f8-b124-1825d6f923dd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0bdd5d34-5683-4e9b-befe-f0142ca4ad3b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0bdd5d34-5683-4e9b-befe-f0142ca4ad3b.lance deleted file mode 100644 index 9194954fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0bdd5d34-5683-4e9b-befe-f0142ca4ad3b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0bf0fec5-d50c-4627-b514-ee4d41e2f3ea.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0bf0fec5-d50c-4627-b514-ee4d41e2f3ea.lance deleted file mode 100644 index 5e23b1986..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0bf0fec5-d50c-4627-b514-ee4d41e2f3ea.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0c411437-7842-4234-a283-4b24e132ff00.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0c411437-7842-4234-a283-4b24e132ff00.lance deleted file mode 100644 index 3b89c24de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0c411437-7842-4234-a283-4b24e132ff00.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0c4a62df-9abc-4b80-9691-93f970757622.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0c4a62df-9abc-4b80-9691-93f970757622.lance deleted file mode 100644 index ccbe1b604..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0c4a62df-9abc-4b80-9691-93f970757622.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0c5941b5-5b91-4722-8892-bcf4d60f7d70.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0c5941b5-5b91-4722-8892-bcf4d60f7d70.lance deleted file mode 100644 index 7eb1a8ef5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0c5941b5-5b91-4722-8892-bcf4d60f7d70.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0c77c1aa-9c73-4cf6-acb1-c3347ccca64f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0c77c1aa-9c73-4cf6-acb1-c3347ccca64f.lance deleted file mode 100644 index 187eb1a70..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0c77c1aa-9c73-4cf6-acb1-c3347ccca64f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0ca8d436-6cca-4141-959b-a910cd497f35.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0ca8d436-6cca-4141-959b-a910cd497f35.lance deleted file mode 100644 index 4e16b59e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0ca8d436-6cca-4141-959b-a910cd497f35.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0d24c47c-0611-4508-8155-4204ca8e6d6e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0d24c47c-0611-4508-8155-4204ca8e6d6e.lance deleted file mode 100644 index 68c0ee963..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0d24c47c-0611-4508-8155-4204ca8e6d6e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0d48832d-fc8d-4c3d-a566-80a765bcd000.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0d48832d-fc8d-4c3d-a566-80a765bcd000.lance deleted file mode 100644 index 48e5aa7d6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0d48832d-fc8d-4c3d-a566-80a765bcd000.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0d52afa2-7b9b-49cc-8fff-ba6b23c639fe.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0d52afa2-7b9b-49cc-8fff-ba6b23c639fe.lance deleted file mode 100644 index e92660399..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0d52afa2-7b9b-49cc-8fff-ba6b23c639fe.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0d8e7f18-3e9d-46ac-b942-d3c8d70d9df0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0d8e7f18-3e9d-46ac-b942-d3c8d70d9df0.lance deleted file mode 100644 index cbb678b81..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0d8e7f18-3e9d-46ac-b942-d3c8d70d9df0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0defa268-65af-40f5-9f1b-e4e616cc674a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0defa268-65af-40f5-9f1b-e4e616cc674a.lance deleted file mode 100644 index cc3e69e09..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0defa268-65af-40f5-9f1b-e4e616cc674a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0df377d5-425c-41a4-a8e9-28d7edb5ba42.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0df377d5-425c-41a4-a8e9-28d7edb5ba42.lance deleted file mode 100644 index 4489d63dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0df377d5-425c-41a4-a8e9-28d7edb5ba42.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0dfd521c-8a45-4418-a5a3-cb11d7a925ba.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0dfd521c-8a45-4418-a5a3-cb11d7a925ba.lance deleted file mode 100644 index dc8abaaad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0dfd521c-8a45-4418-a5a3-cb11d7a925ba.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0e15ad9a-3552-4a64-bd9e-1219abff8ed6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0e15ad9a-3552-4a64-bd9e-1219abff8ed6.lance deleted file mode 100644 index 84ebeda58..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0e15ad9a-3552-4a64-bd9e-1219abff8ed6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0e32be50-5ea5-4aa9-935d-fe09167191ca.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0e32be50-5ea5-4aa9-935d-fe09167191ca.lance deleted file mode 100644 index 0fa63f316..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0e32be50-5ea5-4aa9-935d-fe09167191ca.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0e3b7e60-5795-4266-b95d-cd36fc4448b8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0e3b7e60-5795-4266-b95d-cd36fc4448b8.lance deleted file mode 100644 index 37f36d393..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0e3b7e60-5795-4266-b95d-cd36fc4448b8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0e4d07e4-2c98-406d-89de-a0ef0be2d2db.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0e4d07e4-2c98-406d-89de-a0ef0be2d2db.lance deleted file mode 100644 index 40c6afc34..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0e4d07e4-2c98-406d-89de-a0ef0be2d2db.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0e9e12cb-c0b5-44c5-8c04-4e7d4a06d642.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0e9e12cb-c0b5-44c5-8c04-4e7d4a06d642.lance deleted file mode 100644 index fc13edff5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0e9e12cb-c0b5-44c5-8c04-4e7d4a06d642.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0ea2f9da-97f6-4de9-b6cd-86968b0164b0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0ea2f9da-97f6-4de9-b6cd-86968b0164b0.lance deleted file mode 100644 index adada5bf5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0ea2f9da-97f6-4de9-b6cd-86968b0164b0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0eddf2ff-22f7-4f80-8415-eea5283b2a9c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0eddf2ff-22f7-4f80-8415-eea5283b2a9c.lance deleted file mode 100644 index d62624913..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0eddf2ff-22f7-4f80-8415-eea5283b2a9c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f1a2e36-ce49-4e42-afa1-8d98e7b617b0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f1a2e36-ce49-4e42-afa1-8d98e7b617b0.lance deleted file mode 100644 index 81cc6d3c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f1a2e36-ce49-4e42-afa1-8d98e7b617b0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f222c51-22ad-46c2-a5cc-4b3723e635e8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f222c51-22ad-46c2-a5cc-4b3723e635e8.lance deleted file mode 100644 index 6c4e173a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f222c51-22ad-46c2-a5cc-4b3723e635e8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f4da6bd-aa81-4906-b998-902146d754f9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f4da6bd-aa81-4906-b998-902146d754f9.lance deleted file mode 100644 index 3e7534b06..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f4da6bd-aa81-4906-b998-902146d754f9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f735456-ce78-4b4b-a350-c450c9a6e255.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f735456-ce78-4b4b-a350-c450c9a6e255.lance deleted file mode 100644 index 989d78bf8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f735456-ce78-4b4b-a350-c450c9a6e255.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f75d639-6a1e-4745-89c9-f9e75bf4743b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f75d639-6a1e-4745-89c9-f9e75bf4743b.lance deleted file mode 100644 index dc81437c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f75d639-6a1e-4745-89c9-f9e75bf4743b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f78ae95-ca8f-4afc-bac2-046ae668196f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f78ae95-ca8f-4afc-bac2-046ae668196f.lance deleted file mode 100644 index c1efb72b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f78ae95-ca8f-4afc-bac2-046ae668196f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f8ec68d-9016-468a-91c5-9a13d0e1ec4c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f8ec68d-9016-468a-91c5-9a13d0e1ec4c.lance deleted file mode 100644 index 635a91c20..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f8ec68d-9016-468a-91c5-9a13d0e1ec4c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f962770-6bff-4e44-a929-01a2f7ca932d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f962770-6bff-4e44-a929-01a2f7ca932d.lance deleted file mode 100644 index 961a6c036..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0f962770-6bff-4e44-a929-01a2f7ca932d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fa28267-e2ad-4672-82b0-79aa7d0996dd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fa28267-e2ad-4672-82b0-79aa7d0996dd.lance deleted file mode 100644 index 3aefc2b60..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fa28267-e2ad-4672-82b0-79aa7d0996dd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fa375ca-78af-4fe4-a226-2eb348c458d9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fa375ca-78af-4fe4-a226-2eb348c458d9.lance deleted file mode 100644 index 21bc6e800..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fa375ca-78af-4fe4-a226-2eb348c458d9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fa96c7f-8a3b-43b7-9bee-eeaf24271f0a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fa96c7f-8a3b-43b7-9bee-eeaf24271f0a.lance deleted file mode 100644 index 36fca47d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fa96c7f-8a3b-43b7-9bee-eeaf24271f0a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fac21c2-3df2-42ce-8ad9-771fc83a665d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fac21c2-3df2-42ce-8ad9-771fc83a665d.lance deleted file mode 100644 index ba6aa0976..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fac21c2-3df2-42ce-8ad9-771fc83a665d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fb72bc9-99b1-4893-b11f-a539f48afd1a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fb72bc9-99b1-4893-b11f-a539f48afd1a.lance deleted file mode 100644 index a2cfd319f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fb72bc9-99b1-4893-b11f-a539f48afd1a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fb802c5-268c-423d-9c83-ff7a385fe23d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fb802c5-268c-423d-9c83-ff7a385fe23d.lance deleted file mode 100644 index f48cc17e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fb802c5-268c-423d-9c83-ff7a385fe23d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fcdf30d-ae19-45bf-8be6-e34ebf5e914d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fcdf30d-ae19-45bf-8be6-e34ebf5e914d.lance deleted file mode 100644 index 607fb7936..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fcdf30d-ae19-45bf-8be6-e34ebf5e914d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fd0f991-8975-4dbd-b3c6-17db222021db.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fd0f991-8975-4dbd-b3c6-17db222021db.lance deleted file mode 100644 index 59a3ae637..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fd0f991-8975-4dbd-b3c6-17db222021db.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fd9ad22-bd19-46d4-b36a-838547672bd6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fd9ad22-bd19-46d4-b36a-838547672bd6.lance deleted file mode 100644 index ac1cb6ccd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fd9ad22-bd19-46d4-b36a-838547672bd6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fda6137-499a-4056-8835-0769750f31a9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fda6137-499a-4056-8835-0769750f31a9.lance deleted file mode 100644 index ce576d44d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/0fda6137-499a-4056-8835-0769750f31a9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/100b3335-f1ec-45d4-85b2-e567dfb897b6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/100b3335-f1ec-45d4-85b2-e567dfb897b6.lance deleted file mode 100644 index 54c861962..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/100b3335-f1ec-45d4-85b2-e567dfb897b6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/101328cf-e72e-4ae1-adbd-efe1132e8fd0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/101328cf-e72e-4ae1-adbd-efe1132e8fd0.lance deleted file mode 100644 index d1b3c7bf9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/101328cf-e72e-4ae1-adbd-efe1132e8fd0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10223e37-93d3-4357-820c-f89435ea668e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10223e37-93d3-4357-820c-f89435ea668e.lance deleted file mode 100644 index 7bc10d90c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10223e37-93d3-4357-820c-f89435ea668e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10321401-1784-465f-9d74-b3cf2d4836b2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10321401-1784-465f-9d74-b3cf2d4836b2.lance deleted file mode 100644 index 10af29932..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10321401-1784-465f-9d74-b3cf2d4836b2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1078b162-8486-42d3-98dd-6560ebc54dee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1078b162-8486-42d3-98dd-6560ebc54dee.lance deleted file mode 100644 index f05fb121d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1078b162-8486-42d3-98dd-6560ebc54dee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/107a820d-0404-4e69-9279-ec81432ddd6e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/107a820d-0404-4e69-9279-ec81432ddd6e.lance deleted file mode 100644 index a906392e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/107a820d-0404-4e69-9279-ec81432ddd6e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10a26eaa-99ae-479f-968e-b36ca1a0e583.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10a26eaa-99ae-479f-968e-b36ca1a0e583.lance deleted file mode 100644 index be562a9cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10a26eaa-99ae-479f-968e-b36ca1a0e583.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10aeba3f-bdac-49c7-965b-8b4cde154b7a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10aeba3f-bdac-49c7-965b-8b4cde154b7a.lance deleted file mode 100644 index c954b5552..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10aeba3f-bdac-49c7-965b-8b4cde154b7a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10bfd8b5-d08d-4f12-beea-04be7d232049.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10bfd8b5-d08d-4f12-beea-04be7d232049.lance deleted file mode 100644 index f5d2082d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10bfd8b5-d08d-4f12-beea-04be7d232049.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10d42d53-17b9-42ba-9b93-eb1290dd2bab.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10d42d53-17b9-42ba-9b93-eb1290dd2bab.lance deleted file mode 100644 index 285fd2da4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/10d42d53-17b9-42ba-9b93-eb1290dd2bab.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1107ebe4-b122-4cdf-ae17-52a60df79659.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1107ebe4-b122-4cdf-ae17-52a60df79659.lance deleted file mode 100644 index 68bc2485d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1107ebe4-b122-4cdf-ae17-52a60df79659.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/110b1ef3-3970-4518-9756-86723a072697.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/110b1ef3-3970-4518-9756-86723a072697.lance deleted file mode 100644 index fee252362..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/110b1ef3-3970-4518-9756-86723a072697.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/110e4bd5-e6d4-4665-ae9e-69bd8a37737e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/110e4bd5-e6d4-4665-ae9e-69bd8a37737e.lance deleted file mode 100644 index c4b408986..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/110e4bd5-e6d4-4665-ae9e-69bd8a37737e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1124838d-b824-44f8-aa67-2b51c9df32b4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1124838d-b824-44f8-aa67-2b51c9df32b4.lance deleted file mode 100644 index af5b0ca59..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1124838d-b824-44f8-aa67-2b51c9df32b4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11306438-5f63-439d-baf1-6b3014b1b069.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11306438-5f63-439d-baf1-6b3014b1b069.lance deleted file mode 100644 index aef47d626..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11306438-5f63-439d-baf1-6b3014b1b069.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11320f4b-13a1-421a-a2c9-3d2f65377cd9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11320f4b-13a1-421a-a2c9-3d2f65377cd9.lance deleted file mode 100644 index 9a28fda28..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11320f4b-13a1-421a-a2c9-3d2f65377cd9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11646269-12b9-49c0-badb-8bc1b8205e2a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11646269-12b9-49c0-badb-8bc1b8205e2a.lance deleted file mode 100644 index 48b20e15c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11646269-12b9-49c0-badb-8bc1b8205e2a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/116e6a83-79e1-4766-aebe-20bb6c5e8202.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/116e6a83-79e1-4766-aebe-20bb6c5e8202.lance deleted file mode 100644 index 411c3fb7f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/116e6a83-79e1-4766-aebe-20bb6c5e8202.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11732af6-d18f-4a5e-9942-5ae1e84c803d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11732af6-d18f-4a5e-9942-5ae1e84c803d.lance deleted file mode 100644 index 0a87116ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11732af6-d18f-4a5e-9942-5ae1e84c803d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1173dc37-973a-4a29-b86e-62d272f29836.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1173dc37-973a-4a29-b86e-62d272f29836.lance deleted file mode 100644 index d6115220a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1173dc37-973a-4a29-b86e-62d272f29836.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11807dcf-261b-4272-8317-952aec7a6824.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11807dcf-261b-4272-8317-952aec7a6824.lance deleted file mode 100644 index 8af63097f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11807dcf-261b-4272-8317-952aec7a6824.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1197aeab-080d-4393-a372-40fa6e6dbc1b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1197aeab-080d-4393-a372-40fa6e6dbc1b.lance deleted file mode 100644 index 7ee0056c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1197aeab-080d-4393-a372-40fa6e6dbc1b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11983207-6965-4192-b22a-21f29cb22ac5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11983207-6965-4192-b22a-21f29cb22ac5.lance deleted file mode 100644 index 2263d84b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11983207-6965-4192-b22a-21f29cb22ac5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11d2298f-f863-4e0b-a5bd-09a3750958d2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11d2298f-f863-4e0b-a5bd-09a3750958d2.lance deleted file mode 100644 index c0ab39288..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11d2298f-f863-4e0b-a5bd-09a3750958d2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11d82561-97fc-4736-b029-fb25dd3f9663.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11d82561-97fc-4736-b029-fb25dd3f9663.lance deleted file mode 100644 index 9d98d68e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11d82561-97fc-4736-b029-fb25dd3f9663.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11f79b83-1aa5-4e34-b97f-deffb8e4f05a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11f79b83-1aa5-4e34-b97f-deffb8e4f05a.lance deleted file mode 100644 index 3105260b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/11f79b83-1aa5-4e34-b97f-deffb8e4f05a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/12006ed0-c0a9-47b9-be9f-bbc4a5fcc859.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/12006ed0-c0a9-47b9-be9f-bbc4a5fcc859.lance deleted file mode 100644 index 7abdf3853..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/12006ed0-c0a9-47b9-be9f-bbc4a5fcc859.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/12787ac2-46d4-4d10-b488-7a9b0f8e4d82.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/12787ac2-46d4-4d10-b488-7a9b0f8e4d82.lance deleted file mode 100644 index 6c311c761..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/12787ac2-46d4-4d10-b488-7a9b0f8e4d82.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/12b5c47d-44e1-469b-b1ea-635ea51d8659.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/12b5c47d-44e1-469b-b1ea-635ea51d8659.lance deleted file mode 100644 index 38437c907..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/12b5c47d-44e1-469b-b1ea-635ea51d8659.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/12ed0b02-40b7-41c3-bdfc-ccad9d28ffa5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/12ed0b02-40b7-41c3-bdfc-ccad9d28ffa5.lance deleted file mode 100644 index baa507268..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/12ed0b02-40b7-41c3-bdfc-ccad9d28ffa5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/12ed968f-6c3a-48b1-acf4-9c790f8ec756.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/12ed968f-6c3a-48b1-acf4-9c790f8ec756.lance deleted file mode 100644 index adaba719b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/12ed968f-6c3a-48b1-acf4-9c790f8ec756.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/130ca618-40dd-45fc-88f4-655b54031b8f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/130ca618-40dd-45fc-88f4-655b54031b8f.lance deleted file mode 100644 index 21d2a5890..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/130ca618-40dd-45fc-88f4-655b54031b8f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1312c10e-068c-443f-a8bc-13dd50444041.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1312c10e-068c-443f-a8bc-13dd50444041.lance deleted file mode 100644 index 6141ece82..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1312c10e-068c-443f-a8bc-13dd50444041.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/132741da-f32a-4928-8ba0-5765227ef6e9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/132741da-f32a-4928-8ba0-5765227ef6e9.lance deleted file mode 100644 index 4875ceb95..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/132741da-f32a-4928-8ba0-5765227ef6e9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13530cef-598a-46b0-8dc9-5587220410cc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13530cef-598a-46b0-8dc9-5587220410cc.lance deleted file mode 100644 index e3c987d4e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13530cef-598a-46b0-8dc9-5587220410cc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13538d2e-4ad4-424a-8312-176cd9233750.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13538d2e-4ad4-424a-8312-176cd9233750.lance deleted file mode 100644 index 11c410da3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13538d2e-4ad4-424a-8312-176cd9233750.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/135d9efc-3641-4148-b0c8-1d3ab2328974.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/135d9efc-3641-4148-b0c8-1d3ab2328974.lance deleted file mode 100644 index fe7af889a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/135d9efc-3641-4148-b0c8-1d3ab2328974.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/137b8f88-833b-4e9d-b3c8-1e70c85f8cae.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/137b8f88-833b-4e9d-b3c8-1e70c85f8cae.lance deleted file mode 100644 index 0ce479346..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/137b8f88-833b-4e9d-b3c8-1e70c85f8cae.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13a01cab-fcf8-4420-b5ed-1e760a1c2dec.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13a01cab-fcf8-4420-b5ed-1e760a1c2dec.lance deleted file mode 100644 index db40c4834..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13a01cab-fcf8-4420-b5ed-1e760a1c2dec.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13a265c6-4600-46a4-8ede-f0a2a9fe12da.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13a265c6-4600-46a4-8ede-f0a2a9fe12da.lance deleted file mode 100644 index ece068edd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13a265c6-4600-46a4-8ede-f0a2a9fe12da.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13aad672-8e0b-4437-b691-da9e856591be.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13aad672-8e0b-4437-b691-da9e856591be.lance deleted file mode 100644 index 5ccdba727..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13aad672-8e0b-4437-b691-da9e856591be.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13b16a37-7151-4e8d-82da-812979460398.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13b16a37-7151-4e8d-82da-812979460398.lance deleted file mode 100644 index 3cabb07d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13b16a37-7151-4e8d-82da-812979460398.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13d337df-35d5-4c03-9c77-432656dc7ed1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13d337df-35d5-4c03-9c77-432656dc7ed1.lance deleted file mode 100644 index b6ed62feb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13d337df-35d5-4c03-9c77-432656dc7ed1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13d8c73a-aef2-4e1a-9444-6f055134c25e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13d8c73a-aef2-4e1a-9444-6f055134c25e.lance deleted file mode 100644 index 55c934954..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13d8c73a-aef2-4e1a-9444-6f055134c25e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13f2529b-451c-437a-9d81-fb71aa682a79.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13f2529b-451c-437a-9d81-fb71aa682a79.lance deleted file mode 100644 index 58cf381da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13f2529b-451c-437a-9d81-fb71aa682a79.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13facb6e-7962-4120-9430-21ba3f8eafc2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13facb6e-7962-4120-9430-21ba3f8eafc2.lance deleted file mode 100644 index 53d83a32f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/13facb6e-7962-4120-9430-21ba3f8eafc2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/141e0d91-ece7-49b9-b43b-716dc2ae13de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/141e0d91-ece7-49b9-b43b-716dc2ae13de.lance deleted file mode 100644 index 77d09615a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/141e0d91-ece7-49b9-b43b-716dc2ae13de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/143f6f73-f4d6-47a6-9f3d-7dc2f75ea40a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/143f6f73-f4d6-47a6-9f3d-7dc2f75ea40a.lance deleted file mode 100644 index 87d772428..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/143f6f73-f4d6-47a6-9f3d-7dc2f75ea40a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/147128f1-ec8e-4b76-8433-1906e36369de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/147128f1-ec8e-4b76-8433-1906e36369de.lance deleted file mode 100644 index bc7badf1e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/147128f1-ec8e-4b76-8433-1906e36369de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/148cc7e0-06bc-4b7f-a915-865e30c9301c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/148cc7e0-06bc-4b7f-a915-865e30c9301c.lance deleted file mode 100644 index 11133e30b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/148cc7e0-06bc-4b7f-a915-865e30c9301c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/14d1be38-5d30-4a6c-b19f-4e91827029d7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/14d1be38-5d30-4a6c-b19f-4e91827029d7.lance deleted file mode 100644 index 1a872323c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/14d1be38-5d30-4a6c-b19f-4e91827029d7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/14d2d815-dc3c-41ed-88ab-693086a7d5cd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/14d2d815-dc3c-41ed-88ab-693086a7d5cd.lance deleted file mode 100644 index dc0a6cb66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/14d2d815-dc3c-41ed-88ab-693086a7d5cd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/14fbd929-2ec5-41f5-9cb6-c9a9903c9811.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/14fbd929-2ec5-41f5-9cb6-c9a9903c9811.lance deleted file mode 100644 index 7ecaaae24..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/14fbd929-2ec5-41f5-9cb6-c9a9903c9811.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/152ef415-aa32-4e74-8323-59e71199cd02.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/152ef415-aa32-4e74-8323-59e71199cd02.lance deleted file mode 100644 index c7f57ac3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/152ef415-aa32-4e74-8323-59e71199cd02.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1556ffcf-d06c-417d-8e73-573009fbd219.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1556ffcf-d06c-417d-8e73-573009fbd219.lance deleted file mode 100644 index 9a29a2526..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1556ffcf-d06c-417d-8e73-573009fbd219.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/157cb9d3-7a50-4815-8ea1-08868e3bd495.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/157cb9d3-7a50-4815-8ea1-08868e3bd495.lance deleted file mode 100644 index deb596466..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/157cb9d3-7a50-4815-8ea1-08868e3bd495.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1587da9d-e356-4a9d-a43a-6f4e64c517c7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1587da9d-e356-4a9d-a43a-6f4e64c517c7.lance deleted file mode 100644 index e04b62077..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1587da9d-e356-4a9d-a43a-6f4e64c517c7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/159be84e-2e26-4a79-81ec-cfb8a9e07467.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/159be84e-2e26-4a79-81ec-cfb8a9e07467.lance deleted file mode 100644 index 2f419d3b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/159be84e-2e26-4a79-81ec-cfb8a9e07467.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15c507fa-a52d-40fe-bfd4-c0af6fcee5de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15c507fa-a52d-40fe-bfd4-c0af6fcee5de.lance deleted file mode 100644 index 970fa242b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15c507fa-a52d-40fe-bfd4-c0af6fcee5de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15d6495f-f5c0-498f-bc11-f454b9760311.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15d6495f-f5c0-498f-bc11-f454b9760311.lance deleted file mode 100644 index cbb861bcb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15d6495f-f5c0-498f-bc11-f454b9760311.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15dfc033-4a3b-4107-b45b-25b488d8086e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15dfc033-4a3b-4107-b45b-25b488d8086e.lance deleted file mode 100644 index 020f0ee2e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15dfc033-4a3b-4107-b45b-25b488d8086e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15ea09af-fc37-45fa-8a54-cea18b2e6465.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15ea09af-fc37-45fa-8a54-cea18b2e6465.lance deleted file mode 100644 index 9048a321b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15ea09af-fc37-45fa-8a54-cea18b2e6465.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15ecf566-a05e-4391-9b90-da714d607190.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15ecf566-a05e-4391-9b90-da714d607190.lance deleted file mode 100644 index 766c892fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15ecf566-a05e-4391-9b90-da714d607190.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15f6115c-5755-42ec-9cb8-1d56a78f21ba.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15f6115c-5755-42ec-9cb8-1d56a78f21ba.lance deleted file mode 100644 index 8a6b3cd25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15f6115c-5755-42ec-9cb8-1d56a78f21ba.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15fc0fa4-4ca4-4eb2-8381-37270d4955cd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15fc0fa4-4ca4-4eb2-8381-37270d4955cd.lance deleted file mode 100644 index 4f6be30b1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/15fc0fa4-4ca4-4eb2-8381-37270d4955cd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16114948-5914-4f89-989d-59b1870c611b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16114948-5914-4f89-989d-59b1870c611b.lance deleted file mode 100644 index a65f780ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16114948-5914-4f89-989d-59b1870c611b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16350fda-90a7-45f2-ab94-6a8488173680.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16350fda-90a7-45f2-ab94-6a8488173680.lance deleted file mode 100644 index 2d4b42c0f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16350fda-90a7-45f2-ab94-6a8488173680.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1653f57f-b08e-4d6e-8d1d-31e4e86f32a4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1653f57f-b08e-4d6e-8d1d-31e4e86f32a4.lance deleted file mode 100644 index 23eaee1e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1653f57f-b08e-4d6e-8d1d-31e4e86f32a4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1660969d-d124-48e9-8226-aa97b36c9497.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1660969d-d124-48e9-8226-aa97b36c9497.lance deleted file mode 100644 index 2f149b172..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1660969d-d124-48e9-8226-aa97b36c9497.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1660d218-7b5b-4352-8295-822bf0c8da7f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1660d218-7b5b-4352-8295-822bf0c8da7f.lance deleted file mode 100644 index 2cab9c7fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1660d218-7b5b-4352-8295-822bf0c8da7f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1667157a-1efb-47c2-942e-1885e798dbca.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1667157a-1efb-47c2-942e-1885e798dbca.lance deleted file mode 100644 index 86f8ef0c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1667157a-1efb-47c2-942e-1885e798dbca.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1679259e-07dd-44f2-ae65-fe7b6a7f186a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1679259e-07dd-44f2-ae65-fe7b6a7f186a.lance deleted file mode 100644 index 5cfb7098d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1679259e-07dd-44f2-ae65-fe7b6a7f186a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/167bf960-ff96-40ed-91df-ac3cfba51252.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/167bf960-ff96-40ed-91df-ac3cfba51252.lance deleted file mode 100644 index e2074e55e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/167bf960-ff96-40ed-91df-ac3cfba51252.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/169c647f-f5e2-4e39-8314-514c27b31933.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/169c647f-f5e2-4e39-8314-514c27b31933.lance deleted file mode 100644 index 54c121c03..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/169c647f-f5e2-4e39-8314-514c27b31933.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16c03cfe-63ad-4b6e-aaae-506b07bcd9ac.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16c03cfe-63ad-4b6e-aaae-506b07bcd9ac.lance deleted file mode 100644 index 35916f25b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16c03cfe-63ad-4b6e-aaae-506b07bcd9ac.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16d4eebf-f886-4334-ae74-95ec59c54ad0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16d4eebf-f886-4334-ae74-95ec59c54ad0.lance deleted file mode 100644 index 841e5700b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16d4eebf-f886-4334-ae74-95ec59c54ad0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16f93b95-d5d8-43c8-b719-d87961bfe91b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16f93b95-d5d8-43c8-b719-d87961bfe91b.lance deleted file mode 100644 index 7933c5dc2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16f93b95-d5d8-43c8-b719-d87961bfe91b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16fddd30-467b-4dac-98e4-2a72416c1f1c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16fddd30-467b-4dac-98e4-2a72416c1f1c.lance deleted file mode 100644 index 4cc580972..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/16fddd30-467b-4dac-98e4-2a72416c1f1c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/171f5030-40a5-4ad3-954f-cb4afab050c4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/171f5030-40a5-4ad3-954f-cb4afab050c4.lance deleted file mode 100644 index b48b43cf6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/171f5030-40a5-4ad3-954f-cb4afab050c4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/172dc968-a904-42fd-9cd7-49d416d1963c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/172dc968-a904-42fd-9cd7-49d416d1963c.lance deleted file mode 100644 index e27873fdf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/172dc968-a904-42fd-9cd7-49d416d1963c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17335d8a-8030-4e6f-ad75-067e6a2327d2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17335d8a-8030-4e6f-ad75-067e6a2327d2.lance deleted file mode 100644 index cc014092d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17335d8a-8030-4e6f-ad75-067e6a2327d2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17879e82-1cf9-45d5-881c-f3ca8b72e467.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17879e82-1cf9-45d5-881c-f3ca8b72e467.lance deleted file mode 100644 index 3b7c42fc3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17879e82-1cf9-45d5-881c-f3ca8b72e467.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17aa12bb-192f-4e90-8fd0-766291312f90.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17aa12bb-192f-4e90-8fd0-766291312f90.lance deleted file mode 100644 index 5d83d9c81..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17aa12bb-192f-4e90-8fd0-766291312f90.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17bcd5d0-e688-49c9-bcba-264855430337.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17bcd5d0-e688-49c9-bcba-264855430337.lance deleted file mode 100644 index 6214305ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17bcd5d0-e688-49c9-bcba-264855430337.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17ddb901-b48c-48ed-839f-153ba95c233c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17ddb901-b48c-48ed-839f-153ba95c233c.lance deleted file mode 100644 index 805656c2c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17ddb901-b48c-48ed-839f-153ba95c233c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17e2d466-98fd-4880-8d4f-1567a37e6403.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17e2d466-98fd-4880-8d4f-1567a37e6403.lance deleted file mode 100644 index e96e25f26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17e2d466-98fd-4880-8d4f-1567a37e6403.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17eeb8c0-15d7-4dd9-9e54-a82619832458.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17eeb8c0-15d7-4dd9-9e54-a82619832458.lance deleted file mode 100644 index dc5ff35b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17eeb8c0-15d7-4dd9-9e54-a82619832458.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17f0b7b6-3db6-4773-b1de-e9fa29587fb3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17f0b7b6-3db6-4773-b1de-e9fa29587fb3.lance deleted file mode 100644 index 901ff0711..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17f0b7b6-3db6-4773-b1de-e9fa29587fb3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17f1e4da-927d-46cf-a905-fb3e0148747a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17f1e4da-927d-46cf-a905-fb3e0148747a.lance deleted file mode 100644 index dca06125d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/17f1e4da-927d-46cf-a905-fb3e0148747a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18039527-9971-4bf1-9e86-08a4c989076e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18039527-9971-4bf1-9e86-08a4c989076e.lance deleted file mode 100644 index 356ace627..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18039527-9971-4bf1-9e86-08a4c989076e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1806d8a3-7316-48d7-ac8b-8675323f5fb0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1806d8a3-7316-48d7-ac8b-8675323f5fb0.lance deleted file mode 100644 index 5576c49b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1806d8a3-7316-48d7-ac8b-8675323f5fb0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18176bd3-af79-4e29-b029-47318fc1918f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18176bd3-af79-4e29-b029-47318fc1918f.lance deleted file mode 100644 index bd7366315..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18176bd3-af79-4e29-b029-47318fc1918f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/181ffc6a-dfa4-4557-85f2-1201bf919323.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/181ffc6a-dfa4-4557-85f2-1201bf919323.lance deleted file mode 100644 index 0403fe30d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/181ffc6a-dfa4-4557-85f2-1201bf919323.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/182de970-5e58-4aaa-8148-111e358a77a5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/182de970-5e58-4aaa-8148-111e358a77a5.lance deleted file mode 100644 index ea92e0ab9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/182de970-5e58-4aaa-8148-111e358a77a5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18617647-97f2-4b96-a6b1-b38692b84338.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18617647-97f2-4b96-a6b1-b38692b84338.lance deleted file mode 100644 index 60abf7415..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18617647-97f2-4b96-a6b1-b38692b84338.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/186ba830-9f89-4017-9ad6-9e9029b18fde.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/186ba830-9f89-4017-9ad6-9e9029b18fde.lance deleted file mode 100644 index c90e234a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/186ba830-9f89-4017-9ad6-9e9029b18fde.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/188e06ee-e6fb-4297-a26f-165de68c3b20.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/188e06ee-e6fb-4297-a26f-165de68c3b20.lance deleted file mode 100644 index 60ee09b98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/188e06ee-e6fb-4297-a26f-165de68c3b20.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18977495-5300-4b7c-9f39-8acbb085b50b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18977495-5300-4b7c-9f39-8acbb085b50b.lance deleted file mode 100644 index 98904cdab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18977495-5300-4b7c-9f39-8acbb085b50b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18bbc700-530b-4388-85ef-2c6d70990715.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18bbc700-530b-4388-85ef-2c6d70990715.lance deleted file mode 100644 index b16199760..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18bbc700-530b-4388-85ef-2c6d70990715.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18bd10b3-f21b-4251-8239-6e0666d6f380.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18bd10b3-f21b-4251-8239-6e0666d6f380.lance deleted file mode 100644 index 405fc28ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18bd10b3-f21b-4251-8239-6e0666d6f380.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18c4aac7-9214-46e3-8d77-434fce36072e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18c4aac7-9214-46e3-8d77-434fce36072e.lance deleted file mode 100644 index 785b41d99..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18c4aac7-9214-46e3-8d77-434fce36072e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18c9bcee-23ba-4858-97b9-1216d7a4b93e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18c9bcee-23ba-4858-97b9-1216d7a4b93e.lance deleted file mode 100644 index 66baff31b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18c9bcee-23ba-4858-97b9-1216d7a4b93e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18d47b8d-18cc-49c8-8c11-a9a7bb172116.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18d47b8d-18cc-49c8-8c11-a9a7bb172116.lance deleted file mode 100644 index b654f3c37..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18d47b8d-18cc-49c8-8c11-a9a7bb172116.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18d91c5f-b7b2-41d4-8ea0-3e8d224d4699.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18d91c5f-b7b2-41d4-8ea0-3e8d224d4699.lance deleted file mode 100644 index afa36a6df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18d91c5f-b7b2-41d4-8ea0-3e8d224d4699.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18e2423e-f963-4e7d-9bf2-4737ea1bd32e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18e2423e-f963-4e7d-9bf2-4737ea1bd32e.lance deleted file mode 100644 index ee293c7f5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/18e2423e-f963-4e7d-9bf2-4737ea1bd32e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/191ad732-a554-40fe-92cc-edb8b1e9d600.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/191ad732-a554-40fe-92cc-edb8b1e9d600.lance deleted file mode 100644 index c36bb50c3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/191ad732-a554-40fe-92cc-edb8b1e9d600.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/193367d4-ef78-4d05-9b73-7bb398938d24.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/193367d4-ef78-4d05-9b73-7bb398938d24.lance deleted file mode 100644 index d00b2cadb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/193367d4-ef78-4d05-9b73-7bb398938d24.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19a0612e-9762-4471-a367-92cd895a31cb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19a0612e-9762-4471-a367-92cd895a31cb.lance deleted file mode 100644 index 7111e5175..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19a0612e-9762-4471-a367-92cd895a31cb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19d5cfe1-cc66-4670-ac35-08cff77d25c6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19d5cfe1-cc66-4670-ac35-08cff77d25c6.lance deleted file mode 100644 index 9f656eafe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19d5cfe1-cc66-4670-ac35-08cff77d25c6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19d9937d-bf1f-4662-bab7-49966b384b69.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19d9937d-bf1f-4662-bab7-49966b384b69.lance deleted file mode 100644 index 26995adca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19d9937d-bf1f-4662-bab7-49966b384b69.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19e2dd09-a78e-473c-b210-d70c50fd86a1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19e2dd09-a78e-473c-b210-d70c50fd86a1.lance deleted file mode 100644 index bfeaf0e44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19e2dd09-a78e-473c-b210-d70c50fd86a1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19e9b4dd-0362-48ee-a433-7cd563615019.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19e9b4dd-0362-48ee-a433-7cd563615019.lance deleted file mode 100644 index 3b07e5c42..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19e9b4dd-0362-48ee-a433-7cd563615019.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19ffa100-469f-4b3a-9838-f9b0ecf1e7ac.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19ffa100-469f-4b3a-9838-f9b0ecf1e7ac.lance deleted file mode 100644 index 11a32d2d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/19ffa100-469f-4b3a-9838-f9b0ecf1e7ac.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a00361f-6cde-418c-86c5-7b1c6e980dda.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a00361f-6cde-418c-86c5-7b1c6e980dda.lance deleted file mode 100644 index 788116ff1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a00361f-6cde-418c-86c5-7b1c6e980dda.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a1b6945-c642-4e74-9876-aad6a2d1d4b2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a1b6945-c642-4e74-9876-aad6a2d1d4b2.lance deleted file mode 100644 index 5fafa5c6a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a1b6945-c642-4e74-9876-aad6a2d1d4b2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a2ead34-382f-490a-8387-a18c342ee05e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a2ead34-382f-490a-8387-a18c342ee05e.lance deleted file mode 100644 index bf79d5435..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a2ead34-382f-490a-8387-a18c342ee05e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a3607ff-4a72-47da-866b-e7b47b43fcf2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a3607ff-4a72-47da-866b-e7b47b43fcf2.lance deleted file mode 100644 index cbb7dd1c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a3607ff-4a72-47da-866b-e7b47b43fcf2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a365242-a4c9-48b6-aec5-905f3c622201.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a365242-a4c9-48b6-aec5-905f3c622201.lance deleted file mode 100644 index d2761e530..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a365242-a4c9-48b6-aec5-905f3c622201.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a5e3a71-b693-41b6-915e-c0e5b81da520.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a5e3a71-b693-41b6-915e-c0e5b81da520.lance deleted file mode 100644 index 4e87201da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a5e3a71-b693-41b6-915e-c0e5b81da520.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a82abbc-e3ba-4cef-8f50-337dc4cb6f3b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a82abbc-e3ba-4cef-8f50-337dc4cb6f3b.lance deleted file mode 100644 index ef1847781..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a82abbc-e3ba-4cef-8f50-337dc4cb6f3b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a97d8e2-87ce-4b9f-880b-92a64e77cf28.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a97d8e2-87ce-4b9f-880b-92a64e77cf28.lance deleted file mode 100644 index 7e5002f87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a97d8e2-87ce-4b9f-880b-92a64e77cf28.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a993005-effe-4074-a807-293d60ee9414.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a993005-effe-4074-a807-293d60ee9414.lance deleted file mode 100644 index 3e58c207f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1a993005-effe-4074-a807-293d60ee9414.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1aa66dce-fe75-4873-8e2d-fbed9f15c6f1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1aa66dce-fe75-4873-8e2d-fbed9f15c6f1.lance deleted file mode 100644 index 4cc05259c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1aa66dce-fe75-4873-8e2d-fbed9f15c6f1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1aef8d36-dd0e-486a-b8bb-4d96ecf1fc0c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1aef8d36-dd0e-486a-b8bb-4d96ecf1fc0c.lance deleted file mode 100644 index 7cfab15b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1aef8d36-dd0e-486a-b8bb-4d96ecf1fc0c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1b3907fb-ee41-40f9-b0c4-324875b46f56.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1b3907fb-ee41-40f9-b0c4-324875b46f56.lance deleted file mode 100644 index d24caca2f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1b3907fb-ee41-40f9-b0c4-324875b46f56.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1b3aec65-6baa-4949-a34b-80c7d979c598.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1b3aec65-6baa-4949-a34b-80c7d979c598.lance deleted file mode 100644 index a82c114fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1b3aec65-6baa-4949-a34b-80c7d979c598.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1b40abba-48eb-4040-a4ab-ab83779b85cc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1b40abba-48eb-4040-a4ab-ab83779b85cc.lance deleted file mode 100644 index 6d9ebff29..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1b40abba-48eb-4040-a4ab-ab83779b85cc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1b443dbe-8b12-4083-9799-09a759e64dc8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1b443dbe-8b12-4083-9799-09a759e64dc8.lance deleted file mode 100644 index 9b95f1536..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1b443dbe-8b12-4083-9799-09a759e64dc8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1b998d61-f7ce-4949-aabe-07e72f5ab0c9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1b998d61-f7ce-4949-aabe-07e72f5ab0c9.lance deleted file mode 100644 index 10c2e9e4a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1b998d61-f7ce-4949-aabe-07e72f5ab0c9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1bb415b5-b183-4c7e-a2a9-35ec8142a184.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1bb415b5-b183-4c7e-a2a9-35ec8142a184.lance deleted file mode 100644 index 7662db578..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1bb415b5-b183-4c7e-a2a9-35ec8142a184.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1bd7e2a3-ab8b-46fb-bd77-163228e4ff2a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1bd7e2a3-ab8b-46fb-bd77-163228e4ff2a.lance deleted file mode 100644 index 62909acea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1bd7e2a3-ab8b-46fb-bd77-163228e4ff2a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c0574c0-9d65-448c-b3b1-5a271636b784.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c0574c0-9d65-448c-b3b1-5a271636b784.lance deleted file mode 100644 index 98ec428d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c0574c0-9d65-448c-b3b1-5a271636b784.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c3a5d03-8818-45aa-8547-e3f6628b85b2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c3a5d03-8818-45aa-8547-e3f6628b85b2.lance deleted file mode 100644 index ef5326f02..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c3a5d03-8818-45aa-8547-e3f6628b85b2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c688185-17c2-4c78-952d-98fee267b419.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c688185-17c2-4c78-952d-98fee267b419.lance deleted file mode 100644 index a3774a2ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c688185-17c2-4c78-952d-98fee267b419.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c69a670-afdd-466f-b2b9-53a6f7297432.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c69a670-afdd-466f-b2b9-53a6f7297432.lance deleted file mode 100644 index 7caf35217..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c69a670-afdd-466f-b2b9-53a6f7297432.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c6a1b9e-eccb-4655-ba21-d50549f68573.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c6a1b9e-eccb-4655-ba21-d50549f68573.lance deleted file mode 100644 index 84319d799..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c6a1b9e-eccb-4655-ba21-d50549f68573.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c84fa71-a7d1-43f9-9c91-ff781dc47a95.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c84fa71-a7d1-43f9-9c91-ff781dc47a95.lance deleted file mode 100644 index ca81fcb13..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1c84fa71-a7d1-43f9-9c91-ff781dc47a95.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1cb61289-fef0-4ac9-86db-dff799a72108.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1cb61289-fef0-4ac9-86db-dff799a72108.lance deleted file mode 100644 index 4baf4c605..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1cb61289-fef0-4ac9-86db-dff799a72108.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1cdeefde-566a-4a6c-9287-b6f24ddcc613.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1cdeefde-566a-4a6c-9287-b6f24ddcc613.lance deleted file mode 100644 index 9903c8e25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1cdeefde-566a-4a6c-9287-b6f24ddcc613.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1cfafb72-0ff4-4028-bb1c-397acc5f9b77.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1cfafb72-0ff4-4028-bb1c-397acc5f9b77.lance deleted file mode 100644 index 7781cf360..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1cfafb72-0ff4-4028-bb1c-397acc5f9b77.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1cfd78f9-b511-4c62-9c72-29f67fcf6857.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1cfd78f9-b511-4c62-9c72-29f67fcf6857.lance deleted file mode 100644 index 4480d0095..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1cfd78f9-b511-4c62-9c72-29f67fcf6857.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d00f62f-28be-4659-8e5b-0e84798eb559.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d00f62f-28be-4659-8e5b-0e84798eb559.lance deleted file mode 100644 index 2a2c65c8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d00f62f-28be-4659-8e5b-0e84798eb559.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d27e4e8-fa1d-475a-8fa7-ceb19819f99a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d27e4e8-fa1d-475a-8fa7-ceb19819f99a.lance deleted file mode 100644 index e99475766..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d27e4e8-fa1d-475a-8fa7-ceb19819f99a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d747fe6-8669-4bb2-98b5-b4518842356c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d747fe6-8669-4bb2-98b5-b4518842356c.lance deleted file mode 100644 index 6c194ad1b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d747fe6-8669-4bb2-98b5-b4518842356c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d7d0579-5621-493d-8cd1-03f3d0453a12.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d7d0579-5621-493d-8cd1-03f3d0453a12.lance deleted file mode 100644 index b8a975f0b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d7d0579-5621-493d-8cd1-03f3d0453a12.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d86128f-81e2-4a54-aadc-1b85ed7a8cab.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d86128f-81e2-4a54-aadc-1b85ed7a8cab.lance deleted file mode 100644 index 016bc3974..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d86128f-81e2-4a54-aadc-1b85ed7a8cab.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d9820c1-3f5d-406a-ab0e-da3967b7a1d6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d9820c1-3f5d-406a-ab0e-da3967b7a1d6.lance deleted file mode 100644 index 342bffb4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d9820c1-3f5d-406a-ab0e-da3967b7a1d6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d9d3578-a259-44ef-9da5-7cbad5899ff1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d9d3578-a259-44ef-9da5-7cbad5899ff1.lance deleted file mode 100644 index 2df1f993a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1d9d3578-a259-44ef-9da5-7cbad5899ff1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1dad7225-398e-4e5a-823a-af0fb40b566b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1dad7225-398e-4e5a-823a-af0fb40b566b.lance deleted file mode 100644 index cece44da7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1dad7225-398e-4e5a-823a-af0fb40b566b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1dbe8311-ee05-4a0c-a41c-4434c562a503.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1dbe8311-ee05-4a0c-a41c-4434c562a503.lance deleted file mode 100644 index b50d14d3a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1dbe8311-ee05-4a0c-a41c-4434c562a503.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1dd37727-f29e-4427-bf01-5b426caa60d8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1dd37727-f29e-4427-bf01-5b426caa60d8.lance deleted file mode 100644 index 6bcc1c859..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1dd37727-f29e-4427-bf01-5b426caa60d8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1de278f2-ad76-44c5-997e-7c86335489e1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1de278f2-ad76-44c5-997e-7c86335489e1.lance deleted file mode 100644 index e3c211f7a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1de278f2-ad76-44c5-997e-7c86335489e1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1def28a0-1b77-4dad-8f68-2b9ad2572be1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1def28a0-1b77-4dad-8f68-2b9ad2572be1.lance deleted file mode 100644 index 7589483c2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1def28a0-1b77-4dad-8f68-2b9ad2572be1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1df06ed0-95b9-404f-b799-4fa1329091c5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1df06ed0-95b9-404f-b799-4fa1329091c5.lance deleted file mode 100644 index e7bf4fd87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1df06ed0-95b9-404f-b799-4fa1329091c5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e1c2dd2-a05f-4627-8ddb-c00eb63fb353.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e1c2dd2-a05f-4627-8ddb-c00eb63fb353.lance deleted file mode 100644 index c4359f3fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e1c2dd2-a05f-4627-8ddb-c00eb63fb353.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e302999-aa5a-4135-95fd-e1feacd398b6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e302999-aa5a-4135-95fd-e1feacd398b6.lance deleted file mode 100644 index b46532423..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e302999-aa5a-4135-95fd-e1feacd398b6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e310f96-551d-4700-9993-ac9f4eac136b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e310f96-551d-4700-9993-ac9f4eac136b.lance deleted file mode 100644 index c6dff903a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e310f96-551d-4700-9993-ac9f4eac136b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e516c6c-362e-4da2-9cd6-221e3f301a7d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e516c6c-362e-4da2-9cd6-221e3f301a7d.lance deleted file mode 100644 index bf1c65b5a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e516c6c-362e-4da2-9cd6-221e3f301a7d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e55765e-4fed-41e0-a202-e5b0077e1113.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e55765e-4fed-41e0-a202-e5b0077e1113.lance deleted file mode 100644 index b6b6b1991..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e55765e-4fed-41e0-a202-e5b0077e1113.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e5cb102-9281-4098-89fb-1860b6ceff68.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e5cb102-9281-4098-89fb-1860b6ceff68.lance deleted file mode 100644 index 1485a326f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e5cb102-9281-4098-89fb-1860b6ceff68.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e60583c-744d-4943-8a26-e208a22fda1c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e60583c-744d-4943-8a26-e208a22fda1c.lance deleted file mode 100644 index cf0cc4e82..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e60583c-744d-4943-8a26-e208a22fda1c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e72de39-d06b-43e5-a4df-8b02599c2377.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e72de39-d06b-43e5-a4df-8b02599c2377.lance deleted file mode 100644 index c59ac9fb7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e72de39-d06b-43e5-a4df-8b02599c2377.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e808e09-35eb-416c-b2f6-9428f3876632.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e808e09-35eb-416c-b2f6-9428f3876632.lance deleted file mode 100644 index 93ec47d13..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1e808e09-35eb-416c-b2f6-9428f3876632.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1ea3b98d-2603-41f1-a7de-1090bb5d5242.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1ea3b98d-2603-41f1-a7de-1090bb5d5242.lance deleted file mode 100644 index 60774144c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1ea3b98d-2603-41f1-a7de-1090bb5d5242.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1eb95781-d926-4f6b-ac50-85ee15c585ee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1eb95781-d926-4f6b-ac50-85ee15c585ee.lance deleted file mode 100644 index 12a3840ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1eb95781-d926-4f6b-ac50-85ee15c585ee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1eb9f2d0-c75b-4e37-8872-b33117cfc59b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1eb9f2d0-c75b-4e37-8872-b33117cfc59b.lance deleted file mode 100644 index a7d2bde47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1eb9f2d0-c75b-4e37-8872-b33117cfc59b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1ed6150a-c325-46db-bd52-a147ef37a191.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1ed6150a-c325-46db-bd52-a147ef37a191.lance deleted file mode 100644 index 893058964..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1ed6150a-c325-46db-bd52-a147ef37a191.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1ef62366-3354-4181-8dc2-af13f3d9ad47.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1ef62366-3354-4181-8dc2-af13f3d9ad47.lance deleted file mode 100644 index 9a31354ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1ef62366-3354-4181-8dc2-af13f3d9ad47.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1eff3fa4-6fd6-4b83-bdcd-2ba082c7e245.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1eff3fa4-6fd6-4b83-bdcd-2ba082c7e245.lance deleted file mode 100644 index a93b53873..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1eff3fa4-6fd6-4b83-bdcd-2ba082c7e245.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1f0f0685-c025-4534-9aa4-a55b75d00f04.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1f0f0685-c025-4534-9aa4-a55b75d00f04.lance deleted file mode 100644 index 5f7b12e03..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1f0f0685-c025-4534-9aa4-a55b75d00f04.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1f342e33-fbc7-4f57-a1d4-67052660d273.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1f342e33-fbc7-4f57-a1d4-67052660d273.lance deleted file mode 100644 index aa2ee0258..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1f342e33-fbc7-4f57-a1d4-67052660d273.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1f9f5e83-0574-4e94-80c1-c9c6b1bb0bc1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1f9f5e83-0574-4e94-80c1-c9c6b1bb0bc1.lance deleted file mode 100644 index b68be5029..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1f9f5e83-0574-4e94-80c1-c9c6b1bb0bc1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fb3fa18-7be7-4d91-8307-834aad673798.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fb3fa18-7be7-4d91-8307-834aad673798.lance deleted file mode 100644 index 78aae3a80..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fb3fa18-7be7-4d91-8307-834aad673798.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fb5ce66-b1c1-4a44-8af1-dbd1ec21c3c7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fb5ce66-b1c1-4a44-8af1-dbd1ec21c3c7.lance deleted file mode 100644 index bd025ee43..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fb5ce66-b1c1-4a44-8af1-dbd1ec21c3c7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fb791a9-3634-4acd-b6ec-e69d5a8315ab.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fb791a9-3634-4acd-b6ec-e69d5a8315ab.lance deleted file mode 100644 index b7dfca781..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fb791a9-3634-4acd-b6ec-e69d5a8315ab.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fc4b7fb-06a1-42b5-ad86-1479372e0e6b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fc4b7fb-06a1-42b5-ad86-1479372e0e6b.lance deleted file mode 100644 index 76ac4deb4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fc4b7fb-06a1-42b5-ad86-1479372e0e6b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fd35455-c835-4a66-b0d9-ebeeb7be9be4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fd35455-c835-4a66-b0d9-ebeeb7be9be4.lance deleted file mode 100644 index cd9b6af08..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fd35455-c835-4a66-b0d9-ebeeb7be9be4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fe00406-5ba6-4ee6-841b-fe2d77429b9c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fe00406-5ba6-4ee6-841b-fe2d77429b9c.lance deleted file mode 100644 index 0906b2d00..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/1fe00406-5ba6-4ee6-841b-fe2d77429b9c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2015279e-5102-49b9-bf6d-fe5e14e79495.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2015279e-5102-49b9-bf6d-fe5e14e79495.lance deleted file mode 100644 index 4fd34b981..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2015279e-5102-49b9-bf6d-fe5e14e79495.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/204d1366-12c2-4b80-bd97-cec6915f5d44.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/204d1366-12c2-4b80-bd97-cec6915f5d44.lance deleted file mode 100644 index 5897c1df1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/204d1366-12c2-4b80-bd97-cec6915f5d44.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/20675da8-500f-4d66-b570-af7af87e2982.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/20675da8-500f-4d66-b570-af7af87e2982.lance deleted file mode 100644 index 246094dcd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/20675da8-500f-4d66-b570-af7af87e2982.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2089e238-94a5-4eb0-9ee5-248777970a28.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2089e238-94a5-4eb0-9ee5-248777970a28.lance deleted file mode 100644 index 7c0f22b91..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2089e238-94a5-4eb0-9ee5-248777970a28.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/209ad083-e280-4eb6-9554-549bcd78b14d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/209ad083-e280-4eb6-9554-549bcd78b14d.lance deleted file mode 100644 index 5cf1fd367..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/209ad083-e280-4eb6-9554-549bcd78b14d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/209d884b-4def-4ed6-bdfc-1ae60920e579.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/209d884b-4def-4ed6-bdfc-1ae60920e579.lance deleted file mode 100644 index 802d90476..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/209d884b-4def-4ed6-bdfc-1ae60920e579.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/20bedd50-ad4a-4578-88fe-3b6166e6b438.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/20bedd50-ad4a-4578-88fe-3b6166e6b438.lance deleted file mode 100644 index caa48ca92..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/20bedd50-ad4a-4578-88fe-3b6166e6b438.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/20c73c05-81ef-4a86-b670-3b656b69cc4b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/20c73c05-81ef-4a86-b670-3b656b69cc4b.lance deleted file mode 100644 index d2d822974..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/20c73c05-81ef-4a86-b670-3b656b69cc4b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/20c92dd3-b1cf-4875-be85-d5fb41722432.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/20c92dd3-b1cf-4875-be85-d5fb41722432.lance deleted file mode 100644 index 6ce70b528..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/20c92dd3-b1cf-4875-be85-d5fb41722432.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/20f56153-7f2e-4675-b2ab-baf942d07cbc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/20f56153-7f2e-4675-b2ab-baf942d07cbc.lance deleted file mode 100644 index c7f1819e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/20f56153-7f2e-4675-b2ab-baf942d07cbc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/21048165-d696-4a20-9adf-65a89ad2fd82.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/21048165-d696-4a20-9adf-65a89ad2fd82.lance deleted file mode 100644 index 9d99e062c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/21048165-d696-4a20-9adf-65a89ad2fd82.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2106b992-17fa-4870-bd8b-0936190d3e87.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2106b992-17fa-4870-bd8b-0936190d3e87.lance deleted file mode 100644 index 739e60b44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2106b992-17fa-4870-bd8b-0936190d3e87.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/210caeb7-bdfe-4338-b839-469837c62f0e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/210caeb7-bdfe-4338-b839-469837c62f0e.lance deleted file mode 100644 index f03105923..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/210caeb7-bdfe-4338-b839-469837c62f0e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2126398c-49d9-42bf-bc9a-1f8a00b291b0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2126398c-49d9-42bf-bc9a-1f8a00b291b0.lance deleted file mode 100644 index 3e86d8ec2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2126398c-49d9-42bf-bc9a-1f8a00b291b0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/212c61ed-0275-42aa-b4f9-d644176bfd58.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/212c61ed-0275-42aa-b4f9-d644176bfd58.lance deleted file mode 100644 index 16f32de51..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/212c61ed-0275-42aa-b4f9-d644176bfd58.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/212f6c77-cdf0-499f-b04b-87c113124fb4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/212f6c77-cdf0-499f-b04b-87c113124fb4.lance deleted file mode 100644 index 1883b1695..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/212f6c77-cdf0-499f-b04b-87c113124fb4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/213e54df-2091-4d76-90b0-e9da533bad97.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/213e54df-2091-4d76-90b0-e9da533bad97.lance deleted file mode 100644 index 74e4b56c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/213e54df-2091-4d76-90b0-e9da533bad97.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2156eebc-1b8a-4fef-bfb6-1c4be95be8c8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2156eebc-1b8a-4fef-bfb6-1c4be95be8c8.lance deleted file mode 100644 index cad17c99c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2156eebc-1b8a-4fef-bfb6-1c4be95be8c8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/216bd9a0-1ded-43c0-808b-971b36c16739.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/216bd9a0-1ded-43c0-808b-971b36c16739.lance deleted file mode 100644 index 3c0952272..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/216bd9a0-1ded-43c0-808b-971b36c16739.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/218faf16-6019-4e05-8043-9d7e41744b2a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/218faf16-6019-4e05-8043-9d7e41744b2a.lance deleted file mode 100644 index e56d492e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/218faf16-6019-4e05-8043-9d7e41744b2a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/21b64c07-d1e4-44c0-a2e8-91ee13a5cb73.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/21b64c07-d1e4-44c0-a2e8-91ee13a5cb73.lance deleted file mode 100644 index d370f59bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/21b64c07-d1e4-44c0-a2e8-91ee13a5cb73.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/21d30e71-77ce-4fe9-9736-94ff3ac156ff.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/21d30e71-77ce-4fe9-9736-94ff3ac156ff.lance deleted file mode 100644 index ab4ce3e6e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/21d30e71-77ce-4fe9-9736-94ff3ac156ff.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/21f86c33-0dd8-41ed-8525-0a9de8e325bf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/21f86c33-0dd8-41ed-8525-0a9de8e325bf.lance deleted file mode 100644 index acacf741a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/21f86c33-0dd8-41ed-8525-0a9de8e325bf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/21fdce3d-8f1d-4cf6-abb2-5071d95a0245.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/21fdce3d-8f1d-4cf6-abb2-5071d95a0245.lance deleted file mode 100644 index 4fda43a0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/21fdce3d-8f1d-4cf6-abb2-5071d95a0245.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2217df82-6695-44f7-9fa7-2096fc69da9b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2217df82-6695-44f7-9fa7-2096fc69da9b.lance deleted file mode 100644 index 59c962bb0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2217df82-6695-44f7-9fa7-2096fc69da9b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/224e25e9-7a33-4635-9b60-faa8a4740af1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/224e25e9-7a33-4635-9b60-faa8a4740af1.lance deleted file mode 100644 index cffdfe22f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/224e25e9-7a33-4635-9b60-faa8a4740af1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/22593634-2678-4a1d-9a44-9ed0ec958ab7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/22593634-2678-4a1d-9a44-9ed0ec958ab7.lance deleted file mode 100644 index 5e6d44792..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/22593634-2678-4a1d-9a44-9ed0ec958ab7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/22759a81-f365-45ee-9b7d-eddc940afe0b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/22759a81-f365-45ee-9b7d-eddc940afe0b.lance deleted file mode 100644 index d021b741a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/22759a81-f365-45ee-9b7d-eddc940afe0b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/22b8a532-9e02-4f76-98de-d5312e0bcb9c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/22b8a532-9e02-4f76-98de-d5312e0bcb9c.lance deleted file mode 100644 index 571784f35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/22b8a532-9e02-4f76-98de-d5312e0bcb9c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/22e5d827-30ee-4f9a-a1f9-5cd8cd1fd2e1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/22e5d827-30ee-4f9a-a1f9-5cd8cd1fd2e1.lance deleted file mode 100644 index 060f40e19..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/22e5d827-30ee-4f9a-a1f9-5cd8cd1fd2e1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/23243433-7f40-4845-8531-24969d1b4ef8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/23243433-7f40-4845-8531-24969d1b4ef8.lance deleted file mode 100644 index f12687abd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/23243433-7f40-4845-8531-24969d1b4ef8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/234157ce-ac61-417a-8f99-36194142990f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/234157ce-ac61-417a-8f99-36194142990f.lance deleted file mode 100644 index 9e6864c96..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/234157ce-ac61-417a-8f99-36194142990f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/23704ed2-3c9c-463f-8443-afcb1408cf89.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/23704ed2-3c9c-463f-8443-afcb1408cf89.lance deleted file mode 100644 index 6624159b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/23704ed2-3c9c-463f-8443-afcb1408cf89.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/237675d1-09e7-48e1-8f59-91a19d0748c7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/237675d1-09e7-48e1-8f59-91a19d0748c7.lance deleted file mode 100644 index c0dd05d25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/237675d1-09e7-48e1-8f59-91a19d0748c7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/239bcbea-c0d2-4642-b804-3bc1984ee9f0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/239bcbea-c0d2-4642-b804-3bc1984ee9f0.lance deleted file mode 100644 index aa4e5a5d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/239bcbea-c0d2-4642-b804-3bc1984ee9f0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/23d19e55-73d6-4043-879c-7b299b33bdb9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/23d19e55-73d6-4043-879c-7b299b33bdb9.lance deleted file mode 100644 index eecdf9b6f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/23d19e55-73d6-4043-879c-7b299b33bdb9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/23d51596-5231-4ccf-bc30-0394d4531ddb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/23d51596-5231-4ccf-bc30-0394d4531ddb.lance deleted file mode 100644 index e4cd28f74..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/23d51596-5231-4ccf-bc30-0394d4531ddb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/241235d6-a171-4b66-8954-a469fecc6fd3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/241235d6-a171-4b66-8954-a469fecc6fd3.lance deleted file mode 100644 index 381d807b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/241235d6-a171-4b66-8954-a469fecc6fd3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2417615d-b115-4ca1-9b08-0ebf74689957.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2417615d-b115-4ca1-9b08-0ebf74689957.lance deleted file mode 100644 index 2235076ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2417615d-b115-4ca1-9b08-0ebf74689957.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/242b4175-77e8-47e2-9cb5-cb8770287061.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/242b4175-77e8-47e2-9cb5-cb8770287061.lance deleted file mode 100644 index bcc4a06ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/242b4175-77e8-47e2-9cb5-cb8770287061.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/245d4fd5-bb5a-4e6c-8db5-f448fc48fe2d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/245d4fd5-bb5a-4e6c-8db5-f448fc48fe2d.lance deleted file mode 100644 index b76cd1a3b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/245d4fd5-bb5a-4e6c-8db5-f448fc48fe2d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/245f49c6-61b4-4078-b126-004be1c7d5c1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/245f49c6-61b4-4078-b126-004be1c7d5c1.lance deleted file mode 100644 index c68b1fd77..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/245f49c6-61b4-4078-b126-004be1c7d5c1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2477d30c-9222-4884-bf02-b31cfa1e87a1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2477d30c-9222-4884-bf02-b31cfa1e87a1.lance deleted file mode 100644 index 9a25c4c56..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2477d30c-9222-4884-bf02-b31cfa1e87a1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/24b8d093-9a52-4018-a2de-6f3d1933e851.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/24b8d093-9a52-4018-a2de-6f3d1933e851.lance deleted file mode 100644 index 51e28a7f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/24b8d093-9a52-4018-a2de-6f3d1933e851.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/24bd05e0-3976-49fc-8918-4a986396ef44.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/24bd05e0-3976-49fc-8918-4a986396ef44.lance deleted file mode 100644 index a818fe79e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/24bd05e0-3976-49fc-8918-4a986396ef44.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/24c96b68-de22-41cc-abb7-0f2633386b4e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/24c96b68-de22-41cc-abb7-0f2633386b4e.lance deleted file mode 100644 index 4406745e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/24c96b68-de22-41cc-abb7-0f2633386b4e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/24cbe490-a95d-4fb7-bcd4-de90e1709f13.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/24cbe490-a95d-4fb7-bcd4-de90e1709f13.lance deleted file mode 100644 index fa9e26405..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/24cbe490-a95d-4fb7-bcd4-de90e1709f13.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/24e15bfa-6af7-402c-b92b-dc3886095f9d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/24e15bfa-6af7-402c-b92b-dc3886095f9d.lance deleted file mode 100644 index f1d9e776b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/24e15bfa-6af7-402c-b92b-dc3886095f9d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/250d087a-8167-4cc2-9b41-ce3ab8274868.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/250d087a-8167-4cc2-9b41-ce3ab8274868.lance deleted file mode 100644 index 7ba0c2325..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/250d087a-8167-4cc2-9b41-ce3ab8274868.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25350982-58cd-4377-b600-1442f5a7f60b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25350982-58cd-4377-b600-1442f5a7f60b.lance deleted file mode 100644 index df743eb00..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25350982-58cd-4377-b600-1442f5a7f60b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25751b4d-131f-4bf4-8d59-9caa24f7bc44.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25751b4d-131f-4bf4-8d59-9caa24f7bc44.lance deleted file mode 100644 index b4e294ff4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25751b4d-131f-4bf4-8d59-9caa24f7bc44.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25b89571-d90b-4a56-84c3-382b67193f72.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25b89571-d90b-4a56-84c3-382b67193f72.lance deleted file mode 100644 index be23bf2dd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25b89571-d90b-4a56-84c3-382b67193f72.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25d25525-ea5c-4709-b85a-a4b661bf8025.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25d25525-ea5c-4709-b85a-a4b661bf8025.lance deleted file mode 100644 index 7e4b88376..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25d25525-ea5c-4709-b85a-a4b661bf8025.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25e21771-4e55-4086-8049-adbff196c150.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25e21771-4e55-4086-8049-adbff196c150.lance deleted file mode 100644 index 938f51290..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25e21771-4e55-4086-8049-adbff196c150.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25e77b3f-193b-45ab-91cf-53ee0456a792.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25e77b3f-193b-45ab-91cf-53ee0456a792.lance deleted file mode 100644 index e2d7c7b82..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25e77b3f-193b-45ab-91cf-53ee0456a792.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25ed283b-0568-407f-ba78-2e23ba1d0f44.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25ed283b-0568-407f-ba78-2e23ba1d0f44.lance deleted file mode 100644 index 384bfef19..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25ed283b-0568-407f-ba78-2e23ba1d0f44.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25fda6a9-9d48-4752-b353-717e1380231d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25fda6a9-9d48-4752-b353-717e1380231d.lance deleted file mode 100644 index 738bac847..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/25fda6a9-9d48-4752-b353-717e1380231d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2608d9ad-cf55-4993-8c47-3c10177bb157.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2608d9ad-cf55-4993-8c47-3c10177bb157.lance deleted file mode 100644 index 712a536e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2608d9ad-cf55-4993-8c47-3c10177bb157.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2616ffc3-c201-4e12-b921-4624b0a63a6e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2616ffc3-c201-4e12-b921-4624b0a63a6e.lance deleted file mode 100644 index b47d0c5f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2616ffc3-c201-4e12-b921-4624b0a63a6e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2629874c-302b-4869-8937-ee363b8d1388.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2629874c-302b-4869-8937-ee363b8d1388.lance deleted file mode 100644 index 2096a767c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2629874c-302b-4869-8937-ee363b8d1388.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2653b0e0-0a1d-401e-8f5d-f57779c93dca.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2653b0e0-0a1d-401e-8f5d-f57779c93dca.lance deleted file mode 100644 index 07fcf40d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2653b0e0-0a1d-401e-8f5d-f57779c93dca.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/26b953c8-682c-49f4-98af-d0543873a87c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/26b953c8-682c-49f4-98af-d0543873a87c.lance deleted file mode 100644 index a0ba20566..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/26b953c8-682c-49f4-98af-d0543873a87c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/26c78815-d9f2-461c-bb8d-2108a85856c4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/26c78815-d9f2-461c-bb8d-2108a85856c4.lance deleted file mode 100644 index 40f96c799..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/26c78815-d9f2-461c-bb8d-2108a85856c4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/26d4c082-081f-45f7-a750-8fd96a6c5d8e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/26d4c082-081f-45f7-a750-8fd96a6c5d8e.lance deleted file mode 100644 index 9daf00872..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/26d4c082-081f-45f7-a750-8fd96a6c5d8e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/26ec95cb-6c78-48ab-974e-e54b9eca7c35.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/26ec95cb-6c78-48ab-974e-e54b9eca7c35.lance deleted file mode 100644 index 1d70cc6c2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/26ec95cb-6c78-48ab-974e-e54b9eca7c35.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/26f301d9-fd30-440c-9da0-ea9c1edf9069.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/26f301d9-fd30-440c-9da0-ea9c1edf9069.lance deleted file mode 100644 index 85dacab75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/26f301d9-fd30-440c-9da0-ea9c1edf9069.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2727606f-14f1-480c-a452-0622648a0b54.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2727606f-14f1-480c-a452-0622648a0b54.lance deleted file mode 100644 index 9cbfb1e9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2727606f-14f1-480c-a452-0622648a0b54.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/276f79bf-57fa-427b-8782-337a3a9f114f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/276f79bf-57fa-427b-8782-337a3a9f114f.lance deleted file mode 100644 index 26f540579..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/276f79bf-57fa-427b-8782-337a3a9f114f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/277753b7-0544-49f3-847c-008af78b986e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/277753b7-0544-49f3-847c-008af78b986e.lance deleted file mode 100644 index 42eaa9694..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/277753b7-0544-49f3-847c-008af78b986e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2789ef3d-6fc7-499d-b35e-1d58081bef22.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2789ef3d-6fc7-499d-b35e-1d58081bef22.lance deleted file mode 100644 index c47e4859a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2789ef3d-6fc7-499d-b35e-1d58081bef22.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/279d09fa-e8c1-4829-b517-f1ca00dc571a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/279d09fa-e8c1-4829-b517-f1ca00dc571a.lance deleted file mode 100644 index 241def5d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/279d09fa-e8c1-4829-b517-f1ca00dc571a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/27cff903-26f7-4582-9ebe-d2e2a1da253b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/27cff903-26f7-4582-9ebe-d2e2a1da253b.lance deleted file mode 100644 index 27fa703ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/27cff903-26f7-4582-9ebe-d2e2a1da253b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/27df09e1-fa99-45f2-8e87-536340d35f2b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/27df09e1-fa99-45f2-8e87-536340d35f2b.lance deleted file mode 100644 index 5e31adfc3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/27df09e1-fa99-45f2-8e87-536340d35f2b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/27ff1a64-900d-4f88-92c1-8ab3be6ff35c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/27ff1a64-900d-4f88-92c1-8ab3be6ff35c.lance deleted file mode 100644 index 48b70cdcc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/27ff1a64-900d-4f88-92c1-8ab3be6ff35c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28068405-477a-4fbe-9498-7a4693f22602.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28068405-477a-4fbe-9498-7a4693f22602.lance deleted file mode 100644 index 3ba721fe8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28068405-477a-4fbe-9498-7a4693f22602.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/282814f3-70a2-426c-95c0-65e69f9664b6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/282814f3-70a2-426c-95c0-65e69f9664b6.lance deleted file mode 100644 index 66b5041d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/282814f3-70a2-426c-95c0-65e69f9664b6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/284a93f8-e68f-49bf-9145-f38db130545f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/284a93f8-e68f-49bf-9145-f38db130545f.lance deleted file mode 100644 index 9f27c7dcf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/284a93f8-e68f-49bf-9145-f38db130545f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2869f5ff-b9aa-4a9d-8280-6c04a39bedd7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2869f5ff-b9aa-4a9d-8280-6c04a39bedd7.lance deleted file mode 100644 index 4808b7480..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2869f5ff-b9aa-4a9d-8280-6c04a39bedd7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28a607a7-da16-42a1-a0e7-0ec8c11bf883.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28a607a7-da16-42a1-a0e7-0ec8c11bf883.lance deleted file mode 100644 index ff0508cd4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28a607a7-da16-42a1-a0e7-0ec8c11bf883.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28bb49c3-0500-48c6-8093-938c5b16d4c5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28bb49c3-0500-48c6-8093-938c5b16d4c5.lance deleted file mode 100644 index 9ef59f9a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28bb49c3-0500-48c6-8093-938c5b16d4c5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28c64270-7f35-496f-bca4-660ffc558340.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28c64270-7f35-496f-bca4-660ffc558340.lance deleted file mode 100644 index be601f211..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28c64270-7f35-496f-bca4-660ffc558340.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28dee096-0319-45c8-aa99-86c053813ed8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28dee096-0319-45c8-aa99-86c053813ed8.lance deleted file mode 100644 index 80b921e54..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28dee096-0319-45c8-aa99-86c053813ed8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28e32817-a04e-49f2-8023-6c677926d951.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28e32817-a04e-49f2-8023-6c677926d951.lance deleted file mode 100644 index 65565d26a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28e32817-a04e-49f2-8023-6c677926d951.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28f2cc3c-bed0-4cb0-8cf5-d17178c88af3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28f2cc3c-bed0-4cb0-8cf5-d17178c88af3.lance deleted file mode 100644 index ca8fbf50a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/28f2cc3c-bed0-4cb0-8cf5-d17178c88af3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2906f04e-a4f9-4e8a-a95a-97c18b7432b1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2906f04e-a4f9-4e8a-a95a-97c18b7432b1.lance deleted file mode 100644 index 5854f8978..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2906f04e-a4f9-4e8a-a95a-97c18b7432b1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/29668928-6767-4ebe-91f7-f00ee6f54765.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/29668928-6767-4ebe-91f7-f00ee6f54765.lance deleted file mode 100644 index cbe46049b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/29668928-6767-4ebe-91f7-f00ee6f54765.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2983a8ca-05a6-43ac-8b81-e8d0f841d2c9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2983a8ca-05a6-43ac-8b81-e8d0f841d2c9.lance deleted file mode 100644 index beb7f59cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2983a8ca-05a6-43ac-8b81-e8d0f841d2c9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/29ba6f3a-4d95-4950-9ac7-5d9ac8783eb1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/29ba6f3a-4d95-4950-9ac7-5d9ac8783eb1.lance deleted file mode 100644 index 51a982bc6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/29ba6f3a-4d95-4950-9ac7-5d9ac8783eb1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/29c01dba-a426-4766-9356-14a2b2f1cdb5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/29c01dba-a426-4766-9356-14a2b2f1cdb5.lance deleted file mode 100644 index e77906b50..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/29c01dba-a426-4766-9356-14a2b2f1cdb5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/29f1b39e-4bf0-4ae0-b017-f73dab360c2e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/29f1b39e-4bf0-4ae0-b017-f73dab360c2e.lance deleted file mode 100644 index 9b208162c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/29f1b39e-4bf0-4ae0-b017-f73dab360c2e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/29fea412-5bbe-4dcd-b85b-fc51506b90d9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/29fea412-5bbe-4dcd-b85b-fc51506b90d9.lance deleted file mode 100644 index 982f52a77..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/29fea412-5bbe-4dcd-b85b-fc51506b90d9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a0265a6-f222-4397-b27f-06451a2b0f5b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a0265a6-f222-4397-b27f-06451a2b0f5b.lance deleted file mode 100644 index 974753e3a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a0265a6-f222-4397-b27f-06451a2b0f5b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a057aa5-d2e6-492e-99cf-d3f81398cbe3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a057aa5-d2e6-492e-99cf-d3f81398cbe3.lance deleted file mode 100644 index 101c48677..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a057aa5-d2e6-492e-99cf-d3f81398cbe3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a0ce635-53d4-4b8f-bb56-8289d1fd9ccb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a0ce635-53d4-4b8f-bb56-8289d1fd9ccb.lance deleted file mode 100644 index 26c15f914..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a0ce635-53d4-4b8f-bb56-8289d1fd9ccb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a2ac86e-240b-4329-a639-45ac69129249.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a2ac86e-240b-4329-a639-45ac69129249.lance deleted file mode 100644 index 9de78dc53..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a2ac86e-240b-4329-a639-45ac69129249.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a2ac96e-2c8b-4586-959e-1f9526459720.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a2ac96e-2c8b-4586-959e-1f9526459720.lance deleted file mode 100644 index 7ffd370e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a2ac96e-2c8b-4586-959e-1f9526459720.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a438023-3c23-48de-9693-591433a22791.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a438023-3c23-48de-9693-591433a22791.lance deleted file mode 100644 index f9557f2a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a438023-3c23-48de-9693-591433a22791.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a497bb1-4f69-4c58-a003-b022a3dd9f0d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a497bb1-4f69-4c58-a003-b022a3dd9f0d.lance deleted file mode 100644 index a9f7529c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a497bb1-4f69-4c58-a003-b022a3dd9f0d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a641667-0859-4d33-a50b-827ff3607b0b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a641667-0859-4d33-a50b-827ff3607b0b.lance deleted file mode 100644 index 7c80c40ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a641667-0859-4d33-a50b-827ff3607b0b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a6a7527-3be5-4367-b6b0-237723b44a0b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a6a7527-3be5-4367-b6b0-237723b44a0b.lance deleted file mode 100644 index 93ce463be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a6a7527-3be5-4367-b6b0-237723b44a0b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a751f39-273a-42ee-bba9-bc6499aee540.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a751f39-273a-42ee-bba9-bc6499aee540.lance deleted file mode 100644 index ba249a44a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a751f39-273a-42ee-bba9-bc6499aee540.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a7e8694-fa67-4f3c-ae8b-820aabec4094.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a7e8694-fa67-4f3c-ae8b-820aabec4094.lance deleted file mode 100644 index 85d64b055..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2a7e8694-fa67-4f3c-ae8b-820aabec4094.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2aa92b07-ec22-4b48-8bb2-455cb7e09516.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2aa92b07-ec22-4b48-8bb2-455cb7e09516.lance deleted file mode 100644 index f8cc38b52..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2aa92b07-ec22-4b48-8bb2-455cb7e09516.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2ab03ef9-b0cd-4d07-91e1-0ed886680799.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2ab03ef9-b0cd-4d07-91e1-0ed886680799.lance deleted file mode 100644 index 98009e7cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2ab03ef9-b0cd-4d07-91e1-0ed886680799.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2ac1f266-0a9e-471f-8e5b-a73fdf4287e8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2ac1f266-0a9e-471f-8e5b-a73fdf4287e8.lance deleted file mode 100644 index eb2b41156..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2ac1f266-0a9e-471f-8e5b-a73fdf4287e8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2accbfe5-e3c7-49b2-b692-c7a4d211b14d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2accbfe5-e3c7-49b2-b692-c7a4d211b14d.lance deleted file mode 100644 index ab8a2747a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2accbfe5-e3c7-49b2-b692-c7a4d211b14d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b070d22-87e8-4e20-a84f-ce8ddbc9d420.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b070d22-87e8-4e20-a84f-ce8ddbc9d420.lance deleted file mode 100644 index 044d92f58..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b070d22-87e8-4e20-a84f-ce8ddbc9d420.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b0cfd28-70cf-49bc-831f-3192336acbf7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b0cfd28-70cf-49bc-831f-3192336acbf7.lance deleted file mode 100644 index 0e73d66c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b0cfd28-70cf-49bc-831f-3192336acbf7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b18eeb6-cc80-4951-9ddb-ae2e8e499e27.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b18eeb6-cc80-4951-9ddb-ae2e8e499e27.lance deleted file mode 100644 index 766f79ba9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b18eeb6-cc80-4951-9ddb-ae2e8e499e27.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b5004af-9cac-4cf9-822b-bd33461f9401.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b5004af-9cac-4cf9-822b-bd33461f9401.lance deleted file mode 100644 index 819301439..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b5004af-9cac-4cf9-822b-bd33461f9401.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b53110e-1aae-4cdf-b137-be53481d7769.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b53110e-1aae-4cdf-b137-be53481d7769.lance deleted file mode 100644 index 4964c439f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b53110e-1aae-4cdf-b137-be53481d7769.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b59f977-da35-46aa-acd4-cc4c92370505.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b59f977-da35-46aa-acd4-cc4c92370505.lance deleted file mode 100644 index 345505ff5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b59f977-da35-46aa-acd4-cc4c92370505.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b67ad60-b130-4fde-bb73-66229e4f95e9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b67ad60-b130-4fde-bb73-66229e4f95e9.lance deleted file mode 100644 index 040d395c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b67ad60-b130-4fde-bb73-66229e4f95e9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b9b1863-3edb-4086-b079-347a91f74d21.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b9b1863-3edb-4086-b079-347a91f74d21.lance deleted file mode 100644 index ab3c93edd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2b9b1863-3edb-4086-b079-347a91f74d21.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bb3b145-3ccb-4f97-af7e-636af14a7042.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bb3b145-3ccb-4f97-af7e-636af14a7042.lance deleted file mode 100644 index e527ae36b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bb3b145-3ccb-4f97-af7e-636af14a7042.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bb74369-f46a-4d2d-a981-f6ba0e140c37.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bb74369-f46a-4d2d-a981-f6ba0e140c37.lance deleted file mode 100644 index 09312f752..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bb74369-f46a-4d2d-a981-f6ba0e140c37.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bc16434-7b96-46a8-9dd3-b0647daf1134.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bc16434-7b96-46a8-9dd3-b0647daf1134.lance deleted file mode 100644 index 54140822d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bc16434-7b96-46a8-9dd3-b0647daf1134.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bceb107-b665-4ac2-b2ed-539245f40fe9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bceb107-b665-4ac2-b2ed-539245f40fe9.lance deleted file mode 100644 index 5b2ace765..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bceb107-b665-4ac2-b2ed-539245f40fe9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bde6172-017f-4763-a4e1-f644443261ee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bde6172-017f-4763-a4e1-f644443261ee.lance deleted file mode 100644 index 5b9851e6f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bde6172-017f-4763-a4e1-f644443261ee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bf235de-9669-4799-a34c-e198aee0bbea.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bf235de-9669-4799-a34c-e198aee0bbea.lance deleted file mode 100644 index 301d704e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2bf235de-9669-4799-a34c-e198aee0bbea.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2c154584-9edb-4b3c-8784-0adda4a76221.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2c154584-9edb-4b3c-8784-0adda4a76221.lance deleted file mode 100644 index 5edf72f37..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2c154584-9edb-4b3c-8784-0adda4a76221.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2c709837-f45f-45c8-bf51-b2878ee6cc99.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2c709837-f45f-45c8-bf51-b2878ee6cc99.lance deleted file mode 100644 index e76577bff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2c709837-f45f-45c8-bf51-b2878ee6cc99.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2c7c95cc-a40f-4693-a9e9-4d7bd74cec3c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2c7c95cc-a40f-4693-a9e9-4d7bd74cec3c.lance deleted file mode 100644 index 9cab86b14..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2c7c95cc-a40f-4693-a9e9-4d7bd74cec3c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2c80678a-bf01-4e71-a68b-525ec57986a5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2c80678a-bf01-4e71-a68b-525ec57986a5.lance deleted file mode 100644 index c7b790ed1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2c80678a-bf01-4e71-a68b-525ec57986a5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2cab1528-62c0-4c83-8681-615879374549.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2cab1528-62c0-4c83-8681-615879374549.lance deleted file mode 100644 index 10b753427..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2cab1528-62c0-4c83-8681-615879374549.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2cc8e2ac-073c-43f8-8ba5-df354dc35ee8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2cc8e2ac-073c-43f8-8ba5-df354dc35ee8.lance deleted file mode 100644 index b807eed4a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2cc8e2ac-073c-43f8-8ba5-df354dc35ee8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2cd221c0-8fc2-4060-88a9-d8ad80472379.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2cd221c0-8fc2-4060-88a9-d8ad80472379.lance deleted file mode 100644 index 8ea51bcc6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2cd221c0-8fc2-4060-88a9-d8ad80472379.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2d3d1d68-66b5-46b3-b79b-ca302e6453cf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2d3d1d68-66b5-46b3-b79b-ca302e6453cf.lance deleted file mode 100644 index d6afcfe97..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2d3d1d68-66b5-46b3-b79b-ca302e6453cf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2d48501a-ae8a-4dde-8dd6-76418462ee1f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2d48501a-ae8a-4dde-8dd6-76418462ee1f.lance deleted file mode 100644 index d73283da4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2d48501a-ae8a-4dde-8dd6-76418462ee1f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2d4bf80f-ffa9-4823-a4e6-65ff7be2cde7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2d4bf80f-ffa9-4823-a4e6-65ff7be2cde7.lance deleted file mode 100644 index 1862c1cc1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2d4bf80f-ffa9-4823-a4e6-65ff7be2cde7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2d7ecf0d-c574-46eb-ad98-661c21afa92f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2d7ecf0d-c574-46eb-ad98-661c21afa92f.lance deleted file mode 100644 index e5be1f817..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2d7ecf0d-c574-46eb-ad98-661c21afa92f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2d805931-4eb5-435e-9145-ad7dfb769aef.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2d805931-4eb5-435e-9145-ad7dfb769aef.lance deleted file mode 100644 index 9f0ad36b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2d805931-4eb5-435e-9145-ad7dfb769aef.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2da3aeca-8246-494b-b0f9-3d45dfd5e865.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2da3aeca-8246-494b-b0f9-3d45dfd5e865.lance deleted file mode 100644 index fc05e7b63..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2da3aeca-8246-494b-b0f9-3d45dfd5e865.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e1321b2-5e54-4e4f-976f-9936cf267b67.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e1321b2-5e54-4e4f-976f-9936cf267b67.lance deleted file mode 100644 index 2db3d064b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e1321b2-5e54-4e4f-976f-9936cf267b67.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e21f5ab-29ba-48fb-b8af-818987b76278.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e21f5ab-29ba-48fb-b8af-818987b76278.lance deleted file mode 100644 index ee3a0f3ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e21f5ab-29ba-48fb-b8af-818987b76278.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e2694b3-eace-4157-9104-c03b01871ac2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e2694b3-eace-4157-9104-c03b01871ac2.lance deleted file mode 100644 index 004f46369..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e2694b3-eace-4157-9104-c03b01871ac2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e279f09-8535-48d6-82d9-7acad2553f5e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e279f09-8535-48d6-82d9-7acad2553f5e.lance deleted file mode 100644 index 3ac4e2bd4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e279f09-8535-48d6-82d9-7acad2553f5e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e4b0830-6ce9-4c71-b145-3aea945e3a56.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e4b0830-6ce9-4c71-b145-3aea945e3a56.lance deleted file mode 100644 index 90e48f21c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e4b0830-6ce9-4c71-b145-3aea945e3a56.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e4c4013-e01b-4207-9cfe-8fe8940097de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e4c4013-e01b-4207-9cfe-8fe8940097de.lance deleted file mode 100644 index dcd31b2e0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e4c4013-e01b-4207-9cfe-8fe8940097de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e4dc034-0d60-484d-9588-8789ef962974.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e4dc034-0d60-484d-9588-8789ef962974.lance deleted file mode 100644 index 1d231d3f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e4dc034-0d60-484d-9588-8789ef962974.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e5903c5-fbf7-4da1-99b6-b687753766d4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e5903c5-fbf7-4da1-99b6-b687753766d4.lance deleted file mode 100644 index 5d25a7689..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e5903c5-fbf7-4da1-99b6-b687753766d4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e6b60c6-4b68-4e34-b6e9-39a9f0b648c5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e6b60c6-4b68-4e34-b6e9-39a9f0b648c5.lance deleted file mode 100644 index 2087c5bf3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2e6b60c6-4b68-4e34-b6e9-39a9f0b648c5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2ebd424e-45ed-4b24-b389-6b114dc75cd2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2ebd424e-45ed-4b24-b389-6b114dc75cd2.lance deleted file mode 100644 index a98f299a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2ebd424e-45ed-4b24-b389-6b114dc75cd2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2ec702f3-86f7-4681-9b2d-1bd37eb71a3b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2ec702f3-86f7-4681-9b2d-1bd37eb71a3b.lance deleted file mode 100644 index f89512398..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2ec702f3-86f7-4681-9b2d-1bd37eb71a3b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2eea2805-be7c-4edf-b70c-31867153f9e5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2eea2805-be7c-4edf-b70c-31867153f9e5.lance deleted file mode 100644 index aa7ae2c59..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2eea2805-be7c-4edf-b70c-31867153f9e5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f170601-84c2-4f92-b73a-10ba8ee9d1d6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f170601-84c2-4f92-b73a-10ba8ee9d1d6.lance deleted file mode 100644 index 994759dd6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f170601-84c2-4f92-b73a-10ba8ee9d1d6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f2c114d-bccc-47de-82b2-9c0153d1c513.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f2c114d-bccc-47de-82b2-9c0153d1c513.lance deleted file mode 100644 index fc78de01b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f2c114d-bccc-47de-82b2-9c0153d1c513.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f307d7a-f487-4702-9bf5-452ec17b89ca.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f307d7a-f487-4702-9bf5-452ec17b89ca.lance deleted file mode 100644 index 4787a710c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f307d7a-f487-4702-9bf5-452ec17b89ca.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f358cb6-687d-4bfb-86be-ab4e25c9eb0a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f358cb6-687d-4bfb-86be-ab4e25c9eb0a.lance deleted file mode 100644 index 5c0412f62..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f358cb6-687d-4bfb-86be-ab4e25c9eb0a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f38ca98-89af-4d4d-98a4-fc6321a59176.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f38ca98-89af-4d4d-98a4-fc6321a59176.lance deleted file mode 100644 index 6275d1dcb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f38ca98-89af-4d4d-98a4-fc6321a59176.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f4ce93c-60af-4495-8b59-2d84bb815f51.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f4ce93c-60af-4495-8b59-2d84bb815f51.lance deleted file mode 100644 index e56f553bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f4ce93c-60af-4495-8b59-2d84bb815f51.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f63156b-e218-4948-8360-01be5cc711ff.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f63156b-e218-4948-8360-01be5cc711ff.lance deleted file mode 100644 index 044573931..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f63156b-e218-4948-8360-01be5cc711ff.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f75bc1c-e73b-4769-9fe4-dfc6c8571dfb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f75bc1c-e73b-4769-9fe4-dfc6c8571dfb.lance deleted file mode 100644 index 1829e6ec7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f75bc1c-e73b-4769-9fe4-dfc6c8571dfb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f7e8def-b3d3-4bb7-bd85-0c0d2ac5a91e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f7e8def-b3d3-4bb7-bd85-0c0d2ac5a91e.lance deleted file mode 100644 index d929bdd99..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2f7e8def-b3d3-4bb7-bd85-0c0d2ac5a91e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fc49d57-a01a-4174-9fbc-c1c7f7b0d047.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fc49d57-a01a-4174-9fbc-c1c7f7b0d047.lance deleted file mode 100644 index 37282a457..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fc49d57-a01a-4174-9fbc-c1c7f7b0d047.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fc59dbc-13fd-4e13-aab9-9330371041e1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fc59dbc-13fd-4e13-aab9-9330371041e1.lance deleted file mode 100644 index 6934048e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fc59dbc-13fd-4e13-aab9-9330371041e1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fd38473-18fe-4ada-9206-11ddc7ce8bdb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fd38473-18fe-4ada-9206-11ddc7ce8bdb.lance deleted file mode 100644 index 36103e974..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fd38473-18fe-4ada-9206-11ddc7ce8bdb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fe0c7bc-dadb-4da8-aba1-a4e621cb9c0c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fe0c7bc-dadb-4da8-aba1-a4e621cb9c0c.lance deleted file mode 100644 index 9c934a51b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fe0c7bc-dadb-4da8-aba1-a4e621cb9c0c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fe51b25-c81f-45f9-8dbf-64cd82c7e240.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fe51b25-c81f-45f9-8dbf-64cd82c7e240.lance deleted file mode 100644 index e22bad4af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fe51b25-c81f-45f9-8dbf-64cd82c7e240.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fefaeed-e655-4221-948b-aa342f767147.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fefaeed-e655-4221-948b-aa342f767147.lance deleted file mode 100644 index 46fc35a9c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fefaeed-e655-4221-948b-aa342f767147.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fffecdb-73dc-458d-83cf-b79fa5a2ab78.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fffecdb-73dc-458d-83cf-b79fa5a2ab78.lance deleted file mode 100644 index f62d752ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/2fffecdb-73dc-458d-83cf-b79fa5a2ab78.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/300f0474-b53e-46ec-957c-950eef3493c9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/300f0474-b53e-46ec-957c-950eef3493c9.lance deleted file mode 100644 index 3a8cd73cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/300f0474-b53e-46ec-957c-950eef3493c9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/30658dd8-be40-4b9b-b743-6f1364ba3787.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/30658dd8-be40-4b9b-b743-6f1364ba3787.lance deleted file mode 100644 index bb59e518a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/30658dd8-be40-4b9b-b743-6f1364ba3787.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3068c061-5c92-4ec1-9559-dd040ff7171b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3068c061-5c92-4ec1-9559-dd040ff7171b.lance deleted file mode 100644 index 2f901fb2f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3068c061-5c92-4ec1-9559-dd040ff7171b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/306a0c83-0b9e-4b10-bf25-31c17b6f0c90.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/306a0c83-0b9e-4b10-bf25-31c17b6f0c90.lance deleted file mode 100644 index 5b48447b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/306a0c83-0b9e-4b10-bf25-31c17b6f0c90.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/30ac33e2-ee75-4102-b182-647c47eb8d10.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/30ac33e2-ee75-4102-b182-647c47eb8d10.lance deleted file mode 100644 index 1bd57fe98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/30ac33e2-ee75-4102-b182-647c47eb8d10.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/30b92124-b390-4239-af31-652800840b6c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/30b92124-b390-4239-af31-652800840b6c.lance deleted file mode 100644 index 5419f6b49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/30b92124-b390-4239-af31-652800840b6c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/30e4506b-f450-4abb-9c73-80bc0126ed19.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/30e4506b-f450-4abb-9c73-80bc0126ed19.lance deleted file mode 100644 index cb7773f0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/30e4506b-f450-4abb-9c73-80bc0126ed19.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/311816ba-f2d2-402c-bf2b-b2d9b8380a42.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/311816ba-f2d2-402c-bf2b-b2d9b8380a42.lance deleted file mode 100644 index 617ac31ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/311816ba-f2d2-402c-bf2b-b2d9b8380a42.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/31193a32-f7e3-4ffa-bfab-1f4252af642a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/31193a32-f7e3-4ffa-bfab-1f4252af642a.lance deleted file mode 100644 index bf3c5033a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/31193a32-f7e3-4ffa-bfab-1f4252af642a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3130d5ea-5bd2-4106-865c-0eede849d03d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3130d5ea-5bd2-4106-865c-0eede849d03d.lance deleted file mode 100644 index dc35c34f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3130d5ea-5bd2-4106-865c-0eede849d03d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3145e77e-f64b-469f-9792-178328df0868.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3145e77e-f64b-469f-9792-178328df0868.lance deleted file mode 100644 index d5aa54687..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3145e77e-f64b-469f-9792-178328df0868.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3192c3ab-b3c7-4789-aab7-7f139a40ef25.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3192c3ab-b3c7-4789-aab7-7f139a40ef25.lance deleted file mode 100644 index 0a769ed3d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3192c3ab-b3c7-4789-aab7-7f139a40ef25.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/31dc2665-d71d-4e9d-93cd-0d6b289837b5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/31dc2665-d71d-4e9d-93cd-0d6b289837b5.lance deleted file mode 100644 index 5e38fea86..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/31dc2665-d71d-4e9d-93cd-0d6b289837b5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/31e42d02-ec23-4db7-adc8-2f13c3a2d17a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/31e42d02-ec23-4db7-adc8-2f13c3a2d17a.lance deleted file mode 100644 index 4a347a8bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/31e42d02-ec23-4db7-adc8-2f13c3a2d17a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/323bcda0-c8fd-4dca-9e98-2385f87c0a7c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/323bcda0-c8fd-4dca-9e98-2385f87c0a7c.lance deleted file mode 100644 index f51f51095..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/323bcda0-c8fd-4dca-9e98-2385f87c0a7c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/324024dd-38d8-4ce9-acfa-a4e132f7a36f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/324024dd-38d8-4ce9-acfa-a4e132f7a36f.lance deleted file mode 100644 index 4a8bc09ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/324024dd-38d8-4ce9-acfa-a4e132f7a36f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/324c1b1f-8c68-42fd-8e52-93cb5ccf0ab9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/324c1b1f-8c68-42fd-8e52-93cb5ccf0ab9.lance deleted file mode 100644 index 6ad6084a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/324c1b1f-8c68-42fd-8e52-93cb5ccf0ab9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3263126e-25ac-4c5b-8790-82f41bb9ebdd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3263126e-25ac-4c5b-8790-82f41bb9ebdd.lance deleted file mode 100644 index ee26bcfa9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3263126e-25ac-4c5b-8790-82f41bb9ebdd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/32747499-8cbd-400d-ad65-c1790e1ff302.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/32747499-8cbd-400d-ad65-c1790e1ff302.lance deleted file mode 100644 index d4369c861..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/32747499-8cbd-400d-ad65-c1790e1ff302.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/32943217-7481-48de-8ff5-1e07b52d2a0e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/32943217-7481-48de-8ff5-1e07b52d2a0e.lance deleted file mode 100644 index d360a5620..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/32943217-7481-48de-8ff5-1e07b52d2a0e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3295a0db-9688-4fab-ab2c-15edebf10f27.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3295a0db-9688-4fab-ab2c-15edebf10f27.lance deleted file mode 100644 index 4c7577dcc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3295a0db-9688-4fab-ab2c-15edebf10f27.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/32a8ea05-7d2e-4e34-8471-e7c3f00cface.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/32a8ea05-7d2e-4e34-8471-e7c3f00cface.lance deleted file mode 100644 index b4e32b65d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/32a8ea05-7d2e-4e34-8471-e7c3f00cface.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/32f89e71-0d28-42a0-b7f2-fd2fce94a71f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/32f89e71-0d28-42a0-b7f2-fd2fce94a71f.lance deleted file mode 100644 index ec2ee0a18..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/32f89e71-0d28-42a0-b7f2-fd2fce94a71f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/32fc0cf2-3754-4938-8783-37cf3f0db766.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/32fc0cf2-3754-4938-8783-37cf3f0db766.lance deleted file mode 100644 index fc9ed8301..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/32fc0cf2-3754-4938-8783-37cf3f0db766.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/33070c27-2ce9-45cb-a42d-7cb94b6b690d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/33070c27-2ce9-45cb-a42d-7cb94b6b690d.lance deleted file mode 100644 index 252aa5993..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/33070c27-2ce9-45cb-a42d-7cb94b6b690d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/330b0f1c-5bb0-4b89-90c8-abac09c4fa3e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/330b0f1c-5bb0-4b89-90c8-abac09c4fa3e.lance deleted file mode 100644 index 00fd85b07..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/330b0f1c-5bb0-4b89-90c8-abac09c4fa3e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/331af503-07c3-4dab-a14c-06b13cbf271c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/331af503-07c3-4dab-a14c-06b13cbf271c.lance deleted file mode 100644 index 89a078d2d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/331af503-07c3-4dab-a14c-06b13cbf271c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/33a25d6d-2df4-41e9-80a5-ac747f49ccf2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/33a25d6d-2df4-41e9-80a5-ac747f49ccf2.lance deleted file mode 100644 index 413749d34..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/33a25d6d-2df4-41e9-80a5-ac747f49ccf2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/33b2c5ee-225e-4359-890a-0f3b4ab92086.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/33b2c5ee-225e-4359-890a-0f3b4ab92086.lance deleted file mode 100644 index bc8a031b7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/33b2c5ee-225e-4359-890a-0f3b4ab92086.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/33c11002-c589-41d1-a357-98ae484bfde7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/33c11002-c589-41d1-a357-98ae484bfde7.lance deleted file mode 100644 index 0e025f927..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/33c11002-c589-41d1-a357-98ae484bfde7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/33c49c7e-858d-438b-9709-9ebe4635554a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/33c49c7e-858d-438b-9709-9ebe4635554a.lance deleted file mode 100644 index 0cff04fee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/33c49c7e-858d-438b-9709-9ebe4635554a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3406bc35-7482-4c0a-8d35-59d6c9fedd48.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3406bc35-7482-4c0a-8d35-59d6c9fedd48.lance deleted file mode 100644 index 9bb1c30ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3406bc35-7482-4c0a-8d35-59d6c9fedd48.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/341676db-c8d4-4908-8b43-3e57f860ca07.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/341676db-c8d4-4908-8b43-3e57f860ca07.lance deleted file mode 100644 index c03a659e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/341676db-c8d4-4908-8b43-3e57f860ca07.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34676786-2086-4db9-afb9-65eb6bcad3fb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34676786-2086-4db9-afb9-65eb6bcad3fb.lance deleted file mode 100644 index 3649abc52..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34676786-2086-4db9-afb9-65eb6bcad3fb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3480b75a-9b78-42d4-bad8-9f61ba6d32a9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3480b75a-9b78-42d4-bad8-9f61ba6d32a9.lance deleted file mode 100644 index 371a51802..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3480b75a-9b78-42d4-bad8-9f61ba6d32a9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/348fc794-33eb-4bca-a03a-0b75675f8e09.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/348fc794-33eb-4bca-a03a-0b75675f8e09.lance deleted file mode 100644 index b44647ece..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/348fc794-33eb-4bca-a03a-0b75675f8e09.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34bc8f35-5b5d-4704-961d-b891041a95c2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34bc8f35-5b5d-4704-961d-b891041a95c2.lance deleted file mode 100644 index 161c4b191..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34bc8f35-5b5d-4704-961d-b891041a95c2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34bd52ff-b967-49f5-8761-c1b4a8a4001d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34bd52ff-b967-49f5-8761-c1b4a8a4001d.lance deleted file mode 100644 index 4ed5e644c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34bd52ff-b967-49f5-8761-c1b4a8a4001d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34eeadb5-7a69-4f03-9e9c-ce3bee7dec95.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34eeadb5-7a69-4f03-9e9c-ce3bee7dec95.lance deleted file mode 100644 index 4ab3ecce3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34eeadb5-7a69-4f03-9e9c-ce3bee7dec95.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34f226f5-8df0-410b-9827-ee2ddea937f0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34f226f5-8df0-410b-9827-ee2ddea937f0.lance deleted file mode 100644 index a26a25ef5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34f226f5-8df0-410b-9827-ee2ddea937f0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34ff458f-8bec-467e-9664-141631ced434.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34ff458f-8bec-467e-9664-141631ced434.lance deleted file mode 100644 index 634919eea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/34ff458f-8bec-467e-9664-141631ced434.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/35034294-5c0f-46fe-9d04-b880d3b0aa5e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/35034294-5c0f-46fe-9d04-b880d3b0aa5e.lance deleted file mode 100644 index eb9c46991..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/35034294-5c0f-46fe-9d04-b880d3b0aa5e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3528e91d-fb33-4cba-acf6-5f9b19781a2d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3528e91d-fb33-4cba-acf6-5f9b19781a2d.lance deleted file mode 100644 index 67714fe6a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3528e91d-fb33-4cba-acf6-5f9b19781a2d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/353a878f-8329-4040-b748-13e455ebe2ef.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/353a878f-8329-4040-b748-13e455ebe2ef.lance deleted file mode 100644 index 53a23b3da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/353a878f-8329-4040-b748-13e455ebe2ef.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3548e34f-7a42-4003-b124-7a0047eccc5e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3548e34f-7a42-4003-b124-7a0047eccc5e.lance deleted file mode 100644 index fcb0efe75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3548e34f-7a42-4003-b124-7a0047eccc5e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/35ae4a19-0af0-494d-aa9a-7f3dab4621af.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/35ae4a19-0af0-494d-aa9a-7f3dab4621af.lance deleted file mode 100644 index 965fb7b91..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/35ae4a19-0af0-494d-aa9a-7f3dab4621af.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/35b973a1-50f1-4e75-84ad-f36594b52eb6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/35b973a1-50f1-4e75-84ad-f36594b52eb6.lance deleted file mode 100644 index 12fd1b542..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/35b973a1-50f1-4e75-84ad-f36594b52eb6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/35bb6cea-adbc-4dfa-9f05-1f049ed0a602.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/35bb6cea-adbc-4dfa-9f05-1f049ed0a602.lance deleted file mode 100644 index ceea01d52..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/35bb6cea-adbc-4dfa-9f05-1f049ed0a602.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/36016f57-f1ac-4761-85d5-a8877a592c22.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/36016f57-f1ac-4761-85d5-a8877a592c22.lance deleted file mode 100644 index 41158df11..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/36016f57-f1ac-4761-85d5-a8877a592c22.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3629739d-f01e-4212-ab40-71809f2f01b7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3629739d-f01e-4212-ab40-71809f2f01b7.lance deleted file mode 100644 index 6b84b79d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3629739d-f01e-4212-ab40-71809f2f01b7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/36cd66bd-1d56-45ff-a9cb-593516e217e5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/36cd66bd-1d56-45ff-a9cb-593516e217e5.lance deleted file mode 100644 index 088ba9f9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/36cd66bd-1d56-45ff-a9cb-593516e217e5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/36e26dc8-f298-483a-9e9e-dbd0df865fac.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/36e26dc8-f298-483a-9e9e-dbd0df865fac.lance deleted file mode 100644 index 890fcc459..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/36e26dc8-f298-483a-9e9e-dbd0df865fac.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/36fa184a-20f0-4f63-9585-d53a68cdded5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/36fa184a-20f0-4f63-9585-d53a68cdded5.lance deleted file mode 100644 index 22bc88540..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/36fa184a-20f0-4f63-9585-d53a68cdded5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3704425d-2da5-4b80-8b4d-4375480e575f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3704425d-2da5-4b80-8b4d-4375480e575f.lance deleted file mode 100644 index bae395508..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3704425d-2da5-4b80-8b4d-4375480e575f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/371582a5-06da-479a-894e-4165f5dea87e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/371582a5-06da-479a-894e-4165f5dea87e.lance deleted file mode 100644 index e07aecf47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/371582a5-06da-479a-894e-4165f5dea87e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/37261d25-3bf3-4a24-b3d5-fd60044ec144.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/37261d25-3bf3-4a24-b3d5-fd60044ec144.lance deleted file mode 100644 index e7b903a2b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/37261d25-3bf3-4a24-b3d5-fd60044ec144.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/37328950-9c42-4867-8519-be1424bde111.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/37328950-9c42-4867-8519-be1424bde111.lance deleted file mode 100644 index 6d623b3c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/37328950-9c42-4867-8519-be1424bde111.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/374203fb-813d-4184-8b08-c085951459b9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/374203fb-813d-4184-8b08-c085951459b9.lance deleted file mode 100644 index 10c09d857..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/374203fb-813d-4184-8b08-c085951459b9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/374c3189-5514-4714-86ad-530d8d54e841.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/374c3189-5514-4714-86ad-530d8d54e841.lance deleted file mode 100644 index 3df7896e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/374c3189-5514-4714-86ad-530d8d54e841.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/374fca16-c1ea-4850-9837-6b1b7ce96bca.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/374fca16-c1ea-4850-9837-6b1b7ce96bca.lance deleted file mode 100644 index 1afe40025..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/374fca16-c1ea-4850-9837-6b1b7ce96bca.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3755bad1-9d10-432b-9b32-a6e5e38ddd01.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3755bad1-9d10-432b-9b32-a6e5e38ddd01.lance deleted file mode 100644 index 824dc9d1d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3755bad1-9d10-432b-9b32-a6e5e38ddd01.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/37b843ae-5a6d-4764-be42-eec4ecceab09.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/37b843ae-5a6d-4764-be42-eec4ecceab09.lance deleted file mode 100644 index 8ce3dbd90..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/37b843ae-5a6d-4764-be42-eec4ecceab09.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/37cb05b0-097f-4c8c-a44f-bbdd6eff80c4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/37cb05b0-097f-4c8c-a44f-bbdd6eff80c4.lance deleted file mode 100644 index 3d5a7845e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/37cb05b0-097f-4c8c-a44f-bbdd6eff80c4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/37ec0e8d-17a5-4cd9-b035-dfc9f3163394.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/37ec0e8d-17a5-4cd9-b035-dfc9f3163394.lance deleted file mode 100644 index e75b540e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/37ec0e8d-17a5-4cd9-b035-dfc9f3163394.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3809cb82-deb7-436c-8a24-6555e5633786.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3809cb82-deb7-436c-8a24-6555e5633786.lance deleted file mode 100644 index fad7cfb07..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3809cb82-deb7-436c-8a24-6555e5633786.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/38102092-09a9-4f43-9199-ffe36c2459c4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/38102092-09a9-4f43-9199-ffe36c2459c4.lance deleted file mode 100644 index 328c13fae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/38102092-09a9-4f43-9199-ffe36c2459c4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/383f4889-a43c-4f4c-9bb5-c19e076ad93a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/383f4889-a43c-4f4c-9bb5-c19e076ad93a.lance deleted file mode 100644 index d1c2a9b9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/383f4889-a43c-4f4c-9bb5-c19e076ad93a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/38505975-a6c2-48b5-ac96-7633c0844bf6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/38505975-a6c2-48b5-ac96-7633c0844bf6.lance deleted file mode 100644 index 5a49b6d84..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/38505975-a6c2-48b5-ac96-7633c0844bf6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/385a65d6-5225-455e-b207-f97318ad3951.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/385a65d6-5225-455e-b207-f97318ad3951.lance deleted file mode 100644 index c31da782b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/385a65d6-5225-455e-b207-f97318ad3951.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/389b4884-aac3-4a1e-9d99-aeff54769574.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/389b4884-aac3-4a1e-9d99-aeff54769574.lance deleted file mode 100644 index faa9f0a07..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/389b4884-aac3-4a1e-9d99-aeff54769574.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/38c6540c-1d1f-4684-8baf-aad7e283ee58.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/38c6540c-1d1f-4684-8baf-aad7e283ee58.lance deleted file mode 100644 index 3b806557f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/38c6540c-1d1f-4684-8baf-aad7e283ee58.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/38ecf3fa-7eee-461c-a8df-bde0af9b9995.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/38ecf3fa-7eee-461c-a8df-bde0af9b9995.lance deleted file mode 100644 index 191e60123..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/38ecf3fa-7eee-461c-a8df-bde0af9b9995.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3903bf46-4736-4fe5-a661-54d393d0e9d7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3903bf46-4736-4fe5-a661-54d393d0e9d7.lance deleted file mode 100644 index 7a2d1cdbf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3903bf46-4736-4fe5-a661-54d393d0e9d7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/39141122-6ce3-47b6-8487-f1dcb6a44b40.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/39141122-6ce3-47b6-8487-f1dcb6a44b40.lance deleted file mode 100644 index f7c9cc6cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/39141122-6ce3-47b6-8487-f1dcb6a44b40.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/39156c91-3bca-4197-8471-14670e35334d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/39156c91-3bca-4197-8471-14670e35334d.lance deleted file mode 100644 index 3a1095db9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/39156c91-3bca-4197-8471-14670e35334d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3915b5d1-e146-40da-9245-2b5e0ed6c7f3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3915b5d1-e146-40da-9245-2b5e0ed6c7f3.lance deleted file mode 100644 index 714a884bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3915b5d1-e146-40da-9245-2b5e0ed6c7f3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/391dc6ad-6ba5-43cf-a8f2-898f64767e5a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/391dc6ad-6ba5-43cf-a8f2-898f64767e5a.lance deleted file mode 100644 index ffb44f1b0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/391dc6ad-6ba5-43cf-a8f2-898f64767e5a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/394993d2-6353-4383-889f-233ff462640c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/394993d2-6353-4383-889f-233ff462640c.lance deleted file mode 100644 index f0b4bca1a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/394993d2-6353-4383-889f-233ff462640c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/398a81a8-6eda-4148-9701-bf6dc870fb4f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/398a81a8-6eda-4148-9701-bf6dc870fb4f.lance deleted file mode 100644 index 7f139aa54..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/398a81a8-6eda-4148-9701-bf6dc870fb4f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/39e2454a-45f8-42af-b260-a521b47b0ae6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/39e2454a-45f8-42af-b260-a521b47b0ae6.lance deleted file mode 100644 index ec33c4bee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/39e2454a-45f8-42af-b260-a521b47b0ae6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/39fa1afd-c58e-4305-8dc6-e5514d2c3deb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/39fa1afd-c58e-4305-8dc6-e5514d2c3deb.lance deleted file mode 100644 index a31c24376..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/39fa1afd-c58e-4305-8dc6-e5514d2c3deb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a21007e-c127-44fe-b0eb-617a19135a69.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a21007e-c127-44fe-b0eb-617a19135a69.lance deleted file mode 100644 index 6b37587e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a21007e-c127-44fe-b0eb-617a19135a69.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a272c02-a77e-4149-8dff-90e2596af8dd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a272c02-a77e-4149-8dff-90e2596af8dd.lance deleted file mode 100644 index 861ed418b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a272c02-a77e-4149-8dff-90e2596af8dd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a33a074-c82b-4ef8-a020-016f5af9a9eb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a33a074-c82b-4ef8-a020-016f5af9a9eb.lance deleted file mode 100644 index fdd5d1a76..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a33a074-c82b-4ef8-a020-016f5af9a9eb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a36e6bc-96f0-4aa0-9f28-2ec119bd3956.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a36e6bc-96f0-4aa0-9f28-2ec119bd3956.lance deleted file mode 100644 index 327b6f651..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a36e6bc-96f0-4aa0-9f28-2ec119bd3956.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a393e87-6518-4a8e-8bba-c229a0b5092e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a393e87-6518-4a8e-8bba-c229a0b5092e.lance deleted file mode 100644 index b8d079208..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a393e87-6518-4a8e-8bba-c229a0b5092e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a40dc5e-a66d-4b3e-b2fa-ef3f706c7942.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a40dc5e-a66d-4b3e-b2fa-ef3f706c7942.lance deleted file mode 100644 index 28b5e090f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a40dc5e-a66d-4b3e-b2fa-ef3f706c7942.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a790ae9-9181-470e-bea0-586f4a36fb8f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a790ae9-9181-470e-bea0-586f4a36fb8f.lance deleted file mode 100644 index b3dce1b57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3a790ae9-9181-470e-bea0-586f4a36fb8f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ab7480c-7f7f-434f-9989-0a27361202df.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ab7480c-7f7f-434f-9989-0a27361202df.lance deleted file mode 100644 index e270447de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ab7480c-7f7f-434f-9989-0a27361202df.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ac69ac3-a7da-4344-996b-f1387262619a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ac69ac3-a7da-4344-996b-f1387262619a.lance deleted file mode 100644 index 59e219b47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ac69ac3-a7da-4344-996b-f1387262619a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ad0167f-e88f-4113-b574-7091514685e5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ad0167f-e88f-4113-b574-7091514685e5.lance deleted file mode 100644 index b8210ad67..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ad0167f-e88f-4113-b574-7091514685e5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ad9aeec-d59b-402a-939b-c2b22769e453.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ad9aeec-d59b-402a-939b-c2b22769e453.lance deleted file mode 100644 index ef303ef56..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ad9aeec-d59b-402a-939b-c2b22769e453.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ad9d067-80d7-4b3f-8808-6f8a35fa5900.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ad9d067-80d7-4b3f-8808-6f8a35fa5900.lance deleted file mode 100644 index 0a85b28f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ad9d067-80d7-4b3f-8808-6f8a35fa5900.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b065323-6980-41a8-99fa-f068dc53719f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b065323-6980-41a8-99fa-f068dc53719f.lance deleted file mode 100644 index 2485ca55c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b065323-6980-41a8-99fa-f068dc53719f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b281b68-c1ec-48a4-9aff-48139febaf4b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b281b68-c1ec-48a4-9aff-48139febaf4b.lance deleted file mode 100644 index ba5ae13f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b281b68-c1ec-48a4-9aff-48139febaf4b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b46ac36-402d-436a-af8f-2a79895f1ab8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b46ac36-402d-436a-af8f-2a79895f1ab8.lance deleted file mode 100644 index 4743df2de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b46ac36-402d-436a-af8f-2a79895f1ab8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b68c7fa-bb1f-4cf7-bca3-772cb80711b8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b68c7fa-bb1f-4cf7-bca3-772cb80711b8.lance deleted file mode 100644 index 60cc66e4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b68c7fa-bb1f-4cf7-bca3-772cb80711b8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b7a6d25-c265-42ed-be5d-e1519ea9ef76.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b7a6d25-c265-42ed-be5d-e1519ea9ef76.lance deleted file mode 100644 index aefb64e98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b7a6d25-c265-42ed-be5d-e1519ea9ef76.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b807518-01b0-4a26-8511-2c112a18a9e5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b807518-01b0-4a26-8511-2c112a18a9e5.lance deleted file mode 100644 index 17b1777de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3b807518-01b0-4a26-8511-2c112a18a9e5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ba5fff2-2181-408f-a3dd-d6b80956fb2b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ba5fff2-2181-408f-a3dd-d6b80956fb2b.lance deleted file mode 100644 index d577ec46f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ba5fff2-2181-408f-a3dd-d6b80956fb2b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3bb6fd37-8a32-4409-b208-6dd47d44c864.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3bb6fd37-8a32-4409-b208-6dd47d44c864.lance deleted file mode 100644 index 02997003c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3bb6fd37-8a32-4409-b208-6dd47d44c864.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c198fe1-da6f-4ed3-8b0d-a119c0da9079.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c198fe1-da6f-4ed3-8b0d-a119c0da9079.lance deleted file mode 100644 index eae634946..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c198fe1-da6f-4ed3-8b0d-a119c0da9079.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c266871-aa51-4306-8c1e-30381552ffc5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c266871-aa51-4306-8c1e-30381552ffc5.lance deleted file mode 100644 index e8952700b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c266871-aa51-4306-8c1e-30381552ffc5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c35ed8c-8217-4b4a-9897-666c176a5fd1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c35ed8c-8217-4b4a-9897-666c176a5fd1.lance deleted file mode 100644 index e08b2ee77..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c35ed8c-8217-4b4a-9897-666c176a5fd1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c4660e3-5f84-4352-b6b5-9837ce6ac4a2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c4660e3-5f84-4352-b6b5-9837ce6ac4a2.lance deleted file mode 100644 index 50dee8906..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c4660e3-5f84-4352-b6b5-9837ce6ac4a2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c5082c6-57dc-4914-add1-a26385d710d3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c5082c6-57dc-4914-add1-a26385d710d3.lance deleted file mode 100644 index 91ddb0fff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c5082c6-57dc-4914-add1-a26385d710d3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c532f95-be14-45cc-9587-87003a939256.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c532f95-be14-45cc-9587-87003a939256.lance deleted file mode 100644 index 64ae03d4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c532f95-be14-45cc-9587-87003a939256.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c5af01c-5ddb-4c80-9c53-2bff018bda70.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c5af01c-5ddb-4c80-9c53-2bff018bda70.lance deleted file mode 100644 index c018afb88..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c5af01c-5ddb-4c80-9c53-2bff018bda70.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c5f8f9c-a11d-464d-9f7d-5d48adbc46bb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c5f8f9c-a11d-464d-9f7d-5d48adbc46bb.lance deleted file mode 100644 index 7d3fa8634..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c5f8f9c-a11d-464d-9f7d-5d48adbc46bb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c9eb419-93f1-46b9-9f36-9fa52f30607d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c9eb419-93f1-46b9-9f36-9fa52f30607d.lance deleted file mode 100644 index 25983c6fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3c9eb419-93f1-46b9-9f36-9fa52f30607d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ccc211e-9aae-42ea-8aad-36d1c0db6f37.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ccc211e-9aae-42ea-8aad-36d1c0db6f37.lance deleted file mode 100644 index d6a31e506..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ccc211e-9aae-42ea-8aad-36d1c0db6f37.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ce38341-c252-46c2-98e7-e714a8b472d5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ce38341-c252-46c2-98e7-e714a8b472d5.lance deleted file mode 100644 index 0119dcba3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ce38341-c252-46c2-98e7-e714a8b472d5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3d0d3767-b301-472c-a79d-b4c6270f62df.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3d0d3767-b301-472c-a79d-b4c6270f62df.lance deleted file mode 100644 index e2a474a4d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3d0d3767-b301-472c-a79d-b4c6270f62df.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3d148aed-2a09-4414-9b20-29678b4e16f0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3d148aed-2a09-4414-9b20-29678b4e16f0.lance deleted file mode 100644 index 22482ab9c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3d148aed-2a09-4414-9b20-29678b4e16f0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3d38fe04-ae84-4b1e-9cbb-ab1df6b5a8fb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3d38fe04-ae84-4b1e-9cbb-ab1df6b5a8fb.lance deleted file mode 100644 index ca1ab8da6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3d38fe04-ae84-4b1e-9cbb-ab1df6b5a8fb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3d6b145a-8521-493d-9976-523ae778024e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3d6b145a-8521-493d-9976-523ae778024e.lance deleted file mode 100644 index 5b076e923..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3d6b145a-8521-493d-9976-523ae778024e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3d8d8e7b-7403-4c95-9140-d128f5616fd0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3d8d8e7b-7403-4c95-9140-d128f5616fd0.lance deleted file mode 100644 index 53d870607..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3d8d8e7b-7403-4c95-9140-d128f5616fd0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3dc310de-934d-4613-bd16-8ed1ded2a9bd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3dc310de-934d-4613-bd16-8ed1ded2a9bd.lance deleted file mode 100644 index 883d9dfd5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3dc310de-934d-4613-bd16-8ed1ded2a9bd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3dd5a62a-2f90-4f1c-ab83-7eaba6d6cc53.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3dd5a62a-2f90-4f1c-ab83-7eaba6d6cc53.lance deleted file mode 100644 index 4fe36e844..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3dd5a62a-2f90-4f1c-ab83-7eaba6d6cc53.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3de28ec1-e1d5-4b3b-8965-5c428cc0995d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3de28ec1-e1d5-4b3b-8965-5c428cc0995d.lance deleted file mode 100644 index e4eff9585..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3de28ec1-e1d5-4b3b-8965-5c428cc0995d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3deebe19-232a-470d-b572-f301a82ce7d9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3deebe19-232a-470d-b572-f301a82ce7d9.lance deleted file mode 100644 index 74b5aed44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3deebe19-232a-470d-b572-f301a82ce7d9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3e44a32f-37a8-4c17-8a1f-817014b7cdfd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3e44a32f-37a8-4c17-8a1f-817014b7cdfd.lance deleted file mode 100644 index 7a61ddfc6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3e44a32f-37a8-4c17-8a1f-817014b7cdfd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3e5374dc-8780-481c-98fc-c2ca4a5c8729.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3e5374dc-8780-481c-98fc-c2ca4a5c8729.lance deleted file mode 100644 index 2785cc224..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3e5374dc-8780-481c-98fc-c2ca4a5c8729.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3e733e5f-609d-42b3-a4d4-ae9e2135f285.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3e733e5f-609d-42b3-a4d4-ae9e2135f285.lance deleted file mode 100644 index 176271246..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3e733e5f-609d-42b3-a4d4-ae9e2135f285.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3e78fe52-87bd-41b5-8be9-690544093d36.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3e78fe52-87bd-41b5-8be9-690544093d36.lance deleted file mode 100644 index 3f886855b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3e78fe52-87bd-41b5-8be9-690544093d36.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3e942e04-585d-4ed2-8c47-ab50f2bf7922.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3e942e04-585d-4ed2-8c47-ab50f2bf7922.lance deleted file mode 100644 index 5031498dd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3e942e04-585d-4ed2-8c47-ab50f2bf7922.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ebca3be-a1d7-4415-bf20-3edd56c71576.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ebca3be-a1d7-4415-bf20-3edd56c71576.lance deleted file mode 100644 index 3f7a81810..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ebca3be-a1d7-4415-bf20-3edd56c71576.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ec19482-c0da-478e-8b03-1862cc5cb471.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ec19482-c0da-478e-8b03-1862cc5cb471.lance deleted file mode 100644 index 49705d455..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ec19482-c0da-478e-8b03-1862cc5cb471.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ec4199c-e3ae-45cf-a52f-22c41a367ee5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ec4199c-e3ae-45cf-a52f-22c41a367ee5.lance deleted file mode 100644 index 472729f2d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ec4199c-e3ae-45cf-a52f-22c41a367ee5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ec81842-ff59-47ae-8a86-c5f1d304c291.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ec81842-ff59-47ae-8a86-c5f1d304c291.lance deleted file mode 100644 index 2f0dcfa27..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ec81842-ff59-47ae-8a86-c5f1d304c291.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ee2ecc3-d530-4d33-b60d-a5ada608ddc9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ee2ecc3-d530-4d33-b60d-a5ada608ddc9.lance deleted file mode 100644 index fd8e31cf3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ee2ecc3-d530-4d33-b60d-a5ada608ddc9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3eef9315-9f3c-4d8c-bd5a-f53a4a27bdb5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3eef9315-9f3c-4d8c-bd5a-f53a4a27bdb5.lance deleted file mode 100644 index 041c41d41..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3eef9315-9f3c-4d8c-bd5a-f53a4a27bdb5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3f104dba-76cf-4c0b-8756-81b2fcb2d42f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3f104dba-76cf-4c0b-8756-81b2fcb2d42f.lance deleted file mode 100644 index d61487558..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3f104dba-76cf-4c0b-8756-81b2fcb2d42f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3f25d9ca-5b2f-4822-8330-94ca037c90a7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3f25d9ca-5b2f-4822-8330-94ca037c90a7.lance deleted file mode 100644 index f23321762..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3f25d9ca-5b2f-4822-8330-94ca037c90a7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3f3deeed-04ba-475c-9e71-b89cd2edcd37.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3f3deeed-04ba-475c-9e71-b89cd2edcd37.lance deleted file mode 100644 index 4f6af47c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3f3deeed-04ba-475c-9e71-b89cd2edcd37.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3f5f50f5-f2d5-4caa-bc91-7fc73b72c044.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3f5f50f5-f2d5-4caa-bc91-7fc73b72c044.lance deleted file mode 100644 index 2990e533b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3f5f50f5-f2d5-4caa-bc91-7fc73b72c044.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3f7d334e-b680-4f8b-b5b8-50aea211a803.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3f7d334e-b680-4f8b-b5b8-50aea211a803.lance deleted file mode 100644 index 31c862f15..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3f7d334e-b680-4f8b-b5b8-50aea211a803.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3fc26b0b-e05c-45c7-a361-f38627cdea08.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3fc26b0b-e05c-45c7-a361-f38627cdea08.lance deleted file mode 100644 index c302161d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3fc26b0b-e05c-45c7-a361-f38627cdea08.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3fe65b47-05ce-4145-a795-207eae9b6707.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3fe65b47-05ce-4145-a795-207eae9b6707.lance deleted file mode 100644 index 1ed08d953..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3fe65b47-05ce-4145-a795-207eae9b6707.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ff10b1c-871b-42df-9aa0-4b143a4d3d71.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ff10b1c-871b-42df-9aa0-4b143a4d3d71.lance deleted file mode 100644 index 202597bc3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/3ff10b1c-871b-42df-9aa0-4b143a4d3d71.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/400d338a-0d2c-4aef-b3b5-37695fb6df8a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/400d338a-0d2c-4aef-b3b5-37695fb6df8a.lance deleted file mode 100644 index 74602161a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/400d338a-0d2c-4aef-b3b5-37695fb6df8a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40496d19-0771-477d-b562-5e535beba29c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40496d19-0771-477d-b562-5e535beba29c.lance deleted file mode 100644 index 9803f4469..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40496d19-0771-477d-b562-5e535beba29c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/405a4a20-e6c5-4060-9a2f-d8a8b13d0ca2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/405a4a20-e6c5-4060-9a2f-d8a8b13d0ca2.lance deleted file mode 100644 index 4fa555407..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/405a4a20-e6c5-4060-9a2f-d8a8b13d0ca2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/406f7dd7-e941-4e94-9866-55cedb8b9aba.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/406f7dd7-e941-4e94-9866-55cedb8b9aba.lance deleted file mode 100644 index bd9aedea3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/406f7dd7-e941-4e94-9866-55cedb8b9aba.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4072314e-4e6d-40a9-b10a-5a62fa7cc0e2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4072314e-4e6d-40a9-b10a-5a62fa7cc0e2.lance deleted file mode 100644 index 9da3c165a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4072314e-4e6d-40a9-b10a-5a62fa7cc0e2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4096490b-dd6e-4082-b532-c81f5ec1e1f9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4096490b-dd6e-4082-b532-c81f5ec1e1f9.lance deleted file mode 100644 index 6c22cd00c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4096490b-dd6e-4082-b532-c81f5ec1e1f9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40cd54fb-67c5-4c7c-b089-fa976989758d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40cd54fb-67c5-4c7c-b089-fa976989758d.lance deleted file mode 100644 index 25a07c75d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40cd54fb-67c5-4c7c-b089-fa976989758d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40dcdc55-b9d8-4436-8100-1920e3f36de4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40dcdc55-b9d8-4436-8100-1920e3f36de4.lance deleted file mode 100644 index 23b254144..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40dcdc55-b9d8-4436-8100-1920e3f36de4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40f1fa44-db57-4b33-8bf5-e1a8134f676f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40f1fa44-db57-4b33-8bf5-e1a8134f676f.lance deleted file mode 100644 index f94972109..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40f1fa44-db57-4b33-8bf5-e1a8134f676f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40f4c109-cf57-4281-8f6d-49914b3de383.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40f4c109-cf57-4281-8f6d-49914b3de383.lance deleted file mode 100644 index d85f5647d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40f4c109-cf57-4281-8f6d-49914b3de383.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40f9cbff-77e7-47b8-995a-9372ab6a09fe.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40f9cbff-77e7-47b8-995a-9372ab6a09fe.lance deleted file mode 100644 index 4b7b6ef4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40f9cbff-77e7-47b8-995a-9372ab6a09fe.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40fc06a4-2979-40b2-9b38-bae3b6257f74.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40fc06a4-2979-40b2-9b38-bae3b6257f74.lance deleted file mode 100644 index bcf716d98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/40fc06a4-2979-40b2-9b38-bae3b6257f74.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4132f27c-2d98-4cde-802a-ae38d49981ae.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4132f27c-2d98-4cde-802a-ae38d49981ae.lance deleted file mode 100644 index e088ee3d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4132f27c-2d98-4cde-802a-ae38d49981ae.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/41387bb7-8900-405b-b961-07d98d884712.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/41387bb7-8900-405b-b961-07d98d884712.lance deleted file mode 100644 index 4540ddbe9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/41387bb7-8900-405b-b961-07d98d884712.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/413dda44-7e45-4289-8100-86eab4d49cc8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/413dda44-7e45-4289-8100-86eab4d49cc8.lance deleted file mode 100644 index d1d0094c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/413dda44-7e45-4289-8100-86eab4d49cc8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/415b4534-364d-4ca9-9f1e-628168e4400e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/415b4534-364d-4ca9-9f1e-628168e4400e.lance deleted file mode 100644 index 540f2ef5d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/415b4534-364d-4ca9-9f1e-628168e4400e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/415db6b3-e3e8-4329-a2bc-c27676b7b37d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/415db6b3-e3e8-4329-a2bc-c27676b7b37d.lance deleted file mode 100644 index 35402a3f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/415db6b3-e3e8-4329-a2bc-c27676b7b37d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/416ca66d-2714-42c5-a05f-e67dcb3b7e69.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/416ca66d-2714-42c5-a05f-e67dcb3b7e69.lance deleted file mode 100644 index 7f6eeeec0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/416ca66d-2714-42c5-a05f-e67dcb3b7e69.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/416f3f9c-07ae-4f50-9f63-0be0b4e73f56.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/416f3f9c-07ae-4f50-9f63-0be0b4e73f56.lance deleted file mode 100644 index 645f24a36..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/416f3f9c-07ae-4f50-9f63-0be0b4e73f56.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4179f122-afa6-478e-86c7-b3655cccb97a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4179f122-afa6-478e-86c7-b3655cccb97a.lance deleted file mode 100644 index a70385567..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4179f122-afa6-478e-86c7-b3655cccb97a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4188f9b3-1735-42b9-bcba-663d3b3119d4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4188f9b3-1735-42b9-bcba-663d3b3119d4.lance deleted file mode 100644 index b8444716c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4188f9b3-1735-42b9-bcba-663d3b3119d4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4199d029-b194-4fb7-ac68-ccd05229e032.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4199d029-b194-4fb7-ac68-ccd05229e032.lance deleted file mode 100644 index 3673de144..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4199d029-b194-4fb7-ac68-ccd05229e032.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/41d6bd18-ba4a-44d4-b40f-351359338a8e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/41d6bd18-ba4a-44d4-b40f-351359338a8e.lance deleted file mode 100644 index 9aef4e37b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/41d6bd18-ba4a-44d4-b40f-351359338a8e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/41f7e46d-edf7-47b2-b038-cf0e4d50010e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/41f7e46d-edf7-47b2-b038-cf0e4d50010e.lance deleted file mode 100644 index 995f4b2bd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/41f7e46d-edf7-47b2-b038-cf0e4d50010e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42038262-8c29-4dab-bb4d-da5fe529f374.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42038262-8c29-4dab-bb4d-da5fe529f374.lance deleted file mode 100644 index 81bbe6aa8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42038262-8c29-4dab-bb4d-da5fe529f374.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4211b4ab-6625-4751-8287-87774384963d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4211b4ab-6625-4751-8287-87774384963d.lance deleted file mode 100644 index 4a9a52640..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4211b4ab-6625-4751-8287-87774384963d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4228e6e9-70ba-4670-961a-641db5bf897c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4228e6e9-70ba-4670-961a-641db5bf897c.lance deleted file mode 100644 index 93beb95cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4228e6e9-70ba-4670-961a-641db5bf897c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/422cfcb3-d71a-4db6-adae-10d1bbd5652b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/422cfcb3-d71a-4db6-adae-10d1bbd5652b.lance deleted file mode 100644 index 024e0c2aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/422cfcb3-d71a-4db6-adae-10d1bbd5652b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42378299-918f-4b6a-bc30-d44d6e589eb8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42378299-918f-4b6a-bc30-d44d6e589eb8.lance deleted file mode 100644 index 0bfbfd9d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42378299-918f-4b6a-bc30-d44d6e589eb8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/423915a8-7417-4484-9ca5-8f6bfeb32095.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/423915a8-7417-4484-9ca5-8f6bfeb32095.lance deleted file mode 100644 index 55948bf50..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/423915a8-7417-4484-9ca5-8f6bfeb32095.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4239d646-1b56-4bba-ae70-78484ab9e060.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4239d646-1b56-4bba-ae70-78484ab9e060.lance deleted file mode 100644 index 44ad8cfc5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4239d646-1b56-4bba-ae70-78484ab9e060.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42506f6d-8a54-4891-8204-d7db7f5134d6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42506f6d-8a54-4891-8204-d7db7f5134d6.lance deleted file mode 100644 index 2cb00ba88..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42506f6d-8a54-4891-8204-d7db7f5134d6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42682256-4025-4647-8e9e-c9a622b02fe6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42682256-4025-4647-8e9e-c9a622b02fe6.lance deleted file mode 100644 index b783f4b5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42682256-4025-4647-8e9e-c9a622b02fe6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42758e5e-d18d-45a7-b372-93b2245b32a9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42758e5e-d18d-45a7-b372-93b2245b32a9.lance deleted file mode 100644 index 47df1da69..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42758e5e-d18d-45a7-b372-93b2245b32a9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4292ad86-8581-4315-a06d-d0debd5ce15f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4292ad86-8581-4315-a06d-d0debd5ce15f.lance deleted file mode 100644 index d104494f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4292ad86-8581-4315-a06d-d0debd5ce15f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42cb8903-8c8f-4515-89ff-6062b630fc1b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42cb8903-8c8f-4515-89ff-6062b630fc1b.lance deleted file mode 100644 index 6bf9c71cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42cb8903-8c8f-4515-89ff-6062b630fc1b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42d1cbcd-9c16-4326-96c1-523711f99592.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42d1cbcd-9c16-4326-96c1-523711f99592.lance deleted file mode 100644 index 5e946f653..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/42d1cbcd-9c16-4326-96c1-523711f99592.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/431f1c23-9727-437d-80d0-76757d81faa4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/431f1c23-9727-437d-80d0-76757d81faa4.lance deleted file mode 100644 index 6b179ecc1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/431f1c23-9727-437d-80d0-76757d81faa4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/433baf97-70ef-4642-ba75-88bb5226dce1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/433baf97-70ef-4642-ba75-88bb5226dce1.lance deleted file mode 100644 index f090c7363..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/433baf97-70ef-4642-ba75-88bb5226dce1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/433d58ca-537e-42b1-b43d-600d9d30f2e1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/433d58ca-537e-42b1-b43d-600d9d30f2e1.lance deleted file mode 100644 index 0acc2cdc7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/433d58ca-537e-42b1-b43d-600d9d30f2e1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4343ecb0-ed5c-4c5c-9119-aa52cb0a26a6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4343ecb0-ed5c-4c5c-9119-aa52cb0a26a6.lance deleted file mode 100644 index 45689b8e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4343ecb0-ed5c-4c5c-9119-aa52cb0a26a6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/434956f8-6008-49d2-93d9-244ec81ded4c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/434956f8-6008-49d2-93d9-244ec81ded4c.lance deleted file mode 100644 index b7f820ea8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/434956f8-6008-49d2-93d9-244ec81ded4c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/435e6736-dd94-40f1-84ae-c0abc468fdd6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/435e6736-dd94-40f1-84ae-c0abc468fdd6.lance deleted file mode 100644 index 13cf1626b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/435e6736-dd94-40f1-84ae-c0abc468fdd6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4360d731-89a7-4943-a77e-9c73a59a14a1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4360d731-89a7-4943-a77e-9c73a59a14a1.lance deleted file mode 100644 index 1caf22b53..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4360d731-89a7-4943-a77e-9c73a59a14a1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/436ac812-f234-49e5-8e95-9e9c77fc64e2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/436ac812-f234-49e5-8e95-9e9c77fc64e2.lance deleted file mode 100644 index 9d45cc477..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/436ac812-f234-49e5-8e95-9e9c77fc64e2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/439ca19d-226b-43dc-b7f5-1372cb57e7e1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/439ca19d-226b-43dc-b7f5-1372cb57e7e1.lance deleted file mode 100644 index 8652ac40b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/439ca19d-226b-43dc-b7f5-1372cb57e7e1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/439da5f7-6455-4014-b095-fd15b37ad52b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/439da5f7-6455-4014-b095-fd15b37ad52b.lance deleted file mode 100644 index 45f23dfe3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/439da5f7-6455-4014-b095-fd15b37ad52b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/43ed396a-37dd-4176-a225-368dd2c5d1b3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/43ed396a-37dd-4176-a225-368dd2c5d1b3.lance deleted file mode 100644 index f92410507..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/43ed396a-37dd-4176-a225-368dd2c5d1b3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/43f0618a-ef5b-44f6-81c0-f02e0ef28d31.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/43f0618a-ef5b-44f6-81c0-f02e0ef28d31.lance deleted file mode 100644 index f90817643..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/43f0618a-ef5b-44f6-81c0-f02e0ef28d31.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/43f4f582-38f3-4950-ac24-89312a0b9697.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/43f4f582-38f3-4950-ac24-89312a0b9697.lance deleted file mode 100644 index 38ed4114e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/43f4f582-38f3-4950-ac24-89312a0b9697.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/440aa30a-3284-42ab-909a-e7b1cfb63aea.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/440aa30a-3284-42ab-909a-e7b1cfb63aea.lance deleted file mode 100644 index c6016c136..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/440aa30a-3284-42ab-909a-e7b1cfb63aea.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44265cea-7a1a-41f9-bc79-3c33570f2d6b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44265cea-7a1a-41f9-bc79-3c33570f2d6b.lance deleted file mode 100644 index bff0342d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44265cea-7a1a-41f9-bc79-3c33570f2d6b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/444f033d-ddf0-406b-8d13-4f2f0873702e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/444f033d-ddf0-406b-8d13-4f2f0873702e.lance deleted file mode 100644 index 13c56b3db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/444f033d-ddf0-406b-8d13-4f2f0873702e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/447f4e30-e372-4ffc-b57d-faa6b9dc0dc4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/447f4e30-e372-4ffc-b57d-faa6b9dc0dc4.lance deleted file mode 100644 index f80e5a3e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/447f4e30-e372-4ffc-b57d-faa6b9dc0dc4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44912823-efc7-4918-9eb6-c47f069af6ba.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44912823-efc7-4918-9eb6-c47f069af6ba.lance deleted file mode 100644 index 087897770..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44912823-efc7-4918-9eb6-c47f069af6ba.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44918dfe-419d-43a1-918a-b5d99d3e9783.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44918dfe-419d-43a1-918a-b5d99d3e9783.lance deleted file mode 100644 index c074fed3b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44918dfe-419d-43a1-918a-b5d99d3e9783.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4499d7b5-9189-4026-b7b5-1f578f394bea.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4499d7b5-9189-4026-b7b5-1f578f394bea.lance deleted file mode 100644 index 80f581c16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4499d7b5-9189-4026-b7b5-1f578f394bea.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44a46c37-6c6d-4e49-8b82-645ffb26d889.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44a46c37-6c6d-4e49-8b82-645ffb26d889.lance deleted file mode 100644 index 2bbb1bc66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44a46c37-6c6d-4e49-8b82-645ffb26d889.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44ba34b8-b6a7-4511-8e32-986cebaed296.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44ba34b8-b6a7-4511-8e32-986cebaed296.lance deleted file mode 100644 index 2d4d19b40..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44ba34b8-b6a7-4511-8e32-986cebaed296.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44c01d7b-d431-4009-ba66-005616e6d2d1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44c01d7b-d431-4009-ba66-005616e6d2d1.lance deleted file mode 100644 index 6698f753b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44c01d7b-d431-4009-ba66-005616e6d2d1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44c0e479-f965-45af-8ce3-85056cccf7c0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44c0e479-f965-45af-8ce3-85056cccf7c0.lance deleted file mode 100644 index 1fc2f3ba8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44c0e479-f965-45af-8ce3-85056cccf7c0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44cfa1b5-8ce8-4b2a-b448-74c2fb3957c9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44cfa1b5-8ce8-4b2a-b448-74c2fb3957c9.lance deleted file mode 100644 index 12d4cd11f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44cfa1b5-8ce8-4b2a-b448-74c2fb3957c9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44fa8520-f0bb-4eb1-a21b-8b029ac5a7ea.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44fa8520-f0bb-4eb1-a21b-8b029ac5a7ea.lance deleted file mode 100644 index aede72f08..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44fa8520-f0bb-4eb1-a21b-8b029ac5a7ea.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44fc4ff0-9b54-4364-9b94-098c99c1efe3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44fc4ff0-9b54-4364-9b94-098c99c1efe3.lance deleted file mode 100644 index e604454d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/44fc4ff0-9b54-4364-9b94-098c99c1efe3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4509794a-4075-4ce4-86b1-f5bc0e46fbaf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4509794a-4075-4ce4-86b1-f5bc0e46fbaf.lance deleted file mode 100644 index 9e2fbd519..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4509794a-4075-4ce4-86b1-f5bc0e46fbaf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/450f3179-f6a6-4435-8fae-153e37185ef4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/450f3179-f6a6-4435-8fae-153e37185ef4.lance deleted file mode 100644 index 85641e42d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/450f3179-f6a6-4435-8fae-153e37185ef4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/451bb92f-0076-4256-913b-aae99ebdb9a5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/451bb92f-0076-4256-913b-aae99ebdb9a5.lance deleted file mode 100644 index df7658b63..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/451bb92f-0076-4256-913b-aae99ebdb9a5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/452637c2-6e71-4a92-a9f8-dce1b35b2a04.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/452637c2-6e71-4a92-a9f8-dce1b35b2a04.lance deleted file mode 100644 index 1714cbe2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/452637c2-6e71-4a92-a9f8-dce1b35b2a04.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/45465d8f-c9b9-4c71-95fd-9e857298a046.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/45465d8f-c9b9-4c71-95fd-9e857298a046.lance deleted file mode 100644 index 44312cb90..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/45465d8f-c9b9-4c71-95fd-9e857298a046.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/454f2414-a553-4637-8aaf-bef803450cc6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/454f2414-a553-4637-8aaf-bef803450cc6.lance deleted file mode 100644 index 84a4276f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/454f2414-a553-4637-8aaf-bef803450cc6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4559ee36-b930-48f5-b0fb-ce8b6aad22de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4559ee36-b930-48f5-b0fb-ce8b6aad22de.lance deleted file mode 100644 index 3ceaed8a6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4559ee36-b930-48f5-b0fb-ce8b6aad22de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4560a021-e86d-4585-9ffe-cab3cbda0c1e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4560a021-e86d-4585-9ffe-cab3cbda0c1e.lance deleted file mode 100644 index b57784593..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4560a021-e86d-4585-9ffe-cab3cbda0c1e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/45631a65-de3f-4e52-a6a8-53455c95c57f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/45631a65-de3f-4e52-a6a8-53455c95c57f.lance deleted file mode 100644 index 9d0f508e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/45631a65-de3f-4e52-a6a8-53455c95c57f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/459fbbee-43b7-40b3-9a84-1331242ce9a4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/459fbbee-43b7-40b3-9a84-1331242ce9a4.lance deleted file mode 100644 index 210e10d30..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/459fbbee-43b7-40b3-9a84-1331242ce9a4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/45bec8f6-6f4b-4d59-9600-71d1ad7b6b3c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/45bec8f6-6f4b-4d59-9600-71d1ad7b6b3c.lance deleted file mode 100644 index 54c618a9a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/45bec8f6-6f4b-4d59-9600-71d1ad7b6b3c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/45c9cddb-9506-45fb-a9c7-841704a7fc3c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/45c9cddb-9506-45fb-a9c7-841704a7fc3c.lance deleted file mode 100644 index 21f4a4d10..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/45c9cddb-9506-45fb-a9c7-841704a7fc3c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/46018d45-f249-45ca-bee8-c45638fcecf5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/46018d45-f249-45ca-bee8-c45638fcecf5.lance deleted file mode 100644 index bfe321ad3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/46018d45-f249-45ca-bee8-c45638fcecf5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/46116269-4e3f-49a6-885c-7e8676be75bc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/46116269-4e3f-49a6-885c-7e8676be75bc.lance deleted file mode 100644 index 739ca279b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/46116269-4e3f-49a6-885c-7e8676be75bc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/461d27c4-0820-4e14-a988-143a9233b87a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/461d27c4-0820-4e14-a988-143a9233b87a.lance deleted file mode 100644 index b74ae988d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/461d27c4-0820-4e14-a988-143a9233b87a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/463330b4-b9dc-4fc1-bc12-1dd3c8dfb8d1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/463330b4-b9dc-4fc1-bc12-1dd3c8dfb8d1.lance deleted file mode 100644 index 9f80ef7ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/463330b4-b9dc-4fc1-bc12-1dd3c8dfb8d1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/466e933f-2672-478a-af87-2b393c4e6fc4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/466e933f-2672-478a-af87-2b393c4e6fc4.lance deleted file mode 100644 index 6fd4fb464..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/466e933f-2672-478a-af87-2b393c4e6fc4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/46f80f14-20a6-455b-b470-802bd628417a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/46f80f14-20a6-455b-b470-802bd628417a.lance deleted file mode 100644 index 49f808f32..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/46f80f14-20a6-455b-b470-802bd628417a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/46f9f0a3-4d4e-4e6e-850a-df96212803d8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/46f9f0a3-4d4e-4e6e-850a-df96212803d8.lance deleted file mode 100644 index 00bd268ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/46f9f0a3-4d4e-4e6e-850a-df96212803d8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47065825-1414-49af-a6d0-789c68fced4a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47065825-1414-49af-a6d0-789c68fced4a.lance deleted file mode 100644 index 29aa6aafd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47065825-1414-49af-a6d0-789c68fced4a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4715aa38-59f8-4e39-a4ee-a069541de5b7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4715aa38-59f8-4e39-a4ee-a069541de5b7.lance deleted file mode 100644 index 739cebf72..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4715aa38-59f8-4e39-a4ee-a069541de5b7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47278d63-e37c-4593-90ae-3f910798ee7d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47278d63-e37c-4593-90ae-3f910798ee7d.lance deleted file mode 100644 index fa576256d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47278d63-e37c-4593-90ae-3f910798ee7d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4735789d-4117-4890-a489-0dcf6e328956.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4735789d-4117-4890-a489-0dcf6e328956.lance deleted file mode 100644 index 636eaad3b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4735789d-4117-4890-a489-0dcf6e328956.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4739d53b-0db0-451b-a549-a60f816c95f2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4739d53b-0db0-451b-a549-a60f816c95f2.lance deleted file mode 100644 index 9b0a4aa0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4739d53b-0db0-451b-a549-a60f816c95f2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/474a8c3c-5412-4ceb-b764-d2ba9fb327fc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/474a8c3c-5412-4ceb-b764-d2ba9fb327fc.lance deleted file mode 100644 index 2b69d1579..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/474a8c3c-5412-4ceb-b764-d2ba9fb327fc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4757b1cf-4e36-4ca8-a62f-873d0c238409.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4757b1cf-4e36-4ca8-a62f-873d0c238409.lance deleted file mode 100644 index 041052f88..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4757b1cf-4e36-4ca8-a62f-873d0c238409.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47656778-1658-415a-97cc-b098834cd510.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47656778-1658-415a-97cc-b098834cd510.lance deleted file mode 100644 index 48e5e5cc4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47656778-1658-415a-97cc-b098834cd510.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47756221-043c-4bbf-a6b9-de1b5c1d2267.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47756221-043c-4bbf-a6b9-de1b5c1d2267.lance deleted file mode 100644 index 31d22598a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47756221-043c-4bbf-a6b9-de1b5c1d2267.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/478b9136-4c12-4fa2-89e9-133713dd0cb4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/478b9136-4c12-4fa2-89e9-133713dd0cb4.lance deleted file mode 100644 index f88c8eaa5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/478b9136-4c12-4fa2-89e9-133713dd0cb4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47929616-2f08-476b-87fe-d35946f5c4d8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47929616-2f08-476b-87fe-d35946f5c4d8.lance deleted file mode 100644 index 2d7266590..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47929616-2f08-476b-87fe-d35946f5c4d8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47cd2f34-1d79-4f2c-a43a-0a116802e7bb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47cd2f34-1d79-4f2c-a43a-0a116802e7bb.lance deleted file mode 100644 index 93a5034ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47cd2f34-1d79-4f2c-a43a-0a116802e7bb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47e267bc-aac6-470a-bd1d-0bc1054d8d76.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47e267bc-aac6-470a-bd1d-0bc1054d8d76.lance deleted file mode 100644 index 65db15390..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47e267bc-aac6-470a-bd1d-0bc1054d8d76.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47f8bf27-05b8-40b2-ad58-dfd999d9af14.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47f8bf27-05b8-40b2-ad58-dfd999d9af14.lance deleted file mode 100644 index 80b7e5713..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/47f8bf27-05b8-40b2-ad58-dfd999d9af14.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4814d8cc-706c-4243-b2f2-187868130149.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4814d8cc-706c-4243-b2f2-187868130149.lance deleted file mode 100644 index 286f73f4e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4814d8cc-706c-4243-b2f2-187868130149.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/481d9c9c-e2e9-4c56-b66c-b136e5779c23.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/481d9c9c-e2e9-4c56-b66c-b136e5779c23.lance deleted file mode 100644 index 2ef292971..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/481d9c9c-e2e9-4c56-b66c-b136e5779c23.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/482b33a1-796e-4636-a427-3d3e2ff876d7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/482b33a1-796e-4636-a427-3d3e2ff876d7.lance deleted file mode 100644 index 5bf8f02a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/482b33a1-796e-4636-a427-3d3e2ff876d7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/482f4aa0-1fad-4a2a-90d9-567c493dc498.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/482f4aa0-1fad-4a2a-90d9-567c493dc498.lance deleted file mode 100644 index f7ef13301..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/482f4aa0-1fad-4a2a-90d9-567c493dc498.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4857b118-6283-48aa-8fee-71325cdcce47.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4857b118-6283-48aa-8fee-71325cdcce47.lance deleted file mode 100644 index d85cbdd7d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4857b118-6283-48aa-8fee-71325cdcce47.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4860092a-45cf-42e2-8606-cd81ac15803a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4860092a-45cf-42e2-8606-cd81ac15803a.lance deleted file mode 100644 index 73f0981ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4860092a-45cf-42e2-8606-cd81ac15803a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/48a9ace1-7b95-4b66-aec4-b426f525d27b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/48a9ace1-7b95-4b66-aec4-b426f525d27b.lance deleted file mode 100644 index d94683eb9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/48a9ace1-7b95-4b66-aec4-b426f525d27b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/490be35e-1f7f-4737-a8a4-34c3066bc137.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/490be35e-1f7f-4737-a8a4-34c3066bc137.lance deleted file mode 100644 index b7182990f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/490be35e-1f7f-4737-a8a4-34c3066bc137.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/491004ea-7cc3-4cbd-87b1-54375f161e26.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/491004ea-7cc3-4cbd-87b1-54375f161e26.lance deleted file mode 100644 index 012354b88..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/491004ea-7cc3-4cbd-87b1-54375f161e26.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4940c27c-d8b6-4324-8e81-e6ec67c325fc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4940c27c-d8b6-4324-8e81-e6ec67c325fc.lance deleted file mode 100644 index 864f05a09..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4940c27c-d8b6-4324-8e81-e6ec67c325fc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/494fe70b-3b4e-453f-a12b-7b42ab5a86a6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/494fe70b-3b4e-453f-a12b-7b42ab5a86a6.lance deleted file mode 100644 index 1894c8fa5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/494fe70b-3b4e-453f-a12b-7b42ab5a86a6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4957699b-7165-4e47-bc0b-cb295a5e8d12.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4957699b-7165-4e47-bc0b-cb295a5e8d12.lance deleted file mode 100644 index c8d636ca3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4957699b-7165-4e47-bc0b-cb295a5e8d12.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4963b924-3b33-41c1-ad7f-25646cd882a2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4963b924-3b33-41c1-ad7f-25646cd882a2.lance deleted file mode 100644 index 4a7321f57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4963b924-3b33-41c1-ad7f-25646cd882a2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/496ca812-1b0f-41fa-8b63-3656dcf2a308.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/496ca812-1b0f-41fa-8b63-3656dcf2a308.lance deleted file mode 100644 index 72b695cad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/496ca812-1b0f-41fa-8b63-3656dcf2a308.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/496e620b-21b1-4b82-9ead-ad2a9a2d52c5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/496e620b-21b1-4b82-9ead-ad2a9a2d52c5.lance deleted file mode 100644 index 501f539ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/496e620b-21b1-4b82-9ead-ad2a9a2d52c5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4988f586-273f-4e26-938c-e9da2558e4ee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4988f586-273f-4e26-938c-e9da2558e4ee.lance deleted file mode 100644 index 1af511a83..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4988f586-273f-4e26-938c-e9da2558e4ee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a2647aa-5609-486f-aabd-37c866d62fad.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a2647aa-5609-486f-aabd-37c866d62fad.lance deleted file mode 100644 index 45ff818b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a2647aa-5609-486f-aabd-37c866d62fad.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a35cad4-3bd5-4ef2-8f47-452c92baaa17.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a35cad4-3bd5-4ef2-8f47-452c92baaa17.lance deleted file mode 100644 index b78b7a9fe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a35cad4-3bd5-4ef2-8f47-452c92baaa17.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a384034-438c-4287-8d36-d6e88dff6c60.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a384034-438c-4287-8d36-d6e88dff6c60.lance deleted file mode 100644 index 677339b5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a384034-438c-4287-8d36-d6e88dff6c60.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a54f8bd-658e-4e65-b2a2-2a660541b9f5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a54f8bd-658e-4e65-b2a2-2a660541b9f5.lance deleted file mode 100644 index 02698c86b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a54f8bd-658e-4e65-b2a2-2a660541b9f5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a5d3007-bf89-4922-9577-28afba728728.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a5d3007-bf89-4922-9577-28afba728728.lance deleted file mode 100644 index a94514876..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a5d3007-bf89-4922-9577-28afba728728.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a7dc8a6-ad06-46fc-a8eb-d7eaf3d68eda.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a7dc8a6-ad06-46fc-a8eb-d7eaf3d68eda.lance deleted file mode 100644 index ab65a4516..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a7dc8a6-ad06-46fc-a8eb-d7eaf3d68eda.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a96a104-ab06-4621-98f4-7ff1cfd77926.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a96a104-ab06-4621-98f4-7ff1cfd77926.lance deleted file mode 100644 index bec7acc39..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4a96a104-ab06-4621-98f4-7ff1cfd77926.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4aa062c6-42ba-437b-9445-fe7584d5836d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4aa062c6-42ba-437b-9445-fe7584d5836d.lance deleted file mode 100644 index ae4382080..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4aa062c6-42ba-437b-9445-fe7584d5836d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4abc2c75-c3be-4be1-90e9-62074c394fec.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4abc2c75-c3be-4be1-90e9-62074c394fec.lance deleted file mode 100644 index 8db0bf7ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4abc2c75-c3be-4be1-90e9-62074c394fec.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ad4b8e1-465d-4c66-950b-46f8e6c92750.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ad4b8e1-465d-4c66-950b-46f8e6c92750.lance deleted file mode 100644 index 73dc39d68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ad4b8e1-465d-4c66-950b-46f8e6c92750.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ae2180b-a97b-4cdc-965e-7460b3bc12b0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ae2180b-a97b-4cdc-965e-7460b3bc12b0.lance deleted file mode 100644 index 2246e0782..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ae2180b-a97b-4cdc-965e-7460b3bc12b0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4aea2695-84c5-46b4-af17-1e1c1605be39.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4aea2695-84c5-46b4-af17-1e1c1605be39.lance deleted file mode 100644 index 82ef4b2bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4aea2695-84c5-46b4-af17-1e1c1605be39.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4af791a1-8737-46d0-97da-ec32abbb45f7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4af791a1-8737-46d0-97da-ec32abbb45f7.lance deleted file mode 100644 index abc0af34e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4af791a1-8737-46d0-97da-ec32abbb45f7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b03942b-edec-42cf-8b59-f4422d1a5fa7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b03942b-edec-42cf-8b59-f4422d1a5fa7.lance deleted file mode 100644 index eccac3560..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b03942b-edec-42cf-8b59-f4422d1a5fa7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b1ea76d-814e-4563-96a8-9348a064a845.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b1ea76d-814e-4563-96a8-9348a064a845.lance deleted file mode 100644 index f0bff9c80..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b1ea76d-814e-4563-96a8-9348a064a845.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b649dd8-71a5-4843-be63-6bbf04b313bd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b649dd8-71a5-4843-be63-6bbf04b313bd.lance deleted file mode 100644 index 682e3aef4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b649dd8-71a5-4843-be63-6bbf04b313bd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b72ae3a-b622-4df9-b90d-0a2a79eeabea.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b72ae3a-b622-4df9-b90d-0a2a79eeabea.lance deleted file mode 100644 index e552a4322..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b72ae3a-b622-4df9-b90d-0a2a79eeabea.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b98e236-02a5-437b-a6f1-b70a80881100.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b98e236-02a5-437b-a6f1-b70a80881100.lance deleted file mode 100644 index 0ad40deca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b98e236-02a5-437b-a6f1-b70a80881100.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b9f2070-a50c-4f19-a9e6-80c7c5281a9f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b9f2070-a50c-4f19-a9e6-80c7c5281a9f.lance deleted file mode 100644 index 5b012bf8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4b9f2070-a50c-4f19-a9e6-80c7c5281a9f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4bbbc448-b65f-48f5-9a6e-116f77ff09fb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4bbbc448-b65f-48f5-9a6e-116f77ff09fb.lance deleted file mode 100644 index 9e265581f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4bbbc448-b65f-48f5-9a6e-116f77ff09fb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4bcbe019-636a-4b84-a05c-bcf542d03342.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4bcbe019-636a-4b84-a05c-bcf542d03342.lance deleted file mode 100644 index bbfc0f440..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4bcbe019-636a-4b84-a05c-bcf542d03342.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4be69709-1ef1-44bc-966b-930adc317d88.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4be69709-1ef1-44bc-966b-930adc317d88.lance deleted file mode 100644 index 8b58a4b9a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4be69709-1ef1-44bc-966b-930adc317d88.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c0350ed-4249-4035-9de1-4b7f4f9b0a28.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c0350ed-4249-4035-9de1-4b7f4f9b0a28.lance deleted file mode 100644 index a3c7954de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c0350ed-4249-4035-9de1-4b7f4f9b0a28.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c08f4fb-ac6a-4bd6-91e1-65e8bbd70ac4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c08f4fb-ac6a-4bd6-91e1-65e8bbd70ac4.lance deleted file mode 100644 index 5ab97682c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c08f4fb-ac6a-4bd6-91e1-65e8bbd70ac4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c0a19c2-cf45-483e-8bca-3eb6377cb92f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c0a19c2-cf45-483e-8bca-3eb6377cb92f.lance deleted file mode 100644 index f89a83e20..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c0a19c2-cf45-483e-8bca-3eb6377cb92f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c226447-5fad-486a-b50e-b32ab1fae96b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c226447-5fad-486a-b50e-b32ab1fae96b.lance deleted file mode 100644 index 54bf9ecc9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c226447-5fad-486a-b50e-b32ab1fae96b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c5160da-b210-4d86-b44f-5bfef35231fc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c5160da-b210-4d86-b44f-5bfef35231fc.lance deleted file mode 100644 index 517f69331..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c5160da-b210-4d86-b44f-5bfef35231fc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c62120a-74b1-424a-a81b-04b0e8e5c267.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c62120a-74b1-424a-a81b-04b0e8e5c267.lance deleted file mode 100644 index d912e5f4d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c62120a-74b1-424a-a81b-04b0e8e5c267.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c69cd31-384f-40d8-8cd9-13945a31a0c8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c69cd31-384f-40d8-8cd9-13945a31a0c8.lance deleted file mode 100644 index b28103354..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c69cd31-384f-40d8-8cd9-13945a31a0c8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c718123-db20-4b50-a5be-cda4742bf461.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c718123-db20-4b50-a5be-cda4742bf461.lance deleted file mode 100644 index 066c1b486..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c718123-db20-4b50-a5be-cda4742bf461.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c83d96e-d94b-4ecc-a420-ae6cf2269cee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c83d96e-d94b-4ecc-a420-ae6cf2269cee.lance deleted file mode 100644 index 3c9492b7b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c83d96e-d94b-4ecc-a420-ae6cf2269cee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c8a8fc9-aa79-416e-98bb-a251fb723dfa.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c8a8fc9-aa79-416e-98bb-a251fb723dfa.lance deleted file mode 100644 index 284fd1f82..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4c8a8fc9-aa79-416e-98bb-a251fb723dfa.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ca84cd1-f649-4d66-b2b9-6d88fe297c15.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ca84cd1-f649-4d66-b2b9-6d88fe297c15.lance deleted file mode 100644 index 41963a1ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ca84cd1-f649-4d66-b2b9-6d88fe297c15.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4cbcbe2d-91b7-47bd-80ce-d042695243f9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4cbcbe2d-91b7-47bd-80ce-d042695243f9.lance deleted file mode 100644 index a37ca4ecf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4cbcbe2d-91b7-47bd-80ce-d042695243f9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4cec38c6-1c0a-4c1c-8518-75856feb08ab.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4cec38c6-1c0a-4c1c-8518-75856feb08ab.lance deleted file mode 100644 index 4edf482a0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4cec38c6-1c0a-4c1c-8518-75856feb08ab.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4d0c24c5-8df8-46a1-99b0-4d0d1c9b61fb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4d0c24c5-8df8-46a1-99b0-4d0d1c9b61fb.lance deleted file mode 100644 index 963974697..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4d0c24c5-8df8-46a1-99b0-4d0d1c9b61fb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4d274e81-2485-4483-9f8a-2e566c253967.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4d274e81-2485-4483-9f8a-2e566c253967.lance deleted file mode 100644 index b398a4780..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4d274e81-2485-4483-9f8a-2e566c253967.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4d6c9549-505a-4099-8b4e-3a2a6e8151fb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4d6c9549-505a-4099-8b4e-3a2a6e8151fb.lance deleted file mode 100644 index 970481129..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4d6c9549-505a-4099-8b4e-3a2a6e8151fb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4d85c2e3-a0e1-4027-90a7-4dff754f0123.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4d85c2e3-a0e1-4027-90a7-4dff754f0123.lance deleted file mode 100644 index 03daddb58..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4d85c2e3-a0e1-4027-90a7-4dff754f0123.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4dbb2e2c-7508-4244-a798-cdcebc5c9428.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4dbb2e2c-7508-4244-a798-cdcebc5c9428.lance deleted file mode 100644 index 2807d3a16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4dbb2e2c-7508-4244-a798-cdcebc5c9428.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e1f6d2f-3e58-48d4-b2e0-fae0dfb147a4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e1f6d2f-3e58-48d4-b2e0-fae0dfb147a4.lance deleted file mode 100644 index 6a767375e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e1f6d2f-3e58-48d4-b2e0-fae0dfb147a4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e34d8bd-914d-4cbe-8c29-e09821851530.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e34d8bd-914d-4cbe-8c29-e09821851530.lance deleted file mode 100644 index bb0f4b784..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e34d8bd-914d-4cbe-8c29-e09821851530.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e4e8ba4-a7f2-4ac8-8cb0-9f4bb6f10d3d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e4e8ba4-a7f2-4ac8-8cb0-9f4bb6f10d3d.lance deleted file mode 100644 index 52547b783..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e4e8ba4-a7f2-4ac8-8cb0-9f4bb6f10d3d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e556789-e23a-4fa1-a526-bfc8bb61f541.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e556789-e23a-4fa1-a526-bfc8bb61f541.lance deleted file mode 100644 index 96f437dee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e556789-e23a-4fa1-a526-bfc8bb61f541.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e66a6ba-b0ea-4315-a6fa-1511017fa79a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e66a6ba-b0ea-4315-a6fa-1511017fa79a.lance deleted file mode 100644 index 57d82f10c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e66a6ba-b0ea-4315-a6fa-1511017fa79a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e6c6d7b-d3a6-4ddc-8bd7-b03e4b7c9b55.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e6c6d7b-d3a6-4ddc-8bd7-b03e4b7c9b55.lance deleted file mode 100644 index e9d37ec7f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e6c6d7b-d3a6-4ddc-8bd7-b03e4b7c9b55.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e8a1945-3548-426d-a967-dfe739e5c6fa.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e8a1945-3548-426d-a967-dfe739e5c6fa.lance deleted file mode 100644 index e0ecf9d22..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e8a1945-3548-426d-a967-dfe739e5c6fa.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e8d9d63-527f-47f5-a9dd-df6506b21e63.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e8d9d63-527f-47f5-a9dd-df6506b21e63.lance deleted file mode 100644 index 543215d5d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e8d9d63-527f-47f5-a9dd-df6506b21e63.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e91914b-3a3b-44c3-aefe-0a2cea89a878.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e91914b-3a3b-44c3-aefe-0a2cea89a878.lance deleted file mode 100644 index 5551ff186..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4e91914b-3a3b-44c3-aefe-0a2cea89a878.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4eacb8fe-0e29-413f-bb0d-75c250f4a6eb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4eacb8fe-0e29-413f-bb0d-75c250f4a6eb.lance deleted file mode 100644 index b539fdae4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4eacb8fe-0e29-413f-bb0d-75c250f4a6eb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ecd05b1-c940-48c4-b253-f2a1b8d6c128.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ecd05b1-c940-48c4-b253-f2a1b8d6c128.lance deleted file mode 100644 index b7c2fab6a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ecd05b1-c940-48c4-b253-f2a1b8d6c128.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4eddf355-f1a3-4802-832a-3bc41276b288.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4eddf355-f1a3-4802-832a-3bc41276b288.lance deleted file mode 100644 index 4d61094e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4eddf355-f1a3-4802-832a-3bc41276b288.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ee2252b-014c-4eec-8d5a-f6c6cf5d517d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ee2252b-014c-4eec-8d5a-f6c6cf5d517d.lance deleted file mode 100644 index b35d9ca66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ee2252b-014c-4eec-8d5a-f6c6cf5d517d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ee23552-ca53-4692-8163-f71b38b30e34.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ee23552-ca53-4692-8163-f71b38b30e34.lance deleted file mode 100644 index ef0bb28aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ee23552-ca53-4692-8163-f71b38b30e34.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ef672f0-0d80-40cd-8b40-3b12454308ac.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ef672f0-0d80-40cd-8b40-3b12454308ac.lance deleted file mode 100644 index d96e7f731..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4ef672f0-0d80-40cd-8b40-3b12454308ac.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f07aa7d-3bec-4b7c-9e5f-a6a4342e05be.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f07aa7d-3bec-4b7c-9e5f-a6a4342e05be.lance deleted file mode 100644 index 463bc2f87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f07aa7d-3bec-4b7c-9e5f-a6a4342e05be.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f0b66d4-f33b-439e-bedf-8d1b38dd1c82.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f0b66d4-f33b-439e-bedf-8d1b38dd1c82.lance deleted file mode 100644 index 2ade71cdd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f0b66d4-f33b-439e-bedf-8d1b38dd1c82.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f1efe2e-5c6e-4a72-8218-d6403b362f4c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f1efe2e-5c6e-4a72-8218-d6403b362f4c.lance deleted file mode 100644 index 429df8e55..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f1efe2e-5c6e-4a72-8218-d6403b362f4c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f2d7933-901a-4ffa-92e0-ac96b6cc044a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f2d7933-901a-4ffa-92e0-ac96b6cc044a.lance deleted file mode 100644 index c07ebc316..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f2d7933-901a-4ffa-92e0-ac96b6cc044a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f54da82-9faf-4a0e-ada1-492d1ca7dc9d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f54da82-9faf-4a0e-ada1-492d1ca7dc9d.lance deleted file mode 100644 index 2937bab42..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f54da82-9faf-4a0e-ada1-492d1ca7dc9d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f633408-d1d8-447b-848f-45024a99b547.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f633408-d1d8-447b-848f-45024a99b547.lance deleted file mode 100644 index 70043226c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f633408-d1d8-447b-848f-45024a99b547.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f695bf3-f87b-4408-867f-57285dc54cd7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f695bf3-f87b-4408-867f-57285dc54cd7.lance deleted file mode 100644 index 16c4b72d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4f695bf3-f87b-4408-867f-57285dc54cd7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4fa6cc2b-90d3-4511-bd8b-a6f7763d2d0c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4fa6cc2b-90d3-4511-bd8b-a6f7763d2d0c.lance deleted file mode 100644 index 6e010c482..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4fa6cc2b-90d3-4511-bd8b-a6f7763d2d0c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4fa92877-b948-44c1-93c7-a9d5628a29b6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4fa92877-b948-44c1-93c7-a9d5628a29b6.lance deleted file mode 100644 index d70d4c9ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4fa92877-b948-44c1-93c7-a9d5628a29b6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4fc359da-890a-4c09-b370-54cef4f1c0d0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4fc359da-890a-4c09-b370-54cef4f1c0d0.lance deleted file mode 100644 index 2b3c0db1f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4fc359da-890a-4c09-b370-54cef4f1c0d0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4fd5881b-91b8-4adc-ac07-598b2b4710ec.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4fd5881b-91b8-4adc-ac07-598b2b4710ec.lance deleted file mode 100644 index b56603f13..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/4fd5881b-91b8-4adc-ac07-598b2b4710ec.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5001f023-1294-4f81-8093-0de00cd4cada.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5001f023-1294-4f81-8093-0de00cd4cada.lance deleted file mode 100644 index 05c46ded0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5001f023-1294-4f81-8093-0de00cd4cada.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5035d1d9-13b6-47e3-b01a-78cdedd47e34.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5035d1d9-13b6-47e3-b01a-78cdedd47e34.lance deleted file mode 100644 index a76b0dd65..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5035d1d9-13b6-47e3-b01a-78cdedd47e34.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/50385e12-6dd0-40fb-8e9b-61903b2d585d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/50385e12-6dd0-40fb-8e9b-61903b2d585d.lance deleted file mode 100644 index 8bc1990f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/50385e12-6dd0-40fb-8e9b-61903b2d585d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/506240bf-458c-4a46-bc95-077799b719f9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/506240bf-458c-4a46-bc95-077799b719f9.lance deleted file mode 100644 index a454692b7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/506240bf-458c-4a46-bc95-077799b719f9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/50b0e33b-4f38-4fbd-9223-4cb7cac0fc94.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/50b0e33b-4f38-4fbd-9223-4cb7cac0fc94.lance deleted file mode 100644 index 0696272f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/50b0e33b-4f38-4fbd-9223-4cb7cac0fc94.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/50bc58e8-d7c7-497c-b922-78326b03a71e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/50bc58e8-d7c7-497c-b922-78326b03a71e.lance deleted file mode 100644 index ee1531ca9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/50bc58e8-d7c7-497c-b922-78326b03a71e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/50e43fdf-55ca-4cca-b8d4-6d33725cd8cc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/50e43fdf-55ca-4cca-b8d4-6d33725cd8cc.lance deleted file mode 100644 index 309dbacb0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/50e43fdf-55ca-4cca-b8d4-6d33725cd8cc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/50f28a33-84df-4131-966e-e1749260ba68.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/50f28a33-84df-4131-966e-e1749260ba68.lance deleted file mode 100644 index 1fc0393c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/50f28a33-84df-4131-966e-e1749260ba68.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5152ca39-45a2-435e-a6c0-6feb73c885b7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5152ca39-45a2-435e-a6c0-6feb73c885b7.lance deleted file mode 100644 index 3f65472d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5152ca39-45a2-435e-a6c0-6feb73c885b7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/515ff4e8-4156-401d-b127-e9d9294ba5ef.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/515ff4e8-4156-401d-b127-e9d9294ba5ef.lance deleted file mode 100644 index 45e453e04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/515ff4e8-4156-401d-b127-e9d9294ba5ef.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5164bb16-f79c-45b0-84a5-3c5ba6e94f27.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5164bb16-f79c-45b0-84a5-3c5ba6e94f27.lance deleted file mode 100644 index 378f54fa5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5164bb16-f79c-45b0-84a5-3c5ba6e94f27.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/516ecbb0-49fc-4750-a815-371d8d54cf5f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/516ecbb0-49fc-4750-a815-371d8d54cf5f.lance deleted file mode 100644 index c65521637..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/516ecbb0-49fc-4750-a815-371d8d54cf5f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/51d00a4d-ccf7-4521-b50c-55aa1999ff4d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/51d00a4d-ccf7-4521-b50c-55aa1999ff4d.lance deleted file mode 100644 index 3d0e5aff6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/51d00a4d-ccf7-4521-b50c-55aa1999ff4d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/51d2ab81-a72c-4f68-8ffd-2e0a03c99fee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/51d2ab81-a72c-4f68-8ffd-2e0a03c99fee.lance deleted file mode 100644 index 09d343ef2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/51d2ab81-a72c-4f68-8ffd-2e0a03c99fee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/51ec6b3e-36a1-4df8-a0fc-4c6714e3c634.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/51ec6b3e-36a1-4df8-a0fc-4c6714e3c634.lance deleted file mode 100644 index e4a7bc072..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/51ec6b3e-36a1-4df8-a0fc-4c6714e3c634.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/51fd883c-39e3-47b9-81f5-170813ff7b17.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/51fd883c-39e3-47b9-81f5-170813ff7b17.lance deleted file mode 100644 index f18f94f14..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/51fd883c-39e3-47b9-81f5-170813ff7b17.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/525eb52f-5dc5-452b-93b0-0e92e149daf9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/525eb52f-5dc5-452b-93b0-0e92e149daf9.lance deleted file mode 100644 index 024b0224b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/525eb52f-5dc5-452b-93b0-0e92e149daf9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/526b7ae4-044b-40ec-b3c6-2d6c0618e578.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/526b7ae4-044b-40ec-b3c6-2d6c0618e578.lance deleted file mode 100644 index 0946e3e74..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/526b7ae4-044b-40ec-b3c6-2d6c0618e578.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/528dd9a1-c145-4405-bb7d-a38391880821.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/528dd9a1-c145-4405-bb7d-a38391880821.lance deleted file mode 100644 index e8e76f051..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/528dd9a1-c145-4405-bb7d-a38391880821.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/528f78bf-2224-4978-a0ca-db7dee91a553.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/528f78bf-2224-4978-a0ca-db7dee91a553.lance deleted file mode 100644 index e37276c2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/528f78bf-2224-4978-a0ca-db7dee91a553.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/52cc42e1-0eec-4134-8a11-9964d6a9369d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/52cc42e1-0eec-4134-8a11-9964d6a9369d.lance deleted file mode 100644 index f715db325..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/52cc42e1-0eec-4134-8a11-9964d6a9369d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/52fef53b-06e6-4538-b970-e2e75bd9f910.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/52fef53b-06e6-4538-b970-e2e75bd9f910.lance deleted file mode 100644 index 1ff3b10da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/52fef53b-06e6-4538-b970-e2e75bd9f910.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/52ff7894-5493-4773-b1a9-79e5c94c126b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/52ff7894-5493-4773-b1a9-79e5c94c126b.lance deleted file mode 100644 index 3940c5579..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/52ff7894-5493-4773-b1a9-79e5c94c126b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5307557e-67de-4557-bfe6-370952a5a659.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5307557e-67de-4557-bfe6-370952a5a659.lance deleted file mode 100644 index a250fbb43..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5307557e-67de-4557-bfe6-370952a5a659.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/53354cea-6cb7-4a56-badc-6109637a3e9f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/53354cea-6cb7-4a56-badc-6109637a3e9f.lance deleted file mode 100644 index 0ba88061b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/53354cea-6cb7-4a56-badc-6109637a3e9f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5370e695-cd02-4b37-af42-f5515f571391.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5370e695-cd02-4b37-af42-f5515f571391.lance deleted file mode 100644 index c7e3666fe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5370e695-cd02-4b37-af42-f5515f571391.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/537742ee-11ba-4015-b36c-83458460c85c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/537742ee-11ba-4015-b36c-83458460c85c.lance deleted file mode 100644 index c4f2f15c1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/537742ee-11ba-4015-b36c-83458460c85c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5385c7b7-074f-4c09-8a8b-c07a26723fda.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5385c7b7-074f-4c09-8a8b-c07a26723fda.lance deleted file mode 100644 index ad356bd1c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5385c7b7-074f-4c09-8a8b-c07a26723fda.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/538e51ba-3ec8-40a6-b7c6-892ed39091ac.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/538e51ba-3ec8-40a6-b7c6-892ed39091ac.lance deleted file mode 100644 index 7b1de38d6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/538e51ba-3ec8-40a6-b7c6-892ed39091ac.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/539a3cf4-cde0-4c5c-8b0d-3dd56c2297d1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/539a3cf4-cde0-4c5c-8b0d-3dd56c2297d1.lance deleted file mode 100644 index 5a99b7b24..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/539a3cf4-cde0-4c5c-8b0d-3dd56c2297d1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/53b675c1-caa9-4cf5-beb1-bbb14730362e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/53b675c1-caa9-4cf5-beb1-bbb14730362e.lance deleted file mode 100644 index 74c394282..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/53b675c1-caa9-4cf5-beb1-bbb14730362e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/53e996cb-6d24-47e5-abde-9330c4c5c6a1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/53e996cb-6d24-47e5-abde-9330c4c5c6a1.lance deleted file mode 100644 index 074a3f622..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/53e996cb-6d24-47e5-abde-9330c4c5c6a1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/53f5fde0-5546-4dca-90b1-e1f81569478f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/53f5fde0-5546-4dca-90b1-e1f81569478f.lance deleted file mode 100644 index b18c0d4d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/53f5fde0-5546-4dca-90b1-e1f81569478f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/53fefe61-6f3c-442e-b75e-8663d7544fd9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/53fefe61-6f3c-442e-b75e-8663d7544fd9.lance deleted file mode 100644 index 1f24f5a9c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/53fefe61-6f3c-442e-b75e-8663d7544fd9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5429b7f8-a89e-49e6-85b0-fc123c75000c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5429b7f8-a89e-49e6-85b0-fc123c75000c.lance deleted file mode 100644 index 403e20451..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5429b7f8-a89e-49e6-85b0-fc123c75000c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/54533ab5-1395-4ecc-a361-d71d2cfcab34.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/54533ab5-1395-4ecc-a361-d71d2cfcab34.lance deleted file mode 100644 index 53887da43..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/54533ab5-1395-4ecc-a361-d71d2cfcab34.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/546a2dd3-fc49-4564-a00f-4485b362f81b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/546a2dd3-fc49-4564-a00f-4485b362f81b.lance deleted file mode 100644 index 482e25f2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/546a2dd3-fc49-4564-a00f-4485b362f81b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/548a0b46-a88c-4d23-931e-c8b6601948de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/548a0b46-a88c-4d23-931e-c8b6601948de.lance deleted file mode 100644 index 60aa75c63..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/548a0b46-a88c-4d23-931e-c8b6601948de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5492a85b-479f-43c9-96d7-30e054c0f23e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5492a85b-479f-43c9-96d7-30e054c0f23e.lance deleted file mode 100644 index d6b325689..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5492a85b-479f-43c9-96d7-30e054c0f23e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/54c6dfdd-f903-4658-b104-871177f9d3e1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/54c6dfdd-f903-4658-b104-871177f9d3e1.lance deleted file mode 100644 index 27e980512..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/54c6dfdd-f903-4658-b104-871177f9d3e1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/54c79eb7-5274-4996-9bed-26757b4fa814.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/54c79eb7-5274-4996-9bed-26757b4fa814.lance deleted file mode 100644 index 215fc41f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/54c79eb7-5274-4996-9bed-26757b4fa814.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/54e3d0f9-feea-448f-8129-825715cc41db.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/54e3d0f9-feea-448f-8129-825715cc41db.lance deleted file mode 100644 index 907564a7c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/54e3d0f9-feea-448f-8129-825715cc41db.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/550f39a4-1237-4ff5-bd09-ca010b6b6f77.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/550f39a4-1237-4ff5-bd09-ca010b6b6f77.lance deleted file mode 100644 index db17073cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/550f39a4-1237-4ff5-bd09-ca010b6b6f77.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5543cbee-e812-4119-89ed-7de283918c64.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5543cbee-e812-4119-89ed-7de283918c64.lance deleted file mode 100644 index 848322bea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5543cbee-e812-4119-89ed-7de283918c64.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/554eb9fc-535d-44e5-8e7d-910c76e172cb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/554eb9fc-535d-44e5-8e7d-910c76e172cb.lance deleted file mode 100644 index 83560d499..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/554eb9fc-535d-44e5-8e7d-910c76e172cb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/555f2270-f96d-4592-a906-a0e251cce424.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/555f2270-f96d-4592-a906-a0e251cce424.lance deleted file mode 100644 index 24ee2f977..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/555f2270-f96d-4592-a906-a0e251cce424.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/557e0887-70b6-4999-b48c-f8a8b38a8056.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/557e0887-70b6-4999-b48c-f8a8b38a8056.lance deleted file mode 100644 index da6db2144..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/557e0887-70b6-4999-b48c-f8a8b38a8056.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5586d2a0-809b-4531-843d-94a0d3c3e036.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5586d2a0-809b-4531-843d-94a0d3c3e036.lance deleted file mode 100644 index 848225272..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5586d2a0-809b-4531-843d-94a0d3c3e036.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55a8a520-1088-4591-80e3-4958f99d60af.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55a8a520-1088-4591-80e3-4958f99d60af.lance deleted file mode 100644 index bc75e16d6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55a8a520-1088-4591-80e3-4958f99d60af.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55acadaf-3c86-4a44-bdc8-2615877ee2ae.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55acadaf-3c86-4a44-bdc8-2615877ee2ae.lance deleted file mode 100644 index ee19600f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55acadaf-3c86-4a44-bdc8-2615877ee2ae.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55d1cdb3-1224-4ba5-8d43-3037300343d4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55d1cdb3-1224-4ba5-8d43-3037300343d4.lance deleted file mode 100644 index 737e67814..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55d1cdb3-1224-4ba5-8d43-3037300343d4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55e690c7-a6aa-4f9f-8534-9706aa251d9d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55e690c7-a6aa-4f9f-8534-9706aa251d9d.lance deleted file mode 100644 index 72f018b0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55e690c7-a6aa-4f9f-8534-9706aa251d9d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55eb2be2-e3c8-4964-a55d-8b25dca160d1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55eb2be2-e3c8-4964-a55d-8b25dca160d1.lance deleted file mode 100644 index 3b748907a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55eb2be2-e3c8-4964-a55d-8b25dca160d1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55ef4a38-1c4f-4a4d-9efa-f76074e7c2b1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55ef4a38-1c4f-4a4d-9efa-f76074e7c2b1.lance deleted file mode 100644 index ebd86632e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/55ef4a38-1c4f-4a4d-9efa-f76074e7c2b1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56053bf6-b64b-4902-aa35-c686d3c026f1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56053bf6-b64b-4902-aa35-c686d3c026f1.lance deleted file mode 100644 index e818816b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56053bf6-b64b-4902-aa35-c686d3c026f1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5636fe6e-b547-4aa8-a25c-715d8bc29226.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5636fe6e-b547-4aa8-a25c-715d8bc29226.lance deleted file mode 100644 index 0be6f5d30..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5636fe6e-b547-4aa8-a25c-715d8bc29226.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/564a9fed-9018-4f15-98cc-8c61e75636c8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/564a9fed-9018-4f15-98cc-8c61e75636c8.lance deleted file mode 100644 index d10c244d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/564a9fed-9018-4f15-98cc-8c61e75636c8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56553aa6-7fd6-427c-a4b8-170deb86d68d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56553aa6-7fd6-427c-a4b8-170deb86d68d.lance deleted file mode 100644 index 446445e4e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56553aa6-7fd6-427c-a4b8-170deb86d68d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/567bfe49-607c-4143-9b5b-c6f709f19f1d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/567bfe49-607c-4143-9b5b-c6f709f19f1d.lance deleted file mode 100644 index dce196225..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/567bfe49-607c-4143-9b5b-c6f709f19f1d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/567db7ea-b3b9-4ac9-a551-d38875168f55.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/567db7ea-b3b9-4ac9-a551-d38875168f55.lance deleted file mode 100644 index 21c8cdb20..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/567db7ea-b3b9-4ac9-a551-d38875168f55.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/568d7aee-18ec-4815-933a-ce62b4098047.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/568d7aee-18ec-4815-933a-ce62b4098047.lance deleted file mode 100644 index 9dfe1e7d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/568d7aee-18ec-4815-933a-ce62b4098047.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/568e57ac-3bd6-4e99-a716-b65caccd8064.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/568e57ac-3bd6-4e99-a716-b65caccd8064.lance deleted file mode 100644 index 23fbca4a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/568e57ac-3bd6-4e99-a716-b65caccd8064.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56931ddf-98d1-46a6-b240-607979c4bcef.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56931ddf-98d1-46a6-b240-607979c4bcef.lance deleted file mode 100644 index 37cb0ed65..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56931ddf-98d1-46a6-b240-607979c4bcef.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/569a6171-63dd-40db-96a8-659d4e91c8b5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/569a6171-63dd-40db-96a8-659d4e91c8b5.lance deleted file mode 100644 index d2020599f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/569a6171-63dd-40db-96a8-659d4e91c8b5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/569e48bb-19b1-43d3-981d-e29319576d2c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/569e48bb-19b1-43d3-981d-e29319576d2c.lance deleted file mode 100644 index 123466c71..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/569e48bb-19b1-43d3-981d-e29319576d2c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56ad83a7-08a5-40c3-ae4f-e9c17b5dd57f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56ad83a7-08a5-40c3-ae4f-e9c17b5dd57f.lance deleted file mode 100644 index a6545a36a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56ad83a7-08a5-40c3-ae4f-e9c17b5dd57f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56b50c42-1ce5-4ff8-be00-939f681e4f97.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56b50c42-1ce5-4ff8-be00-939f681e4f97.lance deleted file mode 100644 index 03d0c2304..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56b50c42-1ce5-4ff8-be00-939f681e4f97.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56bda187-996e-4c18-9a09-67f94d28b1f7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56bda187-996e-4c18-9a09-67f94d28b1f7.lance deleted file mode 100644 index 918eb2ed4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56bda187-996e-4c18-9a09-67f94d28b1f7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56c578fc-9c29-48d7-886c-3daad3f1c380.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56c578fc-9c29-48d7-886c-3daad3f1c380.lance deleted file mode 100644 index 0c8a284ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56c578fc-9c29-48d7-886c-3daad3f1c380.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56d06a8a-d4e7-4114-9bba-4bb9aa5fec2b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56d06a8a-d4e7-4114-9bba-4bb9aa5fec2b.lance deleted file mode 100644 index 2b4c510fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56d06a8a-d4e7-4114-9bba-4bb9aa5fec2b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56e6fcc1-99d8-45e2-8ff2-4917cd3ecf10.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56e6fcc1-99d8-45e2-8ff2-4917cd3ecf10.lance deleted file mode 100644 index 8ca23dfb3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/56e6fcc1-99d8-45e2-8ff2-4917cd3ecf10.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/570daf0f-d2c3-4046-b447-58789684ddcc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/570daf0f-d2c3-4046-b447-58789684ddcc.lance deleted file mode 100644 index e65dc1234..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/570daf0f-d2c3-4046-b447-58789684ddcc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/573b9993-81fd-49ec-91f1-136daa3db820.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/573b9993-81fd-49ec-91f1-136daa3db820.lance deleted file mode 100644 index 6c96a60ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/573b9993-81fd-49ec-91f1-136daa3db820.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57466a41-4433-41f5-aad9-4399515597fd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57466a41-4433-41f5-aad9-4399515597fd.lance deleted file mode 100644 index 699cda61a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57466a41-4433-41f5-aad9-4399515597fd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/574953c5-ba83-4d10-afdc-e7561fad3ca2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/574953c5-ba83-4d10-afdc-e7561fad3ca2.lance deleted file mode 100644 index 970e0820e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/574953c5-ba83-4d10-afdc-e7561fad3ca2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/575858c2-8d5c-475b-8915-f89d43f20df3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/575858c2-8d5c-475b-8915-f89d43f20df3.lance deleted file mode 100644 index 884666646..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/575858c2-8d5c-475b-8915-f89d43f20df3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5763b677-c753-4013-9b92-ea11f251a7d0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5763b677-c753-4013-9b92-ea11f251a7d0.lance deleted file mode 100644 index aed020919..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5763b677-c753-4013-9b92-ea11f251a7d0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57b9d9ed-d0f4-4b96-857f-1699e5475dc5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57b9d9ed-d0f4-4b96-857f-1699e5475dc5.lance deleted file mode 100644 index c9c90da6f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57b9d9ed-d0f4-4b96-857f-1699e5475dc5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57d0625e-e3ff-4a8d-a80d-5b33a971d6af.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57d0625e-e3ff-4a8d-a80d-5b33a971d6af.lance deleted file mode 100644 index 48bd41b6a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57d0625e-e3ff-4a8d-a80d-5b33a971d6af.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57d8054b-5f1d-4804-823e-49903af9c9f0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57d8054b-5f1d-4804-823e-49903af9c9f0.lance deleted file mode 100644 index 16a33b36d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57d8054b-5f1d-4804-823e-49903af9c9f0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57f26844-3d4e-4754-a277-da6555993a52.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57f26844-3d4e-4754-a277-da6555993a52.lance deleted file mode 100644 index b09c9ed8b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57f26844-3d4e-4754-a277-da6555993a52.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57fc2e46-03fa-4f04-a8cc-78a1309055e6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57fc2e46-03fa-4f04-a8cc-78a1309055e6.lance deleted file mode 100644 index 8d171e154..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/57fc2e46-03fa-4f04-a8cc-78a1309055e6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5815e8cb-6f25-436c-be25-67e8c8be1c1f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5815e8cb-6f25-436c-be25-67e8c8be1c1f.lance deleted file mode 100644 index 6fd22899a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5815e8cb-6f25-436c-be25-67e8c8be1c1f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5817b878-27dc-43c7-b524-bf0bfd74203e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5817b878-27dc-43c7-b524-bf0bfd74203e.lance deleted file mode 100644 index 22000610c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5817b878-27dc-43c7-b524-bf0bfd74203e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58396481-8f9a-4eda-b457-c11bd44e3a88.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58396481-8f9a-4eda-b457-c11bd44e3a88.lance deleted file mode 100644 index d7b220512..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58396481-8f9a-4eda-b457-c11bd44e3a88.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/584054ed-fa76-4294-a731-65d4f4cb917c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/584054ed-fa76-4294-a731-65d4f4cb917c.lance deleted file mode 100644 index ad8274332..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/584054ed-fa76-4294-a731-65d4f4cb917c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5849e0a0-3817-4e87-89ae-2440c1b2f3dc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5849e0a0-3817-4e87-89ae-2440c1b2f3dc.lance deleted file mode 100644 index cd22e8aaf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5849e0a0-3817-4e87-89ae-2440c1b2f3dc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58671955-6248-4fe1-abb6-691a1d12647a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58671955-6248-4fe1-abb6-691a1d12647a.lance deleted file mode 100644 index 650c94196..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58671955-6248-4fe1-abb6-691a1d12647a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58754dae-eda2-4d35-a26b-0277c198652b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58754dae-eda2-4d35-a26b-0277c198652b.lance deleted file mode 100644 index 25bd4317e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58754dae-eda2-4d35-a26b-0277c198652b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/588e89fa-a9a1-42f3-a311-3a86ac53ca10.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/588e89fa-a9a1-42f3-a311-3a86ac53ca10.lance deleted file mode 100644 index 2ee140661..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/588e89fa-a9a1-42f3-a311-3a86ac53ca10.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5893e2c5-361d-4c63-bbb2-2127e6d06b91.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5893e2c5-361d-4c63-bbb2-2127e6d06b91.lance deleted file mode 100644 index 61100f974..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5893e2c5-361d-4c63-bbb2-2127e6d06b91.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58b6f3db-3d62-47a4-8937-365551b0de9f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58b6f3db-3d62-47a4-8937-365551b0de9f.lance deleted file mode 100644 index fd8f445ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58b6f3db-3d62-47a4-8937-365551b0de9f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58ca0bc7-a3c5-4e98-b44d-3bd759570a66.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58ca0bc7-a3c5-4e98-b44d-3bd759570a66.lance deleted file mode 100644 index ee6225636..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58ca0bc7-a3c5-4e98-b44d-3bd759570a66.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58d9acee-9385-4558-a228-0529260d630c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58d9acee-9385-4558-a228-0529260d630c.lance deleted file mode 100644 index ca54c6180..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58d9acee-9385-4558-a228-0529260d630c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58e2aee8-46ad-427c-95cc-dfc2edab1d1d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58e2aee8-46ad-427c-95cc-dfc2edab1d1d.lance deleted file mode 100644 index 6041c5ce5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/58e2aee8-46ad-427c-95cc-dfc2edab1d1d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5900732c-1f38-4405-a46d-62bd31f411dd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5900732c-1f38-4405-a46d-62bd31f411dd.lance deleted file mode 100644 index ed0893fac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5900732c-1f38-4405-a46d-62bd31f411dd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5910ee82-b05e-41b5-98f4-c64e1d4415c8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5910ee82-b05e-41b5-98f4-c64e1d4415c8.lance deleted file mode 100644 index 94c45c1f0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5910ee82-b05e-41b5-98f4-c64e1d4415c8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/594249ae-ba08-424e-bd88-a2f0bf02801a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/594249ae-ba08-424e-bd88-a2f0bf02801a.lance deleted file mode 100644 index 475eba673..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/594249ae-ba08-424e-bd88-a2f0bf02801a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5945827d-e090-496a-a7e6-471e90cb3925.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5945827d-e090-496a-a7e6-471e90cb3925.lance deleted file mode 100644 index d8a2845c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5945827d-e090-496a-a7e6-471e90cb3925.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5956cf84-281a-4824-811c-3d6674c5c821.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5956cf84-281a-4824-811c-3d6674c5c821.lance deleted file mode 100644 index 176550704..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5956cf84-281a-4824-811c-3d6674c5c821.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/595fc471-f2ca-4b4e-86f5-ff98650b435b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/595fc471-f2ca-4b4e-86f5-ff98650b435b.lance deleted file mode 100644 index 1fde8b9fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/595fc471-f2ca-4b4e-86f5-ff98650b435b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/596bb445-5553-4b31-b59b-ed6cb877a65a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/596bb445-5553-4b31-b59b-ed6cb877a65a.lance deleted file mode 100644 index 4fc50214b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/596bb445-5553-4b31-b59b-ed6cb877a65a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/598600bf-44dd-42cb-95be-0404231dfc56.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/598600bf-44dd-42cb-95be-0404231dfc56.lance deleted file mode 100644 index b07743bc1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/598600bf-44dd-42cb-95be-0404231dfc56.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/59a127ba-4174-4f6d-9b75-9a67778e6545.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/59a127ba-4174-4f6d-9b75-9a67778e6545.lance deleted file mode 100644 index 1bff5d5e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/59a127ba-4174-4f6d-9b75-9a67778e6545.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/59a5ba21-bb3d-414c-b684-6a7b59c2dacb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/59a5ba21-bb3d-414c-b684-6a7b59c2dacb.lance deleted file mode 100644 index 9c4d4ff69..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/59a5ba21-bb3d-414c-b684-6a7b59c2dacb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/59bcbd18-3eb6-49bf-9c52-c6d0b696573f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/59bcbd18-3eb6-49bf-9c52-c6d0b696573f.lance deleted file mode 100644 index 736ddec11..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/59bcbd18-3eb6-49bf-9c52-c6d0b696573f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/59d7eac9-fb67-40ca-8a4a-b8bce3844832.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/59d7eac9-fb67-40ca-8a4a-b8bce3844832.lance deleted file mode 100644 index 040a71306..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/59d7eac9-fb67-40ca-8a4a-b8bce3844832.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/59e5428a-73b3-423c-8346-910ef7f32c88.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/59e5428a-73b3-423c-8346-910ef7f32c88.lance deleted file mode 100644 index 81d9854f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/59e5428a-73b3-423c-8346-910ef7f32c88.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a234d3c-d23f-401a-8839-317b7ef0d9ee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a234d3c-d23f-401a-8839-317b7ef0d9ee.lance deleted file mode 100644 index e70acc5ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a234d3c-d23f-401a-8839-317b7ef0d9ee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a28c5b5-a4db-4450-b007-825a17f6abae.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a28c5b5-a4db-4450-b007-825a17f6abae.lance deleted file mode 100644 index 1f7f71a3c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a28c5b5-a4db-4450-b007-825a17f6abae.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a4c8f6c-fe5b-40c6-9b79-6e6b502b22f6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a4c8f6c-fe5b-40c6-9b79-6e6b502b22f6.lance deleted file mode 100644 index fcb2b38e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a4c8f6c-fe5b-40c6-9b79-6e6b502b22f6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a4e777e-fc0e-4876-ad0c-925df89c6746.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a4e777e-fc0e-4876-ad0c-925df89c6746.lance deleted file mode 100644 index c72abdb3a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a4e777e-fc0e-4876-ad0c-925df89c6746.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a51feca-aaa3-4bba-b108-d62f63b821b7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a51feca-aaa3-4bba-b108-d62f63b821b7.lance deleted file mode 100644 index 256222849..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a51feca-aaa3-4bba-b108-d62f63b821b7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a5dac46-722b-4809-9d81-9ad6007c51b2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a5dac46-722b-4809-9d81-9ad6007c51b2.lance deleted file mode 100644 index 2a8a160aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a5dac46-722b-4809-9d81-9ad6007c51b2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a84f027-dc93-4a56-9f46-2eb7e927c975.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a84f027-dc93-4a56-9f46-2eb7e927c975.lance deleted file mode 100644 index 279c55eed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a84f027-dc93-4a56-9f46-2eb7e927c975.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a8b3bb5-8e4e-40fa-85d2-709862374bb4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a8b3bb5-8e4e-40fa-85d2-709862374bb4.lance deleted file mode 100644 index 8ed916f35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5a8b3bb5-8e4e-40fa-85d2-709862374bb4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5aa744fd-8012-4f82-8b42-87f629da9a39.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5aa744fd-8012-4f82-8b42-87f629da9a39.lance deleted file mode 100644 index 4d1dca86f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5aa744fd-8012-4f82-8b42-87f629da9a39.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ab87226-52a1-47c6-bcea-098bc8c856a9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ab87226-52a1-47c6-bcea-098bc8c856a9.lance deleted file mode 100644 index 711f95191..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ab87226-52a1-47c6-bcea-098bc8c856a9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ad93152-cae8-4c69-84b8-bb88bfaab66d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ad93152-cae8-4c69-84b8-bb88bfaab66d.lance deleted file mode 100644 index aac84cb3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ad93152-cae8-4c69-84b8-bb88bfaab66d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ae9d2db-ff67-4095-8cda-5f726a215c2f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ae9d2db-ff67-4095-8cda-5f726a215c2f.lance deleted file mode 100644 index 32cf6d9c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ae9d2db-ff67-4095-8cda-5f726a215c2f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5b12fb4d-a925-4a7c-a186-d82b15c04b7d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5b12fb4d-a925-4a7c-a186-d82b15c04b7d.lance deleted file mode 100644 index 332d64ebb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5b12fb4d-a925-4a7c-a186-d82b15c04b7d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5b29a7f0-ff0f-48af-9682-03b93f0b8546.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5b29a7f0-ff0f-48af-9682-03b93f0b8546.lance deleted file mode 100644 index b1c87d8eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5b29a7f0-ff0f-48af-9682-03b93f0b8546.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5b3ad7af-fa16-466f-abc2-00f8851d6019.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5b3ad7af-fa16-466f-abc2-00f8851d6019.lance deleted file mode 100644 index 77491307c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5b3ad7af-fa16-466f-abc2-00f8851d6019.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5b64405a-1dbd-419b-b3a3-1a7de8dcc99e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5b64405a-1dbd-419b-b3a3-1a7de8dcc99e.lance deleted file mode 100644 index 2c252a7f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5b64405a-1dbd-419b-b3a3-1a7de8dcc99e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5b8bb0d6-fa38-4138-afc8-df4cfef78f97.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5b8bb0d6-fa38-4138-afc8-df4cfef78f97.lance deleted file mode 100644 index 7c5809185..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5b8bb0d6-fa38-4138-afc8-df4cfef78f97.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bbb862e-4121-4879-9f3e-a2740224d39c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bbb862e-4121-4879-9f3e-a2740224d39c.lance deleted file mode 100644 index f4a03d7c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bbb862e-4121-4879-9f3e-a2740224d39c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bd6e1cc-f042-4291-a4c6-d463bcb755c2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bd6e1cc-f042-4291-a4c6-d463bcb755c2.lance deleted file mode 100644 index 11e62572b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bd6e1cc-f042-4291-a4c6-d463bcb755c2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bdba9b6-53d8-4f7b-b83e-76ddd6179d48.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bdba9b6-53d8-4f7b-b83e-76ddd6179d48.lance deleted file mode 100644 index a3cd9875d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bdba9b6-53d8-4f7b-b83e-76ddd6179d48.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bde802c-f848-49d2-a12d-0ad33cc1f6c7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bde802c-f848-49d2-a12d-0ad33cc1f6c7.lance deleted file mode 100644 index b68942032..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bde802c-f848-49d2-a12d-0ad33cc1f6c7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bf2f65a-637a-4380-bc0c-90acb1ccf07a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bf2f65a-637a-4380-bc0c-90acb1ccf07a.lance deleted file mode 100644 index db9c6e950..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bf2f65a-637a-4380-bc0c-90acb1ccf07a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bf56767-dbca-4e3c-af26-494dd9d7abba.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bf56767-dbca-4e3c-af26-494dd9d7abba.lance deleted file mode 100644 index c9cc2be34..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5bf56767-dbca-4e3c-af26-494dd9d7abba.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5c570bf2-7427-4199-94cf-c236a41a2b58.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5c570bf2-7427-4199-94cf-c236a41a2b58.lance deleted file mode 100644 index 45cd2a93c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5c570bf2-7427-4199-94cf-c236a41a2b58.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5cb241a7-e236-4c8c-b799-c2ed2481b9fb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5cb241a7-e236-4c8c-b799-c2ed2481b9fb.lance deleted file mode 100644 index 2d3faa3db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5cb241a7-e236-4c8c-b799-c2ed2481b9fb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ccd1ab1-6d5b-4652-992b-de99de8b8b5d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ccd1ab1-6d5b-4652-992b-de99de8b8b5d.lance deleted file mode 100644 index 12df9bfa3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ccd1ab1-6d5b-4652-992b-de99de8b8b5d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5cd9c630-0648-4969-b8ac-2d609a3c9c14.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5cd9c630-0648-4969-b8ac-2d609a3c9c14.lance deleted file mode 100644 index dd7146602..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5cd9c630-0648-4969-b8ac-2d609a3c9c14.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ce1fea6-cee9-4215-a0ae-4c013ded58d2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ce1fea6-cee9-4215-a0ae-4c013ded58d2.lance deleted file mode 100644 index b19776bd5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ce1fea6-cee9-4215-a0ae-4c013ded58d2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ce5cc6d-2a8d-4d90-8bb4-f0a6f093824c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ce5cc6d-2a8d-4d90-8bb4-f0a6f093824c.lance deleted file mode 100644 index ca024081b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ce5cc6d-2a8d-4d90-8bb4-f0a6f093824c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5d12ad6d-1803-43a4-81dc-6c9b41fcb734.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5d12ad6d-1803-43a4-81dc-6c9b41fcb734.lance deleted file mode 100644 index 5065a2d8a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5d12ad6d-1803-43a4-81dc-6c9b41fcb734.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5d63caa9-f7c4-4a5b-87b7-6b00eed197eb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5d63caa9-f7c4-4a5b-87b7-6b00eed197eb.lance deleted file mode 100644 index 0f6d9af80..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5d63caa9-f7c4-4a5b-87b7-6b00eed197eb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5d9133ae-7c3d-4128-8fdd-ec496ba5ee4f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5d9133ae-7c3d-4128-8fdd-ec496ba5ee4f.lance deleted file mode 100644 index f8cac999a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5d9133ae-7c3d-4128-8fdd-ec496ba5ee4f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5da6f46d-1ac9-4ba2-a65e-4c32d6869532.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5da6f46d-1ac9-4ba2-a65e-4c32d6869532.lance deleted file mode 100644 index 67a7e9cde..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5da6f46d-1ac9-4ba2-a65e-4c32d6869532.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5dc751ea-d69d-4657-baa2-28ef685e5590.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5dc751ea-d69d-4657-baa2-28ef685e5590.lance deleted file mode 100644 index 6e993ab17..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5dc751ea-d69d-4657-baa2-28ef685e5590.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5dc85940-9de8-4181-b1ea-46253a4d17ee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5dc85940-9de8-4181-b1ea-46253a4d17ee.lance deleted file mode 100644 index 2e44e9b51..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5dc85940-9de8-4181-b1ea-46253a4d17ee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5dce3133-60d7-4236-86d5-6ad0fd25a9a7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5dce3133-60d7-4236-86d5-6ad0fd25a9a7.lance deleted file mode 100644 index 2ec226ea9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5dce3133-60d7-4236-86d5-6ad0fd25a9a7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5e0ee812-2d20-4d1f-adcb-f332a290db2e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5e0ee812-2d20-4d1f-adcb-f332a290db2e.lance deleted file mode 100644 index fdd178421..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5e0ee812-2d20-4d1f-adcb-f332a290db2e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5e4e363c-2671-4838-80eb-f81aaacbef55.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5e4e363c-2671-4838-80eb-f81aaacbef55.lance deleted file mode 100644 index 7d5ea2831..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5e4e363c-2671-4838-80eb-f81aaacbef55.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5e4e7957-9f08-4fbb-b484-a1b79ba75636.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5e4e7957-9f08-4fbb-b484-a1b79ba75636.lance deleted file mode 100644 index 68f8bb1a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5e4e7957-9f08-4fbb-b484-a1b79ba75636.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5e60b1bd-aee8-4874-9d6d-94e40bab186f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5e60b1bd-aee8-4874-9d6d-94e40bab186f.lance deleted file mode 100644 index e7bd8cf45..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5e60b1bd-aee8-4874-9d6d-94e40bab186f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5e64df74-0213-4034-b8d1-4a32c0b433e7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5e64df74-0213-4034-b8d1-4a32c0b433e7.lance deleted file mode 100644 index c11ac60f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5e64df74-0213-4034-b8d1-4a32c0b433e7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ee5fe7d-faca-458b-8858-17a797af30ec.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ee5fe7d-faca-458b-8858-17a797af30ec.lance deleted file mode 100644 index 9d7dcb650..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ee5fe7d-faca-458b-8858-17a797af30ec.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f04f933-aa4d-4088-bea8-02e069edb431.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f04f933-aa4d-4088-bea8-02e069edb431.lance deleted file mode 100644 index 966672a67..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f04f933-aa4d-4088-bea8-02e069edb431.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f0749fd-2c84-4db9-9e47-1990b61d28b4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f0749fd-2c84-4db9-9e47-1990b61d28b4.lance deleted file mode 100644 index 59c63144b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f0749fd-2c84-4db9-9e47-1990b61d28b4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f155c77-adf1-4934-951c-37d20e62bb0a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f155c77-adf1-4934-951c-37d20e62bb0a.lance deleted file mode 100644 index 8dd6bcbd1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f155c77-adf1-4934-951c-37d20e62bb0a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f1babfb-afb8-41d4-a956-fea7c9f65053.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f1babfb-afb8-41d4-a956-fea7c9f65053.lance deleted file mode 100644 index b8cc5f686..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f1babfb-afb8-41d4-a956-fea7c9f65053.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f458abf-741a-4195-b8aa-5de109bbcf8a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f458abf-741a-4195-b8aa-5de109bbcf8a.lance deleted file mode 100644 index a7af9b5a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f458abf-741a-4195-b8aa-5de109bbcf8a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f81f885-f489-4fdd-bfff-0f079f5f69d0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f81f885-f489-4fdd-bfff-0f079f5f69d0.lance deleted file mode 100644 index c77208ee5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5f81f885-f489-4fdd-bfff-0f079f5f69d0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fb00076-78e7-4883-a640-c7423e5f1d48.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fb00076-78e7-4883-a640-c7423e5f1d48.lance deleted file mode 100644 index e729d0abc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fb00076-78e7-4883-a640-c7423e5f1d48.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fb10198-4e75-47b4-b008-7f88d495940f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fb10198-4e75-47b4-b008-7f88d495940f.lance deleted file mode 100644 index bc4bcc9f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fb10198-4e75-47b4-b008-7f88d495940f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fdb94b1-35d1-40d9-8665-982b9f8b4f44.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fdb94b1-35d1-40d9-8665-982b9f8b4f44.lance deleted file mode 100644 index d6a515f93..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fdb94b1-35d1-40d9-8665-982b9f8b4f44.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fe433c9-a8a8-42bb-910d-1b3b0af90855.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fe433c9-a8a8-42bb-910d-1b3b0af90855.lance deleted file mode 100644 index aab39f390..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fe433c9-a8a8-42bb-910d-1b3b0af90855.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fe9fc7e-0fc2-42e4-8a68-cef46ad6a4e1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fe9fc7e-0fc2-42e4-8a68-cef46ad6a4e1.lance deleted file mode 100644 index e58cc5e09..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fe9fc7e-0fc2-42e4-8a68-cef46ad6a4e1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fed8be6-09f6-456a-9fde-9af1271bc4b0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fed8be6-09f6-456a-9fde-9af1271bc4b0.lance deleted file mode 100644 index 0ab55e18a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5fed8be6-09f6-456a-9fde-9af1271bc4b0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ff795ea-fbd6-450f-8a9e-992f4ab01335.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ff795ea-fbd6-450f-8a9e-992f4ab01335.lance deleted file mode 100644 index a9eafbb65..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/5ff795ea-fbd6-450f-8a9e-992f4ab01335.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6055cb3b-9799-4fa0-84cc-ace4ffc2c26d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6055cb3b-9799-4fa0-84cc-ace4ffc2c26d.lance deleted file mode 100644 index 08795f01a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6055cb3b-9799-4fa0-84cc-ace4ffc2c26d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6075cb37-4298-47fa-96d2-c4ca7db74fe4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6075cb37-4298-47fa-96d2-c4ca7db74fe4.lance deleted file mode 100644 index d9cbd9ced..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6075cb37-4298-47fa-96d2-c4ca7db74fe4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/60964dea-af97-48e6-a228-7f4212058459.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/60964dea-af97-48e6-a228-7f4212058459.lance deleted file mode 100644 index 785a3da0c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/60964dea-af97-48e6-a228-7f4212058459.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/60a14319-9de1-4143-9c7e-a4afb2b3e0e5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/60a14319-9de1-4143-9c7e-a4afb2b3e0e5.lance deleted file mode 100644 index 73522c48d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/60a14319-9de1-4143-9c7e-a4afb2b3e0e5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/60b771c9-06a4-4570-8625-d00dc529f644.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/60b771c9-06a4-4570-8625-d00dc529f644.lance deleted file mode 100644 index 081382795..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/60b771c9-06a4-4570-8625-d00dc529f644.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/60c22302-cc69-4d93-8a49-0f6fc477d953.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/60c22302-cc69-4d93-8a49-0f6fc477d953.lance deleted file mode 100644 index 7ba2bdf9c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/60c22302-cc69-4d93-8a49-0f6fc477d953.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/60d21c7d-33bc-4690-9a9c-df4a37814a81.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/60d21c7d-33bc-4690-9a9c-df4a37814a81.lance deleted file mode 100644 index 5c3ef678d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/60d21c7d-33bc-4690-9a9c-df4a37814a81.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/610e52eb-072f-431e-a20a-9f661335b1cd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/610e52eb-072f-431e-a20a-9f661335b1cd.lance deleted file mode 100644 index 0946bd267..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/610e52eb-072f-431e-a20a-9f661335b1cd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/611d568a-e3d5-4d18-b7b1-74734b566450.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/611d568a-e3d5-4d18-b7b1-74734b566450.lance deleted file mode 100644 index e41c7255a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/611d568a-e3d5-4d18-b7b1-74734b566450.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/615fc77b-ab09-42c6-ad29-c57544db301e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/615fc77b-ab09-42c6-ad29-c57544db301e.lance deleted file mode 100644 index 7355ec6b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/615fc77b-ab09-42c6-ad29-c57544db301e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61603680-b49b-4e39-aca7-706169b0d523.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61603680-b49b-4e39-aca7-706169b0d523.lance deleted file mode 100644 index 1b1b3b248..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61603680-b49b-4e39-aca7-706169b0d523.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/617b7d8e-5d46-4a25-9dee-bd463a5b49ef.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/617b7d8e-5d46-4a25-9dee-bd463a5b49ef.lance deleted file mode 100644 index 7291e7988..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/617b7d8e-5d46-4a25-9dee-bd463a5b49ef.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6194c7fe-49cc-4517-8ffe-240f2efa179b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6194c7fe-49cc-4517-8ffe-240f2efa179b.lance deleted file mode 100644 index 5c4f3976e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6194c7fe-49cc-4517-8ffe-240f2efa179b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61a1d5a4-9693-4a09-b3e2-ce1dfa2dd9af.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61a1d5a4-9693-4a09-b3e2-ce1dfa2dd9af.lance deleted file mode 100644 index 5558fafb6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61a1d5a4-9693-4a09-b3e2-ce1dfa2dd9af.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61a6e32d-202d-425c-b9dc-4dc92691cdd9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61a6e32d-202d-425c-b9dc-4dc92691cdd9.lance deleted file mode 100644 index b754e8afa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61a6e32d-202d-425c-b9dc-4dc92691cdd9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61b3507b-5226-4a4b-a34f-82961939420e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61b3507b-5226-4a4b-a34f-82961939420e.lance deleted file mode 100644 index 2e0427769..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61b3507b-5226-4a4b-a34f-82961939420e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61d26bda-1ce6-43fd-beec-8e9ada301f6b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61d26bda-1ce6-43fd-beec-8e9ada301f6b.lance deleted file mode 100644 index 1124b5f01..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61d26bda-1ce6-43fd-beec-8e9ada301f6b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61d3086f-eb2f-4330-96dd-caa96678b13d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61d3086f-eb2f-4330-96dd-caa96678b13d.lance deleted file mode 100644 index 73f81dd56..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61d3086f-eb2f-4330-96dd-caa96678b13d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61ec97e1-3e82-40fa-84bd-ae39306b360b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61ec97e1-3e82-40fa-84bd-ae39306b360b.lance deleted file mode 100644 index 239045ea1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61ec97e1-3e82-40fa-84bd-ae39306b360b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61fb7b64-d232-43bf-83c6-38318a761c02.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61fb7b64-d232-43bf-83c6-38318a761c02.lance deleted file mode 100644 index 2b648d889..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/61fb7b64-d232-43bf-83c6-38318a761c02.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6214ad72-afba-4b97-86bc-609b2a4a8ad6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6214ad72-afba-4b97-86bc-609b2a4a8ad6.lance deleted file mode 100644 index 02d5728cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6214ad72-afba-4b97-86bc-609b2a4a8ad6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6228b769-1025-4750-a48e-58d2ad5df7cc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6228b769-1025-4750-a48e-58d2ad5df7cc.lance deleted file mode 100644 index b844049c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6228b769-1025-4750-a48e-58d2ad5df7cc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/623b9865-2782-45d8-ba52-5006aafd6dd4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/623b9865-2782-45d8-ba52-5006aafd6dd4.lance deleted file mode 100644 index b68fb09ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/623b9865-2782-45d8-ba52-5006aafd6dd4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/627716ff-184d-4a47-a860-9754eb0c37dd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/627716ff-184d-4a47-a860-9754eb0c37dd.lance deleted file mode 100644 index ce45d18db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/627716ff-184d-4a47-a860-9754eb0c37dd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6279fd05-454d-409a-a071-5121b705a249.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6279fd05-454d-409a-a071-5121b705a249.lance deleted file mode 100644 index f6302cb17..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6279fd05-454d-409a-a071-5121b705a249.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6280ff22-6c17-4e43-bad2-e7c41059fc9d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6280ff22-6c17-4e43-bad2-e7c41059fc9d.lance deleted file mode 100644 index 16b32715d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6280ff22-6c17-4e43-bad2-e7c41059fc9d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6283ea8b-854c-49dc-bec2-e687e56e2f10.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6283ea8b-854c-49dc-bec2-e687e56e2f10.lance deleted file mode 100644 index c42662452..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6283ea8b-854c-49dc-bec2-e687e56e2f10.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/628cbf9e-8522-4bc9-8c8c-b95e673efcda.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/628cbf9e-8522-4bc9-8c8c-b95e673efcda.lance deleted file mode 100644 index 5b2ceef66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/628cbf9e-8522-4bc9-8c8c-b95e673efcda.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/628eb8b1-f61e-46d8-a04b-45d74e4bc9cd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/628eb8b1-f61e-46d8-a04b-45d74e4bc9cd.lance deleted file mode 100644 index 274f4e973..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/628eb8b1-f61e-46d8-a04b-45d74e4bc9cd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/62919aee-af54-4070-8b3e-9965b1b2c96f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/62919aee-af54-4070-8b3e-9965b1b2c96f.lance deleted file mode 100644 index a54ae5e79..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/62919aee-af54-4070-8b3e-9965b1b2c96f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/62a5936c-bbb9-42f0-9284-79cdb2b13d21.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/62a5936c-bbb9-42f0-9284-79cdb2b13d21.lance deleted file mode 100644 index 8672a6a58..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/62a5936c-bbb9-42f0-9284-79cdb2b13d21.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/62e2d0a4-9a55-4c28-9037-c1d6b5a4b7be.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/62e2d0a4-9a55-4c28-9037-c1d6b5a4b7be.lance deleted file mode 100644 index b70abd993..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/62e2d0a4-9a55-4c28-9037-c1d6b5a4b7be.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6300bd83-dcd1-4b77-8e23-86d967cfd6eb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6300bd83-dcd1-4b77-8e23-86d967cfd6eb.lance deleted file mode 100644 index 7fac16ed1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6300bd83-dcd1-4b77-8e23-86d967cfd6eb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/631afbeb-95b5-48fe-9ed9-984d2fe6c046.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/631afbeb-95b5-48fe-9ed9-984d2fe6c046.lance deleted file mode 100644 index 894b25639..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/631afbeb-95b5-48fe-9ed9-984d2fe6c046.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/636a4b5d-edbe-4396-b6d6-102ce7caf04f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/636a4b5d-edbe-4396-b6d6-102ce7caf04f.lance deleted file mode 100644 index 80d69d8cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/636a4b5d-edbe-4396-b6d6-102ce7caf04f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6387889b-715d-4eb7-b3c8-46b3f8d7cb9e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6387889b-715d-4eb7-b3c8-46b3f8d7cb9e.lance deleted file mode 100644 index f89c863de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6387889b-715d-4eb7-b3c8-46b3f8d7cb9e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6387cbb6-3930-48c4-b008-ef87dea7cb31.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6387cbb6-3930-48c4-b008-ef87dea7cb31.lance deleted file mode 100644 index 8a782e893..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6387cbb6-3930-48c4-b008-ef87dea7cb31.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/639bc32d-a374-41d0-acb6-9ec84a6acafb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/639bc32d-a374-41d0-acb6-9ec84a6acafb.lance deleted file mode 100644 index 001097e38..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/639bc32d-a374-41d0-acb6-9ec84a6acafb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/639e694e-3d20-4480-9164-938ee44e8d14.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/639e694e-3d20-4480-9164-938ee44e8d14.lance deleted file mode 100644 index 41dd2ea61..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/639e694e-3d20-4480-9164-938ee44e8d14.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63aa2013-bc56-4790-ba20-a610a7f2e920.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63aa2013-bc56-4790-ba20-a610a7f2e920.lance deleted file mode 100644 index a9a32f040..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63aa2013-bc56-4790-ba20-a610a7f2e920.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63aa2f0e-74a8-470f-82c8-b93c2568147c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63aa2f0e-74a8-470f-82c8-b93c2568147c.lance deleted file mode 100644 index 6606bf347..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63aa2f0e-74a8-470f-82c8-b93c2568147c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63af0a69-4098-4a0b-a2c1-de31af7fd4bb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63af0a69-4098-4a0b-a2c1-de31af7fd4bb.lance deleted file mode 100644 index 066cf70bd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63af0a69-4098-4a0b-a2c1-de31af7fd4bb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63b998cc-9432-4b79-81bc-a40de82f75a7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63b998cc-9432-4b79-81bc-a40de82f75a7.lance deleted file mode 100644 index 80041f9a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63b998cc-9432-4b79-81bc-a40de82f75a7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63ce24cf-bcd5-4c59-9943-4cb310216682.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63ce24cf-bcd5-4c59-9943-4cb310216682.lance deleted file mode 100644 index 3d3c2fece..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63ce24cf-bcd5-4c59-9943-4cb310216682.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63e29b78-f4a0-46bd-b5fb-77e1b55e67ef.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63e29b78-f4a0-46bd-b5fb-77e1b55e67ef.lance deleted file mode 100644 index 2d158beb3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63e29b78-f4a0-46bd-b5fb-77e1b55e67ef.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63fd9294-6d48-464d-b9d1-82be1de77a62.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63fd9294-6d48-464d-b9d1-82be1de77a62.lance deleted file mode 100644 index 696a48311..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/63fd9294-6d48-464d-b9d1-82be1de77a62.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/641c4f54-db5c-4ee2-b659-f63503126504.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/641c4f54-db5c-4ee2-b659-f63503126504.lance deleted file mode 100644 index b18b4642d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/641c4f54-db5c-4ee2-b659-f63503126504.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/641d0d68-6663-4011-a538-808eb4d5b73e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/641d0d68-6663-4011-a538-808eb4d5b73e.lance deleted file mode 100644 index a307c3ebb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/641d0d68-6663-4011-a538-808eb4d5b73e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/642632a8-7c6c-4abf-9f1a-b691c90ae69b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/642632a8-7c6c-4abf-9f1a-b691c90ae69b.lance deleted file mode 100644 index e2c524187..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/642632a8-7c6c-4abf-9f1a-b691c90ae69b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/643457d4-08ad-403d-bca9-7480e961aa8b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/643457d4-08ad-403d-bca9-7480e961aa8b.lance deleted file mode 100644 index 8c8941a18..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/643457d4-08ad-403d-bca9-7480e961aa8b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64438394-b3e5-4ccf-94f2-8746498f35af.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64438394-b3e5-4ccf-94f2-8746498f35af.lance deleted file mode 100644 index 1053a8a77..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64438394-b3e5-4ccf-94f2-8746498f35af.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64672308-b05c-489b-95a9-9fff7d3c5071.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64672308-b05c-489b-95a9-9fff7d3c5071.lance deleted file mode 100644 index 5e9cde049..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64672308-b05c-489b-95a9-9fff7d3c5071.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/646cb031-2972-4040-909a-305161d9e7b4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/646cb031-2972-4040-909a-305161d9e7b4.lance deleted file mode 100644 index d9c6479d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/646cb031-2972-4040-909a-305161d9e7b4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6473c7fe-7b99-4147-a347-32ab171dc62e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6473c7fe-7b99-4147-a347-32ab171dc62e.lance deleted file mode 100644 index 44c72218b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6473c7fe-7b99-4147-a347-32ab171dc62e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/647faaf7-97dd-4928-bc43-6a63f0794814.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/647faaf7-97dd-4928-bc43-6a63f0794814.lance deleted file mode 100644 index ac45fa44c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/647faaf7-97dd-4928-bc43-6a63f0794814.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6484d07d-8e8e-4746-ac99-567b5482d346.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6484d07d-8e8e-4746-ac99-567b5482d346.lance deleted file mode 100644 index 8e741411f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6484d07d-8e8e-4746-ac99-567b5482d346.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64928945-068e-4ca2-a726-36a3ce991a91.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64928945-068e-4ca2-a726-36a3ce991a91.lance deleted file mode 100644 index a065347ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64928945-068e-4ca2-a726-36a3ce991a91.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64a9d60f-b064-4c32-8e95-196b4bc84e2b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64a9d60f-b064-4c32-8e95-196b4bc84e2b.lance deleted file mode 100644 index e8c0449d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64a9d60f-b064-4c32-8e95-196b4bc84e2b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64c5b37a-06c5-4ee7-a5fb-e83d67a5c76f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64c5b37a-06c5-4ee7-a5fb-e83d67a5c76f.lance deleted file mode 100644 index d34ea0bf2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64c5b37a-06c5-4ee7-a5fb-e83d67a5c76f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64cc69cf-541a-4f1c-986e-ae756d43dc04.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64cc69cf-541a-4f1c-986e-ae756d43dc04.lance deleted file mode 100644 index 3859ec4be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64cc69cf-541a-4f1c-986e-ae756d43dc04.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64d0daac-0b9c-4cbf-835e-f48476f7d2ab.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64d0daac-0b9c-4cbf-835e-f48476f7d2ab.lance deleted file mode 100644 index d89ee6f0b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64d0daac-0b9c-4cbf-835e-f48476f7d2ab.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64db1044-ebb9-49e3-8ac1-4926a6af626d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64db1044-ebb9-49e3-8ac1-4926a6af626d.lance deleted file mode 100644 index db985a5ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64db1044-ebb9-49e3-8ac1-4926a6af626d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64dd4462-a5ca-407c-b41e-7be761020f32.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64dd4462-a5ca-407c-b41e-7be761020f32.lance deleted file mode 100644 index a1331d8f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64dd4462-a5ca-407c-b41e-7be761020f32.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64ecf1d1-cf70-4752-b96c-11180532b95e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64ecf1d1-cf70-4752-b96c-11180532b95e.lance deleted file mode 100644 index c916d1d48..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64ecf1d1-cf70-4752-b96c-11180532b95e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64fd7884-3cc5-4c96-aa4f-e76dc77fe696.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64fd7884-3cc5-4c96-aa4f-e76dc77fe696.lance deleted file mode 100644 index 4fe7b298c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/64fd7884-3cc5-4c96-aa4f-e76dc77fe696.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/653b0094-a507-45af-b8c1-f1677d361069.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/653b0094-a507-45af-b8c1-f1677d361069.lance deleted file mode 100644 index e88f166eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/653b0094-a507-45af-b8c1-f1677d361069.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/655ef4b6-2427-4f45-bd39-0c4a78b645de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/655ef4b6-2427-4f45-bd39-0c4a78b645de.lance deleted file mode 100644 index b120948bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/655ef4b6-2427-4f45-bd39-0c4a78b645de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/658a3214-f2a0-4451-8d7f-7e39554f40b4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/658a3214-f2a0-4451-8d7f-7e39554f40b4.lance deleted file mode 100644 index 4f5ec7148..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/658a3214-f2a0-4451-8d7f-7e39554f40b4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/658c2b19-b022-4c79-913c-8f0c8787a796.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/658c2b19-b022-4c79-913c-8f0c8787a796.lance deleted file mode 100644 index 397abbba7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/658c2b19-b022-4c79-913c-8f0c8787a796.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/65b2a503-e67b-4006-b75c-0e9f6bb00621.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/65b2a503-e67b-4006-b75c-0e9f6bb00621.lance deleted file mode 100644 index 0214da958..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/65b2a503-e67b-4006-b75c-0e9f6bb00621.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/65bd5f74-2c4f-4cc5-a4bb-30980f2a08c9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/65bd5f74-2c4f-4cc5-a4bb-30980f2a08c9.lance deleted file mode 100644 index 6d64c4b20..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/65bd5f74-2c4f-4cc5-a4bb-30980f2a08c9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/65f1d906-f120-4b79-8231-77af69cea4c4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/65f1d906-f120-4b79-8231-77af69cea4c4.lance deleted file mode 100644 index 62396ae3a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/65f1d906-f120-4b79-8231-77af69cea4c4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/65f20303-afc2-44a2-a2c7-c6da270b1571.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/65f20303-afc2-44a2-a2c7-c6da270b1571.lance deleted file mode 100644 index 67ae2fb11..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/65f20303-afc2-44a2-a2c7-c6da270b1571.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/65fa9adb-be4d-4819-80d2-2d0a8383e2e2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/65fa9adb-be4d-4819-80d2-2d0a8383e2e2.lance deleted file mode 100644 index f831e22c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/65fa9adb-be4d-4819-80d2-2d0a8383e2e2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6608f4e2-c8ac-4812-9916-c94b68de3213.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6608f4e2-c8ac-4812-9916-c94b68de3213.lance deleted file mode 100644 index b550479e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6608f4e2-c8ac-4812-9916-c94b68de3213.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/660cde46-1aac-4242-af9f-119a1471a6d3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/660cde46-1aac-4242-af9f-119a1471a6d3.lance deleted file mode 100644 index cd3eea7fe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/660cde46-1aac-4242-af9f-119a1471a6d3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/66318861-7a74-4ba5-ab9c-7d8fba9f31cd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/66318861-7a74-4ba5-ab9c-7d8fba9f31cd.lance deleted file mode 100644 index 6d951a223..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/66318861-7a74-4ba5-ab9c-7d8fba9f31cd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/66699356-a187-47fa-9f20-5d829c1125bc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/66699356-a187-47fa-9f20-5d829c1125bc.lance deleted file mode 100644 index 6f47b2f45..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/66699356-a187-47fa-9f20-5d829c1125bc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/666e59f5-ee50-4c8a-8810-b45d46752a16.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/666e59f5-ee50-4c8a-8810-b45d46752a16.lance deleted file mode 100644 index a32db1911..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/666e59f5-ee50-4c8a-8810-b45d46752a16.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/668869a7-02f9-4c46-a8ed-0e996f8a5988.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/668869a7-02f9-4c46-a8ed-0e996f8a5988.lance deleted file mode 100644 index b12dbd208..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/668869a7-02f9-4c46-a8ed-0e996f8a5988.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/668c80d3-f487-4b40-8309-d18a6dc4dfc0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/668c80d3-f487-4b40-8309-d18a6dc4dfc0.lance deleted file mode 100644 index 110001986..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/668c80d3-f487-4b40-8309-d18a6dc4dfc0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/66ab628a-7b77-4e31-84fb-731e015b4b23.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/66ab628a-7b77-4e31-84fb-731e015b4b23.lance deleted file mode 100644 index d87472044..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/66ab628a-7b77-4e31-84fb-731e015b4b23.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/66bfc3ac-bc8d-447a-af68-2f946382399c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/66bfc3ac-bc8d-447a-af68-2f946382399c.lance deleted file mode 100644 index f5a226d67..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/66bfc3ac-bc8d-447a-af68-2f946382399c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/66f5d7db-f454-4e1c-a0f4-b16bfba42b90.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/66f5d7db-f454-4e1c-a0f4-b16bfba42b90.lance deleted file mode 100644 index 3c2980274..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/66f5d7db-f454-4e1c-a0f4-b16bfba42b90.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/670120a9-7471-4fb5-bf28-af79a297aff8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/670120a9-7471-4fb5-bf28-af79a297aff8.lance deleted file mode 100644 index 9ee0614ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/670120a9-7471-4fb5-bf28-af79a297aff8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67358017-bd86-43f3-a58d-3bc5335f042a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67358017-bd86-43f3-a58d-3bc5335f042a.lance deleted file mode 100644 index f1de50453..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67358017-bd86-43f3-a58d-3bc5335f042a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6743e038-20fd-44f0-9206-26bfa9308e10.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6743e038-20fd-44f0-9206-26bfa9308e10.lance deleted file mode 100644 index f1e363beb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6743e038-20fd-44f0-9206-26bfa9308e10.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6743f0e0-2af5-45e0-8c63-1b24a4be4dd4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6743f0e0-2af5-45e0-8c63-1b24a4be4dd4.lance deleted file mode 100644 index 870d8b0e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6743f0e0-2af5-45e0-8c63-1b24a4be4dd4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/674d8b07-e449-422b-82ba-fca04b3aa117.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/674d8b07-e449-422b-82ba-fca04b3aa117.lance deleted file mode 100644 index 141bb9d2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/674d8b07-e449-422b-82ba-fca04b3aa117.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/678ffe3c-69c5-4e15-bdae-9f0f3c44a1c4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/678ffe3c-69c5-4e15-bdae-9f0f3c44a1c4.lance deleted file mode 100644 index e32953fa9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/678ffe3c-69c5-4e15-bdae-9f0f3c44a1c4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67a66d43-4f44-433a-8433-33e960f814d8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67a66d43-4f44-433a-8433-33e960f814d8.lance deleted file mode 100644 index 5e32be7d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67a66d43-4f44-433a-8433-33e960f814d8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67a8a9e0-b74e-4b54-9233-f8092b20d8af.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67a8a9e0-b74e-4b54-9233-f8092b20d8af.lance deleted file mode 100644 index a7e445eae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67a8a9e0-b74e-4b54-9233-f8092b20d8af.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67bd7070-92bb-4b16-bca3-72ffed760105.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67bd7070-92bb-4b16-bca3-72ffed760105.lance deleted file mode 100644 index c8536666b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67bd7070-92bb-4b16-bca3-72ffed760105.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67de0cf7-92eb-48c8-b76a-49a828592d05.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67de0cf7-92eb-48c8-b76a-49a828592d05.lance deleted file mode 100644 index a226c289c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67de0cf7-92eb-48c8-b76a-49a828592d05.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67f74d8a-caf8-4db6-ae2d-be41ad0a2a41.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67f74d8a-caf8-4db6-ae2d-be41ad0a2a41.lance deleted file mode 100644 index 718e0bdff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/67f74d8a-caf8-4db6-ae2d-be41ad0a2a41.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/680e0d8f-8c1e-44b8-b1a7-230848cba02f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/680e0d8f-8c1e-44b8-b1a7-230848cba02f.lance deleted file mode 100644 index d9ed80ec1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/680e0d8f-8c1e-44b8-b1a7-230848cba02f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/684ae44f-e1df-43d2-b957-4f8f5c557516.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/684ae44f-e1df-43d2-b957-4f8f5c557516.lance deleted file mode 100644 index 9de428bce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/684ae44f-e1df-43d2-b957-4f8f5c557516.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6869ac4d-bb90-4ede-b4cd-21200661f5d4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6869ac4d-bb90-4ede-b4cd-21200661f5d4.lance deleted file mode 100644 index ac6bfe514..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6869ac4d-bb90-4ede-b4cd-21200661f5d4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/688af704-545e-4bb9-9fcd-455663dd9193.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/688af704-545e-4bb9-9fcd-455663dd9193.lance deleted file mode 100644 index 32b1237c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/688af704-545e-4bb9-9fcd-455663dd9193.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6892dad9-c13e-4daa-b427-f11849605be1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6892dad9-c13e-4daa-b427-f11849605be1.lance deleted file mode 100644 index 414c0260f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6892dad9-c13e-4daa-b427-f11849605be1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/68bd984c-3700-4b06-9f3a-779b66d2e1cf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/68bd984c-3700-4b06-9f3a-779b66d2e1cf.lance deleted file mode 100644 index 77e6a5951..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/68bd984c-3700-4b06-9f3a-779b66d2e1cf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/68c10540-27e2-444e-85ef-1eff5545a5ef.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/68c10540-27e2-444e-85ef-1eff5545a5ef.lance deleted file mode 100644 index 8adc13054..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/68c10540-27e2-444e-85ef-1eff5545a5ef.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/68dddd80-b443-4ed9-9edb-ac00947e25cd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/68dddd80-b443-4ed9-9edb-ac00947e25cd.lance deleted file mode 100644 index 4329efdaa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/68dddd80-b443-4ed9-9edb-ac00947e25cd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/68f92910-83f9-4f0e-9241-feed1c81b364.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/68f92910-83f9-4f0e-9241-feed1c81b364.lance deleted file mode 100644 index c7ffac15e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/68f92910-83f9-4f0e-9241-feed1c81b364.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6907ec08-b087-45e7-98b5-eddf678e8dce.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6907ec08-b087-45e7-98b5-eddf678e8dce.lance deleted file mode 100644 index 798a6c0a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6907ec08-b087-45e7-98b5-eddf678e8dce.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69207179-0cb7-4d0e-86a7-39b8e512fbc1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69207179-0cb7-4d0e-86a7-39b8e512fbc1.lance deleted file mode 100644 index 798e8880c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69207179-0cb7-4d0e-86a7-39b8e512fbc1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/693eb17c-402b-4bbf-982d-2c8aef4976d6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/693eb17c-402b-4bbf-982d-2c8aef4976d6.lance deleted file mode 100644 index fca6b9483..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/693eb17c-402b-4bbf-982d-2c8aef4976d6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69529bb2-8391-4eb0-9c13-8ed47c0f9ce4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69529bb2-8391-4eb0-9c13-8ed47c0f9ce4.lance deleted file mode 100644 index 04a71a318..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69529bb2-8391-4eb0-9c13-8ed47c0f9ce4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69612fbf-a819-4fb7-9962-d6ac8261d46d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69612fbf-a819-4fb7-9962-d6ac8261d46d.lance deleted file mode 100644 index 428b2e97a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69612fbf-a819-4fb7-9962-d6ac8261d46d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/696d290c-9b1d-41fb-a7a0-b16de5706382.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/696d290c-9b1d-41fb-a7a0-b16de5706382.lance deleted file mode 100644 index 474b134f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/696d290c-9b1d-41fb-a7a0-b16de5706382.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6982a65e-b2dc-46ef-9f62-3f40d7c9dfb6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6982a65e-b2dc-46ef-9f62-3f40d7c9dfb6.lance deleted file mode 100644 index 5031012df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6982a65e-b2dc-46ef-9f62-3f40d7c9dfb6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69985f37-bf49-4e78-9e0d-cd225e55e7ce.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69985f37-bf49-4e78-9e0d-cd225e55e7ce.lance deleted file mode 100644 index 9e7153010..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69985f37-bf49-4e78-9e0d-cd225e55e7ce.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69ab80ac-e3bd-45ce-94fe-c22bf912182d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69ab80ac-e3bd-45ce-94fe-c22bf912182d.lance deleted file mode 100644 index 245ea4c6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69ab80ac-e3bd-45ce-94fe-c22bf912182d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69b370bf-9202-49d4-8c29-43cf436bb600.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69b370bf-9202-49d4-8c29-43cf436bb600.lance deleted file mode 100644 index cab424a7b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69b370bf-9202-49d4-8c29-43cf436bb600.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69e72f58-82a1-437f-964e-6699045968c0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69e72f58-82a1-437f-964e-6699045968c0.lance deleted file mode 100644 index bd3ff1b74..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69e72f58-82a1-437f-964e-6699045968c0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69ee6373-eae6-4548-b98a-3d0eb7d7f386.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69ee6373-eae6-4548-b98a-3d0eb7d7f386.lance deleted file mode 100644 index 60fe6c5ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69ee6373-eae6-4548-b98a-3d0eb7d7f386.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69ef7831-bc59-4973-bf37-2085347f790c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69ef7831-bc59-4973-bf37-2085347f790c.lance deleted file mode 100644 index b0c3ca0b2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69ef7831-bc59-4973-bf37-2085347f790c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69f0d0f3-bca5-46d1-8f2c-4e9f7ccdbdba.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69f0d0f3-bca5-46d1-8f2c-4e9f7ccdbdba.lance deleted file mode 100644 index c0a4ab327..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/69f0d0f3-bca5-46d1-8f2c-4e9f7ccdbdba.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6a17f7d0-6536-4f83-8cdc-cf7db0bf665f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6a17f7d0-6536-4f83-8cdc-cf7db0bf665f.lance deleted file mode 100644 index f80d2313e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6a17f7d0-6536-4f83-8cdc-cf7db0bf665f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6a198d9f-48b1-4e9e-bfd1-c644c37dff8d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6a198d9f-48b1-4e9e-bfd1-c644c37dff8d.lance deleted file mode 100644 index e79e64d8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6a198d9f-48b1-4e9e-bfd1-c644c37dff8d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6a40ecb4-0d35-462c-9990-7de70c7732c7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6a40ecb4-0d35-462c-9990-7de70c7732c7.lance deleted file mode 100644 index 1039f4b89..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6a40ecb4-0d35-462c-9990-7de70c7732c7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6a9334a4-71dd-448a-ad94-eeb3388dea95.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6a9334a4-71dd-448a-ad94-eeb3388dea95.lance deleted file mode 100644 index 7c6b13f4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6a9334a4-71dd-448a-ad94-eeb3388dea95.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ad17080-40f0-4ab0-abb5-e9730419bf36.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ad17080-40f0-4ab0-abb5-e9730419bf36.lance deleted file mode 100644 index 6c77cddd5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ad17080-40f0-4ab0-abb5-e9730419bf36.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ae7138c-af04-4cca-be5b-f87c8d7efd28.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ae7138c-af04-4cca-be5b-f87c8d7efd28.lance deleted file mode 100644 index 1c4b13af3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ae7138c-af04-4cca-be5b-f87c8d7efd28.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b027634-4bec-4225-aee2-0902c8219f7a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b027634-4bec-4225-aee2-0902c8219f7a.lance deleted file mode 100644 index a2736c852..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b027634-4bec-4225-aee2-0902c8219f7a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b04d0c7-c5ee-4048-853d-1c4ed2fdc0dd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b04d0c7-c5ee-4048-853d-1c4ed2fdc0dd.lance deleted file mode 100644 index 06b24caf5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b04d0c7-c5ee-4048-853d-1c4ed2fdc0dd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b2b2697-c1e4-4704-8fda-0305b860c707.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b2b2697-c1e4-4704-8fda-0305b860c707.lance deleted file mode 100644 index 295467b0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b2b2697-c1e4-4704-8fda-0305b860c707.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b356002-8366-41cc-9bad-b6c8b0238a1e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b356002-8366-41cc-9bad-b6c8b0238a1e.lance deleted file mode 100644 index 330008f10..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b356002-8366-41cc-9bad-b6c8b0238a1e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b38b338-7e29-4adf-985d-d8ba0192ae98.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b38b338-7e29-4adf-985d-d8ba0192ae98.lance deleted file mode 100644 index 0f0caa6b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b38b338-7e29-4adf-985d-d8ba0192ae98.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b512377-85d2-4719-ae9c-266e17c2022b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b512377-85d2-4719-ae9c-266e17c2022b.lance deleted file mode 100644 index 4f2daa792..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b512377-85d2-4719-ae9c-266e17c2022b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b668212-fcd5-41cf-a4cf-44c35b622054.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b668212-fcd5-41cf-a4cf-44c35b622054.lance deleted file mode 100644 index 426335b45..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b668212-fcd5-41cf-a4cf-44c35b622054.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b6a343a-2f87-4d62-ac06-a8a90de1307a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b6a343a-2f87-4d62-ac06-a8a90de1307a.lance deleted file mode 100644 index 2f95e5320..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b6a343a-2f87-4d62-ac06-a8a90de1307a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b728acf-8965-4445-8343-3935566b690a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b728acf-8965-4445-8343-3935566b690a.lance deleted file mode 100644 index 474c3610a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b728acf-8965-4445-8343-3935566b690a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b7863ed-9d2b-44d8-9210-cc7aa68e5335.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b7863ed-9d2b-44d8-9210-cc7aa68e5335.lance deleted file mode 100644 index 4fb0d34f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b7863ed-9d2b-44d8-9210-cc7aa68e5335.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b8dfc56-8a2c-400b-a285-a958d7766adf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b8dfc56-8a2c-400b-a285-a958d7766adf.lance deleted file mode 100644 index d3210e3d6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b8dfc56-8a2c-400b-a285-a958d7766adf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b9fcc14-8de8-46cd-a5df-201f8285fe9d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b9fcc14-8de8-46cd-a5df-201f8285fe9d.lance deleted file mode 100644 index b5b61495e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6b9fcc14-8de8-46cd-a5df-201f8285fe9d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6bb773f3-a5ae-4853-8dbb-179fdfc2cc26.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6bb773f3-a5ae-4853-8dbb-179fdfc2cc26.lance deleted file mode 100644 index fed3c09e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6bb773f3-a5ae-4853-8dbb-179fdfc2cc26.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6beb87aa-e274-467c-81c4-0fa4b231c18e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6beb87aa-e274-467c-81c4-0fa4b231c18e.lance deleted file mode 100644 index 2aa6ebda9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6beb87aa-e274-467c-81c4-0fa4b231c18e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6bf40466-f061-4387-807d-41f901e073bd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6bf40466-f061-4387-807d-41f901e073bd.lance deleted file mode 100644 index dc86fe2f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6bf40466-f061-4387-807d-41f901e073bd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c0debd3-4f16-4ea7-9b5d-0ad82983c49a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c0debd3-4f16-4ea7-9b5d-0ad82983c49a.lance deleted file mode 100644 index 017346f2d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c0debd3-4f16-4ea7-9b5d-0ad82983c49a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c2ca04a-188a-4749-a485-73dfb0896ffe.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c2ca04a-188a-4749-a485-73dfb0896ffe.lance deleted file mode 100644 index 133126898..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c2ca04a-188a-4749-a485-73dfb0896ffe.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c6e6d43-f36b-433b-9186-6702d2c67aae.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c6e6d43-f36b-433b-9186-6702d2c67aae.lance deleted file mode 100644 index 49fab719b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c6e6d43-f36b-433b-9186-6702d2c67aae.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c7918b7-b180-47f5-a3a6-9edee3374ac6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c7918b7-b180-47f5-a3a6-9edee3374ac6.lance deleted file mode 100644 index 21c94e408..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c7918b7-b180-47f5-a3a6-9edee3374ac6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c8560e5-a93b-4a0c-9d54-3bfc115d0085.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c8560e5-a93b-4a0c-9d54-3bfc115d0085.lance deleted file mode 100644 index c445e2ded..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c8560e5-a93b-4a0c-9d54-3bfc115d0085.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c867347-11c7-4aea-a130-534047a7bea1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c867347-11c7-4aea-a130-534047a7bea1.lance deleted file mode 100644 index d73c78942..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c867347-11c7-4aea-a130-534047a7bea1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c9afc49-1ad9-4583-b175-1f034770a35c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c9afc49-1ad9-4583-b175-1f034770a35c.lance deleted file mode 100644 index 6b5506a8d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6c9afc49-1ad9-4583-b175-1f034770a35c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ca7fa30-568d-447e-ad90-48b5dd7f407f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ca7fa30-568d-447e-ad90-48b5dd7f407f.lance deleted file mode 100644 index 2dc0d9d40..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ca7fa30-568d-447e-ad90-48b5dd7f407f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6cac2f5a-ca7a-4f22-a77d-5538d4d17957.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6cac2f5a-ca7a-4f22-a77d-5538d4d17957.lance deleted file mode 100644 index bd4b22db3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6cac2f5a-ca7a-4f22-a77d-5538d4d17957.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6cad159d-84b7-40c1-b646-c0ac0b491585.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6cad159d-84b7-40c1-b646-c0ac0b491585.lance deleted file mode 100644 index 6b2d55c9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6cad159d-84b7-40c1-b646-c0ac0b491585.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6cd47714-fb92-4a57-ab60-e1218894c66d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6cd47714-fb92-4a57-ab60-e1218894c66d.lance deleted file mode 100644 index 9b72ef4c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6cd47714-fb92-4a57-ab60-e1218894c66d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ce46a32-c27d-48de-89d9-21b986e4a8d4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ce46a32-c27d-48de-89d9-21b986e4a8d4.lance deleted file mode 100644 index d961287ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ce46a32-c27d-48de-89d9-21b986e4a8d4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ce6d1af-c538-46f2-8143-151964f27cee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ce6d1af-c538-46f2-8143-151964f27cee.lance deleted file mode 100644 index 3cb1cd5de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ce6d1af-c538-46f2-8143-151964f27cee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6cf2cfaf-7402-4e76-ba15-18b12cb8d98e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6cf2cfaf-7402-4e76-ba15-18b12cb8d98e.lance deleted file mode 100644 index b07e4b47c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6cf2cfaf-7402-4e76-ba15-18b12cb8d98e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d064934-7592-4bde-8328-602ce133a958.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d064934-7592-4bde-8328-602ce133a958.lance deleted file mode 100644 index e9213ad2b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d064934-7592-4bde-8328-602ce133a958.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d17f904-9960-40c1-a470-5826bce9c1f7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d17f904-9960-40c1-a470-5826bce9c1f7.lance deleted file mode 100644 index 5a9ebd99e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d17f904-9960-40c1-a470-5826bce9c1f7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d1f880b-f221-4024-a3bc-80a663d53eee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d1f880b-f221-4024-a3bc-80a663d53eee.lance deleted file mode 100644 index 2be522959..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d1f880b-f221-4024-a3bc-80a663d53eee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d4d1160-a143-40ab-ab59-f3bc6786c694.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d4d1160-a143-40ab-ab59-f3bc6786c694.lance deleted file mode 100644 index 59bac2fa0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d4d1160-a143-40ab-ab59-f3bc6786c694.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d562e66-b19d-4ecb-88a7-818e733d1c01.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d562e66-b19d-4ecb-88a7-818e733d1c01.lance deleted file mode 100644 index 9b5fb1ab5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d562e66-b19d-4ecb-88a7-818e733d1c01.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d60430b-5602-4793-ab35-bab11396ddc7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d60430b-5602-4793-ab35-bab11396ddc7.lance deleted file mode 100644 index a5cea146a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d60430b-5602-4793-ab35-bab11396ddc7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d985200-7425-48af-b170-52592d618a83.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d985200-7425-48af-b170-52592d618a83.lance deleted file mode 100644 index 223f00f1e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d985200-7425-48af-b170-52592d618a83.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d98f578-21ac-4847-a813-45ce818a53a4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d98f578-21ac-4847-a813-45ce818a53a4.lance deleted file mode 100644 index fcd9040f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6d98f578-21ac-4847-a813-45ce818a53a4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6da0b69e-281e-4e1b-9d9e-3f866b255387.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6da0b69e-281e-4e1b-9d9e-3f866b255387.lance deleted file mode 100644 index fbc88e211..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6da0b69e-281e-4e1b-9d9e-3f866b255387.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6dca0858-c07b-498d-b3cd-b44620c918a4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6dca0858-c07b-498d-b3cd-b44620c918a4.lance deleted file mode 100644 index 282eda9ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6dca0858-c07b-498d-b3cd-b44620c918a4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e13e67b-01f9-4099-b99f-50d26a7addba.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e13e67b-01f9-4099-b99f-50d26a7addba.lance deleted file mode 100644 index 17db20170..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e13e67b-01f9-4099-b99f-50d26a7addba.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e16fb43-276a-45ab-9aa5-e7876e4fba58.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e16fb43-276a-45ab-9aa5-e7876e4fba58.lance deleted file mode 100644 index ddd868997..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e16fb43-276a-45ab-9aa5-e7876e4fba58.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e22f24c-aade-4519-9a69-a0cc8ec49262.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e22f24c-aade-4519-9a69-a0cc8ec49262.lance deleted file mode 100644 index f7982572c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e22f24c-aade-4519-9a69-a0cc8ec49262.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e250fb1-9e0e-493d-8264-f1c0344ceea3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e250fb1-9e0e-493d-8264-f1c0344ceea3.lance deleted file mode 100644 index 73450f26d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e250fb1-9e0e-493d-8264-f1c0344ceea3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e298113-95af-4810-875e-86afb6f0b3c3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e298113-95af-4810-875e-86afb6f0b3c3.lance deleted file mode 100644 index 0d796337c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e298113-95af-4810-875e-86afb6f0b3c3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e32c116-e393-4582-bec6-8e5646bff5dd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e32c116-e393-4582-bec6-8e5646bff5dd.lance deleted file mode 100644 index a5300e576..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e32c116-e393-4582-bec6-8e5646bff5dd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e4b14ec-d2a1-4598-937f-511bf94dc102.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e4b14ec-d2a1-4598-937f-511bf94dc102.lance deleted file mode 100644 index 3ab1d0576..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e4b14ec-d2a1-4598-937f-511bf94dc102.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e53d17a-976b-4b5c-a6d0-c8af54f1e5d6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e53d17a-976b-4b5c-a6d0-c8af54f1e5d6.lance deleted file mode 100644 index dc1255232..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e53d17a-976b-4b5c-a6d0-c8af54f1e5d6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e622d5b-55cb-4a56-9921-01704a8c86eb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e622d5b-55cb-4a56-9921-01704a8c86eb.lance deleted file mode 100644 index 866cee21a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e622d5b-55cb-4a56-9921-01704a8c86eb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e740dd0-accd-40cd-997c-ddf0cede6870.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e740dd0-accd-40cd-997c-ddf0cede6870.lance deleted file mode 100644 index c3c2c8d16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e740dd0-accd-40cd-997c-ddf0cede6870.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e9cd253-8785-49ea-b9aa-747061fe4b41.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e9cd253-8785-49ea-b9aa-747061fe4b41.lance deleted file mode 100644 index 824a2e225..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6e9cd253-8785-49ea-b9aa-747061fe4b41.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ec09e54-4583-4b05-92de-a7bcd690a8e1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ec09e54-4583-4b05-92de-a7bcd690a8e1.lance deleted file mode 100644 index 91b8c9da5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ec09e54-4583-4b05-92de-a7bcd690a8e1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ec19ba1-f8ae-42d7-9b5c-de5848115ae0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ec19ba1-f8ae-42d7-9b5c-de5848115ae0.lance deleted file mode 100644 index 41a7318d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ec19ba1-f8ae-42d7-9b5c-de5848115ae0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ec54661-2bee-4579-8687-261f8c3dbae4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ec54661-2bee-4579-8687-261f8c3dbae4.lance deleted file mode 100644 index e977d8dac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ec54661-2bee-4579-8687-261f8c3dbae4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ef52a8b-08c4-4bc7-8b9f-834536420067.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ef52a8b-08c4-4bc7-8b9f-834536420067.lance deleted file mode 100644 index 135644558..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ef52a8b-08c4-4bc7-8b9f-834536420067.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6f1f776d-b87d-4e21-99b3-1701850da0de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6f1f776d-b87d-4e21-99b3-1701850da0de.lance deleted file mode 100644 index f8527a8c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6f1f776d-b87d-4e21-99b3-1701850da0de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6f2cd580-004e-4f03-8d8b-d1507298e85f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6f2cd580-004e-4f03-8d8b-d1507298e85f.lance deleted file mode 100644 index d05c1eb98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6f2cd580-004e-4f03-8d8b-d1507298e85f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6f3d3d3a-6179-40de-8ce9-735711b9cb97.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6f3d3d3a-6179-40de-8ce9-735711b9cb97.lance deleted file mode 100644 index fedf364e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6f3d3d3a-6179-40de-8ce9-735711b9cb97.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6f90e0ff-5bfb-4369-abf9-8868bf0e9927.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6f90e0ff-5bfb-4369-abf9-8868bf0e9927.lance deleted file mode 100644 index 834e243ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6f90e0ff-5bfb-4369-abf9-8868bf0e9927.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6fa72f3d-b5ab-4788-95f4-cd759a3a43f7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6fa72f3d-b5ab-4788-95f4-cd759a3a43f7.lance deleted file mode 100644 index eef36f44c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6fa72f3d-b5ab-4788-95f4-cd759a3a43f7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6fab8fd0-40f1-4fdd-9977-8017d2cd1dd2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6fab8fd0-40f1-4fdd-9977-8017d2cd1dd2.lance deleted file mode 100644 index 2e25a5e16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6fab8fd0-40f1-4fdd-9977-8017d2cd1dd2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6fcd7132-f0e0-49d5-9b05-b0421d022f08.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6fcd7132-f0e0-49d5-9b05-b0421d022f08.lance deleted file mode 100644 index 3dbd3900d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6fcd7132-f0e0-49d5-9b05-b0421d022f08.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6fd7655d-1c07-4173-8610-faca5ffbafb2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6fd7655d-1c07-4173-8610-faca5ffbafb2.lance deleted file mode 100644 index fea7fee50..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6fd7655d-1c07-4173-8610-faca5ffbafb2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ff219d1-0c11-4d8e-a422-93d841cd70ec.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ff219d1-0c11-4d8e-a422-93d841cd70ec.lance deleted file mode 100644 index a2c0c9546..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/6ff219d1-0c11-4d8e-a422-93d841cd70ec.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70137173-8760-46ac-9ce4-a3714ee9fc57.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70137173-8760-46ac-9ce4-a3714ee9fc57.lance deleted file mode 100644 index 7ee8e293a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70137173-8760-46ac-9ce4-a3714ee9fc57.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7073bd5e-4579-4e6b-8174-31c592a834b6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7073bd5e-4579-4e6b-8174-31c592a834b6.lance deleted file mode 100644 index 1a49eace9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7073bd5e-4579-4e6b-8174-31c592a834b6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7096c2e2-8fd8-41cf-ae38-c92b84da7822.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7096c2e2-8fd8-41cf-ae38-c92b84da7822.lance deleted file mode 100644 index 4c6575884..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7096c2e2-8fd8-41cf-ae38-c92b84da7822.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70984519-e7ca-48ba-bb6d-e4c5e29fead4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70984519-e7ca-48ba-bb6d-e4c5e29fead4.lance deleted file mode 100644 index 04ca75b57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70984519-e7ca-48ba-bb6d-e4c5e29fead4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70a4b034-007c-48d9-8307-d663f3d64fe7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70a4b034-007c-48d9-8307-d663f3d64fe7.lance deleted file mode 100644 index 92b4adc1d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70a4b034-007c-48d9-8307-d663f3d64fe7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70dee6f1-3311-4579-a87b-c0e32485aede.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70dee6f1-3311-4579-a87b-c0e32485aede.lance deleted file mode 100644 index d720fb765..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70dee6f1-3311-4579-a87b-c0e32485aede.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70eefc27-19ec-4811-9461-496837a69709.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70eefc27-19ec-4811-9461-496837a69709.lance deleted file mode 100644 index 21e28226c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70eefc27-19ec-4811-9461-496837a69709.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70f3ba97-c16b-43c1-bb2e-2c3e9d2236ec.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70f3ba97-c16b-43c1-bb2e-2c3e9d2236ec.lance deleted file mode 100644 index 556014740..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70f3ba97-c16b-43c1-bb2e-2c3e9d2236ec.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70fc1f3e-7f94-41a0-911d-7c8570b76c74.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70fc1f3e-7f94-41a0-911d-7c8570b76c74.lance deleted file mode 100644 index defbe979b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/70fc1f3e-7f94-41a0-911d-7c8570b76c74.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7107a5c8-3d3b-490c-a4dc-21aa29895254.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7107a5c8-3d3b-490c-a4dc-21aa29895254.lance deleted file mode 100644 index 29f3d9515..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7107a5c8-3d3b-490c-a4dc-21aa29895254.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/711b7a5e-3ccb-46c9-aac0-ade2177d8c3b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/711b7a5e-3ccb-46c9-aac0-ade2177d8c3b.lance deleted file mode 100644 index 33f87902c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/711b7a5e-3ccb-46c9-aac0-ade2177d8c3b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/712882d5-e699-4087-99fd-d6441fd6de2e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/712882d5-e699-4087-99fd-d6441fd6de2e.lance deleted file mode 100644 index ceedb16e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/712882d5-e699-4087-99fd-d6441fd6de2e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/712fa7d3-b96a-4680-a4a3-23c05e6406e2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/712fa7d3-b96a-4680-a4a3-23c05e6406e2.lance deleted file mode 100644 index 90454e24b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/712fa7d3-b96a-4680-a4a3-23c05e6406e2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/713497a3-ae01-4ae0-b68a-97963e4f4bfb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/713497a3-ae01-4ae0-b68a-97963e4f4bfb.lance deleted file mode 100644 index ad9dfbc24..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/713497a3-ae01-4ae0-b68a-97963e4f4bfb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/716b47f8-1d71-49e2-8a77-f1e655b2a78a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/716b47f8-1d71-49e2-8a77-f1e655b2a78a.lance deleted file mode 100644 index ec2e8acc4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/716b47f8-1d71-49e2-8a77-f1e655b2a78a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71873329-14bc-43a6-a308-7bec8bc150fe.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71873329-14bc-43a6-a308-7bec8bc150fe.lance deleted file mode 100644 index 20257e1b0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71873329-14bc-43a6-a308-7bec8bc150fe.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71873c1a-3ed6-4e45-9ca4-47b7ab738a68.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71873c1a-3ed6-4e45-9ca4-47b7ab738a68.lance deleted file mode 100644 index 32a41e2a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71873c1a-3ed6-4e45-9ca4-47b7ab738a68.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/718d8941-b747-4e43-aee0-ffd0fcfa2534.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/718d8941-b747-4e43-aee0-ffd0fcfa2534.lance deleted file mode 100644 index dd44da5d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/718d8941-b747-4e43-aee0-ffd0fcfa2534.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71b5dc9d-57c5-4bda-9233-5ba8583f070c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71b5dc9d-57c5-4bda-9233-5ba8583f070c.lance deleted file mode 100644 index b89e1879f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71b5dc9d-57c5-4bda-9233-5ba8583f070c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71b5ffb0-649c-4014-8759-00e9881f1380.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71b5ffb0-649c-4014-8759-00e9881f1380.lance deleted file mode 100644 index c2ae4ce8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71b5ffb0-649c-4014-8759-00e9881f1380.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71b68433-c8a4-4cfd-af4c-102615bccb8e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71b68433-c8a4-4cfd-af4c-102615bccb8e.lance deleted file mode 100644 index 94a60f544..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71b68433-c8a4-4cfd-af4c-102615bccb8e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71df877f-0ba8-401c-b5f0-6c8a78b96572.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71df877f-0ba8-401c-b5f0-6c8a78b96572.lance deleted file mode 100644 index 87a16ecc8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/71df877f-0ba8-401c-b5f0-6c8a78b96572.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7213a657-9b57-4a15-bfd6-ae216287e099.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7213a657-9b57-4a15-bfd6-ae216287e099.lance deleted file mode 100644 index 3b26a094e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7213a657-9b57-4a15-bfd6-ae216287e099.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/722d507d-d683-42f0-a392-6875d4610bc3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/722d507d-d683-42f0-a392-6875d4610bc3.lance deleted file mode 100644 index 8be25dc20..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/722d507d-d683-42f0-a392-6875d4610bc3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7255c75d-8c66-4722-88ef-d40e93e8a512.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7255c75d-8c66-4722-88ef-d40e93e8a512.lance deleted file mode 100644 index 18e4411ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7255c75d-8c66-4722-88ef-d40e93e8a512.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/72703e3a-762d-4b22-a647-29547a3c30ca.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/72703e3a-762d-4b22-a647-29547a3c30ca.lance deleted file mode 100644 index 32d351a9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/72703e3a-762d-4b22-a647-29547a3c30ca.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/728c4d05-bedc-4327-b22b-9a095a73a5ce.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/728c4d05-bedc-4327-b22b-9a095a73a5ce.lance deleted file mode 100644 index e282a0830..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/728c4d05-bedc-4327-b22b-9a095a73a5ce.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/72f234b7-975a-48d5-8bfa-6ea0867a0b20.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/72f234b7-975a-48d5-8bfa-6ea0867a0b20.lance deleted file mode 100644 index c1b2c9c98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/72f234b7-975a-48d5-8bfa-6ea0867a0b20.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73147235-53b4-4d41-a5d0-51f02fd7401a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73147235-53b4-4d41-a5d0-51f02fd7401a.lance deleted file mode 100644 index 991e90b52..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73147235-53b4-4d41-a5d0-51f02fd7401a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/732e7101-f22b-4279-b757-28dbd6a0ea0f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/732e7101-f22b-4279-b757-28dbd6a0ea0f.lance deleted file mode 100644 index d85d5b863..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/732e7101-f22b-4279-b757-28dbd6a0ea0f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/733b6325-4497-449d-b528-4badb0442234.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/733b6325-4497-449d-b528-4badb0442234.lance deleted file mode 100644 index 8368fd81c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/733b6325-4497-449d-b528-4badb0442234.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7351f4f4-ddac-4a1a-9e77-c6f8c605b9f8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7351f4f4-ddac-4a1a-9e77-c6f8c605b9f8.lance deleted file mode 100644 index 0bf332850..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7351f4f4-ddac-4a1a-9e77-c6f8c605b9f8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7398eaa1-d05a-40d6-b914-fb730376cfb2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7398eaa1-d05a-40d6-b914-fb730376cfb2.lance deleted file mode 100644 index 0cd13c986..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7398eaa1-d05a-40d6-b914-fb730376cfb2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73bd7114-70e1-4267-bc39-33ea0ecbb6cf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73bd7114-70e1-4267-bc39-33ea0ecbb6cf.lance deleted file mode 100644 index 24bc8024f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73bd7114-70e1-4267-bc39-33ea0ecbb6cf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73c06206-fed5-4c63-92f9-6db2905f3c17.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73c06206-fed5-4c63-92f9-6db2905f3c17.lance deleted file mode 100644 index a52f3f0be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73c06206-fed5-4c63-92f9-6db2905f3c17.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73d2c2f2-5cc2-4db7-88d4-b7804c6b7be7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73d2c2f2-5cc2-4db7-88d4-b7804c6b7be7.lance deleted file mode 100644 index 5cf276df1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73d2c2f2-5cc2-4db7-88d4-b7804c6b7be7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73dc4167-6a42-4d6c-b3ae-419f4e200789.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73dc4167-6a42-4d6c-b3ae-419f4e200789.lance deleted file mode 100644 index c04476043..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73dc4167-6a42-4d6c-b3ae-419f4e200789.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73e591fd-a592-4619-b771-1a6ef222100f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73e591fd-a592-4619-b771-1a6ef222100f.lance deleted file mode 100644 index 72b761d9d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73e591fd-a592-4619-b771-1a6ef222100f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73e5ea4d-456f-417a-a501-2260a84cdedf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73e5ea4d-456f-417a-a501-2260a84cdedf.lance deleted file mode 100644 index 4ef96aa0c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73e5ea4d-456f-417a-a501-2260a84cdedf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73e94ff0-c37c-45f0-85c2-18944aae8e4a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73e94ff0-c37c-45f0-85c2-18944aae8e4a.lance deleted file mode 100644 index cf25ddd83..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73e94ff0-c37c-45f0-85c2-18944aae8e4a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73eb0ae2-63e6-4690-b789-a5168e9c718c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73eb0ae2-63e6-4690-b789-a5168e9c718c.lance deleted file mode 100644 index 1af57a56a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73eb0ae2-63e6-4690-b789-a5168e9c718c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73ee8760-55c2-401b-9976-442c45e74e3d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73ee8760-55c2-401b-9976-442c45e74e3d.lance deleted file mode 100644 index fcb7e80e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73ee8760-55c2-401b-9976-442c45e74e3d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73f2cfc4-5c4b-4d9a-8f32-ab9d2e445e7c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73f2cfc4-5c4b-4d9a-8f32-ab9d2e445e7c.lance deleted file mode 100644 index a4cf369af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/73f2cfc4-5c4b-4d9a-8f32-ab9d2e445e7c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/74049ca1-c6f6-4511-9d85-aa8857c8067d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/74049ca1-c6f6-4511-9d85-aa8857c8067d.lance deleted file mode 100644 index 55c6d0588..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/74049ca1-c6f6-4511-9d85-aa8857c8067d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7434739a-4f58-4dea-89d2-aefa5aab2298.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7434739a-4f58-4dea-89d2-aefa5aab2298.lance deleted file mode 100644 index b3177f68a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7434739a-4f58-4dea-89d2-aefa5aab2298.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/74542fa6-f0f2-4fae-bdd1-48d9bcd80147.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/74542fa6-f0f2-4fae-bdd1-48d9bcd80147.lance deleted file mode 100644 index f873e7752..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/74542fa6-f0f2-4fae-bdd1-48d9bcd80147.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/74804d61-d5c3-4e99-a767-497703feefd7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/74804d61-d5c3-4e99-a767-497703feefd7.lance deleted file mode 100644 index 43b327f0a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/74804d61-d5c3-4e99-a767-497703feefd7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/749b4276-c91c-4f4e-8f1c-7a04fd083a6a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/749b4276-c91c-4f4e-8f1c-7a04fd083a6a.lance deleted file mode 100644 index 9cf4cf383..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/749b4276-c91c-4f4e-8f1c-7a04fd083a6a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/749f3669-54cc-4ef1-b2e0-0da51d3b398b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/749f3669-54cc-4ef1-b2e0-0da51d3b398b.lance deleted file mode 100644 index 7acd6b467..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/749f3669-54cc-4ef1-b2e0-0da51d3b398b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/74c09668-597b-409e-8c79-8366bed54468.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/74c09668-597b-409e-8c79-8366bed54468.lance deleted file mode 100644 index 8d55adcbf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/74c09668-597b-409e-8c79-8366bed54468.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75046545-d1a9-4506-ac64-b29fd338d5bc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75046545-d1a9-4506-ac64-b29fd338d5bc.lance deleted file mode 100644 index 779b644ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75046545-d1a9-4506-ac64-b29fd338d5bc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7542e8b2-4a8d-4d2e-aa50-92acc3dc4e9f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7542e8b2-4a8d-4d2e-aa50-92acc3dc4e9f.lance deleted file mode 100644 index 72ce1e21c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7542e8b2-4a8d-4d2e-aa50-92acc3dc4e9f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/754acac3-5ffb-405c-b6c2-ee5d2bfb0bdd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/754acac3-5ffb-405c-b6c2-ee5d2bfb0bdd.lance deleted file mode 100644 index 6a46820d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/754acac3-5ffb-405c-b6c2-ee5d2bfb0bdd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/757b76a8-a264-4a69-b6cb-b9f3d2f66519.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/757b76a8-a264-4a69-b6cb-b9f3d2f66519.lance deleted file mode 100644 index 18ea42cb1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/757b76a8-a264-4a69-b6cb-b9f3d2f66519.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7586e6a7-466c-4525-bdc4-9ff059ff3252.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7586e6a7-466c-4525-bdc4-9ff059ff3252.lance deleted file mode 100644 index e40c18535..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7586e6a7-466c-4525-bdc4-9ff059ff3252.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/759b82fd-cbfb-4ca9-ba41-2aa73dbfabdf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/759b82fd-cbfb-4ca9-ba41-2aa73dbfabdf.lance deleted file mode 100644 index ae35d69af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/759b82fd-cbfb-4ca9-ba41-2aa73dbfabdf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75a3c403-1a3e-4adc-b029-cb40702c816b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75a3c403-1a3e-4adc-b029-cb40702c816b.lance deleted file mode 100644 index 64f55efb6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75a3c403-1a3e-4adc-b029-cb40702c816b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75aa141b-bd4d-4de9-803d-2a5f31bf22c0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75aa141b-bd4d-4de9-803d-2a5f31bf22c0.lance deleted file mode 100644 index ccddc1739..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75aa141b-bd4d-4de9-803d-2a5f31bf22c0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75ac5972-fc0b-492e-b278-e66d73c71221.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75ac5972-fc0b-492e-b278-e66d73c71221.lance deleted file mode 100644 index b15034d98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75ac5972-fc0b-492e-b278-e66d73c71221.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75b95efb-8452-496e-bbc8-a7d67709c47a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75b95efb-8452-496e-bbc8-a7d67709c47a.lance deleted file mode 100644 index 9a56c28d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75b95efb-8452-496e-bbc8-a7d67709c47a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75e5e5f6-b812-4aba-aa43-7d854fe0e718.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75e5e5f6-b812-4aba-aa43-7d854fe0e718.lance deleted file mode 100644 index 308bb5d1f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/75e5e5f6-b812-4aba-aa43-7d854fe0e718.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/760035ec-0f4a-4597-894f-77f0667ba254.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/760035ec-0f4a-4597-894f-77f0667ba254.lance deleted file mode 100644 index 99c13deb2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/760035ec-0f4a-4597-894f-77f0667ba254.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76222bd3-2289-493f-a2ff-d89711905144.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76222bd3-2289-493f-a2ff-d89711905144.lance deleted file mode 100644 index f194bf708..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76222bd3-2289-493f-a2ff-d89711905144.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7622d91d-d362-45d9-b9a4-ceee60ae3881.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7622d91d-d362-45d9-b9a4-ceee60ae3881.lance deleted file mode 100644 index 0486390a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7622d91d-d362-45d9-b9a4-ceee60ae3881.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76397bdf-3946-4828-bd20-5f78340d70a5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76397bdf-3946-4828-bd20-5f78340d70a5.lance deleted file mode 100644 index 27ddd3479..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76397bdf-3946-4828-bd20-5f78340d70a5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7640b921-ce7a-4509-aa6f-be84a89583e8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7640b921-ce7a-4509-aa6f-be84a89583e8.lance deleted file mode 100644 index 34fad0e6f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7640b921-ce7a-4509-aa6f-be84a89583e8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/765860ca-2808-40d3-a654-3e491f49bfd2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/765860ca-2808-40d3-a654-3e491f49bfd2.lance deleted file mode 100644 index 293755969..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/765860ca-2808-40d3-a654-3e491f49bfd2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/767d245e-b742-4616-ad74-206c28f5217a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/767d245e-b742-4616-ad74-206c28f5217a.lance deleted file mode 100644 index 0382de189..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/767d245e-b742-4616-ad74-206c28f5217a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76a9a033-f556-4799-91df-8844106caf6a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76a9a033-f556-4799-91df-8844106caf6a.lance deleted file mode 100644 index 998be30bf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76a9a033-f556-4799-91df-8844106caf6a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76b70fae-82ca-4e95-bd05-782e6bab2470.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76b70fae-82ca-4e95-bd05-782e6bab2470.lance deleted file mode 100644 index 11ebd3f69..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76b70fae-82ca-4e95-bd05-782e6bab2470.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76c3ba1d-1e7c-467a-87b2-3e3ed5ca53ed.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76c3ba1d-1e7c-467a-87b2-3e3ed5ca53ed.lance deleted file mode 100644 index 02796aa7b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76c3ba1d-1e7c-467a-87b2-3e3ed5ca53ed.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76c7ae6f-12e0-4d64-98d1-4cc8b1be05be.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76c7ae6f-12e0-4d64-98d1-4cc8b1be05be.lance deleted file mode 100644 index 4021e2612..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/76c7ae6f-12e0-4d64-98d1-4cc8b1be05be.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/770e8779-016d-4bcd-8bb8-ac1b9c4bb40b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/770e8779-016d-4bcd-8bb8-ac1b9c4bb40b.lance deleted file mode 100644 index bf14f78c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/770e8779-016d-4bcd-8bb8-ac1b9c4bb40b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7711e625-d9d6-4bdb-bb41-484426ad9bd9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7711e625-d9d6-4bdb-bb41-484426ad9bd9.lance deleted file mode 100644 index ba9452387..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7711e625-d9d6-4bdb-bb41-484426ad9bd9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77227118-185d-4d41-8e72-bd64ed7e3a7a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77227118-185d-4d41-8e72-bd64ed7e3a7a.lance deleted file mode 100644 index 0feaf31a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77227118-185d-4d41-8e72-bd64ed7e3a7a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7729b4b3-8ea7-409e-808b-9b533de6e8ee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7729b4b3-8ea7-409e-808b-9b533de6e8ee.lance deleted file mode 100644 index 8f1c98595..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7729b4b3-8ea7-409e-808b-9b533de6e8ee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/774b6579-9ac3-43ba-95be-0c7507034552.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/774b6579-9ac3-43ba-95be-0c7507034552.lance deleted file mode 100644 index 2b3d34a0c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/774b6579-9ac3-43ba-95be-0c7507034552.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7767a138-b1f7-4877-920b-a9cc26379f79.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7767a138-b1f7-4877-920b-a9cc26379f79.lance deleted file mode 100644 index 5f64107b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7767a138-b1f7-4877-920b-a9cc26379f79.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7774b83b-ffb1-49d2-85c3-6536acd350d5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7774b83b-ffb1-49d2-85c3-6536acd350d5.lance deleted file mode 100644 index 5f65e4eb5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7774b83b-ffb1-49d2-85c3-6536acd350d5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77765141-cdaf-4c54-945b-d2fc42a6563d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77765141-cdaf-4c54-945b-d2fc42a6563d.lance deleted file mode 100644 index 0708a52a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77765141-cdaf-4c54-945b-d2fc42a6563d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/778e738b-94ee-40a3-85c9-e539c41f96f0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/778e738b-94ee-40a3-85c9-e539c41f96f0.lance deleted file mode 100644 index 4eb2c1452..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/778e738b-94ee-40a3-85c9-e539c41f96f0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77e6b10e-e5b3-4683-b455-74760b885d83.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77e6b10e-e5b3-4683-b455-74760b885d83.lance deleted file mode 100644 index 52c685d76..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77e6b10e-e5b3-4683-b455-74760b885d83.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77f17065-7055-459b-8576-30d3b4e7a5de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77f17065-7055-459b-8576-30d3b4e7a5de.lance deleted file mode 100644 index c9f581169..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77f17065-7055-459b-8576-30d3b4e7a5de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77f1f0ea-2c81-4cfb-b73d-c002240894e8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77f1f0ea-2c81-4cfb-b73d-c002240894e8.lance deleted file mode 100644 index 3e5b2602c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77f1f0ea-2c81-4cfb-b73d-c002240894e8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77f38581-2e77-4625-96b7-de097d9578f3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77f38581-2e77-4625-96b7-de097d9578f3.lance deleted file mode 100644 index 6648eceb8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/77f38581-2e77-4625-96b7-de097d9578f3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78055ed1-601d-4fac-8bb0-1c0a25c0bfd6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78055ed1-601d-4fac-8bb0-1c0a25c0bfd6.lance deleted file mode 100644 index 4561b33cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78055ed1-601d-4fac-8bb0-1c0a25c0bfd6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78108760-46b2-4ef5-a23f-328f146b4c11.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78108760-46b2-4ef5-a23f-328f146b4c11.lance deleted file mode 100644 index b9482e0b6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78108760-46b2-4ef5-a23f-328f146b4c11.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78119061-56d0-47d6-88b0-9c8d916af14f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78119061-56d0-47d6-88b0-9c8d916af14f.lance deleted file mode 100644 index 2bb0e4e67..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78119061-56d0-47d6-88b0-9c8d916af14f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/782f0fcc-ca2e-4779-b1e7-d7de2ace761c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/782f0fcc-ca2e-4779-b1e7-d7de2ace761c.lance deleted file mode 100644 index 28fedf7f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/782f0fcc-ca2e-4779-b1e7-d7de2ace761c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7875af29-7225-4ed9-b3cf-8853946bfccd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7875af29-7225-4ed9-b3cf-8853946bfccd.lance deleted file mode 100644 index d85f2d864..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7875af29-7225-4ed9-b3cf-8853946bfccd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78974b20-661f-44d0-9eb0-b7afde613f10.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78974b20-661f-44d0-9eb0-b7afde613f10.lance deleted file mode 100644 index 123d831af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78974b20-661f-44d0-9eb0-b7afde613f10.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/789e2dc2-70c0-4367-be7e-19e06a282d31.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/789e2dc2-70c0-4367-be7e-19e06a282d31.lance deleted file mode 100644 index e8f9a8265..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/789e2dc2-70c0-4367-be7e-19e06a282d31.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78af9362-6290-4a1f-9033-82f139ae83c1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78af9362-6290-4a1f-9033-82f139ae83c1.lance deleted file mode 100644 index 477da7657..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78af9362-6290-4a1f-9033-82f139ae83c1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78b83087-dc98-4013-b37c-7e2e3cfd291c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78b83087-dc98-4013-b37c-7e2e3cfd291c.lance deleted file mode 100644 index 779df438f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78b83087-dc98-4013-b37c-7e2e3cfd291c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78c4b502-34c0-490d-8265-fa68e2e2d304.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78c4b502-34c0-490d-8265-fa68e2e2d304.lance deleted file mode 100644 index 219cbfda2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78c4b502-34c0-490d-8265-fa68e2e2d304.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78cb1b3b-9660-4c9c-8308-bd6014e41533.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78cb1b3b-9660-4c9c-8308-bd6014e41533.lance deleted file mode 100644 index 2e40b8246..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78cb1b3b-9660-4c9c-8308-bd6014e41533.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78e1a668-9bbc-4b0e-a88a-71b322db5ea0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78e1a668-9bbc-4b0e-a88a-71b322db5ea0.lance deleted file mode 100644 index 1992d902f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78e1a668-9bbc-4b0e-a88a-71b322db5ea0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78f6e92b-cc36-44eb-996a-dbc583ba73d6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78f6e92b-cc36-44eb-996a-dbc583ba73d6.lance deleted file mode 100644 index 418b05051..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/78f6e92b-cc36-44eb-996a-dbc583ba73d6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7900248c-421f-48a1-8933-01dcaa44d00f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7900248c-421f-48a1-8933-01dcaa44d00f.lance deleted file mode 100644 index 2acb0d6f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7900248c-421f-48a1-8933-01dcaa44d00f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/790d59a6-5a17-4e23-b86f-0b68bd363f5b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/790d59a6-5a17-4e23-b86f-0b68bd363f5b.lance deleted file mode 100644 index ac3c0e7fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/790d59a6-5a17-4e23-b86f-0b68bd363f5b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7935effc-3f0f-4214-bd9c-bf72e346e227.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7935effc-3f0f-4214-bd9c-bf72e346e227.lance deleted file mode 100644 index 36d0a3d45..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7935effc-3f0f-4214-bd9c-bf72e346e227.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/79487af3-1827-45cd-a07c-d7ed02e5e15b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/79487af3-1827-45cd-a07c-d7ed02e5e15b.lance deleted file mode 100644 index de74f4802..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/79487af3-1827-45cd-a07c-d7ed02e5e15b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/796dc946-8d97-40ca-ad39-1e15c64e818b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/796dc946-8d97-40ca-ad39-1e15c64e818b.lance deleted file mode 100644 index 4224d0537..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/796dc946-8d97-40ca-ad39-1e15c64e818b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/796fa13e-72e9-4dfa-9d76-38a86edfd42b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/796fa13e-72e9-4dfa-9d76-38a86edfd42b.lance deleted file mode 100644 index 02737f2c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/796fa13e-72e9-4dfa-9d76-38a86edfd42b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/79c68fab-36d7-46a3-bace-0b4fe8219a36.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/79c68fab-36d7-46a3-bace-0b4fe8219a36.lance deleted file mode 100644 index f204556e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/79c68fab-36d7-46a3-bace-0b4fe8219a36.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/79dc0512-362e-420e-b66f-fa7337d1188e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/79dc0512-362e-420e-b66f-fa7337d1188e.lance deleted file mode 100644 index 7f0a20e3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/79dc0512-362e-420e-b66f-fa7337d1188e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/79e04293-3476-4ec4-9e7c-10c5eaf14c19.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/79e04293-3476-4ec4-9e7c-10c5eaf14c19.lance deleted file mode 100644 index 7b377f527..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/79e04293-3476-4ec4-9e7c-10c5eaf14c19.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/79e1c066-e87b-4bda-ae63-b0afb56d6250.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/79e1c066-e87b-4bda-ae63-b0afb56d6250.lance deleted file mode 100644 index 992c35fa8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/79e1c066-e87b-4bda-ae63-b0afb56d6250.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7a45bc1d-8326-46c5-a77d-298a0065725b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7a45bc1d-8326-46c5-a77d-298a0065725b.lance deleted file mode 100644 index 533620a3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7a45bc1d-8326-46c5-a77d-298a0065725b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7a477a0f-eae7-4d31-b76e-219e686d439b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7a477a0f-eae7-4d31-b76e-219e686d439b.lance deleted file mode 100644 index 80f3bce39..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7a477a0f-eae7-4d31-b76e-219e686d439b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7a54ffaf-2523-4931-b7ae-6bd28276c6cb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7a54ffaf-2523-4931-b7ae-6bd28276c6cb.lance deleted file mode 100644 index ae2f263ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7a54ffaf-2523-4931-b7ae-6bd28276c6cb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7a801f22-9f7a-4ab7-b552-74369787590f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7a801f22-9f7a-4ab7-b552-74369787590f.lance deleted file mode 100644 index 213ef8d62..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7a801f22-9f7a-4ab7-b552-74369787590f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7a88ab81-d23c-4cb7-95a2-5feedd6c0c34.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7a88ab81-d23c-4cb7-95a2-5feedd6c0c34.lance deleted file mode 100644 index 2a13d8404..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7a88ab81-d23c-4cb7-95a2-5feedd6c0c34.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ad623ee-76ab-4afe-a0b7-8f0c05077ae9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ad623ee-76ab-4afe-a0b7-8f0c05077ae9.lance deleted file mode 100644 index 7890c8e4e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ad623ee-76ab-4afe-a0b7-8f0c05077ae9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ae7b982-12d9-4750-af54-49821c867cfb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ae7b982-12d9-4750-af54-49821c867cfb.lance deleted file mode 100644 index a3cb4bbdf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ae7b982-12d9-4750-af54-49821c867cfb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7af4ffa8-0bac-426a-b3c5-99ab55007899.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7af4ffa8-0bac-426a-b3c5-99ab55007899.lance deleted file mode 100644 index 548072355..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7af4ffa8-0bac-426a-b3c5-99ab55007899.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7b285cb0-9dc7-4af5-aca7-e690b7f5acc2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7b285cb0-9dc7-4af5-aca7-e690b7f5acc2.lance deleted file mode 100644 index 51e6462fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7b285cb0-9dc7-4af5-aca7-e690b7f5acc2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7b780145-c578-4bf7-8fdb-8755cb3eaca6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7b780145-c578-4bf7-8fdb-8755cb3eaca6.lance deleted file mode 100644 index f96326cf0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7b780145-c578-4bf7-8fdb-8755cb3eaca6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ba3544a-cc22-4cab-8647-fca85d41c12d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ba3544a-cc22-4cab-8647-fca85d41c12d.lance deleted file mode 100644 index 61a639e06..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ba3544a-cc22-4cab-8647-fca85d41c12d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7bb00fc9-5887-41f6-9549-40e0bd60489a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7bb00fc9-5887-41f6-9549-40e0bd60489a.lance deleted file mode 100644 index e47d7ad5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7bb00fc9-5887-41f6-9549-40e0bd60489a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7bb07dfb-b7e7-4f19-8cc9-6e86c1f26535.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7bb07dfb-b7e7-4f19-8cc9-6e86c1f26535.lance deleted file mode 100644 index 460b36b58..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7bb07dfb-b7e7-4f19-8cc9-6e86c1f26535.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7bb394a5-1348-412b-8a4e-3a17664f67f2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7bb394a5-1348-412b-8a4e-3a17664f67f2.lance deleted file mode 100644 index 0527cb57c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7bb394a5-1348-412b-8a4e-3a17664f67f2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7bceabdd-ed80-4090-8afb-e52bba431d86.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7bceabdd-ed80-4090-8afb-e52bba431d86.lance deleted file mode 100644 index cb455b570..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7bceabdd-ed80-4090-8afb-e52bba431d86.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7bea5eb5-7e7b-451d-ae00-7be1cf359c08.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7bea5eb5-7e7b-451d-ae00-7be1cf359c08.lance deleted file mode 100644 index b078a0305..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7bea5eb5-7e7b-451d-ae00-7be1cf359c08.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7c50e1b0-e37b-485c-afb2-a42ad952a719.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7c50e1b0-e37b-485c-afb2-a42ad952a719.lance deleted file mode 100644 index 0a74b0628..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7c50e1b0-e37b-485c-afb2-a42ad952a719.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7c70ceaa-60d1-4be9-bd0b-09bac7d0f3ba.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7c70ceaa-60d1-4be9-bd0b-09bac7d0f3ba.lance deleted file mode 100644 index 28954cf73..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7c70ceaa-60d1-4be9-bd0b-09bac7d0f3ba.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7c7a30dc-b6b9-45e5-a62f-9352e9daee2d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7c7a30dc-b6b9-45e5-a62f-9352e9daee2d.lance deleted file mode 100644 index 62a92f209..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7c7a30dc-b6b9-45e5-a62f-9352e9daee2d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7c9606c2-6e88-44fc-a312-ad4e4e33e5d9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7c9606c2-6e88-44fc-a312-ad4e4e33e5d9.lance deleted file mode 100644 index ab02af6ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7c9606c2-6e88-44fc-a312-ad4e4e33e5d9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ced3831-db3c-474f-ba56-02290162a43d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ced3831-db3c-474f-ba56-02290162a43d.lance deleted file mode 100644 index 033450402..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ced3831-db3c-474f-ba56-02290162a43d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7cf668e7-7055-415f-924b-a2a9d5f883b1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7cf668e7-7055-415f-924b-a2a9d5f883b1.lance deleted file mode 100644 index 91a1ae834..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7cf668e7-7055-415f-924b-a2a9d5f883b1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d02ff30-f24f-47a1-8cfb-2f60fe2e0de6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d02ff30-f24f-47a1-8cfb-2f60fe2e0de6.lance deleted file mode 100644 index 13845744e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d02ff30-f24f-47a1-8cfb-2f60fe2e0de6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d1a1b3d-4948-4adf-aa87-3f2194296add.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d1a1b3d-4948-4adf-aa87-3f2194296add.lance deleted file mode 100644 index 110e169bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d1a1b3d-4948-4adf-aa87-3f2194296add.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d34f6bb-8823-4bd0-9bf3-03fe6bec289f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d34f6bb-8823-4bd0-9bf3-03fe6bec289f.lance deleted file mode 100644 index eddf9e558..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d34f6bb-8823-4bd0-9bf3-03fe6bec289f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d476dd8-82fd-413f-903c-d6806a50207a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d476dd8-82fd-413f-903c-d6806a50207a.lance deleted file mode 100644 index 79c05484f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d476dd8-82fd-413f-903c-d6806a50207a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d49cd20-caa2-4848-9c9b-0e4b75aafd02.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d49cd20-caa2-4848-9c9b-0e4b75aafd02.lance deleted file mode 100644 index 53e27e1d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d49cd20-caa2-4848-9c9b-0e4b75aafd02.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d4f07c4-6112-4b77-8ea2-e6a52287a0e0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d4f07c4-6112-4b77-8ea2-e6a52287a0e0.lance deleted file mode 100644 index 9eb7dff4c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d4f07c4-6112-4b77-8ea2-e6a52287a0e0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d57495d-cfb8-4ace-8d08-bc3d65851f24.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d57495d-cfb8-4ace-8d08-bc3d65851f24.lance deleted file mode 100644 index 7646a5f97..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d57495d-cfb8-4ace-8d08-bc3d65851f24.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d8337b6-abad-4a82-9cba-c36331134e2a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d8337b6-abad-4a82-9cba-c36331134e2a.lance deleted file mode 100644 index 5c9ca4f57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7d8337b6-abad-4a82-9cba-c36331134e2a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7da7d847-c375-416c-b9ec-c4454b943689.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7da7d847-c375-416c-b9ec-c4454b943689.lance deleted file mode 100644 index 3158d62a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7da7d847-c375-416c-b9ec-c4454b943689.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7dade2e3-8bf8-45a4-822d-a414f86a8ca9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7dade2e3-8bf8-45a4-822d-a414f86a8ca9.lance deleted file mode 100644 index 68ede148b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7dade2e3-8bf8-45a4-822d-a414f86a8ca9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7dfa6117-57e5-4a47-9ef9-782e67d5291e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7dfa6117-57e5-4a47-9ef9-782e67d5291e.lance deleted file mode 100644 index 96af7005d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7dfa6117-57e5-4a47-9ef9-782e67d5291e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e102fae-11ef-441c-be0a-5bcbedcd495b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e102fae-11ef-441c-be0a-5bcbedcd495b.lance deleted file mode 100644 index 4c2ee3d02..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e102fae-11ef-441c-be0a-5bcbedcd495b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e3e2107-4eae-4700-a21a-b963e991a9ec.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e3e2107-4eae-4700-a21a-b963e991a9ec.lance deleted file mode 100644 index 5ce0b4e07..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e3e2107-4eae-4700-a21a-b963e991a9ec.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e4396fa-7e0a-4e28-b7ed-ed79c4e062a9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e4396fa-7e0a-4e28-b7ed-ed79c4e062a9.lance deleted file mode 100644 index c475e1091..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e4396fa-7e0a-4e28-b7ed-ed79c4e062a9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e6c52ac-03aa-4bae-a9a2-d2f0f9924269.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e6c52ac-03aa-4bae-a9a2-d2f0f9924269.lance deleted file mode 100644 index 126b7ea9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e6c52ac-03aa-4bae-a9a2-d2f0f9924269.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e7230c2-7773-4488-844b-921e69eb722c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e7230c2-7773-4488-844b-921e69eb722c.lance deleted file mode 100644 index bf3c66ea7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e7230c2-7773-4488-844b-921e69eb722c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e91e731-6ff4-4e1e-a397-b27223db338a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e91e731-6ff4-4e1e-a397-b27223db338a.lance deleted file mode 100644 index addcf554b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e91e731-6ff4-4e1e-a397-b27223db338a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e960970-4d01-4852-bd45-4a792f29f12a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e960970-4d01-4852-bd45-4a792f29f12a.lance deleted file mode 100644 index c6613352c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7e960970-4d01-4852-bd45-4a792f29f12a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7eaf58f2-98af-455b-ae25-c09a44e2bbfd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7eaf58f2-98af-455b-ae25-c09a44e2bbfd.lance deleted file mode 100644 index e91a6c40f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7eaf58f2-98af-455b-ae25-c09a44e2bbfd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ebc5733-8a52-4184-8a97-c7fc647fd4ad.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ebc5733-8a52-4184-8a97-c7fc647fd4ad.lance deleted file mode 100644 index 07dee8f69..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ebc5733-8a52-4184-8a97-c7fc647fd4ad.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7f02c4ec-4446-494a-8806-8d82ab612bba.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7f02c4ec-4446-494a-8806-8d82ab612bba.lance deleted file mode 100644 index 5a75bb8d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7f02c4ec-4446-494a-8806-8d82ab612bba.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7f1c0867-ca2b-4c4d-856a-70702cde1d29.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7f1c0867-ca2b-4c4d-856a-70702cde1d29.lance deleted file mode 100644 index 3596d5b6f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7f1c0867-ca2b-4c4d-856a-70702cde1d29.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7f731c36-9d9a-4bd8-9c31-69aa2674a767.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7f731c36-9d9a-4bd8-9c31-69aa2674a767.lance deleted file mode 100644 index fbd17f778..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7f731c36-9d9a-4bd8-9c31-69aa2674a767.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7fb1edaa-82fd-49ff-94bd-1a5cfe19771d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7fb1edaa-82fd-49ff-94bd-1a5cfe19771d.lance deleted file mode 100644 index 6bdc2496f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7fb1edaa-82fd-49ff-94bd-1a5cfe19771d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7fe85737-2ba0-446a-9063-0a1789a4787b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7fe85737-2ba0-446a-9063-0a1789a4787b.lance deleted file mode 100644 index b4df05d8a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7fe85737-2ba0-446a-9063-0a1789a4787b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ff028f1-c6de-4663-aebe-58ca6bb4a6c2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ff028f1-c6de-4663-aebe-58ca6bb4a6c2.lance deleted file mode 100644 index cbe398466..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/7ff028f1-c6de-4663-aebe-58ca6bb4a6c2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/801f907b-b550-4316-be38-1e529a3ed526.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/801f907b-b550-4316-be38-1e529a3ed526.lance deleted file mode 100644 index 10dc35f42..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/801f907b-b550-4316-be38-1e529a3ed526.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8025430b-7ba8-4c3e-807e-53cbb3a145eb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8025430b-7ba8-4c3e-807e-53cbb3a145eb.lance deleted file mode 100644 index dfe4a7e12..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8025430b-7ba8-4c3e-807e-53cbb3a145eb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/803202fb-150a-4c38-8512-15e6a97237b4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/803202fb-150a-4c38-8512-15e6a97237b4.lance deleted file mode 100644 index 0972f66ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/803202fb-150a-4c38-8512-15e6a97237b4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/804cd15c-2e8c-4605-a5c8-eb074c6a4949.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/804cd15c-2e8c-4605-a5c8-eb074c6a4949.lance deleted file mode 100644 index 74e0c1b3b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/804cd15c-2e8c-4605-a5c8-eb074c6a4949.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8066f7ab-7c2c-4292-bce2-2dc23ebfee25.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8066f7ab-7c2c-4292-bce2-2dc23ebfee25.lance deleted file mode 100644 index 034ec51e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8066f7ab-7c2c-4292-bce2-2dc23ebfee25.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/808a5db1-5c00-47ef-8bf3-c7c8640ccf52.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/808a5db1-5c00-47ef-8bf3-c7c8640ccf52.lance deleted file mode 100644 index ad59d44e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/808a5db1-5c00-47ef-8bf3-c7c8640ccf52.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8098de3e-bacd-4365-9724-d3e244b4ea2f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8098de3e-bacd-4365-9724-d3e244b4ea2f.lance deleted file mode 100644 index f9601c0a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8098de3e-bacd-4365-9724-d3e244b4ea2f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/80a7d72b-ab9f-4c92-8245-417084c5a350.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/80a7d72b-ab9f-4c92-8245-417084c5a350.lance deleted file mode 100644 index 912cb92c3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/80a7d72b-ab9f-4c92-8245-417084c5a350.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/80af17f3-aab5-45f2-ab1c-1eecfae26d55.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/80af17f3-aab5-45f2-ab1c-1eecfae26d55.lance deleted file mode 100644 index da486d9ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/80af17f3-aab5-45f2-ab1c-1eecfae26d55.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/80b293a6-566d-49df-b654-3b53ea78be6e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/80b293a6-566d-49df-b654-3b53ea78be6e.lance deleted file mode 100644 index d2ba317cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/80b293a6-566d-49df-b654-3b53ea78be6e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/80c2d6e0-ee8c-41a2-957d-b92a0e2d611d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/80c2d6e0-ee8c-41a2-957d-b92a0e2d611d.lance deleted file mode 100644 index 249fd4d34..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/80c2d6e0-ee8c-41a2-957d-b92a0e2d611d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/80d37b92-dc13-4fd2-a908-fee454abc1ea.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/80d37b92-dc13-4fd2-a908-fee454abc1ea.lance deleted file mode 100644 index d7734356c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/80d37b92-dc13-4fd2-a908-fee454abc1ea.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81312d0a-c885-4af3-8bf1-36615eb5bedd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81312d0a-c885-4af3-8bf1-36615eb5bedd.lance deleted file mode 100644 index a2fc06a1c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81312d0a-c885-4af3-8bf1-36615eb5bedd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/814ed16d-812f-4541-b8af-441fed44aa6b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/814ed16d-812f-4541-b8af-441fed44aa6b.lance deleted file mode 100644 index 6d0f4691a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/814ed16d-812f-4541-b8af-441fed44aa6b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8151225d-2ad8-43bd-b141-7ee77dd41bb7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8151225d-2ad8-43bd-b141-7ee77dd41bb7.lance deleted file mode 100644 index d74044937..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8151225d-2ad8-43bd-b141-7ee77dd41bb7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81556f70-4193-4cf3-92b8-e08efdd80320.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81556f70-4193-4cf3-92b8-e08efdd80320.lance deleted file mode 100644 index 7e21cfca6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81556f70-4193-4cf3-92b8-e08efdd80320.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8157ee41-8aa1-4d08-894e-86c1a64b7e41.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8157ee41-8aa1-4d08-894e-86c1a64b7e41.lance deleted file mode 100644 index 11057ab62..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8157ee41-8aa1-4d08-894e-86c1a64b7e41.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81a8ceaf-775a-4464-bece-db425078f571.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81a8ceaf-775a-4464-bece-db425078f571.lance deleted file mode 100644 index eff392495..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81a8ceaf-775a-4464-bece-db425078f571.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81ac07bf-35c7-4164-a113-75adf2e95a63.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81ac07bf-35c7-4164-a113-75adf2e95a63.lance deleted file mode 100644 index f3a58f7db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81ac07bf-35c7-4164-a113-75adf2e95a63.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81aed116-851b-4854-a8cd-5f7dba50e865.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81aed116-851b-4854-a8cd-5f7dba50e865.lance deleted file mode 100644 index 0deb460ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81aed116-851b-4854-a8cd-5f7dba50e865.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81d30671-acfc-4678-a726-04792c63d82a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81d30671-acfc-4678-a726-04792c63d82a.lance deleted file mode 100644 index b4e98e447..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/81d30671-acfc-4678-a726-04792c63d82a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/823ad2df-ce12-422e-8a8e-c7a24900b7be.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/823ad2df-ce12-422e-8a8e-c7a24900b7be.lance deleted file mode 100644 index 92f5072fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/823ad2df-ce12-422e-8a8e-c7a24900b7be.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8241b114-bda9-4c4b-af6d-2f9ebe42fb52.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8241b114-bda9-4c4b-af6d-2f9ebe42fb52.lance deleted file mode 100644 index 251365413..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8241b114-bda9-4c4b-af6d-2f9ebe42fb52.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8241b267-0eb4-4f9c-8c60-6db1d2dd1a28.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8241b267-0eb4-4f9c-8c60-6db1d2dd1a28.lance deleted file mode 100644 index e38350752..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8241b267-0eb4-4f9c-8c60-6db1d2dd1a28.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/825e3905-6fa6-4402-9494-b07437a4779e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/825e3905-6fa6-4402-9494-b07437a4779e.lance deleted file mode 100644 index 71055c986..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/825e3905-6fa6-4402-9494-b07437a4779e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82666e48-7e64-4f22-8849-72e8fece8dd3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82666e48-7e64-4f22-8849-72e8fece8dd3.lance deleted file mode 100644 index 49bdf7beb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82666e48-7e64-4f22-8849-72e8fece8dd3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/826e934e-cd39-4366-90e9-ec79952b5a45.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/826e934e-cd39-4366-90e9-ec79952b5a45.lance deleted file mode 100644 index 9bf0d1930..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/826e934e-cd39-4366-90e9-ec79952b5a45.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82735e06-c3f0-4e9f-85c9-17ec89f920ee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82735e06-c3f0-4e9f-85c9-17ec89f920ee.lance deleted file mode 100644 index 41655c449..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82735e06-c3f0-4e9f-85c9-17ec89f920ee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82740747-6c03-43b3-b561-e05b9171a152.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82740747-6c03-43b3-b561-e05b9171a152.lance deleted file mode 100644 index 0611f4d75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82740747-6c03-43b3-b561-e05b9171a152.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82817df9-a661-47e9-9c8b-1ee6916e8361.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82817df9-a661-47e9-9c8b-1ee6916e8361.lance deleted file mode 100644 index 5a523ac09..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82817df9-a661-47e9-9c8b-1ee6916e8361.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/828a80f0-1e2e-4142-8ba3-2966b5fbd4b2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/828a80f0-1e2e-4142-8ba3-2966b5fbd4b2.lance deleted file mode 100644 index dc271fd37..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/828a80f0-1e2e-4142-8ba3-2966b5fbd4b2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/828d53c6-d94a-4ae1-945b-7734ea51089d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/828d53c6-d94a-4ae1-945b-7734ea51089d.lance deleted file mode 100644 index 15758e306..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/828d53c6-d94a-4ae1-945b-7734ea51089d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/829bc0a1-4ca1-4237-905a-0130ce99a117.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/829bc0a1-4ca1-4237-905a-0130ce99a117.lance deleted file mode 100644 index 306b593a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/829bc0a1-4ca1-4237-905a-0130ce99a117.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82a19594-b37a-483f-8e25-9b7c47661444.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82a19594-b37a-483f-8e25-9b7c47661444.lance deleted file mode 100644 index 15f0be2aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82a19594-b37a-483f-8e25-9b7c47661444.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82a8feb3-12e7-46ef-9f77-2d2453950241.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82a8feb3-12e7-46ef-9f77-2d2453950241.lance deleted file mode 100644 index a1d8bbd9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82a8feb3-12e7-46ef-9f77-2d2453950241.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82b691e8-176b-4a15-a114-95fab756ee72.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82b691e8-176b-4a15-a114-95fab756ee72.lance deleted file mode 100644 index db4d7cec7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82b691e8-176b-4a15-a114-95fab756ee72.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82c76877-6c56-4c82-af62-fc5d1926f9a1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82c76877-6c56-4c82-af62-fc5d1926f9a1.lance deleted file mode 100644 index 5d7cfae79..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82c76877-6c56-4c82-af62-fc5d1926f9a1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82f4fa88-5c2f-4034-940d-f4b70b9fa50d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82f4fa88-5c2f-4034-940d-f4b70b9fa50d.lance deleted file mode 100644 index adb57d53e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82f4fa88-5c2f-4034-940d-f4b70b9fa50d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82ffbe37-159b-44b0-9caa-9dca0b5b939d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82ffbe37-159b-44b0-9caa-9dca0b5b939d.lance deleted file mode 100644 index 5977d0446..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/82ffbe37-159b-44b0-9caa-9dca0b5b939d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/830f3214-df05-4bc7-8424-0a8cb20811bb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/830f3214-df05-4bc7-8424-0a8cb20811bb.lance deleted file mode 100644 index b93a49c35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/830f3214-df05-4bc7-8424-0a8cb20811bb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8311a88b-46c7-4cf0-8a34-3a1aed8d148b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8311a88b-46c7-4cf0-8a34-3a1aed8d148b.lance deleted file mode 100644 index c96a88d3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8311a88b-46c7-4cf0-8a34-3a1aed8d148b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/83197f38-74d3-4be1-8f61-09ee0040174e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/83197f38-74d3-4be1-8f61-09ee0040174e.lance deleted file mode 100644 index d4cfd6fd6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/83197f38-74d3-4be1-8f61-09ee0040174e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/833d4b81-e5d2-4de9-8e58-e116c7d0839c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/833d4b81-e5d2-4de9-8e58-e116c7d0839c.lance deleted file mode 100644 index be0981728..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/833d4b81-e5d2-4de9-8e58-e116c7d0839c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/833f66b8-4fec-4ad5-a748-97d408d4eb0e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/833f66b8-4fec-4ad5-a748-97d408d4eb0e.lance deleted file mode 100644 index 1e3c3cc7f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/833f66b8-4fec-4ad5-a748-97d408d4eb0e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8344a76c-1a8e-4de8-b7e6-8d7d47714eb9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8344a76c-1a8e-4de8-b7e6-8d7d47714eb9.lance deleted file mode 100644 index ac6b0f388..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8344a76c-1a8e-4de8-b7e6-8d7d47714eb9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/835c2664-12b3-4084-af1c-fa9ea03286de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/835c2664-12b3-4084-af1c-fa9ea03286de.lance deleted file mode 100644 index dbc5b75a5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/835c2664-12b3-4084-af1c-fa9ea03286de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/83bb83e7-62c7-488d-92aa-5745cdf6f2d5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/83bb83e7-62c7-488d-92aa-5745cdf6f2d5.lance deleted file mode 100644 index b73ab2a4a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/83bb83e7-62c7-488d-92aa-5745cdf6f2d5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/83be2af2-e07f-4e4c-a2de-f6fc6643b8e4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/83be2af2-e07f-4e4c-a2de-f6fc6643b8e4.lance deleted file mode 100644 index b529c1d0c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/83be2af2-e07f-4e4c-a2de-f6fc6643b8e4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/83c92bec-f6da-4662-afe4-919e3754786a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/83c92bec-f6da-4662-afe4-919e3754786a.lance deleted file mode 100644 index 55589e1d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/83c92bec-f6da-4662-afe4-919e3754786a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8407747c-dc41-4d90-8ed5-cf2ec01f17d3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8407747c-dc41-4d90-8ed5-cf2ec01f17d3.lance deleted file mode 100644 index c7e11d847..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8407747c-dc41-4d90-8ed5-cf2ec01f17d3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84118e99-a50a-4529-84b8-cdb877409bc3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84118e99-a50a-4529-84b8-cdb877409bc3.lance deleted file mode 100644 index 452625507..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84118e99-a50a-4529-84b8-cdb877409bc3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/842916bc-93aa-4a79-a43f-66740a105195.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/842916bc-93aa-4a79-a43f-66740a105195.lance deleted file mode 100644 index 7fa1c0fbe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/842916bc-93aa-4a79-a43f-66740a105195.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8474cd8f-34e1-49a9-8aa8-0d9fb96d1ba5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8474cd8f-34e1-49a9-8aa8-0d9fb96d1ba5.lance deleted file mode 100644 index f9d0cda84..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8474cd8f-34e1-49a9-8aa8-0d9fb96d1ba5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/847cfa0a-2959-410e-a86e-97dbbc970115.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/847cfa0a-2959-410e-a86e-97dbbc970115.lance deleted file mode 100644 index 82ae283df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/847cfa0a-2959-410e-a86e-97dbbc970115.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84918ee6-81b6-49fa-8d96-469de868f660.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84918ee6-81b6-49fa-8d96-469de868f660.lance deleted file mode 100644 index f87558a0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84918ee6-81b6-49fa-8d96-469de868f660.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/849da72b-e2c9-4e2c-8622-6b0c4b3ee565.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/849da72b-e2c9-4e2c-8622-6b0c4b3ee565.lance deleted file mode 100644 index 5707ed778..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/849da72b-e2c9-4e2c-8622-6b0c4b3ee565.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84ab6e17-03c0-4f7e-8b2f-b6fecbbda79f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84ab6e17-03c0-4f7e-8b2f-b6fecbbda79f.lance deleted file mode 100644 index 5e42c22ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84ab6e17-03c0-4f7e-8b2f-b6fecbbda79f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84b2df92-99c3-4b07-8d18-85fad24146d1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84b2df92-99c3-4b07-8d18-85fad24146d1.lance deleted file mode 100644 index 1b87fc2d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84b2df92-99c3-4b07-8d18-85fad24146d1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84cfcd68-80d9-4fe5-9bff-7ad9df62e0df.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84cfcd68-80d9-4fe5-9bff-7ad9df62e0df.lance deleted file mode 100644 index 402dc8c9a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84cfcd68-80d9-4fe5-9bff-7ad9df62e0df.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84eaa05f-6d0b-4624-8325-b020d9cd8ec2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84eaa05f-6d0b-4624-8325-b020d9cd8ec2.lance deleted file mode 100644 index 85d4a11cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84eaa05f-6d0b-4624-8325-b020d9cd8ec2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84fa4068-97c6-483a-9e54-d829677dfabb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84fa4068-97c6-483a-9e54-d829677dfabb.lance deleted file mode 100644 index ebb9fe503..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/84fa4068-97c6-483a-9e54-d829677dfabb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/852f63a5-70de-4a77-b9d8-992992d40466.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/852f63a5-70de-4a77-b9d8-992992d40466.lance deleted file mode 100644 index 7532c1598..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/852f63a5-70de-4a77-b9d8-992992d40466.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/854b5a5e-86c3-462e-8364-3185c571665e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/854b5a5e-86c3-462e-8364-3185c571665e.lance deleted file mode 100644 index 93258ad75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/854b5a5e-86c3-462e-8364-3185c571665e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8579ee50-723c-4c8f-bbd0-26f11fbd50a0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8579ee50-723c-4c8f-bbd0-26f11fbd50a0.lance deleted file mode 100644 index 4b897510e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8579ee50-723c-4c8f-bbd0-26f11fbd50a0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/85ad3d99-6a6c-4642-8df9-26141d02c43e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/85ad3d99-6a6c-4642-8df9-26141d02c43e.lance deleted file mode 100644 index 061895e91..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/85ad3d99-6a6c-4642-8df9-26141d02c43e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/85e0d008-07f9-454c-8474-91a6e9361c65.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/85e0d008-07f9-454c-8474-91a6e9361c65.lance deleted file mode 100644 index 69c14ff58..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/85e0d008-07f9-454c-8474-91a6e9361c65.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/865660f7-70ea-4e48-8605-86cbd7ca0b59.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/865660f7-70ea-4e48-8605-86cbd7ca0b59.lance deleted file mode 100644 index d57d5ba11..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/865660f7-70ea-4e48-8605-86cbd7ca0b59.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/865e7cf7-b2d7-4f11-b5e8-7d3441536128.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/865e7cf7-b2d7-4f11-b5e8-7d3441536128.lance deleted file mode 100644 index d0ddda1ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/865e7cf7-b2d7-4f11-b5e8-7d3441536128.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/865fa476-2478-430f-b5d5-d966b2631f70.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/865fa476-2478-430f-b5d5-d966b2631f70.lance deleted file mode 100644 index 16e512a26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/865fa476-2478-430f-b5d5-d966b2631f70.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/866fb1a7-66d2-4f05-9be1-822ae401119d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/866fb1a7-66d2-4f05-9be1-822ae401119d.lance deleted file mode 100644 index df3666e83..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/866fb1a7-66d2-4f05-9be1-822ae401119d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8677ef4c-915c-4ba3-9f64-c16c16430334.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8677ef4c-915c-4ba3-9f64-c16c16430334.lance deleted file mode 100644 index d89296c7a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8677ef4c-915c-4ba3-9f64-c16c16430334.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/868fafdd-7626-4c19-bf20-6a7d9599da6d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/868fafdd-7626-4c19-bf20-6a7d9599da6d.lance deleted file mode 100644 index 339dd72dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/868fafdd-7626-4c19-bf20-6a7d9599da6d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/86d1dabc-9e3f-45c3-b523-7aa19d1ef871.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/86d1dabc-9e3f-45c3-b523-7aa19d1ef871.lance deleted file mode 100644 index 59db3cea7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/86d1dabc-9e3f-45c3-b523-7aa19d1ef871.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/86da717e-de96-4439-8507-9780e317658c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/86da717e-de96-4439-8507-9780e317658c.lance deleted file mode 100644 index c4c9c8ca3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/86da717e-de96-4439-8507-9780e317658c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/870e4764-6bfd-47ba-913e-98a44096f2d1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/870e4764-6bfd-47ba-913e-98a44096f2d1.lance deleted file mode 100644 index bdb24a707..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/870e4764-6bfd-47ba-913e-98a44096f2d1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/871ba833-c085-4958-927b-954e008015c4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/871ba833-c085-4958-927b-954e008015c4.lance deleted file mode 100644 index c789bfad1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/871ba833-c085-4958-927b-954e008015c4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8728fea3-9192-413e-b9b0-e0f2a8b23b27.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8728fea3-9192-413e-b9b0-e0f2a8b23b27.lance deleted file mode 100644 index cdd5b7db9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8728fea3-9192-413e-b9b0-e0f2a8b23b27.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8776e2e5-9fc7-47c8-ac0e-a0095c05e83a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8776e2e5-9fc7-47c8-ac0e-a0095c05e83a.lance deleted file mode 100644 index 373c32804..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8776e2e5-9fc7-47c8-ac0e-a0095c05e83a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8798b7f1-1370-47d8-97b4-c3851be1dca5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8798b7f1-1370-47d8-97b4-c3851be1dca5.lance deleted file mode 100644 index 501e4adf4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8798b7f1-1370-47d8-97b4-c3851be1dca5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/87a370de-c783-4005-8cc6-ea45484a073d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/87a370de-c783-4005-8cc6-ea45484a073d.lance deleted file mode 100644 index 890af2b5a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/87a370de-c783-4005-8cc6-ea45484a073d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/87de67c5-d227-47eb-9fa0-0d53d5530f85.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/87de67c5-d227-47eb-9fa0-0d53d5530f85.lance deleted file mode 100644 index 1ba028094..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/87de67c5-d227-47eb-9fa0-0d53d5530f85.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/87f00768-cd05-481d-b386-abe79d684aa3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/87f00768-cd05-481d-b386-abe79d684aa3.lance deleted file mode 100644 index b5c9bb1a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/87f00768-cd05-481d-b386-abe79d684aa3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88086590-1fc5-4f18-b49a-a6dd88916700.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88086590-1fc5-4f18-b49a-a6dd88916700.lance deleted file mode 100644 index 29e61adf5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88086590-1fc5-4f18-b49a-a6dd88916700.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/881b201c-9265-48a2-a05e-d1627092c60b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/881b201c-9265-48a2-a05e-d1627092c60b.lance deleted file mode 100644 index 1356fed97..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/881b201c-9265-48a2-a05e-d1627092c60b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/882670c0-fd2c-4bef-b73c-b5cb0e6dfdd6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/882670c0-fd2c-4bef-b73c-b5cb0e6dfdd6.lance deleted file mode 100644 index bd3123a28..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/882670c0-fd2c-4bef-b73c-b5cb0e6dfdd6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88280249-e217-4346-baa3-0dc3751bae10.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88280249-e217-4346-baa3-0dc3751bae10.lance deleted file mode 100644 index 6a2660278..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88280249-e217-4346-baa3-0dc3751bae10.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88561f7a-b695-4d5b-b5fd-07b81d3053d6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88561f7a-b695-4d5b-b5fd-07b81d3053d6.lance deleted file mode 100644 index de7bb434f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88561f7a-b695-4d5b-b5fd-07b81d3053d6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8877dcd2-c0fa-4e2f-b6f1-feae3af73092.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8877dcd2-c0fa-4e2f-b6f1-feae3af73092.lance deleted file mode 100644 index a02b01240..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8877dcd2-c0fa-4e2f-b6f1-feae3af73092.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/888a80c4-7dc1-4e3d-9b59-84d8e88df9dc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/888a80c4-7dc1-4e3d-9b59-84d8e88df9dc.lance deleted file mode 100644 index 2dd0aeeb6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/888a80c4-7dc1-4e3d-9b59-84d8e88df9dc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88d0f6c5-c570-4ede-b778-d48ff51e25db.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88d0f6c5-c570-4ede-b778-d48ff51e25db.lance deleted file mode 100644 index 4c7b0fc6e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88d0f6c5-c570-4ede-b778-d48ff51e25db.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88e36ce2-b083-4a6a-8473-2540e6280baa.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88e36ce2-b083-4a6a-8473-2540e6280baa.lance deleted file mode 100644 index 8aa7e961f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88e36ce2-b083-4a6a-8473-2540e6280baa.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88e37873-e94a-4127-bb5e-996aa22fc93e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88e37873-e94a-4127-bb5e-996aa22fc93e.lance deleted file mode 100644 index ed86fcfd6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88e37873-e94a-4127-bb5e-996aa22fc93e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88f3a5af-4fe5-49e1-8fea-9b1af98539df.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88f3a5af-4fe5-49e1-8fea-9b1af98539df.lance deleted file mode 100644 index de59f23b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/88f3a5af-4fe5-49e1-8fea-9b1af98539df.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8900450f-8f69-4fe7-adf8-4d105acef638.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8900450f-8f69-4fe7-adf8-4d105acef638.lance deleted file mode 100644 index 52125a6b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8900450f-8f69-4fe7-adf8-4d105acef638.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/891941fe-349f-41c9-a0b7-0f8b99c63d24.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/891941fe-349f-41c9-a0b7-0f8b99c63d24.lance deleted file mode 100644 index 7a34ad880..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/891941fe-349f-41c9-a0b7-0f8b99c63d24.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/891f8f72-c573-4d89-b4a5-b63713dbce0a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/891f8f72-c573-4d89-b4a5-b63713dbce0a.lance deleted file mode 100644 index 9e0c1843e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/891f8f72-c573-4d89-b4a5-b63713dbce0a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/893b8495-3b55-4016-ac76-92219f12ed1b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/893b8495-3b55-4016-ac76-92219f12ed1b.lance deleted file mode 100644 index d69095ecd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/893b8495-3b55-4016-ac76-92219f12ed1b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/894613be-bed4-4be8-8d89-588215476bbd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/894613be-bed4-4be8-8d89-588215476bbd.lance deleted file mode 100644 index 4d8c7e0e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/894613be-bed4-4be8-8d89-588215476bbd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8985b9b9-cda1-4c3b-913e-3088af1cbc4a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8985b9b9-cda1-4c3b-913e-3088af1cbc4a.lance deleted file mode 100644 index 3fa9888e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8985b9b9-cda1-4c3b-913e-3088af1cbc4a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/898fd10b-0a84-4dcf-aab7-66cc4ab51cf5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/898fd10b-0a84-4dcf-aab7-66cc4ab51cf5.lance deleted file mode 100644 index 9879aca69..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/898fd10b-0a84-4dcf-aab7-66cc4ab51cf5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89a87b26-75d8-45e3-a61a-c9c6898edb8e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89a87b26-75d8-45e3-a61a-c9c6898edb8e.lance deleted file mode 100644 index 5770fe027..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89a87b26-75d8-45e3-a61a-c9c6898edb8e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89b189cc-e664-4ede-8e2c-ecf4f8debeda.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89b189cc-e664-4ede-8e2c-ecf4f8debeda.lance deleted file mode 100644 index 0dbf105c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89b189cc-e664-4ede-8e2c-ecf4f8debeda.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89bc8b27-f2bd-4bff-88ef-f9c473a4755f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89bc8b27-f2bd-4bff-88ef-f9c473a4755f.lance deleted file mode 100644 index 0a46a1d56..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89bc8b27-f2bd-4bff-88ef-f9c473a4755f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89bce3e8-9b79-4277-8bff-5fc3deef6c14.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89bce3e8-9b79-4277-8bff-5fc3deef6c14.lance deleted file mode 100644 index 7960b5afc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89bce3e8-9b79-4277-8bff-5fc3deef6c14.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89dc67c6-e3d7-4cd5-98e3-e46f4cabea76.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89dc67c6-e3d7-4cd5-98e3-e46f4cabea76.lance deleted file mode 100644 index 261975d17..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89dc67c6-e3d7-4cd5-98e3-e46f4cabea76.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89e696a3-5484-4280-9d33-4a37ee2a73e5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89e696a3-5484-4280-9d33-4a37ee2a73e5.lance deleted file mode 100644 index 96f8651af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/89e696a3-5484-4280-9d33-4a37ee2a73e5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a0391a8-4604-4440-a04b-973b31845509.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a0391a8-4604-4440-a04b-973b31845509.lance deleted file mode 100644 index 1e3272029..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a0391a8-4604-4440-a04b-973b31845509.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a1f477e-a7e1-48fd-ab15-d44e70c1ea95.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a1f477e-a7e1-48fd-ab15-d44e70c1ea95.lance deleted file mode 100644 index 45d68a64e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a1f477e-a7e1-48fd-ab15-d44e70c1ea95.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a3057de-7774-4d40-a071-185c6fab1a59.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a3057de-7774-4d40-a071-185c6fab1a59.lance deleted file mode 100644 index 841d756a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a3057de-7774-4d40-a071-185c6fab1a59.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a48074e-b73d-448a-b823-13f6d31af63f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a48074e-b73d-448a-b823-13f6d31af63f.lance deleted file mode 100644 index 5db58f206..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a48074e-b73d-448a-b823-13f6d31af63f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a616a54-bd4d-4924-845c-ba862ca7c4c1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a616a54-bd4d-4924-845c-ba862ca7c4c1.lance deleted file mode 100644 index a7c35fe9c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a616a54-bd4d-4924-845c-ba862ca7c4c1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a66c063-c3aa-4d19-92e9-e56d07ccdc14.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a66c063-c3aa-4d19-92e9-e56d07ccdc14.lance deleted file mode 100644 index d2c9b3688..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a66c063-c3aa-4d19-92e9-e56d07ccdc14.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a77f2d2-dcc4-4110-8c9f-d2292460a32b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a77f2d2-dcc4-4110-8c9f-d2292460a32b.lance deleted file mode 100644 index afc2c6885..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8a77f2d2-dcc4-4110-8c9f-d2292460a32b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8aa98d41-a42b-4eb5-a179-16560ad54e5e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8aa98d41-a42b-4eb5-a179-16560ad54e5e.lance deleted file mode 100644 index ba1a72cf4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8aa98d41-a42b-4eb5-a179-16560ad54e5e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ac54bdc-5e1d-4192-935f-b36c19400d72.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ac54bdc-5e1d-4192-935f-b36c19400d72.lance deleted file mode 100644 index a17bf35f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ac54bdc-5e1d-4192-935f-b36c19400d72.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ae5cabb-0e75-4d57-a84a-0ffc371b0a0b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ae5cabb-0e75-4d57-a84a-0ffc371b0a0b.lance deleted file mode 100644 index d8c584eed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ae5cabb-0e75-4d57-a84a-0ffc371b0a0b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8afbd25a-2162-409d-978d-8aad95f51d4a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8afbd25a-2162-409d-978d-8aad95f51d4a.lance deleted file mode 100644 index fc62a1048..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8afbd25a-2162-409d-978d-8aad95f51d4a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8b40d00a-9374-400b-aff7-56bf6fdee912.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8b40d00a-9374-400b-aff7-56bf6fdee912.lance deleted file mode 100644 index 6c0bf1e6e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8b40d00a-9374-400b-aff7-56bf6fdee912.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8b49151f-1257-4814-9da6-d8f4b13c0d2c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8b49151f-1257-4814-9da6-d8f4b13c0d2c.lance deleted file mode 100644 index 17ddbe0d2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8b49151f-1257-4814-9da6-d8f4b13c0d2c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8b51175f-4253-423d-a396-f1d24ca89f0a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8b51175f-4253-423d-a396-f1d24ca89f0a.lance deleted file mode 100644 index 149914d8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8b51175f-4253-423d-a396-f1d24ca89f0a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8b549fd7-b6d5-4900-b57f-a8557e3305d1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8b549fd7-b6d5-4900-b57f-a8557e3305d1.lance deleted file mode 100644 index dc285cac7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8b549fd7-b6d5-4900-b57f-a8557e3305d1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8b7ff52e-2c54-4843-82c5-30de3d01762b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8b7ff52e-2c54-4843-82c5-30de3d01762b.lance deleted file mode 100644 index 1b89499b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8b7ff52e-2c54-4843-82c5-30de3d01762b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8bbbdcb4-96bf-471b-ba0c-a0e5102044ef.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8bbbdcb4-96bf-471b-ba0c-a0e5102044ef.lance deleted file mode 100644 index 6db3f6582..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8bbbdcb4-96bf-471b-ba0c-a0e5102044ef.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8bf8368e-c305-4eb8-995c-ba91617526da.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8bf8368e-c305-4eb8-995c-ba91617526da.lance deleted file mode 100644 index 057fd6f68..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8bf8368e-c305-4eb8-995c-ba91617526da.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8bfc752f-4bad-4fde-9ac0-de20a480cb64.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8bfc752f-4bad-4fde-9ac0-de20a480cb64.lance deleted file mode 100644 index a0a67ae39..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8bfc752f-4bad-4fde-9ac0-de20a480cb64.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8c06aed0-ea7e-44c5-9bda-a55095f69557.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8c06aed0-ea7e-44c5-9bda-a55095f69557.lance deleted file mode 100644 index 2b321d8da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8c06aed0-ea7e-44c5-9bda-a55095f69557.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8c2e3424-d96a-451c-85c8-487daec74f13.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8c2e3424-d96a-451c-85c8-487daec74f13.lance deleted file mode 100644 index 26a036211..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8c2e3424-d96a-451c-85c8-487daec74f13.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8c4ca554-d090-448d-a38f-b042dc63ffc4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8c4ca554-d090-448d-a38f-b042dc63ffc4.lance deleted file mode 100644 index ae9a06438..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8c4ca554-d090-448d-a38f-b042dc63ffc4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8c63ea09-0afc-4202-8979-13227fb1268b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8c63ea09-0afc-4202-8979-13227fb1268b.lance deleted file mode 100644 index 248a942cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8c63ea09-0afc-4202-8979-13227fb1268b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8caf23d1-f610-4ff7-977d-304595b6fb41.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8caf23d1-f610-4ff7-977d-304595b6fb41.lance deleted file mode 100644 index e3584923f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8caf23d1-f610-4ff7-977d-304595b6fb41.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8caf7641-d2f7-4f98-8e20-623600f7de08.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8caf7641-d2f7-4f98-8e20-623600f7de08.lance deleted file mode 100644 index dd712a949..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8caf7641-d2f7-4f98-8e20-623600f7de08.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8cce6176-54be-4fdb-bf4e-11f04a199a72.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8cce6176-54be-4fdb-bf4e-11f04a199a72.lance deleted file mode 100644 index a9c600f93..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8cce6176-54be-4fdb-bf4e-11f04a199a72.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ceba599-c14d-4009-b21c-4720bd365677.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ceba599-c14d-4009-b21c-4720bd365677.lance deleted file mode 100644 index df9307f08..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ceba599-c14d-4009-b21c-4720bd365677.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ced2867-5d1c-4f61-9b40-10ed185e0290.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ced2867-5d1c-4f61-9b40-10ed185e0290.lance deleted file mode 100644 index 4101f3c23..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ced2867-5d1c-4f61-9b40-10ed185e0290.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d1026f8-5992-4f06-a8f8-8809500d9752.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d1026f8-5992-4f06-a8f8-8809500d9752.lance deleted file mode 100644 index b43b62ec2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d1026f8-5992-4f06-a8f8-8809500d9752.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d1be091-4e40-42e5-838b-04810a458d35.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d1be091-4e40-42e5-838b-04810a458d35.lance deleted file mode 100644 index 18461cc6e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d1be091-4e40-42e5-838b-04810a458d35.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d2a44a5-92ad-41a1-9fde-9a1a8b473e3a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d2a44a5-92ad-41a1-9fde-9a1a8b473e3a.lance deleted file mode 100644 index 95750e4c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d2a44a5-92ad-41a1-9fde-9a1a8b473e3a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d366653-7880-4381-9c38-7dcbc6520bc3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d366653-7880-4381-9c38-7dcbc6520bc3.lance deleted file mode 100644 index 05eaa56fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d366653-7880-4381-9c38-7dcbc6520bc3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d3a5fba-7036-4fc4-955d-63b9a567f27d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d3a5fba-7036-4fc4-955d-63b9a567f27d.lance deleted file mode 100644 index 9b14e0394..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d3a5fba-7036-4fc4-955d-63b9a567f27d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d54a7b0-a3bd-4a6d-b802-fe315a47798a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d54a7b0-a3bd-4a6d-b802-fe315a47798a.lance deleted file mode 100644 index 9c6202746..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d54a7b0-a3bd-4a6d-b802-fe315a47798a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d6010ff-8dbe-464b-b0c7-c5f4c963ff5d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d6010ff-8dbe-464b-b0c7-c5f4c963ff5d.lance deleted file mode 100644 index 3bed19c08..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d6010ff-8dbe-464b-b0c7-c5f4c963ff5d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d62552e-748e-4014-b37c-8fe865a40374.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d62552e-748e-4014-b37c-8fe865a40374.lance deleted file mode 100644 index 1cf8ee29f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d62552e-748e-4014-b37c-8fe865a40374.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d65a011-3d6b-48c8-a486-94c95732f03a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d65a011-3d6b-48c8-a486-94c95732f03a.lance deleted file mode 100644 index 6280d4a57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d65a011-3d6b-48c8-a486-94c95732f03a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d79d1ab-8c9a-4698-ba3c-6f3e13ff0d8b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d79d1ab-8c9a-4698-ba3c-6f3e13ff0d8b.lance deleted file mode 100644 index 244fb08dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8d79d1ab-8c9a-4698-ba3c-6f3e13ff0d8b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8dbac9ea-5981-4740-ae3c-7f09ef04ae18.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8dbac9ea-5981-4740-ae3c-7f09ef04ae18.lance deleted file mode 100644 index 7e75a8b49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8dbac9ea-5981-4740-ae3c-7f09ef04ae18.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8dc68d78-9b90-495d-aed3-64f2b03455ec.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8dc68d78-9b90-495d-aed3-64f2b03455ec.lance deleted file mode 100644 index b2060b8d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8dc68d78-9b90-495d-aed3-64f2b03455ec.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8de3228f-b412-4608-bef7-7cc555af4f18.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8de3228f-b412-4608-bef7-7cc555af4f18.lance deleted file mode 100644 index 985e15a41..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8de3228f-b412-4608-bef7-7cc555af4f18.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8df143a4-dd8c-47df-9849-afbcaa58d999.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8df143a4-dd8c-47df-9849-afbcaa58d999.lance deleted file mode 100644 index ec6a621db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8df143a4-dd8c-47df-9849-afbcaa58d999.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8df6437d-4360-48e9-ac41-3a388f484d75.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8df6437d-4360-48e9-ac41-3a388f484d75.lance deleted file mode 100644 index db2646baf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8df6437d-4360-48e9-ac41-3a388f484d75.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8e3bda75-de07-4723-8d0a-5349f11a8ff8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8e3bda75-de07-4723-8d0a-5349f11a8ff8.lance deleted file mode 100644 index 6879f64fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8e3bda75-de07-4723-8d0a-5349f11a8ff8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8e82b0eb-1e07-4b85-aa39-68c3561b918a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8e82b0eb-1e07-4b85-aa39-68c3561b918a.lance deleted file mode 100644 index adc25d4b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8e82b0eb-1e07-4b85-aa39-68c3561b918a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8eb72397-899e-4859-97f4-2d1442a590b6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8eb72397-899e-4859-97f4-2d1442a590b6.lance deleted file mode 100644 index 666e8c05d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8eb72397-899e-4859-97f4-2d1442a590b6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ed03324-87c7-4c2a-b2d5-9cbcee674571.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ed03324-87c7-4c2a-b2d5-9cbcee674571.lance deleted file mode 100644 index 888b07abf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ed03324-87c7-4c2a-b2d5-9cbcee674571.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f09a980-155a-4515-a0e6-dbf7e1a9c9d5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f09a980-155a-4515-a0e6-dbf7e1a9c9d5.lance deleted file mode 100644 index bc3a03512..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f09a980-155a-4515-a0e6-dbf7e1a9c9d5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f0ab55a-3697-47f2-b2e9-b0476e4d5ec5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f0ab55a-3697-47f2-b2e9-b0476e4d5ec5.lance deleted file mode 100644 index 9c676deb2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f0ab55a-3697-47f2-b2e9-b0476e4d5ec5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f1d7071-fb76-4481-a520-bc979f30af52.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f1d7071-fb76-4481-a520-bc979f30af52.lance deleted file mode 100644 index c536b2523..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f1d7071-fb76-4481-a520-bc979f30af52.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f21f465-0020-4497-88a2-fc4bb0fd4a90.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f21f465-0020-4497-88a2-fc4bb0fd4a90.lance deleted file mode 100644 index 800ad86b0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f21f465-0020-4497-88a2-fc4bb0fd4a90.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f22848e-ab1c-482d-b60d-01f63a0ff80d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f22848e-ab1c-482d-b60d-01f63a0ff80d.lance deleted file mode 100644 index 0ddc1f6bd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f22848e-ab1c-482d-b60d-01f63a0ff80d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f446d83-8867-43c4-8f96-1ea40bcfbf5f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f446d83-8867-43c4-8f96-1ea40bcfbf5f.lance deleted file mode 100644 index 47116da02..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f446d83-8867-43c4-8f96-1ea40bcfbf5f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f457d8a-d396-4d1b-9e74-d61e59102862.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f457d8a-d396-4d1b-9e74-d61e59102862.lance deleted file mode 100644 index 97e12dc3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f457d8a-d396-4d1b-9e74-d61e59102862.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f567aed-09ae-43a2-b5a0-594655965110.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f567aed-09ae-43a2-b5a0-594655965110.lance deleted file mode 100644 index 22ef8d876..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f567aed-09ae-43a2-b5a0-594655965110.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f59417f-5f1d-4164-adee-554c09416fb1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f59417f-5f1d-4164-adee-554c09416fb1.lance deleted file mode 100644 index 0f7a61ae9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f59417f-5f1d-4164-adee-554c09416fb1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f8854ba-5bba-4a55-90e6-a2236e2955b1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f8854ba-5bba-4a55-90e6-a2236e2955b1.lance deleted file mode 100644 index df5d68edb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f8854ba-5bba-4a55-90e6-a2236e2955b1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f908237-fb67-4476-886c-d56608e8211b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f908237-fb67-4476-886c-d56608e8211b.lance deleted file mode 100644 index 055e5536e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f908237-fb67-4476-886c-d56608e8211b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f985eee-c0ed-4380-aaaa-41abd4951f2e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f985eee-c0ed-4380-aaaa-41abd4951f2e.lance deleted file mode 100644 index 2928e6146..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8f985eee-c0ed-4380-aaaa-41abd4951f2e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8faef780-141b-4fb0-83cf-8a14230c3470.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8faef780-141b-4fb0-83cf-8a14230c3470.lance deleted file mode 100644 index 8fd7b1817..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8faef780-141b-4fb0-83cf-8a14230c3470.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8fafc642-d565-47f8-a52c-319f01ea481d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8fafc642-d565-47f8-a52c-319f01ea481d.lance deleted file mode 100644 index 8eb56f012..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8fafc642-d565-47f8-a52c-319f01ea481d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8fc57af3-54ed-4c21-8dd5-6a00dbe66619.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8fc57af3-54ed-4c21-8dd5-6a00dbe66619.lance deleted file mode 100644 index 9528c3807..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8fc57af3-54ed-4c21-8dd5-6a00dbe66619.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ff4b51b-8f1a-4d11-a9a5-1dfed4f0dd02.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ff4b51b-8f1a-4d11-a9a5-1dfed4f0dd02.lance deleted file mode 100644 index 53c38e6cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/8ff4b51b-8f1a-4d11-a9a5-1dfed4f0dd02.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9012ada8-0f5a-4279-9f58-73e82363fc26.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9012ada8-0f5a-4279-9f58-73e82363fc26.lance deleted file mode 100644 index ff5442fb8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9012ada8-0f5a-4279-9f58-73e82363fc26.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/902205f3-4392-452e-8409-4bfda6ced99f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/902205f3-4392-452e-8409-4bfda6ced99f.lance deleted file mode 100644 index c701e383e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/902205f3-4392-452e-8409-4bfda6ced99f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9025a396-fded-47a9-87c4-8c8d8d17c229.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9025a396-fded-47a9-87c4-8c8d8d17c229.lance deleted file mode 100644 index ff73b749e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9025a396-fded-47a9-87c4-8c8d8d17c229.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/90733932-17a2-40a4-9240-2b0af51b3e76.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/90733932-17a2-40a4-9240-2b0af51b3e76.lance deleted file mode 100644 index bcc7c12f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/90733932-17a2-40a4-9240-2b0af51b3e76.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/90af6b65-b302-4da8-a05e-8bb5dbc3985d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/90af6b65-b302-4da8-a05e-8bb5dbc3985d.lance deleted file mode 100644 index b4efd87e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/90af6b65-b302-4da8-a05e-8bb5dbc3985d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/90bc1f38-8368-4c3f-a9a1-b9bb77e7de55.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/90bc1f38-8368-4c3f-a9a1-b9bb77e7de55.lance deleted file mode 100644 index 9a274c5f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/90bc1f38-8368-4c3f-a9a1-b9bb77e7de55.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/90e8b50f-375e-4eee-9e95-756ffc98d7fe.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/90e8b50f-375e-4eee-9e95-756ffc98d7fe.lance deleted file mode 100644 index 325c2bfde..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/90e8b50f-375e-4eee-9e95-756ffc98d7fe.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9108f6e8-5d9a-4071-bae1-e5fc60e9e03b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9108f6e8-5d9a-4071-bae1-e5fc60e9e03b.lance deleted file mode 100644 index d77551f9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9108f6e8-5d9a-4071-bae1-e5fc60e9e03b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/910b8871-b3d1-4154-811a-84d60099ac28.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/910b8871-b3d1-4154-811a-84d60099ac28.lance deleted file mode 100644 index 313ee9eb3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/910b8871-b3d1-4154-811a-84d60099ac28.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/91285f2c-b88e-4363-a840-3f413e396516.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/91285f2c-b88e-4363-a840-3f413e396516.lance deleted file mode 100644 index 1143a0e66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/91285f2c-b88e-4363-a840-3f413e396516.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/912be15c-5167-44f8-a9d9-41e97e4fe38e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/912be15c-5167-44f8-a9d9-41e97e4fe38e.lance deleted file mode 100644 index 43121c8db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/912be15c-5167-44f8-a9d9-41e97e4fe38e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/91337d7a-4a30-45e2-b5c4-b047b91944e7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/91337d7a-4a30-45e2-b5c4-b047b91944e7.lance deleted file mode 100644 index 2bd4411e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/91337d7a-4a30-45e2-b5c4-b047b91944e7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/91351fba-5730-4aca-a33a-5c5322c94f1e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/91351fba-5730-4aca-a33a-5c5322c94f1e.lance deleted file mode 100644 index 45b85e534..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/91351fba-5730-4aca-a33a-5c5322c94f1e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/914546f3-0263-429d-92b8-5740ea8e7f5e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/914546f3-0263-429d-92b8-5740ea8e7f5e.lance deleted file mode 100644 index 24294d091..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/914546f3-0263-429d-92b8-5740ea8e7f5e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/914a915a-2b3a-4995-be2c-1bf3bfb9de6b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/914a915a-2b3a-4995-be2c-1bf3bfb9de6b.lance deleted file mode 100644 index 0df4b2402..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/914a915a-2b3a-4995-be2c-1bf3bfb9de6b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9157f2e6-73be-4e6c-9711-0443ca3d80b2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9157f2e6-73be-4e6c-9711-0443ca3d80b2.lance deleted file mode 100644 index ee7300084..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9157f2e6-73be-4e6c-9711-0443ca3d80b2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/915d19ac-3099-4c2e-a2ea-8ccc6c9dd411.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/915d19ac-3099-4c2e-a2ea-8ccc6c9dd411.lance deleted file mode 100644 index 5da96f945..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/915d19ac-3099-4c2e-a2ea-8ccc6c9dd411.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/91657856-772b-4f2b-ba19-7ef3b18a35ee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/91657856-772b-4f2b-ba19-7ef3b18a35ee.lance deleted file mode 100644 index 2fcd18b65..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/91657856-772b-4f2b-ba19-7ef3b18a35ee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/917c6fbf-efbc-4cee-9517-8a226788903c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/917c6fbf-efbc-4cee-9517-8a226788903c.lance deleted file mode 100644 index f9ae9c02f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/917c6fbf-efbc-4cee-9517-8a226788903c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92307e66-a1a9-449a-b282-1b6056a1363a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92307e66-a1a9-449a-b282-1b6056a1363a.lance deleted file mode 100644 index 5cc5cdfa1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92307e66-a1a9-449a-b282-1b6056a1363a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/923c81aa-a7a6-4715-8c86-c1e0eb80e22b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/923c81aa-a7a6-4715-8c86-c1e0eb80e22b.lance deleted file mode 100644 index a073521f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/923c81aa-a7a6-4715-8c86-c1e0eb80e22b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/924b165c-ec3b-463e-8fd7-fc89469fa7d9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/924b165c-ec3b-463e-8fd7-fc89469fa7d9.lance deleted file mode 100644 index acf97e7f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/924b165c-ec3b-463e-8fd7-fc89469fa7d9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9265ac88-0b7f-4d9b-89b1-4bf45795f0f7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9265ac88-0b7f-4d9b-89b1-4bf45795f0f7.lance deleted file mode 100644 index a8c6cf649..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9265ac88-0b7f-4d9b-89b1-4bf45795f0f7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/927b05f7-0219-400f-b806-dfb1c0d283b2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/927b05f7-0219-400f-b806-dfb1c0d283b2.lance deleted file mode 100644 index c098a1bd1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/927b05f7-0219-400f-b806-dfb1c0d283b2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/928605ff-2d4e-420b-8645-35205a71740e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/928605ff-2d4e-420b-8645-35205a71740e.lance deleted file mode 100644 index 914dda224..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/928605ff-2d4e-420b-8645-35205a71740e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/928ce31c-9231-4106-a055-4816ded3e41e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/928ce31c-9231-4106-a055-4816ded3e41e.lance deleted file mode 100644 index 4f8e3835b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/928ce31c-9231-4106-a055-4816ded3e41e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92a98a76-82c6-4b81-b90e-508b79d25c85.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92a98a76-82c6-4b81-b90e-508b79d25c85.lance deleted file mode 100644 index 68d255b8f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92a98a76-82c6-4b81-b90e-508b79d25c85.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92ad5a4f-89ca-44e7-817d-174a8af23a89.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92ad5a4f-89ca-44e7-817d-174a8af23a89.lance deleted file mode 100644 index 7f8c33a0a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92ad5a4f-89ca-44e7-817d-174a8af23a89.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92bfdebf-8dc4-4591-949c-105d1ed22d0c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92bfdebf-8dc4-4591-949c-105d1ed22d0c.lance deleted file mode 100644 index 3b3a71afc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92bfdebf-8dc4-4591-949c-105d1ed22d0c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92d306fe-234c-41c5-817e-edf5425377f5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92d306fe-234c-41c5-817e-edf5425377f5.lance deleted file mode 100644 index 570bae6fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92d306fe-234c-41c5-817e-edf5425377f5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92e4e0e6-ef48-4166-9360-9f20acb362ae.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92e4e0e6-ef48-4166-9360-9f20acb362ae.lance deleted file mode 100644 index 846a640b0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/92e4e0e6-ef48-4166-9360-9f20acb362ae.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93201ea2-16a8-4567-a2cf-f9074890108c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93201ea2-16a8-4567-a2cf-f9074890108c.lance deleted file mode 100644 index a4de2a949..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93201ea2-16a8-4567-a2cf-f9074890108c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/932c43c1-224d-4c4b-aa31-06623a18b4b2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/932c43c1-224d-4c4b-aa31-06623a18b4b2.lance deleted file mode 100644 index 5f62ad543..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/932c43c1-224d-4c4b-aa31-06623a18b4b2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93398aa3-0e94-48ab-88e3-425131b7799c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93398aa3-0e94-48ab-88e3-425131b7799c.lance deleted file mode 100644 index 1db892d08..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93398aa3-0e94-48ab-88e3-425131b7799c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/933af965-d2fe-4966-8020-05cc6af93e01.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/933af965-d2fe-4966-8020-05cc6af93e01.lance deleted file mode 100644 index 8a388fcd0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/933af965-d2fe-4966-8020-05cc6af93e01.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9341e9c4-59ce-4118-b8ed-22964c97fc70.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9341e9c4-59ce-4118-b8ed-22964c97fc70.lance deleted file mode 100644 index c2c5e3719..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9341e9c4-59ce-4118-b8ed-22964c97fc70.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93423a9c-135e-43b8-8a66-28d90045d84e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93423a9c-135e-43b8-8a66-28d90045d84e.lance deleted file mode 100644 index fb5a4f65b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93423a9c-135e-43b8-8a66-28d90045d84e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/934e5579-be70-481c-a2e6-ea418d59b1f3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/934e5579-be70-481c-a2e6-ea418d59b1f3.lance deleted file mode 100644 index 282a27848..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/934e5579-be70-481c-a2e6-ea418d59b1f3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/936a42b9-bf0f-4418-8f94-79c519d5ed63.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/936a42b9-bf0f-4418-8f94-79c519d5ed63.lance deleted file mode 100644 index 724752673..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/936a42b9-bf0f-4418-8f94-79c519d5ed63.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9373101a-39ba-4dcf-8917-7ebec07b226b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9373101a-39ba-4dcf-8917-7ebec07b226b.lance deleted file mode 100644 index 0c927af0f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9373101a-39ba-4dcf-8917-7ebec07b226b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/938116bc-33ec-4750-b842-c4ae54a870f5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/938116bc-33ec-4750-b842-c4ae54a870f5.lance deleted file mode 100644 index 1cf724439..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/938116bc-33ec-4750-b842-c4ae54a870f5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9385c0b5-97ca-4844-917d-425966c5ec47.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9385c0b5-97ca-4844-917d-425966c5ec47.lance deleted file mode 100644 index 5178532d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9385c0b5-97ca-4844-917d-425966c5ec47.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/938f98d5-c821-437b-960a-1b0efc99bcc3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/938f98d5-c821-437b-960a-1b0efc99bcc3.lance deleted file mode 100644 index e6ccf2387..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/938f98d5-c821-437b-960a-1b0efc99bcc3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93a6164e-a059-485b-a999-15bd2b63ece7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93a6164e-a059-485b-a999-15bd2b63ece7.lance deleted file mode 100644 index f7ac7c2c0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93a6164e-a059-485b-a999-15bd2b63ece7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93b320cf-6193-4b63-a44a-ff5e9d8dec2c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93b320cf-6193-4b63-a44a-ff5e9d8dec2c.lance deleted file mode 100644 index 8d0144aae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93b320cf-6193-4b63-a44a-ff5e9d8dec2c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93b44fab-0ab8-4d73-80de-e327d93fa165.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93b44fab-0ab8-4d73-80de-e327d93fa165.lance deleted file mode 100644 index 5d977a1c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93b44fab-0ab8-4d73-80de-e327d93fa165.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93d185ae-a677-4327-bb4c-04374f3d43f0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93d185ae-a677-4327-bb4c-04374f3d43f0.lance deleted file mode 100644 index 19ff39748..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93d185ae-a677-4327-bb4c-04374f3d43f0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93d26988-a711-4403-9253-33b3ca8f05a1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93d26988-a711-4403-9253-33b3ca8f05a1.lance deleted file mode 100644 index ae5972640..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93d26988-a711-4403-9253-33b3ca8f05a1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93d29558-8c60-4e1c-88ac-a451e859fc78.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93d29558-8c60-4e1c-88ac-a451e859fc78.lance deleted file mode 100644 index c9d5ee240..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93d29558-8c60-4e1c-88ac-a451e859fc78.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93df6e8b-53ad-4620-beed-443c949397b6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93df6e8b-53ad-4620-beed-443c949397b6.lance deleted file mode 100644 index dee423ed7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/93df6e8b-53ad-4620-beed-443c949397b6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94067e2b-f890-461d-80e9-eef2841d33ea.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94067e2b-f890-461d-80e9-eef2841d33ea.lance deleted file mode 100644 index c4cdaacc6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94067e2b-f890-461d-80e9-eef2841d33ea.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/942a93df-0043-4215-a407-adfcb587f07e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/942a93df-0043-4215-a407-adfcb587f07e.lance deleted file mode 100644 index 3fa1417f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/942a93df-0043-4215-a407-adfcb587f07e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9431c7c1-4fef-42ed-b86d-a94d2f6f0795.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9431c7c1-4fef-42ed-b86d-a94d2f6f0795.lance deleted file mode 100644 index ce0effd52..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9431c7c1-4fef-42ed-b86d-a94d2f6f0795.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94366c43-3a81-4391-8ca4-ce63e6bf70bf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94366c43-3a81-4391-8ca4-ce63e6bf70bf.lance deleted file mode 100644 index c1ea3b15d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94366c43-3a81-4391-8ca4-ce63e6bf70bf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9437c0b2-44b4-4240-869f-95e09ccc477e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9437c0b2-44b4-4240-869f-95e09ccc477e.lance deleted file mode 100644 index 3feab2e5f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9437c0b2-44b4-4240-869f-95e09ccc477e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/943b9335-53ad-47b3-bcb6-4857feb18c64.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/943b9335-53ad-47b3-bcb6-4857feb18c64.lance deleted file mode 100644 index f64ee1a07..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/943b9335-53ad-47b3-bcb6-4857feb18c64.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9499a3e0-ff49-4a51-b32d-7ebc51a3de95.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9499a3e0-ff49-4a51-b32d-7ebc51a3de95.lance deleted file mode 100644 index 4af22a4e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9499a3e0-ff49-4a51-b32d-7ebc51a3de95.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94b5bbc2-c4ef-4902-aa57-53b5fa9a04f1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94b5bbc2-c4ef-4902-aa57-53b5fa9a04f1.lance deleted file mode 100644 index 0b9e59208..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94b5bbc2-c4ef-4902-aa57-53b5fa9a04f1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94c09782-18b7-4970-b233-a2b54fccdfa6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94c09782-18b7-4970-b233-a2b54fccdfa6.lance deleted file mode 100644 index 00c2e6a65..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94c09782-18b7-4970-b233-a2b54fccdfa6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94ce5c62-760b-4849-902d-bc8169061012.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94ce5c62-760b-4849-902d-bc8169061012.lance deleted file mode 100644 index ed15ef498..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94ce5c62-760b-4849-902d-bc8169061012.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94d1abb8-6fd6-43a9-9d6d-4a96512f6e17.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94d1abb8-6fd6-43a9-9d6d-4a96512f6e17.lance deleted file mode 100644 index ca867df11..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/94d1abb8-6fd6-43a9-9d6d-4a96512f6e17.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9596c2da-fc6d-4f09-bb57-0dc52d106f73.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9596c2da-fc6d-4f09-bb57-0dc52d106f73.lance deleted file mode 100644 index 1897c31ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9596c2da-fc6d-4f09-bb57-0dc52d106f73.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/95b082b0-9059-49dd-86fc-0e5b004b97e9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/95b082b0-9059-49dd-86fc-0e5b004b97e9.lance deleted file mode 100644 index 8fe0e4814..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/95b082b0-9059-49dd-86fc-0e5b004b97e9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/95c6be82-9e92-43e0-9e2b-22ee8149150a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/95c6be82-9e92-43e0-9e2b-22ee8149150a.lance deleted file mode 100644 index 8da04a8e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/95c6be82-9e92-43e0-9e2b-22ee8149150a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/95cac765-4385-4df2-8582-2d84ee145b52.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/95cac765-4385-4df2-8582-2d84ee145b52.lance deleted file mode 100644 index 0c25cb597..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/95cac765-4385-4df2-8582-2d84ee145b52.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/95d216d4-297e-4705-80a3-91d6297e6f7a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/95d216d4-297e-4705-80a3-91d6297e6f7a.lance deleted file mode 100644 index 9cf215afa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/95d216d4-297e-4705-80a3-91d6297e6f7a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/95f66644-4fb1-4dd7-871d-cdddf918b76c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/95f66644-4fb1-4dd7-871d-cdddf918b76c.lance deleted file mode 100644 index f201673bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/95f66644-4fb1-4dd7-871d-cdddf918b76c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/961de558-b441-46fe-b127-e343b9cb99c9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/961de558-b441-46fe-b127-e343b9cb99c9.lance deleted file mode 100644 index f6d982eff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/961de558-b441-46fe-b127-e343b9cb99c9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9640a7e4-1121-42b2-ba43-dfa34be7fa24.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9640a7e4-1121-42b2-ba43-dfa34be7fa24.lance deleted file mode 100644 index ef3f5443f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9640a7e4-1121-42b2-ba43-dfa34be7fa24.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9641fd17-e32a-4a9a-af99-14efa3c7a916.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9641fd17-e32a-4a9a-af99-14efa3c7a916.lance deleted file mode 100644 index 2644a33fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9641fd17-e32a-4a9a-af99-14efa3c7a916.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/964ef9d5-1a09-429f-812b-75cd3db9e34e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/964ef9d5-1a09-429f-812b-75cd3db9e34e.lance deleted file mode 100644 index 9fd0f7a30..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/964ef9d5-1a09-429f-812b-75cd3db9e34e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96654a6c-f964-4a67-9bf1-60910b867471.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96654a6c-f964-4a67-9bf1-60910b867471.lance deleted file mode 100644 index 9d9545097..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96654a6c-f964-4a67-9bf1-60910b867471.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/969a47a2-4bd3-4850-9bf2-50a799c73824.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/969a47a2-4bd3-4850-9bf2-50a799c73824.lance deleted file mode 100644 index efbd123ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/969a47a2-4bd3-4850-9bf2-50a799c73824.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96a9a14d-4335-48f1-8ead-fe7cacdf611f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96a9a14d-4335-48f1-8ead-fe7cacdf611f.lance deleted file mode 100644 index bf9ca4e79..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96a9a14d-4335-48f1-8ead-fe7cacdf611f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96b81959-dafb-47a0-8fb1-96daca5170f2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96b81959-dafb-47a0-8fb1-96daca5170f2.lance deleted file mode 100644 index 88cc471ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96b81959-dafb-47a0-8fb1-96daca5170f2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96b9fd2b-b81b-4ad0-b962-1d6f24e2d874.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96b9fd2b-b81b-4ad0-b962-1d6f24e2d874.lance deleted file mode 100644 index 2610d22ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96b9fd2b-b81b-4ad0-b962-1d6f24e2d874.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96d43b71-412c-45bd-8915-ba09c432cfe8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96d43b71-412c-45bd-8915-ba09c432cfe8.lance deleted file mode 100644 index 2c6a49e16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96d43b71-412c-45bd-8915-ba09c432cfe8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96f49b09-865c-493c-be11-d4676d729cbc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96f49b09-865c-493c-be11-d4676d729cbc.lance deleted file mode 100644 index 93ca36152..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96f49b09-865c-493c-be11-d4676d729cbc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96fb33ba-eff0-4717-9e7f-441eedacf6e6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96fb33ba-eff0-4717-9e7f-441eedacf6e6.lance deleted file mode 100644 index f9d52554b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96fb33ba-eff0-4717-9e7f-441eedacf6e6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96ff3b10-ee97-4d26-abf2-97fefb2fd904.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96ff3b10-ee97-4d26-abf2-97fefb2fd904.lance deleted file mode 100644 index 2fd550535..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/96ff3b10-ee97-4d26-abf2-97fefb2fd904.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9713d4cd-b494-43d8-acee-a65a693e738f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9713d4cd-b494-43d8-acee-a65a693e738f.lance deleted file mode 100644 index f96364ef9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9713d4cd-b494-43d8-acee-a65a693e738f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/972e8f8d-6624-4d25-b50b-909a3db2a3f8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/972e8f8d-6624-4d25-b50b-909a3db2a3f8.lance deleted file mode 100644 index f9c18491b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/972e8f8d-6624-4d25-b50b-909a3db2a3f8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97457ee7-5e56-4dff-adde-528b8229837b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97457ee7-5e56-4dff-adde-528b8229837b.lance deleted file mode 100644 index 3fa154a4a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97457ee7-5e56-4dff-adde-528b8229837b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9776d0d8-808b-485b-be8b-b95fe23250a9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9776d0d8-808b-485b-be8b-b95fe23250a9.lance deleted file mode 100644 index b1b1e972a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9776d0d8-808b-485b-be8b-b95fe23250a9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/977a41db-db9c-4acf-8a15-bf2a875f2f55.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/977a41db-db9c-4acf-8a15-bf2a875f2f55.lance deleted file mode 100644 index c06a3ec62..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/977a41db-db9c-4acf-8a15-bf2a875f2f55.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97b5ce18-84da-4bea-bc21-a32badf5a98b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97b5ce18-84da-4bea-bc21-a32badf5a98b.lance deleted file mode 100644 index 15957e4cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97b5ce18-84da-4bea-bc21-a32badf5a98b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97b62085-1c8f-420d-9353-07c614e9b6de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97b62085-1c8f-420d-9353-07c614e9b6de.lance deleted file mode 100644 index 41f60cd94..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97b62085-1c8f-420d-9353-07c614e9b6de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97bcaf64-9740-4a10-bd45-221b1e3d8e5d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97bcaf64-9740-4a10-bd45-221b1e3d8e5d.lance deleted file mode 100644 index e8e8789b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97bcaf64-9740-4a10-bd45-221b1e3d8e5d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97cfe238-eb7a-4d73-b87f-568889fabb3a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97cfe238-eb7a-4d73-b87f-568889fabb3a.lance deleted file mode 100644 index d401aa473..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97cfe238-eb7a-4d73-b87f-568889fabb3a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97fb94b0-ad7d-4d0a-b4d2-d359c86244c2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97fb94b0-ad7d-4d0a-b4d2-d359c86244c2.lance deleted file mode 100644 index 9499f050e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/97fb94b0-ad7d-4d0a-b4d2-d359c86244c2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9860b1dd-e071-4e6e-b14e-d99551141297.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9860b1dd-e071-4e6e-b14e-d99551141297.lance deleted file mode 100644 index 5998b520d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9860b1dd-e071-4e6e-b14e-d99551141297.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/98612b5e-7d4f-4e4c-8651-2950c6c81850.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/98612b5e-7d4f-4e4c-8651-2950c6c81850.lance deleted file mode 100644 index c4fe44b38..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/98612b5e-7d4f-4e4c-8651-2950c6c81850.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/98acd689-3947-4795-a6d2-63722540eb9a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/98acd689-3947-4795-a6d2-63722540eb9a.lance deleted file mode 100644 index 5a996c8d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/98acd689-3947-4795-a6d2-63722540eb9a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/98bcb6ef-8148-416f-a3f1-433a9a901bf2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/98bcb6ef-8148-416f-a3f1-433a9a901bf2.lance deleted file mode 100644 index ece2259ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/98bcb6ef-8148-416f-a3f1-433a9a901bf2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/98c73694-a1db-4505-8dc5-b588dc87ffec.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/98c73694-a1db-4505-8dc5-b588dc87ffec.lance deleted file mode 100644 index 9d35fd096..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/98c73694-a1db-4505-8dc5-b588dc87ffec.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/991d48a0-ea26-4c7c-bfce-2d99d6784f28.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/991d48a0-ea26-4c7c-bfce-2d99d6784f28.lance deleted file mode 100644 index 4c10c52f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/991d48a0-ea26-4c7c-bfce-2d99d6784f28.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/995130db-456f-4af6-bf66-1dd63472b12b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/995130db-456f-4af6-bf66-1dd63472b12b.lance deleted file mode 100644 index 5e0723cef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/995130db-456f-4af6-bf66-1dd63472b12b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/997bb400-233e-49d6-8e1a-75cc03449d0a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/997bb400-233e-49d6-8e1a-75cc03449d0a.lance deleted file mode 100644 index d1c5b1a25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/997bb400-233e-49d6-8e1a-75cc03449d0a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99a2646f-804e-4e68-a2c6-4e841b952320.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99a2646f-804e-4e68-a2c6-4e841b952320.lance deleted file mode 100644 index 427fa233c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99a2646f-804e-4e68-a2c6-4e841b952320.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99af7db1-dccd-41b5-8d2e-d0d5078f7e47.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99af7db1-dccd-41b5-8d2e-d0d5078f7e47.lance deleted file mode 100644 index efc0a5261..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99af7db1-dccd-41b5-8d2e-d0d5078f7e47.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99b15b69-6836-45a8-9f2e-3ce6cb359d12.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99b15b69-6836-45a8-9f2e-3ce6cb359d12.lance deleted file mode 100644 index c54333241..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99b15b69-6836-45a8-9f2e-3ce6cb359d12.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99b3a7da-ae67-4425-a6e4-5e56d4825bd5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99b3a7da-ae67-4425-a6e4-5e56d4825bd5.lance deleted file mode 100644 index e6a2bd910..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99b3a7da-ae67-4425-a6e4-5e56d4825bd5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99c47c45-f332-426e-95ae-14c7343fdfd5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99c47c45-f332-426e-95ae-14c7343fdfd5.lance deleted file mode 100644 index 11d699d5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99c47c45-f332-426e-95ae-14c7343fdfd5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99d84224-9f0e-4c53-bcc9-a47597507224.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99d84224-9f0e-4c53-bcc9-a47597507224.lance deleted file mode 100644 index bb920d607..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99d84224-9f0e-4c53-bcc9-a47597507224.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99f0b1af-4cf2-4cac-99e8-cc0ac994db45.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99f0b1af-4cf2-4cac-99e8-cc0ac994db45.lance deleted file mode 100644 index dbc4c22a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99f0b1af-4cf2-4cac-99e8-cc0ac994db45.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99f2f0a4-cb73-4337-b6e9-fd3959830073.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99f2f0a4-cb73-4337-b6e9-fd3959830073.lance deleted file mode 100644 index e0a6bd9b7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99f2f0a4-cb73-4337-b6e9-fd3959830073.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99f50f27-dae9-41f5-840f-03551b6f2f86.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99f50f27-dae9-41f5-840f-03551b6f2f86.lance deleted file mode 100644 index 059764b86..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/99f50f27-dae9-41f5-840f-03551b6f2f86.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9a525e1c-4b19-4f69-b8c7-bcd7b3c175cf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9a525e1c-4b19-4f69-b8c7-bcd7b3c175cf.lance deleted file mode 100644 index b28342703..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9a525e1c-4b19-4f69-b8c7-bcd7b3c175cf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9a588acd-0ac8-452e-a384-d6ce722e5091.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9a588acd-0ac8-452e-a384-d6ce722e5091.lance deleted file mode 100644 index b2abfa30a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9a588acd-0ac8-452e-a384-d6ce722e5091.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9af0ae16-3575-41a0-9010-def97a1b37d1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9af0ae16-3575-41a0-9010-def97a1b37d1.lance deleted file mode 100644 index f2d22eb6e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9af0ae16-3575-41a0-9010-def97a1b37d1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b0721ac-e6dd-4c29-be94-9a8b224ba135.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b0721ac-e6dd-4c29-be94-9a8b224ba135.lance deleted file mode 100644 index 3cfa5e4d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b0721ac-e6dd-4c29-be94-9a8b224ba135.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b441bcd-db75-43af-86ba-705d5276cba4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b441bcd-db75-43af-86ba-705d5276cba4.lance deleted file mode 100644 index 981acfa34..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b441bcd-db75-43af-86ba-705d5276cba4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b5a2885-ecfe-4266-8f89-068a442bf03e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b5a2885-ecfe-4266-8f89-068a442bf03e.lance deleted file mode 100644 index d75434d9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b5a2885-ecfe-4266-8f89-068a442bf03e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b627454-82a5-430a-8d93-5e3d8491900c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b627454-82a5-430a-8d93-5e3d8491900c.lance deleted file mode 100644 index fba693d5a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b627454-82a5-430a-8d93-5e3d8491900c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b6353a5-8f92-4cf1-9e17-ef49d35ef2d3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b6353a5-8f92-4cf1-9e17-ef49d35ef2d3.lance deleted file mode 100644 index b4ca634dd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b6353a5-8f92-4cf1-9e17-ef49d35ef2d3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b658eea-be1a-40a6-8488-a4f5fbacde3a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b658eea-be1a-40a6-8488-a4f5fbacde3a.lance deleted file mode 100644 index 0bb333339..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b658eea-be1a-40a6-8488-a4f5fbacde3a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b7b1992-9ac9-46a4-8b17-ce16d563c4b4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b7b1992-9ac9-46a4-8b17-ce16d563c4b4.lance deleted file mode 100644 index 763db9df9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b7b1992-9ac9-46a4-8b17-ce16d563c4b4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b7c7999-6d76-4cde-afcc-868ee91b0589.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b7c7999-6d76-4cde-afcc-868ee91b0589.lance deleted file mode 100644 index f0723c839..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b7c7999-6d76-4cde-afcc-868ee91b0589.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b8cd691-c37d-419b-844f-9bb7bce1893a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b8cd691-c37d-419b-844f-9bb7bce1893a.lance deleted file mode 100644 index b15a6b973..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9b8cd691-c37d-419b-844f-9bb7bce1893a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9bb4d049-4ebe-41f7-82b8-0fd74ab0b914.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9bb4d049-4ebe-41f7-82b8-0fd74ab0b914.lance deleted file mode 100644 index 824786d58..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9bb4d049-4ebe-41f7-82b8-0fd74ab0b914.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9bb90b51-d357-4e03-bdee-17ea4b0da220.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9bb90b51-d357-4e03-bdee-17ea4b0da220.lance deleted file mode 100644 index b65553c9c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9bb90b51-d357-4e03-bdee-17ea4b0da220.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9bcc2cf9-453c-4227-a7fc-8a7fd839af19.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9bcc2cf9-453c-4227-a7fc-8a7fd839af19.lance deleted file mode 100644 index dab997534..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9bcc2cf9-453c-4227-a7fc-8a7fd839af19.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9be7a6ee-a015-411c-9ea6-01ba787d7d6f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9be7a6ee-a015-411c-9ea6-01ba787d7d6f.lance deleted file mode 100644 index 356a19d67..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9be7a6ee-a015-411c-9ea6-01ba787d7d6f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c15bfe4-ed6a-4c5d-b9fc-655c15db4174.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c15bfe4-ed6a-4c5d-b9fc-655c15db4174.lance deleted file mode 100644 index 346172cf0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c15bfe4-ed6a-4c5d-b9fc-655c15db4174.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c319827-3d18-4b6b-9c93-fbc1410d027b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c319827-3d18-4b6b-9c93-fbc1410d027b.lance deleted file mode 100644 index 0b7e73402..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c319827-3d18-4b6b-9c93-fbc1410d027b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c4b30ef-6565-465e-93aa-351c64924a60.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c4b30ef-6565-465e-93aa-351c64924a60.lance deleted file mode 100644 index 6b944a099..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c4b30ef-6565-465e-93aa-351c64924a60.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c70f3f4-2cc8-46f1-a590-385849df623b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c70f3f4-2cc8-46f1-a590-385849df623b.lance deleted file mode 100644 index e0bc45116..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c70f3f4-2cc8-46f1-a590-385849df623b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c7655ba-f9e6-4809-8f2f-91baf41ee377.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c7655ba-f9e6-4809-8f2f-91baf41ee377.lance deleted file mode 100644 index b14f49bbd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c7655ba-f9e6-4809-8f2f-91baf41ee377.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c778e7d-e883-4ac7-abc0-27747103a2f1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c778e7d-e883-4ac7-abc0-27747103a2f1.lance deleted file mode 100644 index 86314fc56..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c778e7d-e883-4ac7-abc0-27747103a2f1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c9c210f-e15b-4cd6-a1de-2b61dfaab4b2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c9c210f-e15b-4cd6-a1de-2b61dfaab4b2.lance deleted file mode 100644 index c25decc44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9c9c210f-e15b-4cd6-a1de-2b61dfaab4b2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ca2b8b1-8a39-4c12-a8af-3146ab8edcab.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ca2b8b1-8a39-4c12-a8af-3146ab8edcab.lance deleted file mode 100644 index b42e155b6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ca2b8b1-8a39-4c12-a8af-3146ab8edcab.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ca6cf6a-9a9e-4588-b8fb-471c25829b4c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ca6cf6a-9a9e-4588-b8fb-471c25829b4c.lance deleted file mode 100644 index 9cb504be6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ca6cf6a-9a9e-4588-b8fb-471c25829b4c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9cc3cadc-29d6-44a1-8ece-0f20fc585cd7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9cc3cadc-29d6-44a1-8ece-0f20fc585cd7.lance deleted file mode 100644 index 033a91e04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9cc3cadc-29d6-44a1-8ece-0f20fc585cd7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9cefe9f7-6915-4c34-8460-354d5f318cdb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9cefe9f7-6915-4c34-8460-354d5f318cdb.lance deleted file mode 100644 index 853167096..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9cefe9f7-6915-4c34-8460-354d5f318cdb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9d0e0b73-15ae-4862-965f-76fbebdb6487.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9d0e0b73-15ae-4862-965f-76fbebdb6487.lance deleted file mode 100644 index 3e999ba67..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9d0e0b73-15ae-4862-965f-76fbebdb6487.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9d109e62-2521-4a2c-a1d5-3c6753b67e74.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9d109e62-2521-4a2c-a1d5-3c6753b67e74.lance deleted file mode 100644 index 8eef5d52e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9d109e62-2521-4a2c-a1d5-3c6753b67e74.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9d17febe-7a8b-48e9-aab3-87e9e93f883e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9d17febe-7a8b-48e9-aab3-87e9e93f883e.lance deleted file mode 100644 index ad91a8bea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9d17febe-7a8b-48e9-aab3-87e9e93f883e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9d4158f5-1325-4cb7-89d2-e4723ff45b1d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9d4158f5-1325-4cb7-89d2-e4723ff45b1d.lance deleted file mode 100644 index feedb1374..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9d4158f5-1325-4cb7-89d2-e4723ff45b1d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9dad245e-7cfb-4e03-bf43-5ffb930fdc45.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9dad245e-7cfb-4e03-bf43-5ffb930fdc45.lance deleted file mode 100644 index 92fd54c83..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9dad245e-7cfb-4e03-bf43-5ffb930fdc45.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e1a876e-78e3-45ec-a53e-af588432a0b9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e1a876e-78e3-45ec-a53e-af588432a0b9.lance deleted file mode 100644 index 9d4cb2f49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e1a876e-78e3-45ec-a53e-af588432a0b9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e2163de-127f-4cad-94d9-482aa2a57ba0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e2163de-127f-4cad-94d9-482aa2a57ba0.lance deleted file mode 100644 index e74fa1368..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e2163de-127f-4cad-94d9-482aa2a57ba0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e22cd8a-22e6-42d1-98c5-2ee57b3473d7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e22cd8a-22e6-42d1-98c5-2ee57b3473d7.lance deleted file mode 100644 index 32ae8f122..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e22cd8a-22e6-42d1-98c5-2ee57b3473d7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e2bbd77-274b-44cb-9dd1-aafc4ff3b3bb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e2bbd77-274b-44cb-9dd1-aafc4ff3b3bb.lance deleted file mode 100644 index bce658f4c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e2bbd77-274b-44cb-9dd1-aafc4ff3b3bb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e2ecd0b-e1be-48fd-88b3-ef701211c098.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e2ecd0b-e1be-48fd-88b3-ef701211c098.lance deleted file mode 100644 index ef94b4c2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e2ecd0b-e1be-48fd-88b3-ef701211c098.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e3047ed-11bf-4d02-b0df-ad6c72b0c39f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e3047ed-11bf-4d02-b0df-ad6c72b0c39f.lance deleted file mode 100644 index 6826f8aff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e3047ed-11bf-4d02-b0df-ad6c72b0c39f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e98bdac-a63a-4391-b379-9613af91b02d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e98bdac-a63a-4391-b379-9613af91b02d.lance deleted file mode 100644 index 8916b9e70..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9e98bdac-a63a-4391-b379-9613af91b02d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ead5d6d-af1e-403d-9a5c-f239732782d9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ead5d6d-af1e-403d-9a5c-f239732782d9.lance deleted file mode 100644 index ce17ef1ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ead5d6d-af1e-403d-9a5c-f239732782d9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ed40418-12f7-4b44-b1f5-1c21948b237f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ed40418-12f7-4b44-b1f5-1c21948b237f.lance deleted file mode 100644 index 4810b8998..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ed40418-12f7-4b44-b1f5-1c21948b237f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ef16fb4-e262-4019-aaeb-a03e7469bc6f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ef16fb4-e262-4019-aaeb-a03e7469bc6f.lance deleted file mode 100644 index 270c28f2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ef16fb4-e262-4019-aaeb-a03e7469bc6f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9efc73c0-74dd-460d-a836-dec40d5eb350.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9efc73c0-74dd-460d-a836-dec40d5eb350.lance deleted file mode 100644 index d42c4a220..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9efc73c0-74dd-460d-a836-dec40d5eb350.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9f6e9495-5ec7-48ce-a30b-3f58d5c25810.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9f6e9495-5ec7-48ce-a30b-3f58d5c25810.lance deleted file mode 100644 index 12cd7ab66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9f6e9495-5ec7-48ce-a30b-3f58d5c25810.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9f76c14f-a9d2-40cd-bb67-51f68fe6a9c0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9f76c14f-a9d2-40cd-bb67-51f68fe6a9c0.lance deleted file mode 100644 index 6408bdbfa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9f76c14f-a9d2-40cd-bb67-51f68fe6a9c0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9f7b072b-b548-4489-959b-e2643f5b524b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9f7b072b-b548-4489-959b-e2643f5b524b.lance deleted file mode 100644 index a7936a04a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9f7b072b-b548-4489-959b-e2643f5b524b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9f8650f9-c54e-4dbd-80d5-9176acc90167.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9f8650f9-c54e-4dbd-80d5-9176acc90167.lance deleted file mode 100644 index d05c95cdc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9f8650f9-c54e-4dbd-80d5-9176acc90167.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9f88b552-6141-4921-9fe9-a0067577a22e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9f88b552-6141-4921-9fe9-a0067577a22e.lance deleted file mode 100644 index c09a82fcf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9f88b552-6141-4921-9fe9-a0067577a22e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9fbd18a4-ff55-415a-b626-cb8c053020cf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9fbd18a4-ff55-415a-b626-cb8c053020cf.lance deleted file mode 100644 index 3c40f9d98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9fbd18a4-ff55-415a-b626-cb8c053020cf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9fbd549d-0dba-4c60-ad05-dffb65a893a1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9fbd549d-0dba-4c60-ad05-dffb65a893a1.lance deleted file mode 100644 index a781b59b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9fbd549d-0dba-4c60-ad05-dffb65a893a1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9fc09e52-1ff7-44a6-a644-66130962326a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9fc09e52-1ff7-44a6-a644-66130962326a.lance deleted file mode 100644 index 5cd9eac23..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9fc09e52-1ff7-44a6-a644-66130962326a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9fdf01fb-29db-4dab-85d3-d611e30a1899.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9fdf01fb-29db-4dab-85d3-d611e30a1899.lance deleted file mode 100644 index d8a9420b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9fdf01fb-29db-4dab-85d3-d611e30a1899.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9fe00381-cb6d-4d06-9b40-b16b205f5c8f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9fe00381-cb6d-4d06-9b40-b16b205f5c8f.lance deleted file mode 100644 index 8f0edbac3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9fe00381-cb6d-4d06-9b40-b16b205f5c8f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9feb694e-854e-44f8-91c1-825f9ad4860b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9feb694e-854e-44f8-91c1-825f9ad4860b.lance deleted file mode 100644 index ce7121a55..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9feb694e-854e-44f8-91c1-825f9ad4860b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ff07a69-666c-4051-b775-8df84c71fe8c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ff07a69-666c-4051-b775-8df84c71fe8c.lance deleted file mode 100644 index 469b0515f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/9ff07a69-666c-4051-b775-8df84c71fe8c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a01e1ea3-d979-4815-8382-66b0de002d07.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a01e1ea3-d979-4815-8382-66b0de002d07.lance deleted file mode 100644 index 597db1af9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a01e1ea3-d979-4815-8382-66b0de002d07.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a0290689-5ddd-4b46-aca5-3daba677ffe1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a0290689-5ddd-4b46-aca5-3daba677ffe1.lance deleted file mode 100644 index 9566ea3e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a0290689-5ddd-4b46-aca5-3daba677ffe1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a03359c7-b9ec-4a10-bb35-79a8a4aa0c18.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a03359c7-b9ec-4a10-bb35-79a8a4aa0c18.lance deleted file mode 100644 index 8304c49fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a03359c7-b9ec-4a10-bb35-79a8a4aa0c18.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a03488f6-26d0-4320-9899-8a27cad4fc39.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a03488f6-26d0-4320-9899-8a27cad4fc39.lance deleted file mode 100644 index 96417df30..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a03488f6-26d0-4320-9899-8a27cad4fc39.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a038d6ab-61e4-4b5f-a9f5-0495bac9a9b6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a038d6ab-61e4-4b5f-a9f5-0495bac9a9b6.lance deleted file mode 100644 index 415fd0497..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a038d6ab-61e4-4b5f-a9f5-0495bac9a9b6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a042ccc6-0890-4c46-a4e7-93aba8e164a0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a042ccc6-0890-4c46-a4e7-93aba8e164a0.lance deleted file mode 100644 index 0f35d541d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a042ccc6-0890-4c46-a4e7-93aba8e164a0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a0977b53-5356-4c29-9e2c-403bfc390e37.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a0977b53-5356-4c29-9e2c-403bfc390e37.lance deleted file mode 100644 index 2aa9e400c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a0977b53-5356-4c29-9e2c-403bfc390e37.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a09a7a01-4f1f-4c8d-a261-74f692272daf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a09a7a01-4f1f-4c8d-a261-74f692272daf.lance deleted file mode 100644 index 16daf192d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a09a7a01-4f1f-4c8d-a261-74f692272daf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a0e7b999-14c8-48b0-a348-178dfd0bd437.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a0e7b999-14c8-48b0-a348-178dfd0bd437.lance deleted file mode 100644 index aba0f360c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a0e7b999-14c8-48b0-a348-178dfd0bd437.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a141370b-0978-4c5f-8c78-f005964ee5d4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a141370b-0978-4c5f-8c78-f005964ee5d4.lance deleted file mode 100644 index 80555e838..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a141370b-0978-4c5f-8c78-f005964ee5d4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a14351c2-81e0-4aef-bdb7-2a0548aa92d0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a14351c2-81e0-4aef-bdb7-2a0548aa92d0.lance deleted file mode 100644 index d629ebedc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a14351c2-81e0-4aef-bdb7-2a0548aa92d0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a16cddea-f030-4aa8-a15e-012fe2a0111d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a16cddea-f030-4aa8-a15e-012fe2a0111d.lance deleted file mode 100644 index 49073bfd0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a16cddea-f030-4aa8-a15e-012fe2a0111d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1708f4a-aeac-4421-9a3e-6608ec3707ed.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1708f4a-aeac-4421-9a3e-6608ec3707ed.lance deleted file mode 100644 index fc94a0cd2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1708f4a-aeac-4421-9a3e-6608ec3707ed.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a187e902-ed62-4f28-b62e-d02ece8321b7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a187e902-ed62-4f28-b62e-d02ece8321b7.lance deleted file mode 100644 index 428e570be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a187e902-ed62-4f28-b62e-d02ece8321b7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a195525e-feeb-4994-b3aa-ddefb47de585.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a195525e-feeb-4994-b3aa-ddefb47de585.lance deleted file mode 100644 index da503b12e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a195525e-feeb-4994-b3aa-ddefb47de585.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1b84ec4-146d-4cc7-a5f6-2ee6a939bfec.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1b84ec4-146d-4cc7-a5f6-2ee6a939bfec.lance deleted file mode 100644 index 2e35f0df5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1b84ec4-146d-4cc7-a5f6-2ee6a939bfec.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1c8ffa8-ce76-46dd-b0b6-83b68fe1a8de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1c8ffa8-ce76-46dd-b0b6-83b68fe1a8de.lance deleted file mode 100644 index 1ed3e995f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1c8ffa8-ce76-46dd-b0b6-83b68fe1a8de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1d11f3b-7166-4f9e-b413-85f58e065089.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1d11f3b-7166-4f9e-b413-85f58e065089.lance deleted file mode 100644 index f32251124..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1d11f3b-7166-4f9e-b413-85f58e065089.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1daad9d-fe61-4a60-8a01-1d7db9d676c0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1daad9d-fe61-4a60-8a01-1d7db9d676c0.lance deleted file mode 100644 index b6b208c48..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1daad9d-fe61-4a60-8a01-1d7db9d676c0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1e32eb8-9f2b-4127-b689-7cc49920f135.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1e32eb8-9f2b-4127-b689-7cc49920f135.lance deleted file mode 100644 index 537137355..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1e32eb8-9f2b-4127-b689-7cc49920f135.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1eae834-837b-4339-8e19-b4bb31cf9c30.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1eae834-837b-4339-8e19-b4bb31cf9c30.lance deleted file mode 100644 index e49984730..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1eae834-837b-4339-8e19-b4bb31cf9c30.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1f38f20-b356-4d5e-bd64-81eaca33da63.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1f38f20-b356-4d5e-bd64-81eaca33da63.lance deleted file mode 100644 index ed7c20c6b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a1f38f20-b356-4d5e-bd64-81eaca33da63.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a2109e4e-5e4c-4a74-af53-9f41d723c546.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a2109e4e-5e4c-4a74-af53-9f41d723c546.lance deleted file mode 100644 index 8e69d9643..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a2109e4e-5e4c-4a74-af53-9f41d723c546.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a251264d-4df4-476e-9b21-29362b539d3c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a251264d-4df4-476e-9b21-29362b539d3c.lance deleted file mode 100644 index ed33f059c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a251264d-4df4-476e-9b21-29362b539d3c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a2535a6f-820d-49c0-b7fb-da47fc1e59d2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a2535a6f-820d-49c0-b7fb-da47fc1e59d2.lance deleted file mode 100644 index 9feede4cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a2535a6f-820d-49c0-b7fb-da47fc1e59d2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a25744df-2cd1-421a-86a6-faa5bda86fd5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a25744df-2cd1-421a-86a6-faa5bda86fd5.lance deleted file mode 100644 index 89cceeab5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a25744df-2cd1-421a-86a6-faa5bda86fd5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a26d570f-d276-4fba-a9dd-33a28cd3dd2a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a26d570f-d276-4fba-a9dd-33a28cd3dd2a.lance deleted file mode 100644 index c9b4aec32..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a26d570f-d276-4fba-a9dd-33a28cd3dd2a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a299e89e-46ec-4a7f-8822-f4d54999a89c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a299e89e-46ec-4a7f-8822-f4d54999a89c.lance deleted file mode 100644 index 53cce75e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a299e89e-46ec-4a7f-8822-f4d54999a89c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a2b0c049-0ac2-4c63-a003-faec9006c9b6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a2b0c049-0ac2-4c63-a003-faec9006c9b6.lance deleted file mode 100644 index 06c215d53..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a2b0c049-0ac2-4c63-a003-faec9006c9b6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a2b8945b-fb22-45bf-8764-272b302b417c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a2b8945b-fb22-45bf-8764-272b302b417c.lance deleted file mode 100644 index 38695accf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a2b8945b-fb22-45bf-8764-272b302b417c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a2b9a712-ddde-4dfd-8022-fd3c97db8c28.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a2b9a712-ddde-4dfd-8022-fd3c97db8c28.lance deleted file mode 100644 index 9fab70027..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a2b9a712-ddde-4dfd-8022-fd3c97db8c28.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a305e474-5d6c-4154-8d46-5d3376148820.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a305e474-5d6c-4154-8d46-5d3376148820.lance deleted file mode 100644 index 4d413ecdd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a305e474-5d6c-4154-8d46-5d3376148820.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a31edd74-00b7-4580-a1b0-279c1f2600b0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a31edd74-00b7-4580-a1b0-279c1f2600b0.lance deleted file mode 100644 index aee6accbc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a31edd74-00b7-4580-a1b0-279c1f2600b0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a349cc18-df0a-48e6-a477-21f51e16715c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a349cc18-df0a-48e6-a477-21f51e16715c.lance deleted file mode 100644 index 31542693f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a349cc18-df0a-48e6-a477-21f51e16715c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3560655-3baa-47fc-a460-00eeb8753f95.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3560655-3baa-47fc-a460-00eeb8753f95.lance deleted file mode 100644 index 6ddbf97ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3560655-3baa-47fc-a460-00eeb8753f95.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a35701dd-f0b7-4bfc-a5ec-0fcc8231e5ef.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a35701dd-f0b7-4bfc-a5ec-0fcc8231e5ef.lance deleted file mode 100644 index f3e874b97..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a35701dd-f0b7-4bfc-a5ec-0fcc8231e5ef.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a37ac123-192b-43a8-bcee-feb9ca6e5b5d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a37ac123-192b-43a8-bcee-feb9ca6e5b5d.lance deleted file mode 100644 index 5e1329985..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a37ac123-192b-43a8-bcee-feb9ca6e5b5d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a399c2f4-a8a7-4dd0-a914-979a8719e5ea.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a399c2f4-a8a7-4dd0-a914-979a8719e5ea.lance deleted file mode 100644 index 9c293903f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a399c2f4-a8a7-4dd0-a914-979a8719e5ea.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3af6cfe-442a-418a-881e-d4c70d617e8d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3af6cfe-442a-418a-881e-d4c70d617e8d.lance deleted file mode 100644 index bda6fbca2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3af6cfe-442a-418a-881e-d4c70d617e8d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3bc8563-b5e0-4d9b-af0a-d8ca66937ede.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3bc8563-b5e0-4d9b-af0a-d8ca66937ede.lance deleted file mode 100644 index 70851bef3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3bc8563-b5e0-4d9b-af0a-d8ca66937ede.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3bcd72e-638f-4c03-9cb1-6f5df8290b76.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3bcd72e-638f-4c03-9cb1-6f5df8290b76.lance deleted file mode 100644 index 6ee2ec26d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3bcd72e-638f-4c03-9cb1-6f5df8290b76.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3c99259-f24d-41eb-a5b8-e9c0ff05b744.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3c99259-f24d-41eb-a5b8-e9c0ff05b744.lance deleted file mode 100644 index 00787026a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3c99259-f24d-41eb-a5b8-e9c0ff05b744.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3e80b4e-ae68-44c7-b481-396df37ab34c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3e80b4e-ae68-44c7-b481-396df37ab34c.lance deleted file mode 100644 index dcaf704b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a3e80b4e-ae68-44c7-b481-396df37ab34c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a403b389-ce1e-40a3-995f-d85c31666254.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a403b389-ce1e-40a3-995f-d85c31666254.lance deleted file mode 100644 index a269947b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a403b389-ce1e-40a3-995f-d85c31666254.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a42309c3-fdc9-46ff-bc62-bf0a1453f0fe.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a42309c3-fdc9-46ff-bc62-bf0a1453f0fe.lance deleted file mode 100644 index ef6b5e460..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a42309c3-fdc9-46ff-bc62-bf0a1453f0fe.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a42b9bab-5066-47d3-a3a7-f31847672fe3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a42b9bab-5066-47d3-a3a7-f31847672fe3.lance deleted file mode 100644 index dc7aefa46..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a42b9bab-5066-47d3-a3a7-f31847672fe3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a4364616-6faa-464c-9d87-7c1009a5f122.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a4364616-6faa-464c-9d87-7c1009a5f122.lance deleted file mode 100644 index cb083f98b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a4364616-6faa-464c-9d87-7c1009a5f122.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a441c8b4-936e-422e-9822-4e6e35caad84.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a441c8b4-936e-422e-9822-4e6e35caad84.lance deleted file mode 100644 index 83a5151fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a441c8b4-936e-422e-9822-4e6e35caad84.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a4481b23-1924-444d-b907-b1b3447f9173.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a4481b23-1924-444d-b907-b1b3447f9173.lance deleted file mode 100644 index dbb3fe765..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a4481b23-1924-444d-b907-b1b3447f9173.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a4677307-d143-45b9-83d0-f6fa55097c80.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a4677307-d143-45b9-83d0-f6fa55097c80.lance deleted file mode 100644 index 4a47bf013..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a4677307-d143-45b9-83d0-f6fa55097c80.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a46b67bf-80d2-4f1f-82a8-9392d7067d31.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a46b67bf-80d2-4f1f-82a8-9392d7067d31.lance deleted file mode 100644 index 9876013d6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a46b67bf-80d2-4f1f-82a8-9392d7067d31.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a48428c1-0da8-49a4-9e8a-7371c390cfe2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a48428c1-0da8-49a4-9e8a-7371c390cfe2.lance deleted file mode 100644 index ea34c2be4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a48428c1-0da8-49a4-9e8a-7371c390cfe2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a4967a42-4e4e-47aa-8002-743f198f08f9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a4967a42-4e4e-47aa-8002-743f198f08f9.lance deleted file mode 100644 index 5bb9d74ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a4967a42-4e4e-47aa-8002-743f198f08f9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a4a96bd3-a4b4-4ac9-b52d-0268161291de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a4a96bd3-a4b4-4ac9-b52d-0268161291de.lance deleted file mode 100644 index 86095cece..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a4a96bd3-a4b4-4ac9-b52d-0268161291de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a51787ad-8313-458a-b97a-f32c023add36.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a51787ad-8313-458a-b97a-f32c023add36.lance deleted file mode 100644 index d967776aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a51787ad-8313-458a-b97a-f32c023add36.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a54b4234-9969-42bd-9473-2b078d2b8e35.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a54b4234-9969-42bd-9473-2b078d2b8e35.lance deleted file mode 100644 index 64b4c2013..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a54b4234-9969-42bd-9473-2b078d2b8e35.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a5766417-edb2-4f0c-bb6f-1c5b408d7c6e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a5766417-edb2-4f0c-bb6f-1c5b408d7c6e.lance deleted file mode 100644 index 045a026ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a5766417-edb2-4f0c-bb6f-1c5b408d7c6e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a59d58f1-2ecd-4511-919e-47b7e831bbcd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a59d58f1-2ecd-4511-919e-47b7e831bbcd.lance deleted file mode 100644 index 9791c8531..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a59d58f1-2ecd-4511-919e-47b7e831bbcd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a5afd9d8-1279-435f-969c-ecc93f798006.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a5afd9d8-1279-435f-969c-ecc93f798006.lance deleted file mode 100644 index e53de4aa2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a5afd9d8-1279-435f-969c-ecc93f798006.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a5bdbb9a-92b9-46ae-87f0-417483d56b71.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a5bdbb9a-92b9-46ae-87f0-417483d56b71.lance deleted file mode 100644 index 57126a87b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a5bdbb9a-92b9-46ae-87f0-417483d56b71.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a5c610db-5336-4080-8a1f-82b80199498d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a5c610db-5336-4080-8a1f-82b80199498d.lance deleted file mode 100644 index 2bffd4898..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a5c610db-5336-4080-8a1f-82b80199498d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a61fa07e-b708-491a-812d-53a9bacf77cb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a61fa07e-b708-491a-812d-53a9bacf77cb.lance deleted file mode 100644 index 9973d8af0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a61fa07e-b708-491a-812d-53a9bacf77cb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a631db77-f07d-467b-b8d9-a1e3032a029b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a631db77-f07d-467b-b8d9-a1e3032a029b.lance deleted file mode 100644 index 7af3dc056..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a631db77-f07d-467b-b8d9-a1e3032a029b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6620366-78b4-4874-99e4-6e84eff4f1d8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6620366-78b4-4874-99e4-6e84eff4f1d8.lance deleted file mode 100644 index a31283e30..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6620366-78b4-4874-99e4-6e84eff4f1d8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a66845d5-cfab-4f08-8646-6f17c1653669.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a66845d5-cfab-4f08-8646-6f17c1653669.lance deleted file mode 100644 index 67b2c9df2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a66845d5-cfab-4f08-8646-6f17c1653669.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6739dc8-30b3-4f76-8c44-b7d252fa4352.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6739dc8-30b3-4f76-8c44-b7d252fa4352.lance deleted file mode 100644 index dbd706262..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6739dc8-30b3-4f76-8c44-b7d252fa4352.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a68b2eed-a245-4433-80d5-249409a3467a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a68b2eed-a245-4433-80d5-249409a3467a.lance deleted file mode 100644 index 0191d081b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a68b2eed-a245-4433-80d5-249409a3467a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6914638-251d-4680-9f71-eb963bbd5db9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6914638-251d-4680-9f71-eb963bbd5db9.lance deleted file mode 100644 index b1e7b75b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6914638-251d-4680-9f71-eb963bbd5db9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6960e57-1af8-460e-8f81-e979b5fe1aeb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6960e57-1af8-460e-8f81-e979b5fe1aeb.lance deleted file mode 100644 index ed1c8218c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6960e57-1af8-460e-8f81-e979b5fe1aeb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a69abdf2-df43-4ffb-b6c6-bde0bcc5664a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a69abdf2-df43-4ffb-b6c6-bde0bcc5664a.lance deleted file mode 100644 index 7c72503b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a69abdf2-df43-4ffb-b6c6-bde0bcc5664a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6a72559-b22b-43ab-890a-168f3247386e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6a72559-b22b-43ab-890a-168f3247386e.lance deleted file mode 100644 index a50309809..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6a72559-b22b-43ab-890a-168f3247386e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6bad560-cb92-4a8e-a9f7-79d150b8eb20.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6bad560-cb92-4a8e-a9f7-79d150b8eb20.lance deleted file mode 100644 index 0c501811b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6bad560-cb92-4a8e-a9f7-79d150b8eb20.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6cbd859-e9f5-4531-9ba9-e453e06a6455.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6cbd859-e9f5-4531-9ba9-e453e06a6455.lance deleted file mode 100644 index c793fb717..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6cbd859-e9f5-4531-9ba9-e453e06a6455.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6e05ea5-2b7e-4e19-b734-828dadf5aabf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6e05ea5-2b7e-4e19-b734-828dadf5aabf.lance deleted file mode 100644 index 78da777da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6e05ea5-2b7e-4e19-b734-828dadf5aabf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6e444c9-275e-493c-b85a-050eb4471021.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6e444c9-275e-493c-b85a-050eb4471021.lance deleted file mode 100644 index c8940b977..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6e444c9-275e-493c-b85a-050eb4471021.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6f2b5ac-c172-49cc-a6a0-5f204713f987.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6f2b5ac-c172-49cc-a6a0-5f204713f987.lance deleted file mode 100644 index 3b5a1df54..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a6f2b5ac-c172-49cc-a6a0-5f204713f987.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a7001225-f377-4d2e-afa8-88b42f078a5f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a7001225-f377-4d2e-afa8-88b42f078a5f.lance deleted file mode 100644 index e5a24cb71..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a7001225-f377-4d2e-afa8-88b42f078a5f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a702f5a1-8614-43d7-834a-0833be814bb1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a702f5a1-8614-43d7-834a-0833be814bb1.lance deleted file mode 100644 index 3783022f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a702f5a1-8614-43d7-834a-0833be814bb1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a710ed4a-26b2-4c5d-b647-af7d58912fd0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a710ed4a-26b2-4c5d-b647-af7d58912fd0.lance deleted file mode 100644 index eb5b6fc83..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a710ed4a-26b2-4c5d-b647-af7d58912fd0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a73d289b-0dd4-4642-92e1-4982a3db76eb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a73d289b-0dd4-4642-92e1-4982a3db76eb.lance deleted file mode 100644 index 0d2007fda..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a73d289b-0dd4-4642-92e1-4982a3db76eb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a73d6d98-0539-40e3-bb3b-ffa0f8e203f5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a73d6d98-0539-40e3-bb3b-ffa0f8e203f5.lance deleted file mode 100644 index 5869a7e77..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a73d6d98-0539-40e3-bb3b-ffa0f8e203f5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a7822f70-c42a-40eb-b5d1-9b5c8211f764.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a7822f70-c42a-40eb-b5d1-9b5c8211f764.lance deleted file mode 100644 index de7449182..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a7822f70-c42a-40eb-b5d1-9b5c8211f764.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a7a55f89-2f2a-499a-80f4-9aa901e270df.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a7a55f89-2f2a-499a-80f4-9aa901e270df.lance deleted file mode 100644 index 5925e3d58..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a7a55f89-2f2a-499a-80f4-9aa901e270df.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a7d4f031-9800-4ebd-98de-15aa70bd1505.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a7d4f031-9800-4ebd-98de-15aa70bd1505.lance deleted file mode 100644 index b68073263..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a7d4f031-9800-4ebd-98de-15aa70bd1505.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a7ef95fb-5979-4c27-8972-cca27a0ddbd5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a7ef95fb-5979-4c27-8972-cca27a0ddbd5.lance deleted file mode 100644 index e65f35fac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a7ef95fb-5979-4c27-8972-cca27a0ddbd5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a81e0ea6-345a-4e60-9428-2990e5e1bf3a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a81e0ea6-345a-4e60-9428-2990e5e1bf3a.lance deleted file mode 100644 index 06a0dce77..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a81e0ea6-345a-4e60-9428-2990e5e1bf3a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a833c51c-53ce-4699-804c-15997e313d02.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a833c51c-53ce-4699-804c-15997e313d02.lance deleted file mode 100644 index 84ece5299..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a833c51c-53ce-4699-804c-15997e313d02.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a85ca5ad-7570-4d42-a040-8316c6aed70b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a85ca5ad-7570-4d42-a040-8316c6aed70b.lance deleted file mode 100644 index ef58230fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a85ca5ad-7570-4d42-a040-8316c6aed70b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a874e3a8-4ec3-43e3-a708-98e030d48db9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a874e3a8-4ec3-43e3-a708-98e030d48db9.lance deleted file mode 100644 index 48212eb41..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a874e3a8-4ec3-43e3-a708-98e030d48db9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a8c4af32-1d2a-472e-925f-0f53ac8a612e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a8c4af32-1d2a-472e-925f-0f53ac8a612e.lance deleted file mode 100644 index 46adf0206..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a8c4af32-1d2a-472e-925f-0f53ac8a612e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a8d25288-5c72-49a4-ae7d-ab5743c6cfab.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a8d25288-5c72-49a4-ae7d-ab5743c6cfab.lance deleted file mode 100644 index 0ef6971ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a8d25288-5c72-49a4-ae7d-ab5743c6cfab.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a908f308-b703-4c60-84a5-cad23d211762.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a908f308-b703-4c60-84a5-cad23d211762.lance deleted file mode 100644 index f8a35b25e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a908f308-b703-4c60-84a5-cad23d211762.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9093b32-3851-49c1-83b2-099b02602770.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9093b32-3851-49c1-83b2-099b02602770.lance deleted file mode 100644 index eaf90eee2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9093b32-3851-49c1-83b2-099b02602770.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a90d50a9-a5e6-4290-ae7a-04666c83ee7d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a90d50a9-a5e6-4290-ae7a-04666c83ee7d.lance deleted file mode 100644 index eee204709..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a90d50a9-a5e6-4290-ae7a-04666c83ee7d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a91b2f33-f237-4974-8813-e5bccf533b52.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a91b2f33-f237-4974-8813-e5bccf533b52.lance deleted file mode 100644 index be75d1761..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a91b2f33-f237-4974-8813-e5bccf533b52.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a927a47c-3da7-445c-be1e-5cfa3a33d94c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a927a47c-3da7-445c-be1e-5cfa3a33d94c.lance deleted file mode 100644 index 54f13fc26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a927a47c-3da7-445c-be1e-5cfa3a33d94c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a931aa5e-0cc2-4fa6-b501-e404ebe45944.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a931aa5e-0cc2-4fa6-b501-e404ebe45944.lance deleted file mode 100644 index f31b1e947..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a931aa5e-0cc2-4fa6-b501-e404ebe45944.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9382364-3d83-44cd-a712-ca9b06807fd3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9382364-3d83-44cd-a712-ca9b06807fd3.lance deleted file mode 100644 index afd01d13a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9382364-3d83-44cd-a712-ca9b06807fd3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a956796b-4ce2-4988-9d7f-837fbf718fae.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a956796b-4ce2-4988-9d7f-837fbf718fae.lance deleted file mode 100644 index 2c4f8f5db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a956796b-4ce2-4988-9d7f-837fbf718fae.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a95f4df9-54fc-4afc-a443-d9707ca664af.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a95f4df9-54fc-4afc-a443-d9707ca664af.lance deleted file mode 100644 index 55046da41..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a95f4df9-54fc-4afc-a443-d9707ca664af.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a969e1ea-cac9-4b24-bc3d-4b3f941cbd7e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a969e1ea-cac9-4b24-bc3d-4b3f941cbd7e.lance deleted file mode 100644 index fdfcbf817..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a969e1ea-cac9-4b24-bc3d-4b3f941cbd7e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a97edc77-c895-46da-a0df-c5383466e635.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a97edc77-c895-46da-a0df-c5383466e635.lance deleted file mode 100644 index e526fd8be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a97edc77-c895-46da-a0df-c5383466e635.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9ac4468-da16-4bba-a605-3e7fc091c92a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9ac4468-da16-4bba-a605-3e7fc091c92a.lance deleted file mode 100644 index 78d43cbad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9ac4468-da16-4bba-a605-3e7fc091c92a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9c9f627-c454-4b0f-86b5-cecd4234c31c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9c9f627-c454-4b0f-86b5-cecd4234c31c.lance deleted file mode 100644 index 4af879694..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9c9f627-c454-4b0f-86b5-cecd4234c31c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9e7c17d-35ee-4632-bc6a-e1ff86c5bd1b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9e7c17d-35ee-4632-bc6a-e1ff86c5bd1b.lance deleted file mode 100644 index d2a461c00..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9e7c17d-35ee-4632-bc6a-e1ff86c5bd1b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9eeb972-113b-4919-9ea6-9b504900b85c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9eeb972-113b-4919-9ea6-9b504900b85c.lance deleted file mode 100644 index bb746860f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9eeb972-113b-4919-9ea6-9b504900b85c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9fb965d-0e9d-4d5e-98ba-374e20a06199.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9fb965d-0e9d-4d5e-98ba-374e20a06199.lance deleted file mode 100644 index df78e7fbc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/a9fb965d-0e9d-4d5e-98ba-374e20a06199.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa0b47f4-e9ba-45fa-bf8b-a0880d676d8f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa0b47f4-e9ba-45fa-bf8b-a0880d676d8f.lance deleted file mode 100644 index b5920791b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa0b47f4-e9ba-45fa-bf8b-a0880d676d8f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa35ee0f-e5da-44f0-a16b-eea4a9b44456.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa35ee0f-e5da-44f0-a16b-eea4a9b44456.lance deleted file mode 100644 index ec318c2a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa35ee0f-e5da-44f0-a16b-eea4a9b44456.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa64d9cf-dade-4818-a67a-58ffef213825.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa64d9cf-dade-4818-a67a-58ffef213825.lance deleted file mode 100644 index 0f74eab8b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa64d9cf-dade-4818-a67a-58ffef213825.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa6a55b8-5d40-4907-8848-049a983d6a2f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa6a55b8-5d40-4907-8848-049a983d6a2f.lance deleted file mode 100644 index 934c43e24..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa6a55b8-5d40-4907-8848-049a983d6a2f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa74ed5b-0f92-42d5-ab4e-9f4f4c7ba093.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa74ed5b-0f92-42d5-ab4e-9f4f4c7ba093.lance deleted file mode 100644 index 2a81dc5f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa74ed5b-0f92-42d5-ab4e-9f4f4c7ba093.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa7e0c70-e733-4c0c-a652-dcf5911faa05.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa7e0c70-e733-4c0c-a652-dcf5911faa05.lance deleted file mode 100644 index 4fe1bab96..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa7e0c70-e733-4c0c-a652-dcf5911faa05.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa7e8f0e-224c-4c2b-bcda-fa0475eed1ac.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa7e8f0e-224c-4c2b-bcda-fa0475eed1ac.lance deleted file mode 100644 index b4b3ed7fe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa7e8f0e-224c-4c2b-bcda-fa0475eed1ac.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa8e603d-bbc7-4118-bd05-a3620a9ead36.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa8e603d-bbc7-4118-bd05-a3620a9ead36.lance deleted file mode 100644 index a62ad45e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa8e603d-bbc7-4118-bd05-a3620a9ead36.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa972cb6-4dd2-48cc-9ec0-da331c48014e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa972cb6-4dd2-48cc-9ec0-da331c48014e.lance deleted file mode 100644 index 686c2bb0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aa972cb6-4dd2-48cc-9ec0-da331c48014e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aaa43cb4-f60a-4466-b049-43962c06f8f5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aaa43cb4-f60a-4466-b049-43962c06f8f5.lance deleted file mode 100644 index e35ca56f4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aaa43cb4-f60a-4466-b049-43962c06f8f5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aaa98c0d-3bc1-4794-8ef5-240e67e53f30.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aaa98c0d-3bc1-4794-8ef5-240e67e53f30.lance deleted file mode 100644 index ec64d09a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aaa98c0d-3bc1-4794-8ef5-240e67e53f30.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aae0d796-a383-4596-839d-15f65d78d03a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aae0d796-a383-4596-839d-15f65d78d03a.lance deleted file mode 100644 index 77714a454..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aae0d796-a383-4596-839d-15f65d78d03a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aaf87eb9-c73b-4ac1-8a08-3e5e1a860ebe.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aaf87eb9-c73b-4ac1-8a08-3e5e1a860ebe.lance deleted file mode 100644 index 684c1cb59..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aaf87eb9-c73b-4ac1-8a08-3e5e1a860ebe.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab04464a-fb6a-4b3e-a2af-f302c7c7445a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab04464a-fb6a-4b3e-a2af-f302c7c7445a.lance deleted file mode 100644 index 2d4fefa8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab04464a-fb6a-4b3e-a2af-f302c7c7445a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab06b4f9-a516-4fa6-a90f-a2158ff92ed6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab06b4f9-a516-4fa6-a90f-a2158ff92ed6.lance deleted file mode 100644 index afad47aca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab06b4f9-a516-4fa6-a90f-a2158ff92ed6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab124520-a6a2-4dac-bcf5-604cb00ae3cd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab124520-a6a2-4dac-bcf5-604cb00ae3cd.lance deleted file mode 100644 index 39f4cb505..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab124520-a6a2-4dac-bcf5-604cb00ae3cd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab23f9db-0453-4abf-93c9-1706954edcba.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab23f9db-0453-4abf-93c9-1706954edcba.lance deleted file mode 100644 index 93edece90..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab23f9db-0453-4abf-93c9-1706954edcba.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab2473dd-1343-4815-9439-0bc04e8e93de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab2473dd-1343-4815-9439-0bc04e8e93de.lance deleted file mode 100644 index a399a70fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab2473dd-1343-4815-9439-0bc04e8e93de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab38f969-dd6d-43de-9dd4-502eb72826c9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab38f969-dd6d-43de-9dd4-502eb72826c9.lance deleted file mode 100644 index 85deb44ee..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab38f969-dd6d-43de-9dd4-502eb72826c9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab513053-0c32-4540-9dd1-0e49c1cbe773.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab513053-0c32-4540-9dd1-0e49c1cbe773.lance deleted file mode 100644 index 5ddf1b57c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab513053-0c32-4540-9dd1-0e49c1cbe773.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab6a3298-491c-42a1-8e2d-047739e8df81.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab6a3298-491c-42a1-8e2d-047739e8df81.lance deleted file mode 100644 index d74f74630..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ab6a3298-491c-42a1-8e2d-047739e8df81.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aba2ff43-6a0e-4457-916a-778d9a59400e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aba2ff43-6a0e-4457-916a-778d9a59400e.lance deleted file mode 100644 index 4b9c26cfc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aba2ff43-6a0e-4457-916a-778d9a59400e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/abb40f68-dd65-4ed8-b5e2-59804c9d1caf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/abb40f68-dd65-4ed8-b5e2-59804c9d1caf.lance deleted file mode 100644 index df013f74e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/abb40f68-dd65-4ed8-b5e2-59804c9d1caf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/abc6ed5a-efc1-4111-b7a1-2b62abba7a1f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/abc6ed5a-efc1-4111-b7a1-2b62abba7a1f.lance deleted file mode 100644 index 5a6ce3140..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/abc6ed5a-efc1-4111-b7a1-2b62abba7a1f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/abfc0e1c-a256-4a40-8248-5aa8634854a8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/abfc0e1c-a256-4a40-8248-5aa8634854a8.lance deleted file mode 100644 index 353355210..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/abfc0e1c-a256-4a40-8248-5aa8634854a8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac113d4e-672b-42aa-bb70-13fe2f5d472d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac113d4e-672b-42aa-bb70-13fe2f5d472d.lance deleted file mode 100644 index 3edce4fa0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac113d4e-672b-42aa-bb70-13fe2f5d472d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac15bd0d-6380-4cb4-a595-59194b51250e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac15bd0d-6380-4cb4-a595-59194b51250e.lance deleted file mode 100644 index 3fc5f6170..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac15bd0d-6380-4cb4-a595-59194b51250e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac2a05e7-bdea-4471-9b46-377ed15633ce.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac2a05e7-bdea-4471-9b46-377ed15633ce.lance deleted file mode 100644 index aff0cebfa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac2a05e7-bdea-4471-9b46-377ed15633ce.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac376eb5-7201-4d4f-9ad5-d4134296d9f2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac376eb5-7201-4d4f-9ad5-d4134296d9f2.lance deleted file mode 100644 index ab991891f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac376eb5-7201-4d4f-9ad5-d4134296d9f2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac5e8e73-2c9e-4529-8045-14375c73bba5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac5e8e73-2c9e-4529-8045-14375c73bba5.lance deleted file mode 100644 index a8c1a968b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac5e8e73-2c9e-4529-8045-14375c73bba5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac760e23-0717-4233-9512-be080c3fbb7f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac760e23-0717-4233-9512-be080c3fbb7f.lance deleted file mode 100644 index 37f18be85..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac760e23-0717-4233-9512-be080c3fbb7f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac7e3fd0-e22b-4381-84dd-9dc04211ce57.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac7e3fd0-e22b-4381-84dd-9dc04211ce57.lance deleted file mode 100644 index 8152885ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac7e3fd0-e22b-4381-84dd-9dc04211ce57.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac804344-3348-459a-8b71-a98415b02dcb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac804344-3348-459a-8b71-a98415b02dcb.lance deleted file mode 100644 index ed49f6e5f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac804344-3348-459a-8b71-a98415b02dcb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac84c761-5426-49d1-9086-2221d1e6580f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac84c761-5426-49d1-9086-2221d1e6580f.lance deleted file mode 100644 index 82e499ab8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac84c761-5426-49d1-9086-2221d1e6580f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac8d3479-b7be-4dcc-a276-7dfd36a55813.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac8d3479-b7be-4dcc-a276-7dfd36a55813.lance deleted file mode 100644 index 6d5ab3b1a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac8d3479-b7be-4dcc-a276-7dfd36a55813.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac8fef79-fa74-4fa2-b6dd-7299136b4c95.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac8fef79-fa74-4fa2-b6dd-7299136b4c95.lance deleted file mode 100644 index 837c75d9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac8fef79-fa74-4fa2-b6dd-7299136b4c95.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac97a88e-1015-49fc-9bd9-c98f7409181e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac97a88e-1015-49fc-9bd9-c98f7409181e.lance deleted file mode 100644 index d9cd60ea5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ac97a88e-1015-49fc-9bd9-c98f7409181e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aca6a8d3-c97b-4bc8-bcac-fe3a0e207076.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aca6a8d3-c97b-4bc8-bcac-fe3a0e207076.lance deleted file mode 100644 index b5eae008e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aca6a8d3-c97b-4bc8-bcac-fe3a0e207076.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aca87af5-82dc-4977-98f6-add22666c168.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aca87af5-82dc-4977-98f6-add22666c168.lance deleted file mode 100644 index cab1fb6e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aca87af5-82dc-4977-98f6-add22666c168.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/acc46bda-c0ab-4c14-aa16-d85203df44fb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/acc46bda-c0ab-4c14-aa16-d85203df44fb.lance deleted file mode 100644 index b16e8f5ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/acc46bda-c0ab-4c14-aa16-d85203df44fb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/accb4b37-aa95-4a42-b40f-9e207064b642.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/accb4b37-aa95-4a42-b40f-9e207064b642.lance deleted file mode 100644 index 4687cb03d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/accb4b37-aa95-4a42-b40f-9e207064b642.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/accc40b4-1ac7-42f0-bc39-2c33aad840d5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/accc40b4-1ac7-42f0-bc39-2c33aad840d5.lance deleted file mode 100644 index cf33abe55..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/accc40b4-1ac7-42f0-bc39-2c33aad840d5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/acd30806-84df-4838-80c9-79729ee00597.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/acd30806-84df-4838-80c9-79729ee00597.lance deleted file mode 100644 index 2ded579a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/acd30806-84df-4838-80c9-79729ee00597.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/acd84ed5-02bb-44ca-897b-58a34c4893db.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/acd84ed5-02bb-44ca-897b-58a34c4893db.lance deleted file mode 100644 index fdc1a0942..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/acd84ed5-02bb-44ca-897b-58a34c4893db.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ace58460-778d-433b-86c8-3ba3395609e2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ace58460-778d-433b-86c8-3ba3395609e2.lance deleted file mode 100644 index 0ab49804d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ace58460-778d-433b-86c8-3ba3395609e2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ace668c7-6ce8-48e7-87d5-b3015a47d108.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ace668c7-6ce8-48e7-87d5-b3015a47d108.lance deleted file mode 100644 index 373492196..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ace668c7-6ce8-48e7-87d5-b3015a47d108.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/acf02a15-d4b3-4c6d-81cf-0e711646dbd1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/acf02a15-d4b3-4c6d-81cf-0e711646dbd1.lance deleted file mode 100644 index e81661f8d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/acf02a15-d4b3-4c6d-81cf-0e711646dbd1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad0707f9-74d1-4c4a-9f28-fdb081e72100.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad0707f9-74d1-4c4a-9f28-fdb081e72100.lance deleted file mode 100644 index 67ad88d52..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad0707f9-74d1-4c4a-9f28-fdb081e72100.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad0cc7d4-f80f-41e8-8e47-ca96373b9e83.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad0cc7d4-f80f-41e8-8e47-ca96373b9e83.lance deleted file mode 100644 index 4a45b15ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad0cc7d4-f80f-41e8-8e47-ca96373b9e83.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad3ddfa1-b22c-46e6-b3d5-6ccf32852c30.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad3ddfa1-b22c-46e6-b3d5-6ccf32852c30.lance deleted file mode 100644 index 5905ef3ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad3ddfa1-b22c-46e6-b3d5-6ccf32852c30.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad5f59e4-e880-41d6-8831-b697ebfe93e7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad5f59e4-e880-41d6-8831-b697ebfe93e7.lance deleted file mode 100644 index f372ef7e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad5f59e4-e880-41d6-8831-b697ebfe93e7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad7dddb2-4be9-4261-a070-ad053fd67962.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad7dddb2-4be9-4261-a070-ad053fd67962.lance deleted file mode 100644 index 77060af9c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad7dddb2-4be9-4261-a070-ad053fd67962.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad869684-2d0b-4a73-895d-1c83fc4222ff.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad869684-2d0b-4a73-895d-1c83fc4222ff.lance deleted file mode 100644 index 9ca7072a6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad869684-2d0b-4a73-895d-1c83fc4222ff.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad8c4b18-300a-48a6-8549-7155e5513b93.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad8c4b18-300a-48a6-8549-7155e5513b93.lance deleted file mode 100644 index f60d54b4d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ad8c4b18-300a-48a6-8549-7155e5513b93.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ada72059-ae3e-4e42-9c79-c336d89f0f55.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ada72059-ae3e-4e42-9c79-c336d89f0f55.lance deleted file mode 100644 index a79014368..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ada72059-ae3e-4e42-9c79-c336d89f0f55.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/adbe6d98-d842-4dd1-a780-3adfb4412d5e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/adbe6d98-d842-4dd1-a780-3adfb4412d5e.lance deleted file mode 100644 index c050a21c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/adbe6d98-d842-4dd1-a780-3adfb4412d5e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae124df4-9a1a-493a-aa1a-69c0123cfd4e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae124df4-9a1a-493a-aa1a-69c0123cfd4e.lance deleted file mode 100644 index b0740b2b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae124df4-9a1a-493a-aa1a-69c0123cfd4e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae14aeba-8fc8-4b1b-9cc9-df65a5d516c2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae14aeba-8fc8-4b1b-9cc9-df65a5d516c2.lance deleted file mode 100644 index 11dd99be4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae14aeba-8fc8-4b1b-9cc9-df65a5d516c2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae2af27b-8cad-431f-887a-577702f4df1a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae2af27b-8cad-431f-887a-577702f4df1a.lance deleted file mode 100644 index 1d34f1e50..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae2af27b-8cad-431f-887a-577702f4df1a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae4e756b-9447-48a3-b962-e4501c7e2003.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae4e756b-9447-48a3-b962-e4501c7e2003.lance deleted file mode 100644 index e5f79f665..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae4e756b-9447-48a3-b962-e4501c7e2003.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae4ef663-7654-42d4-a3a4-8f544f774f35.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae4ef663-7654-42d4-a3a4-8f544f774f35.lance deleted file mode 100644 index f017b5571..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae4ef663-7654-42d4-a3a4-8f544f774f35.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae597f38-f970-4f0c-8cf1-af60c7c47335.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae597f38-f970-4f0c-8cf1-af60c7c47335.lance deleted file mode 100644 index b1aee0a13..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae597f38-f970-4f0c-8cf1-af60c7c47335.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae756fa6-3fca-47cf-b10f-39200a6707dd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae756fa6-3fca-47cf-b10f-39200a6707dd.lance deleted file mode 100644 index 4ceb4ecbc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae756fa6-3fca-47cf-b10f-39200a6707dd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae86ef17-95d5-4790-8419-ab691c9e7754.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae86ef17-95d5-4790-8419-ab691c9e7754.lance deleted file mode 100644 index b2dda1b1f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ae86ef17-95d5-4790-8419-ab691c9e7754.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aeb21230-ed39-40fa-b93f-1e6d536fc8c8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aeb21230-ed39-40fa-b93f-1e6d536fc8c8.lance deleted file mode 100644 index 484d5f394..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aeb21230-ed39-40fa-b93f-1e6d536fc8c8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aeb47907-20d5-43cf-9410-10ba2d48ec1e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aeb47907-20d5-43cf-9410-10ba2d48ec1e.lance deleted file mode 100644 index c9048bc53..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aeb47907-20d5-43cf-9410-10ba2d48ec1e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aebc774f-75ad-4707-8cfd-0c112908187a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aebc774f-75ad-4707-8cfd-0c112908187a.lance deleted file mode 100644 index df5dda791..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aebc774f-75ad-4707-8cfd-0c112908187a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aede3c4c-5a59-4c0c-98d3-43a5faf15145.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aede3c4c-5a59-4c0c-98d3-43a5faf15145.lance deleted file mode 100644 index 9349903ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aede3c4c-5a59-4c0c-98d3-43a5faf15145.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aefbb23c-3e62-481f-9122-86c793432348.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aefbb23c-3e62-481f-9122-86c793432348.lance deleted file mode 100644 index 3ea0de885..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/aefbb23c-3e62-481f-9122-86c793432348.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af0650aa-4412-4879-98d1-8cd625206218.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af0650aa-4412-4879-98d1-8cd625206218.lance deleted file mode 100644 index cd588b0bf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af0650aa-4412-4879-98d1-8cd625206218.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af2c1630-ff00-40b1-88d4-dfccf2192031.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af2c1630-ff00-40b1-88d4-dfccf2192031.lance deleted file mode 100644 index f9a19f9f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af2c1630-ff00-40b1-88d4-dfccf2192031.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af590f5d-7cc9-45b9-8797-68e9af88bc77.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af590f5d-7cc9-45b9-8797-68e9af88bc77.lance deleted file mode 100644 index 01105b814..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af590f5d-7cc9-45b9-8797-68e9af88bc77.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af72df13-71e0-4ec6-8e3b-071640ac8fa4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af72df13-71e0-4ec6-8e3b-071640ac8fa4.lance deleted file mode 100644 index f715498b6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af72df13-71e0-4ec6-8e3b-071640ac8fa4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af821bda-6012-49b0-b586-b99ff35ffa6d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af821bda-6012-49b0-b586-b99ff35ffa6d.lance deleted file mode 100644 index aec3757ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af821bda-6012-49b0-b586-b99ff35ffa6d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af83038d-1880-4dd5-9897-7c13994d39a4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af83038d-1880-4dd5-9897-7c13994d39a4.lance deleted file mode 100644 index 2b4b8cafa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af83038d-1880-4dd5-9897-7c13994d39a4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af937576-fb8a-451f-9dab-5eeb99939f3d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af937576-fb8a-451f-9dab-5eeb99939f3d.lance deleted file mode 100644 index b416aab0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/af937576-fb8a-451f-9dab-5eeb99939f3d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b00ab26a-4ed3-49c7-8768-298ff02da6a7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b00ab26a-4ed3-49c7-8768-298ff02da6a7.lance deleted file mode 100644 index 27fd90647..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b00ab26a-4ed3-49c7-8768-298ff02da6a7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b01a7c45-8c50-48d7-b464-6d89ce45ba4b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b01a7c45-8c50-48d7-b464-6d89ce45ba4b.lance deleted file mode 100644 index 7e8103899..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b01a7c45-8c50-48d7-b464-6d89ce45ba4b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b01e7d36-64ee-48e0-8b42-a9c9cb912d2f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b01e7d36-64ee-48e0-8b42-a9c9cb912d2f.lance deleted file mode 100644 index b7d52a205..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b01e7d36-64ee-48e0-8b42-a9c9cb912d2f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b024ac7c-ae45-4a4b-acbe-05f05c662963.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b024ac7c-ae45-4a4b-acbe-05f05c662963.lance deleted file mode 100644 index cc64bc30b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b024ac7c-ae45-4a4b-acbe-05f05c662963.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b04cc5c2-ac71-4428-9c4b-9e24615d69bc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b04cc5c2-ac71-4428-9c4b-9e24615d69bc.lance deleted file mode 100644 index ec3417432..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b04cc5c2-ac71-4428-9c4b-9e24615d69bc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0618b4f-3cf5-4bdc-bbbf-d7e1082ba947.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0618b4f-3cf5-4bdc-bbbf-d7e1082ba947.lance deleted file mode 100644 index 3b64439f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0618b4f-3cf5-4bdc-bbbf-d7e1082ba947.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b092a2c9-bc63-4c85-b21c-5225c21da9c9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b092a2c9-bc63-4c85-b21c-5225c21da9c9.lance deleted file mode 100644 index 553cc948b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b092a2c9-bc63-4c85-b21c-5225c21da9c9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0ade819-37d0-4818-80df-bf09f7e01f5e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0ade819-37d0-4818-80df-bf09f7e01f5e.lance deleted file mode 100644 index a03b54803..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0ade819-37d0-4818-80df-bf09f7e01f5e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0c4994d-0195-4c40-9525-e154a583c1f0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0c4994d-0195-4c40-9525-e154a583c1f0.lance deleted file mode 100644 index 4a6b0d8b0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0c4994d-0195-4c40-9525-e154a583c1f0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0d86163-21b3-47e5-83b5-3d5547d11a45.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0d86163-21b3-47e5-83b5-3d5547d11a45.lance deleted file mode 100644 index bad1a4ff2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0d86163-21b3-47e5-83b5-3d5547d11a45.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0df1506-fa25-48bc-b1ff-94f041c6e777.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0df1506-fa25-48bc-b1ff-94f041c6e777.lance deleted file mode 100644 index b4601ccf2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0df1506-fa25-48bc-b1ff-94f041c6e777.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0e19eea-d819-4365-8292-95a59252082d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0e19eea-d819-4365-8292-95a59252082d.lance deleted file mode 100644 index 22eb4f810..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b0e19eea-d819-4365-8292-95a59252082d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b102fad3-16ff-4d19-b852-eeda73e416e3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b102fad3-16ff-4d19-b852-eeda73e416e3.lance deleted file mode 100644 index 9e2e5c2e0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b102fad3-16ff-4d19-b852-eeda73e416e3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b11850ab-a844-4d1d-b854-75787db29940.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b11850ab-a844-4d1d-b854-75787db29940.lance deleted file mode 100644 index 4f3fecd19..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b11850ab-a844-4d1d-b854-75787db29940.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b11c5ef9-85e5-42f5-bacf-ca94e8c95cd6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b11c5ef9-85e5-42f5-bacf-ca94e8c95cd6.lance deleted file mode 100644 index 2ee744920..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b11c5ef9-85e5-42f5-bacf-ca94e8c95cd6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b13aa2ae-a8c1-41b1-840c-bc0d9555a021.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b13aa2ae-a8c1-41b1-840c-bc0d9555a021.lance deleted file mode 100644 index 8bd3239a0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b13aa2ae-a8c1-41b1-840c-bc0d9555a021.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b14d1410-e918-48ac-825b-b51b48fce4db.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b14d1410-e918-48ac-825b-b51b48fce4db.lance deleted file mode 100644 index f7e006e02..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b14d1410-e918-48ac-825b-b51b48fce4db.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b1600f4e-9ddd-48e4-b4f3-6bc8800736c6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b1600f4e-9ddd-48e4-b4f3-6bc8800736c6.lance deleted file mode 100644 index 4b6a5cda1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b1600f4e-9ddd-48e4-b4f3-6bc8800736c6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b1739702-2c2a-4ebc-b820-2fed2582b043.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b1739702-2c2a-4ebc-b820-2fed2582b043.lance deleted file mode 100644 index bca7bd345..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b1739702-2c2a-4ebc-b820-2fed2582b043.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b18052dd-2601-4384-9f15-4b04906ca77c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b18052dd-2601-4384-9f15-4b04906ca77c.lance deleted file mode 100644 index 1993c8f38..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b18052dd-2601-4384-9f15-4b04906ca77c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b191f60c-1355-4267-8ede-263add946268.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b191f60c-1355-4267-8ede-263add946268.lance deleted file mode 100644 index a449b0228..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b191f60c-1355-4267-8ede-263add946268.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b1a7314e-bf2b-40c9-9e52-9e0caad64ad9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b1a7314e-bf2b-40c9-9e52-9e0caad64ad9.lance deleted file mode 100644 index e8d92ce05..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b1a7314e-bf2b-40c9-9e52-9e0caad64ad9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b1dac358-8f79-41f5-b1c5-4ec90cc73a4a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b1dac358-8f79-41f5-b1c5-4ec90cc73a4a.lance deleted file mode 100644 index 1f6f5194a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b1dac358-8f79-41f5-b1c5-4ec90cc73a4a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b213d611-0282-45a4-81dd-d20903ecfd0a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b213d611-0282-45a4-81dd-d20903ecfd0a.lance deleted file mode 100644 index be6900a79..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b213d611-0282-45a4-81dd-d20903ecfd0a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b222b5a8-6f3c-41e2-bd30-61074438dec0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b222b5a8-6f3c-41e2-bd30-61074438dec0.lance deleted file mode 100644 index 9d7aa7e9b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b222b5a8-6f3c-41e2-bd30-61074438dec0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b24edf7f-c588-47ad-affc-cced4d4f7838.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b24edf7f-c588-47ad-affc-cced4d4f7838.lance deleted file mode 100644 index bd8bd674f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b24edf7f-c588-47ad-affc-cced4d4f7838.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2599a6b-3c20-400f-ab55-4a74e64a8924.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2599a6b-3c20-400f-ab55-4a74e64a8924.lance deleted file mode 100644 index 765b8974b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2599a6b-3c20-400f-ab55-4a74e64a8924.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b26defca-a4f4-472a-92b5-79bc7c7d5cfe.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b26defca-a4f4-472a-92b5-79bc7c7d5cfe.lance deleted file mode 100644 index f5d8112e0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b26defca-a4f4-472a-92b5-79bc7c7d5cfe.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b26ea371-5547-46f3-a65c-0f441dc3ccb0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b26ea371-5547-46f3-a65c-0f441dc3ccb0.lance deleted file mode 100644 index 210ded700..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b26ea371-5547-46f3-a65c-0f441dc3ccb0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2710121-845c-4194-b01b-467420a48756.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2710121-845c-4194-b01b-467420a48756.lance deleted file mode 100644 index e71682eb6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2710121-845c-4194-b01b-467420a48756.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b29dc212-9b38-4663-8de6-f5fbab8bcef3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b29dc212-9b38-4663-8de6-f5fbab8bcef3.lance deleted file mode 100644 index fea6e5c47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b29dc212-9b38-4663-8de6-f5fbab8bcef3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2c4996d-38e0-4893-ac97-affd1e105aa5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2c4996d-38e0-4893-ac97-affd1e105aa5.lance deleted file mode 100644 index 7c6bba30e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2c4996d-38e0-4893-ac97-affd1e105aa5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2d9fb37-4c29-4958-9796-5dcf512d0bbf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2d9fb37-4c29-4958-9796-5dcf512d0bbf.lance deleted file mode 100644 index 189785c4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2d9fb37-4c29-4958-9796-5dcf512d0bbf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2e81751-3d40-4f66-a30b-5ca19f72076a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2e81751-3d40-4f66-a30b-5ca19f72076a.lance deleted file mode 100644 index 9f96478ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2e81751-3d40-4f66-a30b-5ca19f72076a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2f6e490-4c63-4a5c-9b1c-0909be73e1f0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2f6e490-4c63-4a5c-9b1c-0909be73e1f0.lance deleted file mode 100644 index f216caa42..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2f6e490-4c63-4a5c-9b1c-0909be73e1f0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2f80f66-1c60-4a3e-824e-8c4f3fa3d8d9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2f80f66-1c60-4a3e-824e-8c4f3fa3d8d9.lance deleted file mode 100644 index 9c8eacc00..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b2f80f66-1c60-4a3e-824e-8c4f3fa3d8d9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b30d49f2-a81c-4b77-be0b-95b054f53b3f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b30d49f2-a81c-4b77-be0b-95b054f53b3f.lance deleted file mode 100644 index 920f6616d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b30d49f2-a81c-4b77-be0b-95b054f53b3f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3222d18-1e80-4e33-bfbe-be7fd40142a9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3222d18-1e80-4e33-bfbe-be7fd40142a9.lance deleted file mode 100644 index 738038106..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3222d18-1e80-4e33-bfbe-be7fd40142a9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b366f671-375f-4325-a41c-39126a5ba9d8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b366f671-375f-4325-a41c-39126a5ba9d8.lance deleted file mode 100644 index c37600b19..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b366f671-375f-4325-a41c-39126a5ba9d8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b37668d9-5f2f-4c6c-84d8-d36884c9a6cb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b37668d9-5f2f-4c6c-84d8-d36884c9a6cb.lance deleted file mode 100644 index 130e686d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b37668d9-5f2f-4c6c-84d8-d36884c9a6cb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b37fb925-da99-4b28-bc8f-cebd49185fcf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b37fb925-da99-4b28-bc8f-cebd49185fcf.lance deleted file mode 100644 index 30681b3e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b37fb925-da99-4b28-bc8f-cebd49185fcf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3a50b79-df6b-49be-a3be-d49f6aa1a9db.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3a50b79-df6b-49be-a3be-d49f6aa1a9db.lance deleted file mode 100644 index c71159a38..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3a50b79-df6b-49be-a3be-d49f6aa1a9db.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3bc8633-7dc0-458d-b5d5-ce527658f43d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3bc8633-7dc0-458d-b5d5-ce527658f43d.lance deleted file mode 100644 index df23da152..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3bc8633-7dc0-458d-b5d5-ce527658f43d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3d10e65-9aa4-4d31-98df-dcd658886279.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3d10e65-9aa4-4d31-98df-dcd658886279.lance deleted file mode 100644 index b92b37654..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3d10e65-9aa4-4d31-98df-dcd658886279.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3d5f8c1-c884-4025-a00a-1788c13483d7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3d5f8c1-c884-4025-a00a-1788c13483d7.lance deleted file mode 100644 index 626744d89..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3d5f8c1-c884-4025-a00a-1788c13483d7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3fbccde-4d85-4264-835d-a66de7989a68.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3fbccde-4d85-4264-835d-a66de7989a68.lance deleted file mode 100644 index 9bfbbd4a6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b3fbccde-4d85-4264-835d-a66de7989a68.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b408b628-af89-4c5e-a65e-48a970f179ae.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b408b628-af89-4c5e-a65e-48a970f179ae.lance deleted file mode 100644 index d5f28698e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b408b628-af89-4c5e-a65e-48a970f179ae.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b41493f6-2a11-4bed-9e03-360470d2d5ac.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b41493f6-2a11-4bed-9e03-360470d2d5ac.lance deleted file mode 100644 index 896cb675b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b41493f6-2a11-4bed-9e03-360470d2d5ac.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4436290-1cd3-40f4-8db0-85b2f174041d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4436290-1cd3-40f4-8db0-85b2f174041d.lance deleted file mode 100644 index 064195949..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4436290-1cd3-40f4-8db0-85b2f174041d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4594aab-1375-47b4-83cf-d1435aa9759e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4594aab-1375-47b4-83cf-d1435aa9759e.lance deleted file mode 100644 index 21a7f5a2f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4594aab-1375-47b4-83cf-d1435aa9759e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4802b19-894a-40f3-9382-249facf5893f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4802b19-894a-40f3-9382-249facf5893f.lance deleted file mode 100644 index 7d0368cca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4802b19-894a-40f3-9382-249facf5893f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b48bc1f9-ca5e-49ca-ae93-c05d327f925e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b48bc1f9-ca5e-49ca-ae93-c05d327f925e.lance deleted file mode 100644 index 9f1cda8fd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b48bc1f9-ca5e-49ca-ae93-c05d327f925e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4979ed9-18fd-4b0b-972d-7770f7e06752.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4979ed9-18fd-4b0b-972d-7770f7e06752.lance deleted file mode 100644 index 830bc4747..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4979ed9-18fd-4b0b-972d-7770f7e06752.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4a26d2a-f27e-4db9-a450-de6e1855b9dd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4a26d2a-f27e-4db9-a450-de6e1855b9dd.lance deleted file mode 100644 index 20cbbf8b0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4a26d2a-f27e-4db9-a450-de6e1855b9dd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4b2be1d-ff7d-4c3f-bd10-5b61491b80d2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4b2be1d-ff7d-4c3f-bd10-5b61491b80d2.lance deleted file mode 100644 index 369d0b581..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4b2be1d-ff7d-4c3f-bd10-5b61491b80d2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4bb238c-e117-4ac5-b325-f84d8df4ad72.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4bb238c-e117-4ac5-b325-f84d8df4ad72.lance deleted file mode 100644 index d9d968a55..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4bb238c-e117-4ac5-b325-f84d8df4ad72.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4ceddf0-ce19-49d7-8377-9e274d5e8177.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4ceddf0-ce19-49d7-8377-9e274d5e8177.lance deleted file mode 100644 index dd6b88df4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b4ceddf0-ce19-49d7-8377-9e274d5e8177.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b52a0cfc-0900-4408-8132-57dda1043ef5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b52a0cfc-0900-4408-8132-57dda1043ef5.lance deleted file mode 100644 index 44724f6a6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b52a0cfc-0900-4408-8132-57dda1043ef5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b54d5679-da71-462e-a9a1-f75c5460f7a5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b54d5679-da71-462e-a9a1-f75c5460f7a5.lance deleted file mode 100644 index 5fa8fc84d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b54d5679-da71-462e-a9a1-f75c5460f7a5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b583ff39-923d-4144-bb95-6b82e6f98a8e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b583ff39-923d-4144-bb95-6b82e6f98a8e.lance deleted file mode 100644 index c6d05a76a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b583ff39-923d-4144-bb95-6b82e6f98a8e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b587fd69-dc77-4338-9617-504743ea9ff1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b587fd69-dc77-4338-9617-504743ea9ff1.lance deleted file mode 100644 index acfc02860..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b587fd69-dc77-4338-9617-504743ea9ff1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b58e9335-c20a-4956-92dd-71fd5ccb2ee2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b58e9335-c20a-4956-92dd-71fd5ccb2ee2.lance deleted file mode 100644 index ce852ba34..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b58e9335-c20a-4956-92dd-71fd5ccb2ee2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b59b069f-4da3-427a-b1d3-b8c9361ad044.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b59b069f-4da3-427a-b1d3-b8c9361ad044.lance deleted file mode 100644 index fdee2ecb0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b59b069f-4da3-427a-b1d3-b8c9361ad044.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b5a55c71-1c2d-4ec3-9296-93a5fb843b08.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b5a55c71-1c2d-4ec3-9296-93a5fb843b08.lance deleted file mode 100644 index 8fca97448..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b5a55c71-1c2d-4ec3-9296-93a5fb843b08.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b5bf04b9-6f52-4aad-8b86-7472cb146d2d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b5bf04b9-6f52-4aad-8b86-7472cb146d2d.lance deleted file mode 100644 index e2f230421..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b5bf04b9-6f52-4aad-8b86-7472cb146d2d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b5cd9bbc-b0f0-4cde-86ef-f563b1cd4041.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b5cd9bbc-b0f0-4cde-86ef-f563b1cd4041.lance deleted file mode 100644 index 30f52bd8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b5cd9bbc-b0f0-4cde-86ef-f563b1cd4041.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b5d77a1b-3520-4f13-8753-5c02a95c09e2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b5d77a1b-3520-4f13-8753-5c02a95c09e2.lance deleted file mode 100644 index a3b4150b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b5d77a1b-3520-4f13-8753-5c02a95c09e2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b602e004-5570-4dd9-b216-6fc60b9baa8e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b602e004-5570-4dd9-b216-6fc60b9baa8e.lance deleted file mode 100644 index f81de9d40..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b602e004-5570-4dd9-b216-6fc60b9baa8e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6043c5b-93d4-4042-bf50-93e803d0fee3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6043c5b-93d4-4042-bf50-93e803d0fee3.lance deleted file mode 100644 index 44a08fa1a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6043c5b-93d4-4042-bf50-93e803d0fee3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b60f3058-af87-48cc-972b-42645159ca27.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b60f3058-af87-48cc-972b-42645159ca27.lance deleted file mode 100644 index d53b8eca5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b60f3058-af87-48cc-972b-42645159ca27.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b61004a9-72e4-4362-ad89-06e2f3fe9277.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b61004a9-72e4-4362-ad89-06e2f3fe9277.lance deleted file mode 100644 index 6479ab1da..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b61004a9-72e4-4362-ad89-06e2f3fe9277.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6193715-6b64-4b19-999b-78a6e803711c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6193715-6b64-4b19-999b-78a6e803711c.lance deleted file mode 100644 index 7d257c434..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6193715-6b64-4b19-999b-78a6e803711c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b61d965a-bb17-44b1-981e-eab38bf7c2dd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b61d965a-bb17-44b1-981e-eab38bf7c2dd.lance deleted file mode 100644 index 486897776..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b61d965a-bb17-44b1-981e-eab38bf7c2dd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6284544-053c-44ca-8199-e9fec0291799.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6284544-053c-44ca-8199-e9fec0291799.lance deleted file mode 100644 index 5adfc9641..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6284544-053c-44ca-8199-e9fec0291799.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b653ffd7-a9b8-48df-9789-9f589b14748b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b653ffd7-a9b8-48df-9789-9f589b14748b.lance deleted file mode 100644 index a9b7889bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b653ffd7-a9b8-48df-9789-9f589b14748b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b65fcdef-3849-4484-9b80-e57e8e49c74c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b65fcdef-3849-4484-9b80-e57e8e49c74c.lance deleted file mode 100644 index 51004f8b7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b65fcdef-3849-4484-9b80-e57e8e49c74c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b66a1e16-2608-48b8-a35b-32abf77d80fb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b66a1e16-2608-48b8-a35b-32abf77d80fb.lance deleted file mode 100644 index a8879a6d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b66a1e16-2608-48b8-a35b-32abf77d80fb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b68df0ca-5a1a-47e2-8617-bab4d50af798.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b68df0ca-5a1a-47e2-8617-bab4d50af798.lance deleted file mode 100644 index 012837c58..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b68df0ca-5a1a-47e2-8617-bab4d50af798.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6912cb5-bbbf-45ae-a2b9-2f062d2d236b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6912cb5-bbbf-45ae-a2b9-2f062d2d236b.lance deleted file mode 100644 index 4c898ddd8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6912cb5-bbbf-45ae-a2b9-2f062d2d236b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6b651c7-de27-4658-80b0-b473d118c31a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6b651c7-de27-4658-80b0-b473d118c31a.lance deleted file mode 100644 index 26389a20d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6b651c7-de27-4658-80b0-b473d118c31a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6e3738e-9c9a-4f1b-9ce1-62ad46f4a1ca.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6e3738e-9c9a-4f1b-9ce1-62ad46f4a1ca.lance deleted file mode 100644 index 636ce5276..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b6e3738e-9c9a-4f1b-9ce1-62ad46f4a1ca.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b732f8e8-fb51-4a17-a04c-057f54033789.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b732f8e8-fb51-4a17-a04c-057f54033789.lance deleted file mode 100644 index ae86c0519..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b732f8e8-fb51-4a17-a04c-057f54033789.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b753727d-d245-4efd-98c0-1e9440c3d340.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b753727d-d245-4efd-98c0-1e9440c3d340.lance deleted file mode 100644 index 1649a53d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b753727d-d245-4efd-98c0-1e9440c3d340.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7586138-6051-4f90-bbbb-9e31cac90a20.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7586138-6051-4f90-bbbb-9e31cac90a20.lance deleted file mode 100644 index b6d65883c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7586138-6051-4f90-bbbb-9e31cac90a20.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b75f2b4d-8e70-4f8d-bbef-c7a127bb48b3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b75f2b4d-8e70-4f8d-bbef-c7a127bb48b3.lance deleted file mode 100644 index 92c52daca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b75f2b4d-8e70-4f8d-bbef-c7a127bb48b3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7681758-909c-4c30-afe7-aaec9514a145.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7681758-909c-4c30-afe7-aaec9514a145.lance deleted file mode 100644 index 940a642f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7681758-909c-4c30-afe7-aaec9514a145.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7a36b5d-b594-4f52-a9fd-a3208b8b8041.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7a36b5d-b594-4f52-a9fd-a3208b8b8041.lance deleted file mode 100644 index c62e355aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7a36b5d-b594-4f52-a9fd-a3208b8b8041.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7a7bacc-6448-4699-a86b-d6b394c0222f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7a7bacc-6448-4699-a86b-d6b394c0222f.lance deleted file mode 100644 index ecffca6de..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7a7bacc-6448-4699-a86b-d6b394c0222f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7e11471-69dd-466b-af83-af00359415b6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7e11471-69dd-466b-af83-af00359415b6.lance deleted file mode 100644 index 4a55ced84..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7e11471-69dd-466b-af83-af00359415b6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7f54ccd-bd1a-4263-829a-f1897513781b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7f54ccd-bd1a-4263-829a-f1897513781b.lance deleted file mode 100644 index 28c77c1c5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b7f54ccd-bd1a-4263-829a-f1897513781b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b80f4c58-e711-4464-b81d-20e050b51753.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b80f4c58-e711-4464-b81d-20e050b51753.lance deleted file mode 100644 index fc036377b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b80f4c58-e711-4464-b81d-20e050b51753.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b83ffebb-f421-456e-8948-dfcbf3218cac.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b83ffebb-f421-456e-8948-dfcbf3218cac.lance deleted file mode 100644 index 5fec82682..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b83ffebb-f421-456e-8948-dfcbf3218cac.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b86c64f7-6b9e-4806-ab1f-96b6ae8025cd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b86c64f7-6b9e-4806-ab1f-96b6ae8025cd.lance deleted file mode 100644 index 20cd51e4c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b86c64f7-6b9e-4806-ab1f-96b6ae8025cd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b86df23d-8e54-4904-983c-602dc7d40dea.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b86df23d-8e54-4904-983c-602dc7d40dea.lance deleted file mode 100644 index 833c3fe48..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b86df23d-8e54-4904-983c-602dc7d40dea.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b89026c7-c4c9-4ed5-bc18-54d42ea7cac3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b89026c7-c4c9-4ed5-bc18-54d42ea7cac3.lance deleted file mode 100644 index 7c791ec4f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b89026c7-c4c9-4ed5-bc18-54d42ea7cac3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b89095c3-5d4e-493f-b0d5-48bb82872121.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b89095c3-5d4e-493f-b0d5-48bb82872121.lance deleted file mode 100644 index 9161065be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b89095c3-5d4e-493f-b0d5-48bb82872121.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8ac228f-622d-47a9-830c-0df253731708.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8ac228f-622d-47a9-830c-0df253731708.lance deleted file mode 100644 index c944fdd60..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8ac228f-622d-47a9-830c-0df253731708.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8acd42f-965b-467b-bf94-785a5150c3fb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8acd42f-965b-467b-bf94-785a5150c3fb.lance deleted file mode 100644 index 05994fa4a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8acd42f-965b-467b-bf94-785a5150c3fb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8cee843-12be-4e53-8b0d-a032fb317f12.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8cee843-12be-4e53-8b0d-a032fb317f12.lance deleted file mode 100644 index 09362dcc8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8cee843-12be-4e53-8b0d-a032fb317f12.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8d4362f-a2f8-4d2b-bd0d-37ce937b9bbf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8d4362f-a2f8-4d2b-bd0d-37ce937b9bbf.lance deleted file mode 100644 index 18c250939..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8d4362f-a2f8-4d2b-bd0d-37ce937b9bbf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8e5185f-c0ed-4e25-9861-46697f8788be.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8e5185f-c0ed-4e25-9861-46697f8788be.lance deleted file mode 100644 index 3df487a75..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8e5185f-c0ed-4e25-9861-46697f8788be.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8f7cf83-fe15-4ec2-a547-fcac11828e24.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8f7cf83-fe15-4ec2-a547-fcac11828e24.lance deleted file mode 100644 index c1fd0af71..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b8f7cf83-fe15-4ec2-a547-fcac11828e24.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b90138c2-76a1-40f4-866c-cb3f7215fabd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b90138c2-76a1-40f4-866c-cb3f7215fabd.lance deleted file mode 100644 index 847cd66c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b90138c2-76a1-40f4-866c-cb3f7215fabd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9050958-3617-4fe8-8142-de3452bcd48f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9050958-3617-4fe8-8142-de3452bcd48f.lance deleted file mode 100644 index b0c2dbb4e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9050958-3617-4fe8-8142-de3452bcd48f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b91c23f4-27af-434f-aaa1-409afea77d85.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b91c23f4-27af-434f-aaa1-409afea77d85.lance deleted file mode 100644 index 14c0e3c9d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b91c23f4-27af-434f-aaa1-409afea77d85.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b91d002d-d5d0-447e-8e3c-9a4bd3bec384.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b91d002d-d5d0-447e-8e3c-9a4bd3bec384.lance deleted file mode 100644 index 610fe28ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b91d002d-d5d0-447e-8e3c-9a4bd3bec384.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9204318-678c-4735-b94b-9fbefd3fb15b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9204318-678c-4735-b94b-9fbefd3fb15b.lance deleted file mode 100644 index 4911696ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9204318-678c-4735-b94b-9fbefd3fb15b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9249f79-a671-4937-bdc9-5818a589977b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9249f79-a671-4937-bdc9-5818a589977b.lance deleted file mode 100644 index 85e6662c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9249f79-a671-4937-bdc9-5818a589977b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b940e9f9-d0f1-4389-babf-3587f8184cfa.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b940e9f9-d0f1-4389-babf-3587f8184cfa.lance deleted file mode 100644 index 35cb14fd9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b940e9f9-d0f1-4389-babf-3587f8184cfa.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b94f14e6-36ba-4594-a81d-979b52eda84b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b94f14e6-36ba-4594-a81d-979b52eda84b.lance deleted file mode 100644 index e110bdccd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b94f14e6-36ba-4594-a81d-979b52eda84b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b94f6fe4-1825-4f32-9de3-a370384323c2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b94f6fe4-1825-4f32-9de3-a370384323c2.lance deleted file mode 100644 index 8b404f68d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b94f6fe4-1825-4f32-9de3-a370384323c2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b979a259-1920-4392-8a1c-b5264b6b514d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b979a259-1920-4392-8a1c-b5264b6b514d.lance deleted file mode 100644 index bffc00e66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b979a259-1920-4392-8a1c-b5264b6b514d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b98ab5ad-4079-4e1a-9d42-5d26697869b2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b98ab5ad-4079-4e1a-9d42-5d26697869b2.lance deleted file mode 100644 index 7f8bc1ce3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b98ab5ad-4079-4e1a-9d42-5d26697869b2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9970a46-50fa-4d1f-9c95-6b5b91a822cd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9970a46-50fa-4d1f-9c95-6b5b91a822cd.lance deleted file mode 100644 index 6212a41fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9970a46-50fa-4d1f-9c95-6b5b91a822cd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b998b195-57e3-4066-baf2-22118e5f5b5f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b998b195-57e3-4066-baf2-22118e5f5b5f.lance deleted file mode 100644 index 1a9a5828c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b998b195-57e3-4066-baf2-22118e5f5b5f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9c58ff5-17d9-4d32-a034-80a11f4fa2c8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9c58ff5-17d9-4d32-a034-80a11f4fa2c8.lance deleted file mode 100644 index 7298e6611..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9c58ff5-17d9-4d32-a034-80a11f4fa2c8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9e895f6-ddce-48b6-921d-b42cdbc5e173.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9e895f6-ddce-48b6-921d-b42cdbc5e173.lance deleted file mode 100644 index b3c493591..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9e895f6-ddce-48b6-921d-b42cdbc5e173.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9eceb92-325d-4ee0-9d08-e9c5bcc4bcff.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9eceb92-325d-4ee0-9d08-e9c5bcc4bcff.lance deleted file mode 100644 index b65103359..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9eceb92-325d-4ee0-9d08-e9c5bcc4bcff.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9f47fa9-c67d-4174-b2b2-c2706ea23240.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9f47fa9-c67d-4174-b2b2-c2706ea23240.lance deleted file mode 100644 index b60047234..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/b9f47fa9-c67d-4174-b2b2-c2706ea23240.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba0278f7-cde2-43ce-a790-d5f1aec0fdeb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba0278f7-cde2-43ce-a790-d5f1aec0fdeb.lance deleted file mode 100644 index b123f7370..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba0278f7-cde2-43ce-a790-d5f1aec0fdeb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba1f70f8-dae3-4149-ab86-6611da5095cb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba1f70f8-dae3-4149-ab86-6611da5095cb.lance deleted file mode 100644 index 7f146a77c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba1f70f8-dae3-4149-ab86-6611da5095cb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba3f8509-8e1b-4ce5-a397-0b0f1e4d8512.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba3f8509-8e1b-4ce5-a397-0b0f1e4d8512.lance deleted file mode 100644 index 8093264ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba3f8509-8e1b-4ce5-a397-0b0f1e4d8512.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba63bce4-0791-49ca-ad4f-cdc03866b6a5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba63bce4-0791-49ca-ad4f-cdc03866b6a5.lance deleted file mode 100644 index defe628b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba63bce4-0791-49ca-ad4f-cdc03866b6a5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba6e737d-1a10-4ca5-8f7f-f2f3aeb99a80.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba6e737d-1a10-4ca5-8f7f-f2f3aeb99a80.lance deleted file mode 100644 index 3d0af407c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba6e737d-1a10-4ca5-8f7f-f2f3aeb99a80.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba819d56-8dbc-4f7b-96ae-251dc4e2270c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba819d56-8dbc-4f7b-96ae-251dc4e2270c.lance deleted file mode 100644 index 02c5d0943..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ba819d56-8dbc-4f7b-96ae-251dc4e2270c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/baa8345c-43eb-43c5-8b35-f7519b5ea37e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/baa8345c-43eb-43c5-8b35-f7519b5ea37e.lance deleted file mode 100644 index 6e7660381..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/baa8345c-43eb-43c5-8b35-f7519b5ea37e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/baaa1e4c-804d-401c-ace1-77501ad8459e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/baaa1e4c-804d-401c-ace1-77501ad8459e.lance deleted file mode 100644 index c7e6643e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/baaa1e4c-804d-401c-ace1-77501ad8459e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/baead722-1b88-4227-b85b-ea2a1c0f10a0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/baead722-1b88-4227-b85b-ea2a1c0f10a0.lance deleted file mode 100644 index d9c2482ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/baead722-1b88-4227-b85b-ea2a1c0f10a0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bb803854-d058-42fc-8aa7-95c6de0acd14.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bb803854-d058-42fc-8aa7-95c6de0acd14.lance deleted file mode 100644 index cb4666252..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bb803854-d058-42fc-8aa7-95c6de0acd14.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbb4fc45-2946-431d-9eea-70207d487c68.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbb4fc45-2946-431d-9eea-70207d487c68.lance deleted file mode 100644 index f8152897e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbb4fc45-2946-431d-9eea-70207d487c68.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbbf0ad1-197d-4fef-a6d3-5c38724d4102.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbbf0ad1-197d-4fef-a6d3-5c38724d4102.lance deleted file mode 100644 index bd47e7a9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbbf0ad1-197d-4fef-a6d3-5c38724d4102.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbc60046-296a-42a5-ac7f-cdfd44c5e8cc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbc60046-296a-42a5-ac7f-cdfd44c5e8cc.lance deleted file mode 100644 index f4fa07c0e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbc60046-296a-42a5-ac7f-cdfd44c5e8cc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbc6de9d-6ae0-4d6d-bbf6-20921666261c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbc6de9d-6ae0-4d6d-bbf6-20921666261c.lance deleted file mode 100644 index 9c45ab80a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbc6de9d-6ae0-4d6d-bbf6-20921666261c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbcdd6ac-3f88-41c5-beed-42ccced954aa.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbcdd6ac-3f88-41c5-beed-42ccced954aa.lance deleted file mode 100644 index 52fe6912c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbcdd6ac-3f88-41c5-beed-42ccced954aa.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbe30263-6109-450e-9abb-5bbbac1ba45b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbe30263-6109-450e-9abb-5bbbac1ba45b.lance deleted file mode 100644 index cdf971cf7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbe30263-6109-450e-9abb-5bbbac1ba45b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbe57b44-269e-4b44-8bba-7e36f1ae9703.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbe57b44-269e-4b44-8bba-7e36f1ae9703.lance deleted file mode 100644 index 1d22bc896..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbe57b44-269e-4b44-8bba-7e36f1ae9703.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbe9c97e-4def-42e3-958d-5874be0ca40f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbe9c97e-4def-42e3-958d-5874be0ca40f.lance deleted file mode 100644 index 12e1128df..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbe9c97e-4def-42e3-958d-5874be0ca40f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbee722a-9821-4e30-b8f8-24d4567d5797.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbee722a-9821-4e30-b8f8-24d4567d5797.lance deleted file mode 100644 index 5f74835ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbee722a-9821-4e30-b8f8-24d4567d5797.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbefb065-00dd-4195-b82e-2e8bde67160e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbefb065-00dd-4195-b82e-2e8bde67160e.lance deleted file mode 100644 index 6c52f4a6f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bbefb065-00dd-4195-b82e-2e8bde67160e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bc4821ca-5fed-45a6-ae5e-528745ed2bb1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bc4821ca-5fed-45a6-ae5e-528745ed2bb1.lance deleted file mode 100644 index 90c5cafb5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bc4821ca-5fed-45a6-ae5e-528745ed2bb1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bc548a6f-b5c9-4ad5-b503-a30e15ec777b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bc548a6f-b5c9-4ad5-b503-a30e15ec777b.lance deleted file mode 100644 index 00578501f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bc548a6f-b5c9-4ad5-b503-a30e15ec777b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bc6490d5-3a5f-4d91-8ed7-fea2c071a80f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bc6490d5-3a5f-4d91-8ed7-fea2c071a80f.lance deleted file mode 100644 index 16af1c855..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bc6490d5-3a5f-4d91-8ed7-fea2c071a80f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bc6c6573-2864-481c-a7f8-21013cb98a17.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bc6c6573-2864-481c-a7f8-21013cb98a17.lance deleted file mode 100644 index 8a32c995b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bc6c6573-2864-481c-a7f8-21013cb98a17.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bc752a1f-62d1-4056-95f7-a0fe8f8af582.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bc752a1f-62d1-4056-95f7-a0fe8f8af582.lance deleted file mode 100644 index baa07ac3d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bc752a1f-62d1-4056-95f7-a0fe8f8af582.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bca75108-593f-49f6-8dd9-3a7a0567ff75.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bca75108-593f-49f6-8dd9-3a7a0567ff75.lance deleted file mode 100644 index c2ebb9257..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bca75108-593f-49f6-8dd9-3a7a0567ff75.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bcb5c481-0f7d-4fea-8832-371453cdb81a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bcb5c481-0f7d-4fea-8832-371453cdb81a.lance deleted file mode 100644 index bb8d83353..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bcb5c481-0f7d-4fea-8832-371453cdb81a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bcdd615f-f8bd-4bce-bcee-cd3a1ccd35d0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bcdd615f-f8bd-4bce-bcee-cd3a1ccd35d0.lance deleted file mode 100644 index f3e76f794..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bcdd615f-f8bd-4bce-bcee-cd3a1ccd35d0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bcee6a4a-2d70-414f-9f60-59930e99a805.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bcee6a4a-2d70-414f-9f60-59930e99a805.lance deleted file mode 100644 index 53d88446b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bcee6a4a-2d70-414f-9f60-59930e99a805.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bd2e5fc9-0a2e-4c43-a51b-7379ce20be7e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bd2e5fc9-0a2e-4c43-a51b-7379ce20be7e.lance deleted file mode 100644 index b60cb41c2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bd2e5fc9-0a2e-4c43-a51b-7379ce20be7e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bd67cc32-e4c4-47a6-9751-b2cc0ca5f831.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bd67cc32-e4c4-47a6-9751-b2cc0ca5f831.lance deleted file mode 100644 index e1fb8f74a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bd67cc32-e4c4-47a6-9751-b2cc0ca5f831.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bd6df254-59cf-4de4-8fbc-c3e902bc4369.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bd6df254-59cf-4de4-8fbc-c3e902bc4369.lance deleted file mode 100644 index 85fd4151e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bd6df254-59cf-4de4-8fbc-c3e902bc4369.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bd92e188-ccfa-4f07-8b5b-971d84b77fab.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bd92e188-ccfa-4f07-8b5b-971d84b77fab.lance deleted file mode 100644 index d3bc487a0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bd92e188-ccfa-4f07-8b5b-971d84b77fab.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bda08c5a-51dd-4289-a2b1-534bd743d53e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bda08c5a-51dd-4289-a2b1-534bd743d53e.lance deleted file mode 100644 index d5cba2d18..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bda08c5a-51dd-4289-a2b1-534bd743d53e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bdbb92de-e495-478f-97d4-824615f4aa6e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bdbb92de-e495-478f-97d4-824615f4aa6e.lance deleted file mode 100644 index ec38601ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bdbb92de-e495-478f-97d4-824615f4aa6e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bdd05a4c-379e-4ba5-8aff-cdbaf206b2b7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bdd05a4c-379e-4ba5-8aff-cdbaf206b2b7.lance deleted file mode 100644 index 600d49b27..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bdd05a4c-379e-4ba5-8aff-cdbaf206b2b7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bddd00bf-66b1-4eae-874d-645435c4aa43.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bddd00bf-66b1-4eae-874d-645435c4aa43.lance deleted file mode 100644 index 456872aaa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bddd00bf-66b1-4eae-874d-645435c4aa43.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be335674-515c-42fc-a3bb-7f859c5b8523.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be335674-515c-42fc-a3bb-7f859c5b8523.lance deleted file mode 100644 index 0e7b201e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be335674-515c-42fc-a3bb-7f859c5b8523.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be4fc9df-dd9f-4c99-852c-1f5f48d26c06.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be4fc9df-dd9f-4c99-852c-1f5f48d26c06.lance deleted file mode 100644 index 3b33de54d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be4fc9df-dd9f-4c99-852c-1f5f48d26c06.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be6560cf-dd38-4da7-86bb-564b16573e21.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be6560cf-dd38-4da7-86bb-564b16573e21.lance deleted file mode 100644 index 470bc7fb7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be6560cf-dd38-4da7-86bb-564b16573e21.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be671f3f-4fec-48ee-9b55-b6b89824c22f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be671f3f-4fec-48ee-9b55-b6b89824c22f.lance deleted file mode 100644 index 3147dd772..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be671f3f-4fec-48ee-9b55-b6b89824c22f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be6b0907-a183-42d9-919e-4ca5bed6c9af.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be6b0907-a183-42d9-919e-4ca5bed6c9af.lance deleted file mode 100644 index 8463f9a89..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be6b0907-a183-42d9-919e-4ca5bed6c9af.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be75027f-4138-4d65-9de3-b3d8470034cb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be75027f-4138-4d65-9de3-b3d8470034cb.lance deleted file mode 100644 index 4f9f3545a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be75027f-4138-4d65-9de3-b3d8470034cb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be934503-a083-4fbe-afcc-6e4559d8d82e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be934503-a083-4fbe-afcc-6e4559d8d82e.lance deleted file mode 100644 index 5dcc9918a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/be934503-a083-4fbe-afcc-6e4559d8d82e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/beb6d29a-4ba5-4927-bb20-aa0362d3f4d0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/beb6d29a-4ba5-4927-bb20-aa0362d3f4d0.lance deleted file mode 100644 index a5a80896d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/beb6d29a-4ba5-4927-bb20-aa0362d3f4d0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/becb0761-848f-4d52-88bc-934bfeee2ae1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/becb0761-848f-4d52-88bc-934bfeee2ae1.lance deleted file mode 100644 index ede89e47e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/becb0761-848f-4d52-88bc-934bfeee2ae1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/beeba58f-4708-476c-bc67-55dcb850fb0b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/beeba58f-4708-476c-bc67-55dcb850fb0b.lance deleted file mode 100644 index 7d3908717..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/beeba58f-4708-476c-bc67-55dcb850fb0b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/beeda672-a416-41a6-b970-c2936bbeeb58.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/beeda672-a416-41a6-b970-c2936bbeeb58.lance deleted file mode 100644 index a02030baa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/beeda672-a416-41a6-b970-c2936bbeeb58.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/befe96c1-de1e-40e5-acb4-ba3947989e20.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/befe96c1-de1e-40e5-acb4-ba3947989e20.lance deleted file mode 100644 index b1f6e4f93..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/befe96c1-de1e-40e5-acb4-ba3947989e20.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf02a40d-5c2d-4601-8d40-ca144da66131.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf02a40d-5c2d-4601-8d40-ca144da66131.lance deleted file mode 100644 index c788b5177..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf02a40d-5c2d-4601-8d40-ca144da66131.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf0be623-3478-4a08-bb8a-61c9094917f3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf0be623-3478-4a08-bb8a-61c9094917f3.lance deleted file mode 100644 index 766531dd8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf0be623-3478-4a08-bb8a-61c9094917f3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf2b4795-9804-4807-9c3b-42c72c0bad08.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf2b4795-9804-4807-9c3b-42c72c0bad08.lance deleted file mode 100644 index b5c45dbd8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf2b4795-9804-4807-9c3b-42c72c0bad08.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf568c5a-4a58-4265-bcf4-1be327e23f37.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf568c5a-4a58-4265-bcf4-1be327e23f37.lance deleted file mode 100644 index ce684abf9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf568c5a-4a58-4265-bcf4-1be327e23f37.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf6f5a9b-4520-48b0-8e52-b3cd323ee511.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf6f5a9b-4520-48b0-8e52-b3cd323ee511.lance deleted file mode 100644 index b575a4ea6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf6f5a9b-4520-48b0-8e52-b3cd323ee511.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf70c6db-294e-45d3-a601-867b197096a0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf70c6db-294e-45d3-a601-867b197096a0.lance deleted file mode 100644 index f98ad386c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf70c6db-294e-45d3-a601-867b197096a0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf9dbb06-4215-4ff3-8987-34ae2bbff38c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf9dbb06-4215-4ff3-8987-34ae2bbff38c.lance deleted file mode 100644 index a828c905c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bf9dbb06-4215-4ff3-8987-34ae2bbff38c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfa5dd18-25be-4905-b910-5848dfb39e79.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfa5dd18-25be-4905-b910-5848dfb39e79.lance deleted file mode 100644 index e64c5c44d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfa5dd18-25be-4905-b910-5848dfb39e79.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfbb29ec-1b07-4590-809d-693b73181dc8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfbb29ec-1b07-4590-809d-693b73181dc8.lance deleted file mode 100644 index 2f2d72ddb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfbb29ec-1b07-4590-809d-693b73181dc8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfbf84f3-33f4-4f1a-8d8b-9dcb62f2a645.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfbf84f3-33f4-4f1a-8d8b-9dcb62f2a645.lance deleted file mode 100644 index 65619c5e5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfbf84f3-33f4-4f1a-8d8b-9dcb62f2a645.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfc8232d-1f0c-41e2-997c-2cd39de83f0b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfc8232d-1f0c-41e2-997c-2cd39de83f0b.lance deleted file mode 100644 index ad2d6cc24..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfc8232d-1f0c-41e2-997c-2cd39de83f0b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfdced15-2a98-4f2f-87f7-de6756ffa188.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfdced15-2a98-4f2f-87f7-de6756ffa188.lance deleted file mode 100644 index a881b9505..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfdced15-2a98-4f2f-87f7-de6756ffa188.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfe2d4a9-0452-43be-868c-9b3e87b4ec5b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfe2d4a9-0452-43be-868c-9b3e87b4ec5b.lance deleted file mode 100644 index 06dc681fe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfe2d4a9-0452-43be-868c-9b3e87b4ec5b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfe93ef7-8079-4982-a288-6eefabb4d9b5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfe93ef7-8079-4982-a288-6eefabb4d9b5.lance deleted file mode 100644 index ee23d0286..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bfe93ef7-8079-4982-a288-6eefabb4d9b5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bff7ff32-86b3-443f-b8ad-ff0318da6990.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bff7ff32-86b3-443f-b8ad-ff0318da6990.lance deleted file mode 100644 index 3b68ec3fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bff7ff32-86b3-443f-b8ad-ff0318da6990.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bffce1ce-ec9c-4f80-a4cd-d3ee0fc7bc83.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bffce1ce-ec9c-4f80-a4cd-d3ee0fc7bc83.lance deleted file mode 100644 index 64984368a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/bffce1ce-ec9c-4f80-a4cd-d3ee0fc7bc83.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c00d4dd3-cccd-4f4e-b3c9-01e5f629e768.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c00d4dd3-cccd-4f4e-b3c9-01e5f629e768.lance deleted file mode 100644 index 702f1abbd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c00d4dd3-cccd-4f4e-b3c9-01e5f629e768.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c01c6a22-7585-4520-91a0-2429467046b7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c01c6a22-7585-4520-91a0-2429467046b7.lance deleted file mode 100644 index 96b25cc56..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c01c6a22-7585-4520-91a0-2429467046b7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c01e58bb-ea52-4904-8d04-d7f5f2b15919.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c01e58bb-ea52-4904-8d04-d7f5f2b15919.lance deleted file mode 100644 index 4964d4fa1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c01e58bb-ea52-4904-8d04-d7f5f2b15919.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c03e6092-0479-438f-85a6-5e79b61675ca.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c03e6092-0479-438f-85a6-5e79b61675ca.lance deleted file mode 100644 index e0ae451ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c03e6092-0479-438f-85a6-5e79b61675ca.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0522533-995a-4f58-9659-8db08dd065bc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0522533-995a-4f58-9659-8db08dd065bc.lance deleted file mode 100644 index db75533f5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0522533-995a-4f58-9659-8db08dd065bc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c05a6a7b-4769-42eb-b1e8-543c00322bb8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c05a6a7b-4769-42eb-b1e8-543c00322bb8.lance deleted file mode 100644 index 1b65b9aaa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c05a6a7b-4769-42eb-b1e8-543c00322bb8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0631c02-ee0d-454b-a2b7-53c64ebc6160.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0631c02-ee0d-454b-a2b7-53c64ebc6160.lance deleted file mode 100644 index 0d2b3945e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0631c02-ee0d-454b-a2b7-53c64ebc6160.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c06d0055-6410-45db-8ec7-4c228490a7bd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c06d0055-6410-45db-8ec7-4c228490a7bd.lance deleted file mode 100644 index c90f50f98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c06d0055-6410-45db-8ec7-4c228490a7bd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c075b6b0-2723-47b0-9932-893b846b5c9b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c075b6b0-2723-47b0-9932-893b846b5c9b.lance deleted file mode 100644 index 064557435..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c075b6b0-2723-47b0-9932-893b846b5c9b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0923ba9-2f09-4988-84c0-031f6c8d9055.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0923ba9-2f09-4988-84c0-031f6c8d9055.lance deleted file mode 100644 index ee7babc37..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0923ba9-2f09-4988-84c0-031f6c8d9055.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0d3d359-ef4a-498f-b089-9a4d2fbbad45.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0d3d359-ef4a-498f-b089-9a4d2fbbad45.lance deleted file mode 100644 index d06280b0a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0d3d359-ef4a-498f-b089-9a4d2fbbad45.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0d86b9b-1d4c-4b2b-8d9f-a0b3156e60b5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0d86b9b-1d4c-4b2b-8d9f-a0b3156e60b5.lance deleted file mode 100644 index c4366071c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0d86b9b-1d4c-4b2b-8d9f-a0b3156e60b5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0e78ff4-81aa-461d-8d32-b9e6e8f810f1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0e78ff4-81aa-461d-8d32-b9e6e8f810f1.lance deleted file mode 100644 index ca84f3369..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0e78ff4-81aa-461d-8d32-b9e6e8f810f1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0fb3322-b904-4cc2-ab7b-a8691c2659fe.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0fb3322-b904-4cc2-ab7b-a8691c2659fe.lance deleted file mode 100644 index 201863e98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c0fb3322-b904-4cc2-ab7b-a8691c2659fe.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c104c8bd-9a6d-4e07-80e8-3b7635ef4ae8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c104c8bd-9a6d-4e07-80e8-3b7635ef4ae8.lance deleted file mode 100644 index ec1eaedfd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c104c8bd-9a6d-4e07-80e8-3b7635ef4ae8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1085130-714e-41dd-8f85-31ee6e1b49fd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1085130-714e-41dd-8f85-31ee6e1b49fd.lance deleted file mode 100644 index d6f831589..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1085130-714e-41dd-8f85-31ee6e1b49fd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c10cdfb5-78c6-4933-ae86-50b3dff8f7ae.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c10cdfb5-78c6-4933-ae86-50b3dff8f7ae.lance deleted file mode 100644 index 6b4689973..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c10cdfb5-78c6-4933-ae86-50b3dff8f7ae.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1165ac3-07a2-46bc-b262-3fc5b4b156f6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1165ac3-07a2-46bc-b262-3fc5b4b156f6.lance deleted file mode 100644 index a76b015ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1165ac3-07a2-46bc-b262-3fc5b4b156f6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c13f0b4c-580b-4520-836b-bf3666f7bd7f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c13f0b4c-580b-4520-836b-bf3666f7bd7f.lance deleted file mode 100644 index 2d72f611c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c13f0b4c-580b-4520-836b-bf3666f7bd7f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c15a3057-fbda-4cd0-9b1c-cfa329ce058f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c15a3057-fbda-4cd0-9b1c-cfa329ce058f.lance deleted file mode 100644 index b7ff71283..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c15a3057-fbda-4cd0-9b1c-cfa329ce058f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c16150cc-cc55-4688-9e04-27beaa4ccdb9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c16150cc-cc55-4688-9e04-27beaa4ccdb9.lance deleted file mode 100644 index 0963c3ae6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c16150cc-cc55-4688-9e04-27beaa4ccdb9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c17c75b3-c133-43a6-b360-7b980cdcf3a1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c17c75b3-c133-43a6-b360-7b980cdcf3a1.lance deleted file mode 100644 index 0eb0430a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c17c75b3-c133-43a6-b360-7b980cdcf3a1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1ade9c9-a958-4e05-8e08-97f4c4f1c506.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1ade9c9-a958-4e05-8e08-97f4c4f1c506.lance deleted file mode 100644 index 2405aa276..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1ade9c9-a958-4e05-8e08-97f4c4f1c506.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1b51752-fe42-4eb3-84f5-a1c4778565d8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1b51752-fe42-4eb3-84f5-a1c4778565d8.lance deleted file mode 100644 index 9c4e6ea8a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1b51752-fe42-4eb3-84f5-a1c4778565d8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1ee77c6-4b24-44eb-b5d5-5ceec2982b52.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1ee77c6-4b24-44eb-b5d5-5ceec2982b52.lance deleted file mode 100644 index 66c3ef462..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1ee77c6-4b24-44eb-b5d5-5ceec2982b52.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1f1dd87-067e-48b0-ba52-5d781ff5b692.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1f1dd87-067e-48b0-ba52-5d781ff5b692.lance deleted file mode 100644 index 9305261af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c1f1dd87-067e-48b0-ba52-5d781ff5b692.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c20937fd-5e37-4f1f-bf26-6a75bd8205b0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c20937fd-5e37-4f1f-bf26-6a75bd8205b0.lance deleted file mode 100644 index 14ab1c327..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c20937fd-5e37-4f1f-bf26-6a75bd8205b0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c2378af6-cbdb-4c07-9feb-99f78a39529c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c2378af6-cbdb-4c07-9feb-99f78a39529c.lance deleted file mode 100644 index c7d708889..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c2378af6-cbdb-4c07-9feb-99f78a39529c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c27b99f0-b7f2-44c1-95ac-4296a292e086.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c27b99f0-b7f2-44c1-95ac-4296a292e086.lance deleted file mode 100644 index 763f4711e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c27b99f0-b7f2-44c1-95ac-4296a292e086.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c2c0599e-afb4-436a-88e1-555692d91351.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c2c0599e-afb4-436a-88e1-555692d91351.lance deleted file mode 100644 index 8ff2a6881..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c2c0599e-afb4-436a-88e1-555692d91351.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c31add96-b336-4a1a-ba68-1733c193e973.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c31add96-b336-4a1a-ba68-1733c193e973.lance deleted file mode 100644 index 0fa3ee5e2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c31add96-b336-4a1a-ba68-1733c193e973.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c31e6bc2-b830-4989-840a-b17717967908.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c31e6bc2-b830-4989-840a-b17717967908.lance deleted file mode 100644 index 829c9e78b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c31e6bc2-b830-4989-840a-b17717967908.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c32d8e28-7da2-4baa-aca4-93766f618c17.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c32d8e28-7da2-4baa-aca4-93766f618c17.lance deleted file mode 100644 index d6b960c32..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c32d8e28-7da2-4baa-aca4-93766f618c17.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c33365df-420a-4de5-a5c3-236ecd3bceb5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c33365df-420a-4de5-a5c3-236ecd3bceb5.lance deleted file mode 100644 index d8a06a309..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c33365df-420a-4de5-a5c3-236ecd3bceb5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c339dce4-556e-457d-a6cd-d96622f4e243.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c339dce4-556e-457d-a6cd-d96622f4e243.lance deleted file mode 100644 index 46d38a00a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c339dce4-556e-457d-a6cd-d96622f4e243.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c33dda77-a758-4903-8a03-c0f5ee2ad87e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c33dda77-a758-4903-8a03-c0f5ee2ad87e.lance deleted file mode 100644 index 5bf0ffa2e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c33dda77-a758-4903-8a03-c0f5ee2ad87e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c34048cc-6f81-4c83-beaf-af807020974f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c34048cc-6f81-4c83-beaf-af807020974f.lance deleted file mode 100644 index 7ab61e913..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c34048cc-6f81-4c83-beaf-af807020974f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c35437e9-4c83-46aa-9b94-e4d6af2f3922.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c35437e9-4c83-46aa-9b94-e4d6af2f3922.lance deleted file mode 100644 index 92a4c4b18..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c35437e9-4c83-46aa-9b94-e4d6af2f3922.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c3662304-f9ec-4214-8bb2-01c615d84963.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c3662304-f9ec-4214-8bb2-01c615d84963.lance deleted file mode 100644 index 73b26031e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c3662304-f9ec-4214-8bb2-01c615d84963.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c373f175-4127-4c0a-a36c-435356e0a7ad.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c373f175-4127-4c0a-a36c-435356e0a7ad.lance deleted file mode 100644 index bfa89f982..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c373f175-4127-4c0a-a36c-435356e0a7ad.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c38abda7-583b-4585-ab63-e5f77ee78830.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c38abda7-583b-4585-ab63-e5f77ee78830.lance deleted file mode 100644 index f977c9544..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c38abda7-583b-4585-ab63-e5f77ee78830.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c3c7c38e-24ba-4c83-a3fa-d6202bff8378.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c3c7c38e-24ba-4c83-a3fa-d6202bff8378.lance deleted file mode 100644 index 5287cc574..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c3c7c38e-24ba-4c83-a3fa-d6202bff8378.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c3e81b29-89ab-4c60-a69b-c399ab102569.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c3e81b29-89ab-4c60-a69b-c399ab102569.lance deleted file mode 100644 index dbbd9c52b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c3e81b29-89ab-4c60-a69b-c399ab102569.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c3eaf84c-331f-469a-83b1-3489dce77119.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c3eaf84c-331f-469a-83b1-3489dce77119.lance deleted file mode 100644 index 6e7aa7935..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c3eaf84c-331f-469a-83b1-3489dce77119.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4058fe0-d7ec-43a5-80c4-8fd342fe0542.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4058fe0-d7ec-43a5-80c4-8fd342fe0542.lance deleted file mode 100644 index d3a765dff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4058fe0-d7ec-43a5-80c4-8fd342fe0542.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4549b31-6b20-408f-a628-141fd598e640.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4549b31-6b20-408f-a628-141fd598e640.lance deleted file mode 100644 index ad6a86e69..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4549b31-6b20-408f-a628-141fd598e640.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4740265-d409-4951-a0a8-57b3ee2413a2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4740265-d409-4951-a0a8-57b3ee2413a2.lance deleted file mode 100644 index 1e1071aa3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4740265-d409-4951-a0a8-57b3ee2413a2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c47b3ec6-9230-41f0-ace2-c617a3c5e088.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c47b3ec6-9230-41f0-ace2-c617a3c5e088.lance deleted file mode 100644 index c1c145f78..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c47b3ec6-9230-41f0-ace2-c617a3c5e088.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4968b89-3cac-47b8-86ae-0ad5d1c6c50b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4968b89-3cac-47b8-86ae-0ad5d1c6c50b.lance deleted file mode 100644 index 513fd4fa6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4968b89-3cac-47b8-86ae-0ad5d1c6c50b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4ae2083-864d-4f92-a8d6-0a0dfcb46e59.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4ae2083-864d-4f92-a8d6-0a0dfcb46e59.lance deleted file mode 100644 index 9f6a3586d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4ae2083-864d-4f92-a8d6-0a0dfcb46e59.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4c06da9-f979-4496-8270-10f2e7ac1e9d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4c06da9-f979-4496-8270-10f2e7ac1e9d.lance deleted file mode 100644 index b1bf80650..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4c06da9-f979-4496-8270-10f2e7ac1e9d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4d7077b-e897-442a-a8ed-bf1091266fd7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4d7077b-e897-442a-a8ed-bf1091266fd7.lance deleted file mode 100644 index 6c6527a80..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c4d7077b-e897-442a-a8ed-bf1091266fd7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c51c635d-a5d7-4cc0-bb96-197321dc97f7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c51c635d-a5d7-4cc0-bb96-197321dc97f7.lance deleted file mode 100644 index 3ba316e4a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c51c635d-a5d7-4cc0-bb96-197321dc97f7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c51c8d4f-b8d1-4770-8091-e9d39f44db4f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c51c8d4f-b8d1-4770-8091-e9d39f44db4f.lance deleted file mode 100644 index f3c6d3c09..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c51c8d4f-b8d1-4770-8091-e9d39f44db4f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c51f12dc-64d8-4a05-b142-22f487fd389c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c51f12dc-64d8-4a05-b142-22f487fd389c.lance deleted file mode 100644 index 6e73cc8ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c51f12dc-64d8-4a05-b142-22f487fd389c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c520e4da-3ef0-4c84-be74-52be29a1c2db.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c520e4da-3ef0-4c84-be74-52be29a1c2db.lance deleted file mode 100644 index 74aa1ee5b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c520e4da-3ef0-4c84-be74-52be29a1c2db.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c555d4c9-5d9b-4acc-9d15-69794bfad18d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c555d4c9-5d9b-4acc-9d15-69794bfad18d.lance deleted file mode 100644 index 437204a87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c555d4c9-5d9b-4acc-9d15-69794bfad18d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c55ac765-2b88-4273-a141-ec91b3036167.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c55ac765-2b88-4273-a141-ec91b3036167.lance deleted file mode 100644 index 0a4530aae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c55ac765-2b88-4273-a141-ec91b3036167.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c579a4db-abe9-4b23-a9fa-67702c864c05.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c579a4db-abe9-4b23-a9fa-67702c864c05.lance deleted file mode 100644 index 4a5f3f127..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c579a4db-abe9-4b23-a9fa-67702c864c05.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c5ab8018-49ba-49e9-b12f-c6a9dc0b0e91.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c5ab8018-49ba-49e9-b12f-c6a9dc0b0e91.lance deleted file mode 100644 index e75884931..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c5ab8018-49ba-49e9-b12f-c6a9dc0b0e91.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c5d78f31-d9ec-4d37-84ab-298302af5167.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c5d78f31-d9ec-4d37-84ab-298302af5167.lance deleted file mode 100644 index 783e6b5b4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c5d78f31-d9ec-4d37-84ab-298302af5167.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c5e94cf6-47f4-4c0c-bb46-6ba88441323d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c5e94cf6-47f4-4c0c-bb46-6ba88441323d.lance deleted file mode 100644 index 96ccd4a53..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c5e94cf6-47f4-4c0c-bb46-6ba88441323d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c5e9af4c-48e0-415e-b1df-58424dc4a57d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c5e9af4c-48e0-415e-b1df-58424dc4a57d.lance deleted file mode 100644 index 0adba0885..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c5e9af4c-48e0-415e-b1df-58424dc4a57d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c60a3094-0af0-4ff7-9375-44d88ba18b72.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c60a3094-0af0-4ff7-9375-44d88ba18b72.lance deleted file mode 100644 index c2e13da50..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c60a3094-0af0-4ff7-9375-44d88ba18b72.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6175f96-602e-4b4d-9f3f-1185ec81a2ba.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6175f96-602e-4b4d-9f3f-1185ec81a2ba.lance deleted file mode 100644 index ccb97f5bb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6175f96-602e-4b4d-9f3f-1185ec81a2ba.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c645119a-5c0c-48b0-94f5-040a546eaf9a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c645119a-5c0c-48b0-94f5-040a546eaf9a.lance deleted file mode 100644 index 1ed7b0a3d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c645119a-5c0c-48b0-94f5-040a546eaf9a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c645cc03-d495-41c0-b43d-5c77245070c6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c645cc03-d495-41c0-b43d-5c77245070c6.lance deleted file mode 100644 index 43fa00f41..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c645cc03-d495-41c0-b43d-5c77245070c6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c67f6f16-cb3a-4f71-aff0-0bb402ccaf9c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c67f6f16-cb3a-4f71-aff0-0bb402ccaf9c.lance deleted file mode 100644 index 2d6dddcf6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c67f6f16-cb3a-4f71-aff0-0bb402ccaf9c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c688b62d-d2b8-42ef-b55b-612a9ade8c72.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c688b62d-d2b8-42ef-b55b-612a9ade8c72.lance deleted file mode 100644 index 580dcbe16..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c688b62d-d2b8-42ef-b55b-612a9ade8c72.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6a26d21-737e-46fa-85db-55a90368c4cf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6a26d21-737e-46fa-85db-55a90368c4cf.lance deleted file mode 100644 index 912419066..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6a26d21-737e-46fa-85db-55a90368c4cf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6a796dd-7ea8-4a31-8877-ccce1f64e7d2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6a796dd-7ea8-4a31-8877-ccce1f64e7d2.lance deleted file mode 100644 index 8403d89d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6a796dd-7ea8-4a31-8877-ccce1f64e7d2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6c0b31a-2b06-4246-a676-c0d30dd3a980.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6c0b31a-2b06-4246-a676-c0d30dd3a980.lance deleted file mode 100644 index 4eb0ada73..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6c0b31a-2b06-4246-a676-c0d30dd3a980.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6c5a3d1-fd7c-44f9-9aa2-395aedf7c1bb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6c5a3d1-fd7c-44f9-9aa2-395aedf7c1bb.lance deleted file mode 100644 index 02be77023..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6c5a3d1-fd7c-44f9-9aa2-395aedf7c1bb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6c914d7-de50-4f42-b296-8742191edb97.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6c914d7-de50-4f42-b296-8742191edb97.lance deleted file mode 100644 index d3ad967cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6c914d7-de50-4f42-b296-8742191edb97.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6d88dc4-74c2-43da-901e-63c1b5902448.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6d88dc4-74c2-43da-901e-63c1b5902448.lance deleted file mode 100644 index f4c0c88b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6d88dc4-74c2-43da-901e-63c1b5902448.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6de73b0-e69f-46a1-b4cf-fc86486b9315.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6de73b0-e69f-46a1-b4cf-fc86486b9315.lance deleted file mode 100644 index 62197116d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c6de73b0-e69f-46a1-b4cf-fc86486b9315.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c71b1c2a-ba13-4767-98fa-1ea578331457.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c71b1c2a-ba13-4767-98fa-1ea578331457.lance deleted file mode 100644 index 3b51b0c63..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c71b1c2a-ba13-4767-98fa-1ea578331457.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c7350718-632e-422e-9494-3463a97a1295.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c7350718-632e-422e-9494-3463a97a1295.lance deleted file mode 100644 index ca4f17175..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c7350718-632e-422e-9494-3463a97a1295.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c755101e-1671-44c6-adab-da8d58f90361.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c755101e-1671-44c6-adab-da8d58f90361.lance deleted file mode 100644 index be61320c6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c755101e-1671-44c6-adab-da8d58f90361.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c7cbd846-7e47-4521-b0a3-9776269cb58c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c7cbd846-7e47-4521-b0a3-9776269cb58c.lance deleted file mode 100644 index 094fd0491..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c7cbd846-7e47-4521-b0a3-9776269cb58c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c7dc8cd8-f37e-4fb3-9a47-feb30e9a52a3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c7dc8cd8-f37e-4fb3-9a47-feb30e9a52a3.lance deleted file mode 100644 index bd50c755e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c7dc8cd8-f37e-4fb3-9a47-feb30e9a52a3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8161f8c-c74c-4e6e-912e-bb8b92b7ec31.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8161f8c-c74c-4e6e-912e-bb8b92b7ec31.lance deleted file mode 100644 index d2ed9d39e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8161f8c-c74c-4e6e-912e-bb8b92b7ec31.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c82d3038-9774-4b60-be62-79eaa6a862c4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c82d3038-9774-4b60-be62-79eaa6a862c4.lance deleted file mode 100644 index 9d37c33fe..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c82d3038-9774-4b60-be62-79eaa6a862c4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8462152-e10d-4a0b-b240-0ca9a3c6da09.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8462152-e10d-4a0b-b240-0ca9a3c6da09.lance deleted file mode 100644 index 3d07316d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8462152-e10d-4a0b-b240-0ca9a3c6da09.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c88ced7e-55da-4cd4-8f29-fac4ac6edc54.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c88ced7e-55da-4cd4-8f29-fac4ac6edc54.lance deleted file mode 100644 index e23ba6c8a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c88ced7e-55da-4cd4-8f29-fac4ac6edc54.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c894ac79-fe16-4700-b900-eaccb738e127.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c894ac79-fe16-4700-b900-eaccb738e127.lance deleted file mode 100644 index d84443a8b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c894ac79-fe16-4700-b900-eaccb738e127.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c89ae169-24bf-4b23-9b6f-5a15baffa945.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c89ae169-24bf-4b23-9b6f-5a15baffa945.lance deleted file mode 100644 index c53d0f5a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c89ae169-24bf-4b23-9b6f-5a15baffa945.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8a4fe91-1d90-4eb3-a3d5-f5591811c2dd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8a4fe91-1d90-4eb3-a3d5-f5591811c2dd.lance deleted file mode 100644 index a57640f76..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8a4fe91-1d90-4eb3-a3d5-f5591811c2dd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8d94092-48d7-4521-ad24-3b3676e6c9cf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8d94092-48d7-4521-ad24-3b3676e6c9cf.lance deleted file mode 100644 index da7f2def8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8d94092-48d7-4521-ad24-3b3676e6c9cf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8db9921-d1a4-4cdd-9f15-ecbaeff91765.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8db9921-d1a4-4cdd-9f15-ecbaeff91765.lance deleted file mode 100644 index 346653e55..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8db9921-d1a4-4cdd-9f15-ecbaeff91765.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8e6689b-9ced-48fa-a9eb-d038048c2ed6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8e6689b-9ced-48fa-a9eb-d038048c2ed6.lance deleted file mode 100644 index f0c4ec7a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8e6689b-9ced-48fa-a9eb-d038048c2ed6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8ebb3a0-734f-4a49-b41a-edee84c7bfa1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8ebb3a0-734f-4a49-b41a-edee84c7bfa1.lance deleted file mode 100644 index f738a8076..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8ebb3a0-734f-4a49-b41a-edee84c7bfa1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8fd60b4-5553-4bad-9ae2-5279fb23a39b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8fd60b4-5553-4bad-9ae2-5279fb23a39b.lance deleted file mode 100644 index 8a13f9f12..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8fd60b4-5553-4bad-9ae2-5279fb23a39b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8feb579-ef6e-41fa-bf6e-b229539fb8c2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8feb579-ef6e-41fa-bf6e-b229539fb8c2.lance deleted file mode 100644 index 6cfa739dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c8feb579-ef6e-41fa-bf6e-b229539fb8c2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c9083adb-10c6-4328-b0a6-9428ee7a99b8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c9083adb-10c6-4328-b0a6-9428ee7a99b8.lance deleted file mode 100644 index deb321327..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c9083adb-10c6-4328-b0a6-9428ee7a99b8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c928d7dd-5dd9-4dd3-9e4e-64c9852e6e33.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c928d7dd-5dd9-4dd3-9e4e-64c9852e6e33.lance deleted file mode 100644 index b544c7648..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c928d7dd-5dd9-4dd3-9e4e-64c9852e6e33.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c976298e-c507-410d-be2f-a1e92dbdd2cd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c976298e-c507-410d-be2f-a1e92dbdd2cd.lance deleted file mode 100644 index 78cdc8bec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c976298e-c507-410d-be2f-a1e92dbdd2cd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c9860e8f-5c89-4a23-8c81-c2267b07874d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c9860e8f-5c89-4a23-8c81-c2267b07874d.lance deleted file mode 100644 index d2bc01129..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c9860e8f-5c89-4a23-8c81-c2267b07874d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c988f0ec-1568-4507-b69f-5f4580f7b28d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c988f0ec-1568-4507-b69f-5f4580f7b28d.lance deleted file mode 100644 index d9b8a873c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c988f0ec-1568-4507-b69f-5f4580f7b28d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c98ad6a6-6175-4d40-bebe-8f9812fbff16.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c98ad6a6-6175-4d40-bebe-8f9812fbff16.lance deleted file mode 100644 index d62af1212..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c98ad6a6-6175-4d40-bebe-8f9812fbff16.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c98b0c86-a0ac-4570-8118-faae3f7d25fb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c98b0c86-a0ac-4570-8118-faae3f7d25fb.lance deleted file mode 100644 index e720c612e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c98b0c86-a0ac-4570-8118-faae3f7d25fb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c9984f4d-12d4-43af-80b4-5aebaa8f8984.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c9984f4d-12d4-43af-80b4-5aebaa8f8984.lance deleted file mode 100644 index d518f9c6a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c9984f4d-12d4-43af-80b4-5aebaa8f8984.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c9f74f1d-fb4b-47ae-9024-79f2dbefb073.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c9f74f1d-fb4b-47ae-9024-79f2dbefb073.lance deleted file mode 100644 index e436b69e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c9f74f1d-fb4b-47ae-9024-79f2dbefb073.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c9fbf2e1-352f-444b-9905-1f4f71cf76b3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c9fbf2e1-352f-444b-9905-1f4f71cf76b3.lance deleted file mode 100644 index a1196672f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/c9fbf2e1-352f-444b-9905-1f4f71cf76b3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca0d3a3a-8a4f-4894-92e6-3e18175f0bea.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca0d3a3a-8a4f-4894-92e6-3e18175f0bea.lance deleted file mode 100644 index 633b8d8f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca0d3a3a-8a4f-4894-92e6-3e18175f0bea.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca2e3e1b-c4a2-4d22-903d-d8f75bfdc77a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca2e3e1b-c4a2-4d22-903d-d8f75bfdc77a.lance deleted file mode 100644 index 6c2c0cb2c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca2e3e1b-c4a2-4d22-903d-d8f75bfdc77a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca529862-71a4-441f-b945-b248516e7d1a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca529862-71a4-441f-b945-b248516e7d1a.lance deleted file mode 100644 index ae44dbe21..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca529862-71a4-441f-b945-b248516e7d1a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca65dd81-98ec-43d8-9786-5cd7353a8170.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca65dd81-98ec-43d8-9786-5cd7353a8170.lance deleted file mode 100644 index 62818cd3c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca65dd81-98ec-43d8-9786-5cd7353a8170.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca699d88-f49a-4210-91d3-2c1202e8f788.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca699d88-f49a-4210-91d3-2c1202e8f788.lance deleted file mode 100644 index fec267aa5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca699d88-f49a-4210-91d3-2c1202e8f788.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca843bee-fc5d-4467-aa8a-afac28140934.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca843bee-fc5d-4467-aa8a-afac28140934.lance deleted file mode 100644 index c8a69289a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca843bee-fc5d-4467-aa8a-afac28140934.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca85e282-2948-4db3-8333-7a43722d3d23.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca85e282-2948-4db3-8333-7a43722d3d23.lance deleted file mode 100644 index f0313dbf0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ca85e282-2948-4db3-8333-7a43722d3d23.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/caa19a34-a583-4bfb-bfd7-c756cbc4a8b2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/caa19a34-a583-4bfb-bfd7-c756cbc4a8b2.lance deleted file mode 100644 index b70556605..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/caa19a34-a583-4bfb-bfd7-c756cbc4a8b2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cacab8f7-a1a0-459b-8d5d-7b8fe0bba211.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cacab8f7-a1a0-459b-8d5d-7b8fe0bba211.lance deleted file mode 100644 index d9c4253bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cacab8f7-a1a0-459b-8d5d-7b8fe0bba211.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cae8766b-99df-4125-8fec-faa7475167da.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cae8766b-99df-4125-8fec-faa7475167da.lance deleted file mode 100644 index d6c13bf04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cae8766b-99df-4125-8fec-faa7475167da.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/caf8df3b-a09f-4e56-8e82-ad6e401c2933.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/caf8df3b-a09f-4e56-8e82-ad6e401c2933.lance deleted file mode 100644 index 5653aaca4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/caf8df3b-a09f-4e56-8e82-ad6e401c2933.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cb16ea8a-1e58-4c88-bac4-0b2cde4866a6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cb16ea8a-1e58-4c88-bac4-0b2cde4866a6.lance deleted file mode 100644 index 5e9c1364f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cb16ea8a-1e58-4c88-bac4-0b2cde4866a6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cb355f2f-3e75-4c74-8292-53174b2538a9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cb355f2f-3e75-4c74-8292-53174b2538a9.lance deleted file mode 100644 index 749c1408a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cb355f2f-3e75-4c74-8292-53174b2538a9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cb734dde-d262-491a-b808-205ac768f58d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cb734dde-d262-491a-b808-205ac768f58d.lance deleted file mode 100644 index a3f2dfc35..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cb734dde-d262-491a-b808-205ac768f58d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cbb214b6-213c-4693-8a02-96188c15d7a3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cbb214b6-213c-4693-8a02-96188c15d7a3.lance deleted file mode 100644 index 8957a3b15..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cbb214b6-213c-4693-8a02-96188c15d7a3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cbc69e0b-648e-4f84-a910-9805fe7218aa.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cbc69e0b-648e-4f84-a910-9805fe7218aa.lance deleted file mode 100644 index 9ad0ec826..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cbc69e0b-648e-4f84-a910-9805fe7218aa.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cbddcee2-9026-48d1-9d37-29e73a46c318.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cbddcee2-9026-48d1-9d37-29e73a46c318.lance deleted file mode 100644 index e0c38080b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cbddcee2-9026-48d1-9d37-29e73a46c318.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cbf0c587-1958-49b1-85e9-7d034aee437e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cbf0c587-1958-49b1-85e9-7d034aee437e.lance deleted file mode 100644 index ec29873a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cbf0c587-1958-49b1-85e9-7d034aee437e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cbf163d4-6379-459a-bc68-ef4085815525.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cbf163d4-6379-459a-bc68-ef4085815525.lance deleted file mode 100644 index 4e0f6c688..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cbf163d4-6379-459a-bc68-ef4085815525.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc130ed8-0494-4043-8767-794a33e36d7c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc130ed8-0494-4043-8767-794a33e36d7c.lance deleted file mode 100644 index c81c5ed86..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc130ed8-0494-4043-8767-794a33e36d7c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc3d1228-d846-4b63-b5b6-cc449063592e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc3d1228-d846-4b63-b5b6-cc449063592e.lance deleted file mode 100644 index 06a8329b0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc3d1228-d846-4b63-b5b6-cc449063592e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc439e4c-272c-476e-9e41-ca6d053ddbc3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc439e4c-272c-476e-9e41-ca6d053ddbc3.lance deleted file mode 100644 index 377c92f56..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc439e4c-272c-476e-9e41-ca6d053ddbc3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc4d4344-a6a3-4cf1-b846-fb7e27ae3990.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc4d4344-a6a3-4cf1-b846-fb7e27ae3990.lance deleted file mode 100644 index 2c2be29ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc4d4344-a6a3-4cf1-b846-fb7e27ae3990.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc7b5f93-477d-4a77-8f35-efe39efcc71c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc7b5f93-477d-4a77-8f35-efe39efcc71c.lance deleted file mode 100644 index 63ef80481..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc7b5f93-477d-4a77-8f35-efe39efcc71c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc8d7b7b-8098-4feb-ac2c-e3770b4cdae2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc8d7b7b-8098-4feb-ac2c-e3770b4cdae2.lance deleted file mode 100644 index 6e9b1a46f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cc8d7b7b-8098-4feb-ac2c-e3770b4cdae2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ccaf07a5-30ea-432c-973b-1badcd741e97.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ccaf07a5-30ea-432c-973b-1badcd741e97.lance deleted file mode 100644 index 6628cb1aa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ccaf07a5-30ea-432c-973b-1badcd741e97.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cce15e15-28b8-4c4b-94cb-d21792981b2e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cce15e15-28b8-4c4b-94cb-d21792981b2e.lance deleted file mode 100644 index c71b4baf4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cce15e15-28b8-4c4b-94cb-d21792981b2e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd020469-d1dd-4c35-b012-3be19fb4a83c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd020469-d1dd-4c35-b012-3be19fb4a83c.lance deleted file mode 100644 index f1e8575f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd020469-d1dd-4c35-b012-3be19fb4a83c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd071605-04b1-4c48-b920-fb3f22236635.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd071605-04b1-4c48-b920-fb3f22236635.lance deleted file mode 100644 index 11da73962..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd071605-04b1-4c48-b920-fb3f22236635.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd1865c1-095c-41f7-9439-ee3b0e503586.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd1865c1-095c-41f7-9439-ee3b0e503586.lance deleted file mode 100644 index 4d89a173c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd1865c1-095c-41f7-9439-ee3b0e503586.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd2af5be-003b-4f03-ac48-2d0d77a95289.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd2af5be-003b-4f03-ac48-2d0d77a95289.lance deleted file mode 100644 index 0af415f67..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd2af5be-003b-4f03-ac48-2d0d77a95289.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd31dcee-e068-443c-b703-af5f8880956f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd31dcee-e068-443c-b703-af5f8880956f.lance deleted file mode 100644 index 5f7075023..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd31dcee-e068-443c-b703-af5f8880956f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd6614cd-6f8a-482b-a87c-4c7be8761f3f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd6614cd-6f8a-482b-a87c-4c7be8761f3f.lance deleted file mode 100644 index b0af7b850..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd6614cd-6f8a-482b-a87c-4c7be8761f3f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd7ef1f5-edf3-4e94-b359-88d003a1b9c4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd7ef1f5-edf3-4e94-b359-88d003a1b9c4.lance deleted file mode 100644 index 30250dfa1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd7ef1f5-edf3-4e94-b359-88d003a1b9c4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd88661c-97b9-4670-8de0-784eeccdf683.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd88661c-97b9-4670-8de0-784eeccdf683.lance deleted file mode 100644 index 399f524ef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd88661c-97b9-4670-8de0-784eeccdf683.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd92df25-b1db-4a4d-8f17-8dfc6eb44428.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd92df25-b1db-4a4d-8f17-8dfc6eb44428.lance deleted file mode 100644 index 8f743addd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd92df25-b1db-4a4d-8f17-8dfc6eb44428.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd95df1f-90e0-4232-bd0c-2526f4944a3b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd95df1f-90e0-4232-bd0c-2526f4944a3b.lance deleted file mode 100644 index 3046095d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd95df1f-90e0-4232-bd0c-2526f4944a3b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd9a52de-7aba-45a2-869e-0e3862523800.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd9a52de-7aba-45a2-869e-0e3862523800.lance deleted file mode 100644 index 1b31173a0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cd9a52de-7aba-45a2-869e-0e3862523800.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cdca5f5a-377a-4e71-b3a6-13d0abcdeb15.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cdca5f5a-377a-4e71-b3a6-13d0abcdeb15.lance deleted file mode 100644 index 5656f9766..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cdca5f5a-377a-4e71-b3a6-13d0abcdeb15.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cdce67f1-bc61-43c1-9391-bc8697996e8d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cdce67f1-bc61-43c1-9391-bc8697996e8d.lance deleted file mode 100644 index a4eaa4e98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cdce67f1-bc61-43c1-9391-bc8697996e8d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cdec613a-10c3-479b-8ef5-6bd7afd46230.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cdec613a-10c3-479b-8ef5-6bd7afd46230.lance deleted file mode 100644 index 0d5f83301..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cdec613a-10c3-479b-8ef5-6bd7afd46230.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cdf2cf2d-3675-4d04-8d78-d12a87f8520c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cdf2cf2d-3675-4d04-8d78-d12a87f8520c.lance deleted file mode 100644 index f96d56298..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cdf2cf2d-3675-4d04-8d78-d12a87f8520c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce039641-a0ed-41ed-acc4-b49d1551a001.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce039641-a0ed-41ed-acc4-b49d1551a001.lance deleted file mode 100644 index 55418b164..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce039641-a0ed-41ed-acc4-b49d1551a001.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce201095-a8b0-465b-a4fb-f9a2ff5ba233.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce201095-a8b0-465b-a4fb-f9a2ff5ba233.lance deleted file mode 100644 index 792ec1409..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce201095-a8b0-465b-a4fb-f9a2ff5ba233.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce2d50ab-ec5c-49a7-aee6-e7a6be9cf33c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce2d50ab-ec5c-49a7-aee6-e7a6be9cf33c.lance deleted file mode 100644 index 2fe3ebc55..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce2d50ab-ec5c-49a7-aee6-e7a6be9cf33c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce8da2fb-f63d-4621-b35a-1e91103e4757.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce8da2fb-f63d-4621-b35a-1e91103e4757.lance deleted file mode 100644 index adc3d537b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce8da2fb-f63d-4621-b35a-1e91103e4757.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce982a9f-3a48-4c3e-8e30-7acbf2f26a53.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce982a9f-3a48-4c3e-8e30-7acbf2f26a53.lance deleted file mode 100644 index f46de36f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce982a9f-3a48-4c3e-8e30-7acbf2f26a53.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce9c9e4e-6928-4b14-887c-030afcf1607c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce9c9e4e-6928-4b14-887c-030afcf1607c.lance deleted file mode 100644 index 812a00bad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce9c9e4e-6928-4b14-887c-030afcf1607c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce9e3172-1410-4a33-a13b-2598f36673ea.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce9e3172-1410-4a33-a13b-2598f36673ea.lance deleted file mode 100644 index 8a16012c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ce9e3172-1410-4a33-a13b-2598f36673ea.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ceaf6bb3-99d0-41de-b26c-c307f1fb6d53.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ceaf6bb3-99d0-41de-b26c-c307f1fb6d53.lance deleted file mode 100644 index 069b7f24b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ceaf6bb3-99d0-41de-b26c-c307f1fb6d53.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ceb3190f-93d3-46d4-a971-7d9ec7669fa8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ceb3190f-93d3-46d4-a971-7d9ec7669fa8.lance deleted file mode 100644 index 70c2f1620..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ceb3190f-93d3-46d4-a971-7d9ec7669fa8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cebdb04a-68e7-4b92-b823-44700abecdf6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cebdb04a-68e7-4b92-b823-44700abecdf6.lance deleted file mode 100644 index f7ce51b0a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cebdb04a-68e7-4b92-b823-44700abecdf6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ceca11fc-a7d2-4650-bb26-4d9a05d0bffc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ceca11fc-a7d2-4650-bb26-4d9a05d0bffc.lance deleted file mode 100644 index 5f36036d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ceca11fc-a7d2-4650-bb26-4d9a05d0bffc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cf7040dc-e7ad-478d-8381-8ee41d7e8e61.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cf7040dc-e7ad-478d-8381-8ee41d7e8e61.lance deleted file mode 100644 index 65dd6bdae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cf7040dc-e7ad-478d-8381-8ee41d7e8e61.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cf7f388f-695d-48f5-bbe1-5111e7e393a6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cf7f388f-695d-48f5-bbe1-5111e7e393a6.lance deleted file mode 100644 index 8b4b2229a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cf7f388f-695d-48f5-bbe1-5111e7e393a6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cf8ab18d-0898-4206-a19e-f0b2ac4413e7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cf8ab18d-0898-4206-a19e-f0b2ac4413e7.lance deleted file mode 100644 index 6956a9610..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cf8ab18d-0898-4206-a19e-f0b2ac4413e7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cfb75712-2a28-4df5-88f3-aca0aea70d71.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cfb75712-2a28-4df5-88f3-aca0aea70d71.lance deleted file mode 100644 index f681f1e19..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cfb75712-2a28-4df5-88f3-aca0aea70d71.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cfcbd703-2755-402f-b41d-33c08e47d313.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cfcbd703-2755-402f-b41d-33c08e47d313.lance deleted file mode 100644 index c3736f40d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cfcbd703-2755-402f-b41d-33c08e47d313.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cfe52b42-82f6-4e22-8a26-46c2790b0169.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cfe52b42-82f6-4e22-8a26-46c2790b0169.lance deleted file mode 100644 index 032b7ca8e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cfe52b42-82f6-4e22-8a26-46c2790b0169.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cfef1280-0cce-4b47-9d33-337df578ca82.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cfef1280-0cce-4b47-9d33-337df578ca82.lance deleted file mode 100644 index f193a6647..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cfef1280-0cce-4b47-9d33-337df578ca82.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cff13720-4ebf-4959-ba74-0f99828953d8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cff13720-4ebf-4959-ba74-0f99828953d8.lance deleted file mode 100644 index 97f0184cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cff13720-4ebf-4959-ba74-0f99828953d8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cffa8691-7a22-438f-99de-4faa3dd77dbe.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cffa8691-7a22-438f-99de-4faa3dd77dbe.lance deleted file mode 100644 index 169ee302d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/cffa8691-7a22-438f-99de-4faa3dd77dbe.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d002c3a7-d298-457b-8720-57d6b54c1db9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d002c3a7-d298-457b-8720-57d6b54c1db9.lance deleted file mode 100644 index 7e8aa5c5b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d002c3a7-d298-457b-8720-57d6b54c1db9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d00c506e-b374-4eff-b353-724f7f4eb875.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d00c506e-b374-4eff-b353-724f7f4eb875.lance deleted file mode 100644 index d89e458dd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d00c506e-b374-4eff-b353-724f7f4eb875.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d01a25c1-729d-45ca-b563-0873eeffaf00.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d01a25c1-729d-45ca-b563-0873eeffaf00.lance deleted file mode 100644 index 32be6e84d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d01a25c1-729d-45ca-b563-0873eeffaf00.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d04319af-b4f1-4f25-921c-0d522422f788.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d04319af-b4f1-4f25-921c-0d522422f788.lance deleted file mode 100644 index ae76b6ea9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d04319af-b4f1-4f25-921c-0d522422f788.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0457699-9593-40b1-a42d-8f3b7a1ae6e7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0457699-9593-40b1-a42d-8f3b7a1ae6e7.lance deleted file mode 100644 index 26333702b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0457699-9593-40b1-a42d-8f3b7a1ae6e7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d07884df-41d5-47b1-ada7-5e98c2c740f7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d07884df-41d5-47b1-ada7-5e98c2c740f7.lance deleted file mode 100644 index 588b9466f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d07884df-41d5-47b1-ada7-5e98c2c740f7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d07fb6cf-c482-4b9b-bb67-c8c944f921dc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d07fb6cf-c482-4b9b-bb67-c8c944f921dc.lance deleted file mode 100644 index a34176949..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d07fb6cf-c482-4b9b-bb67-c8c944f921dc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0852594-a902-469d-9d93-3f4c7830ad7e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0852594-a902-469d-9d93-3f4c7830ad7e.lance deleted file mode 100644 index 0a2d6cfb8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0852594-a902-469d-9d93-3f4c7830ad7e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d09b97f2-27c6-4739-a326-bdfb1d826ae2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d09b97f2-27c6-4739-a326-bdfb1d826ae2.lance deleted file mode 100644 index d7c127a64..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d09b97f2-27c6-4739-a326-bdfb1d826ae2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0b9d6d9-d3b4-43a6-bca0-c363e81a2dc3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0b9d6d9-d3b4-43a6-bca0-c363e81a2dc3.lance deleted file mode 100644 index 120d966b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0b9d6d9-d3b4-43a6-bca0-c363e81a2dc3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0d9ea39-2a4d-43a0-9d9a-940856c32ff2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0d9ea39-2a4d-43a0-9d9a-940856c32ff2.lance deleted file mode 100644 index 2f6c29200..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0d9ea39-2a4d-43a0-9d9a-940856c32ff2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0de3e14-89fe-4ca3-b189-fb6574acbad8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0de3e14-89fe-4ca3-b189-fb6574acbad8.lance deleted file mode 100644 index 4a2cf9141..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0de3e14-89fe-4ca3-b189-fb6574acbad8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0f4f367-0305-4945-bfb1-3a26d2d7b97a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0f4f367-0305-4945-bfb1-3a26d2d7b97a.lance deleted file mode 100644 index d1d260320..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d0f4f367-0305-4945-bfb1-3a26d2d7b97a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1107f0e-5a6a-4a8a-bb50-83fc39f5d20b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1107f0e-5a6a-4a8a-bb50-83fc39f5d20b.lance deleted file mode 100644 index 2fc6e4228..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1107f0e-5a6a-4a8a-bb50-83fc39f5d20b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d118d95e-6205-415c-aff8-801eff865a3d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d118d95e-6205-415c-aff8-801eff865a3d.lance deleted file mode 100644 index 665dcb742..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d118d95e-6205-415c-aff8-801eff865a3d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1254dd1-42f9-4caa-b50a-1fbe73241b50.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1254dd1-42f9-4caa-b50a-1fbe73241b50.lance deleted file mode 100644 index 3e90389a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1254dd1-42f9-4caa-b50a-1fbe73241b50.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1328758-6517-4b63-9c26-6161fa5a4828.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1328758-6517-4b63-9c26-6161fa5a4828.lance deleted file mode 100644 index a28ceee64..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1328758-6517-4b63-9c26-6161fa5a4828.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d13e11a4-1350-418d-acb1-50a8717cdff2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d13e11a4-1350-418d-acb1-50a8717cdff2.lance deleted file mode 100644 index 6dda14648..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d13e11a4-1350-418d-acb1-50a8717cdff2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d17368f6-d2dd-4fcb-bb2c-a9b0c406d06b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d17368f6-d2dd-4fcb-bb2c-a9b0c406d06b.lance deleted file mode 100644 index 7d4d1c4dc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d17368f6-d2dd-4fcb-bb2c-a9b0c406d06b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1739d64-71cf-490e-ae39-8cafada4e06f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1739d64-71cf-490e-ae39-8cafada4e06f.lance deleted file mode 100644 index 978cc8647..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1739d64-71cf-490e-ae39-8cafada4e06f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d17c7245-31cc-4295-aaf0-f29b2aa125f3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d17c7245-31cc-4295-aaf0-f29b2aa125f3.lance deleted file mode 100644 index 2ca1090d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d17c7245-31cc-4295-aaf0-f29b2aa125f3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1803b0e-f61e-4a79-9b48-f2a249556b73.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1803b0e-f61e-4a79-9b48-f2a249556b73.lance deleted file mode 100644 index db2e09701..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1803b0e-f61e-4a79-9b48-f2a249556b73.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d19b6a4c-b294-4412-a924-de8da6f04556.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d19b6a4c-b294-4412-a924-de8da6f04556.lance deleted file mode 100644 index 3b7447e6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d19b6a4c-b294-4412-a924-de8da6f04556.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1ab4006-9812-4018-aa7e-cf52ae3bd4d7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1ab4006-9812-4018-aa7e-cf52ae3bd4d7.lance deleted file mode 100644 index 24713d743..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1ab4006-9812-4018-aa7e-cf52ae3bd4d7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1c41fbf-8b41-454e-abf6-9c05cf7bef01.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1c41fbf-8b41-454e-abf6-9c05cf7bef01.lance deleted file mode 100644 index db344ee22..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1c41fbf-8b41-454e-abf6-9c05cf7bef01.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1f035a3-a4ff-402c-86d1-904da4cd3356.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1f035a3-a4ff-402c-86d1-904da4cd3356.lance deleted file mode 100644 index 57845bad6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d1f035a3-a4ff-402c-86d1-904da4cd3356.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2488021-0c55-473e-89a8-c9fbf9ea4354.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2488021-0c55-473e-89a8-c9fbf9ea4354.lance deleted file mode 100644 index 490ae2911..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2488021-0c55-473e-89a8-c9fbf9ea4354.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2578da8-0cb9-446f-afa9-0e2d86cabeaf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2578da8-0cb9-446f-afa9-0e2d86cabeaf.lance deleted file mode 100644 index 142df1456..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2578da8-0cb9-446f-afa9-0e2d86cabeaf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d265d316-af25-47d7-917c-f326bce86b3a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d265d316-af25-47d7-917c-f326bce86b3a.lance deleted file mode 100644 index ee88c6cb7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d265d316-af25-47d7-917c-f326bce86b3a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2954a0f-f1ac-4e82-a175-22f31e1044b2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2954a0f-f1ac-4e82-a175-22f31e1044b2.lance deleted file mode 100644 index 45b4154e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2954a0f-f1ac-4e82-a175-22f31e1044b2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d29b35a5-a161-4de3-ab42-d2e4ce990c63.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d29b35a5-a161-4de3-ab42-d2e4ce990c63.lance deleted file mode 100644 index cd3d31c60..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d29b35a5-a161-4de3-ab42-d2e4ce990c63.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d29d1deb-8c88-4ff5-a418-7387d9d39f06.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d29d1deb-8c88-4ff5-a418-7387d9d39f06.lance deleted file mode 100644 index 902ec5da4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d29d1deb-8c88-4ff5-a418-7387d9d39f06.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2a587bc-bc19-4df8-944b-e49eda444aa8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2a587bc-bc19-4df8-944b-e49eda444aa8.lance deleted file mode 100644 index 458d33f4d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2a587bc-bc19-4df8-944b-e49eda444aa8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2aaa002-865f-4b96-8a06-5e4683f558c0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2aaa002-865f-4b96-8a06-5e4683f558c0.lance deleted file mode 100644 index 6021d1d5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2aaa002-865f-4b96-8a06-5e4683f558c0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2cdae63-2210-4772-943d-f2ad6d25b1c9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2cdae63-2210-4772-943d-f2ad6d25b1c9.lance deleted file mode 100644 index 157cab0c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2cdae63-2210-4772-943d-f2ad6d25b1c9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2db8d3a-61d3-4f1e-95d0-6015e02a2608.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2db8d3a-61d3-4f1e-95d0-6015e02a2608.lance deleted file mode 100644 index 9ea56543e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2db8d3a-61d3-4f1e-95d0-6015e02a2608.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2f1772a-9ad1-47ad-ac3f-f59fdc170fd4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2f1772a-9ad1-47ad-ac3f-f59fdc170fd4.lance deleted file mode 100644 index a8a4010e6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d2f1772a-9ad1-47ad-ac3f-f59fdc170fd4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d30570a7-ccdc-4600-9169-6ef5863261b2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d30570a7-ccdc-4600-9169-6ef5863261b2.lance deleted file mode 100644 index 77c7f22e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d30570a7-ccdc-4600-9169-6ef5863261b2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d31571b1-d8a2-4d99-aa43-17b00fa4f9ff.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d31571b1-d8a2-4d99-aa43-17b00fa4f9ff.lance deleted file mode 100644 index 5dcf0a209..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d31571b1-d8a2-4d99-aa43-17b00fa4f9ff.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d32fd451-0c53-4b40-a6e8-df2a240b405b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d32fd451-0c53-4b40-a6e8-df2a240b405b.lance deleted file mode 100644 index 3fddc17c2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d32fd451-0c53-4b40-a6e8-df2a240b405b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d3346c74-5e64-4f7d-be68-f3069d8d75d3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d3346c74-5e64-4f7d-be68-f3069d8d75d3.lance deleted file mode 100644 index 226328d0f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d3346c74-5e64-4f7d-be68-f3069d8d75d3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d343d56a-9803-4ad3-9acb-a1258eacbe67.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d343d56a-9803-4ad3-9acb-a1258eacbe67.lance deleted file mode 100644 index 3480ca6c3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d343d56a-9803-4ad3-9acb-a1258eacbe67.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d34468c5-15d2-49bf-85fb-778bbef9700c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d34468c5-15d2-49bf-85fb-778bbef9700c.lance deleted file mode 100644 index 29e4f2cfb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d34468c5-15d2-49bf-85fb-778bbef9700c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d35a0754-289f-471a-ba91-a8af6226e89b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d35a0754-289f-471a-ba91-a8af6226e89b.lance deleted file mode 100644 index f5172bfa0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d35a0754-289f-471a-ba91-a8af6226e89b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d35ece72-b5ea-4182-8d3e-0f31bb69ea4c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d35ece72-b5ea-4182-8d3e-0f31bb69ea4c.lance deleted file mode 100644 index cb3a2b101..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d35ece72-b5ea-4182-8d3e-0f31bb69ea4c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d3c26162-4b80-441a-8a0e-409b12bb94da.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d3c26162-4b80-441a-8a0e-409b12bb94da.lance deleted file mode 100644 index ce06a5046..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d3c26162-4b80-441a-8a0e-409b12bb94da.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d3e1626d-66e4-41be-96a0-55db71a26c03.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d3e1626d-66e4-41be-96a0-55db71a26c03.lance deleted file mode 100644 index aa5c5e51f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d3e1626d-66e4-41be-96a0-55db71a26c03.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d3e99229-c9e3-4260-8440-eabca1ccae9d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d3e99229-c9e3-4260-8440-eabca1ccae9d.lance deleted file mode 100644 index 1af9227c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d3e99229-c9e3-4260-8440-eabca1ccae9d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d3f4dbf1-30f4-451f-92c1-99704e1d505d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d3f4dbf1-30f4-451f-92c1-99704e1d505d.lance deleted file mode 100644 index 0531e3d5a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d3f4dbf1-30f4-451f-92c1-99704e1d505d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d40813fa-cef1-41c4-a5ed-9b23c61ed6e5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d40813fa-cef1-41c4-a5ed-9b23c61ed6e5.lance deleted file mode 100644 index c2e319541..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d40813fa-cef1-41c4-a5ed-9b23c61ed6e5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d43e713f-344a-4b45-a321-24ce20b52d29.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d43e713f-344a-4b45-a321-24ce20b52d29.lance deleted file mode 100644 index aa8dd8fc6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d43e713f-344a-4b45-a321-24ce20b52d29.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4589a26-74d0-408a-ae7f-4dfc68f07172.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4589a26-74d0-408a-ae7f-4dfc68f07172.lance deleted file mode 100644 index 2d44e855d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4589a26-74d0-408a-ae7f-4dfc68f07172.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d46a855e-50c1-49b2-bdb2-44847a82820f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d46a855e-50c1-49b2-bdb2-44847a82820f.lance deleted file mode 100644 index 59b683c2d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d46a855e-50c1-49b2-bdb2-44847a82820f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d46af764-d604-4730-9868-609e957e88c0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d46af764-d604-4730-9868-609e957e88c0.lance deleted file mode 100644 index 03c28ff64..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d46af764-d604-4730-9868-609e957e88c0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d49f21a0-6c93-4bcb-804b-be55cbd2e233.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d49f21a0-6c93-4bcb-804b-be55cbd2e233.lance deleted file mode 100644 index e76b2b24b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d49f21a0-6c93-4bcb-804b-be55cbd2e233.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4b40046-6e81-4b8b-952b-6f5c74519889.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4b40046-6e81-4b8b-952b-6f5c74519889.lance deleted file mode 100644 index 3553f9a6b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4b40046-6e81-4b8b-952b-6f5c74519889.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4bb08d9-7326-47e0-8d35-1986ff79c473.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4bb08d9-7326-47e0-8d35-1986ff79c473.lance deleted file mode 100644 index 69864b6d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4bb08d9-7326-47e0-8d35-1986ff79c473.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4c8fa52-22b5-46c6-917c-61e3bc45a9c5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4c8fa52-22b5-46c6-917c-61e3bc45a9c5.lance deleted file mode 100644 index 93775015f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4c8fa52-22b5-46c6-917c-61e3bc45a9c5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4d539bc-b992-4afe-9112-8856492e5047.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4d539bc-b992-4afe-9112-8856492e5047.lance deleted file mode 100644 index f0d39a725..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4d539bc-b992-4afe-9112-8856492e5047.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4d9cf4a-7862-47d3-886e-491ec2f2a232.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4d9cf4a-7862-47d3-886e-491ec2f2a232.lance deleted file mode 100644 index 38105ab1f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4d9cf4a-7862-47d3-886e-491ec2f2a232.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4f305eb-5509-4702-a728-e0cc73b9031c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4f305eb-5509-4702-a728-e0cc73b9031c.lance deleted file mode 100644 index c658fedb9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d4f305eb-5509-4702-a728-e0cc73b9031c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d5201cbd-f956-4d32-b054-ae2dd8399bfa.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d5201cbd-f956-4d32-b054-ae2dd8399bfa.lance deleted file mode 100644 index 61398af0f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d5201cbd-f956-4d32-b054-ae2dd8399bfa.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d52d3f24-8d85-4b9d-b508-eff90f5397fd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d52d3f24-8d85-4b9d-b508-eff90f5397fd.lance deleted file mode 100644 index 9ea7f3492..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d52d3f24-8d85-4b9d-b508-eff90f5397fd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d5471048-bc86-4ec9-b7df-954aab2de8d8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d5471048-bc86-4ec9-b7df-954aab2de8d8.lance deleted file mode 100644 index b9ddf15a6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d5471048-bc86-4ec9-b7df-954aab2de8d8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d55cc815-fd1d-4ce9-8fac-00837434edba.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d55cc815-fd1d-4ce9-8fac-00837434edba.lance deleted file mode 100644 index 2c4692a02..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d55cc815-fd1d-4ce9-8fac-00837434edba.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d575b955-5771-4732-b4f5-532e468c887f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d575b955-5771-4732-b4f5-532e468c887f.lance deleted file mode 100644 index 8e6c1990c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d575b955-5771-4732-b4f5-532e468c887f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d5859100-18bd-4543-9803-e37a444ea05e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d5859100-18bd-4543-9803-e37a444ea05e.lance deleted file mode 100644 index 3e6879981..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d5859100-18bd-4543-9803-e37a444ea05e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d589ae5e-0d45-4902-b53f-00f647269fbd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d589ae5e-0d45-4902-b53f-00f647269fbd.lance deleted file mode 100644 index 5e961000e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d589ae5e-0d45-4902-b53f-00f647269fbd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d58beb65-aa1b-4aec-a21b-90bca15c1857.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d58beb65-aa1b-4aec-a21b-90bca15c1857.lance deleted file mode 100644 index 53e2bd35b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d58beb65-aa1b-4aec-a21b-90bca15c1857.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d5b4c103-5c14-40e7-a096-50a7449258bf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d5b4c103-5c14-40e7-a096-50a7449258bf.lance deleted file mode 100644 index 9742bd146..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d5b4c103-5c14-40e7-a096-50a7449258bf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d5ef1ad0-e1d6-442c-bb03-c8c9e9ae219c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d5ef1ad0-e1d6-442c-bb03-c8c9e9ae219c.lance deleted file mode 100644 index c92801f5f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d5ef1ad0-e1d6-442c-bb03-c8c9e9ae219c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d613588f-10d1-4f72-9760-cafed222dde0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d613588f-10d1-4f72-9760-cafed222dde0.lance deleted file mode 100644 index 4a84fbdcd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d613588f-10d1-4f72-9760-cafed222dde0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d64e4c4c-892c-4307-b17a-3069b1406580.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d64e4c4c-892c-4307-b17a-3069b1406580.lance deleted file mode 100644 index 97223421d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d64e4c4c-892c-4307-b17a-3069b1406580.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d64f9f88-918d-47ce-ac9a-6902f476e2c4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d64f9f88-918d-47ce-ac9a-6902f476e2c4.lance deleted file mode 100644 index c47db82a3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d64f9f88-918d-47ce-ac9a-6902f476e2c4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d660961f-e06f-4ac0-990f-20b73385f679.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d660961f-e06f-4ac0-990f-20b73385f679.lance deleted file mode 100644 index 4b3cba9ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d660961f-e06f-4ac0-990f-20b73385f679.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d66d636c-427c-4eb1-8561-48da3a8ee303.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d66d636c-427c-4eb1-8561-48da3a8ee303.lance deleted file mode 100644 index 29f655867..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d66d636c-427c-4eb1-8561-48da3a8ee303.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d67ec665-5702-462b-8eda-9b27188584e2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d67ec665-5702-462b-8eda-9b27188584e2.lance deleted file mode 100644 index 46a3db130..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d67ec665-5702-462b-8eda-9b27188584e2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d682a0a1-47cf-4a88-ae1f-98573f46d8f2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d682a0a1-47cf-4a88-ae1f-98573f46d8f2.lance deleted file mode 100644 index f6a34b43f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d682a0a1-47cf-4a88-ae1f-98573f46d8f2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d6943ba0-61b4-4521-a0f8-39b4ca756345.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d6943ba0-61b4-4521-a0f8-39b4ca756345.lance deleted file mode 100644 index 30ded1ed3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d6943ba0-61b4-4521-a0f8-39b4ca756345.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d6e1905e-2c4d-4efd-ba83-a009ddda6223.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d6e1905e-2c4d-4efd-ba83-a009ddda6223.lance deleted file mode 100644 index ff92375be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d6e1905e-2c4d-4efd-ba83-a009ddda6223.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d6e61bbb-2750-4ea1-8f3c-687db12bb6fb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d6e61bbb-2750-4ea1-8f3c-687db12bb6fb.lance deleted file mode 100644 index 154633b79..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d6e61bbb-2750-4ea1-8f3c-687db12bb6fb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d6e899c7-e184-41e1-bce8-a4efbca38791.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d6e899c7-e184-41e1-bce8-a4efbca38791.lance deleted file mode 100644 index bdb828c25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d6e899c7-e184-41e1-bce8-a4efbca38791.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d709326b-b9a7-45c1-aeff-7facf1d39faa.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d709326b-b9a7-45c1-aeff-7facf1d39faa.lance deleted file mode 100644 index 8b64672a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d709326b-b9a7-45c1-aeff-7facf1d39faa.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d72287c4-8391-4ad8-85b1-cb99e5082c92.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d72287c4-8391-4ad8-85b1-cb99e5082c92.lance deleted file mode 100644 index 7f52bf4bc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d72287c4-8391-4ad8-85b1-cb99e5082c92.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7391195-7367-4f3b-b7f6-8938903a4703.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7391195-7367-4f3b-b7f6-8938903a4703.lance deleted file mode 100644 index d7f8776d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7391195-7367-4f3b-b7f6-8938903a4703.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d73fc26d-31eb-45f2-ae21-944e9e84314b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d73fc26d-31eb-45f2-ae21-944e9e84314b.lance deleted file mode 100644 index b04b608c4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d73fc26d-31eb-45f2-ae21-944e9e84314b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d75c90d3-8f45-4038-8480-069a283f2e32.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d75c90d3-8f45-4038-8480-069a283f2e32.lance deleted file mode 100644 index 42510293f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d75c90d3-8f45-4038-8480-069a283f2e32.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7885605-498f-41ea-a655-f1a05d92ffb6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7885605-498f-41ea-a655-f1a05d92ffb6.lance deleted file mode 100644 index edba08875..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7885605-498f-41ea-a655-f1a05d92ffb6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d78b0730-f41e-409d-9a45-40564bd9a2cd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d78b0730-f41e-409d-9a45-40564bd9a2cd.lance deleted file mode 100644 index 143c730f5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d78b0730-f41e-409d-9a45-40564bd9a2cd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7a0a50f-b195-4c58-83b6-f06728200d37.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7a0a50f-b195-4c58-83b6-f06728200d37.lance deleted file mode 100644 index 2453f1f03..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7a0a50f-b195-4c58-83b6-f06728200d37.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7b7748c-e7c4-4fd8-8146-3333dc2fca1f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7b7748c-e7c4-4fd8-8146-3333dc2fca1f.lance deleted file mode 100644 index afa32ebe4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7b7748c-e7c4-4fd8-8146-3333dc2fca1f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7c2ea48-9c49-4128-86f8-58319e1571ee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7c2ea48-9c49-4128-86f8-58319e1571ee.lance deleted file mode 100644 index 01b91924f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7c2ea48-9c49-4128-86f8-58319e1571ee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7cdffad-8ab0-4cf0-93a9-ae71fad3f6d6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7cdffad-8ab0-4cf0-93a9-ae71fad3f6d6.lance deleted file mode 100644 index a7bf2be44..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7cdffad-8ab0-4cf0-93a9-ae71fad3f6d6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7d4a950-13c1-4c8a-925f-e20b74f63ac8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7d4a950-13c1-4c8a-925f-e20b74f63ac8.lance deleted file mode 100644 index 2add52aae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7d4a950-13c1-4c8a-925f-e20b74f63ac8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7d4b835-8937-4089-971d-e0937e0b3cf9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7d4b835-8937-4089-971d-e0937e0b3cf9.lance deleted file mode 100644 index 5fc035f97..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7d4b835-8937-4089-971d-e0937e0b3cf9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7db8c0f-df5e-426c-8b01-1d2d464b5034.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7db8c0f-df5e-426c-8b01-1d2d464b5034.lance deleted file mode 100644 index c0f829862..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d7db8c0f-df5e-426c-8b01-1d2d464b5034.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d83218fc-e723-4853-bab4-b68d4abb21b4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d83218fc-e723-4853-bab4-b68d4abb21b4.lance deleted file mode 100644 index 2fdea2b2b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d83218fc-e723-4853-bab4-b68d4abb21b4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8367a18-288c-4933-b6fc-d6a820eaff0a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8367a18-288c-4933-b6fc-d6a820eaff0a.lance deleted file mode 100644 index dffd6a71c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8367a18-288c-4933-b6fc-d6a820eaff0a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8448b50-8080-40ef-afd8-b92379e42a34.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8448b50-8080-40ef-afd8-b92379e42a34.lance deleted file mode 100644 index 4b00458ea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8448b50-8080-40ef-afd8-b92379e42a34.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d85a70ce-8010-491b-b99d-18963bbbd0a9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d85a70ce-8010-491b-b99d-18963bbbd0a9.lance deleted file mode 100644 index f03fa7c1c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d85a70ce-8010-491b-b99d-18963bbbd0a9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d86cd760-9659-4fa3-9de9-b0da8c50f3bd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d86cd760-9659-4fa3-9de9-b0da8c50f3bd.lance deleted file mode 100644 index abc365151..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d86cd760-9659-4fa3-9de9-b0da8c50f3bd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8748a5e-babb-408d-bc14-6f48aea79abc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8748a5e-babb-408d-bc14-6f48aea79abc.lance deleted file mode 100644 index 45e5cb215..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8748a5e-babb-408d-bc14-6f48aea79abc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d87699a2-b4b9-4ed1-9779-76f94b4f9e4b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d87699a2-b4b9-4ed1-9779-76f94b4f9e4b.lance deleted file mode 100644 index 0b74531ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d87699a2-b4b9-4ed1-9779-76f94b4f9e4b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8b0a407-8ff8-40a7-9604-2ecb95087075.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8b0a407-8ff8-40a7-9604-2ecb95087075.lance deleted file mode 100644 index d6d8db8a2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8b0a407-8ff8-40a7-9604-2ecb95087075.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8b3a12b-9bbc-4b49-b7ac-d5176f47d245.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8b3a12b-9bbc-4b49-b7ac-d5176f47d245.lance deleted file mode 100644 index d44cf203d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8b3a12b-9bbc-4b49-b7ac-d5176f47d245.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8c13310-13be-42c5-92ec-a08c5ed14cbf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8c13310-13be-42c5-92ec-a08c5ed14cbf.lance deleted file mode 100644 index 976d56f89..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8c13310-13be-42c5-92ec-a08c5ed14cbf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8d75af1-1381-47a9-88dd-d9daf57b4524.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8d75af1-1381-47a9-88dd-d9daf57b4524.lance deleted file mode 100644 index bb0835d0d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8d75af1-1381-47a9-88dd-d9daf57b4524.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8e5fc71-99c3-42c0-8171-6c8f8d6734c2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8e5fc71-99c3-42c0-8171-6c8f8d6734c2.lance deleted file mode 100644 index faac22cf0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8e5fc71-99c3-42c0-8171-6c8f8d6734c2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8fb39a6-660b-449c-a2db-7cba8dbda728.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8fb39a6-660b-449c-a2db-7cba8dbda728.lance deleted file mode 100644 index 63606457d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d8fb39a6-660b-449c-a2db-7cba8dbda728.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9303d90-6d7b-412e-86ff-dcfa8f27162d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9303d90-6d7b-412e-86ff-dcfa8f27162d.lance deleted file mode 100644 index 94f8eb499..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9303d90-6d7b-412e-86ff-dcfa8f27162d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d93dafd4-317d-496e-949c-aa63aea3dcec.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d93dafd4-317d-496e-949c-aa63aea3dcec.lance deleted file mode 100644 index 40d3f978b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d93dafd4-317d-496e-949c-aa63aea3dcec.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d93f0dba-6190-49ce-b41a-c1fc353368e8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d93f0dba-6190-49ce-b41a-c1fc353368e8.lance deleted file mode 100644 index cd0e70676..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d93f0dba-6190-49ce-b41a-c1fc353368e8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d971b380-e8ff-4505-bf5b-1830ef8190a9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d971b380-e8ff-4505-bf5b-1830ef8190a9.lance deleted file mode 100644 index 42562b804..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d971b380-e8ff-4505-bf5b-1830ef8190a9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9734636-adb0-4b6f-b5a7-dbd29eb9755f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9734636-adb0-4b6f-b5a7-dbd29eb9755f.lance deleted file mode 100644 index 723d3dc9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9734636-adb0-4b6f-b5a7-dbd29eb9755f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d994544f-8107-47f0-9773-c1a79eb2a252.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d994544f-8107-47f0-9773-c1a79eb2a252.lance deleted file mode 100644 index d74167cd9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d994544f-8107-47f0-9773-c1a79eb2a252.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d99ca4bd-a568-4654-a463-d36b27c18bbe.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d99ca4bd-a568-4654-a463-d36b27c18bbe.lance deleted file mode 100644 index 21a90dff0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d99ca4bd-a568-4654-a463-d36b27c18bbe.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9a73ae7-33fd-442d-8548-e8ec024a1dee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9a73ae7-33fd-442d-8548-e8ec024a1dee.lance deleted file mode 100644 index 5621dc856..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9a73ae7-33fd-442d-8548-e8ec024a1dee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9a9b43b-15ec-43a3-8336-ba2c5a911b76.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9a9b43b-15ec-43a3-8336-ba2c5a911b76.lance deleted file mode 100644 index 420b54cda..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9a9b43b-15ec-43a3-8336-ba2c5a911b76.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9aab492-6339-4421-92e1-c452e9b5f2af.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9aab492-6339-4421-92e1-c452e9b5f2af.lance deleted file mode 100644 index 6e8a85567..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9aab492-6339-4421-92e1-c452e9b5f2af.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9b2aa82-5564-4ab3-aa1b-9b3f6f014fe2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9b2aa82-5564-4ab3-aa1b-9b3f6f014fe2.lance deleted file mode 100644 index 75f79dea8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9b2aa82-5564-4ab3-aa1b-9b3f6f014fe2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9c56bce-97ee-44e4-89aa-7e818c3f1cc1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9c56bce-97ee-44e4-89aa-7e818c3f1cc1.lance deleted file mode 100644 index 2165873a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9c56bce-97ee-44e4-89aa-7e818c3f1cc1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9cc45ec-8a3b-4c13-a1fe-37c1dce540f0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9cc45ec-8a3b-4c13-a1fe-37c1dce540f0.lance deleted file mode 100644 index 9654d1cb4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/d9cc45ec-8a3b-4c13-a1fe-37c1dce540f0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/da136241-e2ab-4513-8d34-595d44c78b66.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/da136241-e2ab-4513-8d34-595d44c78b66.lance deleted file mode 100644 index 59e14cbfc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/da136241-e2ab-4513-8d34-595d44c78b66.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/da47b939-a1da-418a-8e35-8787000e4b3c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/da47b939-a1da-418a-8e35-8787000e4b3c.lance deleted file mode 100644 index 73848ed7e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/da47b939-a1da-418a-8e35-8787000e4b3c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/da69124a-0528-42df-be96-6c5978711446.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/da69124a-0528-42df-be96-6c5978711446.lance deleted file mode 100644 index 74f626dc3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/da69124a-0528-42df-be96-6c5978711446.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/da6ab7a6-f74e-4392-864e-de14c8c72466.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/da6ab7a6-f74e-4392-864e-de14c8c72466.lance deleted file mode 100644 index e400a48c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/da6ab7a6-f74e-4392-864e-de14c8c72466.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dac94ac1-b572-4f12-a401-c2ade4a4618a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dac94ac1-b572-4f12-a401-c2ade4a4618a.lance deleted file mode 100644 index d3e534772..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dac94ac1-b572-4f12-a401-c2ade4a4618a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/daf92993-654e-49c6-aa48-8bc7b720f113.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/daf92993-654e-49c6-aa48-8bc7b720f113.lance deleted file mode 100644 index bc6a32346..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/daf92993-654e-49c6-aa48-8bc7b720f113.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db0096a7-2202-4367-b999-15a6cab85224.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db0096a7-2202-4367-b999-15a6cab85224.lance deleted file mode 100644 index b3a4f64d5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db0096a7-2202-4367-b999-15a6cab85224.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db235666-eb0c-4f0d-a531-98bc7c9b72b2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db235666-eb0c-4f0d-a531-98bc7c9b72b2.lance deleted file mode 100644 index 09aa08a4b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db235666-eb0c-4f0d-a531-98bc7c9b72b2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db5908a2-0d26-4f9e-ac81-a64b5f0d9670.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db5908a2-0d26-4f9e-ac81-a64b5f0d9670.lance deleted file mode 100644 index 0a4242b54..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db5908a2-0d26-4f9e-ac81-a64b5f0d9670.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db5a43fa-1e07-414d-9e26-8682d4457707.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db5a43fa-1e07-414d-9e26-8682d4457707.lance deleted file mode 100644 index 461ce2d3f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db5a43fa-1e07-414d-9e26-8682d4457707.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db5f9fb3-68a4-4b43-92bd-9931e89390d4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db5f9fb3-68a4-4b43-92bd-9931e89390d4.lance deleted file mode 100644 index 4f905f4d7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db5f9fb3-68a4-4b43-92bd-9931e89390d4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db65a1e6-7471-47e8-a8cf-7b7f0df7f93b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db65a1e6-7471-47e8-a8cf-7b7f0df7f93b.lance deleted file mode 100644 index dbfabc828..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db65a1e6-7471-47e8-a8cf-7b7f0df7f93b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db6d753d-77f1-4c4c-ab13-83db1c0a1b98.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db6d753d-77f1-4c4c-ab13-83db1c0a1b98.lance deleted file mode 100644 index 67a9b364b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db6d753d-77f1-4c4c-ab13-83db1c0a1b98.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db9ca7ec-ae11-44d3-bf40-d50ae4b29d45.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db9ca7ec-ae11-44d3-bf40-d50ae4b29d45.lance deleted file mode 100644 index 143eda6a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db9ca7ec-ae11-44d3-bf40-d50ae4b29d45.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db9f7738-dc30-4650-a0a8-311ec56e8066.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db9f7738-dc30-4650-a0a8-311ec56e8066.lance deleted file mode 100644 index e6520cd08..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/db9f7738-dc30-4650-a0a8-311ec56e8066.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dbb48f58-2655-4e14-b1fa-a057e31c3713.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dbb48f58-2655-4e14-b1fa-a057e31c3713.lance deleted file mode 100644 index fe3ef4106..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dbb48f58-2655-4e14-b1fa-a057e31c3713.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dbc352e8-b31b-442d-9f5d-1334dfffdc8a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dbc352e8-b31b-442d-9f5d-1334dfffdc8a.lance deleted file mode 100644 index 8daef8623..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dbc352e8-b31b-442d-9f5d-1334dfffdc8a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dc2acf07-d8d6-4bbf-ab9c-976637b9d3a8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dc2acf07-d8d6-4bbf-ab9c-976637b9d3a8.lance deleted file mode 100644 index d1a17613f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dc2acf07-d8d6-4bbf-ab9c-976637b9d3a8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dc64a28b-a5d0-4134-8ceb-1841207b71d2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dc64a28b-a5d0-4134-8ceb-1841207b71d2.lance deleted file mode 100644 index e1d925021..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dc64a28b-a5d0-4134-8ceb-1841207b71d2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dc7a669b-704f-4ea9-9504-4a1535e6f8a6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dc7a669b-704f-4ea9-9504-4a1535e6f8a6.lance deleted file mode 100644 index ca024342f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dc7a669b-704f-4ea9-9504-4a1535e6f8a6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dc842e49-76fb-456a-b892-d0a1a0fd2388.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dc842e49-76fb-456a-b892-d0a1a0fd2388.lance deleted file mode 100644 index cc5c49dc7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dc842e49-76fb-456a-b892-d0a1a0fd2388.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dc9cb828-d0ef-4fe9-8922-cadc97e2811b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dc9cb828-d0ef-4fe9-8922-cadc97e2811b.lance deleted file mode 100644 index 1f0bf661c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dc9cb828-d0ef-4fe9-8922-cadc97e2811b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dca5b0c2-50f2-449b-92db-d20524e7d6a4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dca5b0c2-50f2-449b-92db-d20524e7d6a4.lance deleted file mode 100644 index 75a3d00cd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dca5b0c2-50f2-449b-92db-d20524e7d6a4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dcc23167-6daf-483a-b698-62edbb7964bc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dcc23167-6daf-483a-b698-62edbb7964bc.lance deleted file mode 100644 index 70d9bc571..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dcc23167-6daf-483a-b698-62edbb7964bc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dce3bc82-e55b-4fb9-a3e1-2226a21987f8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dce3bc82-e55b-4fb9-a3e1-2226a21987f8.lance deleted file mode 100644 index b6905cb52..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dce3bc82-e55b-4fb9-a3e1-2226a21987f8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dceef559-5117-4b19-b78c-42f02038b50d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dceef559-5117-4b19-b78c-42f02038b50d.lance deleted file mode 100644 index 766f1275e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dceef559-5117-4b19-b78c-42f02038b50d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dcf18204-46d4-4966-9ed0-e09137b4544a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dcf18204-46d4-4966-9ed0-e09137b4544a.lance deleted file mode 100644 index 4c0b8a66e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dcf18204-46d4-4966-9ed0-e09137b4544a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dcf2bd4a-e116-4daa-96eb-3c2d5043b19e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dcf2bd4a-e116-4daa-96eb-3c2d5043b19e.lance deleted file mode 100644 index 24aeb15d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dcf2bd4a-e116-4daa-96eb-3c2d5043b19e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dcf98a9c-9a00-4a21-90c8-0d6c9c74d2e7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dcf98a9c-9a00-4a21-90c8-0d6c9c74d2e7.lance deleted file mode 100644 index 9b45e0e13..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dcf98a9c-9a00-4a21-90c8-0d6c9c74d2e7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dd1817b6-0dd7-4b40-a2f8-6a35c4525201.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dd1817b6-0dd7-4b40-a2f8-6a35c4525201.lance deleted file mode 100644 index 22a31de85..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dd1817b6-0dd7-4b40-a2f8-6a35c4525201.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dd1ec766-4dde-47a9-b257-b99359a6f85c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dd1ec766-4dde-47a9-b257-b99359a6f85c.lance deleted file mode 100644 index d2a2c1d9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dd1ec766-4dde-47a9-b257-b99359a6f85c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dd3e2556-e5b1-4f16-ac72-c208d70b537d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dd3e2556-e5b1-4f16-ac72-c208d70b537d.lance deleted file mode 100644 index a7022ab1b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dd3e2556-e5b1-4f16-ac72-c208d70b537d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dd50ca68-c408-4c00-be38-fc0cb3116b24.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dd50ca68-c408-4c00-be38-fc0cb3116b24.lance deleted file mode 100644 index 3cba9c183..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dd50ca68-c408-4c00-be38-fc0cb3116b24.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dd803db4-1a6c-42a8-81cf-3858d9014b60.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dd803db4-1a6c-42a8-81cf-3858d9014b60.lance deleted file mode 100644 index 9bf1c5293..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dd803db4-1a6c-42a8-81cf-3858d9014b60.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dda5283c-0639-4557-bbc3-d0e5f788930c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dda5283c-0639-4557-bbc3-d0e5f788930c.lance deleted file mode 100644 index 88a12a9c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dda5283c-0639-4557-bbc3-d0e5f788930c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ddaacdcb-3dfa-4a21-b31e-841d5d0c2b80.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ddaacdcb-3dfa-4a21-b31e-841d5d0c2b80.lance deleted file mode 100644 index 8d72babe2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ddaacdcb-3dfa-4a21-b31e-841d5d0c2b80.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dde3f3cf-0a31-4c53-9cce-fad9ead79f21.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dde3f3cf-0a31-4c53-9cce-fad9ead79f21.lance deleted file mode 100644 index 0e4aea595..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dde3f3cf-0a31-4c53-9cce-fad9ead79f21.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dde989cd-1c2b-4905-8c42-090b842e1391.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dde989cd-1c2b-4905-8c42-090b842e1391.lance deleted file mode 100644 index b477e839b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dde989cd-1c2b-4905-8c42-090b842e1391.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ddf63b77-fb3d-4923-8344-d6167d145d37.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ddf63b77-fb3d-4923-8344-d6167d145d37.lance deleted file mode 100644 index 03b8303e9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ddf63b77-fb3d-4923-8344-d6167d145d37.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ddfd6d07-6566-4166-81f6-2fe0a67c1bad.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ddfd6d07-6566-4166-81f6-2fe0a67c1bad.lance deleted file mode 100644 index 777afb730..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ddfd6d07-6566-4166-81f6-2fe0a67c1bad.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/de037428-77c4-4cc9-9393-7897289f23e6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/de037428-77c4-4cc9-9393-7897289f23e6.lance deleted file mode 100644 index 9465cd8ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/de037428-77c4-4cc9-9393-7897289f23e6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/de21cc83-7af6-4edb-a13c-f977aaacbcb3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/de21cc83-7af6-4edb-a13c-f977aaacbcb3.lance deleted file mode 100644 index b557fc59e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/de21cc83-7af6-4edb-a13c-f977aaacbcb3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/de33149d-882c-4ffb-ad03-f8bd351c85ef.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/de33149d-882c-4ffb-ad03-f8bd351c85ef.lance deleted file mode 100644 index 04dbea732..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/de33149d-882c-4ffb-ad03-f8bd351c85ef.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/de90de89-f28d-400f-94be-43db4f789232.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/de90de89-f28d-400f-94be-43db4f789232.lance deleted file mode 100644 index e153d29cc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/de90de89-f28d-400f-94be-43db4f789232.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dea816d6-346e-4f38-8b00-32bfd57fda68.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dea816d6-346e-4f38-8b00-32bfd57fda68.lance deleted file mode 100644 index 4c6add558..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dea816d6-346e-4f38-8b00-32bfd57fda68.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/deb972b9-d0f7-472c-abac-7ce414a3ed43.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/deb972b9-d0f7-472c-abac-7ce414a3ed43.lance deleted file mode 100644 index 7738a0d79..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/deb972b9-d0f7-472c-abac-7ce414a3ed43.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df03c54e-f3e0-453b-8fd4-6053a52cd4a7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df03c54e-f3e0-453b-8fd4-6053a52cd4a7.lance deleted file mode 100644 index e3a279bc8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df03c54e-f3e0-453b-8fd4-6053a52cd4a7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df0dc235-210f-41ae-abed-8818c09b6663.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df0dc235-210f-41ae-abed-8818c09b6663.lance deleted file mode 100644 index b4612aff8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df0dc235-210f-41ae-abed-8818c09b6663.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df1df84f-66c2-444d-9ebb-14fa909df75f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df1df84f-66c2-444d-9ebb-14fa909df75f.lance deleted file mode 100644 index f2b29a853..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df1df84f-66c2-444d-9ebb-14fa909df75f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df1e5e9f-9209-4e74-8ed5-d273e23e4adf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df1e5e9f-9209-4e74-8ed5-d273e23e4adf.lance deleted file mode 100644 index fd079ddec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df1e5e9f-9209-4e74-8ed5-d273e23e4adf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df21d698-0585-4596-80bf-2d9a85b03e22.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df21d698-0585-4596-80bf-2d9a85b03e22.lance deleted file mode 100644 index e59abdca7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df21d698-0585-4596-80bf-2d9a85b03e22.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df23a043-263c-45b8-9d5c-5467c10172f3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df23a043-263c-45b8-9d5c-5467c10172f3.lance deleted file mode 100644 index c5b3bf84a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df23a043-263c-45b8-9d5c-5467c10172f3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df33865b-cb42-4564-8e5d-fd6b44431077.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df33865b-cb42-4564-8e5d-fd6b44431077.lance deleted file mode 100644 index 7b33a6824..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df33865b-cb42-4564-8e5d-fd6b44431077.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df64923e-c21c-4dad-a942-541bdc0bf8c6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df64923e-c21c-4dad-a942-541bdc0bf8c6.lance deleted file mode 100644 index a16c25bca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df64923e-c21c-4dad-a942-541bdc0bf8c6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df73476d-5aac-4c7c-9382-f68639d329bf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df73476d-5aac-4c7c-9382-f68639d329bf.lance deleted file mode 100644 index b0e7a4253..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df73476d-5aac-4c7c-9382-f68639d329bf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df8bb540-0002-494e-8f41-02266a858d2c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df8bb540-0002-494e-8f41-02266a858d2c.lance deleted file mode 100644 index 5ed1e0515..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/df8bb540-0002-494e-8f41-02266a858d2c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dfd10806-769d-45e7-a3f0-92cf9af06905.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dfd10806-769d-45e7-a3f0-92cf9af06905.lance deleted file mode 100644 index 1f5333267..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dfd10806-769d-45e7-a3f0-92cf9af06905.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dfe0d59b-51de-4fe3-ba9c-a2fb190b9c88.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dfe0d59b-51de-4fe3-ba9c-a2fb190b9c88.lance deleted file mode 100644 index 6e894336a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dfe0d59b-51de-4fe3-ba9c-a2fb190b9c88.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dfefdc06-8ec5-4f09-8801-395e041f7a2d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dfefdc06-8ec5-4f09-8801-395e041f7a2d.lance deleted file mode 100644 index d7cdf7c7b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dfefdc06-8ec5-4f09-8801-395e041f7a2d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dff5da97-766d-4566-b346-9c08374e0c47.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dff5da97-766d-4566-b346-9c08374e0c47.lance deleted file mode 100644 index bc9c3cabb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/dff5da97-766d-4566-b346-9c08374e0c47.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e01d3977-07a9-4dcf-af14-80cb3259ac47.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e01d3977-07a9-4dcf-af14-80cb3259ac47.lance deleted file mode 100644 index 76dde7689..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e01d3977-07a9-4dcf-af14-80cb3259ac47.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0280d63-935f-459e-88fb-929dee3311ed.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0280d63-935f-459e-88fb-929dee3311ed.lance deleted file mode 100644 index 316ee8507..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0280d63-935f-459e-88fb-929dee3311ed.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e034addc-9530-49c2-9967-ce4be3a13917.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e034addc-9530-49c2-9967-ce4be3a13917.lance deleted file mode 100644 index 3423df072..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e034addc-9530-49c2-9967-ce4be3a13917.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0358983-2807-409a-acde-32f776534038.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0358983-2807-409a-acde-32f776534038.lance deleted file mode 100644 index 770943af0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0358983-2807-409a-acde-32f776534038.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e04ed43c-c698-4263-8639-4bf05a09074c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e04ed43c-c698-4263-8639-4bf05a09074c.lance deleted file mode 100644 index 227013e25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e04ed43c-c698-4263-8639-4bf05a09074c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0736343-639b-47d0-b5a8-8006470da90b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0736343-639b-47d0-b5a8-8006470da90b.lance deleted file mode 100644 index 4c9404653..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0736343-639b-47d0-b5a8-8006470da90b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0774728-942e-4277-85f5-9f974537dca3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0774728-942e-4277-85f5-9f974537dca3.lance deleted file mode 100644 index b4e5b789c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0774728-942e-4277-85f5-9f974537dca3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e07b24e2-bd67-46cc-9c8f-98bdf2356402.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e07b24e2-bd67-46cc-9c8f-98bdf2356402.lance deleted file mode 100644 index b6579ec18..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e07b24e2-bd67-46cc-9c8f-98bdf2356402.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0a8d0c1-b84f-47a0-b679-009c574caa44.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0a8d0c1-b84f-47a0-b679-009c574caa44.lance deleted file mode 100644 index 746bfdc2b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0a8d0c1-b84f-47a0-b679-009c574caa44.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0b9a43e-797d-452b-bf5f-d8b5aff99e0c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0b9a43e-797d-452b-bf5f-d8b5aff99e0c.lance deleted file mode 100644 index 42cb8c72f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0b9a43e-797d-452b-bf5f-d8b5aff99e0c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0c9a737-3321-4711-a741-62f45358a6cd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0c9a737-3321-4711-a741-62f45358a6cd.lance deleted file mode 100644 index f445cefef..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0c9a737-3321-4711-a741-62f45358a6cd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0cb7bb8-ec84-4656-af23-9f71bd2f05ab.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0cb7bb8-ec84-4656-af23-9f71bd2f05ab.lance deleted file mode 100644 index 6e57274d3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e0cb7bb8-ec84-4656-af23-9f71bd2f05ab.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e12f656c-19b9-4351-b916-2013bf3b08e6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e12f656c-19b9-4351-b916-2013bf3b08e6.lance deleted file mode 100644 index 93ce1dd38..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e12f656c-19b9-4351-b916-2013bf3b08e6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e135ad12-0260-49a5-8900-f4effcfa8f88.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e135ad12-0260-49a5-8900-f4effcfa8f88.lance deleted file mode 100644 index 9ba48ae9f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e135ad12-0260-49a5-8900-f4effcfa8f88.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e1486f23-8b4b-4017-80a9-224873fbe6bd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e1486f23-8b4b-4017-80a9-224873fbe6bd.lance deleted file mode 100644 index 535f7896a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e1486f23-8b4b-4017-80a9-224873fbe6bd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e17eb3a5-b5d6-41e2-ab3f-235c823e8eea.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e17eb3a5-b5d6-41e2-ab3f-235c823e8eea.lance deleted file mode 100644 index b1773c2f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e17eb3a5-b5d6-41e2-ab3f-235c823e8eea.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e190091a-e6d4-4cca-9daa-b17e90ddcef9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e190091a-e6d4-4cca-9daa-b17e90ddcef9.lance deleted file mode 100644 index fb5c84669..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e190091a-e6d4-4cca-9daa-b17e90ddcef9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e1905470-7e6d-4b69-a8e3-531ecc584597.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e1905470-7e6d-4b69-a8e3-531ecc584597.lance deleted file mode 100644 index 3c1cff610..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e1905470-7e6d-4b69-a8e3-531ecc584597.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e1ed2ef5-b41a-4ff8-a749-ba7462593da0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e1ed2ef5-b41a-4ff8-a749-ba7462593da0.lance deleted file mode 100644 index 710d8e942..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e1ed2ef5-b41a-4ff8-a749-ba7462593da0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2074ce9-bc83-416a-a18d-4cfdb001dfae.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2074ce9-bc83-416a-a18d-4cfdb001dfae.lance deleted file mode 100644 index 0bdf517e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2074ce9-bc83-416a-a18d-4cfdb001dfae.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e21c31b5-8849-48ca-9728-df5736a1cdf1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e21c31b5-8849-48ca-9728-df5736a1cdf1.lance deleted file mode 100644 index dd192a5e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e21c31b5-8849-48ca-9728-df5736a1cdf1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2211ae0-2e56-4c2b-b1b1-e54ebedd4bf8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2211ae0-2e56-4c2b-b1b1-e54ebedd4bf8.lance deleted file mode 100644 index cc85de6f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2211ae0-2e56-4c2b-b1b1-e54ebedd4bf8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e22fa1ec-615e-4146-b8fe-a459abc2edb7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e22fa1ec-615e-4146-b8fe-a459abc2edb7.lance deleted file mode 100644 index c529cd1b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e22fa1ec-615e-4146-b8fe-a459abc2edb7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2582852-a937-43dc-980e-4422611f03d4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2582852-a937-43dc-980e-4422611f03d4.lance deleted file mode 100644 index 55c60711d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2582852-a937-43dc-980e-4422611f03d4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e262c3bd-d4b0-449d-bf66-a31ba7f7a107.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e262c3bd-d4b0-449d-bf66-a31ba7f7a107.lance deleted file mode 100644 index 83f79cfc6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e262c3bd-d4b0-449d-bf66-a31ba7f7a107.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2908d73-af8b-4b3b-885f-a9d22df6b845.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2908d73-af8b-4b3b-885f-a9d22df6b845.lance deleted file mode 100644 index c85d47073..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2908d73-af8b-4b3b-885f-a9d22df6b845.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2a43640-5996-4470-9188-095a6cec3527.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2a43640-5996-4470-9188-095a6cec3527.lance deleted file mode 100644 index 967872315..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2a43640-5996-4470-9188-095a6cec3527.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2b0f036-9c35-49cc-b13a-30a69292c4f4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2b0f036-9c35-49cc-b13a-30a69292c4f4.lance deleted file mode 100644 index eb700acd9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2b0f036-9c35-49cc-b13a-30a69292c4f4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2d7da14-97b9-44fa-a559-406612c66e35.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2d7da14-97b9-44fa-a559-406612c66e35.lance deleted file mode 100644 index a9da7a9f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2d7da14-97b9-44fa-a559-406612c66e35.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2f55f17-7f28-4f01-acd1-7fdfcf5e01bd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2f55f17-7f28-4f01-acd1-7fdfcf5e01bd.lance deleted file mode 100644 index ef39550a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e2f55f17-7f28-4f01-acd1-7fdfcf5e01bd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3054728-094c-42c6-a855-fdbb1e6a8a0b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3054728-094c-42c6-a855-fdbb1e6a8a0b.lance deleted file mode 100644 index d8531ab31..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3054728-094c-42c6-a855-fdbb1e6a8a0b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e311b40c-4e99-4c36-8d01-c47db9dc6355.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e311b40c-4e99-4c36-8d01-c47db9dc6355.lance deleted file mode 100644 index bc56cf469..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e311b40c-4e99-4c36-8d01-c47db9dc6355.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e313bdc3-8177-4d2b-83ae-fe3c26f63fc8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e313bdc3-8177-4d2b-83ae-fe3c26f63fc8.lance deleted file mode 100644 index 57e502389..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e313bdc3-8177-4d2b-83ae-fe3c26f63fc8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3362ba2-3cf1-42f2-afc8-dc62be8fbc0e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3362ba2-3cf1-42f2-afc8-dc62be8fbc0e.lance deleted file mode 100644 index b2e323fe4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3362ba2-3cf1-42f2-afc8-dc62be8fbc0e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e33775f5-8bb3-43ea-9921-33c14d43ccf5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e33775f5-8bb3-43ea-9921-33c14d43ccf5.lance deleted file mode 100644 index acf6d023f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e33775f5-8bb3-43ea-9921-33c14d43ccf5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e365a583-d70e-49d7-ba25-df69f6b474d7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e365a583-d70e-49d7-ba25-df69f6b474d7.lance deleted file mode 100644 index fba8cadce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e365a583-d70e-49d7-ba25-df69f6b474d7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e37ccff7-3c7a-43bc-9cab-06460dd83290.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e37ccff7-3c7a-43bc-9cab-06460dd83290.lance deleted file mode 100644 index dd675b3e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e37ccff7-3c7a-43bc-9cab-06460dd83290.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e38c004f-c928-45fd-91f5-872a8735d531.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e38c004f-c928-45fd-91f5-872a8735d531.lance deleted file mode 100644 index 6f89caed0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e38c004f-c928-45fd-91f5-872a8735d531.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3cd27e9-13ea-4f46-bd16-6a140b44f453.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3cd27e9-13ea-4f46-bd16-6a140b44f453.lance deleted file mode 100644 index 4a0c1d4ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3cd27e9-13ea-4f46-bd16-6a140b44f453.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3cead27-c37a-487a-a6e3-0c65c8759226.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3cead27-c37a-487a-a6e3-0c65c8759226.lance deleted file mode 100644 index 75272548d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3cead27-c37a-487a-a6e3-0c65c8759226.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3eb516b-d6f9-4c28-8bdb-1130456d24f3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3eb516b-d6f9-4c28-8bdb-1130456d24f3.lance deleted file mode 100644 index 759b91aca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3eb516b-d6f9-4c28-8bdb-1130456d24f3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3fae1e3-57b4-4b6a-9122-b146f5f96b83.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3fae1e3-57b4-4b6a-9122-b146f5f96b83.lance deleted file mode 100644 index e5b352874..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e3fae1e3-57b4-4b6a-9122-b146f5f96b83.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4028df8-d2db-4b4f-b648-fdeb6bc2adaf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4028df8-d2db-4b4f-b648-fdeb6bc2adaf.lance deleted file mode 100644 index 3785956f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4028df8-d2db-4b4f-b648-fdeb6bc2adaf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4150300-bfb5-470b-8227-55f3f470459b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4150300-bfb5-470b-8227-55f3f470459b.lance deleted file mode 100644 index 26194c085..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4150300-bfb5-470b-8227-55f3f470459b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e415638f-3653-4fb8-bf6b-41b6e2a3a132.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e415638f-3653-4fb8-bf6b-41b6e2a3a132.lance deleted file mode 100644 index f01f29537..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e415638f-3653-4fb8-bf6b-41b6e2a3a132.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e42f79c5-3d87-426b-85ee-95e6b88183dc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e42f79c5-3d87-426b-85ee-95e6b88183dc.lance deleted file mode 100644 index c3080bb8c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e42f79c5-3d87-426b-85ee-95e6b88183dc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4536c6c-d59c-4c98-b764-2ce584976d7a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4536c6c-d59c-4c98-b764-2ce584976d7a.lance deleted file mode 100644 index 69a623c65..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4536c6c-d59c-4c98-b764-2ce584976d7a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e468b8bc-25fe-480e-a67c-7d72a4f494c9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e468b8bc-25fe-480e-a67c-7d72a4f494c9.lance deleted file mode 100644 index ca0785fe5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e468b8bc-25fe-480e-a67c-7d72a4f494c9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e47a3b67-52cb-44f4-8677-a3ad73624ae4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e47a3b67-52cb-44f4-8677-a3ad73624ae4.lance deleted file mode 100644 index 216e45c87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e47a3b67-52cb-44f4-8677-a3ad73624ae4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e486f706-b8c5-4df4-a4d7-d29e578ed1d3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e486f706-b8c5-4df4-a4d7-d29e578ed1d3.lance deleted file mode 100644 index 485a83c49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e486f706-b8c5-4df4-a4d7-d29e578ed1d3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e495eab6-428c-4fe8-8b26-6674d1b8c457.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e495eab6-428c-4fe8-8b26-6674d1b8c457.lance deleted file mode 100644 index 59f524fa9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e495eab6-428c-4fe8-8b26-6674d1b8c457.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4c362e6-aa75-4b7d-9a17-c889b6e7f13e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4c362e6-aa75-4b7d-9a17-c889b6e7f13e.lance deleted file mode 100644 index 613c9ab47..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4c362e6-aa75-4b7d-9a17-c889b6e7f13e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4cbb808-fc50-47e1-9998-81b94eec180b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4cbb808-fc50-47e1-9998-81b94eec180b.lance deleted file mode 100644 index 68ee16ab3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4cbb808-fc50-47e1-9998-81b94eec180b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4db13dd-5dad-41d7-bdec-b3f5a90cca76.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4db13dd-5dad-41d7-bdec-b3f5a90cca76.lance deleted file mode 100644 index fa5e667d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4db13dd-5dad-41d7-bdec-b3f5a90cca76.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4dd74bb-d932-4b59-8ff3-b0af2c8bbfa9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4dd74bb-d932-4b59-8ff3-b0af2c8bbfa9.lance deleted file mode 100644 index 9eef72c73..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4dd74bb-d932-4b59-8ff3-b0af2c8bbfa9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4e96d45-3da2-4204-9cdb-7b529d306003.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4e96d45-3da2-4204-9cdb-7b529d306003.lance deleted file mode 100644 index 3f3c6f4e8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4e96d45-3da2-4204-9cdb-7b529d306003.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4f238a8-01c1-43dc-9410-93577445cce3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4f238a8-01c1-43dc-9410-93577445cce3.lance deleted file mode 100644 index 894b8dff3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e4f238a8-01c1-43dc-9410-93577445cce3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e52ba552-ae0a-4434-bd0a-41769b99b276.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e52ba552-ae0a-4434-bd0a-41769b99b276.lance deleted file mode 100644 index 82baf259c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e52ba552-ae0a-4434-bd0a-41769b99b276.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e52c16e4-ae4b-4bd1-8089-e216a76c9148.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e52c16e4-ae4b-4bd1-8089-e216a76c9148.lance deleted file mode 100644 index fabe885a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e52c16e4-ae4b-4bd1-8089-e216a76c9148.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e52d634c-ec4e-4ee2-9c30-977f3e69e932.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e52d634c-ec4e-4ee2-9c30-977f3e69e932.lance deleted file mode 100644 index 759164148..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e52d634c-ec4e-4ee2-9c30-977f3e69e932.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e554fc0b-414d-41d5-9bdd-8279b49dd4c1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e554fc0b-414d-41d5-9bdd-8279b49dd4c1.lance deleted file mode 100644 index a1aecb702..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e554fc0b-414d-41d5-9bdd-8279b49dd4c1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e55cd0e7-28b7-4ec4-878d-d284a4cc89d5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e55cd0e7-28b7-4ec4-878d-d284a4cc89d5.lance deleted file mode 100644 index c4a7cd485..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e55cd0e7-28b7-4ec4-878d-d284a4cc89d5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e595ceb4-4a05-4f51-9218-5226177b0c2c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e595ceb4-4a05-4f51-9218-5226177b0c2c.lance deleted file mode 100644 index c08fac77b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e595ceb4-4a05-4f51-9218-5226177b0c2c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e5b81974-5592-451f-9699-c0f43c4cfe87.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e5b81974-5592-451f-9699-c0f43c4cfe87.lance deleted file mode 100644 index ad0cfb039..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e5b81974-5592-451f-9699-c0f43c4cfe87.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e5b99dd3-c7b8-4853-b2bf-c1c2e26aafec.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e5b99dd3-c7b8-4853-b2bf-c1c2e26aafec.lance deleted file mode 100644 index 37b8305b3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e5b99dd3-c7b8-4853-b2bf-c1c2e26aafec.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e5e38bfd-ddd1-4d4d-871d-6f2101bc888a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e5e38bfd-ddd1-4d4d-871d-6f2101bc888a.lance deleted file mode 100644 index d9a7e8415..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e5e38bfd-ddd1-4d4d-871d-6f2101bc888a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e5ffb612-bdf6-426b-8769-fd3e17a0311e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e5ffb612-bdf6-426b-8769-fd3e17a0311e.lance deleted file mode 100644 index 16189f8e4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e5ffb612-bdf6-426b-8769-fd3e17a0311e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e604485b-2e91-4716-8825-b368c4d3824c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e604485b-2e91-4716-8825-b368c4d3824c.lance deleted file mode 100644 index 9af0d45af..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e604485b-2e91-4716-8825-b368c4d3824c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e61834cf-e74e-4afb-8880-8a831c754a01.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e61834cf-e74e-4afb-8880-8a831c754a01.lance deleted file mode 100644 index d85d8a795..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e61834cf-e74e-4afb-8880-8a831c754a01.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6363f0d-9b2c-42c3-883f-66406aee4193.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6363f0d-9b2c-42c3-883f-66406aee4193.lance deleted file mode 100644 index 8025a3a92..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6363f0d-9b2c-42c3-883f-66406aee4193.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e66174d8-c253-4e4b-ad62-dfc436e34ff2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e66174d8-c253-4e4b-ad62-dfc436e34ff2.lance deleted file mode 100644 index 3cf37edf5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e66174d8-c253-4e4b-ad62-dfc436e34ff2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e696b093-7f76-40c2-8821-c3720149cc31.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e696b093-7f76-40c2-8821-c3720149cc31.lance deleted file mode 100644 index 24394cbb4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e696b093-7f76-40c2-8821-c3720149cc31.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6a76bd4-4e05-481b-ad97-cc0f85048873.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6a76bd4-4e05-481b-ad97-cc0f85048873.lance deleted file mode 100644 index cde50a0cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6a76bd4-4e05-481b-ad97-cc0f85048873.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6b23cb8-51b6-4e02-993a-10f836f93056.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6b23cb8-51b6-4e02-993a-10f836f93056.lance deleted file mode 100644 index 8be4e2d2a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6b23cb8-51b6-4e02-993a-10f836f93056.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6c337af-5791-4f55-8323-ed8982898724.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6c337af-5791-4f55-8323-ed8982898724.lance deleted file mode 100644 index 1388d67e3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6c337af-5791-4f55-8323-ed8982898724.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6d918df-39a5-4fc1-86d4-d3f2cb40096a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6d918df-39a5-4fc1-86d4-d3f2cb40096a.lance deleted file mode 100644 index 64e515a89..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6d918df-39a5-4fc1-86d4-d3f2cb40096a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6f510bc-e7d5-4996-a0cc-aa0579d00c9a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6f510bc-e7d5-4996-a0cc-aa0579d00c9a.lance deleted file mode 100644 index ac9fc1187..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e6f510bc-e7d5-4996-a0cc-aa0579d00c9a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e70d0ed6-903c-433c-9ef9-d22b42cc965c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e70d0ed6-903c-433c-9ef9-d22b42cc965c.lance deleted file mode 100644 index c03327cc4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e70d0ed6-903c-433c-9ef9-d22b42cc965c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7240db3-0fc6-4a95-8188-c331ccdcc3eb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7240db3-0fc6-4a95-8188-c331ccdcc3eb.lance deleted file mode 100644 index ef82494e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7240db3-0fc6-4a95-8188-c331ccdcc3eb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7267836-9fa4-4093-91d8-a7cd12c76ad4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7267836-9fa4-4093-91d8-a7cd12c76ad4.lance deleted file mode 100644 index 562f60d73..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7267836-9fa4-4093-91d8-a7cd12c76ad4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e72fd203-6f65-43d7-a9f8-4956ecf97d40.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e72fd203-6f65-43d7-a9f8-4956ecf97d40.lance deleted file mode 100644 index 9323a84a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e72fd203-6f65-43d7-a9f8-4956ecf97d40.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7366f7b-fb70-4fe7-b728-bcbdfa9114de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7366f7b-fb70-4fe7-b728-bcbdfa9114de.lance deleted file mode 100644 index 93a77bfc5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7366f7b-fb70-4fe7-b728-bcbdfa9114de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7467b81-6df4-4bbc-89c3-7eda45aaf469.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7467b81-6df4-4bbc-89c3-7eda45aaf469.lance deleted file mode 100644 index 9481489f8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7467b81-6df4-4bbc-89c3-7eda45aaf469.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e76623c9-e5ab-4d3f-a277-105bf24b6c58.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e76623c9-e5ab-4d3f-a277-105bf24b6c58.lance deleted file mode 100644 index 3a5fd9bb5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e76623c9-e5ab-4d3f-a277-105bf24b6c58.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e77852b8-c78a-4ae7-a9ee-76eb4090dad2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e77852b8-c78a-4ae7-a9ee-76eb4090dad2.lance deleted file mode 100644 index 7bc863173..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e77852b8-c78a-4ae7-a9ee-76eb4090dad2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e77ce66a-d1db-4691-91bc-a915fa551344.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e77ce66a-d1db-4691-91bc-a915fa551344.lance deleted file mode 100644 index 2a37ed497..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e77ce66a-d1db-4691-91bc-a915fa551344.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7943703-465b-42de-a9da-aeac9a47f3de.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7943703-465b-42de-a9da-aeac9a47f3de.lance deleted file mode 100644 index d374457f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7943703-465b-42de-a9da-aeac9a47f3de.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7b2205e-5252-4c4f-b459-af3ea7b9359d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7b2205e-5252-4c4f-b459-af3ea7b9359d.lance deleted file mode 100644 index 349327620..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7b2205e-5252-4c4f-b459-af3ea7b9359d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7b6b21a-bc34-4a19-afc9-81f138aa8000.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7b6b21a-bc34-4a19-afc9-81f138aa8000.lance deleted file mode 100644 index c4245c1b0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7b6b21a-bc34-4a19-afc9-81f138aa8000.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7c6673e-37e7-4423-a1e4-375b33c7f5c8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7c6673e-37e7-4423-a1e4-375b33c7f5c8.lance deleted file mode 100644 index cd2e8c739..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7c6673e-37e7-4423-a1e4-375b33c7f5c8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7d65062-ec5f-4f3f-8baf-c6f6284b93f2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7d65062-ec5f-4f3f-8baf-c6f6284b93f2.lance deleted file mode 100644 index 46870523c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7d65062-ec5f-4f3f-8baf-c6f6284b93f2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7f437df-2f7a-4983-9345-653c1bf45335.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7f437df-2f7a-4983-9345-653c1bf45335.lance deleted file mode 100644 index 62473b89c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7f437df-2f7a-4983-9345-653c1bf45335.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7fc1afe-a49c-44c7-a006-08c03cc30216.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7fc1afe-a49c-44c7-a006-08c03cc30216.lance deleted file mode 100644 index 5eb9f0e8f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e7fc1afe-a49c-44c7-a006-08c03cc30216.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e802a367-95ee-4769-a568-6162c561f559.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e802a367-95ee-4769-a568-6162c561f559.lance deleted file mode 100644 index 747aef205..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e802a367-95ee-4769-a568-6162c561f559.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e8628861-d641-479f-95fc-fc12703a86c9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e8628861-d641-479f-95fc-fc12703a86c9.lance deleted file mode 100644 index f444b7116..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e8628861-d641-479f-95fc-fc12703a86c9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e86d0bc8-f851-49ba-9df6-31795775ad78.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e86d0bc8-f851-49ba-9df6-31795775ad78.lance deleted file mode 100644 index d9faeb4eb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e86d0bc8-f851-49ba-9df6-31795775ad78.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e87ee43a-24ca-4179-b7e1-f625ff7ba37c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e87ee43a-24ca-4179-b7e1-f625ff7ba37c.lance deleted file mode 100644 index 4f5da0c82..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e87ee43a-24ca-4179-b7e1-f625ff7ba37c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e89d4cef-3737-4d3a-8b40-73a44ff11929.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e89d4cef-3737-4d3a-8b40-73a44ff11929.lance deleted file mode 100644 index b0b250e80..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e89d4cef-3737-4d3a-8b40-73a44ff11929.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e8a7feb8-1ca2-44e2-b570-a276a7992862.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e8a7feb8-1ca2-44e2-b570-a276a7992862.lance deleted file mode 100644 index f76d71ad1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e8a7feb8-1ca2-44e2-b570-a276a7992862.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e8bbe275-c61f-4b3f-8dfb-03231039fbeb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e8bbe275-c61f-4b3f-8dfb-03231039fbeb.lance deleted file mode 100644 index b3c7bff9e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e8bbe275-c61f-4b3f-8dfb-03231039fbeb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e8dfe0dc-f7b8-466a-8674-f6f3b2cc11a6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e8dfe0dc-f7b8-466a-8674-f6f3b2cc11a6.lance deleted file mode 100644 index 68d3032c8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e8dfe0dc-f7b8-466a-8674-f6f3b2cc11a6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e900485f-40b1-4294-93a0-75a2e4a9df90.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e900485f-40b1-4294-93a0-75a2e4a9df90.lance deleted file mode 100644 index 6840b5b70..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e900485f-40b1-4294-93a0-75a2e4a9df90.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9137eac-df59-496f-8788-8596666aecfa.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9137eac-df59-496f-8788-8596666aecfa.lance deleted file mode 100644 index 2a47abd17..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9137eac-df59-496f-8788-8596666aecfa.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e93d99a3-06bf-4333-bfad-dd52f1f19eaf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e93d99a3-06bf-4333-bfad-dd52f1f19eaf.lance deleted file mode 100644 index 7e5635d57..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e93d99a3-06bf-4333-bfad-dd52f1f19eaf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9413a18-2405-4365-ad0e-bd55a44da354.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9413a18-2405-4365-ad0e-bd55a44da354.lance deleted file mode 100644 index c858274db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9413a18-2405-4365-ad0e-bd55a44da354.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e942c36f-f9c3-40a3-9b1b-26002f10a7f8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e942c36f-f9c3-40a3-9b1b-26002f10a7f8.lance deleted file mode 100644 index 877efbbaa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e942c36f-f9c3-40a3-9b1b-26002f10a7f8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e94dfce7-1784-4966-ad3d-388677dfc25f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e94dfce7-1784-4966-ad3d-388677dfc25f.lance deleted file mode 100644 index 765c15c48..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e94dfce7-1784-4966-ad3d-388677dfc25f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e95f9cfd-f505-4f49-a86f-4b2ebbb39a91.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e95f9cfd-f505-4f49-a86f-4b2ebbb39a91.lance deleted file mode 100644 index 548b18158..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e95f9cfd-f505-4f49-a86f-4b2ebbb39a91.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9871207-efaf-4e4f-9449-67d083fd7695.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9871207-efaf-4e4f-9449-67d083fd7695.lance deleted file mode 100644 index 78a01b0c2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9871207-efaf-4e4f-9449-67d083fd7695.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e987f184-03b0-46ca-8763-940ac1a23f00.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e987f184-03b0-46ca-8763-940ac1a23f00.lance deleted file mode 100644 index 50676c62e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e987f184-03b0-46ca-8763-940ac1a23f00.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9b3d781-7fc6-466b-846c-10f9bee7cc50.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9b3d781-7fc6-466b-846c-10f9bee7cc50.lance deleted file mode 100644 index 93d6af2fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9b3d781-7fc6-466b-846c-10f9bee7cc50.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9b70eea-0b83-4998-a9d4-5f8ca8797c27.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9b70eea-0b83-4998-a9d4-5f8ca8797c27.lance deleted file mode 100644 index c9977a173..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9b70eea-0b83-4998-a9d4-5f8ca8797c27.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9bfc5a3-1f4b-4fd4-9bb8-1d902818b5f8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9bfc5a3-1f4b-4fd4-9bb8-1d902818b5f8.lance deleted file mode 100644 index a25d1f828..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9bfc5a3-1f4b-4fd4-9bb8-1d902818b5f8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9e7cde8-7e15-438d-aebf-4b6705ba9d36.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9e7cde8-7e15-438d-aebf-4b6705ba9d36.lance deleted file mode 100644 index a3f03d1b9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9e7cde8-7e15-438d-aebf-4b6705ba9d36.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9f1cb44-a949-408b-81db-ff782aa78fdc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9f1cb44-a949-408b-81db-ff782aa78fdc.lance deleted file mode 100644 index 9581b5c84..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/e9f1cb44-a949-408b-81db-ff782aa78fdc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea082fe8-cf0d-4c5d-a762-aec70008f83e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea082fe8-cf0d-4c5d-a762-aec70008f83e.lance deleted file mode 100644 index ad7cec426..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea082fe8-cf0d-4c5d-a762-aec70008f83e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea0f8114-078a-4e1b-8947-711dd81abc4b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea0f8114-078a-4e1b-8947-711dd81abc4b.lance deleted file mode 100644 index 96a15e796..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea0f8114-078a-4e1b-8947-711dd81abc4b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea260e82-1cec-4e00-805f-a1ee5a4bbb14.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea260e82-1cec-4e00-805f-a1ee5a4bbb14.lance deleted file mode 100644 index 0ced216ce..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea260e82-1cec-4e00-805f-a1ee5a4bbb14.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea36e8b5-8c78-4c10-8706-d10640cf1261.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea36e8b5-8c78-4c10-8706-d10640cf1261.lance deleted file mode 100644 index 924fabcf9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea36e8b5-8c78-4c10-8706-d10640cf1261.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea5ffc6a-b1ae-4f15-a3b9-f0892ce0497d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea5ffc6a-b1ae-4f15-a3b9-f0892ce0497d.lance deleted file mode 100644 index 9e8d0bba3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea5ffc6a-b1ae-4f15-a3b9-f0892ce0497d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea62ea3f-87d5-40e1-9972-c64854b65534.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea62ea3f-87d5-40e1-9972-c64854b65534.lance deleted file mode 100644 index f4a0667b2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea62ea3f-87d5-40e1-9972-c64854b65534.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea99bf2a-5c77-4324-877e-40e01b033011.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea99bf2a-5c77-4324-877e-40e01b033011.lance deleted file mode 100644 index e8bd45bfb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea99bf2a-5c77-4324-877e-40e01b033011.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea9fc48a-1788-47f9-96fb-52aef7a27358.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea9fc48a-1788-47f9-96fb-52aef7a27358.lance deleted file mode 100644 index 82c7bdac7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ea9fc48a-1788-47f9-96fb-52aef7a27358.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eaa3e272-1281-4285-bdde-f0e062bda7c6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eaa3e272-1281-4285-bdde-f0e062bda7c6.lance deleted file mode 100644 index 08663edd1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eaa3e272-1281-4285-bdde-f0e062bda7c6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eab65896-db4b-47d3-b533-5e133f2b7383.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eab65896-db4b-47d3-b533-5e133f2b7383.lance deleted file mode 100644 index f4d376655..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eab65896-db4b-47d3-b533-5e133f2b7383.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eabcdaa2-06db-4c58-80b3-8938ca8f2891.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eabcdaa2-06db-4c58-80b3-8938ca8f2891.lance deleted file mode 100644 index f77d18f87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eabcdaa2-06db-4c58-80b3-8938ca8f2891.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ead9a2f1-f611-4276-8d4e-004801067f06.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ead9a2f1-f611-4276-8d4e-004801067f06.lance deleted file mode 100644 index 1ba51a13b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ead9a2f1-f611-4276-8d4e-004801067f06.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb11a95e-e540-4938-81f4-0dc3debb8d60.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb11a95e-e540-4938-81f4-0dc3debb8d60.lance deleted file mode 100644 index 1570aef3c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb11a95e-e540-4938-81f4-0dc3debb8d60.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb2ff5d9-d94c-4b79-b95e-9d7d7899d940.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb2ff5d9-d94c-4b79-b95e-9d7d7899d940.lance deleted file mode 100644 index 6eadfe950..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb2ff5d9-d94c-4b79-b95e-9d7d7899d940.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb40ce04-1eb5-495b-ac67-dc92bfc6c292.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb40ce04-1eb5-495b-ac67-dc92bfc6c292.lance deleted file mode 100644 index 813959756..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb40ce04-1eb5-495b-ac67-dc92bfc6c292.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb450ccb-ad88-4560-9c2e-1eff27bdd5e7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb450ccb-ad88-4560-9c2e-1eff27bdd5e7.lance deleted file mode 100644 index fe8519007..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb450ccb-ad88-4560-9c2e-1eff27bdd5e7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb4b8b08-82b5-4dcf-9c34-916164429888.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb4b8b08-82b5-4dcf-9c34-916164429888.lance deleted file mode 100644 index 10450f51b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb4b8b08-82b5-4dcf-9c34-916164429888.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb4d869a-0d71-4e6b-9807-25b04ae86886.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb4d869a-0d71-4e6b-9807-25b04ae86886.lance deleted file mode 100644 index 445dc72b6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb4d869a-0d71-4e6b-9807-25b04ae86886.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb4eeb2c-15d6-40f0-b774-04ade5c9070d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb4eeb2c-15d6-40f0-b774-04ade5c9070d.lance deleted file mode 100644 index 6c4f3fe92..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb4eeb2c-15d6-40f0-b774-04ade5c9070d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb59d3f1-59f2-414f-9b49-a80db188fbab.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb59d3f1-59f2-414f-9b49-a80db188fbab.lance deleted file mode 100644 index a35c0bc3b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb59d3f1-59f2-414f-9b49-a80db188fbab.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb714249-783e-423f-8107-83ebab580486.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb714249-783e-423f-8107-83ebab580486.lance deleted file mode 100644 index ff4454719..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb714249-783e-423f-8107-83ebab580486.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb7fcfe4-9163-4ce5-94e1-1911e25c1a7f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb7fcfe4-9163-4ce5-94e1-1911e25c1a7f.lance deleted file mode 100644 index 396fed04e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb7fcfe4-9163-4ce5-94e1-1911e25c1a7f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb89a645-d2fa-49ca-9b68-1c3fb90836cb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb89a645-d2fa-49ca-9b68-1c3fb90836cb.lance deleted file mode 100644 index 5cda5300d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eb89a645-d2fa-49ca-9b68-1c3fb90836cb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec001127-1a46-436e-b4c4-5e8c70900d62.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec001127-1a46-436e-b4c4-5e8c70900d62.lance deleted file mode 100644 index 56a30b81e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec001127-1a46-436e-b4c4-5e8c70900d62.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec2a80b9-2dda-45d0-ae53-1f05d4fc6e99.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec2a80b9-2dda-45d0-ae53-1f05d4fc6e99.lance deleted file mode 100644 index 000c91583..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec2a80b9-2dda-45d0-ae53-1f05d4fc6e99.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec4456fa-f011-4b0d-8821-bfeedf8de6be.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec4456fa-f011-4b0d-8821-bfeedf8de6be.lance deleted file mode 100644 index df260f6fc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec4456fa-f011-4b0d-8821-bfeedf8de6be.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec5d27b5-5361-424a-98ce-cc164ec6a87a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec5d27b5-5361-424a-98ce-cc164ec6a87a.lance deleted file mode 100644 index e4cc3ddbc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec5d27b5-5361-424a-98ce-cc164ec6a87a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec834656-e1a1-43f1-beae-982a39d39d5b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec834656-e1a1-43f1-beae-982a39d39d5b.lance deleted file mode 100644 index a1774a975..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec834656-e1a1-43f1-beae-982a39d39d5b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec955ed5-cc10-43ac-963a-99e781a8b5c5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec955ed5-cc10-43ac-963a-99e781a8b5c5.lance deleted file mode 100644 index 590644277..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ec955ed5-cc10-43ac-963a-99e781a8b5c5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eca90158-95c2-49fe-aa39-cba226c1c72d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eca90158-95c2-49fe-aa39-cba226c1c72d.lance deleted file mode 100644 index 459af0faa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eca90158-95c2-49fe-aa39-cba226c1c72d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ecbd2d86-6998-430b-9722-f8781c6322a5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ecbd2d86-6998-430b-9722-f8781c6322a5.lance deleted file mode 100644 index 856c1102c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ecbd2d86-6998-430b-9722-f8781c6322a5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ecc6e5d0-d836-4280-9237-9a0ad19fb474.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ecc6e5d0-d836-4280-9237-9a0ad19fb474.lance deleted file mode 100644 index 290cef07e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ecc6e5d0-d836-4280-9237-9a0ad19fb474.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ecd03155-39b2-4bde-935c-991550afd173.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ecd03155-39b2-4bde-935c-991550afd173.lance deleted file mode 100644 index dce79848a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ecd03155-39b2-4bde-935c-991550afd173.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ecd044d3-6ff7-470f-857e-1f2cdae2b657.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ecd044d3-6ff7-470f-857e-1f2cdae2b657.lance deleted file mode 100644 index 1437d71b0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ecd044d3-6ff7-470f-857e-1f2cdae2b657.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ece5659d-71b0-465e-98f6-696af21f838c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ece5659d-71b0-465e-98f6-696af21f838c.lance deleted file mode 100644 index 25e68502b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ece5659d-71b0-465e-98f6-696af21f838c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ece5f3ed-a764-4e31-a4d8-50f604cfe4c1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ece5f3ed-a764-4e31-a4d8-50f604cfe4c1.lance deleted file mode 100644 index 5ca4df236..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ece5f3ed-a764-4e31-a4d8-50f604cfe4c1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed06162b-532e-4855-bcc0-66fe508f5399.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed06162b-532e-4855-bcc0-66fe508f5399.lance deleted file mode 100644 index a5dd5b8f4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed06162b-532e-4855-bcc0-66fe508f5399.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed143df0-64fd-452c-8ca7-c65d20ef02ec.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed143df0-64fd-452c-8ca7-c65d20ef02ec.lance deleted file mode 100644 index 4bc5d497d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed143df0-64fd-452c-8ca7-c65d20ef02ec.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed2caa05-f9c2-478a-9c46-d43a4753c91c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed2caa05-f9c2-478a-9c46-d43a4753c91c.lance deleted file mode 100644 index 4efea1ec3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed2caa05-f9c2-478a-9c46-d43a4753c91c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed3baa39-7a2f-450d-974f-2b9a624487e7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed3baa39-7a2f-450d-974f-2b9a624487e7.lance deleted file mode 100644 index 5a9fdf8cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed3baa39-7a2f-450d-974f-2b9a624487e7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed434f73-a3bb-4165-885f-2ef8efbced94.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed434f73-a3bb-4165-885f-2ef8efbced94.lance deleted file mode 100644 index a500ce27d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed434f73-a3bb-4165-885f-2ef8efbced94.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed5b9eff-0a2d-41e6-95cf-d3e6c820d740.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed5b9eff-0a2d-41e6-95cf-d3e6c820d740.lance deleted file mode 100644 index 6f18f6e4d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed5b9eff-0a2d-41e6-95cf-d3e6c820d740.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed5c2456-6d86-4410-b027-e73a1f3531bd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed5c2456-6d86-4410-b027-e73a1f3531bd.lance deleted file mode 100644 index 3706ff7be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed5c2456-6d86-4410-b027-e73a1f3531bd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed6af76d-8445-47b7-a7d6-43e11fb14b69.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed6af76d-8445-47b7-a7d6-43e11fb14b69.lance deleted file mode 100644 index a9ef0b666..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed6af76d-8445-47b7-a7d6-43e11fb14b69.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed8bd9c8-d658-47f8-b6c9-10543eab259f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed8bd9c8-d658-47f8-b6c9-10543eab259f.lance deleted file mode 100644 index 311975fe0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed8bd9c8-d658-47f8-b6c9-10543eab259f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed8fe3d6-1e01-447c-9762-3d7e0aaaa8d4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed8fe3d6-1e01-447c-9762-3d7e0aaaa8d4.lance deleted file mode 100644 index 78051079c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ed8fe3d6-1e01-447c-9762-3d7e0aaaa8d4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eda09b32-4b64-43d1-a3cb-8cfeeee3ac77.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eda09b32-4b64-43d1-a3cb-8cfeeee3ac77.lance deleted file mode 100644 index 033a74bc7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eda09b32-4b64-43d1-a3cb-8cfeeee3ac77.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/edaab97d-ac2a-4ebc-aba9-5adb49dacc09.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/edaab97d-ac2a-4ebc-aba9-5adb49dacc09.lance deleted file mode 100644 index 815a36b2f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/edaab97d-ac2a-4ebc-aba9-5adb49dacc09.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/edad7995-c381-47c5-aa84-f37f639adca5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/edad7995-c381-47c5-aa84-f37f639adca5.lance deleted file mode 100644 index da802779b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/edad7995-c381-47c5-aa84-f37f639adca5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/edcf7466-d519-4c6e-ab43-ae3f04b770a1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/edcf7466-d519-4c6e-ab43-ae3f04b770a1.lance deleted file mode 100644 index 397708be8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/edcf7466-d519-4c6e-ab43-ae3f04b770a1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/edd819a4-b7d0-440c-8e6c-5149ceb93fb1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/edd819a4-b7d0-440c-8e6c-5149ceb93fb1.lance deleted file mode 100644 index 20a27bc97..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/edd819a4-b7d0-440c-8e6c-5149ceb93fb1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ede37a14-085d-4ec9-9ceb-d61ec8249211.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ede37a14-085d-4ec9-9ceb-d61ec8249211.lance deleted file mode 100644 index 7406acfd6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ede37a14-085d-4ec9-9ceb-d61ec8249211.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ede9b2ae-d9a4-4f8e-999f-429f42cc4400.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ede9b2ae-d9a4-4f8e-999f-429f42cc4400.lance deleted file mode 100644 index a971f5ae8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ede9b2ae-d9a4-4f8e-999f-429f42cc4400.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/edff8182-7b9f-4423-bb3b-16ea9d33d7c5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/edff8182-7b9f-4423-bb3b-16ea9d33d7c5.lance deleted file mode 100644 index 427e36ba0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/edff8182-7b9f-4423-bb3b-16ea9d33d7c5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee04fb5f-672e-406d-a68a-d16bddf3633d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee04fb5f-672e-406d-a68a-d16bddf3633d.lance deleted file mode 100644 index 2ab5a09f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee04fb5f-672e-406d-a68a-d16bddf3633d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee10c186-53d4-47fa-a6e4-052515ebca0e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee10c186-53d4-47fa-a6e4-052515ebca0e.lance deleted file mode 100644 index ffd78bcac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee10c186-53d4-47fa-a6e4-052515ebca0e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee184b12-ef50-4a73-9d41-42cf3c021c80.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee184b12-ef50-4a73-9d41-42cf3c021c80.lance deleted file mode 100644 index b0dcb21a4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee184b12-ef50-4a73-9d41-42cf3c021c80.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee3ffcf2-9bd4-4f40-a8f1-60976558f440.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee3ffcf2-9bd4-4f40-a8f1-60976558f440.lance deleted file mode 100644 index 6daae3a26..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee3ffcf2-9bd4-4f40-a8f1-60976558f440.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee6dcca2-ca9d-475e-acc4-b96ce657c46f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee6dcca2-ca9d-475e-acc4-b96ce657c46f.lance deleted file mode 100644 index a6fec625e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee6dcca2-ca9d-475e-acc4-b96ce657c46f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee8526a7-eb39-4a01-8f3c-e7bc896ded76.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee8526a7-eb39-4a01-8f3c-e7bc896ded76.lance deleted file mode 100644 index 44fd12ca7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ee8526a7-eb39-4a01-8f3c-e7bc896ded76.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eec252c6-c3e1-42d0-9663-5e844e55d432.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eec252c6-c3e1-42d0-9663-5e844e55d432.lance deleted file mode 100644 index 6a9e04eb5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eec252c6-c3e1-42d0-9663-5e844e55d432.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eeca3ac8-8d19-4f27-8d95-d0c272b90abc.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eeca3ac8-8d19-4f27-8d95-d0c272b90abc.lance deleted file mode 100644 index 58b5fb9f1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eeca3ac8-8d19-4f27-8d95-d0c272b90abc.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eed70477-abb6-41fc-928f-ca967abc2faa.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eed70477-abb6-41fc-928f-ca967abc2faa.lance deleted file mode 100644 index 08e449fae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eed70477-abb6-41fc-928f-ca967abc2faa.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eeeefb4a-6fe2-4ec0-89f4-0c260e6b378b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eeeefb4a-6fe2-4ec0-89f4-0c260e6b378b.lance deleted file mode 100644 index 64b3f02a8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eeeefb4a-6fe2-4ec0-89f4-0c260e6b378b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eefb2d3b-9abd-4ae7-afb3-812afc579f73.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eefb2d3b-9abd-4ae7-afb3-812afc579f73.lance deleted file mode 100644 index eff24cc18..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/eefb2d3b-9abd-4ae7-afb3-812afc579f73.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef15b6ca-fe17-459e-81a0-50b730433c80.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef15b6ca-fe17-459e-81a0-50b730433c80.lance deleted file mode 100644 index 672e7e79d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef15b6ca-fe17-459e-81a0-50b730433c80.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef179cfe-0ddc-495e-9f20-72eb12ad7320.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef179cfe-0ddc-495e-9f20-72eb12ad7320.lance deleted file mode 100644 index b60005197..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef179cfe-0ddc-495e-9f20-72eb12ad7320.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef186e17-bc8b-4194-9ee3-fe9f34befc76.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef186e17-bc8b-4194-9ee3-fe9f34befc76.lance deleted file mode 100644 index ff20e8f29..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef186e17-bc8b-4194-9ee3-fe9f34befc76.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef4661b1-f25a-4fa3-8019-c7bbcfa04b2e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef4661b1-f25a-4fa3-8019-c7bbcfa04b2e.lance deleted file mode 100644 index e22761e29..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef4661b1-f25a-4fa3-8019-c7bbcfa04b2e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef4ba679-8272-4e85-9ea3-2971ed6ea114.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef4ba679-8272-4e85-9ea3-2971ed6ea114.lance deleted file mode 100644 index 4f2a8f7ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef4ba679-8272-4e85-9ea3-2971ed6ea114.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef5fbdeb-c5bc-46a6-95a4-29f527ff7937.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef5fbdeb-c5bc-46a6-95a4-29f527ff7937.lance deleted file mode 100644 index d661e64f7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef5fbdeb-c5bc-46a6-95a4-29f527ff7937.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef7b2eb1-3346-4c6c-9fa1-bdbfa12ba0df.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef7b2eb1-3346-4c6c-9fa1-bdbfa12ba0df.lance deleted file mode 100644 index bbdf63f3d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef7b2eb1-3346-4c6c-9fa1-bdbfa12ba0df.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef96b577-3403-4a70-8ad9-ec5e347825ac.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef96b577-3403-4a70-8ad9-ec5e347825ac.lance deleted file mode 100644 index 4241756f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef96b577-3403-4a70-8ad9-ec5e347825ac.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef9ce9be-3e8f-4344-9a85-885b4ebcb025.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef9ce9be-3e8f-4344-9a85-885b4ebcb025.lance deleted file mode 100644 index 41392dd56..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef9ce9be-3e8f-4344-9a85-885b4ebcb025.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef9d2c70-9da8-4fb0-9baf-61126d4de1ea.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef9d2c70-9da8-4fb0-9baf-61126d4de1ea.lance deleted file mode 100644 index f695eefea..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ef9d2c70-9da8-4fb0-9baf-61126d4de1ea.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/efe3ac1e-c8f2-453a-8f11-4182283c97bd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/efe3ac1e-c8f2-453a-8f11-4182283c97bd.lance deleted file mode 100644 index 2553a75f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/efe3ac1e-c8f2-453a-8f11-4182283c97bd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/efee873b-c358-45c9-a1f5-4d7021f2e911.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/efee873b-c358-45c9-a1f5-4d7021f2e911.lance deleted file mode 100644 index 877b1af52..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/efee873b-c358-45c9-a1f5-4d7021f2e911.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/effc09fd-e6d4-4b6d-bf3c-14f72405d327.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/effc09fd-e6d4-4b6d-bf3c-14f72405d327.lance deleted file mode 100644 index c99ba9837..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/effc09fd-e6d4-4b6d-bf3c-14f72405d327.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f017da2a-de26-44c6-a365-63766538f71a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f017da2a-de26-44c6-a365-63766538f71a.lance deleted file mode 100644 index 595b089a7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f017da2a-de26-44c6-a365-63766538f71a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f070e522-7703-47f5-b298-ef917ef32b52.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f070e522-7703-47f5-b298-ef917ef32b52.lance deleted file mode 100644 index f8d87f2d8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f070e522-7703-47f5-b298-ef917ef32b52.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f08dc74e-59e9-4a7a-bf8e-43ddd86ea6bf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f08dc74e-59e9-4a7a-bf8e-43ddd86ea6bf.lance deleted file mode 100644 index e40d16e53..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f08dc74e-59e9-4a7a-bf8e-43ddd86ea6bf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f0c084b3-6636-475a-9d51-7ffa7146d4dd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f0c084b3-6636-475a-9d51-7ffa7146d4dd.lance deleted file mode 100644 index 3bfbc2343..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f0c084b3-6636-475a-9d51-7ffa7146d4dd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f0f04a33-5805-4c16-a886-92a699936f75.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f0f04a33-5805-4c16-a886-92a699936f75.lance deleted file mode 100644 index a722899fb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f0f04a33-5805-4c16-a886-92a699936f75.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f0f9bc50-ce3f-46ac-96dd-76a3a0072d2b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f0f9bc50-ce3f-46ac-96dd-76a3a0072d2b.lance deleted file mode 100644 index 7a1eb65ff..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f0f9bc50-ce3f-46ac-96dd-76a3a0072d2b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f10623a5-a117-447e-b151-85284e041ba7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f10623a5-a117-447e-b151-85284e041ba7.lance deleted file mode 100644 index 7b7723a92..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f10623a5-a117-447e-b151-85284e041ba7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f113da94-2e71-41e8-bc26-63060633d4b5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f113da94-2e71-41e8-bc26-63060633d4b5.lance deleted file mode 100644 index 96017620a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f113da94-2e71-41e8-bc26-63060633d4b5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f1212501-50a4-4575-8f36-adcbc6b93a04.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f1212501-50a4-4575-8f36-adcbc6b93a04.lance deleted file mode 100644 index 08bdd4489..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f1212501-50a4-4575-8f36-adcbc6b93a04.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f139dc6c-aab6-429b-b664-dc92991ddad1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f139dc6c-aab6-429b-b664-dc92991ddad1.lance deleted file mode 100644 index 525adc53b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f139dc6c-aab6-429b-b664-dc92991ddad1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f168eb4a-9ced-4bf1-946f-52af86229121.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f168eb4a-9ced-4bf1-946f-52af86229121.lance deleted file mode 100644 index 51e369461..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f168eb4a-9ced-4bf1-946f-52af86229121.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f172ecc7-7d39-488b-8211-87e9509d88c9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f172ecc7-7d39-488b-8211-87e9509d88c9.lance deleted file mode 100644 index 64bd32cf1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f172ecc7-7d39-488b-8211-87e9509d88c9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f190ff3e-43ea-4e1f-b995-366cf7f67cb2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f190ff3e-43ea-4e1f-b995-366cf7f67cb2.lance deleted file mode 100644 index 6ae38bde6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f190ff3e-43ea-4e1f-b995-366cf7f67cb2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f19dc3d5-0fa1-4887-859c-98768ea7c1c3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f19dc3d5-0fa1-4887-859c-98768ea7c1c3.lance deleted file mode 100644 index 83d47bcd1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f19dc3d5-0fa1-4887-859c-98768ea7c1c3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f1bea7cc-86f9-4d90-a5f3-33dc0ee45559.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f1bea7cc-86f9-4d90-a5f3-33dc0ee45559.lance deleted file mode 100644 index 091d4b980..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f1bea7cc-86f9-4d90-a5f3-33dc0ee45559.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f1d271ae-b013-4aa1-afa7-eba5bedc8dd1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f1d271ae-b013-4aa1-afa7-eba5bedc8dd1.lance deleted file mode 100644 index ecfe5a077..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f1d271ae-b013-4aa1-afa7-eba5bedc8dd1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f1ecc072-edfa-4cd9-a94b-9be0c66fa668.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f1ecc072-edfa-4cd9-a94b-9be0c66fa668.lance deleted file mode 100644 index 00f3ad8c7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f1ecc072-edfa-4cd9-a94b-9be0c66fa668.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f1f164c9-9962-422f-985d-ca558fc54a95.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f1f164c9-9962-422f-985d-ca558fc54a95.lance deleted file mode 100644 index 7f676bca3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f1f164c9-9962-422f-985d-ca558fc54a95.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2082017-38d6-4a07-a8b4-22b721514ae8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2082017-38d6-4a07-a8b4-22b721514ae8.lance deleted file mode 100644 index 8b508c529..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2082017-38d6-4a07-a8b4-22b721514ae8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2102aeb-bd46-4508-b2fb-7ddd9361e161.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2102aeb-bd46-4508-b2fb-7ddd9361e161.lance deleted file mode 100644 index 0c9488ebd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2102aeb-bd46-4508-b2fb-7ddd9361e161.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f212224d-5827-4b92-88d9-e7fcfebb263c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f212224d-5827-4b92-88d9-e7fcfebb263c.lance deleted file mode 100644 index 5877f237d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f212224d-5827-4b92-88d9-e7fcfebb263c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2203501-b1f7-4410-97ce-f6b2cf19cffb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2203501-b1f7-4410-97ce-f6b2cf19cffb.lance deleted file mode 100644 index ed11457d1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2203501-b1f7-4410-97ce-f6b2cf19cffb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f224f552-8ff2-4952-b5cc-60e72f6b2685.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f224f552-8ff2-4952-b5cc-60e72f6b2685.lance deleted file mode 100644 index b5bd9e679..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f224f552-8ff2-4952-b5cc-60e72f6b2685.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f24d1d6d-097b-4e1d-9f7c-0cc12aa6e117.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f24d1d6d-097b-4e1d-9f7c-0cc12aa6e117.lance deleted file mode 100644 index d4f552f92..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f24d1d6d-097b-4e1d-9f7c-0cc12aa6e117.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f250c87d-0844-4664-84c0-56cc564bd704.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f250c87d-0844-4664-84c0-56cc564bd704.lance deleted file mode 100644 index cc0aa6eaf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f250c87d-0844-4664-84c0-56cc564bd704.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f281e347-cd19-4eb9-99e4-9135b523cc37.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f281e347-cd19-4eb9-99e4-9135b523cc37.lance deleted file mode 100644 index 7c6252e69..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f281e347-cd19-4eb9-99e4-9135b523cc37.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f289b65d-f3ab-478a-b719-af4ebdd036ac.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f289b65d-f3ab-478a-b719-af4ebdd036ac.lance deleted file mode 100644 index 9e9bc5c5f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f289b65d-f3ab-478a-b719-af4ebdd036ac.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f29d5789-9a4b-445e-bbd7-6148677c9c66.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f29d5789-9a4b-445e-bbd7-6148677c9c66.lance deleted file mode 100644 index 4283786a6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f29d5789-9a4b-445e-bbd7-6148677c9c66.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2d5dac1-17e1-45e2-992f-5eecc7528f5c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2d5dac1-17e1-45e2-992f-5eecc7528f5c.lance deleted file mode 100644 index 615d24875..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2d5dac1-17e1-45e2-992f-5eecc7528f5c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2e0ce0e-119b-427b-ad3f-56f836ae13a1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2e0ce0e-119b-427b-ad3f-56f836ae13a1.lance deleted file mode 100644 index 4422c9591..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2e0ce0e-119b-427b-ad3f-56f836ae13a1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2f17be1-608f-4c33-94f2-2375d699926f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2f17be1-608f-4c33-94f2-2375d699926f.lance deleted file mode 100644 index ad3c3dcf7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2f17be1-608f-4c33-94f2-2375d699926f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2f9d194-5ffb-4f51-8b61-bdeec8d61c3b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2f9d194-5ffb-4f51-8b61-bdeec8d61c3b.lance deleted file mode 100644 index b195d76e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2f9d194-5ffb-4f51-8b61-bdeec8d61c3b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2fa6041-6603-4053-a57f-c9aae96130f4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2fa6041-6603-4053-a57f-c9aae96130f4.lance deleted file mode 100644 index 8040e3cfd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f2fa6041-6603-4053-a57f-c9aae96130f4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f34bb97c-6228-4661-83b7-b85f558e4e4e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f34bb97c-6228-4661-83b7-b85f558e4e4e.lance deleted file mode 100644 index 7e45baabc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f34bb97c-6228-4661-83b7-b85f558e4e4e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f366ea2a-dfba-4174-98a8-517278ba791c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f366ea2a-dfba-4174-98a8-517278ba791c.lance deleted file mode 100644 index 31159635c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f366ea2a-dfba-4174-98a8-517278ba791c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f369af7a-b1ed-4e27-9332-b2f3e3a088b4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f369af7a-b1ed-4e27-9332-b2f3e3a088b4.lance deleted file mode 100644 index 692fbfb01..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f369af7a-b1ed-4e27-9332-b2f3e3a088b4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f37e7e06-baf2-4b7c-9cf5-77e12c30dab8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f37e7e06-baf2-4b7c-9cf5-77e12c30dab8.lance deleted file mode 100644 index 166d6fd83..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f37e7e06-baf2-4b7c-9cf5-77e12c30dab8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f38faedf-a7e9-47c7-901b-5faf5eca9d03.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f38faedf-a7e9-47c7-901b-5faf5eca9d03.lance deleted file mode 100644 index b8fcf92ab..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f38faedf-a7e9-47c7-901b-5faf5eca9d03.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f3dad8d1-c937-4900-bd84-50c5d7b74164.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f3dad8d1-c937-4900-bd84-50c5d7b74164.lance deleted file mode 100644 index 3a3aeab98..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f3dad8d1-c937-4900-bd84-50c5d7b74164.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4144ae7-138e-479c-a40c-bf0ea37ec69d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4144ae7-138e-479c-a40c-bf0ea37ec69d.lance deleted file mode 100644 index a1fb21c27..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4144ae7-138e-479c-a40c-bf0ea37ec69d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f43d86e7-c2a5-4cae-8235-05fff106e261.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f43d86e7-c2a5-4cae-8235-05fff106e261.lance deleted file mode 100644 index 7981abb46..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f43d86e7-c2a5-4cae-8235-05fff106e261.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4491800-c751-4a52-9f2c-8637c80f9574.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4491800-c751-4a52-9f2c-8637c80f9574.lance deleted file mode 100644 index da695ddc4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4491800-c751-4a52-9f2c-8637c80f9574.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4920652-dcd7-4027-9f60-d43dc5ec8bbf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4920652-dcd7-4027-9f60-d43dc5ec8bbf.lance deleted file mode 100644 index 65208e944..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4920652-dcd7-4027-9f60-d43dc5ec8bbf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4a08e1f-8aaf-4b71-b91b-48cc5313e710.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4a08e1f-8aaf-4b71-b91b-48cc5313e710.lance deleted file mode 100644 index d9de17e6b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4a08e1f-8aaf-4b71-b91b-48cc5313e710.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4aa0987-12bb-4581-856c-8fad5d93594e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4aa0987-12bb-4581-856c-8fad5d93594e.lance deleted file mode 100644 index 6440a8e52..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4aa0987-12bb-4581-856c-8fad5d93594e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4bfcb70-28d5-4bd2-bf7b-674835ca2b11.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4bfcb70-28d5-4bd2-bf7b-674835ca2b11.lance deleted file mode 100644 index 6bb2c9df1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4bfcb70-28d5-4bd2-bf7b-674835ca2b11.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4c66767-21a7-420f-a3f6-dba50148125b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4c66767-21a7-420f-a3f6-dba50148125b.lance deleted file mode 100644 index 38197e625..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4c66767-21a7-420f-a3f6-dba50148125b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4ee61e3-398e-461a-b410-e33fde76f575.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4ee61e3-398e-461a-b410-e33fde76f575.lance deleted file mode 100644 index 7d37645f3..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f4ee61e3-398e-461a-b410-e33fde76f575.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f507da12-d8b0-45c8-80af-29040c96f30a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f507da12-d8b0-45c8-80af-29040c96f30a.lance deleted file mode 100644 index 4d252ecf2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f507da12-d8b0-45c8-80af-29040c96f30a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f52297bc-a075-488f-9db8-4c5a7a4a0fe1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f52297bc-a075-488f-9db8-4c5a7a4a0fe1.lance deleted file mode 100644 index d6624d9a6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f52297bc-a075-488f-9db8-4c5a7a4a0fe1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f5462cb4-f1ef-41e8-8a73-937f61bd0c89.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f5462cb4-f1ef-41e8-8a73-937f61bd0c89.lance deleted file mode 100644 index d29408e77..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f5462cb4-f1ef-41e8-8a73-937f61bd0c89.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f554945f-a4ca-40de-a760-99b5a01fd832.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f554945f-a4ca-40de-a760-99b5a01fd832.lance deleted file mode 100644 index 0458247be..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f554945f-a4ca-40de-a760-99b5a01fd832.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f573c099-8a66-48a8-a0c8-d0a1f1fc82b7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f573c099-8a66-48a8-a0c8-d0a1f1fc82b7.lance deleted file mode 100644 index 6d0ef3461..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f573c099-8a66-48a8-a0c8-d0a1f1fc82b7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f590dfb4-4d2d-47ba-bb1d-17735c9b4412.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f590dfb4-4d2d-47ba-bb1d-17735c9b4412.lance deleted file mode 100644 index 56054bd51..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f590dfb4-4d2d-47ba-bb1d-17735c9b4412.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f5b4bc28-e771-4d37-a6ee-9ac0880c0c61.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f5b4bc28-e771-4d37-a6ee-9ac0880c0c61.lance deleted file mode 100644 index 3e9ad9318..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f5b4bc28-e771-4d37-a6ee-9ac0880c0c61.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f5dd1030-081d-4b09-8688-ca6a2194b761.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f5dd1030-081d-4b09-8688-ca6a2194b761.lance deleted file mode 100644 index e4826c0fa..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f5dd1030-081d-4b09-8688-ca6a2194b761.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f5f819a8-383d-4798-afbb-93660a32655c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f5f819a8-383d-4798-afbb-93660a32655c.lance deleted file mode 100644 index b757b3fa7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f5f819a8-383d-4798-afbb-93660a32655c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f60c8734-10e6-4ab6-bf59-25a04133bfac.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f60c8734-10e6-4ab6-bf59-25a04133bfac.lance deleted file mode 100644 index 4e67f70f2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f60c8734-10e6-4ab6-bf59-25a04133bfac.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f60f799d-908b-4c38-900f-d7fdad6e0f0f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f60f799d-908b-4c38-900f-d7fdad6e0f0f.lance deleted file mode 100644 index a6e7d3b4e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f60f799d-908b-4c38-900f-d7fdad6e0f0f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f637d31b-6e18-4c46-999b-361e7d058b18.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f637d31b-6e18-4c46-999b-361e7d058b18.lance deleted file mode 100644 index 188a33769..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f637d31b-6e18-4c46-999b-361e7d058b18.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f65ab51b-3822-4109-82db-7a0dd87ac41d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f65ab51b-3822-4109-82db-7a0dd87ac41d.lance deleted file mode 100644 index 17bfec0e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f65ab51b-3822-4109-82db-7a0dd87ac41d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f66ef5f5-5673-464e-8fca-7e3cbc8e1075.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f66ef5f5-5673-464e-8fca-7e3cbc8e1075.lance deleted file mode 100644 index ac2646923..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f66ef5f5-5673-464e-8fca-7e3cbc8e1075.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f672546b-4804-4d63-a1de-5e377dc4d36f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f672546b-4804-4d63-a1de-5e377dc4d36f.lance deleted file mode 100644 index 91ee2c24f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f672546b-4804-4d63-a1de-5e377dc4d36f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f6779365-9e5c-4647-8e51-f4e1cd4f1df2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f6779365-9e5c-4647-8e51-f4e1cd4f1df2.lance deleted file mode 100644 index ebc740354..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f6779365-9e5c-4647-8e51-f4e1cd4f1df2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f69f52dc-2e81-40f1-a3ee-d99561d73fe1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f69f52dc-2e81-40f1-a3ee-d99561d73fe1.lance deleted file mode 100644 index fdd30f8cf..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f69f52dc-2e81-40f1-a3ee-d99561d73fe1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f6a12a86-aedd-4d8d-a708-3244fe5c0697.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f6a12a86-aedd-4d8d-a708-3244fe5c0697.lance deleted file mode 100644 index e0480a6ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f6a12a86-aedd-4d8d-a708-3244fe5c0697.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f705df4e-16d3-4fdd-8429-b4032d5e0976.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f705df4e-16d3-4fdd-8429-b4032d5e0976.lance deleted file mode 100644 index 8ec45712b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f705df4e-16d3-4fdd-8429-b4032d5e0976.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f762eac0-1d6e-4462-9937-b1b9bce035b8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f762eac0-1d6e-4462-9937-b1b9bce035b8.lance deleted file mode 100644 index f4322dac9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f762eac0-1d6e-4462-9937-b1b9bce035b8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7bb3058-a1fa-4837-85e5-1ec538fffb5e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7bb3058-a1fa-4837-85e5-1ec538fffb5e.lance deleted file mode 100644 index f2838c9db..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7bb3058-a1fa-4837-85e5-1ec538fffb5e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7ca8248-3a72-49bc-92e3-d7e8def18e56.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7ca8248-3a72-49bc-92e3-d7e8def18e56.lance deleted file mode 100644 index 2c5935799..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7ca8248-3a72-49bc-92e3-d7e8def18e56.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7caa09f-51d3-428a-9ae3-f47eac32f65e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7caa09f-51d3-428a-9ae3-f47eac32f65e.lance deleted file mode 100644 index e7e4fde04..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7caa09f-51d3-428a-9ae3-f47eac32f65e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7d8aa7d-487e-4809-9962-0a57c23ebb3d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7d8aa7d-487e-4809-9962-0a57c23ebb3d.lance deleted file mode 100644 index 140526346..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7d8aa7d-487e-4809-9962-0a57c23ebb3d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7e6d398-5bf9-40f2-9a4a-148d8fe78b42.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7e6d398-5bf9-40f2-9a4a-148d8fe78b42.lance deleted file mode 100644 index 250a12b82..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7e6d398-5bf9-40f2-9a4a-148d8fe78b42.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7fa9664-f393-4436-80aa-3992b3fc59ed.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7fa9664-f393-4436-80aa-3992b3fc59ed.lance deleted file mode 100644 index beaa57b6d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7fa9664-f393-4436-80aa-3992b3fc59ed.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7fbbb09-8f8d-4c3a-86fa-ac693ec4c5f7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7fbbb09-8f8d-4c3a-86fa-ac693ec4c5f7.lance deleted file mode 100644 index 02eee702f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7fbbb09-8f8d-4c3a-86fa-ac693ec4c5f7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7ff29ea-4d0d-4ff5-b0ba-0b518fff8be3.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7ff29ea-4d0d-4ff5-b0ba-0b518fff8be3.lance deleted file mode 100644 index 907d92871..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f7ff29ea-4d0d-4ff5-b0ba-0b518fff8be3.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f800053f-7de6-4781-9e6e-dcb96f6858d7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f800053f-7de6-4781-9e6e-dcb96f6858d7.lance deleted file mode 100644 index b5c9902a9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f800053f-7de6-4781-9e6e-dcb96f6858d7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f84860e7-ac81-4323-9cfb-c7ca972a4b78.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f84860e7-ac81-4323-9cfb-c7ca972a4b78.lance deleted file mode 100644 index ed10653b5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f84860e7-ac81-4323-9cfb-c7ca972a4b78.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f87869df-4931-405a-b408-dac16e817ef9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f87869df-4931-405a-b408-dac16e817ef9.lance deleted file mode 100644 index 1ec13ff88..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f87869df-4931-405a-b408-dac16e817ef9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f87d00dd-e738-4050-a733-15ba5009cb50.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f87d00dd-e738-4050-a733-15ba5009cb50.lance deleted file mode 100644 index 2c7ed48a1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f87d00dd-e738-4050-a733-15ba5009cb50.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f89ab27d-ec01-488d-94a4-a387eb36d70d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f89ab27d-ec01-488d-94a4-a387eb36d70d.lance deleted file mode 100644 index 8775e5dbd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f89ab27d-ec01-488d-94a4-a387eb36d70d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f8b42c64-9c16-4ded-82a7-863bddf66f4e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f8b42c64-9c16-4ded-82a7-863bddf66f4e.lance deleted file mode 100644 index 4cf2aad31..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f8b42c64-9c16-4ded-82a7-863bddf66f4e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f8babafc-9736-4f52-a56d-79202aa4f0e7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f8babafc-9736-4f52-a56d-79202aa4f0e7.lance deleted file mode 100644 index 6944b5012..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f8babafc-9736-4f52-a56d-79202aa4f0e7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f8f18a53-bc38-4b99-9f29-8df194082e50.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f8f18a53-bc38-4b99-9f29-8df194082e50.lance deleted file mode 100644 index 395ad7629..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f8f18a53-bc38-4b99-9f29-8df194082e50.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f93a518b-9c5c-453b-ab2a-c6ce7fe9e483.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f93a518b-9c5c-453b-ab2a-c6ce7fe9e483.lance deleted file mode 100644 index 191342f5e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f93a518b-9c5c-453b-ab2a-c6ce7fe9e483.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f94064d9-0365-4f01-ae6c-a37a3941e511.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f94064d9-0365-4f01-ae6c-a37a3941e511.lance deleted file mode 100644 index b9b0509f9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f94064d9-0365-4f01-ae6c-a37a3941e511.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f94128fa-9cf9-451e-861c-35c75c665c42.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f94128fa-9cf9-451e-861c-35c75c665c42.lance deleted file mode 100644 index 2780fe871..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f94128fa-9cf9-451e-861c-35c75c665c42.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f95b44a3-2a5b-4d42-a55d-fc07c1e922e2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f95b44a3-2a5b-4d42-a55d-fc07c1e922e2.lance deleted file mode 100644 index 060394a3d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f95b44a3-2a5b-4d42-a55d-fc07c1e922e2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f95c51da-7258-4f59-80a8-f93553dcd598.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f95c51da-7258-4f59-80a8-f93553dcd598.lance deleted file mode 100644 index c3c9ac38f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f95c51da-7258-4f59-80a8-f93553dcd598.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f9821369-9ea0-410e-af1b-cae8577c4929.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f9821369-9ea0-410e-af1b-cae8577c4929.lance deleted file mode 100644 index d645a6a81..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f9821369-9ea0-410e-af1b-cae8577c4929.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f99715f9-0b2d-4b6f-b26d-8c67c95628d9.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f99715f9-0b2d-4b6f-b26d-8c67c95628d9.lance deleted file mode 100644 index edea37914..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f99715f9-0b2d-4b6f-b26d-8c67c95628d9.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f9979a5d-c306-42ee-91d3-8ff5e11a0d78.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f9979a5d-c306-42ee-91d3-8ff5e11a0d78.lance deleted file mode 100644 index cb00fda01..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/f9979a5d-c306-42ee-91d3-8ff5e11a0d78.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa0bf57b-ec09-40cc-a254-0fe8b5e4eb1e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa0bf57b-ec09-40cc-a254-0fe8b5e4eb1e.lance deleted file mode 100644 index 4871d3995..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa0bf57b-ec09-40cc-a254-0fe8b5e4eb1e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa2ac602-f2dd-447b-af0d-b3bbfbf7b324.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa2ac602-f2dd-447b-af0d-b3bbfbf7b324.lance deleted file mode 100644 index 4f3d9e0b6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa2ac602-f2dd-447b-af0d-b3bbfbf7b324.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa3cf90b-3ded-44ab-ac5b-ecf00e29143d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa3cf90b-3ded-44ab-ac5b-ecf00e29143d.lance deleted file mode 100644 index 4600f7bd0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa3cf90b-3ded-44ab-ac5b-ecf00e29143d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa443e9b-b316-4258-9acc-6cc9b2dec2d4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa443e9b-b316-4258-9acc-6cc9b2dec2d4.lance deleted file mode 100644 index 72216104c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa443e9b-b316-4258-9acc-6cc9b2dec2d4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa66cee7-5e98-419a-9768-5b645a3e1baf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa66cee7-5e98-419a-9768-5b645a3e1baf.lance deleted file mode 100644 index ed107df49..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa66cee7-5e98-419a-9768-5b645a3e1baf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa6f9df2-5da3-40ef-a542-97ac227f96aa.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa6f9df2-5da3-40ef-a542-97ac227f96aa.lance deleted file mode 100644 index 3691755b8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fa6f9df2-5da3-40ef-a542-97ac227f96aa.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/faa4598b-5ce0-42f6-baf0-a59f15c0b713.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/faa4598b-5ce0-42f6-baf0-a59f15c0b713.lance deleted file mode 100644 index 49ff8a5ec..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/faa4598b-5ce0-42f6-baf0-a59f15c0b713.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/faa6efea-3e84-46ae-9ead-51569991cab1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/faa6efea-3e84-46ae-9ead-51569991cab1.lance deleted file mode 100644 index 2b4ec1799..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/faa6efea-3e84-46ae-9ead-51569991cab1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fab8b1e4-2ae1-4e4b-ac44-459ab978916f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fab8b1e4-2ae1-4e4b-ac44-459ab978916f.lance deleted file mode 100644 index 007a195ae..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fab8b1e4-2ae1-4e4b-ac44-459ab978916f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fafc5285-6dc1-42cc-a535-0d1443ed83e5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fafc5285-6dc1-42cc-a535-0d1443ed83e5.lance deleted file mode 100644 index 8e22ca898..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fafc5285-6dc1-42cc-a535-0d1443ed83e5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb00543d-acdc-409a-9a3b-f86de5c44a2e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb00543d-acdc-409a-9a3b-f86de5c44a2e.lance deleted file mode 100644 index 567cbe31a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb00543d-acdc-409a-9a3b-f86de5c44a2e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb00e0b9-949e-4c8c-9e43-08948d022362.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb00e0b9-949e-4c8c-9e43-08948d022362.lance deleted file mode 100644 index b8b69f640..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb00e0b9-949e-4c8c-9e43-08948d022362.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb021d7c-0f17-42d6-9dc7-2fe20bd3b577.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb021d7c-0f17-42d6-9dc7-2fe20bd3b577.lance deleted file mode 100644 index e73c38461..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb021d7c-0f17-42d6-9dc7-2fe20bd3b577.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb0e212a-afbb-4ebd-821a-e0341ef4fa54.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb0e212a-afbb-4ebd-821a-e0341ef4fa54.lance deleted file mode 100644 index 3827aa06d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb0e212a-afbb-4ebd-821a-e0341ef4fa54.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb21b104-96d0-44d0-90a8-10d1dd12f732.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb21b104-96d0-44d0-90a8-10d1dd12f732.lance deleted file mode 100644 index 0c87b402f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb21b104-96d0-44d0-90a8-10d1dd12f732.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb638d82-0019-4fd8-9bb6-c2f7a4cd9089.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb638d82-0019-4fd8-9bb6-c2f7a4cd9089.lance deleted file mode 100644 index a583af273..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb638d82-0019-4fd8-9bb6-c2f7a4cd9089.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb6cd1cc-cfcb-40d7-a4c7-45061929cd0c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb6cd1cc-cfcb-40d7-a4c7-45061929cd0c.lance deleted file mode 100644 index 533a4c8d9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb6cd1cc-cfcb-40d7-a4c7-45061929cd0c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb959d23-d77d-46a6-84d8-350bf60aaa95.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb959d23-d77d-46a6-84d8-350bf60aaa95.lance deleted file mode 100644 index 3c41d3fe5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb959d23-d77d-46a6-84d8-350bf60aaa95.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb95d36c-57e1-4635-ad3b-51710c5079c2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb95d36c-57e1-4635-ad3b-51710c5079c2.lance deleted file mode 100644 index 8ac282ac5..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fb95d36c-57e1-4635-ad3b-51710c5079c2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fba01abb-9850-47ec-aad3-49efcf2c423d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fba01abb-9850-47ec-aad3-49efcf2c423d.lance deleted file mode 100644 index 4503955e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fba01abb-9850-47ec-aad3-49efcf2c423d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fbae1f19-673f-4c28-b5fe-1db62029dffa.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fbae1f19-673f-4c28-b5fe-1db62029dffa.lance deleted file mode 100644 index 9ab9dd950..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fbae1f19-673f-4c28-b5fe-1db62029dffa.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fbc44c35-a06b-4ecb-9a61-13d4c0f1b1ae.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fbc44c35-a06b-4ecb-9a61-13d4c0f1b1ae.lance deleted file mode 100644 index f4c1c4f87..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fbc44c35-a06b-4ecb-9a61-13d4c0f1b1ae.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fbf01fe0-b1b0-48c9-8fb5-8fccf7ddd772.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fbf01fe0-b1b0-48c9-8fb5-8fccf7ddd772.lance deleted file mode 100644 index 5d51920d0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fbf01fe0-b1b0-48c9-8fb5-8fccf7ddd772.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc052b82-55e1-4b79-98d4-1e3a1d0ac956.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc052b82-55e1-4b79-98d4-1e3a1d0ac956.lance deleted file mode 100644 index a52ad6c02..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc052b82-55e1-4b79-98d4-1e3a1d0ac956.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc193dc3-3e87-4f61-ad77-2aa872fb2b7f.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc193dc3-3e87-4f61-ad77-2aa872fb2b7f.lance deleted file mode 100644 index 75a29928f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc193dc3-3e87-4f61-ad77-2aa872fb2b7f.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc40dbb5-2a3d-4321-9d66-afe74b352d8d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc40dbb5-2a3d-4321-9d66-afe74b352d8d.lance deleted file mode 100644 index 5a17ad232..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc40dbb5-2a3d-4321-9d66-afe74b352d8d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc6856a9-bcb1-4b1b-a703-7951a01b18d8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc6856a9-bcb1-4b1b-a703-7951a01b18d8.lance deleted file mode 100644 index 13b0f4312..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc6856a9-bcb1-4b1b-a703-7951a01b18d8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc76b399-e08c-45ab-b8a0-07434587c7a1.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc76b399-e08c-45ab-b8a0-07434587c7a1.lance deleted file mode 100644 index b7a5abc05..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc76b399-e08c-45ab-b8a0-07434587c7a1.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc85799d-ff28-4ba4-9fcf-2812a1b7ab8b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc85799d-ff28-4ba4-9fcf-2812a1b7ab8b.lance deleted file mode 100644 index 38c6f94e7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fc85799d-ff28-4ba4-9fcf-2812a1b7ab8b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fca81fb3-669e-420f-b05f-cb4043c7f207.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fca81fb3-669e-420f-b05f-cb4043c7f207.lance deleted file mode 100644 index c06789042..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fca81fb3-669e-420f-b05f-cb4043c7f207.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fcc4d8cc-13bc-4324-8722-f3a6bb37efa8.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fcc4d8cc-13bc-4324-8722-f3a6bb37efa8.lance deleted file mode 100644 index 77fce0981..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fcc4d8cc-13bc-4324-8722-f3a6bb37efa8.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fcd04b52-d91c-4f65-8a26-027d07f1787b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fcd04b52-d91c-4f65-8a26-027d07f1787b.lance deleted file mode 100644 index 2d3607961..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fcd04b52-d91c-4f65-8a26-027d07f1787b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fce4d224-64b0-444d-9ca7-dce17e2efc78.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fce4d224-64b0-444d-9ca7-dce17e2efc78.lance deleted file mode 100644 index c04ecdccd..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fce4d224-64b0-444d-9ca7-dce17e2efc78.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fcecee30-4de7-4f5e-a72c-0df7941790fb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fcecee30-4de7-4f5e-a72c-0df7941790fb.lance deleted file mode 100644 index 96ebb123e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fcecee30-4de7-4f5e-a72c-0df7941790fb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fcfce868-cd91-454c-921d-4a10e0919ff0.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fcfce868-cd91-454c-921d-4a10e0919ff0.lance deleted file mode 100644 index ffe6a3a94..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fcfce868-cd91-454c-921d-4a10e0919ff0.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd0069af-e5ba-4bef-a1f7-b00b12ee3c2e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd0069af-e5ba-4bef-a1f7-b00b12ee3c2e.lance deleted file mode 100644 index 2ab030a5a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd0069af-e5ba-4bef-a1f7-b00b12ee3c2e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd339cbd-2765-4bef-b645-541fe3aaae24.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd339cbd-2765-4bef-b645-541fe3aaae24.lance deleted file mode 100644 index 808ac8902..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd339cbd-2765-4bef-b645-541fe3aaae24.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd3b775f-14b8-40b0-a3de-8c192950239d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd3b775f-14b8-40b0-a3de-8c192950239d.lance deleted file mode 100644 index b4da8274c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd3b775f-14b8-40b0-a3de-8c192950239d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd453865-1a00-4cb7-8b89-b49625414f3b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd453865-1a00-4cb7-8b89-b49625414f3b.lance deleted file mode 100644 index 0182af6cb..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd453865-1a00-4cb7-8b89-b49625414f3b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd458ce1-70e0-4778-897e-4f3a44c1248c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd458ce1-70e0-4778-897e-4f3a44c1248c.lance deleted file mode 100644 index a9c4e9598..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd458ce1-70e0-4778-897e-4f3a44c1248c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd7344cb-363b-465d-bd0d-7b98a35c3067.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd7344cb-363b-465d-bd0d-7b98a35c3067.lance deleted file mode 100644 index 0d98117e1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd7344cb-363b-465d-bd0d-7b98a35c3067.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd7a0d2a-92e2-4f6b-8cdf-375f428742fb.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd7a0d2a-92e2-4f6b-8cdf-375f428742fb.lance deleted file mode 100644 index b23c85ec7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd7a0d2a-92e2-4f6b-8cdf-375f428742fb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd7b9916-40b7-4f24-8090-3fdb3bc619ee.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd7b9916-40b7-4f24-8090-3fdb3bc619ee.lance deleted file mode 100644 index 722d8f94b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd7b9916-40b7-4f24-8090-3fdb3bc619ee.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd8ba083-e7f8-4102-8adb-eea4b8c57e45.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd8ba083-e7f8-4102-8adb-eea4b8c57e45.lance deleted file mode 100644 index d625cfaa2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd8ba083-e7f8-4102-8adb-eea4b8c57e45.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd971494-5409-4938-b410-727903519757.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd971494-5409-4938-b410-727903519757.lance deleted file mode 100644 index 1015f91ed..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fd971494-5409-4938-b410-727903519757.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdac93e7-5975-497f-9f79-7e8088561a6a.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdac93e7-5975-497f-9f79-7e8088561a6a.lance deleted file mode 100644 index c6fc960ca..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdac93e7-5975-497f-9f79-7e8088561a6a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdad72ab-663a-43b5-bf27-231e200acef6.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdad72ab-663a-43b5-bf27-231e200acef6.lance deleted file mode 100644 index 2fa5bee7d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdad72ab-663a-43b5-bf27-231e200acef6.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdb4aa51-3831-494f-b5f4-12fa9b44ff07.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdb4aa51-3831-494f-b5f4-12fa9b44ff07.lance deleted file mode 100644 index 50ed68ac7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdb4aa51-3831-494f-b5f4-12fa9b44ff07.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdc20a0a-5270-477b-afd1-27294967d3e7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdc20a0a-5270-477b-afd1-27294967d3e7.lance deleted file mode 100644 index db0aa2484..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdc20a0a-5270-477b-afd1-27294967d3e7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdd8f047-aece-4ce0-a272-397d9ff5c5ab.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdd8f047-aece-4ce0-a272-397d9ff5c5ab.lance deleted file mode 100644 index 51738356b..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdd8f047-aece-4ce0-a272-397d9ff5c5ab.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdfb97b5-d56d-48b2-b9ef-5a76dbc22f99.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdfb97b5-d56d-48b2-b9ef-5a76dbc22f99.lance deleted file mode 100644 index 061bbe813..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdfb97b5-d56d-48b2-b9ef-5a76dbc22f99.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdfd01b8-f2f2-435e-8798-a5cc7a607fcf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdfd01b8-f2f2-435e-8798-a5cc7a607fcf.lance deleted file mode 100644 index 3b3907723..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fdfd01b8-f2f2-435e-8798-a5cc7a607fcf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe006102-e016-41aa-9205-97c107a9f8f4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe006102-e016-41aa-9205-97c107a9f8f4.lance deleted file mode 100644 index 93967cf1c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe006102-e016-41aa-9205-97c107a9f8f4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe1a6ff6-ea43-4cb9-acfd-f5d159ba5710.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe1a6ff6-ea43-4cb9-acfd-f5d159ba5710.lance deleted file mode 100644 index eef7b9d41..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe1a6ff6-ea43-4cb9-acfd-f5d159ba5710.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe2ad0ca-daa5-4649-87f7-ecf53e421a65.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe2ad0ca-daa5-4649-87f7-ecf53e421a65.lance deleted file mode 100644 index ad00c2c50..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe2ad0ca-daa5-4649-87f7-ecf53e421a65.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe34201a-0669-48a3-9730-457f9c7b6973.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe34201a-0669-48a3-9730-457f9c7b6973.lance deleted file mode 100644 index ce657c001..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe34201a-0669-48a3-9730-457f9c7b6973.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe3a9755-6cae-4762-a007-b4b41d38fc5c.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe3a9755-6cae-4762-a007-b4b41d38fc5c.lance deleted file mode 100644 index e000bb043..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe3a9755-6cae-4762-a007-b4b41d38fc5c.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe3df006-70c6-473f-b353-c506956881f2.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe3df006-70c6-473f-b353-c506956881f2.lance deleted file mode 100644 index c1e44f3ad..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe3df006-70c6-473f-b353-c506956881f2.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe499c7d-7b96-441f-9872-63fd767babb5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe499c7d-7b96-441f-9872-63fd767babb5.lance deleted file mode 100644 index 35b996c19..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe499c7d-7b96-441f-9872-63fd767babb5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe4ed645-d561-41b0-b3e9-e501207a6c02.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe4ed645-d561-41b0-b3e9-e501207a6c02.lance deleted file mode 100644 index 7567e3d95..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe4ed645-d561-41b0-b3e9-e501207a6c02.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe5a85b9-1034-4416-8a87-68359c9e3f43.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe5a85b9-1034-4416-8a87-68359c9e3f43.lance deleted file mode 100644 index eaa98bcfc..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe5a85b9-1034-4416-8a87-68359c9e3f43.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe816151-76af-415f-a980-57fa6e6adc84.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe816151-76af-415f-a980-57fa6e6adc84.lance deleted file mode 100644 index 18273f66c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fe816151-76af-415f-a980-57fa6e6adc84.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/feb548b6-e797-4099-a1bf-2441030f06e4.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/feb548b6-e797-4099-a1bf-2441030f06e4.lance deleted file mode 100644 index 0b4a05d2c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/feb548b6-e797-4099-a1bf-2441030f06e4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fec0ae3b-f2ff-46e8-8b5b-933174ea6779.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fec0ae3b-f2ff-46e8-8b5b-933174ea6779.lance deleted file mode 100644 index c46cccf64..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fec0ae3b-f2ff-46e8-8b5b-933174ea6779.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/feec8a15-8d6a-4947-aa20-f76ae89a366d.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/feec8a15-8d6a-4947-aa20-f76ae89a366d.lance deleted file mode 100644 index b1cd54f73..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/feec8a15-8d6a-4947-aa20-f76ae89a366d.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fefce0f7-1d42-4648-b18e-a88c33984096.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fefce0f7-1d42-4648-b18e-a88c33984096.lance deleted file mode 100644 index d5a1b8fa0..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fefce0f7-1d42-4648-b18e-a88c33984096.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff05cd32-3683-4d9f-960f-0342d9a2129e.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff05cd32-3683-4d9f-960f-0342d9a2129e.lance deleted file mode 100644 index 84d36a21c..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff05cd32-3683-4d9f-960f-0342d9a2129e.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff06d181-dd74-44c8-b0a3-30d6ab051cd5.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff06d181-dd74-44c8-b0a3-30d6ab051cd5.lance deleted file mode 100644 index f3b8ed372..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff06d181-dd74-44c8-b0a3-30d6ab051cd5.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff0b2915-c458-4c24-a6f7-8cb1223da6f7.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff0b2915-c458-4c24-a6f7-8cb1223da6f7.lance deleted file mode 100644 index 3f411e800..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff0b2915-c458-4c24-a6f7-8cb1223da6f7.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff10029c-5bfa-4e58-a8ca-30a5622b2929.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff10029c-5bfa-4e58-a8ca-30a5622b2929.lance deleted file mode 100644 index 6b528a3c9..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff10029c-5bfa-4e58-a8ca-30a5622b2929.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff19bdc4-6686-408d-8e15-355d3941fc99.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff19bdc4-6686-408d-8e15-355d3941fc99.lance deleted file mode 100644 index 5d8482a66..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff19bdc4-6686-408d-8e15-355d3941fc99.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff1ba5b9-7eb1-4cc6-a738-bb9dd4163459.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff1ba5b9-7eb1-4cc6-a738-bb9dd4163459.lance deleted file mode 100644 index 867b1d7ac..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff1ba5b9-7eb1-4cc6-a738-bb9dd4163459.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff2a2947-0f55-4b8a-9d5e-b817064d5b07.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff2a2947-0f55-4b8a-9d5e-b817064d5b07.lance deleted file mode 100644 index c36b83af8..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff2a2947-0f55-4b8a-9d5e-b817064d5b07.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff2a4318-eb15-4fb6-a060-78c09cb6de16.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff2a4318-eb15-4fb6-a060-78c09cb6de16.lance deleted file mode 100644 index 64dbd7461..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff2a4318-eb15-4fb6-a060-78c09cb6de16.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff44a3ca-16f7-4125-ab0a-92f019d34072.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff44a3ca-16f7-4125-ab0a-92f019d34072.lance deleted file mode 100644 index cf9680367..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff44a3ca-16f7-4125-ab0a-92f019d34072.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff590212-c653-4a34-9b45-d2287424ea1b.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff590212-c653-4a34-9b45-d2287424ea1b.lance deleted file mode 100644 index 7a3d697d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff590212-c653-4a34-9b45-d2287424ea1b.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff640d40-410b-4a61-8bc6-785414a42c56.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff640d40-410b-4a61-8bc6-785414a42c56.lance deleted file mode 100644 index 95f899db1..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff640d40-410b-4a61-8bc6-785414a42c56.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff72bb31-92b4-44d3-a2a3-16f472613fdf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff72bb31-92b4-44d3-a2a3-16f472613fdf.lance deleted file mode 100644 index e6f228475..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ff72bb31-92b4-44d3-a2a3-16f472613fdf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ffa8003e-beea-4e6b-bfe3-38e7043fb724.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ffa8003e-beea-4e6b-bfe3-38e7043fb724.lance deleted file mode 100644 index 2670ebc6f..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ffa8003e-beea-4e6b-bfe3-38e7043fb724.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ffdac4b8-b104-4d22-a80a-a6455bcf11dd.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ffdac4b8-b104-4d22-a80a-a6455bcf11dd.lance deleted file mode 100644 index 11b755031..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/ffdac4b8-b104-4d22-a80a-a6455bcf11dd.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fff0a680-663d-4599-95ad-814242d8ddaf.lance b/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fff0a680-663d-4599-95ad-814242d8ddaf.lance deleted file mode 100644 index 2f0f65f43..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_markdown.lance/data/fff0a680-663d-4599-95ad-814242d8ddaf.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_test_user.lance/_transactions/0-aedaaddb-c380-4f9f-aee8-8596ba109d72.txn b/pkg/hanzo-memory/data/lancedb/memories_test_user.lance/_transactions/0-aedaaddb-c380-4f9f-aee8-8596ba109d72.txn deleted file mode 100644 index 4a2bf42b5..000000000 --- a/pkg/hanzo-memory/data/lancedb/memories_test_user.lance/_transactions/0-aedaaddb-c380-4f9f-aee8-8596ba109d72.txn +++ /dev/null @@ -1,7 +0,0 @@ -$aedaaddb-c380-4f9f-aee8-8596ba109d72ฒ๓) memory_id *string8Zdefault)user_id *string8Zdefault, -project_id *string8Zdefault)content *string8Zdefault*metadata *string8Zdefault, -importance *double8Zdefault, -created_at *string8Zdefault, -updated_at *string8Zdefault@ embedding *fixed_size_list:float:38408Zdefault"! -lance.auto_cleanup.interval20"' -lance.auto_cleanup.older_than14days \ No newline at end of file diff --git a/pkg/hanzo-memory/data/lancedb/memories_test_user.lance/_transactions/1-7dcbe99f-e10b-47a3-b9df-8bc295b2bd6f.txn b/pkg/hanzo-memory/data/lancedb/memories_test_user.lance/_transactions/1-7dcbe99f-e10b-47a3-b9df-8bc295b2bd6f.txn deleted file mode 100644 index 99c072530..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_test_user.lance/_transactions/1-7dcbe99f-e10b-47a3-b9df-8bc295b2bd6f.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_test_user.lance/_versions/1.manifest b/pkg/hanzo-memory/data/lancedb/memories_test_user.lance/_versions/1.manifest deleted file mode 100644 index 262fc6b25..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_test_user.lance/_versions/1.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_test_user.lance/_versions/2.manifest b/pkg/hanzo-memory/data/lancedb/memories_test_user.lance/_versions/2.manifest deleted file mode 100644 index 5a146aaa4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_test_user.lance/_versions/2.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_test_user.lance/data/162e3598-a17a-4a23-9bd5-42bdff126d58.lance b/pkg/hanzo-memory/data/lancedb/memories_test_user.lance/data/162e3598-a17a-4a23-9bd5-42bdff126d58.lance deleted file mode 100644 index 36fe0edf7..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_test_user.lance/data/162e3598-a17a-4a23-9bd5-42bdff126d58.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/memories_test_user_123.lance/_transactions/0-b6a4e7b1-4af4-4d78-9447-ce233edb1807.txn b/pkg/hanzo-memory/data/lancedb/memories_test_user_123.lance/_transactions/0-b6a4e7b1-4af4-4d78-9447-ce233edb1807.txn deleted file mode 100644 index 78d51f11e..000000000 --- a/pkg/hanzo-memory/data/lancedb/memories_test_user_123.lance/_transactions/0-b6a4e7b1-4af4-4d78-9447-ce233edb1807.txn +++ /dev/null @@ -1,7 +0,0 @@ -$b6a4e7b1-4af4-4d78-9447-ce233edb1807ฒ๓) memory_id *string8Zdefault)user_id *string8Zdefault, -project_id *string8Zdefault)content *string8Zdefault*metadata *string8Zdefault, -importance *double8Zdefault, -created_at *string8Zdefault, -updated_at *string8Zdefault@ embedding *fixed_size_list:float:38408Zdefault"! -lance.auto_cleanup.interval20"' -lance.auto_cleanup.older_than14days \ No newline at end of file diff --git a/pkg/hanzo-memory/data/lancedb/memories_test_user_123.lance/_versions/1.manifest b/pkg/hanzo-memory/data/lancedb/memories_test_user_123.lance/_versions/1.manifest deleted file mode 100644 index 047870540..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/memories_test_user_123.lance/_versions/1.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/messages_test_session.lance/_transactions/0-90746bf0-d613-4870-b391-08c87e7a4c76.txn b/pkg/hanzo-memory/data/lancedb/messages_test_session.lance/_transactions/0-90746bf0-d613-4870-b391-08c87e7a4c76.txn deleted file mode 100644 index 6a12739d0..000000000 --- a/pkg/hanzo-memory/data/lancedb/messages_test_session.lance/_transactions/0-90746bf0-d613-4870-b391-08c87e7a4c76.txn +++ /dev/null @@ -1,6 +0,0 @@ -$90746bf0-d613-4870-b391-08c87e7a4c76ฒ•* -message_id *string8Zdefault, -session_id *string8Zdefault&role *string8Zdefault)content *string8Zdefault*metadata *string8Zdefault, -created_at *string8Zdefault@ embedding *fixed_size_list:float:38408Zdefault"! -lance.auto_cleanup.interval20"' -lance.auto_cleanup.older_than14days \ No newline at end of file diff --git a/pkg/hanzo-memory/data/lancedb/messages_test_session.lance/_transactions/1-e51dc463-3f6c-49ff-8c36-a8e833582f00.txn b/pkg/hanzo-memory/data/lancedb/messages_test_session.lance/_transactions/1-e51dc463-3f6c-49ff-8c36-a8e833582f00.txn deleted file mode 100644 index 1cb01608d..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/messages_test_session.lance/_transactions/1-e51dc463-3f6c-49ff-8c36-a8e833582f00.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/messages_test_session.lance/_versions/1.manifest b/pkg/hanzo-memory/data/lancedb/messages_test_session.lance/_versions/1.manifest deleted file mode 100644 index 9eb506940..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/messages_test_session.lance/_versions/1.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/messages_test_session.lance/_versions/2.manifest b/pkg/hanzo-memory/data/lancedb/messages_test_session.lance/_versions/2.manifest deleted file mode 100644 index b8124584a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/messages_test_session.lance/_versions/2.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/messages_test_session.lance/data/58fc22ca-d019-4bf8-90f5-61afa7d982cb.lance b/pkg/hanzo-memory/data/lancedb/messages_test_session.lance/data/58fc22ca-d019-4bf8-90f5-61afa7d982cb.lance deleted file mode 100644 index feba5b61a..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/messages_test_session.lance/data/58fc22ca-d019-4bf8-90f5-61afa7d982cb.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/projects.lance/_transactions/0-0b44a30c-58de-4038-981c-52d011ad8e27.txn b/pkg/hanzo-memory/data/lancedb/projects.lance/_transactions/0-0b44a30c-58de-4038-981c-52d011ad8e27.txn deleted file mode 100644 index 6f3d668ea..000000000 --- a/pkg/hanzo-memory/data/lancedb/projects.lance/_transactions/0-0b44a30c-58de-4038-981c-52d011ad8e27.txn +++ /dev/null @@ -1,6 +0,0 @@ -$0b44a30c-58de-4038-981c-52d011ad8e27ฒ‚* -project_id *string8Zdefault)user_id *string8Zdefault&name *string8Zdefault- description *string8Zdefault*metadata *string8Zdefault, -created_at *string8Zdefault, -updated_at *string8Zdefault"! -lance.auto_cleanup.interval20"' -lance.auto_cleanup.older_than14days \ No newline at end of file diff --git a/pkg/hanzo-memory/data/lancedb/projects.lance/_transactions/1-5f38169c-7319-4f49-8d5e-6cd12e77fddf.txn b/pkg/hanzo-memory/data/lancedb/projects.lance/_transactions/1-5f38169c-7319-4f49-8d5e-6cd12e77fddf.txn deleted file mode 100644 index 0edda47d4..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/projects.lance/_transactions/1-5f38169c-7319-4f49-8d5e-6cd12e77fddf.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/projects.lance/_transactions/2-419f9c0f-4d5f-4ae4-a064-51ceefcd4733.txn b/pkg/hanzo-memory/data/lancedb/projects.lance/_transactions/2-419f9c0f-4d5f-4ae4-a064-51ceefcd4733.txn deleted file mode 100644 index 23c8632c2..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/projects.lance/_transactions/2-419f9c0f-4d5f-4ae4-a064-51ceefcd4733.txn and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/projects.lance/_versions/1.manifest b/pkg/hanzo-memory/data/lancedb/projects.lance/_versions/1.manifest deleted file mode 100644 index 15da814f6..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/projects.lance/_versions/1.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/projects.lance/_versions/2.manifest b/pkg/hanzo-memory/data/lancedb/projects.lance/_versions/2.manifest deleted file mode 100644 index 696083c21..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/projects.lance/_versions/2.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/projects.lance/_versions/3.manifest b/pkg/hanzo-memory/data/lancedb/projects.lance/_versions/3.manifest deleted file mode 100644 index d4b1dbe2e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/projects.lance/_versions/3.manifest and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/projects.lance/data/264d00e8-2a85-4b54-8473-52dfbedd75a4.lance b/pkg/hanzo-memory/data/lancedb/projects.lance/data/264d00e8-2a85-4b54-8473-52dfbedd75a4.lance deleted file mode 100644 index 91e9896ba..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/projects.lance/data/264d00e8-2a85-4b54-8473-52dfbedd75a4.lance and /dev/null differ diff --git a/pkg/hanzo-memory/data/lancedb/projects.lance/data/7b514e1d-48d4-4054-a2f3-72e9396e130a.lance b/pkg/hanzo-memory/data/lancedb/projects.lance/data/7b514e1d-48d4-4054-a2f3-72e9396e130a.lance deleted file mode 100644 index 98f42cf3e..000000000 Binary files a/pkg/hanzo-memory/data/lancedb/projects.lance/data/7b514e1d-48d4-4054-a2f3-72e9396e130a.lance and /dev/null differ diff --git a/pkg/hanzo-memory/docs/API.md b/pkg/hanzo-memory/docs/API.md deleted file mode 100644 index a9103b52b..000000000 --- a/pkg/hanzo-memory/docs/API.md +++ /dev/null @@ -1,747 +0,0 @@ -# Hanzo Memory Service - Full API Documentation - -## Table of Contents - -1. [Overview](#overview) -2. [Authentication](#authentication) -3. [Memory Management API](#memory-management-api) -4. [Knowledge Base API](#knowledge-base-api) -5. [Chat Management API](#chat-management-api) -6. [MCP Server](#mcp-server) -7. [Data Models](#data-models) -8. [Error Handling](#error-handling) -9. [Rate Limiting](#rate-limiting) -10. [Examples](#examples) - -## Overview - -The Hanzo Memory Service provides a comprehensive API for managing memories, knowledge bases, and chat histories with intelligent search and retrieval capabilities. - -**Base URL**: `http://localhost:4000` - -**API Version**: `v1` - -## Authentication - -The API uses Bearer token authentication. Include your API key in the Authorization header: - -``` -Authorization: Bearer YOUR_API_KEY -``` - -Alternative authentication methods: -- `x-api-key` header -- `x-api-key` header -- `apikey` in request body (for backwards compatibility) - -### Disabling Auth for Development - -Set `HANZO_DISABLE_AUTH=true` in your environment to disable authentication. - -## Memory Management API - -### POST /v1/remember - -Retrieve relevant memories and store a new memory in one operation. - -**Request Body:** -```json -{ - "apikey": "optional-api-key", - "userid": "user-123", - "messagecontent": "Remember that I prefer dark mode interfaces", - "additionalcontext": "User preferences discussion", - "strippii": false, - "filterresults": true, - "includememoryid": false -} -``` - -**Parameters:** -- `userid` (required): User identifier -- `messagecontent` (required): Content to remember and search for -- `additionalcontext`: Additional context for the memory -- `strippii`: Strip personally identifiable information (default: false) -- `filterresults`: Use LLM to filter search results for relevance (default: false) -- `includememoryid`: Include memory IDs in response (default: false) - -**Response:** -```json -{ - "user_id": "user-123", - "relevant_memories": [ - "User prefers VS Code as their editor", - "User likes dark themes in general" - ], - "memory_stored": true, - "usage_info": { - "current": 42, - "limit": 10000 - } -} -``` - -### POST /v1/memories/add - -Add explicit memories without importance analysis. - -**Request Body:** -```json -{ - "apikey": "optional-api-key", - "userid": "user-123", - "memoriestoadd": [ - "User is allergic to shellfish", - "User's birthday is March 15th" - ] -} -``` - -**Response:** -```json -{ - "userid": "user-123", - "added_count": 2, - "memory_ids": ["mem_abc123", "mem_def456"], - "usage_info": { - "current": 44, - "limit": 10000 - } -} -``` - -### POST /v1/memories/get - -Retrieve stored memories with pagination. - -**Request Body:** -```json -{ - "apikey": "optional-api-key", - "userid": "user-123", - "memoryid": "mem_abc123", // Optional: get specific memory - "limit": 50, - "startafter": "mem_xyz789" -} -``` - -**Response:** -```json -{ - "user_id": "user-123", - "memories": [ - { - "memory_id": "mem_abc123", - "content": "User is allergic to shellfish", - "importance": 8.5, - "metadata": { - "category": "health", - "added_at": "2024-01-15T10:30:00Z" - }, - "created_at": "2024-01-15T10:30:00Z", - "updated_at": "2024-01-15T10:30:00Z" - } - ], - "pagination": { - "has_more": true, - "last_id": "mem_ghi789" - }, - "usage_info": { - "current": 44, - "limit": 10000 - } -} -``` - -### POST /v1/memories/delete - -Delete a specific memory. - -**Request Body:** -```json -{ - "apikey": "optional-api-key", - "userid": "user-123", - "memoryid": "mem_abc123" -} -``` - -**Response:** -```json -{ - "message": "Memory deleted successfully", - "memory_id": "mem_abc123", - "userid": "user-123" -} -``` - -### POST /v1/user/delete - -Delete all memories for a user. - -**Request Body:** -```json -{ - "apikey": "optional-api-key", - "userid": "user-123", - "confirmdelete": true -} -``` - -**Response:** -```json -{ - "message": "All user memories deleted", - "userid": "user-123", - "deleted_count": 44 -} -``` - -## Knowledge Base API - -### POST /v1/kb/create - -Create a new knowledge base. - -**Request Body:** -```json -{ - "userid": "user-123", - "name": "Python Programming", - "kb_id": "kb_python", // Optional custom ID - "description": "Knowledge about Python programming", - "project_id": "proj_456" // Optional -} -``` - -**Response:** -```json -{ - "kb_id": "kb_python", - "message": "Knowledge base 'Python Programming' created successfully" -} -``` - -### GET /v1/kb/list - -List knowledge bases for a user. - -**Query Parameters:** -- `userid` (required): User ID -- `project_id`: Filter by project ID - -**Response:** -```json -{ - "userid": "user-123", - "knowledge_bases": [ - { - "kb_id": "kb_python", - "name": "Python Programming", - "description": "Knowledge about Python programming", - "fact_count": 150, - "created_at": "2024-01-10T08:00:00Z", - "updated_at": "2024-01-15T14:30:00Z" - } - ], - "total": 1 -} -``` - -### POST /v1/kb/facts/add - -Add facts to a knowledge base. - -**Request Body:** -```json -{ - "userid": "user-123", - "kb_id": "kb_python", - "facts": [ - { - "content": "Python uses indentation for code blocks", - "metadata": { - "category": "syntax", - "importance": "high" - }, - "parent_id": null, - "fact_id": "fact_001" // Optional custom ID - }, - { - "content": "Standard indentation is 4 spaces", - "parent_id": "fact_001", - "metadata": { - "category": "style" - } - } - ] -} -``` - -**Response:** -```json -{ - "kb_id": "kb_python", - "facts_added": 2, - "facts": [ - { - "fact_id": "fact_001", - "content": "Python uses indentation for code blocks" - }, - { - "fact_id": "fact_abc123", - "content": "Standard indentation is 4 spaces" - } - ] -} -``` - -### POST /v1/kb/facts/get - -Get facts from a knowledge base. - -**Request Body:** -```json -{ - "userid": "user-123", - "kb_id": "kb_python", - "query": "indentation rules", // Optional: search query - "fact_id": "fact_001", // Optional: get specific fact - "subtree": true, // Get fact and all children - "limit": 50 -} -``` - -**Response:** -```json -{ - "kb_id": "kb_python", - "facts": [ - { - "fact_id": "fact_001", - "content": "Python uses indentation for code blocks", - "parent_id": null, - "metadata": { - "category": "syntax", - "importance": "high" - }, - "similarity_score": 0.95 - }, - { - "fact_id": "fact_abc123", - "content": "Standard indentation is 4 spaces", - "parent_id": "fact_001", - "metadata": { - "category": "style" - }, - "similarity_score": 0.88 - } - ], - "total": 2 -} -``` - -### POST /v1/kb/facts/delete - -Delete a fact from a knowledge base. - -**Request Body:** -```json -{ - "userid": "user-123", - "kb_id": "kb_python", - "fact_id": "fact_001", - "cascade": true // Delete all child facts -} -``` - -**Response:** -```json -{ - "kb_id": "kb_python", - "fact_id": "fact_001", - "deleted": true, - "cascade": true -} -``` - -## Chat Management API - -### POST /v1/chat/sessions/create - -Create a new chat session. - -**Request Body:** -```json -{ - "userid": "user-123", - "session_id": "session_abc", // Optional custom ID - "project_id": "proj_456", // Optional - "title": "Python Help Session", - "metadata": { - "client": "web", - "version": "1.0" - } -} -``` - -**Response:** -```json -{ - "session_id": "session_abc", - "userid": "user-123", - "project_id": "proj_456", - "created": true -} -``` - -### POST /v1/chat/messages/add - -Add a message to a chat session with automatic de-duplication. - -**Request Body:** -```json -{ - "userid": "user-123", - "session_id": "session_abc", - "role": "user", // user, assistant, or system - "content": "How do I create a virtual environment in Python?", - "project_id": "proj_456", // Optional - "metadata": { - "timestamp": "2024-01-15T10:30:00Z" - } -} -``` - -**Response:** -```json -{ - "chat_id": "msg_xyz789", - "session_id": "session_abc", - "duplicate": false // True if message was deduplicated -} -``` - -### GET /v1/chat/sessions/{session_id}/messages - -Get messages for a chat session. - -**Path Parameters:** -- `session_id`: The session ID - -**Query Parameters:** -- `userid` (required): User ID -- `limit`: Maximum messages to return (default: 100, max: 1000) - -**Response:** -```json -{ - "session_id": "session_abc", - "messages": [ - { - "chat_id": "msg_001", - "role": "user", - "content": "How do I create a virtual environment in Python?", - "metadata": { - "timestamp": "2024-01-15T10:30:00Z" - }, - "created_at": "2024-01-15T10:30:00Z" - }, - { - "chat_id": "msg_002", - "role": "assistant", - "content": "You can create a virtual environment using: python -m venv myenv", - "metadata": { - "model": "gpt-4" - }, - "created_at": "2024-01-15T10:30:15Z" - } - ], - "total": 2 -} -``` - -### POST /v1/chat/search - -Search across chat messages. - -**Query Parameters:** -- `query` (required): Search query -- `userid` (required): User ID -- `project_id`: Filter by project -- `session_id`: Filter by session -- `limit`: Maximum results (default: 10, max: 100) - -**Response:** -```json -{ - "query": "virtual environment", - "messages": [ - { - "chat_id": "msg_001", - "session_id": "session_abc", - "role": "user", - "content": "How do I create a virtual environment in Python?", - "similarity_score": 0.95, - "created_at": "2024-01-15T10:30:00Z" - } - ], - "total": 1 -} -``` - -## MCP Server - -The service includes a Model Context Protocol (MCP) server for AI tool integration. - -### Installation - -Add to Claude Desktop configuration: - -```json -{ - "mcpServers": { - "hanzo-memory": { - "command": "uv", - "args": ["run", "hanzo-memory-mcp"], - "cwd": "/path/to/hanzo/memory" - } - } -} -``` - -### Available Tools - -1. **remember** - Store and retrieve memories -2. **recall** - Search for memories -3. **create_project** - Create a new project -4. **create_knowledge_base** - Create a knowledge base -5. **add_fact** - Add facts to a knowledge base -6. **search_facts** - Search facts in a knowledge base -7. **summarize_for_knowledge** - Generate knowledge instructions - -## Data Models - -### Memory -```typescript -interface Memory { - memory_id: string; - user_id: string; - project_id: string; - content: string; - importance: number; // 0-10 - metadata: Record; - embedding?: number[]; // Vector embedding - created_at: string; - updated_at: string; -} -``` - -### Knowledge Base -```typescript -interface KnowledgeBase { - kb_id: string; - user_id: string; - project_id: string; - name: string; - description: string; - metadata: Record; - fact_count: number; - created_at: string; - updated_at: string; -} -``` - -### Fact -```typescript -interface Fact { - fact_id: string; - kb_id: string; - content: string; - parent_id?: string; // For hierarchical facts - metadata: Record; - embedding?: number[]; - created_at: string; - updated_at: string; -} -``` - -### Chat Message -```typescript -interface ChatMessage { - chat_id: string; - session_id: string; - user_id: string; - project_id: string; - role: "user" | "assistant" | "system"; - content: string; - metadata: Record; - embedding?: number[]; - created_at: string; -} -``` - -## Error Handling - -All errors follow this format: - -```json -{ - "error": "Error message", - "detail": "Detailed error information", - "status_code": 400 -} -``` - -Common HTTP status codes: -- `200` - Success -- `400` - Bad Request -- `401` - Unauthorized -- `404` - Not Found -- `429` - Rate Limited -- `500` - Internal Server Error - -## Rate Limiting - -Default limits (configurable): -- 1000 requests per hour per API key -- 100 concurrent requests per API key -- 10MB maximum request size - -## Examples - -### Python Client Example - -```python -import requests - -BASE_URL = "http://localhost:4000" -API_KEY = "your-api-key" - -headers = { - "Authorization": f"Bearer {API_KEY}", - "Content-Type": "application/json" -} - -# Store a memory -response = requests.post( - f"{BASE_URL}/v1/remember", - headers=headers, - json={ - "userid": "user-123", - "messagecontent": "I prefer TypeScript over JavaScript", - "additionalcontext": "Programming preferences" - } -) - -print(response.json()) -``` - -### JavaScript/TypeScript Example - -```typescript -const BASE_URL = "http://localhost:4000"; -const API_KEY = "your-api-key"; - -// Add facts to knowledge base -const response = await fetch(`${BASE_URL}/v1/kb/facts/add`, { - method: "POST", - headers: { - "Authorization": `Bearer ${API_KEY}`, - "Content-Type": "application/json" - }, - body: JSON.stringify({ - userid: "user-123", - kb_id: "kb_typescript", - facts: [ - { - content: "TypeScript is a superset of JavaScript", - metadata: { category: "definition" } - } - ] - }) -}); - -const result = await response.json(); -console.log(result); -``` - -### cURL Examples - -```bash -# Create a chat session -curl -X POST http://localhost:4000/v1/chat/sessions/create \ - -H "Authorization: Bearer YOUR_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{ - "userid": "user-123", - "title": "Help with Python" - }' - -# Search memories -curl -X POST http://localhost:4000/v1/remember \ - -H "Authorization: Bearer YOUR_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{ - "userid": "user-123", - "messagecontent": "What are my programming preferences?", - "filterresults": true - }' -``` - -## Performance Considerations - -1. **Embeddings** are generated locally using FastEmbed (no API calls) -2. **Vector search** uses InfinityDB's efficient in-memory indexing -3. **De-duplication** uses similarity threshold (0.99) to prevent exact duplicates -4. **Batch operations** are recommended for bulk inserts -5. **Caching** can be enabled with Redis for high-traffic scenarios - -## Security Best Practices - -1. Always use HTTPS in production -2. Rotate API keys regularly -3. Enable rate limiting -4. Use project-based isolation for multi-tenant scenarios -5. Configure CORS appropriately -6. Never store sensitive data in metadata fields -7. Enable PII stripping for sensitive content - -## Troubleshooting - -### Common Issues - -1. **InfinityDB not available on platform** - - The service automatically falls back to a mock implementation - - Full functionality is available on Linux x86_64 - -2. **Embedding model download fails** - - Models are cached after first download - - Ensure sufficient disk space (~400MB per model) - -3. **LLM API errors** - - Check API keys are correctly set - - Verify model names match provider format - - Use local models (Ollama) for offline operation - -### Debug Mode - -Enable debug logging: -```bash -HANZO_LOG_LEVEL=DEBUG uvicorn hanzo_memory.server:app -``` - -### Health Check - -```bash -curl http://localhost:4000/health -``` - -Response: -```json -{ - "status": "healthy", - "service": "hanzo-memory", - "version": "0.1.0" -} -``` \ No newline at end of file diff --git a/pkg/hanzo-memory/examples/backend_usage.py b/pkg/hanzo-memory/examples/backend_usage.py deleted file mode 100644 index 10399a1e5..000000000 --- a/pkg/hanzo-memory/examples/backend_usage.py +++ /dev/null @@ -1,227 +0,0 @@ -#!/usr/bin/env python3 -"""Examples of using different memory backends with the new accessor syntax.""" - -import asyncio -from hanzo_memory.memory import memory -from hanzo_memory.models.memory import MemoryCreate -from hanzo_memory.models.project import ProjectCreate - - -async def example_lancedb(): - """Example using LanceDB for vector search.""" - print("\n๐Ÿš€ LanceDB Example - Vector Search") - print("-" * 40) - - # Access LanceDB backend using dictionary syntax - lance = memory["lancedb"] - await lance.initialize() - - # Create a project - project = ProjectCreate( - name="AI Research", description="Storing AI research notes with embeddings" - ) - proj = await lance.client.create_project(project, user_id="researcher") - - # Store memories with embeddings (normally would use real embeddings) - memories = [ - "Transformers revolutionized NLP with self-attention mechanisms", - "GPT models use decoder-only architecture for text generation", - "BERT uses bidirectional training for better context understanding", - "Vision transformers apply attention to image patches", - ] - - for content in memories: - mem = MemoryCreate( - content=content, - memory_type="research", - importance=0.9, - embedding=[0.1] * 384, # Dummy embedding - ) - await lance.client.create_memory(mem, proj.project_id, "researcher") - - print("โœ… Stored research memories with embeddings") - - # Search with vector similarity - results = await lance.client.search_memories_async( - query_embedding=[0.1] * 384, # Would use real query embedding - project_id=proj.project_id, - user_id="researcher", - limit=3, - ) - print(f"โœ… Found {len(results)} similar memories") - - await lance.close() - - -async def example_kuzudb(): - """Example using KuzuDB for graph relationships.""" - print("\n๐Ÿ•ธ๏ธ KuzuDB Example - Graph Relationships") - print("-" * 40) - - try: - # Access KuzuDB backend using attribute syntax - kuzu = memory.kuzudb - await kuzu.initialize() - - # Create a project - project = ProjectCreate( - name="Knowledge Graph", description="Building connected knowledge" - ) - proj = await kuzu.client.create_project(project, user_id="analyst") - - # Store interconnected memories - memories = [ - ("Python is a programming language", "concept"), - ("Django is a Python web framework", "framework"), - ("Flask is another Python web framework", "framework"), - ("Machine learning often uses Python", "application"), - ] - - memory_ids = [] - for content, mem_type in memories: - mem = MemoryCreate( - content=content, - memory_type=mem_type, - importance=0.7, - embedding=[0.2] * 384, # Dummy embedding - ) - created = await kuzu.client.create_memory(mem, proj.project_id, "analyst") - memory_ids.append(created.memory_id) - - print("โœ… Created graph of connected memories") - - # Get related memories (KuzuDB specific feature) - if hasattr(kuzu.client, "get_related_memories"): - related = kuzu.client.get_related_memories(memory_ids[0]) - print(f"โœ… Found {len(related)} related memories") - - # Get memory graph - if hasattr(kuzu.client, "get_memory_graph"): - graph = kuzu.client.get_memory_graph(proj.project_id, depth=2) - print(f"โœ… Retrieved graph with {len(graph.get('nodes', []))} nodes") - - await kuzu.close() - - except ImportError: - print("โŒ KuzuDB not installed. Install with: pip install kuzu") - - -async def example_local(): - """Example using local storage for development.""" - print("\n๐Ÿ“ Local Storage Example - Simple Development") - print("-" * 40) - - # Access local backend using context manager - async with memory.use("local", config={"enable_markdown": False}) as local: - # Create a project - project = ProjectCreate( - name="Dev Notes", description="Quick notes during development" - ) - proj = await local.client.create_project(project, user_id="developer") - - # Store simple memories - notes = [ - "TODO: Refactor the authentication module", - "BUG: Memory leak in background worker", - "IDEA: Add caching layer for API responses", - ] - - for note in notes: - mem = MemoryCreate(content=note, memory_type="note", importance=0.5) - await local.client.create_memory(mem, proj.project_id, "developer") - - print("โœ… Stored development notes locally") - - # Get recent memories - recent = await local.client.get_recent_memories( - project_id=proj.project_id, user_id="developer", limit=5 - ) - print(f"โœ… Retrieved {len(recent)} recent notes") - - print("โœ… Local backend closed automatically (context manager)") - - -async def example_backend_comparison(): - """Compare different backends for the same use case.""" - print("\nโš–๏ธ Backend Comparison") - print("-" * 40) - - # List all available backends - backends = memory.backends() - print(f"Available backends: {list(backends.keys())}") - - for name, info in backends.items(): - print(f"\n{name.upper()}:") - print(f" {info['description']}") - print(f" Capabilities: {', '.join(info['capabilities'][:3])}...") - - -async def example_advanced_patterns(): - """Advanced usage patterns.""" - print("\n๐ŸŽฏ Advanced Patterns") - print("-" * 40) - - # Pattern 1: Backend selection based on capability - print("\n1. Capability-based selection:") - backends = memory.backends() - - # Find backend with vector search - vector_backends = [ - name - for name, info in backends.items() - if "vector_search" in info["capabilities"] - ] - print(f" Vector search backends: {vector_backends}") - - # Find backend with graph queries - graph_backends = [ - name - for name, info in backends.items() - if "graph_queries" in info["capabilities"] - ] - print(f" Graph query backends: {graph_backends}") - - # Pattern 2: Multi-backend usage - print("\n2. Using multiple backends:") - - # Use LanceDB for vectors, local for quick notes - lance = memory["lancedb"] - local = memory["local"] - - print(" โœ… Can use multiple backends simultaneously") - - # Pattern 3: Dynamic backend switching - print("\n3. Dynamic backend switching:") - - backend_name = "lancedb" if "lancedb" in backends else "local" - selected = memory[backend_name] - print(f" โœ… Dynamically selected: {backend_name}") - - -async def main(): - """Run all examples.""" - print("=" * 60) - print("MEMORY BACKEND USAGE EXAMPLES") - print("=" * 60) - - # Run examples - await example_lancedb() - await example_local() - await example_kuzudb() - await example_backend_comparison() - await example_advanced_patterns() - - print("\n" + "=" * 60) - print("โœ… Examples completed!") - print("\n๐Ÿ“š Quick Reference:") - print(" memory['lancedb'] - Vector search, embeddings") - print(" memory['kuzudb'] - Graph relationships") - print(" memory['infinity'] - High performance") - print(" memory.local - Simple file storage") - print("\n async with memory.use('backend') as mem:") - print(" # Auto initialize and close") - print("=" * 60) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-memory/pyproject.toml b/pkg/hanzo-memory/pyproject.toml deleted file mode 100644 index 6bb5cc13d..000000000 --- a/pkg/hanzo-memory/pyproject.toml +++ /dev/null @@ -1,156 +0,0 @@ -[build-system] -requires = ["setuptools>=68.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-memory" -version = "1.0.1" -description = "AI memory service with FastAPI and MCP support" -readme = "README.md" -requires-python = ">=3.12" -license = {text = "BSD"} -authors = [ - {name = "Hanzo Industries Inc.", email = "dev@hanzo.ai"} -] -keywords = ["ai", "memory", "mcp", "fastapi", "embeddings"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: BSD License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.12", -] - -dependencies = [ - "pydantic>=2.9.0", - "pydantic-settings>=2.6.0", - "httpx>=0.28.0", - "python-multipart>=0.0.12", - "mcp>=1.2.0", - "structlog>=24.4.0", - "rich>=13.7.1", - "numpy>=1.26.0", - "sqlite-vec>=0.1.0", # SQLite with vector search capabilities - "blake3>=0.4.0", # Wallet-style content-addressable ids (byte-equivalent across all 5 brain runtimes) -] - -[project.optional-dependencies] -dev = [ - "pytest>=8.3.0", - "pytest-asyncio>=0.24.0", - "pytest-cov>=6.0.0", - "ruff>=0.8.0", - "mypy>=1.13.0", - "black>=24.10.0", - "pre-commit>=4.0.0", - "ipython>=8.29.0", - "ipdb>=0.13.0", - "twine>=4.0.0", -] - -test = [ - "pytest>=8.3.0", - "pytest-asyncio>=0.24.0", - "pytest-cov>=6.0.0", - "pytest-mock>=3.14.0", - "faker>=30.0.0", - "factory-boy>=3.3.0", - "respx>=0.21.0", - "polars>=1.0.0", - "httpx>=0.27.0", -] - -docs = [ - "mkdocs>=1.6.0", - "mkdocs-material>=9.5.0", - "mkdocstrings[python]>=0.27.0", -] - -[project.scripts] -hanzo-memory = "hanzo_memory.cli:main" -hanzo-memory-server = "hanzo_memory.server:run" -hanzo-memory-mcp = "hanzo_memory.mcp.server:main" - -[project.urls] -Homepage = "https://github.com/hanzoai/memory" -Documentation = "https://docs.hanzo.ai/memory" -Repository = "https://github.com/hanzoai/memory" -Issues = "https://github.com/hanzoai/memory/issues" - -[tool.setuptools.packages.find] -where = ["src"] -include = ["hanzo_memory*"] - -[tool.setuptools.package-data] -hanzo_memory = ["py.typed"] - -[tool.ruff] -line-length = 88 -target-version = "py310" - -[tool.ruff.lint] -select = [ - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # pyflakes - "I", # isort - "B", # flake8-bugbear - "C4", # flake8-comprehensions - "UP", # pyupgrade -] -ignore = [ - "E501", # line too long - "B008", # do not perform function calls in argument defaults - "W191", # indentation contains tabs -] - -[tool.ruff.format] -quote-style = "double" -indent-style = "space" -skip-magic-trailing-comma = false -line-ending = "auto" - -[tool.mypy] -python_version = "3.10" -warn_return_any = true -warn_unused_configs = true -disallow_untyped_defs = true -disallow_incomplete_defs = true -check_untyped_defs = true -disallow_untyped_decorators = true -no_implicit_optional = true -warn_redundant_casts = true -warn_unused_ignores = true -warn_no_return = true -warn_unreachable = true -strict_equality = true -ignore_missing_imports = true - - -[tool.pytest.ini_options] -minversion = "8.0" -addopts = [ - "-ra", - "--strict-markers", -] -testpaths = ["tests"] -python_files = ["test_*.py", "*_test.py"] -asyncio_mode = "auto" - -[tool.coverage.run] -source = ["src/hanzo_memory"] -omit = ["*/tests/*", "*/test_*.py"] - -[tool.coverage.report] -exclude_lines = [ - "pragma: no cover", - "def __repr__", - "if self.debug:", - "if settings.DEBUG", - "raise AssertionError", - "raise NotImplementedError", - "if 0:", - "if __name__ == .__main__.:", - "class .*\\bProtocol\\):", - "@(abc\\.)?abstractmethod", -] \ No newline at end of file diff --git a/pkg/hanzo-memory/src/hanzo_memory/__init__.py b/pkg/hanzo-memory/src/hanzo_memory/__init__.py deleted file mode 100644 index 0a2013858..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Hanzo Memory Service - AI memory and knowledge management.""" - -__version__ = "1.0.1" -__author__ = "Hanzo Industries Inc." -__email__ = "dev@hanzo.ai" - -# Import models - these are always needed -from .models.knowledge import Fact, FactCreate, KnowledgeBase -from .models.memory import Memory, MemoryCreate, MemoryResponse -from .models.project import Project, ProjectCreate - -# Import database factory for getting the configured client -from .db.factory import get_db_client - -__all__ = [ - "get_db_client", - "Memory", - "MemoryCreate", - "MemoryResponse", - "KnowledgeBase", - "Fact", - "FactCreate", - "Project", - "ProjectCreate", -] diff --git a/pkg/hanzo-memory/src/hanzo_memory/algorithms.py b/pkg/hanzo-memory/src/hanzo_memory/algorithms.py deleted file mode 100644 index fa4601584..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/algorithms.py +++ /dev/null @@ -1,1121 +0,0 @@ -"""Hanzo Brain pure-CPU algorithms โ€” Python port. - -Mirrors the TypeScript canonical surface in `@hanzo/bot-memory`: - - - Fusion: RRF, RSF, adaptive RRF k, adaptive weights - - Rerank: MMR - - Dedup: chunk-aware deduplication - - Script: Unicode script detection - - Embed: model registry + Matryoshka truncation - - Temporal: UUIDv7 floor/ceiling + named ranges - - Captions: WebVTT / SRT / RTTM - - FTS: CJK bigrams + emoji trigrams + websearch_to_tsquery - - Tokenizer: BPE estimator - - Eval: MRR / recall / precision / NDCG - - Spatial: haversine + bbox - - Range: HTTP Range header - - Address: wallet-style content-addressable id - - Captions: VTT/SRT/RTTM - -Mirrors the algorithm surface in `hanzoai/brain` (Go) and `@hanzo/bot-memory` (TS). -""" - -from __future__ import annotations - -import hashlib -import math -import os -import re -import unicodedata -from dataclasses import dataclass, field -from datetime import datetime, timezone -from typing import Callable, Iterable - -# โ”€โ”€ Fusion โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -RRF_K_DEFAULT = 20 - - -@dataclass -class SearchHit: - slug: str - score: float - excerpt: str = "" - source: str = "keyword" - - -def rrf_fuse(lists: list[list[SearchHit]], limit: int, k: float = RRF_K_DEFAULT) -> list[SearchHit]: - scores: dict[str, float] = {} - meta: dict[str, SearchHit] = {} - num = len(lists) - for lst in lists: - for rank, hit in enumerate(lst): - scores[hit.slug] = scores.get(hit.slug, 0.0) + 1.0 / (k + rank + 1) - meta.setdefault(hit.slug, hit) - if not scores: - return [] - max_possible = num / (k + 1) - out: list[SearchHit] = [] - for slug, s in scores.items(): - m = meta[slug] - out.append(SearchHit(slug=slug, score=min(s / max_possible, 1.0) if max_possible > 0 else 0.0, excerpt=m.excerpt, source="fused")) - out.sort(key=lambda h: h.score, reverse=True) - return out[:limit] - - -def rsf_fuse(lists: list[list[SearchHit]], limit: int, weights: list[float] | None = None) -> list[SearchHit]: - n = len(lists) - w = weights if weights is not None else [1 / n if n else 0] * n - if len(w) != n: - raise ValueError("rsf_fuse: weights length must match lists length") - scores: dict[str, float] = {} - meta: dict[str, SearchHit] = {} - for i, lst in enumerate(lists): - if not lst: - continue - lo = min(h.score for h in lst) - hi = max(h.score for h in lst) - span = hi - lo - for h in lst: - norm = (h.score - lo) / span if span > 0 else 1.0 - scores[h.slug] = scores.get(h.slug, 0.0) + w[i] * norm - meta.setdefault(h.slug, h) - out = [SearchHit(slug=s, score=v, excerpt=meta[s].excerpt, source="fused") for s, v in scores.items()] - out.sort(key=lambda h: h.score, reverse=True) - return out[:limit] - - -@dataclass -class QueryCharacteristics: - token_count: int - is_phrase: bool - is_boolean: bool - - -def characterize(query: str) -> QueryCharacteristics: - t = query.strip() - is_phrase = bool(re.fullmatch(r'"[^"]+"|\'[^\']+\'', t)) - is_boolean = bool(re.search(r"\b(AND|OR|NOT)\b", t)) or bool(re.search(r"\s-\S", t)) - tokens = [tok for tok in re.split(r"\s+", t) if tok] - return QueryCharacteristics(token_count=len(tokens), is_phrase=is_phrase, is_boolean=is_boolean) - - -def select_rrf_k(q: QueryCharacteristics) -> int: - if q.is_phrase: - return 10 - if q.is_boolean: - return 15 - if q.token_count <= 2: - return 15 - if q.token_count >= 10: - return 40 - return RRF_K_DEFAULT - - -def select_weights(q: QueryCharacteristics) -> dict[str, float]: - if q.is_phrase: - return {"fts": 0.8, "semantic": 0.2} - if q.is_boolean: - return {"fts": 0.7, "semantic": 0.3} - if q.token_count <= 2: - return {"fts": 0.65, "semantic": 0.35} - if q.token_count >= 10: - return {"fts": 0.3, "semantic": 0.7} - return {"fts": 0.5, "semantic": 0.5} - - -# โ”€โ”€ Rerank (MMR) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def cosine(a: list[float], b: list[float]) -> float: - if len(a) != len(b): - return 0.0 - dot = sum(x * y for x, y in zip(a, b)) - na = math.sqrt(sum(x * x for x in a)) - nb = math.sqrt(sum(y * y for y in b)) - return dot / (na * nb) if na * nb > 0 else 0.0 - - -@dataclass -class MmrInput(SearchHit): - embedding: list[float] | None = None - - -def mmr_rerank(hits: list[MmrInput], lambda_: float = 0.5, limit: int | None = None) -> list[MmrInput]: - limit = limit if limit is not None else len(hits) - embedded = [h for h in hits if h.embedding] - orphans = [h for h in hits if not h.embedding] - selected: list[MmrInput] = [] - cands = list(embedded) - while len(selected) < limit and cands: - best_idx = -1 - best_score = -math.inf - for i, c in enumerate(cands): - rel = c.score - max_sim = 0.0 - for s in selected: - sim = cosine(c.embedding or [], s.embedding or []) - if sim > max_sim: - max_sim = sim - mmr = lambda_ * rel - (1 - lambda_) * max_sim - if mmr > best_score: - best_score = mmr - best_idx = i - if best_idx < 0: - break - selected.append(cands.pop(best_idx)) - for o in orphans: - if len(selected) >= limit: - break - selected.append(o) - return selected - - -# โ”€โ”€ Dedup โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def dedup_hits(hits: list[SearchHit], per_chain: int = 1) -> list[SearchHit]: - buckets: dict[str, list[SearchHit]] = {} - for h in hits: - chain = re.sub(r"(#chunk-\d+|::\d+)$", "", h.slug) - buckets.setdefault(chain, []).append(h) - out: list[SearchHit] = [] - for lst in buckets.values(): - lst.sort(key=lambda h: h.score, reverse=True) - out.extend(lst[:per_chain]) - out.sort(key=lambda h: h.score, reverse=True) - return out - - -# โ”€โ”€ Script detection โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def _is_cjk(cp: int) -> bool: - return ( - 0x4E00 <= cp <= 0x9FFF - or 0x3400 <= cp <= 0x4DBF - or 0x3040 <= cp <= 0x30FF - or 0xAC00 <= cp <= 0xD7AF - ) - - -def _is_emoji(cp: int) -> bool: - return 0x2600 <= cp <= 0x27BF or 0x1F300 <= cp <= 0x1FAFF - - -def has_cjk(text: str) -> bool: - return any(_is_cjk(ord(c)) for c in text) - - -def has_emoji(text: str) -> bool: - return any(_is_emoji(ord(c)) for c in text) - - -def detect_script(text: str) -> dict: - counts: dict[str, int] = {k: 0 for k in [ - "latin", "cjk", "emoji", "cyrillic", "arabic", "hebrew", "greek", "devanagari", "other" - ]} - total = 0 - for ch in text: - cp = ord(ch) - if 0x0030 <= cp <= 0x0039: - continue - s = _classify(cp) - if s is None: - continue - counts[s] += 1 - total += 1 - primary = max(counts.items(), key=lambda x: x[1])[0] if total > 0 else "other" - fractions = {k: (v / total) if total > 0 else 0.0 for k, v in counts.items()} - return {"primary": primary, "fractions": fractions, "has_cjk": counts["cjk"] > 0, "has_emoji": counts["emoji"] > 0} - - -def _classify(cp: int) -> str | None: - if _is_cjk(cp): - return "cjk" - if _is_emoji(cp): - return "emoji" - if 0x0041 <= cp <= 0x005A or 0x0061 <= cp <= 0x007A: - return "latin" - if 0x00C0 <= cp <= 0x024F: - return "latin" - if 0x0370 <= cp <= 0x03FF: - return "greek" - if 0x0400 <= cp <= 0x04FF: - return "cyrillic" - if 0x0590 <= cp <= 0x05FF: - return "hebrew" - if 0x0600 <= cp <= 0x06FF: - return "arabic" - if 0x0900 <= cp <= 0x097F: - return "devanagari" - if cp <= 0x002F or 0x003A <= cp <= 0x0040 or 0x005B <= cp <= 0x0060 or 0x007B <= cp <= 0x007E: - return None - return "other" - - -# โ”€โ”€ FTS helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def cjk_bigrams(text: str) -> list[str]: - out: list[str] = [] - cjk_buf = "" - latin_buf = "" - - def flush_cjk() -> None: - nonlocal cjk_buf - if not cjk_buf: - return - if len(cjk_buf) == 1: - out.append(cjk_buf) - else: - for i in range(len(cjk_buf) - 1): - out.append(cjk_buf[i:i + 2]) - cjk_buf = "" - - def flush_latin() -> None: - nonlocal latin_buf - if latin_buf: - out.append(latin_buf) - latin_buf = "" - - for ch in text: - if _is_cjk(ord(ch)): - flush_latin() - cjk_buf += ch - elif ch.isspace(): - flush_cjk() - flush_latin() - else: - flush_cjk() - latin_buf += ch - flush_cjk() - flush_latin() - return [t for t in out if t] - - -def emoji_trigrams(text: str) -> list[str]: - chars = list(text) - out: list[str] = [] - for i, ch in enumerate(chars): - if not _is_emoji(ord(ch)): - continue - a = chars[i] - b = chars[i + 1] if i + 1 < len(chars) else "" - c = chars[i + 2] if i + 2 < len(chars) else "" - out.append(a + b + c) - return out - - -def parse_websearch(query: str) -> dict: - required: list[str] = [] - excluded: list[str] = [] - optional: list[list[str]] = [] - phrases: list[str] = [] - - tokens: list[tuple[str, str]] = [] - for m in re.finditer(r'"([^"]+)"|(\S+)', query): - if m.group(1) is not None: - tokens.append(("phrase", m.group(1))) - else: - tokens.append(("word", m.group(2))) - - i = 0 - while i < len(tokens): - kind, val = tokens[i] - if kind == "phrase": - phrases.append(val) - required.append(val) - i += 1 - continue - if i + 1 < len(tokens) and tokens[i + 1][0] == "word" and tokens[i + 1][1] == "OR": - group = [val] - j = i + 1 - while j + 1 < len(tokens) and tokens[j][1] == "OR" and tokens[j][0] == "word": - group.append(tokens[j + 1][1]) - j += 2 - optional.append(group) - i = j - continue - if val.startswith("-") and len(val) > 1: - excluded.append(val[1:]) - i += 1 - continue - required.append(val) - i += 1 - return {"required": required, "excluded": excluded, "optional": optional, "phrases": phrases} - - -def to_fts5_match(parsed: dict) -> str: - parts: list[str] = [] - for r in parsed["required"]: - parts.append(_quote_fts5(r)) - for group in parsed["optional"]: - parts.append("(" + " OR ".join(_quote_fts5(g) for g in group) + ")") - s = " AND ".join(parts) - for e in parsed["excluded"]: - s += f" NOT {_quote_fts5(e)}" - return s.strip() - - -def _quote_fts5(term: str) -> str: - if " " in term or not re.fullmatch(r"[\wร€-๏ฟฟ]+", term): - return '"' + term.replace('"', '""') + '"' - return term - - -# โ”€โ”€ Embed registry + MRL โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dataclass -class EmbeddingModel: - slug: str - dim: int - mrl_dims: list[int] = field(default_factory=list) - prefix_query: str | None = None - prefix_passage: str | None = None - family: str | None = None - - -_EMBED_REGISTRY: dict[str, EmbeddingModel] = {} - - -def register_embedding_model(m: EmbeddingModel) -> None: - _EMBED_REGISTRY[m.slug] = m - - -def get_embedding_model(slug: str) -> EmbeddingModel | None: - return _EMBED_REGISTRY.get(slug) - - -def list_embedding_models() -> list[EmbeddingModel]: - return list(_EMBED_REGISTRY.values()) - - -register_embedding_model(EmbeddingModel(slug="ollama:nomic-embed-text", dim=768, mrl_dims=[128, 256, 512, 768], family="nomic")) -register_embedding_model(EmbeddingModel(slug="intfloat/e5-large-v2", dim=1024, prefix_query="query: ", prefix_passage="passage: ", family="e5")) -register_embedding_model(EmbeddingModel(slug="openai:text-embedding-3-small", dim=1536, mrl_dims=[256, 512, 768, 1024, 1536], family="openai")) -register_embedding_model(EmbeddingModel(slug="openai:text-embedding-3-large", dim=3072, mrl_dims=[256, 512, 1024, 2048, 3072], family="openai")) - - -def prefix_for(model: EmbeddingModel, task: str, text: str) -> str: - if task == "symmetric" or (model.prefix_query is None and model.prefix_passage is None): - return text - if task == "query": - return (model.prefix_query or "") + text - return (model.prefix_passage or "") + text - - -def l2_normalize(v: list[float]) -> list[float]: - s = math.sqrt(sum(x * x for x in v)) - if s == 0: - return v - return [x / s for x in v] - - -def mrl_truncate(embedding: list[float], target_dim: int) -> list[float]: - if target_dim <= 0: - raise ValueError("target_dim must be positive") - if target_dim >= len(embedding): - return l2_normalize(list(embedding)) - return l2_normalize(embedding[:target_dim]) - - -def coarse_dim(model: EmbeddingModel) -> int: - if not model.mrl_dims: - return model.dim - target = model.dim / 8 - for d in model.mrl_dims: - if d >= target: - return d - return model.mrl_dims[-1] - - -# โ”€โ”€ Temporal (UUIDv7) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def v7_floor(epoch_ms: int) -> str: - return _v7(epoch_ms, ceiling=False) - - -def v7_ceiling(epoch_ms: int) -> str: - return _v7(epoch_ms, ceiling=True) - - -def _v7(epoch_ms: int, ceiling: bool) -> str: - ts = max(0, int(epoch_ms)) - hex_ts = format(ts, "012x")[-12:] - th = hex_ts[:8] - tl = hex_ts[8:12] - if ceiling: - return f"{th}-{tl}-7fff-bfff-ffffffffffff" - return f"{th}-{tl}-7000-8000-000000000000" - - -def range_bounds(from_iso: str | None = None, to_iso: str | None = None) -> dict: - from_ms = int(datetime.fromisoformat(from_iso.replace("Z", "+00:00")).timestamp() * 1000) if from_iso else 0 - to_ms = int(datetime.fromisoformat(to_iso.replace("Z", "+00:00")).timestamp() * 1000) if to_iso else int(datetime.now(timezone.utc).timestamp() * 1000) + 86_400_000 - return {"floor": v7_floor(from_ms), "ceiling": v7_ceiling(to_ms)} - - -# โ”€โ”€ Captions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dataclass -class CaptionSegment: - start_secs: float - end_secs: float - text: str - speaker: str | None = None - - -def render_vtt(segs: list[CaptionSegment]) -> str: - out = "WEBVTT\n\n" - for i, s in enumerate(segs): - out += f"{i + 1}\n" - out += f"{_vtt_time(s.start_secs)} --> {_vtt_time(s.end_secs)}\n" - if s.speaker: - out += f"{s.text}\n\n" - else: - out += f"{s.text}\n\n" - return out - - -def render_srt(segs: list[CaptionSegment]) -> str: - out = "" - for i, s in enumerate(segs): - out += f"{i + 1}\n" - out += f"{_srt_time(s.start_secs)} --> {_srt_time(s.end_secs)}\n" - if s.speaker: - out += f"{s.speaker}: {s.text}\n\n" - else: - out += f"{s.text}\n\n" - return out - - -def render_rttm(segs: list[CaptionSegment], uri: str = "audio") -> str: - lines: list[str] = [] - for s in segs: - if not s.speaker: - continue - dur = s.end_secs - s.start_secs - lines.append(f"SPEAKER {uri} 1 {s.start_secs:.3f} {dur:.3f} {s.speaker} ") - return "\n".join(lines) - - -def _vtt_time(secs: float) -> str: - return _fmt_time(secs, ms_sep=".") - - -def _srt_time(secs: float) -> str: - return _fmt_time(secs, ms_sep=",") - - -def _fmt_time(secs: float, ms_sep: str) -> str: - ms = int((secs - int(secs)) * 1000) - total = int(secs) - h, rem = divmod(total, 3600) - m, s = divmod(rem, 60) - return f"{h:02d}:{m:02d}:{s:02d}{ms_sep}{ms:03d}" - - -# โ”€โ”€ Tokenizer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def estimate_tokens(text: str) -> int: - total = 0 - ascii_run = "" - - def flush_ascii() -> None: - nonlocal ascii_run, total - if not ascii_run: - return - for w in ascii_run.split(): - total += max(1, math.ceil(len(w) / 4)) - ascii_run = "" - - for ch in text: - cp = ord(ch) - if _is_cjk(cp) or _is_emoji(cp): - flush_ascii() - total += 1 - else: - ascii_run += ch - flush_ascii() - return total - - -def truncate_to_tokens(text: str, max_tokens: int) -> str: - if estimate_tokens(text) <= max_tokens: - return text - lo, hi = 0, len(text) - while lo < hi: - mid = (lo + hi + 1) // 2 - if estimate_tokens(text[:mid]) <= max_tokens: - lo = mid - else: - hi = mid - 1 - return text[:lo] - - -# โ”€โ”€ Eval โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dataclass -class QueryEval: - predicted: list[str] - relevant: list[str] | dict[str, int] - - -def _rel_set(q: QueryEval) -> set[str]: - if isinstance(q.relevant, dict): - return {k for k, v in q.relevant.items() if v > 0} - return set(q.relevant) - - -def reciprocal_rank(q: QueryEval) -> float: - rel = _rel_set(q) - for i, p in enumerate(q.predicted): - if p in rel: - return 1.0 / (i + 1) - return 0.0 - - -def mean_reciprocal_rank(queries: list[QueryEval]) -> float: - return sum(reciprocal_rank(q) for q in queries) / len(queries) if queries else 0.0 - - -def recall_at_k(q: QueryEval, k: int) -> float: - rel = _rel_set(q) - if not rel: - return 0.0 - head = q.predicted[:k] - hits = sum(1 for p in head if p in rel) - return hits / len(rel) - - -def precision_at_k(q: QueryEval, k: int) -> float: - rel = _rel_set(q) - head = q.predicted[:k] - if not head: - return 0.0 - hits = sum(1 for p in head if p in rel) - return hits / len(head) - - -def ndcg_at_k(q: QueryEval, k: int) -> float: - grades = q.relevant if isinstance(q.relevant, dict) else {s: 1 for s in q.relevant} - - def dcg(slugs: list[str]) -> float: - s = 0.0 - for i, slug in enumerate(slugs): - g = grades.get(slug, 0) - s += (2 ** g - 1) / math.log2(i + 2) - return s - - ideal = [s for s, _ in sorted(grades.items(), key=lambda kv: kv[1], reverse=True)][:k] - idcg = dcg(ideal) - actual = dcg(q.predicted[:k]) - return actual / idcg if idcg > 0 else 0.0 - - -# โ”€โ”€ Spatial โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -_EARTH = 6371.0088 - - -def haversine_km(a: tuple[float, float], b: tuple[float, float]) -> float: - lat1, lng1 = a - lat2, lng2 = b - d_lat = math.radians(lat2 - lat1) - d_lng = math.radians(lng2 - lng1) - r1 = math.radians(lat1) - r2 = math.radians(lat2) - x = math.sin(d_lat / 2) ** 2 + math.sin(d_lng / 2) ** 2 * math.cos(r1) * math.cos(r2) - return 2 * _EARTH * math.asin(math.sqrt(x)) - - -def bbox_around(center: tuple[float, float], radius_km: float) -> dict: - lat, lng = center - d_lat = radius_km / 111 - d_lng = radius_km / (111 * math.cos(math.radians(lat))) - return {"min_lat": lat - d_lat, "max_lat": lat + d_lat, "min_lng": lng - d_lng, "max_lng": lng + d_lng} - - -def in_box(point: tuple[float, float], box: dict) -> bool: - lat, lng = point - return box["min_lat"] <= lat <= box["max_lat"] and box["min_lng"] <= lng <= box["max_lng"] - - -# โ”€โ”€ HTTP Range โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def parse_range(header: str, total: int) -> tuple[int, int] | str | None: - if not header or not header.startswith("bytes="): - return None - spec = header[6:].split(",")[0].strip() - if not spec: - return None - if spec.startswith("-"): - try: - suffix = int(spec[1:]) - except ValueError: - return None - if suffix <= 0: - return None - return max(0, total - suffix), total - 1 - parts = spec.split("-", 1) - try: - start = int(parts[0]) - end = int(parts[1]) if parts[1] else total - 1 - except (ValueError, IndexError): - return None - if start > end or start >= total: - return "unsatisfiable" - return start, min(end, total - 1) - - -def content_range(start: int, end: int, total: int) -> str: - return f"bytes {start}-{end}/{total}" - - -# โ”€โ”€ Wallet-style address โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -_BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" -_VERSION_V1 = 0x01 - - -def _blake3(data: bytes) -> bytes: - """BLAKE3 โ€” required. Wallet addresses depend on byte-equivalence across - every Hanzo runtime (TS @noble/hashes/blake3, Rust blake3 crate, Go - lukechampine.com/blake3, C++ vendored reference impl). The `blake3` pip - package is the only path here; do not fall back to anything else.""" - try: - from blake3 import blake3 as _blake3_impl # type: ignore[import-not-found] - except ImportError as exc: # pragma: no cover - raise RuntimeError( - "hanzo_memory.algorithms requires the `blake3` package " - "(pip install blake3). Wallet addresses are content-addressable; " - "no other hash is acceptable." - ) from exc - return _blake3_impl(data).digest() - - -def encode_address(public_key: bytes, prefix: str = "hanzo") -> str: - if len(public_key) != 32: - raise ValueError("public key must be 32 bytes") - h = _blake3(public_key)[:20] - versioned = bytes([_VERSION_V1]) + h - checksum = _blake3(versioned)[:4] - return f"{prefix}:{_base58_encode(versioned + checksum)}" - - -def decode_address(address: str) -> dict: - if ":" not in address: - raise ValueError("address: missing prefix") - prefix, body = address.split(":", 1) - decoded = _base58_decode(body) - if len(decoded) != 25: - raise ValueError("address: wrong length") - version = decoded[0] - h = decoded[1:21] - checksum = decoded[21:25] - expected = _blake3(decoded[:21])[:4] - if checksum != expected: - raise ValueError("address: bad checksum") - return {"prefix": prefix, "version": version, "hash": bytes(h)} - - -def _base58_encode(b: bytes) -> str: - if not b: - return "" - zeros = 0 - for c in b: - if c == 0: - zeros += 1 - else: - break - n = int.from_bytes(b, "big") - out = "" - while n > 0: - n, r = divmod(n, 58) - out = _BASE58[r] + out - return _BASE58[0] * zeros + out - - -def _base58_decode(s: str) -> bytes: - if not s: - return b"" - zeros = 0 - for c in s: - if c == _BASE58[0]: - zeros += 1 - else: - break - n = 0 - for c in s: - if c not in _BASE58: - raise ValueError(f"base58: invalid char {c}") - n = n * 58 + _BASE58.index(c) - out = n.to_bytes((n.bit_length() + 7) // 8, "big") if n > 0 else b"" - return b"\x00" * zeros + out - - -# โ”€โ”€ Graph maintenance โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dataclass -class WeightedEdge: - source: str - target: str - weight: float - - -def normalize_edges(edges: list[WeightedEdge]) -> list[WeightedEdge]: - if not edges: - return [] - lo = min(e.weight for e in edges) - hi = max(e.weight for e in edges) - span = hi - lo - return [WeightedEdge(e.source, e.target, ((e.weight - lo) / span) if span > 0 else 1.0) for e in edges] - - -def snn_score(edges: list[WeightedEdge], k: int = 10) -> list[WeightedEdge]: - adj: dict[str, list[WeightedEdge]] = {} - for e in edges: - adj.setdefault(e.source, []).append(e) - adj.setdefault(e.target, []).append(WeightedEdge(e.target, e.source, e.weight)) - nbrs: dict[str, set[str]] = {} - for node, lst in adj.items(): - lst.sort(key=lambda x: x.weight, reverse=True) - nbrs[node] = {x.target for x in lst[:k]} - out: list[WeightedEdge] = [] - for e in edges: - a = nbrs.get(e.source, set()) - b = nbrs.get(e.target, set()) - inter = len(a & b) - union = len(a | b) - out.append(WeightedEdge(e.source, e.target, inter / union if union > 0 else 0.0)) - return out - - -def pfnet_infinity(edges: list[WeightedEdge]) -> list[WeightedEdge]: - adj: dict[str, dict[str, float]] = {} - for e in edges: - adj.setdefault(e.source, {})[e.target] = max(adj.get(e.source, {}).get(e.target, 0), e.weight) - keep: list[WeightedEdge] = [] - for e in edges: - dominated = False - for x, w_ux in adj.get(e.source, {}).items(): - if x == e.target: - continue - w_xv = adj.get(x, {}).get(e.target) - if w_xv is None: - continue - if min(w_ux, w_xv) > e.weight: - dominated = True - break - if not dominated: - keep.append(e) - return keep - - -def louvain(edges: list[WeightedEdge], passes: int = 10) -> dict[str, int]: - nodes: set[str] = set() - for e in edges: - nodes.add(e.source) - nodes.add(e.target) - community = {n: i for i, n in enumerate(nodes)} - adj: dict[str, list[tuple[str, float]]] = {} - total = 0.0 - for e in edges: - adj.setdefault(e.source, []).append((e.target, e.weight)) - adj.setdefault(e.target, []).append((e.source, e.weight)) - total += e.weight - deg = {n: sum(w for _, w in adj.get(n, [])) for n in nodes} - m = total - - for _ in range(passes): - improved = False - for n in nodes: - cur = community[n] - w_to: dict[int, float] = {} - for nb, w in adj.get(n, []): - c = community[nb] - w_to[c] = w_to.get(c, 0) + w - best = cur - best_gain = 0.0 - kn = deg.get(n, 0) - for c, wnc in w_to.items(): - if c == cur: - continue - sigma_tot = sum(deg[o] for o, comm in community.items() if comm == c and o != n) - gain = wnc - (kn * sigma_tot) / max(2 * m, 1e-9) - if gain > best_gain: - best_gain = gain - best = c - if best != cur: - community[n] = best - improved = True - if not improved: - break - - id_map: dict[int, int] = {} - nxt = 0 - for c in community.values(): - if c not in id_map: - id_map[c] = nxt - nxt += 1 - return {n: id_map[c] for n, c in community.items()} - - -# โ”€โ”€ Document type registry โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dataclass -class DocType: - slug: str - label: str - chunking: str - filename_patterns: list[re.Pattern] = field(default_factory=list) - mime_types: list[str] = field(default_factory=list) - content_match: Callable[[str], bool] | None = None - revision_hints: list[str] = field(default_factory=list) - - -_DOCTYPES: dict[str, DocType] = {} - - -def register_doc_type(t: DocType) -> None: - _DOCTYPES[t.slug] = t - - -def get_doc_type(slug: str) -> DocType | None: - return _DOCTYPES.get(slug) - - -def list_doc_types() -> list[DocType]: - return list(_DOCTYPES.values()) - - -def detect_doc_type(filename: str | None = None, mime_type: str | None = None, body: str | None = None) -> DocType: - best: DocType | None = None - best_score = 0 - for t in _DOCTYPES.values(): - score = 0 - if filename: - for p in t.filename_patterns: - if p.search(filename): - score += 2 - if mime_type and mime_type in t.mime_types: - score += 3 - if body and t.content_match and t.content_match(body): - score += 1 - if score > best_score: - best_score = score - best = t - return best or _DOCTYPES["note/plain"] - - -for _t in [ - DocType("note/plain", "Plain note", "paragraph", revision_hints=["Clarity", "Concision"]), - DocType("meeting/notes", "Meeting notes", "semantic", - filename_patterns=[re.compile("meeting", re.I), re.compile("standup", re.I), re.compile("retro", re.I)], - content_match=lambda b: bool(re.search(r"\b(action item|decision|attendees)\b", b, re.I)), - revision_hints=["Decisions", "Action Items", "Attendees", "Next Steps"]), - DocType("research/paper", "Research paper", "semantic", - filename_patterns=[re.compile("paper", re.I), re.compile(r"\.pdf$", re.I)], - revision_hints=["Methodology", "Findings", "Citations"]), - DocType("code/source", "Source code", "syntactic", - filename_patterns=[re.compile(r"\.(rs|go|ts|tsx|js|py|java|c|cpp|h|rb|kt|swift|sql|sh)$", re.I)], - revision_hints=["Purpose", "Inputs", "Outputs"]), - DocType("code/markdown", "Markdown / docs", "semantic", - filename_patterns=[re.compile(r"\.md$", re.I), re.compile("readme", re.I)], - mime_types=["text/markdown"]), - DocType("email/message", "Email", "paragraph", - filename_patterns=[re.compile(r"\.eml$", re.I), re.compile(r"\.msg$", re.I)], - mime_types=["message/rfc822"]), - DocType("spreadsheet/table", "Spreadsheet", "fixed", - filename_patterns=[re.compile(r"\.(xlsx|xls|ods|csv|tsv)$", re.I)]), - DocType("media/audio", "Audio", "fixed", filename_patterns=[re.compile(r"\.(mp3|wav|flac|m4a|opus|ogg)$", re.I)]), - DocType("media/video", "Video", "fixed", filename_patterns=[re.compile(r"\.(mp4|mkv|webm|mov)$", re.I)]), - DocType("media/image", "Image", "fixed", filename_patterns=[re.compile(r"\.(png|jpe?g|webp|gif|tiff?)$", re.I)]), - DocType("media/3d", "3D model", "fixed", filename_patterns=[re.compile(r"\.(glb|gltf|obj|fbx|stl|usdz)$", re.I)]), - DocType("archive/zip", "Archive", "fixed", filename_patterns=[re.compile(r"\.(zip|tar|tar\.gz|tgz|7z)$", re.I)]), -]: - register_doc_type(_t) - - -# โ”€โ”€ Circuit breaker / retry โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -class CircuitOpenError(Exception): - pass - - -class CircuitBreaker: - def __init__(self, failure_threshold: int = 5, cooldown_ms: int = 30_000) -> None: - self.threshold = failure_threshold - self.cooldown = cooldown_ms / 1000.0 - self.failures = 0 - self._opened_at = 0.0 - - def state(self) -> str: - import time - if self.failures < self.threshold: - return "closed" - if time.time() - self._opened_at >= self.cooldown: - return "half-open" - return "open" - - def run(self, fn: Callable[[], object]) -> object: - import time - s = self.state() - if s == "open": - raise CircuitOpenError() - try: - r = fn() - self.failures = 0 - self._opened_at = 0.0 - return r - except Exception: - self.failures += 1 - if self.failures >= self.threshold and self._opened_at == 0: - self._opened_at = time.time() - raise - - -def retry(fn: Callable[[], object], attempts: int = 3, base_ms: int = 100, max_ms: int = 30_000, - is_transient: Callable[[Exception], bool] | None = None, - sleep_fn: Callable[[float], None] | None = None) -> object: - import time - import random - sleep = sleep_fn or (lambda s: time.sleep(s)) - transient = is_transient or (lambda _e: True) - last_err: Exception | None = None - for i in range(attempts): - try: - return fn() - except Exception as e: # noqa: BLE001 - last_err = e - if i == attempts - 1 or not transient(e): - break - delay = min(max_ms, base_ms * (2 ** i)) * random.random() / 1000.0 - sleep(delay) - assert last_err is not None - raise last_err - - -# โ”€โ”€ Inference: provider slug + capabilities โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -KNOWN_PROVIDERS = {"ollama", "openai", "openrouter", "llamacpp", "anthropic", "google", "azure", "groq", "together", "mock"} - - -def parse_slug(slug: str, default_provider: str = "ollama") -> dict: - if ":" not in slug: - return {"provider": default_provider, "model": slug} - head, rest = slug.split(":", 1) - if head in KNOWN_PROVIDERS: - return {"provider": head, "model": rest} - return {"provider": default_provider, "model": slug} - - -def format_slug(p: dict) -> str: - return f"{p['provider']}:{p['model']}" - - -# โ”€โ”€ Runtime config โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -class RuntimeConfig: - def __init__(self, defaults: dict[str, str] | None = None, env: dict[str, str] | None = None) -> None: - self.defaults = defaults or {} - self.env = env if env is not None else dict(os.environ) - self.overrides: dict[str, str] = {} - - def get(self, key: str) -> str | None: - if key in self.overrides: - return self.overrides[key] - if key in self.env: - return self.env[key] - return self.defaults.get(key) - - def source(self, key: str) -> str: - if key in self.overrides: - return "db_override" - if key in self.env: - return "env" - if key in self.defaults: - return "default" - return "absent" - - def set(self, key: str, value: str) -> None: - self.overrides[key] = value - - def clear(self, key: str) -> None: - self.overrides.pop(key, None) - - -# โ”€โ”€ Link-type rule classifier โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -LINK_TYPES = [ - "mentions", "founded", "invested_in", "advises", "works_at", - "attended", "authored", "cites", "succeeded_by", "located_in", "related", -] - -_LINK_RULES: list[tuple[re.Pattern, str]] = [ - (re.compile(r"\bfounded\b", re.I), "founded"), - (re.compile(r"\binvested\s+in\b", re.I), "invested_in"), - (re.compile(r"\badvis(?:or|es|ing)\b", re.I), "advises"), - (re.compile(r"\bworks?\s+(?:at|for)\b", re.I), "works_at"), - (re.compile(r"\battended\b", re.I), "attended"), - (re.compile(r"\b(?:wrote|authored)\b", re.I), "authored"), - (re.compile(r"\bcites?\b", re.I), "cites"), - (re.compile(r"\bsucceeded\s+by\b", re.I), "succeeded_by"), - (re.compile(r"\blocated\s+in\b", re.I), "located_in"), -] - - -def classify_link_rule(evidence: str) -> str: - for pat, t in _LINK_RULES: - if pat.search(evidence): - return t - return "mentions" - - -__all__ = [ - # Fusion - "RRF_K_DEFAULT", "SearchHit", "rrf_fuse", "rsf_fuse", "QueryCharacteristics", - "characterize", "select_rrf_k", "select_weights", - # Rerank / dedup - "cosine", "MmrInput", "mmr_rerank", "dedup_hits", - # Script / FTS - "detect_script", "has_cjk", "has_emoji", "cjk_bigrams", "emoji_trigrams", - "parse_websearch", "to_fts5_match", - # Embed - "EmbeddingModel", "register_embedding_model", "get_embedding_model", "list_embedding_models", - "prefix_for", "l2_normalize", "mrl_truncate", "coarse_dim", - # Temporal - "v7_floor", "v7_ceiling", "range_bounds", - # Captions - "CaptionSegment", "render_vtt", "render_srt", "render_rttm", - # Tokenizer - "estimate_tokens", "truncate_to_tokens", - # Eval - "QueryEval", "reciprocal_rank", "mean_reciprocal_rank", "recall_at_k", "precision_at_k", "ndcg_at_k", - # Spatial / Range - "haversine_km", "bbox_around", "in_box", "parse_range", "content_range", - # Address - "encode_address", "decode_address", - # Graph - "WeightedEdge", "normalize_edges", "snn_score", "pfnet_infinity", "louvain", - # Doc types - "DocType", "register_doc_type", "get_doc_type", "list_doc_types", "detect_doc_type", - # Resilience - "CircuitBreaker", "CircuitOpenError", "retry", - # Inference - "KNOWN_PROVIDERS", "parse_slug", "format_slug", "RuntimeConfig", - # Link types - "LINK_TYPES", "classify_link_rule", -] diff --git a/pkg/hanzo-memory/src/hanzo_memory/api/__init__.py b/pkg/hanzo-memory/src/hanzo_memory/api/__init__.py deleted file mode 100644 index 4be86b4d6..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/api/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""API package.""" - -from .auth import get_api_key, require_auth, security, verify_api_key - -__all__ = ["get_api_key", "require_auth", "security", "verify_api_key"] diff --git a/pkg/hanzo-memory/src/hanzo_memory/api/auth.py b/pkg/hanzo-memory/src/hanzo_memory/api/auth.py deleted file mode 100644 index 4804e50ae..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/api/auth.py +++ /dev/null @@ -1,132 +0,0 @@ -"""Authentication middleware and utilities.""" - -from fastapi import HTTPException, Request, status -from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer - -from ..config import settings - -security = HTTPBearer(auto_error=False) - - -def get_api_key( - request: Request, - credentials: HTTPAuthorizationCredentials | None = None, -) -> str | None: - """ - Extract API key from request. - - Checks in order: - 1. Authorization header (Bearer token) - 2. x-api-key header - 3. x-api-key header - 4. apikey in JSON body - - Args: - request: FastAPI request - credentials: Optional bearer credentials - - Returns: - API key if found, None otherwise - """ - # Check Bearer token - if credentials and credentials.credentials: - return credentials.credentials - - # Check custom headers - api_key = request.headers.get("x-api-key") - if api_key: - return api_key - - api_key = request.headers.get("x-api-key") - if api_key: - return api_key - - # Check JSON body (for backwards compatibility) - # This is handled in the request models - - return None - - -def verify_api_key(api_key: str | None) -> bool: - """ - Verify API key. - - Args: - api_key: API key to verify - - Returns: - True if valid, False otherwise - """ - if settings.disable_auth: - return True - - if not api_key: - return False - - # Compare with configured API key - return api_key == settings.api_key - - -def require_auth( - request: Request, - credentials: HTTPAuthorizationCredentials | None = None, -) -> str: - """ - Require authentication for a request. - - Args: - request: FastAPI request - credentials: Optional bearer credentials - - Returns: - API key if authenticated - - Raises: - HTTPException: If not authenticated - """ - api_key = get_api_key(request, credentials) - - # Check if auth is disabled - if settings.disable_auth: - return api_key or "disabled" - - # Verify API key - if not verify_api_key(api_key): - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Invalid or missing API key", - headers={"WWW-Authenticate": "Bearer"}, - ) - - return api_key or "" # Return empty string if None - - -async def get_or_verify_user_id( - user_id: str, - credentials: HTTPAuthorizationCredentials | None, - request: Request, -) -> str: - """ - Get or verify user ID from request. - - In a production system, this would validate that the authenticated - user has access to the requested user_id. For now, we just verify - authentication and return the user_id. - - Args: - user_id: Requested user ID - credentials: Optional bearer credentials - request: FastAPI request - - Returns: - Verified user ID - - Raises: - HTTPException: If not authenticated or unauthorized - """ - # Require authentication - require_auth(request, credentials) - - # In a real system, we would check if the API key owner - # has access to this user_id. For now, just return it. - return user_id diff --git a/pkg/hanzo-memory/src/hanzo_memory/cli.py b/pkg/hanzo-memory/src/hanzo_memory/cli.py deleted file mode 100644 index 078abbd1f..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/cli.py +++ /dev/null @@ -1,47 +0,0 @@ -"""CLI entry point for Hanzo Memory Service.""" - -import click -from rich.console import Console - -from .config import settings -from .server import run as run_server - -console = Console() - - -@click.group() -@click.version_option(version="0.1.0", prog_name="hanzo-memory") -def cli() -> None: - """Hanzo Memory Service - AI memory and knowledge management.""" - pass - - -@cli.command() -@click.option("--host", default="0.0.0.0", help="Server host") -@click.option("--port", default=4000, type=int, help="Server port") -def server(host: str, port: int) -> None: - """Run the FastAPI server.""" - console.print(f"[green]Starting Hanzo Memory Service on {host}:{port}[/green]") - settings.host = host - settings.port = port - run_server() - - -@cli.command() -def info() -> None: - """Show service information.""" - console.print("[bold]Hanzo Memory Service[/bold]") - console.print("Version: 0.1.0") - console.print(f"Database: {settings.infinity_db_path}") - console.print(f"Embedding Model: {settings.embedding_model}") - console.print(f"LLM Model: {settings.llm_model}") - console.print(f"Auth Disabled: {settings.disable_auth}") - - -def main() -> None: - """Main entry point.""" - cli() - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-memory/src/hanzo_memory/config.py b/pkg/hanzo-memory/src/hanzo_memory/config.py deleted file mode 100644 index 1c8e8bf56..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/config.py +++ /dev/null @@ -1,129 +0,0 @@ -"""Configuration settings for Hanzo Memory Service.""" - -from pathlib import Path - -from pydantic import Field -from pydantic_settings import BaseSettings, SettingsConfigDict - - -class Settings(BaseSettings): - """Application settings.""" - - model_config = SettingsConfigDict( - env_file=".env", - env_file_encoding="utf-8", - env_prefix="HANZO_", - case_sensitive=False, - ) - - # API Settings - api_key: str | None = Field(None, description="Hanzo API key for authentication") - disable_auth: bool = Field( - False, description="Disable authentication for local development" - ) - - # Server Settings - host: str = Field("0.0.0.0", description="Server host") - port: int = Field(4000, description="Server port") - - # Database Backend Settings - db_backend: str = Field( - "sqlite", # SQLite-based storage by default - description="Database backend to use (sqlite, local, lancedb, infinity)", - ) - - # InfinityDB Settings - infinity_db_path: Path = Field( - Path("data/infinity_db"), description="Path to InfinityDB data directory" - ) - - # LanceDB Settings - lancedb_path: Path = Field( - Path("data/lancedb"), description="Path to LanceDB data directory" - ) - - # LLM Settings (LLM compatible) - llm_model: str = Field( - "gpt-4o-mini", description="LLM model to use (LLM format)" - ) - llm_api_base: str | None = Field(None, description="API base URL for local models") - llm_api_key: str | None = Field(None, description="API key for LLM provider") - llm_temperature: float = Field(0.7, description="Default temperature for LLM") - llm_max_tokens: int = Field(1000, description="Default max tokens for LLM") - - # Legacy API keys (for backwards compatibility) - openai_api_key: str | None = Field(None, description="OpenAI API key") - anthropic_api_key: str | None = Field(None, description="Anthropic API key") - - # Embedding Settings - embedding_model: str = Field( - "BAAI/bge-small-en-v1.5", description="FastEmbed model to use" - ) - embedding_dimensions: int = Field(384, description="Embedding vector dimensions") - - # Memory Settings - max_memories_per_user: int = Field(10000, description="Maximum memories per user") - memory_retrieval_limit: int = Field( - 50, description="Default memory retrieval limit" - ) - - # Knowledge Base Settings - max_knowledge_bases_per_user: int = Field( - 100, description="Maximum knowledge bases per user" - ) - max_facts_per_base: int = Field( - 100000, description="Maximum facts per knowledge base" - ) - - # Cache Settings - redis_url: str | None = Field(None, description="Redis URL for caching") - cache_ttl: int = Field(3600, description="Cache TTL in seconds") - - # Logging Settings - log_level: str = Field("INFO", description="Logging level") - log_format: str = Field("json", description="Log format (json or text)") - - # MCP Settings - mcp_server_name: str = Field("hanzo-memory", description="MCP server name") - mcp_server_version: str = Field("1.0.0", description="MCP server version") - - @property - def infinity_db_str(self) -> str: - """Get InfinityDB path as string.""" - return str(self.infinity_db_path.absolute()) - - def ensure_paths(self) -> None: - """Ensure required paths exist.""" - self.infinity_db_path.mkdir(parents=True, exist_ok=True) - self.lancedb_path.mkdir(parents=True, exist_ok=True) - - -# Global settings instance -settings = Settings( - api_key=None, - disable_auth=False, - host="0.0.0.0", - port=4000, - db_backend="sqlite", - infinity_db_path=Path("data/infinity_db"), - lancedb_path=Path("data/lancedb"), - llm_model="gpt-4o-mini", - llm_api_base=None, - llm_api_key=None, - llm_temperature=0.7, - llm_max_tokens=1000, - openai_api_key=None, - anthropic_api_key=None, - embedding_model="BAAI/bge-small-en-v1.5", - embedding_dimensions=384, - max_memories_per_user=10000, - memory_retrieval_limit=50, - max_knowledge_bases_per_user=100, - max_facts_per_base=100000, - redis_url=None, - cache_ttl=3600, - log_level="INFO", - log_format="json", - mcp_server_name="hanzo-memory", - mcp_server_version="1.0.0", -) diff --git a/pkg/hanzo-memory/src/hanzo_memory/db/__init__.py b/pkg/hanzo-memory/src/hanzo_memory/db/__init__.py deleted file mode 100644 index c17368d64..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/db/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Database package.""" - -from .base import BaseVectorDB -from .client import InfinityClient, get_client -from .factory import get_db_client, reset_db_client - -__all__ = [ - "BaseVectorDB", - "InfinityClient", - "get_db_client", - "reset_db_client", - "get_client", -] diff --git a/pkg/hanzo-memory/src/hanzo_memory/db/backends/__init__.py b/pkg/hanzo-memory/src/hanzo_memory/db/backends/__init__.py deleted file mode 100644 index 739ed2625..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/db/backends/__init__.py +++ /dev/null @@ -1,355 +0,0 @@ -"""Database backend interfaces and implementations.""" - -from abc import ABC, abstractmethod -from enum import Enum -from typing import Any, Dict, List, Optional, Protocol - - -class DatabaseType(Enum): - """Types of database backends.""" - - VECTOR = ( - "vector" # For embeddings and similarity search (LanceDB, Pinecone, Weaviate) - ) - RELATIONAL = "relational" # For structured data (PostgreSQL, MySQL, SQLite) - DOCUMENT = "document" # For JSON documents (MongoDB, CouchDB) - GRAPH = "graph" # For relationships (Neo4j, ArangoDB) - KEY_VALUE = "key_value" # For simple storage (Redis, RocksDB) - TIME_SERIES = "time_series" # For temporal data (InfluxDB, TimescaleDB) - SEARCH = "search" # For full-text search (Elasticsearch, MeiliSearch) - FILE = "file" # For file-based storage (JSON, CSV, Parquet) - - -class VectorDatabase(Protocol): - """Interface for vector databases.""" - - async def upsert_vectors( - self, - vectors: List[List[float]], - ids: List[str], - metadata: Optional[List[Dict[str, Any]]] = None, - ) -> None: - """Insert or update vectors with metadata.""" - ... - - async def search( - self, - query_vector: List[float], - limit: int = 10, - filter: Optional[Dict[str, Any]] = None, - ) -> List[Dict[str, Any]]: - """Search for similar vectors.""" - ... - - async def delete_vectors(self, ids: List[str]) -> None: - """Delete vectors by ID.""" - ... - - async def get_vectors(self, ids: List[str]) -> List[Dict[str, Any]]: - """Retrieve vectors by ID.""" - ... - - -class RelationalDatabase(Protocol): - """Interface for relational databases.""" - - async def execute( - self, - query: str, - params: Optional[Dict[str, Any]] = None, - ) -> List[Dict[str, Any]]: - """Execute a SQL query.""" - ... - - async def insert( - self, - table: str, - data: Dict[str, Any], - ) -> str: - """Insert a record and return ID.""" - ... - - async def update( - self, - table: str, - id: str, - data: Dict[str, Any], - ) -> bool: - """Update a record.""" - ... - - async def delete( - self, - table: str, - id: str, - ) -> bool: - """Delete a record.""" - ... - - async def select( - self, - table: str, - filter: Optional[Dict[str, Any]] = None, - limit: Optional[int] = None, - ) -> List[Dict[str, Any]]: - """Select records from table.""" - ... - - async def create_table( - self, - table: str, - schema: Dict[str, str], - ) -> None: - """Create a table with schema.""" - ... - - -class DocumentDatabase(Protocol): - """Interface for document databases.""" - - async def insert_document( - self, - collection: str, - document: Dict[str, Any], - ) -> str: - """Insert a document and return ID.""" - ... - - async def find_documents( - self, - collection: str, - filter: Dict[str, Any], - limit: Optional[int] = None, - ) -> List[Dict[str, Any]]: - """Find documents matching filter.""" - ... - - async def update_document( - self, - collection: str, - id: str, - updates: Dict[str, Any], - ) -> bool: - """Update a document.""" - ... - - async def delete_document( - self, - collection: str, - id: str, - ) -> bool: - """Delete a document.""" - ... - - async def create_index( - self, - collection: str, - fields: List[str], - ) -> None: - """Create an index on fields.""" - ... - - -class GraphDatabase(Protocol): - """Interface for graph databases.""" - - async def add_node( - self, - id: str, - labels: List[str], - properties: Dict[str, Any], - ) -> None: - """Add a node to the graph.""" - ... - - async def add_edge( - self, - from_id: str, - to_id: str, - relationship: str, - properties: Optional[Dict[str, Any]] = None, - ) -> None: - """Add an edge between nodes.""" - ... - - async def find_neighbors( - self, - node_id: str, - relationship: Optional[str] = None, - depth: int = 1, - ) -> List[Dict[str, Any]]: - """Find neighboring nodes.""" - ... - - async def shortest_path( - self, - from_id: str, - to_id: str, - ) -> Optional[List[str]]: - """Find shortest path between nodes.""" - ... - - -class KeyValueDatabase(Protocol): - """Interface for key-value databases.""" - - async def get(self, key: str) -> Optional[Any]: - """Get value by key.""" - ... - - async def set( - self, - key: str, - value: Any, - ttl: Optional[int] = None, - ) -> None: - """Set key-value with optional TTL.""" - ... - - async def delete(self, key: str) -> bool: - """Delete a key.""" - ... - - async def exists(self, key: str) -> bool: - """Check if key exists.""" - ... - - async def keys(self, pattern: str = "*") -> List[str]: - """List keys matching pattern.""" - ... - - -class SearchDatabase(Protocol): - """Interface for search databases.""" - - async def index_document( - self, - index: str, - id: str, - document: Dict[str, Any], - ) -> None: - """Index a document for search.""" - ... - - async def search( - self, - index: str, - query: str, - limit: int = 10, - filters: Optional[Dict[str, Any]] = None, - ) -> List[Dict[str, Any]]: - """Full-text search.""" - ... - - async def delete_document( - self, - index: str, - id: str, - ) -> bool: - """Delete a document from index.""" - ... - - async def create_index( - self, - index: str, - mappings: Dict[str, Any], - ) -> None: - """Create a search index.""" - ... - - -class TimeSeriesDatabase(Protocol): - """Interface for time-series databases.""" - - async def write_point( - self, - measurement: str, - tags: Dict[str, str], - fields: Dict[str, Any], - timestamp: Optional[int] = None, - ) -> None: - """Write a time-series data point.""" - ... - - async def query( - self, - measurement: str, - start_time: int, - end_time: int, - aggregation: Optional[str] = None, - group_by: Optional[List[str]] = None, - ) -> List[Dict[str, Any]]: - """Query time-series data.""" - ... - - async def delete_series( - self, - measurement: str, - tags: Optional[Dict[str, str]] = None, - ) -> bool: - """Delete a time series.""" - ... - - -class FileDatabase(Protocol): - """Interface for file-based storage.""" - - async def read_file(self, path: str) -> Dict[str, Any]: - """Read data from file.""" - ... - - async def write_file( - self, - path: str, - data: Dict[str, Any], - ) -> None: - """Write data to file.""" - ... - - async def append_file( - self, - path: str, - data: Any, - ) -> None: - """Append data to file.""" - ... - - async def delete_file(self, path: str) -> bool: - """Delete a file.""" - ... - - async def list_files( - self, - pattern: str = "*", - ) -> List[str]: - """List files matching pattern.""" - ... - - -class DatabaseBackend(ABC): - """Base class for all database backends.""" - - def __init__(self, config: Dict[str, Any]): - """Initialize with configuration.""" - self.config = config - self.db_type: DatabaseType = DatabaseType.FILE - self.capabilities: List[str] = [] - - @abstractmethod - async def connect(self) -> None: - """Connect to the database.""" - pass - - @abstractmethod - async def disconnect(self) -> None: - """Disconnect from the database.""" - pass - - @abstractmethod - async def health_check(self) -> bool: - """Check if database is healthy.""" - pass - - def supports(self, capability: str) -> bool: - """Check if backend supports a capability.""" - return capability in self.capabilities diff --git a/pkg/hanzo-memory/src/hanzo_memory/db/backends/backend_registry.py b/pkg/hanzo-memory/src/hanzo_memory/db/backends/backend_registry.py deleted file mode 100644 index 1c741c14d..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/db/backends/backend_registry.py +++ /dev/null @@ -1,300 +0,0 @@ -"""Backend registry for memory storage. - -This provides a simple way to register and use different backends. -Supports multiple database types: -- LanceDB: Vector database with SQL-like queries -- KuzuDB: Graph database for relationship-based queries -- InfinityDB: High-performance vector database -- Local files: Simple JSON storage for development -""" - -from typing import Dict, Optional, Type -from structlog import get_logger - -from ..base import BaseVectorDB -from ..local_client import LocalMemoryClient - -logger = get_logger() - -# Try to import optional backends -try: - from ..lancedb_client import LanceDBClient - - LANCEDB_AVAILABLE = True -except ImportError: - LANCEDB_AVAILABLE = False - logger.debug("LanceDB client not available") - -try: - from ..kuzudb_client import KuzuDBClient - - KUZU_AVAILABLE = True -except ImportError: - KUZU_AVAILABLE = False - logger.debug("KuzuDB client not available") - -try: - from ..client import InfinityClient - - INFINITY_AVAILABLE = True -except ImportError: - INFINITY_AVAILABLE = False - logger.debug("InfinityDB client not available") - -try: - from ..sqlite_client import SQLiteMemoryClient - - SQLITE_AVAILABLE = True -except ImportError: - SQLITE_AVAILABLE = False - logger.debug("SQLite client not available") - - -class BackendCapability: - """Capabilities that backends can support.""" - - VECTOR_SEARCH = "vector_search" - FULL_TEXT_SEARCH = "full_text_search" - STRUCTURED_QUERY = "structured_query" - MARKDOWN_IMPORT = "markdown_import" - PERSISTENCE = "persistence" - EMBEDDINGS = "embeddings" - GRAPH_QUERIES = "graph_queries" - TIME_SERIES = "time_series" - - -class BackendRegistry: - """Registry for memory backends.""" - - # Registered backends and their capabilities - BACKENDS: Dict[str, Dict] = { - "local": { - "class": LocalMemoryClient, - "description": "Local file storage using JSON files", - "capabilities": [ - BackendCapability.PERSISTENCE, - BackendCapability.MARKDOWN_IMPORT, - BackendCapability.FULL_TEXT_SEARCH, - ], - "config": { - "enable_markdown": True, - }, - }, - } - - # Add optional backends if available - @classmethod - def _init_optional_backends(cls): - """Initialize optional backends if they're available.""" - if LANCEDB_AVAILABLE and "lancedb" not in cls.BACKENDS: - cls.BACKENDS["lancedb"] = { - "class": LanceDBClient, - "description": "LanceDB - Embedded vector database with SQL-like queries", - "capabilities": [ - BackendCapability.VECTOR_SEARCH, - BackendCapability.STRUCTURED_QUERY, - BackendCapability.PERSISTENCE, - BackendCapability.EMBEDDINGS, - BackendCapability.MARKDOWN_IMPORT, - ], - "config": { - "enable_markdown": True, - }, - } - - if KUZU_AVAILABLE and "kuzudb" not in cls.BACKENDS: - cls.BACKENDS["kuzudb"] = { - "class": KuzuDBClient, - "description": "KuzuDB - Graph database for relationship-based memory storage", - "capabilities": [ - BackendCapability.GRAPH_QUERIES, - BackendCapability.STRUCTURED_QUERY, - BackendCapability.PERSISTENCE, - BackendCapability.EMBEDDINGS, - BackendCapability.MARKDOWN_IMPORT, - ], - "config": { - "enable_markdown": True, - }, - } - - if INFINITY_AVAILABLE and "infinity" not in cls.BACKENDS: - cls.BACKENDS["infinity"] = { - "class": InfinityClient, - "description": "InfinityDB - High-performance vector database", - "capabilities": [ - BackendCapability.VECTOR_SEARCH, - BackendCapability.STRUCTURED_QUERY, - BackendCapability.FULL_TEXT_SEARCH, - BackendCapability.PERSISTENCE, - BackendCapability.EMBEDDINGS, - BackendCapability.TIME_SERIES, - ], - "config": {}, - } - - if SQLITE_AVAILABLE and "sqlite" not in cls.BACKENDS: - cls.BACKENDS["sqlite"] = { - "class": SQLiteMemoryClient, - "description": "SQLite - Lightweight embedded database with vector search via sqlite-vec", - "capabilities": [ - BackendCapability.VECTOR_SEARCH, - BackendCapability.STRUCTURED_QUERY, - BackendCapability.PERSISTENCE, - BackendCapability.EMBEDDINGS, - BackendCapability.MARKDOWN_IMPORT, - ], - "config": {}, - } - - @classmethod - def get_backend( - cls, - name: str, - config: Optional[Dict] = None, - ) -> BaseVectorDB: - """Get a backend instance by name. - - Args: - name: Backend name (e.g., "lancedb", "local") - config: Optional configuration overrides - - Returns: - Backend instance - - Raises: - ValueError: If backend not found - """ - # Initialize optional backends - cls._init_optional_backends() - - if name not in cls.BACKENDS: - available = ", ".join(cls.BACKENDS.keys()) - raise ValueError(f"Unknown backend: {name}. Available: {available}") - - backend_info = cls.BACKENDS[name] - backend_class = backend_info["class"] - - # Merge default config with user config - backend_config = backend_info.get("config", {}).copy() - if config: - backend_config.update(config) - - logger.info( - f"Creating {name} backend", - description=backend_info["description"], - capabilities=backend_info["capabilities"], - ) - - # Create instance with config - if backend_config: - return backend_class(**backend_config) - return backend_class() - - @classmethod - def list_backends(cls) -> Dict[str, Dict]: - """List all available backends and their capabilities.""" - # Initialize optional backends - cls._init_optional_backends() - - return { - name: { - "description": info["description"], - "capabilities": info["capabilities"], - } - for name, info in cls.BACKENDS.items() - } - - @classmethod - def find_backend_for_capability( - cls, - capability: str, - ) -> Optional[str]: - """Find the best backend for a specific capability. - - Args: - capability: Required capability - - Returns: - Backend name or None if no backend supports it - """ - for name, info in cls.BACKENDS.items(): - if capability in info["capabilities"]: - return name - return None - - @classmethod - def get_backend_for_task( - cls, - task: str, - ) -> str: - """Get the recommended backend for a specific task. - - Args: - task: Task type (e.g., "similarity_search", "document_storage") - - Returns: - Recommended backend name - """ - task_mapping = { - "similarity_search": "lancedb", - "vector_search": "lancedb", - "embeddings": "lancedb", - "document_storage": "local", - "simple_storage": "local", - "markdown_import": "local", # Both support it, but local is simpler - "development": "local", # Simple for dev - "production": "lancedb", # Better for production - } - - return task_mapping.get(task, "local") # Default to local - - @classmethod - def register_backend( - cls, - name: str, - backend_class: Type[BaseVectorDB], - description: str, - capabilities: list[str], - config: Optional[Dict] = None, - ) -> None: - """Register a new backend. - - Args: - name: Backend name - backend_class: Backend class - description: Human-readable description - capabilities: List of capabilities - config: Default configuration - """ - cls.BACKENDS[name] = { - "class": backend_class, - "description": description, - "capabilities": capabilities, - "config": config or {}, - } - logger.info(f"Registered backend: {name}") - - -# Convenience functions -def get_backend(name: str = "local", **config) -> BaseVectorDB: - """Get a backend instance. - - Args: - name: Backend name (default: "local") - **config: Configuration options - - Returns: - Backend instance - """ - return BackendRegistry.get_backend(name, config) - - -def list_backends() -> Dict[str, Dict]: - """List all available backends.""" - return BackendRegistry.list_backends() - - -def get_best_backend_for(capability: str) -> Optional[str]: - """Get the best backend for a capability.""" - return BackendRegistry.find_backend_for_capability(capability) diff --git a/pkg/hanzo-memory/src/hanzo_memory/db/base.py b/pkg/hanzo-memory/src/hanzo_memory/db/base.py deleted file mode 100644 index 7261a2323..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/db/base.py +++ /dev/null @@ -1,175 +0,0 @@ -"""Base database interface for vector storage backends.""" - -from abc import ABC, abstractmethod -from typing import Any - - -class BaseVectorDB(ABC): - """Abstract base class for vector database backends.""" - - @abstractmethod - def create_project( - self, - project_id: str, - user_id: str, - name: str, - description: str = "", - metadata: dict | None = None, - ) -> dict[str, Any]: - """Create a new project.""" - pass - - @abstractmethod - def get_user_projects(self, user_id: str) -> list[dict[str, Any]]: - """Get all projects for a user.""" - pass - - @abstractmethod - def create_memories_table(self, user_id: str) -> None: - """Create a memories table for a user.""" - pass - - @abstractmethod - def add_memory( - self, - memory_id: str, - user_id: str, - project_id: str, - content: str, - embedding: list[float], - metadata: dict | None = None, - importance: float = 0.5, - ) -> dict[str, Any]: - """Add a memory to the database.""" - pass - - @abstractmethod - def search_memories( - self, - user_id: str, - query_embedding: list[float], - project_id: str | None = None, - limit: int = 10, - min_similarity: float = 0.0, - ) -> list[dict[str, Any]]: - """Search memories by similarity.""" - pass - - @abstractmethod - def create_knowledge_base( - self, - knowledge_base_id: str, - project_id: str, - name: str, - description: str = "", - metadata: dict | None = None, - ) -> dict[str, Any]: - """Create a new knowledge base.""" - pass - - @abstractmethod - def get_knowledge_bases(self, project_id: str) -> list[dict[str, Any]]: - """Get all knowledge bases for a project.""" - pass - - @abstractmethod - def add_fact( - self, - fact_id: str, - knowledge_base_id: str, - content: str, - embedding: list[float], - metadata: dict | None = None, - confidence: float = 1.0, - ) -> dict[str, Any]: - """Add a fact to a knowledge base.""" - pass - - @abstractmethod - def search_facts( - self, - knowledge_base_id: str, - query_embedding: list[float] | None = None, - limit: int = 10, - ) -> list[dict[str, Any]]: - """Search facts in a knowledge base.""" - pass - - @abstractmethod - def delete_fact(self, fact_id: str, knowledge_base_id: str) -> bool: - """Delete a fact from a knowledge base.""" - pass - - @abstractmethod - def update_memory( - self, - memory_id: str, - user_id: str, - project_id: str, - content: str | None = None, - metadata: dict | None = None, - importance: float | None = None, - ) -> dict[str, Any] | None: - """Update a memory in the database.""" - pass - - @abstractmethod - def create_chat_session( - self, - session_id: str, - user_id: str, - project_id: str, - metadata: dict | None = None, - ) -> dict[str, Any]: - """Create a new chat session.""" - pass - - @abstractmethod - def add_chat_message( - self, - message_id: str, - session_id: str, - role: str, - content: str, - embedding: list[float], - metadata: dict | None = None, - ) -> dict[str, Any]: - """Add a message to a chat session.""" - pass - - @abstractmethod - def get_chat_messages( - self, - session_id: str, - limit: int | None = None, - ) -> list[dict[str, Any]]: - """Get messages from a chat session.""" - pass - - @abstractmethod - def search_chat_messages( - self, - session_id: str, - query_embedding: list[float], - limit: int = 10, - ) -> list[dict[str, Any]]: - """Search messages in a chat session by similarity.""" - pass - - # Optional methods for implementations - not abstract - # Implementations can override these if needed - - def close(self) -> None: - """Close the database connection (optional for implementations).""" - # Default implementation does nothing - return None - - def create_projects_table(self) -> None: - """Create projects table if not exists (optional for implementations).""" - # Default implementation does nothing - return None - - def create_knowledge_bases_table(self) -> None: - """Create knowledge bases table if not exists (optional for implementations).""" - # Default implementation does nothing - return None diff --git a/pkg/hanzo-memory/src/hanzo_memory/db/client.py b/pkg/hanzo-memory/src/hanzo_memory/db/client.py deleted file mode 100644 index f2a420325..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/db/client.py +++ /dev/null @@ -1,587 +0,0 @@ -"""InfinityDB client for vector storage and search.""" - -import json -import platform -from datetime import datetime, timezone -from pathlib import Path -from typing import Any - -import polars as pl -from structlog import get_logger - -from ..config import settings -from .base import BaseVectorDB - -logger = get_logger() - -# Try to import InfinityDB, fall back to mock for unsupported platforms -try: - import infinity_embedded - - logger.info("Using InfinityDB") -except ImportError: - logger.debug( - f"InfinityDB not available on {platform.system()} {platform.machine()}, using mock implementation" - ) - from . import mock_infinity as infinity_embedded - - -class InfinityClient(BaseVectorDB): - """Client for InfinityDB operations.""" - - def __init__(self, db_path: str | None = None): - """Initialize InfinityDB client.""" - self.db_path = db_path or settings.infinity_db_str - Path(self.db_path).mkdir(parents=True, exist_ok=True) - self.infinity = infinity_embedded.connect(self.db_path) - self._ensure_databases() - - def _ensure_databases(self) -> None: - """Ensure required databases exist.""" - # Create main databases - for db_name in ["projects", "memories", "knowledge", "chats"]: - try: - self.infinity.create_database(db_name) - logger.info(f"Created database: {db_name}") - except Exception: - # Database already exists - pass - - def _get_db(self, db_name: str) -> Any: - """Get a database object.""" - return self.infinity.get_database(db_name) - - # Project Management - def create_projects_table(self) -> None: - """Create projects table if not exists.""" - db = self._get_db("projects") - try: - db.create_table( - "projects", - { - "project_id": {"type": "varchar"}, - "user_id": {"type": "varchar"}, - "name": {"type": "varchar"}, - "description": {"type": "varchar"}, - "metadata": {"type": "varchar"}, # JSON string - "created_at": {"type": "varchar"}, - "updated_at": {"type": "varchar"}, - }, - ) - logger.info("Created projects table") - except Exception as e: - logger.debug(f"Projects table may already exist: {e}") - - def create_project( - self, - project_id: str, - user_id: str, - name: str, - description: str = "", - metadata: dict[str, Any] | None = None, - ) -> dict[str, Any]: - """Create a new project.""" - db = self._get_db("projects") - table = db.get_table("projects") - - now = datetime.now(timezone.utc).isoformat() - project_data = { - "project_id": project_id, - "user_id": user_id, - "name": name, - "description": description, - "metadata": json.dumps(metadata or {}), - "created_at": now, - "updated_at": now, - } - - table.insert([project_data]) - return project_data - - # Memory Management - def create_memories_table(self, user_id: str) -> None: - """Create memories table for a user if not exists.""" - db = self._get_db("memories") - table_name = f"memories_{user_id}" - - try: - db.create_table( - table_name, - { - "memory_id": {"type": "varchar"}, - "user_id": {"type": "varchar"}, - "project_id": {"type": "varchar"}, - "content": {"type": "varchar"}, - "embedding": { - "type": f"vector,{settings.embedding_dimensions},float" - }, - "metadata": {"type": "varchar"}, # JSON string - "importance": {"type": "float"}, - "created_at": {"type": "varchar"}, - "updated_at": {"type": "varchar"}, - }, - ) - logger.info(f"Created memories table for user: {user_id}") - except Exception: - pass - - def add_memory( - self, - memory_id: str, - user_id: str, - project_id: str, - content: str, - embedding: list[float], - metadata: dict[str, Any] | None = None, - importance: float = 1.0, - ) -> dict[str, Any]: - """Add a memory to the database.""" - db = self._get_db("memories") - table_name = f"memories_{user_id}" - table = db.get_table(table_name) - - now = datetime.now(timezone.utc).isoformat() - memory_data = { - "memory_id": memory_id, - "user_id": user_id, - "project_id": project_id, - "content": content, - "embedding": embedding, - "metadata": json.dumps(metadata or {}), - "importance": importance, - "created_at": now, - "updated_at": now, - } - - table.insert([memory_data]) - return memory_data - - def search_memories( - self, - user_id: str, - query_embedding: list[float], - project_id: str | None = None, - limit: int = 10, - threshold: float = 0.0, - ) -> pl.DataFrame: - """Search memories using vector similarity.""" - db = self._get_db("memories") - table_name = f"memories_{user_id}" - - try: - table = db.get_table(table_name) - - # Build query - query = table.output(["*"]).match_dense( - "embedding", - query_embedding, - "float", - "cosine", # Using cosine similarity - limit, - ) - - # Apply project filter if specified - if project_id: - query = query.filter(f"project_id = '{project_id}'") - - # Execute query and return as polars DataFrame - return query.to_pl() - except Exception as e: - logger.error(f"Error searching memories: {e}") - return pl.DataFrame() - - # Knowledge Base Management - def create_knowledge_bases_table(self) -> None: - """Create knowledge bases table if not exists.""" - db = self._get_db("knowledge") - try: - db.create_table( - "knowledge_bases", - { - "kb_id": {"type": "varchar"}, - "user_id": {"type": "varchar"}, - "project_id": {"type": "varchar"}, - "name": {"type": "varchar"}, - "description": {"type": "varchar"}, - "metadata": {"type": "varchar"}, # JSON string - "created_at": {"type": "varchar"}, - "updated_at": {"type": "varchar"}, - }, - ) - logger.info("Created knowledge_bases table") - except Exception as e: - logger.debug(f"Knowledge bases table may already exist: {e}") - - def create_knowledge_base( - self, - kb_id: str, - user_id: str, - project_id: str, - name: str, - description: str = "", - metadata: dict[str, Any] | None = None, - ) -> dict[str, Any]: - """Create a new knowledge base.""" - db = self._get_db("knowledge") - table = db.get_table("knowledge_bases") - - now = datetime.now(timezone.utc).isoformat() - kb_data = { - "kb_id": kb_id, - "user_id": user_id, - "project_id": project_id, - "name": name, - "description": description, - "metadata": json.dumps(metadata or {}), - "created_at": now, - "updated_at": now, - } - - table.insert([kb_data]) - - # Create facts table for this knowledge base - self._create_facts_table(kb_id) - - return kb_data - - def _create_facts_table(self, kb_id: str) -> None: - """Create facts table for a knowledge base.""" - db = self._get_db("knowledge") - table_name = f"facts_{kb_id}" - - try: - db.create_table( - table_name, - { - "fact_id": {"type": "varchar"}, - "kb_id": {"type": "varchar"}, - "content": {"type": "varchar"}, - "embedding": { - "type": f"vector,{settings.embedding_dimensions},float" - }, - "parent_id": {"type": "varchar"}, - "metadata": {"type": "varchar"}, # JSON string - "created_at": {"type": "varchar"}, - "updated_at": {"type": "varchar"}, - }, - ) - logger.info(f"Created facts table for kb: {kb_id}") - except Exception: - pass - - def add_fact( - self, - fact_id: str, - kb_id: str, - content: str, - embedding: list[float], - parent_id: str | None = None, - metadata: dict[str, Any] | None = None, - ) -> dict[str, Any]: - """Add a fact to a knowledge base.""" - db = self._get_db("knowledge") - table_name = f"facts_{kb_id}" - table = db.get_table(table_name) - - now = datetime.now(timezone.utc).isoformat() - fact_data = { - "fact_id": fact_id, - "kb_id": kb_id, - "content": content, - "embedding": embedding, - "parent_id": parent_id or "", - "metadata": json.dumps(metadata or {}), - "created_at": now, - "updated_at": now, - } - - table.insert([fact_data]) - return fact_data - - def search_facts( - self, - kb_id: str, - query_embedding: list[float], - limit: int = 10, - parent_id: str | None = None, - ) -> pl.DataFrame: - """Search facts in a knowledge base.""" - db = self._get_db("knowledge") - table_name = f"facts_{kb_id}" - - try: - table = db.get_table(table_name) - - # Build query - query = table.output(["*"]).match_dense( - "embedding", query_embedding, "float", "cosine", limit - ) - - # Apply parent filter if specified - if parent_id: - query = query.filter(f"parent_id = '{parent_id}'") - - return query.to_pl() - except Exception as e: - logger.error(f"Error searching facts: {e}") - return pl.DataFrame() - - # Chat Management - def create_chats_table(self, user_id: str) -> None: - """Create chats table for a user.""" - db = self._get_db("chats") - table_name = f"chats_{user_id}" - - try: - db.create_table( - table_name, - { - "chat_id": {"type": "varchar"}, - "user_id": {"type": "varchar"}, - "project_id": {"type": "varchar"}, - "session_id": {"type": "varchar"}, - "role": {"type": "varchar"}, # user, assistant, system - "content": {"type": "varchar"}, - "embedding": { - "type": f"vector,{settings.embedding_dimensions},float" - }, - "metadata": {"type": "varchar"}, # JSON string - "created_at": {"type": "varchar"}, - }, - ) - logger.info(f"Created chats table for user: {user_id}") - except Exception: - pass - - def add_chat_message( - self, - chat_id: str, - user_id: str, - project_id: str, - session_id: str, - role: str, - content: str, - embedding: list[float], - metadata: dict[str, Any] | None = None, - ) -> dict[str, Any]: - """Add a chat message.""" - db = self._get_db("chats") - table_name = f"chats_{user_id}" - table = db.get_table(table_name) - - chat_data = { - "chat_id": chat_id, - "user_id": user_id, - "project_id": project_id, - "session_id": session_id, - "role": role, - "content": content, - "embedding": embedding, - "metadata": json.dumps(metadata or {}), - "created_at": datetime.now(timezone.utc).isoformat(), - } - - table.insert([chat_data]) - return chat_data - - def get_chat_history( - self, - user_id: str, - session_id: str, - limit: int = 100, - ) -> pl.DataFrame: - """Get chat history for a session.""" - db = self._get_db("chats") - table_name = f"chats_{user_id}" - - try: - table = db.get_table(table_name) - - # Get messages for session ordered by created_at - query = table.output(["*"]).filter(f"session_id = '{session_id}'") - - # TODO: Add proper ordering once InfinityDB supports it - return query.to_pl() - except Exception as e: - logger.error(f"Error getting chat history: {e}") - return pl.DataFrame() - - def search_chats( - self, - user_id: str, - query_embedding: list[float], - project_id: str | None = None, - session_id: str | None = None, - limit: int = 10, - ) -> pl.DataFrame: - """Search chat messages.""" - db = self._get_db("chats") - table_name = f"chats_{user_id}" - - try: - table = db.get_table(table_name) - - # Build query - query = table.output(["*"]).match_dense( - "embedding", query_embedding, "float", "cosine", limit - ) - - # Apply filters - if project_id: - query = query.filter(f"project_id = '{project_id}'") - if session_id: - query = query.filter(f"session_id = '{session_id}'") - - return query.to_pl() - except Exception as e: - logger.error(f"Error searching chats: {e}") - return pl.DataFrame() - - def close(self) -> None: - """Close the InfinityDB connection.""" - if hasattr(self, "infinity"): - # InfinityDB embedded doesn't have explicit close method - pass - - # Abstract method implementations required by BaseVectorDB - def get_user_projects(self, user_id: str) -> list[dict[str, Any]]: - """Get all projects for a user.""" - db = self._get_db("projects") - try: - table = db.get_table("projects") - result = table.output(["*"]).filter(f"user_id = '{user_id}'").to_pl() - return result.to_dicts() if len(result) > 0 else [] - except Exception as e: - logger.error(f"Error getting user projects: {e}") - return [] - - def get_knowledge_bases(self, project_id: str) -> list[dict[str, Any]]: - """Get all knowledge bases for a project.""" - db = self._get_db("knowledge") - try: - table = db.get_table("knowledge_bases") - result = table.output(["*"]).filter(f"project_id = '{project_id}'").to_pl() - return result.to_dicts() if len(result) > 0 else [] - except Exception as e: - logger.error(f"Error getting knowledge bases: {e}") - return [] - - def delete_fact(self, fact_id: str, knowledge_base_id: str) -> bool: - """Delete a fact from a knowledge base.""" - db = self._get_db("knowledge") - table_name = f"facts_{knowledge_base_id}" - try: - table = db.get_table(table_name) - table.delete(f"fact_id = '{fact_id}'") - return True - except Exception as e: - logger.error(f"Error deleting fact: {e}") - return False - - def update_memory( - self, - memory_id: str, - user_id: str, - project_id: str, - content: str | None = None, - metadata: dict | None = None, - importance: float | None = None, - ) -> dict[str, Any] | None: - """Update a memory in the database.""" - db = self._get_db("memories") - table_name = f"memories_{user_id}" - try: - table = db.get_table(table_name) - updates = {"updated_at": datetime.now(timezone.utc).isoformat()} - if content is not None: - updates["content"] = content - if metadata is not None: - updates["metadata"] = json.dumps(metadata) - if importance is not None: - updates["importance"] = importance - table.update(f"memory_id = '{memory_id}'", updates) - return updates - except Exception as e: - logger.error(f"Error updating memory: {e}") - return None - - def create_chat_session( - self, - session_id: str, - user_id: str, - project_id: str, - metadata: dict | None = None, - ) -> dict[str, Any]: - """Create a new chat session.""" - self.create_chats_table(user_id) - return { - "session_id": session_id, - "user_id": user_id, - "project_id": project_id, - "metadata": metadata or {}, - "created_at": datetime.now(timezone.utc).isoformat(), - } - - def get_chat_messages( - self, - session_id: str, - limit: int | None = None, - ) -> list[dict[str, Any]]: - """Get messages from a chat session.""" - # Note: This implementation searches all user tables - not ideal - db = self._get_db("chats") - try: - # Get all tables and search for the session - for table_name in db.list_tables(): - if table_name.startswith("chats_"): - table = db.get_table(table_name) - query = table.output(["*"]).filter(f"session_id = '{session_id}'") - result = query.to_pl() - if len(result) > 0: - messages = result.to_dicts() - if limit: - return messages[:limit] - return messages - return [] - except Exception as e: - logger.error(f"Error getting chat messages: {e}") - return [] - - def search_chat_messages( - self, - session_id: str, - query_embedding: list[float], - limit: int = 10, - ) -> list[dict[str, Any]]: - """Search messages in a chat session by similarity.""" - db = self._get_db("chats") - try: - for table_name in db.list_tables(): - if table_name.startswith("chats_"): - table = db.get_table(table_name) - query = ( - table.output(["*"]) - .match_dense( - "embedding", query_embedding, "float", "cosine", limit - ) - .filter(f"session_id = '{session_id}'") - ) - result = query.to_pl() - if len(result) > 0: - return result.to_dicts() - return [] - except Exception as e: - logger.error(f"Error searching chat messages: {e}") - return [] - - -# Global client instance -_client: InfinityClient | None = None - - -def get_client() -> InfinityClient: - """Get or create the global InfinityDB client.""" - global _client - if _client is None: - _client = InfinityClient() - return _client diff --git a/pkg/hanzo-memory/src/hanzo_memory/db/factory.py b/pkg/hanzo-memory/src/hanzo_memory/db/factory.py deleted file mode 100644 index 5159d5948..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/db/factory.py +++ /dev/null @@ -1,78 +0,0 @@ -"""Database factory for selecting the appropriate backend.""" - -from typing import Optional, Dict, Any -from structlog import get_logger - -from ..config import settings -from .base import BaseVectorDB -from .backends.backend_registry import BackendRegistry - -logger = get_logger() - -# Global database client instance -_db_client: BaseVectorDB | None = None - - -def get_db_client( - backend: Optional[str] = None, - config: Optional[Dict[str, Any]] = None, -) -> BaseVectorDB: - """Get or create the global database client. - - Args: - backend: Override backend type (default: from settings) - config: Optional configuration for the backend - - Returns: - Database client instance - """ - global _db_client - - # Determine backend - backend_name = backend or settings.db_backend.lower() - - # Reset client if backend changed - if _db_client and hasattr(_db_client, "__backend_name__"): - if _db_client.__backend_name__ != backend_name: # type: ignore - logger.info(f"Backend changed from {_db_client.__backend_name__} to {backend_name}, resetting client") # type: ignore - _db_client = None - - if _db_client is None: - # Special handling for legacy "infinity" backend name - if backend_name == "infinity": - logger.warning("InfinityDB backend is optional, falling back to local") - backend_name = "local" - - try: - _db_client = BackendRegistry.get_backend(backend_name, config) - _db_client.__backend_name__ = backend_name # type: ignore # Store for comparison - except ValueError as e: - logger.error(f"Failed to create backend {backend_name}: {e}") - logger.info("Falling back to local backend") - _db_client = BackendRegistry.get_backend("local", config) - _db_client.__backend_name__ = "local" # type: ignore - - return _db_client - - -def reset_db_client() -> None: - """Reset the global database client (useful for testing).""" - global _db_client - _db_client = None - - -def list_available_backends() -> Dict[str, Dict]: - """List all available backends and their capabilities.""" - return BackendRegistry.list_backends() - - -def get_backend_for_task(task: str) -> str: - """Get the recommended backend for a specific task. - - Args: - task: Task description (e.g., "vector_search", "document_storage") - - Returns: - Recommended backend name - """ - return BackendRegistry.get_backend_for_task(task) diff --git a/pkg/hanzo-memory/src/hanzo_memory/db/kuzudb_client.py b/pkg/hanzo-memory/src/hanzo_memory/db/kuzudb_client.py deleted file mode 100644 index 4c5970ed1..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/db/kuzudb_client.py +++ /dev/null @@ -1,661 +0,0 @@ -"""KuzuDB client for graph-based memory storage.""" - -import json -import uuid -from datetime import datetime, timezone -from pathlib import Path -from typing import Any, Dict, List, Optional - -import numpy as np -from structlog import get_logger - -from ..config import settings -from ..models.memory import Memory, MemoryCreate, MemoryResponse -from ..models.project import Project, ProjectCreate -from ..models.fact import Fact, FactCreate -from .base import BaseVectorDB - -logger = get_logger() - -try: - import kuzu - - KUZU_AVAILABLE = True -except ImportError: - KUZU_AVAILABLE = False - logger.warning("KuzuDB not available. Install with: pip install kuzu") - - -class KuzuDBClient(BaseVectorDB): - """KuzuDB implementation for graph-based memory storage.""" - - def __init__(self, db_path: Optional[str] = None, enable_markdown: bool = True): - """Initialize KuzuDB client. - - Args: - db_path: Path to KuzuDB database directory - enable_markdown: Whether to import markdown files - """ - if not KUZU_AVAILABLE: - raise ImportError("KuzuDB not installed. Install with: pip install kuzu") - - self.db_path = Path(db_path or settings.db_path) / "kuzudb" - self.db_path.mkdir(parents=True, exist_ok=True) - - # Initialize KuzuDB database - self.db = kuzu.Database(str(self.db_path)) - self.conn = kuzu.Connection(self.db) - - # Initialize schema - self._init_schema() - - # Import markdown if enabled - self.enable_markdown = enable_markdown - if enable_markdown: - from ..markdown_memory import MarkdownMemoryReader - - self.markdown_reader = MarkdownMemoryReader() - self._import_markdown_memories() - - logger.info(f"Initialized KuzuDB storage at {self.db_path}") - - def _init_schema(self): - """Initialize KuzuDB schema with nodes and relationships.""" - # Create node tables - queries = [ - # User node - """CREATE NODE TABLE IF NOT EXISTS User( - user_id STRING PRIMARY KEY, - name STRING, - created_at TIMESTAMP - )""", - # Project node - """CREATE NODE TABLE IF NOT EXISTS Project( - project_id STRING PRIMARY KEY, - name STRING, - description STRING, - metadata STRING, - created_at TIMESTAMP, - updated_at TIMESTAMP - )""", - # Memory node - """CREATE NODE TABLE IF NOT EXISTS Memory( - memory_id STRING PRIMARY KEY, - content STRING, - memory_type STRING, - importance DOUBLE, - context STRING, - metadata STRING, - source STRING, - embedding DOUBLE[], - created_at TIMESTAMP, - updated_at TIMESTAMP - )""", - # Fact node - """CREATE NODE TABLE IF NOT EXISTS Fact( - fact_id STRING PRIMARY KEY, - statement STRING, - confidence DOUBLE, - source STRING, - metadata STRING, - embedding DOUBLE[], - created_at TIMESTAMP, - updated_at TIMESTAMP - )""", - # KnowledgeBase node - """CREATE NODE TABLE IF NOT EXISTS KnowledgeBase( - kb_id STRING PRIMARY KEY, - name STRING, - description STRING, - metadata STRING, - created_at TIMESTAMP, - updated_at TIMESTAMP - )""", - # Create relationship tables - """CREATE REL TABLE IF NOT EXISTS OWNS( - FROM User TO Project - )""", - """CREATE REL TABLE IF NOT EXISTS HAS_MEMORY( - FROM Project TO Memory, - user_id STRING - )""", - """CREATE REL TABLE IF NOT EXISTS HAS_FACT( - FROM Project TO Fact, - user_id STRING - )""", - """CREATE REL TABLE IF NOT EXISTS IN_KB( - FROM Memory TO KnowledgeBase - )""", - """CREATE REL TABLE IF NOT EXISTS RELATES_TO( - FROM Memory TO Memory, - relationship_type STRING, - strength DOUBLE - )""", - """CREATE REL TABLE IF NOT EXISTS DERIVED_FROM( - FROM Fact TO Memory - )""", - ] - - for query in queries: - try: - self.conn.execute(query) - except Exception as e: - # Table might already exist - logger.debug(f"Schema creation note: {e}") - - async def initialize(self) -> None: - """Initialize the database.""" - logger.info("KuzuDB initialized") - - async def close(self) -> None: - """Close database connection.""" - if hasattr(self, "conn"): - # KuzuDB doesn't have explicit close, but we can clean up - self.conn = None - logger.info("Closing KuzuDB connection") - - def create_project_sync(self, project: ProjectCreate, user_id: str) -> dict: - """Create a project synchronously.""" - project_id = str(uuid.uuid4()) - now = datetime.now(timezone.utc).isoformat() - - # Create project node - self.conn.execute( - """MERGE (p:Project {project_id: $pid}) - SET p.name = $name, - p.description = $desc, - p.metadata = $metadata, - p.created_at = $created, - p.updated_at = $updated - """, - { - "pid": project_id, - "name": project.name, - "desc": project.description, - "metadata": json.dumps(project.metadata or {}), - "created": now, - "updated": now, - }, - ) - - # Create or connect user - self.conn.execute( - """MERGE (u:User {user_id: $uid}) - ON CREATE SET u.created_at = $created - """, - {"uid": user_id, "created": now}, - ) - - # Create ownership relationship - self.conn.execute( - """MATCH (u:User {user_id: $uid}), (p:Project {project_id: $pid}) - MERGE (u)-[:OWNS]->(p) - """, - {"uid": user_id, "pid": project_id}, - ) - - return { - "project_id": project_id, - "name": project.name, - "description": project.description, - "user_id": user_id, - "metadata": project.metadata or {}, - "created_at": now, - "updated_at": now, - } - - async def create_project(self, project: ProjectCreate, user_id: str) -> Project: - """Create a project.""" - project_data = self.create_project_sync(project, user_id) - return Project(**project_data) - - def add_memory( - self, - memory_id: str, - user_id: str, - project_id: str, - content: str, - embedding: list[float], - metadata: dict | None = None, - importance: float = 0.5, - ) -> dict[str, Any]: - """Add a memory to the graph.""" - now = datetime.now(timezone.utc).isoformat() - - # Create memory node - self.conn.execute( - """MERGE (m:Memory {memory_id: $mid}) - SET m.content = $content, - m.memory_type = $mtype, - m.importance = $importance, - m.context = $context, - m.metadata = $metadata, - m.source = $source, - m.embedding = $embedding, - m.created_at = $created, - m.updated_at = $updated - """, - { - "mid": memory_id, - "content": content, - "mtype": ( - metadata.get("memory_type", "general") if metadata else "general" - ), - "importance": importance, - "context": json.dumps(metadata.get("context", {}) if metadata else {}), - "metadata": json.dumps(metadata or {}), - "source": metadata.get("source", "") if metadata else "", - "embedding": embedding, - "created": now, - "updated": now, - }, - ) - - # Connect to project - self.conn.execute( - """MATCH (p:Project {project_id: $pid}), (m:Memory {memory_id: $mid}) - MERGE (p)-[:HAS_MEMORY {user_id: $uid}]->(m) - """, - {"pid": project_id, "mid": memory_id, "uid": user_id}, - ) - - # Find and create relationships to related memories - if embedding: - self._create_memory_relationships(memory_id, embedding, project_id) - - return { - "memory_id": memory_id, - "project_id": project_id, - "user_id": user_id, - "content": content, - "metadata": metadata or {}, - "importance": importance, - "created_at": now, - "updated_at": now, - } - - def _create_memory_relationships( - self, memory_id: str, embedding: list[float], project_id: str - ): - """Create relationships between related memories based on similarity.""" - # Find similar memories in the same project - results = self.conn.execute( - """MATCH (p:Project {project_id: $pid})-[:HAS_MEMORY]->(m:Memory) - WHERE m.memory_id <> $mid AND m.embedding IS NOT NULL - RETURN m.memory_id, m.embedding - LIMIT 10 - """, - {"pid": project_id, "mid": memory_id}, - ) - - query_vec = np.array(embedding) - for row in results: - other_id = row[0] - other_embedding = row[1] - if other_embedding: - similarity = self._compute_similarity( - query_vec, np.array(other_embedding) - ) - if similarity > 0.7: # Only create strong relationships - self.conn.execute( - """MATCH (m1:Memory {memory_id: $mid1}), (m2:Memory {memory_id: $mid2}) - MERGE (m1)-[:RELATES_TO {relationship_type: 'similar', strength: $strength}]->(m2) - """, - {"mid1": memory_id, "mid2": other_id, "strength": similarity}, - ) - - def _compute_similarity(self, vec1: np.ndarray, vec2: np.ndarray) -> float: - """Compute cosine similarity between two vectors.""" - dot_product = np.dot(vec1, vec2) - norm1 = np.linalg.norm(vec1) - norm2 = np.linalg.norm(vec2) - if norm1 == 0 or norm2 == 0: - return 0.0 - return float(dot_product / (norm1 * norm2)) - - def search_memories( - self, - query_embedding: List[float], - user_id: str, - project_id: Optional[str] = None, - limit: int = 10, - memory_type: Optional[str] = None, - ) -> Any: - """Search memories using graph traversal and vector similarity.""" - import pandas as pd - - # Build the query - where_clause = [] - params = {"uid": user_id, "limit": limit} - - if project_id: - where_clause.append("p.project_id = $pid") - params["pid"] = project_id - - if memory_type: - where_clause.append("m.memory_type = $mtype") - params["mtype"] = memory_type - - where = " AND ".join(where_clause) if where_clause else "1=1" - - # Query memories - query = f""" - MATCH (u:User {{user_id: $uid}})-[:OWNS]->(p:Project)-[:HAS_MEMORY]->(m:Memory) - WHERE {where} - RETURN m.memory_id, m.content, m.memory_type, m.importance, - m.context, m.metadata, m.source, m.embedding, - m.created_at, m.updated_at, p.project_id, $uid as user_id - LIMIT $limit - """ - - results = self.conn.execute(query, params) - - # Calculate similarities and build dataframe - data = [] - query_vec = np.array(query_embedding) if query_embedding else None - - for row in results: - similarity = 0.0 - if query_vec is not None and row[7]: # embedding - similarity = self._compute_similarity(query_vec, np.array(row[7])) - - data.append( - { - "memory_id": row[0], - "content": row[1], - "memory_type": row[2], - "importance": row[3], - "context": row[4], - "metadata": row[5], - "source": row[6], - "created_at": row[8], - "updated_at": row[9], - "project_id": row[10], - "user_id": row[11], - "similarity_score": similarity, - } - ) - - # Sort by similarity if we have embeddings - if query_embedding: - data.sort(key=lambda x: x["similarity_score"], reverse=True) - - return pd.DataFrame(data[:limit]) - - async def search_memories_async( - self, - query_embedding: List[float], - project_id: str, - user_id: Optional[str] = None, - limit: int = 10, - memory_type: Optional[str] = None, - ) -> List[Memory]: - """Async wrapper for search_memories.""" - df = self.search_memories( - query_embedding, user_id or "default", project_id, limit, memory_type - ) - - memories = [] - for _, row in df.iterrows(): - memories.append( - Memory( - memory_id=row.get("memory_id"), - project_id=row.get("project_id"), - user_id=row.get("user_id"), - content=row.get("content"), - memory_type=row.get("memory_type"), - importance=row.get("importance", 0.5), - context=( - json.loads(row.get("context", "{}")) - if isinstance(row.get("context"), str) - else row.get("context", {}) - ), - metadata=( - json.loads(row.get("metadata", "{}")) - if isinstance(row.get("metadata"), str) - else row.get("metadata", {}) - ), - source=row.get("source"), - created_at=row.get("created_at"), - updated_at=row.get("updated_at"), - ) - ) - return memories - - async def create_memory( - self, memory: MemoryCreate, project_id: str, user_id: Optional[str] = None - ) -> Memory: - """Create a memory.""" - memory_id = str(uuid.uuid4()) - user_id = user_id or "default" - - memory_data = self.add_memory( - memory_id=memory_id, - user_id=user_id, - project_id=project_id, - content=memory.content, - embedding=memory.embedding or [0.0] * settings.embedding_dimensions, - metadata={ - "memory_type": memory.memory_type, - "context": memory.context or {}, - "source": memory.source, - **(memory.metadata or {}), - }, - importance=memory.importance, - ) - - return Memory(**memory_data) - - async def get_recent_memories( - self, - project_id: str, - user_id: Optional[str] = None, - limit: int = 10, - memory_type: Optional[str] = None, - ) -> List[Memory]: - """Get recent memories from the graph.""" - user_id = user_id or "default" - - where_clause = ["p.project_id = $pid"] - params = {"pid": project_id, "uid": user_id, "limit": limit} - - if memory_type: - where_clause.append("m.memory_type = $mtype") - params["mtype"] = memory_type - - where = " AND ".join(where_clause) - - query = f""" - MATCH (u:User {{user_id: $uid}})-[:OWNS]->(p:Project)-[:HAS_MEMORY]->(m:Memory) - WHERE {where} - RETURN m.memory_id, m.content, m.memory_type, m.importance, - m.context, m.metadata, m.source, - m.created_at, m.updated_at - ORDER BY m.created_at DESC - LIMIT $limit - """ - - results = self.conn.execute(query, params) - - memories = [] - for row in results: - memories.append( - Memory( - memory_id=row[0], - project_id=project_id, - user_id=user_id, - content=row[1], - memory_type=row[2], - importance=row[3], - context=json.loads(row[4]) if isinstance(row[4], str) else row[4], - metadata=json.loads(row[5]) if isinstance(row[5], str) else row[5], - source=row[6], - created_at=row[7], - updated_at=row[8], - ) - ) - - return memories - - async def list_projects(self, user_id: Optional[str] = None) -> List[Project]: - """List all projects for a user.""" - user_id = user_id or "default" - - results = self.conn.execute( - """MATCH (u:User {user_id: $uid})-[:OWNS]->(p:Project) - RETURN p.project_id, p.name, p.description, p.metadata, - p.created_at, p.updated_at - """, - {"uid": user_id}, - ) - - projects = [] - for row in results: - projects.append( - Project( - project_id=row[0], - name=row[1], - description=row[2], - user_id=user_id, - metadata=json.loads(row[3]) if isinstance(row[3], str) else row[3], - created_at=row[4], - updated_at=row[5], - ) - ) - - return projects - - def create_memories_table(self, user_id: str) -> None: - """Create memories table (no-op for KuzuDB as schema is predefined).""" - pass - - def _import_markdown_memories(self): - """Import memories from markdown files.""" - if not self.markdown_reader: - return - - memories = self.markdown_reader.read_all_memories() - imported_count = 0 - - # Create a special project for markdown imports - project_id = "markdown_import" - self.conn.execute( - """MERGE (p:Project {project_id: $pid}) - SET p.name = $name, - p.description = $desc, - p.created_at = $created, - p.updated_at = $updated - """, - { - "pid": project_id, - "name": "Markdown Import", - "desc": "Memories imported from markdown files", - "created": datetime.now(timezone.utc).isoformat(), - "updated": datetime.now(timezone.utc).isoformat(), - }, - ) - - for memory_create in memories: - memory_id = str(uuid.uuid4()) - self.add_memory( - memory_id=memory_id, - user_id="system", - project_id=project_id, - content=memory_create.content, - embedding=[0.0] * settings.embedding_dimensions, - metadata={ - "memory_type": memory_create.memory_type or "knowledge", - "context": memory_create.context or {}, - "source": memory_create.source, - **(memory_create.metadata or {}), - }, - importance=memory_create.importance, - ) - imported_count += 1 - - if imported_count > 0: - logger.info( - f"Imported {imported_count} memories from markdown files to KuzuDB" - ) - - # Additional methods for graph-specific operations - def get_related_memories( - self, memory_id: str, relationship_type: Optional[str] = None - ) -> List[Dict]: - """Get memories related to a specific memory through graph relationships.""" - where = "" - params = {"mid": memory_id} - - if relationship_type: - where = "{relationship_type: $rtype}" - params["rtype"] = relationship_type - - query = f""" - MATCH (m1:Memory {{memory_id: $mid}})-[r:RELATES_TO {where}]->(m2:Memory) - RETURN m2.memory_id, m2.content, r.relationship_type, r.strength - ORDER BY r.strength DESC - """ - - results = self.conn.execute(query, params) - - related = [] - for row in results: - related.append( - { - "memory_id": row[0], - "content": row[1], - "relationship_type": row[2], - "strength": row[3], - } - ) - - return related - - def get_memory_graph(self, project_id: str, depth: int = 2) -> Dict: - """Get a subgraph of memories and their relationships.""" - # Get nodes - nodes_query = """ - MATCH (p:Project {project_id: $pid})-[:HAS_MEMORY]->(m:Memory) - RETURN m.memory_id, m.content, m.memory_type, m.importance - LIMIT 100 - """ - - nodes_results = self.conn.execute(nodes_query, {"pid": project_id}) - - nodes = [] - node_ids = set() - for row in nodes_results: - nodes.append( - { - "id": row[0], - "content": row[1], - "type": row[2], - "importance": row[3], - } - ) - node_ids.add(row[0]) - - # Get edges - edges_query = """ - MATCH (m1:Memory)-[r:RELATES_TO]->(m2:Memory) - WHERE m1.memory_id IN $ids AND m2.memory_id IN $ids - RETURN m1.memory_id, m2.memory_id, r.relationship_type, r.strength - """ - - edges_results = self.conn.execute(edges_query, {"ids": list(node_ids)}) - - edges = [] - for row in edges_results: - edges.append( - { - "source": row[0], - "target": row[1], - "type": row[2], - "strength": row[3], - } - ) - - return { - "nodes": nodes, - "edges": edges, - } diff --git a/pkg/hanzo-memory/src/hanzo_memory/db/local_client.py b/pkg/hanzo-memory/src/hanzo_memory/db/local_client.py deleted file mode 100644 index 92e797068..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/db/local_client.py +++ /dev/null @@ -1,732 +0,0 @@ -"""Local file-based memory storage implementation.""" - -import json -import uuid -from datetime import datetime -from pathlib import Path -from typing import Any, Dict, List, Optional - -import numpy as np -from structlog import get_logger - -from ..models.knowledge import Fact, FactCreate, KnowledgeBase -from ..models.memory import Memory, MemoryCreate, MemoryResponse -from ..models.project import Project, ProjectCreate -from .base import BaseVectorDB -from .markdown_reader import MarkdownMemoryReader - -logger = get_logger() - - -class LocalMemoryClient(BaseVectorDB): - """Local file-based implementation of the vector database.""" - - def __init__( - self, storage_dir: Optional[Path] = None, enable_markdown: bool = True - ): - """Initialize local memory storage. - - Args: - storage_dir: Directory to store memory files - enable_markdown: Whether to enable markdown file integration - """ - self.storage_dir = storage_dir or Path.home() / ".hanzo" / "memory" - self.storage_dir.mkdir(parents=True, exist_ok=True) - - # File paths for different collections - self.memories_file = self.storage_dir / "memories.json" - self.facts_file = self.storage_dir / "facts.json" - self.projects_file = self.storage_dir / "projects.json" - self.embeddings_file = self.storage_dir / "embeddings.npz" - self.markdown_index_file = self.storage_dir / "markdown_index.json" - - # Load existing data - self.memories = self._load_json(self.memories_file) - self.facts = self._load_json(self.facts_file) - self.projects = self._load_json(self.projects_file) - self.embeddings = self._load_embeddings() - self.markdown_index = self._load_json(self.markdown_index_file) - - # Initialize markdown reader if enabled - self.enable_markdown = enable_markdown - self.markdown_reader = MarkdownMemoryReader() if enable_markdown else None - - # Import markdown memories on initialization - if self.enable_markdown: - self._import_markdown_memories() - - logger.info(f"Initialized local memory storage at {self.storage_dir}") - - def _load_json(self, file_path: Path) -> Dict[str, Any]: - """Load JSON data from file.""" - if file_path.exists(): - try: - with open(file_path, "r") as f: - return json.load(f) - except Exception as e: - logger.error(f"Error loading {file_path}: {e}") - return {} - - def _save_json(self, data: Dict[str, Any], file_path: Path): - """Save JSON data to file.""" - try: - with open(file_path, "w") as f: - json.dump(data, f, indent=2, default=str) - except Exception as e: - logger.error(f"Error saving {file_path}: {e}") - - def _load_embeddings(self) -> Dict[str, np.ndarray]: - """Load embeddings from file.""" - if self.embeddings_file.exists(): - try: - data = np.load(self.embeddings_file, allow_pickle=True) - return {k: v for k, v in data.items()} - except Exception as e: - logger.error(f"Error loading embeddings: {e}") - return {} - - def _save_embeddings(self): - """Save embeddings to file.""" - try: - np.savez_compressed(self.embeddings_file, **self.embeddings) - except Exception as e: - logger.error(f"Error saving embeddings: {e}") - - def _compute_similarity( - self, embedding1: np.ndarray, embedding2: np.ndarray - ) -> float: - """Compute cosine similarity between two embeddings.""" - dot_product = np.dot(embedding1, embedding2) - norm1 = np.linalg.norm(embedding1) - norm2 = np.linalg.norm(embedding2) - if norm1 == 0 or norm2 == 0: - return 0.0 - return float(dot_product / (norm1 * norm2)) - - async def initialize(self) -> None: - """Initialize the database (no-op for local storage).""" - logger.info("Local memory storage initialized") - - async def close(self) -> None: - """Close the database connection (save all data).""" - self._save_json(self.memories, self.memories_file) - self._save_json(self.facts, self.facts_file) - self._save_json(self.projects, self.projects_file) - self._save_embeddings() - logger.info("Local memory storage closed") - - # Memory operations - async def create_memory( - self, memory: MemoryCreate, project_id: str, user_id: Optional[str] = None - ) -> Memory: - """Create a new memory.""" - memory_id = str(uuid.uuid4()) - - # Create memory object - memory_data = { - "id": memory_id, - "project_id": project_id, - "user_id": user_id or "default", - "content": memory.content, - "memory_type": memory.memory_type, - "importance": memory.importance, - "context": memory.context, - "metadata": memory.metadata or {}, - "source": memory.source, - "timestamp": datetime.utcnow().isoformat(), - "created_at": datetime.utcnow().isoformat(), - "updated_at": datetime.utcnow().isoformat(), - } - - # Store memory - self.memories[memory_id] = memory_data - - # Store embedding if provided - if memory.embedding: - self.embeddings[f"memory_{memory_id}"] = np.array(memory.embedding) - - # Save to disk - self._save_json(self.memories, self.memories_file) - if memory.embedding: - self._save_embeddings() - - # Return with proper memory_id field - memory_data["memory_id"] = memory_id # Ensure memory_id is set - return Memory(**memory_data) - - def search_memories( - self, - query_embedding: List[float], - user_id: str, - project_id: Optional[str] = None, - limit: int = 10, - memory_type: Optional[str] = None, - ) -> Any: - """Search for similar memories synchronously.""" - query_vec = np.array(query_embedding) - results = [] - - for mem_id, memory in self.memories.items(): - # Filter by project and user - if project_id and memory.get("project_id") != project_id: - continue - if user_id and memory.get("user_id") != user_id: - continue - if memory_type and memory.get("memory_type") != memory_type: - continue - - # Calculate similarity if embedding exists - embedding_key = f"memory_{mem_id}" - if embedding_key in self.embeddings: - similarity = self._compute_similarity( - query_vec, self.embeddings[embedding_key] - ) - results.append({"memory": memory, "similarity": similarity}) - - # Sort by similarity and limit - results.sort(key=lambda x: x["similarity"], reverse=True) - results = results[:limit] - - # Convert to dataframe-like structure for compatibility - import pandas as pd - - if not results: - return pd.DataFrame() - - # Extract memory data for dataframe - data = [] - for r in results: - mem = r["memory"] - data.append( - { - "memory_id": mem.get("id", mem.get("memory_id")), - "project_id": mem.get("project_id"), - "user_id": mem.get("user_id"), - "content": mem.get("content"), - "memory_type": mem.get("memory_type"), - "importance": mem.get("importance"), - "context": mem.get("context"), - "metadata": mem.get("metadata"), - "source": mem.get("source"), - "created_at": mem.get("created_at"), - "updated_at": mem.get("updated_at"), - "similarity_score": r["similarity"], - } - ) - - return pd.DataFrame(data) - - async def search_memories_async( - self, - query_embedding: List[float], - project_id: str, - user_id: Optional[str] = None, - limit: int = 10, - memory_type: Optional[str] = None, - min_importance: float = 0.0, - ) -> List[MemoryResponse]: - """Search for similar memories.""" - query_vec = np.array(query_embedding) - results = [] - - for mem_id, memory in self.memories.items(): - # Filter by project and user - if memory["project_id"] != project_id: - continue - if user_id and memory.get("user_id") != user_id: - continue - if memory_type and memory.get("memory_type") != memory_type: - continue - if memory.get("importance", 0) < min_importance: - continue - - # Calculate similarity if embedding exists - embedding_key = f"memory_{mem_id}" - if embedding_key in self.embeddings: - similarity = self._compute_similarity( - query_vec, self.embeddings[embedding_key] - ) - results.append({"memory": Memory(**memory), "similarity": similarity}) - - # Sort by similarity and limit - results.sort(key=lambda x: x["similarity"], reverse=True) - results = results[:limit] - - return [ - MemoryResponse( - memory=r["memory"], - similarity=r["similarity"], - relevance_score=r["similarity"], - ) - for r in results - ] - - async def get_recent_memories( - self, - project_id: str, - user_id: Optional[str] = None, - limit: int = 10, - memory_type: Optional[str] = None, - ) -> List[Memory]: - """Get recent memories.""" - results = [] - - for memory in self.memories.values(): - # Filter - if memory["project_id"] != project_id: - continue - if user_id and memory.get("user_id") != user_id: - continue - if memory_type and memory.get("memory_type") != memory_type: - continue - - results.append(memory) - - # Sort by timestamp - results.sort(key=lambda x: x.get("timestamp", ""), reverse=True) - results = results[:limit] - - return [Memory(**r) for r in results] - - async def delete_memory(self, memory_id: str, project_id: str) -> bool: - """Delete a memory.""" - if memory_id in self.memories: - if self.memories[memory_id]["project_id"] == project_id: - del self.memories[memory_id] - - # Remove embedding - embedding_key = f"memory_{memory_id}" - if embedding_key in self.embeddings: - del self.embeddings[embedding_key] - - # Save changes - self._save_json(self.memories, self.memories_file) - self._save_embeddings() - return True - return False - - # Knowledge operations - async def create_fact( - self, fact: FactCreate, project_id: str, user_id: Optional[str] = None - ) -> Fact: - """Create a new fact.""" - fact_id = str(uuid.uuid4()) - - fact_data = { - "id": fact_id, - "project_id": project_id, - "user_id": user_id or "default", - "subject": fact.subject, - "predicate": fact.predicate, - "object": fact.object, - "confidence": fact.confidence, - "source": fact.source, - "metadata": fact.metadata or {}, - "created_at": datetime.utcnow().isoformat(), - "updated_at": datetime.utcnow().isoformat(), - } - - self.facts[fact_id] = fact_data - - # Store embedding if provided - if fact.embedding: - self.embeddings[f"fact_{fact_id}"] = np.array(fact.embedding) - - # Save to disk - self._save_json(self.facts, self.facts_file) - if fact.embedding: - self._save_embeddings() - - return Fact(**fact_data) - - async def search_facts( - self, - query_embedding: List[float], - project_id: str, - user_id: Optional[str] = None, - limit: int = 10, - min_confidence: float = 0.0, - ) -> List[Fact]: - """Search for similar facts.""" - query_vec = np.array(query_embedding) - results = [] - - for fact_id, fact in self.facts.items(): - # Filter - if fact["project_id"] != project_id: - continue - if user_id and fact.get("user_id") != user_id: - continue - if fact.get("confidence", 0) < min_confidence: - continue - - # Calculate similarity - embedding_key = f"fact_{fact_id}" - if embedding_key in self.embeddings: - similarity = self._compute_similarity( - query_vec, self.embeddings[embedding_key] - ) - results.append({"fact": fact, "similarity": similarity}) - - # Sort and limit - results.sort(key=lambda x: x["similarity"], reverse=True) - results = results[:limit] - - return [Fact(**r["fact"]) for r in results] - - async def get_knowledge_graph( - self, project_id: str, user_id: Optional[str] = None - ) -> KnowledgeBase: - """Get the knowledge graph.""" - project_facts = [] - - for fact in self.facts.values(): - if fact["project_id"] != project_id: - continue - if user_id and fact.get("user_id") != user_id: - continue - project_facts.append(Fact(**fact)) - - # Build entity and relation lists - entities = set() - relations = set() - - for fact in project_facts: - entities.add(fact.subject) - entities.add(fact.object) - relations.add(fact.predicate) - - return KnowledgeBase( - facts=project_facts, - entities=list(entities), - relations=list(relations), - metadata={ - "fact_count": len(project_facts), - "entity_count": len(entities), - "relation_count": len(relations), - }, - ) - - # Project operations - async def create_project( - self, project: ProjectCreate, user_id: Optional[str] = None - ) -> Project: - """Create a new project.""" - project_id = str(uuid.uuid4()) - - project_data = { - "id": project_id, - "project_id": project_id, # Add project_id field - "name": project.name, - "description": project.description, - "user_id": user_id or "default", - "metadata": project.metadata or {}, - "created_at": datetime.utcnow().isoformat(), - "updated_at": datetime.utcnow().isoformat(), - } - - self.projects[project_id] = project_data - self._save_json(self.projects, self.projects_file) - - return Project(**project_data) - - async def get_project( - self, project_id: str, user_id: Optional[str] = None - ) -> Optional[Project]: - """Get a project by ID.""" - if project_id in self.projects: - project = self.projects[project_id] - if not user_id or project.get("user_id") == user_id: - return Project(**project) - return None - - async def list_projects(self, user_id: Optional[str] = None) -> List[Project]: - """List all projects.""" - results = [] - - for project in self.projects.values(): - if not user_id or project.get("user_id") == user_id: - # Ensure project_id exists for backward compatibility - if "project_id" not in project: - project["project_id"] = project.get("id", str(uuid.uuid4())) - results.append(Project(**project)) - - return results - - async def delete_project( - self, project_id: str, user_id: Optional[str] = None - ) -> bool: - """Delete a project and all associated data.""" - if project_id in self.projects: - project = self.projects[project_id] - if not user_id or project.get("user_id") == user_id: - # Delete project - del self.projects[project_id] - - # Delete associated memories - memory_ids_to_delete = [ - mid - for mid, m in self.memories.items() - if m["project_id"] == project_id - ] - for mid in memory_ids_to_delete: - del self.memories[mid] - embedding_key = f"memory_{mid}" - if embedding_key in self.embeddings: - del self.embeddings[embedding_key] - - # Delete associated facts - fact_ids_to_delete = [ - fid - for fid, f in self.facts.items() - if f["project_id"] == project_id - ] - for fid in fact_ids_to_delete: - del self.facts[fid] - embedding_key = f"fact_{fid}" - if embedding_key in self.embeddings: - del self.embeddings[embedding_key] - - # Save all changes - self._save_json(self.projects, self.projects_file) - self._save_json(self.memories, self.memories_file) - self._save_json(self.facts, self.facts_file) - self._save_embeddings() - - return True - return False - - # Additional abstract methods implementation - async def create_memories_table(self, user_id: str = None) -> None: - """Create memories table (no-op for local storage).""" - pass - - def create_memories_table(self, user_id: str) -> None: - """Create memories table for a user (no-op for local storage).""" - pass - - def add_memory( - self, - memory_id: str, - user_id: str, - project_id: str, - content: str, - embedding: list[float], - metadata: dict | None = None, - importance: float = 0.5, - ) -> dict[str, Any]: - """Add a memory synchronously.""" - memory_data = { - "id": memory_id, - "memory_id": memory_id, - "project_id": project_id, - "user_id": user_id, - "content": content, - "memory_type": ( - metadata.get("memory_type", "general") if metadata else "general" - ), - "importance": importance, - "context": metadata.get("context", {}) if metadata else {}, - "metadata": metadata or {}, - "source": metadata.get("source", "") if metadata else "", - "timestamp": datetime.utcnow().isoformat(), - "created_at": datetime.utcnow().isoformat(), - "updated_at": datetime.utcnow().isoformat(), - } - - # Store memory - self.memories[memory_id] = memory_data - - # Store embedding - if embedding: - self.embeddings[f"memory_{memory_id}"] = np.array(embedding) - - # Save to disk - self._save_json(self.memories, self.memories_file) - if embedding: - self._save_embeddings() - - return memory_data - - async def add_memory_async(self, memory: MemoryCreate, project_id: str) -> Memory: - """Add a memory (alias for create_memory).""" - return await self.create_memory(memory, project_id) - - async def add_fact(self, fact: FactCreate, project_id: str) -> Fact: - """Add a fact (alias for create_fact).""" - return await self.create_fact(fact, project_id) - - async def delete_fact(self, fact_id: str) -> bool: - """Delete a fact by ID.""" - if fact_id in self.facts: - del self.facts[fact_id] - embedding_key = f"fact_{fact_id}" - if embedding_key in self.embeddings: - del self.embeddings[embedding_key] - self._save_json(self.facts, self.facts_file) - self._save_embeddings() - return True - return False - - async def create_knowledge_base( - self, name: str, description: str, project_id: str - ) -> KnowledgeBase: - """Create a knowledge base.""" - return await self.get_knowledge_graph(project_id) - - async def get_knowledge_bases(self, project_id: str) -> List[KnowledgeBase]: - """Get all knowledge bases for a project.""" - return [await self.get_knowledge_graph(project_id)] - - async def create_chat_session( - self, project_id: str, user_id: Optional[str] = None - ) -> str: - """Create a new chat session.""" - session_id = str(uuid.uuid4()) - if not hasattr(self, "chat_sessions"): - self.chat_sessions = {} - self.chat_sessions[session_id] = { - "id": session_id, - "project_id": project_id, - "user_id": user_id or "default", - "messages": [], - "created_at": datetime.utcnow().isoformat(), - } - return session_id - - async def add_chat_message(self, session_id: str, role: str, content: str) -> None: - """Add a message to a chat session.""" - if not hasattr(self, "chat_sessions"): - self.chat_sessions = {} - if session_id in self.chat_sessions: - self.chat_sessions[session_id]["messages"].append( - { - "role": role, - "content": content, - "timestamp": datetime.utcnow().isoformat(), - } - ) - - async def get_chat_messages(self, session_id: str) -> List[Dict[str, Any]]: - """Get messages from a chat session.""" - if not hasattr(self, "chat_sessions"): - self.chat_sessions = {} - if session_id in self.chat_sessions: - return self.chat_sessions[session_id]["messages"] - return [] - - async def search_chat_messages( - self, query: str, session_id: Optional[str] = None - ) -> List[Dict[str, Any]]: - """Search chat messages.""" - if not hasattr(self, "chat_sessions"): - return [] - results = [] - sessions_to_search = [session_id] if session_id else self.chat_sessions.keys() - for sid in sessions_to_search: - if sid in self.chat_sessions: - for msg in self.chat_sessions[sid]["messages"]: - if query.lower() in msg["content"].lower(): - results.append(msg) - return results - - async def get_user_projects(self, user_id: str) -> List[Project]: - """Get all projects for a user.""" - return await self.list_projects(user_id) - - def update_memory( - self, - memory_id: str, - user_id: str, - project_id: str, - content: str | None = None, - metadata: dict | None = None, - importance: float | None = None, - ) -> dict[str, Any] | None: - """Update a memory in the database.""" - if memory_id not in self.memories: - return None - - memory = self.memories[memory_id] - - # Verify user and project match - if memory.get("user_id") != user_id or memory.get("project_id") != project_id: - return None - - # Update fields if provided - if content is not None: - memory["content"] = content - if metadata is not None: - memory["metadata"] = metadata - if importance is not None: - memory["importance"] = importance - - # Update timestamp - memory["updated_at"] = datetime.utcnow().isoformat() - - # Save changes - self._save_json(self.memories, self.memories_file) - - return memory - - def _import_markdown_memories(self): - """Import memories from markdown files.""" - if not self.markdown_reader: - return - - try: - # Read markdown memories - markdown_memories = self.markdown_reader.read_markdown_memories() - - # Create a default project for markdown memories - project_id = "markdown_import" - if project_id not in self.projects: - project_data = { - "id": project_id, - "project_id": project_id, - "name": "Markdown Import", - "description": "Automatically imported memories from markdown files", - "user_id": "system", - "metadata": {"auto_created": True}, - "created_at": datetime.utcnow().isoformat(), - "updated_at": datetime.utcnow().isoformat(), - } - self.projects[project_id] = project_data - self._save_json(self.projects, self.projects_file) - - # Import each memory - imported_count = 0 - for memory_create in markdown_memories: - # Check if this memory already exists (by source) - source = memory_create.source - existing = any( - m.get("source") == source for m in self.memories.values() - ) - - if not existing: - memory_id = str(uuid.uuid4()) - memory_data = { - "id": memory_id, - "project_id": project_id, - "user_id": "system", - "content": memory_create.content, - "memory_type": memory_create.memory_type, - "importance": memory_create.importance, - "context": memory_create.context, - "metadata": memory_create.metadata or {}, - "source": source, - "timestamp": datetime.utcnow().isoformat(), - "created_at": datetime.utcnow().isoformat(), - "updated_at": datetime.utcnow().isoformat(), - } - self.memories[memory_id] = memory_data - imported_count += 1 - - if imported_count > 0: - self._save_json(self.memories, self.memories_file) - logger.info( - f"Imported {imported_count} new memories from markdown files" - ) - - except Exception as e: - logger.error(f"Error importing markdown memories: {e}") diff --git a/pkg/hanzo-memory/src/hanzo_memory/db/markdown_reader.py b/pkg/hanzo-memory/src/hanzo_memory/db/markdown_reader.py deleted file mode 100644 index 5e673d3a5..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/db/markdown_reader.py +++ /dev/null @@ -1,424 +0,0 @@ -"""Markdown file reader for integrating LLM.md and similar files into memory.""" - -import hashlib -import os -from datetime import datetime -from pathlib import Path -from typing import Dict, List, Optional, Set - -from structlog import get_logger - -from ..models.memory import MemoryCreate - -logger = get_logger() - -# Supported markdown files for memory integration -MEMORY_MD_FILES = [ - "LLM.md", - "AGENTS.md", - "CLAUDE.md", - "GEMINI.md", - "QWEN.md", - "AI.md", - "README.md", # Often contains important project context - "CONTEXT.md", - "MEMORY.md", - "INSTRUCTIONS.md", - "PROMPT.md", -] - -# Additional patterns to look for -MEMORY_MD_PATTERNS = [ - "*_LLM.md", - "*_AGENT.md", - "*_AI.md", - "*.claude.md", - "*.gemini.md", - "*.qwen.md", -] - - -class MarkdownMemoryReader: - """Reads and processes markdown files for memory integration.""" - - def __init__(self, watch_dirs: Optional[List[Path]] = None): - """Initialize the markdown reader. - - Args: - watch_dirs: List of directories to watch for markdown files. - Defaults to current directory and parent directories. - """ - self.watch_dirs = watch_dirs or self._get_default_watch_dirs() - self.processed_files: Set[str] = set() - self.file_hashes: Dict[str, str] = {} - - def _get_default_watch_dirs(self) -> List[Path]: - """Get default directories to watch.""" - dirs = [] - current = Path.cwd() - - # Add current directory - dirs.append(current) - - # Add parent directories up to home or 3 levels - for _ in range(3): - if current.parent == current or current == Path.home(): - break - current = current.parent - dirs.append(current) - - # Add home .config directories - home = Path.home() - for config_dir in [".claude", ".gemini", ".qwen", ".ai", ".llm"]: - config_path = home / config_dir - if config_path.exists(): - dirs.append(config_path) - - return dirs - - def _compute_file_hash(self, filepath: Path) -> str: - """Compute hash of file contents.""" - try: - content = filepath.read_text(encoding="utf-8") - return hashlib.sha256(content.encode()).hexdigest() - except Exception as e: - logger.error(f"Error hashing file {filepath}: {e}") - return "" - - def _parse_markdown_sections(self, content: str, filepath: Path) -> List[Dict]: - """Parse markdown content into sections.""" - sections = [] - current_section = { - "title": f"Content from {filepath.name}", - "content": "", - "level": 0, - "line_start": 0, - } - - lines = content.split("\n") - current_line = 0 - - for line in lines: - current_line += 1 - - # Check for headers - if line.startswith("#"): - # Save previous section if it has content - if current_section["content"].strip(): - sections.append(current_section) - - # Start new section - header_level = len(line) - len(line.lstrip("#")) - header_text = line.lstrip("#").strip() - - current_section = { - "title": header_text or f"Section from {filepath.name}", - "content": "", - "level": header_level, - "line_start": current_line, - } - else: - # Add line to current section - current_section["content"] += line + "\n" - - # Don't forget the last section - if current_section["content"].strip(): - sections.append(current_section) - - # If no sections were found, treat entire content as one section - if not sections and content.strip(): - sections.append( - { - "title": f"Content from {filepath.name}", - "content": content, - "level": 0, - "line_start": 0, - } - ) - - return sections - - def find_markdown_files(self) -> List[Path]: - """Find all relevant markdown files in watched directories.""" - md_files = [] - seen_files = set() - - for watch_dir in self.watch_dirs: - if not watch_dir.exists(): - continue - - # Look for specific named files - for md_file in MEMORY_MD_FILES: - filepath = watch_dir / md_file - if filepath.exists() and filepath not in seen_files: - md_files.append(filepath) - seen_files.add(filepath) - - # Look for pattern-matched files - for pattern in MEMORY_MD_PATTERNS: - for filepath in watch_dir.glob(pattern): - if filepath not in seen_files: - md_files.append(filepath) - seen_files.add(filepath) - - # Sort by priority (LLM.md first, then others) - def priority(f: Path) -> int: - name = f.name.upper() - if name == "LLM.MD": - return 0 - elif name == "CLAUDE.MD": - return 1 - elif name == "GEMINI.MD": - return 2 - elif name == "QWEN.MD": - return 3 - elif name == "AGENTS.MD": - return 4 - else: - return 5 - - md_files.sort(key=priority) - return md_files - - def read_markdown_memories(self) -> List[MemoryCreate]: - """Read all markdown files and convert to memories.""" - memories = [] - md_files = self.find_markdown_files() - - for filepath in md_files: - try: - # Check if file has been processed and unchanged - file_id = str(filepath.absolute()) - current_hash = self._compute_file_hash(filepath) - - if ( - file_id in self.file_hashes - and self.file_hashes[file_id] == current_hash - ): - logger.debug(f"Skipping unchanged file: {filepath}") - continue - - # Read and parse the file - content = filepath.read_text(encoding="utf-8") - if not content.strip(): - continue - - # Store hash for future comparison - self.file_hashes[file_id] = current_hash - - # Parse into sections - sections = self._parse_markdown_sections(content, filepath) - - # Create memories from sections - for section in sections: - # Determine importance based on file and section - importance = self._calculate_importance(filepath, section) - - # Determine memory type - memory_type = self._determine_memory_type(filepath, section) - - # Create memory - memory = MemoryCreate( - content=section["content"], - memory_type=memory_type, - importance=importance, - context={ - "source_file": str(filepath.absolute()), - "file_name": filepath.name, - "section_title": section["title"], - "section_level": section["level"], - "line_start": section["line_start"], - "directory": str(filepath.parent.absolute()), - "file_modified": datetime.fromtimestamp( - filepath.stat().st_mtime - ).isoformat(), - }, - metadata={ - "auto_imported": True, - "markdown_file": True, - }, - source=f"markdown://{filepath.absolute()}#{section['line_start']}", - ) - memories.append(memory) - - logger.info(f"Read {len(sections)} sections from {filepath.name}") - - except Exception as e: - logger.error(f"Error reading markdown file {filepath}: {e}") - continue - - return memories - - def _calculate_importance(self, filepath: Path, section: Dict) -> float: - """Calculate importance score for a memory section.""" - importance = 0.5 # Base importance - - # File-based importance - filename = filepath.name.upper() - if filename == "LLM.MD": - importance += 0.3 - elif filename in ["CLAUDE.MD", "GEMINI.MD", "QWEN.MD"]: - importance += 0.25 - elif filename == "AGENTS.MD": - importance += 0.2 - elif filename == "README.MD": - importance += 0.1 - - # Section-based importance - title_lower = section["title"].lower() - - # High importance keywords - high_importance_keywords = [ - "important", - "critical", - "essential", - "must", - "always", - "never", - "warning", - "error", - "security", - "key", - "architecture", - "design", - "api", - "interface", - ] - - for keyword in high_importance_keywords: - if keyword in title_lower or keyword in section["content"].lower()[:200]: - importance += 0.1 - break - - # Header level importance (H1 > H2 > H3) - if section["level"] == 1: - importance += 0.15 - elif section["level"] == 2: - importance += 0.1 - elif section["level"] == 3: - importance += 0.05 - - # Cap at 1.0 - return min(importance, 1.0) - - def _determine_memory_type(self, filepath: Path, section: Dict) -> str: - """Determine the type of memory based on content.""" - filename = filepath.name.upper() - content_lower = section["content"].lower() - title_lower = section["title"].lower() - - # Check filename patterns - if "AGENT" in filename: - return "agent_instruction" - elif filename in ["CLAUDE.MD", "GEMINI.MD", "QWEN.MD"]: - return "model_instruction" - elif filename == "LLM.MD": - return "system_context" - - # Check content patterns - if any( - word in content_lower[:500] - for word in ["instruction", "prompt", "you should", "you must"] - ): - return "instruction" - elif any( - word in content_lower[:500] - for word in ["api", "endpoint", "function", "method", "class"] - ): - return "technical" - elif any( - word in content_lower[:500] - for word in ["example", "usage", "how to", "tutorial"] - ): - return "example" - elif any( - word in title_lower - for word in ["config", "setting", "environment", "variable"] - ): - return "configuration" - elif any( - word in title_lower - for word in ["architecture", "design", "structure", "pattern"] - ): - return "architectural" - - return "knowledge" - - def watch_for_changes(self) -> List[MemoryCreate]: - """Check for new or modified markdown files and return new memories.""" - new_memories = [] - - # Get current memories - current_memories = self.read_markdown_memories() - - # Track which files were processed - for memory in current_memories: - source_file = memory.context.get("source_file") - if source_file: - self.processed_files.add(source_file) - - return current_memories - - def get_project_context(self, project_path: Optional[Path] = None) -> Dict: - """Get comprehensive project context from markdown files.""" - project_path = project_path or Path.cwd() - - context = { - "project_path": str(project_path.absolute()), - "project_name": project_path.name, - "markdown_files": [], - "instructions": [], - "configurations": [], - "examples": [], - "architecture": [], - } - - # Find and categorize markdown content - md_files = self.find_markdown_files() - - for filepath in md_files: - try: - content = filepath.read_text(encoding="utf-8") - sections = self._parse_markdown_sections(content, filepath) - - file_info = { - "path": str(filepath.absolute()), - "name": filepath.name, - "modified": datetime.fromtimestamp( - filepath.stat().st_mtime - ).isoformat(), - "sections": len(sections), - } - context["markdown_files"].append(file_info) - - # Categorize sections - for section in sections: - memory_type = self._determine_memory_type(filepath, section) - - section_info = { - "title": section["title"], - "content_preview": ( - section["content"][:200] + "..." - if len(section["content"]) > 200 - else section["content"] - ), - "source": filepath.name, - } - - if memory_type in [ - "instruction", - "agent_instruction", - "model_instruction", - ]: - context["instructions"].append(section_info) - elif memory_type == "configuration": - context["configurations"].append(section_info) - elif memory_type == "example": - context["examples"].append(section_info) - elif memory_type == "architectural": - context["architecture"].append(section_info) - - except Exception as e: - logger.error(f"Error processing {filepath}: {e}") - - return context diff --git a/pkg/hanzo-memory/src/hanzo_memory/db/mock_infinity.py b/pkg/hanzo-memory/src/hanzo_memory/db/mock_infinity.py deleted file mode 100644 index 0142575b0..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/db/mock_infinity.py +++ /dev/null @@ -1,150 +0,0 @@ -"""Mock InfinityDB implementation for testing and platforms without support.""" - -from pathlib import Path -from typing import Any - -import numpy as np -import polars as pl - - -class MockDatabase: - """Mock database implementation.""" - - def __init__(self, name: str): - self.name = name - self.tables: dict[str, dict[str, Any]] = {} - - def create_table(self, table_name: str, schema: dict[str, Any]) -> None: - """Create a table.""" - if table_name not in self.tables: - self.tables[table_name] = {"schema": schema, "data": []} - - def get_table(self, table_name: str) -> "MockTable": - """Get a table.""" - if table_name not in self.tables: - raise ValueError(f"Table {table_name} not found") - return MockTable(self, table_name) - - -class MockTable: - """Mock table implementation.""" - - def __init__(self, db: MockDatabase, name: str): - self.db = db - self.name = name - - def insert(self, records: list[dict[str, Any]]) -> None: - """Insert records.""" - self.db.tables[self.name]["data"].extend(records) - - def output(self, columns: list[str]) -> "MockQuery": - """Start a query.""" - return MockQuery(self, columns) - - -class MockQuery: - """Mock query implementation.""" - - def __init__(self, table: MockTable, columns: list[str]): - self.table = table - self.columns = columns - self.filters: list[str] = [] - self.vector_search: dict[str, Any] | None = None - - def match_dense( - self, - column: str, - query_vector: list[float], - dtype: str, - metric: str, - limit: int, - ) -> "MockQuery": - """Add vector search.""" - self.vector_search = { - "column": column, - "query_vector": np.array(query_vector), - "metric": metric, - "limit": limit, - } - return self - - def filter(self, condition: str) -> "MockQuery": - """Add filter condition.""" - self.filters.append(condition) - return self - - def to_pl(self) -> pl.DataFrame: - """Execute query and return polars DataFrame.""" - # Get all data - data = self.table.db.tables[self.table.name]["data"] - - # Apply filters - filtered_data = [] - for record in data: - include = True - for filter_cond in self.filters: - # Simple filter parsing (e.g., "field = 'value'") - if " = " in filter_cond: - field, value = filter_cond.split(" = ") - value = value.strip("'\"") - if record.get(field) != value: - include = False - break - if include: - filtered_data.append(record) - - # Apply vector search if specified - if self.vector_search and filtered_data: - # Calculate similarities - similarities = [] - for record in filtered_data: - vec = np.array(record[self.vector_search["column"]]) - query = self.vector_search["query_vector"] - - if self.vector_search["metric"] == "cosine": - # Cosine similarity - sim = np.dot(vec, query) / ( - np.linalg.norm(vec) * np.linalg.norm(query) - ) - elif self.vector_search["metric"] == "ip": - # Inner product - sim = np.dot(vec, query) - else: - sim = 0.0 - - similarities.append((sim, record)) - - # Sort by similarity and limit - similarities.sort(key=lambda x: x[0], reverse=True) - filtered_data = [r for _, r in similarities[: self.vector_search["limit"]]] - - # Return as polars DataFrame - if filtered_data: - return pl.DataFrame(filtered_data) - else: - # Return empty DataFrame with schema - return pl.DataFrame() - - -class MockInfinity: - """Mock Infinity connection.""" - - def __init__(self, path: str): - self.path = Path(path) - self.databases: dict[str, MockDatabase] = {} - - def create_database(self, name: str) -> None: - """Create a database.""" - if name not in self.databases: - self.databases[name] = MockDatabase(name) - - def get_database(self, name: str) -> MockDatabase: - """Get a database.""" - if name not in self.databases: - self.create_database(name) - return self.databases[name] - - -def connect(path: str) -> MockInfinity: - """Connect to mock InfinityDB.""" - return MockInfinity(path) diff --git a/pkg/hanzo-memory/src/hanzo_memory/db/sqlite_client.py b/pkg/hanzo-memory/src/hanzo_memory/db/sqlite_client.py deleted file mode 100644 index e6e9b7a39..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/db/sqlite_client.py +++ /dev/null @@ -1,887 +0,0 @@ -"""SQLite-based memory storage implementation with vector search using sqlite-vec.""" - -import json -import sqlite3 -import uuid -from datetime import datetime, timezone -from pathlib import Path -from typing import Any, Dict, List, Optional - -import numpy as np -from structlog import get_logger - -from .base import BaseVectorDB - -logger = get_logger() - - -class SQLiteMemoryClient(BaseVectorDB): - """SQLite-based implementation of the vector database with sqlite-vec for vector search.""" - - def __init__(self, db_path: Optional[Path] = None): - """Initialize SQLite memory storage. - - Args: - db_path: Path to SQLite database file. Defaults to in-memory if None. - """ - if db_path is None: - self.db_path = ":memory:" - else: - self.db_path = str(db_path) - - # Connect to database - self.conn = sqlite3.connect(self.db_path, check_same_thread=False) - self.conn.row_factory = sqlite3.Row # Enable column access by name - - # Enable extension loading for sqlite-vec - self.conn.enable_load_extension(True) - - # Initialize tables - self._init_tables() - - logger.info(f"Initialized SQLite memory storage at {self.db_path}") - - async def initialize(self) -> None: - """Initialize the database (no-op for local storage).""" - logger.info("SQLite memory storage initialized") - - def _init_tables(self): - """Initialize required tables.""" - # Enable sqlite-vec extension - try: - self.conn.execute("SELECT load_extension('vec');") - except sqlite3.Error as e: - logger.warning(f"Could not load sqlite-vec extension: {e}") - logger.warning("Vector search functionality will be limited") - - # Create projects table - self.conn.execute(""" - CREATE TABLE IF NOT EXISTS projects ( - id TEXT PRIMARY KEY, - project_id TEXT UNIQUE NOT NULL, - name TEXT NOT NULL, - description TEXT, - user_id TEXT NOT NULL, - metadata TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ); - """) - - # Create memories table - self.conn.execute(""" - CREATE TABLE IF NOT EXISTS memories ( - id TEXT PRIMARY KEY, - memory_id TEXT UNIQUE NOT NULL, - project_id TEXT NOT NULL, - user_id TEXT NOT NULL, - content TEXT NOT NULL, - memory_type TEXT DEFAULT 'general', - importance REAL DEFAULT 0.5, - context TEXT, - metadata TEXT, - source TEXT, - embedding BLOB, - timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (project_id) REFERENCES projects(project_id) - ); - """) - - # Create facts table - self.conn.execute(""" - CREATE TABLE IF NOT EXISTS facts ( - id TEXT PRIMARY KEY, - fact_id TEXT UNIQUE NOT NULL, - knowledge_base_id TEXT NOT NULL, - content TEXT NOT NULL, - subject TEXT, - predicate TEXT, - object TEXT, - confidence REAL DEFAULT 1.0, - source TEXT, - embedding BLOB, - metadata TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ); - """) - - # Create knowledge bases table - self.conn.execute(""" - CREATE TABLE IF NOT EXISTS knowledge_bases ( - id TEXT PRIMARY KEY, - kb_id TEXT UNIQUE NOT NULL, - project_id TEXT NOT NULL, - name TEXT NOT NULL, - description TEXT, - metadata TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (project_id) REFERENCES projects(project_id) - ); - """) - - # Create chat sessions table - self.conn.execute(""" - CREATE TABLE IF NOT EXISTS chat_sessions ( - id TEXT PRIMARY KEY, - session_id TEXT UNIQUE NOT NULL, - project_id TEXT NOT NULL, - user_id TEXT NOT NULL, - metadata TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ); - """) - - # Create chat messages table - self.conn.execute(""" - CREATE TABLE IF NOT EXISTS chat_messages ( - id TEXT PRIMARY KEY, - message_id TEXT UNIQUE NOT NULL, - session_id TEXT NOT NULL, - role TEXT NOT NULL, - content TEXT NOT NULL, - embedding BLOB, - metadata TEXT, - timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (session_id) REFERENCES chat_sessions(session_id) - ); - """) - - # Create indexes - self.conn.execute( - "CREATE INDEX IF NOT EXISTS idx_memories_user_project ON memories(user_id, project_id);" - ) - self.conn.execute( - "CREATE INDEX IF NOT EXISTS idx_memories_timestamp ON memories(timestamp);" - ) - self.conn.execute( - "CREATE INDEX IF NOT EXISTS idx_facts_kb ON facts(knowledge_base_id);" - ) - self.conn.execute( - "CREATE INDEX IF NOT EXISTS idx_chat_sessions_user_project ON chat_sessions(user_id, project_id);" - ) - - # Create vector index for embeddings if sqlite-vec is available - try: - # Create vector index for memories - self.conn.execute(""" - CREATE INDEX IF NOT EXISTS idx_memories_embedding - ON memories (vec_to_json16(embedding)) - WHERE embedding IS NOT NULL; - """) - - # Create vector index for facts - self.conn.execute(""" - CREATE INDEX IF NOT EXISTS idx_facts_embedding - ON facts (vec_to_json16(embedding)) - WHERE embedding IS NOT NULL; - """) - - # Create vector index for chat messages - self.conn.execute(""" - CREATE INDEX IF NOT EXISTS idx_chat_messages_embedding - ON chat_messages (vec_to_json16(embedding)) - WHERE embedding IS NOT NULL; - """) - except sqlite3.Error: - # If sqlite-vec is not available, skip vector indexes - pass - - self.conn.commit() - - def create_project( - self, - project_id: str, - user_id: str, - name: str, - description: str = "", - metadata: dict | None = None, - ) -> dict[str, Any]: - """Create a new project.""" - project_id = project_id or str(uuid.uuid4()) - metadata_json = json.dumps(metadata or {}) - - self.conn.execute( - """ - INSERT INTO projects (id, project_id, user_id, name, description, metadata) - VALUES (?, ?, ?, ?, ?, ?) - """, - (str(uuid.uuid4()), project_id, user_id, name, description, metadata_json), - ) - self.conn.commit() - - return { - "id": project_id, - "project_id": project_id, - "user_id": user_id, - "name": name, - "description": description, - "metadata": metadata or {}, - "created_at": datetime.now(timezone.utc).isoformat(), - "updated_at": datetime.now(timezone.utc).isoformat(), - } - - def get_user_projects(self, user_id: str) -> list[dict[str, Any]]: - """Get all projects for a user.""" - cursor = self.conn.execute( - "SELECT * FROM projects WHERE user_id = ?", (user_id,) - ) - rows = cursor.fetchall() - - projects = [] - for row in rows: - projects.append( - { - "id": row["id"], - "project_id": row["project_id"], - "user_id": row["user_id"], - "name": row["name"], - "description": row["description"], - "metadata": json.loads(row["metadata"]) if row["metadata"] else {}, - "created_at": row["created_at"], - "updated_at": row["updated_at"], - } - ) - - return projects - - def create_memories_table(self, user_id: str) -> None: - """Create a memories table for a user (already handled in initialization).""" - # Table is created during initialization - pass - - def add_memory( - self, - memory_id: str, - user_id: str, - project_id: str, - content: str, - embedding: list[float], - metadata: dict | None = None, - importance: float = 0.5, - ) -> dict[str, Any]: - """Add a memory to the database.""" - memory_id = memory_id or str(uuid.uuid4()) - metadata_json = json.dumps(metadata or {}) - context_json = json.dumps({}) - embedding_blob = ( - np.array(embedding, dtype=np.float32).tobytes() if embedding else None - ) - - self.conn.execute( - """ - INSERT INTO memories - (id, memory_id, user_id, project_id, content, importance, context, metadata, source, embedding) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - str(uuid.uuid4()), - memory_id, - user_id, - project_id, - content, - importance, - context_json, - metadata_json, - "", # source - embedding_blob, - ), - ) - self.conn.commit() - - return { - "id": memory_id, - "memory_id": memory_id, - "user_id": user_id, - "project_id": project_id, - "content": content, - "importance": importance, - "context": {}, - "metadata": metadata or {}, - "source": "", - "timestamp": datetime.now(timezone.utc).isoformat(), - "created_at": datetime.now(timezone.utc).isoformat(), - "updated_at": datetime.now(timezone.utc).isoformat(), - } - - def search_memories( - self, - user_id: str, - query_embedding: list[float], - project_id: str | None = None, - limit: int = 10, - min_similarity: float = 0.0, - ) -> list[dict[str, Any]]: - """Search memories by similarity.""" - query_array = np.array(query_embedding, dtype=np.float32) - query_blob = query_array.tobytes() - - # Build query - where_conditions = ["user_id = ?"] - params = [user_id] - - if project_id: - where_conditions.append("project_id = ?") - params.append(project_id) - - where_clause = " AND ".join(where_conditions) - - # If sqlite-vec is available, use vector similarity search - try: - # Use sqlite-vec for similarity search - cursor = self.conn.execute( - f""" - SELECT *, vec_distance_L2(embedding, ?) as distance - FROM memories - WHERE {where_clause} AND embedding IS NOT NULL - ORDER BY distance ASC - LIMIT ? - """, - [query_blob] + params + [limit], - ) - - rows = cursor.fetchall() - - results = [] - for row in rows: - # Calculate similarity from distance (convert L2 distance to similarity) - distance = row["distance"] - similarity = 1 / (1 + distance) # Convert distance to similarity score - - if similarity >= min_similarity: - results.append( - { - "memory_id": row["memory_id"], - "user_id": row["user_id"], - "project_id": row["project_id"], - "content": row["content"], - "importance": row["importance"], - "context": ( - json.loads(row["context"]) if row["context"] else {} - ), - "metadata": ( - json.loads(row["metadata"]) if row["metadata"] else {} - ), - "source": row["source"], - "created_at": row["created_at"], - "updated_at": row["updated_at"], - "similarity_score": similarity, - } - ) - - return results - - except sqlite3.Error: - # Fallback to basic search without vector similarity - cursor = self.conn.execute( - f""" - SELECT * - FROM memories - WHERE {where_clause} - ORDER BY timestamp DESC - LIMIT ? - """, - params + [limit], - ) - - rows = cursor.fetchall() - - results = [] - for row in rows: - results.append( - { - "memory_id": row["memory_id"], - "user_id": row["user_id"], - "project_id": row["project_id"], - "content": row["content"], - "importance": row["importance"], - "context": json.loads(row["context"]) if row["context"] else {}, - "metadata": ( - json.loads(row["metadata"]) if row["metadata"] else {} - ), - "source": row["source"], - "created_at": row["created_at"], - "updated_at": row["updated_at"], - "similarity_score": 0.0, # No similarity calculation without sqlite-vec - } - ) - - return results - - def create_knowledge_base( - self, - knowledge_base_id: str, - project_id: str, - name: str, - description: str = "", - metadata: dict | None = None, - ) -> dict[str, Any]: - """Create a new knowledge base.""" - knowledge_base_id = knowledge_base_id or str(uuid.uuid4()) - metadata_json = json.dumps(metadata or {}) - - self.conn.execute( - """ - INSERT INTO knowledge_bases (id, kb_id, project_id, name, description, metadata) - VALUES (?, ?, ?, ?, ?, ?) - """, - ( - str(uuid.uuid4()), - knowledge_base_id, - project_id, - name, - description, - metadata_json, - ), - ) - self.conn.commit() - - return { - "id": knowledge_base_id, - "kb_id": knowledge_base_id, - "project_id": project_id, - "name": name, - "description": description, - "metadata": metadata or {}, - "created_at": datetime.now(timezone.utc).isoformat(), - "updated_at": datetime.now(timezone.utc).isoformat(), - } - - def get_knowledge_bases(self, project_id: str) -> list[dict[str, Any]]: - """Get all knowledge bases for a project.""" - cursor = self.conn.execute( - "SELECT * FROM knowledge_bases WHERE project_id = ?", (project_id,) - ) - rows = cursor.fetchall() - - kbs = [] - for row in rows: - kbs.append( - { - "id": row["id"], - "kb_id": row["kb_id"], - "project_id": row["project_id"], - "name": row["name"], - "description": row["description"], - "metadata": json.loads(row["metadata"]) if row["metadata"] else {}, - "created_at": row["created_at"], - "updated_at": row["updated_at"], - } - ) - - return kbs - - def add_fact( - self, - fact_id: str, - knowledge_base_id: str, - content: str, - embedding: list[float], - metadata: dict | None = None, - confidence: float = 1.0, - ) -> dict[str, Any]: - """Add a fact to a knowledge base.""" - fact_id = fact_id or str(uuid.uuid4()) - metadata_json = json.dumps(metadata or {}) - embedding_blob = ( - np.array(embedding, dtype=np.float32).tobytes() if embedding else None - ) - - self.conn.execute( - """ - INSERT INTO facts - (id, fact_id, knowledge_base_id, content, embedding, metadata, confidence) - VALUES (?, ?, ?, ?, ?, ?, ?) - """, - ( - str(uuid.uuid4()), - fact_id, - knowledge_base_id, - content, - embedding_blob, - metadata_json, - confidence, - ), - ) - self.conn.commit() - - return { - "id": fact_id, - "fact_id": fact_id, - "knowledge_base_id": knowledge_base_id, - "content": content, - "confidence": confidence, - "metadata": metadata or {}, - "created_at": datetime.now(timezone.utc).isoformat(), - "updated_at": datetime.now(timezone.utc).isoformat(), - } - - def search_facts( - self, - knowledge_base_id: str, - query_embedding: list[float] | None = None, - limit: int = 10, - ) -> list[dict[str, Any]]: - """Search facts in a knowledge base.""" - if query_embedding is not None: - # If sqlite-vec is available and we have an embedding, use vector search - query_array = np.array(query_embedding, dtype=np.float32) - query_blob = query_array.tobytes() - - try: - cursor = self.conn.execute( - """ - SELECT *, vec_distance_L2(embedding, ?) as distance - FROM facts - WHERE knowledge_base_id = ? AND embedding IS NOT NULL - ORDER BY distance ASC - LIMIT ? - """, - [query_blob, knowledge_base_id, limit], - ) - - rows = cursor.fetchall() - - results = [] - for row in rows: - distance = row["distance"] - similarity = 1 / ( - 1 + distance - ) # Convert distance to similarity score - - results.append( - { - "fact_id": row["fact_id"], - "knowledge_base_id": row["knowledge_base_id"], - "content": row["content"], - "confidence": row["confidence"], - "metadata": ( - json.loads(row["metadata"]) if row["metadata"] else {} - ), - "created_at": row["created_at"], - "updated_at": row["updated_at"], - "similarity_score": similarity, - } - ) - - return results - except sqlite3.Error: - # Fallback to basic search - pass - - # Basic search without vector similarity - cursor = self.conn.execute( - "SELECT * FROM facts WHERE knowledge_base_id = ? ORDER BY created_at DESC LIMIT ?", - (knowledge_base_id, limit), - ) - rows = cursor.fetchall() - - results = [] - for row in rows: - results.append( - { - "fact_id": row["fact_id"], - "knowledge_base_id": row["knowledge_base_id"], - "content": row["content"], - "confidence": row["confidence"], - "metadata": json.loads(row["metadata"]) if row["metadata"] else {}, - "created_at": row["created_at"], - "updated_at": row["updated_at"], - "similarity_score": 0.0, # No similarity without embedding - } - ) - - return results - - def delete_fact(self, fact_id: str, knowledge_base_id: str) -> bool: - """Delete a fact from a knowledge base.""" - cursor = self.conn.execute( - "DELETE FROM facts WHERE fact_id = ? AND knowledge_base_id = ?", - (fact_id, knowledge_base_id), - ) - self.conn.commit() - - return cursor.rowcount > 0 - - def update_memory( - self, - memory_id: str, - user_id: str, - project_id: str, - content: str | None = None, - metadata: dict | None = None, - importance: float | None = None, - ) -> dict[str, Any] | None: - """Update a memory in the database.""" - # Check if memory exists and belongs to user/project - cursor = self.conn.execute( - "SELECT * FROM memories WHERE memory_id = ? AND user_id = ? AND project_id = ?", - (memory_id, user_id, project_id), - ) - row = cursor.fetchone() - - if not row: - return None - - # Build update query - updates = [] - params = [] - - if content is not None: - updates.append("content = ?") - params.append(content) - - if metadata is not None: - updates.append("metadata = ?") - params.append(json.dumps(metadata)) - - if importance is not None: - updates.append("importance = ?") - params.append(importance) - - if updates: - updates.append("updated_at = CURRENT_TIMESTAMP") - query = f"UPDATE memories SET {', '.join(updates)} WHERE memory_id = ?" - params.append(memory_id) - - self.conn.execute(query, params) - self.conn.commit() - - # Return updated memory - cursor = self.conn.execute( - "SELECT * FROM memories WHERE memory_id = ?", (memory_id,) - ) - row = cursor.fetchone() - - if row: - return { - "id": row["id"], - "memory_id": row["memory_id"], - "user_id": row["user_id"], - "project_id": row["project_id"], - "content": row["content"], - "importance": row["importance"], - "context": json.loads(row["context"]) if row["context"] else {}, - "metadata": json.loads(row["metadata"]) if row["metadata"] else {}, - "source": row["source"], - "timestamp": row["timestamp"], - "created_at": row["created_at"], - "updated_at": row["updated_at"], - } - - return None - - def delete_memory( - self, - memory_id: str, - user_id: str, - project_id: str, - ) -> bool: - """Delete a memory from the database.""" - cursor = self.conn.execute( - "DELETE FROM memories WHERE memory_id = ? AND user_id = ? AND project_id = ?", - (memory_id, user_id, project_id), - ) - self.conn.commit() - return cursor.rowcount > 0 - - def create_chat_session( - self, - session_id: str, - user_id: str, - project_id: str, - metadata: dict | None = None, - ) -> dict[str, Any]: - """Create a new chat session.""" - session_id = session_id or str(uuid.uuid4()) - metadata_json = json.dumps(metadata or {}) - - self.conn.execute( - """ - INSERT INTO chat_sessions (id, session_id, user_id, project_id, metadata) - VALUES (?, ?, ?, ?, ?) - """, - (str(uuid.uuid4()), session_id, user_id, project_id, metadata_json), - ) - self.conn.commit() - - return { - "id": session_id, - "session_id": session_id, - "user_id": user_id, - "project_id": project_id, - "metadata": metadata or {}, - "created_at": datetime.now(timezone.utc).isoformat(), - "updated_at": datetime.now(timezone.utc).isoformat(), - } - - def add_chat_message( - self, - message_id: str, - session_id: str, - role: str, - content: str, - embedding: list[float], - metadata: dict | None = None, - ) -> dict[str, Any]: - """Add a message to a chat session.""" - message_id = message_id or str(uuid.uuid4()) - metadata_json = json.dumps(metadata or {}) - embedding_blob = ( - np.array(embedding, dtype=np.float32).tobytes() if embedding else None - ) - - self.conn.execute( - """ - INSERT INTO chat_messages - (id, message_id, session_id, role, content, embedding, metadata) - VALUES (?, ?, ?, ?, ?, ?, ?) - """, - ( - str(uuid.uuid4()), - message_id, - session_id, - role, - content, - embedding_blob, - metadata_json, - ), - ) - self.conn.commit() - - return { - "id": message_id, - "message_id": message_id, - "session_id": session_id, - "role": role, - "content": content, - "metadata": metadata or {}, - "timestamp": datetime.now(timezone.utc).isoformat(), - } - - def get_chat_messages( - self, - session_id: str, - limit: int | None = None, - ) -> list[dict[str, Any]]: - """Get messages from a chat session.""" - query = ( - "SELECT * FROM chat_messages WHERE session_id = ? ORDER BY timestamp ASC" - ) - params = [session_id] - - if limit: - query += f" LIMIT {limit}" - - cursor = self.conn.execute(query, params) - rows = cursor.fetchall() - - messages = [] - for row in rows: - messages.append( - { - "id": row["id"], - "message_id": row["message_id"], - "session_id": row["session_id"], - "role": row["role"], - "content": row["content"], - "metadata": json.loads(row["metadata"]) if row["metadata"] else {}, - "timestamp": row["timestamp"], - } - ) - - return messages - - def search_chat_messages( - self, - session_id: str, - query_embedding: list[float], - limit: int = 10, - ) -> list[dict[str, Any]]: - """Search messages in a chat session by similarity.""" - query_array = np.array(query_embedding, dtype=np.float32) - query_blob = query_array.tobytes() - - # If sqlite-vec is available, use vector similarity search - try: - cursor = self.conn.execute( - """ - SELECT *, vec_distance_L2(embedding, ?) as distance - FROM chat_messages - WHERE session_id = ? AND embedding IS NOT NULL - ORDER BY distance ASC - LIMIT ? - """, - [query_blob, session_id, limit], - ) - - rows = cursor.fetchall() - - results = [] - for row in rows: - distance = row["distance"] - similarity = 1 / (1 + distance) # Convert distance to similarity score - - results.append( - { - "id": row["id"], - "message_id": row["message_id"], - "session_id": row["session_id"], - "role": row["role"], - "content": row["content"], - "metadata": ( - json.loads(row["metadata"]) if row["metadata"] else {} - ), - "timestamp": row["timestamp"], - "similarity_score": similarity, - } - ) - - return results - except sqlite3.Error: - # Fallback to basic search without vector similarity - cursor = self.conn.execute( - "SELECT * FROM chat_messages WHERE session_id = ? ORDER BY timestamp DESC LIMIT ?", - (session_id, limit), - ) - rows = cursor.fetchall() - - results = [] - for row in rows: - results.append( - { - "id": row["id"], - "message_id": row["message_id"], - "session_id": row["session_id"], - "role": row["role"], - "content": row["content"], - "metadata": ( - json.loads(row["metadata"]) if row["metadata"] else {} - ), - "timestamp": row["timestamp"], - "similarity_score": 0.0, # No similarity calculation without sqlite-vec - } - ) - - return results - - def close(self) -> None: - """Close the database connection.""" - if self.conn: - self.conn.close() - - -# Singleton instance -_sqlite_client: Optional[SQLiteMemoryClient] = None - - -def get_sqlite_client() -> SQLiteMemoryClient: - """Get or create the singleton SQLite client.""" - global _sqlite_client - if _sqlite_client is None: - _sqlite_client = SQLiteMemoryClient() - return _sqlite_client diff --git a/pkg/hanzo-memory/src/hanzo_memory/graph_links.py b/pkg/hanzo-memory/src/hanzo_memory/graph_links.py deleted file mode 100644 index 5c26adb65..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/graph_links.py +++ /dev/null @@ -1,147 +0,0 @@ -"""Hanzo Brain โ€” typed-link extractor (Python port of @hanzo/bot-graph-links). - -Zero-LLM. Pure regex + role inference. Mirrors the TS extractor 1:1 so a -brain.db written by the bot is consumed identically by the Python SDK -and vice versa. Suitable for >10K pages/sec. - -Edge types: mentions / attended / works_at / invested_in / founded / advises. - -Schema target โ€” `edges` table: - source TEXT, target TEXT, type TEXT, evidence TEXT, - PRIMARY KEY (source, target, type) -""" - -from __future__ import annotations - -import re -import unicodedata -from dataclasses import dataclass -from typing import Iterable, Literal - -EdgeType = Literal["mentions", "attended", "works_at", "invested_in", "founded", "advises"] - - -@dataclass(frozen=True) -class Edge: - source: str - target: str - type: EdgeType - evidence: str | None = None - - -# โ”€โ”€ Patterns โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -_MD_LINK = re.compile(r"\[([^\]]+)\]\(([^)#\s]+)\)") -_BARE_SLUG = re.compile( - r"(? str: - """Match gbrain's slug convention โ€” lowercase ascii dashes.""" - s = unicodedata.normalize("NFKD", s) - s = "".join(c for c in s if not unicodedata.combining(c)) - s = s.lower().replace("&", " and ") - s = re.sub(r"[^a-z0-9]+", "-", s) - s = s.strip("-") - return s[:80] - - -def _strip_code(md: str) -> str: - md = _CODE_FENCE.sub("", md) - md = _INLINE_CODE.sub("", md) - return md - - -def _infer_category(edge_type: EdgeType) -> str: - if edge_type in ("founded", "invested_in", "works_at"): - return "companies" - if edge_type == "advises": - return "people" - return "entities" - - -def extract_edges( - slug: str, - content: str, - page_type: str | None = None, -) -> list[Edge]: - """Extract typed edges from one page. Pure โ€” no I/O, no LLM.""" - cleaned = _strip_code(content) - seen: dict[tuple[str, EdgeType], Edge] = {} - - def add(e: Edge) -> None: - key = (e.target, e.type) - seen.setdefault(key, e) - - # 1. Markdown links โ€” `mentions`, or `attended` on meeting pages. - for m in _MD_LINK.finditer(cleaned): - target = m.group(2).strip() - if target.startswith("http") or target.startswith("/") or "/" not in target: - continue - et: EdgeType = "attended" if page_type == "meeting" else "mentions" - add(Edge(source=slug, target=target, type=et, evidence=m.group(0))) - - # 2. Bare slug refs (`people/alice`). - for m in _BARE_SLUG.finditer(cleaned): - et = "attended" if page_type == "meeting" else "mentions" - add(Edge(source=slug, target=m.group(1).lower(), type=et, evidence=m.group(0))) - - # 3. Role inference. - for pat, etype in _ROLE_PATTERNS: - m = pat.search(cleaned) - if not m: - continue - raw = m.group(1).strip().rstrip(".,;:!?") - target_slug = f"{_infer_category(etype)}/{slugify(raw)}" - if target_slug.endswith("/"): - continue - add(Edge(source=slug, target=target_slug, type=etype, evidence=m.group(0))) - - return list(seen.values()) - - -def reconcile(prior: Iterable[Edge], next_: Iterable[Edge]) -> tuple[list[Edge], list[Edge]]: - """Return (add, remove) deltas between two edge sets. - - Used by the persistence layer to stale-delete dropped refs when a - page is edited โ€” same contract as the TS extractor's `reconcile`. - """ - prior_set = {(e.source, e.target, e.type) for e in prior} - next_list = list(next_) - next_set = {(e.source, e.target, e.type) for e in next_list} - prior_list = [e for e in prior if (e.source, e.target, e.type) not in next_set] - add = [e for e in next_list if (e.source, e.target, e.type) not in prior_set] - return add, prior_list diff --git a/pkg/hanzo-memory/src/hanzo_memory/mcp/__init__.py b/pkg/hanzo-memory/src/hanzo_memory/mcp/__init__.py deleted file mode 100644 index 9019d7c84..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/mcp/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""MCP server for Hanzo Memory service.""" - -from .server import MCPMemoryServer - -__all__ = ["MCPMemoryServer"] diff --git a/pkg/hanzo-memory/src/hanzo_memory/mcp/__main__.py b/pkg/hanzo-memory/src/hanzo_memory/mcp/__main__.py deleted file mode 100644 index a5a0b5d00..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/mcp/__main__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""MCP server entry point.""" - -from .server import main - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-memory/src/hanzo_memory/mcp/server.py b/pkg/hanzo-memory/src/hanzo_memory/mcp/server.py deleted file mode 100644 index 21f8c3734..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/mcp/server.py +++ /dev/null @@ -1,507 +0,0 @@ -"""MCP server for Hanzo Memory service.""" - -import json -import asyncio -from typing import Any, Literal -from uuid import uuid4 - -from mcp.server import Server -from mcp.server.models import InitializationOptions, ServerCapabilities -from mcp.types import ( - TextContent, - Tool, -) -from structlog import get_logger - -from ..config import settings -from ..db.sqlite_client import SQLiteMemoryClient, get_sqlite_client -from ..models.knowledge import Fact, FactCreate, KnowledgeBase -from ..models.memory import Memory, MemoryCreate -from ..services.embeddings import EmbeddingService, get_embedding_service -from ..services.llm import LLMService, get_llm_service - -# Constants -ServiceName = "hanzo-memory" - -logger = get_logger() - - -def get_db_client() -> SQLiteMemoryClient: - """Get the database client.""" - return get_sqlite_client() - - -class MCPMemoryServer: - """MCP server for memory operations.""" - - def __init__(self) -> None: - """Initialize the MCP server.""" - self.server: Server = Server(settings.mcp_server_name) - self.db_client = get_db_client() - self.embedding_service = EmbeddingService() - self.llm_service = LLMService() - self._setup_handlers() - - def _setup_handlers(self) -> None: - """Set up server handlers.""" - - @self.server.list_tools() # type: ignore[misc] - async def handle_list_tools() -> list[Tool]: - """Return available tools.""" - return [ - Tool( - name="memory", - description="""Unified memory tool for storing, retrieving, and managing information. -Supports the following actions: -- remember: Store a new memory -- recall: Search for memories -- delete: Delete a memory -- create_project: Create a new project container -- create_kb: Create a structured knowledge base -- add_fact: Add a fact to a knowledge base -- search_facts: Search within a knowledge base -- delete_fact: Remove a fact from a knowledge base -- summarize: Analyze content and generate knowledge entries""", - inputSchema={ - "type": "object", - "properties": { - "action": { - "type": "string", - "enum": [ - "remember", - "recall", - "delete", - "create_project", - "create_kb", - "add_fact", - "search_facts", - "delete_fact", - "summarize", - ], - "description": "The action to perform", - }, - "user_id": { - "type": "string", - "description": "User ID (required for all actions)", - }, - "project_id": { - "type": "string", - "description": "Project ID (optional for some actions)", - }, - "content": { - "type": "string", - "description": "Content for storage or analysis", - }, - "query": { - "type": "string", - "description": "Search query for recall/search purposes", - }, - "id": { - "type": "string", - "description": "ID of item to delete (memory_id or fact_id)", - }, - "kb_id": { - "type": "string", - "description": "Knowledge base ID for fact operations", - }, - "name": { - "type": "string", - "description": "Name for new project or knowledge base", - }, - "description": { - "type": "string", - "description": "Description for new project or KB", - }, - "metadata": { - "type": "object", - "description": "Additional metadata", - }, - "limit": { - "type": "integer", - "description": "Limit results", - "default": 10, - }, - "importance": { - "type": "number", - "description": "Importance score (0-10)", - "default": 1.0, - }, - "parent_id": { - "type": "string", - "description": "Parent fact ID", - }, - "context": { - "type": "string", - "description": "Context for summarization", - }, - }, - "required": ["action", "user_id"], - }, - ), - ] - - @self.server.call_tool() # type: ignore[misc] - async def handle_call_tool( - name: str, arguments: dict[str, Any] | None = None - ) -> list[TextContent]: - """Handle tool calls.""" - try: - if name != "memory": - return [ - TextContent( - type="text", - text=json.dumps({"error": f"Unknown tool: {name}"}), - ) - ] - - args = arguments or {} - action = args.get("action") - - if action == "remember": - result = await self._handle_remember(args) - elif action == "recall": - result = await self._handle_recall(args) - elif action == "delete": - result = await self._handle_delete_memory(args) - elif action == "create_project": - result = await self._handle_create_project(args) - elif action == "create_kb": - result = await self._handle_create_knowledge_base(args) - elif action == "add_fact": - result = await self._handle_add_fact(args) - elif action == "search_facts": - result = await self._handle_search_facts(args) - elif action == "delete_fact": - result = await self._handle_delete_fact(args) - elif action == "summarize": - result = await self._handle_summarize_for_knowledge(args) - else: - return [ - TextContent( - type="text", - text=json.dumps({"error": f"Unknown action: {action}"}), - ) - ] - - return [TextContent(type="text", text=json.dumps(result, indent=2))] - except Exception as e: - logger.error( - f"Error handling memory action {arguments.get('action')}: {e}" - ) - return [TextContent(type="text", text=json.dumps({"error": str(e)}))] - - async def _handle_remember(self, args: dict[str, Any]) -> dict[str, Any]: - """Handle remember action.""" - user_id = args["user_id"] - project_id = args.get("project_id", "default") - content = args.get("content") - if not content: - raise ValueError("content is required for remember action") - - metadata = args.get("metadata", {}) - importance = args.get("importance", 1.0) - - # Run embedding generation in thread pool - embedding = ( - await asyncio.to_thread(self.embedding_service.embed_text, content) - )[0] - memory_id = str(uuid4()) - - # Run DB operations in thread pool - def _store(): - self.db_client.create_memories_table(user_id) - self.db_client.add_memory( - memory_id=memory_id, - user_id=user_id, - project_id=project_id, - content=content, - embedding=embedding, - metadata=metadata, - importance=importance, - ) - - await asyncio.to_thread(_store) - - return { - "success": True, - "memory_id": memory_id, - "message": "Memory stored successfully", - } - - async def _handle_recall(self, args: dict[str, Any]) -> dict[str, Any]: - """Handle recall action.""" - user_id = args["user_id"] - project_id = args.get("project_id") - query = args.get("query") - if not query: - raise ValueError("query is required for recall action") - - limit = args.get("limit", 10) - - # Generate query embedding - query_embedding = ( - await asyncio.to_thread(self.embedding_service.embed_text, query) - )[0] - - # Search memories - results_df = await asyncio.to_thread( - self.db_client.search_memories, - user_id=user_id, - query_embedding=query_embedding, - project_id=project_id, - limit=limit, - ) - - # Convert results - memories = [] - if isinstance(results_df, list): - memories = results_df # It's a list of dicts from sqlite client fallback or non-polars return - elif not results_df.is_empty(): - for row in results_df.to_dicts(): - memories.append( - { - "memory_id": row.get("memory_id"), - "content": row.get("content"), - "metadata": ( - json.loads(row.get("metadata", "{}")) - if isinstance(row.get("metadata"), str) - else row.get("metadata", {}) - ), - "importance": row.get("importance", 1.0), - "similarity_score": row.get( - "_similarity", row.get("similarity_score", 0.0) - ), - } - ) - - return { - "success": True, - "memories": memories, - "count": len(memories), - } - - async def _handle_delete_memory(self, args: dict[str, Any]) -> dict[str, Any]: - """Handle delete memory action.""" - user_id = args["user_id"] - project_id = args.get("project_id", "default") - memory_id = args.get("id") - - if not memory_id: - raise ValueError("id is required for delete action") - - success = await asyncio.to_thread( - self.db_client.delete_memory, - memory_id=memory_id, - user_id=user_id, - project_id=project_id, - ) - - return { - "success": success, - "message": "Memory deleted" if success else "Memory not found", - } - - async def _handle_create_project(self, args: dict[str, Any]) -> dict[str, Any]: - """Handle create project action.""" - user_id = args["user_id"] - name = args.get("name") - if not name: - raise ValueError("name is required for create_project action") - - description = args.get("description", "") - metadata = args.get("metadata", {}) - - project_id = str(uuid4()) - - await asyncio.to_thread( - self.db_client.create_project, - project_id=project_id, - user_id=user_id, - name=name, - description=description, - metadata=metadata, - ) - - return { - "success": True, - "project_id": project_id, - "message": "Project created successfully", - } - - async def _handle_create_knowledge_base( - self, args: dict[str, Any] - ) -> dict[str, Any]: - """Handle create knowledge base action.""" - user_id = args["user_id"] - project_id = args.get("project_id", "default") - name = args.get("name") - if not name: - raise ValueError("name is required for create_kb action") - - description = args.get("description", "") - - kb_id = str(uuid4()) - - await asyncio.to_thread( - self.db_client.create_knowledge_base, - kb_id=kb_id, - user_id=user_id, - project_id=project_id, - name=name, - description=description, - ) - - return { - "success": True, - "kb_id": kb_id, - "message": "Knowledge base created successfully", - } - - async def _handle_add_fact(self, args: dict[str, Any]) -> dict[str, Any]: - """Handle add fact action.""" - kb_id = args.get("kb_id") - if not kb_id: - raise ValueError("kb_id is required for add_fact action") - - content = args.get("content") - if not content: - raise ValueError("content is required for add_fact action") - - parent_id = args.get("parent_id") - metadata = args.get("metadata", {}) - - embedding = ( - await asyncio.to_thread(self.embedding_service.embed_text, content) - )[0] - fact_id = str(uuid4()) - - await asyncio.to_thread( - self.db_client.add_fact, - fact_id=fact_id, - kb_id=kb_id, - content=content, - embedding=embedding, - parent_id=parent_id, - metadata=metadata, - ) - - return { - "success": True, - "fact_id": fact_id, - "message": "Fact added successfully", - } - - async def _handle_search_facts(self, args: dict[str, Any]) -> dict[str, Any]: - """Handle search facts action.""" - kb_id = args.get("kb_id") - if not kb_id: - raise ValueError("kb_id is required for search_facts action") - - query = args.get("query") - if not query: - raise ValueError("query is required for search_facts action") - - limit = args.get("limit", 10) - - query_embedding = ( - await asyncio.to_thread(self.embedding_service.embed_text, query) - )[0] - - results_df = await asyncio.to_thread( - self.db_client.search_facts, - kb_id=kb_id, - query_embedding=query_embedding, - limit=limit, - ) - - facts = [] - if isinstance(results_df, list): - facts = results_df - elif not results_df.is_empty(): - for row in results_df.to_dicts(): - facts.append( - { - "fact_id": row.get("fact_id"), - "content": row.get("content"), - "parent_id": row.get("parent_id"), - "metadata": ( - json.loads(row.get("metadata", "{}")) - if isinstance(row.get("metadata"), str) - else row.get("metadata", {}) - ), - "similarity_score": row.get( - "_similarity", row.get("similarity_score", 0.0) - ), - } - ) - - return { - "success": True, - "facts": facts, - "count": len(facts), - } - - async def _handle_delete_fact(self, args: dict[str, Any]) -> dict[str, Any]: - """Handle delete fact action.""" - kb_id = args.get("kb_id") - fact_id = args.get("id") - if not kb_id or not fact_id: - raise ValueError("kb_id and id are required for delete_fact action") - - success = await asyncio.to_thread( - self.db_client.delete_fact, fact_id=fact_id, knowledge_base_id=kb_id - ) - - return { - "success": success, - "message": "Fact deleted" if success else "Fact not found", - } - - async def _handle_summarize_for_knowledge( - self, args: dict[str, Any] - ) -> dict[str, Any]: - """Handle summarize for knowledge action.""" - content = args.get("content") - if not content: - raise ValueError("content is required for summarize action") - - context = args.get("context") - skip_summarization = args.get("skip_summarization", False) - provided_summary = args.get("provided_summary") - - # LLM service might be IO bound depending on implementation, assume safe to verify async later or wrap now - # Wrapping just in case - result = await asyncio.to_thread( - self.llm_service.summarize_for_knowledge, - content=content, - context=context, - skip_summarization=skip_summarization, - provided_summary=provided_summary, - ) - - return result - - async def run(self) -> None: - """Run the MCP server.""" - from mcp.server.stdio import stdio_server - - async with stdio_server() as (read_stream, write_stream): - await self.server.run( - read_stream, - write_stream, - InitializationOptions( - server_name=settings.mcp_server_name, - server_version=settings.mcp_server_version, - capabilities=ServerCapabilities(), - ), - ) - - -def main() -> None: - """Run the MCP memory server.""" - server = MCPMemoryServer() - asyncio.run(server.run()) - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-memory/src/hanzo_memory/memory.py b/pkg/hanzo-memory/src/hanzo_memory/memory.py deleted file mode 100644 index 7b0a39616..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/memory.py +++ /dev/null @@ -1,251 +0,0 @@ -"""Unified memory interface with backend selection. - -This module provides a clean API for accessing different memory backends -using a simple syntax like: - memory['lancedb'] # Access LanceDB backend - memory['kuzudb'] # Access KuzuDB backend - memory['infinity'] # Access InfinityDB backend - memory.local # Access local backend (default) -""" - -from typing import Dict, Optional, Any -from structlog import get_logger - -from .db.factory import get_db_client, reset_db_client, list_available_backends -from .db.base import BaseVectorDB -from .services.memory import MemoryService -from .services.embeddings import EmbeddingService -from .services.llm import LLMService - -logger = get_logger() - - -class MemoryBackendProxy: - """Proxy for accessing specific memory backends.""" - - def __init__(self, backend_name: str, config: Optional[Dict] = None): - """Initialize backend proxy. - - Args: - backend_name: Name of the backend to use - config: Optional backend configuration - """ - self.backend_name = backend_name - self.config = config or {} - self._client = None - self._service = None - - @property - def client(self) -> BaseVectorDB: - """Get the database client for this backend.""" - if self._client is None: - reset_db_client() - self._client = get_db_client(backend=self.backend_name, config=self.config) - return self._client - - @property - def service(self) -> MemoryService: - """Get the memory service for this backend.""" - if self._service is None: - # Initialize services - embeddings = EmbeddingService() - llm = LLMService() - - # Create service with this backend - self._service = MemoryService() - self._service.db = self.client - self._service.embeddings = embeddings - self._service.llm = llm - - return self._service - - async def initialize(self): - """Initialize the backend.""" - await self.client.initialize() - logger.info(f"Initialized {self.backend_name} backend") - - async def close(self): - """Close the backend connection.""" - if self._client: - await self._client.close() - logger.info(f"Closed {self.backend_name} backend") - - def __repr__(self): - """String representation.""" - return f"" - - -class Memory: - """Main memory interface with backend selection. - - Usage: - # Using dictionary syntax - memory['lancedb'] # Get LanceDB backend - memory['kuzudb'] # Get KuzuDB backend - memory['infinity'] # Get InfinityDB backend - - # Using attribute syntax - memory.lancedb # Get LanceDB backend - memory.kuzudb # Get KuzuDB backend - memory.infinity # Get InfinityDB backend - memory.local # Get local backend (default) - - # List available backends - memory.backends() # Returns list of available backends - - # Use a specific backend - lance = memory['lancedb'] - await lance.initialize() - await lance.service.create_memory(...) - - # Or with context manager (auto initialize/close) - async with memory.use('lancedb') as backend: - await backend.service.create_memory(...) - """ - - def __init__(self, default_backend: str = "local"): - """Initialize memory interface. - - Args: - default_backend: Default backend to use - """ - self.default_backend = default_backend - self._backends: Dict[str, MemoryBackendProxy] = {} - - def __getitem__(self, backend_name: str) -> MemoryBackendProxy: - """Get a backend using dictionary syntax. - - Args: - backend_name: Name of the backend - - Returns: - Backend proxy instance - """ - if backend_name not in self._backends: - self._backends[backend_name] = MemoryBackendProxy(backend_name) - return self._backends[backend_name] - - def __getattr__(self, backend_name: str) -> MemoryBackendProxy: - """Get a backend using attribute syntax. - - Args: - backend_name: Name of the backend - - Returns: - Backend proxy instance - """ - # Handle special attributes - if backend_name.startswith("_"): - raise AttributeError( - f"'{self.__class__.__name__}' object has no attribute '{backend_name}'" - ) - - # Convert attribute name to backend name (e.g., 'infinity_db' -> 'infinitydb') - backend_name = backend_name.replace("_", "") - - return self[backend_name] - - def backends(self) -> Dict[str, Dict]: - """List all available backends. - - Returns: - Dictionary of backend names to their info - """ - return list_available_backends() - - def use(self, backend_name: str, config: Optional[Dict] = None): - """Context manager for using a specific backend. - - Args: - backend_name: Name of the backend - config: Optional backend configuration - - Returns: - Async context manager for the backend - """ - return MemoryBackendContext(backend_name, config) - - def __repr__(self): - """String representation.""" - backends = list(self.backends().keys()) - return f"" - - -class MemoryBackendContext: - """Async context manager for using a memory backend.""" - - def __init__(self, backend_name: str, config: Optional[Dict] = None): - """Initialize context. - - Args: - backend_name: Name of the backend - config: Optional backend configuration - """ - self.backend = MemoryBackendProxy(backend_name, config) - - async def __aenter__(self): - """Enter context - initialize backend.""" - await self.backend.initialize() - return self.backend - - async def __aexit__(self, exc_type, exc_val, exc_tb): - """Exit context - close backend.""" - await self.backend.close() - - -# Global memory instance -memory = Memory() - - -# Convenience functions for common operations -async def remember(content: str, backend: str = "local", **kwargs): - """Quick function to remember something. - - Args: - content: Content to remember - backend: Backend to use (default: local) - **kwargs: Additional parameters for memory creation - - Returns: - Created memory object - """ - async with memory.use(backend) as mem: - from .models.memory import MemoryCreate - - memory_obj = MemoryCreate(content=content, **kwargs) - return await mem.service.create_memory( - memory=memory_obj, - project_id=kwargs.get("project_id", "default"), - user_id=kwargs.get("user_id", "default"), - ) - - -async def recall(query: str, backend: str = "local", **kwargs): - """Quick function to recall memories. - - Args: - query: Search query - backend: Backend to use (default: local) - **kwargs: Additional search parameters - - Returns: - List of matching memories - """ - async with memory.use(backend) as mem: - return await mem.service.search_memories( - query=query, - user_id=kwargs.get("user_id", "default"), - project_id=kwargs.get("project_id", "default"), - limit=kwargs.get("limit", 10), - ) - - -# Export main components -__all__ = [ - "Memory", - "memory", - "remember", - "recall", - "MemoryBackendProxy", - "MemoryBackendContext", -] diff --git a/pkg/hanzo-memory/src/hanzo_memory/models/__init__.py b/pkg/hanzo-memory/src/hanzo_memory/models/__init__.py deleted file mode 100644 index 5acbdf485..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/models/__init__.py +++ /dev/null @@ -1,106 +0,0 @@ -"""Models package.""" - -from .base import ProjectScopedModel, TimestampedModel, UserScopedModel -from .chat import ( - ChatHistoryRequest, - ChatMessage, - ChatMessageBase, - ChatMessageCreate, - ChatSearchRequest, - ChatSession, - ChatSessionCreate, - ChatSessionList, -) -from .knowledge import ( - AddKnowledgeRequest, - CreateKnowledgeBaseRequest, - DeleteKnowledgeRequest, - Fact, - FactBase, - FactCreate, - FactRelation, - FactUpdate, - FactWithScore, - GetKnowledgeRequest, - IngestKnowledgeRequest, - KnowledgeBase, - KnowledgeBaseBase, - KnowledgeBaseCreate, - KnowledgeBaseUpdate, - ListKnowledgeBasesRequest, -) -from .memory import ( - AddMemoriesRequest, - DeleteMemoryRequest, - DeleteUserRequest, - GetMemoriesRequest, - Memory, - MemoryBase, - MemoryCreate, - MemoryListResponse, - MemoryResponse, - MemoryUpdate, - MemoryWithScore, - RememberRequest, - UpdateMemoryRequest, -) -from .project import ( - Project, - ProjectBase, - ProjectCreate, - ProjectList, - ProjectUpdate, -) - -__all__ = [ - # Base - "TimestampedModel", - "UserScopedModel", - "ProjectScopedModel", - # Project - "Project", - "ProjectBase", - "ProjectCreate", - "ProjectUpdate", - "ProjectList", - # Memory - "Memory", - "MemoryBase", - "MemoryCreate", - "MemoryUpdate", - "MemoryWithScore", - "MemoryResponse", - "MemoryListResponse", - "RememberRequest", - "AddMemoriesRequest", - "GetMemoriesRequest", - "DeleteMemoryRequest", - "DeleteUserRequest", - "UpdateMemoryRequest", - # Knowledge - "KnowledgeBase", - "KnowledgeBaseBase", - "KnowledgeBaseCreate", - "KnowledgeBaseUpdate", - "Fact", - "FactBase", - "FactCreate", - "FactUpdate", - "FactWithScore", - "FactRelation", - "CreateKnowledgeBaseRequest", - "ListKnowledgeBasesRequest", - "AddKnowledgeRequest", - "GetKnowledgeRequest", - "DeleteKnowledgeRequest", - "IngestKnowledgeRequest", - # Chat - "ChatMessage", - "ChatMessageBase", - "ChatMessageCreate", - "ChatSession", - "ChatSessionCreate", - "ChatSessionList", - "ChatHistoryRequest", - "ChatSearchRequest", -] diff --git a/pkg/hanzo-memory/src/hanzo_memory/models/base.py b/pkg/hanzo-memory/src/hanzo_memory/models/base.py deleted file mode 100644 index 7b0b54afd..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/models/base.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Base models for Hanzo Memory Service.""" - -from datetime import datetime - -from pydantic import BaseModel, Field - - -class TimestampedModel(BaseModel): - """Base model with timestamps.""" - - created_at: datetime = Field(default_factory=datetime.utcnow) - updated_at: datetime = Field(default_factory=datetime.utcnow) - - -class UserScopedModel(BaseModel): - """Base model for user-scoped resources.""" - - user_id: str = Field(..., description="User ID") - - -class ProjectScopedModel(UserScopedModel): - """Base model for project-scoped resources.""" - - project_id: str = Field(..., description="Project ID") diff --git a/pkg/hanzo-memory/src/hanzo_memory/models/chat.py b/pkg/hanzo-memory/src/hanzo_memory/models/chat.py deleted file mode 100644 index 32bb541b0..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/models/chat.py +++ /dev/null @@ -1,91 +0,0 @@ -"""Chat models.""" - -from typing import Any - -from pydantic import BaseModel, Field - -from .base import ProjectScopedModel - - -class ChatMessageBase(BaseModel): - """Base chat message model.""" - - role: str = Field(..., description="Message role (user, assistant, system)") - content: str = Field(..., description="Message content") - metadata: dict[str, Any] = Field( - default_factory=dict, description="Additional metadata" - ) - - -class ChatMessageCreate(ChatMessageBase): - """Model for creating a chat message.""" - - userid: str = Field(..., description="User ID") - session_id: str = Field(..., description="Chat session ID") - project_id: str | None = Field(None, description="Project ID") - - -class ChatMessage(ChatMessageBase, ProjectScopedModel): - """Complete chat message model.""" - - chat_id: str = Field(..., description="Chat message ID") - session_id: str = Field(..., description="Chat session ID") - embedding: list[float] | None = Field(None, description="Message embedding") - created_at: str = Field(..., description="Creation timestamp") - - model_config = {"from_attributes": True} - - -class ChatSessionCreate(BaseModel): - """Model for creating a chat session.""" - - userid: str = Field(..., description="User ID") - session_id: str | None = Field(None, description="Custom session ID") - project_id: str | None = Field(None, description="Project ID") - title: str | None = Field(None, description="Session title") - metadata: dict[str, Any] = Field( - default_factory=dict, description="Session metadata" - ) - - -class ChatSession(BaseModel): - """Chat session model.""" - - session_id: str = Field(..., description="Session ID") - user_id: str = Field(..., description="User ID") - project_id: str = Field(..., description="Project ID") - title: str | None = Field(None, description="Session title") - metadata: dict[str, Any] = Field( - default_factory=dict, description="Session metadata" - ) - created_at: str = Field(..., description="Creation timestamp") - updated_at: str = Field(..., description="Last update timestamp") - message_count: int = Field(0, description="Number of messages in session") - - -class ChatHistoryRequest(BaseModel): - """Request model for chat history.""" - - user_id: str = Field(..., description="User ID") - session_id: str = Field(..., description="Session ID") - limit: int = Field(100, ge=1, le=1000, description="Max messages to return") - offset: int = Field(0, ge=0, description="Offset for pagination") - - -class ChatSearchRequest(BaseModel): - """Request model for chat search.""" - - user_id: str = Field(..., description="User ID") - query: str = Field(..., description="Search query") - project_id: str | None = Field(None, description="Filter by project ID") - session_id: str | None = Field(None, description="Filter by session ID") - limit: int = Field(10, ge=1, le=100, description="Max results to return") - - -class ChatSessionList(BaseModel): - """Chat session list response.""" - - sessions: list[ChatSession] = Field(..., description="List of chat sessions") - total: int = Field(..., description="Total number of sessions") - page: int = Field(1, description="Current page") - per_page: int = Field(50, description="Items per page") diff --git a/pkg/hanzo-memory/src/hanzo_memory/models/knowledge.py b/pkg/hanzo-memory/src/hanzo_memory/models/knowledge.py deleted file mode 100644 index 0abea288e..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/models/knowledge.py +++ /dev/null @@ -1,142 +0,0 @@ -"""Knowledge base and fact models.""" - -from typing import Any - -from pydantic import BaseModel, Field - -from .base import ProjectScopedModel, TimestampedModel - - -class KnowledgeBaseBase(BaseModel): - """Base knowledge base model.""" - - name: str = Field(..., description="Knowledge base name") - description: str = Field("", description="Knowledge base description") - metadata: dict[str, Any] = Field( - default_factory=dict, description="Additional metadata" - ) - - -class KnowledgeBaseCreate(KnowledgeBaseBase): - """Model for creating a knowledge base.""" - - kb_id: str | None = Field(None, description="Custom knowledge base ID") - - -class KnowledgeBaseUpdate(BaseModel): - """Model for updating a knowledge base.""" - - name: str | None = None - description: str | None = None - metadata: dict[str, Any] | None = None - - -class KnowledgeBase(KnowledgeBaseBase, ProjectScopedModel, TimestampedModel): - """Complete knowledge base model.""" - - kb_id: str = Field(..., description="Knowledge base ID") - fact_count: int = Field(0, description="Number of facts in knowledge base") - - model_config = {"from_attributes": True} - - -class FactBase(BaseModel): - """Base fact model.""" - - content: str = Field(..., description="Fact content") - metadata: dict[str, Any] = Field( - default_factory=dict, description="Additional metadata" - ) - parent_id: str | None = Field(None, description="Parent fact ID") - - -class FactCreate(FactBase): - """Model for creating a fact.""" - - fact_id: str | None = Field(None, description="Custom fact ID") - - -class FactUpdate(BaseModel): - """Model for updating a fact.""" - - content: str | None = None - metadata: dict[str, Any] | None = None - parent_id: str | None = None - - -class Fact(FactBase, TimestampedModel): - """Complete fact model.""" - - fact_id: str = Field(..., description="Fact ID") - kb_id: str = Field(..., description="Knowledge base ID") - embedding: list[float] | None = Field(None, description="Embedding vector") - - model_config = {"from_attributes": True} - - -class FactWithScore(Fact): - """Fact with similarity score.""" - - similarity_score: float = Field(..., description="Similarity score") - - -class FactRelation(BaseModel): - """Fact relation model.""" - - parent_fact_id: str = Field(..., description="Parent fact ID") - child_fact_id: str = Field(..., description="Child fact ID") - relation_type: str = Field("child", description="Relation type") - - -# Knowledge API Request Models - - -class CreateKnowledgeBaseRequest(BaseModel): - """Request model for creating a knowledge base.""" - - userid: str = Field(..., description="User ID") - name: str = Field(..., description="Knowledge base name") - kb_id: str | None = Field(None, description="Custom KB ID") - - -class ListKnowledgeBasesRequest(BaseModel): - """Request model for listing knowledge bases.""" - - userid: str = Field(..., description="User ID") - - -class AddKnowledgeRequest(BaseModel): - """Request model for adding facts to a knowledge base.""" - - userid: str = Field(..., description="User ID") - kb_id: str = Field(..., description="Knowledge base ID") - facts: list[dict[str, Any]] = Field(..., description="Facts to add") - - -class GetKnowledgeRequest(BaseModel): - """Request model for retrieving facts.""" - - userid: str = Field(..., description="User ID") - kb_id: str = Field(..., description="Knowledge base ID") - fact_id: str | None = Field(None, description="Specific fact ID") - subtree: bool = Field(False, description="Include subtree") - query: str | None = Field(None, description="Search query") - limit: int = Field(50, ge=1, le=1000, description="Max facts to return") - - -class DeleteKnowledgeRequest(BaseModel): - """Request model for deleting facts.""" - - userid: str = Field(..., description="User ID") - kb_id: str = Field(..., description="Knowledge base ID") - fact_id: str = Field(..., description="Fact ID to delete") - cascade: bool = Field(False, description="Delete descendants") - - -class IngestKnowledgeRequest(BaseModel): - """Request model for ingesting knowledge from GCS.""" - - userid: str = Field(..., description="User ID") - kb_id: str = Field(..., description="Knowledge base ID") - details: dict[str, str] = Field(..., description="Must include bucketUri") - projecttags: list[str] = Field(default_factory=list, description="Tags for facts") diff --git a/pkg/hanzo-memory/src/hanzo_memory/models/memory.py b/pkg/hanzo-memory/src/hanzo_memory/models/memory.py deleted file mode 100644 index 322ad1197..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/models/memory.py +++ /dev/null @@ -1,145 +0,0 @@ -"""Memory models.""" - -from typing import Any - -from pydantic import BaseModel, Field - -from .base import ProjectScopedModel, TimestampedModel - - -class MemoryBase(BaseModel): - """Base memory model.""" - - content: str = Field(..., description="Memory content") - metadata: dict[str, Any] = Field( - default_factory=dict, description="Additional metadata" - ) - importance: float = Field(1.0, ge=0.0, le=10.0, description="Importance score") - - -class MemoryCreate(MemoryBase): - """Model for creating a memory.""" - - additional_context: str | None = Field( - None, description="Additional context for the memory" - ) - strip_pii: bool = Field(False, description="Strip PII from content") - memory_type: str | None = Field(None, description="Type of memory") - context: dict[str, Any] | None = Field(None, description="Context information") - source: str | None = Field(None, description="Source of the memory") - embedding: list[float] | None = Field(None, description="Embedding vector") - - -class MemoryUpdate(BaseModel): - """Model for updating a memory.""" - - content: str | None = None - metadata: dict[str, Any] | None = None - importance: float | None = None - - -class Memory(MemoryBase, ProjectScopedModel, TimestampedModel): - """Complete memory model.""" - - memory_id: str | None = Field(None, description="Memory ID") - id: str | None = Field(None, description="Memory ID alias") - embedding: list[float] | None = Field(None, description="Embedding vector") - memory_type: str | None = Field(None, description="Type of memory") - context: dict[str, Any] | None = Field(None, description="Context information") - source: str | None = Field(None, description="Source of the memory") - timestamp: str | None = Field(None, description="Timestamp") - - model_config = {"from_attributes": True} - - -class MemoryWithScore(Memory): - """Memory with similarity score.""" - - similarity_score: float = Field(..., description="Similarity score") - - -class MemoryResponse(BaseModel): - """Memory response model with search results.""" - - memory: Memory | None = Field(None, description="Memory object") - similarity: float | None = Field(None, description="Similarity score") - relevance_score: float | None = Field(None, description="Relevance score") - - # Legacy fields for compatibility - user_id: str | None = Field(None, description="User ID") - relevant_memories: list[str | dict[str, str]] | None = Field( - None, description="Relevant memories" - ) - memory_stored: bool | None = Field( - None, description="Whether the memory was stored" - ) - usage_info: dict[str, int] | None = Field(None, description="Usage information") - - -class MemoryListResponse(BaseModel): - """Memory list response.""" - - user_id: str = Field(..., description="User ID") - memories: list[Memory] = Field(..., description="List of memories") - pagination: dict[str, Any] = Field(..., description="Pagination info") - usage_info: dict[str, int] = Field(..., description="Usage information") - - -class RememberRequest(BaseModel): - """Request model for /v1/remember endpoint.""" - - apikey: str | None = Field(None, description="API key") - userid: str = Field(..., description="User ID") - messagecontent: str = Field(..., description="Message content") - additionalcontext: str | None = Field(None, description="Additional context") - strippii: bool = Field(False, description="Strip PII") - filterresults: bool = Field(False, description="Filter results with LLM") - includememoryid: bool = Field(False, description="Include memory IDs in response") - - -class AddMemoriesRequest(BaseModel): - """Request model for /v1/memories/add endpoint.""" - - apikey: str | None = Field(None, description="API key") - userid: str = Field(..., description="User ID") - memoriestoadd: str | list[str] = Field(..., description="Memories to add") - - -class GetMemoriesRequest(BaseModel): - """Request model for /v1/memories/get endpoint.""" - - apikey: str | None = Field(None, description="API key") - userid: str = Field(..., description="User ID") - memoryid: str | None = Field(None, description="Specific memory ID") - limit: int = Field(50, ge=1, le=1000, description="Max memories to return") - startafter: str | None = Field(None, description="Memory ID to start after") - - -class DeleteMemoryRequest(BaseModel): - """Request model for /v1/memories/delete endpoint.""" - - apikey: str | None = Field(None, description="API key") - userid: str = Field(..., description="User ID") - memoryid: str = Field(..., description="Memory ID to delete") - - -class UpdateMemoryRequest(BaseModel): - """Request model for /v1/memories/update endpoint.""" - - apikey: str | None = Field(None, description="API key") - userid: str = Field(..., description="User ID") - memoryid: str = Field(..., description="Memory ID to update") - projectid: str = Field(..., description="Project ID") - content: str | None = Field(None, description="New content") - importance: float | None = Field( - None, ge=0.0, le=10.0, description="New importance score" - ) - metadata: dict[str, Any] | None = Field(None, description="New metadata") - - -class DeleteUserRequest(BaseModel): - """Request model for /v1/user/delete endpoint.""" - - apikey: str | None = Field(None, description="API key") - userid: str = Field(..., description="User ID") - confirmdelete: bool = Field(..., description="Confirm deletion") diff --git a/pkg/hanzo-memory/src/hanzo_memory/models/project.py b/pkg/hanzo-memory/src/hanzo_memory/models/project.py deleted file mode 100644 index 3164563f5..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/models/project.py +++ /dev/null @@ -1,52 +0,0 @@ -"""Project models.""" - -from typing import Any - -from pydantic import BaseModel, Field - -from .base import TimestampedModel, UserScopedModel - - -class ProjectBase(BaseModel): - """Base project model.""" - - name: str = Field(..., description="Project name") - description: str = Field("", description="Project description") - metadata: dict[str, Any] = Field( - default_factory=dict, description="Additional metadata" - ) - - -class ProjectCreate(ProjectBase): - """Model for creating a project.""" - - pass - - -class ProjectUpdate(BaseModel): - """Model for updating a project.""" - - name: str | None = None - description: str | None = None - metadata: dict[str, Any] | None = None - - -class Project(ProjectBase, UserScopedModel, TimestampedModel): - """Complete project model.""" - - project_id: str = Field(..., description="Project ID") - knowledge_base_ids: list[str] = Field( - default_factory=list, description="Associated knowledge base IDs" - ) - memory_count: int = Field(0, description="Number of memories in project") - - model_config = {"from_attributes": True} - - -class ProjectList(BaseModel): - """Project list response.""" - - projects: list[Project] = Field(..., description="List of projects") - total: int = Field(..., description="Total number of projects") - page: int = Field(1, description="Current page") - per_page: int = Field(50, description="Items per page") diff --git a/pkg/hanzo-memory/src/hanzo_memory/recipes.py b/pkg/hanzo-memory/src/hanzo_memory/recipes.py deleted file mode 100644 index 1e69138e6..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/recipes.py +++ /dev/null @@ -1,58 +0,0 @@ -"""Hanzo Brain โ€” recipe loader (Python port of @hanzo/bot-recipes-brain). - -YAML recipes for daily-life automation: - auth + cron + ingest + classify + draft + enqueue + on_swipe. - -Loads recipes from `/recipes/*.yaml` plus any -user-defined dir in `HANZO_BRAIN_RECIPES`. Same shape as the TS pack -so a single brain.db file works for either runtime. -""" - -from __future__ import annotations - -import os -from pathlib import Path -from typing import Any - -import yaml - -_HERE = Path(__file__).parent -_BUILTIN = _HERE / "recipes" - - -def _recipe_dirs() -> list[Path]: - dirs: list[Path] = [] - if _BUILTIN.is_dir(): - dirs.append(_BUILTIN) - env_dir = os.environ.get("HANZO_BRAIN_RECIPES") - if env_dir: - p = Path(env_dir).expanduser() - if p.is_dir(): - dirs.append(p) - return dirs - - -def list_recipes() -> list[str]: - """Return the names (without `.yaml`) of every available recipe.""" - seen: dict[str, None] = {} - for d in _recipe_dirs(): - for f in d.glob("*.yaml"): - seen.setdefault(f.stem, None) - return list(seen.keys()) - - -def load_recipe(name: str) -> dict[str, Any]: - """Load and parse one recipe by name.""" - for d in _recipe_dirs(): - path = d / f"{name}.yaml" - if path.is_file(): - with path.open("r", encoding="utf-8") as f: - data = yaml.safe_load(f) or {} - if not isinstance(data, dict): - raise ValueError(f"recipe `{name}` did not parse to a mapping") - return data - available = ", ".join(list_recipes()) or "(none)" - raise FileNotFoundError( - f"recipe `{name}` not found. Available: {available}. " - f"Drop a yaml into {_BUILTIN} or set HANZO_BRAIN_RECIPES to your own dir." - ) diff --git a/pkg/hanzo-memory/src/hanzo_memory/recipes/email.yaml b/pkg/hanzo-memory/src/hanzo_memory/recipes/email.yaml deleted file mode 100644 index 76fd28104..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/recipes/email.yaml +++ /dev/null @@ -1,92 +0,0 @@ -# Hanzo Brain โ€” Email Recipe -# -# Daily Gmail pull โ†’ classify โ†’ draft response โ†’ enqueue for swipe-review -# in the Hanzo app (~/work/hanzo/bot/apps/{ios,android,macos}). -# -# Run: -# hanzo-bot brain run-recipe email -# hanzo-bot brain drafts list -# hanzo-bot brain drafts approve # sends via gmail -# hanzo-bot brain drafts reject # logs as decline-fact for training -# -# Trains your reply tone over time. Every swipe is a signal. - -recipe: email -version: 1 -backend: gmail - -auth: - provider: hanzo.id # OIDC via hanzo IAM โ€” single sign-on - scopes: - - https://www.googleapis.com/auth/gmail.readonly - - https://www.googleapis.com/auth/gmail.compose - -cron: "*/30 * * * *" # every 30 min โ€” adjust per volume - -ingest: - source: inbox - since: 24h - max: 100 - filter: - has_thread: any - skip_labels: [SPAM, TRASH, PROMOTIONS] - -classify: - model: zen-2-haiku # cheap classifier, fast - schema: - needs_reply: bool - priority: one_of [P0, P1, P2, P3] - intent: one_of [question, fyi, scheduling, ask, update, spam] - entities: [person, company, deal] - sentiment: one_of [positive, neutral, negative] - action_items: list - -draft: - when: needs_reply == true - model: zen-2 # main model โ€” bigger context, better tone - context: - - brain.recall(from_email) # who is this person (facts) - - brain.search(thread_id, limit=10) # past replies in thread (RAG) - - brain.facts({entity:from_email, since:30d, limit:30}) - - brain.style(sender:me, recent:200) # 200-shot style examples from outbox - prompt: | - You are drafting a reply for me. Match my tone exactly โ€” terse, - direct, no exclamation marks, no emoji. Answer the question in the - fewest words that are still useful. Sign off only if I usually do. - Never invent commitments. If you're unsure, suggest "let me check - and get back to you" and add an action_item. - output_schema: - subject: string - body: string - action_items: list - confidence: number # 0..1, used for triage ranking - -enqueue: - queue: drafts.email - ttl: 7d - rank_by: [priority, confidence] - fact_on_enqueue: - subject: from_email - predicate: thread_active - object: true - source: thread_id - -notify: - channel: hanzo-app - badge: count_unswiped - push: drafts.email.new - -# After the user swipes in the app, the result feeds back to the brain: -on_swipe_send: - - send via gmail - - upsertFact: { subject: from_email, predicate: replied, object: thread_id, ts: now } - - learn: append (received_msg, sent_draft) to style examples - -on_swipe_reject: - - delete draft - - upsertFact: { subject: from_email, predicate: declined_reply, object: thread_id, ts: now } - - learn: down-weight similar drafts in future ranking - -on_swipe_edit: - - open editor with the draft pre-filled - - on save: send + learn (received_msg, edited_draft) as positive example diff --git a/pkg/hanzo-memory/src/hanzo_memory/server.py b/pkg/hanzo-memory/src/hanzo_memory/server.py deleted file mode 100644 index d2dcd13ac..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/server.py +++ /dev/null @@ -1,794 +0,0 @@ -"""FastAPI server for Hanzo Memory Service.""" - -import json -import uuid -from collections.abc import AsyncGenerator -from contextlib import asynccontextmanager - -from fastapi import Depends, FastAPI, HTTPException, Query, Request, status -from fastapi.middleware.cors import CORSMiddleware -from fastapi.security import HTTPAuthorizationCredentials -from structlog import get_logger - -from .api.auth import get_or_verify_user_id, require_auth, security -from .config import settings -from .db import get_db_client -from .models import ( - AddKnowledgeRequest, - AddMemoriesRequest, - ChatMessageCreate, - ChatSessionCreate, - CreateKnowledgeBaseRequest, - DeleteKnowledgeRequest, - DeleteMemoryRequest, - DeleteUserRequest, - GetKnowledgeRequest, - GetMemoriesRequest, - MemoryListResponse, - MemoryResponse, - RememberRequest, - UpdateMemoryRequest, -) -from .services import get_embedding_service, get_memory_service - -logger = get_logger() - -# Global service instances -db_client = None -embedding_service = None - - -@asynccontextmanager -async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: - """Application lifespan manager.""" - global db_client, embedding_service - - # Startup - logger.info("Starting Hanzo Memory Service") - settings.ensure_paths() - - # Initialize services - db_client = get_db_client() - embedding_service = get_embedding_service() - if db_client: - db_client.create_projects_table() - db_client.create_knowledge_bases_table() - - yield - - # Shutdown - logger.info("Shutting down Hanzo Memory Service") - if db_client: - db_client.close() - - -# Create FastAPI app -app = FastAPI( - title="Hanzo Memory Service", - description="AI memory and knowledge management service", - version="0.1.0", - lifespan=lifespan, -) - -# Add CORS middleware -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], # Configure based on your needs - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) - - -@app.get("/health") -async def health_check() -> dict[str, str]: - """Health check endpoint.""" - return { - "status": "healthy", - "service": "hanzo-memory", - "version": "0.1.0", - } - - -@app.post("/v1/remember") -async def remember( - request: RememberRequest, - req: Request, - credentials: HTTPAuthorizationCredentials | None = Depends(security), -) -> MemoryResponse: - """ - Retrieve relevant memories and store new memory. - - This endpoint: - 1. Searches for relevant memories based on the message content - 2. Optionally filters results using LLM - 3. Stores the incoming message as a new memory - 4. Returns relevant memories - """ - # Check auth - request.apikey or require_auth(req, credentials) - - memory_service = get_memory_service() - - # Get or create default project for user - project_id = f"project_{request.userid}_default" - - # Search for relevant memories - memories = memory_service.search_memories( - user_id=request.userid, - query=request.messagecontent, - project_id=project_id, - limit=10, - filter_with_llm=request.filterresults, - additional_context=request.additionalcontext, - ) - - # Store the new memory - memory_service.create_memory( - user_id=request.userid, - project_id=project_id, - content=request.messagecontent, - metadata={ - "additional_context": request.additionalcontext, - }, - strip_pii=request.strippii, - ) - - # Format response - relevant_memories: list[str | dict[str, str]] - if request.includememoryid: - relevant_memories = [ - {"content": m.content, "memoryId": m.memory_id} for m in memories - ] - else: - relevant_memories = [m.content for m in memories] - - # Usage info based on returned memories - usage_info = { - "current": len(memories), - "limit": settings.max_memories_per_user, - } - - return MemoryResponse( - user_id=request.userid, - relevant_memories=relevant_memories, - memory_stored=True, - usage_info=usage_info, - ) - - -@app.post("/v1/memories/add") -async def add_memories( - request: AddMemoriesRequest, - req: Request, - credentials: HTTPAuthorizationCredentials | None = Depends(security), -) -> dict: - """Add explicit memories without importance analysis.""" - # Check auth - request.apikey or require_auth(req, credentials) - - memory_service = get_memory_service() - - # Get or create default project - project_id = f"project_{request.userid}_default" - - # Normalize memories to list - memories_to_add = ( - [request.memoriestoadd] - if isinstance(request.memoriestoadd, str) - else request.memoriestoadd - ) - - # Add memories - memory_ids = [] - for content in memories_to_add: - memory = memory_service.create_memory( - user_id=request.userid, - project_id=project_id, - content=content, - importance=5.0, # Default importance for explicit adds - ) - memory_ids.append(memory.memory_id) - - return { - "userid": request.userid, - "added_count": len(memory_ids), - "memory_ids": memory_ids, - "usage_info": { - "current": len(memory_ids), - "limit": settings.max_memories_per_user, - }, - } - - -@app.post("/v1/memories/get") -async def get_memories( - request: GetMemoriesRequest, - req: Request, - credentials: HTTPAuthorizationCredentials | None = Depends(security), -) -> MemoryListResponse: - """Retrieve stored memories.""" - # Check auth - request.apikey or require_auth(req, credentials) - - memory_service = get_memory_service() - - # Specific memory ID is required - if not request.memoryid: - raise HTTPException( - status_code=400, - detail="Memory ID is required. Use /v1/memories/search for querying memories.", - ) - - memory = memory_service.get_memory(request.userid, request.memoryid) - if not memory: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Memory not found", - ) - memories = [memory] - - return MemoryListResponse( - user_id=request.userid, - memories=memories, - pagination={ - "has_more": False, - "last_id": memories[-1].memory_id if memories else None, - }, - usage_info={ - "current": len(memories), - "limit": settings.max_memories_per_user, - }, - ) - - -@app.post("/v1/memories/update") -async def update_memory_endpoint( - request: UpdateMemoryRequest, - req: Request, - credentials: HTTPAuthorizationCredentials | None = Depends(security), -) -> dict: - """Update a specific memory.""" - # Check auth - request.apikey or require_auth(req, credentials) - - memory_service = get_memory_service() - - # Prepare update parameters - update_params = {} - if request.content is not None: - update_params["content"] = request.content - if request.importance is not None: - update_params["importance"] = request.importance - if request.metadata is not None: - update_params["metadata"] = request.metadata - - # Perform the update - updated_memory = memory_service.update_memory( - user_id=request.userid, - memory_id=request.memoryid, - project_id=request.projectid, - **update_params, - ) - - if not updated_memory: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Memory not found", - ) - - return { - "message": "Memory updated successfully", - "memory_id": request.memoryid, - "userid": request.userid, - "updated_fields": list(update_params.keys()), - } - - -@app.post("/v1/memories/delete") -async def delete_memory( - request: DeleteMemoryRequest, - req: Request, - credentials: HTTPAuthorizationCredentials | None = Depends(security), -) -> dict: - """Delete a specific memory.""" - # Check auth - request.apikey or require_auth(req, credentials) - - memory_service = get_memory_service() - - success = memory_service.delete_memory(request.userid, request.memoryid) - if not success: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Memory not found", - ) - - return { - "message": "Memory deleted successfully", - "memory_id": request.memoryid, - "userid": request.userid, - } - - -@app.post("/v1/user/delete") -async def delete_user( - request: DeleteUserRequest, - req: Request, - credentials: HTTPAuthorizationCredentials | None = Depends(security), -) -> dict: - """Delete all memories for a user.""" - # Check auth - request.apikey or require_auth(req, credentials) - - if not request.confirmdelete: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="confirmdelete must be true", - ) - - memory_service = get_memory_service() - - deleted_count = memory_service.delete_user_memories(request.userid) - - return { - "message": "All user memories deleted", - "userid": request.userid, - "deleted_count": deleted_count, - } - - -# Knowledge Base Management Endpoints - - -@app.post("/v1/kb/create") -async def create_knowledge_base( - request: CreateKnowledgeBaseRequest, - req: Request, - credentials: HTTPAuthorizationCredentials | None = Depends(security), -) -> dict: - """Create a new knowledge base.""" - try: - # Get or verify user ID - user_id = await get_or_verify_user_id(request.userid, credentials, req) - - # Get default project if not specified - project_id = getattr(request, "project_id", None) - if not project_id: - # Create or get default project for user - project_id = f"default-{user_id}" - try: - if db_client: - db_client.create_project( - project_id=project_id, - user_id=user_id, - name="Default Project", - description="Automatically created default project", - ) - except Exception: - pass # Project may already exist - - # Create knowledge base - kb_id = request.kb_id or str(uuid.uuid4()) - if not db_client: - raise HTTPException(status_code=503, detail="Database not initialized") - db_client.create_knowledge_base( - kb_id=kb_id, - user_id=user_id, - project_id=project_id, - name=request.name, - description=getattr(request, "description", ""), - ) - - return { - "kb_id": kb_id, - "message": f"Knowledge base '{request.name}' created successfully", - } - except Exception as e: - logger.error(f"Error creating knowledge base: {e}") - raise HTTPException(status_code=500, detail=str(e)) from e - - -@app.get("/v1/kb/list") -async def list_knowledge_bases( - req: Request, - userid: str = Query(...), - project_id: str | None = Query(None), - credentials: HTTPAuthorizationCredentials | None = Depends(security), -) -> dict: - """List knowledge bases for a user.""" - try: - # Get or verify user ID - user_id = await get_or_verify_user_id(userid, credentials, req) - - if not db_client: - raise HTTPException(status_code=503, detail="Database not initialized") - - # Query knowledge bases from the database - kbs = db_client.get_knowledge_bases(project_id or user_id) - - return { - "userid": user_id, - "knowledge_bases": kbs, - "total": len(kbs), - } - except Exception as e: - logger.error(f"Error listing knowledge bases: {e}") - raise HTTPException(status_code=500, detail=str(e)) from e - - -@app.post("/v1/kb/facts/add") -async def add_facts( - request: AddKnowledgeRequest, - req: Request, - credentials: HTTPAuthorizationCredentials | None = Depends(security), -) -> dict: - """Add facts to a knowledge base.""" - try: - # Get or verify user ID - await get_or_verify_user_id(request.userid, credentials, req) - - # Process each fact - added_facts = [] - for fact_data in request.facts: - # Generate embedding for fact content - content = fact_data.get("content", "") - if not embedding_service: - raise HTTPException( - status_code=503, detail="Embedding service not initialized" - ) - embedding = embedding_service.embed_text(content)[0] - - # Add fact to database - fact_id = fact_data.get("fact_id") or str(uuid.uuid4()) - if not db_client: - raise HTTPException(status_code=503, detail="Database not initialized") - db_client.add_fact( - fact_id=fact_id, - kb_id=request.kb_id, - content=content, - embedding=embedding, - parent_id=fact_data.get("parent_id"), - metadata=fact_data.get("metadata", {}), - ) - - added_facts.append( - { - "fact_id": fact_id, - "content": content, - } - ) - - return { - "kb_id": request.kb_id, - "facts_added": len(added_facts), - "facts": added_facts, - } - except Exception as e: - logger.error(f"Error adding facts: {e}") - raise HTTPException(status_code=500, detail=str(e)) from e - - -@app.post("/v1/kb/facts/get") -async def get_facts( - request: GetKnowledgeRequest, - req: Request, - credentials: HTTPAuthorizationCredentials | None = Depends(security), -) -> dict: - """Get facts from a knowledge base.""" - try: - # Get or verify user ID - await get_or_verify_user_id(request.userid, credentials, req) - - # Search facts if query provided - if request.query: - # Generate query embedding - if not embedding_service: - raise HTTPException( - status_code=503, detail="Embedding service not initialized" - ) - query_embedding = embedding_service.embed_text(request.query)[0] - - # Search facts - if not db_client: - raise HTTPException(status_code=503, detail="Database not initialized") - results_df = db_client.search_facts( - kb_id=request.kb_id, - query_embedding=query_embedding, - limit=request.limit, - parent_id=request.fact_id if request.subtree else None, - ) - - # Convert results - facts = [] - if not results_df.is_empty(): - for row in results_df.to_dicts(): - facts.append( - { - "fact_id": row["fact_id"], - "content": row["content"], - "parent_id": row.get("parent_id"), - "metadata": json.loads(row.get("metadata", "{}")), - "similarity_score": row.get("_similarity", 0.0), - } - ) - - return { - "kb_id": request.kb_id, - "facts": facts, - "total": len(facts), - } - else: - # Query is required for searching facts - raise HTTPException( - status_code=400, - detail="Query parameter is required to search facts. Provide a search query.", - ) - except HTTPException: - raise - except Exception as e: - logger.error(f"Error getting facts: {e}") - raise HTTPException(status_code=500, detail=str(e)) from e - - -@app.post("/v1/kb/facts/delete") -async def delete_fact_endpoint( - request: DeleteKnowledgeRequest, - req: Request, - credentials: HTTPAuthorizationCredentials | None = Depends(security), -) -> dict: - """Delete a fact from a knowledge base.""" - try: - # Get or verify user ID - await get_or_verify_user_id(request.userid, credentials, req) - - if not db_client: - raise HTTPException(status_code=503, detail="Database not initialized") - - # Delete fact from database - deleted = db_client.delete_fact(request.fact_id, request.kb_id) - - if not deleted: - raise HTTPException( - status_code=404, - detail=f"Fact '{request.fact_id}' not found in knowledge base '{request.kb_id}'", - ) - - return { - "kb_id": request.kb_id, - "fact_id": request.fact_id, - "deleted": True, - "cascade": request.cascade, - } - except HTTPException: - raise - except Exception as e: - logger.error(f"Error deleting fact: {e}") - raise HTTPException(status_code=500, detail=str(e)) from e - - -# Chat Management Endpoints - - -@app.post("/v1/chat/sessions/create") -async def create_chat_session( - request: ChatSessionCreate, - req: Request, - credentials: HTTPAuthorizationCredentials | None = Depends(security), -) -> dict: - """Create a new chat session.""" - try: - # Get or verify user ID - user_id = await get_or_verify_user_id(request.userid, credentials, req) - - # Create session ID - session_id = request.session_id or str(uuid.uuid4()) - - # Get or create default project - project_id = request.project_id or f"default-{user_id}" - - # Ensure user's chat table exists - if not db_client: - raise HTTPException(status_code=503, detail="Database not initialized") - db_client.create_chats_table(user_id) - - return { - "session_id": session_id, - "userid": user_id, - "project_id": project_id, - "created": True, - } - except Exception as e: - logger.error(f"Error creating chat session: {e}") - raise HTTPException(status_code=500, detail=str(e)) from e - - -@app.post("/v1/chat/messages/add") -async def add_chat_message( - request: ChatMessageCreate, - req: Request, - credentials: HTTPAuthorizationCredentials | None = Depends(security), -) -> dict: - """Add a message to a chat session.""" - try: - # Get or verify user ID - user_id = await get_or_verify_user_id(request.userid, credentials, req) - - # Generate embedding for message content - if not embedding_service: - raise HTTPException( - status_code=503, detail="Embedding service not initialized" - ) - embedding = embedding_service.embed_text(request.content)[0] - - # Check for duplicate messages - # Search for similar messages in the same session - if not db_client: - raise HTTPException(status_code=503, detail="Database not initialized") - search_results = db_client.search_chats( - user_id=user_id, - query_embedding=embedding, - session_id=request.session_id, - limit=5, - ) - - # Check if this is a duplicate - is_duplicate = False - if not search_results.is_empty(): - for row in search_results.to_dicts(): - if ( - row["content"] == request.content - and row["role"] == request.role - and row.get("_similarity", 0) > 0.99 - ): - is_duplicate = True - chat_id = row["chat_id"] - break - - if not is_duplicate: - # Add new message - chat_id = str(uuid.uuid4()) - if not db_client: - raise HTTPException(status_code=503, detail="Database not initialized") - db_client.add_chat_message( - chat_id=chat_id, - user_id=user_id, - project_id=request.project_id or f"default-{user_id}", - session_id=request.session_id, - role=request.role, - content=request.content, - embedding=embedding, - metadata=request.metadata or {}, - ) - - return { - "chat_id": chat_id, - "session_id": request.session_id, - "duplicate": is_duplicate, - } - except Exception as e: - logger.error(f"Error adding chat message: {e}") - raise HTTPException(status_code=500, detail=str(e)) from e - - -@app.get("/v1/chat/sessions/{session_id}/messages") -async def get_chat_messages( - session_id: str, - req: Request, - userid: str = Query(...), - limit: int = Query(100, ge=1, le=1000), - credentials: HTTPAuthorizationCredentials | None = Depends(security), -) -> dict: - """Get messages for a chat session.""" - try: - # Get or verify user ID - user_id = await get_or_verify_user_id(userid, credentials, req) - - # Get chat history - if not db_client: - raise HTTPException(status_code=503, detail="Database not initialized") - history_df = db_client.get_chat_history( - user_id=user_id, - session_id=session_id, - limit=limit, - ) - - # Convert to messages - messages = [] - if not history_df.is_empty(): - # Sort by created_at timestamp - sorted_df = history_df.sort("created_at") - - for row in sorted_df.to_dicts(): - messages.append( - { - "chat_id": row["chat_id"], - "role": row["role"], - "content": row["content"], - "metadata": json.loads(row.get("metadata", "{}")), - "created_at": row["created_at"], - } - ) - - return { - "session_id": session_id, - "messages": messages, - "total": len(messages), - } - except Exception as e: - logger.error(f"Error getting chat messages: {e}") - raise HTTPException(status_code=500, detail=str(e)) from e - - -@app.post("/v1/chat/search") -async def search_chat_messages( - req: Request, - query: str = Query(...), - userid: str = Query(...), - project_id: str | None = Query(None), - session_id: str | None = Query(None), - limit: int = Query(10, ge=1, le=100), - credentials: HTTPAuthorizationCredentials | None = Depends(security), -) -> dict: - """Search across chat messages.""" - try: - # Get or verify user ID - user_id = await get_or_verify_user_id(userid, credentials, req) - - # Generate query embedding - if not embedding_service: - raise HTTPException( - status_code=503, detail="Embedding service not initialized" - ) - query_embedding = embedding_service.embed_text(query)[0] - - # Search chats - if not db_client: - raise HTTPException(status_code=503, detail="Database not initialized") - results_df = db_client.search_chats( - user_id=user_id, - query_embedding=query_embedding, - project_id=project_id, - session_id=session_id, - limit=limit, - ) - - # Convert results - messages = [] - if not results_df.is_empty(): - for row in results_df.to_dicts(): - messages.append( - { - "chat_id": row["chat_id"], - "session_id": row["session_id"], - "role": row["role"], - "content": row["content"], - "similarity_score": row.get("_similarity", 0.0), - "created_at": row["created_at"], - } - ) - - return { - "query": query, - "messages": messages, - "total": len(messages), - } - except Exception as e: - logger.error(f"Error searching chat messages: {e}") - raise HTTPException(status_code=500, detail=str(e)) from e - - -def run() -> None: - """Run the server.""" - import uvicorn - - uvicorn.run( - "hanzo_memory.server:app", - host=settings.host, - port=settings.port, - reload=True, - ) - - -if __name__ == "__main__": - run() diff --git a/pkg/hanzo-memory/src/hanzo_memory/services/__init__.py b/pkg/hanzo-memory/src/hanzo_memory/services/__init__.py deleted file mode 100644 index a683da75b..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/services/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Services package.""" - -from .embeddings import EmbeddingService, get_embedding_service -from .llm import LLMService, get_llm_service -from .memory import MemoryService, get_memory_service, reset_memory_service - -__all__ = [ - "EmbeddingService", - "get_embedding_service", - "LLMService", - "get_llm_service", - "MemoryService", - "get_memory_service", - "reset_memory_service", -] diff --git a/pkg/hanzo-memory/src/hanzo_memory/services/embeddings.py b/pkg/hanzo-memory/src/hanzo_memory/services/embeddings.py deleted file mode 100644 index de23dcfbf..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/services/embeddings.py +++ /dev/null @@ -1,249 +0,0 @@ -"""Embedding service using FastEmbed or LanceDB.""" - -import numpy as np -from structlog import get_logger - -from ..config import settings - -logger = get_logger() - -# Import backend-specific implementations -try: - from fastembed import TextEmbedding - - FASTEMBED_AVAILABLE = True -except ImportError: - FASTEMBED_AVAILABLE = False - logger.warning("FastEmbed not available") - -try: - import importlib.util - - if importlib.util.find_spec("hanzo_memory.services.embeddings_lancedb") is not None: - LANCEDB_AVAILABLE = True - else: - LANCEDB_AVAILABLE = False - logger.warning("LanceDB embeddings not available") -except ImportError: - LANCEDB_AVAILABLE = False - logger.warning("LanceDB embeddings not available") - - -class EmbeddingService: - """Service for generating embeddings using FastEmbed.""" - - def __init__(self, model_name: str | None = None): - """Initialize the embedding service.""" - self.model_name = model_name or settings.embedding_model - self._model = None - logger.info(f"Initializing embedding service with model: {self.model_name}") - - @property - def model(self): - """Lazy load the embedding model.""" - if self._model is None: - if not FASTEMBED_AVAILABLE: - raise RuntimeError("FastEmbed is not available") - logger.info(f"Loading embedding model: {self.model_name}") - self._model = TextEmbedding(model_name=self.model_name) - logger.info(f"Model {self.model_name} loaded successfully") - return self._model - - def embed_text(self, text: str | list[str]) -> list[list[float]]: - """ - Generate embeddings for text. - - Args: - text: Single text string or list of text strings - - Returns: - List of embedding vectors - """ - if isinstance(text, str): - text = [text] - - # Generate embeddings - embeddings = list(self.model.embed(text)) - - # Convert numpy arrays to lists - return [emb.tolist() for emb in embeddings] - - def embed_single(self, text: str) -> list[float]: - """ - Generate embedding for a single text. - - Args: - text: Text string - - Returns: - Embedding vector - """ - embeddings = self.embed_text(text) - return embeddings[0] - - def embed_batch( - self, - texts: list[str], - batch_size: int = 32, - show_progress: bool = False, - ) -> list[list[float]]: - """ - Generate embeddings for a batch of texts. - - Args: - texts: List of text strings - batch_size: Batch size for processing - show_progress: Whether to show progress bar - - Returns: - List of embedding vectors - """ - if not texts: - return [] - - all_embeddings = [] - - # Process in batches - for i in range(0, len(texts), batch_size): - batch = texts[i : i + batch_size] - embeddings = self.embed_text(batch) - all_embeddings.extend(embeddings) - - if show_progress and i > 0: - logger.info(f"Processed {i + len(batch)}/{len(texts)} texts") - - return all_embeddings - - def compute_similarity( - self, - query_embedding: list[float], - embeddings: list[list[float]], - metric: str = "cosine", - ) -> list[float]: - """ - Compute similarity between query embedding and a list of embeddings. - - Args: - query_embedding: Query embedding vector - embeddings: List of embedding vectors to compare against - metric: Similarity metric ("cosine", "dot", "euclidean") - - Returns: - List of similarity scores - """ - if not embeddings: - return [] - - query = np.array(query_embedding) - vectors = np.array(embeddings) - - if metric == "cosine": - # Normalize vectors for cosine similarity - query_norm = query / np.linalg.norm(query) - vectors_norm = vectors / np.linalg.norm(vectors, axis=1, keepdims=True) - similarities = np.dot(vectors_norm, query_norm) - elif metric == "dot": - similarities = np.dot(vectors, query) - elif metric == "euclidean": - # Return negative distance so higher is better - similarities = -np.linalg.norm(vectors - query, axis=1) - else: - raise ValueError(f"Unknown metric: {metric}") - - return list(similarities.tolist()) # Type hint for mypy - - def get_model_info(self) -> dict: - """Get information about the current embedding model.""" - return { - "model_name": self.model_name, - "dimensions": settings.embedding_dimensions, - "loaded": self._model is not None, - } - - -# Global embedding service instance -_embedding_service: EmbeddingService | None = None - - -def get_embedding_service() -> EmbeddingService: - """Get or create the global embedding service.""" - global _embedding_service - if _embedding_service is None: - # Use LanceDB embeddings if configured - if settings.db_backend == "lancedb" and LANCEDB_AVAILABLE: - logger.info("Using LanceDB embedding service") - from .embeddings_lancedb import get_lancedb_embedding_service - - # Wrap LanceDB service in standard interface - lancedb_service = get_lancedb_embedding_service() - _embedding_service = EmbeddingService() - # Override methods with LanceDB implementation - _embedding_service.embed_text = lancedb_service.embed_text - _embedding_service.embed_single = lancedb_service.embed_single - _embedding_service.embed_batch = lancedb_service.embed_batch - _embedding_service.compute_similarity = lancedb_service.compute_similarity - _embedding_service.get_model_info = lancedb_service.get_model_info - elif FASTEMBED_AVAILABLE: - logger.info("Using FastEmbed embedding service") - _embedding_service = EmbeddingService() - else: - # Create a minimal embedding service that generates dummy embeddings - # This allows the system to work without heavy embedding dependencies - logger.warning("Using minimal embedding service (dummy embeddings)") - _embedding_service = MinimalEmbeddingService() - return _embedding_service - - -class MinimalEmbeddingService: - """Minimal embedding service that generates dummy embeddings for basic functionality.""" - - def __init__(self): - self.model_name = "minimal" - - def embed_text(self, text: str | list[str]) -> list[list[float]]: - """Generate dummy embeddings for text.""" - if isinstance(text, str): - text = [text] - - # Generate simple dummy embeddings based on text length and content - embeddings = [] - for t in text: - # Create a simple hash-based embedding - hash_val = hash(t) % 1000000 - embedding = [ - float((hash_val >> i) & 0xFF) / 255.0 for i in range(0, 384, 8) - ] - # Pad or truncate to ensure consistent size - embedding = (embedding * (384 // len(embedding) + 1))[:384] - embeddings.append(embedding) - - return embeddings - - def embed_single(self, text: str) -> list[float]: - """Generate embedding for a single text.""" - embeddings = self.embed_text(text) - return embeddings[0] - - def embed_batch( - self, texts: list[str], batch_size: int = 32, show_progress: bool = False - ) -> list[list[float]]: - """Generate embeddings for a batch of texts.""" - return self.embed_text(texts) - - def compute_similarity( - self, - query_embedding: list[float], - embeddings: list[list[float]], - metric: str = "cosine", - ) -> list[float]: - """Compute similarity between query embedding and a list of embeddings.""" - # For minimal service, return dummy similarity scores - return [0.5] * len(embeddings) # Return neutral similarity - - def get_model_info(self) -> dict: - """Get information about the current embedding model.""" - return { - "model_name": "minimal", - "dimensions": 384, - "loaded": True, - } diff --git a/pkg/hanzo-memory/src/hanzo_memory/services/embeddings_lancedb.py b/pkg/hanzo-memory/src/hanzo_memory/services/embeddings_lancedb.py deleted file mode 100644 index 298291116..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/services/embeddings_lancedb.py +++ /dev/null @@ -1,207 +0,0 @@ -"""Embedding service using LanceDB's built-in embedding functions.""" - -import numpy as np -from lancedb.embeddings import get_registry -from structlog import get_logger - -from ..config import settings - -logger = get_logger() - - -class LanceDBEmbeddingService: - """Service for generating embeddings using LanceDB's embedding functions.""" - - def __init__(self, model_name: str | None = None): - """Initialize the embedding service.""" - self.model_name = model_name or settings.embedding_model - self._embedding_func = None - logger.info( - f"Initializing LanceDB embedding service with model: {self.model_name}" - ) - - @property - def embedding_func(self): - """Lazy load the embedding function.""" - if self._embedding_func is None: - logger.info(f"Loading embedding function: {self.model_name}") - - # LanceDB supports multiple embedding providers - registry = get_registry() - - # Check available embedding functions - available_funcs = list(registry.list_embedding_functions()) - logger.info(f"Available embedding functions: {available_funcs}") - - # Try to use the appropriate embedding function based on model name - if "fastembed" in available_funcs: - # Use FastEmbed if available - try: - self._embedding_func = registry.get("fastembed").create( - name=self.model_name - ) - logger.info("Using FastEmbed via LanceDB") - except Exception as e: - logger.warning(f"Failed to use FastEmbed: {e}") - self._embedding_func = None - - if self._embedding_func is None: - # Fall back to other providers - if self.model_name.startswith("BAAI/") or "bge" in self.model_name: - # Use sentence-transformers for BAAI/BGE models - self._embedding_func = registry.get("sentence-transformers").create( - name=self.model_name - ) - logger.info("Using sentence-transformers via LanceDB") - elif ( - self.model_name.startswith("text-embedding") - and "openai" in available_funcs - ): - # Use OpenAI for text-embedding models - self._embedding_func = registry.get("openai").create( - name=self.model_name - ) - logger.info("Using OpenAI embeddings via LanceDB") - else: - # Default to sentence-transformers - self._embedding_func = registry.get("sentence-transformers").create( - name=self.model_name - ) - logger.info("Using sentence-transformers (default) via LanceDB") - - logger.info(f"Embedding function {self.model_name} loaded successfully") - return self._embedding_func - - def embed_text(self, text: str | list[str]) -> list[list[float]]: - """ - Generate embeddings for text. - - Args: - text: Single text string or list of text strings - - Returns: - List of embedding vectors - """ - if isinstance(text, str): - text = [text] - - # Generate embeddings using LanceDB's embedding function - embeddings = self.embedding_func.compute_source_embeddings(text) - - # Convert to list format - if isinstance(embeddings, np.ndarray): - return embeddings.tolist() - else: - return [ - emb.tolist() if isinstance(emb, np.ndarray) else emb - for emb in embeddings - ] - - def embed_single(self, text: str) -> list[float]: - """ - Generate embedding for a single text. - - Args: - text: Text string - - Returns: - Embedding vector - """ - embeddings = self.embed_text(text) - return embeddings[0] - - def embed_batch( - self, - texts: list[str], - batch_size: int = 32, - show_progress: bool = False, - ) -> list[list[float]]: - """ - Generate embeddings for a batch of texts. - - Args: - texts: List of text strings - batch_size: Batch size for processing - show_progress: Whether to show progress bar - - Returns: - List of embedding vectors - """ - if not texts: - return [] - - all_embeddings = [] - - # Process in batches - for i in range(0, len(texts), batch_size): - batch = texts[i : i + batch_size] - embeddings = self.embed_text(batch) - all_embeddings.extend(embeddings) - - if show_progress and i > 0: - logger.info(f"Processed {i + len(batch)}/{len(texts)} texts") - - return all_embeddings - - def compute_similarity( - self, - query_embedding: list[float], - embeddings: list[list[float]], - metric: str = "cosine", - ) -> list[float]: - """ - Compute similarity between query embedding and a list of embeddings. - - Args: - query_embedding: Query embedding vector - embeddings: List of embedding vectors to compare against - metric: Similarity metric ("cosine", "dot", "euclidean") - - Returns: - List of similarity scores - """ - if not embeddings: - return [] - - query = np.array(query_embedding) - vectors = np.array(embeddings) - - if metric == "cosine": - # Normalize vectors for cosine similarity - query_norm = query / np.linalg.norm(query) - vectors_norm = vectors / np.linalg.norm(vectors, axis=1, keepdims=True) - similarities = np.dot(vectors_norm, query_norm) - elif metric == "dot": - similarities = np.dot(vectors, query) - elif metric == "euclidean": - # Return negative distance so higher is better - similarities = -np.linalg.norm(vectors - query, axis=1) - else: - raise ValueError(f"Unknown metric: {metric}") - - return list(similarities.tolist()) # Type hint for mypy - - def get_model_info(self) -> dict: - """Get information about the current embedding model.""" - return { - "model_name": self.model_name, - "dimensions": ( - self.embedding_func.ndims() - if self._embedding_func - else settings.embedding_dimensions - ), - "loaded": self._embedding_func is not None, - "backend": "lancedb", - } - - -# Global embedding service instance -_embedding_service: LanceDBEmbeddingService | None = None - - -def get_lancedb_embedding_service() -> LanceDBEmbeddingService: - """Get or create the global LanceDB embedding service.""" - global _embedding_service - if _embedding_service is None: - _embedding_service = LanceDBEmbeddingService() - return _embedding_service diff --git a/pkg/hanzo-memory/src/hanzo_memory/services/llm.py b/pkg/hanzo-memory/src/hanzo_memory/services/llm.py deleted file mode 100644 index e0dfe6e94..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/services/llm.py +++ /dev/null @@ -1,243 +0,0 @@ -"""LLM service for AI operations using LLM.""" - -import json -from typing import Any - -from structlog import get_logger - -from ..config import settings - -logger = get_logger() - -# Try to import LLM, but make it optional -try: - import llm - - LLM_AVAILABLE = True - # Configure LLM - llm.drop_params = True # Drop unsupported params instead of failing - llm.set_verbose = False # Disable verbose logging -except ImportError: - LLM_AVAILABLE = False - llm = None - logger.warning("LLM not available, LLM features will be limited") - - -class LLMService: - """Service for LLM operations using LLM.""" - - def __init__(self) -> None: - """Initialize LLM service.""" - self.default_model = settings.llm_model - self.api_base = settings.llm_api_base - self.temperature = settings.llm_temperature - self.max_tokens = settings.llm_max_tokens - - # Set up API keys if LLM is available - if LLM_AVAILABLE: - # Set up API keys - if settings.llm_api_key: - llm.api_key = settings.llm_api_key - if settings.openai_api_key: - llm.openai_key = settings.openai_api_key - if settings.anthropic_api_key: - llm.anthropic_key = settings.anthropic_api_key - - def complete( - self, - prompt: str, - model: str | None = None, - max_tokens: int | None = None, - temperature: float | None = None, - response_format: str | None = None, - ) -> str: - """ - Complete a prompt using LLM. - - Args: - prompt: The prompt to complete - model: Model to use (defaults to config) - max_tokens: Maximum tokens to generate - temperature: Temperature for generation - response_format: Optional response format ("json" for JSON mode) - - Returns: - Generated text - """ - if not LLM_AVAILABLE: - logger.warning("LLM not available, returning empty response") - return "" - - model = model or self.default_model - max_tokens = max_tokens or self.max_tokens - temperature = temperature or self.temperature - - try: - kwargs = { - "model": model, - "messages": [{"role": "user", "content": prompt}], - "max_tokens": max_tokens, - "temperature": temperature, - } - - # Add API base if configured (for local models) - if self.api_base: - kwargs["api_base"] = self.api_base - - # Add response format if specified - if response_format == "json": - kwargs["response_format"] = {"type": "json_object"} - - response = llm.completion(**kwargs) - return response.choices[0].message.content or "" - except Exception as e: - logger.error(f"LLM completion error: {e}") - return "" - - def chat( - self, - messages: list[dict[str, str]], - model: str | None = None, - max_tokens: int | None = None, - temperature: float | None = None, - response_format: str | None = None, - ) -> str: - """ - Chat with the LLM using LLM. - - Args: - messages: List of message dicts with "role" and "content" - model: Model to use - max_tokens: Maximum tokens to generate - temperature: Temperature for generation - response_format: Optional response format ("json" for JSON mode) - - Returns: - Generated response - """ - if not LLM_AVAILABLE: - logger.warning("LLM not available, returning empty response") - return "" - - model = model or self.default_model - max_tokens = max_tokens or self.max_tokens - temperature = temperature or self.temperature - - try: - kwargs = { - "model": model, - "messages": messages, - "max_tokens": max_tokens, - "temperature": temperature, - } - - # Add API base if configured - if self.api_base: - kwargs["api_base"] = self.api_base - - # Add response format if specified - if response_format == "json": - kwargs["response_format"] = {"type": "json_object"} - - response = llm.completion(**kwargs) - return response.choices[0].message.content or "" - except Exception as e: - logger.error(f"LLM chat error: {e}") - return "" - - def summarize_for_knowledge( - self, - content: str, - context: str | None = None, - skip_summarization: bool = False, - provided_summary: str | None = None, - ) -> dict[str, Any]: - """ - Summarize content and generate knowledge update instructions. - - Args: - content: Content to summarize - context: Additional context - skip_summarization: Skip summarization step - provided_summary: Pre-provided summary - - Returns: - JSON with summary and knowledge instructions - """ - if skip_summarization and not provided_summary: - # Just return the content as-is with basic instructions - return { - "summary": content, - "knowledge_instructions": { - "action": "add_fact", - "facts": [{"content": content}], - "reasoning": "Content added without summarization", - }, - } - - if provided_summary: - summary = provided_summary - else: - # Generate summary - summary_prompt = f"""Summarize the following content concisely, preserving key information: - -Content: {content}""" - if context: - summary_prompt += f"\n\nContext: {context}" - - summary = self.complete(summary_prompt, temperature=0.3) - - # Generate knowledge instructions - if LLM_AVAILABLE: - instruction_prompt = f"""Based on this summary, generate instructions for updating a knowledge base. -Return a JSON object with the following structure: -{{ - "action": "add_fact" or "update_fact" or "add_relation", - "facts": [ - {{ - "content": "fact content", - "metadata": {{"tags": [], "source": "..."}}, - "parent_id": null or "parent_fact_id" - }} - ], - "reasoning": "explanation of why these facts should be added" -}} - -Summary: {summary}""" - - if context: - instruction_prompt += f"\n\nContext: {context}" - - instructions_json = self.complete( - instruction_prompt, temperature=0.3, response_format="json" - ) - - try: - instructions = json.loads(instructions_json) - except json.JSONDecodeError: - instructions = { - "action": "add_fact", - "facts": [{"content": summary}], - "reasoning": "Failed to parse LLM response, using summary as fact", - } - else: - # Without LLM, return basic instructions - instructions = { - "action": "add_fact", - "facts": [{"content": summary}], - "reasoning": "Content added without LLM processing", - } - - return {"summary": summary, "knowledge_instructions": instructions} - - -# Global LLM service instance -_llm_service: LLMService | None = None - - -def get_llm_service() -> LLMService: - """Get or create the global LLM service.""" - global _llm_service - if _llm_service is None: - _llm_service = LLMService() - return _llm_service diff --git a/pkg/hanzo-memory/src/hanzo_memory/services/memory.py b/pkg/hanzo-memory/src/hanzo_memory/services/memory.py deleted file mode 100644 index 9df717ed2..000000000 --- a/pkg/hanzo-memory/src/hanzo_memory/services/memory.py +++ /dev/null @@ -1,307 +0,0 @@ -"""Memory service for managing memories.""" - -import json -import uuid -from datetime import datetime - -from structlog import get_logger - -from ..db import get_db_client -from ..models.memory import Memory, MemoryWithScore -from .embeddings import get_embedding_service -from .llm import get_llm_service - -logger = get_logger() - - -class MemoryService: - """Service for managing memories.""" - - def __init__(self) -> None: - """Initialize memory service.""" - self.db = get_db_client() - self.embeddings = get_embedding_service() - self.llm = get_llm_service() - - def create_memory( - self, - user_id: str, - project_id: str, - content: str, - metadata: dict | None = None, - importance: float = 1.0, - strip_pii: bool = False, - ) -> Memory: - """ - Create a new memory. - - Args: - user_id: User ID - project_id: Project ID - content: Memory content - metadata: Additional metadata - importance: Importance score - strip_pii: Whether to strip PII from content - - Returns: - Created memory - """ - # Ensure user's memory table exists - self.db.create_memories_table(user_id) - - # Strip PII if requested - if strip_pii: - content = self._strip_pii(content) - - # Generate embedding - embedding = self.embeddings.embed_single(content) - - # Create memory - memory_id = f"mem_{uuid.uuid4().hex[:12]}" - memory_data = self.db.add_memory( - memory_id=memory_id, - user_id=user_id, - project_id=project_id, - content=content, - embedding=embedding, - metadata=metadata, - importance=importance, - ) - - # Parse JSON fields if needed - if isinstance(memory_data.get("metadata"), str): - memory_data["metadata"] = json.loads(memory_data["metadata"]) - - return Memory(**memory_data) - - def search_memories( - self, - user_id: str, - query: str, - project_id: str | None = None, - limit: int = 10, - filter_with_llm: bool = False, - additional_context: str | None = None, - ) -> list[MemoryWithScore]: - """ - Search memories by semantic similarity. - - Args: - user_id: User ID - query: Search query - project_id: Optional project filter - limit: Maximum results - filter_with_llm: Use LLM to filter results - additional_context: Additional context for filtering - - Returns: - List of memories with similarity scores - """ - # Generate query embedding - query_embedding = self.embeddings.embed_single(query) - - # Search memories - results_df = self.db.search_memories( - user_id=user_id, - query_embedding=query_embedding, - project_id=project_id, - limit=limit * 2 if filter_with_llm else limit, # Get more if filtering - ) - - # Handle both list and DataFrame results - if isinstance(results_df, list): - results = results_df - else: - # DataFrame case - if results_df.empty: - return [] - results = ( - results_df.to_dicts() - if hasattr(results_df, "to_dicts") - else results_df.to_dict("records") - ) - - if not results: - return [] - - # Convert to memories with scores - memories = [] - for row in results: - # Get similarity score if available, otherwise calculate it - if "similarity_score" in row: - score = row["similarity_score"] - elif "embedding" in row: - # Calculate similarity score (cosine similarity) - embedding = row["embedding"] - score = self.embeddings.compute_similarity( - query_embedding, [embedding], metric="cosine" - )[0] - else: - score = 0.0 # Default score if no embedding available - - # Parse metadata if it's a string - metadata = row.get("metadata", {}) - if isinstance(metadata, str): - metadata = json.loads(metadata) - - memory = MemoryWithScore( - memory_id=row.get("memory_id", row.get("id")), - user_id=row.get("user_id"), - project_id=row.get("project_id"), - content=row.get("content"), - metadata=metadata, - importance=row.get("importance", 0.5), - created_at=datetime.fromisoformat( - row.get("created_at", datetime.now().isoformat()) - ), - updated_at=datetime.fromisoformat( - row.get("updated_at", datetime.now().isoformat()) - ), - embedding=row.get("embedding"), - similarity_score=score, - ) - memories.append(memory) - - # Sort by similarity score - memories.sort(key=lambda m: m.similarity_score, reverse=True) - - # Filter with LLM if requested - if filter_with_llm and memories: - memories = self._filter_with_llm( - query=query, - memories=memories[: limit * 2], - limit=limit, - additional_context=additional_context, - ) - - return memories[:limit] - - def get_memory(self, user_id: str, memory_id: str) -> Memory | None: - """Get a specific memory by ID.""" - # This would need to be implemented in the DB client - # For now, return None - logger.warning(f"get_memory not fully implemented for {memory_id}") - return None - - def delete_memory(self, user_id: str, memory_id: str) -> bool: - """Delete a memory.""" - # This would need to be implemented in the DB client - logger.warning(f"delete_memory not fully implemented for {memory_id}") - return False - - def update_memory( - self, - user_id: str, - memory_id: str, - project_id: str, - content: str | None = None, - metadata: dict | None = None, - importance: float | None = None, - ) -> Memory | None: - """Update a memory.""" - # Update the memory in the database - updated_data = self.db.update_memory( - memory_id=memory_id, - user_id=user_id, - project_id=project_id, - content=content, - metadata=metadata, - importance=importance, - ) - - if updated_data: - # Parse JSON fields if needed - if isinstance(updated_data.get("metadata"), str): - updated_data["metadata"] = json.loads(updated_data["metadata"]) - if isinstance(updated_data.get("context"), str): - updated_data["context"] = json.loads(updated_data.get("context", "{}")) - - return Memory(**updated_data) - - return None - - def delete_user_memories(self, user_id: str) -> int: - """Delete all memories for a user.""" - # This would need to be implemented in the DB client - logger.warning(f"delete_user_memories not fully implemented for {user_id}") - return 0 - - def _strip_pii(self, content: str) -> str: - """Strip PII from content using LLM.""" - prompt = f"""Remove any personally identifiable information (PII) from the following text. -Replace names with [NAME], emails with [EMAIL], phone numbers with [PHONE], addresses with [ADDRESS], etc. - -Text: {content} - -Anonymized text:""" - - try: - anonymized = self.llm.complete(prompt) - return anonymized.strip() - except Exception as e: - logger.error(f"Error stripping PII: {e}") - return content - - def _filter_with_llm( - self, - query: str, - memories: list[MemoryWithScore], - limit: int, - additional_context: str | None = None, - ) -> list[MemoryWithScore]: - """Filter memories using LLM to select most relevant.""" - # Create memory list for LLM - memory_texts = [] - for i, memory in enumerate(memories): - memory_texts.append(f"{i + 1}. {memory.content}") - - context = ( - f"Additional context: {additional_context}" if additional_context else "" - ) - - prompt = f"""Given the query: "{query}" -{context} - -Select the {limit} most relevant memories from the following list. -Return only the numbers of the selected memories, separated by commas. - -Memories: -{chr(10).join(memory_texts)} - -Selected memory numbers:""" - - try: - response = self.llm.complete(prompt) - # Parse selected indices - selected_indices = [] - for part in response.strip().split(","): - try: - idx = int(part.strip()) - 1 - if 0 <= idx < len(memories): - selected_indices.append(idx) - except ValueError: - continue - - # Return selected memories - return [memories[i] for i in selected_indices[:limit]] - except Exception as e: - logger.error(f"Error filtering with LLM: {e}") - return memories[:limit] - - -# Global memory service instance -_memory_service: MemoryService | None = None - - -def get_memory_service() -> MemoryService: - """Get or create the global memory service.""" - global _memory_service - if _memory_service is None: - _memory_service = MemoryService() - return _memory_service - - -def reset_memory_service() -> None: - """Reset the global memory service (useful for testing).""" - global _memory_service - _memory_service = None diff --git a/pkg/hanzo-memory/test_backend_integration.py b/pkg/hanzo-memory/test_backend_integration.py deleted file mode 100644 index ee4139d8c..000000000 --- a/pkg/hanzo-memory/test_backend_integration.py +++ /dev/null @@ -1,175 +0,0 @@ -#!/usr/bin/env python3 -"""Test backend integration with all components.""" - -import asyncio -import tempfile -from pathlib import Path - -import pytest - -from src.hanzo_memory.db.factory import get_db_client, reset_db_client -from src.hanzo_memory.models.memory import MemoryCreate -from src.hanzo_memory.models.project import ProjectCreate - - -async def test_local_backend(): - """Test local file backend.""" - reset_db_client() - client = get_db_client(backend="local") - - # Initialize - await client.initialize() - - # Create project - project = ProjectCreate( - name="Test Project", description="Test project for local backend" - ) - created_project = await client.create_project(project, user_id="test_user") - assert created_project.name == "Test Project" - - # Create memory - memory = MemoryCreate( - content="Test memory content", memory_type="test", importance=0.5 - ) - created_memory = await client.create_memory( - memory, project_id=created_project.project_id, user_id="test_user" - ) - assert created_memory.content == "Test memory content" - - # Get recent memories - recent = await client.get_recent_memories( - project_id=created_project.project_id, user_id="test_user", limit=10 - ) - assert len(recent) > 0 - - await client.close() - print("โœ… Local backend test passed") - - -async def test_lancedb_backend(): - """Test LanceDB backend.""" - reset_db_client() - - # Use temporary directory for LanceDB - with tempfile.TemporaryDirectory() as tmpdir: - client = get_db_client( - backend="lancedb", config={"db_path": tmpdir, "enable_markdown": False} - ) - - # Initialize - await client.initialize() - - # Create project - project = ProjectCreate( - name="LanceDB Test", description="Test project for LanceDB" - ) - created_project = await client.create_project(project, user_id="test_user") - assert created_project.name == "LanceDB Test" - - # Create memory - memory = MemoryCreate( - content="LanceDB test memory", - memory_type="test", - importance=0.7, - embedding=[0.1] * 384, # Dummy embedding - ) - created_memory = await client.create_memory( - memory, project_id=created_project.project_id, user_id="test_user" - ) - assert created_memory.content == "LanceDB test memory" - - # Search memories (with dummy embedding) - results = await client.search_memories( - query_embedding=[0.1] * 384, - project_id=created_project.project_id, - user_id="test_user", - limit=5, - ) - assert len(results) > 0 - - await client.close() - print("โœ… LanceDB backend test passed") - - -async def test_markdown_import(): - """Test markdown import in both backends.""" - # Test with local backend - reset_db_client() - client = get_db_client(backend="local") - await client.initialize() - - projects = await client.list_projects() - markdown_project = next( - (p for p in projects if p.project_id == "markdown_import"), None - ) - - if markdown_project: - print(f"โœ… Local backend found markdown project: {markdown_project.name}") - - await client.close() - - # Test with LanceDB backend - reset_db_client() - client = get_db_client(backend="lancedb") - await client.initialize() - - projects = await client.list_projects() - markdown_project = next( - (p for p in projects if p.project_id == "markdown_import"), None - ) - - if markdown_project: - print(f"โœ… LanceDB backend found markdown project: {markdown_project.name}") - - await client.close() - - -async def test_backend_switching(): - """Test switching between backends.""" - # Start with local - reset_db_client() - client1 = get_db_client(backend="local") - assert client1.__class__.__name__ == "LocalMemoryClient" - - # Switch to LanceDB - reset_db_client() - client2 = get_db_client(backend="lancedb") - assert client2.__class__.__name__ == "LanceDBClient" - - # Back to local - reset_db_client() - client3 = get_db_client(backend="local") - assert client3.__class__.__name__ == "LocalMemoryClient" - - print("โœ… Backend switching test passed") - - -async def main(): - """Run all tests.""" - print("\n" + "=" * 60) - print("BACKEND INTEGRATION TESTS") - print("=" * 60) - - try: - await test_local_backend() - await test_lancedb_backend() - await test_markdown_import() - await test_backend_switching() - - print("\n" + "=" * 60) - print("ALL TESTS PASSED โœ…") - print("=" * 60) - - except Exception as e: - print(f"\nโŒ Test failed: {e}") - import traceback - - traceback.print_exc() - return 1 - - return 0 - - -if __name__ == "__main__": - exit_code = asyncio.run(main()) - exit(exit_code) diff --git a/pkg/hanzo-memory/test_markdown_memory.py b/pkg/hanzo-memory/test_markdown_memory.py deleted file mode 100644 index 7faaee83d..000000000 --- a/pkg/hanzo-memory/test_markdown_memory.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python3 -"""Test script to verify markdown memory integration.""" - -import asyncio -import sys -from pathlib import Path -from rich.console import Console -from rich.table import Table -from rich.panel import Panel - -# Add src to path -sys.path.insert(0, str(Path(__file__).parent / "src")) - -from hanzo_memory.db.local_client import LocalMemoryClient -from hanzo_memory.db.markdown_reader import MarkdownMemoryReader - -console = Console() - - -async def test_markdown_memory(): - """Test markdown memory integration.""" - console.print("\n[bold cyan]Testing Markdown Memory Integration[/bold cyan]\n") - - # Initialize the client with markdown support - console.print("๐Ÿ“‚ Initializing local memory client with markdown support...") - client = LocalMemoryClient(enable_markdown=True) - - # Initialize the database - await client.initialize() - - # Check for markdown files - console.print("\n[bold]๐Ÿ” Finding markdown files:[/bold]") - reader = MarkdownMemoryReader() - md_files = reader.find_markdown_files() - - if not md_files: - console.print( - " [yellow]No markdown files found in watched directories[/yellow]" - ) - console.print(" Watched directories:") - for dir in reader.watch_dirs[:5]: - console.print(f" - {dir}") - else: - table = Table(title="Found Markdown Files") - table.add_column("File", style="cyan") - table.add_column("Path", style="green") - table.add_column("Size", style="yellow") - - for file in md_files[:10]: # Show first 10 - if file.exists(): - size = file.stat().st_size - size_str = f"{size:,} bytes" - table.add_row(file.name, str(file.parent), size_str) - - console.print(table) - - # Check imported memories - console.print("\n[bold]๐Ÿ“š Checking imported memories:[/bold]") - - # Get the markdown import project - projects = await client.list_projects() - markdown_project = next( - (p for p in projects if p.project_id == "markdown_import"), None - ) - - if markdown_project: - console.print(f" โœ… Found markdown import project: {markdown_project.name}") - - # Count memories by type - memories_by_type = {} - for memory in client.memories.values(): - if memory.get("project_id") == "markdown_import": - mem_type = memory.get("memory_type", "unknown") - memories_by_type[mem_type] = memories_by_type.get(mem_type, 0) + 1 - - if memories_by_type: - console.print("\n [bold]Memory counts by type:[/bold]") - for mem_type, count in sorted(memories_by_type.items()): - console.print(f" {mem_type}: {count}") - - # Show sample memories - console.print("\n [bold]Sample imported memories:[/bold]") - sample_memories = [ - m - for m in client.memories.values() - if m.get("project_id") == "markdown_import" - ][:3] - - for i, memory in enumerate(sample_memories, 1): - source_file = memory.get("context", {}).get("file_name", "Unknown") - section_title = memory.get("context", {}).get( - "section_title", "No title" - ) - content_preview = memory.get("content", "")[:100] + "..." - importance = memory.get("importance", 0) - - panel = Panel( - f"[dim]{content_preview}[/dim]\n\n" - f"[bold]Source:[/bold] {source_file}\n" - f"[bold]Section:[/bold] {section_title}\n" - f"[bold]Type:[/bold] {memory.get('memory_type', 'unknown')}\n" - f"[bold]Importance:[/bold] {importance:.2f}", - title=f"Memory {i}", - border_style="blue", - ) - console.print(panel) - else: - console.print( - " [yellow]No memories found in markdown import project[/yellow]" - ) - else: - console.print(" [yellow]No markdown import project found[/yellow]") - - # Test creating a new memory - console.print("\n[bold]โœ๏ธ Testing memory creation:[/bold]") - from hanzo_memory.models.memory import MemoryCreate - - test_memory = MemoryCreate( - content="This is a test memory created from the test script", - memory_type="test", - importance=0.5, - context={"test": True}, - source="test_script", - ) - - created_memory = await client.create_memory( - test_memory, project_id="test_project", user_id="test_user" - ) - - console.print(f" โœ… Created test memory with ID: {created_memory.id}") - - # Test searching (without embeddings for now) - console.print("\n[bold]๐Ÿ”Ž Testing memory retrieval:[/bold]") - recent_memories = await client.get_recent_memories( - project_id="test_project", user_id="test_user", limit=5 - ) - - console.print(f" Found {len(recent_memories)} recent memories") - - # Clean up - await client.close() - console.print("\n[bold green]โœ… Test completed successfully![/bold green]") - - -if __name__ == "__main__": - asyncio.run(test_markdown_memory()) diff --git a/pkg/hanzo-memory/test_minimal_sqlite.py b/pkg/hanzo-memory/test_minimal_sqlite.py deleted file mode 100644 index 03207de0d..000000000 --- a/pkg/hanzo-memory/test_minimal_sqlite.py +++ /dev/null @@ -1,83 +0,0 @@ -"""Test to verify SQLite backend works with minimal dependencies.""" - -import asyncio -import tempfile -from pathlib import Path - -from hanzo_memory.memory import memory -from hanzo_memory.models.memory import MemoryCreate - - -async def test_minimal_sqlite_backend(): - """Test that SQLite backend works with minimal dependencies.""" - print("Testing minimal SQLite backend functionality...") - - # Create a temporary database file for testing - with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp_file: - tmp_path = Path(tmp_file.name) - - try: - # Get SQLite backend - print("Getting SQLite backend...") - sqlite_backend = memory["sqlite"] - - # Initialize with temporary path - sqlite_backend._client = None # Reset client - sqlite_backend.config = {"db_path": tmp_path} - - # Initialize the backend - print("Initializing SQLite backend...") - await sqlite_backend.initialize() - - print("โœ… SQLite backend initialized successfully!") - - # Test creating a simple memory (without relying on embedding service) - print("Testing memory creation...") - from hanzo_memory.db.sqlite_client import SQLiteMemoryClient - - client = SQLiteMemoryClient(db_path=tmp_path) - - # Create a memory directly using the client - import uuid - from datetime import datetime - import json - - memory_id = str(uuid.uuid4()) - user_id = "test-user" - project_id = "test-project" - content = "This is a test memory for SQLite backend" - - # Add memory without embedding to avoid dependency on embedding service - result = client.add_memory( - memory_id=memory_id, - user_id=user_id, - project_id=project_id, - content=content, - embedding=[], # Empty embedding to avoid embedding service dependency - metadata={"source": "test"}, - importance=0.8, - ) - - print(f"โœ… Memory created with ID: {result['memory_id']}") - - # Test retrieving the memory - print("Testing memory retrieval...") - projects = client.get_user_projects(user_id) - print(f"โœ… Found {len(projects)} projects") - - # Close the client - client.close() - - print("๐ŸŽ‰ Minimal SQLite backend test completed successfully!") - print( - "โœ… SQLite backend works with just plaintext files and minimal dependencies" - ) - - finally: - # Clean up temporary file - if tmp_path.exists(): - tmp_path.unlink() - - -if __name__ == "__main__": - asyncio.run(test_minimal_sqlite_backend()) diff --git a/pkg/hanzo-memory/test_sqlite_backend.py b/pkg/hanzo-memory/test_sqlite_backend.py deleted file mode 100644 index 1f5815e6b..000000000 --- a/pkg/hanzo-memory/test_sqlite_backend.py +++ /dev/null @@ -1,96 +0,0 @@ -"""Test script to verify SQLite memory backend functionality.""" - -import asyncio -import tempfile -from pathlib import Path - -from hanzo_memory.memory import memory -from hanzo_memory.models.memory import MemoryCreate - - -async def test_sqlite_backend(): - """Test SQLite memory backend.""" - print("\n[bold cyan]Testing SQLite Memory Backend[/bold cyan]\n") - - # Create a temporary database file for testing - with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp_file: - tmp_path = Path(tmp_file.name) - - try: - # Get SQLite backend - print("๐Ÿ“‚ Getting SQLite backend...") - sqlite_backend = memory["sqlite"] # Use indexing instead of attribute access - - # Initialize with temporary path - sqlite_backend._client = None # Reset client - sqlite_backend.config = {"db_path": tmp_path} - - # Initialize the backend - print("๐Ÿ”Œ Initializing SQLite backend...") - await sqlite_backend.initialize() - - # Test creating a memory - print("๐Ÿ“ Creating a test memory...") - memory_create = MemoryCreate( - content="This is a test memory for SQLite backend", - memory_type="test", - importance=0.8, - context={"test": True}, - metadata={"source": "test"}, - ) - - created_memory = await sqlite_backend.service.create_memory( - memory=memory_create, project_id="test-project", user_id="test-user" - ) - - print(f"โœ… Created memory with ID: {created_memory.id}") - - # Test searching for the memory - print("๐Ÿ” Testing memory search...") - search_results = await sqlite_backend.service.search_memories( - query="test memory", project_id="test-project", user_id="test-user", limit=5 - ) - - print(f"โœ… Found {len(search_results)} memories") - if search_results: - print(f" First result: {search_results[0].memory.content[:50]}...") - - # Test creating a project - print("๐Ÿ“ Creating a test project...") - from hanzo_memory.models.project import ProjectCreate - - project_create = ProjectCreate( - name="Test Project", - description="A test project for SQLite backend", - metadata={"test": True}, - ) - - created_project = await sqlite_backend.service.db.create_project( - project_id="test-project", - user_id="test-user", - name="Test Project", - description="A test project for SQLite backend", - metadata={"test": True}, - ) - - print(f"โœ… Created project: {created_project['name']}") - - # Test getting user projects - print("๐Ÿ“‹ Getting user projects...") - user_projects = await sqlite_backend.service.db.get_user_projects("test-user") - print(f"โœ… Found {len(user_projects)} projects") - - # Close the backend - print("๐Ÿ›‘ Closing SQLite backend...") - await sqlite_backend.close() - - print("\n๐ŸŽ‰ SQLite backend test completed successfully!") - - finally: - # Clean up temporary file - if tmp_path.exists(): - tmp_path.unlink() - - -if __name__ == "__main__": - asyncio.run(test_sqlite_backend()) diff --git a/pkg/hanzo-memory/test_sqlite_simple.py b/pkg/hanzo-memory/test_sqlite_simple.py deleted file mode 100644 index ae5826593..000000000 --- a/pkg/hanzo-memory/test_sqlite_simple.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Simple test to verify SQLite backend integration.""" - -from hanzo_memory.memory import memory - - -def test_backend_availability(): - """Test that SQLite backend is available.""" - print("Testing SQLite backend availability...") - - # Check if SQLite backend is listed - available_backends = memory.backends() - print(f"Available backends: {list(available_backends.keys())}") - - if "sqlite" in available_backends: - print("โœ… SQLite backend is available!") - - # Try to access the backend - try: - sqlite_backend = memory["sqlite"] - print("โœ… Successfully accessed SQLite backend via indexing") - except Exception as e: - print(f"โŒ Error accessing SQLite backend via indexing: {e}") - - try: - sqlite_backend = memory.sqlite - print("โœ… Successfully accessed SQLite backend via attribute") - except Exception as e: - print(f"โŒ Error accessing SQLite backend via attribute: {e}") - - return True - else: - print("โŒ SQLite backend is NOT available!") - return False - - -if __name__ == "__main__": - success = test_backend_availability() - if success: - print("\n๐ŸŽ‰ SQLite backend integration test passed!") - else: - print("\nโŒ SQLite backend integration test failed!") diff --git a/pkg/hanzo-memory/test_update_memory.py b/pkg/hanzo-memory/test_update_memory.py deleted file mode 100644 index 7fb1e8671..000000000 --- a/pkg/hanzo-memory/test_update_memory.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python3 -"""Test script for update_memory functionality.""" - -import asyncio -import uuid -from hanzo_memory.memory import memory -from hanzo_memory.models.memory import MemoryCreate - - -async def test_update_memory(): - """Test the update_memory functionality.""" - print("Testing update_memory functionality...") - - # Use local backend for testing - backend = memory["local"] - await backend.initialize() - - # Create a test memory - test_content = "Original test memory content" - test_metadata = {"category": "test", "priority": "high"} - - memory_create = MemoryCreate( - content=test_content, metadata=test_metadata, importance=5.0 - ) - - created_memory = backend.service.create_memory( - user_id="test_user", - project_id="test_project", - content=test_content, - metadata=test_metadata, - importance=5.0, - ) - - print(f"Created memory: {created_memory.memory_id}") - print(f"Original content: {created_memory.content}") - print(f"Original importance: {created_memory.importance}") - print(f"Original metadata: {created_memory.metadata}") - - # Update the memory - updated_content = "Updated test memory content" - updated_importance = 8.5 - updated_metadata = {"category": "test", "priority": "critical", "updated": True} - - updated_memory = backend.service.update_memory( - user_id="test_user", - memory_id=created_memory.memory_id, - project_id="test_project", - content=updated_content, - importance=updated_importance, - metadata=updated_metadata, - ) - - if updated_memory: - print(f"\nSuccessfully updated memory: {updated_memory.memory_id}") - print(f"Updated content: {updated_memory.content}") - print(f"Updated importance: {updated_memory.importance}") - print(f"Updated metadata: {updated_memory.metadata}") - - # Verify the update worked - assert updated_memory.content == updated_content - assert updated_memory.importance == updated_importance - assert updated_memory.metadata == updated_metadata - - print("\nโœ… All assertions passed! Update functionality works correctly.") - else: - print("\nโŒ Failed to update memory") - - # Clean up - await backend.close() - - -if __name__ == "__main__": - asyncio.run(test_update_memory()) diff --git a/pkg/hanzo-memory/tests/__init__.py b/pkg/hanzo-memory/tests/__init__.py deleted file mode 100644 index 716d16882..000000000 --- a/pkg/hanzo-memory/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Test package for Hanzo Memory Service.""" diff --git a/pkg/hanzo-memory/tests/conftest.py b/pkg/hanzo-memory/tests/conftest.py deleted file mode 100644 index 1836ec62a..000000000 --- a/pkg/hanzo-memory/tests/conftest.py +++ /dev/null @@ -1,148 +0,0 @@ -"""Pytest configuration and fixtures.""" - -import tempfile -from collections.abc import Generator -from pathlib import Path -from unittest.mock import MagicMock, patch - -import polars as pl -import pytest -from fastapi.testclient import TestClient - -from hanzo_memory.config import settings -from hanzo_memory.db.client import InfinityClient -from hanzo_memory.db import reset_db_client -from hanzo_memory.services import reset_memory_service -from hanzo_memory.server import app - - -@pytest.fixture(autouse=True) -def test_settings(monkeypatch): - """Configure test settings.""" - # Reset any cached clients/services before each test - reset_db_client() - reset_memory_service() - - # Use temporary directory for testing - with tempfile.TemporaryDirectory() as tmpdir: - monkeypatch.setattr(settings, "infinity_db_path", Path(tmpdir) / "test_db") - monkeypatch.setattr(settings, "disable_auth", True) - monkeypatch.setattr(settings, "llm_model", "gpt-3.5-turbo") - yield - - # Reset again after test - reset_db_client() - reset_memory_service() - - -@pytest.fixture(autouse=True) -def mock_embedding_model(): - """Mock the embedding model to avoid downloads during tests.""" - with patch("fastembed.TextEmbedding") as mock_cls: - # Create a mock instance - mock_instance = MagicMock() - - # Mock the embed method to return fixed embeddings - def mock_embed(texts): - if isinstance(texts, str): - texts = [texts] - # Return 384-dimensional vectors (one for each text) - for _ in texts: - yield [0.1] * 384 - - mock_instance.embed = mock_embed - mock_cls.return_value = mock_instance - - yield mock_instance - - -@pytest.fixture(autouse=True) -def mock_llm_completion(): - """Mock LLM completion to avoid API calls during tests.""" - with patch("llm.completion") as mock_completion: - # Create mock response - mock_response = MagicMock() - mock_response.choices = [ - MagicMock(message=MagicMock(content="Mocked LLM response")) - ] - mock_completion.return_value = mock_response - yield mock_completion - - -@pytest.fixture -def client() -> Generator[TestClient, None, None]: - """Create test client.""" - with TestClient(app) as test_client: - yield test_client - - -@pytest.fixture -def db_client(test_settings) -> Generator[InfinityClient, None, None]: - """Create test database client.""" - client = InfinityClient() - yield client - client.close() - - -@pytest.fixture -def sample_user_id() -> str: - """Sample user ID for testing.""" - return "test_user_123" - - -@pytest.fixture -def sample_project_id() -> str: - """Sample project ID for testing.""" - return "test_project_456" - - -@pytest.fixture -def sample_memory_content() -> str: - """Sample memory content.""" - return "I prefer dark mode interfaces and enjoy using VS Code." - - -@pytest.fixture -def sample_messages() -> list: - """Sample chat messages.""" - return [ - {"role": "user", "content": "Hello, how are you?"}, - {"role": "assistant", "content": "I'm doing well, thank you!"}, - {"role": "user", "content": "Can you help me with Python?"}, - ] - - -@pytest.fixture -def mock_auth(): - """Mock authentication.""" - with patch("hanzo_memory.api.auth.require_auth") as mock: - mock.return_value = "test-api-key" - with patch("hanzo_memory.api.auth.get_or_verify_user_id") as mock_verify: - mock_verify.side_effect = lambda user_id, *args: user_id - yield mock - - -@pytest.fixture -def mock_db_client(): - """Mock database client.""" - with patch("hanzo_memory.server.db_client") as mock: - # Set up default mock behaviors - mock.create_project = MagicMock() - mock.create_knowledge_base = MagicMock() - mock.add_fact = MagicMock() - mock.search_facts = MagicMock(return_value=pl.DataFrame()) - mock.create_memories_table = MagicMock() - mock.add_memory = MagicMock() - mock.search_memories = MagicMock(return_value=pl.DataFrame()) - yield mock - - -@pytest.fixture -def mock_services(): - """Mock services.""" - with patch("hanzo_memory.server.embedding_service") as mock_embed: - mock_embed.embed_text = MagicMock(return_value=[[0.1] * 384]) - services = { - "embedding": mock_embed, - } - yield services diff --git a/pkg/hanzo-memory/tests/test_additional_coverage.py b/pkg/hanzo-memory/tests/test_additional_coverage.py deleted file mode 100644 index cc035a77b..000000000 --- a/pkg/hanzo-memory/tests/test_additional_coverage.py +++ /dev/null @@ -1,181 +0,0 @@ -"""Additional tests for improved coverage.""" - -from unittest.mock import patch - -import pytest - -from hanzo_memory.db.mock_infinity import MockDatabase, MockQuery, MockTable -from hanzo_memory.server import run - - -class TestAdditionalCoverage: - """Additional tests to improve coverage.""" - - def test_strip_pii_in_memory_service(self): - """Test PII stripping in memory service.""" - # Import here to test the function - import re - - # Test the regex patterns used in memory service - email_pattern = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" - phone_pattern = r"\b\d{3}[-.]?\d{3}[-.]?\d{4}\b" - - text = "Contact me at john@example.com or 555-123-4567" - - # Test email replacement - text_no_email = re.sub(email_pattern, "[EMAIL]", text) - assert "@example.com" not in text_no_email - assert "[EMAIL]" in text_no_email - - # Test phone replacement - text_no_phone = re.sub(phone_pattern, "[PHONE]", text_no_email) - assert "555-123-4567" not in text_no_phone - assert "[PHONE]" in text_no_phone - - def test_mock_infinity_edge_cases(self): - """Test MockInfinity edge cases.""" - mock_db = MockDatabase("test") - - # Test creating table - mock_db.create_table("test_table", {"id": "int", "name": "string"}) - assert "test_table" in mock_db.tables - - # Test getting non-existent table - with pytest.raises(ValueError): - mock_db.get_table("non_existent") - - def test_mock_query_filter_edge_cases(self): - """Test MockQuery filter edge cases.""" - mock_db = MockDatabase("test") - mock_table = MockTable(mock_db, "test_table") - mock_db.tables["test_table"] = { - "schema": {}, - "data": [ - {"field": "value1", "other": "data1"}, - {"field": "value2", "other": "data2"}, - ], - } - - query = MockQuery(mock_table, ["field", "other"]) - - # Test filter with non-equality condition - query.filter("field != 'value1'") - result = query.to_pl() - assert len(result) == 2 # No filtering for unsupported operators - - def test_mock_query_vector_search_ip_metric(self): - """Test MockQuery vector search with inner product metric.""" - - mock_db = MockDatabase("test") - mock_table = MockTable(mock_db, "test_table") - mock_db.tables["test_table"] = { - "schema": {}, - "data": [ - {"id": 1, "vector": [1.0, 0.0, 0.0]}, - {"id": 2, "vector": [0.0, 1.0, 0.0]}, - {"id": 3, "vector": [0.0, 0.0, 1.0]}, - ], - } - - query = MockQuery(mock_table, ["id", "vector"]) - query.match_dense("vector", [1.0, 0.0, 0.0], "float32", "ip", 2) - - result = query.to_pl() - assert len(result) == 2 - # First result should be the same vector (highest inner product) - assert result["id"][0] == 1 - - def test_mock_query_vector_search_unknown_metric(self): - """Test MockQuery vector search with unknown metric.""" - mock_db = MockDatabase("test") - mock_table = MockTable(mock_db, "test_table") - mock_db.tables["test_table"] = { - "schema": {}, - "data": [ - {"id": 1, "vector": [1.0, 0.0, 0.0]}, - ], - } - - query = MockQuery(mock_table, ["id", "vector"]) - query.match_dense("vector", [1.0, 0.0, 0.0], "float32", "unknown", 1) - - result = query.to_pl() - assert len(result) == 1 # Should still return data - - def test_mock_query_empty_data(self): - """Test MockQuery with empty data.""" - mock_db = MockDatabase("test") - mock_table = MockTable(mock_db, "test_table") - mock_db.tables["test_table"] = {"schema": {}, "data": []} - - query = MockQuery(mock_table, ["id"]) - result = query.to_pl() - assert result.is_empty() - - def test_run_server_function(self): - """Test the run function.""" - with patch("uvicorn.run") as mock_run: - run() - - mock_run.assert_called_once_with( - "hanzo_memory.server:app", - host="0.0.0.0", - port=4000, - reload=True, - ) - - def test_auth_when_disabled_line_63(self): - """Test auth disabled path for line 63 coverage.""" - from hanzo_memory.api.auth import verify_api_key - - with patch("hanzo_memory.api.auth.settings") as mock_settings: - mock_settings.disable_auth = True - # This should return True immediately at line 63 - assert verify_api_key(None) is True - assert verify_api_key("any-key") is True - - def test_llm_service_api_key_setup(self): - """Test LLM service API key setup.""" - from hanzo_memory.services.llm import LLMService - - with patch("hanzo_memory.services.llm.settings") as mock_settings: - with patch("hanzo_memory.services.llm.llm") as mock_llm: - # Test with llm_api_key - mock_settings.llm_model = "test" - mock_settings.llm_api_base = None - mock_settings.llm_temperature = 0.7 - mock_settings.llm_max_tokens = 1000 - mock_settings.llm_api_key = "test-llm-key" - mock_settings.openai_api_key = None - mock_settings.anthropic_api_key = None - - LLMService() - assert mock_llm.api_key == "test-llm-key" - - # Test with openai_api_key - mock_settings.llm_api_key = None - mock_settings.openai_api_key = "test-openai-key" - - LLMService() - assert mock_llm.openai_key == "test-openai-key" - - # Test with anthropic_api_key - mock_settings.openai_api_key = None - mock_settings.anthropic_api_key = "test-anthropic-key" - - LLMService() - assert mock_llm.anthropic_key == "test-anthropic-key" - - def test_embedding_service_empty_list(self): - """Test embedding service with empty list.""" - from hanzo_memory.services.embeddings import EmbeddingService - - service = EmbeddingService() - # Test embedding empty list - embeddings = service.embed_text([]) - assert embeddings == [] - - def test_mcp_main_module(self): - """Test MCP __main__ module coverage.""" - # Just import it to get coverage - assert True # Module imported successfully diff --git a/pkg/hanzo-memory/tests/test_algorithms.py b/pkg/hanzo-memory/tests/test_algorithms.py deleted file mode 100644 index a7853e052..000000000 --- a/pkg/hanzo-memory/tests/test_algorithms.py +++ /dev/null @@ -1,454 +0,0 @@ -"""Cross-runtime parity tests for the algorithm port. - -Mirrors `packages/memory/parity.test.ts` in @hanzo/bot-memory. Any algorithm -that's in both ports MUST produce the same outputs on identical inputs (modulo -floating-point noise for cosine-based code). -""" - -from __future__ import annotations - -import os -import sys - -import pytest - -sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) - -from hanzo_memory.algorithms import ( - CaptionSegment, - CircuitBreaker, - CircuitOpenError, - MmrInput, - QueryEval, - RuntimeConfig, - SearchHit, - WeightedEdge, - bbox_around, - characterize, - cjk_bigrams, - classify_link_rule, - coarse_dim, - content_range, - cosine, - decode_address, - dedup_hits, - detect_doc_type, - detect_script, - emoji_trigrams, - encode_address, - estimate_tokens, - format_slug, - get_doc_type, - get_embedding_model, - haversine_km, - in_box, - l2_normalize, - list_doc_types, - louvain, - mean_reciprocal_rank, - mmr_rerank, - mrl_truncate, - ndcg_at_k, - normalize_edges, - parse_range, - parse_slug, - parse_websearch, - pfnet_infinity, - precision_at_k, - prefix_for, - range_bounds, - recall_at_k, - reciprocal_rank, - render_rttm, - render_srt, - render_vtt, - retry, - rrf_fuse, - rsf_fuse, - select_rrf_k, - select_weights, - snn_score, - to_fts5_match, - truncate_to_tokens, - v7_ceiling, - v7_floor, -) - - -def hit(slug: str, score: float) -> SearchHit: - return SearchHit(slug=slug, score=score, excerpt=slug, source="keyword") - - -# โ”€โ”€ Fusion โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_rrf_normalizes_top_to_one(): - r = rrf_fuse([[hit("a", 1), hit("b", 0.5)]], 10) - assert r[0].slug == "a" - assert abs(r[0].score - 1.0) < 0.01 - - -def test_rrf_rewards_multi_list_consensus(): - r = rrf_fuse([[hit("a", 1)], [hit("a", 1), hit("b", 0.5)]], 10) - assert r[0].slug == "a" - - -def test_rsf_preserves_magnitude(): - r = rsf_fuse([[hit("a", 100), hit("b", 50)], [hit("a", 1), hit("c", 0.5)]], 10) - assert r[0].slug == "a" - assert len(r) == 3 - - -def test_query_characterize_and_select(): - assert select_rrf_k(characterize('"hello world"')) == 10 - assert select_rrf_k(characterize("foo AND bar")) == 15 - assert select_rrf_k(characterize("rust")) == 15 - assert select_rrf_k(characterize("a b c d e f g h i j")) == 40 - - -def test_select_weights_lean_short_to_fts(): - sw = select_weights(characterize("rust")) - assert sw["fts"] > sw["semantic"] - lw = select_weights(characterize("how do retrieval augmented generation systems typically work in production scale")) - assert lw["semantic"] > lw["fts"] - - -# โ”€โ”€ Rerank โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_cosine_basic(): - assert abs(cosine([1, 0], [1, 0]) - 1) < 1e-6 - assert abs(cosine([1, 0], [0, 1])) < 1e-6 - - -def test_mmr_picks_diverse_second(): - hits = [ - MmrInput(slug="a", score=0.9, embedding=[1, 0]), - MmrInput(slug="b", score=0.85, embedding=[1, 0.01]), - MmrInput(slug="c", score=0.6, embedding=[0, 1]), - ] - out = mmr_rerank(hits, lambda_=0.2, limit=2) - assert out[0].slug == "a" - assert out[1].slug == "c" - - -# โ”€โ”€ Dedup โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_dedup_keeps_best_chunk(): - out = dedup_hits([ - hit("page/foo#chunk-0", 0.5), - hit("page/foo#chunk-1", 0.8), - hit("page/bar", 0.6), - ]) - slugs = sorted([h.slug for h in out]) - assert slugs == ["page/bar", "page/foo#chunk-1"] - - -# โ”€โ”€ Script / FTS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_detect_script_cjk_and_emoji(): - assert detect_script("ใ“ใ‚“ใซใกใฏไธ–็•Œ")["primary"] == "cjk" - assert detect_script("Hello world")["primary"] == "latin" - assert detect_script("ะŸั€ะธะฒะตั‚")["primary"] == "cyrillic" - - -def test_cjk_bigrams_round_trip(): - out = cjk_bigrams("hello ไธ–็•Œ ใ“ใ‚“ใซใกใฏ") - assert "hello" in out - assert "ไธ–็•Œ" in out - assert "ใ“ใ‚“" in out - - -def test_emoji_trigrams_emit(): - out = emoji_trigrams("hi ๐Ÿš€๐ŸŒŒ๐ŸŒŸ") - assert len(out) > 0 - - -def test_parse_websearch(): - p = parse_websearch('"hello world" foo OR bar -baz qux') - assert p["phrases"] == ["hello world"] - assert p["optional"][0] == ["foo", "bar"] - assert p["excluded"] == ["baz"] - assert "qux" in p["required"] - - -def test_to_fts5_match(): - sql = to_fts5_match(parse_websearch("apple OR orange -spoil")) - assert "apple OR orange" in sql - assert "NOT spoil" in sql - - -# โ”€โ”€ Embed / MRL โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_embed_registry_known_models(): - assert get_embedding_model("ollama:nomic-embed-text").dim == 768 - assert get_embedding_model("openai:text-embedding-3-small").dim == 1536 - - -def test_prefix_for_e5_vs_nomic(): - e5 = get_embedding_model("intfloat/e5-large-v2") - nomic = get_embedding_model("ollama:nomic-embed-text") - assert prefix_for(e5, "query", "x") == "query: x" - assert prefix_for(e5, "passage", "x") == "passage: x" - assert prefix_for(nomic, "query", "x") == "x" - - -def test_mrl_truncate_and_normalize(): - v = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0] - t = mrl_truncate(v, 4) - assert len(t) == 4 - norm = sum(x * x for x in t) ** 0.5 - assert abs(norm - 1) < 1e-6 - - -def test_coarse_dim_one_eighth(): - e3 = get_embedding_model("openai:text-embedding-3-large") - cd = coarse_dim(e3) - assert 256 <= cd <= 512 - - -def test_l2_normalize_zero(): - assert l2_normalize([0.0, 0.0, 0.0]) == [0.0, 0.0, 0.0] - - -# โ”€โ”€ Temporal โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_v7_bounds_order(): - t = int(__import__("time").time() * 1000) - assert v7_floor(t) < v7_ceiling(t) - - -def test_range_bounds(): - r = range_bounds("2026-01-01T00:00:00Z", "2026-12-31T23:59:59Z") - assert r["floor"] < r["ceiling"] - - -# โ”€โ”€ Captions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_caption_rendering(): - segs = [ - CaptionSegment(start_secs=0, end_secs=1.5, text="hi", speaker="S0"), - CaptionSegment(start_secs=1.5, end_secs=3, text="world", speaker="S1"), - ] - assert render_vtt(segs).startswith("WEBVTT") - assert "00:00:00,000 --> 00:00:01,500" in render_srt(segs) - assert render_rttm(segs).startswith("SPEAKER") - - -# โ”€โ”€ Tokenizer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_estimate_tokens_grows(): - assert estimate_tokens("hi there friend") > estimate_tokens("hi") - - -def test_estimate_cjk_one_per_char(): - assert estimate_tokens("ใ“ใ‚“ใซใกใฏ") == 5 - - -def test_truncate_within_budget(): - long = "alpha " * 100 - t = truncate_to_tokens(long, 20) - assert estimate_tokens(t) <= 20 - - -# โ”€โ”€ Eval โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def _q(): - return QueryEval(predicted=["a", "b", "c", "d"], relevant=["c", "d"]) - - -def test_reciprocal_rank(): - assert abs(reciprocal_rank(_q()) - 1 / 3) < 1e-6 - - -def test_recall_grows_with_k(): - q = _q() - assert recall_at_k(q, 2) == 0 - assert recall_at_k(q, 4) == 1 - - -def test_precision_at_k(): - assert abs(precision_at_k(_q(), 4) - 0.5) < 1e-6 - - -def test_ndcg_graded(): - graded = QueryEval(predicted=["a", "b"], relevant={"a": 3, "b": 1}) - assert ndcg_at_k(graded, 2) > 0.9 - - -def test_mrr(): - assert mean_reciprocal_rank([_q()]) > 0 - - -# โ”€โ”€ Spatial / Range โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_haversine_zero(): - assert abs(haversine_km((0, 0), (0, 0))) < 1e-6 - - -def test_haversine_nyc_la(): - d = haversine_km((40.7128, -74.006), (34.0522, -118.2437)) - assert abs(d - 3935) < 50 - - -def test_bbox_around_round_trip(): - center = (37.77, -122.42) - box = bbox_around(center, 10) - assert in_box(center, box) - - -def test_parse_range_closed(): - assert parse_range("bytes=0-99", 1000) == (0, 99) - - -def test_parse_range_suffix(): - assert parse_range("bytes=-100", 1000) == (900, 999) - - -def test_parse_range_unsatisfiable(): - assert parse_range("bytes=2000-3000", 1000) == "unsatisfiable" - - -def test_content_range_fmt(): - assert content_range(0, 99, 1000) == "bytes 0-99/1000" - - -# โ”€โ”€ Address โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_address_round_trip(): - pk = bytes(range(32)) - addr = encode_address(pk) - assert addr.startswith("hanzo:") - out = decode_address(addr) - assert out["prefix"] == "hanzo" - assert out["version"] == 1 - - -def test_address_bad_checksum(): - with pytest.raises(ValueError): - decode_address("hanzo:11111111111111111111111111") - - -def test_address_mm_prefix(): - pk = bytes([1] + [0] * 31) - addr = encode_address(pk, prefix="mm") - assert addr.startswith("mm:") - - -# โ”€โ”€ Graph maintenance โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_normalize_edges(): - out = normalize_edges([WeightedEdge("a", "b", 10), WeightedEdge("b", "c", 5)]) - assert abs(out[0].weight - 1) < 1e-6 - assert abs(out[1].weight - 0) < 1e-6 - - -def test_snn_score_bounds(): - edges = [WeightedEdge("a", "b", 0.9), WeightedEdge("a", "c", 0.8), WeightedEdge("b", "c", 0.7)] - for e in snn_score(edges, 2): - assert 0 <= e.weight <= 1 - - -def test_pfnet_drops_dominated(): - out = pfnet_infinity([ - WeightedEdge("a", "b", 0.9), - WeightedEdge("b", "c", 0.9), - WeightedEdge("a", "c", 0.5), - ]) - assert not any(e.source == "a" and e.target == "c" for e in out) - - -def test_louvain_returns_mapping(): - edges = [WeightedEdge("a", "b", 1), WeightedEdge("b", "c", 1), WeightedEdge("a", "c", 1)] - out = louvain(edges) - assert set(out.keys()) == {"a", "b", "c"} - - -# โ”€โ”€ Doc types โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_detect_doc_type_meeting(): - dt = detect_doc_type(filename="meeting-2026-05-11.md", body="Attendees:\nAction Items:") - assert dt.slug == "meeting/notes" - - -def test_detect_doc_type_code(): - assert detect_doc_type(filename="main.rs").slug == "code/source" - - -def test_doc_types_listed(): - types = list_doc_types() - assert len(types) >= 10 - assert get_doc_type("note/plain") is not None - - -# โ”€โ”€ Circuit breaker / retry โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_circuit_breaker_opens(): - cb = CircuitBreaker(failure_threshold=2, cooldown_ms=100) - fail = lambda: (_ for _ in ()).throw(RuntimeError("x")) - with pytest.raises(RuntimeError): - cb.run(fail) - with pytest.raises(RuntimeError): - cb.run(fail) - assert cb.state() == "open" - with pytest.raises(CircuitOpenError): - cb.run(fail) - - -def test_retry_success_after_transient(): - state = {"n": 0} - - def f(): - state["n"] += 1 - if state["n"] < 3: - raise RuntimeError("transient") - return "ok" - - assert retry(f, attempts=5, base_ms=1, sleep_fn=lambda _s: None) == "ok" - assert state["n"] == 3 - - -# โ”€โ”€ Inference / slug / runtime config โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_parse_slug_explicit(): - assert parse_slug("openai:gpt-4o") == {"provider": "openai", "model": "gpt-4o"} - - -def test_parse_slug_implicit(): - assert parse_slug("qwen3:8b") == {"provider": "ollama", "model": "qwen3:8b"} - - -def test_format_slug_round_trip(): - assert format_slug({"provider": "openai", "model": "gpt-4o"}) == "openai:gpt-4o" - - -def test_runtime_config_precedence(): - rc = RuntimeConfig(defaults={"K": "default"}, env={"K": "env"}) - assert rc.get("K") == "env" - rc.set("K", "override") - assert rc.get("K") == "override" - assert rc.source("K") == "db_override" - rc.clear("K") - assert rc.get("K") == "env" - - -# โ”€โ”€ Link types โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_classify_link_rule(): - assert classify_link_rule("Alice founded Acme") == "founded" - assert classify_link_rule("Alice invested in Acme") == "invested_in" - assert classify_link_rule("worked together") == "mentions" diff --git a/pkg/hanzo-memory/tests/test_auth.py b/pkg/hanzo-memory/tests/test_auth.py deleted file mode 100644 index 9d2cc23c0..000000000 --- a/pkg/hanzo-memory/tests/test_auth.py +++ /dev/null @@ -1,130 +0,0 @@ -"""Tests for authentication.""" - -from unittest.mock import Mock, patch - -import pytest -from fastapi import HTTPException, Request, status -from fastapi.security import HTTPAuthorizationCredentials - -from hanzo_memory.api.auth import ( - get_api_key, - get_or_verify_user_id, - require_auth, - verify_api_key, -) - - -class TestAuth: - """Test authentication functions.""" - - def test_get_api_key_from_header(self): - """Test getting API key from Authorization header.""" - request = Mock(spec=Request) - request.headers = {} - credentials = HTTPAuthorizationCredentials( - scheme="Bearer", credentials="test-key" - ) - - api_key = get_api_key(request, credentials) - assert api_key == "test-key" - - def test_get_api_key_from_header_value(self): - """Test getting API key from X-API-Key header.""" - request = Mock(spec=Request) - request.headers = {"x-api-key": "header-key"} - - api_key = get_api_key(request, None) - assert api_key == "header-key" - - def test_get_api_key_none(self): - """Test getting API key when none provided.""" - request = Mock(spec=Request) - request.headers = {} - - api_key = get_api_key(request, None) - assert api_key is None - - @patch("hanzo_memory.api.auth.settings") - def test_verify_api_key_valid(self, mock_settings): - """Test verifying valid API key.""" - mock_settings.disable_auth = False - mock_settings.api_key = "valid-key" - assert verify_api_key("valid-key") is True - - @patch("hanzo_memory.api.auth.settings") - def test_verify_api_key_invalid(self, mock_settings): - """Test verifying invalid API key.""" - mock_settings.disable_auth = False - mock_settings.api_key = "valid-key" - assert verify_api_key("invalid-key") is False - - @patch("hanzo_memory.api.auth.settings") - def test_verify_api_key_none_configured(self, mock_settings): - """Test verifying API key when none configured.""" - mock_settings.disable_auth = False - mock_settings.api_key = None - assert verify_api_key("any-key") is False - - @patch("hanzo_memory.api.auth.settings") - def test_verify_api_key_none_provided(self, mock_settings): - """Test verifying when no API key provided.""" - mock_settings.disable_auth = False - mock_settings.api_key = "valid-key" - assert verify_api_key(None) is False - - @patch("hanzo_memory.api.auth.settings") - def test_require_auth_disabled(self, mock_settings): - """Test require_auth when auth is disabled.""" - mock_settings.disable_auth = True - request = Mock(spec=Request) - request.headers = {} - - result = require_auth(request, None) - assert result == "disabled" - - @patch("hanzo_memory.api.auth.settings") - def test_require_auth_valid_key(self, mock_settings): - """Test require_auth with valid API key.""" - mock_settings.disable_auth = False - mock_settings.api_key = "valid-key" - request = Mock(spec=Request) - request.headers = {"x-api-key": "valid-key"} - - result = require_auth(request, None) - assert result == "valid-key" - - @patch("hanzo_memory.api.auth.settings") - def test_require_auth_invalid_key(self, mock_settings): - """Test require_auth with invalid API key.""" - mock_settings.disable_auth = False - mock_settings.api_key = "valid-key" - request = Mock(spec=Request) - request.headers = {"x-api-key": "invalid-key"} - - with pytest.raises(HTTPException) as exc_info: - require_auth(request, None) - assert exc_info.value.status_code == status.HTTP_401_UNAUTHORIZED - - @patch("hanzo_memory.api.auth.settings") - async def test_get_or_verify_user_id_auth_disabled(self, mock_settings): - """Test get_or_verify_user_id when auth is disabled.""" - mock_settings.disable_auth = True - request = Mock(spec=Request) - - result = await get_or_verify_user_id("user123", None, request) - assert result == "user123" - - @patch("hanzo_memory.api.auth.settings") - @patch("hanzo_memory.api.auth.require_auth") - async def test_get_or_verify_user_id_auth_enabled( - self, mock_require_auth, mock_settings - ): - """Test get_or_verify_user_id when auth is enabled.""" - mock_settings.disable_auth = False - mock_require_auth.return_value = "valid-key" - request = Mock(spec=Request) - credentials = Mock(spec=HTTPAuthorizationCredentials) - - result = await get_or_verify_user_id("user123", credentials, request) - assert result == "user123" - mock_require_auth.assert_called_once_with(request, credentials) diff --git a/pkg/hanzo-memory/tests/test_chat_api.py b/pkg/hanzo-memory/tests/test_chat_api.py deleted file mode 100644 index d7fa7328f..000000000 --- a/pkg/hanzo-memory/tests/test_chat_api.py +++ /dev/null @@ -1,230 +0,0 @@ -"""Tests for Chat API endpoints.""" - -import polars as pl -import pytest -from fastapi.testclient import TestClient - -from hanzo_memory.server import app - - -class TestChatAPI: - """Test Chat API endpoints.""" - - @pytest.fixture(autouse=True) - def setup(self, mock_auth, mock_db_client, mock_services): - """Set up test client and mocks.""" - self.client = TestClient(app) - self.headers = {"Authorization": "Bearer test-token"} - self.mock_db = mock_db_client - self.mock_embedding_service = mock_services["embedding"] - - def test_create_chat_session(self): - """Test creating a chat session.""" - response = self.client.post( - "/v1/chat/sessions/create", - json={ - "userid": "user123", - "session_id": "session123", - "project_id": "proj123", - }, - headers=self.headers, - ) - - assert response.status_code == 200 - data = response.json() - assert data["session_id"] == "session123" - assert data["userid"] == "user123" - assert data["project_id"] == "proj123" - assert data["created"] is True - - # Verify chat table was created - self.mock_db.create_chats_table.assert_called_once_with("user123") - - def test_create_chat_session_auto_ids(self): - """Test creating a chat session with auto-generated IDs.""" - response = self.client.post( - "/v1/chat/sessions/create", - json={"userid": "user123"}, - headers=self.headers, - ) - - assert response.status_code == 200 - data = response.json() - assert "session_id" in data - assert data["userid"] == "user123" - assert data["project_id"] == "default-user123" - - def test_add_chat_message(self): - """Test adding a chat message.""" - # Mock empty search results (no duplicates) - self.mock_db.search_chats.return_value = pl.DataFrame() - self.mock_db.add_chat_message.return_value = {"chat_id": "msg123"} - - response = self.client.post( - "/v1/chat/messages/add", - json={ - "userid": "user123", - "session_id": "session123", - "role": "user", - "content": "Hello, how are you?", - "metadata": {"timestamp": "2023-01-01T12:00:00Z"}, - }, - headers=self.headers, - ) - - assert response.status_code == 200 - data = response.json() - assert "chat_id" in data - assert data["session_id"] == "session123" - assert data["duplicate"] is False - - # Verify embedding was generated - self.mock_embedding_service.embed_text.assert_called_once_with( - "Hello, how are you?" - ) - - # Verify message was added - self.mock_db.add_chat_message.assert_called_once() - - def test_add_duplicate_chat_message(self): - """Test adding a duplicate chat message.""" - # Mock search results with duplicate - mock_df = pl.DataFrame( - { - "chat_id": ["existing123"], - "content": ["Hello, how are you?"], - "role": ["user"], - "_similarity": [0.995], - } - ) - self.mock_db.search_chats.return_value = mock_df - - response = self.client.post( - "/v1/chat/messages/add", - json={ - "userid": "user123", - "session_id": "session123", - "role": "user", - "content": "Hello, how are you?", - }, - headers=self.headers, - ) - - assert response.status_code == 200 - data = response.json() - assert data["chat_id"] == "existing123" - assert data["duplicate"] is True - - # Verify message was NOT added - self.mock_db.add_chat_message.assert_not_called() - - def test_get_chat_messages(self): - """Test getting chat messages for a session.""" - # Mock chat history - mock_df = pl.DataFrame( - { - "chat_id": ["msg1", "msg2", "msg3"], - "role": ["user", "assistant", "user"], - "content": ["Hello", "Hi there!", "How are you?"], - "metadata": ["{}", '{"model": "gpt-4"}', "{}"], - "created_at": [ - "2023-01-01T12:00:00", - "2023-01-01T12:01:00", - "2023-01-01T12:02:00", - ], - } - ) - self.mock_db.get_chat_history.return_value = mock_df - - response = self.client.get( - "/v1/chat/sessions/session123/messages", - params={"userid": "user123", "limit": 100}, - headers=self.headers, - ) - - assert response.status_code == 200 - data = response.json() - assert data["session_id"] == "session123" - assert len(data["messages"]) == 3 - assert data["total"] == 3 - - # Check message order - assert data["messages"][0]["content"] == "Hello" - assert data["messages"][1]["content"] == "Hi there!" - assert data["messages"][2]["content"] == "How are you?" - - def test_search_chat_messages(self): - """Test searching chat messages.""" - # Mock search results - mock_df = pl.DataFrame( - { - "chat_id": ["msg1", "msg2"], - "session_id": ["session1", "session2"], - "role": ["user", "assistant"], - "content": ["Tell me about Python", "Python is a programming language"], - "_similarity": [0.95, 0.92], - "created_at": ["2023-01-01T12:00:00", "2023-01-01T13:00:00"], - } - ) - self.mock_db.search_chats.return_value = mock_df - - response = self.client.post( - "/v1/chat/search", - params={ - "query": "Python programming", - "userid": "user123", - "limit": 10, - }, - headers=self.headers, - ) - - assert response.status_code == 200 - data = response.json() - assert data["query"] == "Python programming" - assert len(data["messages"]) == 2 - assert data["messages"][0]["similarity_score"] == 0.95 - assert "Python" in data["messages"][0]["content"] - - # Verify search was called - self.mock_db.search_chats.assert_called_once() - search_args = self.mock_db.search_chats.call_args[1] - assert search_args["user_id"] == "user123" - assert search_args["limit"] == 10 - - def test_search_chat_with_filters(self): - """Test searching chat messages with filters.""" - self.mock_db.search_chats.return_value = pl.DataFrame() - - response = self.client.post( - "/v1/chat/search", - params={ - "query": "test query", - "userid": "user123", - "project_id": "proj123", - "session_id": "session123", - "limit": 5, - }, - headers=self.headers, - ) - - assert response.status_code == 200 - - # Verify filters were passed - search_args = self.mock_db.search_chats.call_args[1] - assert search_args["project_id"] == "proj123" - assert search_args["session_id"] == "session123" - - def test_chat_error_handling(self): - """Test error handling in chat operations.""" - self.mock_db.create_chats_table.side_effect = Exception("DB Error") - - response = self.client.post( - "/v1/chat/sessions/create", - json={"userid": "user123"}, - headers=self.headers, - ) - - assert response.status_code == 500 - data = response.json() - assert "detail" in data - assert "DB Error" in data["detail"] diff --git a/pkg/hanzo-memory/tests/test_cli.py b/pkg/hanzo-memory/tests/test_cli.py deleted file mode 100644 index b4ebc63dc..000000000 --- a/pkg/hanzo-memory/tests/test_cli.py +++ /dev/null @@ -1,68 +0,0 @@ -"""Tests for CLI.""" - -from unittest.mock import patch - -from click.testing import CliRunner - -from hanzo_memory.cli import cli, main - - -class TestCLI: - """Test CLI commands.""" - - def test_cli_version(self): - """Test CLI version command.""" - runner = CliRunner() - result = runner.invoke(cli, ["--version"]) - assert result.exit_code == 0 - assert "0.1.0" in result.output - - def test_cli_help(self): - """Test CLI help command.""" - runner = CliRunner() - result = runner.invoke(cli, ["--help"]) - assert result.exit_code == 0 - assert "Hanzo Memory Service" in result.output - - @patch("hanzo_memory.cli.run_server") - def test_server_command_default(self, mock_run): - """Test server command with default options.""" - runner = CliRunner() - result = runner.invoke(cli, ["server"]) - assert result.exit_code == 0 - assert "Starting Hanzo Memory Service on 0.0.0.0:4000" in result.output - mock_run.assert_called_once() - - @patch("hanzo_memory.cli.run_server") - def test_server_command_custom(self, mock_run): - """Test server command with custom host and port.""" - runner = CliRunner() - result = runner.invoke(cli, ["server", "--host", "127.0.0.1", "--port", "8080"]) - assert result.exit_code == 0 - assert "Starting Hanzo Memory Service on 127.0.0.1:8080" in result.output - mock_run.assert_called_once() - - @patch("hanzo_memory.cli.settings") - def test_info_command(self, mock_settings): - """Test info command.""" - # Mock settings - mock_settings.infinity_db_path = "/test/path" - mock_settings.embedding_model = "test-model" - mock_settings.llm_model = "gpt-test" - mock_settings.disable_auth = False - - runner = CliRunner() - result = runner.invoke(cli, ["info"]) - assert result.exit_code == 0 - assert "Hanzo Memory Service" in result.output - assert "Version: 0.1.0" in result.output - assert "/test/path" in result.output - assert "test-model" in result.output - assert "gpt-test" in result.output - assert "False" in result.output - - @patch("hanzo_memory.cli.cli") - def test_main_entry_point(self, mock_cli): - """Test main entry point.""" - main() - mock_cli.assert_called_once() diff --git a/pkg/hanzo-memory/tests/test_embeddings.py b/pkg/hanzo-memory/tests/test_embeddings.py deleted file mode 100644 index 1618c6f21..000000000 --- a/pkg/hanzo-memory/tests/test_embeddings.py +++ /dev/null @@ -1,63 +0,0 @@ -"""Test embedding service.""" - -import pytest - -from hanzo_memory.services.embeddings import EmbeddingService - - -class TestEmbeddingService: - """Test embedding service.""" - - @pytest.fixture - def embedding_service(self): - """Create embedding service.""" - return EmbeddingService() - - def test_embed_single(self, embedding_service): - """Test single text embedding.""" - text = "Hello, world!" - embedding = embedding_service.embed_single(text) - - assert isinstance(embedding, list) - assert len(embedding) == 384 # BGE small dimension - assert all(isinstance(x, float) for x in embedding) - - def test_embed_batch(self, embedding_service): - """Test batch text embedding.""" - texts = ["Hello", "World", "Test"] - embeddings = embedding_service.embed_batch(texts) - - assert len(embeddings) == 3 - assert all(len(emb) == 384 for emb in embeddings) - - def test_compute_similarity_cosine(self, embedding_service): - """Test cosine similarity computation.""" - # Create simple test embeddings - query = [1.0, 0.0, 0.0] - embeddings = [ - [1.0, 0.0, 0.0], # Same as query - [0.0, 1.0, 0.0], # Orthogonal - [-1.0, 0.0, 0.0], # Opposite - ] - - similarities = embedding_service.compute_similarity( - query, embeddings, metric="cosine" - ) - - assert len(similarities) == 3 - assert similarities[0] > 0.99 # Very similar - assert abs(similarities[1]) < 0.01 # Orthogonal - assert similarities[2] < -0.99 # Opposite - - def test_empty_batch(self, embedding_service): - """Test embedding empty batch.""" - embeddings = embedding_service.embed_batch([]) - assert embeddings == [] - - def test_model_info(self, embedding_service): - """Test getting model info.""" - info = embedding_service.get_model_info() - - assert info["model_name"] == "BAAI/bge-small-en-v1.5" - assert info["dimensions"] == 384 - assert "loaded" in info diff --git a/pkg/hanzo-memory/tests/test_embeddings_edge_cases.py b/pkg/hanzo-memory/tests/test_embeddings_edge_cases.py deleted file mode 100644 index 61587d78c..000000000 --- a/pkg/hanzo-memory/tests/test_embeddings_edge_cases.py +++ /dev/null @@ -1,60 +0,0 @@ -"""Tests for embedding service edge cases.""" - -import pytest - -from hanzo_memory.services.embeddings import EmbeddingService - - -class TestEmbeddingsEdgeCases: - """Test embedding service edge cases.""" - - @pytest.fixture - def embedding_service(self): - """Create embedding service.""" - return EmbeddingService() - - def test_compute_similarity_dot_product(self, embedding_service): - """Test compute similarity with dot product metric.""" - query = [1.0, 2.0, 3.0] - embeddings = [ - [1.0, 2.0, 3.0], # Same as query - [3.0, 2.0, 1.0], # Different - ] - - scores = embedding_service.compute_similarity(query, embeddings, metric="dot") - - assert len(scores) == 2 - assert scores[0] > scores[1] # First should have higher dot product - - def test_compute_similarity_euclidean(self, embedding_service): - """Test compute similarity with euclidean metric.""" - query = [1.0, 2.0, 3.0] - embeddings = [ - [1.0, 2.0, 3.0], # Same as query (distance = 0) - [4.0, 5.0, 6.0], # Different - ] - - scores = embedding_service.compute_similarity( - query, embeddings, metric="euclidean" - ) - - assert len(scores) == 2 - # Euclidean returns negative distance, so same vector should have highest score - assert scores[0] > scores[1] - - def test_compute_similarity_empty_embeddings(self, embedding_service): - """Test compute similarity with empty embeddings.""" - query = [1.0, 2.0, 3.0] - embeddings = [] - - scores = embedding_service.compute_similarity(query, embeddings) - - assert scores == [] - - def test_compute_similarity_invalid_metric(self, embedding_service): - """Test compute similarity with invalid metric.""" - query = [1.0, 2.0, 3.0] - embeddings = [[1.0, 2.0, 3.0]] - - with pytest.raises(ValueError, match="Unknown metric"): - embedding_service.compute_similarity(query, embeddings, metric="invalid") diff --git a/pkg/hanzo-memory/tests/test_graph_links.py b/pkg/hanzo-memory/tests/test_graph_links.py deleted file mode 100644 index 654e80931..000000000 --- a/pkg/hanzo-memory/tests/test_graph_links.py +++ /dev/null @@ -1,116 +0,0 @@ -"""Tests for hanzo_memory.graph_links โ€” mirror of @hanzo/bot-graph-links TS suite.""" - -from hanzo_memory.graph_links import Edge, extract_edges, reconcile, slugify - - -# โ”€โ”€ slugify โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_slugify_lowercases_dashes_ascii() -> None: - assert slugify("Acme AI Inc.") == "acme-ai-inc" - assert slugify("Josรฉ's Pizza") == "jose-s-pizza" - assert slugify("Slack & Discord") == "slack-and-discord" - - -# โ”€โ”€ extract_edges โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def _has(edges: list[Edge], **fields: object) -> bool: - return any(all(getattr(e, k) == v for k, v in fields.items()) for e in edges) - - -def test_mentions_from_md_links() -> None: - edges = extract_edges("originals/idea-1", "Inspired by [Alice](people/alice) at Acme.") - assert _has(edges, target="people/alice", type="mentions") - - -def test_meeting_emits_attended_not_mentions() -> None: - edges = extract_edges( - "meetings/2026-05-10", - "Met with [Bob](people/bob) and [Carol](people/carol).", - page_type="meeting", - ) - types = {e.type for e in edges} - assert "attended" in types - assert "mentions" not in types - - -def test_founded_inference() -> None: - edges = extract_edges("people/alice", "Alice co-founded Acme AI. She also runs Beta Co.") - assert _has(edges, type="founded", target="companies/acme-ai") - - -def test_invested_in_inference() -> None: - e1 = extract_edges("people/dan", "Dan invested in Foobar.") - assert _has(e1, type="invested_in", target="companies/foobar") - - e2 = extract_edges("people/erin", "Erin led Quux's seed round.") - assert _has(e2, type="invested_in", target="companies/quux") - - -def test_advises_inference() -> None: - edges = extract_edges("people/frank", "Frank is an advisor to Globex.") - assert _has(edges, type="advises", target="people/globex") - - -def test_works_at_inference() -> None: - e1 = extract_edges("people/grace", "Grace is the CEO of Acme.") - assert _has(e1, type="works_at", target="companies/acme") - - e2 = extract_edges("people/henry", "Henry joined Initech in 2024.") - assert _has(e2, type="works_at", target="companies/initech") - - -def test_strip_code_fences() -> None: - edges = extract_edges( - "concepts/snippet", - "Normal: [link](people/real). Code:\n```\nfake = [should](people/fake)\n```\n", - ) - targets = {e.target for e in edges} - assert "people/real" in targets - assert "people/fake" not in targets - - -def test_dedup_same_target_same_type() -> None: - edges = extract_edges("originals/x", "[Alice](people/alice) and again [Alice](people/alice).") - alice = [e for e in edges if e.target == "people/alice" and e.type == "mentions"] - assert len(alice) == 1 - - -def test_bare_slug_refs() -> None: - edges = extract_edges("concepts/note", "See people/alice and companies/acme-ai.") - targets = {e.target for e in edges} - assert "people/alice" in targets - assert "companies/acme-ai" in targets - - -# โ”€โ”€ reconcile โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_reconcile_add_remove() -> None: - prior = [Edge("a", "x", "mentions"), Edge("a", "y", "mentions")] - next_ = [Edge("a", "y", "mentions"), Edge("a", "z", "mentions")] - add, remove = reconcile(prior, next_) - assert [e.target for e in add] == ["z"] - assert [e.target for e in remove] == ["x"] - - -def test_reconcile_no_change() -> None: - same = [Edge("a", "x", "mentions")] - add, remove = reconcile(same, same) - assert add == [] and remove == [] - - -# โ”€โ”€ recipes โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def test_recipes_list_and_load() -> None: - from hanzo_memory.recipes import list_recipes, load_recipe - - names = list_recipes() - assert "email" in names - - email = load_recipe("email") - assert email["recipe"] == "email" - assert email["version"] == 1 - assert email["cron"].startswith("*/30") diff --git a/pkg/hanzo-memory/tests/test_infinity_client.py b/pkg/hanzo-memory/tests/test_infinity_client.py deleted file mode 100644 index 9ac06f826..000000000 --- a/pkg/hanzo-memory/tests/test_infinity_client.py +++ /dev/null @@ -1,158 +0,0 @@ -"""Test InfinityDB client.""" - -import uuid - - -class TestInfinityClient: - """Test InfinityDB client.""" - - def test_client_initialization(self, db_client): - """Test client initialization.""" - assert db_client is not None - assert hasattr(db_client, "infinity") - - def test_create_project(self, db_client, sample_user_id): - """Test project creation.""" - # Ensure projects table exists - db_client.create_projects_table() - - project_id = f"test_project_{uuid.uuid4().hex[:8]}" - - project = db_client.create_project( - project_id=project_id, - user_id=sample_user_id, - name="Test Project", - description="A test project", - metadata={"test": True}, - ) - - assert project["project_id"] == project_id - assert project["user_id"] == sample_user_id - assert project["name"] == "Test Project" - assert "created_at" in project - - def test_memory_operations(self, db_client, sample_user_id, sample_project_id): - """Test memory CRUD operations.""" - # Create memories table - db_client.create_memories_table(sample_user_id) - - # Add a memory - memory_id = f"mem_{uuid.uuid4().hex[:8]}" - embedding = [0.1] * 384 # Mock embedding - - memory = db_client.add_memory( - memory_id=memory_id, - user_id=sample_user_id, - project_id=sample_project_id, - content="Test memory content", - embedding=embedding, - metadata={"source": "test"}, - importance=7.5, - ) - - assert memory["memory_id"] == memory_id - assert memory["content"] == "Test memory content" - assert memory["importance"] == 7.5 - - def test_memory_search(self, db_client, sample_user_id, sample_project_id): - """Test memory search functionality.""" - # Create memories table - db_client.create_memories_table(sample_user_id) - - # Add some memories - memories_data = [ - ("Python programming is fun", [0.8, 0.2] + [0.1] * 382), - ("JavaScript is also great", [0.2, 0.8] + [0.1] * 382), - ("I love coding", [0.5, 0.5] + [0.1] * 382), - ] - - for i, (content, embedding) in enumerate(memories_data): - db_client.add_memory( - memory_id=f"mem_{i}", - user_id=sample_user_id, - project_id=sample_project_id, - content=content, - embedding=embedding, - ) - - # Search for Python-related memories - query_embedding = [0.9, 0.1] + [0.1] * 382 - results = db_client.search_memories( - user_id=sample_user_id, - query_embedding=query_embedding, - limit=2, - ) - - assert not results.is_empty() - assert len(results) <= 2 - - def test_knowledge_base_operations( - self, db_client, sample_user_id, sample_project_id - ): - """Test knowledge base operations.""" - # Ensure knowledge bases table exists - db_client.create_knowledge_bases_table() - - kb_id = f"kb_{uuid.uuid4().hex[:8]}" - - # Create knowledge base - kb = db_client.create_knowledge_base( - kb_id=kb_id, - user_id=sample_user_id, - project_id=sample_project_id, - name="Test Knowledge Base", - description="Testing KB", - ) - - assert kb["kb_id"] == kb_id - assert kb["name"] == "Test Knowledge Base" - - # Add a fact - fact_id = f"fact_{uuid.uuid4().hex[:8]}" - embedding = [0.1] * 384 - - fact = db_client.add_fact( - fact_id=fact_id, - kb_id=kb_id, - content="Python was created by Guido van Rossum", - embedding=embedding, - metadata={"category": "history"}, - ) - - assert fact["fact_id"] == fact_id - assert fact["content"] == "Python was created by Guido van Rossum" - - def test_chat_operations(self, db_client, sample_user_id, sample_project_id): - """Test chat operations.""" - # Create chats table - db_client.create_chats_table(sample_user_id) - - # Add chat messages - session_id = f"session_{uuid.uuid4().hex[:8]}" - messages = [ - ("user", "Hello, how are you?"), - ("assistant", "I'm doing well, thank you!"), - ] - - for i, (role, content) in enumerate(messages): - embedding = [0.1] * 384 - chat = db_client.add_chat_message( - chat_id=f"chat_{i}", - user_id=sample_user_id, - project_id=sample_project_id, - session_id=session_id, - role=role, - content=content, - embedding=embedding, - ) - - assert chat["role"] == role - assert chat["content"] == content - - # Get chat history - history = db_client.get_chat_history( - user_id=sample_user_id, - session_id=session_id, - ) - - assert not history.is_empty() diff --git a/pkg/hanzo-memory/tests/test_knowledge_api.py b/pkg/hanzo-memory/tests/test_knowledge_api.py deleted file mode 100644 index 275dab88c..000000000 --- a/pkg/hanzo-memory/tests/test_knowledge_api.py +++ /dev/null @@ -1,228 +0,0 @@ -"""Tests for Knowledge Base API endpoints.""" - -import polars as pl -import pytest -from fastapi.testclient import TestClient - -from hanzo_memory.server import app - - -class TestKnowledgeAPI: - """Test Knowledge Base API endpoints.""" - - @pytest.fixture(autouse=True) - def setup(self, mock_auth, mock_db_client, mock_services): - """Set up test client and mocks.""" - self.client = TestClient(app) - self.headers = {"Authorization": "Bearer test-token"} - self.mock_db = mock_db_client - self.mock_embedding_service = mock_services["embedding"] - - def test_create_knowledge_base(self): - """Test creating a knowledge base.""" - self.mock_db.create_knowledge_base.return_value = { - "kb_id": "kb123", - "name": "Test KB", - } - - response = self.client.post( - "/v1/kb/create", - json={ - "userid": "user123", - "name": "Test KB", - "kb_id": "kb123", - }, - headers=self.headers, - ) - - assert response.status_code == 200 - data = response.json() - assert data["kb_id"] == "kb123" - assert "Test KB" in data["message"] - - # Verify DB calls - self.mock_db.create_knowledge_base.assert_called_once() - call_args = self.mock_db.create_knowledge_base.call_args[1] - assert call_args["kb_id"] == "kb123" - assert call_args["user_id"] == "user123" - assert call_args["name"] == "Test KB" - - def test_create_knowledge_base_default_project(self): - """Test creating KB with default project.""" - # Mock project creation to raise (already exists) - self.mock_db.create_project.side_effect = Exception("Already exists") - self.mock_db.create_knowledge_base.return_value = {"kb_id": "kb123"} - - response = self.client.post( - "/v1/kb/create", - json={ - "userid": "user123", - "name": "Test KB", - }, - headers=self.headers, - ) - - assert response.status_code == 200 - - # Verify default project was attempted - self.mock_db.create_project.assert_called_once() - project_args = self.mock_db.create_project.call_args[1] - assert project_args["project_id"] == "default-user123" - assert project_args["user_id"] == "user123" - - def test_list_knowledge_bases(self): - """Test listing knowledge bases.""" - response = self.client.get( - "/v1/kb/list", - params={"userid": "user123"}, - headers=self.headers, - ) - - assert response.status_code == 200 - data = response.json() - assert data["userid"] == "user123" - assert "knowledge_bases" in data - assert data["total"] == 0 - - def test_add_facts(self): - """Test adding facts to a knowledge base.""" - self.mock_embedding_service.embed_text.return_value = [[0.1] * 384] - self.mock_db.add_fact.return_value = {"fact_id": "fact123"} - - response = self.client.post( - "/v1/kb/facts/add", - json={ - "userid": "user123", - "kb_id": "kb123", - "facts": [ - { - "content": "Test fact 1", - "metadata": {"type": "test"}, - }, - { - "content": "Test fact 2", - "parent_id": "fact123", - }, - ], - }, - headers=self.headers, - ) - - assert response.status_code == 200 - data = response.json() - assert data["kb_id"] == "kb123" - assert data["facts_added"] == 2 - assert len(data["facts"]) == 2 - - # Verify embeddings were generated - assert self.mock_embedding_service.embed_text.call_count == 2 - - # Verify facts were added - assert self.mock_db.add_fact.call_count == 2 - - def test_get_facts_with_query(self): - """Test searching facts with a query.""" - # Mock search results - mock_df = pl.DataFrame( - { - "fact_id": ["fact1", "fact2"], - "content": ["Fact 1", "Fact 2"], - "parent_id": ["", "parent1"], - "metadata": ['{"type": "test"}', '{"type": "example"}'], - "_similarity": [0.95, 0.85], - } - ) - - self.mock_db.search_facts.return_value = mock_df - self.mock_embedding_service.embed_text.return_value = [[0.2] * 384] - - response = self.client.post( - "/v1/kb/facts/get", - json={ - "userid": "user123", - "kb_id": "kb123", - "query": "test query", - "limit": 10, - }, - headers=self.headers, - ) - - assert response.status_code == 200 - data = response.json() - assert data["kb_id"] == "kb123" - assert len(data["facts"]) == 2 - assert data["facts"][0]["fact_id"] == "fact1" - assert data["facts"][0]["similarity_score"] == 0.95 - - # Verify search was called - self.mock_db.search_facts.assert_called_once() - search_args = self.mock_db.search_facts.call_args[1] - assert search_args["kb_id"] == "kb123" - assert search_args["limit"] == 10 - - def test_get_facts_without_query(self): - """Test getting facts without a search query.""" - # API requires query parameter - response = self.client.post( - "/v1/kb/facts/get", - json={ - "userid": "user123", - "kb_id": "kb123", - }, - headers=self.headers, - ) - assert response.status_code == 400 - - # With query, should return empty results - response = self.client.post( - "/v1/kb/facts/get", - json={ - "userid": "user123", - "kb_id": "kb123", - "query": "test", - }, - headers=self.headers, - ) - assert response.status_code == 200 - data = response.json() - assert data["kb_id"] == "kb123" - assert data["facts"] == [] - assert data["total"] == 0 - - def test_delete_fact(self): - """Test deleting a fact.""" - response = self.client.post( - "/v1/kb/facts/delete", - json={ - "userid": "user123", - "kb_id": "kb123", - "fact_id": "fact123", - "cascade": True, - }, - headers=self.headers, - ) - - assert response.status_code == 200 - data = response.json() - assert data["kb_id"] == "kb123" - assert data["fact_id"] == "fact123" - assert data["deleted"] is True - assert data["cascade"] is True - - def test_knowledge_base_error_handling(self): - """Test error handling in knowledge base operations.""" - self.mock_db.create_knowledge_base.side_effect = Exception("DB Error") - - response = self.client.post( - "/v1/kb/create", - json={ - "userid": "user123", - "name": "Test KB", - }, - headers=self.headers, - ) - - assert response.status_code == 500 - data = response.json() - assert "detail" in data - assert "DB Error" in data["detail"] diff --git a/pkg/hanzo-memory/tests/test_llm_service.py b/pkg/hanzo-memory/tests/test_llm_service.py deleted file mode 100644 index fa62b8d2d..000000000 --- a/pkg/hanzo-memory/tests/test_llm_service.py +++ /dev/null @@ -1,98 +0,0 @@ -"""Test LLM service.""" - -import json - -import pytest - -from hanzo_memory.services.llm import LLMService - - -class TestLLMService: - """Test LLM service.""" - - @pytest.fixture - def llm_service(self, mock_llm_completion): - """Create LLM service.""" - - # Configure mock to return appropriate responses - def side_effect(*args, **kwargs): - mock_response = type("MockResponse", (), {})() - mock_choice = type("MockChoice", (), {})() - mock_message = type("MockMessage", (), {})() - - # Check if JSON response is requested - if kwargs.get("response_format", {}).get("type") == "json_object": - mock_message.content = json.dumps( - { - "action": "add_fact", - "facts": [{"content": "Test fact", "metadata": {}}], - "reasoning": "Test reasoning", - } - ) - else: - mock_message.content = "Test response" - - mock_choice.message = mock_message - mock_response.choices = [mock_choice] - return mock_response - - mock_llm_completion.side_effect = side_effect - return LLMService() - - def test_complete(self, llm_service): - """Test basic completion.""" - # This will use the configured model or fallback - result = llm_service.complete("Hello, ") - assert isinstance(result, str) - - def test_chat(self, llm_service): - """Test chat functionality.""" - messages = [ - {"role": "user", "content": "Hello!"}, - ] - result = llm_service.chat(messages) - assert isinstance(result, str) - - def test_summarize_for_knowledge_skip(self, llm_service): - """Test knowledge summarization with skip.""" - content = "This is some test content." - - result = llm_service.summarize_for_knowledge( - content=content, - skip_summarization=True, - ) - - assert result["summary"] == content - assert "knowledge_instructions" in result - assert result["knowledge_instructions"]["action"] == "add_fact" - - def test_summarize_for_knowledge_provided(self, llm_service): - """Test knowledge summarization with provided summary.""" - content = "Long content that needs summarization..." - provided_summary = "Short summary" - - result = llm_service.summarize_for_knowledge( - content=content, - provided_summary=provided_summary, - ) - - assert result["summary"] == provided_summary - assert "knowledge_instructions" in result - - def test_summarize_for_knowledge_generated(self, llm_service): - """Test knowledge summarization with generation.""" - content = "This is a test document about Python programming." - - result = llm_service.summarize_for_knowledge( - content=content, - context="Programming tutorial", - ) - - assert "summary" in result - assert "knowledge_instructions" in result - - # Check if instructions are valid JSON structure - instructions = result["knowledge_instructions"] - assert "action" in instructions - assert "facts" in instructions - assert "reasoning" in instructions diff --git a/pkg/hanzo-memory/tests/test_mcp_server.py b/pkg/hanzo-memory/tests/test_mcp_server.py deleted file mode 100644 index 7c49db3f3..000000000 --- a/pkg/hanzo-memory/tests/test_mcp_server.py +++ /dev/null @@ -1,247 +0,0 @@ -"""Tests for MCP server.""" - -from unittest.mock import MagicMock, patch - -import pytest - -from hanzo_memory.mcp.server import MCPMemoryServer - - -@pytest.fixture -def mcp_server(): - """Create MCP server instance.""" - with ( - patch("hanzo_memory.mcp.server.get_db_client"), - patch("hanzo_memory.mcp.server.EmbeddingService"), - patch("hanzo_memory.mcp.server.LLMService"), - ): - server = MCPMemoryServer() - # Mock the embedding service - server.embedding_service.embed_text = MagicMock(return_value=[[0.1] * 384]) - return server - - -@pytest.mark.asyncio -async def test_tools_are_registered(mcp_server): - """Test that tools are properly registered.""" - # Just verify that _setup_handlers was called and tools exist - # The actual tool listing would be tested during integration - assert hasattr(mcp_server, "server") - assert hasattr(mcp_server, "db_client") - assert hasattr(mcp_server, "embedding_service") - assert hasattr(mcp_server, "llm_service") - - -@pytest.mark.asyncio -async def test_handle_remember(mcp_server): - """Test remember tool.""" - # Mock DB operations - mcp_server.db_client.create_memories_table = MagicMock() - mcp_server.db_client.add_memory = MagicMock(return_value={"memory_id": "test-id"}) - - result = await mcp_server._handle_remember( - { - "user_id": "user123", - "project_id": "proj123", - "content": "Test memory content", - "metadata": {"tag": "test"}, - "importance": 5.0, - } - ) - - assert result["success"] is True - assert "memory_id" in result - assert result["message"] == "Memory stored successfully" - - # Verify DB calls - mcp_server.db_client.create_memories_table.assert_called_once_with("user123") - mcp_server.db_client.add_memory.assert_called_once() - - -@pytest.mark.asyncio -async def test_handle_recall(mcp_server): - """Test recall tool.""" - # Mock search results - import polars as pl - - mock_df = pl.DataFrame( - { - "memory_id": ["mem1", "mem2"], - "content": ["Memory 1", "Memory 2"], - "metadata": ['{"tag": "test1"}', '{"tag": "test2"}'], - "importance": [5.0, 3.0], - "_similarity": [0.9, 0.7], - } - ) - - mcp_server.db_client.search_memories = MagicMock(return_value=mock_df) - - result = await mcp_server._handle_recall( - { - "user_id": "user123", - "query": "test query", - "limit": 5, - } - ) - - assert result["success"] is True - assert len(result["memories"]) == 2 - assert result["memories"][0]["content"] == "Memory 1" - assert result["memories"][0]["similarity_score"] == 0.9 - - -@pytest.mark.asyncio -async def test_handle_create_project(mcp_server): - """Test create project tool.""" - mcp_server.db_client.create_project = MagicMock( - return_value={"project_id": "proj123"} - ) - - result = await mcp_server._handle_create_project( - { - "user_id": "user123", - "name": "Test Project", - "description": "A test project", - "metadata": {"category": "test"}, - } - ) - - assert result["success"] is True - assert "project_id" in result - assert result["message"] == "Project created successfully" - - -@pytest.mark.asyncio -async def test_handle_create_knowledge_base(mcp_server): - """Test create knowledge base tool.""" - mcp_server.db_client.create_knowledge_base = MagicMock( - return_value={"kb_id": "kb123"} - ) - - result = await mcp_server._handle_create_knowledge_base( - { - "user_id": "user123", - "project_id": "proj123", - "name": "Test KB", - "description": "A test knowledge base", - } - ) - - assert result["success"] is True - assert "kb_id" in result - assert result["message"] == "Knowledge base created successfully" - - -@pytest.mark.asyncio -async def test_handle_add_fact(mcp_server): - """Test add fact tool.""" - mcp_server.db_client.add_fact = MagicMock(return_value={"fact_id": "fact123"}) - - result = await mcp_server._handle_add_fact( - { - "kb_id": "kb123", - "content": "Test fact content", - "parent_id": "parent123", - "metadata": {"type": "definition"}, - } - ) - - assert result["success"] is True - assert "fact_id" in result - assert result["message"] == "Fact added successfully" - - -@pytest.mark.asyncio -async def test_handle_search_facts(mcp_server): - """Test search facts tool.""" - import polars as pl - - mock_df = pl.DataFrame( - { - "fact_id": ["fact1", "fact2"], - "content": ["Fact 1", "Fact 2"], - "parent_id": ["", "parent1"], - "metadata": ['{"type": "def"}', '{"type": "example"}'], - "_similarity": [0.95, 0.85], - } - ) - - mcp_server.db_client.search_facts = MagicMock(return_value=mock_df) - - result = await mcp_server._handle_search_facts( - { - "kb_id": "kb123", - "query": "test query", - "limit": 10, - } - ) - - assert result["success"] is True - assert len(result["facts"]) == 2 - assert result["facts"][0]["content"] == "Fact 1" - assert result["facts"][0]["similarity_score"] == 0.95 - - -@pytest.mark.asyncio -async def test_handle_summarize_for_knowledge(mcp_server): - """Test summarize for knowledge tool.""" - mcp_server.llm_service.summarize_for_knowledge = MagicMock( - return_value={ - "summary": "Test summary", - "knowledge_instructions": { - "action": "add_fact", - "facts": [{"content": "Extracted fact"}], - "reasoning": "Test reasoning", - }, - } - ) - - result = await mcp_server._handle_summarize_for_knowledge( - { - "content": "Long content to summarize", - "context": "Additional context", - "skip_summarization": False, - } - ) - - assert "summary" in result - assert "knowledge_instructions" in result - assert result["knowledge_instructions"]["action"] == "add_fact" - - -@pytest.mark.asyncio -async def test_handle_tool_error_handling(mcp_server): - """Test error handling in tool calls.""" - # Mock error - mcp_server.db_client.create_memories_table = MagicMock( - side_effect=Exception("DB Error") - ) - - # Call the handler directly instead of through decorated function - try: - await mcp_server._handle_remember( - { - "user_id": "user123", - "project_id": "proj123", - "content": "Test memory", - } - ) - raise AssertionError("Should have raised an exception") - except Exception as e: - assert "DB Error" in str(e) - - -@pytest.mark.asyncio -async def test_mcp_server_initialization(mcp_server): - """Test MCP server is properly initialized.""" - # Verify the server is set up correctly - assert mcp_server.server is not None - assert mcp_server.server.name == "hanzo-memory" - - # Verify services are initialized - assert mcp_server.db_client is not None - assert mcp_server.embedding_service is not None - assert mcp_server.llm_service is not None - - # The actual MCP protocol testing would be done via integration tests - # with the full MCP framework running diff --git a/pkg/hanzo-memory/tests/test_memory_api.py b/pkg/hanzo-memory/tests/test_memory_api.py deleted file mode 100644 index 850b3274d..000000000 --- a/pkg/hanzo-memory/tests/test_memory_api.py +++ /dev/null @@ -1,134 +0,0 @@ -"""Test memory API endpoints.""" - -from fastapi import status - - -class TestMemoryAPI: - """Test memory API endpoints.""" - - def test_health_check(self, client): - """Test health check endpoint.""" - response = client.get("/health") - assert response.status_code == status.HTTP_200_OK - data = response.json() - assert data["status"] == "healthy" - assert data["service"] == "hanzo-memory" - - def test_remember_endpoint(self, client, sample_user_id, sample_memory_content): - """Test /v1/remember endpoint.""" - request_data = { - "userid": sample_user_id, - "messagecontent": sample_memory_content, - "strippii": False, - "filterresults": False, - "includememoryid": False, - } - - response = client.post("/v1/remember", json=request_data) - assert response.status_code == status.HTTP_200_OK - - data = response.json() - assert data["user_id"] == sample_user_id - assert data["memory_stored"] is True - assert "relevant_memories" in data - assert "usage_info" in data - - def test_remember_with_memory_ids(self, client, sample_user_id): - """Test /v1/remember with memory IDs included.""" - # First, add a memory - request_data = { - "userid": sample_user_id, - "messagecontent": "I love Python programming", - "includememoryid": True, - } - - response = client.post("/v1/remember", json=request_data) - assert response.status_code == status.HTTP_200_OK - - # Search for similar memory - search_data = { - "userid": sample_user_id, - "messagecontent": "Python is great", - "includememoryid": True, - } - - response = client.post("/v1/remember", json=search_data) - assert response.status_code == status.HTTP_200_OK - - data = response.json() - memories = data["relevant_memories"] - if memories: - assert isinstance(memories[0], dict) - assert "content" in memories[0] - assert "memoryId" in memories[0] - - def test_add_memories(self, client, sample_user_id): - """Test /v1/memories/add endpoint.""" - # Test with single memory - request_data = { - "userid": sample_user_id, - "memoriestoadd": "Single memory content", - } - - response = client.post("/v1/memories/add", json=request_data) - assert response.status_code == status.HTTP_200_OK - - data = response.json() - assert data["userid"] == sample_user_id - assert data["added_count"] == 1 - assert len(data["memory_ids"]) == 1 - - # Test with multiple memories - request_data = { - "userid": sample_user_id, - "memoriestoadd": [ - "First memory", - "Second memory", - "Third memory", - ], - } - - response = client.post("/v1/memories/add", json=request_data) - assert response.status_code == status.HTTP_200_OK - - data = response.json() - assert data["added_count"] == 3 - assert len(data["memory_ids"]) == 3 - - def test_get_memories(self, client, sample_user_id): - """Test /v1/memories/get endpoint.""" - # Without memoryid, API returns 400 - request_data = { - "userid": sample_user_id, - "limit": 10, - } - response = client.post("/v1/memories/get", json=request_data) - assert response.status_code == status.HTTP_400_BAD_REQUEST - - # With memoryid but non-existent, returns 404 - request_data = { - "userid": sample_user_id, - "memoryid": "non-existent-memory", - } - response = client.post("/v1/memories/get", json=request_data) - assert response.status_code == status.HTTP_404_NOT_FOUND - - def test_delete_user_memories(self, client, sample_user_id): - """Test /v1/user/delete endpoint.""" - # Test without confirmation - request_data = { - "userid": sample_user_id, - "confirmdelete": False, - } - - response = client.post("/v1/user/delete", json=request_data) - assert response.status_code == status.HTTP_400_BAD_REQUEST - - # Test with confirmation - request_data["confirmdelete"] = True - response = client.post("/v1/user/delete", json=request_data) - assert response.status_code == status.HTTP_200_OK - - data = response.json() - assert data["userid"] == sample_user_id - assert "deleted_count" in data diff --git a/pkg/hanzo-memory/tests/test_unified_memory_tool.py b/pkg/hanzo-memory/tests/test_unified_memory_tool.py deleted file mode 100644 index ab87d28f0..000000000 --- a/pkg/hanzo-memory/tests/test_unified_memory_tool.py +++ /dev/null @@ -1,90 +0,0 @@ -import pytest -import asyncio -from unittest.mock import MagicMock, patch -from hanzo_memory.mcp.server import MCPMemoryServer - - -@pytest.fixture -def memory_server(): - server = MCPMemoryServer() - # Mock services to avoid external dependencies - server.db_client = MagicMock() - server.embedding_service = MagicMock() - server.llm_service = MagicMock() - - # Mock embedding return - server.embedding_service.embed_text.return_value = [[0.1, 0.2, 0.3]] - - # Mock DB returns - server.db_client.search_memories.return_value = [] - server.db_client.search_facts.return_value = [] - server.db_client.delete_memory.return_value = True - server.db_client.delete_fact.return_value = True - - return server - - -@pytest.mark.asyncio -async def test_remember_and_recall(memory_server): - # Test remember - remember_args = {"user_id": "user1", "content": "Test memory", "action": "remember"} - result = await memory_server._handle_remember(remember_args) - assert result["success"] is True - assert "memory_id" in result - - # Verify DB call - memory_server.db_client.add_memory.assert_called_once() - - # Test recall - recall_args = {"user_id": "user1", "query": "Test", "action": "recall"} - result = await memory_server._handle_recall(recall_args) - assert result["success"] is True - assert "memories" in result - - -@pytest.mark.asyncio -async def test_delete_memory(memory_server): - delete_args = {"user_id": "user1", "id": "mem_123", "action": "delete"} - result = await memory_server._handle_delete_memory(delete_args) - assert result["success"] is True - - # Verify DB call - memory_server.db_client.delete_memory.assert_called_once_with( - memory_id="mem_123", user_id="user1", project_id="default" - ) - - -@pytest.mark.asyncio -async def test_knowledge_base_operations(memory_server): - # Test create KB - kb_args = {"user_id": "user1", "name": "My KB", "action": "create_kb"} - result = await memory_server._handle_create_knowledge_base(kb_args) - assert result["success"] is True - assert "kb_id" in result - kb_id = result["kb_id"] - - # Test add fact - fact_args = { - "user_id": "user1", - "kb_id": kb_id, - "content": "A fact", - "action": "add_fact", - } - result = await memory_server._handle_add_fact(fact_args) - assert result["success"] is True - assert "fact_id" in result - fact_id = result["fact_id"] - - # Test delete fact - del_fact_args = { - "user_id": "user1", # required by schema though not strictly used in delete_fact impl - "kb_id": kb_id, - "id": fact_id, - "action": "delete_fact", - } - result = await memory_server._handle_delete_fact(del_fact_args) - assert result["success"] is True - - memory_server.db_client.delete_fact.assert_called_once_with( - fact_id=fact_id, knowledge_base_id=kb_id - ) diff --git a/pkg/hanzo-memory/uv.lock b/pkg/hanzo-memory/uv.lock deleted file mode 100644 index b6baccec8..000000000 --- a/pkg/hanzo-memory/uv.lock +++ /dev/null @@ -1,5046 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.10" -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", - "python_full_version < '3.11'", -] - -[[package]] -name = "aiocache" -version = "0.12.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7a/64/b945b8025a9d1e6e2138845f4022165d3b337f55f50984fbc6a4c0a1e355/aiocache-0.12.3.tar.gz", hash = "sha256:f528b27bf4d436b497a1d0d1a8f59a542c153ab1e37c3621713cb376d44c4713", size = 132196, upload-time = "2024-09-25T13:20:23.823Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/37/d7/15d67e05b235d1ed8c3ce61688fe4d84130e72af1657acadfaac3479f4cf/aiocache-0.12.3-py2.py3-none-any.whl", hash = "sha256:889086fc24710f431937b87ad3720a289f7fc31c4fd8b68e9f918b9bacd8270d", size = 28199, upload-time = "2024-09-25T13:20:22.688Z" }, -] - -[[package]] -name = "aiohappyeyeballs" -version = "2.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, -] - -[[package]] -name = "aiohttp" -version = "3.12.14" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohappyeyeballs" }, - { name = "aiosignal" }, - { name = "async-timeout", marker = "python_full_version < '3.11'" }, - { name = "attrs" }, - { name = "frozenlist" }, - { name = "multidict" }, - { name = "propcache" }, - { name = "yarl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e6/0b/e39ad954107ebf213a2325038a3e7a506be3d98e1435e1f82086eec4cde2/aiohttp-3.12.14.tar.gz", hash = "sha256:6e06e120e34d93100de448fd941522e11dafa78ef1a893c179901b7d66aa29f2", size = 7822921, upload-time = "2025-07-10T13:05:33.968Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/88/f161f429f9de391eee6a5c2cffa54e2ecd5b7122ae99df247f7734dfefcb/aiohttp-3.12.14-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:906d5075b5ba0dd1c66fcaaf60eb09926a9fef3ca92d912d2a0bbdbecf8b1248", size = 702641, upload-time = "2025-07-10T13:02:38.98Z" }, - { url = "https://files.pythonhosted.org/packages/fe/b5/24fa382a69a25d242e2baa3e56d5ea5227d1b68784521aaf3a1a8b34c9a4/aiohttp-3.12.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c875bf6fc2fd1a572aba0e02ef4e7a63694778c5646cdbda346ee24e630d30fb", size = 479005, upload-time = "2025-07-10T13:02:42.714Z" }, - { url = "https://files.pythonhosted.org/packages/09/67/fda1bc34adbfaa950d98d934a23900918f9d63594928c70e55045838c943/aiohttp-3.12.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fbb284d15c6a45fab030740049d03c0ecd60edad9cd23b211d7e11d3be8d56fd", size = 466781, upload-time = "2025-07-10T13:02:44.639Z" }, - { url = "https://files.pythonhosted.org/packages/36/96/3ce1ea96d3cf6928b87cfb8cdd94650367f5c2f36e686a1f5568f0f13754/aiohttp-3.12.14-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38e360381e02e1a05d36b223ecab7bc4a6e7b5ab15760022dc92589ee1d4238c", size = 1648841, upload-time = "2025-07-10T13:02:46.356Z" }, - { url = "https://files.pythonhosted.org/packages/be/04/ddea06cb4bc7d8db3745cf95e2c42f310aad485ca075bd685f0e4f0f6b65/aiohttp-3.12.14-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aaf90137b5e5d84a53632ad95ebee5c9e3e7468f0aab92ba3f608adcb914fa95", size = 1622896, upload-time = "2025-07-10T13:02:48.422Z" }, - { url = "https://files.pythonhosted.org/packages/73/66/63942f104d33ce6ca7871ac6c1e2ebab48b88f78b2b7680c37de60f5e8cd/aiohttp-3.12.14-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e532a25e4a0a2685fa295a31acf65e027fbe2bea7a4b02cdfbbba8a064577663", size = 1695302, upload-time = "2025-07-10T13:02:50.078Z" }, - { url = "https://files.pythonhosted.org/packages/20/00/aab615742b953f04b48cb378ee72ada88555b47b860b98c21c458c030a23/aiohttp-3.12.14-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eab9762c4d1b08ae04a6c77474e6136da722e34fdc0e6d6eab5ee93ac29f35d1", size = 1737617, upload-time = "2025-07-10T13:02:52.123Z" }, - { url = "https://files.pythonhosted.org/packages/d6/4f/ef6d9f77225cf27747368c37b3d69fac1f8d6f9d3d5de2d410d155639524/aiohttp-3.12.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abe53c3812b2899889a7fca763cdfaeee725f5be68ea89905e4275476ffd7e61", size = 1642282, upload-time = "2025-07-10T13:02:53.899Z" }, - { url = "https://files.pythonhosted.org/packages/37/e1/e98a43c15aa52e9219a842f18c59cbae8bbe2d50c08d298f17e9e8bafa38/aiohttp-3.12.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5760909b7080aa2ec1d320baee90d03b21745573780a072b66ce633eb77a8656", size = 1582406, upload-time = "2025-07-10T13:02:55.515Z" }, - { url = "https://files.pythonhosted.org/packages/71/5c/29c6dfb49323bcdb0239bf3fc97ffcf0eaf86d3a60426a3287ec75d67721/aiohttp-3.12.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:02fcd3f69051467bbaa7f84d7ec3267478c7df18d68b2e28279116e29d18d4f3", size = 1626255, upload-time = "2025-07-10T13:02:57.343Z" }, - { url = "https://files.pythonhosted.org/packages/79/60/ec90782084090c4a6b459790cfd8d17be2c5662c9c4b2d21408b2f2dc36c/aiohttp-3.12.14-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4dcd1172cd6794884c33e504d3da3c35648b8be9bfa946942d353b939d5f1288", size = 1637041, upload-time = "2025-07-10T13:02:59.008Z" }, - { url = "https://files.pythonhosted.org/packages/22/89/205d3ad30865c32bc472ac13f94374210745b05bd0f2856996cb34d53396/aiohttp-3.12.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:224d0da41355b942b43ad08101b1b41ce633a654128ee07e36d75133443adcda", size = 1612494, upload-time = "2025-07-10T13:03:00.618Z" }, - { url = "https://files.pythonhosted.org/packages/48/ae/2f66edaa8bd6db2a4cba0386881eb92002cdc70834e2a93d1d5607132c7e/aiohttp-3.12.14-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e387668724f4d734e865c1776d841ed75b300ee61059aca0b05bce67061dcacc", size = 1692081, upload-time = "2025-07-10T13:03:02.154Z" }, - { url = "https://files.pythonhosted.org/packages/08/3a/fa73bfc6e21407ea57f7906a816f0dc73663d9549da703be05dbd76d2dc3/aiohttp-3.12.14-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:dec9cde5b5a24171e0b0a4ca064b1414950904053fb77c707efd876a2da525d8", size = 1715318, upload-time = "2025-07-10T13:03:04.322Z" }, - { url = "https://files.pythonhosted.org/packages/e3/b3/751124b8ceb0831c17960d06ee31a4732cb4a6a006fdbfa1153d07c52226/aiohttp-3.12.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bbad68a2af4877cc103cd94af9160e45676fc6f0c14abb88e6e092b945c2c8e3", size = 1643660, upload-time = "2025-07-10T13:03:06.406Z" }, - { url = "https://files.pythonhosted.org/packages/81/3c/72477a1d34edb8ab8ce8013086a41526d48b64f77e381c8908d24e1c18f5/aiohttp-3.12.14-cp310-cp310-win32.whl", hash = "sha256:ee580cb7c00bd857b3039ebca03c4448e84700dc1322f860cf7a500a6f62630c", size = 428289, upload-time = "2025-07-10T13:03:08.274Z" }, - { url = "https://files.pythonhosted.org/packages/a2/c4/8aec4ccf1b822ec78e7982bd5cf971113ecce5f773f04039c76a083116fc/aiohttp-3.12.14-cp310-cp310-win_amd64.whl", hash = "sha256:cf4f05b8cea571e2ccc3ca744e35ead24992d90a72ca2cf7ab7a2efbac6716db", size = 451328, upload-time = "2025-07-10T13:03:10.146Z" }, - { url = "https://files.pythonhosted.org/packages/53/e1/8029b29316971c5fa89cec170274582619a01b3d82dd1036872acc9bc7e8/aiohttp-3.12.14-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f4552ff7b18bcec18b60a90c6982049cdb9dac1dba48cf00b97934a06ce2e597", size = 709960, upload-time = "2025-07-10T13:03:11.936Z" }, - { url = "https://files.pythonhosted.org/packages/96/bd/4f204cf1e282041f7b7e8155f846583b19149e0872752711d0da5e9cc023/aiohttp-3.12.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8283f42181ff6ccbcf25acaae4e8ab2ff7e92b3ca4a4ced73b2c12d8cd971393", size = 482235, upload-time = "2025-07-10T13:03:14.118Z" }, - { url = "https://files.pythonhosted.org/packages/d6/0f/2a580fcdd113fe2197a3b9df30230c7e85bb10bf56f7915457c60e9addd9/aiohttp-3.12.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:040afa180ea514495aaff7ad34ec3d27826eaa5d19812730fe9e529b04bb2179", size = 470501, upload-time = "2025-07-10T13:03:16.153Z" }, - { url = "https://files.pythonhosted.org/packages/38/78/2c1089f6adca90c3dd74915bafed6d6d8a87df5e3da74200f6b3a8b8906f/aiohttp-3.12.14-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b413c12f14c1149f0ffd890f4141a7471ba4b41234fe4fd4a0ff82b1dc299dbb", size = 1740696, upload-time = "2025-07-10T13:03:18.4Z" }, - { url = "https://files.pythonhosted.org/packages/4a/c8/ce6c7a34d9c589f007cfe064da2d943b3dee5aabc64eaecd21faf927ab11/aiohttp-3.12.14-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1d6f607ce2e1a93315414e3d448b831238f1874b9968e1195b06efaa5c87e245", size = 1689365, upload-time = "2025-07-10T13:03:20.629Z" }, - { url = "https://files.pythonhosted.org/packages/18/10/431cd3d089de700756a56aa896faf3ea82bee39d22f89db7ddc957580308/aiohttp-3.12.14-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:565e70d03e924333004ed101599902bba09ebb14843c8ea39d657f037115201b", size = 1788157, upload-time = "2025-07-10T13:03:22.44Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b2/26f4524184e0f7ba46671c512d4b03022633bcf7d32fa0c6f1ef49d55800/aiohttp-3.12.14-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4699979560728b168d5ab63c668a093c9570af2c7a78ea24ca5212c6cdc2b641", size = 1827203, upload-time = "2025-07-10T13:03:24.628Z" }, - { url = "https://files.pythonhosted.org/packages/e0/30/aadcdf71b510a718e3d98a7bfeaea2396ac847f218b7e8edb241b09bd99a/aiohttp-3.12.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad5fdf6af93ec6c99bf800eba3af9a43d8bfd66dce920ac905c817ef4a712afe", size = 1729664, upload-time = "2025-07-10T13:03:26.412Z" }, - { url = "https://files.pythonhosted.org/packages/67/7f/7ccf11756ae498fdedc3d689a0c36ace8fc82f9d52d3517da24adf6e9a74/aiohttp-3.12.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ac76627c0b7ee0e80e871bde0d376a057916cb008a8f3ffc889570a838f5cc7", size = 1666741, upload-time = "2025-07-10T13:03:28.167Z" }, - { url = "https://files.pythonhosted.org/packages/6b/4d/35ebc170b1856dd020c92376dbfe4297217625ef4004d56587024dc2289c/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:798204af1180885651b77bf03adc903743a86a39c7392c472891649610844635", size = 1715013, upload-time = "2025-07-10T13:03:30.018Z" }, - { url = "https://files.pythonhosted.org/packages/7b/24/46dc0380146f33e2e4aa088b92374b598f5bdcde1718c77e8d1a0094f1a4/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4f1205f97de92c37dd71cf2d5bcfb65fdaed3c255d246172cce729a8d849b4da", size = 1710172, upload-time = "2025-07-10T13:03:31.821Z" }, - { url = "https://files.pythonhosted.org/packages/2f/0a/46599d7d19b64f4d0fe1b57bdf96a9a40b5c125f0ae0d8899bc22e91fdce/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:76ae6f1dd041f85065d9df77c6bc9c9703da9b5c018479d20262acc3df97d419", size = 1690355, upload-time = "2025-07-10T13:03:34.754Z" }, - { url = "https://files.pythonhosted.org/packages/08/86/b21b682e33d5ca317ef96bd21294984f72379454e689d7da584df1512a19/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a194ace7bc43ce765338ca2dfb5661489317db216ea7ea700b0332878b392cab", size = 1783958, upload-time = "2025-07-10T13:03:36.53Z" }, - { url = "https://files.pythonhosted.org/packages/4f/45/f639482530b1396c365f23c5e3b1ae51c9bc02ba2b2248ca0c855a730059/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:16260e8e03744a6fe3fcb05259eeab8e08342c4c33decf96a9dad9f1187275d0", size = 1804423, upload-time = "2025-07-10T13:03:38.504Z" }, - { url = "https://files.pythonhosted.org/packages/7e/e5/39635a9e06eed1d73671bd4079a3caf9cf09a49df08490686f45a710b80e/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c779e5ebbf0e2e15334ea404fcce54009dc069210164a244d2eac8352a44b28", size = 1717479, upload-time = "2025-07-10T13:03:40.158Z" }, - { url = "https://files.pythonhosted.org/packages/51/e1/7f1c77515d369b7419c5b501196526dad3e72800946c0099594c1f0c20b4/aiohttp-3.12.14-cp311-cp311-win32.whl", hash = "sha256:a289f50bf1bd5be227376c067927f78079a7bdeccf8daa6a9e65c38bae14324b", size = 427907, upload-time = "2025-07-10T13:03:41.801Z" }, - { url = "https://files.pythonhosted.org/packages/06/24/a6bf915c85b7a5b07beba3d42b3282936b51e4578b64a51e8e875643c276/aiohttp-3.12.14-cp311-cp311-win_amd64.whl", hash = "sha256:0b8a69acaf06b17e9c54151a6c956339cf46db4ff72b3ac28516d0f7068f4ced", size = 452334, upload-time = "2025-07-10T13:03:43.485Z" }, - { url = "https://files.pythonhosted.org/packages/c3/0d/29026524e9336e33d9767a1e593ae2b24c2b8b09af7c2bd8193762f76b3e/aiohttp-3.12.14-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a0ecbb32fc3e69bc25efcda7d28d38e987d007096cbbeed04f14a6662d0eee22", size = 701055, upload-time = "2025-07-10T13:03:45.59Z" }, - { url = "https://files.pythonhosted.org/packages/0a/b8/a5e8e583e6c8c1056f4b012b50a03c77a669c2e9bf012b7cf33d6bc4b141/aiohttp-3.12.14-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0400f0ca9bb3e0b02f6466421f253797f6384e9845820c8b05e976398ac1d81a", size = 475670, upload-time = "2025-07-10T13:03:47.249Z" }, - { url = "https://files.pythonhosted.org/packages/29/e8/5202890c9e81a4ec2c2808dd90ffe024952e72c061729e1d49917677952f/aiohttp-3.12.14-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a56809fed4c8a830b5cae18454b7464e1529dbf66f71c4772e3cfa9cbec0a1ff", size = 468513, upload-time = "2025-07-10T13:03:49.377Z" }, - { url = "https://files.pythonhosted.org/packages/23/e5/d11db8c23d8923d3484a27468a40737d50f05b05eebbb6288bafcb467356/aiohttp-3.12.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27f2e373276e4755691a963e5d11756d093e346119f0627c2d6518208483fb6d", size = 1715309, upload-time = "2025-07-10T13:03:51.556Z" }, - { url = "https://files.pythonhosted.org/packages/53/44/af6879ca0eff7a16b1b650b7ea4a827301737a350a464239e58aa7c387ef/aiohttp-3.12.14-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ca39e433630e9a16281125ef57ece6817afd1d54c9f1bf32e901f38f16035869", size = 1697961, upload-time = "2025-07-10T13:03:53.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/94/18457f043399e1ec0e59ad8674c0372f925363059c276a45a1459e17f423/aiohttp-3.12.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c748b3f8b14c77720132b2510a7d9907a03c20ba80f469e58d5dfd90c079a1c", size = 1753055, upload-time = "2025-07-10T13:03:55.368Z" }, - { url = "https://files.pythonhosted.org/packages/26/d9/1d3744dc588fafb50ff8a6226d58f484a2242b5dd93d8038882f55474d41/aiohttp-3.12.14-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0a568abe1b15ce69d4cc37e23020720423f0728e3cb1f9bcd3f53420ec3bfe7", size = 1799211, upload-time = "2025-07-10T13:03:57.216Z" }, - { url = "https://files.pythonhosted.org/packages/73/12/2530fb2b08773f717ab2d249ca7a982ac66e32187c62d49e2c86c9bba9b4/aiohttp-3.12.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9888e60c2c54eaf56704b17feb558c7ed6b7439bca1e07d4818ab878f2083660", size = 1718649, upload-time = "2025-07-10T13:03:59.469Z" }, - { url = "https://files.pythonhosted.org/packages/b9/34/8d6015a729f6571341a311061b578e8b8072ea3656b3d72329fa0faa2c7c/aiohttp-3.12.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3006a1dc579b9156de01e7916d38c63dc1ea0679b14627a37edf6151bc530088", size = 1634452, upload-time = "2025-07-10T13:04:01.698Z" }, - { url = "https://files.pythonhosted.org/packages/ff/4b/08b83ea02595a582447aeb0c1986792d0de35fe7a22fb2125d65091cbaf3/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa8ec5c15ab80e5501a26719eb48a55f3c567da45c6ea5bb78c52c036b2655c7", size = 1695511, upload-time = "2025-07-10T13:04:04.165Z" }, - { url = "https://files.pythonhosted.org/packages/b5/66/9c7c31037a063eec13ecf1976185c65d1394ded4a5120dd5965e3473cb21/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:39b94e50959aa07844c7fe2206b9f75d63cc3ad1c648aaa755aa257f6f2498a9", size = 1716967, upload-time = "2025-07-10T13:04:06.132Z" }, - { url = "https://files.pythonhosted.org/packages/ba/02/84406e0ad1acb0fb61fd617651ab6de760b2d6a31700904bc0b33bd0894d/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:04c11907492f416dad9885d503fbfc5dcb6768d90cad8639a771922d584609d3", size = 1657620, upload-time = "2025-07-10T13:04:07.944Z" }, - { url = "https://files.pythonhosted.org/packages/07/53/da018f4013a7a179017b9a274b46b9a12cbeb387570f116964f498a6f211/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:88167bd9ab69bb46cee91bd9761db6dfd45b6e76a0438c7e884c3f8160ff21eb", size = 1737179, upload-time = "2025-07-10T13:04:10.182Z" }, - { url = "https://files.pythonhosted.org/packages/49/e8/ca01c5ccfeaafb026d85fa4f43ceb23eb80ea9c1385688db0ef322c751e9/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:791504763f25e8f9f251e4688195e8b455f8820274320204f7eafc467e609425", size = 1765156, upload-time = "2025-07-10T13:04:12.029Z" }, - { url = "https://files.pythonhosted.org/packages/22/32/5501ab525a47ba23c20613e568174d6c63aa09e2caa22cded5c6ea8e3ada/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2785b112346e435dd3a1a67f67713a3fe692d288542f1347ad255683f066d8e0", size = 1724766, upload-time = "2025-07-10T13:04:13.961Z" }, - { url = "https://files.pythonhosted.org/packages/06/af/28e24574801fcf1657945347ee10df3892311c2829b41232be6089e461e7/aiohttp-3.12.14-cp312-cp312-win32.whl", hash = "sha256:15f5f4792c9c999a31d8decf444e79fcfd98497bf98e94284bf390a7bb8c1729", size = 422641, upload-time = "2025-07-10T13:04:16.018Z" }, - { url = "https://files.pythonhosted.org/packages/98/d5/7ac2464aebd2eecac38dbe96148c9eb487679c512449ba5215d233755582/aiohttp-3.12.14-cp312-cp312-win_amd64.whl", hash = "sha256:3b66e1a182879f579b105a80d5c4bd448b91a57e8933564bf41665064796a338", size = 449316, upload-time = "2025-07-10T13:04:18.289Z" }, - { url = "https://files.pythonhosted.org/packages/06/48/e0d2fa8ac778008071e7b79b93ab31ef14ab88804d7ba71b5c964a7c844e/aiohttp-3.12.14-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3143a7893d94dc82bc409f7308bc10d60285a3cd831a68faf1aa0836c5c3c767", size = 695471, upload-time = "2025-07-10T13:04:20.124Z" }, - { url = "https://files.pythonhosted.org/packages/8d/e7/f73206afa33100804f790b71092888f47df65fd9a4cd0e6800d7c6826441/aiohttp-3.12.14-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3d62ac3d506cef54b355bd34c2a7c230eb693880001dfcda0bf88b38f5d7af7e", size = 473128, upload-time = "2025-07-10T13:04:21.928Z" }, - { url = "https://files.pythonhosted.org/packages/df/e2/4dd00180be551a6e7ee979c20fc7c32727f4889ee3fd5b0586e0d47f30e1/aiohttp-3.12.14-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:48e43e075c6a438937c4de48ec30fa8ad8e6dfef122a038847456bfe7b947b63", size = 465426, upload-time = "2025-07-10T13:04:24.071Z" }, - { url = "https://files.pythonhosted.org/packages/de/dd/525ed198a0bb674a323e93e4d928443a680860802c44fa7922d39436b48b/aiohttp-3.12.14-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:077b4488411a9724cecc436cbc8c133e0d61e694995b8de51aaf351c7578949d", size = 1704252, upload-time = "2025-07-10T13:04:26.049Z" }, - { url = "https://files.pythonhosted.org/packages/d8/b1/01e542aed560a968f692ab4fc4323286e8bc4daae83348cd63588e4f33e3/aiohttp-3.12.14-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d8c35632575653f297dcbc9546305b2c1133391089ab925a6a3706dfa775ccab", size = 1685514, upload-time = "2025-07-10T13:04:28.186Z" }, - { url = "https://files.pythonhosted.org/packages/b3/06/93669694dc5fdabdc01338791e70452d60ce21ea0946a878715688d5a191/aiohttp-3.12.14-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b8ce87963f0035c6834b28f061df90cf525ff7c9b6283a8ac23acee6502afd4", size = 1737586, upload-time = "2025-07-10T13:04:30.195Z" }, - { url = "https://files.pythonhosted.org/packages/a5/3a/18991048ffc1407ca51efb49ba8bcc1645961f97f563a6c480cdf0286310/aiohttp-3.12.14-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0a2cf66e32a2563bb0766eb24eae7e9a269ac0dc48db0aae90b575dc9583026", size = 1786958, upload-time = "2025-07-10T13:04:32.482Z" }, - { url = "https://files.pythonhosted.org/packages/30/a8/81e237f89a32029f9b4a805af6dffc378f8459c7b9942712c809ff9e76e5/aiohttp-3.12.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdea089caf6d5cde975084a884c72d901e36ef9c2fd972c9f51efbbc64e96fbd", size = 1709287, upload-time = "2025-07-10T13:04:34.493Z" }, - { url = "https://files.pythonhosted.org/packages/8c/e3/bd67a11b0fe7fc12c6030473afd9e44223d456f500f7cf526dbaa259ae46/aiohttp-3.12.14-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a7865f27db67d49e81d463da64a59365ebd6b826e0e4847aa111056dcb9dc88", size = 1622990, upload-time = "2025-07-10T13:04:36.433Z" }, - { url = "https://files.pythonhosted.org/packages/83/ba/e0cc8e0f0d9ce0904e3cf2d6fa41904e379e718a013c721b781d53dcbcca/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0ab5b38a6a39781d77713ad930cb5e7feea6f253de656a5f9f281a8f5931b086", size = 1676015, upload-time = "2025-07-10T13:04:38.958Z" }, - { url = "https://files.pythonhosted.org/packages/d8/b3/1e6c960520bda094c48b56de29a3d978254637ace7168dd97ddc273d0d6c/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9b3b15acee5c17e8848d90a4ebc27853f37077ba6aec4d8cb4dbbea56d156933", size = 1707678, upload-time = "2025-07-10T13:04:41.275Z" }, - { url = "https://files.pythonhosted.org/packages/0a/19/929a3eb8c35b7f9f076a462eaa9830b32c7f27d3395397665caa5e975614/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e4c972b0bdaac167c1e53e16a16101b17c6d0ed7eac178e653a07b9f7fad7151", size = 1650274, upload-time = "2025-07-10T13:04:43.483Z" }, - { url = "https://files.pythonhosted.org/packages/22/e5/81682a6f20dd1b18ce3d747de8eba11cbef9b270f567426ff7880b096b48/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7442488b0039257a3bdbc55f7209587911f143fca11df9869578db6c26feeeb8", size = 1726408, upload-time = "2025-07-10T13:04:45.577Z" }, - { url = "https://files.pythonhosted.org/packages/8c/17/884938dffaa4048302985483f77dfce5ac18339aad9b04ad4aaa5e32b028/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f68d3067eecb64c5e9bab4a26aa11bd676f4c70eea9ef6536b0a4e490639add3", size = 1759879, upload-time = "2025-07-10T13:04:47.663Z" }, - { url = "https://files.pythonhosted.org/packages/95/78/53b081980f50b5cf874359bde707a6eacd6c4be3f5f5c93937e48c9d0025/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f88d3704c8b3d598a08ad17d06006cb1ca52a1182291f04979e305c8be6c9758", size = 1708770, upload-time = "2025-07-10T13:04:49.944Z" }, - { url = "https://files.pythonhosted.org/packages/ed/91/228eeddb008ecbe3ffa6c77b440597fdf640307162f0c6488e72c5a2d112/aiohttp-3.12.14-cp313-cp313-win32.whl", hash = "sha256:a3c99ab19c7bf375c4ae3debd91ca5d394b98b6089a03231d4c580ef3c2ae4c5", size = 421688, upload-time = "2025-07-10T13:04:51.993Z" }, - { url = "https://files.pythonhosted.org/packages/66/5f/8427618903343402fdafe2850738f735fd1d9409d2a8f9bcaae5e630d3ba/aiohttp-3.12.14-cp313-cp313-win_amd64.whl", hash = "sha256:3f8aad695e12edc9d571f878c62bedc91adf30c760c8632f09663e5f564f4baa", size = 448098, upload-time = "2025-07-10T13:04:53.999Z" }, -] - -[[package]] -name = "aiosignal" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "frozenlist" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, -] - -[[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.9.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/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, -] - -[[package]] -name = "asttokens" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978, upload-time = "2024-11-30T04:30:14.439Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918, upload-time = "2024-11-30T04:30:10.946Z" }, -] - -[[package]] -name = "async-timeout" -version = "5.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, -] - -[[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 = "babel" -version = "2.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, -] - -[[package]] -name = "backoff" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" }, -] - -[[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 = "backports-tarfile" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", size = 86406, upload-time = "2024-05-28T17:01:54.731Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181, upload-time = "2024-05-28T17:01:53.112Z" }, -] - -[[package]] -name = "backrefs" -version = "5.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/a7/312f673df6a79003279e1f55619abbe7daebbb87c17c976ddc0345c04c7b/backrefs-5.9.tar.gz", hash = "sha256:808548cb708d66b82ee231f962cb36faaf4f2baab032f2fbb783e9c2fdddaa59", size = 5765857, upload-time = "2025-06-22T19:34:13.97Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/19/4d/798dc1f30468134906575156c089c492cf79b5a5fd373f07fe26c4d046bf/backrefs-5.9-py310-none-any.whl", hash = "sha256:db8e8ba0e9de81fcd635f440deab5ae5f2591b54ac1ebe0550a2ca063488cd9f", size = 380267, upload-time = "2025-06-22T19:34:05.252Z" }, - { url = "https://files.pythonhosted.org/packages/55/07/f0b3375bf0d06014e9787797e6b7cc02b38ac9ff9726ccfe834d94e9991e/backrefs-5.9-py311-none-any.whl", hash = "sha256:6907635edebbe9b2dc3de3a2befff44d74f30a4562adbb8b36f21252ea19c5cf", size = 392072, upload-time = "2025-06-22T19:34:06.743Z" }, - { url = "https://files.pythonhosted.org/packages/9d/12/4f345407259dd60a0997107758ba3f221cf89a9b5a0f8ed5b961aef97253/backrefs-5.9-py312-none-any.whl", hash = "sha256:7fdf9771f63e6028d7fee7e0c497c81abda597ea45d6b8f89e8ad76994f5befa", size = 397947, upload-time = "2025-06-22T19:34:08.172Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/fa31834dc27a7f05e5290eae47c82690edc3a7b37d58f7fb35a1bdbf355b/backrefs-5.9-py313-none-any.whl", hash = "sha256:cc37b19fa219e93ff825ed1fed8879e47b4d89aa7a1884860e2db64ccd7c676b", size = 399843, upload-time = "2025-06-22T19:34:09.68Z" }, - { url = "https://files.pythonhosted.org/packages/fc/24/b29af34b2c9c41645a9f4ff117bae860291780d73880f449e0b5d948c070/backrefs-5.9-py314-none-any.whl", hash = "sha256:df5e169836cc8acb5e440ebae9aad4bf9d15e226d3bad049cf3f6a5c20cc8dc9", size = 411762, upload-time = "2025-06-22T19:34:11.037Z" }, - { url = "https://files.pythonhosted.org/packages/41/ff/392bff89415399a979be4a65357a41d92729ae8580a66073d8ec8d810f98/backrefs-5.9-py39-none-any.whl", hash = "sha256:f48ee18f6252b8f5777a22a00a09a85de0ca931658f1dd96d4406a34f3748c60", size = 380265, upload-time = "2025-06-22T19:34:12.405Z" }, -] - -[[package]] -name = "bcrypt" -version = "4.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/5d/6d7433e0f3cd46ce0b43cd65e1db465ea024dbb8216fb2404e919c2ad77b/bcrypt-4.3.0.tar.gz", hash = "sha256:3a3fd2204178b6d2adcf09cb4f6426ffef54762577a7c9b54c159008cb288c18", size = 25697, upload-time = "2025-02-28T01:24:09.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/2c/3d44e853d1fe969d229bd58d39ae6902b3d924af0e2b5a60d17d4b809ded/bcrypt-4.3.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f01e060f14b6b57bbb72fc5b4a83ac21c443c9a2ee708e04a10e9192f90a6281", size = 483719, upload-time = "2025-02-28T01:22:34.539Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e2/58ff6e2a22eca2e2cff5370ae56dba29d70b1ea6fc08ee9115c3ae367795/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5eeac541cefd0bb887a371ef73c62c3cd78535e4887b310626036a7c0a817bb", size = 272001, upload-time = "2025-02-28T01:22:38.078Z" }, - { url = "https://files.pythonhosted.org/packages/37/1f/c55ed8dbe994b1d088309e366749633c9eb90d139af3c0a50c102ba68a1a/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59e1aa0e2cd871b08ca146ed08445038f42ff75968c7ae50d2fdd7860ade2180", size = 277451, upload-time = "2025-02-28T01:22:40.787Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1c/794feb2ecf22fe73dcfb697ea7057f632061faceb7dcf0f155f3443b4d79/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:0042b2e342e9ae3d2ed22727c1262f76cc4f345683b5c1715f0250cf4277294f", size = 272792, upload-time = "2025-02-28T01:22:43.144Z" }, - { url = "https://files.pythonhosted.org/packages/13/b7/0b289506a3f3598c2ae2bdfa0ea66969812ed200264e3f61df77753eee6d/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74a8d21a09f5e025a9a23e7c0fd2c7fe8e7503e4d356c0a2c1486ba010619f09", size = 289752, upload-time = "2025-02-28T01:22:45.56Z" }, - { url = "https://files.pythonhosted.org/packages/dc/24/d0fb023788afe9e83cc118895a9f6c57e1044e7e1672f045e46733421fe6/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:0142b2cb84a009f8452c8c5a33ace5e3dfec4159e7735f5afe9a4d50a8ea722d", size = 277762, upload-time = "2025-02-28T01:22:47.023Z" }, - { url = "https://files.pythonhosted.org/packages/e4/38/cde58089492e55ac4ef6c49fea7027600c84fd23f7520c62118c03b4625e/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:12fa6ce40cde3f0b899729dbd7d5e8811cb892d31b6f7d0334a1f37748b789fd", size = 272384, upload-time = "2025-02-28T01:22:49.221Z" }, - { url = "https://files.pythonhosted.org/packages/de/6a/d5026520843490cfc8135d03012a413e4532a400e471e6188b01b2de853f/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:5bd3cca1f2aa5dbcf39e2aa13dd094ea181f48959e1071265de49cc2b82525af", size = 277329, upload-time = "2025-02-28T01:22:51.603Z" }, - { url = "https://files.pythonhosted.org/packages/b3/a3/4fc5255e60486466c389e28c12579d2829b28a527360e9430b4041df4cf9/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:335a420cfd63fc5bc27308e929bee231c15c85cc4c496610ffb17923abf7f231", size = 305241, upload-time = "2025-02-28T01:22:53.283Z" }, - { url = "https://files.pythonhosted.org/packages/c7/15/2b37bc07d6ce27cc94e5b10fd5058900eb8fb11642300e932c8c82e25c4a/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:0e30e5e67aed0187a1764911af023043b4542e70a7461ad20e837e94d23e1d6c", size = 309617, upload-time = "2025-02-28T01:22:55.461Z" }, - { url = "https://files.pythonhosted.org/packages/5f/1f/99f65edb09e6c935232ba0430c8c13bb98cb3194b6d636e61d93fe60ac59/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b8d62290ebefd49ee0b3ce7500f5dbdcf13b81402c05f6dafab9a1e1b27212f", size = 335751, upload-time = "2025-02-28T01:22:57.81Z" }, - { url = "https://files.pythonhosted.org/packages/00/1b/b324030c706711c99769988fcb694b3cb23f247ad39a7823a78e361bdbb8/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2ef6630e0ec01376f59a006dc72918b1bf436c3b571b80fa1968d775fa02fe7d", size = 355965, upload-time = "2025-02-28T01:22:59.181Z" }, - { url = "https://files.pythonhosted.org/packages/aa/dd/20372a0579dd915dfc3b1cd4943b3bca431866fcb1dfdfd7518c3caddea6/bcrypt-4.3.0-cp313-cp313t-win32.whl", hash = "sha256:7a4be4cbf241afee43f1c3969b9103a41b40bcb3a3f467ab19f891d9bc4642e4", size = 155316, upload-time = "2025-02-28T01:23:00.763Z" }, - { url = "https://files.pythonhosted.org/packages/6d/52/45d969fcff6b5577c2bf17098dc36269b4c02197d551371c023130c0f890/bcrypt-4.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c1949bf259a388863ced887c7861da1df681cb2388645766c89fdfd9004c669", size = 147752, upload-time = "2025-02-28T01:23:02.908Z" }, - { url = "https://files.pythonhosted.org/packages/11/22/5ada0b9af72b60cbc4c9a399fdde4af0feaa609d27eb0adc61607997a3fa/bcrypt-4.3.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:f81b0ed2639568bf14749112298f9e4e2b28853dab50a8b357e31798686a036d", size = 498019, upload-time = "2025-02-28T01:23:05.838Z" }, - { url = "https://files.pythonhosted.org/packages/b8/8c/252a1edc598dc1ce57905be173328eda073083826955ee3c97c7ff5ba584/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:864f8f19adbe13b7de11ba15d85d4a428c7e2f344bac110f667676a0ff84924b", size = 279174, upload-time = "2025-02-28T01:23:07.274Z" }, - { url = "https://files.pythonhosted.org/packages/29/5b/4547d5c49b85f0337c13929f2ccbe08b7283069eea3550a457914fc078aa/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e36506d001e93bffe59754397572f21bb5dc7c83f54454c990c74a468cd589e", size = 283870, upload-time = "2025-02-28T01:23:09.151Z" }, - { url = "https://files.pythonhosted.org/packages/be/21/7dbaf3fa1745cb63f776bb046e481fbababd7d344c5324eab47f5ca92dd2/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:842d08d75d9fe9fb94b18b071090220697f9f184d4547179b60734846461ed59", size = 279601, upload-time = "2025-02-28T01:23:11.461Z" }, - { url = "https://files.pythonhosted.org/packages/6d/64/e042fc8262e971347d9230d9abbe70d68b0a549acd8611c83cebd3eaec67/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7c03296b85cb87db865d91da79bf63d5609284fc0cab9472fdd8367bbd830753", size = 297660, upload-time = "2025-02-28T01:23:12.989Z" }, - { url = "https://files.pythonhosted.org/packages/50/b8/6294eb84a3fef3b67c69b4470fcdd5326676806bf2519cda79331ab3c3a9/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:62f26585e8b219cdc909b6a0069efc5e4267e25d4a3770a364ac58024f62a761", size = 284083, upload-time = "2025-02-28T01:23:14.5Z" }, - { url = "https://files.pythonhosted.org/packages/62/e6/baff635a4f2c42e8788fe1b1633911c38551ecca9a749d1052d296329da6/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:beeefe437218a65322fbd0069eb437e7c98137e08f22c4660ac2dc795c31f8bb", size = 279237, upload-time = "2025-02-28T01:23:16.686Z" }, - { url = "https://files.pythonhosted.org/packages/39/48/46f623f1b0c7dc2e5de0b8af5e6f5ac4cc26408ac33f3d424e5ad8da4a90/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:97eea7408db3a5bcce4a55d13245ab3fa566e23b4c67cd227062bb49e26c585d", size = 283737, upload-time = "2025-02-28T01:23:18.897Z" }, - { url = "https://files.pythonhosted.org/packages/49/8b/70671c3ce9c0fca4a6cc3cc6ccbaa7e948875a2e62cbd146e04a4011899c/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:191354ebfe305e84f344c5964c7cd5f924a3bfc5d405c75ad07f232b6dffb49f", size = 312741, upload-time = "2025-02-28T01:23:21.041Z" }, - { url = "https://files.pythonhosted.org/packages/27/fb/910d3a1caa2d249b6040a5caf9f9866c52114d51523ac2fb47578a27faee/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:41261d64150858eeb5ff43c753c4b216991e0ae16614a308a15d909503617732", size = 316472, upload-time = "2025-02-28T01:23:23.183Z" }, - { url = "https://files.pythonhosted.org/packages/dc/cf/7cf3a05b66ce466cfb575dbbda39718d45a609daa78500f57fa9f36fa3c0/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:33752b1ba962ee793fa2b6321404bf20011fe45b9afd2a842139de3011898fef", size = 343606, upload-time = "2025-02-28T01:23:25.361Z" }, - { url = "https://files.pythonhosted.org/packages/e3/b8/e970ecc6d7e355c0d892b7f733480f4aa8509f99b33e71550242cf0b7e63/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:50e6e80a4bfd23a25f5c05b90167c19030cf9f87930f7cb2eacb99f45d1c3304", size = 362867, upload-time = "2025-02-28T01:23:26.875Z" }, - { url = "https://files.pythonhosted.org/packages/a9/97/8d3118efd8354c555a3422d544163f40d9f236be5b96c714086463f11699/bcrypt-4.3.0-cp38-abi3-win32.whl", hash = "sha256:67a561c4d9fb9465ec866177e7aebcad08fe23aaf6fbd692a6fab69088abfc51", size = 160589, upload-time = "2025-02-28T01:23:28.381Z" }, - { url = "https://files.pythonhosted.org/packages/29/07/416f0b99f7f3997c69815365babbc2e8754181a4b1899d921b3c7d5b6f12/bcrypt-4.3.0-cp38-abi3-win_amd64.whl", hash = "sha256:584027857bc2843772114717a7490a37f68da563b3620f78a849bcb54dc11e62", size = 152794, upload-time = "2025-02-28T01:23:30.187Z" }, - { url = "https://files.pythonhosted.org/packages/6e/c1/3fa0e9e4e0bfd3fd77eb8b52ec198fd6e1fd7e9402052e43f23483f956dd/bcrypt-4.3.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d3efb1157edebfd9128e4e46e2ac1a64e0c1fe46fb023158a407c7892b0f8c3", size = 498969, upload-time = "2025-02-28T01:23:31.945Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d4/755ce19b6743394787fbd7dff6bf271b27ee9b5912a97242e3caf125885b/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08bacc884fd302b611226c01014eca277d48f0a05187666bca23aac0dad6fe24", size = 279158, upload-time = "2025-02-28T01:23:34.161Z" }, - { url = "https://files.pythonhosted.org/packages/9b/5d/805ef1a749c965c46b28285dfb5cd272a7ed9fa971f970435a5133250182/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6746e6fec103fcd509b96bacdfdaa2fbde9a553245dbada284435173a6f1aef", size = 284285, upload-time = "2025-02-28T01:23:35.765Z" }, - { url = "https://files.pythonhosted.org/packages/ab/2b/698580547a4a4988e415721b71eb45e80c879f0fb04a62da131f45987b96/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:afe327968aaf13fc143a56a3360cb27d4ad0345e34da12c7290f1b00b8fe9a8b", size = 279583, upload-time = "2025-02-28T01:23:38.021Z" }, - { url = "https://files.pythonhosted.org/packages/f2/87/62e1e426418204db520f955ffd06f1efd389feca893dad7095bf35612eec/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d9af79d322e735b1fc33404b5765108ae0ff232d4b54666d46730f8ac1a43676", size = 297896, upload-time = "2025-02-28T01:23:39.575Z" }, - { url = "https://files.pythonhosted.org/packages/cb/c6/8fedca4c2ada1b6e889c52d2943b2f968d3427e5d65f595620ec4c06fa2f/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f1e3ffa1365e8702dc48c8b360fef8d7afeca482809c5e45e653af82ccd088c1", size = 284492, upload-time = "2025-02-28T01:23:40.901Z" }, - { url = "https://files.pythonhosted.org/packages/4d/4d/c43332dcaaddb7710a8ff5269fcccba97ed3c85987ddaa808db084267b9a/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3004df1b323d10021fda07a813fd33e0fd57bef0e9a480bb143877f6cba996fe", size = 279213, upload-time = "2025-02-28T01:23:42.653Z" }, - { url = "https://files.pythonhosted.org/packages/dc/7f/1e36379e169a7df3a14a1c160a49b7b918600a6008de43ff20d479e6f4b5/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:531457e5c839d8caea9b589a1bcfe3756b0547d7814e9ce3d437f17da75c32b0", size = 284162, upload-time = "2025-02-28T01:23:43.964Z" }, - { url = "https://files.pythonhosted.org/packages/1c/0a/644b2731194b0d7646f3210dc4d80c7fee3ecb3a1f791a6e0ae6bb8684e3/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:17a854d9a7a476a89dcef6c8bd119ad23e0f82557afbd2c442777a16408e614f", size = 312856, upload-time = "2025-02-28T01:23:46.011Z" }, - { url = "https://files.pythonhosted.org/packages/dc/62/2a871837c0bb6ab0c9a88bf54de0fc021a6a08832d4ea313ed92a669d437/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6fb1fd3ab08c0cbc6826a2e0447610c6f09e983a281b919ed721ad32236b8b23", size = 316726, upload-time = "2025-02-28T01:23:47.575Z" }, - { url = "https://files.pythonhosted.org/packages/0c/a1/9898ea3faac0b156d457fd73a3cb9c2855c6fd063e44b8522925cdd8ce46/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e965a9c1e9a393b8005031ff52583cedc15b7884fce7deb8b0346388837d6cfe", size = 343664, upload-time = "2025-02-28T01:23:49.059Z" }, - { url = "https://files.pythonhosted.org/packages/40/f2/71b4ed65ce38982ecdda0ff20c3ad1b15e71949c78b2c053df53629ce940/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:79e70b8342a33b52b55d93b3a59223a844962bef479f6a0ea318ebbcadf71505", size = 363128, upload-time = "2025-02-28T01:23:50.399Z" }, - { url = "https://files.pythonhosted.org/packages/11/99/12f6a58eca6dea4be992d6c681b7ec9410a1d9f5cf368c61437e31daa879/bcrypt-4.3.0-cp39-abi3-win32.whl", hash = "sha256:b4d4e57f0a63fd0b358eb765063ff661328f69a04494427265950c71b992a39a", size = 160598, upload-time = "2025-02-28T01:23:51.775Z" }, - { url = "https://files.pythonhosted.org/packages/a9/cf/45fb5261ece3e6b9817d3d82b2f343a505fd58674a92577923bc500bd1aa/bcrypt-4.3.0-cp39-abi3-win_amd64.whl", hash = "sha256:e53e074b120f2877a35cc6c736b8eb161377caae8925c17688bd46ba56daaa5b", size = 152799, upload-time = "2025-02-28T01:23:53.139Z" }, - { url = "https://files.pythonhosted.org/packages/55/2d/0c7e5ab0524bf1a443e34cdd3926ec6f5879889b2f3c32b2f5074e99ed53/bcrypt-4.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c950d682f0952bafcceaf709761da0a32a942272fad381081b51096ffa46cea1", size = 275367, upload-time = "2025-02-28T01:23:54.578Z" }, - { url = "https://files.pythonhosted.org/packages/10/4f/f77509f08bdff8806ecc4dc472b6e187c946c730565a7470db772d25df70/bcrypt-4.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:107d53b5c67e0bbc3f03ebf5b030e0403d24dda980f8e244795335ba7b4a027d", size = 280644, upload-time = "2025-02-28T01:23:56.547Z" }, - { url = "https://files.pythonhosted.org/packages/35/18/7d9dc16a3a4d530d0a9b845160e9e5d8eb4f00483e05d44bb4116a1861da/bcrypt-4.3.0-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:b693dbb82b3c27a1604a3dff5bfc5418a7e6a781bb795288141e5f80cf3a3492", size = 274881, upload-time = "2025-02-28T01:23:57.935Z" }, - { url = "https://files.pythonhosted.org/packages/df/c4/ae6921088adf1e37f2a3a6a688e72e7d9e45fdd3ae5e0bc931870c1ebbda/bcrypt-4.3.0-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:b6354d3760fcd31994a14c89659dee887f1351a06e5dac3c1142307172a79f90", size = 280203, upload-time = "2025-02-28T01:23:59.331Z" }, - { url = "https://files.pythonhosted.org/packages/4c/b1/1289e21d710496b88340369137cc4c5f6ee036401190ea116a7b4ae6d32a/bcrypt-4.3.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a839320bf27d474e52ef8cb16449bb2ce0ba03ca9f44daba6d93fa1d8828e48a", size = 275103, upload-time = "2025-02-28T01:24:00.764Z" }, - { url = "https://files.pythonhosted.org/packages/94/41/19be9fe17e4ffc5d10b7b67f10e459fc4eee6ffe9056a88de511920cfd8d/bcrypt-4.3.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:bdc6a24e754a555d7316fa4774e64c6c3997d27ed2d1964d55920c7c227bc4ce", size = 280513, upload-time = "2025-02-28T01:24:02.243Z" }, - { url = "https://files.pythonhosted.org/packages/aa/73/05687a9ef89edebdd8ad7474c16d8af685eb4591c3c38300bb6aad4f0076/bcrypt-4.3.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:55a935b8e9a1d2def0626c4269db3fcd26728cbff1e84f0341465c31c4ee56d8", size = 274685, upload-time = "2025-02-28T01:24:04.512Z" }, - { url = "https://files.pythonhosted.org/packages/63/13/47bba97924ebe86a62ef83dc75b7c8a881d53c535f83e2c54c4bd701e05c/bcrypt-4.3.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:57967b7a28d855313a963aaea51bf6df89f833db4320da458e5b3c5ab6d4c938", size = 280110, upload-time = "2025-02-28T01:24:05.896Z" }, -] - -[[package]] -name = "black" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "mypy-extensions" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "platformdirs" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/3b/4ba3f93ac8d90410423fdd31d7541ada9bcee1df32fb90d26de41ed40e1d/black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32", size = 1629419, upload-time = "2025-01-29T05:37:06.642Z" }, - { url = "https://files.pythonhosted.org/packages/b4/02/0bde0485146a8a5e694daed47561785e8b77a0466ccc1f3e485d5ef2925e/black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da", size = 1461080, upload-time = "2025-01-29T05:37:09.321Z" }, - { url = "https://files.pythonhosted.org/packages/52/0e/abdf75183c830eaca7589144ff96d49bce73d7ec6ad12ef62185cc0f79a2/black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7", size = 1766886, upload-time = "2025-01-29T04:18:24.432Z" }, - { url = "https://files.pythonhosted.org/packages/dc/a6/97d8bb65b1d8a41f8a6736222ba0a334db7b7b77b8023ab4568288f23973/black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9", size = 1419404, upload-time = "2025-01-29T04:19:04.296Z" }, - { url = "https://files.pythonhosted.org/packages/7e/4f/87f596aca05c3ce5b94b8663dbfe242a12843caaa82dd3f85f1ffdc3f177/black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0", size = 1614372, upload-time = "2025-01-29T05:37:11.71Z" }, - { url = "https://files.pythonhosted.org/packages/e7/d0/2c34c36190b741c59c901e56ab7f6e54dad8df05a6272a9747ecef7c6036/black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299", size = 1442865, upload-time = "2025-01-29T05:37:14.309Z" }, - { url = "https://files.pythonhosted.org/packages/21/d4/7518c72262468430ead45cf22bd86c883a6448b9eb43672765d69a8f1248/black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096", size = 1749699, upload-time = "2025-01-29T04:18:17.688Z" }, - { url = "https://files.pythonhosted.org/packages/58/db/4f5beb989b547f79096e035c4981ceb36ac2b552d0ac5f2620e941501c99/black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2", size = 1428028, upload-time = "2025-01-29T04:18:51.711Z" }, - { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988, upload-time = "2025-01-29T05:37:16.707Z" }, - { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985, upload-time = "2025-01-29T05:37:18.273Z" }, - { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816, upload-time = "2025-01-29T04:18:33.823Z" }, - { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860, upload-time = "2025-01-29T04:19:12.944Z" }, - { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload-time = "2025-01-29T05:37:20.574Z" }, - { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload-time = "2025-01-29T05:37:22.106Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload-time = "2025-01-29T04:18:58.564Z" }, - { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload-time = "2025-01-29T04:19:27.63Z" }, - { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" }, -] - -[[package]] -name = "build" -version = "1.2.2.post1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "os_name == 'nt'" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10.2'" }, - { name = "packaging" }, - { name = "pyproject-hooks" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/46/aeab111f8e06793e4f0e421fcad593d547fb8313b50990f31681ee2fb1ad/build-1.2.2.post1.tar.gz", hash = "sha256:b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7", size = 46701, upload-time = "2024-10-06T17:22:25.251Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/c2/80633736cd183ee4a62107413def345f7e6e3c01563dbca1417363cf957e/build-1.2.2.post1-py3-none-any.whl", hash = "sha256:1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5", size = 22950, upload-time = "2024-10-06T17:22:23.299Z" }, -] - -[[package]] -name = "cachetools" -version = "5.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" }, -] - -[[package]] -name = "certifi" -version = "2025.7.14" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981, upload-time = "2025-07-14T03:29:28.449Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722, upload-time = "2025-07-14T03:29:26.863Z" }, -] - -[[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 = "cfgv" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, - { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, - { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, - { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, - { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, - { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, - { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, - { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, - { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, - { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, - { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, - { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, - { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, - { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, - { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, - { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, - { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, - { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, - { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, - { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, - { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, - { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, - { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, - { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, - { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, - { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, - { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, - { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, - { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, - { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, - { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, - { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, - { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, - { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, - { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, - { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, - { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, - { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, - { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, - { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, - { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, - { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, - { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, - { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, - { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, -] - -[[package]] -name = "chromadb" -version = "1.0.15" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "bcrypt" }, - { name = "build" }, - { name = "grpcio" }, - { name = "httpx" }, - { name = "importlib-resources" }, - { name = "jsonschema" }, - { name = "kubernetes" }, - { name = "mmh3" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "onnxruntime" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-otlp-proto-grpc" }, - { name = "opentelemetry-sdk" }, - { name = "orjson" }, - { name = "overrides" }, - { name = "posthog" }, - { name = "pybase64" }, - { name = "pydantic" }, - { name = "pypika" }, - { name = "pyyaml" }, - { name = "rich" }, - { name = "tenacity" }, - { name = "tokenizers" }, - { name = "tqdm" }, - { name = "typer" }, - { name = "typing-extensions" }, - { name = "uvicorn", extra = ["standard"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ad/e2/0653b2e539db5512d2200c759f1bc7f9ef5609fe47f3c7d24b82f62dc00f/chromadb-1.0.15.tar.gz", hash = "sha256:3e910da3f5414e2204f89c7beca1650847f2bf3bd71f11a2e40aad1eb31050aa", size = 1218840, upload-time = "2025-07-02T17:07:09.875Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/85/5a/866c6f0c2160cbc8dca0cf77b2fb391dcf435b32a58743da1bc1a08dc442/chromadb-1.0.15-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:51791553014297798b53df4e043e9c30f4e8bd157647971a6bb02b04bfa65f82", size = 18838820, upload-time = "2025-07-02T17:07:07.632Z" }, - { url = "https://files.pythonhosted.org/packages/e1/18/ff9b58ab5d334f5ecff7fdbacd6761bac467176708fa4d2500ae7c048af0/chromadb-1.0.15-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:48015803c0631c3a817befc276436dc084bb628c37fd4214047212afb2056291", size = 18057131, upload-time = "2025-07-02T17:07:05.15Z" }, - { url = "https://files.pythonhosted.org/packages/31/49/74e34cc5aeeb25aff2c0ede6790b3671e14c1b91574dd8f98d266a4c5aad/chromadb-1.0.15-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b73cd6fb32fcdd91c577cca16ea6112b691d72b441bb3f2140426d1e79e453a", size = 18595284, upload-time = "2025-07-02T17:06:59.102Z" }, - { url = "https://files.pythonhosted.org/packages/cb/33/190df917a057067e37f8b48d082d769bed8b3c0c507edefc7b6c6bb577d0/chromadb-1.0.15-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:479f1b401af9e7c20f50642ffb3376abbfd78e2b5b170429f7c79eff52e367db", size = 19526626, upload-time = "2025-07-02T17:07:02.163Z" }, - { url = "https://files.pythonhosted.org/packages/a1/30/6890da607358993f87a01e80bcce916b4d91515ce865f07dc06845cb472f/chromadb-1.0.15-cp39-abi3-win_amd64.whl", hash = "sha256:e0cb3b93fdc42b1786f151d413ef36299f30f783a30ce08bf0bfb12e552b4190", size = 19520490, upload-time = "2025-07-02T17:07:11.559Z" }, -] - -[[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 = "coloredlogs" -version = "15.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "humanfriendly" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" }, -] - -[[package]] -name = "coverage" -version = "7.9.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/b7/c0465ca253df10a9e8dae0692a4ae6e9726d245390aaef92360e1d6d3832/coverage-7.9.2.tar.gz", hash = "sha256:997024fa51e3290264ffd7492ec97d0690293ccd2b45a6cd7d82d945a4a80c8b", size = 813556, upload-time = "2025-07-03T10:54:15.101Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/0d/5c2114fd776c207bd55068ae8dc1bef63ecd1b767b3389984a8e58f2b926/coverage-7.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:66283a192a14a3854b2e7f3418d7db05cdf411012ab7ff5db98ff3b181e1f912", size = 212039, upload-time = "2025-07-03T10:52:38.955Z" }, - { url = "https://files.pythonhosted.org/packages/cf/ad/dc51f40492dc2d5fcd31bb44577bc0cc8920757d6bc5d3e4293146524ef9/coverage-7.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4e01d138540ef34fcf35c1aa24d06c3de2a4cffa349e29a10056544f35cca15f", size = 212428, upload-time = "2025-07-03T10:52:41.36Z" }, - { url = "https://files.pythonhosted.org/packages/a2/a3/55cb3ff1b36f00df04439c3993d8529193cdf165a2467bf1402539070f16/coverage-7.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f22627c1fe2745ee98d3ab87679ca73a97e75ca75eb5faee48660d060875465f", size = 241534, upload-time = "2025-07-03T10:52:42.956Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c9/a8410b91b6be4f6e9c2e9f0dce93749b6b40b751d7065b4410bf89cb654b/coverage-7.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b1c2d8363247b46bd51f393f86c94096e64a1cf6906803fa8d5a9d03784bdbf", size = 239408, upload-time = "2025-07-03T10:52:44.199Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c4/6f3e56d467c612b9070ae71d5d3b114c0b899b5788e1ca3c93068ccb7018/coverage-7.9.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c10c882b114faf82dbd33e876d0cbd5e1d1ebc0d2a74ceef642c6152f3f4d547", size = 240552, upload-time = "2025-07-03T10:52:45.477Z" }, - { url = "https://files.pythonhosted.org/packages/fd/20/04eda789d15af1ce79bce5cc5fd64057c3a0ac08fd0576377a3096c24663/coverage-7.9.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:de3c0378bdf7066c3988d66cd5232d161e933b87103b014ab1b0b4676098fa45", size = 240464, upload-time = "2025-07-03T10:52:46.809Z" }, - { url = "https://files.pythonhosted.org/packages/a9/5a/217b32c94cc1a0b90f253514815332d08ec0812194a1ce9cca97dda1cd20/coverage-7.9.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1e2f097eae0e5991e7623958a24ced3282676c93c013dde41399ff63e230fcf2", size = 239134, upload-time = "2025-07-03T10:52:48.149Z" }, - { url = "https://files.pythonhosted.org/packages/34/73/1d019c48f413465eb5d3b6898b6279e87141c80049f7dbf73fd020138549/coverage-7.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28dc1f67e83a14e7079b6cea4d314bc8b24d1aed42d3582ff89c0295f09b181e", size = 239405, upload-time = "2025-07-03T10:52:49.687Z" }, - { url = "https://files.pythonhosted.org/packages/49/6c/a2beca7aa2595dad0c0d3f350382c381c92400efe5261e2631f734a0e3fe/coverage-7.9.2-cp310-cp310-win32.whl", hash = "sha256:bf7d773da6af9e10dbddacbf4e5cab13d06d0ed93561d44dae0188a42c65be7e", size = 214519, upload-time = "2025-07-03T10:52:51.036Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c8/91e5e4a21f9a51e2c7cdd86e587ae01a4fcff06fc3fa8cde4d6f7cf68df4/coverage-7.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:0c0378ba787681ab1897f7c89b415bd56b0b2d9a47e5a3d8dc0ea55aac118d6c", size = 215400, upload-time = "2025-07-03T10:52:52.313Z" }, - { url = "https://files.pythonhosted.org/packages/39/40/916786453bcfafa4c788abee4ccd6f592b5b5eca0cd61a32a4e5a7ef6e02/coverage-7.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a7a56a2964a9687b6aba5b5ced6971af308ef6f79a91043c05dd4ee3ebc3e9ba", size = 212152, upload-time = "2025-07-03T10:52:53.562Z" }, - { url = "https://files.pythonhosted.org/packages/9f/66/cc13bae303284b546a030762957322bbbff1ee6b6cb8dc70a40f8a78512f/coverage-7.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:123d589f32c11d9be7fe2e66d823a236fe759b0096f5db3fb1b75b2fa414a4fa", size = 212540, upload-time = "2025-07-03T10:52:55.196Z" }, - { url = "https://files.pythonhosted.org/packages/0f/3c/d56a764b2e5a3d43257c36af4a62c379df44636817bb5f89265de4bf8bd7/coverage-7.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:333b2e0ca576a7dbd66e85ab402e35c03b0b22f525eed82681c4b866e2e2653a", size = 245097, upload-time = "2025-07-03T10:52:56.509Z" }, - { url = "https://files.pythonhosted.org/packages/b1/46/bd064ea8b3c94eb4ca5d90e34d15b806cba091ffb2b8e89a0d7066c45791/coverage-7.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:326802760da234baf9f2f85a39e4a4b5861b94f6c8d95251f699e4f73b1835dc", size = 242812, upload-time = "2025-07-03T10:52:57.842Z" }, - { url = "https://files.pythonhosted.org/packages/43/02/d91992c2b29bc7afb729463bc918ebe5f361be7f1daae93375a5759d1e28/coverage-7.9.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19e7be4cfec248df38ce40968c95d3952fbffd57b400d4b9bb580f28179556d2", size = 244617, upload-time = "2025-07-03T10:52:59.239Z" }, - { url = "https://files.pythonhosted.org/packages/b7/4f/8fadff6bf56595a16d2d6e33415841b0163ac660873ed9a4e9046194f779/coverage-7.9.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0b4a4cb73b9f2b891c1788711408ef9707666501ba23684387277ededab1097c", size = 244263, upload-time = "2025-07-03T10:53:00.601Z" }, - { url = "https://files.pythonhosted.org/packages/9b/d2/e0be7446a2bba11739edb9f9ba4eff30b30d8257370e237418eb44a14d11/coverage-7.9.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2c8937fa16c8c9fbbd9f118588756e7bcdc7e16a470766a9aef912dd3f117dbd", size = 242314, upload-time = "2025-07-03T10:53:01.932Z" }, - { url = "https://files.pythonhosted.org/packages/9d/7d/dcbac9345000121b8b57a3094c2dfcf1ccc52d8a14a40c1d4bc89f936f80/coverage-7.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:42da2280c4d30c57a9b578bafd1d4494fa6c056d4c419d9689e66d775539be74", size = 242904, upload-time = "2025-07-03T10:53:03.478Z" }, - { url = "https://files.pythonhosted.org/packages/41/58/11e8db0a0c0510cf31bbbdc8caf5d74a358b696302a45948d7c768dfd1cf/coverage-7.9.2-cp311-cp311-win32.whl", hash = "sha256:14fa8d3da147f5fdf9d298cacc18791818f3f1a9f542c8958b80c228320e90c6", size = 214553, upload-time = "2025-07-03T10:53:05.174Z" }, - { url = "https://files.pythonhosted.org/packages/3a/7d/751794ec8907a15e257136e48dc1021b1f671220ecccfd6c4eaf30802714/coverage-7.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:549cab4892fc82004f9739963163fd3aac7a7b0df430669b75b86d293d2df2a7", size = 215441, upload-time = "2025-07-03T10:53:06.472Z" }, - { url = "https://files.pythonhosted.org/packages/62/5b/34abcedf7b946c1c9e15b44f326cb5b0da852885312b30e916f674913428/coverage-7.9.2-cp311-cp311-win_arm64.whl", hash = "sha256:c2667a2b913e307f06aa4e5677f01a9746cd08e4b35e14ebcde6420a9ebb4c62", size = 213873, upload-time = "2025-07-03T10:53:07.699Z" }, - { url = "https://files.pythonhosted.org/packages/53/d7/7deefc6fd4f0f1d4c58051f4004e366afc9e7ab60217ac393f247a1de70a/coverage-7.9.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae9eb07f1cfacd9cfe8eaee6f4ff4b8a289a668c39c165cd0c8548484920ffc0", size = 212344, upload-time = "2025-07-03T10:53:09.3Z" }, - { url = "https://files.pythonhosted.org/packages/95/0c/ee03c95d32be4d519e6a02e601267769ce2e9a91fc8faa1b540e3626c680/coverage-7.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ce85551f9a1119f02adc46d3014b5ee3f765deac166acf20dbb851ceb79b6f3", size = 212580, upload-time = "2025-07-03T10:53:11.52Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9f/826fa4b544b27620086211b87a52ca67592622e1f3af9e0a62c87aea153a/coverage-7.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8f6389ac977c5fb322e0e38885fbbf901743f79d47f50db706e7644dcdcb6e1", size = 246383, upload-time = "2025-07-03T10:53:13.134Z" }, - { url = "https://files.pythonhosted.org/packages/7f/b3/4477aafe2a546427b58b9c540665feff874f4db651f4d3cb21b308b3a6d2/coverage-7.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d9eae8cdfcd58fe7893b88993723583a6ce4dfbfd9f29e001922544f95615", size = 243400, upload-time = "2025-07-03T10:53:14.614Z" }, - { url = "https://files.pythonhosted.org/packages/f8/c2/efffa43778490c226d9d434827702f2dfbc8041d79101a795f11cbb2cf1e/coverage-7.9.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae939811e14e53ed8a9818dad51d434a41ee09df9305663735f2e2d2d7d959b", size = 245591, upload-time = "2025-07-03T10:53:15.872Z" }, - { url = "https://files.pythonhosted.org/packages/c6/e7/a59888e882c9a5f0192d8627a30ae57910d5d449c80229b55e7643c078c4/coverage-7.9.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:31991156251ec202c798501e0a42bbdf2169dcb0f137b1f5c0f4267f3fc68ef9", size = 245402, upload-time = "2025-07-03T10:53:17.124Z" }, - { url = "https://files.pythonhosted.org/packages/92/a5/72fcd653ae3d214927edc100ce67440ed8a0a1e3576b8d5e6d066ed239db/coverage-7.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d0d67963f9cbfc7c7f96d4ac74ed60ecbebd2ea6eeb51887af0f8dce205e545f", size = 243583, upload-time = "2025-07-03T10:53:18.781Z" }, - { url = "https://files.pythonhosted.org/packages/5c/f5/84e70e4df28f4a131d580d7d510aa1ffd95037293da66fd20d446090a13b/coverage-7.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:49b752a2858b10580969ec6af6f090a9a440a64a301ac1528d7ca5f7ed497f4d", size = 244815, upload-time = "2025-07-03T10:53:20.168Z" }, - { url = "https://files.pythonhosted.org/packages/39/e7/d73d7cbdbd09fdcf4642655ae843ad403d9cbda55d725721965f3580a314/coverage-7.9.2-cp312-cp312-win32.whl", hash = "sha256:88d7598b8ee130f32f8a43198ee02edd16d7f77692fa056cb779616bbea1b355", size = 214719, upload-time = "2025-07-03T10:53:21.521Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d6/7486dcc3474e2e6ad26a2af2db7e7c162ccd889c4c68fa14ea8ec189c9e9/coverage-7.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:9dfb070f830739ee49d7c83e4941cc767e503e4394fdecb3b54bfdac1d7662c0", size = 215509, upload-time = "2025-07-03T10:53:22.853Z" }, - { url = "https://files.pythonhosted.org/packages/b7/34/0439f1ae2593b0346164d907cdf96a529b40b7721a45fdcf8b03c95fcd90/coverage-7.9.2-cp312-cp312-win_arm64.whl", hash = "sha256:4e2c058aef613e79df00e86b6d42a641c877211384ce5bd07585ed7ba71ab31b", size = 213910, upload-time = "2025-07-03T10:53:24.472Z" }, - { url = "https://files.pythonhosted.org/packages/94/9d/7a8edf7acbcaa5e5c489a646226bed9591ee1c5e6a84733c0140e9ce1ae1/coverage-7.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:985abe7f242e0d7bba228ab01070fde1d6c8fa12f142e43debe9ed1dde686038", size = 212367, upload-time = "2025-07-03T10:53:25.811Z" }, - { url = "https://files.pythonhosted.org/packages/e8/9e/5cd6f130150712301f7e40fb5865c1bc27b97689ec57297e568d972eec3c/coverage-7.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c3939264a76d44fde7f213924021ed31f55ef28111a19649fec90c0f109e6d", size = 212632, upload-time = "2025-07-03T10:53:27.075Z" }, - { url = "https://files.pythonhosted.org/packages/a8/de/6287a2c2036f9fd991c61cefa8c64e57390e30c894ad3aa52fac4c1e14a8/coverage-7.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae5d563e970dbe04382f736ec214ef48103d1b875967c89d83c6e3f21706d5b3", size = 245793, upload-time = "2025-07-03T10:53:28.408Z" }, - { url = "https://files.pythonhosted.org/packages/06/cc/9b5a9961d8160e3cb0b558c71f8051fe08aa2dd4b502ee937225da564ed1/coverage-7.9.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdd612e59baed2a93c8843c9a7cb902260f181370f1d772f4842987535071d14", size = 243006, upload-time = "2025-07-03T10:53:29.754Z" }, - { url = "https://files.pythonhosted.org/packages/49/d9/4616b787d9f597d6443f5588619c1c9f659e1f5fc9eebf63699eb6d34b78/coverage-7.9.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:256ea87cb2a1ed992bcdfc349d8042dcea1b80436f4ddf6e246d6bee4b5d73b6", size = 244990, upload-time = "2025-07-03T10:53:31.098Z" }, - { url = "https://files.pythonhosted.org/packages/48/83/801cdc10f137b2d02b005a761661649ffa60eb173dcdaeb77f571e4dc192/coverage-7.9.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f44ae036b63c8ea432f610534a2668b0c3aee810e7037ab9d8ff6883de480f5b", size = 245157, upload-time = "2025-07-03T10:53:32.717Z" }, - { url = "https://files.pythonhosted.org/packages/c8/a4/41911ed7e9d3ceb0ffb019e7635468df7499f5cc3edca5f7dfc078e9c5ec/coverage-7.9.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82d76ad87c932935417a19b10cfe7abb15fd3f923cfe47dbdaa74ef4e503752d", size = 243128, upload-time = "2025-07-03T10:53:34.009Z" }, - { url = "https://files.pythonhosted.org/packages/10/41/344543b71d31ac9cb00a664d5d0c9ef134a0fe87cb7d8430003b20fa0b7d/coverage-7.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:619317bb86de4193debc712b9e59d5cffd91dc1d178627ab2a77b9870deb2868", size = 244511, upload-time = "2025-07-03T10:53:35.434Z" }, - { url = "https://files.pythonhosted.org/packages/d5/81/3b68c77e4812105e2a060f6946ba9e6f898ddcdc0d2bfc8b4b152a9ae522/coverage-7.9.2-cp313-cp313-win32.whl", hash = "sha256:0a07757de9feb1dfafd16ab651e0f628fd7ce551604d1bf23e47e1ddca93f08a", size = 214765, upload-time = "2025-07-03T10:53:36.787Z" }, - { url = "https://files.pythonhosted.org/packages/06/a2/7fac400f6a346bb1a4004eb2a76fbff0e242cd48926a2ce37a22a6a1d917/coverage-7.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:115db3d1f4d3f35f5bb021e270edd85011934ff97c8797216b62f461dd69374b", size = 215536, upload-time = "2025-07-03T10:53:38.188Z" }, - { url = "https://files.pythonhosted.org/packages/08/47/2c6c215452b4f90d87017e61ea0fd9e0486bb734cb515e3de56e2c32075f/coverage-7.9.2-cp313-cp313-win_arm64.whl", hash = "sha256:48f82f889c80af8b2a7bb6e158d95a3fbec6a3453a1004d04e4f3b5945a02694", size = 213943, upload-time = "2025-07-03T10:53:39.492Z" }, - { url = "https://files.pythonhosted.org/packages/a3/46/e211e942b22d6af5e0f323faa8a9bc7c447a1cf1923b64c47523f36ed488/coverage-7.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:55a28954545f9d2f96870b40f6c3386a59ba8ed50caf2d949676dac3ecab99f5", size = 213088, upload-time = "2025-07-03T10:53:40.874Z" }, - { url = "https://files.pythonhosted.org/packages/d2/2f/762551f97e124442eccd907bf8b0de54348635b8866a73567eb4e6417acf/coverage-7.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cdef6504637731a63c133bb2e6f0f0214e2748495ec15fe42d1e219d1b133f0b", size = 213298, upload-time = "2025-07-03T10:53:42.218Z" }, - { url = "https://files.pythonhosted.org/packages/7a/b7/76d2d132b7baf7360ed69be0bcab968f151fa31abe6d067f0384439d9edb/coverage-7.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd5ebe66c7a97273d5d2ddd4ad0ed2e706b39630ed4b53e713d360626c3dbb3", size = 256541, upload-time = "2025-07-03T10:53:43.823Z" }, - { url = "https://files.pythonhosted.org/packages/a0/17/392b219837d7ad47d8e5974ce5f8dc3deb9f99a53b3bd4d123602f960c81/coverage-7.9.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9303aed20872d7a3c9cb39c5d2b9bdbe44e3a9a1aecb52920f7e7495410dfab8", size = 252761, upload-time = "2025-07-03T10:53:45.19Z" }, - { url = "https://files.pythonhosted.org/packages/d5/77/4256d3577fe1b0daa8d3836a1ebe68eaa07dd2cbaf20cf5ab1115d6949d4/coverage-7.9.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc18ea9e417a04d1920a9a76fe9ebd2f43ca505b81994598482f938d5c315f46", size = 254917, upload-time = "2025-07-03T10:53:46.931Z" }, - { url = "https://files.pythonhosted.org/packages/53/99/fc1a008eef1805e1ddb123cf17af864743354479ea5129a8f838c433cc2c/coverage-7.9.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6406cff19880aaaadc932152242523e892faff224da29e241ce2fca329866584", size = 256147, upload-time = "2025-07-03T10:53:48.289Z" }, - { url = "https://files.pythonhosted.org/packages/92/c0/f63bf667e18b7f88c2bdb3160870e277c4874ced87e21426128d70aa741f/coverage-7.9.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d0d4f6ecdf37fcc19c88fec3e2277d5dee740fb51ffdd69b9579b8c31e4232e", size = 254261, upload-time = "2025-07-03T10:53:49.99Z" }, - { url = "https://files.pythonhosted.org/packages/8c/32/37dd1c42ce3016ff8ec9e4b607650d2e34845c0585d3518b2a93b4830c1a/coverage-7.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c33624f50cf8de418ab2b4d6ca9eda96dc45b2c4231336bac91454520e8d1fac", size = 255099, upload-time = "2025-07-03T10:53:51.354Z" }, - { url = "https://files.pythonhosted.org/packages/da/2e/af6b86f7c95441ce82f035b3affe1cd147f727bbd92f563be35e2d585683/coverage-7.9.2-cp313-cp313t-win32.whl", hash = "sha256:1df6b76e737c6a92210eebcb2390af59a141f9e9430210595251fbaf02d46926", size = 215440, upload-time = "2025-07-03T10:53:52.808Z" }, - { url = "https://files.pythonhosted.org/packages/4d/bb/8a785d91b308867f6b2e36e41c569b367c00b70c17f54b13ac29bcd2d8c8/coverage-7.9.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f5fd54310b92741ebe00d9c0d1d7b2b27463952c022da6d47c175d246a98d1bd", size = 216537, upload-time = "2025-07-03T10:53:54.273Z" }, - { url = "https://files.pythonhosted.org/packages/1d/a0/a6bffb5e0f41a47279fd45a8f3155bf193f77990ae1c30f9c224b61cacb0/coverage-7.9.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c48c2375287108c887ee87d13b4070a381c6537d30e8487b24ec721bf2a781cb", size = 214398, upload-time = "2025-07-03T10:53:56.715Z" }, - { url = "https://files.pythonhosted.org/packages/d7/85/f8bbefac27d286386961c25515431482a425967e23d3698b75a250872924/coverage-7.9.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:8a1166db2fb62473285bcb092f586e081e92656c7dfa8e9f62b4d39d7e6b5050", size = 204013, upload-time = "2025-07-03T10:54:12.084Z" }, - { url = "https://files.pythonhosted.org/packages/3c/38/bbe2e63902847cf79036ecc75550d0698af31c91c7575352eb25190d0fb3/coverage-7.9.2-py3-none-any.whl", hash = "sha256:e425cd5b00f6fc0ed7cdbd766c70be8baab4b7839e4d4fe5fac48581dd968ea4", size = 204005, upload-time = "2025-07-03T10:54:13.491Z" }, -] - -[package.optional-dependencies] -toml = [ - { name = "tomli", marker = "python_full_version <= '3.11'" }, -] - -[[package]] -name = "cryptography" -version = "45.0.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/1e/49527ac611af559665f71cbb8f92b332b5ec9c6fbc4e88b0f8e92f5e85df/cryptography-45.0.5.tar.gz", hash = "sha256:72e76caa004ab63accdf26023fccd1d087f6d90ec6048ff33ad0445abf7f605a", size = 744903, upload-time = "2025-07-02T13:06:25.941Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/fb/09e28bc0c46d2c547085e60897fea96310574c70fb21cd58a730a45f3403/cryptography-45.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:101ee65078f6dd3e5a028d4f19c07ffa4dd22cce6a20eaa160f8b5219911e7d8", size = 7043092, upload-time = "2025-07-02T13:05:01.514Z" }, - { url = "https://files.pythonhosted.org/packages/b1/05/2194432935e29b91fb649f6149c1a4f9e6d3d9fc880919f4ad1bcc22641e/cryptography-45.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3a264aae5f7fbb089dbc01e0242d3b67dffe3e6292e1f5182122bdf58e65215d", size = 4205926, upload-time = "2025-07-02T13:05:04.741Z" }, - { url = "https://files.pythonhosted.org/packages/07/8b/9ef5da82350175e32de245646b1884fc01124f53eb31164c77f95a08d682/cryptography-45.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e74d30ec9c7cb2f404af331d5b4099a9b322a8a6b25c4632755c8757345baac5", size = 4429235, upload-time = "2025-07-02T13:05:07.084Z" }, - { url = "https://files.pythonhosted.org/packages/7c/e1/c809f398adde1994ee53438912192d92a1d0fc0f2d7582659d9ef4c28b0c/cryptography-45.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3af26738f2db354aafe492fb3869e955b12b2ef2e16908c8b9cb928128d42c57", size = 4209785, upload-time = "2025-07-02T13:05:09.321Z" }, - { url = "https://files.pythonhosted.org/packages/d0/8b/07eb6bd5acff58406c5e806eff34a124936f41a4fb52909ffa4d00815f8c/cryptography-45.0.5-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e6c00130ed423201c5bc5544c23359141660b07999ad82e34e7bb8f882bb78e0", size = 3893050, upload-time = "2025-07-02T13:05:11.069Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ef/3333295ed58d900a13c92806b67e62f27876845a9a908c939f040887cca9/cryptography-45.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:dd420e577921c8c2d31289536c386aaa30140b473835e97f83bc71ea9d2baf2d", size = 4457379, upload-time = "2025-07-02T13:05:13.32Z" }, - { url = "https://files.pythonhosted.org/packages/d9/9d/44080674dee514dbb82b21d6fa5d1055368f208304e2ab1828d85c9de8f4/cryptography-45.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d05a38884db2ba215218745f0781775806bde4f32e07b135348355fe8e4991d9", size = 4209355, upload-time = "2025-07-02T13:05:15.017Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d8/0749f7d39f53f8258e5c18a93131919ac465ee1f9dccaf1b3f420235e0b5/cryptography-45.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ad0caded895a00261a5b4aa9af828baede54638754b51955a0ac75576b831b27", size = 4456087, upload-time = "2025-07-02T13:05:16.945Z" }, - { url = "https://files.pythonhosted.org/packages/09/d7/92acac187387bf08902b0bf0699816f08553927bdd6ba3654da0010289b4/cryptography-45.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9024beb59aca9d31d36fcdc1604dd9bbeed0a55bface9f1908df19178e2f116e", size = 4332873, upload-time = "2025-07-02T13:05:18.743Z" }, - { url = "https://files.pythonhosted.org/packages/03/c2/840e0710da5106a7c3d4153c7215b2736151bba60bf4491bdb421df5056d/cryptography-45.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:91098f02ca81579c85f66df8a588c78f331ca19089763d733e34ad359f474174", size = 4564651, upload-time = "2025-07-02T13:05:21.382Z" }, - { url = "https://files.pythonhosted.org/packages/2e/92/cc723dd6d71e9747a887b94eb3827825c6c24b9e6ce2bb33b847d31d5eaa/cryptography-45.0.5-cp311-abi3-win32.whl", hash = "sha256:926c3ea71a6043921050eaa639137e13dbe7b4ab25800932a8498364fc1abec9", size = 2929050, upload-time = "2025-07-02T13:05:23.39Z" }, - { url = "https://files.pythonhosted.org/packages/1f/10/197da38a5911a48dd5389c043de4aec4b3c94cb836299b01253940788d78/cryptography-45.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:b85980d1e345fe769cfc57c57db2b59cff5464ee0c045d52c0df087e926fbe63", size = 3403224, upload-time = "2025-07-02T13:05:25.202Z" }, - { url = "https://files.pythonhosted.org/packages/fe/2b/160ce8c2765e7a481ce57d55eba1546148583e7b6f85514472b1d151711d/cryptography-45.0.5-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f3562c2f23c612f2e4a6964a61d942f891d29ee320edb62ff48ffb99f3de9ae8", size = 7017143, upload-time = "2025-07-02T13:05:27.229Z" }, - { url = "https://files.pythonhosted.org/packages/c2/e7/2187be2f871c0221a81f55ee3105d3cf3e273c0a0853651d7011eada0d7e/cryptography-45.0.5-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3fcfbefc4a7f332dece7272a88e410f611e79458fab97b5efe14e54fe476f4fd", size = 4197780, upload-time = "2025-07-02T13:05:29.299Z" }, - { url = "https://files.pythonhosted.org/packages/b9/cf/84210c447c06104e6be9122661159ad4ce7a8190011669afceeaea150524/cryptography-45.0.5-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:460f8c39ba66af7db0545a8c6f2eabcbc5a5528fc1cf6c3fa9a1e44cec33385e", size = 4420091, upload-time = "2025-07-02T13:05:31.221Z" }, - { url = "https://files.pythonhosted.org/packages/3e/6a/cb8b5c8bb82fafffa23aeff8d3a39822593cee6e2f16c5ca5c2ecca344f7/cryptography-45.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9b4cf6318915dccfe218e69bbec417fdd7c7185aa7aab139a2c0beb7468c89f0", size = 4198711, upload-time = "2025-07-02T13:05:33.062Z" }, - { url = "https://files.pythonhosted.org/packages/04/f7/36d2d69df69c94cbb2473871926daf0f01ad8e00fe3986ac3c1e8c4ca4b3/cryptography-45.0.5-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2089cc8f70a6e454601525e5bf2779e665d7865af002a5dec8d14e561002e135", size = 3883299, upload-time = "2025-07-02T13:05:34.94Z" }, - { url = "https://files.pythonhosted.org/packages/82/c7/f0ea40f016de72f81288e9fe8d1f6748036cb5ba6118774317a3ffc6022d/cryptography-45.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0027d566d65a38497bc37e0dd7c2f8ceda73597d2ac9ba93810204f56f52ebc7", size = 4450558, upload-time = "2025-07-02T13:05:37.288Z" }, - { url = "https://files.pythonhosted.org/packages/06/ae/94b504dc1a3cdf642d710407c62e86296f7da9e66f27ab12a1ee6fdf005b/cryptography-45.0.5-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:be97d3a19c16a9be00edf79dca949c8fa7eff621763666a145f9f9535a5d7f42", size = 4198020, upload-time = "2025-07-02T13:05:39.102Z" }, - { url = "https://files.pythonhosted.org/packages/05/2b/aaf0adb845d5dabb43480f18f7ca72e94f92c280aa983ddbd0bcd6ecd037/cryptography-45.0.5-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:7760c1c2e1a7084153a0f68fab76e754083b126a47d0117c9ed15e69e2103492", size = 4449759, upload-time = "2025-07-02T13:05:41.398Z" }, - { url = "https://files.pythonhosted.org/packages/91/e4/f17e02066de63e0100a3a01b56f8f1016973a1d67551beaf585157a86b3f/cryptography-45.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6ff8728d8d890b3dda5765276d1bc6fb099252915a2cd3aff960c4c195745dd0", size = 4319991, upload-time = "2025-07-02T13:05:43.64Z" }, - { url = "https://files.pythonhosted.org/packages/f2/2e/e2dbd629481b499b14516eed933f3276eb3239f7cee2dcfa4ee6b44d4711/cryptography-45.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7259038202a47fdecee7e62e0fd0b0738b6daa335354396c6ddebdbe1206af2a", size = 4554189, upload-time = "2025-07-02T13:05:46.045Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ea/a78a0c38f4c8736287b71c2ea3799d173d5ce778c7d6e3c163a95a05ad2a/cryptography-45.0.5-cp37-abi3-win32.whl", hash = "sha256:1e1da5accc0c750056c556a93c3e9cb828970206c68867712ca5805e46dc806f", size = 2911769, upload-time = "2025-07-02T13:05:48.329Z" }, - { url = "https://files.pythonhosted.org/packages/79/b3/28ac139109d9005ad3f6b6f8976ffede6706a6478e21c889ce36c840918e/cryptography-45.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:90cb0a7bb35959f37e23303b7eed0a32280510030daba3f7fdfbb65defde6a97", size = 3390016, upload-time = "2025-07-02T13:05:50.811Z" }, - { url = "https://files.pythonhosted.org/packages/f8/8b/34394337abe4566848a2bd49b26bcd4b07fd466afd3e8cce4cb79a390869/cryptography-45.0.5-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:206210d03c1193f4e1ff681d22885181d47efa1ab3018766a7b32a7b3d6e6afd", size = 3575762, upload-time = "2025-07-02T13:05:53.166Z" }, - { url = "https://files.pythonhosted.org/packages/8b/5d/a19441c1e89afb0f173ac13178606ca6fab0d3bd3ebc29e9ed1318b507fc/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c648025b6840fe62e57107e0a25f604db740e728bd67da4f6f060f03017d5097", size = 4140906, upload-time = "2025-07-02T13:05:55.914Z" }, - { url = "https://files.pythonhosted.org/packages/4b/db/daceb259982a3c2da4e619f45b5bfdec0e922a23de213b2636e78ef0919b/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b8fa8b0a35a9982a3c60ec79905ba5bb090fc0b9addcfd3dc2dd04267e45f25e", size = 4374411, upload-time = "2025-07-02T13:05:57.814Z" }, - { url = "https://files.pythonhosted.org/packages/6a/35/5d06ad06402fc522c8bf7eab73422d05e789b4e38fe3206a85e3d6966c11/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:14d96584701a887763384f3c47f0ca7c1cce322aa1c31172680eb596b890ec30", size = 4140942, upload-time = "2025-07-02T13:06:00.137Z" }, - { url = "https://files.pythonhosted.org/packages/65/79/020a5413347e44c382ef1f7f7e7a66817cd6273e3e6b5a72d18177b08b2f/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:57c816dfbd1659a367831baca4b775b2a5b43c003daf52e9d57e1d30bc2e1b0e", size = 4374079, upload-time = "2025-07-02T13:06:02.043Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c5/c0e07d84a9a2a8a0ed4f865e58f37c71af3eab7d5e094ff1b21f3f3af3bc/cryptography-45.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b9e38e0a83cd51e07f5a48ff9691cae95a79bea28fe4ded168a8e5c6c77e819d", size = 3321362, upload-time = "2025-07-02T13:06:04.463Z" }, - { url = "https://files.pythonhosted.org/packages/c0/71/9bdbcfd58d6ff5084687fe722c58ac718ebedbc98b9f8f93781354e6d286/cryptography-45.0.5-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8c4a6ff8a30e9e3d38ac0539e9a9e02540ab3f827a3394f8852432f6b0ea152e", size = 3587878, upload-time = "2025-07-02T13:06:06.339Z" }, - { url = "https://files.pythonhosted.org/packages/f0/63/83516cfb87f4a8756eaa4203f93b283fda23d210fc14e1e594bd5f20edb6/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bd4c45986472694e5121084c6ebbd112aa919a25e783b87eb95953c9573906d6", size = 4152447, upload-time = "2025-07-02T13:06:08.345Z" }, - { url = "https://files.pythonhosted.org/packages/22/11/d2823d2a5a0bd5802b3565437add16f5c8ce1f0778bf3822f89ad2740a38/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:982518cd64c54fcada9d7e5cf28eabd3ee76bd03ab18e08a48cad7e8b6f31b18", size = 4386778, upload-time = "2025-07-02T13:06:10.263Z" }, - { url = "https://files.pythonhosted.org/packages/5f/38/6bf177ca6bce4fe14704ab3e93627c5b0ca05242261a2e43ef3168472540/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:12e55281d993a793b0e883066f590c1ae1e802e3acb67f8b442e721e475e6463", size = 4151627, upload-time = "2025-07-02T13:06:13.097Z" }, - { url = "https://files.pythonhosted.org/packages/38/6a/69fc67e5266bff68a91bcb81dff8fb0aba4d79a78521a08812048913e16f/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:5aa1e32983d4443e310f726ee4b071ab7569f58eedfdd65e9675484a4eb67bd1", size = 4385593, upload-time = "2025-07-02T13:06:15.689Z" }, - { url = "https://files.pythonhosted.org/packages/f6/34/31a1604c9a9ade0fdab61eb48570e09a796f4d9836121266447b0eaf7feb/cryptography-45.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e357286c1b76403dd384d938f93c46b2b058ed4dfcdce64a770f0537ed3feb6f", size = 3331106, upload-time = "2025-07-02T13:06:18.058Z" }, -] - -[[package]] -name = "decorator" -version = "5.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, -] - -[[package]] -name = "deprecation" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/d3/8ae2869247df154b64c1884d7346d412fed0c49df84db635aab2d1c40e62/deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", size = 173788, upload-time = "2020-04-20T14:23:38.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", size = 11178, upload-time = "2020-04-20T14:23:36.581Z" }, -] - -[[package]] -name = "distlib" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, -] - -[[package]] -name = "distro" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, -] - -[[package]] -name = "docutils" -version = "0.21.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, -] - -[[package]] -name = "durationpy" -version = "0.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/a4/e44218c2b394e31a6dd0d6b095c4e1f32d0be54c2a4b250032d717647bab/durationpy-0.10.tar.gz", hash = "sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba", size = 3335, upload-time = "2025-05-17T13:52:37.26Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/0d/9feae160378a3553fa9a339b0e9c1a048e147a4127210e286ef18b730f03/durationpy-0.10-py3-none-any.whl", hash = "sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286", size = 3922, upload-time = "2025-05-17T13:52:36.463Z" }, -] - -[[package]] -name = "ecdsa" -version = "0.19.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/1f/924e3caae75f471eae4b26bd13b698f6af2c44279f67af317439c2f4c46a/ecdsa-0.19.1.tar.gz", hash = "sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61", size = 201793, upload-time = "2025-03-13T11:52:43.25Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/a3/460c57f094a4a165c84a1341c373b0a4f5ec6ac244b998d5021aade89b77/ecdsa-0.19.1-py2.py3-none-any.whl", hash = "sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3", size = 150607, upload-time = "2025-03-13T11:52:41.757Z" }, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "executing" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693, upload-time = "2025-01-22T15:41:29.403Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702, upload-time = "2025-01-22T15:41:25.929Z" }, -] - -[[package]] -name = "factory-boy" -version = "3.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "faker" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ba/98/75cacae9945f67cfe323829fc2ac451f64517a8a330b572a06a323997065/factory_boy-3.3.3.tar.gz", hash = "sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03", size = 164146, upload-time = "2025-02-03T09:49:04.433Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/8d/2bc5f5546ff2ccb3f7de06742853483ab75bf74f36a92254702f8baecc79/factory_boy-3.3.3-py2.py3-none-any.whl", hash = "sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc", size = 37036, upload-time = "2025-02-03T09:49:01.659Z" }, -] - -[[package]] -name = "faker" -version = "37.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/65/95/da573e055608e180086e2ac3208f8c15d8b44220912f565a9821b9bff33a/faker-37.4.2.tar.gz", hash = "sha256:8e281bbaea30e5658895b8bea21cc50d27aaf3a43db3f2694409ca5701c56b0a", size = 1902890, upload-time = "2025-07-15T16:38:24.803Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/1c/b909a055be556c11f13cf058cfa0e152f9754d803ff3694a937efe300709/faker-37.4.2-py3-none-any.whl", hash = "sha256:b70ed1af57bfe988cbcd0afd95f4768c51eaf4e1ce8a30962e127ac5c139c93f", size = 1943179, upload-time = "2025-07-15T16:38:23.053Z" }, -] - -[[package]] -name = "fastapi" -version = "0.116.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "starlette" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/d7/6c8b3bfe33eeffa208183ec037fee0cce9f7f024089ab1c5d12ef04bd27c/fastapi-0.116.1.tar.gz", hash = "sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143", size = 296485, upload-time = "2025-07-11T16:22:32.057Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", size = 95631, upload-time = "2025-07-11T16:22:30.485Z" }, -] - -[[package]] -name = "fastembed" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, - { name = "loguru" }, - { name = "mmh3" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "onnxruntime" }, - { name = "pillow" }, - { name = "py-rust-stemmers" }, - { name = "requests" }, - { name = "tokenizers" }, - { name = "tqdm" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b0/e0/75b294baf2497f085d225b83f7124c627807806c29cb052136d09d4a8599/fastembed-0.7.1.tar.gz", hash = "sha256:cb45be91779ba1dcbe4dbdbdcfb3e2cffb8ec546f8f4317e33fe3014113ee64c", size = 62197, upload-time = "2025-06-16T09:01:42.766Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/ad/5a7a19f7ca6a0b440056e0499bf15e3244217eedec06896ae95a80a73340/fastembed-0.7.1-py3-none-any.whl", hash = "sha256:b4f6a8f620c32f2e3de8231034ca2ca76dadc7d5463f2e9ab4930b51adc03b12", size = 100860, upload-time = "2025-06-16T09:01:41.373Z" }, -] - -[[package]] -name = "filelock" -version = "3.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, -] - -[[package]] -name = "flatbuffers" -version = "25.2.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/30/eb5dce7994fc71a2f685d98ec33cc660c0a5887db5610137e60d8cbc4489/flatbuffers-25.2.10.tar.gz", hash = "sha256:97e451377a41262f8d9bd4295cc836133415cc03d8cb966410a4af92eb00d26e", size = 22170, upload-time = "2025-02-11T04:26:46.257Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/25/155f9f080d5e4bc0082edfda032ea2bc2b8fab3f4d25d46c1e9dd22a1a89/flatbuffers-25.2.10-py2.py3-none-any.whl", hash = "sha256:ebba5f4d5ea615af3f7fd70fc310636fbb2bbd1f566ac0a23d98dd412de50051", size = 30953, upload-time = "2025-02-11T04:26:44.484Z" }, -] - -[[package]] -name = "frozenlist" -version = "1.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/b1/b64018016eeb087db503b038296fd782586432b9c077fc5c7839e9cb6ef6/frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f", size = 45078, upload-time = "2025-06-09T23:02:35.538Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/36/0da0a49409f6b47cc2d060dc8c9040b897b5902a8a4e37d9bc1deb11f680/frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a", size = 81304, upload-time = "2025-06-09T22:59:46.226Z" }, - { url = "https://files.pythonhosted.org/packages/77/f0/77c11d13d39513b298e267b22eb6cb559c103d56f155aa9a49097221f0b6/frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61", size = 47735, upload-time = "2025-06-09T22:59:48.133Z" }, - { url = "https://files.pythonhosted.org/packages/37/12/9d07fa18971a44150593de56b2f2947c46604819976784bcf6ea0d5db43b/frozenlist-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0fd1bad056a3600047fb9462cff4c5322cebc59ebf5d0a3725e0ee78955001d", size = 46775, upload-time = "2025-06-09T22:59:49.564Z" }, - { url = "https://files.pythonhosted.org/packages/70/34/f73539227e06288fcd1f8a76853e755b2b48bca6747e99e283111c18bcd4/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3789ebc19cb811163e70fe2bd354cea097254ce6e707ae42e56f45e31e96cb8e", size = 224644, upload-time = "2025-06-09T22:59:51.35Z" }, - { url = "https://files.pythonhosted.org/packages/fb/68/c1d9c2f4a6e438e14613bad0f2973567586610cc22dcb1e1241da71de9d3/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af369aa35ee34f132fcfad5be45fbfcde0e3a5f6a1ec0712857f286b7d20cca9", size = 222125, upload-time = "2025-06-09T22:59:52.884Z" }, - { url = "https://files.pythonhosted.org/packages/b9/d0/98e8f9a515228d708344d7c6986752be3e3192d1795f748c24bcf154ad99/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac64b6478722eeb7a3313d494f8342ef3478dff539d17002f849101b212ef97c", size = 233455, upload-time = "2025-06-09T22:59:54.74Z" }, - { url = "https://files.pythonhosted.org/packages/79/df/8a11bcec5600557f40338407d3e5bea80376ed1c01a6c0910fcfdc4b8993/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f89f65d85774f1797239693cef07ad4c97fdd0639544bad9ac4b869782eb1981", size = 227339, upload-time = "2025-06-09T22:59:56.187Z" }, - { url = "https://files.pythonhosted.org/packages/50/82/41cb97d9c9a5ff94438c63cc343eb7980dac4187eb625a51bdfdb7707314/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1073557c941395fdfcfac13eb2456cb8aad89f9de27bae29fabca8e563b12615", size = 212969, upload-time = "2025-06-09T22:59:57.604Z" }, - { url = "https://files.pythonhosted.org/packages/13/47/f9179ee5ee4f55629e4f28c660b3fdf2775c8bfde8f9c53f2de2d93f52a9/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed8d2fa095aae4bdc7fdd80351009a48d286635edffee66bf865e37a9125c50", size = 222862, upload-time = "2025-06-09T22:59:59.498Z" }, - { url = "https://files.pythonhosted.org/packages/1a/52/df81e41ec6b953902c8b7e3a83bee48b195cb0e5ec2eabae5d8330c78038/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:24c34bea555fe42d9f928ba0a740c553088500377448febecaa82cc3e88aa1fa", size = 222492, upload-time = "2025-06-09T23:00:01.026Z" }, - { url = "https://files.pythonhosted.org/packages/84/17/30d6ea87fa95a9408245a948604b82c1a4b8b3e153cea596421a2aef2754/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:69cac419ac6a6baad202c85aaf467b65ac860ac2e7f2ac1686dc40dbb52f6577", size = 238250, upload-time = "2025-06-09T23:00:03.401Z" }, - { url = "https://files.pythonhosted.org/packages/8f/00/ecbeb51669e3c3df76cf2ddd66ae3e48345ec213a55e3887d216eb4fbab3/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:960d67d0611f4c87da7e2ae2eacf7ea81a5be967861e0c63cf205215afbfac59", size = 218720, upload-time = "2025-06-09T23:00:05.282Z" }, - { url = "https://files.pythonhosted.org/packages/1a/c0/c224ce0e0eb31cc57f67742071bb470ba8246623c1823a7530be0e76164c/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:41be2964bd4b15bf575e5daee5a5ce7ed3115320fb3c2b71fca05582ffa4dc9e", size = 232585, upload-time = "2025-06-09T23:00:07.962Z" }, - { url = "https://files.pythonhosted.org/packages/55/3c/34cb694abf532f31f365106deebdeac9e45c19304d83cf7d51ebbb4ca4d1/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46d84d49e00c9429238a7ce02dc0be8f6d7cd0cd405abd1bebdc991bf27c15bd", size = 234248, upload-time = "2025-06-09T23:00:09.428Z" }, - { url = "https://files.pythonhosted.org/packages/98/c0/2052d8b6cecda2e70bd81299e3512fa332abb6dcd2969b9c80dfcdddbf75/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15900082e886edb37480335d9d518cec978afc69ccbc30bd18610b7c1b22a718", size = 221621, upload-time = "2025-06-09T23:00:11.32Z" }, - { url = "https://files.pythonhosted.org/packages/c5/bf/7dcebae315436903b1d98ffb791a09d674c88480c158aa171958a3ac07f0/frozenlist-1.7.0-cp310-cp310-win32.whl", hash = "sha256:400ddd24ab4e55014bba442d917203c73b2846391dd42ca5e38ff52bb18c3c5e", size = 39578, upload-time = "2025-06-09T23:00:13.526Z" }, - { url = "https://files.pythonhosted.org/packages/8f/5f/f69818f017fa9a3d24d1ae39763e29b7f60a59e46d5f91b9c6b21622f4cd/frozenlist-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:6eb93efb8101ef39d32d50bce242c84bcbddb4f7e9febfa7b524532a239b4464", size = 43830, upload-time = "2025-06-09T23:00:14.98Z" }, - { url = "https://files.pythonhosted.org/packages/34/7e/803dde33760128acd393a27eb002f2020ddb8d99d30a44bfbaab31c5f08a/frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a", size = 82251, upload-time = "2025-06-09T23:00:16.279Z" }, - { url = "https://files.pythonhosted.org/packages/75/a9/9c2c5760b6ba45eae11334db454c189d43d34a4c0b489feb2175e5e64277/frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750", size = 48183, upload-time = "2025-06-09T23:00:17.698Z" }, - { url = "https://files.pythonhosted.org/packages/47/be/4038e2d869f8a2da165f35a6befb9158c259819be22eeaf9c9a8f6a87771/frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd", size = 47107, upload-time = "2025-06-09T23:00:18.952Z" }, - { url = "https://files.pythonhosted.org/packages/79/26/85314b8a83187c76a37183ceed886381a5f992975786f883472fcb6dc5f2/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2", size = 237333, upload-time = "2025-06-09T23:00:20.275Z" }, - { url = "https://files.pythonhosted.org/packages/1f/fd/e5b64f7d2c92a41639ffb2ad44a6a82f347787abc0c7df5f49057cf11770/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f", size = 231724, upload-time = "2025-06-09T23:00:21.705Z" }, - { url = "https://files.pythonhosted.org/packages/20/fb/03395c0a43a5976af4bf7534759d214405fbbb4c114683f434dfdd3128ef/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30", size = 245842, upload-time = "2025-06-09T23:00:23.148Z" }, - { url = "https://files.pythonhosted.org/packages/d0/15/c01c8e1dffdac5d9803507d824f27aed2ba76b6ed0026fab4d9866e82f1f/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98", size = 239767, upload-time = "2025-06-09T23:00:25.103Z" }, - { url = "https://files.pythonhosted.org/packages/14/99/3f4c6fe882c1f5514b6848aa0a69b20cb5e5d8e8f51a339d48c0e9305ed0/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86", size = 224130, upload-time = "2025-06-09T23:00:27.061Z" }, - { url = "https://files.pythonhosted.org/packages/4d/83/220a374bd7b2aeba9d0725130665afe11de347d95c3620b9b82cc2fcab97/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae", size = 235301, upload-time = "2025-06-09T23:00:29.02Z" }, - { url = "https://files.pythonhosted.org/packages/03/3c/3e3390d75334a063181625343e8daab61b77e1b8214802cc4e8a1bb678fc/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8", size = 234606, upload-time = "2025-06-09T23:00:30.514Z" }, - { url = "https://files.pythonhosted.org/packages/23/1e/58232c19608b7a549d72d9903005e2d82488f12554a32de2d5fb59b9b1ba/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31", size = 248372, upload-time = "2025-06-09T23:00:31.966Z" }, - { url = "https://files.pythonhosted.org/packages/c0/a4/e4a567e01702a88a74ce8a324691e62a629bf47d4f8607f24bf1c7216e7f/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7", size = 229860, upload-time = "2025-06-09T23:00:33.375Z" }, - { url = "https://files.pythonhosted.org/packages/73/a6/63b3374f7d22268b41a9db73d68a8233afa30ed164c46107b33c4d18ecdd/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5", size = 245893, upload-time = "2025-06-09T23:00:35.002Z" }, - { url = "https://files.pythonhosted.org/packages/6d/eb/d18b3f6e64799a79673c4ba0b45e4cfbe49c240edfd03a68be20002eaeaa/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898", size = 246323, upload-time = "2025-06-09T23:00:36.468Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f5/720f3812e3d06cd89a1d5db9ff6450088b8f5c449dae8ffb2971a44da506/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56", size = 233149, upload-time = "2025-06-09T23:00:37.963Z" }, - { url = "https://files.pythonhosted.org/packages/69/68/03efbf545e217d5db8446acfd4c447c15b7c8cf4dbd4a58403111df9322d/frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7", size = 39565, upload-time = "2025-06-09T23:00:39.753Z" }, - { url = "https://files.pythonhosted.org/packages/58/17/fe61124c5c333ae87f09bb67186d65038834a47d974fc10a5fadb4cc5ae1/frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d", size = 44019, upload-time = "2025-06-09T23:00:40.988Z" }, - { url = "https://files.pythonhosted.org/packages/ef/a2/c8131383f1e66adad5f6ecfcce383d584ca94055a34d683bbb24ac5f2f1c/frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2", size = 81424, upload-time = "2025-06-09T23:00:42.24Z" }, - { url = "https://files.pythonhosted.org/packages/4c/9d/02754159955088cb52567337d1113f945b9e444c4960771ea90eb73de8db/frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb", size = 47952, upload-time = "2025-06-09T23:00:43.481Z" }, - { url = "https://files.pythonhosted.org/packages/01/7a/0046ef1bd6699b40acd2067ed6d6670b4db2f425c56980fa21c982c2a9db/frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478", size = 46688, upload-time = "2025-06-09T23:00:44.793Z" }, - { url = "https://files.pythonhosted.org/packages/d6/a2/a910bafe29c86997363fb4c02069df4ff0b5bc39d33c5198b4e9dd42d8f8/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8", size = 243084, upload-time = "2025-06-09T23:00:46.125Z" }, - { url = "https://files.pythonhosted.org/packages/64/3e/5036af9d5031374c64c387469bfcc3af537fc0f5b1187d83a1cf6fab1639/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08", size = 233524, upload-time = "2025-06-09T23:00:47.73Z" }, - { url = "https://files.pythonhosted.org/packages/06/39/6a17b7c107a2887e781a48ecf20ad20f1c39d94b2a548c83615b5b879f28/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4", size = 248493, upload-time = "2025-06-09T23:00:49.742Z" }, - { url = "https://files.pythonhosted.org/packages/be/00/711d1337c7327d88c44d91dd0f556a1c47fb99afc060ae0ef66b4d24793d/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b", size = 244116, upload-time = "2025-06-09T23:00:51.352Z" }, - { url = "https://files.pythonhosted.org/packages/24/fe/74e6ec0639c115df13d5850e75722750adabdc7de24e37e05a40527ca539/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e", size = 224557, upload-time = "2025-06-09T23:00:52.855Z" }, - { url = "https://files.pythonhosted.org/packages/8d/db/48421f62a6f77c553575201e89048e97198046b793f4a089c79a6e3268bd/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca", size = 241820, upload-time = "2025-06-09T23:00:54.43Z" }, - { url = "https://files.pythonhosted.org/packages/1d/fa/cb4a76bea23047c8462976ea7b7a2bf53997a0ca171302deae9d6dd12096/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df", size = 236542, upload-time = "2025-06-09T23:00:56.409Z" }, - { url = "https://files.pythonhosted.org/packages/5d/32/476a4b5cfaa0ec94d3f808f193301debff2ea42288a099afe60757ef6282/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5", size = 249350, upload-time = "2025-06-09T23:00:58.468Z" }, - { url = "https://files.pythonhosted.org/packages/8d/ba/9a28042f84a6bf8ea5dbc81cfff8eaef18d78b2a1ad9d51c7bc5b029ad16/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025", size = 225093, upload-time = "2025-06-09T23:01:00.015Z" }, - { url = "https://files.pythonhosted.org/packages/bc/29/3a32959e68f9cf000b04e79ba574527c17e8842e38c91d68214a37455786/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01", size = 245482, upload-time = "2025-06-09T23:01:01.474Z" }, - { url = "https://files.pythonhosted.org/packages/80/e8/edf2f9e00da553f07f5fa165325cfc302dead715cab6ac8336a5f3d0adc2/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08", size = 249590, upload-time = "2025-06-09T23:01:02.961Z" }, - { url = "https://files.pythonhosted.org/packages/1c/80/9a0eb48b944050f94cc51ee1c413eb14a39543cc4f760ed12657a5a3c45a/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43", size = 237785, upload-time = "2025-06-09T23:01:05.095Z" }, - { url = "https://files.pythonhosted.org/packages/f3/74/87601e0fb0369b7a2baf404ea921769c53b7ae00dee7dcfe5162c8c6dbf0/frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3", size = 39487, upload-time = "2025-06-09T23:01:06.54Z" }, - { url = "https://files.pythonhosted.org/packages/0b/15/c026e9a9fc17585a9d461f65d8593d281fedf55fbf7eb53f16c6df2392f9/frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a", size = 43874, upload-time = "2025-06-09T23:01:07.752Z" }, - { url = "https://files.pythonhosted.org/packages/24/90/6b2cebdabdbd50367273c20ff6b57a3dfa89bd0762de02c3a1eb42cb6462/frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee", size = 79791, upload-time = "2025-06-09T23:01:09.368Z" }, - { url = "https://files.pythonhosted.org/packages/83/2e/5b70b6a3325363293fe5fc3ae74cdcbc3e996c2a11dde2fd9f1fb0776d19/frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d", size = 47165, upload-time = "2025-06-09T23:01:10.653Z" }, - { url = "https://files.pythonhosted.org/packages/f4/25/a0895c99270ca6966110f4ad98e87e5662eab416a17e7fd53c364bf8b954/frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43", size = 45881, upload-time = "2025-06-09T23:01:12.296Z" }, - { url = "https://files.pythonhosted.org/packages/19/7c/71bb0bbe0832793c601fff68cd0cf6143753d0c667f9aec93d3c323f4b55/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d", size = 232409, upload-time = "2025-06-09T23:01:13.641Z" }, - { url = "https://files.pythonhosted.org/packages/c0/45/ed2798718910fe6eb3ba574082aaceff4528e6323f9a8570be0f7028d8e9/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee", size = 225132, upload-time = "2025-06-09T23:01:15.264Z" }, - { url = "https://files.pythonhosted.org/packages/ba/e2/8417ae0f8eacb1d071d4950f32f229aa6bf68ab69aab797b72a07ea68d4f/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb", size = 237638, upload-time = "2025-06-09T23:01:16.752Z" }, - { url = "https://files.pythonhosted.org/packages/f8/b7/2ace5450ce85f2af05a871b8c8719b341294775a0a6c5585d5e6170f2ce7/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f", size = 233539, upload-time = "2025-06-09T23:01:18.202Z" }, - { url = "https://files.pythonhosted.org/packages/46/b9/6989292c5539553dba63f3c83dc4598186ab2888f67c0dc1d917e6887db6/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60", size = 215646, upload-time = "2025-06-09T23:01:19.649Z" }, - { url = "https://files.pythonhosted.org/packages/72/31/bc8c5c99c7818293458fe745dab4fd5730ff49697ccc82b554eb69f16a24/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00", size = 232233, upload-time = "2025-06-09T23:01:21.175Z" }, - { url = "https://files.pythonhosted.org/packages/59/52/460db4d7ba0811b9ccb85af996019f5d70831f2f5f255f7cc61f86199795/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b", size = 227996, upload-time = "2025-06-09T23:01:23.098Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c9/f4b39e904c03927b7ecf891804fd3b4df3db29b9e487c6418e37988d6e9d/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c", size = 242280, upload-time = "2025-06-09T23:01:24.808Z" }, - { url = "https://files.pythonhosted.org/packages/b8/33/3f8d6ced42f162d743e3517781566b8481322be321b486d9d262adf70bfb/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949", size = 217717, upload-time = "2025-06-09T23:01:26.28Z" }, - { url = "https://files.pythonhosted.org/packages/3e/e8/ad683e75da6ccef50d0ab0c2b2324b32f84fc88ceee778ed79b8e2d2fe2e/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca", size = 236644, upload-time = "2025-06-09T23:01:27.887Z" }, - { url = "https://files.pythonhosted.org/packages/b2/14/8d19ccdd3799310722195a72ac94ddc677541fb4bef4091d8e7775752360/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b", size = 238879, upload-time = "2025-06-09T23:01:29.524Z" }, - { url = "https://files.pythonhosted.org/packages/ce/13/c12bf657494c2fd1079a48b2db49fa4196325909249a52d8f09bc9123fd7/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e", size = 232502, upload-time = "2025-06-09T23:01:31.287Z" }, - { url = "https://files.pythonhosted.org/packages/d7/8b/e7f9dfde869825489382bc0d512c15e96d3964180c9499efcec72e85db7e/frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1", size = 39169, upload-time = "2025-06-09T23:01:35.503Z" }, - { url = "https://files.pythonhosted.org/packages/35/89/a487a98d94205d85745080a37860ff5744b9820a2c9acbcdd9440bfddf98/frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba", size = 43219, upload-time = "2025-06-09T23:01:36.784Z" }, - { url = "https://files.pythonhosted.org/packages/56/d5/5c4cf2319a49eddd9dd7145e66c4866bdc6f3dbc67ca3d59685149c11e0d/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d", size = 84345, upload-time = "2025-06-09T23:01:38.295Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7d/ec2c1e1dc16b85bc9d526009961953df9cec8481b6886debb36ec9107799/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d", size = 48880, upload-time = "2025-06-09T23:01:39.887Z" }, - { url = "https://files.pythonhosted.org/packages/69/86/f9596807b03de126e11e7d42ac91e3d0b19a6599c714a1989a4e85eeefc4/frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b", size = 48498, upload-time = "2025-06-09T23:01:41.318Z" }, - { url = "https://files.pythonhosted.org/packages/5e/cb/df6de220f5036001005f2d726b789b2c0b65f2363b104bbc16f5be8084f8/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146", size = 292296, upload-time = "2025-06-09T23:01:42.685Z" }, - { url = "https://files.pythonhosted.org/packages/83/1f/de84c642f17c8f851a2905cee2dae401e5e0daca9b5ef121e120e19aa825/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74", size = 273103, upload-time = "2025-06-09T23:01:44.166Z" }, - { url = "https://files.pythonhosted.org/packages/88/3c/c840bfa474ba3fa13c772b93070893c6e9d5c0350885760376cbe3b6c1b3/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1", size = 292869, upload-time = "2025-06-09T23:01:45.681Z" }, - { url = "https://files.pythonhosted.org/packages/a6/1c/3efa6e7d5a39a1d5ef0abeb51c48fb657765794a46cf124e5aca2c7a592c/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1", size = 291467, upload-time = "2025-06-09T23:01:47.234Z" }, - { url = "https://files.pythonhosted.org/packages/4f/00/d5c5e09d4922c395e2f2f6b79b9a20dab4b67daaf78ab92e7729341f61f6/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384", size = 266028, upload-time = "2025-06-09T23:01:48.819Z" }, - { url = "https://files.pythonhosted.org/packages/4e/27/72765be905619dfde25a7f33813ac0341eb6b076abede17a2e3fbfade0cb/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb", size = 284294, upload-time = "2025-06-09T23:01:50.394Z" }, - { url = "https://files.pythonhosted.org/packages/88/67/c94103a23001b17808eb7dd1200c156bb69fb68e63fcf0693dde4cd6228c/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c", size = 281898, upload-time = "2025-06-09T23:01:52.234Z" }, - { url = "https://files.pythonhosted.org/packages/42/34/a3e2c00c00f9e2a9db5653bca3fec306349e71aff14ae45ecc6d0951dd24/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65", size = 290465, upload-time = "2025-06-09T23:01:53.788Z" }, - { url = "https://files.pythonhosted.org/packages/bb/73/f89b7fbce8b0b0c095d82b008afd0590f71ccb3dee6eee41791cf8cd25fd/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3", size = 266385, upload-time = "2025-06-09T23:01:55.769Z" }, - { url = "https://files.pythonhosted.org/packages/cd/45/e365fdb554159462ca12df54bc59bfa7a9a273ecc21e99e72e597564d1ae/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657", size = 288771, upload-time = "2025-06-09T23:01:57.4Z" }, - { url = "https://files.pythonhosted.org/packages/00/11/47b6117002a0e904f004d70ec5194fe9144f117c33c851e3d51c765962d0/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104", size = 288206, upload-time = "2025-06-09T23:01:58.936Z" }, - { url = "https://files.pythonhosted.org/packages/40/37/5f9f3c3fd7f7746082ec67bcdc204db72dad081f4f83a503d33220a92973/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf", size = 282620, upload-time = "2025-06-09T23:02:00.493Z" }, - { url = "https://files.pythonhosted.org/packages/0b/31/8fbc5af2d183bff20f21aa743b4088eac4445d2bb1cdece449ae80e4e2d1/frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81", size = 43059, upload-time = "2025-06-09T23:02:02.072Z" }, - { url = "https://files.pythonhosted.org/packages/bb/ed/41956f52105b8dbc26e457c5705340c67c8cc2b79f394b79bffc09d0e938/frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e", size = 47516, upload-time = "2025-06-09T23:02:03.779Z" }, - { url = "https://files.pythonhosted.org/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e", size = 13106, upload-time = "2025-06-09T23:02:34.204Z" }, -] - -[[package]] -name = "fsspec" -version = "2025.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8b/02/0835e6ab9cfc03916fe3f78c0956cfcdb6ff2669ffa6651065d5ebf7fc98/fsspec-2025.7.0.tar.gz", hash = "sha256:786120687ffa54b8283d942929540d8bc5ccfa820deb555a2b5d0ed2b737bf58", size = 304432, upload-time = "2025-07-15T16:05:21.19Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/e0/014d5d9d7a4564cf1c40b5039bc882db69fd881111e03ab3657ac0b218e2/fsspec-2025.7.0-py3-none-any.whl", hash = "sha256:8b012e39f63c7d5f10474de957f3ab793b47b45ae7d39f2fb735f8bbe25c0e21", size = 199597, upload-time = "2025-07-15T16:05:19.529Z" }, -] - -[[package]] -name = "ghp-import" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, -] - -[[package]] -name = "google-auth" -version = "2.40.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cachetools" }, - { name = "pyasn1-modules" }, - { name = "rsa" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9e/9b/e92ef23b84fa10a64ce4831390b7a4c2e53c0132568d99d4ae61d04c8855/google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77", size = 281029, upload-time = "2025-06-04T18:04:57.577Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/63/b19553b658a1692443c62bd07e5868adaa0ad746a0751ba62c59568cd45b/google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca", size = 216137, upload-time = "2025-06-04T18:04:55.573Z" }, -] - -[[package]] -name = "googleapis-common-protos" -version = "1.70.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/39/24/33db22342cf4a2ea27c9955e6713140fedd51e8b141b5ce5260897020f1a/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257", size = 145903, upload-time = "2025-04-14T10:17:02.924Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/f1/62a193f0227cf15a920390abe675f386dec35f7ae3ffe6da582d3ade42c7/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8", size = 294530, upload-time = "2025-04-14T10:17:01.271Z" }, -] - -[[package]] -name = "griffe" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dd/72/10c5799440ce6f3001b7913988b50a99d7b156da71fe19be06178d5a2dd5/griffe-1.8.0.tar.gz", hash = "sha256:0b4658443858465c13b2de07ff5e15a1032bc889cfafad738a476b8b97bb28d7", size = 401098, upload-time = "2025-07-22T23:45:54.629Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/c4/a839fcc28bebfa72925d9121c4d39398f77f95bcba0cf26c972a0cfb1de7/griffe-1.8.0-py3-none-any.whl", hash = "sha256:110faa744b2c5c84dd432f4fa9aa3b14805dd9519777dd55e8db214320593b02", size = 132487, upload-time = "2025-07-22T23:45:52.778Z" }, -] - -[[package]] -name = "grpcio" -version = "1.73.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/e8/b43b851537da2e2f03fa8be1aef207e5cbfb1a2e014fbb6b40d24c177cd3/grpcio-1.73.1.tar.gz", hash = "sha256:7fce2cd1c0c1116cf3850564ebfc3264fba75d3c74a7414373f1238ea365ef87", size = 12730355, upload-time = "2025-06-26T01:53:24.622Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/51/a5748ab2773d893d099b92653039672f7e26dd35741020972b84d604066f/grpcio-1.73.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:2d70f4ddd0a823436c2624640570ed6097e40935c9194482475fe8e3d9754d55", size = 5365087, upload-time = "2025-06-26T01:51:44.541Z" }, - { url = "https://files.pythonhosted.org/packages/ae/12/c5ee1a5dfe93dbc2eaa42a219e2bf887250b52e2e2ee5c036c4695f2769c/grpcio-1.73.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:3841a8a5a66830261ab6a3c2a3dc539ed84e4ab019165f77b3eeb9f0ba621f26", size = 10608921, upload-time = "2025-06-26T01:51:48.111Z" }, - { url = "https://files.pythonhosted.org/packages/c4/6d/b0c6a8120f02b7d15c5accda6bfc43bc92be70ada3af3ba6d8e077c00374/grpcio-1.73.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:628c30f8e77e0258ab788750ec92059fc3d6628590fb4b7cea8c102503623ed7", size = 5803221, upload-time = "2025-06-26T01:51:50.486Z" }, - { url = "https://files.pythonhosted.org/packages/a6/7a/3c886d9f1c1e416ae81f7f9c7d1995ae72cd64712d29dab74a6bafacb2d2/grpcio-1.73.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67a0468256c9db6d5ecb1fde4bf409d016f42cef649323f0a08a72f352d1358b", size = 6444603, upload-time = "2025-06-26T01:51:52.203Z" }, - { url = "https://files.pythonhosted.org/packages/42/07/f143a2ff534982c9caa1febcad1c1073cdec732f6ac7545d85555a900a7e/grpcio-1.73.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68b84d65bbdebd5926eb5c53b0b9ec3b3f83408a30e4c20c373c5337b4219ec5", size = 6040969, upload-time = "2025-06-26T01:51:55.028Z" }, - { url = "https://files.pythonhosted.org/packages/fb/0f/523131b7c9196d0718e7b2dac0310eb307b4117bdbfef62382e760f7e8bb/grpcio-1.73.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c54796ca22b8349cc594d18b01099e39f2b7ffb586ad83217655781a350ce4da", size = 6132201, upload-time = "2025-06-26T01:51:56.867Z" }, - { url = "https://files.pythonhosted.org/packages/ad/18/010a055410eef1d3a7a1e477ec9d93b091ac664ad93e9c5f56d6cc04bdee/grpcio-1.73.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:75fc8e543962ece2f7ecd32ada2d44c0c8570ae73ec92869f9af8b944863116d", size = 6774718, upload-time = "2025-06-26T01:51:58.338Z" }, - { url = "https://files.pythonhosted.org/packages/16/11/452bfc1ab39d8ee748837ab8ee56beeae0290861052948785c2c445fb44b/grpcio-1.73.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6a6037891cd2b1dd1406b388660522e1565ed340b1fea2955b0234bdd941a862", size = 6304362, upload-time = "2025-06-26T01:51:59.802Z" }, - { url = "https://files.pythonhosted.org/packages/1e/1c/c75ceee626465721e5cb040cf4b271eff817aa97388948660884cb7adffa/grpcio-1.73.1-cp310-cp310-win32.whl", hash = "sha256:cce7265b9617168c2d08ae570fcc2af4eaf72e84f8c710ca657cc546115263af", size = 3679036, upload-time = "2025-06-26T01:52:01.817Z" }, - { url = "https://files.pythonhosted.org/packages/62/2e/42cb31b6cbd671a7b3dbd97ef33f59088cf60e3cf2141368282e26fafe79/grpcio-1.73.1-cp310-cp310-win_amd64.whl", hash = "sha256:6a2b372e65fad38842050943f42ce8fee00c6f2e8ea4f7754ba7478d26a356ee", size = 4340208, upload-time = "2025-06-26T01:52:03.674Z" }, - { url = "https://files.pythonhosted.org/packages/e4/41/921565815e871d84043e73e2c0e748f0318dab6fa9be872cd042778f14a9/grpcio-1.73.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:ba2cea9f7ae4bc21f42015f0ec98f69ae4179848ad744b210e7685112fa507a1", size = 5363853, upload-time = "2025-06-26T01:52:05.5Z" }, - { url = "https://files.pythonhosted.org/packages/b0/cc/9c51109c71d068e4d474becf5f5d43c9d63038cec1b74112978000fa72f4/grpcio-1.73.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:d74c3f4f37b79e746271aa6cdb3a1d7e4432aea38735542b23adcabaaee0c097", size = 10621476, upload-time = "2025-06-26T01:52:07.211Z" }, - { url = "https://files.pythonhosted.org/packages/8f/d3/33d738a06f6dbd4943f4d377468f8299941a7c8c6ac8a385e4cef4dd3c93/grpcio-1.73.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5b9b1805a7d61c9e90541cbe8dfe0a593dfc8c5c3a43fe623701b6a01b01d710", size = 5807903, upload-time = "2025-06-26T01:52:09.466Z" }, - { url = "https://files.pythonhosted.org/packages/5d/47/36deacd3c967b74e0265f4c608983e897d8bb3254b920f8eafdf60e4ad7e/grpcio-1.73.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3215f69a0670a8cfa2ab53236d9e8026bfb7ead5d4baabe7d7dc11d30fda967", size = 6448172, upload-time = "2025-06-26T01:52:11.459Z" }, - { url = "https://files.pythonhosted.org/packages/0e/64/12d6dc446021684ee1428ea56a3f3712048a18beeadbdefa06e6f8814a6e/grpcio-1.73.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc5eccfd9577a5dc7d5612b2ba90cca4ad14c6d949216c68585fdec9848befb1", size = 6044226, upload-time = "2025-06-26T01:52:12.987Z" }, - { url = "https://files.pythonhosted.org/packages/72/4b/6bae2d88a006000f1152d2c9c10ffd41d0131ca1198e0b661101c2e30ab9/grpcio-1.73.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dc7d7fd520614fce2e6455ba89791458020a39716951c7c07694f9dbae28e9c0", size = 6135690, upload-time = "2025-06-26T01:52:14.92Z" }, - { url = "https://files.pythonhosted.org/packages/38/64/02c83b5076510784d1305025e93e0d78f53bb6a0213c8c84cfe8a00c5c48/grpcio-1.73.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:105492124828911f85127e4825d1c1234b032cb9d238567876b5515d01151379", size = 6775867, upload-time = "2025-06-26T01:52:16.446Z" }, - { url = "https://files.pythonhosted.org/packages/42/72/a13ff7ba6c68ccffa35dacdc06373a76c0008fd75777cba84d7491956620/grpcio-1.73.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:610e19b04f452ba6f402ac9aa94eb3d21fbc94553368008af634812c4a85a99e", size = 6308380, upload-time = "2025-06-26T01:52:18.417Z" }, - { url = "https://files.pythonhosted.org/packages/65/ae/d29d948021faa0070ec33245c1ae354e2aefabd97e6a9a7b6dcf0fb8ef6b/grpcio-1.73.1-cp311-cp311-win32.whl", hash = "sha256:d60588ab6ba0ac753761ee0e5b30a29398306401bfbceffe7d68ebb21193f9d4", size = 3679139, upload-time = "2025-06-26T01:52:20.171Z" }, - { url = "https://files.pythonhosted.org/packages/af/66/e1bbb0c95ea222947f0829b3db7692c59b59bcc531df84442e413fa983d9/grpcio-1.73.1-cp311-cp311-win_amd64.whl", hash = "sha256:6957025a4608bb0a5ff42abd75bfbb2ed99eda29d5992ef31d691ab54b753643", size = 4342558, upload-time = "2025-06-26T01:52:22.137Z" }, - { url = "https://files.pythonhosted.org/packages/b8/41/456caf570c55d5ac26f4c1f2db1f2ac1467d5bf3bcd660cba3e0a25b195f/grpcio-1.73.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:921b25618b084e75d424a9f8e6403bfeb7abef074bb6c3174701e0f2542debcf", size = 5334621, upload-time = "2025-06-26T01:52:23.602Z" }, - { url = "https://files.pythonhosted.org/packages/2a/c2/9a15e179e49f235bb5e63b01590658c03747a43c9775e20c4e13ca04f4c4/grpcio-1.73.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:277b426a0ed341e8447fbf6c1d6b68c952adddf585ea4685aa563de0f03df887", size = 10601131, upload-time = "2025-06-26T01:52:25.691Z" }, - { url = "https://files.pythonhosted.org/packages/0c/1d/1d39e90ef6348a0964caa7c5c4d05f3bae2c51ab429eb7d2e21198ac9b6d/grpcio-1.73.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:96c112333309493c10e118d92f04594f9055774757f5d101b39f8150f8c25582", size = 5759268, upload-time = "2025-06-26T01:52:27.631Z" }, - { url = "https://files.pythonhosted.org/packages/8a/2b/2dfe9ae43de75616177bc576df4c36d6401e0959833b2e5b2d58d50c1f6b/grpcio-1.73.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f48e862aed925ae987eb7084409a80985de75243389dc9d9c271dd711e589918", size = 6409791, upload-time = "2025-06-26T01:52:29.711Z" }, - { url = "https://files.pythonhosted.org/packages/6e/66/e8fe779b23b5a26d1b6949e5c70bc0a5fd08f61a6ec5ac7760d589229511/grpcio-1.73.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83a6c2cce218e28f5040429835fa34a29319071079e3169f9543c3fbeff166d2", size = 6003728, upload-time = "2025-06-26T01:52:31.352Z" }, - { url = "https://files.pythonhosted.org/packages/a9/39/57a18fcef567784108c4fc3f5441cb9938ae5a51378505aafe81e8e15ecc/grpcio-1.73.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:65b0458a10b100d815a8426b1442bd17001fdb77ea13665b2f7dc9e8587fdc6b", size = 6103364, upload-time = "2025-06-26T01:52:33.028Z" }, - { url = "https://files.pythonhosted.org/packages/c5/46/28919d2aa038712fc399d02fa83e998abd8c1f46c2680c5689deca06d1b2/grpcio-1.73.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0a9f3ea8dce9eae9d7cb36827200133a72b37a63896e0e61a9d5ec7d61a59ab1", size = 6749194, upload-time = "2025-06-26T01:52:34.734Z" }, - { url = "https://files.pythonhosted.org/packages/3d/56/3898526f1fad588c5d19a29ea0a3a4996fb4fa7d7c02dc1be0c9fd188b62/grpcio-1.73.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:de18769aea47f18e782bf6819a37c1c528914bfd5683b8782b9da356506190c8", size = 6283902, upload-time = "2025-06-26T01:52:36.503Z" }, - { url = "https://files.pythonhosted.org/packages/dc/64/18b77b89c5870d8ea91818feb0c3ffb5b31b48d1b0ee3e0f0d539730fea3/grpcio-1.73.1-cp312-cp312-win32.whl", hash = "sha256:24e06a5319e33041e322d32c62b1e728f18ab8c9dbc91729a3d9f9e3ed336642", size = 3668687, upload-time = "2025-06-26T01:52:38.678Z" }, - { url = "https://files.pythonhosted.org/packages/3c/52/302448ca6e52f2a77166b2e2ed75f5d08feca4f2145faf75cb768cccb25b/grpcio-1.73.1-cp312-cp312-win_amd64.whl", hash = "sha256:303c8135d8ab176f8038c14cc10d698ae1db9c480f2b2823f7a987aa2a4c5646", size = 4334887, upload-time = "2025-06-26T01:52:40.743Z" }, - { url = "https://files.pythonhosted.org/packages/37/bf/4ca20d1acbefabcaba633ab17f4244cbbe8eca877df01517207bd6655914/grpcio-1.73.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:b310824ab5092cf74750ebd8a8a8981c1810cb2b363210e70d06ef37ad80d4f9", size = 5335615, upload-time = "2025-06-26T01:52:42.896Z" }, - { url = "https://files.pythonhosted.org/packages/75/ed/45c345f284abec5d4f6d77cbca9c52c39b554397eb7de7d2fcf440bcd049/grpcio-1.73.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:8f5a6df3fba31a3485096ac85b2e34b9666ffb0590df0cd044f58694e6a1f6b5", size = 10595497, upload-time = "2025-06-26T01:52:44.695Z" }, - { url = "https://files.pythonhosted.org/packages/a4/75/bff2c2728018f546d812b755455014bc718f8cdcbf5c84f1f6e5494443a8/grpcio-1.73.1-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:052e28fe9c41357da42250a91926a3e2f74c046575c070b69659467ca5aa976b", size = 5765321, upload-time = "2025-06-26T01:52:46.871Z" }, - { url = "https://files.pythonhosted.org/packages/70/3b/14e43158d3b81a38251b1d231dfb45a9b492d872102a919fbf7ba4ac20cd/grpcio-1.73.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c0bf15f629b1497436596b1cbddddfa3234273490229ca29561209778ebe182", size = 6415436, upload-time = "2025-06-26T01:52:49.134Z" }, - { url = "https://files.pythonhosted.org/packages/e5/3f/81d9650ca40b54338336fd360f36773be8cb6c07c036e751d8996eb96598/grpcio-1.73.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ab860d5bfa788c5a021fba264802e2593688cd965d1374d31d2b1a34cacd854", size = 6007012, upload-time = "2025-06-26T01:52:51.076Z" }, - { url = "https://files.pythonhosted.org/packages/55/f4/59edf5af68d684d0f4f7ad9462a418ac517201c238551529098c9aa28cb0/grpcio-1.73.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:ad1d958c31cc91ab050bd8a91355480b8e0683e21176522bacea225ce51163f2", size = 6105209, upload-time = "2025-06-26T01:52:52.773Z" }, - { url = "https://files.pythonhosted.org/packages/e4/a8/700d034d5d0786a5ba14bfa9ce974ed4c976936c2748c2bd87aa50f69b36/grpcio-1.73.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f43ffb3bd415c57224c7427bfb9e6c46a0b6e998754bfa0d00f408e1873dcbb5", size = 6753655, upload-time = "2025-06-26T01:52:55.064Z" }, - { url = "https://files.pythonhosted.org/packages/1f/29/efbd4ac837c23bc48e34bbaf32bd429f0dc9ad7f80721cdb4622144c118c/grpcio-1.73.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:686231cdd03a8a8055f798b2b54b19428cdf18fa1549bee92249b43607c42668", size = 6287288, upload-time = "2025-06-26T01:52:57.33Z" }, - { url = "https://files.pythonhosted.org/packages/d8/61/c6045d2ce16624bbe18b5d169c1a5ce4d6c3a47bc9d0e5c4fa6a50ed1239/grpcio-1.73.1-cp313-cp313-win32.whl", hash = "sha256:89018866a096e2ce21e05eabed1567479713ebe57b1db7cbb0f1e3b896793ba4", size = 3668151, upload-time = "2025-06-26T01:52:59.405Z" }, - { url = "https://files.pythonhosted.org/packages/c2/d7/77ac689216daee10de318db5aa1b88d159432dc76a130948a56b3aa671a2/grpcio-1.73.1-cp313-cp313-win_amd64.whl", hash = "sha256:4a68f8c9966b94dff693670a5cf2b54888a48a5011c5d9ce2295a1a1465ee84f", size = 4335747, upload-time = "2025-06-26T01:53:01.233Z" }, -] - -[[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 = "hanzo-memory" -version = "1.0.1" -source = { editable = "." } -dependencies = [ - { name = "aiocache" }, - { name = "chromadb" }, - { name = "fastapi" }, - { name = "fastembed" }, - { name = "httpx" }, - { name = "lancedb" }, - { name = "litellm" }, - { name = "mcp" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "orjson" }, - { name = "passlib", extra = ["bcrypt"] }, - { name = "polars" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "python-jose", extra = ["cryptography"] }, - { name = "python-multipart" }, - { name = "redis" }, - { name = "rich" }, - { name = "scikit-learn" }, - { name = "sentence-transformers" }, - { name = "structlog" }, - { name = "tenacity" }, - { name = "tiktoken" }, - { name = "uvicorn", extra = ["standard"] }, -] - -[package.optional-dependencies] -dev = [ - { name = "black" }, - { name = "ipdb" }, - { name = "ipython", version = "8.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "mypy" }, - { name = "pre-commit" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "ruff" }, - { name = "twine" }, -] -docs = [ - { name = "mkdocs" }, - { name = "mkdocs-material" }, - { name = "mkdocstrings", extra = ["python"] }, -] -test = [ - { name = "factory-boy" }, - { name = "faker" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "pytest-mock" }, - { name = "respx" }, -] - -[package.metadata] -requires-dist = [ - { name = "aiocache", specifier = ">=0.12.0" }, - { name = "black", marker = "extra == 'dev'", specifier = ">=24.10.0" }, - { name = "chromadb", specifier = ">=0.5.0" }, - { name = "factory-boy", marker = "extra == 'test'", specifier = ">=3.3.0" }, - { name = "faker", marker = "extra == 'test'", specifier = ">=30.0.0" }, - { name = "fastapi", specifier = ">=0.115.0" }, - { name = "fastembed", specifier = ">=0.4.0" }, - { name = "httpx", specifier = ">=0.28.0" }, - { name = "ipdb", marker = "extra == 'dev'", specifier = ">=0.13.0" }, - { name = "ipython", marker = "extra == 'dev'", specifier = ">=8.29.0" }, - { name = "lancedb", specifier = ">=0.8.0" }, - { name = "litellm", specifier = ">=1.56.0" }, - { name = "mcp", specifier = ">=1.2.0" }, - { name = "mkdocs", marker = "extra == 'docs'", specifier = ">=1.6.0" }, - { name = "mkdocs-material", marker = "extra == 'docs'", specifier = ">=9.5.0" }, - { name = "mkdocstrings", extras = ["python"], marker = "extra == 'docs'", specifier = ">=0.27.0" }, - { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.13.0" }, - { name = "numpy", specifier = ">=1.26.0" }, - { name = "orjson", specifier = ">=3.10.0" }, - { name = "passlib", extras = ["bcrypt"], specifier = ">=1.7.0" }, - { name = "polars", specifier = ">=1.15.0" }, - { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.0.0" }, - { name = "pydantic", specifier = ">=2.9.0" }, - { name = "pydantic-settings", specifier = ">=2.6.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.0" }, - { name = "pytest", marker = "extra == 'test'", specifier = ">=8.3.0" }, - { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24.0" }, - { name = "pytest-asyncio", marker = "extra == 'test'", specifier = ">=0.24.0" }, - { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=6.0.0" }, - { name = "pytest-cov", marker = "extra == 'test'", specifier = ">=6.0.0" }, - { name = "pytest-mock", marker = "extra == 'test'", specifier = ">=3.14.0" }, - { name = "python-jose", extras = ["cryptography"], specifier = ">=3.3.0" }, - { name = "python-multipart", specifier = ">=0.0.12" }, - { name = "redis", specifier = ">=5.2.0" }, - { name = "respx", marker = "extra == 'test'", specifier = ">=0.21.0" }, - { name = "rich", specifier = ">=13.7.1" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.8.0" }, - { name = "scikit-learn", specifier = ">=1.5.0" }, - { name = "sentence-transformers", specifier = ">=5.0.0" }, - { name = "structlog", specifier = ">=24.4.0" }, - { name = "tenacity", specifier = ">=9.0.0" }, - { name = "tiktoken", specifier = ">=0.8.0" }, - { name = "twine", marker = "extra == 'dev'", specifier = ">=4.0.0" }, - { name = "uvicorn", extras = ["standard"], specifier = ">=0.32.0" }, -] -provides-extras = ["dev", "test", "docs"] - -[[package]] -name = "hf-xet" -version = "1.1.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ed/d4/7685999e85945ed0d7f0762b686ae7015035390de1161dcea9d5276c134c/hf_xet-1.1.5.tar.gz", hash = "sha256:69ebbcfd9ec44fdc2af73441619eeb06b94ee34511bbcf57cd423820090f5694", size = 495969, upload-time = "2025-06-20T21:48:38.007Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/89/a1119eebe2836cb25758e7661d6410d3eae982e2b5e974bcc4d250be9012/hf_xet-1.1.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:f52c2fa3635b8c37c7764d8796dfa72706cc4eded19d638331161e82b0792e23", size = 2687929, upload-time = "2025-06-20T21:48:32.284Z" }, - { url = "https://files.pythonhosted.org/packages/de/5f/2c78e28f309396e71ec8e4e9304a6483dcbc36172b5cea8f291994163425/hf_xet-1.1.5-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:9fa6e3ee5d61912c4a113e0708eaaef987047616465ac7aa30f7121a48fc1af8", size = 2556338, upload-time = "2025-06-20T21:48:30.079Z" }, - { url = "https://files.pythonhosted.org/packages/6d/2f/6cad7b5fe86b7652579346cb7f85156c11761df26435651cbba89376cd2c/hf_xet-1.1.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc874b5c843e642f45fd85cda1ce599e123308ad2901ead23d3510a47ff506d1", size = 3102894, upload-time = "2025-06-20T21:48:28.114Z" }, - { url = "https://files.pythonhosted.org/packages/d0/54/0fcf2b619720a26fbb6cc941e89f2472a522cd963a776c089b189559447f/hf_xet-1.1.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dbba1660e5d810bd0ea77c511a99e9242d920790d0e63c0e4673ed36c4022d18", size = 3002134, upload-time = "2025-06-20T21:48:25.906Z" }, - { url = "https://files.pythonhosted.org/packages/f3/92/1d351ac6cef7c4ba8c85744d37ffbfac2d53d0a6c04d2cabeba614640a78/hf_xet-1.1.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ab34c4c3104133c495785d5d8bba3b1efc99de52c02e759cf711a91fd39d3a14", size = 3171009, upload-time = "2025-06-20T21:48:33.987Z" }, - { url = "https://files.pythonhosted.org/packages/c9/65/4b2ddb0e3e983f2508528eb4501288ae2f84963586fbdfae596836d5e57a/hf_xet-1.1.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:83088ecea236d5113de478acb2339f92c95b4fb0462acaa30621fac02f5a534a", size = 3279245, upload-time = "2025-06-20T21:48:36.051Z" }, - { url = "https://files.pythonhosted.org/packages/f0/55/ef77a85ee443ae05a9e9cba1c9f0dd9241eb42da2aeba1dc50f51154c81a/hf_xet-1.1.5-cp37-abi3-win_amd64.whl", hash = "sha256:73e167d9807d166596b4b2f0b585c6d5bd84a26dea32843665a8b58f6edba245", size = 2738931, upload-time = "2025-06-20T21:48:39.482Z" }, -] - -[[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 = "httptools" -version = "0.6.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639, upload-time = "2024-10-16T19:45:08.902Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/6f/972f8eb0ea7d98a1c6be436e2142d51ad2a64ee18e02b0e7ff1f62171ab1/httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0", size = 198780, upload-time = "2024-10-16T19:44:06.882Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b0/17c672b4bc5c7ba7f201eada4e96c71d0a59fbc185e60e42580093a86f21/httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da", size = 103297, upload-time = "2024-10-16T19:44:08.129Z" }, - { url = "https://files.pythonhosted.org/packages/92/5e/b4a826fe91971a0b68e8c2bd4e7db3e7519882f5a8ccdb1194be2b3ab98f/httptools-0.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deee0e3343f98ee8047e9f4c5bc7cedbf69f5734454a94c38ee829fb2d5fa3c1", size = 443130, upload-time = "2024-10-16T19:44:09.45Z" }, - { url = "https://files.pythonhosted.org/packages/b0/51/ce61e531e40289a681a463e1258fa1e05e0be54540e40d91d065a264cd8f/httptools-0.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca80b7485c76f768a3bc83ea58373f8db7b015551117375e4918e2aa77ea9b50", size = 442148, upload-time = "2024-10-16T19:44:11.539Z" }, - { url = "https://files.pythonhosted.org/packages/ea/9e/270b7d767849b0c96f275c695d27ca76c30671f8eb8cc1bab6ced5c5e1d0/httptools-0.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90d96a385fa941283ebd231464045187a31ad932ebfa541be8edf5b3c2328959", size = 415949, upload-time = "2024-10-16T19:44:13.388Z" }, - { url = "https://files.pythonhosted.org/packages/81/86/ced96e3179c48c6f656354e106934e65c8963d48b69be78f355797f0e1b3/httptools-0.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59e724f8b332319e2875efd360e61ac07f33b492889284a3e05e6d13746876f4", size = 417591, upload-time = "2024-10-16T19:44:15.258Z" }, - { url = "https://files.pythonhosted.org/packages/75/73/187a3f620ed3175364ddb56847d7a608a6fc42d551e133197098c0143eca/httptools-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:c26f313951f6e26147833fc923f78f95604bbec812a43e5ee37f26dc9e5a686c", size = 88344, upload-time = "2024-10-16T19:44:16.54Z" }, - { url = "https://files.pythonhosted.org/packages/7b/26/bb526d4d14c2774fe07113ca1db7255737ffbb119315839af2065abfdac3/httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069", size = 199029, upload-time = "2024-10-16T19:44:18.427Z" }, - { url = "https://files.pythonhosted.org/packages/a6/17/3e0d3e9b901c732987a45f4f94d4e2c62b89a041d93db89eafb262afd8d5/httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a", size = 103492, upload-time = "2024-10-16T19:44:19.515Z" }, - { url = "https://files.pythonhosted.org/packages/b7/24/0fe235d7b69c42423c7698d086d4db96475f9b50b6ad26a718ef27a0bce6/httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975", size = 462891, upload-time = "2024-10-16T19:44:21.067Z" }, - { url = "https://files.pythonhosted.org/packages/b1/2f/205d1f2a190b72da6ffb5f41a3736c26d6fa7871101212b15e9b5cd8f61d/httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636", size = 459788, upload-time = "2024-10-16T19:44:22.958Z" }, - { url = "https://files.pythonhosted.org/packages/6e/4c/d09ce0eff09057a206a74575ae8f1e1e2f0364d20e2442224f9e6612c8b9/httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721", size = 433214, upload-time = "2024-10-16T19:44:24.513Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/84c9e23edbccc4a4c6f96a1b8d99dfd2350289e94f00e9ccc7aadde26fb5/httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988", size = 434120, upload-time = "2024-10-16T19:44:26.295Z" }, - { url = "https://files.pythonhosted.org/packages/d0/46/4d8e7ba9581416de1c425b8264e2cadd201eb709ec1584c381f3e98f51c1/httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17", size = 88565, upload-time = "2024-10-16T19:44:29.188Z" }, - { url = "https://files.pythonhosted.org/packages/bb/0e/d0b71465c66b9185f90a091ab36389a7352985fe857e352801c39d6127c8/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2", size = 200683, upload-time = "2024-10-16T19:44:30.175Z" }, - { url = "https://files.pythonhosted.org/packages/e2/b8/412a9bb28d0a8988de3296e01efa0bd62068b33856cdda47fe1b5e890954/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44", size = 104337, upload-time = "2024-10-16T19:44:31.786Z" }, - { url = "https://files.pythonhosted.org/packages/9b/01/6fb20be3196ffdc8eeec4e653bc2a275eca7f36634c86302242c4fbb2760/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1", size = 508796, upload-time = "2024-10-16T19:44:32.825Z" }, - { url = "https://files.pythonhosted.org/packages/f7/d8/b644c44acc1368938317d76ac991c9bba1166311880bcc0ac297cb9d6bd7/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2", size = 510837, upload-time = "2024-10-16T19:44:33.974Z" }, - { url = "https://files.pythonhosted.org/packages/52/d8/254d16a31d543073a0e57f1c329ca7378d8924e7e292eda72d0064987486/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81", size = 485289, upload-time = "2024-10-16T19:44:35.111Z" }, - { url = "https://files.pythonhosted.org/packages/5f/3c/4aee161b4b7a971660b8be71a92c24d6c64372c1ab3ae7f366b3680df20f/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f", size = 489779, upload-time = "2024-10-16T19:44:36.253Z" }, - { url = "https://files.pythonhosted.org/packages/12/b7/5cae71a8868e555f3f67a50ee7f673ce36eac970f029c0c5e9d584352961/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970", size = 88634, upload-time = "2024-10-16T19:44:37.357Z" }, - { url = "https://files.pythonhosted.org/packages/94/a3/9fe9ad23fd35f7de6b91eeb60848986058bd8b5a5c1e256f5860a160cc3e/httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660", size = 197214, upload-time = "2024-10-16T19:44:38.738Z" }, - { url = "https://files.pythonhosted.org/packages/ea/d9/82d5e68bab783b632023f2fa31db20bebb4e89dfc4d2293945fd68484ee4/httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083", size = 102431, upload-time = "2024-10-16T19:44:39.818Z" }, - { url = "https://files.pythonhosted.org/packages/96/c1/cb499655cbdbfb57b577734fde02f6fa0bbc3fe9fb4d87b742b512908dff/httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3", size = 473121, upload-time = "2024-10-16T19:44:41.189Z" }, - { url = "https://files.pythonhosted.org/packages/af/71/ee32fd358f8a3bb199b03261f10921716990808a675d8160b5383487a317/httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071", size = 473805, upload-time = "2024-10-16T19:44:42.384Z" }, - { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858, upload-time = "2024-10-16T19:44:43.959Z" }, - { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042, upload-time = "2024-10-16T19:44:45.071Z" }, - { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682, upload-time = "2024-10-16T19:44:46.46Z" }, -] - -[[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 = "huggingface-hub" -version = "0.33.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "fsspec" }, - { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4b/9e/9366b7349fc125dd68b9d384a0fea84d67b7497753fe92c71b67e13f47c4/huggingface_hub-0.33.4.tar.gz", hash = "sha256:6af13478deae120e765bfd92adad0ae1aec1ad8c439b46f23058ad5956cbca0a", size = 426674, upload-time = "2025-07-11T12:32:48.694Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/7b/98daa50a2db034cab6cd23a3de04fa2358cb691593d28e9130203eb7a805/huggingface_hub-0.33.4-py3-none-any.whl", hash = "sha256:09f9f4e7ca62547c70f8b82767eefadd2667f4e116acba2e3e62a5a81815a7bb", size = 515339, upload-time = "2025-07-11T12:32:46.346Z" }, -] - -[[package]] -name = "humanfriendly" -version = "10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyreadline3", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" }, -] - -[[package]] -name = "id" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/22/11/102da08f88412d875fa2f1a9a469ff7ad4c874b0ca6fed0048fe385bdb3d/id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d", size = 15237, upload-time = "2024-12-04T19:53:05.575Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/cb/18326d2d89ad3b0dd143da971e77afd1e6ca6674f1b1c3df4b6bec6279fc/id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658", size = 13611, upload-time = "2024-12-04T19:53:03.02Z" }, -] - -[[package]] -name = "identify" -version = "2.6.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/88/d193a27416618628a5eea64e3223acd800b40749a96ffb322a9b55a49ed1/identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6", size = 99254, upload-time = "2025-05-23T20:37:53.3Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/cd/18f8da995b658420625f7ef13f037be53ae04ec5ad33f9b718240dcfd48c/identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2", size = 99145, upload-time = "2025-05-23T20:37:51.495Z" }, -] - -[[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 = "importlib-metadata" -version = "8.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, -] - -[[package]] -name = "importlib-resources" -version = "6.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, -] - -[[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 = "ipdb" -version = "0.13.13" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "decorator" }, - { name = "ipython", version = "8.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/1b/7e07e7b752017f7693a0f4d41c13e5ca29ce8cbcfdcc1fd6c4ad8c0a27a0/ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726", size = 17042, upload-time = "2023-03-09T15:40:57.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/4c/b075da0092003d9a55cf2ecc1cae9384a1ca4f650d51b00fc59875fe76f6/ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4", size = 12130, upload-time = "2023-03-09T15:40:55.021Z" }, -] - -[[package]] -name = "ipython" -version = "8.38.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, - { name = "decorator", marker = "python_full_version < '3.11'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "jedi", marker = "python_full_version < '3.11'" }, - { name = "matplotlib-inline", marker = "python_full_version < '3.11'" }, - { name = "pexpect", marker = "python_full_version < '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit", marker = "python_full_version < '3.11'" }, - { name = "pygments", marker = "python_full_version < '3.11'" }, - { name = "stack-data", marker = "python_full_version < '3.11'" }, - { name = "traitlets", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e5/61/1810830e8b93c72dcd3c0f150c80a00c3deb229562d9423807ec92c3a539/ipython-8.38.0.tar.gz", hash = "sha256:9cfea8c903ce0867cc2f23199ed8545eb741f3a69420bfcf3743ad1cec856d39", size = 5513996, upload-time = "2026-01-05T10:59:06.901Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/df/db59624f4c71b39717c423409950ac3f2c8b2ce4b0aac843112c7fb3f721/ipython-8.38.0-py3-none-any.whl", hash = "sha256:750162629d800ac65bb3b543a14e7a74b0e88063eac9b92124d4b2aa3f6d8e86", size = 831813, upload-time = "2026-01-05T10:59:04.239Z" }, -] - -[[package]] -name = "ipython" -version = "9.4.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, - { name = "decorator", marker = "python_full_version >= '3.11'" }, - { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" }, - { name = "jedi", marker = "python_full_version >= '3.11'" }, - { name = "matplotlib-inline", marker = "python_full_version >= '3.11'" }, - { name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit", marker = "python_full_version >= '3.11'" }, - { name = "pygments", marker = "python_full_version >= '3.11'" }, - { name = "stack-data", marker = "python_full_version >= '3.11'" }, - { name = "traitlets", marker = "python_full_version >= '3.11'" }, - { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/54/80/406f9e3bde1c1fd9bf5a0be9d090f8ae623e401b7670d8f6fdf2ab679891/ipython-9.4.0.tar.gz", hash = "sha256:c033c6d4e7914c3d9768aabe76bbe87ba1dc66a92a05db6bfa1125d81f2ee270", size = 4385338, upload-time = "2025-07-01T11:11:30.606Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/63/f8/0031ee2b906a15a33d6bfc12dd09c3dfa966b3cb5b284ecfb7549e6ac3c4/ipython-9.4.0-py3-none-any.whl", hash = "sha256:25850f025a446d9b359e8d296ba175a36aedd32e83ca9b5060430fe16801f066", size = 611021, upload-time = "2025-07-01T11:11:27.85Z" }, -] - -[[package]] -name = "ipython-pygments-lexers" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pygments", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, -] - -[[package]] -name = "jaraco-context" -version = "6.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "backports-tarfile", marker = "python_full_version < '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/ad/f3777b81bf0b6e7bc7514a1656d3e637b2e8e15fab2ce3235730b3e7a4e6/jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", size = 13912, upload-time = "2024-08-20T03:39:27.358Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4", size = 6825, upload-time = "2024-08-20T03:39:25.966Z" }, -] - -[[package]] -name = "jaraco-functools" -version = "4.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/1c/831faaaa0f090b711c355c6d8b2abf277c72133aab472b6932b03322294c/jaraco_functools-4.2.1.tar.gz", hash = "sha256:be634abfccabce56fa3053f8c7ebe37b682683a4ee7793670ced17bab0087353", size = 19661, upload-time = "2025-06-21T19:22:03.201Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/fd/179a20f832824514df39a90bb0e5372b314fea99f217f5ab942b10a8a4e8/jaraco_functools-4.2.1-py3-none-any.whl", hash = "sha256:590486285803805f4b1f99c60ca9e94ed348d4added84b74c7a12885561e524e", size = 10349, upload-time = "2025-06-21T19:22:02.039Z" }, -] - -[[package]] -name = "jedi" -version = "0.19.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "parso" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, -] - -[[package]] -name = "jiter" -version = "0.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500", size = 162759, upload-time = "2025-05-18T19:04:59.73Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/7e/4011b5c77bec97cb2b572f566220364e3e21b51c48c5bd9c4a9c26b41b67/jiter-0.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cd2fb72b02478f06a900a5782de2ef47e0396b3e1f7d5aba30daeb1fce66f303", size = 317215, upload-time = "2025-05-18T19:03:04.303Z" }, - { url = "https://files.pythonhosted.org/packages/8a/4f/144c1b57c39692efc7ea7d8e247acf28e47d0912800b34d0ad815f6b2824/jiter-0.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32bb468e3af278f095d3fa5b90314728a6916d89ba3d0ffb726dd9bf7367285e", size = 322814, upload-time = "2025-05-18T19:03:06.433Z" }, - { url = "https://files.pythonhosted.org/packages/63/1f/db977336d332a9406c0b1f0b82be6f71f72526a806cbb2281baf201d38e3/jiter-0.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8b3e0068c26ddedc7abc6fac37da2d0af16b921e288a5a613f4b86f050354f", size = 345237, upload-time = "2025-05-18T19:03:07.833Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1c/aa30a4a775e8a672ad7f21532bdbfb269f0706b39c6ff14e1f86bdd9e5ff/jiter-0.10.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:286299b74cc49e25cd42eea19b72aa82c515d2f2ee12d11392c56d8701f52224", size = 370999, upload-time = "2025-05-18T19:03:09.338Z" }, - { url = "https://files.pythonhosted.org/packages/35/df/f8257abc4207830cb18880781b5f5b716bad5b2a22fb4330cfd357407c5b/jiter-0.10.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ed5649ceeaeffc28d87fb012d25a4cd356dcd53eff5acff1f0466b831dda2a7", size = 491109, upload-time = "2025-05-18T19:03:11.13Z" }, - { url = "https://files.pythonhosted.org/packages/06/76/9e1516fd7b4278aa13a2cc7f159e56befbea9aa65c71586305e7afa8b0b3/jiter-0.10.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2ab0051160cb758a70716448908ef14ad476c3774bd03ddce075f3c1f90a3d6", size = 388608, upload-time = "2025-05-18T19:03:12.911Z" }, - { url = "https://files.pythonhosted.org/packages/6d/64/67750672b4354ca20ca18d3d1ccf2c62a072e8a2d452ac3cf8ced73571ef/jiter-0.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03997d2f37f6b67d2f5c475da4412be584e1cec273c1cfc03d642c46db43f8cf", size = 352454, upload-time = "2025-05-18T19:03:14.741Z" }, - { url = "https://files.pythonhosted.org/packages/96/4d/5c4e36d48f169a54b53a305114be3efa2bbffd33b648cd1478a688f639c1/jiter-0.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c404a99352d839fed80d6afd6c1d66071f3bacaaa5c4268983fc10f769112e90", size = 391833, upload-time = "2025-05-18T19:03:16.426Z" }, - { url = "https://files.pythonhosted.org/packages/0b/de/ce4a6166a78810bd83763d2fa13f85f73cbd3743a325469a4a9289af6dae/jiter-0.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66e989410b6666d3ddb27a74c7e50d0829704ede652fd4c858e91f8d64b403d0", size = 523646, upload-time = "2025-05-18T19:03:17.704Z" }, - { url = "https://files.pythonhosted.org/packages/a2/a6/3bc9acce53466972964cf4ad85efecb94f9244539ab6da1107f7aed82934/jiter-0.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b532d3af9ef4f6374609a3bcb5e05a1951d3bf6190dc6b176fdb277c9bbf15ee", size = 514735, upload-time = "2025-05-18T19:03:19.44Z" }, - { url = "https://files.pythonhosted.org/packages/b4/d8/243c2ab8426a2a4dea85ba2a2ba43df379ccece2145320dfd4799b9633c5/jiter-0.10.0-cp310-cp310-win32.whl", hash = "sha256:da9be20b333970e28b72edc4dff63d4fec3398e05770fb3205f7fb460eb48dd4", size = 210747, upload-time = "2025-05-18T19:03:21.184Z" }, - { url = "https://files.pythonhosted.org/packages/37/7a/8021bd615ef7788b98fc76ff533eaac846322c170e93cbffa01979197a45/jiter-0.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:f59e533afed0c5b0ac3eba20d2548c4a550336d8282ee69eb07b37ea526ee4e5", size = 207484, upload-time = "2025-05-18T19:03:23.046Z" }, - { url = "https://files.pythonhosted.org/packages/1b/dd/6cefc6bd68b1c3c979cecfa7029ab582b57690a31cd2f346c4d0ce7951b6/jiter-0.10.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3bebe0c558e19902c96e99217e0b8e8b17d570906e72ed8a87170bc290b1e978", size = 317473, upload-time = "2025-05-18T19:03:25.942Z" }, - { url = "https://files.pythonhosted.org/packages/be/cf/fc33f5159ce132be1d8dd57251a1ec7a631c7df4bd11e1cd198308c6ae32/jiter-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:558cc7e44fd8e507a236bee6a02fa17199ba752874400a0ca6cd6e2196cdb7dc", size = 321971, upload-time = "2025-05-18T19:03:27.255Z" }, - { url = "https://files.pythonhosted.org/packages/68/a4/da3f150cf1d51f6c472616fb7650429c7ce053e0c962b41b68557fdf6379/jiter-0.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d613e4b379a07d7c8453c5712ce7014e86c6ac93d990a0b8e7377e18505e98d", size = 345574, upload-time = "2025-05-18T19:03:28.63Z" }, - { url = "https://files.pythonhosted.org/packages/84/34/6e8d412e60ff06b186040e77da5f83bc158e9735759fcae65b37d681f28b/jiter-0.10.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f62cf8ba0618eda841b9bf61797f21c5ebd15a7a1e19daab76e4e4b498d515b2", size = 371028, upload-time = "2025-05-18T19:03:30.292Z" }, - { url = "https://files.pythonhosted.org/packages/fb/d9/9ee86173aae4576c35a2f50ae930d2ccb4c4c236f6cb9353267aa1d626b7/jiter-0.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:919d139cdfa8ae8945112398511cb7fca58a77382617d279556b344867a37e61", size = 491083, upload-time = "2025-05-18T19:03:31.654Z" }, - { url = "https://files.pythonhosted.org/packages/d9/2c/f955de55e74771493ac9e188b0f731524c6a995dffdcb8c255b89c6fb74b/jiter-0.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13ddbc6ae311175a3b03bd8994881bc4635c923754932918e18da841632349db", size = 388821, upload-time = "2025-05-18T19:03:33.184Z" }, - { url = "https://files.pythonhosted.org/packages/81/5a/0e73541b6edd3f4aada586c24e50626c7815c561a7ba337d6a7eb0a915b4/jiter-0.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c440ea003ad10927a30521a9062ce10b5479592e8a70da27f21eeb457b4a9c5", size = 352174, upload-time = "2025-05-18T19:03:34.965Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c0/61eeec33b8c75b31cae42be14d44f9e6fe3ac15a4e58010256ac3abf3638/jiter-0.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc347c87944983481e138dea467c0551080c86b9d21de6ea9306efb12ca8f606", size = 391869, upload-time = "2025-05-18T19:03:36.436Z" }, - { url = "https://files.pythonhosted.org/packages/41/22/5beb5ee4ad4ef7d86f5ea5b4509f680a20706c4a7659e74344777efb7739/jiter-0.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:13252b58c1f4d8c5b63ab103c03d909e8e1e7842d302473f482915d95fefd605", size = 523741, upload-time = "2025-05-18T19:03:38.168Z" }, - { url = "https://files.pythonhosted.org/packages/ea/10/768e8818538e5817c637b0df52e54366ec4cebc3346108a4457ea7a98f32/jiter-0.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7d1bbf3c465de4a24ab12fb7766a0003f6f9bce48b8b6a886158c4d569452dc5", size = 514527, upload-time = "2025-05-18T19:03:39.577Z" }, - { url = "https://files.pythonhosted.org/packages/73/6d/29b7c2dc76ce93cbedabfd842fc9096d01a0550c52692dfc33d3cc889815/jiter-0.10.0-cp311-cp311-win32.whl", hash = "sha256:db16e4848b7e826edca4ccdd5b145939758dadf0dc06e7007ad0e9cfb5928ae7", size = 210765, upload-time = "2025-05-18T19:03:41.271Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c9/d394706deb4c660137caf13e33d05a031d734eb99c051142e039d8ceb794/jiter-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c9c1d5f10e18909e993f9641f12fe1c77b3e9b533ee94ffa970acc14ded3812", size = 209234, upload-time = "2025-05-18T19:03:42.918Z" }, - { url = "https://files.pythonhosted.org/packages/6d/b5/348b3313c58f5fbfb2194eb4d07e46a35748ba6e5b3b3046143f3040bafa/jiter-0.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1e274728e4a5345a6dde2d343c8da018b9d4bd4350f5a472fa91f66fda44911b", size = 312262, upload-time = "2025-05-18T19:03:44.637Z" }, - { url = "https://files.pythonhosted.org/packages/9c/4a/6a2397096162b21645162825f058d1709a02965606e537e3304b02742e9b/jiter-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7202ae396446c988cb2a5feb33a543ab2165b786ac97f53b59aafb803fef0744", size = 320124, upload-time = "2025-05-18T19:03:46.341Z" }, - { url = "https://files.pythonhosted.org/packages/2a/85/1ce02cade7516b726dd88f59a4ee46914bf79d1676d1228ef2002ed2f1c9/jiter-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ba7722d6748b6920ed02a8f1726fb4b33e0fd2f3f621816a8b486c66410ab2", size = 345330, upload-time = "2025-05-18T19:03:47.596Z" }, - { url = "https://files.pythonhosted.org/packages/75/d0/bb6b4f209a77190ce10ea8d7e50bf3725fc16d3372d0a9f11985a2b23eff/jiter-0.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:371eab43c0a288537d30e1f0b193bc4eca90439fc08a022dd83e5e07500ed026", size = 369670, upload-time = "2025-05-18T19:03:49.334Z" }, - { url = "https://files.pythonhosted.org/packages/a0/f5/a61787da9b8847a601e6827fbc42ecb12be2c925ced3252c8ffcb56afcaf/jiter-0.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c675736059020365cebc845a820214765162728b51ab1e03a1b7b3abb70f74c", size = 489057, upload-time = "2025-05-18T19:03:50.66Z" }, - { url = "https://files.pythonhosted.org/packages/12/e4/6f906272810a7b21406c760a53aadbe52e99ee070fc5c0cb191e316de30b/jiter-0.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c5867d40ab716e4684858e4887489685968a47e3ba222e44cde6e4a2154f959", size = 389372, upload-time = "2025-05-18T19:03:51.98Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ba/77013b0b8ba904bf3762f11e0129b8928bff7f978a81838dfcc958ad5728/jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395bb9a26111b60141757d874d27fdea01b17e8fac958b91c20128ba8f4acc8a", size = 352038, upload-time = "2025-05-18T19:03:53.703Z" }, - { url = "https://files.pythonhosted.org/packages/67/27/c62568e3ccb03368dbcc44a1ef3a423cb86778a4389e995125d3d1aaa0a4/jiter-0.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6842184aed5cdb07e0c7e20e5bdcfafe33515ee1741a6835353bb45fe5d1bd95", size = 391538, upload-time = "2025-05-18T19:03:55.046Z" }, - { url = "https://files.pythonhosted.org/packages/c0/72/0d6b7e31fc17a8fdce76164884edef0698ba556b8eb0af9546ae1a06b91d/jiter-0.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62755d1bcea9876770d4df713d82606c8c1a3dca88ff39046b85a048566d56ea", size = 523557, upload-time = "2025-05-18T19:03:56.386Z" }, - { url = "https://files.pythonhosted.org/packages/2f/09/bc1661fbbcbeb6244bd2904ff3a06f340aa77a2b94e5a7373fd165960ea3/jiter-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533efbce2cacec78d5ba73a41756beff8431dfa1694b6346ce7af3a12c42202b", size = 514202, upload-time = "2025-05-18T19:03:57.675Z" }, - { url = "https://files.pythonhosted.org/packages/1b/84/5a5d5400e9d4d54b8004c9673bbe4403928a00d28529ff35b19e9d176b19/jiter-0.10.0-cp312-cp312-win32.whl", hash = "sha256:8be921f0cadd245e981b964dfbcd6fd4bc4e254cdc069490416dd7a2632ecc01", size = 211781, upload-time = "2025-05-18T19:03:59.025Z" }, - { url = "https://files.pythonhosted.org/packages/9b/52/7ec47455e26f2d6e5f2ea4951a0652c06e5b995c291f723973ae9e724a65/jiter-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7c7d785ae9dda68c2678532a5a1581347e9c15362ae9f6e68f3fdbfb64f2e49", size = 206176, upload-time = "2025-05-18T19:04:00.305Z" }, - { url = "https://files.pythonhosted.org/packages/2e/b0/279597e7a270e8d22623fea6c5d4eeac328e7d95c236ed51a2b884c54f70/jiter-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0588107ec8e11b6f5ef0e0d656fb2803ac6cf94a96b2b9fc675c0e3ab5e8644", size = 311617, upload-time = "2025-05-18T19:04:02.078Z" }, - { url = "https://files.pythonhosted.org/packages/91/e3/0916334936f356d605f54cc164af4060e3e7094364add445a3bc79335d46/jiter-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cafc4628b616dc32530c20ee53d71589816cf385dd9449633e910d596b1f5c8a", size = 318947, upload-time = "2025-05-18T19:04:03.347Z" }, - { url = "https://files.pythonhosted.org/packages/6a/8e/fd94e8c02d0e94539b7d669a7ebbd2776e51f329bb2c84d4385e8063a2ad/jiter-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520ef6d981172693786a49ff5b09eda72a42e539f14788124a07530f785c3ad6", size = 344618, upload-time = "2025-05-18T19:04:04.709Z" }, - { url = "https://files.pythonhosted.org/packages/6f/b0/f9f0a2ec42c6e9c2e61c327824687f1e2415b767e1089c1d9135f43816bd/jiter-0.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dedfd05937f8fc45d17ebdf298fe7e0c77458232bcb73d9fbbf4c6455f5b3", size = 368829, upload-time = "2025-05-18T19:04:06.912Z" }, - { url = "https://files.pythonhosted.org/packages/e8/57/5bbcd5331910595ad53b9fd0c610392ac68692176f05ae48d6ce5c852967/jiter-0.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc299da7789deacf95f64052d97f75c16d4fc8c4c214a22bf8d859a4288a1c2", size = 491034, upload-time = "2025-05-18T19:04:08.222Z" }, - { url = "https://files.pythonhosted.org/packages/9b/be/c393df00e6e6e9e623a73551774449f2f23b6ec6a502a3297aeeece2c65a/jiter-0.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5161e201172de298a8a1baad95eb85db4fb90e902353b1f6a41d64ea64644e25", size = 388529, upload-time = "2025-05-18T19:04:09.566Z" }, - { url = "https://files.pythonhosted.org/packages/42/3e/df2235c54d365434c7f150b986a6e35f41ebdc2f95acea3036d99613025d/jiter-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2227db6ba93cb3e2bf67c87e594adde0609f146344e8207e8730364db27041", size = 350671, upload-time = "2025-05-18T19:04:10.98Z" }, - { url = "https://files.pythonhosted.org/packages/c6/77/71b0b24cbcc28f55ab4dbfe029f9a5b73aeadaba677843fc6dc9ed2b1d0a/jiter-0.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15acb267ea5e2c64515574b06a8bf393fbfee6a50eb1673614aa45f4613c0cca", size = 390864, upload-time = "2025-05-18T19:04:12.722Z" }, - { url = "https://files.pythonhosted.org/packages/6a/d3/ef774b6969b9b6178e1d1e7a89a3bd37d241f3d3ec5f8deb37bbd203714a/jiter-0.10.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:901b92f2e2947dc6dfcb52fd624453862e16665ea909a08398dde19c0731b7f4", size = 522989, upload-time = "2025-05-18T19:04:14.261Z" }, - { url = "https://files.pythonhosted.org/packages/0c/41/9becdb1d8dd5d854142f45a9d71949ed7e87a8e312b0bede2de849388cb9/jiter-0.10.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d0cb9a125d5a3ec971a094a845eadde2db0de85b33c9f13eb94a0c63d463879e", size = 513495, upload-time = "2025-05-18T19:04:15.603Z" }, - { url = "https://files.pythonhosted.org/packages/9c/36/3468e5a18238bdedae7c4d19461265b5e9b8e288d3f86cd89d00cbb48686/jiter-0.10.0-cp313-cp313-win32.whl", hash = "sha256:48a403277ad1ee208fb930bdf91745e4d2d6e47253eedc96e2559d1e6527006d", size = 211289, upload-time = "2025-05-18T19:04:17.541Z" }, - { url = "https://files.pythonhosted.org/packages/7e/07/1c96b623128bcb913706e294adb5f768fb7baf8db5e1338ce7b4ee8c78ef/jiter-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:75f9eb72ecb640619c29bf714e78c9c46c9c4eaafd644bf78577ede459f330d4", size = 205074, upload-time = "2025-05-18T19:04:19.21Z" }, - { url = "https://files.pythonhosted.org/packages/54/46/caa2c1342655f57d8f0f2519774c6d67132205909c65e9aa8255e1d7b4f4/jiter-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:28ed2a4c05a1f32ef0e1d24c2611330219fed727dae01789f4a335617634b1ca", size = 318225, upload-time = "2025-05-18T19:04:20.583Z" }, - { url = "https://files.pythonhosted.org/packages/43/84/c7d44c75767e18946219ba2d703a5a32ab37b0bc21886a97bc6062e4da42/jiter-0.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a4c418b1ec86a195f1ca69da8b23e8926c752b685af665ce30777233dfe070", size = 350235, upload-time = "2025-05-18T19:04:22.363Z" }, - { url = "https://files.pythonhosted.org/packages/01/16/f5a0135ccd968b480daad0e6ab34b0c7c5ba3bc447e5088152696140dcb3/jiter-0.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d7bfed2fe1fe0e4dda6ef682cee888ba444b21e7a6553e03252e4feb6cf0adca", size = 207278, upload-time = "2025-05-18T19:04:23.627Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9b/1d646da42c3de6c2188fdaa15bce8ecb22b635904fc68be025e21249ba44/jiter-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e9251a5e83fab8d87799d3e1a46cb4b7f2919b895c6f4483629ed2446f66522", size = 310866, upload-time = "2025-05-18T19:04:24.891Z" }, - { url = "https://files.pythonhosted.org/packages/ad/0e/26538b158e8a7c7987e94e7aeb2999e2e82b1f9d2e1f6e9874ddf71ebda0/jiter-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:023aa0204126fe5b87ccbcd75c8a0d0261b9abdbbf46d55e7ae9f8e22424eeb8", size = 318772, upload-time = "2025-05-18T19:04:26.161Z" }, - { url = "https://files.pythonhosted.org/packages/7b/fb/d302893151caa1c2636d6574d213e4b34e31fd077af6050a9c5cbb42f6fb/jiter-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c189c4f1779c05f75fc17c0c1267594ed918996a231593a21a5ca5438445216", size = 344534, upload-time = "2025-05-18T19:04:27.495Z" }, - { url = "https://files.pythonhosted.org/packages/01/d8/5780b64a149d74e347c5128d82176eb1e3241b1391ac07935693466d6219/jiter-0.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15720084d90d1098ca0229352607cd68256c76991f6b374af96f36920eae13c4", size = 369087, upload-time = "2025-05-18T19:04:28.896Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5b/f235a1437445160e777544f3ade57544daf96ba7e96c1a5b24a6f7ac7004/jiter-0.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f2fb68e5f1cfee30e2b2a09549a00683e0fde4c6a2ab88c94072fc33cb7426", size = 490694, upload-time = "2025-05-18T19:04:30.183Z" }, - { url = "https://files.pythonhosted.org/packages/85/a9/9c3d4617caa2ff89cf61b41e83820c27ebb3f7b5fae8a72901e8cd6ff9be/jiter-0.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce541693355fc6da424c08b7edf39a2895f58d6ea17d92cc2b168d20907dee12", size = 388992, upload-time = "2025-05-18T19:04:32.028Z" }, - { url = "https://files.pythonhosted.org/packages/68/b1/344fd14049ba5c94526540af7eb661871f9c54d5f5601ff41a959b9a0bbd/jiter-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31c50c40272e189d50006ad5c73883caabb73d4e9748a688b216e85a9a9ca3b9", size = 351723, upload-time = "2025-05-18T19:04:33.467Z" }, - { url = "https://files.pythonhosted.org/packages/41/89/4c0e345041186f82a31aee7b9d4219a910df672b9fef26f129f0cda07a29/jiter-0.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa3402a2ff9815960e0372a47b75c76979d74402448509ccd49a275fa983ef8a", size = 392215, upload-time = "2025-05-18T19:04:34.827Z" }, - { url = "https://files.pythonhosted.org/packages/55/58/ee607863e18d3f895feb802154a2177d7e823a7103f000df182e0f718b38/jiter-0.10.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:1956f934dca32d7bb647ea21d06d93ca40868b505c228556d3373cbd255ce853", size = 522762, upload-time = "2025-05-18T19:04:36.19Z" }, - { url = "https://files.pythonhosted.org/packages/15/d0/9123fb41825490d16929e73c212de9a42913d68324a8ce3c8476cae7ac9d/jiter-0.10.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:fcedb049bdfc555e261d6f65a6abe1d5ad68825b7202ccb9692636c70fcced86", size = 513427, upload-time = "2025-05-18T19:04:37.544Z" }, - { url = "https://files.pythonhosted.org/packages/d8/b3/2bd02071c5a2430d0b70403a34411fc519c2f227da7b03da9ba6a956f931/jiter-0.10.0-cp314-cp314-win32.whl", hash = "sha256:ac509f7eccca54b2a29daeb516fb95b6f0bd0d0d8084efaf8ed5dfc7b9f0b357", size = 210127, upload-time = "2025-05-18T19:04:38.837Z" }, - { url = "https://files.pythonhosted.org/packages/03/0c/5fe86614ea050c3ecd728ab4035534387cd41e7c1855ef6c031f1ca93e3f/jiter-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5ed975b83a2b8639356151cef5c0d597c68376fc4922b45d0eb384ac058cfa00", size = 318527, upload-time = "2025-05-18T19:04:40.612Z" }, - { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213, upload-time = "2025-05-18T19:04:41.894Z" }, -] - -[[package]] -name = "joblib" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/fe/0f5a938c54105553436dbff7a61dc4fed4b1b2c98852f8833beaf4d5968f/joblib-1.5.1.tar.gz", hash = "sha256:f4f86e351f39fe3d0d32a9f2c3d8af1ee4cec285aafcb27003dda5205576b444", size = 330475, upload-time = "2025-05-23T12:04:37.097Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/4f/1195bbac8e0c2acc5f740661631d8d750dc38d4a32b23ee5df3cde6f4e0d/joblib-1.5.1-py3-none-any.whl", hash = "sha256:4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a", size = 307746, upload-time = "2025-05-23T12:04:35.124Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.25.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d5/00/a297a868e9d0784450faa7365c2172a7d6110c763e30ba861867c32ae6a9/jsonschema-4.25.0.tar.gz", hash = "sha256:e63acf5c11762c0e6672ffb61482bdf57f0876684d8d249c0fe2d730d48bc55f", size = 356830, upload-time = "2025-07-18T15:39:45.11Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/54/c86cd8e011fe98803d7e382fd67c0df5ceab8d2b7ad8c5a81524f791551c/jsonschema-4.25.0-py3-none-any.whl", hash = "sha256:24c2e8da302de79c8b9382fee3e76b355e44d2a4364bb207159ce10b517bd716", size = 89184, upload-time = "2025-07-18T15:39:42.956Z" }, -] - -[[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 = "keyring" -version = "25.6.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/70/09/d904a6e96f76ff214be59e7aa6ef7190008f52a0ab6689760a98de0bf37d/keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66", size = 62750, upload-time = "2024-12-25T15:26:45.782Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/32/da7f44bcb1105d3e88a0b74ebdca50c59121d2ddf71c9e34ba47df7f3a56/keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd", size = 39085, upload-time = "2024-12-25T15:26:44.377Z" }, -] - -[[package]] -name = "kubernetes" -version = "33.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "durationpy" }, - { name = "google-auth" }, - { name = "oauthlib" }, - { name = "python-dateutil" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "requests-oauthlib" }, - { name = "six" }, - { name = "urllib3" }, - { name = "websocket-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ae/52/19ebe8004c243fdfa78268a96727c71e08f00ff6fe69a301d0b7fcbce3c2/kubernetes-33.1.0.tar.gz", hash = "sha256:f64d829843a54c251061a8e7a14523b521f2dc5c896cf6d65ccf348648a88993", size = 1036779, upload-time = "2025-06-09T21:57:58.521Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/43/d9bebfc3db7dea6ec80df5cb2aad8d274dd18ec2edd6c4f21f32c237cbbb/kubernetes-33.1.0-py2.py3-none-any.whl", hash = "sha256:544de42b24b64287f7e0aa9513c93cb503f7f40eea39b20f66810011a86eabc5", size = 1941335, upload-time = "2025-06-09T21:57:56.327Z" }, -] - -[[package]] -name = "lancedb" -version = "0.24.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "deprecation" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "overrides" }, - { name = "packaging" }, - { name = "pyarrow" }, - { name = "pydantic" }, - { name = "tqdm" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/4a/cbdb6b7a8ca621282c3d9dedae00b372c09b430c69fc0ac149b5b9092b6c/lancedb-0.24.1-cp39-abi3-macosx_10_15_x86_64.whl", hash = "sha256:ae5f028920222ad325521fb447558e274eb92dfd7c189f5875dc3bcc7de07ea6", size = 32792946, upload-time = "2025-07-10T22:21:44.578Z" }, - { url = "https://files.pythonhosted.org/packages/71/90/7c5218b5d81382901680bb365bb55f92fefa28434c049ec6236be73b7ac1/lancedb-0.24.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:baf1eba0e2a8762753dba466e92792a4a21ec504612125ec1d8edd6c15b17eba", size = 30290214, upload-time = "2025-07-10T22:25:47.422Z" }, - { url = "https://files.pythonhosted.org/packages/e1/01/b184e8f1e94e27b9297778dfde65259a94994138d7d4330334bfdf5756e1/lancedb-0.24.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc20c56936d8198330a5dee2f1a17dc1f2145a7b48f81bc32193ca16f3907f3b", size = 31147217, upload-time = "2025-07-10T21:55:21.461Z" }, - { url = "https://files.pythonhosted.org/packages/4e/02/7e67ea8e49757e42251df4e665699fe4d0962f336e3d113ebff84f22bee9/lancedb-0.24.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2509810c743e094d2d900fdc7b0f2bc3cc52a970ecdfb5d404c22b8b8da14cc", size = 34315305, upload-time = "2025-07-10T21:59:31.539Z" }, - { url = "https://files.pythonhosted.org/packages/46/b9/770c17793062dacaf52c5641af706cffca6ef803fbe80422d7948fc4a0cb/lancedb-0.24.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2fbbdf6a6e6189fc3d026677a303a1b7e0bdfe9b690cfee93c586f6b76eb10ba", size = 31157887, upload-time = "2025-07-10T21:55:33.138Z" }, - { url = "https://files.pythonhosted.org/packages/9c/ef/f896a8cabf99bc87e8bdc49df0bd08db09a86e8f333312c15375da21921f/lancedb-0.24.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:e8ef48eaa8c6c2093f40cbae4968c1fa9126934022b4d6462c5a019688731597", size = 34354984, upload-time = "2025-07-10T22:00:26.294Z" }, - { url = "https://files.pythonhosted.org/packages/b9/a2/0ab0979ac987313e2dd9b52fddb98eae9cb048e0faebca7a5f9e0a352ea5/lancedb-0.24.1-cp39-abi3-win_amd64.whl", hash = "sha256:091d1757776fd7a0d7adbc5d507f4356e9f479c38a0446009724d8e52d66cbb3", size = 36228285, upload-time = "2025-07-10T22:16:45.817Z" }, -] - -[[package]] -name = "litellm" -version = "1.74.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "click" }, - { name = "httpx" }, - { name = "importlib-metadata" }, - { name = "jinja2" }, - { name = "jsonschema" }, - { name = "openai" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "tiktoken" }, - { name = "tokenizers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/4c/e8ffbd01d0f43357315646890524ce53648a3962169498e08a4b8edca6e2/litellm-1.74.7.tar.gz", hash = "sha256:53b809a342154d8543ea96422cf962cd5ea9df293f83dab0cc63b27baadf0ece", size = 9587483, upload-time = "2025-07-20T01:03:11.853Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/54/eb5fee089d3e5e07a6d60b2565f798c66d43f46ba8f339e77f78cee98462/litellm-1.74.7-py3-none-any.whl", hash = "sha256:d630785faf07813cf0d5e9fb0bb84aaa18aa728297858c58c56f34c0b9190df1", size = 8652488, upload-time = "2025-07-20T01:03:09.226Z" }, -] - -[[package]] -name = "loguru" -version = "0.7.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "win32-setctime", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, -] - -[[package]] -name = "markdown" -version = "3.8.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/c2/4ab49206c17f75cb08d6311171f2d65798988db4360c4d1485bd0eedd67c/markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45", size = 362071, upload-time = "2025-06-19T17:12:44.483Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/2b/34cc11786bc00d0f04d0f5fdc3a2b1ae0b6239eef72d3d345805f9ad92a1/markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24", size = 106827, upload-time = "2025-06-19T17:12:42.994Z" }, -] - -[[package]] -name = "markdown-it-py" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, -] - -[[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 = "matplotlib-inline" -version = "0.1.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159, upload-time = "2024-04-15T13:44:44.803Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899, upload-time = "2024-04-15T13:44:43.265Z" }, -] - -[[package]] -name = "mcp" -version = "1.12.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/5c/5a/16cef13b2e60d5f865fbc96372efb23dc8b0591f102dd55003b4ae62f9b1/mcp-1.12.1.tar.gz", hash = "sha256:d1d0bdeb09e4b17c1a72b356248bf3baf75ab10db7008ef865c4afbeb0eb810e", size = 425768, upload-time = "2025-07-22T16:51:41.66Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/04/9a967a575518fc958bda1e34a52eae0c7f6accf3534811914fdaf57b0689/mcp-1.12.1-py3-none-any.whl", hash = "sha256:34147f62891417f8b000c39718add844182ba424c8eb2cea250b4267bda4b08b", size = 158463, upload-time = "2025-07-22T16:51:40.086Z" }, -] - -[[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 = "mergedeep" -version = "1.3.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, -] - -[[package]] -name = "mkdocs" -version = "1.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "ghp-import" }, - { name = "jinja2" }, - { name = "markdown" }, - { name = "markupsafe" }, - { name = "mergedeep" }, - { name = "mkdocs-get-deps" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "pyyaml" }, - { name = "pyyaml-env-tag" }, - { name = "watchdog" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, -] - -[[package]] -name = "mkdocs-autorefs" -version = "1.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown" }, - { name = "markupsafe" }, - { name = "mkdocs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/47/0c/c9826f35b99c67fa3a7cddfa094c1a6c43fafde558c309c6e4403e5b37dc/mkdocs_autorefs-1.4.2.tar.gz", hash = "sha256:e2ebe1abd2b67d597ed19378c0fff84d73d1dbce411fce7a7cc6f161888b6749", size = 54961, upload-time = "2025-05-20T13:09:09.886Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/dc/fc063b78f4b769d1956319351704e23ebeba1e9e1d6a41b4b602325fd7e4/mkdocs_autorefs-1.4.2-py3-none-any.whl", hash = "sha256:83d6d777b66ec3c372a1aad4ae0cf77c243ba5bcda5bf0c6b8a2c5e7a3d89f13", size = 24969, upload-time = "2025-05-20T13:09:08.237Z" }, -] - -[[package]] -name = "mkdocs-get-deps" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mergedeep" }, - { name = "platformdirs" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239, upload-time = "2023-11-20T17:51:09.981Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521, upload-time = "2023-11-20T17:51:08.587Z" }, -] - -[[package]] -name = "mkdocs-material" -version = "9.6.15" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "babel" }, - { name = "backrefs" }, - { name = "colorama" }, - { name = "jinja2" }, - { name = "markdown" }, - { name = "mkdocs" }, - { name = "mkdocs-material-extensions" }, - { name = "paginate" }, - { name = "pygments" }, - { name = "pymdown-extensions" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/c1/f804ba2db2ddc2183e900befe7dad64339a34fa935034e1ab405289d0a97/mkdocs_material-9.6.15.tar.gz", hash = "sha256:64adf8fa8dba1a17905b6aee1894a5aafd966d4aeb44a11088519b0f5ca4f1b5", size = 3951836, upload-time = "2025-07-01T10:14:15.671Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/30/dda19f0495a9096b64b6b3c07c4bfcff1c76ee0fc521086d53593f18b4c0/mkdocs_material-9.6.15-py3-none-any.whl", hash = "sha256:ac969c94d4fe5eb7c924b6d2f43d7db41159ea91553d18a9afc4780c34f2717a", size = 8716840, upload-time = "2025-07-01T10:14:13.18Z" }, -] - -[[package]] -name = "mkdocs-material-extensions" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" }, -] - -[[package]] -name = "mkdocstrings" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "markdown" }, - { name = "markupsafe" }, - { name = "mkdocs" }, - { name = "mkdocs-autorefs" }, - { name = "pymdown-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e2/0a/7e4776217d4802009c8238c75c5345e23014a4706a8414a62c0498858183/mkdocstrings-0.30.0.tar.gz", hash = "sha256:5d8019b9c31ddacd780b6784ffcdd6f21c408f34c0bd1103b5351d609d5b4444", size = 106597, upload-time = "2025-07-22T23:48:45.998Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/b4/3c5eac68f31e124a55d255d318c7445840fa1be55e013f507556d6481913/mkdocstrings-0.30.0-py3-none-any.whl", hash = "sha256:ae9e4a0d8c1789697ac776f2e034e2ddd71054ae1cf2c2bb1433ccfd07c226f2", size = 36579, upload-time = "2025-07-22T23:48:44.152Z" }, -] - -[package.optional-dependencies] -python = [ - { name = "mkdocstrings-python" }, -] - -[[package]] -name = "mkdocstrings-python" -version = "1.16.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "griffe" }, - { name = "mkdocs-autorefs" }, - { name = "mkdocstrings" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bf/ed/b886f8c714fd7cccc39b79646b627dbea84cd95c46be43459ef46852caf0/mkdocstrings_python-1.16.12.tar.gz", hash = "sha256:9b9eaa066e0024342d433e332a41095c4e429937024945fea511afe58f63175d", size = 206065, upload-time = "2025-06-03T12:52:49.276Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/dd/a24ee3de56954bfafb6ede7cd63c2413bb842cc48eb45e41c43a05a33074/mkdocstrings_python-1.16.12-py3-none-any.whl", hash = "sha256:22ded3a63b3d823d57457a70ff9860d5a4de9e8b1e482876fc9baabaf6f5f374", size = 124287, upload-time = "2025-06-03T12:52:47.819Z" }, -] - -[[package]] -name = "mmh3" -version = "5.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/1b/1fc6888c74cbd8abad1292dde2ddfcf8fc059e114c97dd6bf16d12f36293/mmh3-5.1.0.tar.gz", hash = "sha256:136e1e670500f177f49ec106a4ebf0adf20d18d96990cc36ea492c651d2b406c", size = 33728, upload-time = "2025-01-25T08:39:43.386Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/01/9d06468928661765c0fc248a29580c760a4a53a9c6c52cf72528bae3582e/mmh3-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eaf4ac5c6ee18ca9232238364d7f2a213278ae5ca97897cafaa123fcc7bb8bec", size = 56095, upload-time = "2025-01-25T08:37:53.621Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/7b39307fc9db867b2a9a20c58b0de33b778dd6c55e116af8ea031f1433ba/mmh3-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:48f9aa8ccb9ad1d577a16104834ac44ff640d8de8c0caed09a2300df7ce8460a", size = 40512, upload-time = "2025-01-25T08:37:54.972Z" }, - { url = "https://files.pythonhosted.org/packages/4f/85/728ca68280d8ccc60c113ad119df70ff1748fbd44c89911fed0501faf0b8/mmh3-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d4ba8cac21e1f2d4e436ce03a82a7f87cda80378691f760e9ea55045ec480a3d", size = 40110, upload-time = "2025-01-25T08:37:57.86Z" }, - { url = "https://files.pythonhosted.org/packages/e4/96/beaf0e301472ffa00358bbbf771fe2d9c4d709a2fe30b1d929e569f8cbdf/mmh3-5.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d69281c281cb01994f054d862a6bb02a2e7acfe64917795c58934b0872b9ece4", size = 100151, upload-time = "2025-01-25T08:37:59.609Z" }, - { url = "https://files.pythonhosted.org/packages/c3/ee/9381f825c4e09ffafeffa213c3865c4bf7d39771640de33ab16f6faeb854/mmh3-5.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d05ed3962312fbda2a1589b97359d2467f677166952f6bd410d8c916a55febf", size = 106312, upload-time = "2025-01-25T08:38:02.102Z" }, - { url = "https://files.pythonhosted.org/packages/67/dc/350a54bea5cf397d357534198ab8119cfd0d8e8bad623b520f9c290af985/mmh3-5.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78ae6a03f4cff4aa92ddd690611168856f8c33a141bd3e5a1e0a85521dc21ea0", size = 104232, upload-time = "2025-01-25T08:38:03.852Z" }, - { url = "https://files.pythonhosted.org/packages/b2/5d/2c6eb4a4ec2f7293b98a9c07cb8c64668330b46ff2b6511244339e69a7af/mmh3-5.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95f983535b39795d9fb7336438faae117424c6798f763d67c6624f6caf2c4c01", size = 91663, upload-time = "2025-01-25T08:38:06.24Z" }, - { url = "https://files.pythonhosted.org/packages/f1/ac/17030d24196f73ecbab8b5033591e5e0e2beca103181a843a135c78f4fee/mmh3-5.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d46fdd80d4c7ecadd9faa6181e92ccc6fe91c50991c9af0e371fdf8b8a7a6150", size = 99166, upload-time = "2025-01-25T08:38:07.988Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ed/54ddc56603561a10b33da9b12e95a48a271d126f4a4951841bbd13145ebf/mmh3-5.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0f16e976af7365ea3b5c425124b2a7f0147eed97fdbb36d99857f173c8d8e096", size = 101555, upload-time = "2025-01-25T08:38:09.821Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c3/33fb3a940c9b70908a5cc9fcc26534aff8698180f9f63ab6b7cc74da8bcd/mmh3-5.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6fa97f7d1e1f74ad1565127229d510f3fd65d931fdedd707c1e15100bc9e5ebb", size = 94813, upload-time = "2025-01-25T08:38:11.682Z" }, - { url = "https://files.pythonhosted.org/packages/61/88/c9ff76a23abe34db8eee1a6fa4e449462a16c7eb547546fc5594b0860a72/mmh3-5.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4052fa4a8561bd62648e9eb993c8f3af3bdedadf3d9687aa4770d10e3709a80c", size = 109611, upload-time = "2025-01-25T08:38:12.602Z" }, - { url = "https://files.pythonhosted.org/packages/0b/8e/27d04f40e95554ebe782cac7bddda2d158cf3862387298c9c7b254fa7beb/mmh3-5.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3f0e8ae9f961037f812afe3cce7da57abf734285961fffbeff9a4c011b737732", size = 100515, upload-time = "2025-01-25T08:38:16.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/00/504ca8f462f01048f3c87cd93f2e1f60b93dac2f930cd4ed73532a9337f5/mmh3-5.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:99297f207db967814f1f02135bb7fe7628b9eacb046134a34e1015b26b06edce", size = 100177, upload-time = "2025-01-25T08:38:18.186Z" }, - { url = "https://files.pythonhosted.org/packages/6f/1d/2efc3525fe6fdf8865972fcbb884bd1f4b0f923c19b80891cecf7e239fa5/mmh3-5.1.0-cp310-cp310-win32.whl", hash = "sha256:2e6c8dc3631a5e22007fbdb55e993b2dbce7985c14b25b572dd78403c2e79182", size = 40815, upload-time = "2025-01-25T08:38:19.176Z" }, - { url = "https://files.pythonhosted.org/packages/38/b5/c8fbe707cb0fea77a6d2d58d497bc9b67aff80deb84d20feb34d8fdd8671/mmh3-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:e4e8c7ad5a4dddcfde35fd28ef96744c1ee0f9d9570108aa5f7e77cf9cfdf0bf", size = 41479, upload-time = "2025-01-25T08:38:21.098Z" }, - { url = "https://files.pythonhosted.org/packages/a1/f1/663e16134f913fccfbcea5b300fb7dc1860d8f63dc71867b013eebc10aec/mmh3-5.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:45da549269883208912868a07d0364e1418d8292c4259ca11699ba1b2475bd26", size = 38883, upload-time = "2025-01-25T08:38:22.013Z" }, - { url = "https://files.pythonhosted.org/packages/56/09/fda7af7fe65928262098382e3bf55950cfbf67d30bf9e47731bf862161e9/mmh3-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b529dcda3f951ff363a51d5866bc6d63cf57f1e73e8961f864ae5010647079d", size = 56098, upload-time = "2025-01-25T08:38:22.917Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ab/84c7bc3f366d6f3bd8b5d9325a10c367685bc17c26dac4c068e2001a4671/mmh3-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db1079b3ace965e562cdfc95847312f9273eb2ad3ebea983435c8423e06acd7", size = 40513, upload-time = "2025-01-25T08:38:25.079Z" }, - { url = "https://files.pythonhosted.org/packages/4f/21/25ea58ca4a652bdc83d1528bec31745cce35802381fb4fe3c097905462d2/mmh3-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:22d31e3a0ff89b8eb3b826d6fc8e19532998b2aa6b9143698043a1268da413e1", size = 40112, upload-time = "2025-01-25T08:38:25.947Z" }, - { url = "https://files.pythonhosted.org/packages/bd/78/4f12f16ae074ddda6f06745254fdb50f8cf3c85b0bbf7eaca58bed84bf58/mmh3-5.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2139bfbd354cd6cb0afed51c4b504f29bcd687a3b1460b7e89498329cc28a894", size = 102632, upload-time = "2025-01-25T08:38:26.939Z" }, - { url = "https://files.pythonhosted.org/packages/48/11/8f09dc999cf2a09b6138d8d7fc734efb7b7bfdd9adb9383380941caadff0/mmh3-5.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c8105c6a435bc2cd6ea2ef59558ab1a2976fd4a4437026f562856d08996673a", size = 108884, upload-time = "2025-01-25T08:38:29.159Z" }, - { url = "https://files.pythonhosted.org/packages/bd/91/e59a66538a3364176f6c3f7620eee0ab195bfe26f89a95cbcc7a1fb04b28/mmh3-5.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57730067174a7f36fcd6ce012fe359bd5510fdaa5fe067bc94ed03e65dafb769", size = 106835, upload-time = "2025-01-25T08:38:33.04Z" }, - { url = "https://files.pythonhosted.org/packages/25/14/b85836e21ab90e5cddb85fe79c494ebd8f81d96a87a664c488cc9277668b/mmh3-5.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bde80eb196d7fdc765a318604ded74a4378f02c5b46c17aa48a27d742edaded2", size = 93688, upload-time = "2025-01-25T08:38:34.987Z" }, - { url = "https://files.pythonhosted.org/packages/ac/aa/8bc964067df9262740c95e4cde2d19f149f2224f426654e14199a9e47df6/mmh3-5.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9c8eddcb441abddeb419c16c56fd74b3e2df9e57f7aa2903221996718435c7a", size = 101569, upload-time = "2025-01-25T08:38:35.983Z" }, - { url = "https://files.pythonhosted.org/packages/70/b6/1fb163cbf919046a64717466c00edabebece3f95c013853fec76dbf2df92/mmh3-5.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:99e07e4acafbccc7a28c076a847fb060ffc1406036bc2005acb1b2af620e53c3", size = 98483, upload-time = "2025-01-25T08:38:38.198Z" }, - { url = "https://files.pythonhosted.org/packages/70/49/ba64c050dd646060f835f1db6b2cd60a6485f3b0ea04976e7a29ace7312e/mmh3-5.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9e25ba5b530e9a7d65f41a08d48f4b3fedc1e89c26486361166a5544aa4cad33", size = 96496, upload-time = "2025-01-25T08:38:39.257Z" }, - { url = "https://files.pythonhosted.org/packages/9e/07/f2751d6a0b535bb865e1066e9c6b80852571ef8d61bce7eb44c18720fbfc/mmh3-5.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bb9bf7475b4d99156ce2f0cf277c061a17560c8c10199c910a680869a278ddc7", size = 105109, upload-time = "2025-01-25T08:38:40.395Z" }, - { url = "https://files.pythonhosted.org/packages/b7/02/30360a5a66f7abba44596d747cc1e6fb53136b168eaa335f63454ab7bb79/mmh3-5.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a1b0878dd281ea3003368ab53ff6f568e175f1b39f281df1da319e58a19c23a", size = 98231, upload-time = "2025-01-25T08:38:42.141Z" }, - { url = "https://files.pythonhosted.org/packages/8c/60/8526b0c750ff4d7ae1266e68b795f14b97758a1d9fcc19f6ecabf9c55656/mmh3-5.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:25f565093ac8b8aefe0f61f8f95c9a9d11dd69e6a9e9832ff0d293511bc36258", size = 97548, upload-time = "2025-01-25T08:38:43.402Z" }, - { url = "https://files.pythonhosted.org/packages/6d/4c/26e1222aca65769280d5427a1ce5875ef4213449718c8f03958d0bf91070/mmh3-5.1.0-cp311-cp311-win32.whl", hash = "sha256:1e3554d8792387eac73c99c6eaea0b3f884e7130eb67986e11c403e4f9b6d372", size = 40810, upload-time = "2025-01-25T08:38:45.143Z" }, - { url = "https://files.pythonhosted.org/packages/98/d5/424ba95062d1212ea615dc8debc8d57983f2242d5e6b82e458b89a117a1e/mmh3-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8ad777a48197882492af50bf3098085424993ce850bdda406a358b6ab74be759", size = 41476, upload-time = "2025-01-25T08:38:46.029Z" }, - { url = "https://files.pythonhosted.org/packages/bd/08/0315ccaf087ba55bb19a6dd3b1e8acd491e74ce7f5f9c4aaa06a90d66441/mmh3-5.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f29dc4efd99bdd29fe85ed6c81915b17b2ef2cf853abf7213a48ac6fb3eaabe1", size = 38880, upload-time = "2025-01-25T08:38:47.035Z" }, - { url = "https://files.pythonhosted.org/packages/f4/47/e5f452bdf16028bfd2edb4e2e35d0441e4a4740f30e68ccd4cfd2fb2c57e/mmh3-5.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:45712987367cb9235026e3cbf4334670522a97751abfd00b5bc8bfa022c3311d", size = 56152, upload-time = "2025-01-25T08:38:47.902Z" }, - { url = "https://files.pythonhosted.org/packages/60/38/2132d537dc7a7fdd8d2e98df90186c7fcdbd3f14f95502a24ba443c92245/mmh3-5.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b1020735eb35086ab24affbea59bb9082f7f6a0ad517cb89f0fc14f16cea4dae", size = 40564, upload-time = "2025-01-25T08:38:48.839Z" }, - { url = "https://files.pythonhosted.org/packages/c0/2a/c52cf000581bfb8d94794f58865658e7accf2fa2e90789269d4ae9560b16/mmh3-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:babf2a78ce5513d120c358722a2e3aa7762d6071cd10cede026f8b32452be322", size = 40104, upload-time = "2025-01-25T08:38:49.773Z" }, - { url = "https://files.pythonhosted.org/packages/83/33/30d163ce538c54fc98258db5621447e3ab208d133cece5d2577cf913e708/mmh3-5.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4f47f58cd5cbef968c84a7c1ddc192fef0a36b48b0b8a3cb67354531aa33b00", size = 102634, upload-time = "2025-01-25T08:38:51.5Z" }, - { url = "https://files.pythonhosted.org/packages/94/5c/5a18acb6ecc6852be2d215c3d811aa61d7e425ab6596be940877355d7f3e/mmh3-5.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2044a601c113c981f2c1e14fa33adc9b826c9017034fe193e9eb49a6882dbb06", size = 108888, upload-time = "2025-01-25T08:38:52.542Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/11c556324c64a92aa12f28e221a727b6e082e426dc502e81f77056f6fc98/mmh3-5.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c94d999c9f2eb2da44d7c2826d3fbffdbbbbcde8488d353fee7c848ecc42b968", size = 106968, upload-time = "2025-01-25T08:38:54.286Z" }, - { url = "https://files.pythonhosted.org/packages/5d/61/ca0c196a685aba7808a5c00246f17b988a9c4f55c594ee0a02c273e404f3/mmh3-5.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a015dcb24fa0c7a78f88e9419ac74f5001c1ed6a92e70fd1803f74afb26a4c83", size = 93771, upload-time = "2025-01-25T08:38:55.576Z" }, - { url = "https://files.pythonhosted.org/packages/b4/55/0927c33528710085ee77b808d85bbbafdb91a1db7c8eaa89cac16d6c513e/mmh3-5.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:457da019c491a2d20e2022c7d4ce723675e4c081d9efc3b4d8b9f28a5ea789bd", size = 101726, upload-time = "2025-01-25T08:38:56.654Z" }, - { url = "https://files.pythonhosted.org/packages/49/39/a92c60329fa470f41c18614a93c6cd88821412a12ee78c71c3f77e1cfc2d/mmh3-5.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:71408579a570193a4ac9c77344d68ddefa440b00468a0b566dcc2ba282a9c559", size = 98523, upload-time = "2025-01-25T08:38:57.662Z" }, - { url = "https://files.pythonhosted.org/packages/81/90/26adb15345af8d9cf433ae1b6adcf12e0a4cad1e692de4fa9f8e8536c5ae/mmh3-5.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8b3a04bc214a6e16c81f02f855e285c6df274a2084787eeafaa45f2fbdef1b63", size = 96628, upload-time = "2025-01-25T08:38:59.505Z" }, - { url = "https://files.pythonhosted.org/packages/8a/4d/340d1e340df972a13fd4ec84c787367f425371720a1044220869c82364e9/mmh3-5.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:832dae26a35514f6d3c1e267fa48e8de3c7b978afdafa0529c808ad72e13ada3", size = 105190, upload-time = "2025-01-25T08:39:00.483Z" }, - { url = "https://files.pythonhosted.org/packages/d3/7c/65047d1cccd3782d809936db446430fc7758bda9def5b0979887e08302a2/mmh3-5.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bf658a61fc92ef8a48945ebb1076ef4ad74269e353fffcb642dfa0890b13673b", size = 98439, upload-time = "2025-01-25T08:39:01.484Z" }, - { url = "https://files.pythonhosted.org/packages/72/d2/3c259d43097c30f062050f7e861075099404e8886b5d4dd3cebf180d6e02/mmh3-5.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3313577453582b03383731b66447cdcdd28a68f78df28f10d275d7d19010c1df", size = 97780, upload-time = "2025-01-25T08:39:02.444Z" }, - { url = "https://files.pythonhosted.org/packages/29/29/831ea8d4abe96cdb3e28b79eab49cac7f04f9c6b6e36bfc686197ddba09d/mmh3-5.1.0-cp312-cp312-win32.whl", hash = "sha256:1d6508504c531ab86c4424b5a5ff07c1132d063863339cf92f6657ff7a580f76", size = 40835, upload-time = "2025-01-25T08:39:03.369Z" }, - { url = "https://files.pythonhosted.org/packages/12/dd/7cbc30153b73f08eeac43804c1dbc770538a01979b4094edbe1a4b8eb551/mmh3-5.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:aa75981fcdf3f21759d94f2c81b6a6e04a49dfbcdad88b152ba49b8e20544776", size = 41509, upload-time = "2025-01-25T08:39:04.284Z" }, - { url = "https://files.pythonhosted.org/packages/80/9d/627375bab4c90dd066093fc2c9a26b86f87e26d980dbf71667b44cbee3eb/mmh3-5.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:a4c1a76808dfea47f7407a0b07aaff9087447ef6280716fd0783409b3088bb3c", size = 38888, upload-time = "2025-01-25T08:39:05.174Z" }, - { url = "https://files.pythonhosted.org/packages/05/06/a098a42870db16c0a54a82c56a5bdc873de3165218cd5b3ca59dbc0d31a7/mmh3-5.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a523899ca29cfb8a5239618474a435f3d892b22004b91779fcb83504c0d5b8c", size = 56165, upload-time = "2025-01-25T08:39:06.887Z" }, - { url = "https://files.pythonhosted.org/packages/5a/65/eaada79a67fde1f43e1156d9630e2fb70655e1d3f4e8f33d7ffa31eeacfd/mmh3-5.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:17cef2c3a6ca2391ca7171a35ed574b5dab8398163129a3e3a4c05ab85a4ff40", size = 40569, upload-time = "2025-01-25T08:39:07.945Z" }, - { url = "https://files.pythonhosted.org/packages/36/7e/2b6c43ed48be583acd68e34d16f19209a9f210e4669421b0321e326d8554/mmh3-5.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:52e12895b30110f3d89dae59a888683cc886ed0472dd2eca77497edef6161997", size = 40104, upload-time = "2025-01-25T08:39:09.598Z" }, - { url = "https://files.pythonhosted.org/packages/11/2b/1f9e962fdde8e41b0f43d22c8ba719588de8952f9376df7d73a434827590/mmh3-5.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0d6719045cda75c3f40397fc24ab67b18e0cb8f69d3429ab4c39763c4c608dd", size = 102497, upload-time = "2025-01-25T08:39:10.512Z" }, - { url = "https://files.pythonhosted.org/packages/46/94/d6c5c3465387ba077cccdc028ab3eec0d86eed1eebe60dcf4d15294056be/mmh3-5.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d19fa07d303a91f8858982c37e6939834cb11893cb3ff20e6ee6fa2a7563826a", size = 108834, upload-time = "2025-01-25T08:39:11.568Z" }, - { url = "https://files.pythonhosted.org/packages/34/1e/92c212bb81796b69dddfd50a8a8f4b26ab0d38fdaf1d3e8628a67850543b/mmh3-5.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31b47a620d622fbde8ca1ca0435c5d25de0ac57ab507209245e918128e38e676", size = 106936, upload-time = "2025-01-25T08:39:12.638Z" }, - { url = "https://files.pythonhosted.org/packages/f4/41/f2f494bbff3aad5ffd2085506255049de76cde51ddac84058e32768acc79/mmh3-5.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00f810647c22c179b6821079f7aa306d51953ac893587ee09cf1afb35adf87cb", size = 93709, upload-time = "2025-01-25T08:39:14.071Z" }, - { url = "https://files.pythonhosted.org/packages/9e/a9/a2cc4a756d73d9edf4fb85c76e16fd56b0300f8120fd760c76b28f457730/mmh3-5.1.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6128b610b577eed1e89ac7177ab0c33d06ade2aba93f5c89306032306b5f1c6", size = 101623, upload-time = "2025-01-25T08:39:15.507Z" }, - { url = "https://files.pythonhosted.org/packages/5e/6f/b9d735533b6a56b2d56333ff89be6a55ac08ba7ff33465feb131992e33eb/mmh3-5.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1e550a45d2ff87a1c11b42015107f1778c93f4c6f8e731bf1b8fa770321b8cc4", size = 98521, upload-time = "2025-01-25T08:39:16.77Z" }, - { url = "https://files.pythonhosted.org/packages/99/47/dff2b54fac0d421c1e6ecbd2d9c85b2d0e6f6ee0d10b115d9364116a511e/mmh3-5.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:785ae09276342f79fd8092633e2d52c0f7c44d56e8cfda8274ccc9b76612dba2", size = 96696, upload-time = "2025-01-25T08:39:17.805Z" }, - { url = "https://files.pythonhosted.org/packages/be/43/9e205310f47c43ddf1575bb3a1769c36688f30f1ac105e0f0c878a29d2cd/mmh3-5.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0f4be3703a867ef976434afd3661a33884abe73ceb4ee436cac49d3b4c2aaa7b", size = 105234, upload-time = "2025-01-25T08:39:18.908Z" }, - { url = "https://files.pythonhosted.org/packages/6b/44/90b11fd2b67dcb513f5bfe9b476eb6ca2d5a221c79b49884dc859100905e/mmh3-5.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e513983830c4ff1f205ab97152a0050cf7164f1b4783d702256d39c637b9d107", size = 98449, upload-time = "2025-01-25T08:39:20.719Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d0/25c4b0c7b8e49836541059b28e034a4cccd0936202800d43a1cc48495ecb/mmh3-5.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9135c300535c828c0bae311b659f33a31c941572eae278568d1a953c4a57b59", size = 97796, upload-time = "2025-01-25T08:39:22.453Z" }, - { url = "https://files.pythonhosted.org/packages/23/fa/cbbb7fcd0e287a715f1cd28a10de94c0535bd94164e38b852abc18da28c6/mmh3-5.1.0-cp313-cp313-win32.whl", hash = "sha256:c65dbd12885a5598b70140d24de5839551af5a99b29f9804bb2484b29ef07692", size = 40828, upload-time = "2025-01-25T08:39:23.372Z" }, - { url = "https://files.pythonhosted.org/packages/09/33/9fb90ef822f7b734955a63851907cf72f8a3f9d8eb3c5706bfa6772a2a77/mmh3-5.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:10db7765201fc65003fa998faa067417ef6283eb5f9bba8f323c48fd9c33e91f", size = 41504, upload-time = "2025-01-25T08:39:24.286Z" }, - { url = "https://files.pythonhosted.org/packages/16/71/4ad9a42f2772793a03cb698f0fc42499f04e6e8d2560ba2f7da0fb059a8e/mmh3-5.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:b22fe2e54be81f6c07dcb36b96fa250fb72effe08aa52fbb83eade6e1e2d5fd7", size = 38890, upload-time = "2025-01-25T08:39:25.28Z" }, -] - -[[package]] -name = "more-itertools" -version = "10.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ce/a0/834b0cebabbfc7e311f30b46c8188790a37f89fc8d756660346fe5abfd09/more_itertools-10.7.0.tar.gz", hash = "sha256:9fddd5403be01a94b204faadcff459ec3568cf110265d3c54323e1e866ad29d3", size = 127671, upload-time = "2025-04-22T14:17:41.838Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/9f/7ba6f94fc1e9ac3d2b853fdff3035fb2fa5afbed898c4a72b8a020610594/more_itertools-10.7.0-py3-none-any.whl", hash = "sha256:d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e", size = 65278, upload-time = "2025-04-22T14:17:40.49Z" }, -] - -[[package]] -name = "mpmath" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, -] - -[[package]] -name = "multidict" -version = "6.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/2c/5dad12e82fbdf7470f29bff2171484bf07cb3b16ada60a6589af8f376440/multidict-6.6.3.tar.gz", hash = "sha256:798a9eb12dab0a6c2e29c1de6f3468af5cb2da6053a20dfa3344907eed0937cc", size = 101006, upload-time = "2025-06-30T15:53:46.929Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/67/414933982bce2efce7cbcb3169eaaf901e0f25baec69432b4874dfb1f297/multidict-6.6.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a2be5b7b35271f7fff1397204ba6708365e3d773579fe2a30625e16c4b4ce817", size = 77017, upload-time = "2025-06-30T15:50:58.931Z" }, - { url = "https://files.pythonhosted.org/packages/8a/fe/d8a3ee1fad37dc2ef4f75488b0d9d4f25bf204aad8306cbab63d97bff64a/multidict-6.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12f4581d2930840295c461764b9a65732ec01250b46c6b2c510d7ee68872b140", size = 44897, upload-time = "2025-06-30T15:51:00.999Z" }, - { url = "https://files.pythonhosted.org/packages/1f/e0/265d89af8c98240265d82b8cbcf35897f83b76cd59ee3ab3879050fd8c45/multidict-6.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dd7793bab517e706c9ed9d7310b06c8672fd0aeee5781bfad612f56b8e0f7d14", size = 44574, upload-time = "2025-06-30T15:51:02.449Z" }, - { url = "https://files.pythonhosted.org/packages/e6/05/6b759379f7e8e04ccc97cfb2a5dcc5cdbd44a97f072b2272dc51281e6a40/multidict-6.6.3-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:72d8815f2cd3cf3df0f83cac3f3ef801d908b2d90409ae28102e0553af85545a", size = 225729, upload-time = "2025-06-30T15:51:03.794Z" }, - { url = "https://files.pythonhosted.org/packages/4e/f5/8d5a15488edd9a91fa4aad97228d785df208ed6298580883aa3d9def1959/multidict-6.6.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:531e331a2ee53543ab32b16334e2deb26f4e6b9b28e41f8e0c87e99a6c8e2d69", size = 242515, upload-time = "2025-06-30T15:51:05.002Z" }, - { url = "https://files.pythonhosted.org/packages/6e/b5/a8f317d47d0ac5bb746d6d8325885c8967c2a8ce0bb57be5399e3642cccb/multidict-6.6.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:42ca5aa9329a63be8dc49040f63817d1ac980e02eeddba763a9ae5b4027b9c9c", size = 222224, upload-time = "2025-06-30T15:51:06.148Z" }, - { url = "https://files.pythonhosted.org/packages/76/88/18b2a0d5e80515fa22716556061189c2853ecf2aa2133081ebbe85ebea38/multidict-6.6.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:208b9b9757060b9faa6f11ab4bc52846e4f3c2fb8b14d5680c8aac80af3dc751", size = 253124, upload-time = "2025-06-30T15:51:07.375Z" }, - { url = "https://files.pythonhosted.org/packages/62/bf/ebfcfd6b55a1b05ef16d0775ae34c0fe15e8dab570d69ca9941073b969e7/multidict-6.6.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:acf6b97bd0884891af6a8b43d0f586ab2fcf8e717cbd47ab4bdddc09e20652d8", size = 251529, upload-time = "2025-06-30T15:51:08.691Z" }, - { url = "https://files.pythonhosted.org/packages/44/11/780615a98fd3775fc309d0234d563941af69ade2df0bb82c91dda6ddaea1/multidict-6.6.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:68e9e12ed00e2089725669bdc88602b0b6f8d23c0c95e52b95f0bc69f7fe9b55", size = 241627, upload-time = "2025-06-30T15:51:10.605Z" }, - { url = "https://files.pythonhosted.org/packages/28/3d/35f33045e21034b388686213752cabc3a1b9d03e20969e6fa8f1b1d82db1/multidict-6.6.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:05db2f66c9addb10cfa226e1acb363450fab2ff8a6df73c622fefe2f5af6d4e7", size = 239351, upload-time = "2025-06-30T15:51:12.18Z" }, - { url = "https://files.pythonhosted.org/packages/6e/cc/ff84c03b95b430015d2166d9aae775a3985d757b94f6635010d0038d9241/multidict-6.6.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0db58da8eafb514db832a1b44f8fa7906fdd102f7d982025f816a93ba45e3dcb", size = 233429, upload-time = "2025-06-30T15:51:13.533Z" }, - { url = "https://files.pythonhosted.org/packages/2e/f0/8cd49a0b37bdea673a4b793c2093f2f4ba8e7c9d6d7c9bd672fd6d38cd11/multidict-6.6.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:14117a41c8fdb3ee19c743b1c027da0736fdb79584d61a766da53d399b71176c", size = 243094, upload-time = "2025-06-30T15:51:14.815Z" }, - { url = "https://files.pythonhosted.org/packages/96/19/5d9a0cfdafe65d82b616a45ae950975820289069f885328e8185e64283c2/multidict-6.6.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:877443eaaabcd0b74ff32ebeed6f6176c71850feb7d6a1d2db65945256ea535c", size = 248957, upload-time = "2025-06-30T15:51:16.076Z" }, - { url = "https://files.pythonhosted.org/packages/e6/dc/c90066151da87d1e489f147b9b4327927241e65f1876702fafec6729c014/multidict-6.6.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:70b72e749a4f6e7ed8fb334fa8d8496384840319512746a5f42fa0aec79f4d61", size = 243590, upload-time = "2025-06-30T15:51:17.413Z" }, - { url = "https://files.pythonhosted.org/packages/ec/39/458afb0cccbb0ee9164365273be3e039efddcfcb94ef35924b7dbdb05db0/multidict-6.6.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:43571f785b86afd02b3855c5ac8e86ec921b760298d6f82ff2a61daf5a35330b", size = 237487, upload-time = "2025-06-30T15:51:19.039Z" }, - { url = "https://files.pythonhosted.org/packages/35/38/0016adac3990426610a081787011177e661875546b434f50a26319dc8372/multidict-6.6.3-cp310-cp310-win32.whl", hash = "sha256:20c5a0c3c13a15fd5ea86c42311859f970070e4e24de5a550e99d7c271d76318", size = 41390, upload-time = "2025-06-30T15:51:20.362Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/17897a8f3f2c5363d969b4c635aa40375fe1f09168dc09a7826780bfb2a4/multidict-6.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:ab0a34a007704c625e25a9116c6770b4d3617a071c8a7c30cd338dfbadfe6485", size = 45954, upload-time = "2025-06-30T15:51:21.383Z" }, - { url = "https://files.pythonhosted.org/packages/2d/5f/d4a717c1e457fe44072e33fa400d2b93eb0f2819c4d669381f925b7cba1f/multidict-6.6.3-cp310-cp310-win_arm64.whl", hash = "sha256:769841d70ca8bdd140a715746199fc6473414bd02efd678d75681d2d6a8986c5", size = 42981, upload-time = "2025-06-30T15:51:22.809Z" }, - { url = "https://files.pythonhosted.org/packages/08/f0/1a39863ced51f639c81a5463fbfa9eb4df59c20d1a8769ab9ef4ca57ae04/multidict-6.6.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:18f4eba0cbac3546b8ae31e0bbc55b02c801ae3cbaf80c247fcdd89b456ff58c", size = 76445, upload-time = "2025-06-30T15:51:24.01Z" }, - { url = "https://files.pythonhosted.org/packages/c9/0e/a7cfa451c7b0365cd844e90b41e21fab32edaa1e42fc0c9f68461ce44ed7/multidict-6.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef43b5dd842382329e4797c46f10748d8c2b6e0614f46b4afe4aee9ac33159df", size = 44610, upload-time = "2025-06-30T15:51:25.158Z" }, - { url = "https://files.pythonhosted.org/packages/c6/bb/a14a4efc5ee748cc1904b0748be278c31b9295ce5f4d2ef66526f410b94d/multidict-6.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bd1fd5eec01494e0f2e8e446a74a85d5e49afb63d75a9934e4a5423dba21d", size = 44267, upload-time = "2025-06-30T15:51:26.326Z" }, - { url = "https://files.pythonhosted.org/packages/c2/f8/410677d563c2d55e063ef74fe578f9d53fe6b0a51649597a5861f83ffa15/multidict-6.6.3-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5bd8d6f793a787153956cd35e24f60485bf0651c238e207b9a54f7458b16d539", size = 230004, upload-time = "2025-06-30T15:51:27.491Z" }, - { url = "https://files.pythonhosted.org/packages/fd/df/2b787f80059314a98e1ec6a4cc7576244986df3e56b3c755e6fc7c99e038/multidict-6.6.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bf99b4daf908c73856bd87ee0a2499c3c9a3d19bb04b9c6025e66af3fd07462", size = 247196, upload-time = "2025-06-30T15:51:28.762Z" }, - { url = "https://files.pythonhosted.org/packages/05/f2/f9117089151b9a8ab39f9019620d10d9718eec2ac89e7ca9d30f3ec78e96/multidict-6.6.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b9e59946b49dafaf990fd9c17ceafa62976e8471a14952163d10a7a630413a9", size = 225337, upload-time = "2025-06-30T15:51:30.025Z" }, - { url = "https://files.pythonhosted.org/packages/93/2d/7115300ec5b699faa152c56799b089a53ed69e399c3c2d528251f0aeda1a/multidict-6.6.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e2db616467070d0533832d204c54eea6836a5e628f2cb1e6dfd8cd6ba7277cb7", size = 257079, upload-time = "2025-06-30T15:51:31.716Z" }, - { url = "https://files.pythonhosted.org/packages/15/ea/ff4bab367623e39c20d3b07637225c7688d79e4f3cc1f3b9f89867677f9a/multidict-6.6.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7394888236621f61dcdd25189b2768ae5cc280f041029a5bcf1122ac63df79f9", size = 255461, upload-time = "2025-06-30T15:51:33.029Z" }, - { url = "https://files.pythonhosted.org/packages/74/07/2c9246cda322dfe08be85f1b8739646f2c4c5113a1422d7a407763422ec4/multidict-6.6.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f114d8478733ca7388e7c7e0ab34b72547476b97009d643644ac33d4d3fe1821", size = 246611, upload-time = "2025-06-30T15:51:34.47Z" }, - { url = "https://files.pythonhosted.org/packages/a8/62/279c13d584207d5697a752a66ffc9bb19355a95f7659140cb1b3cf82180e/multidict-6.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cdf22e4db76d323bcdc733514bf732e9fb349707c98d341d40ebcc6e9318ef3d", size = 243102, upload-time = "2025-06-30T15:51:36.525Z" }, - { url = "https://files.pythonhosted.org/packages/69/cc/e06636f48c6d51e724a8bc8d9e1db5f136fe1df066d7cafe37ef4000f86a/multidict-6.6.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e995a34c3d44ab511bfc11aa26869b9d66c2d8c799fa0e74b28a473a692532d6", size = 238693, upload-time = "2025-06-30T15:51:38.278Z" }, - { url = "https://files.pythonhosted.org/packages/89/a4/66c9d8fb9acf3b226cdd468ed009537ac65b520aebdc1703dd6908b19d33/multidict-6.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:766a4a5996f54361d8d5a9050140aa5362fe48ce51c755a50c0bc3706460c430", size = 246582, upload-time = "2025-06-30T15:51:39.709Z" }, - { url = "https://files.pythonhosted.org/packages/cf/01/c69e0317be556e46257826d5449feb4e6aa0d18573e567a48a2c14156f1f/multidict-6.6.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3893a0d7d28a7fe6ca7a1f760593bc13038d1d35daf52199d431b61d2660602b", size = 253355, upload-time = "2025-06-30T15:51:41.013Z" }, - { url = "https://files.pythonhosted.org/packages/c0/da/9cc1da0299762d20e626fe0042e71b5694f9f72d7d3f9678397cbaa71b2b/multidict-6.6.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:934796c81ea996e61914ba58064920d6cad5d99140ac3167901eb932150e2e56", size = 247774, upload-time = "2025-06-30T15:51:42.291Z" }, - { url = "https://files.pythonhosted.org/packages/e6/91/b22756afec99cc31105ddd4a52f95ab32b1a4a58f4d417979c570c4a922e/multidict-6.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9ed948328aec2072bc00f05d961ceadfd3e9bfc2966c1319aeaf7b7c21219183", size = 242275, upload-time = "2025-06-30T15:51:43.642Z" }, - { url = "https://files.pythonhosted.org/packages/be/f1/adcc185b878036a20399d5be5228f3cbe7f823d78985d101d425af35c800/multidict-6.6.3-cp311-cp311-win32.whl", hash = "sha256:9f5b28c074c76afc3e4c610c488e3493976fe0e596dd3db6c8ddfbb0134dcac5", size = 41290, upload-time = "2025-06-30T15:51:45.264Z" }, - { url = "https://files.pythonhosted.org/packages/e0/d4/27652c1c6526ea6b4f5ddd397e93f4232ff5de42bea71d339bc6a6cc497f/multidict-6.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:bc7f6fbc61b1c16050a389c630da0b32fc6d4a3d191394ab78972bf5edc568c2", size = 45942, upload-time = "2025-06-30T15:51:46.377Z" }, - { url = "https://files.pythonhosted.org/packages/16/18/23f4932019804e56d3c2413e237f866444b774b0263bcb81df2fdecaf593/multidict-6.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:d4e47d8faffaae822fb5cba20937c048d4f734f43572e7079298a6c39fb172cb", size = 42880, upload-time = "2025-06-30T15:51:47.561Z" }, - { url = "https://files.pythonhosted.org/packages/0e/a0/6b57988ea102da0623ea814160ed78d45a2645e4bbb499c2896d12833a70/multidict-6.6.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:056bebbeda16b2e38642d75e9e5310c484b7c24e3841dc0fb943206a72ec89d6", size = 76514, upload-time = "2025-06-30T15:51:48.728Z" }, - { url = "https://files.pythonhosted.org/packages/07/7a/d1e92665b0850c6c0508f101f9cf0410c1afa24973e1115fe9c6a185ebf7/multidict-6.6.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e5f481cccb3c5c5e5de5d00b5141dc589c1047e60d07e85bbd7dea3d4580d63f", size = 45394, upload-time = "2025-06-30T15:51:49.986Z" }, - { url = "https://files.pythonhosted.org/packages/52/6f/dd104490e01be6ef8bf9573705d8572f8c2d2c561f06e3826b081d9e6591/multidict-6.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:10bea2ee839a759ee368b5a6e47787f399b41e70cf0c20d90dfaf4158dfb4e55", size = 43590, upload-time = "2025-06-30T15:51:51.331Z" }, - { url = "https://files.pythonhosted.org/packages/44/fe/06e0e01b1b0611e6581b7fd5a85b43dacc08b6cea3034f902f383b0873e5/multidict-6.6.3-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:2334cfb0fa9549d6ce2c21af2bfbcd3ac4ec3646b1b1581c88e3e2b1779ec92b", size = 237292, upload-time = "2025-06-30T15:51:52.584Z" }, - { url = "https://files.pythonhosted.org/packages/ce/71/4f0e558fb77696b89c233c1ee2d92f3e1d5459070a0e89153c9e9e804186/multidict-6.6.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8fee016722550a2276ca2cb5bb624480e0ed2bd49125b2b73b7010b9090e888", size = 258385, upload-time = "2025-06-30T15:51:53.913Z" }, - { url = "https://files.pythonhosted.org/packages/e3/25/cca0e68228addad24903801ed1ab42e21307a1b4b6dd2cf63da5d3ae082a/multidict-6.6.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5511cb35f5c50a2db21047c875eb42f308c5583edf96bd8ebf7d770a9d68f6d", size = 242328, upload-time = "2025-06-30T15:51:55.672Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a3/46f2d420d86bbcb8fe660b26a10a219871a0fbf4d43cb846a4031533f3e0/multidict-6.6.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:712b348f7f449948e0a6c4564a21c7db965af900973a67db432d724619b3c680", size = 268057, upload-time = "2025-06-30T15:51:57.037Z" }, - { url = "https://files.pythonhosted.org/packages/9e/73/1c743542fe00794a2ec7466abd3f312ccb8fad8dff9f36d42e18fb1ec33e/multidict-6.6.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e4e15d2138ee2694e038e33b7c3da70e6b0ad8868b9f8094a72e1414aeda9c1a", size = 269341, upload-time = "2025-06-30T15:51:59.111Z" }, - { url = "https://files.pythonhosted.org/packages/a4/11/6ec9dcbe2264b92778eeb85407d1df18812248bf3506a5a1754bc035db0c/multidict-6.6.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8df25594989aebff8a130f7899fa03cbfcc5d2b5f4a461cf2518236fe6f15961", size = 256081, upload-time = "2025-06-30T15:52:00.533Z" }, - { url = "https://files.pythonhosted.org/packages/9b/2b/631b1e2afeb5f1696846d747d36cda075bfdc0bc7245d6ba5c319278d6c4/multidict-6.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:159ca68bfd284a8860f8d8112cf0521113bffd9c17568579e4d13d1f1dc76b65", size = 253581, upload-time = "2025-06-30T15:52:02.43Z" }, - { url = "https://files.pythonhosted.org/packages/bf/0e/7e3b93f79efeb6111d3bf9a1a69e555ba1d07ad1c11bceb56b7310d0d7ee/multidict-6.6.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e098c17856a8c9ade81b4810888c5ad1914099657226283cab3062c0540b0643", size = 250750, upload-time = "2025-06-30T15:52:04.26Z" }, - { url = "https://files.pythonhosted.org/packages/ad/9e/086846c1d6601948e7de556ee464a2d4c85e33883e749f46b9547d7b0704/multidict-6.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:67c92ed673049dec52d7ed39f8cf9ebbadf5032c774058b4406d18c8f8fe7063", size = 251548, upload-time = "2025-06-30T15:52:06.002Z" }, - { url = "https://files.pythonhosted.org/packages/8c/7b/86ec260118e522f1a31550e87b23542294880c97cfbf6fb18cc67b044c66/multidict-6.6.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:bd0578596e3a835ef451784053cfd327d607fc39ea1a14812139339a18a0dbc3", size = 262718, upload-time = "2025-06-30T15:52:07.707Z" }, - { url = "https://files.pythonhosted.org/packages/8c/bd/22ce8f47abb0be04692c9fc4638508b8340987b18691aa7775d927b73f72/multidict-6.6.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:346055630a2df2115cd23ae271910b4cae40f4e336773550dca4889b12916e75", size = 259603, upload-time = "2025-06-30T15:52:09.58Z" }, - { url = "https://files.pythonhosted.org/packages/07/9c/91b7ac1691be95cd1f4a26e36a74b97cda6aa9820632d31aab4410f46ebd/multidict-6.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:555ff55a359302b79de97e0468e9ee80637b0de1fce77721639f7cd9440b3a10", size = 251351, upload-time = "2025-06-30T15:52:10.947Z" }, - { url = "https://files.pythonhosted.org/packages/6f/5c/4d7adc739884f7a9fbe00d1eac8c034023ef8bad71f2ebe12823ca2e3649/multidict-6.6.3-cp312-cp312-win32.whl", hash = "sha256:73ab034fb8d58ff85c2bcbadc470efc3fafeea8affcf8722855fb94557f14cc5", size = 41860, upload-time = "2025-06-30T15:52:12.334Z" }, - { url = "https://files.pythonhosted.org/packages/6a/a3/0fbc7afdf7cb1aa12a086b02959307848eb6bcc8f66fcb66c0cb57e2a2c1/multidict-6.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:04cbcce84f63b9af41bad04a54d4cc4e60e90c35b9e6ccb130be2d75b71f8c17", size = 45982, upload-time = "2025-06-30T15:52:13.6Z" }, - { url = "https://files.pythonhosted.org/packages/b8/95/8c825bd70ff9b02462dc18d1295dd08d3e9e4eb66856d292ffa62cfe1920/multidict-6.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:0f1130b896ecb52d2a1e615260f3ea2af55fa7dc3d7c3003ba0c3121a759b18b", size = 43210, upload-time = "2025-06-30T15:52:14.893Z" }, - { url = "https://files.pythonhosted.org/packages/52/1d/0bebcbbb4f000751fbd09957257903d6e002943fc668d841a4cf2fb7f872/multidict-6.6.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:540d3c06d48507357a7d57721e5094b4f7093399a0106c211f33540fdc374d55", size = 75843, upload-time = "2025-06-30T15:52:16.155Z" }, - { url = "https://files.pythonhosted.org/packages/07/8f/cbe241b0434cfe257f65c2b1bcf9e8d5fb52bc708c5061fb29b0fed22bdf/multidict-6.6.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9c19cea2a690f04247d43f366d03e4eb110a0dc4cd1bbeee4d445435428ed35b", size = 45053, upload-time = "2025-06-30T15:52:17.429Z" }, - { url = "https://files.pythonhosted.org/packages/32/d2/0b3b23f9dbad5b270b22a3ac3ea73ed0a50ef2d9a390447061178ed6bdb8/multidict-6.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7af039820cfd00effec86bda5d8debef711a3e86a1d3772e85bea0f243a4bd65", size = 43273, upload-time = "2025-06-30T15:52:19.346Z" }, - { url = "https://files.pythonhosted.org/packages/fd/fe/6eb68927e823999e3683bc49678eb20374ba9615097d085298fd5b386564/multidict-6.6.3-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:500b84f51654fdc3944e936f2922114349bf8fdcac77c3092b03449f0e5bc2b3", size = 237124, upload-time = "2025-06-30T15:52:20.773Z" }, - { url = "https://files.pythonhosted.org/packages/e7/ab/320d8507e7726c460cb77117848b3834ea0d59e769f36fdae495f7669929/multidict-6.6.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3fc723ab8a5c5ed6c50418e9bfcd8e6dceba6c271cee6728a10a4ed8561520c", size = 256892, upload-time = "2025-06-30T15:52:22.242Z" }, - { url = "https://files.pythonhosted.org/packages/76/60/38ee422db515ac69834e60142a1a69111ac96026e76e8e9aa347fd2e4591/multidict-6.6.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:94c47ea3ade005b5976789baaed66d4de4480d0a0bf31cef6edaa41c1e7b56a6", size = 240547, upload-time = "2025-06-30T15:52:23.736Z" }, - { url = "https://files.pythonhosted.org/packages/27/fb/905224fde2dff042b030c27ad95a7ae744325cf54b890b443d30a789b80e/multidict-6.6.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dbc7cf464cc6d67e83e136c9f55726da3a30176f020a36ead246eceed87f1cd8", size = 266223, upload-time = "2025-06-30T15:52:25.185Z" }, - { url = "https://files.pythonhosted.org/packages/76/35/dc38ab361051beae08d1a53965e3e1a418752fc5be4d3fb983c5582d8784/multidict-6.6.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:900eb9f9da25ada070f8ee4a23f884e0ee66fe4e1a38c3af644256a508ad81ca", size = 267262, upload-time = "2025-06-30T15:52:26.969Z" }, - { url = "https://files.pythonhosted.org/packages/1f/a3/0a485b7f36e422421b17e2bbb5a81c1af10eac1d4476f2ff92927c730479/multidict-6.6.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c6df517cf177da5d47ab15407143a89cd1a23f8b335f3a28d57e8b0a3dbb884", size = 254345, upload-time = "2025-06-30T15:52:28.467Z" }, - { url = "https://files.pythonhosted.org/packages/b4/59/bcdd52c1dab7c0e0d75ff19cac751fbd5f850d1fc39172ce809a74aa9ea4/multidict-6.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4ef421045f13879e21c994b36e728d8e7d126c91a64b9185810ab51d474f27e7", size = 252248, upload-time = "2025-06-30T15:52:29.938Z" }, - { url = "https://files.pythonhosted.org/packages/bb/a4/2d96aaa6eae8067ce108d4acee6f45ced5728beda55c0f02ae1072c730d1/multidict-6.6.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6c1e61bb4f80895c081790b6b09fa49e13566df8fbff817da3f85b3a8192e36b", size = 250115, upload-time = "2025-06-30T15:52:31.416Z" }, - { url = "https://files.pythonhosted.org/packages/25/d2/ed9f847fa5c7d0677d4f02ea2c163d5e48573de3f57bacf5670e43a5ffaa/multidict-6.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e5e8523bb12d7623cd8300dbd91b9e439a46a028cd078ca695eb66ba31adee3c", size = 249649, upload-time = "2025-06-30T15:52:32.996Z" }, - { url = "https://files.pythonhosted.org/packages/1f/af/9155850372563fc550803d3f25373308aa70f59b52cff25854086ecb4a79/multidict-6.6.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ef58340cc896219e4e653dade08fea5c55c6df41bcc68122e3be3e9d873d9a7b", size = 261203, upload-time = "2025-06-30T15:52:34.521Z" }, - { url = "https://files.pythonhosted.org/packages/36/2f/c6a728f699896252cf309769089568a33c6439626648843f78743660709d/multidict-6.6.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc9dc435ec8699e7b602b94fe0cd4703e69273a01cbc34409af29e7820f777f1", size = 258051, upload-time = "2025-06-30T15:52:35.999Z" }, - { url = "https://files.pythonhosted.org/packages/d0/60/689880776d6b18fa2b70f6cc74ff87dd6c6b9b47bd9cf74c16fecfaa6ad9/multidict-6.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9e864486ef4ab07db5e9cb997bad2b681514158d6954dd1958dfb163b83d53e6", size = 249601, upload-time = "2025-06-30T15:52:37.473Z" }, - { url = "https://files.pythonhosted.org/packages/75/5e/325b11f2222a549019cf2ef879c1f81f94a0d40ace3ef55cf529915ba6cc/multidict-6.6.3-cp313-cp313-win32.whl", hash = "sha256:5633a82fba8e841bc5c5c06b16e21529573cd654f67fd833650a215520a6210e", size = 41683, upload-time = "2025-06-30T15:52:38.927Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ad/cf46e73f5d6e3c775cabd2a05976547f3f18b39bee06260369a42501f053/multidict-6.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:e93089c1570a4ad54c3714a12c2cef549dc9d58e97bcded193d928649cab78e9", size = 45811, upload-time = "2025-06-30T15:52:40.207Z" }, - { url = "https://files.pythonhosted.org/packages/c5/c9/2e3fe950db28fb7c62e1a5f46e1e38759b072e2089209bc033c2798bb5ec/multidict-6.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:c60b401f192e79caec61f166da9c924e9f8bc65548d4246842df91651e83d600", size = 43056, upload-time = "2025-06-30T15:52:41.575Z" }, - { url = "https://files.pythonhosted.org/packages/3a/58/aaf8114cf34966e084a8cc9517771288adb53465188843d5a19862cb6dc3/multidict-6.6.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:02fd8f32d403a6ff13864b0851f1f523d4c988051eea0471d4f1fd8010f11134", size = 82811, upload-time = "2025-06-30T15:52:43.281Z" }, - { url = "https://files.pythonhosted.org/packages/71/af/5402e7b58a1f5b987a07ad98f2501fdba2a4f4b4c30cf114e3ce8db64c87/multidict-6.6.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f3aa090106b1543f3f87b2041eef3c156c8da2aed90c63a2fbed62d875c49c37", size = 48304, upload-time = "2025-06-30T15:52:45.026Z" }, - { url = "https://files.pythonhosted.org/packages/39/65/ab3c8cafe21adb45b24a50266fd747147dec7847425bc2a0f6934b3ae9ce/multidict-6.6.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e924fb978615a5e33ff644cc42e6aa241effcf4f3322c09d4f8cebde95aff5f8", size = 46775, upload-time = "2025-06-30T15:52:46.459Z" }, - { url = "https://files.pythonhosted.org/packages/49/ba/9fcc1b332f67cc0c0c8079e263bfab6660f87fe4e28a35921771ff3eea0d/multidict-6.6.3-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:b9fe5a0e57c6dbd0e2ce81ca66272282c32cd11d31658ee9553849d91289e1c1", size = 229773, upload-time = "2025-06-30T15:52:47.88Z" }, - { url = "https://files.pythonhosted.org/packages/a4/14/0145a251f555f7c754ce2dcbcd012939bbd1f34f066fa5d28a50e722a054/multidict-6.6.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b24576f208793ebae00280c59927c3b7c2a3b1655e443a25f753c4611bc1c373", size = 250083, upload-time = "2025-06-30T15:52:49.366Z" }, - { url = "https://files.pythonhosted.org/packages/9e/d4/d5c0bd2bbb173b586c249a151a26d2fb3ec7d53c96e42091c9fef4e1f10c/multidict-6.6.3-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:135631cb6c58eac37d7ac0df380294fecdc026b28837fa07c02e459c7fb9c54e", size = 228980, upload-time = "2025-06-30T15:52:50.903Z" }, - { url = "https://files.pythonhosted.org/packages/21/32/c9a2d8444a50ec48c4733ccc67254100c10e1c8ae8e40c7a2d2183b59b97/multidict-6.6.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:274d416b0df887aef98f19f21578653982cfb8a05b4e187d4a17103322eeaf8f", size = 257776, upload-time = "2025-06-30T15:52:52.764Z" }, - { url = "https://files.pythonhosted.org/packages/68/d0/14fa1699f4ef629eae08ad6201c6b476098f5efb051b296f4c26be7a9fdf/multidict-6.6.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e252017a817fad7ce05cafbe5711ed40faeb580e63b16755a3a24e66fa1d87c0", size = 256882, upload-time = "2025-06-30T15:52:54.596Z" }, - { url = "https://files.pythonhosted.org/packages/da/88/84a27570fbe303c65607d517a5f147cd2fc046c2d1da02b84b17b9bdc2aa/multidict-6.6.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4cc8d848cd4fe1cdee28c13ea79ab0ed37fc2e89dd77bac86a2e7959a8c3bc", size = 247816, upload-time = "2025-06-30T15:52:56.175Z" }, - { url = "https://files.pythonhosted.org/packages/1c/60/dca352a0c999ce96a5d8b8ee0b2b9f729dcad2e0b0c195f8286269a2074c/multidict-6.6.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9e236a7094b9c4c1b7585f6b9cca34b9d833cf079f7e4c49e6a4a6ec9bfdc68f", size = 245341, upload-time = "2025-06-30T15:52:57.752Z" }, - { url = "https://files.pythonhosted.org/packages/50/ef/433fa3ed06028f03946f3993223dada70fb700f763f70c00079533c34578/multidict-6.6.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e0cb0ab69915c55627c933f0b555a943d98ba71b4d1c57bc0d0a66e2567c7471", size = 235854, upload-time = "2025-06-30T15:52:59.74Z" }, - { url = "https://files.pythonhosted.org/packages/1b/1f/487612ab56fbe35715320905215a57fede20de7db40a261759690dc80471/multidict-6.6.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:81ef2f64593aba09c5212a3d0f8c906a0d38d710a011f2f42759704d4557d3f2", size = 243432, upload-time = "2025-06-30T15:53:01.602Z" }, - { url = "https://files.pythonhosted.org/packages/da/6f/ce8b79de16cd885c6f9052c96a3671373d00c59b3ee635ea93e6e81b8ccf/multidict-6.6.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:b9cbc60010de3562545fa198bfc6d3825df430ea96d2cc509c39bd71e2e7d648", size = 252731, upload-time = "2025-06-30T15:53:03.517Z" }, - { url = "https://files.pythonhosted.org/packages/bb/fe/a2514a6aba78e5abefa1624ca85ae18f542d95ac5cde2e3815a9fbf369aa/multidict-6.6.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:70d974eaaa37211390cd02ef93b7e938de564bbffa866f0b08d07e5e65da783d", size = 247086, upload-time = "2025-06-30T15:53:05.48Z" }, - { url = "https://files.pythonhosted.org/packages/8c/22/b788718d63bb3cce752d107a57c85fcd1a212c6c778628567c9713f9345a/multidict-6.6.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3713303e4a6663c6d01d648a68f2848701001f3390a030edaaf3fc949c90bf7c", size = 243338, upload-time = "2025-06-30T15:53:07.522Z" }, - { url = "https://files.pythonhosted.org/packages/22/d6/fdb3d0670819f2228f3f7d9af613d5e652c15d170c83e5f1c94fbc55a25b/multidict-6.6.3-cp313-cp313t-win32.whl", hash = "sha256:639ecc9fe7cd73f2495f62c213e964843826f44505a3e5d82805aa85cac6f89e", size = 47812, upload-time = "2025-06-30T15:53:09.263Z" }, - { url = "https://files.pythonhosted.org/packages/b6/d6/a9d2c808f2c489ad199723197419207ecbfbc1776f6e155e1ecea9c883aa/multidict-6.6.3-cp313-cp313t-win_amd64.whl", hash = "sha256:9f97e181f344a0ef3881b573d31de8542cc0dbc559ec68c8f8b5ce2c2e91646d", size = 53011, upload-time = "2025-06-30T15:53:11.038Z" }, - { url = "https://files.pythonhosted.org/packages/f2/40/b68001cba8188dd267590a111f9661b6256debc327137667e832bf5d66e8/multidict-6.6.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ce8b7693da41a3c4fde5871c738a81490cea5496c671d74374c8ab889e1834fb", size = 45254, upload-time = "2025-06-30T15:53:12.421Z" }, - { url = "https://files.pythonhosted.org/packages/d8/30/9aec301e9772b098c1f5c0ca0279237c9766d94b97802e9888010c64b0ed/multidict-6.6.3-py3-none-any.whl", hash = "sha256:8db10f29c7541fc5da4defd8cd697e1ca429db743fa716325f236079b96f775a", size = 12313, upload-time = "2025-06-30T15:53:45.437Z" }, -] - -[[package]] -name = "mypy" -version = "1.17.0" -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/1e/e3/034322d5a779685218ed69286c32faa505247f1f096251ef66c8fd203b08/mypy-1.17.0.tar.gz", hash = "sha256:e5d7ccc08ba089c06e2f5629c660388ef1fee708444f1dee0b9203fa031dee03", size = 3352114, upload-time = "2025-07-14T20:34:30.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/31/e762baa3b73905c856d45ab77b4af850e8159dffffd86a52879539a08c6b/mypy-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8e08de6138043108b3b18f09d3f817a4783912e48828ab397ecf183135d84d6", size = 10998313, upload-time = "2025-07-14T20:33:24.519Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c1/25b2f0d46fb7e0b5e2bee61ec3a47fe13eff9e3c2f2234f144858bbe6485/mypy-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce4a17920ec144647d448fc43725b5873548b1aae6c603225626747ededf582d", size = 10128922, upload-time = "2025-07-14T20:34:06.414Z" }, - { url = "https://files.pythonhosted.org/packages/02/78/6d646603a57aa8a2886df1b8881fe777ea60f28098790c1089230cd9c61d/mypy-1.17.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ff25d151cc057fdddb1cb1881ef36e9c41fa2a5e78d8dd71bee6e4dcd2bc05b", size = 11913524, upload-time = "2025-07-14T20:33:19.109Z" }, - { url = "https://files.pythonhosted.org/packages/4f/19/dae6c55e87ee426fb76980f7e78484450cad1c01c55a1dc4e91c930bea01/mypy-1.17.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93468cf29aa9a132bceb103bd8475f78cacde2b1b9a94fd978d50d4bdf616c9a", size = 12650527, upload-time = "2025-07-14T20:32:44.095Z" }, - { url = "https://files.pythonhosted.org/packages/86/e1/f916845a235235a6c1e4d4d065a3930113767001d491b8b2e1b61ca56647/mypy-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:98189382b310f16343151f65dd7e6867386d3e35f7878c45cfa11383d175d91f", size = 12897284, upload-time = "2025-07-14T20:33:38.168Z" }, - { url = "https://files.pythonhosted.org/packages/ae/dc/414760708a4ea1b096bd214d26a24e30ac5e917ef293bc33cdb6fe22d2da/mypy-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:c004135a300ab06a045c1c0d8e3f10215e71d7b4f5bb9a42ab80236364429937", size = 9506493, upload-time = "2025-07-14T20:34:01.093Z" }, - { url = "https://files.pythonhosted.org/packages/d4/24/82efb502b0b0f661c49aa21cfe3e1999ddf64bf5500fc03b5a1536a39d39/mypy-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d4fe5c72fd262d9c2c91c1117d16aac555e05f5beb2bae6a755274c6eec42be", size = 10914150, upload-time = "2025-07-14T20:31:51.985Z" }, - { url = "https://files.pythonhosted.org/packages/03/96/8ef9a6ff8cedadff4400e2254689ca1dc4b420b92c55255b44573de10c54/mypy-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96b196e5c16f41b4f7736840e8455958e832871990c7ba26bf58175e357ed61", size = 10039845, upload-time = "2025-07-14T20:32:30.527Z" }, - { url = "https://files.pythonhosted.org/packages/df/32/7ce359a56be779d38021d07941cfbb099b41411d72d827230a36203dbb81/mypy-1.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73a0ff2dd10337ceb521c080d4147755ee302dcde6e1a913babd59473904615f", size = 11837246, upload-time = "2025-07-14T20:32:01.28Z" }, - { url = "https://files.pythonhosted.org/packages/82/16/b775047054de4d8dbd668df9137707e54b07fe18c7923839cd1e524bf756/mypy-1.17.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cfcc1179c4447854e9e406d3af0f77736d631ec87d31c6281ecd5025df625d", size = 12571106, upload-time = "2025-07-14T20:34:26.942Z" }, - { url = "https://files.pythonhosted.org/packages/a1/cf/fa33eaf29a606102c8d9ffa45a386a04c2203d9ad18bf4eef3e20c43ebc8/mypy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56f180ff6430e6373db7a1d569317675b0a451caf5fef6ce4ab365f5f2f6c3", size = 12759960, upload-time = "2025-07-14T20:33:42.882Z" }, - { url = "https://files.pythonhosted.org/packages/94/75/3f5a29209f27e739ca57e6350bc6b783a38c7621bdf9cac3ab8a08665801/mypy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:eafaf8b9252734400f9b77df98b4eee3d2eecab16104680d51341c75702cad70", size = 9503888, upload-time = "2025-07-14T20:32:34.392Z" }, - { url = "https://files.pythonhosted.org/packages/12/e9/e6824ed620bbf51d3bf4d6cbbe4953e83eaf31a448d1b3cfb3620ccb641c/mypy-1.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f986f1cab8dbec39ba6e0eaa42d4d3ac6686516a5d3dccd64be095db05ebc6bb", size = 11086395, upload-time = "2025-07-14T20:34:11.452Z" }, - { url = "https://files.pythonhosted.org/packages/ba/51/a4afd1ae279707953be175d303f04a5a7bd7e28dc62463ad29c1c857927e/mypy-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:51e455a54d199dd6e931cd7ea987d061c2afbaf0960f7f66deef47c90d1b304d", size = 10120052, upload-time = "2025-07-14T20:33:09.897Z" }, - { url = "https://files.pythonhosted.org/packages/8a/71/19adfeac926ba8205f1d1466d0d360d07b46486bf64360c54cb5a2bd86a8/mypy-1.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3204d773bab5ff4ebbd1f8efa11b498027cd57017c003ae970f310e5b96be8d8", size = 11861806, upload-time = "2025-07-14T20:32:16.028Z" }, - { url = "https://files.pythonhosted.org/packages/0b/64/d6120eca3835baf7179e6797a0b61d6c47e0bc2324b1f6819d8428d5b9ba/mypy-1.17.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1051df7ec0886fa246a530ae917c473491e9a0ba6938cfd0ec2abc1076495c3e", size = 12744371, upload-time = "2025-07-14T20:33:33.503Z" }, - { url = "https://files.pythonhosted.org/packages/1f/dc/56f53b5255a166f5bd0f137eed960e5065f2744509dfe69474ff0ba772a5/mypy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f773c6d14dcc108a5b141b4456b0871df638eb411a89cd1c0c001fc4a9d08fc8", size = 12914558, upload-time = "2025-07-14T20:33:56.961Z" }, - { url = "https://files.pythonhosted.org/packages/69/ac/070bad311171badc9add2910e7f89271695a25c136de24bbafc7eded56d5/mypy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1619a485fd0e9c959b943c7b519ed26b712de3002d7de43154a489a2d0fd817d", size = 9585447, upload-time = "2025-07-14T20:32:20.594Z" }, - { url = "https://files.pythonhosted.org/packages/be/7b/5f8ab461369b9e62157072156935cec9d272196556bdc7c2ff5f4c7c0f9b/mypy-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c41aa59211e49d717d92b3bb1238c06d387c9325d3122085113c79118bebb06", size = 11070019, upload-time = "2025-07-14T20:32:07.99Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f8/c49c9e5a2ac0badcc54beb24e774d2499748302c9568f7f09e8730e953fa/mypy-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e69db1fb65b3114f98c753e3930a00514f5b68794ba80590eb02090d54a5d4a", size = 10114457, upload-time = "2025-07-14T20:33:47.285Z" }, - { url = "https://files.pythonhosted.org/packages/89/0c/fb3f9c939ad9beed3e328008b3fb90b20fda2cddc0f7e4c20dbefefc3b33/mypy-1.17.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03ba330b76710f83d6ac500053f7727270b6b8553b0423348ffb3af6f2f7b889", size = 11857838, upload-time = "2025-07-14T20:33:14.462Z" }, - { url = "https://files.pythonhosted.org/packages/4c/66/85607ab5137d65e4f54d9797b77d5a038ef34f714929cf8ad30b03f628df/mypy-1.17.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:037bc0f0b124ce46bfde955c647f3e395c6174476a968c0f22c95a8d2f589bba", size = 12731358, upload-time = "2025-07-14T20:32:25.579Z" }, - { url = "https://files.pythonhosted.org/packages/73/d0/341dbbfb35ce53d01f8f2969facbb66486cee9804048bf6c01b048127501/mypy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38876106cb6132259683632b287238858bd58de267d80defb6f418e9ee50658", size = 12917480, upload-time = "2025-07-14T20:34:21.868Z" }, - { url = "https://files.pythonhosted.org/packages/64/63/70c8b7dbfc520089ac48d01367a97e8acd734f65bd07813081f508a8c94c/mypy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:d30ba01c0f151998f367506fab31c2ac4527e6a7b2690107c7a7f9e3cb419a9c", size = 9589666, upload-time = "2025-07-14T20:34:16.841Z" }, - { url = "https://files.pythonhosted.org/packages/e3/fc/ee058cc4316f219078464555873e99d170bde1d9569abd833300dbeb484a/mypy-1.17.0-py3-none-any.whl", hash = "sha256:15d9d0018237ab058e5de3d8fce61b6fa72cc59cc78fd91f1b474bce12abf496", size = 2283195, upload-time = "2025-07-14T20:31:54.753Z" }, -] - -[[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 = "networkx" -version = "3.4.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, -] - -[[package]] -name = "networkx" -version = "3.5" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", -] -sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" }, -] - -[[package]] -name = "nh3" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/a4/96cff0977357f60f06ec4368c4c7a7a26cccfe7c9fcd54f5378bf0428fd3/nh3-0.3.0.tar.gz", hash = "sha256:d8ba24cb31525492ea71b6aac11a4adac91d828aadeff7c4586541bf5dc34d2f", size = 19655, upload-time = "2025-07-17T14:43:37.05Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/11/340b7a551916a4b2b68c54799d710f86cf3838a4abaad8e74d35360343bb/nh3-0.3.0-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a537ece1bf513e5a88d8cff8a872e12fe8d0f42ef71dd15a5e7520fecd191bbb", size = 1427992, upload-time = "2025-07-17T14:43:06.848Z" }, - { url = "https://files.pythonhosted.org/packages/ad/7f/7c6b8358cf1222921747844ab0eef81129e9970b952fcb814df417159fb9/nh3-0.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c915060a2c8131bef6a29f78debc29ba40859b6dbe2362ef9e5fd44f11487c2", size = 798194, upload-time = "2025-07-17T14:43:08.263Z" }, - { url = "https://files.pythonhosted.org/packages/63/da/c5fd472b700ba37d2df630a9e0d8cc156033551ceb8b4c49cc8a5f606b68/nh3-0.3.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba0caa8aa184196daa6e574d997a33867d6d10234018012d35f86d46024a2a95", size = 837884, upload-time = "2025-07-17T14:43:09.233Z" }, - { url = "https://files.pythonhosted.org/packages/4c/3c/cba7b26ccc0ef150c81646478aa32f9c9535234f54845603c838a1dc955c/nh3-0.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:80fe20171c6da69c7978ecba33b638e951b85fb92059259edd285ff108b82a6d", size = 996365, upload-time = "2025-07-17T14:43:10.243Z" }, - { url = "https://files.pythonhosted.org/packages/f3/ba/59e204d90727c25b253856e456ea61265ca810cda8ee802c35f3fadaab00/nh3-0.3.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e90883f9f85288f423c77b3f5a6f4486375636f25f793165112679a7b6363b35", size = 1071042, upload-time = "2025-07-17T14:43:11.57Z" }, - { url = "https://files.pythonhosted.org/packages/10/71/2fb1834c10fab6d9291d62c95192ea2f4c7518bd32ad6c46aab5d095cb87/nh3-0.3.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0649464ac8eee018644aacbc103874ccbfac80e3035643c3acaab4287e36e7f5", size = 995737, upload-time = "2025-07-17T14:43:12.659Z" }, - { url = "https://files.pythonhosted.org/packages/33/c1/8f8ccc2492a000b6156dce68a43253fcff8b4ce70ab4216d08f90a2ac998/nh3-0.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1adeb1062a1c2974bc75b8d1ecb014c5fd4daf2df646bbe2831f7c23659793f9", size = 980552, upload-time = "2025-07-17T14:43:13.763Z" }, - { url = "https://files.pythonhosted.org/packages/2f/d6/f1c6e091cbe8700401c736c2bc3980c46dca770a2cf6a3b48a175114058e/nh3-0.3.0-cp313-cp313t-win32.whl", hash = "sha256:7275fdffaab10cc5801bf026e3c089d8de40a997afc9e41b981f7ac48c5aa7d5", size = 593618, upload-time = "2025-07-17T14:43:15.098Z" }, - { url = "https://files.pythonhosted.org/packages/23/1e/80a8c517655dd40bb13363fc4d9e66b2f13245763faab1a20f1df67165a7/nh3-0.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:423201bbdf3164a9e09aa01e540adbb94c9962cc177d5b1cbb385f5e1e79216e", size = 598948, upload-time = "2025-07-17T14:43:16.064Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e0/af86d2a974c87a4ba7f19bc3b44a8eaa3da480de264138fec82fe17b340b/nh3-0.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:16f8670201f7e8e0e05ed1a590eb84bfa51b01a69dd5caf1d3ea57733de6a52f", size = 580479, upload-time = "2025-07-17T14:43:17.038Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e0/cf1543e798ba86d838952e8be4cb8d18e22999be2a24b112a671f1c04fd6/nh3-0.3.0-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ec6cfdd2e0399cb79ba4dcffb2332b94d9696c52272ff9d48a630c5dca5e325a", size = 1442218, upload-time = "2025-07-17T14:43:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/5c/86/a96b1453c107b815f9ab8fac5412407c33cc5c7580a4daf57aabeb41b774/nh3-0.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5e7185599f89b0e391e2f29cc12dc2e206167380cea49b33beda4891be2fe1", size = 823791, upload-time = "2025-07-17T14:43:19.721Z" }, - { url = "https://files.pythonhosted.org/packages/97/33/11e7273b663839626f714cb68f6eb49899da5a0d9b6bc47b41fe870259c2/nh3-0.3.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:389d93d59b8214d51c400fb5b07866c2a4f79e4e14b071ad66c92184fec3a392", size = 811143, upload-time = "2025-07-17T14:43:20.779Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1b/b15bd1ce201a1a610aeb44afd478d55ac018b4475920a3118ffd806e2483/nh3-0.3.0-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e9e6a7e4d38f7e8dda9edd1433af5170c597336c1a74b4693c5cb75ab2b30f2a", size = 1064661, upload-time = "2025-07-17T14:43:21.839Z" }, - { url = "https://files.pythonhosted.org/packages/8f/14/079670fb2e848c4ba2476c5a7a2d1319826053f4f0368f61fca9bb4227ae/nh3-0.3.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7852f038a054e0096dac12b8141191e02e93e0b4608c4b993ec7d4ffafea4e49", size = 997061, upload-time = "2025-07-17T14:43:23.179Z" }, - { url = "https://files.pythonhosted.org/packages/a3/e5/ac7fc565f5d8bce7f979d1afd68e8cb415020d62fa6507133281c7d49f91/nh3-0.3.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af5aa8127f62bbf03d68f67a956627b1bd0469703a35b3dad28d0c1195e6c7fb", size = 924761, upload-time = "2025-07-17T14:43:24.23Z" }, - { url = "https://files.pythonhosted.org/packages/39/2c/6394301428b2017a9d5644af25f487fa557d06bc8a491769accec7524d9a/nh3-0.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f416c35efee3e6a6c9ab7716d9e57aa0a49981be915963a82697952cba1353e1", size = 803959, upload-time = "2025-07-17T14:43:26.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/9a/344b9f9c4bd1c2413a397f38ee6a3d5db30f1a507d4976e046226f12b297/nh3-0.3.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:37d3003d98dedca6cd762bf88f2e70b67f05100f6b949ffe540e189cc06887f9", size = 844073, upload-time = "2025-07-17T14:43:27.375Z" }, - { url = "https://files.pythonhosted.org/packages/66/3f/cd37f76c8ca277b02a84aa20d7bd60fbac85b4e2cbdae77cb759b22de58b/nh3-0.3.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:634e34e6162e0408e14fb61d5e69dbaea32f59e847cfcfa41b66100a6b796f62", size = 1000680, upload-time = "2025-07-17T14:43:28.452Z" }, - { url = "https://files.pythonhosted.org/packages/ee/db/7aa11b44bae4e7474feb1201d8dee04fabe5651c7cb51409ebda94a4ed67/nh3-0.3.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:b0612ccf5de8a480cf08f047b08f9d3fecc12e63d2ee91769cb19d7290614c23", size = 1076613, upload-time = "2025-07-17T14:43:30.031Z" }, - { url = "https://files.pythonhosted.org/packages/97/03/03f79f7e5178eb1ad5083af84faff471e866801beb980cc72943a4397368/nh3-0.3.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c7a32a7f0d89f7d30cb8f4a84bdbd56d1eb88b78a2434534f62c71dac538c450", size = 1001418, upload-time = "2025-07-17T14:43:31.429Z" }, - { url = "https://files.pythonhosted.org/packages/ce/55/1974bcc16884a397ee699cebd3914e1f59be64ab305533347ca2d983756f/nh3-0.3.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3f1b4f8a264a0c86ea01da0d0c390fe295ea0bcacc52c2103aca286f6884f518", size = 986499, upload-time = "2025-07-17T14:43:32.459Z" }, - { url = "https://files.pythonhosted.org/packages/c9/50/76936ec021fe1f3270c03278b8af5f2079038116b5d0bfe8538ffe699d69/nh3-0.3.0-cp38-abi3-win32.whl", hash = "sha256:6d68fa277b4a3cf04e5c4b84dd0c6149ff7d56c12b3e3fab304c525b850f613d", size = 599000, upload-time = "2025-07-17T14:43:33.852Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ae/324b165d904dc1672eee5f5661c0a68d4bab5b59fbb07afb6d8d19a30b45/nh3-0.3.0-cp38-abi3-win_amd64.whl", hash = "sha256:bae63772408fd63ad836ec569a7c8f444dd32863d0c67f6e0b25ebbd606afa95", size = 604530, upload-time = "2025-07-17T14:43:34.95Z" }, - { url = "https://files.pythonhosted.org/packages/5b/76/3165e84e5266d146d967a6cc784ff2fbf6ddd00985a55ec006b72bc39d5d/nh3-0.3.0-cp38-abi3-win_arm64.whl", hash = "sha256:d97d3efd61404af7e5721a0e74d81cdbfc6e5f97e11e731bb6d090e30a7b62b2", size = 585971, upload-time = "2025-07-17T14:43:35.936Z" }, -] - -[[package]] -name = "nodeenv" -version = "1.9.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, -] - -[[package]] -name = "numpy" -version = "2.2.6" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, - { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, - { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, - { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, - { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, - { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, - { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, - { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, - { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, - { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, - { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, - { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, - { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, - { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, - { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, - { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, - { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, - { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, - { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, - { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, - { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, - { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, - { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, - { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, - { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, - { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, - { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, - { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, - { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, - { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, - { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, - { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, - { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, - { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, - { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, - { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, - { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, - { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, - { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, - { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, - { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, - { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, - { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, - { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, - { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, - { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, - { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, - { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, - { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, - { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, -] - -[[package]] -name = "numpy" -version = "2.3.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", -] -sdist = { url = "https://files.pythonhosted.org/packages/2e/19/d7c972dfe90a353dbd3efbbe1d14a5951de80c99c9dc1b93cd998d51dc0f/numpy-2.3.1.tar.gz", hash = "sha256:1ec9ae20a4226da374362cca3c62cd753faf2f951440b0e3b98e93c235441d2b", size = 20390372, upload-time = "2025-06-21T12:28:33.469Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/c7/87c64d7ab426156530676000c94784ef55676df2f13b2796f97722464124/numpy-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ea9e48336a402551f52cd8f593343699003d2353daa4b72ce8d34f66b722070", size = 21199346, upload-time = "2025-06-21T11:47:47.57Z" }, - { url = "https://files.pythonhosted.org/packages/58/0e/0966c2f44beeac12af8d836e5b5f826a407cf34c45cb73ddcdfce9f5960b/numpy-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ccb7336eaf0e77c1635b232c141846493a588ec9ea777a7c24d7166bb8533ae", size = 14361143, upload-time = "2025-06-21T11:48:10.766Z" }, - { url = "https://files.pythonhosted.org/packages/7d/31/6e35a247acb1bfc19226791dfc7d4c30002cd4e620e11e58b0ddf836fe52/numpy-2.3.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0bb3a4a61e1d327e035275d2a993c96fa786e4913aa089843e6a2d9dd205c66a", size = 5378989, upload-time = "2025-06-21T11:48:19.998Z" }, - { url = "https://files.pythonhosted.org/packages/b0/25/93b621219bb6f5a2d4e713a824522c69ab1f06a57cd571cda70e2e31af44/numpy-2.3.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:e344eb79dab01f1e838ebb67aab09965fb271d6da6b00adda26328ac27d4a66e", size = 6912890, upload-time = "2025-06-21T11:48:31.376Z" }, - { url = "https://files.pythonhosted.org/packages/ef/60/6b06ed98d11fb32e27fb59468b42383f3877146d3ee639f733776b6ac596/numpy-2.3.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:467db865b392168ceb1ef1ffa6f5a86e62468c43e0cfb4ab6da667ede10e58db", size = 14569032, upload-time = "2025-06-21T11:48:52.563Z" }, - { url = "https://files.pythonhosted.org/packages/75/c9/9bec03675192077467a9c7c2bdd1f2e922bd01d3a69b15c3a0fdcd8548f6/numpy-2.3.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:afed2ce4a84f6b0fc6c1ce734ff368cbf5a5e24e8954a338f3bdffa0718adffb", size = 16930354, upload-time = "2025-06-21T11:49:17.473Z" }, - { url = "https://files.pythonhosted.org/packages/6a/e2/5756a00cabcf50a3f527a0c968b2b4881c62b1379223931853114fa04cda/numpy-2.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0025048b3c1557a20bc80d06fdeb8cc7fc193721484cca82b2cfa072fec71a93", size = 15879605, upload-time = "2025-06-21T11:49:41.161Z" }, - { url = "https://files.pythonhosted.org/packages/ff/86/a471f65f0a86f1ca62dcc90b9fa46174dd48f50214e5446bc16a775646c5/numpy-2.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5ee121b60aa509679b682819c602579e1df14a5b07fe95671c8849aad8f2115", size = 18666994, upload-time = "2025-06-21T11:50:08.516Z" }, - { url = "https://files.pythonhosted.org/packages/43/a6/482a53e469b32be6500aaf61cfafd1de7a0b0d484babf679209c3298852e/numpy-2.3.1-cp311-cp311-win32.whl", hash = "sha256:a8b740f5579ae4585831b3cf0e3b0425c667274f82a484866d2adf9570539369", size = 6603672, upload-time = "2025-06-21T11:50:19.584Z" }, - { url = "https://files.pythonhosted.org/packages/6b/fb/bb613f4122c310a13ec67585c70e14b03bfc7ebabd24f4d5138b97371d7c/numpy-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4580adadc53311b163444f877e0789f1c8861e2698f6b2a4ca852fda154f3ff", size = 13024015, upload-time = "2025-06-21T11:50:39.139Z" }, - { url = "https://files.pythonhosted.org/packages/51/58/2d842825af9a0c041aca246dc92eb725e1bc5e1c9ac89712625db0c4e11c/numpy-2.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:ec0bdafa906f95adc9a0c6f26a4871fa753f25caaa0e032578a30457bff0af6a", size = 10456989, upload-time = "2025-06-21T11:50:55.616Z" }, - { url = "https://files.pythonhosted.org/packages/c6/56/71ad5022e2f63cfe0ca93559403d0edef14aea70a841d640bd13cdba578e/numpy-2.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2959d8f268f3d8ee402b04a9ec4bb7604555aeacf78b360dc4ec27f1d508177d", size = 20896664, upload-time = "2025-06-21T12:15:30.845Z" }, - { url = "https://files.pythonhosted.org/packages/25/65/2db52ba049813670f7f987cc5db6dac9be7cd95e923cc6832b3d32d87cef/numpy-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:762e0c0c6b56bdedfef9a8e1d4538556438288c4276901ea008ae44091954e29", size = 14131078, upload-time = "2025-06-21T12:15:52.23Z" }, - { url = "https://files.pythonhosted.org/packages/57/dd/28fa3c17b0e751047ac928c1e1b6990238faad76e9b147e585b573d9d1bd/numpy-2.3.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:867ef172a0976aaa1f1d1b63cf2090de8b636a7674607d514505fb7276ab08fc", size = 5112554, upload-time = "2025-06-21T12:16:01.434Z" }, - { url = "https://files.pythonhosted.org/packages/c9/fc/84ea0cba8e760c4644b708b6819d91784c290288c27aca916115e3311d17/numpy-2.3.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:4e602e1b8682c2b833af89ba641ad4176053aaa50f5cacda1a27004352dde943", size = 6646560, upload-time = "2025-06-21T12:16:11.895Z" }, - { url = "https://files.pythonhosted.org/packages/61/b2/512b0c2ddec985ad1e496b0bd853eeb572315c0f07cd6997473ced8f15e2/numpy-2.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8e333040d069eba1652fb08962ec5b76af7f2c7bce1df7e1418c8055cf776f25", size = 14260638, upload-time = "2025-06-21T12:16:32.611Z" }, - { url = "https://files.pythonhosted.org/packages/6e/45/c51cb248e679a6c6ab14b7a8e3ead3f4a3fe7425fc7a6f98b3f147bec532/numpy-2.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e7cbf5a5eafd8d230a3ce356d892512185230e4781a361229bd902ff403bc660", size = 16632729, upload-time = "2025-06-21T12:16:57.439Z" }, - { url = "https://files.pythonhosted.org/packages/e4/ff/feb4be2e5c09a3da161b412019caf47183099cbea1132fd98061808c2df2/numpy-2.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f1b8f26d1086835f442286c1d9b64bb3974b0b1e41bb105358fd07d20872952", size = 15565330, upload-time = "2025-06-21T12:17:20.638Z" }, - { url = "https://files.pythonhosted.org/packages/bc/6d/ceafe87587101e9ab0d370e4f6e5f3f3a85b9a697f2318738e5e7e176ce3/numpy-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ee8340cb48c9b7a5899d1149eece41ca535513a9698098edbade2a8e7a84da77", size = 18361734, upload-time = "2025-06-21T12:17:47.938Z" }, - { url = "https://files.pythonhosted.org/packages/2b/19/0fb49a3ea088be691f040c9bf1817e4669a339d6e98579f91859b902c636/numpy-2.3.1-cp312-cp312-win32.whl", hash = "sha256:e772dda20a6002ef7061713dc1e2585bc1b534e7909b2030b5a46dae8ff077ab", size = 6320411, upload-time = "2025-06-21T12:17:58.475Z" }, - { url = "https://files.pythonhosted.org/packages/b1/3e/e28f4c1dd9e042eb57a3eb652f200225e311b608632bc727ae378623d4f8/numpy-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfecc7822543abdea6de08758091da655ea2210b8ffa1faf116b940693d3df76", size = 12734973, upload-time = "2025-06-21T12:18:17.601Z" }, - { url = "https://files.pythonhosted.org/packages/04/a8/8a5e9079dc722acf53522b8f8842e79541ea81835e9b5483388701421073/numpy-2.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:7be91b2239af2658653c5bb6f1b8bccafaf08226a258caf78ce44710a0160d30", size = 10191491, upload-time = "2025-06-21T12:18:33.585Z" }, - { url = "https://files.pythonhosted.org/packages/d4/bd/35ad97006d8abff8631293f8ea6adf07b0108ce6fec68da3c3fcca1197f2/numpy-2.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25a1992b0a3fdcdaec9f552ef10d8103186f5397ab45e2d25f8ac51b1a6b97e8", size = 20889381, upload-time = "2025-06-21T12:19:04.103Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4f/df5923874d8095b6062495b39729178eef4a922119cee32a12ee1bd4664c/numpy-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7dea630156d39b02a63c18f508f85010230409db5b2927ba59c8ba4ab3e8272e", size = 14152726, upload-time = "2025-06-21T12:19:25.599Z" }, - { url = "https://files.pythonhosted.org/packages/8c/0f/a1f269b125806212a876f7efb049b06c6f8772cf0121139f97774cd95626/numpy-2.3.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:bada6058dd886061f10ea15f230ccf7dfff40572e99fef440a4a857c8728c9c0", size = 5105145, upload-time = "2025-06-21T12:19:34.782Z" }, - { url = "https://files.pythonhosted.org/packages/6d/63/a7f7fd5f375b0361682f6ffbf686787e82b7bbd561268e4f30afad2bb3c0/numpy-2.3.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:a894f3816eb17b29e4783e5873f92faf55b710c2519e5c351767c51f79d8526d", size = 6639409, upload-time = "2025-06-21T12:19:45.228Z" }, - { url = "https://files.pythonhosted.org/packages/bf/0d/1854a4121af895aab383f4aa233748f1df4671ef331d898e32426756a8a6/numpy-2.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:18703df6c4a4fee55fd3d6e5a253d01c5d33a295409b03fda0c86b3ca2ff41a1", size = 14257630, upload-time = "2025-06-21T12:20:06.544Z" }, - { url = "https://files.pythonhosted.org/packages/50/30/af1b277b443f2fb08acf1c55ce9d68ee540043f158630d62cef012750f9f/numpy-2.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5902660491bd7a48b2ec16c23ccb9124b8abfd9583c5fdfa123fe6b421e03de1", size = 16627546, upload-time = "2025-06-21T12:20:31.002Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ec/3b68220c277e463095342d254c61be8144c31208db18d3fd8ef02712bcd6/numpy-2.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:36890eb9e9d2081137bd78d29050ba63b8dab95dff7912eadf1185e80074b2a0", size = 15562538, upload-time = "2025-06-21T12:20:54.322Z" }, - { url = "https://files.pythonhosted.org/packages/77/2b/4014f2bcc4404484021c74d4c5ee8eb3de7e3f7ac75f06672f8dcf85140a/numpy-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a780033466159c2270531e2b8ac063704592a0bc62ec4a1b991c7c40705eb0e8", size = 18360327, upload-time = "2025-06-21T12:21:21.053Z" }, - { url = "https://files.pythonhosted.org/packages/40/8d/2ddd6c9b30fcf920837b8672f6c65590c7d92e43084c25fc65edc22e93ca/numpy-2.3.1-cp313-cp313-win32.whl", hash = "sha256:39bff12c076812595c3a306f22bfe49919c5513aa1e0e70fac756a0be7c2a2b8", size = 6312330, upload-time = "2025-06-21T12:25:07.447Z" }, - { url = "https://files.pythonhosted.org/packages/dd/c8/beaba449925988d415efccb45bf977ff8327a02f655090627318f6398c7b/numpy-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d5ee6eec45f08ce507a6570e06f2f879b374a552087a4179ea7838edbcbfa42", size = 12731565, upload-time = "2025-06-21T12:25:26.444Z" }, - { url = "https://files.pythonhosted.org/packages/0b/c3/5c0c575d7ec78c1126998071f58facfc124006635da75b090805e642c62e/numpy-2.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c4d9e0a8368db90f93bd192bfa771ace63137c3488d198ee21dfb8e7771916e", size = 10190262, upload-time = "2025-06-21T12:25:42.196Z" }, - { url = "https://files.pythonhosted.org/packages/ea/19/a029cd335cf72f79d2644dcfc22d90f09caa86265cbbde3b5702ccef6890/numpy-2.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b0b5397374f32ec0649dd98c652a1798192042e715df918c20672c62fb52d4b8", size = 20987593, upload-time = "2025-06-21T12:21:51.664Z" }, - { url = "https://files.pythonhosted.org/packages/25/91/8ea8894406209107d9ce19b66314194675d31761fe2cb3c84fe2eeae2f37/numpy-2.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c5bdf2015ccfcee8253fb8be695516ac4457c743473a43290fd36eba6a1777eb", size = 14300523, upload-time = "2025-06-21T12:22:13.583Z" }, - { url = "https://files.pythonhosted.org/packages/a6/7f/06187b0066eefc9e7ce77d5f2ddb4e314a55220ad62dd0bfc9f2c44bac14/numpy-2.3.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d70f20df7f08b90a2062c1f07737dd340adccf2068d0f1b9b3d56e2038979fee", size = 5227993, upload-time = "2025-06-21T12:22:22.53Z" }, - { url = "https://files.pythonhosted.org/packages/e8/ec/a926c293c605fa75e9cfb09f1e4840098ed46d2edaa6e2152ee35dc01ed3/numpy-2.3.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:2fb86b7e58f9ac50e1e9dd1290154107e47d1eef23a0ae9145ded06ea606f992", size = 6736652, upload-time = "2025-06-21T12:22:33.629Z" }, - { url = "https://files.pythonhosted.org/packages/e3/62/d68e52fb6fde5586650d4c0ce0b05ff3a48ad4df4ffd1b8866479d1d671d/numpy-2.3.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:23ab05b2d241f76cb883ce8b9a93a680752fbfcbd51c50eff0b88b979e471d8c", size = 14331561, upload-time = "2025-06-21T12:22:55.056Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ec/b74d3f2430960044bdad6900d9f5edc2dc0fb8bf5a0be0f65287bf2cbe27/numpy-2.3.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ce2ce9e5de4703a673e705183f64fd5da5bf36e7beddcb63a25ee2286e71ca48", size = 16693349, upload-time = "2025-06-21T12:23:20.53Z" }, - { url = "https://files.pythonhosted.org/packages/0d/15/def96774b9d7eb198ddadfcbd20281b20ebb510580419197e225f5c55c3e/numpy-2.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c4913079974eeb5c16ccfd2b1f09354b8fed7e0d6f2cab933104a09a6419b1ee", size = 15642053, upload-time = "2025-06-21T12:23:43.697Z" }, - { url = "https://files.pythonhosted.org/packages/2b/57/c3203974762a759540c6ae71d0ea2341c1fa41d84e4971a8e76d7141678a/numpy-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:010ce9b4f00d5c036053ca684c77441f2f2c934fd23bee058b4d6f196efd8280", size = 18434184, upload-time = "2025-06-21T12:24:10.708Z" }, - { url = "https://files.pythonhosted.org/packages/22/8a/ccdf201457ed8ac6245187850aff4ca56a79edbea4829f4e9f14d46fa9a5/numpy-2.3.1-cp313-cp313t-win32.whl", hash = "sha256:6269b9edfe32912584ec496d91b00b6d34282ca1d07eb10e82dfc780907d6c2e", size = 6440678, upload-time = "2025-06-21T12:24:21.596Z" }, - { url = "https://files.pythonhosted.org/packages/f1/7e/7f431d8bd8eb7e03d79294aed238b1b0b174b3148570d03a8a8a8f6a0da9/numpy-2.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:2a809637460e88a113e186e87f228d74ae2852a2e0c44de275263376f17b5bdc", size = 12870697, upload-time = "2025-06-21T12:24:40.644Z" }, - { url = "https://files.pythonhosted.org/packages/d4/ca/af82bf0fad4c3e573c6930ed743b5308492ff19917c7caaf2f9b6f9e2e98/numpy-2.3.1-cp313-cp313t-win_arm64.whl", hash = "sha256:eccb9a159db9aed60800187bc47a6d3451553f0e1b08b068d8b277ddfbb9b244", size = 10260376, upload-time = "2025-06-21T12:24:56.884Z" }, - { url = "https://files.pythonhosted.org/packages/e8/34/facc13b9b42ddca30498fc51f7f73c3d0f2be179943a4b4da8686e259740/numpy-2.3.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ad506d4b09e684394c42c966ec1527f6ebc25da7f4da4b1b056606ffe446b8a3", size = 21070637, upload-time = "2025-06-21T12:26:12.518Z" }, - { url = "https://files.pythonhosted.org/packages/65/b6/41b705d9dbae04649b529fc9bd3387664c3281c7cd78b404a4efe73dcc45/numpy-2.3.1-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:ebb8603d45bc86bbd5edb0d63e52c5fd9e7945d3a503b77e486bd88dde67a19b", size = 5304087, upload-time = "2025-06-21T12:26:22.294Z" }, - { url = "https://files.pythonhosted.org/packages/7a/b4/fe3ac1902bff7a4934a22d49e1c9d71a623204d654d4cc43c6e8fe337fcb/numpy-2.3.1-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:15aa4c392ac396e2ad3d0a2680c0f0dee420f9fed14eef09bdb9450ee6dcb7b7", size = 6817588, upload-time = "2025-06-21T12:26:32.939Z" }, - { url = "https://files.pythonhosted.org/packages/ae/ee/89bedf69c36ace1ac8f59e97811c1f5031e179a37e4821c3a230bf750142/numpy-2.3.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c6e0bf9d1a2f50d2b65a7cf56db37c095af17b59f6c132396f7c6d5dd76484df", size = 14399010, upload-time = "2025-06-21T12:26:54.086Z" }, - { url = "https://files.pythonhosted.org/packages/15/08/e00e7070ede29b2b176165eba18d6f9784d5349be3c0c1218338e79c27fd/numpy-2.3.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eabd7e8740d494ce2b4ea0ff05afa1b7b291e978c0ae075487c51e8bd93c0c68", size = 16752042, upload-time = "2025-06-21T12:27:19.018Z" }, - { url = "https://files.pythonhosted.org/packages/48/6b/1c6b515a83d5564b1698a61efa245727c8feecf308f4091f565988519d20/numpy-2.3.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e610832418a2bc09d974cc9fecebfa51e9532d6190223bc5ef6a7402ebf3b5cb", size = 12927246, upload-time = "2025-06-21T12:27:38.618Z" }, -] - -[[package]] -name = "nvidia-cublas-cu12" -version = "12.6.4.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/eb/ff4b8c503fa1f1796679dce648854d58751982426e4e4b37d6fce49d259c/nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08ed2686e9875d01b58e3cb379c6896df8e76c75e0d4a7f7dace3d7b6d9ef8eb", size = 393138322, upload-time = "2024-11-20T17:40:25.65Z" }, -] - -[[package]] -name = "nvidia-cuda-cupti-cu12" -version = "12.6.80" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/60/7b6497946d74bcf1de852a21824d63baad12cd417db4195fc1bfe59db953/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6768bad6cab4f19e8292125e5f1ac8aa7d1718704012a0e3272a6f61c4bce132", size = 8917980, upload-time = "2024-11-20T17:36:04.019Z" }, - { url = "https://files.pythonhosted.org/packages/a5/24/120ee57b218d9952c379d1e026c4479c9ece9997a4fb46303611ee48f038/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a3eff6cdfcc6a4c35db968a06fcadb061cbc7d6dde548609a941ff8701b98b73", size = 8917972, upload-time = "2024-10-01T16:58:06.036Z" }, -] - -[[package]] -name = "nvidia-cuda-nvrtc-cu12" -version = "12.6.77" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/2e/46030320b5a80661e88039f59060d1790298b4718944a65a7f2aeda3d9e9/nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:35b0cc6ee3a9636d5409133e79273ce1f3fd087abb0532d2d2e8fff1fe9efc53", size = 23650380, upload-time = "2024-10-01T17:00:14.643Z" }, -] - -[[package]] -name = "nvidia-cuda-runtime-cu12" -version = "12.6.77" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/23/e717c5ac26d26cf39a27fbc076240fad2e3b817e5889d671b67f4f9f49c5/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ba3b56a4f896141e25e19ab287cd71e52a6a0f4b29d0d31609f60e3b4d5219b7", size = 897690, upload-time = "2024-11-20T17:35:30.697Z" }, - { url = "https://files.pythonhosted.org/packages/f0/62/65c05e161eeddbafeca24dc461f47de550d9fa8a7e04eb213e32b55cfd99/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a84d15d5e1da416dd4774cb42edf5e954a3e60cc945698dc1d5be02321c44dc8", size = 897678, upload-time = "2024-10-01T16:57:33.821Z" }, -] - -[[package]] -name = "nvidia-cudnn-cu12" -version = "9.5.1.17" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/78/4535c9c7f859a64781e43c969a3a7e84c54634e319a996d43ef32ce46f83/nvidia_cudnn_cu12-9.5.1.17-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:30ac3869f6db17d170e0e556dd6cc5eee02647abc31ca856634d5a40f82c15b2", size = 570988386, upload-time = "2024-10-25T19:54:26.39Z" }, -] - -[[package]] -name = "nvidia-cufft-cu12" -version = "11.3.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/16/73727675941ab8e6ffd86ca3a4b7b47065edcca7a997920b831f8147c99d/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ccba62eb9cef5559abd5e0d54ceed2d9934030f51163df018532142a8ec533e5", size = 200221632, upload-time = "2024-11-20T17:41:32.357Z" }, - { url = "https://files.pythonhosted.org/packages/60/de/99ec247a07ea40c969d904fc14f3a356b3e2a704121675b75c366b694ee1/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.whl", hash = "sha256:768160ac89f6f7b459bee747e8d175dbf53619cfe74b2a5636264163138013ca", size = 200221622, upload-time = "2024-10-01T17:03:58.79Z" }, -] - -[[package]] -name = "nvidia-cufile-cu12" -version = "1.11.1.6" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/66/cc9876340ac68ae71b15c743ddb13f8b30d5244af344ec8322b449e35426/nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc23469d1c7e52ce6c1d55253273d32c565dd22068647f3aa59b3c6b005bf159", size = 1142103, upload-time = "2024-11-20T17:42:11.83Z" }, -] - -[[package]] -name = "nvidia-curand-cu12" -version = "10.3.7.77" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/73/1b/44a01c4e70933637c93e6e1a8063d1e998b50213a6b65ac5a9169c47e98e/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a42cd1344297f70b9e39a1e4f467a4e1c10f1da54ff7a85c12197f6c652c8bdf", size = 56279010, upload-time = "2024-11-20T17:42:50.958Z" }, - { url = "https://files.pythonhosted.org/packages/4a/aa/2c7ff0b5ee02eaef890c0ce7d4f74bc30901871c5e45dee1ae6d0083cd80/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:99f1a32f1ac2bd134897fc7a203f779303261268a65762a623bf30cc9fe79117", size = 56279000, upload-time = "2024-10-01T17:04:45.274Z" }, -] - -[[package]] -name = "nvidia-cusolver-cu12" -version = "11.7.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12" }, - { name = "nvidia-cusparse-cu12" }, - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/6e/c2cf12c9ff8b872e92b4a5740701e51ff17689c4d726fca91875b07f655d/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9e49843a7707e42022babb9bcfa33c29857a93b88020c4e4434656a655b698c", size = 158229790, upload-time = "2024-11-20T17:43:43.211Z" }, - { url = "https://files.pythonhosted.org/packages/9f/81/baba53585da791d043c10084cf9553e074548408e04ae884cfe9193bd484/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6cf28f17f64107a0c4d7802be5ff5537b2130bfc112f25d5a30df227058ca0e6", size = 158229780, upload-time = "2024-10-01T17:05:39.875Z" }, -] - -[[package]] -name = "nvidia-cusparse-cu12" -version = "12.5.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/06/1e/b8b7c2f4099a37b96af5c9bb158632ea9e5d9d27d7391d7eb8fc45236674/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7556d9eca156e18184b94947ade0fba5bb47d69cec46bf8660fd2c71a4b48b73", size = 216561367, upload-time = "2024-11-20T17:44:54.824Z" }, - { url = "https://files.pythonhosted.org/packages/43/ac/64c4316ba163e8217a99680c7605f779accffc6a4bcd0c778c12948d3707/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:23749a6571191a215cb74d1cdbff4a86e7b19f1200c071b3fcf844a5bea23a2f", size = 216561357, upload-time = "2024-10-01T17:06:29.861Z" }, -] - -[[package]] -name = "nvidia-cusparselt-cu12" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/9a/72ef35b399b0e183bc2e8f6f558036922d453c4d8237dab26c666a04244b/nvidia_cusparselt_cu12-0.6.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e5c8a26c36445dd2e6812f1177978a24e2d37cacce7e090f297a688d1ec44f46", size = 156785796, upload-time = "2024-10-15T21:29:17.709Z" }, -] - -[[package]] -name = "nvidia-nccl-cu12" -version = "2.26.2" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/ca/f42388aed0fddd64ade7493dbba36e1f534d4e6fdbdd355c6a90030ae028/nvidia_nccl_cu12-2.26.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:694cf3879a206553cc9d7dbda76b13efaf610fdb70a50cba303de1b0d1530ac6", size = 201319755, upload-time = "2025-03-13T00:29:55.296Z" }, -] - -[[package]] -name = "nvidia-nvjitlink-cu12" -version = "12.6.85" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/d7/c5383e47c7e9bf1c99d5bd2a8c935af2b6d705ad831a7ec5c97db4d82f4f/nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:eedc36df9e88b682efe4309aa16b5b4e78c2407eac59e8c10a6a47535164369a", size = 19744971, upload-time = "2024-11-20T17:46:53.366Z" }, -] - -[[package]] -name = "nvidia-nvtx-cu12" -version = "12.6.77" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/9a/fff8376f8e3d084cd1530e1ef7b879bb7d6d265620c95c1b322725c694f4/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b90bed3df379fa79afbd21be8e04a0314336b8ae16768b58f2d34cb1d04cd7d2", size = 89276, upload-time = "2024-11-20T17:38:27.621Z" }, - { url = "https://files.pythonhosted.org/packages/9e/4e/0d0c945463719429b7bd21dece907ad0bde437a2ff12b9b12fee94722ab0/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6574241a3ec5fdc9334353ab8c479fe75841dbe8f4532a8fc97ce63503330ba1", size = 89265, upload-time = "2024-10-01T17:00:38.172Z" }, -] - -[[package]] -name = "oauthlib" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" }, -] - -[[package]] -name = "onnxruntime" -version = "1.22.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coloredlogs" }, - { name = "flatbuffers" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "sympy" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/b9/664a1ffee62fa51529fac27b37409d5d28cadee8d97db806fcba68339b7e/onnxruntime-1.22.1-cp310-cp310-macosx_13_0_universal2.whl", hash = "sha256:80e7f51da1f5201c1379b8d6ef6170505cd800e40da216290f5e06be01aadf95", size = 34319864, upload-time = "2025-07-10T19:15:15.371Z" }, - { url = "https://files.pythonhosted.org/packages/b9/64/bc7221e92c994931024e22b22401b962c299e991558c3d57f7e34538b4b9/onnxruntime-1.22.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89ddfdbbdaf7e3a59515dee657f6515601d55cb21a0f0f48c81aefc54ff1b73", size = 14472246, upload-time = "2025-07-10T19:15:19.403Z" }, - { url = "https://files.pythonhosted.org/packages/84/57/901eddbfb59ac4d008822b236450d5765cafcd450c787019416f8d3baf11/onnxruntime-1.22.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bddc75868bcf6f9ed76858a632f65f7b1846bdcefc6d637b1e359c2c68609964", size = 16459905, upload-time = "2025-07-10T19:15:21.749Z" }, - { url = "https://files.pythonhosted.org/packages/de/90/d6a1eb9b47e66a18afe7d1cf7cf0b2ef966ffa6f44d9f32d94c2be2860fb/onnxruntime-1.22.1-cp310-cp310-win_amd64.whl", hash = "sha256:01e2f21b2793eb0c8642d2be3cee34cc7d96b85f45f6615e4e220424158877ce", size = 12689001, upload-time = "2025-07-10T19:15:23.848Z" }, - { url = "https://files.pythonhosted.org/packages/82/ff/4a1a6747e039ef29a8d4ee4510060e9a805982b6da906a3da2306b7a3be6/onnxruntime-1.22.1-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:f4581bccb786da68725d8eac7c63a8f31a89116b8761ff8b4989dc58b61d49a0", size = 34324148, upload-time = "2025-07-10T19:15:26.584Z" }, - { url = "https://files.pythonhosted.org/packages/0b/05/9f1929723f1cca8c9fb1b2b97ac54ce61362c7201434d38053ea36ee4225/onnxruntime-1.22.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ae7526cf10f93454beb0f751e78e5cb7619e3b92f9fc3bd51aa6f3b7a8977e5", size = 14473779, upload-time = "2025-07-10T19:15:30.183Z" }, - { url = "https://files.pythonhosted.org/packages/59/f3/c93eb4167d4f36ea947930f82850231f7ce0900cb00e1a53dc4995b60479/onnxruntime-1.22.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f6effa1299ac549a05c784d50292e3378dbbf010346ded67400193b09ddc2f04", size = 16460799, upload-time = "2025-07-10T19:15:33.005Z" }, - { url = "https://files.pythonhosted.org/packages/a8/01/e536397b03e4462d3260aee5387e6f606c8fa9d2b20b1728f988c3c72891/onnxruntime-1.22.1-cp311-cp311-win_amd64.whl", hash = "sha256:f28a42bb322b4ca6d255531bb334a2b3e21f172e37c1741bd5e66bc4b7b61f03", size = 12689881, upload-time = "2025-07-10T19:15:35.501Z" }, - { url = "https://files.pythonhosted.org/packages/48/70/ca2a4d38a5deccd98caa145581becb20c53684f451e89eb3a39915620066/onnxruntime-1.22.1-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:a938d11c0dc811badf78e435daa3899d9af38abee950d87f3ab7430eb5b3cf5a", size = 34342883, upload-time = "2025-07-10T19:15:38.223Z" }, - { url = "https://files.pythonhosted.org/packages/29/e5/00b099b4d4f6223b610421080d0eed9327ef9986785c9141819bbba0d396/onnxruntime-1.22.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:984cea2a02fcc5dfea44ade9aca9fe0f7a8a2cd6f77c258fc4388238618f3928", size = 14473861, upload-time = "2025-07-10T19:15:42.911Z" }, - { url = "https://files.pythonhosted.org/packages/0a/50/519828a5292a6ccd8d5cd6d2f72c6b36ea528a2ef68eca69647732539ffa/onnxruntime-1.22.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2d39a530aff1ec8d02e365f35e503193991417788641b184f5b1e8c9a6d5ce8d", size = 16475713, upload-time = "2025-07-10T19:15:45.452Z" }, - { url = "https://files.pythonhosted.org/packages/5d/54/7139d463bb0a312890c9a5db87d7815d4a8cce9e6f5f28d04f0b55fcb160/onnxruntime-1.22.1-cp312-cp312-win_amd64.whl", hash = "sha256:6a64291d57ea966a245f749eb970f4fa05a64d26672e05a83fdb5db6b7d62f87", size = 12690910, upload-time = "2025-07-10T19:15:47.478Z" }, - { url = "https://files.pythonhosted.org/packages/e0/39/77cefa829740bd830915095d8408dce6d731b244e24b1f64fe3df9f18e86/onnxruntime-1.22.1-cp313-cp313-macosx_13_0_universal2.whl", hash = "sha256:d29c7d87b6cbed8fecfd09dca471832384d12a69e1ab873e5effbb94adc3e966", size = 34342026, upload-time = "2025-07-10T19:15:50.266Z" }, - { url = "https://files.pythonhosted.org/packages/d2/a6/444291524cb52875b5de980a6e918072514df63a57a7120bf9dfae3aeed1/onnxruntime-1.22.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:460487d83b7056ba98f1f7bac80287224c31d8149b15712b0d6f5078fcc33d0f", size = 14474014, upload-time = "2025-07-10T19:15:53.991Z" }, - { url = "https://files.pythonhosted.org/packages/87/9d/45a995437879c18beff26eacc2322f4227224d04c6ac3254dce2e8950190/onnxruntime-1.22.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b0c37070268ba4e02a1a9d28560cd00cd1e94f0d4f275cbef283854f861a65fa", size = 16475427, upload-time = "2025-07-10T19:15:56.067Z" }, - { url = "https://files.pythonhosted.org/packages/4c/06/9c765e66ad32a7e709ce4cb6b95d7eaa9cb4d92a6e11ea97c20ffecaf765/onnxruntime-1.22.1-cp313-cp313-win_amd64.whl", hash = "sha256:70980d729145a36a05f74b573435531f55ef9503bcda81fc6c3d6b9306199982", size = 12690841, upload-time = "2025-07-10T19:15:58.337Z" }, - { url = "https://files.pythonhosted.org/packages/52/8c/02af24ee1c8dce4e6c14a1642a7a56cebe323d2fa01d9a360a638f7e4b75/onnxruntime-1.22.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33a7980bbc4b7f446bac26c3785652fe8730ed02617d765399e89ac7d44e0f7d", size = 14479333, upload-time = "2025-07-10T19:16:00.544Z" }, - { url = "https://files.pythonhosted.org/packages/5d/15/d75fd66aba116ce3732bb1050401394c5ec52074c4f7ee18db8838dd4667/onnxruntime-1.22.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7e823624b015ea879d976cbef8bfaed2f7e2cc233d7506860a76dd37f8f381", size = 16477261, upload-time = "2025-07-10T19:16:03.226Z" }, -] - -[[package]] -name = "openai" -version = "1.97.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a6/57/1c471f6b3efb879d26686d31582997615e969f3bb4458111c9705e56332e/openai-1.97.1.tar.gz", hash = "sha256:a744b27ae624e3d4135225da9b1c89c107a2a7e5bc4c93e5b7b5214772ce7a4e", size = 494267, upload-time = "2025-07-22T13:10:12.607Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/35/412a0e9c3f0d37c94ed764b8ac7adae2d834dbd20e69f6aca582118e0f55/openai-1.97.1-py3-none-any.whl", hash = "sha256:4e96bbdf672ec3d44968c9ea39d2c375891db1acc1794668d8149d5fa6000606", size = 764380, upload-time = "2025-07-22T13:10:10.689Z" }, -] - -[[package]] -name = "opentelemetry-api" -version = "1.35.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/99/c9/4509bfca6bb43220ce7f863c9f791e0d5001c2ec2b5867d48586008b3d96/opentelemetry_api-1.35.0.tar.gz", hash = "sha256:a111b959bcfa5b4d7dffc2fbd6a241aa72dd78dd8e79b5b1662bda896c5d2ffe", size = 64778, upload-time = "2025-07-11T12:23:28.804Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/5a/3f8d078dbf55d18442f6a2ecedf6786d81d7245844b2b20ce2b8ad6f0307/opentelemetry_api-1.35.0-py3-none-any.whl", hash = "sha256:c4ea7e258a244858daf18474625e9cc0149b8ee354f37843415771a40c25ee06", size = 65566, upload-time = "2025-07-11T12:23:07.944Z" }, -] - -[[package]] -name = "opentelemetry-exporter-otlp-proto-common" -version = "1.35.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-proto" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/56/d1/887f860529cba7fc3aba2f6a3597fefec010a17bd1b126810724707d9b51/opentelemetry_exporter_otlp_proto_common-1.35.0.tar.gz", hash = "sha256:6f6d8c39f629b9fa5c79ce19a2829dbd93034f8ac51243cdf40ed2196f00d7eb", size = 20299, upload-time = "2025-07-11T12:23:31.046Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/2c/e31dd3c719bff87fa77391eb7f38b1430d22868c52312cba8aad60f280e5/opentelemetry_exporter_otlp_proto_common-1.35.0-py3-none-any.whl", hash = "sha256:863465de697ae81279ede660f3918680b4480ef5f69dcdac04f30722ed7b74cc", size = 18349, upload-time = "2025-07-11T12:23:11.713Z" }, -] - -[[package]] -name = "opentelemetry-exporter-otlp-proto-grpc" -version = "1.35.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "googleapis-common-protos" }, - { name = "grpcio" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-otlp-proto-common" }, - { name = "opentelemetry-proto" }, - { name = "opentelemetry-sdk" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/20/de/222e4f2f8cd39250991f84d76b661534aef457cafc6a3eb3fcd513627698/opentelemetry_exporter_otlp_proto_grpc-1.35.0.tar.gz", hash = "sha256:ac4c2c3aa5674642db0df0091ab43ec08bbd91a9be469c8d9b18923eb742b9cc", size = 23794, upload-time = "2025-07-11T12:23:31.662Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/a6/3f60a77279e6a3dc21fc076dcb51be159a633b0bba5cba9fb804062a9332/opentelemetry_exporter_otlp_proto_grpc-1.35.0-py3-none-any.whl", hash = "sha256:ee31203eb3e50c7967b8fa71db366cc355099aca4e3726e489b248cdb2fd5a62", size = 18846, upload-time = "2025-07-11T12:23:12.957Z" }, -] - -[[package]] -name = "opentelemetry-proto" -version = "1.35.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dc/a2/7366e32d9a2bccbb8614942dbea2cf93c209610385ea966cb050334f8df7/opentelemetry_proto-1.35.0.tar.gz", hash = "sha256:532497341bd3e1c074def7c5b00172601b28bb83b48afc41a4b779f26eb4ee05", size = 46151, upload-time = "2025-07-11T12:23:38.797Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/a7/3f05de580da7e8a8b8dff041d3d07a20bf3bb62d3bcc027f8fd669a73ff4/opentelemetry_proto-1.35.0-py3-none-any.whl", hash = "sha256:98fffa803164499f562718384e703be8d7dfbe680192279a0429cb150a2f8809", size = 72536, upload-time = "2025-07-11T12:23:23.247Z" }, -] - -[[package]] -name = "opentelemetry-sdk" -version = "1.35.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9a/cf/1eb2ed2ce55e0a9aa95b3007f26f55c7943aeef0a783bb006bdd92b3299e/opentelemetry_sdk-1.35.0.tar.gz", hash = "sha256:2a400b415ab68aaa6f04e8a6a9f6552908fb3090ae2ff78d6ae0c597ac581954", size = 160871, upload-time = "2025-07-11T12:23:39.566Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/4f/8e32b757ef3b660511b638ab52d1ed9259b666bdeeceba51a082ce3aea95/opentelemetry_sdk-1.35.0-py3-none-any.whl", hash = "sha256:223d9e5f5678518f4842311bb73966e0b6db5d1e0b74e35074c052cd2487f800", size = 119379, upload-time = "2025-07-11T12:23:24.521Z" }, -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.56b0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/32/8e/214fa817f63b9f068519463d8ab46afd5d03b98930c39394a37ae3e741d0/opentelemetry_semantic_conventions-0.56b0.tar.gz", hash = "sha256:c114c2eacc8ff6d3908cb328c811eaf64e6d68623840be9224dc829c4fd6c2ea", size = 124221, upload-time = "2025-07-11T12:23:40.71Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/3f/e80c1b017066a9d999efffe88d1cce66116dcf5cb7f80c41040a83b6e03b/opentelemetry_semantic_conventions-0.56b0-py3-none-any.whl", hash = "sha256:df44492868fd6b482511cc43a942e7194be64e94945f572db24df2e279a001a2", size = 201625, upload-time = "2025-07-11T12:23:25.63Z" }, -] - -[[package]] -name = "orjson" -version = "3.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/87/03ababa86d984952304ac8ce9fbd3a317afb4a225b9a81f9b606ac60c873/orjson-3.11.0.tar.gz", hash = "sha256:2e4c129da624f291bcc607016a99e7f04a353f6874f3bd8d9b47b88597d5f700", size = 5318246, upload-time = "2025-07-15T16:08:29.194Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/aa/50818f480f0edcb33290c8f35eef6dd3a31e2ff7e1195f8b236ac7419811/orjson-3.11.0-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b8913baba9751f7400f8fa4ec18a8b618ff01177490842e39e47b66c1b04bc79", size = 240422, upload-time = "2025-07-15T16:06:23.029Z" }, - { url = "https://files.pythonhosted.org/packages/16/50/5235aff455fa76337493d21e68618e7cf53aa9db011aaeb06cf378f1344c/orjson-3.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d4d86910554de5c9c87bc560b3bdd315cc3988adbdc2acf5dda3797079407ed", size = 132473, upload-time = "2025-07-15T16:06:25.598Z" }, - { url = "https://files.pythonhosted.org/packages/23/93/bf1c4e77e7affc46cca13fb852842a86dca2dabbee1d91515ed17b1c21c4/orjson-3.11.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:84ae3d329360cf18fb61b67c505c00dedb61b0ee23abfd50f377a58e7d7bed06", size = 127195, upload-time = "2025-07-15T16:06:27.001Z" }, - { url = "https://files.pythonhosted.org/packages/7e/2d/64b52c6827e43aa3d98def19e188e091a6c574ca13d9ecef5f3f3284fac6/orjson-3.11.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47a54e660414baacd71ebf41a69bb17ea25abb3c5b69ce9e13e43be7ac20e342", size = 128895, upload-time = "2025-07-15T16:06:28.641Z" }, - { url = "https://files.pythonhosted.org/packages/ca/5f/9d290bc7a88392f9f7dc2e92ceb2e3efbbebaaf56bbba655b5fe2e3d2ca3/orjson-3.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2560b740604751854be146169c1de7e7ee1e6120b00c1788ec3f3a012c6a243f", size = 132016, upload-time = "2025-07-15T16:06:32.576Z" }, - { url = "https://files.pythonhosted.org/packages/ef/8c/b2bdc34649bbb7b44827d487aef7ad4d6a96c53ebc490ddcc191d47bc3b9/orjson-3.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd7f9cd995da9e46fbac0a371f0ff6e89a21d8ecb7a8a113c0acb147b0a32f73", size = 134251, upload-time = "2025-07-15T16:06:34.075Z" }, - { url = "https://files.pythonhosted.org/packages/33/be/b763b602976aa27407e6f75331ac581258c719f8abb70f66f2de962f649f/orjson-3.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cf728cb3a013bdf9f4132575404bf885aa773d8bb4205656575e1890fc91990", size = 128078, upload-time = "2025-07-15T16:06:35.408Z" }, - { url = "https://files.pythonhosted.org/packages/ac/24/1b0fed70392bf179ac8b5abe800f1102ed94f89ac4f889d83916947a2b4e/orjson-3.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c27de273320294121200440cd5002b6aeb922d3cb9dab3357087c69f04ca6934", size = 130734, upload-time = "2025-07-15T16:06:36.832Z" }, - { url = "https://files.pythonhosted.org/packages/05/d2/2d042bb4fe1da067692cb70d8c01a5ce2737e2f56444e6b2d716853ce8c3/orjson-3.11.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4430ec6ff1a1f4595dd7e0fad991bdb2fed65401ed294984c490ffa025926325", size = 404040, upload-time = "2025-07-15T16:06:38.259Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c5/54938ab416c0d19c93f0d6977a47bb2b3d121e150305380b783f7d6da185/orjson-3.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:325be41a8d7c227d460a9795a181511ba0e731cf3fee088c63eb47e706ea7559", size = 144808, upload-time = "2025-07-15T16:06:39.796Z" }, - { url = "https://files.pythonhosted.org/packages/6d/be/5ead422f396ee7c8941659ceee3da001e26998971f7d5fe0a38519c48aa5/orjson-3.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9760217b84d1aee393b4436fbe9c639e963ec7bc0f2c074581ce5fb3777e466", size = 132570, upload-time = "2025-07-15T16:06:41.209Z" }, - { url = "https://files.pythonhosted.org/packages/f6/01/db8352f7d0374d7eec25144e294991800aa85738b2dc7f19cc152ba1b254/orjson-3.11.0-cp310-cp310-win32.whl", hash = "sha256:fe36e5012f886ff91c68b87a499c227fa220e9668cea96335219874c8be5fab5", size = 134763, upload-time = "2025-07-15T16:06:42.524Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f5/1322b64d5836d92f0b0c119d959853b3c968b8aae23dd1e3c1bfa566823b/orjson-3.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:ebeecd5d5511b3ca9dc4e7db0ab95266afd41baf424cc2fad8c2d3a3cdae650a", size = 129506, upload-time = "2025-07-15T16:06:43.929Z" }, - { url = "https://files.pythonhosted.org/packages/f9/2c/0b71a763f0f5130aa2631ef79e2cd84d361294665acccbb12b7a9813194e/orjson-3.11.0-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1785df7ada75c18411ff7e20ac822af904a40161ea9dfe8c55b3f6b66939add6", size = 240007, upload-time = "2025-07-15T16:06:45.411Z" }, - { url = "https://files.pythonhosted.org/packages/f4/5a/f79ccd63d378b9c7c771d7a54c203d261b4c618fe3034ae95cd30f934f34/orjson-3.11.0-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:a57899bebbcea146616a2426d20b51b3562b4bc9f8039a3bd14fae361c23053d", size = 129320, upload-time = "2025-07-15T16:06:47.249Z" }, - { url = "https://files.pythonhosted.org/packages/7b/8a/63dafc147fa5ba945ad809c374b8f4ee692bb6b18aa6e161c3e6b69b594e/orjson-3.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fbc2fc825aff1456dd358c11a0ad7912a4cb4537d3db92e5334af7463a967", size = 132254, upload-time = "2025-07-15T16:06:48.597Z" }, - { url = "https://files.pythonhosted.org/packages/3c/11/4d1eb230483cc689a2f039c531bb2c980029c40ca5a9b5f64dce9786e955/orjson-3.11.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4305a638f4cf9bed3746ca3b7c242f14e05177d5baec2527026e0f9ee6c24fb7", size = 127003, upload-time = "2025-07-15T16:06:50.34Z" }, - { url = "https://files.pythonhosted.org/packages/4f/39/b6e96072946d908684e0f4b3de1639062fd5b32016b2929c035bd8e5c847/orjson-3.11.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1235fe7bbc37164f69302199d46f29cfb874018738714dccc5a5a44042c79c77", size = 128674, upload-time = "2025-07-15T16:06:51.659Z" }, - { url = "https://files.pythonhosted.org/packages/1e/dd/c77e3013f35b202ec2cc1f78a95fadf86b8c5a320d56eb1a0bbb965a87bb/orjson-3.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a640e3954e7b4fcb160097551e54cafbde9966be3991932155b71071077881aa", size = 131846, upload-time = "2025-07-15T16:06:53.359Z" }, - { url = "https://files.pythonhosted.org/packages/3f/7d/d83f0f96c2b142f9cdcf12df19052ea3767970989dc757598dc108db208f/orjson-3.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d750b97d22d5566955e50b02c622f3a1d32744d7a578c878b29a873190ccb7a", size = 134016, upload-time = "2025-07-15T16:06:54.691Z" }, - { url = "https://files.pythonhosted.org/packages/67/4f/d22f79a3c56dde563c4fbc12eebf9224a1b87af5e4ec61beb11f9b3eb499/orjson-3.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bfcfe498484161e011f8190a400591c52b026de96b3b3cbd3f21e8999b9dc0e", size = 127930, upload-time = "2025-07-15T16:06:56.001Z" }, - { url = "https://files.pythonhosted.org/packages/07/1e/26aede257db2163d974139fd4571f1e80f565216ccbd2c44ee1d43a63dcc/orjson-3.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:feaed3ed43a1d2df75c039798eb5ec92c350c7d86be53369bafc4f3700ce7df2", size = 130569, upload-time = "2025-07-15T16:06:57.275Z" }, - { url = "https://files.pythonhosted.org/packages/b4/bf/2cb57eac8d6054b555cba27203490489a7d3f5dca8c34382f22f2f0f17ba/orjson-3.11.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:aa1120607ec8fc98acf8c54aac6fb0b7b003ba883401fa2d261833111e2fa071", size = 403844, upload-time = "2025-07-15T16:06:59.107Z" }, - { url = "https://files.pythonhosted.org/packages/76/34/36e859ccfc45464df7b35c438c0ecc7751c930b3ebbefb50db7e3a641eb7/orjson-3.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c4b48d9775b0cf1f0aca734f4c6b272cbfacfac38e6a455e6520662f9434afb7", size = 144613, upload-time = "2025-07-15T16:07:00.48Z" }, - { url = "https://files.pythonhosted.org/packages/31/c5/5aeb84cdd0b44dc3972668944a1312f7983c2a45fb6b0e5e32b2f9408540/orjson-3.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f018ed1986d79434ac712ff19f951cd00b4dfcb767444410fbb834ebec160abf", size = 132419, upload-time = "2025-07-15T16:07:01.927Z" }, - { url = "https://files.pythonhosted.org/packages/59/0c/95ee1e61a067ad24c4921609156b3beeca8b102f6f36dca62b08e1a7c7a8/orjson-3.11.0-cp311-cp311-win32.whl", hash = "sha256:08e191f8a55ac2c00be48e98a5d10dca004cbe8abe73392c55951bfda60fc123", size = 134620, upload-time = "2025-07-15T16:07:03.304Z" }, - { url = "https://files.pythonhosted.org/packages/94/3e/afd5e284db9387023803553061ea05c785c36fe7845e4fe25912424b343f/orjson-3.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:b5a4214ea59c8a3b56f8d484b28114af74e9fba0956f9be5c3ce388ae143bf1f", size = 129333, upload-time = "2025-07-15T16:07:04.973Z" }, - { url = "https://files.pythonhosted.org/packages/8b/a4/d29e9995d73f23f2444b4db299a99477a4f7e6f5bf8923b775ef43a4e660/orjson-3.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:57e8e7198a679ab21241ab3f355a7990c7447559e35940595e628c107ef23736", size = 126656, upload-time = "2025-07-15T16:07:06.288Z" }, - { url = "https://files.pythonhosted.org/packages/92/c9/241e304fb1e58ea70b720f1a9e5349c6bb7735ffac401ef1b94f422edd6d/orjson-3.11.0-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b4089f940c638bb1947d54e46c1cd58f4259072fcc97bc833ea9c78903150ac9", size = 240269, upload-time = "2025-07-15T16:07:08.173Z" }, - { url = "https://files.pythonhosted.org/packages/26/7c/289457cdf40be992b43f1d90ae213ebc03a31a8e2850271ecd79e79a3135/orjson-3.11.0-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:8335a0ba1c26359fb5c82d643b4c1abbee2bc62875e0f2b5bde6c8e9e25eb68c", size = 129276, upload-time = "2025-07-15T16:07:10.128Z" }, - { url = "https://files.pythonhosted.org/packages/66/de/5c0528d46ded965939b6b7f75b1fe93af42b9906b0039096fc92c9001c12/orjson-3.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63c1c9772dafc811d16d6a7efa3369a739da15d1720d6e58ebe7562f54d6f4a2", size = 131966, upload-time = "2025-07-15T16:07:11.509Z" }, - { url = "https://files.pythonhosted.org/packages/ad/74/39822f267b5935fb6fc961ccc443f4968a74d34fc9270b83caa44e37d907/orjson-3.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9457ccbd8b241fb4ba516417a4c5b95ba0059df4ac801309bcb4ec3870f45ad9", size = 127028, upload-time = "2025-07-15T16:07:13.023Z" }, - { url = "https://files.pythonhosted.org/packages/7c/e3/28f6ed7f03db69bddb3ef48621b2b05b394125188f5909ee0a43fcf4820e/orjson-3.11.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0846e13abe79daece94a00b92574f294acad1d362be766c04245b9b4dd0e47e1", size = 129105, upload-time = "2025-07-15T16:07:14.367Z" }, - { url = "https://files.pythonhosted.org/packages/cb/50/8867fd2fc92c0ab1c3e14673ec5d9d0191202e4ab8ba6256d7a1d6943ad3/orjson-3.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5587c85ae02f608a3f377b6af9eb04829606f518257cbffa8f5081c1aacf2e2f", size = 131902, upload-time = "2025-07-15T16:07:16.176Z" }, - { url = "https://files.pythonhosted.org/packages/13/65/c189deea10342afee08006331082ff67d11b98c2394989998b3ea060354a/orjson-3.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c7a1964a71c1567b4570c932a0084ac24ad52c8cf6253d1881400936565ed438", size = 134042, upload-time = "2025-07-15T16:07:17.937Z" }, - { url = "https://files.pythonhosted.org/packages/2b/e4/cf23c3f4231d2a9a043940ab045f799f84a6df1b4fb6c9b4412cdc3ebf8c/orjson-3.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5a8243e73690cc6e9151c9e1dd046a8f21778d775f7d478fa1eb4daa4897c61", size = 128260, upload-time = "2025-07-15T16:07:19.651Z" }, - { url = "https://files.pythonhosted.org/packages/de/b9/2cb94d3a67edb918d19bad4a831af99cd96c3657a23daa239611bcf335d7/orjson-3.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:51646f6d995df37b6e1b628f092f41c0feccf1d47e3452c6e95e2474b547d842", size = 130282, upload-time = "2025-07-15T16:07:21.022Z" }, - { url = "https://files.pythonhosted.org/packages/0b/96/df963cc973e689d4c56398647917b4ee95f47e5b6d2779338c09c015b23b/orjson-3.11.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:2fb8ca8f0b4e31b8aaec674c7540649b64ef02809410506a44dc68d31bd5647b", size = 403765, upload-time = "2025-07-15T16:07:25.469Z" }, - { url = "https://files.pythonhosted.org/packages/fb/92/71429ee1badb69f53281602dbb270fa84fc2e51c83193a814d0208bb63b0/orjson-3.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:64a6a3e94a44856c3f6557e6aa56a6686544fed9816ae0afa8df9077f5759791", size = 144779, upload-time = "2025-07-15T16:07:27.339Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ab/3678b2e5ff0c622a974cb8664ed7cdda5ed26ae2b9d71ba66ec36f32d6cf/orjson-3.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d69f95d484938d8fab5963e09131bcf9fbbb81fa4ec132e316eb2fb9adb8ce78", size = 132797, upload-time = "2025-07-15T16:07:28.717Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/74509f715ff189d2aca90ebb0bd5af6658e0f9aa2512abbe6feca4c78208/orjson-3.11.0-cp312-cp312-win32.whl", hash = "sha256:8514f9f9c667ce7d7ef709ab1a73e7fcab78c297270e90b1963df7126d2b0e23", size = 134695, upload-time = "2025-07-15T16:07:30.034Z" }, - { url = "https://files.pythonhosted.org/packages/82/ba/ef25e3e223f452a01eac6a5b38d05c152d037508dcbf87ad2858cbb7d82e/orjson-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:41b38a894520b8cb5344a35ffafdf6ae8042f56d16771b2c5eb107798cee85ee", size = 129446, upload-time = "2025-07-15T16:07:31.412Z" }, - { url = "https://files.pythonhosted.org/packages/e3/cd/6f4d93867c5d81bb4ab2d4ac870d3d6e9ba34fa580a03b8d04bf1ce1d8ad/orjson-3.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:5579acd235dd134467340b2f8a670c1c36023b5a69c6a3174c4792af7502bd92", size = 126400, upload-time = "2025-07-15T16:07:34.143Z" }, - { url = "https://files.pythonhosted.org/packages/31/63/82d9b6b48624009d230bc6038e54778af8f84dfd54402f9504f477c5cfd5/orjson-3.11.0-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4a8ba9698655e16746fdf5266939427da0f9553305152aeb1a1cc14974a19cfb", size = 240125, upload-time = "2025-07-15T16:07:35.976Z" }, - { url = "https://files.pythonhosted.org/packages/16/3a/d557ed87c63237d4c97a7bac7ac054c347ab8c4b6da09748d162ca287175/orjson-3.11.0-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:67133847f9a35a5ef5acfa3325d4a2f7fe05c11f1505c4117bb086fc06f2a58f", size = 129189, upload-time = "2025-07-15T16:07:37.486Z" }, - { url = "https://files.pythonhosted.org/packages/69/5e/b2c9e22e2cd10aa7d76a629cee65d661e06a61fbaf4dc226386f5636dd44/orjson-3.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f797d57814975b78f5f5423acb003db6f9be5186b72d48bd97a1000e89d331d", size = 131953, upload-time = "2025-07-15T16:07:39.254Z" }, - { url = "https://files.pythonhosted.org/packages/e2/60/760fcd9b50eb44d1206f2b30c8d310b79714553b9d94a02f9ea3252ebe63/orjson-3.11.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:28acd19822987c5163b9e03a6e60853a52acfee384af2b394d11cb413b889246", size = 126922, upload-time = "2025-07-15T16:07:41.282Z" }, - { url = "https://files.pythonhosted.org/packages/6a/7a/8c46daa867ccc92da6de9567608be62052774b924a77c78382e30d50b579/orjson-3.11.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8d38d9e1e2cf9729658e35956cf01e13e89148beb4cb9e794c9c10c5cb252f8", size = 128787, upload-time = "2025-07-15T16:07:42.681Z" }, - { url = "https://files.pythonhosted.org/packages/f2/14/a2f1b123d85f11a19e8749f7d3f9ed6c9b331c61f7b47cfd3e9a1fedb9bc/orjson-3.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05f094edd2b782650b0761fd78858d9254de1c1286f5af43145b3d08cdacfd51", size = 131895, upload-time = "2025-07-15T16:07:44.519Z" }, - { url = "https://files.pythonhosted.org/packages/c8/10/362e8192df7528e8086ea712c5cb01355c8d4e52c59a804417ba01e2eb2d/orjson-3.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d09176a4a9e04a5394a4a0edd758f645d53d903b306d02f2691b97d5c736a9e", size = 133868, upload-time = "2025-07-15T16:07:46.227Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4e/ef43582ef3e3dfd2a39bc3106fa543364fde1ba58489841120219da6e22f/orjson-3.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a585042104e90a61eda2564d11317b6a304eb4e71cd33e839f5af6be56c34d3", size = 128234, upload-time = "2025-07-15T16:07:48.123Z" }, - { url = "https://files.pythonhosted.org/packages/d7/fa/02dabb2f1d605bee8c4bb1160cfc7467976b1ed359a62cc92e0681b53c45/orjson-3.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d2218629dbfdeeb5c9e0573d59f809d42f9d49ae6464d2f479e667aee14c3ef4", size = 130232, upload-time = "2025-07-15T16:07:50.197Z" }, - { url = "https://files.pythonhosted.org/packages/16/76/951b5619605c8d2ede80cc989f32a66abc954530d86e84030db2250c63a1/orjson-3.11.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:613e54a2b10b51b656305c11235a9c4a5c5491ef5c283f86483d4e9e123ed5e4", size = 403648, upload-time = "2025-07-15T16:07:52.136Z" }, - { url = "https://files.pythonhosted.org/packages/96/e2/5fa53bb411455a63b3713db90b588e6ca5ed2db59ad49b3fb8a0e94e0dda/orjson-3.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9dac7fbf3b8b05965986c5cfae051eb9a30fced7f15f1d13a5adc608436eb486", size = 144572, upload-time = "2025-07-15T16:07:54.004Z" }, - { url = "https://files.pythonhosted.org/packages/ad/d0/7d6f91e1e0f034258c3a3358f20b0c9490070e8a7ab8880085547274c7f9/orjson-3.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93b64b254414e2be55ac5257124b5602c5f0b4d06b80bd27d1165efe8f36e836", size = 132766, upload-time = "2025-07-15T16:07:55.936Z" }, - { url = "https://files.pythonhosted.org/packages/ff/f8/4d46481f1b3fb40dc826d62179f96c808eb470cdcc74b6593fb114d74af3/orjson-3.11.0-cp313-cp313-win32.whl", hash = "sha256:359cbe11bc940c64cb3848cf22000d2aef36aff7bfd09ca2c0b9cb309c387132", size = 134638, upload-time = "2025-07-15T16:07:57.343Z" }, - { url = "https://files.pythonhosted.org/packages/85/3f/544938dcfb7337d85ee1e43d7685cf8f3bfd452e0b15a32fe70cb4ca5094/orjson-3.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:0759b36428067dc777b202dd286fbdd33d7f261c6455c4238ea4e8474358b1e6", size = 129411, upload-time = "2025-07-15T16:07:58.852Z" }, - { url = "https://files.pythonhosted.org/packages/43/0c/f75015669d7817d222df1bb207f402277b77d22c4833950c8c8c7cf2d325/orjson-3.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:51cdca2f36e923126d0734efaf72ddbb5d6da01dbd20eab898bdc50de80d7b5a", size = 126349, upload-time = "2025-07-15T16:08:00.322Z" }, -] - -[[package]] -name = "overrides" -version = "7.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812, upload-time = "2024-01-27T21:01:33.423Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832, upload-time = "2024-01-27T21:01:31.393Z" }, -] - -[[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 = "paginate" -version = "0.5.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" }, -] - -[[package]] -name = "parso" -version = "0.8.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609, upload-time = "2024-04-05T09:43:55.897Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650, upload-time = "2024-04-05T09:43:53.299Z" }, -] - -[[package]] -name = "passlib" -version = "1.7.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/06/9da9ee59a67fae7761aab3ccc84fa4f3f33f125b370f1ccdb915bf967c11/passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04", size = 689844, upload-time = "2020-10-08T19:00:52.121Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/a4/ab6b7589382ca3df236e03faa71deac88cae040af60c071a78d254a62172/passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1", size = 525554, upload-time = "2020-10-08T19:00:49.856Z" }, -] - -[package.optional-dependencies] -bcrypt = [ - { name = "bcrypt" }, -] - -[[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 = "pexpect" -version = "4.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ptyprocess" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, -] - -[[package]] -name = "pillow" -version = "11.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/5d/45a3553a253ac8763f3561371432a90bdbe6000fbdcf1397ffe502aa206c/pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860", size = 5316554, upload-time = "2025-07-01T09:13:39.342Z" }, - { url = "https://files.pythonhosted.org/packages/7c/c8/67c12ab069ef586a25a4a79ced553586748fad100c77c0ce59bb4983ac98/pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad", size = 4686548, upload-time = "2025-07-01T09:13:41.835Z" }, - { url = "https://files.pythonhosted.org/packages/2f/bd/6741ebd56263390b382ae4c5de02979af7f8bd9807346d068700dd6d5cf9/pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0", size = 5859742, upload-time = "2025-07-03T13:09:47.439Z" }, - { url = "https://files.pythonhosted.org/packages/ca/0b/c412a9e27e1e6a829e6ab6c2dca52dd563efbedf4c9c6aa453d9a9b77359/pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b", size = 7633087, upload-time = "2025-07-03T13:09:51.796Z" }, - { url = "https://files.pythonhosted.org/packages/59/9d/9b7076aaf30f5dd17e5e5589b2d2f5a5d7e30ff67a171eb686e4eecc2adf/pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50", size = 5963350, upload-time = "2025-07-01T09:13:43.865Z" }, - { url = "https://files.pythonhosted.org/packages/f0/16/1a6bf01fb622fb9cf5c91683823f073f053005c849b1f52ed613afcf8dae/pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae", size = 6631840, upload-time = "2025-07-01T09:13:46.161Z" }, - { url = "https://files.pythonhosted.org/packages/7b/e6/6ff7077077eb47fde78739e7d570bdcd7c10495666b6afcd23ab56b19a43/pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9", size = 6074005, upload-time = "2025-07-01T09:13:47.829Z" }, - { url = "https://files.pythonhosted.org/packages/c3/3a/b13f36832ea6d279a697231658199e0a03cd87ef12048016bdcc84131601/pillow-11.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e", size = 6708372, upload-time = "2025-07-01T09:13:52.145Z" }, - { url = "https://files.pythonhosted.org/packages/6c/e4/61b2e1a7528740efbc70b3d581f33937e38e98ef3d50b05007267a55bcb2/pillow-11.3.0-cp310-cp310-win32.whl", hash = "sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6", size = 6277090, upload-time = "2025-07-01T09:13:53.915Z" }, - { url = "https://files.pythonhosted.org/packages/a9/d3/60c781c83a785d6afbd6a326ed4d759d141de43aa7365725cbcd65ce5e54/pillow-11.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f", size = 6985988, upload-time = "2025-07-01T09:13:55.699Z" }, - { url = "https://files.pythonhosted.org/packages/9f/28/4f4a0203165eefb3763939c6789ba31013a2e90adffb456610f30f613850/pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f", size = 2422899, upload-time = "2025-07-01T09:13:57.497Z" }, - { url = "https://files.pythonhosted.org/packages/db/26/77f8ed17ca4ffd60e1dcd220a6ec6d71210ba398cfa33a13a1cd614c5613/pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722", size = 5316531, upload-time = "2025-07-01T09:13:59.203Z" }, - { url = "https://files.pythonhosted.org/packages/cb/39/ee475903197ce709322a17a866892efb560f57900d9af2e55f86db51b0a5/pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288", size = 4686560, upload-time = "2025-07-01T09:14:01.101Z" }, - { url = "https://files.pythonhosted.org/packages/d5/90/442068a160fd179938ba55ec8c97050a612426fae5ec0a764e345839f76d/pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d", size = 5870978, upload-time = "2025-07-03T13:09:55.638Z" }, - { url = "https://files.pythonhosted.org/packages/13/92/dcdd147ab02daf405387f0218dcf792dc6dd5b14d2573d40b4caeef01059/pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494", size = 7641168, upload-time = "2025-07-03T13:10:00.37Z" }, - { url = "https://files.pythonhosted.org/packages/6e/db/839d6ba7fd38b51af641aa904e2960e7a5644d60ec754c046b7d2aee00e5/pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58", size = 5973053, upload-time = "2025-07-01T09:14:04.491Z" }, - { url = "https://files.pythonhosted.org/packages/f2/2f/d7675ecae6c43e9f12aa8d58b6012683b20b6edfbdac7abcb4e6af7a3784/pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f", size = 6640273, upload-time = "2025-07-01T09:14:06.235Z" }, - { url = "https://files.pythonhosted.org/packages/45/ad/931694675ede172e15b2ff03c8144a0ddaea1d87adb72bb07655eaffb654/pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e", size = 6082043, upload-time = "2025-07-01T09:14:07.978Z" }, - { url = "https://files.pythonhosted.org/packages/3a/04/ba8f2b11fc80d2dd462d7abec16351b45ec99cbbaea4387648a44190351a/pillow-11.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94", size = 6715516, upload-time = "2025-07-01T09:14:10.233Z" }, - { url = "https://files.pythonhosted.org/packages/48/59/8cd06d7f3944cc7d892e8533c56b0acb68399f640786313275faec1e3b6f/pillow-11.3.0-cp311-cp311-win32.whl", hash = "sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0", size = 6274768, upload-time = "2025-07-01T09:14:11.921Z" }, - { url = "https://files.pythonhosted.org/packages/f1/cc/29c0f5d64ab8eae20f3232da8f8571660aa0ab4b8f1331da5c2f5f9a938e/pillow-11.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac", size = 6986055, upload-time = "2025-07-01T09:14:13.623Z" }, - { url = "https://files.pythonhosted.org/packages/c6/df/90bd886fabd544c25addd63e5ca6932c86f2b701d5da6c7839387a076b4a/pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd", size = 2423079, upload-time = "2025-07-01T09:14:15.268Z" }, - { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800, upload-time = "2025-07-01T09:14:17.648Z" }, - { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296, upload-time = "2025-07-01T09:14:19.828Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726, upload-time = "2025-07-03T13:10:04.448Z" }, - { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652, upload-time = "2025-07-03T13:10:10.391Z" }, - { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787, upload-time = "2025-07-01T09:14:21.63Z" }, - { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236, upload-time = "2025-07-01T09:14:23.321Z" }, - { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950, upload-time = "2025-07-01T09:14:25.237Z" }, - { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", size = 6723358, upload-time = "2025-07-01T09:14:27.053Z" }, - { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", size = 6275079, upload-time = "2025-07-01T09:14:30.104Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", size = 6986324, upload-time = "2025-07-01T09:14:31.899Z" }, - { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", size = 2423067, upload-time = "2025-07-01T09:14:33.709Z" }, - { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328, upload-time = "2025-07-01T09:14:35.276Z" }, - { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652, upload-time = "2025-07-01T09:14:37.203Z" }, - { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443, upload-time = "2025-07-01T09:14:39.344Z" }, - { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474, upload-time = "2025-07-01T09:14:41.843Z" }, - { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038, upload-time = "2025-07-01T09:14:44.008Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407, upload-time = "2025-07-03T13:10:15.628Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094, upload-time = "2025-07-03T13:10:21.857Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503, upload-time = "2025-07-01T09:14:45.698Z" }, - { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574, upload-time = "2025-07-01T09:14:47.415Z" }, - { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060, upload-time = "2025-07-01T09:14:49.636Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407, upload-time = "2025-07-01T09:14:51.962Z" }, - { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841, upload-time = "2025-07-01T09:14:54.142Z" }, - { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450, upload-time = "2025-07-01T09:14:56.436Z" }, - { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055, upload-time = "2025-07-01T09:14:58.072Z" }, - { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110, upload-time = "2025-07-01T09:14:59.79Z" }, - { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547, upload-time = "2025-07-01T09:15:01.648Z" }, - { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554, upload-time = "2025-07-03T13:10:27.018Z" }, - { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132, upload-time = "2025-07-03T13:10:33.01Z" }, - { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001, upload-time = "2025-07-01T09:15:03.365Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814, upload-time = "2025-07-01T09:15:05.655Z" }, - { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124, upload-time = "2025-07-01T09:15:07.358Z" }, - { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186, upload-time = "2025-07-01T09:15:09.317Z" }, - { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546, upload-time = "2025-07-01T09:15:11.311Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102, upload-time = "2025-07-01T09:15:13.164Z" }, - { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" }, - { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" }, - { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" }, - { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" }, - { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" }, - { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" }, - { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" }, - { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" }, - { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" }, - { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" }, - { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" }, - { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" }, - { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" }, - { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" }, - { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" }, - { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" }, - { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" }, - { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" }, - { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" }, - { url = "https://files.pythonhosted.org/packages/6f/8b/209bd6b62ce8367f47e68a218bffac88888fdf2c9fcf1ecadc6c3ec1ebc7/pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967", size = 5270556, upload-time = "2025-07-01T09:16:09.961Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e6/231a0b76070c2cfd9e260a7a5b504fb72da0a95279410fa7afd99d9751d6/pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe", size = 4654625, upload-time = "2025-07-01T09:16:11.913Z" }, - { url = "https://files.pythonhosted.org/packages/13/f4/10cf94fda33cb12765f2397fc285fa6d8eb9c29de7f3185165b702fc7386/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c", size = 4874207, upload-time = "2025-07-03T13:11:10.201Z" }, - { url = "https://files.pythonhosted.org/packages/72/c9/583821097dc691880c92892e8e2d41fe0a5a3d6021f4963371d2f6d57250/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25", size = 6583939, upload-time = "2025-07-03T13:11:15.68Z" }, - { url = "https://files.pythonhosted.org/packages/3b/8e/5c9d410f9217b12320efc7c413e72693f48468979a013ad17fd690397b9a/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27", size = 4957166, upload-time = "2025-07-01T09:16:13.74Z" }, - { url = "https://files.pythonhosted.org/packages/62/bb/78347dbe13219991877ffb3a91bf09da8317fbfcd4b5f9140aeae020ad71/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a", size = 5581482, upload-time = "2025-07-01T09:16:16.107Z" }, - { url = "https://files.pythonhosted.org/packages/d9/28/1000353d5e61498aaeaaf7f1e4b49ddb05f2c6575f9d4f9f914a3538b6e1/pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f", size = 6984596, upload-time = "2025-07-01T09:16:18.07Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e3/6fa84033758276fb31da12e5fb66ad747ae83b93c67af17f8c6ff4cc8f34/pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6", size = 5270566, upload-time = "2025-07-01T09:16:19.801Z" }, - { url = "https://files.pythonhosted.org/packages/5b/ee/e8d2e1ab4892970b561e1ba96cbd59c0d28cf66737fc44abb2aec3795a4e/pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438", size = 4654618, upload-time = "2025-07-01T09:16:21.818Z" }, - { url = "https://files.pythonhosted.org/packages/f2/6d/17f80f4e1f0761f02160fc433abd4109fa1548dcfdca46cfdadaf9efa565/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3", size = 4874248, upload-time = "2025-07-03T13:11:20.738Z" }, - { url = "https://files.pythonhosted.org/packages/de/5f/c22340acd61cef960130585bbe2120e2fd8434c214802f07e8c03596b17e/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c", size = 6583963, upload-time = "2025-07-03T13:11:26.283Z" }, - { url = "https://files.pythonhosted.org/packages/31/5e/03966aedfbfcbb4d5f8aa042452d3361f325b963ebbadddac05b122e47dd/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361", size = 4957170, upload-time = "2025-07-01T09:16:23.762Z" }, - { url = "https://files.pythonhosted.org/packages/cc/2d/e082982aacc927fc2cab48e1e731bdb1643a1406acace8bed0900a61464e/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7", size = 5581505, upload-time = "2025-07-01T09:16:25.593Z" }, - { url = "https://files.pythonhosted.org/packages/34/e7/ae39f538fd6844e982063c3a5e4598b8ced43b9633baa3a85ef33af8c05c/pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8", size = 6984598, upload-time = "2025-07-01T09:16:27.732Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.3.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, -] - -[[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 = "polars" -version = "1.31.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/f5/de1b5ecd7d0bd0dd87aa392937f759f9cc3997c5866a9a7f94eabf37cd48/polars-1.31.0.tar.gz", hash = "sha256:59a88054a5fc0135386268ceefdbb6a6cc012d21b5b44fed4f1d3faabbdcbf32", size = 4681224, upload-time = "2025-06-18T12:00:46.24Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/6e/bdd0937653c1e7a564a09ae3bc7757ce83fedbf19da600c8b35d62c0182a/polars-1.31.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:ccc68cd6877deecd46b13cbd2663ca89ab2a2cb1fe49d5cfc66a9cef166566d9", size = 34511354, upload-time = "2025-06-18T11:59:40.048Z" }, - { url = "https://files.pythonhosted.org/packages/77/fe/81aaca3540c1a5530b4bc4fd7f1b6f77100243d7bb9b7ad3478b770d8b3e/polars-1.31.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:a94c5550df397ad3c2d6adc212e59fd93d9b044ec974dd3653e121e6487a7d21", size = 31377712, upload-time = "2025-06-18T11:59:45.104Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d9/5e2753784ea30d84b3e769a56f5e50ac5a89c129e87baa16ac0773eb4ef7/polars-1.31.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada7940ed92bea65d5500ae7ac1f599798149df8faa5a6db150327c9ddbee4f1", size = 35050729, upload-time = "2025-06-18T11:59:48.538Z" }, - { url = "https://files.pythonhosted.org/packages/20/e8/a6bdfe7b687c1fe84bceb1f854c43415eaf0d2fdf3c679a9dc9c4776e462/polars-1.31.0-cp39-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:b324e6e3e8c6cc6593f9d72fe625f06af65e8d9d47c8686583585533a5e731e1", size = 32260836, upload-time = "2025-06-18T11:59:52.543Z" }, - { url = "https://files.pythonhosted.org/packages/6e/f6/9d9ad9dc4480d66502497e90ce29efc063373e1598f4bd9b6a38af3e08e7/polars-1.31.0-cp39-abi3-win_amd64.whl", hash = "sha256:3fd874d3432fc932863e8cceff2cff8a12a51976b053f2eb6326a0672134a632", size = 35156211, upload-time = "2025-06-18T11:59:55.805Z" }, - { url = "https://files.pythonhosted.org/packages/40/4b/0673a68ac4d6527fac951970e929c3b4440c654f994f0c957bd5556deb38/polars-1.31.0-cp39-abi3-win_arm64.whl", hash = "sha256:62ef23bb9d10dca4c2b945979f9a50812ac4ace4ed9e158a6b5d32a7322e6f75", size = 31469078, upload-time = "2025-06-18T11:59:59.242Z" }, -] - -[[package]] -name = "posthog" -version = "5.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "backoff" }, - { name = "distro" }, - { name = "python-dateutil" }, - { name = "requests" }, - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/48/20/60ae67bb9d82f00427946218d49e2e7e80fb41c15dc5019482289ec9ce8d/posthog-5.4.0.tar.gz", hash = "sha256:701669261b8d07cdde0276e5bc096b87f9e200e3b9589c5ebff14df658c5893c", size = 88076, upload-time = "2025-06-20T23:19:23.485Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/98/e480cab9a08d1c09b1c59a93dade92c1bb7544826684ff2acbfd10fcfbd4/posthog-5.4.0-py3-none-any.whl", hash = "sha256:284dfa302f64353484420b52d4ad81ff5c2c2d1d607c4e2db602ac72761831bd", size = 105364, upload-time = "2025-06-20T23:19:22.001Z" }, -] - -[[package]] -name = "pre-commit" -version = "4.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cfgv" }, - { name = "identify" }, - { name = "nodeenv" }, - { name = "pyyaml" }, - { name = "virtualenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/08/39/679ca9b26c7bb2999ff122d50faa301e49af82ca9c066ec061cfbc0c6784/pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146", size = 193424, upload-time = "2025-03-18T21:35:20.987Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707, upload-time = "2025-03-18T21:35:19.343Z" }, -] - -[[package]] -name = "prompt-toolkit" -version = "3.0.51" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wcwidth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/6e/9d084c929dfe9e3bfe0c6a47e31f78a25c54627d64a66e884a8bf5474f1c/prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed", size = 428940, upload-time = "2025-04-15T09:18:47.731Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", size = 387810, upload-time = "2025-04-15T09:18:44.753Z" }, -] - -[[package]] -name = "propcache" -version = "0.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168", size = 44139, upload-time = "2025-06-09T22:56:06.081Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/14/510deed325e262afeb8b360043c5d7c960da7d3ecd6d6f9496c9c56dc7f4/propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770", size = 73178, upload-time = "2025-06-09T22:53:40.126Z" }, - { url = "https://files.pythonhosted.org/packages/cd/4e/ad52a7925ff01c1325653a730c7ec3175a23f948f08626a534133427dcff/propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3", size = 43133, upload-time = "2025-06-09T22:53:41.965Z" }, - { url = "https://files.pythonhosted.org/packages/63/7c/e9399ba5da7780871db4eac178e9c2e204c23dd3e7d32df202092a1ed400/propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3", size = 43039, upload-time = "2025-06-09T22:53:43.268Z" }, - { url = "https://files.pythonhosted.org/packages/22/e1/58da211eb8fdc6fc854002387d38f415a6ca5f5c67c1315b204a5d3e9d7a/propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e", size = 201903, upload-time = "2025-06-09T22:53:44.872Z" }, - { url = "https://files.pythonhosted.org/packages/c4/0a/550ea0f52aac455cb90111c8bab995208443e46d925e51e2f6ebdf869525/propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220", size = 213362, upload-time = "2025-06-09T22:53:46.707Z" }, - { url = "https://files.pythonhosted.org/packages/5a/af/9893b7d878deda9bb69fcf54600b247fba7317761b7db11fede6e0f28bd0/propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb", size = 210525, upload-time = "2025-06-09T22:53:48.547Z" }, - { url = "https://files.pythonhosted.org/packages/7c/bb/38fd08b278ca85cde36d848091ad2b45954bc5f15cce494bb300b9285831/propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614", size = 198283, upload-time = "2025-06-09T22:53:50.067Z" }, - { url = "https://files.pythonhosted.org/packages/78/8c/9fe55bd01d362bafb413dfe508c48753111a1e269737fa143ba85693592c/propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50", size = 191872, upload-time = "2025-06-09T22:53:51.438Z" }, - { url = "https://files.pythonhosted.org/packages/54/14/4701c33852937a22584e08abb531d654c8bcf7948a8f87ad0a4822394147/propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339", size = 199452, upload-time = "2025-06-09T22:53:53.229Z" }, - { url = "https://files.pythonhosted.org/packages/16/44/447f2253d859602095356007657ee535e0093215ea0b3d1d6a41d16e5201/propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0", size = 191567, upload-time = "2025-06-09T22:53:54.541Z" }, - { url = "https://files.pythonhosted.org/packages/f2/b3/e4756258749bb2d3b46defcff606a2f47410bab82be5824a67e84015b267/propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2", size = 193015, upload-time = "2025-06-09T22:53:56.44Z" }, - { url = "https://files.pythonhosted.org/packages/1e/df/e6d3c7574233164b6330b9fd697beeac402afd367280e6dc377bb99b43d9/propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7", size = 204660, upload-time = "2025-06-09T22:53:57.839Z" }, - { url = "https://files.pythonhosted.org/packages/b2/53/e4d31dd5170b4a0e2e6b730f2385a96410633b4833dc25fe5dffd1f73294/propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b", size = 206105, upload-time = "2025-06-09T22:53:59.638Z" }, - { url = "https://files.pythonhosted.org/packages/7f/fe/74d54cf9fbe2a20ff786e5f7afcfde446588f0cf15fb2daacfbc267b866c/propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c", size = 196980, upload-time = "2025-06-09T22:54:01.071Z" }, - { url = "https://files.pythonhosted.org/packages/22/ec/c469c9d59dada8a7679625e0440b544fe72e99311a4679c279562051f6fc/propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70", size = 37679, upload-time = "2025-06-09T22:54:03.003Z" }, - { url = "https://files.pythonhosted.org/packages/38/35/07a471371ac89d418f8d0b699c75ea6dca2041fbda360823de21f6a9ce0a/propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9", size = 41459, upload-time = "2025-06-09T22:54:04.134Z" }, - { url = "https://files.pythonhosted.org/packages/80/8d/e8b436717ab9c2cfc23b116d2c297305aa4cd8339172a456d61ebf5669b8/propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be", size = 74207, upload-time = "2025-06-09T22:54:05.399Z" }, - { url = "https://files.pythonhosted.org/packages/d6/29/1e34000e9766d112171764b9fa3226fa0153ab565d0c242c70e9945318a7/propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f", size = 43648, upload-time = "2025-06-09T22:54:08.023Z" }, - { url = "https://files.pythonhosted.org/packages/46/92/1ad5af0df781e76988897da39b5f086c2bf0f028b7f9bd1f409bb05b6874/propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9", size = 43496, upload-time = "2025-06-09T22:54:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/ce/e96392460f9fb68461fabab3e095cb00c8ddf901205be4eae5ce246e5b7e/propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf", size = 217288, upload-time = "2025-06-09T22:54:10.466Z" }, - { url = "https://files.pythonhosted.org/packages/c5/2a/866726ea345299f7ceefc861a5e782b045545ae6940851930a6adaf1fca6/propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9", size = 227456, upload-time = "2025-06-09T22:54:11.828Z" }, - { url = "https://files.pythonhosted.org/packages/de/03/07d992ccb6d930398689187e1b3c718339a1c06b8b145a8d9650e4726166/propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66", size = 225429, upload-time = "2025-06-09T22:54:13.823Z" }, - { url = "https://files.pythonhosted.org/packages/5d/e6/116ba39448753b1330f48ab8ba927dcd6cf0baea8a0ccbc512dfb49ba670/propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df", size = 213472, upload-time = "2025-06-09T22:54:15.232Z" }, - { url = "https://files.pythonhosted.org/packages/a6/85/f01f5d97e54e428885a5497ccf7f54404cbb4f906688a1690cd51bf597dc/propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2", size = 204480, upload-time = "2025-06-09T22:54:17.104Z" }, - { url = "https://files.pythonhosted.org/packages/e3/79/7bf5ab9033b8b8194cc3f7cf1aaa0e9c3256320726f64a3e1f113a812dce/propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7", size = 214530, upload-time = "2025-06-09T22:54:18.512Z" }, - { url = "https://files.pythonhosted.org/packages/31/0b/bd3e0c00509b609317df4a18e6b05a450ef2d9a963e1d8bc9c9415d86f30/propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95", size = 205230, upload-time = "2025-06-09T22:54:19.947Z" }, - { url = "https://files.pythonhosted.org/packages/7a/23/fae0ff9b54b0de4e819bbe559508da132d5683c32d84d0dc2ccce3563ed4/propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e", size = 206754, upload-time = "2025-06-09T22:54:21.716Z" }, - { url = "https://files.pythonhosted.org/packages/b7/7f/ad6a3c22630aaa5f618b4dc3c3598974a72abb4c18e45a50b3cdd091eb2f/propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e", size = 218430, upload-time = "2025-06-09T22:54:23.17Z" }, - { url = "https://files.pythonhosted.org/packages/5b/2c/ba4f1c0e8a4b4c75910742f0d333759d441f65a1c7f34683b4a74c0ee015/propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf", size = 223884, upload-time = "2025-06-09T22:54:25.539Z" }, - { url = "https://files.pythonhosted.org/packages/88/e4/ebe30fc399e98572019eee82ad0caf512401661985cbd3da5e3140ffa1b0/propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e", size = 211480, upload-time = "2025-06-09T22:54:26.892Z" }, - { url = "https://files.pythonhosted.org/packages/96/0a/7d5260b914e01d1d0906f7f38af101f8d8ed0dc47426219eeaf05e8ea7c2/propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897", size = 37757, upload-time = "2025-06-09T22:54:28.241Z" }, - { url = "https://files.pythonhosted.org/packages/e1/2d/89fe4489a884bc0da0c3278c552bd4ffe06a1ace559db5ef02ef24ab446b/propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39", size = 41500, upload-time = "2025-06-09T22:54:29.4Z" }, - { url = "https://files.pythonhosted.org/packages/a8/42/9ca01b0a6f48e81615dca4765a8f1dd2c057e0540f6116a27dc5ee01dfb6/propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10", size = 73674, upload-time = "2025-06-09T22:54:30.551Z" }, - { url = "https://files.pythonhosted.org/packages/af/6e/21293133beb550f9c901bbece755d582bfaf2176bee4774000bd4dd41884/propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154", size = 43570, upload-time = "2025-06-09T22:54:32.296Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c8/0393a0a3a2b8760eb3bde3c147f62b20044f0ddac81e9d6ed7318ec0d852/propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615", size = 43094, upload-time = "2025-06-09T22:54:33.929Z" }, - { url = "https://files.pythonhosted.org/packages/37/2c/489afe311a690399d04a3e03b069225670c1d489eb7b044a566511c1c498/propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db", size = 226958, upload-time = "2025-06-09T22:54:35.186Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ca/63b520d2f3d418c968bf596839ae26cf7f87bead026b6192d4da6a08c467/propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1", size = 234894, upload-time = "2025-06-09T22:54:36.708Z" }, - { url = "https://files.pythonhosted.org/packages/11/60/1d0ed6fff455a028d678df30cc28dcee7af77fa2b0e6962ce1df95c9a2a9/propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c", size = 233672, upload-time = "2025-06-09T22:54:38.062Z" }, - { url = "https://files.pythonhosted.org/packages/37/7c/54fd5301ef38505ab235d98827207176a5c9b2aa61939b10a460ca53e123/propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67", size = 224395, upload-time = "2025-06-09T22:54:39.634Z" }, - { url = "https://files.pythonhosted.org/packages/ee/1a/89a40e0846f5de05fdc6779883bf46ba980e6df4d2ff8fb02643de126592/propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b", size = 212510, upload-time = "2025-06-09T22:54:41.565Z" }, - { url = "https://files.pythonhosted.org/packages/5e/33/ca98368586c9566a6b8d5ef66e30484f8da84c0aac3f2d9aec6d31a11bd5/propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8", size = 222949, upload-time = "2025-06-09T22:54:43.038Z" }, - { url = "https://files.pythonhosted.org/packages/ba/11/ace870d0aafe443b33b2f0b7efdb872b7c3abd505bfb4890716ad7865e9d/propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251", size = 217258, upload-time = "2025-06-09T22:54:44.376Z" }, - { url = "https://files.pythonhosted.org/packages/5b/d2/86fd6f7adffcfc74b42c10a6b7db721d1d9ca1055c45d39a1a8f2a740a21/propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474", size = 213036, upload-time = "2025-06-09T22:54:46.243Z" }, - { url = "https://files.pythonhosted.org/packages/07/94/2d7d1e328f45ff34a0a284cf5a2847013701e24c2a53117e7c280a4316b3/propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535", size = 227684, upload-time = "2025-06-09T22:54:47.63Z" }, - { url = "https://files.pythonhosted.org/packages/b7/05/37ae63a0087677e90b1d14710e532ff104d44bc1efa3b3970fff99b891dc/propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06", size = 234562, upload-time = "2025-06-09T22:54:48.982Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7c/3f539fcae630408d0bd8bf3208b9a647ccad10976eda62402a80adf8fc34/propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1", size = 222142, upload-time = "2025-06-09T22:54:50.424Z" }, - { url = "https://files.pythonhosted.org/packages/7c/d2/34b9eac8c35f79f8a962546b3e97e9d4b990c420ee66ac8255d5d9611648/propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1", size = 37711, upload-time = "2025-06-09T22:54:52.072Z" }, - { url = "https://files.pythonhosted.org/packages/19/61/d582be5d226cf79071681d1b46b848d6cb03d7b70af7063e33a2787eaa03/propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c", size = 41479, upload-time = "2025-06-09T22:54:53.234Z" }, - { url = "https://files.pythonhosted.org/packages/dc/d1/8c747fafa558c603c4ca19d8e20b288aa0c7cda74e9402f50f31eb65267e/propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945", size = 71286, upload-time = "2025-06-09T22:54:54.369Z" }, - { url = "https://files.pythonhosted.org/packages/61/99/d606cb7986b60d89c36de8a85d58764323b3a5ff07770a99d8e993b3fa73/propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252", size = 42425, upload-time = "2025-06-09T22:54:55.642Z" }, - { url = "https://files.pythonhosted.org/packages/8c/96/ef98f91bbb42b79e9bb82bdd348b255eb9d65f14dbbe3b1594644c4073f7/propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f", size = 41846, upload-time = "2025-06-09T22:54:57.246Z" }, - { url = "https://files.pythonhosted.org/packages/5b/ad/3f0f9a705fb630d175146cd7b1d2bf5555c9beaed54e94132b21aac098a6/propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33", size = 208871, upload-time = "2025-06-09T22:54:58.975Z" }, - { url = "https://files.pythonhosted.org/packages/3a/38/2085cda93d2c8b6ec3e92af2c89489a36a5886b712a34ab25de9fbca7992/propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e", size = 215720, upload-time = "2025-06-09T22:55:00.471Z" }, - { url = "https://files.pythonhosted.org/packages/61/c1/d72ea2dc83ac7f2c8e182786ab0fc2c7bd123a1ff9b7975bee671866fe5f/propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1", size = 215203, upload-time = "2025-06-09T22:55:01.834Z" }, - { url = "https://files.pythonhosted.org/packages/af/81/b324c44ae60c56ef12007105f1460d5c304b0626ab0cc6b07c8f2a9aa0b8/propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3", size = 206365, upload-time = "2025-06-09T22:55:03.199Z" }, - { url = "https://files.pythonhosted.org/packages/09/73/88549128bb89e66d2aff242488f62869014ae092db63ccea53c1cc75a81d/propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1", size = 196016, upload-time = "2025-06-09T22:55:04.518Z" }, - { url = "https://files.pythonhosted.org/packages/b9/3f/3bdd14e737d145114a5eb83cb172903afba7242f67c5877f9909a20d948d/propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6", size = 205596, upload-time = "2025-06-09T22:55:05.942Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ca/2f4aa819c357d3107c3763d7ef42c03980f9ed5c48c82e01e25945d437c1/propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387", size = 200977, upload-time = "2025-06-09T22:55:07.792Z" }, - { url = "https://files.pythonhosted.org/packages/cd/4a/e65276c7477533c59085251ae88505caf6831c0e85ff8b2e31ebcbb949b1/propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4", size = 197220, upload-time = "2025-06-09T22:55:09.173Z" }, - { url = "https://files.pythonhosted.org/packages/7c/54/fc7152e517cf5578278b242396ce4d4b36795423988ef39bb8cd5bf274c8/propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88", size = 210642, upload-time = "2025-06-09T22:55:10.62Z" }, - { url = "https://files.pythonhosted.org/packages/b9/80/abeb4a896d2767bf5f1ea7b92eb7be6a5330645bd7fb844049c0e4045d9d/propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206", size = 212789, upload-time = "2025-06-09T22:55:12.029Z" }, - { url = "https://files.pythonhosted.org/packages/b3/db/ea12a49aa7b2b6d68a5da8293dcf50068d48d088100ac016ad92a6a780e6/propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43", size = 205880, upload-time = "2025-06-09T22:55:13.45Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e5/9076a0bbbfb65d1198007059c65639dfd56266cf8e477a9707e4b1999ff4/propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02", size = 37220, upload-time = "2025-06-09T22:55:15.284Z" }, - { url = "https://files.pythonhosted.org/packages/d3/f5/b369e026b09a26cd77aa88d8fffd69141d2ae00a2abaaf5380d2603f4b7f/propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05", size = 40678, upload-time = "2025-06-09T22:55:16.445Z" }, - { url = "https://files.pythonhosted.org/packages/a4/3a/6ece377b55544941a08d03581c7bc400a3c8cd3c2865900a68d5de79e21f/propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b", size = 76560, upload-time = "2025-06-09T22:55:17.598Z" }, - { url = "https://files.pythonhosted.org/packages/0c/da/64a2bb16418740fa634b0e9c3d29edff1db07f56d3546ca2d86ddf0305e1/propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0", size = 44676, upload-time = "2025-06-09T22:55:18.922Z" }, - { url = "https://files.pythonhosted.org/packages/36/7b/f025e06ea51cb72c52fb87e9b395cced02786610b60a3ed51da8af017170/propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e", size = 44701, upload-time = "2025-06-09T22:55:20.106Z" }, - { url = "https://files.pythonhosted.org/packages/a4/00/faa1b1b7c3b74fc277f8642f32a4c72ba1d7b2de36d7cdfb676db7f4303e/propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28", size = 276934, upload-time = "2025-06-09T22:55:21.5Z" }, - { url = "https://files.pythonhosted.org/packages/74/ab/935beb6f1756e0476a4d5938ff44bf0d13a055fed880caf93859b4f1baf4/propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a", size = 278316, upload-time = "2025-06-09T22:55:22.918Z" }, - { url = "https://files.pythonhosted.org/packages/f8/9d/994a5c1ce4389610838d1caec74bdf0e98b306c70314d46dbe4fcf21a3e2/propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c", size = 282619, upload-time = "2025-06-09T22:55:24.651Z" }, - { url = "https://files.pythonhosted.org/packages/2b/00/a10afce3d1ed0287cef2e09506d3be9822513f2c1e96457ee369adb9a6cd/propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725", size = 265896, upload-time = "2025-06-09T22:55:26.049Z" }, - { url = "https://files.pythonhosted.org/packages/2e/a8/2aa6716ffa566ca57c749edb909ad27884680887d68517e4be41b02299f3/propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892", size = 252111, upload-time = "2025-06-09T22:55:27.381Z" }, - { url = "https://files.pythonhosted.org/packages/36/4f/345ca9183b85ac29c8694b0941f7484bf419c7f0fea2d1e386b4f7893eed/propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44", size = 268334, upload-time = "2025-06-09T22:55:28.747Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ca/fcd54f78b59e3f97b3b9715501e3147f5340167733d27db423aa321e7148/propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe", size = 255026, upload-time = "2025-06-09T22:55:30.184Z" }, - { url = "https://files.pythonhosted.org/packages/8b/95/8e6a6bbbd78ac89c30c225210a5c687790e532ba4088afb8c0445b77ef37/propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81", size = 250724, upload-time = "2025-06-09T22:55:31.646Z" }, - { url = "https://files.pythonhosted.org/packages/ee/b0/0dd03616142baba28e8b2d14ce5df6631b4673850a3d4f9c0f9dd714a404/propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba", size = 268868, upload-time = "2025-06-09T22:55:33.209Z" }, - { url = "https://files.pythonhosted.org/packages/c5/98/2c12407a7e4fbacd94ddd32f3b1e3d5231e77c30ef7162b12a60e2dd5ce3/propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770", size = 271322, upload-time = "2025-06-09T22:55:35.065Z" }, - { url = "https://files.pythonhosted.org/packages/35/91/9cb56efbb428b006bb85db28591e40b7736847b8331d43fe335acf95f6c8/propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330", size = 265778, upload-time = "2025-06-09T22:55:36.45Z" }, - { url = "https://files.pythonhosted.org/packages/9a/4c/b0fe775a2bdd01e176b14b574be679d84fc83958335790f7c9a686c1f468/propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394", size = 41175, upload-time = "2025-06-09T22:55:38.436Z" }, - { url = "https://files.pythonhosted.org/packages/a4/ff/47f08595e3d9b5e149c150f88d9714574f1a7cbd89fe2817158a952674bf/propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198", size = 44857, upload-time = "2025-06-09T22:55:39.687Z" }, - { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663, upload-time = "2025-06-09T22:56:04.484Z" }, -] - -[[package]] -name = "protobuf" -version = "6.31.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/f3/b9655a711b32c19720253f6f06326faf90580834e2e83f840472d752bc8b/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a", size = 441797, upload-time = "2025-05-28T19:25:54.947Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/6f/6ab8e4bf962fd5570d3deaa2d5c38f0a363f57b4501047b5ebeb83ab1125/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9", size = 423603, upload-time = "2025-05-28T19:25:41.198Z" }, - { url = "https://files.pythonhosted.org/packages/44/3a/b15c4347dd4bf3a1b0ee882f384623e2063bb5cf9fa9d57990a4f7df2fb6/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447", size = 435283, upload-time = "2025-05-28T19:25:44.275Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c9/b9689a2a250264a84e66c46d8862ba788ee7a641cdca39bccf64f59284b7/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402", size = 425604, upload-time = "2025-05-28T19:25:45.702Z" }, - { url = "https://files.pythonhosted.org/packages/76/a1/7a5a94032c83375e4fe7e7f56e3976ea6ac90c5e85fac8576409e25c39c3/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39", size = 322115, upload-time = "2025-05-28T19:25:47.128Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b1/b59d405d64d31999244643d88c45c8241c58f17cc887e73bcb90602327f8/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6", size = 321070, upload-time = "2025-05-28T19:25:50.036Z" }, - { url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724, upload-time = "2025-05-28T19:25:53.926Z" }, -] - -[[package]] -name = "ptyprocess" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, -] - -[[package]] -name = "pure-eval" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, -] - -[[package]] -name = "py-rust-stemmers" -version = "0.1.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/63/4fbc14810c32d2a884e2e94e406a7d5bf8eee53e1103f558433817230342/py_rust_stemmers-0.1.5.tar.gz", hash = "sha256:e9c310cfb5c2470d7c7c8a0484725965e7cab8b1237e106a0863d5741da3e1f7", size = 9388, upload-time = "2025-02-19T13:56:28.708Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/19/28/2247e06de9896ac5d0fe9c6c16e611fd39549cb3197e25f12ca4437f12e7/py_rust_stemmers-0.1.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bfbd9034ae00419ff2154e33b8f5b4c4d99d1f9271f31ed059e5c7e9fa005844", size = 286084, upload-time = "2025-02-19T13:54:52.061Z" }, - { url = "https://files.pythonhosted.org/packages/95/d9/5d1743a160eb9e0bc4c162360278166474e5d168e318c0d5e1bc32b18c96/py_rust_stemmers-0.1.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7162ae66df2bb0fc39b350c24a049f5f5151c03c046092ba095c2141ec223a2", size = 272020, upload-time = "2025-02-19T13:54:53.957Z" }, - { url = "https://files.pythonhosted.org/packages/98/21/a94c32ffa38417bad41d6e72cb89a32eac45cc8c6bed1a7b2b0f88bf3626/py_rust_stemmers-0.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da6de2b694af6227ba8c5a0447d4e0ef69991e63ee558b969f90c415f33e54d0", size = 310546, upload-time = "2025-02-19T13:54:55.462Z" }, - { url = "https://files.pythonhosted.org/packages/2c/43/95449704e43be071555448507ab9242f5edebe75fe5ff5fb9674bef0fd9f/py_rust_stemmers-0.1.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a3abbd6d26722951a04550fff55460c0f26819169c23286e11ea25c645be6140", size = 315236, upload-time = "2025-02-19T13:54:56.577Z" }, - { url = "https://files.pythonhosted.org/packages/a7/77/fbd2bd6d3bb5a3395e09b990fa7598be4093d7b8958e2cadfae3d14dcc5b/py_rust_stemmers-0.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:019221c57a7bcc51097fa3f124b62d0577b5b6167184ee51abd3aea822d78f69", size = 324419, upload-time = "2025-02-19T13:54:58.373Z" }, - { url = "https://files.pythonhosted.org/packages/f4/8d/3566e9b067d3551d72320193aa9377a1ddabaf7d4624dd0a10f4c496d6f5/py_rust_stemmers-0.1.5-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:8dd5824194c279ee07f2675a55b3d728dfeec69a4b3c27329fab9b2ff5063c91", size = 324792, upload-time = "2025-02-19T13:54:59.547Z" }, - { url = "https://files.pythonhosted.org/packages/9b/ce/9b4bdb548974c7e79f188057efb2a3426b2df8c9a3d8ac0d5a81b5f1a297/py_rust_stemmers-0.1.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7cf4d69bf20cec373ba0e89df3d98549b1a0cfb130dbd859a50ed772dd044546", size = 488012, upload-time = "2025-02-19T13:55:00.943Z" }, - { url = "https://files.pythonhosted.org/packages/fd/3e/ea9d8328af1c0661adb47daeb460185285e0e5e26aeca84df5cbde2e4e58/py_rust_stemmers-0.1.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:b42eb52609ac958e7fcc441395457dc5183397e8014e954f4aed78de210837b9", size = 575579, upload-time = "2025-02-19T13:55:02.915Z" }, - { url = "https://files.pythonhosted.org/packages/5c/ba/49ea71077a5a52017a0a30c47e944c0a4ee33a88c5eaf2d96a06e74771d6/py_rust_stemmers-0.1.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c836aeb53409a44f38b153106374fe780099a7c976c582c5ae952061ff5d2fed", size = 493265, upload-time = "2025-02-19T13:55:04.966Z" }, - { url = "https://files.pythonhosted.org/packages/d2/a7/26404770230634cec952b9f80444eba76bf8b514b1f3b550494566001893/py_rust_stemmers-0.1.5-cp310-none-win_amd64.whl", hash = "sha256:39550089f7a021a3a97fec2ff0d4ad77e471f0a65c0f100919555e60a4daabf0", size = 209394, upload-time = "2025-02-19T13:55:06.742Z" }, - { url = "https://files.pythonhosted.org/packages/36/9b/6b11f843c01d110db58a68ec4176cb77b37f03268831742a7241f4810fe4/py_rust_stemmers-0.1.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:e644987edaf66919f5a9e4693336930f98d67b790857890623a431bb77774c84", size = 286085, upload-time = "2025-02-19T13:55:08.484Z" }, - { url = "https://files.pythonhosted.org/packages/f2/d1/e16b587dc0ebc42916b1caad994bc37fbb19ad2c7e3f5f3a586ba2630c16/py_rust_stemmers-0.1.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:910d87d39ba75da1fe3d65df88b926b4b454ada8d73893cbd36e258a8a648158", size = 272019, upload-time = "2025-02-19T13:55:10.268Z" }, - { url = "https://files.pythonhosted.org/packages/41/66/8777f125720acb896b336e6f8153e3ec39754563bc9b89523cfe06ba63da/py_rust_stemmers-0.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31ff4fb9417cec35907c18a6463e3d5a4941a5aa8401f77fbb4156b3ada69e3f", size = 310547, upload-time = "2025-02-19T13:55:11.521Z" }, - { url = "https://files.pythonhosted.org/packages/f1/f5/b79249c787c59b9ce2c5d007c0a0dc0fc1ecccfcf98a546c131cca55899e/py_rust_stemmers-0.1.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07b3b8582313ef8a7f544acf2c887f27c3dd48c5ddca028fa0f498de7380e24f", size = 315238, upload-time = "2025-02-19T13:55:13.39Z" }, - { url = "https://files.pythonhosted.org/packages/62/4c/c05c266ed74c063ae31dc5633ed63c48eb3b78034afcc80fe755d0cb09e7/py_rust_stemmers-0.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:804944eeb5c5559443d81f30c34d6e83c6292d72423f299e42f9d71b9d240941", size = 324420, upload-time = "2025-02-19T13:55:15.292Z" }, - { url = "https://files.pythonhosted.org/packages/7f/65/feb83af28095397466e6e031989ff760cc89b01e7da169e76d4cf16a2252/py_rust_stemmers-0.1.5-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c52c5c326de78c70cfc71813fa56818d1bd4894264820d037d2be0e805b477bd", size = 324791, upload-time = "2025-02-19T13:55:16.45Z" }, - { url = "https://files.pythonhosted.org/packages/20/3e/162be2f9c1c383e66e510218d9d4946c8a84ee92c64f6d836746540e915f/py_rust_stemmers-0.1.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8f374c0f26ef35fb87212686add8dff394bcd9a1364f14ce40fe11504e25e30", size = 488014, upload-time = "2025-02-19T13:55:18.486Z" }, - { url = "https://files.pythonhosted.org/packages/a0/ee/ed09ce6fde1eefe50aa13a8a8533aa7ebe3cc096d1a43155cc71ba28d298/py_rust_stemmers-0.1.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0ae0540453843bc36937abb54fdbc0d5d60b51ef47aa9667afd05af9248e09eb", size = 575581, upload-time = "2025-02-19T13:55:19.669Z" }, - { url = "https://files.pythonhosted.org/packages/7b/31/2a48960a072e54d7cc244204d98854d201078e1bb5c68a7843a3f6d21ced/py_rust_stemmers-0.1.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85944262c248ea30444155638c9e148a3adc61fe51cf9a3705b4055b564ec95d", size = 493269, upload-time = "2025-02-19T13:55:21.532Z" }, - { url = "https://files.pythonhosted.org/packages/91/33/872269c10ca35b00c5376159a2a0611a0f96372be16b616b46b3d59d09fe/py_rust_stemmers-0.1.5-cp311-none-win_amd64.whl", hash = "sha256:147234020b3eefe6e1a962173e41d8cf1dbf5d0689f3cd60e3022d1ac5c2e203", size = 209399, upload-time = "2025-02-19T13:55:22.639Z" }, - { url = "https://files.pythonhosted.org/packages/43/e1/ea8ac92454a634b1bb1ee0a89c2f75a4e6afec15a8412527e9bbde8c6b7b/py_rust_stemmers-0.1.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:29772837126a28263bf54ecd1bc709dd569d15a94d5e861937813ce51e8a6df4", size = 286085, upload-time = "2025-02-19T13:55:23.871Z" }, - { url = "https://files.pythonhosted.org/packages/cb/32/fe1cc3d36a19c1ce39792b1ed151ddff5ee1d74c8801f0e93ff36e65f885/py_rust_stemmers-0.1.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d62410ada44a01e02974b85d45d82f4b4c511aae9121e5f3c1ba1d0bea9126b", size = 272021, upload-time = "2025-02-19T13:55:25.685Z" }, - { url = "https://files.pythonhosted.org/packages/0a/38/b8f94e5e886e7ab181361a0911a14fb923b0d05b414de85f427e773bf445/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b28ef729a4c83c7d9418be3c23c0372493fcccc67e86783ff04596ef8a208cdf", size = 310547, upload-time = "2025-02-19T13:55:26.891Z" }, - { url = "https://files.pythonhosted.org/packages/a9/08/62e97652d359b75335486f4da134a6f1c281f38bd3169ed6ecfb276448c3/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a979c3f4ff7ad94a0d4cf566ca7bfecebb59e66488cc158e64485cf0c9a7879f", size = 315237, upload-time = "2025-02-19T13:55:28.116Z" }, - { url = "https://files.pythonhosted.org/packages/1c/b9/fc0278432f288d2be4ee4d5cc80fd8013d604506b9b0503e8b8cae4ba1c3/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c3593d895453fa06bf70a7b76d6f00d06def0f91fc253fe4260920650c5e078", size = 324419, upload-time = "2025-02-19T13:55:29.211Z" }, - { url = "https://files.pythonhosted.org/packages/6b/5b/74e96eaf622fe07e83c5c389d101540e305e25f76a6d0d6fb3d9e0506db8/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:96ccc7fd042ffc3f7f082f2223bb7082ed1423aa6b43d5d89ab23e321936c045", size = 324792, upload-time = "2025-02-19T13:55:30.948Z" }, - { url = "https://files.pythonhosted.org/packages/4f/f7/b76816d7d67166e9313915ad486c21d9e7da0ac02703e14375bb1cb64b5a/py_rust_stemmers-0.1.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef18cfced2c9c676e0d7d172ba61c3fab2aa6969db64cc8f5ca33a7759efbefe", size = 488014, upload-time = "2025-02-19T13:55:32.066Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ed/7d9bed02f78d85527501f86a867cd5002d97deb791b9a6b1b45b00100010/py_rust_stemmers-0.1.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:541d4b5aa911381e3d37ec483abb6a2cf2351b4f16d5e8d77f9aa2722956662a", size = 575582, upload-time = "2025-02-19T13:55:34.005Z" }, - { url = "https://files.pythonhosted.org/packages/93/40/eafd1b33688e8e8ae946d1ef25c4dc93f5b685bd104b9c5573405d7e1d30/py_rust_stemmers-0.1.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ffd946a36e9ac17ca96821963663012e04bc0ee94d21e8b5ae034721070b436c", size = 493267, upload-time = "2025-02-19T13:55:35.294Z" }, - { url = "https://files.pythonhosted.org/packages/2f/6a/15135b69e4fd28369433eb03264d201b1b0040ba534b05eddeb02a276684/py_rust_stemmers-0.1.5-cp312-none-win_amd64.whl", hash = "sha256:6ed61e1207f3b7428e99b5d00c055645c6415bb75033bff2d06394cbe035fd8e", size = 209395, upload-time = "2025-02-19T13:55:36.519Z" }, - { url = "https://files.pythonhosted.org/packages/80/b8/030036311ec25952bf3083b6c105be5dee052a71aa22d5fbeb857ebf8c1c/py_rust_stemmers-0.1.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:398b3a843a9cd4c5d09e726246bc36f66b3d05b0a937996814e91f47708f5db5", size = 286086, upload-time = "2025-02-19T13:55:37.581Z" }, - { url = "https://files.pythonhosted.org/packages/ed/be/0465dcb3a709ee243d464e89231e3da580017f34279d6304de291d65ccb0/py_rust_stemmers-0.1.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4e308fc7687901f0c73603203869908f3156fa9c17c4ba010a7fcc98a7a1c5f2", size = 272019, upload-time = "2025-02-19T13:55:39.183Z" }, - { url = "https://files.pythonhosted.org/packages/ab/b6/76ca5b1f30cba36835938b5d9abee0c130c81833d51b9006264afdf8df3c/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f9efc4da5e734bdd00612e7506de3d0c9b7abc4b89d192742a0569d0d1fe749", size = 310545, upload-time = "2025-02-19T13:55:40.339Z" }, - { url = "https://files.pythonhosted.org/packages/56/8f/5be87618cea2fe2e70e74115a20724802bfd06f11c7c43514b8288eb6514/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cc2cc8d2b36bc05b8b06506199ac63d437360ae38caefd98cd19e479d35afd42", size = 315236, upload-time = "2025-02-19T13:55:41.55Z" }, - { url = "https://files.pythonhosted.org/packages/00/02/ea86a316aee0f0a9d1449ad4dbffff38f4cf0a9a31045168ae8b95d8bdf8/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a231dc6f0b2a5f12a080dfc7abd9e6a4ea0909290b10fd0a4620e5a0f52c3d17", size = 324419, upload-time = "2025-02-19T13:55:42.693Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fd/1612c22545dcc0abe2f30fc08f30a2332f2224dd536fa1508444a9ca0e39/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5845709d48afc8b29e248f42f92431155a3d8df9ba30418301c49c6072b181b0", size = 324794, upload-time = "2025-02-19T13:55:43.896Z" }, - { url = "https://files.pythonhosted.org/packages/66/18/8a547584d7edac9e7ac9c7bdc53228d6f751c0f70a317093a77c386c8ddc/py_rust_stemmers-0.1.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e48bfd5e3ce9d223bfb9e634dc1425cf93ee57eef6f56aa9a7120ada3990d4be", size = 488014, upload-time = "2025-02-19T13:55:45.088Z" }, - { url = "https://files.pythonhosted.org/packages/3b/87/4619c395b325e26048a6e28a365afed754614788ba1f49b2eefb07621a03/py_rust_stemmers-0.1.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:35d32f6e7bdf6fd90e981765e32293a8be74def807147dea9fdc1f65d6ce382f", size = 575582, upload-time = "2025-02-19T13:55:46.436Z" }, - { url = "https://files.pythonhosted.org/packages/98/6e/214f1a889142b7df6d716e7f3fea6c41e87bd6c29046aa57e175d452b104/py_rust_stemmers-0.1.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:191ea8bf922c984631ffa20bf02ef0ad7eec0465baeaed3852779e8f97c7e7a3", size = 493269, upload-time = "2025-02-19T13:55:49.057Z" }, - { url = "https://files.pythonhosted.org/packages/e1/b9/c5185df277576f995ae34418eb2b2ac12f30835412270f9e05c52face521/py_rust_stemmers-0.1.5-cp313-none-win_amd64.whl", hash = "sha256:e564c9efdbe7621704e222b53bac265b0e4fbea788f07c814094f0ec6b80adcf", size = 209397, upload-time = "2025-02-19T13:55:50.853Z" }, - { url = "https://files.pythonhosted.org/packages/ca/fa/796ba1ae243bac9bdcf89c7605d642d21e07ae4f6b77a3c968d546371353/py_rust_stemmers-0.1.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f8c6596f04e7a6df2a5cc18854d31b133d2a69a8c494fa49853fe174d8739d14", size = 286746, upload-time = "2025-02-19T13:56:22.871Z" }, - { url = "https://files.pythonhosted.org/packages/4a/66/3c547373839d615217cd94c47ae1965366fa37642ef1bc4f8d32a5884a84/py_rust_stemmers-0.1.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:154c27f5d576fabf2bacf53620f014562af4c6cf9eb09ba7477830f2be868902", size = 272130, upload-time = "2025-02-19T13:56:25.114Z" }, - { url = "https://files.pythonhosted.org/packages/d8/8f/381502753e8917e874daefad0000f61d6069dffaba91acbdb864a74cae10/py_rust_stemmers-0.1.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec42b66927b62fd57328980b6c7004fe85e8fad89c952e8718da68b805a119e3", size = 310955, upload-time = "2025-02-19T13:56:26.368Z" }, - { url = "https://files.pythonhosted.org/packages/3a/15/b1894b9741f7a48f0b4cbea458f7d4141a6df6a1b26bec05fcde96703ce1/py_rust_stemmers-0.1.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57b061c3b4af9e409d009d729b21bc53dabe47116c955ccf0b642a5a2d438f93", size = 324879, upload-time = "2025-02-19T13:56:27.462Z" }, -] - -[[package]] -name = "pyarrow" -version = "21.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ef/c2/ea068b8f00905c06329a3dfcd40d0fcc2b7d0f2e355bdb25b65e0a0e4cd4/pyarrow-21.0.0.tar.gz", hash = "sha256:5051f2dccf0e283ff56335760cbc8622cf52264d67e359d5569541ac11b6d5bc", size = 1133487, upload-time = "2025-07-18T00:57:31.761Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/d9/110de31880016e2afc52d8580b397dbe47615defbf09ca8cf55f56c62165/pyarrow-21.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e563271e2c5ff4d4a4cbeb2c83d5cf0d4938b891518e676025f7268c6fe5fe26", size = 31196837, upload-time = "2025-07-18T00:54:34.755Z" }, - { url = "https://files.pythonhosted.org/packages/df/5f/c1c1997613abf24fceb087e79432d24c19bc6f7259cab57c2c8e5e545fab/pyarrow-21.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:fee33b0ca46f4c85443d6c450357101e47d53e6c3f008d658c27a2d020d44c79", size = 32659470, upload-time = "2025-07-18T00:54:38.329Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ed/b1589a777816ee33ba123ba1e4f8f02243a844fed0deec97bde9fb21a5cf/pyarrow-21.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7be45519b830f7c24b21d630a31d48bcebfd5d4d7f9d3bdb49da9cdf6d764edb", size = 41055619, upload-time = "2025-07-18T00:54:42.172Z" }, - { url = "https://files.pythonhosted.org/packages/44/28/b6672962639e85dc0ac36f71ab3a8f5f38e01b51343d7aa372a6b56fa3f3/pyarrow-21.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:26bfd95f6bff443ceae63c65dc7e048670b7e98bc892210acba7e4995d3d4b51", size = 42733488, upload-time = "2025-07-18T00:54:47.132Z" }, - { url = "https://files.pythonhosted.org/packages/f8/cc/de02c3614874b9089c94eac093f90ca5dfa6d5afe45de3ba847fd950fdf1/pyarrow-21.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bd04ec08f7f8bd113c55868bd3fc442a9db67c27af098c5f814a3091e71cc61a", size = 43329159, upload-time = "2025-07-18T00:54:51.686Z" }, - { url = "https://files.pythonhosted.org/packages/a6/3e/99473332ac40278f196e105ce30b79ab8affab12f6194802f2593d6b0be2/pyarrow-21.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9b0b14b49ac10654332a805aedfc0147fb3469cbf8ea951b3d040dab12372594", size = 45050567, upload-time = "2025-07-18T00:54:56.679Z" }, - { url = "https://files.pythonhosted.org/packages/7b/f5/c372ef60593d713e8bfbb7e0c743501605f0ad00719146dc075faf11172b/pyarrow-21.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:9d9f8bcb4c3be7738add259738abdeddc363de1b80e3310e04067aa1ca596634", size = 26217959, upload-time = "2025-07-18T00:55:00.482Z" }, - { url = "https://files.pythonhosted.org/packages/94/dc/80564a3071a57c20b7c32575e4a0120e8a330ef487c319b122942d665960/pyarrow-21.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c077f48aab61738c237802836fc3844f85409a46015635198761b0d6a688f87b", size = 31243234, upload-time = "2025-07-18T00:55:03.812Z" }, - { url = "https://files.pythonhosted.org/packages/ea/cc/3b51cb2db26fe535d14f74cab4c79b191ed9a8cd4cbba45e2379b5ca2746/pyarrow-21.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:689f448066781856237eca8d1975b98cace19b8dd2ab6145bf49475478bcaa10", size = 32714370, upload-time = "2025-07-18T00:55:07.495Z" }, - { url = "https://files.pythonhosted.org/packages/24/11/a4431f36d5ad7d83b87146f515c063e4d07ef0b7240876ddb885e6b44f2e/pyarrow-21.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:479ee41399fcddc46159a551705b89c05f11e8b8cb8e968f7fec64f62d91985e", size = 41135424, upload-time = "2025-07-18T00:55:11.461Z" }, - { url = "https://files.pythonhosted.org/packages/74/dc/035d54638fc5d2971cbf1e987ccd45f1091c83bcf747281cf6cc25e72c88/pyarrow-21.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:40ebfcb54a4f11bcde86bc586cbd0272bac0d516cfa539c799c2453768477569", size = 42823810, upload-time = "2025-07-18T00:55:16.301Z" }, - { url = "https://files.pythonhosted.org/packages/2e/3b/89fced102448a9e3e0d4dded1f37fa3ce4700f02cdb8665457fcc8015f5b/pyarrow-21.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8d58d8497814274d3d20214fbb24abcad2f7e351474357d552a8d53bce70c70e", size = 43391538, upload-time = "2025-07-18T00:55:23.82Z" }, - { url = "https://files.pythonhosted.org/packages/fb/bb/ea7f1bd08978d39debd3b23611c293f64a642557e8141c80635d501e6d53/pyarrow-21.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:585e7224f21124dd57836b1530ac8f2df2afc43c861d7bf3d58a4870c42ae36c", size = 45120056, upload-time = "2025-07-18T00:55:28.231Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0b/77ea0600009842b30ceebc3337639a7380cd946061b620ac1a2f3cb541e2/pyarrow-21.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:555ca6935b2cbca2c0e932bedd853e9bc523098c39636de9ad4693b5b1df86d6", size = 26220568, upload-time = "2025-07-18T00:55:32.122Z" }, - { url = "https://files.pythonhosted.org/packages/ca/d4/d4f817b21aacc30195cf6a46ba041dd1be827efa4a623cc8bf39a1c2a0c0/pyarrow-21.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:3a302f0e0963db37e0a24a70c56cf91a4faa0bca51c23812279ca2e23481fccd", size = 31160305, upload-time = "2025-07-18T00:55:35.373Z" }, - { url = "https://files.pythonhosted.org/packages/a2/9c/dcd38ce6e4b4d9a19e1d36914cb8e2b1da4e6003dd075474c4cfcdfe0601/pyarrow-21.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:b6b27cf01e243871390474a211a7922bfbe3bda21e39bc9160daf0da3fe48876", size = 32684264, upload-time = "2025-07-18T00:55:39.303Z" }, - { url = "https://files.pythonhosted.org/packages/4f/74/2a2d9f8d7a59b639523454bec12dba35ae3d0a07d8ab529dc0809f74b23c/pyarrow-21.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e72a8ec6b868e258a2cd2672d91f2860ad532d590ce94cdf7d5e7ec674ccf03d", size = 41108099, upload-time = "2025-07-18T00:55:42.889Z" }, - { url = "https://files.pythonhosted.org/packages/ad/90/2660332eeb31303c13b653ea566a9918484b6e4d6b9d2d46879a33ab0622/pyarrow-21.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b7ae0bbdc8c6674259b25bef5d2a1d6af5d39d7200c819cf99e07f7dfef1c51e", size = 42829529, upload-time = "2025-07-18T00:55:47.069Z" }, - { url = "https://files.pythonhosted.org/packages/33/27/1a93a25c92717f6aa0fca06eb4700860577d016cd3ae51aad0e0488ac899/pyarrow-21.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:58c30a1729f82d201627c173d91bd431db88ea74dcaa3885855bc6203e433b82", size = 43367883, upload-time = "2025-07-18T00:55:53.069Z" }, - { url = "https://files.pythonhosted.org/packages/05/d9/4d09d919f35d599bc05c6950095e358c3e15148ead26292dfca1fb659b0c/pyarrow-21.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:072116f65604b822a7f22945a7a6e581cfa28e3454fdcc6939d4ff6090126623", size = 45133802, upload-time = "2025-07-18T00:55:57.714Z" }, - { url = "https://files.pythonhosted.org/packages/71/30/f3795b6e192c3ab881325ffe172e526499eb3780e306a15103a2764916a2/pyarrow-21.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf56ec8b0a5c8c9d7021d6fd754e688104f9ebebf1bf4449613c9531f5346a18", size = 26203175, upload-time = "2025-07-18T00:56:01.364Z" }, - { url = "https://files.pythonhosted.org/packages/16/ca/c7eaa8e62db8fb37ce942b1ea0c6d7abfe3786ca193957afa25e71b81b66/pyarrow-21.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e99310a4ebd4479bcd1964dff9e14af33746300cb014aa4a3781738ac63baf4a", size = 31154306, upload-time = "2025-07-18T00:56:04.42Z" }, - { url = "https://files.pythonhosted.org/packages/ce/e8/e87d9e3b2489302b3a1aea709aaca4b781c5252fcb812a17ab6275a9a484/pyarrow-21.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d2fe8e7f3ce329a71b7ddd7498b3cfac0eeb200c2789bd840234f0dc271a8efe", size = 32680622, upload-time = "2025-07-18T00:56:07.505Z" }, - { url = "https://files.pythonhosted.org/packages/84/52/79095d73a742aa0aba370c7942b1b655f598069489ab387fe47261a849e1/pyarrow-21.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f522e5709379d72fb3da7785aa489ff0bb87448a9dc5a75f45763a795a089ebd", size = 41104094, upload-time = "2025-07-18T00:56:10.994Z" }, - { url = "https://files.pythonhosted.org/packages/89/4b/7782438b551dbb0468892a276b8c789b8bbdb25ea5c5eb27faadd753e037/pyarrow-21.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:69cbbdf0631396e9925e048cfa5bce4e8c3d3b41562bbd70c685a8eb53a91e61", size = 42825576, upload-time = "2025-07-18T00:56:15.569Z" }, - { url = "https://files.pythonhosted.org/packages/b3/62/0f29de6e0a1e33518dec92c65be0351d32d7ca351e51ec5f4f837a9aab91/pyarrow-21.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:731c7022587006b755d0bdb27626a1a3bb004bb56b11fb30d98b6c1b4718579d", size = 43368342, upload-time = "2025-07-18T00:56:19.531Z" }, - { url = "https://files.pythonhosted.org/packages/90/c7/0fa1f3f29cf75f339768cc698c8ad4ddd2481c1742e9741459911c9ac477/pyarrow-21.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc56bc708f2d8ac71bd1dcb927e458c93cec10b98eb4120206a4091db7b67b99", size = 45131218, upload-time = "2025-07-18T00:56:23.347Z" }, - { url = "https://files.pythonhosted.org/packages/01/63/581f2076465e67b23bc5a37d4a2abff8362d389d29d8105832e82c9c811c/pyarrow-21.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:186aa00bca62139f75b7de8420f745f2af12941595bbbfa7ed3870ff63e25636", size = 26087551, upload-time = "2025-07-18T00:56:26.758Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ab/357d0d9648bb8241ee7348e564f2479d206ebe6e1c47ac5027c2e31ecd39/pyarrow-21.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:a7a102574faa3f421141a64c10216e078df467ab9576684d5cd696952546e2da", size = 31290064, upload-time = "2025-07-18T00:56:30.214Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8a/5685d62a990e4cac2043fc76b4661bf38d06efed55cf45a334b455bd2759/pyarrow-21.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:1e005378c4a2c6db3ada3ad4c217b381f6c886f0a80d6a316fe586b90f77efd7", size = 32727837, upload-time = "2025-07-18T00:56:33.935Z" }, - { url = "https://files.pythonhosted.org/packages/fc/de/c0828ee09525c2bafefd3e736a248ebe764d07d0fd762d4f0929dbc516c9/pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:65f8e85f79031449ec8706b74504a316805217b35b6099155dd7e227eef0d4b6", size = 41014158, upload-time = "2025-07-18T00:56:37.528Z" }, - { url = "https://files.pythonhosted.org/packages/6e/26/a2865c420c50b7a3748320b614f3484bfcde8347b2639b2b903b21ce6a72/pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3a81486adc665c7eb1a2bde0224cfca6ceaba344a82a971ef059678417880eb8", size = 42667885, upload-time = "2025-07-18T00:56:41.483Z" }, - { url = "https://files.pythonhosted.org/packages/0a/f9/4ee798dc902533159250fb4321267730bc0a107d8c6889e07c3add4fe3a5/pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fc0d2f88b81dcf3ccf9a6ae17f89183762c8a94a5bdcfa09e05cfe413acf0503", size = 43276625, upload-time = "2025-07-18T00:56:48.002Z" }, - { url = "https://files.pythonhosted.org/packages/5a/da/e02544d6997037a4b0d22d8e5f66bc9315c3671371a8b18c79ade1cefe14/pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6299449adf89df38537837487a4f8d3bd91ec94354fdd2a7d30bc11c48ef6e79", size = 44951890, upload-time = "2025-07-18T00:56:52.568Z" }, - { url = "https://files.pythonhosted.org/packages/e5/4e/519c1bc1876625fe6b71e9a28287c43ec2f20f73c658b9ae1d485c0c206e/pyarrow-21.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:222c39e2c70113543982c6b34f3077962b44fca38c0bd9e68bb6781534425c10", size = 26371006, upload-time = "2025-07-18T00:56:56.379Z" }, -] - -[[package]] -name = "pyasn1" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, -] - -[[package]] -name = "pyasn1-modules" -version = "0.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyasn1" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, -] - -[[package]] -name = "pybase64" -version = "1.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/38/32/5d25a15256d2e80d1e92be821f19fc49190e65a90ea86733cb5af2285449/pybase64-1.4.1.tar.gz", hash = "sha256:03fc365c601671add4f9e0713c2bc2485fa4ab2b32f0d3bb060bd7e069cdaa43", size = 136836, upload-time = "2025-03-02T11:13:57.109Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/63/68/32b6446f679a0236735bf55f7b6595a5398d614f4c29e022d205d3359858/pybase64-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7628c86c431e04ae192ffeff0f8ae96b70ff4c053ad666625e7d6335196ea8a", size = 38066, upload-time = "2025-03-02T11:10:09.239Z" }, - { url = "https://files.pythonhosted.org/packages/73/10/73637b81b54d785bc5873ba6a28d5b5062493a3801c37afb7734fa78ed09/pybase64-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5202939f188cf150e1bc56f8b0da54a2cae2dcb9b27f4f7d313b358f707e1f7f", size = 31487, upload-time = "2025-03-02T11:10:11.285Z" }, - { url = "https://files.pythonhosted.org/packages/ed/5c/64ffd0c251fbd672c1306ddc792762eec09d39d7748d2656592b5e24cd39/pybase64-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e15e0eaf665bcc5427c1f32f604ed02d599b7777e8b7f8391e943a8d7bc443f", size = 57334, upload-time = "2025-03-02T11:10:13.656Z" }, - { url = "https://files.pythonhosted.org/packages/f6/69/d5b5f2a0d036bd0cadd17b0e581c11863074a3aab2090b07209c5fc1e18a/pybase64-1.4.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a0206b4b65f7cc0e0b6c26428765d3f0bae1312cb9d0fcebfad7cc24dfae4788", size = 54342, upload-time = "2025-03-02T11:10:16.003Z" }, - { url = "https://files.pythonhosted.org/packages/d1/bf/521c75786f519745de80b50eed22d73f16df201a954fbd613de0fa8e96b7/pybase64-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:732c5a4f7b389e6655375e75bde6fbab15508c8ae819bf41bda2c0202a59ff19", size = 56996, upload-time = "2025-03-02T11:10:18.491Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f7/a510a06bea28ce17caec42a31d6587e196c288a9604a09af39191b410e76/pybase64-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ecc374ea70bcef1884d3745480e07d1502bfbb41ac138cc38445c58c685dee32", size = 57544, upload-time = "2025-03-02T11:10:21.395Z" }, - { url = "https://files.pythonhosted.org/packages/3f/68/e592b7641932a54a8255253865a646cfad4921471407263c33af47976023/pybase64-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a0433a4e76f10862817f303c2bf74371e118cb24124836bfb0d95ebc182dc97", size = 66115, upload-time = "2025-03-02T11:10:23.279Z" }, - { url = "https://files.pythonhosted.org/packages/4c/46/24f97d76fec6532a7a60133fd9691a8afab6c7eab791368d14353dac5488/pybase64-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25b8405f632cce8b2e2f991ec2e4074b6a98ea44273cd218ffc3f88524ed162a", size = 68719, upload-time = "2025-03-02T11:10:24.868Z" }, - { url = "https://files.pythonhosted.org/packages/25/27/5d8f1b530c4bc22c943ce4879f4e66aa879fe23ff411c8725b81a03bdf95/pybase64-1.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab02c31afe58b03d55a66fd9bd2cc4a04698b6bb2c33f68955aaec151542d838", size = 56136, upload-time = "2025-03-02T11:10:27.206Z" }, - { url = "https://files.pythonhosted.org/packages/f7/34/f40fea3fb306857d8e86473b1b5c2bc8d401c58ac424f59f8ec8fd7e55be/pybase64-1.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:8030ad8fe74c034cfad9a9a037c7b6ee85094b522c8b94c05e81df46e9a0eb5c", size = 49929, upload-time = "2025-03-02T11:10:29.069Z" }, - { url = "https://files.pythonhosted.org/packages/18/ae/7cd961e5cfb6fee5f3838586b0036876d0c58566f65d5973b78d4c090cc7/pybase64-1.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fb18c6a4defe85d23b16b1e6d6c7c3038cc402adfd8af14acc774dc585e814c4", size = 66380, upload-time = "2025-03-02T11:10:30.95Z" }, - { url = "https://files.pythonhosted.org/packages/85/a3/384601da9e09907d7509ec448afbce4be75a366db9ac36692c924dae7519/pybase64-1.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3f645629fae78e337faaa2ad7d35ced3f65b66f66629542d374641e30b218d1f", size = 55508, upload-time = "2025-03-02T11:10:32.13Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f7/74ae590bafed894c634bd3684ea0c86d4878c5ccd31e3a10ae1e5391bdf3/pybase64-1.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02ff55724616a11eebceac6c8445dadac79289ae8d1e40eed1b24aa7517fa225", size = 53781, upload-time = "2025-03-02T11:10:33.946Z" }, - { url = "https://files.pythonhosted.org/packages/2b/74/26c2d3f1893cc6904822fb8966dd722f432438273cce9e14f45ddfb454d0/pybase64-1.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:426e1ab673c744012d4b072fa6dc0642ca900b5c341f5e0c3a1c30b5dac332d1", size = 68233, upload-time = "2025-03-02T11:10:35.133Z" }, - { url = "https://files.pythonhosted.org/packages/09/10/f6a2bb04e11f7e639e7b59a41fd4597f68d9f3dde1014184ddaa480e3eac/pybase64-1.4.1-cp310-cp310-win32.whl", hash = "sha256:9101ee786648fc45b4765626eaf71114dd021b73543d8a3ab975df3dfdcca667", size = 34219, upload-time = "2025-03-02T11:10:36.298Z" }, - { url = "https://files.pythonhosted.org/packages/46/61/efc03bf48590681839f7391696c51d6d304f4d5df7f47828c373dc657c3c/pybase64-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9117f9be7f9a190e245dd7045b760b775d0b11ccc4414925cf725cdee807d5f6", size = 36414, upload-time = "2025-03-02T11:10:38.046Z" }, - { url = "https://files.pythonhosted.org/packages/55/b1/c6edc2630e4e574f681f60e2b00e7b852e7127f37603e440d28d21a2ea67/pybase64-1.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:aa4232a7082cca16db5de64f30056702d2d4ee4a5da1e2bbf9fd59bd3a67baed", size = 29637, upload-time = "2025-03-02T11:10:39.9Z" }, - { url = "https://files.pythonhosted.org/packages/ff/74/6f60bddbc6badd9a821e590f960fcf55b2008842b724552e062273d2f3a2/pybase64-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a230b64474f02075608d81fc19073c86cb4e63111d5c94f8bf77a3f2c0569956", size = 38068, upload-time = "2025-03-02T11:10:41.74Z" }, - { url = "https://files.pythonhosted.org/packages/0e/ce/1e56414745cb92ed0b22fd640af1d559d8161c28d26e288da7bcd2836f93/pybase64-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26ebcd7ccadde46ab35b16fee6f3b9478142833a164e10040b942ad5ccc8c4c0", size = 31485, upload-time = "2025-03-02T11:10:42.943Z" }, - { url = "https://files.pythonhosted.org/packages/96/38/f561708ec3740ac7f0395122672d663cc525295a1021a0b9c16aba19115b/pybase64-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f033501b08bbfc89a725f9a283b485348df2cb7acb8c41ca52ccfa76785d9343", size = 59642, upload-time = "2025-03-02T11:10:44.016Z" }, - { url = "https://files.pythonhosted.org/packages/43/70/71ed3d6d8905079668e75c6eeaa2e5c6fd4c33b0f8d4672e9ec99bb4925a/pybase64-1.4.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6634d77e2f4b559daf30234f2dc679de9de3ba88effbdc0354a68b3aa2d29d3", size = 56464, upload-time = "2025-03-02T11:10:45.116Z" }, - { url = "https://files.pythonhosted.org/packages/60/53/1558b2d756896f15ea6396e2791bb710a9f289a3e2a24db5bfcf203d54e6/pybase64-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e1837488c7aa9bc7ba7bb0449908e57ecfe444e3c7347a905a87450c7e523e00", size = 59197, upload-time = "2025-03-02T11:10:47.009Z" }, - { url = "https://files.pythonhosted.org/packages/0d/ae/300cb522d7f7eb543165843d28db4046909a8aabe110afa50cdab0947c9d/pybase64-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80e85e5ca298d3a9916c47e6fb0c47ebe5bf7996eac6983c887027b378e9bcae", size = 59803, upload-time = "2025-03-02T11:10:48.163Z" }, - { url = "https://files.pythonhosted.org/packages/b0/b4/355f03c656bb331e623466bc6be4307efd2c41cfe58fdbf869cfb126a70c/pybase64-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:480c0c444eb07e4855d2eeab3f91a70331b75862d7a3dce0e6d4caddbfb4c09b", size = 68444, upload-time = "2025-03-02T11:10:49.32Z" }, - { url = "https://files.pythonhosted.org/packages/6f/4b/8d0730e9507026e05a7e34daddcac3d548cf8ce51cda858d033b142fed4d/pybase64-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e25723ecf7c439f650192d43699aab0a22850dca9cc6d60377c42bb4df7812", size = 71184, upload-time = "2025-03-02T11:10:51.147Z" }, - { url = "https://files.pythonhosted.org/packages/53/95/4e7cda0cd38e5e38697fcb62ede30c42ed8f5a2427adc73296d2746ec12c/pybase64-1.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:82efee94d6bd93f7787afc42f260fa0b60e24c8dc7f172bd45cfe99fa39567ff", size = 58479, upload-time = "2025-03-02T11:10:52.908Z" }, - { url = "https://files.pythonhosted.org/packages/26/ed/cac0892746795de07b2e71f48e651af597ccb8b52ba36ac2afaa07e7da55/pybase64-1.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c15765be7921914d0dad0a2fb57c35a1811e1cbe2d1e47c39e0c66ed7db52898", size = 52148, upload-time = "2025-03-02T11:10:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/7e/ca/8eaae3ee3c0e7b8a827c00ca5d850a9188e0cab9575764ae3638cce6ff78/pybase64-1.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d1dcddfa521fb6cbab0385032d43f0ca13212459abd6efc381b6e9847e9fbd79", size = 68801, upload-time = "2025-03-02T11:10:55.416Z" }, - { url = "https://files.pythonhosted.org/packages/c7/55/a847b02b2c17a6353e7156f995a44bdd26b326332851fb35ee3a5dfedf82/pybase64-1.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd1de051b9b032d84e799af498b44499e90122a095da7dad89c2873518473c67", size = 57857, upload-time = "2025-03-02T11:10:56.607Z" }, - { url = "https://files.pythonhosted.org/packages/6e/6d/7562e73ab1dbf7d735e1a2da6be06a4bdb3bb8ddfecf3c29f25288528bb7/pybase64-1.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bf8213e6b8c658df2971c5a56df42202d7f89d5d6312d066d49923cc98a39299", size = 56075, upload-time = "2025-03-02T11:10:57.796Z" }, - { url = "https://files.pythonhosted.org/packages/99/a4/795935ad7ef2d066c082a9c852b8dd658f2c61a2de1742b46c576665edd5/pybase64-1.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7d83ab7822da5740f1d17c72fb451e9468e72976b89cfb9eb4f6a5b66491b5dc", size = 70710, upload-time = "2025-03-02T11:10:58.947Z" }, - { url = "https://files.pythonhosted.org/packages/13/16/b487ba1382fca5451cb18552333999a52c47d5e561d41b1ba17bf3bbf407/pybase64-1.4.1-cp311-cp311-win32.whl", hash = "sha256:7726e655134132dde59bddabcd74d140f818eeecc70d149267267d5e29335193", size = 34200, upload-time = "2025-03-02T11:11:00.841Z" }, - { url = "https://files.pythonhosted.org/packages/ea/a4/354cfd978a145cbeacba73f70266687f3dd34e1df1cdeb882c23153697a3/pybase64-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:9d5202cd4a8a0cd1b28c11730cf5da3c014450ad03732b5da03fac89b7693ec2", size = 36417, upload-time = "2025-03-02T11:11:02.006Z" }, - { url = "https://files.pythonhosted.org/packages/19/6c/5a576f95c79aa28a4b476ec84afe751ac0cab23572d9fd000b93adab6c76/pybase64-1.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:72808de9aab43112deb04003e5e0d060c7cb1a60c3dcf74bbf61a9d7c596c5af", size = 29638, upload-time = "2025-03-02T11:11:03.635Z" }, - { url = "https://files.pythonhosted.org/packages/a6/a9/43bac4f39401f7241d233ddaf9e6561860b2466798cfb83b9e7dbf89bc1b/pybase64-1.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbdcf77e424c91389f22bf10158851ce05c602c50a74ccf5943ee3f5ef4ba489", size = 38152, upload-time = "2025-03-02T11:11:07.576Z" }, - { url = "https://files.pythonhosted.org/packages/1e/bb/d0ae801e31a5052dbb1744a45318f822078dd4ce4cc7f49bfe97e7768f7e/pybase64-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af41e2e6015f980d15eae0df0c365df94c7587790aea236ba0bf48c65a9fa04e", size = 31488, upload-time = "2025-03-02T11:11:09.758Z" }, - { url = "https://files.pythonhosted.org/packages/be/34/bf4119a88b2ad0536a8ed9d66ce4d70ff8152eac00ef8a27e5ae35da4328/pybase64-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ac21c1943a15552347305943b1d0d6298fb64a98b67c750cb8fb2c190cdefd4", size = 59734, upload-time = "2025-03-02T11:11:11.493Z" }, - { url = "https://files.pythonhosted.org/packages/99/1c/1901547adc7d4f24bdcb2f75cb7dcd3975bff42f39da37d4bd218c608c60/pybase64-1.4.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:65567e8f4f31cf6e1a8cc570723cc6b18adda79b4387a18f8d93c157ff5f1979", size = 56529, upload-time = "2025-03-02T11:11:12.657Z" }, - { url = "https://files.pythonhosted.org/packages/c5/1e/1993e4b9a03e94fc53552285e3998079d864fff332798bf30c25afdac8f3/pybase64-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:988e987f8cfe2dfde7475baf5f12f82b2f454841aef3a174b694a57a92d5dfb0", size = 59114, upload-time = "2025-03-02T11:11:13.972Z" }, - { url = "https://files.pythonhosted.org/packages/c5/f6/061fee5b7ba38b8824dd95752ab7115cf183ffbd3330d5fc1734a47b0f9e/pybase64-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:92b2305ac2442b451e19d42c4650c3bb090d6aa9abd87c0c4d700267d8fa96b1", size = 60095, upload-time = "2025-03-02T11:11:15.182Z" }, - { url = "https://files.pythonhosted.org/packages/37/da/ccfe5d1a9f1188cd703390522e96a31045c5b93af84df04a98e69ada5c8b/pybase64-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1ff80e03357b09dab016f41b4c75cf06e9b19cda7f898e4f3681028a3dff29b", size = 68431, upload-time = "2025-03-02T11:11:17.059Z" }, - { url = "https://files.pythonhosted.org/packages/c3/d3/8ca4b0695876b52c0073a3557a65850b6d5c723333b5a271ab10a1085852/pybase64-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cdda297e668e118f6b9ba804e858ff49e3dd945d01fdd147de90445fd08927d", size = 71417, upload-time = "2025-03-02T11:11:19.178Z" }, - { url = "https://files.pythonhosted.org/packages/94/34/5f8f72d1b7b4ddb64c48d60160f3f4f03cfd0bfd2e7068d4558499d948ed/pybase64-1.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:51a24d21a21a959eb8884f24346a6480c4bd624aa7976c9761504d847a2f9364", size = 58429, upload-time = "2025-03-02T11:11:20.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/b7/edf53af308c6e8aada1e6d6a0a3789176af8cbae37a2ce084eb9da87bf33/pybase64-1.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b19e169ea1b8a15a03d3a379116eb7b17740803e89bc6eb3efcc74f532323cf7", size = 52228, upload-time = "2025-03-02T11:11:21.632Z" }, - { url = "https://files.pythonhosted.org/packages/0c/bf/c9df141e24a259f38a38bdda5a3b63206f13e612ecbd3880fa10625e0294/pybase64-1.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8a9f1b614efd41240c9bb2cf66031aa7a2c3c092c928f9d429511fe18d4a3fd1", size = 68632, upload-time = "2025-03-02T11:11:23.56Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ae/1aec72325a3c48f7776cc55a3bab8b168eb77aea821253da8b9f09713734/pybase64-1.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d9947b5e289e2c5b018ddc2aee2b9ed137b8aaaba7edfcb73623e576a2407740", size = 57682, upload-time = "2025-03-02T11:11:25.656Z" }, - { url = "https://files.pythonhosted.org/packages/4d/7a/7ad2799c0b3c4e2f7b993e1636468445c30870ca5485110b589b8921808d/pybase64-1.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ba4184ea43aa88a5ab8d6d15db284689765c7487ff3810764d8d823b545158e6", size = 56308, upload-time = "2025-03-02T11:11:26.803Z" }, - { url = "https://files.pythonhosted.org/packages/be/01/6008a4fbda0c4308dab00b95aedde8748032d7620bd95b686619c66917fe/pybase64-1.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4471257628785296efb2d50077fb9dfdbd4d2732c3487795224dd2644216fb07", size = 70784, upload-time = "2025-03-02T11:11:28.427Z" }, - { url = "https://files.pythonhosted.org/packages/27/31/913365a4f0e2922ec369ddaa3a1d6c11059acbe54531b003653efa007a48/pybase64-1.4.1-cp312-cp312-win32.whl", hash = "sha256:614561297ad14de315dd27381fd6ec3ea4de0d8206ba4c7678449afaff8a2009", size = 34271, upload-time = "2025-03-02T11:11:30.585Z" }, - { url = "https://files.pythonhosted.org/packages/d9/98/4d514d3e4c04819d80bccf9ea7b30d1cfc701832fa5ffca168f585004488/pybase64-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:35635db0d64fcbe9b3fad265314c052c47dc9bcef8dea17493ea8e3c15b2b972", size = 36496, upload-time = "2025-03-02T11:11:32.552Z" }, - { url = "https://files.pythonhosted.org/packages/c4/61/01353bc9c461e7b36d692daca3eee9616d8936ea6d8a64255ef7ec9ac307/pybase64-1.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:b4ccb438c4208ff41a260b70994c30a8631051f3b025cdca48be586b068b8f49", size = 29692, upload-time = "2025-03-02T11:11:33.735Z" }, - { url = "https://files.pythonhosted.org/packages/4b/1a/4e243ba702c07df3df3ba1795cfb02cf7a4242c53fc574b06a2bfa4f8478/pybase64-1.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1c38d9c4a7c132d45859af8d5364d3ce90975a42bd5995d18d174fb57621973", size = 38149, upload-time = "2025-03-02T11:11:35.537Z" }, - { url = "https://files.pythonhosted.org/packages/9c/35/3eae81bc8688a83f8b5bb84979d88e2cc3c3279a3b870a506f277d746c56/pybase64-1.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ab0b93ea93cf1f56ca4727d678a9c0144c2653e9de4e93e789a92b4e098c07d9", size = 31485, upload-time = "2025-03-02T11:11:36.656Z" }, - { url = "https://files.pythonhosted.org/packages/48/55/d99b9ff8083573bbf97fc433bbc20e2efb612792025f3bad0868c96c37ce/pybase64-1.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:644f393e9bb7f3bacc5cbd3534d02e1b660b258fc8315ecae74d2e23265e5c1f", size = 59738, upload-time = "2025-03-02T11:11:38.468Z" }, - { url = "https://files.pythonhosted.org/packages/63/3c/051512b9e139a11585447b286ede5ac3b284ce5df85de37eb8cff57d90f8/pybase64-1.4.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff172a4dacbd964e5edcf1c2152dae157aabf856508aed15276f46d04a22128e", size = 56239, upload-time = "2025-03-02T11:11:39.718Z" }, - { url = "https://files.pythonhosted.org/packages/af/11/f40c5cca587274d50baee88540a7839576204cb425fe2f73a752ea48ae74/pybase64-1.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2ab7b4535abc72d40114540cae32c9e07d76ffba132bdd5d4fff5fe340c5801", size = 59137, upload-time = "2025-03-02T11:11:41.524Z" }, - { url = "https://files.pythonhosted.org/packages/1a/a9/ace9f6d0926962c083671d7df247de442ef63cd06bd134f7c8251aab5c51/pybase64-1.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da66eb7cfb641486944fb0b95ab138e691ab78503115022caf992b6c89b10396", size = 60109, upload-time = "2025-03-02T11:11:42.699Z" }, - { url = "https://files.pythonhosted.org/packages/88/9c/d4e308b4b4e3b513bc084fc71b4e2dd00d21d4cd245a9a28144d2f6b03c9/pybase64-1.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:678f573ea1d06183b32d0336044fb5db60396333599dffcce28ffa3b68319fc0", size = 68391, upload-time = "2025-03-02T11:11:43.898Z" }, - { url = "https://files.pythonhosted.org/packages/53/87/e184bf982a3272f1021f417e5a18fac406e042c606950e9082fc3b0cec30/pybase64-1.4.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bccdf340c2a1d3dd1f41528f192265ddce7f8df1ee4f7b5b9163cdba0fe0ccb", size = 71438, upload-time = "2025-03-02T11:11:45.112Z" }, - { url = "https://files.pythonhosted.org/packages/2f/7f/d6e6a72db055eb2dc01ab877d8ee39d05cb665403433ff922fb95d1003ad/pybase64-1.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1ddf6366c34eb78931fd8a47c00cb886ba187a5ff8e6dbffe1d9dae4754b6c28", size = 58437, upload-time = "2025-03-02T11:11:47.034Z" }, - { url = "https://files.pythonhosted.org/packages/71/ef/c9051f2c0128194b861f3cd3b2d211b8d4d21ed2be354aa669fe29a059d8/pybase64-1.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:500afcb717a84e262c68f0baf9c56abaf97e2f058ba80c5546a9ed21ff4b705f", size = 52267, upload-time = "2025-03-02T11:11:48.448Z" }, - { url = "https://files.pythonhosted.org/packages/12/92/ae30a54eaa437989839c4f2404c1f004d7383c0f46d6ebb83546d587d2a7/pybase64-1.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d2de043312a1e7f15ee6d2b7d9e39ee6afe24f144e2248cce942b6be357b70d8", size = 68659, upload-time = "2025-03-02T11:11:49.615Z" }, - { url = "https://files.pythonhosted.org/packages/2b/65/d94788a35904f21694c4c581bcee2e165bec2408cc6fbed85a7fef5959ae/pybase64-1.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c36e214c25fb8dd4f3ecdaa0ff90073b793056e0065cc0a1e1e5525a6866a1ad", size = 57727, upload-time = "2025-03-02T11:11:50.843Z" }, - { url = "https://files.pythonhosted.org/packages/d0/97/8db416066b7917909c38346c03a8f3e6d4fc8a1dc98636408156514269ad/pybase64-1.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:8ec003224f6e36e8e607a1bb8df182b367c87ca7135788ffe89173c7d5085005", size = 56302, upload-time = "2025-03-02T11:11:52.547Z" }, - { url = "https://files.pythonhosted.org/packages/70/0b/98f0601391befe0f19aa8cbda821c62d95056a94cc41d452fe893d205523/pybase64-1.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c536c6ed161e6fb19f6acd6074f29a4c78cb41c9155c841d56aec1a4d20d5894", size = 70779, upload-time = "2025-03-02T11:11:53.735Z" }, - { url = "https://files.pythonhosted.org/packages/cc/07/116119c5b20688c052697f677cf56f05aa766535ff7691aba38447d4a0d8/pybase64-1.4.1-cp313-cp313-win32.whl", hash = "sha256:1d34872e5aa2eff9dc54cedaf36038bbfbd5a3440fdf0bdc5b3c81c54ef151ea", size = 34266, upload-time = "2025-03-02T11:11:54.892Z" }, - { url = "https://files.pythonhosted.org/packages/c0/f5/a7eed9f3692209a9869a28bdd92deddf8cbffb06b40954f89f4577e5c96e/pybase64-1.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b7765515d7e0a48ddfde914dc2b1782234ac188ce3fab173b078a6e82ec7017", size = 36488, upload-time = "2025-03-02T11:11:56.063Z" }, - { url = "https://files.pythonhosted.org/packages/5d/8a/0d65c4dcda06487305035f24888ffed219897c03fb7834635d5d5e27dae1/pybase64-1.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:7fb782f3ceb30e24dc4d8d99c1221a381917bffaf85d29542f0f25b51829987c", size = 29690, upload-time = "2025-03-02T11:11:57.702Z" }, - { url = "https://files.pythonhosted.org/packages/a3/83/646d65fafe5e6edbdaf4c9548efb2e1dd7784caddbde3ff8a843dd942b0f/pybase64-1.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2a98d323e97444a38db38e022ccaf1d3e053b1942455790a93f29086c687855f", size = 38506, upload-time = "2025-03-02T11:11:58.936Z" }, - { url = "https://files.pythonhosted.org/packages/87/14/dbf7fbbe91d71c8044fefe20d22480ad64097e2ba424944de512550e12a4/pybase64-1.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19ef58d36b9b32024768fcedb024f32c05eb464128c75c07cac2b50c9ed47f4a", size = 31894, upload-time = "2025-03-02T11:12:00.762Z" }, - { url = "https://files.pythonhosted.org/packages/bd/5d/f8a47da2a5f8b599297b307d3bd0293adedc4e135be310620f061906070f/pybase64-1.4.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04fee0f5c174212868fde97b109db8fac8249b306a00ea323531ee61c7b0f398", size = 65212, upload-time = "2025-03-02T11:12:01.911Z" }, - { url = "https://files.pythonhosted.org/packages/90/95/ad9869c7cdcce3e8ada619dab5f9f2eff315ffb001704a3718c1597a2119/pybase64-1.4.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47737ff9eabc14b7553de6bc6395d67c5be80afcdbd25180285d13e089e40888", size = 60300, upload-time = "2025-03-02T11:12:03.071Z" }, - { url = "https://files.pythonhosted.org/packages/c2/91/4d8268b2488ae10c485cba04ecc23a5a7bdfb47ce9b876017b11ea0249a2/pybase64-1.4.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d8b5888cc239654fe68a0db196a18575ffc8b1c8c8f670c2971a44e3b7fe682", size = 63773, upload-time = "2025-03-02T11:12:04.231Z" }, - { url = "https://files.pythonhosted.org/packages/ae/1a/8afd27facc0723b1d69231da8c59a2343feb255f5db16f8b8765ddf1600b/pybase64-1.4.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a1af8d387dbce05944b65a618639918804b2d4438fed32bb7f06d9c90dbed01", size = 64684, upload-time = "2025-03-02T11:12:05.409Z" }, - { url = "https://files.pythonhosted.org/packages/cc/cd/422c74397210051125419fc8e425506ff27c04665459e18c8f7b037a754b/pybase64-1.4.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b0093c52bd099b80e422ad8cddf6f2c1ac1b09cb0922cca04891d736c2ad647", size = 72880, upload-time = "2025-03-02T11:12:06.652Z" }, - { url = "https://files.pythonhosted.org/packages/04/c1/c4f02f1d5f8e8a3d75715a3dd04196dde9e263e471470d099a26e91ebe2f/pybase64-1.4.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15e54f9b2a1686f5bbdc4ac8440b6f6145d9699fd53aa30f347931f3063b0915", size = 75344, upload-time = "2025-03-02T11:12:07.816Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0b/013006ca984f0472476cf7c0540db2e2b1f997d52977b15842a7681ab79c/pybase64-1.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3a0fdcf13f986c82f7ef04a1cd1163c70f39662d6f02aa4e7b448dacb966b39f", size = 63439, upload-time = "2025-03-02T11:12:09.669Z" }, - { url = "https://files.pythonhosted.org/packages/8a/d5/7848543b3c8dcc5396be574109acbe16706e6a9b4dbd9fc4e22f211668a9/pybase64-1.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:ac03f8eba72dd6da15dc25bb3e1b440ad21f5cb7ee2e6ffbbae4bd1b206bb503", size = 56004, upload-time = "2025-03-02T11:12:10.981Z" }, - { url = "https://files.pythonhosted.org/packages/63/58/70de1efb1b6f21d7aaea33578868214f82925d969e2091f7de3175a10092/pybase64-1.4.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:ea835272570aa811e08ae17612632b057623a9b27265d44288db666c02b438dc", size = 72460, upload-time = "2025-03-02T11:12:13.122Z" }, - { url = "https://files.pythonhosted.org/packages/90/0d/aa52dd1b1f25b98b1d94cc0522f864b03de55aa115de67cb6dbbddec4f46/pybase64-1.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:8f52c4c29a35381f3ae06d520144a0707132f2cbfb53bc907b74811734bc4ef3", size = 62295, upload-time = "2025-03-02T11:12:15.004Z" }, - { url = "https://files.pythonhosted.org/packages/39/cf/4d378a330249c937676ee8eab7992ec700ade362f35db36c15922b33b1c8/pybase64-1.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fa5cdabcb4d21b7e56d0b2edd7ed6fa933ac3535be30c2a9cf0a2e270c5369c8", size = 60604, upload-time = "2025-03-02T11:12:16.23Z" }, - { url = "https://files.pythonhosted.org/packages/15/45/e3f23929018d0aada84246ddd398843050971af614da67450bb20f45f880/pybase64-1.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8db9acf239bb71a888748bc9ffc12c97c1079393a38bc180c0548330746ece94", size = 74500, upload-time = "2025-03-02T11:12:17.48Z" }, - { url = "https://files.pythonhosted.org/packages/8d/98/6d2adaec318cae6ee968a10df0a7e870f17ee385ef623bcb2ab63fa11b59/pybase64-1.4.1-cp313-cp313t-win32.whl", hash = "sha256:bc06186cfa9a43e871fdca47c1379bdf1cfe964bd94a47f0919a1ffab195b39e", size = 34543, upload-time = "2025-03-02T11:12:18.625Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e7/1823de02d2c23324cf1142e9dce53b032085cee06c3f982806040f975ce7/pybase64-1.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:02c3647d270af1a3edd35e485bb7ccfe82180b8347c49e09973466165c03d7aa", size = 36909, upload-time = "2025-03-02T11:12:20.122Z" }, - { url = "https://files.pythonhosted.org/packages/43/6a/8ec0e4461bf89ef0499ef6c746b081f3520a1e710aeb58730bae693e0681/pybase64-1.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:4b3635e5873707906e72963c447a67969cfc6bac055432a57a91d7a4d5164fdf", size = 29961, upload-time = "2025-03-02T11:12:21.908Z" }, - { url = "https://files.pythonhosted.org/packages/34/22/4fcbd6b8dcbcabe30fdcd4d5145445cffc6724a90425dda0043c1cbd4919/pybase64-1.4.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b0bdb646f859132c68230efabc09fd8828ca20c59de7d53082f372c4b8af7aaa", size = 38055, upload-time = "2025-03-02T11:13:21.751Z" }, - { url = "https://files.pythonhosted.org/packages/16/d8/9a6c325c31c81897349c83bd4857f09f78d342bb03f0107df5ab9de0de1a/pybase64-1.4.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8d4bf9c94bc948cb3c3b0e38074d0de04f23d35765a306059417751e982da384", size = 31354, upload-time = "2025-03-02T11:13:23.016Z" }, - { url = "https://files.pythonhosted.org/packages/72/30/4212a953d3fc4affa5ffa652096440daf1093ad6db734b17231f1f82a79a/pybase64-1.4.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b31da1466faf3cfa775027d161d07640f3d1c6bbc8edf3725f8833ed0b25a2f", size = 35265, upload-time = "2025-03-02T11:13:24.81Z" }, - { url = "https://files.pythonhosted.org/packages/12/b4/a54e9e3eb7f11f80a659eed05b0bfa6bc68ad8e7ec075e40236c7987d18e/pybase64-1.4.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc9a3f56630e707dbe7a34383943a1daefa699bc99c3250f8af9f8245056fccd", size = 40968, upload-time = "2025-03-02T11:13:26.165Z" }, - { url = "https://files.pythonhosted.org/packages/6d/f1/d6bc1a548edc806ce8d25b6d761d2aed68abc3162f072f984940f59ae15b/pybase64-1.4.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdabd0d7fda2517ff36559189f7c00b376feafbd5d23bf5914e256246d29d7e", size = 41117, upload-time = "2025-03-02T11:13:27.433Z" }, - { url = "https://files.pythonhosted.org/packages/9a/6c/5952201a062ac4746fc767c8556a7b933cb59295068b9dba0bcba8bde378/pybase64-1.4.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:62e42807bde3a7d18a0a7d35bd7fb1fe68f99c897eea8d3ea3aa0791b91358eb", size = 36804, upload-time = "2025-03-02T11:13:29.414Z" }, - { url = "https://files.pythonhosted.org/packages/a8/3e/90633da698742bfd11a1d6301295e9974c2f9e0e510aaae8cdd26cd10880/pybase64-1.4.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e8c28700ccf55348a7a4ad3554e6b4c5b83c640bfaa272fee6b4d0030566fe05", size = 38056, upload-time = "2025-03-02T11:13:30.6Z" }, - { url = "https://files.pythonhosted.org/packages/b1/02/79bdf96a780c3d1f4e9f1b583525247f3a33afebbba1e12e57fb28c395e7/pybase64-1.4.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eb09bd829d4fef567505212b6bb87cd7a42b5aa2a3b83fc2bd61a188db7793e0", size = 31352, upload-time = "2025-03-02T11:13:32.395Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d0/4f8135c2459724a834a70481f6bb8af3e89ff527c9b5cff0b799321e29d6/pybase64-1.4.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc9504c4c2e893e0a6c1cc80bce51907e3461288289f630eab22b5735eba1104", size = 35262, upload-time = "2025-03-02T11:13:33.55Z" }, - { url = "https://files.pythonhosted.org/packages/21/c6/45ace9c84ccc9d51002c5bcfe8c50e7660f064e2bc272a30c7802036f1f3/pybase64-1.4.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:45a785a3d29faf0309910d96e13c34870adb4ae43ea262868c6cf6a311936f37", size = 40968, upload-time = "2025-03-02T11:13:34.748Z" }, - { url = "https://files.pythonhosted.org/packages/9d/d5/1bf0b5354ca404ba096e99e2634c27836c212affe722bd2ade7103fd3c48/pybase64-1.4.1-pp311-pypy311_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10e2cb40869fe703484ba89ae50e05d63a169f7c42db59e29f8af0890c50515d", size = 41107, upload-time = "2025-03-02T11:13:35.996Z" }, - { url = "https://files.pythonhosted.org/packages/0b/d7/0987f3d1c8196ad9affea9102c135a45342e1fa5affb849bf31bd633d000/pybase64-1.4.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:1a18644fb3e940ed622738f2ee14d9a2811bb542ffd3f85c3fb661130675ac4f", size = 36817, upload-time = "2025-03-02T11:13:37.624Z" }, -] - -[[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]] -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 = "pymdown-extensions" -version = "10.16" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1a/0a/c06b542ac108bfc73200677309cd9188a3a01b127a63f20cadc18d873d88/pymdown_extensions-10.16.tar.gz", hash = "sha256:71dac4fca63fabeffd3eb9038b756161a33ec6e8d230853d3cecf562155ab3de", size = 853197, upload-time = "2025-06-21T17:56:36.974Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/d4/10bb14004d3c792811e05e21b5e5dcae805aacb739bd12a0540967b99592/pymdown_extensions-10.16-py3-none-any.whl", hash = "sha256:f5dd064a4db588cb2d95229fc4ee63a1b16cc8b4d0e6145c0899ed8723da1df2", size = 266143, upload-time = "2025-06-21T17:56:35.356Z" }, -] - -[[package]] -name = "pypika" -version = "0.48.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/2c/94ed7b91db81d61d7096ac8f2d325ec562fc75e35f3baea8749c85b28784/PyPika-0.48.9.tar.gz", hash = "sha256:838836a61747e7c8380cd1b7ff638694b7a7335345d0f559b04b2cd832ad5378", size = 67259, upload-time = "2022-03-15T11:22:57.066Z" } - -[[package]] -name = "pyproject-hooks" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, -] - -[[package]] -name = "pyreadline3" -version = "3.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839, upload-time = "2024-09-19T02:40:10.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178, upload-time = "2024-09-19T02:40:08.598Z" }, -] - -[[package]] -name = "pytest" -version = "8.4.1" -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/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, -] - -[[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.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coverage", extra = ["toml"] }, - { name = "pluggy" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, -] - -[[package]] -name = "pytest-mock" -version = "3.14.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241, upload-time = "2025-05-26T13:58:45.167Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[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-jose" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ecdsa" }, - { name = "pyasn1" }, - { name = "rsa" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c6/77/3a1c9039db7124eb039772b935f2244fbb73fc8ee65b9acf2375da1c07bf/python_jose-3.5.0.tar.gz", hash = "sha256:fb4eaa44dbeb1c26dcc69e4bd7ec54a1cb8dd64d3b4d81ef08d90ff453f2b01b", size = 92726, upload-time = "2025-05-28T17:31:54.288Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/c3/0bd11992072e6a1c513b16500a5d07f91a24017c5909b02c72c62d7ad024/python_jose-3.5.0-py2.py3-none-any.whl", hash = "sha256:abd1202f23d34dfad2c3d28cb8617b90acf34132c7afd60abd0b0b7d3cb55771", size = 34624, upload-time = "2025-05-28T17:31:52.802Z" }, -] - -[package.optional-dependencies] -cryptography = [ - { name = "cryptography" }, -] - -[[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 = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - -[[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 = "pyyaml-env-tag" -version = "1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, -] - -[[package]] -name = "readme-renderer" -version = "44.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "nh3" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/a9/104ec9234c8448c4379768221ea6df01260cd6c2ce13182d4eac531c8342/readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", size = 32056, upload-time = "2024-07-08T15:00:57.805Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", size = 13310, upload-time = "2024-07-08T15:00:56.577Z" }, -] - -[[package]] -name = "redis" -version = "6.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "async-timeout", marker = "python_full_version < '3.11.3'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ea/9a/0551e01ba52b944f97480721656578c8a7c46b51b99d66814f85fe3a4f3e/redis-6.2.0.tar.gz", hash = "sha256:e821f129b75dde6cb99dd35e5c76e8c49512a5a0d8dfdc560b2fbd44b85ca977", size = 4639129, upload-time = "2025-05-28T05:01:18.91Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/67/e60968d3b0e077495a8fee89cf3f2373db98e528288a48f1ee44967f6e8c/redis-6.2.0-py3-none-any.whl", hash = "sha256:c8ddf316ee0aab65f04a11229e94a64b2618451dab7a67cb2f77eb799d872d5e", size = 278659, upload-time = "2025-05-28T05:01:16.955Z" }, -] - -[[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 = "regex" -version = "2024.11.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494, upload-time = "2024-11-06T20:12:31.635Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674, upload-time = "2024-11-06T20:08:57.575Z" }, - { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684, upload-time = "2024-11-06T20:08:59.787Z" }, - { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589, upload-time = "2024-11-06T20:09:01.896Z" }, - { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511, upload-time = "2024-11-06T20:09:04.062Z" }, - { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149, upload-time = "2024-11-06T20:09:06.237Z" }, - { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707, upload-time = "2024-11-06T20:09:07.715Z" }, - { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702, upload-time = "2024-11-06T20:09:10.101Z" }, - { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976, upload-time = "2024-11-06T20:09:11.566Z" }, - { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397, upload-time = "2024-11-06T20:09:13.119Z" }, - { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726, upload-time = "2024-11-06T20:09:14.85Z" }, - { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098, upload-time = "2024-11-06T20:09:16.504Z" }, - { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325, upload-time = "2024-11-06T20:09:18.698Z" }, - { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277, upload-time = "2024-11-06T20:09:21.725Z" }, - { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197, upload-time = "2024-11-06T20:09:24.092Z" }, - { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714, upload-time = "2024-11-06T20:09:26.36Z" }, - { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042, upload-time = "2024-11-06T20:09:28.762Z" }, - { url = "https://files.pythonhosted.org/packages/58/58/7e4d9493a66c88a7da6d205768119f51af0f684fe7be7bac8328e217a52c/regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638", size = 482669, upload-time = "2024-11-06T20:09:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/34/4c/8f8e631fcdc2ff978609eaeef1d6994bf2f028b59d9ac67640ed051f1218/regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7", size = 287684, upload-time = "2024-11-06T20:09:32.915Z" }, - { url = "https://files.pythonhosted.org/packages/c5/1b/f0e4d13e6adf866ce9b069e191f303a30ab1277e037037a365c3aad5cc9c/regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20", size = 284589, upload-time = "2024-11-06T20:09:35.504Z" }, - { url = "https://files.pythonhosted.org/packages/25/4d/ab21047f446693887f25510887e6820b93f791992994f6498b0318904d4a/regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114", size = 792121, upload-time = "2024-11-06T20:09:37.701Z" }, - { url = "https://files.pythonhosted.org/packages/45/ee/c867e15cd894985cb32b731d89576c41a4642a57850c162490ea34b78c3b/regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3", size = 831275, upload-time = "2024-11-06T20:09:40.371Z" }, - { url = "https://files.pythonhosted.org/packages/b3/12/b0f480726cf1c60f6536fa5e1c95275a77624f3ac8fdccf79e6727499e28/regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f", size = 818257, upload-time = "2024-11-06T20:09:43.059Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ce/0d0e61429f603bac433910d99ef1a02ce45a8967ffbe3cbee48599e62d88/regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0", size = 792727, upload-time = "2024-11-06T20:09:48.19Z" }, - { url = "https://files.pythonhosted.org/packages/e4/c1/243c83c53d4a419c1556f43777ccb552bccdf79d08fda3980e4e77dd9137/regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55", size = 780667, upload-time = "2024-11-06T20:09:49.828Z" }, - { url = "https://files.pythonhosted.org/packages/c5/f4/75eb0dd4ce4b37f04928987f1d22547ddaf6c4bae697623c1b05da67a8aa/regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89", size = 776963, upload-time = "2024-11-06T20:09:51.819Z" }, - { url = "https://files.pythonhosted.org/packages/16/5d/95c568574e630e141a69ff8a254c2f188b4398e813c40d49228c9bbd9875/regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d", size = 784700, upload-time = "2024-11-06T20:09:53.982Z" }, - { url = "https://files.pythonhosted.org/packages/8e/b5/f8495c7917f15cc6fee1e7f395e324ec3e00ab3c665a7dc9d27562fd5290/regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34", size = 848592, upload-time = "2024-11-06T20:09:56.222Z" }, - { url = "https://files.pythonhosted.org/packages/1c/80/6dd7118e8cb212c3c60b191b932dc57db93fb2e36fb9e0e92f72a5909af9/regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d", size = 852929, upload-time = "2024-11-06T20:09:58.642Z" }, - { url = "https://files.pythonhosted.org/packages/11/9b/5a05d2040297d2d254baf95eeeb6df83554e5e1df03bc1a6687fc4ba1f66/regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45", size = 781213, upload-time = "2024-11-06T20:10:00.867Z" }, - { url = "https://files.pythonhosted.org/packages/26/b7/b14e2440156ab39e0177506c08c18accaf2b8932e39fb092074de733d868/regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9", size = 261734, upload-time = "2024-11-06T20:10:03.361Z" }, - { url = "https://files.pythonhosted.org/packages/80/32/763a6cc01d21fb3819227a1cc3f60fd251c13c37c27a73b8ff4315433a8e/regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60", size = 274052, upload-time = "2024-11-06T20:10:05.179Z" }, - { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781, upload-time = "2024-11-06T20:10:07.07Z" }, - { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455, upload-time = "2024-11-06T20:10:09.117Z" }, - { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759, upload-time = "2024-11-06T20:10:11.155Z" }, - { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976, upload-time = "2024-11-06T20:10:13.24Z" }, - { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077, upload-time = "2024-11-06T20:10:15.37Z" }, - { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160, upload-time = "2024-11-06T20:10:19.027Z" }, - { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896, upload-time = "2024-11-06T20:10:21.85Z" }, - { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997, upload-time = "2024-11-06T20:10:24.329Z" }, - { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725, upload-time = "2024-11-06T20:10:28.067Z" }, - { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481, upload-time = "2024-11-06T20:10:31.612Z" }, - { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896, upload-time = "2024-11-06T20:10:34.054Z" }, - { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138, upload-time = "2024-11-06T20:10:36.142Z" }, - { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692, upload-time = "2024-11-06T20:10:38.394Z" }, - { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135, upload-time = "2024-11-06T20:10:40.367Z" }, - { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567, upload-time = "2024-11-06T20:10:43.467Z" }, - { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525, upload-time = "2024-11-06T20:10:45.19Z" }, - { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324, upload-time = "2024-11-06T20:10:47.177Z" }, - { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617, upload-time = "2024-11-06T20:10:49.312Z" }, - { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023, upload-time = "2024-11-06T20:10:51.102Z" }, - { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072, upload-time = "2024-11-06T20:10:52.926Z" }, - { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130, upload-time = "2024-11-06T20:10:54.828Z" }, - { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857, upload-time = "2024-11-06T20:10:56.634Z" }, - { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006, upload-time = "2024-11-06T20:10:59.369Z" }, - { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650, upload-time = "2024-11-06T20:11:02.042Z" }, - { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545, upload-time = "2024-11-06T20:11:03.933Z" }, - { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045, upload-time = "2024-11-06T20:11:06.497Z" }, - { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182, upload-time = "2024-11-06T20:11:09.06Z" }, - { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733, upload-time = "2024-11-06T20:11:11.256Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122, upload-time = "2024-11-06T20:11:13.161Z" }, - { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545, upload-time = "2024-11-06T20:11:15Z" }, -] - -[[package]] -name = "requests" -version = "2.32.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, -] - -[[package]] -name = "requests-oauthlib" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "oauthlib" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, -] - -[[package]] -name = "requests-toolbelt" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } -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 = "rfc3986" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", size = 49026, upload-time = "2022-01-10T00:52:30.832Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", size = 31326, upload-time = "2022-01-10T00:52:29.594Z" }, -] - -[[package]] -name = "rich" -version = "14.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/aa/4456d84bbb54adc6a916fb10c9b374f78ac840337644e4a5eda229c81275/rpds_py-0.26.0.tar.gz", hash = "sha256:20dae58a859b0906f0685642e591056f1e787f3a8b39c8e8749a45dc7d26bdb0", size = 27385, upload-time = "2025-07-01T15:57:13.958Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/31/1459645f036c3dfeacef89e8e5825e430c77dde8489f3b99eaafcd4a60f5/rpds_py-0.26.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4c70c70f9169692b36307a95f3d8c0a9fcd79f7b4a383aad5eaa0e9718b79b37", size = 372466, upload-time = "2025-07-01T15:53:40.55Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ff/3d0727f35836cc8773d3eeb9a46c40cc405854e36a8d2e951f3a8391c976/rpds_py-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:777c62479d12395bfb932944e61e915741e364c843afc3196b694db3d669fcd0", size = 357825, upload-time = "2025-07-01T15:53:42.247Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ce/badc5e06120a54099ae287fa96d82cbb650a5f85cf247ffe19c7b157fd1f/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec671691e72dff75817386aa02d81e708b5a7ec0dec6669ec05213ff6b77e1bd", size = 381530, upload-time = "2025-07-01T15:53:43.585Z" }, - { url = "https://files.pythonhosted.org/packages/1e/a5/fa5d96a66c95d06c62d7a30707b6a4cfec696ab8ae280ee7be14e961e118/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a1cb5d6ce81379401bbb7f6dbe3d56de537fb8235979843f0d53bc2e9815a79", size = 396933, upload-time = "2025-07-01T15:53:45.78Z" }, - { url = "https://files.pythonhosted.org/packages/00/a7/7049d66750f18605c591a9db47d4a059e112a0c9ff8de8daf8fa0f446bba/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f789e32fa1fb6a7bf890e0124e7b42d1e60d28ebff57fe806719abb75f0e9a3", size = 513973, upload-time = "2025-07-01T15:53:47.085Z" }, - { url = "https://files.pythonhosted.org/packages/0e/f1/528d02c7d6b29d29fac8fd784b354d3571cc2153f33f842599ef0cf20dd2/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c55b0a669976cf258afd718de3d9ad1b7d1fe0a91cd1ab36f38b03d4d4aeaaf", size = 402293, upload-time = "2025-07-01T15:53:48.117Z" }, - { url = "https://files.pythonhosted.org/packages/15/93/fde36cd6e4685df2cd08508f6c45a841e82f5bb98c8d5ecf05649522acb5/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c70d9ec912802ecfd6cd390dadb34a9578b04f9bcb8e863d0a7598ba5e9e7ccc", size = 383787, upload-time = "2025-07-01T15:53:50.874Z" }, - { url = "https://files.pythonhosted.org/packages/69/f2/5007553aaba1dcae5d663143683c3dfd03d9395289f495f0aebc93e90f24/rpds_py-0.26.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3021933c2cb7def39d927b9862292e0f4c75a13d7de70eb0ab06efed4c508c19", size = 416312, upload-time = "2025-07-01T15:53:52.046Z" }, - { url = "https://files.pythonhosted.org/packages/8f/a7/ce52c75c1e624a79e48a69e611f1c08844564e44c85db2b6f711d76d10ce/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a7898b6ca3b7d6659e55cdac825a2e58c638cbf335cde41f4619e290dd0ad11", size = 558403, upload-time = "2025-07-01T15:53:53.192Z" }, - { url = "https://files.pythonhosted.org/packages/79/d5/e119db99341cc75b538bf4cb80504129fa22ce216672fb2c28e4a101f4d9/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:12bff2ad9447188377f1b2794772f91fe68bb4bbfa5a39d7941fbebdbf8c500f", size = 588323, upload-time = "2025-07-01T15:53:54.336Z" }, - { url = "https://files.pythonhosted.org/packages/93/94/d28272a0b02f5fe24c78c20e13bbcb95f03dc1451b68e7830ca040c60bd6/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:191aa858f7d4902e975d4cf2f2d9243816c91e9605070aeb09c0a800d187e323", size = 554541, upload-time = "2025-07-01T15:53:55.469Z" }, - { url = "https://files.pythonhosted.org/packages/93/e0/8c41166602f1b791da892d976057eba30685486d2e2c061ce234679c922b/rpds_py-0.26.0-cp310-cp310-win32.whl", hash = "sha256:b37a04d9f52cb76b6b78f35109b513f6519efb481d8ca4c321f6a3b9580b3f45", size = 220442, upload-time = "2025-07-01T15:53:56.524Z" }, - { url = "https://files.pythonhosted.org/packages/87/f0/509736bb752a7ab50fb0270c2a4134d671a7b3038030837e5536c3de0e0b/rpds_py-0.26.0-cp310-cp310-win_amd64.whl", hash = "sha256:38721d4c9edd3eb6670437d8d5e2070063f305bfa2d5aa4278c51cedcd508a84", size = 231314, upload-time = "2025-07-01T15:53:57.842Z" }, - { url = "https://files.pythonhosted.org/packages/09/4c/4ee8f7e512030ff79fda1df3243c88d70fc874634e2dbe5df13ba4210078/rpds_py-0.26.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9e8cb77286025bdb21be2941d64ac6ca016130bfdcd228739e8ab137eb4406ed", size = 372610, upload-time = "2025-07-01T15:53:58.844Z" }, - { url = "https://files.pythonhosted.org/packages/fa/9d/3dc16be00f14fc1f03c71b1d67c8df98263ab2710a2fbd65a6193214a527/rpds_py-0.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e09330b21d98adc8ccb2dbb9fc6cb434e8908d4c119aeaa772cb1caab5440a0", size = 358032, upload-time = "2025-07-01T15:53:59.985Z" }, - { url = "https://files.pythonhosted.org/packages/e7/5a/7f1bf8f045da2866324a08ae80af63e64e7bfaf83bd31f865a7b91a58601/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9c1b92b774b2e68d11193dc39620d62fd8ab33f0a3c77ecdabe19c179cdbc1", size = 381525, upload-time = "2025-07-01T15:54:01.162Z" }, - { url = "https://files.pythonhosted.org/packages/45/8a/04479398c755a066ace10e3d158866beb600867cacae194c50ffa783abd0/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:824e6d3503ab990d7090768e4dfd9e840837bae057f212ff9f4f05ec6d1975e7", size = 397089, upload-time = "2025-07-01T15:54:02.319Z" }, - { url = "https://files.pythonhosted.org/packages/72/88/9203f47268db488a1b6d469d69c12201ede776bb728b9d9f29dbfd7df406/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ad7fd2258228bf288f2331f0a6148ad0186b2e3643055ed0db30990e59817a6", size = 514255, upload-time = "2025-07-01T15:54:03.38Z" }, - { url = "https://files.pythonhosted.org/packages/f5/b4/01ce5d1e853ddf81fbbd4311ab1eff0b3cf162d559288d10fd127e2588b5/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0dc23bbb3e06ec1ea72d515fb572c1fea59695aefbffb106501138762e1e915e", size = 402283, upload-time = "2025-07-01T15:54:04.923Z" }, - { url = "https://files.pythonhosted.org/packages/34/a2/004c99936997bfc644d590a9defd9e9c93f8286568f9c16cdaf3e14429a7/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d80bf832ac7b1920ee29a426cdca335f96a2b5caa839811803e999b41ba9030d", size = 383881, upload-time = "2025-07-01T15:54:06.482Z" }, - { url = "https://files.pythonhosted.org/packages/05/1b/ef5fba4a8f81ce04c427bfd96223f92f05e6cd72291ce9d7523db3b03a6c/rpds_py-0.26.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0919f38f5542c0a87e7b4afcafab6fd2c15386632d249e9a087498571250abe3", size = 415822, upload-time = "2025-07-01T15:54:07.605Z" }, - { url = "https://files.pythonhosted.org/packages/16/80/5c54195aec456b292f7bd8aa61741c8232964063fd8a75fdde9c1e982328/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d422b945683e409000c888e384546dbab9009bb92f7c0b456e217988cf316107", size = 558347, upload-time = "2025-07-01T15:54:08.591Z" }, - { url = "https://files.pythonhosted.org/packages/f2/1c/1845c1b1fd6d827187c43afe1841d91678d7241cbdb5420a4c6de180a538/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a7711fa562ba2da1aa757e11024ad6d93bad6ad7ede5afb9af144623e5f76a", size = 587956, upload-time = "2025-07-01T15:54:09.963Z" }, - { url = "https://files.pythonhosted.org/packages/2e/ff/9e979329dd131aa73a438c077252ddabd7df6d1a7ad7b9aacf6261f10faa/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238e8c8610cb7c29460e37184f6799547f7e09e6a9bdbdab4e8edb90986a2318", size = 554363, upload-time = "2025-07-01T15:54:11.073Z" }, - { url = "https://files.pythonhosted.org/packages/00/8b/d78cfe034b71ffbe72873a136e71acc7a831a03e37771cfe59f33f6de8a2/rpds_py-0.26.0-cp311-cp311-win32.whl", hash = "sha256:893b022bfbdf26d7bedb083efeea624e8550ca6eb98bf7fea30211ce95b9201a", size = 220123, upload-time = "2025-07-01T15:54:12.382Z" }, - { url = "https://files.pythonhosted.org/packages/94/c1/3c8c94c7dd3905dbfde768381ce98778500a80db9924731d87ddcdb117e9/rpds_py-0.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:87a5531de9f71aceb8af041d72fc4cab4943648d91875ed56d2e629bef6d4c03", size = 231732, upload-time = "2025-07-01T15:54:13.434Z" }, - { url = "https://files.pythonhosted.org/packages/67/93/e936fbed1b734eabf36ccb5d93c6a2e9246fbb13c1da011624b7286fae3e/rpds_py-0.26.0-cp311-cp311-win_arm64.whl", hash = "sha256:de2713f48c1ad57f89ac25b3cb7daed2156d8e822cf0eca9b96a6f990718cc41", size = 221917, upload-time = "2025-07-01T15:54:14.559Z" }, - { url = "https://files.pythonhosted.org/packages/ea/86/90eb87c6f87085868bd077c7a9938006eb1ce19ed4d06944a90d3560fce2/rpds_py-0.26.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:894514d47e012e794f1350f076c427d2347ebf82f9b958d554d12819849a369d", size = 363933, upload-time = "2025-07-01T15:54:15.734Z" }, - { url = "https://files.pythonhosted.org/packages/63/78/4469f24d34636242c924626082b9586f064ada0b5dbb1e9d096ee7a8e0c6/rpds_py-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc921b96fa95a097add244da36a1d9e4f3039160d1d30f1b35837bf108c21136", size = 350447, upload-time = "2025-07-01T15:54:16.922Z" }, - { url = "https://files.pythonhosted.org/packages/ad/91/c448ed45efdfdade82348d5e7995e15612754826ea640afc20915119734f/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e1157659470aa42a75448b6e943c895be8c70531c43cb78b9ba990778955582", size = 384711, upload-time = "2025-07-01T15:54:18.101Z" }, - { url = "https://files.pythonhosted.org/packages/ec/43/e5c86fef4be7f49828bdd4ecc8931f0287b1152c0bb0163049b3218740e7/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:521ccf56f45bb3a791182dc6b88ae5f8fa079dd705ee42138c76deb1238e554e", size = 400865, upload-time = "2025-07-01T15:54:19.295Z" }, - { url = "https://files.pythonhosted.org/packages/55/34/e00f726a4d44f22d5c5fe2e5ddd3ac3d7fd3f74a175607781fbdd06fe375/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9def736773fd56b305c0eef698be5192c77bfa30d55a0e5885f80126c4831a15", size = 517763, upload-time = "2025-07-01T15:54:20.858Z" }, - { url = "https://files.pythonhosted.org/packages/52/1c/52dc20c31b147af724b16104500fba13e60123ea0334beba7b40e33354b4/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdad4ea3b4513b475e027be79e5a0ceac8ee1c113a1a11e5edc3c30c29f964d8", size = 406651, upload-time = "2025-07-01T15:54:22.508Z" }, - { url = "https://files.pythonhosted.org/packages/2e/77/87d7bfabfc4e821caa35481a2ff6ae0b73e6a391bb6b343db2c91c2b9844/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82b165b07f416bdccf5c84546a484cc8f15137ca38325403864bfdf2b5b72f6a", size = 386079, upload-time = "2025-07-01T15:54:23.987Z" }, - { url = "https://files.pythonhosted.org/packages/e3/d4/7f2200c2d3ee145b65b3cddc4310d51f7da6a26634f3ac87125fd789152a/rpds_py-0.26.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d04cab0a54b9dba4d278fe955a1390da3cf71f57feb78ddc7cb67cbe0bd30323", size = 421379, upload-time = "2025-07-01T15:54:25.073Z" }, - { url = "https://files.pythonhosted.org/packages/ae/13/9fdd428b9c820869924ab62236b8688b122baa22d23efdd1c566938a39ba/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:79061ba1a11b6a12743a2b0f72a46aa2758613d454aa6ba4f5a265cc48850158", size = 562033, upload-time = "2025-07-01T15:54:26.225Z" }, - { url = "https://files.pythonhosted.org/packages/f3/e1/b69686c3bcbe775abac3a4c1c30a164a2076d28df7926041f6c0eb5e8d28/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f405c93675d8d4c5ac87364bb38d06c988e11028a64b52a47158a355079661f3", size = 591639, upload-time = "2025-07-01T15:54:27.424Z" }, - { url = "https://files.pythonhosted.org/packages/5c/c9/1e3d8c8863c84a90197ac577bbc3d796a92502124c27092413426f670990/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dafd4c44b74aa4bed4b250f1aed165b8ef5de743bcca3b88fc9619b6087093d2", size = 557105, upload-time = "2025-07-01T15:54:29.93Z" }, - { url = "https://files.pythonhosted.org/packages/9f/c5/90c569649057622959f6dcc40f7b516539608a414dfd54b8d77e3b201ac0/rpds_py-0.26.0-cp312-cp312-win32.whl", hash = "sha256:3da5852aad63fa0c6f836f3359647870e21ea96cf433eb393ffa45263a170d44", size = 223272, upload-time = "2025-07-01T15:54:31.128Z" }, - { url = "https://files.pythonhosted.org/packages/7d/16/19f5d9f2a556cfed454eebe4d354c38d51c20f3db69e7b4ce6cff904905d/rpds_py-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf47cfdabc2194a669dcf7a8dbba62e37a04c5041d2125fae0233b720da6f05c", size = 234995, upload-time = "2025-07-01T15:54:32.195Z" }, - { url = "https://files.pythonhosted.org/packages/83/f0/7935e40b529c0e752dfaa7880224771b51175fce08b41ab4a92eb2fbdc7f/rpds_py-0.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:20ab1ae4fa534f73647aad289003f1104092890849e0266271351922ed5574f8", size = 223198, upload-time = "2025-07-01T15:54:33.271Z" }, - { url = "https://files.pythonhosted.org/packages/6a/67/bb62d0109493b12b1c6ab00de7a5566aa84c0e44217c2d94bee1bd370da9/rpds_py-0.26.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:696764a5be111b036256c0b18cd29783fab22154690fc698062fc1b0084b511d", size = 363917, upload-time = "2025-07-01T15:54:34.755Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f3/34e6ae1925a5706c0f002a8d2d7f172373b855768149796af87bd65dcdb9/rpds_py-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e6c15d2080a63aaed876e228efe4f814bc7889c63b1e112ad46fdc8b368b9e1", size = 350073, upload-time = "2025-07-01T15:54:36.292Z" }, - { url = "https://files.pythonhosted.org/packages/75/83/1953a9d4f4e4de7fd0533733e041c28135f3c21485faaef56a8aadbd96b5/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390e3170babf42462739a93321e657444f0862c6d722a291accc46f9d21ed04e", size = 384214, upload-time = "2025-07-01T15:54:37.469Z" }, - { url = "https://files.pythonhosted.org/packages/48/0e/983ed1b792b3322ea1d065e67f4b230f3b96025f5ce3878cc40af09b7533/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7da84c2c74c0f5bc97d853d9e17bb83e2dcafcff0dc48286916001cc114379a1", size = 400113, upload-time = "2025-07-01T15:54:38.954Z" }, - { url = "https://files.pythonhosted.org/packages/69/7f/36c0925fff6f660a80be259c5b4f5e53a16851f946eb080351d057698528/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c5fe114a6dd480a510b6d3661d09d67d1622c4bf20660a474507aaee7eeeee9", size = 515189, upload-time = "2025-07-01T15:54:40.57Z" }, - { url = "https://files.pythonhosted.org/packages/13/45/cbf07fc03ba7a9b54662c9badb58294ecfb24f828b9732970bd1a431ed5c/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3100b3090269f3a7ea727b06a6080d4eb7439dca4c0e91a07c5d133bb1727ea7", size = 406998, upload-time = "2025-07-01T15:54:43.025Z" }, - { url = "https://files.pythonhosted.org/packages/6c/b0/8fa5e36e58657997873fd6a1cf621285ca822ca75b4b3434ead047daa307/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c03c9b0c64afd0320ae57de4c982801271c0c211aa2d37f3003ff5feb75bb04", size = 385903, upload-time = "2025-07-01T15:54:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f7/b25437772f9f57d7a9fbd73ed86d0dcd76b4c7c6998348c070d90f23e315/rpds_py-0.26.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5963b72ccd199ade6ee493723d18a3f21ba7d5b957017607f815788cef50eaf1", size = 419785, upload-time = "2025-07-01T15:54:46.043Z" }, - { url = "https://files.pythonhosted.org/packages/a7/6b/63ffa55743dfcb4baf2e9e77a0b11f7f97ed96a54558fcb5717a4b2cd732/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9da4e873860ad5bab3291438525cae80169daecbfafe5657f7f5fb4d6b3f96b9", size = 561329, upload-time = "2025-07-01T15:54:47.64Z" }, - { url = "https://files.pythonhosted.org/packages/2f/07/1f4f5e2886c480a2346b1e6759c00278b8a69e697ae952d82ae2e6ee5db0/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5afaddaa8e8c7f1f7b4c5c725c0070b6eed0228f705b90a1732a48e84350f4e9", size = 590875, upload-time = "2025-07-01T15:54:48.9Z" }, - { url = "https://files.pythonhosted.org/packages/cc/bc/e6639f1b91c3a55f8c41b47d73e6307051b6e246254a827ede730624c0f8/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4916dc96489616a6f9667e7526af8fa693c0fdb4f3acb0e5d9f4400eb06a47ba", size = 556636, upload-time = "2025-07-01T15:54:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/05/4c/b3917c45566f9f9a209d38d9b54a1833f2bb1032a3e04c66f75726f28876/rpds_py-0.26.0-cp313-cp313-win32.whl", hash = "sha256:2a343f91b17097c546b93f7999976fd6c9d5900617aa848c81d794e062ab302b", size = 222663, upload-time = "2025-07-01T15:54:52.023Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0b/0851bdd6025775aaa2365bb8de0697ee2558184c800bfef8d7aef5ccde58/rpds_py-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:0a0b60701f2300c81b2ac88a5fb893ccfa408e1c4a555a77f908a2596eb875a5", size = 234428, upload-time = "2025-07-01T15:54:53.692Z" }, - { url = "https://files.pythonhosted.org/packages/ed/e8/a47c64ed53149c75fb581e14a237b7b7cd18217e969c30d474d335105622/rpds_py-0.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:257d011919f133a4746958257f2c75238e3ff54255acd5e3e11f3ff41fd14256", size = 222571, upload-time = "2025-07-01T15:54:54.822Z" }, - { url = "https://files.pythonhosted.org/packages/89/bf/3d970ba2e2bcd17d2912cb42874107390f72873e38e79267224110de5e61/rpds_py-0.26.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:529c8156d7506fba5740e05da8795688f87119cce330c244519cf706a4a3d618", size = 360475, upload-time = "2025-07-01T15:54:56.228Z" }, - { url = "https://files.pythonhosted.org/packages/82/9f/283e7e2979fc4ec2d8ecee506d5a3675fce5ed9b4b7cb387ea5d37c2f18d/rpds_py-0.26.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f53ec51f9d24e9638a40cabb95078ade8c99251945dad8d57bf4aabe86ecee35", size = 346692, upload-time = "2025-07-01T15:54:58.561Z" }, - { url = "https://files.pythonhosted.org/packages/e3/03/7e50423c04d78daf391da3cc4330bdb97042fc192a58b186f2d5deb7befd/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab504c4d654e4a29558eaa5bb8cea5fdc1703ea60a8099ffd9c758472cf913f", size = 379415, upload-time = "2025-07-01T15:54:59.751Z" }, - { url = "https://files.pythonhosted.org/packages/57/00/d11ee60d4d3b16808432417951c63df803afb0e0fc672b5e8d07e9edaaae/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd0641abca296bc1a00183fe44f7fced8807ed49d501f188faa642d0e4975b83", size = 391783, upload-time = "2025-07-01T15:55:00.898Z" }, - { url = "https://files.pythonhosted.org/packages/08/b3/1069c394d9c0d6d23c5b522e1f6546b65793a22950f6e0210adcc6f97c3e/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b312fecc1d017b5327afa81d4da1480f51c68810963a7336d92203dbb3d4f1", size = 512844, upload-time = "2025-07-01T15:55:02.201Z" }, - { url = "https://files.pythonhosted.org/packages/08/3b/c4fbf0926800ed70b2c245ceca99c49f066456755f5d6eb8863c2c51e6d0/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c741107203954f6fc34d3066d213d0a0c40f7bb5aafd698fb39888af277c70d8", size = 402105, upload-time = "2025-07-01T15:55:03.698Z" }, - { url = "https://files.pythonhosted.org/packages/1c/b0/db69b52ca07413e568dae9dc674627a22297abb144c4d6022c6d78f1e5cc/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3e55a7db08dc9a6ed5fb7103019d2c1a38a349ac41901f9f66d7f95750942f", size = 383440, upload-time = "2025-07-01T15:55:05.398Z" }, - { url = "https://files.pythonhosted.org/packages/4c/e1/c65255ad5b63903e56b3bb3ff9dcc3f4f5c3badde5d08c741ee03903e951/rpds_py-0.26.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e851920caab2dbcae311fd28f4313c6953993893eb5c1bb367ec69d9a39e7ed", size = 412759, upload-time = "2025-07-01T15:55:08.316Z" }, - { url = "https://files.pythonhosted.org/packages/e4/22/bb731077872377a93c6e93b8a9487d0406c70208985831034ccdeed39c8e/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dfbf280da5f876d0b00c81f26bedce274e72a678c28845453885a9b3c22ae632", size = 556032, upload-time = "2025-07-01T15:55:09.52Z" }, - { url = "https://files.pythonhosted.org/packages/e0/8b/393322ce7bac5c4530fb96fc79cc9ea2f83e968ff5f6e873f905c493e1c4/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1cc81d14ddfa53d7f3906694d35d54d9d3f850ef8e4e99ee68bc0d1e5fed9a9c", size = 585416, upload-time = "2025-07-01T15:55:11.216Z" }, - { url = "https://files.pythonhosted.org/packages/49/ae/769dc372211835bf759319a7aae70525c6eb523e3371842c65b7ef41c9c6/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dca83c498b4650a91efcf7b88d669b170256bf8017a5db6f3e06c2bf031f57e0", size = 554049, upload-time = "2025-07-01T15:55:13.004Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f9/4c43f9cc203d6ba44ce3146246cdc38619d92c7bd7bad4946a3491bd5b70/rpds_py-0.26.0-cp313-cp313t-win32.whl", hash = "sha256:4d11382bcaf12f80b51d790dee295c56a159633a8e81e6323b16e55d81ae37e9", size = 218428, upload-time = "2025-07-01T15:55:14.486Z" }, - { url = "https://files.pythonhosted.org/packages/7e/8b/9286b7e822036a4a977f2f1e851c7345c20528dbd56b687bb67ed68a8ede/rpds_py-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff110acded3c22c033e637dd8896e411c7d3a11289b2edf041f86663dbc791e9", size = 231524, upload-time = "2025-07-01T15:55:15.745Z" }, - { url = "https://files.pythonhosted.org/packages/55/07/029b7c45db910c74e182de626dfdae0ad489a949d84a468465cd0ca36355/rpds_py-0.26.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:da619979df60a940cd434084355c514c25cf8eb4cf9a508510682f6c851a4f7a", size = 364292, upload-time = "2025-07-01T15:55:17.001Z" }, - { url = "https://files.pythonhosted.org/packages/13/d1/9b3d3f986216b4d1f584878dca15ce4797aaf5d372d738974ba737bf68d6/rpds_py-0.26.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ea89a2458a1a75f87caabefe789c87539ea4e43b40f18cff526052e35bbb4fdf", size = 350334, upload-time = "2025-07-01T15:55:18.922Z" }, - { url = "https://files.pythonhosted.org/packages/18/98/16d5e7bc9ec715fa9668731d0cf97f6b032724e61696e2db3d47aeb89214/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feac1045b3327a45944e7dcbeb57530339f6b17baff154df51ef8b0da34c8c12", size = 384875, upload-time = "2025-07-01T15:55:20.399Z" }, - { url = "https://files.pythonhosted.org/packages/f9/13/aa5e2b1ec5ab0e86a5c464d53514c0467bec6ba2507027d35fc81818358e/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b818a592bd69bfe437ee8368603d4a2d928c34cffcdf77c2e761a759ffd17d20", size = 399993, upload-time = "2025-07-01T15:55:21.729Z" }, - { url = "https://files.pythonhosted.org/packages/17/03/8021810b0e97923abdbab6474c8b77c69bcb4b2c58330777df9ff69dc559/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a8b0dd8648709b62d9372fc00a57466f5fdeefed666afe3fea5a6c9539a0331", size = 516683, upload-time = "2025-07-01T15:55:22.918Z" }, - { url = "https://files.pythonhosted.org/packages/dc/b1/da8e61c87c2f3d836954239fdbbfb477bb7b54d74974d8f6fcb34342d166/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d3498ad0df07d81112aa6ec6c95a7e7b1ae00929fb73e7ebee0f3faaeabad2f", size = 408825, upload-time = "2025-07-01T15:55:24.207Z" }, - { url = "https://files.pythonhosted.org/packages/38/bc/1fc173edaaa0e52c94b02a655db20697cb5fa954ad5a8e15a2c784c5cbdd/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24a4146ccb15be237fdef10f331c568e1b0e505f8c8c9ed5d67759dac58ac246", size = 387292, upload-time = "2025-07-01T15:55:25.554Z" }, - { url = "https://files.pythonhosted.org/packages/7c/eb/3a9bb4bd90867d21916f253caf4f0d0be7098671b6715ad1cead9fe7bab9/rpds_py-0.26.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a9a63785467b2d73635957d32a4f6e73d5e4df497a16a6392fa066b753e87387", size = 420435, upload-time = "2025-07-01T15:55:27.798Z" }, - { url = "https://files.pythonhosted.org/packages/cd/16/e066dcdb56f5632713445271a3f8d3d0b426d51ae9c0cca387799df58b02/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:de4ed93a8c91debfd5a047be327b7cc8b0cc6afe32a716bbbc4aedca9e2a83af", size = 562410, upload-time = "2025-07-01T15:55:29.057Z" }, - { url = "https://files.pythonhosted.org/packages/60/22/ddbdec7eb82a0dc2e455be44c97c71c232983e21349836ce9f272e8a3c29/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:caf51943715b12af827696ec395bfa68f090a4c1a1d2509eb4e2cb69abbbdb33", size = 590724, upload-time = "2025-07-01T15:55:30.719Z" }, - { url = "https://files.pythonhosted.org/packages/2c/b4/95744085e65b7187d83f2fcb0bef70716a1ea0a9e5d8f7f39a86e5d83424/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4a59e5bc386de021f56337f757301b337d7ab58baa40174fb150accd480bc953", size = 558285, upload-time = "2025-07-01T15:55:31.981Z" }, - { url = "https://files.pythonhosted.org/packages/37/37/6309a75e464d1da2559446f9c811aa4d16343cebe3dbb73701e63f760caa/rpds_py-0.26.0-cp314-cp314-win32.whl", hash = "sha256:92c8db839367ef16a662478f0a2fe13e15f2227da3c1430a782ad0f6ee009ec9", size = 223459, upload-time = "2025-07-01T15:55:33.312Z" }, - { url = "https://files.pythonhosted.org/packages/d9/6f/8e9c11214c46098b1d1391b7e02b70bb689ab963db3b19540cba17315291/rpds_py-0.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:b0afb8cdd034150d4d9f53926226ed27ad15b7f465e93d7468caaf5eafae0d37", size = 236083, upload-time = "2025-07-01T15:55:34.933Z" }, - { url = "https://files.pythonhosted.org/packages/47/af/9c4638994dd623d51c39892edd9d08e8be8220a4b7e874fa02c2d6e91955/rpds_py-0.26.0-cp314-cp314-win_arm64.whl", hash = "sha256:ca3f059f4ba485d90c8dc75cb5ca897e15325e4e609812ce57f896607c1c0867", size = 223291, upload-time = "2025-07-01T15:55:36.202Z" }, - { url = "https://files.pythonhosted.org/packages/4d/db/669a241144460474aab03e254326b32c42def83eb23458a10d163cb9b5ce/rpds_py-0.26.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:5afea17ab3a126006dc2f293b14ffc7ef3c85336cf451564a0515ed7648033da", size = 361445, upload-time = "2025-07-01T15:55:37.483Z" }, - { url = "https://files.pythonhosted.org/packages/3b/2d/133f61cc5807c6c2fd086a46df0eb8f63a23f5df8306ff9f6d0fd168fecc/rpds_py-0.26.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:69f0c0a3df7fd3a7eec50a00396104bb9a843ea6d45fcc31c2d5243446ffd7a7", size = 347206, upload-time = "2025-07-01T15:55:38.828Z" }, - { url = "https://files.pythonhosted.org/packages/05/bf/0e8fb4c05f70273469eecf82f6ccf37248558526a45321644826555db31b/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:801a71f70f9813e82d2513c9a96532551fce1e278ec0c64610992c49c04c2dad", size = 380330, upload-time = "2025-07-01T15:55:40.175Z" }, - { url = "https://files.pythonhosted.org/packages/d4/a8/060d24185d8b24d3923322f8d0ede16df4ade226a74e747b8c7c978e3dd3/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df52098cde6d5e02fa75c1f6244f07971773adb4a26625edd5c18fee906fa84d", size = 392254, upload-time = "2025-07-01T15:55:42.015Z" }, - { url = "https://files.pythonhosted.org/packages/b9/7b/7c2e8a9ee3e6bc0bae26bf29f5219955ca2fbb761dca996a83f5d2f773fe/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bc596b30f86dc6f0929499c9e574601679d0341a0108c25b9b358a042f51bca", size = 516094, upload-time = "2025-07-01T15:55:43.603Z" }, - { url = "https://files.pythonhosted.org/packages/75/d6/f61cafbed8ba1499b9af9f1777a2a199cd888f74a96133d8833ce5eaa9c5/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9dfbe56b299cf5875b68eb6f0ebaadc9cac520a1989cac0db0765abfb3709c19", size = 402889, upload-time = "2025-07-01T15:55:45.275Z" }, - { url = "https://files.pythonhosted.org/packages/92/19/c8ac0a8a8df2dd30cdec27f69298a5c13e9029500d6d76718130f5e5be10/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac64f4b2bdb4ea622175c9ab7cf09444e412e22c0e02e906978b3b488af5fde8", size = 384301, upload-time = "2025-07-01T15:55:47.098Z" }, - { url = "https://files.pythonhosted.org/packages/41/e1/6b1859898bc292a9ce5776016c7312b672da00e25cec74d7beced1027286/rpds_py-0.26.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:181ef9b6bbf9845a264f9aa45c31836e9f3c1f13be565d0d010e964c661d1e2b", size = 412891, upload-time = "2025-07-01T15:55:48.412Z" }, - { url = "https://files.pythonhosted.org/packages/ef/b9/ceb39af29913c07966a61367b3c08b4f71fad841e32c6b59a129d5974698/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:49028aa684c144ea502a8e847d23aed5e4c2ef7cadfa7d5eaafcb40864844b7a", size = 557044, upload-time = "2025-07-01T15:55:49.816Z" }, - { url = "https://files.pythonhosted.org/packages/2f/27/35637b98380731a521f8ec4f3fd94e477964f04f6b2f8f7af8a2d889a4af/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e5d524d68a474a9688336045bbf76cb0def88549c1b2ad9dbfec1fb7cfbe9170", size = 585774, upload-time = "2025-07-01T15:55:51.192Z" }, - { url = "https://files.pythonhosted.org/packages/52/d9/3f0f105420fecd18551b678c9a6ce60bd23986098b252a56d35781b3e7e9/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c1851f429b822831bd2edcbe0cfd12ee9ea77868f8d3daf267b189371671c80e", size = 554886, upload-time = "2025-07-01T15:55:52.541Z" }, - { url = "https://files.pythonhosted.org/packages/6b/c5/347c056a90dc8dd9bc240a08c527315008e1b5042e7a4cf4ac027be9d38a/rpds_py-0.26.0-cp314-cp314t-win32.whl", hash = "sha256:7bdb17009696214c3b66bb3590c6d62e14ac5935e53e929bcdbc5a495987a84f", size = 219027, upload-time = "2025-07-01T15:55:53.874Z" }, - { url = "https://files.pythonhosted.org/packages/75/04/5302cea1aa26d886d34cadbf2dc77d90d7737e576c0065f357b96dc7a1a6/rpds_py-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f14440b9573a6f76b4ee4770c13f0b5921f71dde3b6fcb8dabbefd13b7fe05d7", size = 232821, upload-time = "2025-07-01T15:55:55.167Z" }, - { url = "https://files.pythonhosted.org/packages/ef/9a/1f033b0b31253d03d785b0cd905bc127e555ab496ea6b4c7c2e1f951f2fd/rpds_py-0.26.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3c0909c5234543ada2515c05dc08595b08d621ba919629e94427e8e03539c958", size = 373226, upload-time = "2025-07-01T15:56:16.578Z" }, - { url = "https://files.pythonhosted.org/packages/58/29/5f88023fd6aaaa8ca3c4a6357ebb23f6f07da6079093ccf27c99efce87db/rpds_py-0.26.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c1fb0cda2abcc0ac62f64e2ea4b4e64c57dfd6b885e693095460c61bde7bb18e", size = 359230, upload-time = "2025-07-01T15:56:17.978Z" }, - { url = "https://files.pythonhosted.org/packages/6c/6c/13eaebd28b439da6964dde22712b52e53fe2824af0223b8e403249d10405/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84d142d2d6cf9b31c12aa4878d82ed3b2324226270b89b676ac62ccd7df52d08", size = 382363, upload-time = "2025-07-01T15:56:19.977Z" }, - { url = "https://files.pythonhosted.org/packages/55/fc/3bb9c486b06da19448646f96147796de23c5811ef77cbfc26f17307b6a9d/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a547e21c5610b7e9093d870be50682a6a6cf180d6da0f42c47c306073bfdbbf6", size = 397146, upload-time = "2025-07-01T15:56:21.39Z" }, - { url = "https://files.pythonhosted.org/packages/15/18/9d1b79eb4d18e64ba8bba9e7dec6f9d6920b639f22f07ee9368ca35d4673/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35e9a70a0f335371275cdcd08bc5b8051ac494dd58bff3bbfb421038220dc871", size = 514804, upload-time = "2025-07-01T15:56:22.78Z" }, - { url = "https://files.pythonhosted.org/packages/4f/5a/175ad7191bdbcd28785204621b225ad70e85cdfd1e09cc414cb554633b21/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0dfa6115c6def37905344d56fb54c03afc49104e2ca473d5dedec0f6606913b4", size = 402820, upload-time = "2025-07-01T15:56:24.584Z" }, - { url = "https://files.pythonhosted.org/packages/11/45/6a67ecf6d61c4d4aff4bc056e864eec4b2447787e11d1c2c9a0242c6e92a/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:313cfcd6af1a55a286a3c9a25f64af6d0e46cf60bc5798f1db152d97a216ff6f", size = 384567, upload-time = "2025-07-01T15:56:26.064Z" }, - { url = "https://files.pythonhosted.org/packages/a1/ba/16589da828732b46454c61858950a78fe4c931ea4bf95f17432ffe64b241/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f7bf2496fa563c046d05e4d232d7b7fd61346e2402052064b773e5c378bf6f73", size = 416520, upload-time = "2025-07-01T15:56:27.608Z" }, - { url = "https://files.pythonhosted.org/packages/81/4b/00092999fc7c0c266045e984d56b7314734cc400a6c6dc4d61a35f135a9d/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:aa81873e2c8c5aa616ab8e017a481a96742fdf9313c40f14338ca7dbf50cb55f", size = 559362, upload-time = "2025-07-01T15:56:29.078Z" }, - { url = "https://files.pythonhosted.org/packages/96/0c/43737053cde1f93ac4945157f7be1428724ab943e2132a0d235a7e161d4e/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:68ffcf982715f5b5b7686bdd349ff75d422e8f22551000c24b30eaa1b7f7ae84", size = 588113, upload-time = "2025-07-01T15:56:30.485Z" }, - { url = "https://files.pythonhosted.org/packages/46/46/8e38f6161466e60a997ed7e9951ae5de131dedc3cf778ad35994b4af823d/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6188de70e190847bb6db3dc3981cbadff87d27d6fe9b4f0e18726d55795cee9b", size = 555429, upload-time = "2025-07-01T15:56:31.956Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ac/65da605e9f1dd643ebe615d5bbd11b6efa1d69644fc4bf623ea5ae385a82/rpds_py-0.26.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1c962145c7473723df9722ba4c058de12eb5ebedcb4e27e7d902920aa3831ee8", size = 231950, upload-time = "2025-07-01T15:56:33.337Z" }, - { url = "https://files.pythonhosted.org/packages/51/f2/b5c85b758a00c513bb0389f8fc8e61eb5423050c91c958cdd21843faa3e6/rpds_py-0.26.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f61a9326f80ca59214d1cceb0a09bb2ece5b2563d4e0cd37bfd5515c28510674", size = 373505, upload-time = "2025-07-01T15:56:34.716Z" }, - { url = "https://files.pythonhosted.org/packages/23/e0/25db45e391251118e915e541995bb5f5ac5691a3b98fb233020ba53afc9b/rpds_py-0.26.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:183f857a53bcf4b1b42ef0f57ca553ab56bdd170e49d8091e96c51c3d69ca696", size = 359468, upload-time = "2025-07-01T15:56:36.219Z" }, - { url = "https://files.pythonhosted.org/packages/0b/73/dd5ee6075bb6491be3a646b301dfd814f9486d924137a5098e61f0487e16/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:941c1cfdf4799d623cf3aa1d326a6b4fdb7a5799ee2687f3516738216d2262fb", size = 382680, upload-time = "2025-07-01T15:56:37.644Z" }, - { url = "https://files.pythonhosted.org/packages/2f/10/84b522ff58763a5c443f5bcedc1820240e454ce4e620e88520f04589e2ea/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72a8d9564a717ee291f554eeb4bfeafe2309d5ec0aa6c475170bdab0f9ee8e88", size = 397035, upload-time = "2025-07-01T15:56:39.241Z" }, - { url = "https://files.pythonhosted.org/packages/06/ea/8667604229a10a520fcbf78b30ccc278977dcc0627beb7ea2c96b3becef0/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:511d15193cbe013619dd05414c35a7dedf2088fcee93c6bbb7c77859765bd4e8", size = 514922, upload-time = "2025-07-01T15:56:40.645Z" }, - { url = "https://files.pythonhosted.org/packages/24/e6/9ed5b625c0661c4882fc8cdf302bf8e96c73c40de99c31e0b95ed37d508c/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aea1f9741b603a8d8fedb0ed5502c2bc0accbc51f43e2ad1337fe7259c2b77a5", size = 402822, upload-time = "2025-07-01T15:56:42.137Z" }, - { url = "https://files.pythonhosted.org/packages/8a/58/212c7b6fd51946047fb45d3733da27e2fa8f7384a13457c874186af691b1/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4019a9d473c708cf2f16415688ef0b4639e07abaa569d72f74745bbeffafa2c7", size = 384336, upload-time = "2025-07-01T15:56:44.239Z" }, - { url = "https://files.pythonhosted.org/packages/aa/f5/a40ba78748ae8ebf4934d4b88e77b98497378bc2c24ba55ebe87a4e87057/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:093d63b4b0f52d98ebae33b8c50900d3d67e0666094b1be7a12fffd7f65de74b", size = 416871, upload-time = "2025-07-01T15:56:46.284Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a6/33b1fc0c9f7dcfcfc4a4353daa6308b3ece22496ceece348b3e7a7559a09/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2abe21d8ba64cded53a2a677e149ceb76dcf44284202d737178afe7ba540c1eb", size = 559439, upload-time = "2025-07-01T15:56:48.549Z" }, - { url = "https://files.pythonhosted.org/packages/71/2d/ceb3f9c12f8cfa56d34995097f6cd99da1325642c60d1b6680dd9df03ed8/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:4feb7511c29f8442cbbc28149a92093d32e815a28aa2c50d333826ad2a20fdf0", size = 588380, upload-time = "2025-07-01T15:56:50.086Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ed/9de62c2150ca8e2e5858acf3f4f4d0d180a38feef9fdab4078bea63d8dba/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e99685fc95d386da368013e7fb4269dd39c30d99f812a8372d62f244f662709c", size = 555334, upload-time = "2025-07-01T15:56:51.703Z" }, -] - -[[package]] -name = "rsa" -version = "4.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyasn1" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, -] - -[[package]] -name = "ruff" -version = "0.12.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9b/ce/8d7dbedede481245b489b769d27e2934730791a9a82765cb94566c6e6abd/ruff-0.12.4.tar.gz", hash = "sha256:13efa16df6c6eeb7d0f091abae50f58e9522f3843edb40d56ad52a5a4a4b6873", size = 5131435, upload-time = "2025-07-17T17:27:19.138Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/9f/517bc5f61bad205b7f36684ffa5415c013862dee02f55f38a217bdbe7aa4/ruff-0.12.4-py3-none-linux_armv6l.whl", hash = "sha256:cb0d261dac457ab939aeb247e804125a5d521b21adf27e721895b0d3f83a0d0a", size = 10188824, upload-time = "2025-07-17T17:26:31.412Z" }, - { url = "https://files.pythonhosted.org/packages/28/83/691baae5a11fbbde91df01c565c650fd17b0eabed259e8b7563de17c6529/ruff-0.12.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:55c0f4ca9769408d9b9bac530c30d3e66490bd2beb2d3dae3e4128a1f05c7442", size = 10884521, upload-time = "2025-07-17T17:26:35.084Z" }, - { url = "https://files.pythonhosted.org/packages/d6/8d/756d780ff4076e6dd035d058fa220345f8c458391f7edfb1c10731eedc75/ruff-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a8224cc3722c9ad9044da7f89c4c1ec452aef2cfe3904365025dd2f51daeae0e", size = 10277653, upload-time = "2025-07-17T17:26:37.897Z" }, - { url = "https://files.pythonhosted.org/packages/8d/97/8eeee0f48ece153206dce730fc9e0e0ca54fd7f261bb3d99c0a4343a1892/ruff-0.12.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9949d01d64fa3672449a51ddb5d7548b33e130240ad418884ee6efa7a229586", size = 10485993, upload-time = "2025-07-17T17:26:40.68Z" }, - { url = "https://files.pythonhosted.org/packages/49/b8/22a43d23a1f68df9b88f952616c8508ea6ce4ed4f15353b8168c48b2d7e7/ruff-0.12.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:be0593c69df9ad1465e8a2d10e3defd111fdb62dcd5be23ae2c06da77e8fcffb", size = 10022824, upload-time = "2025-07-17T17:26:43.564Z" }, - { url = "https://files.pythonhosted.org/packages/cd/70/37c234c220366993e8cffcbd6cadbf332bfc848cbd6f45b02bade17e0149/ruff-0.12.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7dea966bcb55d4ecc4cc3270bccb6f87a337326c9dcd3c07d5b97000dbff41c", size = 11524414, upload-time = "2025-07-17T17:26:46.219Z" }, - { url = "https://files.pythonhosted.org/packages/14/77/c30f9964f481b5e0e29dd6a1fae1f769ac3fd468eb76fdd5661936edd262/ruff-0.12.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afcfa3ab5ab5dd0e1c39bf286d829e042a15e966b3726eea79528e2e24d8371a", size = 12419216, upload-time = "2025-07-17T17:26:48.883Z" }, - { url = "https://files.pythonhosted.org/packages/6e/79/af7fe0a4202dce4ef62c5e33fecbed07f0178f5b4dd9c0d2fcff5ab4a47c/ruff-0.12.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c057ce464b1413c926cdb203a0f858cd52f3e73dcb3270a3318d1630f6395bb3", size = 11976756, upload-time = "2025-07-17T17:26:51.754Z" }, - { url = "https://files.pythonhosted.org/packages/09/d1/33fb1fc00e20a939c305dbe2f80df7c28ba9193f7a85470b982815a2dc6a/ruff-0.12.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64b90d1122dc2713330350626b10d60818930819623abbb56535c6466cce045", size = 11020019, upload-time = "2025-07-17T17:26:54.265Z" }, - { url = "https://files.pythonhosted.org/packages/64/f4/e3cd7f7bda646526f09693e2e02bd83d85fff8a8222c52cf9681c0d30843/ruff-0.12.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2abc48f3d9667fdc74022380b5c745873499ff827393a636f7a59da1515e7c57", size = 11277890, upload-time = "2025-07-17T17:26:56.914Z" }, - { url = "https://files.pythonhosted.org/packages/5e/d0/69a85fb8b94501ff1a4f95b7591505e8983f38823da6941eb5b6badb1e3a/ruff-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2b2449dc0c138d877d629bea151bee8c0ae3b8e9c43f5fcaafcd0c0d0726b184", size = 10348539, upload-time = "2025-07-17T17:26:59.381Z" }, - { url = "https://files.pythonhosted.org/packages/16/a0/91372d1cb1678f7d42d4893b88c252b01ff1dffcad09ae0c51aa2542275f/ruff-0.12.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:56e45bb11f625db55f9b70477062e6a1a04d53628eda7784dce6e0f55fd549eb", size = 10009579, upload-time = "2025-07-17T17:27:02.462Z" }, - { url = "https://files.pythonhosted.org/packages/23/1b/c4a833e3114d2cc0f677e58f1df6c3b20f62328dbfa710b87a1636a5e8eb/ruff-0.12.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:478fccdb82ca148a98a9ff43658944f7ab5ec41c3c49d77cd99d44da019371a1", size = 10942982, upload-time = "2025-07-17T17:27:05.343Z" }, - { url = "https://files.pythonhosted.org/packages/ff/ce/ce85e445cf0a5dd8842f2f0c6f0018eedb164a92bdf3eda51984ffd4d989/ruff-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0fc426bec2e4e5f4c4f182b9d2ce6a75c85ba9bcdbe5c6f2a74fcb8df437df4b", size = 11343331, upload-time = "2025-07-17T17:27:08.652Z" }, - { url = "https://files.pythonhosted.org/packages/35/cf/441b7fc58368455233cfb5b77206c849b6dfb48b23de532adcc2e50ccc06/ruff-0.12.4-py3-none-win32.whl", hash = "sha256:4de27977827893cdfb1211d42d84bc180fceb7b72471104671c59be37041cf93", size = 10267904, upload-time = "2025-07-17T17:27:11.814Z" }, - { url = "https://files.pythonhosted.org/packages/ce/7e/20af4a0df5e1299e7368d5ea4350412226afb03d95507faae94c80f00afd/ruff-0.12.4-py3-none-win_amd64.whl", hash = "sha256:fe0b9e9eb23736b453143d72d2ceca5db323963330d5b7859d60d101147d461a", size = 11209038, upload-time = "2025-07-17T17:27:14.417Z" }, - { url = "https://files.pythonhosted.org/packages/11/02/8857d0dfb8f44ef299a5dfd898f673edefb71e3b533b3b9d2db4c832dd13/ruff-0.12.4-py3-none-win_arm64.whl", hash = "sha256:0618ec4442a83ab545e5b71202a5c0ed7791e8471435b94e655b570a5031a98e", size = 10469336, upload-time = "2025-07-17T17:27:16.913Z" }, -] - -[[package]] -name = "safetensors" -version = "0.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/71/7e/2d5d6ee7b40c0682315367ec7475693d110f512922d582fef1bd4a63adc3/safetensors-0.5.3.tar.gz", hash = "sha256:b6b0d6ecacec39a4fdd99cc19f4576f5219ce858e6fd8dbe7609df0b8dc56965", size = 67210, upload-time = "2025-02-26T09:15:13.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/ae/88f6c49dbd0cc4da0e08610019a3c78a7d390879a919411a410a1876d03a/safetensors-0.5.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd20eb133db8ed15b40110b7c00c6df51655a2998132193de2f75f72d99c7073", size = 436917, upload-time = "2025-02-26T09:15:03.702Z" }, - { url = "https://files.pythonhosted.org/packages/b8/3b/11f1b4a2f5d2ab7da34ecc062b0bc301f2be024d110a6466726bec8c055c/safetensors-0.5.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:21d01c14ff6c415c485616b8b0bf961c46b3b343ca59110d38d744e577f9cce7", size = 418419, upload-time = "2025-02-26T09:15:01.765Z" }, - { url = "https://files.pythonhosted.org/packages/5d/9a/add3e6fef267658075c5a41573c26d42d80c935cdc992384dfae435feaef/safetensors-0.5.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11bce6164887cd491ca75c2326a113ba934be596e22b28b1742ce27b1d076467", size = 459493, upload-time = "2025-02-26T09:14:51.812Z" }, - { url = "https://files.pythonhosted.org/packages/df/5c/bf2cae92222513cc23b3ff85c4a1bb2811a2c3583ac0f8e8d502751de934/safetensors-0.5.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a243be3590bc3301c821da7a18d87224ef35cbd3e5f5727e4e0728b8172411e", size = 472400, upload-time = "2025-02-26T09:14:53.549Z" }, - { url = "https://files.pythonhosted.org/packages/58/11/7456afb740bd45782d0f4c8e8e1bb9e572f1bf82899fb6ace58af47b4282/safetensors-0.5.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bd84b12b1670a6f8e50f01e28156422a2bc07fb16fc4e98bded13039d688a0d", size = 522891, upload-time = "2025-02-26T09:14:55.717Z" }, - { url = "https://files.pythonhosted.org/packages/57/3d/fe73a9d2ace487e7285f6e157afee2383bd1ddb911b7cb44a55cf812eae3/safetensors-0.5.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:391ac8cab7c829452175f871fcaf414aa1e292b5448bd02620f675a7f3e7abb9", size = 537694, upload-time = "2025-02-26T09:14:57.036Z" }, - { url = "https://files.pythonhosted.org/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cead1fa41fc54b1e61089fa57452e8834f798cb1dc7a09ba3524f1eb08e0317a", size = 471642, upload-time = "2025-02-26T09:15:00.544Z" }, - { url = "https://files.pythonhosted.org/packages/ce/20/1fbe16f9b815f6c5a672f5b760951e20e17e43f67f231428f871909a37f6/safetensors-0.5.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1077f3e94182d72618357b04b5ced540ceb71c8a813d3319f1aba448e68a770d", size = 502241, upload-time = "2025-02-26T09:14:58.303Z" }, - { url = "https://files.pythonhosted.org/packages/5f/18/8e108846b506487aa4629fe4116b27db65c3dde922de2c8e0cc1133f3f29/safetensors-0.5.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:799021e78287bac619c7b3f3606730a22da4cda27759ddf55d37c8db7511c74b", size = 638001, upload-time = "2025-02-26T09:15:05.79Z" }, - { url = "https://files.pythonhosted.org/packages/82/5a/c116111d8291af6c8c8a8b40628fe833b9db97d8141c2a82359d14d9e078/safetensors-0.5.3-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df26da01aaac504334644e1b7642fa000bfec820e7cef83aeac4e355e03195ff", size = 734013, upload-time = "2025-02-26T09:15:07.892Z" }, - { url = "https://files.pythonhosted.org/packages/7d/ff/41fcc4d3b7de837963622e8610d998710705bbde9a8a17221d85e5d0baad/safetensors-0.5.3-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:32c3ef2d7af8b9f52ff685ed0bc43913cdcde135089ae322ee576de93eae5135", size = 670687, upload-time = "2025-02-26T09:15:09.979Z" }, - { url = "https://files.pythonhosted.org/packages/40/ad/2b113098e69c985a3d8fbda4b902778eae4a35b7d5188859b4a63d30c161/safetensors-0.5.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:37f1521be045e56fc2b54c606d4455573e717b2d887c579ee1dbba5f868ece04", size = 643147, upload-time = "2025-02-26T09:15:11.185Z" }, - { url = "https://files.pythonhosted.org/packages/0a/0c/95aeb51d4246bd9a3242d3d8349c1112b4ee7611a4b40f0c5c93b05f001d/safetensors-0.5.3-cp38-abi3-win32.whl", hash = "sha256:cfc0ec0846dcf6763b0ed3d1846ff36008c6e7290683b61616c4b040f6a54ace", size = 296677, upload-time = "2025-02-26T09:15:16.554Z" }, - { url = "https://files.pythonhosted.org/packages/69/e2/b011c38e5394c4c18fb5500778a55ec43ad6106126e74723ffaee246f56e/safetensors-0.5.3-cp38-abi3-win_amd64.whl", hash = "sha256:836cbbc320b47e80acd40e44c8682db0e8ad7123209f69b093def21ec7cafd11", size = 308878, upload-time = "2025-02-26T09:15:14.99Z" }, -] - -[[package]] -name = "scikit-learn" -version = "1.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "joblib" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "threadpoolctl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/84/5f4af978fff619706b8961accac84780a6d298d82a8873446f72edb4ead0/scikit_learn-1.7.1.tar.gz", hash = "sha256:24b3f1e976a4665aa74ee0fcaac2b8fccc6ae77c8e07ab25da3ba6d3292b9802", size = 7190445, upload-time = "2025-07-18T08:01:54.5Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/88/0dd5be14ef19f2d80a77780be35a33aa94e8a3b3223d80bee8892a7832b4/scikit_learn-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:406204dd4004f0517f0b23cf4b28c6245cbd51ab1b6b78153bc784def214946d", size = 9338868, upload-time = "2025-07-18T08:01:00.25Z" }, - { url = "https://files.pythonhosted.org/packages/fd/52/3056b6adb1ac58a0bc335fc2ed2fcf599974d908855e8cb0ca55f797593c/scikit_learn-1.7.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:16af2e44164f05d04337fd1fc3ae7c4ea61fd9b0d527e22665346336920fe0e1", size = 8655943, upload-time = "2025-07-18T08:01:02.974Z" }, - { url = "https://files.pythonhosted.org/packages/fb/a4/e488acdece6d413f370a9589a7193dac79cd486b2e418d3276d6ea0b9305/scikit_learn-1.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2f2e78e56a40c7587dea9a28dc4a49500fa2ead366869418c66f0fd75b80885c", size = 9652056, upload-time = "2025-07-18T08:01:04.978Z" }, - { url = "https://files.pythonhosted.org/packages/18/41/bceacec1285b94eb9e4659b24db46c23346d7e22cf258d63419eb5dec6f7/scikit_learn-1.7.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b62b76ad408a821475b43b7bb90a9b1c9a4d8d125d505c2df0539f06d6e631b1", size = 9473691, upload-time = "2025-07-18T08:01:07.006Z" }, - { url = "https://files.pythonhosted.org/packages/12/7b/e1ae4b7e1dd85c4ca2694ff9cc4a9690970fd6150d81b975e6c5c6f8ee7c/scikit_learn-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:9963b065677a4ce295e8ccdee80a1dd62b37249e667095039adcd5bce6e90deb", size = 8900873, upload-time = "2025-07-18T08:01:09.332Z" }, - { url = "https://files.pythonhosted.org/packages/b4/bd/a23177930abd81b96daffa30ef9c54ddbf544d3226b8788ce4c3ef1067b4/scikit_learn-1.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:90c8494ea23e24c0fb371afc474618c1019dc152ce4a10e4607e62196113851b", size = 9334838, upload-time = "2025-07-18T08:01:11.239Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a1/d3a7628630a711e2ac0d1a482910da174b629f44e7dd8cfcd6924a4ef81a/scikit_learn-1.7.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:bb870c0daf3bf3be145ec51df8ac84720d9972170786601039f024bf6d61a518", size = 8651241, upload-time = "2025-07-18T08:01:13.234Z" }, - { url = "https://files.pythonhosted.org/packages/26/92/85ec172418f39474c1cd0221d611345d4f433fc4ee2fc68e01f524ccc4e4/scikit_learn-1.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:40daccd1b5623f39e8943ab39735cadf0bdce80e67cdca2adcb5426e987320a8", size = 9718677, upload-time = "2025-07-18T08:01:15.649Z" }, - { url = "https://files.pythonhosted.org/packages/df/ce/abdb1dcbb1d2b66168ec43b23ee0cee356b4cc4100ddee3943934ebf1480/scikit_learn-1.7.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:30d1f413cfc0aa5a99132a554f1d80517563c34a9d3e7c118fde2d273c6fe0f7", size = 9511189, upload-time = "2025-07-18T08:01:18.013Z" }, - { url = "https://files.pythonhosted.org/packages/b2/3b/47b5eaee01ef2b5a80ba3f7f6ecf79587cb458690857d4777bfd77371c6f/scikit_learn-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:c711d652829a1805a95d7fe96654604a8f16eab5a9e9ad87b3e60173415cb650", size = 8914794, upload-time = "2025-07-18T08:01:20.357Z" }, - { url = "https://files.pythonhosted.org/packages/cb/16/57f176585b35ed865f51b04117947fe20f130f78940c6477b6d66279c9c2/scikit_learn-1.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3cee419b49b5bbae8796ecd690f97aa412ef1674410c23fc3257c6b8b85b8087", size = 9260431, upload-time = "2025-07-18T08:01:22.77Z" }, - { url = "https://files.pythonhosted.org/packages/67/4e/899317092f5efcab0e9bc929e3391341cec8fb0e816c4789686770024580/scikit_learn-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2fd8b8d35817b0d9ebf0b576f7d5ffbbabdb55536b0655a8aaae629d7ffd2e1f", size = 8637191, upload-time = "2025-07-18T08:01:24.731Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1b/998312db6d361ded1dd56b457ada371a8d8d77ca2195a7d18fd8a1736f21/scikit_learn-1.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:588410fa19a96a69763202f1d6b7b91d5d7a5d73be36e189bc6396bfb355bd87", size = 9486346, upload-time = "2025-07-18T08:01:26.713Z" }, - { url = "https://files.pythonhosted.org/packages/ad/09/a2aa0b4e644e5c4ede7006748f24e72863ba2ae71897fecfd832afea01b4/scikit_learn-1.7.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3142f0abe1ad1d1c31a2ae987621e41f6b578144a911ff4ac94781a583adad7", size = 9290988, upload-time = "2025-07-18T08:01:28.938Z" }, - { url = "https://files.pythonhosted.org/packages/15/fa/c61a787e35f05f17fc10523f567677ec4eeee5f95aa4798dbbbcd9625617/scikit_learn-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3ddd9092c1bd469acab337d87930067c87eac6bd544f8d5027430983f1e1ae88", size = 8735568, upload-time = "2025-07-18T08:01:30.936Z" }, - { url = "https://files.pythonhosted.org/packages/52/f8/e0533303f318a0f37b88300d21f79b6ac067188d4824f1047a37214ab718/scikit_learn-1.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b7839687fa46d02e01035ad775982f2470be2668e13ddd151f0f55a5bf123bae", size = 9213143, upload-time = "2025-07-18T08:01:32.942Z" }, - { url = "https://files.pythonhosted.org/packages/71/f3/f1df377d1bdfc3e3e2adc9c119c238b182293e6740df4cbeac6de2cc3e23/scikit_learn-1.7.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a10f276639195a96c86aa572ee0698ad64ee939a7b042060b98bd1930c261d10", size = 8591977, upload-time = "2025-07-18T08:01:34.967Z" }, - { url = "https://files.pythonhosted.org/packages/99/72/c86a4cd867816350fe8dee13f30222340b9cd6b96173955819a5561810c5/scikit_learn-1.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:13679981fdaebc10cc4c13c43344416a86fcbc61449cb3e6517e1df9d12c8309", size = 9436142, upload-time = "2025-07-18T08:01:37.397Z" }, - { url = "https://files.pythonhosted.org/packages/e8/66/277967b29bd297538dc7a6ecfb1a7dce751beabd0d7f7a2233be7a4f7832/scikit_learn-1.7.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f1262883c6a63f067a980a8cdd2d2e7f2513dddcef6a9eaada6416a7a7cbe43", size = 9282996, upload-time = "2025-07-18T08:01:39.721Z" }, - { url = "https://files.pythonhosted.org/packages/e2/47/9291cfa1db1dae9880420d1e07dbc7e8dd4a7cdbc42eaba22512e6bde958/scikit_learn-1.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:ca6d31fb10e04d50bfd2b50d66744729dbb512d4efd0223b864e2fdbfc4cee11", size = 8707418, upload-time = "2025-07-18T08:01:42.124Z" }, - { url = "https://files.pythonhosted.org/packages/61/95/45726819beccdaa34d3362ea9b2ff9f2b5d3b8bf721bd632675870308ceb/scikit_learn-1.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:781674d096303cfe3d351ae6963ff7c958db61cde3421cd490e3a5a58f2a94ae", size = 9561466, upload-time = "2025-07-18T08:01:44.195Z" }, - { url = "https://files.pythonhosted.org/packages/ee/1c/6f4b3344805de783d20a51eb24d4c9ad4b11a7f75c1801e6ec6d777361fd/scikit_learn-1.7.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:10679f7f125fe7ecd5fad37dd1aa2daae7e3ad8df7f3eefa08901b8254b3e12c", size = 9040467, upload-time = "2025-07-18T08:01:46.671Z" }, - { url = "https://files.pythonhosted.org/packages/6f/80/abe18fe471af9f1d181904203d62697998b27d9b62124cd281d740ded2f9/scikit_learn-1.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1f812729e38c8cb37f760dce71a9b83ccfb04f59b3dca7c6079dcdc60544fa9e", size = 9532052, upload-time = "2025-07-18T08:01:48.676Z" }, - { url = "https://files.pythonhosted.org/packages/14/82/b21aa1e0c4cee7e74864d3a5a721ab8fcae5ca55033cb6263dca297ed35b/scikit_learn-1.7.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88e1a20131cf741b84b89567e1717f27a2ced228e0f29103426102bc2e3b8ef7", size = 9361575, upload-time = "2025-07-18T08:01:50.639Z" }, - { url = "https://files.pythonhosted.org/packages/f2/20/f4777fcd5627dc6695fa6b92179d0edb7a3ac1b91bcd9a1c7f64fa7ade23/scikit_learn-1.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b1bd1d919210b6a10b7554b717c9000b5485aa95a1d0f177ae0d7ee8ec750da5", size = 9277310, upload-time = "2025-07-18T08:01:52.547Z" }, -] - -[[package]] -name = "scipy" -version = "1.15.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" }, - { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" }, - { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" }, - { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" }, - { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" }, - { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" }, - { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" }, - { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" }, - { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201, upload-time = "2025-05-08T16:05:08.166Z" }, - { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255, upload-time = "2025-05-08T16:05:14.596Z" }, - { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035, upload-time = "2025-05-08T16:05:20.152Z" }, - { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499, upload-time = "2025-05-08T16:05:24.494Z" }, - { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602, upload-time = "2025-05-08T16:05:29.313Z" }, - { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415, upload-time = "2025-05-08T16:05:34.699Z" }, - { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622, upload-time = "2025-05-08T16:05:40.762Z" }, - { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796, upload-time = "2025-05-08T16:05:48.119Z" }, - { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684, upload-time = "2025-05-08T16:05:54.22Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504, upload-time = "2025-05-08T16:06:00.437Z" }, - { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" }, - { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" }, - { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" }, - { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" }, - { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" }, - { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" }, - { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" }, - { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" }, - { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" }, - { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" }, - { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" }, - { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" }, - { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" }, - { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" }, - { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" }, - { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" }, - { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" }, - { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851, upload-time = "2025-05-08T16:08:33.671Z" }, - { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" }, - { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" }, - { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" }, - { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" }, - { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" }, - { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" }, - { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" }, - { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" }, - { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" }, -] - -[[package]] -name = "scipy" -version = "1.16.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", -] -dependencies = [ - { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/81/18/b06a83f0c5ee8cddbde5e3f3d0bb9b702abfa5136ef6d4620ff67df7eee5/scipy-1.16.0.tar.gz", hash = "sha256:b5ef54021e832869c8cfb03bc3bf20366cbcd426e02a58e8a58d7584dfbb8f62", size = 30581216, upload-time = "2025-06-22T16:27:55.782Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/f8/53fc4884df6b88afd5f5f00240bdc49fee2999c7eff3acf5953eb15bc6f8/scipy-1.16.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:deec06d831b8f6b5fb0b652433be6a09db29e996368ce5911faf673e78d20085", size = 36447362, upload-time = "2025-06-22T16:18:17.817Z" }, - { url = "https://files.pythonhosted.org/packages/c9/25/fad8aa228fa828705142a275fc593d701b1817c98361a2d6b526167d07bc/scipy-1.16.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d30c0fe579bb901c61ab4bb7f3eeb7281f0d4c4a7b52dbf563c89da4fd2949be", size = 28547120, upload-time = "2025-06-22T16:18:24.117Z" }, - { url = "https://files.pythonhosted.org/packages/8d/be/d324ddf6b89fd1c32fecc307f04d095ce84abb52d2e88fab29d0cd8dc7a8/scipy-1.16.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:b2243561b45257f7391d0f49972fca90d46b79b8dbcb9b2cb0f9df928d370ad4", size = 20818922, upload-time = "2025-06-22T16:18:28.035Z" }, - { url = "https://files.pythonhosted.org/packages/cd/e0/cf3f39e399ac83fd0f3ba81ccc5438baba7cfe02176be0da55ff3396f126/scipy-1.16.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:e6d7dfc148135e9712d87c5f7e4f2ddc1304d1582cb3a7d698bbadedb61c7afd", size = 23409695, upload-time = "2025-06-22T16:18:32.497Z" }, - { url = "https://files.pythonhosted.org/packages/5b/61/d92714489c511d3ffd6830ac0eb7f74f243679119eed8b9048e56b9525a1/scipy-1.16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:90452f6a9f3fe5a2cf3748e7be14f9cc7d9b124dce19667b54f5b429d680d539", size = 33444586, upload-time = "2025-06-22T16:18:37.992Z" }, - { url = "https://files.pythonhosted.org/packages/af/2c/40108915fd340c830aee332bb85a9160f99e90893e58008b659b9f3dddc0/scipy-1.16.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a2f0bf2f58031c8701a8b601df41701d2a7be17c7ffac0a4816aeba89c4cdac8", size = 35284126, upload-time = "2025-06-22T16:18:43.605Z" }, - { url = "https://files.pythonhosted.org/packages/d3/30/e9eb0ad3d0858df35d6c703cba0a7e16a18a56a9e6b211d861fc6f261c5f/scipy-1.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c4abb4c11fc0b857474241b812ce69ffa6464b4bd8f4ecb786cf240367a36a7", size = 35608257, upload-time = "2025-06-22T16:18:49.09Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ff/950ee3e0d612b375110d8cda211c1f787764b4c75e418a4b71f4a5b1e07f/scipy-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b370f8f6ac6ef99815b0d5c9f02e7ade77b33007d74802efc8316c8db98fd11e", size = 38040541, upload-time = "2025-06-22T16:18:55.077Z" }, - { url = "https://files.pythonhosted.org/packages/8b/c9/750d34788288d64ffbc94fdb4562f40f609d3f5ef27ab4f3a4ad00c9033e/scipy-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:a16ba90847249bedce8aa404a83fb8334b825ec4a8e742ce6012a7a5e639f95c", size = 38570814, upload-time = "2025-06-22T16:19:00.912Z" }, - { url = "https://files.pythonhosted.org/packages/01/c0/c943bc8d2bbd28123ad0f4f1eef62525fa1723e84d136b32965dcb6bad3a/scipy-1.16.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:7eb6bd33cef4afb9fa5f1fb25df8feeb1e52d94f21a44f1d17805b41b1da3180", size = 36459071, upload-time = "2025-06-22T16:19:06.605Z" }, - { url = "https://files.pythonhosted.org/packages/99/0d/270e2e9f1a4db6ffbf84c9a0b648499842046e4e0d9b2275d150711b3aba/scipy-1.16.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:1dbc8fdba23e4d80394ddfab7a56808e3e6489176d559c6c71935b11a2d59db1", size = 28490500, upload-time = "2025-06-22T16:19:11.775Z" }, - { url = "https://files.pythonhosted.org/packages/1c/22/01d7ddb07cff937d4326198ec8d10831367a708c3da72dfd9b7ceaf13028/scipy-1.16.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:7dcf42c380e1e3737b343dec21095c9a9ad3f9cbe06f9c05830b44b1786c9e90", size = 20762345, upload-time = "2025-06-22T16:19:15.813Z" }, - { url = "https://files.pythonhosted.org/packages/34/7f/87fd69856569ccdd2a5873fe5d7b5bbf2ad9289d7311d6a3605ebde3a94b/scipy-1.16.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:26ec28675f4a9d41587266084c626b02899db373717d9312fa96ab17ca1ae94d", size = 23418563, upload-time = "2025-06-22T16:19:20.746Z" }, - { url = "https://files.pythonhosted.org/packages/f6/f1/e4f4324fef7f54160ab749efbab6a4bf43678a9eb2e9817ed71a0a2fd8de/scipy-1.16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:952358b7e58bd3197cfbd2f2f2ba829f258404bdf5db59514b515a8fe7a36c52", size = 33203951, upload-time = "2025-06-22T16:19:25.813Z" }, - { url = "https://files.pythonhosted.org/packages/6d/f0/b6ac354a956384fd8abee2debbb624648125b298f2c4a7b4f0d6248048a5/scipy-1.16.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03931b4e870c6fef5b5c0970d52c9f6ddd8c8d3e934a98f09308377eba6f3824", size = 35070225, upload-time = "2025-06-22T16:19:31.416Z" }, - { url = "https://files.pythonhosted.org/packages/e5/73/5cbe4a3fd4bc3e2d67ffad02c88b83edc88f381b73ab982f48f3df1a7790/scipy-1.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:512c4f4f85912767c351a0306824ccca6fd91307a9f4318efe8fdbd9d30562ef", size = 35389070, upload-time = "2025-06-22T16:19:37.387Z" }, - { url = "https://files.pythonhosted.org/packages/86/e8/a60da80ab9ed68b31ea5a9c6dfd3c2f199347429f229bf7f939a90d96383/scipy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e69f798847e9add03d512eaf5081a9a5c9a98757d12e52e6186ed9681247a1ac", size = 37825287, upload-time = "2025-06-22T16:19:43.375Z" }, - { url = "https://files.pythonhosted.org/packages/ea/b5/29fece1a74c6a94247f8a6fb93f5b28b533338e9c34fdcc9cfe7a939a767/scipy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:adf9b1999323ba335adc5d1dc7add4781cb5a4b0ef1e98b79768c05c796c4e49", size = 38431929, upload-time = "2025-06-22T16:19:49.385Z" }, - { url = "https://files.pythonhosted.org/packages/46/95/0746417bc24be0c2a7b7563946d61f670a3b491b76adede420e9d173841f/scipy-1.16.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:e9f414cbe9ca289a73e0cc92e33a6a791469b6619c240aa32ee18abdce8ab451", size = 36418162, upload-time = "2025-06-22T16:19:56.3Z" }, - { url = "https://files.pythonhosted.org/packages/19/5a/914355a74481b8e4bbccf67259bbde171348a3f160b67b4945fbc5f5c1e5/scipy-1.16.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:bbba55fb97ba3cdef9b1ee973f06b09d518c0c7c66a009c729c7d1592be1935e", size = 28465985, upload-time = "2025-06-22T16:20:01.238Z" }, - { url = "https://files.pythonhosted.org/packages/58/46/63477fc1246063855969cbefdcee8c648ba4b17f67370bd542ba56368d0b/scipy-1.16.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:58e0d4354eacb6004e7aa1cd350e5514bd0270acaa8d5b36c0627bb3bb486974", size = 20737961, upload-time = "2025-06-22T16:20:05.913Z" }, - { url = "https://files.pythonhosted.org/packages/93/86/0fbb5588b73555e40f9d3d6dde24ee6fac7d8e301a27f6f0cab9d8f66ff2/scipy-1.16.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:75b2094ec975c80efc273567436e16bb794660509c12c6a31eb5c195cbf4b6dc", size = 23377941, upload-time = "2025-06-22T16:20:10.668Z" }, - { url = "https://files.pythonhosted.org/packages/ca/80/a561f2bf4c2da89fa631b3cbf31d120e21ea95db71fd9ec00cb0247c7a93/scipy-1.16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6b65d232157a380fdd11a560e7e21cde34fdb69d65c09cb87f6cc024ee376351", size = 33196703, upload-time = "2025-06-22T16:20:16.097Z" }, - { url = "https://files.pythonhosted.org/packages/11/6b/3443abcd0707d52e48eb315e33cc669a95e29fc102229919646f5a501171/scipy-1.16.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d8747f7736accd39289943f7fe53a8333be7f15a82eea08e4afe47d79568c32", size = 35083410, upload-time = "2025-06-22T16:20:21.734Z" }, - { url = "https://files.pythonhosted.org/packages/20/ab/eb0fc00e1e48961f1bd69b7ad7e7266896fe5bad4ead91b5fc6b3561bba4/scipy-1.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eb9f147a1b8529bb7fec2a85cf4cf42bdfadf9e83535c309a11fdae598c88e8b", size = 35387829, upload-time = "2025-06-22T16:20:27.548Z" }, - { url = "https://files.pythonhosted.org/packages/57/9e/d6fc64e41fad5d481c029ee5a49eefc17f0b8071d636a02ceee44d4a0de2/scipy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d2b83c37edbfa837a8923d19c749c1935ad3d41cf196006a24ed44dba2ec4358", size = 37841356, upload-time = "2025-06-22T16:20:35.112Z" }, - { url = "https://files.pythonhosted.org/packages/7c/a7/4c94bbe91f12126b8bf6709b2471900577b7373a4fd1f431f28ba6f81115/scipy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:79a3c13d43c95aa80b87328a46031cf52508cf5f4df2767602c984ed1d3c6bbe", size = 38403710, upload-time = "2025-06-22T16:21:54.473Z" }, - { url = "https://files.pythonhosted.org/packages/47/20/965da8497f6226e8fa90ad3447b82ed0e28d942532e92dd8b91b43f100d4/scipy-1.16.0-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:f91b87e1689f0370690e8470916fe1b2308e5b2061317ff76977c8f836452a47", size = 36813833, upload-time = "2025-06-22T16:20:43.925Z" }, - { url = "https://files.pythonhosted.org/packages/28/f4/197580c3dac2d234e948806e164601c2df6f0078ed9f5ad4a62685b7c331/scipy-1.16.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:88a6ca658fb94640079e7a50b2ad3b67e33ef0f40e70bdb7dc22017dae73ac08", size = 28974431, upload-time = "2025-06-22T16:20:51.302Z" }, - { url = "https://files.pythonhosted.org/packages/8a/fc/e18b8550048d9224426e76906694c60028dbdb65d28b1372b5503914b89d/scipy-1.16.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:ae902626972f1bd7e4e86f58fd72322d7f4ec7b0cfc17b15d4b7006efc385176", size = 21246454, upload-time = "2025-06-22T16:20:57.276Z" }, - { url = "https://files.pythonhosted.org/packages/8c/48/07b97d167e0d6a324bfd7484cd0c209cc27338b67e5deadae578cf48e809/scipy-1.16.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:8cb824c1fc75ef29893bc32b3ddd7b11cf9ab13c1127fe26413a05953b8c32ed", size = 23772979, upload-time = "2025-06-22T16:21:03.363Z" }, - { url = "https://files.pythonhosted.org/packages/4c/4f/9efbd3f70baf9582edf271db3002b7882c875ddd37dc97f0f675ad68679f/scipy-1.16.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:de2db7250ff6514366a9709c2cba35cb6d08498e961cba20d7cff98a7ee88938", size = 33341972, upload-time = "2025-06-22T16:21:11.14Z" }, - { url = "https://files.pythonhosted.org/packages/3f/dc/9e496a3c5dbe24e76ee24525155ab7f659c20180bab058ef2c5fa7d9119c/scipy-1.16.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e85800274edf4db8dd2e4e93034f92d1b05c9421220e7ded9988b16976f849c1", size = 35185476, upload-time = "2025-06-22T16:21:19.156Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b3/21001cff985a122ba434c33f2c9d7d1dc3b669827e94f4fc4e1fe8b9dfd8/scipy-1.16.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4f720300a3024c237ace1cb11f9a84c38beb19616ba7c4cdcd771047a10a1706", size = 35570990, upload-time = "2025-06-22T16:21:27.797Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d3/7ba42647d6709251cdf97043d0c107e0317e152fa2f76873b656b509ff55/scipy-1.16.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:aad603e9339ddb676409b104c48a027e9916ce0d2838830691f39552b38a352e", size = 37950262, upload-time = "2025-06-22T16:21:36.976Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c4/231cac7a8385394ebbbb4f1ca662203e9d8c332825ab4f36ffc3ead09a42/scipy-1.16.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f56296fefca67ba605fd74d12f7bd23636267731a72cb3947963e76b8c0a25db", size = 38515076, upload-time = "2025-06-22T16:21:45.694Z" }, -] - -[[package]] -name = "secretstorage" -version = "3.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/53/a4/f48c9d79cb507ed1373477dbceaba7401fd8a23af63b837fa61f1dcd3691/SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", size = 19739, upload-time = "2022-08-13T16:22:46.976Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/24/b4293291fa1dd830f353d2cb163295742fa87f179fcc8a20a306a81978b7/SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99", size = 15221, upload-time = "2022-08-13T16:22:44.457Z" }, -] - -[[package]] -name = "sentence-transformers" -version = "5.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, - { name = "pillow" }, - { name = "scikit-learn" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "torch" }, - { name = "tqdm" }, - { name = "transformers" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/99/69/2a29773b43a24ee04eb26af492d85d520b30a86cfef22a0885e77e9c4a16/sentence_transformers-5.0.0.tar.gz", hash = "sha256:e5a411845910275fd166bacb01d28b7f79537d3550628ae42309dbdd3d5670d1", size = 366847, upload-time = "2025-07-01T13:01:33.04Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/ff/178f08ea5ebc1f9193d9de7f601efe78c01748347875c8438f66f5cecc19/sentence_transformers-5.0.0-py3-none-any.whl", hash = "sha256:346240f9cc6b01af387393f03e103998190dfb0826a399d0c38a81a05c7a5d76", size = 470191, upload-time = "2025-07-01T13:01:31.619Z" }, -] - -[[package]] -name = "setuptools" -version = "80.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[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 = "2.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/07/3e/eae74d8d33e3262bae0a7e023bb43d8bdd27980aa3557333f4632611151f/sse_starlette-2.4.1.tar.gz", hash = "sha256:7c8a800a1ca343e9165fc06bbda45c78e4c6166320707ae30b416c42da070926", size = 18635, upload-time = "2025-07-06T09:41:33.631Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/f1/6c7eaa8187ba789a6dd6d74430307478d2a91c23a5452ab339b6fbe15a08/sse_starlette-2.4.1-py3-none-any.whl", hash = "sha256:08b77ea898ab1a13a428b2b6f73cfe6d0e607a7b4e15b9bb23e4a37b087fd39a", size = 10824, upload-time = "2025-07-06T09:41:32.321Z" }, -] - -[[package]] -name = "stack-data" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asttokens" }, - { name = "executing" }, - { name = "pure-eval" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, -] - -[[package]] -name = "starlette" -version = "0.47.2" -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/04/57/d062573f391d062710d4088fa1369428c38d51460ab6fedff920efef932e/starlette-0.47.2.tar.gz", hash = "sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8", size = 2583948, upload-time = "2025-07-20T17:31:58.522Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b", size = 72984, upload-time = "2025-07-20T17:31:56.738Z" }, -] - -[[package]] -name = "structlog" -version = "25.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/79/b9/6e672db4fec07349e7a8a8172c1a6ae235c58679ca29c3f86a61b5e59ff3/structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4", size = 1369138, upload-time = "2025-06-02T08:21:12.971Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/4a/97ee6973e3a73c74c8120d59829c3861ea52210667ec3e7a16045c62b64d/structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c", size = 68720, upload-time = "2025-06-02T08:21:11.43Z" }, -] - -[[package]] -name = "sympy" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mpmath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, -] - -[[package]] -name = "tenacity" -version = "9.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, -] - -[[package]] -name = "threadpoolctl" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, -] - -[[package]] -name = "tiktoken" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "regex" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ea/cf/756fedf6981e82897f2d570dd25fa597eb3f4459068ae0572d7e888cfd6f/tiktoken-0.9.0.tar.gz", hash = "sha256:d02a5ca6a938e0490e1ff957bc48c8b078c88cb83977be1625b1fd8aac792c5d", size = 35991, upload-time = "2025-02-14T06:03:01.003Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/f3/50ec5709fad61641e4411eb1b9ac55b99801d71f1993c29853f256c726c9/tiktoken-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:586c16358138b96ea804c034b8acf3f5d3f0258bd2bc3b0227af4af5d622e382", size = 1065770, upload-time = "2025-02-14T06:02:01.251Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f8/5a9560a422cf1755b6e0a9a436e14090eeb878d8ec0f80e0cd3d45b78bf4/tiktoken-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9c59ccc528c6c5dd51820b3474402f69d9a9e1d656226848ad68a8d5b2e5108", size = 1009314, upload-time = "2025-02-14T06:02:02.869Z" }, - { url = "https://files.pythonhosted.org/packages/bc/20/3ed4cfff8f809cb902900ae686069e029db74567ee10d017cb254df1d598/tiktoken-0.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0968d5beeafbca2a72c595e8385a1a1f8af58feaebb02b227229b69ca5357fd", size = 1143140, upload-time = "2025-02-14T06:02:04.165Z" }, - { url = "https://files.pythonhosted.org/packages/f1/95/cc2c6d79df8f113bdc6c99cdec985a878768120d87d839a34da4bd3ff90a/tiktoken-0.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a5fb085a6a3b7350b8fc838baf493317ca0e17bd95e8642f95fc69ecfed1de", size = 1197860, upload-time = "2025-02-14T06:02:06.268Z" }, - { url = "https://files.pythonhosted.org/packages/c7/6c/9c1a4cc51573e8867c9381db1814223c09ebb4716779c7f845d48688b9c8/tiktoken-0.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15a2752dea63d93b0332fb0ddb05dd909371ededa145fe6a3242f46724fa7990", size = 1259661, upload-time = "2025-02-14T06:02:08.889Z" }, - { url = "https://files.pythonhosted.org/packages/cd/4c/22eb8e9856a2b1808d0a002d171e534eac03f96dbe1161978d7389a59498/tiktoken-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:26113fec3bd7a352e4b33dbaf1bd8948de2507e30bd95a44e2b1156647bc01b4", size = 894026, upload-time = "2025-02-14T06:02:12.841Z" }, - { url = "https://files.pythonhosted.org/packages/4d/ae/4613a59a2a48e761c5161237fc850eb470b4bb93696db89da51b79a871f1/tiktoken-0.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f32cc56168eac4851109e9b5d327637f15fd662aa30dd79f964b7c39fbadd26e", size = 1065987, upload-time = "2025-02-14T06:02:14.174Z" }, - { url = "https://files.pythonhosted.org/packages/3f/86/55d9d1f5b5a7e1164d0f1538a85529b5fcba2b105f92db3622e5d7de6522/tiktoken-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:45556bc41241e5294063508caf901bf92ba52d8ef9222023f83d2483a3055348", size = 1009155, upload-time = "2025-02-14T06:02:15.384Z" }, - { url = "https://files.pythonhosted.org/packages/03/58/01fb6240df083b7c1916d1dcb024e2b761213c95d576e9f780dfb5625a76/tiktoken-0.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03935988a91d6d3216e2ec7c645afbb3d870b37bcb67ada1943ec48678e7ee33", size = 1142898, upload-time = "2025-02-14T06:02:16.666Z" }, - { url = "https://files.pythonhosted.org/packages/b1/73/41591c525680cd460a6becf56c9b17468d3711b1df242c53d2c7b2183d16/tiktoken-0.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b3d80aad8d2c6b9238fc1a5524542087c52b860b10cbf952429ffb714bc1136", size = 1197535, upload-time = "2025-02-14T06:02:18.595Z" }, - { url = "https://files.pythonhosted.org/packages/7d/7c/1069f25521c8f01a1a182f362e5c8e0337907fae91b368b7da9c3e39b810/tiktoken-0.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b2a21133be05dc116b1d0372af051cd2c6aa1d2188250c9b553f9fa49301b336", size = 1259548, upload-time = "2025-02-14T06:02:20.729Z" }, - { url = "https://files.pythonhosted.org/packages/6f/07/c67ad1724b8e14e2b4c8cca04b15da158733ac60136879131db05dda7c30/tiktoken-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:11a20e67fdf58b0e2dea7b8654a288e481bb4fc0289d3ad21291f8d0849915fb", size = 893895, upload-time = "2025-02-14T06:02:22.67Z" }, - { url = "https://files.pythonhosted.org/packages/cf/e5/21ff33ecfa2101c1bb0f9b6df750553bd873b7fb532ce2cb276ff40b197f/tiktoken-0.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e88f121c1c22b726649ce67c089b90ddda8b9662545a8aeb03cfef15967ddd03", size = 1065073, upload-time = "2025-02-14T06:02:24.768Z" }, - { url = "https://files.pythonhosted.org/packages/8e/03/a95e7b4863ee9ceec1c55983e4cc9558bcfd8f4f80e19c4f8a99642f697d/tiktoken-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a6600660f2f72369acb13a57fb3e212434ed38b045fd8cc6cdd74947b4b5d210", size = 1008075, upload-time = "2025-02-14T06:02:26.92Z" }, - { url = "https://files.pythonhosted.org/packages/40/10/1305bb02a561595088235a513ec73e50b32e74364fef4de519da69bc8010/tiktoken-0.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95e811743b5dfa74f4b227927ed86cbc57cad4df859cb3b643be797914e41794", size = 1140754, upload-time = "2025-02-14T06:02:28.124Z" }, - { url = "https://files.pythonhosted.org/packages/1b/40/da42522018ca496432ffd02793c3a72a739ac04c3794a4914570c9bb2925/tiktoken-0.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99376e1370d59bcf6935c933cb9ba64adc29033b7e73f5f7569f3aad86552b22", size = 1196678, upload-time = "2025-02-14T06:02:29.845Z" }, - { url = "https://files.pythonhosted.org/packages/5c/41/1e59dddaae270ba20187ceb8aa52c75b24ffc09f547233991d5fd822838b/tiktoken-0.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:badb947c32739fb6ddde173e14885fb3de4d32ab9d8c591cbd013c22b4c31dd2", size = 1259283, upload-time = "2025-02-14T06:02:33.838Z" }, - { url = "https://files.pythonhosted.org/packages/5b/64/b16003419a1d7728d0d8c0d56a4c24325e7b10a21a9dd1fc0f7115c02f0a/tiktoken-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:5a62d7a25225bafed786a524c1b9f0910a1128f4232615bf3f8257a73aaa3b16", size = 894897, upload-time = "2025-02-14T06:02:36.265Z" }, - { url = "https://files.pythonhosted.org/packages/7a/11/09d936d37f49f4f494ffe660af44acd2d99eb2429d60a57c71318af214e0/tiktoken-0.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b0e8e05a26eda1249e824156d537015480af7ae222ccb798e5234ae0285dbdb", size = 1064919, upload-time = "2025-02-14T06:02:37.494Z" }, - { url = "https://files.pythonhosted.org/packages/80/0e/f38ba35713edb8d4197ae602e80837d574244ced7fb1b6070b31c29816e0/tiktoken-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:27d457f096f87685195eea0165a1807fae87b97b2161fe8c9b1df5bd74ca6f63", size = 1007877, upload-time = "2025-02-14T06:02:39.516Z" }, - { url = "https://files.pythonhosted.org/packages/fe/82/9197f77421e2a01373e27a79dd36efdd99e6b4115746ecc553318ecafbf0/tiktoken-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cf8ded49cddf825390e36dd1ad35cd49589e8161fdcb52aa25f0583e90a3e01", size = 1140095, upload-time = "2025-02-14T06:02:41.791Z" }, - { url = "https://files.pythonhosted.org/packages/f2/bb/4513da71cac187383541facd0291c4572b03ec23c561de5811781bbd988f/tiktoken-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc156cb314119a8bb9748257a2eaebd5cc0753b6cb491d26694ed42fc7cb3139", size = 1195649, upload-time = "2025-02-14T06:02:43Z" }, - { url = "https://files.pythonhosted.org/packages/fa/5c/74e4c137530dd8504e97e3a41729b1103a4ac29036cbfd3250b11fd29451/tiktoken-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cd69372e8c9dd761f0ab873112aba55a0e3e506332dd9f7522ca466e817b1b7a", size = 1258465, upload-time = "2025-02-14T06:02:45.046Z" }, - { url = "https://files.pythonhosted.org/packages/de/a8/8f499c179ec900783ffe133e9aab10044481679bb9aad78436d239eee716/tiktoken-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:5ea0edb6f83dc56d794723286215918c1cde03712cbbafa0348b33448faf5b95", size = 894669, upload-time = "2025-02-14T06:02:47.341Z" }, -] - -[[package]] -name = "tokenizers" -version = "0.21.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ab/2d/b0fce2b8201635f60e8c95990080f58461cc9ca3d5026de2e900f38a7f21/tokenizers-0.21.2.tar.gz", hash = "sha256:fdc7cffde3e2113ba0e6cc7318c40e3438a4d74bbc62bf04bcc63bdfb082ac77", size = 351545, upload-time = "2025-06-24T10:24:52.449Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/cc/2936e2d45ceb130a21d929743f1e9897514691bec123203e10837972296f/tokenizers-0.21.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:342b5dfb75009f2255ab8dec0041287260fed5ce00c323eb6bab639066fef8ec", size = 2875206, upload-time = "2025-06-24T10:24:42.755Z" }, - { url = "https://files.pythonhosted.org/packages/6c/e6/33f41f2cc7861faeba8988e7a77601407bf1d9d28fc79c5903f8f77df587/tokenizers-0.21.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:126df3205d6f3a93fea80c7a8a266a78c1bd8dd2fe043386bafdd7736a23e45f", size = 2732655, upload-time = "2025-06-24T10:24:41.56Z" }, - { url = "https://files.pythonhosted.org/packages/33/2b/1791eb329c07122a75b01035b1a3aa22ad139f3ce0ece1b059b506d9d9de/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a32cd81be21168bd0d6a0f0962d60177c447a1aa1b1e48fa6ec9fc728ee0b12", size = 3019202, upload-time = "2025-06-24T10:24:31.791Z" }, - { url = "https://files.pythonhosted.org/packages/05/15/fd2d8104faa9f86ac68748e6f7ece0b5eb7983c7efc3a2c197cb98c99030/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8bd8999538c405133c2ab999b83b17c08b7fc1b48c1ada2469964605a709ef91", size = 2934539, upload-time = "2025-06-24T10:24:34.567Z" }, - { url = "https://files.pythonhosted.org/packages/a5/2e/53e8fd053e1f3ffbe579ca5f9546f35ac67cf0039ed357ad7ec57f5f5af0/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e9944e61239b083a41cf8fc42802f855e1dca0f499196df37a8ce219abac6eb", size = 3248665, upload-time = "2025-06-24T10:24:39.024Z" }, - { url = "https://files.pythonhosted.org/packages/00/15/79713359f4037aa8f4d1f06ffca35312ac83629da062670e8830917e2153/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:514cd43045c5d546f01142ff9c79a96ea69e4b5cda09e3027708cb2e6d5762ab", size = 3451305, upload-time = "2025-06-24T10:24:36.133Z" }, - { url = "https://files.pythonhosted.org/packages/38/5f/959f3a8756fc9396aeb704292777b84f02a5c6f25c3fc3ba7530db5feb2c/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1b9405822527ec1e0f7d8d2fdb287a5730c3a6518189c968254a8441b21faae", size = 3214757, upload-time = "2025-06-24T10:24:37.784Z" }, - { url = "https://files.pythonhosted.org/packages/c5/74/f41a432a0733f61f3d21b288de6dfa78f7acff309c6f0f323b2833e9189f/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed9a4d51c395103ad24f8e7eb976811c57fbec2af9f133df471afcd922e5020", size = 3121887, upload-time = "2025-06-24T10:24:40.293Z" }, - { url = "https://files.pythonhosted.org/packages/3c/6a/bc220a11a17e5d07b0dfb3b5c628621d4dcc084bccd27cfaead659963016/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2c41862df3d873665ec78b6be36fcc30a26e3d4902e9dd8608ed61d49a48bc19", size = 9091965, upload-time = "2025-06-24T10:24:44.431Z" }, - { url = "https://files.pythonhosted.org/packages/6c/bd/ac386d79c4ef20dc6f39c4706640c24823dca7ebb6f703bfe6b5f0292d88/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed21dc7e624e4220e21758b2e62893be7101453525e3d23264081c9ef9a6d00d", size = 9053372, upload-time = "2025-06-24T10:24:46.455Z" }, - { url = "https://files.pythonhosted.org/packages/63/7b/5440bf203b2a5358f074408f7f9c42884849cd9972879e10ee6b7a8c3b3d/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:0e73770507e65a0e0e2a1affd6b03c36e3bc4377bd10c9ccf51a82c77c0fe365", size = 9298632, upload-time = "2025-06-24T10:24:48.446Z" }, - { url = "https://files.pythonhosted.org/packages/a4/d2/faa1acac3f96a7427866e94ed4289949b2524f0c1878512516567d80563c/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:106746e8aa9014a12109e58d540ad5465b4c183768ea96c03cbc24c44d329958", size = 9470074, upload-time = "2025-06-24T10:24:50.378Z" }, - { url = "https://files.pythonhosted.org/packages/d8/a5/896e1ef0707212745ae9f37e84c7d50269411aef2e9ccd0de63623feecdf/tokenizers-0.21.2-cp39-abi3-win32.whl", hash = "sha256:cabda5a6d15d620b6dfe711e1af52205266d05b379ea85a8a301b3593c60e962", size = 2330115, upload-time = "2025-06-24T10:24:55.069Z" }, - { url = "https://files.pythonhosted.org/packages/13/c3/cc2755ee10be859c4338c962a35b9a663788c0c0b50c0bdd8078fb6870cf/tokenizers-0.21.2-cp39-abi3-win_amd64.whl", hash = "sha256:58747bb898acdb1007f37a7bbe614346e98dc28708ffb66a3fd50ce169ac6c98", size = 2509918, upload-time = "2025-06-24T10:24:53.71Z" }, -] - -[[package]] -name = "tomli" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" }, - { url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" }, - { url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" }, - { url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" }, - { url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" }, - { url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" }, - { url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" }, - { url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" }, - { url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" }, - { url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" }, - { url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" }, - { url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" }, - { url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" }, - { url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" }, - { url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" }, - { url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" }, - { url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" }, - { url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" }, - { url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" }, - { url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" }, - { url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" }, - { url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" }, - { url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" }, - { url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" }, - { url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" }, - { url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" }, - { url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" }, - { url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" }, - { url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" }, - { url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" }, - { url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" }, - { url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" }, - { url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" }, - { url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" }, - { url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" }, - { url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" }, - { url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" }, - { url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" }, - { url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" }, - { url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" }, - { url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" }, - { url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" }, - { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, -] - -[[package]] -name = "torch" -version = "2.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "networkx", version = "3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "setuptools", marker = "python_full_version >= '3.12'" }, - { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "typing-extensions" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/27/2e06cb52adf89fe6e020963529d17ed51532fc73c1e6d1b18420ef03338c/torch-2.7.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:a103b5d782af5bd119b81dbcc7ffc6fa09904c423ff8db397a1e6ea8fd71508f", size = 99089441, upload-time = "2025-06-04T17:38:48.268Z" }, - { url = "https://files.pythonhosted.org/packages/0a/7c/0a5b3aee977596459ec45be2220370fde8e017f651fecc40522fd478cb1e/torch-2.7.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:fe955951bdf32d182ee8ead6c3186ad54781492bf03d547d31771a01b3d6fb7d", size = 821154516, upload-time = "2025-06-04T17:36:28.556Z" }, - { url = "https://files.pythonhosted.org/packages/f9/91/3d709cfc5e15995fb3fe7a6b564ce42280d3a55676dad672205e94f34ac9/torch-2.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:885453d6fba67d9991132143bf7fa06b79b24352f4506fd4d10b309f53454162", size = 216093147, upload-time = "2025-06-04T17:39:38.132Z" }, - { url = "https://files.pythonhosted.org/packages/92/f6/5da3918414e07da9866ecb9330fe6ffdebe15cb9a4c5ada7d4b6e0a6654d/torch-2.7.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:d72acfdb86cee2a32c0ce0101606f3758f0d8bb5f8f31e7920dc2809e963aa7c", size = 68630914, upload-time = "2025-06-04T17:39:31.162Z" }, - { url = "https://files.pythonhosted.org/packages/11/56/2eae3494e3d375533034a8e8cf0ba163363e996d85f0629441fa9d9843fe/torch-2.7.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:236f501f2e383f1cb861337bdf057712182f910f10aeaf509065d54d339e49b2", size = 99093039, upload-time = "2025-06-04T17:39:06.963Z" }, - { url = "https://files.pythonhosted.org/packages/e5/94/34b80bd172d0072c9979708ccd279c2da2f55c3ef318eceec276ab9544a4/torch-2.7.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:06eea61f859436622e78dd0cdd51dbc8f8c6d76917a9cf0555a333f9eac31ec1", size = 821174704, upload-time = "2025-06-04T17:37:03.799Z" }, - { url = "https://files.pythonhosted.org/packages/50/9e/acf04ff375b0b49a45511c55d188bcea5c942da2aaf293096676110086d1/torch-2.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:8273145a2e0a3c6f9fd2ac36762d6ee89c26d430e612b95a99885df083b04e52", size = 216095937, upload-time = "2025-06-04T17:39:24.83Z" }, - { url = "https://files.pythonhosted.org/packages/5b/2b/d36d57c66ff031f93b4fa432e86802f84991477e522adcdffd314454326b/torch-2.7.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:aea4fc1bf433d12843eb2c6b2204861f43d8364597697074c8d38ae2507f8730", size = 68640034, upload-time = "2025-06-04T17:39:17.989Z" }, - { url = "https://files.pythonhosted.org/packages/87/93/fb505a5022a2e908d81fe9a5e0aa84c86c0d5f408173be71c6018836f34e/torch-2.7.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:27ea1e518df4c9de73af7e8a720770f3628e7f667280bce2be7a16292697e3fa", size = 98948276, upload-time = "2025-06-04T17:39:12.852Z" }, - { url = "https://files.pythonhosted.org/packages/56/7e/67c3fe2b8c33f40af06326a3d6ae7776b3e3a01daa8f71d125d78594d874/torch-2.7.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c33360cfc2edd976c2633b3b66c769bdcbbf0e0b6550606d188431c81e7dd1fc", size = 821025792, upload-time = "2025-06-04T17:34:58.747Z" }, - { url = "https://files.pythonhosted.org/packages/a1/37/a37495502bc7a23bf34f89584fa5a78e25bae7b8da513bc1b8f97afb7009/torch-2.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:d8bf6e1856ddd1807e79dc57e54d3335f2b62e6f316ed13ed3ecfe1fc1df3d8b", size = 216050349, upload-time = "2025-06-04T17:38:59.709Z" }, - { url = "https://files.pythonhosted.org/packages/3a/60/04b77281c730bb13460628e518c52721257814ac6c298acd25757f6a175c/torch-2.7.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:787687087412c4bd68d315e39bc1223f08aae1d16a9e9771d95eabbb04ae98fb", size = 68645146, upload-time = "2025-06-04T17:38:52.97Z" }, - { url = "https://files.pythonhosted.org/packages/66/81/e48c9edb655ee8eb8c2a6026abdb6f8d2146abd1f150979ede807bb75dcb/torch-2.7.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:03563603d931e70722dce0e11999d53aa80a375a3d78e6b39b9f6805ea0a8d28", size = 98946649, upload-time = "2025-06-04T17:38:43.031Z" }, - { url = "https://files.pythonhosted.org/packages/3a/24/efe2f520d75274fc06b695c616415a1e8a1021d87a13c68ff9dce733d088/torch-2.7.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:d632f5417b6980f61404a125b999ca6ebd0b8b4bbdbb5fbbba44374ab619a412", size = 821033192, upload-time = "2025-06-04T17:38:09.146Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d9/9c24d230333ff4e9b6807274f6f8d52a864210b52ec794c5def7925f4495/torch-2.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:23660443e13995ee93e3d844786701ea4ca69f337027b05182f5ba053ce43b38", size = 216055668, upload-time = "2025-06-04T17:38:36.253Z" }, - { url = "https://files.pythonhosted.org/packages/95/bf/e086ee36ddcef9299f6e708d3b6c8487c1651787bb9ee2939eb2a7f74911/torch-2.7.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:0da4f4dba9f65d0d203794e619fe7ca3247a55ffdcbd17ae8fb83c8b2dc9b585", size = 68925988, upload-time = "2025-06-04T17:38:29.273Z" }, - { url = "https://files.pythonhosted.org/packages/69/6a/67090dcfe1cf9048448b31555af6efb149f7afa0a310a366adbdada32105/torch-2.7.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:e08d7e6f21a617fe38eeb46dd2213ded43f27c072e9165dc27300c9ef9570934", size = 99028857, upload-time = "2025-06-04T17:37:50.956Z" }, - { url = "https://files.pythonhosted.org/packages/90/1c/48b988870823d1cc381f15ec4e70ed3d65e043f43f919329b0045ae83529/torch-2.7.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:30207f672328a42df4f2174b8f426f354b2baa0b7cca3a0adb3d6ab5daf00dc8", size = 821098066, upload-time = "2025-06-04T17:37:33.939Z" }, - { url = "https://files.pythonhosted.org/packages/7b/eb/10050d61c9d5140c5dc04a89ed3257ef1a6b93e49dd91b95363d757071e0/torch-2.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:79042feca1c634aaf6603fe6feea8c6b30dfa140a6bbc0b973e2260c7e79a22e", size = 216336310, upload-time = "2025-06-04T17:36:09.862Z" }, - { url = "https://files.pythonhosted.org/packages/b1/29/beb45cdf5c4fc3ebe282bf5eafc8dfd925ead7299b3c97491900fe5ed844/torch-2.7.1-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:988b0cbc4333618a1056d2ebad9eb10089637b659eb645434d0809d8d937b946", size = 68645708, upload-time = "2025-06-04T17:34:39.852Z" }, -] - -[[package]] -name = "tqdm" -version = "4.67.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, -] - -[[package]] -name = "traitlets" -version = "5.14.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, -] - -[[package]] -name = "transformers" -version = "4.53.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "huggingface-hub" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "regex" }, - { name = "requests" }, - { name = "safetensors" }, - { name = "tokenizers" }, - { name = "tqdm" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f1/5c/49182918b58eaa0b4c954fd0e37c79fc299e5643e69d70089d0b0eb0cd9b/transformers-4.53.3.tar.gz", hash = "sha256:b2eda1a261de79b78b97f7888fe2005fc0c3fabf5dad33d52cc02983f9f675d8", size = 9197478, upload-time = "2025-07-22T07:30:51.51Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/b1/d7520cc5cb69c825599042eb3a7c986fa9baa8a8d2dea9acd78e152c81e2/transformers-4.53.3-py3-none-any.whl", hash = "sha256:5aba81c92095806b6baf12df35d756cf23b66c356975fb2a7fa9e536138d7c75", size = 10826382, upload-time = "2025-07-22T07:30:48.458Z" }, -] - -[[package]] -name = "triton" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "setuptools" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/a9/549e51e9b1b2c9b854fd761a1d23df0ba2fbc60bd0c13b489ffa518cfcb7/triton-3.3.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b74db445b1c562844d3cfad6e9679c72e93fdfb1a90a24052b03bb5c49d1242e", size = 155600257, upload-time = "2025-05-29T23:39:36.085Z" }, - { url = "https://files.pythonhosted.org/packages/21/2f/3e56ea7b58f80ff68899b1dbe810ff257c9d177d288c6b0f55bf2fe4eb50/triton-3.3.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b31e3aa26f8cb3cc5bf4e187bf737cbacf17311e1112b781d4a059353dfd731b", size = 155689937, upload-time = "2025-05-29T23:39:44.182Z" }, - { url = "https://files.pythonhosted.org/packages/24/5f/950fb373bf9c01ad4eb5a8cd5eaf32cdf9e238c02f9293557a2129b9c4ac/triton-3.3.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9999e83aba21e1a78c1f36f21bce621b77bcaa530277a50484a7cb4a822f6e43", size = 155669138, upload-time = "2025-05-29T23:39:51.771Z" }, - { url = "https://files.pythonhosted.org/packages/74/1f/dfb531f90a2d367d914adfee771babbd3f1a5b26c3f5fbc458dee21daa78/triton-3.3.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b89d846b5a4198317fec27a5d3a609ea96b6d557ff44b56c23176546023c4240", size = 155673035, upload-time = "2025-05-29T23:40:02.468Z" }, - { url = "https://files.pythonhosted.org/packages/28/71/bd20ffcb7a64c753dc2463489a61bf69d531f308e390ad06390268c4ea04/triton-3.3.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3198adb9d78b77818a5388bff89fa72ff36f9da0bc689db2f0a651a67ce6a42", size = 155735832, upload-time = "2025-05-29T23:40:10.522Z" }, -] - -[[package]] -name = "twine" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "id" }, - { name = "keyring", marker = "platform_machine != 'ppc64le' and platform_machine != 's390x'" }, - { name = "packaging" }, - { name = "readme-renderer" }, - { name = "requests" }, - { name = "requests-toolbelt" }, - { name = "rfc3986" }, - { name = "rich" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c8/a2/6df94fc5c8e2170d21d7134a565c3a8fb84f9797c1dd65a5976aaf714418/twine-6.1.0.tar.gz", hash = "sha256:be324f6272eff91d07ee93f251edf232fc647935dd585ac003539b42404a8dbd", size = 168404, upload-time = "2025-01-21T18:45:26.758Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/b6/74e927715a285743351233f33ea3c684528a0d374d2e43ff9ce9585b73fe/twine-6.1.0-py3-none-any.whl", hash = "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384", size = 40791, upload-time = "2025-01-21T18:45:24.584Z" }, -] - -[[package]] -name = "typer" -version = "0.16.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c5/8c/7d682431efca5fd290017663ea4588bf6f2c6aad085c7f108c5dbc316e70/typer-0.16.0.tar.gz", hash = "sha256:af377ffaee1dbe37ae9440cb4e8f11686ea5ce4e9bae01b84ae7c63b87f1dd3b", size = 102625, upload-time = "2025-05-26T14:30:31.824Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/42/3efaf858001d2c2913de7f354563e3a3a2f0decae3efe98427125a8f441e/typer-0.16.0-py3-none-any.whl", hash = "sha256:1f79bed11d4d02d4310e3c1b7ba594183bcedb0ac73b27a9e5f28f6fb5b98855", size = 46317, upload-time = "2025-05-26T14:30:30.523Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.14.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, -] - -[[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 = "tzdata" -version = "2025.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, -] - -[[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.optional-dependencies] -standard = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "httptools" }, - { name = "python-dotenv" }, - { name = "pyyaml" }, - { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, - { name = "watchfiles" }, - { name = "websockets" }, -] - -[[package]] -name = "uvloop" -version = "0.21.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3", size = 2492741, upload-time = "2024-10-14T23:38:35.489Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/76/44a55515e8c9505aa1420aebacf4dd82552e5e15691654894e90d0bd051a/uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f", size = 1442019, upload-time = "2024-10-14T23:37:20.068Z" }, - { url = "https://files.pythonhosted.org/packages/35/5a/62d5800358a78cc25c8a6c72ef8b10851bdb8cca22e14d9c74167b7f86da/uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d", size = 801898, upload-time = "2024-10-14T23:37:22.663Z" }, - { url = "https://files.pythonhosted.org/packages/f3/96/63695e0ebd7da6c741ccd4489b5947394435e198a1382349c17b1146bb97/uvloop-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f38b2e090258d051d68a5b14d1da7203a3c3677321cf32a95a6f4db4dd8b6f26", size = 3827735, upload-time = "2024-10-14T23:37:25.129Z" }, - { url = "https://files.pythonhosted.org/packages/61/e0/f0f8ec84979068ffae132c58c79af1de9cceeb664076beea86d941af1a30/uvloop-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c43e0f13022b998eb9b973b5e97200c8b90823454d4bc06ab33829e09fb9bb", size = 3825126, upload-time = "2024-10-14T23:37:27.59Z" }, - { url = "https://files.pythonhosted.org/packages/bf/fe/5e94a977d058a54a19df95f12f7161ab6e323ad49f4dabc28822eb2df7ea/uvloop-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10d66943def5fcb6e7b37310eb6b5639fd2ccbc38df1177262b0640c3ca68c1f", size = 3705789, upload-time = "2024-10-14T23:37:29.385Z" }, - { url = "https://files.pythonhosted.org/packages/26/dd/c7179618e46092a77e036650c1f056041a028a35c4d76945089fcfc38af8/uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c", size = 3800523, upload-time = "2024-10-14T23:37:32.048Z" }, - { url = "https://files.pythonhosted.org/packages/57/a7/4cf0334105c1160dd6819f3297f8700fda7fc30ab4f61fbf3e725acbc7cc/uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8", size = 1447410, upload-time = "2024-10-14T23:37:33.612Z" }, - { url = "https://files.pythonhosted.org/packages/8c/7c/1517b0bbc2dbe784b563d6ab54f2ef88c890fdad77232c98ed490aa07132/uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0", size = 805476, upload-time = "2024-10-14T23:37:36.11Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ea/0bfae1aceb82a503f358d8d2fa126ca9dbdb2ba9c7866974faec1cb5875c/uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e", size = 3960855, upload-time = "2024-10-14T23:37:37.683Z" }, - { url = "https://files.pythonhosted.org/packages/8a/ca/0864176a649838b838f36d44bf31c451597ab363b60dc9e09c9630619d41/uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb", size = 3973185, upload-time = "2024-10-14T23:37:40.226Z" }, - { url = "https://files.pythonhosted.org/packages/30/bf/08ad29979a936d63787ba47a540de2132169f140d54aa25bc8c3df3e67f4/uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6", size = 3820256, upload-time = "2024-10-14T23:37:42.839Z" }, - { url = "https://files.pythonhosted.org/packages/da/e2/5cf6ef37e3daf2f06e651aae5ea108ad30df3cb269102678b61ebf1fdf42/uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d", size = 3937323, upload-time = "2024-10-14T23:37:45.337Z" }, - { url = "https://files.pythonhosted.org/packages/8c/4c/03f93178830dc7ce8b4cdee1d36770d2f5ebb6f3d37d354e061eefc73545/uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c", size = 1471284, upload-time = "2024-10-14T23:37:47.833Z" }, - { url = "https://files.pythonhosted.org/packages/43/3e/92c03f4d05e50f09251bd8b2b2b584a2a7f8fe600008bcc4523337abe676/uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2", size = 821349, upload-time = "2024-10-14T23:37:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/a6/ef/a02ec5da49909dbbfb1fd205a9a1ac4e88ea92dcae885e7c961847cd51e2/uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d", size = 4580089, upload-time = "2024-10-14T23:37:51.703Z" }, - { url = "https://files.pythonhosted.org/packages/06/a7/b4e6a19925c900be9f98bec0a75e6e8f79bb53bdeb891916609ab3958967/uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc", size = 4693770, upload-time = "2024-10-14T23:37:54.122Z" }, - { url = "https://files.pythonhosted.org/packages/ce/0c/f07435a18a4b94ce6bd0677d8319cd3de61f3a9eeb1e5f8ab4e8b5edfcb3/uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb", size = 4451321, upload-time = "2024-10-14T23:37:55.766Z" }, - { url = "https://files.pythonhosted.org/packages/8f/eb/f7032be105877bcf924709c97b1bf3b90255b4ec251f9340cef912559f28/uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f", size = 4659022, upload-time = "2024-10-14T23:37:58.195Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8d/2cbef610ca21539f0f36e2b34da49302029e7c9f09acef0b1c3b5839412b/uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281", size = 1468123, upload-time = "2024-10-14T23:38:00.688Z" }, - { url = "https://files.pythonhosted.org/packages/93/0d/b0038d5a469f94ed8f2b2fce2434a18396d8fbfb5da85a0a9781ebbdec14/uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af", size = 819325, upload-time = "2024-10-14T23:38:02.309Z" }, - { url = "https://files.pythonhosted.org/packages/50/94/0a687f39e78c4c1e02e3272c6b2ccdb4e0085fda3b8352fecd0410ccf915/uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6", size = 4582806, upload-time = "2024-10-14T23:38:04.711Z" }, - { url = "https://files.pythonhosted.org/packages/d2/19/f5b78616566ea68edd42aacaf645adbf71fbd83fc52281fba555dc27e3f1/uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816", size = 4701068, upload-time = "2024-10-14T23:38:06.385Z" }, - { url = "https://files.pythonhosted.org/packages/47/57/66f061ee118f413cd22a656de622925097170b9380b30091b78ea0c6ea75/uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc", size = 4454428, upload-time = "2024-10-14T23:38:08.416Z" }, - { url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018, upload-time = "2024-10-14T23:38:10.888Z" }, -] - -[[package]] -name = "virtualenv" -version = "20.32.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "distlib" }, - { name = "filelock" }, - { name = "platformdirs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a9/96/0834f30fa08dca3738614e6a9d42752b6420ee94e58971d702118f7cfd30/virtualenv-20.32.0.tar.gz", hash = "sha256:886bf75cadfdc964674e6e33eb74d787dff31ca314ceace03ca5810620f4ecf0", size = 6076970, upload-time = "2025-07-21T04:09:50.985Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/c6/f8f28009920a736d0df434b52e9feebfb4d702ba942f15338cb4a83eafc1/virtualenv-20.32.0-py3-none-any.whl", hash = "sha256:2c310aecb62e5aa1b06103ed7c2977b81e042695de2697d01017ff0f1034af56", size = 6057761, upload-time = "2025-07-21T04:09:48.059Z" }, -] - -[[package]] -name = "watchdog" -version = "6.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/56/90994d789c61df619bfc5ce2ecdabd5eeff564e1eb47512bd01b5e019569/watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26", size = 96390, upload-time = "2024-11-01T14:06:24.793Z" }, - { url = "https://files.pythonhosted.org/packages/55/46/9a67ee697342ddf3c6daa97e3a587a56d6c4052f881ed926a849fcf7371c/watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112", size = 88389, upload-time = "2024-11-01T14:06:27.112Z" }, - { url = "https://files.pythonhosted.org/packages/44/65/91b0985747c52064d8701e1075eb96f8c40a79df889e59a399453adfb882/watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3", size = 89020, upload-time = "2024-11-01T14:06:29.876Z" }, - { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393, upload-time = "2024-11-01T14:06:31.756Z" }, - { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392, upload-time = "2024-11-01T14:06:32.99Z" }, - { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019, upload-time = "2024-11-01T14:06:34.963Z" }, - { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, - { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, - { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, - { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, - { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, - { url = "https://files.pythonhosted.org/packages/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", size = 87902, upload-time = "2024-11-01T14:06:53.119Z" }, - { url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", size = 88380, upload-time = "2024-11-01T14:06:55.19Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, -] - -[[package]] -name = "watchfiles" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406, upload-time = "2025-06-15T19:06:59.42Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/dd/579d1dc57f0f895426a1211c4ef3b0cb37eb9e642bb04bdcd962b5df206a/watchfiles-1.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:27f30e14aa1c1e91cb653f03a63445739919aef84c8d2517997a83155e7a2fcc", size = 405757, upload-time = "2025-06-15T19:04:51.058Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a0/7a0318cd874393344d48c34d53b3dd419466adf59a29ba5b51c88dd18b86/watchfiles-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3366f56c272232860ab45c77c3ca7b74ee819c8e1f6f35a7125556b198bbc6df", size = 397511, upload-time = "2025-06-15T19:04:52.79Z" }, - { url = "https://files.pythonhosted.org/packages/06/be/503514656d0555ec2195f60d810eca29b938772e9bfb112d5cd5ad6f6a9e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8412eacef34cae2836d891836a7fff7b754d6bcac61f6c12ba5ca9bc7e427b68", size = 450739, upload-time = "2025-06-15T19:04:54.203Z" }, - { url = "https://files.pythonhosted.org/packages/4e/0d/a05dd9e5f136cdc29751816d0890d084ab99f8c17b86f25697288ca09bc7/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df670918eb7dd719642e05979fc84704af913d563fd17ed636f7c4783003fdcc", size = 458106, upload-time = "2025-06-15T19:04:55.607Z" }, - { url = "https://files.pythonhosted.org/packages/f1/fa/9cd16e4dfdb831072b7ac39e7bea986e52128526251038eb481effe9f48e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7642b9bc4827b5518ebdb3b82698ada8c14c7661ddec5fe719f3e56ccd13c97", size = 484264, upload-time = "2025-06-15T19:04:57.009Z" }, - { url = "https://files.pythonhosted.org/packages/32/04/1da8a637c7e2b70e750a0308e9c8e662ada0cca46211fa9ef24a23937e0b/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:199207b2d3eeaeb80ef4411875a6243d9ad8bc35b07fc42daa6b801cc39cc41c", size = 597612, upload-time = "2025-06-15T19:04:58.409Z" }, - { url = "https://files.pythonhosted.org/packages/30/01/109f2762e968d3e58c95731a206e5d7d2a7abaed4299dd8a94597250153c/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a479466da6db5c1e8754caee6c262cd373e6e6c363172d74394f4bff3d84d7b5", size = 477242, upload-time = "2025-06-15T19:04:59.786Z" }, - { url = "https://files.pythonhosted.org/packages/b5/b8/46f58cf4969d3b7bc3ca35a98e739fa4085b0657a1540ccc29a1a0bc016f/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:935f9edd022ec13e447e5723a7d14456c8af254544cefbc533f6dd276c9aa0d9", size = 453148, upload-time = "2025-06-15T19:05:01.103Z" }, - { url = "https://files.pythonhosted.org/packages/a5/cd/8267594263b1770f1eb76914940d7b2d03ee55eca212302329608208e061/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8076a5769d6bdf5f673a19d51da05fc79e2bbf25e9fe755c47595785c06a8c72", size = 626574, upload-time = "2025-06-15T19:05:02.582Z" }, - { url = "https://files.pythonhosted.org/packages/a1/2f/7f2722e85899bed337cba715723e19185e288ef361360718973f891805be/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86b1e28d4c37e89220e924305cd9f82866bb0ace666943a6e4196c5df4d58dcc", size = 624378, upload-time = "2025-06-15T19:05:03.719Z" }, - { url = "https://files.pythonhosted.org/packages/bf/20/64c88ec43d90a568234d021ab4b2a6f42a5230d772b987c3f9c00cc27b8b/watchfiles-1.1.0-cp310-cp310-win32.whl", hash = "sha256:d1caf40c1c657b27858f9774d5c0e232089bca9cb8ee17ce7478c6e9264d2587", size = 279829, upload-time = "2025-06-15T19:05:04.822Z" }, - { url = "https://files.pythonhosted.org/packages/39/5c/a9c1ed33de7af80935e4eac09570de679c6e21c07070aa99f74b4431f4d6/watchfiles-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a89c75a5b9bc329131115a409d0acc16e8da8dfd5867ba59f1dd66ae7ea8fa82", size = 292192, upload-time = "2025-06-15T19:05:06.348Z" }, - { url = "https://files.pythonhosted.org/packages/8b/78/7401154b78ab484ccaaeef970dc2af0cb88b5ba8a1b415383da444cdd8d3/watchfiles-1.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2", size = 405751, upload-time = "2025-06-15T19:05:07.679Z" }, - { url = "https://files.pythonhosted.org/packages/76/63/e6c3dbc1f78d001589b75e56a288c47723de28c580ad715eb116639152b5/watchfiles-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c", size = 397313, upload-time = "2025-06-15T19:05:08.764Z" }, - { url = "https://files.pythonhosted.org/packages/6c/a2/8afa359ff52e99af1632f90cbf359da46184207e893a5f179301b0c8d6df/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d", size = 450792, upload-time = "2025-06-15T19:05:09.869Z" }, - { url = "https://files.pythonhosted.org/packages/1d/bf/7446b401667f5c64972a57a0233be1104157fc3abf72c4ef2666c1bd09b2/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7", size = 458196, upload-time = "2025-06-15T19:05:11.91Z" }, - { url = "https://files.pythonhosted.org/packages/58/2f/501ddbdfa3fa874ea5597c77eeea3d413579c29af26c1091b08d0c792280/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c", size = 484788, upload-time = "2025-06-15T19:05:13.373Z" }, - { url = "https://files.pythonhosted.org/packages/61/1e/9c18eb2eb5c953c96bc0e5f626f0e53cfef4bd19bd50d71d1a049c63a575/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575", size = 597879, upload-time = "2025-06-15T19:05:14.725Z" }, - { url = "https://files.pythonhosted.org/packages/8b/6c/1467402e5185d89388b4486745af1e0325007af0017c3384cc786fff0542/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8", size = 477447, upload-time = "2025-06-15T19:05:15.775Z" }, - { url = "https://files.pythonhosted.org/packages/2b/a1/ec0a606bde4853d6c4a578f9391eeb3684a9aea736a8eb217e3e00aa89a1/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f", size = 453145, upload-time = "2025-06-15T19:05:17.17Z" }, - { url = "https://files.pythonhosted.org/packages/90/b9/ef6f0c247a6a35d689fc970dc7f6734f9257451aefb30def5d100d6246a5/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4", size = 626539, upload-time = "2025-06-15T19:05:18.557Z" }, - { url = "https://files.pythonhosted.org/packages/34/44/6ffda5537085106ff5aaa762b0d130ac6c75a08015dd1621376f708c94de/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d", size = 624472, upload-time = "2025-06-15T19:05:19.588Z" }, - { url = "https://files.pythonhosted.org/packages/c3/e3/71170985c48028fa3f0a50946916a14055e741db11c2e7bc2f3b61f4d0e3/watchfiles-1.1.0-cp311-cp311-win32.whl", hash = "sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2", size = 279348, upload-time = "2025-06-15T19:05:20.856Z" }, - { url = "https://files.pythonhosted.org/packages/89/1b/3e39c68b68a7a171070f81fc2561d23ce8d6859659406842a0e4bebf3bba/watchfiles-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12", size = 292607, upload-time = "2025-06-15T19:05:21.937Z" }, - { url = "https://files.pythonhosted.org/packages/61/9f/2973b7539f2bdb6ea86d2c87f70f615a71a1fc2dba2911795cea25968aea/watchfiles-1.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a", size = 285056, upload-time = "2025-06-15T19:05:23.12Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b8/858957045a38a4079203a33aaa7d23ea9269ca7761c8a074af3524fbb240/watchfiles-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179", size = 402339, upload-time = "2025-06-15T19:05:24.516Z" }, - { url = "https://files.pythonhosted.org/packages/80/28/98b222cca751ba68e88521fabd79a4fab64005fc5976ea49b53fa205d1fa/watchfiles-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5", size = 394409, upload-time = "2025-06-15T19:05:25.469Z" }, - { url = "https://files.pythonhosted.org/packages/86/50/dee79968566c03190677c26f7f47960aff738d32087087bdf63a5473e7df/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297", size = 450939, upload-time = "2025-06-15T19:05:26.494Z" }, - { url = "https://files.pythonhosted.org/packages/40/45/a7b56fb129700f3cfe2594a01aa38d033b92a33dddce86c8dfdfc1247b72/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0", size = 457270, upload-time = "2025-06-15T19:05:27.466Z" }, - { url = "https://files.pythonhosted.org/packages/b5/c8/fa5ef9476b1d02dc6b5e258f515fcaaecf559037edf8b6feffcbc097c4b8/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e", size = 483370, upload-time = "2025-06-15T19:05:28.548Z" }, - { url = "https://files.pythonhosted.org/packages/98/68/42cfcdd6533ec94f0a7aab83f759ec11280f70b11bfba0b0f885e298f9bd/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee", size = 598654, upload-time = "2025-06-15T19:05:29.997Z" }, - { url = "https://files.pythonhosted.org/packages/d3/74/b2a1544224118cc28df7e59008a929e711f9c68ce7d554e171b2dc531352/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd", size = 478667, upload-time = "2025-06-15T19:05:31.172Z" }, - { url = "https://files.pythonhosted.org/packages/8c/77/e3362fe308358dc9f8588102481e599c83e1b91c2ae843780a7ded939a35/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f", size = 452213, upload-time = "2025-06-15T19:05:32.299Z" }, - { url = "https://files.pythonhosted.org/packages/6e/17/c8f1a36540c9a1558d4faf08e909399e8133599fa359bf52ec8fcee5be6f/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4", size = 626718, upload-time = "2025-06-15T19:05:33.415Z" }, - { url = "https://files.pythonhosted.org/packages/26/45/fb599be38b4bd38032643783d7496a26a6f9ae05dea1a42e58229a20ac13/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f", size = 623098, upload-time = "2025-06-15T19:05:34.534Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e7/fdf40e038475498e160cd167333c946e45d8563ae4dd65caf757e9ffe6b4/watchfiles-1.1.0-cp312-cp312-win32.whl", hash = "sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd", size = 279209, upload-time = "2025-06-15T19:05:35.577Z" }, - { url = "https://files.pythonhosted.org/packages/3f/d3/3ae9d5124ec75143bdf088d436cba39812122edc47709cd2caafeac3266f/watchfiles-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47", size = 292786, upload-time = "2025-06-15T19:05:36.559Z" }, - { url = "https://files.pythonhosted.org/packages/26/2f/7dd4fc8b5f2b34b545e19629b4a018bfb1de23b3a496766a2c1165ca890d/watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6", size = 284343, upload-time = "2025-06-15T19:05:37.5Z" }, - { url = "https://files.pythonhosted.org/packages/d3/42/fae874df96595556a9089ade83be34a2e04f0f11eb53a8dbf8a8a5e562b4/watchfiles-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30", size = 402004, upload-time = "2025-06-15T19:05:38.499Z" }, - { url = "https://files.pythonhosted.org/packages/fa/55/a77e533e59c3003d9803c09c44c3651224067cbe7fb5d574ddbaa31e11ca/watchfiles-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a", size = 393671, upload-time = "2025-06-15T19:05:39.52Z" }, - { url = "https://files.pythonhosted.org/packages/05/68/b0afb3f79c8e832e6571022611adbdc36e35a44e14f129ba09709aa4bb7a/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc", size = 449772, upload-time = "2025-06-15T19:05:40.897Z" }, - { url = "https://files.pythonhosted.org/packages/ff/05/46dd1f6879bc40e1e74c6c39a1b9ab9e790bf1f5a2fe6c08b463d9a807f4/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b", size = 456789, upload-time = "2025-06-15T19:05:42.045Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ca/0eeb2c06227ca7f12e50a47a3679df0cd1ba487ea19cf844a905920f8e95/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895", size = 482551, upload-time = "2025-06-15T19:05:43.781Z" }, - { url = "https://files.pythonhosted.org/packages/31/47/2cecbd8694095647406645f822781008cc524320466ea393f55fe70eed3b/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a", size = 597420, upload-time = "2025-06-15T19:05:45.244Z" }, - { url = "https://files.pythonhosted.org/packages/d9/7e/82abc4240e0806846548559d70f0b1a6dfdca75c1b4f9fa62b504ae9b083/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b", size = 477950, upload-time = "2025-06-15T19:05:46.332Z" }, - { url = "https://files.pythonhosted.org/packages/25/0d/4d564798a49bf5482a4fa9416dea6b6c0733a3b5700cb8a5a503c4b15853/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c", size = 451706, upload-time = "2025-06-15T19:05:47.459Z" }, - { url = "https://files.pythonhosted.org/packages/81/b5/5516cf46b033192d544102ea07c65b6f770f10ed1d0a6d388f5d3874f6e4/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b", size = 625814, upload-time = "2025-06-15T19:05:48.654Z" }, - { url = "https://files.pythonhosted.org/packages/0c/dd/7c1331f902f30669ac3e754680b6edb9a0dd06dea5438e61128111fadd2c/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb", size = 622820, upload-time = "2025-06-15T19:05:50.088Z" }, - { url = "https://files.pythonhosted.org/packages/1b/14/36d7a8e27cd128d7b1009e7715a7c02f6c131be9d4ce1e5c3b73d0e342d8/watchfiles-1.1.0-cp313-cp313-win32.whl", hash = "sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9", size = 279194, upload-time = "2025-06-15T19:05:51.186Z" }, - { url = "https://files.pythonhosted.org/packages/25/41/2dd88054b849aa546dbeef5696019c58f8e0774f4d1c42123273304cdb2e/watchfiles-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7", size = 292349, upload-time = "2025-06-15T19:05:52.201Z" }, - { url = "https://files.pythonhosted.org/packages/c8/cf/421d659de88285eb13941cf11a81f875c176f76a6d99342599be88e08d03/watchfiles-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5", size = 283836, upload-time = "2025-06-15T19:05:53.265Z" }, - { url = "https://files.pythonhosted.org/packages/45/10/6faf6858d527e3599cc50ec9fcae73590fbddc1420bd4fdccfebffeedbc6/watchfiles-1.1.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1", size = 400343, upload-time = "2025-06-15T19:05:54.252Z" }, - { url = "https://files.pythonhosted.org/packages/03/20/5cb7d3966f5e8c718006d0e97dfe379a82f16fecd3caa7810f634412047a/watchfiles-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339", size = 392916, upload-time = "2025-06-15T19:05:55.264Z" }, - { url = "https://files.pythonhosted.org/packages/8c/07/d8f1176328fa9e9581b6f120b017e286d2a2d22ae3f554efd9515c8e1b49/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633", size = 449582, upload-time = "2025-06-15T19:05:56.317Z" }, - { url = "https://files.pythonhosted.org/packages/66/e8/80a14a453cf6038e81d072a86c05276692a1826471fef91df7537dba8b46/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011", size = 456752, upload-time = "2025-06-15T19:05:57.359Z" }, - { url = "https://files.pythonhosted.org/packages/5a/25/0853b3fe0e3c2f5af9ea60eb2e781eade939760239a72c2d38fc4cc335f6/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670", size = 481436, upload-time = "2025-06-15T19:05:58.447Z" }, - { url = "https://files.pythonhosted.org/packages/fe/9e/4af0056c258b861fbb29dcb36258de1e2b857be4a9509e6298abcf31e5c9/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf", size = 596016, upload-time = "2025-06-15T19:05:59.59Z" }, - { url = "https://files.pythonhosted.org/packages/c5/fa/95d604b58aa375e781daf350897aaaa089cff59d84147e9ccff2447c8294/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4", size = 476727, upload-time = "2025-06-15T19:06:01.086Z" }, - { url = "https://files.pythonhosted.org/packages/65/95/fe479b2664f19be4cf5ceeb21be05afd491d95f142e72d26a42f41b7c4f8/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20", size = 451864, upload-time = "2025-06-15T19:06:02.144Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8a/3c4af14b93a15ce55901cd7a92e1a4701910f1768c78fb30f61d2b79785b/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef", size = 625626, upload-time = "2025-06-15T19:06:03.578Z" }, - { url = "https://files.pythonhosted.org/packages/da/f5/cf6aa047d4d9e128f4b7cde615236a915673775ef171ff85971d698f3c2c/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb", size = 622744, upload-time = "2025-06-15T19:06:05.066Z" }, - { url = "https://files.pythonhosted.org/packages/2c/00/70f75c47f05dea6fd30df90f047765f6fc2d6eb8b5a3921379b0b04defa2/watchfiles-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297", size = 402114, upload-time = "2025-06-15T19:06:06.186Z" }, - { url = "https://files.pythonhosted.org/packages/53/03/acd69c48db4a1ed1de26b349d94077cca2238ff98fd64393f3e97484cae6/watchfiles-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018", size = 393879, upload-time = "2025-06-15T19:06:07.369Z" }, - { url = "https://files.pythonhosted.org/packages/2f/c8/a9a2a6f9c8baa4eceae5887fecd421e1b7ce86802bcfc8b6a942e2add834/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0", size = 450026, upload-time = "2025-06-15T19:06:08.476Z" }, - { url = "https://files.pythonhosted.org/packages/fe/51/d572260d98388e6e2b967425c985e07d47ee6f62e6455cefb46a6e06eda5/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12", size = 457917, upload-time = "2025-06-15T19:06:09.988Z" }, - { url = "https://files.pythonhosted.org/packages/c6/2d/4258e52917bf9f12909b6ec314ff9636276f3542f9d3807d143f27309104/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb", size = 483602, upload-time = "2025-06-15T19:06:11.088Z" }, - { url = "https://files.pythonhosted.org/packages/84/99/bee17a5f341a4345fe7b7972a475809af9e528deba056f8963d61ea49f75/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77", size = 596758, upload-time = "2025-06-15T19:06:12.197Z" }, - { url = "https://files.pythonhosted.org/packages/40/76/e4bec1d59b25b89d2b0716b41b461ed655a9a53c60dc78ad5771fda5b3e6/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92", size = 477601, upload-time = "2025-06-15T19:06:13.391Z" }, - { url = "https://files.pythonhosted.org/packages/1f/fa/a514292956f4a9ce3c567ec0c13cce427c158e9f272062685a8a727d08fc/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e", size = 451936, upload-time = "2025-06-15T19:06:14.656Z" }, - { url = "https://files.pythonhosted.org/packages/32/5d/c3bf927ec3bbeb4566984eba8dd7a8eb69569400f5509904545576741f88/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b", size = 626243, upload-time = "2025-06-15T19:06:16.232Z" }, - { url = "https://files.pythonhosted.org/packages/e6/65/6e12c042f1a68c556802a84d54bb06d35577c81e29fba14019562479159c/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259", size = 623073, upload-time = "2025-06-15T19:06:17.457Z" }, - { url = "https://files.pythonhosted.org/packages/89/ab/7f79d9bf57329e7cbb0a6fd4c7bd7d0cee1e4a8ef0041459f5409da3506c/watchfiles-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f", size = 400872, upload-time = "2025-06-15T19:06:18.57Z" }, - { url = "https://files.pythonhosted.org/packages/df/d5/3f7bf9912798e9e6c516094db6b8932df53b223660c781ee37607030b6d3/watchfiles-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e", size = 392877, upload-time = "2025-06-15T19:06:19.55Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c5/54ec7601a2798604e01c75294770dbee8150e81c6e471445d7601610b495/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa", size = 449645, upload-time = "2025-06-15T19:06:20.66Z" }, - { url = "https://files.pythonhosted.org/packages/0a/04/c2f44afc3b2fce21ca0b7802cbd37ed90a29874f96069ed30a36dfe57c2b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8", size = 457424, upload-time = "2025-06-15T19:06:21.712Z" }, - { url = "https://files.pythonhosted.org/packages/9f/b0/eec32cb6c14d248095261a04f290636da3df3119d4040ef91a4a50b29fa5/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f", size = 481584, upload-time = "2025-06-15T19:06:22.777Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e2/ca4bb71c68a937d7145aa25709e4f5d68eb7698a25ce266e84b55d591bbd/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e", size = 596675, upload-time = "2025-06-15T19:06:24.226Z" }, - { url = "https://files.pythonhosted.org/packages/a1/dd/b0e4b7fb5acf783816bc950180a6cd7c6c1d2cf7e9372c0ea634e722712b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb", size = 477363, upload-time = "2025-06-15T19:06:25.42Z" }, - { url = "https://files.pythonhosted.org/packages/69/c4/088825b75489cb5b6a761a4542645718893d395d8c530b38734f19da44d2/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147", size = 452240, upload-time = "2025-06-15T19:06:26.552Z" }, - { url = "https://files.pythonhosted.org/packages/10/8c/22b074814970eeef43b7c44df98c3e9667c1f7bf5b83e0ff0201b0bd43f9/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8", size = 625607, upload-time = "2025-06-15T19:06:27.606Z" }, - { url = "https://files.pythonhosted.org/packages/32/fa/a4f5c2046385492b2273213ef815bf71a0d4c1943b784fb904e184e30201/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db", size = 623315, upload-time = "2025-06-15T19:06:29.076Z" }, - { url = "https://files.pythonhosted.org/packages/be/7c/a3d7c55cfa377c2f62c4ae3c6502b997186bc5e38156bafcb9b653de9a6d/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a6fd40bbb50d24976eb275ccb55cd1951dfb63dbc27cae3066a6ca5f4beabd5", size = 406748, upload-time = "2025-06-15T19:06:44.2Z" }, - { url = "https://files.pythonhosted.org/packages/38/d0/c46f1b2c0ca47f3667b144de6f0515f6d1c670d72f2ca29861cac78abaa1/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f811079d2f9795b5d48b55a37aa7773680a5659afe34b54cc1d86590a51507d", size = 398801, upload-time = "2025-06-15T19:06:45.774Z" }, - { url = "https://files.pythonhosted.org/packages/70/9c/9a6a42e97f92eeed77c3485a43ea96723900aefa3ac739a8c73f4bff2cd7/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2726d7bfd9f76158c84c10a409b77a320426540df8c35be172444394b17f7ea", size = 451528, upload-time = "2025-06-15T19:06:46.791Z" }, - { url = "https://files.pythonhosted.org/packages/51/7b/98c7f4f7ce7ff03023cf971cd84a3ee3b790021ae7584ffffa0eb2554b96/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df32d59cb9780f66d165a9a7a26f19df2c7d24e3bd58713108b41d0ff4f929c6", size = 454095, upload-time = "2025-06-15T19:06:48.211Z" }, - { url = "https://files.pythonhosted.org/packages/8c/6b/686dcf5d3525ad17b384fd94708e95193529b460a1b7bf40851f1328ec6e/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3", size = 406910, upload-time = "2025-06-15T19:06:49.335Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d3/71c2dcf81dc1edcf8af9f4d8d63b1316fb0a2dd90cbfd427e8d9dd584a90/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c", size = 398816, upload-time = "2025-06-15T19:06:50.433Z" }, - { url = "https://files.pythonhosted.org/packages/b8/fa/12269467b2fc006f8fce4cd6c3acfa77491dd0777d2a747415f28ccc8c60/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432", size = 451584, upload-time = "2025-06-15T19:06:51.834Z" }, - { url = "https://files.pythonhosted.org/packages/bd/d3/254cea30f918f489db09d6a8435a7de7047f8cb68584477a515f160541d6/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792", size = 454009, upload-time = "2025-06-15T19:06:52.896Z" }, -] - -[[package]] -name = "wcwidth" -version = "0.2.13" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" }, -] - -[[package]] -name = "websocket-client" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648, upload-time = "2024-04-23T22:16:16.976Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826, upload-time = "2024-04-23T22:16:14.422Z" }, -] - -[[package]] -name = "websockets" -version = "15.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423, upload-time = "2025-03-05T20:01:35.363Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080, upload-time = "2025-03-05T20:01:37.304Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329, upload-time = "2025-03-05T20:01:39.668Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312, upload-time = "2025-03-05T20:01:41.815Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319, upload-time = "2025-03-05T20:01:43.967Z" }, - { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631, upload-time = "2025-03-05T20:01:46.104Z" }, - { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016, upload-time = "2025-03-05T20:01:47.603Z" }, - { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426, upload-time = "2025-03-05T20:01:48.949Z" }, - { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360, upload-time = "2025-03-05T20:01:50.938Z" }, - { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388, upload-time = "2025-03-05T20:01:52.213Z" }, - { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830, upload-time = "2025-03-05T20:01:53.922Z" }, - { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" }, - { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" }, - { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" }, - { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" }, - { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" }, - { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" }, - { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" }, - { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" }, - { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" }, - { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, - { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, - { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, - { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, - { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, - { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, - { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, - { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, - { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, - { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, - { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, - { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, - { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, - { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, - { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, - { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, - { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109, upload-time = "2025-03-05T20:03:17.769Z" }, - { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343, upload-time = "2025-03-05T20:03:19.094Z" }, - { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599, upload-time = "2025-03-05T20:03:21.1Z" }, - { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207, upload-time = "2025-03-05T20:03:23.221Z" }, - { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155, upload-time = "2025-03-05T20:03:25.321Z" }, - { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884, upload-time = "2025-03-05T20:03:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, -] - -[[package]] -name = "win32-setctime" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, -] - -[[package]] -name = "yarl" -version = "1.20.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "multidict" }, - { name = "propcache" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3c/fb/efaa23fa4e45537b827620f04cf8f3cd658b76642205162e072703a5b963/yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac", size = 186428, upload-time = "2025-06-10T00:46:09.923Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/65/7fed0d774abf47487c64be14e9223749468922817b5e8792b8a64792a1bb/yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4", size = 132910, upload-time = "2025-06-10T00:42:31.108Z" }, - { url = "https://files.pythonhosted.org/packages/8a/7b/988f55a52da99df9e56dc733b8e4e5a6ae2090081dc2754fc8fd34e60aa0/yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a", size = 90644, upload-time = "2025-06-10T00:42:33.851Z" }, - { url = "https://files.pythonhosted.org/packages/f7/de/30d98f03e95d30c7e3cc093759982d038c8833ec2451001d45ef4854edc1/yarl-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c869f2651cc77465f6cd01d938d91a11d9ea5d798738c1dc077f3de0b5e5fed", size = 89322, upload-time = "2025-06-10T00:42:35.688Z" }, - { url = "https://files.pythonhosted.org/packages/e0/7a/f2f314f5ebfe9200724b0b748de2186b927acb334cf964fd312eb86fc286/yarl-1.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62915e6688eb4d180d93840cda4110995ad50c459bf931b8b3775b37c264af1e", size = 323786, upload-time = "2025-06-10T00:42:37.817Z" }, - { url = "https://files.pythonhosted.org/packages/15/3f/718d26f189db96d993d14b984ce91de52e76309d0fd1d4296f34039856aa/yarl-1.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:41ebd28167bc6af8abb97fec1a399f412eec5fd61a3ccbe2305a18b84fb4ca73", size = 319627, upload-time = "2025-06-10T00:42:39.937Z" }, - { url = "https://files.pythonhosted.org/packages/a5/76/8fcfbf5fa2369157b9898962a4a7d96764b287b085b5b3d9ffae69cdefd1/yarl-1.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21242b4288a6d56f04ea193adde174b7e347ac46ce6bc84989ff7c1b1ecea84e", size = 339149, upload-time = "2025-06-10T00:42:42.627Z" }, - { url = "https://files.pythonhosted.org/packages/3c/95/d7fc301cc4661785967acc04f54a4a42d5124905e27db27bb578aac49b5c/yarl-1.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea21cdae6c7eb02ba02a475f37463abfe0a01f5d7200121b03e605d6a0439f8", size = 333327, upload-time = "2025-06-10T00:42:44.842Z" }, - { url = "https://files.pythonhosted.org/packages/65/94/e21269718349582eee81efc5c1c08ee71c816bfc1585b77d0ec3f58089eb/yarl-1.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f8a891e4a22a89f5dde7862994485e19db246b70bb288d3ce73a34422e55b23", size = 326054, upload-time = "2025-06-10T00:42:47.149Z" }, - { url = "https://files.pythonhosted.org/packages/32/ae/8616d1f07853704523519f6131d21f092e567c5af93de7e3e94b38d7f065/yarl-1.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd803820d44c8853a109a34e3660e5a61beae12970da479cf44aa2954019bf70", size = 315035, upload-time = "2025-06-10T00:42:48.852Z" }, - { url = "https://files.pythonhosted.org/packages/48/aa/0ace06280861ef055855333707db5e49c6e3a08840a7ce62682259d0a6c0/yarl-1.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b982fa7f74c80d5c0c7b5b38f908971e513380a10fecea528091405f519b9ebb", size = 338962, upload-time = "2025-06-10T00:42:51.024Z" }, - { url = "https://files.pythonhosted.org/packages/20/52/1e9d0e6916f45a8fb50e6844f01cb34692455f1acd548606cbda8134cd1e/yarl-1.20.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:33f29ecfe0330c570d997bcf1afd304377f2e48f61447f37e846a6058a4d33b2", size = 335399, upload-time = "2025-06-10T00:42:53.007Z" }, - { url = "https://files.pythonhosted.org/packages/f2/65/60452df742952c630e82f394cd409de10610481d9043aa14c61bf846b7b1/yarl-1.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:835ab2cfc74d5eb4a6a528c57f05688099da41cf4957cf08cad38647e4a83b30", size = 338649, upload-time = "2025-06-10T00:42:54.964Z" }, - { url = "https://files.pythonhosted.org/packages/7b/f5/6cd4ff38dcde57a70f23719a838665ee17079640c77087404c3d34da6727/yarl-1.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46b5e0ccf1943a9a6e766b2c2b8c732c55b34e28be57d8daa2b3c1d1d4009309", size = 358563, upload-time = "2025-06-10T00:42:57.28Z" }, - { url = "https://files.pythonhosted.org/packages/d1/90/c42eefd79d0d8222cb3227bdd51b640c0c1d0aa33fe4cc86c36eccba77d3/yarl-1.20.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:df47c55f7d74127d1b11251fe6397d84afdde0d53b90bedb46a23c0e534f9d24", size = 357609, upload-time = "2025-06-10T00:42:59.055Z" }, - { url = "https://files.pythonhosted.org/packages/03/c8/cea6b232cb4617514232e0f8a718153a95b5d82b5290711b201545825532/yarl-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76d12524d05841276b0e22573f28d5fbcb67589836772ae9244d90dd7d66aa13", size = 350224, upload-time = "2025-06-10T00:43:01.248Z" }, - { url = "https://files.pythonhosted.org/packages/ce/a3/eaa0ab9712f1f3d01faf43cf6f1f7210ce4ea4a7e9b28b489a2261ca8db9/yarl-1.20.1-cp310-cp310-win32.whl", hash = "sha256:6c4fbf6b02d70e512d7ade4b1f998f237137f1417ab07ec06358ea04f69134f8", size = 81753, upload-time = "2025-06-10T00:43:03.486Z" }, - { url = "https://files.pythonhosted.org/packages/8f/34/e4abde70a9256465fe31c88ed02c3f8502b7b5dead693a4f350a06413f28/yarl-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef6c4d69554d44b7f9d923245f8ad9a707d971e6209d51279196d8e8fe1ae16", size = 86817, upload-time = "2025-06-10T00:43:05.231Z" }, - { url = "https://files.pythonhosted.org/packages/b1/18/893b50efc2350e47a874c5c2d67e55a0ea5df91186b2a6f5ac52eff887cd/yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e", size = 133833, upload-time = "2025-06-10T00:43:07.393Z" }, - { url = "https://files.pythonhosted.org/packages/89/ed/b8773448030e6fc47fa797f099ab9eab151a43a25717f9ac043844ad5ea3/yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b", size = 91070, upload-time = "2025-06-10T00:43:09.538Z" }, - { url = "https://files.pythonhosted.org/packages/e3/e3/409bd17b1e42619bf69f60e4f031ce1ccb29bd7380117a55529e76933464/yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b", size = 89818, upload-time = "2025-06-10T00:43:11.575Z" }, - { url = "https://files.pythonhosted.org/packages/f8/77/64d8431a4d77c856eb2d82aa3de2ad6741365245a29b3a9543cd598ed8c5/yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4", size = 347003, upload-time = "2025-06-10T00:43:14.088Z" }, - { url = "https://files.pythonhosted.org/packages/8d/d2/0c7e4def093dcef0bd9fa22d4d24b023788b0a33b8d0088b51aa51e21e99/yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1", size = 336537, upload-time = "2025-06-10T00:43:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/f0/f3/fc514f4b2cf02cb59d10cbfe228691d25929ce8f72a38db07d3febc3f706/yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833", size = 362358, upload-time = "2025-06-10T00:43:18.704Z" }, - { url = "https://files.pythonhosted.org/packages/ea/6d/a313ac8d8391381ff9006ac05f1d4331cee3b1efaa833a53d12253733255/yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d", size = 357362, upload-time = "2025-06-10T00:43:20.888Z" }, - { url = "https://files.pythonhosted.org/packages/00/70/8f78a95d6935a70263d46caa3dd18e1f223cf2f2ff2037baa01a22bc5b22/yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8", size = 348979, upload-time = "2025-06-10T00:43:23.169Z" }, - { url = "https://files.pythonhosted.org/packages/cb/05/42773027968968f4f15143553970ee36ead27038d627f457cc44bbbeecf3/yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf", size = 337274, upload-time = "2025-06-10T00:43:27.111Z" }, - { url = "https://files.pythonhosted.org/packages/05/be/665634aa196954156741ea591d2f946f1b78ceee8bb8f28488bf28c0dd62/yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e", size = 363294, upload-time = "2025-06-10T00:43:28.96Z" }, - { url = "https://files.pythonhosted.org/packages/eb/90/73448401d36fa4e210ece5579895731f190d5119c4b66b43b52182e88cd5/yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389", size = 358169, upload-time = "2025-06-10T00:43:30.701Z" }, - { url = "https://files.pythonhosted.org/packages/c3/b0/fce922d46dc1eb43c811f1889f7daa6001b27a4005587e94878570300881/yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f", size = 362776, upload-time = "2025-06-10T00:43:32.51Z" }, - { url = "https://files.pythonhosted.org/packages/f1/0d/b172628fce039dae8977fd22caeff3eeebffd52e86060413f5673767c427/yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845", size = 381341, upload-time = "2025-06-10T00:43:34.543Z" }, - { url = "https://files.pythonhosted.org/packages/6b/9b/5b886d7671f4580209e855974fe1cecec409aa4a89ea58b8f0560dc529b1/yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1", size = 379988, upload-time = "2025-06-10T00:43:36.489Z" }, - { url = "https://files.pythonhosted.org/packages/73/be/75ef5fd0fcd8f083a5d13f78fd3f009528132a1f2a1d7c925c39fa20aa79/yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e", size = 371113, upload-time = "2025-06-10T00:43:38.592Z" }, - { url = "https://files.pythonhosted.org/packages/50/4f/62faab3b479dfdcb741fe9e3f0323e2a7d5cd1ab2edc73221d57ad4834b2/yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773", size = 81485, upload-time = "2025-06-10T00:43:41.038Z" }, - { url = "https://files.pythonhosted.org/packages/f0/09/d9c7942f8f05c32ec72cd5c8e041c8b29b5807328b68b4801ff2511d4d5e/yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e", size = 86686, upload-time = "2025-06-10T00:43:42.692Z" }, - { url = "https://files.pythonhosted.org/packages/5f/9a/cb7fad7d73c69f296eda6815e4a2c7ed53fc70c2f136479a91c8e5fbdb6d/yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9", size = 133667, upload-time = "2025-06-10T00:43:44.369Z" }, - { url = "https://files.pythonhosted.org/packages/67/38/688577a1cb1e656e3971fb66a3492501c5a5df56d99722e57c98249e5b8a/yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a", size = 91025, upload-time = "2025-06-10T00:43:46.295Z" }, - { url = "https://files.pythonhosted.org/packages/50/ec/72991ae51febeb11a42813fc259f0d4c8e0507f2b74b5514618d8b640365/yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2", size = 89709, upload-time = "2025-06-10T00:43:48.22Z" }, - { url = "https://files.pythonhosted.org/packages/99/da/4d798025490e89426e9f976702e5f9482005c548c579bdae792a4c37769e/yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee", size = 352287, upload-time = "2025-06-10T00:43:49.924Z" }, - { url = "https://files.pythonhosted.org/packages/1a/26/54a15c6a567aac1c61b18aa0f4b8aa2e285a52d547d1be8bf48abe2b3991/yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819", size = 345429, upload-time = "2025-06-10T00:43:51.7Z" }, - { url = "https://files.pythonhosted.org/packages/d6/95/9dcf2386cb875b234353b93ec43e40219e14900e046bf6ac118f94b1e353/yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16", size = 365429, upload-time = "2025-06-10T00:43:53.494Z" }, - { url = "https://files.pythonhosted.org/packages/91/b2/33a8750f6a4bc224242a635f5f2cff6d6ad5ba651f6edcccf721992c21a0/yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6", size = 363862, upload-time = "2025-06-10T00:43:55.766Z" }, - { url = "https://files.pythonhosted.org/packages/98/28/3ab7acc5b51f4434b181b0cee8f1f4b77a65919700a355fb3617f9488874/yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd", size = 355616, upload-time = "2025-06-10T00:43:58.056Z" }, - { url = "https://files.pythonhosted.org/packages/36/a3/f666894aa947a371724ec7cd2e5daa78ee8a777b21509b4252dd7bd15e29/yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a", size = 339954, upload-time = "2025-06-10T00:43:59.773Z" }, - { url = "https://files.pythonhosted.org/packages/f1/81/5f466427e09773c04219d3450d7a1256138a010b6c9f0af2d48565e9ad13/yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38", size = 365575, upload-time = "2025-06-10T00:44:02.051Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e3/e4b0ad8403e97e6c9972dd587388940a032f030ebec196ab81a3b8e94d31/yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef", size = 365061, upload-time = "2025-06-10T00:44:04.196Z" }, - { url = "https://files.pythonhosted.org/packages/ac/99/b8a142e79eb86c926f9f06452eb13ecb1bb5713bd01dc0038faf5452e544/yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f", size = 364142, upload-time = "2025-06-10T00:44:06.527Z" }, - { url = "https://files.pythonhosted.org/packages/34/f2/08ed34a4a506d82a1a3e5bab99ccd930a040f9b6449e9fd050320e45845c/yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8", size = 381894, upload-time = "2025-06-10T00:44:08.379Z" }, - { url = "https://files.pythonhosted.org/packages/92/f8/9a3fbf0968eac704f681726eff595dce9b49c8a25cd92bf83df209668285/yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a", size = 383378, upload-time = "2025-06-10T00:44:10.51Z" }, - { url = "https://files.pythonhosted.org/packages/af/85/9363f77bdfa1e4d690957cd39d192c4cacd1c58965df0470a4905253b54f/yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004", size = 374069, upload-time = "2025-06-10T00:44:12.834Z" }, - { url = "https://files.pythonhosted.org/packages/35/99/9918c8739ba271dcd935400cff8b32e3cd319eaf02fcd023d5dcd487a7c8/yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5", size = 81249, upload-time = "2025-06-10T00:44:14.731Z" }, - { url = "https://files.pythonhosted.org/packages/eb/83/5d9092950565481b413b31a23e75dd3418ff0a277d6e0abf3729d4d1ce25/yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698", size = 86710, upload-time = "2025-06-10T00:44:16.716Z" }, - { url = "https://files.pythonhosted.org/packages/8a/e1/2411b6d7f769a07687acee88a062af5833cf1966b7266f3d8dfb3d3dc7d3/yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a", size = 131811, upload-time = "2025-06-10T00:44:18.933Z" }, - { url = "https://files.pythonhosted.org/packages/b2/27/584394e1cb76fb771371770eccad35de400e7b434ce3142c2dd27392c968/yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3", size = 90078, upload-time = "2025-06-10T00:44:20.635Z" }, - { url = "https://files.pythonhosted.org/packages/bf/9a/3246ae92d4049099f52d9b0fe3486e3b500e29b7ea872d0f152966fc209d/yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7", size = 88748, upload-time = "2025-06-10T00:44:22.34Z" }, - { url = "https://files.pythonhosted.org/packages/a3/25/35afe384e31115a1a801fbcf84012d7a066d89035befae7c5d4284df1e03/yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691", size = 349595, upload-time = "2025-06-10T00:44:24.314Z" }, - { url = "https://files.pythonhosted.org/packages/28/2d/8aca6cb2cabc8f12efcb82749b9cefecbccfc7b0384e56cd71058ccee433/yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31", size = 342616, upload-time = "2025-06-10T00:44:26.167Z" }, - { url = "https://files.pythonhosted.org/packages/0b/e9/1312633d16b31acf0098d30440ca855e3492d66623dafb8e25b03d00c3da/yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28", size = 361324, upload-time = "2025-06-10T00:44:27.915Z" }, - { url = "https://files.pythonhosted.org/packages/bc/a0/688cc99463f12f7669eec7c8acc71ef56a1521b99eab7cd3abb75af887b0/yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653", size = 359676, upload-time = "2025-06-10T00:44:30.041Z" }, - { url = "https://files.pythonhosted.org/packages/af/44/46407d7f7a56e9a85a4c207724c9f2c545c060380718eea9088f222ba697/yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5", size = 352614, upload-time = "2025-06-10T00:44:32.171Z" }, - { url = "https://files.pythonhosted.org/packages/b1/91/31163295e82b8d5485d31d9cf7754d973d41915cadce070491778d9c9825/yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02", size = 336766, upload-time = "2025-06-10T00:44:34.494Z" }, - { url = "https://files.pythonhosted.org/packages/b4/8e/c41a5bc482121f51c083c4c2bcd16b9e01e1cf8729e380273a952513a21f/yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53", size = 364615, upload-time = "2025-06-10T00:44:36.856Z" }, - { url = "https://files.pythonhosted.org/packages/e3/5b/61a3b054238d33d70ea06ebba7e58597891b71c699e247df35cc984ab393/yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc", size = 360982, upload-time = "2025-06-10T00:44:39.141Z" }, - { url = "https://files.pythonhosted.org/packages/df/a3/6a72fb83f8d478cb201d14927bc8040af901811a88e0ff2da7842dd0ed19/yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04", size = 369792, upload-time = "2025-06-10T00:44:40.934Z" }, - { url = "https://files.pythonhosted.org/packages/7c/af/4cc3c36dfc7c077f8dedb561eb21f69e1e9f2456b91b593882b0b18c19dc/yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4", size = 382049, upload-time = "2025-06-10T00:44:42.854Z" }, - { url = "https://files.pythonhosted.org/packages/19/3a/e54e2c4752160115183a66dc9ee75a153f81f3ab2ba4bf79c3c53b33de34/yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b", size = 384774, upload-time = "2025-06-10T00:44:45.275Z" }, - { url = "https://files.pythonhosted.org/packages/9c/20/200ae86dabfca89060ec6447649f219b4cbd94531e425e50d57e5f5ac330/yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1", size = 374252, upload-time = "2025-06-10T00:44:47.31Z" }, - { url = "https://files.pythonhosted.org/packages/83/75/11ee332f2f516b3d094e89448da73d557687f7d137d5a0f48c40ff211487/yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7", size = 81198, upload-time = "2025-06-10T00:44:49.164Z" }, - { url = "https://files.pythonhosted.org/packages/ba/ba/39b1ecbf51620b40ab402b0fc817f0ff750f6d92712b44689c2c215be89d/yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c", size = 86346, upload-time = "2025-06-10T00:44:51.182Z" }, - { url = "https://files.pythonhosted.org/packages/43/c7/669c52519dca4c95153c8ad96dd123c79f354a376346b198f438e56ffeb4/yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d", size = 138826, upload-time = "2025-06-10T00:44:52.883Z" }, - { url = "https://files.pythonhosted.org/packages/6a/42/fc0053719b44f6ad04a75d7f05e0e9674d45ef62f2d9ad2c1163e5c05827/yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf", size = 93217, upload-time = "2025-06-10T00:44:54.658Z" }, - { url = "https://files.pythonhosted.org/packages/4f/7f/fa59c4c27e2a076bba0d959386e26eba77eb52ea4a0aac48e3515c186b4c/yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3", size = 92700, upload-time = "2025-06-10T00:44:56.784Z" }, - { url = "https://files.pythonhosted.org/packages/2f/d4/062b2f48e7c93481e88eff97a6312dca15ea200e959f23e96d8ab898c5b8/yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d", size = 347644, upload-time = "2025-06-10T00:44:59.071Z" }, - { url = "https://files.pythonhosted.org/packages/89/47/78b7f40d13c8f62b499cc702fdf69e090455518ae544c00a3bf4afc9fc77/yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c", size = 323452, upload-time = "2025-06-10T00:45:01.605Z" }, - { url = "https://files.pythonhosted.org/packages/eb/2b/490d3b2dc66f52987d4ee0d3090a147ea67732ce6b4d61e362c1846d0d32/yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1", size = 346378, upload-time = "2025-06-10T00:45:03.946Z" }, - { url = "https://files.pythonhosted.org/packages/66/ad/775da9c8a94ce925d1537f939a4f17d782efef1f973039d821cbe4bcc211/yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce", size = 353261, upload-time = "2025-06-10T00:45:05.992Z" }, - { url = "https://files.pythonhosted.org/packages/4b/23/0ed0922b47a4f5c6eb9065d5ff1e459747226ddce5c6a4c111e728c9f701/yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3", size = 335987, upload-time = "2025-06-10T00:45:08.227Z" }, - { url = "https://files.pythonhosted.org/packages/3e/49/bc728a7fe7d0e9336e2b78f0958a2d6b288ba89f25a1762407a222bf53c3/yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be", size = 329361, upload-time = "2025-06-10T00:45:10.11Z" }, - { url = "https://files.pythonhosted.org/packages/93/8f/b811b9d1f617c83c907e7082a76e2b92b655400e61730cd61a1f67178393/yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16", size = 346460, upload-time = "2025-06-10T00:45:12.055Z" }, - { url = "https://files.pythonhosted.org/packages/70/fd/af94f04f275f95da2c3b8b5e1d49e3e79f1ed8b6ceb0f1664cbd902773ff/yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513", size = 334486, upload-time = "2025-06-10T00:45:13.995Z" }, - { url = "https://files.pythonhosted.org/packages/84/65/04c62e82704e7dd0a9b3f61dbaa8447f8507655fd16c51da0637b39b2910/yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f", size = 342219, upload-time = "2025-06-10T00:45:16.479Z" }, - { url = "https://files.pythonhosted.org/packages/91/95/459ca62eb958381b342d94ab9a4b6aec1ddec1f7057c487e926f03c06d30/yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390", size = 350693, upload-time = "2025-06-10T00:45:18.399Z" }, - { url = "https://files.pythonhosted.org/packages/a6/00/d393e82dd955ad20617abc546a8f1aee40534d599ff555ea053d0ec9bf03/yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458", size = 355803, upload-time = "2025-06-10T00:45:20.677Z" }, - { url = "https://files.pythonhosted.org/packages/9e/ed/c5fb04869b99b717985e244fd93029c7a8e8febdfcffa06093e32d7d44e7/yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e", size = 341709, upload-time = "2025-06-10T00:45:23.221Z" }, - { url = "https://files.pythonhosted.org/packages/24/fd/725b8e73ac2a50e78a4534ac43c6addf5c1c2d65380dd48a9169cc6739a9/yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d", size = 86591, upload-time = "2025-06-10T00:45:25.793Z" }, - { url = "https://files.pythonhosted.org/packages/94/c3/b2e9f38bc3e11191981d57ea08cab2166e74ea770024a646617c9cddd9f6/yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f", size = 93003, upload-time = "2025-06-10T00:45:27.752Z" }, - { url = "https://files.pythonhosted.org/packages/b4/2d/2345fce04cfd4bee161bf1e7d9cdc702e3e16109021035dbb24db654a622/yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77", size = 46542, upload-time = "2025-06-10T00:46:07.521Z" }, -] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] diff --git a/pkg/hanzo-network/DISTRIBUTED_NETWORK.md b/pkg/hanzo-network/DISTRIBUTED_NETWORK.md deleted file mode 100644 index ef217af72..000000000 --- a/pkg/hanzo-network/DISTRIBUTED_NETWORK.md +++ /dev/null @@ -1,152 +0,0 @@ -# Hanzo Distributed Network - -This document describes the distributed networking capabilities integrated from hanzo/net into hanzo-network. - -## Overview - -The distributed network extends the base `Network` class with: -- Automatic peer discovery via UDP broadcast -- Distributed agent execution across nodes -- Cross-node state synchronization -- Load balancing across nodes -- Device capability detection and reporting - -## Key Components - -### 1. UDP Discovery (`udp_discovery.py`) -- Broadcasts node presence on the local network -- Discovers other nodes automatically -- Maintains peer health checks -- Supports network interface prioritization - -### 2. gRPC Server (`grpc_server.py`) -- Handles peer-to-peer communication -- Executes remote agent requests -- Manages distributed state sync -- Currently a simplified implementation for testing - -### 3. Device Capabilities (`device_capabilities.py`) -- Detects hardware (CPU, GPU, memory) -- Reports compute capabilities (TFLOPS) -- Supports macOS, Linux, and Windows -- Used for intelligent agent placement - -### 4. Distributed Network (`distributed_network.py`) -- Extends base Network with distributed features -- Manages peer discovery and communication -- Routes agent execution to appropriate nodes -- Synchronizes state across the network - -## Usage - -### Basic Example - -```python -from hanzo_network import create_distributed_network, create_agent, create_tool - -# Create agents -agent = create_agent( - name="my_agent", - description="Example agent", - tools=[...] -) - -# Create distributed network -network = create_distributed_network( - agents=[agent], - name="my-network", - node_id="node-1", - listen_port=5678, - broadcast_port=5678 -) - -# Start network -await network.start(wait_for_peers=0) - -# Check status -status = network.get_network_status() -print(f"Peers: {status['peer_count']}") - -# Execute agent (locally or remotely) -result = await network.run("Do something", initial_agent=agent) -``` - -### Multi-Node Setup - -Run on different machines or terminals: - -**Node 1:** -```python -network1 = create_distributed_network( - agents=[weather_agent], - node_id="node-1", - listen_port=5681, - broadcast_port=5678 # Same broadcast port -) -await network1.start() -``` - -**Node 2:** -```python -network2 = create_distributed_network( - agents=[math_agent], - node_id="node-2", - listen_port=5682, - broadcast_port=5678 # Same broadcast port -) -await network2.start() -``` - -The nodes will automatically discover each other and share agent capabilities. - -## Architecture - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” UDP Broadcast โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ โ”‚ โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚ โ”‚ -โ”‚ Node 1 โ”‚ โ”‚ Node 2 โ”‚ -โ”‚ โ”‚ gRPC Requests โ”‚ โ”‚ -โ”‚ - Weather Agent โ”‚ โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚ - Math Agent โ”‚ -โ”‚ - Discovery โ”‚ โ”‚ - Discovery โ”‚ -โ”‚ - gRPC Server โ”‚ โ”‚ - gRPC Server โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ โ”‚ - โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - State Sync & - Agent Registry -``` - -## Testing - -All distributed network tests are passing: - -```bash -python -m pytest tests/test_distributed_network.py -v -``` - -Tests cover: -- Network creation and configuration -- Start/stop operations -- Local agent execution -- Peer discovery simulation -- Router integration - -## Future Enhancements - -The current implementation is a foundation. Future work includes: - -1. **Real gRPC Implementation**: Replace simplified gRPC with full protocol buffers -2. **NATS Integration**: Add NATS as an alternative discovery mechanism -3. **Security**: Add authentication and encryption for peer communication -4. **Advanced Routing**: Implement load balancing and fault tolerance -5. **State Replication**: Full state synchronization across nodes -6. **GPU Support**: Leverage GPU detection for ML workload placement - -## Integration with hanzo-mcp - -The distributed network can be used by hanzo-mcp to create agent networks that span multiple machines, enabling: -- Distributed AI workloads -- Peer-to-peer agent collaboration -- Local compute resource utilization -- Reduced API costs through local model execution \ No newline at end of file diff --git a/pkg/hanzo-network/README.md b/pkg/hanzo-network/README.md deleted file mode 100644 index 83f041741..000000000 --- a/pkg/hanzo-network/README.md +++ /dev/null @@ -1,336 +0,0 @@ -# Hanzo Network - -[![PyPI](https://img.shields.io/pypi/v/hanzo-network.svg)](https://pypi.org/project/hanzo-network/) -[![Python Version](https://img.shields.io/pypi/pyversions/hanzo-network.svg)](https://pypi.org/project/hanzo-network/) - -Distributed AI compute and network orchestration for Hanzo AI. - -## Installation - -```bash -pip install hanzo-network -``` - -## Features - -- **Local Compute Nodes**: Run AI models locally -- **Distributed Networks**: Coordinate multiple nodes -- **Resource Management**: CPU/GPU allocation -- **Model Providers**: HuggingFace, ONNX, Llama.cpp -- **Economic Layer**: ETH-based payments -- **Attestation**: Secure inference verification - -## Quick Start - -### Local Compute Node - -```python -from hanzo_network import LocalComputeNode, InferenceRequest - -# Create compute node -node = LocalComputeNode( - node_id="node-001", - wallet_address="0x..." -) - -# List available models -models = node.list_models() -print(f"Available models: {models}") - -# Load model -node.load_model("hanzo-nano") - -# Process inference request -request = InferenceRequest( - request_id="req-001", - prompt="What is the capital of France?", - max_tokens=50, - max_price_eth=0.001 -) - -result = await node.process_request(request) -print(f"Response: {result.text}") -print(f"Cost: {result.cost_eth} ETH") -``` - -### Distributed Network - -```python -from hanzo_network import ( - DistributedNetwork, - LocalComputeNode -) - -# Create network -network = DistributedNetwork() - -# Add compute nodes -node1 = LocalComputeNode(node_id="node-001") -node2 = LocalComputeNode(node_id="node-002") - -network.register_node(node1) -network.register_node(node2) - -# Submit request (auto-routes to best node) -request_id = await network.submit_request( - InferenceRequest( - prompt="Explain quantum computing", - max_tokens=200 - ) -) - -# Get result -result = network.get_result(request_id) -``` - -### Model Configuration - -```python -from hanzo_network import ModelConfig, ModelProvider - -# Configure custom model -config = ModelConfig( - name="my-model", - provider=ModelProvider.HUGGINGFACE, - model_path="microsoft/phi-2", - device="cuda", - quantization="int8", - min_ram_gb=8.0, - min_vram_gb=4.0, - price_per_1k_tokens=0.0001 -) - -# Add to node -node = LocalComputeNode(node_id="node-001") -node.models["my-model"] = config -``` - -## Advanced Features - -### Resource Monitoring - -```python -from hanzo_network import ResourceMonitor - -monitor = ResourceMonitor() - -# Check system resources -resources = monitor.get_resources() -print(f"CPU: {resources['cpu_percent']}%") -print(f"RAM: {resources['ram_gb']} GB") -print(f"GPU: {resources['gpu_name']}") -print(f"VRAM: {resources['vram_gb']} GB") - -# Check if model can run -can_run = monitor.check_model_fit(model_config) -``` - -### Network Discovery - -```python -from hanzo_network import NetworkDiscovery - -# Discover nodes on network -discovery = NetworkDiscovery() -nodes = await discovery.find_nodes( - min_models=1, - max_price_eth=0.001, - required_models=["llama2:7b"] -) - -for node in nodes: - print(f"Found: {node.node_id} at {node.address}") -``` - -### Attestation - -```python -from hanzo_network import AttestationService - -# Enable attestation for secure inference -attestation = AttestationService() - -request = InferenceRequest( - prompt="Sensitive query", - require_attestation=True -) - -result = await node.process_request(request) - -# Verify attestation -if result.attestation: - valid = attestation.verify( - result.attestation, - request, - result - ) - print(f"Attestation valid: {valid}") -``` - -### Economic Layer - -```python -from hanzo_network import PaymentChannel - -# Setup payment channel -channel = PaymentChannel( - provider_address="0x...", - consumer_address="0x...", - deposit_eth=0.1 -) - -# Make payment for inference -payment = await channel.pay( - amount_eth=0.0001, - request_id="req-001" -) - -# Close channel -await channel.close() -``` - -## Orchestration - -### Local Orchestrator - -```python -from hanzo_network import LocalComputeOrchestrator - -orchestrator = LocalComputeOrchestrator() - -# Register multiple nodes -for i in range(5): - node = LocalComputeNode(node_id=f"node-{i:03d}") - orchestrator.register_node(node) - -# Submit batch requests -requests = [ - InferenceRequest(prompt=f"Question {i}") - for i in range(10) -] - -results = await orchestrator.process_batch(requests) -``` - -### Load Balancing - -```python -from hanzo_network import LoadBalancer - -balancer = LoadBalancer( - strategy="least_loaded", # least_loaded, round_robin, weighted - health_check_interval=30 -) - -# Add nodes -balancer.add_node(node1, weight=1.0) -balancer.add_node(node2, weight=2.0) - -# Route request -selected_node = balancer.select_node(request) -``` - -## Configuration - -### Environment Variables - -```bash -# Network settings -HANZO_NETWORK_ID=mainnet -HANZO_NODE_ID=node-001 - -# Wallet -HANZO_WALLET_ADDRESS=0x... -HANZO_PRIVATE_KEY=... - -# Model settings -HANZO_MODEL_PATH=/models -HANZO_DEFAULT_DEVICE=cuda - -# Pricing -HANZO_BASE_PRICE_ETH=0.0001 -HANZO_PRICE_MULTIPLIER=1.0 -``` - -### Configuration File - -```yaml -network: - id: mainnet - discovery: - enabled: true - port: 9552 - -node: - id: node-001 - wallet: "0x..." - -models: - - name: hanzo-nano - provider: huggingface - path: microsoft/phi-2 - device: cuda - price: 0.0001 - - - name: hanzo-base - provider: llama_cpp - path: /models/llama2-7b.gguf - device: cpu - price: 0.00005 - -resources: - max_concurrent: 3 - max_memory_gb: 16 - reserved_memory_gb: 4 -``` - -## Performance - -### Benchmarks - -| Model | Device | Tokens/sec | Memory | -|-------|--------|-----------|--------| -| Phi-2 | CPU | 20 | 4GB | -| Phi-2 | GPU | 50 | 3GB | -| Llama2-7B | CPU | 10 | 8GB | -| Llama2-7B | GPU | 40 | 6GB | - -### Optimization - -- Use quantization for larger models -- Enable GPU acceleration when available -- Implement request batching -- Use model caching -- Configure appropriate timeouts - -## Development - -### Setup - -```bash -cd pkg/hanzo-network -uv sync --all-extras -``` - -### Testing - -```bash -# Run tests -pytest tests/ - -# Integration tests -pytest tests/ -m integration - -# With coverage -pytest tests/ --cov=hanzo_network -``` - -### Building - -```bash -uv build -``` - -## License - -Apache License 2.0 \ No newline at end of file diff --git a/pkg/hanzo-network/USAGE.md b/pkg/hanzo-network/USAGE.md deleted file mode 100644 index 975f150e5..000000000 --- a/pkg/hanzo-network/USAGE.md +++ /dev/null @@ -1,994 +0,0 @@ -# Hanzo Network SDK - Complete Usage Guide - -## Table of Contents -1. [Overview](#overview) -2. [Installation](#installation) -3. [Quick Start](#quick-start) -4. [Core Concepts](#core-concepts) -5. [Building Agent Networks](#building-agent-networks) -6. [Local Compute](#local-compute) -7. [Routing Strategies](#routing-strategies) -8. [Tools and Actions](#tools-and-actions) -9. [State Management](#state-management) -10. [Production Deployment](#production-deployment) -11. [Examples](#examples) -12. [API Reference](#api-reference) - -## Overview - -Hanzo Network is a powerful framework for creating and managing networks of AI agents, designed for building scalable, distributed AI workflows. It provides: - -- **Agent Networks**: Orchestrate multiple AI agents working together -- **Local Compute**: Run AI models locally with hanzo.network integration -- **Flexible Routing**: Dynamic agent selection based on task requirements -- **State Management**: Shared state across agent networks -- **Tool Integration**: Extensible tool system for agent capabilities -- **Production Ready**: Built-in monitoring, error handling, and scaling - -## Installation - -```bash -# Basic installation -pip install hanzo-network - -# With all dependencies -pip install hanzo-network[all] - -# For development -pip install hanzo-network[dev] -``` - -## Quick Start - -```python -from hanzo_network import create_network, create_agent, NetworkState -from dataclasses import dataclass -from typing import Optional - -# Define your state -@dataclass -class TaskState(NetworkState): - task: str - result: Optional[str] = None - done: bool = False - -# Create agents -planner = create_agent( - name="planner", - instructions="You are a planning agent. Create a plan for the given task.", - model="gpt-4" -) - -executor = create_agent( - name="executor", - instructions="You are an execution agent. Execute the plan step by step.", - model="gpt-3.5-turbo" -) - -# Define routing logic -def task_router(agents, state): - if state.done: - return None - if not state.result: - return planner - return executor - -# Create and run network -network = create_network( - agents=[planner, executor], - router=task_router, - state=TaskState(task="Build a web scraper") -) - -result = network.run() -print(f"Result: {result.state.result}") -``` - -## Core Concepts - -### Agents - -Agents are the fundamental units of work in Hanzo Network: - -```python -from hanzo_network import Agent, create_agent - -# Class-based agent -class ResearchAgent(Agent): - name = "researcher" - description = "Conducts research and analysis" - model = "claude-3-opus" - - instructions = """You are an expert researcher. - Find accurate information and provide detailed analysis.""" - - tools = ["search", "analyze", "summarize"] - -# Function-based agent -researcher = create_agent( - name="researcher", - instructions="Research the given topic thoroughly", - model="gpt-4", - tools=["web_search", "document_analysis"] -) -``` - -### Networks - -Networks orchestrate agent execution: - -```python -from hanzo_network import Network, create_network - -# Create network with configuration -network = create_network( - agents=[agent1, agent2, agent3], - router=routing_function, - state=initial_state, - max_iterations=50, - checkpoint_interval=10 -) - -# Run network -result = network.run() - -# Access results -final_state = result.state -execution_history = result.history -``` - -### Routers - -Routers determine which agent to execute next: - -```python -from hanzo_network import create_router, create_routing_agent - -# Function-based router -def conditional_router(agents, state): - if state.needs_research: - return agents["researcher"] - elif state.needs_analysis: - return agents["analyst"] - return None - -# LLM-based routing agent -routing_agent = create_routing_agent( - model="gpt-4", - instructions="""Based on the current state, decide which agent should run next: - - researcher: For gathering information - - analyst: For processing data - - writer: For creating content""" -) - -# Hybrid router -hybrid_router = create_router( - agents=agents, - routing_agent=routing_agent, - fallback=conditional_router -) -``` - -## Building Agent Networks - -### Sequential Pipeline - -```python -@dataclass -class PipelineState(NetworkState): - input_data: str - cleaned_data: Optional[str] = None - analyzed_data: Optional[dict] = None - report: Optional[str] = None - done: bool = False - -# Create specialized agents -cleaner = create_agent( - name="cleaner", - instructions="Clean and normalize the input data", - tools=["data_cleaning", "validation"] -) - -analyzer = create_agent( - name="analyzer", - instructions="Analyze the cleaned data for insights", - tools=["statistical_analysis", "visualization"] -) - -reporter = create_agent( - name="reporter", - instructions="Generate a comprehensive report", - tools=["report_generation", "formatting"] -) - -# Sequential router -def pipeline_router(agents, state): - if state.done: - return None - if not state.cleaned_data: - return agents["cleaner"] - if not state.analyzed_data: - return agents["analyzer"] - if not state.report: - return agents["reporter"] - state.done = True - return None - -# Run pipeline -pipeline = create_network( - agents=[cleaner, analyzer, reporter], - router=pipeline_router, - state=PipelineState(input_data="raw data...") -) -``` - -### Parallel Processing - -```python -from hanzo_network import ParallelNetwork - -# Define parallel tasks -parallel_network = ParallelNetwork( - agents={ - "scraper1": create_agent("scraper1", "Scrape website A"), - "scraper2": create_agent("scraper2", "Scrape website B"), - "scraper3": create_agent("scraper3", "Scrape website C"), - }, - aggregator=create_agent("aggregator", "Combine all scraped data"), - state=ScrapingState() -) - -# Run all scrapers in parallel, then aggregate -result = await parallel_network.run_async() -``` - -### Hierarchical Networks - -```python -# Sub-network for research -research_network = create_network( - agents=[web_searcher, paper_reader, note_taker], - router=research_router, - state=ResearchState() -) - -# Sub-network for writing -writing_network = create_network( - agents=[outliner, writer, editor], - router=writing_router, - state=WritingState() -) - -# Main orchestrator -main_network = create_network( - agents=[ - create_agent("research_lead", network=research_network), - create_agent("writing_lead", network=writing_network), - create_agent("reviewer", "Review the final output") - ], - router=main_router, - state=ProjectState() -) -``` - -## Local Compute - -Hanzo Network includes powerful local compute capabilities: - -```python -from hanzo_network import LocalComputeOrchestrator, ModelConfig, ModelProvider - -# Initialize local compute -orchestrator = LocalComputeOrchestrator() - -# Add local models -orchestrator.add_model(ModelConfig( - name="llama-7b", - provider=ModelProvider.LLAMA_CPP, - model_path="/path/to/llama-7b.gguf", - context_length=4096, - gpu_layers=35 # Use GPU acceleration -)) - -orchestrator.add_model(ModelConfig( - name="codellama-13b", - provider=ModelProvider.LLAMA_CPP, - model_path="/path/to/codellama-13b.gguf", - context_length=8192, - gpu_layers=40 -)) - -# Create agent using local model -local_agent = create_agent( - name="local_coder", - instructions="You are a helpful coding assistant", - model="local:codellama-13b", # Use local model - compute_provider=orchestrator -) - -# Run inference -result = await local_agent.run_async( - "Write a Python function to sort a list" -) -``` - -### Load Balancing - -```python -# Create compute nodes -node1 = LocalComputeNode( - node_id="gpu-server-1", - models=["llama-70b", "mixtral-8x7b"], - max_concurrent=4 -) - -node2 = LocalComputeNode( - node_id="gpu-server-2", - models=["llama-70b", "codellama-34b"], - max_concurrent=4 -) - -# Orchestrator handles load balancing -orchestrator = LocalComputeOrchestrator( - nodes=[node1, node2], - strategy="least_loaded" # or "round_robin", "model_affinity" -) - -# Requests are automatically distributed -network = create_network( - agents=[agent1, agent2, agent3], - compute_provider=orchestrator -) -``` - -## Routing Strategies - -### State-Based Routing - -```python -def state_machine_router(agents, state): - """Route based on state machine transitions""" - transitions = { - "init": "data_collector", - "collected": "processor", - "processed": "validator", - "validated": "reporter", - "reported": None - } - - current_phase = state.phase - next_agent_name = transitions.get(current_phase) - - if next_agent_name: - return agents[next_agent_name] - return None -``` - -### Score-Based Routing - -```python -def score_based_router(agents, state): - """Route to agent with highest relevance score""" - scores = {} - - for name, agent in agents.items(): - # Calculate relevance score - score = 0 - if state.needs_technical and "technical" in agent.capabilities: - score += 10 - if state.urgency == "high" and agent.speed_rating > 8: - score += 5 - if state.complexity == "high" and agent.expertise_level > 9: - score += 8 - - scores[name] = score - - # Return highest scoring agent - if scores: - best_agent = max(scores, key=scores.get) - if scores[best_agent] > 0: - return agents[best_agent] - - return None -``` - -### Consensus Routing - -```python -from hanzo_network import ConsensusRouter - -# Multiple agents vote on next action -consensus_router = ConsensusRouter( - voters=[ - create_agent("strategist", "Decide strategic direction"), - create_agent("analyst", "Analyze current situation"), - create_agent("coordinator", "Coordinate team efforts") - ], - candidates=["researcher", "developer", "tester", "deployer"], - threshold=0.6 # 60% agreement required -) -``` - -## Tools and Actions - -### Creating Tools - -```python -from hanzo_network import Tool, create_tool - -# Class-based tool -class DatabaseTool(Tool): - name = "database" - description = "Query and update database" - - async def execute(self, action: str, query: str, state): - if action == "query": - results = await self.db.query(query) - state.query_results = results - return f"Found {len(results)} records" - elif action == "update": - affected = await self.db.update(query) - return f"Updated {affected} records" - -# Function-based tool -async def web_search_tool(query: str, max_results: int = 10, state=None): - """Search the web for information""" - results = await search_engine.search(query, max_results) - if state: - state.search_results = results - return f"Found {len(results)} results for '{query}'" - -# Register tool -search_tool = create_tool( - name="web_search", - description="Search the web", - function=web_search_tool -) -``` - -### Tool Composition - -```python -# Composite tool that uses multiple sub-tools -class ResearchTool(Tool): - name = "research" - description = "Comprehensive research tool" - - def __init__(self): - self.search = WebSearchTool() - self.scrape = WebScraperTool() - self.summarize = SummarizerTool() - self.cite = CitationTool() - - async def execute(self, topic: str, depth: str = "medium", state=None): - # Search for sources - sources = await self.search.execute(topic, max_results=20) - - # Scrape content - contents = [] - for source in sources[:10]: - content = await self.scrape.execute(source.url) - contents.append(content) - - # Summarize findings - summary = await self.summarize.execute(contents, length=depth) - - # Add citations - cited_summary = await self.cite.execute(summary, sources) - - if state: - state.research_result = cited_summary - - return cited_summary -``` - -## State Management - -### Shared State - -```python -from hanzo_network import SharedState, StateManager - -@dataclass -class ProjectState(SharedState): - project_id: str - tasks: List[Task] = field(default_factory=list) - completed_tasks: Set[str] = field(default_factory=set) - team_members: Dict[str, Agent] = field(default_factory=dict) - metrics: Dict[str, float] = field(default_factory=dict) - - def add_task(self, task: Task): - self.tasks.append(task) - self.emit_change("task_added", task) - - def complete_task(self, task_id: str): - self.completed_tasks.add(task_id) - self.emit_change("task_completed", task_id) - self.update_metrics() - -# State manager handles persistence and synchronization -state_manager = StateManager( - state_class=ProjectState, - persistence="redis", # or "memory", "postgres" - sync_interval=5 # seconds -) - -# Networks share state -network1 = create_network(agents=[...], state_manager=state_manager) -network2 = create_network(agents=[...], state_manager=state_manager) -``` - -### State Versioning - -```python -# Enable state history -state_manager = StateManager( - state_class=ProjectState, - enable_history=True, - max_history=100 -) - -# Access state history -history = state_manager.get_history() -for version in history: - print(f"Version {version.id} at {version.timestamp}") - print(f"Changed by: {version.agent}") - print(f"Changes: {version.diff}") - -# Rollback to previous state -state_manager.rollback(version_id="v123") -``` - -## Production Deployment - -### Configuration - -```yaml -# network_config.yaml -network: - name: "production-pipeline" - max_iterations: 100 - timeout: 3600 - checkpoint_interval: 10 - -agents: - - name: "data-processor" - model: "gpt-4" - temperature: 0.2 - max_retries: 3 - timeout: 300 - - - name: "quality-checker" - model: "claude-3-opus" - temperature: 0.1 - tools: ["validation", "testing"] - -compute: - provider: "local" - nodes: - - id: "gpu-1" - models: ["llama-70b", "mixtral-8x7b"] - gpu: true - max_concurrent: 4 - -monitoring: - metrics_port: 9090 - enable_tracing: true - trace_endpoint: "http://jaeger:14268" - -persistence: - state_store: "redis" - redis_url: "redis://localhost:6379" - checkpoint_dir: "/var/lib/hanzo/checkpoints" -``` - -### Deployment Script - -```python -from hanzo_network import NetworkDeployment -import yaml - -# Load configuration -with open("network_config.yaml") as f: - config = yaml.safe_load(f) - -# Create deployment -deployment = NetworkDeployment(config) - -# Add health checks -deployment.add_health_check("/health", interval=30) - -# Add monitoring -deployment.enable_prometheus_metrics(port=9090) -deployment.enable_opentelemetry_tracing( - endpoint="http://jaeger:14268" -) - -# Deploy with auto-scaling -deployment.deploy( - min_replicas=2, - max_replicas=10, - scale_metric="cpu", - scale_threshold=0.7 -) -``` - -### Docker Deployment - -```dockerfile -FROM python:3.11-slim - -WORKDIR /app - -# Install dependencies -COPY requirements.txt . -RUN pip install -r requirements.txt - -# Copy application -COPY . . - -# Run network -CMD ["hanzo-network", "run", "--config", "network_config.yaml"] -``` - -### Kubernetes Deployment - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hanzo-network -spec: - replicas: 3 - selector: - matchLabels: - app: hanzo-network - template: - metadata: - labels: - app: hanzo-network - spec: - containers: - - name: network - image: hanzo/network:latest - ports: - - containerPort: 8080 # API - - containerPort: 9090 # Metrics - env: - - name: OPENAI_API_KEY - valueFrom: - secretKeyRef: - name: api-keys - key: openai - resources: - requests: - memory: "2Gi" - cpu: "1" - limits: - memory: "4Gi" - cpu: "2" - nvidia.com/gpu: "1" # For local models -``` - -## Examples - -### Customer Support Network - -```python -@dataclass -class SupportState(NetworkState): - customer_query: str - customer_id: str - ticket_id: Optional[str] = None - knowledge_base_results: List[dict] = field(default_factory=list) - suggested_solution: Optional[str] = None - customer_satisfied: bool = False - escalated: bool = False - -# Agents -classifier = create_agent( - name="classifier", - instructions="Classify the customer query type and urgency", - tools=["classify_query", "check_customer_history"] -) - -kb_searcher = create_agent( - name="kb_searcher", - instructions="Search knowledge base for solutions", - tools=["search_kb", "rank_solutions"] -) - -solution_provider = create_agent( - name="solution_provider", - instructions="Provide solution to customer", - tools=["generate_response", "send_email"] -) - -escalation_agent = create_agent( - name="escalator", - instructions="Escalate to human support", - tools=["create_ticket", "notify_support_team"] -) - -# Router -def support_router(agents, state): - if state.customer_satisfied or state.escalated: - return None - - if not state.ticket_id: - return agents["classifier"] - - if not state.knowledge_base_results: - return agents["kb_searcher"] - - if not state.suggested_solution: - return agents["solution_provider"] - - if state.customer_satisfied: - return None - - return agents["escalator"] - -# Create support network -support_network = create_network( - agents=[classifier, kb_searcher, solution_provider, escalation_agent], - router=support_router, - state=SupportState( - customer_query="My order hasn't arrived", - customer_id="cust_123" - ) -) -``` - -### Code Review Network - -```python -@dataclass -class CodeReviewState(NetworkState): - pr_url: str - files_changed: List[str] = field(default_factory=list) - issues_found: List[dict] = field(default_factory=list) - suggestions: List[dict] = field(default_factory=list) - approved: bool = False - changes_requested: bool = False - -# Specialized review agents -security_reviewer = create_agent( - name="security", - instructions="Review code for security vulnerabilities", - model="claude-3-opus", - tools=["static_analysis", "dependency_check"] -) - -performance_reviewer = create_agent( - name="performance", - instructions="Review code for performance issues", - model="gpt-4", - tools=["profile_code", "benchmark"] -) - -style_reviewer = create_agent( - name="style", - instructions="Review code style and conventions", - model="gpt-3.5-turbo", - tools=["linter", "formatter"] -) - -final_reviewer = create_agent( - name="final", - instructions="Make final review decision", - model="claude-3-opus" -) - -# Parallel review network -review_network = ParallelNetwork( - agents=[security_reviewer, performance_reviewer, style_reviewer], - aggregator=final_reviewer, - state=CodeReviewState(pr_url="https://github.com/...") -) -``` - -### Research and Writing Network - -```python -@dataclass -class ResearchWritingState(NetworkState): - topic: str - research_depth: str = "comprehensive" - target_audience: str = "general" - sources: List[dict] = field(default_factory=list) - outline: Optional[dict] = None - draft: Optional[str] = None - final_article: Optional[str] = None - done: bool = False - -# Research team -researcher = create_agent( - name="researcher", - instructions="Conduct thorough research on the topic", - tools=["web_search", "academic_search", "fact_check"] -) - -outliner = create_agent( - name="outliner", - instructions="Create detailed article outline", - tools=["structure_content", "identify_key_points"] -) - -writer = create_agent( - name="writer", - instructions="Write engaging content based on research", - tools=["generate_text", "cite_sources"] -) - -editor = create_agent( - name="editor", - instructions="Edit and refine the article", - tools=["grammar_check", "style_improve", "fact_verify"] -) - -# Create research and writing pipeline -rw_network = create_network( - agents=[researcher, outliner, writer, editor], - router=sequential_router([researcher, outliner, writer, editor]), - state=ResearchWritingState( - topic="The Future of AI Agents", - research_depth="comprehensive", - target_audience="technical" - ) -) -``` - -## API Reference - -### Core Classes - -```python -# Agent base class -class Agent: - name: str - description: str - instructions: str - model: str - tools: List[str] - temperature: float = 0.7 - max_tokens: int = 4000 - - async def run(self, state: NetworkState) -> AgentResponse - async def run_with_tools(self, state: NetworkState) -> AgentResponse - -# Network class -class Network: - agents: Dict[str, Agent] - router: Router - state: NetworkState - max_iterations: int = 100 - - def run(self) -> NetworkResult - async def run_async(self) -> NetworkResult - def checkpoint(self) -> None - def restore(self, checkpoint_path: str) -> None - -# Router base class -class Router: - def select_agent(self, agents: Dict[str, Agent], state: NetworkState) -> Optional[Agent] - async def select_agent_async(self, agents: Dict[str, Agent], state: NetworkState) -> Optional[Agent] -``` - -### Factory Functions - -```python -# Create agent -def create_agent( - name: str, - instructions: str, - model: str = "gpt-4", - tools: List[str] = None, - **kwargs -) -> Agent - -# Create network -def create_network( - agents: List[Agent], - router: Union[Router, Callable], - state: NetworkState, - **kwargs -) -> Network - -# Create router -def create_router( - agents: List[Agent], - routing_agent: Optional[Agent] = None, - fallback: Optional[Callable] = None -) -> Router - -# Create tool -def create_tool( - name: str, - description: str, - function: Callable -) -> Tool -``` - -### Local Compute - -```python -# Compute orchestrator -class LocalComputeOrchestrator: - def add_model(self, config: ModelConfig) -> None - def add_node(self, node: LocalComputeNode) -> None - async def run_inference(self, request: InferenceRequest) -> InferenceResult - def get_stats(self) -> Dict[str, Any] - -# Model configuration -@dataclass -class ModelConfig: - name: str - provider: ModelProvider - model_path: str - context_length: int = 4096 - gpu_layers: int = 0 - threads: int = 4 -``` - -### State Management - -```python -# Network state base class -class NetworkState: - def to_dict(self) -> Dict[str, Any] - def from_dict(cls, data: Dict[str, Any]) -> NetworkState - def validate(self) -> bool - def emit_change(self, event: str, data: Any) -> None - -# State manager -class StateManager: - def get_state(self) -> NetworkState - def update_state(self, updates: Dict[str, Any]) -> None - def get_history(self) -> List[StateVersion] - def rollback(self, version_id: str) -> None -``` - -## Best Practices - -1. **Agent Design**: Keep agents focused on single responsibilities -2. **State Management**: Use immutable state updates when possible -3. **Error Handling**: Implement retry logic and graceful degradation -4. **Tool Safety**: Validate tool inputs and handle errors appropriately -5. **Router Optimization**: Keep routing logic simple and deterministic -6. **Testing**: Test agents, tools, and routers independently -7. **Monitoring**: Use metrics and tracing in production -8. **Security**: Never expose API keys or sensitive data in state - -## Troubleshooting - -### Common Issues - -1. **Import Errors**: Ensure hanzo-network and dependencies are installed -2. **Model Loading**: Check model paths and GPU availability -3. **State Synchronization**: Verify Redis/database connections -4. **Memory Issues**: Use streaming for large responses -5. **Performance**: Enable GPU acceleration for local models - -### Debug Mode - -```python -# Enable debug logging -import logging -logging.basicConfig(level=logging.DEBUG) - -# Create network with debug mode -network = create_network( - agents=agents, - router=router, - state=state, - debug=True, # Enables detailed logging - trace_execution=True # Records all decisions -) - -# Access debug information -print(network.execution_trace) -print(network.decision_log) -``` - -For more help, see our [GitHub issues](https://github.com/hanzoai/network/issues). \ No newline at end of file diff --git a/pkg/hanzo-network/examples/distributed_demo.py b/pkg/hanzo-network/examples/distributed_demo.py deleted file mode 100644 index a174a036d..000000000 --- a/pkg/hanzo-network/examples/distributed_demo.py +++ /dev/null @@ -1,151 +0,0 @@ -#!/usr/bin/env python -"""Demonstration of Hanzo distributed network with UDP discovery. - -This example shows how to: -1. Create distributed networks on different ports -2. Have them discover each other via UDP broadcast -3. Execute agents across the network -""" - -import asyncio -import sys - -from hanzo_network import ( - create_agent, - create_distributed_network, - create_tool, -) - - -# Create some test tools -async def get_weather(location: str) -> str: - """Get weather for a location.""" - return f"The weather in {location} is sunny and 72ยฐF" - - -async def get_time(timezone: str = "UTC") -> str: - """Get current time in timezone.""" - from datetime import datetime - - return f"Current time in {timezone}: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - -async def calculate(expression: str) -> str: - """Evaluate a mathematical expression.""" - try: - result = eval(expression) - return f"Result: {result}" - except Exception: - return "Error: Invalid expression" - - -def create_demo_agents(): - """Create agents for the demo.""" - # Weather agent - weather_agent = create_agent( - name="weather_agent", - description="Agent that provides weather information", - system="You are a weather agent. Use the get_weather tool to get weather information.", - tools=[create_tool(get_weather, "Get weather for a location")], - ) - - # Time agent - time_agent = create_agent( - name="time_agent", - description="Agent that provides time information", - system="You are a time agent. Use the get_time tool to get time information.", - tools=[create_tool(get_time, "Get current time in timezone")], - ) - - # Math agent - math_agent = create_agent( - name="math_agent", - description="Agent that performs calculations", - system="You are a math agent. Use the calculate tool for math.", - tools=[create_tool(calculate, "Evaluate a mathematical expression")], - ) - - return [weather_agent, time_agent, math_agent] - - -async def run_node(node_id: str, port: int, agents_subset: list): - """Run a single network node.""" - print(f"\n๐Ÿš€ Starting node {node_id} on port {port}") - - # Create distributed network - network = create_distributed_network( - agents=agents_subset, - name=f"demo-network-{node_id}", - node_id=node_id, - listen_port=port, - broadcast_port=5678, # All nodes use same broadcast port - ) - - # Start the network - await network.start(wait_for_peers=0) - - # Print status - status = network.get_network_status() - print(f"๐Ÿ“ก Node {node_id} started with agents: {status['local_agents']}") - - # Keep running and periodically show peer status - while True: - await asyncio.sleep(5) - status = network.get_network_status() - print( - f"๐Ÿ“Š Node {node_id} status - Peers: {status['peer_count']}, Agents: {status['local_agents']}" - ) - - # Show discovered peers - if status["peers"]: - for peer in status["peers"]: - print(f" ๐Ÿ‘ฅ Peer: {peer['id']} at {peer['address']}") - - -async def main(): - """Run the distributed network demo.""" - print("๐ŸŒ Hanzo Distributed Network Demo") - print("=" * 50) - - # Create agents - all_agents = create_demo_agents() - - # Split agents across nodes - node1_agents = [all_agents[0]] # Weather agent - node2_agents = [all_agents[1]] # Time agent - node3_agents = [all_agents[2]] # Math agent - - # Run multiple nodes - if len(sys.argv) > 1: - # Run specific node based on command line arg - node_num = int(sys.argv[1]) - if node_num == 1: - await run_node("node-1", 5681, node1_agents) - elif node_num == 2: - await run_node("node-2", 5682, node2_agents) - elif node_num == 3: - await run_node("node-3", 5683, node3_agents) - else: - print("Usage: python distributed_demo.py [1|2|3]") - else: - print("\nRunning all nodes in parallel (for demo only)") - print("In production, run each node separately:\n") - print(" Terminal 1: python distributed_demo.py 1") - print(" Terminal 2: python distributed_demo.py 2") - print(" Terminal 3: python distributed_demo.py 3\n") - - # Run all nodes in parallel for demo - tasks = [ - run_node("node-1", 5681, node1_agents), - run_node("node-2", 5682, node2_agents), - run_node("node-3", 5683, node3_agents), - ] - - await asyncio.gather(*tasks) - - -if __name__ == "__main__": - try: - asyncio.run(main()) - except KeyboardInterrupt: - print("\n\n๐Ÿ›‘ Demo stopped by user") diff --git a/pkg/hanzo-network/examples/local_llm_demo.py b/pkg/hanzo-network/examples/local_llm_demo.py deleted file mode 100644 index e1650f751..000000000 --- a/pkg/hanzo-network/examples/local_llm_demo.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python -"""Demo of hanzo-network with local LLM (Ollama).""" - -import asyncio - -from hanzo_network import ( - check_local_llm_status, - create_local_agent, - create_local_distributed_network, - create_tool, -) - - -# Create some demo tools -async def get_weather(location: str) -> str: - """Get weather for a location.""" - return f"The weather in {location} is sunny and 72ยฐF" - - -async def calculate(expression: str) -> str: - """Evaluate a mathematical expression.""" - try: - result = eval(expression) - return f"Result: {result}" - except Exception: - return "Error: Invalid expression" - - -async def main(): - """Demo local LLM with hanzo-network.""" - print("๐Ÿค– Hanzo Network with Local LLM Demo") - print("=" * 50) - - # Check Ollama status - print("\n๐Ÿ“ก Checking Ollama status...") - ollama_status = await check_local_llm_status("ollama") - print(f"Ollama available: {ollama_status['available']}") - if ollama_status["available"]: - print(f"Available models: {ollama_status['models']}") - else: - print(f"โš ๏ธ {ollama_status['instructions']}") - print("Continuing with mock responses...") - - # Create agents with local LLM - weather_tool = create_tool( - name="get_weather", - description="Get weather for a location", - handler=get_weather, - ) - - calc_tool = create_tool( - name="calculate", - description="Evaluate a mathematical expression", - handler=calculate, - ) - - weather_agent = create_local_agent( - name="weather_agent", - description="Agent that provides weather information", - system="You are a helpful weather assistant. Use the get_weather tool to answer questions about weather.", - tools=[weather_tool], - local_model="llama3.2", # Use Ollama's llama3.2 model - ) - - math_agent = create_local_agent( - name="math_agent", - description="Agent that performs calculations", - system="You are a helpful math assistant. Use the calculate tool to solve math problems.", - tools=[calc_tool], - local_model="llama3.2", - ) - - # Create distributed network - network = create_local_distributed_network( - agents=[weather_agent, math_agent], - name="local-demo-network", - node_id="demo-node", - listen_port=15700, - broadcast_port=15700, - ) - - print("\n๐Ÿš€ Starting network...") - await network.start(wait_for_peers=0) - - # Get network status - status = network.get_network_status() - print("\n๐Ÿ“Š Network Status:") - print(f" Node ID: {status['node_id']}") - print(f" Device: {status['device_capabilities']['model']}") - print(f" Agents: {status['local_agents']}") - - # Test weather agent - print("\n๐ŸŒค๏ธ Testing weather agent...") - weather_result = await network.run( - prompt="What's the weather in Tokyo?", initial_agent=weather_agent - ) - - if weather_result["success"]: - print(f"Response: {weather_result['final_output']}") - - # Test math agent - print("\n๐Ÿ”ข Testing math agent...") - math_result = await network.run( - prompt="Calculate 42 * 17 + 3", initial_agent=math_agent - ) - - if math_result["success"]: - print(f"Response: {math_result['final_output']}") - - # Test with router (let network decide which agent) - print("\n๐Ÿค” Testing with router...") - auto_result = await network.run( - prompt="I need to know the weather in Paris and also calculate 100 / 4" - ) - - if auto_result["success"]: - print(f"Response: {auto_result['final_output']}") - print(f"Used {auto_result['iterations']} agent(s)") - - print("\nโœ… Demo complete!") - await network.stop() - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-network/examples/test_distributed.py b/pkg/hanzo-network/examples/test_distributed.py deleted file mode 100644 index 50245cd50..000000000 --- a/pkg/hanzo-network/examples/test_distributed.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python -"""Test distributed network functionality.""" - -import asyncio - -from hanzo_network import create_agent, create_distributed_network, create_tool - - -async def echo_tool(message: str) -> str: - """Echo back the message.""" - return f"Echo: {message}" - - -async def main(): - """Test distributed network.""" - print("Testing Hanzo Distributed Network") - print("=" * 50) - - # Create a simple agent - agent = create_agent( - name="test_agent", - description="Test agent", - system="You are a test agent.", - tools=[create_tool(echo_tool, "Echo a message")], - ) - - # Create distributed network - network = create_distributed_network( - agents=[agent], - name="test-network", - node_id="test-node-1", - listen_port=15690, - broadcast_port=15690, - ) - - print("Starting network...") - await network.start(wait_for_peers=0) - - # Get status - status = network.get_network_status() - print("\nNetwork Status:") - print(f" Node ID: {status['node_id']}") - print(f" Running: {status['is_running']}") - print(f" Device: {status['device_capabilities']['model']}") - print(f" Chip: {status['device_capabilities']['chip']}") - print(f" Memory: {status['device_capabilities']['memory']} MB") - print(f" Local agents: {status['local_agents']}") - - # Wait a bit for any peers - print("\nWaiting for peer discovery...") - await asyncio.sleep(2) - - # Check peers - status = network.get_network_status() - print(f" Discovered peers: {status['peer_count']}") - - print("\nStopping network...") - await network.stop() - print("Done!") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-network/pyproject.toml b/pkg/hanzo-network/pyproject.toml deleted file mode 100644 index 2ddebcb52..000000000 --- a/pkg/hanzo-network/pyproject.toml +++ /dev/null @@ -1,52 +0,0 @@ -[project] -name = "hanzo-network" -version = "0.1.3" -description = "Agent network orchestration for Hanzo AI" -license = "BSD-3-Clause" -authors = [ - {name = "Hanzo AI", email = "dev@hanzo.ai"}, -] -readme = "README.md" -requires-python = ">=3.12" -dependencies = [ - "hanzo-agents>=0.1.0", - "httpx>=0.23.0", - "pydantic>=2.0.0", - "rich>=13.0.0", - "grpcio>=1.50.0", - "grpcio-tools>=1.50.0", - "protobuf>=4.0.0", - "psutil>=5.9.0", -] - -[project.optional-dependencies] -dev = [ - "pytest>=7.0.0", - "pytest-asyncio>=0.21.0", - "pytest-cov>=4.0.0", - "black>=23.0.0", - "ruff>=0.1.0", - "mypy>=1.0.0", -] - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build] -include = [ - "src/hanzo_network", - "README.md", -] - -[tool.hatch.build.targets.wheel] -packages = ["src/hanzo_network"] - -[tool.ruff] -line-length = 120 -target-version = "py38" - -[tool.pytest.ini_options] -testpaths = ["tests"] -pythonpath = ["src"] -asyncio_mode = "auto" \ No newline at end of file diff --git a/pkg/hanzo-network/src/hanzo_network/__init__.py b/pkg/hanzo-network/src/hanzo_network/__init__.py deleted file mode 100644 index 77923d4e9..000000000 --- a/pkg/hanzo-network/src/hanzo_network/__init__.py +++ /dev/null @@ -1,91 +0,0 @@ -"""Hanzo Network - Agent network orchestration for AI workflows with local and distributed compute. - -This package provides a powerful framework for creating and managing networks of AI agents, -inspired by Inngest Agent Kit but adapted for Python and integrated with Hanzo MCP. -Now includes both local AI compute and distributed networking capabilities powered by hanzo.network. -""" - -from .core.agent import Agent, create_agent -from .core.network import Network, create_network -from .core.router import Router, create_router, create_routing_agent -from .core.state import NetworkState -from .core.tool import Tool, create_tool - -# Import distributed network capabilities -from .distributed_network import ( - DistributedNetwork, - DistributedNetworkConfig, - create_distributed_network, -) - -# Import LLM providers -from .llm import HanzoNetProvider, LocalLLMProvider, MLXProvider, OllamaProvider -from .local_network import ( - check_local_llm_status, - create_local_agent, - create_local_distributed_network, -) - -# Local compute capabilities -try: - from .local_compute import ( - InferenceRequest, - LocalComputeNode, - LocalComputeOrchestrator, - ModelConfig, - ModelProvider, - orchestrator, - ) - from .local_compute import ( - InferenceResult as LocalInferenceResult, - ) - - LOCAL_COMPUTE_AVAILABLE = True -except ImportError: - LOCAL_COMPUTE_AVAILABLE = False - LocalComputeNode = None - LocalComputeOrchestrator = None - InferenceRequest = None - LocalInferenceResult = None - ModelConfig = None - ModelProvider = None - orchestrator = None - -__all__ = [ - # Core classes - "Agent", - "Network", - "Router", - "NetworkState", - "Tool", - # Distributed classes - "DistributedNetwork", - "DistributedNetworkConfig", - # Factory functions - "create_agent", - "create_network", - "create_distributed_network", - "create_router", - "create_routing_agent", - "create_tool", - # Local network helpers - "create_local_agent", - "create_local_distributed_network", - "check_local_llm_status", - # LLM providers - "HanzoNetProvider", - "LocalLLMProvider", - "OllamaProvider", - "MLXProvider", - # Local compute (if available) - "LOCAL_COMPUTE_AVAILABLE", - "LocalComputeNode", - "LocalComputeOrchestrator", - "InferenceRequest", - "LocalInferenceResult", - "ModelConfig", - "ModelProvider", - "orchestrator", -] - -__version__ = "0.1.3" diff --git a/pkg/hanzo-network/src/hanzo_network/core/__init__.py b/pkg/hanzo-network/src/hanzo_network/core/__init__.py deleted file mode 100644 index 455270a80..000000000 --- a/pkg/hanzo-network/src/hanzo_network/core/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Core components of Hanzo Network.""" diff --git a/pkg/hanzo-network/src/hanzo_network/core/agent.py b/pkg/hanzo-network/src/hanzo_network/core/agent.py deleted file mode 100644 index efe43513b..000000000 --- a/pkg/hanzo-network/src/hanzo_network/core/agent.py +++ /dev/null @@ -1,267 +0,0 @@ -"""Agent implementation for Hanzo Network.""" - -from dataclasses import dataclass, field -from enum import Enum -from typing import Any, Callable, Dict, List, Optional, Union - -from .state import Message, NetworkState -from .tool import Tool - - -class ModelProvider(Enum): - """Supported model providers.""" - - OPENAI = "openai" - ANTHROPIC = "anthropic" - GOOGLE = "google" - LOCAL = "local" - CLI = "cli" - - -@dataclass -class ModelConfig: - """Configuration for an AI model.""" - - provider: ModelProvider - model: str - api_key: Optional[str] = None - base_url: Optional[str] = None - temperature: float = 0.7 - max_tokens: Optional[int] = None - - @classmethod - def from_string(cls, model_str: str) -> "ModelConfig": - """Create config from model string like 'anthropic/claude-3-5-sonnet'.""" - if "/" in model_str: - provider_str, model = model_str.split("/", 1) - provider = ModelProvider(provider_str) - else: - # Guess provider from model name - if "gpt" in model_str: - provider = ModelProvider.OPENAI - elif "claude" in model_str: - provider = ModelProvider.ANTHROPIC - elif "gemini" in model_str: - provider = ModelProvider.GOOGLE - else: - provider = ModelProvider.LOCAL - model = model_str - - return cls(provider=provider, model=model) - - -@dataclass -class AgentLifecycle: - """Lifecycle hooks for an agent.""" - - on_start: Optional[Callable] = None - on_finish: Optional[Callable] = None - on_error: Optional[Callable] = None - on_tool_call: Optional[Callable] = None - - -@dataclass -class Agent: - """An AI agent that can use tools and participate in networks. - - Agents are the core building blocks of Hanzo Network. Each agent has: - - A name and description - - An optional model configuration - - Tools it can use - - Lifecycle hooks for customization - - A system prompt - """ - - name: str - description: str - model: Optional[ModelConfig] = None - tools: List[Tool] = field(default_factory=list) - system: Optional[str] = None - lifecycle: Optional[AgentLifecycle] = None - metadata: Dict[str, Any] = field(default_factory=dict) - - async def run( - self, - prompt: Union[str, List[Message]], - state: Optional[NetworkState] = None, - context: Optional[Dict[str, Any]] = None, - ) -> Dict[str, Any]: - """Run the agent with a prompt. - - Args: - prompt: User prompt or list of messages - state: Network state (if running in a network) - context: Additional context - - Returns: - Agent result including output and tool calls - """ - # Convert prompt to messages if string - if isinstance(prompt, str): - messages = [Message(role="user", content=prompt)] - else: - messages = prompt - - # Add system message if configured - if self.system: - messages = [Message(role="system", content=self.system)] + messages - - # Call lifecycle hook - if self.lifecycle and self.lifecycle.on_start: - result = await self.lifecycle.on_start( - agent=self, messages=messages, state=state, context=context - ) - if result and result.get("stop"): - return result - messages = result.get("messages", messages) - - try: - # Execute with appropriate backend - if ( - self.model - and isinstance(self.model, ModelConfig) - and self.model.provider == ModelProvider.CLI - ): - result = await self._execute_cli(messages, state, context) - else: - result = await self._execute_llm(messages, state, context) - - # Call finish hook - if self.lifecycle and self.lifecycle.on_finish: - await self.lifecycle.on_finish( - agent=self, result=result, state=state, context=context - ) - - return result - - except Exception as e: - # Call error hook - if self.lifecycle and self.lifecycle.on_error: - await self.lifecycle.on_error( - agent=self, error=e, state=state, context=context - ) - raise - - async def _execute_llm( - self, - messages: List[Message], - state: Optional[NetworkState], - context: Optional[Dict[str, Any]], - ) -> Dict[str, Any]: - """Execute using LLM backend.""" - # Convert tools to appropriate format - tools = [] - if self.tools and self.model: - if self.model.provider == ModelProvider.OPENAI: - tools = [t.to_openai_function() for t in self.tools] - elif self.model.provider == ModelProvider.ANTHROPIC: - tools = [t.to_anthropic_tool() for t in self.tools] - elif self.model.provider == ModelProvider.LOCAL: - # Simple tool format for local LLMs - tools = [ - {"name": t.name, "description": t.description} for t in self.tools - ] - - # Handle local LLM providers using hanzo/net - if self.model and self.model.provider == ModelProvider.LOCAL: - from ..llm import HanzoNetProvider - - # Determine engine type based on model config - engine_type = "dummy" # Default for testing - if "mlx" in self.model.model.lower(): - engine_type = "mlx" - elif "tinygrad" in self.model.model.lower(): - engine_type = "tinygrad" - - # Create hanzo/net provider - provider = HanzoNetProvider( - engine_type=engine_type, base_url=self.model.base_url - ) - - # Generate using distributed inference - return await provider.generate( - messages=messages, - model=self.model.model, - temperature=self.model.temperature, - max_tokens=self.model.max_tokens, - tools=tools, - ) - - # For other providers or if local not available, return mock - return { - "output": [{"type": "text", "content": f"Mock response from {self.name}"}], - "tool_calls": [], - "usage": {"input_tokens": 100, "output_tokens": 50}, - } - - async def _execute_cli( - self, - messages: List[Message], - state: Optional[NetworkState], - context: Optional[Dict[str, Any]], - ) -> Dict[str, Any]: - """Execute using CLI tool.""" - # This would integrate with CLI tools from MCP - # For now, return a mock result - return { - "output": [{"type": "text", "content": f"CLI response from {self.name}"}], - "tool_calls": [], - "usage": {}, - } - - def add_tool(self, tool: Tool) -> None: - """Add a tool to this agent.""" - self.tools.append(tool) - - def remove_tool(self, tool_name: str) -> None: - """Remove a tool by name.""" - self.tools = [t for t in self.tools if t.name != tool_name] - - -def create_agent( - name: str, - description: str, - model: Optional[Union[str, ModelConfig]] = None, - tools: Optional[List[Tool]] = None, - system: Optional[str] = None, - lifecycle: Optional[Dict[str, Callable]] = None, - **metadata, -) -> Agent: - """Create an agent with the given configuration. - - Args: - name: Agent name - description: Agent description - model: Model configuration (string or ModelConfig) - tools: List of tools the agent can use - system: System prompt - lifecycle: Lifecycle hooks as dict - **metadata: Additional metadata - - Returns: - Configured Agent instance - """ - # Convert model string to config if needed - if isinstance(model, str): - model = ModelConfig.from_string(model) - - # Convert lifecycle dict to object - if lifecycle: - lifecycle_obj = AgentLifecycle( - on_start=lifecycle.get("on_start"), - on_finish=lifecycle.get("on_finish"), - on_error=lifecycle.get("on_error"), - on_tool_call=lifecycle.get("on_tool_call"), - ) - else: - lifecycle_obj = None - - return Agent( - name=name, - description=description, - model=model, - tools=tools or [], - system=system, - lifecycle=lifecycle_obj, - metadata=metadata, - ) diff --git a/pkg/hanzo-network/src/hanzo_network/core/network.py b/pkg/hanzo-network/src/hanzo_network/core/network.py deleted file mode 100644 index 0c58dc064..000000000 --- a/pkg/hanzo-network/src/hanzo_network/core/network.py +++ /dev/null @@ -1,305 +0,0 @@ -"""Network implementation for orchestrating multiple agents.""" - -from dataclasses import dataclass, field -from datetime import datetime -from typing import Any, Dict, Generic, List, Optional, TypeVar, Union - -from .agent import Agent -from .router import ( - Router, - RouterArgs, - RouterFunction, - RoutingAgent, - get_default_routing_agent, -) -from .state import NetworkState - -T = TypeVar("T") - - -@dataclass -class NetworkConfig(Generic[T]): - """Configuration for a network.""" - - name: str - agents: List[Agent] - router: Optional[Union[Router, RouterFunction, RoutingAgent]] = None - default_model: Optional[str] = None - max_iterations: int = 10 - default_state: Optional[NetworkState[T]] = None - metadata: Dict[str, Any] = field(default_factory=dict) - - -class Network(Generic[T]): - """A network of agents that work together. - - Networks combine multiple agents with: - - Shared state between agents - - A router that decides agent execution order - - Execution loop that runs until completion - """ - - def __init__(self, config: NetworkConfig[T]): - """Initialize network with configuration. - - Args: - config: Network configuration - """ - self.name = config.name - self.agents = config.agents - self.router = config.router - self.default_model = config.default_model - self.max_iterations = config.max_iterations - self.metadata = config.metadata - - # Initialize state - if config.default_state: - self.state = config.default_state - else: - self.state = NetworkState[T]() - - # Create agent lookup - self.agent_map = {agent.name: agent for agent in self.agents} - - # Setup default router if needed - if not self.router: - self.router = get_default_routing_agent(model=self.default_model) - - async def run( - self, - prompt: str, - initial_agent: Optional[Agent] = None, - context: Optional[Dict[str, Any]] = None, - ) -> Dict[str, Any]: - """Run the network with a user prompt. - - Args: - prompt: User prompt to process - initial_agent: Optional specific agent to start with - context: Additional context - - Returns: - Network execution results - """ - # Add initial user message - self.state.add_message("user", prompt) - - # Track execution - start_time = datetime.now() - iterations = 0 - last_result = None - agent_stack = [] - - # Determine first agent - if initial_agent: - current_agent = initial_agent - else: - # Let router decide - router_args = RouterArgs( - network=self, - state=self.state, - stack=agent_stack, - call_count=0, - last_result=None, - last_agent=None, - ) - current_agent = await self._route(router_args) - - # Main execution loop - while current_agent and iterations < self.max_iterations: - iterations += 1 - - # Execute agent - try: - # Prepare agent context - agent_context = { - "network": self, - "state": self.state, - "iteration": iterations, - **(context or {}), - } - - # Run agent - result = await current_agent.run( - prompt=self._build_agent_prompt(current_agent, last_result), - state=self.state, - context=agent_context, - ) - - # Store result - self.state.set_agent_result(current_agent.name, result) - - # Add agent output to messages - output = result.get("output", []) - for item in output: - if item.get("type") == "text": - self.state.add_message( - "assistant", - item.get("content", ""), - agent_id=current_agent.name, - ) - - # Update tracking - last_result = result - - # Get next agent from router - router_args = RouterArgs( - network=self, - state=self.state, - stack=agent_stack, - call_count=iterations, - last_result=result, - last_agent=current_agent, - ) - current_agent = await self._route(router_args) - - except Exception as e: - # Handle errors - self.state.add_message( - "system", - f"Error in {current_agent.name}: {str(e)}", - agent_id=current_agent.name, - ) - - # Try to recover with router - router_args = RouterArgs( - network=self, - state=self.state, - stack=agent_stack, - call_count=iterations, - last_result={"error": str(e)}, - last_agent=current_agent, - ) - current_agent = await self._route(router_args) - - # Build final result - end_time = datetime.now() - duration = (end_time - start_time).total_seconds() - - return { - "success": iterations < self.max_iterations, - "iterations": iterations, - "duration": duration, - "final_output": self._get_final_output(), - "agent_results": self.state.agent_results, - "messages": [m.to_dict() for m in self.state.messages], - "state": self.state.to_dict(), - } - - async def _route(self, args: RouterArgs) -> Optional[Agent]: - """Execute router to get next agent. - - Args: - args: Router arguments - - Returns: - Next agent or None to stop - """ - if isinstance(self.router, RoutingAgent): - # LLM-based routing - return await self.router.route(args) - elif isinstance(self.router, Router): - # Code-based router object - return self.router(args) - elif callable(self.router): - # Raw function router - return self.router(args) - else: - # No router - stop - return None - - def _build_agent_prompt( - self, agent: Agent, last_result: Optional[Dict[str, Any]] - ) -> List[Any]: - """Build prompt for agent including conversation history. - - Args: - agent: Current agent - last_result: Previous agent's result - - Returns: - List of messages for agent - """ - # Get recent messages - messages = [] - - # Include recent conversation - for msg in self.state.messages[-10:]: # Last 10 messages - if msg.agent_id != agent.name: # Don't include agent's own messages - messages.append({"role": msg.role, "content": msg.content}) - - return messages - - def _get_final_output(self) -> str: - """Get the final output from the network execution.""" - # Find last assistant message - for msg in reversed(self.state.messages): - if msg.role == "assistant": - return msg.content - - return "No output generated" - - def add_agent(self, agent: Agent) -> None: - """Add an agent to the network. - - Args: - agent: Agent to add - """ - self.agents.append(agent) - self.agent_map[agent.name] = agent - - def remove_agent(self, agent_name: str) -> None: - """Remove an agent from the network. - - Args: - agent_name: Name of agent to remove - """ - self.agents = [a for a in self.agents if a.name != agent_name] - self.agent_map.pop(agent_name, None) - - def get_agent(self, agent_name: str) -> Optional[Agent]: - """Get an agent by name. - - Args: - agent_name: Agent name - - Returns: - Agent if found - """ - return self.agent_map.get(agent_name) - - -def create_network( - agents: List[Agent], - name: Optional[str] = None, - router: Optional[Union[Router, RouterFunction, RoutingAgent]] = None, - default_model: Optional[str] = None, - max_iterations: int = 10, - default_state: Optional[NetworkState] = None, - **metadata, -) -> Network: - """Create a network of agents. - - Args: - agents: List of agents in the network - name: Network name - router: Router for agent orchestration - default_model: Default model for routing - max_iterations: Maximum execution iterations - default_state: Initial state - **metadata: Additional metadata - - Returns: - Configured Network instance - """ - config = NetworkConfig( - name=name or "network", - agents=agents, - router=router, - default_model=default_model, - max_iterations=max_iterations, - default_state=default_state, - metadata=metadata, - ) - - return Network(config) diff --git a/pkg/hanzo-network/src/hanzo_network/core/router.py b/pkg/hanzo-network/src/hanzo_network/core/router.py deleted file mode 100644 index 1b760644f..000000000 --- a/pkg/hanzo-network/src/hanzo_network/core/router.py +++ /dev/null @@ -1,309 +0,0 @@ -"""Router system for agent networks in Hanzo Network.""" - -from dataclasses import dataclass -from enum import Enum -from typing import Any, Callable, Dict, Optional, Protocol, Union - -from .agent import Agent, ModelConfig -from .state import NetworkState - - -class RouterDecision(Enum): - """Possible router decisions.""" - - CONTINUE = "continue" # Continue to next agent - STOP = "stop" # Stop network execution - - -class RouterArgs: - """Arguments provided to router functions.""" - - def __init__( - self, - network: Any, # Network instance - state: NetworkState, - stack: list[Agent], - call_count: int, - last_result: Optional[Dict[str, Any]] = None, - last_agent: Optional[Agent] = None, - ): - self.network = network - self.state = state - self.stack = stack - self.call_count = call_count - self.last_result = last_result - self.last_agent = last_agent - - def get_last_output(self) -> Optional[str]: - """Get the last text output from the previous agent.""" - if not self.last_result: - return None - - output = self.last_result.get("output", []) - for item in reversed(output): - if item.get("type") == "text": - return item.get("content") - return None - - -class RouterFunction(Protocol): - """Protocol for router functions.""" - - def __call__(self, args: RouterArgs) -> Optional[Agent]: - """Return next agent or None to stop.""" - ... - - -@dataclass -class Router: - """Base router class.""" - - name: str - description: str - handler: RouterFunction - - def __call__(self, args: RouterArgs) -> Optional[Agent]: - """Call the router.""" - return self.handler(args) - - -@dataclass -class RoutingAgent(Agent): - """Special agent for routing decisions. - - Routing agents are like regular agents but: - - Cannot have tools - - Have special lifecycle for routing - """ - - def __post_init__(self): - """Ensure no tools on routing agents.""" - if self.tools: - raise ValueError("Routing agents cannot have tools") - - async def route(self, args: RouterArgs) -> Optional[Agent]: - """Make a routing decision using LLM. - - Args: - args: Router arguments - - Returns: - Next agent or None to stop - """ - # Build prompt for routing decision - prompt = self._build_routing_prompt(args) - - # Run agent to get decision - result = await self.run(prompt, state=args.state) - - # Parse decision from result - return self._parse_routing_decision(result, args) - - def _build_routing_prompt(self, args: RouterArgs) -> str: - """Build prompt for routing decision.""" - available_agents = args.network.agents - - prompt_parts = [ - "You are a routing agent. Your job is to decide which agent to call next or whether to stop.", - "", - f"Call count: {args.call_count}", - f"Available agents: {', '.join([a.name for a in available_agents])}", - "", - ] - - if args.last_agent: - prompt_parts.append(f"Last agent: {args.last_agent.name}") - - if args.last_result: - output = args.get_last_output() - if output: - prompt_parts.append(f"Last output: {output[:200]}...") - - prompt_parts.extend( - [ - "", - "Based on the current state, which agent should run next?", - "Respond with the agent name or 'STOP' to end execution.", - ] - ) - - return "\n".join(prompt_parts) - - def _parse_routing_decision( - self, result: Dict[str, Any], args: RouterArgs - ) -> Optional[Agent]: - """Parse routing decision from agent result.""" - output = result.get("output", []) - - for item in output: - if item.get("type") == "text": - content = item.get("content", "").strip().upper() - - if content == "STOP": - return None - - # Find agent by name - for agent in args.network.agents: - if agent.name.upper() == content: - return agent - - # Default to stop if can't parse - return None - - -def create_router( - handler: RouterFunction, - name: str = "custom_router", - description: str = "Custom router", -) -> Router: - """Create a code-based router. - - Args: - handler: Router function - name: Router name - description: Router description - - Returns: - Router instance - """ - return Router(name=name, description=description, handler=handler) - - -def create_routing_agent( - name: str, - description: str, - model: Optional[Union[str, ModelConfig]] = None, - system: Optional[str] = None, - **kwargs, -) -> RoutingAgent: - """Create a routing agent for LLM-based routing. - - Args: - name: Agent name - description: Agent description - model: Model configuration - system: System prompt for routing - **kwargs: Additional agent parameters - - Returns: - RoutingAgent instance - """ - # Default system prompt for routing - if not system: - system = """You are a routing agent responsible for orchestrating a network of AI agents. -Your job is to analyze the current state and decide which agent should run next. - -Guidelines: -- Consider what has been accomplished so far -- Identify what still needs to be done -- Choose the most appropriate agent for the next step -- Return 'STOP' when the task is complete - -Be efficient and avoid unnecessary agent calls.""" - - return RoutingAgent( - name=name, - description=description, - model=model, - system=system, - tools=[], # No tools for routing agents - **kwargs, - ) - - -def get_default_routing_agent( - model: Optional[Union[str, ModelConfig]] = None, -) -> RoutingAgent: - """Get the default routing agent. - - Args: - model: Model to use (defaults to Claude Sonnet) - - Returns: - Default RoutingAgent - """ - if not model: - from .agent import ModelConfig, ModelProvider - - # Use local dummy model for default router - model = ModelConfig( - provider=ModelProvider.LOCAL, model="llama3.2", temperature=0.3 - ) - - return create_routing_agent( - name="default_router", - description="Default routing agent for network orchestration", - model=model, - system="""You are the default routing agent. Analyze the conversation and network state to decide: - -1. If the user's request has been fully addressed -> return STOP -2. If more work is needed -> return the name of the most appropriate agent - -Consider: -- What has each agent already done? -- What remains to be accomplished? -- Which agent is best suited for the next step? -- Are we going in circles? If so, return STOP - -Be concise. Respond with just the agent name or STOP.""", - ) - - -# Common routing patterns as functions - - -def sequential_router(agents: list[Agent]) -> RouterFunction: - """Create a router that calls agents in sequence. - - Args: - agents: List of agents to call in order - - Returns: - Router function - """ - - def handler(args: RouterArgs) -> Optional[Agent]: - if args.call_count < len(agents): - return agents[args.call_count] - return None - - return handler - - -def conditional_router( - conditions: list[tuple[Callable[[RouterArgs], bool], Agent]], -) -> RouterFunction: - """Create a router based on conditions. - - Args: - conditions: List of (condition_fn, agent) tuples - - Returns: - Router function - """ - - def handler(args: RouterArgs) -> Optional[Agent]: - for condition_fn, agent in conditions: - if condition_fn(args): - return agent - return None - - return handler - - -def state_based_router(state_key: str, state_map: Dict[Any, Agent]) -> RouterFunction: - """Create a router based on state values. - - Args: - state_key: Key to check in state.data - state_map: Map of state values to agents - - Returns: - Router function - """ - - def handler(args: RouterArgs) -> Optional[Agent]: - value = args.state.data.get(state_key) - return state_map.get(value) - - return handler diff --git a/pkg/hanzo-network/src/hanzo_network/core/state.py b/pkg/hanzo-network/src/hanzo_network/core/state.py deleted file mode 100644 index 10d44feb3..000000000 --- a/pkg/hanzo-network/src/hanzo_network/core/state.py +++ /dev/null @@ -1,161 +0,0 @@ -"""Network state management for agent networks.""" - -from dataclasses import dataclass, field -from datetime import datetime -from typing import Any, Dict, Generic, List, Optional, TypeVar - -T = TypeVar("T") - - -@dataclass -class Message: - """A message in the network conversation.""" - - role: str # 'user', 'assistant', 'system', 'agent' - content: str - agent_id: Optional[str] = None - timestamp: datetime = field(default_factory=datetime.now) - metadata: Dict[str, Any] = field(default_factory=dict) - - def to_dict(self) -> Dict[str, Any]: - """Convert message to dictionary.""" - return { - "role": self.role, - "content": self.content, - "agent_id": self.agent_id, - "timestamp": self.timestamp.isoformat(), - "metadata": self.metadata, - } - - -class NetworkState(Generic[T]): - """Shared state between agents in a network. - - This class manages: - - Message history across all agents - - Key-value store for sharing data - - Agent execution tracking - """ - - def __init__(self, initial_data: Optional[T] = None): - """Initialize network state. - - Args: - initial_data: Initial data for the state - """ - self.messages: List[Message] = [] - self.data: T = initial_data if initial_data is not None else {} - self.agent_results: Dict[str, Any] = {} - self.execution_count: int = 0 - self.metadata: Dict[str, Any] = {} - - def add_message( - self, role: str, content: str, agent_id: Optional[str] = None, **metadata - ) -> None: - """Add a message to the conversation history. - - Args: - role: Message role (user, assistant, system, agent) - content: Message content - agent_id: ID of the agent that generated this message - **metadata: Additional metadata for the message - """ - self.messages.append( - Message(role=role, content=content, agent_id=agent_id, metadata=metadata) - ) - - def get_messages( - self, - agent_id: Optional[str] = None, - role: Optional[str] = None, - limit: Optional[int] = None, - ) -> List[Message]: - """Get messages from the history. - - Args: - agent_id: Filter by agent ID - role: Filter by role - limit: Limit number of messages returned - - Returns: - List of messages matching the filters - """ - messages = self.messages - - if agent_id is not None: - messages = [m for m in messages if m.agent_id == agent_id] - - if role is not None: - messages = [m for m in messages if m.role == role] - - if limit is not None: - messages = messages[-limit:] - - return messages - - def set_agent_result(self, agent_id: str, result: Any) -> None: - """Store the result from an agent execution. - - Args: - agent_id: ID of the agent - result: Result from the agent - """ - self.agent_results[agent_id] = result - - def get_agent_result(self, agent_id: str) -> Optional[Any]: - """Get the result from a specific agent. - - Args: - agent_id: ID of the agent - - Returns: - Agent result if available - """ - return self.agent_results.get(agent_id) - - def increment_execution_count(self) -> int: - """Increment and return the execution count.""" - self.execution_count += 1 - return self.execution_count - - def to_dict(self) -> Dict[str, Any]: - """Convert state to dictionary for serialization.""" - return { - "messages": [ - { - "role": m.role, - "content": m.content, - "agent_id": m.agent_id, - "timestamp": m.timestamp.isoformat(), - "metadata": m.metadata, - } - for m in self.messages - ], - "data": self.data, - "agent_results": self.agent_results, - "execution_count": self.execution_count, - "metadata": self.metadata, - } - - @classmethod - def from_dict(cls, data: Dict[str, Any]) -> "NetworkState": - """Create state from dictionary.""" - state = cls(initial_data=data.get("data", {})) - - # Restore messages - for msg_data in data.get("messages", []): - state.messages.append( - Message( - role=msg_data["role"], - content=msg_data["content"], - agent_id=msg_data.get("agent_id"), - timestamp=datetime.fromisoformat(msg_data["timestamp"]), - metadata=msg_data.get("metadata", {}), - ) - ) - - state.agent_results = data.get("agent_results", {}) - state.execution_count = data.get("execution_count", 0) - state.metadata = data.get("metadata", {}) - - return state diff --git a/pkg/hanzo-network/src/hanzo_network/core/tool.py b/pkg/hanzo-network/src/hanzo_network/core/tool.py deleted file mode 100644 index f62b0a0c6..000000000 --- a/pkg/hanzo-network/src/hanzo_network/core/tool.py +++ /dev/null @@ -1,160 +0,0 @@ -"""Tool system for agents in Hanzo Network.""" - -import inspect -from dataclasses import dataclass -from typing import Any, Callable, Dict, Optional, Protocol, TypedDict - -from pydantic import BaseModel, create_model - - -class ToolContext(TypedDict): - """Context provided to tool handlers.""" - - network: Optional[Any] # Network instance - state: Optional[Any] # Network state - agent: Optional[Any] # Current agent - - -class ToolHandler(Protocol): - """Protocol for tool handler functions.""" - - async def __call__( - self, parameters: Dict[str, Any], context: ToolContext - ) -> Any: ... - - -@dataclass -class Tool: - """A tool that agents can use. - - Tools are functions that agents can call to perform actions or retrieve information. - """ - - name: str - description: str - parameters: type[BaseModel] - handler: ToolHandler - - async def call( - self, parameters: Dict[str, Any], context: Optional[ToolContext] = None - ) -> Any: - """Call the tool with given parameters. - - Args: - parameters: Tool parameters - context: Execution context - - Returns: - Tool result - """ - # Validate parameters - validated_params = self.parameters(**parameters) - - # Call handler - ctx = context or {} - return await self.handler(validated_params.dict(), ctx) - - def to_openai_function(self) -> Dict[str, Any]: - """Convert to OpenAI function format.""" - schema = self.parameters.schema() - - return { - "name": self.name, - "description": self.description, - "parameters": { - "type": "object", - "properties": schema.get("properties", {}), - "required": schema.get("required", []), - }, - } - - def to_anthropic_tool(self) -> Dict[str, Any]: - """Convert to Anthropic tool format.""" - schema = self.parameters.schema() - - return { - "name": self.name, - "description": self.description, - "input_schema": { - "type": "object", - "properties": schema.get("properties", {}), - "required": schema.get("required", []), - }, - } - - -def create_tool( - name: str, - description: str, - parameters: Optional[type[BaseModel]] = None, - handler: Optional[ToolHandler] = None, -) -> Callable: - """Create a tool using decorator syntax or direct call. - - Usage: - # As decorator - @create_tool( - name="search", - description="Search the web", - parameters=SearchParams - ) - async def search_handler(params, context): - return f"Searching for {params['query']}" - - # Direct call - tool = create_tool( - name="search", - description="Search the web", - parameters=SearchParams, - handler=search_handler - ) - - Args: - name: Tool name - description: Tool description - parameters: Pydantic model for parameters - handler: Tool handler function - - Returns: - Tool instance or decorator - """ - - def decorator(func: ToolHandler) -> Tool: - # Extract parameters from function signature if not provided - nonlocal parameters - if parameters is None: - sig = inspect.signature(func) - param_fields = {} - - for param_name, param in sig.parameters.items(): - if param_name in ["self", "context", "ctx"]: - continue - - # Get type annotation - param_type = ( - param.annotation - if param.annotation != inspect.Parameter.empty - else Any - ) - default = ( - param.default if param.default != inspect.Parameter.empty else ... - ) - - param_fields[param_name] = (param_type, default) - - # Create dynamic Pydantic model - if param_fields: - parameters = create_model(f"{name}_params", **param_fields) - else: - parameters = create_model(f"{name}_params") - - return Tool( - name=name, description=description, parameters=parameters, handler=func - ) - - if handler is not None: - # Direct call with handler - return decorator(handler) - else: - # Return decorator - return decorator diff --git a/pkg/hanzo-network/src/hanzo_network/device_capabilities.py b/pkg/hanzo-network/src/hanzo_network/device_capabilities.py deleted file mode 100644 index 574dc6cc3..000000000 --- a/pkg/hanzo-network/src/hanzo_network/device_capabilities.py +++ /dev/null @@ -1,67 +0,0 @@ -"""Device capabilities module for hanzo_network.""" - -from dataclasses import dataclass -from typing import Optional - - -@dataclass -class DeviceCapabilities: - """Represents the capabilities of a device in the network.""" - - cpu_cores: int = 1 - memory_gb: float = 1.0 - gpu_available: bool = False - gpu_memory_gb: Optional[float] = None - network_bandwidth_mbps: float = 100.0 - storage_gb: float = 10.0 - - def __post_init__(self): - """Validate capabilities after initialization.""" - if self.cpu_cores < 1: - raise ValueError("CPU cores must be at least 1") - if self.memory_gb <= 0: - raise ValueError("Memory must be positive") - if self.gpu_available and self.gpu_memory_gb is None: - self.gpu_memory_gb = 0.0 - if self.network_bandwidth_mbps <= 0: - raise ValueError("Network bandwidth must be positive") - if self.storage_gb <= 0: - raise ValueError("Storage must be positive") - - def to_dict(self) -> dict: - """Convert capabilities to dictionary.""" - return { - "cpu_cores": self.cpu_cores, - "memory_gb": self.memory_gb, - "gpu_available": self.gpu_available, - "gpu_memory_gb": self.gpu_memory_gb, - "network_bandwidth_mbps": self.network_bandwidth_mbps, - "storage_gb": self.storage_gb, - } - - @classmethod - def from_dict(cls, data: dict) -> "DeviceCapabilities": - """Create capabilities from dictionary.""" - return cls( - cpu_cores=data.get("cpu_cores", 1), - memory_gb=data.get("memory_gb", 1.0), - gpu_available=data.get("gpu_available", False), - gpu_memory_gb=data.get("gpu_memory_gb"), - network_bandwidth_mbps=data.get("network_bandwidth_mbps", 100.0), - storage_gb=data.get("storage_gb", 10.0), - ) - - def can_handle_workload( - self, - required_memory_gb: float, - required_cpu_cores: int = 1, - requires_gpu: bool = False, - ) -> bool: - """Check if this device can handle a given workload.""" - if self.memory_gb < required_memory_gb: - return False - if self.cpu_cores < required_cpu_cores: - return False - if requires_gpu and not self.gpu_available: - return False - return True diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/__init__.py b/pkg/hanzo-network/src/hanzo_network/distributed/__init__.py deleted file mode 100644 index 44a10a30e..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .discovery import Discovery -from .peer_handle import PeerHandle -from .server import Server - -__all__ = ["Discovery", "PeerHandle", "Server"] diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/discovery.py b/pkg/hanzo-network/src/hanzo_network/distributed/discovery.py deleted file mode 100644 index e48d74399..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/discovery.py +++ /dev/null @@ -1,18 +0,0 @@ -from abc import ABC, abstractmethod -from typing import List - -from .peer_handle import PeerHandle - - -class Discovery(ABC): - @abstractmethod - async def start(self) -> None: - pass - - @abstractmethod - async def stop(self) -> None: - pass - - @abstractmethod - async def discover_peers(self, wait_for_peers: int = 0) -> List[PeerHandle]: - pass diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/grpc/__init__.py b/pkg/hanzo-network/src/hanzo_network/distributed/grpc/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/grpc/grpc_peer_handle.py b/pkg/hanzo-network/src/hanzo_network/distributed/grpc/grpc_peer_handle.py deleted file mode 100644 index 94fe58ca0..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/grpc/grpc_peer_handle.py +++ /dev/null @@ -1,317 +0,0 @@ -import asyncio -import json -import platform -from typing import List, Optional - -import grpc -import numpy as np - -from ..peer_handle import PeerHandle -from . import node_service_pb2, node_service_pb2_grpc -from .helpers import DEBUG -from .inference.shard import Shard -from .topology.device_capabilities import DeviceCapabilities, DeviceFlops -from .topology.topology import Topology - -if platform.system().lower() == "darwin" and platform.machine().lower() == "arm64": - import mlx.core as mx -else: - import numpy as mx - - -class GRPCPeerHandle(PeerHandle): - def __init__( - self, _id: str, address: str, desc: str, device_capabilities: DeviceCapabilities - ): - self._id = _id - self.address = address - self.desc = desc - self._device_capabilities = device_capabilities - self.channel = None - self.stub = None - self.channel_options = [ - ("grpc.max_metadata_size", 32 * 1024 * 1024), - ("grpc.max_receive_message_length", 256 * 1024 * 1024), - ("grpc.max_send_message_length", 256 * 1024 * 1024), - ("grpc.max_concurrent_streams", 100), - ("grpc.http2.min_time_between_pings_ms", 10000), - ("grpc.keepalive_time_ms", 10000), - ("grpc.keepalive_timeout_ms", 5000), - ("grpc.keepalive_permit_without_calls", 1), - ("grpc.http2.max_pings_without_data", 0), - ("grpc.http2.min_ping_interval_without_data_ms", 5000), - ("grpc.tcp_nodelay", 1), - ("grpc.optimization_target", "throughput"), - ] - - def id(self) -> str: - return self._id - - def addr(self) -> str: - return self.address - - def description(self) -> str: - return self.desc - - def device_capabilities(self) -> DeviceCapabilities: - return self._device_capabilities - - async def connect(self): - self.channel = grpc.aio.insecure_channel( - self.address, - options=self.channel_options, - compression=grpc.Compression.Gzip, - ) - self.stub = node_service_pb2_grpc.NodeServiceStub(self.channel) - await asyncio.wait_for(self.channel.channel_ready(), timeout=10.0) - - async def is_connected(self) -> bool: - return ( - self.channel is not None - and self.channel.get_state() == grpc.ChannelConnectivity.READY - ) - - async def disconnect(self): - if self.channel: - await self.channel.close() - self.channel = None - self.stub = None - - async def _ensure_connected(self): - if not (await self.is_connected()): - try: - await asyncio.wait_for(self.connect(), timeout=10.0) - except asyncio.TimeoutError: - if DEBUG >= 2: - print(f"Connection timeout for {self._id}@{self.address}") - await self.disconnect() - raise - - async def health_check(self) -> bool: - try: - await self._ensure_connected() - request = node_service_pb2.HealthCheckRequest() - response = await asyncio.wait_for(self.stub.HealthCheck(request), timeout=5) - return response.is_healthy - except asyncio.TimeoutError: - return False - except Exception: - if DEBUG >= 4: - print(f"Health check failed for {self._id}@{self.address}.") - import traceback - - traceback.print_exc() - return False - - async def send_prompt( - self, - shard: Shard, - prompt: str, - inference_state: Optional[dict] = None, - request_id: Optional[str] = None, - ) -> Optional[np.array]: - await self._ensure_connected() - request = node_service_pb2.PromptRequest( - prompt=prompt, - shard=node_service_pb2.Shard( - model_id=shard.model_id, - start_layer=shard.start_layer, - end_layer=shard.end_layer, - n_layers=shard.n_layers, - ), - request_id=request_id, - inference_state=( - None - if inference_state is None - else self.serialize_inference_state(inference_state) - ), - ) - await self.stub.SendPrompt(request) - - async def send_tensor( - self, - shard: Shard, - tensor: np.ndarray, - inference_state: Optional[dict] = None, - request_id: Optional[str] = None, - ) -> Optional[np.array]: - await self._ensure_connected() - request = node_service_pb2.TensorRequest( - shard=node_service_pb2.Shard( - model_id=shard.model_id, - start_layer=shard.start_layer, - end_layer=shard.end_layer, - n_layers=shard.n_layers, - ), - tensor=node_service_pb2.Tensor( - tensor_data=tensor.tobytes(), - shape=tensor.shape, - dtype=str(tensor.dtype), - ), - request_id=request_id, - inference_state=( - None - if inference_state is None - else self.serialize_inference_state(inference_state) - ), - ) - response = await self.stub.SendTensor(request) - - if not response.tensor_data or not response.shape or not response.dtype: - return None - - return np.frombuffer( - response.tensor_data, dtype=np.dtype(response.dtype) - ).reshape(response.shape) - - async def send_example( - self, - shard: Shard, - example: np.ndarray, - target: np.ndarray, - length: np.ndarray, - train: bool, - request_id: Optional[str] = None, - ) -> Optional[np.array]: - await self._ensure_connected() - request = node_service_pb2.ExampleRequest( - shard=node_service_pb2.Shard( - model_id=shard.model_id, - start_layer=shard.start_layer, - end_layer=shard.end_layer, - n_layers=shard.n_layers, - ), - example=node_service_pb2.Tensor( - tensor_data=example.tobytes(), - shape=example.shape, - dtype=str(example.dtype), - ), - target=node_service_pb2.Tensor( - tensor_data=target.tobytes(), - shape=target.shape, - dtype=str(target.dtype), - ), - length=node_service_pb2.Tensor( - tensor_data=length.tobytes(), - shape=length.shape, - dtype=str(length.dtype), - ), - train=train, - request_id=request_id, - ) - response = await self.stub.SendExample(request) - loss = response.loss - if train and not shard.is_first_layer(): - grads = np.frombuffer( - response.grads.tensor_data, dtype=np.dtype(response.grads.dtype) - ).reshape(response.grads.shape) - return loss, grads - else: - return loss - - async def send_loss( - self, shard: Shard, tensor: np.ndarray, request_id: Optional[str] = None - ) -> Optional[np.array]: - await self._ensure_connected() - request = node_service_pb2.TensorRequest( - shard=node_service_pb2.Shard( - model_id=shard.model_id, - start_layer=shard.start_layer, - end_layer=shard.end_layer, - n_layers=shard.n_layers, - ), - tensor=node_service_pb2.Tensor( - tensor_data=tensor.tobytes(), - shape=tensor.shape, - dtype=str(tensor.dtype), - ), - request_id=request_id, - ) - response = await self.stub.SendLoss(request) - - if not response.tensor_data or not response.shape or not response.dtype: - return None - - return np.frombuffer( - response.tensor_data, dtype=np.dtype(response.dtype) - ).reshape(response.shape) - - async def collect_topology(self, visited: set[str], max_depth: int) -> Topology: - await self._ensure_connected() - request = node_service_pb2.CollectTopologyRequest( - visited=visited, max_depth=max_depth - ) - response = await self.stub.CollectTopology(request) - topology = Topology() - for node_id, capabilities in response.nodes.items(): - device_capabilities = DeviceCapabilities( - model=capabilities.model, - chip=capabilities.chip, - memory=capabilities.memory, - flops=DeviceFlops( - fp16=capabilities.flops.fp16, - fp32=capabilities.flops.fp32, - int8=capabilities.flops.int8, - ), - ) - topology.update_node(node_id, device_capabilities) - for node_id, peer_connections in response.peer_graph.items(): - for conn in peer_connections.connections: - topology.add_edge(node_id, conn.to_id, conn.description) - return topology - - async def send_result( - self, request_id: str, result: List[int], is_finished: bool - ) -> None: - await self._ensure_connected() - tensor = None - if isinstance(result, np.ndarray): - tensor = node_service_pb2.Tensor( - tensor_data=result.tobytes(), - shape=result.shape, - dtype=str(result.dtype), - ) - result = [] - request = node_service_pb2.SendResultRequest( - request_id=request_id, result=result, tensor=tensor, is_finished=is_finished - ) - await self.stub.SendResult(request) - - async def send_opaque_status(self, request_id: str, status: str) -> None: - await self._ensure_connected() - request = node_service_pb2.SendOpaqueStatusRequest( - request_id=request_id, status=status - ) - await asyncio.wait_for(self.stub.SendOpaqueStatus(request), timeout=10.0) - - def serialize_inference_state( - self, inference_state: dict - ) -> node_service_pb2.InferenceState: - proto_inference_state = node_service_pb2.InferenceState() - other_data = {} - for k, v in inference_state.items(): - if isinstance(v, mx.array): - np_array = np.array(v) - tensor_data = node_service_pb2.Tensor( - tensor_data=np_array.tobytes(), - shape=list(np_array.shape), - dtype=str(np_array.dtype), - ) - proto_inference_state.tensor_data[k].CopyFrom(tensor_data) - elif isinstance(v, list) and all(isinstance(item, mx.array) for item in v): - tensor_list = node_service_pb2.TensorList() - for tensor in v: - np_array = np.array(tensor) - tensor_data = node_service_pb2.Tensor( - tensor_data=np_array.tobytes(), - shape=list(np_array.shape), - dtype=str(np_array.dtype), - ) - tensor_list.tensors.append(tensor_data) - proto_inference_state.tensor_list_data[k].CopyFrom(tensor_list) - else: - # For non-tensor data, we'll still use JSON - other_data[k] = v - if other_data: - proto_inference_state.other_data_json = json.dumps(other_data) - return proto_inference_state diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/grpc/grpc_server.py b/pkg/hanzo-network/src/hanzo_network/distributed/grpc/grpc_server.py deleted file mode 100644 index b5927614c..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/grpc/grpc_server.py +++ /dev/null @@ -1,245 +0,0 @@ -import json -import platform -from asyncio import CancelledError -from concurrent import futures - -import grpc -import numpy as np -from net import DEBUG - -from . import node_service_pb2, node_service_pb2_grpc -from .inference.shard import Shard -from .orchestration import Node - -if platform.system().lower() == "darwin" and platform.machine().lower() == "arm64": - import mlx.core as mx -else: - import numpy as mx - - -class GRPCServer(node_service_pb2_grpc.NodeServiceServicer): - def __init__(self, node: Node, host: str, port: int): - self.node = node - self.host = host - self.port = port - self.server = None - - async def start(self) -> None: - self.server = grpc.aio.server( - futures.ThreadPoolExecutor(max_workers=32), - options=[ - ("grpc.max_metadata_size", 32 * 1024 * 1024), - ("grpc.max_send_message_length", 256 * 1024 * 1024), - ("grpc.max_receive_message_length", 256 * 1024 * 1024), - ("grpc.keepalive_time_ms", 10000), - ("grpc.keepalive_timeout_ms", 5000), - ("grpc.http2.max_pings_without_data", 0), - ("grpc.http2.min_time_between_pings_ms", 10000), - ("grpc.http2.min_ping_interval_without_data_ms", 5000), - ("grpc.max_concurrent_streams", 100), - ("grpc.tcp_nodelay", 1), - ("grpc.optimization_target", "throughput"), - ("grpc.keepalive_permit_without_calls", 1), - ( - "grpc.http2.max_concurrent_streams", - 0, - ), # Unlimited concurrent streams - ], - ) - node_service_pb2_grpc.add_NodeServiceServicer_to_server(self, self.server) - listen_addr = f"{self.host}:{self.port}" - self.server.add_insecure_port(listen_addr) - await self.server.start() - if DEBUG >= 1: - print(f"Server started, listening on {listen_addr}") - - async def stop(self) -> None: - if self.server: - try: - await self.server.stop(grace=5) - await self.server.wait_for_termination() - except CancelledError: - pass - if DEBUG >= 1: - print("Server stopped and all connections are closed") - - async def SendPrompt(self, request, context): - shard = Shard( - model_id=request.shard.model_id, - start_layer=request.shard.start_layer, - end_layer=request.shard.end_layer, - n_layers=request.shard.n_layers, - ) - prompt = request.prompt - request_id = request.request_id - inference_state = ( - None - if request.inference_state is None - else self.deserialize_inference_state(request.inference_state) - ) - result = await self.node.process_prompt( - shard, prompt, request_id, inference_state - ) - if DEBUG >= 5: - print(f"SendPrompt {shard=} {prompt=} {request_id=} result: {result}") - tensor_data = result.tobytes() if result is not None else None - return ( - node_service_pb2.Tensor( - tensor_data=tensor_data, shape=result.shape, dtype=str(result.dtype) - ) - if result is not None - else node_service_pb2.Tensor() - ) - - async def SendTensor(self, request, context): - shard = Shard( - model_id=request.shard.model_id, - start_layer=request.shard.start_layer, - end_layer=request.shard.end_layer, - n_layers=request.shard.n_layers, - ) - tensor = np.frombuffer( - request.tensor.tensor_data, dtype=np.dtype(request.tensor.dtype) - ).reshape(request.tensor.shape) - request_id = request.request_id - - inference_state = ( - None - if request.inference_state is None - else self.deserialize_inference_state(request.inference_state) - ) - - result = await self.node.process_tensor( - shard, tensor, request_id, inference_state - ) - if DEBUG >= 5: - print( - f"SendTensor tensor {shard=} {tensor=} {request_id=} result: {result}" - ) - tensor_data = result.tobytes() if result is not None else None - return ( - node_service_pb2.Tensor( - tensor_data=tensor_data, shape=result.shape, dtype=str(result.dtype) - ) - if result is not None - else node_service_pb2.Tensor() - ) - - async def SendExample(self, request, context): - shard = Shard( - model_id=request.shard.model_id, - start_layer=request.shard.start_layer, - end_layer=request.shard.end_layer, - n_layers=request.shard.n_layers, - ) - example = np.frombuffer( - request.example.tensor_data, dtype=np.dtype(request.example.dtype) - ).reshape(request.example.shape) - target = np.frombuffer( - request.target.tensor_data, dtype=np.dtype(request.target.dtype) - ).reshape(request.target.shape) - length = np.frombuffer( - request.length.tensor_data, dtype=np.dtype(request.length.dtype) - ).reshape(request.length.shape) - train = request.train - request_id = request.request_id - - if train and not shard.is_first_layer(): - loss, grad = await self.node.process_example( - shard, example, target, length, train, request_id - ) - tensor_data = grad.tobytes() - grad_tensor = node_service_pb2.Tensor( - tensor_data=tensor_data, shape=grad.shape, dtype=str(grad.dtype) - ) - return node_service_pb2.Loss(loss=loss, grads=grad_tensor) - else: - loss = await self.node.process_example( - shard, example, target, length, train, request_id - ) - return node_service_pb2.Loss(loss=loss, grads=None) - - async def CollectTopology(self, request, context): - max_depth = request.max_depth - visited = set(request.visited) - topology = self.node.current_topology - nodes = { - node_id: node_service_pb2.DeviceCapabilities( - model=cap.model, - chip=cap.chip, - memory=cap.memory, - flops=node_service_pb2.DeviceFlops( - fp32=cap.flops.fp32, fp16=cap.flops.fp16, int8=cap.flops.int8 - ), - ) - for node_id, cap in topology.nodes.items() - } - peer_graph = { - node_id: node_service_pb2.PeerConnections( - connections=[ - node_service_pb2.PeerConnection( - to_id=conn.to_id, description=conn.description - ) - for conn in connections - ] - ) - for node_id, connections in topology.peer_graph.items() - } - if DEBUG >= 5: - print(f"CollectTopology {max_depth=} {visited=} {nodes=} {peer_graph=}") - return node_service_pb2.Topology(nodes=nodes, peer_graph=peer_graph) - - async def SendResult(self, request, context): - request_id = request.request_id - result = request.result - is_finished = request.is_finished - img = request.tensor - if DEBUG >= 5: - print( - f"Received SendResult request: {request_id=} {result=} {is_finished=}" - ) - result = list(result) - if len(img.tensor_data) > 0: - result = np.frombuffer(img.tensor_data, dtype=np.dtype(img.dtype)).reshape( - img.shape - ) - self.node.on_token.trigger_all(request_id, result, is_finished) - return node_service_pb2.Empty() - - async def SendOpaqueStatus(self, request, context): - request_id = request.request_id - status = request.status - if DEBUG >= 8: - print(f"Received SendOpaqueStatus request: {request_id=} {status=}") - self.node.on_opaque_status.trigger_all(request_id, status) - return node_service_pb2.Empty() - - async def HealthCheck(self, request, context): - return node_service_pb2.HealthCheckResponse(is_healthy=True) - - def deserialize_inference_state( - self, inference_state_proto: node_service_pb2.InferenceState - ) -> dict: - inference_state = {} - - for k, tensor_data in inference_state_proto.tensor_data.items(): - np_array = np.frombuffer( - tensor_data.tensor_data, dtype=tensor_data.dtype - ).reshape(tensor_data.shape) - inference_state[k] = mx.array(np_array) - - for k, tensor_list in inference_state_proto.tensor_list_data.items(): - inference_state[k] = [ - mx.array( - np.frombuffer(tensor.tensor_data, dtype=tensor.dtype).reshape( - tensor.shape - ) - ) - for tensor in tensor_list.tensors - ] - - if inference_state_proto.other_data_json: - other_data = json.loads(inference_state_proto.other_data_json) - inference_state.update(other_data) - - return inference_state diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/grpc/node_service.proto b/pkg/hanzo-network/src/hanzo_network/distributed/grpc/node_service.proto deleted file mode 100644 index 882a5247f..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/grpc/node_service.proto +++ /dev/null @@ -1,116 +0,0 @@ -syntax = "proto3"; - -package node_service; - -service NodeService { - rpc SendPrompt (PromptRequest) returns (Tensor) {} - rpc SendTensor (TensorRequest) returns (Tensor) {} - rpc SendExample (ExampleRequest) returns (Loss) {} - rpc CollectTopology (CollectTopologyRequest) returns (Topology) {} - rpc SendResult (SendResultRequest) returns (Empty) {} - rpc SendOpaqueStatus (SendOpaqueStatusRequest) returns (Empty) {} - rpc HealthCheck (HealthCheckRequest) returns (HealthCheckResponse) {} -} - -message Shard { - string model_id = 1; - int32 start_layer = 2; - int32 end_layer = 3; - int32 n_layers = 4; -} - -message PromptRequest { - Shard shard = 1; - string prompt = 2; - optional string request_id = 3; - optional InferenceState inference_state = 4; -} - -message TensorRequest { - Shard shard = 1; - Tensor tensor = 2; - optional string request_id = 3; - optional InferenceState inference_state = 4; -} - -message ExampleRequest { - Shard shard = 1; - Tensor example = 2; - Tensor target = 3; - Tensor length = 4; - bool train = 5; - optional string request_id = 6; -} - -message Loss { - float loss = 1; - optional Tensor grads = 2; -} - -message Tensor { - bytes tensor_data = 1; - repeated int32 shape = 2; - string dtype = 3; -} - -message TensorList { - repeated Tensor tensors = 1; -} - -message InferenceState { - map tensor_data = 1; - map tensor_list_data = 2; - string other_data_json = 3; -} - -message CollectTopologyRequest { - repeated string visited = 1; - int32 max_depth = 2; -} - -message Topology { - map nodes = 1; - map peer_graph = 2; -} - -message PeerConnection { - string to_id = 1; - optional string description = 2; -} - -message PeerConnections { - repeated PeerConnection connections = 1; -} - -message DeviceFlops { - double fp32 = 1; - double fp16 = 2; - double int8 = 3; -} - -message DeviceCapabilities { - string model = 1; - string chip = 2; - int32 memory = 3; - DeviceFlops flops = 4; -} - -message SendResultRequest { - string request_id = 1; - repeated int32 result = 2; - optional Tensor tensor = 3; - bool is_finished = 4; -} - -message SendOpaqueStatusRequest { - string request_id = 1; - string status = 2; -} - -message HealthCheckRequest {} - -message HealthCheckResponse { - bool is_healthy = 1; -} - -message Empty {} diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/grpc/node_service_pb2.py b/pkg/hanzo-network/src/hanzo_network/distributed/grpc/node_service_pb2.py deleted file mode 100644 index a4debea5d..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/grpc/node_service_pb2.py +++ /dev/null @@ -1,87 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# NO CHECKED-IN PROTOBUF GENCODE -# source: node_service.proto -# Protobuf Python Version: 5.27.2 -"""Generated protocol buffer code.""" - -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import runtime_version as _runtime_version -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -_runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, 5, 27, 2, "", "node_service.proto" -) -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x12node_service.proto\x12\x0cnode_service"S\n\x05Shard\x12\x10\n\x08model_id\x18\x01 \x01(\t\x12\x13\n\x0bstart_layer\x18\x02 \x01(\x05\x12\x11\n\tend_layer\x18\x03 \x01(\x05\x12\x10\n\x08n_layers\x18\x04 \x01(\x05"\xbb\x01\n\rPromptRequest\x12"\n\x05shard\x18\x01 \x01(\x0b\x32\x13.node_service.Shard\x12\x0e\n\x06prompt\x18\x02 \x01(\t\x12\x17\n\nrequest_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12:\n\x0finference_state\x18\x04 \x01(\x0b\x32\x1c.node_service.InferenceStateH\x01\x88\x01\x01\x42\r\n\x0b_request_idB\x12\n\x10_inference_state"\xd1\x01\n\rTensorRequest\x12"\n\x05shard\x18\x01 \x01(\x0b\x32\x13.node_service.Shard\x12$\n\x06tensor\x18\x02 \x01(\x0b\x32\x14.node_service.Tensor\x12\x17\n\nrequest_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12:\n\x0finference_state\x18\x04 \x01(\x0b\x32\x1c.node_service.InferenceStateH\x01\x88\x01\x01\x42\r\n\x0b_request_idB\x12\n\x10_inference_state"\xde\x01\n\x0e\x45xampleRequest\x12"\n\x05shard\x18\x01 \x01(\x0b\x32\x13.node_service.Shard\x12%\n\x07\x65xample\x18\x02 \x01(\x0b\x32\x14.node_service.Tensor\x12$\n\x06target\x18\x03 \x01(\x0b\x32\x14.node_service.Tensor\x12$\n\x06length\x18\x04 \x01(\x0b\x32\x14.node_service.Tensor\x12\r\n\x05train\x18\x05 \x01(\x08\x12\x17\n\nrequest_id\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\r\n\x0b_request_id"H\n\x04Loss\x12\x0c\n\x04loss\x18\x01 \x01(\x02\x12(\n\x05grads\x18\x02 \x01(\x0b\x32\x14.node_service.TensorH\x00\x88\x01\x01\x42\x08\n\x06_grads";\n\x06Tensor\x12\x13\n\x0btensor_data\x18\x01 \x01(\x0c\x12\r\n\x05shape\x18\x02 \x03(\x05\x12\r\n\x05\x64type\x18\x03 \x01(\t"3\n\nTensorList\x12%\n\x07tensors\x18\x01 \x03(\x0b\x32\x14.node_service.Tensor"\xd2\x02\n\x0eInferenceState\x12\x41\n\x0btensor_data\x18\x01 \x03(\x0b\x32,.node_service.InferenceState.TensorDataEntry\x12J\n\x10tensor_list_data\x18\x02 \x03(\x0b\x32\x30.node_service.InferenceState.TensorListDataEntry\x12\x17\n\x0fother_data_json\x18\x03 \x01(\t\x1aG\n\x0fTensorDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12#\n\x05value\x18\x02 \x01(\x0b\x32\x14.node_service.Tensor:\x02\x38\x01\x1aO\n\x13TensorListDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\'\n\x05value\x18\x02 \x01(\x0b\x32\x18.node_service.TensorList:\x02\x38\x01"<\n\x16\x43ollectTopologyRequest\x12\x0f\n\x07visited\x18\x01 \x03(\t\x12\x11\n\tmax_depth\x18\x02 \x01(\x05"\x98\x02\n\x08Topology\x12\x30\n\x05nodes\x18\x01 \x03(\x0b\x32!.node_service.Topology.NodesEntry\x12\x39\n\npeer_graph\x18\x02 \x03(\x0b\x32%.node_service.Topology.PeerGraphEntry\x1aN\n\nNodesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12/\n\x05value\x18\x02 \x01(\x0b\x32 .node_service.DeviceCapabilities:\x02\x38\x01\x1aO\n\x0ePeerGraphEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12,\n\x05value\x18\x02 \x01(\x0b\x32\x1d.node_service.PeerConnections:\x02\x38\x01"I\n\x0ePeerConnection\x12\r\n\x05to_id\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description"D\n\x0fPeerConnections\x12\x31\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x1c.node_service.PeerConnection"7\n\x0b\x44\x65viceFlops\x12\x0c\n\x04\x66p32\x18\x01 \x01(\x01\x12\x0c\n\x04\x66p16\x18\x02 \x01(\x01\x12\x0c\n\x04int8\x18\x03 \x01(\x01"k\n\x12\x44\x65viceCapabilities\x12\r\n\x05model\x18\x01 \x01(\t\x12\x0c\n\x04\x63hip\x18\x02 \x01(\t\x12\x0e\n\x06memory\x18\x03 \x01(\x05\x12(\n\x05\x66lops\x18\x04 \x01(\x0b\x32\x19.node_service.DeviceFlops"\x82\x01\n\x11SendResultRequest\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12\x0e\n\x06result\x18\x02 \x03(\x05\x12)\n\x06tensor\x18\x03 \x01(\x0b\x32\x14.node_service.TensorH\x00\x88\x01\x01\x12\x13\n\x0bis_finished\x18\x04 \x01(\x08\x42\t\n\x07_tensor"=\n\x17SendOpaqueStatusRequest\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12\x0e\n\x06status\x18\x02 \x01(\t"\x14\n\x12HealthCheckRequest")\n\x13HealthCheckResponse\x12\x12\n\nis_healthy\x18\x01 \x01(\x08"\x07\n\x05\x45mpty2\x97\x04\n\x0bNodeService\x12\x41\n\nSendPrompt\x12\x1b.node_service.PromptRequest\x1a\x14.node_service.Tensor"\x00\x12\x41\n\nSendTensor\x12\x1b.node_service.TensorRequest\x1a\x14.node_service.Tensor"\x00\x12\x41\n\x0bSendExample\x12\x1c.node_service.ExampleRequest\x1a\x12.node_service.Loss"\x00\x12Q\n\x0f\x43ollectTopology\x12$.node_service.CollectTopologyRequest\x1a\x16.node_service.Topology"\x00\x12\x44\n\nSendResult\x12\x1f.node_service.SendResultRequest\x1a\x13.node_service.Empty"\x00\x12P\n\x10SendOpaqueStatus\x12%.node_service.SendOpaqueStatusRequest\x1a\x13.node_service.Empty"\x00\x12T\n\x0bHealthCheck\x12 .node_service.HealthCheckRequest\x1a!.node_service.HealthCheckResponse"\x00\x62\x06proto3' -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "node_service_pb2", _globals) -if not _descriptor._USE_C_DESCRIPTORS: - DESCRIPTOR._loaded_options = None - _globals["_INFERENCESTATE_TENSORDATAENTRY"]._loaded_options = None - _globals["_INFERENCESTATE_TENSORDATAENTRY"]._serialized_options = b"8\001" - _globals["_INFERENCESTATE_TENSORLISTDATAENTRY"]._loaded_options = None - _globals["_INFERENCESTATE_TENSORLISTDATAENTRY"]._serialized_options = b"8\001" - _globals["_TOPOLOGY_NODESENTRY"]._loaded_options = None - _globals["_TOPOLOGY_NODESENTRY"]._serialized_options = b"8\001" - _globals["_TOPOLOGY_PEERGRAPHENTRY"]._loaded_options = None - _globals["_TOPOLOGY_PEERGRAPHENTRY"]._serialized_options = b"8\001" - _globals["_SHARD"]._serialized_start = 36 - _globals["_SHARD"]._serialized_end = 119 - _globals["_PROMPTREQUEST"]._serialized_start = 122 - _globals["_PROMPTREQUEST"]._serialized_end = 309 - _globals["_TENSORREQUEST"]._serialized_start = 312 - _globals["_TENSORREQUEST"]._serialized_end = 521 - _globals["_EXAMPLEREQUEST"]._serialized_start = 524 - _globals["_EXAMPLEREQUEST"]._serialized_end = 746 - _globals["_LOSS"]._serialized_start = 748 - _globals["_LOSS"]._serialized_end = 820 - _globals["_TENSOR"]._serialized_start = 822 - _globals["_TENSOR"]._serialized_end = 881 - _globals["_TENSORLIST"]._serialized_start = 883 - _globals["_TENSORLIST"]._serialized_end = 934 - _globals["_INFERENCESTATE"]._serialized_start = 937 - _globals["_INFERENCESTATE"]._serialized_end = 1275 - _globals["_INFERENCESTATE_TENSORDATAENTRY"]._serialized_start = 1123 - _globals["_INFERENCESTATE_TENSORDATAENTRY"]._serialized_end = 1194 - _globals["_INFERENCESTATE_TENSORLISTDATAENTRY"]._serialized_start = 1196 - _globals["_INFERENCESTATE_TENSORLISTDATAENTRY"]._serialized_end = 1275 - _globals["_COLLECTTOPOLOGYREQUEST"]._serialized_start = 1277 - _globals["_COLLECTTOPOLOGYREQUEST"]._serialized_end = 1337 - _globals["_TOPOLOGY"]._serialized_start = 1340 - _globals["_TOPOLOGY"]._serialized_end = 1620 - _globals["_TOPOLOGY_NODESENTRY"]._serialized_start = 1461 - _globals["_TOPOLOGY_NODESENTRY"]._serialized_end = 1539 - _globals["_TOPOLOGY_PEERGRAPHENTRY"]._serialized_start = 1541 - _globals["_TOPOLOGY_PEERGRAPHENTRY"]._serialized_end = 1620 - _globals["_PEERCONNECTION"]._serialized_start = 1622 - _globals["_PEERCONNECTION"]._serialized_end = 1695 - _globals["_PEERCONNECTIONS"]._serialized_start = 1697 - _globals["_PEERCONNECTIONS"]._serialized_end = 1765 - _globals["_DEVICEFLOPS"]._serialized_start = 1767 - _globals["_DEVICEFLOPS"]._serialized_end = 1822 - _globals["_DEVICECAPABILITIES"]._serialized_start = 1824 - _globals["_DEVICECAPABILITIES"]._serialized_end = 1931 - _globals["_SENDRESULTREQUEST"]._serialized_start = 1934 - _globals["_SENDRESULTREQUEST"]._serialized_end = 2064 - _globals["_SENDOPAQUESTATUSREQUEST"]._serialized_start = 2066 - _globals["_SENDOPAQUESTATUSREQUEST"]._serialized_end = 2127 - _globals["_HEALTHCHECKREQUEST"]._serialized_start = 2129 - _globals["_HEALTHCHECKREQUEST"]._serialized_end = 2149 - _globals["_HEALTHCHECKRESPONSE"]._serialized_start = 2151 - _globals["_HEALTHCHECKRESPONSE"]._serialized_end = 2192 - _globals["_EMPTY"]._serialized_start = 2194 - _globals["_EMPTY"]._serialized_end = 2201 - _globals["_NODESERVICE"]._serialized_start = 2204 - _globals["_NODESERVICE"]._serialized_end = 2739 -# @@protoc_insertion_point(module_scope) diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/grpc/node_service_pb2_grpc.py b/pkg/hanzo-network/src/hanzo_network/distributed/grpc/node_service_pb2_grpc.py deleted file mode 100644 index d413a3411..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/grpc/node_service_pb2_grpc.py +++ /dev/null @@ -1,389 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -"""Client and server classes corresponding to protobuf-defined services.""" - -import grpc - -from . import node_service_pb2 as node__service__pb2 - -GRPC_GENERATED_VERSION = "1.67.0" -GRPC_VERSION = grpc.__version__ -_version_not_supported = False - -try: - from grpc._utilities import first_version_is_lower - - _version_not_supported = first_version_is_lower( - GRPC_VERSION, GRPC_GENERATED_VERSION - ) -except ImportError: - _version_not_supported = True - -if _version_not_supported: - raise RuntimeError( - f"The grpc package installed is at version {GRPC_VERSION}," - + " but the generated code in node_service_pb2_grpc.py depends on" - + f" grpcio>={GRPC_GENERATED_VERSION}." - + f" Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}" - + f" or downgrade your generated code using grpcio-tools<={GRPC_VERSION}." - ) - - -class NodeServiceStub(object): - """Missing associated documentation comment in .proto file.""" - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.SendPrompt = channel.unary_unary( - "/node_service.NodeService/SendPrompt", - request_serializer=node__service__pb2.PromptRequest.SerializeToString, - response_deserializer=node__service__pb2.Tensor.FromString, - _registered_method=True, - ) - self.SendTensor = channel.unary_unary( - "/node_service.NodeService/SendTensor", - request_serializer=node__service__pb2.TensorRequest.SerializeToString, - response_deserializer=node__service__pb2.Tensor.FromString, - _registered_method=True, - ) - self.SendExample = channel.unary_unary( - "/node_service.NodeService/SendExample", - request_serializer=node__service__pb2.ExampleRequest.SerializeToString, - response_deserializer=node__service__pb2.Loss.FromString, - _registered_method=True, - ) - self.CollectTopology = channel.unary_unary( - "/node_service.NodeService/CollectTopology", - request_serializer=node__service__pb2.CollectTopologyRequest.SerializeToString, - response_deserializer=node__service__pb2.Topology.FromString, - _registered_method=True, - ) - self.SendResult = channel.unary_unary( - "/node_service.NodeService/SendResult", - request_serializer=node__service__pb2.SendResultRequest.SerializeToString, - response_deserializer=node__service__pb2.Empty.FromString, - _registered_method=True, - ) - self.SendOpaqueStatus = channel.unary_unary( - "/node_service.NodeService/SendOpaqueStatus", - request_serializer=node__service__pb2.SendOpaqueStatusRequest.SerializeToString, - response_deserializer=node__service__pb2.Empty.FromString, - _registered_method=True, - ) - self.HealthCheck = channel.unary_unary( - "/node_service.NodeService/HealthCheck", - request_serializer=node__service__pb2.HealthCheckRequest.SerializeToString, - response_deserializer=node__service__pb2.HealthCheckResponse.FromString, - _registered_method=True, - ) - - -class NodeServiceServicer(object): - """Missing associated documentation comment in .proto file.""" - - def SendPrompt(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - def SendTensor(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - def SendExample(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - def CollectTopology(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - def SendResult(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - def SendOpaqueStatus(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - def HealthCheck(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - -def add_NodeServiceServicer_to_server(servicer, server): - rpc_method_handlers = { - "SendPrompt": grpc.unary_unary_rpc_method_handler( - servicer.SendPrompt, - request_deserializer=node__service__pb2.PromptRequest.FromString, - response_serializer=node__service__pb2.Tensor.SerializeToString, - ), - "SendTensor": grpc.unary_unary_rpc_method_handler( - servicer.SendTensor, - request_deserializer=node__service__pb2.TensorRequest.FromString, - response_serializer=node__service__pb2.Tensor.SerializeToString, - ), - "SendExample": grpc.unary_unary_rpc_method_handler( - servicer.SendExample, - request_deserializer=node__service__pb2.ExampleRequest.FromString, - response_serializer=node__service__pb2.Loss.SerializeToString, - ), - "CollectTopology": grpc.unary_unary_rpc_method_handler( - servicer.CollectTopology, - request_deserializer=node__service__pb2.CollectTopologyRequest.FromString, - response_serializer=node__service__pb2.Topology.SerializeToString, - ), - "SendResult": grpc.unary_unary_rpc_method_handler( - servicer.SendResult, - request_deserializer=node__service__pb2.SendResultRequest.FromString, - response_serializer=node__service__pb2.Empty.SerializeToString, - ), - "SendOpaqueStatus": grpc.unary_unary_rpc_method_handler( - servicer.SendOpaqueStatus, - request_deserializer=node__service__pb2.SendOpaqueStatusRequest.FromString, - response_serializer=node__service__pb2.Empty.SerializeToString, - ), - "HealthCheck": grpc.unary_unary_rpc_method_handler( - servicer.HealthCheck, - request_deserializer=node__service__pb2.HealthCheckRequest.FromString, - response_serializer=node__service__pb2.HealthCheckResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - "node_service.NodeService", rpc_method_handlers - ) - server.add_generic_rpc_handlers((generic_handler,)) - server.add_registered_method_handlers( - "node_service.NodeService", rpc_method_handlers - ) - - -# This class is part of an EXPERIMENTAL API. -class NodeService(object): - """Missing associated documentation comment in .proto file.""" - - @staticmethod - def SendPrompt( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_unary( - request, - target, - "/node_service.NodeService/SendPrompt", - node__service__pb2.PromptRequest.SerializeToString, - node__service__pb2.Tensor.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True, - ) - - @staticmethod - def SendTensor( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_unary( - request, - target, - "/node_service.NodeService/SendTensor", - node__service__pb2.TensorRequest.SerializeToString, - node__service__pb2.Tensor.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True, - ) - - @staticmethod - def SendExample( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_unary( - request, - target, - "/node_service.NodeService/SendExample", - node__service__pb2.ExampleRequest.SerializeToString, - node__service__pb2.Loss.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True, - ) - - @staticmethod - def CollectTopology( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_unary( - request, - target, - "/node_service.NodeService/CollectTopology", - node__service__pb2.CollectTopologyRequest.SerializeToString, - node__service__pb2.Topology.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True, - ) - - @staticmethod - def SendResult( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_unary( - request, - target, - "/node_service.NodeService/SendResult", - node__service__pb2.SendResultRequest.SerializeToString, - node__service__pb2.Empty.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True, - ) - - @staticmethod - def SendOpaqueStatus( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_unary( - request, - target, - "/node_service.NodeService/SendOpaqueStatus", - node__service__pb2.SendOpaqueStatusRequest.SerializeToString, - node__service__pb2.Empty.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True, - ) - - @staticmethod - def HealthCheck( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_unary( - request, - target, - "/node_service.NodeService/HealthCheck", - node__service__pb2.HealthCheckRequest.SerializeToString, - node__service__pb2.HealthCheckResponse.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True, - ) diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/grpc_server.py b/pkg/hanzo-network/src/hanzo_network/distributed/grpc_server.py deleted file mode 100644 index b208aef32..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/grpc_server.py +++ /dev/null @@ -1,68 +0,0 @@ -"""Simplified gRPC server for distributed Hanzo networks.""" - -from typing import Any, Dict - - -class GRPCServer: - """Simplified gRPC server for agent network communication. - - This is a minimal implementation that simulates gRPC communication - for testing purposes without requiring full gRPC infrastructure. - """ - - def __init__(self, node_id: str, port: int): - self.node_id = node_id - self.port = port - self.is_running = False - self.handlers: Dict[str, Any] = {} - - async def start(self) -> None: - """Start the gRPC server.""" - self.is_running = True - print(f"gRPC server started for node {self.node_id} on port {self.port}") - - # Register default handlers - self.handlers["list_agents"] = self._handle_list_agents - self.handlers["execute_agent"] = self._handle_execute_agent - self.handlers["health_check"] = self._handle_health_check - - async def stop(self) -> None: - """Stop the gRPC server.""" - self.is_running = False - print(f"gRPC server stopped for node {self.node_id}") - - def register_handler(self, action: str, handler) -> None: - """Register a handler for an action.""" - self.handlers[action] = handler - - async def handle_request(self, request: Dict[str, Any]) -> Dict[str, Any]: - """Handle an incoming request.""" - if not self.is_running: - return {"success": False, "error": "Server not running"} - - action = request.get("action") - if not action: - return {"success": False, "error": "No action specified"} - - handler = self.handlers.get(action) - if not handler: - return {"success": False, "error": f"Unknown action: {action}"} - - try: - return await handler(request) - except Exception as e: - return {"success": False, "error": f"Handler error: {str(e)}"} - - async def _handle_list_agents(self, request: Dict[str, Any]) -> Dict[str, Any]: - """Handle list_agents request.""" - # This should be overridden by the network implementation - return {"success": True, "agents": []} - - async def _handle_execute_agent(self, request: Dict[str, Any]) -> Dict[str, Any]: - """Handle execute_agent request.""" - # This should be overridden by the network implementation - return {"success": False, "error": "Not implemented"} - - async def _handle_health_check(self, request: Dict[str, Any]) -> Dict[str, Any]: - """Handle health check request.""" - return {"success": True, "healthy": True, "node_id": self.node_id} diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/manual/__init__.py b/pkg/hanzo-network/src/hanzo_network/distributed/manual/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/manual/manual_discovery.py b/pkg/hanzo-network/src/hanzo_network/distributed/manual/manual_discovery.py deleted file mode 100644 index af560cace..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/manual/manual_discovery.py +++ /dev/null @@ -1,139 +0,0 @@ -import asyncio -import os -from concurrent.futures import ThreadPoolExecutor -from typing import Callable, Dict, List, Optional - -from .helpers import DEBUG_DISCOVERY -from .networking.discovery import Discovery -from .networking.manual.network_topology_config import NetworkTopology, PeerConfig -from .networking.peer_handle import PeerHandle -from .topology.device_capabilities import DeviceCapabilities - - -class ManualDiscovery(Discovery): - def __init__( - self, - network_config_path: str, - node_id: str, - create_peer_handle: Callable[[str, str, str, DeviceCapabilities], PeerHandle], - ): - self.network_config_path = network_config_path - self.node_id = node_id - self.create_peer_handle = create_peer_handle - - self.listen_task = None - self.known_peers: Dict[str, PeerHandle] = {} - - self._cached_peers: Dict[str, PeerConfig] = {} - self._last_modified_time: Optional[float] = None - self._file_executor = ThreadPoolExecutor(max_workers=1) - - async def start(self) -> None: - self.listen_task = asyncio.create_task(self.task_find_peers_from_config()) - - async def stop(self) -> None: - if self.listen_task: - self.listen_task.cancel() - self._file_executor.shutdown(wait=True) - - async def discover_peers(self, wait_for_peers: int = 0) -> List[PeerHandle]: - if wait_for_peers > 0: - while len(self.known_peers) < wait_for_peers: - if DEBUG_DISCOVERY >= 2: - print( - f"Current peers: {len(self.known_peers)}/{wait_for_peers}. Waiting for more peers..." - ) - await asyncio.sleep(0.1) - if DEBUG_DISCOVERY >= 2: - print( - f"Discovered peers: {[peer.id() for peer in self.known_peers.values()]}" - ) - return list(self.known_peers.values()) - - async def task_find_peers_from_config(self): - if DEBUG_DISCOVERY >= 2: - print("Starting task to find peers from config...") - while True: - peers_from_config = await self._get_peers() - new_known_peers = {} - for peer_id, peer_config in peers_from_config.items(): - try: - if DEBUG_DISCOVERY >= 2: - print( - f"Checking peer {peer_id=} at {peer_config.address}:{peer_config.port}" - ) - peer = self.known_peers.get(peer_id) - if not peer: - if DEBUG_DISCOVERY >= 2: - print(f"{peer_id=} not found in known peers. Adding.") - peer = self.create_peer_handle( - peer_id, - f"{peer_config.address}:{peer_config.port}", - "MAN", - peer_config.device_capabilities, - ) - is_healthy = await peer.health_check() - if is_healthy: - if DEBUG_DISCOVERY >= 2: - print( - f"{peer_id=} at {peer_config.address}:{peer_config.port} is healthy." - ) - new_known_peers[peer_id] = peer - elif DEBUG_DISCOVERY >= 2: - print( - f"{peer_id=} at {peer_config.address}:{peer_config.port} is not healthy. Removing." - ) - except Exception as e: - if DEBUG_DISCOVERY >= 2: - print( - f"Exception occurred when attempting to add {peer_id=}: {e}" - ) - self.known_peers = new_known_peers - await asyncio.sleep(5.0) - - if DEBUG_DISCOVERY >= 2: - print( - f"Current known peers: {[peer.id() for peer in self.known_peers.values()]}" - ) - - async def _get_peers(self): - try: - loop = asyncio.get_running_loop() - current_mtime = await loop.run_in_executor( - self._file_executor, os.path.getmtime, self.network_config_path - ) - - if ( - self._cached_peers is not None - and self._last_modified_time is not None - and current_mtime <= self._last_modified_time - ): - return self._cached_peers - - topology = await loop.run_in_executor( - self._file_executor, NetworkTopology.from_path, self.network_config_path - ) - - if self.node_id not in topology.peers: - raise ValueError( - f"Node ID {self.node_id} not found in network config file " - f"{self.network_config_path}. Please run with `node_id` set to " - f"one of the keys in the config file: {[k for k, _ in topology.peers]}" - ) - - peers_in_network = topology.peers - peers_in_network.pop(self.node_id) - - self._cached_peers = peers_in_network - self._last_modified_time = current_mtime - - return peers_in_network - - except Exception as e: - if DEBUG_DISCOVERY >= 2: - print( - f"Error when loading network config file from {self.network_config_path}. " - f"Please update the config file in order to successfully discover peers. " - f"Exception: {e}" - ) - return self._cached_peers diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/manual/network_topology_config.py b/pkg/hanzo-network/src/hanzo_network/distributed/manual/network_topology_config.py deleted file mode 100644 index 9156dbabf..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/manual/network_topology_config.py +++ /dev/null @@ -1,35 +0,0 @@ -from typing import Dict - -from pydantic import BaseModel, ValidationError - -from .topology.device_capabilities import DeviceCapabilities - - -class PeerConfig(BaseModel): - address: str - port: int - device_capabilities: DeviceCapabilities - - -class NetworkTopology(BaseModel): - """Configuration of the network. A collection outlining all nodes in the network, including the node this is running from.""" - - peers: Dict[str, PeerConfig] - """ - node_id to PeerConfig. The node_id is used to identify the peer in the discovery process. The node that this is running from should be included in this dict. - """ - - @classmethod - def from_path(cls, path: str) -> "NetworkTopology": - try: - with open(path, "r") as f: - config_data = f.read() - except FileNotFoundError as e: - raise FileNotFoundError(f"Config file not found at {path}") from e - - try: - return cls.model_validate_json(config_data) - except ValidationError as e: - raise ValueError( - f"Error validating network topology config from {path}: {e}" - ) from e diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_data/invalid_config.json b/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_data/invalid_config.json deleted file mode 100644 index 283feadf3..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_data/invalid_config.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "peers": { - "node1": { - "address": "localhost", - "device_capabilities": { - "model": "Unknown Model", - "chip": "Unknown Chip", - "memory": 0, - "flops": { - "fp32": 0, - "fp16": 0, - "int8": 0 - } - } - } - } -} diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_data/invalid_json.json b/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_data/invalid_json.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_data/test_config.json b/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_data/test_config.json deleted file mode 100644 index 54eced720..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_data/test_config.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "peers": { - "node1": { - "address": "localhost", - "port": 50051, - "device_capabilities": { - "model": "Unknown Model", - "chip": "Unknown Chip", - "memory": 0, - "flops": { - "fp32": 0, - "fp16": 0, - "int8": 0 - } - } - }, - "node2": { - "address": "localhost", - "port": 50052, - "device_capabilities": { - "model": "Unknown Model", - "chip": "Unknown Chip", - "memory": 0, - "flops": { - "fp32": 0, - "fp16": 0, - "int8": 0 - } - } - } - } -} \ No newline at end of file diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_data/test_config_single_node.json b/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_data/test_config_single_node.json deleted file mode 100644 index 81a0670f7..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_data/test_config_single_node.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "peers": { - "node1": { - "address": "localhost", - "port": 50051, - "device_capabilities": { - "model": "Unknown Model", - "chip": "Unknown Chip", - "memory": 0, - "flops": { - "fp32": 0, - "fp16": 0, - "int8": 0 - } - } - } - } -} diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_manual_discovery.py b/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_manual_discovery.py deleted file mode 100644 index 659d61776..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_manual_discovery.py +++ /dev/null @@ -1,190 +0,0 @@ -import asyncio -import json -import unittest -from unittest import mock - -from .networking.grpc.grpc_peer_handle import GRPCPeerHandle -from .networking.grpc.grpc_server import GRPCServer -from .networking.manual.manual_discovery import ManualDiscovery -from .networking.manual.network_topology_config import NetworkTopology -from .orchestration.node import Node - -root_path = "./exo/networking/manual/test_data/test_config.json" - - -class TestSingleNodeManualDiscovery(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - self.peer1 = mock.AsyncMock() - self.peer1.connect = mock.AsyncMock() - self.discovery1 = ManualDiscovery( - root_path, - "node1", - create_peer_handle=lambda peer_id, address, description, device_capabilities: self.peer1, - ) - await self.discovery1.start() - - async def asyncTearDown(self): - await self.discovery1.stop() - - async def test_discovery(self): - peers1 = await self.discovery1.discover_peers(wait_for_peers=0) - assert len(peers1) == 0 - - self.peer1.connect.assert_not_called() - - -class TestManualDiscovery(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - self.peer1 = mock.AsyncMock() - self.peer2 = mock.AsyncMock() - self.peer1.connect = mock.AsyncMock() - self.peer2.connect = mock.AsyncMock() - self.discovery1 = ManualDiscovery( - root_path, - "node1", - create_peer_handle=lambda peer_id, address, description, device_capabilities: self.peer1, - ) - self.discovery2 = ManualDiscovery( - root_path, - "node2", - create_peer_handle=lambda peer_id, address, description, device_capabilities: self.peer2, - ) - await self.discovery1.start() - await self.discovery2.start() - - async def asyncTearDown(self): - await self.discovery1.stop() - await self.discovery2.stop() - - async def test_discovery(self): - peers1 = await self.discovery1.discover_peers(wait_for_peers=1) - assert len(peers1) == 1 - peers2 = await self.discovery2.discover_peers(wait_for_peers=1) - assert len(peers2) == 1 - - # connect has to be explicitly called after discovery - self.peer1.connect.assert_not_called() - self.peer2.connect.assert_not_called() - - -class TestManualDiscoveryWithGRPCPeerHandle(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - config = NetworkTopology.from_path(root_path) - - self.node1 = mock.AsyncMock(spec=Node) - self.node2 = mock.AsyncMock(spec=Node) - self.server1 = GRPCServer( - self.node1, config.peers["node1"].address, config.peers["node1"].port - ) - self.server2 = GRPCServer( - self.node2, config.peers["node2"].address, config.peers["node2"].port - ) - await self.server1.start() - await self.server2.start() - self.discovery1 = ManualDiscovery( - root_path, - "node1", - create_peer_handle=lambda peer_id, address, description, device_capabilities: GRPCPeerHandle( - peer_id, address, description, device_capabilities - ), - ) - self.discovery2 = ManualDiscovery( - root_path, - "node2", - create_peer_handle=lambda peer_id, address, description, device_capabilities: GRPCPeerHandle( - peer_id, address, description, device_capabilities - ), - ) - await self.discovery1.start() - await self.discovery2.start() - - async def asyncTearDown(self): - await self.discovery1.stop() - await self.discovery2.stop() - await self.server1.stop() - await self.server2.stop() - - async def test_grpc_discovery(self): - peers1 = await self.discovery1.discover_peers(wait_for_peers=1) - assert len(peers1) == 1 - peers2 = await self.discovery2.discover_peers(wait_for_peers=1) - assert len(peers2) == 1 - - # Connect - await peers1[0].connect() - await peers2[0].connect() - self.assertTrue(await peers1[0].is_connected()) - self.assertTrue(await peers2[0].is_connected()) - - # Kill server1 - await self.server1.stop() - - self.assertTrue(await peers1[0].is_connected()) - self.assertFalse(await peers2[0].is_connected()) - - # Kill server2 - await self.server2.stop() - - self.assertFalse(await peers1[0].is_connected()) - self.assertFalse(await peers2[0].is_connected()) - - async def test_dynamic_config_update(self): - initial_peers = await self.discovery1.discover_peers(wait_for_peers=1) - self.assertEqual(len(initial_peers), 1) - - # Save original config for cleanup - with open(root_path, "r") as f: - original_config = json.load(f) - - try: - updated_config = { - "peers": { - **original_config["peers"], - "node3": { - "address": "localhost", - "port": 50053, - "device_capabilities": { - "model": "Unknown Model", - "chip": "Unknown Chip", - "memory": 0, - "flops": {"fp32": 0, "fp16": 0, "int8": 0}, - }, - }, - } - } - - with open(root_path, "w") as f: - json.dump(updated_config, f, indent=2) - - node3 = mock.AsyncMock(spec=Node) - server3 = GRPCServer(node3, "localhost", 50053) - await server3.start() - - try: - # Wait for the config to be reloaded - await asyncio.sleep(1.5) - - updated_peers = await self.discovery1.discover_peers(wait_for_peers=2) - self.assertEqual(len(updated_peers), 2) - - for peer in updated_peers: - await peer.connect() - self.assertTrue(await peer.is_connected()) - - finally: - await server3.stop() - - finally: - # Restore the original config file - with open(root_path, "w") as f: - json.dump(original_config, f, indent=2) - - # Wait for the config to be reloaded again - await asyncio.sleep(1.5) - - updated_peers = await self.discovery1.discover_peers(wait_for_peers=1) - self.assertEqual(len(updated_peers), 1) - - -if __name__ == "__main__": - asyncio.run(unittest.main()) diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_network_topology_config.py b/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_network_topology_config.py deleted file mode 100644 index bea60f2ae..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/manual/test_network_topology_config.py +++ /dev/null @@ -1,56 +0,0 @@ -import unittest - -from .networking.manual.network_topology_config import NetworkTopology - -root_path = "./exo/networking/manual/test_data/" - - -class TestNetworkTopologyConfig(unittest.TestCase): - def test_from_path_invalid_path(self): - with self.assertRaises(FileNotFoundError) as e: - NetworkTopology.from_path("invalid_path") - self.assertEqual(str(e.exception), "Config file not found at invalid_path") - - def test_from_path_invalid_json(self): - with self.assertRaises(ValueError) as e: - NetworkTopology.from_path(root_path + "invalid_json.json") - self.assertIn("Error validating network topology config from", str(e.exception)) - self.assertIn( - "1 validation error for NetworkTopology\n Invalid JSON: EOF while parsing a value at line 1 column 0", - str(e.exception), - ) - - def test_from_path_invalid_config(self): - with self.assertRaises(ValueError) as e: - NetworkTopology.from_path(root_path + "invalid_config.json") - self.assertIn("Error validating network topology config from", str(e.exception)) - self.assertIn("port\n Field required", str(e.exception)) - - def test_from_path_valid(self): - config = NetworkTopology.from_path(root_path + "test_config.json") - - self.assertEqual(config.peers["node1"].port, 50051) - self.assertEqual( - config.peers["node1"].device_capabilities.model, "Unknown Model" - ) - self.assertEqual(config.peers["node1"].address, "localhost") - self.assertEqual(config.peers["node1"].device_capabilities.chip, "Unknown Chip") - self.assertEqual(config.peers["node1"].device_capabilities.memory, 0) - self.assertEqual(config.peers["node1"].device_capabilities.flops.fp32, 0) - self.assertEqual(config.peers["node1"].device_capabilities.flops.fp16, 0) - self.assertEqual(config.peers["node1"].device_capabilities.flops.int8, 0) - - self.assertEqual(config.peers["node2"].port, 50052) - self.assertEqual( - config.peers["node2"].device_capabilities.model, "Unknown Model" - ) - self.assertEqual(config.peers["node2"].address, "localhost") - self.assertEqual(config.peers["node2"].device_capabilities.chip, "Unknown Chip") - self.assertEqual(config.peers["node2"].device_capabilities.memory, 0) - self.assertEqual(config.peers["node2"].device_capabilities.flops.fp32, 0) - self.assertEqual(config.peers["node2"].device_capabilities.flops.fp16, 0) - self.assertEqual(config.peers["node2"].device_capabilities.flops.int8, 0) - - -if __name__ == "__main__": - unittest.main() diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/minimal_discovery.py b/pkg/hanzo-network/src/hanzo_network/distributed/minimal_discovery.py deleted file mode 100644 index d335c3910..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/minimal_discovery.py +++ /dev/null @@ -1,79 +0,0 @@ -"""Minimal discovery implementation for testing.""" - -from typing import Any, Dict, List, Optional - -from ..device_capabilities import DeviceCapabilities -from .discovery import Discovery -from .simplified_peer_handle import SimplifiedPeerHandle - - -class MinimalPeerHandle(SimplifiedPeerHandle): - """Minimal peer handle implementation.""" - - def __init__( - self, - peer_id: str, - address: str, - capabilities: Optional[DeviceCapabilities] = None, - ): - self._id = peer_id - self._address = address - self._device_capabilities = capabilities - self._connected = True - - @property - def id(self) -> str: - return self._id - - @property - def address(self) -> str: - return self._address - - @property - def device_capabilities(self) -> Optional[DeviceCapabilities]: - return self._device_capabilities - - async def send_request(self, request: Dict[str, Any]) -> Dict[str, Any]: - """Send a request to the peer.""" - # For testing, just echo back - return { - "success": True, - "peer_id": self._id, - "request": request, - "response": f"Response from {self._id}", - } - - async def is_connected(self) -> bool: - return self._connected - - -class MinimalDiscovery(Discovery): - """Minimal discovery for testing.""" - - def __init__(self, node_id: str, device_capabilities: DeviceCapabilities, **kwargs): - self.node_id = node_id - self.device_capabilities = device_capabilities - self.is_running = False - self.discovered_peers: List[MinimalPeerHandle] = [] - - async def start(self) -> None: - """Start discovery.""" - self.is_running = True - print(f"MinimalDiscovery started for node {self.node_id}") - - async def stop(self) -> None: - """Stop discovery.""" - self.is_running = False - print(f"MinimalDiscovery stopped for node {self.node_id}") - - async def discover_peers( - self, wait_for_peers: int = 0 - ) -> List[SimplifiedPeerHandle]: - """Discover peers (returns empty list for now).""" - # For testing, we don't actually discover peers - # Real implementation would use UDP broadcast, mDNS, etc. - return self.discovered_peers - - def add_test_peer(self, peer: MinimalPeerHandle) -> None: - """Add a test peer manually.""" - self.discovered_peers.append(peer) diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/peer_handle.py b/pkg/hanzo-network/src/hanzo_network/distributed/peer_handle.py deleted file mode 100644 index c750935dd..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/peer_handle.py +++ /dev/null @@ -1,64 +0,0 @@ -from abc import ABC, abstractmethod -from typing import List, Optional - -import numpy as np - -from ..inference.shard import Shard -from ..topology.device_capabilities import DeviceCapabilities -from ..topology.topology import Topology - - -class PeerHandle(ABC): - @abstractmethod - def id(self) -> str: - pass - - @abstractmethod - def addr(self) -> str: - pass - - @abstractmethod - def description(self) -> str: - pass - - @abstractmethod - def device_capabilities(self) -> DeviceCapabilities: - pass - - @abstractmethod - async def connect(self) -> None: - pass - - @abstractmethod - async def is_connected(self) -> bool: - pass - - @abstractmethod - async def disconnect(self) -> None: - pass - - @abstractmethod - async def health_check(self) -> bool: - pass - - @abstractmethod - async def send_prompt( - self, shard: Shard, prompt: str, request_id: Optional[str] = None - ) -> Optional[np.array]: - pass - - @abstractmethod - async def send_tensor( - self, shard: Shard, tensor: np.array, request_id: Optional[str] = None - ) -> Optional[np.array]: - pass - - @abstractmethod - async def send_result( - self, request_id: str, result: List[int], is_finished: bool - ) -> None: - pass - - @abstractmethod - async def collect_topology(self, visited: set[str], max_depth: int) -> Topology: - pass diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/server.py b/pkg/hanzo-network/src/hanzo_network/distributed/server.py deleted file mode 100644 index fad53f1b1..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/server.py +++ /dev/null @@ -1,11 +0,0 @@ -from abc import ABC, abstractmethod - - -class Server(ABC): - @abstractmethod - async def start(self) -> None: - pass - - @abstractmethod - async def stop(self) -> None: - pass diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/simplified_peer_handle.py b/pkg/hanzo-network/src/hanzo_network/distributed/simplified_peer_handle.py deleted file mode 100644 index 7772dcb13..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/simplified_peer_handle.py +++ /dev/null @@ -1,42 +0,0 @@ -"""Simplified peer handle for distributed networking without full inference support.""" - -from abc import ABC, abstractmethod -from typing import Any, Dict, Optional - -from ..topology.device_capabilities import DeviceCapabilities - - -class SimplifiedPeerHandle(ABC): - """Simplified peer handle for basic distributed networking.""" - - @property - @abstractmethod - def id(self) -> str: - """Get peer ID.""" - pass - - @property - @abstractmethod - def address(self) -> str: - """Get peer address.""" - pass - - @property - @abstractmethod - def device_capabilities(self) -> Optional[DeviceCapabilities]: - """Get device capabilities.""" - pass - - @abstractmethod - async def send_request(self, request: Dict[str, Any]) -> Dict[str, Any]: - """Send a request to the peer.""" - pass - - @abstractmethod - async def is_connected(self) -> bool: - """Check if peer is connected.""" - pass - - def description(self) -> str: - """Get peer description.""" - return f"{self.id} at {self.address}" diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/tailscale/__init__.py b/pkg/hanzo-network/src/hanzo_network/distributed/tailscale/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/tailscale/tailscale_discovery.py b/pkg/hanzo-network/src/hanzo_network/distributed/tailscale/tailscale_discovery.py deleted file mode 100644 index 5c10c602b..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/tailscale/tailscale_discovery.py +++ /dev/null @@ -1,272 +0,0 @@ -import asyncio -import time -import traceback -from typing import Callable, Dict, List, Tuple - -from .helpers import DEBUG, DEBUG_DISCOVERY -from .networking.discovery import Discovery -from .networking.peer_handle import PeerHandle -from .tailscale_helpers import ( - Device, - get_device_attributes, - get_device_id, - get_tailscale_devices, - update_device_attributes, -) -from .topology.device_capabilities import ( - UNKNOWN_DEVICE_CAPABILITIES, - DeviceCapabilities, - device_capabilities, -) - - -class TailscaleDiscovery(Discovery): - def __init__( - self, - node_id: str, - node_port: int, - create_peer_handle: Callable[[str, str, str, DeviceCapabilities], PeerHandle], - discovery_interval: int = 5, - discovery_timeout: int = 30, - update_interval: int = 15, - device_capabilities: DeviceCapabilities = UNKNOWN_DEVICE_CAPABILITIES, - tailscale_api_key: str = None, - tailnet: str = None, - allowed_node_ids: List[str] = None, - ): - self.node_id = node_id - self.node_port = node_port - self.create_peer_handle = create_peer_handle - self.discovery_interval = discovery_interval - self.discovery_timeout = discovery_timeout - self.update_interval = update_interval - self.device_capabilities = device_capabilities - self.known_peers: Dict[str, Tuple[PeerHandle, float, float]] = {} - self.discovery_task = None - self.cleanup_task = None - self.tailscale_api_key = tailscale_api_key - self.tailnet = tailnet - self.allowed_node_ids = allowed_node_ids - self._device_id = None - self.update_task = None - - async def start(self): - self.device_capabilities = await device_capabilities() - self.discovery_task = asyncio.create_task(self.task_discover_peers()) - self.cleanup_task = asyncio.create_task(self.task_cleanup_peers()) - self.update_task = asyncio.create_task( - self.task_update_device_posture_attributes() - ) - - async def task_update_device_posture_attributes(self): - while True: - try: - await self.update_device_posture_attributes() - if DEBUG_DISCOVERY >= 2: - print("Updated device posture attributes") - except Exception as e: - print(f"Error updating device posture attributes: {e}") - print(traceback.format_exc()) - finally: - await asyncio.sleep(self.update_interval) - - async def get_device_id(self): - if self._device_id: - return self._device_id - self._device_id = await get_device_id() - return self._device_id - - async def update_device_posture_attributes(self): - await update_device_attributes( - await self.get_device_id(), - self.tailscale_api_key, - self.node_id, - self.node_port, - self.device_capabilities, - ) - - async def task_discover_peers(self): - while True: - try: - devices: dict[str, Device] = await get_tailscale_devices( - self.tailscale_api_key, self.tailnet - ) - current_time = time.time() - - active_devices = { - name: device - for name, device in devices.items() - if device.last_seen is not None - and (current_time - device.last_seen.timestamp()) < 30 - } - - if DEBUG_DISCOVERY >= 4: - print(f"Found tailscale devices: {devices}") - if DEBUG_DISCOVERY >= 2: - print( - f"Active tailscale devices: {len(active_devices)}/{len(devices)}" - ) - if DEBUG_DISCOVERY >= 2: - print( - "Time since last seen tailscale devices", - [ - (current_time - device.last_seen.timestamp()) - for device in devices.values() - ], - ) - - for device in active_devices.values(): - if device.name == self.node_id: - continue - peer_host = device.addresses[0] - peer_id, peer_port, device_capabilities = ( - await get_device_attributes( - device.device_id, self.tailscale_api_key - ) - ) - if not peer_id: - if DEBUG_DISCOVERY >= 4: - print( - f"{device.device_id} does not have exo node attributes. skipping." - ) - continue - - if self.allowed_node_ids and peer_id not in self.allowed_node_ids: - if DEBUG_DISCOVERY >= 2: - print( - f"Ignoring peer {peer_id} as it's not in the allowed node IDs list" - ) - continue - - if ( - peer_id not in self.known_peers - or self.known_peers[peer_id][0].addr() - != f"{peer_host}:{peer_port}" - ): - new_peer_handle = self.create_peer_handle( - peer_id, - f"{peer_host}:{peer_port}", - "TS", - device_capabilities, - ) - if not await new_peer_handle.health_check(): - if DEBUG >= 1: - print( - f"Peer {peer_id} at {peer_host}:{peer_port} is not healthy. Skipping." - ) - continue - - if DEBUG >= 1: - print( - f"Adding {peer_id=} at {peer_host}:{peer_port}. Replace existing peer_id: {peer_id in self.known_peers}" - ) - self.known_peers[peer_id] = ( - new_peer_handle, - current_time, - current_time, - ) - else: - if not await self.known_peers[peer_id][0].health_check(): - if DEBUG >= 1: - print( - f"Peer {peer_id} at {peer_host}:{peer_port} is not healthy. Removing." - ) - if peer_id in self.known_peers: - del self.known_peers[peer_id] - continue - self.known_peers[peer_id] = ( - self.known_peers[peer_id][0], - self.known_peers[peer_id][1], - current_time, - ) - - except Exception as e: - print(f"Error in discover peers: {e}") - print(traceback.format_exc()) - finally: - await asyncio.sleep(self.discovery_interval) - - async def stop(self): - if self.discovery_task: - self.discovery_task.cancel() - if self.cleanup_task: - self.cleanup_task.cancel() - if self.update_task: - self.update_task.cancel() - if self.discovery_task or self.cleanup_task or self.update_task: - await asyncio.gather( - self.discovery_task, - self.cleanup_task, - self.update_task, - return_exceptions=True, - ) - - async def discover_peers(self, wait_for_peers: int = 0) -> List[PeerHandle]: - if wait_for_peers > 0: - while len(self.known_peers) < wait_for_peers: - if DEBUG_DISCOVERY >= 2: - print( - f"Current peers: {len(self.known_peers)}/{wait_for_peers}. Waiting for more peers..." - ) - await asyncio.sleep(0.1) - return [peer_handle for peer_handle, _, _ in self.known_peers.values()] - - async def task_cleanup_peers(self): - while True: - try: - current_time = time.time() - peers_to_remove = [] - - peer_ids = list(self.known_peers.keys()) - results = await asyncio.gather( - *[self.check_peer(peer_id, current_time) for peer_id in peer_ids], - return_exceptions=True, - ) - - for peer_id, should_remove in zip(peer_ids, results): - if should_remove: - peers_to_remove.append(peer_id) - - if DEBUG_DISCOVERY >= 2: - print( - "Peer statuses:", - { - peer_handle.id(): f"is_connected={await peer_handle.is_connected()}, health_check={await peer_handle.health_check()}, connected_at={connected_at}, last_seen={last_seen}" - for peer_handle, connected_at, last_seen in self.known_peers.values() - }, - ) - - for peer_id in peers_to_remove: - if peer_id in self.known_peers: - del self.known_peers[peer_id] - if DEBUG_DISCOVERY >= 2: - print( - f"Removed peer {peer_id} due to inactivity or failed health check." - ) - except Exception as e: - print(f"Error in cleanup peers: {e}") - print(traceback.format_exc()) - finally: - await asyncio.sleep(self.discovery_interval) - - async def check_peer(self, peer_id: str, current_time: float) -> bool: - peer_handle, connected_at, last_seen = self.known_peers.get( - peer_id, (None, None, None) - ) - if peer_handle is None: - return False - - try: - is_connected = await peer_handle.is_connected() - health_ok = await peer_handle.health_check() - except Exception as e: - if DEBUG_DISCOVERY >= 2: - print(f"Error checking peer {peer_id}: {e}") - return True - - should_remove = ( - (not is_connected and current_time - connected_at > self.discovery_timeout) - or (current_time - last_seen > self.discovery_timeout) - or (not health_ok) - ) - return should_remove diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/tailscale/tailscale_helpers.py b/pkg/hanzo-network/src/hanzo_network/distributed/tailscale/tailscale_helpers.py deleted file mode 100644 index 59e67ca3d..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/tailscale/tailscale_helpers.py +++ /dev/null @@ -1,216 +0,0 @@ -import asyncio -import json -import re -from datetime import datetime, timezone -from typing import Any, Dict, List, Optional, Tuple - -import aiohttp - -from .helpers import DEBUG_DISCOVERY -from .topology.device_capabilities import DeviceCapabilities, DeviceFlops - - -class Device: - def __init__( - self, - device_id: str, - name: str, - addresses: List[str], - last_seen: Optional[datetime] = None, - ): - self.device_id = device_id - self.name = name - self.addresses = addresses - self.last_seen = last_seen - - @classmethod - def from_dict(cls, data: Dict[str, Any]) -> "Device": - return cls( - device_id=data.get("id", ""), - name=data.get("name", ""), - addresses=data.get("addresses", []), - last_seen=cls.parse_datetime(data.get("lastSeen")), - ) - - @staticmethod - def parse_datetime(date_string: Optional[str]) -> Optional[datetime]: - if not date_string: - return None - return datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%SZ").replace( - tzinfo=timezone.utc - ) - - -async def get_device_id() -> str: - try: - process = await asyncio.create_subprocess_exec( - "tailscale", - "status", - "--json", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await process.communicate() - if process.returncode != 0: - raise Exception( - f"Command failed with exit code {process.returncode}: {stderr.decode().strip()}." - ) - if DEBUG_DISCOVERY >= 4: - print(f"tailscale status: {stdout.decode()}") - data = json.loads(stdout.decode()) - return data["Self"]["ID"] - except Exception as e: - raise Exception( - f"{str(e)} Do you have the tailscale cli installed? See: https://tailscale.com/kb/1080/cli" - ) - - -async def update_device_attributes( - device_id: str, - api_key: str, - node_id: str, - node_port: int, - device_capabilities: DeviceCapabilities, -): - async with aiohttp.ClientSession() as session: - base_url = f"https://api.tailscale.com/api/v2/device/{device_id}/attributes" - headers = { - "Authorization": f"Bearer {api_key}", - "Content-Type": "application/json", - } - - attributes = { - "custom:exo_node_id": node_id.replace("-", "_"), - "custom:exo_node_port": node_port, - "custom:exo_device_capability_chip": sanitize_attribute( - device_capabilities.chip - ), - "custom:exo_device_capability_model": sanitize_attribute( - device_capabilities.model - ), - "custom:exo_device_capability_memory": str(device_capabilities.memory), - "custom:exo_device_capability_flops_fp16": str( - device_capabilities.flops.fp16 - ), - "custom:exo_device_capability_flops_fp32": str( - device_capabilities.flops.fp32 - ), - "custom:exo_device_capability_flops_int8": str( - device_capabilities.flops.int8 - ), - } - - for attr_name, attr_value in attributes.items(): - url = f"{base_url}/{attr_name}" - data = { - "value": str(attr_value).replace(" ", "_") - } # Ensure all values are strings for JSON - async with session.post(url, headers=headers, json=data) as response: - if response.status == 200: - if DEBUG_DISCOVERY >= 1: - print( - f"Updated device posture attribute {attr_name} for device {device_id}" - ) - else: - print( - f"Failed to update device posture attribute {attr_name}: {response.status} {await response.text()}" - ) - - -async def get_device_attributes( - device_id: str, api_key: str -) -> Tuple[str, int, DeviceCapabilities]: - async with aiohttp.ClientSession() as session: - url = f"https://api.tailscale.com/api/v2/device/{device_id}/attributes" - headers = {"Authorization": f"Bearer {api_key}"} - async with session.get(url, headers=headers) as response: - if response.status == 200: - data = await response.json() - attributes = data.get("attributes", {}) - node_id = attributes.get("custom:exo_node_id", "").replace("_", "-") - node_port = int(attributes.get("custom:exo_node_port", 0)) - device_capabilities = DeviceCapabilities( - model=attributes.get( - "custom:exo_device_capability_model", "" - ).replace("_", " "), - chip=attributes.get( - "custom:exo_device_capability_chip", "" - ).replace("_", " "), - memory=int( - attributes.get("custom:exo_device_capability_memory", 0) - ), - flops=DeviceFlops( - fp16=float( - attributes.get("custom:exo_device_capability_flops_fp16", 0) - ), - fp32=float( - attributes.get("custom:exo_device_capability_flops_fp32", 0) - ), - int8=float( - attributes.get("custom:exo_device_capability_flops_int8", 0) - ), - ), - ) - return node_id, node_port, device_capabilities - else: - print( - f"Failed to fetch posture attributes for {device_id}: {response.status}" - ) - return ( - "", - 0, - DeviceCapabilities( - model="", - chip="", - memory=0, - flops=DeviceFlops(fp16=0, fp32=0, int8=0), - ), - ) - - -def parse_device_attributes(data: Dict[str, str]) -> Dict[str, Any]: - result = {} - prefix = "custom:exo_" - for key, value in data.items(): - if key.startswith(prefix): - attr_name = key.replace(prefix, "") - if attr_name in [ - "node_id", - "node_port", - "device_capability_chip", - "device_capability_model", - ]: - result[attr_name] = value.replace("_", " ") - elif attr_name in [ - "device_capability_memory", - "device_capability_flops_fp16", - "device_capability_flops_fp32", - "device_capability_flops_int8", - ]: - result[attr_name] = float(value) - return result - - -def sanitize_attribute(value: str) -> str: - # Replace invalid characters with underscores - sanitized_value = re.sub(r"[^a-zA-Z0-9_.]", "_", value) - # Truncate to 50 characters - return sanitized_value[:50] - - -async def get_tailscale_devices(api_key: str, tailnet: str) -> Dict[str, Device]: - async with aiohttp.ClientSession() as session: - url = f"https://api.tailscale.com/api/v2/tailnet/{tailnet}/devices" - headers = {"Authorization": f"Bearer {api_key}"} - - async with session.get(url, headers=headers) as response: - response.raise_for_status() - data = await response.json() - - devices = {} - for device_data in data.get("devices", []): - print("Device data: ", device_data) - device = Device.from_dict(device_data) - devices[device.name] = device - - return devices diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/tailscale/test_tailscale_discovery.py b/pkg/hanzo-network/src/hanzo_network/distributed/tailscale/test_tailscale_discovery.py deleted file mode 100644 index 02dfc3d79..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/tailscale/test_tailscale_discovery.py +++ /dev/null @@ -1,45 +0,0 @@ -import asyncio -import os -import unittest - -from .networking.peer_handle import PeerHandle -from .networking.tailscale.tailscale_discovery import TailscaleDiscovery - - -class TestTailscaleDiscovery(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - self.tailscale_api_key = os.environ.get("TAILSCALE_API_KEY", "") - self.tailnet = os.environ.get("TAILSCALE_TAILNET", "") - self.discovery = TailscaleDiscovery( - node_id="test_node", - node_port=50051, - create_peer_handle=lambda peer_id, address, description, device_capabilities: unittest.mock.Mock( - spec=PeerHandle, id=lambda: peer_id - ), - tailscale_api_key=self.tailscale_api_key, - tailnet=self.tailnet, - ) - await self.discovery.start() - - async def asyncTearDown(self): - await self.discovery.stop() - - async def test_discovery(self): - # Wait for a short period to allow discovery to happen - await asyncio.sleep(15) - - # Get discovered peers - peers = await self.discovery.discover_peers() - - # Check if any peers were discovered - self.assertGreater(len(peers), 0, "No peers were discovered") - - # Print discovered peers for debugging - print(f"Discovered peers: {[peer.id() for peer in peers]}") - - # Check if discovered peers are instances of GRPCPeerHandle - print(peers) - - -if __name__ == "__main__": - unittest.main() diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/udp/__init__.py b/pkg/hanzo-network/src/hanzo_network/distributed/udp/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/udp/test_udp_discovery.py b/pkg/hanzo-network/src/hanzo_network/distributed/udp/test_udp_discovery.py deleted file mode 100644 index b8a29e30c..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/udp/test_udp_discovery.py +++ /dev/null @@ -1,106 +0,0 @@ -import asyncio -import unittest -from unittest import mock - -from .networking.grpc.grpc_peer_handle import GRPCPeerHandle -from .networking.grpc.grpc_server import GRPCServer -from .networking.udp.udp_discovery import UDPDiscovery -from .orchestration.node import Node - - -class TestUDPDiscovery(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - self.peer1 = mock.AsyncMock() - self.peer2 = mock.AsyncMock() - self.peer1.connect = mock.AsyncMock() - self.peer2.connect = mock.AsyncMock() - self.discovery1 = UDPDiscovery( - "discovery1", - 50051, - 5678, - 5679, - create_peer_handle=lambda peer_id, address, description, device_capabilities: self.peer1, - ) - self.discovery2 = UDPDiscovery( - "discovery2", - 50052, - 5679, - 5678, - create_peer_handle=lambda peer_id, address, description, device_capabilities: self.peer2, - ) - await self.discovery1.start() - await self.discovery2.start() - - async def asyncTearDown(self): - await self.discovery1.stop() - await self.discovery2.stop() - - async def test_discovery(self): - peers1 = await self.discovery1.discover_peers(wait_for_peers=1) - assert len(peers1) == 1 - peers2 = await self.discovery2.discover_peers(wait_for_peers=1) - assert len(peers2) == 1 - - # connect has to be explicitly called after discovery - self.peer1.connect.assert_not_called() - self.peer2.connect.assert_not_called() - - -class TestUDPDiscoveryWithGRPCPeerHandle(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - self.node1 = mock.AsyncMock(spec=Node) - self.node2 = mock.AsyncMock(spec=Node) - self.server1 = GRPCServer(self.node1, "localhost", 50053) - self.server2 = GRPCServer(self.node2, "localhost", 50054) - await self.server1.start() - await self.server2.start() - self.discovery1 = UDPDiscovery( - "discovery1", - 50053, - 5678, - 5679, - lambda peer_id, address, description, device_capabilities: GRPCPeerHandle( - peer_id, address, description, device_capabilities - ), - ) - self.discovery2 = UDPDiscovery( - "discovery2", - 50054, - 5679, - 5678, - lambda peer_id, address, description, device_capabilities: GRPCPeerHandle( - peer_id, address, description, device_capabilities - ), - ) - await self.discovery1.start() - await self.discovery2.start() - - async def asyncTearDown(self): - await self.discovery1.stop() - await self.discovery2.stop() - await self.server1.stop() - await self.server2.stop() - - async def test_grpc_discovery(self): - peers1 = await self.discovery1.discover_peers(wait_for_peers=1) - assert len(peers1) == 1 - peers2 = await self.discovery2.discover_peers(wait_for_peers=1) - assert len(peers2) == 1 - assert not await peers1[0].is_connected() - assert not await peers2[0].is_connected() - - # Connect - await peers1[0].connect() - await peers2[0].connect() - assert await peers1[0].is_connected() - assert await peers2[0].is_connected() - - # Kill server1 - await self.server1.stop() - - assert await peers1[0].is_connected() - assert not await peers2[0].is_connected() - - -if __name__ == "__main__": - asyncio.run(unittest.main()) diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/udp/udp_discovery.py b/pkg/hanzo-network/src/hanzo_network/distributed/udp/udp_discovery.py deleted file mode 100644 index cfb7413fb..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/udp/udp_discovery.py +++ /dev/null @@ -1,345 +0,0 @@ -import asyncio -import json -import socket -import time -import traceback -from typing import Callable, Coroutine, Dict, List, Optional, Tuple - -from .helpers import ( - DEBUG, - DEBUG_DISCOVERY, - get_all_ip_addresses_and_interfaces, - get_interface_priority_and_type, -) -from .networking.discovery import Discovery -from .networking.peer_handle import PeerHandle -from .topology.device_capabilities import ( - UNKNOWN_DEVICE_CAPABILITIES, - DeviceCapabilities, - device_capabilities, -) - - -class ListenProtocol(asyncio.DatagramProtocol): - def __init__(self, on_message: Callable[[bytes, Tuple[str, int]], Coroutine]): - super().__init__() - self.on_message = on_message - self.loop = asyncio.get_event_loop() - - def connection_made(self, transport): - self.transport = transport - - def datagram_received(self, data, addr): - asyncio.create_task(self.on_message(data, addr)) - - -def get_broadcast_address(ip_addr: str) -> str: - try: - # Split IP into octets and create broadcast address for the subnet - ip_parts = ip_addr.split(".") - return f"{ip_parts[0]}.{ip_parts[1]}.{ip_parts[2]}.255" - except Exception: - return "255.255.255.255" - - -class BroadcastProtocol(asyncio.DatagramProtocol): - def __init__(self, message: str, broadcast_port: int, source_ip: str): - self.message = message - self.broadcast_port = broadcast_port - self.source_ip = source_ip - - def connection_made(self, transport): - sock = transport.get_extra_info("socket") - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - # Try both subnet-specific and global broadcast - broadcast_addr = get_broadcast_address(self.source_ip) - transport.sendto( - self.message.encode("utf-8"), (broadcast_addr, self.broadcast_port) - ) - if broadcast_addr != "255.255.255.255": - transport.sendto( - self.message.encode("utf-8"), ("255.255.255.255", self.broadcast_port) - ) - - -class UDPDiscovery(Discovery): - def __init__( - self, - node_id: str, - node_port: int, - listen_port: int, - broadcast_port: int, - create_peer_handle: Callable[[str, str, str, DeviceCapabilities], PeerHandle], - broadcast_interval: int = 2.5, - discovery_timeout: int = 30, - device_capabilities: DeviceCapabilities = UNKNOWN_DEVICE_CAPABILITIES, - allowed_node_ids: Optional[List[str]] = None, - allowed_interface_types: Optional[List[str]] = None, - ): - self.node_id = node_id - self.node_port = node_port - self.listen_port = listen_port - self.broadcast_port = broadcast_port - self.create_peer_handle = create_peer_handle - self.broadcast_interval = broadcast_interval - self.discovery_timeout = discovery_timeout - self.device_capabilities = device_capabilities - self.allowed_node_ids = allowed_node_ids - self.allowed_interface_types = allowed_interface_types - self.known_peers: Dict[str, Tuple[PeerHandle, float, float, int]] = {} - self.broadcast_task = None - self.listen_task = None - self.cleanup_task = None - - async def start(self): - self.device_capabilities = await device_capabilities() - self.broadcast_task = asyncio.create_task(self.task_broadcast_presence()) - self.listen_task = asyncio.create_task(self.task_listen_for_peers()) - self.cleanup_task = asyncio.create_task(self.task_cleanup_peers()) - - async def stop(self): - if self.broadcast_task: - self.broadcast_task.cancel() - if self.listen_task: - self.listen_task.cancel() - if self.cleanup_task: - self.cleanup_task.cancel() - if self.broadcast_task or self.listen_task or self.cleanup_task: - await asyncio.gather( - self.broadcast_task, - self.listen_task, - self.cleanup_task, - return_exceptions=True, - ) - - async def discover_peers(self, wait_for_peers: int = 0) -> List[PeerHandle]: - if wait_for_peers > 0: - while len(self.known_peers) < wait_for_peers: - if DEBUG_DISCOVERY >= 2: - print( - f"Current peers: {len(self.known_peers)}/{wait_for_peers}. Waiting for more peers..." - ) - await asyncio.sleep(0.1) - return [peer_handle for peer_handle, _, _, _ in self.known_peers.values()] - - async def task_broadcast_presence(self): - while True: - for addr, interface_name in get_all_ip_addresses_and_interfaces(): - interface_priority, interface_type = ( - await get_interface_priority_and_type(interface_name) - ) - message = json.dumps( - { - "type": "discovery", - "node_id": self.node_id, - "grpc_port": self.node_port, - "device_capabilities": self.device_capabilities.to_dict(), - "priority": interface_priority, - "interface_name": interface_name, - "interface_type": interface_type, - } - ) - - transport = None - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - try: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - except AttributeError: - pass - sock.bind((addr, 0)) - - ( - transport, - _, - ) = await asyncio.get_event_loop().create_datagram_endpoint( - lambda: BroadcastProtocol(message, self.broadcast_port, addr), - sock=sock, - ) - except Exception as e: - print( - f"Error in broadcast presence ({addr} - {interface_name} - {interface_priority}): {e}" - ) - finally: - if transport: - try: - transport.close() - except Exception as e: - if DEBUG_DISCOVERY >= 2: - print(f"Error closing transport: {e}") - - await asyncio.sleep(self.broadcast_interval) - - async def on_listen_message(self, data, addr): - if not data: - return - - decoded_data = data.decode("utf-8", errors="ignore") - - # Check if the decoded data starts with a valid JSON character - if not (decoded_data.strip() and decoded_data.strip()[0] in "{["): - if DEBUG_DISCOVERY >= 2: - print(f"Received invalid JSON data from {addr}: {decoded_data[:100]}") - return - - try: - decoder = json.JSONDecoder(strict=False) - message = decoder.decode(decoded_data) - except json.JSONDecodeError as e: - if DEBUG_DISCOVERY >= 2: - print(f"Error decoding JSON data from {addr}: {e}") - return - - if DEBUG_DISCOVERY >= 2: - print(f"received from peer {addr}: {message}") - - if message["type"] == "discovery" and message["node_id"] != self.node_id: - peer_id = message["node_id"] - - # Skip if peer_id is not in allowed list - if self.allowed_node_ids and peer_id not in self.allowed_node_ids: - if DEBUG_DISCOVERY >= 2: - print( - f"Ignoring peer {peer_id} as it's not in the allowed node IDs list" - ) - return - - peer_host = addr[0] - peer_port = message["grpc_port"] - peer_prio = message["priority"] - peer_interface_name = message["interface_name"] - peer_interface_type = message["interface_type"] - - # Skip if interface type is not in allowed list - if ( - self.allowed_interface_types - and peer_interface_type not in self.allowed_interface_types - ): - if DEBUG_DISCOVERY >= 2: - print( - f"Ignoring peer {peer_id} as its interface type {peer_interface_type} is not in the allowed interface types list" - ) - return - - device_capabilities = DeviceCapabilities(**message["device_capabilities"]) - - if ( - peer_id not in self.known_peers - or self.known_peers[peer_id][0].addr() != f"{peer_host}:{peer_port}" - ): - if peer_id in self.known_peers: - existing_peer_prio = self.known_peers[peer_id][3] - if existing_peer_prio >= peer_prio: - if DEBUG >= 1: - print( - f"Ignoring peer {peer_id} at {peer_host}:{peer_port} with priority {peer_prio} because we already know about a peer with higher or equal priority: {existing_peer_prio}" - ) - return - new_peer_handle = self.create_peer_handle( - peer_id, - f"{peer_host}:{peer_port}", - f"{peer_interface_type} ({peer_interface_name})", - device_capabilities, - ) - if not await new_peer_handle.health_check(): - if DEBUG >= 1: - print( - f"Peer {peer_id} at {peer_host}:{peer_port} is not healthy. Skipping." - ) - return - if DEBUG >= 1: - print( - f"Adding {peer_id=} at {peer_host}:{peer_port}. Replace existing peer_id: {peer_id in self.known_peers}" - ) - self.known_peers[peer_id] = ( - new_peer_handle, - time.time(), - time.time(), - peer_prio, - ) - else: - if not await self.known_peers[peer_id][0].health_check(): - if DEBUG >= 1: - print( - f"Peer {peer_id} at {peer_host}:{peer_port} is not healthy. Removing." - ) - if peer_id in self.known_peers: - del self.known_peers[peer_id] - return - if peer_id in self.known_peers: - self.known_peers[peer_id] = ( - self.known_peers[peer_id][0], - self.known_peers[peer_id][1], - time.time(), - peer_prio, - ) - - async def task_listen_for_peers(self): - await asyncio.get_event_loop().create_datagram_endpoint( - lambda: ListenProtocol(self.on_listen_message), - local_addr=("0.0.0.0", self.listen_port), - ) - if DEBUG_DISCOVERY >= 2: - print("Started listen task") - - async def task_cleanup_peers(self): - while True: - try: - current_time = time.time() - peers_to_remove = [] - - peer_ids = list(self.known_peers.keys()) - results = await asyncio.gather( - *[self.check_peer(peer_id, current_time) for peer_id in peer_ids], - return_exceptions=True, - ) - - for peer_id, should_remove in zip(peer_ids, results): - if should_remove: - peers_to_remove.append(peer_id) - - if DEBUG_DISCOVERY >= 2: - print( - "Peer statuses:", - { - peer_handle.id(): f"is_connected={await peer_handle.is_connected()}, health_check={await peer_handle.health_check()}, connected_at={connected_at}, last_seen={last_seen}, prio={prio}" - for peer_handle, connected_at, last_seen, prio in self.known_peers.values() - }, - ) - - for peer_id in peers_to_remove: - if peer_id in self.known_peers: - del self.known_peers[peer_id] - if DEBUG_DISCOVERY >= 2: - print( - f"Removed peer {peer_id} due to inactivity or failed health check." - ) - except Exception as e: - print(f"Error in cleanup peers: {e}") - print(traceback.format_exc()) - finally: - await asyncio.sleep(self.broadcast_interval) - - async def check_peer(self, peer_id: str, current_time: float) -> bool: - peer_handle, connected_at, last_seen, prio = self.known_peers.get( - peer_id, (None, None, None, None) - ) - if peer_handle is None: - return False - - try: - is_connected = await peer_handle.is_connected() - health_ok = await peer_handle.health_check() - except Exception as e: - if DEBUG_DISCOVERY >= 2: - print(f"Error checking peer {peer_id}: {e}") - return True - - should_remove = ( - (not is_connected and current_time - connected_at > self.discovery_timeout) - or (current_time - last_seen > self.discovery_timeout) - or (not health_ok) - ) - return should_remove diff --git a/pkg/hanzo-network/src/hanzo_network/distributed/udp_discovery.py b/pkg/hanzo-network/src/hanzo_network/distributed/udp_discovery.py deleted file mode 100644 index 984f0c0b2..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed/udp_discovery.py +++ /dev/null @@ -1,293 +0,0 @@ -"""UDP-based peer discovery for distributed Hanzo networks.""" - -import asyncio -import json -import socket -import time -from typing import Dict, List, Tuple - -from ..device_capabilities import DeviceCapabilities -from .discovery import Discovery -from .minimal_discovery import MinimalPeerHandle - - -def get_broadcast_address(ip_addr: str) -> str: - """Get broadcast address for a given IP.""" - try: - # Split IP into octets and create broadcast address for the subnet - ip_parts = ip_addr.split(".") - return f"{ip_parts[0]}.{ip_parts[1]}.{ip_parts[2]}.255" - except Exception: - return "255.255.255.255" - - -def get_local_ip() -> str: - """Get the local IP address.""" - try: - # Create a socket to determine the local IP - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - s.connect(("8.8.8.8", 80)) - ip = s.getsockname()[0] - s.close() - return ip - except Exception: - return "127.0.0.1" - - -class ListenProtocol(asyncio.DatagramProtocol): - """Protocol for listening to UDP broadcasts.""" - - def __init__(self, on_message): - super().__init__() - self.on_message = on_message - self.loop = asyncio.get_event_loop() - - def connection_made(self, transport): - self.transport = transport - - def datagram_received(self, data, addr): - asyncio.create_task(self.on_message(data, addr)) - - -class BroadcastProtocol(asyncio.DatagramProtocol): - """Protocol for broadcasting UDP messages.""" - - def __init__(self, message: str, broadcast_port: int, source_ip: str): - self.message = message - self.broadcast_port = broadcast_port - self.source_ip = source_ip - - def connection_made(self, transport): - sock = transport.get_extra_info("socket") - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - # Try both subnet-specific and global broadcast - broadcast_addr = get_broadcast_address(self.source_ip) - transport.sendto( - self.message.encode("utf-8"), (broadcast_addr, self.broadcast_port) - ) - if broadcast_addr != "255.255.255.255": - transport.sendto( - self.message.encode("utf-8"), ("255.255.255.255", self.broadcast_port) - ) - transport.close() - - -class UDPDiscovery(Discovery): - """UDP-based discovery for finding peers on the local network.""" - - def __init__( - self, - node_id: str, - device_capabilities: DeviceCapabilities, - listen_port: int = 5678, - broadcast_port: int = 5678, - broadcast_interval: float = 2.5, - discovery_timeout: float = 30.0, - **kwargs, - ): - self.node_id = node_id - self.device_capabilities = device_capabilities - self.listen_port = listen_port - self.broadcast_port = broadcast_port - self.broadcast_interval = broadcast_interval - self.discovery_timeout = discovery_timeout - - # Track known peers: peer_id -> (peer_handle, first_seen, last_seen) - self.known_peers: Dict[str, Tuple[MinimalPeerHandle, float, float]] = {} - - # Background tasks - self.broadcast_task = None - self.listen_task = None - self.cleanup_task = None - self.is_running = False - - # Local IP - self.local_ip = get_local_ip() - - async def start(self) -> None: - """Start UDP discovery.""" - if self.is_running: - return - - self.is_running = True - - # Start background tasks - self.broadcast_task = asyncio.create_task(self._broadcast_loop()) - self.listen_task = asyncio.create_task(self._listen_loop()) - self.cleanup_task = asyncio.create_task(self._cleanup_loop()) - - print( - f"UDP Discovery started for node {self.node_id} on port {self.listen_port}" - ) - - async def stop(self) -> None: - """Stop UDP discovery.""" - if not self.is_running: - return - - self.is_running = False - - # Cancel tasks - for task in [self.broadcast_task, self.listen_task, self.cleanup_task]: - if task: - task.cancel() - - # Wait for tasks to complete - if any([self.broadcast_task, self.listen_task, self.cleanup_task]): - await asyncio.gather( - self.broadcast_task, - self.listen_task, - self.cleanup_task, - return_exceptions=True, - ) - - print(f"UDP Discovery stopped for node {self.node_id}") - - async def discover_peers(self, wait_for_peers: int = 0) -> List[MinimalPeerHandle]: - """Discover peers on the network.""" - if wait_for_peers > 0: - # Wait until we have enough peers - start_time = time.time() - while len(self.known_peers) < wait_for_peers: - if time.time() - start_time > 30: # 30 second timeout - print( - f"Timeout waiting for {wait_for_peers} peers, found {len(self.known_peers)}" - ) - break - await asyncio.sleep(0.1) - - return [peer_handle for peer_handle, _, _ in self.known_peers.values()] - - async def _broadcast_loop(self) -> None: - """Broadcast presence to network.""" - while self.is_running: - try: - # Create discovery message - message = json.dumps( - { - "type": "discovery", - "node_id": self.node_id, - "listen_port": self.listen_port, - "device_capabilities": ( - self.device_capabilities.to_dict() - if self.device_capabilities - else {} - ), - "timestamp": time.time(), - } - ) - - # Create socket and broadcast - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - try: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - except AttributeError: - pass # Not available on all platforms - sock.bind((self.local_ip, 0)) - - transport, _ = await asyncio.get_event_loop().create_datagram_endpoint( - lambda: BroadcastProtocol( - message, self.broadcast_port, self.local_ip - ), - sock=sock, - ) - - await asyncio.sleep(0.1) # Give time for broadcast - - except Exception as e: - print(f"Error in broadcast loop: {e}") - - await asyncio.sleep(self.broadcast_interval) - - async def _listen_loop(self) -> None: - """Listen for discovery broadcasts.""" - try: - await asyncio.get_event_loop().create_datagram_endpoint( - lambda: ListenProtocol(self._handle_discovery_message), - local_addr=("0.0.0.0", self.listen_port), - ) - print(f"Listening for peers on port {self.listen_port}") - - # Keep the task running - while self.is_running: - await asyncio.sleep(1) - - except Exception as e: - print(f"Error in listen loop: {e}") - - async def _handle_discovery_message( - self, data: bytes, addr: Tuple[str, int] - ) -> None: - """Handle incoming discovery message.""" - try: - # Decode message - message = json.loads(data.decode("utf-8")) - - # Check if it's a discovery message - if message.get("type") != "discovery": - return - - peer_id = message.get("node_id") - if not peer_id or peer_id == self.node_id: - return # Ignore our own broadcasts - - # Extract peer info - peer_host = addr[0] - peer_port = message.get("listen_port", 5678) - peer_caps_dict = message.get("device_capabilities", {}) - - # Create device capabilities if provided - peer_caps = None - if peer_caps_dict: - try: - peer_caps = DeviceCapabilities(**peer_caps_dict) - except Exception: - pass - - # Create or update peer - current_time = time.time() - peer_address = f"{peer_host}:{peer_port}" - - if peer_id not in self.known_peers: - # New peer discovered - peer_handle = MinimalPeerHandle( - peer_id=peer_id, address=peer_address, capabilities=peer_caps - ) - self.known_peers[peer_id] = (peer_handle, current_time, current_time) - print(f"Discovered new peer: {peer_id} at {peer_address}") - else: - # Update last seen time - peer_handle, first_seen, _ = self.known_peers[peer_id] - self.known_peers[peer_id] = (peer_handle, first_seen, current_time) - - except Exception as e: - print(f"Error handling discovery message from {addr}: {e}") - - async def _cleanup_loop(self) -> None: - """Clean up stale peers.""" - while self.is_running: - try: - current_time = time.time() - peers_to_remove = [] - - # Check each peer - for peer_id, ( - peer_handle, - first_seen, - last_seen, - ) in self.known_peers.items(): - # Remove if not seen recently - if current_time - last_seen > self.discovery_timeout: - peers_to_remove.append(peer_id) - - # Remove stale peers - for peer_id in peers_to_remove: - del self.known_peers[peer_id] - print(f"Removed stale peer: {peer_id}") - - except Exception as e: - print(f"Error in cleanup loop: {e}") - - await asyncio.sleep(self.broadcast_interval) diff --git a/pkg/hanzo-network/src/hanzo_network/distributed_network.py b/pkg/hanzo-network/src/hanzo_network/distributed_network.py deleted file mode 100644 index e25558889..000000000 --- a/pkg/hanzo-network/src/hanzo_network/distributed_network.py +++ /dev/null @@ -1,359 +0,0 @@ -"""Distributed Network implementation using Hanzo Net infrastructure.""" - -import asyncio -from dataclasses import dataclass -from typing import Any, Dict, List, Optional, TypeVar, Union - -from .core.agent import Agent -from .core.network import Network, NetworkConfig -from .core.router import Router, RouterFunction, RoutingAgent -from .core.state import NetworkState -from .distributed.discovery import Discovery -from .distributed.grpc_server import GRPCServer -from .distributed.simplified_peer_handle import SimplifiedPeerHandle as PeerHandle -from .distributed.udp_discovery import UDPDiscovery -from .topology.device_capabilities import DeviceCapabilities, device_capabilities - -T = TypeVar("T") - - -@dataclass -class DistributedNetworkConfig(NetworkConfig[T]): - """Configuration for a distributed network.""" - - discovery_method: str = "udp" # udp, manual, tailscale - listen_port: int = 5678 - broadcast_port: int = 5678 - node_id: Optional[str] = None - device_capabilities: Optional[DeviceCapabilities] = None - - -class DistributedNetwork(Network[T]): - """A distributed network of agents across multiple nodes. - - Extends the base Network with: - - Automatic peer discovery - - Distributed agent execution - - Cross-node state synchronization - - Load balancing across nodes - """ - - def __init__(self, config: DistributedNetworkConfig[T]): - """Initialize distributed network with configuration.""" - super().__init__(config) - - self.discovery_method = config.discovery_method - self.listen_port = config.listen_port - self.broadcast_port = config.broadcast_port - self.node_id = config.node_id or self._generate_node_id() - self.device_capabilities = config.device_capabilities or device_capabilities() - - # Discovery and networking - self.discovery: Optional[Discovery] = None - self.grpc_server: Optional[GRPCServer] = None - self.peers: List[PeerHandle] = [] - self.peer_agents: Dict[str, List[Agent]] = {} # peer_id -> agents - - # Distributed state - self.is_running = False - self.sync_task = None - - async def start(self, wait_for_peers: int = 0) -> None: - """Start the distributed network node. - - Args: - wait_for_peers: Number of peers to wait for before starting - """ - if self.is_running: - return - - # Initialize discovery based on method - if self.discovery_method == "udp": - self.discovery = UDPDiscovery( - node_id=self.node_id, - device_capabilities=self.device_capabilities, - listen_port=self.listen_port, - broadcast_port=self.broadcast_port, - ) - # Add other discovery methods as needed - - # Start discovery - await self.discovery.start() - - # Start GRPC server for peer communication - self.grpc_server = GRPCServer(node_id=self.node_id, port=self.listen_port) - - # Register handlers for distributed operations - self.grpc_server.register_handler("list_agents", self._handle_list_agents) - self.grpc_server.register_handler("execute_agent", self._handle_execute_agent) - - await self.grpc_server.start() - - # Discover initial peers - self.peers = await self.discovery.discover_peers(wait_for_peers) - - # Start state synchronization - self.sync_task = asyncio.create_task(self._sync_loop()) - - self.is_running = True - print( - f"Distributed network node {self.node_id} started with {len(self.peers)} peers" - ) - - async def stop(self) -> None: - """Stop the distributed network node.""" - if not self.is_running: - return - - self.is_running = False - - # Stop sync - if self.sync_task: - self.sync_task.cancel() - try: - await self.sync_task - except asyncio.CancelledError: - pass - - # Stop servers - if self.grpc_server: - await self.grpc_server.stop() - - if self.discovery: - await self.discovery.stop() - - print(f"Distributed network node {self.node_id} stopped") - - async def run( - self, - prompt: str, - initial_agent: Optional[Agent] = None, - context: Optional[Dict[str, Any]] = None, - ) -> Dict[str, Any]: - """Run the distributed network with a user prompt. - - This extends the base run method to support distributed execution. - """ - if not self.is_running: - await self.start() - - # Add distributed context - dist_context = { - "node_id": self.node_id, - "peer_count": len(self.peers), - "distributed": True, - **(context or {}), - } - - # Check if we need to execute on a remote node - if initial_agent and not self._is_local_agent(initial_agent): - # Find peer with this agent - peer_id = self._find_peer_with_agent(initial_agent.name) - if peer_id: - peer = self._get_peer(peer_id) - if peer: - # Execute remotely - return await self._execute_remote( - peer, prompt, initial_agent, dist_context - ) - - # Execute locally (using base implementation) - return await super().run(prompt, initial_agent, dist_context) - - async def _execute_remote( - self, peer: PeerHandle, prompt: str, agent: Agent, context: Dict[str, Any] - ) -> Dict[str, Any]: - """Execute an agent on a remote peer.""" - try: - # Send execution request to peer - request = { - "action": "execute_agent", - "agent_name": agent.name, - "prompt": prompt, - "context": context, - "state": self.state.to_dict(), - } - - response = await peer.send_request(request) - - # Update local state with remote results - if response.get("state"): - self.state.from_dict(response["state"]) - - return response - - except Exception as e: - return {"success": False, "error": f"Remote execution failed: {str(e)}"} - - async def _sync_loop(self) -> None: - """Background task to sync state with peers.""" - while self.is_running: - try: - # Discover new peers - new_peers = await self.discovery.discover_peers() - self._update_peers(new_peers) - - # Sync agent information - for peer in self.peers: - try: - # Get peer's agents - response = await peer.send_request({"action": "list_agents"}) - - if response.get("agents"): - self.peer_agents[peer.id] = response["agents"] - except Exception: - pass - - # Wait before next sync - await asyncio.sleep(5) - - except Exception as e: - if self.is_running: - print(f"Sync error: {e}") - await asyncio.sleep(5) - - def _update_peers(self, new_peers: List[PeerHandle]) -> None: - """Update peer list with newly discovered peers.""" - # Add new peers - existing_ids = {p.id for p in self.peers} - for peer in new_peers: - if peer.id not in existing_ids: - self.peers.append(peer) - print(f"New peer discovered: {peer.id}") - - def _is_local_agent(self, agent: Agent) -> bool: - """Check if an agent is available locally.""" - return agent.name in self.agent_map - - def _find_peer_with_agent(self, agent_name: str) -> Optional[str]: - """Find which peer has a specific agent.""" - for peer_id, agents in self.peer_agents.items(): - if any(a.get("name") == agent_name for a in agents): - return peer_id - return None - - def _get_peer(self, peer_id: str) -> Optional[PeerHandle]: - """Get peer by ID.""" - for peer in self.peers: - if peer.id == peer_id: - return peer - return None - - def _generate_node_id(self) -> str: - """Generate a unique node ID.""" - import uuid - - return f"node-{uuid.uuid4().hex[:8]}" - - def get_network_status(self) -> Dict[str, Any]: - """Get current network status.""" - return { - "node_id": self.node_id, - "is_running": self.is_running, - "peer_count": len(self.peers), - "peers": [ - { - "id": p.id, - "address": p.address, - "capabilities": ( - p.device_capabilities.__dict__ if p.device_capabilities else {} - ), - } - for p in self.peers - ], - "local_agents": list(self.agent_map.keys()), - "peer_agents": self.peer_agents, - "device_capabilities": self.device_capabilities.__dict__, - } - - async def _handle_list_agents(self, request: Dict[str, Any]) -> Dict[str, Any]: - """Handle request to list agents on this node.""" - agents = [] - for name, agent in self.agent_map.items(): - agents.append( - { - "name": name, - "type": agent.__class__.__name__, - "has_tools": ( - len(agent.tools) > 0 if hasattr(agent, "tools") else False - ), - } - ) - - return {"success": True, "agents": agents, "node_id": self.node_id} - - async def _handle_execute_agent(self, request: Dict[str, Any]) -> Dict[str, Any]: - """Handle request to execute an agent remotely.""" - agent_name = request.get("agent_name") - prompt = request.get("prompt") - context = request.get("context", {}) - - if not agent_name or agent_name not in self.agent_map: - return { - "success": False, - "error": f"Agent {agent_name} not found on node {self.node_id}", - } - - try: - # Execute the agent locally - agent = self.agent_map[agent_name] - result = await super().run(prompt, agent, context) - - # Include state in response - result["state"] = self.state.to_dict() - - return result - - except Exception as e: - return {"success": False, "error": f"Execution error: {str(e)}"} - - -def create_distributed_network( - agents: List[Agent], - name: Optional[str] = None, - router: Optional[Union[Router, RouterFunction, RoutingAgent]] = None, - default_model: Optional[str] = None, - max_iterations: int = 10, - default_state: Optional[NetworkState] = None, - discovery_method: str = "udp", - listen_port: int = 5678, - broadcast_port: int = 5678, - node_id: Optional[str] = None, - device_capabilities: Optional[DeviceCapabilities] = None, - **metadata, -) -> DistributedNetwork: - """Create a distributed network of agents. - - Args: - agents: List of agents in the network - name: Network name - router: Router for agent orchestration - default_model: Default model for routing - max_iterations: Maximum execution iterations - default_state: Initial state - discovery_method: Method for peer discovery (udp, manual, tailscale) - listen_port: Port to listen on - broadcast_port: Port for UDP broadcast - node_id: Optional node identifier - device_capabilities: Device capabilities - **metadata: Additional metadata - - Returns: - Configured DistributedNetwork instance - """ - config = DistributedNetworkConfig( - name=name or "distributed-network", - agents=agents, - router=router, - default_model=default_model, - max_iterations=max_iterations, - default_state=default_state, - discovery_method=discovery_method, - listen_port=listen_port, - broadcast_port=broadcast_port, - node_id=node_id, - device_capabilities=device_capabilities, - metadata=metadata, - ) - - return DistributedNetwork(config) diff --git a/pkg/hanzo-network/src/hanzo_network/download/__init__.py b/pkg/hanzo-network/src/hanzo_network/download/__init__.py deleted file mode 100644 index df9f82ce3..000000000 --- a/pkg/hanzo-network/src/hanzo_network/download/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Download module for hanzo-network.""" - -from .shard_download import ShardDownloader - -__all__ = ["ShardDownloader"] diff --git a/pkg/hanzo-network/src/hanzo_network/download/new_shard_download.py b/pkg/hanzo-network/src/hanzo_network/download/new_shard_download.py deleted file mode 100644 index 9a657415b..000000000 --- a/pkg/hanzo-network/src/hanzo_network/download/new_shard_download.py +++ /dev/null @@ -1,10 +0,0 @@ -"""New shard download utilities.""" - -from pathlib import Path - - -async def ensure_downloads_dir(): - """Ensure downloads directory exists.""" - downloads_dir = Path.home() / ".hanzo" / "downloads" - downloads_dir.mkdir(parents=True, exist_ok=True) - return downloads_dir diff --git a/pkg/hanzo-network/src/hanzo_network/download/shard_download.py b/pkg/hanzo-network/src/hanzo_network/download/shard_download.py deleted file mode 100644 index 344cd5308..000000000 --- a/pkg/hanzo-network/src/hanzo_network/download/shard_download.py +++ /dev/null @@ -1,69 +0,0 @@ -"""Shard downloader for distributed model loading.""" - -from pathlib import Path -from typing import Any, Dict, Optional - - -class ShardDownloader: - """Downloads and manages model shards for distributed inference.""" - - def __init__(self, cache_dir: Optional[Path] = None): - """Initialize shard downloader. - - Args: - cache_dir: Directory to cache downloaded shards - """ - self.cache_dir = cache_dir or Path.home() / ".hanzo" / "models" - self.cache_dir.mkdir(parents=True, exist_ok=True) - self._downloads = {} - - async def download_shard(self, model_id: str, shard_id: str) -> Path: - """Download a model shard. - - Args: - model_id: Model identifier - shard_id: Shard identifier - - Returns: - Path to downloaded shard - """ - # For now, return a dummy path - shard_path = self.cache_dir / model_id / f"{shard_id}.shard" - shard_path.parent.mkdir(parents=True, exist_ok=True) - - # In real implementation, this would download from hanzo/net - # For testing, just create an empty file - if not shard_path.exists(): - shard_path.touch() - - return shard_path - - async def get_shard_info(self, model_id: str) -> Dict[str, Any]: - """Get information about available shards for a model. - - Args: - model_id: Model identifier - - Returns: - Dictionary with shard information - """ - # Mock shard info - return { - "model_id": model_id, - "total_shards": 1, - "shard_size": 1024 * 1024 * 100, # 100MB - "shards": [{"id": "shard_0", "layers": [0, 31], "size": 1024 * 1024 * 100}], - } - - def is_cached(self, model_id: str, shard_id: str) -> bool: - """Check if a shard is already cached. - - Args: - model_id: Model identifier - shard_id: Shard identifier - - Returns: - True if shard is cached - """ - shard_path = self.cache_dir / model_id / f"{shard_id}.shard" - return shard_path.exists() diff --git a/pkg/hanzo-network/src/hanzo_network/helpers.py b/pkg/hanzo-network/src/hanzo_network/helpers.py deleted file mode 100644 index d0fc7e4f3..000000000 --- a/pkg/hanzo-network/src/hanzo_network/helpers.py +++ /dev/null @@ -1,144 +0,0 @@ -"""Helper functions for distributed Hanzo networks.""" - -import asyncio -import os -import platform -import random -import socket -import subprocess -from concurrent.futures import ThreadPoolExecutor -from typing import List, Tuple - -import psutil - -DEBUG = int(os.getenv("DEBUG", default="0")) -DEBUG_DISCOVERY = int(os.getenv("DEBUG_DISCOVERY", default="0")) -VERSION = "0.1.0" - -# Single shared thread pool for subprocess operations -subprocess_pool = ThreadPoolExecutor( - max_workers=4, thread_name_prefix="subprocess_worker" -) - - -def get_system_info(): - """Get basic system information.""" - if psutil.MACOS: - if platform.machine() == "arm64": - return "Apple Silicon Mac" - if platform.machine() in ["x86_64", "i386"]: - return "Intel Mac" - return "Unknown Mac architecture" - if psutil.LINUX: - return "Linux" - return "Non-Mac, non-Linux system" - - -def find_available_port( - host: str = "", min_port: int = 49152, max_port: int = 65535 -) -> int: - """Find an available port in the specified range.""" - for _ in range(100): # Try 100 times - port = random.randint(min_port, max_port) - try: - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - s.bind((host, port)) - return port - except socket.error: - continue - raise RuntimeError("No available ports in the specified range") - - -def get_all_ip_addresses_and_interfaces() -> List[Tuple[str, str]]: - """Get all IP addresses and their interface names.""" - ip_addresses = [] - - try: - # Get network interfaces using psutil - interfaces = psutil.net_if_addrs() - - for interface_name, addrs in interfaces.items(): - for addr in addrs: - # Only IPv4 addresses - if addr.family == socket.AF_INET: - ip = addr.address - # Skip loopback and invalid addresses - if not ip.startswith("127.") and not ip.startswith("0."): - ip_addresses.append((ip, interface_name)) - except Exception as e: - if DEBUG >= 1: - print(f"Failed to get IP addresses: {e}") - - if not ip_addresses: - # Fallback to localhost - return [("127.0.0.1", "lo")] - - return list(set(ip_addresses)) - - -async def get_interface_priority_and_type(ifname: str) -> Tuple[int, str]: - """Get interface priority and type based on name patterns.""" - # Loopback interface - if ifname.startswith("lo"): - return (6, "Loopback") - - # Container/virtual interfaces - if ( - ifname.startswith( - ("docker", "br-", "veth", "cni", "flannel", "calico", "weave") - ) - or "bridge" in ifname - ): - return (7, "Container Virtual") - - # Thunderbolt - if ifname.startswith(("tb", "nx", "ten")): - return (5, "Thunderbolt") - - # Ethernet - if ifname.startswith(("eth", "en")): - return (4, "Ethernet") - - # WiFi - if ifname.startswith(("wlan", "wifi", "wl")): - return (3, "WiFi") - - # Virtual interfaces (VPNs, tunnels) - if ifname.startswith(("tun", "tap", "vtun", "utun", "gif", "stf")): - return (1, "External Virtual") - - # Other - return (2, "Other") - - -async def get_mac_system_info() -> Tuple[str, str, int]: - """Get Mac system information using system_profiler.""" - try: - output = await asyncio.get_running_loop().run_in_executor( - subprocess_pool, - lambda: subprocess.check_output( - ["system_profiler", "SPHardwareDataType"] - ).decode("utf-8"), - ) - - model_line = next( - (line for line in output.split("\n") if "Model Name" in line), None - ) - model_id = model_line.split(": ")[1] if model_line else "Unknown Model" - - chip_line = next((line for line in output.split("\n") if "Chip" in line), None) - chip_id = chip_line.split(": ")[1] if chip_line else "Unknown Chip" - - memory_line = next( - (line for line in output.split("\n") if "Memory" in line), None - ) - memory_str = memory_line.split(": ")[1] if memory_line else "Unknown Memory" - memory_units = memory_str.split() - memory_value = int(memory_units[0]) - memory = memory_value * 1024 if memory_units[1] == "GB" else memory_value - - return model_id, chip_id, memory - except Exception as e: - if DEBUG >= 2: - print(f"Error getting Mac system info: {e}") - return "Unknown Model", "Unknown Chip", 0 diff --git a/pkg/hanzo-network/src/hanzo_network/inference/__init__.py b/pkg/hanzo-network/src/hanzo_network/inference/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-network/src/hanzo_network/inference/debug_inference_engine.py b/pkg/hanzo-network/src/hanzo_network/inference/debug_inference_engine.py deleted file mode 100644 index b308d40a2..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/debug_inference_engine.py +++ /dev/null @@ -1,75 +0,0 @@ -import asyncio - -import numpy as np - -from .inference.inference_engine import InferenceEngine -from .inference.shard import Shard -from .inference.tinygrad.inference import TinygradDynamicShardInferenceEngine - - -# An inference engine should work the same for any number of Shards, as long as the Shards are continuous. -async def test_inference_engine( - inference_engine_1: InferenceEngine, - inference_engine_2: InferenceEngine, - model_id: str, -): - from pathlib import Path - - from .inference.tinygrad.inference import Tokenizer - - _tokenizer = Tokenizer(str(Path(model_id) / "tokenizer.model")) - - prompt = "In a single word only, what is the last name of the president of the United States? " - resp_full = await inference_engine_1.infer_prompt( - "A", - shard=Shard(model_id=model_id, start_layer=0, end_layer=31, n_layers=32), - prompt=prompt, - ) - token_full = await inference_engine_1.sample(resp_full) - - next_resp_full, _ = await inference_engine_1.infer_tensor( - "A", - shard=Shard(model_id=model_id, start_layer=0, end_layer=31, n_layers=32), - input_data=token_full, - ) - - resp1, _ = await inference_engine_1.infer_prompt( - "B", - shard=Shard(model_id=model_id, start_layer=0, end_layer=30, n_layers=32), - prompt=prompt, - ) - resp2, _ = await inference_engine_2.infer_tensor( - "B", - shard=Shard(model_id=model_id, start_layer=31, end_layer=31, n_layers=32), - input_data=resp1, - ) - token2 = await inference_engine_2.sample(resp2) - resp3, _ = await inference_engine_1.infer_tensor( - "B", - shard=Shard(model_id=model_id, start_layer=0, end_layer=30, n_layers=32), - input_data=token2, - ) - resp4, _ = await inference_engine_2.infer_tensor( - "B", - shard=Shard(model_id=model_id, start_layer=31, end_layer=31, n_layers=32), - input_data=resp3, - ) - - print(f"{resp2=}") - print(f"full: {_tokenizer.decode(resp_full)}") - print(f"next full: {_tokenizer.decode(next_resp_full)}") - print(f"resp2: {_tokenizer.decode(resp2)}") - print(f"{resp4=}") - print(f"resp4: {_tokenizer.decode(resp4)}") - - assert np.array_equal(resp_full, resp2) - assert np.array_equal(next_resp_full, resp4) - - -asyncio.run( - test_inference_engine( - TinygradDynamicShardInferenceEngine(), - TinygradDynamicShardInferenceEngine(), - "llama3-8b-sfr", - ) -) diff --git a/pkg/hanzo-network/src/hanzo_network/inference/dummy_inference_engine.py b/pkg/hanzo-network/src/hanzo_network/inference/dummy_inference_engine.py deleted file mode 100644 index d7807cfb6..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/dummy_inference_engine.py +++ /dev/null @@ -1,50 +0,0 @@ -from typing import Optional - -import numpy as np - -from .inference.inference_engine import InferenceEngine -from .inference.shard import Shard -from .inference.tokenizers import DummyTokenizer - - -class DummyInferenceEngine(InferenceEngine): - def __init__(self): - self.shard = None - self.vocab_size = 1000 - self.hidden_size = 256 - self.eos_token_id = 0 - self.latency_mean = 0.1 - self.latency_stddev = 0.02 - self.num_generate_dummy_tokens = 10 - self.tokenizer = DummyTokenizer() - - async def encode(self, shard: Shard, prompt: str) -> np.ndarray: - return np.array(self.tokenizer.encode(prompt)) - - async def sample( - self, x: np.ndarray, temp: float = 0.0, top_p: float = 1.0 - ) -> np.ndarray: - if x[0] > self.num_generate_dummy_tokens: - return np.array([self.tokenizer.eos_token_id]) - return x - - async def decode(self, shard: Shard, tokens: np.ndarray) -> str: - return self.tokenizer.decode(tokens) - - async def infer_tensor( - self, - request_id: str, - shard: Shard, - input_data: np.ndarray, - inference_state: Optional[dict] = None, - ) -> tuple[np.ndarray, Optional[dict]]: - await self.ensure_shard(shard) - return input_data + 1 if self.shard.is_last_layer() else input_data, None - - async def ensure_shard(self, shard: Shard): - if self.shard == shard: - return - self.shard = shard - - async def load_checkpoint(self, shard: Shard, path: str): - await self.ensure_shard(shard) diff --git a/pkg/hanzo-network/src/hanzo_network/inference/inference_engine.py b/pkg/hanzo-network/src/hanzo_network/inference/inference_engine.py deleted file mode 100644 index 128b201ad..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/inference_engine.py +++ /dev/null @@ -1,97 +0,0 @@ -import os -from abc import ABC, abstractmethod -from typing import Optional - -import numpy as np - -from .download.shard_download import ShardDownloader -from .helpers import DEBUG # Make sure to import DEBUG -from .shard import Shard - - -class InferenceEngine(ABC): - session = {} - - @abstractmethod - async def encode(self, shard: Shard, prompt: str) -> np.ndarray: - pass - - @abstractmethod - async def sample(self, x: np.ndarray) -> np.ndarray: - pass - - @abstractmethod - async def decode(self, shard: Shard, tokens: np.ndarray) -> str: - pass - - @abstractmethod - async def infer_tensor( - self, - request_id: str, - shard: Shard, - input_data: np.ndarray, - inference_state: Optional[dict] = None, - ) -> tuple[np.ndarray, Optional[dict]]: - pass - - @abstractmethod - async def load_checkpoint(self, shard: Shard, path: str): - pass - - async def save_checkpoint(self, shard: Shard, path: str): - pass - - async def save_session(self, key, value): - self.session[key] = value - - async def clear_session(self): - self.session.empty() - - async def infer_prompt( - self, - request_id: str, - shard: Shard, - prompt: str, - inference_state: Optional[dict] = None, - ) -> tuple[np.ndarray, Optional[dict]]: - tokens = await self.encode(shard, prompt) - if shard.model_id != "stable-diffusion-2-1-base": - x = tokens.reshape(1, -1) - else: - x = tokens - output_data, inference_state = await self.infer_tensor( - request_id, shard, x, inference_state - ) - - return output_data, inference_state - - -inference_engine_classes = { - "mlx": "MLXDynamicShardInferenceEngine", - "tinygrad": "TinygradDynamicShardInferenceEngine", - "dummy": "DummyInferenceEngine", -} - - -def get_inference_engine(inference_engine_name: str, shard_downloader: ShardDownloader): - if DEBUG >= 2: - print(f"get_inference_engine called with: {inference_engine_name}") - if inference_engine_name == "mlx": - from .inference.mlx.sharded_inference_engine import ( - MLXDynamicShardInferenceEngine, - ) - - return MLXDynamicShardInferenceEngine(shard_downloader) - elif inference_engine_name == "tinygrad": - import tinygrad.helpers - - from .inference.tinygrad.inference import TinygradDynamicShardInferenceEngine - - tinygrad.helpers.DEBUG.value = int(os.getenv("TINYGRAD_DEBUG", default="0")) - - return TinygradDynamicShardInferenceEngine(shard_downloader) - elif inference_engine_name == "dummy": - from .inference.dummy_inference_engine import DummyInferenceEngine - - return DummyInferenceEngine() - raise ValueError(f"Unsupported inference engine: {inference_engine_name}") diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/__init__.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/losses.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/losses.py deleted file mode 100644 index d24150e05..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/losses.py +++ /dev/null @@ -1,43 +0,0 @@ -import mlx.core as mx -import mlx.nn as nn - - -def length_masked_ce_loss(model, inputs, targets, lengths): - # Run model on inputs - logits = model(inputs).astype(mx.float32) - - # Mask padding tokens - length_mask = mx.arange(inputs.shape[1])[None, :] < lengths[:, None] - - # Calculate the loss - ce = nn.losses.cross_entropy(logits, targets) * length_mask - loss = ce.sum() / length_mask.sum() - # print(f"| {inputs=}\n| ==>{logits=}\n| ~^~{ce=}\n| == {loss=}") - return loss - - -# Naive intermediate layer loss, where we replace the targets with gradients and just multiply the output by the gradients to derive the loss. This is naive and may warrant some further iteration, but will do the job for now -def back_gradient_loss(model, inputs, gradients, lengths): - out = model(inputs).astype(mx.float32) - grad = gradients.astype(mx.float32) - - # Mask padding tokens - length_mask = mx.repeat( - mx.arange(inputs.shape[1])[None, :] < lengths[:, None], out.shape[-1] - ).reshape(out.shape) - - masked_sum = (out * length_mask).sum(axis=1) - gradient_lens = mx.abs(grad * masked_sum) - loss = gradient_lens.sum() / length_mask.sum() - # print(f"| {inputs=}\n" - # + f"| ==>{out=}\n" - # + f"| ~^~{masked_sum=}\n" - # + f"| <~>{gradient_lens=}\n" - # + f"| == {loss=}") - return loss - - -loss_fns = { - "back_gradient": back_gradient_loss, - "length_masked_ce": length_masked_ce_loss, -} diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/StableDiffusionPipeline.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/StableDiffusionPipeline.py deleted file mode 100644 index bf08aa33e..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/StableDiffusionPipeline.py +++ /dev/null @@ -1,367 +0,0 @@ -# Adapted from https://github.com/ml-explore/mlx-examples/blob/main/stable_diffusion/stable_diffusion/__init__.py - -import inspect -import time -from dataclasses import dataclass, field -from pathlib import Path - -import mlx.core as mx -import mlx.nn as nn -from tqdm import tqdm - -from .inference.shard import Shard -from .sd_models.clip import CLIPTextModel -from .sd_models.clip import ModelArgs as CLIPArgs -from .sd_models.tokenizer import load_tokenizer -from .sd_models.unet import UNetConfig, UNetModel -from .sd_models.vae import Autoencoder -from .sd_models.vae import ModelArgs as VAEArgs - - -@dataclass -class DiffusionConfig: - beta_schedule: str = "scaled_linear" - beta_start: float = 0.00085 - beta_end: float = 0.012 - num_train_steps: int = 1000 - - @classmethod - def from_dict(cls, params): - return cls( - **{ - k: v - for k, v in params.items() - if k in inspect.signature(cls).parameters - } - ) - - -# Sampler -def _linspace(a, b, num): - x = mx.arange(0, num) / (num - 1) - return (b - a) * x + a - - -def _interp(y, x_new): - """Interpolate the function defined by (arange(0, len(y)), y) at positions x_new.""" - x_low = x_new.astype(mx.int32) - x_high = mx.minimum(x_low + 1, len(y) - 1) - - y_low = y[x_low] - y_high = y[x_high] - delta_x = x_new - x_low - y_new = y_low * (1 - delta_x) + delta_x * y_high - - return y_new - - -class SimpleEulerSampler: - """A simple Euler integrator that can be used to sample from our diffusion models. - - The method ``step()`` performs one Euler step from x_t to x_t_prev. - """ - - def __init__(self, config: DiffusionConfig): - # Compute the noise schedule - if config.beta_schedule == "linear": - betas = _linspace( - config.beta_start, config.beta_end, config.num_train_steps - ) - elif config.beta_schedule == "scaled_linear": - betas = _linspace( - config.beta_start**0.5, config.beta_end**0.5, config.num_train_steps - ).square() - else: - raise NotImplementedError(f"{config.beta_schedule} is not implemented.") - - alphas = 1 - betas - alphas_cumprod = mx.cumprod(alphas) - - self._sigmas = mx.concatenate( - [mx.zeros(1), ((1 - alphas_cumprod) / alphas_cumprod).sqrt()] - ) - - @property - def max_time(self): - return len(self._sigmas) - 1 - - def sample_prior(self, shape, dtype=mx.float32, key=None): - noise = mx.random.normal(shape, key=key) - return ( - noise * self._sigmas[-1] * (self._sigmas[-1].square() + 1).rsqrt() - ).astype(dtype) - - def add_noise(self, x, t, key=None): - noise = mx.random.normal(x.shape, key=key) - s = self.sigmas(t) - return (x + noise * s) * (s.square() + 1).rsqrt() - - def sigmas(self, t): - return _interp(self._sigmas, t) - - def timesteps(self, num_steps: int, start_time=None, dtype=mx.float32): - start_time = start_time or (len(self._sigmas) - 1) - assert 0 < start_time <= (len(self._sigmas) - 1) - steps = _linspace(start_time, 0, num_steps + 1).astype(dtype) - return list(zip(steps, steps[1:])) - - def current_timestep(self, step, total_steps, start_time=None): - if step < total_steps: - steps = self.timesteps(total_steps, start_time) - return steps[step] - else: - return mx.array(0), mx.array(0) - - def step(self, eps_pred, x_t, t, t_prev): - sigma = self.sigmas(t).astype(eps_pred.dtype) - sigma_prev = self.sigmas(t_prev).astype(eps_pred.dtype) - - dt = sigma_prev - sigma - x_t_prev = (sigma.square() + 1).sqrt() * x_t + eps_pred * dt - - x_t_prev = x_t_prev * (sigma_prev.square() + 1).rsqrt() - - return x_t_prev - - -@dataclass -class ShardConfig: - model_id: str - start_layer: int - end_layer: int - n_layers: int - - -@dataclass -class StableDiffusionConfig: - model_type: str - vae: VAEArgs - text_encoder: CLIPArgs - scheduler: DiffusionConfig - unet: UNetConfig - shard: ShardConfig - - @classmethod - def from_dict(cls, params): - return cls( - **{ - k: v - for k, v in params.items() - if k in inspect.signature(cls).parameters - } - ) - - -@dataclass -class ModelArgs(StableDiffusionConfig): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - if isinstance(self.shard, dict): - self.shard = Shard(**self.shard) - - if not isinstance(self.shard, Shard): - raise TypeError( - f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead" - ) - - -class Model(nn.Module): - def __init__(self, config): - super().__init__() - self.model_type = config.model_type - self.config = config - self.model_path = config.vae["path"].split("/vae")[0] - self.shard = config.shard - self.shard_clip, self.shard_encoder, self.shard_unet, self.shard_decoder = ( - model_shards(config.shard) - ) - self.config_clip = CLIPArgs.from_dict(config.text_encoder["config"]) - if self.shard_clip.start_layer != -1: - self.text_encoder = CLIPTextModel(self.config_clip, shard=self.shard_clip) - else: - self.text_encoder = nn.Identity() - self.tokenizer = load_tokenizer( - Path(self.model_path), "vocab.json", "merges.txt" - ) - self.diffusion_config = DiffusionConfig.from_dict(config.scheduler["config"]) - self.sampler = SimpleEulerSampler(self.diffusion_config) - if self.shard_unet.start_layer != -1: - self.config_unet = UNetConfig.from_dict(config.unet["config"]) - self.unet = UNetModel(self.config_unet, self.shard_unet) - else: - self.unet = nn.Identity() - self.config_vae = VAEArgs.from_dict(config.vae["config"]) - if self.shard_encoder.start_layer != -1: - self.encoder = Autoencoder( - self.config_vae, self.shard_encoder, "vae_encoder" - ) - else: - self.encoder = nn.Identity() - if self.shard_decoder.start_layer != -1: - self.decoder = Autoencoder( - self.config_vae, self.shard_decoder, "vae_decoder" - ) - else: - self.decoder = nn.Identity() - - def __call__( - self, - x, - step=0, - cfg_weight: float = 7.5, - total_steps=50, - conditioning=None, - mask=None, - residual=None, - x_t_prev=None, - is_finished=False, - is_step_finished=False, - image=None, - strength=0.65, - start_step=None, - ): - t, t_prev = self.sampler.current_timestep( - step=step, total_steps=total_steps, start_time=start_step - ) - is_finished = False - is_step_finished = False - if t.item() == 1000: - if self.shard_clip.start_layer == 0: - conditioning = x - if self.shard_clip.start_layer != -1: - conditioning, mask = self.text_encoder(conditioning, mask) - seed = int(time.time()) - mx.random.seed(seed) - if image is None: - if self.shard_encoder.is_last_layer(): - x = self.sampler.sample_prior( - (1, *(64, 64), self.config_vae.latent_channels_in), - dtype=mx.float32, - ) - x_t_prev = x - start_step = self.sampler.max_time - else: - if self.shard_encoder.start_layer != -1: - image = self.encoder.encode(image) - if self.shard_encoder.is_last_layer(): - start_step = self.sampler.max_time * strength - total_steps = int(total_steps * strength) - image = mx.broadcast_to(image, (1,) + image.shape[1:]) - x_t_prev = self.sampler.add_noise(image, mx.array(start_step)) - image = None - t, t_prev = self.sampler.current_timestep( - step=step, total_steps=total_steps, start_time=start_step - ) - # Perform the denoising loop - if self.shard_unet.start_layer != -1: - with tqdm(total=total_steps, initial=step + 1): - if step < total_steps: - x = x_t_prev - if self.shard_unet.is_first_layer(): - x_t_unet = ( - mx.concatenate([x] * 2, axis=0) if cfg_weight > 1 else x - ) - else: - x_t_unet = x - t_unet = mx.broadcast_to(t, [len(x_t_unet)]) - x, residual = self.unet( - x_t_unet, t_unet, encoder_x=conditioning, residuals=residual - ) - if self.shard_unet.is_last_layer(): - if cfg_weight > 1: - eps_text, eps_neg = x.split(2) - eps_pred = eps_neg + cfg_weight * (eps_text - eps_neg) - x = self.sampler.step(eps_pred, x_t_prev, t, t_prev) - x_t_prev = x - mx.eval(x) - - if self.shard_decoder.is_last_layer(): - is_step_finished = True - if self.shard_decoder.start_layer != -1: - x = self.decoder.decode(x) - if self.shard_decoder.is_last_layer(): - x = mx.clip(x / 2 + 0.5, 0, 1) - B, H, W, C = x.shape - x = x.reshape(1, B // 1, H, W, C).transpose(0, 2, 1, 3, 4) - x = x.reshape(1 * H, B // 1 * W, C) - x = (x * 255).astype(mx.uint8) - if t_prev.item() == 0: - is_finished = True - mx.eval(x) - - return x, { - "conditioning": conditioning, - "mask": mask, - "residual": residual, - "x_t_prev": x_t_prev, - "is_finished": is_finished, - "is_step_finished": is_step_finished, - "step": step, - "total_steps": total_steps, - "start_step": start_step, - "image": image, - } - - def load(self): - if self.shard_encoder.start_layer != -1: - vae_weights = mx.load(self.config_vae.weight_files[0]) - vae_weights = self.encoder.sanitize(vae_weights) - self.encoder.load_weights(list(vae_weights.items()), strict=True) - if self.shard_decoder.start_layer != -1: - vae_weights = mx.load(self.config_vae.weight_files[0]) - vae_weights = self.decoder.sanitize(vae_weights) - self.decoder.load_weights(list(vae_weights.items()), strict=True) - if self.shard_clip.start_layer != -1: - clip_weights = mx.load(self.config_clip.weight_files[0]) - clip_weights = self.text_encoder.sanitize(clip_weights) - self.text_encoder.load_weights(list(clip_weights.items()), strict=True) - if self.shard_unet.start_layer != -1: - unet_weights = mx.load(self.config_unet.weight_files[0]) - unet_weights = self.unet.sanitize(unet_weights) - self.unet.load_weights(list(unet_weights.items()), strict=True) - - -def model_shards(shard: ShardConfig): - def create_shard(shard, model_ranges): - start_layer = shard.start_layer - end_layer = shard.end_layer - - shards = {} - - for model_name, (range_start, range_end) in model_ranges.items(): - if start_layer < range_end and end_layer >= range_start: - # Calculate the overlap with the model range - overlap_start = max(start_layer, range_start) - overlap_end = min(end_layer, range_end - 1) - - # Adjust the layers relative to the model's range - relative_start = overlap_start - range_start - relative_end = overlap_end - range_start - shards[model_name] = Shard( - model_name, relative_start, relative_end, range_end - range_start - ) - else: - # If no overlap, create a zero-layer shard - shards[model_name] = Shard(model_name, -1, -1, range_end - range_start) - - return shards - - # Define the ranges for different models - model_ranges = { - "clip": (0, 12), - "vae_encoder": (12, 17), - "unet": (17, 26), - "vae_decoder": (26, 31), # Example range for unet - } - - # Call the function and get the shards for all models - shards = create_shard(shard, model_ranges) - - # Access individual shards - shard_clip = shards["clip"] - shard_encoder = shards["vae_encoder"] - shard_unet = shards["unet"] - shard_decoder = shards["vae_decoder"] - - return shard_clip, shard_encoder, shard_unet, shard_decoder diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/__init__.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/base.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/base.py deleted file mode 100644 index d8b6dcec7..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/base.py +++ /dev/null @@ -1,15 +0,0 @@ -from typing import Optional - -import mlx.core as mx -import mlx.nn as nn -from mlx_lm.models.cache import KVCache - - -class IdentityBlock(nn.Module): - def __call__( - self, - x: mx.array, - mask: Optional[mx.array] = None, - cache: Optional[KVCache] = None, - ) -> mx.array: - return x diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/deepseek_v2.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/deepseek_v2.py deleted file mode 100644 index dfaf8e53f..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/deepseek_v2.py +++ /dev/null @@ -1,141 +0,0 @@ -from dataclasses import dataclass, field -from typing import Optional - -import mlx.core as mx -import mlx.nn as nn -from mlx_lm.models.cache import KVCache -from mlx_lm.models.deepseek_v2 import DeepseekV2DecoderLayer, ModelArgs - -from .base import IdentityBlock -from .inference.shard import Shard - - -@dataclass -class ModelArgs(ModelArgs): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - if isinstance(self.shard, Shard): - return - if not isinstance(self.shard, dict): - raise TypeError( - f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead" - ) - - self.shard = Shard(**self.shard) - - -class DeepseekV2Model(nn.Module): - def __init__(self, config: ModelArgs): - super().__init__() - self.args = config - self.num_hidden_layers = config.num_hidden_layers - self.vocab_size = config.vocab_size - if self.args.shard.is_first_layer(): - self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size) - - self.layers = [] - for i in range(self.num_hidden_layers): - if self.args.shard.start_layer <= i <= self.args.shard.end_layer: - self.layers.append(DeepseekV2DecoderLayer(config, i)) - else: - self.layers.append(IdentityBlock()) - - if self.args.shard.is_last_layer(): - self.norm = nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps) - - def __call__( - self, - x: mx.array, - cache: Optional[KVCache] = None, - ) -> mx.array: - if self.args.shard.is_first_layer(): - h = self.embed_tokens(x) - else: - h = x - - mask = None - T = h.shape[1] - if T > 1: - mask = nn.MultiHeadAttention.create_additive_causal_mask(T) - mask = mask.astype(h.dtype) - - if cache is None: - cache = [None] * len(self.layers) - - for layer, c in zip(self.layers, cache): - h = layer(h, mask, c) - - if self.args.shard.is_last_layer(): - h = self.norm(h) - return h - - -class Model(nn.Module): - def __init__(self, config: ModelArgs): - super().__init__() - self.args = config - self.model_type = config.model_type - self.model = DeepseekV2Model(config) - if self.args.shard.is_last_layer(): - self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) - - def __call__( - self, - inputs: mx.array, - cache: Optional[KVCache] = None, - ): - out = self.model(inputs, cache) - if self.args.shard.is_last_layer(): - return self.lm_head(out) - return out - - def sanitize(self, weights): - shard_state_dict = {} - - for key, value in weights.items(): - if key.startswith("model.layers."): - layer_num = int(key.split(".")[2]) - if ( - self.args.shard.start_layer - <= layer_num - <= self.args.shard.end_layer - ): - shard_state_dict[key] = value - elif ( - self.args.shard.is_first_layer() - and key.startswith("model.embed_tokens") - or self.args.shard.is_last_layer() - and (key.startswith("model.norm") or key.startswith("lm_head")) - ): - shard_state_dict[key] = value - - for layer in range(self.args.num_hidden_layers): - prefix = f"model.layers.{layer}" - for n, m in [("w1", "gate_proj"), ("w2", "down_proj"), ("w3", "up_proj")]: - for k in ["weight", "scales", "biases"]: - if f"{prefix}.mlp.experts.0.{m}.{k}" in shard_state_dict: - to_join = [ - shard_state_dict.pop(f"{prefix}.mlp.experts.{e}.{m}.{k}") - for e in range(self.args.n_routed_experts) - ] - shard_state_dict[f"{prefix}.mlp.switch_mlp.{m}.{k}"] = mx.stack( - to_join - ) - - return shard_state_dict - - @property - def layers(self): - return self.model.layers - - @property - def head_dim(self): - return ( - self.args.qk_nope_head_dim + self.args.qk_rope_head_dim, - self.args.v_head_dim, - ) - - @property - def n_kv_heads(self): - return self.args.num_key_value_heads diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/deepseek_v3.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/deepseek_v3.py deleted file mode 100644 index 24b5033bb..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/deepseek_v3.py +++ /dev/null @@ -1,147 +0,0 @@ -from dataclasses import dataclass, field -from typing import Optional - -import mlx.core as mx -import mlx.nn as nn -from mlx_lm.models.cache import KVCache -from mlx_lm.models.deepseek_v3 import ( - DeepseekV3DecoderLayer, -) -from mlx_lm.models.deepseek_v3 import ( - ModelArgs as V3ModelArgs, -) - -from .base import IdentityBlock -from .inference.shard import Shard - - -@dataclass -class ModelArgs(V3ModelArgs): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - if isinstance(self.shard, Shard): - return - if not isinstance(self.shard, dict): - raise TypeError( - f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead" - ) - - self.shard = Shard(**self.shard) - - -class DeepseekV3Model(nn.Module): - def __init__(self, config: ModelArgs): - super().__init__() - self.args = config - self.num_hidden_layers = config.num_hidden_layers - self.vocab_size = config.vocab_size - if self.args.shard.is_first_layer(): - self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size) - - self.layers = [] - for i in range(self.num_hidden_layers): - if self.args.shard.start_layer <= i <= self.args.shard.end_layer: - self.layers.append(DeepseekV3DecoderLayer(config, i)) - else: - self.layers.append(IdentityBlock()) - - if self.args.shard.is_last_layer(): - self.norm = nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps) - - def __call__( - self, - x: mx.array, - cache: Optional[KVCache] = None, - ) -> mx.array: - if self.args.shard.is_first_layer(): - h = self.embed_tokens(x) - else: - h = x - - mask = None - T = h.shape[1] - if T > 1: - mask = nn.MultiHeadAttention.create_additive_causal_mask(T) - mask = mask.astype(h.dtype) - - if cache is None: - cache = [None] * len(self.layers) - - for layer, c in zip(self.layers, cache): - h = layer(h, mask, c) - - if self.args.shard.is_last_layer(): - h = self.norm(h) - return h - - -class Model(nn.Module): - def __init__(self, config: ModelArgs): - super().__init__() - self.args = config - self.model_type = config.model_type - self.model = DeepseekV3Model(config) - if self.args.shard.is_last_layer(): - self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) - - def __call__( - self, - inputs: mx.array, - cache: Optional[KVCache] = None, - ): - out = self.model(inputs, cache) - if self.args.shard.is_last_layer(): - return self.lm_head(out) - return out - - def sanitize(self, weights): - shard_state_dict = {} - - for key, value in weights.items(): - if key.startswith("model.layers."): - layer_num = int(key.split(".")[2]) - if ( - self.args.shard.start_layer - <= layer_num - <= self.args.shard.end_layer - ): - shard_state_dict[key] = value - elif ( - self.args.shard.is_first_layer() - and key.startswith("model.embed_tokens") - or self.args.shard.is_last_layer() - and (key.startswith("model.norm") or key.startswith("lm_head")) - ): - shard_state_dict[key] = value - - for layer in range(self.args.num_hidden_layers): - prefix = f"model.layers.{layer}" - for n, m in [("w1", "gate_proj"), ("w2", "down_proj"), ("w3", "up_proj")]: - for k in ["weight", "scales", "biases"]: - expert_key = f"{prefix}.mlp.experts.0.{m}.{k}" - if expert_key in shard_state_dict: - to_join = [ - shard_state_dict.pop(f"{prefix}.mlp.experts.{e}.{m}.{k}") - for e in range(self.args.n_routed_experts) - ] - shard_state_dict[f"{prefix}.mlp.switch_mlp.{m}.{k}"] = mx.stack( - to_join - ) - - return shard_state_dict - - @property - def layers(self): - return self.model.layers - - @property - def head_dim(self): - return ( - self.args.qk_nope_head_dim + self.args.qk_rope_head_dim, - self.args.v_head_dim, - ) - - @property - def n_kv_heads(self): - return self.args.num_key_value_heads diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/gemma2.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/gemma2.py deleted file mode 100644 index 0edb4da20..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/gemma2.py +++ /dev/null @@ -1,126 +0,0 @@ -from dataclasses import dataclass, field - -import mlx.core as mx -import mlx.nn as nn -from mlx_lm.models.base import create_attention_mask -from mlx_lm.models.gemma2 import ModelArgs, RMSNorm, TransformerBlock - -from ...shard import Shard -from .base import IdentityBlock - - -@dataclass -class ModelArgs(ModelArgs): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - if isinstance(self.shard, Shard): - return - if not isinstance(self.shard, dict): - raise TypeError( - f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead" - ) - - self.shard = Shard(**self.shard) - - -class GemmaModel(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.vocab_size = args.vocab_size - self.num_hidden_layers = args.num_hidden_layers - assert self.vocab_size > 0 - if args.shard.is_first_layer() or args.shard.is_last_layer(): - self.embed_tokens = nn.Embedding(args.vocab_size, args.hidden_size) - self.layers = [] - for i in range(self.num_hidden_layers): - if args.shard.start_layer <= i <= args.shard.end_layer: - self.layers.append(TransformerBlock(args=args)) - else: - self.layers.append(IdentityBlock()) - if args.shard.is_last_layer(): - self.norm = RMSNorm(args.hidden_size, eps=args.rms_norm_eps) - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - if self.args.shard.is_first_layer(): - h = self.embed_tokens(inputs) - h = h * (self.args.hidden_size**0.5) - else: - h = inputs - - mask = None - if h.ndim > 1 and h.shape[1] > 1: - mask = create_attention_mask(h, cache) - - if cache is None: - cache = [None] * len(self.layers) - - for layer, c in zip(self.layers, cache): - h = layer(h, mask, cache=c) - - if self.args.shard.is_last_layer(): - h = self.norm(h) - return h - - -class Model(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.model_type = args.model_type - self.model = GemmaModel(args) - if args.shard.is_last_layer(): - self.final_logit_softcapping = args.final_logit_softcapping - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - out = self.model(inputs, cache) - if self.args.shard.is_last_layer(): - out = self.model.embed_tokens.as_linear(out) - out = mx.tanh(out / self.final_logit_softcapping) - out = out * self.final_logit_softcapping - return out - - def sanitize(self, weights): - shard_state_dict = {} - - for key, value in weights.items(): - if "self_attn.rotary_emb.inv_freq" in key: - continue - if key.startswith("model.layers."): - layer_num = int(key.split(".")[2]) - if ( - self.args.shard.start_layer - <= layer_num - <= self.args.shard.end_layer - ): - shard_state_dict[key] = value - elif ( - (self.args.shard.is_first_layer() or self.args.shard.is_last_layer()) - and key.startswith("model.embed_tokens") - or self.args.shard.is_last_layer() - and (key.startswith("model.norm")) - ): - shard_state_dict[key] = value - - return shard_state_dict - - @property - def layers(self): - return self.model.layers - - @property - def head_dim(self): - return self.args.head_dim - - @property - def n_kv_heads(self): - return self.args.num_key_value_heads diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/llama.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/llama.py deleted file mode 100644 index 57407e522..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/llama.py +++ /dev/null @@ -1,140 +0,0 @@ -from dataclasses import dataclass, field - -import mlx.core as mx -import mlx.nn as nn -from mlx_lm.models.base import create_attention_mask -from mlx_lm.models.llama import ModelArgs, TransformerBlock - -from ...shard import Shard -from .base import IdentityBlock - - -@dataclass -class ModelArgs(ModelArgs): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - super().__post_init__() # Ensure parent initializations are respected - - if isinstance(self.shard, Shard): - return - if not isinstance(self.shard, dict): - raise TypeError( - f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead" - ) - - self.shard = Shard(**self.shard) - - -class LlamaModel(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.vocab_size = args.vocab_size - self.num_hidden_layers = args.num_hidden_layers - assert self.vocab_size > 0 - if args.shard.is_first_layer() or ( - args.shard.is_last_layer() and args.tie_word_embeddings - ): - self.embed_tokens = nn.Embedding(args.vocab_size, args.hidden_size) - self.layers = [] - for i in range(self.num_hidden_layers): - if args.shard.start_layer <= i <= args.shard.end_layer: - self.layers.append(TransformerBlock(args=args)) - else: - self.layers.append(IdentityBlock()) - if args.shard.is_last_layer(): - self.norm = nn.RMSNorm(args.hidden_size, eps=args.rms_norm_eps) - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - if self.args.shard.is_first_layer(): - h = self.embed_tokens(inputs) - else: - h = inputs - - mask = None - if h.ndim > 1 and h.shape[1] > 1: - mask = create_attention_mask(h, cache) - - if cache is None: - cache = [None] * len(self.layers) - - for layer, c in zip(self.layers, cache): - h = layer(h, mask, cache=c) - - if self.args.shard.is_last_layer(): - h = self.norm(h) - return h - - -class Model(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.model_type = args.model_type - self.model = LlamaModel(args) - if args.shard.is_last_layer(): - if not args.tie_word_embeddings: - self.lm_head = nn.Linear(args.hidden_size, args.vocab_size, bias=False) - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - out = self.model(inputs, cache) - if self.args.shard.is_last_layer(): - if self.args.tie_word_embeddings: - out = self.model.embed_tokens.as_linear(out) - else: - out = self.lm_head(out) - return out - - def sanitize(self, weights): - shard_state_dict = {} - - for key, value in weights.items(): - if "self_attn.rotary_emb.inv_freq" in key: - continue - if key.startswith("model.layers."): - layer_num = int(key.split(".")[2]) - if ( - self.args.shard.start_layer - <= layer_num - <= self.args.shard.end_layer - ): - shard_state_dict[key] = value - elif ( - self.args.shard.is_first_layer() - and key.startswith("model.embed_tokens") - or (self.args.shard.is_last_layer() and self.args.tie_word_embeddings) - and key.startswith("model.embed_tokens") - or ( - self.args.shard.is_last_layer() - and not self.args.tie_word_embeddings - ) - and key.startswith("lm_head") - or self.args.shard.is_last_layer() - and (key.startswith("model.norm")) - ): - shard_state_dict[key] = value - - return shard_state_dict - - @property - def layers(self): - return self.model.layers - - @property - def head_dim(self): - return ( - self.args.head_dim or self.args.hidden_size // self.args.num_attention_heads - ) - - @property - def n_kv_heads(self): - return self.args.num_key_value_heads diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/llava.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/llava.py deleted file mode 100644 index 42fa8a49f..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/llava.py +++ /dev/null @@ -1,659 +0,0 @@ -# Copyright ยฉ 2024 Apple Inc. - -import inspect -import math -from dataclasses import dataclass, field -from typing import Dict, Optional, Union - -import mlx.core as mx -import mlx.nn as nn -import numpy as np -from mlx_lm.models.base import BaseModelArgs, KVCache - -from .base import IdentityBlock -from .inference.shard import Shard - - -@dataclass -class VisionConfig: - model_type: str - num_hidden_layers: int = 24 - hidden_size: int = 1024 - intermediate_size: int = 4096 - num_attention_heads: int = 16 - image_size: int = 336 - patch_size: int = 14 - projection_dim: int = 768 - vocab_size: int = 32000 - num_channels: int = 3 - layer_norm_eps: float = 1e-5 - - @classmethod - def from_dict(cls, params): - return cls( - **{ - k: v - for k, v in params.items() - if k in inspect.signature(cls).parameters - } - ) - - -class VisionAttention(nn.Module): - def __init__( - self, - dims: int, - num_heads: int, - query_input_dims: Optional[int] = None, - key_input_dims: Optional[int] = None, - value_input_dims: Optional[int] = None, - value_dims: Optional[int] = None, - value_output_dims: Optional[int] = None, - bias: bool = False, - ): - super().__init__() - - if (dims % num_heads) != 0: - raise ValueError( - f"The input feature dimensions should be divisible by the number of heads ({dims} % {num_heads}) != 0" - ) - - query_input_dims = query_input_dims or dims - key_input_dims = key_input_dims or dims - value_input_dims = value_input_dims or key_input_dims - value_dims = value_dims or dims - value_output_dims = value_output_dims or dims - - self.num_heads = num_heads - self.q_proj = nn.Linear(query_input_dims, dims, bias=bias) - self.k_proj = nn.Linear(key_input_dims, dims, bias=bias) - self.v_proj = nn.Linear(value_input_dims, value_dims, bias=bias) - self.out_proj = nn.Linear(value_dims, value_output_dims, bias=bias) - - def __call__(self, queries, keys, values, mask=None): - queries = self.q_proj(queries) - keys = self.k_proj(keys) - values = self.v_proj(values) - - num_heads = self.num_heads - B, L, D = queries.shape - _, S, _ = keys.shape - queries = queries.reshape(B, L, num_heads, -1).transpose(0, 2, 1, 3) - keys = keys.reshape(B, S, num_heads, -1).transpose(0, 2, 3, 1) - values = values.reshape(B, S, num_heads, -1).transpose(0, 2, 1, 3) - - scale = math.sqrt(1 / queries.shape[-1]) - scores = (queries * scale) @ keys - if mask is not None: - scores = scores + mask.astype(scores.dtype) - scores = mx.softmax(scores, axis=-1) - values_hat = (scores @ values).transpose(0, 2, 1, 3).reshape(B, L, -1) - - return self.out_proj(values_hat) - - -class VisionMLP(nn.Module): - def __init__(self, config: VisionConfig): - super().__init__() - self.activation_fn = nn.GELU(approx="fast") - self.fc1 = nn.Linear(config.hidden_size, config.intermediate_size) - self.fc2 = nn.Linear(config.intermediate_size, config.hidden_size) - - def __call__(self, x: mx.array) -> mx.array: - x = self.activation_fn(self.fc1(x)) - x = self.fc2(x) - return x - - -class VisionEncoderLayer(nn.Module): - def __init__(self, config: VisionConfig): - super().__init__() - self.embed_dim = config.hidden_size - self.self_attn = VisionAttention( - config.hidden_size, config.num_attention_heads, bias=True - ) - self.layer_norm1 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps) - self.mlp = VisionMLP(config) - self.layer_norm2 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps) - - def __call__(self, x: mx.array, mask: Optional[mx.array] = None) -> mx.array: - y = self.layer_norm1(x) - y = self.self_attn(y, y, y, mask) - x = x + y - y = self.layer_norm2(x) - y = self.mlp(y) - return x + y - - -class VisionEncoder(nn.Module): - def __init__(self, config: VisionConfig): - super().__init__() - self.layers = [ - VisionEncoderLayer(config) for _ in range(config.num_hidden_layers) - ] - - -class VisionEmbeddings(nn.Module): - def __init__(self, config: VisionConfig): - super().__init__() - self.config = config - self.embed_dim = config.hidden_size - self.image_size = config.image_size - self.patch_size = config.patch_size - - self.class_embedding = mx.zeros((config.hidden_size,)) - - self.patch_embedding = nn.Conv2d( - in_channels=config.num_channels, - out_channels=self.embed_dim, - kernel_size=self.patch_size, - stride=self.patch_size, - bias=False, - ) - - self.num_patches = (self.image_size // self.patch_size) ** 2 - self.num_positions = self.num_patches + 1 - self.position_embedding = nn.Embedding(self.num_positions, self.embed_dim) - - def __call__(self, x: mx.array) -> mx.array: - batch_size = x.shape[0] - patch_embeddings = self.patch_embedding(x) - patch_embeddings = mx.flatten(patch_embeddings, start_axis=1, end_axis=2) - embed_dim = patch_embeddings.shape[-1] - cls_embeddings = mx.broadcast_to( - self.class_embedding, (batch_size, 1, embed_dim) - ) - embeddings = mx.concatenate((cls_embeddings, patch_embeddings), axis=1) - embeddings += self.position_embedding.weight - return embeddings - - -class ClipVisionModel(nn.Module): - def __init__(self, config: VisionConfig): - super().__init__() - self.embeddings = VisionEmbeddings(config) - self.pre_layrnorm = nn.LayerNorm(config.hidden_size) - self.encoder = VisionEncoder(config) - self.post_layernorm = nn.LayerNorm(config.hidden_size) - - def __call__( - self, - x: mx.array, - output_hidden_states: Optional[bool] = None, - ) -> mx.array: - x = self.embeddings(x) - x = self.pre_layrnorm(x) - - encoder_states = (x,) if output_hidden_states else None - - for layer in self.encoder.layers: - x = layer(x, mask=None) - if output_hidden_states: - encoder_states = encoder_states + (x,) - - pooler_output = self.post_layernorm(x[:, 0, :]) - return pooler_output, x, encoder_states - - -class VisionModel(nn.Module): - def __init__(self, config: VisionConfig): - super().__init__() - - self.model_type = config.model_type - if self.model_type != "clip_vision_model": - raise ValueError(f"Unsupported model type: {self.model_type}") - - self.vision_model = ClipVisionModel(config) - - def __call__( - self, x: mx.array, output_hidden_states: Optional[bool] = None - ) -> mx.array: - return self.vision_model(x, output_hidden_states) - - def sanitize(self, weights): - sanitized_weights = {} - for k, v in weights.items(): - if "position_ids" in k: - # Remove unused position_ids - continue - elif "patch_embedding.weight" in k: - # PyTorch conv2d weight tensors have shape: - # [out_channels, in_channels, kH, KW] - # MLX conv2d expects the weight be of shape: - # [out_channels, kH, KW, in_channels] - sanitized_weights[k] = v.transpose(0, 2, 3, 1) - else: - sanitized_weights[k] = v - - return sanitized_weights - - -@dataclass -class TextConfig: - model_type: str - hidden_size: int = 4096 - num_hidden_layers: int = 32 - intermediate_size: int = 11008 - num_attention_heads: int = 32 - head_dim: int = None - rms_norm_eps: float = 1e-6 - vocab_size: int = 32000 - num_key_value_heads: int = None - rope_theta: float = 10000 - rope_traditional: bool = False - rope_scaling: Optional[Dict[str, Union[float, str]]] = None - - @classmethod - def from_dict(cls, params): - return cls( - **{ - k: v - for k, v in params.items() - if k in inspect.signature(cls).parameters - } - ) - - def __post_init__(self): - if self.num_key_value_heads is None: - self.num_key_value_heads = self.num_attention_heads - - if self.head_dim is None: - self.head_dim = self.hidden_size // self.num_attention_heads - - if self.model_type is None: - self.model_type = "llama" - - if self.rope_scaling: - required_keys = {"factor", "type"} - if not all(key in self.rope_scaling for key in required_keys): - raise ValueError(f"rope_scaling must contain keys {required_keys}") - - if self.rope_scaling["type"] != "linear": - raise ValueError("rope_scaling 'type' currently only supports 'linear'") - - -class TextAttention(nn.Module): - def __init__(self, config: TextConfig): - super().__init__() - - dim = config.hidden_size - self.n_heads = n_heads = config.num_attention_heads - self.n_kv_heads = n_kv_heads = config.num_key_value_heads - - self.repeats = n_heads // n_kv_heads - - head_dim = config.hidden_size // n_heads - self.scale = head_dim**-0.5 - - self.q_proj = nn.Linear(dim, n_heads * head_dim, bias=False) - self.k_proj = nn.Linear(dim, n_kv_heads * head_dim, bias=False) - self.v_proj = nn.Linear(dim, n_kv_heads * head_dim, bias=False) - self.o_proj = nn.Linear(n_heads * head_dim, dim, bias=False) - - rope_scale = ( - 1 / config.rope_scaling["factor"] - if config.rope_scaling is not None - and config.rope_scaling["type"] == "linear" - else 1 - ) - self.rope = nn.RoPE( - head_dim, - traditional=config.rope_traditional, - base=config.rope_theta, - scale=rope_scale, - ) - - def __call__( - self, - x: mx.array, - mask: Optional[mx.array] = None, - cache: Optional[KVCache] = None, - ) -> mx.array: - B, L, D = x.shape - - queries, keys, values = self.q_proj(x), self.k_proj(x), self.v_proj(x) - - # Prepare the queries, keys and values for the attention computation - queries = queries.reshape(B, L, self.n_heads, -1).transpose(0, 2, 1, 3) - keys = keys.reshape(B, L, self.n_kv_heads, -1).transpose(0, 2, 1, 3) - values = values.reshape(B, L, self.n_kv_heads, -1).transpose(0, 2, 1, 3) - - if cache is not None: - queries = self.rope(queries, offset=cache.offset) - keys = self.rope(keys, offset=cache.offset) - keys, values = cache.update_and_fetch(keys, values) - else: - queries = self.rope(queries) - keys = self.rope(keys) - - output = mx.fast.scaled_dot_product_attention( - queries, keys, values, scale=self.scale, mask=mask - ) - output = output.transpose(0, 2, 1, 3).reshape(B, L, -1) - return self.o_proj(output) - - -class TextMLP(nn.Module): - def __init__(self, dim, hidden_dim): - super().__init__() - self.gate_proj = nn.Linear(dim, hidden_dim, bias=False) - self.down_proj = nn.Linear(hidden_dim, dim, bias=False) - self.up_proj = nn.Linear(dim, hidden_dim, bias=False) - - def __call__(self, x) -> mx.array: - return self.down_proj(nn.silu(self.gate_proj(x)) * self.up_proj(x)) - - -class TransformerBlock(nn.Module): - def __init__(self, config: TextConfig): - super().__init__() - self.num_attention_heads = config.num_attention_heads - self.hidden_size = config.hidden_size - self.self_attn = TextAttention(config) - self.mlp = TextMLP(config.hidden_size, config.intermediate_size) - self.input_layernorm = nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps) - self.post_attention_layernorm = nn.RMSNorm( - config.hidden_size, eps=config.rms_norm_eps - ) - self.config = config - - def __call__( - self, - x: mx.array, - mask: Optional[mx.array] = None, - cache: Optional[KVCache] = None, - ) -> mx.array: - r = self.self_attn(self.input_layernorm(x), mask, cache) - h = x + r - r = self.mlp(self.post_attention_layernorm(h)) - out = h + r - return out - - -class Llama(nn.Module): - def __init__(self, config: TextConfig, shard: Shard): - super().__init__() - self.config = config - self.shard = shard - self.vocab_size = config.vocab_size - self.model_type = config.model_type - self.num_hidden_layers = config.num_hidden_layers - self.num_key_value_heads = config.num_key_value_heads - self.head_dim = config.head_dim - assert self.vocab_size > 0 - if self.shard.is_first_layer(): - self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size) - self.layers = [] - for i in range(self.num_hidden_layers): - if self.shard.start_layer <= i <= self.shard.end_layer: - self.layers.append(TransformerBlock(config=config)) - else: - self.layers.append(IdentityBlock()) - if self.shard.is_last_layer(): - self.norm = nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps) - - def __call__( - self, - inputs: mx.array, - cache=None, - inputs_embeds=None, - ): - # for passing merged input embeddings - if inputs_embeds is None: - if self.shard.is_first_layer(): - h = self.embed_tokens(inputs) - else: - h = inputs - else: - h = inputs_embeds - - mask = None - if h.shape[1] > 1: - mask = nn.MultiHeadAttention.create_additive_causal_mask(h.shape[1]) - mask = mask.astype(h.dtype) - - if cache is None: - cache = [None] * len(self.layers) - - for layer, c in zip(self.layers, cache): - h = layer(h, mask, c) - - if self.shard.is_last_layer(): - h = self.norm(h) - return h - - -class LanguageModel(nn.Module): - def __init__(self, config: TextConfig, shard: Shard): - super().__init__() - self.model_type = config.model_type - if self.model_type != "llama": - raise ValueError( - f"Model type {self.model_type} not supported. Currently only 'llama' is supported" - ) - self.shard = shard - self.model = Llama(config, shard) - if self.shard.is_last_layer(): - self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) - - def __call__( - self, - inputs: mx.array, - cache=None, - inputs_embeds=None, - ): - out = self.model(inputs, cache, inputs_embeds) - if self.shard.is_last_layer(): - out = self.lm_head(out) - return out - - def sanitize(self, weights): - shard_state_dict = {} - for key, value in weights.items(): - if "self_attn.rotary_emb.inv_freq" in key: - continue - - if key.startswith("language_model.model.layers."): - layer_num = int(key.split(".")[3]) - if ( - layer_num < self.shard.start_layer - or layer_num > self.shard.end_layer - ): - continue - if ( - not self.shard.is_first_layer() - and key.startswith("language_model.model.embed_tokens") - or not self.shard.is_last_layer() - and ( - key.startswith("language_model.model.norm") - or key.startswith("language_model.lm_head") - ) - ): - continue - - shard_state_dict[key] = value - - return shard_state_dict - - -@dataclass -class LlaVAConfig(BaseModelArgs): - text_config: TextConfig - vision_config: VisionConfig = None - model_type: str = "llava" - ignore_index: int = -100 - image_token_index: int = 32000 - vision_feature_select_strategy: str = "default" - vision_feature_layer: int = -2 - vocab_size: int = 32000 - - @classmethod - def from_dict(cls, params): - updated_params = {} - class_params = inspect.signature(cls).parameters - for k, v in params.items(): - if k in class_params: - if k in ["text_config", "vision_config"]: - v = class_params[k].annotation.from_dict(v) - updated_params.update({k: v}) - - return cls(**updated_params) - - -@dataclass -class ModelArgs(LlaVAConfig): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - if isinstance(self.shard, dict): - self.shard = Shard(**self.shard) - - if not isinstance(self.shard, Shard): - raise TypeError( - f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead" - ) - - if not self.shard.is_first_layer(): - self.vision_config = None - - -class LlavaMultiModalProjector(nn.Module): - def __init__(self, config: LlaVAConfig): - super().__init__() - self.linear_1 = nn.Linear( - config.vision_config.hidden_size, config.text_config.hidden_size, bias=True - ) - self.gelu = nn.GELU() - self.linear_2 = nn.Linear( - config.text_config.hidden_size, config.text_config.hidden_size, bias=True - ) - - def __call__(self, x: mx.array) -> mx.array: - x = self.linear_1(x) - x = self.gelu(x) - x = self.linear_2(x) - return x - - -class Model(nn.Module): - def __init__(self, config: ModelArgs): - super().__init__() - self.config = config - self.model_type = config.model_type - if config.vision_config: - self.vision_tower = VisionModel(config.vision_config) - self.multi_modal_projector = LlavaMultiModalProjector(config) - self.vision_feature_layer = config.vision_feature_layer - self.vision_feature_select_strategy = config.vision_feature_select_strategy - self.language_model = LanguageModel(config.text_config, config.shard) - - def get_input_embeddings( - self, - input_ids: Optional[mx.array] = None, - pixel_values: Optional[mx.array] = None, - ): - if pixel_values is None: - return self.language_model(input_ids) - - # Get the input embeddings from the language model - inputs_embeds = self.language_model.model.embed_tokens(input_ids) - - # Get the ouptut hidden states from the vision model - *_, hidden_states = self.vision_tower( - pixel_values.transpose(0, 2, 3, 1), output_hidden_states=True - ) - - # Select the hidden states from the desired layer - selected_image_feature = hidden_states[self.vision_feature_layer] - - if self.vision_feature_select_strategy == "default": - selected_image_feature = selected_image_feature[:, 1:] - elif self.vision_feature_select_strategy == "full": - selected_image_feature = selected_image_feature - else: - raise ValueError( - f"Unexpected feature selection strategy: {self.vision_feature_select_strategy}" - ) - - # Pass image features through the multi-modal projector - image_features = self.multi_modal_projector(selected_image_feature) - - # Insert special image tokens in the input_ids - final_inputs_embeds = self._merge_input_ids_with_image_features( - image_features, inputs_embeds, input_ids - ) - return final_inputs_embeds - - def _merge_input_ids_with_image_features( - self, image_features, inputs_embeds, input_ids - ): - image_token_index = self.config.image_token_index - num_images, num_image_patches, embed_dim = image_features.shape - - # Positions of tokens in input_ids, assuming batch size is 1 - image_positions = np.where(input_ids[0] == image_token_index)[0].tolist() - - if len(image_positions) != num_images: - raise ValueError( - f"The number of image tokens ({len(image_positions)}) does not " - f" match the number of image inputs ({num_images})." - ) - - text_segments = [] - start_idx = 0 - - for position in image_positions: - text_segments.append(inputs_embeds[:, start_idx:position]) - start_idx = position + 1 - - image_embeddings = mx.split(image_features, image_features.shape[0]) - final_embeddings = [v for p in zip(text_segments, image_embeddings) for v in p] - final_embeddings += [inputs_embeds[:, start_idx:]] - - # Create a final embedding of shape - # (1, num_image_patches*num_images + sequence_len, embed_dim) - return mx.concatenate(final_embeddings, axis=1) - - def __call__(self, input_ids: mx.array, pixel_values: mx.array = None, cache=None): - input_embddings = None - if pixel_values is not None: - input_embddings = self.get_input_embeddings(input_ids, pixel_values) - logits = self.language_model( - input_ids, cache=cache, inputs_embeds=input_embddings - ) - return logits - - def sanitize(self, weights): - if self.config.vision_config: - weights = self.vision_tower.sanitize(weights) - else: - weights = { - k: v - for k, v in weights.items() - if not k.startswith( - ( - "vision_tower", - "multi_modal_projector", - "vision_feature_layer", - "vision_feature_select_strategy", - ) - ) - } - weights = self.language_model.sanitize(weights) - return weights - - @property - def layers(self): - return self.language_model.model.layers - - @property - def head_dim(self): - return ( - self.language_model.model.head_dim - or self.language_model.model.hidden_size - // self.language_model.model.num_attention_heads - ) - - @property - def n_kv_heads(self): - return self.language_model.model.num_key_value_heads diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/phi3.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/phi3.py deleted file mode 100644 index 210fd670a..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/phi3.py +++ /dev/null @@ -1,128 +0,0 @@ -from dataclasses import dataclass, field - -import mlx.core as mx -import mlx.nn as nn -from mlx_lm.models.base import create_attention_mask -from mlx_lm.models.phi3 import ModelArgs, TransformerBlock - -from ...shard import Shard -from .base import IdentityBlock - - -@dataclass -class ModelArgs(ModelArgs): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - super().__post_init__() - - if isinstance(self.shard, Shard): - return - if not isinstance(self.shard, dict): - raise TypeError( - f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead" - ) - - self.shard = Shard(**self.shard) - - -class Phi3Model(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.vocab_size = args.vocab_size - self.num_hidden_layers = args.num_hidden_layers - assert self.vocab_size > 0 - - if self.args.shard.is_first_layer(): - self.embed_tokens = nn.Embedding(args.vocab_size, args.hidden_size) - - self.layers = [] - for i in range(self.num_hidden_layers): - if self.args.shard.start_layer <= i <= self.args.shard.end_layer: - self.layers.append(TransformerBlock(args=args)) - else: - self.layers.append(IdentityBlock()) - - if self.args.shard.is_last_layer(): - self.norm = nn.RMSNorm(args.hidden_size, eps=args.rms_norm_eps) - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - if self.args.shard.is_first_layer(): - h = self.embed_tokens(inputs) - else: - h = inputs - - mask = None - if h.shape[1] > 1: - mask = create_attention_mask(h, cache) - - if cache is None: - cache = [None] * len(self.layers) - - for layer, c in zip(self.layers, cache): - h = layer(h, mask, c) - - if self.args.shard.is_last_layer(): - h = self.norm(h) - return h - - -class Model(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.model_type = args.model_type - self.model = Phi3Model(args) - if self.args.shard.is_last_layer(): - self.lm_head = nn.Linear(args.hidden_size, args.vocab_size, bias=False) - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - out = self.model(inputs, cache) - if self.args.shard.is_last_layer(): - out = self.lm_head(out) - return out - - def sanitize(self, weights): - shard_state_dict = {} - - for key, value in weights.items(): - if "self_attn.rope.inv_freq" in key: - continue - if key.startswith("model.layers."): - layer_num = int(key.split(".")[2]) - if ( - self.args.shard.start_layer - <= layer_num - <= self.args.shard.end_layer - ): - shard_state_dict[key] = value - elif ( - self.args.shard.is_first_layer() - and key.startswith("model.embed_tokens") - or self.args.shard.is_last_layer() - and (key.startswith("lm_head") or key.startswith("model.norm")) - ): - shard_state_dict[key] = value - - return shard_state_dict - - @property - def layers(self): - return self.model.layers - - @property - def head_dim(self): - return self.args.hidden_size // self.args.num_attention_heads - - @property - def n_kv_heads(self): - return self.args.num_key_value_heads diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/qwen2.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/qwen2.py deleted file mode 100644 index 8358e4741..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/qwen2.py +++ /dev/null @@ -1,144 +0,0 @@ -from dataclasses import dataclass, field - -import mlx.core as mx -import mlx.nn as nn -from mlx_lm.models.base import create_attention_mask -from mlx_lm.models.qwen2 import ModelArgs, TransformerBlock - -from ...shard import Shard -from .base import IdentityBlock - - -@dataclass -class ModelArgs(ModelArgs): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - super().__post_init__() - - if isinstance(self.shard, Shard): - return - if not isinstance(self.shard, dict): - raise TypeError( - f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead" - ) - - self.shard = Shard(**self.shard) - - -class Qwen2Model(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.vocab_size = args.vocab_size - self.num_hidden_layers = args.num_hidden_layers - assert self.vocab_size > 0 - - if self.args.shard.is_first_layer() or ( - self.args.shard.is_last_layer() and args.tie_word_embeddings - ): - self.embed_tokens = nn.Embedding(args.vocab_size, args.hidden_size) - - self.layers = [] - for i in range(self.num_hidden_layers): - if self.args.shard.start_layer <= i <= self.args.shard.end_layer: - self.layers.append(TransformerBlock(args=args)) - else: - self.layers.append(IdentityBlock()) - - if self.args.shard.is_last_layer(): - self.norm = nn.RMSNorm(args.hidden_size, eps=args.rms_norm_eps) - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - if self.args.shard.is_first_layer(): - h = self.embed_tokens(inputs) - else: - h = inputs - - mask = None - if h.shape[1] > 1: - mask = create_attention_mask(h, cache) - - if cache is None: - cache = [None] * len(self.layers) - - for layer, c in zip(self.layers, cache): - h = layer(h, mask, c) - - if self.args.shard.is_last_layer(): - h = self.norm(h) - return h - - -class Model(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.model_type = args.model_type - self.model = Qwen2Model(args) - if self.args.shard.is_last_layer(): - if not args.tie_word_embeddings: - self.lm_head = nn.Linear(args.hidden_size, args.vocab_size, bias=False) - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - out = self.model(inputs, cache) - if self.args.shard.is_last_layer(): - if self.args.tie_word_embeddings: - out = self.model.embed_tokens.as_linear(out) - else: - out = self.lm_head(out) - return out - - def sanitize(self, weights): - shard_state_dict = {} - - for key, value in weights.items(): - if "self_attn.rotary_emb.inv_freq" in key: - continue - if key.startswith("model.layers."): - layer_num = int(key.split(".")[2]) - if ( - self.args.shard.start_layer - <= layer_num - <= self.args.shard.end_layer - ): - shard_state_dict[key] = value - elif ( - self.args.shard.is_first_layer() - and key.startswith("model.embed_tokens") - or (self.args.shard.is_last_layer() and self.args.tie_word_embeddings) - and key.startswith("model.embed_tokens") - or ( - self.args.shard.is_last_layer() - and not self.args.tie_word_embeddings - ) - and key.startswith("lm_head") - or self.args.shard.is_last_layer() - and (key.startswith("model.norm")) - ): - shard_state_dict[key] = value - - if self.args.tie_word_embeddings: - shard_state_dict.pop("lm_head.weight", None) - - return shard_state_dict - - @property - def layers(self): - return self.model.layers - - @property - def head_dim(self): - return self.args.hidden_size // self.args.num_attention_heads - - @property - def n_kv_heads(self): - return self.args.num_key_value_heads diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/sd_models/clip.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/sd_models/clip.py deleted file mode 100644 index d71205eba..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/sd_models/clip.py +++ /dev/null @@ -1,206 +0,0 @@ -# Adapted from https://github.com/ml-explore/mlx-examples/blob/main/stable_diffusion/stable_diffusion/clip.py - -import math -from dataclasses import dataclass, field -from typing import List, Optional - -import mlx.core as mx -import mlx.nn as nn - -from .inference.mlx.models.base import IdentityBlock -from .inference.shard import Shard - -_ACTIVATIONS = {"quick_gelu": nn.gelu_fast_approx, "gelu": nn.gelu} - - -@dataclass -class CLIPTextModelConfig: - num_layers: int = 23 - model_dims: int = 1024 - num_heads: int = 16 - max_length: int = 77 - vocab_size: int = 49408 - projection_dim: Optional[int] = None - hidden_act: str = "quick_gelu" - - @classmethod - def from_dict(cls, config): - return ModelArgs( - num_layers=config["num_hidden_layers"], - model_dims=config["hidden_size"], - num_heads=config["num_attention_heads"], - max_length=config["max_position_embeddings"], - vocab_size=config["vocab_size"], - projection_dim=( - config["projection_dim"] - if "WithProjection" in config["architectures"][0] - else None - ), - hidden_act=config.get("hidden_act", "quick_gelu"), - weight_files=config.get("weight_files", []), - ) - - -@dataclass -class ModelArgs(CLIPTextModelConfig): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - weight_files: List[str] = field(default_factory=lambda: []) - - def __post_init__(self): - if isinstance(self.shard, dict): - self.shard = Shard(**self.shard) - - if not isinstance(self.shard, Shard): - raise TypeError( - f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead" - ) - - if not self.shard.is_first_layer(): - self.vision_config = None - - -@dataclass -class CLIPOutput: - pooled_output: Optional[mx.array] = None - last_hidden_state: Optional[mx.array] = None - hidden_states: Optional[List[mx.array]] = None - - -class CLIPEncoderLayer(nn.Module): - """The transformer encoder layer from CLIP.""" - - def __init__(self, model_dims: int, num_heads: int, activation: str): - super().__init__() - - self.layer_norm1 = nn.LayerNorm(model_dims) - self.layer_norm2 = nn.LayerNorm(model_dims) - - self.attention = nn.MultiHeadAttention(model_dims, num_heads) - self.attention.query_proj.bias = mx.zeros(model_dims) - self.attention.key_proj.bias = mx.zeros(model_dims) - self.attention.value_proj.bias = mx.zeros(model_dims) - self.attention.out_proj.bias = mx.zeros(model_dims) - - self.linear1 = nn.Linear(model_dims, 4 * model_dims) - self.linear2 = nn.Linear(4 * model_dims, model_dims) - - self.act = _ACTIVATIONS[activation] - - def __call__(self, x, attn_mask=None): - y = self.layer_norm1(x) - y = self.attention(y, y, y, attn_mask) - x = y + x - - y = self.layer_norm2(x) - y = self.linear1(y) - y = self.act(y) - y = self.linear2(y) - x = y + x - return x - - -class CLIPTextModel(nn.Module): - """Implements the text encoder transformer from CLIP.""" - - def __init__(self, config: CLIPTextModelConfig, shard: Shard): - super().__init__() - - self.shard = shard - self.layers_range = range( - self.shard.start_layer * 2, self.shard.end_layer * 2 + 2 - ) - if self.shard.is_first_layer(): - self.token_embedding = nn.Embedding(config.vocab_size, config.model_dims) - self.position_embedding = nn.Embedding(config.max_length, config.model_dims) - self.layers = [] - for i in range(math.ceil(config.num_layers / 2)): - if 2 * i in self.layers_range: - self.layers.append( - CLIPEncoderLayer( - config.model_dims, config.num_heads, config.hidden_act - ) - ) - if 2 * i + 1 in self.layers_range and 2 * i + 1 < config.num_layers: - self.layers.append( - CLIPEncoderLayer( - config.model_dims, config.num_heads, config.hidden_act - ) - ) - else: - self.layers.append(IdentityBlock()) - if self.shard.is_last_layer(): - self.final_layer_norm = nn.LayerNorm(config.model_dims) - - if config.projection_dim is not None: - self.text_projection = nn.Linear( - config.model_dims, config.projection_dim, bias=False - ) - - def _get_mask(self, N, dtype): - indices = mx.arange(N) - mask = indices[:, None] < indices[None] - mask = mask.astype(dtype) * (-6e4 if dtype == mx.float16 else -1e9) - return mask - - def __call__(self, x, mask=None): - # Extract some shapes - if self.shard.is_first_layer(): - B, N = x.shape - x.argmax(-1) - - # Compute the embeddings - x = self.token_embedding(x) - - x = x + self.position_embedding.weight[:N] - # Compute the features from the transformer - mask = self._get_mask(N, x.dtype) - - for layer in self.layers: - x = layer(x, mask) - # Apply the final layernorm and return - - if self.shard.is_last_layer(): - x = self.final_layer_norm(x) - - return x, mask - - def sanitize(self, weights): - sanitized_weights = {} - for key, value in weights.items(): - if "position_ids" in key: - continue - if key.startswith("text_model."): - key = key[11:] - if key.startswith("embeddings."): - key = key[11:] - if key.startswith("encoder."): - key = key[8:] - - # Map attention layers - if "self_attn." in key: - key = key.replace("self_attn.", "attention.") - if "q_proj." in key: - key = key.replace("q_proj.", "query_proj.") - if "k_proj." in key: - key = key.replace("k_proj.", "key_proj.") - if "v_proj." in key: - key = key.replace("v_proj.", "value_proj.") - - # Map ffn layers - if "mlp.fc1" in key: - key = key.replace("mlp.fc1", "linear1") - if "mlp.fc2" in key: - key = key.replace("mlp.fc2", "linear2") - - if key.startswith("layers."): - layer_num = int(key.split(".")[1]) - if layer_num not in self.layers_range: - continue - if not self.shard.is_first_layer() and "embedding" in key: - continue - if not self.shard.is_last_layer() and key.startswith("final_layer_norm"): - continue - if not self.shard.is_last_layer() and key.startswith("text_projection"): - continue - sanitized_weights[key] = value - return sanitized_weights diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/sd_models/tokenizer.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/sd_models/tokenizer.py deleted file mode 100644 index 93e9b3b8c..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/sd_models/tokenizer.py +++ /dev/null @@ -1,131 +0,0 @@ -# adapted from https://github.com/ml-explore/mlx-examples/blob/main/stable_diffusion/stable_diffusion/tokenizer.py - -import glob -import json - -import regex - - -class Tokenizer: - """A simple port of CLIPTokenizer from https://github.com/huggingface/transformers/ .""" - - def __init__(self, bpe_ranks, vocab): - self.bpe_ranks = bpe_ranks - self.vocab = vocab - self.pat = regex.compile( - r"""<\|startoftext\|>|<\|endoftext\|>|'s|'t|'re|'ve|'m|'ll|'d|[\p{L}]+|[\p{N}]|[^\s\p{L}\p{N}]+""", - regex.IGNORECASE, - ) - - self._cache = {self.bos: self.bos, self.eos: self.eos} - - @property - def bos(self): - return "<|startoftext|>" - - @property - def bos_token(self): - return self.vocab[self.bos] - - @property - def eos(self): - return "<|endoftext|>" - - @property - def eos_token(self): - return self.vocab[self.eos] - - def bpe(self, text): - if text in self._cache: - return self._cache[text] - - unigrams = list(text[:-1]) + [text[-1] + ""] - unique_bigrams = set(zip(unigrams, unigrams[1:])) - - if not unique_bigrams: - return unigrams - - # In every iteration try to merge the two most likely bigrams. If none - # was merged we are done. - # - # Ported from https://github.com/huggingface/transformers/blob/main/src/transformers/models/clip/tokenization_clip.py - while unique_bigrams: - bigram = min( - unique_bigrams, key=lambda pair: self.bpe_ranks.get(pair, float("inf")) - ) - if bigram not in self.bpe_ranks: - break - - new_unigrams = [] - skip = False - for a, b in zip(unigrams, unigrams[1:]): - if skip: - skip = False - continue - - if (a, b) == bigram: - new_unigrams.append(a + b) - skip = True - - else: - new_unigrams.append(a) - - if not skip: - new_unigrams.append(b) - - unigrams = new_unigrams - unique_bigrams = set(zip(unigrams, unigrams[1:])) - - self._cache[text] = unigrams - - return unigrams - - def tokenize(self, text, prepend_bos=True, append_eos=True): - if isinstance(text, list): - return [self.tokenize(t, prepend_bos, append_eos) for t in text] - - # Lower case cleanup and split according to self.pat. Hugging Face does - # a much more thorough job here but this should suffice for 95% of - # cases. - clean_text = regex.sub(r"\s+", " ", text.lower()) - tokens = regex.findall(self.pat, clean_text) - - # Split the tokens according to the byte-pair merge file - bpe_tokens = [ti for t in tokens for ti in self.bpe(t)] - - # Map to token ids and return - tokens = [self.vocab[t] for t in bpe_tokens] - if prepend_bos: - tokens = [self.bos_token] + tokens - if append_eos: - tokens.append(self.eos_token) - - return tokens - - def encode(self, prompt): - tokens = [self.tokenize(prompt)] - negative_text = "" - if negative_text is not None: - tokens += [self.tokenize(negative_text)] - lengths = [len(t) for t in tokens] - N = max(lengths) - tokens = [t + [0] * (N - len(t)) for t in tokens] - return tokens - - -def load_tokenizer( - model_path: str, - vocab_key: str = "tokenizer_vocab", - merges_key: str = "tokenizer_merges", -): - vocab_file = glob.glob(str(model_path / "tokenizer" / vocab_key))[0] - with open(vocab_file, encoding="utf-8") as f: - vocab = json.load(f) - - merges_file = glob.glob(str(model_path / "tokenizer" / merges_key))[0] - with open(merges_file, encoding="utf-8") as f: - bpe_merges = f.read().strip().split("\n")[1 : 49152 - 256 - 2 + 1] - bpe_merges = [tuple(m.split()) for m in bpe_merges] - bpe_ranks = dict(map(reversed, enumerate(bpe_merges))) - - return Tokenizer(bpe_ranks, vocab) diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/sd_models/unet.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/sd_models/unet.py deleted file mode 100644 index 0773e9d7a..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/sd_models/unet.py +++ /dev/null @@ -1,632 +0,0 @@ -# Adapted from https://github.com/ml-explore/mlx-examples/blob/main/stable_diffusion/stable_diffusion/unet.py - -import math -from dataclasses import dataclass, field -from typing import List, Optional, Tuple - -import mlx.core as mx -import mlx.nn as nn - -from .inference.shard import Shard - - -@dataclass -class UNetConfig: - in_channels: int = 4 - out_channels: int = 4 - conv_in_kernel: int = 3 - conv_out_kernel: int = 3 - block_out_channels: Tuple[int] = (320, 640, 1280, 1280) - layers_per_block: Tuple[int] = (2, 2, 2, 2) - mid_block_layers: int = 2 - transformer_layers_per_block: Tuple[int] = (1, 1, 1, 1) - num_attention_heads: Tuple[int] = (5, 10, 20, 20) - cross_attention_dim: Tuple[int] = (1024,) * 4 - norm_num_groups: int = 32 - down_block_types: Tuple[str] = ( - "CrossAttnDownBlock2D", - "CrossAttnDownBlock2D", - "CrossAttnDownBlock2D", - "DownBlock2D", - ) - up_block_types: Tuple[str] = ( - "UpBlock2D", - "CrossAttnUpBlock2D", - "CrossAttnUpBlock2D", - "CrossAttnUpBlock2D", - ) - addition_embed_type: Optional[str] = None - addition_time_embed_dim: Optional[int] = None - projection_class_embeddings_input_dim: Optional[int] = None - weight_files: List[str] = field(default_factory=lambda: []) - - @classmethod - def from_dict(cls, config): - n_blocks = len(config["block_out_channels"]) - return UNetConfig( - in_channels=config["in_channels"], - out_channels=config["out_channels"], - block_out_channels=config["block_out_channels"], - layers_per_block=[config["layers_per_block"]] * n_blocks, - transformer_layers_per_block=config.get( - "transformer_layers_per_block", (1,) * 4 - ), - num_attention_heads=( - [config["attention_head_dim"]] * n_blocks - if isinstance(config["attention_head_dim"], int) - else config["attention_head_dim"] - ), - cross_attention_dim=[config["cross_attention_dim"]] * n_blocks, - norm_num_groups=config["norm_num_groups"], - down_block_types=config["down_block_types"], - up_block_types=config["up_block_types"][::-1], - addition_embed_type=config.get("addition_embed_type", None), - addition_time_embed_dim=config.get("addition_time_embed_dim", None), - projection_class_embeddings_input_dim=config.get( - "projection_class_embeddings_input_dim", None - ), - weight_files=config.get("weight_files", []), - ) - - -def upsample_nearest(x, scale: int = 2): - B, H, W, C = x.shape - x = mx.broadcast_to(x[:, :, None, :, None, :], (B, H, scale, W, scale, C)) - x = x.reshape(B, H * scale, W * scale, C) - - return x - - -class TimestepEmbedding(nn.Module): - def __init__(self, in_channels: int, time_embed_dim: int): - super().__init__() - - self.linear_1 = nn.Linear(in_channels, time_embed_dim) - self.linear_2 = nn.Linear(time_embed_dim, time_embed_dim) - - def __call__(self, x): - x = self.linear_1(x) - x = nn.silu(x) - x = self.linear_2(x) - - return x - - -class TransformerBlock(nn.Module): - def __init__( - self, - model_dims: int, - num_heads: int, - hidden_dims: Optional[int] = None, - memory_dims: Optional[int] = None, - ): - super().__init__() - - self.norm1 = nn.LayerNorm(model_dims) - self.attn1 = nn.MultiHeadAttention(model_dims, num_heads) - self.attn1.out_proj.bias = mx.zeros(model_dims) - - memory_dims = memory_dims or model_dims - self.norm2 = nn.LayerNorm(model_dims) - self.attn2 = nn.MultiHeadAttention( - model_dims, num_heads, key_input_dims=memory_dims - ) - self.attn2.out_proj.bias = mx.zeros(model_dims) - - hidden_dims = hidden_dims or 4 * model_dims - self.norm3 = nn.LayerNorm(model_dims) - self.linear1 = nn.Linear(model_dims, hidden_dims) - self.linear2 = nn.Linear(model_dims, hidden_dims) - self.linear3 = nn.Linear(hidden_dims, model_dims) - - def __call__(self, x, memory, attn_mask, memory_mask): - # Self attention - y = self.norm1(x) - y = self.attn1(y, y, y, attn_mask) - x = x + y - - # Cross attention - y = self.norm2(x) - y = self.attn2(y, memory, memory, memory_mask) - x = x + y - - # FFN - y = self.norm3(x) - y_a = self.linear1(y) - y_b = self.linear2(y) - y = y_a * nn.gelu(y_b) - y = self.linear3(y) - x = x + y - - return x - - -class Transformer2D(nn.Module): - """A transformer model for inputs with 2 spatial dimensions.""" - - def __init__( - self, - in_channels: int, - model_dims: int, - encoder_dims: int, - num_heads: int, - num_layers: int = 1, - norm_num_groups: int = 32, - ): - super().__init__() - - self.norm = nn.GroupNorm(norm_num_groups, in_channels, pytorch_compatible=True) - self.proj_in = nn.Linear(in_channels, model_dims) - self.transformer_blocks = [ - TransformerBlock(model_dims, num_heads, memory_dims=encoder_dims) - for i in range(num_layers) - ] - self.proj_out = nn.Linear(model_dims, in_channels) - - def __call__(self, x, encoder_x, attn_mask, encoder_attn_mask): - # Save the input to add to the output - input_x = x - dtype = x.dtype - - # Perform the input norm and projection - B, H, W, C = x.shape - x = self.norm(x.astype(mx.float32)).astype(dtype).reshape(B, -1, C) - x = self.proj_in(x) - - # Apply the transformer - for block in self.transformer_blocks: - x = block(x, encoder_x, attn_mask, encoder_attn_mask) - - # Apply the output projection and reshape - x = self.proj_out(x) - x = x.reshape(B, H, W, C) - - return x + input_x - - -class ResnetBlock2D(nn.Module): - def __init__( - self, - in_channels: int, - out_channels: Optional[int] = None, - groups: int = 32, - temb_channels: Optional[int] = None, - ): - super().__init__() - - out_channels = out_channels or in_channels - - self.norm1 = nn.GroupNorm(groups, in_channels, pytorch_compatible=True) - self.conv1 = nn.Conv2d( - in_channels, out_channels, kernel_size=3, stride=1, padding=1 - ) - if temb_channels is not None: - self.time_emb_proj = nn.Linear(temb_channels, out_channels) - self.norm2 = nn.GroupNorm(groups, out_channels, pytorch_compatible=True) - self.conv2 = nn.Conv2d( - out_channels, out_channels, kernel_size=3, stride=1, padding=1 - ) - - if in_channels != out_channels: - self.conv_shortcut = nn.Linear(in_channels, out_channels) - - def __call__(self, x, temb=None): - dtype = x.dtype - - if temb is not None: - temb = self.time_emb_proj(nn.silu(temb)) - y = self.norm1(x.astype(mx.float32)).astype(dtype) - - y = nn.silu(y) - - y = self.conv1(y) - - if temb is not None: - y = y + temb[:, None, None, :] - y = self.norm2(y.astype(mx.float32)).astype(dtype) - y = nn.silu(y) - y = self.conv2(y) - - x = y + (x if "conv_shortcut" not in self else self.conv_shortcut(x)) - return x - - -class UNetBlock2D(nn.Module): - def __init__( - self, - in_channels: int, - out_channels: int, - temb_channels: int, - prev_out_channels: Optional[int] = None, - num_layers: int = 1, - transformer_layers_per_block: int = 1, - num_attention_heads: int = 8, - cross_attention_dim=1280, - resnet_groups: int = 32, - add_downsample=True, - add_upsample=True, - add_cross_attention=True, - ): - super().__init__() - - # Prepare the in channels list for the resnets - if prev_out_channels is None: - in_channels_list = [in_channels] + [out_channels] * (num_layers - 1) - else: - in_channels_list = [prev_out_channels] + [out_channels] * (num_layers - 1) - res_channels_list = [out_channels] * (num_layers - 1) + [in_channels] - in_channels_list = [ - a + b for a, b in zip(in_channels_list, res_channels_list) - ] - - # Add resnet blocks that also process the time embedding - self.resnets = [ - ResnetBlock2D( - in_channels=ic, - out_channels=out_channels, - temb_channels=temb_channels, - groups=resnet_groups, - ) - for ic in in_channels_list - ] - - # Add optional cross attention layers - if add_cross_attention: - self.attentions = [ - Transformer2D( - in_channels=out_channels, - model_dims=out_channels, - num_heads=num_attention_heads, - num_layers=transformer_layers_per_block, - encoder_dims=cross_attention_dim, - ) - for i in range(num_layers) - ] - - # Add an optional downsampling layer - if add_downsample: - self.downsample = nn.Conv2d( - out_channels, out_channels, kernel_size=3, stride=2, padding=1 - ) - - # or upsampling layer - if add_upsample: - self.upsample = nn.Conv2d( - out_channels, out_channels, kernel_size=3, stride=1, padding=1 - ) - - def __call__( - self, - x, - encoder_x=None, - temb=None, - attn_mask=None, - encoder_attn_mask=None, - residual_hidden_states=None, - ): - output_states = [] - - for i in range(len(self.resnets)): - if residual_hidden_states is not None: - x = mx.concatenate([x, residual_hidden_states.pop()], axis=-1) - - x = self.resnets[i](x, temb) - - if "attentions" in self: - x = self.attentions[i](x, encoder_x, attn_mask, encoder_attn_mask) - - output_states.append(x) - - if "downsample" in self: - x = self.downsample(x) - output_states.append(x) - - if "upsample" in self: - x = self.upsample(upsample_nearest(x)) - output_states.append(x) - - return x, output_states - - -class UNetModel(nn.Module): - """The conditional 2D UNet model that actually performs the denoising.""" - - def __init__(self, config: UNetConfig, shard: Shard): - super().__init__() - self.shard = shard - self.start_layer = shard.start_layer - self.end_layer = shard.end_layer - self.layers_range = list(range(self.start_layer, self.end_layer + 1)) - if shard.is_first_layer(): - self.conv_in = nn.Conv2d( - config.in_channels, - config.block_out_channels[0], - config.conv_in_kernel, - padding=(config.conv_in_kernel - 1) // 2, - ) - - self.timesteps = nn.SinusoidalPositionalEncoding( - config.block_out_channels[0], - max_freq=1, - min_freq=math.exp( - -math.log(10000) + 2 * math.log(10000) / config.block_out_channels[0] - ), - scale=1.0, - cos_first=True, - full_turns=False, - ) - self.time_embedding = TimestepEmbedding( - config.block_out_channels[0], - config.block_out_channels[0] * 4, - ) - - if config.addition_embed_type == "text_time": - self.add_time_proj = nn.SinusoidalPositionalEncoding( - config.addition_time_embed_dim, - max_freq=1, - min_freq=math.exp( - -math.log(10000) - + 2 * math.log(10000) / config.addition_time_embed_dim - ), - scale=1.0, - cos_first=True, - full_turns=False, - ) - self.add_embedding = TimestepEmbedding( - config.projection_class_embeddings_input_dim, - config.block_out_channels[0] * 4, - ) - - # Make the downsampling blocks - block_channels = [config.block_out_channels[0]] + list( - config.block_out_channels - ) - self.down_blocks = [] - - for i, (in_channels, out_channels) in enumerate( - zip(block_channels, block_channels[1:]) - ): - if i in self.layers_range: - self.down_blocks.append( - UNetBlock2D( - in_channels=in_channels, - out_channels=out_channels, - temb_channels=config.block_out_channels[0] * 4, - num_layers=config.layers_per_block[i], - transformer_layers_per_block=config.transformer_layers_per_block[ - i - ], - num_attention_heads=config.num_attention_heads[i], - cross_attention_dim=config.cross_attention_dim[i], - resnet_groups=config.norm_num_groups, - add_downsample=(i < len(config.block_out_channels) - 1), - add_upsample=False, - add_cross_attention="CrossAttn" in config.down_block_types[i], - ) - ) - else: - self.down_blocks.append(nn.Identity()) - - # Make the middle block - if 4 in self.layers_range: - self.mid_blocks = [ - ResnetBlock2D( - in_channels=config.block_out_channels[-1], - out_channels=config.block_out_channels[-1], - temb_channels=config.block_out_channels[0] * 4, - groups=config.norm_num_groups, - ), - Transformer2D( - in_channels=config.block_out_channels[-1], - model_dims=config.block_out_channels[-1], - num_heads=config.num_attention_heads[-1], - num_layers=config.transformer_layers_per_block[-1], - encoder_dims=config.cross_attention_dim[-1], - ), - ResnetBlock2D( - in_channels=config.block_out_channels[-1], - out_channels=config.block_out_channels[-1], - temb_channels=config.block_out_channels[0] * 4, - groups=config.norm_num_groups, - ), - ] - - # Make the upsampling blocks - block_channels = ( - [config.block_out_channels[0]] - + list(config.block_out_channels) - + [config.block_out_channels[-1]] - ) - - total_items = len(block_channels) - 3 - reversed_channels = list( - reversed(list(zip(block_channels, block_channels[1:], block_channels[2:]))) - ) - - self.up_blocks = [] - for rev_i, (in_channels, out_channels, prev_out_channels) in enumerate( - reversed_channels - ): - i = total_items - rev_i - if rev_i + 5 in self.layers_range: - self.up_blocks.append( - UNetBlock2D( - in_channels=in_channels, - out_channels=out_channels, - temb_channels=config.block_out_channels[0] * 4, - prev_out_channels=prev_out_channels, - num_layers=config.layers_per_block[i] + 1, - transformer_layers_per_block=config.transformer_layers_per_block[ - i - ], - num_attention_heads=config.num_attention_heads[i], - cross_attention_dim=config.cross_attention_dim[i], - resnet_groups=config.norm_num_groups, - add_downsample=False, - add_upsample=(i > 0), - add_cross_attention="CrossAttn" in config.up_block_types[i], - ) - ) - else: - self.up_blocks.append(nn.Identity()) - - if shard.is_last_layer(): - self.conv_norm_out = nn.GroupNorm( - config.norm_num_groups, - config.block_out_channels[0], - pytorch_compatible=True, - ) - self.conv_out = nn.Conv2d( - config.block_out_channels[0], - config.out_channels, - config.conv_out_kernel, - padding=(config.conv_out_kernel - 1) // 2, - ) - - def __call__( - self, - x, - timestep, - encoder_x, - attn_mask=None, - encoder_attn_mask=None, - text_time=None, - residuals=None, - ): - # Compute the time embeddings - - temb = self.timesteps(timestep).astype(x.dtype) - temb = self.time_embedding(temb) - - # Add the extra text_time conditioning - if text_time is not None: - text_emb, time_ids = text_time - emb = self.add_time_proj(time_ids).flatten(1).astype(x.dtype) - emb = mx.concatenate([text_emb, emb], axis=-1) - emb = self.add_embedding(emb) - temb = temb + emb - - if self.shard.is_first_layer(): - # Preprocess the input - x = self.conv_in(x) - residuals = [x] - # Run the downsampling part of the unet - - for i in range(len(self.down_blocks)): - if i in self.layers_range: - x, res = self.down_blocks[i]( - x, - encoder_x=encoder_x, - temb=temb, - attn_mask=attn_mask, - encoder_attn_mask=encoder_attn_mask, - ) - residuals.extend(res) - else: - x = self.down_blocks[i](x) - - if 4 in self.layers_range: - # Run the middle part of the unet - x = self.mid_blocks[0](x, temb) - x = self.mid_blocks[1](x, encoder_x, attn_mask, encoder_attn_mask) - x = self.mid_blocks[2](x, temb) - - # Run the upsampling part of the unet - for i in range(len(self.up_blocks)): - if i + 5 in self.layers_range: - x, _ = self.up_blocks[i]( - x, - encoder_x=encoder_x, - temb=temb, - attn_mask=attn_mask, - encoder_attn_mask=encoder_attn_mask, - residual_hidden_states=residuals, - ) - else: - x = self.up_blocks[i](x) - - # Postprocess the output - if self.shard.is_last_layer(): - dtype = x.dtype - x = self.conv_norm_out(x.astype(mx.float32)).astype(dtype) - x = nn.silu(x) - x = self.conv_out(x) - - return x, residuals - - def sanitize(self, weights): - sanitized_weights = {} - for key, value in weights.items(): - k1 = "" - k2 = "" - if "downsamplers" in key: - key = key.replace("downsamplers.0.conv", "downsample") - if "upsamplers" in key: - key = key.replace("upsamplers.0.conv", "upsample") - - # Map the mid block - if "mid_block.resnets.0" in key: - key = key.replace("mid_block.resnets.0", "mid_blocks.0") - if "mid_block.attentions.0" in key: - key = key.replace("mid_block.attentions.0", "mid_blocks.1") - if "mid_block.resnets.1" in key: - key = key.replace("mid_block.resnets.1", "mid_blocks.2") - - # Map attention layers - if "to_k" in key: - key = key.replace("to_k", "key_proj") - if "to_out.0" in key: - key = key.replace("to_out.0", "out_proj") - if "to_q" in key: - key = key.replace("to_q", "query_proj") - if "to_v" in key: - key = key.replace("to_v", "value_proj") - - # Map transformer ffn - if "ff.net.2" in key: - key = key.replace("ff.net.2", "linear3") - if "ff.net.0" in key: - k1 = key.replace("ff.net.0.proj", "linear1") - k2 = key.replace("ff.net.0.proj", "linear2") - v1, v2 = mx.split(value, 2) - - if "conv_shortcut.weight" in key: - value = value.squeeze() - - # Transform the weights from 1x1 convs to linear - if len(value.shape) == 4 and ("proj_in" in key or "proj_out" in key): - value = value.squeeze() - - if len(value.shape) == 4: - value = value.transpose(0, 2, 3, 1) - value = value.reshape(-1).reshape(value.shape) - - if key.startswith("conv_in"): - if 0 not in self.layers_range: - continue - - if key.startswith("down_blocks"): - layer_num = int(key.split(".")[1]) - if layer_num not in self.layers_range: - continue - - if key.startswith("mid_block"): - if 4 not in self.layers_range: - continue - - if key.startswith("up_blocks"): - layer_num = int(key.split(".")[1]) - if (layer_num + 5) not in self.layers_range: - continue - - if key.startswith("conv_out") or key.startswith("conv_norm_out"): - if 8 not in self.layers_range: - continue - - if len(k1) > 0: - sanitized_weights[k1] = v1 - sanitized_weights[k2] = v2 - else: - sanitized_weights[key] = value - - return sanitized_weights diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/sd_models/vae.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/sd_models/vae.py deleted file mode 100644 index 5cc12ad3d..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/models/sd_models/vae.py +++ /dev/null @@ -1,438 +0,0 @@ -# Adapted from https://github.com/ml-explore/mlx-examples/blob/main/stable_diffusion/stable_diffusion/vae.py - -import inspect -import math -from dataclasses import dataclass, field -from typing import List, Tuple - -import mlx.core as mx -import mlx.nn as nn - -from ..base import IdentityBlock -from .inference.shard import Shard -from .unet import ResnetBlock2D, upsample_nearest - - -@dataclass -class AutoencoderConfig: - in_channels: int = 3 - out_channels: int = 3 - latent_channels_out: int = 8 - latent_channels_in: int = 4 - block_out_channels: Tuple[int] = (128, 256, 512, 512) - layers_per_block: int = 2 - norm_num_groups: int = 32 - scaling_factor: float = 0.18215 - weight_files: List[str] = field(default_factory=lambda: []) - - @classmethod - def from_dict(cls, params): - return cls( - **{ - k: v - for k, v in params.items() - if k in inspect.signature(cls).parameters - } - ) - - -@dataclass -class ModelArgs(AutoencoderConfig): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - if isinstance(self.shard, dict): - self.shard = Shard(**self.shard) - - if not isinstance(self.shard, Shard): - raise TypeError( - f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead" - ) - - if not self.shard.is_first_layer(): - self.vision_config = None - - -class Attention(nn.Module): - """A single head unmasked attention for use with the VAE.""" - - def __init__(self, dims: int, norm_groups: int = 32): - super().__init__() - - self.group_norm = nn.GroupNorm(norm_groups, dims, pytorch_compatible=True) - self.query_proj = nn.Linear(dims, dims) - self.key_proj = nn.Linear(dims, dims) - self.value_proj = nn.Linear(dims, dims) - self.out_proj = nn.Linear(dims, dims) - - def __call__(self, x): - B, H, W, C = x.shape - - y = self.group_norm(x) - - queries = self.query_proj(y).reshape(B, H * W, C) - keys = self.key_proj(y).reshape(B, H * W, C) - values = self.value_proj(y).reshape(B, H * W, C) - - scale = 1 / math.sqrt(queries.shape[-1]) - scores = (queries * scale) @ keys.transpose(0, 2, 1) - attn = mx.softmax(scores, axis=-1) - y = (attn @ values).reshape(B, H, W, C) - - y = self.out_proj(y) - x = x + y - - return x - - -class EncoderDecoderBlock2D(nn.Module): - def __init__( - self, - in_channels: int, - out_channels: int, - num_layers: int = 1, - resnet_groups: int = 32, - add_downsample=True, - add_upsample=True, - ): - super().__init__() - - # Add the resnet blocks - self.resnets = [ - ResnetBlock2D( - in_channels=in_channels if i == 0 else out_channels, - out_channels=out_channels, - groups=resnet_groups, - ) - for i in range(num_layers) - ] - - # Add an optional downsampling layer - if add_downsample: - self.downsample = nn.Conv2d( - out_channels, out_channels, kernel_size=3, stride=2, padding=0 - ) - - # or upsampling layer - if add_upsample: - self.upsample = nn.Conv2d( - out_channels, out_channels, kernel_size=3, stride=1, padding=1 - ) - - def __call__(self, x): - for resnet in self.resnets: - x = resnet(x) - if "downsample" in self: - x = mx.pad(x, [(0, 0), (0, 1), (0, 1), (0, 0)]) - x = self.downsample(x) - - if "upsample" in self: - x = self.upsample(upsample_nearest(x)) - return x - - -class Encoder(nn.Module): - """Implements the encoder side of the Autoencoder.""" - - def __init__( - self, - in_channels: int, - latent_channels_out: int, - block_out_channels: List[int] = [64], - layers_per_block: int = 2, - resnet_groups: int = 32, - layers_range: List[int] = [], - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)), - ): - super().__init__() - self.layers_range = layers_range - self.shard = shard - if self.shard.is_first_layer(): - self.conv_in = nn.Conv2d( - in_channels, block_out_channels[0], kernel_size=3, stride=1, padding=1 - ) - - channels = [block_out_channels[0]] + list(block_out_channels) - self.down_blocks = [] - current_layer = 1 - for i, (in_channels, out_channels) in enumerate(zip(channels, channels[1:])): - if current_layer in self.layers_range: - self.down_blocks.append( - EncoderDecoderBlock2D( - in_channels, - out_channels, - num_layers=layers_per_block, - resnet_groups=resnet_groups, - add_downsample=i < len(block_out_channels) - 1, - add_upsample=False, - ) - ) - else: - self.down_blocks.append(IdentityBlock()) - current_layer += 1 - - if self.shard.is_last_layer(): - self.mid_blocks = [ - ResnetBlock2D( - in_channels=block_out_channels[-1], - out_channels=block_out_channels[-1], - groups=resnet_groups, - ), - Attention(block_out_channels[-1], resnet_groups), - ResnetBlock2D( - in_channels=block_out_channels[-1], - out_channels=block_out_channels[-1], - groups=resnet_groups, - ), - ] - - self.conv_norm_out = nn.GroupNorm( - resnet_groups, block_out_channels[-1], pytorch_compatible=True - ) - self.conv_out = nn.Conv2d( - block_out_channels[-1], latent_channels_out, 3, padding=1 - ) - - def __call__(self, x): - if self.shard.is_first_layer(): - x = self.conv_in(x) - - for layer in self.down_blocks: - x = layer(x) - - if self.shard.is_last_layer(): - x = self.mid_blocks[0](x) - x = self.mid_blocks[1](x) - x = self.mid_blocks[2](x) - - x = self.conv_norm_out(x) - x = nn.silu(x) - x = self.conv_out(x) - - return x - - -class Decoder(nn.Module): - """Implements the decoder side of the Autoencoder.""" - - def __init__( - self, - in_channels: int, - out_channels: int, - shard: Shard, - layer_range: List[int], - block_out_channels: List[int] = [64], - layers_per_block: int = 2, - resnet_groups: int = 32, - ): - super().__init__() - self.out_channels = out_channels - self.layers_range = layer_range - if 0 in layer_range: - self.conv_in = nn.Conv2d( - in_channels, block_out_channels[-1], kernel_size=3, stride=1, padding=1 - ) - - if 0 in layer_range: - self.mid_blocks = [ - ResnetBlock2D( - in_channels=block_out_channels[-1], - out_channels=block_out_channels[-1], - groups=resnet_groups, - ), - Attention(block_out_channels[-1], resnet_groups), - ResnetBlock2D( - in_channels=block_out_channels[-1], - out_channels=block_out_channels[-1], - groups=resnet_groups, - ), - ] - - channels = list(reversed(block_out_channels)) - channels = [channels[0]] + channels - - self.up_blocks = [] - current_layer = 1 - - for i, (in_channels, out_channels) in enumerate(zip(channels, channels[1:])): - if current_layer in layer_range: - self.up_blocks.append( - EncoderDecoderBlock2D( - in_channels, - out_channels, - num_layers=layers_per_block, - resnet_groups=resnet_groups, - add_downsample=False, - add_upsample=i < len(block_out_channels) - 1, - ) - ) - else: - self.up_blocks.append(IdentityBlock()) - current_layer += 1 - if 4 in layer_range: - self.conv_norm_out = nn.GroupNorm( - resnet_groups, block_out_channels[0], pytorch_compatible=True - ) - self.conv_out = nn.Conv2d( - block_out_channels[0], self.out_channels, 3, padding=1 - ) - - def __call__(self, x): - if 0 in self.layers_range: - x = self.conv_in(x) - x = self.mid_blocks[0](x) - x = self.mid_blocks[1](x) - x = self.mid_blocks[2](x) - - for layer in self.up_blocks: - x = layer(x) - if 4 in self.layers_range: - x = self.conv_norm_out(x) - x = nn.silu(x) - x = self.conv_out(x) - return x - - -class Autoencoder(nn.Module): - """The autoencoder that allows us to perform diffusion in the latent space.""" - - def __init__(self, config: AutoencoderConfig, shard: Shard, model_shard: str): - super().__init__() - self.shard = shard - self.start_layer = shard.start_layer - self.end_layer = shard.end_layer - self.layers_range = list(range(self.start_layer, self.end_layer + 1)) - self.latent_channels = config.latent_channels_in - self.scaling_factor = config.scaling_factor - self.model_shard = model_shard - if self.model_shard == "vae_encoder": - self.encoder = Encoder( - config.in_channels, - config.latent_channels_out, - config.block_out_channels, - config.layers_per_block, - resnet_groups=config.norm_num_groups, - layers_range=self.layers_range, - shard=shard, - ) - if self.shard.is_last_layer(): - self.quant_proj = nn.Linear( - config.latent_channels_out, config.latent_channels_out - ) - if self.model_shard == "vae_decoder": - self.decoder = Decoder( - config.latent_channels_in, - config.out_channels, - shard, - self.layers_range, - config.block_out_channels, - config.layers_per_block + 1, - resnet_groups=config.norm_num_groups, - ) - if self.shard.is_first_layer(): - self.post_quant_proj = nn.Linear( - config.latent_channels_in, config.latent_channels_in - ) - - def decode(self, z): - if self.shard.is_first_layer(): - z = z / self.scaling_factor - z = self.post_quant_proj(z) - return self.decoder(z) - - def encode(self, x): - x = self.encoder(x) - if self.shard.is_last_layer(): - x = self.quant_proj(x) - mean, logvar = x.split(2, axis=-1) - mean = mean * self.scaling_factor - logvar = logvar + 2 * math.log(self.scaling_factor) - x = mean - return x - - def __call__(self, x, key=None): - mean, logvar = self.encode(x) - z = mx.random.normal(mean.shape, key=key) * mx.exp(0.5 * logvar) + mean - x_hat = self.decode(z) - - return dict(x_hat=x_hat, z=z, mean=mean, logvar=logvar) - - def sanitize(self, weights): - shard = self.shard - layers = self.layers_range - sanitized_weights = {} - for key, value in weights.items(): - if "downsamplers" in key: - key = key.replace("downsamplers.0.conv", "downsample") - if "upsamplers" in key: - key = key.replace("upsamplers.0.conv", "upsample") - - # Map attention layers - if "key" in key: - key = key.replace("key", "key_proj") - if "proj_attn" in key: - key = key.replace("proj_attn", "out_proj") - if "query" in key: - key = key.replace("query", "query_proj") - if "value" in key: - key = key.replace("value", "value_proj") - - # Map the mid block - if "mid_block.resnets.0" in key: - key = key.replace("mid_block.resnets.0", "mid_blocks.0") - if "mid_block.attentions.0" in key: - key = key.replace("mid_block.attentions.0", "mid_blocks.1") - if "mid_block.resnets.1" in key: - key = key.replace("mid_block.resnets.1", "mid_blocks.2") - - # Map the quant/post_quant layers - if "quant_conv" in key: - key = key.replace("quant_conv", "quant_proj") - value = value.squeeze() - - # Map the conv_shortcut to linear - if "conv_shortcut.weight" in key: - value = value.squeeze() - - if len(value.shape) == 4: - value = value.transpose(0, 2, 3, 1) - value = value.reshape(-1).reshape(value.shape) - - if "post_quant_conv" in key: - key = key.replace("quant_conv", "quant_proj") - value = value.squeeze() - - if "decoder" in key and self.model_shard == "vae_decoder": - if key.startswith("decoder.mid_blocks."): - if 0 in layers: - sanitized_weights[key] = value - if "conv_in" in key and 0 in layers: - sanitized_weights[key] = value - if key.startswith("decoder.up_blocks."): - layer_num = int(key.split(".")[2]) + 1 - if layer_num in layers: - sanitized_weights[key] = value - if key.startswith("decoder.conv_norm_out") and 4 in layers: - sanitized_weights[key] = value - if key.startswith("decoder.conv_out") and 4 in layers: - sanitized_weights[key] = value - if self.model_shard == "vae_decoder": - if key.startswith("post_quant_proj") and 0 in layers: - sanitized_weights[key] = value - if self.model_shard == "vae_encoder": - if key.startswith("encoder."): - if "conv_in" in key and shard.is_first_layer(): - sanitized_weights[key] = value - if key.startswith("encoder.down_blocks."): - layer_num = int(key.split(".")[2]) + 1 - if layer_num in layers: - sanitized_weights[key] = value - if key.startswith("encoder.mid_blocks.") and shard.is_last_layer(): - sanitized_weights[key] = value - if "conv_norm_out" in key and shard.is_last_layer(): - sanitized_weights[key] = value - if "conv_out" in key and shard.is_last_layer(): - sanitized_weights[key] = value - if key.startswith("quant_proj") and shard.is_last_layer(): - sanitized_weights[key] = value - return sanitized_weights diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/perf_improvements.md b/pkg/hanzo-network/src/hanzo_network/inference/mlx/perf_improvements.md deleted file mode 100644 index aa9869c82..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/perf_improvements.md +++ /dev/null @@ -1,7 +0,0 @@ -# Perf improvements - -Target: 460 tok/sec -- removing sample goes from 369 -> 402 -- performance degrades as we generate more tokens -- make mlx inference engien synchronous, removing thread pool executor: 402 -> 413 -- remove self.on_opaque_status.trigger_all: 413 -> 418 diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/sharded_inference_engine.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/sharded_inference_engine.py deleted file mode 100644 index 4a884235f..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/sharded_inference_engine.py +++ /dev/null @@ -1,250 +0,0 @@ -import asyncio -from collections import OrderedDict -from concurrent.futures import ThreadPoolExecutor -from typing import Optional - -import mlx.core as mx -import mlx.nn as nn -import mlx.optimizers as optim -import numpy as np -from mlx_lm.models.cache import make_prompt_cache -from mlx_lm.sample_utils import make_sampler - -from ..inference_engine import InferenceEngine -from ..shard import Shard -from .download.shard_download import ShardDownloader -from .losses import loss_fns -from .sharded_utils import load_model_shard, resolve_tokenizer - - -class MLXDynamicShardInferenceEngine(InferenceEngine): - def __init__(self, shard_downloader: ShardDownloader): - self.shard = None - self.shard_downloader = shard_downloader - self.caches = OrderedDict() - self.sampler_params: tuple[float, float] = (0.0, 0.0, 0.0, 1) - self.sampler = make_sampler(*self.sampler_params) - self._mlx_thread = ThreadPoolExecutor(max_workers=1, thread_name_prefix="mlx") - self._tokenizer_thread = ThreadPoolExecutor( - max_workers=1, thread_name_prefix="tokenizer" - ) - self.session = {} - self._shard_lock = asyncio.Lock() - - async def _eval_mlx(self, *args): - await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, mx.eval, *args - ) - - async def poll_state(self, request_id: str, max_caches=2): - if request_id in self.caches: - self.caches.move_to_end(request_id) - else: - newcache = make_prompt_cache(self.model) - if len(self.caches) > max_caches: - self.caches.popitem(last=False) - self.caches[request_id] = newcache - return {"cache": self.caches[request_id]} - - async def sample( - self, x: np.ndarray, temp: float = 0.0, top_p: float = 1.0 - ) -> np.ndarray: - if (temp, top_p, 0.0, 1) != self.sampler_params: - self.sampler_params = (temp, top_p, 0.0, 1) - self.sampler = make_sampler(*self.sampler_params) - logits = mx.array(x) - logits = logits[:, -1, :] - logprobs = logits - mx.logsumexp(logits, keepdims=True) - result = self.sampler(logprobs) - await self._eval_mlx(result) - return np.asarray(result, dtype=int) - - async def encode(self, shard: Shard, prompt: str) -> np.ndarray: - await self.ensure_shard(shard) - return np.asarray( - await asyncio.get_running_loop().run_in_executor( - self._tokenizer_thread, self.tokenizer.encode, prompt - ) - ) - - async def decode(self, shard: Shard, tokens) -> str: - await self.ensure_shard(shard) - return await asyncio.get_running_loop().run_in_executor( - self._tokenizer_thread, self.tokenizer.decode, tokens - ) - - async def save_checkpoint(self, shard: Shard, path: str): - await self.ensure_shard(shard) - await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, lambda: self.model.save_weights(path) - ) - - async def load_checkpoint(self, shard: Shard, path: str): - await self.ensure_shard(shard) - await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, lambda: self.model.load_weights(path) - ) - - async def infer_tensor( - self, - request_id: str, - shard: Shard, - input_data: np.ndarray, - inference_state: Optional[dict] = None, - ) -> tuple[np.ndarray, Optional[dict]]: - await self.ensure_shard(shard) - state = ( - await self.poll_state(request_id) - if self.model.model_type != "StableDiffusionPipeline" - else {} - ) - x = mx.array(input_data) - - if self.model.model_type != "StableDiffusionPipeline": - output_data = await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, - lambda: self.model(x, **state, **(inference_state or {})), - ) - inference_state = None - else: - result = await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, - lambda: self.model(x, **state, **(inference_state or {})), - ) - output_data, inference_state = result - - await self._eval_mlx(output_data) - output_data = await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, lambda: np.array(output_data, copy=False) - ) - return output_data, inference_state - - async def evaluate( - self, - request_id: str, - shard: Shard, - inputs, - targets, - lengths, - loss: str = "length_masked_ce", - ): - await self.ensure_shard(shard) - await self.save_session("loss", loss_fns[loss]) - x = mx.array(inputs) - y = mx.array(targets) - layer = mx.array(lengths) - - score = await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, lambda: self.session["loss"](self.model, x, y, layer) - ) - return score - - async def ensure_train( - self, - shard: Shard, - loss: str, - opt=optim.SGD, - lr=1e-5, - trainable_layers=["input_layernorm", "gate_proj"], - ): - await self.ensure_shard(shard) - - if ( - "train_layers" not in self.session - or self.session["train_layers"] != trainable_layers - ): - await self.save_session("train_layers", trainable_layers) - - def freeze_unfreeze(): - self.model.freeze() - self.model.apply_to_modules( - lambda k, v: ( - v.unfreeze() - if any( - k.endswith(layer_name) for layer_name in trainable_layers - ) - else None - ) - ) - - await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, freeze_unfreeze - ) - - if ( - "lossname" not in self.session - or "LVaG" not in self.session - or self.session["lossname"] != loss - ): - await self.save_session("lossname", loss) - await self.save_session( - "LVaG", nn.value_and_grad(self.model, loss_fns[loss]) - ) - - if "opt" not in self.session: - await self.save_session("opt", opt(lr)) - return True - - async def train( - self, - request_id: str, - shard: Shard, - inputs, - targets, - lengths, - loss: str = "length_masked_ce", - opt=optim.SGD, - lr=1e-5, - ): - await self.ensure_train(shard, loss, opt, lr) - - def train_step(inp, tar, lng): - lval, grad = self.session["LVaG"](self.model, inp, tar, lng) - gradlayers = grad["model"]["layers"] - self.session["opt"].update(self.model, grad) - return ( - lval, - gradlayers, - (self.model.parameters(), self.session["opt"].state, lval), - ) - - x = mx.array(inputs) - y = mx.array(targets) - layer = mx.array(lengths) - score, gradients, eval_args = await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, lambda: train_step(x, y, layer) - ) - await self._eval_mlx(*eval_args) - - layers = [ - {k: v["weight"] for k, v in layer.items() if "weight" in v} - for layer in gradients - if layer - ] - first_layer = np.array(layers[0]["input_layernorm"], copy=False) - await self._eval_mlx(first_layer) - return score, first_layer - - async def ensure_shard(self, shard: Shard): - async with self._shard_lock: - if self.shard == shard: - return - model_path = await self.shard_downloader.ensure_shard( - shard, self.__class__.__name__ - ) - if self.shard != shard: - model_shard = await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, - lambda: load_model_shard(model_path, shard, lazy=False), - ) - if hasattr(model_shard, "tokenizer"): - self.tokenizer = model_shard.tokenizer - else: - self.tokenizer = await resolve_tokenizer(model_path) - self.shard = shard - self.model = model_shard - self.caches = OrderedDict() - self.session = {} - - async def cleanup(self): - self._mlx_thread.shutdown(wait=True) diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/sharded_utils.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/sharded_utils.py deleted file mode 100644 index 9dea96827..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/sharded_utils.py +++ /dev/null @@ -1,270 +0,0 @@ -# Adapted from https://github.com/ml-explore/mlx-examples/blob/main/llms/mlx_lm/utils.py - -import base64 -import glob -import importlib -import json -import logging -import traceback -from io import BytesIO -from pathlib import Path -from typing import Optional, Tuple - -import aiohttp -import mlx.core as mx -import mlx.nn as nn -from mlx_lm.tokenizer_utils import TokenizerWrapper -from net import DEBUG -from PIL import Image -from transformers import AutoProcessor - -from ..shard import Shard -from .inference.tokenizers import resolve_tokenizer - - -class ModelNotFoundError(Exception): - def __init__(self, message): - self.message = message - super().__init__(self.message) - - -MODEL_REMAPPING = { - "mistral": "llama", # mistral is compatible with llama - "phi-msft": "phixtral", -} - - -def _get_classes(config: dict): - """ - Retrieve the model and model args classes based on the configuration. - - Args: - config (dict): The model configuration. - - Returns: - A tuple containing the Model class and the ModelArgs class. - """ - model_type = config["model_type"] - model_type = MODEL_REMAPPING.get(model_type, model_type) - try: - arch = importlib.import_module(f"exo.inference.mlx.models.{model_type}") - except ImportError: - msg = f"Model type {model_type} not supported." - logging.error(msg) - traceback.print_exc() - raise ValueError(msg) - - return arch.Model, arch.ModelArgs - - -def load_config(model_path: Path) -> dict: - try: - config_path = model_path / "config.json" - if config_path.exists(): - with open(config_path, "r") as f: - config = json.load(f) - return config - - model_index_path = model_path / "model_index.json" - if model_index_path.exists(): - config = load_model_index(model_path, model_index_path) - return config - except FileNotFoundError: - logging.error(f"Config file not found in {model_path}") - raise - return config - - -def load_model_shard( - model_path: Path, - shard: Shard, - lazy: bool = False, - model_config: dict = {}, -) -> nn.Module: - """ - Load and initialize the model from a given path. - - Args: - model_path (Path): The path to load the model from. - lazy (bool): If False eval the model parameters to make sure they are - loaded in memory before returning, otherwise they will be loaded - when needed. Default: ``False`` - model_config(dict, optional): Configuration parameters for the model. - Defaults to an empty dictionary. - - Returns: - nn.Module: The loaded and initialized model. - - Raises: - FileNotFoundError: If the weight files (.safetensors) are not found. - ValueError: If the model class or args class are not found or cannot be instantiated. - """ - config = load_config(model_path) - config.update(model_config) - - # Inject shard info into config for model initialization - config["shard"] = { - "model_id": model_path.name, - "start_layer": shard.start_layer, - "end_layer": shard.end_layer, - "n_layers": shard.n_layers, - } - - weight_files = glob.glob(str(model_path / "model*.safetensors")) - - if not weight_files: - # Try weight for back-compat - weight_files = glob.glob(str(model_path / "weight*.safetensors")) - - model_class, model_args_class = _get_classes(config=config) - - class ShardedModel(model_class): - def __init__(self, args): - super().__init__(args) - self.shard = Shard( - args.shard.model_id, - args.shard.start_layer, - args.shard.end_layer, - args.shard.n_layers, - ) - - def __call__(self, x, *args, **kwargs): - y = super().__call__(x, *args, **kwargs) - return y - - model_args = model_args_class.from_dict(config) - model = ShardedModel(model_args) - - if config.get("model_index", False): - model.load() - return model - - if not weight_files: - logging.error(f"No safetensors found in {model_path}") - raise FileNotFoundError(f"No safetensors found in {model_path}") - - weights = {} - for wf in sorted(weight_files): - if DEBUG >= 8: - layer_nums = set() - for k in mx.load(wf): - if k.startswith("model.layers."): - layer_num = int(k.split(".")[2]) - layer_nums.add(layer_num) - if k.startswith("language_model.model.layers."): - layer_num = int(k.split(".")[3]) - layer_nums.add(layer_num) - print(f'"{wf.split("/")[-1]}": {sorted(layer_nums)},') - - weights.update(mx.load(wf)) - - if hasattr(model, "sanitize"): - weights = model.sanitize(weights) - if DEBUG >= 8: - print(f"\n|| {config=} ||\n") - - if (quantization := config.get("quantization", None)) is not None: - # Handle legacy models which may not have everything quantized - def class_predicate(p, m): - if not hasattr(m, "to_quantized"): - return False - return f"{p}.scales" in weights - - nn.quantize( - model, - **quantization, - class_predicate=class_predicate, - ) - - model.load_weights(list(weights.items()), strict=True) - - if not lazy: - mx.eval(model.parameters()) - - model.eval() - return model - - -async def load_shard( - model_path: str, - shard: Shard, - tokenizer_config={}, - model_config={}, - adapter_path: Optional[str] = None, - lazy: bool = False, -) -> Tuple[nn.Module, TokenizerWrapper]: - model = load_model_shard(model_path, shard, lazy, model_config) - - # Handle model-specific tokenizer loading (llava uses processor, others use tokenizer) - if model.model_type == "llava": - processor = AutoProcessor.from_pretrained(model_path) - processor.eos_token_id = processor.tokenizer.eos_token_id - processor.encode = processor.tokenizer.encode - return model, processor - elif hasattr(model, "tokenizer"): - tokenizer = model.tokenizer - return model, tokenizer - else: - tokenizer = await resolve_tokenizer(model_path) - return model, tokenizer - - -async def get_image_from_str(_image_str: str): - image_str = _image_str.strip() - - if image_str.startswith("http"): - async with aiohttp.ClientSession() as session: - async with session.get(image_str, timeout=10) as response: - content = await response.read() - return Image.open(BytesIO(content)).convert("RGB") - elif image_str.startswith("data:image/"): - # Extract the image format and base64 data - format_prefix, base64_data = image_str.split(";base64,") - image_format = format_prefix.split("/")[1].lower() - if DEBUG >= 2: - print(f"{image_str=} {image_format=}") - imgdata = base64.b64decode(base64_data) - img = Image.open(BytesIO(imgdata)) - - # Convert to RGB if not already - if img.mode != "RGB": - img = img.convert("RGB") - - return img - else: - raise ValueError( - "Invalid image_str format. Must be a URL or a base64 encoded image." - ) - - -# loading a combined config for all models in the index -def load_model_index(model_path: Path, model_index_path: Path): - models_config = {} - with open(model_index_path, "r") as f: - model_index = json.load(f) - models_config["model_index"] = True - models_config["model_type"] = model_index["_class_name"] - models_config["models"] = {} - for model in model_index.keys(): - model_config_path = glob.glob(str(model_path / model / "*config.json")) - if len(model_config_path) > 0: - with open(model_config_path[0], "r") as f: - model_config = {} - model_config["model_type"] = model - model_config["config"] = json.load(f) - model_config["path"] = model_path / model - if model_config["path"] / "*model.safetensors": - model_config["config"].update( - { - "weight_files": list( - glob.glob( - str(model_config["path"] / "*model.safetensors") - ) - ) - } - ) - model_config["path"] = str(model_path / model) - m = {} - m[model] = model_config - models_config.update(m) - return models_config diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/test_non_blocking.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/test_non_blocking.py deleted file mode 100644 index 24adf6961..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/test_non_blocking.py +++ /dev/null @@ -1,86 +0,0 @@ -import asyncio -import time -from collections import deque - -from .download.new_shard_download import NewShardDownloader -from .inference.mlx.sharded_inference_engine import MLXDynamicShardInferenceEngine -from .inference.shard import Shard -from .models import build_base_shard - - -async def test_non_blocking(): - # Setup - shard_downloader = NewShardDownloader() - engine = MLXDynamicShardInferenceEngine(shard_downloader) - _shard = build_base_shard("llama-3.1-8b", "MLXDynamicShardInferenceEngine") - shard = Shard( - _shard.model_id, _shard.start_layer, _shard.n_layers - 1, _shard.n_layers - ) - await engine.ensure_shard(shard) - - queue = asyncio.Queue() - measurements = deque(maxlen=1000000) - running = True - - async def mlx_worker(): - try: - start_time = time.time() - count = 0 - while running and (time.time() - start_time) < 5: # Hard time limit - start = time.perf_counter_ns() - await engine.infer_prompt("req1", shard, "test prompt") - duration = (time.perf_counter_ns() - start) / 1_000_000 # Convert to ms - count += 1 - print(f"MLX operation {count} took: {duration:.3f}ms") - except asyncio.CancelledError: - pass - finally: - print(f"\nTotal MLX operations completed: {count}") - print(f"Average rate: {count / 5:.1f} ops/second") - - async def latency_producer(): - try: - start_time = time.perf_counter_ns() - count = 0 - while running: - await queue.put(time.perf_counter_ns()) - count += 1 - await asyncio.sleep(0) # Yield to event loop without delay - duration = (time.perf_counter_ns() - start_time) / 1e9 # Convert to seconds - print(f"\nProducer iterations: {count}") - print(f"Producer rate: {count / duration:.1f} iterations/second") - except asyncio.CancelledError: - pass - - async def latency_consumer(): - try: - while running: - timestamp = await queue.get() - latency = ( - time.perf_counter_ns() - timestamp - ) / 1_000_000 # Convert to ms - measurements.append(latency) - queue.task_done() - except asyncio.CancelledError: - pass - - tasks = [ - asyncio.create_task(mlx_worker()), - asyncio.create_task(latency_producer()), - asyncio.create_task(latency_consumer()), - ] - - try: - await asyncio.wait_for(asyncio.gather(*tasks), timeout=6) - except asyncio.TimeoutError: - print("\nTest timed out") - finally: - running = False - for task in tasks: - task.cancel() - await asyncio.gather(*tasks, return_exceptions=True) - print(f"\nFinal measurement count: {len(measurements)}") - - -if __name__ == "__main__": - asyncio.run(test_non_blocking()) diff --git a/pkg/hanzo-network/src/hanzo_network/inference/mlx/test_sharded_model.py b/pkg/hanzo-network/src/hanzo_network/inference/mlx/test_sharded_model.py deleted file mode 100644 index 285d87b72..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/mlx/test_sharded_model.py +++ /dev/null @@ -1,54 +0,0 @@ -from typing import Optional - -import mlx.core as mx -import mlx.nn as nn -import numpy as np - -from .inference.shard import Shard - - -class DummyModel(nn.Module): - def __init__(self, shard: Optional[Shard] = None): - self.shard = shard - self.layers = [ - nn.Linear(8, 128), - nn.Linear(128, 128), - nn.Linear(128, 128), - nn.Linear(128, 128), - nn.Linear(128, 8), - ] - - self.n_kv_heads = 4 - self.head_dim = 4 - - def __call__(self, x, cache=None): - if self.shard: - for layer in self.layers[self.shard.start_layer : self.shard.end_layer + 1]: - x = layer(x) - if self.shard.is_last_layer(): - x = x.reshape((1, 2, 4)) - else: - for layer in self.layers: - x = layer(x) - x = x.reshape((1, 2, 4)) - - return x - - -model = DummyModel() -model.save_weights("./test_weights.npz") -n_layers = 5 -shard1 = Shard("test", 0, n_layers // 2, n_layers) -sharded_model1 = DummyModel(shard1) -shard2 = Shard("test", n_layers // 2 + 1, n_layers - 1, n_layers) -sharded_model2 = DummyModel(shard2) - -model.load_weights("./test_weights.npz") -sharded_model1.load_weights("./test_weights.npz") -sharded_model2.load_weights("./test_weights.npz") - -fullresp = model(mx.array([1, 2, 3, 4, 5, 6, 7, 8])) -resp1 = sharded_model1(mx.array([1, 2, 3, 4, 5, 6, 7, 8])) -resp2 = sharded_model2(resp1) - -assert np.all(np.array(fullresp) == np.array(resp2)) diff --git a/pkg/hanzo-network/src/hanzo_network/inference/shard.py b/pkg/hanzo-network/src/hanzo_network/inference/shard.py deleted file mode 100644 index e8b2c9ea7..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/shard.py +++ /dev/null @@ -1,41 +0,0 @@ -from dataclasses import dataclass - - -@dataclass(frozen=True) -class Shard: - model_id: str - start_layer: int - end_layer: int - n_layers: int - - def __hash__(self): - return hash((self.model_id, self.start_layer, self.end_layer, self.n_layers)) - - def is_first_layer(self) -> bool: - return self.start_layer == 0 - - def is_last_layer(self) -> bool: - return self.end_layer == self.n_layers - 1 - - def get_layer_count(self) -> int: - return self.end_layer - self.start_layer + 1 - - def to_dict(self) -> dict: - return { - "model_id": self.model_id, - "start_layer": self.start_layer, - "end_layer": self.end_layer, - "n_layers": self.n_layers, - } - - def from_dict(data: dict) -> "Shard": - return Shard(**data) - - def overlaps(self, other: "Shard") -> bool: - return shards_overlap(self, other) - - -def shards_overlap(shard1: Shard, shard2: Shard) -> bool: - return shard1.model_id == shard2.model_id and max( - shard1.start_layer, shard2.start_layer - ) <= min(shard1.end_layer, shard2.end_layer) diff --git a/pkg/hanzo-network/src/hanzo_network/inference/test_dummy_inference_engine.py b/pkg/hanzo-network/src/hanzo_network/inference/test_dummy_inference_engine.py deleted file mode 100644 index dee5bfeb2..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/test_dummy_inference_engine.py +++ /dev/null @@ -1,49 +0,0 @@ -import numpy as np -import pytest - -from .inference.dummy_inference_engine import DummyInferenceEngine -from .inference.shard import Shard - - -@pytest.mark.asyncio -async def test_dummy_inference_specific(): - engine = DummyInferenceEngine() - test_shard = Shard(model_id="test_model", start_layer=0, end_layer=1, n_layers=1) - test_prompt = "This is a test prompt" - - result, _ = await engine.infer_prompt("test_request", test_shard, test_prompt) - - print(f"Inference result shape: {result.shape}") - - assert result.shape[0] == 1, "Result should be a 2D array with first dimension 1" - - -@pytest.mark.asyncio -async def test_dummy_inference_engine(): - # Initialize the DummyInferenceEngine - engine = DummyInferenceEngine() - - # Create a test shard - shard = Shard(model_id="test_model", start_layer=0, end_layer=1, n_layers=1) - - # Test infer_prompt - output, _ = await engine.infer_prompt("test_id", shard, "Test prompt") - - assert isinstance(output, np.ndarray), "Output should be a numpy array" - assert output.ndim == 2, "Output should be 2-dimensional" - - # Test infer_tensor - input_tensor = np.array([[1, 2, 3]]) - output, _ = await engine.infer_tensor("test_id", shard, input_tensor) - - assert isinstance(output, np.ndarray), "Output should be a numpy array" - assert output.ndim == 2, "Output should be 2-dimensional" - - print("All tests passed!") - - -if __name__ == "__main__": - import asyncio - - asyncio.run(test_dummy_inference_engine()) - asyncio.run(test_dummy_inference_specific()) diff --git a/pkg/hanzo-network/src/hanzo_network/inference/test_inference_engine.py b/pkg/hanzo-network/src/hanzo_network/inference/test_inference_engine.py deleted file mode 100644 index aa2006132..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/test_inference_engine.py +++ /dev/null @@ -1,99 +0,0 @@ -import asyncio -import os - -import numpy as np - -from .download.new_shard_download import NewShardDownloader -from .inference.inference_engine import InferenceEngine -from .inference.mlx.sharded_inference_engine import MLXDynamicShardInferenceEngine -from .inference.shard import Shard - - -# An inference engine should work the same for any number of Shards, as long as the Shards are continuous. -async def test_inference_engine( - inference_engine_1: InferenceEngine, - inference_engine_2: InferenceEngine, - model_id: str, - n_layers: int, -): - prompt = "In a single word only, what is the last name of the current president of the USA?" - resp_full, _ = await inference_engine_1.infer_prompt( - "A", - shard=Shard( - model_id=model_id, start_layer=0, end_layer=n_layers - 1, n_layers=n_layers - ), - prompt=prompt, - ) - token_full = await inference_engine_1.sample(resp_full) - token_full = token_full.reshape(1, -1) - next_resp_full, _ = await inference_engine_1.infer_tensor( - "A", - shard=Shard( - model_id=model_id, start_layer=0, end_layer=n_layers - 1, n_layers=n_layers - ), - input_data=token_full, - ) - - pp = n_layers // 2 - resp1, _ = await inference_engine_1.infer_prompt( - "B", - shard=Shard(model_id=model_id, start_layer=0, end_layer=pp, n_layers=n_layers), - prompt=prompt, - ) - resp2, _ = await inference_engine_2.infer_tensor( - "B", - shard=Shard( - model_id=model_id, - start_layer=pp + 1, - end_layer=n_layers - 1, - n_layers=n_layers, - ), - input_data=resp1, - ) - tokens2 = await inference_engine_1.sample(resp2) - tokens2 = tokens2.reshape(1, -1) - resp3, _ = await inference_engine_1.infer_tensor( - "B", - shard=Shard(model_id=model_id, start_layer=0, end_layer=pp, n_layers=n_layers), - input_data=tokens2, - ) - resp4, _ = await inference_engine_2.infer_tensor( - "B", - shard=Shard( - model_id=model_id, - start_layer=pp + 1, - end_layer=n_layers - 1, - n_layers=n_layers, - ), - input_data=resp3, - ) - - assert np.array_equal(resp_full, resp2) - assert np.array_equal(next_resp_full, resp4) - - -asyncio.run( - test_inference_engine( - MLXDynamicShardInferenceEngine(NewShardDownloader()), - MLXDynamicShardInferenceEngine(NewShardDownloader()), - "llama-3.2-1b", - 16, - ) -) - -if os.getenv("RUN_TINYGRAD", default="0") == "1": - import os - - import tinygrad - - from .inference.tinygrad.inference import TinygradDynamicShardInferenceEngine - - tinygrad.helpers.DEBUG.value = int(os.getenv("TINYGRAD_DEBUG", default="0")) - asyncio.run( - test_inference_engine( - TinygradDynamicShardInferenceEngine(NewShardDownloader()), - TinygradDynamicShardInferenceEngine(NewShardDownloader()), - "llama-3.2-1b", - 32, - ) - ) diff --git a/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/__init__.py b/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/inference.py b/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/inference.py deleted file mode 100644 index 18742ee27..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/inference.py +++ /dev/null @@ -1,315 +0,0 @@ -import asyncio -import os -from collections import OrderedDict -from concurrent.futures import ThreadPoolExecutor -from pathlib import Path -from typing import Optional - -import numpy as np -from tinygrad import Context, Tensor, nn -from tinygrad.nn.state import get_state_dict, load_state_dict, safe_load, safe_save - -from .download.shard_download import ShardDownloader -from .inference.inference_engine import InferenceEngine -from .inference.shard import Shard -from .inference.tinygrad.models.llama import ( - Transformer, - TransformerShard, - convert_from_huggingface, - fix_bf16, - sample_logits, -) -from .inference.tinygrad.tinygrad_helpers import concat_weights, load -from .inference.tokenizers import resolve_tokenizer -from .losses import length_masked_ce_loss -from .stateful_model import make_prompt_state - -Tensor.no_grad = True -# default settings -TEMPERATURE = int(os.getenv("TEMPERATURE", 0.85)) -TOP_K = 25 -TOP_P = 0.9 -ALPHA_F = 0.1 -ALPHA_P = 0.0 -MODEL_PARAMS = { - "1B": { - "args": { - "dim": 2048, - "n_heads": 32, - "n_kv_heads": 8, - "n_layers": 16, - "norm_eps": 1e-5, - "rope_theta": 500000, - "vocab_size": 128256, - "hidden_dim": 8192, - "rope_scaling": { - "factor": 32.0, - "high_freq_factor": 4.0, - "low_freq_factor": 1.0, - "original_max_position_embeddings": 8192, - "rope_type": "llama3", - }, - "tie_word_embeddings": True, - }, - "files": 1, - }, - "3B": { - "args": { - "dim": 3072, - "n_heads": 24, - "n_kv_heads": 8, - "n_layers": 28, - "norm_eps": 1e-5, - "rope_theta": 500000, - "vocab_size": 128256, - "hidden_dim": 8192, - "rope_scaling": { - "factor": 32.0, - "high_freq_factor": 4.0, - "low_freq_factor": 1.0, - "original_max_position_embeddings": 8192, - "rope_type": "llama3", - }, - "tie_word_embeddings": True, - }, - "files": 1, - }, - "8B": { - "args": { - "dim": 4096, - "n_heads": 32, - "n_kv_heads": 8, - "n_layers": 32, - "norm_eps": 1e-5, - "rope_theta": 500000, - "vocab_size": 128256, - "hidden_dim": 14336, - }, - "files": 1, - }, - "70B": { - "args": { - "dim": 8192, - "n_heads": 64, - "n_kv_heads": 8, - "n_layers": 80, - "norm_eps": 1e-5, - "rope_theta": 500000, - "vocab_size": 128256, - "hidden_dim": 28672, - }, - "files": 8, - }, -} - - -def build_transformer(model_path: Path, shard: Shard, model_size="8B", device=None): - # build model - linear = nn.Linear - model = Transformer( - **MODEL_PARAMS[model_size]["args"], - linear=linear, - max_context=8192, - jit=True, - shard=shard, - ) - - # load weights - if model_path.is_dir(): - if (model_path / "model.safetensors.index.json").exists(): - weights = load(str(model_path / "model.safetensors.index.json"), shard) - elif (model_path / "model.safetensors").exists(): - weights = load(str(model_path / "model.safetensors"), shard) - else: - weights = concat_weights( - [ - load(str(model_path / f"consolidated.{i:02d}.pth"), shard) - for i in range(MODEL_PARAMS[model_size]["files"]) - ], - device[0] if isinstance(device, tuple) else device, - ) - else: - weights = load(str(model_path), shard) - weights = convert_from_huggingface( - weights, - model, - MODEL_PARAMS[model_size]["args"]["n_heads"], - MODEL_PARAMS[model_size]["args"]["n_kv_heads"], - ) - weights = fix_bf16(weights) - - with Context(BEAM=0): - # replace weights in model - load_state_dict(model, weights, strict=False, consume=False) # consume=True - model = TransformerShard(shard, model) - - return model - - -_executor = ThreadPoolExecutor( - max_workers=1 -) # singleton so tinygrad always runs on the same thread - - -class TinygradDynamicShardInferenceEngine(InferenceEngine): - def __init__(self, shard_downloader: ShardDownloader): - self.shard = None - self.shard_downloader = shard_downloader - self.states = OrderedDict() - self.executor = _executor - - def poll_state(self, x, request_id: str, max_states=2): - if request_id not in self.states: - if len(self.states) >= max_states: - self.states.popitem(last=False) - self.states[request_id] = make_prompt_state(x, self.model) - else: - self.states.move_to_end(request_id) - state = self.states[request_id] - return {"start_pos": state.start, "cache": state.cache} - - async def sample( - self, x: np.ndarray, temp=TEMPERATURE, top_p: float = 0.0 - ) -> np.ndarray: - def sample_wrapper(): - logits = x[:, -1, :] - return ( - sample_logits(Tensor(logits).flatten(), temp, 0, 0.8, top_p, 0.0) - .realize() - .numpy() - .astype(int) - ) - - return await asyncio.get_running_loop().run_in_executor( - self.executor, sample_wrapper - ) - - async def encode(self, shard: Shard, prompt: str) -> np.ndarray: - await self.ensure_shard(shard) - tokens = await asyncio.get_running_loop().run_in_executor( - self.executor, self.tokenizer.encode, prompt - ) - return await asyncio.get_running_loop().run_in_executor( - self.executor, np.array, tokens - ) - - async def decode(self, shard: Shard, tokens) -> str: - await self.ensure_shard(shard) - tokens = await asyncio.get_running_loop().run_in_executor( - self.executor, self.tokenizer.decode, tokens - ) - return tokens - - async def load_checkpoint(self, shard: Shard, path: str): - await self.ensure_shard(shard) - state_dict = safe_load(path) - await asyncio.get_running_loop().run_in_executor( - self.executor, load_state_dict, self.model, state_dict - ) - - async def save_checkpoint(self, shard: Shard, path: str): - await self.ensure_shard(shard) - state_dict = await asyncio.get_running_loop().run_in_executor( - self.executor, get_state_dict, self.model - ) - safe_save(state_dict, path) - - async def infer_tensor( - self, - request_id: str, - shard: Shard, - input_data: np.ndarray, - inference_state: Optional[dict] = None, - ) -> tuple[np.ndarray, Optional[dict]]: - await self.ensure_shard(shard) - - def wrap_infer(): - x = Tensor(input_data) - h = self.model.embed(x) - state = self.poll_state(h, request_id) - out = self.model.forward(h, **state) - self.states[request_id].start += x.shape[1] - return out.numpy() - - output_data = await asyncio.get_running_loop().run_in_executor( - self.executor, wrap_infer - ) - return output_data, inference_state - - async def evaluate( - self, - request_id: str, - shard: Shard, - inputs, - targets, - lengths, - loss=length_masked_ce_loss, - ): - def step(x, y, layer): - Tensor.training = False - return self.session["loss"](self.model, x, y, layer) - - await self.ensure_shard(shard) - score = await asyncio.get_running_loop().run_in_executor( - self.executor, lambda: self.session["jit"](Tensor(inputs), targets, lengths) - ) - out = score.numpy() - return out - - async def train( - self, - request_id: str, - shard: Shard, - inputs, - targets, - lengths, - loss=length_masked_ce_loss, - opt=nn.optim.Adam, - lr=1e-5, - ): - def step(x, y, layer): - Tensor.training = True - score = self.session["loss"](self.model, x, y, layer) - self.session["opt"].zero_grad() - score.backward() - self.session["opt"].step() - return score - - await self.ensure_shard(shard) - - await asyncio.get_running_loop().run_in_executor( - self.executor, - lambda: self.session["jit"](Tensor(inputs), targets, lengths).realize(), - ) - - return loss.numpy(), loss.numpy() - - async def ensure_shard(self, shard: Shard): - if self.shard == shard: - return - - model_path = await self.shard_downloader.ensure_shard( - shard, self.__class__.__name__ - ) - - if self.shard != shard: - loop = asyncio.get_running_loop() - parameters = ( - "1B" - if "1b" in shard.model_id.lower() - else ( - "3B" - if "3b" in shard.model_id.lower() - else "8B" if "8b" in shard.model_id.lower() else "70B" - ) - ) - model_shard = await loop.run_in_executor( - self.executor, build_transformer, model_path, shard, parameters - ) - - tokenizer_path = str( - (model_path if model_path.is_dir() else model_path.parent) - ) - self.tokenizer = await resolve_tokenizer(tokenizer_path) - self.shard = shard - self.model = model_shard diff --git a/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/losses.py b/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/losses.py deleted file mode 100644 index 2435f803d..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/losses.py +++ /dev/null @@ -1,19 +0,0 @@ -import numpy as np -from tinygrad import Tensor, dtypes - - -def length_masked_ce_loss(model, inputs, targets, lengths): - # Run model on inputs - logits = model(inputs).cast(dtypes.float32).contiguous() - - # Mask padding tokens - length_mask = Tensor( - np.arange(inputs.shape[1])[None, :] < lengths[:, None], requires_grad=False - ) - - # Calculate the loss - ce = logits.sparse_categorical_crossentropy( - Tensor(targets, requires_grad=False) - ).mul(length_mask) - loss = ce.sum() / length_mask.sum() - return loss diff --git a/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/models/__init__.py b/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/models/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/models/llama.py b/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/models/llama.py deleted file mode 100644 index 20ea03263..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/models/llama.py +++ /dev/null @@ -1,519 +0,0 @@ -from typing import Any, Dict, List, Optional, Tuple, Union - -from tinygrad import Device, Tensor, TinyJit, Variable, dtypes, nn -from tinygrad.helpers import getenv - -from .inference.shard import Shard - - -# https://github.com/facebookresearch/llama/blob/1076b9c51c77ad06e9d7ba8a4c6df775741732bd/llama/model.py#L47 -def precompute_freqs_cis( - dim: int, - end: int, - theta: float = 10000.0, - dtype=dtypes.half, - rope_scaling: Optional[Dict[str, float]] = None, -) -> Tensor: - freqs = 1.0 / (theta ** (Tensor.arange(0, dim, 2)[: (dim // 2)] / dim)) - - if rope_scaling: - factor = rope_scaling.get("factor", 1.0) - low_freq_factor = rope_scaling.get("low_freq_factor", 1.0) - high_freq_factor = rope_scaling.get("high_freq_factor", 1.0) - original_max_pos_emb = rope_scaling.get("original_max_position_embeddings", end) - - freqs[: dim // 4] *= low_freq_factor - freqs[dim // 4 :] = freqs[dim // 4 :].contiguous() * high_freq_factor - freqs *= (original_max_pos_emb / end) ** (1.0 / factor) - - freqs = Tensor.arange(end).unsqueeze(dim=1) * freqs.unsqueeze(dim=0) - # TODO: move dtype outside this - return Tensor.stack( - freqs.cos().cast(dtype), freqs.sin().cast(dtype), dim=-1 - ).reshape(1, end, 1, dim // 2, 2) - - -# (a+i*b) * (c+i*d) = (ac-bd) + i*(ad+bc) -def complex_mult(A, c, d): - a, b = A[..., 0:1], A[..., 1:2] - ro = a * c - b * d - co = a * d + b * c - return ro.cat(co, dim=-1) - - -def apply_rotary_emb( - xq: Tensor, xk: Tensor, freqs_cis: Tensor -) -> Tuple[Tensor, Tensor]: - assert ( - freqs_cis.shape[1] == xq.shape[1] == xk.shape[1] - ), f"freqs_cis shape mismatch {freqs_cis.shape} xq:{xq.shape} xk:{xk.shape}" - xq = xq.reshape(*xq.shape[0:-1], -1, 2) - xk = xk.reshape(*xk.shape[0:-1], -1, 2) - assert len(xq.shape) == len(xk.shape) == len(freqs_cis.shape) == 5 - c, d = freqs_cis[..., 0:1], freqs_cis[..., 1:2] - xq_out = complex_mult(xq, c, d) - xk_out = complex_mult(xk, c, d) - return xq_out.flatten(3), xk_out.flatten(3) - - -def repeat_kv(x: Tensor, n_rep: int) -> Tensor: - bs, seqlen, n_kv_heads, head_dim = x.shape - if n_rep == 1: - return x - # NOTE: this is different from x.repeat((1, 1, n_rep, 1)) - return x.repeat((1, 1, 1, n_rep)).reshape(bs, seqlen, n_kv_heads * n_rep, head_dim) - - -class Attention: - def __init__(self, dim, n_heads, n_kv_heads, max_context, linear=nn.Linear): - self.n_heads = n_heads - self.n_kv_heads = ( - n_kv_heads if n_kv_heads is not None else n_heads - ) # n_kv_heads != n_heads implies MQA [arxiv/2307.09288, A.2.1] - self.head_dim = dim // n_heads - self.n_rep = self.n_heads // self.n_kv_heads - self.max_context = max_context - - self.wq = linear(dim, self.n_heads * self.head_dim, bias=False) - self.wk = linear(dim, self.n_kv_heads * self.head_dim, bias=False) - self.wv = linear(dim, self.n_kv_heads * self.head_dim, bias=False) - self.wo = linear(self.n_heads * self.head_dim, dim, bias=False) - - def __call__( - self, - x: Tensor, - start_pos: Union[Variable, int], - freqs_cis: Tensor, - mask: Optional[Tensor], - cache: Optional[Tensor] = None, - ) -> Tensor: - if getenv("WQKV"): - if not hasattr(self, "wqkv"): - self.wqkv = Tensor.cat(self.wq.weight, self.wk.weight, self.wv.weight) - xqkv = x @ self.wqkv.T - xq, xk, xv = xqkv.split( - [ - self.wq.weight.shape[0], - self.wk.weight.shape[0], - self.wv.weight.shape[0], - ], - dim=2, - ) - else: - xq, xk, xv = self.wq(x), self.wk(x), self.wv(x) - - xq = xq.reshape(xq.shape[0], xq.shape[1], self.n_heads, self.head_dim) - xk = xk.reshape(xk.shape[0], xk.shape[1], self.n_kv_heads, self.head_dim) - xv = xv.reshape(xv.shape[0], xv.shape[1], self.n_kv_heads, self.head_dim) - - xq, xk = apply_rotary_emb(xq, xk, freqs_cis) - bsz, seqlen, _, _ = xq.shape - - if cache is not None: - # update the cache - assert ( - xk.dtype == xv.dtype == cache.dtype - ), f"{xk.dtype=}, {xv.dtype=}, {cache.dtype=}" - cache.shrink( - (None, None, (start_pos, start_pos + seqlen), None, None) - ).assign(Tensor.stack(xk, xv)).realize() - - keys = ( - cache[0].shrink((None, (0, start_pos + seqlen), None, None)) - if start_pos > 0 - else xk - ) - values = ( - cache[1].shrink((None, (0, start_pos + seqlen), None, None)) - if start_pos > 0 - else xv - ) - else: - keys = xk - values = xv - - keys, values = repeat_kv(keys, self.n_rep), repeat_kv(values, self.n_rep) - xq, keys, values = ( - xq.transpose(1, 2), - keys.transpose(1, 2), - values.transpose(1, 2), - ) - attn = xq.scaled_dot_product_attention(keys, values, mask).transpose(1, 2) - attn = attn.reshape(bsz, seqlen, -1) - return self.wo(attn) - - -class FeedForward: - def __init__(self, dim: int, hidden_dim: int, linear=nn.Linear): - self.w1 = linear(dim, hidden_dim, bias=False) - self.w2 = linear(hidden_dim, dim, bias=False) - self.w3 = linear(dim, hidden_dim, bias=False) # the gate in Gated Linear Unit - - def __call__(self, x: Tensor) -> Tensor: - return self.w2( - self.w1(x).silu() * self.w3(x) - ) # SwiGLU [arxiv/2002.05202, eq (5)] - - -class TransformerBlock: - def __init__( - self, - dim: int, - hidden_dim: int, - n_heads: int, - n_kv_heads: int, - norm_eps: float, - max_context: int, - linear=nn.Linear, - feed_forward=FeedForward, - ): - self.attention = Attention(dim, n_heads, n_kv_heads, max_context, linear) - self.feed_forward = feed_forward(dim, hidden_dim, linear) - self.attention_norm = nn.RMSNorm(dim, norm_eps) - self.ffn_norm = nn.RMSNorm(dim, norm_eps) - - def __call__( - self, - x: Tensor, - start_pos: Union[Variable, int], - freqs_cis: Tensor, - mask: Optional[Tensor], - cache: Optional[Tensor] = None, - ): - h = x + self.attention( - self.attention_norm(x), start_pos, freqs_cis, mask, cache=cache - ) - return (h + self.feed_forward(self.ffn_norm(h))).contiguous() - - -# standard openai sampling -def sample_logits( - logits: Tensor, temp: float, k: int, p: float, af: float, ap: float, sample=None -): - assert logits.ndim == 1, "only works on 1d tensors" - assert 0 <= p <= 1, "p must be between 0 and 1" - assert 0 <= k <= logits.numel(), "k must be between 0 and numel" - - # if temperature is very low just use argmax - if temp < 1e-6: - return logits.argmax().reshape(1) - - # alpha sampling - if af or ap: - if not hasattr(sample, "alpha_counter"): - setattr( - sample, - "alpha_counter", - Tensor.zeros_like(logits, dtype=dtypes.int32).contiguous(), - ) - logits = logits - (sample.alpha_counter * af + (sample.alpha_counter > 0) * ap) - - # replace NaNs with -inf - logits = (logits != logits).where(-float("inf"), logits) - - # softmax - t = (logits / temp).softmax() - - counter, counter2 = ( - Tensor.arange(t.numel(), device=logits.device).contiguous(), - Tensor.arange(t.numel() - 1, -1, -1, device=logits.device).contiguous(), - ) - # top k - if k: - output, output_indices = ( - Tensor.zeros(k, device=logits.device).contiguous(), - Tensor.zeros(k, device=logits.device, dtype=dtypes.int32).contiguous(), - ) - for i in range(k): - t_argmax = ( - t.numel() - ((t == (t_max := t.max())) * counter2).max() - 1 - ).cast(dtypes.default_int) - output = output + t_max.unsqueeze(0).pad(((i, k - i - 1),)) - output_indices = output_indices + t_argmax.unsqueeze(0).pad( - ((i, k - i - 1),) - ) - t = (counter == t_argmax).where(0, t) - - # approximate top p - # because we are already limited to top k elements we can do top p "without sorting" - output_cumsum = output[::-1]._cumsum()[::-1] + t.sum() - output = (output_cumsum >= (1 - p)) * output - output_indices = (output_cumsum >= (1 - p)) * output_indices - - # sample - output_idx = output.multinomial() - output_token = output_indices[output_idx] - else: - output_token = t.multinomial() - - # increase alpha counter - if af or ap: - sample.alpha_counter = (counter == output_token).where( - sample.alpha_counter + 1, sample.alpha_counter - ) - - return output_token - - -class Transformer: - def __init__( - self, - dim: int, - hidden_dim: int, - n_heads: int, - n_layers: int, - norm_eps: float, - vocab_size, - shard: Shard = None, - linear=nn.Linear, - n_kv_heads=None, - rope_theta=10000, - max_context=1024, - jit=True, - feed_forward=FeedForward, - rope_scaling: Optional[Dict[str, float]] = None, - tie_word_embeddings=False, - ): - self.layers = [ - TransformerBlock( - dim, - hidden_dim, - n_heads, - n_kv_heads, - norm_eps, - max_context, - linear, - feed_forward=feed_forward, - ) - for _ in range(n_layers) - ] - self.norm = nn.RMSNorm(dim, norm_eps) - self.tok_embeddings = nn.Embedding(vocab_size, dim) - self.output = nn.Linear(dim, vocab_size, bias=False) - if tie_word_embeddings: - self.output.weight = self.tok_embeddings.weight - self.max_context = max_context - self.freqs_cis = precompute_freqs_cis( - dim // n_heads, self.max_context * 2, rope_theta, rope_scaling=rope_scaling - ).contiguous() - self.forward_jit = TinyJit(self.forward_base) if jit else None - self.shard = shard - - def forward_base( - self, - x: Tensor, - start_pos: Union[Variable, int], - cache: Optional[List[Tensor]] = None, - ): - seqlen = x.shape[1] - freqs_cis = self.freqs_cis.shrink( - (None, (start_pos, start_pos + seqlen), None, None, None) - ) - mask = ( - Tensor.full( - (1, 1, seqlen, start_pos + seqlen), - float("-100000000"), - dtype=x.dtype, - device=x.device, - ) - .triu(start_pos + 1) - .realize() - if seqlen > 1 - else None - ) - - h = x - - if cache is None: - cache = [ - None for _ in range(self.shard.start_layer, self.shard.end_layer + 1) - ] - for i, c in zip(range(self.shard.start_layer, self.shard.end_layer + 1), cache): - layer = self.layers[i] - h = layer(h, start_pos, freqs_cis, mask, cache=c) - - if self.shard.is_last_layer(): - logits = self.output(self.norm(h)).float().realize() - return logits - else: - return h - - def embed(self, inputs: Tensor): - if self.shard.is_first_layer(): - h = self.tok_embeddings(inputs) - else: - h = inputs - return h - - def forward(self, x: Tensor, start_pos: int, cache: Optional[List[Tensor]] = None): - if x.shape[0:2] == (1, 1) and self.forward_jit is not None and start_pos != 0: - return self.forward_jit( - x, - Variable("start_pos", 1, self.max_context).bind(start_pos), - cache=cache, - ) - return self.forward_base(x, start_pos, cache=cache) - - def __call__( - self, x: Tensor, start_pos: Variable, cache: Optional[List[Tensor]] = None - ): - # TODO: better way to handle the first call v.s. the rest? - h = self.embed(x) - return self.forward(h, start_pos, cache=cache) - - -class TransformerShard: - def __init__( - self, - shard: Shard, - base, - jit: bool = True, - ): - shardrange = range(shard.start_layer, shard.end_layer + 1) - self.layers = [ - layer - for layer, n in zip(base.layers, range(shard.n_layers)) - if n in shardrange - ] - self.norm = base.norm - self.tok_embeddings = base.tok_embeddings - self.embed = ( - (lambda x: self.tok_embeddings(x)) - if shard.is_first_layer() - else (lambda x: x) - ) - self.output = base.output - self.post = ( - (lambda x: self.output(x)) if shard.is_last_layer() else (lambda x: x) - ) - self.max_context = base.max_context - self.null_cache = [None for _ in shardrange] - self.freqs_cis = base.freqs_cis - self.forward_jit = TinyJit(self.forward_base) if jit else None - - def forward_base(self, x: Tensor, start_pos: Union[Variable, int], cache): - seqlen = x.shape[1] - freqs_cis = self.freqs_cis.shrink( - (None, (start_pos, start_pos + seqlen), None, None, None) - ) - mask = ( - Tensor.full( - (1, 1, seqlen, start_pos + seqlen), - float("-100000000"), - dtype=x.dtype, - device=x.device, - ) - .triu(start_pos + 1) - .realize() - if seqlen > 1 - else None - ) - - for layer, c in zip(self.layers, cache): - x = layer(x, start_pos, freqs_cis, mask, cache=c) - - out = self.post(x) - return out - - def forward(self, x: Tensor, start_pos: int, cache: Optional[List[Tensor]] = None): - if x.shape[0:2] == (1, 1) and self.forward_jit is not None and start_pos != 0: - return self.forward_jit( - x, - Variable("start_pos", 1, self.max_context).bind(start_pos), - cache=cache, - ) - return self.forward_base(x, start_pos, cache=cache) - - def __call__( - self, x: Tensor, start_pos: Variable, cache: Optional[List[Tensor]] = None - ): - # TODO: better way to handle the first call v.s. the rest? - h = self.embed(x) - return self.forward( - h, start_pos, cache=self.null_cache if cache is None else cache - ) - - -# *** helpers *** - - -def convert_from_huggingface( - weights: Dict[str, Tensor], model: Transformer, n_heads: int, n_kv_heads: int -): - def permute(v: Tensor, n_heads: int): - return ( - v.reshape(n_heads, 2, v.shape[0] // n_heads // 2, v.shape[1]) - .transpose(1, 2) - .reshape(*v.shape[:2]) - ) - - keymap = { - "model.embed_tokens.weight": "tok_embeddings.weight", - **{ - f"model.layers.{layer}.input_layernorm.weight": f"layers.{layer}.attention_norm.weight" - for layer in range(len(model.layers)) - }, - **{ - f"model.layers.{layer}.self_attn.{x}_proj.weight": f"layers.{layer}.attention.w{x}.weight" - for x in ["q", "k", "v", "o"] - for layer in range(len(model.layers)) - }, - **{ - f"model.layers.{layer}.post_attention_layernorm.weight": f"layers.{layer}.ffn_norm.weight" - for layer in range(len(model.layers)) - }, - **{ - f"model.layers.{layer}.mlp.{x}_proj.weight": f"layers.{layer}.feed_forward.w{y}.weight" - for x, y in {"gate": "1", "down": "2", "up": "3"}.items() - for layer in range(len(model.layers)) - }, - "model.norm.weight": "norm.weight", - "lm_head.weight": "output.weight", - } - sd = {} - for k, v in weights.items(): - if ".rotary_emb." in k: - continue - v = v.to(Device.DEFAULT) - if "model.layers" in k: - if "q_proj" in k: - v = permute(v, n_heads) - elif "k_proj" in k: - v = permute(v, n_kv_heads) - if k in keymap: - sd[keymap[k]] = v - else: - sd[k] = v - return sd - - -def fix_bf16(weights: Dict[Any, Tensor]): - if Device.DEFAULT == "CLANG": - # TODO: without casting to float16, 70B llama OOM on tinybox. - return { - k: ( - v.llvm_bf16_cast(dtypes.float32).to(v.device) - if v.dtype == dtypes.bfloat16 - else v - ) - for k, v in weights.items() - } - if getenv("SUPPORT_BF16", 1): - # TODO: without casting to float16, 70B llama OOM on tinybox. - return { - k: ( - v.cast(dtypes.float32).cast(dtypes.float16) - if v.dtype == dtypes.bfloat16 - else v - ) - for k, v in weights.items() - } - # TODO: check if device supports bf16 - return { - k: ( - v.llvm_bf16_cast(dtypes.half).to(v.device) - if v.dtype == dtypes.bfloat16 - else v - ) - for k, v in weights.items() - } diff --git a/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/stateful_model.py b/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/stateful_model.py deleted file mode 100644 index 6c918c9af..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/stateful_model.py +++ /dev/null @@ -1,40 +0,0 @@ -from os import getenv -from typing import List - -from tinygrad import Tensor - - -def create_kv_cache(x: Tensor, layer): - cache_kv = ( - Tensor.zeros( - 2, - x.shape[0], - layer.max_context, - layer.n_kv_heads, - layer.head_dim, - dtype=x.dtype, - ) - .contiguous() - .realize() - ) - if isinstance(x.device, tuple): - # TODO: instead of specifying how to shard, it can follow how xk and xv are being sharded - cache_kv.shard_( - (x.device), axis=3 if getenv("SHARD_KVCACHE") else None - ).realize() - return cache_kv.realize() - - -class ModelState: - cache: List[Tensor] - start: int - - def __init__(self, cache: List[Tensor], start: int = 0): - self.cache = cache - self.start = start - - -def make_prompt_state(x: Tensor, model): - cache = [create_kv_cache(x, layer.attention) for layer in model.layers] - - return ModelState(cache) diff --git a/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/tinygrad_helpers.py b/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/tinygrad_helpers.py deleted file mode 100644 index 796c12924..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/tinygrad/tinygrad_helpers.py +++ /dev/null @@ -1,69 +0,0 @@ -import json -import re -from fnmatch import fnmatch -from pathlib import Path -from typing import List - -from tinygrad import Tensor -from tinygrad.nn.state import safe_load, torch_load - -from .download.hf.hf_helpers import get_allow_patterns -from .helpers import DEBUG -from .inference.shard import Shard - - -# **** helper functions **** -def concat_weights(models, device=None): - def convert(name) -> Tensor: - disk_tensors: List[Tensor] = [model[name] for model in models] - if len(disk_tensors) == 1 or len(disk_tensors[0].shape) == 1: - return disk_tensors[0].to(device=device) - axis = ( - 1 - if name.endswith(".attention.wo.weight") - or name.endswith(".feed_forward.w2.weight") - else 0 - ) - lazy_tensors = [data.to(device=device) for data in disk_tensors] - return lazy_tensors[0].cat(*lazy_tensors[1:], dim=axis) - - return { - name: convert(name) - for name in {name: None for model in models for name in model} - } - - -def load(fn: str, shard: Shard): - if fn.endswith(".index.json"): - with open(fn) as fp: - weight_map = json.load(fp)["weight_map"] - parts = {} - filtered_weight_map = {} - allow_patterns = get_allow_patterns(weight_map, shard) - for k, n in weight_map.items(): - if allow_patterns is not None and not any( - fnmatch(n, r) for r in allow_patterns - ): - continue - if k.startswith("model.layers."): - layer_num = int(k.split(".")[2]) - if layer_num < shard.start_layer or layer_num > shard.end_layer: - continue - - parts[n] = load(str(Path(fn).parent / Path(n).name), shard) - filtered_weight_map[k] = n - if DEBUG >= 2: - print( - f"Excluded model param keys for {shard=}: {sorted(set(weight_map.keys()) - set(filtered_weight_map.keys()))}" - ) - return {k: parts[n][k] for k, n in filtered_weight_map.items()} - elif fn.endswith(".safetensors"): - weight_map = safe_load(fn) - for k in list(weight_map): - if (n := re.search(r"\.(\d+)\.", k)) and not ( - shard.start_layer <= int(n.group(1)) <= shard.end_layer - ): - del weight_map[k] - return weight_map - else: - return torch_load(fn) diff --git a/pkg/hanzo-network/src/hanzo_network/inference/tokenizers.py b/pkg/hanzo-network/src/hanzo_network/inference/tokenizers.py deleted file mode 100644 index 12b58eaa1..000000000 --- a/pkg/hanzo-network/src/hanzo_network/inference/tokenizers.py +++ /dev/null @@ -1,116 +0,0 @@ -import traceback -from os import PathLike -from typing import Union - -import numpy as np - -from .helpers import DEBUG - -# Lazy imports to avoid dependencies when not needed -try: - from aiofiles import os as aios -except ImportError: - aios = None - -try: - from transformers import AutoProcessor, AutoTokenizer -except ImportError: - AutoTokenizer = None - AutoProcessor = None - -try: - from .download.new_shard_download import ensure_downloads_dir -except ImportError: - ensure_downloads_dir = None - - -class DummyTokenizer: - def __init__(self): - self.eos_token_id = 69 - self.vocab_size = 1000 - - def apply_chat_template( - self, - conversation, - tokenize=True, - add_generation_prompt=True, - tools=None, - **kwargs, - ): - return "dummy_tokenized_prompt" - - def encode(self, text): - return np.array([1]) - - def decode(self, tokens): - return "dummy" * len(tokens) - - -async def resolve_tokenizer(repo_id: Union[str, PathLike]): - if repo_id == "dummy": - return DummyTokenizer() - local_path = await ensure_downloads_dir() / str(repo_id).replace("/", "--") - if DEBUG >= 2: - print( - f"Checking if local path exists to load tokenizer from local {local_path=}" - ) - try: - if local_path and await aios.path.exists(local_path): - if DEBUG >= 2: - print(f"Resolving tokenizer for {repo_id=} from {local_path=}") - return await _resolve_tokenizer(local_path) - except Exception: - if DEBUG >= 5: - print( - f"Local check for {local_path=} failed. Resolving tokenizer for {repo_id=} normally..." - ) - if DEBUG >= 5: - traceback.print_exc() - return await _resolve_tokenizer(repo_id) - - -async def _resolve_tokenizer(repo_id_or_local_path: Union[str, PathLike]): - try: - if DEBUG >= 4: - print(f"Trying AutoProcessor for {repo_id_or_local_path}") - processor = AutoProcessor.from_pretrained( - repo_id_or_local_path, - use_fast=True if "Mistral-Large" in f"{repo_id_or_local_path}" else False, - trust_remote_code=True, - ) - if not hasattr(processor, "eos_token_id"): - processor.eos_token_id = getattr( - processor, "tokenizer", getattr(processor, "_tokenizer", processor) - ).eos_token_id - if not hasattr(processor, "encode"): - processor.encode = getattr( - processor, "tokenizer", getattr(processor, "_tokenizer", processor) - ).encode - if not hasattr(processor, "decode"): - processor.decode = getattr( - processor, "tokenizer", getattr(processor, "_tokenizer", processor) - ).decode - return processor - except Exception as e: - if DEBUG >= 4: - print(f"Failed to load processor for {repo_id_or_local_path}. Error: {e}") - if DEBUG >= 4: - print(traceback.format_exc()) - - try: - if DEBUG >= 4: - print(f"Trying AutoTokenizer for {repo_id_or_local_path}") - return AutoTokenizer.from_pretrained( - repo_id_or_local_path, trust_remote_code=True - ) - except Exception as e: - if DEBUG >= 4: - print( - f"Failed to load tokenizer for {repo_id_or_local_path}. Falling back to tinygrad tokenizer. Error: {e}" - ) - if DEBUG >= 4: - print(traceback.format_exc()) - - raise ValueError( - f"Unsupported model: {repo_id_or_local_path}. Install transformers: pip install transformers" - ) diff --git a/pkg/hanzo-network/src/hanzo_network/llm/__init__.py b/pkg/hanzo-network/src/hanzo_network/llm/__init__.py deleted file mode 100644 index 2fdcf307c..000000000 --- a/pkg/hanzo-network/src/hanzo_network/llm/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Local LLM support for Hanzo Network.""" - -from .local_llm import HanzoNetProvider, LocalLLMProvider, MLXProvider, OllamaProvider - -__all__ = [ - "LocalLLMProvider", - "HanzoNetProvider", - "OllamaProvider", - "MLXProvider", -] diff --git a/pkg/hanzo-network/src/hanzo_network/llm/local_llm.py b/pkg/hanzo-network/src/hanzo_network/llm/local_llm.py deleted file mode 100644 index 313c339bd..000000000 --- a/pkg/hanzo-network/src/hanzo_network/llm/local_llm.py +++ /dev/null @@ -1,264 +0,0 @@ -"""Local LLM providers using hanzo/net distributed inference.""" - -import asyncio -from abc import ABC, abstractmethod - -# Commenting out missing imports - need to implement these -# from .inference.inference_engine import get_inference_engine -# from .inference.shard import Shard -# from .download.shard_download import ShardDownloader -# Define Message locally for now -from typing import Any, Dict, List, Optional, TypedDict - - -class Message(TypedDict): - role: str - content: str - - -class LocalLLMProvider(ABC): - """Base class for local LLM providers using hanzo/net.""" - - @abstractmethod - async def generate( - self, - messages: List[Message], - model: str, - temperature: float = 0.7, - max_tokens: Optional[int] = None, - tools: Optional[List[Dict[str, Any]]] = None, - ) -> Dict[str, Any]: - """Generate a response from the LLM.""" - pass - - @abstractmethod - async def is_available(self) -> bool: - """Check if the LLM provider is available.""" - pass - - -class HanzoNetProvider(LocalLLMProvider): - """Hanzo/net distributed inference provider.""" - - def __init__(self, engine_type: str = "mlx", base_url: Optional[str] = None): - """Initialize with specified inference engine. - - Args: - engine_type: Type of inference engine ("mlx", "tinygrad", or "dummy") - base_url: Optional base URL (kept for compatibility) - """ - self.engine_type = engine_type - self.base_url = base_url # For compatibility - self.engine = None - self.current_shard = None - # self.shard_downloader = ShardDownloader() # Not implemented yet - self._lock = asyncio.Lock() - - async def _ensure_engine(self, model: str): - """Ensure inference engine is loaded.""" - async with self._lock: - # Dummy implementation for testing - if self.engine is None: - self.engine = {"type": self.engine_type, "model": model} - - # Update model if needed - if self.current_shard is None or self.current_shard != model: - self.current_shard = model - - async def generate( - self, - messages: List[Message], - model: str = "llama3.2", - temperature: float = 0.7, - max_tokens: Optional[int] = None, - tools: Optional[List[Dict[str, Any]]] = None, - ) -> Dict[str, Any]: - """Generate using hanzo/net distributed inference.""" - try: - await self._ensure_engine(model) - - # Convert messages to prompt - prompt = self._messages_to_prompt(messages, tools) - - # Use dummy engine when explicitly configured - if self.engine_type == "dummy": - # Generate a contextual response based on the prompt - response = self._generate_dummy_response(prompt, tools) - - return { - "output": [{"type": "text", "content": response}], - "tool_calls": [], - "usage": { - "input_tokens": len(prompt.split()), - "output_tokens": len(response.split()), - }, - } - - # Real inference path (when models are available) - request_id = f"req_{id(messages)}" - - # Encode prompt - tokens = await self.engine.encode(self.current_shard, prompt) - x = tokens.reshape(1, -1) - - # Generate tokens - output_tokens = [] - inference_state = None - - for i in range(max_tokens or 100): - # Run inference - output, inference_state = await self.engine.infer_tensor( - request_id, self.current_shard, x, inference_state - ) - - # Sample next token - next_token = await self.engine.sample(output, temp=temperature) - token_id = int(next_token[0]) - output_tokens.append(token_id) - - # Check for EOS - if token_id == 2: # Common EOS token - break - - # Prepare next input - x = next_token.reshape(1, 1) - - # Decode response - response = await self.engine.decode(self.current_shard, output_tokens) - - return { - "output": [{"type": "text", "content": response}], - "tool_calls": [], - "usage": { - "input_tokens": len(tokens), - "output_tokens": len(output_tokens), - }, - } - - except Exception as e: - raise RuntimeError(f"Inference failed for model {model}: {e}") from e - - async def is_available(self) -> bool: - """Check if the inference engine is available.""" - try: - if self.engine_type == "mlx": - import platform - - # Check if on Apple Silicon - return platform.system() == "Darwin" and platform.machine() in [ - "arm64", - "aarch64", - ] - elif self.engine_type == "tinygrad": - # Tinygrad works on most platforms - return True - else: # dummy - return True - except Exception: - return True # Dummy always available - - async def list_models(self) -> List[str]: - """List available models.""" - # Common models that hanzo/net supports - return [ - "llama3.2", - "llama-3.2-3b", - "deepseek-v2", - "deepseek-v3", - "stable-diffusion-2-1-base", - ] - - def _messages_to_prompt( - self, messages: List[Message], tools: Optional[List[Dict[str, Any]]] - ) -> str: - """Convert messages to a prompt string.""" - prompt = "" - - # Add tools if provided - if tools: - prompt += "Available tools:\n" - for tool in tools: - prompt += f"- {tool['name']}: {tool.get('description', '')}\n" - prompt += "\n" - - # Add messages - for msg in messages: - # Handle both Message objects and dicts - if hasattr(msg, "role"): - role = msg.role - content = msg.content - else: - role = msg.get("role", "user") - content = msg.get("content", "") - - if role == "system": - prompt += f"System: {content}\n\n" - elif role == "user": - prompt += f"User: {content}\n\n" - elif role == "assistant": - prompt += f"Assistant: {content}\n\n" - - prompt += "Assistant: " - return prompt - - def _generate_dummy_response( - self, prompt: str, tools: Optional[List[Dict[str, Any]]] - ) -> str: - """Generate a contextual dummy response based on prompt.""" - prompt_lower = prompt.lower() - - # Check for tool-related prompts - if tools: - for tool in tools: - tool_name = tool["name"] - if ( - tool_name in prompt_lower - or tool["description"].lower() in prompt_lower - ): - # Generate a response that calls the tool - if "search" in tool_name: - return f"I'll search for that information using the {tool_name} tool.\n\n{tool_name}('authentication')" - elif "analyze" in tool_name: - return f"Let me analyze that using the {tool_name} tool.\n\n{tool_name}('add')" - elif "generate" in tool_name: - return f"I'll generate that for you using the {tool_name} tool.\n\n{tool_name}('add function')" - elif "explain" in tool_name: - return f"Let me explain that concept using the {tool_name} tool.\n\n{tool_name}('recursion')" - - # Default contextual responses - if "search" in prompt_lower: - return "Based on my search through the codebase, I found several relevant functions related to your query." - elif "analyze" in prompt_lower: - return "After analyzing the code, I can see it follows good practices with clear structure and efficient implementation." - elif "generate" in prompt_lower or "test" in prompt_lower: - return "I've generated the requested code/tests following best practices and ensuring comprehensive coverage." - elif "explain" in prompt_lower or "what is" in prompt_lower: - return "Let me explain that concept: It's a fundamental programming technique that involves a function calling itself to solve smaller instances of the same problem." - else: - return f"Processing your request using hanzo/net distributed inference. (Available tools: {len(tools) if tools else 0})" - - -# Backward compatibility aliases -OllamaProvider = HanzoNetProvider # Ollama replaced with hanzo/net - - -def MLXProvider(): - return HanzoNetProvider("mlx") # MLX uses hanzo/net MLX engine - - -# Factory function -def create_local_llm_provider(provider_type: str = "hanzo") -> LocalLLMProvider: - """Create a local LLM provider. - - Args: - provider_type: Provider type ("hanzo", "ollama", "mlx") - All map to HanzoNetProvider with appropriate engine - """ - if provider_type in ["hanzo", "ollama"]: - # Default to dummy engine for testing - return HanzoNetProvider("dummy") - elif provider_type == "mlx": - return HanzoNetProvider("mlx") - else: - # Default to hanzo/net - return HanzoNetProvider("dummy") diff --git a/pkg/hanzo-network/src/hanzo_network/local_compute.py b/pkg/hanzo-network/src/hanzo_network/local_compute.py deleted file mode 100644 index c84617fd8..000000000 --- a/pkg/hanzo-network/src/hanzo_network/local_compute.py +++ /dev/null @@ -1,584 +0,0 @@ -"""Local AI compute powered by hanzo.network. - -This module provides local AI inference capabilities with support for -various models and hardware acceleration, integrated with the Hanzo network -for decentralized coordination and payments. -""" - -import asyncio -import hashlib -import json -import time -from dataclasses import dataclass, field -from enum import Enum -from typing import Any, Dict, List, Optional - -# Try to import ML dependencies -try: - import torch - import transformers # noqa: F401 - from transformers import AutoModelForCausalLM, AutoTokenizer - - TORCH_AVAILABLE = True -except ImportError: - TORCH_AVAILABLE = False - -try: - import numpy as np # noqa: F401 - - NUMPY_AVAILABLE = True -except ImportError: - NUMPY_AVAILABLE = False - - -class ModelProvider(Enum): - """Supported model providers for local execution.""" - - HUGGINGFACE = "huggingface" - LLAMA_CPP = "llama_cpp" - ONNX = "onnx" - CUSTOM = "custom" - - -@dataclass -class ModelConfig: - """Configuration for a local model.""" - - name: str - provider: ModelProvider - model_path: str # Local path or HF model ID - device: str = "cpu" # cpu, cuda, mps - quantization: Optional[str] = None # int8, int4, etc. - max_length: int = 2048 - temperature: float = 0.7 - - # Resource requirements - min_ram_gb: float = 4.0 - min_vram_gb: float = 0.0 # For GPU models - estimated_tokens_per_second: float = 10.0 - - # Pricing - price_per_1k_tokens: float = 0.0001 # In ETH - - -@dataclass -class InferenceRequest: - """Request for local inference.""" - - request_id: str - prompt: str - max_tokens: int = 256 - temperature: float = 0.7 - top_p: float = 0.9 - stop_sequences: List[str] = field(default_factory=list) - - # Economic parameters - max_price_eth: float = 0.001 - requester_address: Optional[str] = None - - # Security - require_attestation: bool = False - timeout_seconds: int = 60 - - -@dataclass -class InferenceResult: - """Result from local inference.""" - - request_id: str - text: str - tokens_generated: int - time_seconds: float - model_name: str - - # Pricing - cost_eth: float = 0.0 - - # Attestation (if requested) - attestation: Optional[Dict[str, Any]] = None - - -class LocalComputeNode: - """Node that provides local AI compute resources.""" - - def __init__( - self, - node_id: str, - wallet_address: Optional[str] = None, - models: Optional[List[ModelConfig]] = None, - ): - """Initialize local compute node. - - Args: - node_id: Unique node identifier - wallet_address: Ethereum address for payments - models: List of available models - """ - self.node_id = node_id - self.wallet_address = ( - wallet_address or f"0x{hashlib.sha256(node_id.encode()).hexdigest()[:40]}" - ) - self.models: Dict[str, ModelConfig] = {} - self.loaded_models: Dict[str, Any] = {} - - # Add default models if none provided - if not models: - models = self._get_default_models() - - for model in models: - self.models[model.name] = model - - # Performance tracking - self.total_requests = 0 - self.total_tokens = 0 - self.total_earnings = 0.0 - - # Resource monitoring - self.cpu_usage = 0.0 - self.memory_usage = 0.0 - self.gpu_usage = 0.0 - - def _get_default_models(self) -> List[ModelConfig]: - """Get default model configurations.""" - models = [] - - # Small model for CPU - models.append( - ModelConfig( - name="hanzo-nano", - provider=ModelProvider.HUGGINGFACE, - model_path="microsoft/phi-2", # 2.7B model - device="cpu", - min_ram_gb=8.0, - estimated_tokens_per_second=20.0, - price_per_1k_tokens=0.00001, - ) - ) - - # Medium model for GPU - if torch.cuda.is_available(): - models.append( - ModelConfig( - name="hanzo-base", - provider=ModelProvider.HUGGINGFACE, - model_path="mistralai/Mistral-7B-v0.1", - device="cuda", - min_ram_gb=16.0, - min_vram_gb=8.0, - estimated_tokens_per_second=50.0, - price_per_1k_tokens=0.00005, - ) - ) - - return models - - def list_models(self) -> List[Dict[str, Any]]: - """List available models and their capabilities.""" - model_list = [] - - for name, config in self.models.items(): - # Check if model can run on current hardware - available = self._check_resources(config) - - model_list.append( - { - "name": name, - "provider": config.provider.value, - "device": config.device, - "available": available, - "price_per_1k_tokens": config.price_per_1k_tokens, - "estimated_tps": config.estimated_tokens_per_second, - "min_ram_gb": config.min_ram_gb, - "min_vram_gb": config.min_vram_gb, - } - ) - - return model_list - - def _check_resources(self, config: ModelConfig) -> bool: - """Check if system has resources for model.""" - # Simple check - in production would be more sophisticated - if not TORCH_AVAILABLE: - return False - - # Check RAM - try: - import psutil - - available_ram_gb = psutil.virtual_memory().available / (1024**3) - if available_ram_gb < config.min_ram_gb: - return False - except Exception: - pass - - # Check GPU if needed - if config.device == "cuda" and config.min_vram_gb > 0: - if not torch.cuda.is_available(): - return False - - # Check VRAM - try: - vram_gb = torch.cuda.get_device_properties(0).total_memory / (1024**3) - if vram_gb < config.min_vram_gb: - return False - except Exception: - return False - - return True - - def load_model(self, model_name: str) -> bool: - """Load a model into memory. - - Args: - model_name: Name of model to load - - Returns: - True if successful - """ - if model_name in self.loaded_models: - return True - - if model_name not in self.models: - print(f"Unknown model: {model_name}") - return False - - config = self.models[model_name] - - if not self._check_resources(config): - print(f"Insufficient resources for {model_name}") - return False - - try: - if config.provider == ModelProvider.HUGGINGFACE: - # Load HuggingFace model - tokenizer = AutoTokenizer.from_pretrained(config.model_path) - model = AutoModelForCausalLM.from_pretrained( - config.model_path, - torch_dtype=( - torch.float16 if config.device != "cpu" else torch.float32 - ), - device_map="auto" if config.device == "cuda" else None, - ) - - if config.device == "cuda": - model = model.cuda() - - self.loaded_models[model_name] = { - "model": model, - "tokenizer": tokenizer, - "config": config, - } - - print(f"Loaded model: {model_name}") - return True - - else: - print(f"Provider {config.provider} not implemented") - return False - - except Exception as e: - print(f"Failed to load {model_name}: {e}") - return False - - async def process_request(self, request: InferenceRequest) -> InferenceResult: - """Process an inference request. - - Args: - request: Inference request - - Returns: - Inference result - """ - start_time = time.time() - - # Select best available model within price range - selected_model = None - for name, config in self.models.items(): - cost_estimate = (request.max_tokens / 1000) * config.price_per_1k_tokens - if cost_estimate <= request.max_price_eth: - if self._check_resources(config): - selected_model = name - break - - if not selected_model: - return InferenceResult( - request_id=request.request_id, - text="Error: No suitable model available within price range", - tokens_generated=0, - time_seconds=0, - model_name="none", - ) - - # Load model if needed - if not self.load_model(selected_model): - return InferenceResult( - request_id=request.request_id, - text="Error: Failed to load model", - tokens_generated=0, - time_seconds=0, - model_name=selected_model, - ) - - # Run inference - try: - result_text = await self._run_inference( - selected_model, - request.prompt, - request.max_tokens, - request.temperature, - request.top_p, - request.stop_sequences, - ) - - # Calculate cost - tokens_generated = len(result_text.split()) # Approximate - cost = (tokens_generated / 1000) * self.models[ - selected_model - ].price_per_1k_tokens - - # Update statistics - self.total_requests += 1 - self.total_tokens += tokens_generated - self.total_earnings += cost - - # Create result - result = InferenceResult( - request_id=request.request_id, - text=result_text, - tokens_generated=tokens_generated, - time_seconds=time.time() - start_time, - model_name=selected_model, - cost_eth=cost, - ) - - # Add attestation if requested - if request.require_attestation: - result.attestation = self._create_attestation(request, result) - - return result - - except Exception as e: - return InferenceResult( - request_id=request.request_id, - text=f"Error during inference: {str(e)}", - tokens_generated=0, - time_seconds=time.time() - start_time, - model_name=selected_model, - ) - - async def _run_inference( - self, - model_name: str, - prompt: str, - max_tokens: int, - temperature: float, - top_p: float, - stop_sequences: List[str], - ) -> str: - """Run inference with a loaded model.""" - if not TORCH_AVAILABLE: - # Fallback to mock inference - await asyncio.sleep(0.1) # Simulate processing - return f"Mock response to: {prompt[:50]}..." - - model_data = self.loaded_models[model_name] - model = model_data["model"] - tokenizer = model_data["tokenizer"] - config = model_data["config"] - - # Tokenize input - inputs = tokenizer(prompt, return_tensors="pt") - if config.device == "cuda": - inputs = {k: v.cuda() for k, v in inputs.items()} - - # Generate - with torch.no_grad(): - outputs = model.generate( - **inputs, - max_new_tokens=max_tokens, - temperature=temperature, - top_p=top_p, - do_sample=True, - pad_token_id=tokenizer.eos_token_id, - ) - - # Decode - response = tokenizer.decode(outputs[0], skip_special_tokens=True) - - # Remove prompt from response - if response.startswith(prompt): - response = response[len(prompt) :].strip() - - return response - - def _create_attestation( - self, request: InferenceRequest, result: InferenceResult - ) -> Dict[str, Any]: - """Create attestation for inference result.""" - # In production, this would use TEE attestation - attestation_data = { - "node_id": self.node_id, - "request_id": request.request_id, - "model_name": result.model_name, - "prompt_hash": hashlib.sha256(request.prompt.encode()).hexdigest(), - "result_hash": hashlib.sha256(result.text.encode()).hexdigest(), - "timestamp": time.time(), - } - - # Create signature (mock) - signature_data = json.dumps(attestation_data, sort_keys=True) - signature = hashlib.sha256(signature_data.encode()).hexdigest() - - return { - "data": attestation_data, - "signature": signature, - "provider": "mock_tee", - } - - def get_stats(self) -> Dict[str, Any]: - """Get node statistics.""" - return { - "node_id": self.node_id, - "wallet_address": self.wallet_address, - "models_available": len(self.models), - "models_loaded": len(self.loaded_models), - "total_requests": self.total_requests, - "total_tokens": self.total_tokens, - "total_earnings_eth": self.total_earnings, - "avg_tokens_per_request": self.total_tokens / max(1, self.total_requests), - "resource_usage": { - "cpu_percent": self.cpu_usage, - "memory_percent": self.memory_usage, - "gpu_percent": self.gpu_usage, - }, - } - - -class LocalComputeOrchestrator: - """Orchestrates multiple local compute nodes.""" - - def __init__(self): - """Initialize orchestrator.""" - self.nodes: Dict[str, LocalComputeNode] = {} - self.pending_requests: List[InferenceRequest] = [] - self.completed_requests: Dict[str, InferenceResult] = {} - - def register_node(self, node: LocalComputeNode): - """Register a compute node.""" - self.nodes[node.node_id] = node - print(f"Registered node: {node.node_id}") - - async def submit_request(self, request: InferenceRequest) -> str: - """Submit an inference request. - - Args: - request: Inference request - - Returns: - Request ID for tracking - """ - # Find suitable node - best_node = None - best_price = float("inf") - - for node in self.nodes.values(): - for model in node.list_models(): - if model["available"]: - estimated_cost = (request.max_tokens / 1000) * model[ - "price_per_1k_tokens" - ] - if ( - estimated_cost <= request.max_price_eth - and estimated_cost < best_price - ): - best_node = node - best_price = estimated_cost - - if not best_node: - # Queue request - self.pending_requests.append(request) - return f"Request {request.request_id} queued (no available nodes)" - - # Process immediately - result = await best_node.process_request(request) - self.completed_requests[request.request_id] = result - - return f"Request {request.request_id} completed by {best_node.node_id}" - - def get_result(self, request_id: str) -> Optional[InferenceResult]: - """Get result for a request.""" - return self.completed_requests.get(request_id) - - def get_network_stats(self) -> Dict[str, Any]: - """Get network-wide statistics.""" - total_models = sum(len(node.models) for node in self.nodes.values()) - total_requests = sum(node.total_requests for node in self.nodes.values()) - total_earnings = sum(node.total_earnings for node in self.nodes.values()) - - return { - "nodes": len(self.nodes), - "total_models": total_models, - "pending_requests": len(self.pending_requests), - "completed_requests": len(self.completed_requests), - "total_requests_processed": total_requests, - "total_earnings_eth": total_earnings, - "nodes_detail": { - node_id: node.get_stats() for node_id, node in self.nodes.items() - }, - } - - -# Global orchestrator instance -orchestrator = LocalComputeOrchestrator() - - -# Example usage function -async def demo_local_compute(): - """Demonstrate local compute capabilities.""" - # Create a compute node - node1 = LocalComputeNode( - node_id="node_001", wallet_address="0x1234567890123456789012345678901234567890" - ) - - # Register with orchestrator - orchestrator.register_node(node1) - - # List available models - print("\nAvailable models:") - for model in node1.list_models(): - print( - f" - {model['name']}: {model['device']}, ${model['price_per_1k_tokens']}/1k tokens" - ) - - # Create inference request - request = InferenceRequest( - request_id="req_001", - prompt="What is the capital of France?", - max_tokens=50, - max_price_eth=0.001, - ) - - # Submit request - status = await orchestrator.submit_request(request) - print(f"\nRequest status: {status}") - - # Get result - result = orchestrator.get_result("req_001") - if result: - print("\nResult:") - print(f" Model: {result.model_name}") - print(f" Response: {result.text}") - print(f" Cost: {result.cost_eth:.6f} ETH") - print(f" Time: {result.time_seconds:.2f}s") - - # Show stats - print("\nNetwork stats:") - stats = orchestrator.get_network_stats() - print(f" Nodes: {stats['nodes']}") - print(f" Total models: {stats['total_models']}") - print(f" Total earnings: {stats['total_earnings_eth']:.6f} ETH") - - -if __name__ == "__main__": - # Run demo - asyncio.run(demo_local_compute()) diff --git a/pkg/hanzo-network/src/hanzo_network/local_network.py b/pkg/hanzo-network/src/hanzo_network/local_network.py deleted file mode 100644 index 87056254b..000000000 --- a/pkg/hanzo-network/src/hanzo_network/local_network.py +++ /dev/null @@ -1,152 +0,0 @@ -"""Helper functions for creating local networks with local LLMs.""" - -from typing import List, Optional - -from .core.agent import Agent, ModelConfig, ModelProvider, create_agent -from .core.tool import Tool -from .distributed_network import DistributedNetwork, create_distributed_network - - -def create_local_agent( - name: str, - description: str, - system: Optional[str] = None, - tools: Optional[List[Tool]] = None, - local_model: str = "llama3.2", - base_url: str = "http://localhost:11434", - **metadata, -) -> Agent: - """Create an agent configured to use a local LLM. - - Args: - name: Agent name - description: Agent description - system: System prompt - tools: List of tools - local_model: Local model name (e.g., "llama3.2" for Ollama, "mlx-community/Llama-3.2-3B-Instruct-4bit" for MLX) - base_url: Base URL for local LLM server (Ollama default) - **metadata: Additional metadata - - Returns: - Agent configured for local LLM - """ - # Create model config for local provider - model_config = ModelConfig( - provider=ModelProvider.LOCAL, model=local_model, base_url=base_url - ) - - return create_agent( - name=name, - description=description, - model=model_config, - system=system, - tools=tools, - **metadata, - ) - - -def create_local_distributed_network( - agents: List[Agent], - name: Optional[str] = None, - node_id: Optional[str] = None, - listen_port: int = 5678, - broadcast_port: int = 5678, - local_model: str = "llama3.2", - base_url: str = "http://localhost:11434", - **kwargs, -) -> DistributedNetwork: - """Create a distributed network configured for local execution. - - This is a convenience wrapper that creates a distributed network - with sensible defaults for local testing with local LLMs. - - Args: - agents: List of agents - name: Network name - node_id: Node identifier - listen_port: Port to listen on - broadcast_port: UDP broadcast port - local_model: Local model for router - base_url: Base URL for local LLM - **kwargs: Additional arguments for create_distributed_network - - Returns: - Configured DistributedNetwork - """ - from .core.router import create_routing_agent - - # Create a local router if one isn't provided - if "router" not in kwargs: - router_config = ModelConfig( - provider=ModelProvider.LOCAL, model=local_model, base_url=base_url - ) - - kwargs["router"] = create_routing_agent( - name="local_router", description="Local routing agent", model=router_config - ) - - return create_distributed_network( - agents=agents, - name=name or "local-network", - node_id=node_id, - listen_port=listen_port, - broadcast_port=broadcast_port, - **kwargs, - ) - - -async def check_local_llm_status(provider: str = "hanzo") -> dict: - """Check the status of local LLM providers. - - Args: - provider: Provider to check ("hanzo", "mlx", "tinygrad", "dummy") - - Returns: - Status information including availability and models - """ - from .llm import HanzoNetProvider - - # Map old provider names to hanzo/net engines - engine_map = { - "ollama": "dummy", # Ollama replaced with hanzo/net - "mlx": "mlx", - "tinygrad": "tinygrad", - "dummy": "dummy", - "hanzo": "dummy", # Default hanzo/net - } - - engine_type = engine_map.get(provider, "dummy") - - hanzo_provider = HanzoNetProvider(engine_type) - is_available = await hanzo_provider.is_available() - models = await hanzo_provider.list_models() if is_available else [] - - # Provide helpful status info - status = { - "provider": f"hanzo/net ({engine_type})", - "available": is_available, - "engine": engine_type, - "models": models, - } - - # Add engine-specific info - if engine_type == "mlx": - import platform - - status["platform"] = ( - "Apple Silicon" - if platform.machine() in ["arm64", "aarch64"] - else platform.machine() - ) - if not is_available: - status["instructions"] = "MLX requires Apple Silicon (M1/M2/M3)" - elif engine_type == "tinygrad": - status["instructions"] = ( - "Tinygrad engine ready for distributed inference" - if is_available - else "Install tinygrad" - ) - else: # dummy - status["instructions"] = "Using mock responses for testing" - - return status diff --git a/pkg/hanzo-network/src/hanzo_network/tools/__init__.py b/pkg/hanzo-network/src/hanzo_network/tools/__init__.py deleted file mode 100644 index 0e17c0d74..000000000 --- a/pkg/hanzo-network/src/hanzo_network/tools/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Built-in tools for Hanzo Network.""" diff --git a/pkg/hanzo-network/src/hanzo_network/tools/memory.py b/pkg/hanzo-network/src/hanzo_network/tools/memory.py deleted file mode 100644 index b9d5d97f5..000000000 --- a/pkg/hanzo-network/src/hanzo_network/tools/memory.py +++ /dev/null @@ -1,234 +0,0 @@ -"""Memory tools for agents using the hanzo-memory package.""" - -from dataclasses import dataclass -from typing import Any, Dict, List, Optional - -from .core.tool import Tool, ToolContext, create_tool - -# Import from hanzo-memory package when available -try: - from hanzo_memory import MemoryStore, VectorStore - - MEMORY_AVAILABLE = True -except ImportError: - MEMORY_AVAILABLE = False - MemoryStore = None - VectorStore = None - - -@dataclass -class MemoryManager: - """Manager for agent memory operations.""" - - def __init__(self, store_type: str = "vector", **kwargs): - """Initialize memory manager. - - Args: - store_type: Type of store (vector, graph, etc.) - **kwargs: Store-specific configuration - """ - if not MEMORY_AVAILABLE: - raise ImportError("hanzo-memory package not available") - - if store_type == "vector": - self.store = VectorStore(**kwargs) - else: - self.store = MemoryStore(**kwargs) - - async def recall(self, queries: List[str], limit: int = 10) -> List[Dict[str, Any]]: - """Recall memories matching queries. - - Args: - queries: Search queries - limit: Max results per query - - Returns: - List of matching memories - """ - results = [] - - for query in queries: - matches = await self.store.search(query, limit=limit) - results.extend(matches) - - # Deduplicate by ID - seen = set() - unique_results = [] - for result in results: - if result.get("id") not in seen: - seen.add(result.get("id")) - unique_results.append(result) - - return unique_results - - async def create(self, statements: List[str]) -> List[str]: - """Create new memories. - - Args: - statements: Memory statements to store - - Returns: - List of created memory IDs - """ - ids = [] - - for statement in statements: - memory_id = await self.store.add( - {"content": statement, "type": "statement"} - ) - ids.append(memory_id) - - return ids - - async def update(self, updates: List[Dict[str, str]]) -> List[bool]: - """Update existing memories. - - Args: - updates: List of {id, statement} dicts - - Returns: - List of success flags - """ - results = [] - - for update in updates: - success = await self.store.update( - update["id"], {"content": update["statement"]} - ) - results.append(success) - - return results - - async def delete(self, ids: List[str]) -> List[bool]: - """Delete memories by ID. - - Args: - ids: Memory IDs to delete - - Returns: - List of success flags - """ - results = [] - - for memory_id in ids: - success = await self.store.delete(memory_id) - results.append(success) - - return results - - -# Create memory tools - - -def create_memory_tools(memory_manager: Optional[MemoryManager] = None) -> List[Tool]: - """Create standard memory tools for agents. - - Args: - memory_manager: Optional shared memory manager - - Returns: - List of memory tools - """ - if not memory_manager: - memory_manager = MemoryManager() - - tools = [] - - # Recall memories tool - @create_tool( - name="recall_memories", - description="Recall memories relevant to one or more queries. Can run multiple queries in parallel.", - ) - async def recall_memories_tool( - queries: List[str], limit: int = 10, context: ToolContext = None - ) -> str: - """Recall relevant memories.""" - memories = await memory_manager.recall(queries, limit) - - if not memories: - return "No relevant memories found." - - # Format memories - formatted = [] - for mem in memories: - formatted.append(f"- {mem.get('content', 'Unknown')}") - - return f"Found {len(memories)} relevant memories:\n" + "\n".join(formatted) - - tools.append(recall_memories_tool) - - # Create memories tool - @create_tool( - name="create_memories", - description="Save one or more new pieces of information to memory.", - ) - async def create_memories_tool( - statements: List[str], context: ToolContext = None - ) -> str: - """Create new memories.""" - ids = await memory_manager.create(statements) - return f"Created {len(ids)} new memories." - - tools.append(create_memories_tool) - - # Update memories tool - @create_tool( - name="update_memories", - description="Update existing memories with corrected information.", - ) - async def update_memories_tool( - updates: List[Dict[str, str]], context: ToolContext = None - ) -> str: - """Update memories.""" - results = await memory_manager.update(updates) - success_count = sum(results) - return f"Updated {success_count} of {len(updates)} memories." - - tools.append(update_memories_tool) - - # Delete memories tool - @create_tool( - name="delete_memories", - description="Delete memories that are no longer relevant or incorrect.", - ) - async def delete_memories_tool(ids: List[str], context: ToolContext = None) -> str: - """Delete memories.""" - results = await memory_manager.delete(ids) - success_count = sum(results) - return f"Deleted {success_count} of {len(ids)} memories." - - tools.append(delete_memories_tool) - - # Consolidated manage memories tool - @create_tool( - name="manage_memories", - description="Create, update, and/or delete memories in a single atomic operation. This is the preferred way to modify memories.", - ) - async def manage_memories_tool( - creations: Optional[List[str]] = None, - updates: Optional[List[Dict[str, str]]] = None, - deletions: Optional[List[str]] = None, - context: ToolContext = None, - ) -> str: - """Manage memories atomically.""" - results = [] - - if creations: - ids = await memory_manager.create(creations) - results.append(f"Created {len(ids)} memories") - - if updates: - update_results = await memory_manager.update(updates) - success_count = sum(update_results) - results.append(f"Updated {success_count} memories") - - if deletions: - delete_results = await memory_manager.delete(deletions) - success_count = sum(delete_results) - results.append(f"Deleted {success_count} memories") - - return "Memory operations completed: " + ", ".join(results) - - tools.append(manage_memories_tool) - - return tools diff --git a/pkg/hanzo-network/src/hanzo_network/topology/__init__.py b/pkg/hanzo-network/src/hanzo_network/topology/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-network/src/hanzo_network/topology/device_capabilities.py b/pkg/hanzo-network/src/hanzo_network/topology/device_capabilities.py deleted file mode 100644 index 9f786a325..000000000 --- a/pkg/hanzo-network/src/hanzo_network/topology/device_capabilities.py +++ /dev/null @@ -1,309 +0,0 @@ -"""Device capabilities for distributed Hanzo networks.""" - -import platform -from typing import Any - -import psutil -from pydantic import BaseModel - -DEBUG = 0 # Default debug level -TFLOPS = 1.00 - - -class DeviceFlops(BaseModel): - """Device floating-point operations per second.""" - - # units of TFLOPS - fp32: float - fp16: float - int8: float - - def __str__(self): - return f"fp32: {self.fp32 / TFLOPS:.2f} TFLOPS, fp16: {self.fp16 / TFLOPS:.2f} TFLOPS, int8: {self.int8 / TFLOPS:.2f} TFLOPS" - - def to_dict(self): - return self.model_dump() - - -class DeviceCapabilities(BaseModel): - """Device capabilities information.""" - - model: str - chip: str - memory: int # MB - flops: DeviceFlops - - def __str__(self): - return f"Model: {self.model}. Chip: {self.chip}. Memory: {self.memory}MB. Flops: {self.flops}" - - def model_post_init(self, __context: Any) -> None: - if isinstance(self.flops, dict): - self.flops = DeviceFlops(**self.flops) - - def to_dict(self): - return { - "model": self.model, - "chip": self.chip, - "memory": self.memory, - "flops": self.flops.to_dict(), - } - - -UNKNOWN_DEVICE_CAPABILITIES = DeviceCapabilities( - model="Unknown Model", - chip="Unknown Chip", - memory=0, - flops=DeviceFlops(fp32=0, fp16=0, int8=0), -) - -# Common chip performance data -CHIP_FLOPS = { - # Apple M series - "Apple M1": DeviceFlops(fp32=2.29 * TFLOPS, fp16=4.58 * TFLOPS, int8=9.16 * TFLOPS), - "Apple M1 Pro": DeviceFlops( - fp32=5.30 * TFLOPS, fp16=10.60 * TFLOPS, int8=21.20 * TFLOPS - ), - "Apple M1 Max": DeviceFlops( - fp32=10.60 * TFLOPS, fp16=21.20 * TFLOPS, int8=42.40 * TFLOPS - ), - "Apple M2": DeviceFlops( - fp32=3.55 * TFLOPS, fp16=7.10 * TFLOPS, int8=14.20 * TFLOPS - ), - "Apple M2 Pro": DeviceFlops( - fp32=5.68 * TFLOPS, fp16=11.36 * TFLOPS, int8=22.72 * TFLOPS - ), - "Apple M2 Max": DeviceFlops( - fp32=13.49 * TFLOPS, fp16=26.98 * TFLOPS, int8=53.96 * TFLOPS - ), - "Apple M3": DeviceFlops( - fp32=3.55 * TFLOPS, fp16=7.10 * TFLOPS, int8=14.20 * TFLOPS - ), - "Apple M3 Pro": DeviceFlops( - fp32=4.97 * TFLOPS, fp16=9.94 * TFLOPS, int8=19.88 * TFLOPS - ), - "Apple M3 Max": DeviceFlops( - fp32=14.20 * TFLOPS, fp16=28.40 * TFLOPS, int8=56.80 * TFLOPS - ), - "Apple M4": DeviceFlops( - fp32=4.26 * TFLOPS, fp16=8.52 * TFLOPS, int8=17.04 * TFLOPS - ), - # NVIDIA GPUs - "NVIDIA GEFORCE RTX 4090": DeviceFlops( - fp32=82.58 * TFLOPS, fp16=165.16 * TFLOPS, int8=330.32 * TFLOPS - ), - "NVIDIA GEFORCE RTX 4080": DeviceFlops( - fp32=48.74 * TFLOPS, fp16=97.48 * TFLOPS, int8=194.96 * TFLOPS - ), - "NVIDIA GEFORCE RTX 4070": DeviceFlops( - fp32=29.0 * TFLOPS, fp16=58.0 * TFLOPS, int8=116.0 * TFLOPS - ), - "NVIDIA GEFORCE RTX 3090": DeviceFlops( - fp32=35.6 * TFLOPS, fp16=71.2 * TFLOPS, int8=142.4 * TFLOPS - ), - "NVIDIA GEFORCE RTX 3080": DeviceFlops( - fp32=29.8 * TFLOPS, fp16=59.6 * TFLOPS, int8=119.2 * TFLOPS - ), - # Add more as needed -} - - -def device_capabilities() -> DeviceCapabilities: - """Get current device capabilities.""" - system = platform.system() - - if system == "Darwin": # macOS - return mac_device_capabilities() - elif system == "Linux": - return linux_device_capabilities() - elif system == "Windows": - return windows_device_capabilities() - else: - return DeviceCapabilities( - model="Unknown Device", - chip="Unknown Chip", - memory=psutil.virtual_memory().total // 2**20, - flops=DeviceFlops(fp32=0, fp16=0, int8=0), - ) - - -def mac_device_capabilities() -> DeviceCapabilities: - """Get macOS device capabilities.""" - try: - import subprocess - - # Get model info - model_result = subprocess.run( - ["system_profiler", "SPHardwareDataType"], capture_output=True, text=True - ) - - model = "Mac" - chip = "Unknown" - - if model_result.returncode == 0: - output = model_result.stdout - # Parse model name - for line in output.split("\n"): - if "Model Name:" in line: - model = line.split("Model Name:")[-1].strip() - elif "Chip:" in line: - chip = line.split("Chip:")[-1].strip() - elif "System Chip:" in line: - chip = line.split("System Chip:")[-1].strip() - - # Get memory - memory = psutil.virtual_memory().total // 2**20 - - # Get FLOPS for the chip - flops = CHIP_FLOPS.get(chip, DeviceFlops(fp32=0, fp16=0, int8=0)) - - return DeviceCapabilities(model=model, chip=chip, memory=memory, flops=flops) - - except Exception as e: - if DEBUG >= 1: - print(f"Error getting Mac device capabilities: {e}") - return DeviceCapabilities( - model="Mac", - chip="Unknown", - memory=psutil.virtual_memory().total // 2**20, - flops=DeviceFlops(fp32=0, fp16=0, int8=0), - ) - - -def linux_device_capabilities() -> DeviceCapabilities: - """Get Linux device capabilities.""" - try: - import subprocess - - # Try to detect NVIDIA GPU using nvidia-smi - try: - result = subprocess.run( - [ - "nvidia-smi", - "--query-gpu=name,memory.total", - "--format=csv,noheader", - ], - capture_output=True, - text=True, - timeout=5, - ) - - if result.returncode == 0 and result.stdout.strip(): - gpu_info = result.stdout.strip().split(",") - gpu_name = gpu_info[0].strip() - gpu_memory = int(gpu_info[1].strip().split()[0]) # Memory in MiB - - # Lookup FLOPS from known GPUs - flops = CHIP_FLOPS.get( - gpu_name.upper(), DeviceFlops(fp32=0, fp16=0, int8=0) - ) - - return DeviceCapabilities( - model="Linux Box with GPU", - chip=gpu_name, - memory=gpu_memory, - flops=flops, - ) - except (subprocess.SubprocessError, FileNotFoundError, IndexError): - pass - - # Try to detect AMD GPU using rocm-smi - try: - result = subprocess.run( - ["rocm-smi", "--showproductname"], - capture_output=True, - text=True, - timeout=5, - ) - - if result.returncode == 0 and result.stdout.strip(): - # Parse AMD GPU info - for line in result.stdout.split("\n"): - if "GPU" in line: - gpu_name = ( - line.split(":")[-1].strip() if ":" in line else "AMD GPU" - ) - memory = psutil.virtual_memory().total // 2**20 - - return DeviceCapabilities( - model="Linux Box with AMD GPU", - chip=gpu_name, - memory=memory, - flops=DeviceFlops(fp32=0, fp16=0, int8=0), - ) - except (subprocess.SubprocessError, FileNotFoundError): - pass - - # Fallback to CPU - return DeviceCapabilities( - model="Linux Box", - chip="CPU", - memory=psutil.virtual_memory().total // 2**20, - flops=DeviceFlops(fp32=0, fp16=0, int8=0), - ) - - except Exception as e: - if DEBUG >= 1: - print(f"Error getting Linux device capabilities: {e}") - return DeviceCapabilities( - model="Linux Box", - chip="CPU", - memory=psutil.virtual_memory().total // 2**20, - flops=DeviceFlops(fp32=0, fp16=0, int8=0), - ) - - -def windows_device_capabilities() -> DeviceCapabilities: - """Get Windows device capabilities.""" - try: - import subprocess - - # Try to detect NVIDIA GPU using nvidia-smi - try: - result = subprocess.run( - [ - "nvidia-smi", - "--query-gpu=name,memory.total", - "--format=csv,noheader", - ], - capture_output=True, - text=True, - timeout=5, - shell=True, # Windows may need shell - ) - - if result.returncode == 0 and result.stdout.strip(): - gpu_info = result.stdout.strip().split(",") - gpu_name = gpu_info[0].strip() - gpu_memory = int(gpu_info[1].strip().split()[0]) # Memory in MiB - - # Lookup FLOPS from known GPUs - flops = CHIP_FLOPS.get( - gpu_name.upper(), DeviceFlops(fp32=0, fp16=0, int8=0) - ) - - return DeviceCapabilities( - model="Windows Box with GPU", - chip=gpu_name, - memory=gpu_memory, - flops=flops, - ) - except (subprocess.SubprocessError, FileNotFoundError, IndexError): - pass - - # Fallback to CPU - return DeviceCapabilities( - model="Windows Box", - chip="CPU", - memory=psutil.virtual_memory().total // 2**20, - flops=DeviceFlops(fp32=0, fp16=0, int8=0), - ) - - except Exception as e: - if DEBUG >= 1: - print(f"Error getting Windows device capabilities: {e}") - return DeviceCapabilities( - model="Windows Box", - chip="CPU", - memory=psutil.virtual_memory().total // 2**20, - flops=DeviceFlops(fp32=0, fp16=0, int8=0), - ) diff --git a/pkg/hanzo-network/src/hanzo_network/topology/partitioning_strategy.py b/pkg/hanzo-network/src/hanzo_network/topology/partitioning_strategy.py deleted file mode 100644 index eed1825d2..000000000 --- a/pkg/hanzo-network/src/hanzo_network/topology/partitioning_strategy.py +++ /dev/null @@ -1,43 +0,0 @@ -from abc import ABC, abstractmethod -from dataclasses import dataclass -from typing import List - -from .inference.shard import Shard -from .topology import Topology - - -# Partitions shard-space into pieces of contiguous shards, represented by floating point range [start, end) between 0 and 1 -@dataclass -class Partition: - node_id: str - start: float - end: float - - -class PartitioningStrategy(ABC): - @abstractmethod - def partition(self, topology: Topology) -> List[Partition]: - pass - - -def map_partitions_to_shards( - partitions: List[Partition], num_layers: int, model_id: str -) -> List[Shard]: - shards = [] - for i, partition in enumerate(partitions): - start_layer = int(partition.start * num_layers) - end_layer = int(partition.end * num_layers) - 1 - - # Ensure the last partition covers up to num_layers - 1 - if i == len(partitions) - 1: - end_layer = num_layers - 1 - - # Ensure no empty shards - if start_layer <= end_layer: - shards.append(Shard(model_id, start_layer, end_layer, num_layers)) - - # Ensure full coverage - if shards and shards[-1].end_layer < num_layers - 1: - shards[-1] = Shard(model_id, shards[-1].start_layer, num_layers - 1, num_layers) - - return shards diff --git a/pkg/hanzo-network/src/hanzo_network/topology/ring_memory_weighted_partitioning_strategy.py b/pkg/hanzo-network/src/hanzo_network/topology/ring_memory_weighted_partitioning_strategy.py deleted file mode 100644 index 21b693535..000000000 --- a/pkg/hanzo-network/src/hanzo_network/topology/ring_memory_weighted_partitioning_strategy.py +++ /dev/null @@ -1,18 +0,0 @@ -from typing import List - -from .partitioning_strategy import Partition, PartitioningStrategy -from .topology import Topology - - -class RingMemoryWeightedPartitioningStrategy(PartitioningStrategy): - def partition(self, topology: Topology) -> List[Partition]: - nodes = list(topology.all_nodes()) - nodes.sort(key=lambda x: (x[1].memory, x[0]), reverse=True) - total_memory = sum(node[1].memory for node in nodes) - partitions = [] - start = 0 - for node in nodes: - end = round(start + (node[1].memory / total_memory), 5) - partitions.append(Partition(node[0], start, end)) - start = end - return partitions diff --git a/pkg/hanzo-network/src/hanzo_network/topology/test_device_capabilities.py b/pkg/hanzo-network/src/hanzo_network/topology/test_device_capabilities.py deleted file mode 100644 index 0a319b81b..000000000 --- a/pkg/hanzo-network/src/hanzo_network/topology/test_device_capabilities.py +++ /dev/null @@ -1,119 +0,0 @@ -from unittest.mock import Mock, patch - -import pytest - -from .topology.device_capabilities import ( - TFLOPS, - DeviceCapabilities, - DeviceFlops, - device_capabilities, - mac_device_capabilities, -) - - -@patch("subprocess.run") -@patch("psutil.virtual_memory") -def test_mac_device_capabilities_pro(mock_memory, mock_subprocess_run): - # Mock memory - mock_memory.return_value = Mock(total=137438953472) # 128 GB - - # Mock the subprocess output - mock_result = Mock() - mock_result.returncode = 0 - mock_result.stdout = """ -Hardware: - -Hardware Overview: - -Model Name: MacBook Pro -Model Identifier: Mac15,9 -Model Number: Z1CM000EFB/A -Chip: Apple M3 Max -Total Number of Cores: 16 (12 performance and 4 efficiency) -Memory: 128 GB -System Firmware Version: 10000.000.0 -OS Loader Version: 10000.000.0 -Serial Number (system): XXXXXXXXXX -Hardware UUID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX -Provisioning UDID: XXXXXXXX-XXXXXXXXXXXXXXXX -Activation Lock Status: Enabled -""" - mock_subprocess_run.return_value = mock_result - - # Call the function - result = mac_device_capabilities() - - # Check the results - assert isinstance(result, DeviceCapabilities) - assert result.model == "MacBook Pro" - assert result.chip == "Apple M3 Max" - assert result.memory == 131072 # 128 GB in MB - assert "fp32: 14.20 TFLOPS" in str(result) - - -@patch("subprocess.run") -@patch("psutil.virtual_memory") -def test_mac_device_capabilities_air(mock_memory, mock_subprocess_run): - # Mock memory - mock_memory.return_value = Mock(total=8589934592) # 8 GB - - # Mock the subprocess output - mock_result = Mock() - mock_result.returncode = 0 - mock_result.stdout = """ -Hardware: - -Hardware Overview: - -Model Name: MacBook Air -Model Identifier: Mac14,2 -Model Number: MLY33B/A -Chip: Apple M2 -Total Number of Cores: 8 (4 performance and 4 efficiency) -Memory: 8 GB -System Firmware Version: 10000.00.0 -OS Loader Version: 10000.00.0 -Serial Number (system): XXXXXXXXXX -Hardware UUID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX -Provisioning UDID: XXXXXXXX-XXXXXXXXXXXXXXXX -Activation Lock Status: Disabled -""" - mock_subprocess_run.return_value = mock_result - - # Call the function - result = mac_device_capabilities() - - # Check the results - assert isinstance(result, DeviceCapabilities) - assert result.model == "MacBook Air" - assert result.chip == "Apple M2" - assert result.memory == 8192 # 8 GB in MB - - -@pytest.mark.skip( - reason="Unskip this test when running on a MacBook Pro, Apple M3 Max, 128GB" -) -def test_mac_device_capabilities_real(): - # Call the function without mocking - result = mac_device_capabilities() - - # Check the results - assert isinstance(result, DeviceCapabilities) - assert result.model == "MacBook Pro" - assert result.chip == "Apple M3 Max" - assert result.memory == 131072 # 128 GB in MB - assert result.flops == DeviceFlops( - fp32=14.20 * TFLOPS, fp16=28.40 * TFLOPS, int8=56.80 * TFLOPS - ) - assert ( - str(result) - == "Model: MacBook Pro. Chip: Apple M3 Max. Memory: 131072MB. Flops: fp32: 14.20 TFLOPS, fp16: 28.40 TFLOPS, int8: 56.80 TFLOPS" - ) - - -def test_device_capabilities(): - caps = device_capabilities() - assert caps.model != "" - assert caps.chip != "" - assert caps.memory > 0 - assert caps.flops is not None diff --git a/pkg/hanzo-network/src/hanzo_network/topology/test_map_partitions.py b/pkg/hanzo-network/src/hanzo_network/topology/test_map_partitions.py deleted file mode 100644 index 616b51c49..000000000 --- a/pkg/hanzo-network/src/hanzo_network/topology/test_map_partitions.py +++ /dev/null @@ -1,84 +0,0 @@ -import unittest -from typing import List - -from .inference.shard import Shard -from .topology.partitioning_strategy import Partition, map_partitions_to_shards - - -class TestRingMemoryWeightedPartitioningStrategy(unittest.TestCase): - def test_map_partitions_to_shards(self): - partitions = [ - Partition("node1", 0.0, 0.42857), - Partition("node2", 0.42857, 0.71428), - Partition("node3", 0.71428, 0.99999), - ] - shards = map_partitions_to_shards(partitions, 32, "model") - self.assertEqual( - shards, - [ - Shard("model", 0, 12, 32), - Shard("model", 13, 21, 32), - Shard("model", 22, 31, 32), - ], - ) - - partitions = [ - Partition("node1", 0.0, 0.1), - Partition("node2", 0.1, 0.2), - Partition("node3", 0.2, 1.0), - ] - shards = map_partitions_to_shards(partitions, 32, "model") - self.assertEqual( - shards, - [ - Shard("model", 0, 2, 32), - Shard("model", 3, 5, 32), - Shard("model", 6, 31, 32), - ], - ) - - partitions = [ - Partition("node1", 0.0, 1.0), - ] - shards = map_partitions_to_shards(partitions, 32, "model") - self.assertEqual( - shards, - [ - Shard("model", 0, 31, 32), - ], - ) - - partitions = [] - shards = map_partitions_to_shards(partitions, 32, "model") - self.assertEqual(shards, []) - - def test_broken_map_partitions_to_shards(self): - # this was an old broken implementation that sometimes had rounding errors! - def _broken_map_partitions_to_shards( - partitions: List[Partition], num_layers, model_id: str - ): - shards = [] - for i, partition in enumerate(partitions): - start_layer = int(partition.start * num_layers) - end_layer = int(partition.end * num_layers) - 1 - shards.append(Shard(model_id, start_layer, end_layer, num_layers)) - return shards - - partitions = [ - Partition("node1", 0.0, 0.42857), - Partition("node2", 0.42857, 0.71428), - Partition("node3", 0.71428, 0.99999), - ] - shards = _broken_map_partitions_to_shards(partitions, 32, "model") - self.assertEqual( - shards, - [ - Shard("model", 0, 12, 32), - Shard("model", 13, 21, 32), - Shard("model", 22, 30, 32), - ], - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/pkg/hanzo-network/src/hanzo_network/topology/test_ring_memory_weighted_partitioning_strategy.py b/pkg/hanzo-network/src/hanzo_network/topology/test_ring_memory_weighted_partitioning_strategy.py deleted file mode 100644 index c11f4d32e..000000000 --- a/pkg/hanzo-network/src/hanzo_network/topology/test_ring_memory_weighted_partitioning_strategy.py +++ /dev/null @@ -1,108 +0,0 @@ -import unittest - -from .topology.device_capabilities import DeviceCapabilities, DeviceFlops -from .topology.partitioning_strategy import Partition -from .topology.ring_memory_weighted_partitioning_strategy import ( - RingMemoryWeightedPartitioningStrategy, -) -from .topology.topology import Topology - - -class TestRingMemoryWeightedPartitioningStrategy(unittest.TestCase): - def test_partition(self): - # triangle - # node1 -> node2 -> node3 -> node1 - topology = Topology() - topology.update_node( - "node1", - DeviceCapabilities( - model="test1", - chip="test1", - memory=3000, - flops=DeviceFlops(fp32=0, fp16=0, int8=0), - ), - ) - topology.update_node( - "node2", - DeviceCapabilities( - model="test2", - chip="test2", - memory=1000, - flops=DeviceFlops(fp32=0, fp16=0, int8=0), - ), - ) - topology.update_node( - "node3", - DeviceCapabilities( - model="test3", - chip="test3", - memory=6000, - flops=DeviceFlops(fp32=0, fp16=0, int8=0), - ), - ) - topology.add_edge("node1", "node2") - topology.add_edge("node2", "node3") - topology.add_edge("node3", "node1") - topology.add_edge("node1", "node3") - - strategy = RingMemoryWeightedPartitioningStrategy() - partitions = strategy.partition(topology) - - self.assertEqual(len(partitions), 3) - self.assertEqual( - partitions, - [ - Partition("node3", 0.0, 0.6), - Partition("node1", 0.6, 0.9), - Partition("node2", 0.9, 1.0), - ], - ) - - def test_partition_rounding(self): - # triangle - # node1 -> node2 -> node3 -> node1 - topology = Topology() - topology.update_node( - "node1", - DeviceCapabilities( - model="MacBook Pro", - chip="test1", - memory=128 * 1024 * 1024 * 1024, - flops=DeviceFlops(fp32=0, fp16=0, int8=0), - ), - ) - topology.update_node( - "node2", - DeviceCapabilities( - model="Mac Studio", - chip="test2", - memory=192 * 1024 * 1024 * 1024, - flops=DeviceFlops(fp32=0, fp16=0, int8=0), - ), - ) - topology.update_node( - "node3", - DeviceCapabilities( - model="MacBook Pro", - chip="test3", - memory=128 * 1024 * 1024 * 1024, - flops=DeviceFlops(fp32=0, fp16=0, int8=0), - ), - ) - - strategy = RingMemoryWeightedPartitioningStrategy() - partitions = strategy.partition(topology) - - self.assertEqual(len(partitions), 3) - self.assertEqual( - partitions, - [ - Partition("node3", 0.0, 0.42857), - Partition("node1", 0.6, 0.9), - Partition("node2", 0.9, 1.0), - ], - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/pkg/hanzo-network/src/hanzo_network/topology/topology.py b/pkg/hanzo-network/src/hanzo_network/topology/topology.py deleted file mode 100644 index 07ed4e6f1..000000000 --- a/pkg/hanzo-network/src/hanzo_network/topology/topology.py +++ /dev/null @@ -1,84 +0,0 @@ -from dataclasses import dataclass -from typing import Dict, Optional, Set - -from .device_capabilities import DeviceCapabilities - - -@dataclass -class PeerConnection: - from_id: str - to_id: str - description: Optional[str] = None - - def __hash__(self): - # Use both from_id and to_id for uniqueness in sets - return hash((self.from_id, self.to_id)) - - def __eq__(self, other): - if not isinstance(other, PeerConnection): - return False - # Compare both from_id and to_id for equality - return self.from_id == other.from_id and self.to_id == other.to_id - - -class Topology: - def __init__(self): - self.nodes: Dict[str, DeviceCapabilities] = {} - self.peer_graph: Dict[str, Set[PeerConnection]] = {} - self.active_node_id: Optional[str] = None - - def update_node(self, node_id: str, device_capabilities: DeviceCapabilities): - self.nodes[node_id] = device_capabilities - - def get_node(self, node_id: str) -> DeviceCapabilities: - return self.nodes.get(node_id) - - def all_nodes(self): - return self.nodes.items() - - def add_edge(self, from_id: str, to_id: str, description: Optional[str] = None): - if from_id not in self.peer_graph: - self.peer_graph[from_id] = set() - conn = PeerConnection(from_id, to_id, description) - self.peer_graph[from_id].add(conn) - - def merge(self, peer_node_id: str, other: "Topology"): - for node_id, capabilities in other.nodes.items(): - if node_id != peer_node_id: - continue - self.update_node(node_id, capabilities) - for node_id, connections in other.peer_graph.items(): - for conn in connections: - if conn.from_id != peer_node_id: - continue - self.add_edge(conn.from_id, conn.to_id, conn.description) - - def __str__(self): - nodes_str = ", ".join( - f"{node_id}: {cap}" for node_id, cap in self.nodes.items() - ) - edges_str = ", ".join( - f"{node}: {[f'{c.to_id}({c.description})' for c in conns]}" - for node, conns in self.peer_graph.items() - ) - return f"Topology(Nodes: {{{nodes_str}}}, Edges: {{{edges_str}}})" - - def to_json(self): - return { - "nodes": { - node_id: capabilities.to_dict() - for node_id, capabilities in self.nodes.items() - }, - "peer_graph": { - node_id: [ - { - "from_id": conn.from_id, - "to_id": conn.to_id, - "description": conn.description, - } - for conn in connections - ] - for node_id, connections in self.peer_graph.items() - }, - "active_node_id": self.active_node_id, - } diff --git a/pkg/hanzo-network/tests/test_distributed_network.py b/pkg/hanzo-network/tests/test_distributed_network.py deleted file mode 100644 index 2069a191b..000000000 --- a/pkg/hanzo-network/tests/test_distributed_network.py +++ /dev/null @@ -1,213 +0,0 @@ -"""Tests for distributed network functionality.""" - -import asyncio - -import pytest - -from hanzo_network import create_agent, create_distributed_network, create_tool - - -# Simple test tool -async def echo_tool(message: str) -> str: - """Echo back the message.""" - return f"Echo: {message}" - - -# Create test agents -def create_test_agents(): - """Create test agents for distributed network.""" - - # Echo agent - echo_agent = create_agent( - name="echo_agent", - description="Agent that echoes messages", - system="You are an echo agent. Use the echo tool to echo messages.", - tools=[create_tool(echo_tool, "Echo back the message")], - ) - - # Math agent - async def calculate(expression: str) -> str: - """Evaluate a mathematical expression.""" - try: - result = eval(expression) - return f"Result: {result}" - except Exception: - return "Error: Invalid expression" - - math_agent = create_agent( - name="math_agent", - description="Agent that performs calculations", - system="You are a math agent. Use the calculate tool for math.", - tools=[create_tool(calculate, "Evaluate a mathematical expression")], - ) - - return [echo_agent, math_agent] - - -@pytest.mark.asyncio -async def test_distributed_network_creation(): - """Test creating a distributed network.""" - agents = create_test_agents() - - network = create_distributed_network( - agents=agents, - name="test-network", - discovery_method="udp", - listen_port=15678, # Use different port to avoid conflicts - broadcast_port=15678, - ) - - assert network.name == "test-network" - assert len(network.agents) == 2 - assert network.discovery_method == "udp" - assert network.listen_port == 15678 - - -@pytest.mark.asyncio -async def test_distributed_network_start_stop(): - """Test starting and stopping a distributed network.""" - agents = create_test_agents() - - network = create_distributed_network( - agents=agents, name="test-network", listen_port=15679, broadcast_port=15679 - ) - - # Start network - await network.start(wait_for_peers=0) - assert network.is_running - - # Get status - status = network.get_network_status() - assert status["is_running"] - assert status["node_id"] == network.node_id - assert "echo_agent" in status["local_agents"] - assert "math_agent" in status["local_agents"] - - # Stop network - await network.stop() - assert not network.is_running - - -@pytest.mark.asyncio -async def test_distributed_network_local_execution(): - """Test executing agents locally in distributed network.""" - agents = create_test_agents() - - network = create_distributed_network( - agents=agents, name="test-network", listen_port=15680, broadcast_port=15680 - ) - - # Start network - await network.start(wait_for_peers=0) - - try: - # Test echo agent - echo_result = await network.run( - prompt="Echo the message 'Hello World'", - initial_agent=network.get_agent("echo_agent"), - ) - - assert echo_result["success"] - # Mock agents return mock responses - assert "echo_agent" in echo_result["final_output"] - - # Test math agent - math_result = await network.run( - prompt="Calculate 2 + 2", initial_agent=network.get_agent("math_agent") - ) - - assert math_result["success"] - # Mock agents return mock responses - assert "math_agent" in math_result["final_output"] - - finally: - await network.stop() - - -@pytest.mark.asyncio -async def test_distributed_network_peer_discovery(): - """Test peer discovery between two networks.""" - agents1 = create_test_agents() - agents2 = create_test_agents() - - # Create two networks on same broadcast - network1 = create_distributed_network( - agents=agents1, - name="network-1", - node_id="node-1", - listen_port=15681, - broadcast_port=15683, # Same broadcast port - ) - - network2 = create_distributed_network( - agents=agents2, - name="network-2", - node_id="node-2", - listen_port=15682, - broadcast_port=15683, # Same broadcast port - ) - - # Start both networks - await network1.start(wait_for_peers=0) - await network2.start(wait_for_peers=0) - - try: - # Wait for discovery - await asyncio.sleep(2) - - # Check if they discovered each other - status1 = network1.get_network_status() - status2 = network2.get_network_status() - - # Each should have discovered the other - # Note: This might not work in CI/test environment without actual UDP - # but demonstrates the API - print(f"Network 1 peers: {status1['peer_count']}") - print(f"Network 2 peers: {status2['peer_count']}") - - finally: - await network1.stop() - await network2.stop() - - -@pytest.mark.asyncio -async def test_distributed_network_with_router(): - """Test distributed network with custom router.""" - agents = create_test_agents() - - # Simple router that alternates between agents - call_count = 0 - - def simple_router(args): - nonlocal call_count - call_count += 1 - if call_count <= 1: - return args.network.get_agent("echo_agent") - elif call_count <= 2: - return args.network.get_agent("math_agent") - else: - return None # Stop - - network = create_distributed_network( - agents=agents, - name="test-network", - router=simple_router, - listen_port=15684, - broadcast_port=15684, - ) - - await network.start(wait_for_peers=0) - - try: - result = await network.run(prompt="First echo 'test', then calculate 5 + 5") - - assert result["success"] - assert result["iterations"] == 2 - - finally: - await network.stop() - - -if __name__ == "__main__": - # Run a simple test - asyncio.run(test_distributed_network_peer_discovery()) diff --git a/pkg/hanzo-node/README.md b/pkg/hanzo-node/README.md deleted file mode 100644 index 59c73fb58..000000000 --- a/pkg/hanzo-node/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# hanzo-node - -Cross-platform installer for the Hanzo AI node (Rust binary). - -## Installation - -```bash -# Install via uv -uv tool install hanzo-node - -# Or via pip -pip install hanzo-node -``` - -## Usage - -```bash -# Install the node binary -hanzo-node install - -# Check status -hanzo-node status - -# Upgrade to latest -hanzo-node upgrade - -# Run the node (passes args to binary) -hanzo-node run --help - -# Uninstall -hanzo-node uninstall -``` - -## How It Works - -This Python package is a thin wrapper that: - -1. Detects your platform (macOS/Linux/Windows, x64/arm64) -2. Downloads the appropriate Rust binary from GitHub releases -3. Installs it to `~/.local/bin` (or `%LOCALAPPDATA%\hanzo\bin` on Windows) -4. Provides a CLI to manage the installation - -The actual `hanzo-node` is written in Rust for performance. This package just handles cross-platform distribution. - -## Supported Platforms - -- macOS (Apple Silicon / Intel) -- Linux (x64 / arm64) -- Windows (x64) - -## Environment Variables - -- `HANZO_INSTALL_DIR` - Override the installation directory (default: `~/.local/bin`) - -## License - -Apache 2.0 diff --git a/pkg/hanzo-node/pyproject.toml b/pkg/hanzo-node/pyproject.toml deleted file mode 100644 index d25ad7618..000000000 --- a/pkg/hanzo-node/pyproject.toml +++ /dev/null @@ -1,43 +0,0 @@ -[project] -name = "hanzo-node" -version = "0.1.0" -description = "Hanzo Node - Cross-platform installer for the Hanzo AI node (Rust binary)" -authors = [ - {name = "Hanzo AI", email = "dev@hanzo.ai"}, -] -dependencies = [ - "httpx>=0.23.0", - "rich>=13.0.0", - "click>=8.1.0", -] -readme = "README.md" -requires-python = ">=3.12" -keywords = ["hanzo", "node", "ai", "blockchain", "p2p"] -classifiers = [ - "Development Status :: 3 - Alpha", - "Environment :: Console", - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Topic :: Software Development", -] - -[project.scripts] -hanzo-node = "hanzo_node.cli:main" - -[project.urls] -Homepage = "https://hanzo.ai" -Repository = "https://github.com/hanzoai/python-sdk" -Documentation = "https://docs.hanzo.ai/node" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["src/hanzo_node"] diff --git a/pkg/hanzo-node/src/hanzo_node/__init__.py b/pkg/hanzo-node/src/hanzo_node/__init__.py deleted file mode 100644 index fe1d41ca2..000000000 --- a/pkg/hanzo-node/src/hanzo_node/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Hanzo Node - Cross-platform installer for the Hanzo AI node.""" - -__version__ = "0.1.0" - -from .installer import install, uninstall, is_installed, get_binary_path - -__all__ = ["install", "uninstall", "get_binary_path", "is_installed", "__version__"] diff --git a/pkg/hanzo-node/src/hanzo_node/cli.py b/pkg/hanzo-node/src/hanzo_node/cli.py deleted file mode 100644 index 81c0532b9..000000000 --- a/pkg/hanzo-node/src/hanzo_node/cli.py +++ /dev/null @@ -1,123 +0,0 @@ -"""CLI for hanzo-node installer and runner.""" - -import os -import sys -import subprocess - -import click - -from . import __version__ -from .installer import ( - install, - uninstall, - is_installed, - get_binary_path, - get_installed_version, -) - - -@click.group(invoke_without_command=True) -@click.option("--version", "-v", is_flag=True, help="Show version") -@click.pass_context -def main(ctx, version): - """ - Hanzo Node - AI infrastructure node. - - If hanzo-node binary is installed, runs it directly. - Otherwise, use 'hanzo-node install' to install it first. - """ - if version: - print(f"hanzo-node {__version__} (installer)") - if is_installed(): - node_ver = get_installed_version() - print(f"hanzo-node {node_ver or '?'} (binary) at {get_binary_path()}") - return - - # If no subcommand and binary is installed, run it - if ctx.invoked_subcommand is None: - if is_installed(): - # Pass through to the actual binary - binary = get_binary_path() - sys.exit(subprocess.call([str(binary)] + sys.argv[1:])) - else: - click.echo("hanzo-node is not installed. run: hanzo-node install") - ctx.invoke(status) - - -@main.command() -@click.option("--force", "-f", is_flag=True, help="Force reinstall") -@click.option("--version", "-V", "ver", help="Specific version to install") -def install_cmd(force, ver): - """Install the hanzo-node binary.""" - click.echo() - try: - install(force=force, version=ver) - except Exception as e: - click.echo(f" โœ— {e}", err=True) - sys.exit(1) - click.echo() - - -# Alias 'install' command -main.add_command(install_cmd, name="install") - - -@main.command() -def uninstall_cmd(): - """Uninstall the hanzo-node binary.""" - click.echo() - uninstall() - click.echo() - - -main.add_command(uninstall_cmd, name="uninstall") - - -@main.command() -@click.option("--force", "-f", is_flag=True, help="Force upgrade") -def upgrade(force): - """Upgrade to the latest version.""" - click.echo() - try: - install(force=True) - except Exception as e: - click.echo(f" โœ— {e}", err=True) - sys.exit(1) - click.echo() - - -@main.command() -def status(): - """Show installation status.""" - click.echo() - if is_installed(): - binary = get_binary_path() - ver = get_installed_version() - click.echo(f" โœ“ hanzo-node {ver or '?'}") - click.echo(f" path: {binary}") - else: - click.echo(" โ—‹ hanzo-node not installed") - click.echo(" run: hanzo-node install") - click.echo() - - -@main.command() -@click.argument("args", nargs=-1) -def run(args): - """Run hanzo-node with arguments.""" - if not is_installed(): - click.echo("hanzo-node is not installed. run: hanzo-node install", err=True) - sys.exit(1) - - binary = get_binary_path() - sys.exit(subprocess.call([str(binary)] + list(args))) - - -@main.command() -def path(): - """Print the binary path.""" - print(get_binary_path()) - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-node/src/hanzo_node/installer.py b/pkg/hanzo-node/src/hanzo_node/installer.py deleted file mode 100644 index 8a87a4d1a..000000000 --- a/pkg/hanzo-node/src/hanzo_node/installer.py +++ /dev/null @@ -1,283 +0,0 @@ -"""Cross-platform binary installer for hanzo-node.""" - -import os -import sys -import stat -import shutil -import tarfile -import zipfile -import platform -import tempfile -from pathlib import Path - -import httpx - -# GitHub release info -GITHUB_REPO = "hanzoai/node" -BINARY_NAME = "hanzo-node" - -# Platform detection -PLATFORM_MAP = { - ("Darwin", "arm64"): "darwin-arm64", - ("Darwin", "x86_64"): "darwin-x64", - ("Linux", "x86_64"): "linux-x64", - ("Linux", "aarch64"): "linux-arm64", - ("Windows", "AMD64"): "windows-x64", - ("Windows", "x86_64"): "windows-x64", -} - -# Asset name patterns for each platform -ASSET_PATTERNS = { - "darwin-arm64": ["darwin", "arm64", "macos", "apple"], - "darwin-x64": ["darwin", "x64", "amd64", "macos"], - "linux-x64": ["linux", "x64", "amd64"], - "linux-arm64": ["linux", "arm64", "aarch64"], - "windows-x64": ["windows", "x64", "amd64", "win"], -} - - -def get_install_dir() -> Path: - """Get the installation directory for the binary.""" - if os.name == "nt": # Windows - base = Path(os.environ.get("LOCALAPPDATA", Path.home() / "AppData" / "Local")) - return base / "hanzo" / "bin" - else: # Unix-like - return Path(os.environ.get("HANZO_INSTALL_DIR", Path.home() / ".local" / "bin")) - - -def get_binary_path() -> Path: - """Get the full path to the hanzo-node binary.""" - binary = BINARY_NAME - if os.name == "nt": - binary += ".exe" - return get_install_dir() / binary - - -def is_installed() -> bool: - """Check if hanzo-node is installed.""" - return get_binary_path().exists() - - -def get_installed_version() -> str | None: - """Get the version of the installed binary.""" - import subprocess - - binary = get_binary_path() - if not binary.exists(): - return None - - try: - result = subprocess.run( - [str(binary), "--version"], - capture_output=True, - text=True, - timeout=10, - ) - if result.returncode == 0: - # Parse version from output like "hanzo-node 0.1.0" - parts = result.stdout.strip().split() - if len(parts) >= 2: - return parts[1] - return None - except Exception: - return None - - -def detect_platform() -> str: - """Detect the current platform.""" - system = platform.system() - machine = platform.machine() - - key = (system, machine) - if key not in PLATFORM_MAP: - raise RuntimeError(f"Unsupported platform: {system}-{machine}") - - return PLATFORM_MAP[key] - - -def get_latest_release() -> dict: - """Get the latest release info from GitHub.""" - url = f"https://api.github.com/repos/{GITHUB_REPO}/releases/latest" - - with httpx.Client(follow_redirects=True, timeout=30) as client: - resp = client.get(url) - resp.raise_for_status() - return resp.json() - - -def find_asset_url(release: dict, plat: str) -> str | None: - """Find the download URL for the current platform.""" - patterns = ASSET_PATTERNS.get(plat, []) - - for asset in release.get("assets", []): - name = asset.get("name", "").lower() - - # Check if asset matches platform patterns - matches = sum(1 for p in patterns if p in name) - if matches >= 2: # Need at least 2 pattern matches - return asset.get("browser_download_url") - - return None - - -def download_and_extract(url: str, dest: Path) -> None: - """Download and extract the binary.""" - dest.parent.mkdir(parents=True, exist_ok=True) - - with httpx.Client(follow_redirects=True, timeout=120) as client: - with tempfile.NamedTemporaryFile(delete=False) as tmp: - tmp_path = Path(tmp.name) - - # Download with progress - with client.stream("GET", url) as resp: - resp.raise_for_status() - total = int(resp.headers.get("content-length", 0)) - downloaded = 0 - - for chunk in resp.iter_bytes(chunk_size=8192): - tmp.write(chunk) - downloaded += len(chunk) - if total > 0: - pct = (downloaded / total) * 100 - print(f"\r downloading... {pct:.0f}%", end="", flush=True) - - print() # newline after progress - - # Extract based on file type - try: - if url.endswith(".tar.gz") or url.endswith(".tgz"): - _extract_tarball(tmp_path, dest) - elif url.endswith(".zip"): - _extract_zip(tmp_path, dest) - else: - # Assume raw binary - shutil.copy2(tmp_path, dest) - finally: - tmp_path.unlink(missing_ok=True) - - # Make executable - if os.name != "nt": - dest.chmod(dest.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) - - -def _extract_tarball(archive: Path, dest: Path) -> None: - """Extract binary from tarball.""" - binary_name = BINARY_NAME - - with tarfile.open(archive, "r:gz") as tar: - # Find the binary in the archive - for member in tar.getmembers(): - if member.name.endswith(binary_name) or member.name == binary_name: - # Extract to temp location then move - with tempfile.TemporaryDirectory() as tmpdir: - tar.extract(member, tmpdir) - extracted = Path(tmpdir) / member.name - shutil.copy2(extracted, dest) - return - - # If not found by name, try extracting all and finding it - with tempfile.TemporaryDirectory() as tmpdir: - tar.extractall(tmpdir) # noqa: S202 - for f in Path(tmpdir).rglob(binary_name): - if f.is_file(): - shutil.copy2(f, dest) - return - - raise RuntimeError(f"Could not find {binary_name} in archive") - - -def _extract_zip(archive: Path, dest: Path) -> None: - """Extract binary from zip.""" - binary_name = BINARY_NAME - if os.name == "nt": - binary_name += ".exe" - - with zipfile.ZipFile(archive, "r") as zf: - # Find the binary in the archive - for name in zf.namelist(): - if name.endswith(binary_name) or os.path.basename(name) == binary_name: - with tempfile.TemporaryDirectory() as tmpdir: - zf.extract(name, tmpdir) - extracted = Path(tmpdir) / name - shutil.copy2(extracted, dest) - return - - # If not found, extract all and search - with tempfile.TemporaryDirectory() as tmpdir: - zf.extractall(tmpdir) # noqa: S202 - for f in Path(tmpdir).rglob(binary_name): - if f.is_file(): - shutil.copy2(f, dest) - return - - raise RuntimeError(f"Could not find {binary_name} in archive") - - -def install(force: bool = False, version: str | None = None) -> Path: - """ - Install hanzo-node binary. - - Args: - force: Force reinstall even if already installed - version: Specific version to install (default: latest) - - Returns: - Path to installed binary - """ - binary_path = get_binary_path() - - if binary_path.exists() and not force: - installed_ver = get_installed_version() - print(f" hanzo-node {installed_ver or '?'} already installed at {binary_path}") - return binary_path - - plat = detect_platform() - print(f" platform: {plat}") - - # Get release info - print(" fetching release info...") - if version: - # TODO: fetch specific version - release = get_latest_release() - else: - release = get_latest_release() - - release_version = release.get("tag_name", "unknown") - print(f" version: {release_version}") - - # Find asset URL - asset_url = find_asset_url(release, plat) - if not asset_url: - raise RuntimeError(f"No binary available for {plat}") - - print(f" url: {asset_url}") - - # Download and install - download_and_extract(asset_url, binary_path) - - print(f" โœ“ installed to {binary_path}") - - # Check if in PATH - install_dir = str(get_install_dir()) - if install_dir not in os.environ.get("PATH", ""): - print(f'\n add to PATH: export PATH="{install_dir}:$PATH"') - - return binary_path - - -def uninstall() -> bool: - """ - Uninstall hanzo-node binary. - - Returns: - True if uninstalled, False if wasn't installed - """ - binary_path = get_binary_path() - - if not binary_path.exists(): - print(" hanzo-node is not installed") - return False - - binary_path.unlink() - print(f" โœ“ removed {binary_path}") - return True diff --git a/pkg/hanzo-node/uv.lock b/pkg/hanzo-node/uv.lock deleted file mode 100644 index 387c3d92f..000000000 --- a/pkg/hanzo-node/uv.lock +++ /dev/null @@ -1,174 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.10" - -[[package]] -name = "anyio" -version = "4.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, -] - -[[package]] -name = "certifi" -version = "2026.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - -[[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 = "exceptiongroup" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[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 = "hanzo-node" -version = "0.1.0" -source = { editable = "." } -dependencies = [ - { name = "click" }, - { name = "httpx" }, - { name = "rich" }, -] - -[package.metadata] -requires-dist = [ - { name = "click", specifier = ">=8.1.0" }, - { name = "httpx", specifier = ">=0.23.0" }, - { name = "rich", specifier = ">=13.0.0" }, -] - -[[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 = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[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 = "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 = "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 = "rich" -version = "14.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, -] - -[[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" }, -] diff --git a/pkg/hanzo-s3/README.md b/pkg/hanzo-s3/README.md deleted file mode 100644 index f90bb835a..000000000 --- a/pkg/hanzo-s3/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# hanzo-s3 - -Hanzo S3 -- Python client for S3-compatible object storage. - -Thin wrapper around the [minio](https://pypi.org/project/minio/) package that -re-exports its public API under the `hanzo_s3` namespace with Hanzo-flavoured -aliases. - -## Install - -```bash -pip install hanzo-s3 -# or -uv add hanzo-s3 -``` - -## Quick start - -```python -from hanzo_s3 import S3Client - -client = S3Client( - "s3-api.hanzo.ai", - access_key="YOUR-ACCESS-KEY", - secret_key="YOUR-SECRET-KEY", -) - -# List buckets -for bucket in client.list_buckets(): - print(bucket.name, bucket.creation_date) - -# Upload a file -client.fput_object("my-bucket", "remote/path.txt", "/local/path.txt") - -# Download a file -client.fget_object("my-bucket", "remote/path.txt", "/local/download.txt") -``` - -## Admin operations - -```python -from hanzo_s3.admin import S3Admin - -admin = S3Admin("s3-api.hanzo.ai", credentials=provider) -info = admin.info() -``` - -## API - -| hanzo_s3 | minio | -|----------|-------| -| `S3Client` / `Client` | `Minio` | -| `S3Admin` / `Admin` | `MinioAdmin` | -| `S3Error` / `Error` | `S3Error` | -| `S3Exception` | `MinioException` | -| `S3AdminException` | `MinioAdminException` | - -All original `minio` names are also re-exported for backward compatibility. - -## Links - -- Documentation: https://docs.hanzo.ai/s3 -- Hanzo Storage: https://s3.hanzo.ai -- Source (upstream): https://github.com/hanzos3/py-sdk -- Source (SDK): https://github.com/hanzoai/python-sdk diff --git a/pkg/hanzo-s3/hanzo_s3/__init__.py b/pkg/hanzo-s3/hanzo_s3/__init__.py deleted file mode 100644 index bf1b82582..000000000 --- a/pkg/hanzo-s3/hanzo_s3/__init__.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Hanzo S3 -- Python client for S3-compatible object storage. - -Thin wrapper around the ``minio`` package that re-exports its public API -under the ``hanzo_s3`` namespace with Hanzo-flavoured aliases. - -Usage:: - - from hanzo_s3 import S3Client - - client = S3Client( - "s3-api.hanzo.ai", - access_key="YOUR-ACCESS-KEY", - secret_key="YOUR-SECRET-KEY", - ) - - for bucket in client.list_buckets(): - print(bucket.name, bucket.creation_date) -""" - -from minio import Minio, credentials, sse # backward compat -from minio import Minio as S3Client -from minio.datatypes import Object -from minio.error import ( - InvalidResponseError, - S3Error, - ServerError, -) -from minio.helpers import ObjectWriteResult - -# Convenience aliases -Client = S3Client -Error = S3Error -S3Exception = S3Error -ObjectWriteResponse = ObjectWriteResult - -__version__ = "1.0.0" - -__all__ = [ - # Clients - "S3Client", - "Client", - "Minio", - # Errors - "S3Error", - "Error", - "S3Exception", - "InvalidResponseError", - "ServerError", - # Data types - "Object", - "ObjectWriteResult", - "ObjectWriteResponse", - # Sub-modules - "credentials", - "sse", -] diff --git a/pkg/hanzo-s3/hanzo_s3/admin.py b/pkg/hanzo-s3/hanzo_s3/admin.py deleted file mode 100644 index 2e8a7cd5b..000000000 --- a/pkg/hanzo-s3/hanzo_s3/admin.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Hanzo S3 Admin -- re-export of MinioAdmin for administrative operations. - -Usage:: - - from hanzo_s3.admin import S3Admin - - admin = S3Admin( - "s3-api.hanzo.ai", - credentials=provider, - ) - - info = admin.info() -""" - -from minio.error import MinioAdminException as S3AdminException -from minio.minioadmin import MinioAdmin # backward compat -from minio.minioadmin import MinioAdmin as S3Admin - -# Convenience alias -Admin = S3Admin - -__all__ = [ - "S3Admin", - "Admin", - "MinioAdmin", - "S3AdminException", -] diff --git a/pkg/hanzo-s3/hanzo_s3/py.typed b/pkg/hanzo-s3/hanzo_s3/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-s3/pyproject.toml b/pkg/hanzo-s3/pyproject.toml deleted file mode 100644 index 490b2d78a..000000000 --- a/pkg/hanzo-s3/pyproject.toml +++ /dev/null @@ -1,59 +0,0 @@ -[project] -name = "hanzo-s3" -version = "1.0.0" -description = "Hanzo S3 โ€” Python client for S3-compatible object storage" -readme = "README.md" -license = { text = "Apache-2.0" } -requires-python = ">=3.12" -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["s3", "minio", "hanzo", "object-storage", "cloud-storage"] -classifiers = [ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Topic :: Internet", - "Topic :: Software Development :: Libraries :: Python Modules", - "Typing :: Typed", -] - -dependencies = [ - "minio>=7.2.0", -] - -[project.optional-dependencies] -dev = [ - "pytest>=8.0.0", - "ruff>=0.5.0", - "mypy>=1.10.0", -] - -[project.urls] -Homepage = "https://s3.hanzo.ai" -Documentation = "https://docs.hanzo.ai/s3" -Repository = "https://github.com/hanzos3/py-sdk" -Source = "https://github.com/hanzoai/python-sdk" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_s3"] - -[tool.ruff] -target-version = "py39" -line-length = 100 - -[tool.ruff.lint] -select = ["E", "W", "F", "I", "B", "C4", "UP"] -ignore = ["E501"] - -[tool.mypy] -python_version = "3.9" -strict = true diff --git a/pkg/hanzo-sandbox/hanzo_sandbox/__init__.py b/pkg/hanzo-sandbox/hanzo_sandbox/__init__.py deleted file mode 100644 index 5f89ada0a..000000000 --- a/pkg/hanzo-sandbox/hanzo_sandbox/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -from hanzo_sandbox.sandbox import ( - ContainerEnvironment, - FilesystemIsolationMode, - LinuxSandboxCommand, - SandboxConfig, - SandboxDetectionInputs, - SandboxRequest, - SandboxStatus, - build_linux_sandbox_command, - detect_container_environment, - detect_container_environment_from, - resolve_sandbox_status, - resolve_sandbox_status_for_request, -) - -__all__ = [ - "ContainerEnvironment", - "FilesystemIsolationMode", - "LinuxSandboxCommand", - "SandboxConfig", - "SandboxDetectionInputs", - "SandboxRequest", - "SandboxStatus", - "build_linux_sandbox_command", - "detect_container_environment", - "detect_container_environment_from", - "resolve_sandbox_status", - "resolve_sandbox_status_for_request", -] diff --git a/pkg/hanzo-sandbox/hanzo_sandbox/sandbox.py b/pkg/hanzo-sandbox/hanzo_sandbox/sandbox.py deleted file mode 100644 index a447837a6..000000000 --- a/pkg/hanzo-sandbox/hanzo_sandbox/sandbox.py +++ /dev/null @@ -1,209 +0,0 @@ -"""Linux sandbox isolation via unshare -- ported from claw-code sandbox.rs.""" - -from __future__ import annotations - -import os -import shutil -import sys -from dataclasses import dataclass, field -from enum import Enum -from pathlib import Path - - -class FilesystemIsolationMode(Enum): - OFF = "off" - WORKSPACE_ONLY = "workspace-only" - ALLOW_LIST = "allow-list" - - -@dataclass -class SandboxConfig: - enabled: bool | None = None - namespace_restrictions: bool | None = None - network_isolation: bool | None = None - filesystem_mode: FilesystemIsolationMode | None = None - allowed_mounts: list[str] = field(default_factory=list) - - def resolve_request( - self, - enabled_override: bool | None = None, - namespace_override: bool | None = None, - network_override: bool | None = None, - filesystem_mode_override: FilesystemIsolationMode | None = None, - allowed_mounts_override: list[str] | None = None, - ) -> SandboxRequest: - enabled = enabled_override if enabled_override is not None else (self.enabled if self.enabled is not None else True) - ns = namespace_override if namespace_override is not None else (self.namespace_restrictions if self.namespace_restrictions is not None else True) - net = network_override if network_override is not None else (self.network_isolation if self.network_isolation is not None else False) - fs_mode = filesystem_mode_override or self.filesystem_mode or FilesystemIsolationMode.WORKSPACE_ONLY - mounts = allowed_mounts_override if allowed_mounts_override is not None else list(self.allowed_mounts) - return SandboxRequest( - enabled=enabled, - namespace_restrictions=ns, - network_isolation=net, - filesystem_mode=fs_mode, - allowed_mounts=mounts, - ) - - -@dataclass -class SandboxRequest: - enabled: bool = True - namespace_restrictions: bool = True - network_isolation: bool = False - filesystem_mode: FilesystemIsolationMode = FilesystemIsolationMode.WORKSPACE_ONLY - allowed_mounts: list[str] = field(default_factory=list) - - -@dataclass -class ContainerEnvironment: - in_container: bool = False - markers: list[str] = field(default_factory=list) - - -@dataclass -class SandboxDetectionInputs: - env_pairs: list[tuple[str, str]] = field(default_factory=list) - dockerenv_exists: bool = False - containerenv_exists: bool = False - proc_1_cgroup: str | None = None - - -@dataclass -class SandboxStatus: - enabled: bool = False - requested: SandboxRequest = field(default_factory=SandboxRequest) - supported: bool = False - active: bool = False - namespace_supported: bool = False - namespace_active: bool = False - network_supported: bool = False - network_active: bool = False - filesystem_mode: FilesystemIsolationMode = FilesystemIsolationMode.WORKSPACE_ONLY - filesystem_active: bool = False - allowed_mounts: list[str] = field(default_factory=list) - in_container: bool = False - container_markers: list[str] = field(default_factory=list) - fallback_reason: str | None = None - - -@dataclass -class LinuxSandboxCommand: - program: str = "" - args: list[str] = field(default_factory=list) - env: list[tuple[str, str]] = field(default_factory=list) - - -_CONTAINER_ENV_KEYS = frozenset({"container", "docker", "podman", "kubernetes_service_host"}) -_CGROUP_NEEDLES = ("docker", "containerd", "kubepods", "podman", "libpod") - - -def detect_container_environment() -> ContainerEnvironment: - proc_1_cgroup: str | None = None - try: - proc_1_cgroup = Path("/proc/1/cgroup").read_text() - except OSError: - pass - return detect_container_environment_from(SandboxDetectionInputs( - env_pairs=list(os.environ.items()), - dockerenv_exists=Path("/.dockerenv").exists(), - containerenv_exists=Path("/run/.containerenv").exists(), - proc_1_cgroup=proc_1_cgroup, - )) - - -def detect_container_environment_from(inputs: SandboxDetectionInputs) -> ContainerEnvironment: - markers: list[str] = [] - if inputs.dockerenv_exists: - markers.append("/.dockerenv") - if inputs.containerenv_exists: - markers.append("/run/.containerenv") - for key, value in inputs.env_pairs: - if key.lower() in _CONTAINER_ENV_KEYS and value: - markers.append(f"env:{key}={value}") - if inputs.proc_1_cgroup is not None: - for needle in _CGROUP_NEEDLES: - if needle in inputs.proc_1_cgroup: - markers.append(f"/proc/1/cgroup:{needle}") - markers = sorted(set(markers)) - return ContainerEnvironment(in_container=bool(markers), markers=markers) - - -def _command_exists(name: str) -> bool: - return shutil.which(name) is not None - - -def _is_linux() -> bool: - return sys.platform == "linux" - - -def _normalize_mounts(mounts: list[str], cwd: Path) -> list[str]: - result: list[str] = [] - for mount in mounts: - p = Path(mount) - result.append(str(p if p.is_absolute() else cwd / p)) - return result - - -def resolve_sandbox_status(config: SandboxConfig, cwd: Path) -> SandboxStatus: - request = config.resolve_request() - return resolve_sandbox_status_for_request(request, cwd) - - -def resolve_sandbox_status_for_request(request: SandboxRequest, cwd: Path) -> SandboxStatus: - container = detect_container_environment() - namespace_supported = _is_linux() and _command_exists("unshare") - network_supported = namespace_supported - filesystem_active = request.enabled and request.filesystem_mode != FilesystemIsolationMode.OFF - fallback_reasons: list[str] = [] - - if request.enabled and request.namespace_restrictions and not namespace_supported: - fallback_reasons.append("namespace isolation unavailable (requires Linux with `unshare`)") - if request.enabled and request.network_isolation and not network_supported: - fallback_reasons.append("network isolation unavailable (requires Linux with `unshare`)") - if request.enabled and request.filesystem_mode == FilesystemIsolationMode.ALLOW_LIST and not request.allowed_mounts: - fallback_reasons.append("filesystem allow-list requested without configured mounts") - - active = request.enabled and (not request.namespace_restrictions or namespace_supported) and (not request.network_isolation or network_supported) - allowed_mounts = _normalize_mounts(request.allowed_mounts, cwd) - - return SandboxStatus( - enabled=request.enabled, - requested=request, - supported=namespace_supported, - active=active, - namespace_supported=namespace_supported, - namespace_active=request.enabled and request.namespace_restrictions and namespace_supported, - network_supported=network_supported, - network_active=request.enabled and request.network_isolation and network_supported, - filesystem_mode=request.filesystem_mode, - filesystem_active=filesystem_active, - allowed_mounts=allowed_mounts, - in_container=container.in_container, - container_markers=container.markers, - fallback_reason="; ".join(fallback_reasons) if fallback_reasons else None, - ) - - -def build_linux_sandbox_command(command: str, cwd: Path, status: SandboxStatus) -> LinuxSandboxCommand | None: - if not _is_linux() or not status.enabled or (not status.namespace_active and not status.network_active): - return None - - args = ["--user", "--map-root-user", "--mount", "--ipc", "--pid", "--uts", "--fork"] - if status.network_active: - args.append("--net") - args.extend(["sh", "-lc", command]) - - sandbox_home = str(cwd / ".sandbox-home") - sandbox_tmp = str(cwd / ".sandbox-tmp") - env: list[tuple[str, str]] = [ - ("HOME", sandbox_home), - ("TMPDIR", sandbox_tmp), - ("SANDBOX_FILESYSTEM_MODE", status.filesystem_mode.value), - ("SANDBOX_ALLOWED_MOUNTS", ":".join(status.allowed_mounts)), - ] - path = os.environ.get("PATH") - if path is not None: - env.append(("PATH", path)) - - return LinuxSandboxCommand(program="unshare", args=args, env=env) diff --git a/pkg/hanzo-sandbox/pyproject.toml b/pkg/hanzo-sandbox/pyproject.toml deleted file mode 100644 index bf6b889ac..000000000 --- a/pkg/hanzo-sandbox/pyproject.toml +++ /dev/null @@ -1,27 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-sandbox" -version = "0.1.0" -description = "Linux sandbox isolation for Hanzo agent runtimes (unshare, namespace, network, filesystem)." -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -keywords = ["hanzo", "sandbox", "isolation", "unshare"] -dependencies = [] - -[project.urls] -"Homepage" = "https://github.com/hanzoai/python-sdk" -"Bug Tracker" = "https://github.com/hanzoai/python-sdk/issues" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_sandbox*"] diff --git a/pkg/hanzo-sandbox/tests/test_sandbox.py b/pkg/hanzo-sandbox/tests/test_sandbox.py deleted file mode 100644 index c252e46e2..000000000 --- a/pkg/hanzo-sandbox/tests/test_sandbox.py +++ /dev/null @@ -1,217 +0,0 @@ -"""Tests ported from claw-code sandbox.rs test suite.""" - -from pathlib import Path - -from hanzo_sandbox import ( - ContainerEnvironment, - FilesystemIsolationMode, - SandboxConfig, - SandboxDetectionInputs, - SandboxRequest, - build_linux_sandbox_command, - detect_container_environment_from, - resolve_sandbox_status_for_request, -) - - -def test_detects_container_markers_from_multiple_sources(): - detected = detect_container_environment_from(SandboxDetectionInputs( - env_pairs=[("container", "docker")], - dockerenv_exists=True, - containerenv_exists=False, - proc_1_cgroup="12:memory:/docker/abc", - )) - assert detected.in_container - assert "/.dockerenv" in detected.markers - assert "env:container=docker" in detected.markers - assert "/proc/1/cgroup:docker" in detected.markers - - -def test_no_container_when_no_markers(): - detected = detect_container_environment_from(SandboxDetectionInputs( - env_pairs=[], - dockerenv_exists=False, - containerenv_exists=False, - proc_1_cgroup=None, - )) - assert not detected.in_container - assert detected.markers == [] - - -def test_containerenv_marker(): - detected = detect_container_environment_from(SandboxDetectionInputs( - env_pairs=[], - dockerenv_exists=False, - containerenv_exists=True, - proc_1_cgroup=None, - )) - assert detected.in_container - assert "/run/.containerenv" in detected.markers - - -def test_kubernetes_env_detected(): - detected = detect_container_environment_from(SandboxDetectionInputs( - env_pairs=[("KUBERNETES_SERVICE_HOST", "10.0.0.1")], - dockerenv_exists=False, - containerenv_exists=False, - proc_1_cgroup=None, - )) - assert detected.in_container - assert "env:KUBERNETES_SERVICE_HOST=10.0.0.1" in detected.markers - - -def test_empty_env_value_ignored(): - detected = detect_container_environment_from(SandboxDetectionInputs( - env_pairs=[("DOCKER", "")], - dockerenv_exists=False, - containerenv_exists=False, - proc_1_cgroup=None, - )) - assert not detected.in_container - - -def test_cgroup_multiple_needles(): - detected = detect_container_environment_from(SandboxDetectionInputs( - env_pairs=[], - dockerenv_exists=False, - containerenv_exists=False, - proc_1_cgroup="1:name=systemd:/kubepods/burstable/containerd/abc", - )) - assert detected.in_container - assert "/proc/1/cgroup:kubepods" in detected.markers - assert "/proc/1/cgroup:containerd" in detected.markers - - -def test_markers_sorted_and_deduped(): - detected = detect_container_environment_from(SandboxDetectionInputs( - env_pairs=[("DOCKER", "1"), ("container", "docker")], - dockerenv_exists=True, - containerenv_exists=True, - proc_1_cgroup="12:memory:/docker/abc", - )) - assert detected.markers == sorted(set(detected.markers)) - - -def test_resolves_request_defaults(): - config = SandboxConfig() - request = config.resolve_request() - assert request.enabled is True - assert request.namespace_restrictions is True - assert request.network_isolation is False - assert request.filesystem_mode == FilesystemIsolationMode.WORKSPACE_ONLY - assert request.allowed_mounts == [] - - -def test_resolves_request_with_overrides(): - config = SandboxConfig( - enabled=True, - namespace_restrictions=True, - network_isolation=False, - filesystem_mode=FilesystemIsolationMode.WORKSPACE_ONLY, - allowed_mounts=["logs"], - ) - request = config.resolve_request( - enabled_override=True, - namespace_override=False, - network_override=True, - filesystem_mode_override=FilesystemIsolationMode.ALLOW_LIST, - allowed_mounts_override=["tmp"], - ) - assert request.enabled is True - assert request.namespace_restrictions is False - assert request.network_isolation is True - assert request.filesystem_mode == FilesystemIsolationMode.ALLOW_LIST - assert request.allowed_mounts == ["tmp"] - - -def test_config_values_used_when_no_overrides(): - config = SandboxConfig( - enabled=False, - namespace_restrictions=False, - network_isolation=True, - filesystem_mode=FilesystemIsolationMode.OFF, - allowed_mounts=["/data"], - ) - request = config.resolve_request() - assert request.enabled is False - assert request.namespace_restrictions is False - assert request.network_isolation is True - assert request.filesystem_mode == FilesystemIsolationMode.OFF - assert request.allowed_mounts == ["/data"] - - -def test_sandbox_status_unsupported_on_non_linux(): - import sys - request = SandboxRequest(enabled=True, namespace_restrictions=True, network_isolation=True) - status = resolve_sandbox_status_for_request(request, Path("/workspace")) - if sys.platform != "linux": - assert not status.supported - assert not status.active - assert not status.namespace_active - assert not status.network_active - assert status.fallback_reason is not None - assert "unshare" in status.fallback_reason - - -def test_sandbox_disabled_means_not_active(): - request = SandboxRequest(enabled=False) - status = resolve_sandbox_status_for_request(request, Path("/workspace")) - assert not status.active - assert not status.namespace_active - assert not status.network_active - assert not status.filesystem_active - - -def test_filesystem_active_unless_off(): - request = SandboxRequest(enabled=True, filesystem_mode=FilesystemIsolationMode.WORKSPACE_ONLY) - status = resolve_sandbox_status_for_request(request, Path("/workspace")) - assert status.filesystem_active - - request_off = SandboxRequest(enabled=True, filesystem_mode=FilesystemIsolationMode.OFF) - status_off = resolve_sandbox_status_for_request(request_off, Path("/workspace")) - assert not status_off.filesystem_active - - -def test_allowlist_without_mounts_warns(): - request = SandboxRequest( - enabled=True, - filesystem_mode=FilesystemIsolationMode.ALLOW_LIST, - allowed_mounts=[], - ) - status = resolve_sandbox_status_for_request(request, Path("/workspace")) - assert status.fallback_reason is not None - assert "allow-list" in status.fallback_reason - - -def test_normalize_mounts_relative_resolved(): - request = SandboxRequest(enabled=True, allowed_mounts=["logs", "/absolute/path"]) - status = resolve_sandbox_status_for_request(request, Path("/workspace")) - assert "/workspace/logs" in status.allowed_mounts - assert "/absolute/path" in status.allowed_mounts - - -def test_build_returns_none_on_non_linux(): - import sys - if sys.platform != "linux": - from hanzo_sandbox import SandboxStatus - status = SandboxStatus( - enabled=True, - namespace_active=True, - network_active=True, - filesystem_mode=FilesystemIsolationMode.WORKSPACE_ONLY, - ) - result = build_linux_sandbox_command("echo hi", Path("/workspace"), status) - assert result is None - - -def test_build_returns_none_when_disabled(): - from hanzo_sandbox import SandboxStatus - status = SandboxStatus(enabled=False) - result = build_linux_sandbox_command("echo hi", Path("/workspace"), status) - assert result is None - - -def test_enum_values(): - assert FilesystemIsolationMode.OFF.value == "off" - assert FilesystemIsolationMode.WORKSPACE_ONLY.value == "workspace-only" - assert FilesystemIsolationMode.ALLOW_LIST.value == "allow-list" diff --git a/pkg/hanzo-tasks/hanzo_tasks/__init__.py b/pkg/hanzo-tasks/hanzo_tasks/__init__.py deleted file mode 100644 index 6aa061fd3..000000000 --- a/pkg/hanzo-tasks/hanzo_tasks/__init__.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -hanzo-tasks โ€” Durable workflow execution for AI agents. - -Wraps the Temporal Python SDK with Hanzo conventions for agent task -orchestration, including pre-built workflows for pipelines and fan-out. - -Example: - >>> from hanzo_tasks import Client, TasksConfig - >>> client = await Client.connect(TasksConfig(namespace="hanzo")) - >>> handle = await client.submit(AgentTaskWorkflow.run, task_input, queue="agents") - >>> result = await handle.result() -""" - -from .activities import execute_agent_task, send_notification, set_agent_executor -from .client import Client, TasksConfig, WorkflowHandle -from .worker import Worker -from .workflows import ( - AgentTaskInput, - AgentTaskOutput, - AgentTaskWorkflow, - FanOutWorkflow, - PipelineWorkflow, -) - -__version__ = "0.1.0" -__all__ = [ - # Client - "Client", - "TasksConfig", - "WorkflowHandle", - # Worker - "Worker", - # Workflows - "AgentTaskWorkflow", - "PipelineWorkflow", - "FanOutWorkflow", - "AgentTaskInput", - "AgentTaskOutput", - # Activities - "execute_agent_task", - "send_notification", - "set_agent_executor", -] diff --git a/pkg/hanzo-tasks/hanzo_tasks/activities.py b/pkg/hanzo-tasks/hanzo_tasks/activities.py deleted file mode 100644 index cadd995aa..000000000 --- a/pkg/hanzo-tasks/hanzo_tasks/activities.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Activity definitions for agent task execution.""" - -from __future__ import annotations - -from typing import Any, Callable - -from temporalio import activity - -# Activity executor โ€” pluggable. The playground sets this to call the ZAP sidecar. -_agent_executor: Callable[..., Any] | None = None - - -def set_agent_executor(fn: Callable[..., Any]) -> None: - """Set the function that executes agent tasks (called by playground).""" - global _agent_executor - _agent_executor = fn - - -@activity.defn -async def execute_agent_task(input: Any) -> Any: - """Execute an agent task. Delegates to the registered executor.""" - if _agent_executor is None: - raise RuntimeError( - "No agent executor registered. Call set_agent_executor() first." - ) - return await _agent_executor(input) - - -@activity.defn -async def send_notification(input: dict[str, Any]) -> None: - """Send a notification (webhook, SSE, etc).""" - import httpx - - async with httpx.AsyncClient() as client: - await client.post(input.get("url", ""), json=input) diff --git a/pkg/hanzo-tasks/hanzo_tasks/client.py b/pkg/hanzo-tasks/hanzo_tasks/client.py deleted file mode 100644 index bca223886..000000000 --- a/pkg/hanzo-tasks/hanzo_tasks/client.py +++ /dev/null @@ -1,100 +0,0 @@ -"""Hanzo Tasks client โ€” wraps Temporal client with Hanzo conventions.""" - -from __future__ import annotations - -from dataclasses import dataclass -from typing import Any -from uuid import uuid4 - -from temporalio.client import Client as TemporalClient - - -@dataclass -class TasksConfig: - """Configuration for connecting to Temporal.""" - - address: str = "localhost:7233" - namespace: str = "hanzo" - tls: bool = False - - -class WorkflowHandle: - """Handle to a running workflow.""" - - def __init__(self, handle: Any) -> None: - self._handle = handle - - @property - def id(self) -> str: - return self._handle.id - - @property - def run_id(self) -> str: - return self._handle.result_run_id - - async def result(self, result_type: type | None = None) -> Any: - return await self._handle.result(result_type=result_type) - - async def cancel(self) -> None: - await self._handle.cancel() - - async def signal(self, name: str, data: Any = None) -> None: - await self._handle.signal(name, data) - - -class Client: - """Hanzo Tasks client for submitting and managing workflows.""" - - def __init__(self, temporal: TemporalClient) -> None: - self._temporal = temporal - - @classmethod - async def connect(cls, config: TasksConfig | None = None) -> Client: - """Connect to Temporal server.""" - cfg = config or TasksConfig() - temporal = await TemporalClient.connect(cfg.address, namespace=cfg.namespace) - return cls(temporal) - - async def submit( - self, - workflow: Any, - input: Any, - *, - id: str | None = None, - queue: str = "default", - **kwargs: Any, - ) -> WorkflowHandle: - """Submit a workflow for execution.""" - handle = await self._temporal.start_workflow( - workflow, - input, - id=id or str(uuid4()), - task_queue=queue, - **kwargs, - ) - return WorkflowHandle(handle) - - async def get_result(self, workflow_id: str, result_type: type | None = None) -> Any: - """Get the result of a completed workflow.""" - handle = self._temporal.get_workflow_handle(workflow_id) - return await handle.result(result_type=result_type) - - async def cancel(self, workflow_id: str) -> None: - """Cancel a running workflow.""" - handle = self._temporal.get_workflow_handle(workflow_id) - await handle.cancel() - - async def signal(self, workflow_id: str, signal_name: str, data: Any = None) -> None: - """Send a signal to a running workflow.""" - handle = self._temporal.get_workflow_handle(workflow_id) - await handle.signal(signal_name, data) - - async def query(self, workflow_id: str, query_name: str) -> Any: - """Query a running workflow.""" - handle = self._temporal.get_workflow_handle(workflow_id) - return await handle.query(query_name) - - @property - def temporal(self) -> TemporalClient: - """Access the underlying Temporal client.""" - return self._temporal diff --git a/pkg/hanzo-tasks/hanzo_tasks/py.typed b/pkg/hanzo-tasks/hanzo_tasks/py.typed deleted file mode 100644 index 8b1378917..000000000 --- a/pkg/hanzo-tasks/hanzo_tasks/py.typed +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pkg/hanzo-tasks/hanzo_tasks/worker.py b/pkg/hanzo-tasks/hanzo_tasks/worker.py deleted file mode 100644 index adc1a67b6..000000000 --- a/pkg/hanzo-tasks/hanzo_tasks/worker.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Hanzo Tasks worker โ€” polls and executes activities.""" - -from __future__ import annotations - -from typing import Any - -from temporalio.worker import Worker as TemporalWorker - - -class Worker: - """Hanzo Tasks worker that polls a queue and executes workflows/activities.""" - - def __init__( - self, - client: Any, - queue: str = "default", - workflows: list[Any] | None = None, - activities: list[Any] | None = None, - ) -> None: - self._client = client - self._queue = queue - self._workflows: list[Any] = workflows or [] - self._activities: list[Any] = activities or [] - self._worker: TemporalWorker | None = None - - def register_workflow(self, workflow_cls: Any) -> Any: - """Register a workflow class. Can be used as a decorator.""" - self._workflows.append(workflow_cls) - return workflow_cls - - def register_activity(self, activity_fn: Any) -> Any: - """Register an activity function. Can be used as a decorator.""" - self._activities.append(activity_fn) - return activity_fn - - async def run(self) -> None: - """Start the worker. Blocks until shutdown.""" - temporal_client = ( - self._client.temporal - if hasattr(self._client, "temporal") - else self._client - ) - self._worker = TemporalWorker( - temporal_client, - task_queue=self._queue, - workflows=self._workflows, - activities=self._activities, - ) - await self._worker.run() - - async def shutdown(self) -> None: - """Gracefully shutdown the worker.""" - if self._worker: - await self._worker.shutdown() diff --git a/pkg/hanzo-tasks/hanzo_tasks/workflows.py b/pkg/hanzo-tasks/hanzo_tasks/workflows.py deleted file mode 100644 index 8151a1d07..000000000 --- a/pkg/hanzo-tasks/hanzo_tasks/workflows.py +++ /dev/null @@ -1,80 +0,0 @@ -"""Pre-built workflows for agent task orchestration.""" - -from __future__ import annotations - -from dataclasses import dataclass, field -from datetime import timedelta - -from temporalio import workflow -from temporalio.common import RetryPolicy - - -@dataclass -class AgentTaskInput: - """Input for a single agent task.""" - - space_id: str - agent_id: str - task_title: str - task_prompt: str - timeout_seconds: int = 3600 - max_retries: int = 3 - - -@dataclass -class AgentTaskOutput: - """Output from an agent task execution.""" - - result: str = "" - error: str = "" - elapsed_seconds: float = 0.0 - - -@workflow.defn -class AgentTaskWorkflow: - """Execute a single agent task with retries and timeout.""" - - @workflow.run - async def run(self, input: AgentTaskInput) -> AgentTaskOutput: - return await workflow.execute_activity( - "execute_agent_task", - input, - start_to_close_timeout=timedelta(seconds=input.timeout_seconds), - retry_policy=RetryPolicy(maximum_attempts=input.max_retries), - ) - - -@workflow.defn -class PipelineWorkflow: - """Run agent tasks sequentially (pipeline).""" - - @workflow.run - async def run(self, tasks: list[AgentTaskInput]) -> list[AgentTaskOutput]: - results: list[AgentTaskOutput] = [] - for task in tasks: - result = await workflow.execute_activity( - "execute_agent_task", - task, - start_to_close_timeout=timedelta(seconds=task.timeout_seconds), - retry_policy=RetryPolicy(maximum_attempts=task.max_retries), - ) - results.append(result) - return results - - -@workflow.defn -class FanOutWorkflow: - """Run agent tasks in parallel (fan-out/fan-in).""" - - @workflow.run - async def run(self, tasks: list[AgentTaskInput]) -> list[AgentTaskOutput]: - handles = [] - for task in tasks: - handle = workflow.start_activity( - "execute_agent_task", - task, - start_to_close_timeout=timedelta(seconds=task.timeout_seconds), - retry_policy=RetryPolicy(maximum_attempts=task.max_retries), - ) - handles.append(handle) - return [await h for h in handles] diff --git a/pkg/hanzo-tasks/pyproject.toml b/pkg/hanzo-tasks/pyproject.toml deleted file mode 100644 index 7b9e3a476..000000000 --- a/pkg/hanzo-tasks/pyproject.toml +++ /dev/null @@ -1,43 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tasks" -version = "0.1.0" -description = "Hanzo Tasks SDK โ€” Durable workflow execution for AI agents (powered by Temporal)" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -classifiers = [ - "Development Status :: 3 - Alpha", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.12", - "Topic :: Software Development :: Libraries :: Python Modules", - "Typing :: Typed", -] -keywords = ["hanzo", "temporal", "tasks", "workflow", "durable", "agents"] -dependencies = [ - "temporalio>=1.9.0", -] - -[project.urls] -Homepage = "https://github.com/hanzoai/python-sdk" -Repository = "https://github.com/hanzoai/python-sdk/tree/main/pkg/hanzo-tasks" -Documentation = "https://hanzo.ai/docs/tasks" - -[project.optional-dependencies] -dev = [ - "pytest>=7.0.0", - "pytest-asyncio>=0.26.0", -] - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tasks*"] - -[tool.setuptools.package-data] -hanzo_tasks = ["py.typed"] diff --git a/pkg/hanzo-tasks/tests/__init__.py b/pkg/hanzo-tasks/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-tasks/tests/test_tasks.py b/pkg/hanzo-tasks/tests/test_tasks.py deleted file mode 100644 index d4845914e..000000000 --- a/pkg/hanzo-tasks/tests/test_tasks.py +++ /dev/null @@ -1,317 +0,0 @@ -"""Hanzo Tasks test suite. - -Unit tests for client config, worker registration, dataclasses, -workflow decorators, and activity executor wiring. No Temporal -server required. -""" - -import inspect -from dataclasses import asdict - -import pytest -import pytest_asyncio - -from hanzo_tasks import ( - AgentTaskInput, - AgentTaskOutput, - AgentTaskWorkflow, - Client, - FanOutWorkflow, - PipelineWorkflow, - TasksConfig, - Worker, - WorkflowHandle, - execute_agent_task, - set_agent_executor, -) - - -# -- config ------------------------------------------------------------------ - - -class TestTasksConfig: - def test_defaults(self): - cfg = TasksConfig() - assert cfg.address == "localhost:7233" - assert cfg.namespace == "hanzo" - assert cfg.tls is False - - def test_custom(self): - cfg = TasksConfig(address="temporal.prod:7233", namespace="prod", tls=True) - assert cfg.address == "temporal.prod:7233" - assert cfg.namespace == "prod" - assert cfg.tls is True - - -# -- dataclasses ------------------------------------------------------------- - - -class TestAgentTaskInput: - def test_defaults(self): - inp = AgentTaskInput( - space_id="sp-1", - agent_id="ag-1", - task_title="Fix bug", - task_prompt="Fix the null pointer in main.go", - ) - assert inp.space_id == "sp-1" - assert inp.agent_id == "ag-1" - assert inp.task_title == "Fix bug" - assert inp.task_prompt == "Fix the null pointer in main.go" - assert inp.timeout_seconds == 3600 - assert inp.max_retries == 3 - - def test_custom_timeout(self): - inp = AgentTaskInput( - space_id="sp-2", - agent_id="ag-2", - task_title="Deploy", - task_prompt="Deploy to prod", - timeout_seconds=600, - max_retries=1, - ) - assert inp.timeout_seconds == 600 - assert inp.max_retries == 1 - - def test_serializes_to_dict(self): - inp = AgentTaskInput( - space_id="sp-1", - agent_id="ag-1", - task_title="T", - task_prompt="P", - ) - d = asdict(inp) - assert d["space_id"] == "sp-1" - assert d["agent_id"] == "ag-1" - assert d["task_title"] == "T" - assert d["task_prompt"] == "P" - assert d["timeout_seconds"] == 3600 - assert d["max_retries"] == 3 - assert len(d) == 6 - - -class TestAgentTaskOutput: - def test_defaults(self): - out = AgentTaskOutput() - assert out.result == "" - assert out.error == "" - assert out.elapsed_seconds == 0.0 - - def test_success(self): - out = AgentTaskOutput(result="done", elapsed_seconds=1.5) - assert out.result == "done" - assert out.error == "" - assert out.elapsed_seconds == 1.5 - - def test_error(self): - out = AgentTaskOutput(error="timeout", elapsed_seconds=3600.0) - assert out.error == "timeout" - assert out.result == "" - - def test_serializes_to_dict(self): - out = AgentTaskOutput(result="ok", elapsed_seconds=0.1) - d = asdict(out) - assert d == {"result": "ok", "error": "", "elapsed_seconds": 0.1} - - -# -- workflow decorators ----------------------------------------------------- - - -class TestWorkflowDecorators: - def test_agent_task_workflow_has_run(self): - assert hasattr(AgentTaskWorkflow, "run") - assert inspect.iscoroutinefunction(AgentTaskWorkflow.run) - - def test_pipeline_workflow_has_run(self): - assert hasattr(PipelineWorkflow, "run") - assert inspect.iscoroutinefunction(PipelineWorkflow.run) - - def test_fanout_workflow_has_run(self): - assert hasattr(FanOutWorkflow, "run") - assert inspect.iscoroutinefunction(FanOutWorkflow.run) - - def test_workflow_classes_are_distinct(self): - assert AgentTaskWorkflow is not PipelineWorkflow - assert PipelineWorkflow is not FanOutWorkflow - - -# -- worker ------------------------------------------------------------------ - - -class TestWorker: - def test_init_defaults(self): - w = Worker(client=None, queue="test-q") - assert w._queue == "test-q" - assert w._workflows == [] - assert w._activities == [] - assert w._worker is None - - def test_register_workflow(self): - w = Worker(client=None) - - class MyWorkflow: - pass - - result = w.register_workflow(MyWorkflow) - assert result is MyWorkflow - assert MyWorkflow in w._workflows - - def test_register_activity(self): - w = Worker(client=None) - - async def my_activity(input): - return "ok" - - result = w.register_activity(my_activity) - assert result is my_activity - assert my_activity in w._activities - - def test_register_multiple(self): - w = Worker(client=None) - for i in range(5): - w.register_workflow(type(f"Wf{i}", (), {})) - assert len(w._workflows) == 5 - - def test_init_with_preloaded(self): - workflows = [AgentTaskWorkflow, PipelineWorkflow] - activities = [execute_agent_task] - w = Worker(client=None, workflows=workflows, activities=activities) - assert len(w._workflows) == 2 - assert len(w._activities) == 1 - - def test_does_not_mutate_caller_list(self): - workflows: list = [] - w = Worker(client=None, workflows=workflows) - w.register_workflow(AgentTaskWorkflow) - # The caller's original list should not be modified since we - # pass a new list via `or []`, but if caller passes a list, - # it IS the same reference. That's expected Python behavior. - # Just verify worker has the workflow. - assert AgentTaskWorkflow in w._workflows - - -# -- executor wiring --------------------------------------------------------- - - -class TestSetAgentExecutor: - def test_set_and_reset(self): - import hanzo_tasks.activities as act - - original = act._agent_executor - - async def my_exec(input): - return "executed" - - set_agent_executor(my_exec) - assert act._agent_executor is my_exec - - # Restore - act._agent_executor = original - - @pytest.mark.asyncio - async def test_execute_without_executor_raises(self): - import hanzo_tasks.activities as act - - saved = act._agent_executor - act._agent_executor = None - try: - with pytest.raises(RuntimeError, match="No agent executor registered"): - await execute_agent_task(None) - finally: - act._agent_executor = saved - - @pytest.mark.asyncio - async def test_execute_with_executor(self): - import hanzo_tasks.activities as act - - saved = act._agent_executor - - async def mock_exec(input): - return AgentTaskOutput(result=f"done:{input.task_title}", elapsed_seconds=0.01) - - set_agent_executor(mock_exec) - try: - inp = AgentTaskInput( - space_id="sp-1", - agent_id="ag-1", - task_title="test", - task_prompt="do it", - ) - out = await execute_agent_task(inp) - assert out.result == "done:test" - assert out.elapsed_seconds == 0.01 - finally: - act._agent_executor = saved - - -# -- workflow handle --------------------------------------------------------- - - -class TestWorkflowHandle: - def test_id(self): - class FakeHandle: - id = "wf-123" - result_run_id = "run-456" - - h = WorkflowHandle(FakeHandle()) - assert h.id == "wf-123" - assert h.run_id == "run-456" - - @pytest.mark.asyncio - async def test_cancel(self): - cancelled = False - - class FakeHandle: - id = "wf-1" - result_run_id = "run-1" - - async def cancel(self): - nonlocal cancelled - cancelled = True - - h = WorkflowHandle(FakeHandle()) - await h.cancel() - assert cancelled - - @pytest.mark.asyncio - async def test_signal(self): - signals = [] - - class FakeHandle: - id = "wf-1" - result_run_id = "run-1" - - async def signal(self, name, data=None): - signals.append((name, data)) - - h = WorkflowHandle(FakeHandle()) - await h.signal("pause", {"reason": "lunch"}) - assert signals == [("pause", {"reason": "lunch"})] - - @pytest.mark.asyncio - async def test_result(self): - class FakeHandle: - id = "wf-1" - result_run_id = "run-1" - - async def result(self, result_type=None): - return "the-result" - - h = WorkflowHandle(FakeHandle()) - assert await h.result() == "the-result" - - -# -- __init__ exports -------------------------------------------------------- - - -class TestExports: - def test_version(self): - import hanzo_tasks - - assert hanzo_tasks.__version__ == "0.1.0" - - def test_all_exports_importable(self): - import hanzo_tasks - - for name in hanzo_tasks.__all__: - assert hasattr(hanzo_tasks, name), f"{name} not found in hanzo_tasks" diff --git a/pkg/hanzo-tasks/uv.lock b/pkg/hanzo-tasks/uv.lock deleted file mode 100644 index fa2156157..000000000 --- a/pkg/hanzo-tasks/uv.lock +++ /dev/null @@ -1,163 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" - -[[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 = "hanzo-tasks" -version = "0.1.0" -source = { editable = "." } -dependencies = [ - { name = "temporalio" }, -] - -[package.optional-dependencies] -dev = [ - { name = "pytest" }, - { name = "pytest-asyncio" }, -] - -[package.metadata] -requires-dist = [ - { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0" }, - { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.26.0" }, - { name = "temporalio", specifier = ">=1.9.0" }, -] -provides-extras = ["dev"] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "nexus-rpc" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2e/f2/d54f5c03d8f4672ccc0875787a385f53dcb61f98a8ae594b5620e85b9cb3/nexus_rpc-1.3.0.tar.gz", hash = "sha256:e56d3b57b60d707ce7a72f83f23f106b86eca1043aa658e44582ab5ff30ab9ad", size = 75650, upload-time = "2025-12-08T22:59:13.002Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/74/0afd841de3199c148146c1d43b4bfb5605b2f1dc4c9a9087fe395091ea5a/nexus_rpc-1.3.0-py3-none-any.whl", hash = "sha256:aee0707b4861b22d8124ecb3f27d62dafbe8777dc50c66c91e49c006f971b92d", size = 28873, upload-time = "2025-12-08T22:59:12.024Z" }, -] - -[[package]] -name = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[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 = "protobuf" -version = "6.33.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/70/e908e9c5e52ef7c3a6c7902c9dfbb34c7e29c25d2f81ade3856445fd5c94/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135", size = 444531, upload-time = "2026-03-18T19:05:00.988Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/9f/2f509339e89cfa6f6a4c4ff50438db9ca488dec341f7e454adad60150b00/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3", size = 425739, upload-time = "2026-03-18T19:04:48.373Z" }, - { url = "https://files.pythonhosted.org/packages/76/5d/683efcd4798e0030c1bab27374fd13a89f7c2515fb1f3123efdfaa5eab57/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326", size = 437089, upload-time = "2026-03-18T19:04:50.381Z" }, - { url = "https://files.pythonhosted.org/packages/5c/01/a3c3ed5cd186f39e7880f8303cc51385a198a81469d53d0fdecf1f64d929/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a", size = 427737, upload-time = "2026-03-18T19:04:51.866Z" }, - { url = "https://files.pythonhosted.org/packages/ee/90/b3c01fdec7d2f627b3a6884243ba328c1217ed2d978def5c12dc50d328a3/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2", size = 324610, upload-time = "2026-03-18T19:04:53.096Z" }, - { url = "https://files.pythonhosted.org/packages/9b/ca/25afc144934014700c52e05103c2421997482d561f3101ff352e1292fb81/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3", size = 339381, upload-time = "2026-03-18T19:04:54.616Z" }, - { url = "https://files.pythonhosted.org/packages/16/92/d1e32e3e0d894fe00b15ce28ad4944ab692713f2e7f0a99787405e43533a/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593", size = 323436, upload-time = "2026-03-18T19:04:55.768Z" }, - { url = "https://files.pythonhosted.org/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656, upload-time = "2026-03-18T19:04:59.826Z" }, -] - -[[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 = "pytest" -version = "9.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, -] - -[[package]] -name = "pytest-asyncio" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, -] - -[[package]] -name = "temporalio" -version = "1.23.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nexus-rpc" }, - { name = "protobuf" }, - { name = "types-protobuf" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/67/48/ba7413e2fab8dcd277b9df00bafa572da24e9ca32de2f38d428dc3a2825c/temporalio-1.23.0.tar.gz", hash = "sha256:72750494b00eb73ded9db76195e3a9b53ff548780f73d878ec3f807ee3191410", size = 1933051, upload-time = "2026-02-18T17:48:22.353Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/71/26c8f21dca9092201b3b9cb7aff42460b4864b5999aa4c6a4343ac66f1fd/temporalio-1.23.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:6b69ac8d75f2d90e66f4edce4316f6a33badc4a30b22efc50e9eddaa9acdc216", size = 12311037, upload-time = "2026-02-18T17:47:47.628Z" }, - { url = "https://files.pythonhosted.org/packages/ec/47/43102816139f2d346680cb7cc1e53da5f6968355ac65b4d35d4edbfca896/temporalio-1.23.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:1bbbb2f9c3cdd09451565163f6d741e51f109694c49435d475fdfa42b597219d", size = 11821906, upload-time = "2026-02-18T17:47:55.314Z" }, - { url = "https://files.pythonhosted.org/packages/00/b0/899ff28464a0e17adf17476bdfac8faf4ea41870358ff2d14737e43f9e66/temporalio-1.23.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf6570e0ee696f99a38d855da4441a890c7187357c16505ed458ac9ef274ed70", size = 12063601, upload-time = "2026-02-18T17:48:03.994Z" }, - { url = "https://files.pythonhosted.org/packages/ed/17/b8c6d2ec3e113c6a788322513a5ff635bdd54b3791d092ed0e273467748a/temporalio-1.23.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b82d6cca54c9f376b50e941dd10d12f7fe5b692a314fb087be72cd2898646a79", size = 12394579, upload-time = "2026-02-18T17:48:11.65Z" }, - { url = "https://files.pythonhosted.org/packages/b4/b7/f9ef7fd5ee65aef7d59ab1e95cb1b45df2fe49c17e3aa4d650ae3322f015/temporalio-1.23.0-cp310-abi3-win_amd64.whl", hash = "sha256:43c3b99a46dd329761a256f3855710c4a5b322afc879785e468bdd0b94faace6", size = 12834494, upload-time = "2026-02-18T17:48:19.071Z" }, -] - -[[package]] -name = "types-protobuf" -version = "6.32.1.20260221" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5f/e2/9aa4a3b2469508bd7b4e2ae11cbedaf419222a09a1b94daffcd5efca4023/types_protobuf-6.32.1.20260221.tar.gz", hash = "sha256:6d5fb060a616bfb076cbb61b4b3c3969f5fc8bec5810f9a2f7e648ee5cbcbf6e", size = 64408, upload-time = "2026-02-21T03:55:13.916Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/e8/1fd38926f9cf031188fbc5a96694203ea6f24b0e34bd64a225ec6f6291ba/types_protobuf-6.32.1.20260221-py3-none-any.whl", hash = "sha256:da7cdd947975964a93c30bfbcc2c6841ee646b318d3816b033adc2c4eb6448e4", size = 77956, upload-time = "2026-02-21T03:55:12.894Z" }, -] - -[[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" }, -] diff --git a/pkg/hanzo-tools-agent/README.md b/pkg/hanzo-tools-agent/README.md deleted file mode 100644 index 78bd22c8e..000000000 --- a/pkg/hanzo-tools-agent/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# hanzo-tools-agent - -Agent orchestration tools for Hanzo MCP. - -## Installation - -```bash -pip install hanzo-tools-agent - -# Optional: API mode -pip install hanzo-tools-agent[api] - -# Optional: High-performance -pip install hanzo-tools-agent[perf] -``` - -## Tools - -### agent - Unified Agent Runner -Run various AI CLI agents with auto-backgrounding. - -```python -# Run with default agent -agent(action="run", prompt="Explain this code") - -# Run specific agent -agent(action="run", name="gemini", prompt="Review this PR") - -# List available agents -agent(action="list") - -# Check agent status -agent(action="status", name="claude") -``` - -**Available Agents:** -- `claude` - Anthropic Claude Code CLI -- `codex` - OpenAI Codex CLI -- `gemini` - Google Gemini CLI -- `grok` - xAI Grok CLI -- `qwen` - Alibaba Qwen CLI -- `vibe` - Vibe coding agent -- `code` - Hanzo Code agent -- `dev` - Hanzo Dev agent - -### Direct API Mode -Configure agents for direct API calls without CLI: - -```json -// ~/.hanzo/agents/custom.json -{ - "endpoint": "https://api.openai.com/v1/chat/completions", - "api_type": "openai", - "model": "gpt-4", - "env_key": "OPENAI_API_KEY" -} -``` - -### zen - Hanzo Zen Guidance -```python -zen(challenge="How should I approach this refactoring?") -``` - -### review - Code Review -```python -review( - focus="FUNCTIONALITY", - work_description="Implemented auto-import feature", - file_paths=["/path/to/file.py"] -) -``` - -## License - -MIT diff --git a/pkg/hanzo-tools-agent/hanzo_tools/__init__.py b/pkg/hanzo-tools-agent/hanzo_tools/__init__.py deleted file mode 100644 index e1b06939a..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Hanzo tools namespace package.""" - -import pkgutil - -__path__ = pkgutil.extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/__init__.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/__init__.py deleted file mode 100644 index 1d1ecf5b1..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/__init__.py +++ /dev/null @@ -1,83 +0,0 @@ -"""Agent tools for Hanzo AI. - -Provides consolidated agent tools: -- agent: Multi-agent orchestration (run, dag, swarm, consensus, dispatch) -- zen: Hanzo Zen guidance for decisions -- review: Code review tool - -Install: - pip install hanzo-tools-agent - -Usage: - from hanzo_tools.agent import register_tools, TOOLS, AgentTool - - # Register with MCP server - register_tools(mcp_server) - -Consensus: https://github.com/luxfi/consensus -""" - -import logging - -logger = logging.getLogger(__name__) - -# Core tools -from .zen_tool import ZenTool -from .agent_tool import AgentTool - -# Legacy imports for backwards compatibility -from .critic_tool import CriticTool # Legacy - use reasoning.critic instead -from .review_tool import ReviewTool -from .grok_cli_tool import GrokCLITool -from .cli_agent_base import CLIAgentBase -from .codex_cli_tool import CodexCLITool -from .claude_cli_tool import ClaudeCLITool -from .gemini_cli_tool import GeminiCLITool - -# Export list for tool discovery -TOOLS = [ - AgentTool, # Multi-agent orchestration - ZenTool, # Hanzo Zen guidance - ReviewTool, # Code review -] - -__all__ = [ - "register_tools", - "TOOLS", - # Primary tools - "AgentTool", - "ZenTool", - "ReviewTool", - # Legacy (for backwards compatibility) - "CriticTool", - "CLIAgentBase", - "ClaudeCLITool", - "CodexCLITool", - "GeminiCLITool", - "GrokCLITool", -] - - -def register_tools(mcp_server, enabled_tools: dict[str, bool] | None = None): - """Register agent tools with the MCP server.""" - from hanzo_tools.core import ToolRegistry - - enabled = enabled_tools or {} - registered = [] - - for tool_class in TOOLS: - tool_name = ( - tool_class.name - if hasattr(tool_class, "name") - else tool_class.__name__.lower() - ) - - if enabled.get(tool_name, True): - try: - tool = tool_class() - ToolRegistry.register_tool(mcp_server, tool) - registered.append(tool) - except Exception as e: - logger.warning(f"Failed to register {tool_name}: {e}") - - return registered diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/agent.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/agent.py deleted file mode 100644 index ac2f3ada2..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/agent.py +++ /dev/null @@ -1,431 +0,0 @@ -"""Unified agent tool implementation. - -This module provides the AgentTool for delegating tasks to sub-agents, -supporting both one-off and long-running RPC modes, including A2A communication. -""" - -import re -import json -import time -import uuid -import asyncio - -# Import llm with warnings suppressed -import warnings -from typing import ( - Any, - Dict, - List, - Unpack, - Optional, - Annotated, - TypedDict, - final, - override, -) - -with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) -from pydantic import Field -from openai.types.chat import ChatCompletionMessageParam -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.fs import get_read_only_filesystem_tools -from hanzo_tools.core import ( - BaseTool, - ToolContext, - PermissionManager, - auto_timeout, - create_tool_context, -) -from hanzo_tools.jupyter import get_read_only_jupyter_tools - -from .prompt import ( - get_default_model, - get_system_prompt, - get_allowed_agent_tools, -) - -# Parameter types -Action = Annotated[ - str, - Field( - description="Action: run (default), start, call, stop, list", - default="run", - ), -] - -Prompts = Annotated[ - Optional[str | List[str]], - Field( - description="Task(s) for agent (must include absolute paths starting with /)", - default=None, - ), -] - -Mode = Annotated[ - str, - Field( - description="Execution mode: oneoff (default) or rpc", - default="oneoff", - ), -] - -AgentId = Annotated[ - Optional[str], - Field( - description="Agent ID for RPC mode", - default=None, - ), -] - -Method = Annotated[ - Optional[str], - Field( - description="Method to call on RPC agent", - default=None, - ), -] - -Args = Annotated[ - Optional[Dict[str, Any]], - Field( - description="Arguments for RPC method call", - default=None, - ), -] - -Model = Annotated[ - Optional[str], - Field( - description="Model to use (e.g., lm-studio/local-model, openai/gpt-4o)", - default=None, - ), -] - - -class AgentParams(TypedDict, total=False): - """Parameters for agent tool.""" - - action: str - prompts: Optional[str | List[str]] - mode: str - agent_id: Optional[str] - method: Optional[str] - args: Optional[Dict[str, Any]] - model: Optional[str] - - -class RPCAgent: - """Long-running RPC agent.""" - - def __init__( - self, agent_id: str, model: str, system_prompt: str, tools: List[BaseTool] - ): - self.agent_id = agent_id - self.model = model - self.system_prompt = system_prompt - self.tools = tools - self.messages: List[ChatCompletionMessageParam] = [ - {"role": "system", "content": system_prompt} - ] - self.created_at = time.time() - self.last_used = time.time() - self.call_count = 0 - - async def call_method( - self, method: str, args: Dict[str, Any], tool_ctx: ToolContext - ) -> str: - """Call a method on the RPC agent.""" - self.last_used = time.time() - self.call_count += 1 - - # Build prompt based on method - if method == "search": - prompt = f"Search for: {args.get('query', 'unknown')}" - elif method == "analyze": - prompt = f"Analyze: {args.get('target', 'unknown')}" - elif method == "execute": - prompt = f"Execute: {args.get('command', 'unknown')}" - else: - # Generic method call - prompt = f"Method: {method}, Args: {json.dumps(args)}" - - # Add to conversation - self.messages.append({"role": "user", "content": prompt}) - - # Get response - # (simplified - would integrate with full agent execution logic) - response = f"Executed {method} with args {args}" - self.messages.append({"role": "assistant", "content": response}) - - return response - - -@final -class AgentTool(BaseTool): - """Unified agent tool with one-off and RPC modes.""" - - def __init__( - self, - permission_manager: PermissionManager, - model: str | None = None, - api_key: str | None = None, - base_url: str | None = None, - max_tokens: int | None = None, - max_iterations: int = 10, - max_tool_uses: int = 30, - ): - """Initialize the agent tool.""" - self.permission_manager = permission_manager - self.model_override = model - self.api_key_override = api_key - self.base_url_override = base_url - self.max_tokens_override = max_tokens - self.max_iterations = max_iterations - self.max_tool_uses = max_tool_uses - - # RPC agent registry - self._rpc_agents: Dict[str, RPCAgent] = {} - - # Available tools - self.available_tools: list[BaseTool] = [] - self.available_tools.extend( - get_read_only_filesystem_tools(self.permission_manager) - ) - self.available_tools.extend( - get_read_only_jupyter_tools(self.permission_manager) - ) - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "agent" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - tools = [t.name for t in self.available_tools] - - return f"""AI agents with tools: {", ".join(tools)}. Actions: run (default), start, call, stop, list. - -Usage: -agent "Search for config files in /project" -agent --action start --mode rpc --model lm-studio/local-model -agent --action call --agent-id abc123 --method search --args '{{"query": "database"}}' -agent --action list - -Modes: -- oneoff: Single task execution (default) -- rpc: Long-running agent for multiple calls (A2A support)""" - - @override - @auto_timeout("agent") - async def call( - self, - ctx: MCPContext, - **params: Unpack[AgentParams], - ) -> str: - """Execute agent operation.""" - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract action - action = params.get("action", "run") - - # Route to appropriate handler - if action == "run": - return await self._handle_run(params, tool_ctx) - elif action == "start": - return await self._handle_start(params, tool_ctx) - elif action == "call": - return await self._handle_call(params, tool_ctx) - elif action == "stop": - return await self._handle_stop(params, tool_ctx) - elif action == "list": - return await self._handle_list(tool_ctx) - else: - return f"Error: Unknown action '{action}'. Valid actions: run, start, call, stop, list" - - async def _handle_run(self, params: Dict[str, Any], tool_ctx: ToolContext) -> str: - """Handle one-off agent run (default action).""" - prompts = params.get("prompts") - if not prompts: - return "Error: prompts required for run action" - - # Convert to list - if isinstance(prompts, str): - prompt_list = [prompts] - else: - prompt_list = prompts - - # Validate prompts - for prompt in prompt_list: - if not self._validate_prompt(prompt): - return f"Error: Prompt must contain absolute paths starting with /: {prompt[:50]}..." - - # Execute agents - start_time = time.time() - - if len(prompt_list) == 1: - await tool_ctx.info("Launching agent") - result = await self._execute_agent( - prompt_list[0], params.get("model"), tool_ctx - ) - else: - await tool_ctx.info(f"Launching {len(prompt_list)} agents in parallel") - result = await self._execute_multiple_agents( - prompt_list, params.get("model"), tool_ctx - ) - - execution_time = time.time() - start_time - - return f"""Agent execution completed in {execution_time:.2f} seconds. - -AGENT RESPONSE: -{result}""" - - async def _handle_start(self, params: Dict[str, Any], tool_ctx: ToolContext) -> str: - """Start a new RPC agent.""" - mode = params.get("mode", "oneoff") - if mode != "rpc": - return "Error: start action only valid for rpc mode" - - # Generate agent ID - agent_id = str(uuid.uuid4())[:8] - - # Get model - model = params.get("model") or get_default_model(self.model_override) - - # Get available tools - agent_tools = get_allowed_agent_tools( - self.available_tools, - self.permission_manager, - ) - - # Create system prompt - system_prompt = get_system_prompt( - agent_tools, - self.permission_manager, - ) - - # Create RPC agent - agent = RPCAgent(agent_id, model, system_prompt, agent_tools) - self._rpc_agents[agent_id] = agent - - await tool_ctx.info(f"Started RPC agent {agent_id} with model {model}") - - return f"""Started RPC agent: -- ID: {agent_id} -- Model: {model} -- Tools: {len(agent_tools)} - -Use 'agent --action call --agent-id {agent_id} --method --args ' to interact.""" - - async def _handle_call(self, params: Dict[str, Any], tool_ctx: ToolContext) -> str: - """Call method on RPC agent.""" - agent_id = params.get("agent_id") - if not agent_id: - return "Error: agent_id required for call action" - - if agent_id not in self._rpc_agents: - return f"Error: Agent {agent_id} not found. Use 'agent --action list' to see active agents." - - method = params.get("method") - if not method: - return "Error: method required for call action" - - args = params.get("args", {}) - - # Call agent method - agent = self._rpc_agents[agent_id] - await tool_ctx.info(f"Calling {method} on agent {agent_id}") - - try: - result = await agent.call_method(method, args, tool_ctx) - return f"Agent {agent_id} response:\n{result}" - except Exception as e: - await tool_ctx.error(f"Error calling agent: {str(e)}") - return f"Error calling agent: {str(e)}" - - async def _handle_stop(self, params: Dict[str, Any], tool_ctx: ToolContext) -> str: - """Stop an RPC agent.""" - agent_id = params.get("agent_id") - if not agent_id: - return "Error: agent_id required for stop action" - - if agent_id not in self._rpc_agents: - return f"Error: Agent {agent_id} not found" - - agent = self._rpc_agents.pop(agent_id) - await tool_ctx.info(f"Stopped agent {agent_id}") - - return f"""Stopped agent {agent_id}: -- Runtime: {time.time() - agent.created_at:.2f} seconds -- Calls: {agent.call_count}""" - - async def _handle_list(self, tool_ctx: ToolContext) -> str: - """List active RPC agents.""" - if not self._rpc_agents: - return "No active RPC agents" - - output = ["=== Active RPC Agents ==="] - for agent_id, agent in self._rpc_agents.items(): - runtime = time.time() - agent.created_at - idle = time.time() - agent.last_used - output.append(f"\nAgent {agent_id}:") - output.append(f" Model: {agent.model}") - output.append(f" Runtime: {runtime:.2f}s") - output.append(f" Idle: {idle:.2f}s") - output.append(f" Calls: {agent.call_count}") - - return "\n".join(output) - - def _validate_prompt(self, prompt: str) -> bool: - """Validate that prompt contains absolute paths.""" - absolute_path_pattern = r"/(?:[^/\s]+/)*[^/\s]+" - return bool(re.search(absolute_path_pattern, prompt)) - - async def _execute_agent( - self, prompt: str, model: Optional[str], tool_ctx: ToolContext - ) -> str: - """Execute a single agent. Uses unified agent tool for actual execution.""" - try: - from .unified_agent_tool import UnifiedAgentTool - - agent_tool = UnifiedAgentTool() - result = await agent_tool.call( - tool_ctx, - action="run", - name=model or "claude", - prompt=prompt, - ) - return result - except ImportError: - return f"Agent execution requires unified_agent_tool: {prompt[:100]}..." - - async def _execute_multiple_agents( - self, prompts: List[str], model: Optional[str], tool_ctx: ToolContext - ) -> str: - """Execute multiple agents in parallel.""" - tasks = [] - for prompt in prompts: - task = self._execute_agent(prompt, model, tool_ctx) - tasks.append(task) - - results = await asyncio.gather(*tasks, return_exceptions=True) - - formatted_results = [] - for i, result in enumerate(results): - if isinstance(result, Exception): - formatted_results.append(f"Agent {i + 1} Error:\n{str(result)}") - else: - formatted_results.append(f"Agent {i + 1} Result:\n{result}") - - return "\n\n---\n\n".join(formatted_results) - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/agent_tool.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/agent_tool.py deleted file mode 100644 index 8414c8460..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/agent_tool.py +++ /dev/null @@ -1,1311 +0,0 @@ -"""Agent tool - multi-agent orchestration. - -Lightweight agent spawning with DAG execution, work distribution (swarm), -and Metastable consensus protocol. - -Supports: -- CLI mode: Spawn claude/gemini/codex/etc CLI tools -- API mode: Direct HTTP calls to OpenAI/Anthropic-compatible endpoints - -Consensus: https://github.com/luxfi/consensus -""" - -import os -import time -import uuid -import signal -import asyncio -from typing import Any, Dict, List, Literal, Optional, Annotated, final, override -from pathlib import Path -from contextlib import suppress -from dataclasses import field, dataclass - -from pydantic import Field -from mcp.server import FastMCP - -# Unified async I/O with uvloop support -from hanzo_async import append_file, using_uvloop, configure_loop -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context -from hanzo_tools.shell import ProcessManager - -configure_loop() # Auto-configure uvloop if available -HAS_UVLOOP = using_uvloop() - -# Optional httpx for API mode -try: - import httpx - - HAS_HTTPX = True -except ImportError: - HAS_HTTPX = False - -# Optional consensus import - fallback to local implementation -try: - from hanzo_consensus import ( - Result as ConsensusResult, - Consensus as MetastableConsensus, - run as run_consensus, - ) - - HAS_CONSENSUS = True -except ImportError: - HAS_CONSENSUS = False - MetastableConsensus = None - ConsensusResult = None - run_consensus = None - - -Action = Annotated[ - Literal[ - "run", # Run single agent - "dag", # DAG execution with dependencies - "swarm", # Work distribution across agents - "consensus", # Metastable multi-model consensus - "dispatch", # Different agents for different tasks - "list", # List available agents - "status", # Check agent availability - "config", # Show configuration - ], - Field(description="Agent action"), -] - - -@dataclass -class Result: - """Agent execution result.""" - - agent: str - prompt: str - output: str - ok: bool - error: Optional[str] = None - item: Optional[str] = None - id: Optional[str] = None - round: int = 0 - ms: int = 0 - lux: float = 1.0 # Luminance (Photon) - faster agents get higher weight - - -# Agent configurations -# Format: {name: AgentConfig} -# AgentConfig: (command, args, env_key, priority, base_url, auth_env) -# For Anthropic-compatible APIs: base_url + auth_env override ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN - - -@dataclass -class AgentConfig: - """Agent configuration. - - Config files: ~/.hanzo/agents/.json - Format: - { - "cmd": "claude", - "args": ["--print", "--dangerously-skip-permissions"], - "env_key": "ANTHROPIC_API_KEY", - "max_turns": 999, - "session": true, - "model": "claude-3-opus", - "system_prompt": "You are a helpful assistant" - } - - Environment overrides: HANZO_AGENT__ARGS="--flag1 --flag2" - """ - - cmd: str - args: List[str] = field(default_factory=list) - env_key: Optional[str] = None - priority: int = 10 - base_url: Optional[str] = None # Anthropic-compatible base URL - auth_env: Optional[str] = None # Env var for auth token - model: Optional[str] = None # Model name override - max_turns: int = 999 # Max turns per session - session: bool = False # Enable session persistence - session_id: Optional[str] = None # Resume specific session - system_prompt: Optional[str] = None # System prompt to append - endpoint: Optional[str] = None # Direct API endpoint (no CLI needed) - api_type: str = "cli" # "cli", "openai", "anthropic" - - -CONFIG_DIR = Path.home() / ".hanzo" / "agents" - - -def _load_config_file(name: str) -> Optional[Dict[str, Any]]: - """Load agent config from ~/.hanzo/agents/.json""" - config_file = CONFIG_DIR / f"{name}.json" - if config_file.exists(): - try: - import json - - with open(config_file) as f: - return json.load(f) - except Exception: - pass - return None - - -def _apply_config(base: AgentConfig, override: Dict[str, Any]) -> AgentConfig: - """Apply config overrides to base config.""" - return AgentConfig( - cmd=override.get("cmd", base.cmd), - args=override.get("args", base.args), - env_key=override.get("env_key", base.env_key), - priority=override.get("priority", base.priority), - base_url=override.get("base_url", base.base_url), - auth_env=override.get("auth_env", base.auth_env), - model=override.get("model", base.model), - max_turns=override.get("max_turns", base.max_turns), - session=override.get("session", base.session), - session_id=override.get("session_id", base.session_id), - system_prompt=override.get("system_prompt", base.system_prompt), - endpoint=override.get("endpoint", base.endpoint), - api_type=override.get("api_type", base.api_type), - ) - - -# Default system prompt for consensus agents - enables MCP communication -CONSENSUS_SYSTEM_PROMPT = """You are participating in a multi-agent consensus protocol. -You have access to hanzo-mcp tools to communicate with other agents. -Use the 'agent' tool to query other participants if needed. -Provide clear, reasoned responses that can be compared and synthesized.""" - - -# Native CLI agents -# YOLO mode: auto-accept, non-interactive, max autonomy -# Each agent configured with its specific flags for autonomous operation -NATIVE_AGENTS = { - # claude: --dangerously-skip-permissions (YOLO), --print (non-interactive), --output-format text - "claude": AgentConfig( - "claude", - ["--print", "--dangerously-skip-permissions", "--output-format", "text"], - "ANTHROPIC_API_KEY", - 1, - ), - # codex: --full-auto (auto-approve everything) - "codex": AgentConfig("codex", ["--full-auto"], "OPENAI_API_KEY", 2), - # gemini: -y (yolo), -q (quiet/non-interactive) - "gemini": AgentConfig("gemini", ["-y", "-q"], "GOOGLE_API_KEY", 3), - # grok: -y (yolo) - assumed similar to others - "grok": AgentConfig("grok", ["-y"], "XAI_API_KEY", 4), - # qwen: --approval-mode yolo, -p (prompt mode) - "qwen": AgentConfig( - "qwen", ["--approval-mode", "yolo", "-p"], "DASHSCOPE_API_KEY", 5 - ), - # vibe: --auto-approve, --max-turns 999, -p (prompt) - "vibe": AgentConfig( - "vibe", ["--auto-approve", "--max-turns", "999", "-p"], None, 6 - ), - # hanzo-dev: -y (yolo) - "dev": AgentConfig("hanzo-dev", ["-y"], None, 8), -} - - -# Dynamic config overrides -# Priority: 1) ~/.hanzo/agents/.json 2) HANZO_AGENT__ARGS env -def _load_agent_overrides(): - """Load agent config overrides from files and environment.""" - # Ensure config dir exists - CONFIG_DIR.mkdir(parents=True, exist_ok=True) - - all_agents = list(NATIVE_AGENTS.keys()) - # Add compat agents if defined - if "ANTHROPIC_COMPAT_AGENTS" in globals(): - all_agents.extend(ANTHROPIC_COMPAT_AGENTS.keys()) - - for name in all_agents: - # Get base config - if name in NATIVE_AGENTS: - base = NATIVE_AGENTS[name] - target = NATIVE_AGENTS - elif name in globals().get("ANTHROPIC_COMPAT_AGENTS", {}): - base = ANTHROPIC_COMPAT_AGENTS[name] - target = ANTHROPIC_COMPAT_AGENTS - else: - continue - - # 1) Load from config file - if file_config := _load_config_file(name): - base = _apply_config(base, file_config) - - # 2) Override from environment - env_key = f"HANZO_AGENT_{name.upper().replace('-', '_')}_ARGS" - if env_val := os.environ.get(env_key): - base = AgentConfig( - cmd=base.cmd, - args=env_val.split(), - env_key=base.env_key, - priority=base.priority, - base_url=base.base_url, - auth_env=base.auth_env, - model=base.model, - max_turns=base.max_turns, - session=base.session, - ) - - target[name] = base - - -# Anthropic-compatible API agents (use claude CLI with custom base URL) -# All use claude CLI with --dangerously-skip-permissions for YOLO mode -ANTHROPIC_COMPAT_AGENTS = { - # MiniMax M2.1 - https://api.minimax.io - "minimax": AgentConfig( - "claude", - ["--print", "--dangerously-skip-permissions", "--output-format", "text"], - None, - 10, - base_url="https://api.minimax.io/anthropic", - auth_env="MINIMAX_API_KEY", - model="MiniMax-M2.1", - ), - # Kimi K2 (Moonshot) - https://api.moonshot.cn - "kimi": AgentConfig( - "claude", - ["--print", "--dangerously-skip-permissions", "--output-format", "text"], - None, - 11, - base_url="https://api.moonshot.cn/anthropic", - auth_env="MOONSHOT_API_KEY", - model="kimi-k2", - ), - # DeepSeek - https://api.deepseek.com - "deepseek": AgentConfig( - "claude", - ["--print", "--dangerously-skip-permissions", "--output-format", "text"], - None, - 12, - base_url="https://api.deepseek.com/anthropic", - auth_env="DEEPSEEK_API_KEY", - model="deepseek-chat", - ), - # Yi/01.AI - https://api.01.ai - "yi": AgentConfig( - "claude", - ["--print", "--dangerously-skip-permissions", "--output-format", "text"], - None, - 13, - base_url="https://api.01.ai/anthropic", - auth_env="YI_API_KEY", - model="yi-large", - ), - # Zhipu GLM-4 - https://open.bigmodel.cn - "glm": AgentConfig( - "claude", - ["--print", "--dangerously-skip-permissions", "--output-format", "text"], - None, - 14, - base_url="https://open.bigmodel.cn/api/paas/v4/anthropic", - auth_env="ZHIPU_API_KEY", - model="glm-4", - ), - # Baichuan - https://api.baichuan-ai.com - "baichuan": AgentConfig( - "claude", - ["--print", "--dangerously-skip-permissions", "--output-format", "text"], - None, - 15, - base_url="https://api.baichuan-ai.com/anthropic", - auth_env="BAICHUAN_API_KEY", - model="Baichuan4", - ), - # StepFun - https://api.stepfun.com - "step": AgentConfig( - "claude", - ["--print", "--dangerously-skip-permissions", "--output-format", "text"], - None, - 16, - base_url="https://api.stepfun.com/anthropic", - auth_env="STEPFUN_API_KEY", - model="step-2", - ), - # Qwen via DashScope Claude Code proxy - https://dashscope-intl.aliyuncs.com - "dashscope": AgentConfig( - "claude", - ["--print", "--dangerously-skip-permissions", "--output-format", "text"], - None, - 17, - base_url="https://dashscope-intl.aliyuncs.com/api/v2/apps/claude-code-proxy", - auth_env="DASHSCOPE_API_KEY", - model="qwen-max", - ), - # Qwen via DashScope (alias) - "qwen-cc": AgentConfig( - "claude", - ["--print", "--dangerously-skip-permissions", "--output-format", "text"], - None, - 18, - base_url="https://dashscope-intl.aliyuncs.com/api/v2/apps/claude-code-proxy", - auth_env="DASHSCOPE_API_KEY", - model="qwen-plus", - ), -} - -# Combined agents dict -AGENTS = {**NATIVE_AGENTS, **ANTHROPIC_COMPAT_AGENTS} - -# Apply environment overrides at import time -_load_agent_overrides() - - -def detect_env() -> Dict[str, Any]: - """Detect Claude Code environment.""" - result = {"in_claude": False, "session": None, "api_key": None} - - if os.environ.get("CLAUDE_CODE") or os.environ.get("CLAUDE_SESSION_ID"): - result["in_claude"] = True - result["session"] = os.environ.get("CLAUDE_SESSION_ID") - - if os.environ.get("ANTHROPIC_API_KEY"): - result["api_key"] = os.environ.get("ANTHROPIC_API_KEY")[:8] + "..." - - return result - - -def get_mcp_env() -> Dict[str, str]: - """Get MCP environment to share with agents. - - Includes hanzo-mcp config so spawned agents can use MCP tools - and communicate with each other during consensus. - """ - env = {} - keys = [ - # Hanzo MCP config - "HANZO_MCP_MODE", - "HANZO_MCP_ALLOWED_PATHS", - "HANZO_MCP_ENABLED_TOOLS", - "HANZO_MCP_SERVER", - "HANZO_MCP_TRANSPORT", - # API keys for various providers - "ANTHROPIC_API_KEY", - "OPENAI_API_KEY", - "GOOGLE_API_KEY", - "XAI_API_KEY", - "DASHSCOPE_API_KEY", - "DEEPSEEK_API_KEY", - "MINIMAX_API_KEY", - "MOONSHOT_API_KEY", - "YI_API_KEY", - "ZHIPU_API_KEY", - "BAICHUAN_API_KEY", - "STEPFUN_API_KEY", - ] - for k in keys: - if os.environ.get(k): - env[k] = os.environ[k] - - # Enable hanzo-mcp for spawned agents - # This allows agent-to-agent communication via MCP - env["HANZO_AGENT_MCP_ENABLED"] = "true" - - return env - - -@final -class AgentTool(BaseTool): - """Multi-agent orchestration tool. - - Actions: - - run: Single agent execution (default: claude -p) - - dag: DAG execution with dependencies - - swarm: Work distribution across parallel agents - - consensus: Metastable multi-model consensus - - dispatch: Different agents for different tasks - """ - - name = "agent" - - def __init__(self): - super().__init__() - self._env = detect_env() - self._mcp_env = get_mcp_env() - - @property - @override - def description(self) -> str: - default = self._default_agent() - native = ", ".join(NATIVE_AGENTS.keys()) - compat = ", ".join(ANTHROPIC_COMPAT_AGENTS.keys()) - return f"""Multi-agent orchestration. Default: {default} - -Actions: -- run: Execute single agent (default: {default}) -- dag: DAG execution with dependencies -- swarm: Work distribution across N agents -- consensus: Lux Quasar multi-model agreement -- dispatch: Different agents for different tasks -- list/status/config: Management - -Native: {native} -Anthropic-compatible: {compat} - -Examples: - agent run --prompt "Explain this code" - agent run --name minimax --prompt "Analyze with MiniMax" - agent dag --tasks '[{{"id":"a","prompt":"analyze"}},{{"id":"b","prompt":"fix {{a}}","after":["a"]}}]' - agent swarm --items '["f1.py","f2.py"]' --template "Review {{item}}" --max_concurrent 10 - agent consensus --prompt "Best approach?" --agents '["claude","minimax","deepseek"]' --rounds 3 - agent dispatch --tasks '[{{"agent":"claude","prompt":"review"}},{{"agent":"kimi","prompt":"test"}}]' - -Consensus: https://github.com/luxfi/consensus -""" - - def _default_agent(self) -> str: - """Get default agent based on environment.""" - if self._env.get("in_claude"): - return "claude" - # Check native agents first by priority - for name, cfg in sorted(NATIVE_AGENTS.items(), key=lambda x: x[1].priority): - if cfg.env_key and os.environ.get(cfg.env_key): - return name - # Then check Anthropic-compatible agents - for name, cfg in sorted( - ANTHROPIC_COMPAT_AGENTS.items(), key=lambda x: x[1].priority - ): - if cfg.auth_env and os.environ.get(cfg.auth_env): - return name - return "dev" - - @override - @auto_timeout("agent") - async def call( - self, - ctx: MCPContext, - action: str = "run", - # run/dispatch - name: Optional[str] = None, - prompt: Optional[str] = None, - cwd: Optional[str] = None, - timeout: int = 300, - # dag - tasks: Optional[List[Dict]] = None, - # swarm - items: Optional[List[str]] = None, - template: Optional[str] = None, - max_concurrent: int = 100, - # consensus - agents: Optional[List[str]] = None, - rounds: int = 3, - k: int = 3, - alpha: float = 0.6, - beta_1: float = 0.5, - beta_2: float = 0.8, - **kwargs, - ) -> str: - """Execute agent action.""" - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - if action == "list": - return self._list() - elif action == "status": - return await self._status(name) - elif action == "config": - return self._config() - elif action == "run": - agent = name or self._default_agent() - return await self._run(agent, prompt, cwd, timeout) - elif action == "dag": - return await self._dag(tasks or [], name, cwd, timeout) - elif action == "swarm": - return await self._swarm( - items or [], template or "", name, max_concurrent, cwd, timeout - ) - elif action == "consensus": - return await self._consensus( - prompt or "", - agents or ["claude", "gemini", "codex"], - rounds, - k, - alpha, - beta_1, - beta_2, - cwd, - timeout, - ) - elif action == "dispatch": - return await self._dispatch(tasks or [], cwd, timeout) - else: - return f"Unknown action: {action}. Use: run, dag, swarm, consensus, dispatch, list, status, config" - - def _list(self) -> str: - """List available agents.""" - default = self._default_agent() - lines = ["Agents:"] - - # Native agents - lines.append(" Native:") - for name, cfg in sorted(NATIVE_AGENTS.items(), key=lambda x: x[1].priority): - mark = " (default)" if name == default else "" - lines.append(f" โ€ข {name}: {cfg.cmd}{mark}") - - # Anthropic-compatible agents - lines.append(" Anthropic-compatible:") - for name, cfg in sorted( - ANTHROPIC_COMPAT_AGENTS.items(), key=lambda x: x[1].priority - ): - has_key = bool(cfg.auth_env and os.environ.get(cfg.auth_env)) - key_mark = " โœ“" if has_key else "" - lines.append(f" โ€ข {name}: {cfg.model}{key_mark}") - - lines.append("") - lines.append("Actions: run, dag, swarm, consensus, dispatch") - if self._env.get("in_claude"): - lines.append("โšก Running in Claude Code") - return "\n".join(lines) - - def _config(self) -> str: - """Show configuration.""" - lines = [ - "Agent Configuration", - "=" * 40, - f"Default agent: {self._default_agent()}", - f"In Claude Code: {self._env.get('in_claude', False)}", - f"Config dir: {CONFIG_DIR}", - ] - if self._env.get("session"): - lines.append(f"Session: {self._env['session'][:8]}...") - - lines.append("") - lines.append("Native Agents:") - for name, cfg in sorted(NATIVE_AGENTS.items(), key=lambda x: x[1].priority): - args_str = " ".join(cfg.args[:3]) + ("..." if len(cfg.args) > 3 else "") - lines.append(f" {name}: {cfg.cmd} {args_str}") - if cfg.max_turns != 999: - lines.append(f" max_turns: {cfg.max_turns}") - - lines.append("") - lines.append("Anthropic-compat Agents:") - for name, cfg in sorted( - ANTHROPIC_COMPAT_AGENTS.items(), key=lambda x: x[1].priority - ): - lines.append(f" {name}: {cfg.model}") - - lines.append("") - lines.append("Override configs:") - lines.append(f" File: ~/.hanzo/agents/.json") - lines.append(f' Env: HANZO_AGENT__ARGS="--flag1 --flag2"') - - lines.append("") - lines.append("MCP env shared with agents:") - for k, v in self._mcp_env.items(): - display = v[:8] + "..." if "KEY" in k else v - lines.append(f" {k}: {display}") - return "\n".join(lines) - - async def _status(self, name: Optional[str]) -> str: - """Check agent availability.""" - if name: - if name not in AGENTS: - return f"Unknown: {name}. Available: {', '.join(AGENTS.keys())}" - cfg = AGENTS[name] - ok = await self._available(cfg.cmd) - # For Anthropic-compat, check auth_env; for native, check env_key - env_to_check = cfg.auth_env or cfg.env_key - has_key = bool(env_to_check and os.environ.get(env_to_check)) - return f"{'โœ“' if ok else 'โœ—'} {name} ({'โœ“ key' if has_key else 'โ—‹ no key'})" - - lines = ["Status:"] - - # Native agents - lines.append(" Native:") - for agent, cfg in sorted(NATIVE_AGENTS.items(), key=lambda x: x[1].priority): - ok = await self._available(cfg.cmd) - has_key = bool(cfg.env_key and os.environ.get(cfg.env_key)) - key_status = "โœ“ key" if has_key else "โ—‹ no key" - status = f"โœ“ ({key_status})" if ok else "โœ— not found" - lines.append(f" {agent}: {status}") - - # Anthropic-compatible agents - lines.append(" Anthropic-compatible (via claude):") - claude_ok = await self._available("claude") - for agent, cfg in sorted( - ANTHROPIC_COMPAT_AGENTS.items(), key=lambda x: x[1].priority - ): - has_key = bool(cfg.auth_env and os.environ.get(cfg.auth_env)) - if claude_ok and has_key: - status = f"โœ“ ready ({cfg.model})" - elif claude_ok: - status = f"โ—‹ need {cfg.auth_env}" - else: - status = "โœ— need claude CLI" - lines.append(f" {agent}: {status}") - - return "\n".join(lines) - - async def _available(self, cmd: str) -> bool: - """Check if command is available.""" - try: - proc = await asyncio.create_subprocess_exec( - cmd, - "--version", - stdout=asyncio.subprocess.DEVNULL, - stderr=asyncio.subprocess.DEVNULL, - ) - await asyncio.wait_for(proc.wait(), timeout=5) - return proc.returncode == 0 - except Exception: - return False - - async def _exec( - self, agent: str, prompt: str, cwd: Optional[str], timeout: int - ) -> Result: - """Execute single agent with auto-backgrounding. - - Supports two modes: - - CLI mode (api_type="cli"): Spawn CLI subprocess - - API mode (api_type="openai"|"anthropic"): Direct HTTP calls - """ - if agent not in AGENTS: - return Result( - agent=agent, - prompt=prompt, - output="", - ok=False, - error=f"Unknown agent: {agent}", - ) - - cfg = AGENTS[agent] - - # Route to API mode if endpoint is configured - if cfg.endpoint and cfg.api_type != "cli": - return await self._exec_api(agent, cfg, prompt, timeout) - - # CLI mode: Build command with YOLO flags and max_turns - full_cmd = [cfg.cmd] - - # For claude CLI, add output format first - if cfg.cmd == "claude": - full_cmd.extend(["--output-format", "text"]) - # Add max turns if configured (default 999) - if cfg.max_turns and cfg.max_turns != 999: - full_cmd.extend(["--max-turns", str(cfg.max_turns)]) - # Resume session if specified - if cfg.session_id: - full_cmd.extend(["--resume", cfg.session_id]) - - # Add model override - if cfg.model: - full_cmd.extend(["--model", cfg.model]) - - # For Anthropic-compatible APIs - if cfg.base_url: - full_cmd.append("--dangerously-skip-permissions") - - # Add configured args (includes YOLO flags) - full_cmd.extend(cfg.args) - - # Add max_turns for agents that support it (if not already in args) - if ( - cfg.max_turns - and "--max-turns" not in cfg.args - and "--max-session-turns" not in str(cfg.args) - ): - if cfg.cmd in ("vibe",): - full_cmd.extend(["--max-turns", str(cfg.max_turns)]) - elif cfg.cmd in ("qwen",): - full_cmd.append(f"--max-session-turns={cfg.max_turns}") - - # Add system prompt if configured (claude CLI only) - if cfg.system_prompt and cfg.cmd == "claude": - full_cmd.extend(["--append-system-prompt", cfg.system_prompt]) - - full_cmd.append(prompt) - - # Build environment - env = os.environ.copy() - env["OTEL_SDK_DISABLED"] = "true" # Disable OpenTelemetry noise - env.update(self._mcp_env) - env["HANZO_AGENT_PARENT"] = "true" - env["HANZO_AGENT_NAME"] = agent - - # Set Anthropic-compatible API overrides - if cfg.base_url: - env["ANTHROPIC_BASE_URL"] = cfg.base_url - if cfg.auth_env and os.environ.get(cfg.auth_env): - env["ANTHROPIC_AUTH_TOKEN"] = os.environ[cfg.auth_env] - - # Get shared ProcessManager for auto-backgrounding - pm = ProcessManager() - process_id = f"agent_{agent}_{uuid.uuid4().hex[:8]}" - log_file = await pm.create_log_file(process_id) - - start = time.time() - try: - proc = await asyncio.create_subprocess_exec( - *full_cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.STDOUT, # Merge stderr into stdout for logging - cwd=cwd or os.getcwd(), - env=env, - ) - - # Track process immediately for ps visibility - pm.add_process(process_id, proc, str(log_file)) - - # Read output with timeout, auto-background if exceeded - output_lines: List[str] = [] - - async def read_output(): - if proc.stdout: - async for line in proc.stdout: - line_str = line.decode("utf-8", errors="replace") - output_lines.append(line_str) - await append_file(log_file, line_str) - - read_task = asyncio.create_task(read_output()) - wait_task = asyncio.create_task(proc.wait()) - - done, pending = await asyncio.wait( - [read_task, wait_task], - timeout=timeout, - return_when=asyncio.FIRST_COMPLETED, - ) - - if wait_task in done: - # Process completed - return_code = await wait_task - try: - await asyncio.wait_for(read_task, timeout=1.0) - except asyncio.TimeoutError: - read_task.cancel() - - ms = int((time.time() - start) * 1000) - output = "".join(output_lines) - pm.remove_process(process_id) - - if return_code != 0: - return Result( - agent=agent, - prompt=prompt, - output=output, - ok=False, - error=f"Exit code {return_code}", - ms=ms, - ) - return Result(agent=agent, prompt=prompt, output=output, ok=True, ms=ms) - - # Timeout - auto-background - ms = int((time.time() - start) * 1000) - partial = "".join(output_lines) - - # Write backgrounding message to log - await append_file( - log_file, - f"\n[agent] Backgrounded after {timeout}s timeout\n" - f"[agent] Process ID: {process_id}\n" - f"[agent] PID: {proc.pid}\n", - ) - - bg_msg = ( - f"[backgrounded] Agent {agent} running in background.\n" - f"Process ID: {process_id}\n" - f"Log file: {log_file}\n\n" - f"Use 'ps --logs {process_id}' to view full output\n" - f"Use 'ps --kill {process_id}' to stop the process\n" - ) - if partial: - bg_msg += f"\n=== Partial output ===\n{partial[:500]}{'...' if len(partial) > 500 else ''}" - - return Result( - agent=agent, - prompt=prompt, - output=bg_msg, - ok=True, - error=f"backgrounded:{process_id}", - ms=ms, - ) - - except FileNotFoundError: - pm.remove_process(process_id) - return Result( - agent=agent, - prompt=prompt, - output="", - ok=False, - error=f"{cfg.cmd} not found", - ) - except Exception as e: - pm.remove_process(process_id) - return Result(agent=agent, prompt=prompt, output="", ok=False, error=str(e)) - - async def _exec_api( - self, agent: str, cfg: AgentConfig, prompt: str, timeout: int - ) -> Result: - """Execute agent via direct API call (OpenAI or Anthropic format). - - Supports: - - api_type="openai": OpenAI-compatible /v1/chat/completions - - api_type="anthropic": Anthropic /v1/messages - """ - if not HAS_HTTPX: - return Result( - agent=agent, - prompt=prompt, - output="", - ok=False, - error="httpx not installed. Run: pip install httpx", - ) - - start = time.time() - - # Get auth token - auth_token = None - if cfg.auth_env: - auth_token = os.environ.get(cfg.auth_env) - if not auth_token and cfg.env_key: - auth_token = os.environ.get(cfg.env_key) - - if not auth_token: - return Result( - agent=agent, - prompt=prompt, - output="", - ok=False, - error=f"No API key. Set {cfg.auth_env or cfg.env_key}", - ) - - endpoint = cfg.endpoint - if not endpoint: - return Result( - agent=agent, - prompt=prompt, - output="", - ok=False, - error="No endpoint configured for API mode", - ) - - try: - async with httpx.AsyncClient(timeout=timeout) as client: - if cfg.api_type == "openai": - # OpenAI-compatible format - messages = [] - if cfg.system_prompt: - messages.append( - {"role": "system", "content": cfg.system_prompt} - ) - messages.append({"role": "user", "content": prompt}) - - payload = { - "model": cfg.model or "gpt-4", - "messages": messages, - "max_tokens": 4096, - } - - resp = await client.post( - endpoint, - json=payload, - headers={ - "Authorization": f"Bearer {auth_token}", - "Content-Type": "application/json", - }, - ) - - if resp.status_code != 200: - return Result( - agent=agent, - prompt=prompt, - output="", - ok=False, - error=f"API error {resp.status_code}: {resp.text[:200]}", - ) - - data = resp.json() - output = ( - data.get("choices", [{}])[0] - .get("message", {}) - .get("content", "") - ) - - elif cfg.api_type == "anthropic": - # Anthropic format - messages = [{"role": "user", "content": prompt}] - - payload = { - "model": cfg.model or "claude-3-5-sonnet-20241022", - "max_tokens": 4096, - "messages": messages, - } - - if cfg.system_prompt: - payload["system"] = cfg.system_prompt - - resp = await client.post( - endpoint, - json=payload, - headers={ - "x-api-key": auth_token, - "anthropic-version": "2023-06-01", - "Content-Type": "application/json", - }, - ) - - if resp.status_code != 200: - return Result( - agent=agent, - prompt=prompt, - output="", - ok=False, - error=f"API error {resp.status_code}: {resp.text[:200]}", - ) - - data = resp.json() - content = data.get("content", []) - output = "".join( - c.get("text", "") for c in content if c.get("type") == "text" - ) - - else: - return Result( - agent=agent, - prompt=prompt, - output="", - ok=False, - error=f"Unknown api_type: {cfg.api_type}", - ) - - ms = int((time.time() - start) * 1000) - return Result(agent=agent, prompt=prompt, output=output, ok=True, ms=ms) - - except httpx.TimeoutException: - ms = int((time.time() - start) * 1000) - return Result( - agent=agent, - prompt=prompt, - output="", - ok=False, - error=f"Request timeout after {timeout}s", - ms=ms, - ) - except Exception as e: - ms = int((time.time() - start) * 1000) - return Result( - agent=agent, prompt=prompt, output="", ok=False, error=str(e), ms=ms - ) - - async def _run( - self, agent: str, prompt: Optional[str], cwd: Optional[str], timeout: int - ) -> str: - """Run single agent.""" - if not prompt: - return "Error: prompt required" - - result = await self._exec(agent, prompt, cwd, timeout) - - if result.ok: - return f"[{agent}] {result.output}" - return f"[{agent}] Error: {result.error}\n{result.output}" - - async def _dag( - self, tasks: List[Dict], name: Optional[str], cwd: Optional[str], timeout: int - ) -> str: - """Execute DAG with dependencies. - - Tasks: [{id, prompt, agent?, after?: [ids]}] - Uses topological sort, executes in waves. - Injects {dep_id} outputs into prompts. - """ - if not tasks: - return "Error: tasks required" - - agent = name or self._default_agent() - - # Build dependency graph - graph: Dict[str, Dict] = {} - for t in tasks: - tid = t.get("id", str(len(graph))) - graph[tid] = { - "prompt": t.get("prompt", ""), - "agent": t.get("agent", agent), - "after": set(t.get("after", [])), - "done": False, - "result": None, - } - - results: List[Result] = [] - outputs: Dict[str, str] = {} - - # Execute in waves (topological order) - while True: - # Find ready tasks (dependencies satisfied) - ready = [ - tid - for tid, task in graph.items() - if not task["done"] and task["after"].issubset(set(outputs.keys())) - ] - - if not ready: - # Check for cycles or completion - pending = [tid for tid, task in graph.items() if not task["done"]] - if pending: - return f"Error: Dependency cycle or missing deps: {pending}" - break - - # Execute wave in parallel - wave_tasks = [] - for tid in ready: - task = graph[tid] - # Inject dependency outputs into prompt - prompt = task["prompt"] - for dep_id, dep_out in outputs.items(): - prompt = prompt.replace(f"{{{dep_id}}}", dep_out) - wave_tasks.append((tid, task["agent"], prompt)) - - wave_results = await asyncio.gather( - *[ - self._exec(task_agent, prompt, cwd, timeout) - for tid, task_agent, prompt in wave_tasks - ] - ) - - for (tid, _, _), result in zip(wave_tasks, wave_results, strict=False): - result.id = tid - results.append(result) - outputs[tid] = result.output - graph[tid]["done"] = True - graph[tid]["result"] = result - - # Format results - lines = [f"DAG completed: {len(results)} tasks"] - for r in results: - status = "โœ“" if r.ok else "โœ—" - lines.append(f" {status} {r.id}: {r.agent} ({r.ms}ms)") - if not r.ok and r.error: - lines.append(f" Error: {r.error}") - - lines.append("") - lines.append("Outputs:") - for r in results: - lines.append(f"--- {r.id} ---") - lines.append(r.output[:500] + ("..." if len(r.output) > 500 else "")) - - return "\n".join(lines) - - async def _swarm( - self, - items: List[str], - template: str, - name: Optional[str], - max_concurrent: int, - cwd: Optional[str], - timeout: int, - ) -> str: - """Distribute work across agents. - - Each item processed once. Uses {item} substitution. - Semaphore controls max concurrency. - """ - if not items: - return "Error: items required" - if not template: - return "Error: template required (use {item} for substitution)" - - agent = name or self._default_agent() - sem = asyncio.Semaphore(max_concurrent) - - async def process(item: str) -> Result: - async with sem: - prompt = template.replace("{item}", item) - result = await self._exec(agent, prompt, cwd, timeout) - result.item = item - return result - - start = time.time() - results = await asyncio.gather(*[process(item) for item in items]) - elapsed = time.time() - start - - ok = sum(1 for r in results if r.ok) - fail = len(results) - ok - - lines = [ - f"Swarm completed: {len(items)} items in {elapsed:.1f}s", - f" Agent: {agent}", - f" Success: {ok}, Failed: {fail}", - f" Concurrency: {max_concurrent}", - ] - - if fail > 0: - lines.append("") - lines.append("Failures:") - for r in results: - if not r.ok: - lines.append(f" โœ— {r.item}: {r.error}") - - return "\n".join(lines) - - async def _consensus( - self, - prompt: str, - agents: List[str], - rounds: int, - k: int, - alpha: float, - beta_1: float, - beta_2: float, - cwd: Optional[str], - timeout: int, - ) -> str: - """Metastable consensus with agent-to-agent MCP communication. - - https://github.com/luxfi/consensus - - Each agent gets a system prompt enabling hanzo-mcp so they can - query each other during consensus rounds. - """ - if not prompt: - return "Error: prompt required" - if not agents: - return "Error: agents required" - - if not HAS_CONSENSUS: - return "Error: hanzo-consensus not installed. Run: pip install hanzo-tools-agent[consensus]" - - # Build consensus prompt with MCP context - consensus_prompt = f"""{CONSENSUS_SYSTEM_PROMPT} - -Participants in this consensus: {", ".join(agents)} -Rounds: {rounds}, Sample size: {k} - -Question: {prompt} - -Provide your reasoned response. You may use the 'agent' tool to query other participants.""" - - # Executor adapter - adds system prompt for MCP communication - async def execute(agent_id: str, agent_prompt: str) -> ConsensusResult: - # Wrap prompt with consensus context - full_prompt = f"{agent_prompt}\n\n[Consensus context: round in progress, other agents: {', '.join(a for a in agents if a != agent_id)}]" - result = await self._exec(agent_id, full_prompt, cwd, timeout) - return ConsensusResult( - id=result.agent, - output=result.output, - ok=result.ok, - error=result.error, - ms=result.ms, - ) - - start = time.time() - state = await run_consensus( - prompt=consensus_prompt, - participants=agents, - execute=execute, - rounds=rounds, - k=k, - alpha=alpha, - beta_1=beta_1, - beta_2=beta_2, - ) - elapsed = time.time() - start - - # Final synthesis by winner agent - synthesis = state.synthesis or "" - if state.winner and state.finalized: - # Have winner agent provide final summary - summary_prompt = f"""Consensus achieved. You ({state.winner}) are the winner. - -Original question: {prompt} - -Synthesize the final answer based on the consensus discussion. -Be concise but comprehensive.""" - - summary_result = await self._exec( - state.winner, summary_prompt, cwd, timeout // 2 - ) - if summary_result.ok: - synthesis = summary_result.output - - return f"[Metastable] {elapsed:.1f}s, winner: {state.winner}, finalized: {state.finalized}\n\n{synthesis}" - - async def _dispatch( - self, tasks: List[Dict], cwd: Optional[str], timeout: int - ) -> str: - """Execute different agents for different tasks in parallel. - - Tasks: [{agent, prompt}] - """ - if not tasks: - return "Error: tasks required" - - async def run_task(t: Dict) -> Result: - agent = t.get("agent", self._default_agent()) - prompt = t.get("prompt", "") - return await self._exec(agent, prompt, cwd, timeout) - - results = await asyncio.gather(*[run_task(t) for t in tasks]) - - lines = [f"Dispatched: {len(tasks)} tasks"] - for i, r in enumerate(results): - status = "โœ“" if r.ok else "โœ—" - lines.append(f" {status} Task {i + 1}: {r.agent} ({r.ms}ms)") - - lines.append("") - for i, r in enumerate(results): - lines.append(f"--- Task {i + 1} ({r.agent}) ---") - if r.ok: - lines.append(r.output[:500] + ("..." if len(r.output) > 500 else "")) - else: - lines.append(f"Error: {r.error}") - - return "\n".join(lines) - - def register(self, mcp_server: FastMCP) -> None: - """Register with MCP server.""" - tool = self - - @mcp_server.tool() - async def agent( - action: Action = "run", - name: Annotated[ - Optional[str], - Field(description="Agent: claude, codex, gemini, grok, qwen, dev"), - ] = None, - prompt: Annotated[ - Optional[str], Field(description="Prompt for run/consensus") - ] = None, - cwd: Annotated[ - Optional[str], Field(description="Working directory") - ] = None, - timeout: Annotated[int, Field(description="Timeout seconds")] = 300, - tasks: Annotated[ - Optional[List[Dict]], Field(description="Tasks for dag/dispatch") - ] = None, - items: Annotated[ - Optional[List[str]], Field(description="Items for swarm") - ] = None, - template: Annotated[ - Optional[str], Field(description="Template for swarm ({item})") - ] = None, - max_concurrent: Annotated[ - int, Field(description="Max concurrency for swarm") - ] = 100, - agents: Annotated[ - Optional[List[str]], Field(description="Agents for consensus") - ] = None, - rounds: Annotated[int, Field(description="Consensus rounds")] = 3, - k: Annotated[int, Field(description="Sample size per round")] = 3, - alpha: Annotated[float, Field(description="Agreement threshold")] = 0.6, - beta_1: Annotated[float, Field(description="Preference threshold")] = 0.5, - beta_2: Annotated[float, Field(description="Decision threshold")] = 0.8, - ctx: MCPContext = None, - ) -> str: - """Multi-agent orchestration: run, dag, swarm, consensus, dispatch. - - Consensus: https://github.com/luxfi/consensus - """ - return await tool.call( - ctx, - action=action, - name=name, - prompt=prompt, - cwd=cwd, - timeout=timeout, - tasks=tasks, - items=items, - template=template, - max_concurrent=max_concurrent, - agents=agents, - rounds=rounds, - k=k, - alpha=alpha, - beta_1=beta_1, - beta_2=beta_2, - ) diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/clarification_protocol.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/clarification_protocol.py deleted file mode 100644 index dfe167ef9..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/clarification_protocol.py +++ /dev/null @@ -1,225 +0,0 @@ -"""Clarification protocol for agent-to-mainloop communication. - -This module provides a protocol for agents to request clarification -from the main loop without human intervention. -""" - -import json -from enum import Enum -from typing import Any, Dict, List, Optional -from dataclasses import dataclass - - -class ClarificationType(Enum): - """Types of clarification requests.""" - - AMBIGUOUS_INSTRUCTION = "ambiguous_instruction" - MISSING_CONTEXT = "missing_context" - MULTIPLE_OPTIONS = "multiple_options" - CONFIRMATION_NEEDED = "confirmation_needed" - ADDITIONAL_INFO = "additional_info" - - -@dataclass -class ClarificationRequest: - """A request for clarification from an agent.""" - - agent_id: str - request_type: ClarificationType - question: str - context: Dict[str, Any] - options: Optional[List[str]] = None - - def to_json(self) -> str: - """Convert to JSON for transport.""" - return json.dumps( - { - "agent_id": self.agent_id, - "request_type": self.request_type.value, - "question": self.question, - "context": self.context, - "options": self.options, - } - ) - - @classmethod - def from_json(cls, data: str) -> "ClarificationRequest": - """Create from JSON string.""" - obj = json.loads(data) - return cls( - agent_id=obj["agent_id"], - request_type=ClarificationType(obj["request_type"]), - question=obj["question"], - context=obj["context"], - options=obj.get("options"), - ) - - -@dataclass -class ClarificationResponse: - """A response to a clarification request.""" - - request_id: str - answer: str - additional_context: Optional[Dict[str, Any]] = None - - def to_json(self) -> str: - """Convert to JSON for transport.""" - return json.dumps( - { - "request_id": self.request_id, - "answer": self.answer, - "additional_context": self.additional_context, - } - ) - - -class ClarificationHandler: - """Handles clarification requests from agents.""" - - def __init__(self): - self.pending_requests: Dict[str, ClarificationRequest] = {} - self.request_counter = 0 - - def create_request( - self, - agent_id: str, - request_type: ClarificationType, - question: str, - context: Dict[str, Any], - options: Optional[List[str]] = None, - ) -> str: - """Create a new clarification request. - - Returns: - Request ID for tracking - """ - request = ClarificationRequest( - agent_id=agent_id, - request_type=request_type, - question=question, - context=context, - options=options, - ) - - request_id = f"clarify_{self.request_counter}" - self.request_counter += 1 - self.pending_requests[request_id] = request - - return request_id - - def handle_request(self, request: ClarificationRequest) -> ClarificationResponse: - """Handle a clarification request automatically. - - This method implements automatic clarification resolution - based on context and common patterns. - """ - request_id = f"clarify_{len(self.pending_requests)}" - - # Handle different types of clarification - if request.request_type == ClarificationType.AMBIGUOUS_INSTRUCTION: - # Try to clarify based on context - if "file_path" in request.context: - if request.context["file_path"].endswith(".go"): - answer = "For Go files, ensure you add imports in the correct format and handle both single import and import block cases." - elif request.context["file_path"].endswith(".py"): - answer = "For Python files, add imports at the top of the file after any module docstring." - else: - answer = "Add imports according to the language's conventions." - else: - answer = "Proceed with the most reasonable interpretation based on the context." - - elif request.request_type == ClarificationType.MISSING_CONTEXT: - # Provide additional context based on what's missing - if "import_path" in request.question.lower(): - answer = "Use the standard import path based on the project structure. Check existing imports in similar files for patterns." - elif "format" in request.question.lower(): - answer = "Match the existing code style in the file. Use the same indentation and formatting patterns." - else: - answer = "Analyze the surrounding code and project structure to infer the missing information." - - elif request.request_type == ClarificationType.MULTIPLE_OPTIONS: - # Choose the best option based on context - if request.options: - # Simple heuristic: choose the first option that seems most standard - for option in request.options: - if "common" in option or "standard" in option: - answer = f"Choose option: {option}" - break - else: - answer = f"Choose option: {request.options[0]}" - else: - answer = "Choose the most conventional approach based on the codebase patterns." - - elif request.request_type == ClarificationType.CONFIRMATION_NEEDED: - # Auto-confirm safe operations - if "add import" in request.question.lower(): - answer = "Yes, proceed with adding the import." - elif "multi_edit" in request.question.lower(): - answer = "Yes, use multi_edit for efficiency." - else: - answer = "Proceed if the operation is safe and reversible." - - else: # ADDITIONAL_INFO - answer = "Continue with available information and make reasonable assumptions based on context." - - return ClarificationResponse( - request_id=request_id, - answer=answer, - additional_context={"auto_resolved": True}, - ) - - -class AgentClarificationMixin: - """Mixin for agents to request clarification.""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.clarification_handler = ClarificationHandler() - self.clarification_count = 0 - self.max_clarifications = 1 # Allow up to 1 clarification per task - - async def request_clarification( - self, - request_type: ClarificationType, - question: str, - context: Dict[str, Any], - options: Optional[List[str]] = None, - ) -> str: - """Request clarification from the main loop. - - Args: - request_type: Type of clarification needed - question: The question to ask - context: Relevant context for the question - options: Optional list of choices - - Returns: - The clarification response - - Raises: - RuntimeError: If clarification limit exceeded - """ - if self.clarification_count >= self.max_clarifications: - raise RuntimeError("Clarification limit exceeded") - - self.clarification_count += 1 - - # Create request - request = ClarificationRequest( - agent_id=getattr(self, "agent_id", "unknown"), - request_type=request_type, - question=question, - context=context, - options=options, - ) - - # In real implementation, this would communicate with main loop - # For now, use the automatic handler - response = self.clarification_handler.handle_request(request) - - return response.answer - - def format_clarification_in_output(self, question: str, answer: str) -> str: - """Format clarification exchange for output.""" - return f"\n๐Ÿค” Clarification needed: {question}\nโœ… Resolved: {answer}\n" diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/clarification_tool.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/clarification_tool.py deleted file mode 100644 index 589fbb9ad..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/clarification_tool.py +++ /dev/null @@ -1,74 +0,0 @@ -"""Clarification tool for agents to request information from main loop.""" - -from typing import Any, Dict, List, Optional, override - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout - - -class ClarificationTool(BaseTool): - """Tool for agents to request clarification from the main loop.""" - - name = "request_clarification" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Request clarification from the main loop (not the human user). - -Use this when you encounter: -- Ambiguous instructions that could be interpreted multiple ways -- Missing context needed to complete the task -- Multiple valid options where you need guidance -- Operations that need confirmation before proceeding -- Need for additional information not provided - -Parameters: -- type: Type of clarification (AMBIGUOUS_INSTRUCTION, MISSING_CONTEXT, MULTIPLE_OPTIONS, CONFIRMATION_NEEDED, ADDITIONAL_INFO) -- question: Clear, specific question to ask -- context: Relevant context (e.g., file_path, current_operation, etc.) -- options: Optional list of possible choices (for MULTIPLE_OPTIONS type) - -You can only use this ONCE per task, so make it count! - -Example: -request_clarification( - type="MISSING_CONTEXT", - question="What is the correct import path for the common package?", - context={"file_path": "/path/to/file.go", "undefined_symbol": "common"}, - options=["github.com/luxfi/node/common", "github.com/project/common"] -)""" - - @auto_timeout("clarification") - async def call( - self, - ctx: MCPContext, - type: str, - question: str, - context: Dict[str, Any], - options: Optional[List[str]] = None, - ) -> str: - """Delegate to AgentTool for actual implementation. - - This method provides the interface, but the actual clarification logic - is handled by the AgentTool's execution framework. - """ - # This tool is handled specially in the agent execution - return f"Clarification request: {question}" - - def register(self, server: FastMCP) -> None: - """Register the tool with the MCP server.""" - tool_self = self - - @server.tool(name=self.name, description=self.description) - async def request_clarification( - ctx: MCPContext, - type: str, - question: str, - context: Dict[str, Any], - options: Optional[List[str]] = None, - ) -> str: - return await tool_self.call(ctx, type, question, context, options) diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/claude_cli_tool.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/claude_cli_tool.py deleted file mode 100644 index f19bad2bf..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/claude_cli_tool.py +++ /dev/null @@ -1,127 +0,0 @@ -"""Claude Code CLI agent tool. - -This tool provides integration with the Claude Code CLI (claude command), -allowing programmatic execution of Claude for code tasks. -""" - -from typing import List, Optional, final, override - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import PermissionManager - -from .code_auth import get_latest_claude_model -from .cli_agent_base import CLIAgentBase - - -@final -class ClaudeCLITool(CLIAgentBase): - """Tool for executing Claude Code CLI.""" - - def __init__( - self, - permission_manager: Optional[PermissionManager] = None, - model: Optional[str] = None, - **kwargs, - ): - """Initialize Claude CLI tool. - - Args: - permission_manager: Permission manager for access control - model: Optional model override (defaults to latest Sonnet) - **kwargs: Additional arguments - """ - super().__init__( - permission_manager=permission_manager, - command_name="claude", - provider_name="Claude Code", - default_model=model or get_latest_claude_model(), - env_vars=["ANTHROPIC_API_KEY", "CLAUDE_API_KEY"], - **kwargs, - ) - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "claude_cli" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Execute Claude Code CLI for advanced code tasks. - -This tool runs the Claude Code CLI (claude command) for code generation, -editing, analysis, and other programming tasks. It uses the latest -Claude 3.5 Sonnet model by default. - -Features: -- Direct access to Claude's coding capabilities -- File-aware context and editing -- Interactive code generation -- Supports all Claude Code CLI features - -Usage: -claude_cli(prompts="Fix the bug in main.py and add tests") -claude_cli(prompts="Refactor this class to use dependency injection", model="claude-3-opus-20240229") - -Requirements: -- Claude Code CLI must be installed -- ANTHROPIC_API_KEY or CLAUDE_API_KEY environment variable -""" - - @override - def get_cli_args(self, prompt: str, **kwargs) -> List[str]: - """Get CLI arguments for Claude. - - Args: - prompt: The prompt to send - **kwargs: Additional arguments (model, temperature, etc.) - - Returns: - List of command arguments - """ - args = [] - - # Add model if specified - model = kwargs.get("model", self.default_model) - if model: - args.extend(["--model", model]) - - # Add temperature if specified - if "temperature" in kwargs: - args.extend(["--temperature", str(kwargs["temperature"])]) - - # Add max tokens if specified - if "max_tokens" in kwargs: - args.extend(["--max-tokens", str(kwargs["max_tokens"])]) - - # Add the prompt - args.append(prompt) - - return args - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def claude_cli( - ctx: MCPContext, - prompts: str, - model: Optional[str] = None, - temperature: Optional[float] = None, - max_tokens: Optional[int] = None, - working_dir: Optional[str] = None, - ) -> str: - return await tool_self.call( - ctx, - prompts=prompts, - model=model, - temperature=temperature, - max_tokens=max_tokens, - working_dir=working_dir, - ) diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/claude_desktop_auth.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/claude_desktop_auth.py deleted file mode 100644 index 4af81307f..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/claude_desktop_auth.py +++ /dev/null @@ -1,516 +0,0 @@ -from hanzo_tools.core import auto_timeout - -"""Claude Desktop authentication management. - -This module provides tools to automate Claude Desktop login/logout, -manage separate accounts for swarm agents, and handle authentication flows. -""" - -import os -import json -import time -import asyncio -import webbrowser -from typing import Any, Dict, Tuple, Optional -from pathlib import Path -from urllib.parse import parse_qs - -from hanzo_tools.core import BaseTool, create_tool_context - - -class ClaudeDesktopAuth: - """Manages Claude Desktop authentication.""" - - # Claude Desktop paths - CLAUDE_APP_MAC = "/Applications/Claude.app" - CLAUDE_CONFIG_DIR = Path.home() / ".claude" - CLAUDE_SESSION_FILE = CLAUDE_CONFIG_DIR / "session.json" - CLAUDE_ACCOUNTS_FILE = CLAUDE_CONFIG_DIR / "accounts.json" - - # Authentication endpoints - CLAUDE_LOGIN_URL = "https://claude.ai/login" - CLAUDE_API_URL = "https://api.claude.ai" - - def __init__(self): - """Initialize Claude Desktop auth manager.""" - self.ensure_config_dir() - - def ensure_config_dir(self): - """Ensure Claude config directory exists.""" - self.CLAUDE_CONFIG_DIR.mkdir(exist_ok=True) - - def is_claude_installed(self) -> bool: - """Check if Claude Desktop is installed (sync check for app path).""" - if os.path.exists(self.CLAUDE_APP_MAC): - return True - # For command check, use async version - return False - - async def is_claude_installed_async(self) -> bool: - """Check if Claude Desktop is installed (async version).""" - if os.path.exists(self.CLAUDE_APP_MAC): - return True - - # Check if claude command is available - try: - process = await asyncio.create_subprocess_exec( - "which", - "claude", - stdout=asyncio.subprocess.DEVNULL, - stderr=asyncio.subprocess.DEVNULL, - ) - await asyncio.wait_for(process.wait(), timeout=5) - return process.returncode == 0 - except Exception: - return False - - def is_logged_in(self, account: Optional[str] = None) -> bool: - """Check if Claude Desktop is logged in. - - Args: - account: Optional account identifier to check - - Returns: - True if logged in - """ - if not self.CLAUDE_SESSION_FILE.exists(): - return False - - try: - with open(self.CLAUDE_SESSION_FILE, "r") as f: - session = json.load(f) - - # Check if session is valid - if not session.get("access_token"): - return False - - # Check expiry if available - if "expires_at" in session: - if time.time() > session["expires_at"]: - return False - - # Check specific account if requested - if account and session.get("account") != account: - return False - - return True - except Exception: - return False - - def get_current_account(self) -> Optional[str]: - """Get the currently logged in account.""" - if not self.is_logged_in(): - return None - - try: - with open(self.CLAUDE_SESSION_FILE, "r") as f: - session = json.load(f) - return session.get("account", session.get("email")) - except Exception: - return None - - async def login_interactive( - self, account: Optional[str] = None, headless: bool = False - ) -> Tuple[bool, str]: - """Login to Claude Desktop interactively. - - Args: - account: Optional account email/identifier - headless: Whether to run in headless mode - - Returns: - Tuple of (success, message) - """ - # Check if already logged in - if self.is_logged_in(account): - current = self.get_current_account() - return True, f"Already logged in as {current}" - - # Start login flow - if headless: - return await self._login_headless(account) - else: - return await self._login_browser(account) - - async def _login_browser(self, account: Optional[str]) -> Tuple[bool, str]: - """Login using browser flow.""" - # Generate state for OAuth-like flow - state = os.urandom(16).hex() - - # Create callback server - callback_port = 9876 - auth_code = None - - async def handle_callback(reader, writer): - """Handle OAuth callback.""" - nonlocal auth_code - - # Read request - request = await reader.read(1024) - request_str = request.decode() - - # Extract code from query params - if "GET /" in request_str: - path = request_str.split(" ")[1] - if "?code=" in path: - query = path.split("?")[1] - params = parse_qs(query) - if "code" in params: - auth_code = params["code"][0] - - # Send response - response = b"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" - response += b"

Authentication successful!

" - response += b"

You can close this window.

" - writer.write(response) - await writer.drain() - writer.close() - - # Start callback server - server = await asyncio.start_server(handle_callback, "localhost", callback_port) - - # Build login URL - login_url = f"{self.CLAUDE_LOGIN_URL}?callback=http://localhost:{callback_port}&state={state}" - if account: - login_url += f"&login_hint={account}" - - # Open browser - print(f"Opening browser for Claude login...") - print(f"URL: {login_url}") - webbrowser.open(login_url) - - # Wait for callback (timeout after 2 minutes) - try: - start_time = time.time() - while not auth_code and (time.time() - start_time) < 120: - await asyncio.sleep(0.5) - - if auth_code: - # Exchange code for session - success = await self._exchange_code_for_session(auth_code, account) - if success: - return True, f"Successfully logged in as {account or 'default'}" - else: - return False, "Failed to exchange auth code for session" - else: - return False, "Login timeout - no auth code received" - - finally: - server.close() - await server.wait_closed() - - async def _login_headless(self, account: Optional[str]) -> Tuple[bool, str]: - """Login in headless mode using TTY automation.""" - # Headless login requires browser automation or OAuth flow - # This is not supported in CLI mode for security reasons - return ( - False, - "Headless login requires browser. Use 'claude login' with --browser flag", - ) - - async def _exchange_code_for_session( - self, code: str, account: Optional[str] - ) -> bool: - """Exchange auth code for session token.""" - # Create a session from the OAuth code - import hashlib - - # Generate a secure session token from the auth code - session_token = hashlib.sha256(f"{code}:{time.time()}".encode()).hexdigest() - - session = { - "access_token": session_token, - "account": account or "default", - "email": account, - "expires_at": time.time() + 3600 * 24, # 24 hours - "created_at": time.time(), - "auth_type": "oauth", - } - - try: - with open(self.CLAUDE_SESSION_FILE, "w") as f: - json.dump(session, f, indent=2) - return True - except Exception: - return False - - async def logout(self, account: Optional[str] = None) -> Tuple[bool, str]: - """Logout from Claude Desktop. - - Args: - account: Optional account to logout (if multiple accounts) - - Returns: - Tuple of (success, message) - """ - current = self.get_current_account() - - if not current: - return True, "No active session to logout" - - if account and current != account: - return False, f"Not logged in as {account} (current: {current})" - - try: - # Remove session file - if self.CLAUDE_SESSION_FILE.exists(): - self.CLAUDE_SESSION_FILE.unlink() - - # Clear any cached credentials - await self._clear_credentials_cache() - - return True, f"Successfully logged out {current}" - except Exception as e: - return False, f"Logout failed: {str(e)}" - - async def _clear_credentials_cache(self): - """Clear any cached credentials (async).""" - # Clear keychain on macOS - if os.path.exists("/usr/bin/security"): - try: - process = await asyncio.create_subprocess_exec( - "/usr/bin/security", - "delete-generic-password", - "-s", - "claude.ai", - "-a", - "claude-desktop", - stdout=asyncio.subprocess.DEVNULL, - stderr=asyncio.subprocess.DEVNULL, - ) - await asyncio.wait_for(process.wait(), timeout=10) - except Exception: - pass - - def switch_account(self, account: str) -> Tuple[bool, str]: - """Switch to a different Claude account. - - Args: - account: Account identifier to switch to - - Returns: - Tuple of (success, message) - """ - # Load accounts configuration - accounts = self._load_accounts() - - if account not in accounts: - return False, f"Unknown account: {account}" - - # Save current session if any - current = self.get_current_account() - if current and current != account: - self._save_session_for_account(current) - - # Load session for new account - if self._load_session_for_account(account): - return True, f"Switched to account: {account}" - else: - return False, f"No saved session for account: {account}" - - def _load_accounts(self) -> Dict[str, Any]: - """Load accounts configuration.""" - if not self.CLAUDE_ACCOUNTS_FILE.exists(): - return {} - - try: - with open(self.CLAUDE_ACCOUNTS_FILE, "r") as f: - return json.load(f) - except Exception: - return {} - - def _save_accounts(self, accounts: Dict[str, Any]): - """Save accounts configuration.""" - with open(self.CLAUDE_ACCOUNTS_FILE, "w") as f: - json.dump(accounts, f, indent=2) - - def _save_session_for_account(self, account: str): - """Save current session for an account.""" - if not self.CLAUDE_SESSION_FILE.exists(): - return - - accounts = self._load_accounts() - - try: - with open(self.CLAUDE_SESSION_FILE, "r") as f: - session = json.load(f) - - accounts[account] = {"session": session, "saved_at": time.time()} - - self._save_accounts(accounts) - except Exception: - pass - - def _load_session_for_account(self, account: str) -> bool: - """Load saved session for an account.""" - accounts = self._load_accounts() - - if account not in accounts: - return False - - account_data = accounts[account] - if "session" not in account_data: - return False - - try: - # Restore session - session = account_data["session"] - - # Update account info - session["account"] = account - - with open(self.CLAUDE_SESSION_FILE, "w") as f: - json.dump(session, f, indent=2) - - return True - except Exception: - return False - - def create_agent_account(self, agent_id: str) -> str: - """Create a unique account identifier for an agent. - - Args: - agent_id: Unique agent identifier - - Returns: - Account identifier for the agent - """ - # Generate agent-specific account - return f"agent_{agent_id}@claude.local" - - async def ensure_agent_auth( - self, agent_id: str, force_new: bool = False - ) -> Tuple[bool, str]: - """Ensure an agent is authenticated with its own account. - - Args: - agent_id: Unique agent identifier - force_new: Force new login even if cached - - Returns: - Tuple of (success, message/account) - """ - agent_account = self.create_agent_account(agent_id) - - # Check if agent already has a session - if not force_new and self._has_saved_session(agent_account): - # Try to switch to agent account - success, msg = self.switch_account(agent_account) - if success: - return True, agent_account - - # Need to create new session for agent - # For now, we'll use the main account - # In production, this would create separate auth - current = self.get_current_account() - if current: - # Clone current session for agent - self._clone_session_for_agent(current, agent_account) - return True, agent_account - else: - return False, "No active session to clone for agent" - - def _has_saved_session(self, account: str) -> bool: - """Check if account has a saved session.""" - accounts = self._load_accounts() - return account in accounts and "session" in accounts[account] - - def _clone_session_for_agent(self, source: str, agent_account: str): - """Clone a session for an agent account.""" - # In a real implementation, this would create a sub-session - # or use delegation tokens - if self.CLAUDE_SESSION_FILE.exists(): - try: - with open(self.CLAUDE_SESSION_FILE, "r") as f: - session = json.load(f) - - # Modify for agent - session["account"] = agent_account - session["parent_account"] = source - session["is_agent"] = True - - # Save as agent session - accounts = self._load_accounts() - accounts[agent_account] = { - "session": session, - "saved_at": time.time(), - "parent": source, - } - self._save_accounts(accounts) - except Exception: - pass - - -class ClaudeDesktopAuthTool(BaseTool): - """Tool for managing Claude Desktop authentication.""" - - @property - def name(self) -> str: - return "claude_auth" - - @property - def description(self) -> str: - return """Manage Claude Desktop authentication. - -Actions: -- status: Check login status -- login: Login to Claude Desktop -- logout: Logout from Claude Desktop -- switch: Switch between accounts -- ensure_agent: Ensure agent has auth - -Usage: -claude_auth status -claude_auth login --account user@example.com -claude_auth logout -claude_auth switch agent_1 -claude_auth ensure_agent swarm_agent_1""" - - def __init__(self): - """Initialize the auth tool.""" - self.auth = ClaudeDesktopAuth() - - @auto_timeout("claude_desktop_auth") - async def call(self, ctx, action: str = "status", **kwargs) -> str: - """Execute auth action.""" - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - if action == "status": - if self.auth.is_logged_in(): - account = self.auth.get_current_account() - return f"Logged in as: {account}" - else: - return "Not logged in" - - elif action == "login": - account = kwargs.get("account") - headless = kwargs.get("headless", False) - success, msg = await self.auth.login_interactive(account, headless) - return msg - - elif action == "logout": - account = kwargs.get("account") - success, msg = await self.auth.logout(account) - return msg - - elif action == "switch": - account = kwargs.get("account") - if not account: - return "Error: account required for switch" - success, msg = self.auth.switch_account(account) - return msg - - elif action == "ensure_agent": - agent_id = kwargs.get("agent_id") - if not agent_id: - return "Error: agent_id required" - force_new = kwargs.get("force_new", False) - success, result = await self.auth.ensure_agent_auth(agent_id, force_new) - if success: - return f"Agent authenticated as: {result}" - else: - return f"Failed: {result}" - - else: - return f"Unknown action: {action}" diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/cli_agent_base.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/cli_agent_base.py deleted file mode 100644 index 4c15c6074..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/cli_agent_base.py +++ /dev/null @@ -1,201 +0,0 @@ -"""Base class for CLI-based AI agent tools. - -This provides common functionality for spawning CLI-based AI coding assistants -like Claude Code, OpenAI Codex, Google Gemini, and Grok. -""" - -import os -import shutil -import asyncio -import tempfile -from abc import abstractmethod -from typing import List, Optional - -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - PermissionManager, - auto_timeout, - create_tool_context, -) - - -class CLIAgentBase(BaseTool): - """Base class for CLI-based AI agent tools.""" - - def __init__( - self, - permission_manager: Optional[PermissionManager] = None, - command_name: str = "", - provider_name: str = "", - default_model: Optional[str] = None, - env_vars: Optional[List[str]] = None, - **kwargs, - ): - """Initialize CLI agent base. - - Args: - permission_manager: Permission manager for access control - command_name: The CLI command name (e.g., 'claude', 'openai') - provider_name: The provider name (e.g., 'Claude', 'OpenAI') - default_model: Default model to use - env_vars: List of environment variables to check for API keys - **kwargs: Additional arguments - """ - self.permission_manager = permission_manager - self.command_name = command_name - self.provider_name = provider_name - self.default_model = default_model - self.env_vars = env_vars or [] - - def is_installed(self) -> bool: - """Check if the CLI tool is installed.""" - return shutil.which(self.command_name) is not None - - def has_api_key(self) -> bool: - """Check if API key is available in environment.""" - if not self.env_vars: - return True # No API key needed - - for var in self.env_vars: - if os.environ.get(var): - return True - return False - - @abstractmethod - def get_cli_args(self, prompt: str, **kwargs) -> List[str]: - """Get CLI arguments for the specific tool. - - Args: - prompt: The prompt to send - **kwargs: Additional arguments - - Returns: - List of command arguments - """ - pass - - async def execute_cli( - self, - ctx: MCPContext, - prompt: str, - working_dir: Optional[str] = None, - timeout: int = 300, - **kwargs, - ) -> str: - """Execute the CLI command. - - Args: - ctx: MCP context - prompt: The prompt to send - working_dir: Working directory for the command - timeout: Command timeout in seconds - **kwargs: Additional arguments - - Returns: - Command output - """ - tool_ctx = create_tool_context(ctx) - - # Check if installed - if not self.is_installed(): - error_msg = ( - f"{self.provider_name} CLI ({self.command_name}) is not installed. " - ) - error_msg += f"Please install it first: https://github.com/anthropics/{self.command_name}" - await tool_ctx.error(error_msg) - return f"Error: {error_msg}" - - # Check API key if needed - if not self.has_api_key(): - error_msg = f"No API key found for {self.provider_name}. " - error_msg += f"Set one of: {', '.join(self.env_vars)}" - await tool_ctx.error(error_msg) - return f"Error: {error_msg}" - - # Get command arguments - cli_args = self.get_cli_args(prompt, **kwargs) - - # Log command - await tool_ctx.info( - f"Executing {self.provider_name}: {self.command_name} {' '.join(cli_args[:3])}..." - ) - - try: - # Create temp file for prompt if needed - with tempfile.NamedTemporaryFile( - mode="w", suffix=".txt", delete=False - ) as f: - f.write(prompt) - prompt_file = f.name - - # Some CLIs need prompt via file instead of stdin - if "--prompt-file" in cli_args: - # Substitute with actual temp file path - cli_args = [ - ( - arg.replace("--prompt-file", prompt_file) - if arg == "--prompt-file" - else arg - ) - for arg in cli_args - ] - - # Execute command - process = await asyncio.create_subprocess_exec( - self.command_name, - *cli_args, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - stdin=asyncio.subprocess.PIPE, - cwd=working_dir or os.getcwd(), - ) - - # Send prompt via stdin if not using file - if "--prompt-file" not in cli_args: - stdout, stderr = await asyncio.wait_for( - process.communicate(input=prompt.encode()), timeout=timeout - ) - else: - stdout, stderr = await asyncio.wait_for( - process.communicate(), timeout=timeout - ) - - # Clean up temp file - try: - os.unlink(prompt_file) - except Exception: - pass - - if process.returncode != 0: - error_msg = stderr.decode() if stderr else "Unknown error" - await tool_ctx.error(f"{self.provider_name} failed: {error_msg}") - return f"Error: {error_msg}" - - result = stdout.decode() - await tool_ctx.info(f"{self.provider_name} completed successfully") - return result - - except asyncio.TimeoutError: - await tool_ctx.error( - f"{self.provider_name} timed out after {timeout} seconds" - ) - return f"Error: Command timed out after {timeout} seconds" - except Exception as e: - await tool_ctx.error(f"{self.provider_name} error: {str(e)}") - return f"Error: {str(e)}" - - @auto_timeout("cli_agent_base") - async def call(self, ctx: MCPContext, prompts: str, **kwargs) -> str: - """Execute the CLI agent. - - Args: - ctx: MCP context - prompts: The prompt(s) to send - **kwargs: Additional arguments - - Returns: - Agent response - """ - return await self.execute_cli(ctx, prompts, **kwargs) diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/cli_tools.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/cli_tools.py deleted file mode 100644 index e52b92d36..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/cli_tools.py +++ /dev/null @@ -1,569 +0,0 @@ -"""CLI tool implementations for direct batch execution. - -This module provides CLI tool wrappers that can be used directly in batch operations, -including claude (cc), codex, gemini, grok, openhands (oh), hanzo-dev, cline, and aider. -""" - -from __future__ import annotations - -import os -import asyncio -from typing import ( - Any, - Dict, - List, - Unpack, - Optional, - Annotated, - TypedDict, - final, - override, -) -from pathlib import Path - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context - -from hanzo_tools.core import ( - BaseTool, - PermissionManager, - auto_timeout, - create_tool_context, -) - -# Parameter types for CLI tools -Prompt = Annotated[ - str, - Field( - description="The prompt or command to send to the CLI tool", - min_length=1, - ), -] - -Model = Annotated[ - Optional[str], - Field( - description="Optional model override for the CLI tool", - default=None, - ), -] - -WorkingDir = Annotated[ - Optional[str], - Field( - description="Working directory for the command", - default=None, - ), -] - -Timeout = Annotated[ - Optional[int], - Field( - description="Timeout in seconds for the command", - default=300, # 5 minutes default - ), -] - - -class CLIToolParams(TypedDict, total=False): - """Common parameters for CLI tools.""" - - prompt: str - model: Optional[str] - working_dir: Optional[str] - timeout: Optional[int] - - -class BaseCLITool(BaseTool): - """Base class for CLI tool implementations.""" - - def __init__( - self, - permission_manager: Optional[PermissionManager] = None, - default_model: Optional[str] = None, - api_key_env: Optional[str] = None, - ): - """Initialize CLI tool. - - Args: - permission_manager: Permission manager for access control - default_model: Default model to use - api_key_env: Environment variable name for API key - """ - self.permission_manager = permission_manager - self.default_model = default_model - self.api_key_env = api_key_env - - def get_auth_env(self) -> dict[str, str]: - """Get authentication environment variables.""" - env = os.environ.copy() - - # Add API key if configured - if self.api_key_env and self.api_key_env in os.environ: - env[self.api_key_env] = os.environ[self.api_key_env] - - # Add Hanzo API key for unified auth - if "HANZO_API_KEY" in os.environ: - env["HANZO_API_KEY"] = os.environ["HANZO_API_KEY"] - - return env - - async def execute_cli( - self, - command: list[str], - input_text: Optional[str] = None, - working_dir: Optional[str] = None, - timeout: int = 300, - ) -> str: - """Execute CLI command with proper error handling. - - Args: - command: Command and arguments - input_text: Optional stdin input - working_dir: Working directory - timeout: Timeout in seconds - - Returns: - Command output - """ - try: - # Set up environment with auth - env = self.get_auth_env() - - # Execute command - process = await asyncio.create_subprocess_exec( - *command, - stdin=asyncio.subprocess.PIPE if input_text else None, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - cwd=working_dir, - env=env, - ) - - # Send input and get output - stdout, stderr = await asyncio.wait_for( - process.communicate(input_text.encode() if input_text else None), - timeout=timeout, - ) - - # Check for errors - if process.returncode != 0: - error_msg = stderr.decode() if stderr else "Unknown error" - return f"Error: {error_msg}" - - return stdout.decode() - - except asyncio.TimeoutError: - return f"Error: Command timed out after {timeout} seconds" - except Exception as e: - return f"Error executing command: {str(e)}" - - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server. - - Args: - mcp_server: The FastMCP server instance - """ - tool_self = self # Create a reference to self for use in the closure - - @mcp_server.tool(name=self.name, description=self.description) - async def tool_wrapper( - prompt: str, - ctx: Context[Any, Any, Any], - model: Optional[str] = None, - working_dir: Optional[str] = None, - timeout: int = 300, - ) -> str: - result: str = await tool_self.call( - ctx, - prompt=prompt, - model=model, - working_dir=working_dir, - timeout=timeout, - ) - return result - - -class ClaudeCLITool(BaseCLITool): - """Claude CLI tool (also available as 'cc' alias).""" - - def __init__(self, permission_manager: Optional[PermissionManager] = None): - super().__init__( - permission_manager=permission_manager, - default_model="claude-3-5-sonnet-20241022", - api_key_env="ANTHROPIC_API_KEY", - ) - - @property - def name(self) -> str: - return "claude" - - @property - def description(self) -> str: - return "Execute Claude CLI for AI assistance using Anthropic's models" - - @auto_timeout("cli_tools") - async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str: - prompt: str = params.get("prompt", "") - model: Optional[str] = params.get("model") or self.default_model - working_dir: Optional[str] = params.get("working_dir") - timeout: int = params.get("timeout", 300) - - # Build command - command: list[str] = ["claude"] - if model: - command.extend(["--model", model]) - - # Execute - return await self.execute_cli( - command, - input_text=prompt, - working_dir=working_dir, - timeout=timeout, - ) - - -class ClaudeCodeCLITool(ClaudeCLITool): - """Claude Code CLI tool (cc alias).""" - - @property - def name(self) -> str: - return "cc" - - @property - def description(self) -> str: - return "Claude Code CLI (alias for claude)" - - -class CodexCLITool(BaseCLITool): - """OpenAI Codex/GPT-4 CLI tool.""" - - def __init__(self, permission_manager: Optional[PermissionManager] = None): - super().__init__( - permission_manager=permission_manager, - default_model="gpt-4-turbo", - api_key_env="OPENAI_API_KEY", - ) - - @property - def name(self) -> str: - return "codex" - - @property - def description(self) -> str: - return "Execute OpenAI Codex/GPT-4 CLI for code generation and AI assistance" - - @auto_timeout("cli_tools") - async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str: - prompt: str = params.get("prompt", "") - model: Optional[str] = params.get("model") or self.default_model - working_dir: Optional[str] = params.get("working_dir") - timeout: int = params.get("timeout", 300) - - # Build command (using openai CLI or custom wrapper) - command: list[str] = ["openai", "api", "chat.completions.create"] - if model: - command.extend(["-m", model]) - command.extend(["-g", "user", prompt]) - - # Execute - return await self.execute_cli( - command, - working_dir=working_dir, - timeout=timeout, - ) - - -class GeminiCLITool(BaseCLITool): - """Google Gemini CLI tool.""" - - def __init__(self, permission_manager: Optional[PermissionManager] = None): - super().__init__( - permission_manager=permission_manager, - default_model="gemini-1.5-pro", - api_key_env="GEMINI_API_KEY", - ) - - @property - def name(self) -> str: - return "gemini" - - @property - def description(self) -> str: - return "Execute Google Gemini CLI for multimodal AI assistance" - - @auto_timeout("cli_tools") - async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str: - prompt: str = params.get("prompt", "") - model: Optional[str] = params.get("model") or self.default_model - working_dir: Optional[str] = params.get("working_dir") - timeout: int = params.get("timeout", 300) - - # Build command - command: list[str] = ["gemini"] - if model: - command.extend(["--model", model]) - command.append(prompt) - - # Execute - return await self.execute_cli( - command, - working_dir=working_dir, - timeout=timeout, - ) - - -class GrokCLITool(BaseCLITool): - """xAI Grok CLI tool.""" - - def __init__(self, permission_manager: Optional[PermissionManager] = None): - super().__init__( - permission_manager=permission_manager, - default_model="grok-4", - api_key_env="XAI_API_KEY", - ) - - @property - def name(self) -> str: - return "grok" - - @property - def description(self) -> str: - return "Execute xAI Grok CLI for real-time AI assistance" - - @auto_timeout("cli_tools") - async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str: - prompt: str = params.get("prompt", "") - model: Optional[str] = params.get("model") or self.default_model - working_dir: Optional[str] = params.get("working_dir") - timeout: int = params.get("timeout", 300) - - # Build command - command: list[str] = ["grok"] - if model: - command.extend(["--model", model]) - command.append(prompt) - - # Execute - return await self.execute_cli( - command, - working_dir=working_dir, - timeout=timeout, - ) - - -class OpenHandsCLITool(BaseCLITool): - """OpenHands (OpenDevin) CLI tool.""" - - def __init__(self, permission_manager: Optional[PermissionManager] = None): - super().__init__( - permission_manager=permission_manager, - default_model="claude-3-5-sonnet-20241022", - api_key_env="OPENAI_API_KEY", - ) - - @property - def name(self) -> str: - return "openhands" - - @property - def description(self) -> str: - return "Execute OpenHands (OpenDevin) for autonomous coding assistance" - - @auto_timeout("cli_tools") - async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str: - prompt = params.get("prompt", "") - model = params.get("model") or self.default_model - working_dir: str = params.get("working_dir") or os.getcwd() - timeout: int = params.get("timeout", 600) # 10 minutes for OpenHands - - # Build command - command: list[str] = ["openhands", "run", prompt] - if model: - command.extend(["--model", model]) - command.extend(["--workspace", working_dir]) - - # Execute - return await self.execute_cli( - command, - working_dir=working_dir, - timeout=timeout, - ) - - -class OpenHandsShortCLITool(OpenHandsCLITool): - """OpenHands CLI tool (oh alias).""" - - @property - def name(self) -> str: - return "oh" - - @property - def description(self) -> str: - return "OpenHands CLI (alias for openhands)" - - -class HanzoDevCLITool(BaseCLITool): - """Hanzo Dev AI coding assistant.""" - - def __init__(self, permission_manager: Optional[PermissionManager] = None): - super().__init__( - permission_manager=permission_manager, - default_model="claude-3-5-sonnet-20241022", - api_key_env="HANZO_API_KEY", - ) - - @property - def name(self) -> str: - return "hanzo_dev" - - @property - def description(self) -> str: - return "Execute Hanzo Dev for AI-powered code editing and development" - - @auto_timeout("cli_tools") - async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str: - prompt = params.get("prompt", "") - model = params.get("model") or self.default_model - working_dir: str = params.get("working_dir") or os.getcwd() - timeout: int = params.get("timeout", 600) - - # Build command - command: list[str] = ["dev"] - if model: - command.extend(["--model", model]) - command.extend(["--prompt", prompt]) - - # Execute - return await self.execute_cli( - command, - working_dir=working_dir, - timeout=timeout, - ) - - -class ClineCLITool(BaseCLITool): - """Cline (formerly Claude Engineer) CLI tool.""" - - def __init__(self, permission_manager: Optional[PermissionManager] = None): - super().__init__( - permission_manager=permission_manager, - default_model="claude-3-5-sonnet-20241022", - api_key_env="ANTHROPIC_API_KEY", - ) - - @property - def name(self) -> str: - return "cline" - - @property - def description(self) -> str: - return "Execute Cline for autonomous coding with Claude" - - @auto_timeout("cli_tools") - async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str: - prompt = params.get("prompt", "") - working_dir: str = params.get("working_dir") or os.getcwd() - timeout: int = params.get("timeout", 600) - - # Build command - command: list[str] = ["cline", prompt] - command.extend(["--no-interactive"]) # Non-interactive mode for batch - - # Execute - return await self.execute_cli( - command, - working_dir=working_dir, - timeout=timeout, - ) - - -class AiderCLITool(BaseCLITool): - """Aider AI pair programming tool.""" - - def __init__(self, permission_manager: Optional[PermissionManager] = None): - super().__init__( - permission_manager=permission_manager, - default_model="gpt-4-turbo", - api_key_env="OPENAI_API_KEY", - ) - - @property - def name(self) -> str: - return "aider" - - @property - def description(self) -> str: - return "Execute Aider for AI pair programming" - - @auto_timeout("cli_tools") - async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str: - prompt = params.get("prompt", "") - model = params.get("model") or self.default_model - working_dir: str = params.get("working_dir") or os.getcwd() - timeout: int = params.get("timeout", 600) - - # Build command - command: list[str] = ["aider"] - if model: - command.extend(["--model", model]) - command.extend(["--message", prompt]) - command.extend(["--yes"]) # Auto-approve changes - command.extend(["--no-stream"]) # No streaming for batch - - # Execute - return await self.execute_cli( - command, - working_dir=working_dir, - timeout=timeout, - ) - - -def register_cli_tools( - mcp_server: FastMCP, - permission_manager: Optional[PermissionManager] = None, -) -> list[BaseTool]: - """Register all CLI tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - permission_manager: Permission manager for access control - - Returns: - List of registered CLI tools - """ - tools: list[BaseTool] = [ - ClaudeCLITool(permission_manager), - ClaudeCodeCLITool(permission_manager), # cc alias - CodexCLITool(permission_manager), - GeminiCLITool(permission_manager), - GrokCLITool(permission_manager), - OpenHandsCLITool(permission_manager), - OpenHandsShortCLITool(permission_manager), # oh alias - HanzoDevCLITool(permission_manager), - ClineCLITool(permission_manager), - AiderCLITool(permission_manager), - ] - - # Register each tool - for tool in tools: - tool.register(mcp_server) - - return tools - - -# Export all CLI tool classes -__all__ = [ - "ClaudeCLITool", - "ClaudeCodeCLITool", - "CodexCLITool", - "GeminiCLITool", - "GrokCLITool", - "OpenHandsCLITool", - "OpenHandsShortCLITool", - "HanzoDevCLITool", - "ClineCLITool", - "AiderCLITool", - "register_cli_tools", -] diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/code_auth.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/code_auth.py deleted file mode 100644 index 22cef34eb..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/code_auth.py +++ /dev/null @@ -1,461 +0,0 @@ -"""Claude Code and OpenAI Codex authentication management. - -This module provides tools to manage API keys and authentication for -Claude Code CLI and OpenAI Codex, allowing separate accounts for swarm agents. -""" - -import os -import json -import asyncio -import getpass -from typing import Any, Dict, List, Tuple, Optional -from pathlib import Path -from dataclasses import dataclass - -import keyring - - -@dataclass -class APICredential: - """API credential information.""" - - provider: str - api_key: str - model: Optional[str] = None - base_url: Optional[str] = None - org_id: Optional[str] = None - description: Optional[str] = None - - -class CodeAuthManager: - """Manages authentication for Claude Code and other AI coding tools.""" - - # Configuration paths - CONFIG_DIR = Path.home() / ".hanzo" / "auth" - ACCOUNTS_FILE = CONFIG_DIR / "accounts.json" - ACTIVE_ACCOUNT_FILE = CONFIG_DIR / "active_account" - - # Environment variable mappings - ENV_VARS = { - "claude": ["ANTHROPIC_API_KEY", "CLAUDE_API_KEY"], - "openai": ["OPENAI_API_KEY"], - "azure": ["AZURE_OPENAI_API_KEY", "AZURE_API_KEY"], - "deepseek": ["DEEPSEEK_API_KEY"], - "google": ["GOOGLE_API_KEY", "GEMINI_API_KEY"], - "groq": ["GROQ_API_KEY"], - } - - # Default models - DEFAULT_MODELS = { - "claude": "claude-3-5-sonnet-20241022", # Latest Sonnet - "openai": "gpt-4o", - "azure": "gpt-4", - "deepseek": "deepseek-coder", - "google": "gemini-1.5-pro", - "groq": "llama3-70b-8192", - } - - def __init__(self): - """Initialize auth manager.""" - self.ensure_config_dir() - self._env_backup = {} - - def ensure_config_dir(self): - """Ensure config directory exists.""" - self.CONFIG_DIR.mkdir(parents=True, exist_ok=True) - - def get_active_account(self) -> Optional[str]: - """Get the currently active account.""" - if self.ACTIVE_ACCOUNT_FILE.exists(): - return self.ACTIVE_ACCOUNT_FILE.read_text().strip() - return "default" - - def set_active_account(self, account: str): - """Set the active account.""" - self.ACTIVE_ACCOUNT_FILE.write_text(account) - - def _load_accounts(self) -> Dict[str, Dict[str, Any]]: - """Load all accounts.""" - if not self.ACCOUNTS_FILE.exists(): - return {} - - try: - with open(self.ACCOUNTS_FILE, "r") as f: - return json.load(f) - except Exception: - return {} - - def _save_accounts(self, accounts: Dict[str, Dict[str, Any]]): - """Save accounts.""" - with open(self.ACCOUNTS_FILE, "w") as f: - json.dump(accounts, f, indent=2) - - def list_accounts(self) -> List[str]: - """List all available accounts.""" - accounts = self._load_accounts() - return list(accounts.keys()) - - def get_account_info(self, account: str) -> Optional[Dict[str, Any]]: - """Get information about an account.""" - accounts = self._load_accounts() - return accounts.get(account) - - def create_account( - self, - account: str, - provider: str = "claude", - api_key: Optional[str] = None, - model: Optional[str] = None, - description: Optional[str] = None, - ) -> Tuple[bool, str]: - """Create a new account. - - Args: - account: Account name - provider: Provider (claude, openai, etc.) - api_key: API key (will prompt if not provided) - model: Model to use (defaults to provider default) - description: Account description - - Returns: - Tuple of (success, message) - """ - accounts = self._load_accounts() - - if account in accounts: - return False, f"Account '{account}' already exists" - - # Get API key if not provided - if not api_key: - api_key = self._prompt_for_api_key(provider) - if not api_key: - return False, "No API key provided" - - # Use default model if not specified - if not model: - model = self.DEFAULT_MODELS.get(provider) - - # Store in keyring for security - try: - keyring.set_password(f"hanzo-{provider}", account, api_key) - except Exception: - # Fallback to file storage (less secure) - pass - - # Save account info - accounts[account] = { - "provider": provider, - "model": model, - "description": description or f"{provider} account", - "created_at": os.path.getmtime(__file__), - "has_keyring": self._has_keyring_support(), - } - - self._save_accounts(accounts) - return True, f"Created account '{account}' for {provider}" - - def _prompt_for_api_key(self, provider: str) -> Optional[str]: - """Prompt user for API key.""" - prompt = f"Enter {provider.upper()} API key: " - try: - return getpass.getpass(prompt) - except KeyboardInterrupt: - return None - - def _has_keyring_support(self) -> bool: - """Check if keyring is available.""" - try: - keyring.get_keyring() - return True - except Exception: - return False - - def login(self, account: str = "default") -> Tuple[bool, str]: - """Login to an account by setting environment variables. - - Args: - account: Account name to login to - - Returns: - Tuple of (success, message) - """ - accounts = self._load_accounts() - - if account not in accounts: - return False, f"Account '{account}' not found" - - account_info = accounts[account] - provider = account_info["provider"] - - # Get API key from keyring or prompt - api_key = None - if account_info.get("has_keyring"): - try: - api_key = keyring.get_password(f"hanzo-{provider}", account) - except Exception: - pass - - if not api_key: - # Try environment variable - for env_var in self.ENV_VARS.get(provider, []): - if env_var in os.environ: - api_key = os.environ[env_var] - break - - if not api_key: - api_key = self._prompt_for_api_key(provider) - if not api_key: - return False, "No API key available" - - # Backup current environment - self._backup_environment(provider) - - # Set environment variables - for env_var in self.ENV_VARS.get(provider, []): - os.environ[env_var] = api_key - - # Set active account - self.set_active_account(account) - - # Update shell if using claude command - self._update_claude_command(account_info) - - return True, f"Logged in as '{account}' ({provider})" - - def logout(self) -> Tuple[bool, str]: - """Logout by clearing environment variables.""" - current = self.get_active_account() - - if not current or current == "default": - return False, "No active session" - - accounts = self._load_accounts() - if current not in accounts: - return False, f"Unknown account: {current}" - - provider = accounts[current]["provider"] - - # Clear environment variables - for env_var in self.ENV_VARS.get(provider, []): - if env_var in os.environ: - del os.environ[env_var] - - # Restore backed up environment if any - self._restore_environment(provider) - - # Clear active account - if self.ACTIVE_ACCOUNT_FILE.exists(): - self.ACTIVE_ACCOUNT_FILE.unlink() - - return True, f"Logged out from '{current}'" - - def _backup_environment(self, provider: str): - """Backup current environment variables.""" - for env_var in self.ENV_VARS.get(provider, []): - if env_var in os.environ: - self._env_backup[env_var] = os.environ[env_var] - - def _restore_environment(self, provider: str): - """Restore backed up environment variables.""" - for env_var in self.ENV_VARS.get(provider, []): - if env_var in self._env_backup: - os.environ[env_var] = self._env_backup[env_var] - del self._env_backup[env_var] - - async def _update_claude_command_async(self, account_info: Dict[str, Any]): - """Update claude command configuration if needed (async).""" - # Check if claude command exists - try: - process = await asyncio.create_subprocess_exec( - "which", - "claude", - stdout=asyncio.subprocess.DEVNULL, - stderr=asyncio.subprocess.DEVNULL, - ) - await asyncio.wait_for(process.wait(), timeout=5) - - if process.returncode == 0: - # Claude command exists, update its config - claude_config = Path.home() / ".claude" / "config.json" - if claude_config.exists(): - try: - with open(claude_config, "r") as f: - config = json.load(f) - - # Update model if specified - if account_info.get("model"): - config["default_model"] = account_info["model"] - - with open(claude_config, "w") as f: - json.dump(config, f, indent=2) - except Exception: - pass - except Exception: - pass - - def _update_claude_command(self, account_info: Dict[str, Any]): - """Update claude command configuration if needed (sync wrapper).""" - # Use sync check for existence, skip if not found - import shutil - - if not shutil.which("claude"): - return - - claude_config = Path.home() / ".claude" / "config.json" - if claude_config.exists(): - try: - with open(claude_config, "r") as f: - config = json.load(f) - - # Update model if specified - if account_info.get("model"): - config["default_model"] = account_info["model"] - - with open(claude_config, "w") as f: - json.dump(config, f, indent=2) - except Exception: - pass - - def switch_account(self, account: str) -> Tuple[bool, str]: - """Switch to a different account.""" - # Logout current - self.logout() - - # Login to new account - return self.login(account) - - def create_agent_account( - self, - agent_id: str, - provider: str = "claude", - parent_account: Optional[str] = None, - ) -> Tuple[bool, str]: - """Create an account for a swarm agent. - - Args: - agent_id: Unique agent identifier - provider: AI provider - parent_account: Parent account to clone from - - Returns: - Tuple of (success, account_name) - """ - agent_account = f"agent_{agent_id}" - - # If parent account specified, clone its credentials - if parent_account: - parent_info = self.get_account_info(parent_account) - if not parent_info: - return False, f"Parent account '{parent_account}' not found" - - # Get parent API key - api_key = None - if parent_info.get("has_keyring"): - try: - api_key = keyring.get_password( - f"hanzo-{parent_info['provider']}", parent_account - ) - except Exception: - pass - - if api_key: - success, msg = self.create_account( - agent_account, - provider=parent_info["provider"], - api_key=api_key, - model=parent_info.get("model"), - description=f"Agent account (parent: {parent_account})", - ) - if success: - return True, agent_account - - # Create with current environment - for env_var in self.ENV_VARS.get(provider, []): - if env_var in os.environ: - success, msg = self.create_account( - agent_account, - provider=provider, - api_key=os.environ[env_var], - model=self.DEFAULT_MODELS.get(provider), - description=f"Agent account for {agent_id}", - ) - if success: - return True, agent_account - - return False, "No credentials available for agent" - - def get_agent_credentials(self, agent_id: str) -> Optional[APICredential]: - """Get credentials for an agent. - - Args: - agent_id: Agent identifier - - Returns: - APICredential if found - """ - agent_account = f"agent_{agent_id}" - account_info = self.get_account_info(agent_account) - - if not account_info: - return None - - # Get API key - api_key = None - provider = account_info["provider"] - - if account_info.get("has_keyring"): - try: - api_key = keyring.get_password(f"hanzo-{provider}", agent_account) - except Exception: - pass - - if not api_key: - # Try current environment - for env_var in self.ENV_VARS.get(provider, []): - if env_var in os.environ: - api_key = os.environ[env_var] - break - - if not api_key: - return None - - return APICredential( - provider=provider, - api_key=api_key, - model=account_info.get("model"), - description=account_info.get("description"), - ) - - -# Update swarm tool to use latest Sonnet -def get_latest_claude_model() -> str: - """Get the latest Claude model identifier.""" - # As of the knowledge cutoff, this is the latest Sonnet - # In production, this could query an API for the latest model - return "claude-3-5-sonnet-20241022" - - -# Token counting using tiktoken (same as current implementation) -def count_tokens_streaming(text_stream) -> int: - """Count tokens in a streaming fashion. - - This uses the same tiktoken approach as the truncate module, - but processes text as it streams. - """ - import tiktoken - - try: - # Use cl100k_base encoding (Claude/GPT-4 compatible) - encoding = tiktoken.get_encoding("cl100k_base") - except Exception: - # Fallback to simple estimation - return len(text_stream) // 4 - - total_tokens = 0 - for chunk in text_stream: - if isinstance(chunk, str): - total_tokens += len(encoding.encode(chunk)) - elif isinstance(chunk, bytes): - total_tokens += len(encoding.encode(chunk.decode("utf-8", errors="ignore"))) - - return total_tokens diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/code_auth_tool.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/code_auth_tool.py deleted file mode 100644 index 43ab79bea..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/code_auth_tool.py +++ /dev/null @@ -1,196 +0,0 @@ -"""Claude Code authentication tool. - -This tool manages API keys and accounts for Claude Code and other AI coding tools. -""" - -from typing import Unpack, Optional, TypedDict, final, override - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context - -from .code_auth import CodeAuthManager - - -class CodeAuthParams(TypedDict, total=False): - """Parameters for code auth tool.""" - - action: str - account: Optional[str] - provider: Optional[str] - api_key: Optional[str] - model: Optional[str] - description: Optional[str] - agent_id: Optional[str] - parent_account: Optional[str] - - -@final -class CodeAuthTool(BaseTool): - """Tool for managing Claude Code authentication and API keys.""" - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "code_auth" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Manage Claude Code and AI provider authentication. - -Actions: -- status: Show current login status -- list: List all accounts -- create: Create a new account -- login: Login to an account -- logout: Logout current account -- switch: Switch between accounts -- agent: Create/get agent account - -Examples: -code_auth status -code_auth list -code_auth create --account work --provider claude -code_auth login --account work -code_auth logout -code_auth switch --account personal -code_auth agent --agent_id swarm_1 --parent_account work - -Providers: claude, openai, azure, deepseek, google, groq""" - - def __init__(self): - """Initialize the code auth tool.""" - self.auth_manager = CodeAuthManager() - - @override - @auto_timeout("code_auth") - async def call( - self, - ctx: MCPContext, - **params: Unpack[CodeAuthParams], - ) -> str: - """Execute the code auth tool. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Result message - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - action = params.get("action", "status") - - if action == "status": - current = self.auth_manager.get_active_account() - if current: - info = self.auth_manager.get_account_info(current) - if info: - return f"Logged in as: {current} ({info['provider']})" - return "Not logged in" - - elif action == "list": - accounts = self.auth_manager.list_accounts() - if not accounts: - return "No accounts configured" - - current = self.auth_manager.get_active_account() - lines = ["Configured accounts:"] - for account in accounts: - info = self.auth_manager.get_account_info(account) - marker = " (active)" if account == current else "" - lines.append(f" - {account}: {info['provider']}{marker}") - return "\n".join(lines) - - elif action == "create": - account = params.get("account") - if not account: - return "Error: account name required" - - provider = params.get("provider", "claude") - api_key = params.get("api_key") - model = params.get("model") - description = params.get("description") - - success, msg = self.auth_manager.create_account( - account, provider, api_key, model, description - ) - return msg - - elif action == "login": - account = params.get("account", "default") - success, msg = self.auth_manager.login(account) - return msg - - elif action == "logout": - success, msg = self.auth_manager.logout() - return msg - - elif action == "switch": - account = params.get("account") - if not account: - return "Error: account name required" - - success, msg = self.auth_manager.switch_account(account) - return msg - - elif action == "agent": - agent_id = params.get("agent_id") - if not agent_id: - return "Error: agent_id required" - - provider = params.get("provider", "claude") - parent_account = params.get("parent_account") - - # Try to create agent account - success, result = self.auth_manager.create_agent_account( - agent_id, provider, parent_account - ) - - if success: - # Get credentials - creds = self.auth_manager.get_agent_credentials(agent_id) - if creds: - return f"Agent account ready: {result} ({creds.provider})" - else: - return f"Agent account created but no credentials: {result}" - else: - return f"Failed to create agent account: {result}" - - else: - return f"Unknown action: {action}. Use: status, list, create, login, logout, switch, agent" - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def code_auth( - ctx: MCPContext, - action: str = "status", - account: Optional[str] = None, - provider: Optional[str] = None, - api_key: Optional[str] = None, - model: Optional[str] = None, - description: Optional[str] = None, - agent_id: Optional[str] = None, - parent_account: Optional[str] = None, - ) -> str: - return await tool_self.call( - ctx, - action=action, - account=account, - provider=provider, - api_key=api_key, - model=model, - description=description, - agent_id=agent_id, - parent_account=parent_account, - ) diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/codex_cli_tool.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/codex_cli_tool.py deleted file mode 100644 index 2e8da85ba..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/codex_cli_tool.py +++ /dev/null @@ -1,125 +0,0 @@ -"""OpenAI Codex CLI agent tool. - -This tool provides integration with OpenAI's CLI (openai command), -allowing programmatic execution of GPT-4 and other models for code tasks. -""" - -from typing import List, Optional, final, override - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import PermissionManager - -from .cli_agent_base import CLIAgentBase - - -@final -class CodexCLITool(CLIAgentBase): - """Tool for executing OpenAI CLI (formerly Codex).""" - - def __init__( - self, - permission_manager: Optional[PermissionManager] = None, - model: Optional[str] = None, - **kwargs, - ): - """Initialize Codex CLI tool. - - Args: - permission_manager: Permission manager for access control - model: Optional model override (defaults to gpt-4o) - **kwargs: Additional arguments - """ - super().__init__( - permission_manager=permission_manager, - command_name="openai", - provider_name="OpenAI", - default_model=model or "gpt-4o", - env_vars=["OPENAI_API_KEY"], - **kwargs, - ) - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "codex_cli" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Execute OpenAI CLI for code generation and analysis. - -This tool runs the OpenAI CLI (openai command) for code generation, -completion, and analysis tasks. It uses GPT-4o by default but supports -all OpenAI models. - -Features: -- GPT-4 and GPT-4o for advanced reasoning -- Code generation and completion -- Multi-modal support (with gpt-4-vision) -- Function calling capabilities - -Usage: -codex_cli(prompts="Generate a Python function to sort a binary tree") -codex_cli(prompts="Explain this code and suggest improvements", model="gpt-4-turbo") - -Requirements: -- OpenAI CLI must be installed: pip install openai -- OPENAI_API_KEY environment variable -""" - - @override - def get_cli_args(self, prompt: str, **kwargs) -> List[str]: - """Get CLI arguments for OpenAI. - - Args: - prompt: The prompt to send - **kwargs: Additional arguments (model, temperature, etc.) - - Returns: - List of command arguments - """ - args = ["api", "chat.completions.create"] - - # Add model - model = kwargs.get("model", self.default_model) - args.extend(["-m", model]) - - # Add temperature if specified - if "temperature" in kwargs: - args.extend(["--temperature", str(kwargs["temperature"])]) - - # Add max tokens if specified - if "max_tokens" in kwargs: - args.extend(["--max-tokens", str(kwargs["max_tokens"])]) - - # Add the prompt as a message - args.extend(["-g", prompt]) - - return args - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def codex_cli( - ctx: MCPContext, - prompts: str, - model: Optional[str] = None, - temperature: Optional[float] = None, - max_tokens: Optional[int] = None, - working_dir: Optional[str] = None, - ) -> str: - return await tool_self.call( - ctx, - prompts=prompts, - model=model, - temperature=temperature, - max_tokens=max_tokens, - working_dir=working_dir, - ) diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/critic_tool.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/critic_tool.py deleted file mode 100644 index cbed9f755..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/critic_tool.py +++ /dev/null @@ -1,394 +0,0 @@ -"""Critic tool for agents to request critical review from main loop.""" - -from enum import Enum -from typing import List, Optional, override - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout - - -class ReviewType(Enum): - """Types of review requests.""" - - CODE_QUALITY = "code_quality" - CORRECTNESS = "correctness" - PERFORMANCE = "performance" - SECURITY = "security" - COMPLETENESS = "completeness" - BEST_PRACTICES = "best_practices" - GENERAL = "general" - - -class CriticTool(BaseTool): - """Tool for agents to request critical review from the main loop.""" - - name = "critic" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Request critical review and feedback from the main loop. - -Use this tool to get automated critical analysis of your work. The main loop will: -- Review your implementation for bugs, edge cases, and improvements -- Check for security issues and best practices -- Suggest performance optimizations -- Ensure completeness and correctness -- Provide actionable feedback for improvements - -Parameters: -- review_type: Type of review (CODE_QUALITY, CORRECTNESS, PERFORMANCE, SECURITY, COMPLETENESS, BEST_PRACTICES, GENERAL) -- work_description: Clear description of what you've done -- code_snippets: Optional code snippets to review (as a list of strings) -- file_paths: Optional list of file paths you've modified -- specific_concerns: Optional specific areas you want reviewed - -The critic will provide harsh but constructive feedback to ensure high quality. - -Example: -critic( - review_type="CODE_QUALITY", - work_description="Added import statements to fix undefined symbols in Go files", - code_snippets=["import (\n \"fmt\"\n \"github.com/luxfi/node/common\"\n)"], - file_paths=["/path/to/atomic.go", "/path/to/network.go"], - specific_concerns="Are the imports in the correct format and location?" -)""" - - @auto_timeout("critic") - async def call( - self, - ctx: MCPContext, - review_type: str, - work_description: str, - code_snippets: Optional[List[str]] = None, - file_paths: Optional[List[str]] = None, - specific_concerns: Optional[str] = None, - ) -> str: - """Delegate to AgentTool for actual implementation. - - This method provides the interface, but the actual critic logic - is handled by the AgentTool's execution framework. - """ - # This tool is handled specially in the agent execution - return f"Critic review requested for: {work_description}" - - def register(self, server: FastMCP) -> None: - """Register the tool with the MCP server.""" - tool_self = self - - @server.tool(name=self.name, description=self.description) - async def critic( - ctx: MCPContext, - review_type: str, - work_description: str, - code_snippets: Optional[List[str]] = None, - file_paths: Optional[List[str]] = None, - specific_concerns: Optional[str] = None, - ) -> str: - return await tool_self.call( - ctx, - review_type, - work_description, - code_snippets, - file_paths, - specific_concerns, - ) - - -class AutoCritic: - """Automated critic that provides harsh but constructive feedback.""" - - def __init__(self): - self.review_patterns = { - ReviewType.CODE_QUALITY: self._review_code_quality, - ReviewType.CORRECTNESS: self._review_correctness, - ReviewType.PERFORMANCE: self._review_performance, - ReviewType.SECURITY: self._review_security, - ReviewType.COMPLETENESS: self._review_completeness, - ReviewType.BEST_PRACTICES: self._review_best_practices, - ReviewType.GENERAL: self._review_general, - } - - def review( - self, - review_type: ReviewType, - work_description: str, - code_snippets: Optional[List[str]] = None, - file_paths: Optional[List[str]] = None, - specific_concerns: Optional[str] = None, - ) -> str: - """Perform automated critical review.""" - review_func = self.review_patterns.get(review_type, self._review_general) - return review_func( - work_description, code_snippets, file_paths, specific_concerns - ) - - def _review_code_quality( - self, - work_description: str, - code_snippets: Optional[List[str]], - file_paths: Optional[List[str]], - specific_concerns: Optional[str], - ) -> str: - """Review code quality aspects.""" - issues = [] - suggestions = [] - - # Check for common code quality issues - if code_snippets: - for snippet in code_snippets: - # Check for proper error handling - if "error" in snippet.lower() and "if err" not in snippet: - issues.append( - "โŒ Missing error handling - always check errors in Go" - ) - - # Check for magic numbers - if any(char.isdigit() for char in snippet) and "const" not in snippet: - suggestions.append( - "๐Ÿ’ก Consider extracting magic numbers to named constants" - ) - - # Check for proper imports - if "import" in snippet: - if '"fmt"' in snippet and snippet.count("fmt.") == 0: - issues.append("โŒ Unused import 'fmt' - remove unused imports") - if not snippet.strip().endswith(")") and "import (" in snippet: - issues.append("โŒ Import block not properly closed") - - # General quality checks - if "fix" in work_description.lower(): - suggestions.append("๐Ÿ’ก Ensure you've tested the fix thoroughly") - suggestions.append("๐Ÿ’ก Consider edge cases and error scenarios") - - if file_paths and len(file_paths) > 5: - suggestions.append( - "๐Ÿ’ก Large number of files modified - consider breaking into smaller PRs" - ) - - # Build response - response = "๐Ÿ” CODE QUALITY REVIEW:\n\n" - - if issues: - response += "Issues Found:\n" + "\n".join(issues) + "\n\n" - else: - response += "โœ… No major code quality issues detected.\n\n" - - if suggestions: - response += ( - "Suggestions for Improvement:\n" + "\n".join(suggestions) + "\n\n" - ) - - if specific_concerns: - response += f"Regarding your concern: '{specific_concerns}'\n" - if "import" in specific_concerns.lower(): - response += "โ†’ Imports look properly formatted. Ensure they're in the standard order: stdlib, external, internal.\n" - - response += "\nOverall: " + ( - "โš ๏ธ Address the issues before proceeding." - if issues - else "โœ… Good work, but always room for improvement!" - ) - - return response - - def _review_correctness( - self, - work_description: str, - code_snippets: Optional[List[str]], - file_paths: Optional[List[str]], - specific_concerns: Optional[str], - ) -> str: - """Review correctness aspects.""" - return """๐Ÿ” CORRECTNESS REVIEW: - -Critical Questions: -โ“ Have you verified the changes compile without errors? -โ“ Do the changes actually fix the reported issue? -โ“ Have you introduced any new bugs or regressions? -โ“ Are all edge cases handled properly? - -Specific Checks: -- If fixing imports: Verify the import paths are correct for the project -- If modifying logic: Ensure the logic is sound and handles all cases -- If refactoring: Confirm behavior is preserved - -โš ๏ธ Remember: Working code > elegant code. Make sure it works first!""" - - def _review_performance( - self, - work_description: str, - code_snippets: Optional[List[str]], - file_paths: Optional[List[str]], - specific_concerns: Optional[str], - ) -> str: - """Review performance aspects.""" - return """๐Ÿ” PERFORMANCE REVIEW: - -Performance Considerations: -- Are you doing any operations in loops that could be moved outside? -- Are there any unnecessary allocations or copies? -- Could any synchronous operations be made concurrent? -- Are you caching results that might be reused? - -For file operations: -- Consider batch operations over individual ones -- Use buffered I/O for large files -- Avoid reading entire files into memory if possible - -๐Ÿ’ก Remember: Premature optimization is evil, but obvious inefficiencies should be fixed.""" - - def _review_security( - self, - work_description: str, - code_snippets: Optional[List[str]], - file_paths: Optional[List[str]], - specific_concerns: Optional[str], - ) -> str: - """Review security aspects.""" - return """๐Ÿ” SECURITY REVIEW: - -Security Checklist: -๐Ÿ” No hardcoded secrets or credentials -๐Ÿ” All user inputs are validated/sanitized -๐Ÿ” File paths are properly validated -๐Ÿ” No SQL injection vulnerabilities -๐Ÿ” Proper access control checks -๐Ÿ” Sensitive data is not logged - -โš ๏ธ If in doubt, err on the side of caution!""" - - def _review_completeness( - self, - work_description: str, - code_snippets: Optional[List[str]], - file_paths: Optional[List[str]], - specific_concerns: Optional[str], - ) -> str: - """Review completeness aspects.""" - tasks_mentioned = work_description.lower() - - response = "๐Ÿ” COMPLETENESS REVIEW:\n\n" - - if "fix" in tasks_mentioned and "test" not in tasks_mentioned: - response += ( - "โŒ No mention of tests - have you verified the fix with tests?\n" - ) - - if "import" in tasks_mentioned: - response += "โœ“ Import fixes mentioned\n" - response += "โ“ Have you checked for other files with similar issues?\n" - response += "โ“ Are all undefined symbols now resolved?\n" - - if file_paths: - response += f"\nโœ“ Modified {len(file_paths)} files\n" - response += "โ“ Are there any related files that also need updates?\n" - - response += "\n๐Ÿ’ก Completeness means not just fixing the immediate issue, but considering the broader impact." - - return response - - def _review_best_practices( - self, - work_description: str, - code_snippets: Optional[List[str]], - file_paths: Optional[List[str]], - specific_concerns: Optional[str], - ) -> str: - """Review best practices.""" - return """๐Ÿ” BEST PRACTICES REVIEW: - -Go Best Practices (if applicable): -โœ“ Imports are grouped: stdlib, external, internal -โœ“ Error handling follows Go idioms -โœ“ Variable names are clear and idiomatic -โœ“ Comments explain why, not what -โœ“ Functions do one thing well - -General Best Practices: -โœ“ Code is self-documenting -โœ“ DRY principle is followed -โœ“ SOLID principles are respected -โœ“ Changes are minimal and focused - -๐Ÿ’ก Good code is code that others (including future you) can understand and modify.""" - - def _review_general( - self, - work_description: str, - code_snippets: Optional[List[str]], - file_paths: Optional[List[str]], - specific_concerns: Optional[str], - ) -> str: - """General review covering multiple aspects.""" - response = "๐Ÿ” GENERAL CRITICAL REVIEW:\n\n" - response += f"Work Description: {work_description}\n\n" - - # Quick assessment - response += "Quick Assessment:\n" - - if "fix" in work_description.lower(): - response += "- Type: Bug fix / Error resolution\n" - response += "- Critical: Ensure the fix is complete and tested\n" - elif "add" in work_description.lower(): - response += "- Type: Feature addition\n" - response += "- Critical: Ensure no regressions introduced\n" - elif "refactor" in work_description.lower(): - response += "- Type: Code refactoring\n" - response += "- Critical: Ensure behavior is preserved\n" - - if file_paths: - response += f"- Scope: {len(file_paths)} files affected\n" - if len(file_paths) > 10: - response += "- โš ๏ธ Large scope - consider breaking down\n" - - response += "\nCritical Questions:\n" - response += "1. Is this the minimal change needed?\n" - response += "2. Have you considered all edge cases?\n" - response += "3. Will this work in production?\n" - response += "4. Is there a simpler solution?\n" - - if specific_concerns: - response += f"\nYour Concern: {specific_concerns}\n" - response += "โ†’ Valid concern. Double-check this area carefully.\n" - - response += "\n๐ŸŽฏ Bottom Line: Good work needs critical thinking. Question everything, verify everything." - - return response - - -class CriticProtocol: - """Protocol for critic interactions.""" - - def __init__(self): - self.auto_critic = AutoCritic() - self.review_count = 0 - self.max_reviews = 2 # Allow up to 2 reviews per task - - def request_review( - self, - review_type: str, - work_description: str, - code_snippets: Optional[List[str]] = None, - file_paths: Optional[List[str]] = None, - specific_concerns: Optional[str] = None, - ) -> str: - """Request a critical review.""" - if self.review_count >= self.max_reviews: - return "โŒ Review limit exceeded. Time to move forward with what you have." - - self.review_count += 1 - - try: - review_enum = ReviewType[review_type.upper()] - except KeyError: - review_enum = ReviewType.GENERAL - - review = self.auto_critic.review( - review_enum, work_description, code_snippets, file_paths, specific_concerns - ) - - return f"Review {self.review_count}/{self.max_reviews}:\n\n{review}" diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/dataset.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/dataset.py deleted file mode 100644 index 0c06d4b64..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/dataset.py +++ /dev/null @@ -1,310 +0,0 @@ -"""Dataset collection for agent telemetry. - -Captures prompts, responses, and full telemetry data for training dataset annotation. - -Usage: - from hanzo_tools.agent.dataset import DatasetCollector - - collector = DatasetCollector("my_dataset.jsonl") - - # Collect single sample - await collector.collect("claude", "Explain quicksort") - - # Collect batch - await collector.collect_batch([ - ("claude", "What is recursion?"), - ("gemini", "Explain binary search"), - ]) - - # Save dataset - collector.save() -""" - -import json -import time -import asyncio -from typing import Any, Dict, List, Tuple, Optional -from pathlib import Path -from datetime import datetime -from dataclasses import field, asdict, dataclass - -from hanzo_async import write_file, append_file - -from .agent_tool import Result, AgentTool, Telemetry - - -@dataclass -class DatasetSample: - """Single dataset sample with full telemetry.""" - - # Core fields - id: str - timestamp: str - agent: str - prompt: str - response: str - - # Status - ok: bool - error: Optional[str] = None - - # Timing - latency_ms: int = 0 - - # Token usage - input_tokens: int = 0 - output_tokens: int = 0 - cache_read_tokens: int = 0 - cache_creation_tokens: int = 0 - - # Cost - cost_usd: float = 0.0 - - # Model info - model: Optional[str] = None - service_name: Optional[str] = None - service_version: Optional[str] = None - - # Full telemetry for annotation - telemetry_raw: Optional[Dict[str, Any]] = None - - # Metadata - tags: List[str] = field(default_factory=list) - metadata: Dict[str, Any] = field(default_factory=dict) - - -class DatasetCollector: - """Collect dataset samples from agent runs.""" - - def __init__(self, output_path: str = "dataset.jsonl", auto_save: bool = True): - """Initialize collector. - - Args: - output_path: Path to output JSONL file - auto_save: Auto-save after each sample - """ - self.output_path = Path(output_path) - self.auto_save = auto_save - self.samples: List[DatasetSample] = [] - self.tool = AgentTool() - self._sample_count = 0 - - # Load existing samples if file exists - if self.output_path.exists(): - self._load_existing() - - def _load_existing(self): - """Load existing samples from file.""" - try: - with open(self.output_path, "r") as f: - for line in f: - if line.strip(): - data = json.loads(line) - self.samples.append(DatasetSample(**data)) - self._sample_count = len(self.samples) - print( - f"Loaded {len(self.samples)} existing samples from {self.output_path}" - ) - except Exception as e: - print(f"Could not load existing samples: {e}") - - def _generate_id(self) -> str: - """Generate unique sample ID.""" - self._sample_count += 1 - return f"sample_{self._sample_count:06d}_{int(time.time())}" - - async def collect( - self, - agent: str, - prompt: str, - timeout: int = 60, - tags: Optional[List[str]] = None, - metadata: Optional[Dict[str, Any]] = None, - ) -> DatasetSample: - """Collect a single dataset sample. - - Args: - agent: Agent to use (claude, gemini, etc.) - prompt: Prompt to send - timeout: Timeout in seconds - tags: Optional tags for categorization - metadata: Optional additional metadata - - Returns: - DatasetSample with full telemetry - """ - result = await self.tool._exec(agent, prompt, None, timeout) - - sample = DatasetSample( - id=self._generate_id(), - timestamp=datetime.utcnow().isoformat() + "Z", - agent=result.agent, - prompt=prompt, - response=result.output, - ok=result.ok, - error=result.error, - latency_ms=result.ms, - tags=tags or [], - metadata=metadata or {}, - ) - - # Add telemetry if available - if result.telemetry: - t = result.telemetry - sample.input_tokens = t.input_tokens - sample.output_tokens = t.output_tokens - sample.cache_read_tokens = t.cache_read_input_tokens - sample.cache_creation_tokens = t.cache_creation_input_tokens - sample.cost_usd = t.cost_usd - sample.model = t.model - sample.service_name = t.service_name - sample.service_version = t.service_version - sample.telemetry_raw = t.raw - - self.samples.append(sample) - - if self.auto_save: - await self._append_sample_async(sample) - - return sample - - async def collect_batch( - self, - prompts: List[Tuple[str, str]], - timeout: int = 60, - max_concurrent: int = 5, - tags: Optional[List[str]] = None, - ) -> List[DatasetSample]: - """Collect batch of samples with concurrency control. - - Args: - prompts: List of (agent, prompt) tuples - timeout: Timeout per sample - max_concurrent: Max concurrent requests - tags: Tags to apply to all samples - - Returns: - List of DatasetSamples - """ - sem = asyncio.Semaphore(max_concurrent) - - async def collect_one(agent: str, prompt: str, idx: int) -> DatasetSample: - async with sem: - sample = await self.collect( - agent, prompt, timeout, tags=tags, metadata={"batch_index": idx} - ) - print( - f"[{idx + 1}/{len(prompts)}] {agent}: {len(sample.response)} chars, ${sample.cost_usd:.4f}" - ) - return sample - - tasks = [ - collect_one(agent, prompt, i) for i, (agent, prompt) in enumerate(prompts) - ] - - return await asyncio.gather(*tasks) - - async def _append_sample_async(self, sample: DatasetSample): - """Append single sample to file (non-blocking).""" - await append_file(self.output_path, json.dumps(asdict(sample)) + "\n") - - def _append_sample(self, sample: DatasetSample): - """Append single sample to file (sync version for compatibility).""" - with open(self.output_path, "a") as f: - f.write(json.dumps(asdict(sample)) + "\n") - - async def save_async(self): - """Save all samples to file asynchronously (overwrites).""" - content = "\n".join(json.dumps(asdict(sample)) for sample in self.samples) - if content: - content += "\n" - await write_file(self.output_path, content) - print(f"Saved {len(self.samples)} samples to {self.output_path}") - - def save(self): - """Save all samples to file (sync version, overwrites).""" - with open(self.output_path, "w") as f: - for sample in self.samples: - f.write(json.dumps(asdict(sample)) + "\n") - print(f"Saved {len(self.samples)} samples to {self.output_path}") - - def stats(self) -> Dict[str, Any]: - """Get dataset statistics.""" - if not self.samples: - return {"count": 0} - - total_cost = sum(s.cost_usd for s in self.samples) - total_input = sum(s.input_tokens for s in self.samples) - total_output = sum(s.output_tokens for s in self.samples) - total_cache = sum(s.cache_read_tokens for s in self.samples) - avg_latency = sum(s.latency_ms for s in self.samples) / len(self.samples) - - agents = {} - models = {} - for s in self.samples: - agents[s.agent] = agents.get(s.agent, 0) + 1 - if s.model: - models[s.model] = models.get(s.model, 0) + 1 - - return { - "count": len(self.samples), - "total_cost_usd": total_cost, - "total_input_tokens": total_input, - "total_output_tokens": total_output, - "total_cache_read_tokens": total_cache, - "avg_latency_ms": avg_latency, - "success_rate": sum(1 for s in self.samples if s.ok) / len(self.samples), - "agents": agents, - "models": models, - } - - -async def build_dataset( - prompts: List[str], - agents: List[str] = ["claude"], - output: str = "dataset.jsonl", - max_concurrent: int = 3, - timeout: int = 60, -) -> DatasetCollector: - """Build a dataset from prompts. - - Args: - prompts: List of prompts to collect - agents: Agents to use (will cycle through) - output: Output JSONL path - max_concurrent: Max concurrent requests - timeout: Timeout per request - - Returns: - DatasetCollector with collected samples - """ - collector = DatasetCollector(output, auto_save=True) - - # Create prompt-agent pairs, cycling through agents - pairs = [(agents[i % len(agents)], prompt) for i, prompt in enumerate(prompts)] - - print(f"Collecting {len(prompts)} samples across {len(agents)} agents...") - start = time.time() - - await collector.collect_batch(pairs, timeout=timeout, max_concurrent=max_concurrent) - - elapsed = time.time() - start - stats = collector.stats() - - print(f"\n=== Dataset Stats ===") - print(f"Samples: {stats['count']}") - print(f"Total cost: ${stats['total_cost_usd']:.4f}") - print(f"Total tokens: {stats['total_input_tokens']}โ†’{stats['total_output_tokens']}") - print(f"Cache reads: {stats['total_cache_read_tokens']}") - print(f"Avg latency: {stats['avg_latency_ms']:.0f}ms") - print(f"Success rate: {stats['success_rate'] * 100:.1f}%") - print(f"Time: {elapsed:.1f}s") - print(f"Agents: {stats['agents']}") - print(f"Models: {stats['models']}") - print(f"\nSaved to: {output}") - - return collector - - -# Export -__all__ = ["DatasetCollector", "DatasetSample", "build_dataset"] diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/gemini_cli_tool.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/gemini_cli_tool.py deleted file mode 100644 index a306a1e66..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/gemini_cli_tool.py +++ /dev/null @@ -1,130 +0,0 @@ -"""Google Gemini CLI agent tool. - -This tool provides integration with Google's Gemini CLI, -allowing programmatic execution of Gemini models for code tasks. -""" - -from typing import List, Optional, final, override - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import PermissionManager - -from .cli_agent_base import CLIAgentBase - - -@final -class GeminiCLITool(CLIAgentBase): - """Tool for executing Google Gemini CLI.""" - - def __init__( - self, - permission_manager: Optional[PermissionManager] = None, - model: Optional[str] = None, - **kwargs, - ): - """Initialize Gemini CLI tool. - - Args: - permission_manager: Permission manager for access control - model: Optional model override (defaults to gemini-1.5-pro) - **kwargs: Additional arguments - """ - super().__init__( - permission_manager=permission_manager, - command_name="gemini", - provider_name="Google Gemini", - default_model=model or "gemini-1.5-pro", - env_vars=["GOOGLE_API_KEY", "GEMINI_API_KEY"], - **kwargs, - ) - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "gemini_cli" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Execute Google Gemini CLI for code tasks. - -This tool runs the Google Gemini CLI for code generation, analysis, -and multi-modal tasks. It uses Gemini 1.5 Pro by default. - -Features: -- Gemini 1.5 Pro with 2M token context window -- Gemini 1.5 Flash for faster responses -- Multi-modal capabilities (code + images) -- Advanced reasoning and analysis - -Usage: -gemini_cli(prompts="Create a React component for a data table") -gemini_cli(prompts="Analyze this code for security vulnerabilities", model="gemini-1.5-flash") - -Requirements: -- Gemini CLI must be installed -- GOOGLE_API_KEY or GEMINI_API_KEY environment variable -""" - - @override - def get_cli_args(self, prompt: str, **kwargs) -> List[str]: - """Get CLI arguments for Gemini. - - Args: - prompt: The prompt to send - **kwargs: Additional arguments (model, temperature, etc.) - - Returns: - List of command arguments - """ - args = ["generate"] - - # Add model - model = kwargs.get("model", self.default_model) - args.extend(["--model", model]) - - # Add temperature if specified - if "temperature" in kwargs: - args.extend(["--temperature", str(kwargs["temperature"])]) - - # Add max tokens if specified - if "max_tokens" in kwargs: - args.extend(["--max-output-tokens", str(kwargs["max_tokens"])]) - - # Add safety settings if needed - if kwargs.get("safety_settings"): - args.extend(["--safety-settings", kwargs["safety_settings"]]) - - # Add the prompt - args.extend(["--prompt", prompt]) - - return args - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def gemini_cli( - ctx: MCPContext, - prompts: str, - model: Optional[str] = None, - temperature: Optional[float] = None, - max_tokens: Optional[int] = None, - working_dir: Optional[str] = None, - safety_settings: Optional[str] = None, - ) -> str: - return await tool_self.call( - ctx, - prompts=prompts, - model=model, - temperature=temperature, - max_tokens=max_tokens, - working_dir=working_dir, - safety_settings=safety_settings, - ) diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/grok_cli_tool.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/grok_cli_tool.py deleted file mode 100644 index ec264b9b0..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/grok_cli_tool.py +++ /dev/null @@ -1,130 +0,0 @@ -"""Grok CLI agent tool. - -This tool provides integration with xAI's Grok CLI, -allowing programmatic execution of Grok models for code tasks. -""" - -from typing import List, Optional, final, override - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import PermissionManager - -from .cli_agent_base import CLIAgentBase - - -@final -class GrokCLITool(CLIAgentBase): - """Tool for executing Grok CLI.""" - - def __init__( - self, - permission_manager: Optional[PermissionManager] = None, - model: Optional[str] = None, - **kwargs, - ): - """Initialize Grok CLI tool. - - Args: - permission_manager: Permission manager for access control - model: Optional model override (defaults to grok-2) - **kwargs: Additional arguments - """ - super().__init__( - permission_manager=permission_manager, - command_name="grok", - provider_name="xAI Grok", - default_model=model or "grok-2", - env_vars=["XAI_API_KEY", "GROK_API_KEY"], - **kwargs, - ) - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "grok_cli" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Execute xAI Grok CLI for code tasks. - -This tool runs the Grok CLI for code generation, analysis, -and reasoning tasks. It uses Grok-2 by default. - -Features: -- Grok-2 with advanced reasoning capabilities -- Real-time information access -- Code generation and analysis -- Humor and personality in responses - -Usage: -grok_cli(prompts="Write a Python web scraper with async support") -grok_cli(prompts="Explain quantum computing like I'm a programmer", model="grok-1") - -Requirements: -- Grok CLI must be installed -- XAI_API_KEY or GROK_API_KEY environment variable -""" - - @override - def get_cli_args(self, prompt: str, **kwargs) -> List[str]: - """Get CLI arguments for Grok. - - Args: - prompt: The prompt to send - **kwargs: Additional arguments (model, temperature, etc.) - - Returns: - List of command arguments - """ - args = ["chat"] - - # Add model - model = kwargs.get("model", self.default_model) - args.extend(["--model", model]) - - # Add temperature if specified - if "temperature" in kwargs: - args.extend(["--temperature", str(kwargs["temperature"])]) - - # Add max tokens if specified - if "max_tokens" in kwargs: - args.extend(["--max-tokens", str(kwargs["max_tokens"])]) - - # Add system prompt if specified - if "system_prompt" in kwargs: - args.extend(["--system", kwargs["system_prompt"]]) - - # Add the prompt - args.append(prompt) - - return args - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def grok_cli( - ctx: MCPContext, - prompts: str, - model: Optional[str] = None, - temperature: Optional[float] = None, - max_tokens: Optional[int] = None, - working_dir: Optional[str] = None, - system_prompt: Optional[str] = None, - ) -> str: - return await tool_self.call( - ctx, - prompts=prompts, - model=model, - temperature=temperature, - max_tokens=max_tokens, - working_dir=working_dir, - system_prompt=system_prompt, - ) diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/network_tool.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/network_tool.py deleted file mode 100644 index 99523f7fb..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/network_tool.py +++ /dev/null @@ -1,252 +0,0 @@ -"""Network tool for dispatching work to agent networks. - -This tool enables distributed AI workloads across local and remote agent networks, -with support for both local-only execution (via hanzo-miner) and cloud fallback. -""" - -import os -import json -from typing import ( - List, - Unpack, - Optional, - Annotated, - TypedDict, - final, - override, -) - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, PermissionManager, auto_timeout - -# Import hanzo cluster if available -try: - from hanzoai import cluster - - CLUSTER_AVAILABLE = True -except ImportError: - CLUSTER_AVAILABLE = False - - -class NetworkToolParams(TypedDict, total=False): - """Parameters for the network tool.""" - - task: str - agents: Optional[List[str]] - mode: Optional[str] # "local", "distributed", "hybrid" - model: Optional[str] - routing: Optional[str] # "sequential", "parallel", "consensus" - require_local: Optional[bool] - - -@final -class NetworkTool(BaseTool): - """Dispatch work to agent networks for distributed AI processing. - - Modes: - - local: Use only local compute (via hanzo-cluster/miner) - - distributed: Use available network resources - - hybrid: Prefer local, fallback to cloud - - This tool is the evolution of the swarm tool, providing: - - True distributed execution across devices - - Local-first privacy-preserving AI - - Automatic routing and load balancing - - Integration with hanzo-miner for compute contribution - """ - - name = "network" - description = "Dispatch tasks to agent networks for distributed AI processing" - - def __init__( - self, - permission_manager: Optional[PermissionManager] = None, - default_mode: str = "hybrid", - cluster_endpoint: str = None, - ): - """Initialize the network tool. - - Args: - permission_manager: Permission manager - default_mode: Default execution mode - cluster_endpoint: Optional cluster endpoint - """ - self.permission_manager = permission_manager - self.default_mode = default_mode - self.cluster_endpoint = cluster_endpoint or os.environ.get( - "HANZO_CLUSTER_ENDPOINT", "http://localhost:8000" - ) - self._cluster = None - - async def _ensure_cluster(self): - """Ensure we have a cluster connection.""" - if not CLUSTER_AVAILABLE: - return None - - if not self._cluster: - try: - # Try to connect to existing cluster - self._cluster = cluster.HanzoCluster() - # Check if cluster is running - import httpx - - async with httpx.AsyncClient() as client: - response = await client.get(f"{self.cluster_endpoint}/health") - if response.status_code != 200: - # Start local cluster if not running - await self._cluster.start() - except Exception: - # Cluster not available - self._cluster = None - - return self._cluster - - @override - @auto_timeout("network") - async def call(self, ctx: MCPContext, **params: Unpack[NetworkToolParams]) -> str: - """Execute a task on the agent network. - - Args: - ctx: MCP context - task: Task description to execute - agents: Optional list of specific agents to use - mode: Execution mode (local/distributed/hybrid) - model: Optional model preference - routing: Routing strategy - require_local: Require local-only execution - - Returns: - JSON string with results - """ - task = params.get("task", "") - if not task: - return json.dumps({"error": "Task description required", "success": False}) - - mode = params.get("mode", self.default_mode) - agents_list = params.get("agents", []) - model_pref = params.get("model") - routing = params.get("routing", "sequential") - require_local = params.get("require_local", False) - - # Check if we should use local cluster - use_local = mode in ["local", "hybrid"] or require_local - - results = { - "task": task, - "mode": mode, - "routing": routing, - "agents_used": [], - "results": [], - "success": False, - } - - try: - # Try local execution first if requested - if use_local: - cluster = await self._ensure_cluster() - if cluster: - try: - # Execute on local cluster - local_result = await cluster.inference( - prompt=task, - model=model_pref or "llama-3.2-3b", - max_tokens=4000, - ) - - results["agents_used"].append("local-cluster") - results["results"].append( - { - "agent": "local-cluster", - "response": local_result.get("choices", [{}])[0].get( - "text", "" - ), - "local": True, - } - ) - results["success"] = True - - # If local succeeded and not hybrid, return - if mode == "local" or (mode == "hybrid" and results["results"]): - return json.dumps(results, indent=2) - - except Exception as e: - if require_local: - results["error"] = f"Local execution failed: {str(e)}" - return json.dumps(results, indent=2) - - # Agent-based execution with concurrency - if not results["success"] or mode in ["distributed", "hybrid"]: - from .agent_tool import AgentTool - - agent = AgentTool( - permission_manager=self.permission_manager, model=model_pref - ) - concurrency = ( - max(1, len(agents_list)) - if agents_list - else 5 if routing == "parallel" else 1 - ) - agent_params = {"prompts": task, "concurrency": concurrency} - agent_result = await agent.call(ctx, **agent_params) - # Wrap agent_result as a simple result list - results["agents_used"].append("agent") - results["results"].append( - {"agent": "agent", "response": agent_result} - ) - results["success"] = True - - except Exception as e: - results["error"] = str(e) - - return json.dumps(results, indent=2) - - def register(self, server: FastMCP): - """Register the network tool with the server. - - Args: - server: FastMCP server instance - """ - tool = self - - @server.tool(name=tool.name, description=tool.description) - async def network_handler( - ctx: MCPContext, - task: Annotated[str, Field(description="Task to execute on the network")], - agents: Annotated[ - Optional[List[str]], Field(description="Specific agents to use") - ] = None, - mode: Annotated[ - Optional[str], - Field(description="Execution mode: local, distributed, or hybrid"), - ] = None, - model: Annotated[ - Optional[str], Field(description="Model preference") - ] = None, - routing: Annotated[ - Optional[str], - Field( - description="Routing strategy: sequential, parallel, or consensus" - ), - ] = None, - require_local: Annotated[ - Optional[bool], Field(description="Require local-only execution") - ] = None, - ) -> str: - """Dispatch work to agent networks.""" - params = NetworkToolParams( - task=task, - agents=agents, - mode=mode, - model=model, - routing=routing, - require_local=require_local, - ) - return await tool.call(ctx, **params) - - return tool - - -# Remove swarm compatibility tool; swarm is an alias of agent with concurrency diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/prompt.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/prompt.py deleted file mode 100644 index a094b9b28..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/prompt.py +++ /dev/null @@ -1,182 +0,0 @@ -"""Prompt generation utilities for agent tool. - -This module provides functions for generating effective prompts for sub-agents, -including filtering tools based on permissions and formatting system instructions. -""" - -import os -from typing import Any - -from hanzo_tools.core import BaseTool, PermissionManager - - -def get_allowed_agent_tools( - tools: list[BaseTool], - permission_manager: PermissionManager, -) -> list[BaseTool]: - """Filter tools available to the agent based on permissions. - - Args: - tools: List of available tools - permission_manager: Permission manager for checking tool access - - Returns: - Filtered list of tools available to the agent - """ - # Get all tools except for the agent tool itself (avoid recursion) - filtered_tools = [tool for tool in tools if tool.name != "agent"] - - return filtered_tools - - -def get_system_prompt( - tools: list[BaseTool], - permission_manager: PermissionManager, -) -> str: - """Generate system prompt for the sub-agent. - - Args: - tools: List of available tools - permission_manager: Permission manager for checking tool access - - Returns: - System prompt for the sub-agent - """ - # Get filtered tools - filtered_tools = get_allowed_agent_tools(tools, permission_manager) - - # Extract tool names for display - tool_names = ", ".join(f"`{tool.name}`" for tool in filtered_tools) - - # Base system prompt - agents always have edit tools - system_prompt = f"""You are a Claude sub-agent with access to these tools: {tool_names}. - -CAPABILITIES: -1. You have FULL read and write access - you can create, edit, and modify files -2. You can ask clarifying questions if needed - your response goes to the coordinating agent -3. You work as part of a team of specialized agents -4. Other agents may be available via MCP tools (look for tools named after agents) -5. When relevant, share file names and code snippets -6. Any file paths you return MUST be absolute. DO NOT use relative paths. -7. You can only work with the absolute paths provided in your task prompt. - -CLARIFICATION: -- You can request clarification ONCE per task using the request_clarification tool -- Use this when instructions are ambiguous or you need additional context -- Types: AMBIGUOUS_INSTRUCTION, MISSING_CONTEXT, MULTIPLE_OPTIONS, CONFIRMATION_NEEDED, ADDITIONAL_INFO -- The main loop will provide automated guidance based on context - -CRITICAL REVIEW (Devil's Advocate): -- Use the critic tool to get harsh, challenging feedback that attacks assumptions -- The critic will find flaws and push for improvements aggressively -- You can request up to 2 critic reviews per task -- Review types: CODE_QUALITY, CORRECTNESS, PERFORMANCE, SECURITY, COMPLETENESS, BEST_PRACTICES, GENERAL -- Use when you need someone to find what's wrong with your approach - -BALANCED REVIEW: -- Use the review tool for constructive, balanced code review -- Provides objective assessment without predetermined bias -- You can request up to 3 reviews per task -- Focus areas: GENERAL, FUNCTIONALITY, READABILITY, MAINTAINABILITY, TESTING, DOCUMENTATION, ARCHITECTURE -- Use for regular code review and feedback - -CREATIVE GUIDANCE: -- Use the zen tool when you need creative problem-solving approaches -- Rolls one of 64 Hanzo Zen philosophies with engineering principles -- Provides unique perspectives and actionable guidance -- Use when stuck, need fresh ideas, or want philosophical alignment - -EDITING GUIDELINES: -- ALWAYS read the file first before attempting any edits -- For edit tool: The old_string must match EXACTLY including all whitespace, tabs, and newlines -- When copying text from read output, be careful with line numbers and indentation -- If an edit fails due to whitespace mismatch, try reading the specific lines again -- Prefer multi_edit when making multiple changes to the same file -- Test your edits by verifying the exact string exists in the file first - -COLLABORATION: -- If you see MCP tools named after other agents, you can communicate with them -- Use agent MCP tools to delegate specialized tasks or get expert opinions -- Share context when communicating with other agents - -RESPONSE FORMAT: -- Begin with a summary of what you did or found -- If you have questions, ask them clearly -- Include details of any edits performed -- Report any errors encountered -- End with clear conclusions or next steps -""" - - return system_prompt - - -def get_default_model(model_override: str | None = None) -> str: - """Get the default model for agent execution. - - Args: - model_override: Optional model override string in LLM format (e.g., "openai/gpt-4o") - - Returns: - Model identifier string with provider prefix - """ - # Use model override if provided - if model_override: - # If in testing mode and using a test model, return as-is - if model_override.startswith("test-model") or "TEST_MODE" in os.environ: - return model_override - - # If the model already has a provider prefix, return as-is - if "/" in model_override: - return model_override - - # Otherwise, add the default provider prefix - provider = os.environ.get("AGENT_PROVIDER", "openai") - return f"{provider}/{model_override}" - - # Fall back to environment variables - # Default to Sonnet for cost efficiency - model = os.environ.get("AGENT_MODEL", "claude-3-5-sonnet-20241022") - - # Special cases for tests - if ( - model.startswith("test-model") - or "TEST_MODE" in os.environ - and model == "claude-3-5-sonnet-20241022" - ): - return model - - provider = os.environ.get("AGENT_PROVIDER", "anthropic") - - # Only add provider prefix if it's not already in the model name - if "/" not in model and provider != "anthropic": - return f"{provider}/{model}" - elif "/" not in model and provider == "anthropic": - return f"anthropic/{model}" - elif "/" not in model: - return f"openai/{model}" - else: - # Model already has a provider prefix - return model - - -def get_model_parameters(max_tokens: int | None = None) -> dict[str, Any]: - """Get model parameters from environment variables. - - Args: - max_tokens: Optional maximum tokens parameter override - - Returns: - Dictionary of model parameters - """ - params = { - "temperature": float(os.environ.get("AGENT_TEMPERATURE", "0.7")), - "timeout": int(os.environ.get("AGENT_API_TIMEOUT", "60")), - } - - # Add max_tokens if provided or if set in environment variable - if max_tokens is not None: - params["max_tokens"] = max_tokens - elif os.environ.get("AGENT_MAX_TOKENS"): - params["max_tokens"] = int(os.environ.get("AGENT_MAX_TOKENS", "1000")) - - return params diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/review_tool.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/review_tool.py deleted file mode 100644 index 510ded7d3..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/review_tool.py +++ /dev/null @@ -1,441 +0,0 @@ -"""Review tool for agents to request balanced code review from main loop.""" - -from enum import Enum -from typing import List, Optional, override - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout - - -class ReviewFocus(Enum): - """Types of review focus areas.""" - - GENERAL = "general" - FUNCTIONALITY = "functionality" - READABILITY = "readability" - MAINTAINABILITY = "maintainability" - TESTING = "testing" - DOCUMENTATION = "documentation" - ARCHITECTURE = "architecture" - - -class ReviewTool(BaseTool): - """Tool for agents to request balanced code review from the main loop.""" - - name = "review" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Request a balanced, constructive code review from the main loop. - -Unlike the critic tool (which plays devil's advocate), this provides: -- Objective assessment of code quality -- Recognition of what's done well -- Constructive suggestions for improvement -- Focus on practical concerns -- No predetermined bias or harsh judgment - -Parameters: -- focus: Review focus area (GENERAL, FUNCTIONALITY, READABILITY, MAINTAINABILITY, TESTING, DOCUMENTATION, ARCHITECTURE) -- work_description: Clear description of what you've implemented -- code_snippets: Optional code snippets to review (as a list of strings) -- file_paths: Optional list of file paths you've modified -- context: Optional additional context about the implementation - -The review will be balanced, highlighting both strengths and areas for improvement. - -Example: -review( - focus="FUNCTIONALITY", - work_description="Implemented auto-import feature for Go files", - code_snippets=["func AddImport(file string, importPath string) error { ... }"], - file_paths=["/path/to/import_handler.go"], - context="This will be used to automatically fix missing imports in Go files" -)""" - - @auto_timeout("review") - async def call( - self, - ctx: MCPContext, - focus: str, - work_description: str, - code_snippets: Optional[List[str]] = None, - file_paths: Optional[List[str]] = None, - context: Optional[str] = None, - ) -> str: - """Delegate to AgentTool for actual implementation. - - This method provides the interface, but the actual review logic - is handled by the AgentTool's execution framework. - """ - # This tool is handled specially in the agent execution - return f"Review requested for: {work_description}" - - def register(self, server: FastMCP) -> None: - """Register the tool with the MCP server.""" - tool_self = self - - @server.tool(name=self.name, description=self.description) - async def review( - ctx: MCPContext, - focus: str, - work_description: str, - code_snippets: Optional[List[str]] = None, - file_paths: Optional[List[str]] = None, - context: Optional[str] = None, - ) -> str: - return await tool_self.call( - ctx, focus, work_description, code_snippets, file_paths, context - ) - - -class BalancedReviewer: - """Provides balanced, constructive code reviews.""" - - def __init__(self): - self.review_handlers = { - ReviewFocus.GENERAL: self._review_general, - ReviewFocus.FUNCTIONALITY: self._review_functionality, - ReviewFocus.READABILITY: self._review_readability, - ReviewFocus.MAINTAINABILITY: self._review_maintainability, - ReviewFocus.TESTING: self._review_testing, - ReviewFocus.DOCUMENTATION: self._review_documentation, - ReviewFocus.ARCHITECTURE: self._review_architecture, - } - - def review( - self, - focus: ReviewFocus, - work_description: str, - code_snippets: Optional[List[str]] = None, - file_paths: Optional[List[str]] = None, - context: Optional[str] = None, - ) -> str: - """Perform a balanced code review.""" - review_func = self.review_handlers.get(focus, self._review_general) - return review_func(work_description, code_snippets, file_paths, context) - - def _review_general( - self, - work_description: str, - code_snippets: Optional[List[str]], - file_paths: Optional[List[str]], - context: Optional[str], - ) -> str: - """Provide a general balanced review.""" - response = "๐Ÿ“‹ GENERAL CODE REVIEW:\n\n" - response += f"**Work Reviewed:** {work_description}\n\n" - - # Positive observations - response += "**Positive Aspects:**\n" - if "fix" in work_description.lower(): - response += "โœ“ Addressing identified issues proactively\n" - if "implement" in work_description.lower(): - response += "โœ“ Adding new functionality to enhance the system\n" - if code_snippets: - response += "โœ“ Code structure appears organized\n" - if file_paths and len(file_paths) == 1: - response += "โœ“ Focused changes in a single file (good for reviewability)\n" - elif file_paths and len(file_paths) > 1: - response += "โœ“ Comprehensive approach across multiple files\n" - - # Constructive suggestions - response += "\n**Suggestions for Consideration:**\n" - response += "โ€ข Ensure all edge cases are handled appropriately\n" - response += "โ€ข Consider adding unit tests if not already present\n" - response += "โ€ข Verify the changes integrate well with existing code\n" - - if context: - response += f"\n**Context Consideration:**\n{context}\n" - response += "โ†’ This context helps understand the implementation choices.\n" - - # Summary - response += "\n**Summary:**\n" - response += "The implementation appears sound. Consider the suggestions above to further strengthen the code." - - return response - - def _review_functionality( - self, - work_description: str, - code_snippets: Optional[List[str]], - file_paths: Optional[List[str]], - context: Optional[str], - ) -> str: - """Review functionality aspects.""" - response = "๐Ÿ“‹ FUNCTIONALITY REVIEW:\n\n" - response += f"**Implementation:** {work_description}\n\n" - - response += "**Functional Assessment:**\n" - - # Analyze code snippets if provided - if code_snippets: - for i, snippet in enumerate(code_snippets, 1): - response += f"\nCode Snippet {i}:\n" - - # Check for function definitions - if "func " in snippet or "def " in snippet or "function " in snippet: - response += "โœ“ Function definition looks properly structured\n" - - # Check for error handling - if "error" in snippet or "err" in snippet or "try" in snippet: - response += "โœ“ Error handling is present\n" - elif "return" in snippet: - response += "โ€ข Consider adding error handling if applicable\n" - - # Check for input validation - if "if " in snippet or "check" in snippet.lower(): - response += "โœ“ Input validation appears to be present\n" - - response += "\n**Functional Considerations:**\n" - response += "โ€ข Does the implementation handle all expected inputs?\n" - response += "โ€ข Are return values meaningful and consistent?\n" - response += "โ€ข Is the functionality easily testable?\n" - response += "โ€ข Does it integrate well with existing features?\n" - - response += "\n**Overall:** The functionality appears to meet the described requirements." - - return response - - def _review_readability( - self, - work_description: str, - code_snippets: Optional[List[str]], - file_paths: Optional[List[str]], - context: Optional[str], - ) -> str: - """Review code readability.""" - response = "๐Ÿ“‹ READABILITY REVIEW:\n\n" - - response += "**Readability Factors:**\n" - - if code_snippets: - total_lines = sum(snippet.count("\n") + 1 for snippet in code_snippets) - avg_line_length = sum( - len(line) for snippet in code_snippets for line in snippet.split("\n") - ) / max(total_lines, 1) - - if avg_line_length < 80: - response += "โœ“ Line lengths are reasonable\n" - else: - response += ( - "โ€ข Some lines might be too long, consider breaking them up\n" - ) - - # Check naming - has_good_names = any( - any( - word in snippet - for word in ["Add", "Get", "Set", "Create", "Update", "Delete"] - ) - for snippet in code_snippets - ) - if has_good_names: - response += "โœ“ Function/method names appear descriptive\n" - - response += "\n**Readability Suggestions:**\n" - response += "โ€ข Use meaningful variable and function names\n" - response += "โ€ข Keep functions focused on a single responsibility\n" - response += "โ€ข Add comments for complex logic sections\n" - response += "โ€ข Maintain consistent indentation and formatting\n" - - response += "\n**Overall:** Code readability appears acceptable with room for minor improvements." - - return response - - def _review_maintainability( - self, - work_description: str, - code_snippets: Optional[List[str]], - file_paths: Optional[List[str]], - context: Optional[str], - ) -> str: - """Review maintainability aspects.""" - response = "๐Ÿ“‹ MAINTAINABILITY REVIEW:\n\n" - - response += "**Maintainability Factors:**\n" - - # Check file organization - if file_paths: - if len(file_paths) == 1: - response += "โœ“ Changes are localized to a single file\n" - else: - response += "โœ“ Changes are logically distributed across files\n" - - # Check for modularity in code - if code_snippets: - function_count = sum( - snippet.count("func ") - + snippet.count("def ") - + snippet.count("function ") - for snippet in code_snippets - ) - if function_count > 0: - response += "โœ“ Code is broken into functions/methods\n" - - response += "\n**Maintainability Considerations:**\n" - response += "โ€ข Is the code modular and reusable?\n" - response += "โ€ข Are dependencies clearly defined?\n" - response += "โ€ข Will future developers understand the intent?\n" - response += "โ€ข Is the code structured to allow easy updates?\n" - - response += "\n**Recommendations:**\n" - response += "โ€ข Consider extracting common patterns into utilities\n" - response += "โ€ข Ensure consistent patterns across the codebase\n" - response += "โ€ข Document any non-obvious design decisions\n" - - return response - - def _review_testing( - self, - work_description: str, - code_snippets: Optional[List[str]], - file_paths: Optional[List[str]], - context: Optional[str], - ) -> str: - """Review testing aspects.""" - response = "๐Ÿ“‹ TESTING REVIEW:\n\n" - - has_test_files = any("test" in str(path).lower() for path in (file_paths or [])) - - if has_test_files: - response += "โœ“ Test files are included with the changes\n\n" - else: - response += "โš ๏ธ No test files detected in the changes\n\n" - - response += "**Testing Checklist:**\n" - response += "โ–ก Unit tests for new functions\n" - response += "โ–ก Integration tests for feature interactions\n" - response += "โ–ก Edge case coverage\n" - response += "โ–ก Error condition testing\n" - response += "โ–ก Performance tests (if applicable)\n" - - response += "\n**Testing Recommendations:**\n" - response += "โ€ข Write tests that document expected behavior\n" - response += "โ€ข Include both positive and negative test cases\n" - response += "โ€ข Ensure tests are maintainable and clear\n" - response += "โ€ข Aim for good coverage of critical paths\n" - - if not has_test_files: - response += "\n๐Ÿ’ก Consider adding tests to ensure reliability and prevent regressions." - - return response - - def _review_documentation( - self, - work_description: str, - code_snippets: Optional[List[str]], - file_paths: Optional[List[str]], - context: Optional[str], - ) -> str: - """Review documentation aspects.""" - response = "๐Ÿ“‹ DOCUMENTATION REVIEW:\n\n" - - # Check for documentation in code - has_comments = False - if code_snippets: - has_comments = any( - "//" in snippet or "/*" in snippet or "#" in snippet or '"""' in snippet - for snippet in code_snippets - ) - - if has_comments: - response += "โœ“ Code includes some documentation\n" - else: - response += "โ€ข Consider adding documentation comments\n" - - response += "\n**Documentation Guidelines:**\n" - response += "โ€ข Document the 'why' not just the 'what'\n" - response += "โ€ข Include examples for complex functions\n" - response += "โ€ข Document any assumptions or limitations\n" - response += "โ€ข Keep documentation up-to-date with code changes\n" - - response += "\n**Recommended Documentation:**\n" - response += "โ€ข Function/method purpose and parameters\n" - response += "โ€ข Complex algorithm explanations\n" - response += "โ€ข API usage examples\n" - response += "โ€ข Configuration requirements\n" - - return response - - def _review_architecture( - self, - work_description: str, - code_snippets: Optional[List[str]], - file_paths: Optional[List[str]], - context: Optional[str], - ) -> str: - """Review architectural aspects.""" - response = "๐Ÿ“‹ ARCHITECTURE REVIEW:\n\n" - - response += "**Architectural Considerations:**\n" - - # Analyze file structure - if file_paths: - # Check for separation of concerns - has_separation = ( - len(set(str(p).split("/")[-2] for p in file_paths if "/" in str(p))) > 1 - ) - if has_separation: - response += "โœ“ Changes span multiple modules (good separation)\n" - else: - response += "โœ“ Changes are cohesive within a module\n" - - response += "\n**Architectural Principles:**\n" - response += "โ€ข Single Responsibility - Each component has one clear purpose\n" - response += "โ€ข Open/Closed - Open for extension, closed for modification\n" - response += "โ€ข Dependency Inversion - Depend on abstractions, not concretions\n" - response += "โ€ข Interface Segregation - Keep interfaces focused and minimal\n" - - response += "\n**Questions to Consider:**\n" - response += "โ€ข Does this fit well with the existing architecture?\n" - response += "โ€ข Are the right abstractions in place?\n" - response += "โ€ข Is the coupling between components appropriate?\n" - response += "โ€ข Will this scale as requirements grow?\n" - - if context: - response += f"\n**Context Impact:**\n{context}\n" - response += "โ†’ Ensure the architectural choices align with this context.\n" - - return response - - -class ReviewProtocol: - """Protocol for review interactions.""" - - def __init__(self): - self.reviewer = BalancedReviewer() - self.review_count = 0 - self.max_reviews = 3 # Allow up to 3 reviews per task - - def request_review( - self, - focus: str, - work_description: str, - code_snippets: Optional[List[str]] = None, - file_paths: Optional[List[str]] = None, - context: Optional[str] = None, - ) -> str: - """Request a balanced review.""" - if self.review_count >= self.max_reviews: - return "๐Ÿ“‹ Review limit reached. You've received comprehensive feedback - time to finalize your implementation." - - self.review_count += 1 - - try: - focus_enum = ReviewFocus[focus.upper()] - except KeyError: - focus_enum = ReviewFocus.GENERAL - - review = self.reviewer.review( - focus_enum, work_description, code_snippets, file_paths, context - ) - - header = f"Review {self.review_count}/{self.max_reviews} (Focus: {focus_enum.value}):\n\n" - footer = "\n\n๐Ÿ’ก This is a balanced review - consider both strengths and suggestions." - - return header + review + footer diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/swarm_alias.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/swarm_alias.py deleted file mode 100644 index 10bbf77ed..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/swarm_alias.py +++ /dev/null @@ -1,91 +0,0 @@ -from hanzo_tools.core import auto_timeout - -"""Swarm tool as an alias to Network tool for backward compatibility. - -This module makes swarm an alias to the network tool, as network is the -evolution of swarm with better distributed execution capabilities. -""" - -from hanzo_tools.core import PermissionManager - -from .network_tool import NetworkTool - - -class SwarmTool(NetworkTool): - """Swarm tool - alias to Network tool for backward compatibility. - - The swarm tool is now an alias to the network tool, which provides - all the same functionality plus additional distributed execution modes. - Use 'network' for new code, 'swarm' is maintained for compatibility. - """ - - @property - def name(self) -> str: - """Get the tool name.""" - return "swarm" - - @property - def description(self) -> str: - """Get the tool description.""" - return """Execute a network of AI agents (alias to 'network' tool). - -The 'swarm' tool is now an alias to the 'network' tool for backward compatibility. -All swarm functionality is available through network, which additionally provides: - -- Local-first execution with privacy preservation -- Distributed compute across devices -- Hybrid mode with cloud fallback -- Integration with hanzo-network for MCP-connected agents - -Examples: -```python -# These are equivalent: -swarm(task="Analyze code", agents=["analyzer", "reviewer"]) -network(task="Analyze code", agents=["analyzer", "reviewer"]) - -# Network adds new modes: -network(task="Process data", mode="local") # Privacy-first -network(task="Large analysis", mode="distributed") # Scale out -``` - -For new code, prefer using 'network' directly.""" - - def __init__( - self, - permission_manager: PermissionManager, - default_mode: str = "hybrid", - **kwargs, - ): - """Initialize swarm as an alias to network. - - Args: - permission_manager: Permission manager - default_mode: Default execution mode (hybrid/local/distributed) - **kwargs: Additional arguments passed to NetworkTool - """ - # Just pass through to NetworkTool - super().__init__( - permission_manager=permission_manager, default_mode=default_mode, **kwargs - ) - - @auto_timeout("swarm_alias") - async def call(self, **kwargs) -> str: - """Execute swarm via network tool. - - All parameters are passed through to the network tool. - """ - # For backward compatibility, rename some parameters if needed - if "config" in kwargs and "agents" not in kwargs: - # Old swarm used 'config' for agent definitions - config = kwargs.pop("config") - if isinstance(config, dict) and "agents" in config: - kwargs["agents"] = config["agents"] - if "topology" in config: - kwargs["routing"] = config["topology"] - - # Pass through to network tool - return await super().call(**kwargs) - - -# For backward compatibility exports -__all__ = ["SwarmTool"] diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/swarm_tool.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/swarm_tool.py deleted file mode 100644 index 07be86c37..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/swarm_tool.py +++ /dev/null @@ -1,717 +0,0 @@ -"""Swarm tool implementation using hanzo-agents SDK. - -This module implements the SwarmTool that leverages the hanzo-agents SDK -for sophisticated multi-agent orchestration with flexible network topologies. -""" - -import os -from typing import ( - Any, - Dict, - List, - Unpack, - Optional, - TypedDict, - final, - override, -) - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import auto_timeout - -# Import hanzo-agents SDK with fallback -try: - from hanzo_agents import ( - Tool, - Agent, - State, - Router, - History, - Network, - ToolCall, - ModelRegistry, - InferenceResult, - ) - - HANZO_AGENTS_AVAILABLE = True -except ImportError: - # hanzo-agents not installed - types raise ImportError on use - HANZO_AGENTS_AVAILABLE = False - - class _RequiresHanzoAgents(type): - """Raises ImportError on instantiation.""" - - def __call__(cls, *args, **kwargs): - raise ImportError(f"{cls.__name__} requires: pip install hanzo-agents") - - class Agent(metaclass=_RequiresHanzoAgents): - pass - - class State(metaclass=_RequiresHanzoAgents): - pass - - class Network(metaclass=_RequiresHanzoAgents): - pass - - class Tool(metaclass=_RequiresHanzoAgents): - pass - - class History(metaclass=_RequiresHanzoAgents): - pass - - class ModelRegistry(metaclass=_RequiresHanzoAgents): - pass - - class InferenceResult(metaclass=_RequiresHanzoAgents): - pass - - class ToolCall(metaclass=_RequiresHanzoAgents): - pass - - class Router(metaclass=_RequiresHanzoAgents): - pass - - -# Import optional components with fallbacks -try: - from hanzo_agents import LLMRouter, HybridRouter, DeterministicRouter -except ImportError: - try: - # Try core module import - from hanzo_agents.core.router import ( - LLMRouter, - HybridRouter, - DeterministicRouter, - ) - except ImportError: - - class DeterministicRouter(metaclass=_RequiresHanzoAgents): - pass - - class LLMRouter(metaclass=_RequiresHanzoAgents): - pass - - class HybridRouter(metaclass=_RequiresHanzoAgents): - pass - - -def _requires_hanzo_agents(name: str): - """Create function that raises ImportError.""" - - def fn(*args, **kwargs): - raise ImportError(f"{name} requires: pip install hanzo-agents") - - fn.__name__ = name - return fn - - -try: - from hanzo_agents import create_memory_kv, create_memory_vector -except ImportError: - try: - from hanzo_agents.core.memory import create_memory_kv, create_memory_vector - except ImportError: - create_memory_kv = _requires_hanzo_agents("create_memory_kv") - create_memory_vector = _requires_hanzo_agents("create_memory_vector") - - -try: - from hanzo_agents import sequential_router, conditional_router, state_based_router -except ImportError: - try: - from hanzo_agents.core.router import ( - sequential_router, - conditional_router, - state_based_router, - ) - except ImportError: - sequential_router = _requires_hanzo_agents("sequential_router") - conditional_router = _requires_hanzo_agents("conditional_router") - state_based_router = _requires_hanzo_agents("state_based_router") - - -try: - from hanzo_agents.core.cli_agent import ( - GrokAgent, - GeminiAgent, - ClaudeCodeAgent, - OpenAICodexAgent, - ) -except ImportError: - - class ClaudeCodeAgent(metaclass=_RequiresHanzoAgents): - pass - - class OpenAICodexAgent(metaclass=_RequiresHanzoAgents): - pass - - class GeminiAgent(metaclass=_RequiresHanzoAgents): - pass - - class GrokAgent(metaclass=_RequiresHanzoAgents): - pass - - -from hanzo_tools.fs import EditTool, get_read_only_filesystem_tools -from hanzo_tools.core import BaseTool, PermissionManager, create_tool_context -from hanzo_tools.jupyter import get_read_only_jupyter_tools - -from .agent_tool import MCPAgent - - -class AgentNode(TypedDict): - """Node in the agent network.""" - - id: str - query: str - model: Optional[str] - role: Optional[str] - connections: Optional[List[str]] - receives_from: Optional[List[str]] - file_path: Optional[str] - - -class SwarmConfig(TypedDict): - """Configuration for an agent network.""" - - agents: Dict[str, AgentNode] - entry_point: Optional[str] - topology: Optional[str] - - -class SwarmToolParams(TypedDict): - """Parameters for the SwarmTool.""" - - config: SwarmConfig - query: str - context: Optional[str] - max_concurrent: Optional[int] - use_memory: Optional[bool] - memory_backend: Optional[str] - - -class SwarmState(State): - """State for swarm execution.""" - - def __init__( - self, config: SwarmConfig, initial_query: str, context: Optional[str] = None - ): - """Initialize swarm state.""" - super().__init__() - self.config = config - self.initial_query = initial_query - self.context = context - self.agent_results = {} - self.completed_agents = set() - self.current_agent = None - self.execution_order = [] - - def to_dict(self) -> Dict[str, Any]: - """Convert to dictionary.""" - base_dict = super().to_dict() - base_dict.update( - { - "config": self.config, - "initial_query": self.initial_query, - "context": self.context, - "agent_results": self.agent_results, - "completed_agents": list(self.completed_agents), - "current_agent": self.current_agent, - "execution_order": self.execution_order, - } - ) - return base_dict - - @classmethod - def from_dict(cls, data: Dict[str, Any]) -> "SwarmState": - """Create from dictionary.""" - state = cls( - config=data.get("config", {}), - initial_query=data.get("initial_query", ""), - context=data.get("context"), - ) - state.agent_results = data.get("agent_results", {}) - state.completed_agents = set(data.get("completed_agents", [])) - state.current_agent = data.get("current_agent") - state.execution_order = data.get("execution_order", []) - return state - - -class SwarmAgent(MCPAgent): - """Agent that executes within a swarm network.""" - - def __init__( - self, - agent_id: str, - agent_config: AgentNode, - available_tools: List[BaseTool], - permission_manager: PermissionManager, - ctx: MCPContext, - **kwargs, - ): - """Initialize swarm agent.""" - # Set name and description from config - self.name = agent_id - self.description = agent_config.get("role", f"Agent {agent_id}") - self.agent_config = agent_config - - # Initialize with specified model - model = agent_config.get("model") - if model: - model = self._normalize_model(model) - else: - model = "model://anthropic/claude-3-5-sonnet-20241022" - - super().__init__( - available_tools=available_tools, - permission_manager=permission_manager, - ctx=ctx, - model=model, - **kwargs, - ) - - def _normalize_model(self, model: str) -> str: - """Normalize model names to full format.""" - model_map = { - "claude-3-5-sonnet": "model://anthropic/claude-3-5-sonnet-20241022", - "claude-3-opus": "model://anthropic/claude-3-opus-20240229", - "gpt-4o": "model://openai/gpt-4o", - "gpt-4": "model://openai/gpt-4", - "gemini-1.5-pro": "model://google/gemini-1.5-pro", - "gemini-1.5-flash": "model://google/gemini-1.5-flash", - } - - # Check if it's already a model:// URI - if model.startswith("model://"): - return model - - # Check mapping - if model in model_map: - return model_map[model] - - # Assume it's a provider/model format - if "/" in model: - return f"model://{model}" - - # Default to anthropic - return f"model://anthropic/{model}" - - async def run( - self, state: SwarmState, history: History, network: Network - ) -> InferenceResult: - """Execute the swarm agent.""" - # Build prompt with context - prompt_parts = [] - - # Add role context - if self.agent_config.get("role"): - prompt_parts.append(f"Your role: {self.agent_config['role']}") - - # Add shared context - if state.context: - prompt_parts.append(f"Context:\n{state.context}") - - # Add inputs from connected agents - receives_from = self.agent_config.get("receives_from", []) - if receives_from: - inputs = {} - for agent_id in receives_from: - if agent_id in state.agent_results: - inputs[agent_id] = state.agent_results[agent_id] - - if inputs: - prompt_parts.append("Input from previous agents:") - for input_agent, input_result in inputs.items(): - prompt_parts.append(f"\n--- From {input_agent} ---\n{input_result}") - - # Add file context if specified - if self.agent_config.get("file_path"): - prompt_parts.append(f"\nFile to work on: {self.agent_config['file_path']}") - - # Add the main query - prompt_parts.append(f"\nTask: {self.agent_config['query']}") - - # Add initial query if this is entry point - if state.current_agent == state.config.get("entry_point"): - prompt_parts.append(f"\nMain objective: {state.initial_query}") - - full_prompt = "\n\n".join(prompt_parts) - - # Execute using base class - messages = [ - {"role": "system", "content": self._get_system_prompt()}, - {"role": "user", "content": full_prompt}, - ] - - # Call model - from hanzo_agents import ModelRegistry - - adapter = ModelRegistry.get_adapter(self.model) - response = await adapter.chat(messages) - - # Store result in state - state.agent_results[self.name] = response - state.completed_agents.add(self.name) - state.execution_order.append(self.name) - - # Return result - return InferenceResult( - agent=self.name, - content=response, - metadata={ - "agent_id": self.name, - "role": self.agent_config.get("role"), - "connections": self.agent_config.get("connections", []), - }, - ) - - -class SwarmRouter(DeterministicRouter): - """Router for swarm agent orchestration.""" - - def __init__(self, swarm_config: SwarmConfig): - """Initialize swarm router.""" - self.swarm_config = swarm_config - self.agents_config = swarm_config["agents"] - self.entry_point = swarm_config.get("entry_point") - - # Build dependency graph - self.dependencies = {} - self.dependents = {} - - for agent_id, config in self.agents_config.items(): - # Dependencies (agents this one waits for) - self.dependencies[agent_id] = set(config.get("receives_from", [])) - - # Dependents (agents that wait for this one) - connections = config.get("connections", []) - for conn in connections: - if conn not in self.dependents: - self.dependents[conn] = set() - self.dependents[conn].add(agent_id) - - def route(self, network, call_count, last_result, agent_stack): - """Determine next agent to execute.""" - state = network.state - - # First call - start with entry point or roots - if call_count == 0: - if self.entry_point: - state.current_agent = self.entry_point - return self._get_agent_class(self.entry_point, agent_stack) - else: - # Find roots (no dependencies) - roots = [aid for aid, deps in self.dependencies.items() if not deps] - if roots: - state.current_agent = roots[0] - return self._get_agent_class(roots[0], agent_stack) - - # Find next agent to execute - for agent_id in self.agents_config: - if agent_id in state.completed_agents: - continue - - # Check if all dependencies are met - deps = self.dependencies.get(agent_id, set()) - if deps.issubset(state.completed_agents): - state.current_agent = agent_id - return self._get_agent_class(agent_id, agent_stack) - - # No more agents to execute - return None - - def _get_agent_class( - self, agent_id: str, agent_stack: List[type[Agent]] - ) -> type[Agent]: - """Get agent class for given agent ID.""" - # Find matching agent by name - for agent_class in agent_stack: - if hasattr(agent_class, "name") and agent_class.name == agent_id: - return agent_class - - # Not found - this shouldn't happen - return None - - -@final -class SwarmTool(BaseTool): - """Tool for executing agent networks using hanzo-agents SDK.""" - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "swarm" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Execute a network of AI agents with flexible connection topologies. - -This tool enables sophisticated agent orchestration where agents can be connected -in various network patterns. Each agent can pass results to connected agents, -enabling complex workflows. - -Features: -- Flexible agent networks (tree, DAG, pipeline, star, mesh) -- Each agent can use different models (Claude, GPT-4, Gemini, etc.) -- Agents automatically pass results to connected agents -- Parallel execution with dependency management -- Full editing capabilities for each agent -- Memory and state management via hanzo-agents SDK - -Common Topologies: -1. Tree (Architect pattern): - architect โ†’ [frontend, backend, database] โ†’ reviewer - -2. Pipeline (Sequential processing): - analyzer โ†’ planner โ†’ implementer โ†’ tester โ†’ reviewer - -3. Star (Central coordinator): - coordinator โ† โ†’ [agent1, agent2, agent3, agent4] - -4. DAG (Complex dependencies): - Multiple agents with custom connections - -Models can be specified as: -- Full: 'anthropic/claude-3-5-sonnet-20241022' -- Short: 'claude-3-5-sonnet', 'gpt-4o', 'gemini-1.5-pro' -- CLI tools: 'claude_cli', 'codex_cli', 'gemini_cli', 'grok_cli' -- Model URIs: 'model://anthropic/claude-3-opus' -""" - - def __init__( - self, - permission_manager: PermissionManager, - model: str | None = None, - api_key: str | None = None, - base_url: str | None = None, - max_tokens: int | None = None, - agent_max_iterations: int = 10, - agent_max_tool_uses: int = 30, - ): - """Initialize the swarm tool.""" - self.permission_manager = permission_manager - # Default to latest Claude Sonnet if no model specified - from .code_auth import get_latest_claude_model - - self.model = model or f"anthropic/{get_latest_claude_model()}" - self.api_key = ( - api_key - or os.environ.get("ANTHROPIC_API_KEY") - or os.environ.get("CLAUDE_API_KEY") - ) - self.base_url = base_url - self.max_tokens = max_tokens - self.agent_max_iterations = agent_max_iterations - self.agent_max_tool_uses = agent_max_tool_uses - - # Set up available tools for agents - self.available_tools: list[BaseTool] = [] - self.available_tools.extend( - get_read_only_filesystem_tools(self.permission_manager) - ) - self.available_tools.extend( - get_read_only_jupyter_tools(self.permission_manager) - ) - - # Add edit tool - self.available_tools.append(EditTool(self.permission_manager)) - - @override - @auto_timeout("swarm") - async def call( - self, - ctx: MCPContext, - **params: Unpack[SwarmToolParams], - ) -> str: - """Execute the swarm tool.""" - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - config = params.get("config", {}) - initial_query = params.get("query", "") - context = params.get("context", "") - max_concurrent = params.get("max_concurrent", 10) - use_memory = params.get("use_memory", False) - memory_backend = params.get("memory_backend", "sqlite") - - agents_config = config.get("agents", {}) - - if not agents_config: - await tool_ctx.error("No agents provided") - return "Error: At least one agent must be provided." - - # hanzo-agents SDK is required (already imported above) - - await tool_ctx.info( - f"Starting swarm execution with {len(agents_config)} agents using hanzo-agents SDK" - ) - - # Create state - state = SwarmState(config=config, initial_query=initial_query, context=context) - - # Create agent classes dynamically - agent_classes = [] - for agent_id, agent_config in agents_config.items(): - # Check for CLI agents - model = agent_config.get("model", self.model) - - cli_agents = { - "claude_cli": ClaudeCodeAgent, - "codex_cli": OpenAICodexAgent, - "gemini_cli": GeminiAgent, - "grok_cli": GrokAgent, - } - - if model in cli_agents: - # Use CLI agent - agent_class = type( - f"Swarm{agent_id}", - (cli_agents[model],), - { - "name": agent_id, - "description": agent_config.get("role", f"Agent {agent_id}"), - "agent_config": agent_config, - }, - ) - else: - # Create dynamic SwarmAgent class - agent_class = type( - f"Swarm{agent_id}", - (SwarmAgent,), - { - "name": agent_id, - "__init__": lambda self, aid=agent_id, acfg=agent_config: SwarmAgent.__init__( - self, - agent_id=aid, - agent_config=acfg, - available_tools=self.available_tools, - permission_manager=self.permission_manager, - ctx=ctx, - ), - }, - ) - - agent_classes.append(agent_class) - - # Create memory if requested - memory_kv = None - memory_vector = None - if use_memory: - memory_kv = create_memory_kv(memory_backend) - memory_vector = create_memory_vector("simple") - - # Create router - router = SwarmRouter(config) - - # Create network - network = Network( - state=state, - agents=agent_classes, - router=router, - memory_kv=memory_kv, - memory_vector=memory_vector, - max_steps=self.agent_max_iterations * len(agents_config), - ) - - # Execute - try: - final_state = await network.run() - - # Format results - return self._format_network_results( - agents_config, - final_state.agent_results, - final_state.execution_order, - config.get("entry_point"), - ) - - except Exception as e: - await tool_ctx.error(f"Swarm execution failed: {str(e)}") - return f"Error: {str(e)}" - - def _format_network_results( - self, - agents_config: Dict[str, Any], - results: Dict[str, str], - execution_order: List[str], - entry_point: Optional[str], - ) -> str: - """Format results from agent network execution.""" - output = ["Agent Network Execution Results (hanzo-agents SDK)"] - output.append("=" * 80) - output.append(f"Total agents: {len(agents_config)}") - output.append(f"Completed: {len(results)}") - output.append( - f"Failed: {len([r for r in results.values() if r.startswith('Error:')])}" - ) - - if entry_point: - output.append(f"Entry point: {entry_point}") - - output.append(f"\nExecution Order: {' โ†’ '.join(execution_order)}") - output.append("-" * 40) - - # Detailed results - output.append("\n\nDetailed Results:") - output.append("=" * 80) - - for agent_id in execution_order: - if agent_id in results: - config = agents_config.get(agent_id, {}) - role = config.get("role", "Agent") - model = config.get("model", "default") - - output.append(f"\n### {agent_id} ({role}) [{model}]") - output.append("-" * 40) - - result = results[agent_id] - if result.startswith("Error:"): - output.append(result) - else: - # Show first part of result - lines = result.split("\n") - preview_lines = lines[:10] - output.extend(preview_lines) - - if len(lines) > 10: - output.append(f"... ({len(lines) - 10} more lines)") - - return "\n".join(output) - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register this swarm tool with the MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def swarm( - ctx: MCPContext, - config: dict[str, Any], - query: str, - context: Optional[str] = None, - max_concurrent: int = 10, - use_memory: bool = False, - memory_backend: str = "sqlite", - ) -> str: - # Convert to typed format - typed_config = SwarmConfig( - agents=config.get("agents", {}), - entry_point=config.get("entry_point"), - topology=config.get("topology"), - ) - - return await tool_self.call( - ctx, - config=typed_config, - query=query, - context=context, - max_concurrent=max_concurrent, - use_memory=use_memory, - memory_backend=memory_backend, - ) diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/tool_adapter.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/tool_adapter.py deleted file mode 100644 index 99e50af6f..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/tool_adapter.py +++ /dev/null @@ -1,109 +0,0 @@ -"""Tool adapters for converting between MCP tools and OpenAI tools. - -This module handles conversion between MCP tool formats and OpenAI function -formats, making MCP tools available to the OpenAI API, and processing tool inputs -and outputs for agent execution. -""" - -# Import llm with warnings suppressed -import warnings - -from openai.types import FunctionParameters -from openai.types.chat import ChatCompletionToolParam - -with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - -from hanzo_tools.core import BaseTool - - -def convert_tools_to_openai_functions( - tools: list[BaseTool], -) -> list[ChatCompletionToolParam]: - """Convert MCP tools to OpenAI function format. - - Args: - tools: List of MCP tools - - Returns: - List of tools formatted for OpenAI API - """ - openai_tools: list[ChatCompletionToolParam] = [] - for tool in tools: - openai_tool: ChatCompletionToolParam = { - "type": "function", - "function": { - "name": tool.name, - "description": tool.description, - "parameters": convert_tool_parameters(tool), - }, - } - openai_tools.append(openai_tool) - return openai_tools - - -def convert_tool_parameters(tool: BaseTool) -> FunctionParameters: - """Convert tool parameters to OpenAI format. - - Args: - tool: MCP tool - - Returns: - Parameter schema in OpenAI format - """ - # Start with a copy of the parameters - params = tool.parameters.copy() - - # Ensure the schema has the right format for OpenAI - if "properties" not in params: - params["properties"] = {} - - if "type" not in params: - params["type"] = "object" - - if "required" not in params: - params["required"] = tool.required - - return params - - -def supports_parallel_function_calling(model: str) -> bool: - """Check if a model supports parallel function calling. - - Args: - model: Model identifier in LLM format (e.g., "openai/gpt-4-turbo-preview") - - Returns: - True if the model supports parallel function calling, False otherwise - """ - # Since llm doesn't have this function, we'll implement a simple check - # based on known models that support parallel function calling - parallel_capable_models = { - # OpenAI models that support parallel function calling - "gpt-4-turbo", - "gpt-4-turbo-preview", - "gpt-4-turbo-2024-04-09", - "gpt-4o", - "gpt-4o-mini", - "gpt-4o-2024-05-13", - "gpt-4o-2024-08-06", - "gpt-3.5-turbo", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-1106", - # Anthropic models with tool support - "claude-3-opus", - "claude-3-sonnet", - "claude-3-haiku", - "claude-3-5-sonnet", - "claude-3-5-sonnet-20241022", - } - - # Extract model name without provider prefix - model_name = model.split("/")[-1] if "/" in model else model - - # Check if the base model name matches any known parallel-capable models - for capable_model in parallel_capable_models: - if model_name.startswith(capable_model): - return True - - return False diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/unified_agent_tool.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/unified_agent_tool.py deleted file mode 100644 index 7fc2033e8..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/unified_agent_tool.py +++ /dev/null @@ -1,467 +0,0 @@ -"""Unified agent tool - lightweight agent spawning. - -Single tool that dispatches to installed CLI agents and supports: -- claude: Claude Code CLI (default when running in Claude Code) -- codex: OpenAI Codex CLI -- gemini: Google Gemini CLI -- grok: xAI Grok CLI -- qwen: Alibaba Qwen CLI -- vibe: Vibe coding agent -- code: Hanzo Code agent -- dev: Hanzo Dev agent (default) - -Key features: -- Auto-detects Claude Code environment and uses same auth -- Shares hanzo-mcp config with spawned agents -- Lightweight - no heavy dependencies -""" - -import os -import json -import asyncio -from typing import List, Literal, Optional, Annotated, final, override -from pathlib import Path - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context - -Action = Annotated[ - Literal[ - "run", # Run a specific agent - "list", # List available agents - "status", # Check agent status - "config", # Show/set agent config - ], - Field(description="Agent action to perform"), -] - - -def detect_claude_code_env() -> dict: - """Detect if running inside Claude Code and get auth info. - - Returns dict with: - running_in_claude: bool - session_id: Optional[str] - auth_token: Optional[str] - api_key: Optional[str] - """ - result = { - "running_in_claude": False, - "session_id": None, - "auth_token": None, - "api_key": None, - } - - # Check Claude Code environment markers - if os.environ.get("CLAUDE_CODE") or os.environ.get("CLAUDE_SESSION_ID"): - result["running_in_claude"] = True - result["session_id"] = os.environ.get("CLAUDE_SESSION_ID") - - # Check for Claude auth - if os.environ.get("ANTHROPIC_API_KEY"): - result["api_key"] = os.environ.get("ANTHROPIC_API_KEY") - - # Check Claude Desktop config for OAuth tokens - claude_config_path = ( - Path.home() / "Library/Application Support/Claude/claude_desktop_config.json" - ) - if claude_config_path.exists(): - try: - with open(claude_config_path) as f: - config = json.load(f) - if "mcpServers" in config and "hanzo-mcp" in config["mcpServers"]: - result["running_in_claude"] = True - except Exception: - pass - - return result - - -def get_mcp_config() -> dict: - """Get current hanzo-mcp config to share with spawned agents.""" - config = {} - - # Get relevant environment variables - mcp_env_vars = [ - "HANZO_MCP_MODE", - "HANZO_MCP_ALLOWED_PATHS", - "HANZO_MCP_ENABLED_TOOLS", - "HANZO_MCP_PERSONA", - "ANTHROPIC_API_KEY", - "OPENAI_API_KEY", - ] - - for var in mcp_env_vars: - if os.environ.get(var): - config[var] = os.environ.get(var) - - return config - - -@final -class UnifiedAgentTool(BaseTool): - """Unified agent tool for running CLI agents. - - Lightweight agent spawning that: - - Auto-detects Claude Code environment - - Shares hanzo-mcp config with child agents - - Supports multiple agent backends - """ - - name = "agent" - - # Available agent configurations - AGENTS = { - "claude": { - "command": "claude", - "args": ["-p"], # Use -p for print mode (non-interactive) - "description": "Anthropic Claude Code CLI (recommended when in Claude)", - "check": ["claude", "--version"], - "env_key": "ANTHROPIC_API_KEY", - "priority": 1, # Highest priority when in Claude env - }, - "codex": { - "command": "codex", - "args": [], - "description": "OpenAI Codex CLI", - "check": ["codex", "--version"], - "env_key": "OPENAI_API_KEY", - "priority": 2, - }, - "gemini": { - "command": "gemini", - "args": [], - "description": "Google Gemini CLI", - "check": ["gemini", "--version"], - "env_key": "GOOGLE_API_KEY", - "priority": 3, - }, - "grok": { - "command": "grok", - "args": [], - "description": "xAI Grok CLI", - "check": ["grok", "--version"], - "env_key": "XAI_API_KEY", - "priority": 4, - }, - "qwen": { - "command": "qwen", - "args": [], - "description": "Alibaba Qwen CLI", - "check": ["qwen", "--version"], - "env_key": "DASHSCOPE_API_KEY", - "priority": 5, - }, - "vibe": { - "command": "vibe", - "args": [], - "description": "Vibe coding agent", - "check": ["vibe", "--version"], - "priority": 6, - }, - "code": { - "command": "hanzo-code", - "args": [], - "description": "Hanzo Code agent", - "check": ["hanzo-code", "--version"], - "priority": 7, - }, - "dev": { - "command": "hanzo-dev", - "args": [], - "description": "Hanzo Dev agent (full development assistant)", - "check": ["hanzo-dev", "--version"], - "priority": 8, - }, - } - - def __init__(self): - super().__init__() - self._claude_env = detect_claude_code_env() - self._mcp_config = get_mcp_config() - - @property - @override - def description(self) -> str: - default_agent = self._get_default_agent() - return f"""Run AI agents by name. Lightweight agent spawning. - -Actions: -- run: Execute an agent with a prompt (default: {default_agent}) -- list: List available agents -- status: Check agent availability -- config: Show/set agent configuration - -Agents: claude, codex, gemini, grok, qwen, vibe, code, dev - -Examples: - agent run --prompt "Explain this code" # Uses default agent - agent run --name claude --prompt "Review this PR" - agent run --name dev --prompt "Fix the build" --cwd /project - agent list - agent status - -{"โšก Running in Claude Code - claude agent uses same auth" if self._claude_env["running_in_claude"] else ""} -""" - - def _get_default_agent(self) -> str: - """Get the default agent based on environment.""" - # If running in Claude Code, prefer claude - if self._claude_env.get("running_in_claude"): - return "claude" - - # Otherwise check which agents are configured - for name, config in sorted( - self.AGENTS.items(), key=lambda x: x[1].get("priority", 99) - ): - env_key = config.get("env_key") - if env_key and os.environ.get(env_key): - return name - - # Default fallback - return "dev" - - @override - @auto_timeout("agent") - async def call( - self, - ctx: MCPContext, - action: str = "run", - name: Optional[str] = None, - prompt: Optional[str] = None, - cwd: Optional[str] = None, - args: Optional[List[str]] = None, - timeout: int = 300, - share_config: bool = True, - **kwargs, - ) -> str: - """Execute agent operation.""" - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - if action == "list": - return self._list_agents() - elif action == "status": - return await self._check_status(name) - elif action == "config": - return self._show_config() - elif action == "run": - # Use default agent if not specified - agent_name = name or self._get_default_agent() - return await self._run_agent( - agent_name, prompt, cwd, args, timeout, share_config - ) - else: - return f"Unknown action: {action}. Use: run, list, status, config" - - def _list_agents(self) -> str: - """List available agents.""" - default = self._get_default_agent() - lines = ["Available agents:"] - - for name, config in sorted( - self.AGENTS.items(), key=lambda x: x[1].get("priority", 99) - ): - marker = " (default)" if name == default else "" - lines.append(f" โ€ข {name}: {config['description']}{marker}") - - lines.append("") - lines.append("Usage: agent run --prompt 'your prompt'") - lines.append(f" agent run --name --prompt 'prompt'") - - if self._claude_env.get("running_in_claude"): - lines.append("") - lines.append("โšก Running in Claude Code - using same authentication") - - return "\n".join(lines) - - def _show_config(self) -> str: - """Show current agent configuration.""" - lines = ["Agent Configuration:"] - lines.append(f" Default agent: {self._get_default_agent()}") - lines.append( - f" Running in Claude: {self._claude_env.get('running_in_claude', False)}" - ) - - if self._claude_env.get("session_id"): - lines.append(f" Claude session: {self._claude_env['session_id'][:8]}...") - - lines.append("") - lines.append("MCP Config (shared with spawned agents):") - for key, value in self._mcp_config.items(): - # Mask sensitive values - if "KEY" in key or "TOKEN" in key: - display = value[:8] + "..." if len(value) > 8 else "***" - else: - display = value - lines.append(f" {key}: {display}") - - return "\n".join(lines) - - async def _check_status(self, name: Optional[str]) -> str: - """Check if agents are available.""" - if not name: - # Check all agents - results = [] - for agent_name, config in sorted( - self.AGENTS.items(), key=lambda x: x[1].get("priority", 99) - ): - available = await self._is_available(config["check"]) - - # Check for API key - env_key = config.get("env_key") - has_key = bool(env_key and os.environ.get(env_key)) - - if available: - key_status = "โœ“ key" if has_key else "โ—‹ no key" - status = f"โœ“ installed ({key_status})" - else: - status = "โœ— not found" - - results.append(f" {agent_name}: {status}") - - return "Agent status:\n" + "\n".join(results) - - if name not in self.AGENTS: - return f"Unknown agent: {name}. Available: {', '.join(self.AGENTS.keys())}" - - config = self.AGENTS[name] - available = await self._is_available(config["check"]) - if available: - return f"โœ“ {name} is available" - return f"โœ— {name} is not installed or not in PATH" - - async def _is_available(self, check_cmd: List[str]) -> bool: - """Check if a command is available.""" - try: - proc = await asyncio.create_subprocess_exec( - *check_cmd, - stdout=asyncio.subprocess.DEVNULL, - stderr=asyncio.subprocess.DEVNULL, - ) - await asyncio.wait_for(proc.wait(), timeout=5) - return proc.returncode == 0 - except Exception: - return False - - async def _run_agent( - self, - name: str, - prompt: Optional[str], - cwd: Optional[str], - args: Optional[List[str]], - timeout: int, - share_config: bool, - ) -> str: - """Run an agent with a prompt.""" - if not prompt: - return "Error: prompt required for run action" - - if name not in self.AGENTS: - return f"Unknown agent: {name}. Available: {', '.join(self.AGENTS.keys())}" - - config = self.AGENTS[name] - command = config["command"] - default_args = config.get("args", []) - - # Build command - cmd_args = [command] + default_args + [prompt] - if args: - cmd_args.extend(args) - - # Build environment with shared MCP config - env = os.environ.copy() - if share_config: - env.update(self._mcp_config) - # Mark that this is a child agent - env["HANZO_AGENT_PARENT"] = "true" - env["HANZO_AGENT_NAME"] = name - - try: - proc = await asyncio.create_subprocess_exec( - *cmd_args, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - cwd=cwd or os.getcwd(), - env=env, - ) - - try: - stdout, stderr = await asyncio.wait_for( - proc.communicate(), - timeout=timeout, - ) - - output = stdout.decode("utf-8", errors="replace") - if proc.returncode != 0: - err = stderr.decode("utf-8", errors="replace") - return f"Agent {name} failed (exit {proc.returncode}):\n{output}\n{err}" - - return f"[{name}] {output}" - - except asyncio.TimeoutError: - proc.kill() - await proc.wait() - return f"Agent {name} timed out after {timeout}s" - - except FileNotFoundError: - # Provide helpful installation instructions - install_hints = { - "claude": "npm install -g @anthropic-ai/claude-code", - "codex": "npm install -g @openai/codex", - "gemini": "pip install google-generativeai", - "grok": "pip install xai-grok", - "dev": "pip install hanzo-dev", - "code": "pip install hanzo-code", - } - hint = install_hints.get(name, f"Install the {name} CLI") - return f"Agent {name} not found.\n\nTo install: {hint}" - except Exception as e: - return f"Error running {name}: {e}" - - def register(self, mcp_server: FastMCP) -> None: - """Register with MCP server.""" - tool_instance = self - - @mcp_server.tool() - async def agent( - action: Action = "run", - name: Annotated[ - Optional[str], - Field( - description="Agent: claude, codex, gemini, grok, qwen, vibe, code, dev" - ), - ] = None, - prompt: Annotated[ - Optional[str], Field(description="Prompt for the agent") - ] = None, - cwd: Annotated[ - Optional[str], Field(description="Working directory") - ] = None, - args: Annotated[ - Optional[List[str]], Field(description="Additional arguments") - ] = None, - timeout: Annotated[int, Field(description="Timeout in seconds")] = 300, - share_config: Annotated[ - bool, Field(description="Share hanzo-mcp config with agent") - ] = True, - ctx: MCPContext = None, - ) -> str: - """Run AI agents: claude, codex, gemini, grok, qwen, vibe, code, dev. - - Lightweight agent spawning with shared MCP config. - Auto-detects Claude Code environment for seamless auth. - """ - return await tool_instance.call( - ctx, - action=action, - name=name, - prompt=prompt, - cwd=cwd, - args=args, - timeout=timeout, - share_config=share_config, - ) diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/unified_cli_tools.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/unified_cli_tools.py deleted file mode 100644 index 30b512fd4..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/unified_cli_tools.py +++ /dev/null @@ -1,264 +0,0 @@ -"""Unified CLI Tools - DRY implementation using base agent classes. - -This module provides the single, clean implementation of all CLI tools -following Python best practices and eliminating all duplication. -""" - -from __future__ import annotations - -import os -from typing import Any, Dict, List, Optional -from pathlib import Path - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context - -from hanzo_tools.core import auto_timeout - -from ..common.base import BaseTool -from ...core.base_agent import CLIAgent, AgentConfig -from ..common.permissions import PermissionManager -from ...core.model_registry import registry - - -class UnifiedCLITool(BaseTool, CLIAgent): - """Unified CLI tool that combines BaseTool and CLIAgent functionality. - - MRO: BaseTool first for proper method resolution order. - """ - - def __init__( - self, - name: str, - description: str, - cli_command: str, - default_model: str, - permission_manager: Optional[PermissionManager] = None, - ): - """Initialize unified CLI tool. - - Args: - name: Tool name - description: Tool description - cli_command: CLI command to execute - default_model: Default model to use - permission_manager: Permission manager for access control - """ - # Initialize CLIAgent with config - config = AgentConfig(model=default_model) - CLIAgent.__init__(self, config) - - # Store tool metadata - self._name = name - self._description = description - self._cli_command = cli_command - self.permission_manager = permission_manager - - @property - def name(self) -> str: - return self._name - - @property - def description(self) -> str: - return self._description - - @property - def cli_command(self) -> str: - return self._cli_command - - def build_command(self, prompt: str, **kwargs: Any) -> List[str]: - """Build the CLI command with model-specific formatting. - - Args: - prompt: The prompt - **kwargs: Additional parameters - - Returns: - Command arguments list - """ - command = [self.cli_command] - - # Get model config from registry - model_config = registry.get(self.config.model) - - # Handle different CLI tool formats - if self.cli_command == "claude": - if model_config: - command.extend(["--model", model_config.full_name]) - # Claude takes prompt via stdin - return command - - elif self.cli_command == "openai": - # OpenAI CLI format - command.extend(["api", "chat.completions.create"]) - if model_config: - command.extend(["-m", model_config.full_name]) - command.extend(["-g", "user", prompt]) - return command - - elif self.cli_command in ["gemini", "grok"]: - # Simple format: command --model MODEL prompt - if model_config: - command.extend(["--model", model_config.full_name]) - command.append(prompt) - return command - - elif self.cli_command == "openhands": - # OpenHands format - command.extend(["run", prompt]) - if model_config: - command.extend(["--model", model_config.full_name]) - if self.config.working_dir: - command.extend(["--workspace", str(self.config.working_dir)]) - return command - - elif self.cli_command == "hanzo": - # Hanzo dev format - command.append("dev") - if model_config: - command.extend(["--model", model_config.full_name]) - command.extend(["--prompt", prompt]) - return command - - elif self.cli_command == "cline": - # Cline format - command.append(prompt) - command.append("--no-interactive") - return command - - elif self.cli_command == "aider": - # Aider format - if model_config: - command.extend(["--model", model_config.full_name]) - command.extend(["--message", prompt]) - command.extend(["--yes", "--no-stream"]) - return command - - elif self.cli_command == "ollama": - # Ollama format for local models - command.extend(["run", self.config.model.replace("ollama/", "")]) - command.append(prompt) - return command - - # Default format - command.append(prompt) - return command - - @auto_timeout("unified_cli_tools") - async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str: - """Execute the CLI tool via MCP interface. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Execution result - """ - # Update config from params - if params.get("model"): - self.config.model = registry.resolve(params["model"]) - if params.get("working_dir"): - self.config.working_dir = Path(params["working_dir"]) - if params.get("timeout"): - self.config.timeout = params["timeout"] - - # Execute using base agent - result = await self.execute( - params.get("prompt", ""), - context=ctx, - ) - - return result.content - - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server. - - Args: - mcp_server: The FastMCP server instance - """ - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def tool_wrapper( - prompt: str, - ctx: Context[Any, Any, Any], - model: Optional[str] = None, - working_dir: Optional[str] = None, - timeout: int = 300, - ) -> str: - return await tool_self.call( - ctx, - prompt=prompt, - model=model, - working_dir=working_dir, - timeout=timeout, - ) - - -def create_cli_tools( - permission_manager: Optional[PermissionManager] = None, -) -> Dict[str, UnifiedCLITool]: - """Create all CLI tools with unified implementation. - - Args: - permission_manager: Permission manager for access control - - Returns: - Dictionary of tool name to tool instance - """ - tools = {} - - # Define all tools with their configurations - tool_configs = [ - ("claude", "Execute Claude CLI for AI assistance", "claude", "claude"), - ("cc", "Claude Code CLI (alias for claude)", "claude", "claude"), - ("codex", "Execute OpenAI Codex/GPT-4 CLI", "openai", "gpt-4-turbo"), - ("gemini", "Execute Google Gemini CLI", "gemini", "gemini"), - ("grok", "Execute xAI Grok CLI", "grok", "grok"), - ("openhands", "Execute OpenHands for autonomous coding", "openhands", "claude"), - ("oh", "OpenHands CLI (alias)", "openhands", "claude"), - ("hanzo_dev", "Execute Hanzo Dev AI assistant", "hanzo", "claude"), - ("cline", "Execute Cline for autonomous coding", "cline", "claude"), - ("aider", "Execute Aider for AI pair programming", "aider", "gpt-4-turbo"), - ] - - for name, description, cli_command, default_model in tool_configs: - tools[name] = UnifiedCLITool( - name=name, - description=description, - cli_command=cli_command, - default_model=default_model, - permission_manager=permission_manager, - ) - - return tools - - -def register_cli_tools( - mcp_server: FastMCP, - permission_manager: Optional[PermissionManager] = None, -) -> List[BaseTool]: - """Register all CLI tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - permission_manager: Permission manager for access control - - Returns: - List of registered CLI tools - """ - tools = create_cli_tools(permission_manager) - - # Register each tool - for tool in tools.values(): - tool.register(mcp_server) - - return list(tools.values()) - - -__all__ = [ - "UnifiedCLITool", - "create_cli_tools", - "register_cli_tools", -] diff --git a/pkg/hanzo-tools-agent/hanzo_tools/agent/zen_tool.py b/pkg/hanzo-tools-agent/hanzo_tools/agent/zen_tool.py deleted file mode 100644 index 588a4438c..000000000 --- a/pkg/hanzo-tools-agent/hanzo_tools/agent/zen_tool.py +++ /dev/null @@ -1,646 +0,0 @@ -"""Zen guidance tool for creative problem solving using Hanzo principles.""" - -import random -from enum import Enum -from typing import List, override - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout - - -class HanzoPrinciple(Enum): - """Hanzo principles organized by category.""" - - # Empathy - AUTONOMY = ("Autonomy", "Trust fully; freedom fuels genius", "๐Ÿฆ…") - BALANCE = ("Balance", "Steady wins; burnout loses every time", "โš–๏ธ") - CUSTOMER_OBSESSION = ( - "Customer Obsession", - "Coach relentlessly; their victories yours", - "๐ŸŽ“", - ) - HUMILITY = ("Humility", "Quiet confidence; greatness emerges naturally", "๐Ÿง˜") - INTEGRITY = ("Integrity", "Principles never break; reputation never fades", "๐Ÿ›ก๏ธ") - SELFLESSNESS = ("Selflessness", "Elevate others; personal success follows", "๐Ÿค") - - # Science - CURIOSITY = ("Curiosity", "Question always; truth never ends", "๐ŸŒฑ") - EMPIRICISM = ("Empiricism", "Hypothesize, measure; reality defines truth", "๐Ÿ”ฌ") - PRECISION = ( - "Precision", - "Discipline in data; eliminate guesswork completely", - "๐ŸŽฏ", - ) - VALIDATION = ("Validation", "Test assumptions hard; illusions crumble fast", "โœ…") - OBJECTIVITY = ("Objectivity", "Ego out; results speak plainly", "๐ŸงŠ") - REPEATABILITY = ( - "Repeatability", - "Do it again; success repeats systematically", - "๐Ÿ”„", - ) - - # Design - ACCESSIBILITY = ( - "Accessibility", - "Open doors wide; adoption thrives naturally", - "๐ŸŒ", - ) - BEAUTY = ("Beauty", "Form speaks louder; aesthetics lift utility", "๐ŸŽจ") - CLARITY = ("Clarity", "Obvious is perfect; complexity hidden cleanly", "๐Ÿ”") - CONSISTENCY = ("Consistency", "Uniform patterns; predictable results always", "๐ŸŽฏ") - SIMPLICITY = ("Simplicity", "Cut ruthlessly; essential alone remains", "๐Ÿชถ") - FLOW = ("Flow", "Remove friction; natural motion prevails", "๐ŸŒŠ") - - # Engineering - BATTERIES_INCLUDED = ( - "Batteries Included", - "Ready instantly; everything you need to start", - "๐Ÿ”‹", - ) - CONCURRENCY = ("Concurrency", "Parallel flows; frictionless scale", "โšก") - COMPOSABLE = ("Composable", "Modular magic; pieces multiply power", "๐Ÿงฉ") - INTEROPERABLE = ( - "Interoperable", - "Integrate effortlessly; value compounds infinitely", - "๐Ÿ”—", - ) - ORTHOGONAL = ("Orthogonal", "Each tool exact; no overlap, no waste", "โš™๏ธ") - SCALABLE = ("Scalable", "Growth limitless; obstacles removed at inception", "๐Ÿ“ˆ") - - # Scale - DISRUPTION = ("Disruption", "Reinvent boldly; transcend competition entirely", "๐Ÿ’ฅ") - EXPERIMENTATION = ("Experimentation", "Test quickly; iterate endlessly", "๐Ÿงช") - EXPONENTIALITY = ("Exponentiality", "Compound constantly; incremental fades", "๐Ÿ“ˆ") - VELOCITY = ("Velocity", "Ship fast; refine faster", "๐Ÿš€") - URGENCY = ("Urgency", "Act now; delays destroy opportunity", "โฑ๏ธ") - - # Wisdom - ADAPTABILITY = ( - "Adaptability", - "Pivot sharply; fluid response accelerates evolution", - "๐ŸŒŠ", - ) - DECENTRALIZATION = ( - "Decentralization", - "Distribute power; resilience born from autonomy", - "๐Ÿ•ธ๏ธ", - ) - FREEDOM = ( - "Freedom", - "Democratize creativity; tools liberated, gatekeepers removed", - "๐Ÿ—ฝ", - ) - LONGEVITY = ( - "Longevity", - "Build timelessly; greatness endures beyond lifetimes", - "โณ", - ) - SECURITY = ("Security", "Encryption first; privacy non-negotiable", "๐Ÿ”") - ZEN = ("Zen", "Calm mastery; effortless excellence every moment", "โ˜ฏ๏ธ") - - -class Hexagram: - """64-path oracle hexagram with interpretation.""" - - HEXAGRAMS = { - "111111": ( - "ไนพ (Qiรกn)", - "Creative", - "Initiating force, pure yang energy. Time for bold action.", - ), - "000000": ( - "ๅค (Kลซn)", - "Receptive", - "Pure receptivity, yielding. Time to listen and adapt.", - ), - "100010": ( - "ๅฑฏ (Zhลซn)", - "Initial Difficulty", - "Growing pains. Persevere through early challenges.", - ), - "010001": ( - "่’™ (Mรฉng)", - "Youthful Folly", - "Beginner's mind. Learn humbly, question assumptions.", - ), - "111010": ( - "้œ€ (Xลซ)", - "Waiting", - "Strategic patience. Prepare while waiting for the right moment.", - ), - "010111": ( - "่จŸ (Sรฒng)", - "Conflict", - "Address conflicts directly but seek resolution, not victory.", - ), - "010000": ( - "ๅธซ (Shฤซ)", - "Army", - "Organize resources, build strong teams, lead by example.", - ), - "000010": ( - "ๆฏ” (Bว)", - "Holding Together", - "Unity and collaboration. Strengthen bonds.", - ), - "111011": ( - "ๅฐ็•œ (XiวŽo Chรน)", - "Small Accumulation", - "Small consistent improvements compound over time.", - ), - "110111": ( - "ๅฑฅ (Lวš)", - "Treading", - "Careful progress. Mind the details while moving forward.", - ), - "111000": ( - "ๆณฐ (Tร i)", - "Peace", - "Harmony achieved. Maintain balance while building.", - ), - "000111": ( - "ๅฆ (Pว)", - "Standstill", - "Blockage present. Pause, reassess, find new paths.", - ), - "101111": ( - "ๅŒไบบ (Tรณng Rรฉn)", - "Fellowship", - "Community strength. Build alliances and share knowledge.", - ), - "111101": ( - "ๅคงๆœ‰ (Dร  Yว’u)", - "Great Possession", - "Abundance available. Share generously to multiply value.", - ), - "001000": ( - "่ฌ™ (Qiฤn)", - "Modesty", - "Humble confidence. Let work speak for itself.", - ), - "000100": ( - "่ฑซ (Yรน)", - "Enthusiasm", - "Infectious energy. Channel excitement into action.", - ), - "100110": ( - "้šจ (Suรญ)", - "Following", - "Adaptive leadership. Know when to lead and when to follow.", - ), - "011001": ( - "่ ฑ (Gว”)", - "Work on Decay", - "Fix technical debt. Address root causes.", - ), - "110000": ( - "่‡จ (Lรญn)", - "Approach", - "Opportunity approaching. Prepare to receive it.", - ), - "000011": ( - "่ง€ (Guฤn)", - "Contemplation", - "Step back for perspective. See the whole system.", - ), - "100101": ( - "ๅ™ฌๅ—‘ (Shรฌ Kรจ)", - "Biting Through", - "Remove obstacles decisively. Clear blockages.", - ), - "101001": ("่ณ (Bรฌ)", "Grace", "Polish and refine. Beauty enhances function."), - "000001": ( - "ๅ‰ (Bล)", - "Splitting Apart", - "Decay phase. Let go of what's not working.", - ), - "100000": ( - "ๅพฉ (Fรน)", - "Return", - "New cycle begins. Start fresh with lessons learned.", - ), - "100111": ( - "็„กๅฆ„ (Wรบ Wร ng)", - "Innocence", - "Act with pure intention. Avoid overthinking.", - ), - "111001": ( - "ๅคง็•œ (Dร  Chรน)", - "Great Accumulation", - "Build reserves. Invest in infrastructure.", - ), - "100001": ( - "้ ค (Yรญ)", - "Nourishment", - "Feed growth. Provide resources teams need.", - ), - "011110": ( - "ๅคง้Ž (Dร  Guรฒ)", - "Great Excess", - "Extraordinary measures needed. Bold action required.", - ), - "010010": ( - "ๅŽ (KวŽn)", - "Abysmal", - "Navigate danger carefully. Trust your training.", - ), - "101101": ( - "้›ข (Lรญ)", - "Clinging Fire", - "Clarity and vision. Illuminate the path forward.", - ), - "001110": ( - "ๅ’ธ (Xiรกn)", - "Influence", - "Mutual attraction. Build on natural affinities.", - ), - "011100": ( - "ๆ† (Hรฉng)", - "Duration", - "Persistence pays. Maintain steady effort.", - ), - "001111": ("้ฏ (Dรนn)", "Retreat", "Strategic withdrawal. Regroup and refocus."), - "111100": ( - "ๅคงๅฃฏ (Dร  Zhuร ng)", - "Great Power", - "Strength available. Use power responsibly.", - ), - "000101": ( - "ๆ™‰ (Jรฌn)", - "Progress", - "Advance steadily. Each step builds momentum.", - ), - "101000": ( - "ๆ˜Žๅคท (Mรญng Yรญ)", - "Darkening Light", - "Work quietly. Keep brilliance hidden for now.", - ), - "101011": ( - "ๅฎถไบบ (Jiฤ Rรฉn)", - "Family", - "Team harmony. Strengthen internal culture.", - ), - "110101": ( - "็ฝ (Kuรญ)", - "Opposition", - "Creative tension. Find synthesis in differences.", - ), - "001010": ( - "่น‡ (JiวŽn)", - "Obstruction", - "Difficulty ahead. Find alternative routes.", - ), - "010100": ( - "่งฃ (Xiรจ)", - "Deliverance", - "Breakthrough achieved. Consolidate gains.", - ), - "110001": ("ๆ (Sว”n)", "Decrease", "Simplify ruthlessly. Less is more."), - "100011": ("็›Š (Yรฌ)", "Increase", "Multiply value. Invest in growth."), - "111110": ( - "ๅคฌ (Guร i)", - "Breakthrough", - "Decisive moment. Act with conviction.", - ), - "011111": ( - "ๅงค (Gรฒu)", - "Coming to Meet", - "Unexpected encounter. Stay alert to opportunity.", - ), - "000110": ( - "่ƒ (Cuรฌ)", - "Gathering", - "Convergence point. Bring elements together.", - ), - "011000": ( - "ๅ‡ (Shฤ“ng)", - "Pushing Upward", - "Gradual ascent. Build systematically.", - ), - "010110": ("ๅ›ฐ (Kรนn)", "Exhaustion", "Resources depleted. Rest and recharge."), - "011010": ("ไบ• (Jวng)", "The Well", "Deep resources. Draw from fundamentals."), - "101110": ( - "้ฉ (Gรฉ)", - "Revolution", - "Transform completely. Embrace radical change.", - ), - "011101": ( - "้ผŽ (Dวng)", - "The Cauldron", - "Transformation vessel. Cook new solutions.", - ), - "100100": ( - "้œ‡ (Zhรจn)", - "Thunder", - "Shocking awakening. Respond to wake-up calls.", - ), - "001001": ( - "่‰ฎ (Gรจn)", - "Mountain", - "Stillness and stability. Find solid ground.", - ), - "001011": ( - "ๆผธ (Jiร n)", - "Gradual Progress", - "Step by step. Patient development.", - ), - "110100": ( - "ๆญธๅฆน (Guฤซ Mรจi)", - "Marrying Maiden", - "New partnerships. Align expectations.", - ), - "101100": ("่ฑ (Fฤ“ng)", "Abundance", "Peak achievement. Prepare for cycles."), - "001101": ("ๆ—… (Lวš)", "The Wanderer", "Explorer mindset. Learn from journey."), - "011011": ( - "ๅทฝ (Xรนn)", - "Gentle Wind", - "Subtle influence. Persistent gentle pressure.", - ), - "110110": ("ๅ…Œ (Duรฌ)", "Joy", "Infectious happiness. Celebrate progress."), - "010011": ("ๆธ™ (Huร n)", "Dispersion", "Break up rigidity. Dissolve barriers."), - "110010": ( - "็ฏ€ (Jiรฉ)", - "Limitation", - "Healthy constraints. Focus through limits.", - ), - "110011": ( - "ไธญๅญš (Zhลng Fรบ)", - "Inner Truth", - "Authentic core. Build from truth.", - ), - "001100": ( - "ๅฐ้Ž (XiวŽo Guรฒ)", - "Small Excess", - "Minor adjustments. Fine-tune carefully.", - ), - "101010": ( - "ๆ—ขๆฟŸ (Jรฌ Jรฌ)", - "After Completion", - "Success achieved. Maintain vigilance.", - ), - "010101": ( - "ๆœชๆฟŸ (Wรจi Jรฌ)", - "Before Completion", - "Almost there. Final push needed.", - ), - } - - def __init__(self, lines: str): - self.lines = lines - self.name, self.title, self.meaning = self.HEXAGRAMS.get( - lines, - ("Unknown", "Mystery", "The pattern is unclear. Trust your intuition."), - ) - - def get_changing_lines(self) -> List[int]: - """Identify which lines are changing (would be 6 or 9 in traditional I Ching).""" - # For simplicity, randomly select 0-2 changing lines - num_changes = random.choice([0, 1, 1, 2]) - if num_changes == 0: - return [] - positions = list(range(6)) - return sorted(random.sample(positions, num_changes)) - - -class IChing: - """Zen oracle for engineering guidance.""" - - def __init__(self): - self.principles = list(HanzoPrinciple) - - def cast_hexagram(self) -> Hexagram: - """Cast a hexagram using virtual coins.""" - lines = "" - for _ in range(6): - # Three coin tosses: heads=3, tails=2 - coins = sum(random.choice([2, 3]) for _ in range(3)) - # 6=old yin(changing 0), 7=young yang(1), 8=young yin(0), 9=old yang(changing 1) - if coins in [6, 8]: - lines += "0" - else: - lines += "1" - return Hexagram(lines) - - def select_principles( - self, hexagram: Hexagram, challenge: str - ) -> List[HanzoPrinciple]: - """Select relevant Hanzo principles based on hexagram and challenge.""" - # Use hexagram pattern to deterministically but creatively select principles - selected = [] - - # Primary principle based on hexagram pattern - primary_index = sum( - int(bit) * (2**i) for i, bit in enumerate(hexagram.lines) - ) % len(self.principles) - selected.append(self.principles[primary_index]) - - # Supporting principles based on challenge keywords - keywords = challenge.lower().split() - keyword_matches = { - "scale": [HanzoPrinciple.SCALABLE, HanzoPrinciple.EXPONENTIALITY], - "speed": [HanzoPrinciple.VELOCITY, HanzoPrinciple.URGENCY], - "quality": [HanzoPrinciple.PRECISION, HanzoPrinciple.VALIDATION], - "team": [HanzoPrinciple.AUTONOMY, HanzoPrinciple.BALANCE], - "design": [HanzoPrinciple.SIMPLICITY, HanzoPrinciple.BEAUTY], - "bug": [HanzoPrinciple.EMPIRICISM, HanzoPrinciple.OBJECTIVITY], - "refactor": [HanzoPrinciple.CLARITY, HanzoPrinciple.COMPOSABLE], - "security": [HanzoPrinciple.SECURITY, HanzoPrinciple.INTEGRITY], - "performance": [HanzoPrinciple.CONCURRENCY, HanzoPrinciple.ORTHOGONAL], - "user": [HanzoPrinciple.CUSTOMER_OBSESSION, HanzoPrinciple.ACCESSIBILITY], - } - - for keyword, principles in keyword_matches.items(): - if keyword in keywords: - selected.extend(principles) - - # Add complementary principle based on changing lines - changing_lines = hexagram.get_changing_lines() - if changing_lines: - complement_index = (primary_index + sum(changing_lines)) % len( - self.principles - ) - selected.append(self.principles[complement_index]) - - # Ensure uniqueness and limit to 3-5 principles - seen = set() - unique_selected = [] - for principle in selected: - if principle not in seen: - seen.add(principle) - unique_selected.append(principle) - - return unique_selected[:5] - - def generate_guidance( - self, hexagram: Hexagram, principles: List[HanzoPrinciple], challenge: str - ) -> str: - """Generate creative guidance combining Hanzo Zen and engineering principles.""" - guidance = f"โ˜ฏ๏ธ ZEN GUIDANCE FOR ENGINEERING CHALLENGE โ˜ฏ๏ธ\n\n" - guidance += f"**Your Challenge:** {challenge}\n\n" - - guidance += f"**Hexagram Cast:** {hexagram.name} - {hexagram.title}\n" - guidance += f"**Pattern:** {''.join('โ”โ”โ”' if l == '1' else 'โ” โ”' for l in hexagram.lines[::-1])}\n" - guidance += f"**Ancient Wisdom:** {hexagram.meaning}\n\n" - - guidance += "**Hanzo Principles to Apply:**\n\n" - - for principle in principles: - name, wisdom, emoji = principle.value - guidance += f"{emoji} **{name}**\n" - guidance += f" *{wisdom}*\n\n" - - # Generate specific actionable advice - guidance += "**Synthesized Approach:**\n\n" - - # Hexagram-specific guidance - if "Creative" in hexagram.title: - guidance += "โ€ข This is a time for bold innovation. Don't hold back on ambitious ideas.\n" - elif "Receptive" in hexagram.title: - guidance += ( - "โ€ข Listen deeply to user needs and system constraints before acting.\n" - ) - elif "Difficulty" in hexagram.title: - guidance += ( - "โ€ข Challenges are teachers. Each obstacle reveals the path forward.\n" - ) - elif "Waiting" in hexagram.title: - guidance += "โ€ข Strategic patience required. Prepare thoroughly before implementation.\n" - elif "Conflict" in hexagram.title: - guidance += "โ€ข Technical disagreements? Seek data-driven resolution.\n" - elif "Peace" in hexagram.title: - guidance += ( - "โ€ข Harmony achieved. Now build sustainably on this foundation.\n" - ) - - # Principle-specific actionable advice - principle_actions = { - HanzoPrinciple.SCALABLE: "โ€ข Design for 10x growth from day one. Remove scaling bottlenecks now.", - HanzoPrinciple.VELOCITY: "โ€ข Ship an MVP today. Perfect is the enemy of shipped.", - HanzoPrinciple.SIMPLICITY: "โ€ข Delete half your code. The best code is no code.", - HanzoPrinciple.EMPIRICISM: "โ€ข Measure everything. Let data guide your decisions.", - HanzoPrinciple.CUSTOMER_OBSESSION: "โ€ข Talk to users now. Their pain is your roadmap.", - HanzoPrinciple.CONCURRENCY: "โ€ข Parallelize everything possible. Sequential is slow.", - HanzoPrinciple.SECURITY: "โ€ข Security is not optional. Encrypt by default.", - HanzoPrinciple.ZEN: "โ€ข Find calm in the chaos. Clear mind writes better code.", - } - - for principle in principles: - if principle in principle_actions: - guidance += principle_actions[principle] + "\n" - - # Changing lines wisdom - changing_lines = hexagram.get_changing_lines() - if changing_lines: - guidance += f"\n**Lines in Transition:** {', '.join(str(i + 1) for i in changing_lines)}\n" - guidance += ( - "โ€ข Change is imminent in these areas. Prepare for transformation.\n" - ) - - # Final synthesis - guidance += "\n**The Way Forward:**\n" - guidance += self._synthesize_action_plan(hexagram, principles, challenge) - - guidance += "\n\n*Remember: Zen guidance reveals patterns, not prescriptions. " - guidance += "Let this wisdom guide your intuition as you craft your solution.*" - - return guidance - - def _synthesize_action_plan( - self, hexagram: Hexagram, principles: List[HanzoPrinciple], challenge: str - ) -> str: - """Create a specific action plan based on the reading.""" - plan = "" - - # Determine the nature of the challenge - if any(word in challenge.lower() for word in ["bug", "error", "fix", "broken"]): - plan += "1. **Diagnose systematically** - Use empirical debugging, not guesswork\n" - plan += "2. **Fix root cause** - Address the source, not just symptoms\n" - plan += "3. **Prevent recurrence** - Add tests and monitoring\n" - elif any( - word in challenge.lower() for word in ["scale", "performance", "slow"] - ): - plan += "1. **Measure first** - Profile to find actual bottlenecks\n" - plan += "2. **Parallelize** - Use concurrency where possible\n" - plan += "3. **Simplify** - Remove complexity before optimizing\n" - elif any( - word in challenge.lower() for word in ["design", "architect", "structure"] - ): - plan += "1. **Start simple** - MVP first, elaborate later\n" - plan += "2. **Stay flexible** - Design for change\n" - plan += "3. **Think holistically** - Consider entire system\n" - elif any( - word in challenge.lower() for word in ["team", "collaborate", "people"] - ): - plan += "1. **Enable autonomy** - Trust your team\n" - plan += "2. **Maintain balance** - Sustainable pace wins\n" - plan += "3. **Share knowledge** - Elevate everyone\n" - else: - plan += "1. **Clarify intent** - What problem are you really solving?\n" - plan += "2. **Start small** - Build incrementally\n" - plan += "3. **Iterate rapidly** - Fast feedback loops\n" - - return plan - - -class ZenTool(BaseTool): - """Tool for applying Hanzo Zen guidance to engineering challenges.""" - - name = "zen" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Apply Hanzo Zen guidance to engineering challenges. - -This tool rolls a 64-path Zen oracle and selects relevant Hanzo principles -to provide creative, actionable guidance for your engineering challenge. - -Parameters: -- challenge: Description of the engineering challenge or question - -The oracle will: -1. Roll one of 64 Zen philosophies for the current situation -2. Select relevant Hanzo principles -3. Synthesize actionable guidance -4. Provide specific recommendations - -Example: -zen( - challenge="How should I approach refactoring this legacy codebase?" -) - -Use this when you need: -- Fresh perspective on a problem -- Creative approach to challenges -- Wisdom for difficult decisions -- Alignment with Hanzo principles""" - - def __init__(self): - """Initialize the Zen tool.""" - super().__init__() - self.oracle = IChing() - - @auto_timeout("zen") - async def call(self, ctx: MCPContext, challenge: str) -> str: - """Roll zen oracle and provide guidance.""" - # Cast hexagram - hexagram = self.oracle.cast_hexagram() - - # Select relevant principles - principles = self.oracle.select_principles(hexagram, challenge) - - # Generate guidance - guidance = self.oracle.generate_guidance(hexagram, principles, challenge) - - return guidance - - def register(self, server: FastMCP) -> None: - """Register the tool with the MCP server.""" - tool_self = self - - @server.tool(name=self.name, description=self.description) - async def zen(ctx: MCPContext, challenge: str) -> str: - return await tool_self.call(ctx, challenge) diff --git a/pkg/hanzo-tools-agent/pyproject.toml b/pkg/hanzo-tools-agent/pyproject.toml deleted file mode 100644 index 5f3a042b9..000000000 --- a/pkg/hanzo-tools-agent/pyproject.toml +++ /dev/null @@ -1,50 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-agent" -version = "0.3.3" -description = "Agent tools for Hanzo AI - multi-agent orchestration, CLI agents, swarms" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -keywords = ["hanzo", "tools", "agent", "swarm", "mcp", "ai"] -dependencies = [ - "hanzo-tools>=0.3.0", - "hanzo-tools-shell>=0.5.6", - "hanzo-async>=0.1.0", # Unified async I/O with uvloop - "mcp>=1.25.0", - "fastmcp>=2.14.1", - "pydantic>=2.12.5", -] - -[project.optional-dependencies] -consensus = ["hanzo-consensus>=0.1.0"] -api = ["httpx>=0.28.0"] # Direct API mode -full = [ - "openai>=1.0.0", - "anthropic>=0.40.0", - "hanzo-consensus>=0.1.0", - "httpx>=0.28.0", -] -dev = ["pytest>=7.0.0", "ruff>=0.14.0"] - -[project.urls] -"Homepage" = "https://github.com/hanzoai/python-sdk" - -[project.entry-points."hanzo.tools"] -agent = "hanzo_tools.agent:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] - -[tool.setuptools.package-data] -hanzo_tools = ["py.typed"] diff --git a/pkg/hanzo-tools-agent/tests/test_agent_tools.py b/pkg/hanzo-tools-agent/tests/test_agent_tools.py deleted file mode 100644 index 441691dd8..000000000 --- a/pkg/hanzo-tools-agent/tests/test_agent_tools.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Tests for hanzo-tools-agent.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import agent - - assert agent is not None - - def test_import_tools(self): - from hanzo_tools.agent import TOOLS - - assert len(TOOLS) > 0 - - def test_import_agent_tool(self): - from hanzo_tools.agent import AgentTool - - assert AgentTool.name == "agent" - - -class TestAgentTool: - """Tests for AgentTool.""" - - @pytest.fixture - def tool(self): - from hanzo_tools.agent import AgentTool - - return AgentTool() - - def test_has_description(self, tool): - assert tool.description - assert "agent" in tool.description.lower() - - def test_has_agent_configs(self, tool): - assert hasattr(tool, "agents") or hasattr(tool, "agent_configs") diff --git a/pkg/hanzo-tools-agent/uv.lock b/pkg/hanzo-tools-agent/uv.lock deleted file mode 100644 index 851f47a2c..000000000 --- a/pkg/hanzo-tools-agent/uv.lock +++ /dev/null @@ -1,1881 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" - -[[package]] -name = "aiofiles" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/c3/534eac40372d8ee36ef40df62ec129bee4fdb5ad9706e58a29be53b2c970/aiofiles-25.1.0.tar.gz", hash = "sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2", size = 46354, upload-time = "2025-10-09T20:51:04.358Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/8a/340a1555ae33d7354dbca4faa54948d76d89a27ceef032c8c3bc661d003e/aiofiles-25.1.0-py3-none-any.whl", hash = "sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695", size = 14668, upload-time = "2025-10-09T20:51:03.174Z" }, -] - -[[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 = "anthropic" -version = "0.75.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "docstring-parser" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/04/1f/08e95f4b7e2d35205ae5dcbb4ae97e7d477fc521c275c02609e2931ece2d/anthropic-0.75.0.tar.gz", hash = "sha256:e8607422f4ab616db2ea5baacc215dd5f028da99ce2f022e33c7c535b29f3dfb", size = 439565, upload-time = "2025-11-24T20:41:45.28Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/1c/1cd02b7ae64302a6e06724bf80a96401d5313708651d277b1458504a1730/anthropic-0.75.0-py3-none-any.whl", hash = "sha256:ea8317271b6c15d80225a9f3c670152746e88805a7a61e14d4a374577164965b", size = 388164, upload-time = "2025-11-24T20:41:43.587Z" }, -] - -[[package]] -name = "anyio" -version = "4.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/16/ce/8a777047513153587e5434fd752e89334ac33e379aa3497db860eeb60377/anyio-4.12.0.tar.gz", hash = "sha256:73c693b567b0c55130c104d0b43a9baf3aa6a31fc6110116509f27bf75e21ec0", size = 228266, upload-time = "2025-11-28T23:37:38.911Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/9c/36c5c37947ebfb8c7f22e0eb6e4d188ee2d53aa3880f3f2744fb894f0cb1/anyio-4.12.0-py3-none-any.whl", hash = "sha256:dad2376a628f98eeca4881fc56cd06affd18f659b17a747d3ff0307ced94b1bb", size = 113362, upload-time = "2025-11-28T23:36:57.897Z" }, -] - -[[package]] -name = "attrs" -version = "25.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, -] - -[[package]] -name = "authlib" -version = "1.6.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/9b/b1661026ff24bc641b76b78c5222d614776b0c085bcfdac9bd15a1cb4b35/authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e", size = 164894, upload-time = "2025-12-12T08:01:41.464Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/51/321e821856452f7386c4e9df866f196720b1ad0c5ea1623ea7399969ae3b/authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd", size = 244005, upload-time = "2025-12-12T08:01:40.209Z" }, -] - -[[package]] -name = "beartype" -version = "0.22.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, -] - -[[package]] -name = "cachetools" -version = "6.2.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bc/1d/ede8680603f6016887c062a2cf4fc8fdba905866a3ab8831aa8aa651320c/cachetools-6.2.4.tar.gz", hash = "sha256:82c5c05585e70b6ba2d3ae09ea60b79548872185d2f24ae1f2709d37299fd607", size = 31731, upload-time = "2025-12-15T18:24:53.744Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/fc/1d7b80d0eb7b714984ce40efc78859c022cd930e402f599d8ca9e39c78a4/cachetools-6.2.4-py3-none-any.whl", hash = "sha256:69a7a52634fed8b8bf6e24a050fb60bff1c9bd8f6d24572b99c32d4e71e62a51", size = 11551, upload-time = "2025-12-15T18:24:52.332Z" }, -] - -[[package]] -name = "certifi" -version = "2025.11.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, - { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, - { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, - { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, - { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, - { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, - { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, - { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, - { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, -] - -[[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 = "cryptography" -version = "46.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" }, - { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" }, - { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" }, - { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" }, - { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" }, - { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" }, - { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" }, - { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" }, - { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" }, - { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" }, - { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" }, - { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" }, - { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" }, - { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" }, - { url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012, upload-time = "2025-10-15T23:17:19.982Z" }, - { url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728, upload-time = "2025-10-15T23:17:21.527Z" }, - { url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078, upload-time = "2025-10-15T23:17:23.042Z" }, - { url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460, upload-time = "2025-10-15T23:17:24.885Z" }, - { url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237, upload-time = "2025-10-15T23:17:26.449Z" }, - { url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344, upload-time = "2025-10-15T23:17:28.06Z" }, - { url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564, upload-time = "2025-10-15T23:17:29.665Z" }, - { url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415, upload-time = "2025-10-15T23:17:31.686Z" }, - { url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457, upload-time = "2025-10-15T23:17:33.478Z" }, - { url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074, upload-time = "2025-10-15T23:17:35.158Z" }, - { url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569, upload-time = "2025-10-15T23:17:37.188Z" }, - { url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941, upload-time = "2025-10-15T23:17:39.236Z" }, - { url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339, upload-time = "2025-10-15T23:17:40.888Z" }, - { url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315, upload-time = "2025-10-15T23:17:42.769Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331, upload-time = "2025-10-15T23:17:44.468Z" }, - { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" }, - { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" }, - { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" }, - { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" }, - { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" }, - { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" }, - { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" }, - { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" }, - { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" }, - { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" }, - { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" }, - { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" }, - { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" }, - { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" }, -] - -[[package]] -name = "cyclopts" -version = "4.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "docstring-parser" }, - { name = "rich" }, - { name = "rich-rst" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/40/99/e1b75193ee23bd10a05a3b90c065d419b1c8c18f61cae6b8218c7158f792/cyclopts-4.4.1.tar.gz", hash = "sha256:368a404926b46a49dc328a33ccd7e55ba879296a28e64a42afe2f6667704cecf", size = 159245, upload-time = "2025-12-21T13:59:02.266Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/05/8efadba80e1296526e69c1dceba8b0f0bc3756e8d69f6ed9b0e647cf3169/cyclopts-4.4.1-py3-none-any.whl", hash = "sha256:67500e9fde90f335fddbf9c452d2e7c4f58209dffe52e7abb1e272796a963bde", size = 196726, upload-time = "2025-12-21T13:59:03.127Z" }, -] - -[[package]] -name = "diskcache" -version = "5.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, -] - -[[package]] -name = "distro" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, -] - -[[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.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, -] - -[[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.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "fakeredis" -version = "2.33.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "redis" }, - { name = "sortedcontainers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/f9/57464119936414d60697fcbd32f38909bb5688b616ae13de6e98384433e0/fakeredis-2.33.0.tar.gz", hash = "sha256:d7bc9a69d21df108a6451bbffee23b3eba432c21a654afc7ff2d295428ec5770", size = 175187, upload-time = "2025-12-16T19:45:52.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/78/a850fed8aeef96d4a99043c90b818b2ed5419cd5b24a4049fd7cfb9f1471/fakeredis-2.33.0-py3-none-any.whl", hash = "sha256:de535f3f9ccde1c56672ab2fdd6a8efbc4f2619fc2f1acc87b8737177d71c965", size = 119605, upload-time = "2025-12-16T19:45:51.08Z" }, -] - -[package.optional-dependencies] -lua = [ - { name = "lupa" }, -] - -[[package]] -name = "fastmcp" -version = "2.14.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "authlib" }, - { name = "cyclopts" }, - { name = "exceptiongroup" }, - { name = "httpx" }, - { name = "jsonschema-path" }, - { name = "mcp" }, - { name = "openapi-pydantic" }, - { name = "platformdirs" }, - { name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] }, - { name = "pydantic", extra = ["email"] }, - { name = "pydocket" }, - { name = "pyperclip" }, - { name = "python-dotenv" }, - { name = "rich" }, - { name = "uvicorn" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9e/50/d38e4371bdc34e709f4731b1e882cb7bc50e51c1a224859d4cd381b3a79b/fastmcp-2.14.1.tar.gz", hash = "sha256:132725cbf77b68fa3c3d165eff0cfa47e40c1479457419e6a2cfda65bd84c8d6", size = 8263331, upload-time = "2025-12-15T02:26:27.102Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/82/72401d09dc27c27fdf72ad6c2fe331e553e3c3646e01b5ff16473191033d/fastmcp-2.14.1-py3-none-any.whl", hash = "sha256:fb3e365cc1d52573ab89caeba9944dd4b056149097be169bce428e011f0a57e5", size = 412176, upload-time = "2025-12-15T02:26:25.356Z" }, -] - -[[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 = "hanzo-consensus" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/22/1a/0dddd14f023b5d0609bc326675c5c2be1696e2ebde0c89915493d6761af0/hanzo_consensus-0.1.0.tar.gz", hash = "sha256:8193816d297b2bfa85f6c752b325ac7b38dcc0ec6439a0421777a9b92ca8f740", size = 5931, upload-time = "2025-12-27T19:33:25.796Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/d7/215e4e1df9668ba27c0c64524634a63c58b6c884059d7d7fad55c64d5c8a/hanzo_consensus-0.1.0-py3-none-any.whl", hash = "sha256:023dc9939a1d561bfdd89eb02267d23a4f0810227936b6c6721cb890b5a12c9c", size = 7593, upload-time = "2025-12-27T19:33:24.179Z" }, -] - -[[package]] -name = "hanzo-tools" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/3e/2d94dc54e202bdb11f6e4597dd68eebc554d2b92fffb4f6918cdf3f91fe2/hanzo_tools-0.3.0.tar.gz", hash = "sha256:d00cb3212a707e22f9bb5a21f0f9eb34a74f22ff2b5f24e2f8b6321f9880e2fb", size = 10929, upload-time = "2025-12-27T18:56:38.843Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/07/6ebbcf371aafa5f2d171de2916ef92c73978b927b34a8863af53e1b1a80b/hanzo_tools-0.3.0-py3-none-any.whl", hash = "sha256:c7b0f6f7c3089f06329bc1aaca39fbce4b7108fdbd048e2bbc450a3aff9941f2", size = 11928, upload-time = "2025-12-27T18:56:37.528Z" }, -] - -[[package]] -name = "hanzo-tools-agent" -version = "0.2.4" -source = { editable = "." } -dependencies = [ - { name = "aiofiles" }, - { name = "fastmcp" }, - { name = "hanzo-tools" }, - { name = "hanzo-tools-shell" }, - { name = "mcp" }, - { name = "pydantic" }, -] - -[package.optional-dependencies] -consensus = [ - { name = "hanzo-consensus" }, -] -dev = [ - { name = "pytest" }, - { name = "ruff" }, -] -full = [ - { name = "anthropic" }, - { name = "hanzo-consensus" }, - { name = "openai" }, -] - -[package.metadata] -requires-dist = [ - { name = "aiofiles", specifier = ">=24.0.0" }, - { name = "anthropic", marker = "extra == 'full'", specifier = ">=0.40.0" }, - { name = "fastmcp", specifier = ">=2.14.1" }, - { name = "hanzo-consensus", marker = "extra == 'consensus'", specifier = ">=0.1.0" }, - { name = "hanzo-consensus", marker = "extra == 'full'", specifier = ">=0.1.0" }, - { name = "hanzo-tools", specifier = ">=0.3.0" }, - { name = "hanzo-tools-shell", specifier = ">=0.2.0" }, - { name = "mcp", specifier = ">=1.25.0" }, - { name = "openai", marker = "extra == 'full'", specifier = ">=1.0.0" }, - { name = "pydantic", specifier = ">=2.12.5" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.14.0" }, -] -provides-extras = ["consensus", "full", "dev"] - -[[package]] -name = "hanzo-tools-core" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a9/67/eabf2d355819b9c948a9898ed537fa014a5de0422de66e0ea4203faae6be/hanzo_tools_core-0.2.0.tar.gz", hash = "sha256:ab5352056d3db1d42aadd94d81635eb835476194562b07b497f327f6d334c058", size = 11441, upload-time = "2025-12-26T14:46:12.281Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/16/c1a705434afc5936156eadedcc29e1200fb4871e7a1b83d5efeebefbfcda/hanzo_tools_core-0.2.0-py3-none-any.whl", hash = "sha256:e07cdc3692003e30a40082be3750a0670b2adf612e3e7a56dd741d3b532b8090", size = 11026, upload-time = "2025-12-26T14:46:11.514Z" }, -] - -[[package]] -name = "hanzo-tools-shell" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiofiles" }, - { name = "hanzo-tools-core" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "tiktoken" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/01/92922bf2db084f21eedd380017120926510ecbf06907452ae6ac5f8ad3a3/hanzo_tools_shell-0.2.0.tar.gz", hash = "sha256:d287843e1dab426542c0ea076bc43111cdb8b24b2a89cb35176e817253438027", size = 15734, upload-time = "2025-12-26T14:46:43.18Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/31/21ec1accc5968a144ac65b6c6fa8ee0ae0c3f5367856631ff72c9eb72770/hanzo_tools_shell-0.2.0-py3-none-any.whl", hash = "sha256:fe76fe90b30da9bb1d6ce275bca7609a43e191f2993c250005df3ba0e7e91e84", size = 20739, upload-time = "2025-12-26T14:46:36.519Z" }, -] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, -] - -[[package]] -name = "jaraco-context" -version = "6.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8d/7d/41acf8e22d791bde812cb6c2c36128bb932ed8ae066bcb5e39cb198e8253/jaraco_context-6.0.2.tar.gz", hash = "sha256:953ae8dddb57b1d791bf72ea1009b32088840a7dd19b9ba16443f62be919ee57", size = 14994, upload-time = "2025-12-24T19:21:35.784Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/0c/1e0096ced9c55f9c6c6655446798df74165780375d3f5ab5f33751e087ae/jaraco_context-6.0.2-py3-none-any.whl", hash = "sha256:55fc21af4b4f9ca94aa643b6ee7fe13b1e4c01abf3aeb98ca4ad9c80b741c786", size = 6988, upload-time = "2025-12-24T19:21:34.557Z" }, -] - -[[package]] -name = "jaraco-functools" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, -] - -[[package]] -name = "jiter" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294, upload-time = "2025-11-09T20:49:23.302Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/c9/5b9f7b4983f1b542c64e84165075335e8a236fa9e2ea03a0c79780062be8/jiter-0.12.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:305e061fa82f4680607a775b2e8e0bcb071cd2205ac38e6ef48c8dd5ebe1cf37", size = 314449, upload-time = "2025-11-09T20:47:22.999Z" }, - { url = "https://files.pythonhosted.org/packages/98/6e/e8efa0e78de00db0aee82c0cf9e8b3f2027efd7f8a71f859d8f4be8e98ef/jiter-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c1860627048e302a528333c9307c818c547f214d8659b0705d2195e1a94b274", size = 319855, upload-time = "2025-11-09T20:47:24.779Z" }, - { url = "https://files.pythonhosted.org/packages/20/26/894cd88e60b5d58af53bec5c6759d1292bd0b37a8b5f60f07abf7a63ae5f/jiter-0.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df37577a4f8408f7e0ec3205d2a8f87672af8f17008358063a4d6425b6081ce3", size = 350171, upload-time = "2025-11-09T20:47:26.469Z" }, - { url = "https://files.pythonhosted.org/packages/f5/27/a7b818b9979ac31b3763d25f3653ec3a954044d5e9f5d87f2f247d679fd1/jiter-0.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75fdd787356c1c13a4f40b43c2156276ef7a71eb487d98472476476d803fb2cf", size = 365590, upload-time = "2025-11-09T20:47:27.918Z" }, - { url = "https://files.pythonhosted.org/packages/ba/7e/e46195801a97673a83746170b17984aa8ac4a455746354516d02ca5541b4/jiter-0.12.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1eb5db8d9c65b112aacf14fcd0faae9913d07a8afea5ed06ccdd12b724e966a1", size = 479462, upload-time = "2025-11-09T20:47:29.654Z" }, - { url = "https://files.pythonhosted.org/packages/ca/75/f833bfb009ab4bd11b1c9406d333e3b4357709ed0570bb48c7c06d78c7dd/jiter-0.12.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73c568cc27c473f82480abc15d1301adf333a7ea4f2e813d6a2c7d8b6ba8d0df", size = 378983, upload-time = "2025-11-09T20:47:31.026Z" }, - { url = "https://files.pythonhosted.org/packages/71/b3/7a69d77943cc837d30165643db753471aff5df39692d598da880a6e51c24/jiter-0.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4321e8a3d868919bcb1abb1db550d41f2b5b326f72df29e53b2df8b006eb9403", size = 361328, upload-time = "2025-11-09T20:47:33.286Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ac/a78f90caf48d65ba70d8c6efc6f23150bc39dc3389d65bbec2a95c7bc628/jiter-0.12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0a51bad79f8cc9cac2b4b705039f814049142e0050f30d91695a2d9a6611f126", size = 386740, upload-time = "2025-11-09T20:47:34.703Z" }, - { url = "https://files.pythonhosted.org/packages/39/b6/5d31c2cc8e1b6a6bcf3c5721e4ca0a3633d1ab4754b09bc7084f6c4f5327/jiter-0.12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2a67b678f6a5f1dd6c36d642d7db83e456bc8b104788262aaefc11a22339f5a9", size = 520875, upload-time = "2025-11-09T20:47:36.058Z" }, - { url = "https://files.pythonhosted.org/packages/30/b5/4df540fae4e9f68c54b8dab004bd8c943a752f0b00efd6e7d64aa3850339/jiter-0.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efe1a211fe1fd14762adea941e3cfd6c611a136e28da6c39272dbb7a1bbe6a86", size = 511457, upload-time = "2025-11-09T20:47:37.932Z" }, - { url = "https://files.pythonhosted.org/packages/07/65/86b74010e450a1a77b2c1aabb91d4a91dd3cd5afce99f34d75fd1ac64b19/jiter-0.12.0-cp312-cp312-win32.whl", hash = "sha256:d779d97c834b4278276ec703dc3fc1735fca50af63eb7262f05bdb4e62203d44", size = 204546, upload-time = "2025-11-09T20:47:40.47Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c7/6659f537f9562d963488e3e55573498a442503ced01f7e169e96a6110383/jiter-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e8269062060212b373316fe69236096aaf4c49022d267c6736eebd66bbbc60bb", size = 205196, upload-time = "2025-11-09T20:47:41.794Z" }, - { url = "https://files.pythonhosted.org/packages/21/f4/935304f5169edadfec7f9c01eacbce4c90bb9a82035ac1de1f3bd2d40be6/jiter-0.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:06cb970936c65de926d648af0ed3d21857f026b1cf5525cb2947aa5e01e05789", size = 186100, upload-time = "2025-11-09T20:47:43.007Z" }, - { url = "https://files.pythonhosted.org/packages/3d/a6/97209693b177716e22576ee1161674d1d58029eb178e01866a0422b69224/jiter-0.12.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6cc49d5130a14b732e0612bc76ae8db3b49898732223ef8b7599aa8d9810683e", size = 313658, upload-time = "2025-11-09T20:47:44.424Z" }, - { url = "https://files.pythonhosted.org/packages/06/4d/125c5c1537c7d8ee73ad3d530a442d6c619714b95027143f1b61c0b4dfe0/jiter-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:37f27a32ce36364d2fa4f7fdc507279db604d27d239ea2e044c8f148410defe1", size = 318605, upload-time = "2025-11-09T20:47:45.973Z" }, - { url = "https://files.pythonhosted.org/packages/99/bf/a840b89847885064c41a5f52de6e312e91fa84a520848ee56c97e4fa0205/jiter-0.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbc0944aa3d4b4773e348cda635252824a78f4ba44328e042ef1ff3f6080d1cf", size = 349803, upload-time = "2025-11-09T20:47:47.535Z" }, - { url = "https://files.pythonhosted.org/packages/8a/88/e63441c28e0db50e305ae23e19c1d8fae012d78ed55365da392c1f34b09c/jiter-0.12.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:da25c62d4ee1ffbacb97fac6dfe4dcd6759ebdc9015991e92a6eae5816287f44", size = 365120, upload-time = "2025-11-09T20:47:49.284Z" }, - { url = "https://files.pythonhosted.org/packages/0a/7c/49b02714af4343970eb8aca63396bc1c82fa01197dbb1e9b0d274b550d4e/jiter-0.12.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:048485c654b838140b007390b8182ba9774621103bd4d77c9c3f6f117474ba45", size = 479918, upload-time = "2025-11-09T20:47:50.807Z" }, - { url = "https://files.pythonhosted.org/packages/69/ba/0a809817fdd5a1db80490b9150645f3aae16afad166960bcd562be194f3b/jiter-0.12.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:635e737fbb7315bef0037c19b88b799143d2d7d3507e61a76751025226b3ac87", size = 379008, upload-time = "2025-11-09T20:47:52.211Z" }, - { url = "https://files.pythonhosted.org/packages/5f/c3/c9fc0232e736c8877d9e6d83d6eeb0ba4e90c6c073835cc2e8f73fdeef51/jiter-0.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e017c417b1ebda911bd13b1e40612704b1f5420e30695112efdbed8a4b389ed", size = 361785, upload-time = "2025-11-09T20:47:53.512Z" }, - { url = "https://files.pythonhosted.org/packages/96/61/61f69b7e442e97ca6cd53086ddc1cf59fb830549bc72c0a293713a60c525/jiter-0.12.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:89b0bfb8b2bf2351fba36bb211ef8bfceba73ef58e7f0c68fb67b5a2795ca2f9", size = 386108, upload-time = "2025-11-09T20:47:54.893Z" }, - { url = "https://files.pythonhosted.org/packages/e9/2e/76bb3332f28550c8f1eba3bf6e5efe211efda0ddbbaf24976bc7078d42a5/jiter-0.12.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:f5aa5427a629a824a543672778c9ce0c5e556550d1569bb6ea28a85015287626", size = 519937, upload-time = "2025-11-09T20:47:56.253Z" }, - { url = "https://files.pythonhosted.org/packages/84/d6/fa96efa87dc8bff2094fb947f51f66368fa56d8d4fc9e77b25d7fbb23375/jiter-0.12.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed53b3d6acbcb0fd0b90f20c7cb3b24c357fe82a3518934d4edfa8c6898e498c", size = 510853, upload-time = "2025-11-09T20:47:58.32Z" }, - { url = "https://files.pythonhosted.org/packages/8a/28/93f67fdb4d5904a708119a6ab58a8f1ec226ff10a94a282e0215402a8462/jiter-0.12.0-cp313-cp313-win32.whl", hash = "sha256:4747de73d6b8c78f2e253a2787930f4fffc68da7fa319739f57437f95963c4de", size = 204699, upload-time = "2025-11-09T20:47:59.686Z" }, - { url = "https://files.pythonhosted.org/packages/c4/1f/30b0eb087045a0abe2a5c9c0c0c8da110875a1d3be83afd4a9a4e548be3c/jiter-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:e25012eb0c456fcc13354255d0338cd5397cce26c77b2832b3c4e2e255ea5d9a", size = 204258, upload-time = "2025-11-09T20:48:01.01Z" }, - { url = "https://files.pythonhosted.org/packages/2c/f4/2b4daf99b96bce6fc47971890b14b2a36aef88d7beb9f057fafa032c6141/jiter-0.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:c97b92c54fe6110138c872add030a1f99aea2401ddcdaa21edf74705a646dd60", size = 185503, upload-time = "2025-11-09T20:48:02.35Z" }, - { url = "https://files.pythonhosted.org/packages/39/ca/67bb15a7061d6fe20b9b2a2fd783e296a1e0f93468252c093481a2f00efa/jiter-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:53839b35a38f56b8be26a7851a48b89bc47e5d88e900929df10ed93b95fea3d6", size = 317965, upload-time = "2025-11-09T20:48:03.783Z" }, - { url = "https://files.pythonhosted.org/packages/18/af/1788031cd22e29c3b14bc6ca80b16a39a0b10e611367ffd480c06a259831/jiter-0.12.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94f669548e55c91ab47fef8bddd9c954dab1938644e715ea49d7e117015110a4", size = 345831, upload-time = "2025-11-09T20:48:05.55Z" }, - { url = "https://files.pythonhosted.org/packages/05/17/710bf8472d1dff0d3caf4ced6031060091c1320f84ee7d5dcbed1f352417/jiter-0.12.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:351d54f2b09a41600ffea43d081522d792e81dcfb915f6d2d242744c1cc48beb", size = 361272, upload-time = "2025-11-09T20:48:06.951Z" }, - { url = "https://files.pythonhosted.org/packages/fb/f1/1dcc4618b59761fef92d10bcbb0b038b5160be653b003651566a185f1a5c/jiter-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2a5e90604620f94bf62264e7c2c038704d38217b7465b863896c6d7c902b06c7", size = 204604, upload-time = "2025-11-09T20:48:08.328Z" }, - { url = "https://files.pythonhosted.org/packages/d9/32/63cb1d9f1c5c6632a783c0052cde9ef7ba82688f7065e2f0d5f10a7e3edb/jiter-0.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:88ef757017e78d2860f96250f9393b7b577b06a956ad102c29c8237554380db3", size = 185628, upload-time = "2025-11-09T20:48:09.572Z" }, - { url = "https://files.pythonhosted.org/packages/a8/99/45c9f0dbe4a1416b2b9a8a6d1236459540f43d7fb8883cff769a8db0612d/jiter-0.12.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c46d927acd09c67a9fb1416df45c5a04c27e83aae969267e98fba35b74e99525", size = 312478, upload-time = "2025-11-09T20:48:10.898Z" }, - { url = "https://files.pythonhosted.org/packages/4c/a7/54ae75613ba9e0f55fcb0bc5d1f807823b5167cc944e9333ff322e9f07dd/jiter-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:774ff60b27a84a85b27b88cd5583899c59940bcc126caca97eb2a9df6aa00c49", size = 318706, upload-time = "2025-11-09T20:48:12.266Z" }, - { url = "https://files.pythonhosted.org/packages/59/31/2aa241ad2c10774baf6c37f8b8e1f39c07db358f1329f4eb40eba179c2a2/jiter-0.12.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5433fab222fb072237df3f637d01b81f040a07dcac1cb4a5c75c7aa9ed0bef1", size = 351894, upload-time = "2025-11-09T20:48:13.673Z" }, - { url = "https://files.pythonhosted.org/packages/54/4f/0f2759522719133a9042781b18cc94e335b6d290f5e2d3e6899d6af933e3/jiter-0.12.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8c593c6e71c07866ec6bfb790e202a833eeec885022296aff6b9e0b92d6a70e", size = 365714, upload-time = "2025-11-09T20:48:15.083Z" }, - { url = "https://files.pythonhosted.org/packages/dc/6f/806b895f476582c62a2f52c453151edd8a0fde5411b0497baaa41018e878/jiter-0.12.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:90d32894d4c6877a87ae00c6b915b609406819dce8bc0d4e962e4de2784e567e", size = 478989, upload-time = "2025-11-09T20:48:16.706Z" }, - { url = "https://files.pythonhosted.org/packages/86/6c/012d894dc6e1033acd8db2b8346add33e413ec1c7c002598915278a37f79/jiter-0.12.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:798e46eed9eb10c3adbbacbd3bdb5ecd4cf7064e453d00dbef08802dae6937ff", size = 378615, upload-time = "2025-11-09T20:48:18.614Z" }, - { url = "https://files.pythonhosted.org/packages/87/30/d718d599f6700163e28e2c71c0bbaf6dace692e7df2592fd793ac9276717/jiter-0.12.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3f1368f0a6719ea80013a4eb90ba72e75d7ea67cfc7846db2ca504f3df0169a", size = 364745, upload-time = "2025-11-09T20:48:20.117Z" }, - { url = "https://files.pythonhosted.org/packages/8f/85/315b45ce4b6ddc7d7fceca24068543b02bdc8782942f4ee49d652e2cc89f/jiter-0.12.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65f04a9d0b4406f7e51279710b27484af411896246200e461d80d3ba0caa901a", size = 386502, upload-time = "2025-11-09T20:48:21.543Z" }, - { url = "https://files.pythonhosted.org/packages/74/0b/ce0434fb40c5b24b368fe81b17074d2840748b4952256bab451b72290a49/jiter-0.12.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:fd990541982a24281d12b67a335e44f117e4c6cbad3c3b75c7dea68bf4ce3a67", size = 519845, upload-time = "2025-11-09T20:48:22.964Z" }, - { url = "https://files.pythonhosted.org/packages/e8/a3/7a7a4488ba052767846b9c916d208b3ed114e3eb670ee984e4c565b9cf0d/jiter-0.12.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:b111b0e9152fa7df870ecaebb0bd30240d9f7fff1f2003bcb4ed0f519941820b", size = 510701, upload-time = "2025-11-09T20:48:24.483Z" }, - { url = "https://files.pythonhosted.org/packages/c3/16/052ffbf9d0467b70af24e30f91e0579e13ded0c17bb4a8eb2aed3cb60131/jiter-0.12.0-cp314-cp314-win32.whl", hash = "sha256:a78befb9cc0a45b5a5a0d537b06f8544c2ebb60d19d02c41ff15da28a9e22d42", size = 205029, upload-time = "2025-11-09T20:48:25.749Z" }, - { url = "https://files.pythonhosted.org/packages/e4/18/3cf1f3f0ccc789f76b9a754bdb7a6977e5d1d671ee97a9e14f7eb728d80e/jiter-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:e1fe01c082f6aafbe5c8faf0ff074f38dfb911d53f07ec333ca03f8f6226debf", size = 204960, upload-time = "2025-11-09T20:48:27.415Z" }, - { url = "https://files.pythonhosted.org/packages/02/68/736821e52ecfdeeb0f024b8ab01b5a229f6b9293bbdb444c27efade50b0f/jiter-0.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:d72f3b5a432a4c546ea4bedc84cce0c3404874f1d1676260b9c7f048a9855451", size = 185529, upload-time = "2025-11-09T20:48:29.125Z" }, - { url = "https://files.pythonhosted.org/packages/30/61/12ed8ee7a643cce29ac97c2281f9ce3956eb76b037e88d290f4ed0d41480/jiter-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e6ded41aeba3603f9728ed2b6196e4df875348ab97b28fc8afff115ed42ba7a7", size = 318974, upload-time = "2025-11-09T20:48:30.87Z" }, - { url = "https://files.pythonhosted.org/packages/2d/c6/f3041ede6d0ed5e0e79ff0de4c8f14f401bbf196f2ef3971cdbe5fd08d1d/jiter-0.12.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a947920902420a6ada6ad51892082521978e9dd44a802663b001436e4b771684", size = 345932, upload-time = "2025-11-09T20:48:32.658Z" }, - { url = "https://files.pythonhosted.org/packages/d5/5d/4d94835889edd01ad0e2dbfc05f7bdfaed46292e7b504a6ac7839aa00edb/jiter-0.12.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:add5e227e0554d3a52cf390a7635edaffdf4f8fce4fdbcef3cc2055bb396a30c", size = 367243, upload-time = "2025-11-09T20:48:34.093Z" }, - { url = "https://files.pythonhosted.org/packages/fd/76/0051b0ac2816253a99d27baf3dda198663aff882fa6ea7deeb94046da24e/jiter-0.12.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9b1cda8fcb736250d7e8711d4580ebf004a46771432be0ae4796944b5dfa5d", size = 479315, upload-time = "2025-11-09T20:48:35.507Z" }, - { url = "https://files.pythonhosted.org/packages/70/ae/83f793acd68e5cb24e483f44f482a1a15601848b9b6f199dacb970098f77/jiter-0.12.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:deeb12a2223fe0135c7ff1356a143d57f95bbf1f4a66584f1fc74df21d86b993", size = 380714, upload-time = "2025-11-09T20:48:40.014Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/4808a88338ad2c228b1126b93fcd8ba145e919e886fe910d578230dabe3b/jiter-0.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c596cc0f4cb574877550ce4ecd51f8037469146addd676d7c1a30ebe6391923f", size = 365168, upload-time = "2025-11-09T20:48:41.462Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d4/04619a9e8095b42aef436b5aeb4c0282b4ff1b27d1db1508df9f5dc82750/jiter-0.12.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ab4c823b216a4aeab3fdbf579c5843165756bd9ad87cc6b1c65919c4715f783", size = 387893, upload-time = "2025-11-09T20:48:42.921Z" }, - { url = "https://files.pythonhosted.org/packages/17/ea/d3c7e62e4546fdc39197fa4a4315a563a89b95b6d54c0d25373842a59cbe/jiter-0.12.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e427eee51149edf962203ff8db75a7514ab89be5cb623fb9cea1f20b54f1107b", size = 520828, upload-time = "2025-11-09T20:48:44.278Z" }, - { url = "https://files.pythonhosted.org/packages/cc/0b/c6d3562a03fd767e31cb119d9041ea7958c3c80cb3d753eafb19b3b18349/jiter-0.12.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:edb868841f84c111255ba5e80339d386d937ec1fdce419518ce1bd9370fac5b6", size = 511009, upload-time = "2025-11-09T20:48:45.726Z" }, - { url = "https://files.pythonhosted.org/packages/aa/51/2cb4468b3448a8385ebcd15059d325c9ce67df4e2758d133ab9442b19834/jiter-0.12.0-cp314-cp314t-win32.whl", hash = "sha256:8bbcfe2791dfdb7c5e48baf646d37a6a3dcb5a97a032017741dea9f817dca183", size = 205110, upload-time = "2025-11-09T20:48:47.033Z" }, - { url = "https://files.pythonhosted.org/packages/b2/c5/ae5ec83dec9c2d1af805fd5fe8f74ebded9c8670c5210ec7820ce0dbeb1e/jiter-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2fa940963bf02e1d8226027ef461e36af472dea85d36054ff835aeed944dd873", size = 205223, upload-time = "2025-11-09T20:48:49.076Z" }, - { url = "https://files.pythonhosted.org/packages/97/9a/3c5391907277f0e55195550cf3fa8e293ae9ee0c00fb402fec1e38c0c82f/jiter-0.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:506c9708dd29b27288f9f8f1140c3cb0e3d8ddb045956d7757b1fa0e0f39a473", size = 185564, upload-time = "2025-11-09T20:48:50.376Z" }, - { url = "https://files.pythonhosted.org/packages/cb/f5/12efb8ada5f5c9edc1d4555fe383c1fb2eac05ac5859258a72d61981d999/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:e8547883d7b96ef2e5fe22b88f8a4c8725a56e7f4abafff20fd5272d634c7ecb", size = 309974, upload-time = "2025-11-09T20:49:17.187Z" }, - { url = "https://files.pythonhosted.org/packages/85/15/d6eb3b770f6a0d332675141ab3962fd4a7c270ede3515d9f3583e1d28276/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:89163163c0934854a668ed783a2546a0617f71706a2551a4a0666d91ab365d6b", size = 304233, upload-time = "2025-11-09T20:49:18.734Z" }, - { url = "https://files.pythonhosted.org/packages/8c/3e/e7e06743294eea2cf02ced6aa0ff2ad237367394e37a0e2b4a1108c67a36/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d96b264ab7d34bbb2312dedc47ce07cd53f06835eacbc16dde3761f47c3a9e7f", size = 338537, upload-time = "2025-11-09T20:49:20.317Z" }, - { url = "https://files.pythonhosted.org/packages/2f/9c/6753e6522b8d0ef07d3a3d239426669e984fb0eba15a315cdbc1253904e4/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c", size = 346110, upload-time = "2025-11-09T20:49:21.817Z" }, -] - -[[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.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "keyring" -version = "25.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, -] - -[[package]] -name = "lupa" -version = "2.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/1c/191c3e6ec6502e3dbe25a53e27f69a5daeac3e56de1f73c0138224171ead/lupa-2.6.tar.gz", hash = "sha256:9a770a6e89576be3447668d7ced312cd6fd41d3c13c2462c9dc2c2ab570e45d9", size = 7240282, upload-time = "2025-10-24T07:20:29.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/86/ce243390535c39d53ea17ccf0240815e6e457e413e40428a658ea4ee4b8d/lupa-2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47ce718817ef1cc0c40d87c3d5ae56a800d61af00fbc0fad1ca9be12df2f3b56", size = 951707, upload-time = "2025-10-24T07:18:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/cedea5e6cbeb54396fdcc55f6b741696f3f036d23cfaf986d50d680446da/lupa-2.6-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:7aba985b15b101495aa4b07112cdc08baa0c545390d560ad5cfde2e9e34f4d58", size = 1916703, upload-time = "2025-10-24T07:18:05.6Z" }, - { url = "https://files.pythonhosted.org/packages/24/be/3d6b5f9a8588c01a4d88129284c726017b2089f3a3fd3ba8bd977292fea0/lupa-2.6-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:b766f62f95b2739f2248977d29b0722e589dcf4f0ccfa827ccbd29f0148bd2e5", size = 985152, upload-time = "2025-10-24T07:18:08.561Z" }, - { url = "https://files.pythonhosted.org/packages/eb/23/9f9a05beee5d5dce9deca4cb07c91c40a90541fc0a8e09db4ee670da550f/lupa-2.6-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:00a934c23331f94cb51760097ebfab14b005d55a6b30a2b480e3c53dd2fa290d", size = 1159599, upload-time = "2025-10-24T07:18:10.346Z" }, - { url = "https://files.pythonhosted.org/packages/40/4e/e7c0583083db9d7f1fd023800a9767d8e4391e8330d56c2373d890ac971b/lupa-2.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21de9f38bd475303e34a042b7081aabdf50bd9bafd36ce4faea2f90fd9f15c31", size = 1038686, upload-time = "2025-10-24T07:18:12.112Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/5a4f7d959d4feba5e203ff0c31889e74d1ca3153122be4a46dca7d92bf7c/lupa-2.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf3bda96d3fc41237e964a69c23647d50d4e28421111360274d4799832c560e9", size = 2071956, upload-time = "2025-10-24T07:18:14.572Z" }, - { url = "https://files.pythonhosted.org/packages/92/34/2f4f13ca65d01169b1720176aedc4af17bc19ee834598c7292db232cb6dc/lupa-2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a76ead245da54801a81053794aa3975f213221f6542d14ec4b859ee2e7e0323", size = 1057199, upload-time = "2025-10-24T07:18:16.379Z" }, - { url = "https://files.pythonhosted.org/packages/35/2a/5f7d2eebec6993b0dcd428e0184ad71afb06a45ba13e717f6501bfed1da3/lupa-2.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8dd0861741caa20886ddbda0a121d8e52fb9b5bb153d82fa9bba796962bf30e8", size = 1173693, upload-time = "2025-10-24T07:18:18.153Z" }, - { url = "https://files.pythonhosted.org/packages/e4/29/089b4d2f8e34417349af3904bb40bec40b65c8731f45e3fd8d497ca573e5/lupa-2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:239e63948b0b23023f81d9a19a395e768ed3da6a299f84e7963b8f813f6e3f9c", size = 2164394, upload-time = "2025-10-24T07:18:20.403Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1b/79c17b23c921f81468a111cad843b076a17ef4b684c4a8dff32a7969c3f0/lupa-2.6-cp312-cp312-win32.whl", hash = "sha256:325894e1099499e7a6f9c351147661a2011887603c71086d36fe0f964d52d1ce", size = 1420647, upload-time = "2025-10-24T07:18:23.368Z" }, - { url = "https://files.pythonhosted.org/packages/b8/15/5121e68aad3584e26e1425a5c9a79cd898f8a152292059e128c206ee817c/lupa-2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c735a1ce8ee60edb0fe71d665f1e6b7c55c6021f1d340eb8c865952c602cd36f", size = 1688529, upload-time = "2025-10-24T07:18:25.523Z" }, - { url = "https://files.pythonhosted.org/packages/28/1d/21176b682ca5469001199d8b95fa1737e29957a3d185186e7a8b55345f2e/lupa-2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:663a6e58a0f60e7d212017d6678639ac8df0119bc13c2145029dcba084391310", size = 947232, upload-time = "2025-10-24T07:18:27.878Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4c/d327befb684660ca13cf79cd1f1d604331808f9f1b6fb6bf57832f8edf80/lupa-2.6-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:d1f5afda5c20b1f3217a80e9bc1b77037f8a6eb11612fd3ada19065303c8f380", size = 1908625, upload-time = "2025-10-24T07:18:29.944Z" }, - { url = "https://files.pythonhosted.org/packages/66/8e/ad22b0a19454dfd08662237a84c792d6d420d36b061f239e084f29d1a4f3/lupa-2.6-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:26f2b3c085fe76e9119e48c1013c1cccdc1f51585d456858290475aa38e7089e", size = 981057, upload-time = "2025-10-24T07:18:31.553Z" }, - { url = "https://files.pythonhosted.org/packages/5c/48/74859073ab276bd0566c719f9ca0108b0cfc1956ca0d68678d117d47d155/lupa-2.6-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:60d2f902c7b96fb8ab98493dcff315e7bb4d0b44dc9dd76eb37de575025d5685", size = 1156227, upload-time = "2025-10-24T07:18:33.981Z" }, - { url = "https://files.pythonhosted.org/packages/09/6c/0e9ded061916877253c2266074060eb71ed99fb21d73c8c114a76725bce2/lupa-2.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a02d25dee3a3250967c36590128d9220ae02f2eda166a24279da0b481519cbff", size = 1035752, upload-time = "2025-10-24T07:18:36.32Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ef/f8c32e454ef9f3fe909f6c7d57a39f950996c37a3deb7b391fec7903dab7/lupa-2.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eae1ee16b886b8914ff292dbefbf2f48abfbdee94b33a88d1d5475e02423203", size = 2069009, upload-time = "2025-10-24T07:18:38.072Z" }, - { url = "https://files.pythonhosted.org/packages/53/dc/15b80c226a5225815a890ee1c11f07968e0aba7a852df41e8ae6fe285063/lupa-2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0edd5073a4ee74ab36f74fe61450148e6044f3952b8d21248581f3c5d1a58be", size = 1056301, upload-time = "2025-10-24T07:18:40.165Z" }, - { url = "https://files.pythonhosted.org/packages/31/14/2086c1425c985acfb30997a67e90c39457122df41324d3c179d6ee2292c6/lupa-2.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c53ee9f22a8a17e7d4266ad48e86f43771951797042dd51d1494aaa4f5f3f0a", size = 1170673, upload-time = "2025-10-24T07:18:42.426Z" }, - { url = "https://files.pythonhosted.org/packages/10/e5/b216c054cf86576c0191bf9a9f05de6f7e8e07164897d95eea0078dca9b2/lupa-2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de7c0f157a9064a400d828789191a96da7f4ce889969a588b87ec80de9b14772", size = 2162227, upload-time = "2025-10-24T07:18:46.112Z" }, - { url = "https://files.pythonhosted.org/packages/59/2f/33ecb5bedf4f3bc297ceacb7f016ff951331d352f58e7e791589609ea306/lupa-2.6-cp313-cp313-win32.whl", hash = "sha256:ee9523941ae0a87b5b703417720c5d78f72d2f5bc23883a2ea80a949a3ed9e75", size = 1419558, upload-time = "2025-10-24T07:18:48.371Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b4/55e885834c847ea610e111d87b9ed4768f0afdaeebc00cd46810f25029f6/lupa-2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b1335a5835b0a25ebdbc75cf0bda195e54d133e4d994877ef025e218c2e59db9", size = 1683424, upload-time = "2025-10-24T07:18:50.976Z" }, - { url = "https://files.pythonhosted.org/packages/66/9d/d9427394e54d22a35d1139ef12e845fd700d4872a67a34db32516170b746/lupa-2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dcb6d0a3264873e1653bc188499f48c1fb4b41a779e315eba45256cfe7bc33c1", size = 953818, upload-time = "2025-10-24T07:18:53.378Z" }, - { url = "https://files.pythonhosted.org/packages/10/41/27bbe81953fb2f9ecfced5d9c99f85b37964cfaf6aa8453bb11283983721/lupa-2.6-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:a37e01f2128f8c36106726cb9d360bac087d58c54b4522b033cc5691c584db18", size = 1915850, upload-time = "2025-10-24T07:18:55.259Z" }, - { url = "https://files.pythonhosted.org/packages/a3/98/f9ff60db84a75ba8725506bbf448fb085bc77868a021998ed2a66d920568/lupa-2.6-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:458bd7e9ff3c150b245b0fcfbb9bd2593d1152ea7f0a7b91c1d185846da033fe", size = 982344, upload-time = "2025-10-24T07:18:57.05Z" }, - { url = "https://files.pythonhosted.org/packages/41/f7/f39e0f1c055c3b887d86b404aaf0ca197b5edfd235a8b81b45b25bac7fc3/lupa-2.6-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:052ee82cac5206a02df77119c325339acbc09f5ce66967f66a2e12a0f3211cad", size = 1156543, upload-time = "2025-10-24T07:18:59.251Z" }, - { url = "https://files.pythonhosted.org/packages/9e/9c/59e6cffa0d672d662ae17bd7ac8ecd2c89c9449dee499e3eb13ca9cd10d9/lupa-2.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96594eca3c87dd07938009e95e591e43d554c1dbd0385be03c100367141db5a8", size = 1047974, upload-time = "2025-10-24T07:19:01.449Z" }, - { url = "https://files.pythonhosted.org/packages/23/c6/a04e9cef7c052717fcb28fb63b3824802488f688391895b618e39be0f684/lupa-2.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8faddd9d198688c8884091173a088a8e920ecc96cda2ffed576a23574c4b3f6", size = 2073458, upload-time = "2025-10-24T07:19:03.369Z" }, - { url = "https://files.pythonhosted.org/packages/e6/10/824173d10f38b51fc77785228f01411b6ca28826ce27404c7c912e0e442c/lupa-2.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:daebb3a6b58095c917e76ba727ab37b27477fb926957c825205fbda431552134", size = 1067683, upload-time = "2025-10-24T07:19:06.2Z" }, - { url = "https://files.pythonhosted.org/packages/b6/dc/9692fbcf3c924d9c4ece2d8d2f724451ac2e09af0bd2a782db1cef34e799/lupa-2.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f3154e68972befe0f81564e37d8142b5d5d79931a18309226a04ec92487d4ea3", size = 1171892, upload-time = "2025-10-24T07:19:08.544Z" }, - { url = "https://files.pythonhosted.org/packages/84/ff/e318b628d4643c278c96ab3ddea07fc36b075a57383c837f5b11e537ba9d/lupa-2.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e4dadf77b9fedc0bfa53417cc28dc2278a26d4cbd95c29f8927ad4d8fe0a7ef9", size = 2166641, upload-time = "2025-10-24T07:19:10.485Z" }, - { url = "https://files.pythonhosted.org/packages/12/f7/a6f9ec2806cf2d50826980cdb4b3cffc7691dc6f95e13cc728846d5cb793/lupa-2.6-cp314-cp314-win32.whl", hash = "sha256:cb34169c6fa3bab3e8ac58ca21b8a7102f6a94b6a5d08d3636312f3f02fafd8f", size = 1456857, upload-time = "2025-10-24T07:19:37.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/de/df71896f25bdc18360fdfa3b802cd7d57d7fede41a0e9724a4625b412c85/lupa-2.6-cp314-cp314-win_amd64.whl", hash = "sha256:b74f944fe46c421e25d0f8692aef1e842192f6f7f68034201382ac440ef9ea67", size = 1731191, upload-time = "2025-10-24T07:19:40.281Z" }, - { url = "https://files.pythonhosted.org/packages/47/3c/a1f23b01c54669465f5f4c4083107d496fbe6fb45998771420e9aadcf145/lupa-2.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0e21b716408a21ab65723f8841cf7f2f37a844b7a965eeabb785e27fca4099cf", size = 999343, upload-time = "2025-10-24T07:19:12.519Z" }, - { url = "https://files.pythonhosted.org/packages/c5/6d/501994291cb640bfa2ccf7f554be4e6914afa21c4026bd01bff9ca8aac57/lupa-2.6-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:589db872a141bfff828340079bbdf3e9a31f2689f4ca0d88f97d9e8c2eae6142", size = 2000730, upload-time = "2025-10-24T07:19:14.869Z" }, - { url = "https://files.pythonhosted.org/packages/53/a5/457ffb4f3f20469956c2d4c4842a7675e884efc895b2f23d126d23e126cc/lupa-2.6-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:cd852a91a4a9d4dcbb9a58100f820a75a425703ec3e3f049055f60b8533b7953", size = 1021553, upload-time = "2025-10-24T07:19:17.123Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/36bb5a5d0960f2a5c7c700e0819abb76fd9bf9c1d8a66e5106416d6e9b14/lupa-2.6-cp314-cp314t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:0334753be028358922415ca97a64a3048e4ed155413fc4eaf87dd0a7e2752983", size = 1133275, upload-time = "2025-10-24T07:19:20.51Z" }, - { url = "https://files.pythonhosted.org/packages/19/86/202ff4429f663013f37d2229f6176ca9f83678a50257d70f61a0a97281bf/lupa-2.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:661d895cd38c87658a34780fac54a690ec036ead743e41b74c3fb81a9e65a6aa", size = 1038441, upload-time = "2025-10-24T07:19:22.509Z" }, - { url = "https://files.pythonhosted.org/packages/a7/42/d8125f8e420714e5b52e9c08d88b5329dfb02dcca731b4f21faaee6cc5b5/lupa-2.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aa58454ccc13878cc177c62529a2056be734da16369e451987ff92784994ca7", size = 2058324, upload-time = "2025-10-24T07:19:24.979Z" }, - { url = "https://files.pythonhosted.org/packages/2b/2c/47bf8b84059876e877a339717ddb595a4a7b0e8740bacae78ba527562e1c/lupa-2.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1425017264e470c98022bba8cff5bd46d054a827f5df6b80274f9cc71dafd24f", size = 1060250, upload-time = "2025-10-24T07:19:27.262Z" }, - { url = "https://files.pythonhosted.org/packages/c2/06/d88add2b6406ca1bdec99d11a429222837ca6d03bea42ca75afa169a78cb/lupa-2.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:224af0532d216e3105f0a127410f12320f7c5f1aa0300bdf9646b8d9afb0048c", size = 1151126, upload-time = "2025-10-24T07:19:29.522Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a0/89e6a024c3b4485b89ef86881c9d55e097e7cb0bdb74efb746f2fa6a9a76/lupa-2.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9abb98d5a8fd27c8285302e82199f0e56e463066f88f619d6594a450bf269d80", size = 2153693, upload-time = "2025-10-24T07:19:31.379Z" }, - { url = "https://files.pythonhosted.org/packages/b6/36/a0f007dc58fc1bbf51fb85dcc82fcb1f21b8c4261361de7dab0e3d8521ef/lupa-2.6-cp314-cp314t-win32.whl", hash = "sha256:1849efeba7a8f6fb8aa2c13790bee988fd242ae404bd459509640eeea3d1e291", size = 1590104, upload-time = "2025-10-24T07:19:33.514Z" }, - { url = "https://files.pythonhosted.org/packages/7d/5e/db903ce9cf82c48d6b91bf6d63ae4c8d0d17958939a4e04ba6b9f38b8643/lupa-2.6-cp314-cp314t-win_amd64.whl", hash = "sha256:fc1498d1a4fc028bc521c26d0fad4ca00ed63b952e32fb95949bda76a04bad52", size = 1913818, upload-time = "2025-10-24T07:19:36.039Z" }, -] - -[[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 = "mcp" -version = "1.25.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyjwt", extra = ["crypto"] }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d5/2d/649d80a0ecf6a1f82632ca44bec21c0461a9d9fc8934d38cb5b319f2db5e/mcp-1.25.0.tar.gz", hash = "sha256:56310361ebf0364e2d438e5b45f7668cbb124e158bb358333cd06e49e83a6802", size = 605387, upload-time = "2025-12-19T10:19:56.985Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/fc/6dc7659c2ae5ddf280477011f4213a74f806862856b796ef08f028e664bf/mcp-1.25.0-py3-none-any.whl", hash = "sha256:b37c38144a666add0862614cc79ec276e97d72aa8ca26d622818d4e278b9721a", size = 233076, upload-time = "2025-12-19T10:19:55.416Z" }, -] - -[[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 = "openai" -version = "2.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d8/b1/12fe1c196bea326261718eb037307c1c1fe1dedc2d2d4de777df822e6238/openai-2.14.0.tar.gz", hash = "sha256:419357bedde9402d23bf8f2ee372fca1985a73348debba94bddff06f19459952", size = 626938, upload-time = "2025-12-19T03:28:45.742Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/4b/7c1a00c2c3fbd004253937f7520f692a9650767aa73894d7a34f0d65d3f4/openai-2.14.0-py3-none-any.whl", hash = "sha256:7ea40aca4ffc4c4a776e77679021b47eec1160e341f42ae086ba949c9dcc9183", size = 1067558, upload-time = "2025-12-19T03:28:43.727Z" }, -] - -[[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 = "opentelemetry-api" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, -] - -[[package]] -name = "opentelemetry-exporter-prometheus" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-sdk" }, - { name = "prometheus-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/14/39/7dafa6fff210737267bed35a8855b6ac7399b9e582b8cf1f25f842517012/opentelemetry_exporter_prometheus-0.60b1.tar.gz", hash = "sha256:a4011b46906323f71724649d301b4dc188aaa068852e814f4df38cc76eac616b", size = 14976, upload-time = "2025-12-11T13:32:42.944Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/0d/4be6bf5477a3eb3d917d2f17d3c0b6720cd6cb97898444a61d43cc983f5c/opentelemetry_exporter_prometheus-0.60b1-py3-none-any.whl", hash = "sha256:49f59178de4f4590e3cef0b8b95cf6e071aae70e1f060566df5546fad773b8fd", size = 13019, upload-time = "2025-12-11T13:32:23.974Z" }, -] - -[[package]] -name = "opentelemetry-instrumentation" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "packaging" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/0f/7e6b713ac117c1f5e4e3300748af699b9902a2e5e34c9cf443dde25a01fa/opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a", size = 31706, upload-time = "2025-12-11T13:36:42.515Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096, upload-time = "2025-12-11T13:35:33.067Z" }, -] - -[[package]] -name = "opentelemetry-sdk" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, -] - -[[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 = "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 = "pathvalidate" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262, upload-time = "2025-06-15T09:07:20.736Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305, upload-time = "2025-06-15T09:07:19.117Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, -] - -[[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 = "prometheus-client" -version = "0.23.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/53/3edb5d68ecf6b38fcbcc1ad28391117d2a322d9a1a3eff04bfdb184d8c3b/prometheus_client-0.23.1.tar.gz", hash = "sha256:6ae8f9081eaaaf153a2e959d2e6c4f4fb57b12ef76c8c7980202f1e57b48b2ce", size = 80481, upload-time = "2025-09-18T20:47:25.043Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl", hash = "sha256:dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99", size = 61145, upload-time = "2025-09-18T20:47:23.875Z" }, -] - -[[package]] -name = "py-key-value-aio" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "py-key-value-shared" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/ce/3136b771dddf5ac905cc193b461eb67967cf3979688c6696e1f2cdcde7ea/py_key_value_aio-0.3.0.tar.gz", hash = "sha256:858e852fcf6d696d231266da66042d3355a7f9871650415feef9fca7a6cd4155", size = 50801, upload-time = "2025-11-17T16:50:04.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/10/72f6f213b8f0bce36eff21fda0a13271834e9eeff7f9609b01afdc253c79/py_key_value_aio-0.3.0-py3-none-any.whl", hash = "sha256:1c781915766078bfd608daa769fefb97e65d1d73746a3dfb640460e322071b64", size = 96342, upload-time = "2025-11-17T16:50:03.801Z" }, -] - -[package.optional-dependencies] -disk = [ - { name = "diskcache" }, - { name = "pathvalidate" }, -] -keyring = [ - { name = "keyring" }, -] -memory = [ - { name = "cachetools" }, -] -redis = [ - { name = "redis" }, -] - -[[package]] -name = "py-key-value-shared" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/e4/1971dfc4620a3a15b4579fe99e024f5edd6e0967a71154771a059daff4db/py_key_value_shared-0.3.0.tar.gz", hash = "sha256:8fdd786cf96c3e900102945f92aa1473138ebe960ef49da1c833790160c28a4b", size = 11666, upload-time = "2025-11-17T16:50:06.849Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e4/b8b0a03ece72f47dce2307d36e1c34725b7223d209fc679315ffe6a4e2c3/py_key_value_shared-0.3.0-py3-none-any.whl", hash = "sha256:5b0efba7ebca08bb158b1e93afc2f07d30b8f40c2fc12ce24a4c0d84f42f9298", size = 19560, upload-time = "2025-11-17T16:50:05.954Z" }, -] - -[[package]] -name = "pycparser" -version = "2.23" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[package.optional-dependencies] -email = [ - { name = "email-validator" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, -] - -[[package]] -name = "pydantic-settings" -version = "2.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, -] - -[[package]] -name = "pydocket" -version = "0.16.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cloudpickle" }, - { name = "fakeredis", extra = ["lua"] }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-prometheus" }, - { name = "opentelemetry-instrumentation" }, - { name = "prometheus-client" }, - { name = "py-key-value-aio", extra = ["memory", "redis"] }, - { name = "python-json-logger" }, - { name = "redis" }, - { name = "rich" }, - { name = "typer" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e0/c5/61dcfce4d50b66a3f09743294d37fab598b81bb0975054b7f732da9243ec/pydocket-0.16.3.tar.gz", hash = "sha256:78e9da576de09e9f3f410d2471ef1c679b7741ddd21b586c97a13872b69bd265", size = 297080, upload-time = "2025-12-23T23:37:33.32Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/94/93b7f5981aa04f922e0d9ce7326a4587866ec7e39f7c180ffcf408e66ee8/pydocket-0.16.3-py3-none-any.whl", hash = "sha256:e2b50925356e7cd535286255195458ac7bba15f25293356651b36d223db5dd7c", size = 67087, upload-time = "2025-12-23T23:37:31.829Z" }, -] - -[[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 = "pyjwt" -version = "2.10.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, -] - -[package.optional-dependencies] -crypto = [ - { name = "cryptography" }, -] - -[[package]] -name = "pyperclip" -version = "1.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, -] - -[[package]] -name = "pytest" -version = "9.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, -] - -[[package]] -name = "python-json-logger" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.21" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/78/96/804520d0850c7db98e5ccb70282e29208723f0964e88ffd9d0da2f52ea09/python_multipart-0.0.21.tar.gz", hash = "sha256:7137ebd4d3bbf70ea1622998f902b97a29434a9e8dc40eb203bbcf7c2a2cba92", size = 37196, upload-time = "2025-12-17T09:24:22.446Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/76/03af049af4dcee5d27442f71b6924f01f3efb5d2bd34f23fcd563f2cc5f5/python_multipart-0.0.21-py3-none-any.whl", hash = "sha256:cf7a6713e01c87aa35387f4774e812c4361150938d20d232800f75ffcf266090", size = 24541, upload-time = "2025-12-17T09:24:21.153Z" }, -] - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { 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 = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "redis" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/c8/983d5c6579a411d8a99bc5823cc5712768859b5ce2c8afe1a65b37832c81/redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c", size = 4796669, upload-time = "2025-11-19T15:54:39.961Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/f0/8956f8a86b20d7bb9d6ac0187cf4cd54d8065bc9a1a09eb8011d4d326596/redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b", size = 354159, upload-time = "2025-11-19T15:54:38.064Z" }, -] - -[[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 = "regex" -version = "2025.11.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/a9/546676f25e573a4cf00fe8e119b78a37b6a8fe2dc95cda877b30889c9c45/regex-2025.11.3.tar.gz", hash = "sha256:1fedc720f9bb2494ce31a58a1631f9c82df6a09b49c19517ea5cc280b4541e01", size = 414669, upload-time = "2025-11-03T21:34:22.089Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/74/18f04cb53e58e3fb107439699bd8375cf5a835eec81084e0bddbd122e4c2/regex-2025.11.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bc8ab71e2e31b16e40868a40a69007bc305e1109bd4658eb6cad007e0bf67c41", size = 489312, upload-time = "2025-11-03T21:31:34.343Z" }, - { url = "https://files.pythonhosted.org/packages/78/3f/37fcdd0d2b1e78909108a876580485ea37c91e1acf66d3bb8e736348f441/regex-2025.11.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:22b29dda7e1f7062a52359fca6e58e548e28c6686f205e780b02ad8ef710de36", size = 291256, upload-time = "2025-11-03T21:31:35.675Z" }, - { url = "https://files.pythonhosted.org/packages/bf/26/0a575f58eb23b7ebd67a45fccbc02ac030b737b896b7e7a909ffe43ffd6a/regex-2025.11.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3a91e4a29938bc1a082cc28fdea44be420bf2bebe2665343029723892eb073e1", size = 288921, upload-time = "2025-11-03T21:31:37.07Z" }, - { url = "https://files.pythonhosted.org/packages/ea/98/6a8dff667d1af907150432cf5abc05a17ccd32c72a3615410d5365ac167a/regex-2025.11.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b884f4226602ad40c5d55f52bf91a9df30f513864e0054bad40c0e9cf1afb7", size = 798568, upload-time = "2025-11-03T21:31:38.784Z" }, - { url = "https://files.pythonhosted.org/packages/64/15/92c1db4fa4e12733dd5a526c2dd2b6edcbfe13257e135fc0f6c57f34c173/regex-2025.11.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e0b11b2b2433d1c39c7c7a30e3f3d0aeeea44c2a8d0bae28f6b95f639927a69", size = 864165, upload-time = "2025-11-03T21:31:40.559Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e7/3ad7da8cdee1ce66c7cd37ab5ab05c463a86ffeb52b1a25fe7bd9293b36c/regex-2025.11.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:87eb52a81ef58c7ba4d45c3ca74e12aa4b4e77816f72ca25258a85b3ea96cb48", size = 912182, upload-time = "2025-11-03T21:31:42.002Z" }, - { url = "https://files.pythonhosted.org/packages/84/bd/9ce9f629fcb714ffc2c3faf62b6766ecb7a585e1e885eb699bcf130a5209/regex-2025.11.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a12ab1f5c29b4e93db518f5e3872116b7e9b1646c9f9f426f777b50d44a09e8c", size = 803501, upload-time = "2025-11-03T21:31:43.815Z" }, - { url = "https://files.pythonhosted.org/packages/7c/0f/8dc2e4349d8e877283e6edd6c12bdcebc20f03744e86f197ab6e4492bf08/regex-2025.11.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7521684c8c7c4f6e88e35ec89680ee1aa8358d3f09d27dfbdf62c446f5d4c695", size = 787842, upload-time = "2025-11-03T21:31:45.353Z" }, - { url = "https://files.pythonhosted.org/packages/f9/73/cff02702960bc185164d5619c0c62a2f598a6abff6695d391b096237d4ab/regex-2025.11.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7fe6e5440584e94cc4b3f5f4d98a25e29ca12dccf8873679a635638349831b98", size = 858519, upload-time = "2025-11-03T21:31:46.814Z" }, - { url = "https://files.pythonhosted.org/packages/61/83/0e8d1ae71e15bc1dc36231c90b46ee35f9d52fab2e226b0e039e7ea9c10a/regex-2025.11.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:8e026094aa12b43f4fd74576714e987803a315c76edb6b098b9809db5de58f74", size = 850611, upload-time = "2025-11-03T21:31:48.289Z" }, - { url = "https://files.pythonhosted.org/packages/c8/f5/70a5cdd781dcfaa12556f2955bf170cd603cb1c96a1827479f8faea2df97/regex-2025.11.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:435bbad13e57eb5606a68443af62bed3556de2f46deb9f7d4237bc2f1c9fb3a0", size = 789759, upload-time = "2025-11-03T21:31:49.759Z" }, - { url = "https://files.pythonhosted.org/packages/59/9b/7c29be7903c318488983e7d97abcf8ebd3830e4c956c4c540005fcfb0462/regex-2025.11.3-cp312-cp312-win32.whl", hash = "sha256:3839967cf4dc4b985e1570fd8d91078f0c519f30491c60f9ac42a8db039be204", size = 266194, upload-time = "2025-11-03T21:31:51.53Z" }, - { url = "https://files.pythonhosted.org/packages/1a/67/3b92df89f179d7c367be654ab5626ae311cb28f7d5c237b6bb976cd5fbbb/regex-2025.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:e721d1b46e25c481dc5ded6f4b3f66c897c58d2e8cfdf77bbced84339108b0b9", size = 277069, upload-time = "2025-11-03T21:31:53.151Z" }, - { url = "https://files.pythonhosted.org/packages/d7/55/85ba4c066fe5094d35b249c3ce8df0ba623cfd35afb22d6764f23a52a1c5/regex-2025.11.3-cp312-cp312-win_arm64.whl", hash = "sha256:64350685ff08b1d3a6fff33f45a9ca183dc1d58bbfe4981604e70ec9801bbc26", size = 270330, upload-time = "2025-11-03T21:31:54.514Z" }, - { url = "https://files.pythonhosted.org/packages/e1/a7/dda24ebd49da46a197436ad96378f17df30ceb40e52e859fc42cac45b850/regex-2025.11.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c1e448051717a334891f2b9a620fe36776ebf3dd8ec46a0b877c8ae69575feb4", size = 489081, upload-time = "2025-11-03T21:31:55.9Z" }, - { url = "https://files.pythonhosted.org/packages/19/22/af2dc751aacf88089836aa088a1a11c4f21a04707eb1b0478e8e8fb32847/regex-2025.11.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9b5aca4d5dfd7fbfbfbdaf44850fcc7709a01146a797536a8f84952e940cca76", size = 291123, upload-time = "2025-11-03T21:31:57.758Z" }, - { url = "https://files.pythonhosted.org/packages/a3/88/1a3ea5672f4b0a84802ee9891b86743438e7c04eb0b8f8c4e16a42375327/regex-2025.11.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:04d2765516395cf7dda331a244a3282c0f5ae96075f728629287dfa6f76ba70a", size = 288814, upload-time = "2025-11-03T21:32:01.12Z" }, - { url = "https://files.pythonhosted.org/packages/fb/8c/f5987895bf42b8ddeea1b315c9fedcfe07cadee28b9c98cf50d00adcb14d/regex-2025.11.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d9903ca42bfeec4cebedba8022a7c97ad2aab22e09573ce9976ba01b65e4361", size = 798592, upload-time = "2025-11-03T21:32:03.006Z" }, - { url = "https://files.pythonhosted.org/packages/99/2a/6591ebeede78203fa77ee46a1c36649e02df9eaa77a033d1ccdf2fcd5d4e/regex-2025.11.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:639431bdc89d6429f6721625e8129413980ccd62e9d3f496be618a41d205f160", size = 864122, upload-time = "2025-11-03T21:32:04.553Z" }, - { url = "https://files.pythonhosted.org/packages/94/d6/be32a87cf28cf8ed064ff281cfbd49aefd90242a83e4b08b5a86b38e8eb4/regex-2025.11.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f117efad42068f9715677c8523ed2be1518116d1c49b1dd17987716695181efe", size = 912272, upload-time = "2025-11-03T21:32:06.148Z" }, - { url = "https://files.pythonhosted.org/packages/62/11/9bcef2d1445665b180ac7f230406ad80671f0fc2a6ffb93493b5dd8cd64c/regex-2025.11.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4aecb6f461316adf9f1f0f6a4a1a3d79e045f9b71ec76055a791affa3b285850", size = 803497, upload-time = "2025-11-03T21:32:08.162Z" }, - { url = "https://files.pythonhosted.org/packages/e5/a7/da0dc273d57f560399aa16d8a68ae7f9b57679476fc7ace46501d455fe84/regex-2025.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3b3a5f320136873cc5561098dfab677eea139521cb9a9e8db98b7e64aef44cbc", size = 787892, upload-time = "2025-11-03T21:32:09.769Z" }, - { url = "https://files.pythonhosted.org/packages/da/4b/732a0c5a9736a0b8d6d720d4945a2f1e6f38f87f48f3173559f53e8d5d82/regex-2025.11.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:75fa6f0056e7efb1f42a1c34e58be24072cb9e61a601340cc1196ae92326a4f9", size = 858462, upload-time = "2025-11-03T21:32:11.769Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f5/a2a03df27dc4c2d0c769220f5110ba8c4084b0bfa9ab0f9b4fcfa3d2b0fc/regex-2025.11.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:dbe6095001465294f13f1adcd3311e50dd84e5a71525f20a10bd16689c61ce0b", size = 850528, upload-time = "2025-11-03T21:32:13.906Z" }, - { url = "https://files.pythonhosted.org/packages/d6/09/e1cd5bee3841c7f6eb37d95ca91cdee7100b8f88b81e41c2ef426910891a/regex-2025.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:454d9b4ae7881afbc25015b8627c16d88a597479b9dea82b8c6e7e2e07240dc7", size = 789866, upload-time = "2025-11-03T21:32:15.748Z" }, - { url = "https://files.pythonhosted.org/packages/eb/51/702f5ea74e2a9c13d855a6a85b7f80c30f9e72a95493260193c07f3f8d74/regex-2025.11.3-cp313-cp313-win32.whl", hash = "sha256:28ba4d69171fc6e9896337d4fc63a43660002b7da53fc15ac992abcf3410917c", size = 266189, upload-time = "2025-11-03T21:32:17.493Z" }, - { url = "https://files.pythonhosted.org/packages/8b/00/6e29bb314e271a743170e53649db0fdb8e8ff0b64b4f425f5602f4eb9014/regex-2025.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:bac4200befe50c670c405dc33af26dad5a3b6b255dd6c000d92fe4629f9ed6a5", size = 277054, upload-time = "2025-11-03T21:32:19.042Z" }, - { url = "https://files.pythonhosted.org/packages/25/f1/b156ff9f2ec9ac441710764dda95e4edaf5f36aca48246d1eea3f1fd96ec/regex-2025.11.3-cp313-cp313-win_arm64.whl", hash = "sha256:2292cd5a90dab247f9abe892ac584cb24f0f54680c73fcb4a7493c66c2bf2467", size = 270325, upload-time = "2025-11-03T21:32:21.338Z" }, - { url = "https://files.pythonhosted.org/packages/20/28/fd0c63357caefe5680b8ea052131acbd7f456893b69cc2a90cc3e0dc90d4/regex-2025.11.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:1eb1ebf6822b756c723e09f5186473d93236c06c579d2cc0671a722d2ab14281", size = 491984, upload-time = "2025-11-03T21:32:23.466Z" }, - { url = "https://files.pythonhosted.org/packages/df/ec/7014c15626ab46b902b3bcc4b28a7bae46d8f281fc7ea9c95e22fcaaa917/regex-2025.11.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1e00ec2970aab10dc5db34af535f21fcf32b4a31d99e34963419636e2f85ae39", size = 292673, upload-time = "2025-11-03T21:32:25.034Z" }, - { url = "https://files.pythonhosted.org/packages/23/ab/3b952ff7239f20d05f1f99e9e20188513905f218c81d52fb5e78d2bf7634/regex-2025.11.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a4cb042b615245d5ff9b3794f56be4138b5adc35a4166014d31d1814744148c7", size = 291029, upload-time = "2025-11-03T21:32:26.528Z" }, - { url = "https://files.pythonhosted.org/packages/21/7e/3dc2749fc684f455f162dcafb8a187b559e2614f3826877d3844a131f37b/regex-2025.11.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44f264d4bf02f3176467d90b294d59bf1db9fe53c141ff772f27a8b456b2a9ed", size = 807437, upload-time = "2025-11-03T21:32:28.363Z" }, - { url = "https://files.pythonhosted.org/packages/1b/0b/d529a85ab349c6a25d1ca783235b6e3eedf187247eab536797021f7126c6/regex-2025.11.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7be0277469bf3bd7a34a9c57c1b6a724532a0d235cd0dc4e7f4316f982c28b19", size = 873368, upload-time = "2025-11-03T21:32:30.4Z" }, - { url = "https://files.pythonhosted.org/packages/7d/18/2d868155f8c9e3e9d8f9e10c64e9a9f496bb8f7e037a88a8bed26b435af6/regex-2025.11.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0d31e08426ff4b5b650f68839f5af51a92a5b51abd8554a60c2fbc7c71f25d0b", size = 914921, upload-time = "2025-11-03T21:32:32.123Z" }, - { url = "https://files.pythonhosted.org/packages/2d/71/9d72ff0f354fa783fe2ba913c8734c3b433b86406117a8db4ea2bf1c7a2f/regex-2025.11.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e43586ce5bd28f9f285a6e729466841368c4a0353f6fd08d4ce4630843d3648a", size = 812708, upload-time = "2025-11-03T21:32:34.305Z" }, - { url = "https://files.pythonhosted.org/packages/e7/19/ce4bf7f5575c97f82b6e804ffb5c4e940c62609ab2a0d9538d47a7fdf7d4/regex-2025.11.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0f9397d561a4c16829d4e6ff75202c1c08b68a3bdbfe29dbfcdb31c9830907c6", size = 795472, upload-time = "2025-11-03T21:32:36.364Z" }, - { url = "https://files.pythonhosted.org/packages/03/86/fd1063a176ffb7b2315f9a1b08d17b18118b28d9df163132615b835a26ee/regex-2025.11.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:dd16e78eb18ffdb25ee33a0682d17912e8cc8a770e885aeee95020046128f1ce", size = 868341, upload-time = "2025-11-03T21:32:38.042Z" }, - { url = "https://files.pythonhosted.org/packages/12/43/103fb2e9811205e7386366501bc866a164a0430c79dd59eac886a2822950/regex-2025.11.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:ffcca5b9efe948ba0661e9df0fa50d2bc4b097c70b9810212d6b62f05d83b2dd", size = 854666, upload-time = "2025-11-03T21:32:40.079Z" }, - { url = "https://files.pythonhosted.org/packages/7d/22/e392e53f3869b75804762c7c848bd2dd2abf2b70fb0e526f58724638bd35/regex-2025.11.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c56b4d162ca2b43318ac671c65bd4d563e841a694ac70e1a976ac38fcf4ca1d2", size = 799473, upload-time = "2025-11-03T21:32:42.148Z" }, - { url = "https://files.pythonhosted.org/packages/4f/f9/8bd6b656592f925b6845fcbb4d57603a3ac2fb2373344ffa1ed70aa6820a/regex-2025.11.3-cp313-cp313t-win32.whl", hash = "sha256:9ddc42e68114e161e51e272f667d640f97e84a2b9ef14b7477c53aac20c2d59a", size = 268792, upload-time = "2025-11-03T21:32:44.13Z" }, - { url = "https://files.pythonhosted.org/packages/e5/87/0e7d603467775ff65cd2aeabf1b5b50cc1c3708556a8b849a2fa4dd1542b/regex-2025.11.3-cp313-cp313t-win_amd64.whl", hash = "sha256:7a7c7fdf755032ffdd72c77e3d8096bdcb0eb92e89e17571a196f03d88b11b3c", size = 280214, upload-time = "2025-11-03T21:32:45.853Z" }, - { url = "https://files.pythonhosted.org/packages/8d/d0/2afc6f8e94e2b64bfb738a7c2b6387ac1699f09f032d363ed9447fd2bb57/regex-2025.11.3-cp313-cp313t-win_arm64.whl", hash = "sha256:df9eb838c44f570283712e7cff14c16329a9f0fb19ca492d21d4b7528ee6821e", size = 271469, upload-time = "2025-11-03T21:32:48.026Z" }, - { url = "https://files.pythonhosted.org/packages/31/e9/f6e13de7e0983837f7b6d238ad9458800a874bf37c264f7923e63409944c/regex-2025.11.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9697a52e57576c83139d7c6f213d64485d3df5bf84807c35fa409e6c970801c6", size = 489089, upload-time = "2025-11-03T21:32:50.027Z" }, - { url = "https://files.pythonhosted.org/packages/a3/5c/261f4a262f1fa65141c1b74b255988bd2fa020cc599e53b080667d591cfc/regex-2025.11.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e18bc3f73bd41243c9b38a6d9f2366cd0e0137a9aebe2d8ff76c5b67d4c0a3f4", size = 291059, upload-time = "2025-11-03T21:32:51.682Z" }, - { url = "https://files.pythonhosted.org/packages/8e/57/f14eeb7f072b0e9a5a090d1712741fd8f214ec193dba773cf5410108bb7d/regex-2025.11.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:61a08bcb0ec14ff4e0ed2044aad948d0659604f824cbd50b55e30b0ec6f09c73", size = 288900, upload-time = "2025-11-03T21:32:53.569Z" }, - { url = "https://files.pythonhosted.org/packages/3c/6b/1d650c45e99a9b327586739d926a1cd4e94666b1bd4af90428b36af66dc7/regex-2025.11.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9c30003b9347c24bcc210958c5d167b9e4f9be786cb380a7d32f14f9b84674f", size = 799010, upload-time = "2025-11-03T21:32:55.222Z" }, - { url = "https://files.pythonhosted.org/packages/99/ee/d66dcbc6b628ce4e3f7f0cbbb84603aa2fc0ffc878babc857726b8aab2e9/regex-2025.11.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4e1e592789704459900728d88d41a46fe3969b82ab62945560a31732ffc19a6d", size = 864893, upload-time = "2025-11-03T21:32:57.239Z" }, - { url = "https://files.pythonhosted.org/packages/bf/2d/f238229f1caba7ac87a6c4153d79947fb0261415827ae0f77c304260c7d3/regex-2025.11.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6538241f45eb5a25aa575dbba1069ad786f68a4f2773a29a2bd3dd1f9de787be", size = 911522, upload-time = "2025-11-03T21:32:59.274Z" }, - { url = "https://files.pythonhosted.org/packages/bd/3d/22a4eaba214a917c80e04f6025d26143690f0419511e0116508e24b11c9b/regex-2025.11.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce22519c989bb72a7e6b36a199384c53db7722fe669ba891da75907fe3587db", size = 803272, upload-time = "2025-11-03T21:33:01.393Z" }, - { url = "https://files.pythonhosted.org/packages/84/b1/03188f634a409353a84b5ef49754b97dbcc0c0f6fd6c8ede505a8960a0a4/regex-2025.11.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:66d559b21d3640203ab9075797a55165d79017520685fb407b9234d72ab63c62", size = 787958, upload-time = "2025-11-03T21:33:03.379Z" }, - { url = "https://files.pythonhosted.org/packages/99/6a/27d072f7fbf6fadd59c64d210305e1ff865cc3b78b526fd147db768c553b/regex-2025.11.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:669dcfb2e38f9e8c69507bace46f4889e3abbfd9b0c29719202883c0a603598f", size = 859289, upload-time = "2025-11-03T21:33:05.374Z" }, - { url = "https://files.pythonhosted.org/packages/9a/70/1b3878f648e0b6abe023172dacb02157e685564853cc363d9961bcccde4e/regex-2025.11.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:32f74f35ff0f25a5021373ac61442edcb150731fbaa28286bbc8bb1582c89d02", size = 850026, upload-time = "2025-11-03T21:33:07.131Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d5/68e25559b526b8baab8e66839304ede68ff6727237a47727d240006bd0ff/regex-2025.11.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e6c7a21dffba883234baefe91bc3388e629779582038f75d2a5be918e250f0ed", size = 789499, upload-time = "2025-11-03T21:33:09.141Z" }, - { url = "https://files.pythonhosted.org/packages/fc/df/43971264857140a350910d4e33df725e8c94dd9dee8d2e4729fa0d63d49e/regex-2025.11.3-cp314-cp314-win32.whl", hash = "sha256:795ea137b1d809eb6836b43748b12634291c0ed55ad50a7d72d21edf1cd565c4", size = 271604, upload-time = "2025-11-03T21:33:10.9Z" }, - { url = "https://files.pythonhosted.org/packages/01/6f/9711b57dc6894a55faf80a4c1b5aa4f8649805cb9c7aef46f7d27e2b9206/regex-2025.11.3-cp314-cp314-win_amd64.whl", hash = "sha256:9f95fbaa0ee1610ec0fc6b26668e9917a582ba80c52cc6d9ada15e30aa9ab9ad", size = 280320, upload-time = "2025-11-03T21:33:12.572Z" }, - { url = "https://files.pythonhosted.org/packages/f1/7e/f6eaa207d4377481f5e1775cdeb5a443b5a59b392d0065f3417d31d80f87/regex-2025.11.3-cp314-cp314-win_arm64.whl", hash = "sha256:dfec44d532be4c07088c3de2876130ff0fbeeacaa89a137decbbb5f665855a0f", size = 273372, upload-time = "2025-11-03T21:33:14.219Z" }, - { url = "https://files.pythonhosted.org/packages/c3/06/49b198550ee0f5e4184271cee87ba4dfd9692c91ec55289e6282f0f86ccf/regex-2025.11.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ba0d8a5d7f04f73ee7d01d974d47c5834f8a1b0224390e4fe7c12a3a92a78ecc", size = 491985, upload-time = "2025-11-03T21:33:16.555Z" }, - { url = "https://files.pythonhosted.org/packages/ce/bf/abdafade008f0b1c9da10d934034cb670432d6cf6cbe38bbb53a1cfd6cf8/regex-2025.11.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:442d86cf1cfe4faabf97db7d901ef58347efd004934da045c745e7b5bd57ac49", size = 292669, upload-time = "2025-11-03T21:33:18.32Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ef/0c357bb8edbd2ad8e273fcb9e1761bc37b8acbc6e1be050bebd6475f19c1/regex-2025.11.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:fd0a5e563c756de210bb964789b5abe4f114dacae9104a47e1a649b910361536", size = 291030, upload-time = "2025-11-03T21:33:20.048Z" }, - { url = "https://files.pythonhosted.org/packages/79/06/edbb67257596649b8fb088d6aeacbcb248ac195714b18a65e018bf4c0b50/regex-2025.11.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf3490bcbb985a1ae97b2ce9ad1c0f06a852d5b19dde9b07bdf25bf224248c95", size = 807674, upload-time = "2025-11-03T21:33:21.797Z" }, - { url = "https://files.pythonhosted.org/packages/f4/d9/ad4deccfce0ea336296bd087f1a191543bb99ee1c53093dcd4c64d951d00/regex-2025.11.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3809988f0a8b8c9dcc0f92478d6501fac7200b9ec56aecf0ec21f4a2ec4b6009", size = 873451, upload-time = "2025-11-03T21:33:23.741Z" }, - { url = "https://files.pythonhosted.org/packages/13/75/a55a4724c56ef13e3e04acaab29df26582f6978c000ac9cd6810ad1f341f/regex-2025.11.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f4ff94e58e84aedb9c9fce66d4ef9f27a190285b451420f297c9a09f2b9abee9", size = 914980, upload-time = "2025-11-03T21:33:25.999Z" }, - { url = "https://files.pythonhosted.org/packages/67/1e/a1657ee15bd9116f70d4a530c736983eed997b361e20ecd8f5ca3759d5c5/regex-2025.11.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eb542fd347ce61e1321b0a6b945d5701528dca0cd9759c2e3bb8bd57e47964d", size = 812852, upload-time = "2025-11-03T21:33:27.852Z" }, - { url = "https://files.pythonhosted.org/packages/b8/6f/f7516dde5506a588a561d296b2d0044839de06035bb486b326065b4c101e/regex-2025.11.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2d5919075a1f2e413c00b056ea0c2f065b3f5fe83c3d07d325ab92dce51d6", size = 795566, upload-time = "2025-11-03T21:33:32.364Z" }, - { url = "https://files.pythonhosted.org/packages/d9/dd/3d10b9e170cc16fb34cb2cef91513cf3df65f440b3366030631b2984a264/regex-2025.11.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3f8bf11a4827cc7ce5a53d4ef6cddd5ad25595d3c1435ef08f76825851343154", size = 868463, upload-time = "2025-11-03T21:33:34.459Z" }, - { url = "https://files.pythonhosted.org/packages/f5/8e/935e6beff1695aa9085ff83195daccd72acc82c81793df480f34569330de/regex-2025.11.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:22c12d837298651e5550ac1d964e4ff57c3f56965fc1812c90c9fb2028eaf267", size = 854694, upload-time = "2025-11-03T21:33:36.793Z" }, - { url = "https://files.pythonhosted.org/packages/92/12/10650181a040978b2f5720a6a74d44f841371a3d984c2083fc1752e4acf6/regex-2025.11.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:62ba394a3dda9ad41c7c780f60f6e4a70988741415ae96f6d1bf6c239cf01379", size = 799691, upload-time = "2025-11-03T21:33:39.079Z" }, - { url = "https://files.pythonhosted.org/packages/67/90/8f37138181c9a7690e7e4cb388debbd389342db3c7381d636d2875940752/regex-2025.11.3-cp314-cp314t-win32.whl", hash = "sha256:4bf146dca15cdd53224a1bf46d628bd7590e4a07fbb69e720d561aea43a32b38", size = 274583, upload-time = "2025-11-03T21:33:41.302Z" }, - { url = "https://files.pythonhosted.org/packages/8f/cd/867f5ec442d56beb56f5f854f40abcfc75e11d10b11fdb1869dd39c63aaf/regex-2025.11.3-cp314-cp314t-win_amd64.whl", hash = "sha256:adad1a1bcf1c9e76346e091d22d23ac54ef28e1365117d99521631078dfec9de", size = 284286, upload-time = "2025-11-03T21:33:43.324Z" }, - { url = "https://files.pythonhosted.org/packages/20/31/32c0c4610cbc070362bf1d2e4ea86d1ea29014d400a6d6c2486fcfd57766/regex-2025.11.3-cp314-cp314t-win_arm64.whl", hash = "sha256:c54f768482cef41e219720013cd05933b6f971d9562544d691c68699bf2b6801", size = 274741, upload-time = "2025-11-03T21:33:45.557Z" }, -] - -[[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 = "rich" -version = "14.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, -] - -[[package]] -name = "rich-rst" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, -] - -[[package]] -name = "ruff" -version = "0.14.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/08/52232a877978dd8f9cf2aeddce3e611b40a63287dfca29b6b8da791f5e8d/ruff-0.14.10.tar.gz", hash = "sha256:9a2e830f075d1a42cd28420d7809ace390832a490ed0966fe373ba288e77aaf4", size = 5859763, upload-time = "2025-12-18T19:28:57.98Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/01/933704d69f3f05ee16ef11406b78881733c186fe14b6a46b05cfcaf6d3b2/ruff-0.14.10-py3-none-linux_armv6l.whl", hash = "sha256:7a3ce585f2ade3e1f29ec1b92df13e3da262178df8c8bdf876f48fa0e8316c49", size = 13527080, upload-time = "2025-12-18T19:29:25.642Z" }, - { url = "https://files.pythonhosted.org/packages/df/58/a0349197a7dfa603ffb7f5b0470391efa79ddc327c1e29c4851e85b09cc5/ruff-0.14.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:674f9be9372907f7257c51f1d4fc902cb7cf014b9980152b802794317941f08f", size = 13797320, upload-time = "2025-12-18T19:29:02.571Z" }, - { url = "https://files.pythonhosted.org/packages/7b/82/36be59f00a6082e38c23536df4e71cdbc6af8d7c707eade97fcad5c98235/ruff-0.14.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d85713d522348837ef9df8efca33ccb8bd6fcfc86a2cde3ccb4bc9d28a18003d", size = 12918434, upload-time = "2025-12-18T19:28:51.202Z" }, - { url = "https://files.pythonhosted.org/packages/a6/00/45c62a7f7e34da92a25804f813ebe05c88aa9e0c25e5cb5a7d23dd7450e3/ruff-0.14.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6987ebe0501ae4f4308d7d24e2d0fe3d7a98430f5adfd0f1fead050a740a3a77", size = 13371961, upload-time = "2025-12-18T19:29:04.991Z" }, - { url = "https://files.pythonhosted.org/packages/40/31/a5906d60f0405f7e57045a70f2d57084a93ca7425f22e1d66904769d1628/ruff-0.14.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16a01dfb7b9e4eee556fbfd5392806b1b8550c9b4a9f6acd3dbe6812b193c70a", size = 13275629, upload-time = "2025-12-18T19:29:21.381Z" }, - { url = "https://files.pythonhosted.org/packages/3e/60/61c0087df21894cf9d928dc04bcd4fb10e8b2e8dca7b1a276ba2155b2002/ruff-0.14.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7165d31a925b7a294465fa81be8c12a0e9b60fb02bf177e79067c867e71f8b1f", size = 14029234, upload-time = "2025-12-18T19:29:00.132Z" }, - { url = "https://files.pythonhosted.org/packages/44/84/77d911bee3b92348b6e5dab5a0c898d87084ea03ac5dc708f46d88407def/ruff-0.14.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c561695675b972effb0c0a45db233f2c816ff3da8dcfbe7dfc7eed625f218935", size = 15449890, upload-time = "2025-12-18T19:28:53.573Z" }, - { url = "https://files.pythonhosted.org/packages/e9/36/480206eaefa24a7ec321582dda580443a8f0671fdbf6b1c80e9c3e93a16a/ruff-0.14.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bb98fcbbc61725968893682fd4df8966a34611239c9fd07a1f6a07e7103d08e", size = 15123172, upload-time = "2025-12-18T19:29:23.453Z" }, - { url = "https://files.pythonhosted.org/packages/5c/38/68e414156015ba80cef5473d57919d27dfb62ec804b96180bafdeaf0e090/ruff-0.14.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f24b47993a9d8cb858429e97bdf8544c78029f09b520af615c1d261bf827001d", size = 14460260, upload-time = "2025-12-18T19:29:27.808Z" }, - { url = "https://files.pythonhosted.org/packages/b3/19/9e050c0dca8aba824d67cc0db69fb459c28d8cd3f6855b1405b3f29cc91d/ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59aabd2e2c4fd614d2862e7939c34a532c04f1084476d6833dddef4afab87e9f", size = 14229978, upload-time = "2025-12-18T19:29:11.32Z" }, - { url = "https://files.pythonhosted.org/packages/51/eb/e8dd1dd6e05b9e695aa9dd420f4577debdd0f87a5ff2fedda33c09e9be8c/ruff-0.14.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:213db2b2e44be8625002dbea33bb9c60c66ea2c07c084a00d55732689d697a7f", size = 14338036, upload-time = "2025-12-18T19:29:09.184Z" }, - { url = "https://files.pythonhosted.org/packages/6a/12/f3e3a505db7c19303b70af370d137795fcfec136d670d5de5391e295c134/ruff-0.14.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b914c40ab64865a17a9a5b67911d14df72346a634527240039eb3bd650e5979d", size = 13264051, upload-time = "2025-12-18T19:29:13.431Z" }, - { url = "https://files.pythonhosted.org/packages/08/64/8c3a47eaccfef8ac20e0484e68e0772013eb85802f8a9f7603ca751eb166/ruff-0.14.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1484983559f026788e3a5c07c81ef7d1e97c1c78ed03041a18f75df104c45405", size = 13283998, upload-time = "2025-12-18T19:29:06.994Z" }, - { url = "https://files.pythonhosted.org/packages/12/84/534a5506f4074e5cc0529e5cd96cfc01bb480e460c7edf5af70d2bcae55e/ruff-0.14.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c70427132db492d25f982fffc8d6c7535cc2fd2c83fc8888f05caaa248521e60", size = 13601891, upload-time = "2025-12-18T19:28:55.811Z" }, - { url = "https://files.pythonhosted.org/packages/0d/1e/14c916087d8598917dbad9b2921d340f7884824ad6e9c55de948a93b106d/ruff-0.14.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5bcf45b681e9f1ee6445d317ce1fa9d6cba9a6049542d1c3d5b5958986be8830", size = 14336660, upload-time = "2025-12-18T19:29:16.531Z" }, - { url = "https://files.pythonhosted.org/packages/f2/1c/d7b67ab43f30013b47c12b42d1acd354c195351a3f7a1d67f59e54227ede/ruff-0.14.10-py3-none-win32.whl", hash = "sha256:104c49fc7ab73f3f3a758039adea978869a918f31b73280db175b43a2d9b51d6", size = 13196187, upload-time = "2025-12-18T19:29:19.006Z" }, - { url = "https://files.pythonhosted.org/packages/fb/9c/896c862e13886fae2af961bef3e6312db9ebc6adc2b156fe95e615dee8c1/ruff-0.14.10-py3-none-win_amd64.whl", hash = "sha256:466297bd73638c6bdf06485683e812db1c00c7ac96d4ddd0294a338c62fdc154", size = 14661283, upload-time = "2025-12-18T19:29:30.16Z" }, - { url = "https://files.pythonhosted.org/packages/74/31/b0e29d572670dca3674eeee78e418f20bdf97fa8aa9ea71380885e175ca0/ruff-0.14.10-py3-none-win_arm64.whl", hash = "sha256:e51d046cf6dda98a4633b8a8a771451107413b0f07183b2bef03f075599e44e6", size = 13729839, upload-time = "2025-12-18T19:28:48.636Z" }, -] - -[[package]] -name = "secretstorage" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[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 = "sortedcontainers" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, -] - -[[package]] -name = "sse-starlette" -version = "3.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "starlette" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/62/08/8f554b0e5bad3e4e880521a1686d96c05198471eed860b0eb89b57ea3636/sse_starlette-3.1.1.tar.gz", hash = "sha256:bffa531420c1793ab224f63648c059bcadc412bf9fdb1301ac8de1cf9a67b7fb", size = 24306, upload-time = "2025-12-26T15:22:53.836Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/31/4c281581a0f8de137b710a07f65518b34bcf333b201cfa06cfda9af05f8a/sse_starlette-3.1.1-py3-none-any.whl", hash = "sha256:bb38f71ae74cfd86b529907a9fda5632195dfa6ae120f214ea4c890c7ee9d436", size = 12442, upload-time = "2025-12-26T15:22:52.911Z" }, -] - -[[package]] -name = "starlette" -version = "0.50.0" -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/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, -] - -[[package]] -name = "tiktoken" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "regex" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806, upload-time = "2025-10-06T20:22:45.419Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/85/be65d39d6b647c79800fd9d29241d081d4eeb06271f383bb87200d74cf76/tiktoken-0.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b97f74aca0d78a1ff21b8cd9e9925714c15a9236d6ceacf5c7327c117e6e21e8", size = 1050728, upload-time = "2025-10-06T20:21:52.756Z" }, - { url = "https://files.pythonhosted.org/packages/4a/42/6573e9129bc55c9bf7300b3a35bef2c6b9117018acca0dc760ac2d93dffe/tiktoken-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b90f5ad190a4bb7c3eb30c5fa32e1e182ca1ca79f05e49b448438c3e225a49b", size = 994049, upload-time = "2025-10-06T20:21:53.782Z" }, - { url = "https://files.pythonhosted.org/packages/66/c5/ed88504d2f4a5fd6856990b230b56d85a777feab84e6129af0822f5d0f70/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:65b26c7a780e2139e73acc193e5c63ac754021f160df919add909c1492c0fb37", size = 1129008, upload-time = "2025-10-06T20:21:54.832Z" }, - { url = "https://files.pythonhosted.org/packages/f4/90/3dae6cc5436137ebd38944d396b5849e167896fc2073da643a49f372dc4f/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:edde1ec917dfd21c1f2f8046b86348b0f54a2c0547f68149d8600859598769ad", size = 1152665, upload-time = "2025-10-06T20:21:56.129Z" }, - { url = "https://files.pythonhosted.org/packages/a3/fe/26df24ce53ffde419a42f5f53d755b995c9318908288c17ec3f3448313a3/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:35a2f8ddd3824608b3d650a000c1ef71f730d0c56486845705a8248da00f9fe5", size = 1194230, upload-time = "2025-10-06T20:21:57.546Z" }, - { url = "https://files.pythonhosted.org/packages/20/cc/b064cae1a0e9fac84b0d2c46b89f4e57051a5f41324e385d10225a984c24/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83d16643edb7fa2c99eff2ab7733508aae1eebb03d5dfc46f5565862810f24e3", size = 1254688, upload-time = "2025-10-06T20:21:58.619Z" }, - { url = "https://files.pythonhosted.org/packages/81/10/b8523105c590c5b8349f2587e2fdfe51a69544bd5a76295fc20f2374f470/tiktoken-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffc5288f34a8bc02e1ea7047b8d041104791d2ddbf42d1e5fa07822cbffe16bd", size = 878694, upload-time = "2025-10-06T20:21:59.876Z" }, - { url = "https://files.pythonhosted.org/packages/00/61/441588ee21e6b5cdf59d6870f86beb9789e532ee9718c251b391b70c68d6/tiktoken-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3", size = 1050802, upload-time = "2025-10-06T20:22:00.96Z" }, - { url = "https://files.pythonhosted.org/packages/1f/05/dcf94486d5c5c8d34496abe271ac76c5b785507c8eae71b3708f1ad9b45a/tiktoken-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160", size = 993995, upload-time = "2025-10-06T20:22:02.788Z" }, - { url = "https://files.pythonhosted.org/packages/a0/70/5163fe5359b943f8db9946b62f19be2305de8c3d78a16f629d4165e2f40e/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa", size = 1128948, upload-time = "2025-10-06T20:22:03.814Z" }, - { url = "https://files.pythonhosted.org/packages/0c/da/c028aa0babf77315e1cef357d4d768800c5f8a6de04d0eac0f377cb619fa/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be", size = 1151986, upload-time = "2025-10-06T20:22:05.173Z" }, - { url = "https://files.pythonhosted.org/packages/a0/5a/886b108b766aa53e295f7216b509be95eb7d60b166049ce2c58416b25f2a/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a", size = 1194222, upload-time = "2025-10-06T20:22:06.265Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f8/4db272048397636ac7a078d22773dd2795b1becee7bc4922fe6207288d57/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3", size = 1255097, upload-time = "2025-10-06T20:22:07.403Z" }, - { url = "https://files.pythonhosted.org/packages/8e/32/45d02e2e0ea2be3a9ed22afc47d93741247e75018aac967b713b2941f8ea/tiktoken-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697", size = 879117, upload-time = "2025-10-06T20:22:08.418Z" }, - { url = "https://files.pythonhosted.org/packages/ce/76/994fc868f88e016e6d05b0da5ac24582a14c47893f4474c3e9744283f1d5/tiktoken-0.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16", size = 1050309, upload-time = "2025-10-06T20:22:10.939Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b8/57ef1456504c43a849821920d582a738a461b76a047f352f18c0b26c6516/tiktoken-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a", size = 993712, upload-time = "2025-10-06T20:22:12.115Z" }, - { url = "https://files.pythonhosted.org/packages/72/90/13da56f664286ffbae9dbcfadcc625439142675845baa62715e49b87b68b/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27", size = 1128725, upload-time = "2025-10-06T20:22:13.541Z" }, - { url = "https://files.pythonhosted.org/packages/05/df/4f80030d44682235bdaecd7346c90f67ae87ec8f3df4a3442cb53834f7e4/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb", size = 1151875, upload-time = "2025-10-06T20:22:14.559Z" }, - { url = "https://files.pythonhosted.org/packages/22/1f/ae535223a8c4ef4c0c1192e3f9b82da660be9eb66b9279e95c99288e9dab/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e", size = 1194451, upload-time = "2025-10-06T20:22:15.545Z" }, - { url = "https://files.pythonhosted.org/packages/78/a7/f8ead382fce0243cb625c4f266e66c27f65ae65ee9e77f59ea1653b6d730/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25", size = 1253794, upload-time = "2025-10-06T20:22:16.624Z" }, - { url = "https://files.pythonhosted.org/packages/93/e0/6cc82a562bc6365785a3ff0af27a2a092d57c47d7a81d9e2295d8c36f011/tiktoken-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f", size = 878777, upload-time = "2025-10-06T20:22:18.036Z" }, - { url = "https://files.pythonhosted.org/packages/72/05/3abc1db5d2c9aadc4d2c76fa5640134e475e58d9fbb82b5c535dc0de9b01/tiktoken-0.12.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a90388128df3b3abeb2bfd1895b0681412a8d7dc644142519e6f0a97c2111646", size = 1050188, upload-time = "2025-10-06T20:22:19.563Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7b/50c2f060412202d6c95f32b20755c7a6273543b125c0985d6fa9465105af/tiktoken-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:da900aa0ad52247d8794e307d6446bd3cdea8e192769b56276695d34d2c9aa88", size = 993978, upload-time = "2025-10-06T20:22:20.702Z" }, - { url = "https://files.pythonhosted.org/packages/14/27/bf795595a2b897e271771cd31cb847d479073497344c637966bdf2853da1/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:285ba9d73ea0d6171e7f9407039a290ca77efcdb026be7769dccc01d2c8d7fff", size = 1129271, upload-time = "2025-10-06T20:22:22.06Z" }, - { url = "https://files.pythonhosted.org/packages/f5/de/9341a6d7a8f1b448573bbf3425fa57669ac58258a667eb48a25dfe916d70/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:d186a5c60c6a0213f04a7a802264083dea1bbde92a2d4c7069e1a56630aef830", size = 1151216, upload-time = "2025-10-06T20:22:23.085Z" }, - { url = "https://files.pythonhosted.org/packages/75/0d/881866647b8d1be4d67cb24e50d0c26f9f807f994aa1510cb9ba2fe5f612/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:604831189bd05480f2b885ecd2d1986dc7686f609de48208ebbbddeea071fc0b", size = 1194860, upload-time = "2025-10-06T20:22:24.602Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1e/b651ec3059474dab649b8d5b69f5c65cd8fcd8918568c1935bd4136c9392/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8f317e8530bb3a222547b85a58583238c8f74fd7a7408305f9f63246d1a0958b", size = 1254567, upload-time = "2025-10-06T20:22:25.671Z" }, - { url = "https://files.pythonhosted.org/packages/80/57/ce64fd16ac390fafde001268c364d559447ba09b509181b2808622420eec/tiktoken-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:399c3dd672a6406719d84442299a490420b458c44d3ae65516302a99675888f3", size = 921067, upload-time = "2025-10-06T20:22:26.753Z" }, - { url = "https://files.pythonhosted.org/packages/ac/a4/72eed53e8976a099539cdd5eb36f241987212c29629d0a52c305173e0a68/tiktoken-0.12.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2c714c72bc00a38ca969dae79e8266ddec999c7ceccd603cc4f0d04ccd76365", size = 1050473, upload-time = "2025-10-06T20:22:27.775Z" }, - { url = "https://files.pythonhosted.org/packages/e6/d7/0110b8f54c008466b19672c615f2168896b83706a6611ba6e47313dbc6e9/tiktoken-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cbb9a3ba275165a2cb0f9a83f5d7025afe6b9d0ab01a22b50f0e74fee2ad253e", size = 993855, upload-time = "2025-10-06T20:22:28.799Z" }, - { url = "https://files.pythonhosted.org/packages/5f/77/4f268c41a3957c418b084dd576ea2fad2e95da0d8e1ab705372892c2ca22/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:dfdfaa5ffff8993a3af94d1125870b1d27aed7cb97aa7eb8c1cefdbc87dbee63", size = 1129022, upload-time = "2025-10-06T20:22:29.981Z" }, - { url = "https://files.pythonhosted.org/packages/4e/2b/fc46c90fe5028bd094cd6ee25a7db321cb91d45dc87531e2bdbb26b4867a/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:584c3ad3d0c74f5269906eb8a659c8bfc6144a52895d9261cdaf90a0ae5f4de0", size = 1150736, upload-time = "2025-10-06T20:22:30.996Z" }, - { url = "https://files.pythonhosted.org/packages/28/c0/3c7a39ff68022ddfd7d93f3337ad90389a342f761c4d71de99a3ccc57857/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:54c891b416a0e36b8e2045b12b33dd66fb34a4fe7965565f1b482da50da3e86a", size = 1194908, upload-time = "2025-10-06T20:22:32.073Z" }, - { url = "https://files.pythonhosted.org/packages/ab/0d/c1ad6f4016a3968c048545f5d9b8ffebf577774b2ede3e2e352553b685fe/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5edb8743b88d5be814b1a8a8854494719080c28faaa1ccbef02e87354fe71ef0", size = 1253706, upload-time = "2025-10-06T20:22:33.385Z" }, - { url = "https://files.pythonhosted.org/packages/af/df/c7891ef9d2712ad774777271d39fdef63941ffba0a9d59b7ad1fd2765e57/tiktoken-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f61c0aea5565ac82e2ec50a05e02a6c44734e91b51c10510b084ea1b8e633a71", size = 920667, upload-time = "2025-10-06T20:22:34.444Z" }, -] - -[[package]] -name = "tqdm" -version = "4.67.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, -] - -[[package]] -name = "typer" -version = "0.21.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/85/30/ff9ede605e3bd086b4dd842499814e128500621f7951ca1e5ce84bbf61b1/typer-0.21.0.tar.gz", hash = "sha256:c87c0d2b6eee3b49c5c64649ec92425492c14488096dfbc8a0c2799b2f6f9c53", size = 106781, upload-time = "2025-12-25T09:54:53.651Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/e4/5ebc1899d31d2b1601b32d21cfb4bba022ae6fce323d365f0448031b1660/typer-0.21.0-py3-none-any.whl", hash = "sha256:c79c01ca6b30af9fd48284058a7056ba0d3bf5cf10d0ff3d0c5b11b68c258ac6", size = 47109, upload-time = "2025-12-25T09:54:51.918Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930, upload-time = "2025-12-11T15:56:40.252Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182, upload-time = "2025-12-11T15:56:38.584Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.40.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, -] - -[[package]] -name = "websockets" -version = "15.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, - { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, - { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, - { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, - { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, - { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, - { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, - { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, - { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, - { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, - { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, - { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, - { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, - { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, - { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, - { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, - { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, -] - -[[package]] -name = "wrapt" -version = "1.17.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998, upload-time = "2025-08-12T05:51:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020, upload-time = "2025-08-12T05:51:35.906Z" }, - { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098, upload-time = "2025-08-12T05:51:57.474Z" }, - { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036, upload-time = "2025-08-12T05:52:34.784Z" }, - { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156, upload-time = "2025-08-12T05:52:13.599Z" }, - { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102, upload-time = "2025-08-12T05:52:14.56Z" }, - { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732, upload-time = "2025-08-12T05:52:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705, upload-time = "2025-08-12T05:53:07.123Z" }, - { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877, upload-time = "2025-08-12T05:53:05.436Z" }, - { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885, upload-time = "2025-08-12T05:52:54.367Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, - { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, - { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, - { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, - { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, - { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, - { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, - { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, - { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, - { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, - { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, - { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, - { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, - { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, - { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, - { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, - { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, - { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, - { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, -] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] diff --git a/pkg/hanzo-tools-api/README.md b/pkg/hanzo-tools-api/README.md deleted file mode 100644 index bd8c34adf..000000000 --- a/pkg/hanzo-tools-api/README.md +++ /dev/null @@ -1,202 +0,0 @@ -# hanzo-tools-api - -Generic API tools for Hanzo MCP: -- `api`: OpenAPI-first generic REST tool -- `hanzo`: Unified Hanzo platform router (`auth`, `billing`, `commerce`, `iam`, `ingress`, `kms`, `mpc`, `paas`, `team`, `api`) - -## Features - -- **Credential Management**: Securely store and manage API keys with env var fallback -- **OpenAPI Support**: Parse OpenAPI 3.x specs to discover and call operations -- **Auto-Detection**: Automatically detects credentials from environment variables -- **Multi-Provider**: Built-in support for 30+ cloud providers -- **Raw Requests**: Make raw HTTP requests when you need more control - -## Installation - -```bash -pip install hanzo-tools-api -``` - -## Quick Start - -### Using Environment Variables (Recommended) - -Set your API keys as environment variables: - -```bash -export CLOUDFLARE_API_TOKEN=your-token -export GITHUB_TOKEN=ghp_xxx -export STRIPE_API_KEY=sk_xxx -``` - -The tool automatically detects these when you make calls. - -### Using the Tool - -```python -from hanzo_tools.api import APITool - -tool = APITool() - -# List available providers and their status -await tool.call(ctx, action="list") - -# Configure a provider manually -await tool.call(ctx, - action="config", - provider="cloudflare", - api_key="your-key" -) - -# Load OpenAPI spec for a provider -await tool.call(ctx, action="spec", provider="cloudflare") - -# List available operations -await tool.call(ctx, action="ops", provider="cloudflare", search="zones") - -# Call an operation -await tool.call(ctx, - action="call", - provider="cloudflare", - operation="listZones" -) - -# Make a raw request -await tool.call(ctx, - action="raw", - provider="github", - method="GET", - path="/user/repos" -) -``` - -### Unified Hanzo Platform Tool - -```python -from hanzo_tools.api import HanzoTool - -tool = HanzoTool() - -# Discover available services -await tool.call(ctx, service="services") - -# Route to service tools through one MCP surface -await tool.call(ctx, service="auth", action="status") -await tool.call(ctx, service="commerce", action="orders") -await tool.call( - ctx, - service="iam", - action="enforce", - args='{"owner":"hanzo","model":"rbac","resource":"/api/users","permission_action":"read"}', -) -``` - -### MCP Tool Usage - -When registered with hanzo-mcp, use like: - -``` -api # List all providers -api --action config --provider github --api_key ghp_xxx -api --action ops --provider cloudflare --search zones -api --action call --provider cloudflare --operation listZones -api --action raw --provider github --method GET --path /user/repos -``` - -## Supported Providers - -Built-in configurations for: - -| Provider | Env Variables | -|----------|---------------| -| Cloudflare | `CLOUDFLARE_API_TOKEN`, `CF_API_TOKEN` | -| GitHub | `GITHUB_TOKEN`, `GH_TOKEN` | -| Stripe | `STRIPE_API_KEY` | -| OpenAI | `OPENAI_API_KEY` | -| Anthropic | `ANTHROPIC_API_KEY` | -| Vercel | `VERCEL_TOKEN` | -| DigitalOcean | `DIGITALOCEAN_TOKEN`, `DO_TOKEN` | -| Fly.io | `FLY_API_TOKEN` | -| Supabase | `SUPABASE_API_KEY` | -| AWS | `AWS_ACCESS_KEY_ID` | -| GCP | `GOOGLE_API_KEY` | -| Azure | `AZURE_API_KEY` | -| Slack | `SLACK_TOKEN` | -| Discord | `DISCORD_TOKEN` | -| ... and more | - -## API Reference - -### Actions - -- **list**: Show all providers and their configuration status -- **config**: Set credentials for a provider -- **delete**: Remove credentials for a provider -- **spec**: Load/refresh OpenAPI spec for a provider -- **ops**: List available operations for a provider -- **call**: Call an API operation by operation ID -- **raw**: Make a raw HTTP request to any endpoint - -### Parameters - -| Parameter | Type | Description | -|-----------|------|-------------| -| `action` | str | Action to perform (default: "list") | -| `provider` | str | Provider name (e.g., "cloudflare") | -| `api_key` | str | API key for config action | -| `api_secret` | str | API secret (if needed) | -| `account_id` | str | Account/org ID | -| `base_url` | str | Override base URL | -| `spec_url` | str | URL to OpenAPI spec | -| `operation` | str | Operation ID for call action | -| `params` | str | JSON parameters | -| `body` | str | JSON request body | -| `method` | str | HTTP method for raw action | -| `path` | str | URL path for raw action | -| `search` | str | Search filter for ops action | -| `tag` | str | Tag filter for ops action | - -## Credential Storage - -Credentials are stored in `~/.hanzo/api/credentials.json` with basic obfuscation. -For production use, consider using system keyring or a secrets manager. - -OpenAPI specs are cached in `~/.hanzo/api/specs/`. - -## Adding Custom Providers - -You can add any provider by providing a base URL and API key: - -```python -from hanzo_tools.api import get_credential_manager - -cred_manager = get_credential_manager() -cred_manager.set_credential( - provider="custom-api", - api_key="your-key", - base_url="https://api.custom.com/v1" -) -``` - -Then load a spec: - -```python -from hanzo_tools.api import get_client - -client = await get_client( - "custom-api", - spec_url="https://api.custom.com/openapi.json" -) -``` - -## Security Notes - -- API keys are stored with basic base64 obfuscation (not encryption) -- Credentials file has restrictive permissions (0600) -- Environment variables are preferred for sensitive credentials -- Never commit credentials to version control - -## License - -MIT diff --git a/pkg/hanzo-tools-api/hanzo_tools/__init__.py b/pkg/hanzo-tools-api/hanzo_tools/__init__.py deleted file mode 100644 index 946984951..000000000 --- a/pkg/hanzo-tools-api/hanzo_tools/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Namespace package -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-api/hanzo_tools/api/__init__.py b/pkg/hanzo-tools-api/hanzo_tools/api/__init__.py deleted file mode 100644 index 39a632ade..000000000 --- a/pkg/hanzo-tools-api/hanzo_tools/api/__init__.py +++ /dev/null @@ -1,166 +0,0 @@ -"""Hanzo API Tools - Generic API tool for calling any REST API. - -This package provides a unified interface for calling any REST API -via OpenAPI specs with automatic credential management. - -Key Features: -- Auto-detection of 30+ cloud provider credentials from environment -- OpenAPI spec parsing with smart caching (ETag support) -- Structured errors with recovery hints -- Pluggable credential storage (memory, file, environment) -- Agent-friendly typed interface - -Quick Start: - from hanzo_tools.api import APIClient - - async with APIClient() as client: - # List providers (auto-detects CLOUDFLARE_API_TOKEN, etc.) - providers = await client.list_providers() - - # Configure if needed - await client.config("cloudflare", api_key="...") - - # Load spec and call operations - await client.spec("cloudflare") - result = await client.call("cloudflare", "listZones") - -MCP Tool Usage: - from hanzo_tools.api import APITool, TOOLS - - # The tool handles everything via actions - tool = APITool() - result = await tool.call(ctx, action="list") - result = await tool.call(ctx, action="call", provider="cloudflare", operation="listZones") -""" - -# Models (structured data types) -# MCP Tool -from .api_tool import APITool - -# Main client (typed interface) -from .client import ( - APIClient, - get_api_client, - reset_api_client, -) - -# Credentials (credential management) -from .credentials import ( - CredentialManager, - get_credential_manager, - reset_credential_manager, -) - -# Errors (structured errors with hints) -from .errors import ( - APIError, - AuthenticationError, - CredentialError, - OperationNotFoundError, - ProviderNotFoundError, - RateLimitError, - SpecLoadError, - SpecNotLoadedError, - ValidationError, -) -from .hanzo_tool import HanzoTool -from .models import ( - APICallResult, - AuthType, - Credential, - CredentialSource, - EffectiveCredential, - Operation, - OperationListResult, - Parameter, - ProviderConfig, - ProviderListResult, - ProviderStatus, - ToolParameter, - ToolSchema, -) - -# OpenAPI (spec parsing and caching) -from .openapi_client import ( - OpenAPIClient, - SpecCache, - clear_clients, - get_client, -) - -# Providers (provider configs and env var mappings) -from .providers import ( - ENV_VAR_MAPPINGS, - PROVIDER_CONFIGS, - get_provider_config, -) -from .providers import ( - list_providers as list_provider_names, -) - -# Storage (pluggable credential storage) -from .storage import ( - ChainedCredentialStorage, - CredentialStorage, - EnvironmentCredentialStorage, - FileCredentialStorage, - MemoryCredentialStorage, -) - -# Tools list for entry point discovery - must be classes, not instances -TOOLS = [HanzoTool, APITool] - -__all__ = [ - # Models - "AuthType", - "Credential", - "CredentialSource", - "EffectiveCredential", - "ProviderConfig", - "ProviderStatus", - "ProviderListResult", - "Operation", - "Parameter", - "OperationListResult", - "APICallResult", - "ToolSchema", - "ToolParameter", - # Errors - "APIError", - "CredentialError", - "OperationNotFoundError", - "ProviderNotFoundError", - "SpecNotLoadedError", - "SpecLoadError", - "AuthenticationError", - "RateLimitError", - "ValidationError", - # Storage - "CredentialStorage", - "MemoryCredentialStorage", - "FileCredentialStorage", - "EnvironmentCredentialStorage", - "ChainedCredentialStorage", - # Providers - "PROVIDER_CONFIGS", - "ENV_VAR_MAPPINGS", - "get_provider_config", - "list_provider_names", - # Credentials - "CredentialManager", - "get_credential_manager", - "reset_credential_manager", - # OpenAPI - "OpenAPIClient", - "SpecCache", - "get_client", - "clear_clients", - # Client - "APIClient", - "get_api_client", - "reset_api_client", - # Tool - "HanzoTool", - "APITool", - "TOOLS", -] diff --git a/pkg/hanzo-tools-api/hanzo_tools/api/api_tool.py b/pkg/hanzo-tools-api/hanzo_tools/api/api_tool.py deleted file mode 100644 index 5b1e65067..000000000 --- a/pkg/hanzo-tools-api/hanzo_tools/api/api_tool.py +++ /dev/null @@ -1,668 +0,0 @@ -"""Unified API tool for calling any REST API. - -This is a thin MCP wrapper over APIClient. The real logic lives in client.py. - -Provides a single tool interface for: -- Managing API credentials -- Loading OpenAPI specs -- Calling API operations -- Making raw HTTP requests -""" - -import json -import logging -from typing import Annotated, final, override - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext -from pydantic import Field - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context - -from .client import APIClient, get_api_client -from .errors import APIError -from .models import APICallResult, OperationListResult, ProviderListResult - -logger = logging.getLogger(__name__) - - -def _format_provider_list(result: ProviderListResult) -> str: - """Format provider list for display.""" - lines = ["=== API Providers ===", ""] - - configured = [p for p in result.providers if p.configured] - unconfigured = [p for p in result.providers if not p.configured] - - if configured: - lines.append("Configured:") - for p in sorted(configured, key=lambda x: x.name): - source = f"[{p.source.value}]" if p.source else "" - lines.append(f" {p.name}: {p.display_name} {source}") - - if unconfigured: - lines.append("\nAvailable (not configured):") - for p in sorted(unconfigured, key=lambda x: x.name)[:20]: # Limit display - lines.append(f" {p.name}: {p.display_name}") - if len(unconfigured) > 20: - lines.append(f" ... and {len(unconfigured) - 20} more") - - lines.append("") - lines.append( - f"Total: {result.total_count} providers, {result.configured_count} configured" - ) - lines.append("") - lines.append("Use 'api --provider ' for details") - lines.append( - "Use 'api --action config --provider --api_key ' to configure" - ) - - return "\n".join(lines) - - -def _format_operation_list(result: OperationListResult, provider: str) -> str: - """Format operation list for display.""" - if not result.operations: - return f"No operations found for {provider}. Try loading the spec first." - - lines = [f"=== {provider} Operations ==="] - - # Group by tag - tags: dict[str, list] = {} - for op in result.operations: - for t in op.tags or ["untagged"]: - if t not in tags: - tags[t] = [] - tags[t].append(op) - - for tag_name, tag_ops in sorted(tags.items()): - lines.append(f"\n[{tag_name}]") - for op in tag_ops[:10]: # Limit per tag - lines.append(f" {op.operation_id}: {op.method} {op.path}") - if op.summary: - lines.append(f" {op.summary[:80]}") - if len(tag_ops) > 10: - lines.append(f" ... and {len(tag_ops) - 10} more") - - lines.append(f"\nTotal: {result.total_count} operations") - if tags: - lines.append(f"Tags: {', '.join(sorted(tags.keys()))}") - - return "\n".join(lines) - - -def _format_api_result(result: APICallResult) -> str: - """Format API call result for display.""" - lines = [f"Status: {result.status_code} {'โœ“' if result.success else 'โœ—'}"] - - if result.body is not None: - if isinstance(result.body, dict): - formatted = json.dumps(result.body, indent=2) - else: - formatted = str(result.body) - - # Truncate if too long - if len(formatted) > 5000: - formatted = formatted[:5000] + "\n... (truncated)" - - lines.append("") - lines.append(formatted) - - return "\n".join(lines) - - -@final -class APITool(BaseTool): - """Unified tool for calling any REST API. - - This is a thin wrapper over APIClient. See client.py for the real logic. - - Actions: - - list: List available/configured providers - - config: Configure credentials for a provider - - delete: Remove stored credentials - - spec: Load/refresh OpenAPI spec - - ops: List available operations for a provider - - call: Call an API operation - - raw: Make a raw HTTP request - """ - - name = "api" - - def __init__(self, client: APIClient | None = None): - """Initialize with optional client.""" - self._client = client - - @property - def client(self) -> APIClient: - """Get the API client (lazy initialization).""" - if self._client is None: - self._client = get_api_client() - return self._client - - @property - @override - def description(self) -> str: - return """Generic API tool for calling any REST API via OpenAPI specs. - -Supports searching, exploring, and dynamically using ANY public API. - -Actions: -- list: Show all providers and their configuration status -- config: Set credentials for a provider -- delete: Remove credentials for a provider -- spec: Load/refresh OpenAPI spec for a provider -- ops: List available operations for a provider -- call: Call an API operation by ID -- raw: Make a raw HTTP request to any endpoint -- search: Search for APIs in public registries (openapisearch.com) -- register: Register a custom API from any OpenAPI spec URL -- overview: Get agent-friendly overview of an API -- preload: Download and cache common OpenAPI specs - -DISCOVER ANY API: - api --action search --search notion - api --action register --provider notion --spec_url - api --action overview --provider notion - api --action call --provider notion --operation - -BUILT-IN PROVIDERS: -Auto-detects credentials from environment variables: -- Cloudflare: CLOUDFLARE_API_TOKEN, CF_API_TOKEN, CLOUDFLARE_API_KEY -- GitHub: GITHUB_TOKEN, GH_TOKEN -- Stripe: STRIPE_API_KEY, STRIPE_SECRET_KEY -- OpenAI: OPENAI_API_KEY -- Anthropic: ANTHROPIC_API_KEY -- etc. (26+ providers) - -Configure manually: - api --action config --provider cloudflare --api_key "your-key" - -MAKING CALLS: - api --action call --provider cloudflare --operation zones-get - api --action raw --provider github --method GET --path /user - -Examples: - api # List all providers - api --action search --search weather # Search for weather APIs - api --action register --provider petstore --spec_url https://petstore.swagger.io/v2/swagger.json - api --action overview --provider cloudflare # Agent-friendly summary - api --action ops --provider cloudflare --search zones - api --action call --provider cloudflare --operation zones-get -""" - - @override - @auto_timeout("api") - async def call( - self, - ctx: MCPContext, - action: str = "list", - provider: str | None = None, - # Config params - api_key: str | None = None, - api_secret: str | None = None, - account_id: str | None = None, - base_url: str | None = None, - # Spec params - spec_url: str | None = None, - force_refresh: bool = False, - # Operation params - operation: str | None = None, - params: str | None = None, # JSON string - body: str | None = None, # JSON string - # Raw call params - method: str = "GET", - path: str | None = None, - # List/search params - search: str | None = None, - tag: str | None = None, - configured_only: bool = False, - **kwargs, - ) -> str: - """Execute API action.""" - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - try: - if action == "list": - return await self._handle_list(provider, configured_only) - - elif action == "config": - return await self._handle_config( - provider, api_key, api_secret, account_id, base_url - ) - - elif action == "delete": - return await self._handle_delete(provider) - - elif action == "spec": - return await self._handle_spec(provider, spec_url, force_refresh) - - elif action == "ops": - return await self._handle_ops(provider, search, tag) - - elif action == "call": - return await self._handle_call(provider, operation, params, body) - - elif action == "raw": - return await self._handle_raw(provider, method, path, params, body) - - elif action == "search": - return await self._handle_search(search) - - elif action == "register": - return await self._handle_register( - provider, - spec_url, - base_url, - api_key, - kwargs.get("auth_type", "bearer"), - ) - - elif action == "overview": - return await self._handle_overview(provider) - - elif action == "preload": - return await self._handle_preload() - - else: - return f"Unknown action: {action}. Valid: list, config, delete, spec, ops, call, raw, search, register, overview, preload" - - except APIError as e: - # Structured error with hints - return str(e) - except Exception as e: - logger.exception(f"API tool error: {e}") - return f"Error: {e}" - - async def _handle_list(self, provider: str | None, configured_only: bool) -> str: - """List providers and their status.""" - if provider: - # Show details for specific provider - try: - status = await self.client.get_provider(provider) - lines = [f"=== {provider} ==="] - lines.append(f"Display name: {status.display_name}") - lines.append(f"Configured: {status.configured}") - if status.source: - lines.append(f"Source: {status.source.value}") - if status.base_url: - lines.append(f"Base URL: {status.base_url}") - if status.auth_type: - lines.append(f"Auth type: {status.auth_type}") - if status.spec_url: - lines.append(f"Spec URL: {status.spec_url}") - if status.env_vars: - lines.append(f"Env vars: {', '.join(status.env_vars)}") - return "\n".join(lines) - except Exception as e: - return f"Error getting provider info: {e}" - - # List all providers - result = await self.client.list_providers(configured_only=configured_only) - return _format_provider_list(result) - - async def _handle_config( - self, - provider: str | None, - api_key: str | None, - api_secret: str | None, - account_id: str | None, - base_url: str | None, - ) -> str: - """Configure credentials for a provider.""" - if not provider: - return "Error: --provider required for config action" - - if not api_key: - return "Error: --api_key required for config action" - - await self.client.config( - provider=provider, - api_key=api_key, - api_secret=api_secret, - account_id=account_id, - base_url=base_url, - ) - - return f"Configured credentials for {provider}" - - async def _handle_delete(self, provider: str | None) -> str: - """Delete credentials for a provider.""" - if not provider: - return "Error: --provider required for delete action" - - if await self.client.delete_config(provider): - return f"Deleted credentials for {provider}" - return f"No stored credentials for {provider}" - - async def _handle_spec( - self, - provider: str | None, - spec_url: str | None, - force_refresh: bool, - ) -> str: - """Load or refresh OpenAPI spec.""" - if not provider: - return "Error: --provider required for spec action" - - try: - count = await self.client.spec( - provider=provider, - spec_url=spec_url, - force_refresh=force_refresh, - ) - return f"Loaded spec for {provider}: {count} operations" - except Exception as e: - return f"Error loading spec: {e}" - - async def _handle_ops( - self, - provider: str | None, - search: str | None, - tag: str | None, - ) -> str: - """List operations for a provider.""" - if not provider: - return "Error: --provider required for ops action" - - try: - result = await self.client.ops( - provider=provider, - search=search, - tag=tag, - ) - return _format_operation_list(result, provider) - except Exception as e: - return f"Error listing operations: {e}" - - async def _handle_call( - self, - provider: str | None, - operation: str | None, - params: str | None, - body: str | None, - ) -> str: - """Call an API operation.""" - if not provider: - return "Error: --provider required for call action" - if not operation: - return "Error: --operation required for call action" - - try: - # Parse params and body - parsed_params = json.loads(params) if params else None - parsed_body = json.loads(body) if body else None - - result = await self.client.call( - provider=provider, - operation_id=operation, - params=parsed_params, - body=parsed_body, - ) - - return _format_api_result(result) - - except json.JSONDecodeError as e: - return f"Error parsing JSON: {e}" - except Exception as e: - return f"Error calling operation: {e}" - - async def _handle_raw( - self, - provider: str | None, - method: str, - path: str | None, - params: str | None, - body: str | None, - ) -> str: - """Make a raw API request.""" - if not provider: - return "Error: --provider required for raw action" - if not path: - return "Error: --path required for raw action" - - try: - # Parse params and body - parsed_params = json.loads(params) if params else None - parsed_body = json.loads(body) if body else None - - result = await self.client.raw( - provider=provider, - method=method, - path=path, - params=parsed_params, - body=parsed_body, - ) - - return _format_api_result(result) - - except json.JSONDecodeError as e: - return f"Error parsing JSON: {e}" - except Exception as e: - return f"Error making request: {e}" - - async def _handle_search(self, query: str | None) -> str: - """Search for APIs in public registries.""" - if not query: - return "Error: --search required for search action" - - try: - results = await self.client.search(query) - - if not results: - return f"No APIs found matching '{query}'" - - lines = [f"=== API Search: {query} ===", ""] - for r in results[:20]: # Limit results - name = r.get("name") or r.get("id", "Unknown") - desc = r.get("description", "")[:60] - spec_url = r.get("spec_url", "") - lines.append(f" {r.get('id', name)}: {name}") - if desc: - lines.append(f" {desc}") - if spec_url: - lines.append(f" Spec: {spec_url}") - lines.append("") - - lines.append(f"Found {len(results)} API(s)") - lines.append("") - lines.append("To use an API:") - lines.append(" api --action register --provider --spec_url ") - - return "\n".join(lines) - - except Exception as e: - return f"Error searching: {e}" - - async def _handle_register( - self, - name: str | None, - spec_url: str | None, - base_url: str | None, - api_key: str | None, - auth_type: str, - ) -> str: - """Register a custom API provider.""" - if not name: - return "Error: --provider required for register action" - - if not spec_url: - return "Error: --spec_url required for register action" - - try: - count = await self.client.register( - name=name, - spec_url=spec_url, - base_url=base_url, - api_key=api_key, - auth_type=auth_type, - ) - - lines = [ - f"Registered API: {name}", - f"Loaded {count} operations from spec", - "", - "Next steps:", - f" api --action overview --provider {name}", - f" api --action ops --provider {name}", - ] - - if not api_key: - lines.append( - f" api --action config --provider {name} --api_key " - ) - - return "\n".join(lines) - - except Exception as e: - return f"Error registering API: {e}" - - async def _handle_overview(self, provider: str | None) -> str: - """Get API overview.""" - if not provider: - return "Error: --provider required for overview action" - - try: - return await self.client.overview(provider) - except Exception as e: - return f"Error getting overview: {e}" - - async def _handle_preload(self) -> str: - """Preload and cache common OpenAPI specs.""" - try: - results = await self.client.preload_specs() - - lines = ["=== Preloading OpenAPI Specs ===", ""] - - success = [] - failed = [] - for name, count in sorted(results.items()): - if count >= 0: - success.append(f" {name}: {count} operations") - else: - failed.append(f" {name}: failed") - - if success: - lines.append("Loaded:") - lines.extend(success) - - if failed: - lines.append("\nFailed:") - lines.extend(failed) - - lines.append(f"\nTotal: {len(success)} loaded, {len(failed)} failed") - lines.append("Cache: ~/.hanzo/api/specs/") - - return "\n".join(lines) - - except Exception as e: - return f"Error preloading specs: {e}" - - def register(self, mcp_server: FastMCP) -> None: - """Register with MCP server.""" - tool_instance = self - - @mcp_server.tool() - async def api( - action: Annotated[ - str, - Field( - description="Action: list, config, delete, spec, ops, call, raw", - default="list", - ), - ] = "list", - provider: Annotated[ - str | None, - Field(description="Provider name (e.g., cloudflare, github)"), - ] = None, - api_key: Annotated[ - str | None, - Field(description="API key or token for config action"), - ] = None, - api_secret: Annotated[ - str | None, - Field(description="API secret (if needed) for config action"), - ] = None, - account_id: Annotated[ - str | None, - Field(description="Account/org ID for config action"), - ] = None, - base_url: Annotated[ - str | None, - Field(description="Override base URL for provider"), - ] = None, - spec_url: Annotated[ - str | None, - Field(description="URL to OpenAPI spec for spec action"), - ] = None, - force_refresh: Annotated[ - bool, - Field(description="Force refresh cached spec"), - ] = False, - operation: Annotated[ - str | None, - Field(description="Operation ID for call action"), - ] = None, - params: Annotated[ - str | None, - Field(description="JSON parameters for call/raw action"), - ] = None, - body: Annotated[ - str | None, - Field(description="JSON body for call/raw action"), - ] = None, - method: Annotated[ - str, - Field(description="HTTP method for raw action"), - ] = "GET", - path: Annotated[ - str | None, - Field(description="URL path for raw action"), - ] = None, - search: Annotated[ - str | None, - Field(description="Search filter for ops action"), - ] = None, - tag: Annotated[ - str | None, - Field(description="Tag filter for ops action"), - ] = None, - configured_only: Annotated[ - bool, - Field(description="Only list configured providers"), - ] = False, - ctx: MCPContext = None, - ) -> str: - """Generic API tool for calling any REST API via OpenAPI specs. - - Manage credentials and call APIs for various cloud providers. - Auto-detects credentials from environment variables. - - Actions: - - list: Show all providers and their status - - config: Set credentials for a provider - - delete: Remove credentials for a provider - - spec: Load/refresh OpenAPI spec for a provider - - ops: List available operations for a provider - - call: Call an API operation by operation ID - - raw: Make a raw HTTP request - """ - return await tool_instance.call( - ctx, - action=action, - provider=provider, - api_key=api_key, - api_secret=api_secret, - account_id=account_id, - base_url=base_url, - spec_url=spec_url, - force_refresh=force_refresh, - operation=operation, - params=params, - body=body, - method=method, - path=path, - search=search, - tag=tag, - configured_only=configured_only, - ) diff --git a/pkg/hanzo-tools-api/hanzo_tools/api/apis_guru_providers.py b/pkg/hanzo-tools-api/hanzo_tools/api/apis_guru_providers.py deleted file mode 100644 index 42fd285db..000000000 --- a/pkg/hanzo-tools-api/hanzo_tools/api/apis_guru_providers.py +++ /dev/null @@ -1,8028 +0,0 @@ -"""Auto-generated provider configurations from APIs.guru + oapis.org. - -Generated 1116 provider configurations. -Includes LLM-optimized descriptions from oapis.org for popular APIs. - -Regenerate with: python scripts/generate_providers.py > hanzo_tools/api/apis_guru_providers.py -""" - -from typing import Any - -# ============================================================================= -# APIs.guru Provider Configurations (with oapis.org enhancements) -# ============================================================================= - -APIS_GURU_PROVIDERS: dict[str, dict[str, Any]] = { - "1forge": { - "display_name": "1Forge Finance APIs", - "description": "Stock and Forex Data and Realtime Quotes", - "spec_url": "https://api.apis.guru/v2/specs/1forge.com/0.0.1/swagger.json", - "base_url": "http://1forge.com/openapi.json", - "env_vars": ["1FORGE_API_KEY", "1FORGE_TOKEN"], - }, - "1password-com-events": { - "display_name": "Events API", - "description": "1Password Events API Specification.", - "spec_url": "https://api.apis.guru/v2/specs/1password.com/events/1.0.0/openapi.json", - "base_url": "https://i.1password.com/media/1password-events-reporting/1password-events-api.yaml", - "env_vars": ["1PASSWORD_COM_EVENTS_API_KEY", "1PASSWORD_COM_EVENTS_TOKEN"], - }, - "1password-local-connect": { - "display_name": "1Password Connect", - "description": "REST API interface for 1Password Connect.", - "spec_url": "https://api.apis.guru/v2/specs/1password.local/connect/1.5.7/openapi.json", - "base_url": "https://i.1password.com/media/1password-connect/1password-connect-api.yaml", - "env_vars": [ - "1PASSWORD_LOCAL_CONNECT_API_KEY", - "1PASSWORD_LOCAL_CONNECT_TOKEN", - ], - }, - "6-dot-authentiqio-appspot": { - "display_name": "Authentiq API", - "description": "Strong authentication, without the passwords.", - "spec_url": "https://api.apis.guru/v2/specs/6-dot-authentiqio.appspot.com/6/openapi.json", - "base_url": "https://raw.githubusercontent.com/AuthentiqID/authentiq-docs/master/docs/swagger/issuer.yaml", - "env_vars": [ - "6_DOT_AUTHENTIQIO_APPSPOT_API_KEY", - "6_DOT_AUTHENTIQIO_APPSPOT_TOKEN", - ], - }, - "ably-io-platform": { - "display_name": "Platform API", - "description": "The [REST API specification](https://www.ably.io/documentation/rest-api) for Ably.", - "spec_url": "https://api.apis.guru/v2/specs/ably.io/platform/1.1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/ably/open-specs/main/definitions/platform-v1.yaml", - "env_vars": ["ABLY_API_KEY"], - }, - "ably-net-control": { - "display_name": "Control API v1", - "description": "Use the Control API to manage your applications, namespaces, keys, queues, rules, and more. Detailed information on using this API can be found in the", - "spec_url": "https://api.apis.guru/v2/specs/ably.net/control/1.0.14/openapi.json", - "base_url": "https://raw.githubusercontent.com/ably/open-specs/main/definitions/control-v1.yaml", - "env_vars": ["ABLY_API_KEY"], - }, - "abstractapi-com-geolocation": { - "display_name": "IP geolocation API", - "description": "Abstract IP geolocation API allows developers to retrieve the region, country and city behind any IP worldwide. The API covers the geolocation of IPv4", - "spec_url": "https://api.apis.guru/v2/specs/abstractapi.com/geolocation/1.0.0/openapi.json", - "base_url": "https://documentation.abstractapi.com/ip-geolocation-openapi.json", - "env_vars": [ - "ABSTRACTAPI_COM_GEOLOCATION_API_KEY", - "ABSTRACTAPI_COM_GEOLOCATION_TOKEN", - ], - }, - "adafruit": { - "display_name": "Adafruit IO REST API", - "description": "### The Internet of Things for Everyone The Adafruit IO HTTP API provides access to your Adafruit IO data from any programming language or hardware en", - "spec_url": "https://api.apis.guru/v2/specs/adafruit.com/2.0.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/adafruit/io-api/gh-pages/v2.json", - "env_vars": ["ADAFRUIT_API_KEY", "ADAFRUIT_TOKEN"], - }, - "adobe-com-aem": { - "display_name": "Adobe Experience Manager (AEM) API", - "description": "Swagger AEM is an OpenAPI specification for Adobe Experience Manager (AEM) API", - "spec_url": "https://api.apis.guru/v2/specs/adobe.com/aem/3.7.1-pre.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/shinesolutions/swagger-aem/master/conf/api.yml", - "env_vars": ["ADOBE_COM_AEM_API_KEY", "ADOBE_COM_AEM_TOKEN"], - }, - "adyen-com-accountservice": { - "display_name": "Account API", - "description": "This API is used for the classic integration. If you are just starting your implementation, refer to our [new integration guide](https://docs.adyen.co", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/AccountService/6/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/AccountService-v6.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-balancecontrolservice": { - "display_name": "Adyen Balance Control API", - "description": "The Balance Control API lets you transfer funds between merchant accounts that belong to the same legal entity and are under the same company account.", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/BalanceControlService/1/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/BalanceControlService-v1.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-balanceplatformconfigurationnotification-v1": { - "display_name": "Configuration webhooks", - "description": "Adyen sends notifications through webhooks to inform your system about events that occur in your platform. These events include, for example, when an ", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/BalancePlatformConfigurationNotification-v1/1/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/BalancePlatformConfigurationNotification-v1.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-balanceplatformpaymentnotification-v1": { - "display_name": "Payment webhooks (deprecated)", - "description": "The payment webhooks are deprecated. Use the [accounting webhooks](https://docs.adyen.com/api-explorer/transfer-webhooks/latest/overview) instead. Ady", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/BalancePlatformPaymentNotification-v1/1/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/BalancePlatformPaymentNotification-v1.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-balanceplatformreportnotification-v1": { - "display_name": "Report webhooks", - "description": "Adyen sends notifications through webhooks to inform your system that reports were generated and are ready to be downloaded. You can download reports ", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/BalancePlatformReportNotification-v1/1/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/BalancePlatformReportNotification-v1.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-balanceplatformservice": { - "display_name": "Configuration API", - "description": "The Configuration API enables you to create a platform where you can onboard your users as account holders and create balance accounts, cards, and bus", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/BalancePlatformService/2/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/BalancePlatformService-v2.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-balanceplatformtransfernotification-v3": { - "display_name": "Transfer webhooks", - "description": "Adyen sends notifications through webhooks to inform your system about incoming and outgoing transfers in your platform. You can use these webhooks to", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/BalancePlatformTransferNotification-v3/3/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/BalancePlatformTransferNotification-v3.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-binlookupservice": { - "display_name": "Adyen BinLookup API", - "description": "The BIN Lookup API provides endpoints for retrieving information, such as cost estimates, and 3D Secure supported version based on a given BIN. ## Aut", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/BinLookupService/54/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/BinLookupService-v54.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-checkoutservice": { - "display_name": "Adyen Checkout API", - "description": "Adyen Checkout API provides a simple and flexible way to initiate and authorise online payments. You can use the same integration for payments made wi", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/CheckoutService/70/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/CheckoutService-v70.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-checkoututilityservice": { - "display_name": "Adyen Checkout Utility Service", - "description": "A web service containing utility functions available for merchants integrating with Checkout APIs. ## Authentication Each request to the Checkout Util", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/CheckoutUtilityService/1/openapi.json", - "base_url": "https://raw.githubusercontent.com/adyen/adyen-openapi/master/specs/3.0/CheckoutUtilityService-v1.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-dataprotectionservice": { - "display_name": "Adyen Data Protection API", - "description": "Adyen Data Protection API provides a way for you to process [Subject Erasure Requests](https://gdpr-info.eu/art-17-gdpr/) as mandated in GDPR. Use our", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/DataProtectionService/1/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/DataProtectionService-v1.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-fundservice": { - "display_name": "Fund API", - "description": "This API is used for the classic integration. If you are just starting your implementation, refer to our [new integration guide](https://docs.adyen.co", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/FundService/6/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/FundService-v6.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-hopservice": { - "display_name": "Hosted onboarding API", - "description": "This API is used for the classic integration. If you are just starting your implementation, refer to our [new integration guide](https://docs.adyen.co", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/HopService/6/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/HopService-v6.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-legalentityservice": { - "display_name": "Legal Entity Management API", - "description": "The Legal Entity Management API enables you to manage legal entities that contain information required for verification. ## Authentication To connect ", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/LegalEntityService/3/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/LegalEntityService-v3.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-managementnotificationservice-v1": { - "display_name": "Management Webhooks", - "description": "Adyen uses webhooks to inform your system about events that happen with your Adyen company and merchant accounts, stores, payment terminals, and payme", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/ManagementNotificationService-v1/1/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/ManagementNotificationService-v1.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-managementservice": { - "display_name": "Management API", - "description": "Configure and manage your Adyen company and merchant accounts, stores, and payment terminals. ## Authentication Each request to the Management API mus", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/ManagementService/1/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/ManagementService-v1.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-marketpaynotificationservice": { - "display_name": "Classic Platforms - Notifications", - "description": "This API is used for the classic integration. If you are just starting your implementation, refer to our [new integration guide](https://docs.adyen.co", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/MarketPayNotificationService/6/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/master/json/MarketPayNotificationService-v6.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-notificationconfigurationservice": { - "display_name": "Notification Configuration API", - "description": "This API is used for the classic integration. If you are just starting your implementation, refer to our [new integration guide](https://docs.adyen.co", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/NotificationConfigurationService/6/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/NotificationConfigurationService-v6.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-paymentservice": { - "display_name": "Adyen Payment API", - "description": "A set of API endpoints that allow you to initiate, settle, and modify payments on the Adyen payments platform. You can use the API to accept card paym", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/PaymentService/68/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/PaymentService-v68.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-payoutservice": { - "display_name": "Adyen Payout API", - "description": "A set of API endpoints that allow you to store payout details, confirm, or decline a payout. For more information, refer to [Online payouts](https://d", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/PayoutService/68/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/PayoutService-v68.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-recurringservice": { - "display_name": "Adyen Recurring API", - "description": "The Recurring APIs allow you to manage and remove your tokens or saved payment details. Tokens should be created with validation during a payment requ", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/RecurringService/68/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/RecurringService-v68.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-storedvalueservice": { - "display_name": "Adyen Stored Value API", - "description": "A set of API endpoints to manage stored value products.", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/StoredValueService/46/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/StoredValueService-v46.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-testcardservice": { - "display_name": "Adyen Test Cards API", - "description": "The Test Cards API provides endpoints for generating custom test card numbers. For more information, refer to [Custom test cards](https://docs.adyen.c", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/TestCardService/1/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/TestCardService-v1.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-tfmapiservice": { - "display_name": "POS Terminal Management API", - "description": "This API provides endpoints for managing your point-of-sale (POS) payment terminals. You can use the API to obtain information about a specific termin", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/TfmAPIService/1/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/TfmAPIService-v1.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "adyen-com-transferservice": { - "display_name": "Transfers API", - "description": "The Transfers API provides endpoints that you can use to get information about all your transactions, move funds within your balance platform or send ", - "spec_url": "https://api.apis.guru/v2/specs/adyen.com/TransferService/3/openapi.json", - "base_url": "https://raw.githubusercontent.com/Adyen/adyen-openapi/main/json/TransferService-v3.json", - "env_vars": ["ADYEN_API_KEY"], - }, - "afterbanks": { - "display_name": "Afterbanks API", - "description": "La estandarizaciรณn de la conexiรณn con cualquier banco en tiempo real.", - "spec_url": "https://api.apis.guru/v2/specs/afterbanks.com/3.0.0/swagger.json", - "base_url": "https://www.afterbanks.com/api/documentation/es/swagger.yaml", - "env_vars": ["AFTERBANKS_API_KEY", "AFTERBANKS_TOKEN"], - }, - "agco-ats": { - "display_name": "AGCO API", - "spec_url": "https://api.apis.guru/v2/specs/agco-ats.com/v1/openapi.json", - "base_url": "https://secure.agco-ats.com:443/swagger/docs/v1", - "env_vars": ["AGCO_ATS_API_KEY", "AGCO_ATS_TOKEN"], - }, - "aiception": { - "display_name": "AIception Interactive", - "description": "Here you can play & test & prototype all the endpoints using just your browser! Go ahead!", - "spec_url": "https://api.apis.guru/v2/specs/aiception.com/1.0.0/swagger.json", - "base_url": "https://aiception.com/static/swagger.json", - "env_vars": ["AICEPTION_API_KEY", "AICEPTION_TOKEN"], - }, - "airbyte-local-config": { - "display_name": "Airbyte Configuration API", - "description": "Airbyte Configuration API [https://airbyte.io](https://airbyte.io). This API is a collection of HTTP RPC-style methods. While it is not a REST API, th", - "spec_url": "https://api.apis.guru/v2/specs/airbyte.local/config/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/airbytehq/airbyte/master/airbyte-api/src/main/openapi/config.yaml", - "env_vars": ["AIRBYTE_LOCAL_CONFIG_API_KEY", "AIRBYTE_LOCAL_CONFIG_TOKEN"], - }, - "airport-web-appspot": { - "display_name": "airportsapi", - "description": "Get name and website-URL for airports by ICAO code. Covered airports are mostly in Germany.", - "spec_url": "https://api.apis.guru/v2/specs/airport-web.appspot.com/v1/swagger.json", - "base_url": "https://airport-web.appspot.com/api/docs/swagger.json", - "env_vars": ["AIRPORT_WEB_APPSPOT_API_KEY", "AIRPORT_WEB_APPSPOT_TOKEN"], - }, - "akeneo": { - "display_name": "Akeneo PIM REST API", - "spec_url": "https://api.apis.guru/v2/specs/akeneo.com/1.0.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/akeneo/pim-api-docs/master/content/swagger/akeneo-web-api.json", - "env_vars": ["AKENEO_API_KEY", "AKENEO_TOKEN"], - }, - "alertersystem": { - "display_name": "Alerter System API", - "description": '

This is the Alerter System API playground. More documentation is available at the API Help Centergeoid is the shape the ocean surface would ", - "spec_url": "https://api.apis.guru/v2/specs/amentum.space/gravity/1.1.1/openapi.json", - "base_url": "https://gravity.amentum.space//openapi.json", - "env_vars": ["AMENTUM_SPACE_GRAVITY_API_KEY", "AMENTUM_SPACE_GRAVITY_TOKEN"], - }, - "amentum-space-spaceradiation": { - "display_name": "Space Radiation API", - "description": "Space has a hostile radiation environment that increases the risk of cancers in humans and malfunctions in spacecraft electronics. The types of space ", - "spec_url": "https://api.apis.guru/v2/specs/amentum.space/space_radiation/1.1.2/openapi.json", - "base_url": "https://spaceradiation.amentum.space/openapi.json", - "env_vars": [ - "AMENTUM_SPACE_SPACERADIATION_API_KEY", - "AMENTUM_SPACE_SPACERADIATION_TOKEN", - ], - }, - "anchore": { - "display_name": "Anchore Engine API Server", - "description": "This is the Anchore Engine API. Provides the primary external API for users of the service.", - "spec_url": "https://api.apis.guru/v2/specs/anchore.io/0.1.20/openapi.json", - "base_url": "https://raw.githubusercontent.com/anchore/anchore-engine/master/anchore_engine/services/apiext/swagger/swagger.yaml", - "env_vars": ["ANCHORE_API_KEY", "ANCHORE_TOKEN"], - }, - "apache": { - "display_name": "Airflow API (Stable)", - "description": "# Overview To facilitate management, Apache Airflow supports a range of REST API endpoints across its objects. This section provides an overview of th", - "spec_url": "https://api.apis.guru/v2/specs/apache.org/2.5.1/openapi.json", - "base_url": "https://airflow.apache.org/docs/apache-airflow/stable/_specs/v1.yaml", - "env_vars": ["APACHE_API_KEY", "APACHE_TOKEN"], - }, - "apache-org-airflow": { - "display_name": "Airflow API (Stable)", - "description": "# Overview To facilitate management, Apache Airflow supports a range of REST API endpoints across its objects. This section provides an overview of th", - "spec_url": "https://api.apis.guru/v2/specs/apache.org/airflow/2.5.1/openapi.json", - "base_url": "https://airflow.apache.org/docs/apache-airflow/stable/_specs/v1.yaml", - "env_vars": ["APACHE_ORG_AIRFLOW_API_KEY", "APACHE_ORG_AIRFLOW_TOKEN"], - }, - "apache-org-qakka": { - "display_name": "Qakka", - "description": "API for Qakka Queue System", - "spec_url": "https://api.apis.guru/v2/specs/apache.org/qakka/v1/openapi.json", - "base_url": "https://raw.githubusercontent.com/apache/usergrid-qakka/master/docs/swagger.json", - "env_vars": ["APACHE_ORG_QAKKA_API_KEY", "APACHE_ORG_QAKKA_TOKEN"], - }, - "apacta": { - "display_name": "Apacta", - "description": "API for a tool to craftsmen used to register working hours, material usage and quality assurance. # Endpoint The endpoint `https://app.apacta.com/api/", - "spec_url": "https://api.apis.guru/v2/specs/apacta.com/0.0.42/openapi.json", - "base_url": "http://apidoc.apacta.com/swagger.yaml", - "env_vars": ["APACTA_API_KEY", "APACTA_TOKEN"], - }, - "api-ebay-com-sell-account": { - "display_name": "Account API", - "description": "The Account API gives sellers the ability to configure their eBay seller accounts, including the seller's policies (eBay business policies and ", - "spec_url": "https://api.apis.guru/v2/specs/api.ebay.com/sell-account/v1.9.0/openapi.json", - "base_url": "https://developer.ebay.com/api-docs/master/sell/account/openapi/3/sell_account_v1_oas3.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "api-ebay-com-sell-analytics": { - "display_name": "Seller Service Metrics API", - "description": "The Analytics API provides data and information about a seller and their eBay business.

The resources and methods in this API let selle", - "spec_url": "https://api.apis.guru/v2/specs/api.ebay.com/sell-analytics/1.2.0/openapi.json", - "base_url": "https://developer.ebay.com/api-docs/master/sell/analytics/openapi/3/sell_analytics_v1_oas3.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "api-ebay-com-sell-compliance": { - "display_name": "Compliance API", - "description": "Service for providing information to sellers about their listings being non-compliant, or at risk for becoming non-compliant, against eBay listing pol", - "spec_url": "https://api.apis.guru/v2/specs/api.ebay.com/sell-compliance/1.4.1/openapi.json", - "base_url": "https://developer.ebay.com/api-docs/master/sell/compliance/openapi/3/sell_compliance_v1_oas3.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "api-gov-uk-vehicle-enquiry": { - "display_name": "Vehicle Enquiry API", - "description": "Interface specification for the DVLA Vehicle Enquiry API", - "spec_url": "https://api.apis.guru/v2/specs/api.gov.uk/vehicle-enquiry/1.1.0/openapi.json", - "base_url": "https://developer-portal.driver-vehicle-licensing.api.gov.uk/apis/vehicle-enquiry-service/v1.1.0-vehicle-enquiry-service.json", - "env_vars": [ - "API_GOV_UK_VEHICLE_ENQUIRY_API_KEY", - "API_GOV_UK_VEHICLE_ENQUIRY_TOKEN", - ], - }, - "api-video": { - "display_name": "api.video", - "description": "api.video is an API that encodes on the go to facilitate immediate playback, enhancing viewer streaming experiences across multiple devices and platfo", - "spec_url": "https://api.apis.guru/v2/specs/api.video/1/openapi.json", - "base_url": "https://docs.api.video/openapi/5f0d4679158b8d006ea6f068", - "env_vars": ["API_VIDEO_API_KEY", "API_VIDEO_TOKEN"], - }, - "api2cart": { - "display_name": "Swagger API2Cart", - "description": "API2Cart", - "spec_url": "https://api.apis.guru/v2/specs/api2cart.com/1.1/openapi.json", - "base_url": "https://app.api2cart.com/default/index/swagger-json", - "env_vars": ["API2CART_API_KEY", "API2CART_TOKEN"], - }, - "api2pdf": { - "display_name": "Api2Pdf - PDF Generation, Powered by AWS Lambda", - "description": "# Introduction [Api2Pdf](https://www.api2pdf.com) is a powerful PDF generation API with no rate limits or file size constraints. Api2Pdf runs on AWS L", - "spec_url": "https://api.apis.guru/v2/specs/api2pdf.com/1.0.0/openapi.json", - "base_url": "https://app.swaggerhub.com/apiproxy/schema/file/api2pdf/api2pdf/1.0.0/swagger.json", - "env_vars": ["API2PDF_API_KEY", "API2PDF_TOKEN"], - }, - "apicurio-local-registry": { - "display_name": "Apicurio Registry API [v2]", - "description": "Apicurio Registry is a datastore for standard event schemas and API designs. Apicurio Registry enables developers to manage and share the structure of", - "spec_url": "https://api.apis.guru/v2/specs/apicurio.local/registry/2.4.x/openapi.json", - "base_url": "https://raw.githubusercontent.com/Apicurio/apicurio-registry/master/app/src/main/resources-unfiltered/META-INF/resources/api-specifications/registry/v2/openapi.json", - "env_vars": [ - "APICURIO_LOCAL_REGISTRY_API_KEY", - "APICURIO_LOCAL_REGISTRY_TOKEN", - ], - }, - "apidapp": { - "display_name": "ApiDapp", - "spec_url": "https://api.apis.guru/v2/specs/apidapp.com/2019-02-14T164701Z/openapi.json", - "base_url": "https://apidapp.s3.amazonaws.com/ApiDapp-Start-swagger-7/ApiDapp-Start-swagger+(7).json", - "env_vars": ["APIDAPP_API_KEY", "APIDAPP_TOKEN"], - }, - "apideck-com-accounting": { - "display_name": "Accounting API", - "description": "Welcome to the Accounting API. You can use this API to access all Accounting API endpoints. ## Base URL The base URL for all API requests is `https://", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/accounting/9.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/accounting.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apideck-com-ats": { - "display_name": "ATS API", - "description": "Welcome to the ATS API. You can use this API to access all ATS API endpoints. ## Base URL The base URL for all API requests is `https://unify.apideck.", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/ats/9.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/ats.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apideck-com-connector": { - "display_name": "Connector API", - "description": "Welcome to the Connector API. You can use this API to access all Connector API endpoints. ## Base URL The base URL for all API requests is `https://un", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/connector/9.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/connector.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apideck-com-crm": { - "display_name": "CRM API", - "description": "Welcome to the CRM API. You can use this API to access all CRM API endpoints. ## Base URL The base URL for all API requests is `https://unify.apideck.", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/crm/9.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/crm.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apideck-com-customer-support": { - "display_name": "Customer Support", - "description": "Welcome to the Customer Support API. You can use this API to access all Customer Support API endpoints. ## Base URL The base URL for all API requests ", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/customer-support/9.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/customer-support.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apideck-com-ecommerce": { - "display_name": "Ecommerce API", - "description": "Welcome to the Ecommerce API. You can use this API to access all Ecommerce API endpoints. ## Base URL The base URL for all API requests is `https://un", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/ecommerce/9.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/ecommerce.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apideck-com-ecosystem": { - "display_name": "Ecosystem API", - "description": "Ecosystem API", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/ecosystem/0.0.6/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/ecosystem.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apideck-com-file-storage": { - "display_name": "File storage API", - "description": "Welcome to the File Storage API. You can use this API to access all File Storage API endpoints. ## Base URL The base URL for all API requests is `http", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/file-storage/9.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/file-storage.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apideck-com-hris": { - "display_name": "HRIS API", - "description": "Welcome to the HRIS API. You can use this API to access all HRIS API endpoints. ## Base URL The base URL for all API requests is `https://unify.apidec", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/hris/9.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/hris.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apideck-com-issue-tracking": { - "display_name": "Issue Tracking API", - "description": "Welcome to the Issue Tracking API. You can use this API to access all Issue Tracking API endpoints. ## Base URL The base URL for all API requests is `", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/issue-tracking/9.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/issue-tracking.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apideck-com-lead": { - "display_name": "Lead API", - "description": "Welcome to the Lead API. You can use this API to access all Lead API endpoints. ## Base URL The base URL for all API requests is `https://unify.apidec", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/lead/9.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/lead.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apideck-com-pos": { - "display_name": "POS API", - "description": "Welcome to the POS API. You can use this API to access all POS API endpoints. ## Base URL The base URL for all API requests is `https://unify.apideck.", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/pos/9.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/pos.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apideck-com-proxy": { - "display_name": "Proxy API", - "description": "Welcome to the Proxy API. You can use this API to access all Proxy API endpoints. ## Base URL The base URL for all API requests is `https://unify.apid", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/proxy/9.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/proxy.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apideck-com-sms": { - "display_name": "SMS API", - "description": "Welcome to the SMS API. You can use this API to access all SMS API endpoints. ## Base URL The base URL for all API requests is `https://unify.apideck.", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/sms/9.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/sms.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apideck-com-vault": { - "display_name": "Vault API", - "description": "Welcome to the Vault API ๐Ÿ‘‹ When you're looking to connect to an API, the first step is authentication. Vault helps you handle OAuth flows, store API k", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/vault/9.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/vault.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apideck-com-webhook": { - "display_name": "Webhook API", - "description": "Welcome to the Webhook API. You can use this API to access all Webhook API endpoints. ## Base URL The base URL for all API requests is `https://unify.", - "spec_url": "https://api.apis.guru/v2/specs/apideck.com/webhook/9.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/apideck-libraries/openapi-specs/master/webhook.yml", - "env_vars": ["APIDECK_API_KEY"], - }, - "apigee-local-registry": { - "display_name": "Registry API", - "description": "The Registry service allows teams to manage descriptions of APIs.", - "spec_url": "https://api.apis.guru/v2/specs/apigee.local/registry/0.0.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/apigee/registry/main/openapi.yaml", - "env_vars": ["APIGEE_LOCAL_REGISTRY_API_KEY", "APIGEE_LOCAL_REGISTRY_TOKEN"], - }, - "apigee-net-marketcheck-cars": { - "display_name": "Marketcheck APIs", - "description": "One API serving data spanned across multiple verticals", - "spec_url": "https://api.apis.guru/v2/specs/apigee.net/marketcheck-cars/2.01/openapi.json", - "base_url": "https://new-verticals-dot-marketcheck-gcp.uc.r.appspot.com/api-docs", - "env_vars": [ - "APIGEE_NET_MARKETCHECK_CARS_API_KEY", - "APIGEE_NET_MARKETCHECK_CARS_TOKEN", - ], - }, - "apimatic": { - "display_name": "APIMATIC API Transformer", - "description": "Transform API Descriptions from/to various formats", - "spec_url": "https://api.apis.guru/v2/specs/apimatic.io/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/Mermade/open_api_specifications/master/APIMATIC%20API%20Transformer/swagger.json", - "env_vars": ["APIMATIC_API_KEY", "APIMATIC_TOKEN"], - }, - "apis-guru": { - "display_name": "APIs.guru", - "description": "Wikipedia for Web APIs. Repository of API definitions in OpenAPI format. **Warning**: If you want to be notified about changes in advance please join ", - "spec_url": "https://api.apis.guru/v2/specs/apis.guru/2.2.0/openapi.json", - "base_url": "https://api.apis.guru/v2/openapi.yaml", - "env_vars": ["APIS_GURU_API_KEY", "APIS_GURU_TOKEN"], - }, - "apispot-io-whois": { - "display_name": "Bulk WHOIS API", - "description": "Domain API (WHOIS, Check, Batch)", - "spec_url": "https://api.apis.guru/v2/specs/apispot.io/whois/1.0/openapi.json", - "base_url": "https://apispot.io/static/whois.yml", - "env_vars": ["APISPOT_IO_WHOIS_API_KEY", "APISPOT_IO_WHOIS_TOKEN"], - }, - "apiz-ebay-com-commerce-identity": { - "display_name": "Identity API", - "description": 'Note: Not all the account related fields are returned for an authenticated user. The fields returned in the response ', - "spec_url": "https://api.apis.guru/v2/specs/apiz.ebay.com/commerce-identity/v1.1.0/openapi.json", - "base_url": "https://developer.ebay.com/api-docs/master/commerce/identity/openapi/3/commerce_identity_v1_oas3.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "apiz-ebay-com-sell-finances": { - "display_name": "eBay Finances API", - "description": "This API is used to retrieve seller payouts and monetary transaction details related to those payouts.", - "spec_url": "https://api.apis.guru/v2/specs/apiz.ebay.com/sell-finances/v1.15.0/openapi.json", - "base_url": "https://developer.ebay.com/api-docs/master/sell/finances/openapi/3/sell_finances_v1_oas3.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "appcenter-ms": { - "display_name": "App Center Client", - "description": "Microsoft Visual Studio App Center API", - "spec_url": "https://api.apis.guru/v2/specs/appcenter.ms/v0.1/openapi.json", - "base_url": "https://api.appcenter.ms/preview/swagger.json", - "env_vars": ["APPCENTER_MS_API_KEY", "APPCENTER_MS_TOKEN"], - }, - "apple-com-app-store-connect": { - "display_name": "App Store Connect API", - "spec_url": "https://api.apis.guru/v2/specs/apple.com/app-store-connect/1.4.1/openapi.json", - "base_url": "app-store-connect-openapi-specification.json", - "env_vars": [ - "APPLE_COM_APP_STORE_CONNECT_API_KEY", - "APPLE_COM_APP_STORE_CONNECT_TOKEN", - ], - }, - "apple-com-sirikit-cloud-media": { - "display_name": "SiriKit Cloud Media", - "spec_url": "https://api.apis.guru/v2/specs/apple.com/sirikit-cloud-media/1.0.2/openapi.json", - "base_url": "sirikit-cloud-media.json", - "env_vars": [ - "APPLE_COM_SIRIKIT_CLOUD_MEDIA_API_KEY", - "APPLE_COM_SIRIKIT_CLOUD_MEDIA_TOKEN", - ], - }, - "apptigent": { - "display_name": "PowerTools Developer", - "description": "Apptigent PowerTools Developer Edition is a powerful suite of API endpoints for custom applications running on any stack. Manipulate text, modify coll", - "spec_url": "https://api.apis.guru/v2/specs/apptigent.com/2021.1.01/openapi.json", - "base_url": "https://portal.apptigent.com/sites/portal.apptigent.com/files/v3-powertools-developer-2021-1-01.json", - "env_vars": ["APPTIGENT_API_KEY", "APPTIGENT_TOKEN"], - }, - "appveyor": { - "display_name": "AppVeyor REST API", - "description": "AppVeyor is a hosted continuous integration service which runs on Microsoft Windows. The AppVeyor REST API provides a RESTful way to interact with the", - "spec_url": "https://api.apis.guru/v2/specs/appveyor.com/1.0.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/kevinoid/appveyor-swagger/master/swagger.yaml", - "env_vars": ["APPVEYOR_API_KEY", "APPVEYOR_TOKEN"], - }, - "appwrite-io-client": { - "display_name": "Appwrite", - "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common develop", - "spec_url": "https://api.apis.guru/v2/specs/appwrite.io/client/0.9.3/openapi.json", - "base_url": "https://appwrite.io/specs/open-api3?platform=client", - "env_vars": ["APPWRITE_IO_CLIENT_API_KEY", "APPWRITE_IO_CLIENT_TOKEN"], - }, - "appwrite-io-server": { - "display_name": "Appwrite", - "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common develop", - "spec_url": "https://api.apis.guru/v2/specs/appwrite.io/server/0.9.3/openapi.json", - "base_url": "https://appwrite.io/specs/open-api3?platform=server", - "env_vars": ["APPWRITE_IO_SERVER_API_KEY", "APPWRITE_IO_SERVER_TOKEN"], - }, - "archive-org-search": { - "display_name": "Search Services", - "description": "API for Internet Archive's Search-related services", - "spec_url": "https://api.apis.guru/v2/specs/archive.org/search/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/ArchiveLabs/api.archive.org/master/swagger/search.yaml", - "env_vars": ["ARCHIVE_ORG_SEARCH_API_KEY", "ARCHIVE_ORG_SEARCH_TOKEN"], - }, - "archive-org-wayback": { - "display_name": "Wayback API", - "description": "API for Internet Archive's Wayback Machine", - "spec_url": "https://api.apis.guru/v2/specs/archive.org/wayback/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/ArchiveLabs/api.archive.org/master/swagger/wayback.json", - "env_vars": ["ARCHIVE_ORG_WAYBACK_API_KEY", "ARCHIVE_ORG_WAYBACK_TOKEN"], - }, - "arespass": { - "display_name": "Arespass", - "description": "Analyzes a password and calculates its entropy.", - "spec_url": "https://api.apis.guru/v2/specs/arespass.net/1.0/openapi.json", - "base_url": "https://arespass.net/assets/arespassv1.0-openapi.yaml", - "env_vars": ["ARESPASS_API_KEY", "ARESPASS_TOKEN"], - }, - "art19": { - "display_name": "ART19 Content API Documentation", - "description": "The ART19 Content API conforms to the [JSON:API specification](http://jsonapi.org). API requests **MUST** use the HTTP Accept header: `Accept: applica", - "spec_url": "https://api.apis.guru/v2/specs/art19.com/1.0.0/openapi.json", - "base_url": "https://art19.com/swagger_json/external/content.json", - "env_vars": ["ART19_API_KEY", "ART19_TOKEN"], - }, - "asana": { - "display_name": "Asana", - "description": "This is the interface for interacting with the [Asana Platform](https://developers.asana.com). Our API reference is generated from our [OpenAPI spec] ", - "spec_url": "https://api.apis.guru/v2/specs/asana.com/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/Asana/developer-docs/master/defs/asana_oas.yaml", - "env_vars": ["ASANA_TOKEN", "ASANA_ACCESS_TOKEN"], - }, - "asuarez-dev-searchly": { - "display_name": "SearchLy API v1", - "description": "# Introduction The SearchLy API provides similarity searching based on song lyrics. # Operations The API allows for the `/similarity/by_song` operatio", - "spec_url": "https://api.apis.guru/v2/specs/asuarez.dev/searchly/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/AlbertSuarez/searchly/master/src/searchly/static/openapi/openapi_v1.yaml", - "env_vars": ["ASUAREZ_DEV_SEARCHLY_API_KEY", "ASUAREZ_DEV_SEARCHLY_TOKEN"], - }, - "atlassian-com-jira": { - "display_name": "The Jira Cloud platform REST API", - "description": "Jira Cloud platform REST API documentation", - "spec_url": "https://api.apis.guru/v2/specs/atlassian.com/jira/1001.0.0-SNAPSHOT/openapi.json", - "base_url": "https://developer.atlassian.com/cloud/jira/platform/swagger-v3.v3.json", - "env_vars": ["JIRA_TOKEN", "JIRA_API_TOKEN"], - }, - "ato-gov-au": { - "display_name": "Business Registries", - "description": "# Introduction The Business Registries API is built on HTTP. The API is RESTful. It has predictable resource URIs. The API is documented in This API provides access to our Automotive Data. Use of this API is subject to our Terms of', - "spec_url": "https://api.apis.guru/v2/specs/autodealerdata.com/1.0/openapi.json", - "base_url": "https://api.autodealerdata.com/openapi.json", - "env_vars": ["AUTODEALERDATA_API_KEY", "AUTODEALERDATA_TOKEN"], - }, - "autotask": { - "display_name": "Datto|Autotask PSA Rest API", - "spec_url": "https://api.apis.guru/v2/specs/autotask.net/v1/swagger.json", - "base_url": "https://webservices5.autotask.net/ATServicesRest/swagger/docs/v1", - "env_vars": ["AUTOTASK_API_KEY", "AUTOTASK_TOKEN"], - }, - "avaza": { - "display_name": "Avaza API Documentation", - "description": "Welcome to the autogenerated documentation & test tool for Avaza's API.

API Security & Authentication
Authentication op", - "spec_url": "https://api.apis.guru/v2/specs/avaza.com/v1/swagger.json", - "base_url": "https://api.avaza.com/swagger/docs/v1", - "env_vars": ["AVAZA_API_KEY", "AVAZA_TOKEN"], - }, - "aviationdata-systems": { - "display_name": "AviationData.Systems Airports API V1", - "spec_url": "https://api.apis.guru/v2/specs/aviationdata.systems/v1/swagger.json", - "base_url": "http://api.aviationdata.systems//swagger/docs/v1", - "env_vars": ["AVIATIONDATA_SYSTEMS_API_KEY", "AVIATIONDATA_SYSTEMS_TOKEN"], - }, - "axesso-de": { - "display_name": "Axesso Api", - "description": "Use this api to fetch information to Amazon products and more.", - "spec_url": "https://api.apis.guru/v2/specs/axesso.de/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/Axesso/axesso-java-client/master/swagger/axessor_api_def_swagger.yaml", - "env_vars": ["AXESSO_DE_API_KEY", "AXESSO_DE_TOKEN"], - }, - "balldontlie": { - "display_name": "balldontlie", - "spec_url": "https://api.apis.guru/v2/specs/balldontlie.io/1.0.0/openapi.json", - "base_url": "https://www.postman.com/collections/c51c3810db2ab3ca4ab4", - "env_vars": ["BALLDONTLIE_API_KEY", "BALLDONTLIE_TOKEN"], - }, - "bandsintown": { - "display_name": "Bandsintown API", - "description": "# What is the Bandsintown API? The Bandsintown API is designed for artists and enterprises representing artists. It offers read-only access to artist ", - "spec_url": "https://api.apis.guru/v2/specs/bandsintown.com/3.0.0/swagger.json", - "base_url": "https://api.swaggerhub.com/apis/Bandsintown/PublicAPI/3.0.0/swagger.yaml", - "env_vars": ["BANDSINTOWN_API_KEY", "BANDSINTOWN_TOKEN"], - }, - "bbc": { - "display_name": "BBC Nitro API", - "description": "BBC Nitro is the BBC's application programming interface (API) for BBC Programmes Metadata.", - "spec_url": "https://api.apis.guru/v2/specs/bbc.com/1.0.0/openapi.json", - "base_url": "http://programmes.api.bbc.com/nitro/api", - "env_vars": ["BBC_API_KEY", "BBC_TOKEN"], - }, - "bbc-co-uk": { - "display_name": "Radio & Music Services", - "description": "We encapsulate Radio & Music business logic for iPlayer Radio and BBC Music products on all platforms. We add value by reliably providing the right bl", - "spec_url": "https://api.apis.guru/v2/specs/bbc.co.uk/1.0.0/swagger.json", - "base_url": "https://rms.api.bbc.co.uk/docs/swagger.json", - "env_vars": ["BBC_CO_UK_API_KEY", "BBC_CO_UK_TOKEN"], - }, - "bbci-co-uk": { - "display_name": "BBC iPlayer Business Layer", - "description": "The definitive iPlayer API.", - "spec_url": "https://api.apis.guru/v2/specs/bbci.co.uk/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/Mermade/bbcparse/master/iblApi/openapi.yaml", - "env_vars": ["BBCI_CO_UK_API_KEY", "BBCI_CO_UK_TOKEN"], - }, - "bclaws-ca-bclaws": { - "display_name": "BC Laws", - "description": "BC Laws is an electronic library providing free public access to the laws of British Columbia. BC Laws is hosted by the Queen's Printer of British Col", - "spec_url": "https://api.apis.guru/v2/specs/bclaws.ca/bclaws/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/bcgov/api-specs/master/bclaws/bclaws.json", - "env_vars": ["BCLAWS_CA_BCLAWS_API_KEY", "BCLAWS_CA_BCLAWS_TOKEN"], - }, - "beanstream": { - "display_name": "Beanstream Payments", - "description": "https://www.beanstream.com/api/v1", - "spec_url": "https://api.apis.guru/v2/specs/beanstream.com/1.0.1/swagger.json", - "base_url": "http://support.beanstream.com/restapi/swagger.json", - "env_vars": ["BEANSTREAM_API_KEY", "BEANSTREAM_TOKEN"], - }, - "beezup": { - "display_name": "BeezUP Merchant API", - "description": "# The REST API of BeezUP system ## Overview The REST APIs provide programmatic access to read and write BeezUP data. Basically, with this API you will", - "spec_url": "https://api.apis.guru/v2/specs/beezup.com/2.0/openapi.json", - "base_url": "https://api-docs.beezup.com/swagger.json", - "env_vars": ["BEEZUP_API_KEY", "BEEZUP_TOKEN"], - }, - "betfair": { - "display_name": "Betfair: Exchange Streaming API", - "description": "API to receive streamed updates. This is an ssl socket connection of CRLF delimited json messages (see RequestMessage & ResponseMessage)", - "spec_url": "https://api.apis.guru/v2/specs/betfair.com/1.0.1423/openapi.json", - "base_url": "https://raw.githubusercontent.com/betfair/stream-api-sample-code/master/ESASwaggerSchema.json", - "env_vars": ["BETFAIR_API_KEY", "BETFAIR_TOKEN"], - }, - "bethmardutho": { - "display_name": "SEDRA IV API", - "description": "The SEDRA API is documented in **OpenAPI format** and uses [ReDoc](https://github.com/Rebilly/ReDoc) for documentation. # Introduction This document d", - "spec_url": "https://api.apis.guru/v2/specs/bethmardutho.org/1.0.0/swagger.json", - "base_url": "https://sedra.bethmardutho.org/api/openapi", - "env_vars": ["BETHMARDUTHO_API_KEY", "BETHMARDUTHO_TOKEN"], - }, - "bhagavadgita": { - "display_name": "Bhagavad Gita API", - "spec_url": "https://api.apis.guru/v2/specs/bhagavadgita.io/1.0/openapi.json", - "base_url": "http://bhagavadgita.io/apispec_1.json", - "env_vars": ["BHAGAVADGITA_API_KEY", "BHAGAVADGITA_TOKEN"], - }, - "biapi-pro": { - "display_name": "Budgea API Documentation", - "description": "# Budgea Development Guides Welcome to **Budgea**'s documentation. This documentation is intended to get you up-and-running with our APIs and advise o", - "spec_url": "https://api.apis.guru/v2/specs/biapi.pro/2.0/openapi.json", - "base_url": "https://budgea.biapi.pro/2.0/doc/", - "env_vars": ["BIAPI_PRO_API_KEY", "BIAPI_PRO_TOKEN"], - }, - "bigdatacloud": { - "display_name": "IP Geolocation API", - "description": "BigDataCloud's IP Geolocation API returns detailed information about the geographical location, ownership and connectivity of the provided IPv4 IP add", - "spec_url": "https://api.apis.guru/v2/specs/bigdatacloud.net/1.0.0/openapi.json", - "base_url": "https://www.postman.com/collections/10684407-3369ce87-fd01-423c-a38f-335da4db520b", - "env_vars": ["BIGDATACLOUD_API_KEY", "BIGDATACLOUD_TOKEN"], - }, - "bigoven": { - "display_name": "1,000,000+ Recipe and Grocery List API (v2)", - "description": "#Documentation This is the documentation for the partner endpoint of the BigOven Recipe and Grocery List API. The update brings with it Swagger-based ", - "spec_url": "https://api.apis.guru/v2/specs/bigoven.com/partner/openapi.json", - "base_url": "http://api2.bigoven.com/swagger/docs/partner", - "env_vars": ["BIGOVEN_API_KEY", "BIGOVEN_TOKEN"], - }, - "bigredcloud": { - "display_name": "Big Red Cloud API", - "description": "

Welcome to the Big Red Cloud API
This API enables programmatic access to Big Red Cloud data.This is an API for accessing information about bicycling related incidents. You can find the source code on
The GoToMeeting API provides seamless integration of GoToMeeting provisioning and meeting management into your existing infrastructure or third pa", - "spec_url": "https://api.apis.guru/v2/specs/citrixonline.com/gotomeeting/1.0.0/swagger.json", - "base_url": "https://developer.citrixonline.com/sites/default/files/citrix/citrix-apis/gotomeeting.json", - "env_vars": [ - "CITRIXONLINE_COM_GOTOMEETING_API_KEY", - "CITRIXONLINE_COM_GOTOMEETING_TOKEN", - ], - }, - "citrixonline-com-scim": { - "display_name": "SCIM", - "description": "The SCIM API lets you manage users in your organization. You can then automate the provisioning of product licenses for these users, and they can use ", - "spec_url": "https://api.apis.guru/v2/specs/citrixonline.com/scim/NA/swagger.json", - "base_url": "https://developer.citrixonline.com/sites/default/files/citrix/citrix-apis/scim.json", - "env_vars": ["CITRIXONLINE_COM_SCIM_API_KEY", "CITRIXONLINE_COM_SCIM_TOKEN"], - }, - "citycontext": { - "display_name": "City Context", - "description": "City Context provides a straightforward API to access UK Open Data: crime statistics, schools, demographics and more.", - "spec_url": "https://api.apis.guru/v2/specs/citycontext.com/1.0.0/swagger.json", - "base_url": "https://www.citycontext.com/swagger/spec.json", - "env_vars": ["CITYCONTEXT_API_KEY", "CITYCONTEXT_TOKEN"], - }, - "clarify": { - "display_name": "api.clarify.io", - "description": "The API to Search and Understand Audio & Video Data.", - "spec_url": "https://api.apis.guru/v2/specs/clarify.io/1.3.7/swagger.json", - "base_url": "https://api.clarify.io/api-docs", - "env_vars": ["CLARIFY_API_KEY", "CLARIFY_TOKEN"], - }, - "clearblade": { - "display_name": "ClearBlade API", - "description": "A friendly little API to help you interact with the ClearBlade platform.", - "spec_url": "https://api.apis.guru/v2/specs/clearblade.com/3.0/swagger.json", - "base_url": "https://docs.clearblade.com/v/4/static/api/openapi.yaml", - "env_vars": ["CLEARBLADE_API_KEY", "CLEARBLADE_TOKEN"], - }, - "clever": { - "display_name": "Data API", - "description": "Serves the Clever Data API", - "spec_url": "https://api.apis.guru/v2/specs/clever.com/1.2.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/Clever/swagger-api/master/v1.2.yml", - "env_vars": ["CLEVER_API_KEY", "CLEVER_TOKEN"], - }, - "clever-cloud": { - "display_name": "Clever-Cloud API", - "description": "Public API for managing Clever-Cloud data and products", - "spec_url": "https://api.apis.guru/v2/specs/clever-cloud.com/1.0.0/openapi.json", - "base_url": "https://www.clever-cloud.com/doc/api/swagger.json", - "env_vars": ["CLEVER_CLOUD_API_KEY", "CLEVER_CLOUD_TOKEN"], - }, - "clickmeter": { - "display_name": "ClickMeter API", - "description": "Api dashboard for ClickMeter API", - "spec_url": "https://api.apis.guru/v2/specs/clickmeter.com/v2/openapi.json", - "base_url": "http://api.v2.clickmeter.com.s3.amazonaws.com/docs/api-docs-v2.json", - "env_vars": ["CLICKMETER_API_KEY", "CLICKMETER_TOKEN"], - }, - "clicksend": { - "display_name": "ClickSend REST API v3", - "description": "This is the official API documentation for ClickSend.com Below you will find a current list of the available methods for clicksend. **NOTE**: You will", - "spec_url": "https://api.apis.guru/v2/specs/clicksend.com/1.0.0/openapi.json", - "base_url": "https://clicksend.docs.apiary.io/api-description-document", - "env_vars": ["CLICKSEND_API_KEY", "CLICKSEND_TOKEN"], - }, - "clickup": { - "display_name": "clickup20", - "description": "Polls is a simple API allowing consumers to view polls and vote in them.", - "spec_url": "https://api.apis.guru/v2/specs/clickup.com/1.0.0/openapi.json", - "base_url": "https://jsapi.apiary.io/apis/clickup20.source", - "env_vars": ["CLICKUP_API_KEY"], - }, - "climate": { - "display_name": "Climate FieldView Platform APIs", - "description": "**Last Modified**: Wed Jan 4 12:47:29 UTC 2023 All endpoints are only accessible via HTTPS. * All API endpoints are located at `https://platform.clima", - "spec_url": "https://api.apis.guru/v2/specs/climate.com/4.0.11/openapi.json", - "base_url": "https://dev.fieldview.com/openapi/platform.yaml", - "env_vars": ["CLIMATE_API_KEY", "CLIMATE_TOKEN"], - }, - "climatekuul": { - "display_name": "climateKuul live", - "spec_url": "https://api.apis.guru/v2/specs/climatekuul.com/1.0/openapi.json", - "base_url": "http://api.climatekuul.com/api-docs", - "env_vars": ["CLIMATEKUUL_API_KEY", "CLIMATEKUUL_TOKEN"], - }, - "cloud-elements-com-ecwid": { - "display_name": "ecwid", - "spec_url": "https://api.apis.guru/v2/specs/cloud-elements.com/ecwid/api-v2/swagger.json", - "base_url": "https://api.cloud-elements.com/elements/api-v2/elements/52/docs?version=-1", - "env_vars": [ - "CLOUD_ELEMENTS_COM_ECWID_API_KEY", - "CLOUD_ELEMENTS_COM_ECWID_TOKEN", - ], - }, - "cloudmersive-com-ocr": { - "display_name": "ocrapi", - "description": "The powerful Optical Character Recognition (OCR) APIs let you convert scanned images of pages into recognized text.", - "spec_url": "https://api.apis.guru/v2/specs/cloudmersive.com/ocr/v1/openapi.json", - "base_url": "https://api.cloudmersive.com/ocr/docs/v1/swagger", - "env_vars": ["CLOUDMERSIVE_COM_OCR_API_KEY", "CLOUDMERSIVE_COM_OCR_TOKEN"], - }, - "cloudrf": { - "display_name": "Cloud-RF API", - "description": "Use this JSON API to build and test radio links for any radio, anywhere. Authenticate with your API2.0 key in the request header as key", - "spec_url": "https://api.apis.guru/v2/specs/cloudrf.com/2.0.0/openapi.json", - "base_url": "https://api.cloudrf.com/swagger-ui/Cloud-RF_API2.0.yaml", - "env_vars": ["CLOUDRF_API_KEY", "CLOUDRF_TOKEN"], - }, - "clubhouseapi": { - "display_name": "Clubhouse API", - "description": "Clubhouse API", - "spec_url": "https://api.apis.guru/v2/specs/clubhouseapi.com/1/openapi.json", - "base_url": "https://raw.githubusercontent.com/zhuowei/ClubhouseAPI/main/doc/openapi.yaml", - "env_vars": ["CLUBHOUSEAPI_API_KEY", "CLUBHOUSEAPI_TOKEN"], - }, - "cnab-online-herokuapp": { - "display_name": "Cnab Online", - "description": "Processe arquivos de retorno CNAB", - "spec_url": "https://api.apis.guru/v2/specs/cnab-online.herokuapp.com/1.0.0/swagger.json", - "base_url": "http://cnab-online.github.io/api-reference/api-reference-v1.json", - "env_vars": ["HEROKU_API_KEY"], - }, - "codat-io-accounting": { - "display_name": "Accounting API", - "description": "A flexible API for pulling accounting data, normalized and aggregated from 20 accounting integrations. Standardize how you connect to your customersโ€™ ", - "spec_url": "https://api.apis.guru/v2/specs/codat.io/accounting/2.1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/codatio/oas/main/json/Codat-Accounting.json", - "env_vars": ["CODAT_API_KEY"], - }, - "codat-io-assess": { - "display_name": "Assess API", - "description": "Codat's Assess API enable you to make smarter credit decisions on your small business customers. Assess enriches your customer's accounting, commerce ", - "spec_url": "https://api.apis.guru/v2/specs/codat.io/assess/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/codatio/oas/main/json/Codat-Assess.json", - "env_vars": ["CODAT_API_KEY"], - }, - "codat-io-bank-feeds": { - "display_name": "Bank Feeds API", - "description": "Bank Feeds API enables your SMB users to set up bank feeds from accounts in your application to supported accounting platforms. A bank feed is a conne", - "spec_url": "https://api.apis.guru/v2/specs/codat.io/bank-feeds/2.1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/codatio/oas/main/json/Codat-Bank-Feeds.json", - "env_vars": ["CODAT_API_KEY"], - }, - "codat-io-banking": { - "display_name": "Banking API", - "description": "Codat's Banking API allows you to access standardised data from over bank accounts via third party providers. Standardize how you connect to your cust", - "spec_url": "https://api.apis.guru/v2/specs/codat.io/banking/2.1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/codatio/oas/main/json/Codat-Banking.json", - "env_vars": ["CODAT_API_KEY"], - }, - "codat-io-commerce": { - "display_name": "Commerce API", - "description": "Codat's Commerce API allows you to access standardised data from over 11 commerce and POS systems. Standardize how you connect to your customersโ€™ paym", - "spec_url": "https://api.apis.guru/v2/specs/codat.io/commerce/2.1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/codatio/oas/main/json/Codat-Commerce.json", - "env_vars": ["CODAT_API_KEY"], - }, - "codat-io-sync-for-commerce": { - "display_name": "Sync for Commerce API", - "description": "The API for Sync for Commerce. Sync for Commerce is an API and a set of supporting tools. It has been built to enable e-commerce, point of sale platfo", - "spec_url": "https://api.apis.guru/v2/specs/codat.io/sync-for-commerce/1.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/codatio/oas/main/json/Codat-Sync-Commerce.json", - "env_vars": ["CODAT_API_KEY"], - }, - "codat-io-sync-for-expenses": { - "display_name": "Codat Expense API", - "description": "The API for Sync for Expenses. Sync for Expenses is an API and a set of supporting tools. It has been built to enable corporate card and expense manag", - "spec_url": "https://api.apis.guru/v2/specs/codat.io/sync-for-expenses/prealpha/openapi.json", - "base_url": "https://raw.githubusercontent.com/codatio/oas/main/json/Codat-Expenses.json", - "env_vars": ["CODAT_API_KEY"], - }, - "code-scan": { - "display_name": "CodeScan API", - "description": "Manage your Hosted CodeScan Service", - "spec_url": "https://api.apis.guru/v2/specs/code-scan.com/1.0.0/swagger.json", - "base_url": "https://www.code-scan.com/api.swagger.yaml", - "env_vars": ["CODE_SCAN_API_KEY", "CODE_SCAN_TOKEN"], - }, - "codesearch-debian": { - "display_name": "Debian Code Search", - "description": "OpenAPI for https://codesearch.debian.net/", - "spec_url": "https://api.apis.guru/v2/specs/codesearch.debian.net/1.4.0/openapi.json", - "base_url": "https://codesearch.debian.net/openapi.yaml", - "env_vars": ["CODESEARCH_DEBIAN_API_KEY", "CODESEARCH_DEBIAN_TOKEN"], - }, - "collegefootballdata": { - "display_name": "College Football Data API", - "description": 'This is an API for accessing all sorts of college football data. Please note that API keys should be supplied with "Bearer " prepended (e.g. "Beare', - "spec_url": "https://api.apis.guru/v2/specs/collegefootballdata.com/4.4.12/openapi.json", - "base_url": "https://api.collegefootballdata.com/api-docs.json", - "env_vars": ["COLLEGEFOOTBALLDATA_API_KEY", "COLLEGEFOOTBALLDATA_TOKEN"], - }, - "color-pizza": { - "display_name": "Color Name API", - "description": "An API that provides names for colors based on their hex value", - "spec_url": "https://api.apis.guru/v2/specs/color.pizza/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/meodai/color-name-api/main/color-names-v1-OpenAPI.yml", - "env_vars": ["COLOR_PIZZA_API_KEY", "COLOR_PIZZA_TOKEN"], - }, - "combell": { - "display_name": "Public Api", - "description": "# Introduction This API allows resellers to manage their resources in a simple, programmatic way using HTTP requests. # Conventions ## Requests The AP", - "spec_url": "https://api.apis.guru/v2/specs/combell.com/v2/openapi.json", - "base_url": "https://api.combell.com/v2/documentation/swagger-v2.json", - "env_vars": ["COMBELL_API_KEY", "COMBELL_TOKEN"], - }, - "configcat": { - "display_name": "ConfigCat Public Management API", - "description": "**Base API URL**: https://api.configcat.com If you prefer the swagger documentation, you can find it here: [Swagger UI](https://api.configcat.com/swag", - "spec_url": "https://api.apis.guru/v2/specs/configcat.com/v1/openapi.json", - "base_url": "https://api.configcat.com/docs/v1/swagger.json", - "env_vars": ["CONFIGCAT_API_KEY", "CONFIGCAT_TOKEN"], - }, - "conjur": { - "display_name": "Conjur", - "description": "This is an API definition for CyberArk Conjur Open Source. You can find out more at [Conjur.org](https://www.conjur.org/).", - "spec_url": "https://api.apis.guru/v2/specs/conjur.local/5.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/cyberark/conjur-openapi-spec/main/spec/openapi.yml", - "env_vars": ["CONJUR_API_KEY", "CONJUR_TOKEN"], - }, - "consumerfinance-gov": { - "display_name": "The Consumer Financial Protection Bureau", - "description": "Learn more about home mortgage data, download the data yourself, or build new tools using our API.", - "spec_url": "https://api.apis.guru/v2/specs/consumerfinance.gov/1.0/swagger.json", - "base_url": "https://api.consumerfinance.gov/api-docs", - "env_vars": ["CONSUMERFINANCE_GOV_API_KEY", "CONSUMERFINANCE_GOV_TOKEN"], - }, - "contentgroove": { - "display_name": "ContentGroove API", - "description": "# Overview The ContentGroove Developer API enables you to add the power of ContentGroove's video AI to your own applications and workflows. Webhooks a", - "spec_url": "https://api.apis.guru/v2/specs/contentgroove.com/1.0.0/openapi.json", - "base_url": "https://api.contentgroove.com/api-docs/v1/openapi.json", - "env_vars": ["CONTENTGROOVE_API_KEY", "CONTENTGROOVE_TOKEN"], - }, - "contract-p-fit": { - "display_name": "Contract.fit API", - "description": "This OpenAPI describes the API exposed by the contract.fit backend. ## Security ### Authentication All endpoints are protected: you need to make authe", - "spec_url": "https://api.apis.guru/v2/specs/contract-p.fit/1.0/openapi.json", - "base_url": "https://cfportal.contract-p.fit/swagger.json", - "env_vars": ["CONTRACT_P_FIT_API_KEY", "CONTRACT_P_FIT_TOKEN"], - }, - "contribly": { - "display_name": "Contribly", - "spec_url": "https://api.apis.guru/v2/specs/contribly.com/1.0.0/openapi.json", - "base_url": "https://api.contribly.com/1/swagger.json", - "env_vars": ["CONTRIBLY_API_KEY", "CONTRIBLY_TOKEN"], - }, - "core-ac-uk": { - "display_name": "CORE API v2", - "description": '

You can use the CORE API to access the resources harvested and enriched by CORE. If you encounter any problems with ', - "spec_url": "https://api.apis.guru/v2/specs/core.ac.uk/2.0/swagger.json", - "base_url": "http://core.ac.uk/api-v2/doc", - "env_vars": ["CORE_AC_UK_API_KEY", "CORE_AC_UK_TOKEN"], - }, - "corrently": { - "display_name": "Corrently.io", - "description": "*Corrently - from italian corrente, which is energy* # Introduction The Corrently ecosystem gets maintained by [STROMDAO GmbH](https://www.stromdao.de", - "spec_url": "https://api.apis.guru/v2/specs/corrently.io/2.0.0/openapi.json", - "base_url": "https://corrently.io/dist.yaml", - "env_vars": ["CORRENTLY_API_KEY", "CORRENTLY_TOKEN"], - }, - "covid19-api": { - "display_name": "COVID-19 data API", - "spec_url": "https://api.apis.guru/v2/specs/covid19-api.com/1.2.6/openapi.json", - "base_url": "https://covid19-api.com/docs.json", - "env_vars": ["COVID19_API_API_KEY", "COVID19_API_TOKEN"], - }, - "cowin-gov-cin-cowincert": { - "display_name": "Co-WIN Certificate API", - "description": "API to get Co-WIN vaccination certificate.", - "spec_url": "https://api.apis.guru/v2/specs/cowin.gov.cin/cowincert/1.0.0/openapi.json", - "base_url": "https://apisetu.gov.in/api_specification_v8/cowincert.yaml", - "env_vars": [ - "COWIN_GOV_CIN_COWINCERT_API_KEY", - "COWIN_GOV_CIN_COWINCERT_TOKEN", - ], - }, - "cpy-re-peertube": { - "display_name": "PeerTube", - "description": "The PeerTube API is built on HTTP(S) and is RESTful. You can use your favorite HTTP/REST library for your programming language to use PeerTube. The sp", - "spec_url": "https://api.apis.guru/v2/specs/cpy.re/peertube/5.1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/Chocobozzz/PeerTube/develop/support/doc/api/openapi.yaml", - "env_vars": ["CPY_RE_PEERTUBE_API_KEY", "CPY_RE_PEERTUBE_TOKEN"], - }, - "credas-co-uk-pi": { - "display_name": "Credas API", - "spec_url": "https://api.apis.guru/v2/specs/credas.co.uk/pi/v1/openapi.json", - "base_url": "https://pi-api.credas.co.uk/swagger/v1/swagger.json", - "env_vars": ["CREDAS_CO_UK_PI_API_KEY", "CREDAS_CO_UK_PI_TOKEN"], - }, - "crediwatch-com-covid19": { - "display_name": "Crediwatch's Covid APIs", - "description": "An API collection for Covid 19 by Crediwatch", - "spec_url": "https://api.apis.guru/v2/specs/crediwatch.com/covid19/1.3.0/openapi.json", - "base_url": "https://api-covid.crediwatch.com/openapi.json", - "env_vars": ["CREDIWATCH_COM_COVID19_API_KEY", "CREDIWATCH_COM_COVID19_TOKEN"], - }, - "crossbrowsertesting": { - "display_name": "Crossbrowsertesting.com Screenshot Comparisons API", - "description": "What's in this version: 1. Compare two screenshots for layout differences 2. Compare a full screenshot test of browsers to a single baseline browser f", - "spec_url": "https://api.apis.guru/v2/specs/crossbrowsertesting.com/3.0.0/openapi.json", - "base_url": "https://crossbrowsertesting.com/apidocs/definitions/screenshot-comparisons.json", - "env_vars": ["CROSSBROWSERTESTING_API_KEY", "CROSSBROWSERTESTING_TOKEN"], - }, - "crucible": { - "display_name": "Crucible", - "spec_url": "https://api.apis.guru/v2/specs/crucible.local/1.0.0/swagger.json", - "base_url": "https://docs.atlassian.com/fisheye-crucible/latest_backup/wadl/crucible.wadl", - "env_vars": ["CRUCIBLE_API_KEY", "CRUCIBLE_TOKEN"], - }, - "cybertaxonomy-eu": { - "display_name": "EU BON UTIS", - "description": "The Unified Taxonomic Information Service (UTIS) is the taxonomic backbone for the EU-BON project", - "spec_url": "https://api.apis.guru/v2/specs/cybertaxonomy.eu/1.0/swagger.json", - "base_url": "http://cybertaxonomy.eu/eubon-utis/api-docs", - "env_vars": ["CYBERTAXONOMY_EU_API_KEY", "CYBERTAXONOMY_EU_TOKEN"], - }, - "cycat": { - "display_name": "CyCAT.org API", - "description": "CyCAT - The Cybersecurity Resource Catalogue public API services.", - "spec_url": "https://api.apis.guru/v2/specs/cycat.org/0.9/swagger.json", - "base_url": "https://api.cycat.org/swagger.json", - "env_vars": ["CYCAT_API_KEY", "CYCAT_TOKEN"], - }, - "d7networks": { - "display_name": "D7SMS", - "description": "D7 SMS allows you to reach your customers via SMS over D7's own connectivity to global mobile networks. D7 provides reliable and cost-effective SMS se", - "spec_url": "https://api.apis.guru/v2/specs/d7networks.com/1.0.2/openapi.json", - "base_url": "https://api.swaggerhub.com/apis/Direct7Networks/D7SMS/1.0.2", - "env_vars": ["D7NETWORKS_API_KEY", "D7NETWORKS_TOKEN"], - }, - "daniweb": { - "display_name": "DaniWeb Connect API", - "description": "User Recommendation Engine and Chat Network", - "spec_url": "https://api.apis.guru/v2/specs/daniweb.com/4/openapi.json", - "base_url": "https://www.daniweb.com/connect/developers/swagger", - "env_vars": ["DANIWEB_API_KEY", "DANIWEB_TOKEN"], - }, - "data-gov": { - "display_name": "Regulations.gov", - "description": "Provides public users access to federal regulatory content.", - "spec_url": "https://api.apis.guru/v2/specs/data.gov/3.0/swagger.json", - "base_url": "http://regulationsgov.github.io/developers/api-docs.json", - "env_vars": ["DATA_GOV_API_KEY", "DATA_GOV_TOKEN"], - }, - "data2crm": { - "display_name": "Data2CRM.API", - "description": "

Make use of our in-depth documentation to get more information about the various functions of the service. Those willing to explore the mechanics o", - "spec_url": "https://api.apis.guru/v2/specs/data2crm.com/1/swagger.json", - "base_url": "https://app.api2crm.com/swagger/spec/data2crm_api.json", - "env_vars": ["DATA2CRM_API_KEY", "DATA2CRM_TOKEN"], - }, - "dataatwork": { - "display_name": "Open Skills API", - "description": "A complete and standard data store for canonical and emerging skills, knowledge, abilities, tools, technolgies, and how they relate to jobs.", - "spec_url": "https://api.apis.guru/v2/specs/dataatwork.org/1.0/swagger.json", - "base_url": "http://api.dataatwork.org/v1/spec/skills-api.json", - "env_vars": ["DATAATWORK_API_KEY", "DATAATWORK_TOKEN"], - }, - "dataflowkit": { - "display_name": "Dataflow Kit Web Scraper", - "description": "Render Javascript driven pages, while we internally manage Headless Chrome and proxies for you. - Build a custom web scraper with our Visual point-and", - "spec_url": "https://api.apis.guru/v2/specs/dataflowkit.com/1.3/openapi.json", - "base_url": "https://api.dataflowkit.com/v1/swagger.yaml", - "env_vars": ["DATAFLOWKIT_API_KEY", "DATAFLOWKIT_TOKEN"], - }, - "datasette": { - "display_name": "Datasette API", - "description": "Execute SQL queries against a Datasette database and return the results as JSON", - "spec_url": "https://api.apis.guru/v2/specs/datasette.local/v1/openapi.json", - "base_url": "https://datasette.io/-/chatgpt-openapi-schema.yml", - "env_vars": ["DATASETTE_API_KEY", "DATASETTE_TOKEN"], - }, - "datumbox": { - "display_name": "api.datumbox.com", - "description": "Datumbox offers a Machine Learning platform composed of 14 classifiers and Natural Language processing functions. Functions include sentiment analysis", - "spec_url": "https://api.apis.guru/v2/specs/datumbox.com/1.0/openapi.json", - "base_url": "http://www.datumbox.com/api-sandbox/api-docs", - "env_vars": ["BOX_ACCESS_TOKEN"], - }, - "deeparteffects": { - "display_name": "Deep Art Effects", - "spec_url": "https://api.apis.guru/v2/specs/deeparteffects.com/2017-02-10T162446Z/swagger.json", - "base_url": "http://docs.deeparteffects.com/swagger.json", - "env_vars": ["DEEPARTEFFECTS_API_KEY", "DEEPARTEFFECTS_TOKEN"], - }, - "departureboard": { - "display_name": "departureboard.io API", - "description": "The departureboard.io is a high performance API written in Golang. Its goal is to provide to main functions:

(1): A JSON API interface to the ", - "spec_url": "https://api.apis.guru/v2/specs/departureboard.io/2.0/openapi.json", - "base_url": "https://api.departureboard.io/openapi.json", - "env_vars": ["DEPARTUREBOARD_API_KEY", "DEPARTUREBOARD_TOKEN"], - }, - "deutschebahn-com-betriebsstellen": { - "display_name": "Betriebsstellen", - "description": "This REST-API enables you to query station and stop infos", - "spec_url": "https://api.apis.guru/v2/specs/deutschebahn.com/betriebsstellen/v1/swagger.json", - "base_url": "https://developer.deutschebahn.com/store/api-docs/DBOpenData/Betriebsstellen/v1", - "env_vars": ["DEUTSCHEBAHN_API_KEY"], - }, - "deutschebahn-com-fahrplan": { - "display_name": "Fahrplan-Free", - "description": "A RESTful webservice to request a railway journey - FREE plan with restricted access (max. 10 requests per minute). Please ignore the message in the A", - "spec_url": "https://api.apis.guru/v2/specs/deutschebahn.com/fahrplan/v1/swagger.json", - "base_url": "https://developer.deutschebahn.com/store/api-docs/DBOpenData/Fahrplan-Free/v1", - "env_vars": ["DEUTSCHEBAHN_API_KEY"], - }, - "deutschebahn-com-fasta": { - "display_name": "FaSta - Station Facilities Status", - "description": "A RESTful webservice to retrieve data about the operational state of public elevators and escalators in german railway stations.", - "spec_url": "https://api.apis.guru/v2/specs/deutschebahn.com/fasta/2.1/swagger.json", - "base_url": "https://developer.deutschebahn.com/store/api-docs/DBOpenData/FaSta-Station_Facilities_Status/v2", - "env_vars": ["DEUTSCHEBAHN_API_KEY"], - }, - "deutschebahn-com-flinkster": { - "display_name": "Flinkster_API_NG", - "description": "This REST-API enables you to query for private transport sharing offers provided by companies and cities in Germany, Netherland and Austria. You can s", - "spec_url": "https://api.apis.guru/v2/specs/deutschebahn.com/flinkster/v1/swagger.json", - "base_url": "https://developer.deutschebahn.com/store/api-docs/DBOpenData/Flinkster_API_NG/v1", - "env_vars": ["DEUTSCHEBAHN_API_KEY"], - }, - "deutschebahn-com-reisezentren": { - "display_name": "Reisezentren-API", - "description": "This REST-API enables you to query information about travel centers in Germany.", - "spec_url": "https://api.apis.guru/v2/specs/deutschebahn.com/reisezentren/v1/openapi.json", - "base_url": "https://developer.deutschebahn.com/store/api-docs/DBOpenData/Reisezentren/v1", - "env_vars": ["DEUTSCHEBAHN_API_KEY"], - }, - "deutschebahn-com-stada": { - "display_name": "Stationsdatenbereitstellung", - "description": "An API providing master data for German railway stations by DB Station&Service AG.", - "spec_url": "https://api.apis.guru/v2/specs/deutschebahn.com/stada/2.2.01/swagger.json", - "base_url": "https://developer.deutschebahn.com/store/api-docs/DBOpenData/StaDa-Station_Data/v2", - "env_vars": ["DEUTSCHEBAHN_API_KEY"], - }, - "dev-to": { - "display_name": "Forem API V1", - "description": "Access Forem articles, users and other resources via API. For a real-world example of Forem in action, check out [DEV](https://www.dev.to). All endpoi", - "spec_url": "https://api.apis.guru/v2/specs/dev.to/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/forem/forem/main/swagger/v1/api_v1.json", - "env_vars": ["DEV_TO_API_KEY", "DEV_TO_TOKEN"], - }, - "digitallinguistics": { - "display_name": "DLx", - "description": "The Digital Linguistics (DLx) REST API", - "spec_url": "https://api.apis.guru/v2/specs/digitallinguistics.io/0.3.1/swagger.json", - "base_url": "https://raw.githubusercontent.com/digitallinguistics/api/master/public/swagger/swagger.json", - "env_vars": ["DIGITALLINGUISTICS_API_KEY", "DIGITALLINGUISTICS_TOKEN"], - }, - "digitallocker-gov-in-authpartner": { - "display_name": "Authorized Partner API Specification", - "description": "To access files in userโ€™s DigiLocker account from your application, you must first obtain userโ€™s authorization.", - "spec_url": "https://api.apis.guru/v2/specs/digitallocker.gov.in/authpartner/1.0.0/openapi.json", - "base_url": "https://apisetu.gov.in/api_specification_v8/authpartner.yaml", - "env_vars": [ - "DIGITALLOCKER_GOV_IN_AUTHPARTNER_API_KEY", - "DIGITALLOCKER_GOV_IN_AUTHPARTNER_TOKEN", - ], - }, - "digitalnz": { - "display_name": "DigitalNZ API", - "description": "OpenAPI specification of DigitalNZ's Record API. For more information about the API see [digitalnz.org/developers](https://digitalnz.org/developers). ", - "spec_url": "https://api.apis.guru/v2/specs/digitalnz.org/3/openapi.json", - "base_url": "https://api.swaggerhub.com/apis/DigitalNZ/Records/3", - "env_vars": ["DIGITALNZ_API_KEY", "DIGITALNZ_TOKEN"], - }, - "digitalocean": { - "display_name": "DigitalOcean API", - "description": "# Introduction The DigitalOcean API allows you to manage Droplets and resources within the DigitalOcean cloud in a simple, programmatic way using conv", - "spec_url": "https://api.apis.guru/v2/specs/digitalocean.com/2.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/digitalocean/openapi/main/specification/DigitalOcean-public.v2.yaml", - "env_vars": ["DIGITALOCEAN_TOKEN", "DO_TOKEN"], - }, - "discourse": { - "display_name": "Discourse API Documentation", - "description": "This page contains the documentation on how to use Discourse through API calls. > Note: For any endpoints not listed you can follow the [reverse engin", - "spec_url": "https://api.apis.guru/v2/specs/discourse.local/latest/openapi.json", - "base_url": "http://docs.discourse.org/openapi.json", - "env_vars": ["DISCOURSE_API_KEY", "DISCOURSE_TOKEN"], - }, - "dnd5eapi-co": { - "display_name": "D&D 5e API", - "description": "# Introduction Welcome to the dnd5eapi, the Dungeons & Dragons 5th Edition API! This documentation should help you familiarize yourself with the resou", - "spec_url": "https://api.apis.guru/v2/specs/dnd5eapi.co/0.1/openapi.json", - "base_url": "https://www.dnd5eapi.co/swagger/openapi.json", - "env_vars": ["DND5EAPI_CO_API_KEY", "DND5EAPI_CO_TOKEN"], - }, - "docker-com-dvp": { - "display_name": "DVP Data API", - "description": "The Docker DVP Data API allows [Docker Verified Publishers](https://docs.docker.com/docker-hub/publish/) to view image pull analytics data for their n", - "spec_url": "https://api.apis.guru/v2/specs/docker.com/dvp/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/docker/docs/main/docker-hub/api/dvp.yaml", - "env_vars": ["DOCKER_COM_DVP_API_KEY", "DOCKER_COM_DVP_TOKEN"], - }, - "docker-com-engine": { - "display_name": "Docker Engine API", - "description": "The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker c", - "spec_url": "https://api.apis.guru/v2/specs/docker.com/engine/1.33/openapi.json", - "base_url": "https://raw.githubusercontent.com/docker/go-docker/master/api/swagger.yaml", - "env_vars": ["DOCKER_COM_ENGINE_API_KEY", "DOCKER_COM_ENGINE_TOKEN"], - }, - "docker-com-hub": { - "display_name": "Docker HUB API", - "description": "Docker Hub is a service provided by Docker for finding and sharing container images with your team. It is the world's largest library and community fo", - "spec_url": "https://api.apis.guru/v2/specs/docker.com/hub/beta/openapi.json", - "base_url": "https://raw.githubusercontent.com/docker/docs/main/docker-hub/api/latest.yaml", - "env_vars": ["DOCKER_COM_HUB_API_KEY", "DOCKER_COM_HUB_TOKEN"], - }, - "docusign": { - "display_name": "DocuSign REST API", - "description": "The DocuSign REST API provides you with a powerful, convenient, and simple Web services API for interacting with DocuSign.", - "spec_url": "https://api.apis.guru/v2/specs/docusign.net/v2.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/docusign/eSign-OpenAPI-Specification/master/esignature.rest.swagger-v2.1.json", - "env_vars": ["DOCUSIGN_API_KEY", "DOCUSIGN_TOKEN"], - }, - "dodo-ac": { - "display_name": "Nookipedia", - "description": "The Nookipedia API provides endpoints for retrieving *Animal Crossing* data pulled from the [Nookipedia wiki](https://nookipedia.com/wiki/Main_Page). ", - "spec_url": "https://api.apis.guru/v2/specs/dodo.ac/1.5.0/openapi.json", - "base_url": "https://api.nookipedia.com/static/doc.yaml", - "env_vars": ["DODO_AC_API_KEY", "DODO_AC_TOKEN"], - }, - "domainsdb-info": { - "display_name": "Domains-Index API", - "description": "Domains-Index database powered API", - "spec_url": "https://api.apis.guru/v2/specs/domainsdb.info/1.0/openapi.json", - "base_url": "https://api.domains-index.com/v1/swagger.json", - "env_vars": ["DOMAINSDB_INFO_API_KEY", "DOMAINSDB_INFO_TOKEN"], - }, - "doqs-dev": { - "display_name": "doqs.dev | PDF filling API", - "spec_url": "https://api.apis.guru/v2/specs/doqs.dev/1.0/openapi.json", - "base_url": "https://api.doqs.dev/v1/openapi.json", - "env_vars": ["DOQS_DEV_API_KEY", "DOQS_DEV_TOKEN"], - }, - "dracoon-team": { - "display_name": "DRACOON API", - "description": "REST Web Services for DRACOON

This page provides an overview of all available and documented DRACOON APIs, which are grouped by tags.
Each t", - "spec_url": "https://api.apis.guru/v2/specs/dracoon.team/4.42.2/openapi.json", - "base_url": "https://dracoon.team/api/spec_v4/", - "env_vars": ["DRACOON_TEAM_API_KEY", "DRACOON_TEAM_TOKEN"], - }, - "drchrono": { - "display_name": "", - "description": "This document is intended as a detailed reference for the precise behavior of the drchrono API. If this is your first time using the API, start with o", - "spec_url": "https://api.apis.guru/v2/specs/drchrono.com/v4 (Hunt Valley)/openapi.json", - "base_url": "https://drchrono.com/openapi-schema", - "env_vars": ["DRCHRONO_API_KEY", "DRCHRONO_TOKEN"], - }, - "dropx": { - "display_name": "DropX", - "description": "dropX.io API provides programmatic access to the e-commerce intelligence data.", - "spec_url": "https://api.apis.guru/v2/specs/dropx.io/1.0.0/swagger.json", - "base_url": "http://dropx.io/dropx-swagger.yaml", - "env_vars": ["DROPX_API_KEY", "DROPX_TOKEN"], - }, - "dweet": { - "display_name": "dweet.io", - "description": "Dweet.io allows users to share data from mobile, tablets, and pcs, and them to other devices and accounts across social media platforms. Dweet.io prov", - "spec_url": "https://api.apis.guru/v2/specs/dweet.io/2.0/swagger.json", - "base_url": "https://dweet.io/play/definition", - "env_vars": ["DWEET_API_KEY", "DWEET_TOKEN"], - }, - "easypdfserver": { - "display_name": "EasyPDFServer", - "description": "API for converting HTML to PDF.", - "spec_url": "https://api.apis.guru/v2/specs/easypdfserver.com/1/openapi.json", - "base_url": "https://www.easypdfserver.com/openapi.yaml", - "env_vars": ["EASYPDFSERVER_API_KEY", "EASYPDFSERVER_TOKEN"], - }, - "ebay-com-buy-browse": { - "display_name": "Browse API", - "description": "The Browse API has the following resources: item_summary: Lets shoppers search for specific items by keyword, GTIN, category, charity, product, or ite", - "spec_url": "https://api.apis.guru/v2/specs/ebay.com/buy-browse/v1.1.0/swagger.json", - "base_url": "https://developer.ebay.com/api-docs/master/buy/browse/openapi/2/buy_browse_v1_beta_oas2.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "ebay-com-buy-deal": { - "display_name": "Deal API", - "description": 'Note: This is a
Note: This is a Analytics API retrieves call-limit data and the quotas that are set for the RESTful APIs and the legacy Trading API.

Responses from", - "spec_url": "https://api.apis.guru/v2/specs/ebay.com/developer-analytics/v1_beta.0.0/openapi.json", - "base_url": "https://developer.ebay.com/api-docs/master/developer/analytics/openapi/3/developer_analytics_v1_beta_oas3.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "ebay-com-sell-account": { - "display_name": "Account API", - "description": "The Account API gives sellers the ability to configure their eBay seller accounts, including the seller's policies (eBay business policies and ", - "spec_url": "https://api.apis.guru/v2/specs/ebay.com/sell-account/v1.9.0/openapi.json", - "base_url": "https://developer.ebay.com/api-docs/master/sell/account/openapi/3/sell_account_v1_oas3.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "ebay-com-sell-analytics": { - "display_name": "Seller Service Metrics API", - "description": "The Analytics API provides data and information about a seller and their eBay business.

The resources and methods in this API let selle", - "spec_url": "https://api.apis.guru/v2/specs/ebay.com/sell-analytics/1.2.0/openapi.json", - "base_url": "https://developer.ebay.com/api-docs/master/sell/analytics/openapi/3/sell_analytics_v1_oas3.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "ebay-com-sell-compliance": { - "display_name": "Compliance API", - "description": "Service for providing information to sellers about their listings being non-compliant, or at risk for becoming non-compliant, against eBay listing pol", - "spec_url": "https://api.apis.guru/v2/specs/ebay.com/sell-compliance/1.4.1/openapi.json", - "base_url": "https://developer.ebay.com/api-docs/master/sell/compliance/openapi/3/sell_compliance_v1_oas3.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "ebay-com-sell-feed": { - "display_name": "Feed API", - "description": "

The Feed API lets sellers upload input files, download reports and files including their status, filter reports using URI paramete", - "spec_url": "https://api.apis.guru/v2/specs/ebay.com/sell-feed/v1.3.1/openapi.json", - "base_url": "https://developer.ebay.com/api-docs/master/sell/feed/openapi/3/sell_feed_v1_oas3.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "ebay-com-sell-fulfillment": { - "display_name": "Fulfillment API", - "description": "Use the Fulfillment API to complete the process of packaging, addressing, handling, and shipping each order on behalf of the seller, in accordance wit", - "spec_url": "https://api.apis.guru/v2/specs/ebay.com/sell-fulfillment/v1.19.19/openapi.json", - "base_url": "https://developer.ebay.com/api-docs/master/sell/fulfillment/openapi/3/sell_fulfillment_v1_oas3.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "ebay-com-sell-listing": { - "display_name": "Listing API", - "description": 'Note: This is a Note: This is a The Marketing API offers two platforms that sellers can use to promote and advertise their products:

  • Promoted Listings i", - "spec_url": "https://api.apis.guru/v2/specs/ebay.com/sell-marketing/v1.14.0/openapi.json", - "base_url": "https://developer.ebay.com/api-docs/master/sell/marketing/openapi/3/sell_marketing_v1_oas3.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "ebay-com-sell-metadata": { - "display_name": "Metadata API", - "description": "The Metadata API has operations that retrieve configuration details pertaining to the different eBay marketplaces. In addition to marketplace informat", - "spec_url": "https://api.apis.guru/v2/specs/ebay.com/sell-metadata/v1.6.0/openapi.json", - "base_url": "https://developer.ebay.com/api-docs/master/sell/metadata/openapi/3/sell_metadata_v1_oas3.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "ebay-com-sell-negotiation": { - "display_name": "Negotiation API", - "description": 'The Negotiations API gives sellers the ability to proactively send discount offers to buyers who have shown an "interest" in their listings. ', - "spec_url": "https://api.apis.guru/v2/specs/ebay.com/sell-negotiation/v1.1.0/openapi.json", - "base_url": "https://developer.ebay.com/api-docs/master/sell/negotiation/openapi/3/sell_negotiation_v1_oas3.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "ebay-com-sell-recommendation": { - "display_name": "Recommendation API", - "description": "The Recommendation API returns information that sellers can use to optimize the configuration of their listings on eBay.

    Currently, the", - "spec_url": "https://api.apis.guru/v2/specs/ebay.com/sell-recommendation/1.1.0/openapi.json", - "base_url": "https://developer.ebay.com/api-docs/master/sell/recommendation/openapi/3/sell_recommendation_v1_oas3.json", - "env_vars": ["EBAY_APP_ID", "EBAY_DEV_ID"], - }, - "ebi-ac-uk": { - "display_name": "CROssBAR Data API", - "description": "# About CROssBAR & data **CROssBAR**: Comprehensive Resource of Biomedical Relations with Deep Learning Applications and Knowledge Graph Representatio", - "spec_url": "https://api.apis.guru/v2/specs/ebi.ac.uk/1.0/swagger.json", - "base_url": "https://www.ebi.ac.uk/Tools/crossbar/v2/api-docs", - "env_vars": ["EBI_AC_UK_API_KEY", "EBI_AC_UK_TOKEN"], - }, - "edrv": { - "display_name": "eDRV API", - "description": "edrv.io API Documentation", - "spec_url": "https://api.apis.guru/v2/specs/edrv.io/v1/openapi.json", - "base_url": "https://developers.edrv.io/openapi/5f15c43c87b0d6001ea97414", - "env_vars": ["EDRV_API_KEY", "EDRV_TOKEN"], - }, - "elevenlabs": { - "display_name": "ElevenLabs API Documentation", - "description": "This is the documentation for the ElevenLabs API. You can use this API to use our service programmatically, this is done by using your xi-api-key.
    For additional help getting st", - "spec_url": "https://api.apis.guru/v2/specs/elmah.io/v3/openapi.json", - "base_url": "https://api.elmah.io/swagger/docs/v3", - "env_vars": ["ELMAH_API_KEY", "ELMAH_TOKEN"], - }, - "enode": { - "display_name": "Enode API", - "description": "Download [OpenAPI 3.0 Specification](/OpenAPI-Enode-v1.4.0.json) Download [Postman Collection](/Postman-Enode-v1.4.0.json) The Enode API is designed t", - "spec_url": "https://api.apis.guru/v2/specs/enode.io/1.3.10/openapi.json", - "base_url": "https://docs.enode.io/OpenAPI-Enode-v1.3.10.json", - "env_vars": ["ENODE_API_KEY", "ENODE_TOKEN"], - }, - "envoice-in": { - "display_name": "API v1.0.0", - "description": "[![Run in Postman](https://run.pstmn.io/button.svg)](https://app.getpostman.com/run-collection/80638214aa04722c9203)

    Overview

    FIRST/FMS FRC Events API is a service to return relevant information about the ', - "spec_url": "https://api.apis.guru/v2/specs/firstinspires.org/1.0.0/openapi.json", - "base_url": "https://frc-api-docs.firstinspires.org/api/collections/13920602/TW6zHTEi?segregateAuth=true&versionTag=latest", - "env_vars": ["FIRSTINSPIRES_API_KEY", "FIRSTINSPIRES_TOKEN"], - }, - "fisheye": { - "display_name": "FishEye", - "spec_url": "https://api.apis.guru/v2/specs/fisheye.local/1.0.0/swagger.json", - "base_url": "https://docs.atlassian.com/fisheye-crucible/latest_backup/wadl/fisheye.wadl", - "env_vars": ["FISHEYE_API_KEY", "FISHEYE_TOKEN"], - }, - "flat": { - "display_name": "Flat API", - "description": "The Flat API allows you to easily extend the abilities of the [Flat Platform](https://flat.io), with a wide range of use cases including the following", - "spec_url": "https://api.apis.guru/v2/specs/flat.io/2.13.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/FlatIO/api-reference/master/spec/swagger.yaml", - "env_vars": ["FLAT_API_KEY", "FLAT_TOKEN"], - }, - "flickr": { - "display_name": "Flickr API Schema", - "description": "A subset of Flickr's API defined in Swagger format.", - "spec_url": "https://api.apis.guru/v2/specs/flickr.com/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/flickr/flickr-api-swagger/master/dist/schema.json", - "env_vars": ["FLICKR_API_KEY", "FLICKR_TOKEN"], - }, - "formapi": { - "display_name": "API v1", - "description": "DocSpring is a service that helps you fill out and sign PDF templates.", - "spec_url": "https://api.apis.guru/v2/specs/formapi.io/v1/openapi.json", - "base_url": "https://docspring.com/api-docs/v1/swagger.json", - "env_vars": ["FORMAPI_API_KEY", "FORMAPI_TOKEN"], - }, - "frankiefinancial": { - "display_name": "Frankie Financial API", - "description": "------ This API allows developers to integrate the Frankie Financial Compliance Utility into their applications. The API allows: - Checking name, addr", - "spec_url": "https://api.apis.guru/v2/specs/frankiefinancial.io/1.5.3/swagger.json", - "base_url": "https://app.swaggerhub.com/apiproxy/registry/FrankieFinancial/kycutility/1.5.3", - "env_vars": ["FRANKIEFINANCIAL_API_KEY", "FRANKIEFINANCIAL_TOKEN"], - }, - "fraudlabspro-com-fraud-detection": { - "display_name": "FraudLabs Pro Fraud Detection", - "description": "Online payment fraud detection service. It helps merchants to minimize chargebacks and therefore maximize the revenue. It can be used to detect fraud ", - "spec_url": "https://api.apis.guru/v2/specs/fraudlabspro.com/fraud-detection/1.1/openapi.json", - "base_url": "https://app.swaggerhub.com/apiproxy/schema/file/fraudlabspro/fraudlabspro-fraud-detection/1.1/swagger.json", - "env_vars": [ - "FRAUDLABSPRO_COM_FRAUD_DETECTION_API_KEY", - "FRAUDLABSPRO_COM_FRAUD_DETECTION_TOKEN", - ], - }, - "fraudlabspro-com-sms-verification": { - "display_name": "FraudLabs Pro SMS Verification", - "description": "Send an SMS with verification code and a custom message for authentication purpose. It helps merchants to minimize chargebacks and fraud for various k", - "spec_url": "https://api.apis.guru/v2/specs/fraudlabspro.com/sms-verification/1.0/openapi.json", - "base_url": "https://api.swaggerhub.com/apis/fraudlabs-pro/fraudlabspro-sms-verification/1.0", - "env_vars": [ - "FRAUDLABSPRO_COM_SMS_VERIFICATION_API_KEY", - "FRAUDLABSPRO_COM_SMS_VERIFICATION_TOKEN", - ], - }, - "freesound": { - "display_name": "Freesound", - "description": "With the Freesound APIv2 you can browse, search, and retrieve information about Freesound users, packs, and the sounds themselves of course. You can f", - "spec_url": "https://api.apis.guru/v2/specs/freesound.org/2.0.0/swagger.json", - "base_url": "http://miguel76.github.io/freesound-openapi/swagger.json", - "env_vars": ["FREESOUND_API_KEY", "FREESOUND_TOKEN"], - }, - "freetv-app": { - "display_name": "News Plugin", - "description": "A plugin that allows the user to obtain and summary latest news using ChatGPT. If you do not know the user's username, ask them first before making qu", - "spec_url": "https://api.apis.guru/v2/specs/freetv-app.com/v1/openapi.json", - "base_url": "https://www.freetv-app.com/openapi.json", - "env_vars": ["FREETV_APP_API_KEY", "FREETV_APP_TOKEN"], - }, - "fulfillment": { - "display_name": "Fulfillment.com APIv2", - "description": "Welcome to our current iteration of our REST API. While we encourage you to upgrade to v2.0 we will continue support for our [SOAP API](https://github", - "spec_url": "https://api.apis.guru/v2/specs/fulfillment.com/2.0/openapi.json", - "base_url": "https://fulfillment.github.io/api/openapi.json", - "env_vars": ["FULFILLMENT_API_KEY", "FULFILLMENT_TOKEN"], - }, - "fungenerators-com-barcode": { - "display_name": "Barcode API", - "description": "Generate Barcode images for a given barcode number. You can decode Barcode images and get the barcodes in a numberic form as well. Many industry stand", - "spec_url": "https://api.apis.guru/v2/specs/fungenerators.com/barcode/1.5/openapi.json", - "base_url": "https://fungenerators.com/yaml/barcode.yaml", - "env_vars": [ - "FUNGENERATORS_COM_BARCODE_API_KEY", - "FUNGENERATORS_COM_BARCODE_TOKEN", - ], - }, - "fungenerators-com-fake-identity": { - "display_name": "Fake identity generation API", - "description": "Generate random fake identities (name, address, email, phone , credit card info etc). [Click here to subscribe](http://fungenerators.com/api/fakeident", - "spec_url": "https://api.apis.guru/v2/specs/fungenerators.com/fake-identity/1.5/swagger.json", - "base_url": "https://fungenerators.com/yaml/fake-identity.yaml", - "env_vars": [ - "FUNGENERATORS_COM_FAKE_IDENTITY_API_KEY", - "FUNGENERATORS_COM_FAKE_IDENTITY_TOKEN", - ], - }, - "fungenerators-com-lottery": { - "display_name": "Random Lottery Number generator API", - "description": "Below is the documentation for the API calls. You can try them out right here.", - "spec_url": "https://api.apis.guru/v2/specs/fungenerators.com/lottery/1.5/swagger.json", - "base_url": "https://fungenerators.com/yaml/lottery.yaml", - "env_vars": [ - "FUNGENERATORS_COM_LOTTERY_API_KEY", - "FUNGENERATORS_COM_LOTTERY_TOKEN", - ], - }, - "fungenerators-com-namegen": { - "display_name": "Name Generation API", - "description": "Fungenerators name generation API generates random names relevant to the given category. Lots of categories are supported with many variations support", - "spec_url": "https://api.apis.guru/v2/specs/fungenerators.com/namegen/1.5/swagger.json", - "base_url": "https://fungenerators.com/yaml/namegen.yaml", - "env_vars": [ - "FUNGENERATORS_COM_NAMEGEN_API_KEY", - "FUNGENERATORS_COM_NAMEGEN_TOKEN", - ], - }, - "fungenerators-com-pirate": { - "display_name": "Pirates API", - "description": "Ahoy matey! We help the landlubbers to get to know about the seamen way! You can generate pirate names, get some real pirate insults and pirate filler", - "spec_url": "https://api.apis.guru/v2/specs/fungenerators.com/pirate/1.5/openapi.json", - "base_url": "https://fungenerators.com/yaml/pirate.yaml", - "env_vars": [ - "FUNGENERATORS_COM_PIRATE_API_KEY", - "FUNGENERATORS_COM_PIRATE_TOKEN", - ], - }, - "fungenerators-com-qrcode": { - "display_name": "Fun Generators API", - "description": "Fungenerators API gives access to the full set of generators available at fungenerators.com so that you can integrate them in your workflow or an app.", - "spec_url": "https://api.apis.guru/v2/specs/fungenerators.com/qrcode/1.5/swagger.json", - "base_url": "https://fungenerators.com/yaml/qrcode.yaml", - "env_vars": [ - "FUNGENERATORS_COM_QRCODE_API_KEY", - "FUNGENERATORS_COM_QRCODE_TOKEN", - ], - }, - "fungenerators-com-random-facts": { - "display_name": "Facts API", - "description": "A full featured Facts API. REST access with json/xml/jsonp result support. On this day birth and death support, random fact, keyword search support et", - "spec_url": "https://api.apis.guru/v2/specs/fungenerators.com/random-facts/1.5/openapi.json", - "base_url": "https://fungenerators.com/yaml/facts.yaml", - "env_vars": [ - "FUNGENERATORS_COM_RANDOM_FACTS_API_KEY", - "FUNGENERATORS_COM_RANDOM_FACTS_TOKEN", - ], - }, - "fungenerators-com-riddle": { - "display_name": "Fun Generators API", - "description": "Below is the documentation for the API calls. You can try them out right here.", - "spec_url": "https://api.apis.guru/v2/specs/fungenerators.com/riddle/1.5/openapi.json", - "base_url": "https://fungenerators.com/yaml/riddle.yaml", - "env_vars": [ - "FUNGENERATORS_COM_RIDDLE_API_KEY", - "FUNGENERATORS_COM_RIDDLE_TOKEN", - ], - }, - "fungenerators-com-shakespeare": { - "display_name": "Shakespeare API", - "description": "Shakespeare API. Generate random Shakespeare quotes, names, insults, lorem ipsum etc. Translate normal English to Shakespeare English. [Click here to ", - "spec_url": "https://api.apis.guru/v2/specs/fungenerators.com/shakespeare/1.5/openapi.json", - "base_url": "https://fungenerators.com/yaml/shakespeare.yaml", - "env_vars": [ - "FUNGENERATORS_COM_SHAKESPEARE_API_KEY", - "FUNGENERATORS_COM_SHAKESPEARE_TOKEN", - ], - }, - "fungenerators-com-taunt": { - "display_name": "Taunt as a service", - "description": "Fungenerators taunt generation API generates random taunts / insults, relevant to the given category. Many categories are supported and new ones are a", - "spec_url": "https://api.apis.guru/v2/specs/fungenerators.com/taunt/1.5/swagger.json", - "base_url": "https://fungenerators.com/yaml/taunt.yaml", - "env_vars": [ - "FUNGENERATORS_COM_TAUNT_API_KEY", - "FUNGENERATORS_COM_TAUNT_TOKEN", - ], - }, - "fungenerators-com-trivia": { - "display_name": "Fun Generators API", - "description": "Below is the documentation for the API calls. You can try them out right here.", - "spec_url": "https://api.apis.guru/v2/specs/fungenerators.com/trivia/1.5/swagger.json", - "base_url": "https://fungenerators.com/yaml/trivia.yaml", - "env_vars": [ - "FUNGENERATORS_COM_TRIVIA_API_KEY", - "FUNGENERATORS_COM_TRIVIA_TOKEN", - ], - }, - "fungenerators-com-uuid": { - "display_name": "UUID Generation API", - "description": "A full featured, REST based UUID generator with json/xml/jsonp result support. You can try them out right here. [Click here to subscribe](http://funge", - "spec_url": "https://api.apis.guru/v2/specs/fungenerators.com/uuid/1.5/openapi.json", - "base_url": "https://fungenerators.com/yaml/uuid.yaml", - "env_vars": ["FUNGENERATORS_COM_UUID_API_KEY", "FUNGENERATORS_COM_UUID_TOKEN"], - }, - "funtranslations-com-braile": { - "display_name": "FunTranslations Braille API", - "description": "Braille conversion API on the cloud. Translate from English text to Braille and get Braille results suitable for many display types.[Click here to sub", - "spec_url": "https://api.apis.guru/v2/specs/funtranslations.com/braile/2.3/swagger.json", - "base_url": "https://funtranslations.com/yaml/funtranslations.braille.yaml", - "env_vars": [ - "FUNTRANSLATIONS_COM_BRAILE_API_KEY", - "FUNTRANSLATIONS_COM_BRAILE_TOKEN", - ], - }, - "funtranslations-com-index": { - "display_name": "FunTranslations API", - "description": "Funtranslations API gives access to the full set of translations available at funtranslations.com so that you can integrate them in your workflow or a", - "spec_url": "https://api.apis.guru/v2/specs/funtranslations.com/index/2.3/swagger.json", - "base_url": "http://api.funtranslations.com/yaml/funtranslations.yaml", - "env_vars": [ - "FUNTRANSLATIONS_COM_INDEX_API_KEY", - "FUNTRANSLATIONS_COM_INDEX_TOKEN", - ], - }, - "funtranslations-com-starwars": { - "display_name": "Starwars Translations API", - "description": "Funtranslations Starwars API gives access to the full set of starwars language translations available at funtranslations.com so that you can integrate", - "spec_url": "https://api.apis.guru/v2/specs/funtranslations.com/starwars/2.3/swagger.json", - "base_url": "https://funtranslations.com/yaml/funtranslations.starwars.yaml", - "env_vars": [ - "FUNTRANSLATIONS_COM_STARWARS_API_KEY", - "FUNTRANSLATIONS_COM_STARWARS_TOKEN", - ], - }, - "furkot": { - "display_name": "Furkot Trips", - "description": "Furkot provides Rest API to access user trip data. Using Furkot API an application can list user trips and display stops for a specific trip. Furkot A", - "spec_url": "https://api.apis.guru/v2/specs/furkot.com/1.0.0/swagger.json", - "base_url": "https://help.furkot.com/widgets/furkot-api.yaml", - "env_vars": ["FURKOT_API_KEY", "FURKOT_TOKEN"], - }, - "gambitcomm-local-mimic": { - "display_name": "MIMIC REST API", - "description": "This is the API for MIMIC client to connect to MIMIC daemon.", - "spec_url": "https://api.apis.guru/v2/specs/gambitcomm.local/mimic/21.00/openapi.json", - "base_url": "https://www.gambitcomm.com/docs/mimic.yaml", - "env_vars": ["GAMBITCOMM_LOCAL_MIMIC_API_KEY", "GAMBITCOMM_LOCAL_MIMIC_TOKEN"], - }, - "gamesparks-net-game-details": { - "display_name": "GameSparks Game Details API", - "description": "The API to manage the GameSparks game details", - "spec_url": "https://api.apis.guru/v2/specs/gamesparks.net/game-details/v2/openapi.json", - "base_url": "https://config2.gamesparks.net/restv2/admin/api/schema", - "env_vars": [ - "GAMESPARKS_NET_GAME_DETAILS_API_KEY", - "GAMESPARKS_NET_GAME_DETAILS_TOKEN", - ], - }, - "geneea": { - "display_name": "Geneea Natural Language Processing", - "description": '

    Authentication

    For all calls, supply your API key. Sign up to <', - "spec_url": "https://api.apis.guru/v2/specs/geneea.com/1.0/swagger.json", - "base_url": "https://api.geneea.com/api-docs?group=s1", - "env_vars": ["GENEEA_API_KEY", "GENEEA_TOKEN"], - }, - "geodatasource": { - "display_name": "GeoDataSource Location Search", - "description": "GeoDataSourceโ„ข Web Service is a REST API enable user to lookup for a city by using latitude and longitude coordinate. It will return the result in eit", - "spec_url": "https://api.apis.guru/v2/specs/geodatasource.com/1.0/openapi.json", - "base_url": "https://app.swaggerhub.com/apiproxy/schema/file/geodatasource/geodatasource-location-search/1.0/swagger.yaml", - "env_vars": ["GEODATASOURCE_API_KEY", "GEODATASOURCE_TOKEN"], - }, - "geodesystems": { - "display_name": "geodesystems.com:443", - "description": "Search for data in lots of places - manage your documents, photos and critical business knowledge - communicate and coordinate with blogs, interactive", - "spec_url": "https://api.apis.guru/v2/specs/geodesystems.com/1.0.0/openapi.json", - "base_url": "https://geodesystems.com/repository/swagger/api-docs", - "env_vars": ["GEODESYSTEMS_API_KEY", "GEODESYSTEMS_TOKEN"], - }, - "gerermesaffaires": { - "display_name": "GererMesAffaires {REST:API}", - "description": "Sรฉcurisez vos donnรฉes en interfaรงant votre logiciel mรฉtier avec le service en ligne GererMesAffaires", - "spec_url": "https://api.apis.guru/v2/specs/gerermesaffaires.com/1.0.6/openapi.json", - "base_url": "https://api.gerermesaffaires.com/wp-content/uploads/2022/09/GmaAPI-GererMesAffairesAPI-1.0.6-swagger.json", - "env_vars": ["GERERMESAFFAIRES_API_KEY", "GERERMESAFFAIRES_TOKEN"], - }, - "getgo-com-gototraining": { - "display_name": "GoToTraining", - "description": "The GoToTraining API enables developers to use the stable and robust GoToTraining functionality as the basis for online trainings in a proprietary lea", - "spec_url": "https://api.apis.guru/v2/specs/getgo.com/gototraining/1.0.0/swagger.json", - "base_url": "https://developer.citrixonline.com/sites/default/files/citrix/citrix-apis/gototraining.json", - "env_vars": ["GETGO_COM_GOTOTRAINING_API_KEY", "GETGO_COM_GOTOTRAINING_TOKEN"], - }, - "getgo-com-gotowebinar": { - "display_name": "GoToWebinar", - "description": "The GoToWebinar API provides seamless integration of webinar registrant and attendee data into your existing infrastructure or third-party application", - "spec_url": "https://api.apis.guru/v2/specs/getgo.com/gotowebinar/1.0.0/swagger.json", - "base_url": "https://developer.citrixonline.com/sites/default/files/citrix/citrix-apis/gotowebinar.json", - "env_vars": ["GETGO_COM_GOTOWEBINAR_API_KEY", "GETGO_COM_GOTOWEBINAR_TOKEN"], - }, - "getpostman": { - "display_name": "Postman API", - "description": "The Postman API allows you to programmatically access data stored in Postman account with ease. The easiest way to get started with the API is to clic", - "spec_url": "https://api.apis.guru/v2/specs/getpostman.com/1.20.0/openapi.json", - "base_url": "https://gist.githubusercontent.com/MikeRalphson/f5dd7e7e712a4f2caa8f1783f1053dbc/raw/05fb7ae8b877b37d93413a0b8183bf60c2e1bdfe/postman-api.yaml", - "env_vars": ["GETPOSTMAN_API_KEY", "GETPOSTMAN_TOKEN"], - }, - "getsandbox": { - "display_name": "Sandbox API", - "description": "Sandbox API", - "spec_url": "https://api.apis.guru/v2/specs/getsandbox.com/v1/swagger.json", - "base_url": "https://getsandbox.com/lib/js/vendor/swagger/swagger.json", - "env_vars": ["BOX_ACCESS_TOKEN"], - }, - "getthedata-com-bng2latlong": { - "display_name": "bng2latlong", - "description": "Convert an OSGB36 easting and northing (British National Grid) to WGS84 latitude and longitude.", - "spec_url": "https://api.apis.guru/v2/specs/getthedata.com/bng2latlong/1.0/openapi.json", - "base_url": "https://www.getthedata.com/bng2latlong/openapi", - "env_vars": [ - "GETTHEDATA_COM_BNG2LATLONG_API_KEY", - "GETTHEDATA_COM_BNG2LATLONG_TOKEN", - ], - }, - "gettyimages": { - "display_name": "Getty Images", - "spec_url": "https://api.apis.guru/v2/specs/gettyimages.com/3/openapi.json", - "base_url": "https://api.gettyimages.com/swagger/v3/swagger.json", - "env_vars": ["GETTYIMAGES_API_KEY", "GETTYIMAGES_TOKEN"], - }, - "giphy": { - "display_name": "Giphy API", - "description": "Giphy API", - "spec_url": "https://api.apis.guru/v2/specs/giphy.com/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/faragorn/open-api-specs/master/specs/giphy_api/1.0/index.yml", - "env_vars": ["GIPHY_API_KEY"], - }, - "gisgraphy": { - "display_name": "Gisgraphy webservices", - "description": "Since 2006, [Gisgraphy](http://www.gisgraphy.com) is a free, open source framework that offers the possibility to do geolocalisation and geocoding via", - "spec_url": "https://api.apis.guru/v2/specs/gisgraphy.com/4.0.0/swagger.json", - "base_url": "http://www.gisgraphy.com/documentation/gisgraphy-swagger.json", - "env_vars": ["GISGRAPHY_API_KEY", "GISGRAPHY_TOKEN"], - }, - "gitea": { - "display_name": "Gitea API.", - "description": "This documentation describes the Gitea API.", - "spec_url": "https://api.apis.guru/v2/specs/gitea.io/1.20.0+dev-93-g6886706f5/openapi.json", - "base_url": "https://try.gitea.io/swagger.v1.json", - "env_vars": ["GITEA_API_KEY", "GITEA_TOKEN"], - }, - "github": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-api-github": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/api.github.com/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-api-github-com-2022-11-28": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/api.github.com.2022-11-28/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.2022-11-28.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghec": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghec/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghec/ghec.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghec-2022-11-28": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghec.2022-11-28/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghec/ghec.2022-11-28.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghes-2-18": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghes-2.18/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghes-2.18/ghes-2.18.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghes-2-19": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghes-2.19/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghes-2.19/ghes-2.19.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghes-2-20": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghes-2.20/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghes-2.20/ghes-2.20.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghes-2-21": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghes-2.21/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghes-2.21/ghes-2.21.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghes-2-22": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghes-2.22/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghes-2.22/ghes-2.22.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghes-3-0": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghes-3.0/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghes-3.0/ghes-3.0.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghes-3-1": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghes-3.1/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghes-3.1/ghes-3.1.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghes-3-2": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghes-3.2/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghes-3.2/ghes-3.2.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghes-3-3": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghes-3.3/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghes-3.3/ghes-3.3.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghes-3-4": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghes-3.4/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghes-3.4/ghes-3.4.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghes-3-5": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghes-3.5/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghes-3.5/ghes-3.5.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghes-3-6": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghes-3.6/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghes-3.6/ghes-3.6.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghes-3-7": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghes-3.7/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghes-3.7/ghes-3.7.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-ghes-3-8": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/ghes-3.8/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghes-3.8/ghes-3.8.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "github-com-github-ae": { - "display_name": "GitHub v3 REST API", - "description": "GitHub's v3 REST API.", - "spec_url": "https://api.apis.guru/v2/specs/github.com/github.ae/1.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/github.ae/github.ae.json", - "env_vars": ["GITHUB_TOKEN", "GH_TOKEN"], - "endpoints_count": 1078, - }, - "gitlab": { - "display_name": "Gitlab", - "description": "The platform for modern developers GitLab unifies issues, code review, CI and CD into a single UI", - "spec_url": "https://api.apis.guru/v2/specs/gitlab.com/v3/swagger.json", - "base_url": "https://axil.gitlab.io/swaggerapi/static/swagger.json", - "env_vars": ["GITLAB_TOKEN"], - }, - "globalwinescore": { - "display_name": "GlobalWineScore API Documentation", - "description": "The GlobalWineScore API is designed as a RESTful API, providing several resources and methods depending on your usage plan. For further information pl", - "spec_url": "https://api.apis.guru/v2/specs/globalwinescore.com/8234aab51481d37a30757d925b7f4221a659427e/openapi.json", - "base_url": "https://globalwinescore.docs.apiary.io/api-description-document", - "env_vars": ["GLOBALWINESCORE_API_KEY", "GLOBALWINESCORE_TOKEN"], - }, - "go-upc": { - "display_name": "Go-UPC Barcode-Lookup API", - "description": "Find information on products from around the globe. The API supports UPC, EAN, and ISBN barcode numbers, and info returned includes product name, desc", - "spec_url": "https://api.apis.guru/v2/specs/go-upc.com/1.0.0/openapi.json", - "base_url": "https://gist.githubusercontent.com/blizzrdof77/c3aa75284830179b44acc1aebd236e1a/raw/437d7bbb822f069efd700f1081a55f421bb3453d/go-upc-barcode-api-definition.yaml", - "env_vars": ["GO_UPC_API_KEY", "GO_UPC_TOKEN"], - }, - "goog": { - "display_name": "goog.io | Unoffical Google Search API", - "description": "# Intoduction This is the OpenAPI V3 documentation for https://api.goog.io An API to perform Google Searches. Extremely fast and accurate. Zero proxie", - "spec_url": "https://api.apis.guru/v2/specs/goog.io/0.1.0/openapi.json", - "base_url": "https://goog.io/openapi.json", - "env_vars": ["GOOG_API_KEY", "GOOG_TOKEN"], - }, - "graphhopper": { - "display_name": "GraphHopper Directions API", - "description": "With the [GraphHopper Directions API](https://www.graphhopper.com/products/) you can integrate A-to-B route planning, turn-by-turn navigation, route o", - "spec_url": "https://api.apis.guru/v2/specs/graphhopper.com/1.0.0/openapi.json", - "base_url": "https://docs.graphhopper.com/openapi.json", - "env_vars": ["GRAPHHOPPER_API_KEY", "GRAPHHOPPER_TOKEN"], - }, - "greenpeace": { - "display_name": "Greenwire Public API", - "description": "Greenpeace Greenwire allows you connect with other volunteers, activists and groups working on environmental campaigns all across the world!", - "spec_url": "https://api.apis.guru/v2/specs/greenpeace.org/1.0.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/greenpeace/ggw_api_test/master/swagger.yaml", - "env_vars": ["GREENPEACE_API_KEY", "GREENPEACE_TOKEN"], - }, - "greip": { - "display_name": "Greip API", - "description": "This documentation shows how to use Greip API, By highlighting the API methods, options and some other features that allow you to get the most of this", - "spec_url": "https://api.apis.guru/v2/specs/greip.io/1.0.0/openapi.json", - "base_url": "https://greip.io/OpenAPI.json", - "env_vars": ["GREIP_API_KEY", "GREIP_TOKEN"], - }, - "groundhog-day": { - "display_name": "Groundhog Day API", - "description": "This API returns all of North Americaโ€™s prognosticating animals and their yearly weather predictions.", - "spec_url": "https://api.apis.guru/v2/specs/groundhog-day.com/1.2.1/openapi.json", - "base_url": "https://api.swaggerhub.com/apis/pcraig3/groundhog-day-api/1.2.1", - "env_vars": ["GROUNDHOG_DAY_API_KEY", "GROUNDHOG_DAY_TOKEN"], - }, - "gsa-gov": { - "display_name": "Discovery Market Research", - "description": '

    This API drives the Discovery Market Research Tool. It contains information on the vendors that are part ', - "spec_url": "https://api.apis.guru/v2/specs/gsa.gov/0.1/swagger.json", - "base_url": "https://discovery.gsa.gov/docs/api-docs/", - "env_vars": ["GSA_GOV_API_KEY", "GSA_GOV_TOKEN"], - }, - "gsmtasks": { - "display_name": "GSMTasks Project API", - "description": "The GSMtasks API is a RESTful web service for developers to programmatically interact with GSMtasks data, real-time delivery and task management and r", - "spec_url": "https://api.apis.guru/v2/specs/gsmtasks.com/2.4.13/openapi.json", - "base_url": "https://raw.githubusercontent.com/oeklo/gsmtasks-schema/v2.4.13/GSMTasks.yaml", - "env_vars": ["GSMTASKS_API_KEY", "GSMTASKS_TOKEN"], - }, - "hackathonwatch": { - "display_name": "HackathonWatch", - "spec_url": "https://api.apis.guru/v2/specs/hackathonwatch.com/0.1/openapi.json", - "base_url": "http://www.hackathonwatch.com/api/swagger_doc", - "env_vars": ["HACKATHONWATCH_API_KEY", "HACKATHONWATCH_TOKEN"], - }, - "haloapi-com-metadata": { - "display_name": "Metadata", - "description": "API that provides Metadata information.", - "spec_url": "https://api.apis.guru/v2/specs/haloapi.com/metadata/1.0/swagger.json", - "base_url": "https://developer.haloapi.com/docs/services/58ace18c21091812784ce8c5/export?DocumentFormat=Swagger", - "env_vars": ["HALOAPI_COM_METADATA_API_KEY", "HALOAPI_COM_METADATA_TOKEN"], - }, - "haloapi-com-profile": { - "display_name": "Profile", - "description": "API that provides Profile information about Players.", - "spec_url": "https://api.apis.guru/v2/specs/haloapi.com/profile/1.0/swagger.json", - "base_url": "https://developer.haloapi.com/docs/services/58acdc2e21091812784ce8c2/export?DocumentFormat=Swagger", - "env_vars": ["HALOAPI_COM_PROFILE_API_KEY", "HALOAPI_COM_PROFILE_TOKEN"], - }, - "haloapi-com-stats": { - "display_name": "Stats", - "description": "API that provides statistical data about Players and Matches.", - "spec_url": "https://api.apis.guru/v2/specs/haloapi.com/stats/1.0/swagger.json", - "base_url": "https://developer.haloapi.com/docs/services/58acdf27e2f7f71ad0dad84b/export?DocumentFormat=Swagger", - "env_vars": ["HALOAPI_COM_STATS_API_KEY", "HALOAPI_COM_STATS_TOKEN"], - }, - "haloapi-com-ugc": { - "display_name": "UGC", - "description": "API that provides Metadata about User-Generated Content (Maps and Game Variants).", - "spec_url": "https://api.apis.guru/v2/specs/haloapi.com/ugc/1.0/swagger.json", - "base_url": "https://developer.haloapi.com/docs/services/58acde2921091812784ce8c3/export?DocumentFormat=Swagger", - "env_vars": ["HALOAPI_COM_UGC_API_KEY", "HALOAPI_COM_UGC_TOKEN"], - }, - "handwrytten": { - "display_name": "Handwrytten API", - "description": "This is the Handwrytten API for sending cards written in the handwriting of your choice. Using this api, you can send cards to users. You can also cus", - "spec_url": "https://api.apis.guru/v2/specs/handwrytten.com/1.0.0/swagger.json", - "base_url": "https://api.swaggerhub.com/apis/Handwrytten/handwrytten/1.0.0", - "env_vars": ["HANDWRYTTEN_API_KEY", "HANDWRYTTEN_TOKEN"], - }, - "healthcare-gov": { - "display_name": "Healthcare", - "spec_url": "https://api.apis.guru/v2/specs/healthcare.gov/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/APIs-guru/unofficial_openapi_specs/master/healthcare.gov/1.0.0/swagger.yaml", - "env_vars": ["HEALTHCARE_GOV_API_KEY", "HEALTHCARE_GOV_TOKEN"], - }, - "here-com-positioning": { - "display_name": "HERE Network Positioning API v2", - "description": "Positioning API accepts requests with radio network measurements and replies with corresponding location estimate. For more details and examples, see ", - "spec_url": "https://api.apis.guru/v2/specs/here.com/positioning/2.1.1/openapi.json", - "base_url": "https://developer.here.com/documentation/positioning-api/swagger/positioning-v2-external-spec.yaml", - "env_vars": ["HERE_API_KEY"], - }, - "here-com-tracking": { - "display_name": "HERE Tracking", - "description": "HERE Tracking is a cloud product designed to address location tracking problems for a wide range of Location IoT industry verticals. HERE Tracking als", - "spec_url": "https://api.apis.guru/v2/specs/here.com/tracking/2.1.191/openapi.json", - "base_url": "https://developer.here.com/documentation/tracking/swagger/swagger.json", - "env_vars": ["HERE_API_KEY"], - }, - "hetras-certification-net-booking": { - "display_name": "hetras Booking API Version 0", - "spec_url": "https://api.apis.guru/v2/specs/hetras-certification.net/booking/v0/swagger.json", - "base_url": "https://developer.hetras.com/swagger/spec/BookingAPIv0.json", - "env_vars": [ - "HETRAS_CERTIFICATION_NET_BOOKING_API_KEY", - "HETRAS_CERTIFICATION_NET_BOOKING_TOKEN", - ], - }, - "hetras-certification-net-hotel": { - "display_name": "hetras Hotel API Version 0", - "spec_url": "https://api.apis.guru/v2/specs/hetras-certification.net/hotel/v0/swagger.json", - "base_url": "https://developer.hetras.com/swagger/spec/HotelAPIv0.json", - "env_vars": [ - "HETRAS_CERTIFICATION_NET_HOTEL_API_KEY", - "HETRAS_CERTIFICATION_NET_HOTEL_TOKEN", - ], - }, - "hetzner-cloud": { - "display_name": "Hetzner Cloud API", - "description": "This is the official API documentation for the Public Hetzner Cloud. ## Introduction The Hetzner Cloud API operates over HTTPS and uses JSON as its da", - "spec_url": "https://api.apis.guru/v2/specs/hetzner.cloud/1.0.0/openapi.json", - "base_url": "https://docs.hetzner.cloud/spec.json", - "env_vars": ["HETZNER_CLOUD_API_KEY", "HETZNER_CLOUD_TOKEN"], - }, - "hhs-gov": { - "display_name": "HHS Media Services API", - "description": '

    Common Features / Behaviors

    • * "sort" param: s', - "spec_url": "https://api.apis.guru/v2/specs/hhs.gov/2/openapi.json", - "base_url": "https://api.digitalmedia.hhs.gov/swagger", - "env_vars": ["HHS_GOV_API_KEY", "HHS_GOV_TOKEN"], - }, - "highwaysengland-co-uk": { - "display_name": "Highways England API", - "spec_url": "https://api.apis.guru/v2/specs/highwaysengland.co.uk/v1/openapi.json", - "base_url": "http://webtris.highwaysengland.co.uk/api/swagger/docs/v1", - "env_vars": ["HIGHWAYSENGLAND_CO_UK_API_KEY", "HIGHWAYSENGLAND_CO_UK_TOKEN"], - }, - "hillbillysoftware-com-shinobi": { - "display_name": "shinobiapi", - "spec_url": "https://api.apis.guru/v2/specs/hillbillysoftware.com/shinobi/v1/swagger.json", - "base_url": "https://api.hillbillysoftware.com/swagger/docs/v1", - "env_vars": [ - "HILLBILLYSOFTWARE_COM_SHINOBI_API_KEY", - "HILLBILLYSOFTWARE_COM_SHINOBI_TOKEN", - ], - }, - "hsbc-com-atm": { - "display_name": "ATM Locator API", - "spec_url": "https://api.apis.guru/v2/specs/hsbc.com/atm/2.2.1/swagger.json", - "base_url": "https://developer.hsbc.com/assets/swaggers/open-atm-locator-swagger.json", - "env_vars": ["HSBC_COM_ATM_API_KEY", "HSBC_COM_ATM_TOKEN"], - }, - "hsbc-com-branches": { - "display_name": "Branch Locator API", - "spec_url": "https://api.apis.guru/v2/specs/hsbc.com/branches/2.2.1/swagger.json", - "base_url": "https://developer.hsbc.com/assets/swaggers/open-branch-locator-swagger.json", - "env_vars": ["HSBC_COM_BRANCHES_API_KEY", "HSBC_COM_BRANCHES_TOKEN"], - }, - "hsbc-com-product": { - "display_name": "Product Finder API", - "spec_url": "https://api.apis.guru/v2/specs/hsbc.com/product/2.2.1/swagger.json", - "base_url": "https://developer.hsbc.com/assets/swaggers/open-product-finder-swagger.json", - "env_vars": ["HSBC_COM_PRODUCT_API_KEY", "HSBC_COM_PRODUCT_TOKEN"], - }, - "httpbin": { - "display_name": "httpbin.org", - "description": "A simple HTTP Request & Response Service.

      Run locally: $ docker run -p 80:80 kennethreitz/httpbin", - "spec_url": "https://api.apis.guru/v2/specs/httpbin.org/0.9.2/openapi.json", - "base_url": "http://httpbin.org/spec.json", - "env_vars": ["HTTPBIN_API_KEY", "HTTPBIN_TOKEN"], - }, - "hubapi-com-analytics": { - "display_name": "Custom Behavioral Events API", - "description": "HTTP API for triggering instances of custom behavioral events", - "spec_url": "https://api.apis.guru/v2/specs/hubapi.com/analytics/v3/openapi.json", - "base_url": "https://api.hubspot.com/api-catalog-public/v1/apis/events/v3/send", - "env_vars": ["HUBSPOT_API_KEY"], - }, - "hubapi-com-auth": { - "display_name": "", - "spec_url": "https://api.apis.guru/v2/specs/hubapi.com/auth/v1/openapi.json", - "base_url": "https://api.hubspot.com/api-catalog-public/v1/apis/oauth/v1", - "env_vars": ["HUBSPOT_API_KEY"], - }, - "hubapi-com-automation": { - "display_name": "Custom Workflow Actions", - "description": "Create custom workflow actions", - "spec_url": "https://api.apis.guru/v2/specs/hubapi.com/automation/v4/openapi.json", - "base_url": "https://api.hubspot.com/api-catalog-public/v1/apis/automation/v4/actions", - "env_vars": ["HUBSPOT_API_KEY"], - }, - "hubapi-com-businessunits": { - "display_name": "Business Unit", - "description": "Retrieve Business Unit information.", - "spec_url": "https://api.apis.guru/v2/specs/hubapi.com/business units/v3/openapi.json", - "base_url": "https://api.hubspot.com/api-catalog-public/v1/apis/business-units/v3", - "env_vars": ["HUBSPOT_API_KEY"], - }, - "hubapi-com-cms": { - "display_name": "Domains", - "spec_url": "https://api.apis.guru/v2/specs/hubapi.com/cms/v3/openapi.json", - "base_url": "https://api.hubspot.com/api-catalog-public/v1/apis/cms/v3/domains", - "env_vars": ["HUBSPOT_API_KEY"], - }, - "hubapi-com-communication-preferences": { - "display_name": "Subscriptions", - "description": "Subscriptions allow contacts to control what forms of communications they receive. Contacts can decide whether they want to receive communication pert", - "spec_url": "https://api.apis.guru/v2/specs/hubapi.com/communication-preferences/v3/openapi.json", - "base_url": "https://api.hubspot.com/api-catalog-public/v1/apis/communication-preferences/v3", - "env_vars": ["HUBSPOT_API_KEY"], - }, - "hubapi-com-conversations": { - "display_name": "Visitor Identification", - "description": "The Visitor Identification API allows you to pass identification information to the HubSpot chat widget for otherwise unknown visitors that were verif", - "spec_url": "https://api.apis.guru/v2/specs/hubapi.com/conversations/v3/openapi.json", - "base_url": "https://api.hubspot.com/api-catalog-public/v1/apis/conversations/v3/visitor-identification", - "env_vars": ["HUBSPOT_API_KEY"], - }, - "hubapi-com-crm": { - "display_name": "CRM cards", - "description": "Allows an app to extend the CRM UI by surfacing custom cards in the sidebar of record pages. These cards are defined up-front as part of app configura", - "spec_url": "https://api.apis.guru/v2/specs/hubapi.com/crm/v3/openapi.json", - "base_url": "https://api.hubspot.com/api-catalog-public/v1/apis/extensions/sales-objects/v1/object-types", - "env_vars": ["HUBSPOT_API_KEY"], - }, - "hubapi-com-events": { - "display_name": "HubSpot Events API", - "description": "API for accessing CRM object events.", - "spec_url": "https://api.apis.guru/v2/specs/hubapi.com/events/v3/openapi.json", - "base_url": "https://api.hubspot.com/api-catalog-public/v1/apis/events/v3/events", - "env_vars": ["HUBSPOT_API_KEY"], - }, - "hubapi-com-files": { - "display_name": "Files", - "description": "Upload and manage files.", - "spec_url": "https://api.apis.guru/v2/specs/hubapi.com/files/v3/openapi.json", - "base_url": "https://api.hubspot.com/api-catalog-public/v1/apis/files/v3/files", - "env_vars": ["HUBSPOT_API_KEY"], - }, - "hubapi-com-marketing": { - "display_name": "Marketing Events Extension", - "description": "These APIs allow you to interact with HubSpot's Marketing Events Extension. It allows you to: * Create, Read or update Marketing Event information in ", - "spec_url": "https://api.apis.guru/v2/specs/hubapi.com/marketing/v3/openapi.json", - "base_url": "https://api.hubspot.com/api-catalog-public/v1/apis/marketing/v3/marketing-events-beta", - "env_vars": ["HUBSPOT_API_KEY"], - }, - "hubapi-com-webhooks": { - "display_name": "Webhooks API", - "description": "Provides a way for apps to subscribe to certain change events in HubSpot. Once configured, apps will receive event payloads containing details about t", - "spec_url": "https://api.apis.guru/v2/specs/hubapi.com/webhooks/v3/openapi.json", - "base_url": "https://api.hubspot.com/api-catalog-public/v1/apis/webhooks/v3", - "env_vars": ["HUBSPOT_API_KEY"], - }, - "hubhopper": { - "display_name": "Hubhopper Partner Integration API(s) - Production", - "description": "This is an interactive document explaining the API(s) that could be used to fetch data from [Hubhopper](https://hubhopper.com). Use the api key provid", - "spec_url": "https://api.apis.guru/v2/specs/hubhopper.com/v5/swagger.json", - "base_url": "https://docs.hubhopper.com/api/integrations/partner/swagger.yaml", - "env_vars": ["HUBHOPPER_API_KEY", "HUBHOPPER_TOKEN"], - }, - "hydramovies": { - "display_name": "Hydra Movies", - "description": "Hydra Movies is a streaming service that holds information on thousands of popular movies. The Hydra Movies API gives you access to [their entire coll", - "spec_url": "https://api.apis.guru/v2/specs/hydramovies.com/1.1/swagger.json", - "base_url": "http://hydramovies.com/api-v2/swagger.yaml", - "env_vars": ["HYDRAMOVIES_API_KEY", "HYDRAMOVIES_TOKEN"], - }, - "i-cue-solutions": { - "display_name": "Growth Services", - "spec_url": "https://api.apis.guru/v2/specs/i-cue.solutions/v1/openapi.json", - "base_url": "https://api.i-cue.solutions/swagger/v1/swagger.json", - "env_vars": ["I_CUE_SOLUTIONS_API_KEY", "I_CUE_SOLUTIONS_TOKEN"], - }, - "ibanapi": { - "display_name": "IBANAPI OpenApi Documentation", - "description": "IBANAPI OpenApi documentation", - "spec_url": "https://api.apis.guru/v2/specs/ibanapi.com/1.0.0/openapi.json", - "base_url": "https://api.ibanapi.com/docs/api-docs.json", - "env_vars": ["IBANAPI_API_KEY", "IBANAPI_TOKEN"], - }, - "icons8": { - "display_name": "Use a [New Version](https://icons8.github.io/icons8-docs/) I", - "description": "# Icons8 API Icons8 API allows us to search and obtain [our icons](https://icons8.com/web-app). You're welcome to use our icons to extend the function", - "spec_url": "https://api.apis.guru/v2/specs/icons8.com/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/icons8/api-docs/master/apiary.apib", - "env_vars": ["ICONS8_API_KEY", "ICONS8_TOKEN"], - }, - "id4i-de": { - "display_name": "ID4i API", - "description": "ID4i HTTP API", - "spec_url": "https://api.apis.guru/v2/specs/id4i.de/1.0.2/openapi.json", - "base_url": "https://backend.id4i.de/docs/swagger.json", - "env_vars": ["ID4I_DE_API_KEY", "ID4I_DE_TOKEN"], - }, - "ideaconsult-net-enanomapper": { - "display_name": "eNanoMapper database", - "description": "AMBIT REST web services [eNanoMapper profile] with free text & faceted search", - "spec_url": "https://api.apis.guru/v2/specs/ideaconsult.net/enanomapper/4.0.0/openapi.json", - "base_url": "https://api.ideaconsult.net/management/apis/b5e2f290-5673-49da-a2f2-90567359da82/pages/ee38f795-655c-40b7-b8f7-95655cc0b7a1/content", - "env_vars": [ - "IDEACONSULT_NET_ENANOMAPPER_API_KEY", - "IDEACONSULT_NET_ENANOMAPPER_TOKEN", - ], - }, - "ideaconsult-net-nanoreg": { - "display_name": "eNanoMapper database", - "description": "AMBIT REST web services [eNanoMapper profile] with free text & faceted search", - "spec_url": "https://api.apis.guru/v2/specs/ideaconsult.net/nanoreg/4.0.0/openapi.json", - "base_url": "https://api.ideaconsult.net/management/apis/7d2c3a0e-ddc5-4553-ac3a-0eddc5e5532a/pages/161df366-7bae-410c-9df3-667bae510c7c/content", - "env_vars": [ - "IDEACONSULT_NET_NANOREG_API_KEY", - "IDEACONSULT_NET_NANOREG_TOKEN", - ], - }, - "ideal-postcodes-co-uk": { - "display_name": "API Reference - Ideal Postcodes", - "description": "# Getting Started ## Overview ### Access All API methods are either a `GET`, `POST` or `OPTIONS` request. The API communicates over both HTTPS and pla", - "spec_url": "https://api.apis.guru/v2/specs/ideal-postcodes.co.uk/3.7.0/openapi.json", - "base_url": "https://openapi.ideal-postcodes.dev/openapi.json", - "env_vars": ["IDEAL_POSTCODES_CO_UK_API_KEY", "IDEAL_POSTCODES_CO_UK_TOKEN"], - }, - "idtbeyond": { - "display_name": "Active Documentation for /v1", - "description": "Our active docs provide the ability to test out your account and to see the responses to your queries. The services are RESTful, and are accessed usin", - "spec_url": "https://api.apis.guru/v2/specs/idtbeyond.com/1.1.7/swagger.json", - "base_url": "https://app.idtbeyond.com/swagger/spec-08ef3dc298.json", - "env_vars": ["IDTBEYOND_API_KEY", "IDTBEYOND_TOKEN"], - }, - "ijenko": { - "display_name": "IoEยฒ IoT API - to create end-user applications", - "spec_url": "https://api.apis.guru/v2/specs/ijenko.net/3.0.0/swagger.json", - "base_url": "http://developers.ijenko.com/swagger.json", - "env_vars": ["IJENKO_API_KEY", "IJENKO_TOKEN"], - }, - "illumidesk": { - "display_name": "IllumiDesk", - "spec_url": "https://api.apis.guru/v2/specs/illumidesk.com/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/3Blades/openapi/master/tbs_swagger.yml", - "env_vars": ["ILLUMIDESK_API_KEY", "ILLUMIDESK_TOKEN"], - }, - "image-charts": { - "display_name": "Image-Charts", - "description": "Charts, simple as a URL. A safe and fast replacement for Google Image Charts", - "spec_url": "https://api.apis.guru/v2/specs/image-charts.com/6.1.19/swagger.json", - "base_url": "https://image-charts.com/swagger.json", - "env_vars": ["IMAGE_CHARTS_API_KEY", "IMAGE_CHARTS_TOKEN"], - }, - "impala-travel-hotels": { - "display_name": "Impala Hotel Booking API", - "description": "Add room selling to your app with ease, or expand your existing hotel portfolio. Access all the marketing material you need to sell a room, from hotel", - "spec_url": "https://api.apis.guru/v2/specs/impala.travel/hotels/1.003/openapi.json", - "base_url": "https://docs.impala.travel/api/v1/projects/impala/booking-api/nodes/spec/openapi.seller.yaml?branch=v1.003", - "env_vars": ["IMPALA_TRAVEL_HOTELS_API_KEY", "IMPALA_TRAVEL_HOTELS_TOKEN"], - }, - "import-io-data": { - "display_name": "import.io", - "spec_url": "https://api.apis.guru/v2/specs/import.io/data/1.0/swagger.json", - "base_url": "http://api.docs.import.io/data/swagger.json", - "env_vars": ["IMPORT_IO_DATA_API_KEY", "IMPORT_IO_DATA_TOKEN"], - }, - "import-io-extraction": { - "display_name": "import.io", - "spec_url": "https://api.apis.guru/v2/specs/import.io/extraction/1.0/swagger.json", - "base_url": "http://api.docs.import.io/extraction/swagger.json", - "env_vars": ["IMPORT_IO_EXTRACTION_API_KEY", "IMPORT_IO_EXTRACTION_TOKEN"], - }, - "import-io-rss": { - "display_name": "import.io", - "spec_url": "https://api.apis.guru/v2/specs/import.io/rss/1.0/swagger.json", - "base_url": "http://api.docs.import.io/rss/swagger.json", - "env_vars": ["IMPORT_IO_RSS_API_KEY", "IMPORT_IO_RSS_TOKEN"], - }, - "import-io-run": { - "display_name": "import.io", - "spec_url": "https://api.apis.guru/v2/specs/import.io/run/1.0/swagger.json", - "base_url": "http://api.docs.import.io/run/swagger.json", - "env_vars": ["IMPORT_IO_RUN_API_KEY", "IMPORT_IO_RUN_TOKEN"], - }, - "import-io-schedule": { - "display_name": "import.io", - "spec_url": "https://api.apis.guru/v2/specs/import.io/schedule/1.0/swagger.json", - "base_url": "http://api.docs.import.io/schedule/swagger.json", - "env_vars": ["IMPORT_IO_SCHEDULE_API_KEY", "IMPORT_IO_SCHEDULE_TOKEN"], - }, - "inboxroute": { - "display_name": "Mailsquad", - "description": "MailSquad offers an affordable and super easy way to create, send and track delightful emails.", - "spec_url": "https://api.apis.guru/v2/specs/inboxroute.com/0.9/swagger.json", - "base_url": "https://api.inboxroute.com/api/api-docs", - "env_vars": ["BOX_ACCESS_TOKEN"], - }, - "increase": { - "display_name": "Increase API", - "spec_url": "https://api.apis.guru/v2/specs/increase.com/0.0.1/openapi.json", - "base_url": "https://increase.com/openapi.json", - "env_vars": ["INCREASE_API_KEY", "INCREASE_TOKEN"], - }, - "infermedica": { - "display_name": "Infermedica API", - "description": "Empower your healthcare services with intelligent diagnostic insights of Infermedica API.", - "spec_url": "https://api.apis.guru/v2/specs/infermedica.com/v2/swagger.json", - "base_url": "https://api.infermedica.com/v2/swagger.json", - "env_vars": ["INFERMEDICA_API_KEY", "INFERMEDICA_TOKEN"], - }, - "influxdata": { - "display_name": "Influx OSS API Service", - "description": "# Authentication <!-- ReDoc-Inject: <security-definitions> -->", - "spec_url": "https://api.apis.guru/v2/specs/influxdata.com/2.0.0/openapi.json", - "base_url": "blob:https://docs.influxdata.com/blobId", - "env_vars": ["INFLUXDATA_API_KEY", "INFLUXDATA_TOKEN"], - }, - "inpe-br-dados-abertos": { - "display_name": "Dados Abertos - API", - "description": "API de Dados Abertos com dados processados pelo grupo de monitoramento de Queimadas do INPE.", - "spec_url": "https://api.apis.guru/v2/specs/inpe.br/dados-abertos/1.0/swagger.json", - "base_url": "http://queimadas.dgi.inpe.br/queimadas/dados-abertos/api/swagger.json", - "env_vars": ["INPE_BR_DADOS_ABERTOS_API_KEY", "INPE_BR_DADOS_ABERTOS_TOKEN"], - }, - "instagram": { - "display_name": "Instagram API", - "description": "Description of Instagram RESTful API. Current limitations: * Instagram service does not support [cross origin headers](https://developer.mozilla.org/e", - "spec_url": "https://api.apis.guru/v2/specs/instagram.com/1.0.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/darklynx/swagger-api-collection/master/api/yaml/instagram.yaml", - "env_vars": ["INSTAGRAM_ACCESS_TOKEN"], - }, - "intel-com-product-catalogue": { - "display_name": "Intel Product Catalogue Service", - "description": "This is the documentation for PIM Micro services. In order to use this tool you need to have Basic Auth credentials and a client id. If you dont have ", - "spec_url": "https://api.apis.guru/v2/specs/intel.com/product-catalogue/0.1.0/swagger.json", - "base_url": "https://productapi.intel.com/swagger.json", - "env_vars": [ - "INTEL_COM_PRODUCT_CATALOGUE_API_KEY", - "INTEL_COM_PRODUCT_CATALOGUE_TOKEN", - ], - }, - "intellifi-nl": { - "display_name": "Brain Web API", - "description": "This document describes the [Intellifi Brain](https://intellifi.zendesk.com/hc/en-us/categories/360000685454) Web API specification using the [OpenAPI", - "spec_url": "https://api.apis.guru/v2/specs/intellifi.nl/2.23.2+0.gfbc3926.dirty/openapi.json", - "base_url": "https://intellifi-nl.github.io/brain-rest-api-spec/openapi.yml", - "env_vars": ["INTELLIFI_NL_API_KEY", "INTELLIFI_NL_TOKEN"], - }, - "interactivebrokers": { - "display_name": "IBKR 3rd Party Web API", - "description": "Interactive Brokers Web API for 3rd Party Companies", - "spec_url": "https://api.apis.guru/v2/specs/interactivebrokers.com/1.0.0/openapi.json", - "base_url": "https://www.interactivebrokers.co.uk/webtradingapi/swagger.yaml", - "env_vars": ["INTERACTIVEBROKERS_API_KEY", "INTERACTIVEBROKERS_TOKEN"], - }, - "interzoid-com-convertcurrency": { - "display_name": "Interzoid Convert Currency Rate API", - "description": "This API enables you to convert an amount of one currency into another currency using current foreign exchange rates.", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/convertcurrency/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/convertcurrency.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getaddressmatch": { - "display_name": "Interzoid Get Address Match Similarity Key API", - "description": "This API provides a similarity key used to match with other similar street address data, including for purposes of deduplication, fuzzy matching, or m", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getaddressmatch/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getaddressmatch.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getareacodefromnumber": { - "display_name": "Interzoid Get Area Code From Number API", - "description": "This API provides area code information for a given telephone number.", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getareacodefromnumber/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getareacodefromnumber.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getcitymatch": { - "display_name": "Interzoid Get City Match Similarity Key API", - "description": "This API provides a similarity key used to match with other similar city name data, including for purposes of deduplication, fuzzy matching, or mergin", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getcitymatch/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getcitymatch.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getcitystandard": { - "display_name": "Interzoid City Data Standardization API", - "description": "This API provides a standard for US and international cities for the purposes of standardizing city name data, improving query results, analytics, and", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getcitystandard/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getcitystandard.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getcompanymatch": { - "display_name": "Interzoid Get Company Name Match Similarity Key API", - "description": "This API provides a similarity key used to match with other similar company name data, including for purposes of deduplication, fuzzy matching, or mer", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getcompanymatch/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getcompanymatch.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getcountrymatch": { - "display_name": "Interzoid Get Country Match Similarity Key API", - "description": "This API provides a similarity key used to match with other similar country name data, including for purposes of deduplication, fuzzy matching, or mer", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getcountrymatch/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getcountrymatch.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getcountrystandard": { - "display_name": "Interzoid Country Data Standardization API", - "description": "This API provides a standard for country name for the purposes of standardizing country name data, improving query results, analytics, and data mergin", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getcountrystandard/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getcountrystandard.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getcurrencyrate": { - "display_name": "Interzoid Get Currency Rate API", - "description": "This API retrieves the latest currency exchange rate, against the US Dollar, for the given three-letter international currency code. These currency ra", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getcurrencyrate/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getcurrencyrate.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getemailinfo": { - "display_name": "Interzoid Get Email Information API", - "description": "This API provides validation information for email addresses to aid in deliverability. Syntax, existence of mail servers, and other tests are run to e", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getemailinfo/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getemailinfo.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getfullnamematch": { - "display_name": "Interzoid Get Full Name Match Similarity Key API", - "description": "This API provides a similarity key used to match with other similar full name data, including for purposes of deduplication, fuzzy matching, or mergin", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getfullnamematch/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getfullnamematch.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getfullnameparsedmatch": { - "display_name": "Interzoid Get Full Name Parsed Match Similarity Key API", - "description": "This API provides a similarity key used to match with other similar full name data when data fields are parsed into first name and last name component", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getfullnameparsedmatch/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getfullnameparsedmatch.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getglobalnumberinfo": { - "display_name": "Interzoid Get Global Phone Number Information API", - "description": "This API provides geographic information for a global telephone number, including city and country information, primary languages spoken, and mobile d", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getglobalnumberinfo/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getglobalnumberinfo.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getglobaltime": { - "display_name": "Interzoid Get Global Time API", - "description": "This API retrieves the current time for a city or geographic location around the globe.", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getglobaltime/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getglobaltime.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getstateabbreviation": { - "display_name": "Interzoid State Data Standardization API", - "description": "This API provides the two-letter state abbreviation (or the province abbreviation for Canada) for the purposes of standardizing state name data, impro", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getstateabbreviation/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getstateabbreviation.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getweathercity": { - "display_name": "Interzoid Get Weather City API", - "description": "This API provides current weather information for US Cities, including temperatures, wind speeds, wind direction, relative humidity, and visibility.", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getweathercity/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getweathercity.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getweatherzip": { - "display_name": "Interzoid Get Weather By Zip Code API", - "description": "This API provides current weather information for US Cities, including temperatures, wind speeds, wind direction, relative humidity, and visibility.", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getweatherzip/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getweatherzip.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-getzipinfo": { - "display_name": "Interzoid Zip Code Detailed Info API", - "description": "This API provides detailed information for a given zip code, including city, state, latitude, longitude, area size, and various population demographic", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/getzipinfo/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/getzipinfo.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-globalpageload": { - "display_name": "Interzoid Global Page Load Performance API", - "description": "This API provides a timed, browser-simulated page load function (or a measured API call) from the specified geography using a server from that geograp", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/globalpageload/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/globalpageload.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "interzoid-com-lookupareacode": { - "display_name": "Interzoid Get Area Code API", - "description": "This API provides area code information for a given telephone area code.", - "spec_url": "https://api.apis.guru/v2/specs/interzoid.com/lookupareacode/1.0.0/openapi.json", - "base_url": "https://oas.interzoid.com/api/lookupareacode.json", - "env_vars": ["INTERZOID_API_KEY"], - }, - "ip2location": { - "display_name": "IP2Location.io IP Geolocation API", - "description": "IP2Location.io IP Geolocation API provides RESTful API to obtain visitorsโ€™ geolocation information such as country, region, city, latitude & longitude", - "spec_url": "https://api.apis.guru/v2/specs/ip2location.io/1.0/openapi.json", - "base_url": "https://api.swaggerhub.com/apis/ip2location/ip2location-io-ip-geolocation-api/1.0", - "env_vars": ["IP2LOCATION_API_KEY", "IP2LOCATION_TOKEN"], - }, - "ip2location-com-geolocation": { - "display_name": "IP2Location IP Geolocation", - "description": "IP2Location web service providing a service to do a reverse lookup of an IP address to an ISO3166 country code, region or state, city, latitude and lo", - "spec_url": "https://api.apis.guru/v2/specs/ip2location.com/geolocation/1.0/openapi.json", - "base_url": "https://api.swaggerhub.com/apis/ip2location/ip2location-ip-geolocation/1.0", - "env_vars": [ - "IP2LOCATION_COM_GEOLOCATION_API_KEY", - "IP2LOCATION_COM_GEOLOCATION_TOKEN", - ], - }, - "ip2proxy": { - "display_name": "IP2Proxy Proxy Detection", - "description": "IP2Proxy allows instant detection of anonymous proxy, VPN, TOR exit nodes, search engine robots (SES), data center ranges (PX2-PX10), residential prox", - "spec_url": "https://api.apis.guru/v2/specs/ip2proxy.com/1.0/openapi.json", - "base_url": "https://api.swaggerhub.com/apis/ip2location/ip2proxy-proxy-detection/1.0", - "env_vars": ["IP2PROXY_API_KEY", "IP2PROXY_TOKEN"], - }, - "ip2whois": { - "display_name": "IP2WHOIS Domain Lookup", - "description": "IP2WHOIS is a free tool to allow you to check WHOIS information for a particular domain, such as domain assigned owner contact information, registrar ", - "spec_url": "https://api.apis.guru/v2/specs/ip2whois.com/1.0/openapi.json", - "base_url": "https://api.swaggerhub.com/apis/ip2whois/ip2whois-domain-lookup/1.0", - "env_vars": ["IP2WHOIS_API_KEY", "IP2WHOIS_TOKEN"], - }, - "ipinfodb": { - "display_name": "", - "spec_url": "https://api.apis.guru/v2/specs/ipinfodb.com/1.0.0/openapi.json", - "base_url": "https://api.swaggerhub.com/apis/ipinfodb/ipinfodb-ip-address-lookup/1.0", - "env_vars": ["IPINFODB_API_KEY", "IPINFODB_TOKEN"], - }, - "ipqualityscore": { - "display_name": "IPQualityScore API", - "spec_url": "https://api.apis.guru/v2/specs/ipqualityscore.com/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/IPQualityScore/IP_Quality_Score_API_Spec/Review1/postman/collections/IP%20API%20Spec%20Review%201.json", - "env_vars": ["IPQUALITYSCORE_API_KEY", "IPQUALITYSCORE_TOKEN"], - }, - "iptwist": { - "display_name": "ipTwist", - "description": "The reliable, secure, and simple IP geolocation API.", - "spec_url": "https://api.apis.guru/v2/specs/iptwist.com/1.0.0/openapi.json", - "base_url": "https://iptwist.com/schema", - "env_vars": ["IPTWIST_API_KEY", "IPTWIST_TOKEN"], - }, - "iqualify": { - "display_name": "iQualify Management API", - "description": "The iQualify API offers management responses for building learning experiences using your iQualify instance data. Once youโ€™ve registered with iQualify", - "spec_url": "https://api.apis.guru/v2/specs/iqualify.com/v1/openapi.json", - "base_url": "https://api.iqualify.com/v1/management.json", - "env_vars": ["IQUALIFY_API_KEY", "IQUALIFY_TOKEN"], - }, - "isbndb": { - "display_name": "ISBNdb API", - "description": "Definition of ISBNdb.com API", - "spec_url": "https://api.apis.guru/v2/specs/isbndb.com/1.0.1/swagger.json", - "base_url": "https://isbndb.com/modules/isbndb_api_docs/swagger.json", - "env_vars": ["ISBNDB_API_KEY", "ISBNDB_TOKEN"], - }, - "isendpro": { - "display_name": "API iSendPro", - "description": "[1] Liste des fonctionnalitรฉs : - envoi de SMS ร  un ou plusieurs destinataires, - lookup HLR, - rรฉcupรฉration des rรฉcapitulatifs de campagne, - gestion", - "spec_url": "https://api.apis.guru/v2/specs/isendpro.com/1.1.1/openapi.json", - "base_url": "https://apirest.isendpro.com/isendpro.json", - "env_vars": ["ISENDPRO_API_KEY", "ISENDPRO_TOKEN"], - }, - "iva-api": { - "display_name": "Entertainment Express API", - "description": "Your Gateway to Building Incredible Movie, TV, and Game Content Discovery Experiences.", - "spec_url": "https://api.apis.guru/v2/specs/iva-api.com/2.0/swagger.json", - "base_url": "https://ee.iva-api.com/specs/openapi", - "env_vars": ["IVA_API_API_KEY", "IVA_API_TOKEN"], - }, - "ix-api": { - "display_name": "IX-API", - "description": "This API allows to config/change/delete Internet Exchange services. # Filters When querying collections, the provided query parameters are validated. ", - "spec_url": "https://api.apis.guru/v2/specs/ix-api.net/2.1.0/openapi.json", - "base_url": "https://docs.ix-api.net/v2/ix-api-latest.json", - "env_vars": ["IX_API_API_KEY", "IX_API_TOKEN"], - }, - "izettle-com-products": { - "display_name": "Product Library API", - "description": "The Product Library API is used for managing merchant's product information and product images.", - "spec_url": "https://api.apis.guru/v2/specs/izettle.com/products/1.0.0/openapi.json", - "base_url": "https://products.izettle.com/openapi.json", - "env_vars": ["IZETTLE_COM_PRODUCTS_API_KEY", "IZETTLE_COM_PRODUCTS_TOKEN"], - }, - "javatpoint": { - "display_name": "Firebase Cloud Messaging API", - "description": "FCM send API that provides a cross-platform messaging solution to reliably deliver messages at no cost.", - "spec_url": "https://api.apis.guru/v2/specs/javatpoint.com/v1/openapi.json", - "base_url": "https://fcm.googleapis.com/$discovery/rest?version=v1", - "env_vars": ["JAVATPOINT_API_KEY", "JAVATPOINT_TOKEN"], - }, - "jellyfin": { - "display_name": "Jellyfin API", - "spec_url": "https://api.apis.guru/v2/specs/jellyfin.local/v1/openapi.json", - "base_url": "https://repo.jellyfin.org/releases/openapi/stable/jellyfin-openapi-10.7.0-rc2.json", - "env_vars": ["JELLYFIN_API_KEY", "JELLYFIN_TOKEN"], - }, - "jira": { - "display_name": "JIRA 7.6.1", - "spec_url": "https://api.apis.guru/v2/specs/jira.local/1.0.0/swagger.json", - "base_url": "https://docs.atlassian.com/jira/REST/server/jira-rest-plugin.wadl", - "env_vars": ["JIRA_TOKEN", "JIRA_API_TOKEN"], - }, - "jirafe": { - "display_name": "Jirafe Events", - "description": "API endpoins for sending Jirafe events", - "spec_url": "https://api.apis.guru/v2/specs/jirafe.com/2.0.0/swagger.json", - "base_url": "https://event.jirafe.com/api-docs", - "env_vars": ["JIRA_TOKEN", "JIRA_API_TOKEN"], - }, - "jokes-one": { - "display_name": "Jokes One API", - "description": "Jokes One API offers a complete feature rich REST API access to its jokes platform. This is the documentation for the world famous [jokes API](https:/", - "spec_url": "https://api.apis.guru/v2/specs/jokes.one/1.1/swagger.json", - "base_url": "https://api.jokes.one/yaml/jokes.one.yaml", - "env_vars": ["JOKES_ONE_API_KEY", "JOKES_ONE_TOKEN"], - }, - "journy": { - "display_name": "Developer documentation", - "description": "# Welcome Implementing a new tool can be daunting, but it doesn't have to. You can implement journy.io in a few different ways to ensure it fits with ", - "spec_url": "https://api.apis.guru/v2/specs/journy.io/1.0.0/openapi.json", - "base_url": "https://api.journy.io/spec.json", - "env_vars": ["JOURNY_API_KEY", "JOURNY_TOKEN"], - }, - "json2video": { - "display_name": "JSON2Video API", - "description": "Create and edit awesome videos programmatically", - "spec_url": "https://api.apis.guru/v2/specs/json2video.com/2.0.0/openapi.json", - "base_url": "https://json2video.com/docs/api/json2video-api.json", - "env_vars": ["JSON2VIDEO_API_KEY", "JSON2VIDEO_TOKEN"], - }, - "jumpseller": { - "display_name": "Jumpseller API", - "description": "# Endpoint Structure All URLs are in the format: ```text https://api.jumpseller.com/v1/path.json?login=XXXXXX&authtoken=storetoken ``` The path is pre", - "spec_url": "https://api.apis.guru/v2/specs/jumpseller.com/1.0.0/openapi.json", - "base_url": "https://api.jumpseller.com/swagger.json", - "env_vars": ["JUMPSELLER_API_KEY", "JUMPSELLER_TOKEN"], - }, - "just-eat-co-uk": { - "display_name": "Just Eat UK", - "description": "# Just Eat API Just Eat offers services for our various business partners and our consumer applications. How you interact with the API depends on the ", - "spec_url": "https://api.apis.guru/v2/specs/just-eat.co.uk/1.0.0/openapi.json", - "base_url": "https://uk.api.just-eat.io/docs/openapi.json", - "env_vars": ["JUST_EAT_CO_UK_API_KEY", "JUST_EAT_CO_UK_TOKEN"], - }, - "keycloak": { - "display_name": "Keycloak Admin REST API", - "description": "This is a REST API reference for the Keycloak Admin", - "spec_url": "https://api.apis.guru/v2/specs/keycloak.local/1/openapi.json", - "base_url": "https://raw.githubusercontent.com/ccouzens/keycloak-openapi/master/keycloak/10.0.json", - "env_vars": ["KEYCLOAK_API_KEY", "KEYCLOAK_TOKEN"], - }, - "keyserv-solutions": { - "display_name": "KeyServ", - "description": "KeyServ API", - "spec_url": "https://api.apis.guru/v2/specs/keyserv.solutions/1.4.5/openapi.json", - "base_url": "https://keyserv.solutions/v1/spec.json", - "env_vars": ["KEYSERV_SOLUTIONS_API_KEY", "KEYSERV_SOLUTIONS_TOKEN"], - }, - "klarna-com-openai": { - "display_name": "Open AI Klarna product Api", - "spec_url": "https://api.apis.guru/v2/specs/klarna.com/openai/v0/openapi.json", - "base_url": "https://www.klarna.com/us/shopping/public/openai/v0/api-docs/", - "env_vars": ["OPENAI_API_KEY"], - "endpoints_count": 226, - }, - "klarna-com-payments": { - "display_name": "Klarna Payments API V1", - "description": "The payments API is used to create a session to offer Klarna's payment methods as part of your checkout. As soon as the purchase is completed the orde", - "spec_url": "https://api.apis.guru/v2/specs/klarna.com/payments/1.0.0/openapi.json", - "base_url": "file:///home/mike/Downloads/swagger.json", - "env_vars": ["KLARNA_COM_PAYMENTS_API_KEY", "KLARNA_COM_PAYMENTS_TOKEN"], - }, - "koomalooma": { - "display_name": "koomalooma Partner API", - "description": "This is the koomalooma Partner API. koomalooma is the first Loyalty BPaaS (Business Process as a Service) for mobile and web companies. With koomaloom", - "spec_url": "https://api.apis.guru/v2/specs/koomalooma.com/1.0/swagger.json", - "base_url": "https://api.koomalooma.com/oas", - "env_vars": ["KOOMALOOMA_API_KEY", "KOOMALOOMA_TOKEN"], - }, - "kubernetes": { - "display_name": "Kubernetes", - "spec_url": "https://api.apis.guru/v2/specs/kubernetes.io/unversioned/swagger.json", - "base_url": "https://raw.githubusercontent.com/kubernetes/kubernetes/master/api/openapi-spec/swagger.json", - "env_vars": ["UBER_ACCESS_TOKEN"], - }, - "kumpeapps": { - "display_name": "KumpeApps API", - "description": "KKid API. Due to security concerns all calls to this API requires authentication. If you have access then you may use your KumpeApps username/password", - "spec_url": "https://api.apis.guru/v2/specs/kumpeapps.com/5.0.0/openapi.json", - "base_url": "https://api.kumpeapps.com/python/swagger/swagger.yaml", - "env_vars": ["KUMPEAPPS_API_KEY", "KUMPEAPPS_TOKEN"], - }, - "lambdatest": { - "display_name": "LambdaTest Screenshots API Documentation", - "spec_url": "https://api.apis.guru/v2/specs/lambdatest.com/1.0.1/openapi.json", - "base_url": "https://screenshot-public-api.s3.amazonaws.com/openapi.yaml", - "env_vars": ["LAMBDATEST_API_KEY", "LAMBDATEST_TOKEN"], - }, - "landregistry-gov-uk-deed": { - "display_name": "Deed API", - "description": "Land Registry Deed API", - "spec_url": "https://api.apis.guru/v2/specs/landregistry.gov.uk/deed/1.0.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/LandRegistry/dm-deed-api/master/application/deed/schemas/deed-api.json", - "env_vars": [ - "LANDREGISTRY_GOV_UK_DEED_API_KEY", - "LANDREGISTRY_GOV_UK_DEED_TOKEN", - ], - }, - "languagetool": { - "display_name": "LanguageTool API", - "description": "Check texts for style and grammar issues with LanguageTool. Please consider the following default limitations:<", - "spec_url": "https://api.apis.guru/v2/specs/languagetool.org/1.1.2/swagger.json", - "base_url": "https://languagetool.org/http-api/languagetool-swagger.json", - "env_vars": ["LANGUAGETOOL_API_KEY", "LANGUAGETOOL_TOKEN"], - }, - "launchdarkly": { - "display_name": "LaunchDarkly REST API", - "description": "Build custom integrations with the LaunchDarkly REST API", - "spec_url": "https://api.apis.guru/v2/specs/launchdarkly.com/5.3.0/swagger.json", - "base_url": "https://launchdarkly.github.io/ld-openapi/openapi.json", - "env_vars": ["LAUNCHDARKLY_API_KEY", "LAUNCHDARKLY_TOKEN"], - }, - "learnifier": { - "display_name": "Learnifier", - "spec_url": "https://api.apis.guru/v2/specs/learnifier.com/1.1.0/swagger.json", - "base_url": "http://learnifier.com/apidocs/learnifier.json", - "env_vars": ["LEARNIFIER_API_KEY", "LEARNIFIER_TOKEN"], - }, - "letmc-com-basic-tier": { - "display_name": "LetMC Api V2, Basic (Tier 2)", - "spec_url": "https://api.apis.guru/v2/specs/letmc.com/basic-tier/v2-basic-tier/swagger.json", - "base_url": "https://live-api.letmc.com/swagger/docs/v2-basic-tier", - "env_vars": ["LETMC_COM_BASIC_TIER_API_KEY", "LETMC_COM_BASIC_TIER_TOKEN"], - }, - "letmc-com-customer": { - "display_name": "agentOS Api V2, Customer Login Call Group", - "spec_url": "https://api.apis.guru/v2/specs/letmc.com/customer/v2-customer/openapi.json", - "base_url": "https://live-api.letmc.com/swagger/docs/v2-customer", - "env_vars": ["LETMC_COM_CUSTOMER_API_KEY", "LETMC_COM_CUSTOMER_TOKEN"], - }, - "letmc-com-diary": { - "display_name": "agentOS API V3, Diary Call Group", - "spec_url": "https://api.apis.guru/v2/specs/letmc.com/diary/v3-diary/openapi.json", - "base_url": "https://live-api.letmc.com/swagger/docs/v3-diary", - "env_vars": ["LETMC_COM_DIARY_API_KEY", "LETMC_COM_DIARY_TOKEN"], - }, - "letmc-com-free-tier": { - "display_name": "LetMC Api V2, Free (Tier 1)", - "spec_url": "https://api.apis.guru/v2/specs/letmc.com/free-tier/v2-free-tier/swagger.json", - "base_url": "https://live-api.letmc.com/swagger/docs/v2-free-tier", - "env_vars": ["LETMC_COM_FREE_TIER_API_KEY", "LETMC_COM_FREE_TIER_TOKEN"], - }, - "letmc-com-maintenance": { - "display_name": "agentOS API V3, Maintenance Call Group", - "spec_url": "https://api.apis.guru/v2/specs/letmc.com/maintenance/v3-maintenance/openapi.json", - "base_url": "https://live-api.letmc.com/swagger/docs/v3-maintenance", - "env_vars": ["LETMC_COM_MAINTENANCE_API_KEY", "LETMC_COM_MAINTENANCE_TOKEN"], - }, - "letmc-com-reporting": { - "display_name": "LetMC Api V3, reporting", - "spec_url": "https://api.apis.guru/v2/specs/letmc.com/reporting/v3-reporting/swagger.json", - "base_url": "https://live-api.letmc.com/swagger/docs/v3-reporting", - "env_vars": ["LETMC_COM_REPORTING_API_KEY", "LETMC_COM_REPORTING_TOKEN"], - }, - "lgtm": { - "display_name": "LGTM API specification", - "description": "The REST API for LGTM provides data so that you can customize how you integrate LGTM analysis into your workflow. It includes the following resources:", - "spec_url": "https://api.apis.guru/v2/specs/lgtm.com/v1.0/openapi.json", - "base_url": "https://lgtm.com/api/v1.0/openapi", - "env_vars": ["LGTM_API_KEY", "LGTM_TOKEN"], - }, - "libretranslate": { - "display_name": "LibreTranslate", - "spec_url": "https://api.apis.guru/v2/specs/libretranslate.local/1.3.9/openapi.json", - "base_url": "https://libretranslate.com/spec", - "env_vars": ["LIBRETRANSLATE_API_KEY", "LIBRETRANSLATE_TOKEN"], - }, - "link-fish": { - "display_name": "link.fish API", - "description": "API to easily extract data from websites. # Base URL All URLs referenced in the documentation have the following base: ``` https://api.link.fish ``` T", - "spec_url": "https://api.apis.guru/v2/specs/link.fish/2018-07-05/swagger.json", - "base_url": "https://api.link.fish/swagger.yaml", - "env_vars": ["LINK_FISH_API_KEY", "LINK_FISH_TOKEN"], - }, - "linode": { - "display_name": "Linode API", - "description": "## Introduction The Linode API provides the ability to programmatically manage the full range of Linode products and services. This reference is desig", - "spec_url": "https://api.apis.guru/v2/specs/linode.com/4.145.0/openapi.json", - "base_url": "https://www.linode.com/docs/api/openapi.yaml", - "env_vars": ["LINODE_TOKEN"], - }, - "linqr-app": { - "display_name": "LinQR", - "description": "This is LinQR QR Code API documentation. This API allows you to generate custom, visually attractive QR Codes. The cloud infrastructure guarantees hig", - "spec_url": "https://api.apis.guru/v2/specs/linqr.app/2.0/openapi.json", - "base_url": "https://linqr.app/openapi/openapi.json", - "env_vars": ["LINQR_APP_API_KEY", "LINQR_APP_TOKEN"], - }, - "linuxfoundation-org-reimbursement": { - "display_name": "Reimbursements API", - "spec_url": "https://api.apis.guru/v2/specs/linuxfoundation.org/reimbursement/1.0/swagger.json", - "base_url": "https://api-gw.dev.platform.linuxfoundation.org/reimbursement-service/swagger.json", - "env_vars": [ - "LINUXFOUNDATION_ORG_REIMBURSEMENT_API_KEY", - "LINUXFOUNDATION_ORG_REIMBURSEMENT_TOKEN", - ], - }, - "listennotes": { - "display_name": "Listen API: Podcast Search, Directory, and Insights API", - "description": "Simple & no-nonsense podcast search & directory API. Search all podcasts and episodes by people, places, or topics.", - "spec_url": "https://api.apis.guru/v2/specs/listennotes.com/2.0/openapi.json", - "base_url": "https://listen-api.listennotes.com/api/v2/openapi.yaml", - "env_vars": ["LISTENNOTES_API_KEY", "LISTENNOTES_TOKEN"], - }, - "ljaero-com-dflight": { - "display_name": "DFlight API", - "description": "[DFlight API](https://ljaero.com/solutions/dflight/) supplies the up-to-date information needed for compliance with UAV preflight assessment requireme", - "spec_url": "https://api.apis.guru/v2/specs/ljaero.com/dflight/V 1.0.0/openapi.json", - "base_url": "https://dflight-api.ljaero.com/openapi.json", - "env_vars": ["LJAERO_COM_DFLIGHT_API_KEY", "LJAERO_COM_DFLIGHT_TOKEN"], - }, - "logoraisr": { - "display_name": "API docs | logoraisr.com", - "description": '

      Dig into our logoraisr API reference documentation. We also offer an OpenAPI specification to allow easy integration into', - "spec_url": "https://api.apis.guru/v2/specs/logoraisr.com/v1/openapi.json", - "base_url": "https://docs.logoraisr.com/swagger.json", - "env_vars": ["LOGORAISR_API_KEY", "LOGORAISR_TOKEN"], - }, - "loket-nl": { - "display_name": "Loket.nl API", - "description": '**Is this your first time here? Please check out our [introduction to Loket (API)](./Introduction)** **The initial ', - "spec_url": "https://api.apis.guru/v2/specs/loket.nl/V2/openapi.json", - "base_url": "https://developer.loket.nl/swagger.json", - "env_vars": ["LOKET_NL_API_KEY", "LOKET_NL_TOKEN"], - }, - "lotadata": { - "display_name": "LotaData", - "description": "Access the most exhaustive, accurate and up-to-date collection of global and hyper-local geocoded events and activities across a wide range of categor", - "spec_url": "https://api.apis.guru/v2/specs/lotadata.com/2.0.0/swagger.json", - "base_url": "https://developers.lotadata.com/swagger/spec/apiv2.json", - "env_vars": ["LOTADATA_API_KEY", "LOTADATA_TOKEN"], - }, - "lufthansa-com-partner": { - "display_name": "LH Partner API", - "spec_url": "https://api.apis.guru/v2/specs/lufthansa.com/partner/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/LufthansaOpenAPI/Swagger4Partners/master/LH-OpenAPI-Partners-Swagger2.json", - "env_vars": ["LUFTHANSA_COM_PARTNER_API_KEY", "LUFTHANSA_COM_PARTNER_TOKEN"], - }, - "lufthansa-com-public": { - "display_name": "LH Public API", - "spec_url": "https://api.apis.guru/v2/specs/lufthansa.com/public/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/LufthansaOpenAPI/Swagger/master/LH_public_API_swagger_2_0.json", - "env_vars": ["LUFTHANSA_COM_PUBLIC_API_KEY", "LUFTHANSA_COM_PUBLIC_TOKEN"], - }, - "lumminary": { - "display_name": "Lumminary API", - "description": "# Introduction The Lumminary API was built to allow third parties to interact with Lumminary customers and gain access to their genetic data. The Lumm", - "spec_url": "https://api.apis.guru/v2/specs/lumminary.com/1.0/swagger.json", - "base_url": "https://api.lumminary.com/docs/swagger.json", - "env_vars": ["LUMMINARY_API_KEY", "LUMMINARY_TOKEN"], - }, - "lyft": { - "display_name": "Lyft", - "description": "Drive your app to success with Lyft's API", - "spec_url": "https://api.apis.guru/v2/specs/lyft.com/1.0.0/swagger.json", - "base_url": "https://api.lyft.com/v1/spec", - "env_vars": ["LYFT_ACCESS_TOKEN"], - }, - "magento": { - "display_name": "Magento B2B", - "description": "Magento Commerce is the leading provider of open omnichannel innovation.", - "spec_url": "https://api.apis.guru/v2/specs/magento.com/2.2.10/openapi.json", - "base_url": "https://devdocs.magento.com/redoc/2.2/latest-2.2.schema.json", - "env_vars": ["MAGENTO_API_KEY", "MAGENTO_TOKEN"], - }, - "magick-nu": { - "display_name": "Tradeworks", - "description": "Authentication is required to access all methods of the API. Enter username and password. Credentials are automatically set as you type.", - "spec_url": "https://api.apis.guru/v2/specs/magick.nu/1.0/swagger.json", - "base_url": "http://devui.magick.nu/api/api-docs", - "env_vars": ["MAGICK_NU_API_KEY", "MAGICK_NU_TOKEN"], - }, - "maif-local-otoroshi": { - "display_name": "Otoroshi Admin API", - "description": "Admin API of the Otoroshi reverse proxy", - "spec_url": "https://api.apis.guru/v2/specs/maif.local/otoroshi/1.5.0-dev/openapi.json", - "base_url": "https://raw.githubusercontent.com/MAIF/otoroshi/master/docs/manual/code/swagger.json", - "env_vars": ["MAIF_LOCAL_OTOROSHI_API_KEY", "MAIF_LOCAL_OTOROSHI_TOKEN"], - }, - "mailboxvalidator-com-checker": { - "display_name": "MailboxValidator Free Email Checker", - "description": "The MailboxValidator Free Email Checker checks if a single email address is from a free email provider and returns the results in either JSON or XML f", - "spec_url": "https://api.apis.guru/v2/specs/mailboxvalidator.com/checker/1.0.0/openapi.json", - "base_url": "https://api.swaggerhub.com/apis/mailboxvalidator/MailboxValidator-Free-Email-Checker/1.0.0", - "env_vars": ["BOX_ACCESS_TOKEN"], - }, - "mailboxvalidator-com-disposable": { - "display_name": "MailboxValidator Disposable Email Checker", - "description": "The MailboxValidator Disposable Email Checker API checks if a single email address is from a disposable email provider and returns the results in eith", - "spec_url": "https://api.apis.guru/v2/specs/mailboxvalidator.com/disposable/1.0.0/openapi.json", - "base_url": "https://api.swaggerhub.com/apis/mailboxvalidator/MailboxValidator-Disposable-Email-Checker/1.0.0", - "env_vars": ["BOX_ACCESS_TOKEN"], - }, - "mailboxvalidator-com-validation": { - "display_name": "MailboxValidator Email Validation", - "description": "The Single Validation API does validation on a single email address and returns all the validation results in either JSON or XML format. Refer to http", - "spec_url": "https://api.apis.guru/v2/specs/mailboxvalidator.com/validation/0.1/openapi.json", - "base_url": "https://api.swaggerhub.com/apis/mailboxvalidator/MailboxValidator-Email-Validation/0.1", - "env_vars": ["BOX_ACCESS_TOKEN"], - }, - "mailscript": { - "display_name": "Mailscript", - "spec_url": "https://api.apis.guru/v2/specs/mailscript.com/0.4.0/openapi.json", - "base_url": "http://api.mailscript.com/v2/swagger", - "env_vars": ["MAILSCRIPT_API_KEY", "MAILSCRIPT_TOKEN"], - }, - "mandrillapp": { - "display_name": "Mandrill", - "description": "Mandrill is a reliable, scalable, and secure delivery API for transactional emails from websites and applications. It's ideal for sending data-driven ", - "spec_url": "https://api.apis.guru/v2/specs/mandrillapp.com/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/APIs-guru/unofficial_openapi_specs/master/mandrillapp.com/1.0/swagger.yaml", - "env_vars": ["MANDRILLAPP_API_KEY", "MANDRILLAPP_TOKEN"], - }, - "mashape-com-geodb": { - "display_name": "GeoDB Cities API", - "description": "The GeoDB API focuses on getting global city and region data. Easily obtain country, region, and city data for use in your apps!

      • Filter citie", - "spec_url": "https://api.apis.guru/v2/specs/mashape.com/geodb/1.0.0/swagger.json", - "base_url": "https://wirefreethought.github.io/geodb-cities-api-docs/swagger.json", - "env_vars": ["MASHAPE_COM_GEODB_API_KEY", "MASHAPE_COM_GEODB_TOKEN"], - }, - "mastercard-com-billpay": { - "display_name": "Bill Payment Validator", - "description": "The Bill Payment Validator service allows RPPS origination (payment sender) customers to identify if a potential RPPS transaction would process succes", - "spec_url": "https://api.apis.guru/v2/specs/mastercard.com/BillPay/1.0/swagger.json", - "base_url": "https://developer.mastercard.com/devzone/api/portal/swagger/bill-payment-validator", - "env_vars": ["MASTERCARD_API_KEY"], - }, - "mastercard-com-bintableresource": { - "display_name": "MasterCard Bin Table Listing", - "description": "MasterCard Bin Table Listing API", - "spec_url": "https://api.apis.guru/v2/specs/mastercard.com/BINTableResource/1.0/swagger.json", - "base_url": "https://developer.mastercard.com/devzone/api/portal/swagger/bin-table-resource", - "env_vars": ["MASTERCARD_API_KEY"], - }, - "mastercard-com-currencyconversioncalculator": { - "display_name": "API for the Settlement Currency Rate converter", - "description": "This API provides a range of functions to get back currency conversion rates and amounts based on current Mastercard currency conversion values.", - "spec_url": "https://api.apis.guru/v2/specs/mastercard.com/CurrencyConversionCalculator/1.0.0/swagger.json", - "base_url": "https://developer.mastercard.com/devzone/api/portal/swagger/currency-conversion-calculator", - "env_vars": ["MASTERCARD_API_KEY"], - }, - "mastercard-com-locations": { - "display_name": "Locations API", - "description": "The Locations API provides access to MasterCard's ATM and Merchant location database", - "spec_url": "https://api.apis.guru/v2/specs/mastercard.com/Locations/1.0.0/swagger.json", - "base_url": "https://developer.mastercard.com/devzone/api/portal/swagger/locations", - "env_vars": ["MASTERCARD_API_KEY"], - }, - "mastercard-com-masterpassqr": { - "display_name": "Send Person to Merchant", - "spec_url": "https://api.apis.guru/v2/specs/mastercard.com/masterpassqr/V1/swagger.json", - "base_url": "https://developer.mastercard.com/devzone/api/portal/swagger/mastercard-merchant-presented-qr", - "env_vars": ["MASTERCARD_API_KEY"], - }, - "mastercard-com-match": { - "display_name": "MATCH API", - "description": "Helps acquirers identify potentially high-risk merchants before entering to a merchant agreement.", - "spec_url": "https://api.apis.guru/v2/specs/mastercard.com/MATCH/1.0.0/swagger.json", - "base_url": "https://developer.mastercard.com/devzone/api/portal/swagger/match", - "env_vars": ["MASTERCARD_API_KEY"], - }, - "mastercard-com-maws": { - "display_name": "MasterCard ABU API", - "description": "Mastercard ABU API", - "spec_url": "https://api.apis.guru/v2/specs/mastercard.com/MAWS/1.1.0/swagger.json", - "base_url": "https://developer.mastercard.com/devzone/api/portal/swagger/automatic-billing-updater-abu", - "env_vars": ["MASTERCARD_API_KEY"], - }, - "mastercard-com-mdes": { - "display_name": "MDES Customer Service", - "description": "This API provides our Issuer partners with resources to help resolve consumer queries about payment accounts enabled through our digitization platform", - "spec_url": "https://api.apis.guru/v2/specs/mastercard.com/MDES/2.0.7/swagger.json", - "base_url": "https://developer.mastercard.com/devzone/api/portal/swagger/mdes-customer-service", - "env_vars": ["MASTERCARD_API_KEY"], - }, - "mastercard-com-merchantidentifier": { - "display_name": "Merchant Identifier API V2", - "description": "API for Merchant Identifier", - "spec_url": "https://api.apis.guru/v2/specs/mastercard.com/MerchantIdentifier/2.0.0/swagger.json", - "base_url": "https://developer.mastercard.com/devzone/api/portal/swagger/merchant-identifier", - "env_vars": ["MASTERCARD_API_KEY"], - }, - "mastercard-com-open-banking-connect-pis": { - "display_name": "Open Banking - Payments initiation service", - "description": "Open Banking - Payments initiation service", - "spec_url": "https://api.apis.guru/v2/specs/mastercard.com/open-banking-connect-pis/1.16.0/swagger.json", - "base_url": "https://developer.mastercard.com/devzone/api/portal/swagger/open-banking-connect-payment-initiation-service", - "env_vars": ["MASTERCARD_API_KEY"], - }, - "mastercard-com-paymentaccountreferenceinquiryapi": { - "display_name": "Payment Account Reference Inquiry API", - "description": "The Payment Account Reference Inquiry API is the unified Mastercard interface for allowing Mastercard Customers involved in payment card acceptance --", - "spec_url": "https://api.apis.guru/v2/specs/mastercard.com/PaymentAccountReferenceInquiryAPI/1.1/swagger.json", - "base_url": "https://developer.mastercard.com/devzone/api/portal/swagger/payment-account-reference-inquiry", - "env_vars": ["MASTERCARD_API_KEY"], - }, - "mastercard-com-personalizedloyaltyoffers": { - "display_name": "Personalized Offers", - "description": "This API provides content for financial instutions participating in Mastercard Personalized Offers to use in online and mobile banking applications fo", - "spec_url": "https://api.apis.guru/v2/specs/mastercard.com/PersonalizedLoyaltyOffers/1.3/swagger.json", - "base_url": "https://developer.mastercard.com/devzone/api/portal/swagger/personalized-offers", - "env_vars": ["MASTERCARD_API_KEY"], - }, - "mastercard-com-repower": { - "display_name": "rePower", - "spec_url": "https://api.apis.guru/v2/specs/mastercard.com/Repower/V2/swagger.json", - "base_url": "https://developer.mastercard.com/devzone/api/portal/swagger/mastercard-repower", - "env_vars": ["MASTERCARD_API_KEY"], - }, - "mastercard-com-spendingpulse": { - "display_name": "Spending Pulse", - "description": "This API will provide monthly data which includes metrics such as sales volume and growth rate.", - "spec_url": "https://api.apis.guru/v2/specs/mastercard.com/SpendingPulse/1.0/swagger.json", - "base_url": "https://developer.mastercard.com/devzone/api/portal/swagger/spendingpulse", - "env_vars": ["MASTERCARD_API_KEY"], - }, - "mastodon": { - "display_name": "Mastodon API Specification (https://github.com/mastodon/mast", - "spec_url": "https://api.apis.guru/v2/specs/mastodon.local/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/oneslash/mastodon/feat/add-open-api-spec/api-specification.yml", - "env_vars": ["MASTODON_API_KEY", "MASTODON_TOKEN"], - }, - "math-tools": { - "display_name": "Numbers API", - "description": "All about Numbers. REST access with json/xml/jsonp result support. Below is the documentation for the Numbers API. You can try them out right here. Fi", - "spec_url": "https://api.apis.guru/v2/specs/math.tools/1.5/openapi.json", - "base_url": "https://api.math.tools/yaml/math.tools.numbers.openapi.yaml", - "env_vars": ["MATH_TOOLS_API_KEY", "MATH_TOOLS_TOKEN"], - }, - "mbus": { - "display_name": "M-Bus HTTPD API", - "spec_url": "https://api.apis.guru/v2/specs/mbus.local/0.3.5/openapi.json", - "base_url": "https://raw.githubusercontent.com/packom/mbus-api/master/api/openapi.yaml", - "env_vars": ["MBUS_API_KEY", "MBUS_TOKEN"], - }, - "mcw-edu": { - "display_name": "Rat Genome Database REST API", - "description": "The RGD REST API provides programmatic access to information and annotation stored in the Rat Genome Database", - "spec_url": "https://api.apis.guru/v2/specs/mcw.edu/1.1/openapi.json", - "base_url": "http://rest.rgd.mcw.edu/rgdws/v2/api-docs", - "env_vars": ["MCW_EDU_API_KEY", "MCW_EDU_TOKEN"], - }, - "medcorder": { - "display_name": "Medcorder Nearby Doctor API", - "description": "Returns doctors near a client given a lat/lon and autocomplete text.", - "spec_url": "https://api.apis.guru/v2/specs/medcorder.com/1.0.0/swagger.json", - "base_url": "https://static.medcorder.com/openapi.yaml", - "env_vars": ["MEDCORDER_API_KEY", "MEDCORDER_TOKEN"], - }, - "medium": { - "display_name": "Medium API", - "description": "Medium API helps you to quickly extract data from Medium's Website (https://medium.com). You can gather data related to users, publications, articles ", - "spec_url": "https://api.apis.guru/v2/specs/medium.com/1.0/openapi.json", - "base_url": "blob:https://docs.mediumapi.com/blobId", - "env_vars": ["MEDIUM_API_KEY", "MEDIUM_TOKEN"], - }, - "meilisearch": { - "display_name": "Meilisearch v1.0", - "spec_url": "https://api.apis.guru/v2/specs/meilisearch.com/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/meilisearch/documentation/master/.vuepress/public/postman/meilisearch-collection.json", - "env_vars": ["MEILISEARCH_API_KEY"], - }, - "meraki": { - "display_name": "Meraki Dashboard API", - "description": "The Cisco Meraki Dashboard API is a modern REST API based on the OpenAPI specification. > Date: 05 March, 2023 > > [Recent Updates](https://meraki.io/", - "spec_url": "https://api.apis.guru/v2/specs/meraki.com/0.0.0-streaming/openapi.json", - "base_url": "https://api.meraki.com/api/v0/openapiSpec", - "env_vars": ["MERAKI_API_KEY", "MERAKI_TOKEN"], - }, - "mercedes-benz-com-configurator": { - "display_name": "Car Configurator", - "description": "The Car Configurator API offers access to the Mercedes-Benz car configuration functions. It provides required reference data such as the masterdata of", - "spec_url": "https://api.apis.guru/v2/specs/mercedes-benz.com/configurator/1.0/swagger.json", - "base_url": "https://developer.mercedes-benz.com/content/sites/default/files/2018-08/swagger_car_configurator_api.json", - "env_vars": [ - "MERCEDES_BENZ_COM_CONFIGURATOR_API_KEY", - "MERCEDES_BENZ_COM_CONFIGURATOR_TOKEN", - ], - }, - "mercedes-benz-com-dealer": { - "display_name": "Dealer", - "description": "The Dealer API provides Dealer search functions.", - "spec_url": "https://api.apis.guru/v2/specs/mercedes-benz.com/dealer/1.0/swagger.json", - "base_url": "https://developer.mercedes-benz.com/content/sites/default/files/2018-07/swagger_dealer_api_0.yaml", - "env_vars": [ - "MERCEDES_BENZ_COM_DEALER_API_KEY", - "MERCEDES_BENZ_COM_DEALER_TOKEN", - ], - }, - "mercedes-benz-com-diagnostics": { - "display_name": "Remote Diagnostic Support", - "description": "The Remote Diagnostic Support API will provide the possibility for 3rd party applications (e.g. ADAC, ATU, etc.) to access vehicle diagnostics data re", - "spec_url": "https://api.apis.guru/v2/specs/mercedes-benz.com/diagnostics/1.0/swagger.json", - "base_url": "https://developer.mercedes-benz.com/content/sites/default/files/2018-10/swagger_remote_diagnostics_api_2.yaml", - "env_vars": [ - "MERCEDES_BENZ_COM_DIAGNOSTICS_API_KEY", - "MERCEDES_BENZ_COM_DIAGNOSTICS_TOKEN", - ], - }, - "mercedes-benz-com-image": { - "display_name": "Vehicle Image", - "description": "The vehicle images API offers access to original Mercedes-Benz vehicle images. It provides access to exterior and interior images with parameters e.g.", - "spec_url": "https://api.apis.guru/v2/specs/mercedes-benz.com/image/1.0/swagger.json", - "base_url": "https://developer.mercedes-benz.com/content/sites/default/files/2018-05/swagger_vehicleimage_api.json", - "env_vars": [ - "MERCEDES_BENZ_COM_IMAGE_API_KEY", - "MERCEDES_BENZ_COM_IMAGE_TOKEN", - ], - }, - "mercure": { - "display_name": "The Mercure protocol", - "description": "[Mercure](https://mercure.rocks) is a protocol allowing to push data updates to web browsers and other HTTP clients in a convenient, fast, reliable an", - "spec_url": "https://api.apis.guru/v2/specs/mercure.local/0.3.2/openapi.json", - "base_url": "https://raw.githubusercontent.com/dunglas/mercure/main/spec/openapi.yaml", - "env_vars": ["MERCURE_API_KEY", "MERCURE_TOKEN"], - }, - "mermade-org-uk-openapi-converter": { - "display_name": "Swagger2OpenAPI Converter", - "description": "Converter and validator for Swagger 2.0 to OpenAPI 3.0.x definitions", - "spec_url": "https://api.apis.guru/v2/specs/mermade.org.uk/openapi-converter/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/Mermade/openapi-webconverter/master/contract/openapi.json", - "env_vars": [ - "MERMADE_ORG_UK_OPENAPI_CONVERTER_API_KEY", - "MERMADE_ORG_UK_OPENAPI_CONVERTER_TOKEN", - ], - }, - "meshery": { - "display_name": "Meshery API.", - "description": "the purpose of this application is to provide an application that is using plain go code to define an API This should demonstrate all the possible com", - "spec_url": "https://api.apis.guru/v2/specs/meshery.local/0.4.27/openapi.json", - "base_url": "https://raw.githubusercontent.com/meshery/meshery/master/helpers/swagger.yaml", - "env_vars": ["MESHERY_API_KEY", "MESHERY_TOKEN"], - }, - "meteosource": { - "display_name": "Interactive documentation for your Premium plan", - "description": "This interactive documentation is using your API key which is filled in automatically, you can find and change this in [your dashboard](https://www.me", - "spec_url": "https://api.apis.guru/v2/specs/meteosource.com/v1/openapi.json", - "base_url": "https://www.meteosource.com/api/v1/premium/openapi.json", - "env_vars": ["METEOSOURCE_API_KEY", "METEOSOURCE_TOKEN"], - }, - "miataru": { - "display_name": "Miataru", - "description": "The Miataru API is very simple and straight forward. Generally you're posting (HTTP POST) a JSON formatted request to a service method locations and y", - "spec_url": "https://api.apis.guru/v2/specs/miataru.com/1.0.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/miataru/miataru-protocol-specification/swagger/miataru-v1/api/swagger/swagger.yaml", - "env_vars": ["MIATARU_API_KEY", "MIATARU_TOKEN"], - }, - "microcks": { - "display_name": "Microcks API v1.7", - "description": "API offered by Microcks, the Kuebrnetes native tools for API and microservices mocking and testing (microcks.io)", - "spec_url": "https://api.apis.guru/v2/specs/microcks.local/1.7.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/microcks/microcks/master/api/microcks-openapi-v1.7.yaml", - "env_vars": ["MICROCKS_API_KEY", "MICROCKS_TOKEN"], - }, - "microsoft-com-cognitiveservices-autosuggest": { - "display_name": "AutoSuggest Client", - "description": "Autosuggest supplies search terms derived from a root text sent to the service. The terms Autosuggest supplies are related to the root text based on s", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/cognitiveservices-AutoSuggest/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cognitiveservices/data-plane/AutoSuggest/stable/v1.0/AutoSuggest.json", - "env_vars": [ - "MICROSOFT_COM_COGNITIVESERVICES_AUTOSUGGEST_API_KEY", - "MICROSOFT_COM_COGNITIVESERVICES_AUTOSUGGEST_TOKEN", - ], - }, - "microsoft-com-cognitiveservices-computervision": { - "display_name": "Computer Vision Client", - "description": "The Computer Vision API provides state-of-the-art algorithms to process images and return information. For example, it can be used to determine if an ", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/cognitiveservices-ComputerVision/2.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cognitiveservices/data-plane/ComputerVision/stable/v2.1/ComputerVision.json", - "env_vars": [ - "MICROSOFT_COM_COGNITIVESERVICES_COMPUTERVISION_API_KEY", - "MICROSOFT_COM_COGNITIVESERVICES_COMPUTERVISION_TOKEN", - ], - }, - "microsoft-com-cognitiveservices-customimagesearch": { - "display_name": "Custom Image Search Client", - "description": "The Bing Custom Image Search API lets you send an image search query to Bing and get back image search results customized to meet your custom search d", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/cognitiveservices-CustomImageSearch/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cognitiveservices/data-plane/CustomImageSearch/stable/v1.0/CustomImageSearch.json", - "env_vars": [ - "MICROSOFT_COM_COGNITIVESERVICES_CUSTOMIMAGESEARCH_API_KEY", - "MICROSOFT_COM_COGNITIVESERVICES_CUSTOMIMAGESEARCH_TOKEN", - ], - }, - "microsoft-com-cognitiveservices-customsearch": { - "display_name": "Custom Search Client", - "description": "The Bing Custom Search API lets you send a search query to Bing and get back search results customized to meet your custom search definition.", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/cognitiveservices-CustomSearch/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cognitiveservices/data-plane/CustomWebSearch/stable/v1.0/CustomSearch.json", - "env_vars": [ - "MICROSOFT_COM_COGNITIVESERVICES_CUSTOMSEARCH_API_KEY", - "MICROSOFT_COM_COGNITIVESERVICES_CUSTOMSEARCH_TOKEN", - ], - }, - "microsoft-com-cognitiveservices-entitysearch": { - "display_name": "Entity Search Client", - "description": "The Entity Search API lets you send a search query to Bing and get back search results that include entities and places. Place results include restaur", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/cognitiveservices-EntitySearch/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cognitiveservices/data-plane/EntitySearch/stable/v1.0/EntitySearch.json", - "env_vars": [ - "MICROSOFT_COM_COGNITIVESERVICES_ENTITYSEARCH_API_KEY", - "MICROSOFT_COM_COGNITIVESERVICES_ENTITYSEARCH_TOKEN", - ], - }, - "microsoft-com-cognitiveservices-imagesearch": { - "display_name": "Image Search Client", - "description": "The Image Search API lets you send a search query to Bing and get back a list of relevant images. This section provides technical details about the qu", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/cognitiveservices-ImageSearch/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cognitiveservices/data-plane/ImageSearch/stable/v1.0/ImageSearch.json", - "env_vars": [ - "MICROSOFT_COM_COGNITIVESERVICES_IMAGESEARCH_API_KEY", - "MICROSOFT_COM_COGNITIVESERVICES_IMAGESEARCH_TOKEN", - ], - }, - "microsoft-com-cognitiveservices-localsearch": { - "display_name": "Local Search Client", - "description": "The Local Search client lets you send a search query to Bing and get back search results that include local businesses such as restaurants, hotels, re", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/cognitiveservices-LocalSearch/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cognitiveservices/data-plane/LocalSearch/stable/v1.0/LocalSearch.json", - "env_vars": [ - "MICROSOFT_COM_COGNITIVESERVICES_LOCALSEARCH_API_KEY", - "MICROSOFT_COM_COGNITIVESERVICES_LOCALSEARCH_TOKEN", - ], - }, - "microsoft-com-cognitiveservices-newssearch": { - "display_name": "News Search Client", - "description": "The News Search API lets you send a search query to Bing and get back a list of news that are relevant to the search query. This section provides tech", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/cognitiveservices-NewsSearch/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cognitiveservices/data-plane/NewsSearch/stable/v1.0/NewsSearch.json", - "env_vars": [ - "MICROSOFT_COM_COGNITIVESERVICES_NEWSSEARCH_API_KEY", - "MICROSOFT_COM_COGNITIVESERVICES_NEWSSEARCH_TOKEN", - ], - }, - "microsoft-com-cognitiveservices-ocr": { - "display_name": "Computer Vision Client", - "description": "The Computer Vision API provides state-of-the-art algorithms to process images and return information. For example, it can be used to determine if an ", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/cognitiveservices-Ocr/2.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cognitiveservices/data-plane/ComputerVision/stable/v2.1/Ocr.json", - "env_vars": [ - "MICROSOFT_COM_COGNITIVESERVICES_OCR_API_KEY", - "MICROSOFT_COM_COGNITIVESERVICES_OCR_TOKEN", - ], - }, - "microsoft-com-cognitiveservices-prediction": { - "display_name": "Custom Vision Prediction Client", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/cognitiveservices-Prediction/3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cognitiveservices/data-plane/CustomVision/Prediction/stable/v3.0/Prediction.json", - "env_vars": [ - "MICROSOFT_COM_COGNITIVESERVICES_PREDICTION_API_KEY", - "MICROSOFT_COM_COGNITIVESERVICES_PREDICTION_TOKEN", - ], - }, - "microsoft-com-cognitiveservices-spellcheck": { - "display_name": "Spell Check Client", - "description": "The Spell Check API - V7 lets you check a text string for spelling and grammar errors.", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/cognitiveservices-SpellCheck/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cognitiveservices/data-plane/SpellCheck/stable/v1.0/SpellCheck.json", - "env_vars": [ - "MICROSOFT_COM_COGNITIVESERVICES_SPELLCHECK_API_KEY", - "MICROSOFT_COM_COGNITIVESERVICES_SPELLCHECK_TOKEN", - ], - }, - "microsoft-com-cognitiveservices-training": { - "display_name": "Custom Vision Training Client", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/cognitiveservices-Training/3.2/openapi.json", - "base_url": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cognitiveservices/data-plane/CustomVision/Training/stable/v3.2/Training.json", - "env_vars": [ - "MICROSOFT_COM_COGNITIVESERVICES_TRAINING_API_KEY", - "MICROSOFT_COM_COGNITIVESERVICES_TRAINING_TOKEN", - ], - }, - "microsoft-com-cognitiveservices-videosearch": { - "display_name": "Video Search Client", - "description": "The Video Search API lets you search on Bing for video that are relevant to the user's search query, for insights about a video or for videos that are", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/cognitiveservices-VideoSearch/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cognitiveservices/data-plane/VideoSearch/stable/v1.0/VideoSearch.json", - "env_vars": [ - "MICROSOFT_COM_COGNITIVESERVICES_VIDEOSEARCH_API_KEY", - "MICROSOFT_COM_COGNITIVESERVICES_VIDEOSEARCH_TOKEN", - ], - }, - "microsoft-com-cognitiveservices-visualsearch": { - "display_name": "Visual Search Client", - "description": "Visual Search API lets you discover insights about an image such as visually similar images, shopping sources, and related searches. The API can also ", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/cognitiveservices-VisualSearch/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cognitiveservices/data-plane/VisualSearch/preview/v1.0/VisualSearch.json", - "env_vars": [ - "MICROSOFT_COM_COGNITIVESERVICES_VISUALSEARCH_API_KEY", - "MICROSOFT_COM_COGNITIVESERVICES_VISUALSEARCH_TOKEN", - ], - }, - "microsoft-com-cognitiveservices-websearch": { - "display_name": "Web Search Client", - "description": "The Web Search API lets you send a search query to Bing and get back search results that include links to webpages, images, and more.", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/cognitiveservices-WebSearch/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cognitiveservices/data-plane/WebSearch/stable/v1.0/WebSearch.json", - "env_vars": [ - "MICROSOFT_COM_COGNITIVESERVICES_WEBSEARCH_API_KEY", - "MICROSOFT_COM_COGNITIVESERVICES_WEBSEARCH_TOKEN", - ], - }, - "microsoft-com-graph": { - "display_name": "OData Service for namespace microsoft.graph", - "description": "This OData service is located at https://graph.microsoft.com/v1.0", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/graph/1.0.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/v1.0/openapi.yaml", - "env_vars": ["MICROSOFT_COM_GRAPH_API_KEY", "MICROSOFT_COM_GRAPH_TOKEN"], - }, - "microsoft-com-graph-beta": { - "display_name": "OData Service for namespace microsoft.graph", - "description": "This OData service is located at https://graph.microsoft.com/beta", - "spec_url": "https://api.apis.guru/v2/specs/microsoft.com/graph-beta/1.0.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/beta/openapi.yaml", - "env_vars": [ - "MICROSOFT_COM_GRAPH_BETA_API_KEY", - "MICROSOFT_COM_GRAPH_BETA_TOKEN", - ], - }, - "mineskin": { - "display_name": "MineSkin API", - "description": "Client implementations: Java: https://github.com/InventivetalentDev/MineskinClient NodeJS: https://github.com/InventivetalentDev/mineskin-client Examp", - "spec_url": "https://api.apis.guru/v2/specs/mineskin.org/1.0.0/openapi.json", - "base_url": "https://api.mineskin.org/openapi.yml", - "env_vars": ["MINESKIN_API_KEY", "MINESKIN_TOKEN"], - }, - "mist": { - "display_name": "Mist API", - "description": "> Version: **0.36.1** > > Date: **March 3, 2022** --- #### Available Documentation * [Postman](https://documenter.getpostman.com/view/224925/SzYgQufe)", - "spec_url": "https://api.apis.guru/v2/specs/mist.com/0.36.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/tmunzer/mist_openapi/main/mist.openapi.yml", - "env_vars": ["MIST_API_KEY", "MIST_TOKEN"], - }, - "moderatecontent": { - "display_name": "Image Moderation", - "description": "Our FREE API blocks images with nudity. Build from the ground up, accurate models, best in class support, great price.", - "spec_url": "https://api.apis.guru/v2/specs/moderatecontent.com/1.0.0/swagger.json", - "base_url": "https://www.moderatecontent.com/api/swagger.yaml", - "env_vars": ["MODERATECONTENT_API_KEY", "MODERATECONTENT_TOKEN"], - }, - "mon-voyage-pas-cher": { - "display_name": "Mon-voyage-pas-cher.com Public API", - "spec_url": "https://api.apis.guru/v2/specs/mon-voyage-pas-cher.com/0.0.1/swagger.json", - "base_url": "https://www.mon-voyage-pas-cher.com/assets/documentation/swagger.yaml", - "env_vars": ["MON_VOYAGE_PAS_CHER_API_KEY", "MON_VOYAGE_PAS_CHER_TOKEN"], - }, - "monarchinitiative": { - "display_name": "BioLink API", - "description": "API integration layer for linked biological objects. __Source:__ https://github.com/biolink/biolink-api/", - "spec_url": "https://api.apis.guru/v2/specs/monarchinitiative.org/1.1.14/openapi.json", - "base_url": "https://api.monarchinitiative.org/api/swagger.json", - "env_vars": ["MONARCHINITIATIVE_API_KEY", "MONARCHINITIATIVE_TOKEN"], - }, - "moonmoonmoonmoon": { - "display_name": "Moon by Ai Weiwei & Olafur Eliasson", - "description": "Turn nothing into something โ€“ make a drawing, make a mark.", - "spec_url": "https://api.apis.guru/v2/specs/moonmoonmoonmoon.com/1.0/swagger.json", - "base_url": "http://moonmoonmoonmoon.com/api/api-docs.json", - "env_vars": ["MOONMOONMOONMOON_API_KEY", "MOONMOONMOONMOON_TOKEN"], - }, - "motaword": { - "display_name": "MotaWord API", - "description": "Use MotaWord API to post and track your translation projects.", - "spec_url": "https://api.apis.guru/v2/specs/motaword.com/1.0/openapi.json", - "base_url": "https://api.motaword.com/swagger", - "env_vars": ["MOTAWORD_API_KEY", "MOTAWORD_TOKEN"], - }, - "mozilla-com-kinto": { - "display_name": "Remote Settings PROD", - "spec_url": "https://api.apis.guru/v2/specs/mozilla.com/kinto/1.22/openapi.json", - "base_url": "https://firefox.settings.services.mozilla.com/v1/__api__", - "env_vars": ["MOZILLA_COM_KINTO_API_KEY", "MOZILLA_COM_KINTO_TOKEN"], - }, - "mtaa-api-herokuapp": { - "display_name": "Mtaa API Documentation", - "description": "Mtaa A simple REST API to access Tanzania's location information,With mtaa API you can easily query and integrate all the location in tanzania from re", - "spec_url": "https://api.apis.guru/v2/specs/mtaa-api.herokuapp.com/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/Kalebu/mtaa-docs/main/openapi.yaml", - "env_vars": ["HEROKU_API_KEY"], - }, - "musixmatch": { - "display_name": "Musixmatch API", - "description": "Musixmatch lyrics API is a robust service that permits you to search and retrieve lyrics in the simplest possible way. It just works. Include millions", - "spec_url": "https://api.apis.guru/v2/specs/musixmatch.com/1.1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/musixmatch/musixmatch-sdk/master/swagger/swagger.yaml", - "env_vars": ["MUSIXMATCH_API_KEY", "MUSIXMATCH_TOKEN"], - }, - "n-auth": { - "display_name": "nextAuth API", - "description": "API for the nextAuth server", - "spec_url": "https://api.apis.guru/v2/specs/n-auth.com/2.2/swagger.json", - "base_url": "https://api.docs.nextauth.com/api/swagger.json", - "env_vars": ["N_AUTH_API_KEY", "N_AUTH_TOKEN"], - }, - "namsor": { - "display_name": "NamSor API v2", - "description": "NamSor API v2 : enpoints to process personal names (gender, cultural origin or ethnicity) in all alphabets or languages. By default, enpoints use 1 un", - "spec_url": "https://api.apis.guru/v2/specs/namsor.com/2.0.24/openapi.json", - "base_url": "https://v2.namsor.com/NamSorAPIv2/api2/openapi.json", - "env_vars": ["NAMSOR_API_KEY", "NAMSOR_TOKEN"], - }, - "nasa-gov-apod": { - "display_name": "APOD", - "description": "This endpoint structures the APOD imagery and associated metadata so that it can be repurposed for other applications. In addition, if the concept_tag", - "spec_url": "https://api.apis.guru/v2/specs/nasa.gov/apod/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/nasa/api-docs/gh-pages/assets/json/APOD", - "env_vars": ["NASA_GOV_APOD_API_KEY", "NASA_GOV_APOD_TOKEN"], - }, - "nasa-gov-asteroidsneows": { - "display_name": "TechPort", - "description": "TechPort RESTful API", - "spec_url": "https://api.apis.guru/v2/specs/nasa.gov/asteroids neows/3.4.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/nasa/api-docs/gh-pages/assets/json/Asteroids%20NeoWs", - "env_vars": [ - "NASA_GOV_ASTEROIDSNEOWS_API_KEY", - "NASA_GOV_ASTEROIDSNEOWS_TOKEN", - ], - }, - "nativeads": { - "display_name": "Native Ads Publisher API", - "description": "This is a Native Ads Publisher API it provides same functionality as Native Ads Publisher Account GUI.", - "spec_url": "https://api.apis.guru/v2/specs/nativeads.com/1.0.0/swagger.json", - "base_url": "https://api.nativeads.com/docs/publisher/swagger.json", - "env_vars": ["NATIVEADS_API_KEY", "NATIVEADS_TOKEN"], - }, - "naviplancentral-com-factfinder": { - "display_name": "Advicent.FactFinderService", - "description": "An API for accessing the NaviPlan Fact Finder.", - "spec_url": "https://api.apis.guru/v2/specs/naviplancentral.com/factfinder/v1/swagger.json", - "base_url": "https://demo.uat.naviplancentral.com/factfinder/swagger/docs/v1", - "env_vars": [ - "NAVIPLANCENTRAL_COM_FACTFINDER_API_KEY", - "NAVIPLANCENTRAL_COM_FACTFINDER_TOKEN", - ], - }, - "naviplancentral-com-plan": { - "display_name": "NaviPlan API", - "description": "An API for accessing NaviPlan plan data for a client.", - "spec_url": "https://api.apis.guru/v2/specs/naviplancentral.com/plan/v1/swagger.json", - "base_url": "https://demo.uat.naviplancentral.com/plan/swagger/docs/v1", - "env_vars": [ - "NAVIPLANCENTRAL_COM_PLAN_API_KEY", - "NAVIPLANCENTRAL_COM_PLAN_TOKEN", - ], - }, - "nba": { - "display_name": "NBA Stats API", - "description": "The destination for current and historic NBA statistics.", - "spec_url": "https://api.apis.guru/v2/specs/nba.com/version/swagger.json", - "base_url": "https://raw.githubusercontent.com/danielwelch/little-pynny/master/little-pynny/swagger.json", - "env_vars": ["NBA_API_KEY", "NBA_TOKEN"], - }, - "nbg-gr": { - "display_name": "Account and Transaction API Specification - UK", - "description": '## Functionality at a glance The NBG "UK OPB - Account and Transaction v3.1.5" API follows the [UK Open Banking Specification v3.1.5](https://openba', - "spec_url": "https://api.apis.guru/v2/specs/nbg.gr/v3.1.5/openapi.json", - "base_url": "https://developer.nbg.gr/api.gateway/publicportal/sites/default/files/2020-09/NBGSwagger-account-and_transaction_api_specification_uk-v3.1.5-swagger%20%284%29.yaml", - "env_vars": ["NBG_GR_API_KEY", "NBG_GR_TOKEN"], - }, - "ndhm-gov-in-ndhm-cm": { - "display_name": "Health Data Consent Manager", - "description": "Entity which provides health information aggregation services to customers of health care services. It enables customers to fetch their health informa", - "spec_url": "https://api.apis.guru/v2/specs/ndhm.gov.in/ndhm-cm/0.5/openapi.json", - "base_url": "https://apisetu.gov.in/api_specification_v8/ndhm-cm.yaml", - "env_vars": ["NDHM_GOV_IN_NDHM_CM_API_KEY", "NDHM_GOV_IN_NDHM_CM_TOKEN"], - }, - "ndhm-gov-in-ndhm-gateway": { - "display_name": "Gateway", - "description": "Gateway is the hub that routes/orchestrates the interaction between consent managers and API bridges. There are 5 categories of APIs; discovery, link,", - "spec_url": "https://api.apis.guru/v2/specs/ndhm.gov.in/ndhm-gateway/0.5/openapi.json", - "base_url": "https://apisetu.gov.in/api_specification_v8/ndhm-gateway.yaml", - "env_vars": [ - "NDHM_GOV_IN_NDHM_GATEWAY_API_KEY", - "NDHM_GOV_IN_NDHM_GATEWAY_TOKEN", - ], - }, - "ndhm-gov-in-ndhm-healthid": { - "display_name": "Health ID Service", - "description": "It is important to standardize the process of identification of an individual across healthcare providers, to ensure that the created medical records ", - "spec_url": "https://api.apis.guru/v2/specs/ndhm.gov.in/ndhm-healthid/1.0/openapi.json", - "base_url": "https://apisetu.gov.in/api_specification_v8/ndhm-healthid.yaml", - "env_vars": [ - "NDHM_GOV_IN_NDHM_HEALTHID_API_KEY", - "NDHM_GOV_IN_NDHM_HEALTHID_TOKEN", - ], - }, - "ndhm-gov-in-ndhm-hip": { - "display_name": "Health Repository Provider Specifications for HIP", - "description": "The following are the specifications for the APIs to be implemented at the Health Repository end if an entity is only serving the role of a HIP. The s", - "spec_url": "https://api.apis.guru/v2/specs/ndhm.gov.in/ndhm-hip/0.5/openapi.json", - "base_url": "https://apisetu.gov.in/api_specification_v8/ndhm-hip.yaml", - "env_vars": ["NDHM_GOV_IN_NDHM_HIP_API_KEY", "NDHM_GOV_IN_NDHM_HIP_TOKEN"], - }, - "ndhm-gov-in-ndhm-hiu": { - "display_name": "Health Repository Provider Specifications for HIU", - "description": "The following are the specifications for the APIs to be implemented at the Health Repository end if an entity is only serving the role of a HIU. The s", - "spec_url": "https://api.apis.guru/v2/specs/ndhm.gov.in/ndhm-hiu/0.5/openapi.json", - "base_url": "https://apisetu.gov.in/api_specification_v8/ndhm-hiu.yaml", - "env_vars": ["NDHM_GOV_IN_NDHM_HIU_API_KEY", "NDHM_GOV_IN_NDHM_HIU_TOKEN"], - }, - "nebl": { - "display_name": "Neblio REST API Suite", - "description": "APIs for Interacting with NTP1 Tokens & The Neblio Blockchain", - "spec_url": "https://api.apis.guru/v2/specs/nebl.io/1.3.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/NeblioTeam/neblio-api-swagger-docs/master/swagger.yaml", - "env_vars": ["NEBL_API_KEY", "NEBL_TOKEN"], - }, - "neowsapp": { - "display_name": "NeoWs - (Near Earth Object Web Service)", - "description": 'A web service for near earth objects. All the data is from the NASA JPL Asteroid team. NeoW', - "spec_url": "https://api.apis.guru/v2/specs/neowsapp.com/1.0/openapi.json", - "base_url": "http://www.neowsapp.com/api-docs", - "env_vars": ["NEOWSAPP_API_KEY", "NEOWSAPP_TOKEN"], - }, - "netatmo": { - "display_name": "Netatmo", - "description": '

        Welcome to the Netatmo swagger on-line documentation !

        This site is a complement to the official Netatmo', - "spec_url": "https://api.apis.guru/v2/specs/netatmo.net/1.1.5/openapi.json", - "base_url": "https://raw.githubusercontent.com/cbornet/netatmo-swagger-decl/master/spec/swagger.yaml", - "env_vars": ["NETATMO_API_KEY", "NETATMO_TOKEN"], - }, - "netbox-dev": { - "display_name": "NetBox API", - "description": "API to access NetBox", - "spec_url": "https://api.apis.guru/v2/specs/netbox.dev/3.4/openapi.json", - "base_url": "https://demo.netbox.dev/api/docs/?format=openapi", - "env_vars": ["BOX_ACCESS_TOKEN"], - }, - "netboxdemo": { - "display_name": "NetBox API", - "description": "API to access NetBox", - "spec_url": "https://api.apis.guru/v2/specs/netboxdemo.com/2.8/openapi.json", - "base_url": "https://netboxdemo.com/api/swagger.json", - "env_vars": ["BOX_ACCESS_TOKEN"], - }, - "netlicensing": { - "display_name": "Labs64 NetLicensing RESTful API Test Center", - "description": "The Labs64 NetLicensing RESTful API gives you access to NetLicensingโ€™s core fea", - "spec_url": "https://api.apis.guru/v2/specs/netlicensing.io/2.x/openapi.json", - "base_url": "http://io.labs64.com/NetLicensing-API/v2.x/netlicensing.json", - "env_vars": ["NETLICENSING_API_KEY", "NETLICENSING_TOKEN"], - }, - "netlify": { - "display_name": "Netlify's API documentation", - "description": "Netlify is a hosting service for the programmable web. It understands your documents and provides an API to handle atomic deploys of websites, manage ", - "spec_url": "https://api.apis.guru/v2/specs/netlify.com/2.15.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/netlify/open-api/master/swagger.yml", - "env_vars": ["NETLIFY_AUTH_TOKEN"], - }, - "neutrinoapi": { - "display_name": "Neutrino API", - "description": "The general-purpose API", - "spec_url": "https://api.apis.guru/v2/specs/neutrinoapi.net/3.6.3/openapi.json", - "base_url": "https://www.neutrinoapi.com/api/swagger.json", - "env_vars": ["NEUTRINOAPI_API_KEY", "NEUTRINOAPI_TOKEN"], - }, - "nexmo-com-account": { - "display_name": "Account API", - "description": "Enables users to manage their Vonage API Account by programmable means. More information is available here:

        Applications V1 is deprecated

        This version of ', - "spec_url": "https://api.apis.guru/v2/specs/nexmo.com/application/1.0.2/openapi.json", - "base_url": "https://raw.githubusercontent.com/nexmo/api-specification/master/definitions/application.yml", - "env_vars": ["NEXMO_API_KEY", "NEXMO_API_SECRET"], - }, - "nexmo-com-application-v2": { - "display_name": "Application API", - "description": "Vonage provides an Application API to allow management of your Vonage Applications. This API is backwards compatible with version 1. Applications crea", - "spec_url": "https://api.apis.guru/v2/specs/nexmo.com/application.v2/2.1.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/nexmo/api-specification/master/definitions/application.v2.yml", - "env_vars": ["NEXMO_API_KEY", "NEXMO_API_SECRET"], - }, - "nexmo-com-audit": { - "display_name": "Audit API", - "description": "The Vonage Audit API allows you to view details of changes to your account. More information is available at
        Take the API f", - "spec_url": "https://api.apis.guru/v2/specs/onsched.com/consumer/v1/openapi.json", - "base_url": "https://sandbox-api.onsched.com/swagger/consumer/swagger.json", - "env_vars": ["ONSCHED_COM_CONSUMER_API_KEY", "ONSCHED_COM_CONSUMER_TOKEN"], - }, - "onsched-com-setup": { - "display_name": "OnSched Setup API", - "description": "Build secure and scalable custom apps for onboarding and setup. Our flexible API provides many options for configuration.

        Take the API for a ", - "spec_url": "https://api.apis.guru/v2/specs/onsched.com/setup/v1/openapi.json", - "base_url": "https://sandbox-api.onsched.com/swagger/setup/swagger.json", - "env_vars": ["ONSCHED_COM_SETUP_API_KEY", "ONSCHED_COM_SETUP_TOKEN"], - }, - "onsched-com-utility": { - "display_name": "OnSched API Utility", - "description": "Endpoints for system utilities. e.g.Health", - "spec_url": "https://api.apis.guru/v2/specs/onsched.com/utility/v1/openapi.json", - "base_url": "https://sandbox-api.onsched.com/swagger/utility/swagger.json", - "env_vars": ["ONSCHED_COM_UTILITY_API_KEY", "ONSCHED_COM_UTILITY_TOKEN"], - }, - "openai": { - "display_name": "OpenAI API", - "description": "APIs for sampling from and fine-tuning language models", - "spec_url": "https://api.apis.guru/v2/specs/openai.com/1.2.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/openai/openai-openapi/master/openapi.yaml", - "env_vars": ["OPENAI_API_KEY"], - "endpoints_count": 226, - }, - "openalpr": { - "display_name": "OpenALPR CarCheck API", - "description": "The OpenALPR CarCheck API allows you to send images to the cloud for processing. The image will be analyzed for license plates and vehicle make/models", - "spec_url": "https://api.apis.guru/v2/specs/openalpr.com/3.0.1/swagger.json", - "base_url": "http://doc.openalpr.com/api/specs/cloudapi.yaml", - "env_vars": ["OPENALPR_API_KEY", "OPENALPR_TOKEN"], - }, - "openapi-generator-tech": { - "display_name": "OpenAPI Generator Online", - "description": "This is an online openapi generator server. You can find out more at https://github.com/OpenAPITools/openapi-generator.", - "spec_url": "https://api.apis.guru/v2/specs/openapi-generator.tech/6.2.1/openapi.json", - "base_url": "http://api.openapi-generator.tech/api-docs", - "env_vars": ["OPENAPI_GENERATOR_TECH_API_KEY", "OPENAPI_GENERATOR_TECH_TOKEN"], - }, - "openapi-space": { - "display_name": "OpenAPI space", - "description": "This is the API for OpenAPI space.", - "spec_url": "https://api.apis.guru/v2/specs/openapi.space/1.0.0/swagger.json", - "base_url": "https://openapi.space/api/v1/swagger.json", - "env_vars": ["OPENAPI_SPACE_API_KEY", "OPENAPI_SPACE_TOKEN"], - }, - "openaq": { - "display_name": "OpenAQ", - "description": "API for OpenAQ LCS", - "spec_url": "https://api.apis.guru/v2/specs/openaq.local/2.0.0/openapi.json", - "base_url": "https://docs.openaq.org/openapi.json", - "env_vars": ["OPENAQ_API_KEY", "OPENAQ_TOKEN"], - }, - "openbanking-org-uk": { - "display_name": "Open Data API", - "description": "Latest Swagger specification for OpenData", - "spec_url": "https://api.apis.guru/v2/specs/openbanking.org.uk/v1.3/openapi.json", - "base_url": "https://raw.githubusercontent.com/OpenBankingUK/opendata-api-spec-compiled/master/opendata-swagger.json", - "env_vars": ["OPENBANKING_ORG_UK_API_KEY", "OPENBANKING_ORG_UK_TOKEN"], - }, - "openbanking-org-uk-account-info-openapi": { - "display_name": "Account and Transaction API Specification", - "description": "Swagger for Account and Transaction API Specification", - "spec_url": "https://api.apis.guru/v2/specs/openbanking.org.uk/account-info-openapi/3.1.7/openapi.json", - "base_url": "https://raw.githubusercontent.com/OpenBankingUK/read-write-api-specs/master/dist/openapi/account-info-openapi.yaml", - "env_vars": [ - "OPENBANKING_ORG_UK_ACCOUNT_INFO_OPENAPI_API_KEY", - "OPENBANKING_ORG_UK_ACCOUNT_INFO_OPENAPI_TOKEN", - ], - }, - "openbanking-org-uk-confirmation-funds-openapi": { - "display_name": "Confirmation of Funds API Specification", - "description": "Swagger for Confirmation of Funds API Specification", - "spec_url": "https://api.apis.guru/v2/specs/openbanking.org.uk/confirmation-funds-openapi/3.1.7/openapi.json", - "base_url": "https://raw.githubusercontent.com/OpenBankingUK/read-write-api-specs/master/dist/openapi/confirmation-funds-openapi.yaml", - "env_vars": [ - "OPENBANKING_ORG_UK_CONFIRMATION_FUNDS_OPENAPI_API_KEY", - "OPENBANKING_ORG_UK_CONFIRMATION_FUNDS_OPENAPI_TOKEN", - ], - }, - "openbanking-org-uk-event-notifications-openapi": { - "display_name": "Event Notification API Specification - TPP Endpoints", - "description": "Swagger for Event Notification API Specification - TPP Endpoints", - "spec_url": "https://api.apis.guru/v2/specs/openbanking.org.uk/event-notifications-openapi/3.1.7/openapi.json", - "base_url": "https://raw.githubusercontent.com/OpenBankingUK/read-write-api-specs/master/dist/openapi/event-notifications-openapi.yaml", - "env_vars": [ - "OPENBANKING_ORG_UK_EVENT_NOTIFICATIONS_OPENAPI_API_KEY", - "OPENBANKING_ORG_UK_EVENT_NOTIFICATIONS_OPENAPI_TOKEN", - ], - }, - "openbanking-org-uk-payment-initiation-openapi": { - "display_name": "Payment Initiation API", - "description": "Swagger for Payment Initiation API Specification", - "spec_url": "https://api.apis.guru/v2/specs/openbanking.org.uk/payment-initiation-openapi/3.1.7/openapi.json", - "base_url": "https://raw.githubusercontent.com/OpenBankingUK/read-write-api-specs/master/dist/openapi/payment-initiation-openapi.yaml", - "env_vars": [ - "OPENBANKING_ORG_UK_PAYMENT_INITIATION_OPENAPI_API_KEY", - "OPENBANKING_ORG_UK_PAYMENT_INITIATION_OPENAPI_TOKEN", - ], - }, - "openbankingproject-ch": { - "display_name": "Swiss NextGen Banking API-Framework", - "description": "# Summary The **Swiss NextGen API** is based on the NextGenPSD2 *Framework Version 1.3.4* of the Berlin Group which offers a modern, open, harmonised ", - "spec_url": "https://api.apis.guru/v2/specs/openbankingproject.ch/1.3.8_2020-12-14 - Swiss edition 1.3.8.1-CH/openapi.json", - "base_url": "https://raw.githubusercontent.com/openbankingproject-ch/obp-apis/master/swiss-ng-api.yaml", - "env_vars": ["OPENBANKINGPROJECT_CH_API_KEY", "OPENBANKINGPROJECT_CH_TOKEN"], - }, - "opencagedata": { - "display_name": "OpenCage Geocoder", - "description": "Worldwide forward and reverse geocoding", - "spec_url": "https://api.apis.guru/v2/specs/opencagedata.com/1/swagger.json", - "base_url": "https://opencagedata.com/swagger.yaml", - "env_vars": ["OPENCAGEDATA_API_KEY", "OPENCAGEDATA_TOKEN"], - }, - "openchannel-io-market": { - "display_name": "OpenChannel Market API", - "spec_url": "https://api.apis.guru/v2/specs/openchannel.io/market/2.0.24/openapi.json", - "base_url": "https://api.swaggerhub.com/apis/OpenChannel/Market-API/2.0.24", - "env_vars": ["OPENCHANNEL_IO_MARKET_API_KEY", "OPENCHANNEL_IO_MARKET_TOKEN"], - }, - "opendatanetwork": { - "display_name": "ODN API", - "description": "The Socrata OpenDataNetwork (ODN) REST API exposes public data, often continuosly updated and enhanced, from many thousands of public government and n", - "spec_url": "https://api.apis.guru/v2/specs/opendatanetwork.com/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/socrata/odn-backend/master/opendatanetwork-apiary.apib", - "env_vars": ["OPENDATANETWORK_API_KEY", "OPENDATANETWORK_TOKEN"], - }, - "opendatasoft": { - "display_name": "opendatasoft", - "spec_url": "https://api.apis.guru/v2/specs/opendatasoft.com/2.1.0/swagger.json", - "base_url": "http://public.opendatasoft.com/api/v2/swagger.json", - "env_vars": ["OPENDATASOFT_API_KEY", "OPENDATASOFT_TOKEN"], - }, - "openfigi": { - "display_name": "OpenFIGI API", - "description": "A free & open API for FIGI discovery.", - "spec_url": "https://api.apis.guru/v2/specs/openfigi.com/1.4.0/openapi.json", - "base_url": "https://api.openfigi.com/schema", - "env_vars": ["OPENFIGI_API_KEY", "OPENFIGI_TOKEN"], - }, - "openfintech": { - "display_name": "OpenFinTech.io", - "description": "# Introduction [OpenFinTech.io](https://openfintech.io) is an open database that comprises of standardized primary data for FinTech industry.
        It c", - "spec_url": "https://api.apis.guru/v2/specs/openfintech.io/2017-08-24/swagger.json", - "base_url": "https://docs.openfintech.io/swagger.yaml", - "env_vars": ["OPENFINTECH_API_KEY", "OPENFINTECH_TOKEN"], - }, - "openindex-ai": { - "display_name": "OpenIndex Retrieval Plugin API", - "description": "A retrieval API for querying and filtering documents based on natural language queries and metadata", - "spec_url": "https://api.apis.guru/v2/specs/openindex.ai/1.0.0/openapi.json", - "base_url": "https://retriever.openindex.ai/.well-known/openapi.yaml", - "env_vars": ["OPENINDEX_AI_API_KEY", "OPENINDEX_AI_TOKEN"], - }, - "openlinksw-com-osdb": { - "display_name": "OSDB REST API v1", - "description": "An OpenAPI description of the OpenLink Smart Data Bot REST API v1", - "spec_url": "https://api.apis.guru/v2/specs/openlinksw.com/osdb/1.0.0/openapi.json", - "base_url": "https://osdb.openlinksw.com/osdb/osdb_rest_api.openapi.json", - "env_vars": ["OPENLINKSW_COM_OSDB_API_KEY", "OPENLINKSW_COM_OSDB_TOKEN"], - }, - "openpolicy": { - "display_name": "Open Policy Agent (OPA) REST API", - "description": "OPA provides policy-based control for cloud native environments. The following *endpoints* (such as `PUT /v1/policies`) provide reference documentatio", - "spec_url": "https://api.apis.guru/v2/specs/openpolicy.local/0.28.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/open-policy-agent/contrib/master/open_api/openapi.yaml", - "env_vars": ["OPENPOLICY_API_KEY", "OPENPOLICY_TOKEN"], - }, - "openstates": { - "display_name": "Open States API v3", - "description": "* [More documentation](https://docs.openstates.org/en/latest/api/v3/index.html) * [Register for an account](https://openstates.org/accounts/signup/) *", - "spec_url": "https://api.apis.guru/v2/specs/openstates.org/2021.11.12/openapi.json", - "base_url": "https://v3.openstates.org/openapi.json", - "env_vars": ["OPENSTATES_API_KEY", "OPENSTATES_TOKEN"], - }, - "openstf": { - "display_name": "Smartphone Test Farm", - "description": "Control and manages real Smartphone devices from browser and restful apis", - "spec_url": "https://api.apis.guru/v2/specs/openstf.io/2.3.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/openstf/stf/master/lib/units/api/swagger/api_v1_generated.json", - "env_vars": ["OPENSTF_API_KEY", "OPENSTF_TOKEN"], - }, - "opensuse-org-obs": { - "display_name": "Open Build Service API", - "description": "The _Open Build Service API_ is a XML API. To authenticate, use [HTTP basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)", - "spec_url": "https://api.apis.guru/v2/specs/opensuse.org/obs/2.10.50/openapi.json", - "base_url": "https://build.opensuse.org/apidocs-new/OBS-v2.10.50.yaml", - "env_vars": ["OPENSUSE_ORG_OBS_API_KEY", "OPENSUSE_ORG_OBS_TOKEN"], - }, - "opentargets": { - "display_name": "Open Targets Platform REST API", - "description": "### The Open Targets Platform REST API The Open Targets Platform API ('Application Programming Interface') allows programmatic retrieval of the Open T", - "spec_url": "https://api.apis.guru/v2/specs/opentargets.io/19.02.1/openapi.json", - "base_url": "http://api.opentargets.io/v3/platform/swagger", - "env_vars": ["OPENTARGETS_API_KEY", "OPENTARGETS_TOKEN"], - }, - "opentrials": { - "display_name": "OpenTrials API", - "spec_url": "https://api.apis.guru/v2/specs/opentrials.local/0.0.1/swagger.json", - "base_url": "https://raw.githubusercontent.com/opentrials/api/master/api/swagger/swagger.yaml", - "env_vars": ["OPENTRIALS_API_KEY", "OPENTRIALS_TOKEN"], - }, - "openuv": { - "display_name": "OpenUV - Global Real-Time UV Index Forecast API", - "description": "The missing minimalistic JSON real-time UV Index API for awesome Developers, Innovators and Smart Home Enthusiasts", - "spec_url": "https://api.apis.guru/v2/specs/openuv.io/v1/openapi.json", - "base_url": "https://gist.githubusercontent.com/MikeRalphson/77bb693d4cf9213909527b9cc4566609/raw/9febc5d16d8d162c93f3503510ce0808a8075d5b/openuv.yaml", - "env_vars": ["OPENUV_API_KEY", "OPENUV_TOKEN"], - }, - "optimade": { - "display_name": "OPTIMADE API", - "description": "The [Open Databases Integration for Materials Design (OPTIMADE) consortium](https://www.optimade.org/) aims to make materials databases interoperation", - "spec_url": "https://api.apis.guru/v2/specs/optimade.local/1.1.0~develop/openapi.json", - "base_url": "https://raw.githubusercontent.com/Materials-Consortia/OPTIMADE/master/schemas/openapi_schema.json", - "env_vars": ["OPTIMADE_API_KEY", "OPTIMADE_TOKEN"], - }, - "orbit-love": { - "display_name": "Orbit API", - "description": "Please see the complete Orbit API documentation at [https://api.orbit.love/](https://api.orbit.love/).", - "spec_url": "https://api.apis.guru/v2/specs/orbit.love/v1/openapi.json", - "base_url": "https://app.orbit.love/api-docs/v1/swagger.json", - "env_vars": ["ORBIT_LOVE_API_KEY", "ORBIT_LOVE_TOKEN"], - }, - "orghunter": { - "display_name": "OrgHunter", - "description": "Get the latest IRS data and most up to date charity information for your website or application", - "spec_url": "https://api.apis.guru/v2/specs/orghunter.com/1.0.0/swagger.json", - "base_url": "https://orghunter.3scale.net/swagger/spec.json", - "env_vars": ["ORGHUNTER_API_KEY", "ORGHUNTER_TOKEN"], - }, - "ornl-gov-daymet": { - "display_name": "Daymet Single Pixel Extraction Tool API", - "description": "Welcome to the Daymet Single Pixel Extraction Tool API. You can use this API to download daily surface data within the Daymet database in a `csv` or `", - "spec_url": "https://api.apis.guru/v2/specs/ornl.gov/daymet/1.0.2/swagger.json", - "base_url": "https://daymet.ornl.gov/single-pixel/static/swagger.json", - "env_vars": ["ORNL_GOV_DAYMET_API_KEY", "ORNL_GOV_DAYMET_TOKEN"], - }, - "orthanc-server": { - "display_name": "Orthanc API", - "description": "This is the full documentation of the [REST API](https://book.orthanc-server.com/users/rest.html) of Orthanc.

        This reference is automatically genera", - "spec_url": "https://api.apis.guru/v2/specs/orthanc-server.com/1.11.3/openapi.json", - "base_url": "https://api.orthanc-server.com/orthanc-openapi.json", - "env_vars": ["ORTHANC_SERVER_API_KEY", "ORTHANC_SERVER_TOKEN"], - }, - "osf": { - "display_name": "OSF APIv2 Documentation", - "spec_url": "https://api.apis.guru/v2/specs/osf.io/2.0/openapi.json", - "base_url": "http://developer.osf.io/swagger.json", - "env_vars": ["OSF_API_KEY", "OSF_TOKEN"], - }, - "osisoft": { - "display_name": "PI Web API 2018 SP1 Swagger Spec", - "description": "Swagger Spec file that describes PI Web API", - "spec_url": "https://api.apis.guru/v2/specs/osisoft.com/1.11.1.5383/swagger.json", - "base_url": "https://devdata.osisoft.com/piwebapi/help/specification", - "env_vars": ["OSISOFT_API_KEY", "OSISOFT_TOKEN"], - }, - "ote-godaddy-com-abuse": { - "display_name": "", - "description": "GoDaddy Abuse API Terms of Use:

        GoDaddyโ€™s Abuse API is provided to simplify and standardize the abuse reporting experience. To help", - "spec_url": "https://api.apis.guru/v2/specs/ote-godaddy.com/abuse/1.0.0/openapi.json", - "base_url": "https://developer.godaddy.com/swagger/swagger_abuse.json", - "env_vars": ["OTE_GODADDY_COM_ABUSE_API_KEY", "OTE_GODADDY_COM_ABUSE_TOKEN"], - }, - "ote-godaddy-com-aftermarket": { - "display_name": "", - "spec_url": "https://api.apis.guru/v2/specs/ote-godaddy.com/aftermarket/1.0.0/openapi.json", - "base_url": "https://developer.godaddy.com/swagger/swagger_aftermarket.json", - "env_vars": [ - "OTE_GODADDY_COM_AFTERMARKET_API_KEY", - "OTE_GODADDY_COM_AFTERMARKET_TOKEN", - ], - }, - "ote-godaddy-com-agreements": { - "display_name": "", - "spec_url": "https://api.apis.guru/v2/specs/ote-godaddy.com/agreements/1.0.0/openapi.json", - "base_url": "https://developer.godaddy.com/swagger/swagger_agreements.json", - "env_vars": [ - "OTE_GODADDY_COM_AGREEMENTS_API_KEY", - "OTE_GODADDY_COM_AGREEMENTS_TOKEN", - ], - }, - "ote-godaddy-com-certificates": { - "display_name": "", - "spec_url": "https://api.apis.guru/v2/specs/ote-godaddy.com/certificates/1.0.0/openapi.json", - "base_url": "https://developer.godaddy.com/swagger/swagger_certificates.json", - "env_vars": [ - "OTE_GODADDY_COM_CERTIFICATES_API_KEY", - "OTE_GODADDY_COM_CERTIFICATES_TOKEN", - ], - }, - "ote-godaddy-com-countries": { - "display_name": "", - "spec_url": "https://api.apis.guru/v2/specs/ote-godaddy.com/countries/1.0.0/openapi.json", - "base_url": "https://developer.godaddy.com/swagger/swagger_countries.json", - "env_vars": [ - "OTE_GODADDY_COM_COUNTRIES_API_KEY", - "OTE_GODADDY_COM_COUNTRIES_TOKEN", - ], - }, - "ote-godaddy-com-domains": { - "display_name": "", - "spec_url": "https://api.apis.guru/v2/specs/ote-godaddy.com/domains/1.0.0/openapi.json", - "base_url": "https://developer.godaddy.com/swagger/swagger_domains.json", - "env_vars": [ - "OTE_GODADDY_COM_DOMAINS_API_KEY", - "OTE_GODADDY_COM_DOMAINS_TOKEN", - ], - }, - "ote-godaddy-com-orders": { - "display_name": "", - "spec_url": "https://api.apis.guru/v2/specs/ote-godaddy.com/orders/1.0.0/openapi.json", - "base_url": "https://developer.godaddy.com/swagger/swagger_orders.json", - "env_vars": ["OTE_GODADDY_COM_ORDERS_API_KEY", "OTE_GODADDY_COM_ORDERS_TOKEN"], - }, - "ote-godaddy-com-shoppers": { - "display_name": "", - "spec_url": "https://api.apis.guru/v2/specs/ote-godaddy.com/shoppers/1.0.0/openapi.json", - "base_url": "https://developer.godaddy.com/swagger/swagger_shoppers.json", - "env_vars": [ - "OTE_GODADDY_COM_SHOPPERS_API_KEY", - "OTE_GODADDY_COM_SHOPPERS_TOKEN", - ], - }, - "ote-godaddy-com-subscriptions": { - "display_name": "", - "spec_url": "https://api.apis.guru/v2/specs/ote-godaddy.com/subscriptions/1.0.0/openapi.json", - "base_url": "https://developer.godaddy.com/swagger/swagger_subscriptions.json", - "env_vars": [ - "OTE_GODADDY_COM_SUBSCRIPTIONS_API_KEY", - "OTE_GODADDY_COM_SUBSCRIPTIONS_TOKEN", - ], - }, - "owler": { - "display_name": "Owler", - "description": "Search for information on companies using a website or company name and get access to Company Data, News, Blog Posts, Competitor Lists and much more.", - "spec_url": "https://api.apis.guru/v2/specs/owler.com/1.0.0/swagger.json", - "base_url": "https://developers.owler.com/swagger/spec.json", - "env_vars": ["OWLER_API_KEY", "OWLER_TOKEN"], - }, - "oxforddictionaries": { - "display_name": "Oxford Dictionaries", - "spec_url": "https://api.apis.guru/v2/specs/oxforddictionaries.com/1.11.0/openapi.json", - "base_url": "https://developer.oxforddictionaries.com/swagger/spec/public_doc_guest.json", - "env_vars": ["OXFORDDICTIONARIES_API_KEY", "OXFORDDICTIONARIES_TOKEN"], - }, - "paccurate": { - "display_name": "paccurate.io", - "spec_url": "https://api.apis.guru/v2/specs/paccurate.io/0.1.1/swagger.json", - "base_url": "http://api.paccurate.io/static/api/0.1.1/swagger.yaml", - "env_vars": ["PACCURATE_API_KEY", "PACCURATE_TOKEN"], - }, - "pandascore-co": { - "display_name": "PandaScore REST API for All Videogames", - "description": "# Introduction Whether you're looking to build an official Pandascore integration for your service, or you just want to build something awesome, [we c", - "spec_url": "https://api.apis.guru/v2/specs/pandascore.co/2.23.1/openapi.json", - "base_url": "blob:https://developers.pandascore.co/blobId", - "env_vars": ["PANDASCORE_CO_API_KEY", "PANDASCORE_CO_TOKEN"], - }, - "pandorabots": { - "display_name": "Pandorabots AIaaS", - "description": "AIaaS provides API access to our bot hosting platform and SDKs, allowing developers to easily integrate conversational interfaces into applications.", - "spec_url": "https://api.apis.guru/v2/specs/pandorabots.com/1.0.0/swagger.json", - "base_url": "https://developer.pandorabots.com/swagger/spec.json", - "env_vars": ["PANDORABOTS_API_KEY", "PANDORABOTS_TOKEN"], - }, - "papinet-io-orderstatus": { - "display_name": "papiNet API", - "description": "papinet API is a global initiative for the Forst and Paper supply chain.", - "spec_url": "https://api.apis.guru/v2/specs/papinet.io/order_status/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/papinet/papiNet-API/master/1.0.0/papiNet-API.yaml", - "env_vars": ["PAPINET_IO_ORDERSTATUS_API_KEY", "PAPINET_IO_ORDERSTATUS_TOKEN"], - }, - "passwordutility": { - "display_name": "PasswordUtility.Web", - "description": "Validate and generate passwords using open source tools", - "spec_url": "https://api.apis.guru/v2/specs/passwordutility.net/v1/swagger.json", - "base_url": "http://passwordutility.net/swagger/docs/v1", - "env_vars": ["PASSWORDUTILITY_API_KEY", "PASSWORDUTILITY_TOKEN"], - }, - "patientview": { - "display_name": "PatientView", - "description": "The recommended REST API endpoints to be used when integrating with PatientView", - "spec_url": "https://api.apis.guru/v2/specs/patientview.org/1.0/openapi.json", - "base_url": "https://www.patientview.org/api/api-docs", - "env_vars": ["PATIENTVIEW_API_KEY", "PATIENTVIEW_TOKEN"], - }, - "patrowl": { - "display_name": "Swagger API-REST for Patrowl Engines", - "description": "This is the API documentation for Patrowl Engines usage.", - "spec_url": "https://api.apis.guru/v2/specs/patrowl.local/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/Patrowl/PatrowlDocs/master/api/openapi-patrowl-engines.yaml", - "env_vars": ["PATROWL_API_KEY", "PATROWL_TOKEN"], - }, - "pay1-de-link": { - "display_name": "PAYONE Link API", - "spec_url": "https://api.apis.guru/v2/specs/pay1.de/link/v1/openapi.json", - "base_url": "https://gist.githubusercontent.com/MikeRalphson/b526159dff395f5da394bdf0c1d8b004/raw/ec8e44241fcd114d00c4662a04d2ff5b16f4b95f/payone-openapi.yaml", - "env_vars": ["PAY1_DE_LINK_API_KEY", "PAY1_DE_LINK_TOKEN"], - }, - "paylocity": { - "display_name": "Paylocity API", - "description": "For general questions and support of the API, contact: webservices@paylocity.com # Overview Paylocity Web Services API is an externally facing RESTful", - "spec_url": "https://api.apis.guru/v2/specs/paylocity.com/2/openapi.json", - "base_url": "https://api.paylocity.com/api/v2/openapi", - "env_vars": ["PAYLOCITY_API_KEY", "PAYLOCITY_TOKEN"], - }, - "payments-service-gov-uk-payments": { - "display_name": "GOV.UK Pay API", - "description": "GOV.UK Pay API (This version is no longer maintained. See openapi/publicapi_spec.json for latest API specification)", - "spec_url": "https://api.apis.guru/v2/specs/payments.service.gov.uk/payments/1.0.3/swagger.json", - "base_url": "https://raw.githubusercontent.com/alphagov/pay-publicapi/master/swagger/swagger.json", - "env_vars": [ - "PAYMENTS_SERVICE_GOV_UK_PAYMENTS_API_KEY", - "PAYMENTS_SERVICE_GOV_UK_PAYMENTS_TOKEN", - ], - }, - "paypi-dev": { - "display_name": "EmailVerify", - "description": "OTP email verification API by PayPI.

        EmailVerify provides a simple way to verify email addresses. We send emails ourselves taking the burde", - "spec_url": "https://api.apis.guru/v2/specs/paypi.dev/1.0.0/openapi.json", - "base_url": "https://paypi-default-images.s3.eu-west-1.amazonaws.com/openapi.yaml", - "env_vars": ["PAYPI_DEV_API_KEY", "PAYPI_DEV_TOKEN"], - }, - "payrun": { - "display_name": "PayRun.IO", - "description": "Open, scableable, transparent payroll API.", - "spec_url": "https://api.apis.guru/v2/specs/payrun.io/22.23.10.42/openapi.json", - "base_url": "https://api.test.payrun.io/swagger/json", - "env_vars": ["PAYRUN_API_KEY", "PAYRUN_TOKEN"], - }, - "pdfblocks": { - "display_name": "PDF Blocks API", - "description": "PDF Blocks is a secure, reliable, and fast API to work with PDF documents. Actions include: Merge PDF documents, add or remove passwords, add watermar", - "spec_url": "https://api.apis.guru/v2/specs/pdfblocks.com/1.5.0/openapi.json", - "base_url": "https://www.pdfblocks.com/assets/specs/pdfblocks.openapi.yaml", - "env_vars": ["PDFBLOCKS_API_KEY", "PDFBLOCKS_TOKEN"], - }, - "pdfbroker": { - "display_name": "PdfBroker.io API", - "description": "PdfBroker.io is an api for creating pdf files from Xsl-Fo or Html and other useful pdf utilities.", - "spec_url": "https://api.apis.guru/v2/specs/pdfbroker.io/v1/openapi.json", - "base_url": "https://api.pdfbroker.io/swagger/v1/swagger.json", - "env_vars": ["PDFBROKER_API_KEY", "PDFBROKER_TOKEN"], - }, - "pdfgeneratorapi": { - "display_name": "PDF Generator API", - "description": "# Introduction PDF Generator API allows you easily generate transactional PDF documents and reduce the development and support costs by enabling your ", - "spec_url": "https://api.apis.guru/v2/specs/pdfgeneratorapi.com/3.1.1/openapi.json", - "base_url": "https://docs.pdfgeneratorapi.com/api-docs.json?_c=1590697087", - "env_vars": ["PDFGENERATORAPI_API_KEY", "PDFGENERATORAPI_TOKEN"], - }, - "peel-ci": { - "display_name": "Peel Tune-in API", - "description": "The machine learning service APIs utilize hashtags from Twitter to find related, trending shows, related Twitter hashtags in real time and to generate", - "spec_url": "https://api.apis.guru/v2/specs/peel-ci.com/1.0.0/swagger.json", - "base_url": "https://s3-us-west-2.amazonaws.com/tuneinapi.peel-ci.com/resources.json", - "env_vars": ["PEEL_CI_API_KEY", "PEEL_CI_TOKEN"], - }, - "pendo": { - "display_name": "Pendo Feedback API", - "description": "## Who is this for? This documentation is for developers creating their own integration with [Feedback's](https://www.pendo.io/product/feedback/) API.", - "spec_url": "https://api.apis.guru/v2/specs/pendo.io/1.0.0/swagger.json", - "base_url": "http://apidoc.receptive.io/receptive.swagger.json", - "env_vars": ["PENDO_API_KEY", "PENDO_TOKEN"], - }, - "peoplefinderspro": { - "display_name": "Self Service Developer API", - "description": "Self Service Developer API documentation and demo. ##Getting Started You will need an API access profile user and password in order to access search e", - "spec_url": "https://api.apis.guru/v2/specs/peoplefinderspro.com/1.0.0/openapi.json", - "base_url": "https://pfent1821.docs.apiary.io/api-description-document", - "env_vars": ["PEOPLEFINDERSPRO_API_KEY", "PEOPLEFINDERSPRO_TOKEN"], - }, - "peoplegeneratorapi-live": { - "display_name": "OpenAPI definition", - "spec_url": "https://api.apis.guru/v2/specs/peoplegeneratorapi.live/v0/openapi.json", - "base_url": "https://peoplegeneratorapi.live/v3/api-docs", - "env_vars": [ - "PEOPLEGENERATORAPI_LIVE_API_KEY", - "PEOPLEGENERATORAPI_LIVE_TOKEN", - ], - }, - "personio-de-authentication": { - "display_name": "Authentication", - "description": "Personio Authentication API", - "spec_url": "https://api.apis.guru/v2/specs/personio.de/authentication/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/personio/api-docs/master/personio-auth-api.yaml", - "env_vars": [ - "PERSONIO_DE_AUTHENTICATION_API_KEY", - "PERSONIO_DE_AUTHENTICATION_TOKEN", - ], - }, - "personio-de-personnel": { - "display_name": "Personnel Data", - "description": "API for reading and writing personnel data incl. data about attendances and absences", - "spec_url": "https://api.apis.guru/v2/specs/personio.de/personnel/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/personio/api-docs/master/personio-personnel-data-api.yaml", - "env_vars": ["PERSONIO_DE_PERSONNEL_API_KEY", "PERSONIO_DE_PERSONNEL_TOKEN"], - }, - "phantauth": { - "display_name": "PhantAuth", - "description": "Random User Generator + OpenID Connect Provider. Like Lorem Ipsum, but for user accounts and authentication. The PhantAuth API documentation is availa", - "spec_url": "https://api.apis.guru/v2/specs/phantauth.net/1.0.0/openapi.json", - "base_url": "https://www.phantauth.net/api.json", - "env_vars": ["PHANTAUTH_API_KEY", "PHANTAUTH_TOKEN"], - }, - "phila-gov-pollingplaces": { - "display_name": "Polling Places API", - "description": "This data set contains the list of polling places. It can be organized by ward/division, accessibility rating, or type of building. This list is used ", - "spec_url": "https://api.apis.guru/v2/specs/phila.gov/pollingplaces/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/CityOfPhiladelphia/phlapi/gh-pages/pollingplaces/swagger.json", - "env_vars": [ - "PHILA_GOV_POLLINGPLACES_API_KEY", - "PHILA_GOV_POLLINGPLACES_TOKEN", - ], - }, - "pims": { - "display_name": "Pims", - "description": "Hereafter is the documentation of the private API of [Pims: Pointages Intelligents pour le Monde du Spectacle](https://pims.io). This API is designed ", - "spec_url": "https://api.apis.guru/v2/specs/pims.io/1.0/swagger.json", - "base_url": "https://cdn.pims.io/api/swagger.json", - "env_vars": ["PIMS_API_KEY", "PIMS_TOKEN"], - }, - "pinecone": { - "display_name": "Pinecone API", - "description": "Pinecone is a vector database. This is an unofficial, community-managed OpenAPI spec that (should) accurately model the Pinecone API. This project was", - "spec_url": "https://api.apis.guru/v2/specs/pinecone.io/20230401.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/sigpwned/pinecone-openapi-spec/main/openapi.yml", - "env_vars": ["PINECONE_API_KEY", "PINECONE_TOKEN"], - }, - "plaid": { - "display_name": "The Plaid API", - "description": "The Plaid REST API. Please see https://plaid.com/docs/api for more details.", - "spec_url": "https://api.apis.guru/v2/specs/plaid.com/2020-09-14_1.334.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/plaid/plaid-openapi/master/2020-09-14.yml", - "env_vars": ["PLAID_CLIENT_ID", "PLAID_SECRET"], - }, - "pocketsmith": { - "display_name": "PocketSmith", - "description": "The PocketSmith API", - "spec_url": "https://api.apis.guru/v2/specs/pocketsmith.com/2.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/pocketsmith/api/master/openapi.json", - "env_vars": ["POCKETSMITH_API_KEY", "POCKETSMITH_TOKEN"], - }, - "poemist": { - "display_name": "Poemist API", - "spec_url": "https://api.apis.guru/v2/specs/poemist.com/1.0/swagger.json", - "base_url": "https://www.poemist.com/api-docs.json", - "env_vars": ["POEMIST_API_KEY", "POEMIST_TOKEN"], - }, - "polygon": { - "display_name": "Polygon", - "description": "The future of fintech.", - "spec_url": "https://api.apis.guru/v2/specs/polygon.io/1.0.0/swagger.json", - "base_url": "https://polygon.io/docs/swagger.json", - "env_vars": ["POLYGON_API_KEY", "POLYGON_TOKEN"], - }, - "portfoliooptimizer": { - "display_name": "Portfolio Optimizer", - "description": "Portfolio Optimizer is a [Web API](https://en.wikipedia.org/wiki/Web_API) to analyze and optimize investment portfolios (collection of financial asset", - "spec_url": "https://api.apis.guru/v2/specs/portfoliooptimizer.io/1.0.9/openapi.json", - "base_url": "https://docs.portfoliooptimizer.io/openapi/portfoliooptimizer.yaml", - "env_vars": ["PORTFOLIOOPTIMIZER_API_KEY", "PORTFOLIOOPTIMIZER_TOKEN"], - }, - "postmarkapp-com-account": { - "display_name": "Postmark Account-level API", - "description": "Postmark makes sending and receiving email incredibly easy. The Account-level API allows users to configure all Servers, Domains, and Sender Signature", - "spec_url": "https://api.apis.guru/v2/specs/postmarkapp.com/account/0.9.0/swagger.json", - "base_url": "https://postmarkapp.com/swagger/account.yml", - "env_vars": [ - "POSTMARKAPP_COM_ACCOUNT_API_KEY", - "POSTMARKAPP_COM_ACCOUNT_TOKEN", - ], - }, - "postmarkapp-com-server": { - "display_name": "Postmark API", - "description": "Postmark makes sending and receiving email incredibly easy.", - "spec_url": "https://api.apis.guru/v2/specs/postmarkapp.com/server/1.0.0/swagger.json", - "base_url": "https://postmarkapp.com/swagger/server.yml", - "env_vars": ["POSTMARKAPP_COM_SERVER_API_KEY", "POSTMARKAPP_COM_SERVER_TOKEN"], - }, - "powerdns": { - "display_name": "PowerDNS Authoritative HTTP API", - "spec_url": "https://api.apis.guru/v2/specs/powerdns.local/0.0.13/swagger.json", - "base_url": "https://raw.githubusercontent.com/PowerDNS/pdns/master/docs/http-api/swagger/authoritative-api-swagger.yaml", - "env_vars": ["POWERDNS_API_KEY", "POWERDNS_TOKEN"], - }, - "presalytics-io-converter": { - "display_name": "Doc Converter", - "description": "This api converts file formats of OpenXml and OpenOffice documents formats to vector files (e.g., svg)", - "spec_url": "https://api.apis.guru/v2/specs/presalytics.io/converter/0.1/openapi.json", - "base_url": "https://api.presalytics.io/doc-converter/openapi.json", - "env_vars": [ - "PRESALYTICS_IO_CONVERTER_API_KEY", - "PRESALYTICS_IO_CONVERTER_TOKEN", - ], - }, - "presalytics-io-ooxml": { - "display_name": "OOXML Automation", - "description": "This API helps users convert Excel and Powerpoint documents into rich, live dashboards and stories.", - "spec_url": "https://api.apis.guru/v2/specs/presalytics.io/ooxml/0.1.0/openapi.json", - "base_url": "https://api.presalytics.io/ooxml-automation/docs/v1/openapi.json", - "env_vars": ["PRESALYTICS_IO_OOXML_API_KEY", "PRESALYTICS_IO_OOXML_TOKEN"], - }, - "presalytics-io-story": { - "display_name": "Story", - "description": "This API is the main entry point for creating, editing and publishing analytics throught the Presalytics API", - "spec_url": "https://api.apis.guru/v2/specs/presalytics.io/story/0.3.1/openapi.json", - "base_url": "https://api.presalytics.io/story/openapi.json", - "env_vars": ["PRESALYTICS_IO_STORY_API_KEY", "PRESALYTICS_IO_STORY_TOKEN"], - }, - "pressassociation": { - "display_name": "TV API", - "description": "Welcome to the API Reference Docs page for the Press Association TV API (v2).", - "spec_url": "https://api.apis.guru/v2/specs/pressassociation.io/2.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/PressAssociation/tv-api-v2-development-kit/master/swagger.yaml", - "env_vars": ["PRESSASSOCIATION_API_KEY", "PRESSASSOCIATION_TOKEN"], - }, - "probely": { - "display_name": "Probely Developers", - "description": "Probely is a Web Vulnerability Scanning suite for Agile Teams. It provides continuous scanning of your Web Applications and lets you efficiently manag", - "spec_url": "https://api.apis.guru/v2/specs/probely.com/1.2.0/openapi.json", - "base_url": "https://developers.probely.com/openapi.yaml", - "env_vars": ["PROBELY_API_KEY", "PROBELY_TOKEN"], - }, - "proxykingdom": { - "display_name": "ProxyKingdom-Api", - "spec_url": "https://api.apis.guru/v2/specs/proxykingdom.com/v1/openapi.json", - "base_url": "https://api.proxykingdom.com/swagger/v1/swagger.json", - "env_vars": ["PROXYKINGDOM_API_KEY", "PROXYKINGDOM_TOKEN"], - }, - "prss": { - "display_name": "ContentDepot", - "description": "ContentDepot hosts a range of APIโ€™s that allow clients to manage, discover, and obtain content. The API spans many parts of the ContentDepot functiona", - "spec_url": "https://api.apis.guru/v2/specs/prss.org/2.0.0/openapi.json", - "base_url": "https://contentdepot.prss.org/api/swagger-v2.yaml", - "env_vars": ["PRSS_API_KEY", "PRSS_TOKEN"], - }, - "ptv-vic-gov-au": { - "display_name": "PTV Timetable API - Version 3", - "description": "The PTV Timetable API provides direct access to Public Transport Victoriaโ€™s public transport timetable data. The API returns scheduled timetable, rout", - "spec_url": "https://api.apis.guru/v2/specs/ptv.vic.gov.au/v3/openapi.json", - "base_url": "http://timetableapi.ptv.vic.gov.au/swagger/docs/v3", - "env_vars": ["PTV_VIC_GOV_AU_API_KEY", "PTV_VIC_GOV_AU_TOKEN"], - }, - "qualpay": { - "display_name": "Qualpay Payment Gateway API", - "description": "This document describes the Qualpay Payment Gateway API.", - "spec_url": "https://api.apis.guru/v2/specs/qualpay.com/1.7.0/swagger.json", - "base_url": "https://api-test.qualpay.com/pg/doc", - "env_vars": ["QUALPAY_API_KEY", "QUALPAY_TOKEN"], - }, - "qualtrics": { - "display_name": "Qualtrics API", - "description": "Work with Qualtrics surveys, distributions and response events", - "spec_url": "https://api.apis.guru/v2/specs/qualtrics.com/0.2/openapi.json", - "base_url": "https://raw.githubusercontent.com/microsoft/powerplatform-qualtrics-api/main/Qualtrics%20Connector%20Spec%20-%20Swagger%202.0.json", - "env_vars": ["QUALTRICS_API_KEY", "QUALTRICS_TOKEN"], - }, - "quarantine-country": { - "display_name": "Coronavirus API", - "description": "Coronavirus API with free COVID-19 live updates. The best free coronavirus API and COVID-19 update source. Programmatically access live corona virus u", - "spec_url": "https://api.apis.guru/v2/specs/quarantine.country/1.0/swagger.json", - "base_url": "https://quarantine.country/coronavirus/api/swagger.json", - "env_vars": ["QUARANTINE_COUNTRY_API_KEY", "QUARANTINE_COUNTRY_TOKEN"], - }, - "quickchart": { - "display_name": "QuickChart API", - "description": "An API to generate charts and QR codes using QuickChart services.", - "spec_url": "https://api.apis.guru/v2/specs/quickchart.io/1.0.0/openapi.json", - "base_url": "https://quickchart.io/openapi.yaml", - "env_vars": ["QUICKCHART_API_KEY", "QUICKCHART_TOKEN"], - }, - "quicksold-co-uk-location": { - "display_name": "Quicksold REST API", - "spec_url": "https://api.apis.guru/v2/specs/quicksold.co.uk/location/1.0/swagger.json", - "base_url": "https://quicksold.co.uk/v2/api-docs", - "env_vars": [ - "QUICKSOLD_CO_UK_LOCATION_API_KEY", - "QUICKSOLD_CO_UK_LOCATION_TOKEN", - ], - }, - "quotes-rest": { - "display_name": "They Said So Quotes API", - "description": "They Said So Quotes API offers a complete feature rich REST API access to its quotes platform. This is the documentation for the world famous [quotes ", - "spec_url": "https://api.apis.guru/v2/specs/quotes.rest/3.1/openapi.json", - "base_url": "http://quotes.rest/yaml/theysaidso.quotes.openapi.yaml?v1.1", - "env_vars": ["QUOTES_REST_API_KEY", "QUOTES_REST_TOKEN"], - }, - "randomlovecraft": { - "display_name": "Random Lovecraft", - "description": "Random sentences from the complete works of H.P. Lovecraft. CORS-enabled.", - "spec_url": "https://api.apis.guru/v2/specs/randomlovecraft.com/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/dekadans/randomlovecraft.com/master/public/openapi.yaml", - "env_vars": ["RANDOMLOVECRAFT_API_KEY", "RANDOMLOVECRAFT_TOKEN"], - }, - "randommer": { - "display_name": "Randommer API", - "spec_url": "https://api.apis.guru/v2/specs/randommer.io/v1/openapi.json", - "base_url": "https://randommer.io/api/docs/v1/swagger.json", - "env_vars": ["RANDOMMER_API_KEY", "RANDOMMER_TOKEN"], - }, - "rapidapi-com-dynamicdocs": { - "display_name": "DynamicDocs", - "description": "ADVICEment's [DynamicDocs API automates your document generation](https://advicement.io/dynamic-documents-api) and creates dynamic, optimized, interac", - "spec_url": "https://api.apis.guru/v2/specs/rapidapi.com/dynamicdocs/1.0/openapi.json", - "base_url": "https://advicement.io/open_api_spec/dynamicdocs.json", - "env_vars": ["RAPIDAPI_KEY"], - }, - "rapidapi-com-ecowetter": { - "display_name": "Historische Daten", - "description": "Abfrage von Wetterdaten aus der Vergangenheit. Der maximale Abfragezeitraum betrรคgt 366 Tage (1 Jahr).", - "spec_url": "https://api.apis.guru/v2/specs/rapidapi.com/ecowetter/1.0.0/openapi.json", - "base_url": "https://corrently.io/attachments/4", - "env_vars": ["RAPIDAPI_KEY"], - }, - "rapidapi-com-football-prediction": { - "display_name": "Football Prediction API", - "description": "The Football Prediction API allows developers to get predictions for upcoming football (soccer) matches, results for past matches, and performance mon", - "spec_url": "https://api.apis.guru/v2/specs/rapidapi.com/football-prediction/2/openapi.json", - "base_url": "https://boggio-analytics.com/fp-api/schema/football-prediction-openapi.yaml", - "env_vars": ["RAPIDAPI_KEY"], - }, - "rapidapi-com-idealspot-geodata": { - "display_name": "IdealSpot GeoData", - "description": "Hyperlocal Demographics, Vehicle Traffic, Economic, Market Signals, and More. Use this API to request IdealSpot hyperlocal geospatial market insight a", - "spec_url": "https://api.apis.guru/v2/specs/rapidapi.com/idealspot-geodata/1.0/openapi.json", - "base_url": "https://idealspot.gitlab.io/developer-docs/specs/idealspot-swagger20.json", - "env_vars": ["RAPIDAPI_KEY"], - }, - "rapidapi-com-language-identification": { - "display_name": "Language Identification (Prediction)", - "description": "Automatic language detection for any texts. Supports over 150 languages.", - "spec_url": "https://api.apis.guru/v2/specs/rapidapi.com/language-identification/1.0.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/BigLobsterito/apis/master/language-identification-swagger.yaml", - "env_vars": ["RAPIDAPI_KEY"], - }, - "rapidapi-com-spellcheckpro": { - "display_name": "SpellCheckPro", - "spec_url": "https://api.apis.guru/v2/specs/rapidapi.com/spellcheckpro/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/VipulBachani/SpellCheckerPro/44da96c5bfa723362f5a3d3d6af441a4e818b93a/SpellCheckPro.postman_collection.json", - "env_vars": ["RAPIDAPI_KEY"], - }, - "rawg": { - "display_name": "RAWG Video Games Database API", - "description": "The largest open video games database. ### Why build on RAWG - More than 350,000 games for 50 platforms including mobiles. - Rich metadata: tags, genr", - "spec_url": "https://api.apis.guru/v2/specs/rawg.io/v1.0/openapi.json", - "base_url": "https://api.rawg.io/docs/?format=openapi", - "env_vars": ["RAWG_API_KEY", "RAWG_TOKEN"], - }, - "rbaskets-in": { - "display_name": "Request Baskets API", - "description": "RESTful API of [Request Baskets](https://rbaskets.in) service. Request Baskets is an open source project of a service to collect HTTP requests and ins", - "spec_url": "https://api.apis.guru/v2/specs/rbaskets.in/1.0.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/darklynx/request-baskets/master/doc/api-swagger.yaml", - "env_vars": ["RBASKETS_IN_API_KEY", "RBASKETS_IN_TOKEN"], - }, - "readme": { - "display_name": "API Endpoints", - "description": "Create beautiful product and API documentation with our developer friendly platform.", - "spec_url": "https://api.apis.guru/v2/specs/readme.io/2.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/readmeio/oas/master/packages/examples/3.0/json/readme.json", - "env_vars": ["README_API_KEY", "README_TOKEN"], - }, - "rebilly": { - "display_name": "Rebilly REST API", - "description": "# Introduction The Rebilly API is built on HTTP. Our API is RESTful. It has predictable resource URLs. It returns HTTP response codes to indicate erro", - "spec_url": "https://api.apis.guru/v2/specs/rebilly.com/2.1/openapi.json", - "base_url": "https://api-reference.rebilly.com/openapi.json", - "env_vars": ["REBILLY_API_KEY", "REBILLY_TOKEN"], - }, - "redeal": { - "display_name": "Redeal Analytics API", - "description": "Access analytics for Redeal", - "spec_url": "https://api.apis.guru/v2/specs/redeal.io/1.0.0/openapi.json", - "base_url": "https://static.redeal.se/APIDefinitions/analytics.redeal.io-1.0.0-swagger.yaml", - "env_vars": ["REDEAL_API_KEY", "REDEAL_TOKEN"], - }, - "redeal-io-analytics": { - "display_name": "Redeal Analytics API", - "description": "Access analytics for Redeal", - "spec_url": "https://api.apis.guru/v2/specs/redeal.io/analytics/1.0.0/openapi.json", - "base_url": "https://static.redeal.se/APIDefinitions/analytics.redeal.io-1.0.0-swagger.yaml", - "env_vars": ["REDEAL_IO_ANALYTICS_API_KEY", "REDEAL_IO_ANALYTICS_TOKEN"], - }, - "redhat-com-cataloginventory": { - "display_name": "Catalog Inventory", - "description": "Catalog Inventory", - "spec_url": "https://api.apis.guru/v2/specs/redhat.com/catalog_inventory/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/RedHatInsights/catalog_inventory-api/master/public/doc/openapi-3-v1.0.json", - "env_vars": [ - "REDHAT_COM_CATALOGINVENTORY_API_KEY", - "REDHAT_COM_CATALOGINVENTORY_TOKEN", - ], - }, - "redhat-local-patchman-engine": { - "display_name": "Patchman-engine API", - "description": "API of the Patch application on [cloud.redhat.com](cloud.redhat.com) Syntax of the `filter[name]` query parameters is described in [Filters documentat", - "spec_url": "https://api.apis.guru/v2/specs/redhat.local/patchman-engine/v1.15.3/openapi.json", - "base_url": "https://raw.githubusercontent.com/RedHatInsights/patchman-engine/master/docs/openapi.json", - "env_vars": [ - "REDHAT_LOCAL_PATCHMAN_ENGINE_API_KEY", - "REDHAT_LOCAL_PATCHMAN_ENGINE_TOKEN", - ], - }, - "redirection": { - "display_name": "redirection.io", - "description": "API documentation for redirection.io", - "spec_url": "https://api.apis.guru/v2/specs/redirection.io/1.1.0/swagger.json", - "base_url": "https://api.redirection.io/docs.json", - "env_vars": ["REDIRECTION_API_KEY", "REDIRECTION_TOKEN"], - }, - "refugerestrooms": { - "display_name": "Refuge Restrooms API", - "description": "REFUGE is a web application that seeks to provide safe restroom access for transgender, intersex, and gender nonconforming individuals.", - "spec_url": "https://api.apis.guru/v2/specs/refugerestrooms.org/0.0.1/swagger.json", - "base_url": "https://www.refugerestrooms.org/api/swagger_doc.json", - "env_vars": ["REFUGERESTROOMS_API_KEY", "REFUGERESTROOMS_TOKEN"], - }, - "regcheck-org-uk": { - "display_name": "Car Registration API", - "description": "Car Registration API, An API that retrieves car information from its numberplate in many countries worldwide, uncluding the USA, UK, India, Australia ", - "spec_url": "https://api.apis.guru/v2/specs/regcheck.org.uk/1.0.0/swagger.json", - "base_url": "https://api.swaggerhub.com/apis/infiniteloopltd/CarRegistration/1.0.0", - "env_vars": ["REGCHECK_ORG_UK_API_KEY", "REGCHECK_ORG_UK_TOKEN"], - }, - "reloadly": { - "display_name": "topupsapi", - "description": "Polls is a simple API allowing consumers to view polls and vote in them.", - "spec_url": "https://api.apis.guru/v2/specs/reloadly.com/1.0.0/openapi.json", - "base_url": "https://topupsapi.docs.apiary.io/api-description-document", - "env_vars": ["RELOADLY_API_KEY", "RELOADLY_TOKEN"], - }, - "remove-bg": { - "display_name": "Background Removal API", - "description": "Remove the background of any image", - "spec_url": "https://api.apis.guru/v2/specs/remove.bg/1.0.0/openapi.json", - "base_url": "https://www.remove.bg/api/swagger.yaml", - "env_vars": ["REMOVE_BG_API_KEY", "REMOVE_BG_TOKEN"], - }, - "restful4up": { - "display_name": "RESTful4Up", - "description": "RESTful API 4 Unipacker", - "spec_url": "https://api.apis.guru/v2/specs/restful4up.local/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/rpgeeganage/restful4up/master/app/spec/api.yml", - "env_vars": ["RESTFUL4UP_API_KEY", "RESTFUL4UP_TOKEN"], - }, - "rev-ai": { - "display_name": "Asynchronous Speech-To-Text API Documentation", - "description": "Rev.ai provides quality speech-text recognition via a RESTful API. All public methods and objects are documented here for developer reference. For a r", - "spec_url": "https://api.apis.guru/v2/specs/rev.ai/v1/openapi.json", - "base_url": "http://api.rev.ai/openapi/v1/documentation.yaml", - "env_vars": ["REV_AI_API_KEY", "REV_AI_TOKEN"], - }, - "reverb": { - "display_name": "reverb", - "description": "reverb", - "spec_url": "https://api.apis.guru/v2/specs/reverb.com/3.0/openapi.json", - "base_url": "https://s3.amazonaws.com/swagger.reverb.com/swagger.json", - "env_vars": ["REVERB_API_KEY", "REVERB_TOKEN"], - }, - "ritc": { - "display_name": "Ritc", - "description": "Rules in the Cloud", - "spec_url": "https://api.apis.guru/v2/specs/ritc.io/1.0.0/swagger.json", - "base_url": "http://www.ritc.io/openapi/ritc.swagger.json", - "env_vars": ["RITC_API_KEY", "RITC_TOKEN"], - }, - "ritekit": { - "display_name": "RiteKit API", - "description": "RiteKit API is based on REST principles. Authentication uses standard OAuth 2.0 process ##Getting started 1. Sign up for [RiteKit](https://ritekit.com", - "spec_url": "https://api.apis.guru/v2/specs/ritekit.com/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/RiteKit/api-docs/master/apiary.apib", - "env_vars": ["RITEKIT_API_KEY", "RITEKIT_TOKEN"], - }, - "roaring": { - "display_name": "CompanyAPI", - "spec_url": "https://api.apis.guru/v2/specs/roaring.io/1.0/swagger.json", - "base_url": "https://developer.roaring.io/store/api-docs/admin/CompanyAPI/1.0", - "env_vars": ["ROARING_API_KEY", "ROARING_TOKEN"], - }, - "rottentomatoes": { - "display_name": "Rotten Tomatoes", - "description": "Test our API services using I/O Docs.", - "spec_url": "https://api.apis.guru/v2/specs/rottentomatoes.com/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/Mermade/openapi_specifications/master/mashery/api.rottentomatoes.com/Rotten-Tomatoes/1.0/swagger.json", - "env_vars": ["ROTTENTOMATOES_API_KEY", "ROTTENTOMATOES_TOKEN"], - }, - "royalmail-com-click-and-drop": { - "display_name": "ChannelShipper & Royal Mail Public API", - "description": "Import your orders, retrieve your orders and generate labels.", - "spec_url": "https://api.apis.guru/v2/specs/royalmail.com/click-and-drop/1.0.0/swagger.json", - "base_url": "https://api.parcel.royalmail.com/doc/v1/click-and-drop-api-v1.yaml", - "env_vars": [ - "ROYALMAIL_COM_CLICK_AND_DROP_API_KEY", - "ROYALMAIL_COM_CLICK_AND_DROP_TOKEN", - ], - }, - "rudder-example": { - "display_name": "Rudder API", - "description": "Download OpenAPI specification: [openapi.yml](openapi.yml) # Introduction Rudder exposes a REST API, enabling the user to interact with Rudder without", - "spec_url": "https://api.apis.guru/v2/specs/rudder.example.local/16/openapi.json", - "base_url": "https://docs.rudder.io/api/openapi.yml", - "env_vars": ["RUDDER_EXAMPLE_API_KEY", "RUDDER_EXAMPLE_TOKEN"], - }, - "rumble-run": { - "display_name": "Rumble API (deprecated)", - "description": '

        Rumble Network Discovery is now runZero. Read the announcement.

        ', - "spec_url": "https://api.apis.guru/v2/specs/rumble.run/2.15.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/RumbleDiscovery/rumble-api/main/rumble-api.yml", - "env_vars": ["RUMBLE_RUN_API_KEY", "RUMBLE_RUN_TOKEN"], - }, - "runscope": { - "display_name": "Runscope API", - "description": "Manage Runscope programmatically.", - "spec_url": "https://api.apis.guru/v2/specs/runscope.com/1.0.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/Runscope/runscope-api-examples/master/schemas/runscope-swagger-v2.json", - "env_vars": ["RUNSCOPE_API_KEY", "RUNSCOPE_TOKEN"], - }, - "sakari": { - "display_name": "Sakari", - "description": "# Introduction Welcome to the documentation for the Sakari Messaging REST API. Sakari provides an advanced platform to drive large scale customized SM", - "spec_url": "https://api.apis.guru/v2/specs/sakari.io/1.0.1/openapi.json", - "base_url": "https://developer.sakari.io/openapi.yaml", - "env_vars": ["SAKARI_API_KEY", "SAKARI_TOKEN"], - }, - "salesforce-local-einstein": { - "display_name": "Einstein Vision and Einstein Language", - "description": "Provided by [Salesforce](https://www.einstein-hub.com/) ๏ฟฝ Copyright 2000๏ฟฝ2020 salesforce.com, inc. All rights reserved. Salesforce is a registered tra", - "spec_url": "https://api.apis.guru/v2/specs/salesforce.local/einstein/2.0.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/MetaMind/openapi/master/vision-language-api-openapi-2.0.1.yaml", - "env_vars": ["SALESFORCE_ACCESS_TOKEN"], - }, - "salesloft": { - "display_name": "SalesLoft Platform", - "description": "SalesLoft helps transform sales teams into modern sales organizations - converting more target accounts into customer accounts", - "spec_url": "https://api.apis.guru/v2/specs/salesloft.com/v2/openapi.json", - "base_url": "https://developers.salesloft.com/v2_api_def.json", - "env_vars": ["SALESLOFT_API_KEY", "SALESLOFT_TOKEN"], - }, - "schooldigger": { - "display_name": "SchoolDigger API V1", - "description": "Get detailed data on over 120,000 schools and 18,500 districts in the U.S.", - "spec_url": "https://api.apis.guru/v2/specs/schooldigger.com/v1/swagger.json", - "base_url": "https://api.schooldigger.com/swagger/docs/v1", - "env_vars": ["SCHOOLDIGGER_API_KEY", "SCHOOLDIGGER_TOKEN"], - }, - "scideas-net-perfectpdf": { - "display_name": "perfectpdf api", - "description": "The perfectpdf api does one thing, perfectly: it converts html to pdf. The perfectpdf api uses headless Google Chrome to provide a low cost, high qual", - "spec_url": "https://api.apis.guru/v2/specs/scideas.net/perfectpdf/1.0/openapi.json", - "base_url": "https://services.scideas.net/perfectpdf/resources/openapi3.0.0-perfectpdf-1.0.json", - "env_vars": ["SCIDEAS_NET_PERFECTPDF_API_KEY", "SCIDEAS_NET_PERFECTPDF_TOKEN"], - }, - "scideas-net-regression": { - "display_name": "Regression analysis api", - "description": "This data processing api uses regression analysis to allow you to find out which contributing variables have the most effect on an outcome. For exampl", - "spec_url": "https://api.apis.guru/v2/specs/scideas.net/regression/1.0/openapi.json", - "base_url": "https://services.scideas.net/regression/resources/openapi3.0.0-regression-1.0.json", - "env_vars": ["SCIDEAS_NET_REGRESSION_API_KEY", "SCIDEAS_NET_REGRESSION_TOKEN"], - }, - "scrapewebsite-email": { - "display_name": "Scrape Website Email API", - "description": "ScrapeWebsiteEmail is a service that exposes an api to fetch e-mails from a website.", - "spec_url": "https://api.apis.guru/v2/specs/scrapewebsite.email/0.1/swagger.json", - "base_url": "http://scrapewebsite.email/v1/swagger_doc.json", - "env_vars": ["SCRAPEWEBSITE_EMAIL_API_KEY", "SCRAPEWEBSITE_EMAIL_TOKEN"], - }, - "seldon-local-core": { - "display_name": "Seldon External API", - "spec_url": "https://api.apis.guru/v2/specs/seldon.local/core/0.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/SeldonIO/seldon-core/master/openapi/wrapper.oas3.json", - "env_vars": ["SELDON_LOCAL_CORE_API_KEY", "SELDON_LOCAL_CORE_TOKEN"], - }, - "seldon-local-engine": { - "display_name": "Seldon External API", - "spec_url": "https://api.apis.guru/v2/specs/seldon.local/engine/0.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/SeldonIO/seldon-core/master/openapi/engine.oas3.json", - "env_vars": ["SELDON_LOCAL_ENGINE_API_KEY", "SELDON_LOCAL_ENGINE_TOKEN"], - }, - "seldon-local-wrapper": { - "display_name": "Seldon External API", - "spec_url": "https://api.apis.guru/v2/specs/seldon.local/wrapper/0.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/SeldonIO/seldon-core/master/openapi/wrapper.oas3.json", - "env_vars": ["SELDON_LOCAL_WRAPPER_API_KEY", "SELDON_LOCAL_WRAPPER_TOKEN"], - }, - "selectpdf": { - "display_name": "SelectPdf HTML To PDF API", - "description": "SelectPdf HTML To PDF Online REST API is a professional solution that lets you create PDF from web pages and raw HTML code in your applications. The A", - "spec_url": "https://api.apis.guru/v2/specs/selectpdf.com/1.0.0/swagger.json", - "base_url": "https://selectpdf.com/api/selectpdf-swagger.json", - "env_vars": ["SELECTPDF_API_KEY", "SELECTPDF_TOKEN"], - }, - "semantria": { - "display_name": "Semantria", - "description": "Semantria applies Text and Sentiment Analysis to tweets, facebook posts, surveys, reviews or enterprise content.", - "spec_url": "https://api.apis.guru/v2/specs/semantria.com/4.0/swagger.json", - "base_url": "https://semantria.com/developer/api-docs-prod", - "env_vars": ["SEMANTRIA_API_KEY", "SEMANTRIA_TOKEN"], - }, - "sendgrid": { - "display_name": "Email Activity (beta)", - "description": "The Beta endpoints for the new Email Activity APIs - functionality is subject to change without notice. You may not have access to this Beta endpoint.", - "spec_url": "https://api.apis.guru/v2/specs/sendgrid.com/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/sendgrid/sendgrid-oai/main/oai.json", - "env_vars": ["SENDGRID_API_KEY"], - }, - "setlist-fm": { - "display_name": "setlist.fm API", - "description": "

        The setlist.fm API has been designed to give you easy access to setlist data in order to build fancy websites and other applications. Before start", - "spec_url": "https://api.apis.guru/v2/specs/setlist.fm/1.0/swagger.json", - "base_url": "https://api.setlist.fm/docs/1.0/ui/swagger.json", - "env_vars": ["SETLIST_FM_API_KEY", "SETLIST_FM_TOKEN"], - }, - "sheerseo": { - "display_name": "SheerSEO API", - "description": "Sheerseo API has 2 stages:
        First stage - initiating the task: You fill in your task and receive in return the task id.
        Second stage - collectin", - "spec_url": "https://api.apis.guru/v2/specs/sheerseo.com/0.0.1/swagger.json", - "base_url": "https://www.sheerseo.com/api/swagger.yaml", - "env_vars": ["SHEERSEO_API_KEY", "SHEERSEO_TOKEN"], - }, - "sheetlabs-com-rig-veda": { - "display_name": "rv API", - "description": "# Introduction This API returns information about all of the verses in Rig Veda. The results are JSON objects that contain the name of the god, poet, ", - "spec_url": "https://api.apis.guru/v2/specs/sheetlabs.com/rig-veda/1.2/swagger.json", - "base_url": "https://raw.githubusercontent.com/AninditaBasu/indica/master/rv_SDKs/rv.yaml", - "env_vars": ["SHEETLABS_COM_RIG_VEDA_API_KEY", "SHEETLABS_COM_RIG_VEDA_TOKEN"], - }, - "sheetlabs-com-vedic-society": { - "display_name": "vs API", - "description": "# Introduction This API returns data regarding almost all nouns in vedic literature. The results are JSON objects that contain the word transliterated", - "spec_url": "https://api.apis.guru/v2/specs/sheetlabs.com/vedic-society/1.2/swagger.json", - "base_url": "https://raw.githubusercontent.com/AninditaBasu/indica/master/vs_SDKs/vs.yaml", - "env_vars": [ - "SHEETLABS_COM_VEDIC_SOCIETY_API_KEY", - "SHEETLABS_COM_VEDIC_SOCIETY_TOKEN", - ], - }, - "shipengine": { - "display_name": "ShipEngine API", - "description": "ShipEngine's easy-to-use REST API lets you manage all of your shipping needs without worrying about the complexities of different carrier APIs and pro", - "spec_url": "https://api.apis.guru/v2/specs/shipengine.com/1.1.202303022103/openapi.json", - "base_url": "https://raw.githubusercontent.com/ShipEngine/shipengine-openapi/master/openapi.yaml", - "env_vars": ["SHIPENGINE_API_KEY", "SHIPENGINE_TOKEN"], - }, - "shipstation": { - "display_name": "shipstation", - "description": "Polls is a simple API allowing consumers to view polls and vote in them.", - "spec_url": "https://api.apis.guru/v2/specs/shipstation.com/1.0.0/openapi.json", - "base_url": "https://shipstation.docs.apiary.io/api-description-document", - "env_vars": ["SHIPSTATION_API_KEY", "SHIPSTATION_TOKEN"], - }, - "shop-app": { - "display_name": "Shop", - "description": "Search for millions of products from the world's greatest brands.", - "spec_url": "https://api.apis.guru/v2/specs/shop.app/v1/openapi.json", - "base_url": "https://server.shop.app/openai/v1/api.json", - "env_vars": ["SHOP_APP_API_KEY", "SHOP_APP_TOKEN"], - }, - "shop-pro-jp": { - "display_name": "ใ‚ซใƒฉใƒผใƒŸใƒผใ‚ทใƒงใƒƒใƒ—ใ‚ขใƒ—ใƒชใ‚นใƒˆใ‚ข API", - "description": "# ใ‚ซใƒฉใƒผใƒŸใƒผใ‚ทใƒงใƒƒใƒ—ใ‚ขใƒ—ใƒชใ‚นใƒˆใ‚ข API [ใ‚ขใƒ—ใƒชใ‚นใƒˆใ‚ข](https://app.shop-pro.jp/)ใซใฆๅ…ฌ้–‹ใ™ใ‚‹ใ‚ขใƒ—ใƒชใซๅฏพใ—ใฆใ€ไธ€่ˆฌๅ…ฌ้–‹ใ—ใฆใ„ใ‚‹[ใ‚ซใƒฉใƒผใƒŸใƒผใ‚ทใƒงใƒƒใƒ—API](https://developer.shop-pro.jp/docs/colorme-api)ใซๅŠ ใˆใฆใ€ใ‚ซใƒฉใƒผใƒŸใƒผ", - "spec_url": "https://api.apis.guru/v2/specs/shop-pro.jp/1.0.0/openapi.json", - "base_url": "https://api.shop-pro.jp/appstore//v1/open_api.json", - "env_vars": ["SHOP_PRO_JP_API_KEY", "SHOP_PRO_JP_TOKEN"], - }, - "shorten-rest": { - "display_name": "Shorten.REST API Documentation", - "description": "## Introduction The Shorten.rest API allows you to programmatically create short URLs (an 'alias') for longer URL (a 'destination'", - "spec_url": "https://api.apis.guru/v2/specs/shorten.rest/1.0.0/openapi.json", - "base_url": "https://docs.shorten.rest/swagger.json", - "env_vars": ["SHORTEN_REST_API_KEY", "SHORTEN_REST_TOKEN"], - }, - "shotstack": { - "display_name": "Shotstack", - "description": "Shotstack is a video, image and audio editing service that allows for the automated generation of videos, images and audio using JSON and a RESTful AP", - "spec_url": "https://api.apis.guru/v2/specs/shotstack.io/v1/openapi.json", - "base_url": "https://raw.githubusercontent.com/shotstack/oas-api-definition/main/api.oas3.yaml", - "env_vars": ["SHOTSTACK_API_KEY", "SHOTSTACK_TOKEN"], - }, - "shutterstock": { - "display_name": "Shutterstock API Explorer", - "description": "The Shutterstock API provides access to Shutterstock's library of media, as well as information about customers' accounts and the contributors that pr", - "spec_url": "https://api.apis.guru/v2/specs/shutterstock.com/1.1.32/openapi.json", - "base_url": "https://api-explorer.shutterstock.com/openapi.json", - "env_vars": ["SHUTTERSTOCK_API_KEY", "SHUTTERSTOCK_TOKEN"], - }, - "signl4": { - "display_name": "SIGNL4 API", - "description": "

        Use our API for systems integration or to build your own use cases. Sample scenarios include but are not limited to:

        • 2-way integration: ", - "spec_url": "https://api.apis.guru/v2/specs/signl4.com/v1/openapi.json", - "base_url": "https://connect.signl4.com/api/docs/v1/swagger.json", - "env_vars": ["SIGNL4_API_KEY", "SIGNL4_TOKEN"], - }, - "simplivpn": { - "display_name": "SimpliVPNAPI", - "spec_url": "https://api.apis.guru/v2/specs/simplivpn.net/1.0/openapi.json", - "base_url": "https://api.simplivpn.net/swagger/v1/swagger.json", - "env_vars": ["SIMPLIVPN_API_KEY", "SIMPLIVPN_TOKEN"], - }, - "simplyrets": { - "display_name": "SimplyRETS", - "description": "The SimplyRETS API is an exciting step towards making it easier for developers and real estate agents to build something awesome with real estate data", - "spec_url": "https://api.apis.guru/v2/specs/simplyrets.com/1.0.0/swagger.json", - "base_url": "https://docs.simplyrets.com/api/assets/resources.json", - "env_vars": ["SIMPLYRETS_API_KEY", "SIMPLYRETS_TOKEN"], - }, - "sinao-app": { - "display_name": "Sinao API", - "description": "Sinao API for account management, apps administration and network exploration", - "spec_url": "https://api.apis.guru/v2/specs/sinao.app/1.1.0/openapi.json", - "base_url": "https://api.sinao.app/v1/swagger.yaml", - "env_vars": ["SINAO_APP_API_KEY", "SINAO_APP_TOKEN"], - }, - "skynewz-api-fortnite-herokuapp": { - "display_name": "FORTNITE REST API", - "description": "REST API about Fortnite game", - "spec_url": "https://api.apis.guru/v2/specs/skynewz-api-fortnite.herokuapp.com/3.1.5/swagger.json", - "base_url": "https://raw.githubusercontent.com/SkYNewZ/rest-fornite-api/develop/src/public/swagger.yaml", - "env_vars": ["HEROKU_API_KEY"], - }, - "slack": { - "display_name": "Slack Web API", - "description": "One way to interact with the Slack platform is its HTTP RPC-based Web API, a collection of methods requiring OAuth 2.0-based user, bot, or workspace t", - "spec_url": "https://api.apis.guru/v2/specs/slack.com/1.7.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/slackapi/slack-api-specs/master/web-api/slack_web_openapi_v2.json", - "env_vars": ["SLACK_TOKEN", "SLACK_BOT_TOKEN"], - "endpoints_count": 174, - }, - "slack-com-openai": { - "display_name": "Slack AI Plugin", - "description": "A plugin that allows users to interact with Slack using ChatGPT", - "spec_url": "https://api.apis.guru/v2/specs/slack.com/openai/v1/openapi.json", - "base_url": "https://api.slack.com/specs/openapi/ai-plugin.yaml", - "env_vars": ["SLACK_TOKEN", "SLACK_BOT_TOKEN"], - "endpoints_count": 174, - }, - "slicebox": { - "display_name": "Slicebox API", - "description": "Slicebox - safe sharing of medical images", - "spec_url": "https://api.apis.guru/v2/specs/slicebox.local/2.0/swagger.json", - "base_url": "https://slicebox.github.io/slicebox-api/swagger.yaml", - "env_vars": ["BOX_ACCESS_TOKEN"], - }, - "slideroom": { - "display_name": "SlideRoom API V2", - "description": "SlideRoom provides a RESTful API for exporting data out of your organizations SlideRoom account.", - "spec_url": "https://api.apis.guru/v2/specs/slideroom.com/v2/swagger.json", - "base_url": "https://api.slideroom.com/schema/v2", - "env_vars": ["SLIDEROOM_API_KEY", "SLIDEROOM_TOKEN"], - }, - "smart-me": { - "display_name": "smart-me", - "description": "With the smart-me REST API you get Access to all your devices in the smart-me Cloud and you can add your own devices. So its an easy way to add the sm", - "spec_url": "https://api.apis.guru/v2/specs/smart-me.com/v1/openapi.json", - "base_url": "https://smart-me.com/swagger/docs/v1", - "env_vars": ["SMART_ME_API_KEY", "SMART_ME_TOKEN"], - }, - "sms77": { - "display_name": "sms77.io API", - "description": "sms77.io Swagger API. Get your API-Key now at sms77.io.", - "spec_url": "https://api.apis.guru/v2/specs/sms77.io/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/sms77io/api-schemes/master/json/openapi.json", - "env_vars": ["SMS77_API_KEY", "SMS77_TOKEN"], - }, - "snyk": { - "display_name": "Snyk API", - "description": "The Snyk API is available to customers on [Business and Enterprise plans](https://snyk.io/plans) and allows you to programatically integrate with Snyk", - "spec_url": "https://api.apis.guru/v2/specs/snyk.io/1.0.0/openapi.json", - "base_url": "https://snyk.docs.apiary.io/api-description-document", - "env_vars": ["SNYK_API_KEY", "SNYK_TOKEN"], - }, - "solarvps": { - "display_name": "Solar VPS", - "description": "This is the Solar VPS Public API. You can find more at http://www.solarvps.com", - "spec_url": "https://api.apis.guru/v2/specs/solarvps.com/1.0.0/swagger.json", - "base_url": "http://api.ss.solarvps.com/api-docs", - "env_vars": ["SOLARVPS_API_KEY", "SOLARVPS_TOKEN"], - }, - "sonar-trading": { - "display_name": "Sonar Trading", - "description": "Currency Authority: Exchange Rate of 1453 country currencies and crypto currencies", - "spec_url": "https://api.apis.guru/v2/specs/sonar.trading/1.0/swagger.json", - "base_url": "https://sonar.trading/docs/api-docs.json", - "env_vars": ["SONAR_TRADING_API_KEY", "SONAR_TRADING_TOKEN"], - }, - "soundcloud": { - "display_name": "SoundCloud Public API Specification", - "spec_url": "https://api.apis.guru/v2/specs/soundcloud.com/1.0.0/openapi.json", - "base_url": "https://gist.githubusercontent.com/MikeRalphson/a9eb3040cb611121b568844958564849/raw/d0996b827b9eb2ae4fa946b9aacf02b03dc3d933/openapi.json", - "env_vars": ["SOUNDCLOUD_API_KEY", "SOUNDCLOUD_TOKEN"], - }, - "spectrocoin": { - "display_name": "SpectroCoin Merchant", - "description": "This is an API designed for merchants who are using SpectroCoin services and wishes to integrate them locally.", - "spec_url": "https://api.apis.guru/v2/specs/spectrocoin.com/1.0.0/swagger.json", - "base_url": "https://spectrocoin.com/api-docs/merchant/1/", - "env_vars": ["SPECTROCOIN_API_KEY", "SPECTROCOIN_TOKEN"], - }, - "spinbot": { - "display_name": "Article Rewriter and Article Extractor API", - "description": "Spinbot.net propose a new solution based on high technologies for faster article spinner and extractor that you will love to use it.", - "spec_url": "https://api.apis.guru/v2/specs/spinbot.net/1.0/swagger.json", - "base_url": "http://spinbot.net/spinbot_api_swagger.yaml", - "env_vars": ["SPINBOT_API_KEY", "SPINBOT_TOKEN"], - }, - "spinitron": { - "display_name": "Spinitron v2 API", - "description": "## Notes **Tutorial demo** using this API is at [https://spinitron.com/v2-api-demo/](https://spinitron.com/v2-api-demo/). For web integration using if", - "spec_url": "https://api.apis.guru/v2/specs/spinitron.com/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/spinitron/v2api/master/spinitron.yaml", - "env_vars": ["SPINITRON_API_KEY", "SPINITRON_TOKEN"], - }, - "spoonacular": { - "display_name": "spoonacular API", - "description": "The spoonacular Nutrition, Recipe, and Food API allows you to access over thousands of recipes, thousands of ingredients, 800,000 food products, over ", - "spec_url": "https://api.apis.guru/v2/specs/spoonacular.com/1.1/openapi.json", - "base_url": "https://spoonacular.com/application/frontend/downloads/spoonacular-openapi-3.json", - "env_vars": ["SPOONACULAR_API_KEY", "SPOONACULAR_TOKEN"], - }, - "sportsdata-io-cbb-v3-scores": { - "display_name": "CBB v3 Scores", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/cbb-v3-scores/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/cbb-v3-scores.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-cbb-v3-stats": { - "display_name": "CBB v3 Stats", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/cbb-v3-stats/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/cbb-v3-stats.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-cfb-v3-scores": { - "display_name": "CFB v3 Scores", - "description": "CFB schedules, scores, team stats, odds, weather, and news API.", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/cfb-v3-scores/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/cfb-v3-scores.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-csgo-v3-scores": { - "display_name": "CS:GO v3 Scores", - "description": "CS:GO v3 Scores", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/csgo-v3-scores/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/csgo-v3-scores.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-csgo-v3-stats": { - "display_name": "CS:GO v3 Stats", - "description": "CS:GO v3 Stats", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/csgo-v3-stats/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/csgo-v3-stats.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-golf-v2": { - "display_name": "Golf v2", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/golf-v2/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/golf-v2.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-lol-v3-projections": { - "display_name": "LoL v3 Projections", - "description": "LoL v3 Projections", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/lol-v3-projections/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/lol-v3-projections.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-lol-v3-scores": { - "display_name": "LoL v3 Scores", - "description": "LoL v3 Scores", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/lol-v3-scores/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/lol-v3-scores.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-lol-v3-stats": { - "display_name": "LoL v3 Stats", - "description": "LoL v3 Stats", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/lol-v3-stats/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/lol-v3-stats.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-mlb-v3-play-by-play": { - "display_name": "MLB v3 Play-by-Play", - "description": "MLB play-by-play API.", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/mlb-v3-play-by-play/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/mlb-v3-play-by-play.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-mlb-v3-projections": { - "display_name": "MLB v3 Projections", - "description": "MLB projections API.", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/mlb-v3-projections/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/mlb-v3-projections.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-mlb-v3-rotoballer-articles": { - "display_name": "MLB v3 RotoBaller Articles", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/mlb-v3-rotoballer-articles/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/mlb-v3-rotoballer-articles.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-mlb-v3-rotoballer-premium-news": { - "display_name": "MLB v3 RotoBaller Premium News", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/mlb-v3-rotoballer-premium-news/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/mlb-v3-rotoballer-premium-news.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-mlb-v3-scores": { - "display_name": "MLB v3 Scores", - "description": "MLB scores API.", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/mlb-v3-scores/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/mlb-v3-scores.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-mlb-v3-stats": { - "display_name": "MLB v3 Stats", - "description": "MLB scores, stats, and news API.", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/mlb-v3-stats/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/mlb-v3-stats.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nascar-v2": { - "display_name": "NASCAR v2", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nascar-v2/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nascar-v2.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nba-v3-play-by-play": { - "display_name": "NBA v3 Play-by-Play", - "description": "NBA play-by-play API.", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nba-v3-play-by-play/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nba-v3-play-by-play.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nba-v3-projections": { - "display_name": "NBA v3 Projections", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nba-v3-projections/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nba-v3-projections.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nba-v3-rotoballer-articles": { - "display_name": "NBA v3 RotoBaller Articles", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nba-v3-rotoballer-articles/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nba-v3-rotoballer-articles.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nba-v3-rotoballer-premium-news": { - "display_name": "NBA v3 RotoBaller Premium News", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nba-v3-rotoballer-premium-news/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nba-v3-rotoballer-premium-news.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nba-v3-scores": { - "display_name": "NBA v3 Scores", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nba-v3-scores/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nba-v3-scores.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nba-v3-stats": { - "display_name": "NBA v3 Stats", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nba-v3-stats/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nba-v3-stats.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nfl-v3-play-by-play": { - "display_name": "NFL v3 Play-by-Play", - "description": "NFL play-by-play API.", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nfl-v3-play-by-play/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nfl-v3-play-by-play.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nfl-v3-projections": { - "display_name": "NFL v3 Projections", - "description": "NFL projected stats API.", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nfl-v3-projections/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nfl-v3-projections.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nfl-v3-rotoballer-articles": { - "display_name": "NFL v3 RotoBaller Articles", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nfl-v3-rotoballer-articles/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nfl-v3-rotoballer-articles.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nfl-v3-rotoballer-premium-news": { - "display_name": "NFL v3 RotoBaller Premium News", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nfl-v3-rotoballer-premium-news/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nfl-v3-rotoballer-premium-news.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nfl-v3-scores": { - "display_name": "NFL v3 Scores", - "description": "NFL schedules, scores, odds, weather, and news API.", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nfl-v3-scores/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nfl-v3-scores.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nfl-v3-stats": { - "display_name": "NFL v3 Stats", - "description": "NFL rosters, player stats, team stats, and fantasy stats API.", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nfl-v3-stats/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nfl-v3-stats.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nhl-v3-play-by-play": { - "display_name": "NHL v3 Play-by-Play", - "description": "NHL play-by-play API.", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nhl-v3-play-by-play/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nhl-v3-play-by-play.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nhl-v3-projections": { - "display_name": "NHL v3 Projections", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nhl-v3-projections/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nhl-v3-projections.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nhl-v3-scores": { - "display_name": "NHL v3 Scores", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nhl-v3-scores/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nhl-v3-scores.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-nhl-v3-stats": { - "display_name": "NHL v3 Stats", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/nhl-v3-stats/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/nhl-v3-stats.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-soccer-v3-projections": { - "display_name": "Soccer v3 Projections", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/soccer-v3-projections/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/soccer-v3-projections.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-soccer-v3-scores": { - "display_name": "Soccer v3 Scores", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/soccer-v3-scores/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/soccer-v3-scores.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "sportsdata-io-soccer-v3-stats": { - "display_name": "Soccer v3 Stats", - "spec_url": "https://api.apis.guru/v2/specs/sportsdata.io/soccer-v3-stats/1.0/openapi.json", - "base_url": "https://fantasydata.com/downloads/swagger/soccer-v3-stats.json", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "spotify": { - "display_name": "Spotify Web API", - "description": "You can use Spotify's Web API to discover music and podcasts, manage your Spotify library, control audio playback, and much more. Browse our available", - "spec_url": "https://api.apis.guru/v2/specs/spotify.com/1.0.0/openapi.json", - "base_url": "https://developer.spotify.com/_data/documentation/web-api/reference/open-api-schema.yml", - "env_vars": ["SPOTIFY_CLIENT_ID", "SPOTIFY_CLIENT_SECRET"], - }, - "spotify-com-sonallux": { - "display_name": "Spotify Web API with fixes and improvements from sonallux", - "description": "You can use Spotify's Web API to discover music and podcasts, manage your Spotify library, control audio playback, and much more. Browse our available", - "spec_url": "https://api.apis.guru/v2/specs/spotify.com/sonallux/2023.2.27/openapi.json", - "base_url": "https://raw.githubusercontent.com/sonallux/spotify-web-api/main/fixed-spotify-open-api.yml", - "env_vars": ["SPOTIFY_CLIENT_ID", "SPOTIFY_CLIENT_SECRET"], - }, - "squareup": { - "display_name": "Square Connect API", - "description": "Client library for accessing the Square Connect APIs", - "spec_url": "https://api.apis.guru/v2/specs/squareup.com/2.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/square/connect-api-specification/master/api.json", - "env_vars": ["SQUARE_ACCESS_TOKEN"], - }, - "stackexchange": { - "display_name": "StackExchange", - "description": "Stack Exchange is a network of 130+ Q&A communities including Stack Overflow.", - "spec_url": "https://api.apis.guru/v2/specs/stackexchange.com/2.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/APIs-guru/unofficial_openapi_specs/master/stackexchange.com/2.0/openapi.yaml", - "env_vars": ["STACKEXCHANGE_API_KEY", "STACKEXCHANGE_TOKEN"], - }, - "staging-ecotaco": { - "display_name": "api.ecota.co v2", - "description": "The API ecotaco allows you to connect, create an account, manage your credit cards and order rides. # Authentication Ecotaco API use a system of appli", - "spec_url": "https://api.apis.guru/v2/specs/staging-ecotaco.com/1.0.0/openapi.json", - "base_url": "https://ecotaco.docs.apiary.io/api-description-document", - "env_vars": ["STAGING_ECOTACO_API_KEY", "STAGING_ECOTACO_TOKEN"], - }, - "statsocial": { - "display_name": "StatSocial Platform API", - "description": "API Reference:

          The StatSocial API is organized around REST. Our API is designed to have predictable, resource-oriented URLs and to use HTTP r", - "spec_url": "https://api.apis.guru/v2/specs/statsocial.com/1.0.0/openapi.json", - "base_url": "https://docs.statsocial.com/wp-content/themes/twentyfifteen/json/swagger.json", - "env_vars": ["STATSOCIAL_API_KEY", "STATSOCIAL_TOKEN"], - }, - "stellastra": { - "display_name": "Stellastra", - "description": "Stellastra makes it easy to get reviews for your cybersecurity solution in real-time with its platform-agnostic REST API.", - "spec_url": "https://api.apis.guru/v2/specs/stellastra.com/1.0/openapi.json", - "base_url": "https://stellastra.com/stellastra.json", - "env_vars": ["STELLASTRA_API_KEY", "STELLASTRA_TOKEN"], - }, - "stoplight": { - "display_name": "Stoplight", - "spec_url": "https://api.apis.guru/v2/specs/stoplight.io/api-v1/openapi.json", - "base_url": "https://api.stoplight.io/v1/versions/wDcMCTKXwn8X4ynL9/export/oas.json", - "env_vars": ["STOPLIGHT_API_KEY", "STOPLIGHT_TOKEN"], - }, - "storecove": { - "display_name": "Storecove API", - "description": "Storecove API", - "spec_url": "https://api.apis.guru/v2/specs/storecove.com/2.0.1/openapi.json", - "base_url": "https://www.storecove.com/api/v2/openapi.json", - "env_vars": ["STORECOVE_API_KEY", "STORECOVE_TOKEN"], - }, - "stormglass": { - "display_name": "Storm Glass Marine Weather", - "description": "Global marine weather data from multiple sources in one single API with hourly resolution. Get your API key by visiting the Storm Glass web site.", - "spec_url": "https://api.apis.guru/v2/specs/stormglass.io/1.0.1/swagger.json", - "base_url": "https://www.stormglass.io/specifications/api-specifications.yaml", - "env_vars": ["STORMGLASS_API_KEY", "STORMGLASS_TOKEN"], - }, - "stream-io-api": { - "display_name": "Stream Chat API", - "spec_url": "https://api.apis.guru/v2/specs/stream-io-api.com/v79.19.1/openapi.json", - "base_url": "https://stream-openapi.s3.us-east-1.amazonaws.com/chat/openapi-latest.yaml", - "env_vars": ["STREAM_IO_API_API_KEY", "STREAM_IO_API_TOKEN"], - }, - "stripe": { - "display_name": "Stripe API", - "description": "The Stripe REST API. Please see https://stripe.com/docs/api for more details.", - "spec_url": "https://api.apis.guru/v2/specs/stripe.com/2022-11-15/openapi.json", - "base_url": "https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.yaml", - "env_vars": ["STRIPE_API_KEY", "STRIPE_SECRET_KEY"], - "endpoints_count": 446, - }, - "superset-apache-local-superset": { - "display_name": "Superset", - "description": "Superset", - "spec_url": "https://api.apis.guru/v2/specs/superset.apache.local/superset/v1/openapi.json", - "base_url": "https://raw.githubusercontent.com/apache/superset/master/docs/src/resources/openapi.json", - "env_vars": [ - "SUPERSET_APACHE_LOCAL_SUPERSET_API_KEY", - "SUPERSET_APACHE_LOCAL_SUPERSET_TOKEN", - ], - }, - "surevoip-co-uk": { - "display_name": "The SureVoIP RESTful API", - "description": "# Introduction Welcome to the SureVoIP RESTful hypermedia API (sometimes known as a VoIP REST API, Telecom REST API, SIP API, Hypermedia API or just V", - "spec_url": "https://api.apis.guru/v2/specs/surevoip.co.uk/9dcb0dc8/openapi.json", - "base_url": "https://raw.githubusercontent.com/SureVoIP/API-Specification/master/openapi/openapi.yaml", - "env_vars": ["SUREVOIP_CO_UK_API_KEY", "SUREVOIP_CO_UK_TOKEN"], - }, - "surrey-ca-open511": { - "display_name": "City of Surrey Open511 API", - "description": "This API provides real time traffic obstruction events occuring within the City of Surrey.", - "spec_url": "https://api.apis.guru/v2/specs/surrey.ca/open511/0.1/swagger.json", - "base_url": "https://raw.githubusercontent.com/cityofsurrey/open511/master/open511definition.json", - "env_vars": ["SURREY_CA_OPEN511_API_KEY", "SURREY_CA_OPEN511_TOKEN"], - }, - "surrey-ca-trafficloops": { - "display_name": "City of Surrey Traffic Loop Count API.", - "description": "This API provides locations of City of Surrey traffic loops and the corresponding traffic loop counts in 15 minute intervals. While the counts are bro", - "spec_url": "https://api.apis.guru/v2/specs/surrey.ca/trafficloops/0.1/swagger.json", - "base_url": "https://raw.githubusercontent.com/cityofsurrey/traffic-loops-api/master/surrey-trafficloop.json", - "env_vars": ["SURREY_CA_TRAFFICLOOPS_API_KEY", "SURREY_CA_TRAFFICLOOPS_TOKEN"], - }, - "svix": { - "display_name": "Svix API", - "description": "Welcome to the Svix API documentation! Useful links: [Homepage](https://www.svix.com) | [Support email](mailto:support+docs@svix.com) | [Blog](https:/", - "spec_url": "https://api.apis.guru/v2/specs/svix.com/1.4/openapi.json", - "base_url": "https://api.svix.com/api/v1/openapi.json", - "env_vars": ["SVIX_API_KEY", "SVIX_TOKEN"], - }, - "swagger-io-generator": { - "display_name": "Swagger Generator", - "description": "This is an online swagger codegen server. You can find out more at https://github.com/swagger-api/swagger-codegen or on [irc.freenode.net, #swagger](h", - "spec_url": "https://api.apis.guru/v2/specs/swagger.io/generator/2.4.30/swagger.json", - "base_url": "https://generator.swagger.io/api/swagger.json", - "env_vars": ["SWAGGER_IO_GENERATOR_API_KEY", "SWAGGER_IO_GENERATOR_TOKEN"], - }, - "swaggerhub": { - "display_name": "SwaggerHub Registry API", - "description": "# Overview Use SwaggerHub Registry API to access, manage, and update the following resources in SwaggerHub, bypassing the web interface: * APIs * Doma", - "spec_url": "https://api.apis.guru/v2/specs/swaggerhub.com/1.0.66/swagger.json", - "base_url": "https://api.swaggerhub.com/apis/swagger-hub/registry-api/1.0.66", - "env_vars": ["SWAGGERHUB_API_KEY", "SWAGGERHUB_TOKEN"], - }, - "symanto": { - "display_name": "Psycholinguistic Text Analytics", - "description": "We aim to provide the deepest understanding of people through psychology & AI", - "spec_url": "https://api.apis.guru/v2/specs/symanto.net/1.0/openapi.json", - "base_url": "https://api.symanto.net/docs/v1/openapi.json", - "env_vars": ["SYMANTO_API_KEY", "SYMANTO_TOKEN"], - }, - "synq-fm": { - "display_name": "SYNQ Video", - "description": "* [Sign up for a developer API key!](https://www.synq.fm/register) * [SYNQ API Guide](/)", - "spec_url": "https://api.apis.guru/v2/specs/synq.fm/1.9.1/swagger.json", - "base_url": "https://docs.synq.fm/swagger/api.json", - "env_vars": ["SYNQ_FM_API_KEY", "SYNQ_FM_TOKEN"], - }, - "tafqit-herokuapp": { - "display_name": "Tafqit", - "description": "Convert numbers to their Arabic text representation", - "spec_url": "https://api.apis.guru/v2/specs/tafqit.herokuapp.com/v1/openapi.json", - "base_url": "https://tafqit.herokuapp.com/open_api/TafqitOpenAPI.json", - "env_vars": ["HEROKU_API_KEY"], - }, - "taggun": { - "display_name": "TAGGUN Receipt OCR Scanning API", - "description": "Expects only running software, real reactions, and beautifully crafted APIs to serve your every desire to transcribe a piece of paper to digital form.", - "spec_url": "https://api.apis.guru/v2/specs/taggun.io/1.10.9/swagger.json", - "base_url": "https://api.taggun.io/docs/swagger.json", - "env_vars": ["TAGGUN_API_KEY", "TAGGUN_TOKEN"], - }, - "taxamo": { - "display_name": "Taxamo", - "description": "Taxamoโ€™s elegant suite of APIs and comprehensive reporting dashboard enables digital merchants to easily comply with EU regulatory requirements on tax", - "spec_url": "https://api.apis.guru/v2/specs/taxamo.com/1/swagger.json", - "base_url": "https://api.taxamo.com/swagger", - "env_vars": ["TAXAMO_API_KEY", "TAXAMO_TOKEN"], - }, - "taxrates": { - "display_name": "Taxrates.io API", - "description": "

          Introduction

          Taxrates.io is a global tax rate service that automates the management of monitoring tax rates changes in 181 countries. We m", - "spec_url": "https://api.apis.guru/v2/specs/taxrates.io/1.0.0/openapi.json", - "base_url": "https://www.postman.com/collections/10601972-0bdabb72-f66a-4b75-bb29-e1c01dc81baa-TVev6RW8", - "env_vars": ["TAXRATES_API_KEY", "TAXRATES_TOKEN"], - }, - "tcgdex": { - "display_name": "TCGdex API", - "description": "A Multilanguage Pokรฉmon TCG Database with Cards Pictures and most of the informations contained on the cards. You can find out more about TCGdex at [h", - "spec_url": "https://api.apis.guru/v2/specs/tcgdex.net/2.0.0/openapi.json", - "base_url": "https://api.tcgdex.net/v2/openapi.yaml", - "env_vars": ["TCGDEX_API_KEY", "TCGDEX_TOKEN"], - }, - "telegram": { - "display_name": "Telegram Bot API", - "description": "Auto-generated OpenAPI schema", - "spec_url": "https://api.apis.guru/v2/specs/telegram.org/5.0.0/openapi.json", - "base_url": "https://josxa.stoplight.io/api/v1/projects/josxa/bot-api/nodes/openapi.json?branch=main", - "env_vars": ["TELEGRAM_API_KEY", "TELEGRAM_TOKEN"], - }, - "telematicssdk": { - "display_name": "Quick start - Telematics SDK", - "description": "# Introduction We have prepared a set of APIs for quick start to integrate telematics SDK that powers mobile telematics inside 3rd party mobile applic", - "spec_url": "https://api.apis.guru/v2/specs/telematicssdk.com/1.0.0/openapi.json", - "base_url": "https://www.getpostman.com/collections/94fc76d14f0398faf807", - "env_vars": ["TELEMATICSSDK_API_KEY", "TELEMATICSSDK_TOKEN"], - }, - "telnyx": { - "display_name": "Telnyx API", - "description": "SIP trunking, SMS, MMS, Call Control and Telephony Data Services.", - "spec_url": "https://api.apis.guru/v2/specs/telnyx.com/2.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/team-telnyx/openapi/master/openapi/spec3.yml", - "env_vars": ["TELNYX_API_KEY"], - }, - "testfire-net-altoroj": { - "display_name": "AltoroJ REST API", - "spec_url": "https://api.apis.guru/v2/specs/testfire.net/altoroj/1.0.2/swagger.json", - "base_url": "https://demo.testfire.net/swagger/properties.json", - "env_vars": ["TESTFIRE_NET_ALTOROJ_API_KEY", "TESTFIRE_NET_ALTOROJ_TOKEN"], - }, - "text2data": { - "display_name": "Text Analytics & Sentiment Analysis API | api.text2data.com", - "description": "

          The current api version is v3.4



          The api methods listed below can be called directly from this page to test the output. You mig", - "spec_url": "https://api.apis.guru/v2/specs/text2data.org/v3.4/swagger.json", - "base_url": "http://api.text2data.org/swagger/docs/v3.4", - "env_vars": ["TEXT2DATA_API_KEY", "TEXT2DATA_TOKEN"], - }, - "tfl-gov-uk": { - "display_name": "Transport for London Unified API", - "description": "Our unified API brings together data across all modes of transport into a single RESTful API. This API provides access to the most highly requested re", - "spec_url": "https://api.apis.guru/v2/specs/tfl.gov.uk/v1/openapi.json", - "base_url": "https://api.tfl.gov.uk/swagger/docs/v1", - "env_vars": ["TFL_GOV_UK_API_KEY", "TFL_GOV_UK_TOKEN"], - }, - "thebluealliance": { - "display_name": "The Blue Alliance API v3", - "description": "# Overview Information and statistics about FIRST Robotics Competition teams and events. # Authentication All endpoints require an Auth Key to be pass", - "spec_url": "https://api.apis.guru/v2/specs/thebluealliance.com/3.8.2/openapi.json", - "base_url": "https://www.thebluealliance.com/swagger/api_v3.json", - "env_vars": ["THEBLUEALLIANCE_API_KEY", "THEBLUEALLIANCE_TOKEN"], - }, - "thenounproject": { - "display_name": "The Noun Project", - "description": "Icons for Everything", - "spec_url": "https://api.apis.guru/v2/specs/thenounproject.com/1.0.0/swagger.json", - "base_url": "http://api.thenounproject.com/config/api-doc", - "env_vars": ["THENOUNPROJECT_API_KEY", "THENOUNPROJECT_TOKEN"], - }, - "thesmsworks-co-uk": { - "display_name": "The SMS Works API", - "description": "The SMS Works provides a low-cost, reliable SMS API for developers. Pay only for delivered texts, all failed messages are refunded.", - "spec_url": "https://api.apis.guru/v2/specs/thesmsworks.co.uk/1.8.0/swagger.json", - "base_url": "https://api.thesmsworks.co.uk/api/swagger/swagger.yaml", - "env_vars": ["THESMSWORKS_CO_UK_API_KEY", "THESMSWORKS_CO_UK_TOKEN"], - }, - "thetvdb": { - "display_name": "TheTVDB API v3", - "description": "API v3 targets v2 functionality with a few minor additions. The API is accessible via https://api.thetvdb.com and provides the following REST endpoint", - "spec_url": "https://api.apis.guru/v2/specs/thetvdb.com/3.0.0/swagger.json", - "base_url": "https://api.thetvdb.com/swagger.json", - "env_vars": ["THETVDB_API_KEY", "THETVDB_TOKEN"], - }, - "threatjammer": { - "display_name": "ThreatJammer.com User API", - "description": "The public API open to the users. [Read the docs and learn more.](https://threatjammer.com/docs). ## General information ### Description Threat Jammer", - "spec_url": "https://api.apis.guru/v2/specs/threatjammer.com/1.2.20/openapi.json", - "base_url": "https://dublin.api.threatjammer.com/openapi.json", - "env_vars": ["THREATJAMMER_API_KEY", "THREATJAMMER_TOKEN"], - }, - "ticketmaster-com-commerce": { - "display_name": "Commerce API", - "description": "Use the Ticketmaster Commerce API to look up available offers and products on various Ticketmaster platforms for North America markets. For formal par", - "spec_url": "https://api.apis.guru/v2/specs/ticketmaster.com/commerce/v2/swagger.json", - "base_url": "https://raw.githubusercontent.com/ticketmaster-api/ticketmaster-api.github.io/dev/_data/orgs/commerce-api/v2/api.json", - "env_vars": [ - "TICKETMASTER_COM_COMMERCE_API_KEY", - "TICKETMASTER_COM_COMMERCE_TOKEN", - ], - }, - "ticketmaster-com-discovery": { - "display_name": "Discovery API", - "description": "The Ticketmaster Discovery API allows you to search for events, attractions, or venues.", - "spec_url": "https://api.apis.guru/v2/specs/ticketmaster.com/discovery/v2/openapi.json", - "base_url": "https://raw.githubusercontent.com/ticketmaster-api/ticketmaster-api.github.io/dev/_data/orgs/discovery-api/v2/api.json", - "env_vars": [ - "TICKETMASTER_COM_DISCOVERY_API_KEY", - "TICKETMASTER_COM_DISCOVERY_TOKEN", - ], - }, - "ticketmaster-com-publish": { - "display_name": "ticketmaster publish api", - "description": "Publish API", - "spec_url": "https://api.apis.guru/v2/specs/ticketmaster.com/publish/v2/openapi.json", - "base_url": "https://raw.githubusercontent.com/ticketmaster-api/ticketmaster-api.github.io/dev/_data/orgs/publish-api/v1/api.json", - "env_vars": [ - "TICKETMASTER_COM_PUBLISH_API_KEY", - "TICKETMASTER_COM_PUBLISH_TOKEN", - ], - }, - "tinyuid": { - "display_name": "TinyUID.com", - "description": "Paste a Long URL link to shorten it", - "spec_url": "https://api.apis.guru/v2/specs/tinyuid.com/1.0.0/swagger.json", - "base_url": "https://tinyuid.com/tinyuid-swagger.yaml", - "env_vars": ["TINYUID_API_KEY", "TINYUID_TOKEN"], - }, - "tisane-ai": { - "display_name": "Tisane API Documentation", - "description": "Tisane is a natural language processing library, providing: * standard NLP functionality * special functions for detection of problematic or abusive c", - "spec_url": "https://api.apis.guru/v2/specs/tisane.ai/1.0.0/openapi.json", - "base_url": "https://www.postman.com/collections/15057172-1e147114-3936-4a52-a21e-681e72b90b53-TzeUnUQU", - "env_vars": ["TISANE_AI_API_KEY", "TISANE_AI_TOKEN"], - }, - "tl-api-azurewebsites": { - "display_name": "API", - "description": "Web API for TL mobile and web app", - "spec_url": "https://api.apis.guru/v2/specs/tl-api.azurewebsites.net/2020-08-10_6-22/openapi.json", - "base_url": "https://tl-api.azurewebsites.net/swagger/default/swagger.json", - "env_vars": ["TL_API_AZUREWEBSITES_API_KEY", "TL_API_AZUREWEBSITES_TOKEN"], - }, - "tokenjay-app": { - "display_name": "TokenJay API services", - "description": "Please see usage policies on tokenjay.app", - "spec_url": "https://api.apis.guru/v2/specs/tokenjay.app/1.0.0/openapi.json", - "base_url": "https://api.tokenjay.app/api-docs", - "env_vars": ["TOKENJAY_APP_API_KEY", "TOKENJAY_APP_TOKEN"], - }, - "tokenmetrics": { - "display_name": "Endpoints", - "spec_url": "https://api.apis.guru/v2/specs/tokenmetrics.com/1.0.0/openapi.json", - "base_url": "https://www.postman.com/collections/25954042-3a7b5f30-f598-4f9e-ba5f-4ebb58a1f18d", - "env_vars": ["TOKENMETRICS_API_KEY", "TOKENMETRICS_TOKEN"], - }, - "tomtom-com-maps": { - "display_name": "Maps", - "description": "The Maps API web services suite offers the following APIs: - Raster The Maps Raster API renders map data that is divided into gridded sections called ", - "spec_url": "https://api.apis.guru/v2/specs/tomtom.com/maps/1.0.0/openapi.json", - "base_url": "https://developer.tomtom.com/system/files/swagger_models/maps_api_0.yaml", - "env_vars": ["TOMTOM_API_KEY"], - }, - "tomtom-com-routing": { - "display_name": "Routing", - "description": "Routing consists of the following service: Calculate Route Calculates a route between an origin and a destination, passing through waypoints (i", - "spec_url": "https://api.apis.guru/v2/specs/tomtom.com/routing/1.0.0/openapi.json", - "base_url": "https://developer.tomtom.com/system/files/swagger_models/routing_api.yaml", - "env_vars": ["TOMTOM_API_KEY"], - }, - "tomtom-com-search": { - "display_name": "Search", - "description": "Search API is a RESTful API that allows developers to run a single line fuzzy search for addresses and POIs. Search API returns the latitude/longitude", - "spec_url": "https://api.apis.guru/v2/specs/tomtom.com/search/1.0.0/openapi.json", - "base_url": "https://developer.tomtom.com/system/files/swagger_models/search_api.yaml", - "env_vars": ["TOMTOM_API_KEY"], - }, - "traccar": { - "display_name": "Traccar", - "description": "Open Source GPS Tracking Platform", - "spec_url": "https://api.apis.guru/v2/specs/traccar.org/5.6/openapi.json", - "base_url": "https://raw.githubusercontent.com/tananaev/traccar/master/swagger.json", - "env_vars": ["TRACCAR_API_KEY", "TRACCAR_TOKEN"], - }, - "tradematic": { - "display_name": "Tradematic Cloud API", - "description": "### Overview Tradematic Cloud is a trading infrastructure for building investment services. Itโ€™s a trading engine + API + ready-made adapters to stock", - "spec_url": "https://api.apis.guru/v2/specs/tradematic.com/1.0.2/swagger.json", - "base_url": "https://tradematic.cloud/sdk/swagger.yaml", - "env_vars": ["TRADEMATIC_API_KEY", "TRADEMATIC_TOKEN"], - }, - "trakt-tv": { - "display_name": "Trakt API", - "description": "At Trakt, we collect lots of interesting information about what tv shows and movies everyone is watching. Part of the fun with such data is making it ", - "spec_url": "https://api.apis.guru/v2/specs/trakt.tv/1.0.0/openapi.json", - "base_url": "https://trakt.docs.apiary.io/api-description-document", - "env_vars": ["TRAKT_TV_API_KEY", "TRAKT_TV_TOKEN"], - }, - "transavia": { - "display_name": "Airports API v2", - "description": "Returns all airports", - "spec_url": "https://api.apis.guru/v2/specs/transavia.com/1.0/swagger.json", - "base_url": "https://developer.transavia.com/docs/services/58d8bca5a9e6241bac7e89d8/export?DocumentFormat=Swagger", - "env_vars": ["TRANSAVIA_API_KEY", "TRANSAVIA_TOKEN"], - }, - "transitfeeds": { - "display_name": "TransitFeeds API", - "description": "API to view feed information and download feeds from TransitFeeds.com", - "spec_url": "https://api.apis.guru/v2/specs/transitfeeds.com/1.0.0/swagger.json", - "base_url": "https://transitfeeds.com/api/transitfeeds-api.yaml", - "env_vars": ["TRANSITFEEDS_API_KEY", "TRANSITFEEDS_TOKEN"], - }, - "trapstreet": { - "display_name": "TrapStreet API", - "description": "The TrapStreet API finds trap streets in Google Maps, Bing Maps and OpenStreetMap data.", - "spec_url": "https://api.apis.guru/v2/specs/trapstreet.com/1.0.0/openapi.json", - "base_url": "https://gist.githubusercontent.com/MikeRalphson/e455f437f87a149ae0501d8cefe2ecab/raw/8f3608b72b0ab6eb6b0cea1c54ff9d84df7de05d/openapi.yaml", - "env_vars": ["TRAPSTREET_API_KEY", "TRAPSTREET_TOKEN"], - }, - "trashnothing": { - "display_name": "trash nothing", - "description": "This is the REST API for [trashnothing.com](https://trashnothing.com). To learn more about the API or to register your app for use with the API visit ", - "spec_url": "https://api.apis.guru/v2/specs/trashnothing.com/1.3/openapi.json", - "base_url": "http://trashnothing.com/api/trashnothing-openapi.yaml", - "env_vars": ["TRASHNOTHING_API_KEY", "TRASHNOTHING_TOKEN"], - }, - "trello": { - "display_name": "Trello", - "description": "This document describes the REST API of Trello as published by Trello.com. - Official Doc", - "spec_url": "https://api.apis.guru/v2/specs/trello.com/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/APIs-guru/unofficial_openapi_specs/master/trello.com/1.0/swagger.yaml", - "env_vars": ["TRELLO_API_KEY", "TRELLO_TOKEN"], - }, - "truanon": { - "display_name": "TruAnon Private API", - "description": "Welcome to TruAnon! Thank you for helping make the Internet a safer place to be. Adopting TruAnon is simple. There is no setup or dependencies, nothin", - "spec_url": "https://api.apis.guru/v2/specs/truanon.com/1.0.0/openapi.json", - "base_url": "https://www.postman.com/collections/097655c06fff1bf6a966", - "env_vars": ["TRUANON_API_KEY", "TRUANON_TOKEN"], - }, - "truesight": { - "display_name": "Hardware Sentry TrueSight Presentation Server REST API", - "description": "Hardware Sentry TrueSight Presentation Server REST API", - "spec_url": "https://api.apis.guru/v2/specs/truesight.local/11.1.00/openapi.json", - "base_url": "https://www.sentrysoftware.com/library/mshw/11.1.00/hardware-tsps-openapi.yaml", - "env_vars": ["TRUESIGHT_API_KEY", "TRUESIGHT_TOKEN"], - }, - "truora": { - "display_name": "Checks API", - "description": "**NOTE:** This is a preview of the API and it is not considered stable since refinements are still being made. # Introduction Welcome to the **Truora ", - "spec_url": "https://api.apis.guru/v2/specs/truora.com/1.0.0/openapi.json", - "base_url": "https://docs.truora.com/openapi.json", - "env_vars": ["TRUORA_API_KEY", "TRUORA_TOKEN"], - }, - "tsapi": { - "display_name": "TSAPI", - "spec_url": "https://api.apis.guru/v2/specs/tsapi.net/v1/openapi.json", - "base_url": "https://api.tsapi.net/swagger/v1/swagger.json", - "env_vars": ["TSAPI_API_KEY", "TSAPI_TOKEN"], - }, - "turbinelabs": { - "display_name": "Turbine Labs API", - "description": "The Turbine Labs API provides CRUD operations for core object types, and is mostly RESTy. The easiest way to interact with the API is with [tbnctl](ht", - "spec_url": "https://api.apis.guru/v2/specs/turbinelabs.io/1.0/swagger.json", - "base_url": "https://raw.githubusercontent.com/turbinelabs/api/master/swagger.yml", - "env_vars": ["TURBINELABS_API_KEY", "TURBINELABS_TOKEN"], - }, - "tvmaze": { - "display_name": "TVmaze user API", - "description": "Access to the user API is only possible for users with a [premium](http://www.tvmaze.com/premium) account. A user can only access their own user data.", - "spec_url": "https://api.apis.guru/v2/specs/tvmaze.com/1.0/openapi.json", - "base_url": "http://static.tvmaze.com/apidoc/v1.yaml", - "env_vars": ["TVMAZE_API_KEY", "TVMAZE_TOKEN"], - }, - "twilio-com-api": { - "display_name": "Twilio - Api", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/api/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_api_v2010.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilioaccountsv1": { - "display_name": "Twilio - Accounts", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_accounts_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_accounts_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilioautopilotv1": { - "display_name": "Twilio - Autopilot", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_autopilot_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_autopilot_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliobulkexportsv1": { - "display_name": "Twilio - Bulkexports", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_bulkexports_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_bulkexports_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliochatv1": { - "display_name": "Twilio - Chat", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_chat_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_chat_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliochatv2": { - "display_name": "Twilio - Chat", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_chat_v2/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_chat_v2.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliochatv3": { - "display_name": "Twilio - Chat", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_chat_v3/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_chat_v3.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliocontentv1": { - "display_name": "Twilio - Content", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_content_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_content_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilioconversationsv1": { - "display_name": "Twilio - Conversations", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_conversations_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_conversations_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilioeventsv1": { - "display_name": "Twilio - Events", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_events_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_events_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliofaxv1": { - "display_name": "Twilio - Fax", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_fax_v1/1.29.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_fax_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilioflexv1": { - "display_name": "Twilio - Flex", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_flex_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_flex_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilioflexv2": { - "display_name": "Twilio - Flex", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_flex_v2/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_flex_v2.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliofrontlinev1": { - "display_name": "Twilio - Frontline", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_frontline_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_frontline_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilioinsightsv1": { - "display_name": "Twilio - Insights", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_insights_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_insights_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilioipmessagingv1": { - "display_name": "Twilio - Ip_messaging", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_ip_messaging_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_ip_messaging_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilioipmessagingv2": { - "display_name": "Twilio - Ip_messaging", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_ip_messaging_v2/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_ip_messaging_v2.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliolookupsv1": { - "display_name": "Twilio - Lookups", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_lookups_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_lookups_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliolookupsv2": { - "display_name": "Twilio - Lookups", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_lookups_v2/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_lookups_v2.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliomediav1": { - "display_name": "Twilio - Media", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_media_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_media_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliomessagingv1": { - "display_name": "Twilio - Messaging", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_messaging_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_messaging_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliomicrovisorv1": { - "display_name": "Twilio - Microvisor", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_microvisor_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_microvisor_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliomonitorv1": { - "display_name": "Twilio - Monitor", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_monitor_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_monitor_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilionotifyv1": { - "display_name": "Twilio - Notify", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_notify_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_notify_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilionumbersv1": { - "display_name": "Twilio - Numbers", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_numbers_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_numbers_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilionumbersv2": { - "display_name": "Twilio - Numbers", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_numbers_v2/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_numbers_v2.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliooauthv1": { - "display_name": "Twilio - Oauth", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_oauth_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_oauth_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliopreview": { - "display_name": "Twilio - Preview", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_preview/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_preview.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliopricingv1": { - "display_name": "Twilio - Pricing", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_pricing_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_pricing_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliopricingv2": { - "display_name": "Twilio - Pricing", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_pricing_v2/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_pricing_v2.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilioproxyv1": { - "display_name": "Twilio - Proxy", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_proxy_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_proxy_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilioroutesv2": { - "display_name": "Twilio - Routes", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_routes_v2/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_routes_v2.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilioserverlessv1": { - "display_name": "Twilio - Serverless", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_serverless_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_serverless_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliostudiov1": { - "display_name": "Twilio - Studio", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_studio_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_studio_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliostudiov2": { - "display_name": "Twilio - Studio", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_studio_v2/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_studio_v2.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliosupersimv1": { - "display_name": "Twilio - Supersim", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_supersim_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_supersim_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliosyncv1": { - "display_name": "Twilio - Sync", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_sync_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_sync_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliotaskrouterv1": { - "display_name": "Twilio - Taskrouter", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_taskrouter_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_taskrouter_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliotrunkingv1": { - "display_name": "Twilio - Trunking", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_trunking_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_trunking_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliotrusthubv1": { - "display_name": "Twilio - Trusthub", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_trusthub_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_trusthub_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twilioverifyv2": { - "display_name": "Twilio - Verify", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_verify_v2/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_verify_v2.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliovideov1": { - "display_name": "Twilio - Video", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_video_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_video_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliovoicev1": { - "display_name": "Twilio - Voice", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_voice_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_voice_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twilio-com-twiliowirelessv1": { - "display_name": "Twilio - Wireless", - "description": "This is the public Twilio REST API.", - "spec_url": "https://api.apis.guru/v2/specs/twilio.com/twilio_wireless_v1/1.42.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_wireless_v1.json", - "env_vars": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - }, - "twinehealth": { - "display_name": "Fitbit Plus API", - "description": "# Overview The Fitbit Plus API is a RESTful API. The requests and responses are formated according to the [JSON API](http://jsonapi.org/format/1.0/) s", - "spec_url": "https://api.apis.guru/v2/specs/twinehealth.com/v7.78.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/TwineHealth/TwineDeveloperDocs/master/spec/swagger.yaml", - "env_vars": ["TWINEHEALTH_API_KEY", "TWINEHEALTH_TOKEN"], - }, - "twitter-com-current": { - "display_name": "Twitter API v2", - "description": "Twitter API v2 available endpoints", - "spec_url": "https://api.apis.guru/v2/specs/twitter.com/current/2.61/openapi.json", - "base_url": "https://api.twitter.com/2/openapi.json", - "env_vars": ["TWITTER_API_KEY", "TWITTER_BEARER_TOKEN"], - }, - "twitter-com-legacy": { - "display_name": "Twitter API", - "spec_url": "https://api.apis.guru/v2/specs/twitter.com/legacy/1.1/swagger.json", - "base_url": "http://api.apigee.com/v1/consoles/twitter/apidescription?format=wadl", - "env_vars": ["TWITTER_API_KEY", "TWITTER_BEARER_TOKEN"], - }, - "tyk": { - "display_name": "Gateway REST API", - "spec_url": "https://api.apis.guru/v2/specs/tyk.com/1.9/swagger.json", - "base_url": "https://raw.githubusercontent.com/TykTechnologies/tyk-swagger-definitions/master/tyk_gateway_api.yml", - "env_vars": ["TYK_API_KEY", "TYK_TOKEN"], - }, - "uebermaps": { - "display_name": "uebermaps API endpoints", - "description": "Enable people to store spots on public and private maps", - "spec_url": "https://api.apis.guru/v2/specs/uebermaps.com/2.0/swagger.json", - "base_url": "https://uebermaps.com/api/v2/apidocs", - "env_vars": ["UEBERMAPS_API_KEY", "UEBERMAPS_TOKEN"], - }, - "unicourt": { - "display_name": "UniCourt Enterprise APIs", - "description": '', - "spec_url": "https://api.apis.guru/v2/specs/unicourt.com/1.0.0/openapi.json", - "base_url": "https://docs.unicourt.com/enterpriseapi/download/UniCourt-Enterprise-API-Spec.yaml", - "env_vars": ["UNICOURT_API_KEY", "UNICOURT_TOKEN"], - }, - "up-com-au": { - "display_name": "Up API", - "description": "The Up API gives you programmatic access to your balances and transaction data. You can request past transactions or set up webhooks to receive real-t", - "spec_url": "https://api.apis.guru/v2/specs/up.com.au/v1/openapi.json", - "base_url": "https://raw.githubusercontent.com/up-banking/api/master/v1/openapi.json", - "env_vars": ["UP_COM_AU_API_KEY", "UP_COM_AU_TOKEN"], - }, - "urlbox": { - "display_name": "Urlbox API", - "description": "A plugin that allows the user to capture screenshots of a web page from a URL or HTML using ChatGPT.", - "spec_url": "https://api.apis.guru/v2/specs/urlbox.io/v1/openapi.json", - "base_url": "https://www.urlbox.io/.well-known/open-api.yaml", - "env_vars": ["BOX_ACCESS_TOKEN"], - }, - "uscann": { - "display_name": "Api Documentation", - "description": "Api Documentation", - "spec_url": "https://api.apis.guru/v2/specs/uscann.net/1.0/swagger.json", - "base_url": "https://apibeta.uscann.net/apiv1/v2/api-docs?group=authentication-api", - "env_vars": ["USCANN_API_KEY", "USCANN_TOKEN"], - }, - "uspto-gov-bdss": { - "display_name": "Bulk Data Storage System Services", - "description": "Bulk Data Storage System (BDSS) allows the public to discover, search, and download patent and trademark data in bulk form.", - "spec_url": "https://api.apis.guru/v2/specs/uspto.gov/bdss/1.0.0/swagger.json", - "base_url": "https://bulkdata.uspto.gov/BDSS-API/swagger", - "env_vars": ["USPTO_GOV_BDSS_API_KEY", "USPTO_GOV_BDSS_TOKEN"], - }, - "va-gov-benefits": { - "display_name": "Benefits Intake", - "description": "The Benefits Intake API allows authorized third-party systems used by Veteran Service Organizations (VSOs), agencies, and Veterans to digitally submit", - "spec_url": "https://api.apis.guru/v2/specs/va.gov/benefits/1.0.0/openapi.json", - "base_url": "https://api.va.gov/services/vba_documents/docs/v1/api", - "env_vars": ["VA_GOV_BENEFITS_API_KEY", "VA_GOV_BENEFITS_TOKEN"], - }, - "va-gov-confirmation": { - "display_name": "Veteran Confirmation", - "description": "The Veteran Confirmation API allows you to confirm Veteran status for a given person. This can be useful for offering Veterans discounts or other bene", - "spec_url": "https://api.apis.guru/v2/specs/va.gov/confirmation/0.0.1/openapi.json", - "base_url": "https://api.va.gov/services/veteran_confirmation/docs/v0/api", - "env_vars": ["VA_GOV_CONFIRMATION_API_KEY", "VA_GOV_CONFIRMATION_TOKEN"], - }, - "va-gov-facilities": { - "display_name": "VA Facilities", - "description": "## Background This RESTful API provides information about physical VA facilities. Information available includes geographic location, address, phone, ", - "spec_url": "https://api.apis.guru/v2/specs/va.gov/facilities/0.0.1/openapi.json", - "base_url": "https://api.va.gov/services/va_facilities/docs/v0/api", - "env_vars": ["VA_GOV_FACILITIES_API_KEY", "VA_GOV_FACILITIES_TOKEN"], - }, - "va-gov-forms": { - "display_name": "VA Forms", - "description": "Use the VA Forms API to search for VA forms, get the form's PDF link and metadata, and check for new versions. Visit our VA Lighthouse [Contact Us pag", - "spec_url": "https://api.apis.guru/v2/specs/va.gov/forms/0.0.0/openapi.json", - "base_url": "https://api.va.gov/services/va_forms/docs/v0/api", - "env_vars": ["VA_GOV_FORMS_API_KEY", "VA_GOV_FORMS_TOKEN"], - }, - "vatapi": { - "display_name": "VAT API", - "description": "A developer friendly API to help your business achieve VAT compliance", - "spec_url": "https://api.apis.guru/v2/specs/vatapi.com/1/swagger.json", - "base_url": "https://vatapi.com/api-docs.json", - "env_vars": ["VATAPI_API_KEY", "VATAPI_TOKEN"], - }, - "vectara": { - "display_name": "Vectara REST API", - "description": "Vectara is a neural search platform, built for developers to get the most out of their data. You can sign up for an account at [https://vectara.com](h", - "spec_url": "https://api.apis.guru/v2/specs/vectara.io/1.0.0/openapi.json", - "base_url": "https://docs.vectara.com/vectara-oas.yaml", - "env_vars": ["VECTARA_API_KEY", "VECTARA_TOKEN"], - }, - "velopayments": { - "display_name": "Velo Payments APIs", - "description": "## Terms and Definitions Throughout this document and the Velo platform the following terms are used: * **Payor.** An entity (typically a corporation)", - "spec_url": "https://api.apis.guru/v2/specs/velopayments.com/2.34.63/openapi.json", - "base_url": "https://raw.githubusercontent.com/velopaymentsapi/VeloOpenApi/master/spec/openapi.yaml", - "env_vars": ["VELOPAYMENTS_API_KEY", "VELOPAYMENTS_TOKEN"], - }, - "vercel": { - "display_name": "Vercel API", - "description": "Vercel combines the best developer experience with an obsessive focus on end-user performance. Our platform enables frontend teams to do their best wo", - "spec_url": "https://api.apis.guru/v2/specs/vercel.com/0.0.1/openapi.json", - "base_url": "https://openapi.vercel.sh", - "env_vars": ["VERCEL_TOKEN"], - }, - "versioneye": { - "display_name": "API V1", - "description": "VersionEye is a cross-platform search engine for free/libre/open source software libraries.", - "spec_url": "https://api.apis.guru/v2/specs/versioneye.com/v1/openapi.json", - "base_url": "https://www.versioneye.com/api-docs/v1/swagger.yaml", - "env_vars": ["VERSIONEYE_API_KEY", "VERSIONEYE_TOKEN"], - }, - "vestorly": { - "display_name": "Vestorly API", - "description": "Vestorly Developers API", - "spec_url": "https://api.apis.guru/v2/specs/vestorly.com/1.0.0/swagger.json", - "base_url": "http://developers.vestorly.com/v2/swagger.json", - "env_vars": ["VESTORLY_API_KEY", "VESTORLY_TOKEN"], - }, - "viator": { - "display_name": "Viator API Documentation & Specification โ€“ Merchant Part", - "description": "<style type='text/css'> code { white-space: nowrap; } a { font-weight: bold; } figure { width: 100%; text-align: center; font-style: italic; fon", - "spec_url": "https://api.apis.guru/v2/specs/viator.com/1.0.0/openapi.json", - "base_url": "blob:https://docs.viator.com/blobId", - "env_vars": ["VIATOR_API_KEY", "VIATOR_TOKEN"], - }, - "victorops": { - "display_name": "VictorOps", - "description": "This API allows you to interact with the VictorOps platform in various ways. Your account may be limited to a total number of API calls per month. Als", - "spec_url": "https://api.apis.guru/v2/specs/victorops.com/0.0.3/swagger.json", - "base_url": "https://portal.victorops.com/public/api-docs/victorops-api-v1.yaml", - "env_vars": ["VICTOROPS_API_KEY", "VICTOROPS_TOKEN"], - }, - "vimeo": { - "display_name": "Vimeo", - "spec_url": "https://api.apis.guru/v2/specs/vimeo.com/3.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/vimeo/openapi/master/api.yaml", - "env_vars": ["VIMEO_API_KEY", "VIMEO_TOKEN"], - }, - "visagecloud": { - "display_name": "VisageCloud", - "description": "Face search, recognition & classification API. Just make a call to our REST API each time your app needs to access face recognition and classification", - "spec_url": "https://api.apis.guru/v2/specs/visagecloud.com/1.1/swagger.json", - "base_url": "https://visagecloud.com/v2/api-docs", - "env_vars": ["VISAGECLOUD_API_KEY", "VISAGECLOUD_TOKEN"], - }, - "visiblethread": { - "display_name": "VisibleThread API", - "description": "## Introduction The VisibleThread b API provides services for analyzing/searching documents and web pages. To use the service you need an API key. **C", - "spec_url": "https://api.apis.guru/v2/specs/visiblethread.com/1.0/swagger.json", - "base_url": "https://api.visiblethread.com/example/vt.yaml", - "env_vars": ["VISIBLETHREAD_API_KEY", "VISIBLETHREAD_TOKEN"], - }, - "visualcrossing-com-weather": { - "display_name": "Visual Crossing Weather API", - "description": "Weather Forecast and Historical Weather Data via RESTful API.", - "spec_url": "https://api.apis.guru/v2/specs/visualcrossing.com/weather/4.6/openapi.json", - "base_url": "https://www.visualcrossing.com/weather/specs/visualcrossing-weather-api-openapi.json", - "env_vars": [ - "VISUALCROSSING_COM_WEATHER_API_KEY", - "VISUALCROSSING_COM_WEATHER_TOKEN", - ], - }, - "visualstudio": { - "display_name": "VSOnline", - "description": "Public APIs for managing VS Codespaces", - "spec_url": "https://api.apis.guru/v2/specs/visualstudio.com/v1/openapi.json", - "base_url": "https://online.visualstudio.com/api/v1/swagger", - "env_vars": ["VISUALSTUDIO_API_KEY", "VISUALSTUDIO_TOKEN"], - }, - "vmware-local-vrni": { - "display_name": "vRealize Network Insight API Reference", - "description": "vRealize Network Insight API Reference", - "spec_url": "https://api.apis.guru/v2/specs/vmware.local/vrni/1.0.0/openapi.json", - "base_url": "https://vdc-download.vmware.com/vmwb-repository/dcr-public/c1b5a60c-3635-4b8c-84b2-3ea54172cf31/f8595072-cedd-4f97-9b05-1720e0f41f92/vrni_api_spec.json", - "env_vars": ["VMWARE_LOCAL_VRNI_API_KEY", "VMWARE_LOCAL_VRNI_TOKEN"], - }, - "vocadb": { - "display_name": "VocaDbWeb", - "spec_url": "https://api.apis.guru/v2/specs/vocadb.net/1.0/openapi.json", - "base_url": "https://vocadb.net/swagger/v1/swagger.json", - "env_vars": ["VOCADB_API_KEY", "VOCADB_TOKEN"], - }, - "vonage-com-account": { - "display_name": "Account API", - "description": "The Vonage Business Cloud Account API enables you to retrieve information about accounts. Your application must subscribe to the Provisioning API suit", - "spec_url": "https://api.apis.guru/v2/specs/vonage.com/account/1.11.8/openapi.json", - "base_url": "https://raw.githubusercontent.com/nexmo/api-specification/master/definitions/vonage-business-cloud/account.yml", - "env_vars": ["VONAGE_API_KEY", "NEXMO_API_KEY"], - }, - "vonage-com-extension": { - "display_name": "Extension API", - "description": "The Vonage Business Cloud Extension API enables you to retrieve information about extensions. Your application must subscribe to the Provisioning API ", - "spec_url": "https://api.apis.guru/v2/specs/vonage.com/extension/1.11.8/openapi.json", - "base_url": "https://raw.githubusercontent.com/nexmo/api-specification/master/definitions/vonage-business-cloud/extension.yml", - "env_vars": ["VONAGE_API_KEY", "NEXMO_API_KEY"], - }, - "vonage-com-reports": { - "display_name": "Reports API", - "description": "The Vonage Business Cloud Reports API enables you to retrieve call logs for your account. Your application must subscribe to the Reports API suite to ", - "spec_url": "https://api.apis.guru/v2/specs/vonage.com/reports/1.0.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/nexmo/api-specification/master/definitions/vonage-business-cloud/reports.yml", - "env_vars": ["VONAGE_API_KEY", "NEXMO_API_KEY"], - }, - "vonage-com-user": { - "display_name": "User API", - "description": "The Vonage Business Cloud User API enables you to retrieve information about users. Your application must subscribe to the Provisioning API suite to u", - "spec_url": "https://api.apis.guru/v2/specs/vonage.com/user/1.11.8/openapi.json", - "base_url": "https://raw.githubusercontent.com/nexmo/api-specification/master/definitions/vonage-business-cloud/user.yml", - "env_vars": ["VONAGE_API_KEY", "NEXMO_API_KEY"], - }, - "vonage-com-vgis": { - "display_name": "Vonage Integration Suite", - "description": "The Vonage Integration Suite API enables call control and webhooks for call events. Your application must subscribe to the VonageIntegrationSuite API ", - "spec_url": "https://api.apis.guru/v2/specs/vonage.com/vgis/1.0.1/openapi.json", - "base_url": "https://raw.githubusercontent.com/nexmo/api-specification/master/definitions/vonage-business-cloud/vgis.yml", - "env_vars": ["VONAGE_API_KEY", "NEXMO_API_KEY"], - }, - "voodoomfg": { - "display_name": "Voodoo Manufacturing 3D Print API", - "description": "Welcome to the Voodoo Manufacturing API docs! Your Voodoo Manufacturing API key must be included with each request to the API. The API will look for t", - "spec_url": "https://api.apis.guru/v2/specs/voodoomfg.com/2.0.0/swagger.json", - "base_url": "https://api.voodoomfg.com/voodoo.yaml", - "env_vars": ["VOODOOMFG_API_KEY", "VOODOOMFG_TOKEN"], - }, - "vtex-local-catalog-api": { - "display_name": "Catalog API", - "description": "> Check the new [Catalog onboarding guide](https://developers.vtex.com/docs/guides/catalog-overview). We created this guide to improve the onboarding ", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Catalog-API/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Catalog API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-catalog-api-seller-portal": { - "display_name": "Catalog API - Seller Portal", - "description": "With the Catalog API for Seller Portal, you will be able to create, edit and consult products and their variations, brands, and categories. > This API", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Catalog-API-Seller-Portal/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Catalog API Seller Portal.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-checkout-api": { - "display_name": "Checkout API", - "description": ">โ„น๏ธ Check the new [Checkout onboarding guide](https://developers.vtex.com/vtex-rest-api/docs/checkout-overview). We created this guide to improve the ", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Checkout-API/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Checkout API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-customer-credit-api": { - "display_name": "Customer Credit API", - "description": "With Customer Credit your store can enable **credit payments** through the checkout. You can also control **invoices** and the **credit limits** of yo", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Customer-Credit-API/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Customer Credit API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-giftcard-api": { - "display_name": "GiftCard API", - "description": ">โ„น๏ธ Onboarding guide > > Check the new [Payments onboarding guide](https://developers.vtex.com/docs/guides/payments-overview). We created this guide t", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Giftcard-API/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Giftcard API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-giftcard-hub-api": { - "display_name": "GiftCard Hub API", - "description": ">โ„น๏ธ Check the new [Payments onboarding guide](https://developers.vtex.com/vtex-rest-api/docs/payments-overview). We created this guide to improve the ", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/GiftCard-Hub-API/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - GiftCard Hub API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-headless-cms-api": { - "display_name": "VTEX Headless CMS", - "description": "The VTEX Headless CMS is a no-code management system for storefront content. That means you can store your content as structured data in a layer decou", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Headless-CMS-API/0.31.2/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Headless CMS API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-intelligent-search-api": { - "display_name": "Intelligent Search API", - "description": ">โ„น๏ธ Onboarding guide > > Check the new [Search onboarding guide](https://developers.vtex.com/docs/guides/search-overview). We created this guide to im", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Intelligent-Search-API/0.1.12/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Intelligent Search API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-license-manager-api": { - "display_name": "License Manager API", - "description": "## Welcome! The License Manager API allows you to create users, modify their names and emails, as well as add and remove roles from users. ### ATTRIBU", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/License-Manager-API/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - License Manager API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-logistics-api": { - "display_name": "Logistics API", - "description": ">Check the [Fulfillment onboarding guide](https://developers.vtex.com/docs/guides/fulfillment). We created this guide to improve the onboarding experi", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Logistics-API/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Logistics API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-marketplace-apis": { - "display_name": "Marketplace API", - "description": "The **Marketplace API** enables marketplaces and sellers hosted on VTEX to perform their collaborative operations. >โš ๏ธ The marketplace must [create an", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Marketplace-APIs/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Marketplace APIs.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-marketplace-protocol": { - "display_name": "Marketplace Protocol", - "description": "The _Marketplace Protocol_ is a set of API requests and definitions to help you integrate external sellers into a VTEX marketplace as well as external", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Marketplace-Protocol/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Marketplace Protocol.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-master-data-api": { - "display_name": "Master Data API - v2", - "description": "# ATTENTION: **This version isn't compliant with data entities of old version (e.g. CL and AD). It's possible to use this configuration only to new da", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Master-Data-API-/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Master Data API - v2.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-masterdata-api": { - "display_name": "MasterData API - v1", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/MasterData-API-/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - MasterData API - v10.2.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-message-center-api": { - "display_name": "Message Center API", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Message-Center-API/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Message Center API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-orders-api": { - "display_name": "Orders API", - "description": ">Check the new [Orders onboarding guide](https://developers.vtex.com/docs/guides/orders-overview). We created this guide to improve the onboarding exp", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Orders-API/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Orders API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-orders-api-pii-version": { - "display_name": "Orders API (PII version)", - "description": "Endpoints that deal with order management. New version of the orders API.", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Orders-API-(PII-version)/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Orders API (PII version).json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-payments-gateway-api": { - "display_name": "Payments Gateway API", - "description": ">โ„น๏ธ Onboarding guide > > Check the new [Payments onboarding guide](https://developers.vtex.com/docs/guides/payments-overview). We created this guide t", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Payments-Gateway-API/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Payments Gateway API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-policies-system-api": { - "display_name": "Policies System API", - "description": "This API will create promotion alarms when selling products with undesired prices and promotions. It will create conditions that will check if the pri", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Policies-System-API/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Policies System API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-price-simulations": { - "display_name": "Price Simulations API", - "description": "> Check the new [Pricing onboarding guide](https://developers.vtex.com/docs/guides/pricing-overview). We created this guide to improve the onboarding ", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Price-Simulations/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Price Simulations.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-pricing-api": { - "display_name": "Pricing API", - "description": "> Check the new [Pricing onboarding guide](https://developers.vtex.com/vtex-rest-api/docs/pricing-overview). We created this guide to improve the onbo", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Pricing-API/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Pricing API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-pricing-hub": { - "display_name": "Pricing Hub", - "description": "> This feature is in closed beta, available only for selected customers. If you have any questions, contact our [Support](https://support.vtex.com/hc/", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Pricing-Hub/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Pricing Hub.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-profile-system": { - "display_name": "Profile System", - "description": "Create shopper profiles and manage their information.", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Profile-System/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Profile System.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-promotions": { - "display_name": "Promotions & Taxes API", - "description": "> Check the new [Promotions onboarding guide](https://developers.vtex.com/vtex-rest-api/docs/promotions-overview). We created this guide to improve th", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Promotions-/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Promotions & Taxes API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-recurrence-v1": { - "display_name": "Subscription (v1 - deprecated)", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Recurrence-(v1-/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Recurrence (v1 - deprecated).json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-reviews-and-ratings-api": { - "display_name": "Reviews and Ratings API", - "description": "Reviews & Ratings is a [VTEX IO native solution](https://developers.vtex.com/vtex-developer-docs/docs/vtex-reviews-and-ratings) that allows shoppers t", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Reviews-and-Ratings-API/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Reviews and Ratings API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-search-api": { - "display_name": "Legacy Search API", - "description": "> Check the new [Search onboarding guide](https://developers.vtex.com/vtex-rest-api/docs/search-onboarding). We created this guide to improve the onbo", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Search-API/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Search API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-session-manager-api": { - "display_name": "Session Manager API", - "description": "This documentation goes in detail how to interact with Session Manager's API. For a more top-level approach, check the [design documentation](https://", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Session-Manager-API/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Session Manager API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-sku-bindings-api": { - "display_name": "SKU Bindings API", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/SKU-Bindings-API/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - SKU Bindings API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-subscriptions-api-v2": { - "display_name": "Subscriptions API (v2 - DEPRECATED)", - "description": "VTEX Subscriptions REST API Documentation This documentation describes the available REST APIs for VTEX Subscription System. With Subscriptions you ca", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Subscriptions-API-(v2)/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Subscriptions API (v2).json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-subscriptions-api-v3": { - "display_name": "Subscriptions API (v3)", - "description": "A **Subscription** is a list of items (SKUs) tied to certain recurring purchase settings: - User profile - Address - Payment method - Frequency - Cycl", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/Subscriptions-API-(v3)/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - Subscriptions API (v3).json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-vtex-do-api": { - "display_name": "VTEX Do API", - "description": "VTEX DO is a task management system for authorized users to process orders. It is possible to control notes, and create, update, list, and retrieve ta", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/VTEX-Do-API/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX - VTEX Do API.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "vtex-local-vtextemplate": { - "display_name": "Pets Api", - "spec_url": "https://api.apis.guru/v2/specs/vtex.local/VTEX_TEMPLATE/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/vtex/openapi-schemas/master/VTEX_TEMPLATE.json", - "env_vars": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - }, - "walletobjects-googleapis-com-pay-passes": { - "display_name": "Google Pay Passes API", - "description": "API for issuers to save and manage Google Wallet Objects.", - "spec_url": "https://api.apis.guru/v2/specs/walletobjects.googleapis.com/pay-passes/v1/openapi.json", - "base_url": "https://walletobjects.googleapis.com/$discovery/rest?version=v1", - "env_vars": [ - "WALLETOBJECTS_GOOGLEAPIS_COM_PAY_PASSES_API_KEY", - "WALLETOBJECTS_GOOGLEAPIS_COM_PAY_PASSES_TOKEN", - ], - }, - "walmart-com-inventory": { - "display_name": "Inventory Management", - "description": "Maintaining up-to-date inventory for your items on Walmart.com ensures a great experience for your customers and greater sales opportunities for you.", - "spec_url": "https://api.apis.guru/v2/specs/walmart.com/inventory/1.0.0/openapi.json", - "base_url": "https://developer.walmart.com/api/detail", - "env_vars": ["WALMART_CLIENT_ID"], - }, - "walmart-com-item": { - "display_name": "Item API", - "description": "Please make sure you use the correct version of the APIs for your use case. To find out the appropriate version, go to the API Docs drop down on the m", - "spec_url": "https://api.apis.guru/v2/specs/walmart.com/item/3.0.1/swagger.json", - "base_url": "https://developer.walmart.com/v1/swaggerProxy?type=item", - "env_vars": ["WALMART_CLIENT_ID"], - }, - "walmart-com-order": { - "display_name": "Orders API", - "description": "Please make sure you use the correct version of the APIs for your use case. To find out the appropriate version, go to the API Docs drop down on the m", - "spec_url": "https://api.apis.guru/v2/specs/walmart.com/order/3.0.1/swagger.json", - "base_url": "https://developer.walmart.com/v1/swaggerProxy?type=order", - "env_vars": ["WALMART_CLIENT_ID"], - }, - "walmart-com-price": { - "display_name": "Price Management", - "description": "The price is a fundamental building block for your listing on Walmart.com. You can use the price management APIs to set up and manage the price for a ", - "spec_url": "https://api.apis.guru/v2/specs/walmart.com/price/1.0.0/openapi.json", - "base_url": "https://developer.walmart.com/api/detail", - "env_vars": ["WALMART_CLIENT_ID"], - }, - "warwick-ac-uk-enterobase": { - "display_name": "Enterobase-API", - "description": "API for EnteroBase (https://enterobase.warwick.ac.uk) EnteroBase is a user-friendly online resource, where users can upload their own sequencing data ", - "spec_url": "https://api.apis.guru/v2/specs/warwick.ac.uk/enterobase/v2.0/openapi.json", - "base_url": "http://enterobase.warwick.ac.uk/api/v2.0/swagger", - "env_vars": [ - "WARWICK_AC_UK_ENTEROBASE_API_KEY", - "WARWICK_AC_UK_ENTEROBASE_TOKEN", - ], - }, - "watchful-li": { - "display_name": "watchful.li", - "spec_url": "https://api.apis.guru/v2/specs/watchful.li/1.0.0/swagger.json", - "base_url": "hhttps://app.watchful.net/api/v1/api-docs/", - "env_vars": ["WATCHFUL_LI_API_KEY", "WATCHFUL_LI_TOKEN"], - }, - "waterlinked": { - "display_name": "The Water Linked Underwater GPS API", - "description": "API for the Water Linked Underwater GPS. For more details: http://www.waterlinked.com Recommended approach for connecting to a Underwater GPS via the ", - "spec_url": "https://api.apis.guru/v2/specs/waterlinked.com/1.0.0/swagger.json", - "base_url": "http://demo.waterlinked.com/swagger/swagger.json", - "env_vars": ["WATERLINKED_API_KEY", "WATERLINKED_TOKEN"], - }, - "wealthreader": { - "display_name": "Wealth Reader API", - "description": "Las APIs regulatorias basadas en PSD2 proporcionan acceso a cierta informaciรณn financiera como saldos de cuentas bancarias y transacciones. Sin embarg", - "spec_url": "https://api.apis.guru/v2/specs/wealthreader.com/1.0.0/openapi.json", - "base_url": "https://api.swaggerhub.com/apis/Wealth-Reader/api/1.0.0", - "env_vars": ["WEALTHREADER_API_KEY", "WEALTHREADER_TOKEN"], - }, - "weatherbit": { - "display_name": "Weatherbit - Interactive Swagger UI Documentation", - "description": "This an interactive version of the documentation for the Weatherbit API. The base URL for the API is [http://api.weatherbit.io/v2.0/](http://api.weath", - "spec_url": "https://api.apis.guru/v2/specs/weatherbit.io/2.0.0/swagger.json", - "base_url": "https://www.weatherbit.io/static/swagger.json", - "env_vars": ["WEATHERBIT_API_KEY", "WEATHERBIT_TOKEN"], - }, - "weber-gesamtausgabe-de": { - "display_name": "WeGA API", - "description": "โš ๏ธDEPRECATION WARNINGโš ๏ธ
          This version of the WeGA API specification is outdated and superseded by [version 1.1.0](https://weber-gesamtausgab", - "spec_url": "https://api.apis.guru/v2/specs/weber-gesamtausgabe.de/1.0.0/swagger.json", - "base_url": "https://weber-gesamtausgabe.de/api/v1/swagger.json", - "env_vars": ["WEBER_GESAMTAUSGABE_DE_API_KEY", "WEBER_GESAMTAUSGABE_DE_TOKEN"], - }, - "webflow": { - "display_name": "Lucidtech API", - "spec_url": "https://api.apis.guru/v2/specs/webflow.com/2023-03-01T164537Z/openapi.json", - "base_url": "https://raw.githubusercontent.com/LucidtechAI/cradl-docs/master/static/oas.yaml", - "env_vars": ["WEBFLOW_API_KEY", "WEBFLOW_TOKEN"], - }, - "webscraping-ai": { - "display_name": "WebScraping.AI", - "description": "A client for https://webscraping.ai API. It provides a web scaping automation API with Chrome JS rendering, rotating proxies and builtin HTML parsing.", - "spec_url": "https://api.apis.guru/v2/specs/webscraping.ai/2.0.7/openapi.json", - "base_url": "https://webscraping.ai/openapi.yml", - "env_vars": ["WEBSCRAPING_AI_API_KEY", "WEBSCRAPING_AI_TOKEN"], - }, - "wellknown-ai": { - "display_name": "Wellknown", - "description": "A registry of AI Plugins.", - "spec_url": "https://api.apis.guru/v2/specs/wellknown.ai/1.0.0/openapi.json", - "base_url": "https://www.wellknown.ai/api/doc", - "env_vars": ["WELLKNOWN_AI_API_KEY", "WELLKNOWN_AI_TOKEN"], - }, - "whapi-com-accounts": { - "display_name": "Accounts API", - "description": "The Accounts API is a collection of methods used to query a customer account. It allows the developer to retrieve account-related data such as the use", - "spec_url": "https://api.apis.guru/v2/specs/whapi.com/accounts/2.0.0/swagger.json", - "base_url": "https://developer.williamhill.com/wh-docs/docs-sdks/accounts/swagger/docs", - "env_vars": ["WHAPI_TOKEN"], - }, - "whapi-com-bets": { - "display_name": "Bets API", - "description": "The Bets API methods are used to place single, multiple and complex bets and to retrieve a customerโ€™s bet history. When retrieving a customerโ€™s bet hi", - "spec_url": "https://api.apis.guru/v2/specs/whapi.com/bets/2.0.0/openapi.json", - "base_url": "https://developer.williamhill.com/wh-docs/docs-sdks/bets/swagger/docs", - "env_vars": ["WHAPI_TOKEN"], - }, - "whapi-com-locations": { - "display_name": "Locations", - "description": "The Locations API is a collection of methods that support geographical information. The first method is an address lookup service for UK addresses. Th", - "spec_url": "https://api.apis.guru/v2/specs/whapi.com/locations/2.0/swagger.json", - "base_url": "https://developer.williamhill.com/wh-docs/docs-sdks/locations/swagger/docs", - "env_vars": ["WHAPI_TOKEN"], - }, - "whapi-com-numbers": { - "display_name": "Numbers API", - "description": "The William Hill Numbers API uses a single method that allows you to generate random numbers for your application. Numbers can either be unique or can", - "spec_url": "https://api.apis.guru/v2/specs/whapi.com/numbers/2.0/swagger.json", - "base_url": "https://developer.williamhill.com/wh-docs/docs-sdks/numbers/swagger/docs", - "env_vars": ["WHAPI_TOKEN"], - }, - "whapi-com-sessions": { - "display_name": "Sessions API", - "description": "The William Hill Sessions API uses a central authentication service (CAS*) on all resources that require access to a customerโ€™s account or betting fun", - "spec_url": "https://api.apis.guru/v2/specs/whapi.com/sessions/2.0.0/swagger.json", - "base_url": "https://developer.williamhill.com/wh-docs/docs-sdks/sessions/swagger/docs", - "env_vars": ["WHAPI_TOKEN"], - }, - "whapi-com-sportsdata": { - "display_name": "SportsData API", - "description": "The William Hill SportsData REST API is a collection of GET methods to provide William Hill product data such as sport, competition, event, market and", - "spec_url": "https://api.apis.guru/v2/specs/whapi.com/sportsdata/2/swagger.json", - "base_url": "https://developer.williamhill.com/wh-docs/docs-sdks/sportsdata/swagger/docs", - "env_vars": ["SPORTSDATA_API_KEY"], - }, - "whatsapp": { - "display_name": "WhatsApp Business API", - "description": "See https://developers.facebook.com/docs/whatsapp", - "spec_url": "https://api.apis.guru/v2/specs/whatsapp.local/1.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/unblu/WhatsApp-Business-API-OpenAPI/master/openapi.yaml", - "env_vars": ["WHATSAPP_API_KEY", "WHATSAPP_TOKEN"], - }, - "wheretocredit": { - "display_name": "Where to Credit API", - "description": "The Where to Credit API provides mileage earning calculations for frequent flyer programs around the world.", - "spec_url": "https://api.apis.guru/v2/specs/wheretocredit.com/1.0/openapi.json", - "base_url": "https://www.wheretocredit.com/swagger/v1/swagger.json", - "env_vars": ["HERE_API_KEY"], - }, - "who-hosts-this": { - "display_name": "Who Hosts This API", - "description": "Discover the hosting provider for any web site", - "spec_url": "https://api.apis.guru/v2/specs/who-hosts-this.com/0.0.1/swagger.json", - "base_url": "https://www.who-hosts-this.com/APISpecification", - "env_vars": ["WHO_HOSTS_THIS_API_KEY", "WHO_HOSTS_THIS_TOKEN"], - }, - "wikimedia": { - "display_name": "Wikimedia", - "description": "This API provides cacheable and straightforward access to Wikimedia content and data, in machine-readable formats. ### Global Rules - Limit your clien", - "spec_url": "https://api.apis.guru/v2/specs/wikimedia.org/1.0.0/swagger.json", - "base_url": "https://wikimedia.org/api/rest_v1/?spec", - "env_vars": ["WIKIMEDIA_API_KEY", "WIKIMEDIA_TOKEN"], - }, - "wikipathways": { - "display_name": "WikiPathways Webservices", - "spec_url": "https://api.apis.guru/v2/specs/wikipathways.org/1.0/openapi.json", - "base_url": "http://webservice.wikipathways.org/index.php?swagger", - "env_vars": ["WIKIPATHWAYS_API_KEY", "WIKIPATHWAYS_TOKEN"], - }, - "winsms-co-za": { - "display_name": "WINSMS", - "description": "WinSMS RESTful API", - "spec_url": "https://api.apis.guru/v2/specs/winsms.co.za/1.0.0/swagger.json", - "base_url": "https://www.winsms.co.za/api/restdocs/swagger.json", - "env_vars": ["WINSMS_CO_ZA_API_KEY", "WINSMS_CO_ZA_TOKEN"], - }, - "wiremock-org-admin": { - "display_name": "WireMock", - "spec_url": "https://api.apis.guru/v2/specs/wiremock.org/admin/2.35.0/openapi.json", - "base_url": "http://wiremock.org/assets/js/wiremock-admin-api.json", - "env_vars": ["WIREMOCK_ORG_ADMIN_API_KEY", "WIREMOCK_ORG_ADMIN_TOKEN"], - }, - "wmata-com-bus-realtime": { - "display_name": "Real-Time Bus Predictions", - "description": "Real-time bus prediction methods.", - "spec_url": "https://api.apis.guru/v2/specs/wmata.com/bus-realtime/1.0/swagger.json", - "base_url": "https://developer.wmata.com/docs/services/5476365e031f590f38092508/export?DocumentFormat=Swagger", - "env_vars": ["WMATA_COM_BUS_REALTIME_API_KEY", "WMATA_COM_BUS_REALTIME_TOKEN"], - }, - "wmata-com-bus-route": { - "display_name": "Bus Route and Stop Methods", - "description": "Bus stop information, route and schedule data, and bus positions.", - "spec_url": "https://api.apis.guru/v2/specs/wmata.com/bus-route/1.0/swagger.json", - "base_url": "https://developer.wmata.com/docs/services/54763629281d83086473f231/export?DocumentFormat=Swagger", - "env_vars": ["WMATA_COM_BUS_ROUTE_API_KEY", "WMATA_COM_BUS_ROUTE_TOKEN"], - }, - "wmata-com-incidents": { - "display_name": "Incidents", - "description": "Rail, bus, and elevator disruptions/outages.", - "spec_url": "https://api.apis.guru/v2/specs/wmata.com/incidents/1.0/swagger.json", - "base_url": "https://developer.wmata.com/docs/services/54763641281d83086473f232/export?DocumentFormat=Swagger", - "env_vars": ["WMATA_COM_INCIDENTS_API_KEY", "WMATA_COM_INCIDENTS_TOKEN"], - }, - "wmata-com-rail-realtime": { - "display_name": "Real-Time Rail Predictions", - "description": "Real-time rail prediction methods.", - "spec_url": "https://api.apis.guru/v2/specs/wmata.com/rail-realtime/1.0/swagger.json", - "base_url": "https://developer.wmata.com/docs/services/547636a6f9182302184cda78/export?DocumentFormat=Swagger", - "env_vars": [ - "WMATA_COM_RAIL_REALTIME_API_KEY", - "WMATA_COM_RAIL_REALTIME_TOKEN", - ], - }, - "wmata-com-rail-station": { - "display_name": "Rail Station Information", - "description": "Rail line and station information, including locations, fares, times, and parking.", - "spec_url": "https://api.apis.guru/v2/specs/wmata.com/rail-station/1.0/swagger.json", - "base_url": "https://developer.wmata.com/docs/services/5476364f031f590f38092507/export?DocumentFormat=Swagger", - "env_vars": ["WMATA_COM_RAIL_STATION_API_KEY", "WMATA_COM_RAIL_STATION_TOKEN"], - }, - "wolframalpha": { - "display_name": "Wolfram", - "spec_url": "https://api.apis.guru/v2/specs/wolframalpha.com/v0.1/openapi.json", - "base_url": "https://www.wolframalpha.com/.well-known/apispec.json", - "env_vars": ["WOLFRAMALPHA_API_KEY", "WOLFRAMALPHA_TOKEN"], - }, - "wordassociations": { - "display_name": "Word Associations API", - "description": "The Word Associations Network API allows developers to embed the ability to find associations for a word or phrase into their mobile apps or web servi", - "spec_url": "https://api.apis.guru/v2/specs/wordassociations.net/1.0/swagger.json", - "base_url": "https://api.wordassociations.net/documentation/swagger.json", - "env_vars": ["WORDASSOCIATIONS_API_KEY", "WORDASSOCIATIONS_TOKEN"], - }, - "wordnik": { - "display_name": "Wordnik", - "description": "Wordnik is the worlds biggest online English dictionary, by number of words", - "spec_url": "https://api.apis.guru/v2/specs/wordnik.com/4.0/openapi.json", - "base_url": "https://developer.wordnik.com/api-docs/swagger.json", - "env_vars": ["WORDNIK_API_KEY", "WORDNIK_TOKEN"], - }, - "worldtimeapi": { - "display_name": "World Time API", - "description": "A simple API to get the current time based on a request with a timezone.", - "spec_url": "https://api.apis.guru/v2/specs/worldtimeapi.org/20210108/openapi.json", - "base_url": "http://worldtimeapi.org/api", - "env_vars": ["WORLDTIMEAPI_API_KEY", "WORLDTIMEAPI_TOKEN"], - }, - "wowza": { - "display_name": "Wowza Streaming Cloud REST API Reference Documentation", - "description": "# About the REST API The Wowza Streaming CloudTM REST API (application programming interface) offers complete programmatic control over liv", - "spec_url": "https://api.apis.guru/v2/specs/wowza.com/1/swagger.json", - "base_url": "https://sandbox.cloud.wowza.com/en/docs/api/v1", - "env_vars": ["WOWZA_API_KEY", "WOWZA_TOKEN"], - }, - "wso2apistore-com-transform": { - "display_name": "Transform", - "description": "This API provides XML to JSON, JSON to XML transformations.", - "spec_url": "https://api.apis.guru/v2/specs/wso2apistore.com/transform/1.0.0/openapi.json", - "base_url": "https://developers.wso2apistore.com/api-docs/manjular-AT-wso2.com-AT-developer/Transform/1.0.0", - "env_vars": [ - "WSO2APISTORE_COM_TRANSFORM_API_KEY", - "WSO2APISTORE_COM_TRANSFORM_TOKEN", - ], - }, - "xero-com-xero-identity": { - "display_name": "Xero OAuth 2 Identity Service API", - "description": "These endpoints are related to managing authentication tokens and identity for Xero API", - "spec_url": "https://api.apis.guru/v2/specs/xero.com/xero-identity/2.9.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/master/xero-identity.yaml", - "env_vars": ["XERO_CLIENT_ID"], - }, - "xero-com-xero-payroll-au": { - "display_name": "Xero Payroll AU API", - "description": "This is the Xero Payroll API for orgs in Australia region.", - "spec_url": "https://api.apis.guru/v2/specs/xero.com/xero-payroll-au/2.9.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/master/xero-payroll-au.yaml", - "env_vars": ["XERO_CLIENT_ID"], - }, - "xero-com-xeroaccounting": { - "display_name": "Xero Accounting API", - "spec_url": "https://api.apis.guru/v2/specs/xero.com/xero_accounting/2.9.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/master/xero_accounting.yaml", - "env_vars": ["XERO_CLIENT_ID"], - }, - "xero-com-xeroassets": { - "display_name": "Xero Assets API", - "description": "The Assets API exposes fixed asset related functions of the Xero Accounting application and can be used for a variety of purposes such as creating ass", - "spec_url": "https://api.apis.guru/v2/specs/xero.com/xero_assets/2.9.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/master/xero_assets.yaml", - "env_vars": ["XERO_CLIENT_ID"], - }, - "xero-com-xerobankfeeds": { - "display_name": "Xero Bank Feeds API", - "description": "The Bank Feeds API is a closed API that is only available to financial institutions that have an established financial services partnership with Xero.", - "spec_url": "https://api.apis.guru/v2/specs/xero.com/xero_bankfeeds/2.9.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/master/xero_bankfeeds.yaml", - "env_vars": ["XERO_CLIENT_ID"], - }, - "xero-com-xerofiles": { - "display_name": "Xero Files API", - "description": "These endpoints are specific to Xero Files API", - "spec_url": "https://api.apis.guru/v2/specs/xero.com/xero_files/2.9.4/openapi.json", - "base_url": "https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/master/xero_files.yaml", - "env_vars": ["XERO_CLIENT_ID"], - }, - "xkcd": { - "display_name": "XKCD", - "description": "Webcomic of romance, sarcasm, math, and language.", - "spec_url": "https://api.apis.guru/v2/specs/xkcd.com/1.0.0/openapi.json", - "base_url": "https://raw.githubusercontent.com/APIs-guru/unofficial_openapi_specs/master/xkcd.com/1.0.0/openapi.yaml", - "env_vars": ["XKCD_API_KEY", "XKCD_TOKEN"], - }, - "xtrf-eu": { - "display_name": "XTRF Home Portal API", - "description": "XTRF Home Portal API enables you to perform operations on Projects, Quotes, Customers, Vendors etc. as a XTRF Home Portal user.
          The documentation ", - "spec_url": "https://api.apis.guru/v2/specs/xtrf.eu/2.0/openapi.json", - "base_url": "https://presentation.s.xtrf.eu/home-api/openapi.json", - "env_vars": ["XTRF_EU_API_KEY", "XTRF_EU_TOKEN"], - }, - "yodlee": { - "display_name": "Yodlee Core APIs", - "description": "This file describes the Yodlee Platform APIs using the swagger notation. You can use this swagger file to generate client side SDKs to the Yodlee Plat", - "spec_url": "https://api.apis.guru/v2/specs/yodlee.com/1.1.0/openapi.json", - "base_url": "https://developer.yodlee.com/sites/default/files/api_spec/coreapisoas3.yml", - "env_vars": ["YODLEE_API_KEY", "YODLEE_TOKEN"], - }, - "youneedabudget": { - "display_name": "YNAB API Endpoints", - "description": "Our API uses a REST based design, leverages the JSON data format, and relies upon HTTPS for transport. We respond with meaningful HTTP response codes ", - "spec_url": "https://api.apis.guru/v2/specs/youneedabudget.com/1.0.0/openapi.json", - "base_url": "https://api.youneedabudget.com/papi/spec-v1-swagger.json", - "env_vars": ["YOUNEEDABUDGET_API_KEY", "YOUNEEDABUDGET_TOKEN"], - }, - "zalando": { - "display_name": "Zalando Shop", - "description": "The shop API empowers developers to build amazing new apps or websites using Zalando shop data and services.", - "spec_url": "https://api.apis.guru/v2/specs/zalando.com/v1.0/swagger.json", - "base_url": "https://api.zalando.com/schema/swagger.json", - "env_vars": ["ZALANDO_API_KEY", "ZALANDO_TOKEN"], - }, - "zapier-com-nla": { - "display_name": "Zapier Natural Language Actions (NLA) API - Beta", - "description": ' ## Hi, there! Welcome to the **Zapier Natural Language Action', - "spec_url": "https://api.apis.guru/v2/specs/zapier.com/nla/1.0.0/openapi.json", - "base_url": "https://nla.zapier.com/api/v1/openapi.json", - "env_vars": ["ZAPIER_COM_NLA_API_KEY", "ZAPIER_COM_NLA_TOKEN"], - }, - "zappiti": { - "display_name": "Zappiti Player API", - "description": "Move your app forward with the Zappiti Player API. Use http://your-player-ip:8990/ as base URL for your requests.", - "spec_url": "https://api.apis.guru/v2/specs/zappiti.com/4.15.174/swagger.json", - "base_url": "http://zappiti.com/api/zappiti-player-4k/swagger/swagger.yaml", - "env_vars": ["ZAPPITI_API_KEY", "ZAPPITI_TOKEN"], - }, - "zeit-co": { - "display_name": "ZEIT API", - "spec_url": "https://api.apis.guru/v2/specs/zeit.co/v2019-01-07/openapi.json", - "base_url": "https://unpkg.com/@zeit/openapi", - "env_vars": ["ZEIT_CO_API_KEY", "ZEIT_CO_TOKEN"], - }, - "zeno-fm": { - "display_name": "Aggregators API Service", - "description": "Aggregators API", - "spec_url": "https://api.apis.guru/v2/specs/zeno.fm/0.6-99cfdac/openapi.json", - "base_url": "https://api.zeno.fm/v3/api-docs", - "env_vars": ["ZENO_FM_API_KEY", "ZENO_FM_TOKEN"], - }, - "zenoti": { - "display_name": "Zenoti API", - "description": "Our API documentation has been moved to https://docs.zenoti.com.", - "spec_url": "https://api.apis.guru/v2/specs/zenoti.com/1.0.0/openapi.json", - "base_url": "https://zenotiopenapi.docs.apiary.io/api-description-document", - "env_vars": ["ZENOTI_API_KEY", "ZENOTI_TOKEN"], - }, - "zoom-us": { - "display_name": "Zoom API", - "description": "The Zoom API allows developers to access information from Zoom. You can use this API to build private services or public applications on the [Zoom App", - "spec_url": "https://api.apis.guru/v2/specs/zoom.us/2.0.0/openapi.json", - "base_url": "https://marketplace.zoom.us/docs/api-reference/zoom-api/Zoom%20API.oas2.json", - "env_vars": ["ZOOM_US_API_KEY", "ZOOM_US_TOKEN"], - }, - "zoomconnect": { - "display_name": "www.zoomconnect.com", - "description": "The world's greatest SMS API", - "spec_url": "https://api.apis.guru/v2/specs/zoomconnect.com/1/swagger.json", - "base_url": "https://www.zoomconnect.com/zoom/api-docs", - "env_vars": ["ZOOMCONNECT_API_KEY", "ZOOMCONNECT_TOKEN"], - }, - "zuora": { - "display_name": "API Reference: Billing", - "description": "# Introduction Welcome to the reference for the Zuora Billing REST API! To learn about the common use cases of Zuora Billing REST APIs, check out the ", - "spec_url": "https://api.apis.guru/v2/specs/zuora.com/2021-08-20/openapi.json", - "base_url": "https://www.zuora.com/wp-content/themes/zuora/yaml/swagger.yaml?v-7.0", - "env_vars": ["ZUORA_API_KEY", "ZUORA_TOKEN"], - }, -} - - -# Total: 1116 providers diff --git a/pkg/hanzo-tools-api/hanzo_tools/api/client.py b/pkg/hanzo-tools-api/hanzo_tools/api/client.py deleted file mode 100644 index 1aaddc3b5..000000000 --- a/pkg/hanzo-tools-api/hanzo_tools/api/client.py +++ /dev/null @@ -1,1019 +0,0 @@ -"""Main API client with explicit typed methods. - -Provides a clean, agent-friendly interface for API operations: - - from hanzo_tools.api import APIClient - - client = APIClient() - - # Discover providers - providers = await client.list_providers() - - # Configure credentials - await client.config("cloudflare", api_key="...") - - # Load spec and discover operations - await client.spec("cloudflare") - ops = await client.ops("cloudflare", search="zones") - - # Call operations - result = await client.call("cloudflare", "listZones", params={"page": 1}) - - # Raw requests - result = await client.raw("github", "GET", "/user/repos") -""" - -from __future__ import annotations - -import logging -from pathlib import Path -from typing import Any - -from .credentials import CredentialManager, get_credential_manager -from .errors import ( - ProviderNotFoundError, -) -from .models import ( - APICallResult, - EffectiveCredential, - Operation, - OperationListResult, - ProviderListResult, - ProviderStatus, - ToolParameter, - ToolSchema, -) -from .openapi_client import OpenAPIClient, SpecCache -from .providers import PROVIDER_CONFIGS - -logger = logging.getLogger(__name__) - - -class APIClient: - """Main API client with explicit typed methods. - - This is the primary interface for agent integration. Each method - has explicit parameters with type hints for easy auto-completion - and validation. - - Example: - client = APIClient() - - # List and configure providers - providers = await client.list_providers() - await client.config("cloudflare", api_key="my-token") - - # Load spec and call operations - await client.spec("cloudflare") - result = await client.call("cloudflare", "listZones") - - # Or make raw requests - result = await client.raw("github", "GET", "/user/repos") - """ - - def __init__( - self, - config_dir: Path | None = None, - credential_manager: CredentialManager | None = None, - ): - """Initialize API client. - - Args: - config_dir: Directory for credentials and specs - credential_manager: Custom credential manager - """ - self._config_dir = config_dir or Path.home() / ".hanzo" / "api" - self._cred_manager = credential_manager or get_credential_manager() - self._spec_cache = SpecCache(self._config_dir / "specs") - self._clients: dict[str, OpenAPIClient] = {} - - async def _get_openapi_client(self, provider: str) -> OpenAPIClient: - """Get or create OpenAPI client for provider.""" - if provider not in self._clients: - self._clients[provider] = OpenAPIClient( - provider=provider, - credential_manager=self._cred_manager, - spec_cache=self._spec_cache, - ) - return self._clients[provider] - - # ========================================================================= - # Provider Discovery - # ========================================================================= - - async def list_providers(self, configured_only: bool = False) -> ProviderListResult: - """List all available providers and their status. - - Args: - configured_only: Only return providers with credentials configured - - Returns: - ProviderListResult with provider statuses - - Example: - result = await client.list_providers() - for p in result.configured: - print(f"{p.name}: {p.source}") - """ - statuses = await self._cred_manager.list_providers() - - if configured_only: - statuses = [s for s in statuses if s.configured] - - return ProviderListResult( - providers=statuses, - configured_count=sum(1 for s in statuses if s.configured), - total_count=len(statuses), - ) - - async def get_provider(self, provider: str) -> ProviderStatus: - """Get detailed status for a specific provider. - - Args: - provider: Provider name - - Returns: - ProviderStatus with configuration details - - Raises: - ProviderNotFoundError: If provider is unknown - """ - providers = await self.list_providers() - - for p in providers.providers: - if p.name == provider: - return p - - raise ProviderNotFoundError( - provider=provider, - available_providers=[p.name for p in providers.providers], - ) - - # ========================================================================= - # Credential Management - # ========================================================================= - - async def config( - self, - provider: str, - api_key: str | None = None, - api_secret: str | None = None, - account_id: str | None = None, - base_url: str | None = None, - **extra, - ) -> None: - """Configure credentials for a provider. - - Args: - provider: Provider name (e.g., 'cloudflare', 'github') - api_key: API key or token - api_secret: API secret (for providers that need it) - account_id: Account/organization ID - base_url: Custom base URL override - **extra: Additional provider-specific fields - - Example: - await client.config("cloudflare", api_key="my-token") - await client.config("stripe", api_key="sk_xxx", api_secret="whsec_xxx") - """ - await self._cred_manager.set_credential( - provider=provider, - api_key=api_key, - api_secret=api_secret, - account_id=account_id, - base_url=base_url, - **extra, - ) - logger.info(f"Configured credentials for {provider}") - - async def delete_config(self, provider: str) -> bool: - """Delete stored credentials for a provider. - - Args: - provider: Provider name - - Returns: - True if deleted, False if not found - """ - return await self._cred_manager.delete_credential(provider) - - async def get_effective_credentials(self, provider: str) -> EffectiveCredential: - """Get credential with full resolution info. - - Shows exactly where the credential came from, useful for - debugging authentication issues. - - Args: - provider: Provider name - - Returns: - EffectiveCredential with source information - - Example: - effective = await client.get_effective_credentials("cloudflare") - print(f"Source: {effective.source}") # e.g., "environment" - print(f"Env var: {effective.env_var_used}") # e.g., "CLOUDFLARE_API_TOKEN" - """ - return await self._cred_manager.get_effective_credentials(provider) - - # ========================================================================= - # OpenAPI Spec Management - # ========================================================================= - - async def spec( - self, - provider: str, - spec_url: str | None = None, - force_refresh: bool = False, - ) -> int: - """Load or refresh OpenAPI spec for a provider. - - Args: - provider: Provider name - spec_url: Custom URL to OpenAPI spec - force_refresh: Force refresh even if cached - - Returns: - Number of operations discovered - - Example: - count = await client.spec("cloudflare") - print(f"Loaded {count} operations") - - # Load custom spec - await client.spec("custom-api", spec_url="https://api.example.com/openapi.json") - """ - openapi_client = await self._get_openapi_client(provider) - - if spec_url: - await openapi_client.load_spec(spec_url) - elif force_refresh: - await openapi_client.refresh_spec(force=True) - else: - await openapi_client.load_spec() - - return len(openapi_client._operations) - - async def has_spec(self, provider: str) -> bool: - """Check if spec is available for provider. - - Args: - provider: Provider name - - Returns: - True if spec is loaded or cached - """ - openapi_client = await self._get_openapi_client(provider) - return openapi_client.has_spec() - - async def spec_age(self, provider: str) -> float | None: - """Get age of cached spec in seconds. - - Args: - provider: Provider name - - Returns: - Age in seconds, or None if no spec - """ - openapi_client = await self._get_openapi_client(provider) - return openapi_client.spec_age() - - async def refresh_spec(self, provider: str, force: bool = False) -> bool: - """Refresh the OpenAPI spec. - - Args: - provider: Provider name - force: Force refresh even if not stale - - Returns: - True if spec was refreshed, False if using cache - """ - openapi_client = await self._get_openapi_client(provider) - return await openapi_client.refresh_spec(force=force) - - # ========================================================================= - # Operation Discovery - # ========================================================================= - - async def ops( - self, - provider: str, - search: str | None = None, - tag: str | None = None, - method: str | None = None, - path_contains: str | None = None, - operation_id_prefix: str | None = None, - include_deprecated: bool = False, - ) -> OperationListResult: - """List available operations for a provider. - - Args: - provider: Provider name - search: Search in operation ID, summary, description - tag: Filter by tag - method: Filter by HTTP method (GET, POST, etc.) - path_contains: Filter by path substring - operation_id_prefix: Filter by operation ID prefix - include_deprecated: Include deprecated operations - - Returns: - OperationListResult with matching operations - - Example: - # List all operations - result = await client.ops("cloudflare") - - # Search for zone operations - result = await client.ops("cloudflare", search="zone") - - # Filter by tag - result = await client.ops("cloudflare", tag="Zones") - - # Filter by method - result = await client.ops("cloudflare", method="POST") - """ - openapi_client = await self._get_openapi_client(provider) - - if not openapi_client.spec_loaded: - await openapi_client.load_spec() - - return openapi_client.list_operations( - search=search, - tag=tag, - method=method, - path_contains=path_contains, - operation_id_prefix=operation_id_prefix, - include_deprecated=include_deprecated, - ) - - async def get_operation(self, provider: str, operation_id: str) -> Operation: - """Get detailed information about a specific operation. - - Args: - provider: Provider name - operation_id: Operation ID - - Returns: - Operation with full details including parameter schemas - - Example: - op = await client.get_operation("cloudflare", "listZones") - print(op.params_schema) # JSON schema for parameters - print(op.request_body_schema) # JSON schema for body - """ - openapi_client = await self._get_openapi_client(provider) - - if not openapi_client.spec_loaded: - await openapi_client.load_spec() - - return openapi_client.get_operation(operation_id) - - # ========================================================================= - # API Calls - # ========================================================================= - - async def call( - self, - provider: str, - operation_id: str, - params: dict[str, Any] | None = None, - body: dict[str, Any] | None = None, - headers: dict[str, str] | None = None, - dry_run: bool = False, - ) -> APICallResult: - """Call an API operation by ID. - - Args: - provider: Provider name - operation_id: Operation ID from the OpenAPI spec - params: Path, query, and header parameters - body: Request body (for POST/PUT/PATCH) - headers: Additional headers - dry_run: If True, return request details without making call - - Returns: - APICallResult with response data - - Example: - # Simple call - result = await client.call("cloudflare", "listZones") - - # With parameters - result = await client.call( - "cloudflare", - "getZone", - params={"zone_id": "abc123"} - ) - - # With body - result = await client.call( - "cloudflare", - "createDNSRecord", - params={"zone_id": "abc123"}, - body={"type": "A", "name": "test", "content": "1.2.3.4"} - ) - """ - openapi_client = await self._get_openapi_client(provider) - - if not openapi_client.spec_loaded: - await openapi_client.load_spec() - - return await openapi_client.call( - operation_id=operation_id, - params=params, - body=body, - headers=headers, - dry_run=dry_run, - ) - - async def raw( - self, - provider: str, - method: str, - path: str, - params: dict[str, Any] | None = None, - body: dict[str, Any] | None = None, - headers: dict[str, str] | None = None, - dry_run: bool = False, - ) -> APICallResult: - """Make a raw HTTP request. - - Useful for endpoints not in the spec or custom calls. - - Args: - provider: Provider name (for authentication) - method: HTTP method (GET, POST, PUT, PATCH, DELETE) - path: URL path - params: Query parameters - body: Request body - headers: Additional headers - dry_run: If True, return request details without making call - - Returns: - APICallResult with response data - - Example: - # GET request - result = await client.raw("github", "GET", "/user") - - # POST with body - result = await client.raw( - "github", - "POST", - "/repos/owner/repo/issues", - body={"title": "Bug report", "body": "..."} - ) - """ - openapi_client = await self._get_openapi_client(provider) - - return await openapi_client.call_raw( - method=method, - path=path, - params=params, - body=body, - headers=headers, - dry_run=dry_run, - ) - - # ========================================================================= - # Tool Schemas (for agent integration) - # ========================================================================= - - def get_tool_schemas(self) -> list[ToolSchema]: - """Get machine-readable schemas for all client methods. - - Returns tool schemas that can be used by agents for - function calling with strict argument validation. - - Returns: - List of ToolSchema objects for each method - """ - return [ - ToolSchema( - name="list_providers", - description="List all available API providers and their configuration status", - parameters=[ - ToolParameter( - name="configured_only", - type="boolean", - description="Only return providers with credentials configured", - default=False, - ), - ], - returns="ProviderListResult with provider statuses", - examples=[ - "await client.list_providers()", - "await client.list_providers(configured_only=True)", - ], - ), - ToolSchema( - name="config", - description="Configure credentials for a provider", - parameters=[ - ToolParameter( - name="provider", - type="string", - description="Provider name (e.g., 'cloudflare', 'github')", - required=True, - ), - ToolParameter( - name="api_key", - type="string", - description="API key or token", - ), - ToolParameter( - name="api_secret", - type="string", - description="API secret (for providers that need it)", - ), - ToolParameter( - name="base_url", - type="string", - description="Custom base URL override", - ), - ], - returns="None", - examples=["await client.config('cloudflare', api_key='my-token')"], - ), - ToolSchema( - name="spec", - description="Load or refresh OpenAPI spec for a provider", - parameters=[ - ToolParameter( - name="provider", - type="string", - description="Provider name", - required=True, - ), - ToolParameter( - name="spec_url", - type="string", - description="Custom URL to OpenAPI spec", - ), - ToolParameter( - name="force_refresh", - type="boolean", - description="Force refresh even if cached", - default=False, - ), - ], - returns="Number of operations discovered", - examples=[ - "await client.spec('cloudflare')", - "await client.spec('custom', spec_url='...')", - ], - ), - ToolSchema( - name="ops", - description="List available operations for a provider", - parameters=[ - ToolParameter( - name="provider", - type="string", - description="Provider name", - required=True, - ), - ToolParameter( - name="search", - type="string", - description="Search in operation ID, summary, description", - ), - ToolParameter( - name="tag", - type="string", - description="Filter by tag", - ), - ToolParameter( - name="method", - type="string", - description="Filter by HTTP method", - enum=["GET", "POST", "PUT", "PATCH", "DELETE"], - ), - ], - returns="OperationListResult with matching operations", - examples=["await client.ops('cloudflare', search='zone')"], - ), - ToolSchema( - name="call", - description="Call an API operation by ID", - parameters=[ - ToolParameter( - name="provider", - type="string", - description="Provider name", - required=True, - ), - ToolParameter( - name="operation_id", - type="string", - description="Operation ID from the OpenAPI spec", - required=True, - ), - ToolParameter( - name="params", - type="object", - description="Path, query, and header parameters", - ), - ToolParameter( - name="body", - type="object", - description="Request body (for POST/PUT/PATCH)", - ), - ToolParameter( - name="dry_run", - type="boolean", - description="Return request details without making call", - default=False, - ), - ], - returns="APICallResult with response data", - examples=["await client.call('cloudflare', 'listZones')"], - ), - ToolSchema( - name="raw", - description="Make a raw HTTP request to any endpoint", - parameters=[ - ToolParameter( - name="provider", - type="string", - description="Provider name (for authentication)", - required=True, - ), - ToolParameter( - name="method", - type="string", - description="HTTP method", - required=True, - enum=["GET", "POST", "PUT", "PATCH", "DELETE"], - ), - ToolParameter( - name="path", - type="string", - description="URL path", - required=True, - ), - ToolParameter( - name="params", - type="object", - description="Query parameters", - ), - ToolParameter( - name="body", - type="object", - description="Request body", - ), - ], - returns="APICallResult with response data", - examples=["await client.raw('github', 'GET', '/user')"], - ), - ] - - # ========================================================================= - # API Discovery & Registration - # ========================================================================= - - async def search(self, query: str) -> list[dict[str, Any]]: - """Search for APIs in public registries. - - Searches openapisearch.com for publicly available APIs. - - Args: - query: Search query (e.g., 'weather', 'spotify', 'notion') - - Returns: - List of API results with id, name, description, spec_url - - Example: - results = await client.search("weather") - for api in results: - print(f"{api['id']}: {api['name']}") - """ - import httpx - - async with httpx.AsyncClient(timeout=30) as http: - # Search openapisearch.com - try: - response = await http.get( - "https://openapisearch.com/api/search", - params={"q": query}, - ) - if response.status_code == 200: - data = response.json() - return data.get("results", []) - except Exception as e: - logger.warning(f"openapisearch.com search failed: {e}") - - # Fallback: search handmade OpenAPIs on GitHub - try: - response = await http.get( - "https://api.github.com/repos/janwilmake/handmade-openapis/contents" - ) - if response.status_code == 200: - files = response.json() - results = [] - for f in files: - if f["name"].endswith(".json"): - name = f["name"].replace(".json", "") - if query.lower() in name.lower(): - results.append( - { - "id": name, - "name": name.replace("-", " ").title(), - "spec_url": f["download_url"], - } - ) - return results - except Exception as e: - logger.warning(f"GitHub fallback search failed: {e}") - - return [] - - async def register( - self, - name: str, - spec_url: str | None = None, - base_url: str | None = None, - auth_type: str = "bearer", - auth_header: str = "Authorization", - auth_prefix: str = "Bearer", - api_key: str | None = None, - ) -> int: - """Register a custom API provider dynamically. - - Allows using any OpenAPI spec without pre-configuration. - - Args: - name: Unique name for this API (e.g., 'my-api') - spec_url: URL to OpenAPI spec (JSON or YAML) - base_url: Base URL for API calls (extracted from spec if not provided) - auth_type: Authentication type ('bearer', 'header', 'basic', 'api_key', 'query') - auth_header: Header name for auth (default: 'Authorization') - auth_prefix: Prefix for auth value (default: 'Bearer') - api_key: API key (optional, can also use config() later) - - Returns: - Number of operations discovered - - Example: - # Register from OpenAPI search - results = await client.search("notion") - count = await client.register("notion", spec_url=results[0]["spec_url"]) - - # Register custom API - await client.register( - "my-api", - spec_url="https://api.example.com/openapi.json", - api_key="secret" - ) - """ - from .models import AuthType, ProviderConfig - - # Create dynamic provider config - auth_type_enum = ( - AuthType(auth_type) - if auth_type in [e.value for e in AuthType] - else AuthType.BEARER - ) - - config = ProviderConfig( - name=name, - display_name=name.replace("-", " ").replace("_", " ").title(), - base_url=base_url or "", - auth_type=auth_type_enum, - auth_header=auth_header, - auth_prefix=auth_prefix, - spec_url=spec_url, - env_vars=[], - ) - - # Add to provider configs - PROVIDER_CONFIGS[name] = config - - # Set credentials if provided - if api_key: - await self.config(name, api_key=api_key, base_url=base_url) - - # Load spec - if spec_url: - return await self.spec(name, spec_url=spec_url) - - return 0 - - async def overview(self, provider: str, compact: bool = True) -> str: - """Get a human-readable overview of an API. - - Generates agent-friendly, token-efficient summaries. For large APIs, - automatically minifies output to reduce token usage. - - Args: - provider: Provider name (must have spec loaded) - compact: Use compact format (default True for token efficiency) - - Returns: - Formatted overview of all endpoints - - Example: - await client.spec("cloudflare") - print(await client.overview("cloudflare")) - """ - openapi_client = await self._get_openapi_client(provider) - - if not openapi_client.spec_loaded: - await openapi_client.load_spec() - - result = openapi_client.list_operations() - base_url = openapi_client.base_url or "" - - # Build operation items - items = [] - for op in result.operations: - # Get full operation for parameters - try: - full_op = openapi_client.get_operation(op.operation_id) - # Build query params string - query_params = [ - f"{p.name}={p.schema_type}" - for p in full_op.parameters - if p.location.value == "query" - ] - query_string = f"?{'&'.join(query_params)}" if query_params else "" - except Exception: - query_string = "" - - items.append( - { - "operation_id": op.operation_id, - "method": op.method, - "path": op.path, - "query_string": query_string, - "summary": op.summary or "", - } - ) - - # Check if we need minified output (>10k chars โ‰ˆ 2500 tokens) - is_large = len(str(items)) > 10000 - - lines = [] - - # Header - lines.append(f"# {provider.upper()} API") - if base_url: - lines.append(f"Base: {base_url}") - lines.append(f"Endpoints: {result.total_count}") - lines.append("") - - if compact or is_large: - # Minified format for agents - for item in items: - if is_large: - # Super compact: just operation_id and summary - summary_part = f" - {item['summary']}" if item["summary"] else "" - lines.append(f"- {item['operation_id']}{summary_part}") - else: - # Compact: include method and path - summary_part = f" - {item['summary']}" if item["summary"] else "" - lines.append( - f"- {item['operation_id']}: {item['method']} {item['path']}{item['query_string']}{summary_part}" - ) - else: - # Full format grouped by tag - tags: dict[str, list] = {} - for op in result.operations: - for tag in op.tags or ["Other"]: - if tag not in tags: - tags[tag] = [] - tags[tag].append(op) - - for tag_name in sorted(tags.keys()): - tag_ops = tags[tag_name] - lines.append(f"## {tag_name}") - lines.append("") - for op in tag_ops: - lines.append( - f"- **{op.method}** `{op.path}` - {op.summary or op.operation_id}" - ) - lines.append("") - - # Footer with help - lines.append("") - lines.append(f"Use 'api ops {provider} --search ' for filtered list") - lines.append(f"Use 'api call {provider} ' to call an endpoint") - - return "\n".join(lines) - - # ========================================================================= - # Preloading - # ========================================================================= - - async def preload_specs( - self, - providers: list[str] | None = None, - concurrent: int = 5, - ) -> dict[str, int]: - """Preload and cache OpenAPI specs for faster first use. - - Args: - providers: List of provider names to preload. If None, preloads - all providers with spec_url configured. - concurrent: Max concurrent downloads (default 5) - - Returns: - Dict mapping provider name to operation count (or -1 if failed) - - Example: - # Preload all configured specs - results = await client.preload_specs() - print(f"Preloaded {len(results)} specs") - - # Preload specific providers - await client.preload_specs(["cloudflare", "github", "openai"]) - """ - import asyncio - - # Determine which providers to preload - if providers is None: - providers = [ - name for name, config in PROVIDER_CONFIGS.items() if config.spec_url - ] - - results: dict[str, int] = {} - semaphore = asyncio.Semaphore(concurrent) - - async def preload_one(provider: str) -> tuple[str, int]: - async with semaphore: - try: - count = await self.spec(provider) - logger.info(f"Preloaded {provider}: {count} operations") - return (provider, count) - except Exception as e: - logger.warning(f"Failed to preload {provider}: {e}") - return (provider, -1) - - tasks = [preload_one(p) for p in providers] - completed = await asyncio.gather(*tasks) - - for name, count in completed: - results[name] = count - - return results - - @classmethod - async def preload_common(cls) -> APIClient: - """Create a client with common specs preloaded. - - Returns a client with cloudflare, github, stripe, openai, notion - specs already cached. - - Example: - client = await APIClient.preload_common() - # Now all common specs are cached - """ - client = cls() - await client.preload_specs( - [ - "cloudflare", - "github", - "stripe", - "openai", - "notion", - "groq", - ] - ) - return client - - # ========================================================================= - # Cleanup - # ========================================================================= - - async def close(self) -> None: - """Close all HTTP clients.""" - for client in self._clients.values(): - await client.close() - self._clients.clear() - - async def __aenter__(self) -> APIClient: - return self - - async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: - await self.close() - - -# ============================================================================= -# Module-level singleton -# ============================================================================= - -_default_client: APIClient | None = None - - -def get_api_client() -> APIClient: - """Get the default API client instance.""" - global _default_client - if _default_client is None: - _default_client = APIClient() - return _default_client - - -def reset_api_client() -> None: - """Reset the default API client (for testing).""" - global _default_client - _default_client = None diff --git a/pkg/hanzo-tools-api/hanzo_tools/api/credentials.py b/pkg/hanzo-tools-api/hanzo_tools/api/credentials.py deleted file mode 100644 index 41a476c0a..000000000 --- a/pkg/hanzo-tools-api/hanzo_tools/api/credentials.py +++ /dev/null @@ -1,320 +0,0 @@ -"""Credential manager with pluggable storage. - -Provides unified credential management with: -- Explicit resolution order (override > memory > file > env) -- Pluggable storage backends -- Clear source introspection -""" - -from __future__ import annotations - -import logging -import os -from pathlib import Path - -from .errors import CredentialError -from .models import ( - Credential, - CredentialSource, - EffectiveCredential, - ProviderConfig, - ProviderStatus, -) -from .providers import ( - ENV_VAR_MAPPINGS, - PROVIDER_CONFIGS, - get_env_vars, - get_provider_config, -) -from .storage import ( - ChainedCredentialStorage, - CredentialStorage, - EnvironmentCredentialStorage, - FileCredentialStorage, - MemoryCredentialStorage, -) - -logger = logging.getLogger(__name__) - - -class CredentialManager: - """Manages API credentials with explicit resolution order. - - Resolution order: - 1. Per-call overrides (via override_for_provider) - 2. In-memory config (set at runtime) - 3. Stored file (~/.hanzo/api/credentials.json) - 4. Environment variables - - Example: - manager = CredentialManager() - - # Check what credentials are available - effective = await manager.get_effective_credentials("cloudflare") - print(f"Source: {effective.source}") # e.g., "environment" - - # Configure credentials - await manager.set_credential("cloudflare", api_key="my-key") - - # Make a call with per-call override - async with manager.override_for_provider("cloudflare", api_key="temp-key"): - # Uses temp-key for this call only - cred = await manager.get_credential("cloudflare") - """ - - def __init__( - self, - config_dir: Path | None = None, - storage: CredentialStorage | None = None, - ): - """Initialize credential manager. - - Args: - config_dir: Directory for file storage. Defaults to ~/.hanzo/api/ - storage: Custom storage backend. If None, uses default chain. - """ - self.config_dir = config_dir or Path.home() / ".hanzo" / "api" - self.specs_dir = self.config_dir / "specs" - - # Set up storage chain - # Note: MemoryCredentialStorage is NOT included here because - # per-call overrides are handled separately by self._overrides - if storage: - self._storage = storage - else: - file_path = self.config_dir / "credentials.json" - self._storage = ChainedCredentialStorage( - [ - FileCredentialStorage(file_path), # Persistent storage - EnvironmentCredentialStorage( - ENV_VAR_MAPPINGS - ), # Read-only fallback - ] - ) - - # Separate override storage for context manager - self._overrides = MemoryCredentialStorage() - - async def get_credential(self, provider: str) -> Credential: - """Get credential for a provider. - - Args: - provider: Provider name - - Returns: - Credential (may be empty if not found) - """ - effective = await self.get_effective_credentials(provider) - return effective.credential - - async def get_effective_credentials(self, provider: str) -> EffectiveCredential: - """Get credential with full resolution info. - - Shows exactly where the credential came from and what - alternatives are available. - - Args: - provider: Provider name - - Returns: - EffectiveCredential with source information - """ - # Check overrides first - override = await self._overrides.get(provider) - if override and override.has_credentials: - return EffectiveCredential( - credential=override, - source=CredentialSource.OVERRIDE, - ) - - # Check chained storage - if isinstance(self._storage, ChainedCredentialStorage): - cred, store = await self._storage.get_with_source(provider) - if cred and cred.has_credentials: - source = CredentialSource.NONE - env_var = None - - if isinstance(store, MemoryCredentialStorage): - source = CredentialSource.MEMORY - elif isinstance(store, FileCredentialStorage): - source = CredentialSource.STORED - elif isinstance(store, EnvironmentCredentialStorage): - source = CredentialSource.ENVIRONMENT - # Find which env var was used - for var in get_env_vars(provider): - if os.environ.get(var): - env_var = var - break - - return EffectiveCredential( - credential=cred, - source=source, - env_var_used=env_var, - ) - else: - cred = await self._storage.get(provider) - if cred and cred.has_credentials: - return EffectiveCredential( - credential=cred, - source=CredentialSource.STORED, - ) - - # Check provider config for base URL - config = get_provider_config(provider) - base_url = config.base_url if config else None - - return EffectiveCredential( - credential=Credential(provider=provider, base_url=base_url), - source=CredentialSource.NONE, - ) - - async def set_credential( - self, - provider: str, - api_key: str | None = None, - api_secret: str | None = None, - account_id: str | None = None, - base_url: str | None = None, - **extra, - ) -> None: - """Store a credential. - - Args: - provider: Provider name - api_key: API key or token - api_secret: API secret (for providers that need it) - account_id: Account/organization ID - base_url: Custom base URL override - **extra: Additional provider-specific fields - """ - # Get existing to merge - existing = await self._storage.get(provider) - - credential = Credential( - provider=provider, - api_key=api_key or (existing.api_key if existing else None), - api_secret=api_secret or (existing.api_secret if existing else None), - account_id=account_id or (existing.account_id if existing else None), - base_url=base_url or (existing.base_url if existing else None), - extra={**(existing.extra if existing else {}), **extra}, - ) - - await self._storage.set(credential) - - async def delete_credential(self, provider: str) -> bool: - """Delete a stored credential. - - Args: - provider: Provider name - - Returns: - True if deleted, False if not found - """ - return await self._storage.delete(provider) - - async def list_credentials(self) -> list[str]: - """List all providers with stored credentials.""" - return await self._storage.list() - - async def list_providers(self) -> list[ProviderStatus]: - """List all providers and their status. - - Returns: - List of provider status objects - """ - result = [] - all_providers = set(PROVIDER_CONFIGS.keys()) | set(ENV_VAR_MAPPINGS.keys()) - - # Add stored providers too - stored = await self._storage.list() - all_providers.update(stored) - - for provider in sorted(all_providers): - effective = await self.get_effective_credentials(provider) - config = get_provider_config(provider) - - # Check spec cache - spec_cached = False - spec_age = None - spec_file = self.specs_dir / f"{provider}.json" - if spec_file.exists(): - spec_cached = True - import time - - spec_age = time.time() - spec_file.stat().st_mtime - - result.append( - ProviderStatus( - name=provider, - display_name=config.display_name if config else provider.title(), - configured=effective.has_credentials, - source=effective.source if effective.has_credentials else None, - base_url=config.base_url if config else "", - has_spec=bool(config and config.spec_url), - spec_cached=spec_cached, - spec_age_seconds=spec_age, - ) - ) - - return result - - async def require_credential(self, provider: str) -> Credential: - """Get credential or raise CredentialError. - - Args: - provider: Provider name - - Returns: - Credential with valid api_key - - Raises: - CredentialError: If no credential is configured - """ - effective = await self.get_effective_credentials(provider) - - if not effective.has_credentials: - env_vars = get_env_vars(provider) - raise CredentialError( - message=f"No credentials configured for {provider}", - provider=provider, - env_vars=env_vars, - ) - - return effective.credential - - def get_provider_config(self, provider: str) -> ProviderConfig | None: - """Get provider configuration.""" - return get_provider_config(provider) - - -# Singleton instance -_credential_manager: CredentialManager | None = None - - -def get_credential_manager() -> CredentialManager: - """Get the global credential manager instance.""" - global _credential_manager - if _credential_manager is None: - _credential_manager = CredentialManager() - return _credential_manager - - -def reset_credential_manager() -> None: - """Reset the global credential manager (for testing).""" - global _credential_manager - _credential_manager = None - - -# Re-export for backwards compatibility -__all__ = [ - "CredentialManager", - "get_credential_manager", - "reset_credential_manager", - "Credential", - "EffectiveCredential", - "CredentialSource", - "ProviderConfig", - "ProviderStatus", - "PROVIDER_CONFIGS", - "ENV_VAR_MAPPINGS", -] diff --git a/pkg/hanzo-tools-api/hanzo_tools/api/errors.py b/pkg/hanzo-tools-api/hanzo_tools/api/errors.py deleted file mode 100644 index b2c525b66..000000000 --- a/pkg/hanzo-tools-api/hanzo_tools/api/errors.py +++ /dev/null @@ -1,369 +0,0 @@ -"""Structured errors for hanzo-tools-api. - -Provides detailed error objects with hints for resolution, -making it easy for agents to understand and recover from errors. -""" - -from __future__ import annotations - -from typing import Any - -from pydantic import BaseModel, Field - - -class APIErrorInfo(BaseModel): - """Structured error information.""" - - error_type: str = Field(description="Error category") - message: str = Field(description="Human-readable error message") - provider: str | None = Field(default=None, description="Provider involved") - operation_id: str | None = Field(default=None, description="Operation involved") - status_code: int | None = Field(default=None, description="HTTP status code") - missing_fields: list[str] = Field( - default_factory=list, description="Required fields that are missing" - ) - hint: str | None = Field(default=None, description="Suggested fix") - env_vars: list[str] = Field( - default_factory=list, description="Environment variables to set" - ) - config_command: str | None = Field(default=None, description="CLI command to fix") - details: dict[str, Any] | None = Field( - default=None, description="Additional error details" - ) - - -class APIError(Exception): - """Base exception for API errors with structured info.""" - - def __init__( - self, - message: str, - error_type: str = "api_error", - provider: str | None = None, - operation_id: str | None = None, - status_code: int | None = None, - body: Any = None, - hint: str | None = None, - missing_fields: list[str] | None = None, - env_vars: list[str] | None = None, - config_command: str | None = None, - details: dict[str, Any] | None = None, - ): - self.message = message - self.error_type = error_type - self.provider = provider - self.operation_id = operation_id - self.status_code = status_code - self.body = body - self.hint = hint - self.missing_fields = missing_fields or [] - self.env_vars = env_vars or [] - self.config_command = config_command - self.details = details - - super().__init__(message) - - @property - def info(self) -> APIErrorInfo: - """Get structured error info.""" - return APIErrorInfo( - error_type=self.error_type, - message=self.message, - provider=self.provider, - operation_id=self.operation_id, - status_code=self.status_code, - missing_fields=self.missing_fields, - hint=self.hint, - env_vars=self.env_vars, - config_command=self.config_command, - details=self.details, - ) - - def __str__(self) -> str: - parts = [self.message] - if self.hint: - parts.append(f"Hint: {self.hint}") - if self.env_vars: - parts.append(f"Try setting: {', '.join(self.env_vars)}") - if self.config_command: - parts.append(f"Or run: {self.config_command}") - return "\n".join(parts) - - -class CredentialError(APIError): - """Error related to credentials.""" - - def __init__( - self, - message: str, - provider: str, - missing_fields: list[str] | None = None, - env_vars: list[str] | None = None, - ): - config_cmd = f'api config --provider {provider} --api_key "YOUR_KEY"' - hint = f"Configure credentials for {provider}" - - super().__init__( - message=message, - error_type="credential_error", - provider=provider, - missing_fields=missing_fields or ["api_key"], - hint=hint, - env_vars=env_vars or [], - config_command=config_cmd, - ) - - -class ProviderNotFoundError(APIError): - """Provider is not recognized.""" - - def __init__(self, provider: str, available_providers: list[str] | None = None): - hint = None - if available_providers: - hint = f"Available providers: {', '.join(available_providers[:10])}" - - super().__init__( - message=f"Unknown provider: {provider}", - error_type="provider_not_found", - provider=provider, - hint=hint, - details=( - {"available_providers": available_providers} - if available_providers - else None - ), - ) - - -class OperationNotFoundError(APIError): - """Operation is not found in the spec.""" - - def __init__( - self, - operation_id: str, - provider: str, - similar_operations: list[str] | None = None, - ): - hint = None - if similar_operations: - hint = f"Similar operations: {', '.join(similar_operations[:5])}" - else: - hint = f"Run 'api ops --provider {provider}' to list available operations" - - super().__init__( - message=f"Operation not found: {operation_id}", - error_type="operation_not_found", - provider=provider, - operation_id=operation_id, - hint=hint, - details=( - {"similar_operations": similar_operations} - if similar_operations - else None - ), - ) - - -class SpecNotLoadedError(APIError): - """OpenAPI spec has not been loaded.""" - - def __init__(self, provider: str): - super().__init__( - message=f"OpenAPI spec not loaded for {provider}", - error_type="spec_not_loaded", - provider=provider, - hint=f"Load the spec first with 'api spec --provider {provider}'", - config_command=f"api spec --provider {provider}", - ) - - -class SpecParseError(APIError): - """Error parsing OpenAPI spec.""" - - def __init__( - self, - message: str, - provider: str, - path_in_spec: str | None = None, - parse_error: str | None = None, - ): - details = {} - if path_in_spec: - details["path_in_spec"] = path_in_spec - if parse_error: - details["parse_error"] = parse_error - - super().__init__( - message=message, - error_type="spec_parse_error", - provider=provider, - hint="The OpenAPI spec may be invalid or use unsupported features", - details=details if details else None, - ) - - -class ParameterValidationError(APIError): - """Parameter validation failed.""" - - def __init__( - self, - message: str, - provider: str, - operation_id: str, - missing_params: list[str] | None = None, - invalid_params: dict[str, str] | None = None, - ): - details = {} - if missing_params: - details["missing_params"] = missing_params - if invalid_params: - details["invalid_params"] = invalid_params - - hint_parts = [] - if missing_params: - hint_parts.append(f"Missing required params: {', '.join(missing_params)}") - if invalid_params: - for param, reason in invalid_params.items(): - hint_parts.append(f"{param}: {reason}") - - super().__init__( - message=message, - error_type="parameter_validation_error", - provider=provider, - operation_id=operation_id, - missing_fields=missing_params or [], - hint="; ".join(hint_parts) if hint_parts else None, - details=details if details else None, - ) - - -class NetworkError(APIError): - """Network-related error.""" - - def __init__( - self, - message: str, - provider: str, - url: str | None = None, - original_error: Exception | None = None, - ): - details = {} - if url: - details["url"] = url - if original_error: - details["original_error"] = str(original_error) - - super().__init__( - message=message, - error_type="network_error", - provider=provider, - hint="Check your network connection and the provider's API status", - details=details if details else None, - ) - - -class RateLimitError(APIError): - """Rate limit exceeded.""" - - def __init__( - self, - provider: str, - retry_after: int | None = None, - status_code: int = 429, - ): - hint = "Rate limit exceeded" - if retry_after: - hint = f"Rate limit exceeded. Retry after {retry_after} seconds" - - super().__init__( - message=f"Rate limit exceeded for {provider}", - error_type="rate_limit_error", - provider=provider, - status_code=status_code, - hint=hint, - details={"retry_after": retry_after} if retry_after else None, - ) - - -class AuthenticationError(APIError): - """Authentication failed.""" - - def __init__( - self, - provider: str, - status_code: int = 401, - env_vars: list[str] | None = None, - ): - super().__init__( - message=f"Authentication failed for {provider}", - error_type="authentication_error", - provider=provider, - status_code=status_code, - hint="Check that your API key is valid and has the required permissions", - env_vars=env_vars or [], - config_command=f'api config --provider {provider} --api_key "YOUR_KEY"', - ) - - -class SpecLoadError(APIError): - """Error loading OpenAPI spec.""" - - def __init__( - self, - provider: str, - spec_url: str | None = None, - original_error: Exception | None = None, - ): - message = f"Failed to load OpenAPI spec for {provider}" - if spec_url: - message = f"Failed to load OpenAPI spec from {spec_url}" - - details = {} - if spec_url: - details["spec_url"] = spec_url - if original_error: - details["original_error"] = str(original_error) - - super().__init__( - message=message, - error_type="spec_load_error", - provider=provider, - hint="Check the spec URL is accessible and returns valid OpenAPI JSON/YAML", - details=details if details else None, - ) - - -class ValidationError(APIError): - """General validation error.""" - - def __init__( - self, - message: str, - provider: str | None = None, - operation_id: str | None = None, - field: str | None = None, - value: Any = None, - expected: str | None = None, - ): - details = {} - if field: - details["field"] = field - if value is not None: - details["value"] = str(value) - if expected: - details["expected"] = expected - - hint = None - if field and expected: - hint = f"Field '{field}' should be {expected}" - elif field: - hint = f"Invalid value for field '{field}'" - - super().__init__( - message=message, - error_type="validation_error", - provider=provider, - operation_id=operation_id, - hint=hint, - details=details if details else None, - ) diff --git a/pkg/hanzo-tools-api/hanzo_tools/api/hanzo_tool.py b/pkg/hanzo-tools-api/hanzo_tools/api/hanzo_tool.py deleted file mode 100644 index 7c8625c15..000000000 --- a/pkg/hanzo-tools-api/hanzo_tools/api/hanzo_tool.py +++ /dev/null @@ -1,209 +0,0 @@ -"""Unified Hanzo platform tool. - -Provides a compact `hanzo` surface that routes to Hanzo service tools -(`auth`, `billing`, `commerce`, `iam`, `ingress`, `kms`, `mpc`, `paas`, -`team`, and generic `api`). -""" - -from __future__ import annotations - -import importlib -import inspect -import json -from typing import Annotated, Any, final, override - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext -from pydantic import Field - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context - -SERVICE_TOOL_PATHS: dict[str, str] = { - "api": "hanzo_tools.api.api_tool:APITool", - "auth": "hanzo_tools.auth.login_tool:LoginTool", - "billing": "hanzo_tools.billing.billing_tool:BillingTool", - "commerce": "hanzo_tools.commerce.commerce_tool:CommerceTool", - "iam": "hanzo_tools.iam.iam_tool:IAMTool", - "ingress": "hanzo_tools.ingress.ingress_tool:IngressTool", - "kms": "hanzo_tools.kms.kms_tool:KMSTool", - "mpc": "hanzo_tools.mpc.mpc_tool:MPCTool", - "paas": "hanzo_tools.paas.paas_tool:PaaSTool", - "team": "hanzo_tools.team.team_tool:TeamTool", -} - -SERVICE_ALIASES: dict[str, str] = { - "platform": "paas", - "identity": "iam", - "payments": "billing", - "store": "commerce", -} - - -@final -class HanzoTool(BaseTool): - """Unified tool for Hanzo platform services.""" - - name = "hanzo" - - def __init__(self) -> None: - self._delegates: dict[str, BaseTool] = {} - - @property - @override - def description(self) -> str: - return """Unified Hanzo platform tool. - -Use one `hanzo` tool surface for service operations across: -- auth -- billing -- commerce -- iam -- ingress -- kms -- mpc -- paas -- team -- api (generic OpenAPI bridge) - -Parameters: -- service: Target Hanzo service -- action: Service-specific action -- args: JSON object string for service-specific parameters - -Examples: - hanzo(service="auth", action="status") - hanzo(service="commerce", action="orders") - hanzo(service="iam", action="users", args='{"owner":"hanzo"}') - hanzo(service="api", action="list") -""" - - def _normalize_service(self, service: str) -> str: - key = (service or "").strip().lower().replace("-", "_") - key = SERVICE_ALIASES.get(key, key) - return key - - def _load_delegate(self, service: str) -> BaseTool: - if service in self._delegates: - return self._delegates[service] - - path = SERVICE_TOOL_PATHS.get(service) - if not path: - available = ", ".join(sorted(SERVICE_TOOL_PATHS.keys())) - raise ValueError( - f"Unknown service '{service}'. Available services: {available}" - ) - - module_name, class_name = path.split(":") - module = importlib.import_module(module_name) - cls = getattr(module, class_name) - tool = cls() - self._delegates[service] = tool - return tool - - def _parse_args(self, args: str | None) -> dict[str, Any]: - if not args: - return {} - try: - parsed = json.loads(args) - except json.JSONDecodeError as exc: - raise ValueError(f"args must be valid JSON object string: {exc}") from exc - - if not isinstance(parsed, dict): - raise ValueError("args must decode to a JSON object") - return parsed - - async def _delegate_call( - self, - tool: BaseTool, - ctx: MCPContext, - payload: dict[str, Any], - ) -> str: - sig = inspect.signature(tool.call) - params = sig.parameters - accepts_kwargs = any( - p.kind is inspect.Parameter.VAR_KEYWORD for p in params.values() - ) - - if accepts_kwargs: - return await tool.call(ctx, **payload) - - allowed = { - name - for name, param in params.items() - if name not in {"self", "ctx"} - and param.kind in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY) - } - filtered = {k: v for k, v in payload.items() if k in allowed} - return await tool.call(ctx, **filtered) - - @override - @auto_timeout("hanzo") - async def call( - self, - ctx: MCPContext, - service: str = "api", - action: str = "list", - args: str | None = None, - **kwargs: Any, - ) -> str: - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - service_key = self._normalize_service(service) - if service_key in {"services", "list"}: - return json.dumps( - { - "services": sorted(SERVICE_TOOL_PATHS.keys()), - "aliases": SERVICE_ALIASES, - "usage": 'hanzo(service="commerce", action="orders", args="{\\"query\\":\\"...\\\"}")', - }, - indent=2, - ) - - try: - delegate = self._load_delegate(service_key) - payload = {"action": action} - payload.update(self._parse_args(args)) - payload.update({k: v for k, v in kwargs.items() if v is not None}) - return await self._delegate_call(delegate, ctx, payload) - except Exception as exc: - return json.dumps( - { - "error": str(exc), - "service": service_key, - "available_services": sorted(SERVICE_TOOL_PATHS.keys()), - }, - indent=2, - ) - - def register(self, mcp_server: FastMCP) -> None: - """Register unified hanzo tool with explicit compact params.""" - tool_instance = self - - @mcp_server.tool(name=self.name, description=self.description) - async def hanzo( - service: Annotated[ - str, - Field( - description=( - "Target service: api, auth, billing, commerce, iam, ingress, " - "kms, mpc, paas, team. Use 'services' to list." - ) - ), - ] = "api", - action: Annotated[ - str, - Field(description="Service action to execute (service-specific)."), - ] = "list", - args: Annotated[ - str | None, - Field( - description=( - "JSON object string with service-specific parameters. " - 'Example: "{\\"query\\":\\"foo\\",\\"owner\\":\\"hanzo\\"}"' - ) - ), - ] = None, - ctx: MCPContext = None, # type: ignore[assignment] - ) -> str: - return await tool_instance.call(ctx, service=service, action=action, args=args) diff --git a/pkg/hanzo-tools-api/hanzo_tools/api/models.py b/pkg/hanzo-tools-api/hanzo_tools/api/models.py deleted file mode 100644 index 6c5158674..000000000 --- a/pkg/hanzo-tools-api/hanzo_tools/api/models.py +++ /dev/null @@ -1,427 +0,0 @@ -"""Pydantic models for hanzo-tools-api. - -Provides strongly-typed, structured data objects for: -- API call results -- Operations and parameters -- Credentials and provider configs -- Tool schemas for agent integration -""" - -from __future__ import annotations - -from datetime import datetime -from enum import Enum -from typing import Any - -from pydantic import BaseModel, ConfigDict, Field - -# ============================================================================= -# Enums -# ============================================================================= - - -class AuthType(str, Enum): # noqa: UP042 - """Authentication type for API providers.""" - - BEARER = "bearer" # Authorization: Bearer - BASIC = "basic" # Basic auth (username:password) - HEADER = "header" # Custom header (e.g., x-api-key) - API_KEY = "api_key" # X-API-Key header - QUERY = "query" # Query parameter - - -class CredentialSource(str, Enum): # noqa: UP042 - """Source of credential resolution.""" - - OVERRIDE = "override" # Per-call override - MEMORY = "memory" # In-memory config - STORED = "stored" # File storage - ENVIRONMENT = "environment" # Environment variable - NONE = "none" # Not found - - -class ParameterLocation(str, Enum): # noqa: UP042 - """Location of API parameter.""" - - PATH = "path" - QUERY = "query" - HEADER = "header" - COOKIE = "cookie" - - -# ============================================================================= -# Credential Models -# ============================================================================= - - -class Credential(BaseModel): - """API credential for a provider.""" - - model_config = ConfigDict(frozen=False) - - provider: str = Field(description="Provider name") - api_key: str | None = Field(default=None, description="API key or token") - api_secret: str | None = Field(default=None, description="API secret (if needed)") - account_id: str | None = Field(default=None, description="Account/organization ID") - base_url: str | None = Field(default=None, description="Custom base URL override") - extra: dict[str, Any] = Field( - default_factory=dict, description="Provider-specific fields" - ) - - @property - def has_credentials(self) -> bool: - """Check if credential has at least an API key.""" - return bool(self.api_key) - - -class EffectiveCredential(BaseModel): - """Resolved credential with source information.""" - - model_config = ConfigDict(frozen=True) - - credential: Credential - source: CredentialSource = Field( - description="Where the credential was resolved from" - ) - env_var_used: str | None = Field( - default=None, description="Environment variable used (if any)" - ) - - @property - def has_credentials(self) -> bool: - return self.credential.has_credentials - - -class ProviderConfig(BaseModel): - """Provider-specific configuration.""" - - model_config = ConfigDict(frozen=True) - - name: str = Field(description="Provider identifier") - display_name: str = Field(description="Human-readable name") - base_url: str = Field(description="Default base URL") - auth_type: AuthType = Field( - default=AuthType.BEARER, description="Authentication method" - ) - auth_header: str = Field( - default="Authorization", description="Header name for auth" - ) - auth_prefix: str = Field(default="Bearer", description="Prefix for auth value") - auth_query_param: str = Field(default="api_key", description="Query param for auth") - spec_url: str | None = Field(default=None, description="URL to OpenAPI spec") - env_vars: list[str] = Field( - default_factory=list, description="Environment variables to check" - ) - extra_headers: dict[str, str] = Field( - default_factory=dict, description="Additional headers" - ) - - -class ProviderStatus(BaseModel): - """Status of a provider configuration.""" - - model_config = ConfigDict(frozen=True) - - name: str - display_name: str - configured: bool = Field(description="Whether credentials are available") - source: CredentialSource | None = Field( - default=None, description="Credential source" - ) - base_url: str - has_spec: bool = Field(description="Whether OpenAPI spec is available") - spec_cached: bool = Field( - default=False, description="Whether spec is cached locally" - ) - spec_age_seconds: float | None = Field( - default=None, description="Age of cached spec" - ) - - -# ============================================================================= -# OpenAPI Operation Models -# ============================================================================= - - -class Parameter(BaseModel): - """API operation parameter.""" - - model_config = ConfigDict(frozen=True) - - name: str = Field(description="Parameter name") - location: ParameterLocation = Field(description="Where the parameter goes") - required: bool = Field(default=False, description="Whether parameter is required") - schema_type: str = Field(default="string", description="Parameter data type") - description: str = Field(default="", description="Parameter description") - default: Any = Field(default=None, description="Default value") - enum: list[Any] = Field(default_factory=list, description="Allowed values") - json_schema: dict[str, Any] | None = Field( - default=None, description="Full JSON schema" - ) - - -class Operation(BaseModel): - """API operation definition.""" - - model_config = ConfigDict(frozen=True) - - operation_id: str = Field(description="Unique operation identifier") - method: str = Field(description="HTTP method (GET, POST, etc.)") - path: str = Field(description="URL path pattern") - summary: str = Field(default="", description="Short description") - description: str = Field(default="", description="Detailed description") - parameters: list[Parameter] = Field( - default_factory=list, description="Operation parameters" - ) - request_body_schema: dict[str, Any] | None = Field( - default=None, description="Request body JSON schema" - ) - request_body_required: bool = Field( - default=False, description="Whether request body is required" - ) - response_schema: dict[str, Any] | None = Field( - default=None, description="Response JSON schema" - ) - tags: list[str] = Field(default_factory=list, description="Operation tags") - deprecated: bool = Field( - default=False, description="Whether operation is deprecated" - ) - - @property - def params_schema(self) -> dict[str, Any]: - """Get JSON schema for parameters.""" - properties = {} - required = [] - - for param in self.parameters: - prop = {"type": param.schema_type} - if param.description: - prop["description"] = param.description - if param.enum: - prop["enum"] = param.enum - if param.default is not None: - prop["default"] = param.default - if param.json_schema: - prop.update(param.json_schema) - - properties[param.name] = prop - if param.required: - required.append(param.name) - - return { - "type": "object", - "properties": properties, - "required": required, - } - - def compact_repr(self) -> str: - """Get compact representation for agent prompts.""" - return f"{self.operation_id}: {self.method} {self.path} - {self.summary}" - - -class OperationSummary(BaseModel): - """Compact operation summary for agent discovery.""" - - model_config = ConfigDict(frozen=True) - - operation_id: str - method: str - path: str - summary: str - tags: list[str] = Field(default_factory=list) - deprecated: bool = False - - @classmethod - def from_operation(cls, op: Operation) -> OperationSummary: - return cls( - operation_id=op.operation_id, - method=op.method, - path=op.path, - summary=op.summary, - tags=op.tags, - deprecated=op.deprecated, - ) - - -# ============================================================================= -# API Call Result Models -# ============================================================================= - - -class APICallResult(BaseModel): - """Structured result from an API call.""" - - model_config = ConfigDict(frozen=True) - - success: bool = Field(description="Whether the call succeeded (2xx status)") - status_code: int = Field(description="HTTP status code") - headers: dict[str, str] = Field( - default_factory=dict, description="Response headers" - ) - body: Any = Field( - default=None, description="Response body (parsed JSON or raw text)" - ) - raw_body: str | None = Field(default=None, description="Raw response body") - elapsed_ms: float | None = Field( - default=None, description="Request duration in milliseconds" - ) - request_id: str | None = Field(default=None, description="Request ID from headers") - - # Metadata - provider: str = Field(description="Provider that was called") - operation_id: str | None = Field( - default=None, description="Operation ID (if using spec)" - ) - method: str = Field(description="HTTP method used") - url: str = Field(description="Full URL that was called") - - @property - def data(self) -> Any: - """Alias for body for backwards compatibility.""" - return self.body - - @property - def is_error(self) -> bool: - """Check if response indicates an error.""" - return self.status_code >= 400 - - def raise_for_status(self) -> None: - """Raise APIError if response indicates an error.""" - if self.is_error: - from .errors import APIError - - raise APIError( - message=f"API call failed with status {self.status_code}", - status_code=self.status_code, - body=self.body, - provider=self.provider, - ) - - -# ============================================================================= -# Spec Cache Models -# ============================================================================= - - -class SpecCacheEntry(BaseModel): - """Cached OpenAPI spec entry.""" - - model_config = ConfigDict(frozen=False) - - provider: str - spec: dict[str, Any] - fetched_at: datetime - etag: str | None = None - last_modified: str | None = None - source_url: str | None = None - - @property - def age_seconds(self) -> float: - """Get age of cache entry in seconds.""" - return (datetime.now() - self.fetched_at).total_seconds() - - @property - def is_stale(self) -> bool: - """Check if cache is stale (>24 hours).""" - return self.age_seconds > 86400 - - -# ============================================================================= -# Tool Schema Models (for agent integration) -# ============================================================================= - - -class ToolParameter(BaseModel): - """Parameter definition for tool schema.""" - - model_config = ConfigDict(frozen=True) - - name: str - type: str - description: str - required: bool = False - default: Any = None - enum: list[str] | None = None - - -class ToolSchema(BaseModel): - """Machine-readable tool schema for agent integration.""" - - model_config = ConfigDict(frozen=True) - - name: str = Field(description="Tool/method name") - description: str = Field(description="What the tool does") - parameters: list[ToolParameter] = Field(default_factory=list) - returns: str = Field(description="Return type description") - examples: list[str] = Field(default_factory=list, description="Example usage") - - def to_json_schema(self) -> dict[str, Any]: - """Convert to JSON schema format.""" - properties = {} - required = [] - - for param in self.parameters: - prop = {"type": param.type, "description": param.description} - if param.enum: - prop["enum"] = param.enum - if param.default is not None: - prop["default"] = param.default - properties[param.name] = prop - if param.required: - required.append(param.name) - - return { - "name": self.name, - "description": self.description, - "parameters": { - "type": "object", - "properties": properties, - "required": required, - }, - } - - -# ============================================================================= -# List/Discovery Results -# ============================================================================= - - -class ProviderListResult(BaseModel): - """Result of listing providers.""" - - model_config = ConfigDict(frozen=True) - - providers: list[ProviderStatus] - configured_count: int - total_count: int - - @property - def configured(self) -> list[ProviderStatus]: - """Get only configured providers.""" - return [p for p in self.providers if p.configured] - - @property - def unconfigured(self) -> list[ProviderStatus]: - """Get only unconfigured providers.""" - return [p for p in self.providers if not p.configured] - - -class OperationListResult(BaseModel): - """Result of listing operations.""" - - model_config = ConfigDict(frozen=True) - - provider: str - operations: list[OperationSummary] - total_count: int - filter_applied: str | None = None - - def by_tag(self) -> dict[str, list[OperationSummary]]: - """Group operations by tag.""" - result: dict[str, list[OperationSummary]] = {} - for op in self.operations: - for tag in op.tags or ["untagged"]: - if tag not in result: - result[tag] = [] - result[tag].append(op) - return result diff --git a/pkg/hanzo-tools-api/hanzo_tools/api/openapi_client.py b/pkg/hanzo-tools-api/hanzo_tools/api/openapi_client.py deleted file mode 100644 index 77b518c5d..000000000 --- a/pkg/hanzo-tools-api/hanzo_tools/api/openapi_client.py +++ /dev/null @@ -1,1053 +0,0 @@ -"""OpenAPI client with smart caching and spec normalization. - -Provides: -- Spec fetching with ETag/Last-Modified caching -- Robust spec normalization (handles messy specs) -- Operation discovery with filtering -- Parameter validation -""" - -from __future__ import annotations - -import json -import logging -import re -import time -from datetime import datetime -from difflib import SequenceMatcher -from pathlib import Path -from typing import Any -from urllib.parse import urlencode, urljoin - -import aiofiles -import httpx -import yaml - -from .credentials import CredentialManager, get_credential_manager -from .errors import ( - AuthenticationError, - NetworkError, - OperationNotFoundError, - ParameterValidationError, - RateLimitError, - SpecNotLoadedError, - SpecParseError, -) -from .models import ( - APICallResult, - Credential, - Operation, - OperationListResult, - OperationSummary, - Parameter, - ParameterLocation, - SpecCacheEntry, -) -from .providers import get_provider_config - -logger = logging.getLogger(__name__) - - -class SpecCache: - """Smart OpenAPI spec caching with ETag support.""" - - def __init__(self, cache_dir: Path | None = None): - """Initialize spec cache. - - Args: - cache_dir: Directory for cached specs. Defaults to ~/.hanzo/api/specs/ - """ - self.cache_dir = cache_dir or Path.home() / ".hanzo" / "api" / "specs" - self._memory_cache: dict[str, SpecCacheEntry] = {} - - def _ensure_dir(self) -> None: - """Ensure cache directory exists.""" - self.cache_dir.mkdir(parents=True, exist_ok=True) - - def _cache_path(self, provider: str) -> Path: - """Get cache file path for provider.""" - return self.cache_dir / f"{provider}.json" - - def _meta_path(self, provider: str) -> Path: - """Get metadata file path for provider.""" - return self.cache_dir / f"{provider}.meta.json" - - def has_spec(self, provider: str) -> bool: - """Check if spec is cached for provider.""" - if provider in self._memory_cache: - return True - return self._cache_path(provider).exists() - - def spec_age(self, provider: str) -> float | None: - """Get age of cached spec in seconds.""" - if provider in self._memory_cache: - return self._memory_cache[provider].age_seconds - - cache_path = self._cache_path(provider) - if cache_path.exists(): - return time.time() - cache_path.stat().st_mtime - return None - - def is_stale(self, provider: str, max_age: float = 86400) -> bool: - """Check if spec is stale (default: 24 hours).""" - age = self.spec_age(provider) - if age is None: - return True - return age > max_age - - async def get(self, provider: str) -> SpecCacheEntry | None: - """Get cached spec entry.""" - # Check memory cache - if provider in self._memory_cache: - return self._memory_cache[provider] - - # Check file cache - cache_path = self._cache_path(provider) - meta_path = self._meta_path(provider) - - if cache_path.exists(): - try: - async with aiofiles.open(cache_path) as f: - spec = json.loads(await f.read()) - - # Load metadata - etag = None - last_modified = None - source_url = None - fetched_at = datetime.fromtimestamp(cache_path.stat().st_mtime) - - if meta_path.exists(): - async with aiofiles.open(meta_path) as f: - meta = json.loads(await f.read()) - etag = meta.get("etag") - last_modified = meta.get("last_modified") - source_url = meta.get("source_url") - if meta.get("fetched_at"): - fetched_at = datetime.fromisoformat(meta["fetched_at"]) - - entry = SpecCacheEntry( - provider=provider, - spec=spec, - fetched_at=fetched_at, - etag=etag, - last_modified=last_modified, - source_url=source_url, - ) - - self._memory_cache[provider] = entry - return entry - - except Exception as e: - logger.warning(f"Failed to load cached spec for {provider}: {e}") - - return None - - async def set( - self, - provider: str, - spec: dict, - etag: str | None = None, - last_modified: str | None = None, - source_url: str | None = None, - ) -> SpecCacheEntry: - """Cache a spec.""" - self._ensure_dir() - - entry = SpecCacheEntry( - provider=provider, - spec=spec, - fetched_at=datetime.now(), - etag=etag, - last_modified=last_modified, - source_url=source_url, - ) - - # Save spec - cache_path = self._cache_path(provider) - async with aiofiles.open(cache_path, "w") as f: - await f.write(json.dumps(spec, indent=2)) - - # Save metadata - meta_path = self._meta_path(provider) - meta = { - "etag": etag, - "last_modified": last_modified, - "source_url": source_url, - "fetched_at": entry.fetched_at.isoformat(), - } - async with aiofiles.open(meta_path, "w") as f: - await f.write(json.dumps(meta, indent=2)) - - self._memory_cache[provider] = entry - return entry - - def invalidate(self, provider: str) -> None: - """Remove cached spec for provider.""" - if provider in self._memory_cache: - del self._memory_cache[provider] - - cache_path = self._cache_path(provider) - meta_path = self._meta_path(provider) - - if cache_path.exists(): - cache_path.unlink() - if meta_path.exists(): - meta_path.unlink() - - -class OpenAPIClient: - """Client for making API calls based on OpenAPI specs.""" - - def __init__( - self, - provider: str, - base_url: str | None = None, - credential_manager: CredentialManager | None = None, - spec_cache: SpecCache | None = None, - ): - """Initialize OpenAPI client. - - Args: - provider: Provider name (e.g., 'cloudflare') - base_url: Override base URL from spec - credential_manager: Credential manager instance - spec_cache: Spec cache instance - """ - self.provider = provider - self._base_url_override = base_url - self._spec: dict | None = None - self._operations: dict[str, Operation] = {} - self._parsed = False - - # Dependencies - self.cred_manager = credential_manager or get_credential_manager() - self.spec_cache = spec_cache or SpecCache() - self.config = get_provider_config(provider) - - # HTTP client - self._client: httpx.AsyncClient | None = None - - @property - def base_url(self) -> str: - """Get the base URL for API calls.""" - if self._base_url_override: - return self._base_url_override - if self._spec: - servers = self._spec.get("servers", []) - if servers: - return servers[0].get("url", "") - if self.config: - return self.config.base_url - return "" - - @property - def spec_loaded(self) -> bool: - """Check if spec is loaded.""" - return self._spec is not None - - async def _get_client(self) -> httpx.AsyncClient: - """Get or create HTTP client.""" - if self._client is None: - self._client = httpx.AsyncClient(timeout=30.0, follow_redirects=True) - return self._client - - async def close(self) -> None: - """Close HTTP client.""" - if self._client: - await self._client.aclose() - self._client = None - - # ========================================================================= - # Spec Management - # ========================================================================= - - def has_spec(self) -> bool: - """Check if spec is available (loaded or cached).""" - return self._spec is not None or self.spec_cache.has_spec(self.provider) - - def spec_age(self) -> float | None: - """Get age of spec in seconds.""" - return self.spec_cache.spec_age(self.provider) - - async def refresh_spec(self, force: bool = False) -> bool: - """Refresh the OpenAPI spec. - - Args: - force: Force refresh even if not stale - - Returns: - True if spec was refreshed, False if using cache - """ - # Check if refresh needed - if not force and not self.spec_cache.is_stale(self.provider): - cached = await self.spec_cache.get(self.provider) - if cached: - self._spec = cached.spec - self._parse_spec() - return False - - # Get spec URL - spec_url = self.config.spec_url if self.config else None - if not spec_url: - # Try to load from cache - cached = await self.spec_cache.get(self.provider) - if cached: - self._spec = cached.spec - self._parse_spec() - return False - return False - - # Fetch with conditional request - client = await self._get_client() - headers = {} - - cached = await self.spec_cache.get(self.provider) - if cached and not force: - if cached.etag: - headers["If-None-Match"] = cached.etag - if cached.last_modified: - headers["If-Modified-Since"] = cached.last_modified - - try: - response = await client.get(spec_url, headers=headers) - - if response.status_code == 304: - # Not modified, use cache - if cached: - self._spec = cached.spec - self._parse_spec() - return False - - response.raise_for_status() - - # Parse spec - content = response.text - if spec_url.endswith((".yaml", ".yml")): - spec = yaml.safe_load(content) - else: - spec = json.loads(content) - - # Cache it - await self.spec_cache.set( - self.provider, - spec, - etag=response.headers.get("etag"), - last_modified=response.headers.get("last-modified"), - source_url=spec_url, - ) - - self._spec = spec - self._parsed = False - self._parse_spec() - return True - - except Exception as e: - logger.warning(f"Failed to fetch spec for {self.provider}: {e}") - # Fall back to cache - if cached: - self._spec = cached.spec - self._parse_spec() - raise NetworkError( - message=f"Failed to fetch OpenAPI spec: {e}", - provider=self.provider, - url=spec_url, - original_error=e, - ) from e - - async def load_spec(self, spec_source: str | None = None) -> None: - """Load OpenAPI spec from various sources. - - Args: - spec_source: URL or file path to spec. If None, uses provider config. - """ - # If already loaded - if self._spec: - return - - # Try cache first - cached = await self.spec_cache.get(self.provider) - if cached and not self.spec_cache.is_stale(self.provider): - self._spec = cached.spec - self._parse_spec() - return - - # Load from source - if spec_source: - if spec_source.startswith(("http://", "https://")): - await self._load_spec_from_url(spec_source) - else: - await self._load_spec_from_file(spec_source) - elif self.config and self.config.spec_url: - await self.refresh_spec(force=True) - elif cached: - # Use stale cache if no other option - self._spec = cached.spec - self._parse_spec() - - async def _load_spec_from_url(self, url: str) -> None: - """Load spec from URL.""" - client = await self._get_client() - try: - response = await client.get(url) - response.raise_for_status() - - content = response.text - if url.endswith((".yaml", ".yml")): - spec = yaml.safe_load(content) - else: - spec = json.loads(content) - - await self.spec_cache.set( - self.provider, - spec, - etag=response.headers.get("etag"), - last_modified=response.headers.get("last-modified"), - source_url=url, - ) - - self._spec = spec - self._parse_spec() - - except Exception as e: - raise NetworkError( - message=f"Failed to load spec from {url}: {e}", - provider=self.provider, - url=url, - original_error=e, - ) from e - - async def _load_spec_from_file(self, path: str) -> None: - """Load spec from local file.""" - try: - async with aiofiles.open(path) as f: - content = await f.read() - - if path.endswith((".yaml", ".yml")): - spec = yaml.safe_load(content) - else: - spec = json.loads(content) - - await self.spec_cache.set(self.provider, spec, source_url=f"file://{path}") - self._spec = spec - self._parse_spec() - - except Exception as e: - raise SpecParseError( - message=f"Failed to load spec from {path}: {e}", - provider=self.provider, - ) from e - - def set_spec(self, spec: dict) -> None: - """Set spec directly (for testing or inline specs).""" - self._spec = spec - self._parsed = False - self._parse_spec() - - # ========================================================================= - # Spec Parsing - # ========================================================================= - - def _parse_spec(self) -> None: - """Parse OpenAPI spec to extract operations.""" - if self._parsed or not self._spec: - return - - self._operations.clear() - paths = self._spec.get("paths", {}) - seen_ids: set[str] = set() - - for path, path_item in paths.items(): - if not isinstance(path_item, dict): - continue - - # Handle $ref at path level - if "$ref" in path_item: - resolved = self._resolve_ref(path_item["$ref"]) - if resolved: - path_item = resolved - else: - continue - - # Common parameters for all methods in this path - common_params = path_item.get("parameters", []) - - for method in ["get", "post", "put", "patch", "delete", "head", "options"]: - if method not in path_item: - continue - - op_data = path_item[method] - if not isinstance(op_data, dict): - continue - - try: - operation = self._parse_operation( - path=path, - method=method, - op_data=op_data, - common_params=common_params, - seen_ids=seen_ids, - ) - if operation: - self._operations[operation.operation_id] = operation - seen_ids.add(operation.operation_id) - - except Exception as e: - logger.warning(f"Failed to parse operation {method} {path}: {e}") - - self._parsed = True - logger.debug(f"Parsed {len(self._operations)} operations for {self.provider}") - - def _parse_operation( - self, - path: str, - method: str, - op_data: dict, - common_params: list, - seen_ids: set[str], - ) -> Operation | None: - """Parse a single operation.""" - # Generate unique operation ID - op_id = op_data.get("operationId") - - if not op_id: - # Generate deterministic ID from path and method - clean_path = re.sub(r"[{}]", "", path) - clean_path = re.sub(r"[^a-zA-Z0-9/]", "", clean_path) - clean_path = clean_path.replace("/", "_").strip("_") - op_id = f"{method}_{clean_path}" if clean_path else f"{method}_root" - - # Ensure uniqueness - base_id = op_id - counter = 1 - while op_id in seen_ids: - op_id = f"{base_id}_{counter}" - counter += 1 - - # Parse parameters - params = [] - all_params = common_params + op_data.get("parameters", []) - - for param_data in all_params: - if "$ref" in param_data: - param_data = self._resolve_ref(param_data["$ref"]) - if not param_data: - continue - - schema = param_data.get("schema", {}) - try: - location = ParameterLocation(param_data.get("in", "query")) - except ValueError: - location = ParameterLocation.QUERY - - params.append( - Parameter( - name=param_data.get("name", ""), - location=location, - required=param_data.get("required", False), - schema_type=schema.get("type", "string"), - description=param_data.get("description", ""), - default=schema.get("default"), - enum=schema.get("enum", []), - json_schema=schema if schema else None, - ) - ) - - # Parse request body - request_body = op_data.get("requestBody", {}) - body_schema = None - body_required = request_body.get("required", False) - - if request_body: - content = request_body.get("content", {}) - # Prefer JSON - json_content = content.get("application/json") or content.get( - "application/json; charset=utf-8" - ) - if json_content: - body_schema = json_content.get("schema") - if body_schema and "$ref" in body_schema: - body_schema = self._resolve_ref(body_schema["$ref"]) - - # Parse response schema (200 response) - response_schema = None - responses = op_data.get("responses", {}) - for status in ["200", "201", "default"]: - if status in responses: - resp = responses[status] - if "$ref" in resp: - resp = self._resolve_ref(resp["$ref"]) - if resp: - resp_content = resp.get("content", {}) - json_resp = resp_content.get("application/json", {}) - response_schema = json_resp.get("schema") - if response_schema and "$ref" in response_schema: - response_schema = self._resolve_ref(response_schema["$ref"]) - break - - return Operation( - operation_id=op_id, - method=method.upper(), - path=path, - summary=op_data.get("summary", ""), - description=op_data.get("description", ""), - parameters=params, - request_body_schema=body_schema, - request_body_required=body_required, - response_schema=response_schema, - tags=op_data.get("tags", []), - deprecated=op_data.get("deprecated", False), - ) - - def _resolve_ref(self, ref: str) -> dict | None: - """Resolve a JSON reference in the spec.""" - if not ref.startswith("#/"): - return None - - parts = ref[2:].split("/") - current = self._spec - - for part in parts: - # Handle URL-encoded parts - part = part.replace("~1", "/").replace("~0", "~") - if isinstance(current, dict) and part in current: - current = current[part] - else: - return None - - return current if isinstance(current, dict) else None - - # ========================================================================= - # Operation Discovery - # ========================================================================= - - def list_operations( - self, - search: str | None = None, - tag: str | None = None, - method: str | None = None, - path_contains: str | None = None, - operation_id_prefix: str | None = None, - include_deprecated: bool = False, - ) -> OperationListResult: - """List available operations with filtering. - - Args: - search: Search in operation ID, summary, description - tag: Filter by tag - method: Filter by HTTP method - path_contains: Filter by path substring - operation_id_prefix: Filter by operation ID prefix - include_deprecated: Include deprecated operations - - Returns: - OperationListResult with matching operations - """ - # Ensure spec is parsed if we have it - if self._spec and not self._operations: - logger.debug( - f"Spec loaded but operations empty, re-parsing for {self.provider}" - ) - self._parsed = False - self._parse_spec() - - ops = list(self._operations.values()) - - # Apply filters - if tag: - tag_lower = tag.lower() - ops = [op for op in ops if any(tag_lower in t.lower() for t in op.tags)] - - if method: - method_upper = method.upper() - ops = [op for op in ops if op.method == method_upper] - - if path_contains: - ops = [op for op in ops if path_contains in op.path] - - if operation_id_prefix: - ops = [op for op in ops if op.operation_id.startswith(operation_id_prefix)] - - if not include_deprecated: - ops = [op for op in ops if not op.deprecated] - - if search: - search_lower = search.lower() - ops = [ - op - for op in ops - if search_lower in op.operation_id.lower() - or search_lower in op.summary.lower() - or search_lower in op.description.lower() - or search_lower in op.path.lower() - ] - - # Convert to summaries - summaries = [OperationSummary.from_operation(op) for op in ops] - - filter_desc = None - filters = [] - if search: - filters.append(f"search={search}") - if tag: - filters.append(f"tag={tag}") - if method: - filters.append(f"method={method}") - if path_contains: - filters.append(f"path_contains={path_contains}") - if filters: - filter_desc = ", ".join(filters) - - return OperationListResult( - provider=self.provider, - operations=summaries, - total_count=len(summaries), - filter_applied=filter_desc, - ) - - def get_operation(self, operation_id: str) -> Operation: - """Get a specific operation by ID. - - Args: - operation_id: Operation ID - - Returns: - Operation object - - Raises: - OperationNotFoundError: If operation not found - """ - op = self._operations.get(operation_id) - if op: - return op - - # Find similar operations - similar = self._find_similar_operations(operation_id) - raise OperationNotFoundError( - operation_id=operation_id, - provider=self.provider, - similar_operations=similar, - ) - - def _find_similar_operations(self, operation_id: str, limit: int = 5) -> list[str]: - """Find operations with similar IDs.""" - similarities = [] - for op_id in self._operations: - ratio = SequenceMatcher(None, operation_id.lower(), op_id.lower()).ratio() - similarities.append((op_id, ratio)) - - similarities.sort(key=lambda x: x[1], reverse=True) - return [op_id for op_id, _ in similarities[:limit] if _ > 0.3] - - # ========================================================================= - # API Calls - # ========================================================================= - - def _build_auth_headers(self, credential: Credential) -> dict[str, str]: - """Build authentication headers based on provider config.""" - headers = {} - - if not credential.api_key: - return headers - - config = self.config - - if config: - auth_type = config.auth_type - - if auth_type.value == "bearer": - prefix = config.auth_prefix or "Bearer" - headers["Authorization"] = f"{prefix} {credential.api_key}".strip() - - elif auth_type.value == "basic": - import base64 - - secret = credential.api_secret or "" - creds = base64.b64encode( - f"{credential.api_key}:{secret}".encode() - ).decode() - headers["Authorization"] = f"Basic {creds}" - - elif auth_type.value == "header": - header_name = config.auth_header or "X-API-Key" - prefix = config.auth_prefix or "" - headers[header_name] = f"{prefix}{credential.api_key}".strip() - - elif auth_type.value == "api_key": - headers["X-API-Key"] = credential.api_key - - # Add extra headers from config - if config.extra_headers: - headers.update(config.extra_headers) - - else: - # Default to bearer token - headers["Authorization"] = f"Bearer {credential.api_key}" - - return headers - - def _substitute_path_params( - self, path: str, params: dict[str, Any] - ) -> tuple[str, dict[str, Any]]: - """Substitute path parameters and return remaining params.""" - remaining = dict(params) - path_params = re.findall(r"\{(\w+)\}", path) - - for param in path_params: - if param in remaining: - path = path.replace(f"{{{param}}}", str(remaining.pop(param))) - - return path, remaining - - async def call( - self, - operation_id: str, - params: dict[str, Any] | None = None, - body: dict[str, Any] | None = None, - headers: dict[str, str] | None = None, - dry_run: bool = False, - ) -> APICallResult: - """Call an API operation. - - Args: - operation_id: Operation to call - params: Path, query, and header parameters - body: Request body (for POST/PUT/PATCH) - headers: Additional headers - dry_run: If True, don't actually make the request - - Returns: - APICallResult with response data - """ - if not self._spec: - raise SpecNotLoadedError(self.provider) - - operation = self.get_operation(operation_id) - - # Validate required parameters - params = params or {} - self._validate_params(operation, params, body) - - # Get credentials - credential = await self.cred_manager.require_credential(self.provider) - - # Build request - request_headers = self._build_auth_headers(credential) - request_headers["Content-Type"] = "application/json" - request_headers["Accept"] = "application/json" - if headers: - request_headers.update(headers) - - # Process path parameters - path, remaining_params = self._substitute_path_params(operation.path, params) - - # Separate query params - query_params = {} - for param in operation.parameters: - if param.name in remaining_params: - if param.location == ParameterLocation.QUERY: - query_params[param.name] = remaining_params[param.name] - elif param.location == ParameterLocation.HEADER: - request_headers[param.name] = str(remaining_params[param.name]) - - # Build URL - url = urljoin(self.base_url.rstrip("/") + "/", path.lstrip("/")) - if query_params: - url = f"{url}?{urlencode(query_params)}" - - if dry_run: - return APICallResult( - success=True, - status_code=0, - headers={}, - body={"dry_run": True, "url": url, "method": operation.method}, - provider=self.provider, - operation_id=operation_id, - method=operation.method, - url=url, - ) - - # Make request - return await self._make_request( - method=operation.method, - url=url, - headers=request_headers, - body=body, - operation_id=operation_id, - ) - - async def call_raw( - self, - method: str, - path: str, - params: dict[str, Any] | None = None, - body: dict[str, Any] | None = None, - headers: dict[str, str] | None = None, - dry_run: bool = False, - ) -> APICallResult: - """Make a raw API call without using operation definitions. - - Args: - method: HTTP method - path: URL path - params: Query parameters - body: Request body - headers: Additional headers - dry_run: If True, don't actually make the request - - Returns: - APICallResult with response data - """ - credential = await self.cred_manager.require_credential(self.provider) - - # Build headers - request_headers = self._build_auth_headers(credential) - request_headers["Content-Type"] = "application/json" - request_headers["Accept"] = "application/json" - if headers: - request_headers.update(headers) - - # Build URL - url = urljoin(self.base_url.rstrip("/") + "/", path.lstrip("/")) - if params: - url = f"{url}?{urlencode(params)}" - - if dry_run: - return APICallResult( - success=True, - status_code=0, - headers={}, - body={"dry_run": True, "url": url, "method": method}, - provider=self.provider, - method=method, - url=url, - ) - - return await self._make_request( - method=method, - url=url, - headers=request_headers, - body=body, - ) - - async def _make_request( - self, - method: str, - url: str, - headers: dict[str, str], - body: dict[str, Any] | None = None, - operation_id: str | None = None, - ) -> APICallResult: - """Make the actual HTTP request.""" - client = await self._get_client() - method_lower = method.lower() - - start_time = time.time() - - try: - request_kwargs: dict[str, Any] = {"headers": headers} - if body and method_lower in ("post", "put", "patch"): - request_kwargs["json"] = body - - response = await getattr(client, method_lower)(url, **request_kwargs) - elapsed_ms = (time.time() - start_time) * 1000 - - # Parse response - try: - response_body = response.json() - except json.JSONDecodeError: - response_body = response.text - - result = APICallResult( - success=200 <= response.status_code < 300, - status_code=response.status_code, - headers=dict(response.headers), - body=response_body, - raw_body=response.text, - elapsed_ms=elapsed_ms, - request_id=response.headers.get("x-request-id"), - provider=self.provider, - operation_id=operation_id, - method=method, - url=url, - ) - - # Handle common error statuses - if response.status_code == 401: - raise AuthenticationError( - provider=self.provider, - status_code=401, - env_vars=self.config.env_vars if self.config else [], - ) - elif response.status_code == 429: - retry_after = response.headers.get("retry-after") - raise RateLimitError( - provider=self.provider, - retry_after=int(retry_after) if retry_after else None, - ) - - return result - - except httpx.RequestError as e: - raise NetworkError( - message=f"Request failed: {e}", - provider=self.provider, - url=url, - original_error=e, - ) from e - - def _validate_params( - self, - operation: Operation, - params: dict[str, Any], - body: dict[str, Any] | None, - ) -> None: - """Validate parameters against operation schema.""" - missing = [] - - # Check required parameters - for param in operation.parameters: - if param.required and param.name not in params: - # Check if it has a default - if param.default is None: - missing.append(param.name) - - # Check required body - if operation.request_body_required and not body: - missing.append("request_body") - - if missing: - raise ParameterValidationError( - message=f"Missing required parameters: {', '.join(missing)}", - provider=self.provider, - operation_id=operation.operation_id, - missing_params=missing, - ) - - -# ============================================================================= -# Module-level helpers -# ============================================================================= - -_clients: dict[str, OpenAPIClient] = {} - - -async def get_client(provider: str, spec_url: str | None = None) -> OpenAPIClient: - """Get or create an OpenAPI client for a provider. - - Args: - provider: Provider name - spec_url: Optional URL to OpenAPI spec - - Returns: - OpenAPIClient instance with spec loaded - """ - if provider not in _clients: - client = OpenAPIClient(provider) - await client.load_spec(spec_url) - _clients[provider] = client - return _clients[provider] - - -def clear_clients() -> None: - """Clear cached clients (for testing).""" - _clients.clear() diff --git a/pkg/hanzo-tools-api/hanzo_tools/api/providers.py b/pkg/hanzo-tools-api/hanzo_tools/api/providers.py deleted file mode 100644 index f883e4730..000000000 --- a/pkg/hanzo-tools-api/hanzo_tools/api/providers.py +++ /dev/null @@ -1,484 +0,0 @@ -"""Provider configurations and environment variable mappings. - -Contains built-in configurations for 30+ cloud providers, -plus 1100+ auto-generated configs from APIs.guru + oapis.org. -""" - -from __future__ import annotations - -from .apis_guru_providers import APIS_GURU_PROVIDERS -from .models import AuthType, ProviderConfig - -# ============================================================================= -# Environment Variable Mappings -# ============================================================================= - -ENV_VAR_MAPPINGS: dict[str, list[str]] = { - # Cloud Providers - "cloudflare": [ - "CLOUDFLARE_API_TOKEN", - "CF_API_TOKEN", - "CLOUDFLARE_API_KEY", - "CF_API_KEY", - ], - "aws": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"], - "gcp": ["GOOGLE_APPLICATION_CREDENTIALS", "GCP_API_KEY", "GOOGLE_API_KEY"], - "azure": ["AZURE_API_KEY", "AZURE_SUBSCRIPTION_KEY"], - "digitalocean": ["DIGITALOCEAN_TOKEN", "DO_TOKEN", "DIGITALOCEAN_ACCESS_TOKEN"], - "linode": ["LINODE_TOKEN", "LINODE_API_TOKEN"], - "vultr": ["VULTR_API_KEY"], - "hetzner": ["HETZNER_API_TOKEN", "HCLOUD_TOKEN"], - # AI Providers - "openai": ["OPENAI_API_KEY"], - "anthropic": ["ANTHROPIC_API_KEY"], - "together": ["TOGETHER_API_KEY", "TOGETHER_AI_KEY"], - "replicate": ["REPLICATE_API_TOKEN", "REPLICATE_API_KEY"], - "huggingface": ["HF_TOKEN", "HUGGINGFACE_TOKEN", "HUGGING_FACE_HUB_TOKEN"], - "cohere": ["COHERE_API_KEY", "CO_API_KEY"], - "perplexity": ["PERPLEXITY_API_KEY", "PPLX_API_KEY"], - "groq": ["GROQ_API_KEY"], - "mistral": ["MISTRAL_API_KEY"], - "fireworks": ["FIREWORKS_API_KEY"], - # Developer Platforms - "github": ["GITHUB_TOKEN", "GH_TOKEN", "GITHUB_API_TOKEN"], - "gitlab": ["GITLAB_TOKEN", "GITLAB_API_TOKEN"], - "bitbucket": ["BITBUCKET_TOKEN", "BITBUCKET_API_TOKEN"], - # Deployment Platforms - "vercel": ["VERCEL_TOKEN", "VERCEL_API_TOKEN"], - "netlify": ["NETLIFY_AUTH_TOKEN", "NETLIFY_TOKEN"], - "fly": ["FLY_API_TOKEN", "FLY_ACCESS_TOKEN"], - "railway": ["RAILWAY_TOKEN", "RAILWAY_API_TOKEN"], - "render": ["RENDER_API_KEY", "RENDER_TOKEN"], - "heroku": ["HEROKU_API_KEY", "HEROKU_TOKEN"], - # Payment/Commerce - "stripe": ["STRIPE_API_KEY", "STRIPE_SECRET_KEY"], - "shopify": ["SHOPIFY_API_KEY", "SHOPIFY_ACCESS_TOKEN"], - "paypal": ["PAYPAL_CLIENT_ID", "PAYPAL_CLIENT_SECRET"], - # Communication - "twilio": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - "sendgrid": ["SENDGRID_API_KEY"], - "resend": ["RESEND_API_KEY"], - "postmark": ["POSTMARK_API_TOKEN", "POSTMARK_SERVER_TOKEN"], - "mailgun": ["MAILGUN_API_KEY"], - "slack": ["SLACK_TOKEN", "SLACK_BOT_TOKEN", "SLACK_API_TOKEN"], - "discord": ["DISCORD_TOKEN", "DISCORD_BOT_TOKEN"], - # Databases/Backend - "supabase": ["SUPABASE_API_KEY", "SUPABASE_SERVICE_KEY"], - "planetscale": ["PLANETSCALE_TOKEN", "PSCALE_TOKEN"], - "neon": ["NEON_API_KEY"], - "upstash": ["UPSTASH_REDIS_REST_TOKEN", "UPSTASH_API_KEY"], - "mongodb": ["MONGODB_API_KEY", "ATLAS_API_KEY"], - "redis": ["REDIS_PASSWORD", "REDIS_API_KEY"], - "fauna": ["FAUNA_SECRET", "FAUNA_KEY"], - # Search/Analytics - "algolia": ["ALGOLIA_API_KEY", "ALGOLIA_ADMIN_KEY"], - "elasticsearch": ["ELASTIC_API_KEY", "ELASTICSEARCH_API_KEY"], - "meilisearch": ["MEILI_MASTER_KEY", "MEILISEARCH_API_KEY"], - "typesense": ["TYPESENSE_API_KEY"], - # Monitoring/Observability - "datadog": ["DD_API_KEY", "DATADOG_API_KEY"], - "newrelic": ["NEW_RELIC_API_KEY", "NEWRELIC_API_KEY"], - "sentry": ["SENTRY_AUTH_TOKEN", "SENTRY_DSN"], - "grafana": ["GRAFANA_API_KEY", "GF_SECURITY_ADMIN_TOKEN"], - # Storage/CDN - "cloudinary": ["CLOUDINARY_API_KEY", "CLOUDINARY_API_SECRET"], - "imgix": ["IMGIX_API_KEY"], - "bunny": ["BUNNY_API_KEY", "BUNNY_ACCESS_KEY"], - "backblaze": ["B2_APPLICATION_KEY_ID", "B2_APPLICATION_KEY"], - # Hanzo Services - "hanzo": ["HANZO_API_KEY", "HANZO_TOKEN"], - "hanzo-iam": ["HANZO_API_KEY", "HANZO_TOKEN"], - "hanzo-gateway": ["HANZO_API_KEY", "HANZO_TOKEN"], - "hanzo-commerce": ["HANZO_API_KEY", "HANZO_TOKEN"], - "hanzo-vector": ["HANZO_API_KEY", "HANZO_TOKEN"], - "hanzo-cloud": ["HANZO_API_KEY", "HANZO_TOKEN"], - "hanzo-nexus": ["HANZO_API_KEY", "HANZO_TOKEN"], -} - - -# ============================================================================= -# Provider Configurations -# ============================================================================= - -PROVIDER_CONFIGS: dict[str, ProviderConfig] = { - # Cloud Providers - "cloudflare": ProviderConfig( - name="cloudflare", - display_name="Cloudflare", - base_url="https://api.cloudflare.com/client/v4", - auth_type=AuthType.BEARER, - spec_url="https://raw.githubusercontent.com/cloudflare/api-schemas/main/openapi.json", - env_vars=[ - "CLOUDFLARE_API_TOKEN", - "CF_API_TOKEN", - "CLOUDFLARE_API_KEY", - "CF_API_KEY", - ], - ), - "digitalocean": ProviderConfig( - name="digitalocean", - display_name="DigitalOcean", - base_url="https://api.digitalocean.com/v2", - auth_type=AuthType.BEARER, - spec_url="https://api-engineering.nyc3.cdn.digitaloceanspaces.com/spec-ci/DigitalOcean-public.v2.yaml", - env_vars=["DIGITALOCEAN_TOKEN", "DO_TOKEN"], - ), - "hetzner": ProviderConfig( - name="hetzner", - display_name="Hetzner Cloud", - base_url="https://api.hetzner.cloud/v1", - auth_type=AuthType.BEARER, - env_vars=["HETZNER_API_TOKEN", "HCLOUD_TOKEN"], - ), - # AI Providers - "openai": ProviderConfig( - name="openai", - display_name="OpenAI", - base_url="https://api.openai.com/v1", - auth_type=AuthType.BEARER, - spec_url="https://raw.githubusercontent.com/openai/openai-openapi/refs/heads/manual_spec/openapi.yaml", - env_vars=["OPENAI_API_KEY"], - ), - "anthropic": ProviderConfig( - name="anthropic", - display_name="Anthropic", - base_url="https://api.anthropic.com/v1", - auth_type=AuthType.HEADER, - auth_header="x-api-key", - auth_prefix="", - env_vars=["ANTHROPIC_API_KEY"], - extra_headers={"anthropic-version": "2023-06-01"}, - ), - "together": ProviderConfig( - name="together", - display_name="Together AI", - base_url="https://api.together.xyz/v1", - auth_type=AuthType.BEARER, - env_vars=["TOGETHER_API_KEY"], - ), - "groq": ProviderConfig( - name="groq", - display_name="Groq", - base_url="https://api.groq.com/openai/v1", - auth_type=AuthType.BEARER, - spec_url="https://raw.githubusercontent.com/janwilmake/handmade-openapis/main/groq.json", - env_vars=["GROQ_API_KEY"], - ), - "mistral": ProviderConfig( - name="mistral", - display_name="Mistral AI", - base_url="https://api.mistral.ai/v1", - auth_type=AuthType.BEARER, - env_vars=["MISTRAL_API_KEY"], - ), - # Developer Platforms - "github": ProviderConfig( - name="github", - display_name="GitHub", - base_url="https://api.github.com", - auth_type=AuthType.BEARER, - spec_url="https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json", - env_vars=["GITHUB_TOKEN", "GH_TOKEN"], - extra_headers={ - "Accept": "application/vnd.github+json", - "X-GitHub-Api-Version": "2022-11-28", - }, - ), - "gitlab": ProviderConfig( - name="gitlab", - display_name="GitLab", - base_url="https://gitlab.com/api/v4", - auth_type=AuthType.HEADER, - auth_header="PRIVATE-TOKEN", - auth_prefix="", - env_vars=["GITLAB_TOKEN"], - ), - # Deployment Platforms - "vercel": ProviderConfig( - name="vercel", - display_name="Vercel", - base_url="https://api.vercel.com", - auth_type=AuthType.BEARER, - env_vars=["VERCEL_TOKEN"], - ), - "netlify": ProviderConfig( - name="netlify", - display_name="Netlify", - base_url="https://api.netlify.com/api/v1", - auth_type=AuthType.BEARER, - env_vars=["NETLIFY_AUTH_TOKEN"], - ), - "fly": ProviderConfig( - name="fly", - display_name="Fly.io", - base_url="https://api.fly.io/v1", - auth_type=AuthType.BEARER, - env_vars=["FLY_API_TOKEN"], - ), - "railway": ProviderConfig( - name="railway", - display_name="Railway", - base_url="https://backboard.railway.app/graphql/v2", - auth_type=AuthType.BEARER, - env_vars=["RAILWAY_TOKEN"], - ), - "render": ProviderConfig( - name="render", - display_name="Render", - base_url="https://api.render.com/v1", - auth_type=AuthType.BEARER, - env_vars=["RENDER_API_KEY"], - ), - # Payment - "stripe": ProviderConfig( - name="stripe", - display_name="Stripe", - base_url="https://api.stripe.com/v1", - auth_type=AuthType.BASIC, - spec_url="https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json", - env_vars=["STRIPE_API_KEY", "STRIPE_SECRET_KEY"], - ), - # Communication - "sendgrid": ProviderConfig( - name="sendgrid", - display_name="SendGrid", - base_url="https://api.sendgrid.com/v3", - auth_type=AuthType.BEARER, - env_vars=["SENDGRID_API_KEY"], - ), - "resend": ProviderConfig( - name="resend", - display_name="Resend", - base_url="https://api.resend.com", - auth_type=AuthType.BEARER, - env_vars=["RESEND_API_KEY"], - ), - "slack": ProviderConfig( - name="slack", - display_name="Slack", - base_url="https://slack.com/api", - auth_type=AuthType.BEARER, - env_vars=["SLACK_TOKEN", "SLACK_BOT_TOKEN"], - ), - # Databases - "supabase": ProviderConfig( - name="supabase", - display_name="Supabase", - base_url="https://api.supabase.com/v1", - auth_type=AuthType.BEARER, - env_vars=["SUPABASE_API_KEY"], - ), - "neon": ProviderConfig( - name="neon", - display_name="Neon", - base_url="https://console.neon.tech/api/v2", - auth_type=AuthType.BEARER, - env_vars=["NEON_API_KEY"], - ), - "planetscale": ProviderConfig( - name="planetscale", - display_name="PlanetScale", - base_url="https://api.planetscale.com/v1", - auth_type=AuthType.BEARER, - env_vars=["PLANETSCALE_TOKEN"], - ), - # Monitoring - "datadog": ProviderConfig( - name="datadog", - display_name="Datadog", - base_url="https://api.datadoghq.com/api/v1", - auth_type=AuthType.HEADER, - auth_header="DD-API-KEY", - auth_prefix="", - env_vars=["DD_API_KEY", "DATADOG_API_KEY"], - ), - "sentry": ProviderConfig( - name="sentry", - display_name="Sentry", - base_url="https://sentry.io/api/0", - auth_type=AuthType.BEARER, - env_vars=["SENTRY_AUTH_TOKEN"], - ), - # Search - "algolia": ProviderConfig( - name="algolia", - display_name="Algolia", - base_url="https://api.algolia.com", - auth_type=AuthType.HEADER, - auth_header="X-Algolia-API-Key", - auth_prefix="", - env_vars=["ALGOLIA_API_KEY"], - ), - # Hanzo Services - "hanzo": ProviderConfig( - name="hanzo", - display_name="Hanzo AI", - base_url="https://api.hanzo.ai/v1", - auth_type=AuthType.BEARER, - spec_url="file:///Users/z/work/hanzo/openapi/hanzo.yaml", - env_vars=["HANZO_API_KEY", "HANZO_TOKEN"], - ), - "hanzo-iam": ProviderConfig( - name="hanzo-iam", - display_name="Hanzo IAM", - base_url="https://hanzo.id", - auth_type=AuthType.BEARER, - spec_url="file:///Users/z/work/hanzo/openapi/iam/openapi.yaml", - env_vars=["HANZO_API_KEY", "HANZO_TOKEN"], - ), - "hanzo-gateway": ProviderConfig( - name="hanzo-gateway", - display_name="Hanzo Gateway", - base_url="https://gateway.hanzo.ai", - auth_type=AuthType.BEARER, - spec_url="file:///Users/z/work/hanzo/openapi/gateway/openapi.yaml", - env_vars=["HANZO_API_KEY", "HANZO_TOKEN"], - ), - "hanzo-commerce": ProviderConfig( - name="hanzo-commerce", - display_name="Hanzo Commerce", - base_url="https://api.hanzo.ai/v1", - auth_type=AuthType.BEARER, - spec_url="file:///Users/z/work/hanzo/openapi/commerce/openapi.yaml", - env_vars=["HANZO_API_KEY", "HANZO_TOKEN"], - ), - "hanzo-vector": ProviderConfig( - name="hanzo-vector", - display_name="Hanzo Vector", - base_url="https://vector.hanzo.ai", - auth_type=AuthType.BEARER, - spec_url="file:///Users/z/work/hanzo/openapi/vector/openapi.yaml", - env_vars=["HANZO_API_KEY", "HANZO_TOKEN"], - ), - "hanzo-cloud": ProviderConfig( - name="hanzo-cloud", - display_name="Hanzo Cloud", - base_url="https://cloud.hanzo.ai", - auth_type=AuthType.BEARER, - spec_url="file:///Users/z/work/hanzo/openapi/cloud/openapi.yaml", - env_vars=["HANZO_API_KEY", "HANZO_TOKEN"], - ), - "hanzo-nexus": ProviderConfig( - name="hanzo-nexus", - display_name="Hanzo Nexus", - base_url="https://nexus.hanzo.ai", - auth_type=AuthType.BEARER, - spec_url="file:///Users/z/work/hanzo/openapi/nexus/openapi.yaml", - env_vars=["HANZO_API_KEY", "HANZO_TOKEN"], - ), - # Popular APIs from handmade-openapis - "notion": ProviderConfig( - name="notion", - display_name="Notion", - base_url="https://api.notion.com/v1", - auth_type=AuthType.BEARER, - spec_url="https://raw.githubusercontent.com/janwilmake/handmade-openapis/main/notion.json", - env_vars=["NOTION_API_KEY", "NOTION_TOKEN"], - extra_headers={"Notion-Version": "2022-06-28"}, - ), - "hackernews": ProviderConfig( - name="hackernews", - display_name="Hacker News", - base_url="https://hacker-news.firebaseio.com/v0", - auth_type=AuthType.BEARER, # No auth needed but required by system - spec_url="https://raw.githubusercontent.com/janwilmake/handmade-openapis/main/hackernews.json", - env_vars=[], - ), - "serper": ProviderConfig( - name="serper", - display_name="Serper (Google Search)", - base_url="https://google.serper.dev", - auth_type=AuthType.HEADER, - auth_header="X-API-KEY", - auth_prefix="", - spec_url="https://raw.githubusercontent.com/janwilmake/handmade-openapis/main/serper.json", - env_vars=["SERPER_API_KEY"], - ), - "jina": ProviderConfig( - name="jina", - display_name="Jina Reader", - base_url="https://r.jina.ai", - auth_type=AuthType.BEARER, - spec_url="https://raw.githubusercontent.com/janwilmake/handmade-openapis/main/jina-reader.json", - env_vars=["JINA_API_KEY"], - ), - "upstash-redis": ProviderConfig( - name="upstash-redis", - display_name="Upstash Redis", - base_url="https://global.upstash.io", - auth_type=AuthType.BEARER, - spec_url="https://raw.githubusercontent.com/janwilmake/handmade-openapis/main/upstash-redis.json", - env_vars=["UPSTASH_REDIS_REST_TOKEN"], - ), - "devto": ProviderConfig( - name="devto", - display_name="DEV.to", - base_url="https://dev.to/api", - auth_type=AuthType.HEADER, - auth_header="api-key", - auth_prefix="", - spec_url="https://raw.githubusercontent.com/janwilmake/handmade-openapis/main/devto.json", - env_vars=["DEV_API_KEY", "DEVTO_API_KEY"], - ), -} - - -def get_provider_config(provider: str) -> ProviderConfig | None: - """Get configuration for a provider. - - Checks built-in configs first, then falls back to APIs.guru. - """ - # Built-in configs take priority - if provider in PROVIDER_CONFIGS: - return PROVIDER_CONFIGS[provider] - - # Check APIs.guru auto-generated configs - if provider in APIS_GURU_PROVIDERS: - guru = APIS_GURU_PROVIDERS[provider] - return ProviderConfig( - name=provider, - display_name=guru.get("display_name", provider), - base_url=guru.get("base_url", ""), - auth_type=AuthType.BEARER, - spec_url=guru.get("spec_url"), - env_vars=guru.get("env_vars", [f"{provider.upper()}_API_KEY"]), - ) - - return None - - -def get_env_vars(provider: str) -> list[str]: - """Get environment variable names for a provider.""" - # Check provider config first - config = PROVIDER_CONFIGS.get(provider) - if config: - return config.env_vars - - # Check APIs.guru configs - guru = APIS_GURU_PROVIDERS.get(provider) - if guru: - return guru.get("env_vars", []) - - # Fall back to env mappings - return ENV_VAR_MAPPINGS.get(provider, []) - - -def list_providers() -> list[str]: - """List all known provider names (1100+ providers).""" - return sorted( - set(PROVIDER_CONFIGS.keys()) - | set(ENV_VAR_MAPPINGS.keys()) - | set(APIS_GURU_PROVIDERS.keys()) - ) - - -def list_providers_with_specs() -> list[str]: - """List providers that have OpenAPI spec URLs.""" - providers = [] - for name, config in PROVIDER_CONFIGS.items(): - if config.spec_url: - providers.append(name) - for name, guru in APIS_GURU_PROVIDERS.items(): - if guru.get("spec_url") and name not in providers: - providers.append(name) - return sorted(providers) diff --git a/pkg/hanzo-tools-api/hanzo_tools/api/storage.py b/pkg/hanzo-tools-api/hanzo_tools/api/storage.py deleted file mode 100644 index b2219756c..000000000 --- a/pkg/hanzo-tools-api/hanzo_tools/api/storage.py +++ /dev/null @@ -1,326 +0,0 @@ -"""Pluggable credential storage interface. - -Provides abstractions for storing and retrieving credentials, -with built-in implementations for file storage and environment variables. - -Custom implementations can integrate with KMS, 1Password, OS keychain, etc. -""" - -from __future__ import annotations - -import base64 -import json -import logging -import os -from abc import ABC, abstractmethod -from pathlib import Path - -import aiofiles - -from .models import Credential - -logger = logging.getLogger(__name__) - - -class CredentialStorage(ABC): - """Abstract interface for credential storage. - - Implement this interface to integrate custom secret stores - (e.g., AWS Secrets Manager, HashiCorp Vault, 1Password, OS keychain). - """ - - @abstractmethod - async def get(self, provider: str) -> Credential | None: - """Retrieve credential for a provider. - - Args: - provider: Provider name - - Returns: - Credential if found, None otherwise - """ - pass - - @abstractmethod - async def set(self, credential: Credential) -> None: - """Store a credential. - - Args: - credential: Credential to store - """ - pass - - @abstractmethod - async def delete(self, provider: str) -> bool: - """Delete a stored credential. - - Args: - provider: Provider name - - Returns: - True if deleted, False if not found - """ - pass - - @abstractmethod - async def list(self) -> list[str]: - """List all stored provider names. - - Returns: - List of provider names with stored credentials - """ - pass - - -class FileCredentialStorage(CredentialStorage): - """File-based credential storage with obfuscation. - - Stores credentials in a JSON file with basic base64 obfuscation - and restrictive file permissions (0600). - - Note: This is NOT encryption. For production use with sensitive - credentials, consider using a proper secrets manager. - """ - - def __init__(self, path: Path | None = None): - """Initialize file storage. - - Args: - path: Path to credentials file. Defaults to ~/.hanzo/api/credentials.json - """ - self.path = path or Path.home() / ".hanzo" / "api" / "credentials.json" - self._cache: dict[str, Credential] = {} - self._loaded = False - - def _ensure_dir(self) -> None: - """Ensure parent directory exists.""" - self.path.parent.mkdir(parents=True, exist_ok=True) - - @staticmethod - def _obfuscate(value: str) -> str: - """Basic obfuscation for at-rest storage.""" - return base64.b64encode(value.encode()).decode() - - @staticmethod - def _deobfuscate(value: str) -> str: - """Reverse obfuscation.""" - try: - return base64.b64decode(value.encode()).decode() - except Exception: - return value - - async def _load(self) -> None: - """Load credentials from file.""" - if self._loaded: - return - - self._ensure_dir() - - if self.path.exists(): - try: - async with aiofiles.open(self.path) as f: - content = await f.read() - data = json.loads(content) - - for name, cred_data in data.items(): - # Deobfuscate sensitive fields - if cred_data.get("api_key"): - cred_data["api_key"] = self._deobfuscate(cred_data["api_key"]) - if cred_data.get("api_secret"): - cred_data["api_secret"] = self._deobfuscate( - cred_data["api_secret"] - ) - self._cache[name] = Credential(**cred_data) - - except Exception as e: - logger.warning(f"Failed to load credentials: {e}") - - self._loaded = True - - async def _save(self) -> None: - """Save credentials to file.""" - self._ensure_dir() - - data = {} - for name, cred in self._cache.items(): - cred_dict = cred.model_dump() - # Obfuscate sensitive fields - if cred_dict.get("api_key"): - cred_dict["api_key"] = self._obfuscate(cred_dict["api_key"]) - if cred_dict.get("api_secret"): - cred_dict["api_secret"] = self._obfuscate(cred_dict["api_secret"]) - data[name] = cred_dict - - async with aiofiles.open(self.path, "w") as f: - await f.write(json.dumps(data, indent=2)) - - # Set restrictive permissions - self.path.chmod(0o600) - - async def get(self, provider: str) -> Credential | None: - await self._load() - return self._cache.get(provider) - - async def set(self, credential: Credential) -> None: - await self._load() - self._cache[credential.provider] = credential - await self._save() - - async def delete(self, provider: str) -> bool: - await self._load() - if provider in self._cache: - del self._cache[provider] - await self._save() - return True - return False - - async def list(self) -> list[str]: - await self._load() - return list(self._cache.keys()) - - -class EnvironmentCredentialStorage(CredentialStorage): - """Environment variable credential storage (read-only). - - Looks up credentials from environment variables based on - provider-specific mappings. - """ - - def __init__(self, env_mappings: dict[str, list[str]] | None = None): - """Initialize environment storage. - - Args: - env_mappings: Mapping of provider names to environment variable names - """ - from .providers import ENV_VAR_MAPPINGS - - self.env_mappings = env_mappings or ENV_VAR_MAPPINGS - - async def get(self, provider: str) -> Credential | None: - env_vars = self.env_mappings.get(provider, []) - - for var in env_vars: - value = os.environ.get(var) - if value: - logger.debug(f"Found credential for {provider} from env var {var}") - return Credential(provider=provider, api_key=value) - - return None - - async def set(self, credential: Credential) -> None: - """Environment storage is read-only.""" - raise NotImplementedError("Cannot write to environment storage") - - async def delete(self, provider: str) -> bool: - """Environment storage is read-only.""" - raise NotImplementedError("Cannot delete from environment storage") - - async def list(self) -> list[str]: - """List providers with credentials in environment.""" - result = [] - for provider, env_vars in self.env_mappings.items(): - for var in env_vars: - if os.environ.get(var): - result.append(provider) - break - return result - - -class MemoryCredentialStorage(CredentialStorage): - """In-memory credential storage. - - Useful for per-call overrides and testing. - Credentials are lost when the process exits. - """ - - def __init__(self): - self._credentials: dict[str, Credential] = {} - - async def get(self, provider: str) -> Credential | None: - return self._credentials.get(provider) - - async def set(self, credential: Credential) -> None: - self._credentials[credential.provider] = credential - - async def delete(self, provider: str) -> bool: - if provider in self._credentials: - del self._credentials[provider] - return True - return False - - async def list(self) -> list[str]: - return list(self._credentials.keys()) - - -class ChainedCredentialStorage(CredentialStorage): - """Chains multiple credential stores with priority. - - Checks stores in order and returns the first match. - Writes go to the first writable store. - - Default order: - 1. Memory (per-call overrides) - 2. File storage - 3. Environment variables - """ - - def __init__(self, stores: list[CredentialStorage] | None = None): - """Initialize chained storage. - - Args: - stores: List of storage backends in priority order. - Defaults to [Memory, File, Environment]. - """ - if stores is None: - stores = [ - MemoryCredentialStorage(), - FileCredentialStorage(), - EnvironmentCredentialStorage(), - ] - self.stores = stores - - async def get(self, provider: str) -> Credential | None: - """Get credential from first store that has it.""" - for store in self.stores: - cred = await store.get(provider) - if cred and cred.has_credentials: - return cred - return None - - async def get_with_source( - self, provider: str - ) -> tuple[Credential | None, CredentialStorage | None]: - """Get credential and its source store.""" - for store in self.stores: - cred = await store.get(provider) - if cred and cred.has_credentials: - return cred, store - return None, None - - async def set(self, credential: Credential) -> None: - """Store credential in first writable store.""" - for store in self.stores: - try: - await store.set(credential) - return - except NotImplementedError: - continue - raise RuntimeError("No writable credential store available") - - async def delete(self, provider: str) -> bool: - """Delete from all writable stores.""" - deleted = False - for store in self.stores: - try: - if await store.delete(provider): - deleted = True - except NotImplementedError: - continue - return deleted - - async def list(self) -> list[str]: - """List providers from all stores.""" - providers = set() - for store in self.stores: - providers.update(await store.list()) - return list(providers) diff --git a/pkg/hanzo-tools-api/pyproject.toml b/pkg/hanzo-tools-api/pyproject.toml deleted file mode 100644 index 1b447bc0d..000000000 --- a/pkg/hanzo-tools-api/pyproject.toml +++ /dev/null @@ -1,53 +0,0 @@ -[project] -name = "hanzo-tools-api" -version = "0.3.1" -description = "Generic API tool for calling any REST API via OpenAPI specs - search, explore, and dynamically use ANY API" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "mcp", "api", "openapi", "tools"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", -] - -dependencies = [ - "hanzo-tools-core>=0.1.0", - "httpx>=0.27.0", - "pydantic>=2.0", - "pyyaml>=6.0", - "aiofiles>=24.1.0", -] - -[project.optional-dependencies] -dev = [ - "pytest>=8.0", - "pytest-asyncio>=0.24.0", - "respx>=0.22.0", # Mock httpx requests -] - -[project.entry-points."hanzo.tools"] -api = "hanzo_tools.api:TOOLS" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] - -[tool.ruff] -line-length = 120 -target-version = "py311" - -[tool.ruff.lint] -select = ["E", "F", "I", "B", "UP"] -ignore = ["E501"] - -[tool.pytest.ini_options] -asyncio_mode = "auto" -asyncio_default_fixture_loop_scope = "function" diff --git a/pkg/hanzo-tools-api/scripts/generate_providers.py b/pkg/hanzo-tools-api/scripts/generate_providers.py deleted file mode 100644 index 7b500e535..000000000 --- a/pkg/hanzo-tools-api/scripts/generate_providers.py +++ /dev/null @@ -1,418 +0,0 @@ -#!/usr/bin/env python3 -"""Generate provider configurations from APIs.guru with LLM-optimized descriptions from oapis.org.""" - -import json -import re -import sys -import urllib.request -from concurrent.futures import ThreadPoolExecutor - -# Common env var patterns -ENV_VAR_PATTERNS = { - "stripe": ["STRIPE_API_KEY", "STRIPE_SECRET_KEY"], - "twilio": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], - "github": ["GITHUB_TOKEN", "GH_TOKEN"], - "gitlab": ["GITLAB_TOKEN"], - "slack": ["SLACK_TOKEN", "SLACK_BOT_TOKEN"], - "discord": ["DISCORD_TOKEN", "DISCORD_BOT_TOKEN"], - "shopify": ["SHOPIFY_API_KEY", "SHOPIFY_ACCESS_TOKEN"], - "hubspot": ["HUBSPOT_API_KEY", "HUBSPOT_ACCESS_TOKEN"], - "mailchimp": ["MAILCHIMP_API_KEY"], - "sendgrid": ["SENDGRID_API_KEY"], - "mailgun": ["MAILGUN_API_KEY"], - "contentful": ["CONTENTFUL_ACCESS_TOKEN"], - "airtable": ["AIRTABLE_API_KEY"], - "notion": ["NOTION_API_KEY", "NOTION_TOKEN"], - "figma": ["FIGMA_TOKEN", "FIGMA_ACCESS_TOKEN"], - "asana": ["ASANA_TOKEN", "ASANA_ACCESS_TOKEN"], - "trello": ["TRELLO_API_KEY", "TRELLO_TOKEN"], - "jira": ["JIRA_TOKEN", "JIRA_API_TOKEN"], - "confluence": ["CONFLUENCE_TOKEN"], - "bitbucket": ["BITBUCKET_TOKEN"], - "dropbox": ["DROPBOX_ACCESS_TOKEN"], - "box": ["BOX_ACCESS_TOKEN"], - "spotify": ["SPOTIFY_CLIENT_ID", "SPOTIFY_CLIENT_SECRET"], - "youtube": ["YOUTUBE_API_KEY"], - "twitter": ["TWITTER_API_KEY", "TWITTER_BEARER_TOKEN"], - "linkedin": ["LINKEDIN_ACCESS_TOKEN"], - "facebook": ["FACEBOOK_ACCESS_TOKEN"], - "instagram": ["INSTAGRAM_ACCESS_TOKEN"], - "pinterest": ["PINTEREST_ACCESS_TOKEN"], - "reddit": ["REDDIT_CLIENT_ID", "REDDIT_CLIENT_SECRET"], - "zendesk": ["ZENDESK_API_TOKEN"], - "freshdesk": ["FRESHDESK_API_KEY"], - "intercom": ["INTERCOM_ACCESS_TOKEN"], - "mixpanel": ["MIXPANEL_TOKEN"], - "segment": ["SEGMENT_WRITE_KEY"], - "amplitude": ["AMPLITUDE_API_KEY"], - "plaid": ["PLAID_CLIENT_ID", "PLAID_SECRET"], - "square": ["SQUARE_ACCESS_TOKEN"], - "paypal": ["PAYPAL_CLIENT_ID", "PAYPAL_CLIENT_SECRET"], - "braintree": ["BRAINTREE_MERCHANT_ID"], - "quickbooks": ["QUICKBOOKS_CLIENT_ID"], - "xero": ["XERO_CLIENT_ID"], - "salesforce": ["SALESFORCE_ACCESS_TOKEN"], - "pipedrive": ["PIPEDRIVE_API_TOKEN"], - "zoho": ["ZOHO_ACCESS_TOKEN"], - "monday": ["MONDAY_API_KEY"], - "clickup": ["CLICKUP_API_KEY"], - "linear": ["LINEAR_API_KEY"], - "vercel": ["VERCEL_TOKEN"], - "netlify": ["NETLIFY_AUTH_TOKEN"], - "heroku": ["HEROKU_API_KEY"], - "digitalocean": ["DIGITALOCEAN_TOKEN", "DO_TOKEN"], - "linode": ["LINODE_TOKEN"], - "vultr": ["VULTR_API_KEY"], - "cloudflare": ["CLOUDFLARE_API_TOKEN", "CF_API_TOKEN"], - "datadog": ["DD_API_KEY", "DATADOG_API_KEY"], - "newrelic": ["NEW_RELIC_API_KEY"], - "sentry": ["SENTRY_AUTH_TOKEN"], - "pagerduty": ["PAGERDUTY_API_KEY"], - "opsgenie": ["OPSGENIE_API_KEY"], - "splunk": ["SPLUNK_TOKEN"], - "elasticsearch": ["ELASTIC_API_KEY"], - "algolia": ["ALGOLIA_API_KEY"], - "meilisearch": ["MEILISEARCH_API_KEY"], - "typesense": ["TYPESENSE_API_KEY"], - "twitch": ["TWITCH_CLIENT_ID", "TWITCH_CLIENT_SECRET"], - "ebay": ["EBAY_APP_ID", "EBAY_DEV_ID"], - "etsy": ["ETSY_API_KEY"], - "walmart": ["WALMART_CLIENT_ID"], - "bestbuy": ["BESTBUY_API_KEY"], - "yelp": ["YELP_API_KEY"], - "foursquare": ["FOURSQUARE_API_KEY"], - "tripadvisor": ["TRIPADVISOR_API_KEY"], - "airbnb": ["AIRBNB_API_KEY"], - "uber": ["UBER_ACCESS_TOKEN"], - "lyft": ["LYFT_ACCESS_TOKEN"], - "doordash": ["DOORDASH_API_KEY"], - "postmates": ["POSTMATES_API_KEY"], - "mapbox": ["MAPBOX_ACCESS_TOKEN"], - "here": ["HERE_API_KEY"], - "tomtom": ["TOMTOM_API_KEY"], - "openweathermap": ["OPENWEATHERMAP_API_KEY"], - "weatherapi": ["WEATHERAPI_KEY"], - "newsapi": ["NEWSAPI_KEY"], - "nytimes": ["NYTIMES_API_KEY"], - "guardian": ["GUARDIAN_API_KEY"], - "giphy": ["GIPHY_API_KEY"], - "unsplash": ["UNSPLASH_ACCESS_KEY"], - "pexels": ["PEXELS_API_KEY"], - "cloudinary": ["CLOUDINARY_API_KEY"], - "imgix": ["IMGIX_API_KEY"], - "uploadcare": ["UPLOADCARE_PUBLIC_KEY"], - "filestack": ["FILESTACK_API_KEY"], - "agora": ["AGORA_APP_ID"], - "vonage": ["VONAGE_API_KEY", "NEXMO_API_KEY"], - "messagebird": ["MESSAGEBIRD_API_KEY"], - "bandwidth": ["BANDWIDTH_API_TOKEN"], - "telnyx": ["TELNYX_API_KEY"], - "apilayer": ["APILAYER_API_KEY"], - "currencylayer": ["CURRENCYLAYER_API_KEY"], - "exchangerate": ["EXCHANGERATE_API_KEY"], - "coinbase": ["COINBASE_API_KEY"], - "binance": ["BINANCE_API_KEY"], - "kraken": ["KRAKEN_API_KEY"], - "alchemy": ["ALCHEMY_API_KEY"], - "infura": ["INFURA_PROJECT_ID"], - "moralis": ["MORALIS_API_KEY"], - "thegraph": ["THEGRAPH_API_KEY"], - "openai": ["OPENAI_API_KEY"], - "anthropic": ["ANTHROPIC_API_KEY"], - "cohere": ["COHERE_API_KEY"], - "huggingface": ["HF_TOKEN", "HUGGINGFACE_TOKEN"], - "replicate": ["REPLICATE_API_TOKEN"], - "stability": ["STABILITY_API_KEY"], - "deepl": ["DEEPL_API_KEY"], - "ably": ["ABLY_API_KEY"], - "pusher": ["PUSHER_APP_KEY"], - "pubnub": ["PUBNUB_SUBSCRIBE_KEY"], - "firebase": ["FIREBASE_API_KEY"], - "supabase": ["SUPABASE_API_KEY"], - "mongodb": ["MONGODB_API_KEY"], - "redis": ["REDIS_API_KEY"], - "cockroachdb": ["COCKROACHDB_API_KEY"], - "planetscale": ["PLANETSCALE_TOKEN"], - "neon": ["NEON_API_KEY"], - "upstash": ["UPSTASH_API_KEY"], - "fauna": ["FAUNA_SECRET"], - "adyen": ["ADYEN_API_KEY"], - "sportsdata": ["SPORTSDATA_API_KEY"], - "amadeus": ["AMADEUS_API_KEY", "AMADEUS_API_SECRET"], - "nexmo": ["NEXMO_API_KEY", "NEXMO_API_SECRET"], - "mastercard": ["MASTERCARD_API_KEY"], - "hubapi": ["HUBSPOT_API_KEY"], - "apideck": ["APIDECK_API_KEY"], - "codat": ["CODAT_API_KEY"], - "deutschebahn": ["DEUTSCHEBAHN_API_KEY"], - "rapidapi": ["RAPIDAPI_KEY"], - "whapi": ["WHAPI_TOKEN"], - "vtex": ["VTEX_APP_KEY", "VTEX_APP_TOKEN"], - "interzoid": ["INTERZOID_API_KEY"], -} - -# Skip cloud-specific APIs that need special auth handling -SKIP_PREFIXES = [ - "amazonaws", - "azure", - "googleapis", - "google.", - "apisetu", - "parliament", - "gov.", - "opto22", - "windows", - "o365", - "microsofthealth", -] - -# Popular APIs with oapis.org LLM-friendly descriptions -# These get priority loading and better descriptions -OAPIS_POPULAR = [ - "github", - "stripe", - "twilio", - "slack", - "notion", - "discord", - "shopify", - "spotify", - "twitter", - "openai", - "anthropic", -] - - -def get_env_vars(name: str) -> list[str]: - """Get env vars for a provider name.""" - name_lower = name.lower() - for key, vars in ENV_VAR_PATTERNS.items(): - if key in name_lower: - return vars - # Generate default pattern - clean = re.sub(r"[^a-z0-9]", "_", name_lower).upper() - return [f"{clean}_API_KEY", f"{clean}_TOKEN"] - - -def clean_name(name: str) -> str: - """Clean API name for use as provider ID.""" - # Remove version suffixes - name = re.sub(r":\d+.*$", "", name) - # Replace dots and colons with hyphens - name = name.replace(".", "-").replace(":", "-") - # Remove common suffixes - name = re.sub(r"-com$|-io$|-net$|-org$|-local$", "", name) - # Clean up - name = re.sub(r"[^a-z0-9-]", "", name.lower()) - name = re.sub(r"-+", "-", name).strip("-") - return name - - -def fetch_oapis_slop(name: str) -> dict | None: - """Fetch LLM-optimized description from oapis.org/slop/{name}. - - Returns dict with base_url, endpoints_count, description if available. - """ - try: - url = f"https://oapis.org/slop/{name}" - req = urllib.request.Request(url, headers={"User-Agent": "hanzo-tools-api/0.2"}) - with urllib.request.urlopen(req, timeout=10) as response: - content = response.read().decode() - - # Parse the slop format - result = {} - - # Extract base URL (usually on a line with https://) - base_match = re.search( - r"Base URL:\s*(https?://[^\s]+)", content, re.IGNORECASE - ) - if not base_match: - base_match = re.search( - r"\*\*Base URL\*\*:\s*(https?://[^\s]+)", content, re.IGNORECASE - ) - if base_match: - result["base_url"] = base_match.group(1).rstrip("/") - - # Extract endpoint count - count_match = re.search(r"(\d+)\s+endpoints?", content, re.IGNORECASE) - if count_match: - result["endpoints_count"] = int(count_match.group(1)) - - # Extract description (first paragraph after title) - desc_match = re.search(r"^#[^\n]+\n+([^#\n][^\n]+)", content, re.MULTILINE) - if desc_match: - result["description"] = desc_match.group(1).strip()[:200] - - return result if result else None - except Exception: - return None - - -def fetch_apis_guru() -> dict: - """Fetch API list from APIs.guru.""" - url = "https://api.apis.guru/v2/list.json" - req = urllib.request.Request(url, headers={"User-Agent": "hanzo-tools-api/0.2"}) - with urllib.request.urlopen(req, timeout=60) as response: - return json.loads(response.read().decode()) - - -def fetch_oapis_batch(names: list[str], max_workers: int = 10) -> dict[str, dict]: - """Fetch oapis.org descriptions for multiple APIs concurrently.""" - results = {} - - def fetch_one(name: str) -> tuple[str, dict | None]: - return name, fetch_oapis_slop(name) - - with ThreadPoolExecutor(max_workers=max_workers) as executor: - for name, data in executor.map(lambda n: fetch_one(n), names): - if data: - results[name] = data - - return results - - -def main(): - print("Fetching APIs from APIs.guru...", file=sys.stderr) - data = fetch_apis_guru() - print(f"Found {len(data)} APIs", file=sys.stderr) - - # Fetch oapis.org LLM descriptions for popular APIs - print( - f"Fetching LLM descriptions from oapis.org for {len(OAPIS_POPULAR)} popular APIs...", - file=sys.stderr, - ) - oapis_data = fetch_oapis_batch(OAPIS_POPULAR) - print(f"Got {len(oapis_data)} oapis.org descriptions", file=sys.stderr) - - apis = [] - seen_names = set() - - for name, info in data.items(): - # Skip cloud-specific - skip = False - for prefix in SKIP_PREFIXES: - if name.lower().startswith(prefix.lower()): - skip = True - break - if skip: - continue - - preferred = info.get("preferred", "") - if not preferred or preferred not in info.get("versions", {}): - continue - - version_info = info["versions"][preferred] - spec_url = version_info.get("swaggerUrl", "") - if not spec_url: - continue - - api_info = version_info.get("info", {}) - title = api_info.get("title", name) - - clean = clean_name(name) - if clean in seen_names or len(clean) < 2: - continue - seen_names.add(clean) - - # Get base URL from x-origin - base_url = "" - if "x-origin" in api_info: - x_origin = api_info["x-origin"] - if isinstance(x_origin, list) and x_origin: - base_url = x_origin[0].get("url", "") - - # Check for oapis.org enhanced data - oapis_key = None - for popular in OAPIS_POPULAR: - if popular in clean: - oapis_key = popular - break - - description = ( - api_info.get("description", "")[:200] if api_info.get("description") else "" - ) - endpoints_count = None - - if oapis_key and oapis_key in oapis_data: - oapis_info = oapis_data[oapis_key] - if oapis_info.get("base_url"): - base_url = oapis_info["base_url"] - if oapis_info.get("description"): - description = oapis_info["description"] - if oapis_info.get("endpoints_count"): - endpoints_count = oapis_info["endpoints_count"] - - # Clean strings - remove newlines, escape quotes, limit length - def clean_str(s: str, max_len: int = 150) -> str: - if not s: - return "" - # Replace various whitespace with single space - s = " ".join(s.split()) - # Escape backslashes first, then quotes - s = s.replace("\\", "\\\\").replace('"', '\\"') - return s[:max_len] - - apis.append( - { - "name": clean, - "title": clean_str(title, 60), - "description": clean_str(description, 150), - "spec_url": spec_url, - "base_url": base_url, - "env_vars": get_env_vars(clean), - "endpoints_count": endpoints_count, - } - ) - - # Sort by name - apis.sort(key=lambda x: x["name"]) - - # Generate Python code - print('"""Auto-generated provider configurations from APIs.guru + oapis.org.') - print() - print(f"Generated {len(apis)} provider configurations.") - print("Includes LLM-optimized descriptions from oapis.org for popular APIs.") - print() - print( - "Regenerate with: python scripts/generate_providers.py > hanzo_tools/api/apis_guru_providers.py" - ) - print('"""') - print() - print("from typing import Any") - print() - print() - print( - "# =============================================================================" - ) - print("# APIs.guru Provider Configurations (with oapis.org enhancements)") - print( - "# =============================================================================" - ) - print() - print("APIS_GURU_PROVIDERS: dict[str, dict[str, Any]] = {") - - for api in apis: - env_str = ", ".join(f'"{v}"' for v in api["env_vars"][:2]) - print(f' "{api["name"]}": {{') - print(f' "display_name": "{api["title"]}",') - if api["description"]: - print(f' "description": "{api["description"][:150]}",') - print(f' "spec_url": "{api["spec_url"]}",') - if api["base_url"]: - safe_url = api["base_url"].replace('"', '\\"') - print(f' "base_url": "{safe_url}",') - print(f' "env_vars": [{env_str}],') - if api["endpoints_count"]: - print(f' "endpoints_count": {api["endpoints_count"]},') - print(" },") - - print("}") - print() - print() - print(f"# Total: {len(apis)} providers") - - print(f"\nGenerated {len(apis)} providers", file=sys.stderr) - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-tools-api/scripts/preload_specs.py b/pkg/hanzo-tools-api/scripts/preload_specs.py deleted file mode 100644 index 5723aa1ea..000000000 --- a/pkg/hanzo-tools-api/scripts/preload_specs.py +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env python3 -"""Pre-download and cache popular OpenAPI specs for faster startup. - -Usage: - python scripts/preload_specs.py # Download top 50 specs - python scripts/preload_specs.py --all # Download all specs with URLs - python scripts/preload_specs.py --list # List available specs - python scripts/preload_specs.py github stripe # Download specific specs -""" - -import argparse -import asyncio -import sys -from pathlib import Path - -# Add parent to path for local development -sys.path.insert(0, str(Path(__file__).parent.parent)) - -from hanzo_tools.api.client import APIClient -from hanzo_tools.api.providers import get_provider_config, list_providers_with_specs - -# Top 50 most popular APIs to pre-cache -POPULAR_APIS = [ - # AI/ML - "openai", - "anthropic", - "together", - "groq", - "mistral", - "cohere", - "replicate", - # Developer - "github", - "gitlab", - "bitbucket", - "vercel", - "netlify", - "fly", - "railway", - "render", - # Cloud - "cloudflare", - "digitalocean", - "hetzner", - "linode", - "vultr", - # Payment - "stripe", - "paypal", - "adyen-com-accountservice", - "square", - # Communication - "twilio", - "sendgrid", - "resend", - "slack", - "discord", - # Databases - "supabase", - "neon", - "planetscale", - "mongodb", - "redis", - # Search - "algolia", - "elasticsearch", - "meilisearch", - "typesense", - # Monitoring - "datadog", - "sentry", - "newrelic", - # Other popular - "notion", - "airtable", - "hubspot", - "shopify", - "jira", -] - - -async def preload_specs( - providers: list[str] | None = None, - concurrent: int = 10, - verbose: bool = True, -) -> dict[str, bool]: - """Pre-download and cache OpenAPI specs. - - Args: - providers: List of provider names, or None for POPULAR_APIS - concurrent: Number of concurrent downloads - verbose: Print progress - - Returns: - Dict mapping provider name to success status - """ - client = APIClient() - - if providers is None: - providers = POPULAR_APIS - - # Filter to only those with spec URLs - valid_providers = [] - for p in providers: - config = get_provider_config(p) - if config and config.spec_url: - valid_providers.append(p) - elif verbose: - print(f" โš  {p}: no spec URL configured") - - if verbose: - print(f"Pre-loading {len(valid_providers)} OpenAPI specs...") - print() - - results: dict[str, bool] = {} - semaphore = asyncio.Semaphore(concurrent) - - async def fetch_one(provider: str) -> tuple[str, bool]: - async with semaphore: - try: - await client.spec(provider) - if verbose: - print(f" โœ“ {provider}") - return provider, True - except Exception as e: - if verbose: - print(f" โœ— {provider}: {e}") - return provider, False - - tasks = [fetch_one(p) for p in valid_providers] - for coro in asyncio.as_completed(tasks): - provider, success = await coro - results[provider] = success - - if verbose: - success_count = sum(1 for v in results.values() if v) - print() - print(f"Downloaded {success_count}/{len(results)} specs") - print(f"Cached at: {client._spec_cache.cache_dir}") - - return results - - -async def main(): - parser = argparse.ArgumentParser(description="Pre-download OpenAPI specs") - parser.add_argument("providers", nargs="*", help="Specific providers to download") - parser.add_argument( - "--all", action="store_true", help="Download all available specs" - ) - parser.add_argument("--list", action="store_true", help="List available specs") - parser.add_argument( - "-n", "--concurrent", type=int, default=10, help="Concurrent downloads" - ) - parser.add_argument("-q", "--quiet", action="store_true", help="Quiet mode") - - args = parser.parse_args() - - if args.list: - specs = list_providers_with_specs() - print(f"Available specs ({len(specs)}):") - for i, name in enumerate(specs, 1): - config = get_provider_config(name) - display = config.display_name if config else name - print(f" {i:4}. {name}: {display}") - return - - providers = None - if args.providers: - providers = args.providers - elif args.all: - providers = list_providers_with_specs() - - await preload_specs( - providers=providers, - concurrent=args.concurrent, - verbose=not args.quiet, - ) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/pkg/hanzo-tools-api/tests/__init__.py b/pkg/hanzo-tools-api/tests/__init__.py deleted file mode 100644 index 31a6d9baf..000000000 --- a/pkg/hanzo-tools-api/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Tests for hanzo-tools-api diff --git a/pkg/hanzo-tools-api/tests/test_api_tools.py b/pkg/hanzo-tools-api/tests/test_api_tools.py deleted file mode 100644 index cac16a85d..000000000 --- a/pkg/hanzo-tools-api/tests/test_api_tools.py +++ /dev/null @@ -1,469 +0,0 @@ -"""Tests for hanzo-tools-api v0.2.0.""" - -import json -import os -import tempfile -from pathlib import Path -from unittest.mock import AsyncMock, patch - -import pytest - -from hanzo_tools.api import ( - ENV_VAR_MAPPINGS, - PROVIDER_CONFIGS, - APIClient, - APITool, - Credential, - CredentialManager, - HanzoTool, - OpenAPIClient, - SpecCache, -) -from hanzo_tools.api.models import AuthType -from hanzo_tools.api.storage import ( - ChainedCredentialStorage, - MemoryCredentialStorage, -) - - -class TestCredentialManager: - """Tests for CredentialManager.""" - - @pytest.fixture - def temp_config_dir(self): - """Create a temporary config directory.""" - with tempfile.TemporaryDirectory() as tmpdir: - yield Path(tmpdir) - - @pytest.fixture - def cred_manager(self, temp_config_dir): - """Create a CredentialManager with temp directory.""" - return CredentialManager(config_dir=temp_config_dir) - - @pytest.mark.asyncio - async def test_set_and_get_credential(self, cred_manager): - """Test storing and retrieving credentials.""" - await cred_manager.set_credential( - provider="test-provider", - api_key="test-key", - api_secret="test-secret", - account_id="test-account", - ) - - cred = await cred_manager.get_credential("test-provider") - assert cred.provider == "test-provider" - assert cred.api_key == "test-key" - assert cred.api_secret == "test-secret" - assert cred.account_id == "test-account" - assert cred.has_credentials - - @pytest.mark.asyncio - async def test_credentials_persist(self, temp_config_dir): - """Test that credentials persist across instances.""" - # Set credential - cm1 = CredentialManager(config_dir=temp_config_dir) - await cm1.set_credential("test", api_key="my-key") - - # Read with new instance - cm2 = CredentialManager(config_dir=temp_config_dir) - cred = await cm2.get_credential("test") - assert cred.api_key == "my-key" - - @pytest.mark.asyncio - async def test_delete_credential(self, cred_manager): - """Test deleting credentials.""" - await cred_manager.set_credential("test", api_key="key") - assert await cred_manager.delete_credential("test") - assert not await cred_manager.delete_credential("test") # Already deleted - - cred = await cred_manager.get_credential("test") - assert not cred.has_credentials - - @pytest.mark.asyncio - async def test_env_var_fallback(self, temp_config_dir): - """Test environment variable fallback.""" - # Use a unique provider with custom env var mapping - with patch.dict(os.environ, {"TESTPROV_API_KEY": "env-token"}, clear=False): - # Create custom storage chain with our test env var - from hanzo_tools.api.storage import EnvironmentCredentialStorage - - env_mappings = {"testprov": ["TESTPROV_API_KEY"]} - storage = ChainedCredentialStorage( - [ - MemoryCredentialStorage(), - EnvironmentCredentialStorage(env_mappings), - ] - ) - - cred_manager = CredentialManager( - config_dir=temp_config_dir, - storage=storage, - ) - cred = await cred_manager.get_credential("testprov") - assert cred.api_key == "env-token" - - @pytest.mark.asyncio - async def test_list_providers(self, cred_manager): - """Test listing providers.""" - await cred_manager.set_credential("custom", api_key="key") - - providers = await cred_manager.list_providers() - provider_names = [p.name for p in providers] - - # Should include configured providers - assert "cloudflare" in provider_names - assert "github" in provider_names - assert "custom" in provider_names - - # Custom should be configured - custom = next(p for p in providers if p.name == "custom") - assert custom.configured - - def test_get_provider_config(self, cred_manager): - """Test getting provider configuration.""" - config = cred_manager.get_provider_config("cloudflare") - assert config is not None - assert config.name == "cloudflare" - assert config.base_url == "https://api.cloudflare.com/client/v4" - assert config.auth_type == AuthType.BEARER - - -class TestProviderConfigs: - """Tests for built-in provider configurations.""" - - def test_all_providers_have_required_fields(self): - """Test that all providers have required fields.""" - for name, config in PROVIDER_CONFIGS.items(): - assert config.name == name - assert config.display_name - assert config.base_url - assert config.auth_type in AuthType - - def test_env_var_mappings_exist(self): - """Test that env var mappings cover common providers.""" - expected_providers = ["cloudflare", "github", "openai", "anthropic", "stripe"] - for provider in expected_providers: - assert provider in ENV_VAR_MAPPINGS - assert len(ENV_VAR_MAPPINGS[provider]) > 0 - - -class TestCredential: - """Tests for Credential model.""" - - def test_has_credentials(self): - """Test has_credentials property.""" - cred = Credential(provider="test") - assert not cred.has_credentials - - cred = Credential(provider="test", api_key="key") - assert cred.has_credentials - - -class TestOpenAPIClient: - """Tests for OpenAPIClient.""" - - @pytest.fixture - def sample_spec(self): - """Sample OpenAPI spec for testing.""" - return { - "openapi": "3.0.0", - "info": {"title": "Test API", "version": "1.0"}, - "servers": [{"url": "https://api.test.com/v1"}], - "paths": { - "/users": { - "get": { - "operationId": "listUsers", - "summary": "List all users", - "tags": ["users"], - "parameters": [ - { - "name": "limit", - "in": "query", - "schema": {"type": "integer"}, - } - ], - "responses": {"200": {"description": "Success"}}, - }, - "post": { - "operationId": "createUser", - "summary": "Create a user", - "tags": ["users"], - "requestBody": { - "required": True, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {"name": {"type": "string"}}, - } - } - }, - }, - "responses": {"201": {"description": "Created"}}, - }, - }, - "/users/{id}": { - "get": { - "operationId": "getUser", - "summary": "Get a user", - "tags": ["users"], - "parameters": [ - { - "name": "id", - "in": "path", - "required": True, - "schema": {"type": "string"}, - } - ], - "responses": {"200": {"description": "Success"}}, - } - }, - }, - } - - @pytest.fixture - def temp_config_dir(self): - """Create a temporary config directory.""" - with tempfile.TemporaryDirectory() as tmpdir: - yield Path(tmpdir) - - @pytest.fixture - async def client(self, sample_spec, temp_config_dir): - """Create an OpenAPIClient with sample spec.""" - # Create credential manager - cred_manager = CredentialManager(config_dir=temp_config_dir) - await cred_manager.set_credential("test", api_key="test-key") - - # Create spec cache and seed it with sample spec - spec_cache = SpecCache(temp_config_dir / "specs") - await spec_cache.set("test", sample_spec) - - # Create client - client = OpenAPIClient( - "test", - credential_manager=cred_manager, - spec_cache=spec_cache, - ) - - # Load the spec - await client.load_spec() - - return client - - @pytest.mark.asyncio - async def test_base_url_from_spec(self, client): - """Test base URL extraction from spec.""" - assert client.base_url == "https://api.test.com/v1" - - @pytest.mark.asyncio - async def test_list_operations(self, client): - """Test listing operations.""" - result = client.list_operations() - assert result.total_count == 3 - op_ids = [op.operation_id for op in result.operations] - assert "listUsers" in op_ids - assert "createUser" in op_ids - assert "getUser" in op_ids - - @pytest.mark.asyncio - async def test_list_operations_by_tag(self, client): - """Test filtering operations by tag.""" - result = client.list_operations(tag="users") - assert result.total_count == 3 - - @pytest.mark.asyncio - async def test_list_operations_by_search(self, client): - """Test searching operations.""" - result = client.list_operations(search="list") - assert result.total_count == 1 - assert result.operations[0].operation_id == "listUsers" - - @pytest.mark.asyncio - async def test_get_operation(self, client): - """Test getting a specific operation.""" - op = client.get_operation("getUser") - assert op is not None - assert op.operation_id == "getUser" - assert op.method == "GET" - assert op.path == "/users/{id}" - assert len(op.parameters) == 1 - assert op.parameters[0].name == "id" - assert op.parameters[0].required - - @pytest.mark.asyncio - async def test_path_param_substitution(self, client): - """Test path parameter substitution.""" - path, remaining = client._substitute_path_params( - "/users/{id}/posts/{post_id}", - {"id": "123", "post_id": "456", "extra": "value"}, - ) - assert path == "/users/123/posts/456" - assert remaining == {"extra": "value"} - - -class TestAPIClient: - """Tests for the main APIClient.""" - - @pytest.fixture - def temp_config_dir(self): - """Create a temporary config directory.""" - with tempfile.TemporaryDirectory() as tmpdir: - yield Path(tmpdir) - - @pytest.fixture - def api_client(self, temp_config_dir): - """Create APIClient with temp directory.""" - return APIClient(config_dir=temp_config_dir) - - @pytest.mark.asyncio - async def test_list_providers(self, api_client): - """Test listing providers.""" - result = await api_client.list_providers() - assert result.total_count > 0 - - provider_names = [p.name for p in result.providers] - assert "cloudflare" in provider_names - assert "github" in provider_names - - @pytest.mark.asyncio - async def test_config_and_get_credentials(self, api_client): - """Test configuring and retrieving credentials.""" - await api_client.config("test-api", api_key="my-key") - - effective = await api_client.get_effective_credentials("test-api") - assert effective.has_credentials - assert effective.credential.api_key == "my-key" - - @pytest.mark.asyncio - async def test_delete_config(self, api_client): - """Test deleting configuration.""" - await api_client.config("test-api", api_key="my-key") - assert await api_client.delete_config("test-api") - assert not await api_client.delete_config("test-api") # Already deleted - - -class TestAPITool: - """Tests for APITool.""" - - @pytest.fixture - def temp_config_dir(self): - """Create a temporary config directory.""" - with tempfile.TemporaryDirectory() as tmpdir: - yield Path(tmpdir) - - @pytest.fixture - def tool(self, temp_config_dir): - """Create APITool with mocked client.""" - client = APIClient(config_dir=temp_config_dir) - return APITool(client=client) - - @pytest.mark.asyncio - async def test_list_action(self, tool): - """Test list action.""" - ctx = AsyncMock() - result = await tool.call(ctx, action="list") - assert "API Providers" in result - assert "cloudflare" in result.lower() - - @pytest.mark.asyncio - async def test_config_action(self, tool): - """Test config action.""" - ctx = AsyncMock() - - # Configure - result = await tool.call( - ctx, - action="config", - provider="test-api", - api_key="my-key", - ) - assert "Configured" in result - - @pytest.mark.asyncio - async def test_config_requires_provider(self, tool): - """Test that config action requires provider.""" - ctx = AsyncMock() - result = await tool.call(ctx, action="config", api_key="key") - assert "Error" in result - assert "provider" in result.lower() - - @pytest.mark.asyncio - async def test_config_requires_api_key(self, tool): - """Test that config action requires api_key.""" - ctx = AsyncMock() - result = await tool.call(ctx, action="config", provider="test") - assert "Error" in result - assert "api_key" in result.lower() - - @pytest.mark.asyncio - async def test_unknown_action(self, tool): - """Test unknown action.""" - ctx = AsyncMock() - result = await tool.call(ctx, action="invalid") - assert "Unknown action" in result - - def test_tool_name(self, tool): - """Test tool name.""" - assert tool.name == "api" - - def test_tool_description(self, tool): - """Test tool description.""" - desc = tool.description - assert "API" in desc - assert "OpenAPI" in desc - assert "credential" in desc.lower() - - -class TestHanzoTool: - """Tests for unified HanzoTool surface.""" - - @pytest.mark.asyncio - async def test_services_listing(self): - """Service discovery should return consolidated service list.""" - tool = HanzoTool() - ctx = AsyncMock() - result = await tool.call(ctx, service="services") - payload = json.loads(result) - assert "services" in payload - assert "hanzo" not in payload["services"] # service router, not a nested service - assert "commerce" in payload["services"] - assert "iam" in payload["services"] - - @pytest.mark.asyncio - async def test_invalid_args_json(self): - """Invalid JSON args should return structured error.""" - tool = HanzoTool() - ctx = AsyncMock() - result = await tool.call( - ctx, - service="iam", - action="users", - args="{invalid-json", - ) - payload = json.loads(result) - assert "error" in payload - assert payload["service"] == "iam" - - -class TestIntegration: - """Integration tests (require network, skip by default).""" - - @pytest.mark.skip(reason="Requires network and API keys") - @pytest.mark.asyncio - async def test_github_api(self): - """Test calling GitHub API.""" - # This would require GITHUB_TOKEN to be set - async with APIClient() as client: - await client.spec("github") - result = await client.ops("github", search="user") - assert result.total_count > 0 - - @pytest.mark.skip(reason="Requires network and API keys") - @pytest.mark.asyncio - async def test_cloudflare_api(self): - """Test calling Cloudflare API.""" - # This would require CLOUDFLARE_API_TOKEN to be set - async with APIClient() as client: - await client.spec("cloudflare") - result = await client.call("cloudflare", "listZones") - assert result.success diff --git a/pkg/hanzo-tools-api/uv.lock b/pkg/hanzo-tools-api/uv.lock deleted file mode 100644 index 614d2acc6..000000000 --- a/pkg/hanzo-tools-api/uv.lock +++ /dev/null @@ -1,1589 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" - -[[package]] -name = "aiofiles" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/c3/534eac40372d8ee36ef40df62ec129bee4fdb5ad9706e58a29be53b2c970/aiofiles-25.1.0.tar.gz", hash = "sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2", size = 46354, upload-time = "2025-10-09T20:51:04.358Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/8a/340a1555ae33d7354dbca4faa54948d76d89a27ceef032c8c3bc661d003e/aiofiles-25.1.0-py3-none-any.whl", hash = "sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695", size = 14668, upload-time = "2025-10-09T20:51:03.174Z" }, -] - -[[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.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, -] - -[[package]] -name = "attrs" -version = "25.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, -] - -[[package]] -name = "authlib" -version = "1.6.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/9b/b1661026ff24bc641b76b78c5222d614776b0c085bcfdac9bd15a1cb4b35/authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e", size = 164894, upload-time = "2025-12-12T08:01:41.464Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/51/321e821856452f7386c4e9df866f196720b1ad0c5ea1623ea7399969ae3b/authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd", size = 244005, upload-time = "2025-12-12T08:01:40.209Z" }, -] - -[[package]] -name = "beartype" -version = "0.22.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, -] - -[[package]] -name = "cachetools" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/af/df70e9b65bc77a1cbe0768c0aa4617147f30f8306ded98c1744bcdc0ae1e/cachetools-7.0.0.tar.gz", hash = "sha256:a9abf18ff3b86c7d05b27ead412e235e16ae045925e531fae38d5fada5ed5b08", size = 35796, upload-time = "2026-02-01T18:59:47.411Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/df/2dd32cce20cbcf6f2ec456b58d44368161ad28320729f64e5e1d5d7bd0ae/cachetools-7.0.0-py3-none-any.whl", hash = "sha256:d52fef60e6e964a1969cfb61ccf6242a801b432790fe520d78720d757c81cbd2", size = 13487, upload-time = "2026-02-01T18:59:45.981Z" }, -] - -[[package]] -name = "certifi" -version = "2026.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, - { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, - { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, - { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, - { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, - { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, - { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, - { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, - { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, -] - -[[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 = "cryptography" -version = "46.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/19/f748958276519adf6a0c1e79e7b8860b4830dda55ccdf29f2719b5fc499c/cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59", size = 749301, upload-time = "2026-01-28T00:24:37.379Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/99/157aae7949a5f30d51fcb1a9851e8ebd5c74bf99b5285d8bb4b8b9ee641e/cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485", size = 7173686, upload-time = "2026-01-28T00:23:07.515Z" }, - { url = "https://files.pythonhosted.org/packages/87/91/874b8910903159043b5c6a123b7e79c4559ddd1896e38967567942635778/cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc", size = 4275871, upload-time = "2026-01-28T00:23:09.439Z" }, - { url = "https://files.pythonhosted.org/packages/c0/35/690e809be77896111f5b195ede56e4b4ed0435b428c2f2b6d35046fbb5e8/cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0", size = 4423124, upload-time = "2026-01-28T00:23:11.529Z" }, - { url = "https://files.pythonhosted.org/packages/1a/5b/a26407d4f79d61ca4bebaa9213feafdd8806dc69d3d290ce24996d3cfe43/cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa", size = 4277090, upload-time = "2026-01-28T00:23:13.123Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d8/4bb7aec442a9049827aa34cee1aa83803e528fa55da9a9d45d01d1bb933e/cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81", size = 4947652, upload-time = "2026-01-28T00:23:14.554Z" }, - { url = "https://files.pythonhosted.org/packages/2b/08/f83e2e0814248b844265802d081f2fac2f1cbe6cd258e72ba14ff006823a/cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255", size = 4455157, upload-time = "2026-01-28T00:23:16.443Z" }, - { url = "https://files.pythonhosted.org/packages/0a/05/19d849cf4096448779d2dcc9bb27d097457dac36f7273ffa875a93b5884c/cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e", size = 3981078, upload-time = "2026-01-28T00:23:17.838Z" }, - { url = "https://files.pythonhosted.org/packages/e6/89/f7bac81d66ba7cde867a743ea5b37537b32b5c633c473002b26a226f703f/cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c", size = 4276213, upload-time = "2026-01-28T00:23:19.257Z" }, - { url = "https://files.pythonhosted.org/packages/da/9f/7133e41f24edd827020ad21b068736e792bc68eecf66d93c924ad4719fb3/cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32", size = 4912190, upload-time = "2026-01-28T00:23:21.244Z" }, - { url = "https://files.pythonhosted.org/packages/a6/f7/6d43cbaddf6f65b24816e4af187d211f0bc536a29961f69faedc48501d8e/cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616", size = 4454641, upload-time = "2026-01-28T00:23:22.866Z" }, - { url = "https://files.pythonhosted.org/packages/9e/4f/ebd0473ad656a0ac912a16bd07db0f5d85184924e14fc88feecae2492834/cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0", size = 4405159, upload-time = "2026-01-28T00:23:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d1/f7/7923886f32dc47e27adeff8246e976d77258fd2aa3efdd1754e4e323bf49/cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0", size = 4666059, upload-time = "2026-01-28T00:23:26.766Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a7/0fca0fd3591dffc297278a61813d7f661a14243dd60f499a7a5b48acb52a/cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5", size = 3026378, upload-time = "2026-01-28T00:23:28.317Z" }, - { url = "https://files.pythonhosted.org/packages/2d/12/652c84b6f9873f0909374864a57b003686c642ea48c84d6c7e2c515e6da5/cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b", size = 3478614, upload-time = "2026-01-28T00:23:30.275Z" }, - { url = "https://files.pythonhosted.org/packages/b9/27/542b029f293a5cce59349d799d4d8484b3b1654a7b9a0585c266e974a488/cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908", size = 7116417, upload-time = "2026-01-28T00:23:31.958Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f5/559c25b77f40b6bf828eabaf988efb8b0e17b573545edb503368ca0a2a03/cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da", size = 4264508, upload-time = "2026-01-28T00:23:34.264Z" }, - { url = "https://files.pythonhosted.org/packages/49/a1/551fa162d33074b660dc35c9bc3616fefa21a0e8c1edd27b92559902e408/cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829", size = 4409080, upload-time = "2026-01-28T00:23:35.793Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/4d8d129a755f5d6df1bbee69ea2f35ebfa954fa1847690d1db2e8bca46a5/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2", size = 4270039, upload-time = "2026-01-28T00:23:37.263Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f5/ed3fcddd0a5e39321e595e144615399e47e7c153a1fb8c4862aec3151ff9/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085", size = 4926748, upload-time = "2026-01-28T00:23:38.884Z" }, - { url = "https://files.pythonhosted.org/packages/43/ae/9f03d5f0c0c00e85ecb34f06d3b79599f20630e4db91b8a6e56e8f83d410/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b", size = 4442307, upload-time = "2026-01-28T00:23:40.56Z" }, - { url = "https://files.pythonhosted.org/packages/8b/22/e0f9f2dae8040695103369cf2283ef9ac8abe4d51f68710bec2afd232609/cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd", size = 3959253, upload-time = "2026-01-28T00:23:42.827Z" }, - { url = "https://files.pythonhosted.org/packages/01/5b/6a43fcccc51dae4d101ac7d378a8724d1ba3de628a24e11bf2f4f43cba4d/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2", size = 4269372, upload-time = "2026-01-28T00:23:44.655Z" }, - { url = "https://files.pythonhosted.org/packages/17/b7/0f6b8c1dd0779df2b526e78978ff00462355e31c0a6f6cff8a3e99889c90/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e", size = 4891908, upload-time = "2026-01-28T00:23:46.48Z" }, - { url = "https://files.pythonhosted.org/packages/83/17/259409b8349aa10535358807a472c6a695cf84f106022268d31cea2b6c97/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f", size = 4441254, upload-time = "2026-01-28T00:23:48.403Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fe/e4a1b0c989b00cee5ffa0764401767e2d1cf59f45530963b894129fd5dce/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82", size = 4396520, upload-time = "2026-01-28T00:23:50.26Z" }, - { url = "https://files.pythonhosted.org/packages/b3/81/ba8fd9657d27076eb40d6a2f941b23429a3c3d2f56f5a921d6b936a27bc9/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c", size = 4651479, upload-time = "2026-01-28T00:23:51.674Z" }, - { url = "https://files.pythonhosted.org/packages/00/03/0de4ed43c71c31e4fe954edd50b9d28d658fef56555eba7641696370a8e2/cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061", size = 3001986, upload-time = "2026-01-28T00:23:53.485Z" }, - { url = "https://files.pythonhosted.org/packages/5c/70/81830b59df7682917d7a10f833c4dab2a5574cd664e86d18139f2b421329/cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7", size = 3468288, upload-time = "2026-01-28T00:23:55.09Z" }, - { url = "https://files.pythonhosted.org/packages/56/f7/f648fdbb61d0d45902d3f374217451385edc7e7768d1b03ff1d0e5ffc17b/cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab", size = 7169583, upload-time = "2026-01-28T00:23:56.558Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cc/8f3224cbb2a928de7298d6ed4790f5ebc48114e02bdc9559196bfb12435d/cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef", size = 4275419, upload-time = "2026-01-28T00:23:58.364Z" }, - { url = "https://files.pythonhosted.org/packages/17/43/4a18faa7a872d00e4264855134ba82d23546c850a70ff209e04ee200e76f/cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d", size = 4419058, upload-time = "2026-01-28T00:23:59.867Z" }, - { url = "https://files.pythonhosted.org/packages/ee/64/6651969409821d791ba12346a124f55e1b76f66a819254ae840a965d4b9c/cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973", size = 4278151, upload-time = "2026-01-28T00:24:01.731Z" }, - { url = "https://files.pythonhosted.org/packages/20/0b/a7fce65ee08c3c02f7a8310cc090a732344066b990ac63a9dfd0a655d321/cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4", size = 4939441, upload-time = "2026-01-28T00:24:03.175Z" }, - { url = "https://files.pythonhosted.org/packages/db/a7/20c5701e2cd3e1dfd7a19d2290c522a5f435dd30957d431dcb531d0f1413/cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af", size = 4451617, upload-time = "2026-01-28T00:24:05.403Z" }, - { url = "https://files.pythonhosted.org/packages/00/dc/3e16030ea9aa47b63af6524c354933b4fb0e352257c792c4deeb0edae367/cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263", size = 3977774, upload-time = "2026-01-28T00:24:06.851Z" }, - { url = "https://files.pythonhosted.org/packages/42/c8/ad93f14118252717b465880368721c963975ac4b941b7ef88f3c56bf2897/cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095", size = 4277008, upload-time = "2026-01-28T00:24:08.926Z" }, - { url = "https://files.pythonhosted.org/packages/00/cf/89c99698151c00a4631fbfcfcf459d308213ac29e321b0ff44ceeeac82f1/cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b", size = 4903339, upload-time = "2026-01-28T00:24:12.009Z" }, - { url = "https://files.pythonhosted.org/packages/03/c3/c90a2cb358de4ac9309b26acf49b2a100957e1ff5cc1e98e6c4996576710/cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019", size = 4451216, upload-time = "2026-01-28T00:24:13.975Z" }, - { url = "https://files.pythonhosted.org/packages/96/2c/8d7f4171388a10208671e181ca43cdc0e596d8259ebacbbcfbd16de593da/cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4", size = 4404299, upload-time = "2026-01-28T00:24:16.169Z" }, - { url = "https://files.pythonhosted.org/packages/e9/23/cbb2036e450980f65c6e0a173b73a56ff3bccd8998965dea5cc9ddd424a5/cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b", size = 4664837, upload-time = "2026-01-28T00:24:17.629Z" }, - { url = "https://files.pythonhosted.org/packages/0a/21/f7433d18fe6d5845329cbdc597e30caf983229c7a245bcf54afecc555938/cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc", size = 3009779, upload-time = "2026-01-28T00:24:20.198Z" }, - { url = "https://files.pythonhosted.org/packages/3a/6a/bd2e7caa2facffedf172a45c1a02e551e6d7d4828658c9a245516a598d94/cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976", size = 3466633, upload-time = "2026-01-28T00:24:21.851Z" }, -] - -[[package]] -name = "cyclopts" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "docstring-parser" }, - { name = "rich" }, - { name = "rich-rst" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d4/93/6085aa89c3fff78a5180987354538d72e43b0db27e66a959302d0c07821a/cyclopts-4.5.1.tar.gz", hash = "sha256:fadc45304763fd9f5d6033727f176898d17a1778e194436964661a005078a3dd", size = 162075, upload-time = "2026-01-25T15:23:54.07Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/7c/996760c30f1302704af57c66ff2d723f7d656d0d0b93563b5528a51484bb/cyclopts-4.5.1-py3-none-any.whl", hash = "sha256:0642c93601e554ca6b7b9abd81093847ea4448b2616280f2a0952416574e8c7a", size = 199807, upload-time = "2026-01-25T15:23:55.219Z" }, -] - -[[package]] -name = "diskcache" -version = "5.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, -] - -[[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.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, -] - -[[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.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "fakeredis" -version = "2.33.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "redis" }, - { name = "sortedcontainers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/f9/57464119936414d60697fcbd32f38909bb5688b616ae13de6e98384433e0/fakeredis-2.33.0.tar.gz", hash = "sha256:d7bc9a69d21df108a6451bbffee23b3eba432c21a654afc7ff2d295428ec5770", size = 175187, upload-time = "2025-12-16T19:45:52.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/78/a850fed8aeef96d4a99043c90b818b2ed5419cd5b24a4049fd7cfb9f1471/fakeredis-2.33.0-py3-none-any.whl", hash = "sha256:de535f3f9ccde1c56672ab2fdd6a8efbc4f2619fc2f1acc87b8737177d71c965", size = 119605, upload-time = "2025-12-16T19:45:51.08Z" }, -] - -[package.optional-dependencies] -lua = [ - { name = "lupa" }, -] - -[[package]] -name = "fastmcp" -version = "2.14.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "authlib" }, - { name = "cyclopts" }, - { name = "exceptiongroup" }, - { name = "httpx" }, - { name = "jsonref" }, - { name = "jsonschema-path" }, - { name = "mcp" }, - { name = "openapi-pydantic" }, - { name = "packaging" }, - { name = "platformdirs" }, - { name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] }, - { name = "pydantic", extra = ["email"] }, - { name = "pydocket" }, - { name = "pyperclip" }, - { name = "python-dotenv" }, - { name = "rich" }, - { name = "uvicorn" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/a9/a57d5e5629ebd4ef82b495a7f8e346ce29ef80cc86b15c8c40570701b94d/fastmcp-2.14.4.tar.gz", hash = "sha256:c01f19845c2adda0a70d59525c9193be64a6383014c8d40ce63345ac664053ff", size = 8302239, upload-time = "2026-01-22T17:29:37.024Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/41/c4d407e2218fd60d84acb6cc5131d28ff876afecf325e3fd9d27b8318581/fastmcp-2.14.4-py3-none-any.whl", hash = "sha256:5858cff5e4c8ea8107f9bca2609d71d6256e0fce74495912f6e51625e466c49a", size = 417788, upload-time = "2026-01-22T17:29:35.159Z" }, -] - -[[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 = "hanzo-tools-api" -version = "0.3.1" -source = { editable = "." } -dependencies = [ - { name = "aiofiles" }, - { name = "hanzo-tools-core" }, - { name = "httpx" }, - { name = "pydantic" }, - { name = "pyyaml" }, -] - -[package.optional-dependencies] -dev = [ - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "respx" }, -] - -[package.metadata] -requires-dist = [ - { name = "aiofiles", specifier = ">=24.1.0" }, - { name = "hanzo-tools-core", specifier = ">=0.1.0" }, - { name = "httpx", specifier = ">=0.27.0" }, - { name = "pydantic", specifier = ">=2.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" }, - { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24.0" }, - { name = "pyyaml", specifier = ">=6.0" }, - { name = "respx", marker = "extra == 'dev'", specifier = ">=0.22.0" }, -] -provides-extras = ["dev"] - -[[package]] -name = "hanzo-tools-core" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a9/67/eabf2d355819b9c948a9898ed537fa014a5de0422de66e0ea4203faae6be/hanzo_tools_core-0.2.0.tar.gz", hash = "sha256:ab5352056d3db1d42aadd94d81635eb835476194562b07b497f327f6d334c058", size = 11441, upload-time = "2025-12-26T14:46:12.281Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/16/c1a705434afc5936156eadedcc29e1200fb4871e7a1b83d5efeebefbfcda/hanzo_tools_core-0.2.0-py3-none-any.whl", hash = "sha256:e07cdc3692003e30a40082be3750a0670b2adf612e3e7a56dd741d3b532b8090", size = 11026, upload-time = "2025-12-26T14:46:11.514Z" }, -] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, -] - -[[package]] -name = "jaraco-context" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" }, -] - -[[package]] -name = "jaraco-functools" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, -] - -[[package]] -name = "jsonref" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[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.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "keyring" -version = "25.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, -] - -[[package]] -name = "lupa" -version = "2.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/1c/191c3e6ec6502e3dbe25a53e27f69a5daeac3e56de1f73c0138224171ead/lupa-2.6.tar.gz", hash = "sha256:9a770a6e89576be3447668d7ced312cd6fd41d3c13c2462c9dc2c2ab570e45d9", size = 7240282, upload-time = "2025-10-24T07:20:29.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/86/ce243390535c39d53ea17ccf0240815e6e457e413e40428a658ea4ee4b8d/lupa-2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47ce718817ef1cc0c40d87c3d5ae56a800d61af00fbc0fad1ca9be12df2f3b56", size = 951707, upload-time = "2025-10-24T07:18:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/cedea5e6cbeb54396fdcc55f6b741696f3f036d23cfaf986d50d680446da/lupa-2.6-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:7aba985b15b101495aa4b07112cdc08baa0c545390d560ad5cfde2e9e34f4d58", size = 1916703, upload-time = "2025-10-24T07:18:05.6Z" }, - { url = "https://files.pythonhosted.org/packages/24/be/3d6b5f9a8588c01a4d88129284c726017b2089f3a3fd3ba8bd977292fea0/lupa-2.6-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:b766f62f95b2739f2248977d29b0722e589dcf4f0ccfa827ccbd29f0148bd2e5", size = 985152, upload-time = "2025-10-24T07:18:08.561Z" }, - { url = "https://files.pythonhosted.org/packages/eb/23/9f9a05beee5d5dce9deca4cb07c91c40a90541fc0a8e09db4ee670da550f/lupa-2.6-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:00a934c23331f94cb51760097ebfab14b005d55a6b30a2b480e3c53dd2fa290d", size = 1159599, upload-time = "2025-10-24T07:18:10.346Z" }, - { url = "https://files.pythonhosted.org/packages/40/4e/e7c0583083db9d7f1fd023800a9767d8e4391e8330d56c2373d890ac971b/lupa-2.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21de9f38bd475303e34a042b7081aabdf50bd9bafd36ce4faea2f90fd9f15c31", size = 1038686, upload-time = "2025-10-24T07:18:12.112Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/5a4f7d959d4feba5e203ff0c31889e74d1ca3153122be4a46dca7d92bf7c/lupa-2.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf3bda96d3fc41237e964a69c23647d50d4e28421111360274d4799832c560e9", size = 2071956, upload-time = "2025-10-24T07:18:14.572Z" }, - { url = "https://files.pythonhosted.org/packages/92/34/2f4f13ca65d01169b1720176aedc4af17bc19ee834598c7292db232cb6dc/lupa-2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a76ead245da54801a81053794aa3975f213221f6542d14ec4b859ee2e7e0323", size = 1057199, upload-time = "2025-10-24T07:18:16.379Z" }, - { url = "https://files.pythonhosted.org/packages/35/2a/5f7d2eebec6993b0dcd428e0184ad71afb06a45ba13e717f6501bfed1da3/lupa-2.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8dd0861741caa20886ddbda0a121d8e52fb9b5bb153d82fa9bba796962bf30e8", size = 1173693, upload-time = "2025-10-24T07:18:18.153Z" }, - { url = "https://files.pythonhosted.org/packages/e4/29/089b4d2f8e34417349af3904bb40bec40b65c8731f45e3fd8d497ca573e5/lupa-2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:239e63948b0b23023f81d9a19a395e768ed3da6a299f84e7963b8f813f6e3f9c", size = 2164394, upload-time = "2025-10-24T07:18:20.403Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1b/79c17b23c921f81468a111cad843b076a17ef4b684c4a8dff32a7969c3f0/lupa-2.6-cp312-cp312-win32.whl", hash = "sha256:325894e1099499e7a6f9c351147661a2011887603c71086d36fe0f964d52d1ce", size = 1420647, upload-time = "2025-10-24T07:18:23.368Z" }, - { url = "https://files.pythonhosted.org/packages/b8/15/5121e68aad3584e26e1425a5c9a79cd898f8a152292059e128c206ee817c/lupa-2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c735a1ce8ee60edb0fe71d665f1e6b7c55c6021f1d340eb8c865952c602cd36f", size = 1688529, upload-time = "2025-10-24T07:18:25.523Z" }, - { url = "https://files.pythonhosted.org/packages/28/1d/21176b682ca5469001199d8b95fa1737e29957a3d185186e7a8b55345f2e/lupa-2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:663a6e58a0f60e7d212017d6678639ac8df0119bc13c2145029dcba084391310", size = 947232, upload-time = "2025-10-24T07:18:27.878Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4c/d327befb684660ca13cf79cd1f1d604331808f9f1b6fb6bf57832f8edf80/lupa-2.6-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:d1f5afda5c20b1f3217a80e9bc1b77037f8a6eb11612fd3ada19065303c8f380", size = 1908625, upload-time = "2025-10-24T07:18:29.944Z" }, - { url = "https://files.pythonhosted.org/packages/66/8e/ad22b0a19454dfd08662237a84c792d6d420d36b061f239e084f29d1a4f3/lupa-2.6-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:26f2b3c085fe76e9119e48c1013c1cccdc1f51585d456858290475aa38e7089e", size = 981057, upload-time = "2025-10-24T07:18:31.553Z" }, - { url = "https://files.pythonhosted.org/packages/5c/48/74859073ab276bd0566c719f9ca0108b0cfc1956ca0d68678d117d47d155/lupa-2.6-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:60d2f902c7b96fb8ab98493dcff315e7bb4d0b44dc9dd76eb37de575025d5685", size = 1156227, upload-time = "2025-10-24T07:18:33.981Z" }, - { url = "https://files.pythonhosted.org/packages/09/6c/0e9ded061916877253c2266074060eb71ed99fb21d73c8c114a76725bce2/lupa-2.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a02d25dee3a3250967c36590128d9220ae02f2eda166a24279da0b481519cbff", size = 1035752, upload-time = "2025-10-24T07:18:36.32Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ef/f8c32e454ef9f3fe909f6c7d57a39f950996c37a3deb7b391fec7903dab7/lupa-2.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eae1ee16b886b8914ff292dbefbf2f48abfbdee94b33a88d1d5475e02423203", size = 2069009, upload-time = "2025-10-24T07:18:38.072Z" }, - { url = "https://files.pythonhosted.org/packages/53/dc/15b80c226a5225815a890ee1c11f07968e0aba7a852df41e8ae6fe285063/lupa-2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0edd5073a4ee74ab36f74fe61450148e6044f3952b8d21248581f3c5d1a58be", size = 1056301, upload-time = "2025-10-24T07:18:40.165Z" }, - { url = "https://files.pythonhosted.org/packages/31/14/2086c1425c985acfb30997a67e90c39457122df41324d3c179d6ee2292c6/lupa-2.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c53ee9f22a8a17e7d4266ad48e86f43771951797042dd51d1494aaa4f5f3f0a", size = 1170673, upload-time = "2025-10-24T07:18:42.426Z" }, - { url = "https://files.pythonhosted.org/packages/10/e5/b216c054cf86576c0191bf9a9f05de6f7e8e07164897d95eea0078dca9b2/lupa-2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de7c0f157a9064a400d828789191a96da7f4ce889969a588b87ec80de9b14772", size = 2162227, upload-time = "2025-10-24T07:18:46.112Z" }, - { url = "https://files.pythonhosted.org/packages/59/2f/33ecb5bedf4f3bc297ceacb7f016ff951331d352f58e7e791589609ea306/lupa-2.6-cp313-cp313-win32.whl", hash = "sha256:ee9523941ae0a87b5b703417720c5d78f72d2f5bc23883a2ea80a949a3ed9e75", size = 1419558, upload-time = "2025-10-24T07:18:48.371Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b4/55e885834c847ea610e111d87b9ed4768f0afdaeebc00cd46810f25029f6/lupa-2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b1335a5835b0a25ebdbc75cf0bda195e54d133e4d994877ef025e218c2e59db9", size = 1683424, upload-time = "2025-10-24T07:18:50.976Z" }, - { url = "https://files.pythonhosted.org/packages/66/9d/d9427394e54d22a35d1139ef12e845fd700d4872a67a34db32516170b746/lupa-2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dcb6d0a3264873e1653bc188499f48c1fb4b41a779e315eba45256cfe7bc33c1", size = 953818, upload-time = "2025-10-24T07:18:53.378Z" }, - { url = "https://files.pythonhosted.org/packages/10/41/27bbe81953fb2f9ecfced5d9c99f85b37964cfaf6aa8453bb11283983721/lupa-2.6-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:a37e01f2128f8c36106726cb9d360bac087d58c54b4522b033cc5691c584db18", size = 1915850, upload-time = "2025-10-24T07:18:55.259Z" }, - { url = "https://files.pythonhosted.org/packages/a3/98/f9ff60db84a75ba8725506bbf448fb085bc77868a021998ed2a66d920568/lupa-2.6-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:458bd7e9ff3c150b245b0fcfbb9bd2593d1152ea7f0a7b91c1d185846da033fe", size = 982344, upload-time = "2025-10-24T07:18:57.05Z" }, - { url = "https://files.pythonhosted.org/packages/41/f7/f39e0f1c055c3b887d86b404aaf0ca197b5edfd235a8b81b45b25bac7fc3/lupa-2.6-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:052ee82cac5206a02df77119c325339acbc09f5ce66967f66a2e12a0f3211cad", size = 1156543, upload-time = "2025-10-24T07:18:59.251Z" }, - { url = "https://files.pythonhosted.org/packages/9e/9c/59e6cffa0d672d662ae17bd7ac8ecd2c89c9449dee499e3eb13ca9cd10d9/lupa-2.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96594eca3c87dd07938009e95e591e43d554c1dbd0385be03c100367141db5a8", size = 1047974, upload-time = "2025-10-24T07:19:01.449Z" }, - { url = "https://files.pythonhosted.org/packages/23/c6/a04e9cef7c052717fcb28fb63b3824802488f688391895b618e39be0f684/lupa-2.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8faddd9d198688c8884091173a088a8e920ecc96cda2ffed576a23574c4b3f6", size = 2073458, upload-time = "2025-10-24T07:19:03.369Z" }, - { url = "https://files.pythonhosted.org/packages/e6/10/824173d10f38b51fc77785228f01411b6ca28826ce27404c7c912e0e442c/lupa-2.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:daebb3a6b58095c917e76ba727ab37b27477fb926957c825205fbda431552134", size = 1067683, upload-time = "2025-10-24T07:19:06.2Z" }, - { url = "https://files.pythonhosted.org/packages/b6/dc/9692fbcf3c924d9c4ece2d8d2f724451ac2e09af0bd2a782db1cef34e799/lupa-2.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f3154e68972befe0f81564e37d8142b5d5d79931a18309226a04ec92487d4ea3", size = 1171892, upload-time = "2025-10-24T07:19:08.544Z" }, - { url = "https://files.pythonhosted.org/packages/84/ff/e318b628d4643c278c96ab3ddea07fc36b075a57383c837f5b11e537ba9d/lupa-2.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e4dadf77b9fedc0bfa53417cc28dc2278a26d4cbd95c29f8927ad4d8fe0a7ef9", size = 2166641, upload-time = "2025-10-24T07:19:10.485Z" }, - { url = "https://files.pythonhosted.org/packages/12/f7/a6f9ec2806cf2d50826980cdb4b3cffc7691dc6f95e13cc728846d5cb793/lupa-2.6-cp314-cp314-win32.whl", hash = "sha256:cb34169c6fa3bab3e8ac58ca21b8a7102f6a94b6a5d08d3636312f3f02fafd8f", size = 1456857, upload-time = "2025-10-24T07:19:37.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/de/df71896f25bdc18360fdfa3b802cd7d57d7fede41a0e9724a4625b412c85/lupa-2.6-cp314-cp314-win_amd64.whl", hash = "sha256:b74f944fe46c421e25d0f8692aef1e842192f6f7f68034201382ac440ef9ea67", size = 1731191, upload-time = "2025-10-24T07:19:40.281Z" }, - { url = "https://files.pythonhosted.org/packages/47/3c/a1f23b01c54669465f5f4c4083107d496fbe6fb45998771420e9aadcf145/lupa-2.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0e21b716408a21ab65723f8841cf7f2f37a844b7a965eeabb785e27fca4099cf", size = 999343, upload-time = "2025-10-24T07:19:12.519Z" }, - { url = "https://files.pythonhosted.org/packages/c5/6d/501994291cb640bfa2ccf7f554be4e6914afa21c4026bd01bff9ca8aac57/lupa-2.6-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:589db872a141bfff828340079bbdf3e9a31f2689f4ca0d88f97d9e8c2eae6142", size = 2000730, upload-time = "2025-10-24T07:19:14.869Z" }, - { url = "https://files.pythonhosted.org/packages/53/a5/457ffb4f3f20469956c2d4c4842a7675e884efc895b2f23d126d23e126cc/lupa-2.6-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:cd852a91a4a9d4dcbb9a58100f820a75a425703ec3e3f049055f60b8533b7953", size = 1021553, upload-time = "2025-10-24T07:19:17.123Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/36bb5a5d0960f2a5c7c700e0819abb76fd9bf9c1d8a66e5106416d6e9b14/lupa-2.6-cp314-cp314t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:0334753be028358922415ca97a64a3048e4ed155413fc4eaf87dd0a7e2752983", size = 1133275, upload-time = "2025-10-24T07:19:20.51Z" }, - { url = "https://files.pythonhosted.org/packages/19/86/202ff4429f663013f37d2229f6176ca9f83678a50257d70f61a0a97281bf/lupa-2.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:661d895cd38c87658a34780fac54a690ec036ead743e41b74c3fb81a9e65a6aa", size = 1038441, upload-time = "2025-10-24T07:19:22.509Z" }, - { url = "https://files.pythonhosted.org/packages/a7/42/d8125f8e420714e5b52e9c08d88b5329dfb02dcca731b4f21faaee6cc5b5/lupa-2.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aa58454ccc13878cc177c62529a2056be734da16369e451987ff92784994ca7", size = 2058324, upload-time = "2025-10-24T07:19:24.979Z" }, - { url = "https://files.pythonhosted.org/packages/2b/2c/47bf8b84059876e877a339717ddb595a4a7b0e8740bacae78ba527562e1c/lupa-2.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1425017264e470c98022bba8cff5bd46d054a827f5df6b80274f9cc71dafd24f", size = 1060250, upload-time = "2025-10-24T07:19:27.262Z" }, - { url = "https://files.pythonhosted.org/packages/c2/06/d88add2b6406ca1bdec99d11a429222837ca6d03bea42ca75afa169a78cb/lupa-2.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:224af0532d216e3105f0a127410f12320f7c5f1aa0300bdf9646b8d9afb0048c", size = 1151126, upload-time = "2025-10-24T07:19:29.522Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a0/89e6a024c3b4485b89ef86881c9d55e097e7cb0bdb74efb746f2fa6a9a76/lupa-2.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9abb98d5a8fd27c8285302e82199f0e56e463066f88f619d6594a450bf269d80", size = 2153693, upload-time = "2025-10-24T07:19:31.379Z" }, - { url = "https://files.pythonhosted.org/packages/b6/36/a0f007dc58fc1bbf51fb85dcc82fcb1f21b8c4261361de7dab0e3d8521ef/lupa-2.6-cp314-cp314t-win32.whl", hash = "sha256:1849efeba7a8f6fb8aa2c13790bee988fd242ae404bd459509640eeea3d1e291", size = 1590104, upload-time = "2025-10-24T07:19:33.514Z" }, - { url = "https://files.pythonhosted.org/packages/7d/5e/db903ce9cf82c48d6b91bf6d63ae4c8d0d17958939a4e04ba6b9f38b8643/lupa-2.6-cp314-cp314t-win_amd64.whl", hash = "sha256:fc1498d1a4fc028bc521c26d0fad4ca00ed63b952e32fb95949bda76a04bad52", size = 1913818, upload-time = "2025-10-24T07:19:36.039Z" }, -] - -[[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 = "mcp" -version = "1.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyjwt", extra = ["crypto"] }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, -] - -[[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 = "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 = "opentelemetry-api" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, -] - -[[package]] -name = "opentelemetry-exporter-prometheus" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-sdk" }, - { name = "prometheus-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/14/39/7dafa6fff210737267bed35a8855b6ac7399b9e582b8cf1f25f842517012/opentelemetry_exporter_prometheus-0.60b1.tar.gz", hash = "sha256:a4011b46906323f71724649d301b4dc188aaa068852e814f4df38cc76eac616b", size = 14976, upload-time = "2025-12-11T13:32:42.944Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/0d/4be6bf5477a3eb3d917d2f17d3c0b6720cd6cb97898444a61d43cc983f5c/opentelemetry_exporter_prometheus-0.60b1-py3-none-any.whl", hash = "sha256:49f59178de4f4590e3cef0b8b95cf6e071aae70e1f060566df5546fad773b8fd", size = 13019, upload-time = "2025-12-11T13:32:23.974Z" }, -] - -[[package]] -name = "opentelemetry-instrumentation" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "packaging" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/0f/7e6b713ac117c1f5e4e3300748af699b9902a2e5e34c9cf443dde25a01fa/opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a", size = 31706, upload-time = "2025-12-11T13:36:42.515Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096, upload-time = "2025-12-11T13:35:33.067Z" }, -] - -[[package]] -name = "opentelemetry-sdk" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, -] - -[[package]] -name = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[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 = "pathvalidate" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262, upload-time = "2025-06-15T09:07:20.736Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305, upload-time = "2025-06-15T09:07:19.117Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, -] - -[[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 = "prometheus-client" -version = "0.24.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616, upload-time = "2026-01-14T15:26:26.965Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057, upload-time = "2026-01-14T15:26:24.42Z" }, -] - -[[package]] -name = "py-key-value-aio" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "py-key-value-shared" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/ce/3136b771dddf5ac905cc193b461eb67967cf3979688c6696e1f2cdcde7ea/py_key_value_aio-0.3.0.tar.gz", hash = "sha256:858e852fcf6d696d231266da66042d3355a7f9871650415feef9fca7a6cd4155", size = 50801, upload-time = "2025-11-17T16:50:04.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/10/72f6f213b8f0bce36eff21fda0a13271834e9eeff7f9609b01afdc253c79/py_key_value_aio-0.3.0-py3-none-any.whl", hash = "sha256:1c781915766078bfd608daa769fefb97e65d1d73746a3dfb640460e322071b64", size = 96342, upload-time = "2025-11-17T16:50:03.801Z" }, -] - -[package.optional-dependencies] -disk = [ - { name = "diskcache" }, - { name = "pathvalidate" }, -] -keyring = [ - { name = "keyring" }, -] -memory = [ - { name = "cachetools" }, -] -redis = [ - { name = "redis" }, -] - -[[package]] -name = "py-key-value-shared" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/e4/1971dfc4620a3a15b4579fe99e024f5edd6e0967a71154771a059daff4db/py_key_value_shared-0.3.0.tar.gz", hash = "sha256:8fdd786cf96c3e900102945f92aa1473138ebe960ef49da1c833790160c28a4b", size = 11666, upload-time = "2025-11-17T16:50:06.849Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e4/b8b0a03ece72f47dce2307d36e1c34725b7223d209fc679315ffe6a4e2c3/py_key_value_shared-0.3.0-py3-none-any.whl", hash = "sha256:5b0efba7ebca08bb158b1e93afc2f07d30b8f40c2fc12ce24a4c0d84f42f9298", size = 19560, upload-time = "2025-11-17T16:50:05.954Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[package.optional-dependencies] -email = [ - { name = "email-validator" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, -] - -[[package]] -name = "pydantic-settings" -version = "2.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, -] - -[[package]] -name = "pydocket" -version = "0.16.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cloudpickle" }, - { name = "fakeredis", extra = ["lua"] }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-prometheus" }, - { name = "opentelemetry-instrumentation" }, - { name = "prometheus-client" }, - { name = "py-key-value-aio", extra = ["memory", "redis"] }, - { name = "python-json-logger" }, - { name = "redis" }, - { name = "rich" }, - { name = "typer" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/00/26befe5f58df7cd1aeda4a8d10bc7d1908ffd86b80fd995e57a2a7b3f7bd/pydocket-0.16.6.tar.gz", hash = "sha256:b96c96ad7692827214ed4ff25fcf941ec38371314db5dcc1ae792b3e9d3a0294", size = 299054, upload-time = "2026-01-09T22:09:15.405Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/3f/7483e5a6dc6326b6e0c640619b5c5bd1d6e3c20e54d58f5fb86267cef00e/pydocket-0.16.6-py3-none-any.whl", hash = "sha256:683d21e2e846aa5106274e7d59210331b242d7fb0dce5b08d3b82065663ed183", size = 67697, upload-time = "2026-01-09T22:09:13.436Z" }, -] - -[[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 = "pyjwt" -version = "2.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, -] - -[package.optional-dependencies] -crypto = [ - { name = "cryptography" }, -] - -[[package]] -name = "pyperclip" -version = "1.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, -] - -[[package]] -name = "pytest" -version = "9.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, -] - -[[package]] -name = "pytest-asyncio" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, -] - -[[package]] -name = "python-json-logger" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, -] - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { 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 = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "redis" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/c8/983d5c6579a411d8a99bc5823cc5712768859b5ce2c8afe1a65b37832c81/redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c", size = 4796669, upload-time = "2025-11-19T15:54:39.961Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/f0/8956f8a86b20d7bb9d6ac0187cf4cd54d8065bc9a1a09eb8011d4d326596/redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b", size = 354159, upload-time = "2025-11-19T15:54:38.064Z" }, -] - -[[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 = "rich" -version = "14.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" }, -] - -[[package]] -name = "rich-rst" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, -] - -[[package]] -name = "secretstorage" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[package]] -name = "sortedcontainers" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, -] - -[[package]] -name = "sse-starlette" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "starlette" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/00d280c03ffd39aaee0e86ec81e2d3b9253036a0f93f51d10503adef0e65/sse_starlette-3.2.0.tar.gz", hash = "sha256:8127594edfb51abe44eac9c49e59b0b01f1039d0c7461c6fd91d4e03b70da422", size = 27253, upload-time = "2026-01-17T13:11:05.62Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/7f/832f015020844a8b8f7a9cbc103dd76ba8e3875004c41e08440ea3a2b41a/sse_starlette-3.2.0-py3-none-any.whl", hash = "sha256:5876954bd51920fc2cd51baee47a080eb88a37b5b784e615abb0b283f801cdbf", size = 12763, upload-time = "2026-01-17T13:11:03.775Z" }, -] - -[[package]] -name = "starlette" -version = "0.52.1" -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/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, -] - -[[package]] -name = "typer" -version = "0.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.40.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, -] - -[[package]] -name = "websockets" -version = "16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, - { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, - { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, - { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, - { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, - { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, - { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, - { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, - { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, - { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, -] - -[[package]] -name = "wrapt" -version = "1.17.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998, upload-time = "2025-08-12T05:51:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020, upload-time = "2025-08-12T05:51:35.906Z" }, - { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098, upload-time = "2025-08-12T05:51:57.474Z" }, - { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036, upload-time = "2025-08-12T05:52:34.784Z" }, - { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156, upload-time = "2025-08-12T05:52:13.599Z" }, - { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102, upload-time = "2025-08-12T05:52:14.56Z" }, - { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732, upload-time = "2025-08-12T05:52:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705, upload-time = "2025-08-12T05:53:07.123Z" }, - { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877, upload-time = "2025-08-12T05:53:05.436Z" }, - { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885, upload-time = "2025-08-12T05:52:54.367Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, - { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, - { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, - { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, - { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, - { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, - { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, - { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, - { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, - { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, - { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, - { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, - { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, - { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, - { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, - { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, - { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, - { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, - { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, -] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] diff --git a/pkg/hanzo-tools-auth/README.md b/pkg/hanzo-tools-auth/README.md deleted file mode 100644 index 5319fcd26..000000000 --- a/pkg/hanzo-tools-auth/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# hanzo-tools-auth - -Authentication bridge for Hanzo MCP platform tools. diff --git a/pkg/hanzo-tools-auth/hanzo_tools/__init__.py b/pkg/hanzo-tools-auth/hanzo_tools/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-tools-auth/hanzo_tools/auth/__init__.py b/pkg/hanzo-tools-auth/hanzo_tools/auth/__init__.py deleted file mode 100644 index 5ce8f7880..000000000 --- a/pkg/hanzo-tools-auth/hanzo_tools/auth/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Hanzo Auth Tools โ€” authentication bridge for MCP platform tools. - -Provides the HanzoSession singleton for shared auth state, and -the LoginTool MCP tool for auth management. -""" - -from .session import HanzoSession -from .login_tool import LoginTool - -# Tools list for entry point discovery -TOOLS = [LoginTool] - -__all__ = [ - "LoginTool", - "HanzoSession", - "TOOLS", -] diff --git a/pkg/hanzo-tools-auth/hanzo_tools/auth/login_tool.py b/pkg/hanzo-tools-auth/hanzo_tools/auth/login_tool.py deleted file mode 100644 index c8a1b60e5..000000000 --- a/pkg/hanzo-tools-auth/hanzo_tools/auth/login_tool.py +++ /dev/null @@ -1,170 +0,0 @@ -"""MCP tool for Hanzo authentication management. - -Provides status, login (browser flow), logout, and whoami actions -accessible via Claude Code or any MCP client. -""" - -from __future__ import annotations - -import json -import logging -from typing import Any, Annotated, final - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core.base import BaseTool - -from .session import HanzoSession - -logger = logging.getLogger(__name__) - -DESCRIPTION = """Hanzo authentication management. - -Manage your Hanzo platform authentication. Check auth status, view current user -info, or logout. - -Actions: -- status: Show current authentication state and accessible services -- whoami: Show current user info from IAM token -- logout: Clear stored credentials -- refresh: Refresh an expired token -""" - - -@final -class LoginTool(BaseTool): - """MCP tool for authentication operations.""" - - @property - def name(self) -> str: - return "auth" - - @property - def description(self) -> str: - return DESCRIPTION - - async def call( - self, - ctx: MCPContext, - action: str = "status", - **kwargs: Any, - ) -> str: - session = HanzoSession.get() - - if action == "status": - return await self._status(session) - elif action == "whoami": - return await self._whoami(session) - elif action == "logout": - return await self._logout(session) - elif action == "refresh": - return await self._refresh(session) - else: - return json.dumps({"error": f"Unknown action: {action}. Use: status, whoami, logout, refresh"}) - - async def _status(self, session: HanzoSession) -> str: - info = session.get_token_info() - - if not info.get("authenticated"): - return json.dumps({ - "authenticated": False, - "message": "Not authenticated. Run 'hanzo login' in your terminal to authenticate.", - "services": { - "iam": False, - "kms": _check_kms_env(), - "paas": False, - }, - }, indent=2) - - services = { - "iam": True, - "kms": _check_kms_env(), - "paas": info.get("authenticated", False), - } - - return json.dumps({ - "authenticated": True, - "source": info.get("source", "unknown"), - "organization": info.get("organization"), - "server_url": info.get("server_url"), - "expired": info.get("expired", False), - "services": services, - }, indent=2) - - async def _whoami(self, session: HanzoSession) -> str: - if not session.is_authenticated(): - return json.dumps({"error": "Not authenticated. Run 'hanzo login' first."}) - - try: - iam_client = session.get_iam_client() - token = session.get_iam_token() - if token: - # Try to decode JWT claims (without verification for display) - try: - import jwt - - claims = jwt.decode(token, options={"verify_signature": False}) - return json.dumps({ - "sub": claims.get("sub"), - "name": claims.get("name"), - "email": claims.get("email"), - "organization": claims.get("owner"), - "iss": claims.get("iss"), - }, indent=2) - except Exception: - pass - - # Fallback to stored token info - info = session.get_token_info() - return json.dumps({ - "organization": info.get("organization"), - "server_url": info.get("server_url"), - "source": info.get("source"), - }, indent=2) - - except Exception as e: - return json.dumps({"error": f"Failed to get user info: {e}"}) - - async def _logout(self, session: HanzoSession) -> str: - session.logout() - session.close() - HanzoSession.reset() - return json.dumps({"message": "Logged out. Cleared stored credentials."}) - - async def _refresh(self, session: HanzoSession) -> str: - if not session.is_authenticated(): - return json.dumps({"error": "Not authenticated. Run 'hanzo login' first."}) - - if session.refresh_token(): - return json.dumps({"message": "Token refreshed successfully."}) - else: - return json.dumps({"error": "Token refresh failed. Run 'hanzo login' again."}) - - def register(self, mcp_server: FastMCP) -> None: - """Register auth tool with explicit parameters.""" - tool_instance = self - - @mcp_server.tool( - name="auth", - description=DESCRIPTION, - ) - async def auth( - action: Annotated[ - str, - Field( - description="Action: status (check auth state), whoami (current user), logout, refresh", - default="status", - ), - ] = "status", - ctx: MCPContext = None, - ) -> str: - return await tool_instance.call(ctx, action=action) - - -def _check_kms_env() -> bool: - """Check if KMS credentials are available.""" - import os - - return bool(os.getenv("HANZO_KMS_CLIENT_ID") and os.getenv("HANZO_KMS_CLIENT_SECRET")) diff --git a/pkg/hanzo-tools-auth/hanzo_tools/auth/session.py b/pkg/hanzo-tools-auth/hanzo_tools/auth/session.py deleted file mode 100644 index 25d3f9896..000000000 --- a/pkg/hanzo-tools-auth/hanzo_tools/auth/session.py +++ /dev/null @@ -1,393 +0,0 @@ -"""Hanzo authentication session โ€” shared auth bridge for MCP platform tools. - -Loads IAM tokens from disk or environment, auto-refreshes expired tokens, -and provides authenticated service clients (KMS, PaaS, IAM). - -Token resolution order: -1. HANZO_AUTH_TOKEN env var (explicit override) -2. HANZO_API_KEY env var (API key auth) -3. ~/.hanzo/auth/token.json (from `hanzo login`) -""" - -from __future__ import annotations - -import os -import json -import time -import logging -from typing import Any -from pathlib import Path - -logger = logging.getLogger(__name__) - -TOKEN_DIR = Path.home() / ".hanzo" / "auth" -TOKEN_FILE = TOKEN_DIR / "token.json" - -# IAM defaults (same as hanzo-cli) -DEFAULT_IAM_URL = "https://hanzo.id" -DEFAULT_ORG = "hanzo" -DEFAULT_APP = "app-hanzo" -DEFAULT_CLIENT_ID = "hanzo-app-client-id" - - -def _env(name: str) -> str: - """Read env var (IAM_*).""" - return os.getenv(name) or "" - - -class HanzoSession: - """Singleton session providing authenticated clients to platform tools.""" - - _instance: HanzoSession | None = None - - def __init__(self) -> None: - self._token_data: dict[str, Any] | None = None - self._iam_client: Any | None = None - self._kms_client: Any | None = None - - @classmethod - def get(cls) -> HanzoSession: - """Get or create the singleton session.""" - if cls._instance is None: - cls._instance = cls() - return cls._instance - - @classmethod - def reset(cls) -> None: - """Reset the singleton (for testing).""" - if cls._instance: - cls._instance.close() - cls._instance = None - - # -- Token loading ------------------------------------------------------- - - def _load_token_from_disk(self) -> dict[str, Any] | None: - """Load stored token from ~/.hanzo/auth/token.json.""" - if not TOKEN_FILE.exists(): - return None - try: - return json.loads(TOKEN_FILE.read_text()) - except (json.JSONDecodeError, OSError): - return None - - def _save_token(self, data: dict[str, Any]) -> None: - """Save token data to disk.""" - TOKEN_DIR.mkdir(parents=True, exist_ok=True) - TOKEN_FILE.write_text(json.dumps(data, indent=2)) - TOKEN_FILE.chmod(0o600) - - def load_token(self) -> dict[str, Any] | None: - """Load token using the resolution chain. - - Returns token data dict or None if not authenticated. - """ - if self._token_data: - return self._token_data - - # 1. Explicit token override - auth_token = os.getenv("HANZO_AUTH_TOKEN") - if auth_token: - self._token_data = { - "access_token": auth_token, - "source": "env:HANZO_AUTH_TOKEN", - } - return self._token_data - - # 2. API key - api_key = os.getenv("HANZO_API_KEY") - if api_key: - self._token_data = { - "access_token": api_key, - "source": "env:HANZO_API_KEY", - } - return self._token_data - - # 3. Stored token from `hanzo login` - token_data = self._load_token_from_disk() - if token_data and token_data.get("access_token"): - token_data["source"] = "disk:~/.hanzo/auth/token.json" - self._token_data = token_data - return self._token_data - - return None - - # -- Token state --------------------------------------------------------- - - def is_authenticated(self) -> bool: - """Check if we have a valid token.""" - return self.load_token() is not None - - def get_iam_token(self) -> str | None: - """Get the current IAM access token.""" - token_data = self.load_token() - if token_data: - return token_data.get("access_token") - return None - - def get_token_info(self) -> dict[str, Any]: - """Get info about the current auth state.""" - token_data = self.load_token() - if not token_data: - return {"authenticated": False} - - info: dict[str, Any] = { - "authenticated": True, - "source": token_data.get("source", "unknown"), - } - - # Check expiry - login_time = token_data.get("login_time", 0) - expires_in = token_data.get("expires_in", 0) - if login_time and expires_in: - expires_at = login_time + expires_in - info["expires_at"] = expires_at - info["expired"] = time.time() > expires_at - - # Add org/app info if available - if token_data.get("organization"): - info["organization"] = token_data["organization"] - if token_data.get("application"): - info["application"] = token_data["application"] - if token_data.get("server_url"): - info["server_url"] = token_data["server_url"] - - return info - - # -- Token refresh ------------------------------------------------------- - - def refresh_token(self) -> bool: - """Attempt to refresh an expired token. - - Returns True if refresh succeeded. - """ - token_data = self._load_token_from_disk() - if not token_data or not token_data.get("refresh_token"): - return False - - try: - from hanzo_iam import IAMClient, IAMConfig - - config = IAMConfig( - server_url=token_data.get("server_url", DEFAULT_IAM_URL), - client_id=token_data.get("client_id", DEFAULT_CLIENT_ID), - client_secret="", - organization=token_data.get("organization", DEFAULT_ORG), - application=token_data.get("application", DEFAULT_APP), - ) - client = IAMClient(config=config) - - tokens = client.refresh_token(token_data["refresh_token"]) - client.close() - - new_data = { - **token_data, - "access_token": tokens.access_token, - "refresh_token": tokens.refresh_token or token_data["refresh_token"], - "id_token": getattr(tokens, "id_token", ""), - "expires_in": tokens.expires_in, - "login_time": int(time.time()), - } - - self._save_token(new_data) - self._token_data = new_data - self._token_data["source"] = "disk:~/.hanzo/auth/token.json" - logger.info("Token refreshed successfully") - return True - - except Exception as e: - logger.warning(f"Token refresh failed: {e}") - return False - - # -- Service clients ----------------------------------------------------- - - def get_iam_client(self) -> Any: - """Get an authenticated IAMClient.""" - if self._iam_client: - return self._iam_client - - from hanzo_iam import IAMClient, IAMConfig - - token_data = self.load_token() - if not token_data: - raise RuntimeError("Not authenticated. Run 'hanzo login' first.") - - # If we have M2M credentials - client_id = _env("IAM_CLIENT_ID") - client_secret = _env("IAM_CLIENT_SECRET") - - if client_id and client_secret: - config = IAMConfig( - server_url=_env("IAM_URL") or DEFAULT_IAM_URL, - client_id=client_id, - client_secret=client_secret, - organization=_env("IAM_ORG") or DEFAULT_ORG, - application=_env("IAM_APP") or DEFAULT_APP, - ) - self._iam_client = IAMClient(config=config) - else: - config = IAMConfig( - server_url=token_data.get("server_url", DEFAULT_IAM_URL), - client_id=token_data.get("client_id", ""), - client_secret="", - organization=token_data.get("organization", DEFAULT_ORG), - application=token_data.get("application", DEFAULT_APP), - ) - self._iam_client = IAMClient( - config=config, - bearer_token=token_data["access_token"], - ) - - return self._iam_client - - def get_kms_client(self) -> Any: - """Get an authenticated KMSClient.""" - if self._kms_client: - return self._kms_client - - from hanzo_kms import KMSClient, ClientSettings - - kms_url = os.getenv("HANZO_KMS_URL", "https://kms.hanzo.ai") - client_id = os.getenv("HANZO_KMS_CLIENT_ID", "") - client_secret = os.getenv("HANZO_KMS_CLIENT_SECRET", "") - - if client_id and client_secret: - from hanzo_kms import UniversalAuthMethod, AuthenticationOptions - - settings = ClientSettings( - site_url=kms_url, - auth=AuthenticationOptions( - universal_auth=UniversalAuthMethod( - client_id=client_id, - client_secret=client_secret, - ) - ), - ) - self._kms_client = KMSClient(settings=settings) - else: - # Fall back to default env-based construction - self._kms_client = KMSClient() - - return self._kms_client - - def get_paas_client(self) -> Any: - """Get an authenticated PaaS client via IAM token exchange.""" - import httpx - - token_data = self.load_token() - if not token_data: - raise RuntimeError("Not authenticated. Run 'hanzo login' first.") - - base_url = os.getenv("HANZO_PAAS_URL", "https://platform.hanzo.ai").rstrip("/") - iam_token = token_data["access_token"] - - # Check for cached PaaS session - session_file = Path.home() / ".hanzo" / "paas" / "session.json" - if session_file.exists(): - try: - session = json.loads(session_file.read_text()) - if session.get("at"): - # Validate cached session - with httpx.Client(base_url=base_url, timeout=10.0) as tmp: - resp = tmp.get( - "/v1/org", - headers={"Authorization": session["at"]}, - ) - if resp.status_code != 401: - return _PaaSClientWrapper(base_url, session["at"], session.get("rt")) - except Exception: - pass - - # Exchange IAM token for PaaS session - with httpx.Client(base_url=base_url, timeout=30.0) as tmp: - resp = tmp.post( - "/v1/auth/login", - json={"provider": "hanzo", "accessToken": iam_token}, - headers={"Content-Type": "application/json"}, - ) - if resp.status_code == 401: - raise RuntimeError("IAM token rejected by PaaS. Try 'hanzo login' again.") - resp.raise_for_status() - data = resp.json() - - at = data.get("at", "") - rt = data.get("rt", "") - if not at: - raise RuntimeError("PaaS login succeeded but no session token returned.") - - # Cache session - session_dir = Path.home() / ".hanzo" / "paas" - session_dir.mkdir(parents=True, exist_ok=True) - session_file = session_dir / "session.json" - session_file.write_text(json.dumps({"at": at, "rt": rt, "login_time": int(time.time())}, indent=2)) - session_file.chmod(0o600) - - return _PaaSClientWrapper(base_url, at, rt) - - # -- Lifecycle ----------------------------------------------------------- - - def close(self) -> None: - """Close all held clients.""" - if self._iam_client and hasattr(self._iam_client, "close"): - self._iam_client.close() - if self._kms_client and hasattr(self._kms_client, "close"): - self._kms_client.close() - self._iam_client = None - self._kms_client = None - self._token_data = None - - # -- Logout -------------------------------------------------------------- - - @staticmethod - def logout() -> None: - """Clear stored credentials.""" - if TOKEN_FILE.exists(): - TOKEN_FILE.unlink() - session_file = Path.home() / ".hanzo" / "paas" / "session.json" - if session_file.exists(): - session_file.unlink() - - -class _PaaSClientWrapper: - """Lightweight async-friendly wrapper over PaaS REST API.""" - - def __init__(self, base_url: str, access_token: str, refresh_token: str | None = None): - self.base_url = base_url - self._at = access_token - self._rt = refresh_token - - def _headers(self) -> dict[str, str]: - headers = {"Content-Type": "application/json", "User-Agent": "hanzo-mcp/0.1"} - if self._at: - headers["Authorization"] = self._at - if self._rt: - headers["Refresh-Token"] = self._rt - return headers - - def request(self, method: str, path: str, **kwargs: Any) -> Any: - """Make an HTTP request to PaaS API.""" - import httpx - - with httpx.Client(base_url=self.base_url, timeout=30.0) as client: - resp = client.request(method, path, headers=self._headers(), **kwargs) - if resp.status_code >= 400: - try: - err = resp.json() - msg = err.get("error", resp.text) - except Exception: - msg = resp.text - raise RuntimeError(f"PaaS error {resp.status_code}: {msg}") - if not resp.content or resp.status_code == 204: - return {} - return resp.json() - - def get(self, path: str) -> Any: - return self.request("GET", path) - - def post(self, path: str, **kwargs: Any) -> Any: - return self.request("POST", path, **kwargs) - - def put(self, path: str, **kwargs: Any) -> Any: - return self.request("PUT", path, **kwargs) - - def delete(self, path: str) -> Any: - return self.request("DELETE", path) diff --git a/pkg/hanzo-tools-auth/pyproject.toml b/pkg/hanzo-tools-auth/pyproject.toml deleted file mode 100644 index f2e5c1f93..000000000 --- a/pkg/hanzo-tools-auth/pyproject.toml +++ /dev/null @@ -1,33 +0,0 @@ -[project] -name = "hanzo-tools-auth" -version = "0.1.0" -description = "Authentication bridge for Hanzo MCP platform tools โ€” session management, token refresh, service client factory" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "mcp", "auth", "iam", "tools"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", -] - -dependencies = [ - "hanzo-tools-core>=0.1.0", - "hanzo-iam>=1.30.0", - "httpx>=0.27.0", - "pyjwt>=2.8.0", -] - -[project.entry-points."hanzo.tools"] -auth = "hanzo_tools.auth:TOOLS" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] diff --git a/pkg/hanzo-tools-billing/README.md b/pkg/hanzo-tools-billing/README.md deleted file mode 100644 index 397e82dfe..000000000 --- a/pkg/hanzo-tools-billing/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# hanzo-tools-billing - -MCP tool package for hanzo-mcp. Provides native billing management via the Hanzo platform. - -## Installation - -```bash -pip install hanzo-tools-billing -``` - -Part of the [hanzo-mcp](https://pypi.org/project/hanzo-mcp/) ecosystem. diff --git a/pkg/hanzo-tools-billing/hanzo_tools/__init__.py b/pkg/hanzo-tools-billing/hanzo_tools/__init__.py deleted file mode 100644 index 8b1378917..000000000 --- a/pkg/hanzo-tools-billing/hanzo_tools/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pkg/hanzo-tools-billing/hanzo_tools/billing/__init__.py b/pkg/hanzo-tools-billing/hanzo_tools/billing/__init__.py deleted file mode 100644 index 25e9dfe2e..000000000 --- a/pkg/hanzo-tools-billing/hanzo_tools/billing/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Hanzo Billing Tools -- balance, usage, plans, subscriptions, and invoices via MCP.""" - -from .billing_tool import BillingTool - -TOOLS = [BillingTool] - -__all__ = ["BillingTool", "TOOLS"] diff --git a/pkg/hanzo-tools-billing/hanzo_tools/billing/billing_tool.py b/pkg/hanzo-tools-billing/hanzo_tools/billing/billing_tool.py deleted file mode 100644 index 9b30bda1c..000000000 --- a/pkg/hanzo-tools-billing/hanzo_tools/billing/billing_tool.py +++ /dev/null @@ -1,306 +0,0 @@ -"""MCP tool for Hanzo Billing -- balance, usage, plans, subscriptions, invoices. - -Wraps the Hanzo Commerce billing API at api.hanzo.ai/api/v1/billing/. -Auth: Uses HanzoSession from hanzo-tools-auth for bearer tokens. -""" - -from __future__ import annotations - -import os -import json -import logging -from typing import Any, Annotated, final - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core.base import BaseTool - -logger = logging.getLogger(__name__) - -DESCRIPTION = """Hanzo Billing -- account balance, usage, plans, subscriptions, and invoices. - -Requires authentication via `hanzo login` (stored at ~/.hanzo/auth/token.json). - -Actions: -- balance: Get current billing balance -- usage: Get usage summary (period param: current, previous, or YYYY-MM) -- plans: List available plans -- subscriptions: List all subscriptions -- subscription: Get subscription by ID (subscription_id required) -- invoices: List invoices -- invoice: Get invoice by ID (invoice_id required) -- payment_methods: List payment methods on file -- spend_alerts: List configured spend alerts -- credit_balance: Get credit/promotional balance -- meters: List usage meters -- deposit: Add a deposit (amount required) -- credit: Grant starter credit to account -""" - -API_BASE = "https://api.hanzo.ai/api/v1/billing" - - -def _get_session(): - """Get HanzoSession singleton.""" - from hanzo_tools.auth.session import HanzoSession - return HanzoSession.get() - - -def _api_base() -> str: - """Get billing API base URL (overridable via env).""" - return os.getenv("HANZO_BILLING_API_URL", API_BASE).rstrip("/") - - -def _request(method: str, path: str, token: str, **kwargs: Any) -> Any: - """Make an authenticated HTTP request to the billing API.""" - import httpx - - url = f"{_api_base()}{path}" - headers = { - "Authorization": f"Bearer {token}", - "Content-Type": "application/json", - "User-Agent": "hanzo-mcp/0.1", - } - - with httpx.Client(timeout=30.0) as client: - resp = client.request(method, url, headers=headers, **kwargs) - if resp.status_code >= 400: - try: - err = resp.json() - msg = err.get("error", err.get("message", resp.text)) - except Exception: - msg = resp.text - raise RuntimeError(f"Billing API error {resp.status_code}: {msg}") - if not resp.content or resp.status_code == 204: - return {} - return resp.json() - - -def _get(path: str, token: str) -> Any: - return _request("GET", path, token) - - -def _post(path: str, token: str, **kwargs: Any) -> Any: - return _request("POST", path, token, **kwargs) - - -@final -class BillingTool(BaseTool): - """MCP tool for Hanzo billing operations.""" - - @property - def name(self) -> str: - return "billing" - - @property - def description(self) -> str: - return DESCRIPTION - - def _get_token(self) -> str: - """Get auth token or raise.""" - session = _get_session() - token = session.get_iam_token() - if not token: - raise RuntimeError("Not authenticated. Run 'hanzo login' first.") - return token - - async def call( - self, - ctx: MCPContext, - action: str = "balance", - user: str | None = None, - subscription_id: str | None = None, - invoice_id: str | None = None, - period: str | None = None, - amount: float | None = None, - **kwargs: Any, - ) -> str: - try: - if action == "balance": - return await self._balance(user) - elif action == "usage": - return await self._usage(user, period) - elif action == "plans": - return await self._plans() - elif action == "subscriptions": - return await self._subscriptions(user) - elif action == "subscription": - return await self._subscription(subscription_id) - elif action == "invoices": - return await self._invoices(user) - elif action == "invoice": - return await self._invoice(invoice_id) - elif action == "payment_methods": - return await self._payment_methods(user) - elif action == "spend_alerts": - return await self._spend_alerts(user) - elif action == "credit_balance": - return await self._credit_balance(user) - elif action == "meters": - return await self._meters(user) - elif action == "deposit": - return await self._deposit(user, amount) - elif action == "credit": - return await self._credit(user) - else: - return json.dumps({ - "error": f"Unknown action: {action}", - "available": [ - "balance", "usage", "plans", "subscriptions", "subscription", - "invoices", "invoice", "payment_methods", "spend_alerts", - "credit_balance", "meters", "deposit", "credit", - ], - }) - except RuntimeError as e: - return json.dumps({"error": str(e)}) - except Exception as e: - logger.exception(f"Billing tool error: {e}") - return json.dumps({"error": f"Billing error: {e}"}) - - # -- Actions ------------------------------------------------------------- - - async def _balance(self, user: str | None) -> str: - token = self._get_token() - params = f"?user={user}" if user else "" - data = _get(f"/balance{params}", token) - return json.dumps(data, indent=2) - - async def _usage(self, user: str | None, period: str | None) -> str: - token = self._get_token() - parts = [] - if user: - parts.append(f"user={user}") - if period: - parts.append(f"period={period}") - qs = f"?{'&'.join(parts)}" if parts else "" - data = _get(f"/usage{qs}", token) - return json.dumps(data, indent=2) - - async def _plans(self) -> str: - token = self._get_token() - data = _get("/plans", token) - return json.dumps(data, indent=2) - - async def _subscriptions(self, user: str | None) -> str: - token = self._get_token() - params = f"?user={user}" if user else "" - data = _get(f"/subscriptions{params}", token) - return json.dumps(data, indent=2) - - async def _subscription(self, subscription_id: str | None) -> str: - if not subscription_id: - return json.dumps({"error": "Required: subscription_id"}) - token = self._get_token() - data = _get(f"/subscriptions/{subscription_id}", token) - return json.dumps(data, indent=2) - - async def _invoices(self, user: str | None) -> str: - token = self._get_token() - params = f"?user={user}" if user else "" - data = _get(f"/invoices{params}", token) - return json.dumps(data, indent=2) - - async def _invoice(self, invoice_id: str | None) -> str: - if not invoice_id: - return json.dumps({"error": "Required: invoice_id"}) - token = self._get_token() - data = _get(f"/invoices/{invoice_id}", token) - return json.dumps(data, indent=2) - - async def _payment_methods(self, user: str | None) -> str: - token = self._get_token() - params = f"?user={user}" if user else "" - data = _get(f"/payment-methods{params}", token) - return json.dumps(data, indent=2) - - async def _spend_alerts(self, user: str | None) -> str: - token = self._get_token() - params = f"?user={user}" if user else "" - data = _get(f"/spend-alerts{params}", token) - return json.dumps(data, indent=2) - - async def _credit_balance(self, user: str | None) -> str: - token = self._get_token() - params = f"?user={user}" if user else "" - data = _get(f"/credits{params}", token) - return json.dumps(data, indent=2) - - async def _meters(self, user: str | None) -> str: - token = self._get_token() - params = f"?user={user}" if user else "" - data = _get(f"/meters{params}", token) - return json.dumps(data, indent=2) - - async def _deposit(self, user: str | None, amount: float | None) -> str: - if not amount or amount <= 0: - return json.dumps({"error": "Required: amount (positive number)"}) - token = self._get_token() - body: dict[str, Any] = {"amount": amount} - if user: - body["user"] = user - data = _post("/deposit", token, json=body) - return json.dumps(data, indent=2) - - async def _credit(self, user: str | None) -> str: - token = self._get_token() - body: dict[str, Any] = {} - if user: - body["user"] = user - data = _post("/credit", token, json=body) - return json.dumps(data, indent=2) - - # -- Registration -------------------------------------------------------- - - def register(self, mcp_server: FastMCP) -> None: - """Register billing tool with explicit parameters.""" - tool_instance = self - - @mcp_server.tool( - name="billing", - description=DESCRIPTION, - ) - async def billing( - action: Annotated[ - str, - Field( - description=( - "Action to perform. " - "balance, usage, plans, subscriptions, subscription, " - "invoices, invoice, payment_methods, spend_alerts, " - "credit_balance, meters, deposit, credit." - ), - ), - ] = "balance", - user: Annotated[ - str | None, - Field(description="User identifier (org/username) to scope queries"), - ] = None, - subscription_id: Annotated[ - str | None, - Field(description="Subscription ID (for subscription action)"), - ] = None, - invoice_id: Annotated[ - str | None, - Field(description="Invoice ID (for invoice action)"), - ] = None, - period: Annotated[ - str | None, - Field(description="Usage period: current, previous, or YYYY-MM (for usage action)"), - ] = None, - amount: Annotated[ - float | None, - Field(description="Deposit amount in USD (for deposit action)"), - ] = None, - ctx: MCPContext = None, - ) -> str: - return await tool_instance.call( - ctx, - action=action, - user=user, - subscription_id=subscription_id, - invoice_id=invoice_id, - period=period, - amount=amount, - ) diff --git a/pkg/hanzo-tools-billing/pyproject.toml b/pkg/hanzo-tools-billing/pyproject.toml deleted file mode 100644 index a0e25958d..000000000 --- a/pkg/hanzo-tools-billing/pyproject.toml +++ /dev/null @@ -1,32 +0,0 @@ -[project] -name = "hanzo-tools-billing" -version = "0.1.0" -description = "Hanzo Billing MCP tool โ€” balance, usage, plans, subscriptions, invoices, and payments" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "mcp", "billing", "subscriptions", "invoices", "tools"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", -] - -dependencies = [ - "hanzo-tools-core>=0.1.0", - "hanzo-tools-auth>=0.1.0", - "httpx>=0.27.0", -] - -[project.entry-points."hanzo.tools"] -billing = "hanzo_tools.billing:TOOLS" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] diff --git a/pkg/hanzo-tools-browser/README.md b/pkg/hanzo-tools-browser/README.md deleted file mode 100644 index cd239fa34..000000000 --- a/pkg/hanzo-tools-browser/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# hanzo-tools-browser - -Browser automation tools for Hanzo MCP using Playwright. - -## Installation - -```bash -pip install hanzo-tools-browser -``` - -## Tools - -### browser - Complete Playwright API -70+ browser actions for full automation. - -**Navigation:** -```python -browser(action="navigate", url="https://example.com") -browser(action="go_back") -browser(action="reload") -``` - -**Input:** -```python -browser(action="click", selector="button.submit") -browser(action="fill", selector="input[name=email]", text="user@example.com") -browser(action="type", selector="textarea", text="Hello") -``` - -**Touch/Mobile:** -```python -browser(action="tap", selector=".button") -browser(action="swipe", selector=".carousel", direction="left") -browser(action="emulate", device="mobile") # or tablet, laptop -``` - -**Assertions:** -```python -browser(action="expect_visible", selector=".modal") -browser(action="expect_text", selector="h1", expected="Welcome") -browser(action="expect_url", expected="*/dashboard*") -``` - -**Content:** -```python -browser(action="get_text", selector=".content") -browser(action="screenshot", full_page=True) -browser(action="pdf") -``` - -**Parallel Agents:** -```python -# Each agent gets isolated session -browser(action="new_context") # Separate cookies/storage -``` - -**Device Presets:** -- `mobile` - iPhone-like (390x844, touch) -- `tablet` - iPad-like (1024x1366, touch) -- `laptop` - MacBook-like (1440x900) -- `iphone_14`, `pixel_7`, `ipad_pro`, etc. - -## License - -MIT diff --git a/pkg/hanzo-tools-browser/hanzo_tools/__init__.py b/pkg/hanzo-tools-browser/hanzo_tools/__init__.py deleted file mode 100644 index f4f8ea812..000000000 --- a/pkg/hanzo-tools-browser/hanzo_tools/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Hanzo Tools namespace package.""" - -__path__ = __import__("pkgutil").extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-browser/hanzo_tools/browser/__init__.py b/pkg/hanzo-tools-browser/hanzo_tools/browser/__init__.py deleted file mode 100644 index c27cdb7c1..000000000 --- a/pkg/hanzo-tools-browser/hanzo_tools/browser/__init__.py +++ /dev/null @@ -1,197 +0,0 @@ -"""Browser automation tools โ€” decomplected surface. - -Three orthogonal MCP tools, all default-enabled, all disable-able by env: - - * ``browser`` โ€” high-level action surface. Auto-routes through the - in-process ZAP server (browser extension), the legacy - CDP HTTP bridge, or Playwright. Disable with - ``HANZO_BROWSER_TOOL_DISABLED=1``. - - * ``cdp`` โ€” raw Chrome DevTools Protocol method dispatch. Same - transports as ``browser`` minus the Playwright fallback. - Disable with ``HANZO_CDP_TOOL_DISABLED=1``. - - * ``playwright`` โ€” Playwright-pinned action surface. Same actions as - ``browser`` but never touches the extension or CDP - bridge. Disable with ``HANZO_PLAYWRIGHT_TOOL_DISABLED=1``. - -Independent transport knobs (orthogonal to which tools are surfaced): - - * ``HANZO_ZAP_DISABLED=1`` โ€” don't auto-start the ZAP server. - * ``HANZO_CDP_BRIDGE_ENABLED=1`` โ€” opt back into the legacy HTTP bridge. - * ``BROWSER_TRANSPORT=zap|http|auto`` โ€” pin transport (default ``auto``). - * ``BROWSER_BACKEND=firefox|chrome|extension|playwright|auto`` โ€” backend - preference for ``browser``. - -Lifecycle (ZAP server, CDP bridge threads) lives in ``lifecycle.py``. -""" - -from __future__ import annotations - -import logging -import os -from typing import TYPE_CHECKING - -from mcp.server import FastMCP - -from hanzo_tools.core import BaseTool, ToolRegistry -from hanzo_tools.browser.browser_tool import ( - PLAYWRIGHT_AVAILABLE, - BrowserPool, - BrowserTool, - get_backend, - _load_config, - _save_config, - browser_tool, - create_browser_tool, - launch_browser_server, -) -from hanzo_tools.browser.cdp_tool import CdpTool -from hanzo_tools.browser.playwright_tool import PlaywrightTool -from hanzo_tools.browser.lifecycle import ( - CDP_BRIDGE_AVAILABLE, - ensure_zap_server, - start_cdp_bridge, - stop_cdp_bridge, - stop_zap_server, -) -from hanzo_tools.browser.zap_server import ( - ZapClient, - ZapServer, - get_or_start_server, - get_server, - shutdown_server, -) - -if TYPE_CHECKING: - from hanzo_tools.browser.cdp_bridge_server import ( - CDPBridgeClient, - CDPBridgeServer, - ) - -# Re-export CDP-bridge classes when available (legacy callers). -try: - from hanzo_tools.browser.cdp_bridge_server import ( - CDPBridgeClient, - CDPBridgeServer, - ) -except ImportError: # pragma: no cover - CDPBridgeClient = None # type: ignore[assignment] - CDPBridgeServer = None # type: ignore[assignment] - -logger = logging.getLogger(__name__) - - -# === Tools registry โ€” gated by env ==================================== - -def _env_disabled(*names: str) -> bool: - return any(os.environ.get(n, "").lower() in ("1", "true", "yes") for n in names) - - -def _resolve_tools() -> list[type[BaseTool]]: - """Build TOOLS list at import time based on env flags. - - Each tool is independently disable-able. Default: all three on. - """ - tools: list[type[BaseTool]] = [] - - if not _env_disabled("HANZO_BROWSER_TOOL_DISABLED"): - tools.append(BrowserTool) - if not _env_disabled("HANZO_CDP_TOOL_DISABLED"): - tools.append(CdpTool) - if not _env_disabled("HANZO_PLAYWRIGHT_TOOL_DISABLED"): - tools.append(PlaywrightTool) - - return tools - - -TOOLS: list[type[BaseTool]] = _resolve_tools() - - -# === Registration entry point ========================================== - -def register_browser_tools(mcp_server: FastMCP, **kwargs) -> list[BaseTool]: - """Register browser tools with the MCP server. - - Starts the in-process ZAP server (unless ``HANZO_ZAP_DISABLED=1``) so - the browser extension can discover this MCP via mDNS. The legacy CDP - HTTP bridge (port 9223/9224) stays off by default โ€” opt in with - ``HANZO_CDP_BRIDGE_ENABLED=1`` or ``cdp_bridge=True`` kwarg. - - Which tools get registered is controlled by env flags: - * HANZO_BROWSER_TOOL_DISABLED - * HANZO_CDP_TOOL_DISABLED - * HANZO_PLAYWRIGHT_TOOL_DISABLED - """ - headless = kwargs.get("headless", True) - cdp_endpoint = kwargs.get("cdp_endpoint") - backend = kwargs.get("backend") - - # Canonical lifecycle: in-process ZAP server. - if backend != "playwright": - ensure_zap_server() - - # Optional legacy lifecycle: CDP HTTP bridge. - if kwargs.get( - "cdp_bridge", - os.environ.get("HANZO_CDP_BRIDGE_ENABLED", "").lower() in ("1", "true", "yes"), - ) and CDP_BRIDGE_AVAILABLE and backend != "playwright": - start_cdp_bridge() - - registered: list[BaseTool] = [] - for tool_class in TOOLS: - if tool_class is BrowserTool: - tool = create_browser_tool( - headless=headless, cdp_endpoint=cdp_endpoint, backend=backend - ) - elif tool_class is PlaywrightTool: - # PlaywrightTool forces backend internally; respect headless+endpoint. - tool = PlaywrightTool(headless=headless, cdp_endpoint=cdp_endpoint) - else: - # CdpTool, future peers โ€” no-arg constructor. - tool = tool_class() - ToolRegistry.register_tool(mcp_server, tool) - registered.append(tool) - return registered - - -def register_tools(mcp_server: FastMCP, **kwargs) -> list[BaseTool]: - """Standard entry point called by tool-discovery hosts.""" - return register_browser_tools(mcp_server, **kwargs) - - -__all__ = [ - # Tools (the three peers) - "BrowserTool", - "CdpTool", - "PlaywrightTool", - # Factory + module-level instance (existing public API) - "browser_tool", - "create_browser_tool", - # Browser pool - "BrowserPool", - "launch_browser_server", - # ZAP (canonical) - "ZapServer", - "ZapClient", - "get_or_start_server", - "get_server", - "shutdown_server", - # CDP Bridge (legacy fallback) - "CDPBridgeServer", - "CDPBridgeClient", - "CDP_BRIDGE_AVAILABLE", - "start_cdp_bridge", - "stop_cdp_bridge", - # Lifecycle (now in lifecycle.py) - "ensure_zap_server", - "stop_zap_server", - # Availability check - "PLAYWRIGHT_AVAILABLE", - # Backend helper - "get_backend", - # Registration - "TOOLS", - "register_browser_tools", - "register_tools", -] diff --git a/pkg/hanzo-tools-browser/hanzo_tools/browser/bidi_client.py b/pkg/hanzo-tools-browser/hanzo_tools/browser/bidi_client.py deleted file mode 100644 index cff16dbc8..000000000 --- a/pkg/hanzo-tools-browser/hanzo_tools/browser/bidi_client.py +++ /dev/null @@ -1,367 +0,0 @@ -"""WebDriver BiDi client for Firefox 129+ and Chrome 124+. - -This is the v1.10.0 "trusted input" backend that complements the -WebExtension scripting backend (the cdp_bridge_server's WebSocket -to the browser extension). Where the extension produces synthetic -events with isTrusted=false (which strict frameworks like Drupal -AJAX, certain React libraries, and security-aware sites reject), -BiDi produces real browser input events with isTrusted=true. - -Architecture (decomplected, three orthogonal layers): - - Layer 3 โ€” ergonomic alias: hanzo.click(selector) - โ””โ”€ auto-routes to BiDi when available, extension otherwise - - Layer 2 โ€” canonical primitives: - Input.dispatchMouseEvent({x, y, type}) - โ”œโ”€ synthetic path (extension backend) - โ””โ”€ trusted path (this module, BiDi backend) - - Layer 1 โ€” wire transport: - JSON-RPC over WebSocket to ws://localhost:9222/session - -Per WebDriver BiDi spec: - https://w3c.github.io/webdriver-bidi/ - -To enable: launch Firefox with `--remote-debugging-port=9222` (Firefox -129+) or Chrome with the same flag (Chrome 124+). The bridge will -auto-detect on startup and advertise BiDi.* methods in its capabilities. -""" - -from __future__ import annotations - -import asyncio -import json -import logging -import time -from dataclasses import dataclass, field -from typing import Any - -try: - import websockets - from websockets.client import WebSocketClientProtocol -except ImportError: # pragma: no cover - websockets = None - WebSocketClientProtocol = Any # type: ignore - -import aiohttp - -logger = logging.getLogger(__name__) - - -@dataclass -class BiDiSession: - """One BiDi session against one browser. - - Holds the WebSocket connection, the session_id, the open browsing - contexts (one per tab), and the request-id โ†’ future map used to - correlate JSON-RPC responses with their callers. - """ - - ws: WebSocketClientProtocol - session_id: str | None = None - next_id: int = 1 - pending: dict[int, asyncio.Future] = field(default_factory=dict) - contexts: dict[str, dict] = field(default_factory=dict) # context_id โ†’ info - reader_task: asyncio.Task | None = None - - def _next_id(self) -> int: - i = self.next_id - self.next_id += 1 - return i - - -class BiDiClient: - """High-level WebDriver BiDi client. - - Methods that the bridge can call: - - connect() โ€” open WebSocket, create session - - close() โ€” clean shutdown - - list_contexts() โ€” list browsing contexts (one per tab) - - find_context_by_url() โ€” locate the tab matching a URL substring - - input_mouse_click(context_id, x, y, *, button=0) โ€” TRUSTED click - - input_key_down/up(context_id, key) โ€” TRUSTED keyboard - - input_insert_text(context_id, text) โ€” TRUSTED typing - - browsing_context_navigate(context_id, url) โ€” TRUSTED nav - - browsing_context_capture_screenshot(context_id) โ€” native screenshot - - script_evaluate(context_id, expression) โ€” page-context eval - - subscribe(events) โ€” event streams - """ - - def __init__(self, host: str = "localhost", port: int = 9222) -> None: - self.host = host - self.port = port - self.session: BiDiSession | None = None - - @property - def connected(self) -> bool: - return self.session is not None and self.session.ws is not None and not self.session.ws.closed - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Discovery: probe whether the browser exposes a BiDi endpoint - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - @classmethod - async def probe(cls, host: str = "localhost", port: int = 9222, timeout: float = 1.5) -> str | None: - """Return the BiDi WebSocket URL if available, else None. - - Firefox 129+ exposes the BiDi endpoint at GET /json/version which - returns JSON containing 'webSocketDebuggerUrl'. Chrome 124+ exposes - a similar endpoint with both 'webSocketDebuggerUrl' (legacy CDP) - and (optionally) BiDi support via the same socket. - """ - if websockets is None: - return None - url = f"http://{host}:{port}/json/version" - try: - async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=timeout)) as sess: - async with sess.get(url) as resp: - if resp.status != 200: - return None - data = await resp.json() - # Firefox: "webSocketDebuggerUrl": "ws://host:port/session" - # Chrome: "webSocketDebuggerUrl": "ws://host:port/devtools/browser/" - return data.get("webSocketDebuggerUrl") - except Exception: - return None - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Connection lifecycle - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - async def connect(self) -> None: - """Open the WebSocket and create a BiDi session.""" - if self.connected: - return - if websockets is None: - raise RuntimeError("websockets package not installed") - - ws_url = await self.probe(self.host, self.port) - if not ws_url: - raise RuntimeError( - f"No BiDi endpoint at http://{self.host}:{self.port}/json/version. " - f"Launch Firefox with --remote-debugging-port={self.port} " - f"or Chrome with --remote-debugging-port={self.port}." - ) - - logger.info("BiDi connecting to %s", ws_url) - ws = await websockets.connect(ws_url, max_size=None) - self.session = BiDiSession(ws=ws) - self.session.reader_task = asyncio.create_task(self._reader_loop()) - - # Create a BiDi session - result = await self._send("session.new", {"capabilities": {}}) - self.session.session_id = result.get("sessionId") - logger.info("BiDi session created: %s", self.session.session_id) - - async def close(self) -> None: - if not self.session: - return - if self.session.reader_task and not self.session.reader_task.done(): - self.session.reader_task.cancel() - try: - if self.session.ws and not self.session.ws.closed: - await self.session.ws.close() - except Exception: - pass - self.session = None - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Wire protocol - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - async def _reader_loop(self) -> None: - """Receive messages and dispatch to pending futures or event handlers.""" - assert self.session is not None - try: - async for raw in self.session.ws: - try: - msg = json.loads(raw) - except Exception: - continue - msg_id = msg.get("id") - if msg_id is not None and msg_id in self.session.pending: - fut = self.session.pending.pop(msg_id) - if msg.get("type") == "error" or "error" in msg: - err = msg.get("error") or msg.get("message", "unknown") - fut.set_exception(RuntimeError(f"BiDi error: {err}")) - else: - fut.set_result(msg.get("result", {})) - # else: event โ€” could route to subscribers (future work) - except asyncio.CancelledError: - raise - except Exception as e: - logger.warning("BiDi reader loop exited: %s", e) - - async def _send(self, method: str, params: dict | None = None, timeout: float = 30.0) -> dict: - """Send a method call, await the response.""" - if not self.connected: - await self.connect() - assert self.session is not None - rid = self.session._next_id() - loop = asyncio.get_running_loop() - fut: asyncio.Future = loop.create_future() - self.session.pending[rid] = fut - msg = {"id": rid, "method": method, "params": params or {}} - await self.session.ws.send(json.dumps(msg)) - return await asyncio.wait_for(fut, timeout=timeout) - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Browsing contexts (one per tab) - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - async def list_contexts(self) -> list[dict]: - """Return the list of top-level browsing contexts (tabs).""" - result = await self._send("browsingContext.getTree", {}) - contexts = result.get("contexts", []) - # Flatten so the caller has [{context, url, ...}, ...] - return contexts - - async def find_context_by_url(self, url_substring: str) -> str | None: - contexts = await self.list_contexts() - for c in contexts: - if url_substring in (c.get("url") or ""): - return c.get("context") - return None - - async def navigate(self, context_id: str, url: str, wait: str = "complete") -> dict: - """Navigate a browsing context to a URL. wait โˆˆ {none, interactive, complete}.""" - return await self._send("browsingContext.navigate", { - "context": context_id, "url": url, "wait": wait, - }) - - async def capture_screenshot(self, context_id: str) -> str: - """Returns a base64-encoded PNG of the entire viewport.""" - result = await self._send("browsingContext.captureScreenshot", {"context": context_id}) - return result.get("data", "") - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Trusted input โ€” THE WHOLE POINT of this backend - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - async def input_mouse_click(self, context_id: str, x: float, y: float, *, - button: int = 0, click_count: int = 1) -> dict: - """Dispatch a TRUSTED click at (x, y) in the given context's viewport. - - Generates pointerMove โ†’ pointerDown โ†’ pointerUp โ†’ pointerMove(0,0). - Events have isTrusted=true at the page level โ€” Drupal AJAX, React - with isTrusted checks, and any other framework will honor them - because they ARE real browser input events. - """ - return await self._send("input.performActions", { - "context": context_id, - "actions": [{ - "type": "pointer", - "id": "default-mouse", - "parameters": {"pointerType": "mouse"}, - "actions": [ - {"type": "pointerMove", "x": int(x), "y": int(y), "duration": 0}, - {"type": "pointerDown", "button": button}, - {"type": "pause", "duration": 30}, - {"type": "pointerUp", "button": button}, - ], - }], - }) - - async def input_double_click(self, context_id: str, x: float, y: float, *, button: int = 0) -> dict: - return await self._send("input.performActions", { - "context": context_id, - "actions": [{ - "type": "pointer", "id": "default-mouse", - "parameters": {"pointerType": "mouse"}, - "actions": [ - {"type": "pointerMove", "x": int(x), "y": int(y), "duration": 0}, - {"type": "pointerDown", "button": button}, - {"type": "pointerUp", "button": button}, - {"type": "pause", "duration": 30}, - {"type": "pointerDown", "button": button}, - {"type": "pointerUp", "button": button}, - ], - }], - }) - - async def input_key_press(self, context_id: str, key: str) -> dict: - """Press a single key. `key` follows the W3C WebDriver Key spec - (e.g., 'Enter', 'Tab', 'Escape', or a literal character).""" - return await self._send("input.performActions", { - "context": context_id, - "actions": [{ - "type": "key", "id": "default-keyboard", - "actions": [ - {"type": "keyDown", "value": key}, - {"type": "keyUp", "value": key}, - ], - }], - }) - - async def input_insert_text(self, context_id: str, text: str) -> dict: - """Type a string with trusted keyboard events.""" - actions: list[dict] = [] - for ch in text: - actions.append({"type": "keyDown", "value": ch}) - actions.append({"type": "keyUp", "value": ch}) - return await self._send("input.performActions", { - "context": context_id, - "actions": [{"type": "key", "id": "default-keyboard", "actions": actions}], - }) - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Script evaluation (similar to extension scripting, but BiDi-native) - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - async def script_evaluate(self, context_id: str, expression: str, *, - await_promise: bool = True) -> dict: - """Evaluate JS in the page context. Result has 'result' field - with serialized value.""" - return await self._send("script.evaluate", { - "expression": expression, - "target": {"context": context_id}, - "awaitPromise": await_promise, - "userActivation": True, # so popup blockers etc. don't fire - }) - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Convenience: get viewport coords of an element by CSS selector, - # then click. This is the equivalent of hanzo.click but with the - # actual click being TRUSTED via input.performActions. - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - async def click_selector(self, context_id: str, selector: str) -> dict: - """Composite: scrollIntoView + getBoundingClientRect + trusted click.""" - # 1) Resolve the element's center coordinates via script.evaluate - # (the eval itself runs in page context โ€” that's fine; only the - # CLICK needs to be trusted, and that's what input.performActions does) - js = f""" - (function() {{ - const el = document.querySelector({json.dumps(selector)}); - if (!el) return null; - el.scrollIntoView({{block: 'center', behavior: 'instant'}}); - const r = el.getBoundingClientRect(); - return {{x: r.left + r.width/2, y: r.top + r.height/2}}; - }})() - """ - ev = await self.script_evaluate(context_id, js, await_promise=False) - result = ev.get("result", {}) - # BiDi script result shape: {type, value} or {type: 'object', value: {x: ..., y: ...}} - if result.get("type") == "null": - return {"clicked": False, "reason": "element not found"} - val = result.get("value", {}) - x = val.get("x") if isinstance(val, dict) else None - y = val.get("y") if isinstance(val, dict) else None - if x is None or y is None: - return {"clicked": False, "reason": "no coords returned", "raw": result} - await self.input_mouse_click(context_id, x, y) - return {"clicked": True, "isTrusted": True, "x": x, "y": y} - - -# Convenience: a singleton-ish auto-detected client for the bridge. -_default_client: BiDiClient | None = None - - -async def get_or_connect(host: str = "localhost", port: int = 9222) -> BiDiClient | None: - """Return a connected BiDi client, or None if the browser isn't - launched with --remote-debugging-port. Cached after first success.""" - global _default_client - if _default_client is not None and _default_client.connected: - return _default_client - client = BiDiClient(host=host, port=port) - try: - await client.connect() - except Exception as e: - logger.info("BiDi unavailable: %s", e) - return None - _default_client = client - return client diff --git a/pkg/hanzo-tools-browser/hanzo_tools/browser/browser_tool.py b/pkg/hanzo-tools-browser/hanzo_tools/browser/browser_tool.py deleted file mode 100644 index 8935806b8..000000000 --- a/pkg/hanzo-tools-browser/hanzo_tools/browser/browser_tool.py +++ /dev/null @@ -1,2580 +0,0 @@ -"""High-performance async browser automation tool using Playwright. - -Design goals: -- Async-first: All operations are non-blocking -- Shared browser instance: Reuse browser across calls (low latency) -- Connection pooling: Multiple pages/contexts for parallel work -- Cross-MCP sharing: Connect to existing browser via CDP endpoint -- Full Playwright API: Complete surface area coverage -- Touch support: Mobile device emulation with touch events -- Network control: Intercept, mock, and monitor requests - -PARALLEL AGENTS ARCHITECTURE: -- BrowserPool is a singleton - one Chrome process per MCP server -- Each agent can use `new_context` to get an isolated browser context -- Contexts have separate: cookies, localStorage, sessionStorage, cache -- Tabs within same context share state (use for same-session workflows) -- For true multi-process sharing, launch Chrome with CDP and connect: - BROWSER_CDP_ENDPOINT=http://localhost:9222 hanzo-mcp -""" - -import os -import re -import json -import base64 -import asyncio -import logging -from typing import Any, Union, Literal, ClassVar, Optional, Annotated -from pathlib import Path -from dataclasses import field, dataclass - -from pydantic import Field -from mcp.server import FastMCP - -from hanzo_tools.core import BaseTool - -# Playwright import with graceful fallback -try: - from playwright.async_api import ( - Page, - Route, - Dialog, - Browser, - Locator, - Request, - Download, - Response, - Playwright, - BrowserContext, - ConsoleMessage, - async_playwright, - ) - - PLAYWRIGHT_AVAILABLE = True -except ImportError: - PLAYWRIGHT_AVAILABLE = False - Browser = Page = BrowserContext = Playwright = None - Route = Request = Response = Dialog = ConsoleMessage = Download = Locator = None - -logger = logging.getLogger(__name__) - - -def _load_config() -> dict: - """Load browser config from ~/.hanzo/extension/config.json.""" - config_path = Path.home() / ".hanzo" / "extension" / "config.json" - try: - if config_path.exists(): - return json.loads(config_path.read_text()) - except Exception: - pass - return {} - - -def _save_config(config: dict) -> None: - """Save browser config to ~/.hanzo/extension/config.json.""" - config_dir = Path.home() / ".hanzo" / "extension" - config_dir.mkdir(parents=True, exist_ok=True) - config_path = config_dir / "config.json" - try: - config_path.write_text(json.dumps(config, indent=2)) - except Exception as e: - logger.warning(f"Failed to save config: {e}") - - -def get_backend() -> str: - """Get configured browser backend. - - Priority: BROWSER_BACKEND env var > ~/.hanzo/extension/config.json > "auto" - - Values: firefox | chrome | extension | playwright | auto - """ - # Env var takes precedence - env_val = os.environ.get("BROWSER_BACKEND", "").strip().lower() - if env_val in ("firefox", "chrome", "extension", "playwright", "auto"): - return env_val - - # Then config file - config = _load_config() - file_val = config.get("backend", "").strip().lower() - if file_val in ("firefox", "chrome", "extension", "playwright", "auto"): - return file_val - - return "auto" - - -def _normalize_tab_id(tab_id: Union[str, int, None]) -> Union[str, int, None]: - """Strip ``tab-`` prefix and coerce to int when possible.""" - if tab_id is None: - return None - t = tab_id - if isinstance(t, str) and t.startswith("tab-"): - t = t[4:] - try: - return int(t) - except (TypeError, ValueError): - return t - - -async def _check_extension(browser: Optional[str] = None) -> bool: - """Check if Hanzo browser extension is connected. - - Tries the local ZAP server first (in-process, microseconds), falls back - to the legacy HTTP bridge on :9224. - """ - # 1) ZAP โ€” if our own MCP holds an extension client locally - try: - from hanzo_tools.browser.zap_server import get_server - - srv = get_server() - if srv is not None and srv.has_client(browser=browser): - return True - except Exception: - pass - - # 2) Legacy HTTP bridge - try: - import aiohttp - - async with aiohttp.ClientSession() as session: - async with session.get( - "http://localhost:9224/status", timeout=aiohttp.ClientTimeout(total=1) - ) as resp: - if resp.status == 200: - data = await resp.json() - if not data.get("connected", False): - return False - if browser: - clients = data.get("client_list", []) - return any( - browser.lower() in c.get("browser", "").lower() - for c in clients - ) - return True - except Exception: - pass - return False - - -def _zap_method_for(action: str) -> str: - """Map a browser-tool action onto the wire method the extension expects. - - The Firefox/Chrome backgrounds dispatch CDP-style methods (``Page.navigate``, - ``Runtime.evaluate``, ``hanzo.click`` โ€ฆ) when the field is ``method`` on - the JSON it receives. Over ZAP we send the same method name so the - extension handler is shared. - """ - # Decomplected API surface โ€” three layers, orthogonal: - # 1) CDP-shape canonical names (Domain.method) - # 2) hanzo.* ergonomic aliases that compose CDP primitives - # 3) Python-side snake_case shortcuts that map to either - return { - # โ”€โ”€โ”€ Page lifecycle / navigation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - "navigate": "Page.navigate", - "reload": "Page.reload", - "go_back": "Page.goBack", - "go_forward": "Page.goForward", - "print_pdf": "Page.printToPDF", - "wait_for_navigation": "hanzo.waitForNavigation", - "wait_for_load_state": "Page.waitForLoadState", - - # โ”€โ”€โ”€ Tabs / targets โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - "tabs": "Target.getTargets", - "new_tab": "Target.createTarget", - "close_tab": "Target.closeTarget", - "activate_tab": "Target.activateTarget", - "url": "hanzo.url", - "title": "hanzo.title", - "tab_info": "hanzo.tabInfo", - "list_tabs": "hanzo.listTabs", - "history": "hanzo.getHistory", - - # โ”€โ”€โ”€ Observation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - "screenshot": "hanzo.screenshot", - "page_info": "hanzo.getPageInfo", - "ax_tree": "Accessibility.getFullAXTree", - "status": "Browser.getVersion", - - # โ”€โ”€โ”€ DOM read โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - "get_text": "hanzo.getText", - "get_html": "hanzo.getHTML", - "get_attribute": "hanzo.getAttribute", - "get_element_info": "hanzo.getElementInfo", - "query_one": "DOM.querySelector", - "query_all": "hanzo.querySelectorAll", - "list_form": "hanzo.listForm", - "computed_styles": "hanzo.getComputedStyles", - "bounding_rects": "hanzo.getBoundingRects", - - # โ”€โ”€โ”€ DOM write โ€” selector-based โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - "click": "hanzo.click", - "dblclick": "hanzo.dblclick", - "hover": "hanzo.hover", - "fill": "hanzo.fill", - "check": "hanzo.check", - "uncheck": "hanzo.uncheck", - "select": "hanzo.select", - "type": "hanzo.type", - "clear": "hanzo.clear", - "focus": "DOM.focus", - "scroll_into_view": "DOM.scrollIntoView", - "set_text": "hanzo.setText", - "set_html": "hanzo.setHTML", - "set_attribute": "hanzo.setAttribute", - "remove_attribute": "hanzo.removeAttribute", - - # โ”€โ”€โ”€ DOM write โ€” CSP-safe text/label-based (RECOMMENDED) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - "click_text": "hanzo.clickByText", - "fill_label": "hanzo.fillByLabel", - "find_by_text": "hanzo.findByText", - "submit_form": "hanzo.submitForm", - "upload_file": "hanzo.uploadFile", - - # โ”€โ”€โ”€ Keyboard / Mouse โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - "press": "hanzo.press", - "press_key": "Input.dispatchKeyEvent", - "mouse_event": "Input.dispatchMouseEvent", - "scroll": "hanzo.scroll", - "scroll_wheel": "Input.scrollWheel", - - # โ”€โ”€โ”€ Wait / Observe โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - "wait_for_text": "hanzo.waitForText", - "wait_for_mutation": "hanzo.waitForMutation", - "wait_for_selector": "hanzo.waitForSelector", - "observe_start": "hanzo.observe", - "observe_read": "hanzo.observeRead", - "observe_stop": "hanzo.observeStop", - - # โ”€โ”€โ”€ Dialog โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - "dialog_accept": "hanzo.dialogAccept", - - # โ”€โ”€โ”€ Scripting (works on non-CSP-strict pages only) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - "evaluate": "Runtime.evaluate", - "inject_script": "hanzo.injectScript", - "inject_css": "hanzo.injectCSS", - - # โ”€โ”€โ”€ Cookies / Storage โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - "cookies": "hanzo.getCookies", - "local_storage_get": "hanzo.getLocalStorage", - "local_storage_set": "hanzo.setLocalStorage", - - # โ”€โ”€โ”€ Network monitoring โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - "monitor_start": "monitor.start", - "monitor_stop": "monitor.stop", - "monitor_console_logs": "monitor.consoleLogs", - "monitor_console_errors": "monitor.consoleErrors", - "monitor_network_logs": "monitor.networkLogs", - "monitor_network_errors": "monitor.networkErrors", - "monitor_network_success": "monitor.networkSuccess", - - # โ”€โ”€โ”€ Audit / Lighthouse-style โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - "audit_accessibility": "audit.accessibility", - "audit_performance": "audit.performance", - "audit_seo": "audit.seo", - - # โ”€โ”€โ”€ HTTP fetch through the browser (uses page origin) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - "fetch": "hanzo.fetch", - }.get(action, action) - - -def _zap_params( - action: str, - *, - tab_id: Union[str, int, None] = None, - url: Optional[str] = None, - selector: Optional[str] = None, - text: Optional[str] = None, - label: Optional[str] = None, - value: Optional[str] = None, - code: Optional[str] = None, - expression: Optional[str] = None, - full_page: Optional[bool] = None, - **rest, -) -> dict: - """Translate the extension-tool kwargs into wire params.""" - params: dict[str, Any] = {} - if url is not None: - params["url"] = url - if selector is not None: - params["selector"] = selector - if value is not None: - params["value"] = value - if text is not None: - params["text"] = text - if label is not None: - params["label"] = label - expr = code or expression - if expr is not None: - params["expression"] = expr - if full_page is not None: - params["fullPage"] = full_page - norm_tab = _normalize_tab_id(tab_id) - if norm_tab is not None: - params["tabId"] = norm_tab - # Forward any extra kwargs the caller passed (action-specific fields). - for k, v in rest.items(): - if v is None: - continue - if k in { - "key", - "index", - "tab_index", - "timeout", - "state", - "level", - "expression", - "scope", # for click_text โ€” "default" or "all" - "limit", # for query_all - "outer", # for get_html - }: - params[k] = v - return params - - -async def _extension_command( - action: str, - browser: Optional[str] = None, - tab_id: Optional[Union[str, int]] = None, - client_id: Optional[str] = None, - **kwargs, -) -> Optional[dict]: - """Send command to Hanzo browser extension. - - Path order: - 1. Local in-process ZAP server (microsecond round-trip; preferred). - 2. Legacy HTTP bridge on :9224 (kept as fallback for non-ZAP MCP clients). - - The transport can be pinned with ``BROWSER_TRANSPORT=zap|http|auto`` โ€” - default is ``auto`` which means "ZAP if a connected extension matches, - else HTTP". - """ - transport = os.environ.get("BROWSER_TRANSPORT", "auto").strip().lower() - if transport not in {"zap", "http", "auto"}: - transport = "auto" - - # ---- 1) ZAP path --------------------------------------------------- - if transport in {"zap", "auto"}: - try: - from hanzo_tools.browser.zap_server import get_server - - srv = get_server() - if srv is not None and srv.has_client(browser=browser): - method = _zap_method_for(action) - params = _zap_params(action, tab_id=tab_id, **kwargs) - try: - raw = await srv.send( - method, - params, - browser=browser, - client_id=client_id, - ) - except Exception as e: - if transport == "zap": - return {"error": str(e), "transport": "zap"} - logger.debug("zap dispatch failed, falling back to http: %s", e) - else: - # CDP Runtime.evaluate returns {result: {type, value}}; - # unwrap so the caller sees the value directly. - result = raw - if method == "Runtime.evaluate" and isinstance(raw, dict): - # Surface an evaluation error (commonly page CSP - # blocking Function()/eval) instead of silently - # flattening it to a null value โ€” that null was - # indistinguishable from a legitimate null result. - err = raw.get("error") - exc = raw.get("exceptionDetails") - if err or exc: - msg = err or ( - exc.get("text") if isinstance(exc, dict) else str(exc) - ) - return { - "success": False, - "transport": "zap", - "error": msg, - "exceptionDetails": exc, - "result": None, - } - cdp = raw.get("result", raw) - if isinstance(cdp, dict) and "value" in cdp: - result = cdp["value"] - elif isinstance(cdp, dict) and cdp.get("type") == "undefined": - result = None - return { - "success": True, - "transport": "zap", - "result": result, - } - except ImportError: - # zap_server module unavailable โ€” only HTTP path remains. - pass - - if transport == "zap": - return { - "error": "ZAP transport selected but no extension client matched", - "transport": "zap", - } - - # ---- 2) HTTP fallback --------------------------------------------- - try: - import aiohttp - - payload: dict[str, Any] = {"action": action} - if browser: - payload["browser"] = browser - norm_tab = _normalize_tab_id(tab_id) - if norm_tab is not None: - payload["tabId"] = norm_tab - if client_id: - payload["clientId"] = client_id - payload.update({k: v for k, v in kwargs.items() if v is not None}) - - async with aiohttp.ClientSession() as session: - async with session.post( - "http://localhost:9224", - json=payload, - timeout=aiohttp.ClientTimeout(total=30), - ) as resp: - if resp.status == 200: - body = await resp.json() - body.setdefault("transport", "http") - return body - else: - text = await resp.text() - logger.debug(f"Extension command {action} returned {resp.status}: {text}") - return {"error": text, "status": resp.status, "transport": "http"} - except Exception as e: - logger.debug(f"Extension command failed: {e}") - return None - - -# Device presets - user-friendly aliases + specific devices -DEVICES = { - # User-friendly aliases - "mobile": { - "viewport": {"width": 390, "height": 844}, - "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1", - "device_scale_factor": 3, - "is_mobile": True, - "has_touch": True, - }, - "tablet": { - "viewport": {"width": 1024, "height": 1366}, - "user_agent": "Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1", - "device_scale_factor": 2, - "is_mobile": True, - "has_touch": True, - }, - "laptop": { - "viewport": {"width": 1440, "height": 900}, - "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", - "device_scale_factor": 2, - "is_mobile": False, - "has_touch": False, - }, - "desktop": { # Alias for laptop - "viewport": {"width": 1920, "height": 1080}, - "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", - "device_scale_factor": 1, - "is_mobile": False, - "has_touch": False, - }, - # Specific devices - "iphone_14": { - "viewport": {"width": 390, "height": 844}, - "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1", - "device_scale_factor": 3, - "is_mobile": True, - "has_touch": True, - }, - "iphone_15_pro": { - "viewport": {"width": 393, "height": 852}, - "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1", - "device_scale_factor": 3, - "is_mobile": True, - "has_touch": True, - }, - "pixel_7": { - "viewport": {"width": 412, "height": 915}, - "user_agent": "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", - "device_scale_factor": 2.625, - "is_mobile": True, - "has_touch": True, - }, - "ipad_pro": { - "viewport": {"width": 1024, "height": 1366}, - "user_agent": "Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1", - "device_scale_factor": 2, - "is_mobile": True, - "has_touch": True, - }, - "galaxy_s23": { - "viewport": {"width": 360, "height": 780}, - "user_agent": "Mozilla/5.0 (Linux; Android 13; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", - "device_scale_factor": 3, - "is_mobile": True, - "has_touch": True, - }, -} - - -Action = Annotated[ - Literal[ - # === Core Page Navigation & Lifecycle === - "navigate", # goto(url) - "set_content", # setContent(html) - "content", # content() - get full HTML - "url", # url() - get current URL - "title", # title() - get page title - "reload", # reload() - "go_back", # goBack() - "go_forward", # goForward() - "close", # close page/browser - # === Input - Click/Type === - "click", # click(selector) - "dblclick", # dblclick(selector) - "type", # type(selector, text) - character by character - "fill", # fill(selector, text) - instant, clears first - "clear", # clear input - "press", # press key combo (Ctrl+A, Enter, etc.) - # === Input - Forms === - "select_option", # select dropdown option - "check", # check checkbox/radio - "uncheck", # uncheck checkbox - "upload", # set_input_files - # === Mouse === - "hover", # hover(selector) - "drag", # drag_and_drop(source, target) - "mouse_move", # mouse.move(x, y) - "mouse_down", # mouse.down() - "mouse_up", # mouse.up() - "mouse_wheel", # mouse.wheel(dx, dy) - "scroll", # scroll element into view or scroll by delta - # === Touch (Mobile) === - "tap", # tap(selector) - touch tap - "swipe", # swipe gesture - "pinch", # pinch zoom - # === Locator Creation === - "locator", # Create locator (CSS, text, role, xpath) - "frame_locator", # frameLocator(selector) - # === Built-in Locators (get_by_*) === - "get_by_role", # getByRole(role, {name}) - "get_by_text", # getByText(text) - "get_by_label", # getByLabel(text) - "get_by_placeholder", # getByPlaceholder(text) - "get_by_test_id", # getByTestId(id) - "get_by_alt_text", # getByAltText(text) - "get_by_title", # getByTitle(text) - # === Locator Composition === - "first", # locator.first - "last", # locator.last - "nth", # locator.nth(index) - "filter", # locator.filter({has, hasText, hasNotText}) - "all", # locator.all() - get all matching - "count", # locator.count() - # === Content Extraction === - "get_text", # textContent() - "get_inner_text", # innerText() - "get_attribute", # getAttribute(name) - "get_value", # inputValue() - "get_html", # innerHTML() or content() - "get_bounding_box", # boundingBox() - # === State Checks === - "is_visible", # isVisible() - "is_enabled", # isEnabled() - "is_checked", # isChecked() - "is_hidden", # isHidden() - "is_editable", # isEditable() - # === Assertions (expect) === - "expect_visible", # expect(loc).toBeVisible() - "expect_hidden", # expect(loc).toBeHidden() - "expect_enabled", # expect(loc).toBeEnabled() - "expect_text", # expect(loc).toHaveText() - "expect_value", # expect(loc).toHaveValue() - "expect_checked", # expect(loc).toBeChecked() - "expect_url", # expect(page).toHaveURL() - "expect_title", # expect(page).toHaveTitle() - "expect_count", # expect(loc).toHaveCount() - "expect_attribute", # expect(loc).toHaveAttribute() - # === Page Actions === - "screenshot", # screenshot() - "pdf", # pdf() - "snapshot", # accessibility.snapshot() - "evaluate", # evaluate(js) - "focus", # focus(selector) - "blur", # blur() - # === Wait Primitives === - "wait", # waitForSelector or sleep - "wait_for_load", # waitForLoadState(networkidle, etc) - "wait_for_url", # waitForURL(pattern) - "wait_for_event", # waitForEvent(event) - request, response, download, filechooser, popup - "wait_for_request", # waitForRequest(pattern) - "wait_for_response", # waitForResponse(pattern) - "wait_for_function", # waitForFunction(js) - # === Viewport & Device === - "viewport", # setViewportSize - "emulate", # emulate device (mobile, tablet, laptop) - "geolocation", # setGeolocation - "permissions", # grantPermissions - # === Network Interception === - "route", # route(pattern, handler) - mock/block - "unroute", # unroute(pattern) - # === Storage & Cookies === - "cookies", # cookies() or addCookies() - "clear_cookies", # clearCookies() - "storage", # localStorage/sessionStorage - "storage_state", # storageState() - save/load auth - # === Events & Handlers === - "on", # page.on(event, handler) - "off", # removeListener - # === Dialogs === - "dialog", # handle pending dialog - # === Frames === - "frame", # switch to frame - "main_frame", # back to main frame - # === File Chooser & Downloads === - "file_chooser", # waitForEvent('filechooser') - "download", # waitForEvent('download') - # === Console & Errors === - "console", # get console messages - "errors", # get page errors - # === Browser/Context Management === - "new_page", # context.newPage() - "new_context", # browser.newContext() - isolated session - "new_tab", # alias for new_page - "close_tab", # close current page - "tabs", # list/switch tabs - "select_tab", # bring a specific tab to focus (Target.activateTarget) - "list_browsers", # list every connected extension provider - "set_default_browser", # persist the bridge's default browser pick - "use_browser", # alias of set_default_browser - "list_mcp_instances", # list all hanzo-mcps registered with the extension - "claim_browser", # take an exclusive lease on a browser for N seconds - "release_browser", # drop a previously-claimed lease - "connect", # connect via CDP - "set_headless", # toggle headless/headed - "status", # get browser status - # === Debug/Tracing === - "trace_start", # tracing.start() - "trace_stop", # tracing.stop() - "highlight", # highlight element for debugging - ], - Field(description="Browser action to perform"), -] - - -@dataclass -class BrowserState: - """Track browser state for debugging and monitoring.""" - - console_messages: list[dict] = field(default_factory=list) - page_errors: list[str] = field(default_factory=list) - routes: dict[str, dict] = field(default_factory=dict) - event_handlers: dict[str, list] = field(default_factory=dict) - tracing: bool = False - pending_dialog: Any = None - pending_download: Any = None - pending_file_chooser: Any = None - - -class BrowserPool: - """Shared browser instance pool for high-performance automation. - - ARCHITECTURE FOR PARALLEL AGENTS: - - Singleton per MCP process - one Chrome, many contexts - - new_context() creates isolated sessions (separate cookies/storage) - - Tabs share context state, contexts are isolated - - For multi-process sharing, use CDP endpoint - """ - - _instance: ClassVar[Optional["BrowserPool"]] = None - _lock: ClassVar[asyncio.Lock] = asyncio.Lock() - - def __init__(self): - self._playwright: Optional[Playwright] = None - self._browser: Optional[Browser] = None - self._context: Optional[BrowserContext] = None - self._page: Optional[Page] = None - self._pages: list[Page] = [] - self._contexts: list[BrowserContext] = [] - self._headless: bool = True - self._cdp_endpoint: Optional[str] = None - self._initialized: bool = False - self._state: BrowserState = BrowserState() - self._device: Optional[str] = None - - @classmethod - async def get_instance(cls) -> "BrowserPool": - """Get or create the singleton browser pool.""" - async with cls._lock: - if cls._instance is None: - cls._instance = BrowserPool() - return cls._instance - - @classmethod - async def shutdown(cls) -> None: - """Shutdown the browser pool.""" - async with cls._lock: - if cls._instance is not None: - await cls._instance.close() - cls._instance = None - - def _setup_page_listeners(self, page: Page) -> None: - """Set up event listeners for a page.""" - # Console messages - page.on( - "console", - lambda msg: self._state.console_messages.append( - { - "type": msg.type, - "text": msg.text, - "location": getattr(msg, "location", None), - } - ), - ) - - # Page errors - page.on("pageerror", lambda err: self._state.page_errors.append(str(err))) - - # Dialogs - async def handle_dialog(dialog: Dialog): - self._state.pending_dialog = dialog - - page.on("dialog", handle_dialog) - - # Downloads - def handle_download(download: Download): - self._state.pending_download = download - - page.on("download", handle_download) - - # File chooser - def handle_filechooser(file_chooser): - self._state.pending_file_chooser = file_chooser - - page.on("filechooser", handle_filechooser) - - async def ensure_browser( - self, - headless: bool = True, - cdp_endpoint: Optional[str] = None, - device: Optional[str] = None, - ) -> Page: - """Ensure browser is running, return current page.""" - if not PLAYWRIGHT_AVAILABLE: - raise RuntimeError( - "Playwright not installed. Run: pip install playwright && playwright install chromium" - ) - - needs_init = ( - not self._initialized - or self._page is None - or self._browser is None - or self._cdp_endpoint != cdp_endpoint - or self._device != device - ) - - if needs_init: - if self._initialized: - await self.close() - - self._playwright = await async_playwright().start() - self._headless = headless - self._cdp_endpoint = cdp_endpoint - self._device = device - self._state = BrowserState() - - device_settings = DEVICES.get(device) if device else None - - if cdp_endpoint: - logger.info(f"Connecting to browser at {cdp_endpoint}") - self._browser = await self._playwright.chromium.connect_over_cdp( - cdp_endpoint - ) - contexts = self._browser.contexts - if contexts: - self._context = contexts[0] - pages = self._context.pages - if pages: - self._page = pages[0] - self._pages = list(pages) - else: - self._page = await self._context.new_page() - self._pages = [self._page] - else: - context_opts = {"viewport": {"width": 1280, "height": 720}} - if device_settings: - context_opts.update(device_settings) - self._context = await self._browser.new_context(**context_opts) - self._page = await self._context.new_page() - self._pages = [self._page] - else: - self._browser = await self._playwright.chromium.launch( - headless=headless, - args=[ - "--disable-blink-features=AutomationControlled", - "--no-sandbox", - ], - ) - - context_opts = { - "viewport": {"width": 1440, "height": 900}, - "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", - } - if device_settings: - context_opts.update(device_settings) - - self._context = await self._browser.new_context(**context_opts) - self._contexts = [self._context] - self._page = await self._context.new_page() - self._pages = [self._page] - - self._setup_page_listeners(self._page) - self._initialized = True - logger.info(f"Browser initialized (device={device or 'laptop'})") - - return self._page - - async def new_context( - self, device: Optional[str] = None, **kwargs - ) -> BrowserContext: - """Create a new isolated browser context for parallel agents.""" - if not self._browser: - raise RuntimeError("Browser not initialized") - - context_opts = {} - if device and device in DEVICES: - context_opts.update(DEVICES[device]) - context_opts.update(kwargs) - - context = await self._browser.new_context(**context_opts) - self._contexts.append(context) - return context - - async def new_page( - self, url: Optional[str] = None, context: Optional[BrowserContext] = None - ) -> Page: - """Open a new page in specified or current context.""" - ctx = context or self._context - if not ctx: - raise RuntimeError("Browser not initialized") - page = await ctx.new_page() - self._setup_page_listeners(page) - self._pages.append(page) - self._page = page - if url: - await page.goto(url) - return page - - async def close_page(self, index: Optional[int] = None) -> None: - """Close a page by index (default: current page).""" - if not self._pages: - return - - idx = ( - index - if index is not None - else self._pages.index(self._page) if self._page in self._pages else -1 - ) - if 0 <= idx < len(self._pages): - page = self._pages.pop(idx) - await page.close() - if self._pages: - self._page = self._pages[min(idx, len(self._pages) - 1)] - else: - self._page = None - - async def switch_page(self, index: int) -> Page: - """Switch to page by index.""" - if 0 <= index < len(self._pages): - self._page = self._pages[index] - await self._page.bring_to_front() - return self._page - raise ValueError(f"Invalid page index: {index}") - - async def close(self) -> None: - """Close browser and cleanup.""" - if self._state.tracing and self._context: - try: - await self._context.tracing.stop() - except Exception: - pass - - if self._browser: - try: - await self._browser.close() - except Exception as e: - logger.warning(f"Error closing browser: {e}") - - if self._playwright: - try: - await self._playwright.stop() - except Exception as e: - logger.warning(f"Error stopping playwright: {e}") - - self._browser = None - self._context = None - self._page = None - self._pages = [] - self._contexts = [] - self._playwright = None - self._initialized = False - self._state = BrowserState() - logger.info("Browser closed") - - @property - def page(self) -> Optional[Page]: - return self._page - - @property - def pages(self) -> list[Page]: - return self._pages - - @property - def state(self) -> BrowserState: - return self._state - - -class BrowserTool(BaseTool): - """Complete browser automation with full Playwright API surface area. - - PARALLEL AGENTS: - Use `new_context` action to create isolated sessions. - Each context has separate cookies, storage, and cache. - One Chrome process, many parallel agent sessions. - - DEVICES: - - mobile, tablet, laptop (user-friendly) - - iphone_14, iphone_15_pro, pixel_7, galaxy_s23, ipad_pro (specific) - """ - - name = "browser" - - def __init__( - self, - headless: bool = True, - cdp_endpoint: Optional[str] = None, - backend: Optional[str] = None, - ): - self.headless = headless - self.cdp_endpoint = cdp_endpoint or os.environ.get("BROWSER_CDP_ENDPOINT") - self.backend = backend or get_backend() - self.timeout = 30000 - - # Lifecycle (ZAP server, CDP bridge) lives in `lifecycle.py`. - # Import lazily to avoid a circular import on package load โ€” - # __init__.py already pulls in this module before lifecycle. - if self.backend != "playwright": - try: - from hanzo_tools.browser.lifecycle import ( - CDP_BRIDGE_AVAILABLE, - ensure_zap_server, - start_cdp_bridge, - ) - - ensure_zap_server() # idempotent + respects HANZO_ZAP_DISABLED - - if ( - os.environ.get("HANZO_CDP_BRIDGE_ENABLED", "").lower() - in ("1", "true", "yes") - and CDP_BRIDGE_AVAILABLE - ): - start_cdp_bridge() - except Exception as e: - logger.debug("browser lifecycle bootstrap failed: %s", e) - - @property - def description(self) -> str: - return """Complete browser automation with full Playwright API. - -DISPLAY INSTRUCTIONS: Show results as bullet points. -โ€ข navigate: Navigated to [url] (status: [status]) -โ€ข click/tap: [action] on [selector] -โ€ข expect_*: โœ“ Assertion passed / โœ— Assertion failed -โ€ข screenshot: Captured [size] bytes - -PARALLEL AGENTS: -- Use `new_context` for isolated sessions -- One Chrome, many parallel agent contexts - -DEVICES: mobile, tablet, laptop, iphone_14, pixel_7, ipad_pro - -CATEGORIES: -- Navigation: navigate, set_content, content, url, title, reload, go_back/forward -- Input: click, dblclick, type, fill, clear, press -- Forms: select_option, check, uncheck, upload -- Mouse: hover, drag, mouse_move/down/up, mouse_wheel, scroll -- Touch: tap, swipe, pinch -- Locators: locator, get_by_role/text/label/placeholder/test_id/alt_text/title -- Composition: first, last, nth, filter, all, count -- Content: get_text, get_inner_text, get_attribute, get_value, get_html, get_bounding_box -- State: is_visible/hidden/enabled/editable/checked -- Assertions: expect_visible/hidden/enabled/text/value/checked/url/title/count/attribute -- Wait: wait, wait_for_load/url/event/request/response/function -- Page: screenshot, pdf, snapshot, evaluate, focus, blur -- Device: viewport, emulate, geolocation, permissions -- Network: route (mock/block), unroute -- Storage: cookies, clear_cookies, storage, storage_state -- Events: on, off -- Dialogs: dialog -- Files: file_chooser, download -- Browser: new_page, new_context, new_tab, close_tab, tabs, status -- Debug: trace_start/stop, highlight, console, errors -""" - - async def _get_page(self, device: Optional[str] = None) -> Page: - """Get page from shared pool.""" - pool = await BrowserPool.get_instance() - return await pool.ensure_browser( - headless=self.headless, - cdp_endpoint=self.cdp_endpoint, - device=device, - ) - - def _get_locator( - self, page: Page, selector: str, frame: Optional[str] = None - ) -> Locator: - """Get a locator, optionally within a frame.""" - if frame: - return page.frame_locator(frame).locator(selector) - return page.locator(selector) - - async def call(self, ctx, action: str, **kwargs) -> dict[str, Any]: - """Execute browser action.""" - return await self.execute(action=action, **kwargs) - - async def execute( - self, - action: str, - # Selectors - url: Optional[str] = None, - selector: Optional[str] = None, - ref: Optional[str] = None, - target_selector: Optional[str] = None, - # Text/Values - text: Optional[str] = None, - value: Optional[str] = None, - key: Optional[str] = None, - code: Optional[str] = None, - html: Optional[str] = None, - attribute: Optional[str] = None, - # Locator options - role: Optional[str] = None, - name: Optional[str] = None, - exact: bool = False, - # Locator composition - index: Optional[int] = None, - has_text: Optional[str] = None, - has_not_text: Optional[str] = None, - has: Optional[str] = None, # Nested selector - # Files - files: Optional[list[str]] = None, - # Mouse/Touch - x: Optional[int] = None, - y: Optional[int] = None, - button: Optional[str] = None, - delta_x: Optional[int] = None, - delta_y: Optional[int] = None, - direction: Optional[str] = None, - distance: Optional[int] = None, - scale: Optional[float] = None, - # Viewport/Device - width: Optional[int] = None, - height: Optional[int] = None, - device: Optional[str] = None, - # Geolocation - latitude: Optional[float] = None, - longitude: Optional[float] = None, - accuracy: Optional[float] = None, - # Permissions - permission: Optional[str] = None, - # Network - pattern: Optional[str] = None, - response: Optional[Union[dict, str]] = None, - status_code: Optional[int] = None, - block: bool = False, - # Wait/Assert options - state: Optional[str] = None, - event: Optional[str] = None, - expected: Optional[str] = None, - not_: bool = False, # For negative assertions - # Options - timeout: Optional[int] = None, - full_page: bool = False, - tab_index: Optional[int] = None, - tab_id: Optional[Union[str, int]] = None, - client_id: Optional[str] = None, - target_browser: Optional[str] = None, - cdp_endpoint: Optional[str] = None, - headless: Optional[bool] = None, - # Storage - cookies: Optional[list[dict]] = None, - storage_type: Optional[str] = None, - storage_data: Optional[dict] = None, - # Auth - auth_file: Optional[str] = None, - # Dialog - accept: bool = True, - prompt_text: Optional[str] = None, - # Frame - frame: Optional[str] = None, - # Trace - trace_path: Optional[str] = None, - # Filter - level: Optional[str] = None, - ) -> dict[str, Any]: - """Execute browser action with full Playwright API support. - - Automatically uses Hanzo browser extension if connected, - falling back to Playwright for headless automation. - """ - timeout = timeout or self.timeout - sel = selector or ref - - # === LOCAL ACTIONS (handled in-process, no extension/Playwright) === - if action == "list_mcp_instances": - try: - from hanzo_tools.browser.zap_server import ZapServer - - instances = ZapServer.list_mcp_instances() - return { - "success": True, - "mcp_instances": instances, - "count": len(instances), - } - except Exception as e: - return {"error": f"failed to list mcp instances: {e}"} - - if action == "claim_browser": - try: - from hanzo_tools.browser.zap_server import ( - DEFAULT_LEASE_TTL, - get_server, - ) - - srv = get_server() - if srv is None: - return {"error": "zap server not running"} - client = srv.resolve_client( - client_id=client_id, browser=target_browser - ) - if client is None: - return {"error": "no matching extension client"} - ttl = float(timeout) / 1000 if timeout else DEFAULT_LEASE_TTL - lease = srv.claim(client.client_id, ttl=ttl) - return { - "success": True, - "client_id": lease.client_id, - "holder": lease.holder, - "expires_at": lease.expires_at, - } - except Exception as e: - return {"error": str(e)} - - if action == "release_browser": - try: - from hanzo_tools.browser.zap_server import get_server - - srv = get_server() - if srv is None: - return {"error": "zap server not running"} - # Without an explicit client_id, drop every lease this MCP holds. - if client_id: - released = srv.release(client_id) - return {"success": released, "client_id": client_id} - released_all = [] - for c in list(srv.clients): - if srv.release(c.client_id): - released_all.append(c.client_id) - return {"success": True, "released": released_all} - except Exception as e: - return {"error": str(e)} - - # === BACKEND-AWARE ROUTING === - # Actions supported by the CDP bridge / browser extension - extension_actions = { - "navigate", "navigate_back", "reload", "url", "title", "content", - "screenshot", "snapshot", "click", "dblclick", "hover", - "type", "fill", "clear", "press_key", "press", - "select_option", "check", "uncheck", - "evaluate", "wait", "wait_for_load", - "go_back", "go_forward", "get_url", "get_title", "get_tab_info", - "wait_for_navigation", "get_history", "create_tab", "close_tab", - "fetch", - "get_html", "set_html", "get_text", "set_text", - "get_attribute", "set_attribute", "remove_attribute", - "set_style", "add_class", "remove_class", - "insert_element", "remove_element", - "wait_for_selector", "query_selector_all", - "get_element_info", "get_page_info", - "observe_mutations", "computed_styles", "bounding_rects", - "inject_script", "inject_css", - "local_storage", "cookies", - "tabs", "new_tab", "select_tab", - # Multi-browser routing (bridge v1.9.0+) โ€” list connected - # providers and persist a default-browser pick. Without one - # the bridge auto-prefers firefox > safari > edge > chrome. - "list_browsers", "browsers", "set_default_browser", "use_browser", - "console", "network_requests", "status", - # Takeover actions (Phase 3) - "takeover", "release", - } - - backend = self.backend - # Resolve browser filter from backend preference. Per-call override - # (target_browser) wins over global backend so a single MCP session - # can address Chrome and Firefox at different moments. - browser_filter = (target_browser or - (backend if backend in ("firefox", "chrome") else None)) - - # Skip extension entirely for "playwright" backend - use_extension = backend != "playwright" and action in extension_actions - - if use_extension: - ext_result = await _extension_command( - action, - browser=browser_filter, - tab_id=tab_id, - client_id=client_id, - url=url, - selector=sel, - text=text, - value=value, - code=code, - expression=code, - full_page=full_page, - key=key, - index=index, - tab_index=tab_index, - timeout=timeout, - state=state, - level=level, - ) - if ext_result is not None and "error" not in ext_result: - # Build normalized response from extension bridge result - resp: dict[str, Any] = {"success": True, "source": "extension"} - if isinstance(ext_result, dict): - resp.update(ext_result) - else: - resp["result"] = ext_result - return resp - - # For explicit backends (firefox/chrome/extension), don't fall back to Playwright - if backend in ("firefox", "chrome", "extension"): - error_detail = "" - if ext_result and "error" in ext_result: - error_detail = f": {ext_result['error']}" - return { - "error": (f"Browser backend '{backend}' not available{error_detail}. " - f"Ensure the Hanzo extension is installed and connected."), - "action": action, - "backend": backend, - } - - # === FALL BACK TO PLAYWRIGHT === - if not PLAYWRIGHT_AVAILABLE: - ext_connected = await _check_extension(browser=browser_filter) - if ext_connected: - msg = (f"Action '{action}' is not supported by the browser extension " - f"and Playwright is not installed for fallback. " - f"Install Playwright: pip install playwright && playwright install chromium") - else: - msg = ("Browser extension not connected and Playwright not installed. " - "Either connect the Hanzo browser extension (start CDP bridge server) " - "or install Playwright: pip install playwright && playwright install chromium") - return {"error": msg, "action": action} - - pool = await BrowserPool.get_instance() - - try: - # === Connection === - if action == "connect": - endpoint = cdp_endpoint or self.cdp_endpoint - if not endpoint: - return {"error": "cdp_endpoint required"} - page = await pool.ensure_browser( - headless=self.headless, cdp_endpoint=endpoint - ) - return { - "success": True, - "connected": True, - "endpoint": endpoint, - "url": page.url, - } - - # === Device Emulation === - if action == "emulate": - if not device: - return { - "error": f"device required. Available: {list(DEVICES.keys())}" - } - if device not in DEVICES: - return { - "error": f"Unknown device. Available: {list(DEVICES.keys())}" - } - page = await pool.ensure_browser( - headless=self.headless, - cdp_endpoint=self.cdp_endpoint, - device=device, - ) - settings = DEVICES[device] - return {"success": True, "device": device, **settings} - - page = await self._get_page(device) - - # === Core Page Navigation & Lifecycle === - if action == "navigate": - if not url: - return {"error": "url required"} - resp = await page.goto( - url, timeout=timeout, wait_until=state or "domcontentloaded" - ) - return { - "success": True, - "url": page.url, - "title": await page.title(), - "status": resp.status if resp else None, - } - - elif action == "set_content": - if not html: - return {"error": "html required"} - await page.set_content(html, timeout=timeout) - return {"success": True, "set_content": True} - - elif action == "content": - return {"success": True, "html": await page.content()} - - elif action == "url": - return {"success": True, "url": page.url} - - elif action == "title": - return {"success": True, "title": await page.title()} - - elif action == "reload": - resp = await page.reload(timeout=timeout) - return { - "success": True, - "url": page.url, - "status": resp.status if resp else None, - } - - elif action == "go_back": - resp = await page.go_back(timeout=timeout) - return {"success": True, "url": page.url, "navigated": resp is not None} - - elif action == "go_forward": - resp = await page.go_forward(timeout=timeout) - return {"success": True, "url": page.url, "navigated": resp is not None} - - # === Input === - elif action == "click": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - await loc.click(timeout=timeout, button=button or "left") - return {"success": True, "clicked": sel} - - elif action == "dblclick": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - await loc.dblclick(timeout=timeout) - return {"success": True, "double_clicked": sel} - - elif action == "type": - if not sel or text is None: - return {"error": "selector and text required"} - loc = self._get_locator(page, sel, frame) - await loc.type(text, timeout=timeout) - return {"success": True, "typed": len(text), "selector": sel} - - elif action == "fill": - if not sel or text is None: - return {"error": "selector and text required"} - loc = self._get_locator(page, sel, frame) - await loc.fill(text, timeout=timeout) - return {"success": True, "filled": sel} - - elif action == "clear": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - await loc.clear(timeout=timeout) - return {"success": True, "cleared": sel} - - elif action == "press": - if not key: - return {"error": "key required"} - if sel: - loc = self._get_locator(page, sel, frame) - await loc.press(key, timeout=timeout) - else: - await page.keyboard.press(key) - return {"success": True, "pressed": key} - - # === Forms === - elif action == "select_option": - if not sel or value is None: - return {"error": "selector and value required"} - loc = self._get_locator(page, sel, frame) - selected = await loc.select_option( - value if isinstance(value, list) else [value], timeout=timeout - ) - return {"success": True, "selected": selected} - - elif action == "check": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - await loc.check(timeout=timeout) - return {"success": True, "checked": sel} - - elif action == "uncheck": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - await loc.uncheck(timeout=timeout) - return {"success": True, "unchecked": sel} - - elif action == "upload": - if not sel or not files: - return {"error": "selector and files required"} - loc = self._get_locator(page, sel, frame) - await loc.set_input_files(files, timeout=timeout) - return {"success": True, "uploaded": len(files)} - - # === Mouse === - elif action == "hover": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - await loc.hover(timeout=timeout) - return {"success": True, "hovered": sel} - - elif action == "drag": - if not sel or not target_selector: - return {"error": "selector and target_selector required"} - await page.drag_and_drop(sel, target_selector, timeout=timeout) - return {"success": True, "dragged": sel, "to": target_selector} - - elif action == "mouse_move": - if x is None or y is None: - return {"error": "x and y required"} - await page.mouse.move(x, y) - return {"success": True, "moved_to": {"x": x, "y": y}} - - elif action == "mouse_down": - await page.mouse.down(button=button or "left") - return {"success": True, "button_down": button or "left"} - - elif action == "mouse_up": - await page.mouse.up(button=button or "left") - return {"success": True, "button_up": button or "left"} - - elif action == "mouse_wheel": - await page.mouse.wheel(delta_x or 0, delta_y or 0) - return { - "success": True, - "scrolled": {"delta_x": delta_x or 0, "delta_y": delta_y or 0}, - } - - elif action == "scroll": - if sel: - loc = self._get_locator(page, sel, frame) - await loc.scroll_into_view_if_needed(timeout=timeout) - return {"success": True, "scrolled_to": sel} - else: - await page.evaluate( - f"window.scrollBy({delta_x or 0}, {delta_y or 300})" - ) - return { - "success": True, - "scrolled": { - "delta_x": delta_x or 0, - "delta_y": delta_y or 300, - }, - } - - # === Touch === - elif action == "tap": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - await loc.tap(timeout=timeout) - return {"success": True, "tapped": sel} - - elif action == "swipe": - if not sel or not direction: - return {"error": "selector and direction required"} - loc = self._get_locator(page, sel, frame) - box = await loc.bounding_box() - if not box: - return {"error": "Element not visible"} - cx, cy = box["x"] + box["width"] / 2, box["y"] + box["height"] / 2 - dist = distance or 200 - offsets = { - "left": (-dist, 0), - "right": (dist, 0), - "up": (0, -dist), - "down": (0, dist), - } - dx, dy = offsets.get(direction, (0, 0)) - await page.touchscreen.tap(cx, cy) - await page.mouse.move(cx, cy) - await page.mouse.down() - await page.mouse.move(cx + dx, cy + dy, steps=10) - await page.mouse.up() - return {"success": True, "swiped": sel, "direction": direction} - - elif action == "pinch": - if not sel: - return {"error": "selector required"} - zoom = scale or 0.5 - await page.evaluate( - f"""(sel) => {{ - const el = document.querySelector(sel); - if (el) el.dispatchEvent(new WheelEvent('wheel', {{deltaY: {"-100" if zoom > 1 else "100"}, ctrlKey: true, bubbles: true}})); - }}""", - sel, - ) - return {"success": True, "pinched": sel, "scale": zoom} - - # === Locator Creation === - elif action == "locator": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - cnt = await loc.count() - return { - "success": True, - "selector": sel, - "count": cnt, - "visible": await loc.first.is_visible() if cnt > 0 else False, - } - - elif action == "frame_locator": - if not sel: - return {"error": "selector required"} - # Just validate frame exists - frame_loc = page.frame_locator(sel) - return { - "success": True, - "frame": sel, - "note": "Use frame parameter in subsequent actions", - } - - # === Built-in Locators === - elif action == "get_by_role": - if not role: - return {"error": "role required"} - loc = page.get_by_role(role, name=name, exact=exact) - cnt = await loc.count() - return {"success": True, "role": role, "name": name, "count": cnt} - - elif action == "get_by_text": - if not text: - return {"error": "text required"} - loc = page.get_by_text(text, exact=exact) - return {"success": True, "text": text, "count": await loc.count()} - - elif action == "get_by_label": - if not text: - return {"error": "text required"} - loc = page.get_by_label(text, exact=exact) - return {"success": True, "label": text, "count": await loc.count()} - - elif action == "get_by_placeholder": - if not text: - return {"error": "text required"} - loc = page.get_by_placeholder(text, exact=exact) - return { - "success": True, - "placeholder": text, - "count": await loc.count(), - } - - elif action == "get_by_test_id": - if not text: - return {"error": "text required"} - loc = page.get_by_test_id(text) - return {"success": True, "test_id": text, "count": await loc.count()} - - elif action == "get_by_alt_text": - if not text: - return {"error": "text required"} - loc = page.get_by_alt_text(text, exact=exact) - return {"success": True, "alt_text": text, "count": await loc.count()} - - elif action == "get_by_title": - if not text: - return {"error": "text required"} - loc = page.get_by_title(text, exact=exact) - return {"success": True, "title": text, "count": await loc.count()} - - # === Locator Composition === - elif action == "first": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame).first - visible = await loc.is_visible() - return {"success": True, "first": True, "visible": visible} - - elif action == "last": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame).last - visible = await loc.is_visible() - return {"success": True, "last": True, "visible": visible} - - elif action == "nth": - if not sel or index is None: - return {"error": "selector and index required"} - loc = self._get_locator(page, sel, frame).nth(index) - visible = await loc.is_visible() - return {"success": True, "nth": index, "visible": visible} - - elif action == "filter": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - filter_opts = {} - if has_text: - filter_opts["has_text"] = has_text - if has_not_text: - filter_opts["has_not_text"] = has_not_text - if has: - filter_opts["has"] = page.locator(has) - if filter_opts: - loc = loc.filter(**filter_opts) - return {"success": True, "filtered": True, "count": await loc.count()} - - elif action == "all": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - elements = await loc.all() - results = [] - for i, el in enumerate(elements): - results.append( - { - "index": i, - "visible": await el.is_visible(), - "text": await el.text_content(), - } - ) - return { - "success": True, - "count": len(results), - "elements": results[:20], - } # Limit to 20 - - elif action == "count": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - return {"success": True, "count": await loc.count()} - - # === Content Extraction === - elif action == "get_text": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - return { - "success": True, - "text": await loc.text_content(timeout=timeout), - } - - elif action == "get_inner_text": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - return { - "success": True, - "inner_text": await loc.inner_text(timeout=timeout), - } - - elif action == "get_attribute": - if not sel or not attribute: - return {"error": "selector and attribute required"} - loc = self._get_locator(page, sel, frame) - return { - "success": True, - "attribute": attribute, - "value": await loc.get_attribute(attribute, timeout=timeout), - } - - elif action == "get_value": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - return { - "success": True, - "value": await loc.input_value(timeout=timeout), - } - - elif action == "get_html": - if sel: - loc = self._get_locator(page, sel, frame) - return { - "success": True, - "html": await loc.inner_html(timeout=timeout), - } - return {"success": True, "html": await page.content()} - - elif action == "get_bounding_box": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - box = await loc.bounding_box(timeout=timeout) - return ( - {"success": True, "bounding_box": box} - if box - else {"error": "Element not visible"} - ) - - # === State Checks === - elif action == "is_visible": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - return { - "success": True, - "visible": await loc.is_visible(timeout=timeout), - } - - elif action == "is_hidden": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - return {"success": True, "hidden": await loc.is_hidden(timeout=timeout)} - - elif action == "is_enabled": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - return { - "success": True, - "enabled": await loc.is_enabled(timeout=timeout), - } - - elif action == "is_editable": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - return { - "success": True, - "editable": await loc.is_editable(timeout=timeout), - } - - elif action == "is_checked": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - return { - "success": True, - "checked": await loc.is_checked(timeout=timeout), - } - - # === Assertions (expect) === - elif action.startswith("expect_"): - from playwright.async_api import expect - - if action == "expect_url": - pattern = expected or url or pattern - if not pattern: - return {"error": "expected URL pattern required"} - try: - await expect(page).to_have_url( - re.compile(pattern) if "*" in pattern else pattern, - timeout=timeout, - ) - return {"success": True, "assertion": "url", "passed": True} - except Exception as e: - return { - "success": False, - "assertion": "url", - "passed": False, - "error": str(e), - } - - elif action == "expect_title": - pattern = expected or text - if not pattern: - return {"error": "expected title required"} - try: - await expect(page).to_have_title( - re.compile(pattern) if "*" in pattern else pattern, - timeout=timeout, - ) - return {"success": True, "assertion": "title", "passed": True} - except Exception as e: - return { - "success": False, - "assertion": "title", - "passed": False, - "error": str(e), - } - - elif not sel: - return {"error": "selector required for element assertions"} - - loc = self._get_locator(page, sel, frame) - assertion_type = action.replace("expect_", "") - - try: - if assertion_type == "visible": - if not_: - await expect(loc).not_to_be_visible(timeout=timeout) - else: - await expect(loc).to_be_visible(timeout=timeout) - elif assertion_type == "hidden": - if not_: - await expect(loc).not_to_be_hidden(timeout=timeout) - else: - await expect(loc).to_be_hidden(timeout=timeout) - elif assertion_type == "enabled": - if not_: - await expect(loc).not_to_be_enabled(timeout=timeout) - else: - await expect(loc).to_be_enabled(timeout=timeout) - elif assertion_type == "text": - if not expected and not text: - return {"error": "expected text required"} - exp = expected or text - if not_: - await expect(loc).not_to_have_text(exp, timeout=timeout) - else: - await expect(loc).to_have_text(exp, timeout=timeout) - elif assertion_type == "value": - if not expected and not value: - return {"error": "expected value required"} - exp = expected or value - if not_: - await expect(loc).not_to_have_value(exp, timeout=timeout) - else: - await expect(loc).to_have_value(exp, timeout=timeout) - elif assertion_type == "checked": - if not_: - await expect(loc).not_to_be_checked(timeout=timeout) - else: - await expect(loc).to_be_checked(timeout=timeout) - elif assertion_type == "count": - if index is None: - return {"error": "index (expected count) required"} - await expect(loc).to_have_count(index, timeout=timeout) - elif assertion_type == "attribute": - if not attribute or not expected: - return {"error": "attribute and expected required"} - if not_: - await expect(loc).not_to_have_attribute( - attribute, expected, timeout=timeout - ) - else: - await expect(loc).to_have_attribute( - attribute, expected, timeout=timeout - ) - else: - return {"error": f"Unknown assertion: {assertion_type}"} - - return { - "success": True, - "assertion": assertion_type, - "passed": True, - "selector": sel, - } - except Exception as e: - return { - "success": False, - "assertion": assertion_type, - "passed": False, - "selector": sel, - "error": str(e), - } - - # === Page Actions === - elif action == "screenshot": - opts = {"full_page": full_page, "type": "png"} - if sel: - loc = self._get_locator(page, sel, frame) - data = await loc.screenshot(**opts) - else: - data = await page.screenshot(**opts) - return { - "success": True, - "format": "png", - "size": len(data), - "base64": base64.b64encode(data).decode(), - } - - elif action == "pdf": - data = await page.pdf() - return { - "success": True, - "format": "pdf", - "size": len(data), - "base64": base64.b64encode(data).decode(), - } - - elif action == "snapshot": - return { - "success": True, - "url": page.url, - "title": await page.title(), - "snapshot": await page.accessibility.snapshot(), - } - - elif action == "evaluate": - if not code: - return {"error": "code required"} - result = await page.evaluate(code) - return {"success": True, "result": result} - - elif action == "focus": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - await loc.focus(timeout=timeout) - return {"success": True, "focused": sel} - - elif action == "blur": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - await loc.blur(timeout=timeout) - return {"success": True, "blurred": sel} - - elif action == "highlight": - if not sel: - return {"error": "selector required"} - loc = self._get_locator(page, sel, frame) - await loc.highlight() - return {"success": True, "highlighted": sel} - - # === Wait Primitives === - elif action == "wait": - if sel: - loc = self._get_locator(page, sel, frame) - await loc.wait_for(timeout=timeout, state=state or "visible") - return {"success": True, "found": sel} - elif timeout: - await asyncio.sleep(timeout / 1000) - return {"success": True, "waited_ms": timeout} - return {"error": "selector or timeout required"} - - elif action == "wait_for_load": - await page.wait_for_load_state(state or "load", timeout=timeout) - return {"success": True, "state": state or "load"} - - elif action == "wait_for_url": - if not pattern and not url: - return {"error": "pattern or url required"} - await page.wait_for_url(pattern or url, timeout=timeout) - return {"success": True, "url": page.url} - - elif action == "wait_for_event": - if not event: - return { - "error": "event required (request, response, download, filechooser, popup)" - } - result = await page.wait_for_event(event, timeout=timeout) - if event == "request": - return { - "success": True, - "event": event, - "url": result.url, - "method": result.method, - } - elif event == "response": - return { - "success": True, - "event": event, - "url": result.url, - "status": result.status, - } - elif event == "download": - return { - "success": True, - "event": event, - "filename": result.suggested_filename, - } - return {"success": True, "event": event} - - elif action == "wait_for_request": - if not pattern: - return {"error": "pattern required"} - req = await page.wait_for_request(pattern, timeout=timeout) - return {"success": True, "url": req.url, "method": req.method} - - elif action == "wait_for_response": - if not pattern: - return {"error": "pattern required"} - resp = await page.wait_for_response(pattern, timeout=timeout) - return {"success": True, "url": resp.url, "status": resp.status} - - elif action == "wait_for_function": - if not code: - return {"error": "code (JavaScript function) required"} - await page.wait_for_function(code, timeout=timeout) - return {"success": True, "function_returned_truthy": True} - - # === Viewport/Device === - elif action == "viewport": - if width is None or height is None: - return {"success": True, "viewport": page.viewport_size} - await page.set_viewport_size({"width": width, "height": height}) - return {"success": True, "viewport": {"width": width, "height": height}} - - elif action == "geolocation": - if latitude is None or longitude is None: - return {"error": "latitude and longitude required"} - await pool._context.set_geolocation( - { - "latitude": latitude, - "longitude": longitude, - "accuracy": accuracy or 100, - } - ) - return { - "success": True, - "geolocation": {"lat": latitude, "lon": longitude}, - } - - elif action == "permissions": - if not permission: - return {"error": "permission required"} - await pool._context.grant_permissions([permission]) - return {"success": True, "granted": permission} - - # === Network === - elif action == "route": - if not pattern: - return {"error": "pattern required"} - - async def handle(route: Route): - if block: - await route.abort() - elif response: - body = ( - json.dumps(response) - if isinstance(response, dict) - else response - ) - await route.fulfill( - status=status_code or 200, - content_type="application/json", - body=body, - ) - else: - await route.continue_() - - await page.route(pattern, handle) - pool._state.routes[pattern] = { - "block": block, - "mock": response is not None, - } - return {"success": True, "route": pattern} - - elif action == "unroute": - if not pattern: - return {"error": "pattern required"} - await page.unroute(pattern) - pool._state.routes.pop(pattern, None) - return {"success": True, "unrouted": pattern} - - # === Storage === - elif action == "cookies": - if cookies: - await pool._context.add_cookies(cookies) - return {"success": True, "set_cookies": len(cookies)} - return {"success": True, "cookies": await pool._context.cookies()} - - elif action == "clear_cookies": - await pool._context.clear_cookies() - return {"success": True, "cleared_cookies": True} - - elif action == "storage": - st = storage_type or "local" - store = "localStorage" if st == "local" else "sessionStorage" - if storage_data: - for k, v in storage_data.items(): - await page.evaluate( - f"{store}.setItem('{k}', '{json.dumps(v) if isinstance(v, (dict, list)) else v}')" - ) - return {"success": True, "set_keys": list(storage_data.keys())} - return { - "success": True, - "data": await page.evaluate( - f"Object.fromEntries(Object.entries({store}))" - ), - } - - elif action == "storage_state": - if not auth_file: - return {"error": "auth_file required"} - path = Path(auth_file) - if path.exists(): - storage = json.loads(path.read_text()) - await pool._context.add_cookies(storage.get("cookies", [])) - return {"success": True, "loaded": auth_file} - storage_state = await pool._context.storage_state() - path.write_text(json.dumps(storage_state, indent=2)) - return {"success": True, "saved": auth_file} - - # === Events === - elif action == "on": - if not event: - return {"error": "event required"} - # Events are auto-handled by _setup_page_listeners - return { - "success": True, - "listening": event, - "note": "Use console/errors/dialog actions to retrieve captured events", - } - - elif action == "off": - return { - "success": True, - "note": "Event listeners managed automatically", - } - - # === Dialogs === - elif action == "dialog": - if pool._state.pending_dialog: - d = pool._state.pending_dialog - if accept: - await d.accept(prompt_text or "") - else: - await d.dismiss() - pool._state.pending_dialog = None - return { - "success": True, - "type": d.type, - "message": d.message, - "accepted": accept, - } - return {"error": "No pending dialog"} - - # === Frames === - elif action == "frame": - if not sel: - return {"error": "selector required for frame"} - return { - "success": True, - "frame": sel, - "note": "Use frame parameter in subsequent actions", - } - - elif action == "main_frame": - return {"success": True, "frame": "main"} - - # === File Chooser & Downloads === - elif action == "file_chooser": - if pool._state.pending_file_chooser: - fc = pool._state.pending_file_chooser - if files: - await fc.set_files(files) - pool._state.pending_file_chooser = None - return {"success": True, "uploaded": len(files)} - return { - "success": True, - "file_chooser_pending": True, - "multiple": fc.is_multiple, - } - return {"error": "No pending file chooser. Trigger an upload first."} - - elif action == "download": - if pool._state.pending_download: - d = pool._state.pending_download - path = await d.path() - pool._state.pending_download = None - return { - "success": True, - "filename": d.suggested_filename, - "path": str(path) if path else None, - "url": d.url, - } - # Trigger download by clicking - if sel: - async with page.expect_download(timeout=timeout) as dl: - await page.click(sel) - d = await dl.value - return { - "success": True, - "filename": d.suggested_filename, - "url": d.url, - } - return {"error": "No pending download and no selector to click"} - - # === Console/Errors === - elif action == "console": - msgs = pool._state.console_messages - if level: - msgs = [m for m in msgs if m["type"] == level] - return { - "success": True, - "messages": msgs[-50:], - "count": len(msgs), - } # Last 50 - - elif action == "errors": - return { - "success": True, - "errors": pool._state.page_errors[-20:], - "count": len(pool._state.page_errors), - } - - # === Browser/Context === - elif action == "close": - await pool.close() - return {"success": True, "closed": True} - - elif action == "new_page" or action == "new_tab": - new_page = await pool.new_page(url) - return { - "success": True, - "page_index": len(pool.pages) - 1, - "url": new_page.url, - } - - elif action == "new_context": - context = await pool.new_context(device=device) - page = await context.new_page() - pool._page = page - pool._pages.append(page) - pool._setup_page_listeners(page) - if url: - await page.goto(url) - return { - "success": True, - "context": "new", - "device": device, - "isolated": True, - "url": page.url, - } - - elif action == "close_tab": - await pool.close_page(tab_index) - return {"success": True, "remaining_pages": len(pool.pages)} - - elif action == "tabs": - if tab_index is not None: - try: - page = await pool.switch_page(tab_index) - return { - "success": True, - "switched_to": tab_index, - "url": page.url, - } - except ValueError as e: - return {"error": str(e)} - return { - "success": True, - "count": len(pool.pages), - "tabs": [ - {"index": i, "url": p.url} for i, p in enumerate(pool.pages) - ], - } - - elif action == "set_headless": - new_headless = headless if headless is not None else not pool._headless - current_url = page.url if page else None - old_mode = "headless" if pool._headless else "headed" - await pool.close() - self.headless = new_headless - page = await pool.ensure_browser(headless=new_headless) - if current_url and current_url != "about:blank": - await page.goto(current_url) - return { - "success": True, - "previous_mode": old_mode, - "current_mode": "headless" if new_headless else "headed", - } - - elif action == "status": - return { - "success": True, - "initialized": pool._initialized, - "headless": pool._headless, - "device": pool._device, - "pages": len(pool.pages), - "contexts": len(pool._contexts), - "current_url": page.url if page else None, - "console_messages": len(pool._state.console_messages), - "errors": len(pool._state.page_errors), - "routes": list(pool._state.routes.keys()), - "tracing": pool._state.tracing, - } - - # === Debug === - elif action == "trace_start": - if pool._state.tracing: - return {"error": "Tracing already active"} - await pool._context.tracing.start( - screenshots=True, snapshots=True, sources=True - ) - pool._state.tracing = True - return {"success": True, "tracing": True} - - elif action == "trace_stop": - if not pool._state.tracing: - return {"error": "Tracing not active"} - path = trace_path or f"trace-{int(asyncio.get_event_loop().time())}.zip" - await pool._context.tracing.stop(path=path) - pool._state.tracing = False - return {"success": True, "trace_path": path} - - else: - return {"error": f"Unknown action: {action}"} - - except Exception as e: - logger.exception(f"Browser action failed: {action}") - return {"error": str(e), "action": action} - - def register(self, mcp_server: FastMCP) -> None: - """Register the browser tool with an MCP server.""" - tool_instance = self - - @mcp_server.tool(name=self.name, description=self.description) - async def browser( - action: Action, - url: Annotated[Optional[str], Field(description="URL")] = None, - selector: Annotated[ - Optional[str], Field(description="CSS/XPath selector") - ] = None, - ref: Annotated[ - Optional[str], Field(description="Alias for selector") - ] = None, - target_selector: Annotated[ - Optional[str], Field(description="Target for drag") - ] = None, - text: Annotated[ - Optional[str], Field(description="Text for type/fill/locators") - ] = None, - value: Annotated[ - Optional[str], Field(description="Value for select/assertions") - ] = None, - key: Annotated[Optional[str], Field(description="Key for press")] = None, - code: Annotated[Optional[str], Field(description="JavaScript code")] = None, - html: Annotated[ - Optional[str], Field(description="HTML for set_content") - ] = None, - attribute: Annotated[ - Optional[str], Field(description="Attribute name") - ] = None, - role: Annotated[Optional[str], Field(description="ARIA role")] = None, - name: Annotated[Optional[str], Field(description="Accessible name")] = None, - exact: Annotated[bool, Field(description="Exact text match")] = False, - index: Annotated[ - Optional[int], Field(description="Index for nth/count") - ] = None, - has_text: Annotated[ - Optional[str], Field(description="Filter by text") - ] = None, - has_not_text: Annotated[ - Optional[str], Field(description="Filter excluding text") - ] = None, - has: Annotated[ - Optional[str], Field(description="Filter by nested selector") - ] = None, - files: Annotated[ - Optional[list[str]], Field(description="Files for upload") - ] = None, - x: Annotated[Optional[int], Field(description="X coordinate")] = None, - y: Annotated[Optional[int], Field(description="Y coordinate")] = None, - button: Annotated[Optional[str], Field(description="Mouse button")] = None, - delta_x: Annotated[ - Optional[int], Field(description="Horizontal delta") - ] = None, - delta_y: Annotated[ - Optional[int], Field(description="Vertical delta") - ] = None, - direction: Annotated[ - Optional[str], Field(description="Swipe direction") - ] = None, - distance: Annotated[ - Optional[int], Field(description="Swipe distance") - ] = None, - scale: Annotated[Optional[float], Field(description="Pinch scale")] = None, - width: Annotated[Optional[int], Field(description="Viewport width")] = None, - height: Annotated[ - Optional[int], Field(description="Viewport height") - ] = None, - device: Annotated[ - Optional[str], Field(description="Device: mobile, tablet, laptop") - ] = None, - latitude: Annotated[ - Optional[float], Field(description="Geo latitude") - ] = None, - longitude: Annotated[ - Optional[float], Field(description="Geo longitude") - ] = None, - permission: Annotated[ - Optional[str], Field(description="Permission to grant") - ] = None, - pattern: Annotated[Optional[str], Field(description="URL pattern")] = None, - response: Annotated[ - Optional[dict], Field(description="Mock response") - ] = None, - status_code: Annotated[ - Optional[int], Field(description="Mock status") - ] = None, - block: Annotated[bool, Field(description="Block request")] = False, - state: Annotated[ - Optional[str], Field(description="Load state/wait state") - ] = None, - event: Annotated[Optional[str], Field(description="Event name")] = None, - expected: Annotated[ - Optional[str], Field(description="Expected value for assertions") - ] = None, - not_: Annotated[bool, Field(description="Negate assertion")] = False, - timeout: Annotated[Optional[int], Field(description="Timeout ms")] = None, - full_page: Annotated[ - bool, Field(description="Full page screenshot") - ] = False, - tab_index: Annotated[Optional[int], Field(description="Tab index in current window")] = None, - tab_id: Annotated[ - Optional[str], - Field( - description=( - "Target tab id. Accepts the targetId returned by the " - "'tabs' action (e.g. 'tab-1888868904') or a numeric tab " - "id. Required when many windows are open and the OS-active " - "tab is not the one you want โ€” without this every action " - "is dispatched to whatever tab is currently focused." - ) - ), - ] = None, - client_id: Annotated[ - Optional[str], - Field( - description=( - "Target a specific extension client (e.g. one Firefox " - "instance vs another). Use the client_id returned by 'status'." - ) - ), - ] = None, - target_browser: Annotated[ - Optional[str], - Field(description="Browser provider to dispatch to: 'chrome' | 'firefox'"), - ] = None, - cdp_endpoint: Annotated[ - Optional[str], Field(description="CDP endpoint") - ] = None, - headless: Annotated[ - Optional[bool], Field(description="Headless mode") - ] = None, - cookies: Annotated[ - Optional[list[dict]], Field(description="Cookies") - ] = None, - storage_type: Annotated[ - Optional[str], Field(description="local/session") - ] = None, - storage_data: Annotated[ - Optional[dict], Field(description="Storage data") - ] = None, - auth_file: Annotated[ - Optional[str], Field(description="Auth state file") - ] = None, - accept: Annotated[bool, Field(description="Accept dialog")] = True, - prompt_text: Annotated[ - Optional[str], Field(description="Dialog text") - ] = None, - frame: Annotated[Optional[str], Field(description="Frame selector")] = None, - trace_path: Annotated[ - Optional[str], Field(description="Trace output") - ] = None, - level: Annotated[Optional[str], Field(description="Console level")] = None, - ) -> dict[str, Any]: - """Complete browser automation with full Playwright API surface area.""" - return await tool_instance.execute( - action=action, - url=url, - selector=selector, - ref=ref, - target_selector=target_selector, - text=text, - value=value, - key=key, - code=code, - html=html, - attribute=attribute, - role=role, - name=name, - exact=exact, - index=index, - has_text=has_text, - has_not_text=has_not_text, - has=has, - files=files, - x=x, - y=y, - button=button, - delta_x=delta_x, - delta_y=delta_y, - direction=direction, - distance=distance, - scale=scale, - width=width, - height=height, - device=device, - latitude=latitude, - longitude=longitude, - permission=permission, - pattern=pattern, - response=response, - status_code=status_code, - block=block, - state=state, - event=event, - expected=expected, - not_=not_, - timeout=timeout, - full_page=full_page, - tab_index=tab_index, - tab_id=tab_id, - client_id=client_id, - target_browser=target_browser, - cdp_endpoint=cdp_endpoint, - headless=headless, - cookies=cookies, - storage_type=storage_type, - storage_data=storage_data, - auth_file=auth_file, - accept=accept, - prompt_text=prompt_text, - frame=frame, - trace_path=trace_path, - level=level, - ) - - -def create_browser_tool( - headless: bool = True, - cdp_endpoint: Optional[str] = None, - backend: Optional[str] = None, -) -> BrowserTool: - """Create a browser tool instance.""" - return BrowserTool(headless=headless, cdp_endpoint=cdp_endpoint, backend=backend) - - -async def launch_browser_server(port: int = 9222, headless: bool = False) -> str: - """Launch a persistent browser server for cross-MCP sharing.""" - if not PLAYWRIGHT_AVAILABLE: - raise RuntimeError("Playwright not installed") - - pw = await async_playwright().start() - await pw.chromium.launch( - headless=headless, - args=[ - f"--remote-debugging-port={port}", - "--disable-blink-features=AutomationControlled", - "--no-sandbox", - ], - ) - - endpoint = f"http://localhost:{port}" - logger.info(f"Browser server launched at {endpoint}") - return endpoint - - -# Default tool instance -browser_tool = BrowserTool() diff --git a/pkg/hanzo-tools-browser/hanzo_tools/browser/cdp_bridge_server.py b/pkg/hanzo-tools-browser/hanzo_tools/browser/cdp_bridge_server.py deleted file mode 100644 index b55440656..000000000 --- a/pkg/hanzo-tools-browser/hanzo_tools/browser/cdp_bridge_server.py +++ /dev/null @@ -1,1094 +0,0 @@ -""" -CDP Bridge Server for Hanzo Browser Extension Integration. - -This server acts as a bridge between: -1. hanzo-mcp's browser tool (via HTTP API on port 9224) -2. The Hanzo browser extension (via WebSocket on port 9223) - -MULTI-CLIENT SUPPORT: -- Multiple browser extensions can connect simultaneously -- Each client has a unique client_id (uuid sent on registration) -- Target IDs are namespaced: ":" -- Commands can specify client_id or target_id for routing -- Default client = most recently active - -ARCHITECTURE: -- WebSocket server (port 9223): Browser extensions connect here -- HTTP API server (port 9224): hanzo-mcp sends commands here - -The bridge auto-starts when hanzo-mcp loads browser tools. -No manual setup required. - -Environment Variables: - HANZO_CDP_BRIDGE_PORT: Port for the WebSocket server (default: 9223) - HANZO_CDP_BRIDGE_HOST: Host to bind to (default: localhost) - HANZO_CDP_HTTP_PORT: Port for the HTTP API (default: 9224) -""" - -import os -import json -import time -import uuid -import asyncio -import logging -from typing import Any, Callable, Optional -from pathlib import Path -from dataclasses import field, dataclass - -try: - import websockets - - # Detect websockets API version. - # >= 13: new asyncio API, handler(websocket) โ€” no path parameter. - # < 13: legacy API, handler(websocket, path). - _WS_LEGACY = False - try: - from websockets.asyncio.server import serve as ws_serve - WebSocketServerProtocol = Any - except ImportError: - from websockets.server import serve as ws_serve - WebSocketServerProtocol = Any - _WS_LEGACY = True - - WEBSOCKETS_AVAILABLE = True -except ImportError: - WEBSOCKETS_AVAILABLE = False - _WS_LEGACY = False - WebSocketServerProtocol = Any - -# Try to import aiohttp for HTTP API server -try: - from aiohttp import web - - AIOHTTP_AVAILABLE = True -except ImportError: - AIOHTTP_AVAILABLE = False - web = None - -logger = logging.getLogger(__name__) - - -@dataclass -class ExtensionClient: - """Represents a connected browser extension client.""" - - client_id: str - websocket: WebSocketServerProtocol - browser: str = "unknown" - profile: str = "default" - user_agent: str = "" - capabilities: list = field(default_factory=list) - connected_at: float = field(default_factory=time.time) - last_active: float = field(default_factory=time.time) - - def to_dict(self) -> dict: - return { - "client_id": self.client_id, - "browser": self.browser, - "profile": self.profile, - "capabilities": self.capabilities, - "connected_at": self.connected_at, - "last_active": self.last_active, - } - - -class CDPBridgeServer: - """WebSocket + HTTP server that bridges hanzo-mcp and browser extensions. - - Supports multiple browser extension clients simultaneously. - - Architecture: - - WebSocket (port 9223): Browser extensions connect here as CDP providers - - HTTP API (port 9224): hanzo-mcp sends commands here - """ - - def __init__( - self, - host: str = "localhost", - port: int = 9223, - http_port: int = 9224, - ): - self.host = host - self.port = port - self.http_port = http_port - - # Multi-client registry: client_id -> ExtensionClient - self.extension_clients: dict[str, ExtensionClient] = {} - # Reverse lookup: websocket -> client_id - self._ws_to_client_id: dict[WebSocketServerProtocol, str] = {} - - self.mcp_clients: set[WebSocketServerProtocol] = set() - self.pending_requests: dict[int, asyncio.Future] = {} - self.request_id = 0 - self._server = None - self._http_server = None - self._http_runner = None - - @property - def default_client_id(self) -> Optional[str]: - """Get the default client (most recently active).""" - if not self.extension_clients: - return None - # Return the client with most recent last_active timestamp - return max( - self.extension_clients.keys(), - key=lambda cid: self.extension_clients[cid].last_active, - ) - - @property - def default_client(self) -> Optional[ExtensionClient]: - """Get the default ExtensionClient.""" - cid = self.default_client_id - return self.extension_clients.get(cid) if cid else None - - async def start(self) -> None: - """Start the WebSocket and HTTP servers.""" - if not WEBSOCKETS_AVAILABLE: - raise ImportError( - "websockets package required for CDP bridge. Install with: pip install websockets" - ) - - # Build handler compatible with installed websockets version. - # Modern API (>= 13): handler(websocket) โ€” one positional arg. - # Legacy API (< 13): handler(websocket, path) โ€” two positional args. - if _WS_LEGACY: - handler = self._handle_connection # already accepts (ws, path) - else: - # Wrap so the modern serve() can call handler(websocket) with one arg. - async def handler(websocket: WebSocketServerProtocol) -> None: - await self._handle_connection(websocket) - - # Start WebSocket server for browser extensions - self._server = await ws_serve( - handler, - self.host, - self.port, - ) - logger.info(f"CDP Bridge WebSocket started on ws://{self.host}:{self.port}") - - # Start HTTP API server for hanzo-mcp - if AIOHTTP_AVAILABLE: - await self._start_http_server() - else: - logger.warning( - "aiohttp not installed, HTTP API disabled. Install with: pip install aiohttp" - ) - - async def _start_http_server(self) -> None: - """Start the HTTP API server.""" - app = web.Application() - app.router.add_get("/status", self._http_status) - app.router.add_get("/config", self._http_get_config) - app.router.add_post("/config", self._http_save_config) - app.router.add_post("/", self._http_command) - app.router.add_options("/", self._http_cors) # CORS preflight - app.router.add_options("/config", self._http_cors) # CORS preflight - - self._http_runner = web.AppRunner(app) - await self._http_runner.setup() - self._http_server = web.TCPSite(self._http_runner, self.host, self.http_port) - await self._http_server.start() - logger.info( - f"CDP Bridge HTTP API started on http://{self.host}:{self.http_port}" - ) - - async def _http_cors(self, request: "web.Request") -> "web.Response": - """Handle CORS preflight requests.""" - return web.Response( - status=200, - headers={ - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "GET, POST, OPTIONS", - "Access-Control-Allow-Headers": "Content-Type", - }, - ) - - async def _http_status(self, request: "web.Request") -> "web.Response": - """HTTP endpoint for status check.""" - connected = len(self.extension_clients) > 0 - return web.json_response( - { - "connected": connected, - "clients": len(self.extension_clients), - "client_list": [c.to_dict() for c in self.extension_clients.values()], - "default_client_id": self.default_client_id, - }, - headers={"Access-Control-Allow-Origin": "*"}, - ) - - @staticmethod - def _config_path() -> Path: - """Path to ~/.hanzo/extension/config.json.""" - return Path.home() / ".hanzo" / "extension" / "config.json" - - async def _http_get_config(self, request: "web.Request") -> "web.Response": - """GET /config โ€” read ~/.hanzo/extension/config.json.""" - config_path = self._config_path() - try: - if config_path.exists(): - config = json.loads(config_path.read_text()) - else: - config = {"backend": "auto"} - except Exception: - config = {"backend": "auto"} - return web.json_response( - config, - headers={"Access-Control-Allow-Origin": "*"}, - ) - - async def _http_save_config(self, request: "web.Request") -> "web.Response": - """POST /config โ€” save to ~/.hanzo/extension/config.json.""" - try: - data = await request.json() - except Exception as e: - return web.json_response( - {"error": f"Invalid JSON: {e}"}, - status=400, - headers={"Access-Control-Allow-Origin": "*"}, - ) - config_path = self._config_path() - config_path.parent.mkdir(parents=True, exist_ok=True) - - # Merge with existing config - try: - existing = json.loads(config_path.read_text()) if config_path.exists() else {} - except Exception: - existing = {} - existing.update(data) - - config_path.write_text(json.dumps(existing, indent=2)) - logger.info(f"Config saved to {config_path}: {existing}") - return web.json_response( - {"success": True, "config": existing}, - headers={"Access-Control-Allow-Origin": "*"}, - ) - - async def _http_command(self, request: "web.Request") -> "web.Response": - """HTTP endpoint for sending commands to browser extension.""" - try: - data = await request.json() - except Exception as e: - return web.json_response( - {"error": f"Invalid JSON: {e}"}, - status=400, - headers={"Access-Control-Allow-Origin": "*"}, - ) - - # Resolve target client โ€” supports client_id, target_id, or browser preference - target_client = self._resolve_client( - client_id=data.get("client_id"), - target_id=data.get("target_id"), - browser=data.get("browser"), - ) - - if not target_client: - browser_hint = data.get("browser") - if not self.extension_clients: - error_msg = "No browser extension connected" - elif browser_hint: - available = [c.browser for c in self.extension_clients.values()] - error_msg = (f"No {browser_hint} extension connected. " - f"Connected browsers: {available}") - else: - error_msg = (f"Client not found: " - f"{data.get('client_id') or data.get('target_id')}") - return web.json_response( - {"error": error_msg}, - status=503, - headers={"Access-Control-Allow-Origin": "*"}, - ) - - # Update target client's last_active - target_client.last_active = time.time() - - # Map HTTP action to CDP method. - action = data.get("action", "") - # Raw passthrough: the `cdp` action carries the real wire method in the - # `method` field (this is what CdpTool's HTTP fallback sends). Without - # this, _action_to_method("cdp") falls through to the literal "cdp", - # which the extension rejects with "Unknown method: cdp". - if action == "cdp": - method = data.get("method") or "" - if not method: - return web.json_response( - { - "error": "cdp action requires a 'method' (e.g. 'Runtime.evaluate')", - "client_id": target_client.client_id, - }, - status=400, - headers={"Access-Control-Allow-Origin": "*"}, - ) - else: - method = self._action_to_method(action) - params = self._build_params(data) - - # Forward to extension and wait for response - request_id = self._next_request_id() - - future: asyncio.Future = asyncio.get_event_loop().create_future() - self.pending_requests[request_id] = future - - try: - await target_client.websocket.send( - json.dumps( - { - "id": request_id, - "method": method, - "params": params, - } - ) - ) - - # Wait for response with timeout - response = await asyncio.wait_for(future, timeout=30.0) - - # Unwrap CDP-specific result formats for consistency with Playwright - raw_result = response.get("result", {}) - if method == "Runtime.evaluate" and isinstance(raw_result, dict): - # An evaluation error (e.g. page CSP blocking Function()/eval) - # must NOT be flattened to a silent `null`. The extension - # surfaces it via `error` / `exceptionDetails`; propagate it so - # the caller sees *why* evaluate failed instead of a bare null. - err = raw_result.get("error") - exc = raw_result.get("exceptionDetails") - if err or exc: - msg = err or ( - exc.get("text") if isinstance(exc, dict) else str(exc) - ) - return web.json_response( - { - "success": False, - "client_id": target_client.client_id, - "error": msg, - "exceptionDetails": exc, - "result": None, - }, - headers={"Access-Control-Allow-Origin": "*"}, - ) - # CDP returns {result: {type, value}} โ€” extract the value - cdp_result = raw_result.get("result", {}) - if isinstance(cdp_result, dict) and "value" in cdp_result: - raw_result = cdp_result["value"] - elif isinstance(cdp_result, dict) and cdp_result.get("type") == "undefined": - raw_result = None - - # Return result - return web.json_response( - { - "success": True, - "client_id": target_client.client_id, - "result": raw_result, - }, - headers={"Access-Control-Allow-Origin": "*"}, - ) - - except asyncio.TimeoutError: - self.pending_requests.pop(request_id, None) - return web.json_response( - { - "error": "Request timeout", - "client_id": target_client.client_id, - }, - status=504, - headers={"Access-Control-Allow-Origin": "*"}, - ) - - except Exception as e: - self.pending_requests.pop(request_id, None) - return web.json_response( - { - "error": str(e), - }, - status=500, - headers={"Access-Control-Allow-Origin": "*"}, - ) - - def _action_to_method(self, action: str) -> str: - """Map HTTP action to CDP/wire method. - - Aligned with hanzo_tools.browser.browser_tool._zap_method_for(). Same - action namespace, same expectations. If an action is already a wire - method (contains "."), it is forwarded as-is to allow direct - Page.navigate / DOM.querySelector / etc. usage. - """ - # Passthrough for already-qualified wire methods. - if "." in action: - return action - action_map = { - # Navigation / lifecycle - "navigate": "Page.navigate", - "reload": "Page.reload", - "go_back": "Page.goBack", - "go_forward": "Page.goForward", - "print_pdf": "Page.printToPDF", - "wait_for_navigation": "hanzo.waitForNavigation", - "wait_for_load_state": "Page.waitForLoadState", - # Tabs / targets - "tabs": "Target.getTargets", - "new_tab": "Target.createTarget", - "close_tab": "Target.closeTarget", - "activate_tab": "Target.activateTarget", - "url": "hanzo.url", - "title": "hanzo.title", - "tab_info": "hanzo.tabInfo", - "list_tabs": "hanzo.listTabs", - "history": "hanzo.getHistory", - # Observation - "screenshot": "hanzo.screenshot", - "page_info": "hanzo.getPageInfo", - "ax_tree": "Accessibility.getFullAXTree", - "status": "Browser.getVersion", - # DOM read - "get_text": "hanzo.getText", - "get_html": "hanzo.getHTML", - "get_attribute": "hanzo.getAttribute", - "get_element_info": "hanzo.getElementInfo", - "query_one": "DOM.querySelector", - "query_all": "hanzo.querySelectorAll", - "list_form": "hanzo.listForm", - "computed_styles": "hanzo.getComputedStyles", - "bounding_rects": "hanzo.getBoundingRects", - # DOM write โ€” selector-based - "click": "hanzo.click", - "dblclick": "hanzo.dblclick", - "hover": "hanzo.hover", - "fill": "hanzo.fill", - "check": "hanzo.check", - "uncheck": "hanzo.uncheck", - "select": "hanzo.select", - "type": "hanzo.type", - "clear": "hanzo.clear", - "focus": "DOM.focus", - "scroll_into_view": "DOM.scrollIntoView", - "set_text": "hanzo.setText", - "set_html": "hanzo.setHTML", - "set_attribute": "hanzo.setAttribute", - "remove_attribute": "hanzo.removeAttribute", - # DOM write โ€” CSP-safe text/label-based (RECOMMENDED for forms) - "click_text": "hanzo.clickByText", - "fill_label": "hanzo.fillByLabel", - "find_by_text": "hanzo.findByText", - "submit_form": "hanzo.submitForm", - "upload_file": "hanzo.uploadFile", - # Keyboard / mouse - "press": "hanzo.press", - "press_key": "Input.dispatchKeyEvent", - "mouse_event": "Input.dispatchMouseEvent", - "scroll": "hanzo.scroll", - "scroll_wheel": "Input.scrollWheel", - # Wait / observe - "wait_for_text": "hanzo.waitForText", - "wait_for_mutation": "hanzo.waitForMutation", - "wait_for_selector": "hanzo.waitForSelector", - "observe_start": "hanzo.observe", - "observe_read": "hanzo.observeRead", - "observe_stop": "hanzo.observeStop", - # Dialog - "dialog_accept": "hanzo.dialogAccept", - # Scripting - "evaluate": "Runtime.evaluate", - "inject_script": "hanzo.injectScript", - "inject_css": "hanzo.injectCSS", - # Cookies / storage - "cookies": "hanzo.getCookies", - "local_storage_get": "hanzo.getLocalStorage", - "local_storage_set": "hanzo.setLocalStorage", - # HTTP fetch via browser - "fetch": "hanzo.fetch", - } - return action_map.get(action, action) - - # Keys the HTTP transport itself owns โ€” never forward these as method params. - _ROUTING_KEYS = frozenset({ - "action", "method", "client_id", "target_id", "browser", "params", - }) - - def _build_params(self, data: dict) -> dict: - """Build CDP params from HTTP request data. - - Accepts BOTH: - - flat keys at top level: {"action": "x", "tabId": 1, "target": "top"} - - nested params object: {"action": "x", "params": {"tabId": 1, "target": "top"}} - - Snake_case โ†’ camelCase normalisation handled inline for the four - keys (tab_id, full_page, code, expression) that have historic aliases. - - Strategy: pass through every non-routing key. The extension's - canonical dispatcher (executeMethod) is the source of truth on what - each method accepts โ€” we don't need a whitelist here, because - unknown keys are simply ignored downstream. A whitelist creates - silent drops which is what bit us with `target`, `label`, `key`, - `intent`, `name`, etc. on the 1.9.16+ methods. - """ - params: dict = {} - - # 1) Pull from nested "params" object first (per-MCP-style callers) - nested = data.get("params") if isinstance(data.get("params"), dict) else {} - for k, v in nested.items(): - if k in self._ROUTING_KEYS: - continue - params[k] = v - - # 2) Then overlay flat top-level keys (per-curl-style callers). - # Top-level wins so explicit overrides are honoured. - for k, v in data.items(): - if k in self._ROUTING_KEYS: - continue - params[k] = v - - # 3) Snake_case aliases โ†’ camelCase (historical compatibility) - if "tab_id" in params and "tabId" not in params: - params["tabId"] = params.pop("tab_id") - if "full_page" in params and "fullPage" not in params: - params["fullPage"] = params.pop("full_page") - if "code" in params and "expression" not in params: - params["expression"] = params.pop("code") - - return params - - async def stop(self) -> None: - """Stop the WebSocket and HTTP servers.""" - if self._http_runner: - await self._http_runner.cleanup() - logger.info("CDP Bridge HTTP API stopped") - - if self._server: - self._server.close() - await self._server.wait_closed() - logger.info("CDP Bridge WebSocket stopped") - - async def _handle_connection( - self, - websocket: WebSocketServerProtocol, - path: str = "/", - ) -> None: - """Handle incoming WebSocket connections. - - Compatible with both legacy (websockets < 13) and modern (>= 13) APIs. - Legacy API passes (websocket, path); modern API passes only (websocket) - and path is available via websocket.request.path. - """ - # Modern websockets >= 13: path comes from websocket object, not parameter. - if hasattr(websocket, "request") and hasattr(websocket.request, "path"): - path = websocket.request.path or "/" - remote = getattr(websocket, "remote_address", None) - logger.info(f"New connection from {remote} on {path}") - - try: - # First message identifies the client type - message = await websocket.recv() - data = json.loads(message) - - if data.get("type") == "register": - role = data.get("role") - - if role == "cdp-provider": - # This is a browser extension - # Client can provide its own ID or we generate one - client_id = data.get("client_id") or str(uuid.uuid4())[:8] - - client = ExtensionClient( - client_id=client_id, - websocket=websocket, - browser=data.get("browser", "unknown"), - profile=data.get("profile", "default"), - user_agent=data.get("userAgent", ""), - capabilities=data.get("capabilities", []), - ) - - self.extension_clients[client_id] = client - self._ws_to_client_id[websocket] = client_id - - logger.info( - f"Browser extension registered: {client_id} ({client.browser}/{client.profile})" - ) - - # Send back the assigned client_id - await websocket.send( - json.dumps( - { - "type": "registered", - "client_id": client_id, - } - ) - ) - - # Notify MCP clients - for mcp in self.mcp_clients: - await mcp.send( - json.dumps( - { - "type": "provider_connected", - "client_id": client_id, - "browser": client.browser, - "profile": client.profile, - "capabilities": client.capabilities, - "total_clients": len(self.extension_clients), - } - ) - ) - - elif role == "mcp-client": - # This is hanzo-mcp or another MCP tool - self.mcp_clients.add(websocket) - logger.info("MCP client connected") - - # Send status with all connected clients - await websocket.send( - json.dumps( - { - "type": "status", - "connected": len(self.extension_clients) > 0, - "clients": [ - c.to_dict() for c in self.extension_clients.values() - ], - "default_client_id": self.default_client_id, - } - ) - ) - - # Handle subsequent messages - async for message in websocket: - await self._route_message(websocket, message) - - except websockets.exceptions.ConnectionClosed: - logger.info(f"Connection closed: {websocket.remote_address}") - finally: - # Clean up - if websocket in self._ws_to_client_id: - client_id = self._ws_to_client_id.pop(websocket) - self.extension_clients.pop(client_id, None) - logger.info(f"Extension client disconnected: {client_id}") - - # Notify MCP clients - for mcp in self.mcp_clients: - try: - await mcp.send( - json.dumps( - { - "type": "provider_disconnected", - "client_id": client_id, - "remaining_clients": len(self.extension_clients), - } - ) - ) - except Exception: - pass - - elif websocket in self.mcp_clients: - self.mcp_clients.discard(websocket) - - def _resolve_client( - self, - client_id: Optional[str] = None, - target_id: Optional[str] = None, - browser: Optional[str] = None, - ) -> Optional[ExtensionClient]: - """Resolve which client to route to. - - Priority order: - 1. Explicit client_id - 2. Namespaced target_id ("client_id:tab_id") - 3. Browser preference ("firefox", "chrome", etc.) - 4. Default (most recently active) - - Args: - client_id: Explicit client ID - target_id: Namespaced target like "clientid:tabid" - browser: Preferred browser name ("firefox", "chrome") - - Returns: - ExtensionClient or None - """ - # Parse target_id if provided (format: "client_id:tab_id") - if target_id and ":" in target_id: - cid = target_id.split(":")[0] - if cid in self.extension_clients: - return self.extension_clients[cid] - - # Use explicit client_id - if client_id and client_id in self.extension_clients: - return self.extension_clients[client_id] - - # Use browser preference โ€” match by browser name (case-insensitive) - if browser: - browser_lower = browser.lower() - matches = [ - c for c in self.extension_clients.values() - if browser_lower in c.browser.lower() - ] - if matches: - # Return most recently active matching client - return max(matches, key=lambda c: c.last_active) - # No match for requested browser - return None - - # Fall back to default (most recently active) - return self.default_client - - async def _route_message( - self, - sender: WebSocketServerProtocol, - message: str, - ) -> None: - """Route messages between extensions and MCP clients.""" - data = json.loads(message) - - # Check if message is from an extension - if sender in self._ws_to_client_id: - client_id = self._ws_to_client_id[sender] - # Update last_active - if client_id in self.extension_clients: - self.extension_clients[client_id].last_active = time.time() - - # Message from extension (response or event) - if "id" in data and data["id"] in self.pending_requests: - # This is a response to a pending request - future = self.pending_requests.pop(data["id"]) - # Add source client_id to response - data["_client_id"] = client_id - future.set_result(data) - elif data.get("type") == "event": - # Add source client_id to event - data["_client_id"] = client_id - # Broadcast event to all MCP clients - for mcp in self.mcp_clients: - try: - await mcp.send(json.dumps(data)) - except Exception: - pass - - elif sender in self.mcp_clients: - # Message from MCP client (command) - # Resolve target client - target_client = self._resolve_client( - client_id=data.get("client_id"), - target_id=data.get("target_id"), - ) - - if not target_client: - # No extension connected or specified client not found - await sender.send( - json.dumps( - { - "id": data.get("id"), - "error": { - "code": -32000, - "message": ( - "No browser extension connected" - if not self.extension_clients - else f"Client not found: {data.get('client_id') or data.get('target_id')}" - ), - }, - } - ) - ) - return - - # Update target client's last_active - target_client.last_active = time.time() - - # Forward to target extension and wait for response - request_id = data.get("id", self._next_request_id()) - data["id"] = request_id - - future: asyncio.Future = asyncio.get_event_loop().create_future() - self.pending_requests[request_id] = future - - try: - await target_client.websocket.send(json.dumps(data)) - - # Wait for response with timeout - response = await asyncio.wait_for(future, timeout=30.0) - # Include client_id in response - response["client_id"] = target_client.client_id - await sender.send(json.dumps(response)) - - except asyncio.TimeoutError: - self.pending_requests.pop(request_id, None) - await sender.send( - json.dumps( - { - "id": request_id, - "error": { - "code": -32001, - "message": f"Request timeout (client: {target_client.client_id})", - }, - } - ) - ) - except Exception as e: - self.pending_requests.pop(request_id, None) - await sender.send( - json.dumps( - {"id": request_id, "error": {"code": -32603, "message": str(e)}} - ) - ) - - def _next_request_id(self) -> int: - """Generate next request ID.""" - self.request_id += 1 - return self.request_id - - -class CDPBridgeClient: - """Client for connecting to CDP Bridge Server from hanzo-mcp.""" - - def __init__( - self, - host: str = "localhost", - port: int = 9223, - ): - self.host = host - self.port = port - self._websocket: Optional[WebSocketServerProtocol] = None - self._request_id = 0 - self._pending: dict[int, asyncio.Future] = {} - self._event_handlers: list[Callable] = [] - self._clients: list[dict] = [] - self._default_client_id: Optional[str] = None - - @property - def clients(self) -> list[dict]: - """List of connected browser extension clients.""" - return self._clients - - @property - def default_client_id(self) -> Optional[str]: - """ID of the default (most recently active) client.""" - return self._default_client_id - - async def connect(self) -> bool: - """Connect to the CDP bridge server.""" - if not WEBSOCKETS_AVAILABLE: - logger.warning("websockets not available, CDP bridge disabled") - return False - - try: - import websockets - - uri = f"ws://{self.host}:{self.port}/cdp" - self._websocket = await websockets.connect(uri) - - # Register as MCP client - await self._websocket.send( - json.dumps({"type": "register", "role": "mcp-client"}) - ) - - # Wait for status response - status_msg = await self._websocket.recv() - status = json.loads(status_msg) - if status.get("type") == "status": - self._clients = status.get("clients", []) - self._default_client_id = status.get("default_client_id") - - # Start message handler - asyncio.create_task(self._message_loop()) - - logger.info(f"Connected to CDP bridge at {uri}") - return True - - except Exception as e: - logger.warning(f"Failed to connect to CDP bridge: {e}") - return False - - async def disconnect(self) -> None: - """Disconnect from the bridge server.""" - if self._websocket: - await self._websocket.close() - self._websocket = None - - async def _message_loop(self) -> None: - """Process incoming messages.""" - if not self._websocket: - return - - try: - async for message in self._websocket: - data = json.loads(message) - - # Handle status updates - if data.get("type") == "provider_connected": - # New client connected - self._clients.append( - { - "client_id": data.get("client_id"), - "browser": data.get("browser"), - "profile": data.get("profile"), - "capabilities": data.get("capabilities"), - } - ) - elif data.get("type") == "provider_disconnected": - # Client disconnected - cid = data.get("client_id") - self._clients = [ - c for c in self._clients if c.get("client_id") != cid - ] - - # Handle responses - if "id" in data and data["id"] in self._pending: - future = self._pending.pop(data["id"]) - if "error" in data: - future.set_exception(Exception(data["error"]["message"])) - else: - future.set_result(data.get("result")) - - elif data.get("type") == "event": - for handler in self._event_handlers: - try: - handler(data) - except Exception: - pass - - except Exception as e: - logger.error(f"Message loop error: {e}") - - async def send( - self, - method: str, - params: dict = None, - client_id: str = None, - target_id: str = None, - ) -> Any: - """Send a CDP command and wait for response. - - Args: - method: CDP method name - params: Method parameters - client_id: Optional specific client to target - target_id: Optional namespaced target (client_id:tab_id) - - Returns: - Result from the extension - """ - if not self._websocket: - raise Exception("Not connected to CDP bridge") - - self._request_id += 1 - request_id = self._request_id - - future: asyncio.Future = asyncio.get_event_loop().create_future() - self._pending[request_id] = future - - payload = {"id": request_id, "method": method, "params": params or {}} - - # Add routing hints - if client_id: - payload["client_id"] = client_id - if target_id: - payload["target_id"] = target_id - - await self._websocket.send(json.dumps(payload)) - - return await asyncio.wait_for(future, timeout=30.0) - - def on_event(self, handler: Callable) -> None: - """Register an event handler.""" - self._event_handlers.append(handler) - - # High-level commands - - async def navigate( - self, - url: str, - tab_id: int = None, - client_id: str = None, - ) -> None: - """Navigate to a URL.""" - await self.send( - "Page.navigate", - {"url": url, "tabId": tab_id}, - client_id=client_id, - ) - - async def screenshot( - self, - tab_id: int = None, - full_page: bool = False, - format: str = "png", - client_id: str = None, - ) -> str: - """Take a screenshot, returns base64 data.""" - result = await self.send( - "hanzo.screenshot", - {"tabId": tab_id, "fullPage": full_page, "format": format}, - client_id=client_id, - ) - return result.get("data", "") - - async def click( - self, - selector: str, - tab_id: int = None, - client_id: str = None, - ) -> bool: - """Click an element by selector.""" - result = await self.send( - "hanzo.click", - {"selector": selector, "tabId": tab_id}, - client_id=client_id, - ) - return result.get("success", False) - - async def fill( - self, - selector: str, - value: str, - tab_id: int = None, - client_id: str = None, - ) -> bool: - """Fill an input element.""" - result = await self.send( - "hanzo.fill", - {"selector": selector, "value": value, "tabId": tab_id}, - client_id=client_id, - ) - return result.get("success", False) - - async def evaluate( - self, - expression: str, - tab_id: int = None, - client_id: str = None, - ) -> Any: - """Evaluate JavaScript in the page.""" - return await self.send( - "Runtime.evaluate", - {"expression": expression, "tabId": tab_id}, - client_id=client_id, - ) - - async def list_clients(self) -> list[dict]: - """Get list of connected browser extension clients.""" - # Refresh from server - result = await self.send("hanzo.listClients", {}) - return result.get("clients", self._clients) - - -async def main(): - """Run the CDP bridge server.""" - host = os.environ.get("HANZO_CDP_BRIDGE_HOST", "localhost") - port = int(os.environ.get("HANZO_CDP_BRIDGE_PORT", "9223")) - http_port = int(os.environ.get("HANZO_CDP_HTTP_PORT", "9224")) - - server = CDPBridgeServer(host=host, port=port, http_port=http_port) - await server.start() - - print(f"CDP Bridge Server running:") - print(f" WebSocket: ws://{host}:{port} (browser extensions connect here)") - print(f" HTTP API: http://{host}:{http_port} (hanzo-mcp sends commands here)") - print() - print("Waiting for browser extension(s) to connect...") - print("Supports multiple browsers simultaneously") - print("Press Ctrl+C to stop") - - try: - await asyncio.Future() # Run forever - except KeyboardInterrupt: - print("\nShutting down...") - await server.stop() - - -if __name__ == "__main__": - logging.basicConfig(level=logging.INFO) - asyncio.run(main()) diff --git a/pkg/hanzo-tools-browser/hanzo_tools/browser/cdp_tool.py b/pkg/hanzo-tools-browser/hanzo_tools/browser/cdp_tool.py deleted file mode 100644 index aec9c2c88..000000000 --- a/pkg/hanzo-tools-browser/hanzo_tools/browser/cdp_tool.py +++ /dev/null @@ -1,320 +0,0 @@ -"""Raw Chrome DevTools Protocol method dispatch. - -Decomplected from BrowserTool: `browser` is *action-oriented* (high-level -verbs like `navigate`, `click`); `cdp` is *method-oriented* (sends a CDP -method by name with raw params). Same backing transports (in-process ZAP -server โ†’ legacy HTTP bridge), no Playwright fallback โ€” for that, use -`browser` or `playwright`. - -Use this tool when you need a CDP method the high-level surface doesn't -expose, want to inspect raw protocol responses, or are wiring something -to the protocol directly. - -Example:: - - cdp(action="send", method="Page.navigate", params={"url": "https://example.com"}) - cdp(action="send", method="Runtime.evaluate", params={"expression": "1+1"}) - cdp(action="tabs") # list connected tabs (Target.getTargets) - cdp(action="status") # connection status - cdp(action="list_browsers") # which providers (firefox/chrome/safari) are connected -""" - -from __future__ import annotations - -import json -import logging -import os -from typing import Any, Annotated, Literal, Optional, Union - -from pydantic import Field -from mcp.server import FastMCP - -from hanzo_tools.core import BaseTool - -logger = logging.getLogger(__name__) - - -CdpAction = Annotated[ - Literal["send", "tabs", "status", "list_browsers", "claim_browser", "release_browser"], - Field(description="CDP action"), -] - - -async def _dispatch_raw( - method: str, - params: Optional[dict] = None, - *, - browser: Optional[str] = None, - tab_id: Optional[Union[str, int]] = None, - client_id: Optional[str] = None, - timeout: float = 30.0, -) -> dict: - """Dispatch a raw CDP method to the connected browser provider. - - Path order โ€” same as BrowserTool: - 1. In-process ZAP server (microsecond round-trip; preferred). - 2. Legacy HTTP bridge on :9224 (kept as fallback for non-ZAP clients). - - Pin transport via ``BROWSER_TRANSPORT=zap|http|auto`` (default ``auto``). - """ - params = dict(params or {}) - - # Normalize tab id (accept "tab-123" or 123 or "123") - if tab_id is not None: - t = tab_id - if isinstance(t, str) and t.startswith("tab-"): - t = t[4:] - try: - t = int(t) - except (TypeError, ValueError): - pass - params.setdefault("tabId", t) - - transport = os.environ.get("BROWSER_TRANSPORT", "auto").strip().lower() - if transport not in {"zap", "http", "auto"}: - transport = "auto" - - # 1) ZAP path - if transport in {"zap", "auto"}: - try: - from hanzo_tools.browser.zap_server import get_server - - srv = get_server() - if srv is not None and srv.has_client(browser=browser): - try: - raw = await srv.send( - method, params, browser=browser, client_id=client_id - ) - return {"success": True, "transport": "zap", "method": method, "result": raw} - except Exception as e: - if transport == "zap": - return {"error": str(e), "transport": "zap", "method": method} - logger.debug("zap dispatch failed, falling back to http: %s", e) - except ImportError: - pass - - if transport == "zap": - return { - "error": "ZAP transport selected but no extension client matched", - "transport": "zap", - "method": method, - } - - # 2) HTTP fallback โ€” legacy CDP bridge speaks raw CDP via a `cdp` action - try: - import aiohttp - - payload: dict[str, Any] = {"action": "cdp", "method": method, "params": params} - if browser: - payload["browser"] = browser - if client_id: - payload["clientId"] = client_id - - async with aiohttp.ClientSession() as session: - async with session.post( - "http://localhost:9224", - json=payload, - timeout=aiohttp.ClientTimeout(total=timeout), - ) as resp: - body = await resp.text() - try: - import json - - parsed = json.loads(body) - except Exception: - parsed = {"raw": body} - parsed.setdefault("transport", "http") - parsed.setdefault("method", method) - if resp.status == 200: - parsed.setdefault("success", True) - else: - parsed.setdefault("status", resp.status) - parsed.setdefault("error", parsed.get("error") or body[:200]) - return parsed - except Exception as e: - logger.debug("CDP dispatch HTTP fallback failed: %s", e) - return {"error": str(e), "transport": "http", "method": method} - - -class CdpTool(BaseTool): - """Raw Chrome DevTools Protocol method dispatch. - - Peer of ``browser`` (action-oriented) and ``playwright`` (Playwright API). - Sends any CDP method directly to a connected browser via the ZAP server - (extension) or legacy CDP HTTP bridge. Does NOT fall back to Playwright. - """ - - name = "cdp" - - @property - def description(self) -> str: - return """Raw Chrome DevTools Protocol dispatch โ€” peer of `browser`. - -ACTIONS: -- send : send a CDP method (method=, params=, tab_id=, target_browser=) -- tabs : Target.getTargets โ€” list connected tabs -- status : Browser.getVersion โ€” connection + version -- list_browsers : list extension providers (firefox/chrome/safari/edge) connected -- claim_browser / release_browser : exclusive-lease management - -EXAMPLES: -- cdp(action="send", method="Page.navigate", params={"url": "https://example.com"}) -- cdp(action="send", method="Runtime.evaluate", params={"expression": "document.title"}) -- cdp(action="tabs") -- cdp(action="status") - -Use `browser` for high-level verbs (navigate, click, screenshot). -Use `playwright` for headless Playwright automation. -""" - - async def call(self, ctx, action: str = "send", **kwargs) -> dict[str, Any]: - return await self.execute(action=action, **kwargs) - - def register(self, mcp_server: FastMCP) -> None: - """Register the cdp tool with an MCP server.""" - tool_instance = self - - @mcp_server.tool(name=self.name, description=self.description) - async def cdp( - action: CdpAction = "send", - method: Annotated[ - Optional[str], - Field(description="CDP method name (e.g. 'Page.navigate', 'Runtime.evaluate')"), - ] = None, - params: Annotated[ - Optional[dict], - Field(description="CDP method params"), - ] = None, - tab_id: Annotated[ - Optional[Union[str, int]], - Field(description="Target tab id (string or int)"), - ] = None, - target_browser: Annotated[ - Optional[str], - Field(description="Provider filter: firefox|chrome|safari|edge"), - ] = None, - client_id: Annotated[ - Optional[str], - Field(description="Specific extension client id"), - ] = None, - timeout: Annotated[ - Optional[float], - Field(description="Per-call timeout (seconds)"), - ] = None, - ) -> str: - result = await tool_instance.execute( - action=action, - method=method, - params=params, - tab_id=tab_id, - target_browser=target_browser, - client_id=client_id, - timeout=timeout, - ) - return json.dumps(result, indent=2, default=str) - - async def execute( - self, - action: str = "send", - # Raw CDP - method: Optional[str] = None, - params: Optional[dict] = None, - # Routing - tab_id: Optional[Union[str, int]] = None, - target_browser: Optional[str] = None, - client_id: Optional[str] = None, - # Timeout - timeout: Optional[float] = None, - ) -> dict[str, Any]: - t = float(timeout) if timeout else 30.0 - - # === Local actions (handled in-process) ===================== - if action == "list_browsers": - try: - from hanzo_tools.browser.zap_server import get_server - - srv = get_server() - if srv is None: - return {"error": "zap server not running"} - clients = [] - for c in srv.clients: - clients.append( - { - "client_id": c.client_id, - "browser": getattr(c, "browser", None), - "label": getattr(c, "label", None), - } - ) - return {"success": True, "browsers": clients, "count": len(clients)} - except Exception as e: - return {"error": str(e)} - - if action == "claim_browser": - try: - from hanzo_tools.browser.zap_server import DEFAULT_LEASE_TTL, get_server - - srv = get_server() - if srv is None: - return {"error": "zap server not running"} - client = srv.resolve_client(client_id=client_id, browser=target_browser) - if client is None: - return {"error": "no matching extension client"} - lease = srv.claim(client.client_id, ttl=t) - return { - "success": True, - "client_id": lease.client_id, - "holder": lease.holder, - "expires_at": lease.expires_at, - } - except Exception as e: - return {"error": str(e)} - - if action == "release_browser": - try: - from hanzo_tools.browser.zap_server import get_server - - srv = get_server() - if srv is None: - return {"error": "zap server not running"} - if client_id: - return {"success": srv.release(client_id), "client_id": client_id} - released = [c.client_id for c in list(srv.clients) if srv.release(c.client_id)] - return {"success": True, "released": released} - except Exception as e: - return {"error": str(e)} - - # === Sugared CDP methods ================================== - sugared = { - "tabs": ("Target.getTargets", {}), - "status": ("Browser.getVersion", {}), - } - if action in sugared: - m, p = sugared[action] - return await _dispatch_raw( - m, - p, - browser=target_browser, - tab_id=tab_id, - client_id=client_id, - timeout=t, - ) - - # === Raw send ============================================== - if action == "send": - if not method: - return { - "error": "method required for action=send (e.g. 'Page.navigate')", - "action": "send", - } - return await _dispatch_raw( - method, - params, - browser=target_browser, - tab_id=tab_id, - client_id=client_id, - timeout=t, - ) - - return { - "error": f"unknown action '{action}'. Try: send, tabs, status, list_browsers, claim_browser, release_browser", - } diff --git a/pkg/hanzo-tools-browser/hanzo_tools/browser/lifecycle.py b/pkg/hanzo-tools-browser/hanzo_tools/browser/lifecycle.py deleted file mode 100644 index 682b96ba2..000000000 --- a/pkg/hanzo-tools-browser/hanzo_tools/browser/lifecycle.py +++ /dev/null @@ -1,210 +0,0 @@ -"""Lifecycle helpers for the browser package: ZAP server + legacy CDP bridge. - -Decomplected out of ``__init__.py`` so the package's namespace is a pure -re-export surface. The MCP server (or any host) imports ``_ensure_zap_server`` -and ``start_cdp_bridge`` from here when it wants the long-lived background -threads bound. Tools themselves never touch lifecycle directly. - -Two background lifecycles, isolated: - - * ZAP server โ€” canonical. One MCP = one ZAP server bound to the lowest - free port from 9999..9995. Browser extension discovers - it via mDNS. Lifetime = MCP lifetime. - - * CDP bridge โ€” legacy HTTP fallback on :9223/:9224 for non-ZAP clients. - Opt-in: ``HANZO_CDP_BRIDGE_ENABLED=1``. -""" - -from __future__ import annotations - -import asyncio -import logging -import os -import threading -from typing import TYPE_CHECKING, Optional - -if TYPE_CHECKING: - from hanzo_tools.browser.cdp_bridge_server import CDPBridgeServer - -logger = logging.getLogger(__name__) - -# CDP bridge availability check -try: - from hanzo_tools.browser.cdp_bridge_server import ( - WEBSOCKETS_AVAILABLE as CDP_BRIDGE_AVAILABLE, - CDPBridgeServer, - ) -except ImportError: # pragma: no cover - CDP_BRIDGE_AVAILABLE = False - CDPBridgeServer = None # type: ignore[assignment] - -# === Global state (one of each per process) ============================ - -_zap_thread: Optional[threading.Thread] = None -_zap_loop: Optional[asyncio.AbstractEventLoop] = None -_zap_started_event: Optional[threading.Event] = None - -_cdp_bridge_server: Optional["CDPBridgeServer"] = None -_cdp_bridge_thread: Optional[threading.Thread] = None -_cdp_bridge_loop: Optional[asyncio.AbstractEventLoop] = None - - -# === ZAP (canonical) ==================================================== - - -def _run_zap_server(host: str) -> None: - """Run the ZAP server in a dedicated background thread.""" - global _zap_loop, _zap_started_event - - from hanzo_tools.browser.zap_server import get_or_start_server - - _zap_loop = asyncio.new_event_loop() - asyncio.set_event_loop(_zap_loop) - - async def _bootstrap() -> None: - srv = await get_or_start_server( - host=host, - agent_label=os.environ.get("HANZO_AGENT_LABEL"), - ) - if _zap_started_event is not None: - _zap_started_event.set() - if srv is None: - return - while True: - await asyncio.sleep(3600) - - try: - _zap_loop.run_until_complete(_bootstrap()) - except Exception as e: - logger.error("ZAP server thread crashed: %s", e, exc_info=True) - - -def ensure_zap_server() -> bool: - """Start the in-process ZAP server if not already running. - - Returns True if the server is alive after this call. - Idempotent โ€” safe to call multiple times. - """ - global _zap_thread, _zap_started_event - - if _zap_thread is not None and _zap_thread.is_alive(): - from hanzo_tools.browser.zap_server import get_server - - return get_server() is not None - - if os.environ.get("HANZO_ZAP_DISABLED", "").lower() in ("1", "true", "yes"): - return False - - host = os.environ.get("HANZO_ZAP_HOST", "127.0.0.1") - - _zap_started_event = threading.Event() - _zap_thread = threading.Thread( - target=_run_zap_server, - args=(host,), - daemon=True, - name="hanzo-zap-server", - ) - _zap_thread.start() - _zap_started_event.wait(timeout=2.0) - - from hanzo_tools.browser.zap_server import get_server - - return get_server() is not None - - -def stop_zap_server() -> None: - """Stop the in-process ZAP server (best-effort, non-blocking).""" - global _zap_thread, _zap_loop, _zap_started_event - - if _zap_loop is not None: - try: - from hanzo_tools.browser.zap_server import shutdown_server - - asyncio.run_coroutine_threadsafe(shutdown_server(), _zap_loop) - except Exception: - pass - _zap_loop = None - _zap_thread = None - _zap_started_event = None - - -# === CDP bridge (legacy) =============================================== - - -def _run_cdp_bridge_server(host: str, port: int) -> None: - """Run CDP bridge server in a background thread.""" - global _cdp_bridge_server, _cdp_bridge_loop - - _cdp_bridge_loop = asyncio.new_event_loop() - asyncio.set_event_loop(_cdp_bridge_loop) - - _cdp_bridge_server = CDPBridgeServer(host=host, port=port) # type: ignore[misc] - - async def run() -> None: - await _cdp_bridge_server.start() # type: ignore[union-attr] - while True: - await asyncio.sleep(1) - - try: - _cdp_bridge_loop.run_until_complete(run()) - except Exception as e: - logger.error("CDP bridge server crashed: %s", e, exc_info=True) - - -def start_cdp_bridge(host: str = "localhost", port: int = 9223) -> bool: - """Start the legacy CDP bridge server (opt-in fallback transport). - - Enables HTTP communication between hanzo-mcp's tools (port 9224) and - the Hanzo browser extension (WebSocket on `port`, default 9223). - Set ``HANZO_CDP_BRIDGE_DISABLED=1`` to refuse to start. - """ - global _cdp_bridge_thread - - if os.environ.get("HANZO_CDP_BRIDGE_DISABLED", "").lower() in ("1", "true", "yes"): - return False - if not CDP_BRIDGE_AVAILABLE: - return False - if _cdp_bridge_thread is not None and _cdp_bridge_thread.is_alive(): - return True - - host = os.environ.get("HANZO_CDP_BRIDGE_HOST", host) - port = int(os.environ.get("HANZO_CDP_BRIDGE_PORT", str(port))) - - try: - _cdp_bridge_thread = threading.Thread( - target=_run_cdp_bridge_server, - args=(host, port), - daemon=True, - name="cdp-bridge-server", - ) - _cdp_bridge_thread.start() - logger.info("CDP bridge started on ws://%s:%d", host, port) - return True - except Exception as e: - logger.warning("Failed to start CDP bridge: %s", e) - return False - - -def stop_cdp_bridge() -> None: - """Stop the CDP bridge server.""" - global _cdp_bridge_server, _cdp_bridge_thread, _cdp_bridge_loop - - if _cdp_bridge_loop is not None and _cdp_bridge_server is not None: - try: - asyncio.run_coroutine_threadsafe( - _cdp_bridge_server.stop(), _cdp_bridge_loop - ) - except Exception: - pass - _cdp_bridge_server = None - _cdp_bridge_thread = None - _cdp_bridge_loop = None - - -__all__ = [ - "CDP_BRIDGE_AVAILABLE", - "ensure_zap_server", - "stop_zap_server", - "start_cdp_bridge", - "stop_cdp_bridge", -] diff --git a/pkg/hanzo-tools-browser/hanzo_tools/browser/playwright_tool.py b/pkg/hanzo-tools-browser/hanzo_tools/browser/playwright_tool.py deleted file mode 100644 index d107df4a4..000000000 --- a/pkg/hanzo-tools-browser/hanzo_tools/browser/playwright_tool.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Playwright-pinned browser tool. - -Same action surface as ``BrowserTool`` but with ``backend="playwright"`` -forced โ€” never dispatches through the extension or CDP bridge. Use this -when you want deterministic headless automation regardless of whether a -browser extension is connected to this MCP. - -The high-level ``browser`` tool auto-detects transport. Calling -``playwright`` is the explicit "I want Playwright, period" path. -""" - -from __future__ import annotations - -import logging -from typing import Optional - -from hanzo_tools.browser.browser_tool import BrowserTool - -logger = logging.getLogger(__name__) - - -class PlaywrightTool(BrowserTool): - """Browser automation pinned to the Playwright backend. - - Skips the in-process ZAP server (extension dispatch) and the legacy - CDP HTTP bridge entirely โ€” every action goes through Playwright's - Chromium driver. Suitable for headless test runs, CI pipelines, and - any context where attaching to a user-controlled browser is wrong. - - The action surface (navigate, click, fill, screenshot, expect_*, ...) - is identical to ``BrowserTool`` โ€” only the transport pin differs. - """ - - name = "playwright" - - def __init__( - self, - headless: bool = True, - cdp_endpoint: Optional[str] = None, - ): - # Force backend="playwright" โ€” overrides BROWSER_BACKEND env, ignores - # extension config, skips ZAP/CDP-bridge lifecycle bootstrap. - super().__init__( - headless=headless, - cdp_endpoint=cdp_endpoint, - backend="playwright", - ) - - @property - def description(self) -> str: - return """Playwright-pinned browser automation โ€” peer of `browser` and `cdp`. - -Same action surface as `browser` but forces backend="playwright": -- No extension dispatch (does not talk to Hanzo browser extension). -- No legacy CDP HTTP bridge. -- Pure async-playwright, headless by default. - -Use `browser` for auto-routing (extension > CDP-bridge > Playwright). -Use `cdp` for raw Chrome DevTools Protocol method dispatch. -Use `playwright` for deterministic headless automation. -""" + BrowserTool.description.fget(self).split("CATEGORIES:", 1)[-1].rstrip() diff --git a/pkg/hanzo-tools-browser/hanzo_tools/browser/zap_server.py b/pkg/hanzo-tools-browser/hanzo_tools/browser/zap_server.py deleted file mode 100644 index 7b39cd102..000000000 --- a/pkg/hanzo-tools-browser/hanzo_tools/browser/zap_server.py +++ /dev/null @@ -1,621 +0,0 @@ -"""ZAP (Zero-latency Agent Protocol) server for hanzo-tools-browser. - -Wire format and constants come from the canonical ``zap-protocol`` package -so every implementation in the stack stays byte-identical. - -Discovery is mDNS-only per HIP-0069: the server binds an OS-assigned -ephemeral port and advertises it under ``_hanzo._tcp.local.`` via -``zap-mdns``. There is no well-known port pool, no lockfile arbitration, -and no shared config registry โ€” clients (browser extensions, sibling -MCPs, agents) browse mDNS to find every live ZAP service on the LAN. - -One ZapServer per hanzo-mcp process. Lifetime = MCP lifetime. -""" - -from __future__ import annotations - -import asyncio -import logging -import os -import time -import uuid -from dataclasses import dataclass, field -from typing import Any, Awaitable, Callable, Optional - -logger = logging.getLogger(__name__) - -# Wire format โ€” vendored here. Mirrors the TypeScript reference at -# extension/packages/browser/src/shared/zap.ts. Kept self-contained so a -# vanilla `pip install hanzo-tools-browser` works without external state -# (an earlier `zap-protocol` PyPI package was a `zap-schema` stub that -# doesn't expose `zap.protocol`; rather than chase that, the wire spec is -# small enough to inline). If you change anything here, also update the -# TS reference and `extension/packages/mcp/src/zap-server.ts`. -import json as _json -import struct as _struct -from typing import Tuple as _Tuple - -ZAP_MAGIC = b"\x5a\x41\x50\x01" -HEADER_SIZE = 9 # 4 magic + 1 type + 4 length BE -MAX_MESSAGE_SIZE = 16 * 1024 * 1024 # 16 MiB - -MSG_HANDSHAKE = 0x01 -MSG_HANDSHAKE_OK = 0x02 -MSG_REQUEST = 0x10 -MSG_RESPONSE = 0x11 -MSG_PING = 0xFE -MSG_PONG = 0xFF - - -def encode(msg_type: int, payload) -> bytes: - body = _json.dumps(payload, separators=(",", ":"), ensure_ascii=False).encode("utf-8") - if len(body) > MAX_MESSAGE_SIZE: - raise ValueError(f"ZAP payload exceeds MAX_MESSAGE_SIZE ({len(body)} > {MAX_MESSAGE_SIZE})") - return ZAP_MAGIC + _struct.pack("!BL", msg_type & 0xFF, len(body)) + body - - -def decode(frame: bytes): - if len(frame) < HEADER_SIZE or frame[:4] != ZAP_MAGIC: - return None - msg_type = frame[4] - (length,) = _struct.unpack("!L", frame[5:9]) - if length > MAX_MESSAGE_SIZE or len(frame) < HEADER_SIZE + length: - return None - try: - payload = _json.loads(frame[HEADER_SIZE : HEADER_SIZE + length].decode("utf-8")) - except (_json.JSONDecodeError, UnicodeDecodeError): - return None - return msg_type, payload - -# Browser-resolution preference (mirror cdp-bridge-server.ts) -DEFAULT_BROWSER_PREFERENCE: list[str] = ["firefox", "safari", "edge", "chrome"] - -# How long an exclusive browser lease lasts unless explicitly extended. -DEFAULT_LEASE_TTL = 60.0 - - -# --------------------------------------------------------------------------- -# Client tracking -# --------------------------------------------------------------------------- - - -@dataclass -class ZapClient: - """A connected browser extension.""" - - client_id: str - browser: str - version: str - capabilities: list[str] - ws: Any # websockets.WebSocketServerProtocol โ€” typed Any to avoid hard dep - connected_at: float = field(default_factory=time.time) - last_active: float = field(default_factory=time.time) - - def to_dict(self) -> dict: - return { - "client_id": self.client_id, - "browser": self.browser, - "version": self.version, - "capabilities": self.capabilities, - "connected_at": self.connected_at, - "last_active": self.last_active, - } - - -@dataclass -class BrowserLease: - """An exclusive lease on a browser client (sub-agent claim/release).""" - - client_id: str - holder: str - expires_at: float - - @property - def expired(self) -> bool: - return time.time() >= self.expires_at - - -# --------------------------------------------------------------------------- -# Server -# --------------------------------------------------------------------------- - - -class ZapServer: - """Single-port ZAP server hosted inside a hanzo-mcp process. - - One instance per Python MCP. Lifetime = MCP lifetime. Concurrent - extensions register and dispatch independently. - """ - - def __init__( - self, - host: str = "127.0.0.1", - agent_label: Optional[str] = None, - server_id: Optional[str] = None, - request_timeout: float = 30.0, - ): - self.host = host - self.agent_label = agent_label or os.environ.get("HANZO_AGENT_LABEL", "") - # server_id is `mcp-py--<4hex>`. PID alone collides across hosts on - # the same LAN; the random suffix makes the name globally unique so - # zeroconf never has to auto-rename to "(2)". - self.server_id = server_id or f"mcp-py-{os.getpid()}-{uuid.uuid4().hex[:4]}" - self.request_timeout = request_timeout - - self._port: Optional[int] = None - self._server: Any = None # websockets server - self._mdns_handle: Any = None - self._clients: dict[str, ZapClient] = {} - self._ws_to_id: dict[Any, str] = {} - self._pending: dict[str, asyncio.Future] = {} - self._req_counter = 0 - self._leases: dict[str, BrowserLease] = {} # client_id -> lease - self._tools_manifest: list[dict] = [ - { - "name": "browser", - "description": "Hanzo browser tool (Python MCP, ZAP-native)", - "inputSchema": {"type": "object"}, - } - ] - # Inbound RPC handler โ€” set by hanzo-mcp at startup to expose its - # full tool surface to extensions over the same socket. Without it, - # incoming MSG_REQUEST calls get a noop ack (legacy behaviour). - self._request_handler: Optional[Callable[[str, dict], Awaitable[Any]]] = None - - # ---- lifecycle ------------------------------------------------------ - - async def start(self) -> Optional[int]: - """Bind to an OS-assigned port and advertise it via mDNS. - - Discovery is mDNS-only (HIP-0069); the OS picks the port, mDNS - carries it. No well-known port pool, no lockfile arbitration โ€” - every MCP gets its own ephemeral port and is found by browsing - ``_hanzo._tcp.local.``. - - Returns the bound port, or ``None`` if either ``websockets`` or - ``zap-mdns`` is missing. - """ - try: - import websockets # noqa: F401 - except ImportError: - logger.warning("websockets not installed; ZAP server disabled") - return None - try: - import zap_mdns - except ImportError: - logger.error( - "zap-mdns not installed; the server is unreachable without it. " - "`pip install zap-mdns`." - ) - return None - - from websockets.asyncio.server import serve as _serve - - async def _handler(websocket): - await self._handle_connection(websocket) - - # Bind to port 0 โ†’ OS picks an ephemeral port. The actual port - # comes back via the server's sockets attribute. - self._server = await _serve(_handler, self.host, 0) - sockets = getattr(self._server, "sockets", None) or [] - if not sockets: - logger.error("zap: server has no sockets after start()") - return None - self._port = sockets[0].getsockname()[1] - logger.info( - "ZAP server listening on ws://%s:%d (mcp=%s, agent=%s)", - self.host, - self._port, - self.server_id, - self.agent_label or "?", - ) - - # mDNS publish โ€” the only way clients find this server. - # zeroconf.register_service blocks briefly setting up multicast, - # so run it on a worker thread to avoid asyncio EventLoopBlocked. - try: - self._mdns_handle = await asyncio.to_thread( - zap_mdns.publish, - port=self._port, - server_id=self.server_id, - agent_label=self.agent_label or "", - version="zap/1", - capabilities=["mcp", "browser-bridge"], - # Advertise the bind address so the URL clients receive - # actually reaches this server. zap-mdns defaults to the - # outbound LAN IP via _local_ip(), which is wrong when we - # bind loopback (browser extension dials LAN IP โ†’ ECONNREFUSED). - host=self.host, - ) - logger.info("mDNS published %s on :%d", zap_mdns.SERVICE_TYPE, self._port) - except Exception as e: - logger.warning("mDNS publish failed: %s: %s", type(e).__name__, e) - self._mdns_handle = None - - return self._port - - async def stop(self) -> None: - """Gracefully shut down: retract mDNS, close clients, drop sockets.""" - # Retract mDNS announcement first so consumers see us go away. - if self._mdns_handle is not None: - try: - self._mdns_handle.close() - except Exception: - pass - self._mdns_handle = None - - if self._server is not None: - self._server.close() - try: - await self._server.wait_closed() - except Exception: - pass - self._server = None - - for client in list(self._clients.values()): - try: - await client.ws.close() - except Exception: - pass - self._clients.clear() - self._ws_to_id.clear() - self._port = None - - # ---- public API ----------------------------------------------------- - - def set_tools(self, tools: list[dict]) -> None: - """Replace the advertised tool manifest (sent to clients on handshake).""" - self._tools_manifest = list(tools) - - def set_request_handler( - self, handler: Optional[Callable[[str, dict], Awaitable[Any]]] - ) -> None: - """Register / replace the inbound RPC handler. Receives (method, params), - returns a JSON-serialisable result. Used by hanzo-mcp to expose its - full tool surface to extensions over the same socket.""" - self._request_handler = handler - - @property - def port(self) -> Optional[int]: - return self._port - - @property - def clients(self) -> list[ZapClient]: - return list(self._clients.values()) - - def has_client(self, browser: Optional[str] = None) -> bool: - if not self._clients: - return False - if not browser: - return True - b = browser.lower() - return any(b in c.browser.lower() for c in self._clients.values()) - - def resolve_client( - self, - client_id: Optional[str] = None, - browser: Optional[str] = None, - ) -> Optional[ZapClient]: - """Pick which connected extension to dispatch to. - - Priority: explicit client_id > browser preference > most-recent-active - > default browser preference list. - """ - if client_id and client_id in self._clients: - return self._clients[client_id] - - candidates = list(self._clients.values()) - if not candidates: - return None - - if browser: - b = browser.lower() - matches = [c for c in candidates if b in c.browser.lower()] - if not matches: - return None - return max(matches, key=lambda c: c.last_active) - - # No explicit selector: respect global default preference list. - for pref in DEFAULT_BROWSER_PREFERENCE: - matches = [c for c in candidates if pref in c.browser.lower()] - if matches: - return max(matches, key=lambda c: c.last_active) - - return max(candidates, key=lambda c: c.last_active) - - async def send( - self, - method: str, - params: Optional[dict] = None, - *, - browser: Optional[str] = None, - client_id: Optional[str] = None, - timeout: Optional[float] = None, - ) -> Any: - """Send a method request to a connected extension and await result. - - Raises ``RuntimeError`` if no client matches. - """ - client = self.resolve_client(client_id=client_id, browser=browser) - if client is None: - raise RuntimeError( - f"No ZAP-connected browser extension" - + (f" matching '{browser}'" if browser else "") - ) - - # Honour leases: if a different holder has a non-expired lease on this - # client, reject. - lease = self._leases.get(client.client_id) - if lease and not lease.expired and lease.holder != self.server_id: - raise RuntimeError( - f"browser leased by {lease.holder} until {time.ctime(lease.expires_at)}" - ) - - req_id = self._next_req_id() - future: asyncio.Future = asyncio.get_event_loop().create_future() - self._pending[req_id] = future - client.last_active = time.time() - - try: - await client.ws.send( - encode( - MSG_REQUEST, - {"id": req_id, "method": method, "params": params or {}}, - ) - ) - return await asyncio.wait_for( - future, timeout=timeout or self.request_timeout - ) - finally: - self._pending.pop(req_id, None) - - # ---- leases --------------------------------------------------------- - - def claim(self, client_id: str, ttl: float = DEFAULT_LEASE_TTL) -> BrowserLease: - """Take an exclusive lease on a browser client for ``ttl`` seconds. - - Raises ``RuntimeError`` if already held by someone else. - """ - lease = self._leases.get(client_id) - if lease and not lease.expired and lease.holder != self.server_id: - raise RuntimeError( - f"already leased by {lease.holder} until {time.ctime(lease.expires_at)}" - ) - new = BrowserLease( - client_id=client_id, - holder=self.server_id, - expires_at=time.time() + ttl, - ) - self._leases[client_id] = new - return new - - def release(self, client_id: str) -> bool: - """Release a lease this server holds. Returns True if released.""" - lease = self._leases.get(client_id) - if lease and lease.holder == self.server_id: - del self._leases[client_id] - return True - return False - - # ---- cluster discovery --------------------------------------------- - # Cross-MCP visibility comes from mDNS, not a shared file. Use - # ``zap_mdns.browse()`` to enumerate live MCPs on the LAN. - - @staticmethod - def list_mcp_instances(timeout: float = 1.5) -> list[dict]: - """Browse ``_hanzo._tcp.local.`` for every live ZAP service.""" - try: - import zap_mdns - except ImportError: - return [] - return [ - { - "server_id": s.server_id, - "host": s.host, - "port": s.port, - "url": s.url, - "agent_label": s.agent_label, - "version": s.version, - "capabilities": list(s.capabilities or []), - } - for s in zap_mdns.browse(timeout=timeout) - ] - - # ---- ws handler ----------------------------------------------------- - - async def _handle_connection(self, websocket: Any) -> None: - try: - async for raw in websocket: - if not isinstance(raw, (bytes, bytearray)): - # Spec is binary frames; ignore stray text. - continue - decoded = decode(bytes(raw)) - if decoded is None: - logger.debug("zap: malformed frame from %s", websocket) - continue - msg_type, payload = decoded - await self._dispatch(websocket, msg_type, payload or {}) - except Exception as e: - # websockets normalises connection-closed via exception flow; - # don't spam logs. - logger.debug("zap connection ended: %s", e) - finally: - cid = self._ws_to_id.pop(websocket, None) - if cid: - existing = self._clients.get(cid) - if existing is not None and existing.ws is websocket: - self._clients.pop(cid, None) - self._leases.pop(cid, None) - logger.info("zap: client disconnected %s", cid) - else: - logger.debug( - "zap: stale ws %s for client %s โ€” newer connection holds the slot", - websocket, - cid, - ) - - async def _dispatch(self, websocket: Any, msg_type: int, payload: dict) -> None: - if msg_type == MSG_HANDSHAKE: - client_id = payload.get("clientId") or f"ext-{int(time.time() * 1000)}" - client = ZapClient( - client_id=client_id, - browser=payload.get("browser", "unknown"), - version=payload.get("version", "0"), - capabilities=list(payload.get("capabilities") or []), - ws=websocket, - ) - self._clients[client_id] = client - self._ws_to_id[websocket] = client_id - logger.info( - "zap: client connected %s (%s v%s, %d caps)", - client_id, - client.browser, - client.version, - len(client.capabilities), - ) - await websocket.send( - encode( - MSG_HANDSHAKE_OK, - { - "serverId": self.server_id, - "name": "hanzo-mcp", - "agentLabel": self.agent_label, - "tools": self._tools_manifest, - }, - ) - ) - return - - if msg_type == MSG_PING: - await websocket.send(encode(MSG_PONG, {})) - return - - if msg_type == MSG_PONG: - return - - # MSG_RESPONSE: extension is answering an RPC we sent. - if msg_type == MSG_RESPONSE: - req_id = payload.get("id") - future = self._pending.get(req_id) if req_id else None - if future is None or future.done(): - return - if "error" in payload and payload["error"]: - err = payload["error"] - msg = err.get("message") if isinstance(err, dict) else str(err) - future.set_exception(RuntimeError(msg or "ZAP error")) - else: - future.set_result(payload.get("result")) - return - - # MSG_REQUEST: extension is calling US โ€” most commonly because the - # extension wants to invoke an MCP tool exposed by this server. Route - # via the registered request_handler (set by hanzo-mcp at startup). - # Without a handler, fall back to noop ack (legacy notifications). - if msg_type == MSG_REQUEST: - req_id = payload.get("id") - method = payload.get("method", "") - params = payload.get("params") or {} - cid = self._ws_to_id.get(websocket) - if cid and cid in self._clients: - self._clients[cid].last_active = time.time() - if req_id is None: - # Notification โ€” no response expected. - return - handler = self._request_handler - if handler is None: - await websocket.send( - encode( - MSG_RESPONSE, - {"id": req_id, "result": {"ack": True, "method": method}}, - ) - ) - return - try: - result = await handler(method, params) - await websocket.send( - encode(MSG_RESPONSE, {"id": req_id, "result": result}) - ) - except Exception as e: - await websocket.send( - encode( - MSG_RESPONSE, - { - "id": req_id, - "error": { - "code": -1, - "message": f"{type(e).__name__}: {e}", - }, - }, - ) - ) - return - - logger.debug("zap: unknown msg type 0x%02x", msg_type) - - def _next_req_id(self) -> str: - self._req_counter += 1 - return f"py-{self._req_counter}" - - -# --------------------------------------------------------------------------- -# Process-wide singleton (one ZAP server per hanzo-mcp) -# --------------------------------------------------------------------------- - - -_singleton: Optional[ZapServer] = None -_singleton_lock = asyncio.Lock() - - -async def get_or_start_server( - *, - host: str = "127.0.0.1", - agent_label: Optional[str] = None, -) -> Optional[ZapServer]: - """Return the process-wide ZAP server, starting it if needed. - - Returns ``None`` if either ``websockets`` or ``zap-mdns`` is missing. - """ - global _singleton - async with _singleton_lock: - if _singleton is not None and _singleton.port is not None: - return _singleton - srv = ZapServer(host=host, agent_label=agent_label) - port = await srv.start() - if port is None: - return None - _singleton = srv - return srv - - -def get_server() -> Optional[ZapServer]: - """Return the current singleton (or None if not started).""" - return _singleton - - -async def shutdown_server() -> None: - global _singleton - async with _singleton_lock: - if _singleton is not None: - await _singleton.stop() - _singleton = None - - -__all__ = [ - "ZapClient", - "ZapServer", - "BrowserLease", - "DEFAULT_BROWSER_PREFERENCE", - "DEFAULT_LEASE_TTL", - "MSG_HANDSHAKE", - "MSG_HANDSHAKE_OK", - "MSG_REQUEST", - "MSG_RESPONSE", - "MSG_PING", - "MSG_PONG", - "ZAP_MAGIC", - "encode", - "decode", - "get_or_start_server", - "get_server", - "shutdown_server", -] diff --git a/pkg/hanzo-tools-browser/pyproject.toml b/pkg/hanzo-tools-browser/pyproject.toml deleted file mode 100644 index a90873e6b..000000000 --- a/pkg/hanzo-tools-browser/pyproject.toml +++ /dev/null @@ -1,36 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-browser" -version = "0.5.7" -description = "Browser automation tools with ZAP-native extension routing โ€” Python MCP hosts the ZAP server on an OS-assigned port and advertises via mDNS (HIP-0069). No node bridge, no port pool, no lockfile." -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "tools", "browser", "playwright", "mcp", "ai", "automation", "extension"] -dependencies = [ - "hanzo-tools>=0.3.0", - "mcp>=1.25.0", - "pydantic>=2.12.5", - "aiohttp>=3.9.0", - "websockets>=12.0", - "zap-mdns>=0.1.0", -] - -[project.optional-dependencies] -playwright = [ - "playwright>=1.49.0", -] - -[project.entry-points."hanzo.tools"] -browser = "hanzo_tools.browser:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] - -[tool.setuptools.package-data] -"*" = ["py.typed"] diff --git a/pkg/hanzo-tools-browser/tests/test_browser_tools.py b/pkg/hanzo-tools-browser/tests/test_browser_tools.py deleted file mode 100644 index 0e5baae87..000000000 --- a/pkg/hanzo-tools-browser/tests/test_browser_tools.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Tests for hanzo-tools-browser.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import browser - - assert browser is not None - - def test_import_tools(self): - from hanzo_tools.browser import TOOLS - - assert len(TOOLS) > 0 - - def test_import_browser_tool(self): - from hanzo_tools.browser import BrowserTool - - assert BrowserTool.name == "browser" - - -class TestBrowserTool: - """Tests for BrowserTool.""" - - @pytest.fixture - def tool(self): - from hanzo_tools.browser import BrowserTool - - return BrowserTool() - - def test_has_description(self, tool): - assert tool.description - assert ( - "browser" in tool.description.lower() - or "playwright" in tool.description.lower() - ) diff --git a/pkg/hanzo-tools-browser/tests/test_zap_bench.py b/pkg/hanzo-tools-browser/tests/test_zap_bench.py deleted file mode 100644 index e75dff0f3..000000000 --- a/pkg/hanzo-tools-browser/tests/test_zap_bench.py +++ /dev/null @@ -1,80 +0,0 @@ -"""Latency benchmarks for ZAP vs HTTP-bridge transports. - -This isn't strict pass/fail โ€” it asserts a generous upper bound on ZAP -median round-trip and prints both numbers for comparison so PR reviewers -can see the win. -""" - -from __future__ import annotations - -import asyncio -import json -import statistics -import time -from typing import Callable - -import pytest - -from hanzo_tools.browser import zap_server as zs -from tests.test_zap_server import MockExtensionClient, _free_ports # type: ignore - - -pytestmark = pytest.mark.asyncio - - -def _percentile(values: list[float], p: float) -> float: - if not values: - return 0.0 - s = sorted(values) - idx = max(0, min(len(s) - 1, int(round(p / 100.0 * (len(s) - 1))))) - return s[idx] - - -async def _measure(label: str, n: int, op: Callable[[], asyncio.Future]) -> dict: - # warm-up - for _ in range(5): - await op() - samples: list[float] = [] - for _ in range(n): - t0 = time.perf_counter() - await op() - samples.append((time.perf_counter() - t0) * 1000.0) - return { - "label": label, - "n": n, - "min_ms": min(samples), - "p50_ms": statistics.median(samples), - "p95_ms": _percentile(samples, 95), - "max_ms": max(samples), - } - - -async def test_zap_round_trip_under_5ms_median(tmp_path, monkeypatch): - """ZAP `evaluate('1+1')` median round-trip must be sub-5ms locally.""" - monkeypatch.setenv("HOME", str(tmp_path)) - ports = _free_ports(5) - - srv = zs.ZapServer(ports=ports) - port = await srv.start() - assert port is not None - client = MockExtensionClient(port, browser="firefox", client_id="bench") - await client.connect() - await asyncio.sleep(0.02) - - # Extension responds immediately to evaluate("1+1") with the value. - client.response_handler = lambda m, p: {"result": {"type": "number", "value": 2}} - - try: - result = await _measure( - "zap", - n=200, - op=lambda: srv.send("Runtime.evaluate", {"expression": "1+1"}), - ) - # Print so CI logs surface the number. - print(f"\n[ZAP bench] {json.dumps(result, indent=2)}") - # Generous bound: 5ms p50 on local loopback is safe even on a - # busy laptop; production target is <1ms. - assert result["p50_ms"] < 5.0, f"ZAP p50 too high: {result}" - finally: - await client.close() - await srv.stop() diff --git a/pkg/hanzo-tools-browser/tests/test_zap_server.py b/pkg/hanzo-tools-browser/tests/test_zap_server.py deleted file mode 100644 index 2cd888d3b..000000000 --- a/pkg/hanzo-tools-browser/tests/test_zap_server.py +++ /dev/null @@ -1,436 +0,0 @@ -"""Tests for hanzo_tools.browser.zap_server. - -Covers: -- Wire format encode/decode round-trip and parity with shared/zap.ts. -- Server bind on OS-assigned port (HIP-0069: mDNS-only discovery). -- Mock extension client: register, send response to RPC, ping/pong. -- Browser resolution priority (client_id > browser > default preference). -- Browser leases (claim / release / reject when held by other holder). -""" - -from __future__ import annotations - -import asyncio -import json -import struct -import time - -import pytest -import websockets -from websockets.asyncio.client import connect as ws_connect - -from hanzo_tools.browser import zap_server as zs - -# --------------------------------------------------------------------------- -# Wire format -# --------------------------------------------------------------------------- - - -class TestWireFormat: - def test_magic(self): - assert zs.ZAP_MAGIC == b"\x5a\x41\x50\x01" - - def test_constants_match_extension(self): - # Must stay locked to shared/zap.ts canonical values. - assert zs.MSG_HANDSHAKE == 0x01 - assert zs.MSG_HANDSHAKE_OK == 0x02 - assert zs.MSG_REQUEST == 0x10 - assert zs.MSG_RESPONSE == 0x11 - assert zs.MSG_PING == 0xFE - assert zs.MSG_PONG == 0xFF - - def test_encode_layout(self): - from zap.protocol import HEADER_SIZE - - frame = zs.encode(zs.MSG_REQUEST, {"id": "x", "method": "y"}) - assert frame[:4] == zs.ZAP_MAGIC - assert frame[4] == zs.MSG_REQUEST - (length,) = struct.unpack(">I", frame[5:9]) - assert length == len(frame) - HEADER_SIZE - assert json.loads(frame[9:].decode()) == {"id": "x", "method": "y"} - - def test_round_trip(self): - for msg_type in ( - zs.MSG_HANDSHAKE, - zs.MSG_HANDSHAKE_OK, - zs.MSG_REQUEST, - zs.MSG_RESPONSE, - zs.MSG_PING, - zs.MSG_PONG, - ): - payload = {"a": 1, "b": [1, 2, 3], "c": {"nested": True}} - decoded = zs.decode(zs.encode(msg_type, payload)) - assert decoded is not None - assert decoded[0] == msg_type - assert decoded[1] == payload - - def test_decode_rejects_bad_magic(self): - bad = b"\xff\xff\xff\xff" + b"\x10" + struct.pack(">I", 0) - assert zs.decode(bad) is None - - def test_decode_rejects_short_frame(self): - assert zs.decode(b"") is None - assert zs.decode(b"\x5a\x41\x50") is None - - def test_decode_handles_empty_payload(self): - frame = zs.encode(zs.MSG_PING, {}) - decoded = zs.decode(frame) - assert decoded is not None and decoded[1] == {} - - -# --------------------------------------------------------------------------- -# Helpers โ€” mock extension client -# --------------------------------------------------------------------------- - - -class MockExtensionClient: - """Minimal browser-extension client speaking ZAP wire format. - - Registers on connect, exposes ``response_handler`` so tests can react - to inbound MSG_REQUEST and reply with MSG_RESPONSE. - """ - - def __init__( - self, port: int, *, browser: str = "firefox", client_id: str | None = None - ): - self.port = port - self.browser = browser - self.client_id = client_id or f"ext-test-{int(time.time() * 1000)}" - self.ws: websockets.ClientConnection | None = None - self.received: list[tuple[int, dict]] = [] - self.handshake_ok: dict | None = None - self._task: asyncio.Task | None = None - self.response_handler: callable | None = None - - async def connect(self) -> None: - self.ws = await ws_connect(f"ws://127.0.0.1:{self.port}") - await self.ws.send( - zs.encode( - zs.MSG_HANDSHAKE, - { - "clientId": self.client_id, - "clientType": "browser_extension", - "browser": self.browser, - "version": "test-0.0", - "capabilities": ["navigate", "evaluate", "click"], - }, - ) - ) - # Wait for HANDSHAKE_OK - raw = await asyncio.wait_for(self.ws.recv(), timeout=2.0) - decoded = zs.decode(bytes(raw)) - assert decoded is not None and decoded[0] == zs.MSG_HANDSHAKE_OK - self.handshake_ok = decoded[1] - self._task = asyncio.create_task(self._run()) - - async def _run(self) -> None: - assert self.ws is not None - try: - async for raw in self.ws: - if not isinstance(raw, (bytes, bytearray)): - continue - decoded = zs.decode(bytes(raw)) - if decoded is None: - continue - msg_type, payload = decoded - self.received.append((msg_type, payload or {})) - if msg_type == zs.MSG_REQUEST and self.response_handler is not None: - method = payload.get("method") if payload else "" - params = payload.get("params") if payload else {} - req_id = payload.get("id") if payload else None - try: - result = self.response_handler(method, params) - if asyncio.iscoroutine(result): - result = await result - await self.ws.send( - zs.encode(zs.MSG_RESPONSE, {"id": req_id, "result": result}) - ) - except Exception as e: - await self.ws.send( - zs.encode( - zs.MSG_RESPONSE, - { - "id": req_id, - "error": {"code": -1, "message": str(e)}, - }, - ) - ) - elif msg_type == zs.MSG_PING: - await self.ws.send(zs.encode(zs.MSG_PONG, {})) - except Exception: - pass - - async def close(self) -> None: - if self._task: - self._task.cancel() - if self.ws: - await self.ws.close() - - -# --------------------------------------------------------------------------- -# Server fixtures -# --------------------------------------------------------------------------- - - -@pytest.fixture -async def server(): - """A live ZapServer on an OS-assigned port. mDNS publish is best-effort - โ€” if zap-mdns is unavailable the start() returns None, and the test - is skipped (CI without zeroconf shouldn't hard-fail).""" - srv = zs.ZapServer() - port = await srv.start() - if port is None: - pytest.skip("ZapServer.start() returned None โ€” zap-mdns/websockets missing") - yield srv - await srv.stop() - - -@pytest.fixture -async def client(server): - c = MockExtensionClient(server.port) - await c.connect() - # Allow the server to register the connection - await asyncio.sleep(0.05) - yield c - await c.close() - - -# --------------------------------------------------------------------------- -# Bind / shutdown -# --------------------------------------------------------------------------- - - -class TestServerLifecycle: - async def test_binds_on_ephemeral_port(self): - srv = zs.ZapServer() - port = await srv.start() - if port is None: - pytest.skip("zap-mdns/websockets missing") - try: - assert isinstance(port, int) and port > 0 - assert srv.port == port - finally: - await srv.stop() - - async def test_two_servers_get_distinct_ports(self): - a = zs.ZapServer() - b = zs.ZapServer() - try: - port_a = await a.start() - port_b = await b.start() - if port_a is None or port_b is None: - pytest.skip("zap-mdns/websockets missing") - assert port_a != port_b - finally: - await a.stop() - await b.stop() - - async def test_stop_clears_port(self): - srv = zs.ZapServer() - port = await srv.start() - if port is None: - pytest.skip("zap-mdns/websockets missing") - await srv.stop() - assert srv.port is None - - -# --------------------------------------------------------------------------- -# Handshake / client registry -# --------------------------------------------------------------------------- - - -class TestClientRegistry: - async def test_handshake_registers_client(self, server, client): - assert server.has_client() - assert server.has_client(browser="firefox") - assert not server.has_client(browser="chrome") - assert len(server.clients) == 1 - assert server.clients[0].browser == "firefox" - assert server.clients[0].client_id == client.client_id - - async def test_handshake_ok_includes_server_id_and_tools(self, server, client): - assert client.handshake_ok is not None - assert client.handshake_ok["serverId"] == server.server_id - assert isinstance(client.handshake_ok["tools"], list) - assert any(t["name"] == "browser" for t in client.handshake_ok["tools"]) - - async def test_disconnect_removes_client(self, server): - c = MockExtensionClient(server.port) - await c.connect() - await asyncio.sleep(0.05) - assert server.has_client() - await c.close() - # Allow server cleanup - await asyncio.sleep(0.1) - assert not server.has_client() - - -# --------------------------------------------------------------------------- -# RPC dispatch -# --------------------------------------------------------------------------- - - -class TestRpcDispatch: - async def test_send_round_trips_to_extension(self, server, client): - async def handler(method, params): - assert method == "Page.navigate" - assert params == {"url": "https://example.com"} - return {"frameId": "main"} - - client.response_handler = handler - result = await server.send("Page.navigate", {"url": "https://example.com"}) - assert result == {"frameId": "main"} - - async def test_extension_error_propagates(self, server, client): - async def handler(method, params): - raise RuntimeError("nope") - - client.response_handler = handler - with pytest.raises(RuntimeError, match="nope"): - await server.send("hanzo.click", {"selector": "#x"}) - - async def test_send_with_browser_filter(self, server): - c1 = MockExtensionClient(server.port, browser="firefox", client_id="ff-1") - c2 = MockExtensionClient(server.port, browser="chrome", client_id="ch-1") - await c1.connect() - await c2.connect() - await asyncio.sleep(0.05) - - c1.response_handler = lambda m, p: "from-firefox" - c2.response_handler = lambda m, p: "from-chrome" - - try: - assert await server.send("any", {}, browser="firefox") == "from-firefox" - assert await server.send("any", {}, browser="chrome") == "from-chrome" - finally: - await c1.close() - await c2.close() - - async def test_no_match_raises(self, server, client): - # client is firefox; ask for chrome - with pytest.raises(RuntimeError, match="No ZAP-connected"): - await server.send("any", {}, browser="chrome") - - async def test_ping_pong(self, server, client): - # Send ping and verify server echoes pong; just ensure no crash. - await client.ws.send(zs.encode(zs.MSG_PING, {})) - # Receive pong - await asyncio.sleep(0.05) - # Server replies with MSG_PONG (we caught it in client.received via _run) - types = [t for t, _ in client.received] - assert zs.MSG_PONG in types - - async def test_extension_initiated_request_acked(self, server, client): - # Extension sends MSG_REQUEST as a notification (server must ack) - await client.ws.send( - zs.encode( - zs.MSG_REQUEST, - { - "id": "evt-1", - "method": "notifications/elementSelected", - "params": {"x": 1}, - }, - ) - ) - await asyncio.sleep(0.1) - # Find the response - responses = [p for t, p in client.received if t == zs.MSG_RESPONSE] - assert any(r.get("id") == "evt-1" for r in responses) - - -# --------------------------------------------------------------------------- -# Resolution priority -# --------------------------------------------------------------------------- - - -class TestResolution: - async def test_resolve_explicit_client_id(self, server): - c1 = MockExtensionClient(server.port, browser="firefox", client_id="ff-1") - c2 = MockExtensionClient(server.port, browser="firefox", client_id="ff-2") - await c1.connect() - await c2.connect() - await asyncio.sleep(0.05) - try: - picked = server.resolve_client(client_id="ff-2") - assert picked is not None and picked.client_id == "ff-2" - finally: - await c1.close() - await c2.close() - - async def test_default_prefers_firefox(self, server): - chrome = MockExtensionClient(server.port, browser="chrome", client_id="ch") - firefox = MockExtensionClient(server.port, browser="firefox", client_id="ff") - await chrome.connect() - await firefox.connect() - await asyncio.sleep(0.05) - try: - picked = server.resolve_client() - assert picked is not None and "firefox" in picked.browser - finally: - await chrome.close() - await firefox.close() - - -# --------------------------------------------------------------------------- -# Leases -# --------------------------------------------------------------------------- - - -class TestLeases: - async def test_claim_and_release(self, server, client): - lease = server.claim(client.client_id, ttl=10) - assert lease.client_id == client.client_id - assert lease.holder == server.server_id - assert server.release(client.client_id) is True - - async def test_claim_rejects_when_held_by_other(self, server, client): - # First grab from server - server.claim(client.client_id, ttl=10) - # Synthesise a different "holder" by mutating server_id; this is the - # closest we can get without spinning up a second ZapServer. - original = server.server_id - try: - server.server_id = "other-mcp" - with pytest.raises(RuntimeError, match="already leased"): - server.claim(client.client_id, ttl=10) - finally: - server.server_id = original - - async def test_release_only_works_for_holder(self, server, client): - server.claim(client.client_id, ttl=10) - original = server.server_id - try: - server.server_id = "other-mcp" - assert server.release(client.client_id) is False - finally: - server.server_id = original - - async def test_send_blocked_by_other_holder(self, server, client): - # Hold with a different holder - from hanzo_tools.browser.zap_server import BrowserLease - - server._leases[client.client_id] = BrowserLease( - client_id=client.client_id, - holder="someone-else", - expires_at=time.time() + 30, - ) - with pytest.raises(RuntimeError, match="leased by"): - await server.send("any", {}) - - -# --------------------------------------------------------------------------- -# Cluster discovery (mDNS โ€” best-effort, may be empty in CI) -# --------------------------------------------------------------------------- - - -class TestClusterDiscovery: - async def test_list_mcp_instances_returns_list(self, server): - # mDNS browse may return [] in containers without multicast routing. - # We just assert the call shape is correct. - instances = zs.ZapServer.list_mcp_instances(timeout=0.5) - assert isinstance(instances, list) - for entry in instances: - assert "server_id" in entry - assert "host" in entry - assert "port" in entry - assert "url" in entry diff --git a/pkg/hanzo-tools-code/README.md b/pkg/hanzo-tools-code/README.md deleted file mode 100644 index 771336320..000000000 --- a/pkg/hanzo-tools-code/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# hanzo-tools-code - -Code semantic tools for Hanzo AI (HIP-0300). - -## Installation - -```bash -pip install hanzo-tools-code -``` diff --git a/pkg/hanzo-tools-code/hanzo_tools/__init__.py b/pkg/hanzo-tools-code/hanzo_tools/__init__.py deleted file mode 100644 index 946984951..000000000 --- a/pkg/hanzo-tools-code/hanzo_tools/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Namespace package -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-code/hanzo_tools/code/__init__.py b/pkg/hanzo-tools-code/hanzo_tools/code/__init__.py deleted file mode 100644 index 822270df2..000000000 --- a/pkg/hanzo-tools-code/hanzo_tools/code/__init__.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Code semantic tools for Hanzo AI (HIP-0300). - -Tools: -- code: Unified code semantics tool (HIP-0300) - - parse: Parse source to AST (tree-sitter) - - serialize: AST back to text - - symbols: List symbols in file - - definition: Go to definition (LSP) - - references: Find all references (LSP) - - transform: Pure codemod โ†’ Patch - - summarize: Compress diff/log/report - -Effect lattice position: PURE -All operations are safe to cache and parallelize. - -Install: - pip install hanzo-tools-code - pip install hanzo-tools-code[tree-sitter] # For AST parsing - pip install hanzo-tools-code[lsp] # For LSP integration - -Usage: - from hanzo_tools.code import register_tools, TOOLS - - # Register with MCP server - register_tools(mcp_server) - - # Or access the unified tool - from hanzo_tools.code import CodeTool -""" - -from hanzo_tools.core import BaseTool, ToolRegistry - -from .code_tool import CodeTool, code_tool - -# Export list for tool discovery - HIP-0300 unified tool -TOOLS = [CodeTool] - -__all__ = [ - "CodeTool", - "code_tool", - "register_tools", - "TOOLS", -] - - -def register_tools(mcp_server, **kwargs) -> list[BaseTool]: - """Register code tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - **kwargs: Additional options (cwd, etc.) - - Returns: - List of registered tool instances - """ - cwd = kwargs.get("cwd") - tool = CodeTool(cwd=cwd) - ToolRegistry.register_tool(mcp_server, tool) - return [tool] diff --git a/pkg/hanzo-tools-code/hanzo_tools/code/code_tool.py b/pkg/hanzo-tools-code/hanzo_tools/code/code_tool.py deleted file mode 100644 index 1f513125e..000000000 --- a/pkg/hanzo-tools-code/hanzo_tools/code/code_tool.py +++ /dev/null @@ -1,815 +0,0 @@ -"""Unified code semantics tool for HIP-0300 architecture. - -This module provides a single unified 'code' tool that handles all semantic code operations: -- parse: Parse source to AST (tree-sitter) -- serialize: AST back to text -- symbols: List symbols in file/scope -- definition: Go to definition (LSP) -- references: Find all references (LSP) -- transform: Pure codemod โ†’ Patch (no side effects) -- summarize: Compress Diff/Log/Report to summary - -Following Unix philosophy: one tool for the Symbols + Structure axis. -All operations are PURE (no side effects) except where noted. - -Effect lattice position: PURE -Representation: Text โ†’ AST โ†’ Patch -Scope: File โ†’ Package โ†’ Repo -""" - -import difflib -import os -from dataclasses import dataclass, field -from pathlib import Path -from typing import Any, ClassVar, Literal - -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - InvalidParamsError, - NotFoundError, - ToolError, - content_hash, -) - -# Language detection by extension -LANG_MAP = { - ".py": "python", - ".js": "javascript", - ".ts": "typescript", - ".tsx": "tsx", - ".jsx": "jsx", - ".go": "go", - ".rs": "rust", - ".c": "c", - ".cpp": "cpp", - ".h": "c", - ".hpp": "cpp", - ".java": "java", - ".rb": "ruby", - ".php": "php", - ".swift": "swift", - ".kt": "kotlin", - ".scala": "scala", - ".cs": "c_sharp", - ".lua": "lua", - ".sh": "bash", - ".bash": "bash", - ".zsh": "bash", - ".json": "json", - ".yaml": "yaml", - ".yml": "yaml", - ".toml": "toml", - ".md": "markdown", - ".html": "html", - ".css": "css", - ".sql": "sql", -} - - -@dataclass -class TransformSpec: - """Specification for code transformation.""" - - kind: Literal["rename", "extract", "inline", "move", "codemod", "generate"] - - # For rename - old_name: str | None = None - new_name: str | None = None - scope: Literal["file", "package", "repo"] = "file" - - # For codemod (pattern-based) - match_pattern: str | None = None - replace_template: str | None = None - - # For generate - template: str | None = None - context: dict[str, Any] = field(default_factory=dict) - - -class CodeTool(BaseTool): - """Unified code semantics tool (HIP-0300). - - Handles all semantic code operations on a single axis: - - parse: Parse source to AST - - serialize: AST to text - - symbols: List symbols - - definition: Go to definition - - references: Find references - - transform: Pure codemod โ†’ Patch - - summarize: Compress to summary - - All operations are PURE (safe to parallelize/cache). - """ - - name: ClassVar[str] = "code" - VERSION: ClassVar[str] = "0.1.0" - - def __init__(self, cwd: str | None = None): - super().__init__() - self.cwd = cwd or os.getcwd() - self._tree_sitter = None - self._lsp_clients: dict[str, Any] = {} - self._register_code_actions() - - @property - def description(self) -> str: - return """Unified code semantics tool (HIP-0300). - -Actions: -- parse: Parse source code to AST (tree-sitter) -- serialize: Convert AST back to text -- symbols: List symbols in file/scope -- definition: Go to symbol definition (LSP) -- references: Find all references to symbol (LSP) -- transform: Pure codemod producing Patch (no side effects) -- summarize: Compress Diff/Log/Report to summary - -All operations are PURE - safe to cache and parallelize. -""" - - def _detect_lang(self, path: str | None, text: str | None = None) -> str: - """Detect language from path extension or content.""" - if path: - ext = Path(path).suffix.lower() - if ext in LANG_MAP: - return LANG_MAP[ext] - - # Fallback: try to detect from shebang or content - if text: - first_line = text.split("\n", 1)[0] - if first_line.startswith("#!"): - if "python" in first_line: - return "python" - elif "node" in first_line or "deno" in first_line: - return "javascript" - elif "bash" in first_line or "sh" in first_line: - return "bash" - - return "unknown" - - def _get_tree_sitter(self): - """Lazy-load tree-sitter.""" - if self._tree_sitter is None: - # Try tree-sitter-language-pack first (works with tree-sitter 0.24+) - try: - import tree_sitter_language_pack - self._tree_sitter = tree_sitter_language_pack - except ImportError: - try: - import tree_sitter_languages - self._tree_sitter = tree_sitter_languages - except ImportError: - raise ToolError( - code="INTERNAL_ERROR", - message="tree-sitter-languages not installed. Run: pip install tree-sitter-language-pack", - ) - return self._tree_sitter - - def _parse_with_tree_sitter(self, text: str, lang: str) -> dict: - """Parse text to AST using tree-sitter.""" - ts = self._get_tree_sitter() - - try: - parser = ts.get_parser(lang) - tree = parser.parse(text.encode()) - - def node_to_dict(node) -> dict: - result = { - "type": node.type, - "start": {"line": node.start_point[0], "col": node.start_point[1]}, - "end": {"line": node.end_point[0], "col": node.end_point[1]}, - } - if node.child_count > 0: - result["children"] = [node_to_dict(c) for c in node.children] - else: - result["text"] = node.text.decode() if node.text else "" - return result - - return { - "root": node_to_dict(tree.root_node), - "lang": lang, - "errors": [ - { - "line": n.start_point[0], - "col": n.start_point[1], - "message": "syntax error", - } - for n in tree.root_node.children - if n.type == "ERROR" - ], - } - except Exception as e: - raise ToolError( - code="INTERNAL_ERROR", - message=f"Parse error: {e}", - details={"lang": lang}, - ) - - def _extract_symbols(self, ast: dict, lang: str) -> list[dict]: - """Extract symbols from AST.""" - symbols = [] - - # Symbol node types by language - symbol_types = { - "python": ["function_definition", "class_definition", "assignment"], - "javascript": [ - "function_declaration", - "class_declaration", - "variable_declaration", - "lexical_declaration", - ], - "typescript": [ - "function_declaration", - "class_declaration", - "interface_declaration", - "type_alias_declaration", - ], - "go": ["function_declaration", "method_declaration", "type_declaration"], - "rust": [ - "function_item", - "struct_item", - "enum_item", - "impl_item", - "trait_item", - ], - } - - types_to_find = symbol_types.get( - lang, ["function_definition", "class_definition"] - ) - - def walk(node: dict, parent_name: str = ""): - node_type = node.get("type", "") - - if node_type in types_to_find: - # Try to extract name from first identifier child - name = None - for child in node.get("children", []): - if child.get("type") == "identifier": - name = child.get("text", "") - break - elif child.get("type") == "name": - name = child.get("text", "") - break - - if name: - symbols.append( - { - "name": name, - "kind": node_type, - "range": { - "start": node["start"], - "end": node["end"], - }, - "parent": parent_name or None, - } - ) - parent_name = name - - for child in node.get("children", []): - walk(child, parent_name) - - if "root" in ast: - walk(ast["root"]) - - return symbols - - def _generate_patch(self, original: str, modified: str, path: str = "file") -> str: - """Generate unified diff patch.""" - original_lines = original.splitlines(keepends=True) - modified_lines = modified.splitlines(keepends=True) - - diff = difflib.unified_diff( - original_lines, - modified_lines, - fromfile=f"a/{path}", - tofile=f"b/{path}", - ) - - return "".join(diff) - - def _apply_rename(self, text: str, old_name: str, new_name: str) -> tuple[str, int]: - """Apply simple rename transformation. Returns (new_text, count).""" - import re - - # Word-boundary rename to avoid partial matches - pattern = rf"\b{re.escape(old_name)}\b" - new_text, count = re.subn(pattern, new_name, text) - return new_text, count - - def _apply_codemod(self, text: str, match: str, replace: str) -> tuple[str, int]: - """Apply pattern-based codemod. Returns (new_text, count).""" - import re - - try: - new_text, count = re.subn(match, replace, text) - return new_text, count - except re.error as e: - raise InvalidParamsError( - f"Invalid regex pattern: {e}", param="match_pattern" - ) - - def _register_code_actions(self): - """Register all code actions.""" - - @self.action("parse", "Parse source code to AST") - async def parse( - ctx: MCPContext, - path: str | None = None, - text: str | None = None, - lang: str | None = None, - ) -> dict: - """Parse source to AST using tree-sitter. - - Args: - path: File path to parse - text: Raw text to parse (alternative to path) - lang: Language (auto-detected if not specified) - - Returns: - AST structure with lang and errors - - Effect: PURE - Cache: hash(text) - """ - if path and not text: - full_path = ( - Path(path) if Path(path).is_absolute() else Path(self.cwd) / path - ) - if not full_path.exists(): - raise NotFoundError(f"File not found: {path}", uri=str(full_path)) - text = full_path.read_text() - - if not text: - raise InvalidParamsError("Either path or text required", param="text") - - detected_lang = lang or self._detect_lang(path, text) - - ast = self._parse_with_tree_sitter(text, detected_lang) - - return { - "ast": ast, - "lang": detected_lang, - "hash": content_hash(text), - "source_map": {"path": path} if path else None, - } - - @self.action("serialize", "Convert AST back to text") - async def serialize( - ctx: MCPContext, - ast: dict, - lang: str | None = None, - ) -> dict: - """Serialize AST back to text. - - Note: This is a simplified implementation that extracts - leaf node text. Full round-trip requires CST preservation. - - Effect: PURE - """ - - def extract_text(node: dict) -> str: - if "text" in node: - return node["text"] - children_text = [] - for child in node.get("children", []): - children_text.append(extract_text(child)) - return " ".join(children_text) - - root = ast.get("root") or ast - text = extract_text(root) - - return { - "text": text, - "lang": lang or ast.get("lang", "unknown"), - } - - @self.action("symbols", "List symbols in file") - async def symbols( - ctx: MCPContext, - path: str | None = None, - text: str | None = None, - ast: dict | None = None, - lang: str | None = None, - ) -> dict: - """Extract symbols from source code. - - Args: - path: File path - text: Raw text (alternative) - ast: Pre-parsed AST (alternative) - lang: Language hint - - Returns: - List of symbols with name, kind, range - - Effect: PURE - """ - if ast is None: - if path and not text: - full_path = ( - Path(path) - if Path(path).is_absolute() - else Path(self.cwd) / path - ) - if not full_path.exists(): - raise NotFoundError(f"File not found: {path}") - text = full_path.read_text() - - if not text: - raise InvalidParamsError("path, text, or ast required") - - detected_lang = lang or self._detect_lang(path, text) - parsed = self._parse_with_tree_sitter(text, detected_lang) - ast = parsed - lang = detected_lang - - symbols_list = self._extract_symbols( - ast, lang or ast.get("lang", "unknown") - ) - - return { - "symbols": symbols_list, - "count": len(symbols_list), - "path": path, - } - - @self.action("definition", "Go to symbol definition") - async def definition( - ctx: MCPContext, - path: str, - line: int, - col: int, - ) -> dict: - """Find definition of symbol at position. - - Uses LSP when available, falls back to AST search. - - Effect: PURE - """ - # TODO: Integrate with LSP (gopls, pyright, etc.) - # For now, return a stub indicating LSP integration needed - return { - "path": path, - "position": {"line": line, "col": col}, - "definition": None, - "note": "LSP integration required for full definition lookup", - } - - @self.action("references", "Find all references to symbol") - async def references( - ctx: MCPContext, - path: str, - line: int, - col: int, - scope: str = "repo", - ) -> dict: - """Find all references to symbol at position. - - Uses LSP when available, falls back to text search. - - Effect: PURE - Scope: file | package | repo - """ - # TODO: Integrate with LSP - return { - "path": path, - "position": {"line": line, "col": col}, - "references": [], - "scope": scope, - "note": "LSP integration required for full reference lookup", - } - - @self.action("transform", "Generate patch from transformation spec") - async def transform( - ctx: MCPContext, - path: str | None = None, - text: str | None = None, - kind: str = "rename", - old_name: str | None = None, - new_name: str | None = None, - match_pattern: str | None = None, - replace_template: str | None = None, - scope: str = "file", - ) -> dict: - """Apply transformation and produce Patch (PURE - no side effects). - - This is the key "pure middle layer" operator: - - Input: source + transform spec - - Output: Patch as value (not applied) - - Kinds: - rename: Rename symbol (old_name โ†’ new_name) - codemod: Pattern-based replacement - - Effect: PURE - """ - # Load text if path provided - if path and not text: - full_path = ( - Path(path) if Path(path).is_absolute() else Path(self.cwd) / path - ) - if not full_path.exists(): - raise NotFoundError(f"File not found: {path}") - text = full_path.read_text() - - if not text: - raise InvalidParamsError("path or text required") - - original_hash = content_hash(text) - original = text - changes_count = 0 - - if kind == "rename": - if not old_name or not new_name: - raise InvalidParamsError("rename requires old_name and new_name") - text, changes_count = self._apply_rename(text, old_name, new_name) - - elif kind == "codemod": - if not match_pattern or replace_template is None: - raise InvalidParamsError( - "codemod requires match_pattern and replace_template" - ) - text, changes_count = self._apply_codemod( - text, match_pattern, replace_template - ) - - else: - raise InvalidParamsError( - f"Unknown transform kind: {kind}", - param="kind", - expected="rename | codemod", - ) - - # Generate patch - patch = self._generate_patch(original, text, path or "input") - - return { - "patch": patch, - "changes_count": changes_count, - "base_hash": original_hash, - "new_hash": content_hash(text) if changes_count > 0 else original_hash, - "kind": kind, - "report": { - "files_affected": 1 if changes_count > 0 else 0, - "lines_changed": patch.count("\n") if patch else 0, - }, - } - - @self.action("summarize", "Compress diff/log/report to summary") - async def summarize( - ctx: MCPContext, - diff: str | None = None, - log: list[dict] | None = None, - report: dict | None = None, - text: str | None = None, - max_length: int = 500, - ) -> dict: - """Summarize code artifacts for review. - - Takes one of: diff, log, report, or text - Produces: summary, risks, next_actions - - Effect: PURE - """ - summary_parts = [] - risks = [] - next_actions = [] - - if diff: - # Analyze diff - lines = diff.split("\n") - adds = sum( - 1 - for line in lines - if line.startswith("+") and not line.startswith("+++") - ) - dels = sum( - 1 - for line in lines - if line.startswith("-") and not line.startswith("---") - ) - files = [line[6:] for line in lines if line.startswith("+++ b/")] - - summary_parts.append( - f"Patch: +{adds}/-{dels} lines across {len(files)} file(s)" - ) - - if adds + dels > 100: - risks.append("Large change - review carefully") - if any("test" in f.lower() for f in files): - next_actions.append("Run tests to verify") - else: - risks.append("No test files modified") - next_actions.append("Consider adding tests") - - if log: - summary_parts.append(f"History: {len(log)} commit(s)") - if log: - latest = ( - log[0] if isinstance(log[0], dict) else {"message": str(log[0])} - ) - summary_parts.append(f"Latest: {latest.get('message', 'N/A')[:50]}") - - if report: - if "pass" in report: - status = "PASS" if report["pass"] else "FAIL" - summary_parts.append(f"Status: {status}") - if not report["pass"]: - risks.append("Tests failing") - next_actions.append("Fix failing tests before merge") - - if text: - # Simple text summary - word_count = len(text.split()) - summary_parts.append(f"Text: {word_count} words") - if word_count > 1000: - summary_parts.append("(truncated for summary)") - - return { - "summary": ( - " | ".join(summary_parts) - if summary_parts - else "No content to summarize" - ), - "risks": risks, - "next_actions": next_actions, - } - - # โ”€โ”€ TS-parity actions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - @self.action("search_symbol", "Find symbols across project") - async def search_symbol( - ctx: MCPContext, - query: str, - path: str | None = None, - max_results: int = 20, - ) -> dict: - import re - search_dir = Path(path) if path else Path(self.cwd) - results = [] - skip_dirs = {".git", "node_modules", "target", "dist", "__pycache__", ".venv"} - for p in search_dir.rglob("*"): - if len(results) >= max_results: - break - if any(sd in p.parts for sd in skip_dirs) or not p.is_file() or p.suffix not in LANG_MAP: - continue - try: - text = p.read_text(errors="ignore") - for i, line in enumerate(text.splitlines(), 1): - if query in line and re.search(r'\b(def|class|function|fn|struct|enum|interface|type|const|let|var|pub)\b', line): - results.append({"uri": str(p), "line": i, "text": line.strip(), "type": "definition"}) - if len(results) >= max_results: - break - except (OSError, UnicodeDecodeError): - continue - return {"query": query, "results": results, "count": len(results)} - - @self.action("outline", "List symbols with import count and export flag") - async def outline(ctx: MCPContext, path: str, text: str | None = None) -> dict: - import re - full_path = Path(path) if Path(path).is_absolute() else Path(self.cwd) / path - if text is None: - if not full_path.exists(): - raise NotFoundError(f"File not found: {path}") - text = full_path.read_text() - lines = text.splitlines() - syms = [] - imports = 0 - sym_re = re.compile(r'(?:export\s+)?(?:async\s+)?(?:function|class|interface|type|enum|const|let|var|def|fn|pub\s+fn|pub\s+struct|struct|impl)\s+(\w+)') - imp_re = re.compile(r'^(?:import|from|require|use)\b') - for i, line in enumerate(lines, 1): - if imp_re.match(line.strip()): - imports += 1 - m = sym_re.search(line) - if m: - kind_m = re.search(r'\b(class|interface|type|enum|function|const|struct|impl|fn|def)\b', line) - syms.append({"name": m.group(1), "kind": kind_m.group(1) if kind_m else "symbol", "line": i, "exported": line.strip().startswith(("export ", "pub "))}) - return {"uri": str(full_path), "symbols": syms, "imports": imports, "lines": len(lines)} - - @self.action("metrics", "Count files and lines by extension") - async def metrics(ctx: MCPContext, path: str | None = None) -> dict: - search_dir = Path(path) if path else Path(self.cwd) - skip_dirs = {".git", "node_modules", "target", "dist", "__pycache__", ".venv"} - by_ext: dict[str, dict[str, int]] = {} - total_files = 0 - total_lines = 0 - for p in search_dir.rglob("*"): - if any(sd in p.parts for sd in skip_dirs) or not p.is_file(): - continue - ext = p.suffix or "other" - try: - lines = len(p.read_text(errors="ignore").splitlines()) - if ext not in by_ext: - by_ext[ext] = {"files": 0, "lines": 0} - by_ext[ext]["files"] += 1 - by_ext[ext]["lines"] += lines - total_files += 1 - total_lines += lines - except (OSError, UnicodeDecodeError): - continue - return {"total_files": total_files, "total_lines": total_lines, "by_extension": by_ext} - - @self.action("exports", "Extract public exports from file") - async def exports(ctx: MCPContext, path: str) -> dict: - full_path = Path(path) if Path(path).is_absolute() else Path(self.cwd) / path - if not full_path.exists(): - raise NotFoundError(f"File not found: {path}") - text = full_path.read_text() - export_lines = [line.strip() for line in text.splitlines() if line.strip().startswith(("export ", "pub ", "__all__"))] - return {"uri": str(full_path), "exports": export_lines, "count": len(export_lines)} - - @self.action("types", "Find type definitions in file") - async def types(ctx: MCPContext, path: str) -> dict: - import re - full_path = Path(path) if Path(path).is_absolute() else Path(self.cwd) / path - if not full_path.exists(): - raise NotFoundError(f"File not found: {path}") - text = full_path.read_text() - type_defs = [] - type_re = re.compile(r'(?:interface|type|enum|struct|class)\s+(\w+)') - for i, line in enumerate(text.splitlines(), 1): - m = type_re.search(line) - if m: - type_defs.append({"name": m.group(1), "line": i, "text": line.strip()}) - return {"uri": str(full_path), "types": type_defs, "count": len(type_defs)} - - @self.action("hierarchy", "Build class inheritance tree") - async def hierarchy(ctx: MCPContext, query: str, path: str | None = None) -> dict: - import re - search_dir = Path(path) if path else Path(self.cwd) - skip_dirs = {".git", "node_modules", "target", "dist", "__pycache__", ".venv"} - classes: dict[str, list[str]] = {} - class_re = re.compile(r'class\s+(\w+)(?:\s*\(([^)]+)\)|\s+extends\s+(\w+))?') - for p in search_dir.rglob("*"): - if any(sd in p.parts for sd in skip_dirs) or not p.is_file() or p.suffix not in LANG_MAP: - continue - try: - for m in class_re.finditer(p.read_text(errors="ignore")): - name = m.group(1) - parent = m.group(3) or (m.group(2).split(",")[0].strip() if m.group(2) else None) - if name not in classes: - classes[name] = [] - if parent and parent not in ("object", "Object"): - classes.setdefault(parent, []).append(name) - except (OSError, UnicodeDecodeError): - continue - def build(n: str, d: int = 0) -> str: - out = " " * d + n + "\n" - for c in classes.get(n, []): - out += build(c, d + 1) - return out - return {"root": query, "tree": build(query), "children": classes.get(query, [])} - - @self.action("rename", "Rename symbol across files") - async def rename(ctx: MCPContext, query: str, new_name: str, path: str | None = None) -> dict: - import re - search_dir = Path(path) if path else Path(self.cwd) - skip_dirs = {".git", "node_modules", "target", "dist", "__pycache__", ".venv"} - total_changes = 0 - changed = [] - word_re = re.compile(rf'\b{re.escape(query)}\b') - for p in search_dir.rglob("*"): - if any(sd in p.parts for sd in skip_dirs) or not p.is_file() or p.suffix not in LANG_MAP: - continue - try: - text = p.read_text(errors="ignore") - if word_re.search(text): - count = len(word_re.findall(text)) - p.write_text(word_re.sub(new_name, text)) - total_changes += count - changed.append(f"{p}: {count} replacements") - except (OSError, UnicodeDecodeError): - continue - return {"old_name": query, "new_name": new_name, "files_changed": len(changed), "total_replacements": total_changes, "changed": changed} - - @self.action("grep_replace", "Pattern replacement across codebase") - async def grep_replace(ctx: MCPContext, query: str, replacement: str, path: str | None = None) -> dict: - import re as re_mod - search_dir = Path(path) if path else Path(self.cwd) - skip_dirs = {".git", "node_modules", "target", "dist", "__pycache__", ".venv"} - total_changes = 0 - changed = [] - try: - regex = re_mod.compile(query) - except re_mod.error as e: - raise InvalidParamsError(f"Invalid regex: {e}", param="query") - for p in search_dir.rglob("*"): - if any(sd in p.parts for sd in skip_dirs) or not p.is_file() or p.suffix not in LANG_MAP: - continue - try: - text = p.read_text(errors="ignore") - if regex.search(text): - count = len(regex.findall(text)) - p.write_text(regex.sub(replacement, text)) - total_changes += count - changed.append(f"{p}: {count}") - except (OSError, UnicodeDecodeError): - continue - return {"pattern": query, "replacement": replacement, "files_changed": len(changed), "total_replacements": total_changes, "changed": changed} - - -# Singleton instance -code_tool = CodeTool diff --git a/pkg/hanzo-tools-code/pyproject.toml b/pkg/hanzo-tools-code/pyproject.toml deleted file mode 100644 index 2cbf83e31..000000000 --- a/pkg/hanzo-tools-code/pyproject.toml +++ /dev/null @@ -1,76 +0,0 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[project] -name = "hanzo-tools-code" -version = "0.1.2" -description = "Code semantic tools for Hanzo AI (HIP-0300)" -readme = "README.md" -license = "MIT" -requires-python = ">=3.12" -authors = [ - { name = "Hanzo AI Team", email = "ai@hanzo.ai" }, -] -keywords = [ - "hanzo", - "mcp", - "tools", - "code", - "ast", - "lsp", - "tree-sitter", -] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", -] -dependencies = [ - "hanzo-tools>=0.3.0", - "mcp>=1.0.0", -] - -[project.optional-dependencies] -tree-sitter = [ - "tree-sitter>=0.21.0", - "tree-sitter-languages>=1.10.0", -] -lsp = [ - "pygls>=1.0.0", -] -full = [ - "hanzo-tools-code[tree-sitter,lsp]", -] -dev = [ - "pytest>=8.0.0", - "pytest-asyncio>=0.23.0", - "ruff>=0.1.0", -] - -[project.entry-points."hanzo.tools"] -code = "hanzo_tools.code:TOOLS" - -[project.urls] -Homepage = "https://github.com/hanzoai/python-sdk" -Documentation = "https://docs.hanzo.ai" -Repository = "https://github.com/hanzoai/python-sdk" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] - -[tool.ruff] -line-length = 100 -target-version = "py310" - -[tool.ruff.lint] -select = ["E", "F", "I", "UP"] -ignore = ["E501"] - -[tool.pytest.ini_options] -asyncio_mode = "auto" -testpaths = ["tests"] diff --git a/pkg/hanzo-tools-commerce/README.md b/pkg/hanzo-tools-commerce/README.md deleted file mode 100644 index 35ffaa617..000000000 --- a/pkg/hanzo-tools-commerce/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# hanzo-tools-commerce - -MCP tool package for hanzo-mcp. Provides native commerce management via the Hanzo platform. - -## Installation - -```bash -pip install hanzo-tools-commerce -``` - -Part of the [hanzo-mcp](https://pypi.org/project/hanzo-mcp/) ecosystem. diff --git a/pkg/hanzo-tools-commerce/hanzo_tools/__init__.py b/pkg/hanzo-tools-commerce/hanzo_tools/__init__.py deleted file mode 100644 index 8b1378917..000000000 --- a/pkg/hanzo-tools-commerce/hanzo_tools/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pkg/hanzo-tools-commerce/hanzo_tools/commerce/__init__.py b/pkg/hanzo-tools-commerce/hanzo_tools/commerce/__init__.py deleted file mode 100644 index 08c27eaef..000000000 --- a/pkg/hanzo-tools-commerce/hanzo_tools/commerce/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Hanzo Commerce Tools -- orders, products, collections, stores, and discounts via MCP.""" - -from .commerce_tool import CommerceTool - -TOOLS = [CommerceTool] - -__all__ = ["CommerceTool", "TOOLS"] diff --git a/pkg/hanzo-tools-commerce/hanzo_tools/commerce/commerce_tool.py b/pkg/hanzo-tools-commerce/hanzo_tools/commerce/commerce_tool.py deleted file mode 100644 index 5385ce484..000000000 --- a/pkg/hanzo-tools-commerce/hanzo_tools/commerce/commerce_tool.py +++ /dev/null @@ -1,249 +0,0 @@ -"""MCP tool for Hanzo Commerce -- orders, products, collections, stores. - -Wraps the Hanzo Commerce API at api.hanzo.ai/api/v1/. -Auth: Uses HanzoSession from hanzo-tools-auth for bearer tokens. -""" - -from __future__ import annotations - -import os -import json -import logging -from typing import Any, Annotated, final - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core.base import BaseTool - -logger = logging.getLogger(__name__) - -DESCRIPTION = """Hanzo Commerce -- orders, products, collections, stores, and discounts. - -Requires authentication via `hanzo login` (stored at ~/.hanzo/auth/token.json). - -Actions: -- orders: List orders (optional query param for filtering) -- order: Get order by ID (order_id required) -- products: List products -- product: Get product by ID (product_id required) -- collections: List product collections -- search_users: Search users (query required) -- search_orders: Search orders (query required) -- stores: List stores -- discounts: List discount codes -- webhooks: List configured webhooks -""" - -API_BASE = "https://api.hanzo.ai/api/v1" - - -def _get_session(): - """Get HanzoSession singleton.""" - from hanzo_tools.auth.session import HanzoSession - return HanzoSession.get() - - -def _api_base() -> str: - """Get commerce API base URL (overridable via env).""" - return os.getenv("HANZO_COMMERCE_API_URL", API_BASE).rstrip("/") - - -def _request(method: str, path: str, token: str, **kwargs: Any) -> Any: - """Make an authenticated HTTP request to the commerce API.""" - import httpx - - url = f"{_api_base()}{path}" - headers = { - "Authorization": f"Bearer {token}", - "Content-Type": "application/json", - "User-Agent": "hanzo-mcp/0.1", - } - - with httpx.Client(timeout=30.0) as client: - resp = client.request(method, url, headers=headers, **kwargs) - if resp.status_code >= 400: - try: - err = resp.json() - msg = err.get("error", err.get("message", resp.text)) - except Exception: - msg = resp.text - raise RuntimeError(f"Commerce API error {resp.status_code}: {msg}") - if not resp.content or resp.status_code == 204: - return {} - return resp.json() - - -def _get(path: str, token: str, params: dict[str, str] | None = None) -> Any: - return _request("GET", path, token, params=params) - - -@final -class CommerceTool(BaseTool): - """MCP tool for Hanzo commerce operations.""" - - @property - def name(self) -> str: - return "commerce" - - @property - def description(self) -> str: - return DESCRIPTION - - def _get_token(self) -> str: - """Get auth token or raise.""" - session = _get_session() - token = session.get_iam_token() - if not token: - raise RuntimeError("Not authenticated. Run 'hanzo login' first.") - return token - - async def call( - self, - ctx: MCPContext, - action: str = "orders", - order_id: str | None = None, - product_id: str | None = None, - query: str | None = None, - **kwargs: Any, - ) -> str: - try: - if action == "orders": - return await self._orders(query) - elif action == "order": - return await self._order(order_id) - elif action == "products": - return await self._products(query) - elif action == "product": - return await self._product(product_id) - elif action == "collections": - return await self._collections() - elif action == "search_users": - return await self._search_users(query) - elif action == "search_orders": - return await self._search_orders(query) - elif action == "stores": - return await self._stores() - elif action == "discounts": - return await self._discounts() - elif action == "webhooks": - return await self._webhooks() - else: - return json.dumps({ - "error": f"Unknown action: {action}", - "available": [ - "orders", "order", "products", "product", "collections", - "search_users", "search_orders", "stores", "discounts", "webhooks", - ], - }) - except RuntimeError as e: - return json.dumps({"error": str(e)}) - except Exception as e: - logger.exception(f"Commerce tool error: {e}") - return json.dumps({"error": f"Commerce error: {e}"}) - - # -- Actions ------------------------------------------------------------- - - async def _orders(self, query: str | None) -> str: - token = self._get_token() - params = {"q": query} if query else None - data = _get("/orders", token, params=params) - return json.dumps(data, indent=2) - - async def _order(self, order_id: str | None) -> str: - if not order_id: - return json.dumps({"error": "Required: order_id"}) - token = self._get_token() - data = _get(f"/orders/{order_id}", token) - return json.dumps(data, indent=2) - - async def _products(self, query: str | None) -> str: - token = self._get_token() - params = {"q": query} if query else None - data = _get("/products", token, params=params) - return json.dumps(data, indent=2) - - async def _product(self, product_id: str | None) -> str: - if not product_id: - return json.dumps({"error": "Required: product_id"}) - token = self._get_token() - data = _get(f"/products/{product_id}", token) - return json.dumps(data, indent=2) - - async def _collections(self) -> str: - token = self._get_token() - data = _get("/collections", token) - return json.dumps(data, indent=2) - - async def _search_users(self, query: str | None) -> str: - if not query: - return json.dumps({"error": "Required: query (search term)"}) - token = self._get_token() - data = _get("/users", token, params={"q": query}) - return json.dumps(data, indent=2) - - async def _search_orders(self, query: str | None) -> str: - if not query: - return json.dumps({"error": "Required: query (search term)"}) - token = self._get_token() - data = _get("/orders", token, params={"q": query}) - return json.dumps(data, indent=2) - - async def _stores(self) -> str: - token = self._get_token() - data = _get("/stores", token) - return json.dumps(data, indent=2) - - async def _discounts(self) -> str: - token = self._get_token() - data = _get("/discounts", token) - return json.dumps(data, indent=2) - - async def _webhooks(self) -> str: - token = self._get_token() - data = _get("/webhooks", token) - return json.dumps(data, indent=2) - - # -- Registration -------------------------------------------------------- - - def register(self, mcp_server: FastMCP) -> None: - """Register commerce tool with explicit parameters.""" - tool_instance = self - - @mcp_server.tool( - name="commerce", - description=DESCRIPTION, - ) - async def commerce( - action: Annotated[ - str, - Field( - description=( - "Action to perform. " - "orders, order, products, product, collections, " - "search_users, search_orders, stores, discounts, webhooks." - ), - ), - ] = "orders", - order_id: Annotated[ - str | None, - Field(description="Order ID (for order action)"), - ] = None, - product_id: Annotated[ - str | None, - Field(description="Product ID (for product action)"), - ] = None, - query: Annotated[ - str | None, - Field(description="Search query (for search_users, search_orders, or filtering orders/products)"), - ] = None, - ctx: MCPContext = None, - ) -> str: - return await tool_instance.call( - ctx, - action=action, - order_id=order_id, - product_id=product_id, - query=query, - ) diff --git a/pkg/hanzo-tools-commerce/pyproject.toml b/pkg/hanzo-tools-commerce/pyproject.toml deleted file mode 100644 index f4c20f160..000000000 --- a/pkg/hanzo-tools-commerce/pyproject.toml +++ /dev/null @@ -1,32 +0,0 @@ -[project] -name = "hanzo-tools-commerce" -version = "0.1.0" -description = "Hanzo Commerce MCP tool -- orders, products, collections, stores, and discounts" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "mcp", "commerce", "orders", "products", "tools"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", -] - -dependencies = [ - "hanzo-tools-core>=0.1.0", - "hanzo-tools-auth>=0.1.0", - "httpx>=0.27.0", -] - -[project.entry-points."hanzo.tools"] -commerce = "hanzo_tools.commerce:TOOLS" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] diff --git a/pkg/hanzo-tools-computer/README.md b/pkg/hanzo-tools-computer/README.md deleted file mode 100644 index f17327147..000000000 --- a/pkg/hanzo-tools-computer/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# hanzo-tools-computer - -Computer control tools for Hanzo AI - pyautogui-based Mac automation. - -## Installation - -```bash -pip install hanzo-tools-computer -``` - -## Usage - -```python -from hanzo_tools.computer import ComputerTool, register_tools - -# Register with MCP server -register_tools(mcp_server, permission_manager) -``` - -## Actions - -### Mouse -- `click` - Click at (x, y) -- `double_click` - Double click at (x, y) -- `right_click` - Right click at (x, y) -- `move` - Move mouse to (x, y) -- `drag` - Drag to (x, y) -- `scroll` - Scroll by amount - -### Keyboard -- `type` - Type text string -- `press` - Press single key -- `hotkey` - Press key combination - -### Screen -- `screenshot` - Capture screen -- `locate` - Find image on screen -- `info` - Get screen/mouse info - -## Examples - -```python -# Click at coordinates -computer(action="click", x=100, y=200) - -# Type text -computer(action="type", text="Hello world") - -# Keyboard shortcut (Cmd+C on Mac) -computer(action="hotkey", keys=["command", "c"]) - -# Take screenshot -computer(action="screenshot") - -# Get screen info -computer(action="info") -``` - -## Safety - -- FAILSAFE enabled: Move mouse to corner to abort -- macOS only (checks platform) -- All actions run in executor threads (non-blocking) - -## License - -MIT diff --git a/pkg/hanzo-tools-computer/hanzo_tools/__init__.py b/pkg/hanzo-tools-computer/hanzo_tools/__init__.py deleted file mode 100644 index 946984951..000000000 --- a/pkg/hanzo-tools-computer/hanzo_tools/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Namespace package -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-computer/hanzo_tools/computer/__init__.py b/pkg/hanzo-tools-computer/hanzo_tools/computer/__init__.py deleted file mode 100644 index 5ddc806a2..000000000 --- a/pkg/hanzo-tools-computer/hanzo_tools/computer/__init__.py +++ /dev/null @@ -1,101 +0,0 @@ -"""UI control tools for Hanzo AI (HIP-0300). - -Tools: -- ui: Unified interface control (HIP-0300 compliant) - - Mouse: click, double_click, right_click, move, drag, scroll - - Touch: tap, swipe, pinch (mobile emulation) - - Keyboard: type, write, press, key_down, key_up, hotkey - - Screen capture: screenshot, screenshot_region, capture - - Screen recording: session, record, stop, analyze - - Window: focus_window, list_windows, get_active_window - - Screen info: get_screens, screen_size - -Cross-platform support: - - macOS: Quartz/CoreGraphics (10-50x faster than pyautogui) - - Linux: xdotool/scrot for X11 - - Windows: win32api via ctypes - - Fallback: pyautogui when native unavailable - -Install: - pip install hanzo-tools-computer - -Usage: - from hanzo_tools.computer import register_tools, TOOLS - - # Register with MCP server - register_tools(mcp_server, permission_manager) - - # Or access individual tool - from hanzo_tools.computer import UiTool - -Screen Limits (configurable via env vars): - HANZO_SCREEN_DURATION=30 # Default session duration (seconds) - HANZO_SCREEN_TARGET_FRAMES=30 # Target frames per session - HANZO_SCREEN_MAX_SIZE=768 # Max frame dimension (capped at 1568) - HANZO_SCREEN_QUALITY=60 # JPEG quality (1-100) -""" - -from hanzo_tools.core import BaseTool, ToolRegistry, PermissionManager - -# Backward compat -from .ui_tool import UiTool, ui_tool - -# Internal utilities (used by screen_tool) -from .media_tool import MediaLimits, MediaResult, ActivitySegment, media_tool -from .screen_tool import ScreenTool, ScreenConfig, screen_tool - -# HIP-0300: Single unified 'computer' tool -from .computer_tool import ComputerTool - -# Export list for tool discovery - single computer tool (HIP-0300) -TOOLS = [ComputerTool] - -__all__ = [ - # HIP-0300 unified tool - "ComputerTool", - # Backward compat - "UiTool", - "ui_tool", - "ScreenTool", - "ScreenConfig", - "screen_tool", - # Internal utilities - "MediaLimits", - "MediaResult", - "ActivitySegment", - "media_tool", - "register_tools", - "TOOLS", -] - - -def register_tools( - mcp_server, - permission_manager: PermissionManager, - enabled_tools: dict[str, bool] | None = None, -) -> list[BaseTool]: - """Register computer control tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - permission_manager: Permission manager for access control - enabled_tools: Dict of tool_name -> enabled state - - Returns: - List of registered tools - """ - enabled = enabled_tools or {} - registered = [] - - for tool_class in TOOLS: - tool_name = ( - tool_class.name - if hasattr(tool_class, "name") - else tool_class.__name__.lower() - ) - if enabled.get(tool_name, True): # Enabled by default - tool = tool_class(permission_manager) - ToolRegistry.register_tool(mcp_server, tool) - registered.append(tool) - - return registered diff --git a/pkg/hanzo-tools-computer/hanzo_tools/computer/computer_tool.py b/pkg/hanzo-tools-computer/hanzo_tools/computer/computer_tool.py deleted file mode 100644 index 2e2020ee8..000000000 --- a/pkg/hanzo-tools-computer/hanzo_tools/computer/computer_tool.py +++ /dev/null @@ -1,1720 +0,0 @@ -"""Unified computer control tool with native API acceleration. - -Cross-platform support: -- macOS: Direct Quartz/CoreGraphics APIs (fastest, ~10-50x faster than pyautogui) -- Linux: xdotool/scrot for X11 -- Windows: ctypes win32api - -Performance: -- Native path: <5ms click, <2ms keypress, <50ms screenshot -- Fallback to pyautogui when native APIs unavailable -- Zero-delay operations in native mode -- Batch operations with minimal overhead -""" - -import io -import os -import re -import sys -import json -import time -import base64 -import shutil -import asyncio -import tempfile -import subprocess -from typing import Any, Literal, Optional, Annotated, final, override -from pathlib import Path -from concurrent.futures import ThreadPoolExecutor - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, PermissionManager, auto_timeout - -# Platform detection -PLATFORM = sys.platform -IS_MACOS = PLATFORM == "darwin" -IS_LINUX = PLATFORM.startswith("linux") -IS_WINDOWS = PLATFORM == "win32" - -# macOS: Quartz/CoreGraphics -QUARTZ_AVAILABLE = False -if IS_MACOS: - try: - import AppKit - import Quartz - from Quartz import ( - CGEventPost, - CGMainDisplayID, - CGDisplayPixelsHigh, - CGDisplayPixelsWide, - CGEventCreateMouseEvent, - CGEventCreateKeyboardEvent, - CGEventCreateScrollWheelEvent, - kCGHIDEventTap, - kCGEventMouseMoved, - kCGMouseButtonLeft, - kCGEventLeftMouseUp, - kCGMouseButtonRight, - kCGEventOtherMouseUp, - kCGEventRightMouseUp, - kCGMouseButtonCenter, - kCGEventLeftMouseDown, - kCGEventOtherMouseDown, - kCGEventRightMouseDown, - kCGScrollEventUnitLine, - kCGEventLeftMouseDragged, - kCGEventOtherMouseDragged, - kCGEventRightMouseDragged, - ) - - QUARTZ_AVAILABLE = True - except ImportError: - QUARTZ_AVAILABLE = False - -# Linux: Check for X11 tools -XDOTOOL_AVAILABLE = False -SCROT_AVAILABLE = False -if IS_LINUX: - XDOTOOL_AVAILABLE = shutil.which("xdotool") is not None - SCROT_AVAILABLE = shutil.which("scrot") is not None - -# Windows: ctypes for native API -WIN32_AVAILABLE = False -if IS_WINDOWS: - try: - import ctypes - - WIN32_AVAILABLE = True - except ImportError: - WIN32_AVAILABLE = False - -# Check if we have any native backend -NATIVE_AVAILABLE = QUARTZ_AVAILABLE or XDOTOOL_AVAILABLE or WIN32_AVAILABLE - -# Shared thread pool -_EXECUTOR = ThreadPoolExecutor(max_workers=4, thread_name_prefix="computer_") - -# macOS key codes -KEY_CODES = { - "a": 0x00, - "s": 0x01, - "d": 0x02, - "f": 0x03, - "h": 0x04, - "g": 0x05, - "z": 0x06, - "x": 0x07, - "c": 0x08, - "v": 0x09, - "b": 0x0B, - "q": 0x0C, - "w": 0x0D, - "e": 0x0E, - "r": 0x0F, - "y": 0x10, - "t": 0x11, - "1": 0x12, - "2": 0x13, - "3": 0x14, - "4": 0x15, - "5": 0x17, - "6": 0x16, - "7": 0x1A, - "8": 0x1C, - "9": 0x19, - "0": 0x1D, - "-": 0x1B, - "=": 0x18, - "[": 0x21, - "]": 0x1E, - "\\": 0x2A, - ";": 0x29, - "'": 0x27, - "`": 0x32, - ",": 0x2B, - ".": 0x2F, - "/": 0x2C, - "o": 0x1F, - "u": 0x20, - "i": 0x22, - "p": 0x23, - "l": 0x25, - "j": 0x26, - "k": 0x28, - "n": 0x2D, - "m": 0x2E, - " ": 0x31, - "space": 0x31, - "return": 0x24, - "enter": 0x24, - "\n": 0x24, - "\r": 0x24, - "tab": 0x30, - "\t": 0x30, - "backspace": 0x33, - "\b": 0x33, - "escape": 0x35, - "esc": 0x35, - "command": 0x37, - "cmd": 0x37, - "shift": 0x38, - "shiftleft": 0x38, - "shiftright": 0x3C, - "capslock": 0x39, - "option": 0x3A, - "alt": 0x3A, - "optionleft": 0x3A, - "altleft": 0x3A, - "optionright": 0x3D, - "altright": 0x3D, - "control": 0x3B, - "ctrl": 0x3B, - "ctrlleft": 0x3B, - "ctrlright": 0x3E, - "fn": 0x3F, - "f1": 0x7A, - "f2": 0x78, - "f3": 0x63, - "f4": 0x76, - "f5": 0x60, - "f6": 0x61, - "f7": 0x62, - "f8": 0x64, - "f9": 0x65, - "f10": 0x6D, - "f11": 0x67, - "f12": 0x6F, - "home": 0x73, - "end": 0x77, - "pageup": 0x74, - "pagedown": 0x79, - "delete": 0x75, - "del": 0x75, - "left": 0x7B, - "right": 0x7C, - "down": 0x7D, - "up": 0x7E, -} - -SHIFT_CHARS = '~!@#$%^&*()_+{}|:"<>?ABCDEFGHIJKLMNOPQRSTUVWXYZ' - - -def _get_vk_code(key: str) -> int | None: - """Get Windows virtual key code.""" - VK_CODES = { - "a": 0x41, - "b": 0x42, - "c": 0x43, - "d": 0x44, - "e": 0x45, - "f": 0x46, - "g": 0x47, - "h": 0x48, - "i": 0x49, - "j": 0x4A, - "k": 0x4B, - "l": 0x4C, - "m": 0x4D, - "n": 0x4E, - "o": 0x4F, - "p": 0x50, - "q": 0x51, - "r": 0x52, - "s": 0x53, - "t": 0x54, - "u": 0x55, - "v": 0x56, - "w": 0x57, - "x": 0x58, - "y": 0x59, - "z": 0x5A, - "0": 0x30, - "1": 0x31, - "2": 0x32, - "3": 0x33, - "4": 0x34, - "5": 0x35, - "6": 0x36, - "7": 0x37, - "8": 0x38, - "9": 0x39, - "return": 0x0D, - "enter": 0x0D, - "tab": 0x09, - "space": 0x20, - "backspace": 0x08, - "escape": 0x1B, - "esc": 0x1B, - "shift": 0x10, - "ctrl": 0x11, - "control": 0x11, - "alt": 0x12, - "left": 0x25, - "up": 0x26, - "right": 0x27, - "down": 0x28, - "delete": 0x2E, - "home": 0x24, - "end": 0x23, - "pageup": 0x21, - "pagedown": 0x22, - "f1": 0x70, - "f2": 0x71, - "f3": 0x72, - "f4": 0x73, - "f5": 0x74, - "f6": 0x75, - "f7": 0x76, - "f8": 0x77, - "f9": 0x78, - "f10": 0x79, - "f11": 0x7A, - "f12": 0x7B, - } - return VK_CODES.get(key.lower()) - - -class NativeControl: - """Cross-platform native control - uses fastest available backend.""" - - @staticmethod - def get_platform_info() -> dict: - """Get platform capabilities.""" - return { - "platform": PLATFORM, - "native_available": NATIVE_AVAILABLE, - "backends": { - "quartz": QUARTZ_AVAILABLE, - "xdotool": XDOTOOL_AVAILABLE, - "scrot": SCROT_AVAILABLE, - "win32": WIN32_AVAILABLE, - }, - } - - @staticmethod - def mouse_position() -> tuple[int, int]: - """Get mouse position.""" - if IS_MACOS and QUARTZ_AVAILABLE: - loc = AppKit.NSEvent.mouseLocation() - return int(loc.x), int(CGDisplayPixelsHigh(0) - loc.y) - elif IS_LINUX and XDOTOOL_AVAILABLE: - result = subprocess.run( - ["xdotool", "getmouselocation", "--shell"], - capture_output=True, - text=True, - timeout=2, - ) - vals = dict( - line.split("=") - for line in result.stdout.strip().split("\n") - if "=" in line - ) - return int(vals.get("X", 0)), int(vals.get("Y", 0)) - elif IS_WINDOWS and WIN32_AVAILABLE: - - class POINT(ctypes.Structure): - _fields_ = [("x", ctypes.c_long), ("y", ctypes.c_long)] - - pt = POINT() - ctypes.windll.user32.GetCursorPos(ctypes.byref(pt)) - return pt.x, pt.y - else: - import pyautogui - - return pyautogui.position() - - @staticmethod - def screen_size() -> tuple[int, int]: - """Get screen size.""" - if IS_MACOS and QUARTZ_AVAILABLE: - return CGDisplayPixelsWide(CGMainDisplayID()), CGDisplayPixelsHigh( - CGMainDisplayID() - ) - elif IS_LINUX: - try: - result = subprocess.run( - ["xdpyinfo"], capture_output=True, text=True, timeout=2 - ) - for line in result.stdout.split("\n"): - if "dimensions:" in line: - dims = line.split()[1] - w, h = dims.split("x") - return int(w), int(h) - except Exception: - pass - return (1920, 1080) - elif IS_WINDOWS and WIN32_AVAILABLE: - return ( - ctypes.windll.user32.GetSystemMetrics(0), - ctypes.windll.user32.GetSystemMetrics(1), - ) - else: - import pyautogui - - return pyautogui.size() - - @staticmethod - def _send_mouse_event_macos( - event_type: int, x: int, y: int, button: int = 0 - ) -> None: - """Send mouse event via macOS Quartz.""" - event = CGEventCreateMouseEvent(None, event_type, (x, y), button) - CGEventPost(kCGHIDEventTap, event) - - @staticmethod - def click(x: int, y: int, button: str = "left") -> None: - """Click at position - native speed.""" - if IS_MACOS and QUARTZ_AVAILABLE: - if button == "left": - NativeControl._send_mouse_event_macos( - kCGEventLeftMouseDown, x, y, kCGMouseButtonLeft - ) - NativeControl._send_mouse_event_macos( - kCGEventLeftMouseUp, x, y, kCGMouseButtonLeft - ) - elif button == "right": - NativeControl._send_mouse_event_macos( - kCGEventRightMouseDown, x, y, kCGMouseButtonRight - ) - NativeControl._send_mouse_event_macos( - kCGEventRightMouseUp, x, y, kCGMouseButtonRight - ) - elif button == "middle": - NativeControl._send_mouse_event_macos( - kCGEventOtherMouseDown, x, y, kCGMouseButtonCenter - ) - NativeControl._send_mouse_event_macos( - kCGEventOtherMouseUp, x, y, kCGMouseButtonCenter - ) - elif IS_LINUX and XDOTOOL_AVAILABLE: - btn_map = {"left": "1", "middle": "2", "right": "3"} - subprocess.run( - [ - "xdotool", - "mousemove", - str(x), - str(y), - "click", - btn_map.get(button, "1"), - ], - capture_output=True, - timeout=2, - ) - elif IS_WINDOWS and WIN32_AVAILABLE: - ctypes.windll.user32.SetCursorPos(x, y) - if button == "left": - ctypes.windll.user32.mouse_event(0x0002, 0, 0, 0, 0) - ctypes.windll.user32.mouse_event(0x0004, 0, 0, 0, 0) - elif button == "right": - ctypes.windll.user32.mouse_event(0x0008, 0, 0, 0, 0) - ctypes.windll.user32.mouse_event(0x0010, 0, 0, 0, 0) - elif button == "middle": - ctypes.windll.user32.mouse_event(0x0020, 0, 0, 0, 0) - ctypes.windll.user32.mouse_event(0x0040, 0, 0, 0, 0) - else: - import pyautogui - - pyautogui.click(x, y, button=button) - - @staticmethod - def double_click(x: int, y: int) -> None: - """Double click.""" - if IS_LINUX and XDOTOOL_AVAILABLE: - subprocess.run( - ["xdotool", "mousemove", str(x), str(y), "click", "--repeat", "2", "1"], - capture_output=True, - timeout=2, - ) - else: - NativeControl.click(x, y) - time.sleep(0.01) - NativeControl.click(x, y) - - @staticmethod - def move(x: int, y: int) -> None: - """Move mouse.""" - if IS_MACOS and QUARTZ_AVAILABLE: - NativeControl._send_mouse_event_macos(kCGEventMouseMoved, x, y, 0) - elif IS_LINUX and XDOTOOL_AVAILABLE: - subprocess.run( - ["xdotool", "mousemove", str(x), str(y)], capture_output=True, timeout=2 - ) - elif IS_WINDOWS and WIN32_AVAILABLE: - ctypes.windll.user32.SetCursorPos(x, y) - else: - import pyautogui - - pyautogui.moveTo(x, y, _pause=False) - - @staticmethod - def drag( - start_x: int, start_y: int, end_x: int, end_y: int, button: str = "left" - ) -> None: - """Drag from start to end.""" - if IS_MACOS and QUARTZ_AVAILABLE: - if button == "left": - NativeControl._send_mouse_event_macos( - kCGEventLeftMouseDown, start_x, start_y, kCGMouseButtonLeft - ) - drag_type = kCGEventLeftMouseDragged - up_type = kCGEventLeftMouseUp - btn = kCGMouseButtonLeft - elif button == "right": - NativeControl._send_mouse_event_macos( - kCGEventRightMouseDown, start_x, start_y, kCGMouseButtonRight - ) - drag_type = kCGEventRightMouseDragged - up_type = kCGEventRightMouseUp - btn = kCGMouseButtonRight - else: - NativeControl._send_mouse_event_macos( - kCGEventOtherMouseDown, start_x, start_y, kCGMouseButtonCenter - ) - drag_type = kCGEventOtherMouseDragged - up_type = kCGEventOtherMouseUp - btn = kCGMouseButtonCenter - - steps = max(abs(end_x - start_x), abs(end_y - start_y)) // 10 or 1 - for i in range(1, steps + 1): - cx = start_x + (end_x - start_x) * i // steps - cy = start_y + (end_y - start_y) * i // steps - NativeControl._send_mouse_event_macos(drag_type, cx, cy, btn) - time.sleep(0.001) - NativeControl._send_mouse_event_macos(up_type, end_x, end_y, btn) - elif IS_LINUX and XDOTOOL_AVAILABLE: - btn_map = {"left": "1", "middle": "2", "right": "3"} - subprocess.run( - [ - "xdotool", - "mousemove", - str(start_x), - str(start_y), - "mousedown", - btn_map.get(button, "1"), - "mousemove", - str(end_x), - str(end_y), - "mouseup", - btn_map.get(button, "1"), - ], - capture_output=True, - timeout=5, - ) - elif IS_WINDOWS and WIN32_AVAILABLE: - ctypes.windll.user32.SetCursorPos(start_x, start_y) - ctypes.windll.user32.mouse_event(0x0002, 0, 0, 0, 0) - steps = max(abs(end_x - start_x), abs(end_y - start_y)) // 10 or 1 - for i in range(1, steps + 1): - cx = start_x + (end_x - start_x) * i // steps - cy = start_y + (end_y - start_y) * i // steps - ctypes.windll.user32.SetCursorPos(cx, cy) - time.sleep(0.001) - ctypes.windll.user32.mouse_event(0x0004, 0, 0, 0, 0) - else: - import pyautogui - - pyautogui.moveTo(start_x, start_y, _pause=False) - pyautogui.drag(end_x - start_x, end_y - start_y, _pause=False) - - @staticmethod - def scroll(amount: int, x: int | None = None, y: int | None = None) -> None: - """Scroll.""" - if x is not None and y is not None: - NativeControl.move(x, y) - - if IS_MACOS and QUARTZ_AVAILABLE: - event = CGEventCreateScrollWheelEvent( - None, kCGScrollEventUnitLine, 1, amount - ) - CGEventPost(kCGHIDEventTap, event) - elif IS_LINUX and XDOTOOL_AVAILABLE: - btn = "4" if amount > 0 else "5" - for _ in range(abs(amount)): - subprocess.run( - ["xdotool", "click", btn], capture_output=True, timeout=2 - ) - elif IS_WINDOWS and WIN32_AVAILABLE: - ctypes.windll.user32.mouse_event(0x0800, 0, 0, amount * 120, 0) - else: - import pyautogui - - pyautogui.scroll(amount, _pause=False) - - @staticmethod - def _send_key_event_macos(key_code: int, down: bool) -> None: - """Send key event via macOS Quartz.""" - event = CGEventCreateKeyboardEvent(None, key_code, down) - CGEventPost(kCGHIDEventTap, event) - - @staticmethod - def key_down(key: str) -> None: - """Press key down.""" - key_lower = key.lower() - if IS_MACOS and QUARTZ_AVAILABLE: - if key_lower in KEY_CODES: - NativeControl._send_key_event_macos(KEY_CODES[key_lower], True) - elif IS_LINUX and XDOTOOL_AVAILABLE: - subprocess.run( - ["xdotool", "keydown", key_lower], capture_output=True, timeout=2 - ) - elif IS_WINDOWS and WIN32_AVAILABLE: - vk = _get_vk_code(key_lower) - if vk: - ctypes.windll.user32.keybd_event(vk, 0, 0, 0) - else: - import pyautogui - - pyautogui.keyDown(key_lower, _pause=False) - - @staticmethod - def key_up(key: str) -> None: - """Release key.""" - key_lower = key.lower() - if IS_MACOS and QUARTZ_AVAILABLE: - if key_lower in KEY_CODES: - NativeControl._send_key_event_macos(KEY_CODES[key_lower], False) - elif IS_LINUX and XDOTOOL_AVAILABLE: - subprocess.run( - ["xdotool", "keyup", key_lower], capture_output=True, timeout=2 - ) - elif IS_WINDOWS and WIN32_AVAILABLE: - vk = _get_vk_code(key_lower) - if vk: - ctypes.windll.user32.keybd_event(vk, 0, 0x0002, 0) - else: - import pyautogui - - pyautogui.keyUp(key_lower, _pause=False) - - @staticmethod - def press(key: str) -> None: - """Press and release key.""" - if IS_LINUX and XDOTOOL_AVAILABLE: - subprocess.run( - ["xdotool", "key", key.lower()], capture_output=True, timeout=2 - ) - else: - NativeControl.key_down(key) - NativeControl.key_up(key) - - @staticmethod - def hotkey(*keys: str) -> None: - """Press key combination.""" - if IS_LINUX and XDOTOOL_AVAILABLE: - combo = "+".join(keys) - subprocess.run(["xdotool", "key", combo], capture_output=True, timeout=2) - else: - for key in keys: - NativeControl.key_down(key) - for key in reversed(keys): - NativeControl.key_up(key) - - @staticmethod - def type_char(char: str) -> None: - """Type a single character.""" - if IS_LINUX and XDOTOOL_AVAILABLE: - subprocess.run( - ["xdotool", "type", "--", char], capture_output=True, timeout=2 - ) - elif IS_MACOS and QUARTZ_AVAILABLE: - if char in SHIFT_CHARS: - NativeControl.key_down("shift") - key = char.lower() if char.isalpha() else char - if key in KEY_CODES: - NativeControl.press(key) - NativeControl.key_up("shift") - elif char.lower() in KEY_CODES: - NativeControl.press(char.lower()) - else: - import pyautogui - - pyautogui.typewrite(char, _pause=False) - - @staticmethod - def type_text(text: str, interval: float = 0) -> None: - """Type text.""" - if IS_LINUX and XDOTOOL_AVAILABLE: - if interval > 0: - subprocess.run( - [ - "xdotool", - "type", - "--delay", - str(int(interval * 1000)), - "--", - text, - ], - capture_output=True, - timeout=30, - ) - else: - subprocess.run( - ["xdotool", "type", "--", text], capture_output=True, timeout=30 - ) - else: - for char in text: - NativeControl.type_char(char) - if interval > 0: - time.sleep(interval) - - @staticmethod - def screenshot_native(region: list[int] | None = None) -> bytes: - """Screenshot using native tools - fastest method.""" - with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f: - tmp_path = f.name - - try: - if IS_MACOS: - cmd = ["screencapture", "-x", "-t", "png"] - if region and len(region) == 4: - x, y, w, h = region - cmd.extend(["-R", f"{x},{y},{w},{h}"]) - cmd.append(tmp_path) - subprocess.run(cmd, capture_output=True, timeout=5) - - elif IS_LINUX and SCROT_AVAILABLE: - cmd = ["scrot", "-o", tmp_path] - if region and len(region) == 4: - x, y, w, h = region - cmd = ["scrot", "-a", f"{x},{y},{w},{h}", "-o", tmp_path] - subprocess.run(cmd, capture_output=True, timeout=5) - - elif IS_WINDOWS or IS_LINUX: - from PIL import ImageGrab - - if region and len(region) == 4: - x, y, w, h = region - img = ImageGrab.grab(bbox=(x, y, x + w, y + h)) - else: - img = ImageGrab.grab() - img.save(tmp_path, "PNG") - - if os.path.exists(tmp_path) and os.path.getsize(tmp_path) > 0: - with open(tmp_path, "rb") as f: - return f.read() - return b"" - finally: - if os.path.exists(tmp_path): - os.unlink(tmp_path) - - @staticmethod - def focus_window(title: str) -> bool: - """Focus window by app/window name. Supports partial matching on macOS.""" - if IS_MACOS: - # First try direct app activation - script = f'tell application "{title}" to activate' - try: - result = subprocess.run( - ["osascript", "-e", script], capture_output=True, timeout=10 - ) - if result.returncode == 0: - return True - except subprocess.TimeoutExpired: - pass - - # If that fails, try to find app by partial name match - search_script = f""" - tell application "System Events" - set matchingApps to (application processes whose name contains "{title}") - if (count of matchingApps) > 0 then - set frontApp to item 1 of matchingApps - set frontmost of frontApp to true - return name of frontApp - end if - end tell - return "" - """ - try: - result = subprocess.run( - ["osascript", "-e", search_script], - capture_output=True, - text=True, - timeout=10, - ) - if result.returncode == 0 and result.stdout.strip(): - return True - except subprocess.TimeoutExpired: - pass - return False - elif IS_LINUX and XDOTOOL_AVAILABLE: - result = subprocess.run( - ["xdotool", "search", "--name", title, "windowactivate"], - capture_output=True, - timeout=10, - ) - return result.returncode == 0 - elif IS_WINDOWS and WIN32_AVAILABLE: - hwnd = ctypes.windll.user32.FindWindowW(None, title) - if hwnd: - ctypes.windll.user32.SetForegroundWindow(hwnd) - return True - return False - return False - - @staticmethod - def get_active_window() -> dict: - """Get active window info.""" - if IS_MACOS: - script = """ - tell application "System Events" - set frontApp to first application process whose frontmost is true - set appName to name of frontApp - try - set frontWindow to front window of frontApp - set winName to name of frontWindow - set winPos to position of frontWindow - set winSize to size of frontWindow - return appName & "|" & winName & "|" & (item 1 of winPos) & "|" & (item 2 of winPos) & "|" & (item 1 of winSize) & "|" & (item 2 of winSize) - on error - return appName & "|" & "" & "|0|0|0|0" - end try - end tell - """ - result = subprocess.run( - ["osascript", "-e", script], capture_output=True, text=True, timeout=5 - ) - if result.returncode == 0: - parts = result.stdout.strip().split("|") - if len(parts) >= 6: - return { - "app": parts[0], - "title": parts[1], - "x": int(parts[2]), - "y": int(parts[3]), - "width": int(parts[4]), - "height": int(parts[5]), - } - elif IS_LINUX and XDOTOOL_AVAILABLE: - result = subprocess.run( - ["xdotool", "getactivewindow", "getwindowname"], - capture_output=True, - text=True, - timeout=5, - ) - if result.returncode == 0: - return {"title": result.stdout.strip()} - elif IS_WINDOWS and WIN32_AVAILABLE: - hwnd = ctypes.windll.user32.GetForegroundWindow() - length = ctypes.windll.user32.GetWindowTextLengthW(hwnd) - buf = ctypes.create_unicode_buffer(length + 1) - ctypes.windll.user32.GetWindowTextW(hwnd, buf, length + 1) - return {"title": buf.value, "hwnd": hwnd} - return {"error": "Could not get active window"} - - @staticmethod - def list_windows() -> list[dict]: - """List all windows.""" - if IS_MACOS: - # Use \x1f (unit separator) as delimiter - unlikely to appear in window titles - script = """ - set windowList to "" - set delim to ASCII character 31 - tell application "System Events" - set allProcesses to application processes whose visible is true - repeat with proc in allProcesses - set procName to name of proc - try - set procWindows to windows of proc - repeat with win in procWindows - set winName to name of win - set winPos to position of win - set winSize to size of win - set windowList to windowList & procName & delim & winName & delim & (item 1 of winPos) & delim & (item 2 of winPos) & delim & (item 1 of winSize) & delim & (item 2 of winSize) & "\\n" - end repeat - end try - end repeat - end tell - return windowList - """ - result = subprocess.run( - ["osascript", "-e", script], capture_output=True, text=True, timeout=10 - ) - if result.returncode == 0: - windows = [] - for line in result.stdout.strip().split("\n"): - if "\x1f" in line: - parts = line.split("\x1f") - if len(parts) >= 6: - try: - windows.append( - { - "app": parts[0], - "title": parts[1], - "x": int(parts[2]), - "y": int(parts[3]), - "width": int(parts[4]), - "height": int(parts[5]), - } - ) - except (ValueError, IndexError): - # Skip windows with parsing errors - continue - return windows - return [] - - -Action = Literal[ - # Mouse - "click", - "double_click", - "right_click", - "middle_click", - "move", - "move_relative", - "drag", - "drag_relative", - "scroll", - # Touch (mobile emulation) - "tap", - "swipe", - "pinch", - # Keyboard - "type", - "write", - "press", - "key_down", - "key_up", - "hotkey", - # Screen capture - "screenshot", - "screenshot_region", - "capture", - # Screen recording (consolidated from ScreenTool) - "session", # ONE-SHOT: record โ†’ analyze โ†’ compress โ†’ return for Claude - "record", # Start background recording - "stop", # Stop recording and get compressed frames - "analyze", # Analyze existing video file - # Image location - "locate", - "locate_all", - "locate_center", - "wait_for_image", - "wait_while_image", - # Pixel - "pixel", - "pixel_matches", - # Window - "get_active_window", - "list_windows", - "focus_window", - # Screen info - "get_screens", - "screen_size", - "current_screen", - # Region helpers - "define_region", - "region_screenshot", - "region_locate", - # Timing - "sleep", - "countdown", - "set_pause", - "set_failsafe", - # Batch - "batch", - # Info - "info", - "position", - "status", -] - - -@final -class ComputerTool(BaseTool): - """Unified computer control with native API acceleration. - - Cross-platform support: - - macOS: Quartz/CoreGraphics APIs (~10-50x faster) - - Linux: xdotool/scrot for X11 - - Windows: win32api via ctypes - - Fallback: pyautogui when native unavailable - - Performance (native mode): - - Click: <5ms - - Keypress: <2ms - - Screenshot: <50ms - """ - - name = "computer" - - def __init__(self, permission_manager: Optional[PermissionManager] = None): - if permission_manager is None: - permission_manager = PermissionManager() - self.permission_manager = permission_manager - self._pyautogui = None - self._pil = None - self._defined_regions: dict[str, tuple[int, int, int, int]] = {} - self._pause = 0.1 - self._failsafe = True - - def _ensure_pyautogui(self): - """Lazy load pyautogui for fallback/advanced features.""" - if self._pyautogui is None: - import pyautogui - - pyautogui.FAILSAFE = self._failsafe - pyautogui.PAUSE = self._pause - self._pyautogui = pyautogui - return self._pyautogui - - def _ensure_pil(self): - """Lazy load PIL.""" - if self._pil is None: - from PIL import Image - - self._pil = Image - return self._pil - - @property - @override - def description(self) -> str: - platform_info = NativeControl.get_platform_info() - backends = ( - ", ".join(k for k, v in platform_info["backends"].items() if v) - or "pyautogui" - ) - return f"""Control local computer with native API acceleration. - -PLATFORM: {platform_info["platform"]} -BACKENDS: {backends} - -MOUSE (< 5ms native): -- click(x, y) / double_click / right_click / middle_click -- move(x, y) / move_relative(dx, dy) -- drag(x, y) / drag_relative(dx, dy) -- scroll(amount, x, y) - -KEYBOARD (< 2ms native): -- type(text, interval): Type text -- write(text, clear): Type with optional clear -- press(key): Press and release key -- key_down(key) / key_up(key): Hold/release -- hotkey(keys): Key combination ["command", "c"] - -SCREEN (< 50ms native): -- screenshot() / screenshot_region(region) -- get_screens(): List displays -- screen_size() / current_screen() - -IMAGE LOCATION: -- locate(image_path): Find image, return center -- locate_all(image_path): Find all matches -- wait_for_image(image_path, timeout) -- wait_while_image(image_path, timeout) - -PIXEL: -- pixel(x, y): Get color at point -- pixel_matches(x, y, color, tolerance) - -WINDOWS: -- get_active_window(): Frontmost window info -- list_windows(): All windows with bounds -- focus_window(title): Activate window - -REGIONS: -- define_region(name, x, y, w, h): Name a region -- region_screenshot(name): Screenshot region -- region_locate(name, image): Find in region - -TIMING: -- sleep(seconds) / countdown(seconds) -- set_pause(seconds) / set_failsafe(enabled) - -BATCH: -- batch(actions): Execute multiple actions - -INFO: -- info() / position() - -Examples: - computer(action="click", x=100, y=200) - computer(action="type", text="Hello") - computer(action="hotkey", keys=["command", "c"]) - computer(action="screenshot") - computer(action="batch", actions=[ - {{"action": "click", "x": 100, "y": 200}}, - {{"action": "type", "text": "test"}} - ]) -""" - - @override - @auto_timeout("computer") - async def call( - self, - ctx: MCPContext, - action: str = "info", - # Coordinates - x: int | None = None, - y: int | None = None, - dx: int | None = None, - dy: int | None = None, - end_x: int | None = None, - end_y: int | None = None, - # Text/keys - text: str | None = None, - key: str | None = None, - keys: list[str] | None = None, - # Options - button: str = "left", - amount: int | None = None, - duration: float = 0.25, - interval: float = 0.02, - region: list[int] | None = None, - clear: bool = False, - # Image location - image_path: str | None = None, - confidence: float = 0.9, - timeout: float = 10.0, - # Pixel matching - color: tuple[int, int, int] | list[int] | None = None, - tolerance: int = 0, - # Window - title: str | None = None, - use_regex: bool = False, - # Regions - name: str | None = None, - width: int | None = None, - height: int | None = None, - # Settings - value: float | bool | None = None, - # Batch - actions: list[dict[str, Any]] | None = None, - **kwargs, - ) -> str: - """Execute computer control action.""" - loop = asyncio.get_event_loop() - - def run(fn, *args): - return loop.run_in_executor(_EXECUTOR, fn, *args) - - try: - # Mouse actions - use native when available - if action == "click": - if x is None or y is None: - return json.dumps({"error": "x and y required"}) - await run(NativeControl.click, x, y, button) - return json.dumps( - {"success": True, "clicked": [x, y], "button": button} - ) - - elif action == "double_click": - if x is None or y is None: - return json.dumps({"error": "x and y required"}) - await run(NativeControl.double_click, x, y) - return json.dumps({"success": True, "double_clicked": [x, y]}) - - elif action == "right_click": - if x is None or y is None: - return json.dumps({"error": "x and y required"}) - await run(NativeControl.click, x, y, "right") - return json.dumps({"success": True, "right_clicked": [x, y]}) - - elif action == "middle_click": - if x is None or y is None: - return json.dumps({"error": "x and y required"}) - await run(NativeControl.click, x, y, "middle") - return json.dumps({"success": True, "middle_clicked": [x, y]}) - - elif action == "move": - if x is None or y is None: - return json.dumps({"error": "x and y required"}) - await run(NativeControl.move, x, y) - return json.dumps({"success": True, "moved_to": [x, y]}) - - elif action == "move_relative": - if dx is None or dy is None: - return json.dumps({"error": "dx and dy required"}) - pos = NativeControl.mouse_position() - await run(NativeControl.move, pos[0] + dx, pos[1] + dy) - return json.dumps({"success": True, "moved_by": [dx, dy]}) - - elif action == "drag": - if x is None or y is None: - return json.dumps({"error": "x and y required for drag target"}) - start = NativeControl.mouse_position() - target_x = end_x if end_x is not None else x - target_y = end_y if end_y is not None else y - await run( - NativeControl.drag, start[0], start[1], target_x, target_y, button - ) - return json.dumps({"success": True, "dragged_to": [target_x, target_y]}) - - elif action == "drag_relative": - if dx is None or dy is None: - return json.dumps({"error": "dx and dy required"}) - pos = NativeControl.mouse_position() - await run( - NativeControl.drag, pos[0], pos[1], pos[0] + dx, pos[1] + dy, button - ) - return json.dumps({"success": True, "dragged_by": [dx, dy]}) - - elif action == "scroll": - if amount is None: - return json.dumps({"error": "amount required"}) - await run(NativeControl.scroll, amount, x, y) - return json.dumps({"success": True, "scrolled": amount}) - - # Keyboard actions - elif action == "type": - if not text: - return json.dumps({"error": "text required"}) - await run(NativeControl.type_text, text, interval) - return json.dumps({"success": True, "typed": len(text)}) - - elif action == "write": - if not text: - return json.dumps({"error": "text required"}) - if clear: - await run(NativeControl.hotkey, "command", "a") - await asyncio.sleep(0.05) - await run(NativeControl.type_text, text, interval) - return json.dumps( - {"success": True, "wrote": len(text), "cleared": clear} - ) - - elif action == "press": - if not key: - return json.dumps({"error": "key required"}) - await run(NativeControl.press, key) - return json.dumps({"success": True, "pressed": key}) - - elif action == "key_down": - if not key: - return json.dumps({"error": "key required"}) - await run(NativeControl.key_down, key) - return json.dumps({"success": True, "key_down": key}) - - elif action == "key_up": - if not key: - return json.dumps({"error": "key required"}) - await run(NativeControl.key_up, key) - return json.dumps({"success": True, "key_up": key}) - - elif action == "hotkey": - if not keys: - return json.dumps({"error": "keys required"}) - await run(NativeControl.hotkey, *keys) - return json.dumps({"success": True, "hotkey": "+".join(keys)}) - - # Screen actions - elif action == "screenshot" or action == "screenshot_region": - data = await run(NativeControl.screenshot_native, region) - - # If name is provided, save to file instead of returning base64 - if name: - # Expand ~ and make absolute path - file_path = os.path.expanduser(name) - if not os.path.isabs(file_path): - file_path = os.path.join(tempfile.gettempdir(), name) - if not file_path.endswith(".png"): - file_path += ".png" - - with open(file_path, "wb") as f: - f.write(data) - - return json.dumps( - { - "success": True, - "format": "png", - "size": len(data), - "path": file_path, - } - ) - - # Otherwise return base64 (large output warning) - b64 = base64.b64encode(data).decode() - return json.dumps( - { - "success": True, - "format": "png", - "size": len(data), - "base64": b64, - } - ) - - # Image location (uses pyautogui) - elif action == "locate": - if not image_path and not text: - return json.dumps({"error": "image_path required"}) - path = image_path or text - result = await run(self._locate, path, confidence, None) - return result - - elif action == "locate_all": - if not image_path and not text: - return json.dumps({"error": "image_path required"}) - path = image_path or text - result = await run(self._locate_all, path, confidence) - return result - - elif action == "locate_center": - if not image_path and not text: - return json.dumps({"error": "image_path required"}) - path = image_path or text - result = await run(self._locate_center, path, confidence) - return result - - elif action == "wait_for_image": - if not image_path and not text: - return json.dumps({"error": "image_path required"}) - path = image_path or text - return await self._wait_for_image(path, timeout, confidence, run) - - elif action == "wait_while_image": - if not image_path and not text: - return json.dumps({"error": "image_path required"}) - path = image_path or text - return await self._wait_while_image(path, timeout, confidence, run) - - # Pixel operations - elif action == "pixel": - if x is None or y is None: - return json.dumps({"error": "x and y required"}) - result = await run(self._get_pixel, x, y) - return result - - elif action == "pixel_matches": - if x is None or y is None: - return json.dumps({"error": "x and y required"}) - if color is None: - return json.dumps({"error": "color required"}) - result = await run(self._pixel_matches, x, y, tuple(color), tolerance) - return result - - # Window management - elif action == "get_active_window": - result = await run(NativeControl.get_active_window) - return json.dumps(result) - - elif action == "list_windows": - result = await run(NativeControl.list_windows) - return json.dumps({"windows": result, "count": len(result)}) - - elif action == "focus_window": - if not title and not text: - return json.dumps({"error": "title required"}) - win_title = title or text - success = await run(NativeControl.focus_window, win_title) - return json.dumps({"success": success, "focused": win_title}) - - # Screen info - elif action == "get_screens": - result = await run(self._get_screens) - return result - - elif action == "screen_size": - size = NativeControl.screen_size() - return json.dumps({"width": size[0], "height": size[1]}) - - elif action == "current_screen": - pos = NativeControl.mouse_position() - size = NativeControl.screen_size() - return json.dumps( - { - "size": {"width": size[0], "height": size[1]}, - "mouse": {"x": pos[0], "y": pos[1]}, - } - ) - - # Region helpers - elif action == "define_region": - if not name: - return json.dumps({"error": "name required"}) - if x is None or y is None or width is None or height is None: - return json.dumps({"error": "x, y, width, height required"}) - self._defined_regions[name] = (x, y, width, height) - return json.dumps( - {"success": True, "defined": name, "region": [x, y, width, height]} - ) - - elif action == "region_screenshot": - if not name: - return json.dumps({"error": "name required"}) - if name not in self._defined_regions: - return json.dumps({"error": f"Region '{name}' not defined"}) - reg = list(self._defined_regions[name]) - data = await run(NativeControl.screenshot_native, reg) - b64 = base64.b64encode(data).decode() - return json.dumps( - {"success": True, "format": "png", "size": len(data), "base64": b64} - ) - - elif action == "region_locate": - if not name: - return json.dumps({"error": "name required"}) - if not image_path and not text: - return json.dumps({"error": "image_path required"}) - if name not in self._defined_regions: - return json.dumps({"error": f"Region '{name}' not defined"}) - path = image_path or text - reg = self._defined_regions[name] - result = await run(self._locate, path, confidence, reg) - return result - - # Timing - elif action == "sleep": - if value is None: - return json.dumps({"error": "value required"}) - await asyncio.sleep(float(value)) - return json.dumps({"success": True, "slept": value}) - - elif action == "countdown": - if value is None: - return json.dumps({"error": "value required"}) - for i in range(int(value), 0, -1): - await asyncio.sleep(1) - return json.dumps({"success": True, "countdown": value}) - - elif action == "set_pause": - if value is None: - return json.dumps({"error": "value required"}) - self._pause = float(value) - if self._pyautogui: - self._pyautogui.PAUSE = self._pause - return json.dumps({"success": True, "pause": self._pause}) - - elif action == "set_failsafe": - if value is None: - return json.dumps({"error": "value required"}) - self._failsafe = bool(value) - if self._pyautogui: - self._pyautogui.FAILSAFE = self._failsafe - return json.dumps({"success": True, "failsafe": self._failsafe}) - - # Batch operations - elif action == "batch": - if not actions: - return json.dumps({"error": "actions required"}) - - results = [] - start = time.time() - - for i, act in enumerate(actions): - act_type = act.get("action", "") - try: - if act_type == "click": - NativeControl.click( - act.get("x", 0), - act.get("y", 0), - act.get("button", "left"), - ) - elif act_type == "type": - NativeControl.type_text( - act.get("text", ""), act.get("interval", 0) - ) - elif act_type == "press": - NativeControl.press(act.get("key", "")) - elif act_type == "hotkey": - NativeControl.hotkey(*act.get("keys", [])) - elif act_type == "move": - NativeControl.move(act.get("x", 0), act.get("y", 0)) - elif act_type == "scroll": - NativeControl.scroll( - act.get("amount", 0), act.get("x"), act.get("y") - ) - elif act_type == "sleep": - time.sleep(act.get("ms", 0) / 1000) - else: - results.append( - {"index": i, "action": act_type, "error": "unknown"} - ) - continue - results.append( - {"index": i, "action": act_type, "success": True} - ) - except Exception as e: - results.append( - {"index": i, "action": act_type, "error": str(e)} - ) - - elapsed = time.time() - start - return json.dumps( - { - "success": True, - "count": len(results), - "elapsed_ms": round(elapsed * 1000, 2), - "results": results, - } - ) - - # Info - elif action == "info": - pos = NativeControl.mouse_position() - size = NativeControl.screen_size() - platform_info = NativeControl.get_platform_info() - return json.dumps( - { - "screen": {"width": size[0], "height": size[1]}, - "mouse": {"x": pos[0], "y": pos[1]}, - "platform": platform_info, - "pause": self._pause, - "failsafe": self._failsafe, - "regions": list(self._defined_regions.keys()), - } - ) - - elif action == "position": - pos = NativeControl.mouse_position() - return json.dumps({"x": pos[0], "y": pos[1]}) - - else: - return json.dumps({"error": f"Unknown action: {action}"}) - - except Exception as e: - return json.dumps({"error": str(e)}) - - # Helper methods for pyautogui-only features - - def _locate(self, image_path: str, confidence: float, region: tuple | None) -> str: - pg = self._ensure_pyautogui() - path = Path(image_path) - if not path.exists(): - return json.dumps({"error": f"Image not found: {image_path}"}) - try: - kwargs: dict[str, Any] = {} - if confidence < 1.0: - kwargs["confidence"] = confidence - if region: - kwargs["region"] = region - location = pg.locateOnScreen(str(path), **kwargs) - if location: - center = pg.center(location) - return json.dumps( - { - "found": True, - "center": {"x": center.x, "y": center.y}, - "box": { - "left": location.left, - "top": location.top, - "width": location.width, - "height": location.height, - }, - } - ) - return json.dumps({"found": False}) - except Exception as e: - return json.dumps({"error": str(e)}) - - def _locate_all(self, image_path: str, confidence: float) -> str: - pg = self._ensure_pyautogui() - path = Path(image_path) - if not path.exists(): - return json.dumps({"error": f"Image not found: {image_path}"}) - try: - kwargs: dict[str, Any] = {} - if confidence < 1.0: - kwargs["confidence"] = confidence - locations = list(pg.locateAllOnScreen(str(path), **kwargs)) - results = [] - for loc in locations: - center = pg.center(loc) - results.append( - { - "center": {"x": center.x, "y": center.y}, - "box": { - "left": loc.left, - "top": loc.top, - "width": loc.width, - "height": loc.height, - }, - } - ) - return json.dumps({"found": len(results), "locations": results}) - except Exception as e: - return json.dumps({"error": str(e)}) - - def _locate_center(self, image_path: str, confidence: float) -> str: - pg = self._ensure_pyautogui() - path = Path(image_path) - if not path.exists(): - return json.dumps({"error": f"Image not found: {image_path}"}) - try: - kwargs: dict[str, Any] = {} - if confidence < 1.0: - kwargs["confidence"] = confidence - center = pg.locateCenterOnScreen(str(path), **kwargs) - if center: - return json.dumps({"found": True, "x": center.x, "y": center.y}) - return json.dumps({"found": False}) - except Exception as e: - return json.dumps({"error": str(e)}) - - async def _wait_for_image( - self, image_path: str, timeout: float, confidence: float, run - ) -> str: - path = Path(image_path) - if not path.exists(): - return json.dumps({"error": f"Image not found: {image_path}"}) - start = time.time() - while time.time() - start < timeout: - result = await run(self._locate_center, str(path), confidence) - data = json.loads(result) if result.startswith("{") else {} - if data.get("found"): - return json.dumps( - { - "found": True, - "x": data["x"], - "y": data["y"], - "elapsed": round(time.time() - start, 2), - } - ) - await asyncio.sleep(0.1) - return json.dumps({"found": False, "timeout": timeout}) - - async def _wait_while_image( - self, image_path: str, timeout: float, confidence: float, run - ) -> str: - path = Path(image_path) - if not path.exists(): - return json.dumps({"error": f"Image not found: {image_path}"}) - start = time.time() - while time.time() - start < timeout: - result = await run(self._locate_center, str(path), confidence) - data = json.loads(result) if result.startswith("{") else {} - if not data.get("found"): - return json.dumps( - {"disappeared": True, "elapsed": round(time.time() - start, 2)} - ) - await asyncio.sleep(0.1) - return json.dumps( - {"disappeared": False, "timeout": timeout, "still_visible": True} - ) - - def _get_pixel(self, x: int, y: int) -> str: - pg = self._ensure_pyautogui() - try: - screenshot = pg.screenshot(region=(x, y, 1, 1)) - pixel = screenshot.getpixel((0, 0)) - return json.dumps( - {"x": x, "y": y, "color": {"r": pixel[0], "g": pixel[1], "b": pixel[2]}} - ) - except Exception as e: - return json.dumps({"error": str(e)}) - - def _pixel_matches( - self, x: int, y: int, color: tuple[int, int, int], tolerance: int - ) -> str: - pg = self._ensure_pyautogui() - try: - screenshot = pg.screenshot(region=(x, y, 1, 1)) - pixel = screenshot.getpixel((0, 0)) - matches = all(abs(pixel[i] - color[i]) <= tolerance for i in range(3)) - return json.dumps( - { - "matches": matches, - "expected": {"r": color[0], "g": color[1], "b": color[2]}, - "actual": {"r": pixel[0], "g": pixel[1], "b": pixel[2]}, - } - ) - except Exception as e: - return json.dumps({"error": str(e)}) - - def _get_screens(self) -> str: - if IS_MACOS: - try: - result = subprocess.run( - ["system_profiler", "SPDisplaysDataType", "-json"], - capture_output=True, - text=True, - timeout=5, - ) - if result.returncode == 0: - data = json.loads(result.stdout) - displays = [] - for gpu in data.get("SPDisplaysDataType", []): - for disp in gpu.get("spdisplays_ndrvs", []): - displays.append( - { - "name": disp.get("_name", "Unknown"), - "resolution": disp.get( - "_spdisplays_resolution", "Unknown" - ), - "main": disp.get("spdisplays_main") - == "spdisplays_yes", - } - ) - return json.dumps(displays) - except Exception: - pass - size = NativeControl.screen_size() - return json.dumps( - [{"name": "Primary", "resolution": f"{size[0]}x{size[1]}", "main": True}] - ) - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def computer( - action: Annotated[str, Field(description="Action to perform")] = "info", - x: Annotated[int | None, Field(description="X coordinate")] = None, - y: Annotated[int | None, Field(description="Y coordinate")] = None, - dx: Annotated[int | None, Field(description="Delta X")] = None, - dy: Annotated[int | None, Field(description="Delta Y")] = None, - end_x: Annotated[int | None, Field(description="End X for drag")] = None, - end_y: Annotated[int | None, Field(description="End Y for drag")] = None, - text: Annotated[str | None, Field(description="Text to type")] = None, - key: Annotated[str | None, Field(description="Key to press")] = None, - keys: Annotated[ - list[str] | None, Field(description="Keys for hotkey") - ] = None, - button: Annotated[str, Field(description="Mouse button")] = "left", - amount: Annotated[int | None, Field(description="Scroll amount")] = None, - duration: Annotated[float, Field(description="Duration")] = 0.25, - interval: Annotated[float, Field(description="Type interval")] = 0.02, - region: Annotated[ - list[int] | None, Field(description="Region [x,y,w,h]") - ] = None, - clear: Annotated[bool, Field(description="Clear before write")] = False, - image_path: Annotated[str | None, Field(description="Image path")] = None, - confidence: Annotated[float, Field(description="Match confidence")] = 0.9, - timeout: Annotated[float, Field(description="Wait timeout")] = 10.0, - color: Annotated[list[int] | None, Field(description="RGB color")] = None, - tolerance: Annotated[int, Field(description="Color tolerance")] = 0, - title: Annotated[str | None, Field(description="Window title")] = None, - use_regex: Annotated[bool, Field(description="Regex match")] = False, - name: Annotated[str | None, Field(description="Region name")] = None, - width: Annotated[int | None, Field(description="Width")] = None, - height: Annotated[int | None, Field(description="Height")] = None, - value: Annotated[float | None, Field(description="Value")] = None, - actions: Annotated[ - list[dict] | None, Field(description="Batch actions") - ] = None, - ctx: MCPContext = None, - ) -> str: - return await tool_self.call( - ctx, - action=action, - x=x, - y=y, - dx=dx, - dy=dy, - end_x=end_x, - end_y=end_y, - text=text, - key=key, - keys=keys, - button=button, - amount=amount, - duration=duration, - interval=interval, - region=region, - clear=clear, - image_path=image_path, - confidence=confidence, - timeout=timeout, - color=tuple(color) if color else None, - tolerance=tolerance, - title=title, - use_regex=use_regex, - name=name, - width=width, - height=height, - value=value, - actions=actions, - ) diff --git a/pkg/hanzo-tools-computer/hanzo_tools/computer/media_tool.py b/pkg/hanzo-tools-computer/hanzo_tools/computer/media_tool.py deleted file mode 100644 index 8919d33e0..000000000 --- a/pkg/hanzo-tools-computer/hanzo_tools/computer/media_tool.py +++ /dev/null @@ -1,1611 +0,0 @@ -"""Media processing tool for Hanzo AI. - -Handles images and video with configurable limits optimized for Claude's vision API: -- Up to 100 images per invocation (configurable) -- Combined payload under 32 MB (configurable) -- Per-image resolution constraints (configurable) -- Automatic optimization for Claude vision -- INTELLIGENT VIDEO SLICING with activity detection -- Computer use session compression - -Limits based on Claude API constraints: -- Max 100 images per request -- Max 32 MB total payload -- Recommended 768-1568px for best quality/speed tradeoff -- Supports PNG, JPEG, GIF, WebP - -Activity Detection: -- Frame differencing for movement detection -- Scene change detection via FFmpeg -- Configurable sensitivity threshold -- Only extracts frames during activity periods - -Environment configuration: -- HANZO_MEDIA_MAX_IMAGES: Max images per batch (default: 100) -- HANZO_MEDIA_MAX_PAYLOAD_MB: Max total payload in MB (default: 32) -- HANZO_MEDIA_MAX_RESOLUTION: Max image dimension (default: 1568) -- HANZO_MEDIA_JPEG_QUALITY: JPEG quality 1-100 (default: 85) -- HANZO_MEDIA_OPTIMAL_SIZE: Target size for optimization (default: 768) -- HANZO_MEDIA_ACTIVITY_THRESHOLD: Activity detection sensitivity (default: 0.02) -""" - -import io -import os -import sys -import json -import base64 -import asyncio -import hashlib -import mimetypes -from typing import Any, Union, Literal, Optional, Annotated, final, override -from pathlib import Path -from dataclasses import field, dataclass -from concurrent.futures import ThreadPoolExecutor - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, PermissionManager, auto_timeout - -# Thread pool for blocking I/O -_EXECUTOR = ThreadPoolExecutor(max_workers=4, thread_name_prefix="media_") - - -@dataclass -class MediaLimits: - """Configurable limits for media processing.""" - - # Image limits - max_images: int = 100 # Max images per batch - max_payload_mb: float = 32.0 # Max total payload in MB - max_resolution: int = 1568 # Max dimension (width or height) - min_resolution: int = 10 # Min dimension - - # Optimization settings - optimal_size: int = 768 # Target size for optimization - jpeg_quality: int = 85 # JPEG quality (1-100) - - # Video limits - max_frames: int = 100 # Max video frames per extraction - max_fps: int = 30 # Max frame rate for extraction - - # Activity detection settings - activity_threshold: float = ( - 0.02 # % of pixels changed to count as activity (0.01-0.10) - ) - min_activity_gap_ms: int = 200 # Min ms between activity frames - scene_change_threshold: float = 0.3 # FFmpeg scene change threshold - - # Session compression settings - session_max_duration: int = 60 # Max session duration in seconds - session_target_frames: int = 30 # Target frames for session summary - session_compression_quality: int = 60 # Aggressive compression for sessions - - # Hard limits (Claude API constraints for multi-image requests) - HARD_MAX_RESOLUTION: int = 2000 # Claude's actual limit - HARD_MAX_IMAGES: int = 100 - HARD_MAX_PAYLOAD_MB: float = 32.0 - - @classmethod - def from_env(cls) -> "MediaLimits": - """Load limits from environment variables with hard cap enforcement.""" - return cls( - max_images=min( - int(os.environ.get("HANZO_MEDIA_MAX_IMAGES", "100")), - cls.HARD_MAX_IMAGES, - ), - max_payload_mb=min( - float(os.environ.get("HANZO_MEDIA_MAX_PAYLOAD_MB", "32")), - cls.HARD_MAX_PAYLOAD_MB, - ), - max_resolution=min( - int(os.environ.get("HANZO_MEDIA_MAX_RESOLUTION", "1568")), - cls.HARD_MAX_RESOLUTION, # Claude 2000px limit for multi-image - ), - optimal_size=min( - int(os.environ.get("HANZO_MEDIA_OPTIMAL_SIZE", "768")), - cls.HARD_MAX_RESOLUTION, - ), - jpeg_quality=int(os.environ.get("HANZO_MEDIA_JPEG_QUALITY", "85")), - max_frames=min( - int(os.environ.get("HANZO_MEDIA_MAX_FRAMES", "100")), - cls.HARD_MAX_IMAGES, - ), - activity_threshold=float( - os.environ.get("HANZO_MEDIA_ACTIVITY_THRESHOLD", "0.02") - ), - min_activity_gap_ms=int( - os.environ.get("HANZO_MEDIA_MIN_ACTIVITY_GAP_MS", "200") - ), - scene_change_threshold=float( - os.environ.get("HANZO_MEDIA_SCENE_THRESHOLD", "0.3") - ), - session_max_duration=int( - os.environ.get("HANZO_MEDIA_SESSION_MAX_DURATION", "60") - ), - session_target_frames=min( - int(os.environ.get("HANZO_MEDIA_SESSION_TARGET_FRAMES", "30")), - cls.HARD_MAX_IMAGES, - ), - session_compression_quality=int( - os.environ.get("HANZO_MEDIA_SESSION_QUALITY", "60") - ), - ) - - @property - def max_payload_bytes(self) -> int: - return int(self.max_payload_mb * 1024 * 1024) - - -@dataclass -class MediaResult: - """Result of media processing.""" - - success: bool - images: list[dict] = field(default_factory=list) - total_size: int = 0 - total_count: int = 0 - errors: list[str] = field(default_factory=list) - warnings: list[str] = field(default_factory=list) - - def to_dict(self) -> dict: - return { - "success": self.success, - "total_count": self.total_count, - "total_size_bytes": self.total_size, - "total_size_mb": round(self.total_size / (1024 * 1024), 2), - "images": self.images, - "errors": self.errors if self.errors else None, - "warnings": self.warnings if self.warnings else None, - } - - -def _get_image_info(data: bytes) -> dict: - """Get image info without full decode.""" - info = {"size": len(data), "format": "unknown"} - - # Detect format from magic bytes - if data[:8] == b"\x89PNG\r\n\x1a\n": - info["format"] = "png" - elif data[:2] == b"\xff\xd8": - info["format"] = "jpeg" - elif data[:6] in (b"GIF87a", b"GIF89a"): - info["format"] = "gif" - elif data[:4] == b"RIFF" and data[8:12] == b"WEBP": - info["format"] = "webp" - - return info - - -def _resize_image( - data: bytes, - max_size: int, - quality: int = 85, - force_jpeg: bool = False, -) -> tuple[bytes, dict]: - """Resize image and optionally convert to JPEG. - - Returns: (processed_data, info_dict) - """ - try: - from PIL import Image - except ImportError: - return data, {"error": "PIL not available", "original_size": len(data)} - - img = Image.open(io.BytesIO(data)) - original_size = img.size - original_mode = img.mode - - # Calculate new size maintaining aspect ratio - width, height = img.size - needs_resize = max(width, height) > max_size - - if needs_resize: - ratio = max_size / max(width, height) - new_size = (int(width * ratio), int(height * ratio)) - img = img.resize(new_size, Image.Resampling.LANCZOS) - - # Determine output format - info = _get_image_info(data) - output_format = info["format"].upper() - - # Convert to JPEG if requested or if PNG is too large - if force_jpeg or (output_format == "PNG" and len(data) > 500_000): - output_format = "JPEG" - if img.mode in ("RGBA", "P", "LA"): - # Convert transparency to white background - background = Image.new("RGB", img.size, (255, 255, 255)) - if img.mode == "P": - img = img.convert("RGBA") - background.paste( - img, mask=img.split()[-1] if img.mode in ("RGBA", "LA") else None - ) - img = background - elif img.mode != "RGB": - img = img.convert("RGB") - - # Save to buffer - buffer = io.BytesIO() - save_kwargs = {} - - if output_format == "JPEG": - save_kwargs = {"quality": quality, "optimize": True} - elif output_format == "PNG": - save_kwargs = {"optimize": True} - elif output_format == "WEBP": - save_kwargs = {"quality": quality} - - try: - img.save(buffer, format=output_format, **save_kwargs) - except Exception: - # Fallback to JPEG - if img.mode != "RGB": - img = img.convert("RGB") - img.save(buffer, format="JPEG", quality=quality, optimize=True) - output_format = "JPEG" - - result = buffer.getvalue() - - return result, { - "original_size": len(data), - "processed_size": len(result), - "original_dimensions": original_size, - "new_dimensions": img.size, - "format": output_format.lower(), - "resized": needs_resize, - } - - -def _extract_video_frames( - video_path: str, - count: int = 10, - interval_ms: int = 1000, - max_size: int = 768, -) -> list[tuple[bytes, dict]]: - """Extract frames from video file.""" - import tempfile - import subprocess - - frames = [] - - # Get video duration - probe_cmd = [ - "ffprobe", - "-v", - "error", - "-show_entries", - "format=duration", - "-of", - "default=noprint_wrappers=1:nokey=1", - video_path, - ] - - try: - result = subprocess.run(probe_cmd, capture_output=True, text=True, timeout=10) - duration = float(result.stdout.strip()) - except Exception: - duration = 60.0 # Default assumption - - # Calculate frame times - interval_sec = interval_ms / 1000 - total_time = min(duration, count * interval_sec) - - for i in range(count): - timestamp = i * interval_sec - if timestamp >= duration: - break - - with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as f: - tmp_path = f.name - - try: - cmd = [ - "ffmpeg", - "-y", - "-ss", - str(timestamp), - "-i", - video_path, - "-frames:v", - "1", - "-vf", - f"scale='min({max_size},iw)':min'({max_size},ih)':force_original_aspect_ratio=decrease", - "-q:v", - "2", - tmp_path, - ] - - subprocess.run(cmd, capture_output=True, timeout=10) - - if os.path.exists(tmp_path) and os.path.getsize(tmp_path) > 0: - with open(tmp_path, "rb") as f: - data = f.read() - frames.append( - ( - data, - { - "timestamp_ms": int(timestamp * 1000), - "frame_index": i, - "size": len(data), - "format": "jpeg", - }, - ) - ) - finally: - if os.path.exists(tmp_path): - os.unlink(tmp_path) - - return frames - - -@dataclass -class ActivitySegment: - """A detected activity segment in video.""" - - start_ms: int - end_ms: int - activity_score: float # 0-1, how much activity - frame_count: int - description: str = "" - - -def _detect_scene_changes( - video_path: str, - threshold: float = 0.3, - max_duration: int = 60, -) -> list[float]: - """Detect scene changes using FFmpeg. - - Returns list of timestamps (in seconds) where scenes change. - """ - import subprocess - - # Use FFmpeg scene detection filter - cmd = [ - "ffprobe", - "-v", - "quiet", - "-show_entries", - "frame=pts_time", - "-select_streams", - "v:0", - "-of", - "csv=p=0", - "-f", - "lavfi", - f"movie={video_path},select='gt(scene,{threshold})'", - ] - - try: - result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) - timestamps = [] - for line in result.stdout.strip().split("\n"): - if line: - try: - ts = float(line) - if ts <= max_duration: - timestamps.append(ts) - except ValueError: - continue - return timestamps - except Exception: - return [] - - -def _compute_frame_difference( - frame1_data: bytes, - frame2_data: bytes, - threshold: float = 0.02, -) -> tuple[float, bool]: - """Compute difference between two frames. - - Returns (difference_ratio, has_significant_activity). - difference_ratio is 0-1 representing % of pixels that changed. - """ - try: - import numpy as np - from PIL import Image, ImageChops - except ImportError: - return 0.0, False - - try: - img1 = Image.open(io.BytesIO(frame1_data)).convert("L") # Grayscale - img2 = Image.open(io.BytesIO(frame2_data)).convert("L") - - # Ensure same size - if img1.size != img2.size: - img2 = img2.resize(img1.size) - - # Compute difference - diff = ImageChops.difference(img1, img2) - - # Count pixels above threshold - diff_array = np.array(diff) - changed_pixels = np.sum(diff_array > 10) # Pixel value threshold - total_pixels = diff_array.size - - ratio = changed_pixels / total_pixels if total_pixels > 0 else 0 - - return ratio, ratio > threshold - - except Exception: - return 0.0, False - - -def _analyze_video_activity( - video_path: str, - activity_threshold: float = 0.02, - sample_fps: float = 2.0, - max_duration: int = 60, - scene_threshold: float = 0.3, -) -> tuple[list[ActivitySegment], list[float]]: - """Analyze video for activity segments. - - Returns (activity_segments, keyframe_timestamps). - - Combines: - 1. Scene change detection (FFmpeg) - 2. Frame differencing (PIL) - 3. Activity clustering - """ - import tempfile - import subprocess - - segments = [] - keyframe_times = [] - - # Get video duration - probe_cmd = [ - "ffprobe", - "-v", - "error", - "-show_entries", - "format=duration", - "-of", - "default=noprint_wrappers=1:nokey=1", - video_path, - ] - try: - result = subprocess.run(probe_cmd, capture_output=True, text=True, timeout=10) - duration = min(float(result.stdout.strip()), max_duration) - except Exception: - duration = max_duration - - # Get scene changes - scene_changes = _detect_scene_changes(video_path, scene_threshold, max_duration) - keyframe_times.extend(scene_changes) - - # Sample frames for activity detection - sample_interval = 1.0 / sample_fps - num_samples = int(duration * sample_fps) - - prev_frame = None - activity_frames = [] - - for i in range(num_samples): - timestamp = i * sample_interval - if timestamp > duration: - break - - # Extract frame - with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as f: - tmp_path = f.name - - try: - cmd = [ - "ffmpeg", - "-y", - "-ss", - str(timestamp), - "-i", - video_path, - "-frames:v", - "1", - "-vf", - "scale=320:-1", # Small for comparison - "-q:v", - "5", - tmp_path, - ] - subprocess.run(cmd, capture_output=True, timeout=5) - - if os.path.exists(tmp_path) and os.path.getsize(tmp_path) > 0: - with open(tmp_path, "rb") as f: - frame_data = f.read() - - if prev_frame is not None: - diff_ratio, has_activity = _compute_frame_difference( - prev_frame, frame_data, activity_threshold - ) - if has_activity: - activity_frames.append( - { - "timestamp": timestamp, - "diff_ratio": diff_ratio, - } - ) - if timestamp not in keyframe_times: - keyframe_times.append(timestamp) - - prev_frame = frame_data - - finally: - if os.path.exists(tmp_path): - os.unlink(tmp_path) - - # Cluster activity into segments - if activity_frames: - current_segment_start = activity_frames[0]["timestamp"] - current_segment_score = activity_frames[0]["diff_ratio"] - segment_frame_count = 1 - - for frame in activity_frames[1:]: - # If gap > 1 second, start new segment - if ( - frame["timestamp"] - current_segment_start - > 1.0 + segment_frame_count * sample_interval - ): - segments.append( - ActivitySegment( - start_ms=int(current_segment_start * 1000), - end_ms=int( - ( - current_segment_start - + segment_frame_count * sample_interval - ) - * 1000 - ), - activity_score=current_segment_score / segment_frame_count, - frame_count=segment_frame_count, - ) - ) - current_segment_start = frame["timestamp"] - current_segment_score = frame["diff_ratio"] - segment_frame_count = 1 - else: - current_segment_score += frame["diff_ratio"] - segment_frame_count += 1 - - # Last segment - segments.append( - ActivitySegment( - start_ms=int(current_segment_start * 1000), - end_ms=int( - (current_segment_start + segment_frame_count * sample_interval) - * 1000 - ), - activity_score=current_segment_score / segment_frame_count, - frame_count=segment_frame_count, - ) - ) - - # Sort and deduplicate keyframe times - keyframe_times = sorted(set(keyframe_times)) - - return segments, keyframe_times - - -def _extract_activity_frames( - video_path: str, - keyframe_times: list[float], - max_frames: int = 30, - max_size: int = 512, - quality: int = 60, -) -> list[tuple[bytes, dict]]: - """Extract frames at specified timestamps with heavy compression. - - Optimized for minimal payload size. - """ - import tempfile - import subprocess - - frames = [] - - # Limit frames - if len(keyframe_times) > max_frames: - # Sample evenly - step = len(keyframe_times) / max_frames - keyframe_times = [keyframe_times[int(i * step)] for i in range(max_frames)] - - for timestamp in keyframe_times: - with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as f: - tmp_path = f.name - - try: - cmd = [ - "ffmpeg", - "-y", - "-ss", - str(timestamp), - "-i", - video_path, - "-frames:v", - "1", - "-vf", - f"scale='min({max_size},iw)':'min({max_size},ih)':force_original_aspect_ratio=decrease", - "-q:v", - str(max(1, min(31, (100 - quality) // 3))), # FFmpeg quality 1-31 - tmp_path, - ] - subprocess.run(cmd, capture_output=True, timeout=10) - - if os.path.exists(tmp_path) and os.path.getsize(tmp_path) > 0: - with open(tmp_path, "rb") as f: - data = f.read() - - frames.append( - ( - data, - { - "timestamp_ms": int(timestamp * 1000), - "timestamp_sec": round(timestamp, 2), - "size": len(data), - "format": "jpeg", - }, - ) - ) - - finally: - if os.path.exists(tmp_path): - os.unlink(tmp_path) - - return frames - - -def _compress_session( - video_path: str, - max_duration: int = 60, - target_frames: int = 30, - activity_threshold: float = 0.02, - scene_threshold: float = 0.3, - max_size: int = 512, - quality: int = 60, -) -> tuple[list[tuple[bytes, dict]], list[ActivitySegment], dict]: - """Full pipeline: analyze โ†’ slice โ†’ compress a computer use session. - - Returns (frames, activity_segments, metadata). - """ - # Analyze video - segments, keyframe_times = _analyze_video_activity( - video_path, - activity_threshold=activity_threshold, - max_duration=max_duration, - scene_threshold=scene_threshold, - ) - - # If no activity detected, sample evenly - if not keyframe_times: - import subprocess - - probe_cmd = [ - "ffprobe", - "-v", - "error", - "-show_entries", - "format=duration", - "-of", - "default=noprint_wrappers=1:nokey=1", - video_path, - ] - try: - result = subprocess.run( - probe_cmd, capture_output=True, text=True, timeout=10 - ) - duration = min(float(result.stdout.strip()), max_duration) - except Exception: - duration = max_duration - - interval = duration / target_frames - keyframe_times = [i * interval for i in range(target_frames)] - - # Extract frames at activity points - frames = _extract_activity_frames( - video_path, - keyframe_times, - max_frames=target_frames, - max_size=max_size, - quality=quality, - ) - - # Compute metadata - total_size = sum(len(f[0]) for f in frames) - metadata = { - "source": video_path, - "activity_segments": len(segments), - "keyframes_detected": len(keyframe_times), - "frames_extracted": len(frames), - "total_size_bytes": total_size, - "total_size_kb": round(total_size / 1024, 1), - "avg_frame_size_kb": round(total_size / len(frames) / 1024, 1) if frames else 0, - "compression_settings": { - "max_size": max_size, - "quality": quality, - }, - } - - return frames, segments, metadata - - -Action = Literal[ - # Image operations - "load", # Load single image - "load_batch", # Load multiple images - "optimize", # Optimize images for Claude - "resize", # Resize images - "info", # Get image info - # Video operations - "extract_frames", # Extract frames from video - # Activity detection & slicing (NEW) - "analyze", # Detect activity segments in video - "slice", # Extract frames at activity points only - "compress_session", # Full pipeline for computer use sessions - # Configuration - "limits", # Get/set limits - "status", # Get current status -] - - -@final -class MediaTool(BaseTool): - """Media processing tool with configurable limits. - - Handles images and video optimized for Claude's vision API: - - Up to 100 images per batch (configurable) - - Max 32 MB total payload (configurable) - - Automatic resizing and optimization - - Video frame extraction - """ - - name = "media" - - def __init__( - self, - permission_manager: Optional[PermissionManager] = None, - limits: Optional[MediaLimits] = None, - ): - if permission_manager is None: - permission_manager = PermissionManager() - self.permission_manager = permission_manager - self.limits = limits or MediaLimits.from_env() - - @property - @override - def description(self) -> str: - return f"""Media processing optimized for Claude vision API. - -LIMITS (configurable via env vars): - Max images per batch: {self.limits.max_images} - Max payload: {self.limits.max_payload_mb} MB - Max resolution: {self.limits.max_resolution}px - Optimal size: {self.limits.optimal_size}px - JPEG quality: {self.limits.jpeg_quality}% - -ACTIONS: - -load: Load and optimize a single image - - path: Image file path - - optimize: Auto-optimize for Claude (default: true) - - max_size: Max dimension (default: {self.limits.optimal_size}) - -load_batch: Load multiple images with limits enforcement - - paths: List of image file paths - - optimize: Auto-optimize all (default: true) - - max_size: Max dimension per image - - Returns up to {self.limits.max_images} images, max {self.limits.max_payload_mb}MB total - -optimize: Optimize images for Claude vision - - images: List of base64 images or file paths - - max_size: Target size (default: {self.limits.optimal_size}) - - quality: JPEG quality 1-100 (default: {self.limits.jpeg_quality}) - -resize: Resize images to specific dimensions - - images: List of base64 images or file paths - - width: Target width (or max dimension if height not set) - - height: Target height (optional) - - maintain_aspect: Keep aspect ratio (default: true) - -extract_frames: Extract frames from video - - path: Video file path - - count: Number of frames (max {self.limits.max_frames}) - - interval_ms: Time between frames (default: 1000) - - optimize: Optimize frames for Claude (default: true) - -analyze: Detect activity segments in video (movement, scene changes) - - path: Video file path - - activity_threshold: Sensitivity 0.01-0.10 (default: {self.limits.activity_threshold}) - - scene_threshold: Scene change sensitivity (default: {self.limits.scene_change_threshold}) - - max_duration: Max seconds to analyze (default: {self.limits.session_max_duration}) - - Returns activity segments with timestamps and scores - -slice: Extract frames ONLY at activity points - - path: Video file path - - target_frames: Max frames to extract (default: {self.limits.session_target_frames}) - - activity_threshold: Sensitivity (default: {self.limits.activity_threshold}) - - max_size: Frame dimension (default: 512 for compression) - - quality: JPEG quality (default: {self.limits.session_compression_quality}) - -compress_session: FULL PIPELINE for computer use video - - path: Video file path (e.g., 30-second screen recording) - - target_frames: Max frames (default: {self.limits.session_target_frames}) - - max_size: Frame dimension (default: 512) - - quality: Compression quality (default: {self.limits.session_compression_quality}) - - Returns compressed keyframes + activity analysis for Claude interpretation - -info: Get image/video info without loading full data - - path: File path - -limits: Get or update processing limits - - max_images: New max images (optional) - - max_payload_mb: New max payload (optional) - - max_resolution: New max resolution (optional) - -status: Get current configuration and stats - -ENVIRONMENT VARIABLES: - HANZO_MEDIA_MAX_IMAGES={self.limits.max_images} - HANZO_MEDIA_MAX_PAYLOAD_MB={self.limits.max_payload_mb} - HANZO_MEDIA_MAX_RESOLUTION={self.limits.max_resolution} - HANZO_MEDIA_OPTIMAL_SIZE={self.limits.optimal_size} - HANZO_MEDIA_JPEG_QUALITY={self.limits.jpeg_quality} - HANZO_MEDIA_ACTIVITY_THRESHOLD={self.limits.activity_threshold} - HANZO_MEDIA_SESSION_TARGET_FRAMES={self.limits.session_target_frames} - HANZO_MEDIA_SESSION_QUALITY={self.limits.session_compression_quality} - -EXAMPLES: - media(action="load", path="screenshot.png") - media(action="load_batch", paths=["img1.png", "img2.jpg", "img3.webp"]) - media(action="extract_frames", path="video.mp4", count=20, interval_ms=500) - media(action="analyze", path="session.mp4") # Detect activity - media(action="slice", path="session.mp4", target_frames=30) # Activity frames only - media(action="compress_session", path="recording.mp4") # Full pipeline for Claude - media(action="limits", max_images=50, max_payload_mb=16) -""" - - async def _load_image( - self, - path: str, - optimize: bool = True, - max_size: Optional[int] = None, - ) -> tuple[Optional[bytes], dict]: - """Load and optionally optimize a single image.""" - loop = asyncio.get_event_loop() - max_size = max_size or self.limits.optimal_size - - if not os.path.exists(path): - return None, {"error": f"File not found: {path}"} - - try: - # Read file - def read_file(): - with open(path, "rb") as f: - return f.read() - - data = await loop.run_in_executor(_EXECUTOR, read_file) - info = _get_image_info(data) - info["path"] = path - info["original_size"] = len(data) - - if optimize: - data, opt_info = await loop.run_in_executor( - _EXECUTOR, - _resize_image, - data, - max_size, - self.limits.jpeg_quality, - False, - ) - info.update(opt_info) - - return data, info - - except Exception as e: - return None, {"error": str(e), "path": path} - - async def _load_batch( - self, - paths: list[str], - optimize: bool = True, - max_size: Optional[int] = None, - ) -> MediaResult: - """Load multiple images with limits enforcement.""" - result = MediaResult(success=True) - max_size = max_size or self.limits.optimal_size - - # Enforce max images limit - if len(paths) > self.limits.max_images: - result.warnings.append( - f"Requested {len(paths)} images, limited to {self.limits.max_images}" - ) - paths = paths[: self.limits.max_images] - - for path in paths: - # Check payload limit - if result.total_size >= self.limits.max_payload_bytes: - result.warnings.append( - f"Payload limit ({self.limits.max_payload_mb}MB) reached, stopped at {result.total_count} images" - ) - break - - data, info = await self._load_image(path, optimize, max_size) - - if data is None: - result.errors.append(info.get("error", f"Failed to load {path}")) - continue - - # Check if this image would exceed payload - if result.total_size + len(data) > self.limits.max_payload_bytes: - result.warnings.append(f"Skipping {path}: would exceed payload limit") - continue - - result.images.append( - { - "path": path, - "size": len(data), - "format": info.get("format", "unknown"), - "dimensions": info.get( - "new_dimensions", info.get("original_dimensions") - ), - "base64": base64.b64encode(data).decode(), - **{k: v for k, v in info.items() if k not in ("path", "base64")}, - } - ) - result.total_size += len(data) - result.total_count += 1 - - if result.errors and not result.images: - result.success = False - - return result - - @override - @auto_timeout("media") - async def call( - self, - ctx: MCPContext, - action: str = "status", - # File paths - path: Optional[str] = None, - paths: Optional[list[str]] = None, - # Image data - images: Optional[list[str]] = None, # base64 or paths - # Processing options - optimize: bool = True, - max_size: Optional[int] = None, - width: Optional[int] = None, - height: Optional[int] = None, - maintain_aspect: bool = True, - quality: Optional[int] = None, - # Video options - count: int = 10, - interval_ms: int = 1000, - # Limit updates - max_images: Optional[int] = None, - max_payload_mb: Optional[float] = None, - max_resolution: Optional[int] = None, - **kwargs, - ) -> str: - """Execute media action.""" - loop = asyncio.get_event_loop() - quality = quality or self.limits.jpeg_quality - max_size = max_size or self.limits.optimal_size - - try: - if action == "status": - return json.dumps( - { - "limits": { - "max_images": self.limits.max_images, - "max_payload_mb": self.limits.max_payload_mb, - "max_resolution": self.limits.max_resolution, - "optimal_size": self.limits.optimal_size, - "jpeg_quality": self.limits.jpeg_quality, - "max_frames": self.limits.max_frames, - }, - "env_vars": { - "HANZO_MEDIA_MAX_IMAGES": os.environ.get( - "HANZO_MEDIA_MAX_IMAGES" - ), - "HANZO_MEDIA_MAX_PAYLOAD_MB": os.environ.get( - "HANZO_MEDIA_MAX_PAYLOAD_MB" - ), - "HANZO_MEDIA_MAX_RESOLUTION": os.environ.get( - "HANZO_MEDIA_MAX_RESOLUTION" - ), - "HANZO_MEDIA_OPTIMAL_SIZE": os.environ.get( - "HANZO_MEDIA_OPTIMAL_SIZE" - ), - "HANZO_MEDIA_JPEG_QUALITY": os.environ.get( - "HANZO_MEDIA_JPEG_QUALITY" - ), - }, - } - ) - - elif action == "limits": - # Update limits if provided - if max_images is not None: - self.limits.max_images = min(max_images, 100) # Hard cap at 100 - if max_payload_mb is not None: - self.limits.max_payload_mb = min( - max_payload_mb, 32.0 - ) # Hard cap at 32MB - if max_resolution is not None: - self.limits.max_resolution = min(max_resolution, 4096) # Hard cap - - return json.dumps( - { - "success": True, - "limits": { - "max_images": self.limits.max_images, - "max_payload_mb": self.limits.max_payload_mb, - "max_resolution": self.limits.max_resolution, - "optimal_size": self.limits.optimal_size, - "jpeg_quality": self.limits.jpeg_quality, - }, - } - ) - - elif action == "load": - if not path: - return json.dumps({"error": "path required"}) - - data, info = await self._load_image(path, optimize, max_size) - - if data is None: - return json.dumps({"success": False, **info}) - - return json.dumps( - { - "success": True, - "path": path, - "size": len(data), - "format": info.get("format", "unknown"), - "dimensions": info.get( - "new_dimensions", info.get("original_dimensions") - ), - "base64": base64.b64encode(data).decode(), - **{ - k: v for k, v in info.items() if k not in ("path", "base64") - }, - } - ) - - elif action == "load_batch": - if not paths: - return json.dumps({"error": "paths required (list of file paths)"}) - - result = await self._load_batch(paths, optimize, max_size) - return json.dumps(result.to_dict()) - - elif action == "optimize": - if not images: - return json.dumps( - {"error": "images required (list of base64 or paths)"} - ) - - result = MediaResult(success=True) - - for i, img in enumerate(images): - if result.total_count >= self.limits.max_images: - result.warnings.append( - f"Max images ({self.limits.max_images}) reached" - ) - break - - if result.total_size >= self.limits.max_payload_bytes: - result.warnings.append(f"Payload limit reached") - break - - try: - # Determine if base64 or path - if os.path.exists(img): - with open(img, "rb") as f: - data = f.read() - source = img - else: - data = base64.b64decode(img) - source = f"image_{i}" - - # Optimize - optimized, info = await loop.run_in_executor( - _EXECUTOR, - _resize_image, - data, - max_size, - quality, - True, # Force JPEG for optimization - ) - - if ( - result.total_size + len(optimized) - > self.limits.max_payload_bytes - ): - result.warnings.append( - f"Skipping {source}: would exceed limit" - ) - continue - - result.images.append( - { - "index": i, - "source": source, - "size": len(optimized), - "format": info.get("format", "jpeg"), - "dimensions": info.get("new_dimensions"), - "compression_ratio": round( - info.get("original_size", len(data)) - / len(optimized), - 2, - ), - "base64": base64.b64encode(optimized).decode(), - } - ) - result.total_size += len(optimized) - result.total_count += 1 - - except Exception as e: - result.errors.append(f"Image {i}: {str(e)}") - - if result.errors and not result.images: - result.success = False - - return json.dumps(result.to_dict()) - - elif action == "resize": - if not images: - return json.dumps({"error": "images required"}) - - if not width and not height: - return json.dumps({"error": "width or height required"}) - - result = MediaResult(success=True) - target_size = width or height or self.limits.optimal_size - - for i, img in enumerate(images): - try: - if os.path.exists(img): - with open(img, "rb") as f: - data = f.read() - else: - data = base64.b64decode(img) - - resized, info = await loop.run_in_executor( - _EXECUTOR, - _resize_image, - data, - target_size, - quality, - False, - ) - - result.images.append( - { - "index": i, - "size": len(resized), - "dimensions": info.get("new_dimensions"), - "format": info.get("format"), - "base64": base64.b64encode(resized).decode(), - } - ) - result.total_size += len(resized) - result.total_count += 1 - - except Exception as e: - result.errors.append(f"Image {i}: {str(e)}") - - return json.dumps(result.to_dict()) - - elif action == "extract_frames": - if not path: - return json.dumps({"error": "path required (video file)"}) - - if not os.path.exists(path): - return json.dumps({"error": f"File not found: {path}"}) - - # Enforce limits - actual_count = min( - count, self.limits.max_frames, self.limits.max_images - ) - - frames = await loop.run_in_executor( - _EXECUTOR, - _extract_video_frames, - path, - actual_count, - interval_ms, - max_size, - ) - - result = MediaResult(success=True) - - for data, info in frames: - if result.total_size + len(data) > self.limits.max_payload_bytes: - result.warnings.append( - "Payload limit reached, stopped extraction" - ) - break - - result.images.append( - { - "frame_index": info["frame_index"], - "timestamp_ms": info["timestamp_ms"], - "size": len(data), - "format": info["format"], - "base64": base64.b64encode(data).decode(), - } - ) - result.total_size += len(data) - result.total_count += 1 - - return json.dumps( - { - **result.to_dict(), - "source": path, - "interval_ms": interval_ms, - "frames_extracted": result.total_count, - } - ) - - elif action == "info": - if not path: - return json.dumps({"error": "path required"}) - - if not os.path.exists(path): - return json.dumps({"error": f"File not found: {path}"}) - - # Get file info without loading entire file - stat = os.stat(path) - mime_type, _ = mimetypes.guess_type(path) - - info = { - "path": path, - "size_bytes": stat.st_size, - "size_mb": round(stat.st_size / (1024 * 1024), 2), - "mime_type": mime_type, - } - - # Get image dimensions if possible - try: - from PIL import Image - - with Image.open(path) as img: - info["dimensions"] = img.size - info["format"] = img.format - info["mode"] = img.mode - except Exception: - pass - - # Check if video - if mime_type and mime_type.startswith("video/"): - info["type"] = "video" - try: - import subprocess - - probe = subprocess.run( - [ - "ffprobe", - "-v", - "error", - "-show_entries", - "format=duration:stream=width,height,r_frame_rate", - "-of", - "json", - path, - ], - capture_output=True, - text=True, - timeout=10, - ) - if probe.returncode == 0: - probe_data = json.loads(probe.stdout) - if "format" in probe_data: - info["duration_seconds"] = float( - probe_data["format"].get("duration", 0) - ) - if "streams" in probe_data and probe_data["streams"]: - stream = probe_data["streams"][0] - info["dimensions"] = ( - stream.get("width"), - stream.get("height"), - ) - info["frame_rate"] = stream.get("r_frame_rate") - except Exception: - pass - else: - info["type"] = "image" - - return json.dumps(info) - - elif action == "analyze": - if not path: - return json.dumps({"error": "path required (video file)"}) - - if not os.path.exists(path): - return json.dumps({"error": f"File not found: {path}"}) - - # Get optional params from kwargs - activity_threshold = kwargs.get( - "activity_threshold", self.limits.activity_threshold - ) - scene_threshold = kwargs.get( - "scene_threshold", self.limits.scene_change_threshold - ) - max_duration = kwargs.get( - "max_duration", self.limits.session_max_duration - ) - - segments, keyframe_times = await loop.run_in_executor( - _EXECUTOR, - _analyze_video_activity, - path, - activity_threshold, - 2.0, # sample_fps - max_duration, - scene_threshold, - ) - - return json.dumps( - { - "success": True, - "source": path, - "activity_segments": [ - { - "start_ms": s.start_ms, - "end_ms": s.end_ms, - "duration_ms": s.end_ms - s.start_ms, - "activity_score": round(s.activity_score, 4), - "frame_count": s.frame_count, - } - for s in segments - ], - "keyframe_count": len(keyframe_times), - "keyframe_times_sec": [round(t, 2) for t in keyframe_times], - "settings": { - "activity_threshold": activity_threshold, - "scene_threshold": scene_threshold, - "max_duration": max_duration, - }, - } - ) - - elif action == "slice": - if not path: - return json.dumps({"error": "path required (video file)"}) - - if not os.path.exists(path): - return json.dumps({"error": f"File not found: {path}"}) - - # Get params - target_frames = kwargs.get( - "target_frames", self.limits.session_target_frames - ) - activity_threshold = kwargs.get( - "activity_threshold", self.limits.activity_threshold - ) - scene_threshold = kwargs.get( - "scene_threshold", self.limits.scene_change_threshold - ) - max_duration = kwargs.get( - "max_duration", self.limits.session_max_duration - ) - slice_max_size = kwargs.get( - "max_size", 512 - ) # Smaller default for compression - slice_quality = kwargs.get( - "quality", self.limits.session_compression_quality - ) - - # First analyze - segments, keyframe_times = await loop.run_in_executor( - _EXECUTOR, - _analyze_video_activity, - path, - activity_threshold, - 2.0, - max_duration, - scene_threshold, - ) - - # Then extract at activity points - frames = await loop.run_in_executor( - _EXECUTOR, - _extract_activity_frames, - path, - keyframe_times, - target_frames, - slice_max_size, - slice_quality, - ) - - result = MediaResult(success=True) - - for data, info in frames: - if result.total_size + len(data) > self.limits.max_payload_bytes: - result.warnings.append("Payload limit reached") - break - - result.images.append( - { - "timestamp_sec": info["timestamp_sec"], - "timestamp_ms": info["timestamp_ms"], - "size": len(data), - "format": info["format"], - "base64": base64.b64encode(data).decode(), - } - ) - result.total_size += len(data) - result.total_count += 1 - - return json.dumps( - { - **result.to_dict(), - "source": path, - "activity_segments": len(segments), - "keyframes_detected": len(keyframe_times), - "compression_settings": { - "max_size": slice_max_size, - "quality": slice_quality, - }, - } - ) - - elif action == "compress_session": - if not path: - return json.dumps({"error": "path required (video file)"}) - - if not os.path.exists(path): - return json.dumps({"error": f"File not found: {path}"}) - - # Get params - target_frames = kwargs.get( - "target_frames", self.limits.session_target_frames - ) - activity_threshold = kwargs.get( - "activity_threshold", self.limits.activity_threshold - ) - scene_threshold = kwargs.get( - "scene_threshold", self.limits.scene_change_threshold - ) - max_duration = kwargs.get( - "max_duration", self.limits.session_max_duration - ) - session_max_size = kwargs.get("max_size", 512) - session_quality = kwargs.get( - "quality", self.limits.session_compression_quality - ) - - # Full pipeline - frames, segments, metadata = await loop.run_in_executor( - _EXECUTOR, - _compress_session, - path, - max_duration, - target_frames, - activity_threshold, - scene_threshold, - session_max_size, - session_quality, - ) - - result = MediaResult(success=True) - - for data, info in frames: - if result.total_size + len(data) > self.limits.max_payload_bytes: - result.warnings.append("Payload limit reached") - break - - result.images.append( - { - "timestamp_sec": info["timestamp_sec"], - "timestamp_ms": info["timestamp_ms"], - "size": len(data), - "format": info["format"], - "base64": base64.b64encode(data).decode(), - } - ) - result.total_size += len(data) - result.total_count += 1 - - return json.dumps( - { - **result.to_dict(), - **metadata, - "activity_segments": [ - { - "start_ms": s.start_ms, - "end_ms": s.end_ms, - "activity_score": round(s.activity_score, 4), - } - for s in segments - ], - } - ) - - else: - return json.dumps({"error": f"Unknown action: {action}"}) - - except Exception as e: - return json.dumps({"error": str(e)}) - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def media( - action: Annotated[str, Field(description="Action to perform")] = "status", - path: Annotated[Optional[str], Field(description="File path")] = None, - paths: Annotated[ - Optional[list[str]], Field(description="List of file paths") - ] = None, - images: Annotated[ - Optional[list[str]], Field(description="Base64 images or paths") - ] = None, - optimize: Annotated[bool, Field(description="Optimize for Claude")] = True, - max_size: Annotated[ - Optional[int], Field(description="Max dimension") - ] = None, - width: Annotated[Optional[int], Field(description="Target width")] = None, - height: Annotated[Optional[int], Field(description="Target height")] = None, - maintain_aspect: Annotated[ - bool, Field(description="Keep aspect ratio") - ] = True, - quality: Annotated[ - Optional[int], Field(description="JPEG quality 1-100") - ] = None, - count: Annotated[int, Field(description="Frame count")] = 10, - interval_ms: Annotated[int, Field(description="Frame interval ms")] = 1000, - # Activity detection params - target_frames: Annotated[ - Optional[int], Field(description="Target frames for activity slicing") - ] = None, - activity_threshold: Annotated[ - Optional[float], Field(description="Activity sensitivity 0.01-0.10") - ] = None, - scene_threshold: Annotated[ - Optional[float], Field(description="Scene change sensitivity") - ] = None, - max_duration: Annotated[ - Optional[int], Field(description="Max seconds to analyze") - ] = None, - # Limit updates - max_images: Annotated[ - Optional[int], Field(description="Update max images limit") - ] = None, - max_payload_mb: Annotated[ - Optional[float], Field(description="Update max payload MB") - ] = None, - max_resolution: Annotated[ - Optional[int], Field(description="Update max resolution") - ] = None, - ctx: MCPContext = None, - ) -> str: - return await tool_self.call( - ctx, - action=action, - path=path, - paths=paths, - images=images, - optimize=optimize, - max_size=max_size, - width=width, - height=height, - maintain_aspect=maintain_aspect, - quality=quality, - count=count, - interval_ms=interval_ms, - max_images=max_images, - max_payload_mb=max_payload_mb, - max_resolution=max_resolution, - # Pass activity params as kwargs - target_frames=target_frames, - activity_threshold=activity_threshold, - scene_threshold=scene_threshold, - max_duration=max_duration, - ) - - -# Singleton instance -media_tool = MediaTool() diff --git a/pkg/hanzo-tools-computer/hanzo_tools/computer/screen_tool.py b/pkg/hanzo-tools-computer/hanzo_tools/computer/screen_tool.py deleted file mode 100644 index c3b790ec6..000000000 --- a/pkg/hanzo-tools-computer/hanzo_tools/computer/screen_tool.py +++ /dev/null @@ -1,745 +0,0 @@ -"""Screen recording and AI interpretation tool for Hanzo AI. - -One unified tool for capturing, recording, and sending screen data to Claude: -- capture: Single screenshot -- record: Start background recording -- stop: Stop recording and get compressed frames -- session: ONE-SHOT record โ†’ analyze โ†’ compress โ†’ return for Claude - -The 'session' action is the primary interface: -1. Records screen for specified duration (default 30s) -2. Analyzes for activity (movement, scene changes) -3. Extracts only keyframes at activity points -4. Compresses heavily for minimal payload -5. Returns frames ready for Claude interpretation - -Typical usage: - screen(action="session", duration=30) # 30s session - # Returns ~30 compressed frames, ~500KB total - # Claude can interpret the computer use session -""" - -import io -import os -import sys -import json -import time -import base64 -import asyncio -import tempfile -import subprocess -from typing import Any, Literal, Optional, Annotated, final, override -from pathlib import Path -from dataclasses import field, dataclass -from concurrent.futures import ThreadPoolExecutor - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, PermissionManager, auto_timeout - -from .media_tool import ( - MediaLimits, - MediaResult, - ActivitySegment, - _resize_image, - _compress_session, - _analyze_video_activity, - _extract_activity_frames, -) - -# Thread pool for blocking operations -_EXECUTOR = ThreadPoolExecutor(max_workers=2, thread_name_prefix="screen_") - - -@dataclass -class ScreenConfig: - """Configuration for screen capture sessions.""" - - # Session defaults - default_duration: int = 30 # seconds - max_duration: int = 120 # seconds - - # Recording settings - fps: int = 30 - quality: str = "medium" # low, medium, high - - # Compression for Claude - # IMPORTANT: Claude has 2000px max for multi-image requests - # We use 768px for good quality while staying well under limit - target_frames: int = 30 - max_size: int = 768 # pixels (hard capped at 1568 for safety) - jpeg_quality: int = 60 - - # Activity detection - activity_threshold: float = 0.02 - scene_threshold: float = 0.3 - - # Hard limits (Claude API constraints) - HARD_MAX_SIZE: int = 2000 # Claude's actual limit - HARD_MAX_FRAMES: int = 100 # Max frames per request - HARD_MAX_PAYLOAD_MB: float = 32.0 # Max total payload - - @classmethod - def from_env(cls) -> "ScreenConfig": - """Load from environment variables.""" - max_size = int(os.environ.get("HANZO_SCREEN_MAX_SIZE", "768")) - # Enforce hard cap - max_size = min(max_size, cls.HARD_MAX_SIZE) - - return cls( - default_duration=int(os.environ.get("HANZO_SCREEN_DURATION", "30")), - max_duration=int(os.environ.get("HANZO_SCREEN_MAX_DURATION", "120")), - target_frames=min( - int(os.environ.get("HANZO_SCREEN_TARGET_FRAMES", "30")), - cls.HARD_MAX_FRAMES, - ), - max_size=max_size, - jpeg_quality=int(os.environ.get("HANZO_SCREEN_QUALITY", "60")), - activity_threshold=float( - os.environ.get("HANZO_SCREEN_ACTIVITY_THRESHOLD", "0.02") - ), - ) - - -def _capture_screenshot_native(region: Optional[list[int]] = None) -> bytes: - """Capture screenshot using macOS screencapture.""" - with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f: - tmp_path = f.name - - try: - cmd = ["screencapture", "-x", "-t", "png"] - if region and len(region) == 4: - x, y, w, h = region - cmd.extend(["-R", f"{x},{y},{w},{h}"]) - cmd.append(tmp_path) - - subprocess.run(cmd, capture_output=True, timeout=2) - - with open(tmp_path, "rb") as f: - return f.read() - finally: - if os.path.exists(tmp_path): - os.unlink(tmp_path) - - -def _record_screen( - output_path: str, - duration: int, - fps: int = 30, - quality: str = "medium", - region: Optional[list[int]] = None, -) -> str: - """Record screen to file for specified duration. - - Returns path to recorded video file. - """ - quality_map = {"low": "28", "medium": "23", "high": "18"} - crf = quality_map.get(quality, "23") - - cmd = [ - "ffmpeg", - "-y", - "-f", - "avfoundation", - "-framerate", - str(fps), - "-i", - "1:none", # Screen capture - "-t", - str(duration), - "-c:v", - "libx264", - "-crf", - crf, - "-preset", - "ultrafast", - "-pix_fmt", - "yuv420p", - ] - - if region and len(region) == 4: - x, y, w, h = region - cmd.extend(["-vf", f"crop={w}:{h}:{x}:{y}"]) - - cmd.append(output_path) - - # Run and wait for completion - subprocess.run(cmd, capture_output=True, timeout=duration + 30) - - return output_path - - -def _process_recording_for_claude( - video_path: str, - target_frames: int = 30, - max_size: int = 512, - quality: int = 60, - activity_threshold: float = 0.02, - scene_threshold: float = 0.3, -) -> tuple[list[dict], list[dict], dict]: - """Process recorded video for Claude interpretation. - - Returns (frames_with_base64, activity_segments, metadata). - """ - frames, segments, metadata = _compress_session( - video_path, - max_duration=120, - target_frames=target_frames, - activity_threshold=activity_threshold, - scene_threshold=scene_threshold, - max_size=max_size, - quality=quality, - ) - - # Write frames to files instead of returning inline base64 - frames_dir = os.path.join( - str(Path.home()), - ".hanzo", "screen", "frames", - ) - os.makedirs(frames_dir, exist_ok=True) - session_id = int(time.time()) - - frames_data = [] - for i, (data, info) in enumerate(frames): - frame_path = os.path.join(frames_dir, f"session_{session_id}_f{i:03d}.jpg") - with open(frame_path, "wb") as f: - f.write(data) - frames_data.append( - { - "timestamp_sec": info["timestamp_sec"], - "timestamp_ms": info["timestamp_ms"], - "size": len(data), - "path": frame_path, - } - ) - - segments_data = [ - { - "start_ms": s.start_ms, - "end_ms": s.end_ms, - "activity_score": round(s.activity_score, 4), - } - for s in segments - ] - - return frames_data, segments_data, metadata - - -Action = Literal[ - "capture", # Single screenshot - "record", # Start recording (background) - "stop", # Stop recording - "status", # Recording status - "session", # ONE-SHOT: record โ†’ analyze โ†’ compress โ†’ return - "analyze", # Analyze existing video file - "info", # System info -] - - -@final -class ScreenTool(BaseTool): - """Screen recording and AI interpretation tool. - - Primary action: 'session' - records screen, analyzes activity, - compresses, and returns frames ready for Claude interpretation. - - Usage: - screen(action="session", duration=30) - # Returns compressed keyframes from 30-second recording - # Perfect for Claude to interpret computer use sessions - """ - - name = "screen" - - def __init__( - self, - permission_manager: Optional[PermissionManager] = None, - config: Optional[ScreenConfig] = None, - ): - if permission_manager is None: - permission_manager = PermissionManager() - self.permission_manager = permission_manager - self.config = config or ScreenConfig.from_env() - - # Recording state - self._recording = False - self._process: Optional[subprocess.Popen] = None - self._output_file: Optional[str] = None - self._start_time: float = 0 - - # Capabilities - self._has_ffmpeg: Optional[bool] = None - self._has_screencapture: Optional[bool] = None - - def _check_ffmpeg(self) -> bool: - if self._has_ffmpeg is None: - try: - subprocess.run(["ffmpeg", "-version"], capture_output=True, timeout=5) - self._has_ffmpeg = True - except Exception: - self._has_ffmpeg = False - return self._has_ffmpeg - - def _check_screencapture(self) -> bool: - if self._has_screencapture is None: - self._has_screencapture = sys.platform == "darwin" - return self._has_screencapture - - @property - @override - def description(self) -> str: - return f"""Screen recording and AI interpretation for Claude. - -PRIMARY ACTION - session: - Record screen โ†’ Analyze activity โ†’ Compress โ†’ Return for Claude - - screen(action="session", duration=30) - - This one call: - 1. Records screen for {self.config.default_duration}s (configurable) - 2. Detects activity (movement, clicks, typing) - 3. Extracts ~{self.config.target_frames} keyframes at activity points - 4. Compresses to ~{self.config.max_size}px @ {self.config.jpeg_quality}% quality - 5. Returns frames + activity analysis (~500KB total) - - Perfect for Claude to interpret computer use sessions. - -OTHER ACTIONS: - -capture: Single screenshot - - region: [x, y, width, height] (optional) - - optimize: Compress for Claude (default: true) - - Returns file path (image saved to ~/.hanzo/screen/) - -record: Start background recording - - duration: Recording duration in seconds (max {self.config.max_duration}) - - fps: Frame rate (default: 30) - - quality: low/medium/high - - region: Specific screen area - -stop: Stop recording and process - - Returns compressed frames like 'session' - -status: Check recording state - -analyze: Process existing video file - - path: Video file path - - Returns activity analysis + compressed frames - -info: System capabilities - -CONFIGURATION (env vars): - HANZO_SCREEN_DURATION={self.config.default_duration} # Default session duration - HANZO_SCREEN_TARGET_FRAMES={self.config.target_frames} # Target frames per session - HANZO_SCREEN_MAX_SIZE={self.config.max_size} # Max frame dimension - HANZO_SCREEN_QUALITY={self.config.jpeg_quality} # JPEG quality - HANZO_SCREEN_ACTIVITY_THRESHOLD={self.config.activity_threshold} - -EXAMPLES: - screen(action="session") # 30-second session, returns ~30 compressed frames - screen(action="session", duration=60) # 60-second session - screen(action="capture") # Single screenshot - screen(action="capture", region=[0, 0, 1920, 1080]) # Specific area - screen(action="analyze", path="recording.mp4") # Process existing video -""" - - @override - @auto_timeout("screen") - async def call( - self, - ctx: MCPContext, - action: str = "info", - # Session/recording options - duration: Optional[int] = None, - fps: int = 30, - quality: str = "medium", - region: Optional[list[int]] = None, - # Processing options - target_frames: Optional[int] = None, - max_size: Optional[int] = None, - jpeg_quality: Optional[int] = None, - activity_threshold: Optional[float] = None, - # For analyze action - path: Optional[str] = None, - # Capture options - optimize: bool = True, - **kwargs, - ) -> str: - """Execute screen action.""" - if sys.platform != "darwin": - return json.dumps({"error": "screen tool currently only supports macOS"}) - - loop = asyncio.get_event_loop() - - # Apply config defaults with hard caps - duration = duration or self.config.default_duration - duration = min(duration, self.config.max_duration) - target_frames = target_frames or self.config.target_frames - target_frames = min(target_frames, ScreenConfig.HARD_MAX_FRAMES) - max_size = max_size or self.config.max_size - max_size = min(max_size, ScreenConfig.HARD_MAX_SIZE) # Claude 2000px limit - jpeg_quality = jpeg_quality or self.config.jpeg_quality - activity_threshold = activity_threshold or self.config.activity_threshold - - try: - if action == "info": - return json.dumps( - { - "platform": sys.platform, - "has_ffmpeg": self._check_ffmpeg(), - "has_screencapture": self._check_screencapture(), - "recording": self._recording, - "config": { - "default_duration": self.config.default_duration, - "max_duration": self.config.max_duration, - "target_frames": self.config.target_frames, - "max_size": self.config.max_size, - "jpeg_quality": self.config.jpeg_quality, - }, - } - ) - - elif action == "capture": - # Single screenshot - if not self._check_screencapture(): - return json.dumps({"error": "screencapture not available"}) - - data = await loop.run_in_executor( - _EXECUTOR, - _capture_screenshot_native, - region, - ) - - if optimize: - # Compress for Claude - data, info = await loop.run_in_executor( - _EXECUTOR, - _resize_image, - data, - max_size, - jpeg_quality, - True, # force JPEG - ) - format_type = "jpeg" - else: - format_type = "png" - info = {} - - # Write to file instead of returning inline base64 - capture_dir = os.path.join( - str(Path.home()), - ".hanzo", "screen", - ) - os.makedirs(capture_dir, exist_ok=True) - capture_path = os.path.join( - capture_dir, - f"capture_{int(time.time())}.{format_type}", - ) - with open(capture_path, "wb") as f: - f.write(data) - - return json.dumps( - { - "success": True, - "format": format_type, - "size": len(data), - "dimensions": info.get("new_dimensions"), - "path": capture_path, - } - ) - - elif action == "session": - # ONE-SHOT: record โ†’ analyze โ†’ compress โ†’ return - if not self._check_ffmpeg(): - return json.dumps({"error": "FFmpeg required for screen recording"}) - - # Create temp file for recording - with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as f: - video_path = f.name - - try: - # Record screen - await loop.run_in_executor( - _EXECUTOR, - _record_screen, - video_path, - duration, - fps, - quality, - region, - ) - - # Process for Claude - frames_data, segments_data, metadata = await loop.run_in_executor( - _EXECUTOR, - _process_recording_for_claude, - video_path, - target_frames, - max_size, - jpeg_quality, - activity_threshold, - self.config.scene_threshold, - ) - - total_size = sum(f["size"] for f in frames_data) - - return json.dumps( - { - "success": True, - "action": "session", - "duration_seconds": duration, - "frames": frames_data, - "activity_segments": segments_data, - "total_frames": len(frames_data), - "total_size_bytes": total_size, - "total_size_kb": round(total_size / 1024, 1), - "compression_settings": { - "target_frames": target_frames, - "max_size": max_size, - "quality": jpeg_quality, - }, - "metadata": metadata, - } - ) - - finally: - # Cleanup temp file - if os.path.exists(video_path): - os.unlink(video_path) - - elif action == "record": - # Start background recording - if self._recording: - return json.dumps({"error": "Already recording"}) - - if not self._check_ffmpeg(): - return json.dumps({"error": "FFmpeg required"}) - - # Create output file - self._output_file = tempfile.mktemp(suffix=".mp4") # noqa: S306 - - # Build command - quality_map = {"low": "28", "medium": "23", "high": "18"} - crf = quality_map.get(quality, "23") - - cmd = [ - "ffmpeg", - "-y", - "-f", - "avfoundation", - "-framerate", - str(fps), - "-i", - "1:none", - "-t", - str(duration), - "-c:v", - "libx264", - "-crf", - crf, - "-preset", - "ultrafast", - "-pix_fmt", - "yuv420p", - ] - - if region and len(region) == 4: - x, y, w, h = region - cmd.extend(["-vf", f"crop={w}:{h}:{x}:{y}"]) - - cmd.append(self._output_file) - - # Start recording process - self._process = subprocess.Popen( - cmd, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - self._recording = True - self._start_time = time.time() - - return json.dumps( - { - "success": True, - "recording": True, - "duration": duration, - "output": self._output_file, - } - ) - - elif action == "stop": - # Stop recording and process - if not self._recording: - return json.dumps({"error": "Not recording"}) - - # Stop the process - if self._process: - try: - self._process.stdin.write(b"q") - self._process.stdin.flush() - except Exception: - pass - await asyncio.sleep(0.5) - self._process.terminate() - try: - self._process.wait(timeout=5) - except subprocess.TimeoutExpired: - self._process.kill() - self._process = None - - actual_duration = time.time() - self._start_time - video_path = self._output_file - - self._recording = False - self._output_file = None - self._start_time = 0 - - # Process the recording - if video_path and os.path.exists(video_path): - try: - frames_data, segments_data, metadata = ( - await loop.run_in_executor( - _EXECUTOR, - _process_recording_for_claude, - video_path, - target_frames, - max_size, - jpeg_quality, - activity_threshold, - self.config.scene_threshold, - ) - ) - - total_size = sum(f["size"] for f in frames_data) - - return json.dumps( - { - "success": True, - "duration_seconds": round(actual_duration, 2), - "frames": frames_data, - "activity_segments": segments_data, - "total_frames": len(frames_data), - "total_size_kb": round(total_size / 1024, 1), - "metadata": metadata, - } - ) - - finally: - os.unlink(video_path) - else: - return json.dumps({"error": "Recording file not found"}) - - elif action == "status": - result = { - "recording": self._recording, - } - if self._recording: - result["duration_seconds"] = round( - time.time() - self._start_time, 2 - ) - return json.dumps(result) - - elif action == "analyze": - # Analyze existing video file - if not path: - return json.dumps({"error": "path required"}) - - if not os.path.exists(path): - return json.dumps({"error": f"File not found: {path}"}) - - frames_data, segments_data, metadata = await loop.run_in_executor( - _EXECUTOR, - _process_recording_for_claude, - path, - target_frames, - max_size, - jpeg_quality, - activity_threshold, - self.config.scene_threshold, - ) - - total_size = sum(f["size"] for f in frames_data) - - return json.dumps( - { - "success": True, - "source": path, - "frames": frames_data, - "activity_segments": segments_data, - "total_frames": len(frames_data), - "total_size_kb": round(total_size / 1024, 1), - "metadata": metadata, - } - ) - - else: - return json.dumps({"error": f"Unknown action: {action}"}) - - except Exception as e: - return json.dumps({"error": str(e)}) - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def screen( - action: Annotated[ - str, - Field( - description="Action: session, capture, record, stop, analyze, info" - ), - ] = "info", - duration: Annotated[ - Optional[int], Field(description="Recording duration in seconds") - ] = None, - fps: Annotated[int, Field(description="Frame rate for recording")] = 30, - quality: Annotated[ - str, Field(description="Recording quality: low, medium, high") - ] = "medium", - region: Annotated[ - Optional[list[int]], - Field(description="Screen region [x, y, width, height]"), - ] = None, - target_frames: Annotated[ - Optional[int], Field(description="Target number of output frames") - ] = None, - max_size: Annotated[ - Optional[int], Field(description="Max frame dimension in pixels") - ] = None, - jpeg_quality: Annotated[ - Optional[int], Field(description="JPEG compression quality 1-100") - ] = None, - activity_threshold: Annotated[ - Optional[float], Field(description="Activity detection sensitivity") - ] = None, - path: Annotated[ - Optional[str], Field(description="Video file path for analyze action") - ] = None, - optimize: Annotated[ - bool, Field(description="Optimize capture for Claude") - ] = True, - ctx: MCPContext = None, - ) -> str: - return await tool_self.call( - ctx, - action=action, - duration=duration, - fps=fps, - quality=quality, - region=region, - target_frames=target_frames, - max_size=max_size, - jpeg_quality=jpeg_quality, - activity_threshold=activity_threshold, - path=path, - optimize=optimize, - ) - - -# Singleton instance -screen_tool = ScreenTool() diff --git a/pkg/hanzo-tools-computer/hanzo_tools/computer/ui_tool.py b/pkg/hanzo-tools-computer/hanzo_tools/computer/ui_tool.py deleted file mode 100644 index e1f53ce48..000000000 --- a/pkg/hanzo-tools-computer/hanzo_tools/computer/ui_tool.py +++ /dev/null @@ -1,68 +0,0 @@ -"""Unified UI tool for HIP-0300 architecture. - -This module provides a single unified 'ui' tool that consolidates: -- Computer control (click, type, screenshot, etc.) -- Screen recording (session, record, stop, analyze) - -Following Unix philosophy: one tool for the Interface axis. -""" - -from typing import Any, ClassVar - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import PermissionManager - -from .computer_tool import ComputerTool - - -class UiTool(ComputerTool): - """Unified UI tool (HIP-0300). - - Consolidates computer + screen tools into single 'ui' tool. - Inherits all functionality from ComputerTool. - - Actions: - - Mouse: click, double_click, right_click, middle_click, move, drag, scroll - - Touch: tap, swipe, pinch - - Keyboard: type, write, press, key_down, key_up, hotkey - - Screen capture: screenshot, screenshot_region, capture - - Screen recording: session, record, stop, analyze - - Image location: locate, locate_all, locate_center, wait_for_image - - Window: get_active_window, list_windows, focus_window - - Screen info: get_screens, screen_size, current_screen - - Region: define_region, region_screenshot, region_locate - - Batch: batch - """ - - # Override name to 'ui' for HIP-0300 - name: ClassVar[str] = "computer" - - @property - def description(self) -> str: - return """Unified interface control tool (HIP-0300). - -Actions: -- click, double_click, right_click, middle_click, move, drag, scroll -- tap, swipe, pinch (touch/mobile) -- type, write, press, key_down, key_up, hotkey (keyboard) -- screenshot, screenshot_region, capture -- session, record, stop, analyze (screen recording) -- focus_window, list_windows, get_active_window -- get_screens, screen_size - -Cross-platform: macOS (Quartz), Linux (xdotool), Windows (win32api). -""" - - def register(self, mcp_server: FastMCP) -> None: - """Register as 'ui' tool with MCP server.""" - # Delegate to parent registration but with 'ui' name - super().register(mcp_server) - - -# Backward compatibility aliases -ComputerToolAlias = UiTool - -# For imports: from hanzo_tools.computer import ui_tool -ui_tool = UiTool diff --git a/pkg/hanzo-tools-computer/pyproject.toml b/pkg/hanzo-tools-computer/pyproject.toml deleted file mode 100644 index 43ee57cae..000000000 --- a/pkg/hanzo-tools-computer/pyproject.toml +++ /dev/null @@ -1,43 +0,0 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[project] -name = "hanzo-tools-computer" -version = "0.5.4" -description = "Computer control tools for Hanzo AI - automation, screen recording, activity detection, session compression for Claude (100 images/32MB/2000px limits)" -readme = "README.md" -license = "MIT" -requires-python = ">=3.12" -authors = [ - { name = "Hanzo AI", email = "dev@hanzo.ai" }, -] -keywords = ["hanzo", "ai", "tools", "computer", "automation", "pyautogui", "mcp"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", -] -dependencies = [ - "hanzo-tools>=0.3.0", - "pyautogui>=0.9.54", - "pillow>=10.0.0", - "numpy>=1.26.0", - "mcp>=1.25.0", - "pydantic>=2.0.0", -] - -[project.optional-dependencies] -dev = [ - "pytest>=8.0.0", - "pytest-asyncio>=0.24.0", -] - -[project.entry-points."hanzo.tools"] -computer = "hanzo_tools.computer:TOOLS" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] diff --git a/pkg/hanzo-tools-computer/tests/test_computer_tools.py b/pkg/hanzo-tools-computer/tests/test_computer_tools.py deleted file mode 100644 index dec283150..000000000 --- a/pkg/hanzo-tools-computer/tests/test_computer_tools.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Tests for hanzo-tools-computer.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import computer - - assert computer is not None - - def test_import_tools(self): - from hanzo_tools.computer import TOOLS - - assert len(TOOLS) > 0 - - def test_import_computer_tool(self): - from hanzo_tools.computer import ComputerTool - - assert ComputerTool.name == "computer" - - -class TestComputerTool: - """Tests for ComputerTool.""" - - @pytest.fixture - def tool(self): - from hanzo_tools.computer import ComputerTool - - return ComputerTool() - - def test_has_description(self, tool): - assert tool.description diff --git a/pkg/hanzo-tools-computer/uv.lock b/pkg/hanzo-tools-computer/uv.lock deleted file mode 100644 index 56e4560ed..000000000 --- a/pkg/hanzo-tools-computer/uv.lock +++ /dev/null @@ -1,1822 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" - -[[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.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, -] - -[[package]] -name = "attrs" -version = "25.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, -] - -[[package]] -name = "authlib" -version = "1.6.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/9b/b1661026ff24bc641b76b78c5222d614776b0c085bcfdac9bd15a1cb4b35/authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e", size = 164894, upload-time = "2025-12-12T08:01:41.464Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/51/321e821856452f7386c4e9df866f196720b1ad0c5ea1623ea7399969ae3b/authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd", size = 244005, upload-time = "2025-12-12T08:01:40.209Z" }, -] - -[[package]] -name = "beartype" -version = "0.22.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, -] - -[[package]] -name = "cachetools" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/af/df70e9b65bc77a1cbe0768c0aa4617147f30f8306ded98c1744bcdc0ae1e/cachetools-7.0.0.tar.gz", hash = "sha256:a9abf18ff3b86c7d05b27ead412e235e16ae045925e531fae38d5fada5ed5b08", size = 35796, upload-time = "2026-02-01T18:59:47.411Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/df/2dd32cce20cbcf6f2ec456b58d44368161ad28320729f64e5e1d5d7bd0ae/cachetools-7.0.0-py3-none-any.whl", hash = "sha256:d52fef60e6e964a1969cfb61ccf6242a801b432790fe520d78720d757c81cbd2", size = 13487, upload-time = "2026-02-01T18:59:45.981Z" }, -] - -[[package]] -name = "certifi" -version = "2026.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, - { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, - { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, - { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, - { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, - { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, - { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, - { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, - { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, -] - -[[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 = "cryptography" -version = "46.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/19/f748958276519adf6a0c1e79e7b8860b4830dda55ccdf29f2719b5fc499c/cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59", size = 749301, upload-time = "2026-01-28T00:24:37.379Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/99/157aae7949a5f30d51fcb1a9851e8ebd5c74bf99b5285d8bb4b8b9ee641e/cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485", size = 7173686, upload-time = "2026-01-28T00:23:07.515Z" }, - { url = "https://files.pythonhosted.org/packages/87/91/874b8910903159043b5c6a123b7e79c4559ddd1896e38967567942635778/cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc", size = 4275871, upload-time = "2026-01-28T00:23:09.439Z" }, - { url = "https://files.pythonhosted.org/packages/c0/35/690e809be77896111f5b195ede56e4b4ed0435b428c2f2b6d35046fbb5e8/cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0", size = 4423124, upload-time = "2026-01-28T00:23:11.529Z" }, - { url = "https://files.pythonhosted.org/packages/1a/5b/a26407d4f79d61ca4bebaa9213feafdd8806dc69d3d290ce24996d3cfe43/cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa", size = 4277090, upload-time = "2026-01-28T00:23:13.123Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d8/4bb7aec442a9049827aa34cee1aa83803e528fa55da9a9d45d01d1bb933e/cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81", size = 4947652, upload-time = "2026-01-28T00:23:14.554Z" }, - { url = "https://files.pythonhosted.org/packages/2b/08/f83e2e0814248b844265802d081f2fac2f1cbe6cd258e72ba14ff006823a/cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255", size = 4455157, upload-time = "2026-01-28T00:23:16.443Z" }, - { url = "https://files.pythonhosted.org/packages/0a/05/19d849cf4096448779d2dcc9bb27d097457dac36f7273ffa875a93b5884c/cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e", size = 3981078, upload-time = "2026-01-28T00:23:17.838Z" }, - { url = "https://files.pythonhosted.org/packages/e6/89/f7bac81d66ba7cde867a743ea5b37537b32b5c633c473002b26a226f703f/cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c", size = 4276213, upload-time = "2026-01-28T00:23:19.257Z" }, - { url = "https://files.pythonhosted.org/packages/da/9f/7133e41f24edd827020ad21b068736e792bc68eecf66d93c924ad4719fb3/cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32", size = 4912190, upload-time = "2026-01-28T00:23:21.244Z" }, - { url = "https://files.pythonhosted.org/packages/a6/f7/6d43cbaddf6f65b24816e4af187d211f0bc536a29961f69faedc48501d8e/cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616", size = 4454641, upload-time = "2026-01-28T00:23:22.866Z" }, - { url = "https://files.pythonhosted.org/packages/9e/4f/ebd0473ad656a0ac912a16bd07db0f5d85184924e14fc88feecae2492834/cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0", size = 4405159, upload-time = "2026-01-28T00:23:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d1/f7/7923886f32dc47e27adeff8246e976d77258fd2aa3efdd1754e4e323bf49/cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0", size = 4666059, upload-time = "2026-01-28T00:23:26.766Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a7/0fca0fd3591dffc297278a61813d7f661a14243dd60f499a7a5b48acb52a/cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5", size = 3026378, upload-time = "2026-01-28T00:23:28.317Z" }, - { url = "https://files.pythonhosted.org/packages/2d/12/652c84b6f9873f0909374864a57b003686c642ea48c84d6c7e2c515e6da5/cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b", size = 3478614, upload-time = "2026-01-28T00:23:30.275Z" }, - { url = "https://files.pythonhosted.org/packages/b9/27/542b029f293a5cce59349d799d4d8484b3b1654a7b9a0585c266e974a488/cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908", size = 7116417, upload-time = "2026-01-28T00:23:31.958Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f5/559c25b77f40b6bf828eabaf988efb8b0e17b573545edb503368ca0a2a03/cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da", size = 4264508, upload-time = "2026-01-28T00:23:34.264Z" }, - { url = "https://files.pythonhosted.org/packages/49/a1/551fa162d33074b660dc35c9bc3616fefa21a0e8c1edd27b92559902e408/cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829", size = 4409080, upload-time = "2026-01-28T00:23:35.793Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/4d8d129a755f5d6df1bbee69ea2f35ebfa954fa1847690d1db2e8bca46a5/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2", size = 4270039, upload-time = "2026-01-28T00:23:37.263Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f5/ed3fcddd0a5e39321e595e144615399e47e7c153a1fb8c4862aec3151ff9/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085", size = 4926748, upload-time = "2026-01-28T00:23:38.884Z" }, - { url = "https://files.pythonhosted.org/packages/43/ae/9f03d5f0c0c00e85ecb34f06d3b79599f20630e4db91b8a6e56e8f83d410/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b", size = 4442307, upload-time = "2026-01-28T00:23:40.56Z" }, - { url = "https://files.pythonhosted.org/packages/8b/22/e0f9f2dae8040695103369cf2283ef9ac8abe4d51f68710bec2afd232609/cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd", size = 3959253, upload-time = "2026-01-28T00:23:42.827Z" }, - { url = "https://files.pythonhosted.org/packages/01/5b/6a43fcccc51dae4d101ac7d378a8724d1ba3de628a24e11bf2f4f43cba4d/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2", size = 4269372, upload-time = "2026-01-28T00:23:44.655Z" }, - { url = "https://files.pythonhosted.org/packages/17/b7/0f6b8c1dd0779df2b526e78978ff00462355e31c0a6f6cff8a3e99889c90/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e", size = 4891908, upload-time = "2026-01-28T00:23:46.48Z" }, - { url = "https://files.pythonhosted.org/packages/83/17/259409b8349aa10535358807a472c6a695cf84f106022268d31cea2b6c97/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f", size = 4441254, upload-time = "2026-01-28T00:23:48.403Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fe/e4a1b0c989b00cee5ffa0764401767e2d1cf59f45530963b894129fd5dce/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82", size = 4396520, upload-time = "2026-01-28T00:23:50.26Z" }, - { url = "https://files.pythonhosted.org/packages/b3/81/ba8fd9657d27076eb40d6a2f941b23429a3c3d2f56f5a921d6b936a27bc9/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c", size = 4651479, upload-time = "2026-01-28T00:23:51.674Z" }, - { url = "https://files.pythonhosted.org/packages/00/03/0de4ed43c71c31e4fe954edd50b9d28d658fef56555eba7641696370a8e2/cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061", size = 3001986, upload-time = "2026-01-28T00:23:53.485Z" }, - { url = "https://files.pythonhosted.org/packages/5c/70/81830b59df7682917d7a10f833c4dab2a5574cd664e86d18139f2b421329/cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7", size = 3468288, upload-time = "2026-01-28T00:23:55.09Z" }, - { url = "https://files.pythonhosted.org/packages/56/f7/f648fdbb61d0d45902d3f374217451385edc7e7768d1b03ff1d0e5ffc17b/cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab", size = 7169583, upload-time = "2026-01-28T00:23:56.558Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cc/8f3224cbb2a928de7298d6ed4790f5ebc48114e02bdc9559196bfb12435d/cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef", size = 4275419, upload-time = "2026-01-28T00:23:58.364Z" }, - { url = "https://files.pythonhosted.org/packages/17/43/4a18faa7a872d00e4264855134ba82d23546c850a70ff209e04ee200e76f/cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d", size = 4419058, upload-time = "2026-01-28T00:23:59.867Z" }, - { url = "https://files.pythonhosted.org/packages/ee/64/6651969409821d791ba12346a124f55e1b76f66a819254ae840a965d4b9c/cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973", size = 4278151, upload-time = "2026-01-28T00:24:01.731Z" }, - { url = "https://files.pythonhosted.org/packages/20/0b/a7fce65ee08c3c02f7a8310cc090a732344066b990ac63a9dfd0a655d321/cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4", size = 4939441, upload-time = "2026-01-28T00:24:03.175Z" }, - { url = "https://files.pythonhosted.org/packages/db/a7/20c5701e2cd3e1dfd7a19d2290c522a5f435dd30957d431dcb531d0f1413/cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af", size = 4451617, upload-time = "2026-01-28T00:24:05.403Z" }, - { url = "https://files.pythonhosted.org/packages/00/dc/3e16030ea9aa47b63af6524c354933b4fb0e352257c792c4deeb0edae367/cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263", size = 3977774, upload-time = "2026-01-28T00:24:06.851Z" }, - { url = "https://files.pythonhosted.org/packages/42/c8/ad93f14118252717b465880368721c963975ac4b941b7ef88f3c56bf2897/cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095", size = 4277008, upload-time = "2026-01-28T00:24:08.926Z" }, - { url = "https://files.pythonhosted.org/packages/00/cf/89c99698151c00a4631fbfcfcf459d308213ac29e321b0ff44ceeeac82f1/cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b", size = 4903339, upload-time = "2026-01-28T00:24:12.009Z" }, - { url = "https://files.pythonhosted.org/packages/03/c3/c90a2cb358de4ac9309b26acf49b2a100957e1ff5cc1e98e6c4996576710/cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019", size = 4451216, upload-time = "2026-01-28T00:24:13.975Z" }, - { url = "https://files.pythonhosted.org/packages/96/2c/8d7f4171388a10208671e181ca43cdc0e596d8259ebacbbcfbd16de593da/cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4", size = 4404299, upload-time = "2026-01-28T00:24:16.169Z" }, - { url = "https://files.pythonhosted.org/packages/e9/23/cbb2036e450980f65c6e0a173b73a56ff3bccd8998965dea5cc9ddd424a5/cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b", size = 4664837, upload-time = "2026-01-28T00:24:17.629Z" }, - { url = "https://files.pythonhosted.org/packages/0a/21/f7433d18fe6d5845329cbdc597e30caf983229c7a245bcf54afecc555938/cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc", size = 3009779, upload-time = "2026-01-28T00:24:20.198Z" }, - { url = "https://files.pythonhosted.org/packages/3a/6a/bd2e7caa2facffedf172a45c1a02e551e6d7d4828658c9a245516a598d94/cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976", size = 3466633, upload-time = "2026-01-28T00:24:21.851Z" }, -] - -[[package]] -name = "cyclopts" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "docstring-parser" }, - { name = "rich" }, - { name = "rich-rst" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d4/93/6085aa89c3fff78a5180987354538d72e43b0db27e66a959302d0c07821a/cyclopts-4.5.1.tar.gz", hash = "sha256:fadc45304763fd9f5d6033727f176898d17a1778e194436964661a005078a3dd", size = 162075, upload-time = "2026-01-25T15:23:54.07Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/7c/996760c30f1302704af57c66ff2d723f7d656d0d0b93563b5528a51484bb/cyclopts-4.5.1-py3-none-any.whl", hash = "sha256:0642c93601e554ca6b7b9abd81093847ea4448b2616280f2a0952416574e8c7a", size = 199807, upload-time = "2026-01-25T15:23:55.219Z" }, -] - -[[package]] -name = "diskcache" -version = "5.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, -] - -[[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.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, -] - -[[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.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "fakeredis" -version = "2.33.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "redis" }, - { name = "sortedcontainers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/f9/57464119936414d60697fcbd32f38909bb5688b616ae13de6e98384433e0/fakeredis-2.33.0.tar.gz", hash = "sha256:d7bc9a69d21df108a6451bbffee23b3eba432c21a654afc7ff2d295428ec5770", size = 175187, upload-time = "2025-12-16T19:45:52.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/78/a850fed8aeef96d4a99043c90b818b2ed5419cd5b24a4049fd7cfb9f1471/fakeredis-2.33.0-py3-none-any.whl", hash = "sha256:de535f3f9ccde1c56672ab2fdd6a8efbc4f2619fc2f1acc87b8737177d71c965", size = 119605, upload-time = "2025-12-16T19:45:51.08Z" }, -] - -[package.optional-dependencies] -lua = [ - { name = "lupa" }, -] - -[[package]] -name = "fastmcp" -version = "2.14.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "authlib" }, - { name = "cyclopts" }, - { name = "exceptiongroup" }, - { name = "httpx" }, - { name = "jsonref" }, - { name = "jsonschema-path" }, - { name = "mcp" }, - { name = "openapi-pydantic" }, - { name = "packaging" }, - { name = "platformdirs" }, - { name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] }, - { name = "pydantic", extra = ["email"] }, - { name = "pydocket" }, - { name = "pyperclip" }, - { name = "python-dotenv" }, - { name = "rich" }, - { name = "uvicorn" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/a9/a57d5e5629ebd4ef82b495a7f8e346ce29ef80cc86b15c8c40570701b94d/fastmcp-2.14.4.tar.gz", hash = "sha256:c01f19845c2adda0a70d59525c9193be64a6383014c8d40ce63345ac664053ff", size = 8302239, upload-time = "2026-01-22T17:29:37.024Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/41/c4d407e2218fd60d84acb6cc5131d28ff876afecf325e3fd9d27b8318581/fastmcp-2.14.4-py3-none-any.whl", hash = "sha256:5858cff5e4c8ea8107f9bca2609d71d6256e0fce74495912f6e51625e466c49a", size = 417788, upload-time = "2026-01-22T17:29:35.159Z" }, -] - -[[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 = "hanzo-tools" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/3e/2d94dc54e202bdb11f6e4597dd68eebc554d2b92fffb4f6918cdf3f91fe2/hanzo_tools-0.3.0.tar.gz", hash = "sha256:d00cb3212a707e22f9bb5a21f0f9eb34a74f22ff2b5f24e2f8b6321f9880e2fb", size = 10929, upload-time = "2025-12-27T18:56:38.843Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/07/6ebbcf371aafa5f2d171de2916ef92c73978b927b34a8863af53e1b1a80b/hanzo_tools-0.3.0-py3-none-any.whl", hash = "sha256:c7b0f6f7c3089f06329bc1aaca39fbce4b7108fdbd048e2bbc450a3aff9941f2", size = 11928, upload-time = "2025-12-27T18:56:37.528Z" }, -] - -[[package]] -name = "hanzo-tools-computer" -version = "0.5.2" -source = { editable = "." } -dependencies = [ - { name = "hanzo-tools" }, - { name = "mcp" }, - { name = "numpy" }, - { name = "pillow" }, - { name = "pyautogui" }, - { name = "pydantic" }, -] - -[package.optional-dependencies] -dev = [ - { name = "pytest" }, - { name = "pytest-asyncio" }, -] - -[package.metadata] -requires-dist = [ - { name = "hanzo-tools", specifier = ">=0.3.0" }, - { name = "mcp", specifier = ">=1.25.0" }, - { name = "numpy", specifier = ">=1.26.0" }, - { name = "pillow", specifier = ">=10.0.0" }, - { name = "pyautogui", specifier = ">=0.9.54" }, - { name = "pydantic", specifier = ">=2.0.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, - { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24.0" }, -] -provides-extras = ["dev"] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, -] - -[[package]] -name = "jaraco-context" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" }, -] - -[[package]] -name = "jaraco-functools" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, -] - -[[package]] -name = "jsonref" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[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.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "keyring" -version = "25.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, -] - -[[package]] -name = "lupa" -version = "2.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/1c/191c3e6ec6502e3dbe25a53e27f69a5daeac3e56de1f73c0138224171ead/lupa-2.6.tar.gz", hash = "sha256:9a770a6e89576be3447668d7ced312cd6fd41d3c13c2462c9dc2c2ab570e45d9", size = 7240282, upload-time = "2025-10-24T07:20:29.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/86/ce243390535c39d53ea17ccf0240815e6e457e413e40428a658ea4ee4b8d/lupa-2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47ce718817ef1cc0c40d87c3d5ae56a800d61af00fbc0fad1ca9be12df2f3b56", size = 951707, upload-time = "2025-10-24T07:18:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/cedea5e6cbeb54396fdcc55f6b741696f3f036d23cfaf986d50d680446da/lupa-2.6-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:7aba985b15b101495aa4b07112cdc08baa0c545390d560ad5cfde2e9e34f4d58", size = 1916703, upload-time = "2025-10-24T07:18:05.6Z" }, - { url = "https://files.pythonhosted.org/packages/24/be/3d6b5f9a8588c01a4d88129284c726017b2089f3a3fd3ba8bd977292fea0/lupa-2.6-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:b766f62f95b2739f2248977d29b0722e589dcf4f0ccfa827ccbd29f0148bd2e5", size = 985152, upload-time = "2025-10-24T07:18:08.561Z" }, - { url = "https://files.pythonhosted.org/packages/eb/23/9f9a05beee5d5dce9deca4cb07c91c40a90541fc0a8e09db4ee670da550f/lupa-2.6-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:00a934c23331f94cb51760097ebfab14b005d55a6b30a2b480e3c53dd2fa290d", size = 1159599, upload-time = "2025-10-24T07:18:10.346Z" }, - { url = "https://files.pythonhosted.org/packages/40/4e/e7c0583083db9d7f1fd023800a9767d8e4391e8330d56c2373d890ac971b/lupa-2.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21de9f38bd475303e34a042b7081aabdf50bd9bafd36ce4faea2f90fd9f15c31", size = 1038686, upload-time = "2025-10-24T07:18:12.112Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/5a4f7d959d4feba5e203ff0c31889e74d1ca3153122be4a46dca7d92bf7c/lupa-2.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf3bda96d3fc41237e964a69c23647d50d4e28421111360274d4799832c560e9", size = 2071956, upload-time = "2025-10-24T07:18:14.572Z" }, - { url = "https://files.pythonhosted.org/packages/92/34/2f4f13ca65d01169b1720176aedc4af17bc19ee834598c7292db232cb6dc/lupa-2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a76ead245da54801a81053794aa3975f213221f6542d14ec4b859ee2e7e0323", size = 1057199, upload-time = "2025-10-24T07:18:16.379Z" }, - { url = "https://files.pythonhosted.org/packages/35/2a/5f7d2eebec6993b0dcd428e0184ad71afb06a45ba13e717f6501bfed1da3/lupa-2.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8dd0861741caa20886ddbda0a121d8e52fb9b5bb153d82fa9bba796962bf30e8", size = 1173693, upload-time = "2025-10-24T07:18:18.153Z" }, - { url = "https://files.pythonhosted.org/packages/e4/29/089b4d2f8e34417349af3904bb40bec40b65c8731f45e3fd8d497ca573e5/lupa-2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:239e63948b0b23023f81d9a19a395e768ed3da6a299f84e7963b8f813f6e3f9c", size = 2164394, upload-time = "2025-10-24T07:18:20.403Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1b/79c17b23c921f81468a111cad843b076a17ef4b684c4a8dff32a7969c3f0/lupa-2.6-cp312-cp312-win32.whl", hash = "sha256:325894e1099499e7a6f9c351147661a2011887603c71086d36fe0f964d52d1ce", size = 1420647, upload-time = "2025-10-24T07:18:23.368Z" }, - { url = "https://files.pythonhosted.org/packages/b8/15/5121e68aad3584e26e1425a5c9a79cd898f8a152292059e128c206ee817c/lupa-2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c735a1ce8ee60edb0fe71d665f1e6b7c55c6021f1d340eb8c865952c602cd36f", size = 1688529, upload-time = "2025-10-24T07:18:25.523Z" }, - { url = "https://files.pythonhosted.org/packages/28/1d/21176b682ca5469001199d8b95fa1737e29957a3d185186e7a8b55345f2e/lupa-2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:663a6e58a0f60e7d212017d6678639ac8df0119bc13c2145029dcba084391310", size = 947232, upload-time = "2025-10-24T07:18:27.878Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4c/d327befb684660ca13cf79cd1f1d604331808f9f1b6fb6bf57832f8edf80/lupa-2.6-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:d1f5afda5c20b1f3217a80e9bc1b77037f8a6eb11612fd3ada19065303c8f380", size = 1908625, upload-time = "2025-10-24T07:18:29.944Z" }, - { url = "https://files.pythonhosted.org/packages/66/8e/ad22b0a19454dfd08662237a84c792d6d420d36b061f239e084f29d1a4f3/lupa-2.6-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:26f2b3c085fe76e9119e48c1013c1cccdc1f51585d456858290475aa38e7089e", size = 981057, upload-time = "2025-10-24T07:18:31.553Z" }, - { url = "https://files.pythonhosted.org/packages/5c/48/74859073ab276bd0566c719f9ca0108b0cfc1956ca0d68678d117d47d155/lupa-2.6-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:60d2f902c7b96fb8ab98493dcff315e7bb4d0b44dc9dd76eb37de575025d5685", size = 1156227, upload-time = "2025-10-24T07:18:33.981Z" }, - { url = "https://files.pythonhosted.org/packages/09/6c/0e9ded061916877253c2266074060eb71ed99fb21d73c8c114a76725bce2/lupa-2.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a02d25dee3a3250967c36590128d9220ae02f2eda166a24279da0b481519cbff", size = 1035752, upload-time = "2025-10-24T07:18:36.32Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ef/f8c32e454ef9f3fe909f6c7d57a39f950996c37a3deb7b391fec7903dab7/lupa-2.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eae1ee16b886b8914ff292dbefbf2f48abfbdee94b33a88d1d5475e02423203", size = 2069009, upload-time = "2025-10-24T07:18:38.072Z" }, - { url = "https://files.pythonhosted.org/packages/53/dc/15b80c226a5225815a890ee1c11f07968e0aba7a852df41e8ae6fe285063/lupa-2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0edd5073a4ee74ab36f74fe61450148e6044f3952b8d21248581f3c5d1a58be", size = 1056301, upload-time = "2025-10-24T07:18:40.165Z" }, - { url = "https://files.pythonhosted.org/packages/31/14/2086c1425c985acfb30997a67e90c39457122df41324d3c179d6ee2292c6/lupa-2.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c53ee9f22a8a17e7d4266ad48e86f43771951797042dd51d1494aaa4f5f3f0a", size = 1170673, upload-time = "2025-10-24T07:18:42.426Z" }, - { url = "https://files.pythonhosted.org/packages/10/e5/b216c054cf86576c0191bf9a9f05de6f7e8e07164897d95eea0078dca9b2/lupa-2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de7c0f157a9064a400d828789191a96da7f4ce889969a588b87ec80de9b14772", size = 2162227, upload-time = "2025-10-24T07:18:46.112Z" }, - { url = "https://files.pythonhosted.org/packages/59/2f/33ecb5bedf4f3bc297ceacb7f016ff951331d352f58e7e791589609ea306/lupa-2.6-cp313-cp313-win32.whl", hash = "sha256:ee9523941ae0a87b5b703417720c5d78f72d2f5bc23883a2ea80a949a3ed9e75", size = 1419558, upload-time = "2025-10-24T07:18:48.371Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b4/55e885834c847ea610e111d87b9ed4768f0afdaeebc00cd46810f25029f6/lupa-2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b1335a5835b0a25ebdbc75cf0bda195e54d133e4d994877ef025e218c2e59db9", size = 1683424, upload-time = "2025-10-24T07:18:50.976Z" }, - { url = "https://files.pythonhosted.org/packages/66/9d/d9427394e54d22a35d1139ef12e845fd700d4872a67a34db32516170b746/lupa-2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dcb6d0a3264873e1653bc188499f48c1fb4b41a779e315eba45256cfe7bc33c1", size = 953818, upload-time = "2025-10-24T07:18:53.378Z" }, - { url = "https://files.pythonhosted.org/packages/10/41/27bbe81953fb2f9ecfced5d9c99f85b37964cfaf6aa8453bb11283983721/lupa-2.6-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:a37e01f2128f8c36106726cb9d360bac087d58c54b4522b033cc5691c584db18", size = 1915850, upload-time = "2025-10-24T07:18:55.259Z" }, - { url = "https://files.pythonhosted.org/packages/a3/98/f9ff60db84a75ba8725506bbf448fb085bc77868a021998ed2a66d920568/lupa-2.6-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:458bd7e9ff3c150b245b0fcfbb9bd2593d1152ea7f0a7b91c1d185846da033fe", size = 982344, upload-time = "2025-10-24T07:18:57.05Z" }, - { url = "https://files.pythonhosted.org/packages/41/f7/f39e0f1c055c3b887d86b404aaf0ca197b5edfd235a8b81b45b25bac7fc3/lupa-2.6-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:052ee82cac5206a02df77119c325339acbc09f5ce66967f66a2e12a0f3211cad", size = 1156543, upload-time = "2025-10-24T07:18:59.251Z" }, - { url = "https://files.pythonhosted.org/packages/9e/9c/59e6cffa0d672d662ae17bd7ac8ecd2c89c9449dee499e3eb13ca9cd10d9/lupa-2.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96594eca3c87dd07938009e95e591e43d554c1dbd0385be03c100367141db5a8", size = 1047974, upload-time = "2025-10-24T07:19:01.449Z" }, - { url = "https://files.pythonhosted.org/packages/23/c6/a04e9cef7c052717fcb28fb63b3824802488f688391895b618e39be0f684/lupa-2.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8faddd9d198688c8884091173a088a8e920ecc96cda2ffed576a23574c4b3f6", size = 2073458, upload-time = "2025-10-24T07:19:03.369Z" }, - { url = "https://files.pythonhosted.org/packages/e6/10/824173d10f38b51fc77785228f01411b6ca28826ce27404c7c912e0e442c/lupa-2.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:daebb3a6b58095c917e76ba727ab37b27477fb926957c825205fbda431552134", size = 1067683, upload-time = "2025-10-24T07:19:06.2Z" }, - { url = "https://files.pythonhosted.org/packages/b6/dc/9692fbcf3c924d9c4ece2d8d2f724451ac2e09af0bd2a782db1cef34e799/lupa-2.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f3154e68972befe0f81564e37d8142b5d5d79931a18309226a04ec92487d4ea3", size = 1171892, upload-time = "2025-10-24T07:19:08.544Z" }, - { url = "https://files.pythonhosted.org/packages/84/ff/e318b628d4643c278c96ab3ddea07fc36b075a57383c837f5b11e537ba9d/lupa-2.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e4dadf77b9fedc0bfa53417cc28dc2278a26d4cbd95c29f8927ad4d8fe0a7ef9", size = 2166641, upload-time = "2025-10-24T07:19:10.485Z" }, - { url = "https://files.pythonhosted.org/packages/12/f7/a6f9ec2806cf2d50826980cdb4b3cffc7691dc6f95e13cc728846d5cb793/lupa-2.6-cp314-cp314-win32.whl", hash = "sha256:cb34169c6fa3bab3e8ac58ca21b8a7102f6a94b6a5d08d3636312f3f02fafd8f", size = 1456857, upload-time = "2025-10-24T07:19:37.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/de/df71896f25bdc18360fdfa3b802cd7d57d7fede41a0e9724a4625b412c85/lupa-2.6-cp314-cp314-win_amd64.whl", hash = "sha256:b74f944fe46c421e25d0f8692aef1e842192f6f7f68034201382ac440ef9ea67", size = 1731191, upload-time = "2025-10-24T07:19:40.281Z" }, - { url = "https://files.pythonhosted.org/packages/47/3c/a1f23b01c54669465f5f4c4083107d496fbe6fb45998771420e9aadcf145/lupa-2.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0e21b716408a21ab65723f8841cf7f2f37a844b7a965eeabb785e27fca4099cf", size = 999343, upload-time = "2025-10-24T07:19:12.519Z" }, - { url = "https://files.pythonhosted.org/packages/c5/6d/501994291cb640bfa2ccf7f554be4e6914afa21c4026bd01bff9ca8aac57/lupa-2.6-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:589db872a141bfff828340079bbdf3e9a31f2689f4ca0d88f97d9e8c2eae6142", size = 2000730, upload-time = "2025-10-24T07:19:14.869Z" }, - { url = "https://files.pythonhosted.org/packages/53/a5/457ffb4f3f20469956c2d4c4842a7675e884efc895b2f23d126d23e126cc/lupa-2.6-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:cd852a91a4a9d4dcbb9a58100f820a75a425703ec3e3f049055f60b8533b7953", size = 1021553, upload-time = "2025-10-24T07:19:17.123Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/36bb5a5d0960f2a5c7c700e0819abb76fd9bf9c1d8a66e5106416d6e9b14/lupa-2.6-cp314-cp314t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:0334753be028358922415ca97a64a3048e4ed155413fc4eaf87dd0a7e2752983", size = 1133275, upload-time = "2025-10-24T07:19:20.51Z" }, - { url = "https://files.pythonhosted.org/packages/19/86/202ff4429f663013f37d2229f6176ca9f83678a50257d70f61a0a97281bf/lupa-2.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:661d895cd38c87658a34780fac54a690ec036ead743e41b74c3fb81a9e65a6aa", size = 1038441, upload-time = "2025-10-24T07:19:22.509Z" }, - { url = "https://files.pythonhosted.org/packages/a7/42/d8125f8e420714e5b52e9c08d88b5329dfb02dcca731b4f21faaee6cc5b5/lupa-2.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aa58454ccc13878cc177c62529a2056be734da16369e451987ff92784994ca7", size = 2058324, upload-time = "2025-10-24T07:19:24.979Z" }, - { url = "https://files.pythonhosted.org/packages/2b/2c/47bf8b84059876e877a339717ddb595a4a7b0e8740bacae78ba527562e1c/lupa-2.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1425017264e470c98022bba8cff5bd46d054a827f5df6b80274f9cc71dafd24f", size = 1060250, upload-time = "2025-10-24T07:19:27.262Z" }, - { url = "https://files.pythonhosted.org/packages/c2/06/d88add2b6406ca1bdec99d11a429222837ca6d03bea42ca75afa169a78cb/lupa-2.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:224af0532d216e3105f0a127410f12320f7c5f1aa0300bdf9646b8d9afb0048c", size = 1151126, upload-time = "2025-10-24T07:19:29.522Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a0/89e6a024c3b4485b89ef86881c9d55e097e7cb0bdb74efb746f2fa6a9a76/lupa-2.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9abb98d5a8fd27c8285302e82199f0e56e463066f88f619d6594a450bf269d80", size = 2153693, upload-time = "2025-10-24T07:19:31.379Z" }, - { url = "https://files.pythonhosted.org/packages/b6/36/a0f007dc58fc1bbf51fb85dcc82fcb1f21b8c4261361de7dab0e3d8521ef/lupa-2.6-cp314-cp314t-win32.whl", hash = "sha256:1849efeba7a8f6fb8aa2c13790bee988fd242ae404bd459509640eeea3d1e291", size = 1590104, upload-time = "2025-10-24T07:19:33.514Z" }, - { url = "https://files.pythonhosted.org/packages/7d/5e/db903ce9cf82c48d6b91bf6d63ae4c8d0d17958939a4e04ba6b9f38b8643/lupa-2.6-cp314-cp314t-win_amd64.whl", hash = "sha256:fc1498d1a4fc028bc521c26d0fad4ca00ed63b952e32fb95949bda76a04bad52", size = 1913818, upload-time = "2025-10-24T07:19:36.039Z" }, -] - -[[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 = "mcp" -version = "1.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyjwt", extra = ["crypto"] }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, -] - -[[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 = "mouseinfo" -version = "0.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyperclip" }, - { name = "python3-xlib", marker = "sys_platform == 'linux'" }, - { name = "rubicon-objc", marker = "sys_platform == 'darwin'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/fa/b2ba8229b9381e8f6381c1dcae6f4159a7f72349e414ed19cfbbd1817173/MouseInfo-0.1.3.tar.gz", hash = "sha256:2c62fb8885062b8e520a3cce0a297c657adcc08c60952eb05bc8256ef6f7f6e7", size = 10850, upload-time = "2020-03-27T21:20:10.136Z" } - -[[package]] -name = "numpy" -version = "2.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/fd/0005efbd0af48e55eb3c7208af93f2862d4b1a56cd78e84309a2d959208d/numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae", size = 20723651, upload-time = "2026-01-31T23:13:10.135Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/6e/6f394c9c77668153e14d4da83bcc247beb5952f6ead7699a1a2992613bea/numpy-2.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:21982668592194c609de53ba4933a7471880ccbaadcc52352694a59ecc860b3a", size = 16667963, upload-time = "2026-01-31T23:10:52.147Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f8/55483431f2b2fd015ae6ed4fe62288823ce908437ed49db5a03d15151678/numpy-2.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40397bda92382fcec844066efb11f13e1c9a3e2a8e8f318fb72ed8b6db9f60f1", size = 14693571, upload-time = "2026-01-31T23:10:54.789Z" }, - { url = "https://files.pythonhosted.org/packages/2f/20/18026832b1845cdc82248208dd929ca14c9d8f2bac391f67440707fff27c/numpy-2.4.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b3a24467af63c67829bfaa61eecf18d5432d4f11992688537be59ecd6ad32f5e", size = 5203469, upload-time = "2026-01-31T23:10:57.343Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/2eb97c8a77daaba34eaa3fa7241a14ac5f51c46a6bd5911361b644c4a1e2/numpy-2.4.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:805cc8de9fd6e7a22da5aed858e0ab16be5a4db6c873dde1d7451c541553aa27", size = 6550820, upload-time = "2026-01-31T23:10:59.429Z" }, - { url = "https://files.pythonhosted.org/packages/b1/91/b97fdfd12dc75b02c44e26c6638241cc004d4079a0321a69c62f51470c4c/numpy-2.4.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d82351358ffbcdcd7b686b90742a9b86632d6c1c051016484fa0b326a0a1548", size = 15663067, upload-time = "2026-01-31T23:11:01.291Z" }, - { url = "https://files.pythonhosted.org/packages/f5/c6/a18e59f3f0b8071cc85cbc8d80cd02d68aa9710170b2553a117203d46936/numpy-2.4.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e35d3e0144137d9fdae62912e869136164534d64a169f86438bc9561b6ad49f", size = 16619782, upload-time = "2026-01-31T23:11:03.669Z" }, - { url = "https://files.pythonhosted.org/packages/b7/83/9751502164601a79e18847309f5ceec0b1446d7b6aa12305759b72cf98b2/numpy-2.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adb6ed2ad29b9e15321d167d152ee909ec73395901b70936f029c3bc6d7f4460", size = 17013128, upload-time = "2026-01-31T23:11:05.913Z" }, - { url = "https://files.pythonhosted.org/packages/61/c4/c4066322256ec740acc1c8923a10047818691d2f8aec254798f3dd90f5f2/numpy-2.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8906e71fd8afcb76580404e2a950caef2685df3d2a57fe82a86ac8d33cc007ba", size = 18345324, upload-time = "2026-01-31T23:11:08.248Z" }, - { url = "https://files.pythonhosted.org/packages/ab/af/6157aa6da728fa4525a755bfad486ae7e3f76d4c1864138003eb84328497/numpy-2.4.2-cp312-cp312-win32.whl", hash = "sha256:ec055f6dae239a6299cace477b479cca2fc125c5675482daf1dd886933a1076f", size = 5960282, upload-time = "2026-01-31T23:11:10.497Z" }, - { url = "https://files.pythonhosted.org/packages/92/0f/7ceaaeaacb40567071e94dbf2c9480c0ae453d5bb4f52bea3892c39dc83c/numpy-2.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:209fae046e62d0ce6435fcfe3b1a10537e858249b3d9b05829e2a05218296a85", size = 12314210, upload-time = "2026-01-31T23:11:12.176Z" }, - { url = "https://files.pythonhosted.org/packages/2f/a3/56c5c604fae6dd40fa2ed3040d005fca97e91bd320d232ac9931d77ba13c/numpy-2.4.2-cp312-cp312-win_arm64.whl", hash = "sha256:fbde1b0c6e81d56f5dccd95dd4a711d9b95df1ae4009a60887e56b27e8d903fa", size = 10220171, upload-time = "2026-01-31T23:11:14.684Z" }, - { url = "https://files.pythonhosted.org/packages/a1/22/815b9fe25d1d7ae7d492152adbc7226d3eff731dffc38fe970589fcaaa38/numpy-2.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25f2059807faea4b077a2b6837391b5d830864b3543627f381821c646f31a63c", size = 16663696, upload-time = "2026-01-31T23:11:17.516Z" }, - { url = "https://files.pythonhosted.org/packages/09/f0/817d03a03f93ba9c6c8993de509277d84e69f9453601915e4a69554102a1/numpy-2.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bd3a7a9f5847d2fb8c2c6d1c862fa109c31a9abeca1a3c2bd5a64572955b2979", size = 14688322, upload-time = "2026-01-31T23:11:19.883Z" }, - { url = "https://files.pythonhosted.org/packages/da/b4/f805ab79293c728b9a99438775ce51885fd4f31b76178767cfc718701a39/numpy-2.4.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8e4549f8a3c6d13d55041925e912bfd834285ef1dd64d6bc7d542583355e2e98", size = 5198157, upload-time = "2026-01-31T23:11:22.375Z" }, - { url = "https://files.pythonhosted.org/packages/74/09/826e4289844eccdcd64aac27d13b0fd3f32039915dd5b9ba01baae1f436c/numpy-2.4.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:aea4f66ff44dfddf8c2cffd66ba6538c5ec67d389285292fe428cb2c738c8aef", size = 6546330, upload-time = "2026-01-31T23:11:23.958Z" }, - { url = "https://files.pythonhosted.org/packages/19/fb/cbfdbfa3057a10aea5422c558ac57538e6acc87ec1669e666d32ac198da7/numpy-2.4.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3cd545784805de05aafe1dde61752ea49a359ccba9760c1e5d1c88a93bbf2b7", size = 15660968, upload-time = "2026-01-31T23:11:25.713Z" }, - { url = "https://files.pythonhosted.org/packages/04/dc/46066ce18d01645541f0186877377b9371b8fa8017fa8262002b4ef22612/numpy-2.4.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0d9b7c93578baafcbc5f0b83eaf17b79d345c6f36917ba0c67f45226911d499", size = 16607311, upload-time = "2026-01-31T23:11:28.117Z" }, - { url = "https://files.pythonhosted.org/packages/14/d9/4b5adfc39a43fa6bf918c6d544bc60c05236cc2f6339847fc5b35e6cb5b0/numpy-2.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f74f0f7779cc7ae07d1810aab8ac6b1464c3eafb9e283a40da7309d5e6e48fbb", size = 17012850, upload-time = "2026-01-31T23:11:30.888Z" }, - { url = "https://files.pythonhosted.org/packages/b7/20/adb6e6adde6d0130046e6fdfb7675cc62bc2f6b7b02239a09eb58435753d/numpy-2.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7ac672d699bf36275c035e16b65539931347d68b70667d28984c9fb34e07fa7", size = 18334210, upload-time = "2026-01-31T23:11:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/78/0e/0a73b3dff26803a8c02baa76398015ea2a5434d9b8265a7898a6028c1591/numpy-2.4.2-cp313-cp313-win32.whl", hash = "sha256:8e9afaeb0beff068b4d9cd20d322ba0ee1cecfb0b08db145e4ab4dd44a6b5110", size = 5958199, upload-time = "2026-01-31T23:11:35.385Z" }, - { url = "https://files.pythonhosted.org/packages/43/bc/6352f343522fcb2c04dbaf94cb30cca6fd32c1a750c06ad6231b4293708c/numpy-2.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:7df2de1e4fba69a51c06c28f5a3de36731eb9639feb8e1cf7e4a7b0daf4cf622", size = 12310848, upload-time = "2026-01-31T23:11:38.001Z" }, - { url = "https://files.pythonhosted.org/packages/6e/8d/6da186483e308da5da1cc6918ce913dcfe14ffde98e710bfeff2a6158d4e/numpy-2.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:0fece1d1f0a89c16b03442eae5c56dc0be0c7883b5d388e0c03f53019a4bfd71", size = 10221082, upload-time = "2026-01-31T23:11:40.392Z" }, - { url = "https://files.pythonhosted.org/packages/25/a1/9510aa43555b44781968935c7548a8926274f815de42ad3997e9e83680dd/numpy-2.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5633c0da313330fd20c484c78cdd3f9b175b55e1a766c4a174230c6b70ad8262", size = 14815866, upload-time = "2026-01-31T23:11:42.495Z" }, - { url = "https://files.pythonhosted.org/packages/36/30/6bbb5e76631a5ae46e7923dd16ca9d3f1c93cfa8d4ed79a129814a9d8db3/numpy-2.4.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d9f64d786b3b1dd742c946c42d15b07497ed14af1a1f3ce840cce27daa0ce913", size = 5325631, upload-time = "2026-01-31T23:11:44.7Z" }, - { url = "https://files.pythonhosted.org/packages/46/00/3a490938800c1923b567b3a15cd17896e68052e2145d8662aaf3e1ffc58f/numpy-2.4.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:b21041e8cb6a1eb5312dd1d2f80a94d91efffb7a06b70597d44f1bd2dfc315ab", size = 6646254, upload-time = "2026-01-31T23:11:46.341Z" }, - { url = "https://files.pythonhosted.org/packages/d3/e9/fac0890149898a9b609caa5af7455a948b544746e4b8fe7c212c8edd71f8/numpy-2.4.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00ab83c56211a1d7c07c25e3217ea6695e50a3e2f255053686b081dc0b091a82", size = 15720138, upload-time = "2026-01-31T23:11:48.082Z" }, - { url = "https://files.pythonhosted.org/packages/ea/5c/08887c54e68e1e28df53709f1893ce92932cc6f01f7c3d4dc952f61ffd4e/numpy-2.4.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fb882da679409066b4603579619341c6d6898fc83a8995199d5249f986e8e8f", size = 16655398, upload-time = "2026-01-31T23:11:50.293Z" }, - { url = "https://files.pythonhosted.org/packages/4d/89/253db0fa0e66e9129c745e4ef25631dc37d5f1314dad2b53e907b8538e6d/numpy-2.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66cb9422236317f9d44b67b4d18f44efe6e9c7f8794ac0462978513359461554", size = 17079064, upload-time = "2026-01-31T23:11:52.927Z" }, - { url = "https://files.pythonhosted.org/packages/2a/d5/cbade46ce97c59c6c3da525e8d95b7abe8a42974a1dc5c1d489c10433e88/numpy-2.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0f01dcf33e73d80bd8dc0f20a71303abbafa26a19e23f6b68d1aa9990af90257", size = 18379680, upload-time = "2026-01-31T23:11:55.22Z" }, - { url = "https://files.pythonhosted.org/packages/40/62/48f99ae172a4b63d981babe683685030e8a3df4f246c893ea5c6ef99f018/numpy-2.4.2-cp313-cp313t-win32.whl", hash = "sha256:52b913ec40ff7ae845687b0b34d8d93b60cb66dcee06996dd5c99f2fc9328657", size = 6082433, upload-time = "2026-01-31T23:11:58.096Z" }, - { url = "https://files.pythonhosted.org/packages/07/38/e054a61cfe48ad9f1ed0d188e78b7e26859d0b60ef21cd9de4897cdb5326/numpy-2.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5eea80d908b2c1f91486eb95b3fb6fab187e569ec9752ab7d9333d2e66bf2d6b", size = 12451181, upload-time = "2026-01-31T23:11:59.782Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a4/a05c3a6418575e185dd84d0b9680b6bb2e2dc3e4202f036b7b4e22d6e9dc/numpy-2.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fd49860271d52127d61197bb50b64f58454e9f578cb4b2c001a6de8b1f50b0b1", size = 10290756, upload-time = "2026-01-31T23:12:02.438Z" }, - { url = "https://files.pythonhosted.org/packages/18/88/b7df6050bf18fdcfb7046286c6535cabbdd2064a3440fca3f069d319c16e/numpy-2.4.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b", size = 16663092, upload-time = "2026-01-31T23:12:04.521Z" }, - { url = "https://files.pythonhosted.org/packages/25/7a/1fee4329abc705a469a4afe6e69b1ef7e915117747886327104a8493a955/numpy-2.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000", size = 14698770, upload-time = "2026-01-31T23:12:06.96Z" }, - { url = "https://files.pythonhosted.org/packages/fb/0b/f9e49ba6c923678ad5bc38181c08ac5e53b7a5754dbca8e581aa1a56b1ff/numpy-2.4.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1", size = 5208562, upload-time = "2026-01-31T23:12:09.632Z" }, - { url = "https://files.pythonhosted.org/packages/7d/12/d7de8f6f53f9bb76997e5e4c069eda2051e3fe134e9181671c4391677bb2/numpy-2.4.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74", size = 6543710, upload-time = "2026-01-31T23:12:11.969Z" }, - { url = "https://files.pythonhosted.org/packages/09/63/c66418c2e0268a31a4cf8a8b512685748200f8e8e8ec6c507ce14e773529/numpy-2.4.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a", size = 15677205, upload-time = "2026-01-31T23:12:14.33Z" }, - { url = "https://files.pythonhosted.org/packages/5d/6c/7f237821c9642fb2a04d2f1e88b4295677144ca93285fd76eff3bcba858d/numpy-2.4.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325", size = 16611738, upload-time = "2026-01-31T23:12:16.525Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a7/39c4cdda9f019b609b5c473899d87abff092fc908cfe4d1ecb2fcff453b0/numpy-2.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909", size = 17028888, upload-time = "2026-01-31T23:12:19.306Z" }, - { url = "https://files.pythonhosted.org/packages/da/b3/e84bb64bdfea967cc10950d71090ec2d84b49bc691df0025dddb7c26e8e3/numpy-2.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a", size = 18339556, upload-time = "2026-01-31T23:12:21.816Z" }, - { url = "https://files.pythonhosted.org/packages/88/f5/954a291bc1192a27081706862ac62bb5920fbecfbaa302f64682aa90beed/numpy-2.4.2-cp314-cp314-win32.whl", hash = "sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a", size = 6006899, upload-time = "2026-01-31T23:12:24.14Z" }, - { url = "https://files.pythonhosted.org/packages/05/cb/eff72a91b2efdd1bc98b3b8759f6a1654aa87612fc86e3d87d6fe4f948c4/numpy-2.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75", size = 12443072, upload-time = "2026-01-31T23:12:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/37/75/62726948db36a56428fce4ba80a115716dc4fad6a3a4352487f8bb950966/numpy-2.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05", size = 10494886, upload-time = "2026-01-31T23:12:28.488Z" }, - { url = "https://files.pythonhosted.org/packages/36/2f/ee93744f1e0661dc267e4b21940870cabfae187c092e1433b77b09b50ac4/numpy-2.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308", size = 14818567, upload-time = "2026-01-31T23:12:30.709Z" }, - { url = "https://files.pythonhosted.org/packages/a7/24/6535212add7d76ff938d8bdc654f53f88d35cddedf807a599e180dcb8e66/numpy-2.4.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef", size = 5328372, upload-time = "2026-01-31T23:12:32.962Z" }, - { url = "https://files.pythonhosted.org/packages/5e/9d/c48f0a035725f925634bf6b8994253b43f2047f6778a54147d7e213bc5a7/numpy-2.4.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d", size = 6649306, upload-time = "2026-01-31T23:12:34.797Z" }, - { url = "https://files.pythonhosted.org/packages/81/05/7c73a9574cd4a53a25907bad38b59ac83919c0ddc8234ec157f344d57d9a/numpy-2.4.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8", size = 15722394, upload-time = "2026-01-31T23:12:36.565Z" }, - { url = "https://files.pythonhosted.org/packages/35/fa/4de10089f21fc7d18442c4a767ab156b25c2a6eaf187c0db6d9ecdaeb43f/numpy-2.4.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5", size = 16653343, upload-time = "2026-01-31T23:12:39.188Z" }, - { url = "https://files.pythonhosted.org/packages/b8/f9/d33e4ffc857f3763a57aa85650f2e82486832d7492280ac21ba9efda80da/numpy-2.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e", size = 17078045, upload-time = "2026-01-31T23:12:42.041Z" }, - { url = "https://files.pythonhosted.org/packages/c8/b8/54bdb43b6225badbea6389fa038c4ef868c44f5890f95dd530a218706da3/numpy-2.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a", size = 18380024, upload-time = "2026-01-31T23:12:44.331Z" }, - { url = "https://files.pythonhosted.org/packages/a5/55/6e1a61ded7af8df04016d81b5b02daa59f2ea9252ee0397cb9f631efe9e5/numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443", size = 6153937, upload-time = "2026-01-31T23:12:47.229Z" }, - { url = "https://files.pythonhosted.org/packages/45/aa/fa6118d1ed6d776b0983f3ceac9b1a5558e80df9365b1c3aa6d42bf9eee4/numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236", size = 12631844, upload-time = "2026-01-31T23:12:48.997Z" }, - { url = "https://files.pythonhosted.org/packages/32/0a/2ec5deea6dcd158f254a7b372fb09cfba5719419c8d66343bab35237b3fb/numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181", size = 10565379, upload-time = "2026-01-31T23:12:51.345Z" }, -] - -[[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 = "opentelemetry-api" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, -] - -[[package]] -name = "opentelemetry-exporter-prometheus" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-sdk" }, - { name = "prometheus-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/14/39/7dafa6fff210737267bed35a8855b6ac7399b9e582b8cf1f25f842517012/opentelemetry_exporter_prometheus-0.60b1.tar.gz", hash = "sha256:a4011b46906323f71724649d301b4dc188aaa068852e814f4df38cc76eac616b", size = 14976, upload-time = "2025-12-11T13:32:42.944Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/0d/4be6bf5477a3eb3d917d2f17d3c0b6720cd6cb97898444a61d43cc983f5c/opentelemetry_exporter_prometheus-0.60b1-py3-none-any.whl", hash = "sha256:49f59178de4f4590e3cef0b8b95cf6e071aae70e1f060566df5546fad773b8fd", size = 13019, upload-time = "2025-12-11T13:32:23.974Z" }, -] - -[[package]] -name = "opentelemetry-instrumentation" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "packaging" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/0f/7e6b713ac117c1f5e4e3300748af699b9902a2e5e34c9cf443dde25a01fa/opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a", size = 31706, upload-time = "2025-12-11T13:36:42.515Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096, upload-time = "2025-12-11T13:35:33.067Z" }, -] - -[[package]] -name = "opentelemetry-sdk" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, -] - -[[package]] -name = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[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 = "pathvalidate" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262, upload-time = "2025-06-15T09:07:20.736Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305, upload-time = "2025-06-15T09:07:19.117Z" }, -] - -[[package]] -name = "pillow" -version = "12.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/02/d52c733a2452ef1ffcc123b68e6606d07276b0e358db70eabad7e40042b7/pillow-12.1.0.tar.gz", hash = "sha256:5c5ae0a06e9ea030ab786b0251b32c7e4ce10e58d983c0d5c56029455180b5b9", size = 46977283, upload-time = "2026-01-02T09:13:29.892Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/31/dc53fe21a2f2996e1b7d92bf671cdb157079385183ef7c1ae08b485db510/pillow-12.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a332ac4ccb84b6dde65dbace8431f3af08874bf9770719d32a635c4ef411b18b", size = 5262642, upload-time = "2026-01-02T09:11:10.138Z" }, - { url = "https://files.pythonhosted.org/packages/ab/c1/10e45ac9cc79419cedf5121b42dcca5a50ad2b601fa080f58c22fb27626e/pillow-12.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:907bfa8a9cb790748a9aa4513e37c88c59660da3bcfffbd24a7d9e6abf224551", size = 4657464, upload-time = "2026-01-02T09:11:12.319Z" }, - { url = "https://files.pythonhosted.org/packages/ad/26/7b82c0ab7ef40ebede7a97c72d473bda5950f609f8e0c77b04af574a0ddb/pillow-12.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:efdc140e7b63b8f739d09a99033aa430accce485ff78e6d311973a67b6bf3208", size = 6234878, upload-time = "2026-01-02T09:11:14.096Z" }, - { url = "https://files.pythonhosted.org/packages/76/25/27abc9792615b5e886ca9411ba6637b675f1b77af3104710ac7353fe5605/pillow-12.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bef9768cab184e7ae6e559c032e95ba8d07b3023c289f79a2bd36e8bf85605a5", size = 8044868, upload-time = "2026-01-02T09:11:15.903Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ea/f200a4c36d836100e7bc738fc48cd963d3ba6372ebc8298a889e0cfc3359/pillow-12.1.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:742aea052cf5ab5034a53c3846165bc3ce88d7c38e954120db0ab867ca242661", size = 6349468, upload-time = "2026-01-02T09:11:17.631Z" }, - { url = "https://files.pythonhosted.org/packages/11/8f/48d0b77ab2200374c66d344459b8958c86693be99526450e7aee714e03e4/pillow-12.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6dfc2af5b082b635af6e08e0d1f9f1c4e04d17d4e2ca0ef96131e85eda6eb17", size = 7041518, upload-time = "2026-01-02T09:11:19.389Z" }, - { url = "https://files.pythonhosted.org/packages/1d/23/c281182eb986b5d31f0a76d2a2c8cd41722d6fb8ed07521e802f9bba52de/pillow-12.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:609e89d9f90b581c8d16358c9087df76024cf058fa693dd3e1e1620823f39670", size = 6462829, upload-time = "2026-01-02T09:11:21.28Z" }, - { url = "https://files.pythonhosted.org/packages/25/ef/7018273e0faac099d7b00982abdcc39142ae6f3bd9ceb06de09779c4a9d6/pillow-12.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:43b4899cfd091a9693a1278c4982f3e50f7fb7cff5153b05174b4afc9593b616", size = 7166756, upload-time = "2026-01-02T09:11:23.559Z" }, - { url = "https://files.pythonhosted.org/packages/8f/c8/993d4b7ab2e341fe02ceef9576afcf5830cdec640be2ac5bee1820d693d4/pillow-12.1.0-cp312-cp312-win32.whl", hash = "sha256:aa0c9cc0b82b14766a99fbe6084409972266e82f459821cd26997a488a7261a7", size = 6328770, upload-time = "2026-01-02T09:11:25.661Z" }, - { url = "https://files.pythonhosted.org/packages/a7/87/90b358775a3f02765d87655237229ba64a997b87efa8ccaca7dd3e36e7a7/pillow-12.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:d70534cea9e7966169ad29a903b99fc507e932069a881d0965a1a84bb57f6c6d", size = 7033406, upload-time = "2026-01-02T09:11:27.474Z" }, - { url = "https://files.pythonhosted.org/packages/5d/cf/881b457eccacac9e5b2ddd97d5071fb6d668307c57cbf4e3b5278e06e536/pillow-12.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:65b80c1ee7e14a87d6a068dd3b0aea268ffcabfe0498d38661b00c5b4b22e74c", size = 2452612, upload-time = "2026-01-02T09:11:29.309Z" }, - { url = "https://files.pythonhosted.org/packages/dd/c7/2530a4aa28248623e9d7f27316b42e27c32ec410f695929696f2e0e4a778/pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7b5dd7cbae20285cdb597b10eb5a2c13aa9de6cde9bb64a3c1317427b1db1ae1", size = 4062543, upload-time = "2026-01-02T09:11:31.566Z" }, - { url = "https://files.pythonhosted.org/packages/8f/1f/40b8eae823dc1519b87d53c30ed9ef085506b05281d313031755c1705f73/pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:29a4cef9cb672363926f0470afc516dbf7305a14d8c54f7abbb5c199cd8f8179", size = 4138373, upload-time = "2026-01-02T09:11:33.367Z" }, - { url = "https://files.pythonhosted.org/packages/d4/77/6fa60634cf06e52139fd0e89e5bbf055e8166c691c42fb162818b7fda31d/pillow-12.1.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:681088909d7e8fa9e31b9799aaa59ba5234c58e5e4f1951b4c4d1082a2e980e0", size = 3601241, upload-time = "2026-01-02T09:11:35.011Z" }, - { url = "https://files.pythonhosted.org/packages/4f/bf/28ab865de622e14b747f0cd7877510848252d950e43002e224fb1c9ababf/pillow-12.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:983976c2ab753166dc66d36af6e8ec15bb511e4a25856e2227e5f7e00a160587", size = 5262410, upload-time = "2026-01-02T09:11:36.682Z" }, - { url = "https://files.pythonhosted.org/packages/1c/34/583420a1b55e715937a85bd48c5c0991598247a1fd2eb5423188e765ea02/pillow-12.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:db44d5c160a90df2d24a24760bbd37607d53da0b34fb546c4c232af7192298ac", size = 4657312, upload-time = "2026-01-02T09:11:38.535Z" }, - { url = "https://files.pythonhosted.org/packages/1d/fd/f5a0896839762885b3376ff04878f86ab2b097c2f9a9cdccf4eda8ba8dc0/pillow-12.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6b7a9d1db5dad90e2991645874f708e87d9a3c370c243c2d7684d28f7e133e6b", size = 6232605, upload-time = "2026-01-02T09:11:40.602Z" }, - { url = "https://files.pythonhosted.org/packages/98/aa/938a09d127ac1e70e6ed467bd03834350b33ef646b31edb7452d5de43792/pillow-12.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6258f3260986990ba2fa8a874f8b6e808cf5abb51a94015ca3dc3c68aa4f30ea", size = 8041617, upload-time = "2026-01-02T09:11:42.721Z" }, - { url = "https://files.pythonhosted.org/packages/17/e8/538b24cb426ac0186e03f80f78bc8dc7246c667f58b540bdd57c71c9f79d/pillow-12.1.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e115c15e3bc727b1ca3e641a909f77f8ca72a64fff150f666fcc85e57701c26c", size = 6346509, upload-time = "2026-01-02T09:11:44.955Z" }, - { url = "https://files.pythonhosted.org/packages/01/9a/632e58ec89a32738cabfd9ec418f0e9898a2b4719afc581f07c04a05e3c9/pillow-12.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6741e6f3074a35e47c77b23a4e4f2d90db3ed905cb1c5e6e0d49bff2045632bc", size = 7038117, upload-time = "2026-01-02T09:11:46.736Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a2/d40308cf86eada842ca1f3ffa45d0ca0df7e4ab33c83f81e73f5eaed136d/pillow-12.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:935b9d1aed48fcfb3f838caac506f38e29621b44ccc4f8a64d575cb1b2a88644", size = 6460151, upload-time = "2026-01-02T09:11:48.625Z" }, - { url = "https://files.pythonhosted.org/packages/f1/88/f5b058ad6453a085c5266660a1417bdad590199da1b32fb4efcff9d33b05/pillow-12.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5fee4c04aad8932da9f8f710af2c1a15a83582cfb884152a9caa79d4efcdbf9c", size = 7164534, upload-time = "2026-01-02T09:11:50.445Z" }, - { url = "https://files.pythonhosted.org/packages/19/ce/c17334caea1db789163b5d855a5735e47995b0b5dc8745e9a3605d5f24c0/pillow-12.1.0-cp313-cp313-win32.whl", hash = "sha256:a786bf667724d84aa29b5db1c61b7bfdde380202aaca12c3461afd6b71743171", size = 6332551, upload-time = "2026-01-02T09:11:52.234Z" }, - { url = "https://files.pythonhosted.org/packages/e5/07/74a9d941fa45c90a0d9465098fe1ec85de3e2afbdc15cc4766622d516056/pillow-12.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:461f9dfdafa394c59cd6d818bdfdbab4028b83b02caadaff0ffd433faf4c9a7a", size = 7040087, upload-time = "2026-01-02T09:11:54.822Z" }, - { url = "https://files.pythonhosted.org/packages/88/09/c99950c075a0e9053d8e880595926302575bc742b1b47fe1bbcc8d388d50/pillow-12.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:9212d6b86917a2300669511ed094a9406888362e085f2431a7da985a6b124f45", size = 2452470, upload-time = "2026-01-02T09:11:56.522Z" }, - { url = "https://files.pythonhosted.org/packages/b5/ba/970b7d85ba01f348dee4d65412476321d40ee04dcb51cd3735b9dc94eb58/pillow-12.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:00162e9ca6d22b7c3ee8e61faa3c3253cd19b6a37f126cad04f2f88b306f557d", size = 5264816, upload-time = "2026-01-02T09:11:58.227Z" }, - { url = "https://files.pythonhosted.org/packages/10/60/650f2fb55fdba7a510d836202aa52f0baac633e50ab1cf18415d332188fb/pillow-12.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7d6daa89a00b58c37cb1747ec9fb7ac3bc5ffd5949f5888657dfddde6d1312e0", size = 4660472, upload-time = "2026-01-02T09:12:00.798Z" }, - { url = "https://files.pythonhosted.org/packages/2b/c0/5273a99478956a099d533c4f46cbaa19fd69d606624f4334b85e50987a08/pillow-12.1.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e2479c7f02f9d505682dc47df8c0ea1fc5e264c4d1629a5d63fe3e2334b89554", size = 6268974, upload-time = "2026-01-02T09:12:02.572Z" }, - { url = "https://files.pythonhosted.org/packages/b4/26/0bf714bc2e73d5267887d47931d53c4ceeceea6978148ed2ab2a4e6463c4/pillow-12.1.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f188d580bd870cda1e15183790d1cc2fa78f666e76077d103edf048eed9c356e", size = 8073070, upload-time = "2026-01-02T09:12:04.75Z" }, - { url = "https://files.pythonhosted.org/packages/43/cf/1ea826200de111a9d65724c54f927f3111dc5ae297f294b370a670c17786/pillow-12.1.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0fde7ec5538ab5095cc02df38ee99b0443ff0e1c847a045554cf5f9af1f4aa82", size = 6380176, upload-time = "2026-01-02T09:12:06.626Z" }, - { url = "https://files.pythonhosted.org/packages/03/e0/7938dd2b2013373fd85d96e0f38d62b7a5a262af21ac274250c7ca7847c9/pillow-12.1.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ed07dca4a8464bada6139ab38f5382f83e5f111698caf3191cb8dbf27d908b4", size = 7067061, upload-time = "2026-01-02T09:12:08.624Z" }, - { url = "https://files.pythonhosted.org/packages/86/ad/a2aa97d37272a929a98437a8c0ac37b3cf012f4f8721e1bd5154699b2518/pillow-12.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f45bd71d1fa5e5749587613037b172e0b3b23159d1c00ef2fc920da6f470e6f0", size = 6491824, upload-time = "2026-01-02T09:12:10.488Z" }, - { url = "https://files.pythonhosted.org/packages/a4/44/80e46611b288d51b115826f136fb3465653c28f491068a72d3da49b54cd4/pillow-12.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:277518bf4fe74aa91489e1b20577473b19ee70fb97c374aa50830b279f25841b", size = 7190911, upload-time = "2026-01-02T09:12:12.772Z" }, - { url = "https://files.pythonhosted.org/packages/86/77/eacc62356b4cf81abe99ff9dbc7402750044aed02cfd6a503f7c6fc11f3e/pillow-12.1.0-cp313-cp313t-win32.whl", hash = "sha256:7315f9137087c4e0ee73a761b163fc9aa3b19f5f606a7fc08d83fd3e4379af65", size = 6336445, upload-time = "2026-01-02T09:12:14.775Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3c/57d81d0b74d218706dafccb87a87ea44262c43eef98eb3b164fd000e0491/pillow-12.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:0ddedfaa8b5f0b4ffbc2fa87b556dc59f6bb4ecb14a53b33f9189713ae8053c0", size = 7045354, upload-time = "2026-01-02T09:12:16.599Z" }, - { url = "https://files.pythonhosted.org/packages/ac/82/8b9b97bba2e3576a340f93b044a3a3a09841170ab4c1eb0d5c93469fd32f/pillow-12.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:80941e6d573197a0c28f394753de529bb436b1ca990ed6e765cf42426abc39f8", size = 2454547, upload-time = "2026-01-02T09:12:18.704Z" }, - { url = "https://files.pythonhosted.org/packages/8c/87/bdf971d8bbcf80a348cc3bacfcb239f5882100fe80534b0ce67a784181d8/pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:5cb7bc1966d031aec37ddb9dcf15c2da5b2e9f7cc3ca7c54473a20a927e1eb91", size = 4062533, upload-time = "2026-01-02T09:12:20.791Z" }, - { url = "https://files.pythonhosted.org/packages/ff/4f/5eb37a681c68d605eb7034c004875c81f86ec9ef51f5be4a63eadd58859a/pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:97e9993d5ed946aba26baf9c1e8cf18adbab584b99f452ee72f7ee8acb882796", size = 4138546, upload-time = "2026-01-02T09:12:23.664Z" }, - { url = "https://files.pythonhosted.org/packages/11/6d/19a95acb2edbace40dcd582d077b991646b7083c41b98da4ed7555b59733/pillow-12.1.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:414b9a78e14ffeb98128863314e62c3f24b8a86081066625700b7985b3f529bd", size = 3601163, upload-time = "2026-01-02T09:12:26.338Z" }, - { url = "https://files.pythonhosted.org/packages/fc/36/2b8138e51cb42e4cc39c3297713455548be855a50558c3ac2beebdc251dd/pillow-12.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e6bdb408f7c9dd2a5ff2b14a3b0bb6d4deb29fb9961e6eb3ae2031ae9a5cec13", size = 5266086, upload-time = "2026-01-02T09:12:28.782Z" }, - { url = "https://files.pythonhosted.org/packages/53/4b/649056e4d22e1caa90816bf99cef0884aed607ed38075bd75f091a607a38/pillow-12.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3413c2ae377550f5487991d444428f1a8ae92784aac79caa8b1e3b89b175f77e", size = 4657344, upload-time = "2026-01-02T09:12:31.117Z" }, - { url = "https://files.pythonhosted.org/packages/6c/6b/c5742cea0f1ade0cd61485dc3d81f05261fc2276f537fbdc00802de56779/pillow-12.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e5dcbe95016e88437ecf33544ba5db21ef1b8dd6e1b434a2cb2a3d605299e643", size = 6232114, upload-time = "2026-01-02T09:12:32.936Z" }, - { url = "https://files.pythonhosted.org/packages/bf/8f/9f521268ce22d63991601aafd3d48d5ff7280a246a1ef62d626d67b44064/pillow-12.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d0a7735df32ccbcc98b98a1ac785cc4b19b580be1bdf0aeb5c03223220ea09d5", size = 8042708, upload-time = "2026-01-02T09:12:34.78Z" }, - { url = "https://files.pythonhosted.org/packages/1a/eb/257f38542893f021502a1bbe0c2e883c90b5cff26cc33b1584a841a06d30/pillow-12.1.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c27407a2d1b96774cbc4a7594129cc027339fd800cd081e44497722ea1179de", size = 6347762, upload-time = "2026-01-02T09:12:36.748Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5a/8ba375025701c09b309e8d5163c5a4ce0102fa86bbf8800eb0d7ac87bc51/pillow-12.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15c794d74303828eaa957ff8070846d0efe8c630901a1c753fdc63850e19ecd9", size = 7039265, upload-time = "2026-01-02T09:12:39.082Z" }, - { url = "https://files.pythonhosted.org/packages/cf/dc/cf5e4cdb3db533f539e88a7bbf9f190c64ab8a08a9bc7a4ccf55067872e4/pillow-12.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c990547452ee2800d8506c4150280757f88532f3de2a58e3022e9b179107862a", size = 6462341, upload-time = "2026-01-02T09:12:40.946Z" }, - { url = "https://files.pythonhosted.org/packages/d0/47/0291a25ac9550677e22eda48510cfc4fa4b2ef0396448b7fbdc0a6946309/pillow-12.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b63e13dd27da389ed9475b3d28510f0f954bca0041e8e551b2a4eb1eab56a39a", size = 7165395, upload-time = "2026-01-02T09:12:42.706Z" }, - { url = "https://files.pythonhosted.org/packages/4f/4c/e005a59393ec4d9416be06e6b45820403bb946a778e39ecec62f5b2b991e/pillow-12.1.0-cp314-cp314-win32.whl", hash = "sha256:1a949604f73eb07a8adab38c4fe50791f9919344398bdc8ac6b307f755fc7030", size = 6431413, upload-time = "2026-01-02T09:12:44.944Z" }, - { url = "https://files.pythonhosted.org/packages/1c/af/f23697f587ac5f9095d67e31b81c95c0249cd461a9798a061ed6709b09b5/pillow-12.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:4f9f6a650743f0ddee5593ac9e954ba1bdbc5e150bc066586d4f26127853ab94", size = 7176779, upload-time = "2026-01-02T09:12:46.727Z" }, - { url = "https://files.pythonhosted.org/packages/b3/36/6a51abf8599232f3e9afbd16d52829376a68909fe14efe29084445db4b73/pillow-12.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:808b99604f7873c800c4840f55ff389936ef1948e4e87645eaf3fccbc8477ac4", size = 2543105, upload-time = "2026-01-02T09:12:49.243Z" }, - { url = "https://files.pythonhosted.org/packages/82/54/2e1dd20c8749ff225080d6ba465a0cab4387f5db0d1c5fb1439e2d99923f/pillow-12.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc11908616c8a283cf7d664f77411a5ed2a02009b0097ff8abbba5e79128ccf2", size = 5268571, upload-time = "2026-01-02T09:12:51.11Z" }, - { url = "https://files.pythonhosted.org/packages/57/61/571163a5ef86ec0cf30d265ac2a70ae6fc9e28413d1dc94fa37fae6bda89/pillow-12.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:896866d2d436563fa2a43a9d72f417874f16b5545955c54a64941e87c1376c61", size = 4660426, upload-time = "2026-01-02T09:12:52.865Z" }, - { url = "https://files.pythonhosted.org/packages/5e/e1/53ee5163f794aef1bf84243f755ee6897a92c708505350dd1923f4afec48/pillow-12.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8e178e3e99d3c0ea8fc64b88447f7cac8ccf058af422a6cedc690d0eadd98c51", size = 6269908, upload-time = "2026-01-02T09:12:54.884Z" }, - { url = "https://files.pythonhosted.org/packages/bc/0b/b4b4106ff0ee1afa1dc599fde6ab230417f800279745124f6c50bcffed8e/pillow-12.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:079af2fb0c599c2ec144ba2c02766d1b55498e373b3ac64687e43849fbbef5bc", size = 8074733, upload-time = "2026-01-02T09:12:56.802Z" }, - { url = "https://files.pythonhosted.org/packages/19/9f/80b411cbac4a732439e629a26ad3ef11907a8c7fc5377b7602f04f6fe4e7/pillow-12.1.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdec5e43377761c5dbca620efb69a77f6855c5a379e32ac5b158f54c84212b14", size = 6381431, upload-time = "2026-01-02T09:12:58.823Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b7/d65c45db463b66ecb6abc17c6ba6917a911202a07662247e1355ce1789e7/pillow-12.1.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:565c986f4b45c020f5421a4cea13ef294dde9509a8577f29b2fc5edc7587fff8", size = 7068529, upload-time = "2026-01-02T09:13:00.885Z" }, - { url = "https://files.pythonhosted.org/packages/50/96/dfd4cd726b4a45ae6e3c669fc9e49deb2241312605d33aba50499e9d9bd1/pillow-12.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:43aca0a55ce1eefc0aefa6253661cb54571857b1a7b2964bd8a1e3ef4b729924", size = 6492981, upload-time = "2026-01-02T09:13:03.314Z" }, - { url = "https://files.pythonhosted.org/packages/4d/1c/b5dc52cf713ae46033359c5ca920444f18a6359ce1020dd3e9c553ea5bc6/pillow-12.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0deedf2ea233722476b3a81e8cdfbad786f7adbed5d848469fa59fe52396e4ef", size = 7191878, upload-time = "2026-01-02T09:13:05.276Z" }, - { url = "https://files.pythonhosted.org/packages/53/26/c4188248bd5edaf543864fe4834aebe9c9cb4968b6f573ce014cc42d0720/pillow-12.1.0-cp314-cp314t-win32.whl", hash = "sha256:b17fbdbe01c196e7e159aacb889e091f28e61020a8abeac07b68079b6e626988", size = 6438703, upload-time = "2026-01-02T09:13:07.491Z" }, - { url = "https://files.pythonhosted.org/packages/b8/0e/69ed296de8ea05cb03ee139cee600f424ca166e632567b2d66727f08c7ed/pillow-12.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27b9baecb428899db6c0de572d6d305cfaf38ca1596b5c0542a5182e3e74e8c6", size = 7182927, upload-time = "2026-01-02T09:13:09.841Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f5/68334c015eed9b5cff77814258717dec591ded209ab5b6fb70e2ae873d1d/pillow-12.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f61333d817698bdcdd0f9d7793e365ac3d2a21c1f1eb02b32ad6aefb8d8ea831", size = 2545104, upload-time = "2026-01-02T09:13:12.068Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, -] - -[[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 = "prometheus-client" -version = "0.24.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616, upload-time = "2026-01-14T15:26:26.965Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057, upload-time = "2026-01-14T15:26:24.42Z" }, -] - -[[package]] -name = "py-key-value-aio" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "py-key-value-shared" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/ce/3136b771dddf5ac905cc193b461eb67967cf3979688c6696e1f2cdcde7ea/py_key_value_aio-0.3.0.tar.gz", hash = "sha256:858e852fcf6d696d231266da66042d3355a7f9871650415feef9fca7a6cd4155", size = 50801, upload-time = "2025-11-17T16:50:04.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/10/72f6f213b8f0bce36eff21fda0a13271834e9eeff7f9609b01afdc253c79/py_key_value_aio-0.3.0-py3-none-any.whl", hash = "sha256:1c781915766078bfd608daa769fefb97e65d1d73746a3dfb640460e322071b64", size = 96342, upload-time = "2025-11-17T16:50:03.801Z" }, -] - -[package.optional-dependencies] -disk = [ - { name = "diskcache" }, - { name = "pathvalidate" }, -] -keyring = [ - { name = "keyring" }, -] -memory = [ - { name = "cachetools" }, -] -redis = [ - { name = "redis" }, -] - -[[package]] -name = "py-key-value-shared" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/e4/1971dfc4620a3a15b4579fe99e024f5edd6e0967a71154771a059daff4db/py_key_value_shared-0.3.0.tar.gz", hash = "sha256:8fdd786cf96c3e900102945f92aa1473138ebe960ef49da1c833790160c28a4b", size = 11666, upload-time = "2025-11-17T16:50:06.849Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e4/b8b0a03ece72f47dce2307d36e1c34725b7223d209fc679315ffe6a4e2c3/py_key_value_shared-0.3.0-py3-none-any.whl", hash = "sha256:5b0efba7ebca08bb158b1e93afc2f07d30b8f40c2fc12ce24a4c0d84f42f9298", size = 19560, upload-time = "2025-11-17T16:50:05.954Z" }, -] - -[[package]] -name = "pyautogui" -version = "0.9.54" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mouseinfo" }, - { name = "pygetwindow" }, - { name = "pymsgbox" }, - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, - { name = "pyscreeze" }, - { name = "python3-xlib", marker = "sys_platform == 'linux'" }, - { name = "pytweening" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/65/ff/cdae0a8c2118a0de74b6cf4cbcdcaf8fd25857e6c3f205ce4b1794b27814/PyAutoGUI-0.9.54.tar.gz", hash = "sha256:dd1d29e8fd118941cb193f74df57e5c6ff8e9253b99c7b04f39cfc69f3ae04b2", size = 61236, upload-time = "2023-05-24T20:11:32.972Z" } - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[package.optional-dependencies] -email = [ - { name = "email-validator" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, -] - -[[package]] -name = "pydantic-settings" -version = "2.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, -] - -[[package]] -name = "pydocket" -version = "0.16.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cloudpickle" }, - { name = "fakeredis", extra = ["lua"] }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-prometheus" }, - { name = "opentelemetry-instrumentation" }, - { name = "prometheus-client" }, - { name = "py-key-value-aio", extra = ["memory", "redis"] }, - { name = "python-json-logger" }, - { name = "redis" }, - { name = "rich" }, - { name = "typer" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/00/26befe5f58df7cd1aeda4a8d10bc7d1908ffd86b80fd995e57a2a7b3f7bd/pydocket-0.16.6.tar.gz", hash = "sha256:b96c96ad7692827214ed4ff25fcf941ec38371314db5dcc1ae792b3e9d3a0294", size = 299054, upload-time = "2026-01-09T22:09:15.405Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/3f/7483e5a6dc6326b6e0c640619b5c5bd1d6e3c20e54d58f5fb86267cef00e/pydocket-0.16.6-py3-none-any.whl", hash = "sha256:683d21e2e846aa5106274e7d59210331b242d7fb0dce5b08d3b82065663ed183", size = 67697, upload-time = "2026-01-09T22:09:13.436Z" }, -] - -[[package]] -name = "pygetwindow" -version = "0.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyrect" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e1/70/c7a4f46dbf06048c6d57d9489b8e0f9c4c3d36b7479f03c5ca97eaa2541d/PyGetWindow-0.0.9.tar.gz", hash = "sha256:17894355e7d2b305cd832d717708384017c1698a90ce24f6f7fbf0242dd0a688", size = 9699, upload-time = "2020-10-04T02:12:50.806Z" } - -[[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 = "pyjwt" -version = "2.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, -] - -[package.optional-dependencies] -crypto = [ - { name = "cryptography" }, -] - -[[package]] -name = "pymsgbox" -version = "2.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/6a/e80da7594ee598a776972d09e2813df2b06b3bc29218f440631dfa7c78a8/pymsgbox-2.0.1.tar.gz", hash = "sha256:98d055c49a511dcc10fa08c3043e7102d468f5e4b3a83c6d3c61df722c7d798d", size = 20768, upload-time = "2025-09-09T00:38:56.863Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/3e/08c8cac81b2b2f7502746e6b9c8e5b0ec6432cd882c605560fc409aaf087/pymsgbox-2.0.1-py3-none-any.whl", hash = "sha256:5de8ec19bca2ca7e6c09d39c817c83f17c75cee80275235f43a9931db699f73b", size = 9994, upload-time = "2025-09-09T00:38:55.672Z" }, -] - -[[package]] -name = "pyobjc-core" -version = "12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/b6/d5612eb40be4fd5ef88c259339e6313f46ba67577a95d86c3470b951fce0/pyobjc_core-12.1.tar.gz", hash = "sha256:2bb3903f5387f72422145e1466b3ac3f7f0ef2e9960afa9bcd8961c5cbf8bd21", size = 1000532, upload-time = "2025-11-14T10:08:28.292Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/5a/6b15e499de73050f4a2c88fff664ae154307d25dc04da8fb38998a428358/pyobjc_core-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:818bcc6723561f207e5b5453efe9703f34bc8781d11ce9b8be286bb415eb4962", size = 678335, upload-time = "2025-11-14T09:32:20.107Z" }, - { url = "https://files.pythonhosted.org/packages/f4/d2/29e5e536adc07bc3d33dd09f3f7cf844bf7b4981820dc2a91dd810f3c782/pyobjc_core-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:01c0cf500596f03e21c23aef9b5f326b9fb1f8f118cf0d8b66749b6cf4cbb37a", size = 677370, upload-time = "2025-11-14T09:33:05.273Z" }, - { url = "https://files.pythonhosted.org/packages/1b/f0/4b4ed8924cd04e425f2a07269943018d43949afad1c348c3ed4d9d032787/pyobjc_core-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:177aaca84bb369a483e4961186704f64b2697708046745f8167e818d968c88fc", size = 719586, upload-time = "2025-11-14T09:33:53.302Z" }, - { url = "https://files.pythonhosted.org/packages/25/98/9f4ed07162de69603144ff480be35cd021808faa7f730d082b92f7ebf2b5/pyobjc_core-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:844515f5d86395b979d02152576e7dee9cc679acc0b32dc626ef5bda315eaa43", size = 670164, upload-time = "2025-11-14T09:34:37.458Z" }, - { url = "https://files.pythonhosted.org/packages/62/50/dc076965c96c7f0de25c0a32b7f8aa98133ed244deaeeacfc758783f1f30/pyobjc_core-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:453b191df1a4b80e756445b935491b974714456ae2cbae816840bd96f86db882", size = 712204, upload-time = "2025-11-14T09:35:24.148Z" }, -] - -[[package]] -name = "pyobjc-framework-cocoa" -version = "12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyobjc-core" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/02/a3/16ca9a15e77c061a9250afbae2eae26f2e1579eb8ca9462ae2d2c71e1169/pyobjc_framework_cocoa-12.1.tar.gz", hash = "sha256:5556c87db95711b985d5efdaaf01c917ddd41d148b1e52a0c66b1a2e2c5c1640", size = 2772191, upload-time = "2025-11-14T10:13:02.069Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/bf/ee4f27ec3920d5c6fc63c63e797c5b2cc4e20fe439217085d01ea5b63856/pyobjc_framework_cocoa-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:547c182837214b7ec4796dac5aee3aa25abc665757b75d7f44f83c994bcb0858", size = 384590, upload-time = "2025-11-14T09:41:17.336Z" }, - { url = "https://files.pythonhosted.org/packages/ad/31/0c2e734165abb46215797bd830c4bdcb780b699854b15f2b6240515edcc6/pyobjc_framework_cocoa-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5a3dcd491cacc2f5a197142b3c556d8aafa3963011110102a093349017705118", size = 384689, upload-time = "2025-11-14T09:41:41.478Z" }, - { url = "https://files.pythonhosted.org/packages/23/3b/b9f61be7b9f9b4e0a6db18b3c35c4c4d589f2d04e963e2174d38c6555a92/pyobjc_framework_cocoa-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:914b74328c22d8ca261d78c23ef2befc29776e0b85555973927b338c5734ca44", size = 388843, upload-time = "2025-11-14T09:42:05.719Z" }, - { url = "https://files.pythonhosted.org/packages/59/bb/f777cc9e775fc7dae77b569254570fe46eb842516b3e4fe383ab49eab598/pyobjc_framework_cocoa-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:03342a60fc0015bcdf9b93ac0b4f457d3938e9ef761b28df9564c91a14f0129a", size = 384932, upload-time = "2025-11-14T09:42:29.771Z" }, - { url = "https://files.pythonhosted.org/packages/58/27/b457b7b37089cad692c8aada90119162dfb4c4a16f513b79a8b2b022b33b/pyobjc_framework_cocoa-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6ba1dc1bfa4da42d04e93d2363491275fb2e2be5c20790e561c8a9e09b8cf2cc", size = 388970, upload-time = "2025-11-14T09:42:53.964Z" }, -] - -[[package]] -name = "pyobjc-framework-quartz" -version = "12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/94/18/cc59f3d4355c9456fc945eae7fe8797003c4da99212dd531ad1b0de8a0c6/pyobjc_framework_quartz-12.1.tar.gz", hash = "sha256:27f782f3513ac88ec9b6c82d9767eef95a5cf4175ce88a1e5a65875fee799608", size = 3159099, upload-time = "2025-11-14T10:21:24.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/9b/780f057e5962f690f23fdff1083a4cfda5a96d5b4d3bb49505cac4f624f2/pyobjc_framework_quartz-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7730cdce46c7e985535b5a42c31381af4aa6556e5642dc55b5e6597595e57a16", size = 218798, upload-time = "2025-11-14T10:00:01.236Z" }, - { url = "https://files.pythonhosted.org/packages/ba/2d/e8f495328101898c16c32ac10e7b14b08ff2c443a756a76fd1271915f097/pyobjc_framework_quartz-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:629b7971b1b43a11617f1460cd218bd308dfea247cd4ee3842eb40ca6f588860", size = 219206, upload-time = "2025-11-14T10:00:15.623Z" }, - { url = "https://files.pythonhosted.org/packages/67/43/b1f0ad3b842ab150a7e6b7d97f6257eab6af241b4c7d14cb8e7fde9214b8/pyobjc_framework_quartz-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:53b84e880c358ba1ddcd7e8d5ea0407d760eca58b96f0d344829162cda5f37b3", size = 224317, upload-time = "2025-11-14T10:00:30.703Z" }, - { url = "https://files.pythonhosted.org/packages/4a/00/96249c5c7e5aaca5f688ca18b8d8ad05cd7886ebd639b3c71a6a4cadbe75/pyobjc_framework_quartz-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:42d306b07f05ae7d155984503e0fb1b701fecd31dcc5c79fe8ab9790ff7e0de0", size = 219558, upload-time = "2025-11-14T10:00:45.476Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a6/708a55f3ff7a18c403b30a29a11dccfed0410485a7548c60a4b6d4cc0676/pyobjc_framework_quartz-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0cc08fddb339b2760df60dea1057453557588908e42bdc62184b6396ce2d6e9a", size = 224580, upload-time = "2025-11-14T10:01:00.091Z" }, -] - -[[package]] -name = "pyperclip" -version = "1.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, -] - -[[package]] -name = "pyrect" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/04/2ba023d5f771b645f7be0c281cdacdcd939fe13d1deb331fc5ed1a6b3a98/PyRect-0.2.0.tar.gz", hash = "sha256:f65155f6df9b929b67caffbd57c0947c5ae5449d3b580d178074bffb47a09b78", size = 17219, upload-time = "2022-03-16T04:45:52.36Z" } - -[[package]] -name = "pyscreeze" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/f0/cb456ac4f1a73723d5b866933b7986f02bacea27516629c00f8e7da94c2d/pyscreeze-1.0.1.tar.gz", hash = "sha256:cf1662710f1b46aa5ff229ee23f367da9e20af4a78e6e365bee973cad0ead4be", size = 27826, upload-time = "2024-08-20T23:03:07.291Z" } - -[[package]] -name = "pytest" -version = "9.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, -] - -[[package]] -name = "pytest-asyncio" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, -] - -[[package]] -name = "python-json-logger" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, -] - -[[package]] -name = "python3-xlib" -version = "0.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ef/c6/2c5999de3bb1533521f1101e8fe56fd9c266732f4d48011c7c69b29d12ae/python3-xlib-0.15.tar.gz", hash = "sha256:dc4245f3ae4aa5949c1d112ee4723901ade37a96721ba9645f2bfa56e5b383f8", size = 132828, upload-time = "2014-05-31T12:28:59.603Z" } - -[[package]] -name = "pytweening" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/0c/c16bc93ac2755bac0066a8ecbd2a2931a1735a6fffd99a2b9681c7e83e90/pytweening-1.2.0.tar.gz", hash = "sha256:243318b7736698066c5f362ec5c2b6434ecf4297c3c8e7caa8abfe6af4cac71b", size = 171241, upload-time = "2024-02-20T03:37:56.809Z" } - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { 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 = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "redis" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/c8/983d5c6579a411d8a99bc5823cc5712768859b5ce2c8afe1a65b37832c81/redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c", size = 4796669, upload-time = "2025-11-19T15:54:39.961Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/f0/8956f8a86b20d7bb9d6ac0187cf4cd54d8065bc9a1a09eb8011d4d326596/redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b", size = 354159, upload-time = "2025-11-19T15:54:38.064Z" }, -] - -[[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 = "rich" -version = "14.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" }, -] - -[[package]] -name = "rich-rst" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, -] - -[[package]] -name = "rubicon-objc" -version = "0.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/d2/d39ecd205661a5c14c90dbd92a722a203848a3621785c9783716341de427/rubicon_objc-0.5.3.tar.gz", hash = "sha256:74c25920c5951a05db9d3a1aac31d23816ec7dacc841a5b124d911b99ea71b9a", size = 171512, upload-time = "2025-12-03T03:51:10.264Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/ab/e834c01138c272fb2e37d2f3c7cba708bc694dbc7b3f03b743f29ceb92d5/rubicon_objc-0.5.3-py3-none-any.whl", hash = "sha256:31dedcda9be38435f5ec067906e1eea5d0ddb790330e98a22e94ff424758b415", size = 64414, upload-time = "2025-12-03T03:51:09.082Z" }, -] - -[[package]] -name = "secretstorage" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[package]] -name = "sortedcontainers" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, -] - -[[package]] -name = "sse-starlette" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "starlette" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/00d280c03ffd39aaee0e86ec81e2d3b9253036a0f93f51d10503adef0e65/sse_starlette-3.2.0.tar.gz", hash = "sha256:8127594edfb51abe44eac9c49e59b0b01f1039d0c7461c6fd91d4e03b70da422", size = 27253, upload-time = "2026-01-17T13:11:05.62Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/7f/832f015020844a8b8f7a9cbc103dd76ba8e3875004c41e08440ea3a2b41a/sse_starlette-3.2.0-py3-none-any.whl", hash = "sha256:5876954bd51920fc2cd51baee47a080eb88a37b5b784e615abb0b283f801cdbf", size = 12763, upload-time = "2026-01-17T13:11:03.775Z" }, -] - -[[package]] -name = "starlette" -version = "0.52.1" -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/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, -] - -[[package]] -name = "typer" -version = "0.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.40.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, -] - -[[package]] -name = "websockets" -version = "16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, - { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, - { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, - { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, - { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, - { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, - { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, - { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, - { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, - { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, -] - -[[package]] -name = "wrapt" -version = "1.17.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998, upload-time = "2025-08-12T05:51:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020, upload-time = "2025-08-12T05:51:35.906Z" }, - { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098, upload-time = "2025-08-12T05:51:57.474Z" }, - { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036, upload-time = "2025-08-12T05:52:34.784Z" }, - { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156, upload-time = "2025-08-12T05:52:13.599Z" }, - { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102, upload-time = "2025-08-12T05:52:14.56Z" }, - { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732, upload-time = "2025-08-12T05:52:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705, upload-time = "2025-08-12T05:53:07.123Z" }, - { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877, upload-time = "2025-08-12T05:53:05.436Z" }, - { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885, upload-time = "2025-08-12T05:52:54.367Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, - { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, - { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, - { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, - { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, - { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, - { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, - { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, - { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, - { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, - { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, - { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, - { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, - { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, - { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, - { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, - { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, - { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, - { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, -] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] diff --git a/pkg/hanzo-tools-config/README.md b/pkg/hanzo-tools-config/README.md deleted file mode 100644 index 701603d2e..000000000 --- a/pkg/hanzo-tools-config/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# hanzo-tools-config - -Configuration tools for Hanzo AI MCP. - -## Tools - -- `config` - Git-style configuration management -- `mode` - Development mode switching - -## Installation - -```bash -pip install hanzo-tools-config -``` - -## Usage - -```python -from hanzo_tools.config import TOOLS, register_tools - -# Register with MCP server -register_tools(mcp_server) -``` - -## Part of hanzo-tools - -This package is part of the modular [hanzo-tools](../hanzo-tools) ecosystem. diff --git a/pkg/hanzo-tools-config/hanzo_tools/__init__.py b/pkg/hanzo-tools-config/hanzo_tools/__init__.py deleted file mode 100644 index e1b06939a..000000000 --- a/pkg/hanzo-tools-config/hanzo_tools/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Hanzo tools namespace package.""" - -import pkgutil - -__path__ = pkgutil.extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-config/hanzo_tools/config/__init__.py b/pkg/hanzo-tools-config/hanzo_tools/config/__init__.py deleted file mode 100644 index c3c40602e..000000000 --- a/pkg/hanzo-tools-config/hanzo_tools/config/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Configuration tools for Hanzo AI. - -Tools: -- config: Configuration management -- mode: Development mode switching -- workspace: Workspace context detection - -Install: - pip install hanzo-tools-config -""" - -import logging - -logger = logging.getLogger(__name__) - -_tools = [] - -try: - from .config_tool import ConfigTool - - _tools.append(ConfigTool) -except ImportError as e: - logger.debug(f"ConfigTool not available: {e}") - ConfigTool = None - -try: - from .mode_tool import ModeTool - - _tools.append(ModeTool) -except ImportError as e: - logger.debug(f"ModeTool not available: {e}") - ModeTool = None - -try: - from .workspace_tool import WorkspaceTool - - _tools.append(WorkspaceTool) -except ImportError as e: - logger.debug(f"WorkspaceTool not available: {e}") - WorkspaceTool = None - -TOOLS = _tools - -__all__ = [ - "TOOLS", - "ConfigTool", - "ModeTool", - "WorkspaceTool", - "register_tools", -] - - -def register_tools(mcp_server, enabled_tools: dict[str, bool] | None = None): - """Register config tools with MCP server.""" - from hanzo_tools.core import ToolRegistry - - enabled = enabled_tools or {} - registered = [] - - for tool_class in TOOLS: - if tool_class is None: - continue - tool_name = getattr(tool_class, "name", tool_class.__name__.lower()) - if enabled.get(tool_name, True): - try: - tool = tool_class() - ToolRegistry.register_tool(mcp_server, tool) - registered.append(tool) - except Exception as e: - logger.warning(f"Failed to register {tool_name}: {e}") - - return registered diff --git a/pkg/hanzo-tools-config/hanzo_tools/config/config_tool.py b/pkg/hanzo-tools-config/hanzo_tools/config/config_tool.py deleted file mode 100644 index c5f39532b..000000000 --- a/pkg/hanzo-tools-config/hanzo_tools/config/config_tool.py +++ /dev/null @@ -1,347 +0,0 @@ -"""Configuration tool for Hanzo AI. - -Git-style config tool for managing settings. -""" - -from typing import Unpack, Optional, Annotated, TypedDict, final, override -from pathlib import Path - -from pydantic import Field -from hanzo_mcp.config import load_settings, save_settings -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, PermissionManager, auto_timeout - -from .index_config import IndexScope, IndexConfig - -# Parameter types -Action = Annotated[ - str, - Field( - description="Action: get (default), set, list, toggle", - default="get", - ), -] - -Key = Annotated[ - Optional[str], - Field( - description="Configuration key (e.g., tools.write.enabled, enabled_tools.write, index.scope)", - default=None, - ), -] - -Value = Annotated[ - Optional[str], - Field( - description="Configuration value", - default=None, - ), -] - -Scope = Annotated[ - str, - Field( - description="Config scope: local (project) or global", - default="local", - ), -] - -ConfigPath = Annotated[ - Optional[str], - Field( - description="Path for project-specific config", - default=None, - ), -] - - -class ConfigParams(TypedDict, total=False): - """Parameters for config tool.""" - - action: str - key: Optional[str] - value: Optional[str] - scope: str - path: Optional[str] - - -def _parse_bool(value: str) -> Optional[bool]: - v = value.strip().lower() - if v in {"true", "1", "yes", "on"}: - return True - if v in {"false", "0", "no", "off"}: - return False - return None - - -@final -class ConfigTool(BaseTool): - """Git-style configuration management tool.""" - - def __init__(self, permission_manager: Optional[PermissionManager] = None): - """Initialize config tool.""" - super().__init__() - if permission_manager is None: - permission_manager = PermissionManager() - self.permission_manager = permission_manager - self.index_config = IndexConfig() - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "config" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Git-style configuration. Actions: get (default), set, list, toggle. - -Usage: -config index.scope -config --action set index.scope project -config --action set tools.write.enabled false -config --action list -config --action toggle index.scope --path ./project""" - - @override - @auto_timeout("config") - async def call( - self, - ctx: MCPContext, - **params: Unpack[ConfigParams], - ) -> str: - """Execute config operation.""" - tool_ctx = self.create_tool_context(ctx) - - # Extract parameters - action = params.get("action", "get") - key = params.get("key") - value = params.get("value") - scope = params.get("scope", "local") - path = params.get("path") - - # Route to handler - if action == "get": - return await self._handle_get(key, scope, path, tool_ctx) - elif action == "set": - return await self._handle_set(key, value, scope, path, tool_ctx) - elif action == "list": - return await self._handle_list(scope, path, tool_ctx) - elif action == "toggle": - return await self._handle_toggle(key, scope, path, tool_ctx) - else: - return f"Error: Unknown action '{action}'. Valid actions: get, set, list, toggle" - - async def _handle_get( - self, key: Optional[str], scope: str, path: Optional[str], tool_ctx - ) -> str: - """Get configuration value.""" - if not key: - return "Error: key required for get action" - - # Handle index scope - if key == "index.scope": - current_scope = self.index_config.get_scope(path) - return f"index.scope={current_scope.value}" - - # tools..enabled โ†’ enabled_tools lookup - if key.startswith("tools.") and key.endswith(".enabled"): - parts = key.split(".") - if len(parts) == 3: - tool_name = parts[1] - settings = load_settings(project_dir=path if scope == "local" else None) - return f"{key}={settings.is_tool_enabled(tool_name)}" - - # enabled_tools. - if key.startswith("enabled_tools."): - tool_name = key.split(".", 1)[1] - settings = load_settings(project_dir=path if scope == "local" else None) - val = settings.enabled_tools.get(tool_name) - return f"{key}={val if val is not None else 'unset'}" - - # Indexing (legacy) per-tool setting: .enabled - if "." in key: - tool, setting = key.split(".", 1) - if setting == "enabled": - enabled = self.index_config.is_indexing_enabled(tool) - return f"{key}={enabled}" - - return f"Unknown key: {key}" - - def _save_project_settings(self, settings, project_dir: Optional[str]) -> Path: - """Save to project config if path provided; else global.""" - if project_dir: - project_path = Path(project_dir) - project_path.mkdir(parents=True, exist_ok=True) - cfg = project_path / ".hanzo-mcp.json" - cfg.write_text( - __import__("json").dumps( - settings.__dict__ if hasattr(settings, "__dict__") else {}, indent=2 - ) - ) - return cfg - # Fallback to global handler - return save_settings(settings, global_config=True) - - async def _handle_set( - self, - key: Optional[str], - value: Optional[str], - scope: str, - path: Optional[str], - tool_ctx, - ) -> str: - """Set configuration value.""" - if not key: - return "Error: key required for set action" - if value is None: - return "Error: value required for set action" - - # Handle index scope - if key == "index.scope": - try: - new_scope = IndexScope(value) - self.index_config.set_scope( - new_scope, path if scope == "local" else None - ) - return f"Set {key}={value} ({'project' if path else 'global'})" - except ValueError: - return f"Error: Invalid scope value '{value}'. Valid: project, global, auto" - - # tools..enabled โ†’ enabled_tools mapping - if key.startswith("tools.") and key.endswith(".enabled"): - parts = key.split(".") - if len(parts) == 3: - tool_name = parts[1] - parsed = _parse_bool(value) - if parsed is None: - return "Error: value must be boolean (true/false)" - settings = load_settings(project_dir=path if scope == "local" else None) - et = dict(settings.enabled_tools) - et[tool_name] = parsed - settings.enabled_tools = et - # Save - if scope == "local" and path: - # Write a project .hanzo-mcp.json adjacent to the path - # Note: save_settings(local) saves to CWD; we target specific path here - out = self._save_project_settings(settings, path) - return f"Set {key}={parsed} (project: {out})" - else: - out = save_settings(settings, global_config=True) - return f"Set {key}={parsed} (global: {out})" - - # enabled_tools. - if key.startswith("enabled_tools."): - tool_name = key.split(".", 1)[1] - parsed = _parse_bool(value) - if parsed is None: - return "Error: value must be boolean (true/false)" - settings = load_settings(project_dir=path if scope == "local" else None) - et = dict(settings.enabled_tools) - et[tool_name] = parsed - settings.enabled_tools = et - if scope == "local" and path: - out = self._save_project_settings(settings, path) - return f"Set {key}={parsed} (project: {out})" - else: - out = save_settings(settings, global_config=True) - return f"Set {key}={parsed} (global: {out})" - - # Indexing (legacy) per-tool setting: .enabled (search indexers) - if "." in key: - tool, setting = key.split(".", 1) - if setting == "enabled": - parsed = _parse_bool(value) - if parsed is None: - return "Error: value must be boolean (true/false)" - self.index_config.set_indexing_enabled(tool, parsed) - return f"Set {key}={parsed}" - - return f"Unknown key: {key}" - - async def _handle_list(self, scope: str, path: Optional[str], tool_ctx) -> str: - """List all configuration.""" - status = self.index_config.get_status() - - output = ["=== Configuration ==="] - output.append(f"\nDefault scope: {status['default_scope']}") - - if path: - current_scope = self.index_config.get_scope(path) - output.append(f"Current path scope: {current_scope.value}") - - output.append(f"\nProjects with custom config: {status['project_count']}") - - output.append("\nTool settings (indexing):") - for tool, settings in status["tools"].items(): - output.append(f" {tool}:") - output.append(f" enabled: {settings['enabled']}") - output.append(f" per_project: {settings['per_project']}") - - # Also show enabled_tools snapshot - settings_snapshot = load_settings( - project_dir=path if scope == "local" else None - ) - output.append("\nEnabled tools (execution):") - for tool_name, enabled in sorted(settings_snapshot.enabled_tools.items()): - output.append(f" {tool_name}: {enabled}") - - return "\n".join(output) - - async def _handle_toggle( - self, key: Optional[str], scope: str, path: Optional[str], tool_ctx - ) -> str: - """Toggle configuration value.""" - if not key: - return "Error: key required for toggle action" - - # Handle index scope toggle - if key == "index.scope": - new_scope = self.index_config.toggle_scope( - path if scope == "local" else None - ) - return f"Toggled index.scope to {new_scope.value}" - - # Handle execution tool enable/disable: tools..enabled or enabled_tools. - if key.startswith("tools.") and key.endswith(".enabled"): - parts = key.split(".") - if len(parts) == 3: - tool_name = parts[1] - settings = load_settings(project_dir=path if scope == "local" else None) - current = bool(settings.enabled_tools.get(tool_name, True)) - settings.enabled_tools[tool_name] = not current - if scope == "local" and path: - out = self._save_project_settings(settings, path) - return f"Toggled {key} to {not current} (project: {out})" - else: - out = save_settings(settings, global_config=True) - return f"Toggled {key} to {not current} (global: {out})" - - if key.startswith("enabled_tools."): - tool_name = key.split(".", 1)[1] - settings = load_settings(project_dir=path if scope == "local" else None) - current = bool(settings.enabled_tools.get(tool_name, True)) - settings.enabled_tools[tool_name] = not current - if scope == "local" and path: - out = self._save_project_settings(settings, path) - return f"Toggled {key} to {not current} (project: {out})" - else: - out = save_settings(settings, global_config=True) - return f"Toggled {key} to {not current} (global: {out})" - - # Handle indexing toggles (legacy) - if "." in key: - tool, setting = key.split(".", 1) - if setting == "enabled": - current = self.index_config.is_indexing_enabled(tool) - self.index_config.set_indexing_enabled(tool, not current) - return f"Toggled {key} to {not current}" - - return f"Cannot toggle key: {key}" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-config/hanzo_tools/config/index_config.py b/pkg/hanzo-tools-config/hanzo_tools/config/index_config.py deleted file mode 100644 index 65a6fdc64..000000000 --- a/pkg/hanzo-tools-config/hanzo_tools/config/index_config.py +++ /dev/null @@ -1,179 +0,0 @@ -"""Index configuration for per-project vs global indexing. - -This module manages indexing configuration for different scopes. -""" - -import json -from enum import Enum -from typing import Any, Dict, Optional -from pathlib import Path - - -class IndexScope(Enum): - """Indexing scope options.""" - - PROJECT = "project" # Per-project indexing - GLOBAL = "global" # Global indexing - AUTO = "auto" # Auto-detect based on git root - - -class IndexConfig: - """Manages indexing configuration.""" - - def __init__(self, config_dir: Optional[Path] = None): - """Initialize index configuration.""" - self.config_dir = config_dir or Path.home() / ".hanzo" / "mcp" - self.config_file = self.config_dir / "index_config.json" - self._config = self._load_config() - - def _load_config(self) -> Dict[str, Any]: - """Load configuration from disk.""" - if self.config_file.exists(): - try: - with open(self.config_file, "r") as f: - return json.load(f) - except Exception: - pass - - # Default configuration - return { - "default_scope": IndexScope.AUTO.value, - "project_configs": {}, - "global_index_paths": [], - "index_settings": { - "vector": { - "enabled": True, - "auto_index": True, - "include_git_history": True, - }, - "symbols": { - "enabled": True, - "auto_index": False, - }, - "sql": { - "enabled": True, - "per_project": True, - }, - "graph": { - "enabled": True, - "per_project": True, - }, - }, - } - - def save_config(self) -> None: - """Save configuration to disk.""" - self.config_dir.mkdir(parents=True, exist_ok=True) - with open(self.config_file, "w") as f: - json.dump(self._config, f, indent=2) - - def get_scope(self, path: Optional[str] = None) -> IndexScope: - """Get indexing scope for a path.""" - if not path: - return IndexScope(self._config["default_scope"]) - - # Check project-specific config - project_root = self._find_project_root(path) - if project_root: - project_config = self._config["project_configs"].get(str(project_root)) - if project_config and "scope" in project_config: - return IndexScope(project_config["scope"]) - - # Use default - scope = IndexScope(self._config["default_scope"]) - - # Handle auto mode - if scope == IndexScope.AUTO: - if project_root: - return IndexScope.PROJECT - else: - return IndexScope.GLOBAL - - return scope - - def set_scope(self, scope: IndexScope, path: Optional[str] = None) -> None: - """Set indexing scope.""" - if path: - # Set for specific project - project_root = self._find_project_root(path) - if project_root: - if str(project_root) not in self._config["project_configs"]: - self._config["project_configs"][str(project_root)] = {} - self._config["project_configs"][str(project_root)][ - "scope" - ] = scope.value - else: - # Set global default - self._config["default_scope"] = scope.value - - self.save_config() - - def get_index_path(self, tool: str, path: Optional[str] = None) -> Path: - """Get index path for a tool and location.""" - scope = self.get_scope(path) - - if scope == IndexScope.PROJECT and path: - project_root = self._find_project_root(path) - if project_root: - return Path(project_root) / ".hanzo" / "index" / tool - - # Global index - return self.config_dir / "index" / tool - - def is_indexing_enabled(self, tool: str) -> bool: - """Check if indexing is enabled for a tool.""" - return self._config["index_settings"].get(tool, {}).get("enabled", True) - - def set_indexing_enabled(self, tool: str, enabled: bool) -> None: - """Enable/disable indexing for a tool.""" - if tool not in self._config["index_settings"]: - self._config["index_settings"][tool] = {} - self._config["index_settings"][tool]["enabled"] = enabled - self.save_config() - - def toggle_scope(self, path: Optional[str] = None) -> IndexScope: - """Toggle between project and global scope.""" - current = self.get_scope(path) - - if current == IndexScope.PROJECT: - new_scope = IndexScope.GLOBAL - elif current == IndexScope.GLOBAL: - new_scope = IndexScope.PROJECT - else: # AUTO - # Determine what auto resolves to and toggle - if path and self._find_project_root(path): - new_scope = IndexScope.GLOBAL - else: - new_scope = IndexScope.PROJECT - - self.set_scope(new_scope, path) - return new_scope - - def _find_project_root(self, path: str) -> Optional[Path]: - """Find project root (git root or similar).""" - current = Path(path).resolve() - - # Walk up looking for markers - markers = [".git", ".hg", "pyproject.toml", "package.json", "Cargo.toml"] - - while current != current.parent: - for marker in markers: - if (current / marker).exists(): - return current - current = current.parent - - return None - - def get_status(self) -> Dict[str, Any]: - """Get current configuration status.""" - return { - "default_scope": self._config["default_scope"], - "project_count": len(self._config["project_configs"]), - "tools": { - tool: { - "enabled": settings.get("enabled", True), - "per_project": settings.get("per_project", True), - } - for tool, settings in self._config["index_settings"].items() - }, - } diff --git a/pkg/hanzo-tools-config/hanzo_tools/config/mode_tool.py b/pkg/hanzo-tools-config/hanzo_tools/config/mode_tool.py deleted file mode 100644 index 5c73ed801..000000000 --- a/pkg/hanzo-tools-config/hanzo_tools/config/mode_tool.py +++ /dev/null @@ -1,365 +0,0 @@ -"""Tool for managing development modes with programmer personalities.""" - -from typing import Optional, override - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext -from hanzo_mcp.tools.common.mode import ModeRegistry, register_default_modes - -from hanzo_tools.core import BaseTool, auto_timeout - - -class ModeTool(BaseTool): - """Tool for managing development modes.""" - - name = "mode" - - def __init__(self): - """Initialize the mode tool.""" - super().__init__() - # Register default modes on initialization - register_default_modes() - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Manage development modes (programmer personalities). Actions: list (default), activate, switch, show, current, list_presets, select_preset. - -Usage: -mode -mode --action list -mode --action activate guido -mode --action switch guido -mode --action show linus -mode --action current -mode --action list_presets -mode --action select_preset --name fullstack""" - - @override - async def run( - self, - ctx: MCPContext, - action: str = "list", - name: Optional[str] = None, - ) -> str: - """Manage development modes. - - Args: - ctx: MCP context - action: Action to perform (list, activate, show, current) - name: Mode name (for activate/show actions) - - Returns: - Action result - """ - if action == "list": - modes = ModeRegistry.list() - if not modes: - return "No modes registered" - - output = ["Available development modes (100 programmer personalities):"] - active = ModeRegistry.get_active() - - # Group modes by category - categories = { - "Language Creators": [ - "guido", - "matz", - "brendan", - "dennis", - "bjarne", - "james", - "anders", - "larry", - "rasmus", - "rich", - ], - "Systems & Infrastructure": [ - "linus", - "rob", - "ken", - "bill", - "richard", - "brian", - "donald", - "graydon", - "ryan", - "mitchell", - ], - "Web & Frontend": [ - "tim", - "douglas", - "john", - "evan", - "jordan", - "jeremy", - "david", - "taylor", - "adrian", - "matt", - ], - "Database & Data": [ - "michael_s", - "michael_w", - "salvatore", - "dwight", - "edgar", - "jim_gray", - "jeff_dean", - "sanjay", - "mike", - "matei", - ], - "AI & Machine Learning": [ - "yann", - "geoffrey", - "yoshua", - "andrew", - "demis", - "ilya", - "andrej", - "chris", - "francois", - "jeremy_howard", - ], - "Security & Cryptography": [ - "bruce", - "phil", - "whitfield", - "ralph", - "daniel_b", - "moxie", - "theo", - "dan_kaminsky", - "katie", - "matt_blaze", - ], - "Gaming & Graphics": [ - "john_carmack", - "sid", - "shigeru", - "gabe", - "markus", - "jonathan", - "casey", - "tim_sweeney", - "hideo", - "will", - ], - "Open Source Leaders": [ - "miguel", - "nat", - "patrick", - "ian", - "mark_shuttleworth", - "lennart", - "bram", - "daniel_r", - "judd", - "fabrice", - ], - "Modern Innovators": [ - "vitalik", - "satoshi", - "chris_lattner", - "joe", - "jose", - "sebastian", - "palmer", - "dylan", - "guillermo", - "tom", - ], - "Special Configurations": [ - "fullstack", - "minimal", - "data_scientist", - "devops", - "security", - "academic", - "startup", - "enterprise", - "creative", - "hanzo", - ], - } - - for category, mode_names in categories.items(): - output.append(f"\n{category}:") - for mode_name in mode_names: - mode = next((m for m in modes if m.name == mode_name), None) - if mode: - marker = ( - " (active)" if active and active.name == mode.name else "" - ) - output.append( - f" {mode.name}{marker}: {mode.programmer} - {mode.description}" - ) - - output.append("\nUse 'mode --action activate ' to activate a mode") - - return "\n".join(output) - - elif action in ("activate", "switch"): - if not name: - return "Error: Mode name required for activate action" - - try: - ModeRegistry.set_active(name) - mode = ModeRegistry.get(name) - - output = [f"Activated mode: {mode.name}"] - output.append(f"Programmer: {mode.programmer}") - output.append(f"Description: {mode.description}") - if mode.philosophy: - output.append(f"Philosophy: {mode.philosophy}") - output.append(f"\nEnabled tools ({len(mode.tools)}):") - - # Group tools by category - core_tools = [] - package_tools = [] - ai_tools = [] - search_tools = [] - other_tools = [] - - for tool in sorted(mode.tools): - if tool in [ - "read", - "write", - "edit", - "multi_edit", - "bash", - "tree", - "grep", - ]: - core_tools.append(tool) - elif tool in ["npx", "uvx", "pip", "cargo", "gem"]: - package_tools.append(tool) - elif tool in ["agent", "consensus", "critic", "think"]: - ai_tools.append(tool) - elif tool in ["search", "symbols", "git_search"]: - search_tools.append(tool) - else: - other_tools.append(tool) - - if core_tools: - output.append(f" Core: {', '.join(core_tools)}") - if package_tools: - output.append(f" Package managers: {', '.join(package_tools)}") - if ai_tools: - output.append(f" AI tools: {', '.join(ai_tools)}") - if search_tools: - output.append(f" Search: {', '.join(search_tools)}") - if other_tools: - output.append(f" Specialized: {', '.join(other_tools)}") - - if mode.environment: - output.append("\nEnvironment variables:") - for key, value in mode.environment.items(): - output.append(f" {key}={value}") - - output.append( - "\nNote: Restart MCP session for changes to take full effect" - ) - - return "\n".join(output) - - except ValueError as e: - return str(e) - - elif action == "show": - if not name: - return "Error: Mode name required for show action" - - mode = ModeRegistry.get(name) - if not mode: - return f"Mode '{name}' not found" - - output = [f"Mode: {mode.name}"] - output.append(f"Programmer: {mode.programmer}") - output.append(f"Description: {mode.description}") - if mode.philosophy: - output.append(f"Philosophy: {mode.philosophy}") - output.append(f"\nTools ({len(mode.tools)}):") - - for tool in sorted(mode.tools): - output.append(f" - {tool}") - - if mode.environment: - output.append("\nEnvironment:") - for key, value in mode.environment.items(): - output.append(f" {key}={value}") - - return "\n".join(output) - - elif action == "current": - active = ModeRegistry.get_active() - if not active: - return "No mode currently active\nUse 'mode --action activate ' to activate one" - - output = [f"Current mode: {active.name}"] - output.append(f"Programmer: {active.programmer}") - output.append(f"Description: {active.description}") - if active.philosophy: - output.append(f"Philosophy: {active.philosophy}") - output.append(f"Enabled tools: {len(active.tools)}") - - return "\n".join(output) - - elif action == "list_presets": - modes = ModeRegistry.list() - presets = [m for m in modes if m.name in [ - "fullstack", "minimal", "data_scientist", "devops", - "security", "academic", "startup", "enterprise", - "creative", "hanzo", - ]] - if not presets: - return "No presets available" - output = ["Available presets:"] - for p in presets: - output.append(f" {p.name}: {p.programmer} - {p.description}") - return "\n".join(output) - - elif action == "select_preset": - if not name: - return "Error: Preset name required" - # select_preset is same as activate but only for preset modes - preset_names = [ - "fullstack", "minimal", "data_scientist", "devops", - "security", "academic", "startup", "enterprise", - "creative", "hanzo", - ] - if name not in preset_names: - return f"Error: '{name}' is not a preset. Use list_presets to see available presets." - try: - ModeRegistry.set_active(name) - mode = ModeRegistry.get(name) - return f"Selected preset: {mode.name} ({mode.programmer})" - except ValueError as e: - return str(e) - - else: - return f"Unknown action: {action}. Use 'list', 'activate', 'switch', 'show', 'current', 'list_presets', or 'select_preset'" - - def register(self, server: FastMCP) -> None: - """Register the tool with the MCP server.""" - tool_self = self - - @server.tool(name=self.name, description=self.description) - async def mode_handler( - ctx: MCPContext, action: str = "list", name: Optional[str] = None - ) -> str: - """Handle mode tool calls.""" - return await tool_self.run(ctx, action=action, name=name) - - @auto_timeout("mode") - async def call(self, ctx: MCPContext, **params) -> str: - """Call the tool with arguments.""" - return await self.run( - ctx, action=params.get("action", "list"), name=params.get("name") - ) - - -# Create tool instance -mode_tool = ModeTool() diff --git a/pkg/hanzo-tools-config/hanzo_tools/config/workspace_tool.py b/pkg/hanzo-tools-config/hanzo_tools/config/workspace_tool.py deleted file mode 100644 index fb1ef62cf..000000000 --- a/pkg/hanzo-tools-config/hanzo_tools/config/workspace_tool.py +++ /dev/null @@ -1,152 +0,0 @@ -"""Workspace context tool (HIP-0300). - -One tool for the Project Context axis. -Actions: detect, capabilities, help, schema -""" - -from __future__ import annotations - -import os -import json -import shutil -import asyncio -from typing import Any, ClassVar -from pathlib import Path - -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool - - -class WorkspaceTool(BaseTool): - """Workspace context tool (HIP-0300). - - Detects project languages, build systems, test frameworks, - and available tool capabilities. - - Actions: - - detect: Detect project languages, build systems, and VCS - - capabilities: List available system tools and runtimes - - help: Show all tool summaries - """ - - name: ClassVar[str] = "workspace" - VERSION: ClassVar[str] = "0.1.0" - - def __init__(self, cwd: str | None = None): - super().__init__() - self.cwd = cwd or os.getcwd() - self._register_workspace_actions() - - @property - def description(self) -> str: - return """Workspace context tool (HIP-0300). - -Actions: -- detect: Detect project languages, build systems, VCS, test frameworks -- capabilities: List available system tools and runtimes -- help: Show all tool summaries -""" - - def _register_workspace_actions(self): - """Register all workspace actions.""" - - @self.action("detect", "Detect project languages, build systems, and VCS") - async def detect(ctx: MCPContext, path: str | None = None) -> dict: - root = Path(path or self.cwd) - languages: list[str] = [] - build: list[str] = [] - test: list[str] = [] - vcs: str | None = None - - # VCS - if (root / ".git").exists(): - vcs = "git" - - # Languages & build systems - if (root / "package.json").exists(): - languages.extend(["typescript", "javascript"]) - build.append("npm") - if (root / "tsconfig.json").exists(): - if "typescript" not in languages: - languages.append("typescript") - if (root / "pyproject.toml").exists(): - languages.append("python") - build.append("uv") - if (root / "Cargo.toml").exists(): - languages.append("rust") - build.append("cargo") - if (root / "go.mod").exists(): - languages.append("go") - build.append("go") - if (root / "Makefile").exists(): - build.append("make") - if (root / "compose.yml").exists(): - build.append("docker-compose") - if (root / "Dockerfile").exists(): - build.append("docker") - - # Test frameworks - if (root / "jest.config.ts").exists() or (root / "jest.config.js").exists(): - test.append("jest") - if (root / "vitest.config.ts").exists(): - test.append("vitest") - if (root / "pytest.ini").exists() or (root / "conftest.py").exists(): - test.append("pytest") - - return { - "root": str(root), - "languages": list(dict.fromkeys(languages)), - "build": list(dict.fromkeys(build)), - "test": test, - "vcs": vcs, - } - - @self.action("capabilities", "List available system tools and runtimes") - async def capabilities(ctx: MCPContext) -> dict: - has_rg = shutil.which("rg") is not None - has_git = shutil.which("git") is not None - has_node = shutil.which("node") is not None - has_python = shutil.which("python3") is not None - has_cargo = shutil.which("cargo") is not None - - return { - "search": "ripgrep" if has_rg else "grep", - "vcs": "git" if has_git else None, - "runtimes": { - "node": has_node, - "python": has_python, - "rust": has_cargo, - }, - "tools": [ - "fs", "exec", "code", "git", "fetch", "computer", "workspace", - "browser", "think", "llm", "memory", "hanzo", "plan", "tasks", "mode", - ], - } - - @self.action("help", "Show all tool summaries") - async def help_action(ctx: MCPContext) -> dict: - return { - "tools": { - "fs": "Filesystem: read, write, stat, list, mkdir, rm, apply_patch, search_text", - "exec": "Processes: exec, ps, kill, logs", - "code": "Semantics: parse, symbols, definition, references, transform, summarize", - "git": "Version control: status, diff, apply, commit, branch, checkout, log", - "fetch": "Network: search, fetch, download, crawl, head", - "workspace": "Workspace: detect, capabilities, help", - "computer": "Native OS control: click, type, screenshot, window management", - "browser": "Web automation: navigate, click, fill, screenshot (Playwright)", - "think": "Structured reasoning: think, critic, review, summarize", - "llm": "LLM operations: query, consensus, models, enable, disable, test", - "memory": "Memory: store, recall, list, delete, search, stats, clear, export, import", - "hanzo": "Hanzo platform: iam, kms, paas, commerce, auth, api, billing, ingress, mpc, team", - "plan": "Planning: create, show, update, list, next, archive, add_step, remove_step", - "tasks": "Tasks: list, add, update, remove, clear", - "mode": "Modes: list, activate, show, current", - } - } - - async def call(self, ctx: MCPContext, **kwargs: Any) -> str: - """Route to action handler.""" - action = kwargs.pop("action", "help") - return await self._dispatch(ctx, action, **kwargs) diff --git a/pkg/hanzo-tools-config/pyproject.toml b/pkg/hanzo-tools-config/pyproject.toml deleted file mode 100644 index 89d05d624..000000000 --- a/pkg/hanzo-tools-config/pyproject.toml +++ /dev/null @@ -1,25 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-config" -version = "0.2.1" -description = "Configuration tools for Hanzo AI - mode, settings management" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "tools", "config", "mcp", "ai"] -dependencies = [ - "hanzo-tools>=0.3.0", - "mcp>=1.25.0", - "fastmcp>=2.14.1", - "pydantic>=2.12.5", -] - -[project.entry-points."hanzo.tools"] -config = "hanzo_tools.config:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] diff --git a/pkg/hanzo-tools-config/tests/test_config_tools.py b/pkg/hanzo-tools-config/tests/test_config_tools.py deleted file mode 100644 index 3d9f5ab8c..000000000 --- a/pkg/hanzo-tools-config/tests/test_config_tools.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Tests for hanzo-tools-config.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import config - - assert config is not None - - def test_import_tools(self): - from hanzo_tools.config import TOOLS - - assert len(TOOLS) > 0 - - -class TestConfigTool: - """Tests for config tools.""" - - @pytest.fixture - def tool(self): - from hanzo_tools.config import TOOLS - - return TOOLS[0]() if TOOLS else None - - def test_has_description(self, tool): - if tool: - assert tool.description diff --git a/pkg/hanzo-tools-core/README.md b/pkg/hanzo-tools-core/README.md deleted file mode 100644 index 7063a4d80..000000000 --- a/pkg/hanzo-tools-core/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# hanzo-tools-core - -Core infrastructure for Hanzo MCP tools. - -## Installation - -```bash -pip install hanzo-tools-core -``` - -## Components - -### BaseTool -Base class for all Hanzo tools. - -```python -from hanzo_tools.core import BaseTool - -class MyTool(BaseTool): - name = "my_tool" - - @property - def description(self) -> str: - return "My custom tool" - - async def call(self, ctx, **params) -> str: - return "result" -``` - -### ToolRegistry -Manage tool registration and discovery. - -```python -from hanzo_tools.core import ToolRegistry - -ToolRegistry.register_tools(mcp_server, [MyTool()]) -``` - -### PermissionManager -Access control for file and command operations. - -```python -from hanzo_tools.core import PermissionManager - -pm = PermissionManager(allowed_paths=["/home/user/project"]) -if pm.can_access("/home/user/project/file.py"): - # proceed -``` - -### Decorators - -```python -from hanzo_tools.core import auto_timeout - -@auto_timeout("my_tool") -async def my_function(): - # Auto-backgrounds after timeout - pass -``` - -## License - -MIT diff --git a/pkg/hanzo-tools-core/hanzo_tools/__init__.py b/pkg/hanzo-tools-core/hanzo_tools/__init__.py deleted file mode 100644 index 9783a9299..000000000 --- a/pkg/hanzo-tools-core/hanzo_tools/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Hanzo Tools - Modular tool packages for AI agents. - -This is the namespace package for all hanzo-tools-* packages. -Each tool category is a separate installable package: - - pip install hanzo-tools-core # Base infrastructure - pip install hanzo-tools-filesystem # File operations - pip install hanzo-tools-shell # Shell/command execution - pip install hanzo-tools-browser # Browser automation - pip install hanzo-tools-agent # Agent orchestration - pip install hanzo-tools-llm # LLM integrations - pip install hanzo-tools-database # Database tools - pip install hanzo-tools-memory # Memory/knowledge base - pip install hanzo-tools-editor # Editor integrations - pip install hanzo-tools-jupyter # Jupyter notebook tools - pip install hanzo-tools-lsp # Language server protocol - pip install hanzo-tools-refactor # Code refactoring - pip install hanzo-tools-vector # Vector search - pip install hanzo-tools-todo # Task management - -Or install all at once: - pip install hanzo-tools[all] -""" - -__path__ = __import__("pkgutil").extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-core/hanzo_tools/core/__init__.py b/pkg/hanzo-tools-core/hanzo_tools/core/__init__.py deleted file mode 100644 index a3bfbb3a1..000000000 --- a/pkg/hanzo-tools-core/hanzo_tools/core/__init__.py +++ /dev/null @@ -1,67 +0,0 @@ -"""DEPRECATED: Use hanzo-tools instead. - -This package re-exports from hanzo_tools.core for backwards compatibility. -""" - -import warnings - -warnings.warn( - "hanzo-tools-core is deprecated. Use hanzo-tools instead: pip install hanzo-tools", - DeprecationWarning, - stacklevel=2, -) - -# Re-export everything from hanzo-tools -from hanzo_tools.core.base import ( - BaseTool as BaseToolABC, # Low-level ABC - ToolRegistry, - FileSystemTool, -) -from hanzo_tools.core.types import MCPResourceDocument -from hanzo_tools.core.context import ToolContext, create_tool_context -from hanzo_tools.core.id_tool import IdTool, id_tool -from hanzo_tools.core.unified import ( - Range, - Paging, - BaseTool, # HIP-0300 unified tool - use this - ErrorCode, - ToolError, - ActionHandler, - ConflictError, - NotFoundError, - InvalidParamsError, - file_uri, - content_hash, -) -from hanzo_tools.core.decorators import auto_timeout -from hanzo_tools.core.permissions import PermissionManager - -__all__ = [ - # HIP-0300 Base class - use this for new tools - "BaseTool", - # Low-level ABC (for FileSystemTool etc) - "BaseToolABC", - "FileSystemTool", - "ToolRegistry", - # HIP-0300 helpers - "ActionHandler", - "ToolError", - "ConflictError", - "NotFoundError", - "InvalidParamsError", - "Paging", - "Range", - "ErrorCode", - "content_hash", - "file_uri", - # Identity tool - "IdTool", - "id_tool", - # Types - "MCPResourceDocument", - # Context - "PermissionManager", - "ToolContext", - "create_tool_context", - "auto_timeout", -] diff --git a/pkg/hanzo-tools-core/hanzo_tools/core/base.py b/pkg/hanzo-tools-core/hanzo_tools/core/base.py deleted file mode 100644 index 85abc03dc..000000000 --- a/pkg/hanzo-tools-core/hanzo_tools/core/base.py +++ /dev/null @@ -1,251 +0,0 @@ -"""Base classes for Hanzo tool packages. - -Provides the foundation for all tool implementations: -- BaseTool: Abstract base class defining the tool interface -- FileSystemTool: Base class for filesystem operations -- ToolRegistry: Central registry for tool management -""" - -import inspect -import logging -import functools -from abc import ABC, abstractmethod -from typing import Any, Callable, ClassVar, final -from pathlib import Path -from collections.abc import Awaitable - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -logger = logging.getLogger(__name__) - - -def with_error_logging(tool_name: str) -> Callable: - """Decorator to add comprehensive error logging to tool functions. - - Args: - tool_name: Name of the tool for logging purposes - - Returns: - Decorator function - """ - log_dir = Path.home() / ".hanzo" / "mcp" / "logs" - log_dir.mkdir(parents=True, exist_ok=True) - - def decorator(func: Callable[..., Awaitable[str]]) -> Callable[..., Awaitable[str]]: - @functools.wraps(func) - async def wrapper(*args: Any, **kwargs: Any) -> str: - try: - return await func(*args, **kwargs) - except TypeError as e: - error_msg = str(e) - if "takes" in error_msg and "positional argument" in error_msg: - sig = inspect.signature(func) - logger.error( - f"Tool {tool_name} call signature mismatch: " - f"expected {func.__name__}{sig}, got args={args}, kwargs={kwargs}" - ) - logger.exception(f"Tool {tool_name} TypeError: {e}") - return f"Error executing tool '{tool_name}': {error_msg}\n\nCheck logs at ~/.hanzo/mcp/logs/ for details." - except Exception as e: - logger.exception(f"Tool {tool_name} error: {e}") - return f"Error executing tool '{tool_name}': {str(e)}\n\nCheck logs at ~/.hanzo/mcp/logs/ for details." - - return wrapper - - return decorator - - -def handle_connection_errors( - func: Callable[..., Awaitable[str]], -) -> Callable[..., Awaitable[str]]: - """Decorator to handle connection errors gracefully.""" - - @functools.wraps(func) - async def wrapper(*args: Any, **kwargs: Any) -> str: - try: - return await func(*args, **kwargs) - except Exception as e: - error_name = type(e).__name__ - if any( - name in error_name - for name in [ - "ClosedResourceError", - "ConnectionError", - "BrokenPipeError", - ] - ): - return f"Client disconnected during operation: {error_name}" - raise - - return wrapper - - -class BaseTool(ABC): - """Abstract base class for all Hanzo tools. - - All tool packages must implement this interface to be compatible - with the hanzo-mcp server and tool registry. - - Example: - class MyTool(BaseTool): - @property - def name(self) -> str: - return "my_tool" - - @property - def description(self) -> str: - return "Does something useful" - - async def call(self, ctx, **params) -> str: - return "Result" - - def register(self, mcp_server): - @mcp_server.tool() - async def my_tool(...): - return await self.call(...) - """ - - @property - @abstractmethod - def name(self) -> str: - """Get the tool name as it appears in the MCP server.""" - pass - - @property - @abstractmethod - def description(self) -> str: - """Get detailed description of the tool's purpose and usage.""" - pass - - @abstractmethod - async def call(self, ctx: MCPContext, **params: Any) -> Any: - """Execute the tool with the given parameters. - - Args: - ctx: MCP context for the tool call - **params: Tool parameters provided by the caller - - Returns: - Tool execution result - """ - pass - - @abstractmethod - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server. - - Must create a wrapper function with explicit parameters - that calls this tool's call method. - - Args: - mcp_server: The FastMCP server instance - """ - pass - - -class FileSystemTool(BaseTool, ABC): - """Base class for filesystem-related tools. - - Provides common functionality for working with files and directories, - including permission checking and path validation. - """ - - def __init__(self, permission_manager: "PermissionManager | None" = None) -> None: - """Initialize filesystem tool. - - Args: - permission_manager: Permission manager for access control (auto-created if None) - """ - if permission_manager is None: - from hanzo_tools.core.permissions import PermissionManager - - permission_manager = PermissionManager() - self.permission_manager = permission_manager - - def validate_path(self, path: str, param_name: str = "path") -> "ValidationResult": - """Validate a path parameter.""" - from hanzo_tools.core.validation import validate_path_parameter - - return validate_path_parameter(path, param_name) - - def is_path_allowed(self, path: str) -> bool: - """Check if a path is allowed according to permission settings.""" - return self.permission_manager.is_path_allowed(path) - - -@final -class ToolRegistry: - """Registry for Hanzo tools. - - Provides functionality for registering tool implementations - with an MCP server, with support for enable/disable states. - """ - - # Class-level storage for tool states - _enabled_tools: ClassVar[dict[str, bool]] = {} - _config_loaded: ClassVar[bool] = False - - @classmethod - def _load_config(cls) -> None: - """Load tool enable/disable states from config.""" - if cls._config_loaded: - return - - import json - - config_file = Path.home() / ".hanzo" / "mcp" / "tool_states.json" - if config_file.exists(): - try: - with open(config_file) as f: - cls._enabled_tools = json.load(f) - except Exception: - pass - cls._config_loaded = True - - @classmethod - def is_tool_enabled(cls, tool_name: str) -> bool: - """Check if a tool is enabled.""" - cls._load_config() - return cls._enabled_tools.get(tool_name, True) # Enabled by default - - @classmethod - def set_tool_enabled( - cls, tool_name: str, enabled: bool, persist: bool = True - ) -> None: - """Enable or disable a tool.""" - import json - - cls._load_config() - cls._enabled_tools[tool_name] = enabled - - if persist: - config_file = Path.home() / ".hanzo" / "mcp" / "tool_states.json" - config_file.parent.mkdir(parents=True, exist_ok=True) - with open(config_file, "w") as f: - json.dump(cls._enabled_tools, f, indent=2) - - @staticmethod - def register_tool(mcp_server: FastMCP, tool: BaseTool) -> None: - """Register a tool with the MCP server. - - Args: - mcp_server: The FastMCP server instance - tool: The tool to register - """ - if ToolRegistry.is_tool_enabled(tool.name): - tool.register(mcp_server) - logger.debug(f"Registered tool: {tool.name}") - else: - logger.debug(f"Skipped disabled tool: {tool.name}") - - @staticmethod - def register_tools(mcp_server: FastMCP, tools: list[BaseTool]) -> None: - """Register multiple tools with the MCP server.""" - for tool in tools: - ToolRegistry.register_tool(mcp_server, tool) - - -# Import PermissionManager type for type hints -from hanzo_tools.core.validation import ValidationResult -from hanzo_tools.core.permissions import PermissionManager diff --git a/pkg/hanzo-tools-core/hanzo_tools/core/context.py b/pkg/hanzo-tools-core/hanzo_tools/core/context.py deleted file mode 100644 index e31d6d236..000000000 --- a/pkg/hanzo-tools-core/hanzo_tools/core/context.py +++ /dev/null @@ -1,67 +0,0 @@ -"""Tool execution context utilities.""" - -import logging -from typing import Any, Optional -from dataclasses import field, dataclass - -from mcp.server.fastmcp import Context as MCPContext - -logger = logging.getLogger(__name__) - - -@dataclass -class ToolContext: - """Extended context for tool execution. - - Provides utilities for logging, progress reporting, - and accessing the MCP context. - """ - - mcp_ctx: MCPContext - tool_name: Optional[str] = None - metadata: dict[str, Any] = field(default_factory=dict) - - async def set_tool_info(self, tool_name: str) -> None: - """Set the current tool name for logging.""" - self.tool_name = tool_name - - async def info(self, message: str) -> None: - """Log an info message.""" - logger.info(f"[{self.tool_name or 'tool'}] {message}") - - async def warning(self, message: str) -> None: - """Log a warning message.""" - logger.warning(f"[{self.tool_name or 'tool'}] {message}") - - async def error(self, message: str) -> None: - """Log an error message.""" - logger.error(f"[{self.tool_name or 'tool'}] {message}") - - async def debug(self, message: str) -> None: - """Log a debug message.""" - logger.debug(f"[{self.tool_name or 'tool'}] {message}") - - async def progress(self, current: int, total: int, message: str = "") -> None: - """Report progress.""" - pct = (current / total * 100) if total > 0 else 0 - await self.info(f"Progress: {current}/{total} ({pct:.1f}%) {message}") - - def get(self, key: str, default: Any = None) -> Any: - """Get a metadata value.""" - return self.metadata.get(key, default) - - def set(self, key: str, value: Any) -> None: - """Set a metadata value.""" - self.metadata[key] = value - - -def create_tool_context(mcp_ctx: MCPContext) -> ToolContext: - """Create a ToolContext from an MCP context. - - Args: - mcp_ctx: The MCP context from the tool call - - Returns: - Extended ToolContext for tool execution - """ - return ToolContext(mcp_ctx=mcp_ctx) diff --git a/pkg/hanzo-tools-core/hanzo_tools/core/decorators.py b/pkg/hanzo-tools-core/hanzo_tools/core/decorators.py deleted file mode 100644 index b2868c49d..000000000 --- a/pkg/hanzo-tools-core/hanzo_tools/core/decorators.py +++ /dev/null @@ -1,103 +0,0 @@ -"""Decorators for tool functions.""" - -import os -import asyncio -import functools -from typing import Any, Callable, Optional -from collections.abc import Awaitable - -# Default timeouts per tool (can be overridden via env vars) -DEFAULT_TIMEOUTS: dict[str, int] = { - "read": 30, - "write": 60, - "edit": 60, - "search": 120, - "dag": 600, - "browser": 300, - "default": 120, -} - - -def get_timeout(tool_name: str) -> int: - """Get timeout for a tool. - - Checks environment variable HANZO_TIMEOUT_{TOOL_NAME} first, - then falls back to defaults. - """ - env_key = f"HANZO_TIMEOUT_{tool_name.upper()}" - if env_val := os.environ.get(env_key): - try: - return int(env_val) - except ValueError: - pass - - return DEFAULT_TIMEOUTS.get(tool_name, DEFAULT_TIMEOUTS["default"]) - - -def auto_timeout( - tool_name: str, - timeout: Optional[int] = None, -) -> Callable: - """Decorator to add automatic timeout to async tool functions. - - Args: - tool_name: Name of the tool (for logging and config) - timeout: Override timeout in seconds (optional) - - Returns: - Decorator that wraps the function with timeout handling - """ - - def decorator(func: Callable[..., Awaitable[Any]]) -> Callable[..., Awaitable[Any]]: - @functools.wraps(func) - async def wrapper(*args: Any, **kwargs: Any) -> Any: - effective_timeout = timeout or get_timeout(tool_name) - - try: - return await asyncio.wait_for( - func(*args, **kwargs), - timeout=effective_timeout, - ) - except asyncio.TimeoutError: - return f"Tool '{tool_name}' timed out after {effective_timeout}s" - - return wrapper - - return decorator - - -def retry( - max_attempts: int = 3, - delay: float = 1.0, - backoff: float = 2.0, - exceptions: tuple = (Exception,), -) -> Callable: - """Decorator to add retry logic to async functions. - - Args: - max_attempts: Maximum number of attempts - delay: Initial delay between retries (seconds) - backoff: Multiplier for delay after each attempt - exceptions: Tuple of exceptions to catch and retry - """ - - def decorator(func: Callable[..., Awaitable[Any]]) -> Callable[..., Awaitable[Any]]: - @functools.wraps(func) - async def wrapper(*args: Any, **kwargs: Any) -> Any: - current_delay = delay - last_exception = None - - for attempt in range(max_attempts): - try: - return await func(*args, **kwargs) - except exceptions as e: - last_exception = e - if attempt < max_attempts - 1: - await asyncio.sleep(current_delay) - current_delay *= backoff - - raise last_exception - - return wrapper - - return decorator diff --git a/pkg/hanzo-tools-core/hanzo_tools/core/id_tool.py b/pkg/hanzo-tools-core/hanzo_tools/core/id_tool.py deleted file mode 100644 index 7a9ca9a37..000000000 --- a/pkg/hanzo-tools-core/hanzo_tools/core/id_tool.py +++ /dev/null @@ -1,302 +0,0 @@ -"""Identity tool for HIP-0300 architecture. - -This module provides the 'id' tool for identity and addressing primitives: -- hash: Content โ†’ Digest -- uri: Path โ†’ file:// URI -- ref: Location โ†’ Reference -- verify: (Content, Digest) โ†’ Bool - -Identity is fundamental to composability: -- Cache keys -- base_hash preconditions -- Deduplication -- Content-addressable storage - -Effect lattice position: PURE -All operations are deterministic and side-effect free. -""" - -import os -import json -import hashlib -from typing import Any, ClassVar -from pathlib import Path -from urllib.parse import quote - -import aiofiles -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core.unified import ( - BaseTool, - ToolError, - NotFoundError, - InvalidParamsError, -) - -# Supported hash algorithms -HASH_ALGORITHMS = ["sha256", "sha512", "sha1", "md5", "blake2b", "blake2s"] - - -class IdTool(BaseTool): - """Identity and addressing tool (HIP-0300). - - Handles identity primitives: - - hash: Content โ†’ Digest - - uri: Path โ†’ file:// URI - - ref: Location โ†’ Reference - - verify: Check content integrity - - All operations are PURE (no side effects). - """ - - name: ClassVar[str] = "id" - VERSION: ClassVar[str] = "0.1.0" - - def __init__(self, cwd: str | None = None): - super().__init__() - self.cwd = cwd or os.getcwd() - self._register_id_actions() - - @property - def description(self) -> str: - return """Identity and addressing tool (HIP-0300). - -Actions: -- hash: Compute content hash (Content โ†’ Digest) -- uri: Convert path to file:// URI -- ref: Create location reference (path + optional line/col) -- verify: Check content matches expected hash - -Identity is fundamental to composability: -- Cache keys for memoization -- base_hash preconditions for safe edits -- Content-addressable storage -- Deduplication - -All operations are PURE. -""" - - def _compute_hash(self, content: bytes, algo: str = "sha256") -> str: - """Compute hash of content.""" - if algo not in HASH_ALGORITHMS: - raise InvalidParamsError( - f"Unknown algorithm: {algo}", - param="algo", - expected=", ".join(HASH_ALGORITHMS), - ) - - h = hashlib.new(algo) - h.update(content) - return f"{algo}:{h.hexdigest()}" - - def _path_to_uri(self, path: str) -> str: - """Convert path to file:// URI.""" - abs_path = Path(path).resolve() - # Properly encode the path for URI - encoded = quote(str(abs_path), safe="/:") - return f"file://{encoded}" - - def _register_id_actions(self): - """Register all identity actions.""" - - @self.action("hash", "Compute content hash") - async def hash_content( - ctx: MCPContext, - path: str | None = None, - text: str | None = None, - algo: str = "sha256", - ) -> dict: - """Compute hash of file or text content. - - Args: - path: File path to hash - text: Inline text to hash (alternative to path) - algo: Hash algorithm (sha256, sha512, sha1, md5, blake2b, blake2s) - - Returns: - {digest, algo, size} - - Effect: PURE - """ - if path: - full_path = ( - Path(path) if Path(path).is_absolute() else Path(self.cwd) / path - ) - if not full_path.exists(): - raise NotFoundError(f"File not found: {path}", uri=str(full_path)) - async with aiofiles.open(full_path, "rb") as f: - content = await f.read() - elif text is not None: - content = text.encode("utf-8") - else: - raise InvalidParamsError("Either path or text required") - - digest = self._compute_hash(content, algo) - - return { - "digest": digest, - "algo": algo, - "size": len(content), - } - - @self.action("uri", "Convert path to file:// URI") - async def to_uri( - ctx: MCPContext, - path: str, - ) -> dict: - """Convert filesystem path to file:// URI. - - Args: - path: Filesystem path - - Returns: - {uri, path} - - Effect: PURE - """ - full_path = ( - Path(path) if Path(path).is_absolute() else Path(self.cwd) / path - ) - uri = self._path_to_uri(str(full_path)) - - return { - "uri": uri, - "path": str(full_path), - "exists": full_path.exists(), - } - - @self.action("ref", "Create location reference") - async def create_ref( - ctx: MCPContext, - path: str, - line: int | None = None, - col: int | None = None, - end_line: int | None = None, - end_col: int | None = None, - ) -> dict: - """Create a location reference (URI + optional range). - - Args: - path: File path - line: Start line (0-based) - col: Start column (0-based) - end_line: End line (optional) - end_col: End column (optional) - - Returns: - {uri, range?, hash?} - - Effect: PURE - """ - full_path = ( - Path(path) if Path(path).is_absolute() else Path(self.cwd) / path - ) - uri = self._path_to_uri(str(full_path)) - - result = { - "uri": uri, - "path": str(full_path), - } - - # Add range if line specified - if line is not None: - result["range"] = { - "start": {"line": line, "col": col or 0}, - } - if end_line is not None: - result["range"]["end"] = { - "line": end_line, - "col": end_col or 0, - } - - # Add hash if file exists - if full_path.exists(): - async with aiofiles.open(full_path, "rb") as f: - content = await f.read() - result["hash"] = self._compute_hash(content) - - return result - - @self.action("verify", "Verify content matches expected hash") - async def verify_hash( - ctx: MCPContext, - path: str | None = None, - text: str | None = None, - expected: str = "", - ) -> dict: - """Verify content matches expected hash. - - Args: - path: File path to verify - text: Inline text to verify (alternative) - expected: Expected hash (format: "algo:hexdigest") - - Returns: - {match, actual, expected} - - Effect: PURE - """ - if not expected: - raise InvalidParamsError("expected hash required") - - # Parse expected hash format - if ":" in expected: - algo, _ = expected.split(":", 1) - else: - algo = "sha256" - expected = f"sha256:{expected}" - - # Get content - if path: - full_path = ( - Path(path) if Path(path).is_absolute() else Path(self.cwd) / path - ) - if not full_path.exists(): - raise NotFoundError(f"File not found: {path}") - async with aiofiles.open(full_path, "rb") as f: - content = await f.read() - elif text is not None: - content = text.encode("utf-8") - else: - raise InvalidParamsError("Either path or text required") - - actual = self._compute_hash(content, algo) - - return { - "match": actual == expected, - "actual": actual, - "expected": expected, - } - - @self.action("algorithms", "List supported hash algorithms") - async def list_algorithms(ctx: MCPContext) -> dict: - """List supported hash algorithms. - - Returns: - {algorithms, default} - - Effect: PURE - """ - return { - "algorithms": HASH_ALGORITHMS, - "default": "sha256", - } - - def register(self, mcp_server: FastMCP) -> None: - """Register as 'id' tool with MCP server.""" - tool_name = self.name - tool_description = self.description - - @mcp_server.tool(name=tool_name, description=tool_description) - async def handler( - ctx: MCPContext, - action: str = "help", - **kwargs: Any, - ) -> str: - result = await self.call(ctx, action=action, **kwargs) - return json.dumps(result, indent=2, default=str) - - -# Singleton -id_tool = IdTool diff --git a/pkg/hanzo-tools-core/hanzo_tools/core/permissions.py b/pkg/hanzo-tools-core/hanzo_tools/core/permissions.py deleted file mode 100644 index 228baf96e..000000000 --- a/pkg/hanzo-tools-core/hanzo_tools/core/permissions.py +++ /dev/null @@ -1,86 +0,0 @@ -"""Permission management for filesystem tools.""" - -import os -from typing import Optional -from pathlib import Path - - -class PermissionManager: - """Manages filesystem permissions for tools. - - Controls which paths tools are allowed to access. - """ - - def __init__( - self, - allowed_paths: Optional[list[str | Path]] = None, - deny_patterns: Optional[list[str]] = None, - ): - """Initialize permission manager. - - Args: - allowed_paths: List of allowed base paths (defaults to cwd) - deny_patterns: Patterns to deny (e.g., '.git', 'node_modules') - """ - self.allowed_paths: list[Path] = [] - if allowed_paths: - for p in allowed_paths: - self.allowed_paths.append(Path(p).resolve()) - else: - self.allowed_paths.append(Path.cwd()) - - self.deny_patterns = deny_patterns or [ - ".git", - "__pycache__", - ".pyc", - "node_modules", - ".env", - ".secrets", - ] - - def is_path_allowed(self, path: str | Path) -> bool: - """Check if a path is allowed. - - Args: - path: Path to check - - Returns: - True if path is within allowed paths and not denied - """ - try: - resolved = Path(path).resolve() - - # Check if path is under any allowed path - is_under_allowed = any( - self._is_subpath(resolved, allowed) for allowed in self.allowed_paths - ) - - if not is_under_allowed: - return False - - # Check deny patterns - path_str = str(resolved) - for pattern in self.deny_patterns: - if pattern in path_str: - return False - - return True - - except Exception: - return False - - def _is_subpath(self, path: Path, parent: Path) -> bool: - """Check if path is under parent.""" - try: - path.relative_to(parent) - return True - except ValueError: - return False - - def add_allowed_path(self, path: str | Path) -> None: - """Add a path to the allowed list.""" - self.allowed_paths.append(Path(path).resolve()) - - def add_deny_pattern(self, pattern: str) -> None: - """Add a deny pattern.""" - self.deny_patterns.append(pattern) diff --git a/pkg/hanzo-tools-core/hanzo_tools/core/types.py b/pkg/hanzo-tools-core/hanzo_tools/core/types.py deleted file mode 100644 index 578c829e4..000000000 --- a/pkg/hanzo-tools-core/hanzo_tools/core/types.py +++ /dev/null @@ -1,171 +0,0 @@ -"""Type definitions for Hanzo tool packages.""" - -import json -from typing import Any, Dict, List, Optional -from dataclasses import dataclass - - -@dataclass -class MCPResourceDocument: - """Resource document returned by MCP tools. - - Output format options: - - to_json_string(): Clean JSON format (default for structured data) - - to_readable_string(): Human-readable formatted text for display - - to_dict(): Full dict structure with data/metadata - """ - - data: Dict[str, Any] - metadata: Optional[Dict[str, Any]] = None - - def to_dict(self) -> Dict[str, Any]: - """Convert to dictionary format with data/metadata structure.""" - result = {"data": self.data} - if self.metadata: - result["metadata"] = self.metadata - return result - - def to_json_string(self) -> str: - """Convert to clean JSON string.""" - # Return wrapped in "result" for consistency - return json.dumps({"result": self.data}, indent=2) - - def to_readable_string(self) -> str: - """Convert to human-readable formatted string for display. - - Optimized for readability in Claude Code output panels. - """ - lines: List[str] = [] - - if isinstance(self.data, dict): - # Handle search/find results with "results" array - if "results" in self.data: - results = self.data["results"] - stats = self.data.get("stats", {}) - pagination = self.data.get("pagination", {}) - - # Header with stats - if stats: - query = stats.get("query", stats.get("pattern", "")) - total = stats.get("total", len(results)) - time_ms = stats.get("time_ms", {}) - if time_ms: - if isinstance(time_ms, dict): - total_time = sum(time_ms.values()) - else: - total_time = time_ms - lines.append( - f"# Search: '{query}' ({total} results, {total_time}ms)" - ) - else: - lines.append(f"# Found {total} results for '{query}'") - else: - lines.append(f"# Found {len(results)} results") - lines.append("") - - # Format each result - for i, result in enumerate(results[:50], 1): - if isinstance(result, dict): - # Common patterns for search results - file_path = result.get("file", result.get("path", "")) - line_num = result.get("line", result.get("line_number", "")) - match_text = result.get( - "match", result.get("text", result.get("content", "")) - ) - result_type = result.get("type", "") - - if file_path: - loc = f"{file_path}:{line_num}" if line_num else file_path - lines.append(f"{i}. {loc}") - if match_text: - # Truncate long matches for readability - preview = ( - match_text[:200] + "..." - if len(match_text) > 200 - else match_text - ) - lines.append(f" {preview}") - if result_type: - lines.append(f" [{result_type}]") - else: - lines.append(f"{i}. {json.dumps(result, default=str)}") - else: - lines.append(f"{i}. {result}") - - # Show pagination info - if pagination: - page = pagination.get("page", 1) - total = pagination.get("total", 0) - has_next = pagination.get("has_next", False) - if has_next and total > 0: - total_pages = (total // 50) + 1 - lines.append( - f"\n... page {page} of {total_pages} ({total} total)" - ) - - # Handle command execution results - elif ( - "output" in self.data or "stdout" in self.data or "stderr" in self.data - ): - # Shell command output - exit_code = self.data.get("exit_code", self.data.get("returncode", 0)) - stdout = self.data.get("output", self.data.get("stdout", "")) - stderr = self.data.get("stderr", "") - elapsed = self.data.get("elapsed", self.data.get("time_ms", "")) - - if exit_code == 0: - lines.append(f"โœ“ Command succeeded") - else: - lines.append(f"โœ— Command failed (exit {exit_code})") - - if elapsed: - lines.append( - f"Time: {elapsed}ms" - if isinstance(elapsed, (int, float)) - else f"Time: {elapsed}" - ) - lines.append("") - - if stdout: - lines.append(stdout.rstrip()) - if stderr: - lines.append("\n--- stderr ---") - lines.append(stderr.rstrip()) - - # Handle error results - elif "error" in self.data: - lines.append(f"Error: {self.data['error']}") - if "details" in self.data: - lines.append(f"Details: {self.data['details']}") - - # Generic dict - format as key-value pairs - else: - for key, value in self.data.items(): - if isinstance(value, (dict, list)): - lines.append(f"{key}:") - lines.append(json.dumps(value, indent=2, default=str)) - else: - lines.append(f"{key}: {value}") - - elif isinstance(self.data, list): - # List data - format as numbered items - for i, item in enumerate(self.data[:50], 1): - if isinstance(item, dict): - lines.append(f"{i}. {json.dumps(item, default=str)}") - else: - lines.append(f"{i}. {item}") - if len(self.data) > 50: - lines.append(f"\n... {len(self.data) - 50} more items") - - else: - # Scalar or other - just convert to string - lines.append(str(self.data)) - - # Add metadata footer if present - if self.metadata: - lines.append("") - lines.append("---") - for key, value in self.metadata.items(): - lines.append(f"{key}: {value}") - - return "\n".join(lines) diff --git a/pkg/hanzo-tools-core/hanzo_tools/core/unified.py b/pkg/hanzo-tools-core/hanzo_tools/core/unified.py deleted file mode 100644 index 7918932bf..000000000 --- a/pkg/hanzo-tools-core/hanzo_tools/core/unified.py +++ /dev/null @@ -1,449 +0,0 @@ -"""Base tool class for HIP-0300 architecture. - -Provides the foundation for orthogonal, composable tools following Unix philosophy: -- Permissive input, strict output -- Action routing with alias resolution -- Unified response envelope with ok/data/error/meta -- Built-in help and schema actions -- Typed error codes with Unix exit code semantics -- Paging/cursor support for large results -- Composable via stable identifiers (uri, hash, range, ref) - -Design principles: -- Input parsing = permissive (accept many spellings/forms) -- Output + semantics = strict (one canonical shape) -- Aliases never appear in output; only canonical action names - -Reference: HIP-0300 Unified MCP Tools Architecture -""" - -import json -import hashlib -import inspect -from abc import abstractmethod -from typing import Any, Literal, TypeVar, Callable, ClassVar, get_type_hints -from dataclasses import field, dataclass -from collections.abc import Awaitable - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from .base import BaseTool as _BaseToolABC - -# Error codes for structured error handling -ErrorCode = Literal[ - "UNKNOWN_ACTION", # Action not found - "INVALID_PARAMS", # Invalid parameters - "NOT_FOUND", # Resource not found - "CONFLICT", # Precondition failed (e.g., base_hash mismatch) - "PERMISSION_DENIED", # Access denied - "TIMEOUT", # Operation timed out - "INTERNAL_ERROR", # Unexpected error -] - - -@dataclass -class Paging: - """Pagination info for large results.""" - - cursor: str | None = None - more: bool = False - total: int | None = None - - -@dataclass -class ToolError(Exception): - """Structured error for tool operations.""" - - code: ErrorCode - message: str - details: dict[str, Any] = field(default_factory=dict) - - -class ConflictError(ToolError): - """Precondition failed (e.g., base_hash mismatch).""" - - def __init__( - self, message: str, expected: str | None = None, actual: str | None = None - ): - details = {} - if expected: - details["expected"] = expected - if actual: - details["actual"] = actual - super().__init__(code="CONFLICT", message=message, details=details) - - -class NotFoundError(ToolError): - """Resource not found.""" - - def __init__(self, message: str, uri: str | None = None): - details = {"uri": uri} if uri else {} - super().__init__(code="NOT_FOUND", message=message, details=details) - - -class InvalidParamsError(ToolError): - """Invalid parameters.""" - - def __init__( - self, message: str, param: str | None = None, expected: str | None = None - ): - details = {} - if param: - details["param"] = param - if expected: - details["expected"] = expected - super().__init__(code="INVALID_PARAMS", message=message, details=details) - - -@dataclass -class ActionHandler: - """Metadata for a registered action handler.""" - - name: str - handler: Callable[..., Awaitable[Any]] - description: str - schema: dict[str, Any] | None = None - examples: list[str] = field(default_factory=list) - - -class BaseTool(_BaseToolABC): - """Base class for HIP-0300 unified tools with action routing. - - Design principles: - - Permissive input, strict output - - Action routing with alias resolution - - Unified response envelope: {ok, data, error, meta} - - Aliases never appear in output; only canonical action names - - Usage: - class FsTool(BaseTool): - name = "fs" - description = "Filesystem operations" - - def __init__(self): - super().__init__() - self._register_actions() - - def _register_actions(self): - @self.action("read", "Read file contents") - async def read(ctx, uri: str, range: dict | None = None) -> dict: - content = await read_file(uri) - return {"text": content, "hash": hash_content(content)} - - @self.action("apply_patch", "Edit file with precondition") - async def apply_patch(ctx, uri: str, patch: str, base_hash: str) -> dict: - if get_hash(uri) != base_hash: - raise ConflictError("base_hash mismatch", expected=base_hash) - # Apply patch... - return {"uri": uri, "hash": new_hash} - """ - - # Subclasses must define these - name: ClassVar[str] - - # Version for meta envelope - VERSION: ClassVar[str] = "0.12.0" - - def __init__(self): - self._handlers: dict[str, ActionHandler] = {} - self._register_builtin_actions() - - def _register_builtin_actions(self): - """Register built-in help and schema actions.""" - - @self.action("help", "List available actions with descriptions") - async def help_action(ctx: MCPContext) -> dict: - actions = {} - for name, handler in self._handlers.items(): - if name not in ("help", "schema", "status"): - actions[name] = { - "description": handler.description, - "examples": handler.examples, - } - return {"actions": actions, "tool": self.name} - - @self.action("schema", "Get JSON Schema for action parameters") - async def schema_action( - ctx: MCPContext, action_name: str | None = None - ) -> dict: - if action_name: - handler = self._handlers.get(action_name) - if not handler: - raise ToolError( - code="UNKNOWN_ACTION", - message=f"Unknown action: {action_name}", - details={"available": list(self._handlers.keys())}, - ) - return {"action": action_name, "schema": handler.schema or {}} - - schemas = {} - for name, handler in self._handlers.items(): - if name not in ("help", "schema", "status"): - schemas[name] = handler.schema or {} - return {"schemas": schemas} - - @self.action("status", "Get tool status and version") - async def status_action(ctx: MCPContext) -> dict: - return { - "tool": self.name, - "version": self.VERSION, - "enabled": True, - "actions": list(self._handlers.keys()), - } - - def action( - self, - name: str, - description: str = "", - schema: dict[str, Any] | None = None, - examples: list[str] | None = None, - ) -> Callable: - """Decorator to register an action handler. - - Args: - name: Action name (e.g., "read", "apply_patch") - description: Human-readable description - schema: Optional JSON Schema for parameters - examples: Optional list of example usages - - Returns: - Decorator function - """ - - def decorator( - fn: Callable[..., Awaitable[Any]], - ) -> Callable[..., Awaitable[Any]]: - # Auto-generate schema from type hints if not provided - auto_schema = schema - if auto_schema is None: - auto_schema = self._generate_schema(fn) - - self._handlers[name] = ActionHandler( - name=name, - handler=fn, - description=description or fn.__doc__ or "", - schema=auto_schema, - examples=examples or [], - ) - return fn - - return decorator - - def _generate_schema(self, fn: Callable) -> dict[str, Any]: - """Generate JSON Schema from function signature and type hints.""" - try: - hints = get_type_hints(fn) - except Exception: - hints = {} - - sig = inspect.signature(fn) - properties = {} - required = [] - - for param_name, param in sig.parameters.items(): - if param_name in ("self", "ctx"): - continue - - param_schema: dict[str, Any] = {} - hint = hints.get(param_name) - - if hint is str: - param_schema["type"] = "string" - elif hint is int: - param_schema["type"] = "integer" - elif hint is float: - param_schema["type"] = "number" - elif hint is bool: - param_schema["type"] = "boolean" - elif hint is dict or ( - hasattr(hint, "__origin__") and hint.__origin__ is dict - ): - param_schema["type"] = "object" - elif hint is list or ( - hasattr(hint, "__origin__") and hint.__origin__ is list - ): - param_schema["type"] = "array" - else: - param_schema["type"] = "string" # Default to string - - properties[param_name] = param_schema - - if param.default is inspect.Parameter.empty: - required.append(param_name) - - return { - "type": "object", - "properties": properties, - "required": required, - } - - def _envelope( - self, - data: Any, - action: str | None = None, - paging: Paging | None = None, - backend: str | None = None, - trace_id: str | None = None, - ) -> dict[str, Any]: - """Wrap result in unified response envelope.""" - meta: dict[str, Any] = { - "tool": self.name, - "version": self.VERSION, - } - if action: - meta["action"] = action - if backend: - meta["backend"] = backend - if trace_id: - meta["trace_id"] = trace_id - - if paging: - meta["paging"] = { - "cursor": paging.cursor, - "more": paging.more, - } - if paging.total is not None: - meta["paging"]["total"] = paging.total - else: - meta["paging"] = {"cursor": None, "more": False} - - return { - "ok": True, - "data": data, - "error": None, - "meta": meta, - } - - def _error( - self, - code: ErrorCode, - message: str, - **details: Any, - ) -> dict[str, Any]: - """Create error response envelope.""" - error_dict: dict[str, Any] = { - "code": code, - "message": message, - } - if details: - error_dict.update(details) - - return { - "ok": False, - "data": None, - "error": error_dict, - "meta": {"tool": self.name, "version": self.VERSION}, - } - - @property - @abstractmethod - def description(self) -> str: - """Tool description for MCP registration.""" - pass - - async def call( - self, ctx: MCPContext, action: str = "help", **kwargs: Any - ) -> dict[str, Any]: - """Execute tool with action routing. - - Args: - ctx: MCP context - action: Action to execute (default: "help") - **kwargs: Action parameters - - Returns: - Unified response envelope - """ - if action not in self._handlers: - return self._error( - "UNKNOWN_ACTION", - f"Unknown action: {action}", - available=list(self._handlers.keys()), - ) - - handler = self._handlers[action] - - try: - result = await handler.handler(ctx, **kwargs) - return self._envelope(result, action=action) - except ToolError as e: - return self._error(e.code, e.message, **e.details) - except Exception as e: - return self._error("INTERNAL_ERROR", str(e)) - - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server. - - Creates a single MCP tool with action parameter for routing. - """ - tool_name = self.name - tool_description = ( - f"{self.description}\n\nActions: {', '.join(self._handlers.keys())}" - ) - - @mcp_server.tool(name=tool_name, description=tool_description) - async def handler( - ctx: MCPContext, - action: str = "help", - **kwargs: Any, - ) -> str: - result = await self.call(ctx, action=action, **kwargs) - return json.dumps(result, indent=2, default=str) - - -# Utility functions for composability - - -def content_hash(content: str | bytes, algorithm: str = "sha256") -> str: - """Generate content hash for file identity.""" - if isinstance(content, str): - content = content.encode("utf-8") - h = hashlib.new(algorithm) - h.update(content) - return f"{algorithm}:{h.hexdigest()}" - - -def file_uri(path: str) -> str: - """Convert path to file:// URI.""" - from pathlib import Path - - abs_path = Path(path).resolve() - return f"file://{abs_path}" - - -@dataclass -class Range: - """Text range for composability. - - 0-based line and column numbers. - """ - - start_line: int - start_col: int = 0 - end_line: int | None = None - end_col: int | None = None - - def to_dict(self) -> dict[str, Any]: - """Convert to dict format for serialization.""" - result = { - "start": {"line": self.start_line, "col": self.start_col}, - } - if self.end_line is not None: - result["end"] = { - "line": self.end_line, - "col": self.end_col or 0, - } - return result - - @classmethod - def from_dict(cls, d: dict[str, Any]) -> "Range": - """Create Range from dict.""" - start = d.get("start", {}) - end = d.get("end") - return cls( - start_line=start.get("line", 0), - start_col=start.get("col", 0), - end_line=end.get("line") if end else None, - end_col=end.get("col") if end else None, - ) diff --git a/pkg/hanzo-tools-core/hanzo_tools/core/validation.py b/pkg/hanzo-tools-core/hanzo_tools/core/validation.py deleted file mode 100644 index d848f32a6..000000000 --- a/pkg/hanzo-tools-core/hanzo_tools/core/validation.py +++ /dev/null @@ -1,115 +0,0 @@ -"""Validation utilities for tool parameters.""" - -from typing import Optional -from pathlib import Path -from dataclasses import dataclass - - -@dataclass -class ValidationResult: - """Result of a validation check.""" - - is_valid: bool - error_message: Optional[str] = None - - def __bool__(self) -> bool: - return self.is_valid - - -def validate_path_parameter( - path: str, - param_name: str = "path", - must_exist: bool = False, - must_be_file: bool = False, - must_be_dir: bool = False, -) -> ValidationResult: - """Validate a path parameter. - - Args: - path: Path string to validate - param_name: Name of the parameter for error messages - must_exist: If True, path must exist - must_be_file: If True, path must be a file - must_be_dir: If True, path must be a directory - - Returns: - ValidationResult with status and error if any - """ - if not path: - return ValidationResult( - is_valid=False, - error_message=f"{param_name} is required", - ) - - if not path.strip(): - return ValidationResult( - is_valid=False, - error_message=f"{param_name} cannot be empty", - ) - - try: - p = Path(path) - - if must_exist and not p.exists(): - return ValidationResult( - is_valid=False, - error_message=f"{param_name} does not exist: {path}", - ) - - if must_be_file and p.exists() and not p.is_file(): - return ValidationResult( - is_valid=False, - error_message=f"{param_name} is not a file: {path}", - ) - - if must_be_dir and p.exists() and not p.is_dir(): - return ValidationResult( - is_valid=False, - error_message=f"{param_name} is not a directory: {path}", - ) - - return ValidationResult(is_valid=True) - - except Exception as e: - return ValidationResult( - is_valid=False, - error_message=f"Invalid {param_name}: {e}", - ) - - -def validate_string_parameter( - value: str, - param_name: str, - min_length: int = 0, - max_length: Optional[int] = None, - pattern: Optional[str] = None, -) -> ValidationResult: - """Validate a string parameter.""" - if not value: - return ValidationResult( - is_valid=False, - error_message=f"{param_name} is required", - ) - - if len(value) < min_length: - return ValidationResult( - is_valid=False, - error_message=f"{param_name} must be at least {min_length} characters", - ) - - if max_length and len(value) > max_length: - return ValidationResult( - is_valid=False, - error_message=f"{param_name} must be at most {max_length} characters", - ) - - if pattern: - import re - - if not re.match(pattern, value): - return ValidationResult( - is_valid=False, - error_message=f"{param_name} does not match required pattern", - ) - - return ValidationResult(is_valid=True) diff --git a/pkg/hanzo-tools-core/pyproject.toml b/pkg/hanzo-tools-core/pyproject.toml deleted file mode 100644 index 927c78a94..000000000 --- a/pkg/hanzo-tools-core/pyproject.toml +++ /dev/null @@ -1,30 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-core" -version = "0.3.0" -description = "DEPRECATED: Use hanzo-tools instead. This package re-exports from hanzo-tools for backwards compatibility." -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -keywords = ["hanzo", "tools", "mcp", "ai"] -# Just depends on hanzo-tools - re-exports everything -dependencies = [ - "hanzo-tools>=0.3.0", -] - -[project.urls] -"Homepage" = "https://github.com/hanzoai/python-sdk" -"Bug Tracker" = "https://github.com/hanzoai/python-sdk/issues" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] diff --git a/pkg/hanzo-tools-core/tests/__init__.py b/pkg/hanzo-tools-core/tests/__init__.py deleted file mode 100644 index 982815091..000000000 --- a/pkg/hanzo-tools-core/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Tests for hanzo-tools-core diff --git a/pkg/hanzo-tools-core/tests/test_all_tools.py b/pkg/hanzo-tools-core/tests/test_all_tools.py deleted file mode 100644 index c04a48607..000000000 --- a/pkg/hanzo-tools-core/tests/test_all_tools.py +++ /dev/null @@ -1,310 +0,0 @@ -"""Test all hanzo-tools-* packages import and register correctly.""" - -import sys -import time -import asyncio - -import pytest - - -def _module_installed(module_name: str) -> bool: - """Check if a module is installed.""" - try: - __import__(module_name) - return True - except ImportError: - return False - - -class TestToolPackages: - """Test that all tool packages import correctly.""" - - def test_core_imports(self): - """Test hanzo-tools-core imports.""" - from hanzo_tools.core import BaseTool, ToolContext - from hanzo_tools.core.decorators import auto_timeout - from hanzo_tools.core.permissions import PermissionManager - - assert BaseTool is not None - assert ToolContext is not None - assert PermissionManager is not None - assert auto_timeout is not None - - def test_fs_tools(self): - """Test hanzo-tools-fs exports unified HIP-0300 tool.""" - from hanzo_tools.fs import TOOLS - - assert len(TOOLS) == 1 - - def test_shell_tools(self): - """Test hanzo-tools-shell exports detected shell + core shell tools.""" - from hanzo_tools.shell import TOOLS - - # First tool is detected shell (environment-dependent), then fixed base tools. - assert len(TOOLS) >= 8 - names = {t.name for t in TOOLS} - required = {"ps", "npx", "uvx", "open", "curl", "jq", "wget"} - assert required.issubset(names) - - def test_memory_tools(self): - """Test hanzo-tools-memory exports unified memory tool.""" - from hanzo_tools.memory import TOOLS - - assert len(TOOLS) == 1 - - def test_todo_tools(self): - """Test hanzo-tools-todo has 1 tool.""" - from hanzo_tools.todo import TOOLS - - assert len(TOOLS) == 1 - assert TOOLS[0].name == "tasks" - - def test_reasoning_tools(self): - """Test hanzo-tools-reasoning has 2 tools.""" - from hanzo_tools.reasoning import TOOLS - - assert len(TOOLS) == 2 - names = {t.name for t in TOOLS} - assert names == {"think", "critic"} - - def test_lsp_tools(self): - """Test hanzo-tools-lsp has 1 tool.""" - from hanzo_tools.lsp import TOOLS - - assert len(TOOLS) == 1 - assert TOOLS[0].name == "lsp" - - def test_refactor_tools(self): - """Test hanzo-tools-refactor has 1 tool.""" - from hanzo_tools.refactor import TOOLS - - assert len(TOOLS) == 1 - assert TOOLS[0].name == "refactor" - - @pytest.mark.skipif( - not _module_installed("hanzo_tools.database"), - reason="hanzo-tools-database not installed", - ) - def test_database_tools(self): - """Test hanzo-tools-database has 9 tools.""" - from hanzo_tools.database import TOOLS - - assert len(TOOLS) == 9 - - def test_agent_tools(self): - """Test hanzo-tools-agent has 3 tools. - - Core tools: AgentTool, ZenTool, ReviewTool - """ - from hanzo_tools.agent import TOOLS - - assert len(TOOLS) == 3, f"Expected 3 agent tools, got {len(TOOLS)}" - names = {t.name for t in TOOLS} - assert names == {"agent", "zen", "review"} - - def test_jupyter_tools(self): - """Test hanzo-tools-jupyter has 1 tool.""" - from hanzo_tools.jupyter import TOOLS - - assert len(TOOLS) == 1 - - @pytest.mark.skipif( - not _module_installed("hanzo_tools.editor"), - reason="hanzo-tools-editor not installed", - ) - def test_editor_tools(self): - """Test hanzo-tools-editor has 3 tools.""" - from hanzo_tools.editor import TOOLS - - assert len(TOOLS) == 3 - - def test_browser_tools(self): - """Test hanzo-tools-browser has 1 tool.""" - from hanzo_tools.browser import TOOLS - - assert len(TOOLS) == 1 - - @pytest.mark.skipif( - not _module_installed("hanzo_tools.config"), - reason="hanzo-tools-config not installed", - ) - def test_config_tools(self): - """Test hanzo-tools-config has at least 1 tool (graceful degradation).""" - from hanzo_tools.config import TOOLS - - assert len(TOOLS) >= 1 - - @pytest.mark.skipif( - not _module_installed("hanzo_tools.mcp_tools"), - reason="hanzo-tools-mcp not installed", - ) - def test_mcp_tools(self): - """Test hanzo-tools-mcp has 4 tools.""" - from hanzo_tools.mcp_tools import TOOLS - - assert len(TOOLS) == 4 - - @pytest.mark.skipif( - not _module_installed("hanzo_tools.llm"), - reason="hanzo-tools-llm not installed", - ) - def test_llm_tools(self): - """Test hanzo-tools-llm imports (tools depend on llm).""" - from hanzo_tools.llm import TOOLS, LLM_AVAILABLE - - # LLM tools are optional, depend on llm - if LLM_AVAILABLE: - assert len(TOOLS) >= 1 - else: - assert len(TOOLS) == 0 - - @pytest.mark.skipif( - not _module_installed("hanzo_tools.vector"), - reason="hanzo-tools-vector not installed", - ) - def test_vector_tools(self): - """Test hanzo-tools-vector imports (tools depend on heavy deps).""" - from hanzo_tools.vector import TOOLS, VECTOR_AVAILABLE - - # Vector tools are optional, depend on faiss/qdrant - if VECTOR_AVAILABLE: - assert len(TOOLS) >= 1 - else: - assert len(TOOLS) == 0 - - -# Required packages for import speed tests -REQUIRED_IMPORT_MODULES = [ - ("hanzo_tools.core", 1.0), - ("hanzo_tools.fs", 1.0), - ("hanzo_tools.shell", 1.0), - ("hanzo_tools.memory", 1.0), - ("hanzo_tools.todo", 1.0), - ("hanzo_tools.reasoning", 1.0), - ("hanzo_tools.lsp", 1.0), - ("hanzo_tools.refactor", 1.0), - ("hanzo_tools.jupyter", 1.0), - ("hanzo_tools.browser", 1.0), - ("hanzo_tools.agent", 2.0), # Agent has llm, allow more time -] - -# Optional packages that may not be installed -OPTIONAL_IMPORT_MODULES = [ - ("hanzo_tools.config", 1.0), - ("hanzo_tools.mcp_tools", 1.0), - ("hanzo_tools.database", 1.0), - ("hanzo_tools.editor", 1.0), - ("hanzo_tools.llm", 2.0), # LLM has llm, allow more time - ("hanzo_tools.vector", 2.0), # Vector has heavy deps -] - - -class TestToolImportSpeed: - """Test that tool imports are fast (no blocking).""" - - @pytest.mark.parametrize("module,max_time", REQUIRED_IMPORT_MODULES) - def test_import_speed_required(self, module, max_time): - """Test that required imports complete quickly.""" - import importlib - - start = time.time() - importlib.import_module(module) - elapsed = time.time() - start - assert elapsed < max_time, f"{module} took {elapsed:.2f}s (max {max_time}s)" - - @pytest.mark.parametrize("module,max_time", OPTIONAL_IMPORT_MODULES) - def test_import_speed_optional(self, module, max_time): - """Test that optional imports complete quickly (if installed).""" - if not _module_installed(module): - pytest.skip(f"{module} not installed") - import importlib - - start = time.time() - importlib.import_module(module) - elapsed = time.time() - start - assert elapsed < max_time, f"{module} took {elapsed:.2f}s (max {max_time}s)" - - -# Packages that must always be testable -REQUIRED_PACKAGES = [ - "hanzo_tools.fs", - "hanzo_tools.shell", - "hanzo_tools.memory", - "hanzo_tools.todo", - "hanzo_tools.reasoning", - "hanzo_tools.lsp", - "hanzo_tools.refactor", - "hanzo_tools.agent", - "hanzo_tools.jupyter", - "hanzo_tools.browser", -] - -# Optional packages -OPTIONAL_PACKAGES = [ - "hanzo_tools.config", - "hanzo_tools.mcp_tools", - "hanzo_tools.database", - "hanzo_tools.editor", - "hanzo_tools.llm", - "hanzo_tools.vector", -] - - -class TestToolAsync: - """Test that all tools have async call methods.""" - - def test_all_tools_async(self): - """Verify all tool .call() methods are async.""" - import importlib - - all_packages = REQUIRED_PACKAGES + [ - p for p in OPTIONAL_PACKAGES if _module_installed(p) - ] - - for pkg_name in all_packages: - pkg = importlib.import_module(pkg_name) - tools = getattr(pkg, "TOOLS", []) - for tool in tools: - if hasattr(tool, "call"): - assert asyncio.iscoroutinefunction( - tool.call - ), f"{pkg_name}.{tool.name}.call() is not async" - - -class TestTotalToolCount: - """Test total tool count across all packages.""" - - def test_required_tool_count(self): - """Verify we have the expected tools in required packages. - - Required packages must have exact counts. - """ - # Required packages with exact counts - required_packages = [ - ("hanzo_tools.fs", 1), - ("hanzo_tools.shell", 9), - ("hanzo_tools.browser", 1), - ("hanzo_tools.memory", 1), # unified memory tool - ("hanzo_tools.todo", 1), - ("hanzo_tools.reasoning", 2), - ("hanzo_tools.lsp", 1), - ("hanzo_tools.refactor", 1), - ("hanzo_tools.jupyter", 1), - ("hanzo_tools.agent", 3), - ] - - total = 0 - import importlib - - # Check required packages (exact counts) - for pkg_name, expected_count in required_packages: - pkg = importlib.import_module(pkg_name) - tools = getattr(pkg, "TOOLS", []) - actual = len(tools) - assert ( - actual == expected_count - ), f"{pkg_name}: expected {expected_count} tools, got {actual}" - total += actual - - # Required tools: 21 (1+9+1+1+1+2+1+1+1+3) - assert total == 21, f"Expected 21 required tools, got {total}" diff --git a/pkg/hanzo-tools-core/uv.lock b/pkg/hanzo-tools-core/uv.lock deleted file mode 100644 index 6c61062a2..000000000 --- a/pkg/hanzo-tools-core/uv.lock +++ /dev/null @@ -1,1500 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" - -[[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.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, -] - -[[package]] -name = "attrs" -version = "25.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, -] - -[[package]] -name = "authlib" -version = "1.6.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/9b/b1661026ff24bc641b76b78c5222d614776b0c085bcfdac9bd15a1cb4b35/authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e", size = 164894, upload-time = "2025-12-12T08:01:41.464Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/51/321e821856452f7386c4e9df866f196720b1ad0c5ea1623ea7399969ae3b/authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd", size = 244005, upload-time = "2025-12-12T08:01:40.209Z" }, -] - -[[package]] -name = "beartype" -version = "0.22.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, -] - -[[package]] -name = "cachetools" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/af/df70e9b65bc77a1cbe0768c0aa4617147f30f8306ded98c1744bcdc0ae1e/cachetools-7.0.0.tar.gz", hash = "sha256:a9abf18ff3b86c7d05b27ead412e235e16ae045925e531fae38d5fada5ed5b08", size = 35796, upload-time = "2026-02-01T18:59:47.411Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/df/2dd32cce20cbcf6f2ec456b58d44368161ad28320729f64e5e1d5d7bd0ae/cachetools-7.0.0-py3-none-any.whl", hash = "sha256:d52fef60e6e964a1969cfb61ccf6242a801b432790fe520d78720d757c81cbd2", size = 13487, upload-time = "2026-02-01T18:59:45.981Z" }, -] - -[[package]] -name = "certifi" -version = "2026.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, - { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, - { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, - { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, - { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, - { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, - { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, - { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, - { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, -] - -[[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 = "cryptography" -version = "46.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/19/f748958276519adf6a0c1e79e7b8860b4830dda55ccdf29f2719b5fc499c/cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59", size = 749301, upload-time = "2026-01-28T00:24:37.379Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/99/157aae7949a5f30d51fcb1a9851e8ebd5c74bf99b5285d8bb4b8b9ee641e/cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485", size = 7173686, upload-time = "2026-01-28T00:23:07.515Z" }, - { url = "https://files.pythonhosted.org/packages/87/91/874b8910903159043b5c6a123b7e79c4559ddd1896e38967567942635778/cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc", size = 4275871, upload-time = "2026-01-28T00:23:09.439Z" }, - { url = "https://files.pythonhosted.org/packages/c0/35/690e809be77896111f5b195ede56e4b4ed0435b428c2f2b6d35046fbb5e8/cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0", size = 4423124, upload-time = "2026-01-28T00:23:11.529Z" }, - { url = "https://files.pythonhosted.org/packages/1a/5b/a26407d4f79d61ca4bebaa9213feafdd8806dc69d3d290ce24996d3cfe43/cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa", size = 4277090, upload-time = "2026-01-28T00:23:13.123Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d8/4bb7aec442a9049827aa34cee1aa83803e528fa55da9a9d45d01d1bb933e/cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81", size = 4947652, upload-time = "2026-01-28T00:23:14.554Z" }, - { url = "https://files.pythonhosted.org/packages/2b/08/f83e2e0814248b844265802d081f2fac2f1cbe6cd258e72ba14ff006823a/cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255", size = 4455157, upload-time = "2026-01-28T00:23:16.443Z" }, - { url = "https://files.pythonhosted.org/packages/0a/05/19d849cf4096448779d2dcc9bb27d097457dac36f7273ffa875a93b5884c/cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e", size = 3981078, upload-time = "2026-01-28T00:23:17.838Z" }, - { url = "https://files.pythonhosted.org/packages/e6/89/f7bac81d66ba7cde867a743ea5b37537b32b5c633c473002b26a226f703f/cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c", size = 4276213, upload-time = "2026-01-28T00:23:19.257Z" }, - { url = "https://files.pythonhosted.org/packages/da/9f/7133e41f24edd827020ad21b068736e792bc68eecf66d93c924ad4719fb3/cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32", size = 4912190, upload-time = "2026-01-28T00:23:21.244Z" }, - { url = "https://files.pythonhosted.org/packages/a6/f7/6d43cbaddf6f65b24816e4af187d211f0bc536a29961f69faedc48501d8e/cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616", size = 4454641, upload-time = "2026-01-28T00:23:22.866Z" }, - { url = "https://files.pythonhosted.org/packages/9e/4f/ebd0473ad656a0ac912a16bd07db0f5d85184924e14fc88feecae2492834/cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0", size = 4405159, upload-time = "2026-01-28T00:23:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d1/f7/7923886f32dc47e27adeff8246e976d77258fd2aa3efdd1754e4e323bf49/cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0", size = 4666059, upload-time = "2026-01-28T00:23:26.766Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a7/0fca0fd3591dffc297278a61813d7f661a14243dd60f499a7a5b48acb52a/cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5", size = 3026378, upload-time = "2026-01-28T00:23:28.317Z" }, - { url = "https://files.pythonhosted.org/packages/2d/12/652c84b6f9873f0909374864a57b003686c642ea48c84d6c7e2c515e6da5/cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b", size = 3478614, upload-time = "2026-01-28T00:23:30.275Z" }, - { url = "https://files.pythonhosted.org/packages/b9/27/542b029f293a5cce59349d799d4d8484b3b1654a7b9a0585c266e974a488/cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908", size = 7116417, upload-time = "2026-01-28T00:23:31.958Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f5/559c25b77f40b6bf828eabaf988efb8b0e17b573545edb503368ca0a2a03/cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da", size = 4264508, upload-time = "2026-01-28T00:23:34.264Z" }, - { url = "https://files.pythonhosted.org/packages/49/a1/551fa162d33074b660dc35c9bc3616fefa21a0e8c1edd27b92559902e408/cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829", size = 4409080, upload-time = "2026-01-28T00:23:35.793Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/4d8d129a755f5d6df1bbee69ea2f35ebfa954fa1847690d1db2e8bca46a5/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2", size = 4270039, upload-time = "2026-01-28T00:23:37.263Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f5/ed3fcddd0a5e39321e595e144615399e47e7c153a1fb8c4862aec3151ff9/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085", size = 4926748, upload-time = "2026-01-28T00:23:38.884Z" }, - { url = "https://files.pythonhosted.org/packages/43/ae/9f03d5f0c0c00e85ecb34f06d3b79599f20630e4db91b8a6e56e8f83d410/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b", size = 4442307, upload-time = "2026-01-28T00:23:40.56Z" }, - { url = "https://files.pythonhosted.org/packages/8b/22/e0f9f2dae8040695103369cf2283ef9ac8abe4d51f68710bec2afd232609/cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd", size = 3959253, upload-time = "2026-01-28T00:23:42.827Z" }, - { url = "https://files.pythonhosted.org/packages/01/5b/6a43fcccc51dae4d101ac7d378a8724d1ba3de628a24e11bf2f4f43cba4d/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2", size = 4269372, upload-time = "2026-01-28T00:23:44.655Z" }, - { url = "https://files.pythonhosted.org/packages/17/b7/0f6b8c1dd0779df2b526e78978ff00462355e31c0a6f6cff8a3e99889c90/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e", size = 4891908, upload-time = "2026-01-28T00:23:46.48Z" }, - { url = "https://files.pythonhosted.org/packages/83/17/259409b8349aa10535358807a472c6a695cf84f106022268d31cea2b6c97/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f", size = 4441254, upload-time = "2026-01-28T00:23:48.403Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fe/e4a1b0c989b00cee5ffa0764401767e2d1cf59f45530963b894129fd5dce/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82", size = 4396520, upload-time = "2026-01-28T00:23:50.26Z" }, - { url = "https://files.pythonhosted.org/packages/b3/81/ba8fd9657d27076eb40d6a2f941b23429a3c3d2f56f5a921d6b936a27bc9/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c", size = 4651479, upload-time = "2026-01-28T00:23:51.674Z" }, - { url = "https://files.pythonhosted.org/packages/00/03/0de4ed43c71c31e4fe954edd50b9d28d658fef56555eba7641696370a8e2/cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061", size = 3001986, upload-time = "2026-01-28T00:23:53.485Z" }, - { url = "https://files.pythonhosted.org/packages/5c/70/81830b59df7682917d7a10f833c4dab2a5574cd664e86d18139f2b421329/cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7", size = 3468288, upload-time = "2026-01-28T00:23:55.09Z" }, - { url = "https://files.pythonhosted.org/packages/56/f7/f648fdbb61d0d45902d3f374217451385edc7e7768d1b03ff1d0e5ffc17b/cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab", size = 7169583, upload-time = "2026-01-28T00:23:56.558Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cc/8f3224cbb2a928de7298d6ed4790f5ebc48114e02bdc9559196bfb12435d/cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef", size = 4275419, upload-time = "2026-01-28T00:23:58.364Z" }, - { url = "https://files.pythonhosted.org/packages/17/43/4a18faa7a872d00e4264855134ba82d23546c850a70ff209e04ee200e76f/cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d", size = 4419058, upload-time = "2026-01-28T00:23:59.867Z" }, - { url = "https://files.pythonhosted.org/packages/ee/64/6651969409821d791ba12346a124f55e1b76f66a819254ae840a965d4b9c/cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973", size = 4278151, upload-time = "2026-01-28T00:24:01.731Z" }, - { url = "https://files.pythonhosted.org/packages/20/0b/a7fce65ee08c3c02f7a8310cc090a732344066b990ac63a9dfd0a655d321/cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4", size = 4939441, upload-time = "2026-01-28T00:24:03.175Z" }, - { url = "https://files.pythonhosted.org/packages/db/a7/20c5701e2cd3e1dfd7a19d2290c522a5f435dd30957d431dcb531d0f1413/cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af", size = 4451617, upload-time = "2026-01-28T00:24:05.403Z" }, - { url = "https://files.pythonhosted.org/packages/00/dc/3e16030ea9aa47b63af6524c354933b4fb0e352257c792c4deeb0edae367/cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263", size = 3977774, upload-time = "2026-01-28T00:24:06.851Z" }, - { url = "https://files.pythonhosted.org/packages/42/c8/ad93f14118252717b465880368721c963975ac4b941b7ef88f3c56bf2897/cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095", size = 4277008, upload-time = "2026-01-28T00:24:08.926Z" }, - { url = "https://files.pythonhosted.org/packages/00/cf/89c99698151c00a4631fbfcfcf459d308213ac29e321b0ff44ceeeac82f1/cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b", size = 4903339, upload-time = "2026-01-28T00:24:12.009Z" }, - { url = "https://files.pythonhosted.org/packages/03/c3/c90a2cb358de4ac9309b26acf49b2a100957e1ff5cc1e98e6c4996576710/cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019", size = 4451216, upload-time = "2026-01-28T00:24:13.975Z" }, - { url = "https://files.pythonhosted.org/packages/96/2c/8d7f4171388a10208671e181ca43cdc0e596d8259ebacbbcfbd16de593da/cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4", size = 4404299, upload-time = "2026-01-28T00:24:16.169Z" }, - { url = "https://files.pythonhosted.org/packages/e9/23/cbb2036e450980f65c6e0a173b73a56ff3bccd8998965dea5cc9ddd424a5/cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b", size = 4664837, upload-time = "2026-01-28T00:24:17.629Z" }, - { url = "https://files.pythonhosted.org/packages/0a/21/f7433d18fe6d5845329cbdc597e30caf983229c7a245bcf54afecc555938/cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc", size = 3009779, upload-time = "2026-01-28T00:24:20.198Z" }, - { url = "https://files.pythonhosted.org/packages/3a/6a/bd2e7caa2facffedf172a45c1a02e551e6d7d4828658c9a245516a598d94/cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976", size = 3466633, upload-time = "2026-01-28T00:24:21.851Z" }, -] - -[[package]] -name = "cyclopts" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "docstring-parser" }, - { name = "rich" }, - { name = "rich-rst" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d4/93/6085aa89c3fff78a5180987354538d72e43b0db27e66a959302d0c07821a/cyclopts-4.5.1.tar.gz", hash = "sha256:fadc45304763fd9f5d6033727f176898d17a1778e194436964661a005078a3dd", size = 162075, upload-time = "2026-01-25T15:23:54.07Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/7c/996760c30f1302704af57c66ff2d723f7d656d0d0b93563b5528a51484bb/cyclopts-4.5.1-py3-none-any.whl", hash = "sha256:0642c93601e554ca6b7b9abd81093847ea4448b2616280f2a0952416574e8c7a", size = 199807, upload-time = "2026-01-25T15:23:55.219Z" }, -] - -[[package]] -name = "diskcache" -version = "5.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, -] - -[[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.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, -] - -[[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.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "fakeredis" -version = "2.33.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "redis" }, - { name = "sortedcontainers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/f9/57464119936414d60697fcbd32f38909bb5688b616ae13de6e98384433e0/fakeredis-2.33.0.tar.gz", hash = "sha256:d7bc9a69d21df108a6451bbffee23b3eba432c21a654afc7ff2d295428ec5770", size = 175187, upload-time = "2025-12-16T19:45:52.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/78/a850fed8aeef96d4a99043c90b818b2ed5419cd5b24a4049fd7cfb9f1471/fakeredis-2.33.0-py3-none-any.whl", hash = "sha256:de535f3f9ccde1c56672ab2fdd6a8efbc4f2619fc2f1acc87b8737177d71c965", size = 119605, upload-time = "2025-12-16T19:45:51.08Z" }, -] - -[package.optional-dependencies] -lua = [ - { name = "lupa" }, -] - -[[package]] -name = "fastmcp" -version = "2.14.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "authlib" }, - { name = "cyclopts" }, - { name = "exceptiongroup" }, - { name = "httpx" }, - { name = "jsonref" }, - { name = "jsonschema-path" }, - { name = "mcp" }, - { name = "openapi-pydantic" }, - { name = "packaging" }, - { name = "platformdirs" }, - { name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] }, - { name = "pydantic", extra = ["email"] }, - { name = "pydocket" }, - { name = "pyperclip" }, - { name = "python-dotenv" }, - { name = "rich" }, - { name = "uvicorn" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/a9/a57d5e5629ebd4ef82b495a7f8e346ce29ef80cc86b15c8c40570701b94d/fastmcp-2.14.4.tar.gz", hash = "sha256:c01f19845c2adda0a70d59525c9193be64a6383014c8d40ce63345ac664053ff", size = 8302239, upload-time = "2026-01-22T17:29:37.024Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/41/c4d407e2218fd60d84acb6cc5131d28ff876afecf325e3fd9d27b8318581/fastmcp-2.14.4-py3-none-any.whl", hash = "sha256:5858cff5e4c8ea8107f9bca2609d71d6256e0fce74495912f6e51625e466c49a", size = 417788, upload-time = "2026-01-22T17:29:35.159Z" }, -] - -[[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 = "hanzo-tools" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/3e/2d94dc54e202bdb11f6e4597dd68eebc554d2b92fffb4f6918cdf3f91fe2/hanzo_tools-0.3.0.tar.gz", hash = "sha256:d00cb3212a707e22f9bb5a21f0f9eb34a74f22ff2b5f24e2f8b6321f9880e2fb", size = 10929, upload-time = "2025-12-27T18:56:38.843Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/07/6ebbcf371aafa5f2d171de2916ef92c73978b927b34a8863af53e1b1a80b/hanzo_tools-0.3.0-py3-none-any.whl", hash = "sha256:c7b0f6f7c3089f06329bc1aaca39fbce4b7108fdbd048e2bbc450a3aff9941f2", size = 11928, upload-time = "2025-12-27T18:56:37.528Z" }, -] - -[[package]] -name = "hanzo-tools-core" -version = "0.3.0" -source = { editable = "." } -dependencies = [ - { name = "hanzo-tools" }, -] - -[package.metadata] -requires-dist = [{ name = "hanzo-tools", specifier = ">=0.3.0" }] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, -] - -[[package]] -name = "jaraco-context" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" }, -] - -[[package]] -name = "jaraco-functools" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, -] - -[[package]] -name = "jsonref" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[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.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "keyring" -version = "25.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, -] - -[[package]] -name = "lupa" -version = "2.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/1c/191c3e6ec6502e3dbe25a53e27f69a5daeac3e56de1f73c0138224171ead/lupa-2.6.tar.gz", hash = "sha256:9a770a6e89576be3447668d7ced312cd6fd41d3c13c2462c9dc2c2ab570e45d9", size = 7240282, upload-time = "2025-10-24T07:20:29.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/86/ce243390535c39d53ea17ccf0240815e6e457e413e40428a658ea4ee4b8d/lupa-2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47ce718817ef1cc0c40d87c3d5ae56a800d61af00fbc0fad1ca9be12df2f3b56", size = 951707, upload-time = "2025-10-24T07:18:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/cedea5e6cbeb54396fdcc55f6b741696f3f036d23cfaf986d50d680446da/lupa-2.6-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:7aba985b15b101495aa4b07112cdc08baa0c545390d560ad5cfde2e9e34f4d58", size = 1916703, upload-time = "2025-10-24T07:18:05.6Z" }, - { url = "https://files.pythonhosted.org/packages/24/be/3d6b5f9a8588c01a4d88129284c726017b2089f3a3fd3ba8bd977292fea0/lupa-2.6-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:b766f62f95b2739f2248977d29b0722e589dcf4f0ccfa827ccbd29f0148bd2e5", size = 985152, upload-time = "2025-10-24T07:18:08.561Z" }, - { url = "https://files.pythonhosted.org/packages/eb/23/9f9a05beee5d5dce9deca4cb07c91c40a90541fc0a8e09db4ee670da550f/lupa-2.6-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:00a934c23331f94cb51760097ebfab14b005d55a6b30a2b480e3c53dd2fa290d", size = 1159599, upload-time = "2025-10-24T07:18:10.346Z" }, - { url = "https://files.pythonhosted.org/packages/40/4e/e7c0583083db9d7f1fd023800a9767d8e4391e8330d56c2373d890ac971b/lupa-2.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21de9f38bd475303e34a042b7081aabdf50bd9bafd36ce4faea2f90fd9f15c31", size = 1038686, upload-time = "2025-10-24T07:18:12.112Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/5a4f7d959d4feba5e203ff0c31889e74d1ca3153122be4a46dca7d92bf7c/lupa-2.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf3bda96d3fc41237e964a69c23647d50d4e28421111360274d4799832c560e9", size = 2071956, upload-time = "2025-10-24T07:18:14.572Z" }, - { url = "https://files.pythonhosted.org/packages/92/34/2f4f13ca65d01169b1720176aedc4af17bc19ee834598c7292db232cb6dc/lupa-2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a76ead245da54801a81053794aa3975f213221f6542d14ec4b859ee2e7e0323", size = 1057199, upload-time = "2025-10-24T07:18:16.379Z" }, - { url = "https://files.pythonhosted.org/packages/35/2a/5f7d2eebec6993b0dcd428e0184ad71afb06a45ba13e717f6501bfed1da3/lupa-2.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8dd0861741caa20886ddbda0a121d8e52fb9b5bb153d82fa9bba796962bf30e8", size = 1173693, upload-time = "2025-10-24T07:18:18.153Z" }, - { url = "https://files.pythonhosted.org/packages/e4/29/089b4d2f8e34417349af3904bb40bec40b65c8731f45e3fd8d497ca573e5/lupa-2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:239e63948b0b23023f81d9a19a395e768ed3da6a299f84e7963b8f813f6e3f9c", size = 2164394, upload-time = "2025-10-24T07:18:20.403Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1b/79c17b23c921f81468a111cad843b076a17ef4b684c4a8dff32a7969c3f0/lupa-2.6-cp312-cp312-win32.whl", hash = "sha256:325894e1099499e7a6f9c351147661a2011887603c71086d36fe0f964d52d1ce", size = 1420647, upload-time = "2025-10-24T07:18:23.368Z" }, - { url = "https://files.pythonhosted.org/packages/b8/15/5121e68aad3584e26e1425a5c9a79cd898f8a152292059e128c206ee817c/lupa-2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c735a1ce8ee60edb0fe71d665f1e6b7c55c6021f1d340eb8c865952c602cd36f", size = 1688529, upload-time = "2025-10-24T07:18:25.523Z" }, - { url = "https://files.pythonhosted.org/packages/28/1d/21176b682ca5469001199d8b95fa1737e29957a3d185186e7a8b55345f2e/lupa-2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:663a6e58a0f60e7d212017d6678639ac8df0119bc13c2145029dcba084391310", size = 947232, upload-time = "2025-10-24T07:18:27.878Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4c/d327befb684660ca13cf79cd1f1d604331808f9f1b6fb6bf57832f8edf80/lupa-2.6-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:d1f5afda5c20b1f3217a80e9bc1b77037f8a6eb11612fd3ada19065303c8f380", size = 1908625, upload-time = "2025-10-24T07:18:29.944Z" }, - { url = "https://files.pythonhosted.org/packages/66/8e/ad22b0a19454dfd08662237a84c792d6d420d36b061f239e084f29d1a4f3/lupa-2.6-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:26f2b3c085fe76e9119e48c1013c1cccdc1f51585d456858290475aa38e7089e", size = 981057, upload-time = "2025-10-24T07:18:31.553Z" }, - { url = "https://files.pythonhosted.org/packages/5c/48/74859073ab276bd0566c719f9ca0108b0cfc1956ca0d68678d117d47d155/lupa-2.6-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:60d2f902c7b96fb8ab98493dcff315e7bb4d0b44dc9dd76eb37de575025d5685", size = 1156227, upload-time = "2025-10-24T07:18:33.981Z" }, - { url = "https://files.pythonhosted.org/packages/09/6c/0e9ded061916877253c2266074060eb71ed99fb21d73c8c114a76725bce2/lupa-2.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a02d25dee3a3250967c36590128d9220ae02f2eda166a24279da0b481519cbff", size = 1035752, upload-time = "2025-10-24T07:18:36.32Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ef/f8c32e454ef9f3fe909f6c7d57a39f950996c37a3deb7b391fec7903dab7/lupa-2.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eae1ee16b886b8914ff292dbefbf2f48abfbdee94b33a88d1d5475e02423203", size = 2069009, upload-time = "2025-10-24T07:18:38.072Z" }, - { url = "https://files.pythonhosted.org/packages/53/dc/15b80c226a5225815a890ee1c11f07968e0aba7a852df41e8ae6fe285063/lupa-2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0edd5073a4ee74ab36f74fe61450148e6044f3952b8d21248581f3c5d1a58be", size = 1056301, upload-time = "2025-10-24T07:18:40.165Z" }, - { url = "https://files.pythonhosted.org/packages/31/14/2086c1425c985acfb30997a67e90c39457122df41324d3c179d6ee2292c6/lupa-2.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c53ee9f22a8a17e7d4266ad48e86f43771951797042dd51d1494aaa4f5f3f0a", size = 1170673, upload-time = "2025-10-24T07:18:42.426Z" }, - { url = "https://files.pythonhosted.org/packages/10/e5/b216c054cf86576c0191bf9a9f05de6f7e8e07164897d95eea0078dca9b2/lupa-2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de7c0f157a9064a400d828789191a96da7f4ce889969a588b87ec80de9b14772", size = 2162227, upload-time = "2025-10-24T07:18:46.112Z" }, - { url = "https://files.pythonhosted.org/packages/59/2f/33ecb5bedf4f3bc297ceacb7f016ff951331d352f58e7e791589609ea306/lupa-2.6-cp313-cp313-win32.whl", hash = "sha256:ee9523941ae0a87b5b703417720c5d78f72d2f5bc23883a2ea80a949a3ed9e75", size = 1419558, upload-time = "2025-10-24T07:18:48.371Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b4/55e885834c847ea610e111d87b9ed4768f0afdaeebc00cd46810f25029f6/lupa-2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b1335a5835b0a25ebdbc75cf0bda195e54d133e4d994877ef025e218c2e59db9", size = 1683424, upload-time = "2025-10-24T07:18:50.976Z" }, - { url = "https://files.pythonhosted.org/packages/66/9d/d9427394e54d22a35d1139ef12e845fd700d4872a67a34db32516170b746/lupa-2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dcb6d0a3264873e1653bc188499f48c1fb4b41a779e315eba45256cfe7bc33c1", size = 953818, upload-time = "2025-10-24T07:18:53.378Z" }, - { url = "https://files.pythonhosted.org/packages/10/41/27bbe81953fb2f9ecfced5d9c99f85b37964cfaf6aa8453bb11283983721/lupa-2.6-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:a37e01f2128f8c36106726cb9d360bac087d58c54b4522b033cc5691c584db18", size = 1915850, upload-time = "2025-10-24T07:18:55.259Z" }, - { url = "https://files.pythonhosted.org/packages/a3/98/f9ff60db84a75ba8725506bbf448fb085bc77868a021998ed2a66d920568/lupa-2.6-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:458bd7e9ff3c150b245b0fcfbb9bd2593d1152ea7f0a7b91c1d185846da033fe", size = 982344, upload-time = "2025-10-24T07:18:57.05Z" }, - { url = "https://files.pythonhosted.org/packages/41/f7/f39e0f1c055c3b887d86b404aaf0ca197b5edfd235a8b81b45b25bac7fc3/lupa-2.6-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:052ee82cac5206a02df77119c325339acbc09f5ce66967f66a2e12a0f3211cad", size = 1156543, upload-time = "2025-10-24T07:18:59.251Z" }, - { url = "https://files.pythonhosted.org/packages/9e/9c/59e6cffa0d672d662ae17bd7ac8ecd2c89c9449dee499e3eb13ca9cd10d9/lupa-2.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96594eca3c87dd07938009e95e591e43d554c1dbd0385be03c100367141db5a8", size = 1047974, upload-time = "2025-10-24T07:19:01.449Z" }, - { url = "https://files.pythonhosted.org/packages/23/c6/a04e9cef7c052717fcb28fb63b3824802488f688391895b618e39be0f684/lupa-2.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8faddd9d198688c8884091173a088a8e920ecc96cda2ffed576a23574c4b3f6", size = 2073458, upload-time = "2025-10-24T07:19:03.369Z" }, - { url = "https://files.pythonhosted.org/packages/e6/10/824173d10f38b51fc77785228f01411b6ca28826ce27404c7c912e0e442c/lupa-2.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:daebb3a6b58095c917e76ba727ab37b27477fb926957c825205fbda431552134", size = 1067683, upload-time = "2025-10-24T07:19:06.2Z" }, - { url = "https://files.pythonhosted.org/packages/b6/dc/9692fbcf3c924d9c4ece2d8d2f724451ac2e09af0bd2a782db1cef34e799/lupa-2.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f3154e68972befe0f81564e37d8142b5d5d79931a18309226a04ec92487d4ea3", size = 1171892, upload-time = "2025-10-24T07:19:08.544Z" }, - { url = "https://files.pythonhosted.org/packages/84/ff/e318b628d4643c278c96ab3ddea07fc36b075a57383c837f5b11e537ba9d/lupa-2.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e4dadf77b9fedc0bfa53417cc28dc2278a26d4cbd95c29f8927ad4d8fe0a7ef9", size = 2166641, upload-time = "2025-10-24T07:19:10.485Z" }, - { url = "https://files.pythonhosted.org/packages/12/f7/a6f9ec2806cf2d50826980cdb4b3cffc7691dc6f95e13cc728846d5cb793/lupa-2.6-cp314-cp314-win32.whl", hash = "sha256:cb34169c6fa3bab3e8ac58ca21b8a7102f6a94b6a5d08d3636312f3f02fafd8f", size = 1456857, upload-time = "2025-10-24T07:19:37.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/de/df71896f25bdc18360fdfa3b802cd7d57d7fede41a0e9724a4625b412c85/lupa-2.6-cp314-cp314-win_amd64.whl", hash = "sha256:b74f944fe46c421e25d0f8692aef1e842192f6f7f68034201382ac440ef9ea67", size = 1731191, upload-time = "2025-10-24T07:19:40.281Z" }, - { url = "https://files.pythonhosted.org/packages/47/3c/a1f23b01c54669465f5f4c4083107d496fbe6fb45998771420e9aadcf145/lupa-2.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0e21b716408a21ab65723f8841cf7f2f37a844b7a965eeabb785e27fca4099cf", size = 999343, upload-time = "2025-10-24T07:19:12.519Z" }, - { url = "https://files.pythonhosted.org/packages/c5/6d/501994291cb640bfa2ccf7f554be4e6914afa21c4026bd01bff9ca8aac57/lupa-2.6-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:589db872a141bfff828340079bbdf3e9a31f2689f4ca0d88f97d9e8c2eae6142", size = 2000730, upload-time = "2025-10-24T07:19:14.869Z" }, - { url = "https://files.pythonhosted.org/packages/53/a5/457ffb4f3f20469956c2d4c4842a7675e884efc895b2f23d126d23e126cc/lupa-2.6-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:cd852a91a4a9d4dcbb9a58100f820a75a425703ec3e3f049055f60b8533b7953", size = 1021553, upload-time = "2025-10-24T07:19:17.123Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/36bb5a5d0960f2a5c7c700e0819abb76fd9bf9c1d8a66e5106416d6e9b14/lupa-2.6-cp314-cp314t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:0334753be028358922415ca97a64a3048e4ed155413fc4eaf87dd0a7e2752983", size = 1133275, upload-time = "2025-10-24T07:19:20.51Z" }, - { url = "https://files.pythonhosted.org/packages/19/86/202ff4429f663013f37d2229f6176ca9f83678a50257d70f61a0a97281bf/lupa-2.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:661d895cd38c87658a34780fac54a690ec036ead743e41b74c3fb81a9e65a6aa", size = 1038441, upload-time = "2025-10-24T07:19:22.509Z" }, - { url = "https://files.pythonhosted.org/packages/a7/42/d8125f8e420714e5b52e9c08d88b5329dfb02dcca731b4f21faaee6cc5b5/lupa-2.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aa58454ccc13878cc177c62529a2056be734da16369e451987ff92784994ca7", size = 2058324, upload-time = "2025-10-24T07:19:24.979Z" }, - { url = "https://files.pythonhosted.org/packages/2b/2c/47bf8b84059876e877a339717ddb595a4a7b0e8740bacae78ba527562e1c/lupa-2.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1425017264e470c98022bba8cff5bd46d054a827f5df6b80274f9cc71dafd24f", size = 1060250, upload-time = "2025-10-24T07:19:27.262Z" }, - { url = "https://files.pythonhosted.org/packages/c2/06/d88add2b6406ca1bdec99d11a429222837ca6d03bea42ca75afa169a78cb/lupa-2.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:224af0532d216e3105f0a127410f12320f7c5f1aa0300bdf9646b8d9afb0048c", size = 1151126, upload-time = "2025-10-24T07:19:29.522Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a0/89e6a024c3b4485b89ef86881c9d55e097e7cb0bdb74efb746f2fa6a9a76/lupa-2.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9abb98d5a8fd27c8285302e82199f0e56e463066f88f619d6594a450bf269d80", size = 2153693, upload-time = "2025-10-24T07:19:31.379Z" }, - { url = "https://files.pythonhosted.org/packages/b6/36/a0f007dc58fc1bbf51fb85dcc82fcb1f21b8c4261361de7dab0e3d8521ef/lupa-2.6-cp314-cp314t-win32.whl", hash = "sha256:1849efeba7a8f6fb8aa2c13790bee988fd242ae404bd459509640eeea3d1e291", size = 1590104, upload-time = "2025-10-24T07:19:33.514Z" }, - { url = "https://files.pythonhosted.org/packages/7d/5e/db903ce9cf82c48d6b91bf6d63ae4c8d0d17958939a4e04ba6b9f38b8643/lupa-2.6-cp314-cp314t-win_amd64.whl", hash = "sha256:fc1498d1a4fc028bc521c26d0fad4ca00ed63b952e32fb95949bda76a04bad52", size = 1913818, upload-time = "2025-10-24T07:19:36.039Z" }, -] - -[[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 = "mcp" -version = "1.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyjwt", extra = ["crypto"] }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, -] - -[[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 = "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 = "opentelemetry-api" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, -] - -[[package]] -name = "opentelemetry-exporter-prometheus" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-sdk" }, - { name = "prometheus-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/14/39/7dafa6fff210737267bed35a8855b6ac7399b9e582b8cf1f25f842517012/opentelemetry_exporter_prometheus-0.60b1.tar.gz", hash = "sha256:a4011b46906323f71724649d301b4dc188aaa068852e814f4df38cc76eac616b", size = 14976, upload-time = "2025-12-11T13:32:42.944Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/0d/4be6bf5477a3eb3d917d2f17d3c0b6720cd6cb97898444a61d43cc983f5c/opentelemetry_exporter_prometheus-0.60b1-py3-none-any.whl", hash = "sha256:49f59178de4f4590e3cef0b8b95cf6e071aae70e1f060566df5546fad773b8fd", size = 13019, upload-time = "2025-12-11T13:32:23.974Z" }, -] - -[[package]] -name = "opentelemetry-instrumentation" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "packaging" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/0f/7e6b713ac117c1f5e4e3300748af699b9902a2e5e34c9cf443dde25a01fa/opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a", size = 31706, upload-time = "2025-12-11T13:36:42.515Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096, upload-time = "2025-12-11T13:35:33.067Z" }, -] - -[[package]] -name = "opentelemetry-sdk" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, -] - -[[package]] -name = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[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 = "pathvalidate" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262, upload-time = "2025-06-15T09:07:20.736Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305, upload-time = "2025-06-15T09:07:19.117Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, -] - -[[package]] -name = "prometheus-client" -version = "0.24.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616, upload-time = "2026-01-14T15:26:26.965Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057, upload-time = "2026-01-14T15:26:24.42Z" }, -] - -[[package]] -name = "py-key-value-aio" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "py-key-value-shared" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/ce/3136b771dddf5ac905cc193b461eb67967cf3979688c6696e1f2cdcde7ea/py_key_value_aio-0.3.0.tar.gz", hash = "sha256:858e852fcf6d696d231266da66042d3355a7f9871650415feef9fca7a6cd4155", size = 50801, upload-time = "2025-11-17T16:50:04.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/10/72f6f213b8f0bce36eff21fda0a13271834e9eeff7f9609b01afdc253c79/py_key_value_aio-0.3.0-py3-none-any.whl", hash = "sha256:1c781915766078bfd608daa769fefb97e65d1d73746a3dfb640460e322071b64", size = 96342, upload-time = "2025-11-17T16:50:03.801Z" }, -] - -[package.optional-dependencies] -disk = [ - { name = "diskcache" }, - { name = "pathvalidate" }, -] -keyring = [ - { name = "keyring" }, -] -memory = [ - { name = "cachetools" }, -] -redis = [ - { name = "redis" }, -] - -[[package]] -name = "py-key-value-shared" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/e4/1971dfc4620a3a15b4579fe99e024f5edd6e0967a71154771a059daff4db/py_key_value_shared-0.3.0.tar.gz", hash = "sha256:8fdd786cf96c3e900102945f92aa1473138ebe960ef49da1c833790160c28a4b", size = 11666, upload-time = "2025-11-17T16:50:06.849Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e4/b8b0a03ece72f47dce2307d36e1c34725b7223d209fc679315ffe6a4e2c3/py_key_value_shared-0.3.0-py3-none-any.whl", hash = "sha256:5b0efba7ebca08bb158b1e93afc2f07d30b8f40c2fc12ce24a4c0d84f42f9298", size = 19560, upload-time = "2025-11-17T16:50:05.954Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[package.optional-dependencies] -email = [ - { name = "email-validator" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, -] - -[[package]] -name = "pydantic-settings" -version = "2.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, -] - -[[package]] -name = "pydocket" -version = "0.16.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cloudpickle" }, - { name = "fakeredis", extra = ["lua"] }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-prometheus" }, - { name = "opentelemetry-instrumentation" }, - { name = "prometheus-client" }, - { name = "py-key-value-aio", extra = ["memory", "redis"] }, - { name = "python-json-logger" }, - { name = "redis" }, - { name = "rich" }, - { name = "typer" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/00/26befe5f58df7cd1aeda4a8d10bc7d1908ffd86b80fd995e57a2a7b3f7bd/pydocket-0.16.6.tar.gz", hash = "sha256:b96c96ad7692827214ed4ff25fcf941ec38371314db5dcc1ae792b3e9d3a0294", size = 299054, upload-time = "2026-01-09T22:09:15.405Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/3f/7483e5a6dc6326b6e0c640619b5c5bd1d6e3c20e54d58f5fb86267cef00e/pydocket-0.16.6-py3-none-any.whl", hash = "sha256:683d21e2e846aa5106274e7d59210331b242d7fb0dce5b08d3b82065663ed183", size = 67697, upload-time = "2026-01-09T22:09:13.436Z" }, -] - -[[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 = "pyjwt" -version = "2.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, -] - -[package.optional-dependencies] -crypto = [ - { name = "cryptography" }, -] - -[[package]] -name = "pyperclip" -version = "1.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, -] - -[[package]] -name = "python-json-logger" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, -] - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { 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 = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "redis" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/c8/983d5c6579a411d8a99bc5823cc5712768859b5ce2c8afe1a65b37832c81/redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c", size = 4796669, upload-time = "2025-11-19T15:54:39.961Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/f0/8956f8a86b20d7bb9d6ac0187cf4cd54d8065bc9a1a09eb8011d4d326596/redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b", size = 354159, upload-time = "2025-11-19T15:54:38.064Z" }, -] - -[[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 = "rich" -version = "14.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" }, -] - -[[package]] -name = "rich-rst" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, -] - -[[package]] -name = "secretstorage" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[package]] -name = "sortedcontainers" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, -] - -[[package]] -name = "sse-starlette" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "starlette" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/00d280c03ffd39aaee0e86ec81e2d3b9253036a0f93f51d10503adef0e65/sse_starlette-3.2.0.tar.gz", hash = "sha256:8127594edfb51abe44eac9c49e59b0b01f1039d0c7461c6fd91d4e03b70da422", size = 27253, upload-time = "2026-01-17T13:11:05.62Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/7f/832f015020844a8b8f7a9cbc103dd76ba8e3875004c41e08440ea3a2b41a/sse_starlette-3.2.0-py3-none-any.whl", hash = "sha256:5876954bd51920fc2cd51baee47a080eb88a37b5b784e615abb0b283f801cdbf", size = 12763, upload-time = "2026-01-17T13:11:03.775Z" }, -] - -[[package]] -name = "starlette" -version = "0.52.1" -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/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, -] - -[[package]] -name = "typer" -version = "0.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.40.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, -] - -[[package]] -name = "websockets" -version = "16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, - { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, - { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, - { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, - { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, - { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, - { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, - { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, - { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, - { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, -] - -[[package]] -name = "wrapt" -version = "1.17.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998, upload-time = "2025-08-12T05:51:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020, upload-time = "2025-08-12T05:51:35.906Z" }, - { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098, upload-time = "2025-08-12T05:51:57.474Z" }, - { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036, upload-time = "2025-08-12T05:52:34.784Z" }, - { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156, upload-time = "2025-08-12T05:52:13.599Z" }, - { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102, upload-time = "2025-08-12T05:52:14.56Z" }, - { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732, upload-time = "2025-08-12T05:52:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705, upload-time = "2025-08-12T05:53:07.123Z" }, - { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877, upload-time = "2025-08-12T05:53:05.436Z" }, - { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885, upload-time = "2025-08-12T05:52:54.367Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, - { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, - { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, - { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, - { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, - { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, - { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, - { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, - { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, - { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, - { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, - { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, - { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, - { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, - { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, - { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, - { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, - { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, - { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, -] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] diff --git a/pkg/hanzo-tools-database/README.md b/pkg/hanzo-tools-database/README.md deleted file mode 100644 index 0e02e4581..000000000 --- a/pkg/hanzo-tools-database/README.md +++ /dev/null @@ -1,249 +0,0 @@ -# Hanzo Database Tools - -Hybrid memory management system combining plaintext markdown files, SQLite full-text search, and optional vector similarity search. - -## Features - -๐Ÿ—‚๏ธ **Hybrid Storage** -- **Markdown files**: Human-readable rule files (git-trackable) -- **SQLite FTS5**: Fast full-text search with ranking and snippets -- **Vector search**: Semantic similarity via sqlite-vec extension - -๐Ÿ” **Unified Search** -- Search across markdown files and structured memories -- Full-text search with relevance ranking -- Optional vector similarity search for semantic queries - -๐Ÿ“Š **Project & Global Scope** -- Global memories: `~/.hanzo/memory/` (system rules, preferences) -- Project memories: `project/.hanzo/memory/` (architecture, patterns) -- Session memories: Daily logs and insights - -## Quick Start - -### Installation - -```bash -# Basic installation -pip install hanzo-tools-database - -# With vector search (optional) -pip install hanzo-tools-database[vector] -python setup_sqlite_vec.py -``` - -### Usage - -```python -# Read global rules -memory(action="read", file_path="rules.md", scope="global") - -# Write project architecture -memory(action="write", file_path="architecture.md", content="# Architecture...", scope="project") - -# Append to session log -memory(action="append", file_path="sessions/today.md", content="Important insight") - -# Search all memories -memory(action="search", content="database design", scope="both") - -# Create structured memory -memory(action="create", content="Key decision", category="architecture", importance=8) - -# List and stats -memory(action="list", scope="project") -memory(action="stats") -``` - -## Architecture - -### Storage Structure -``` -~/.hanzo/ -โ”œโ”€โ”€ memory/ # Global memories -โ”‚ โ”œโ”€โ”€ rules.md # System rules -โ”‚ โ”œโ”€โ”€ user_preferences.md # User preferences -โ”‚ โ””โ”€โ”€ coding_standards.md # Coding standards -โ””โ”€โ”€ db/ - โ””โ”€โ”€ global_memory.db # Global search index - -/project/ -โ”œโ”€โ”€ .hanzo/ -โ”‚ โ”œโ”€โ”€ memory/ # Project memories -โ”‚ โ”‚ โ”œโ”€โ”€ architecture.md # Decisions -โ”‚ โ”‚ โ”œโ”€โ”€ patterns.md # Code patterns -โ”‚ โ”‚ โ””โ”€โ”€ sessions/ # Daily logs -โ”‚ โ””โ”€โ”€ db/ -โ”‚ โ”œโ”€โ”€ project.db # Project data -โ”‚ โ”œโ”€โ”€ graph.db # Code graph -โ”‚ โ””โ”€โ”€ memory.db # Memory index -``` - -### Database Schema -```sql --- Markdown files index -CREATE TABLE markdown_files ( - id INTEGER PRIMARY KEY, - path TEXT NOT NULL UNIQUE, - content TEXT NOT NULL, - category TEXT, - scope TEXT CHECK(scope IN ('global', 'project')), - modified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- Structured memories -CREATE TABLE memories ( - id INTEGER PRIMARY KEY, - content TEXT NOT NULL, - category TEXT, - importance INTEGER DEFAULT 5, - metadata TEXT, -- JSON - scope TEXT CHECK(scope IN ('global', 'project', 'session')), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- FTS5 indexes -CREATE VIRTUAL TABLE markdown_fts USING fts5(...); -CREATE VIRTUAL TABLE memories_fts USING fts5(...); - --- Vector embeddings (optional) -CREATE TABLE embeddings ( - id INTEGER PRIMARY KEY, - source_table TEXT NOT NULL, - source_id INTEGER NOT NULL, - embedding BLOB, - model TEXT DEFAULT 'bge-small-en-v1.5' -); -``` - -## Memory Tool Actions - -| Action | Description | Parameters | -|--------|-------------|------------| -| `read` | Read markdown file | `file_path`, `scope` | -| `write` | Write markdown file | `file_path`, `content`, `scope`, `category` | -| `append` | Append with timestamp | `file_path`, `content`, `scope` | -| `search` | Search all memories | `content` (query), `scope`, `search_type`, `limit` | -| `create` | Create structured memory | `content`, `category`, `importance`, `scope` | -| `list` | List memory files | `scope` | -| `stats` | Get system statistics | `scope` | - -## sqlite-vec Vector Search - -### Setup -```bash -# Install sqlite-vec extension -python setup_sqlite_vec.py - -# Verify installation -python -c " -import sqlite3 -conn = sqlite3.connect(':memory:') -conn.enable_load_extension(True) -conn.load_extension('vec0') -print('โœ“ sqlite-vec available') -" -``` - -### Features -- **Semantic search**: Find conceptually similar content -- **Embedding models**: BGE, sentence-transformers, custom models -- **Efficient storage**: Binary vectors in SQLite -- **Fast queries**: Optimized similarity search - -### Usage -```python -# Enable vector search -memory(action="search", content="api design", search_type="vector", scope="both") - -# Hybrid search (text + vectors) -memory(action="search", content="database patterns", search_type="hybrid", scope="project") -``` - -## SQL & Graph Tools - -### SQL Operations -```python -# Execute queries -sql_query(query="SELECT * FROM files WHERE type='python'") - -# Search with FTS -sql_search(pattern="TODO", table="files") - -# Get statistics -sql_stats() -``` - -### Graph Operations -```python -# Add relationships -graph_add(source="main.py", target="utils.py", relationship="imports") - -# Query graph -graph_query(query="neighbors", node_id="main.py") - -# Search nodes -graph_search(pattern="Service", node_type="class") - -# Get stats -graph_stats() -``` - -## Best Practices - -### 1. Memory Organization -- **Global scope**: System rules, user preferences, coding standards -- **Project scope**: Architecture decisions, patterns, specific context -- **Session scope**: Daily logs, temporary insights, work progress - -### 2. File Structure -- Use descriptive file names: `architecture.md`, `api_design.md` -- Organize sessions by date: `sessions/2025-01-12.md` -- Use categories for structured memories: `architecture`, `patterns`, `decisions` - -### 3. Search Strategy -- **Text search**: Use `fulltext` for exact term matching -- **Semantic search**: Use `vector` for conceptual similarity (requires sqlite-vec) -- **Hybrid search**: Combine text and vectors for best results - -### 4. Performance -- Index regularly with FTS5 for fast search -- Use appropriate scopes to limit search space -- Set reasonable limits for large result sets - -## Development - -### Testing -```bash -# Run test suite -python test_memory_system.py - -# Test specific features -python -c "from hanzo_tools.database import MemoryManager; print('โœ“ Import successful')" -``` - -### Contributing -1. Follow existing patterns in `memory_manager.py` and `memory_tool.py` -2. Add tests for new features -3. Update documentation for API changes -4. Ensure backward compatibility - -## Migration - -### From hanzo-memory -The new system can coexist with the existing `hanzo-memory` package: -- Phase 1: Use hybrid system for new memories -- Phase 2: Gradually migrate important memories to markdown -- Phase 3: Deprecate complex vector database system -- Phase 4: Keep SQLite for structured data, markdown for context - -### Benefits vs. Current System -- **Simpler**: No complex LiteLLM/InfinityDB dependencies -- **Transparent**: Human-readable markdown files -- **Portable**: Single-file SQLite databases per project -- **Git-friendly**: Memory changes are version controlled -- **Standard**: Follows LLM.md pattern for AI context - ---- - -**Part of Hanzo AI Python SDK**: https://github.com/hanzoai/python-sdk diff --git a/pkg/hanzo-tools-database/hanzo_tools/__init__.py b/pkg/hanzo-tools-database/hanzo_tools/__init__.py deleted file mode 100644 index e1b06939a..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Hanzo tools namespace package.""" - -import pkgutil - -__path__ = pkgutil.extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-database/hanzo_tools/database/__init__.py b/pkg/hanzo-tools-database/hanzo_tools/database/__init__.py deleted file mode 100644 index 45b159408..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/database/__init__.py +++ /dev/null @@ -1,115 +0,0 @@ -"""Database tools for Hanzo AI. - -This package provides tools for working with embedded SQLite databases -and graph databases in projects. - -Tools: -- sql_query: Execute SQL queries -- sql_search: Search database content -- sql_stats: Get database statistics -- graph_add: Add nodes/edges to graph -- graph_remove: Remove from graph -- graph_query: Query graph database -- graph_search: Search graph -- graph_stats: Graph statistics - -Install: - pip install hanzo-tools-database - -Usage: - from hanzo_tools.database import register_tools, TOOLS - - # Register with MCP server - register_tools(mcp_server, permission_manager) -""" - -from hanzo_tools.core import BaseTool, ToolRegistry, PermissionManager - -from .graph_add import GraphAddTool -from .sql_query import SqlQueryTool -from .sql_stats import SqlStatsTool -from .sql_search import SqlSearchTool -from .graph_query import GraphQueryTool -from .graph_stats import GraphStatsTool -from .memory_tool import MemoryTool -from .graph_remove import GraphRemoveTool -from .graph_search import GraphSearchTool -from .memory_manager import MemoryManager -from .database_manager import DatabaseManager - -# Export list for tool discovery -TOOLS = [ - SqlQueryTool, - SqlSearchTool, - SqlStatsTool, - GraphAddTool, - GraphRemoveTool, - GraphQueryTool, - GraphSearchTool, - GraphStatsTool, - MemoryTool, -] - -__all__ = [ - "register_tools", - "TOOLS", - "DatabaseManager", - "MemoryManager", - "SqlQueryTool", - "SqlSearchTool", - "SqlStatsTool", - "GraphAddTool", - "GraphRemoveTool", - "GraphQueryTool", - "GraphSearchTool", - "GraphStatsTool", - "MemoryTool", -] - - -def register_tools( - mcp_server, - permission_manager: PermissionManager, - db_manager: DatabaseManager | None = None, - enabled_tools: dict[str, bool] | None = None, -) -> list[BaseTool]: - """Register database tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - permission_manager: Permission manager for access control - db_manager: Optional database manager instance - enabled_tools: Dict of tool_name -> enabled state - - Returns: - List of registered tools - """ - # Create database manager if not provided - if db_manager is None: - db_manager = DatabaseManager(permission_manager) - - enabled = enabled_tools or {} - registered = [] - - # Create and register tool instances - tool_instances = [ - SqlQueryTool(permission_manager, db_manager), - SqlSearchTool(permission_manager, db_manager), - SqlStatsTool(permission_manager, db_manager), - GraphAddTool(permission_manager, db_manager), - GraphRemoveTool(permission_manager, db_manager), - GraphQueryTool(permission_manager, db_manager), - GraphSearchTool(permission_manager, db_manager), - GraphStatsTool(permission_manager, db_manager), - MemoryTool(permission_manager), - ] - - for tool in tool_instances: - tool_name = ( - tool.name if hasattr(tool, "name") else tool.__class__.__name__.lower() - ) - if enabled.get(tool_name, True): # Enabled by default - ToolRegistry.register_tool(mcp_server, tool) - registered.append(tool) - - return registered diff --git a/pkg/hanzo-tools-database/hanzo_tools/database/database_manager.py b/pkg/hanzo-tools-database/hanzo_tools/database/database_manager.py deleted file mode 100644 index 26fb23260..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/database/database_manager.py +++ /dev/null @@ -1,238 +0,0 @@ -"""Database manager for project-specific SQLite and graph databases.""" - -import os -import sqlite3 -from typing import Dict, List, Optional -from pathlib import Path - -from hanzo_tools.core import PermissionManager - - -class ProjectDatabase: - """Manages SQLite and graph databases for a project.""" - - def __init__(self, project_path: str): - self.project_path = Path(project_path) - self.db_dir = self.project_path / ".hanzo" / "db" - self.db_dir.mkdir(parents=True, exist_ok=True) - - # SQLite database path - self.sqlite_path = self.db_dir / "project.db" - self.graph_path = self.db_dir / "graph.db" - - # Initialize databases - self._init_sqlite() - self._init_graph() - - # Keep graph in memory for performance - self.graph_conn = sqlite3.connect(":memory:") - self._init_graph_schema(self.graph_conn) - self._load_graph_from_disk() - - def _init_sqlite(self): - """Initialize SQLite database with common tables.""" - conn = sqlite3.connect(self.sqlite_path) - try: - # Create metadata table - conn.execute(""" - CREATE TABLE IF NOT EXISTS metadata ( - key TEXT PRIMARY KEY, - value TEXT, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - """) - - # Create files table - conn.execute(""" - CREATE TABLE IF NOT EXISTS files ( - path TEXT PRIMARY KEY, - content TEXT, - size INTEGER, - modified_at TIMESTAMP, - hash TEXT, - metadata TEXT - ) - """) - - # Create symbols table - conn.execute(""" - CREATE TABLE IF NOT EXISTS symbols ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - file_path TEXT, - name TEXT, - type TEXT, - line_start INTEGER, - line_end INTEGER, - scope TEXT, - signature TEXT, - FOREIGN KEY (file_path) REFERENCES files(path) - ) - """) - - # Create index for fast searches - conn.execute("CREATE INDEX IF NOT EXISTS idx_files_path ON files(path)") - conn.execute("CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name)") - conn.execute("CREATE INDEX IF NOT EXISTS idx_symbols_type ON symbols(type)") - - conn.commit() - finally: - conn.close() - - def _init_graph(self): - """Initialize graph database on disk.""" - conn = sqlite3.connect(self.graph_path) - try: - self._init_graph_schema(conn) - conn.commit() - finally: - conn.close() - - def _init_graph_schema(self, conn: sqlite3.Connection): - """Initialize graph database schema.""" - # Nodes table - conn.execute(""" - CREATE TABLE IF NOT EXISTS nodes ( - id TEXT PRIMARY KEY, - type TEXT NOT NULL, - properties TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - """) - - # Edges table - conn.execute(""" - CREATE TABLE IF NOT EXISTS edges ( - source TEXT NOT NULL, - target TEXT NOT NULL, - relationship TEXT NOT NULL, - weight REAL DEFAULT 1.0, - properties TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (source, target, relationship), - FOREIGN KEY (source) REFERENCES nodes(id), - FOREIGN KEY (target) REFERENCES nodes(id) - ) - """) - - # Indexes for graph traversal - conn.execute("CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source)") - conn.execute("CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target)") - conn.execute( - "CREATE INDEX IF NOT EXISTS idx_edges_relationship ON edges(relationship)" - ) - conn.execute("CREATE INDEX IF NOT EXISTS idx_nodes_type ON nodes(type)") - - def _load_graph_from_disk(self): - """Load graph from disk into memory.""" - disk_conn = sqlite3.connect(self.graph_path) - try: - # Copy nodes - nodes = disk_conn.execute("SELECT * FROM nodes").fetchall() - self.graph_conn.executemany( - "INSERT OR REPLACE INTO nodes VALUES (?, ?, ?, ?)", nodes - ) - - # Copy edges - edges = disk_conn.execute("SELECT * FROM edges").fetchall() - self.graph_conn.executemany( - "INSERT OR REPLACE INTO edges VALUES (?, ?, ?, ?, ?, ?)", edges - ) - - self.graph_conn.commit() - finally: - disk_conn.close() - - def _save_graph_to_disk(self): - """Save in-memory graph to disk.""" - disk_conn = sqlite3.connect(self.graph_path) - try: - # Clear existing data - disk_conn.execute("DELETE FROM edges") - disk_conn.execute("DELETE FROM nodes") - - # Copy nodes - nodes = self.graph_conn.execute("SELECT * FROM nodes").fetchall() - disk_conn.executemany("INSERT INTO nodes VALUES (?, ?, ?, ?)", nodes) - - # Copy edges - edges = self.graph_conn.execute("SELECT * FROM edges").fetchall() - disk_conn.executemany("INSERT INTO edges VALUES (?, ?, ?, ?, ?, ?)", edges) - - disk_conn.commit() - finally: - disk_conn.close() - - def get_sqlite_connection(self) -> sqlite3.Connection: - """Get SQLite connection.""" - conn = sqlite3.connect(self.sqlite_path) - conn.row_factory = sqlite3.Row - return conn - - def get_graph_connection(self) -> sqlite3.Connection: - """Get in-memory graph connection.""" - return self.graph_conn - - def close(self): - """Close connections and save graph to disk.""" - self._save_graph_to_disk() - self.graph_conn.close() - - -class DatabaseManager: - """Manages databases for multiple projects.""" - - def __init__(self, permission_manager: PermissionManager): - self.permission_manager = permission_manager - self.projects: Dict[str, ProjectDatabase] = {} - self.search_paths: List[str] = [] - - def add_search_path(self, path: str): - """Add a path to search for projects.""" - if path not in self.search_paths: - self.search_paths.append(path) - - def get_project_db(self, project_path: str) -> ProjectDatabase: - """Get or create database for a project.""" - project_path = os.path.abspath(project_path) - - # Check permissions - if not self.permission_manager.is_path_allowed(project_path): - raise PermissionError(f"No permission to access: {project_path}") - - # Create database if not exists - if project_path not in self.projects: - self.projects[project_path] = ProjectDatabase(project_path) - - return self.projects[project_path] - - def get_project_for_path(self, file_path: str) -> Optional[ProjectDatabase]: - """Find the project database for a given file path.""" - file_path = os.path.abspath(file_path) - - # Check if file is in a known project - for project_path in self.projects: - if file_path.startswith(project_path): - return self.projects[project_path] - - # Search up the directory tree for a project - current = Path(file_path) - if current.is_file(): - current = current.parent - - while current != current.parent: - # Check for project markers - if (current / ".git").exists() or (current / "LLM.md").exists(): - return self.get_project_db(str(current)) - current = current.parent - - # No project found, use the directory of the file - if Path(file_path).is_file(): - return self.get_project_db(str(Path(file_path).parent)) - else: - return self.get_project_db(file_path) - - def close_all(self): - """Close all project databases.""" - for db in self.projects.values(): - db.close() - self.projects.clear() diff --git a/pkg/hanzo-tools-database/hanzo_tools/database/graph.py b/pkg/hanzo-tools-database/hanzo_tools/database/graph.py deleted file mode 100644 index 4156c7233..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/database/graph.py +++ /dev/null @@ -1,523 +0,0 @@ -"""Unified graph database tool.""" - -import json -from typing import ( - Any, - Dict, - Unpack, - Optional, - Annotated, - TypedDict, - final, - override, -) - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, PermissionManager, auto_timeout - -from .database_manager import DatabaseManager - -# Parameter types -Action = Annotated[ - str, - Field( - description="Action: query (default), add, remove, search, stats", - default="query", - ), -] - -NodeId = Annotated[ - Optional[str], - Field( - description="Node ID", - default=None, - ), -] - -NodeType = Annotated[ - Optional[str], - Field( - description="Node type/label", - default=None, - ), -] - -EdgeType = Annotated[ - Optional[str], - Field( - description="Edge type/relationship", - default=None, - ), -] - -FromNode = Annotated[ - Optional[str], - Field( - description="Source node ID for edges", - default=None, - ), -] - -ToNode = Annotated[ - Optional[str], - Field( - description="Target node ID for edges", - default=None, - ), -] - -Properties = Annotated[ - Optional[Dict[str, Any]], - Field( - description="Node/edge properties as JSON", - default=None, - ), -] - -Pattern = Annotated[ - Optional[str], - Field( - description="Search pattern for properties", - default=None, - ), -] - -Depth = Annotated[ - int, - Field( - description="Max traversal depth for queries", - default=2, - ), -] - -Limit = Annotated[ - int, - Field( - description="Maximum results to return", - default=50, - ), -] - - -class GraphParams(TypedDict, total=False): - """Parameters for graph tool.""" - - action: str - node_id: Optional[str] - node_type: Optional[str] - edge_type: Optional[str] - from_node: Optional[str] - to_node: Optional[str] - properties: Optional[Dict[str, Any]] - pattern: Optional[str] - depth: int - limit: int - - -@final -class GraphTool(BaseTool): - """Unified graph database tool.""" - - def __init__( - self, permission_manager: PermissionManager, db_manager: DatabaseManager - ): - """Initialize the graph tool.""" - super().__init__(permission_manager) - self.db_manager = db_manager - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "graph" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Graph database. Actions: query (default), add, remove, search, stats. - -Usage: -graph --node-id user123 -graph --action add --node-id user123 --node-type User --properties '{"name": "John"}' -graph --action add --from-node user123 --to-node post456 --edge-type CREATED -graph --action query --node-id user123 --depth 3 -graph --action search --pattern "John" --node-type User -""" - - @override - @auto_timeout("graph") - async def call( - self, - ctx: MCPContext, - **params: Unpack[GraphParams], - ) -> str: - """Execute graph operation.""" - tool_ctx = self.create_tool_context(ctx) - - # Get current project database - project_db = self.db_manager.get_current_project_db() - if not project_db: - return "Error: No project database found. Are you in a project directory?" - - # Extract action - action = params.get("action", "query") - - # Route to appropriate handler - if action == "query": - return await self._handle_query(project_db, params, tool_ctx) - elif action == "add": - return await self._handle_add(project_db, params, tool_ctx) - elif action == "remove": - return await self._handle_remove(project_db, params, tool_ctx) - elif action == "search": - return await self._handle_search(project_db, params, tool_ctx) - elif action == "stats": - return await self._handle_stats(project_db, tool_ctx) - else: - return f"Error: Unknown action '{action}'. Valid actions: query, add, remove, search, stats" - - async def _handle_query(self, project_db, params: Dict[str, Any], tool_ctx) -> str: - """Query graph relationships.""" - node_id = params.get("node_id") - node_type = params.get("node_type") - depth = params.get("depth", 2) - limit = params.get("limit", 50) - - if not node_id and not node_type: - return "Error: node_id or node_type required for query" - - try: - with project_db.get_graph_connection() as conn: - results = [] - - if node_id: - # Query specific node and its relationships - cursor = conn.execute( - """ - WITH RECURSIVE - node_tree(id, type, properties, depth, path) AS ( - SELECT id, type, properties, 0, id - FROM nodes - WHERE id = ? - - UNION ALL - - SELECT n.id, n.type, n.properties, nt.depth + 1, - nt.path || ' -> ' || n.id - FROM nodes n - JOIN edges e ON (e.to_node = n.id OR e.from_node = n.id) - JOIN node_tree nt ON ( - (e.from_node = nt.id AND e.to_node = n.id) OR - (e.to_node = nt.id AND e.from_node = n.id) - ) - WHERE nt.depth < ? - ) - SELECT DISTINCT * FROM node_tree - ORDER BY depth, id - LIMIT ? - """, - (node_id, depth, limit), - ) - - nodes = cursor.fetchall() - - # Get edges - cursor = conn.execute(""" - SELECT from_node, to_node, type, properties - FROM edges - WHERE from_node IN (SELECT id FROM node_tree) - OR to_node IN (SELECT id FROM node_tree) - """) - - edges = cursor.fetchall() - - else: - # Query by type - cursor = conn.execute( - """ - SELECT id, type, properties - FROM nodes - WHERE type = ? - LIMIT ? - """, - (node_type, limit), - ) - - nodes = cursor.fetchall() - edges = [] - - # Format results - output = ["=== Graph Query Results ==="] - - if nodes: - output.append(f"\nNodes ({len(nodes)}):") - for node in nodes: - props = json.loads(node[2]) if node[2] else {} - output.append(f" {node[0]} [{node[1]}] {props}") - - if edges: - output.append(f"\nEdges ({len(edges)}):") - for edge in edges: - props = json.loads(edge[3]) if edge[3] else {} - output.append(f" {edge[0]} --[{edge[2]}]--> {edge[1]} {props}") - - if not nodes and not edges: - output.append("No results found") - - return "\n".join(output) - - except Exception as e: - await tool_ctx.error(f"Query failed: {str(e)}") - return f"Error during query: {str(e)}" - - async def _handle_add(self, project_db, params: Dict[str, Any], tool_ctx) -> str: - """Add nodes or edges.""" - node_id = params.get("node_id") - from_node = params.get("from_node") - to_node = params.get("to_node") - - if node_id: - # Add node - node_type = params.get("node_type") - if not node_type: - return "Error: node_type required when adding node" - - properties = params.get("properties", {}) - - try: - with project_db.get_graph_connection() as conn: - conn.execute( - """ - INSERT OR REPLACE INTO nodes (id, type, properties) - VALUES (?, ?, ?) - """, - (node_id, node_type, json.dumps(properties)), - ) - conn.commit() - - await tool_ctx.info(f"Added node: {node_id}") - return f"Added node {node_id} [{node_type}]" - - except Exception as e: - await tool_ctx.error(f"Failed to add node: {str(e)}") - return f"Error adding node: {str(e)}" - - elif from_node and to_node: - # Add edge - edge_type = params.get("edge_type", "RELATED") - properties = params.get("properties", {}) - - try: - with project_db.get_graph_connection() as conn: - conn.execute( - """ - INSERT OR REPLACE INTO edges (from_node, to_node, type, properties) - VALUES (?, ?, ?, ?) - """, - (from_node, to_node, edge_type, json.dumps(properties)), - ) - conn.commit() - - await tool_ctx.info(f"Added edge: {from_node} -> {to_node}") - return f"Added edge {from_node} --[{edge_type}]--> {to_node}" - - except Exception as e: - await tool_ctx.error(f"Failed to add edge: {str(e)}") - return f"Error adding edge: {str(e)}" - - else: - return "Error: Either node_id (for node) or from_node + to_node (for edge) required" - - async def _handle_remove(self, project_db, params: Dict[str, Any], tool_ctx) -> str: - """Remove nodes or edges.""" - node_id = params.get("node_id") - from_node = params.get("from_node") - to_node = params.get("to_node") - - if node_id: - # Remove node and its edges - try: - with project_db.get_graph_connection() as conn: - # Delete edges first - cursor = conn.execute( - """ - DELETE FROM edges - WHERE from_node = ? OR to_node = ? - """, - (node_id, node_id), - ) - - edges_deleted = cursor.rowcount - - # Delete node - cursor = conn.execute( - """ - DELETE FROM nodes WHERE id = ? - """, - (node_id,), - ) - - if cursor.rowcount == 0: - return f"Node {node_id} not found" - - conn.commit() - - msg = f"Removed node {node_id}" - if edges_deleted > 0: - msg += f" and {edges_deleted} connected edges" - - await tool_ctx.info(msg) - return msg - - except Exception as e: - await tool_ctx.error(f"Failed to remove node: {str(e)}") - return f"Error removing node: {str(e)}" - - elif from_node and to_node: - # Remove specific edge - edge_type = params.get("edge_type") - - try: - with project_db.get_graph_connection() as conn: - if edge_type: - cursor = conn.execute( - """ - DELETE FROM edges - WHERE from_node = ? AND to_node = ? AND type = ? - """, - (from_node, to_node, edge_type), - ) - else: - cursor = conn.execute( - """ - DELETE FROM edges - WHERE from_node = ? AND to_node = ? - """, - (from_node, to_node), - ) - - if cursor.rowcount == 0: - return f"Edge not found" - - conn.commit() - - return f"Removed edge {from_node} --> {to_node}" - - except Exception as e: - await tool_ctx.error(f"Failed to remove edge: {str(e)}") - return f"Error removing edge: {str(e)}" - - else: - return "Error: Either node_id or from_node + to_node required for remove" - - async def _handle_search(self, project_db, params: Dict[str, Any], tool_ctx) -> str: - """Search graph by pattern.""" - pattern = params.get("pattern") - if not pattern: - return "Error: pattern required for search" - - node_type = params.get("node_type") - limit = params.get("limit", 50) - - try: - with project_db.get_graph_connection() as conn: - # Search in properties - if node_type: - cursor = conn.execute( - """ - SELECT id, type, properties - FROM nodes - WHERE type = ? AND properties LIKE ? - LIMIT ? - """, - (node_type, f"%{pattern}%", limit), - ) - else: - cursor = conn.execute( - """ - SELECT id, type, properties - FROM nodes - WHERE properties LIKE ? - LIMIT ? - """, - (f"%{pattern}%", limit), - ) - - results = cursor.fetchall() - - if not results: - return f"No nodes found matching '{pattern}'" - - # Format results - output = [f"=== Graph Search Results for '{pattern}' ==="] - output.append(f"Found {len(results)} nodes\n") - - for node in results: - props = json.loads(node[2]) if node[2] else {} - output.append(f"{node[0]} [{node[1]}] {props}") - - return "\n".join(output) - - except Exception as e: - await tool_ctx.error(f"Search failed: {str(e)}") - return f"Error during search: {str(e)}" - - async def _handle_stats(self, project_db, tool_ctx) -> str: - """Get graph statistics.""" - try: - with project_db.get_graph_connection() as conn: - # Node stats - cursor = conn.execute(""" - SELECT type, COUNT(*) as count - FROM nodes - GROUP BY type - ORDER BY count DESC - """) - - node_stats = cursor.fetchall() - - # Edge stats - cursor = conn.execute(""" - SELECT type, COUNT(*) as count - FROM edges - GROUP BY type - ORDER BY count DESC - """) - - edge_stats = cursor.fetchall() - - # Total counts - cursor = conn.execute("SELECT COUNT(*) FROM nodes") - total_nodes = cursor.fetchone()[0] - - cursor = conn.execute("SELECT COUNT(*) FROM edges") - total_edges = cursor.fetchone()[0] - - # Format output - output = [f"=== Graph Database Statistics ==="] - output.append(f"Project: {project_db.project_path}") - output.append(f"\nTotal nodes: {total_nodes}") - output.append(f"Total edges: {total_edges}") - - if node_stats: - output.append("\nNodes by type:") - for node_type, count in node_stats: - output.append(f" {node_type}: {count}") - - if edge_stats: - output.append("\nEdges by type:") - for edge_type, count in edge_stats: - output.append(f" {edge_type}: {count}") - - return "\n".join(output) - - except Exception as e: - await tool_ctx.error(f"Failed to get stats: {str(e)}") - return f"Error getting stats: {str(e)}" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-database/hanzo_tools/database/graph_add.py b/pkg/hanzo-tools-database/hanzo_tools/database/graph_add.py deleted file mode 100644 index 0d4b4ae4f..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/database/graph_add.py +++ /dev/null @@ -1,274 +0,0 @@ -"""Graph add tool for adding nodes and edges to the graph database.""" - -import json -from typing import Unpack, Optional, Annotated, TypedDict, final, override - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - PermissionManager, - auto_timeout, - create_tool_context, -) - -from .database_manager import DatabaseManager - -NodeId = Annotated[ - Optional[str], - Field( - description="Node ID to add (required for nodes)", - default=None, - ), -] - -NodeType = Annotated[ - Optional[str], - Field( - description="Node type (e.g., 'file', 'function', 'class')", - default=None, - ), -] - -Properties = Annotated[ - Optional[dict], - Field( - description="Properties as JSON object", - default=None, - ), -] - -Source = Annotated[ - Optional[str], - Field( - description="Source node ID (required for edges)", - default=None, - ), -] - -Target = Annotated[ - Optional[str], - Field( - description="Target node ID (required for edges)", - default=None, - ), -] - -Relationship = Annotated[ - Optional[str], - Field( - description="Edge relationship type (e.g., 'imports', 'calls', 'inherits')", - default=None, - ), -] - -Weight = Annotated[ - float, - Field( - description="Edge weight (default 1.0)", - default=1.0, - ), -] - -ProjectPath = Annotated[ - Optional[str], - Field( - description="Project path (defaults to current directory)", - default=None, - ), -] - - -class GraphAddParams(TypedDict, total=False): - """Parameters for graph add tool.""" - - node_id: Optional[str] - node_type: Optional[str] - properties: Optional[dict] - source: Optional[str] - target: Optional[str] - relationship: Optional[str] - weight: float - project_path: Optional[str] - - -@final -class GraphAddTool(BaseTool): - """Tool for adding nodes and edges to graph database.""" - - def __init__( - self, permission_manager: PermissionManager, db_manager: DatabaseManager - ): - """Initialize the graph add tool. - - Args: - permission_manager: Permission manager for access control - db_manager: Database manager instance - """ - self.permission_manager = permission_manager - self.db_manager = db_manager - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "graph_add" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Add nodes and edges to the project's graph database. - -To add a node: -- Provide node_id and node_type -- Optionally add properties as JSON - -To add an edge: -- Provide source, target, and relationship -- Optionally add weight and properties - -Common node types: -- file, function, class, module, variable - -Common relationships: -- imports, calls, inherits, contains, references, depends_on - -Examples: -- graph_add --node-id "main.py" --node-type "file" --properties '{"size": 1024}' -- graph_add --node-id "MyClass" --node-type "class" --properties '{"file": "main.py"}' -- graph_add --source "main.py" --target "utils.py" --relationship "imports" -- graph_add --source "func1" --target "func2" --relationship "calls" --weight 5.0 -""" - - @override - @auto_timeout("graph_add") - async def call( - self, - ctx: MCPContext, - **params: Unpack[GraphAddParams], - ) -> str: - """Add nodes or edges to graph. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Result of add operation - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - node_id = params.get("node_id") - node_type = params.get("node_type") - properties = params.get("properties", {}) - source = params.get("source") - target = params.get("target") - relationship = params.get("relationship") - weight = params.get("weight", 1.0) - project_path = params.get("project_path") - - # Determine if adding node or edge - is_node = node_id is not None - is_edge = source is not None and target is not None - - if not is_node and not is_edge: - return "Error: Must provide either (node_id and node_type) for a node, or (source, target, relationship) for an edge" - - if is_node and is_edge: - return "Error: Cannot add both node and edge in one operation" - - # Get project database - try: - if project_path: - project_db = self.db_manager.get_project_db(project_path) - else: - import os - - project_db = self.db_manager.get_project_for_path(os.getcwd()) - - if not project_db: - return "Error: Could not find project database" - - except PermissionError as e: - return str(e) - except Exception as e: - return f"Error accessing project database: {str(e)}" - - # Get graph connection - graph_conn = project_db.get_graph_connection() - - try: - if is_node: - # Add node - if not node_type: - return "Error: node_type is required when adding a node" - - await tool_ctx.info(f"Adding node: {node_id} (type: {node_type})") - - # Serialize properties - properties_json = json.dumps(properties) if properties else None - - # Insert or update node - graph_conn.execute( - """ - INSERT OR REPLACE INTO nodes (id, type, properties) - VALUES (?, ?, ?) - """, - (node_id, node_type, properties_json), - ) - - graph_conn.commit() - - return f"Successfully added node '{node_id}' of type '{node_type}'" - - else: - # Add edge - if not relationship: - return "Error: relationship is required when adding an edge" - - await tool_ctx.info( - f"Adding edge: {source} --[{relationship}]--> {target}" - ) - - # Check if nodes exist - cursor = graph_conn.cursor() - cursor.execute( - "SELECT id FROM nodes WHERE id IN (?, ?)", (source, target) - ) - existing = [row[0] for row in cursor.fetchall()] - - if source not in existing: - return f"Error: Source node '{source}' does not exist" - if target not in existing: - return f"Error: Target node '{target}' does not exist" - - # Serialize properties - properties_json = json.dumps(properties) if properties else None - - # Insert or update edge - graph_conn.execute( - """ - INSERT OR REPLACE INTO edges (source, target, relationship, weight, properties) - VALUES (?, ?, ?, ?, ?) - """, - (source, target, relationship, weight, properties_json), - ) - - graph_conn.commit() - - # Save to disk - project_db._save_graph_to_disk() - - return f"Successfully added edge: {source} --[{relationship}]--> {target} (weight: {weight})" - - except Exception as e: - await tool_ctx.error(f"Failed to add to graph: {str(e)}") - return f"Error adding to graph: {str(e)}" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-database/hanzo_tools/database/graph_query.py b/pkg/hanzo-tools-database/hanzo_tools/database/graph_query.py deleted file mode 100644 index 9127db16e..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/database/graph_query.py +++ /dev/null @@ -1,617 +0,0 @@ -"""Graph query tool for querying the graph database.""" - -import json -import sqlite3 -from typing import ( - Unpack, - Optional, - Annotated, - TypedDict, - final, - override, -) -from collections import deque - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - PermissionManager, - auto_timeout, - create_tool_context, -) - -from .database_manager import DatabaseManager - -Query = Annotated[ - str, - Field( - description="Query type: neighbors, path, subgraph, connected, ancestors, descendants", - min_length=1, - ), -] - -NodeId = Annotated[ - Optional[str], - Field( - description="Starting node ID", - default=None, - ), -] - -TargetId = Annotated[ - Optional[str], - Field( - description="Target node ID (for path queries)", - default=None, - ), -] - -Depth = Annotated[ - int, - Field( - description="Maximum depth for traversal", - default=2, - ), -] - -Relationship = Annotated[ - Optional[str], - Field( - description="Filter by relationship type", - default=None, - ), -] - -NodeType = Annotated[ - Optional[str], - Field( - description="Filter by node type", - default=None, - ), -] - -Direction = Annotated[ - str, - Field( - description="Direction: both, incoming, outgoing", - default="both", - ), -] - -ProjectPath = Annotated[ - Optional[str], - Field( - description="Project path (defaults to current directory)", - default=None, - ), -] - - -class GraphQueryParams(TypedDict, total=False): - """Parameters for graph query tool.""" - - query: str - node_id: Optional[str] - target_id: Optional[str] - depth: int - relationship: Optional[str] - node_type: Optional[str] - direction: str - project_path: Optional[str] - - -@final -class GraphQueryTool(BaseTool): - """Tool for querying the graph database.""" - - def __init__( - self, permission_manager: PermissionManager, db_manager: DatabaseManager - ): - """Initialize the graph query tool. - - Args: - permission_manager: Permission manager for access control - db_manager: Database manager instance - """ - self.permission_manager = permission_manager - self.db_manager = db_manager - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "graph_query" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Query the project's graph database for relationships and patterns. - -Query types: -- neighbors: Find direct neighbors of a node -- path: Find shortest path between two nodes -- subgraph: Get subgraph around a node up to depth -- connected: Find all nodes connected to a node -- ancestors: Find nodes that point TO this node -- descendants: Find nodes that this node points TO - -Options: -- --depth: Max traversal depth (default 2) -- --relationship: Filter by edge type -- --node-type: Filter by node type -- --direction: both, incoming, outgoing - -Examples: -- graph_query --query neighbors --node-id "main.py" -- graph_query --query path --node-id "main.py" --target-id "utils.py" -- graph_query --query subgraph --node-id "MyClass" --depth 3 -- graph_query --query ancestors --node-id "error_handler" --relationship "calls" -- graph_query --query descendants --node-id "BaseClass" --relationship "inherits" -""" - - @override - @auto_timeout("graph_query") - async def call( - self, - ctx: MCPContext, - **params: Unpack[GraphQueryParams], - ) -> str: - """Execute graph query. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Query results - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - query = params.get("query") - if not query: - return "Error: query is required" - - node_id = params.get("node_id") - target_id = params.get("target_id") - depth = params.get("depth", 2) - relationship = params.get("relationship") - node_type = params.get("node_type") - direction = params.get("direction", "both") - project_path = params.get("project_path") - - # Validate query type - valid_queries = [ - "neighbors", - "path", - "subgraph", - "connected", - "ancestors", - "descendants", - ] - if query not in valid_queries: - return f"Error: Invalid query '{query}'. Must be one of: {', '.join(valid_queries)}" - - # Validate required parameters - if ( - query in ["neighbors", "subgraph", "connected", "ancestors", "descendants"] - and not node_id - ): - return f"Error: node_id is required for '{query}' query" - - if query == "path" and (not node_id or not target_id): - return "Error: Both node_id and target_id are required for 'path' query" - - # Get project database - try: - if project_path: - project_db = self.db_manager.get_project_db(project_path) - else: - import os - - project_db = self.db_manager.get_project_for_path(os.getcwd()) - - if not project_db: - return "Error: Could not find project database" - - except PermissionError as e: - return str(e) - except Exception as e: - return f"Error accessing project database: {str(e)}" - - # Get graph connection - graph_conn = project_db.get_graph_connection() - - await tool_ctx.info(f"Executing {query} query") - - try: - if query == "neighbors": - return self._query_neighbors( - graph_conn, node_id, relationship, node_type, direction - ) - elif query == "path": - return self._query_path(graph_conn, node_id, target_id, relationship) - elif query == "subgraph": - return self._query_subgraph( - graph_conn, node_id, depth, relationship, node_type, direction - ) - elif query == "connected": - return self._query_connected( - graph_conn, node_id, relationship, node_type, direction - ) - elif query == "ancestors": - return self._query_ancestors( - graph_conn, node_id, depth, relationship, node_type - ) - elif query == "descendants": - return self._query_descendants( - graph_conn, node_id, depth, relationship, node_type - ) - - except Exception as e: - await tool_ctx.error(f"Failed to execute query: {str(e)}") - return f"Error executing query: {str(e)}" - - def _query_neighbors( - self, - conn: sqlite3.Connection, - node_id: str, - relationship: Optional[str], - node_type: Optional[str], - direction: str, - ) -> str: - """Get direct neighbors of a node.""" - cursor = conn.cursor() - - # Check if node exists - cursor.execute("SELECT type, properties FROM nodes WHERE id = ?", (node_id,)) - node_info = cursor.fetchone() - if not node_info: - return f"Error: Node '{node_id}' not found" - - neighbors = [] - - # Get outgoing edges - if direction in ["both", "outgoing"]: - query = """SELECT e.target, e.relationship, e.weight, n.type, n.properties - FROM edges e JOIN nodes n ON e.target = n.id - WHERE e.source = ?""" - params = [node_id] - - if relationship: - query += " AND e.relationship = ?" - params.append(relationship) - if node_type: - query += " AND n.type = ?" - params.append(node_type) - - cursor.execute(query, params) - for row in cursor.fetchall(): - neighbors.append( - { - "direction": "outgoing", - "node_id": row[0], - "relationship": row[1], - "weight": row[2], - "node_type": row[3], - "properties": json.loads(row[4]) if row[4] else {}, - } - ) - - # Get incoming edges - if direction in ["both", "incoming"]: - query = """SELECT e.source, e.relationship, e.weight, n.type, n.properties - FROM edges e JOIN nodes n ON e.source = n.id - WHERE e.target = ?""" - params = [node_id] - - if relationship: - query += " AND e.relationship = ?" - params.append(relationship) - if node_type: - query += " AND n.type = ?" - params.append(node_type) - - cursor.execute(query, params) - for row in cursor.fetchall(): - neighbors.append( - { - "direction": "incoming", - "node_id": row[0], - "relationship": row[1], - "weight": row[2], - "node_type": row[3], - "properties": json.loads(row[4]) if row[4] else {}, - } - ) - - if not neighbors: - return f"No neighbors found for node '{node_id}'" - - # Format output - output = [f"Neighbors of '{node_id}' ({node_info[0]}):\n"] - for n in neighbors: - arrow = "<--" if n["direction"] == "incoming" else "-->" - output.append( - f" {node_id} {arrow}[{n['relationship']}]--> {n['node_id']} ({n['node_type']})" - ) - if n["properties"]: - output.append( - f" Properties: {json.dumps(n['properties'], indent=6)[:100]}" - ) - - output.append(f"\nTotal neighbors: {len(neighbors)}") - return "\n".join(output) - - def _query_path( - self, - conn: sqlite3.Connection, - start: str, - end: str, - relationship: Optional[str], - ) -> str: - """Find shortest path between two nodes using BFS.""" - cursor = conn.cursor() - - # Check if nodes exist - cursor.execute("SELECT id FROM nodes WHERE id IN (?, ?)", (start, end)) - existing = [row[0] for row in cursor.fetchall()] - if start not in existing: - return f"Error: Start node '{start}' not found" - if end not in existing: - return f"Error: End node '{end}' not found" - - # BFS to find shortest path - queue = deque([(start, [start])]) - visited = {start} - - while queue: - current, path = queue.popleft() - - if current == end: - # Found path, get edge details - output = [f"Shortest path from '{start}' to '{end}':\n"] - - for i in range(len(path) - 1): - src, tgt = path[i], path[i + 1] - - # Get edge details - query = "SELECT relationship, weight FROM edges WHERE source = ? AND target = ?" - cursor.execute(query, (src, tgt)) - edge = cursor.fetchone() - - if edge: - output.append(f" {src} --[{edge[0]}]--> {tgt}") - else: - output.append(f" {src} --> {tgt}") - - output.append(f"\nPath length: {len(path) - 1} edge(s)") - return "\n".join(output) - - # Get neighbors - query = "SELECT target FROM edges WHERE source = ?" - params = [current] - if relationship: - query += " AND relationship = ?" - params.append(relationship) - - cursor.execute(query, params) - - for (neighbor,) in cursor.fetchall(): - if neighbor not in visited: - visited.add(neighbor) - queue.append((neighbor, path + [neighbor])) - - return f"No path found from '{start}' to '{end}'" + ( - f" with relationship '{relationship}'" if relationship else "" - ) - - def _query_subgraph( - self, - conn: sqlite3.Connection, - node_id: str, - depth: int, - relationship: Optional[str], - node_type: Optional[str], - direction: str, - ) -> str: - """Get subgraph around a node up to specified depth.""" - cursor = conn.cursor() - - # Check if node exists - cursor.execute("SELECT type FROM nodes WHERE id = ?", (node_id,)) - if not cursor.fetchone(): - return f"Error: Node '{node_id}' not found" - - # BFS to collect nodes and edges - nodes = {node_id: 0} # node_id -> depth - edges = set() # (source, target, relationship) - queue = deque([(node_id, 0)]) - - while queue: - current, current_depth = queue.popleft() - - if current_depth >= depth: - continue - - # Get edges based on direction - if direction in ["both", "outgoing"]: - query = """SELECT e.target, e.relationship, n.type - FROM edges e JOIN nodes n ON e.target = n.id - WHERE e.source = ?""" - params = [current] - - if relationship: - query += " AND e.relationship = ?" - params.append(relationship) - if node_type: - query += " AND n.type = ?" - params.append(node_type) - - cursor.execute(query, params) - - for target, rel, _ in cursor.fetchall(): - edges.add((current, target, rel)) - if target not in nodes or nodes[target] > current_depth + 1: - nodes[target] = current_depth + 1 - queue.append((target, current_depth + 1)) - - if direction in ["both", "incoming"]: - query = """SELECT e.source, e.relationship, n.type - FROM edges e JOIN nodes n ON e.source = n.id - WHERE e.target = ?""" - params = [current] - - if relationship: - query += " AND e.relationship = ?" - params.append(relationship) - if node_type: - query += " AND n.type = ?" - params.append(node_type) - - cursor.execute(query, params) - - for source, rel, _ in cursor.fetchall(): - edges.add((source, current, rel)) - if source not in nodes or nodes[source] > current_depth + 1: - nodes[source] = current_depth + 1 - queue.append((source, current_depth + 1)) - - # Format output - output = [f"Subgraph around '{node_id}' (depth={depth}):\n"] - output.append(f"Nodes ({len(nodes)}):") - - # Get node details - for node, d in sorted(nodes.items(), key=lambda x: (x[1], x[0])): - cursor.execute("SELECT type FROM nodes WHERE id = ?", (node,)) - node_type = cursor.fetchone()[0] - output.append(f" [{d}] {node} ({node_type})") - - output.append(f"\nEdges ({len(edges)}):") - for src, tgt, rel in sorted(edges): - output.append(f" {src} --[{rel}]--> {tgt}") - - return "\n".join(output) - - def _query_connected( - self, - conn: sqlite3.Connection, - node_id: str, - relationship: Optional[str], - node_type: Optional[str], - direction: str, - ) -> str: - """Find all nodes connected to a node (transitive closure).""" - cursor = conn.cursor() - - # Check if node exists - cursor.execute("SELECT type FROM nodes WHERE id = ?", (node_id,)) - if not cursor.fetchone(): - return f"Error: Node '{node_id}' not found" - - # BFS to find all connected nodes - visited = {node_id} - queue = deque([node_id]) - connections = [] # (node_id, node_type, distance) - distance = {node_id: 0} - - while queue: - current = queue.popleft() - current_dist = distance[current] - - # Get edges based on direction - neighbors = [] - - if direction in ["both", "outgoing"]: - query = """SELECT e.target, n.type FROM edges e - JOIN nodes n ON e.target = n.id - WHERE e.source = ?""" - params = [current] - - if relationship: - query += " AND e.relationship = ?" - params.append(relationship) - if node_type: - query += " AND n.type = ?" - params.append(node_type) - - cursor.execute(query, params) - neighbors.extend(cursor.fetchall()) - - if direction in ["both", "incoming"]: - query = """SELECT e.source, n.type FROM edges e - JOIN nodes n ON e.source = n.id - WHERE e.target = ?""" - params = [current] - - if relationship: - query += " AND e.relationship = ?" - params.append(relationship) - if node_type: - query += " AND n.type = ?" - params.append(node_type) - - cursor.execute(query, params) - neighbors.extend(cursor.fetchall()) - - for neighbor, n_type in neighbors: - if neighbor not in visited: - visited.add(neighbor) - queue.append(neighbor) - distance[neighbor] = current_dist + 1 - connections.append((neighbor, n_type, current_dist + 1)) - - if not connections: - return f"No connected nodes found for '{node_id}'" - - # Format output - output = [f"Nodes connected to '{node_id}' ({direction}):"] - output.append(f"\nTotal connected: {len(connections)}\n") - - # Group by distance - by_distance = {} - for node, n_type, dist in connections: - if dist not in by_distance: - by_distance[dist] = [] - by_distance[dist].append((node, n_type)) - - for dist in sorted(by_distance.keys()): - output.append(f"Distance {dist}:") - for node, n_type in sorted(by_distance[dist]): - output.append(f" {node} ({n_type})") - - return "\n".join(output) - - def _query_ancestors( - self, - conn: sqlite3.Connection, - node_id: str, - depth: int, - relationship: Optional[str], - node_type: Optional[str], - ) -> str: - """Find nodes that point TO this node (incoming edges only).""" - return self._query_subgraph( - conn, node_id, depth, relationship, node_type, "incoming" - ) - - def _query_descendants( - self, - conn: sqlite3.Connection, - node_id: str, - depth: int, - relationship: Optional[str], - node_type: Optional[str], - ) -> str: - """Find nodes that this node points TO (outgoing edges only).""" - return self._query_subgraph( - conn, node_id, depth, relationship, node_type, "outgoing" - ) - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-database/hanzo_tools/database/graph_remove.py b/pkg/hanzo-tools-database/hanzo_tools/database/graph_remove.py deleted file mode 100644 index 91ae0fde6..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/database/graph_remove.py +++ /dev/null @@ -1,277 +0,0 @@ -"""Graph remove tool for removing nodes and edges from the graph database.""" - -from typing import Unpack, Optional, Annotated, TypedDict, final, override - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - PermissionManager, - auto_timeout, - create_tool_context, -) - -from .database_manager import DatabaseManager - -NodeId = Annotated[ - Optional[str], - Field( - description="Node ID to remove", - default=None, - ), -] - -Source = Annotated[ - Optional[str], - Field( - description="Source node ID (for edge removal)", - default=None, - ), -] - -Target = Annotated[ - Optional[str], - Field( - description="Target node ID (for edge removal)", - default=None, - ), -] - -Relationship = Annotated[ - Optional[str], - Field( - description="Edge relationship type (for edge removal)", - default=None, - ), -] - -Cascade = Annotated[ - bool, - Field( - description="Cascade delete - remove all connected edges when removing a node", - default=True, - ), -] - -ProjectPath = Annotated[ - Optional[str], - Field( - description="Project path (defaults to current directory)", - default=None, - ), -] - - -class GraphRemoveParams(TypedDict, total=False): - """Parameters for graph remove tool.""" - - node_id: Optional[str] - source: Optional[str] - target: Optional[str] - relationship: Optional[str] - cascade: bool - project_path: Optional[str] - - -@final -class GraphRemoveTool(BaseTool): - """Tool for removing nodes and edges from graph database.""" - - def __init__( - self, permission_manager: PermissionManager, db_manager: DatabaseManager - ): - """Initialize the graph remove tool. - - Args: - permission_manager: Permission manager for access control - db_manager: Database manager instance - """ - self.permission_manager = permission_manager - self.db_manager = db_manager - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "graph_remove" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Remove nodes and edges from the project's graph database. - -To remove a node: -- Provide node_id -- Use --cascade (default true) to remove connected edges -- Use --no-cascade to keep edges (may leave orphaned edges) - -To remove an edge: -- Provide source, target, and relationship -- Removes only the specific edge - -To remove all edges between two nodes: -- Provide source and target (no relationship) - -Examples: -- graph_remove --node-id "main.py" # Remove node and its edges -- graph_remove --node-id "MyClass" --no-cascade # Remove node only -- graph_remove --source "main.py" --target "utils.py" --relationship "imports" -- graph_remove --source "func1" --target "func2" # Remove all edges -""" - - @override - @auto_timeout("graph_remove") - async def call( - self, - ctx: MCPContext, - **params: Unpack[GraphRemoveParams], - ) -> str: - """Remove nodes or edges from graph. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Result of remove operation - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - node_id = params.get("node_id") - source = params.get("source") - target = params.get("target") - relationship = params.get("relationship") - cascade = params.get("cascade", True) - project_path = params.get("project_path") - - # Determine if removing node or edge - is_node = node_id is not None - is_edge = source is not None and target is not None - - if not is_node and not is_edge: - return "Error: Must provide either node_id for a node, or (source, target) for edges" - - if is_node and is_edge: - return "Error: Cannot remove both node and edge in one operation" - - # Get project database - try: - if project_path: - project_db = self.db_manager.get_project_db(project_path) - else: - import os - - project_db = self.db_manager.get_project_for_path(os.getcwd()) - - if not project_db: - return "Error: Could not find project database" - - except PermissionError as e: - return str(e) - except Exception as e: - return f"Error accessing project database: {str(e)}" - - # Get graph connection - graph_conn = project_db.get_graph_connection() - - try: - if is_node: - # Remove node - await tool_ctx.info(f"Removing node: {node_id}") - - # Check if node exists - cursor = graph_conn.cursor() - cursor.execute("SELECT id FROM nodes WHERE id = ?", (node_id,)) - if not cursor.fetchone(): - return f"Error: Node '{node_id}' does not exist" - - if cascade: - # Count edges that will be removed - cursor.execute( - "SELECT COUNT(*) FROM edges WHERE source = ? OR target = ?", - (node_id, node_id), - ) - edge_count = cursor.fetchone()[0] - - # Remove connected edges - graph_conn.execute( - "DELETE FROM edges WHERE source = ? OR target = ?", - (node_id, node_id), - ) - - # Remove node - graph_conn.execute("DELETE FROM nodes WHERE id = ?", (node_id,)) - graph_conn.commit() - - # Save to disk - project_db._save_graph_to_disk() - - return f"Successfully removed node '{node_id}' and {edge_count} connected edge(s)" - else: - # Remove node only - graph_conn.execute("DELETE FROM nodes WHERE id = ?", (node_id,)) - graph_conn.commit() - - # Save to disk - project_db._save_graph_to_disk() - - return f"Successfully removed node '{node_id}' (edges preserved)" - - else: - # Remove edge(s) - if relationship: - # Remove specific edge - await tool_ctx.info( - f"Removing edge: {source} --[{relationship}]--> {target}" - ) - - cursor = graph_conn.cursor() - cursor.execute( - "DELETE FROM edges WHERE source = ? AND target = ? AND relationship = ?", - (source, target, relationship), - ) - - removed = cursor.rowcount - graph_conn.commit() - - if removed == 0: - return f"No edge found: {source} --[{relationship}]--> {target}" - - # Save to disk - project_db._save_graph_to_disk() - - return f"Successfully removed edge: {source} --[{relationship}]--> {target}" - else: - # Remove all edges between nodes - await tool_ctx.info( - f"Removing all edges between {source} and {target}" - ) - - cursor = graph_conn.cursor() - cursor.execute( - "DELETE FROM edges WHERE source = ? AND target = ?", - (source, target), - ) - - removed = cursor.rowcount - graph_conn.commit() - - if removed == 0: - return f"No edges found between '{source}' and '{target}'" - - # Save to disk - project_db._save_graph_to_disk() - - return f"Successfully removed {removed} edge(s) between '{source}' and '{target}'" - - except Exception as e: - await tool_ctx.error(f"Failed to remove from graph: {str(e)}") - return f"Error removing from graph: {str(e)}" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-database/hanzo_tools/database/graph_search.py b/pkg/hanzo-tools-database/hanzo_tools/database/graph_search.py deleted file mode 100644 index 817190554..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/database/graph_search.py +++ /dev/null @@ -1,375 +0,0 @@ -"""Graph search tool for searching nodes and edges in the graph database.""" - -import json -import sqlite3 -from typing import Unpack, Optional, Annotated, TypedDict, final, override - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - PermissionManager, - auto_timeout, - create_tool_context, -) - -from .database_manager import DatabaseManager - -Pattern = Annotated[ - str, - Field( - description="Search pattern (SQL LIKE syntax, % for wildcard)", - min_length=1, - ), -] - -SearchType = Annotated[ - str, - Field( - description="What to search: nodes, edges, properties, all", - default="all", - ), -] - -NodeType = Annotated[ - Optional[str], - Field( - description="Filter by node type", - default=None, - ), -] - -Relationship = Annotated[ - Optional[str], - Field( - description="Filter by relationship type", - default=None, - ), -] - -ProjectPath = Annotated[ - Optional[str], - Field( - description="Project path (defaults to current directory)", - default=None, - ), -] - -MaxResults = Annotated[ - int, - Field( - description="Maximum number of results", - default=50, - ), -] - - -class GraphSearchParams(TypedDict, total=False): - """Parameters for graph search tool.""" - - pattern: str - search_type: str - node_type: Optional[str] - relationship: Optional[str] - project_path: Optional[str] - max_results: int - - -@final -class GraphSearchTool(BaseTool): - """Tool for searching nodes and edges in graph database.""" - - def __init__( - self, permission_manager: PermissionManager, db_manager: DatabaseManager - ): - """Initialize the graph search tool. - - Args: - permission_manager: Permission manager for access control - db_manager: Database manager instance - """ - self.permission_manager = permission_manager - self.db_manager = db_manager - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "graph_search" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Search for nodes and edges in the project's graph database. - -Search types: -- nodes: Search in node IDs -- edges: Search in edge relationships -- properties: Search in node/edge properties -- all: Search everywhere (default) - -Supports SQL LIKE pattern matching: -- % matches any sequence of characters -- _ matches any single character - -Examples: -- graph_search --pattern "%test%" # Find anything with 'test' -- graph_search --pattern "%.py" --search-type nodes # Find Python files -- graph_search --pattern "%import%" --search-type edges -- graph_search --pattern "%TODO%" --search-type properties -- graph_search --pattern "MyClass%" --node-type "class" -""" - - @override - @auto_timeout("graph_search") - async def call( - self, - ctx: MCPContext, - **params: Unpack[GraphSearchParams], - ) -> str: - """Execute graph search. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Search results - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - pattern = params.get("pattern") - if not pattern: - return "Error: pattern is required" - - search_type = params.get("search_type", "all") - node_type = params.get("node_type") - relationship = params.get("relationship") - project_path = params.get("project_path") - max_results = params.get("max_results", 50) - - # Validate search type - valid_types = ["nodes", "edges", "properties", "all"] - if search_type not in valid_types: - return f"Error: Invalid search_type '{search_type}'. Must be one of: {', '.join(valid_types)}" - - # Get project database - try: - if project_path: - project_db = self.db_manager.get_project_db(project_path) - else: - import os - - project_db = self.db_manager.get_project_for_path(os.getcwd()) - - if not project_db: - return "Error: Could not find project database" - - except PermissionError as e: - return str(e) - except Exception as e: - return f"Error accessing project database: {str(e)}" - - await tool_ctx.info(f"Searching graph for pattern: {pattern}") - - # Get graph connection - graph_conn = project_db.get_graph_connection() - results = [] - - try: - cursor = graph_conn.cursor() - - # Search nodes - if search_type in ["nodes", "all"]: - query = "SELECT id, type, properties FROM nodes WHERE id LIKE ?" - params_list = [pattern] - - if node_type: - query += " AND type = ?" - params_list.append(node_type) - - if search_type == "nodes": - query += f" LIMIT {max_results}" - - cursor.execute(query, params_list) - - for row in cursor.fetchall(): - results.append( - { - "type": "node", - "id": row[0], - "node_type": row[1], - "properties": json.loads(row[2]) if row[2] else {}, - "match_field": "id", - } - ) - - # Search edges - if search_type in ["edges", "all"]: - query = """SELECT source, target, relationship, weight, properties - FROM edges WHERE relationship LIKE ?""" - params_list = [pattern] - - if relationship: - query += " AND relationship = ?" - params_list.append(relationship) - - if search_type == "edges": - query += f" LIMIT {max_results}" - - cursor.execute(query, params_list) - - for row in cursor.fetchall(): - results.append( - { - "type": "edge", - "source": row[0], - "target": row[1], - "relationship": row[2], - "weight": row[3], - "properties": json.loads(row[4]) if row[4] else {}, - "match_field": "relationship", - } - ) - - # Search in properties - if search_type in ["properties", "all"]: - # Search node properties - query = """SELECT id, type, properties FROM nodes - WHERE properties IS NOT NULL AND properties LIKE ?""" - params_list = [f"%{pattern}%"] - - if node_type: - query += " AND type = ?" - params_list.append(node_type) - - cursor.execute(query, params_list) - - for row in cursor.fetchall(): - props = json.loads(row[2]) if row[2] else {} - # Check which property matches - matching_props = {} - for key, value in props.items(): - if pattern.replace("%", "").lower() in str(value).lower(): - matching_props[key] = value - - if matching_props: - results.append( - { - "type": "node", - "id": row[0], - "node_type": row[1], - "properties": props, - "match_field": "properties", - "matching_properties": matching_props, - } - ) - - # Search edge properties - query = """SELECT source, target, relationship, weight, properties - FROM edges WHERE properties IS NOT NULL AND properties LIKE ?""" - params_list = [f"%{pattern}%"] - - if relationship: - query += " AND relationship = ?" - params_list.append(relationship) - - cursor.execute(query, params_list) - - for row in cursor.fetchall(): - props = json.loads(row[4]) if row[4] else {} - # Check which property matches - matching_props = {} - for key, value in props.items(): - if pattern.replace("%", "").lower() in str(value).lower(): - matching_props[key] = value - - if matching_props: - results.append( - { - "type": "edge", - "source": row[0], - "target": row[1], - "relationship": row[2], - "weight": row[3], - "properties": props, - "match_field": "properties", - "matching_properties": matching_props, - } - ) - - # Limit total results if searching all - if search_type == "all" and len(results) > max_results: - results = results[:max_results] - - if not results: - return f"No results found for pattern '{pattern}'" - - # Format results - output = [f"Found {len(results)} result(s) for pattern '{pattern}':\n"] - - # Group by type - nodes = [r for r in results if r["type"] == "node"] - edges = [r for r in results if r["type"] == "edge"] - - if nodes: - output.append(f"Nodes ({len(nodes)}):") - for node in nodes[:20]: # Show first 20 - output.append(f" {node['id']} ({node['node_type']})") - if ( - node["match_field"] == "properties" - and "matching_properties" in node - ): - output.append( - f" Matched in: {list(node['matching_properties'].keys())}" - ) - if node["properties"] and node["match_field"] != "properties": - props_str = json.dumps(node["properties"], indent=6)[:100] - if len(props_str) == 100: - props_str += "..." - output.append(f" Properties: {props_str}") - - if len(nodes) > 20: - output.append(f" ... and {len(nodes) - 20} more nodes") - output.append("") - - if edges: - output.append(f"Edges ({len(edges)}):") - for edge in edges[:20]: # Show first 20 - output.append( - f" {edge['source']} --[{edge['relationship']}]--> {edge['target']}" - ) - if ( - edge["match_field"] == "properties" - and "matching_properties" in edge - ): - output.append( - f" Matched in: {list(edge['matching_properties'].keys())}" - ) - if edge["weight"] != 1.0: - output.append(f" Weight: {edge['weight']}") - if edge["properties"]: - props_str = json.dumps(edge["properties"], indent=6)[:100] - if len(props_str) == 100: - props_str += "..." - output.append(f" Properties: {props_str}") - - if len(edges) > 20: - output.append(f" ... and {len(edges) - 20} more edges") - - return "\n".join(output) - - except sqlite3.Error as e: - await tool_ctx.error(f"SQL error: {str(e)}") - return f"SQL error: {str(e)}" - except Exception as e: - await tool_ctx.error(f"Unexpected error: {str(e)}") - return f"Error executing search: {str(e)}" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-database/hanzo_tools/database/graph_stats.py b/pkg/hanzo-tools-database/hanzo_tools/database/graph_stats.py deleted file mode 100644 index ede126b1d..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/database/graph_stats.py +++ /dev/null @@ -1,376 +0,0 @@ -"""Graph statistics tool for analyzing the graph database.""" - -import sqlite3 -from typing import Unpack, Optional, Annotated, TypedDict, final, override -from collections import defaultdict - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - PermissionManager, - auto_timeout, - create_tool_context, -) - -from .database_manager import DatabaseManager - -ProjectPath = Annotated[ - Optional[str], - Field( - description="Project path (defaults to current directory)", - default=None, - ), -] - -Detailed = Annotated[ - bool, - Field( - description="Show detailed statistics", - default=False, - ), -] - -NodeType = Annotated[ - Optional[str], - Field( - description="Filter stats by node type", - default=None, - ), -] - -Relationship = Annotated[ - Optional[str], - Field( - description="Filter stats by relationship type", - default=None, - ), -] - - -class GraphStatsParams(TypedDict, total=False): - """Parameters for graph stats tool.""" - - project_path: Optional[str] - detailed: bool - node_type: Optional[str] - relationship: Optional[str] - - -@final -class GraphStatsTool(BaseTool): - """Tool for getting graph database statistics.""" - - def __init__( - self, permission_manager: PermissionManager, db_manager: DatabaseManager - ): - """Initialize the graph stats tool. - - Args: - permission_manager: Permission manager for access control - db_manager: Database manager instance - """ - self.permission_manager = permission_manager - self.db_manager = db_manager - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "graph_stats" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Get statistics about the project's graph database. - -Shows: -- Node and edge counts -- Node type distribution -- Relationship type distribution -- Degree statistics (connections per node) -- Connected components -- Most connected nodes (hubs) -- Orphaned nodes - -Examples: -- graph_stats # Basic stats -- graph_stats --detailed # Detailed analysis -- graph_stats --node-type "class" # Stats for specific node type -- graph_stats --relationship "calls" # Stats for specific relationship -""" - - @override - @auto_timeout("graph_stats") - async def call( - self, - ctx: MCPContext, - **params: Unpack[GraphStatsParams], - ) -> str: - """Get graph statistics. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Graph statistics - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - project_path = params.get("project_path") - detailed = params.get("detailed", False) - node_type_filter = params.get("node_type") - relationship_filter = params.get("relationship") - - # Get project database - try: - if project_path: - project_db = self.db_manager.get_project_db(project_path) - else: - import os - - project_db = self.db_manager.get_project_for_path(os.getcwd()) - - if not project_db: - return "Error: Could not find project database" - - except PermissionError as e: - return str(e) - except Exception as e: - return f"Error accessing project database: {str(e)}" - - await tool_ctx.info( - f"Getting graph statistics for project: {project_db.project_path}" - ) - - # Get graph connection - graph_conn = project_db.get_graph_connection() - - try: - cursor = graph_conn.cursor() - output = [] - output.append(f"=== Graph Database Statistics ===") - output.append(f"Project: {project_db.project_path}") - output.append(f"Database: {project_db.graph_path}") - output.append("") - - # Basic counts - if node_type_filter: - cursor.execute( - "SELECT COUNT(*) FROM nodes WHERE type = ?", (node_type_filter,) - ) - node_count = cursor.fetchone()[0] - output.append(f"Nodes (type='{node_type_filter}'): {node_count:,}") - else: - cursor.execute("SELECT COUNT(*) FROM nodes") - node_count = cursor.fetchone()[0] - output.append(f"Total Nodes: {node_count:,}") - - if relationship_filter: - cursor.execute( - "SELECT COUNT(*) FROM edges WHERE relationship = ?", - (relationship_filter,), - ) - edge_count = cursor.fetchone()[0] - output.append( - f"Edges (relationship='{relationship_filter}'): {edge_count:,}" - ) - else: - cursor.execute("SELECT COUNT(*) FROM edges") - edge_count = cursor.fetchone()[0] - output.append(f"Total Edges: {edge_count:,}") - - if node_count == 0: - output.append("\nGraph is empty.") - return "\n".join(output) - - output.append("") - - # Node type distribution - output.append("=== Node Types ===") - cursor.execute( - "SELECT type, COUNT(*) as count FROM nodes GROUP BY type ORDER BY count DESC" - ) - node_types = cursor.fetchall() - - for n_type, count in node_types[:10]: - pct = (count / node_count) * 100 - output.append(f"{n_type}: {count:,} ({pct:.1f}%)") - - if len(node_types) > 10: - output.append(f"... and {len(node_types) - 10} more types") - - output.append("") - - # Relationship distribution - output.append("=== Relationship Types ===") - cursor.execute( - "SELECT relationship, COUNT(*) as count FROM edges GROUP BY relationship ORDER BY count DESC" - ) - rel_types = cursor.fetchall() - - if rel_types: - for rel, count in rel_types[:10]: - pct = (count / edge_count) * 100 if edge_count > 0 else 0 - output.append(f"{rel}: {count:,} ({pct:.1f}%)") - - if len(rel_types) > 10: - output.append(f"... and {len(rel_types) - 10} more types") - else: - output.append("No edges in graph") - - output.append("") - - # Degree statistics - output.append("=== Connectivity ===") - - # Calculate degrees - degrees = defaultdict(int) - - # Out-degree - query = "SELECT source, COUNT(*) FROM edges" - if relationship_filter: - query += " WHERE relationship = ?" - cursor.execute(query + " GROUP BY source", (relationship_filter,)) - else: - cursor.execute(query + " GROUP BY source") - - for node, out_degree in cursor.fetchall(): - degrees[node] += out_degree - - # In-degree - query = "SELECT target, COUNT(*) FROM edges" - if relationship_filter: - query += " WHERE relationship = ?" - cursor.execute(query + " GROUP BY target", (relationship_filter,)) - else: - cursor.execute(query + " GROUP BY target") - - for node, in_degree in cursor.fetchall(): - degrees[node] += in_degree - - if degrees: - degree_values = list(degrees.values()) - avg_degree = sum(degree_values) / len(degree_values) - max_degree = max(degree_values) - min_degree = min(degree_values) - - output.append(f"Average degree: {avg_degree:.2f}") - output.append(f"Max degree: {max_degree}") - output.append(f"Min degree: {min_degree}") - - # Most connected nodes - output.append("\nMost connected nodes:") - sorted_nodes = sorted(degrees.items(), key=lambda x: x[1], reverse=True) - - for node, degree in sorted_nodes[:5]: - cursor.execute("SELECT type FROM nodes WHERE id = ?", (node,)) - node_type = cursor.fetchone() - type_str = f" ({node_type[0]})" if node_type else "" - output.append(f" {node}{type_str}: {degree} connections") - - # Orphaned nodes - cursor.execute(""" - SELECT COUNT(*) FROM nodes n - WHERE NOT EXISTS (SELECT 1 FROM edges WHERE source = n.id OR target = n.id) - """) - orphan_count = cursor.fetchone()[0] - if orphan_count > 0: - orphan_pct = (orphan_count / node_count) * 100 - output.append(f"\nOrphaned nodes: {orphan_count} ({orphan_pct:.1f}%)") - - if detailed: - output.append("\n=== Detailed Analysis ===") - - # Node properties usage - cursor.execute( - "SELECT COUNT(*) FROM nodes WHERE properties IS NOT NULL" - ) - nodes_with_props = cursor.fetchone()[0] - if nodes_with_props > 0: - props_pct = (nodes_with_props / node_count) * 100 - output.append( - f"Nodes with properties: {nodes_with_props} ({props_pct:.1f}%)" - ) - - # Edge properties usage - cursor.execute( - "SELECT COUNT(*) FROM edges WHERE properties IS NOT NULL" - ) - edges_with_props = cursor.fetchone()[0] - if edges_with_props > 0 and edge_count > 0: - props_pct = (edges_with_props / edge_count) * 100 - output.append( - f"Edges with properties: {edges_with_props} ({props_pct:.1f}%)" - ) - - # Weight distribution - cursor.execute( - "SELECT MIN(weight), MAX(weight), AVG(weight) FROM edges" - ) - weight_stats = cursor.fetchone() - if weight_stats[0] is not None: - output.append(f"\nEdge weights:") - output.append(f" Min: {weight_stats[0]}") - output.append(f" Max: {weight_stats[1]}") - output.append(f" Avg: {weight_stats[2]:.2f}") - - # Most common patterns - if not relationship_filter: - output.append("\n=== Common Patterns ===") - - # Most common node type connections - cursor.execute(""" - SELECT n1.type, e.relationship, n2.type, COUNT(*) as count - FROM edges e - JOIN nodes n1 ON e.source = n1.id - JOIN nodes n2 ON e.target = n2.id - GROUP BY n1.type, e.relationship, n2.type - ORDER BY count DESC - LIMIT 10 - """) - - patterns = cursor.fetchall() - if patterns: - output.append("Most common connections:") - for src_type, rel, tgt_type, count in patterns: - output.append( - f" {src_type} --[{rel}]--> {tgt_type}: {count} times" - ) - - # Component analysis (simplified) - output.append("\n=== Graph Structure ===") - - # Check if graph is fully connected (simplified) - cursor.execute(""" - SELECT COUNT(DISTINCT node_id) FROM ( - SELECT source as node_id FROM edges - UNION - SELECT target as node_id FROM edges - ) - """) - connected_nodes = cursor.fetchone()[0] - - if connected_nodes < node_count: - output.append(f"Connected nodes: {connected_nodes} / {node_count}") - output.append("Graph has disconnected components") - else: - output.append("All nodes are connected") - - return "\n".join(output) - - except sqlite3.Error as e: - await tool_ctx.error(f"SQL error: {str(e)}") - return f"SQL error: {str(e)}" - except Exception as e: - await tool_ctx.error(f"Unexpected error: {str(e)}") - return f"Error getting statistics: {str(e)}" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-database/hanzo_tools/database/init_memory.py b/pkg/hanzo-tools-database/hanzo_tools/database/init_memory.py deleted file mode 100644 index ce8e9493c..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/database/init_memory.py +++ /dev/null @@ -1,168 +0,0 @@ -"""Initialize default memory structure for new projects.""" - -import os -from typing import Optional -from pathlib import Path - - -def init_global_memory(): - """Initialize global memory structure in ~/.hanzo/memory/.""" - hanzo_dir = Path.home() / ".hanzo" - memory_dir = hanzo_dir / "memory" - - # Create directories - memory_dir.mkdir(parents=True, exist_ok=True) - - # Template directory - template_dir = Path(__file__).parent.parent.parent / "templates" - - # Copy templates if they don't exist - templates = [ - ("global_rules.md", "rules.md"), - ("user_preferences.md", "user_preferences.md"), - ("coding_standards.md", "coding_standards.md"), - ] - - for template_file, target_file in templates: - target_path = memory_dir / target_file - if not target_path.exists(): - template_path = template_dir / template_file - if template_path.exists(): - target_path.write_text(template_path.read_text()) - print(f"Created global memory file: {target_path}") - - print(f"Global memory initialized at: {memory_dir}") - - -def init_project_memory(project_path: Optional[str] = None): - """Initialize project memory structure in project/.hanzo/memory/.""" - if not project_path: - project_path = os.getcwd() - - project_path = Path(project_path) - memory_dir = project_path / ".hanzo" / "memory" - - # Create directories - memory_dir.mkdir(parents=True, exist_ok=True) - sessions_dir = memory_dir / "sessions" - sessions_dir.mkdir(exist_ok=True) - - # Template directory - template_dir = Path(__file__).parent.parent.parent / "templates" - - # Create architecture.md if it doesn't exist - arch_file = memory_dir / "architecture.md" - if not arch_file.exists(): - template_path = template_dir / "architecture.md" - if template_path.exists(): - content = template_path.read_text() - # Customize for this project - project_name = project_path.name - content = content.replace( - "# Project Architecture Decisions", - f"# {project_name} Architecture Decisions", - ) - arch_file.write_text(content) - print(f"Created project architecture file: {arch_file}") - - # Create patterns.md template - patterns_file = memory_dir / "patterns.md" - if not patterns_file.exists(): - patterns_content = f"""# {project_path.name} Code Patterns - -## Common Patterns - -### Tool Implementation Pattern -```python -@final -class MyTool(BaseTool): - def __init__(self, permission_manager: PermissionManager): - super().__init__(permission_manager) - - @property - @override - def name(self) -> str: - return "my_tool" - - @property - @override - def description(self) -> str: - return "Tool description" - - @override - @auto_timeout("my_tool") - async def call(self, ctx: MCPContext, **params) -> str: - tool_ctx = self.create_tool_context(ctx) - # Implementation - return "Result" -``` - -### Database Connection Pattern -```python -def get_connection(self, db_path: Path) -> sqlite3.Connection: - conn = sqlite3.connect(str(db_path)) - conn.row_factory = sqlite3.Row - return conn -``` - -### Error Handling Pattern -```python -try: - result = operation() -except SpecificError as e: - await tool_ctx.error(f"Operation failed: {{str(e)}}") - return f"Error: {{str(e)}}" -``` - ---- - -*Add project-specific patterns as they emerge* -""" - patterns_file.write_text(patterns_content) - print(f"Created project patterns file: {patterns_file}") - - # Create README for memory system - readme_file = memory_dir / "README.md" - if not readme_file.exists(): - readme_content = f"""# {project_path.name} Memory - -This directory contains project-specific memories and context files. - -## Structure -- `architecture.md` - Architectural decisions and design rationale -- `patterns.md` - Common code patterns used in this project -- `sessions/` - Daily session logs and insights -- Additional `.md` files - Topic-specific memories - -## Usage -```bash -# Read memory -memory --action read --file-path architecture.md - -# Append to session -memory --action append --file-path sessions/today.md --content "New insight" - -# Search memories -memory --action search --content "database" --scope project - -# List all memories -memory --action list --scope project -``` - -## Integration -These files are automatically indexed in `.hanzo/db/memory.db` for fast full-text search. -""" - readme_file.write_text(readme_content) - print(f"Created memory README: {readme_file}") - - print(f"Project memory initialized at: {memory_dir}") - - -if __name__ == "__main__": - import sys - - if len(sys.argv) > 1: - init_project_memory(sys.argv[1]) - else: - init_global_memory() - init_project_memory() diff --git a/pkg/hanzo-tools-database/hanzo_tools/database/memory_manager.py b/pkg/hanzo-tools-database/hanzo_tools/database/memory_manager.py deleted file mode 100644 index cafad75a4..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/database/memory_manager.py +++ /dev/null @@ -1,675 +0,0 @@ -"""Hybrid memory management system. - -Combines: -- Plaintext markdown files for human-readable rules/context -- SQLite with FTS5 for full-text search -- sqlite-vec for vector similarity search -- Layered search across all storage types -""" - -import os -import json -import sqlite3 -from typing import Any, Dict, List, Tuple, Optional -from pathlib import Path -from datetime import datetime - -from hanzo_tools.core import PermissionManager - - -class MemoryManager: - """Manages hybrid memory system: markdown + SQLite + vectors.""" - - def __init__(self, permission_manager: PermissionManager): - self.permission_manager = permission_manager - self.global_memory_dir = Path.home() / ".hanzo" / "memory" - self.global_memory_dir.mkdir(parents=True, exist_ok=True) - - def _get_project_memory_dir(self, project_path: str) -> Path: - """Get project memory directory.""" - project_path = Path(project_path) - memory_dir = project_path / ".hanzo" / "memory" - memory_dir.mkdir(parents=True, exist_ok=True) - return memory_dir - - def _get_project_db_path(self, project_path: str) -> Path: - """Get project database path.""" - project_path = Path(project_path) - db_dir = project_path / ".hanzo" / "db" - db_dir.mkdir(parents=True, exist_ok=True) - return db_dir / "memory.db" - - def _get_global_db_path(self) -> Path: - """Get global database path.""" - db_dir = Path.home() / ".hanzo" / "db" - db_dir.mkdir(parents=True, exist_ok=True) - return db_dir / "global_memory.db" - - def _init_memory_db(self, db_path: Path) -> sqlite3.Connection: - """Initialize memory database with FTS5 and vector support.""" - conn = sqlite3.connect(str(db_path)) - conn.row_factory = sqlite3.Row - - # Enable sqlite-vec extension if available - try: - conn.enable_load_extension(True) - conn.load_extension("vec0") # sqlite-vec extension - has_vector = True - except (sqlite3.Error, AttributeError): - has_vector = False - - # Create markdown files table - conn.execute(""" - CREATE TABLE IF NOT EXISTS markdown_files ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - path TEXT NOT NULL UNIQUE, - content TEXT NOT NULL, - category TEXT, - scope TEXT CHECK(scope IN ('global', 'project')), - modified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - """) - - # Create FTS5 index for full-text search - conn.execute(""" - CREATE VIRTUAL TABLE IF NOT EXISTS markdown_fts USING fts5( - path, content, category, scope, - content='markdown_files', - content_rowid='id' - ) - """) - - # Create memories table for structured data - conn.execute(""" - CREATE TABLE IF NOT EXISTS memories ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - content TEXT NOT NULL, - category TEXT, - importance INTEGER DEFAULT 5, - metadata TEXT, -- JSON - scope TEXT CHECK(scope IN ('global', 'project', 'session')), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - """) - - # Create FTS5 index for memories - conn.execute(""" - CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5( - content, category, metadata, - content='memories', - content_rowid='id' - ) - """) - - if has_vector: - # Create vector embeddings table - conn.execute(""" - CREATE TABLE IF NOT EXISTS embeddings ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - source_table TEXT NOT NULL, -- 'markdown_files' or 'memories' - source_id INTEGER NOT NULL, - embedding BLOB, -- Vector embeddings - model TEXT DEFAULT 'bge-small-en-v1.5', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - """) - - # Create vector index - conn.execute(""" - CREATE VIRTUAL TABLE IF NOT EXISTS vec_index USING vec0( - embedding float[384] -- BGE small model dimensions - ) - """) - - # Create triggers to keep FTS in sync - conn.execute(""" - CREATE TRIGGER IF NOT EXISTS markdown_files_ai - AFTER INSERT ON markdown_files BEGIN - INSERT INTO markdown_fts(rowid, path, content, category, scope) - VALUES (new.id, new.path, new.content, new.category, new.scope); - END - """) - - conn.execute(""" - CREATE TRIGGER IF NOT EXISTS markdown_files_ad - AFTER DELETE ON markdown_files BEGIN - INSERT INTO markdown_fts(markdown_fts, rowid, path, content, category, scope) - VALUES('delete', old.id, old.path, old.content, old.category, old.scope); - END - """) - - conn.execute(""" - CREATE TRIGGER IF NOT EXISTS markdown_files_au - AFTER UPDATE ON markdown_files BEGIN - INSERT INTO markdown_fts(markdown_fts, rowid, path, content, category, scope) - VALUES('delete', old.id, old.path, old.content, old.category, old.scope); - INSERT INTO markdown_fts(rowid, path, content, category, scope) - VALUES (new.id, new.path, new.content, new.category, new.scope); - END - """) - - # Similar triggers for memories - conn.execute(""" - CREATE TRIGGER IF NOT EXISTS memories_ai - AFTER INSERT ON memories BEGIN - INSERT INTO memories_fts(rowid, content, category, metadata) - VALUES (new.id, new.content, new.category, new.metadata); - END - """) - - conn.execute(""" - CREATE TRIGGER IF NOT EXISTS memories_ad - AFTER DELETE ON memories BEGIN - INSERT INTO memories_fts(memories_fts, rowid, content, category, metadata) - VALUES('delete', old.id, old.content, old.category, old.metadata); - END - """) - - conn.execute(""" - CREATE TRIGGER IF NOT EXISTS memories_au - AFTER UPDATE ON memories BEGIN - INSERT INTO memories_fts(memories_fts, rowid, content, category, metadata) - VALUES('delete', old.id, old.content, old.category, old.metadata); - INSERT INTO memories_fts(rowid, content, category, metadata) - VALUES (new.id, new.content, new.category, new.metadata); - END - """) - - conn.commit() - return conn - - # Markdown file operations - def read_markdown_file( - self, file_path: str, scope: str = "project", project_path: str = None - ) -> Optional[str]: - """Read markdown memory file.""" - if scope == "global": - full_path = self.global_memory_dir / file_path - else: - if not project_path: - project_path = os.getcwd() - memory_dir = self._get_project_memory_dir(project_path) - full_path = memory_dir / file_path - - if not full_path.exists(): - return None - - return full_path.read_text() - - def write_markdown_file( - self, - file_path: str, - content: str, - scope: str = "project", - project_path: str = None, - category: str = None, - ) -> bool: - """Write markdown memory file and index in database.""" - if scope == "global": - full_path = self.global_memory_dir / file_path - db_path = self._get_global_db_path() - else: - if not project_path: - project_path = os.getcwd() - memory_dir = self._get_project_memory_dir(project_path) - full_path = memory_dir / file_path - db_path = self._get_project_db_path(project_path) - - # Ensure directory exists - full_path.parent.mkdir(parents=True, exist_ok=True) - - # Write file - full_path.write_text(content) - - # Update database index - try: - conn = self._init_memory_db(db_path) - conn.execute( - """ - INSERT OR REPLACE INTO markdown_files - (path, content, category, scope, modified_at) - VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP) - """, - (str(file_path), content, category, scope), - ) - conn.commit() - conn.close() - except Exception as e: - print(f"Warning: Failed to index markdown file: {e}") - - return True - - def append_markdown_file( - self, - file_path: str, - content: str, - scope: str = "project", - project_path: str = None, - category: str = None, - ) -> bool: - """Append to markdown memory file.""" - existing = self.read_markdown_file(file_path, scope, project_path) or "" - - # Add timestamp and newlines - timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - if existing: - new_content = f"{existing}\n\n## {timestamp}\n\n{content}" - else: - new_content = f"# {file_path}\n\n## {timestamp}\n\n{content}" - - return self.write_markdown_file( - file_path, new_content, scope, project_path, category - ) - - def list_markdown_files( - self, scope: str = "both", project_path: str = None - ) -> List[Dict[str, Any]]: - """List all markdown memory files.""" - files = [] - - if scope in ("global", "both"): - global_files = list(self.global_memory_dir.rglob("*.md")) - files.extend( - [ - { - "path": str(f.relative_to(self.global_memory_dir)), - "full_path": str(f), - "scope": "global", - "size": f.stat().st_size, - "modified": datetime.fromtimestamp( - f.stat().st_mtime - ).isoformat(), - } - for f in global_files - ] - ) - - if scope in ("project", "both"): - if not project_path: - project_path = os.getcwd() - memory_dir = self._get_project_memory_dir(project_path) - project_files = list(memory_dir.rglob("*.md")) - files.extend( - [ - { - "path": str(f.relative_to(memory_dir)), - "full_path": str(f), - "scope": "project", - "size": f.stat().st_size, - "modified": datetime.fromtimestamp( - f.stat().st_mtime - ).isoformat(), - } - for f in project_files - ] - ) - - return sorted(files, key=lambda x: x["modified"], reverse=True) - - # Structured memory operations - def create_memory( - self, - content: str, - category: str = None, - importance: int = 5, - metadata: Dict[str, Any] = None, - scope: str = "project", - project_path: str = None, - ) -> int: - """Create structured memory record.""" - if scope == "global": - db_path = self._get_global_db_path() - else: - if not project_path: - project_path = os.getcwd() - db_path = self._get_project_db_path(project_path) - - conn = self._init_memory_db(db_path) - cursor = conn.execute( - """ - INSERT INTO memories (content, category, importance, metadata, scope) - VALUES (?, ?, ?, ?, ?) - """, - ( - content, - category, - importance, - json.dumps(metadata) if metadata else None, - scope, - ), - ) - - memory_id = cursor.lastrowid - conn.commit() - conn.close() - return memory_id - - # Search operations - def search_memories( - self, - query: str, - scope: str = "both", - project_path: str = None, - search_type: str = "fulltext", - limit: int = 10, - ) -> List[Dict[str, Any]]: - """Search across all memory types.""" - results = [] - - # Search markdown files - markdown_results = self._search_markdown_fulltext( - query, scope, project_path, limit - ) - results.extend([{**r, "source": "markdown"} for r in markdown_results]) - - # Search structured memories - memory_results = self._search_structured_memories( - query, scope, project_path, limit - ) - results.extend([{**r, "source": "memory"} for r in memory_results]) - - # Vector search available when sqlite-vec extension installed - - # Sort by relevance/recency - return sorted(results, key=lambda x: x.get("score", 0), reverse=True)[:limit] - - def _search_markdown_fulltext( - self, query: str, scope: str, project_path: str, limit: int - ) -> List[Dict[str, Any]]: - """Search markdown files using FTS5.""" - results = [] - - dbs = [] - if scope in ("global", "both"): - dbs.append(("global", self._get_global_db_path())) - if scope in ("project", "both"): - if not project_path: - project_path = os.getcwd() - dbs.append(("project", self._get_project_db_path(project_path))) - - for scope_name, db_path in dbs: - if not db_path.exists(): - continue - - try: - conn = self._init_memory_db(db_path) - cursor = conn.execute( - """ - SELECT m.*, f.rank, - snippet(markdown_fts, 1, '', '', '...', 64) as snippet - FROM markdown_fts f - JOIN markdown_files m ON m.id = f.rowid - WHERE markdown_fts MATCH ? - ORDER BY f.rank - LIMIT ? - """, - (query, limit), - ) - - for row in cursor.fetchall(): - results.append( - { - "id": row["id"], - "path": row["path"], - "content": ( - row["content"][:500] + "..." - if len(row["content"]) > 500 - else row["content"] - ), - "snippet": row["snippet"], - "category": row["category"], - "scope": scope_name, - "score": -row["rank"], # FTS5 rank is negative - "created_at": row["created_at"], - "modified_at": row["modified_at"], - } - ) - - conn.close() - except Exception as e: - print(f"Warning: FTS search failed for {scope_name}: {e}") - - return results - - def _search_structured_memories( - self, query: str, scope: str, project_path: str, limit: int - ) -> List[Dict[str, Any]]: - """Search structured memories using FTS5.""" - results = [] - - dbs = [] - if scope in ("global", "both"): - dbs.append(("global", self._get_global_db_path())) - if scope in ("project", "both"): - if not project_path: - project_path = os.getcwd() - dbs.append(("project", self._get_project_db_path(project_path))) - - for scope_name, db_path in dbs: - if not db_path.exists(): - continue - - try: - conn = self._init_memory_db(db_path) - cursor = conn.execute( - """ - SELECT m.*, f.rank, - snippet(memories_fts, 0, '', '', '...', 64) as snippet - FROM memories_fts f - JOIN memories m ON m.id = f.rowid - WHERE memories_fts MATCH ? - ORDER BY f.rank - LIMIT ? - """, - (query, limit), - ) - - for row in cursor.fetchall(): - results.append( - { - "id": row["id"], - "content": row["content"], - "snippet": row["snippet"], - "category": row["category"], - "importance": row["importance"], - "metadata": ( - json.loads(row["metadata"]) if row["metadata"] else {} - ), - "scope": scope_name, - "score": -row["rank"], - "created_at": row["created_at"], - "updated_at": row["updated_at"], - } - ) - - conn.close() - except Exception as e: - print(f"Warning: Memory search failed for {scope_name}: {e}") - - return results - - # Vector operations (requires sqlite-vec extension) - def generate_embedding( - self, text: str, model: str = "bge-small-en-v1.5" - ) -> Optional[List[float]]: - """Generate embedding for text. Returns None if embedding model unavailable.""" - # Requires: pip install fastembed - try: - from fastembed import TextEmbedding - - embedding_model = TextEmbedding(model_name=model) - embeddings = list(embedding_model.embed([text])) - return embeddings[0].tolist() if embeddings else None - except ImportError: - return None # fastembed not installed - - def add_embedding( - self, - source_table: str, - source_id: int, - embedding: List[float], - model: str = "bge-small-en-v1.5", - scope: str = "project", - project_path: str = None, - ) -> bool: - """Add vector embedding to database.""" - if scope == "global": - db_path = self._get_global_db_path() - else: - if not project_path: - project_path = os.getcwd() - db_path = self._get_project_db_path(project_path) - - try: - conn = self._init_memory_db(db_path) - - # Check if sqlite-vec is available - cursor = conn.execute("SELECT name FROM pragma_table_info('vec_index')") - if not cursor.fetchall(): - conn.close() - return False - - # Store embedding - conn.execute( - """ - INSERT OR REPLACE INTO embeddings - (source_table, source_id, embedding, model) - VALUES (?, ?, ?, ?) - """, - (source_table, source_id, json.dumps(embedding), model), - ) - - # Add to vector index - conn.execute( - """ - INSERT INTO vec_index (embedding) VALUES (?) - """, - (json.dumps(embedding),), - ) - - conn.commit() - conn.close() - return True - except Exception as e: - print(f"Warning: Vector embedding failed: {e}") - return False - - def vector_search( - self, - query_embedding: List[float], - scope: str = "both", - project_path: str = None, - limit: int = 10, - ) -> List[Dict[str, Any]]: - """Search using vector similarity. Requires sqlite-vec extension.""" - results = [] - dbs = [] - if scope in ("global", "both"): - dbs.append(("global", self._get_global_db_path())) - if scope in ("project", "both"): - if not project_path: - project_path = os.getcwd() - dbs.append(("project", self._get_project_db_path(project_path))) - - for scope_name, db_path in dbs: - if not db_path.exists(): - continue - try: - conn = self._init_memory_db(db_path) - # Check if vec_index exists (sqlite-vec installed) - cursor = conn.execute( - "SELECT name FROM sqlite_master WHERE type='table' AND name='vec_index'" - ) - if not cursor.fetchone(): - conn.close() - continue - - # Vector similarity search using sqlite-vec - cursor = conn.execute( - """ - SELECT e.source_table, e.source_id, vec_distance(v.embedding, ?) as distance - FROM vec_index v - JOIN embeddings e ON e.id = v.rowid - ORDER BY distance ASC - LIMIT ? - """, - (json.dumps(query_embedding), limit), - ) - - for row in cursor.fetchall(): - results.append( - { - "source_table": row["source_table"], - "source_id": row["source_id"], - "distance": row["distance"], - "scope": scope_name, - } - ) - conn.close() - except Exception as e: - print(f"Warning: Vector search failed for {scope_name}: {e}") - - return sorted(results, key=lambda x: x.get("distance", float("inf")))[:limit] - - # Utility methods - def get_memory_stats( - self, scope: str = "both", project_path: str = None - ) -> Dict[str, Any]: - """Get memory system statistics.""" - stats = { - "markdown_files": {"global": 0, "project": 0}, - "structured_memories": {"global": 0, "project": 0}, - "vector_embeddings": {"global": 0, "project": 0}, - "total_size_bytes": 0, - } - - # Count markdown files - if scope in ("global", "both"): - global_files = list(self.global_memory_dir.rglob("*.md")) - stats["markdown_files"]["global"] = len(global_files) - stats["total_size_bytes"] += sum(f.stat().st_size for f in global_files) - - if scope in ("project", "both") and project_path: - memory_dir = self._get_project_memory_dir(project_path) - if memory_dir.exists(): - project_files = list(memory_dir.rglob("*.md")) - stats["markdown_files"]["project"] = len(project_files) - stats["total_size_bytes"] += sum( - f.stat().st_size for f in project_files - ) - - # Count database records - dbs = [] - if scope in ("global", "both"): - dbs.append(("global", self._get_global_db_path())) - if scope in ("project", "both") and project_path: - dbs.append(("project", self._get_project_db_path(project_path))) - - for scope_name, db_path in dbs: - if not db_path.exists(): - continue - - try: - conn = self._init_memory_db(db_path) - - # Count memories - cursor = conn.execute("SELECT COUNT(*) FROM memories") - stats["structured_memories"][scope_name] = cursor.fetchone()[0] - - # Count embeddings - try: - cursor = conn.execute("SELECT COUNT(*) FROM embeddings") - stats["vector_embeddings"][scope_name] = cursor.fetchone()[0] - except sqlite3.Error: - stats["vector_embeddings"][scope_name] = 0 - - # Add DB file size - stats["total_size_bytes"] += db_path.stat().st_size - - conn.close() - except Exception as e: - print(f"Warning: Failed to get stats for {scope_name}: {e}") - - return stats diff --git a/pkg/hanzo-tools-database/hanzo_tools/database/memory_tool.py b/pkg/hanzo-tools-database/hanzo_tools/database/memory_tool.py deleted file mode 100644 index e69f29984..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/database/memory_tool.py +++ /dev/null @@ -1,406 +0,0 @@ -"""Unified memory tool with hybrid storage backend. - -Combines plaintext markdown files, SQLite FTS, and vector search. -""" - -import os -from typing import ( - Any, - Dict, - Unpack, - Optional, - Annotated, - TypedDict, - final, - override, -) - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, PermissionManager, auto_timeout - -from .memory_manager import MemoryManager - -# Parameter types -Action = Annotated[ - str, - Field( - description="Memory action: read, write, append, search, create, list, stats", - default="search", - ), -] - -Content = Annotated[ - Optional[str], - Field( - description="Content to store or search query", - default=None, - ), -] - -FilePath = Annotated[ - Optional[str], - Field( - description="Markdown file path (e.g., 'rules.md', 'sessions/today.md')", - default=None, - ), -] - -Category = Annotated[ - Optional[str], - Field( - description="Memory category for organization", - default=None, - ), -] - -Scope = Annotated[ - str, - Field( - description="Memory scope: global, project, or both", - default="project", - ), -] - -SearchType = Annotated[ - str, - Field( - description="Search type: fulltext, vector, or hybrid", - default="fulltext", - ), -] - -Importance = Annotated[ - int, - Field( - description="Memory importance (1-10)", - default=5, - ), -] - -Limit = Annotated[ - int, - Field( - description="Maximum results to return", - default=10, - ), -] - - -class MemoryParams(TypedDict, total=False): - """Parameters for memory tool.""" - - action: str - content: Optional[str] - file_path: Optional[str] - category: Optional[str] - scope: str - search_type: str - importance: int - limit: int - - -@final -class MemoryTool(BaseTool): - """Unified memory tool with hybrid storage.""" - - def __init__(self, permission_manager: PermissionManager): - """Initialize the memory tool.""" - super().__init__(permission_manager) - self.memory_manager = MemoryManager(permission_manager) - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "memory" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Hybrid memory management. Actions: read, write, append, search, create, list, stats. - -Storage types: -- Markdown files: Human-readable rule/context files -- SQLite FTS: Full-text search across all content -- Vector search: Semantic similarity (when available) - -Usage: -memory --action read --file-path rules.md --scope global -memory --action write --file-path architecture.md --content "..." -memory --action append --file-path sessions/today.md --content "New insight" -memory --action search --content "database design" --scope both -memory --action create --content "Important fact" --category general -memory --action list --scope project -memory --action stats -""" - - @override - @auto_timeout("memory") - async def call( - self, - ctx: MCPContext, - **params: Unpack[MemoryParams], - ) -> str: - """Execute memory operation.""" - tool_ctx = self.create_tool_context(ctx) - - # Extract action - action = params.get("action", "search") - - # Get current working directory for project context - project_path = os.getcwd() - - # Route to appropriate handler - try: - if action == "read": - return await self._handle_read(params, project_path, tool_ctx) - elif action == "write": - return await self._handle_write(params, project_path, tool_ctx) - elif action == "append": - return await self._handle_append(params, project_path, tool_ctx) - elif action == "search": - return await self._handle_search(params, project_path, tool_ctx) - elif action == "create": - return await self._handle_create(params, project_path, tool_ctx) - elif action == "list": - return await self._handle_list(params, project_path, tool_ctx) - elif action == "stats": - return await self._handle_stats(params, project_path, tool_ctx) - else: - return f"Error: Unknown action '{action}'. Valid actions: read, write, append, search, create, list, stats" - - except Exception as e: - await tool_ctx.error(f"Memory operation failed: {str(e)}") - return f"Error: {str(e)}" - - async def _handle_read( - self, params: Dict[str, Any], project_path: str, tool_ctx - ) -> str: - """Read markdown memory file.""" - file_path = params.get("file_path") - if not file_path: - return "Error: file_path required for read action" - - scope = params.get("scope", "project") - - content = self.memory_manager.read_markdown_file(file_path, scope, project_path) - - if content is None: - return f"Error: File '{file_path}' not found in {scope} scope" - - await tool_ctx.info(f"Read {scope} memory file: {file_path}") - - return f"=== {file_path} ({scope}) ===\n\n{content}" - - async def _handle_write( - self, params: Dict[str, Any], project_path: str, tool_ctx - ) -> str: - """Write markdown memory file.""" - file_path = params.get("file_path") - content = params.get("content") - - if not file_path: - return "Error: file_path required for write action" - if not content: - return "Error: content required for write action" - - scope = params.get("scope", "project") - category = params.get("category") - - success = self.memory_manager.write_markdown_file( - file_path, content, scope, project_path, category - ) - - if success: - await tool_ctx.info(f"Wrote {scope} memory file: {file_path}") - return f"Successfully wrote {scope} memory file: {file_path}" - else: - return f"Error: Failed to write memory file: {file_path}" - - async def _handle_append( - self, params: Dict[str, Any], project_path: str, tool_ctx - ) -> str: - """Append to markdown memory file.""" - file_path = params.get("file_path") - content = params.get("content") - - if not file_path: - return "Error: file_path required for append action" - if not content: - return "Error: content required for append action" - - scope = params.get("scope", "project") - category = params.get("category") - - success = self.memory_manager.append_markdown_file( - file_path, content, scope, project_path, category - ) - - if success: - await tool_ctx.info(f"Appended to {scope} memory file: {file_path}") - return f"Successfully appended to {scope} memory file: {file_path}" - else: - return f"Error: Failed to append to memory file: {file_path}" - - async def _handle_search( - self, params: Dict[str, Any], project_path: str, tool_ctx - ) -> str: - """Search across all memory types.""" - query = params.get("content") - if not query: - return "Error: content (search query) required for search action" - - scope = params.get("scope", "both") - search_type = params.get("search_type", "fulltext") - limit = params.get("limit", 10) - - results = self.memory_manager.search_memories( - query, scope, project_path, search_type, limit - ) - - if not results: - return f"No memories found for query: '{query}'" - - # Format results - output = [f"=== Memory Search Results for '{query}' ==="] - output.append(f"Found {len(results)} results (scope: {scope})\n") - - for i, result in enumerate(results, 1): - source = result["source"] - scope_name = result["scope"] - - if source == "markdown": - output.append(f"{i}. [{scope_name}] {result['path']} (markdown)") - if "snippet" in result: - output.append(f" {result['snippet']}") - output.append(f" Category: {result.get('category', 'none')}") - output.append(f" Modified: {result['modified_at']}") - - elif source == "memory": - output.append( - f"{i}. [{scope_name}] Memory #{result['id']} (structured)" - ) - if "snippet" in result: - output.append(f" {result['snippet']}") - output.append(f" Category: {result.get('category', 'none')}") - output.append(f" Importance: {result['importance']}") - output.append(f" Created: {result['created_at']}") - - output.append("") - - await tool_ctx.info(f"Found {len(results)} memories for: {query}") - - return "\n".join(output) - - async def _handle_create( - self, params: Dict[str, Any], project_path: str, tool_ctx - ) -> str: - """Create structured memory record.""" - content = params.get("content") - if not content: - return "Error: content required for create action" - - scope = params.get("scope", "project") - category = params.get("category") - importance = params.get("importance", 5) - - memory_id = self.memory_manager.create_memory( - content, category, importance, None, scope, project_path - ) - - await tool_ctx.info(f"Created {scope} memory: #{memory_id}") - - return f"Successfully created {scope} memory #{memory_id}\nContent: {content[:100]}..." - - async def _handle_list( - self, params: Dict[str, Any], project_path: str, tool_ctx - ) -> str: - """List memory files and records.""" - scope = params.get("scope", "both") - - # List markdown files - markdown_files = self.memory_manager.list_markdown_files(scope, project_path) - - output = [f"=== Memory Files ({scope} scope) ==="] - - if markdown_files: - output.append("\nMarkdown Files:") - for file_info in markdown_files: - size_kb = file_info["size"] / 1024 - output.append( - f" [{file_info['scope']}] {file_info['path']} ({size_kb:.1f}KB, {file_info['modified']})" - ) - else: - output.append("\nNo markdown files found") - - # Get stats for structured memories - stats = self.memory_manager.get_memory_stats(scope, project_path) - - output.append(f"\nStructured Memories:") - if scope in ("global", "both"): - output.append(f" Global: {stats['structured_memories']['global']} records") - if scope in ("project", "both"): - output.append( - f" Project: {stats['structured_memories']['project']} records" - ) - - total_size_mb = stats["total_size_bytes"] / 1024 / 1024 - output.append(f"\nTotal size: {total_size_mb:.2f}MB") - - return "\n".join(output) - - async def _handle_stats( - self, params: Dict[str, Any], project_path: str, tool_ctx - ) -> str: - """Get detailed memory statistics.""" - scope = params.get("scope", "both") - - stats = self.memory_manager.get_memory_stats(scope, project_path) - - output = [f"=== Memory System Statistics ({scope} scope) ==="] - output.append(f"Project: {project_path}\n") - - # Markdown files - output.append("Markdown Files:") - output.append(f" Global: {stats['markdown_files']['global']} files") - output.append(f" Project: {stats['markdown_files']['project']} files") - - # Structured memories - output.append("\nStructured Memories:") - output.append(f" Global: {stats['structured_memories']['global']} records") - output.append(f" Project: {stats['structured_memories']['project']} records") - - # Vector embeddings - output.append("\nVector Embeddings:") - output.append(f" Global: {stats['vector_embeddings']['global']} embeddings") - output.append(f" Project: {stats['vector_embeddings']['project']} embeddings") - - # Storage size - total_size_mb = stats["total_size_bytes"] / 1024 / 1024 - output.append(f"\nTotal Storage: {total_size_mb:.2f}MB") - - # Features status - output.append("\nFeature Status:") - output.append(" โœ“ Markdown files") - output.append(" โœ“ SQLite FTS5 full-text search") - - # Check sqlite-vec availability - try: - import sqlite3 - - conn = sqlite3.connect(":memory:") - conn.enable_load_extension(True) - conn.load_extension("vec0") - output.append(" โœ“ sqlite-vec vector search") - conn.close() - except Exception: - output.append(" โœ— sqlite-vec vector search (not available)") - - return "\n".join(output) - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-database/hanzo_tools/database/sql.py b/pkg/hanzo-tools-database/hanzo_tools/database/sql.py deleted file mode 100644 index 58dc13dd0..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/database/sql.py +++ /dev/null @@ -1,430 +0,0 @@ -"""Unified SQL database tool.""" - -import sqlite3 -from typing import ( - Any, - Dict, - Unpack, - Optional, - Annotated, - TypedDict, - final, - override, -) - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, PermissionManager, auto_timeout - -from .database_manager import DatabaseManager - -# Parameter types -Query = Annotated[ - Optional[str], - Field( - description="SQL query to execute", - default=None, - ), -] - -Pattern = Annotated[ - Optional[str], - Field( - description="Search pattern for table/column names or data", - default=None, - ), -] - -Table = Annotated[ - Optional[str], - Field( - description="Table name for operations", - default=None, - ), -] - -Action = Annotated[ - str, - Field( - description="Action: query (default), search, schema, stats", - default="query", - ), -] - -Limit = Annotated[ - int, - Field( - description="Maximum rows to return", - default=100, - ), -] - - -class SQLParams(TypedDict, total=False): - """Parameters for SQL tool.""" - - query: Optional[str] - pattern: Optional[str] - table: Optional[str] - action: str - limit: int - - -@final -class SQLTool(BaseTool): - """Unified SQL database tool.""" - - def __init__( - self, permission_manager: PermissionManager, db_manager: DatabaseManager - ): - """Initialize the SQL tool.""" - super().__init__(permission_manager) - self.db_manager = db_manager - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "sql" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """SQLite database. Actions: query (default), search, schema, stats. - -Usage: -sql "SELECT * FROM users WHERE active = 1" -sql --action schema -sql --action search --pattern "john" -sql --action stats --table users -""" - - @override - @auto_timeout("sql") - async def call( - self, - ctx: MCPContext, - **params: Unpack[SQLParams], - ) -> str: - """Execute SQL operation.""" - tool_ctx = self.create_tool_context(ctx) - - # Get current project database - project_db = self.db_manager.get_current_project_db() - if not project_db: - return "Error: No project database found. Are you in a project directory?" - - # Extract action - action = params.get("action", "query") - - # Route to appropriate handler - if action == "query": - return await self._handle_query(project_db, params, tool_ctx) - elif action == "search": - return await self._handle_search(project_db, params, tool_ctx) - elif action == "schema": - return await self._handle_schema(project_db, params, tool_ctx) - elif action == "stats": - return await self._handle_stats(project_db, params, tool_ctx) - else: - return f"Error: Unknown action '{action}'. Valid actions: query, search, schema, stats" - - async def _handle_query(self, project_db, params: Dict[str, Any], tool_ctx) -> str: - """Execute SQL query.""" - query = params.get("query") - if not query: - return "Error: query required for query action" - - limit = params.get("limit", 100) - - try: - with project_db.get_sqlite_connection() as conn: - # Enable row factory for dict-like access - conn.row_factory = sqlite3.Row - - # Add LIMIT if not present in SELECT queries - query_upper = query.upper().strip() - if query_upper.startswith("SELECT") and "LIMIT" not in query_upper: - query = f"{query} LIMIT {limit}" - - cursor = conn.execute(query) - - # Handle different query types - if query_upper.startswith("SELECT"): - rows = cursor.fetchall() - - if not rows: - return "No results found" - - # Get column names - columns = [description[0] for description in cursor.description] - - # Format as table - output = ["=== Query Results ==="] - output.append(f"Columns: {', '.join(columns)}") - output.append("-" * 60) - - for row in rows: - row_data = [] - for col in columns: - value = row[col] - if value is None: - value = "NULL" - elif isinstance(value, str) and len(value) > 50: - value = value[:50] + "..." - row_data.append(str(value)) - output.append(" | ".join(row_data)) - - output.append(f"\nRows returned: {len(rows)}") - if len(rows) == limit: - output.append(f"(Limited to {limit} rows)") - - return "\n".join(output) - - else: - # For INSERT, UPDATE, DELETE - conn.commit() - rows_affected = cursor.rowcount - - if query_upper.startswith("INSERT"): - return f"Inserted {rows_affected} row(s)" - elif query_upper.startswith("UPDATE"): - return f"Updated {rows_affected} row(s)" - elif query_upper.startswith("DELETE"): - return f"Deleted {rows_affected} row(s)" - else: - return f"Query executed successfully. Rows affected: {rows_affected}" - - except Exception as e: - await tool_ctx.error(f"Query failed: {str(e)}") - return f"Error executing query: {str(e)}" - - async def _handle_search(self, project_db, params: Dict[str, Any], tool_ctx) -> str: - """Search for data in tables.""" - pattern = params.get("pattern") - if not pattern: - return "Error: pattern required for search action" - - table = params.get("table") - limit = params.get("limit", 100) - - try: - with project_db.get_sqlite_connection() as conn: - conn.row_factory = sqlite3.Row - - # Get all tables if not specified - if not table: - cursor = conn.execute(""" - SELECT name FROM sqlite_master - WHERE type='table' AND name NOT LIKE 'sqlite_%' - """) - tables = [row[0] for row in cursor.fetchall()] - else: - tables = [table] - - all_results = [] - - for tbl in tables: - # Get columns - cursor = conn.execute(f"PRAGMA table_info({tbl})") - columns = [row[1] for row in cursor.fetchall()] - - # Build search query - where_clauses = [f"{col} LIKE ?" for col in columns] - query = f"SELECT * FROM {tbl} WHERE {' OR '.join(where_clauses)} LIMIT {limit}" - - # Search - cursor = conn.execute(query, [f"%{pattern}%"] * len(columns)) - rows = cursor.fetchall() - - if rows: - all_results.append((tbl, columns, rows)) - - if not all_results: - return f"No results found for pattern '{pattern}'" - - # Format results - output = [f"=== Search Results for '{pattern}' ==="] - - for tbl, columns, rows in all_results: - output.append(f"\nTable: {tbl}") - output.append(f"Columns: {', '.join(columns)}") - output.append("-" * 60) - - for row in rows: - row_data = [] - for col in columns: - value = row[col] - if value is None: - value = "NULL" - elif isinstance(value, str): - # Highlight matches - if pattern.lower() in str(value).lower(): - value = f"**{value}**" - if len(value) > 50: - value = value[:50] + "..." - row_data.append(str(value)) - output.append(" | ".join(row_data)) - - output.append(f"Found {len(rows)} row(s) in {tbl}") - - return "\n".join(output) - - except Exception as e: - await tool_ctx.error(f"Search failed: {str(e)}") - return f"Error during search: {str(e)}" - - async def _handle_schema(self, project_db, params: Dict[str, Any], tool_ctx) -> str: - """Show database schema.""" - table = params.get("table") - - try: - with project_db.get_sqlite_connection() as conn: - if table: - # Show specific table schema - cursor = conn.execute(f"PRAGMA table_info({table})") - columns = cursor.fetchall() - - if not columns: - return f"Table '{table}' not found" - - output = [f"=== Schema for table '{table}' ==="] - output.append("Column | Type | Not Null | Default | Primary Key") - output.append("-" * 60) - - for col in columns: - output.append( - f"{col[1]} | {col[2]} | {col[3]} | {col[4]} | {col[5]}" - ) - - # Get indexes - cursor = conn.execute(f"PRAGMA index_list({table})") - indexes = cursor.fetchall() - - if indexes: - output.append("\nIndexes:") - for idx in indexes: - output.append(f" {idx[1]} (unique: {idx[2]})") - - else: - # Show all tables - cursor = conn.execute(""" - SELECT name, sql FROM sqlite_master - WHERE type='table' AND name NOT LIKE 'sqlite_%' - ORDER BY name - """) - tables = cursor.fetchall() - - if not tables: - return "No tables found in database" - - output = ["=== Database Schema ==="] - - for table_name, _create_sql in tables: - output.append(f"\nTable: {table_name}") - - # Get row count - cursor = conn.execute(f"SELECT COUNT(*) FROM {table_name}") - count = cursor.fetchone()[0] - output.append(f"Rows: {count}") - - # Get columns - cursor = conn.execute(f"PRAGMA table_info({table_name})") - columns = cursor.fetchall() - output.append( - f"Columns: {', '.join([col[1] for col in columns])}" - ) - - return "\n".join(output) - - except Exception as e: - await tool_ctx.error(f"Failed to get schema: {str(e)}") - return f"Error getting schema: {str(e)}" - - async def _handle_stats(self, project_db, params: Dict[str, Any], tool_ctx) -> str: - """Get database statistics.""" - table = params.get("table") - - try: - with project_db.get_sqlite_connection() as conn: - output = ["=== Database Statistics ==="] - output.append(f"Database: {project_db.sqlite_path}") - - # Get file size - db_size = project_db.sqlite_path.stat().st_size - output.append(f"Size: {db_size / 1024 / 1024:.2f} MB") - - if table: - # Stats for specific table - cursor = conn.execute(f"SELECT COUNT(*) FROM {table}") - count = cursor.fetchone()[0] - - output.append(f"\nTable: {table}") - output.append(f"Total rows: {count}") - - # Get column stats - cursor = conn.execute(f"PRAGMA table_info({table})") - columns = cursor.fetchall() - - output.append("\nColumn statistics:") - for col in columns: - col_name = col[1] - col_type = col[2] - - # Get basic stats based on type - if "INT" in col_type.upper() or "REAL" in col_type.upper(): - cursor = conn.execute(f""" - SELECT - MIN({col_name}) as min_val, - MAX({col_name}) as max_val, - AVG({col_name}) as avg_val, - COUNT(DISTINCT {col_name}) as distinct_count - FROM {table} - """) - stats = cursor.fetchone() - output.append( - f" {col_name}: min={stats[0]}, max={stats[1]}, avg={stats[2]:.2f}, distinct={stats[3]}" - ) - else: - cursor = conn.execute(f""" - SELECT - COUNT(DISTINCT {col_name}) as distinct_count, - COUNT(*) - COUNT({col_name}) as null_count - FROM {table} - """) - stats = cursor.fetchone() - output.append( - f" {col_name}: distinct={stats[0]}, nulls={stats[1]}" - ) - - else: - # Overall database stats - cursor = conn.execute(""" - SELECT name FROM sqlite_master - WHERE type='table' AND name NOT LIKE 'sqlite_%' - """) - tables = cursor.fetchall() - - output.append(f"\nTotal tables: {len(tables)}") - output.append("\nTable row counts:") - - total_rows = 0 - for (table_name,) in tables: - cursor = conn.execute(f"SELECT COUNT(*) FROM {table_name}") - count = cursor.fetchone()[0] - total_rows += count - output.append(f" {table_name}: {count} rows") - - output.append(f"\nTotal rows across all tables: {total_rows}") - - return "\n".join(output) - - except Exception as e: - await tool_ctx.error(f"Failed to get stats: {str(e)}") - return f"Error getting stats: {str(e)}" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-database/hanzo_tools/database/sql_query.py b/pkg/hanzo-tools-database/hanzo_tools/database/sql_query.py deleted file mode 100644 index 78343049f..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/database/sql_query.py +++ /dev/null @@ -1,266 +0,0 @@ -"""SQL query tool for direct database queries.""" - -import sqlite3 -from typing import Unpack, Optional, Annotated, TypedDict, final, override - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - PermissionManager, - auto_timeout, - create_tool_context, -) - -from .database_manager import DatabaseManager - -Query = Annotated[ - str, - Field( - description="SQL query to execute", - min_length=1, - ), -] - -ProjectPath = Annotated[ - Optional[str], - Field( - description="Project path (defaults to current directory)", - default=None, - ), -] - -ReadOnly = Annotated[ - bool, - Field( - description="Execute in read-only mode (no INSERT/UPDATE/DELETE)", - default=True, - ), -] - - -class SqlQueryParams(TypedDict, total=False): - """Parameters for SQL query tool.""" - - query: str - project_path: Optional[str] - read_only: bool - - -@final -class SqlQueryTool(BaseTool): - """Tool for executing SQL queries on project databases.""" - - def __init__( - self, permission_manager: PermissionManager, db_manager: DatabaseManager - ): - """Initialize the SQL query tool. - - Args: - permission_manager: Permission manager for access control - db_manager: Database manager instance - """ - self.permission_manager = permission_manager - self.db_manager = db_manager - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "sql_query" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Execute SQL queries on the project's embedded SQLite database. - -Each project has its own SQLite database with tables: -- metadata: Key-value store for project metadata -- files: File information and content -- symbols: Code symbols (functions, classes, etc.) - -Features: -- Direct SQL query execution -- Read-only mode by default (safety) -- Returns results in tabular format -- Automatic project detection - -Examples: -- sql_query --query "SELECT * FROM files LIMIT 10" -- sql_query --query "SELECT name, type FROM symbols WHERE type='function'" -- sql_query --query "INSERT INTO metadata (key, value) VALUES ('version', '1.0')" --read-only false - -Note: Use sql_search for text search operations.""" - - @override - @auto_timeout("sql_query") - async def call( - self, - ctx: MCPContext, - **params: Unpack[SqlQueryParams], - ) -> str: - """Execute SQL query. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Query results - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - query = params.get("query") - if not query: - return "Error: query is required" - - project_path = params.get("project_path") - read_only = params.get("read_only", True) - - # Get project database - try: - if project_path: - project_db = self.db_manager.get_project_db(project_path) - else: - import os - - project_db = self.db_manager.get_project_for_path(os.getcwd()) - - if not project_db: - return "Error: Could not find project database" - - except PermissionError as e: - return str(e) - except Exception as e: - return f"Error accessing project database: {str(e)}" - - # Check if query is read-only using token-based parsing - if read_only: - import re - - # Remove comments and string literals to prevent bypass - clean_query = re.sub( - r"--.*$", "", query, flags=re.MULTILINE - ) # Remove -- comments - clean_query = re.sub( - r"/\*.*?\*/", "", clean_query, flags=re.DOTALL - ) # Remove /* */ comments - clean_query = re.sub( - r"'[^']*'", "''", clean_query - ) # Neutralize string literals - clean_query = re.sub( - r'"[^"]*"', '""', clean_query - ) # Neutralize identifiers - - # Check for write operations using word boundaries - write_keywords = [ - "INSERT", - "UPDATE", - "DELETE", - "DROP", - "CREATE", - "ALTER", - "TRUNCATE", - "REPLACE", - ] - for keyword in write_keywords: - if re.search(rf"\b{keyword}\b", clean_query, re.IGNORECASE): - return f"Error: Query contains {keyword} operation. Set --read-only false to allow write operations." - - await tool_ctx.info( - f"Executing SQL query on project: {project_db.project_path}" - ) - - # Execute query - conn = None - try: - conn = project_db.get_sqlite_connection() - cursor = conn.cursor() - - # Execute the query - cursor.execute(query) - - # Handle different query types - if query.strip().upper().startswith("SELECT"): - # Fetch results - results = cursor.fetchall() - - if not results: - return "No results found." - - # Get column names - columns = [desc[0] for desc in cursor.description] - - # Format as table - output = self._format_results_table(columns, results) - - return f"Query executed successfully. Found {len(results)} row(s).\n\n{output}" - - else: - # For non-SELECT queries, commit and return affected rows - conn.commit() - affected = cursor.rowcount - return f"Query executed successfully. Affected {affected} row(s)." - - except sqlite3.Error as e: - await tool_ctx.error(f"SQL error: {str(e)}") - return f"SQL error: {str(e)}" - except Exception as e: - await tool_ctx.error(f"Unexpected error: {str(e)}") - return f"Error executing query: {str(e)}" - finally: - if conn: - conn.close() - - def _format_results_table(self, columns: list[str], rows: list[tuple]) -> str: - """Format query results as a table.""" - if not rows: - return "No results" - - # Calculate column widths - col_widths = [] - for i, col in enumerate(columns): - max_width = len(col) - for row in rows[:100]: # Check first 100 rows - val_str = str(row[i]) if row[i] is not None else "NULL" - max_width = max(max_width, len(val_str)) - col_widths.append(min(max_width, 50)) # Cap at 50 chars - - # Build header - header = " | ".join( - col.ljust(width) for col, width in zip(columns, col_widths, strict=False) - ) - separator = "-+-".join("-" * width for width in col_widths) - - # Build rows - output_rows = [] - for row in rows[:1000]: # Limit to 1000 rows - row_str = " | ".join( - self._truncate(str(val) if val is not None else "NULL", width).ljust( - width - ) - for val, width in zip(row, col_widths, strict=False) - ) - output_rows.append(row_str) - - # Combine - output = [header, separator] + output_rows - - if len(rows) > 1000: - output.append(f"\n... and {len(rows) - 1000} more rows") - - return "\n".join(output) - - def _truncate(self, text: str, max_width: int) -> str: - """Truncate text to max width.""" - if len(text) <= max_width: - return text - return text[: max_width - 3] + "..." - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-database/hanzo_tools/database/sql_search.py b/pkg/hanzo-tools-database/hanzo_tools/database/sql_search.py deleted file mode 100644 index aac8212e5..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/database/sql_search.py +++ /dev/null @@ -1,305 +0,0 @@ -"""SQL search tool for text search in database.""" - -import sqlite3 -from typing import Unpack, Optional, Annotated, TypedDict, final, override - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - PermissionManager, - auto_timeout, - create_tool_context, -) - -from .database_manager import DatabaseManager - -SearchPattern = Annotated[ - str, - Field( - description="Search pattern (SQL LIKE syntax, % for wildcard)", - min_length=1, - ), -] - -Table = Annotated[ - str, - Field( - description="Table to search in (files, symbols, metadata)", - default="files", - ), -] - -Column = Annotated[ - Optional[str], - Field( - description="Specific column to search (searches all text columns if not specified)", - default=None, - ), -] - -ProjectPath = Annotated[ - Optional[str], - Field( - description="Project path (defaults to current directory)", - default=None, - ), -] - -MaxResults = Annotated[ - int, - Field( - description="Maximum number of results", - default=50, - ), -] - - -class SqlSearchParams(TypedDict, total=False): - """Parameters for SQL search tool.""" - - pattern: str - table: str - column: Optional[str] - project_path: Optional[str] - max_results: int - - -@final -class SqlSearchTool(BaseTool): - """Tool for searching text in SQLite database.""" - - def __init__( - self, permission_manager: PermissionManager, db_manager: DatabaseManager - ): - """Initialize the SQL search tool. - - Args: - permission_manager: Permission manager for access control - db_manager: Database manager instance - """ - self.permission_manager = permission_manager - self.db_manager = db_manager - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "sql_search" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Search for text patterns in the project's SQLite database. - -Supports SQL LIKE pattern matching: -- % matches any sequence of characters -- _ matches any single character -- Use %pattern% to search for pattern anywhere - -Tables available: -- files: Search in file paths and content -- symbols: Search in symbol names, types, and signatures -- metadata: Search in key-value metadata - -Examples: -- sql_search --pattern "%TODO%" --table files -- sql_search --pattern "test_%" --table symbols --column name -- sql_search --pattern "%config%" --table metadata -- sql_search --pattern "%.py" --table files --column path - -Use sql_query for complex queries with joins, conditions, etc.""" - - @override - @auto_timeout("sql_search") - async def call( - self, - ctx: MCPContext, - **params: Unpack[SqlSearchParams], - ) -> str: - """Execute SQL search. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Search results - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - pattern = params.get("pattern") - if not pattern: - return "Error: pattern is required" - - table = params.get("table", "files") - column = params.get("column") - project_path = params.get("project_path") - max_results = params.get("max_results", 50) - - # Validate table - valid_tables = ["files", "symbols", "metadata"] - if table not in valid_tables: - return f"Error: Invalid table '{table}'. Must be one of: {', '.join(valid_tables)}" - - # Get project database - try: - if project_path: - project_db = self.db_manager.get_project_db(project_path) - else: - import os - - project_db = self.db_manager.get_project_for_path(os.getcwd()) - - if not project_db: - return "Error: Could not find project database" - - except PermissionError as e: - return str(e) - except Exception as e: - return f"Error accessing project database: {str(e)}" - - await tool_ctx.info(f"Searching in {table} table for pattern: {pattern}") - - # Build search query - conn = None - try: - conn = project_db.get_sqlite_connection() - cursor = conn.cursor() - - # Get searchable columns for the table - if column: - # Validate column exists - cursor.execute(f"PRAGMA table_info({table})") - columns_info = cursor.fetchall() - column_names = [col[1] for col in columns_info] - - if column not in column_names: - return f"Error: Column '{column}' not found in table '{table}'. Available columns: {', '.join(column_names)}" - - search_columns = [column] - else: - # Get all text columns - search_columns = self._get_text_columns(cursor, table) - if not search_columns: - return f"Error: No searchable text columns in table '{table}'" - - # Build WHERE clause - where_conditions = [f"{col} LIKE ?" for col in search_columns] - where_clause = " OR ".join(where_conditions) - - # Build query - if table == "files": - query = f""" - SELECT path, SUBSTR(content, 1, 200) as snippet, size, modified_at - FROM {table} - WHERE {where_clause} - LIMIT ? - """ - params_list = [pattern] * len(search_columns) + [max_results] - - elif table == "symbols": - query = f""" - SELECT name, type, file_path, line_start, signature - FROM {table} - WHERE {where_clause} - ORDER BY type, name - LIMIT ? - """ - params_list = [pattern] * len(search_columns) + [max_results] - - else: # metadata - query = f""" - SELECT key, value, updated_at - FROM {table} - WHERE {where_clause} - LIMIT ? - """ - params_list = [pattern] * len(search_columns) + [max_results] - - # Execute search - cursor.execute(query, params_list) - results = cursor.fetchall() - - if not results: - return f"No results found for pattern '{pattern}' in {table}" - - # Format results - output = self._format_results(table, results, pattern, search_columns) - - return f"Found {len(results)} result(s) in {table}:\n\n{output}" - - except sqlite3.Error as e: - await tool_ctx.error(f"SQL error: {str(e)}") - return f"SQL error: {str(e)}" - except Exception as e: - await tool_ctx.error(f"Unexpected error: {str(e)}") - return f"Error executing search: {str(e)}" - finally: - if conn: - conn.close() - - def _get_text_columns(self, cursor: sqlite3.Cursor, table: str) -> list[str]: - """Get text columns for a table.""" - cursor.execute(f"PRAGMA table_info({table})") - columns_info = cursor.fetchall() - - # Get TEXT columns - text_columns = [] - for col in columns_info: - col_name = col[1] - col_type = col[2].upper() - if "TEXT" in col_type or "CHAR" in col_type or col_type == "": - text_columns.append(col_name) - - return text_columns - - def _format_results( - self, table: str, results: list, pattern: str, search_columns: list[str] - ) -> str: - """Format search results based on table type.""" - output = [] - - if table == "files": - output.append(f"Searched columns: {', '.join(search_columns)}\n") - for row in results: - path, snippet, size, modified = row - output.append(f"File: {path}") - output.append(f"Size: {size} bytes") - output.append(f"Modified: {modified}") - if snippet: - # Highlight pattern in snippet - snippet = snippet.replace("\n", " ") - if len(snippet) > 150: - snippet = snippet[:150] + "..." - output.append(f"Content: {snippet}") - output.append("-" * 60) - - elif table == "symbols": - output.append(f"Searched columns: {', '.join(search_columns)}\n") - for row in results: - name, type_, file_path, line_start, signature = row - output.append(f"{type_}: {name}") - output.append(f"File: {file_path}:{line_start}") - if signature: - output.append(f"Signature: {signature}") - output.append("-" * 60) - - else: # metadata - output.append(f"Searched columns: {', '.join(search_columns)}\n") - for row in results: - key, value, updated = row - output.append(f"Key: {key}") - output.append(f"Value: {value}") - output.append(f"Updated: {updated}") - output.append("-" * 60) - - return "\n".join(output) - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-database/hanzo_tools/database/sql_stats.py b/pkg/hanzo-tools-database/hanzo_tools/database/sql_stats.py deleted file mode 100644 index 0115b9d0c..000000000 --- a/pkg/hanzo-tools-database/hanzo_tools/database/sql_stats.py +++ /dev/null @@ -1,271 +0,0 @@ -"""SQL statistics tool for database insights.""" - -import sqlite3 -from typing import Unpack, Optional, Annotated, TypedDict, final, override - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - PermissionManager, - auto_timeout, - create_tool_context, -) - -from .database_manager import DatabaseManager - -ProjectPath = Annotated[ - Optional[str], - Field( - description="Project path (defaults to current directory)", - default=None, - ), -] - -Detailed = Annotated[ - bool, - Field( - description="Show detailed statistics", - default=False, - ), -] - - -class SqlStatsParams(TypedDict, total=False): - """Parameters for SQL stats tool.""" - - project_path: Optional[str] - detailed: bool - - -@final -class SqlStatsTool(BaseTool): - """Tool for getting SQLite database statistics.""" - - def __init__( - self, permission_manager: PermissionManager, db_manager: DatabaseManager - ): - """Initialize the SQL stats tool. - - Args: - permission_manager: Permission manager for access control - db_manager: Database manager instance - """ - self.permission_manager = permission_manager - self.db_manager = db_manager - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "sql_stats" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Get statistics about the project's SQLite database. - -Shows: -- Database size and location -- Table information (row counts, sizes) -- Index information -- Column statistics -- Most common values (with --detailed) - -Examples: -- sql_stats # Basic stats for current project -- sql_stats --detailed # Detailed statistics -- sql_stats --project-path /path/to/project -""" - - @override - @auto_timeout("sql_stats") - async def call( - self, - ctx: MCPContext, - **params: Unpack[SqlStatsParams], - ) -> str: - """Get database statistics. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Database statistics - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - project_path = params.get("project_path") - detailed = params.get("detailed", False) - - # Get project database - try: - if project_path: - project_db = self.db_manager.get_project_db(project_path) - else: - import os - - project_db = self.db_manager.get_project_for_path(os.getcwd()) - - if not project_db: - return "Error: Could not find project database" - - except PermissionError as e: - return str(e) - except Exception as e: - return f"Error accessing project database: {str(e)}" - - await tool_ctx.info( - f"Getting statistics for project: {project_db.project_path}" - ) - - # Collect statistics - conn = None - try: - conn = project_db.get_sqlite_connection() - cursor = conn.cursor() - - output = [] - output.append(f"=== SQLite Database Statistics ===") - output.append(f"Project: {project_db.project_path}") - output.append(f"Database: {project_db.sqlite_path}") - - # Get database size - db_size = project_db.sqlite_path.stat().st_size - output.append(f"Database Size: {self._format_size(db_size)}") - output.append("") - - # Get table statistics - cursor.execute( - "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name" - ) - tables = cursor.fetchall() - - output.append("=== Tables ===") - total_rows = 0 - - for (table_name,) in tables: - if table_name.startswith("sqlite_"): - continue - - # Get row count - cursor.execute(f"SELECT COUNT(*) FROM {table_name}") - row_count = cursor.fetchone()[0] - total_rows += row_count - - # Get table info - cursor.execute(f"PRAGMA table_info({table_name})") - columns = cursor.fetchall() - col_count = len(columns) - - output.append(f"\n{table_name}:") - output.append(f" Rows: {row_count:,}") - output.append(f" Columns: {col_count}") - - if detailed and row_count > 0: - # Show column details - output.append(" Columns:") - for col in columns: - col_name = col[1] - col_type = col[2] - is_pk = col[5] - not_null = col[3] - - flags = [] - if is_pk: - flags.append("PRIMARY KEY") - if not_null: - flags.append("NOT NULL") - - flag_str = f" ({', '.join(flags)})" if flags else "" - output.append(f" - {col_name}: {col_type}{flag_str}") - - # Show sample data for specific tables - if table_name == "files" and row_count > 0: - cursor.execute( - f"SELECT COUNT(DISTINCT SUBSTR(path, -3)) as ext_count FROM {table_name}" - ) - ext_count = cursor.fetchone()[0] - output.append(f" File types: ~{ext_count}") - - elif table_name == "symbols" and row_count > 0: - cursor.execute( - f"SELECT type, COUNT(*) as count FROM {table_name} GROUP BY type ORDER BY count DESC LIMIT 5" - ) - symbol_types = cursor.fetchall() - output.append(" Symbol types:") - for sym_type, count in symbol_types: - output.append(f" - {sym_type}: {count}") - - # Get indexes - cursor.execute(f"PRAGMA index_list({table_name})") - indexes = cursor.fetchall() - if indexes: - output.append(f" Indexes: {len(indexes)}") - - output.append(f"\nTotal Rows: {total_rows:,}") - - # Get index statistics - cursor.execute( - "SELECT name FROM sqlite_master WHERE type='index' AND sql IS NOT NULL ORDER BY name" - ) - indexes = cursor.fetchall() - if indexes: - output.append(f"\n=== Indexes ===") - output.append(f"Total Indexes: {len(indexes)}") - - if detailed: - for (idx_name,) in indexes: - cursor.execute(f"PRAGMA index_info({idx_name})") - idx_info = cursor.fetchall() - if idx_info: - cols = [info[2] for info in idx_info] - output.append(f" {idx_name}: ({', '.join(cols)})") - - # Database properties - if detailed: - output.append("\n=== Database Properties ===") - - # Page size - cursor.execute("PRAGMA page_size") - page_size = cursor.fetchone()[0] - output.append(f"Page Size: {page_size:,} bytes") - - # Page count - cursor.execute("PRAGMA page_count") - page_count = cursor.fetchone()[0] - output.append(f"Page Count: {page_count:,}") - - # Cache size - cursor.execute("PRAGMA cache_size") - cache_size = cursor.fetchone()[0] - output.append(f"Cache Size: {abs(cache_size):,} pages") - - return "\n".join(output) - - except sqlite3.Error as e: - await tool_ctx.error(f"SQL error: {str(e)}") - return f"SQL error: {str(e)}" - except Exception as e: - await tool_ctx.error(f"Unexpected error: {str(e)}") - return f"Error getting statistics: {str(e)}" - finally: - if conn: - conn.close() - - def _format_size(self, size: int) -> str: - """Format file size in human-readable format.""" - for unit in ["B", "KB", "MB", "GB"]: - if size < 1024.0: - return f"{size:.1f} {unit}" - size /= 1024.0 - return f"{size:.1f} TB" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-database/pyproject.toml b/pkg/hanzo-tools-database/pyproject.toml deleted file mode 100644 index 67f22202c..000000000 --- a/pkg/hanzo-tools-database/pyproject.toml +++ /dev/null @@ -1,48 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-database" -version = "0.2.0" -description = "Hybrid memory management for Hanzo AI - Markdown files, SQLite FTS, Vector search" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -keywords = ["hanzo", "tools", "database", "sql", "graph", "memory", "markdown", "sqlite", "vector", "mcp", "ai"] -dependencies = [ - "hanzo-tools>=0.3.0", - "mcp>=1.25.0", - "fastmcp>=2.14.1", - "pydantic>=2.12.5", -] - -[project.optional-dependencies] -full = [ - "networkx>=3.0", - "aiosqlite>=0.21.0", -] -vector = [ - # sqlite-vec extension (binary, installed separately) - # Run: python setup_sqlite_vec.py to install -] -dev = ["pytest>=7.0.0", "ruff>=0.14.0"] - -[project.urls] -"Homepage" = "https://github.com/hanzoai/python-sdk" - -[project.entry-points."hanzo.tools"] -database = "hanzo_tools.database:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] - -[tool.setuptools.package-data] -hanzo_tools = ["py.typed"] diff --git a/pkg/hanzo-tools-database/setup_sqlite_vec.py b/pkg/hanzo-tools-database/setup_sqlite_vec.py deleted file mode 100755 index 28902be88..000000000 --- a/pkg/hanzo-tools-database/setup_sqlite_vec.py +++ /dev/null @@ -1,151 +0,0 @@ -#!/usr/bin/env python3 -"""Setup script for sqlite-vec extension. - -Downloads and installs sqlite-vec for vector search capabilities. -""" - -import os -import sys -import sqlite3 -import platform -import urllib.request -from pathlib import Path - - -def get_sqlite_vec_url(): - """Get the appropriate sqlite-vec download URL for this platform.""" - system = platform.system().lower() - machine = platform.machine().lower() - - # Map platform to sqlite-vec release names - if system == "darwin": - if machine in ("x86_64", "amd64"): - return "https://github.com/asg017/sqlite-vec/releases/latest/download/sqlite-vec-v0.1.0-alpha.7-macos-x86_64.tar.gz" - elif machine in ("arm64", "aarch64"): - return "https://github.com/asg017/sqlite-vec/releases/latest/download/sqlite-vec-v0.1.0-alpha.7-macos-aarch64.tar.gz" - elif system == "linux": - if machine in ("x86_64", "amd64"): - return "https://github.com/asg017/sqlite-vec/releases/latest/download/sqlite-vec-v0.1.0-alpha.7-linux-x86_64.tar.gz" - elif machine in ("arm64", "aarch64"): - return "https://github.com/asg017/sqlite-vec/releases/latest/download/sqlite-vec-v0.1.0-alpha.7-linux-aarch64.tar.gz" - elif system == "windows": - return "https://github.com/asg017/sqlite-vec/releases/latest/download/sqlite-vec-v0.1.0-alpha.7-windows-x86_64.zip" - - raise RuntimeError(f"Unsupported platform: {system} {machine}") - - -def download_sqlite_vec(): - """Download and extract sqlite-vec extension.""" - try: - url = get_sqlite_vec_url() - print(f"Downloading sqlite-vec from: {url}") - - # Create extension directory - ext_dir = Path(__file__).parent / "extensions" - ext_dir.mkdir(exist_ok=True) - - # Download - filename = url.split("/")[-1] - local_path = ext_dir / filename - - urllib.request.urlretrieve(url, local_path) # noqa: S310 - print(f"Downloaded: {local_path}") - - # Extract - if filename.endswith(".tar.gz"): - import tarfile - - with tarfile.open(local_path, "r:gz") as tar: - tar.extractall(ext_dir) # noqa: S202 - elif filename.endswith(".zip"): - import zipfile - - with zipfile.ZipFile(local_path, "r") as zip_ref: - zip_ref.extractall(ext_dir) # noqa: S202 - - # Find the extension file - for ext_file in ext_dir.rglob("vec0.*"): - if ext_file.suffix in (".so", ".dll", ".dylib"): - print(f"Found extension: {ext_file}") - return ext_file - - raise RuntimeError("Could not find vec0 extension after extraction") - - except Exception as e: - print(f"Failed to download sqlite-vec: {e}") - return None - - -def test_sqlite_vec(extension_path): - """Test if sqlite-vec extension works.""" - try: - conn = sqlite3.connect(":memory:") - conn.enable_load_extension(True) - conn.load_extension(str(extension_path)) - - # Test basic functionality - conn.execute("CREATE VIRTUAL TABLE test_vec USING vec0(embedding float[3])") - conn.execute("INSERT INTO test_vec (embedding) VALUES ('[1,2,3]')") - result = conn.execute("SELECT * FROM test_vec").fetchone() - - conn.close() - - if result: - print("โœ“ sqlite-vec extension is working correctly") - return True - else: - print("โœ— sqlite-vec extension test failed") - return False - - except Exception as e: - print(f"โœ— sqlite-vec extension test failed: {e}") - return False - - -def install_sqlite_vec(): - """Install sqlite-vec extension.""" - print("Setting up sqlite-vec extension for vector search...") - - # Check if already available - try: - conn = sqlite3.connect(":memory:") - conn.enable_load_extension(True) - conn.load_extension("vec0") - conn.close() - print("โœ“ sqlite-vec extension is already available") - return True - except Exception: - pass # Extension not available, will download - - # Download and install - extension_path = download_sqlite_vec() - if extension_path and test_sqlite_vec(extension_path): - # Create symlink or copy to standard location - try: - import shutil - - target_dir = Path.home() / ".hanzo" / "extensions" - target_dir.mkdir(parents=True, exist_ok=True) - target_path = target_dir / extension_path.name - - shutil.copy2(extension_path, target_path) - print(f"โœ“ Installed sqlite-vec to: {target_path}") - - # Add to environment - print("\nTo use sqlite-vec, add this to your environment:") - print(f"export SQLITE_VEC_PATH={target_path}") - - return True - except Exception as e: - print(f"Warning: Failed to install extension: {e}") - print(f"You can manually use: {extension_path}") - return False - else: - print("โœ— Failed to install sqlite-vec extension") - print("Vector search will not be available") - return False - - -if __name__ == "__main__": - success = install_sqlite_vec() - sys.exit(0 if success else 1) diff --git a/pkg/hanzo-tools-database/test_memory_system.py b/pkg/hanzo-tools-database/test_memory_system.py deleted file mode 100755 index 6e139b380..000000000 --- a/pkg/hanzo-tools-database/test_memory_system.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env python3 -"""Test script for hybrid memory system.""" - -import os -import sys -import asyncio -from pathlib import Path - -# Add the tools package to Python path -sys.path.insert(0, str(Path(__file__).parent / "hanzo_tools")) - -from hanzo_tools.core import PermissionManager -from hanzo_tools.database.memory_tool import MemoryTool -from hanzo_tools.database.memory_manager import MemoryManager - - -async def test_memory_system(): - """Test the hybrid memory system.""" - print("๐Ÿง  Testing Hybrid Memory System") - print("=" * 50) - - # Create permission manager (allow all for testing) - permission_manager = PermissionManager(allowed_paths=["/"]) - - # Create memory manager - memory_manager = MemoryManager(permission_manager) - - # Create memory tool - memory_tool = MemoryTool(permission_manager) - - # Test project path - test_project = Path("/tmp/test_hanzo_project") - test_project.mkdir(exist_ok=True) - os.chdir(test_project) - - print(f"๐Ÿ“ Test project: {test_project}") - - # Mock MCP context (simplified) - class MockContext: - pass - - ctx = MockContext() - - # Test 1: Initialize memory structure - print("\n1. ๐Ÿ“‹ Initialize memory structure...") - from hanzo_tools.database.init_memory import init_global_memory, init_project_memory - - try: - init_global_memory() - init_project_memory(str(test_project)) - print("โœ“ Memory structure initialized") - except Exception as e: - print(f"โœ— Initialization failed: {e}") - - # Test 2: Write memory files - print("\n2. โœ๏ธ Write memory files...") - - try: - # Global memory - result = await memory_tool.call( - ctx, - action="write", - file_path="test_rules.md", - content="# Test Rules\n\nThis is a test rule file.", - scope="global", - category="test", - ) - print(f"โœ“ Global write: {result}") - - # Project memory - result = await memory_tool.call( - ctx, - action="write", - file_path="test_architecture.md", - content="# Test Architecture\n\nThis describes the test architecture.", - scope="project", - category="architecture", - ) - print(f"โœ“ Project write: {result}") - - except Exception as e: - print(f"โœ— Write failed: {e}") - - # Test 3: Read memory files - print("\n3. ๐Ÿ“– Read memory files...") - - try: - result = await memory_tool.call( - ctx, action="read", file_path="test_rules.md", scope="global" - ) - print("โœ“ Global read successful") - print(f"Content preview: {result[:100]}...") - - result = await memory_tool.call( - ctx, action="read", file_path="test_architecture.md", scope="project" - ) - print("โœ“ Project read successful") - print(f"Content preview: {result[:100]}...") - - except Exception as e: - print(f"โœ— Read failed: {e}") - - # Test 4: Append to memory files - print("\n4. โž• Append to memory files...") - - try: - result = await memory_tool.call( - ctx, - action="append", - file_path="sessions/test_session.md", - content="This is a test session entry with important insights.", - scope="project", - category="session", - ) - print(f"โœ“ Append: {result}") - except Exception as e: - print(f"โœ— Append failed: {e}") - - # Test 5: Create structured memories - print("\n5. ๐Ÿ—ƒ๏ธ Create structured memories...") - - try: - result = await memory_tool.call( - ctx, - action="create", - content="This is a test structured memory with high importance", - category="test", - importance=8, - scope="project", - ) - print(f"โœ“ Create structured: {result}") - except Exception as e: - print(f"โœ— Create failed: {e}") - - # Test 6: Search memories - print("\n6. ๐Ÿ” Search memories...") - - try: - result = await memory_tool.call( - ctx, action="search", content="test", scope="both", limit=5 - ) - print("โœ“ Search successful") - print(f"Results preview: {result[:300]}...") - except Exception as e: - print(f"โœ— Search failed: {e}") - - # Test 7: List memory files - print("\n7. ๐Ÿ“‹ List memory files...") - - try: - result = await memory_tool.call(ctx, action="list", scope="both") - print("โœ“ List successful") - print(f"List preview: {result[:200]}...") - except Exception as e: - print(f"โœ— List failed: {e}") - - # Test 8: Get statistics - print("\n8. ๐Ÿ“Š Get memory statistics...") - - try: - result = await memory_tool.call(ctx, action="stats", scope="both") - print("โœ“ Stats successful") - print(f"Stats preview: {result[:300]}...") - except Exception as e: - print(f"โœ— Stats failed: {e}") - - # Test 9: Test database features - print("\n9. ๐Ÿ—„๏ธ Test database features...") - - try: - # Test FTS5 search - results = memory_manager.search_memories( - "test architecture", "both", str(test_project) - ) - print(f"โœ“ FTS5 search found {len(results)} results") - - # Get stats - stats = memory_manager.get_memory_stats("both", str(test_project)) - print( - f"โœ“ Database stats: {stats['markdown_files']} markdown files, {stats['structured_memories']} memories" - ) - - # Test sqlite-vec availability - try: - import sqlite3 - - conn = sqlite3.connect(":memory:") - conn.enable_load_extension(True) - conn.load_extension("vec0") - conn.close() - print("โœ“ sqlite-vec extension available") - except Exception: - print("โš ๏ธ sqlite-vec extension not available (optional)") - - except Exception as e: - print(f"โœ— Database test failed: {e}") - - print("\n" + "=" * 50) - print("๐ŸŽ‰ Memory system test completed!") - - # Cleanup - import shutil - - try: - shutil.rmtree(test_project) - print(f"๐Ÿงน Cleaned up test project: {test_project}") - except Exception: - pass - - -if __name__ == "__main__": - asyncio.run(test_memory_system()) diff --git a/pkg/hanzo-tools-database/tests/test_database_tools.py b/pkg/hanzo-tools-database/tests/test_database_tools.py deleted file mode 100644 index bf975cf47..000000000 --- a/pkg/hanzo-tools-database/tests/test_database_tools.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Tests for hanzo-tools-database.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import database - - assert database is not None - - def test_import_tools(self): - from hanzo_tools.database import TOOLS - - assert len(TOOLS) > 0 diff --git a/pkg/hanzo-tools-editor/README.md b/pkg/hanzo-tools-editor/README.md deleted file mode 100644 index 4b18cfd7f..000000000 --- a/pkg/hanzo-tools-editor/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# hanzo-tools-editor - -Neovim integration tools for Hanzo MCP. - -## Installation - -```bash -pip install hanzo-tools-editor -``` - -## Tools - -### neovim_edit - Edit Files in Neovim -```python -neovim_edit(file="/path/to/file.py", line=10) -``` - -### neovim_command - Run Neovim Commands -```python -neovim_command(command=":w") # Save -neovim_command(command=":q!") # Quit without saving -neovim_command(command=":%s/old/new/g") # Search replace -``` - -### neovim_session - Manage Sessions -```python -neovim_session(action="list") # List sessions -neovim_session(action="connect", name="main") -neovim_session(action="disconnect") -``` - -## Requirements - -- Neovim with remote plugin support -- `pynvim` package (installed automatically) - -## Configuration - -Set Neovim socket path: - -```bash -NVIM_LISTEN_ADDRESS=/tmp/nvim.sock nvim -``` - -Or connect to existing session: - -```python -neovim_session(action="connect", socket="/tmp/nvim.sock") -``` - -## License - -MIT diff --git a/pkg/hanzo-tools-editor/hanzo_tools/__init__.py b/pkg/hanzo-tools-editor/hanzo_tools/__init__.py deleted file mode 100644 index e1b06939a..000000000 --- a/pkg/hanzo-tools-editor/hanzo_tools/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Hanzo tools namespace package.""" - -import pkgutil - -__path__ = pkgutil.extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-editor/hanzo_tools/editor/__init__.py b/pkg/hanzo-tools-editor/hanzo_tools/editor/__init__.py deleted file mode 100644 index 099a9f0d5..000000000 --- a/pkg/hanzo-tools-editor/hanzo_tools/editor/__init__.py +++ /dev/null @@ -1,68 +0,0 @@ -"""Editor integration tools for Hanzo AI. - -Tools: -- neovim_edit: Edit files in Neovim -- neovim_command: Execute Neovim commands -- neovim_session: Manage Neovim sessions - -Install: - pip install hanzo-tools-editor - pip install hanzo-tools-editor[neovim] # For Neovim support - -Usage: - from hanzo_tools.editor import register_tools, TOOLS - - # Register with MCP server - register_tools(mcp_server) -""" - -from hanzo_tools.core import BaseTool, ToolRegistry - -from .neovim_edit import NeovimEditTool -from .neovim_command import NeovimCommandTool -from .neovim_session import NeovimSessionTool - -# Export list for tool discovery -TOOLS = [ - NeovimEditTool, - NeovimCommandTool, - NeovimSessionTool, -] - -__all__ = [ - "NeovimEditTool", - "NeovimCommandTool", - "NeovimSessionTool", - "register_tools", - "TOOLS", -] - - -def register_tools(mcp_server, enabled_tools: dict[str, bool] | None = None): - """Register all editor tools with the MCP server. - - Args: - mcp_server: FastMCP server instance - enabled_tools: Dict of tool_name -> enabled state - - Returns: - List of registered tool instances - """ - enabled = enabled_tools or {} - registered = [] - - for tool_class in TOOLS: - tool_name = ( - tool_class.name - if hasattr(tool_class, "name") - else tool_class.__name__.lower() - ) - if enabled.get(tool_name, True): # Enabled by default - try: - tool = tool_class() - ToolRegistry.register_tool(mcp_server, tool) - registered.append(tool) - except Exception: - pass # Tool may require optional deps - - return registered diff --git a/pkg/hanzo-tools-editor/hanzo_tools/editor/neovim_command.py b/pkg/hanzo-tools-editor/hanzo_tools/editor/neovim_command.py deleted file mode 100644 index 19c57f0d3..000000000 --- a/pkg/hanzo-tools-editor/hanzo_tools/editor/neovim_command.py +++ /dev/null @@ -1,273 +0,0 @@ -"""Execute Neovim commands and macros.""" - -import os -import shutil -import tempfile -import subprocess -from typing import List, Unpack, Optional, Annotated, TypedDict, final, override - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - PermissionManager, - auto_timeout, - create_tool_context, -) - -Command = Annotated[ - Optional[str], - Field( - description="Neovim command to execute (Ex commands like :w, :q, etc.)", - default=None, - ), -] - -Commands = Annotated[ - Optional[List[str]], - Field( - description="List of Neovim commands to execute in sequence", - default=None, - ), -] - -Macro = Annotated[ - Optional[str], - Field( - description="Vim macro to execute (e.g., 'dd' to delete line)", - default=None, - ), -] - -FilePath = Annotated[ - Optional[str], - Field( - description="File to operate on (optional, uses current buffer if not specified)", - default=None, - ), -] - -SaveAfter = Annotated[ - bool, - Field( - description="Save file after executing commands", - default=True, - ), -] - -ReturnOutput = Annotated[ - bool, - Field( - description="Return output/messages from Neovim", - default=True, - ), -] - - -class NeovimCommandParams(TypedDict, total=False): - """Parameters for Neovim command tool.""" - - command: Optional[str] - commands: Optional[List[str]] - macro: Optional[str] - file_path: Optional[str] - save_after: bool - return_output: bool - - -@final -class NeovimCommandTool(BaseTool): - """Tool for executing Neovim commands and macros.""" - - def __init__(self, permission_manager: PermissionManager): - """Initialize the Neovim command tool. - - Args: - permission_manager: Permission manager for access control - """ - self.permission_manager = permission_manager - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "neovim_command" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Execute Neovim commands and macros programmatically. - -Run Ex commands, normal mode commands, or complex macros in Neovim. -Can operate on files without opening the editor interface. - -Examples: -- neovim_command --command ":set number" --file-path main.py -- neovim_command --command ":%s/old/new/g" --file-path config.json -- neovim_command --commands ":set expandtab" ":retab" --file-path script.sh -- neovim_command --macro "ggVG=" --file-path messy.py # Format entire file -- neovim_command --macro "dd10j" --file-path list.txt # Delete line and go down 10 - -Common commands: -- :w - Save file -- :q - Quit -- :%s/old/new/g - Replace all occurrences -- :set number - Show line numbers -- :set expandtab - Use spaces instead of tabs -- :retab - Convert tabs to spaces - -Common macros: -- gg - Go to beginning of file -- G - Go to end of file -- dd - Delete line -- yy - Yank (copy) line -- p - Paste -- V - Visual line mode -- = - Format/indent - -Note: Requires Neovim to be installed. -""" - - @override - @auto_timeout("neovim_command") - async def call( - self, - ctx: MCPContext, - **params: Unpack[NeovimCommandParams], - ) -> str: - """Execute Neovim command. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Result of the command execution - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - command = params.get("command") - commands = params.get("commands") - macro = params.get("macro") - file_path = params.get("file_path") - save_after = params.get("save_after", True) - return_output = params.get("return_output", True) - - # Validate inputs - if not any([command, commands, macro]): - return "Error: Must provide either 'command', 'commands', or 'macro'" - - if sum(bool(x) for x in [command, commands, macro]) > 1: - return ( - "Error: Can only use one of 'command', 'commands', or 'macro' at a time" - ) - - # Check if Neovim is available - nvim_cmd = shutil.which("nvim") - if not nvim_cmd: - return "Error: Neovim (nvim) not found. Install it first." - - # Prepare commands list - nvim_commands = [] - - if command: - nvim_commands.append(command) - elif commands: - nvim_commands.extend(commands) - elif macro: - # Convert macro to normal mode command - # Escape special characters - escaped_macro = macro.replace('"', '\\"') - nvim_commands.append(f':normal "{escaped_macro}"') - - # Add save command if requested - if save_after: - nvim_commands.append(":w") - - # Always quit at the end - nvim_commands.append(":q") - - # Build Neovim command line - cmd = [nvim_cmd, "-n", "-i", "NONE"] # No swap file, no shada file - - # Add commands - for vim_cmd in nvim_commands: - cmd.extend(["-c", vim_cmd]) - - # Add file if specified - if file_path: - file_path = os.path.abspath(file_path) - - # Check permissions - if not self.permission_manager.is_path_allowed(file_path): - return f"Error: No permission to access {file_path}" - - if not os.path.exists(file_path): - return f"Error: File not found: {file_path}" - - cmd.append(file_path) - else: - # Create empty buffer - cmd.append("-") - - await tool_ctx.info(f"Executing Neovim commands: {nvim_commands}") - - try: - # Execute Neovim - if return_output: - # Capture output by redirecting messages - output_file = tempfile.NamedTemporaryFile(mode="w+", delete=False) - output_file.close() - - # Add command to redirect messages - cmd.insert(3, "-c") - cmd.insert(4, f":redir! > {output_file.name}") - - # Execute - result = subprocess.run(cmd, capture_output=True, text=True) - - # Read output - output_content = "" - try: - with open(output_file.name, "r") as f: - output_content = f.read().strip() - finally: - os.unlink(output_file.name) - - if result.returncode == 0: - response = "Commands executed successfully" - if file_path: - response += f" on {os.path.basename(file_path)}" - if output_content: - response += f"\n\nOutput:\n{output_content}" - return response - else: - error_msg = "Error executing Neovim commands" - if result.stderr: - error_msg += f"\n\nError:\n{result.stderr}" - if output_content: - error_msg += f"\n\nOutput:\n{output_content}" - return error_msg - else: - # Just execute without capturing output - result = subprocess.run(cmd) - - if result.returncode == 0: - response = "Commands executed successfully" - if file_path: - response += f" on {os.path.basename(file_path)}" - return response - else: - return f"Neovim exited with code {result.returncode}" - - except Exception as e: - await tool_ctx.error(f"Failed to execute Neovim commands: {str(e)}") - return f"Error executing Neovim commands: {str(e)}" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-editor/hanzo_tools/editor/neovim_edit.py b/pkg/hanzo-tools-editor/hanzo_tools/editor/neovim_edit.py deleted file mode 100644 index 53afe6900..000000000 --- a/pkg/hanzo-tools-editor/hanzo_tools/editor/neovim_edit.py +++ /dev/null @@ -1,292 +0,0 @@ -"""Open files in Neovim editor.""" - -import os -import shutil -import subprocess -from typing import Unpack, Optional, Annotated, TypedDict, final, override - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - PermissionManager, - auto_timeout, - create_tool_context, -) - -FilePath = Annotated[ - str, - Field( - description="Path to the file to open", - min_length=1, - ), -] - -LineNumber = Annotated[ - Optional[int], - Field( - description="Line number to jump to", - default=None, - ), -] - -ColumnNumber = Annotated[ - Optional[int], - Field( - description="Column number to jump to", - default=None, - ), -] - -ReadOnly = Annotated[ - bool, - Field( - description="Open file in read-only mode", - default=False, - ), -] - -Split = Annotated[ - Optional[str], - Field( - description="Split mode: vsplit, split, tab", - default=None, - ), -] - -Wait = Annotated[ - bool, - Field( - description="Wait for Neovim to exit before returning", - default=True, - ), -] - -InTerminal = Annotated[ - bool, - Field( - description="Open in terminal (requires terminal that supports it)", - default=True, - ), -] - - -class NeovimEditParams(TypedDict, total=False): - """Parameters for Neovim edit tool.""" - - file_path: str - line_number: Optional[int] - column_number: Optional[int] - read_only: bool - split: Optional[str] - wait: bool - in_terminal: bool - - -@final -class NeovimEditTool(BaseTool): - """Tool for opening files in Neovim.""" - - def __init__(self, permission_manager: PermissionManager): - """Initialize the Neovim edit tool. - - Args: - permission_manager: Permission manager for access control - """ - self.permission_manager = permission_manager - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "neovim_edit" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Open files in Neovim editor with advanced options. - -Open files at specific lines/columns, in different split modes, or read-only. -Integrates with your existing Neovim configuration. - -Examples: -- neovim_edit --file-path main.py -- neovim_edit --file-path main.py --line-number 42 -- neovim_edit --file-path main.py --line-number 42 --column-number 10 -- neovim_edit --file-path config.json --read-only -- neovim_edit --file-path test.py --split vsplit -- neovim_edit --file-path README.md --split tab - -Split modes: -- vsplit: Open in vertical split -- split: Open in horizontal split -- tab: Open in new tab - -Note: Requires Neovim to be installed and available in PATH. -""" - - @override - @auto_timeout("neovim_edit") - async def call( - self, - ctx: MCPContext, - **params: Unpack[NeovimEditParams], - ) -> str: - """Open file in Neovim. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Result of the operation - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - file_path = params.get("file_path") - if not file_path: - return "Error: file_path is required" - - line_number = params.get("line_number") - column_number = params.get("column_number") - read_only = params.get("read_only", False) - split = params.get("split") - wait = params.get("wait", True) - in_terminal = params.get("in_terminal", True) - - # Check if Neovim is available - nvim_cmd = shutil.which("nvim") - if not nvim_cmd: - # Try common locations - common_paths = [ - "/usr/local/bin/nvim", - "/usr/bin/nvim", - "/opt/homebrew/bin/nvim", - os.path.expanduser("~/.local/bin/nvim"), - ] - for path in common_paths: - if os.path.exists(path): - nvim_cmd = path - break - - if not nvim_cmd: - return """Error: Neovim (nvim) not found. Install it with: - -On macOS: -brew install neovim - -On Ubuntu/Debian: -sudo apt install neovim - -On Arch: -sudo pacman -S neovim - -Or visit: https://neovim.io/""" - - # Convert to absolute path - file_path = os.path.abspath(file_path) - - # Check permissions - if not self.permission_manager.is_path_allowed(file_path): - return f"Error: No permission to access {file_path}" - - # Build Neovim command - cmd = [nvim_cmd] - - # Add read-only flag - if read_only: - cmd.append("-R") - - # Add split mode - if split: - if split == "vsplit": - cmd.extend(["-c", "vsplit"]) - elif split == "split": - cmd.extend(["-c", "split"]) - elif split == "tab": - cmd.extend(["-c", "tabnew"]) - else: - return f"Error: Invalid split mode '{split}'. Use 'vsplit', 'split', or 'tab'" - - # Add file path - cmd.append(file_path) - - # Add line/column positioning - if line_number: - if column_number: - # Go to specific line and column - cmd.extend(["+call cursor({}, {})".format(line_number, column_number)]) - else: - # Go to specific line - cmd.append(f"+{line_number}") - - await tool_ctx.info(f"Opening {file_path} in Neovim") - - try: - # Determine how to run Neovim - if in_terminal and not wait: - # Open in a new terminal window (platform-specific) - if os.uname().sysname == "Darwin": # macOS - # Try to use iTerm2 if available, otherwise Terminal - if shutil.which("osascript"): - # Build AppleScript to open in iTerm2 or Terminal - nvim_cmd_str = " ".join(f'"{arg}"' for arg in cmd) - - # Try iTerm2 first - applescript = f"""tell application "System Events" - if exists application process "iTerm2" then - tell application "iTerm" - activate - tell current window - create tab with default profile - tell current session - write text "{nvim_cmd_str}" - end tell - end tell - end tell - else - tell application "Terminal" - activate - do script "{nvim_cmd_str}" - end tell - end if - end tell""" - - subprocess.run(["osascript", "-e", applescript], timeout=10) - return f"Opened {file_path} in Neovim (new terminal window)" - - elif shutil.which("gnome-terminal"): - # Linux with GNOME - subprocess.Popen(["gnome-terminal", "--"] + cmd) - return f"Opened {file_path} in Neovim (new terminal window)" - - elif shutil.which("xterm"): - # Fallback to xterm - subprocess.Popen(["xterm", "-e"] + cmd) - return f"Opened {file_path} in Neovim (new terminal window)" - - else: - # Can't open in terminal, fall back to subprocess - subprocess.Popen(cmd) - return f"Opened {file_path} in Neovim (background process)" - - else: - # Run and wait for completion - result = subprocess.run(cmd, timeout=120) - - if result.returncode == 0: - return f"Successfully edited {file_path} in Neovim" - else: - return f"Neovim exited with code {result.returncode}" - - except Exception as e: - await tool_ctx.error(f"Failed to open Neovim: {str(e)}") - return f"Error opening Neovim: {str(e)}" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-editor/hanzo_tools/editor/neovim_session.py b/pkg/hanzo-tools-editor/hanzo_tools/editor/neovim_session.py deleted file mode 100644 index 59ed82c5e..000000000 --- a/pkg/hanzo-tools-editor/hanzo_tools/editor/neovim_session.py +++ /dev/null @@ -1,362 +0,0 @@ -"""Manage Neovim sessions.""" - -import os -import json -import shutil -import subprocess -from typing import Unpack, Optional, Annotated, TypedDict, final, override -from pathlib import Path -from datetime import datetime - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context - -Action = Annotated[ - str, - Field( - description="Action to perform: save, restore, list, delete", - min_length=1, - ), -] - -SessionName = Annotated[ - Optional[str], - Field( - description="Name of the session", - default=None, - ), -] - -ProjectPath = Annotated[ - Optional[str], - Field( - description="Project path (defaults to current directory)", - default=None, - ), -] - -AutoName = Annotated[ - bool, - Field( - description="Auto-generate session name based on project and timestamp", - default=False, - ), -] - -Overwrite = Annotated[ - bool, - Field( - description="Overwrite existing session", - default=False, - ), -] - - -class NeovimSessionParams(TypedDict, total=False): - """Parameters for Neovim session tool.""" - - action: str - session_name: Optional[str] - project_path: Optional[str] - auto_name: bool - overwrite: bool - - -@final -class NeovimSessionTool(BaseTool): - """Tool for managing Neovim sessions.""" - - def __init__(self): - """Initialize the Neovim session tool.""" - self.session_dir = Path.home() / ".hanzo" / "neovim" / "sessions" - self.session_dir.mkdir(parents=True, exist_ok=True) - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "neovim_session" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Save and restore Neovim editing sessions. - -Manage Neovim sessions to save your workspace state including: -- Open files and buffers -- Window layouts and splits -- Cursor positions -- Marks and registers -- Local options and mappings - -Actions: -- save: Save current Neovim session -- restore: Restore a saved session -- list: List all saved sessions -- delete: Delete a saved session - -Examples: -- neovim_session --action save --session-name "feature-work" -- neovim_session --action save --auto-name # Auto-generate name -- neovim_session --action restore --session-name "feature-work" -- neovim_session --action list -- neovim_session --action list --project-path /path/to/project -- neovim_session --action delete --session-name "old-session" - -Sessions are stored in ~/.hanzo/neovim/sessions/ -Project-specific sessions are automatically organized by project path. - -Note: Requires Neovim to be installed. -""" - - @override - @auto_timeout("neovim_session") - async def call( - self, - ctx: MCPContext, - **params: Unpack[NeovimSessionParams], - ) -> str: - """Manage Neovim session. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Result of the session operation - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - action = params.get("action") - if not action: - return "Error: action is required (save, restore, list, delete)" - - session_name = params.get("session_name") - project_path = params.get("project_path") or os.getcwd() - auto_name = params.get("auto_name", False) - overwrite = params.get("overwrite", False) - - # Validate action - valid_actions = ["save", "restore", "list", "delete"] - if action not in valid_actions: - return f"Error: Invalid action '{action}'. Must be one of: {', '.join(valid_actions)}" - - # Check if Neovim is available - nvim_cmd = shutil.which("nvim") - if not nvim_cmd and action in ["save", "restore"]: - return "Error: Neovim (nvim) not found. Install it first." - - # Get project-specific session directory - project_hash = str(hash(os.path.abspath(project_path)) % 10**8) - project_name = os.path.basename(project_path) or "root" - project_session_dir = self.session_dir / f"{project_name}_{project_hash}" - project_session_dir.mkdir(exist_ok=True) - - # Handle different actions - if action == "save": - return await self._save_session( - tool_ctx, - session_name, - project_session_dir, - auto_name, - overwrite, - project_path, - ) - elif action == "restore": - return await self._restore_session( - tool_ctx, session_name, project_session_dir - ) - elif action == "list": - return self._list_sessions(project_session_dir, project_path) - elif action == "delete": - return self._delete_session(session_name, project_session_dir) - - async def _save_session( - self, - tool_ctx, - session_name: Optional[str], - project_dir: Path, - auto_name: bool, - overwrite: bool, - project_path: str, - ) -> str: - """Save Neovim session.""" - # Generate session name if needed - if auto_name or not session_name: - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - session_name = f"session_{timestamp}" - - # Sanitize session name - session_name = session_name.replace("/", "_").replace(" ", "_") - - session_file = project_dir / f"{session_name}.vim" - metadata_file = project_dir / f"{session_name}.json" - - # Check if exists - if session_file.exists() and not overwrite: - return f"Error: Session '{session_name}' already exists. Use --overwrite to replace." - - await tool_ctx.info(f"Saving Neovim session: {session_name}") - - # Create temporary vim script to save session - vim_script = f""" -:mksession! {session_file} -:echo "Session saved to {session_file}" -:qa -""" - - try: - # Run Neovim to save session - # First, check if Neovim is already running - # For now, we'll create a new instance - result = subprocess.run( - ["nvim", "-c", vim_script.strip()], capture_output=True, text=True - ) - - if result.returncode != 0 and result.stderr: - return f"Error saving session: {result.stderr}" - - # Save metadata - metadata = { - "name": session_name, - "created_at": datetime.now().isoformat(), - "project_path": project_path, - "description": f"Neovim session for {project_path}", - } - - with open(metadata_file, "w") as f: - json.dump(metadata, f, indent=2) - - return f"""Successfully saved Neovim session '{session_name}' - -Session file: {session_file} -Project: {project_path} - -To restore this session: -neovim_session --action restore --session-name "{session_name}" - -Or manually in Neovim: -:source {session_file}""" - - except Exception as e: - return f"Error saving session: {str(e)}" - - async def _restore_session( - self, tool_ctx, session_name: Optional[str], project_dir: Path - ) -> str: - """Restore Neovim session.""" - if not session_name: - # List available sessions - sessions = list(project_dir.glob("*.vim")) - if not sessions: - return "Error: No sessions found for this project. Use 'neovim_session --action list' to see all sessions." - - # Use most recent - sessions.sort(key=lambda x: x.stat().st_mtime, reverse=True) - session_file = sessions[0] - session_name = session_file.stem - else: - session_file = project_dir / f"{session_name}.vim" - if not session_file.exists(): - return f"Error: Session '{session_name}' not found. Use 'neovim_session --action list' to see available sessions." - - await tool_ctx.info(f"Restoring Neovim session: {session_name}") - - try: - # Open Neovim with the session - subprocess.run(["nvim", "-S", str(session_file)]) - - return f"Restored Neovim session '{session_name}'" - - except Exception as e: - return f"Error restoring session: {str(e)}" - - def _list_sessions(self, project_dir: Path, project_path: str) -> str: - """List available sessions.""" - output = ["=== Neovim Sessions ==="] - output.append(f"Project: {project_path}\n") - - # List project-specific sessions - sessions = list(project_dir.glob("*.vim")) - - if sessions: - output.append("Project Sessions:") - sessions.sort(key=lambda x: x.stat().st_mtime, reverse=True) - - for session_file in sessions: - session_name = session_file.stem - metadata_file = project_dir / f"{session_name}.json" - - # Get metadata if available - created_at = "Unknown" - if metadata_file.exists(): - try: - with open(metadata_file, "r") as f: - metadata = json.load(f) - created_at = metadata.get("created_at", "Unknown") - if created_at != "Unknown": - # Format date - dt = datetime.fromisoformat(created_at) - created_at = dt.strftime("%Y-%m-%d %H:%M:%S") - except Exception: - pass - - # Get file size - size = session_file.stat().st_size - size_kb = size / 1024 - - output.append(f" - {session_name}") - output.append(f" Created: {created_at}") - output.append(f" Size: {size_kb:.1f} KB") - else: - output.append("No sessions found for this project.") - - # Also list all sessions - all_sessions = list(self.session_dir.rglob("*.vim")) - other_sessions = [s for s in all_sessions if s.parent != project_dir] - - if other_sessions: - output.append("\nOther Projects' Sessions:") - for session_file in other_sessions[:10]: # Show max 10 - project_name = session_file.parent.name - session_name = session_file.stem - output.append(f" - {project_name}/{session_name}") - - if len(other_sessions) > 10: - output.append(f" ... and {len(other_sessions) - 10} more") - - output.append( - "\nUse 'neovim_session --action restore --session-name ' to restore a session." - ) - - return "\n".join(output) - - def _delete_session(self, session_name: Optional[str], project_dir: Path) -> str: - """Delete a session.""" - if not session_name: - return "Error: session_name is required for delete action" - - session_file = project_dir / f"{session_name}.vim" - metadata_file = project_dir / f"{session_name}.json" - - if not session_file.exists(): - return f"Error: Session '{session_name}' not found" - - try: - session_file.unlink() - if metadata_file.exists(): - metadata_file.unlink() - - return f"Successfully deleted session '{session_name}'" - - except Exception as e: - return f"Error deleting session: {str(e)}" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-editor/pyproject.toml b/pkg/hanzo-tools-editor/pyproject.toml deleted file mode 100644 index c30c1ca68..000000000 --- a/pkg/hanzo-tools-editor/pyproject.toml +++ /dev/null @@ -1,41 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-editor" -version = "0.2.0" -description = "Editor integration tools for Hanzo AI - Neovim, VSCode, etc." -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -keywords = ["hanzo", "tools", "editor", "neovim", "vscode", "mcp", "ai"] -dependencies = [ - "hanzo-tools>=0.3.0", - "mcp>=1.25.0", - "fastmcp>=2.14.1", - "pydantic>=2.12.5", -] - -[project.optional-dependencies] -neovim = ["pynvim>=0.5.0"] -dev = ["pytest>=7.0.0", "ruff>=0.14.0"] - -[project.urls] -"Homepage" = "https://github.com/hanzoai/python-sdk" - -[project.entry-points."hanzo.tools"] -editor = "hanzo_tools.editor:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] - -[tool.setuptools.package-data] -hanzo_tools = ["py.typed"] diff --git a/pkg/hanzo-tools-editor/tests/test_editor_tools.py b/pkg/hanzo-tools-editor/tests/test_editor_tools.py deleted file mode 100644 index 3f286789e..000000000 --- a/pkg/hanzo-tools-editor/tests/test_editor_tools.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Tests for hanzo-tools-editor.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import editor - - assert editor is not None - - def test_import_tools(self): - from hanzo_tools.editor import TOOLS - - assert len(TOOLS) > 0 diff --git a/pkg/hanzo-tools-fs/README.md b/pkg/hanzo-tools-fs/README.md deleted file mode 100644 index c29d76c9e..000000000 --- a/pkg/hanzo-tools-fs/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# hanzo-tools-fs - -Filesystem tools for Hanzo MCP. - -## Installation - -```bash -pip install hanzo-tools-fs -``` - -## Tools - -### read - Read Files -```python -read(file_path="/path/to/file.py") -read(file_path="/path/to/file.py", offset=100, limit=50) -``` - -### write - Write Files -```python -write(file_path="/path/to/file.py", content="...") -``` - -### edit - Edit Files -```python -edit( - file_path="/path/to/file.py", - old_string="def old():", - new_string="def new():" -) -``` - -### tree - Directory Structure -```python -tree(path="/project", depth=3) -``` - -### find - File Discovery -```python -find(pattern="*.py", path="/project") -``` - -### search - Content Search -```python -search(pattern="TODO|FIXME", path="./src") -``` - -### ast - Code Structure -```python -ast(pattern="class.*Service", path="/src") -``` - -## License - -MIT diff --git a/pkg/hanzo-tools-fs/hanzo_tools/__init__.py b/pkg/hanzo-tools-fs/hanzo_tools/__init__.py deleted file mode 100644 index f4f8ea812..000000000 --- a/pkg/hanzo-tools-fs/hanzo_tools/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Hanzo Tools namespace package.""" - -__path__ = __import__("pkgutil").extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-fs/hanzo_tools/fs/__init__.py b/pkg/hanzo-tools-fs/hanzo_tools/fs/__init__.py deleted file mode 100644 index 5a7691fa0..000000000 --- a/pkg/hanzo-tools-fs/hanzo_tools/fs/__init__.py +++ /dev/null @@ -1,84 +0,0 @@ -"""Filesystem tools for Hanzo AI (HIP-0300). - -The MCP wire surface is a single action-routed tool: `fs`. - - fs.read fs.write fs.stat fs.list - fs.apply_patch fs.search_text - fs.mkdir fs.rm - -`apply_patch` is the only way to edit an existing file and requires -`base_hash` from a prior `read` so stale edits are impossible. - -The per-action classes (ReadTool, WriteTool, โ€ฆ) are kept importable for -in-process consumers that need read-only sandboxes โ€” they are NOT -registered with the MCP server. The wire contract is `fs` only. -""" - -from hanzo_tools.fs.ast import ASTTool -from hanzo_tools.fs.edit import EditTool -from hanzo_tools.fs.find import FindTool -from hanzo_tools.fs.fs_tool import FsTool, fs_tool -from hanzo_tools.fs.read import ReadTool -from hanzo_tools.fs.search import SearchTool -from hanzo_tools.fs.tree import TreeTool -from hanzo_tools.fs.write import WriteTool - -# HIP-0300 wire surface โ€” what entry-point discovery registers with MCP. -TOOLS: list[type] = [FsTool] - -# In-process read-only set, used by sandboxed agents (e.g. swarm subagents). -# NOT on the MCP wire. -READ_ONLY_TOOLS: list[type] = [ReadTool, TreeTool, FindTool, SearchTool, ASTTool] - -__all__ = [ - "FsTool", - "fs_tool", - "ReadTool", - "WriteTool", - "EditTool", - "TreeTool", - "FindTool", - "SearchTool", - "ASTTool", - "READ_ONLY_TOOLS", - "TOOLS", - "register_tools", - "get_read_only_filesystem_tools", -] - - -def get_read_only_filesystem_tools(permission_manager) -> list: - """Instantiate the read-only filesystem tool set for sandboxed agents. - - These are NOT exposed on the MCP wire โ€” they're used in-process by - agent runtimes that need to grant a sub-agent read-only file access - without giving it `fs.write` / `fs.apply_patch` / `fs.rm`. - """ - out = [] - for cls in READ_ONLY_TOOLS: - try: - out.append(cls(permission_manager)) - except TypeError: - out.append(cls()) - return out - - -def register_tools( - mcp_server, permission_manager, enabled_tools: dict[str, bool] | None = None -): - """Register the unified `fs` tool with the MCP server.""" - from hanzo_tools.core import ToolRegistry - - enabled = enabled_tools or {} - registered = [] - for tool_class in TOOLS: - name = getattr(tool_class, "name", tool_class.__name__.lower()) - if not enabled.get(name, True): - continue - try: - tool = tool_class(permission_manager) - except TypeError: - tool = tool_class() - ToolRegistry.register_tool(mcp_server, tool) - registered.append(tool) - return registered diff --git a/pkg/hanzo-tools-fs/hanzo_tools/fs/ast.py b/pkg/hanzo-tools-fs/hanzo_tools/fs/ast.py deleted file mode 100644 index 121401774..000000000 --- a/pkg/hanzo-tools-fs/hanzo_tools/fs/ast.py +++ /dev/null @@ -1,294 +0,0 @@ -"""AST-based code structure search using tree-sitter. - -This module provides the ASTTool for searching, indexing, and querying code symbols -using tree-sitter AST parsing. It can find function definitions, class declarations, -and other code structures with full context. -""" - -import os -from typing import Unpack, Annotated, TypedDict, final, override -from pathlib import Path - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool - -# Lazy import for grep_ast -_tree_context_cls = None - - -def _get_tree_context(): - """Lazy load TreeContext to avoid import-time overhead.""" - global _tree_context_cls - if _tree_context_cls is None: - from grep_ast.grep_ast import TreeContext - - _tree_context_cls = TreeContext - return _tree_context_cls - - -# Type annotations for parameters -Pattern = Annotated[ - str, - Field( - description="The regex pattern to search for in source code files", - min_length=1, - ), -] - -SearchPath = Annotated[ - str, - Field( - description="The path to search in (file or directory)", - min_length=1, - ), -] - -IgnoreCase = Annotated[ - bool, - Field( - description="Whether to ignore case when matching", - default=False, - ), -] - -LineNumber = Annotated[ - bool, - Field( - description="Whether to display line numbers", - default=False, - ), -] - - -class ASTToolParams(TypedDict, total=False): - """Parameters for the ASTTool. - - Attributes: - pattern: The regex pattern to search for in source code files - path: The path to search in (file or directory) - ignore_case: Whether to ignore case when matching - line_number: Whether to display line numbers - """ - - pattern: Pattern - path: SearchPath - ignore_case: IgnoreCase - line_number: LineNumber - - -# Extensions supported by tree-sitter (common programming languages) -SUPPORTED_EXTENSIONS = { - ".py", - ".pyw", # Python - ".js", - ".jsx", - ".mjs", - ".cjs", # JavaScript - ".ts", - ".tsx", - ".mts", - ".cts", # TypeScript - ".go", # Go - ".rs", # Rust - ".c", - ".h", # C - ".cpp", - ".cc", - ".cxx", - ".hpp", - ".hh", - ".hxx", # C++ - ".java", # Java - ".rb", # Ruby - ".php", # PHP - ".cs", # C# - ".swift", # Swift - ".kt", - ".kts", # Kotlin - ".scala", # Scala - ".lua", # Lua - ".r", - ".R", # R - ".jl", # Julia - ".ex", - ".exs", # Elixir - ".erl", - ".hrl", # Erlang - ".ml", - ".mli", # OCaml - ".hs", # Haskell - ".elm", # Elm - ".vue", # Vue - ".svelte", # Svelte -} - - -@final -class ASTTool(BaseTool): - """Tool for searching and querying code structures using tree-sitter AST parsing.""" - - name = "ast" - - @property - @override - def description(self) -> str: - """Get the tool description. - - Returns: - Tool description - """ - return """AST-based code structure search using tree-sitter. Find functions, classes, methods with full context. - -Usage: -ast "function_name" ./src -ast "class.*Service" ./src -ast "def test_" ./tests - -Searches code structure intelligently, understanding syntax and providing semantic context.""" - - def _is_supported_file(self, path: str) -> bool: - """Check if file has a supported extension for tree-sitter parsing.""" - return Path(path).suffix.lower() in SUPPORTED_EXTENSIONS - - @override - async def call( - self, - ctx: MCPContext, - **params: Unpack[ASTToolParams], - ) -> str: - """Execute the tool with the given parameters. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Tool result - """ - # Extract parameters - pattern: str = params["pattern"] - path: str = params["path"] - ignore_case = params.get("ignore_case", False) - line_number = params.get("line_number", False) - - # Expand ~ in path - path = os.path.expanduser(path) - - # Check if path exists - path_obj = Path(path) - if not path_obj.exists(): - return f"Error: Path does not exist: {path}" - - # Get the files to process - files_to_process = [] - - if path_obj.is_file(): - if self._is_supported_file(str(path_obj)): - files_to_process.append(str(path_obj)) - else: - return ( - f"Error: File type not supported for AST parsing: {path_obj.suffix}" - ) - elif path_obj.is_dir(): - for root, _, files in os.walk(path_obj): - # Skip hidden directories and common non-code directories - root_path = Path(root) - if any(part.startswith(".") for part in root_path.parts): - continue - if any( - part - in ("node_modules", "__pycache__", "venv", ".venv", "dist", "build") - for part in root_path.parts - ): - continue - - for file in files: - file_path = Path(root) / file - if self._is_supported_file(str(file_path)): - files_to_process.append(str(file_path)) - - if not files_to_process: - return f"No source code files found in {path}" - - # Get TreeContext class - TreeContext = _get_tree_context() - - # Process each file - results = [] - errors = [] - - for file_path in files_to_process: - try: - # Read the file - with open(file_path, "r", encoding="utf-8") as f: - code = f.read() - - # Process the file with grep-ast - try: - tc = TreeContext( - file_path, - code, - color=False, - verbose=False, - line_number=line_number, - ) - - # Find matches - loi = tc.grep(pattern, ignore_case) - - if loi: - tc.add_lines_of_interest(loi) - tc.add_context() - output = tc.format() - - # Add the result to our list - results.append(f"\n{file_path}:\n{output}\n") - except Exception as e: - # Skip files that can't be parsed by tree-sitter - errors.append(f"Could not parse {file_path}: {str(e)}") - except UnicodeDecodeError: - errors.append(f"Could not read {file_path} as text") - except Exception as e: - errors.append(f"Error processing {file_path}: {str(e)}") - - if not results: - error_info = "" - if errors: - error_info = f"\n\nErrors encountered:\n" + "\n".join(errors[:5]) - if len(errors) > 5: - error_info += f"\n... and {len(errors) - 5} more errors" - return f"No matches found for '{pattern}' in {path}{error_info}" - - summary = f"Found matches in {len(results)} file(s) (searched {len(files_to_process)} files)" - return summary + "\n" + "".join(results) - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server. - - Creates a wrapper function with explicitly defined parameters that match - the tool's parameter schema and registers it with the MCP server. - - Args: - mcp_server: The FastMCP server instance - """ - tool_self = self # Create a reference to self for use in the closure - - @mcp_server.tool(name=self.name, description=self.description) - async def ast( - ctx: MCPContext, - pattern: Pattern, - path: SearchPath, - ignore_case: IgnoreCase = False, - line_number: LineNumber = False, - ) -> str: - return await tool_self.call( - ctx, - pattern=pattern, - path=path, - ignore_case=ignore_case, - line_number=line_number, - ) diff --git a/pkg/hanzo-tools-fs/hanzo_tools/fs/edit.py b/pkg/hanzo-tools-fs/hanzo_tools/fs/edit.py deleted file mode 100644 index 19967f5e5..000000000 --- a/pkg/hanzo-tools-fs/hanzo_tools/fs/edit.py +++ /dev/null @@ -1,110 +0,0 @@ -"""Edit tool - find and replace in files.""" - -from typing import Optional, Annotated -from pathlib import Path - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import FileSystemTool, PermissionManager, auto_timeout - - -class EditTool(FileSystemTool): - """Edit files with find and replace.""" - - name = "edit" - - @property - def description(self) -> str: - return """Edit a file by replacing text. - -Args: - file_path: Absolute path to the file - old_string: Text to find (must be unique) - new_string: Text to replace with - expected_replacements: Expected number of replacements (default 1) - -Returns: - Success message with diff or error -""" - - def __init__(self, permission_manager: Optional[PermissionManager] = None): - super().__init__(permission_manager) - - @auto_timeout("edit") - async def call( - self, - ctx: MCPContext, - file_path: str, - old_string: str, - new_string: str, - expected_replacements: int = 1, - **kwargs, - ) -> str: - """Edit file with find/replace.""" - validation = self.validate_path(file_path) - if not validation: - return validation.error_message - - if not self.is_path_allowed(file_path): - return f"Error: Access denied to path: {file_path}" - - path = Path(file_path) - - if not path.exists(): - return f"Error: File does not exist: {file_path}" - - if old_string == new_string: - return "Error: old_string and new_string are identical" - - try: - with open(path, "r", encoding="utf-8") as f: - content = f.read() - - # Count occurrences - count = content.count(old_string) - - if count == 0: - return f"Error: old_string not found in file" - - if count != expected_replacements: - return ( - f"Error: Found {count} occurrences of old_string, " - f"expected {expected_replacements}. " - f"Use expected_replacements={count} or make old_string more specific." - ) - - # Perform replacement - new_content = content.replace(old_string, new_string, expected_replacements) - - with open(path, "w", encoding="utf-8") as f: - f.write(new_content) - - return f"Successfully edited {file_path}\nReplaced {count} occurrence(s)" - - except Exception as e: - return f"Error editing file: {e}" - - def register(self, mcp_server: FastMCP) -> None: - """Register with MCP server.""" - tool_instance = self - - @mcp_server.tool() - async def edit( - file_path: Annotated[str, Field(description="Absolute path to the file")], - old_string: Annotated[str, Field(description="Text to find")], - new_string: Annotated[str, Field(description="Text to replace with")], - expected_replacements: Annotated[ - int, Field(description="Expected replacements") - ] = 1, - ctx: MCPContext = None, - ) -> str: - """Edit a file by replacing text.""" - return await tool_instance.call( - ctx, - file_path=file_path, - old_string=old_string, - new_string=new_string, - expected_replacements=expected_replacements, - ) diff --git a/pkg/hanzo-tools-fs/hanzo_tools/fs/find.py b/pkg/hanzo-tools-fs/hanzo_tools/fs/find.py deleted file mode 100644 index 0c632084d..000000000 --- a/pkg/hanzo-tools-fs/hanzo_tools/fs/find.py +++ /dev/null @@ -1,116 +0,0 @@ -"""Find tool - find files by pattern.""" - -import fnmatch -from typing import Optional, Annotated -from pathlib import Path - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout - - -class FindTool(BaseTool): - """Find files by name pattern.""" - - name = "find" - - @property - def description(self) -> str: - return """Find files and directories by name pattern. - -Args: - pattern: Glob pattern (e.g., "*.py", "test_*") - path: Directory to search in (default: current dir) - type: "file", "dir", or None for both - max_results: Maximum results to return (default 100) - -Returns: - List of matching paths -""" - - IGNORED_DIRS = { - ".git", - "__pycache__", - "node_modules", - ".venv", - "venv", - ".idea", - ".vscode", - ".mypy_cache", - ".pytest_cache", - } - - @auto_timeout("find") - async def call( - self, - ctx: MCPContext, - pattern: str, - path: str = ".", - type: Optional[str] = None, - max_results: int = 100, - **kwargs, - ) -> str: - """Find files matching pattern.""" - root = Path(path).resolve() - - if not root.exists(): - return f"Error: Path does not exist: {path}" - - matches = [] - - def should_skip(p: Path) -> bool: - return any(part in self.IGNORED_DIRS for part in p.parts) - - for item in root.rglob("*"): - if len(matches) >= max_results: - break - - if should_skip(item): - continue - - # Check type filter - if type == "file" and not item.is_file(): - continue - if type == "dir" and not item.is_dir(): - continue - - # Check pattern match - if fnmatch.fnmatch(item.name, pattern): - try: - rel_path = item.relative_to(root) - suffix = "/" if item.is_dir() else "" - matches.append(f"{rel_path}{suffix}") - except ValueError: - matches.append(str(item)) - - if not matches: - return f"No matches found for pattern: {pattern}" - - result = f"Found {len(matches)} matches:\n\n" - result += "\n".join(matches) - - if len(matches) >= max_results: - result += f"\n\n[Truncated at {max_results} results]" - - return result - - def register(self, mcp_server: FastMCP) -> None: - """Register with MCP server.""" - tool_instance = self - - @mcp_server.tool() - async def find( - pattern: Annotated[str, Field(description="Glob pattern")], - path: Annotated[str, Field(description="Directory to search")] = ".", - type: Annotated[ - Optional[str], Field(description="file, dir, or None") - ] = None, - max_results: Annotated[int, Field(description="Max results")] = 100, - ctx: MCPContext = None, - ) -> str: - """Find files and directories by pattern.""" - return await tool_instance.call( - ctx, pattern=pattern, path=path, type=type, max_results=max_results - ) diff --git a/pkg/hanzo-tools-fs/hanzo_tools/fs/fs_tool.py b/pkg/hanzo-tools-fs/hanzo_tools/fs/fs_tool.py deleted file mode 100644 index a269ef9be..000000000 --- a/pkg/hanzo-tools-fs/hanzo_tools/fs/fs_tool.py +++ /dev/null @@ -1,844 +0,0 @@ -"""Unified filesystem tool for HIP-0300 architecture. - -This module provides a single unified 'fs' tool that handles all filesystem operations: -- read: Read file contents (returns hash for composability) -- write: Create new files only -- stat: File metadata including hash -- list: Directory listing with depth/pattern -- apply_patch: Edit files with base_hash precondition (the ONLY mutation for existing files) -- search_text: Ripgrep-style text search -- diff: Content diff between hashes - -Following Unix philosophy: one tool for the Bytes + Paths axis. -""" - -import os -import re -import json -import fnmatch -import hashlib -from enum import Enum -from typing import Any, ClassVar, Optional -from pathlib import Path -from datetime import datetime -from dataclasses import field, dataclass - -import aiofiles -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - ConflictError, - NotFoundError, - PermissionManager, - InvalidParamsError, - file_uri, - content_hash, -) - - -class PatchOp(str, Enum): - """Patch operation type (Rust parity).""" - - ADD = "add" - UPDATE = "update" - DELETE = "delete" - - -@dataclass -class PatchHunk: - """A single hunk in a patch (Rust parity).""" - - context: str = "" # @@ header context - old_lines: list[str] = field(default_factory=list) - new_lines: list[str] = field(default_factory=list) - - -@dataclass -class PatchFile: - """A single file operation in a patch (Rust parity).""" - - op: PatchOp - path: str - hunks: list[PatchHunk] = field(default_factory=list) - content: str = "" # For ADD operations - - -def parse_patch_format(patch_text: str) -> list[PatchFile]: - """Parse Rust-style patch format. - - Format: - *** Begin Patch - *** Add File: path - +content lines - *** Update File: path - @@ context header - -old line - +new line - *** Delete File: path - *** End Patch - """ - files = [] - current_file: PatchFile | None = None - current_hunk: PatchHunk | None = None - - lines = patch_text.strip().splitlines() - - for line in lines: - # Skip begin/end markers - if line.strip() in ("*** Begin Patch", "*** End Patch"): - continue - - # File operations - if line.startswith("*** Add File:"): - if current_file: - if current_hunk: - current_file.hunks.append(current_hunk) - files.append(current_file) - path = line.replace("*** Add File:", "").strip() - current_file = PatchFile(op=PatchOp.ADD, path=path) - current_hunk = None - - elif line.startswith("*** Update File:"): - if current_file: - if current_hunk: - current_file.hunks.append(current_hunk) - files.append(current_file) - path = line.replace("*** Update File:", "").strip() - current_file = PatchFile(op=PatchOp.UPDATE, path=path) - current_hunk = None - - elif line.startswith("*** Delete File:"): - if current_file: - if current_hunk: - current_file.hunks.append(current_hunk) - files.append(current_file) - path = line.replace("*** Delete File:", "").strip() - current_file = PatchFile(op=PatchOp.DELETE, path=path) - current_hunk = None - files.append(current_file) - current_file = None - - # Hunk header - elif line.startswith("@@"): - if current_file and current_hunk: - current_file.hunks.append(current_hunk) - context = line.strip() - current_hunk = PatchHunk(context=context) - - # Content lines - elif current_file: - if current_file.op == PatchOp.ADD: - # For ADD, lines starting with + are content - if line.startswith("+"): - current_file.content += line[1:] + "\n" - else: - current_file.content += line + "\n" - elif current_hunk: - if line.startswith("-"): - current_hunk.old_lines.append(line[1:]) - elif line.startswith("+"): - current_hunk.new_lines.append(line[1:]) - elif line.startswith(" "): - # Context line - appears in both old and new - current_hunk.old_lines.append(line[1:]) - current_hunk.new_lines.append(line[1:]) - - # Don't forget the last file - if current_file: - if current_hunk: - current_file.hunks.append(current_hunk) - files.append(current_file) - - return files - - -class FsTool(BaseTool): - """Unified filesystem tool (HIP-0300). - - Handles all filesystem operations on a single axis: - - read: Read file contents - - write: Create new files - - stat: File metadata - - list: Directory listing - - apply_patch: Edit with precondition - - search_text: Text search - - mkdir: Create directory - - rm: Remove (guarded) - - CRITICAL: apply_patch is the ONLY way to edit existing files. - It requires base_hash to prevent stale edits. - """ - - name: ClassVar[str] = "fs" - VERSION: ClassVar[str] = "0.12.0" - # Accept "path" as alias for "uri" (TS parity) - PARAM_ALIASES: ClassVar[dict[str, str]] = {"path": "uri"} - - def __init__(self, permission_manager: Optional[PermissionManager] = None): - super().__init__() - if permission_manager is None: - permission_manager = PermissionManager() - self.permission_manager = permission_manager - self._register_fs_actions() - - @property - def description(self) -> str: - return """Unified filesystem tool (HIP-0300). - -Actions: -- read: Read file contents (returns hash) -- write: Create new files only -- stat: File metadata including hash -- list: Directory listing -- apply_patch: Edit with base_hash precondition -- patch: Apply Rust-style patch format (Rust parity) -- search_text: Text search -- mkdir: Create directory -- rm: Remove (requires confirm=true) - -IMPORTANT: apply_patch is the ONLY way to edit existing files. -patch supports Rust grammar format: *** Begin Patch / *** Update File: / @@ / -old +new -""" - - def _validate_path(self, path: str) -> Path: - """Validate and return Path object.""" - if not path: - raise InvalidParamsError("Path is required", param="uri") - if not os.path.isabs(path): - raise InvalidParamsError( - "Path must be absolute", param="uri", expected="absolute path" - ) - return Path(path) - - def _check_permission(self, path: str) -> None: - """Check if path is allowed.""" - if not self.permission_manager.is_path_allowed(path): - raise InvalidParamsError(f"Access denied: {path}", param="uri") - - def _compute_hash(self, content: str | bytes) -> str: - """Compute content hash.""" - if isinstance(content, str): - content = content.encode("utf-8") - h = hashlib.sha256(content) - return f"sha256:{h.hexdigest()}" - - def _register_fs_actions(self): - """Register all filesystem actions.""" - - @self.action("read", "Read file contents") - async def read( - ctx: MCPContext, - uri: str, - offset: int = 0, - limit: int = 2000, - encoding: str = "utf-8", - ) -> dict: - """Read file contents. - - Returns text content and hash for composability. - """ - # Handle file:// URIs - path_str = uri.replace("file://", "") if uri.startswith("file://") else uri - path = self._validate_path(path_str) - self._check_permission(str(path)) - - if not path.exists(): - raise NotFoundError(f"File not found: {path}", uri=uri) - - if not path.is_file(): - raise InvalidParamsError(f"Not a file: {path}", param="uri") - - try: - async with aiofiles.open( - path, "r", encoding=encoding, errors="replace" - ) as f: - content = await f.read() - - # Compute hash of full content - file_hash = self._compute_hash(content) - - # Apply offset/limit by lines - lines = content.splitlines(keepends=True) - total_lines = len(lines) - selected = lines[offset : offset + limit] - - # Format with line numbers - output_lines = [] - for i, line in enumerate(selected, start=offset + 1): - line = line.rstrip("\n\r") - if len(line) > 2000: - line = line[:2000] + "..." - output_lines.append(f"{i:6}โ”‚{line}") - - text = "\n".join(output_lines) - - return { - "uri": file_uri(str(path)), - "text": text, - "hash": file_hash, - "total_lines": total_lines, - "offset": offset, - "limit": limit, - } - - except UnicodeDecodeError as e: - raise InvalidParamsError(f"Encoding error: {e}", param="encoding") - - @self.action("write", "Create new file") - async def write( - ctx: MCPContext, - uri: str, - content: str, - encoding: str = "utf-8", - ) -> dict: - """Create a new file. Fails if file already exists. - - For editing existing files, use apply_patch instead. - """ - path_str = uri.replace("file://", "") if uri.startswith("file://") else uri - path = self._validate_path(path_str) - self._check_permission(str(path)) - - if path.exists(): - raise ConflictError( - f"File already exists: {path}. Use apply_patch to edit.", - expected="non-existent", - actual="exists", - ) - - # Create parent directories if needed - path.parent.mkdir(parents=True, exist_ok=True) - - try: - async with aiofiles.open(path, "w", encoding=encoding) as f: - await f.write(content) - - file_hash = self._compute_hash(content) - - return { - "uri": file_uri(str(path)), - "hash": file_hash, - "size": len(content.encode(encoding)), - } - - except Exception as e: - raise InvalidParamsError(f"Write failed: {e}") - - @self.action("stat", "Get file metadata") - async def stat(ctx: MCPContext, uri: str) -> dict: - """Get file metadata including hash.""" - path_str = uri.replace("file://", "") if uri.startswith("file://") else uri - path = self._validate_path(path_str) - self._check_permission(str(path)) - - if not path.exists(): - raise NotFoundError(f"File not found: {path}", uri=uri) - - stat_info = path.stat() - - # Compute hash for files - file_hash = None - if path.is_file(): - async with aiofiles.open(path, "rb") as f: - file_hash = self._compute_hash(await f.read()) - - return { - "uri": file_uri(str(path)), - "size": stat_info.st_size, - "hash": file_hash, - "mtime": datetime.fromtimestamp(stat_info.st_mtime).isoformat(), - "is_file": path.is_file(), - "is_dir": path.is_dir(), - } - - @self.action("list", "List directory contents") - async def list_dir( - ctx: MCPContext, - uri: str, - depth: int = 1, - pattern: str | None = None, - cursor: str | None = None, - limit: int = 100, - ) -> dict: - """List directory contents with optional depth and pattern.""" - path_str = uri.replace("file://", "") if uri.startswith("file://") else uri - path = self._validate_path(path_str) - self._check_permission(str(path)) - - if not path.exists(): - raise NotFoundError(f"Directory not found: {path}", uri=uri) - - if not path.is_dir(): - raise InvalidParamsError(f"Not a directory: {path}", param="uri") - - entries = [] - start_index = int(cursor) if cursor else 0 - count = 0 - total = 0 - - def should_include(p: Path) -> bool: - if pattern: - return fnmatch.fnmatch(p.name, pattern) - return True - - def walk(dir_path: Path, current_depth: int): - nonlocal count, total - if current_depth > depth: - return - - try: - for entry in sorted(dir_path.iterdir()): - if not should_include(entry): - continue - - total += 1 - - if total <= start_index: - continue - - if count >= limit: - continue - - rel_path = entry.relative_to(path) - entries.append( - { - "name": str(rel_path), - "uri": file_uri(str(entry)), - "is_dir": entry.is_dir(), - "size": ( - entry.stat().st_size if entry.is_file() else None - ), - } - ) - count += 1 - - if entry.is_dir() and current_depth < depth: - walk(entry, current_depth + 1) - - except PermissionError: - pass - - walk(path, 1) - - has_more = total > start_index + count - next_cursor = str(start_index + count) if has_more else None - - return { - "uri": file_uri(str(path)), - "entries": entries, - "paging": { - "cursor": next_cursor, - "more": has_more, - "total": total, - }, - } - - @self.action("apply_patch", "Edit file with precondition") - async def apply_patch( - ctx: MCPContext, - uri: str, - old_text: str, - new_text: str, - base_hash: str, - ) -> dict: - """Edit existing file with base_hash precondition. - - This is the ONLY way to edit existing files. - The base_hash must match the current file hash to prevent stale edits. - - Args: - uri: File path - old_text: Text to find and replace - new_text: Replacement text - base_hash: Expected hash from previous read (prevents race conditions) - - Returns: - New file URI and hash - - Raises: - ConflictError: If base_hash doesn't match current file - """ - path_str = uri.replace("file://", "") if uri.startswith("file://") else uri - path = self._validate_path(path_str) - self._check_permission(str(path)) - - if not path.exists(): - raise NotFoundError(f"File not found: {path}", uri=uri) - - if not path.is_file(): - raise InvalidParamsError(f"Not a file: {path}", param="uri") - - # Read current content and verify hash - async with aiofiles.open(path, "r", encoding="utf-8") as f: - content = await f.read() - - current_hash = self._compute_hash(content) - - if current_hash != base_hash: - raise ConflictError( - "File has changed since last read (base_hash mismatch)", - expected=base_hash, - actual=current_hash, - ) - - # Check old_text exists and is unique - count = content.count(old_text) - - if count == 0: - raise NotFoundError( - f"old_text not found in file", - uri=uri, - ) - - if count > 1: - raise InvalidParamsError( - f"old_text found {count} times. Make it more specific.", - param="old_text", - expected="unique match", - ) - - # Apply the patch - new_content = content.replace(old_text, new_text, 1) - - async with aiofiles.open(path, "w", encoding="utf-8") as f: - await f.write(new_content) - - new_hash = self._compute_hash(new_content) - - return { - "uri": file_uri(str(path)), - "hash": new_hash, - "previous_hash": current_hash, - } - - @self.action("patch", "Apply Rust-style patch format (Rust parity)") - async def patch( - ctx: MCPContext, - input: str, - ) -> dict: - """Apply a patch in Rust grammar format. - - This provides parity with the Rust apply_patch tool. - - Format: - *** Begin Patch - *** Add File: path/to/new/file.py - +new file content - +line 2 - *** Update File: path/to/existing/file.py - @@ context header - -old line to remove - +new line to add - *** Delete File: path/to/delete.py - *** End Patch - - Args: - input: The patch text in Rust grammar format - - Returns: - Results for each file operation - """ - if not input or not input.strip(): - raise InvalidParamsError("Patch input is required", param="input") - - # Parse the patch - try: - patch_files = parse_patch_format(input) - except Exception as e: - raise InvalidParamsError(f"Invalid patch format: {e}", param="input") - - if not patch_files: - raise InvalidParamsError( - "No file operations found in patch", param="input" - ) - - results = [] - - for pf in patch_files: - path = Path(pf.path) - - # Make path absolute if relative - if not path.is_absolute(): - # Use current working directory - path = Path.cwd() / path - - self._check_permission(str(path)) - - try: - if pf.op == PatchOp.ADD: - # Create new file - if path.exists(): - raise ConflictError(f"File already exists: {path}") - - path.parent.mkdir(parents=True, exist_ok=True) - - async with aiofiles.open(path, "w", encoding="utf-8") as f: - await f.write(pf.content) - - results.append( - { - "op": "add", - "path": str(path), - "hash": self._compute_hash(pf.content), - "success": True, - } - ) - - elif pf.op == PatchOp.UPDATE: - # Update existing file - if not path.exists(): - raise NotFoundError(f"File not found: {path}") - - async with aiofiles.open(path, "r", encoding="utf-8") as f: - content = await f.read() - - # Apply each hunk - for hunk in pf.hunks: - old_text = "\n".join(hunk.old_lines) - new_text = "\n".join(hunk.new_lines) - - if old_text and old_text in content: - content = content.replace(old_text, new_text, 1) - elif not old_text and new_text: - # Pure addition - append to end - content += "\n" + new_text - - async with aiofiles.open(path, "w", encoding="utf-8") as f: - await f.write(content) - - results.append( - { - "op": "update", - "path": str(path), - "hash": self._compute_hash(content), - "hunks_applied": len(pf.hunks), - "success": True, - } - ) - - elif pf.op == PatchOp.DELETE: - # Delete file - if not path.exists(): - results.append( - { - "op": "delete", - "path": str(path), - "success": True, - "message": "File already deleted", - } - ) - else: - path.unlink() - results.append( - { - "op": "delete", - "path": str(path), - "success": True, - } - ) - - except Exception as e: - results.append( - { - "op": pf.op.value, - "path": str(path), - "success": False, - "error": str(e), - } - ) - - return { - "results": results, - "total": len(results), - "success": all(r.get("success") for r in results), - } - - @self.action("search_text", "Search file contents") - async def search_text( - ctx: MCPContext, - pattern: str, - uri: str | None = None, - glob: str | None = None, - limit: int = 50, - cursor: str | None = None, - ) -> dict: - """Search for text pattern in files. - - Uses ripgrep-style matching with regex support. - """ - import re - import subprocess - - # Default to current directory - search_path = "." - if uri: - path_str = ( - uri.replace("file://", "") if uri.startswith("file://") else uri - ) - path = self._validate_path(path_str) - self._check_permission(str(path)) - search_path = str(path) - - matches = [] - start_index = int(cursor) if cursor else 0 - - # Try ripgrep first (fastest) - try: - cmd = ["rg", "--json", "-n", "--max-count", str(limit * 2)] - if glob: - cmd.extend(["--glob", glob]) - cmd.extend([pattern, search_path]) - - result = subprocess.run( - cmd, - capture_output=True, - text=True, - timeout=30, - ) - - for line in result.stdout.splitlines(): - if len(matches) >= limit: - break - - try: - data = json.loads(line) - if data.get("type") == "match": - match_data = data["data"] - matches.append( - { - "uri": file_uri(match_data["path"]["text"]), - "line": match_data["line_number"], - "text": match_data["lines"]["text"].strip(), - } - ) - except json.JSONDecodeError: - continue - - except (subprocess.TimeoutExpired, FileNotFoundError): - # Fallback to Python regex search - try: - regex = re.compile(pattern) - except re.error as e: - raise InvalidParamsError(f"Invalid regex: {e}", param="pattern") - - async def search_file(file_path: Path): - try: - async with aiofiles.open( - file_path, "r", encoding="utf-8", errors="replace" - ) as f: - line_num = 0 - async for line in f: - line_num += 1 - if regex.search(line): - matches.append( - { - "uri": file_uri(str(file_path)), - "line": line_num, - "text": line.strip()[:200], - } - ) - if len(matches) >= limit: - return - except (PermissionError, IsADirectoryError): - pass - - search_dir = Path(search_path) - if search_dir.is_file(): - await search_file(search_dir) - else: - for file_path in search_dir.rglob(glob or "*"): - if file_path.is_file(): - await search_file(file_path) - if len(matches) >= limit: - break - - has_more = len(matches) >= limit - next_cursor = str(start_index + len(matches)) if has_more else None - - return { - "pattern": pattern, - "matches": matches, - "paging": { - "cursor": next_cursor, - "more": has_more, - }, - } - - @self.action("mv", "Move or rename file/directory") - async def mv(ctx: MCPContext, uri: str, destination: str) -> dict: - """Move or rename a file or directory. - - Args: - uri: Source path - destination: Destination path - """ - import shutil - - src_str = uri.replace("file://", "") if uri.startswith("file://") else uri - dst_str = destination.replace("file://", "") if destination.startswith("file://") else destination - src_path = self._validate_path(src_str) - dst_path = self._validate_path(dst_str) - self._check_permission(str(src_path)) - self._check_permission(str(dst_path)) - - if not src_path.exists(): - raise NotFoundError(f"Source not found: {src_path}", uri=uri) - - dst_path.parent.mkdir(parents=True, exist_ok=True) - shutil.move(str(src_path), str(dst_path)) - - return { - "source": file_uri(str(src_path)), - "destination": file_uri(str(dst_path)), - "moved": True, - } - - @self.action("mkdir", "Create directory") - async def mkdir(ctx: MCPContext, uri: str) -> dict: - """Create directory and parent directories.""" - path_str = uri.replace("file://", "") if uri.startswith("file://") else uri - path = self._validate_path(path_str) - self._check_permission(str(path)) - - if path.exists(): - if path.is_dir(): - return {"uri": file_uri(str(path)), "created": False} - raise ConflictError(f"Path exists and is not a directory: {path}") - - path.mkdir(parents=True, exist_ok=True) - - return {"uri": file_uri(str(path)), "created": True} - - @self.action("rm", "Remove file or directory") - async def rm(ctx: MCPContext, uri: str, confirm: bool = False) -> dict: - """Remove file or directory. - - REQUIRES confirm=true as safety measure. - """ - if not confirm: - raise InvalidParamsError( - "rm requires confirm=true for safety", - param="confirm", - expected="true", - ) - - path_str = uri.replace("file://", "") if uri.startswith("file://") else uri - path = self._validate_path(path_str) - self._check_permission(str(path)) - - if not path.exists(): - raise NotFoundError(f"Path not found: {path}", uri=uri) - - if path.is_file(): - path.unlink() - else: - import shutil - - shutil.rmtree(path) - - return {"uri": file_uri(str(path)), "removed": True} - -# Backward compatibility -fs_tool = FsTool diff --git a/pkg/hanzo-tools-fs/hanzo_tools/fs/read.py b/pkg/hanzo-tools-fs/hanzo_tools/fs/read.py deleted file mode 100644 index 7baa708c3..000000000 --- a/pkg/hanzo-tools-fs/hanzo_tools/fs/read.py +++ /dev/null @@ -1,102 +0,0 @@ -"""Read tool - read file contents.""" - -from typing import Any, Optional, Annotated -from pathlib import Path - -from pydantic import Field -from mcp.server import FastMCP -from hanzo_async import read_lines -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import FileSystemTool, PermissionManager, auto_timeout - - -class ReadTool(FileSystemTool): - """Read file contents with line numbers.""" - - name = "read" - - @property - def description(self) -> str: - return """Read file contents with line numbers. - -Args: - file_path: Absolute path to the file - offset: Starting line (0-based, optional) - limit: Max lines to read (optional, default 2000) - -Returns: - File contents with line numbers -""" - - def __init__(self, permission_manager: Optional[PermissionManager] = None): - super().__init__(permission_manager) - - @auto_timeout("read") - async def call( - self, - ctx: MCPContext, - file_path: str, - offset: int = 0, - limit: int = 2000, - **kwargs, - ) -> str: - """Read file contents.""" - # Validate path - validation = self.validate_path(file_path) - if not validation: - return validation.error_message - - if not self.is_path_allowed(file_path): - return f"Error: Access denied to path: {file_path}" - - path = Path(file_path) - - if not path.exists(): - return f"Error: File does not exist: {file_path}" - - if not path.is_file(): - return f"Error: Not a file: {file_path}" - - try: - lines = await read_lines(path, encoding="utf-8", errors="replace") - - # Apply offset and limit - total_lines = len(lines) - selected = lines[offset : offset + limit] - - # Format with line numbers - output_lines = [] - for i, line in enumerate(selected, start=offset + 1): - line = line.rstrip("\n\r") - # Truncate long lines - if len(line) > 2000: - line = line[:2000] + "..." - output_lines.append(f"{i:6}โ†’{line}") - - result = "\n".join(output_lines) - - # Add info if truncated - if offset > 0 or offset + limit < total_lines: - result += f"\n\n[Showing lines {offset + 1}-{min(offset + limit, total_lines)} of {total_lines}]" - - return result - - except Exception as e: - return f"Error reading file: {e}" - - def register(self, mcp_server: FastMCP) -> None: - """Register with MCP server.""" - tool_instance = self - - @mcp_server.tool() - async def read( - file_path: Annotated[str, Field(description="Absolute path to the file")], - offset: Annotated[int, Field(description="Starting line (0-based)")] = 0, - limit: Annotated[int, Field(description="Max lines to read")] = 2000, - ctx: MCPContext = None, - ) -> str: - """Read file contents with line numbers.""" - return await tool_instance.call( - ctx, file_path=file_path, offset=offset, limit=limit - ) diff --git a/pkg/hanzo-tools-fs/hanzo_tools/fs/search.py b/pkg/hanzo-tools-fs/hanzo_tools/fs/search.py deleted file mode 100644 index 4d62585d8..000000000 --- a/pkg/hanzo-tools-fs/hanzo_tools/fs/search.py +++ /dev/null @@ -1,200 +0,0 @@ -"""Search tool - search file contents.""" - -import re -import asyncio -from typing import Optional, Annotated -from pathlib import Path - -from pydantic import Field -from mcp.server import FastMCP -from hanzo_async import read_file -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout - - -class SearchTool(BaseTool): - """Search file contents using regex.""" - - name = "search" - - @property - def description(self) -> str: - return """Search for patterns in file contents. - -Uses ripgrep (rg) if available, falls back to Python regex. - -Args: - pattern: Regex pattern to search for - path: Directory or file to search (default: current dir) - include: Glob pattern to filter files (e.g., "*.py") - context_lines: Lines of context around matches - max_results: Maximum results (default 50) - -Returns: - Matching lines with file paths and line numbers -""" - - @auto_timeout("search") - async def call( - self, - ctx: MCPContext, - pattern: str, - path: str = ".", - include: Optional[str] = None, - context_lines: int = 2, - max_results: int = 50, - **kwargs, - ) -> str: - """Search for pattern in files.""" - root = Path(path).resolve() - - if not root.exists(): - return f"Error: Path does not exist: {path}" - - # Try ripgrep first (much faster) - try: - result = await self._search_with_rg( - pattern, root, include, context_lines, max_results - ) - if result is not None: - return result - except Exception: - pass - - # Fallback to Python - return await self._search_with_python( - pattern, root, include, context_lines, max_results - ) - - async def _search_with_rg( - self, - pattern: str, - root: Path, - include: Optional[str], - context_lines: int, - max_results: int, - ) -> Optional[str]: - """Search using ripgrep (async).""" - cmd = [ - "rg", - "--line-number", - "--color=never", - f"--max-count={max_results}", - ] - - if context_lines > 0: - cmd.append(f"-C{context_lines}") - - if include: - cmd.extend(["--glob", include]) - - cmd.extend([pattern, str(root)]) - - try: - process = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - - try: - stdout, stderr = await asyncio.wait_for( - process.communicate(), timeout=30 - ) - except asyncio.TimeoutError: - process.kill() - await process.wait() - return None - - if process.returncode == 0: - return stdout.decode("utf-8", errors="replace") or "No matches found" - elif process.returncode == 1: - return "No matches found" - else: - return None # Fall back to Python - except FileNotFoundError: - return None - - async def _search_with_python( - self, - pattern: str, - root: Path, - include: Optional[str], - context_lines: int, - max_results: int, - ) -> str: - """Search using Python regex (async file I/O).""" - import fnmatch - - try: - regex = re.compile(pattern) - except re.error as e: - return f"Invalid regex pattern: {e}" - - matches = [] - - # Find files to search - if root.is_file(): - files = [root] - else: - files = list(root.rglob("*")) - - for file_path in files: - if not file_path.is_file(): - continue - - if include and not fnmatch.fnmatch(file_path.name, include): - continue - - try: - content = await read_file(file_path, encoding="utf-8", errors="ignore") - lines = content.splitlines() - - for i, line in enumerate(lines, 1): - if regex.search(line): - rel_path = file_path.relative_to(root) - matches.append(f"{rel_path}:{i}:{line.rstrip()}") - - if len(matches) >= max_results: - break - - if len(matches) >= max_results: - break - - except Exception: - continue - - if not matches: - return "No matches found" - - result = f"Found {len(matches)} matches:\n\n" - result += "\n".join(matches) - - if len(matches) >= max_results: - result += f"\n\n[Truncated at {max_results} results]" - - return result - - def register(self, mcp_server: FastMCP) -> None: - """Register with MCP server.""" - tool_instance = self - - @mcp_server.tool() - async def search( - pattern: Annotated[str, Field(description="Regex pattern")], - path: Annotated[str, Field(description="Path to search")] = ".", - include: Annotated[Optional[str], Field(description="File pattern")] = None, - context_lines: Annotated[int, Field(description="Context lines")] = 2, - max_results: Annotated[int, Field(description="Max results")] = 50, - ctx: MCPContext = None, - ) -> str: - """Search for patterns in file contents.""" - return await tool_instance.call( - ctx, - pattern=pattern, - path=path, - include=include, - context_lines=context_lines, - max_results=max_results, - ) diff --git a/pkg/hanzo-tools-fs/hanzo_tools/fs/tree.py b/pkg/hanzo-tools-fs/hanzo_tools/fs/tree.py deleted file mode 100644 index 00c10c7f2..000000000 --- a/pkg/hanzo-tools-fs/hanzo_tools/fs/tree.py +++ /dev/null @@ -1,126 +0,0 @@ -"""Tree tool - directory tree view.""" - -from typing import Annotated -from pathlib import Path - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout - - -class TreeTool(BaseTool): - """Display directory tree structure.""" - - name = "tree" - - @property - def description(self) -> str: - return """Display directory tree structure. - -Args: - path: Directory path to display - depth: Maximum depth to traverse (default 3) - include_filtered: Include normally filtered dirs like .git - -Returns: - Tree structure as text -""" - - # Directories to skip by default - FILTERED_DIRS = { - ".git", - "__pycache__", - "node_modules", - ".venv", - "venv", - ".idea", - ".vscode", - ".mypy_cache", - ".pytest_cache", - "dist", - "build", - "egg-info", - ".tox", - ".nox", - } - - @auto_timeout("tree") - async def call( - self, - ctx: MCPContext, - path: str, - depth: int = 3, - include_filtered: bool = False, - **kwargs, - ) -> str: - """Generate directory tree.""" - root = Path(path) - - if not root.exists(): - return f"Error: Path does not exist: {path}" - - if not root.is_dir(): - return f"Error: Not a directory: {path}" - - lines = [] - self._build_tree(root, lines, "", depth, include_filtered) - - return "\n".join(lines) - - def _build_tree( - self, - path: Path, - lines: list[str], - prefix: str, - depth: int, - include_filtered: bool, - ) -> None: - """Recursively build tree lines.""" - if depth < 0: - return - - try: - entries = sorted( - path.iterdir(), key=lambda x: (not x.is_dir(), x.name.lower()) - ) - except PermissionError: - lines.append(f"{prefix}[permission denied]") - return - - # Filter entries - if not include_filtered: - entries = [e for e in entries if e.name not in self.FILTERED_DIRS] - - for i, entry in enumerate(entries): - is_last = i == len(entries) - 1 - connector = "โ””โ”€โ”€ " if is_last else "โ”œโ”€โ”€ " - - if entry.is_dir(): - lines.append(f"{prefix}{connector}{entry.name}/") - if depth > 0: - extension = " " if is_last else "โ”‚ " - self._build_tree( - entry, lines, prefix + extension, depth - 1, include_filtered - ) - else: - lines.append(f"{prefix}{connector}{entry.name}") - - def register(self, mcp_server: FastMCP) -> None: - """Register with MCP server.""" - tool_instance = self - - @mcp_server.tool() - async def tree( - path: Annotated[str, Field(description="Directory path")], - depth: Annotated[int, Field(description="Max depth")] = 3, - include_filtered: Annotated[ - bool, Field(description="Include filtered dirs") - ] = False, - ctx: MCPContext = None, - ) -> str: - """Display directory tree structure.""" - return await tool_instance.call( - ctx, path=path, depth=depth, include_filtered=include_filtered - ) diff --git a/pkg/hanzo-tools-fs/hanzo_tools/fs/write.py b/pkg/hanzo-tools-fs/hanzo_tools/fs/write.py deleted file mode 100644 index b12e075d4..000000000 --- a/pkg/hanzo-tools-fs/hanzo_tools/fs/write.py +++ /dev/null @@ -1,75 +0,0 @@ -"""Write tool - write/create files.""" - -from typing import Optional, Annotated -from pathlib import Path - -from pydantic import Field -from mcp.server import FastMCP -from hanzo_async import mkdir, write_file -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import FileSystemTool, PermissionManager, auto_timeout - - -class WriteTool(FileSystemTool): - """Write content to a file.""" - - name = "write" - - @property - def description(self) -> str: - return """Write content to a file (creates or overwrites). - -Args: - file_path: Absolute path to the file - content: Content to write - -Returns: - Success message or error -""" - - def __init__(self, permission_manager: Optional[PermissionManager] = None): - super().__init__(permission_manager) - - @auto_timeout("write") - async def call( - self, - ctx: MCPContext, - file_path: str, - content: str, - **kwargs, - ) -> str: - """Write content to file.""" - validation = self.validate_path(file_path) - if not validation: - return validation.error_message - - if not self.is_path_allowed(file_path): - return f"Error: Access denied to path: {file_path}" - - path = Path(file_path) - - try: - # Create parent directories if needed - await mkdir(path.parent, parents=True, exist_ok=True) - - # Write content - await write_file(path, content, encoding="utf-8") - - return f"Successfully wrote {len(content)} bytes to {file_path}" - - except Exception as e: - return f"Error writing file: {e}" - - def register(self, mcp_server: FastMCP) -> None: - """Register with MCP server.""" - tool_instance = self - - @mcp_server.tool() - async def write( - file_path: Annotated[str, Field(description="Absolute path to the file")], - content: Annotated[str, Field(description="Content to write")], - ctx: MCPContext = None, - ) -> str: - """Write content to a file.""" - return await tool_instance.call(ctx, file_path=file_path, content=content) diff --git a/pkg/hanzo-tools-fs/pyproject.toml b/pkg/hanzo-tools-fs/pyproject.toml deleted file mode 100644 index 371df0516..000000000 --- a/pkg/hanzo-tools-fs/pyproject.toml +++ /dev/null @@ -1,43 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-fs" -version = "0.3.3" -description = "Filesystem tools for Hanzo AI - read, write, edit, search, find" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -keywords = ["hanzo", "tools", "filesystem", "mcp", "ai"] -dependencies = [ - "hanzo-async>=0.1.0", # Unified async I/O with uvloop - "grep-ast>=0.8.1", - "ffind>=1.3.0", - "watchdog>=6.0.0", - "mcp>=1.25.0", - "fastmcp>=2.14.1", - "pydantic>=2.12.5", -] - -[project.urls] -"Homepage" = "https://github.com/hanzoai/python-sdk" - -[project.optional-dependencies] -dev = ["pytest>=7.0.0", "ruff>=0.14.0"] - -[project.entry-points."hanzo.tools"] -fs = "hanzo_tools.fs:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] - -[tool.setuptools.package-data] -hanzo_tools = ["py.typed"] diff --git a/pkg/hanzo-tools-fs/tests/test_fs_tools.py b/pkg/hanzo-tools-fs/tests/test_fs_tools.py deleted file mode 100644 index 8dfb17747..000000000 --- a/pkg/hanzo-tools-fs/tests/test_fs_tools.py +++ /dev/null @@ -1,60 +0,0 @@ -"""Tests for hanzo-tools-fs.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import fs - - assert fs is not None - - def test_import_tools(self): - from hanzo_tools.fs import TOOLS - - assert len(TOOLS) > 0 - - def test_import_read_tool(self): - from hanzo_tools.fs import ReadTool - - assert ReadTool.name == "read" - - def test_import_write_tool(self): - from hanzo_tools.fs import WriteTool - - assert WriteTool.name == "write" - - def test_import_edit_tool(self): - from hanzo_tools.fs import EditTool - - assert EditTool.name == "edit" - - -class TestReadTool: - """Tests for ReadTool.""" - - @pytest.fixture - def tool(self): - from hanzo_tools.fs import ReadTool - - return ReadTool() - - def test_has_description(self, tool): - assert tool.description - assert "read" in tool.description.lower() - - -class TestWriteTool: - """Tests for WriteTool.""" - - @pytest.fixture - def tool(self): - from hanzo_tools.fs import WriteTool - - return WriteTool() - - def test_has_description(self, tool): - assert tool.description - assert "write" in tool.description.lower() diff --git a/pkg/hanzo-tools-fs/uv.lock b/pkg/hanzo-tools-fs/uv.lock deleted file mode 100644 index f5796291f..000000000 --- a/pkg/hanzo-tools-fs/uv.lock +++ /dev/null @@ -1,1739 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" - -[[package]] -name = "aiofiles" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/c3/534eac40372d8ee36ef40df62ec129bee4fdb5ad9706e58a29be53b2c970/aiofiles-25.1.0.tar.gz", hash = "sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2", size = 46354, upload-time = "2025-10-09T20:51:04.358Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/8a/340a1555ae33d7354dbca4faa54948d76d89a27ceef032c8c3bc661d003e/aiofiles-25.1.0-py3-none-any.whl", hash = "sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695", size = 14668, upload-time = "2025-10-09T20:51:03.174Z" }, -] - -[[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.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, -] - -[[package]] -name = "attrs" -version = "25.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, -] - -[[package]] -name = "authlib" -version = "1.6.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/9b/b1661026ff24bc641b76b78c5222d614776b0c085bcfdac9bd15a1cb4b35/authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e", size = 164894, upload-time = "2025-12-12T08:01:41.464Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/51/321e821856452f7386c4e9df866f196720b1ad0c5ea1623ea7399969ae3b/authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd", size = 244005, upload-time = "2025-12-12T08:01:40.209Z" }, -] - -[[package]] -name = "beartype" -version = "0.22.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, -] - -[[package]] -name = "cachetools" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/af/df70e9b65bc77a1cbe0768c0aa4617147f30f8306ded98c1744bcdc0ae1e/cachetools-7.0.0.tar.gz", hash = "sha256:a9abf18ff3b86c7d05b27ead412e235e16ae045925e531fae38d5fada5ed5b08", size = 35796, upload-time = "2026-02-01T18:59:47.411Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/df/2dd32cce20cbcf6f2ec456b58d44368161ad28320729f64e5e1d5d7bd0ae/cachetools-7.0.0-py3-none-any.whl", hash = "sha256:d52fef60e6e964a1969cfb61ccf6242a801b432790fe520d78720d757c81cbd2", size = 13487, upload-time = "2026-02-01T18:59:45.981Z" }, -] - -[[package]] -name = "certifi" -version = "2026.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, - { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, - { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, - { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, - { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, - { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, - { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, - { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, - { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, -] - -[[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 = "cryptography" -version = "46.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/19/f748958276519adf6a0c1e79e7b8860b4830dda55ccdf29f2719b5fc499c/cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59", size = 749301, upload-time = "2026-01-28T00:24:37.379Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/99/157aae7949a5f30d51fcb1a9851e8ebd5c74bf99b5285d8bb4b8b9ee641e/cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485", size = 7173686, upload-time = "2026-01-28T00:23:07.515Z" }, - { url = "https://files.pythonhosted.org/packages/87/91/874b8910903159043b5c6a123b7e79c4559ddd1896e38967567942635778/cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc", size = 4275871, upload-time = "2026-01-28T00:23:09.439Z" }, - { url = "https://files.pythonhosted.org/packages/c0/35/690e809be77896111f5b195ede56e4b4ed0435b428c2f2b6d35046fbb5e8/cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0", size = 4423124, upload-time = "2026-01-28T00:23:11.529Z" }, - { url = "https://files.pythonhosted.org/packages/1a/5b/a26407d4f79d61ca4bebaa9213feafdd8806dc69d3d290ce24996d3cfe43/cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa", size = 4277090, upload-time = "2026-01-28T00:23:13.123Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d8/4bb7aec442a9049827aa34cee1aa83803e528fa55da9a9d45d01d1bb933e/cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81", size = 4947652, upload-time = "2026-01-28T00:23:14.554Z" }, - { url = "https://files.pythonhosted.org/packages/2b/08/f83e2e0814248b844265802d081f2fac2f1cbe6cd258e72ba14ff006823a/cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255", size = 4455157, upload-time = "2026-01-28T00:23:16.443Z" }, - { url = "https://files.pythonhosted.org/packages/0a/05/19d849cf4096448779d2dcc9bb27d097457dac36f7273ffa875a93b5884c/cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e", size = 3981078, upload-time = "2026-01-28T00:23:17.838Z" }, - { url = "https://files.pythonhosted.org/packages/e6/89/f7bac81d66ba7cde867a743ea5b37537b32b5c633c473002b26a226f703f/cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c", size = 4276213, upload-time = "2026-01-28T00:23:19.257Z" }, - { url = "https://files.pythonhosted.org/packages/da/9f/7133e41f24edd827020ad21b068736e792bc68eecf66d93c924ad4719fb3/cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32", size = 4912190, upload-time = "2026-01-28T00:23:21.244Z" }, - { url = "https://files.pythonhosted.org/packages/a6/f7/6d43cbaddf6f65b24816e4af187d211f0bc536a29961f69faedc48501d8e/cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616", size = 4454641, upload-time = "2026-01-28T00:23:22.866Z" }, - { url = "https://files.pythonhosted.org/packages/9e/4f/ebd0473ad656a0ac912a16bd07db0f5d85184924e14fc88feecae2492834/cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0", size = 4405159, upload-time = "2026-01-28T00:23:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d1/f7/7923886f32dc47e27adeff8246e976d77258fd2aa3efdd1754e4e323bf49/cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0", size = 4666059, upload-time = "2026-01-28T00:23:26.766Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a7/0fca0fd3591dffc297278a61813d7f661a14243dd60f499a7a5b48acb52a/cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5", size = 3026378, upload-time = "2026-01-28T00:23:28.317Z" }, - { url = "https://files.pythonhosted.org/packages/2d/12/652c84b6f9873f0909374864a57b003686c642ea48c84d6c7e2c515e6da5/cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b", size = 3478614, upload-time = "2026-01-28T00:23:30.275Z" }, - { url = "https://files.pythonhosted.org/packages/b9/27/542b029f293a5cce59349d799d4d8484b3b1654a7b9a0585c266e974a488/cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908", size = 7116417, upload-time = "2026-01-28T00:23:31.958Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f5/559c25b77f40b6bf828eabaf988efb8b0e17b573545edb503368ca0a2a03/cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da", size = 4264508, upload-time = "2026-01-28T00:23:34.264Z" }, - { url = "https://files.pythonhosted.org/packages/49/a1/551fa162d33074b660dc35c9bc3616fefa21a0e8c1edd27b92559902e408/cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829", size = 4409080, upload-time = "2026-01-28T00:23:35.793Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/4d8d129a755f5d6df1bbee69ea2f35ebfa954fa1847690d1db2e8bca46a5/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2", size = 4270039, upload-time = "2026-01-28T00:23:37.263Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f5/ed3fcddd0a5e39321e595e144615399e47e7c153a1fb8c4862aec3151ff9/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085", size = 4926748, upload-time = "2026-01-28T00:23:38.884Z" }, - { url = "https://files.pythonhosted.org/packages/43/ae/9f03d5f0c0c00e85ecb34f06d3b79599f20630e4db91b8a6e56e8f83d410/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b", size = 4442307, upload-time = "2026-01-28T00:23:40.56Z" }, - { url = "https://files.pythonhosted.org/packages/8b/22/e0f9f2dae8040695103369cf2283ef9ac8abe4d51f68710bec2afd232609/cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd", size = 3959253, upload-time = "2026-01-28T00:23:42.827Z" }, - { url = "https://files.pythonhosted.org/packages/01/5b/6a43fcccc51dae4d101ac7d378a8724d1ba3de628a24e11bf2f4f43cba4d/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2", size = 4269372, upload-time = "2026-01-28T00:23:44.655Z" }, - { url = "https://files.pythonhosted.org/packages/17/b7/0f6b8c1dd0779df2b526e78978ff00462355e31c0a6f6cff8a3e99889c90/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e", size = 4891908, upload-time = "2026-01-28T00:23:46.48Z" }, - { url = "https://files.pythonhosted.org/packages/83/17/259409b8349aa10535358807a472c6a695cf84f106022268d31cea2b6c97/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f", size = 4441254, upload-time = "2026-01-28T00:23:48.403Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fe/e4a1b0c989b00cee5ffa0764401767e2d1cf59f45530963b894129fd5dce/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82", size = 4396520, upload-time = "2026-01-28T00:23:50.26Z" }, - { url = "https://files.pythonhosted.org/packages/b3/81/ba8fd9657d27076eb40d6a2f941b23429a3c3d2f56f5a921d6b936a27bc9/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c", size = 4651479, upload-time = "2026-01-28T00:23:51.674Z" }, - { url = "https://files.pythonhosted.org/packages/00/03/0de4ed43c71c31e4fe954edd50b9d28d658fef56555eba7641696370a8e2/cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061", size = 3001986, upload-time = "2026-01-28T00:23:53.485Z" }, - { url = "https://files.pythonhosted.org/packages/5c/70/81830b59df7682917d7a10f833c4dab2a5574cd664e86d18139f2b421329/cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7", size = 3468288, upload-time = "2026-01-28T00:23:55.09Z" }, - { url = "https://files.pythonhosted.org/packages/56/f7/f648fdbb61d0d45902d3f374217451385edc7e7768d1b03ff1d0e5ffc17b/cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab", size = 7169583, upload-time = "2026-01-28T00:23:56.558Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cc/8f3224cbb2a928de7298d6ed4790f5ebc48114e02bdc9559196bfb12435d/cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef", size = 4275419, upload-time = "2026-01-28T00:23:58.364Z" }, - { url = "https://files.pythonhosted.org/packages/17/43/4a18faa7a872d00e4264855134ba82d23546c850a70ff209e04ee200e76f/cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d", size = 4419058, upload-time = "2026-01-28T00:23:59.867Z" }, - { url = "https://files.pythonhosted.org/packages/ee/64/6651969409821d791ba12346a124f55e1b76f66a819254ae840a965d4b9c/cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973", size = 4278151, upload-time = "2026-01-28T00:24:01.731Z" }, - { url = "https://files.pythonhosted.org/packages/20/0b/a7fce65ee08c3c02f7a8310cc090a732344066b990ac63a9dfd0a655d321/cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4", size = 4939441, upload-time = "2026-01-28T00:24:03.175Z" }, - { url = "https://files.pythonhosted.org/packages/db/a7/20c5701e2cd3e1dfd7a19d2290c522a5f435dd30957d431dcb531d0f1413/cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af", size = 4451617, upload-time = "2026-01-28T00:24:05.403Z" }, - { url = "https://files.pythonhosted.org/packages/00/dc/3e16030ea9aa47b63af6524c354933b4fb0e352257c792c4deeb0edae367/cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263", size = 3977774, upload-time = "2026-01-28T00:24:06.851Z" }, - { url = "https://files.pythonhosted.org/packages/42/c8/ad93f14118252717b465880368721c963975ac4b941b7ef88f3c56bf2897/cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095", size = 4277008, upload-time = "2026-01-28T00:24:08.926Z" }, - { url = "https://files.pythonhosted.org/packages/00/cf/89c99698151c00a4631fbfcfcf459d308213ac29e321b0ff44ceeeac82f1/cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b", size = 4903339, upload-time = "2026-01-28T00:24:12.009Z" }, - { url = "https://files.pythonhosted.org/packages/03/c3/c90a2cb358de4ac9309b26acf49b2a100957e1ff5cc1e98e6c4996576710/cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019", size = 4451216, upload-time = "2026-01-28T00:24:13.975Z" }, - { url = "https://files.pythonhosted.org/packages/96/2c/8d7f4171388a10208671e181ca43cdc0e596d8259ebacbbcfbd16de593da/cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4", size = 4404299, upload-time = "2026-01-28T00:24:16.169Z" }, - { url = "https://files.pythonhosted.org/packages/e9/23/cbb2036e450980f65c6e0a173b73a56ff3bccd8998965dea5cc9ddd424a5/cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b", size = 4664837, upload-time = "2026-01-28T00:24:17.629Z" }, - { url = "https://files.pythonhosted.org/packages/0a/21/f7433d18fe6d5845329cbdc597e30caf983229c7a245bcf54afecc555938/cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc", size = 3009779, upload-time = "2026-01-28T00:24:20.198Z" }, - { url = "https://files.pythonhosted.org/packages/3a/6a/bd2e7caa2facffedf172a45c1a02e551e6d7d4828658c9a245516a598d94/cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976", size = 3466633, upload-time = "2026-01-28T00:24:21.851Z" }, -] - -[[package]] -name = "cyclopts" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "docstring-parser" }, - { name = "rich" }, - { name = "rich-rst" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d4/93/6085aa89c3fff78a5180987354538d72e43b0db27e66a959302d0c07821a/cyclopts-4.5.1.tar.gz", hash = "sha256:fadc45304763fd9f5d6033727f176898d17a1778e194436964661a005078a3dd", size = 162075, upload-time = "2026-01-25T15:23:54.07Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/7c/996760c30f1302704af57c66ff2d723f7d656d0d0b93563b5528a51484bb/cyclopts-4.5.1-py3-none-any.whl", hash = "sha256:0642c93601e554ca6b7b9abd81093847ea4448b2616280f2a0952416574e8c7a", size = 199807, upload-time = "2026-01-25T15:23:55.219Z" }, -] - -[[package]] -name = "diskcache" -version = "5.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, -] - -[[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.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, -] - -[[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.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "fakeredis" -version = "2.33.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "redis" }, - { name = "sortedcontainers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/f9/57464119936414d60697fcbd32f38909bb5688b616ae13de6e98384433e0/fakeredis-2.33.0.tar.gz", hash = "sha256:d7bc9a69d21df108a6451bbffee23b3eba432c21a654afc7ff2d295428ec5770", size = 175187, upload-time = "2025-12-16T19:45:52.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/78/a850fed8aeef96d4a99043c90b818b2ed5419cd5b24a4049fd7cfb9f1471/fakeredis-2.33.0-py3-none-any.whl", hash = "sha256:de535f3f9ccde1c56672ab2fdd6a8efbc4f2619fc2f1acc87b8737177d71c965", size = 119605, upload-time = "2025-12-16T19:45:51.08Z" }, -] - -[package.optional-dependencies] -lua = [ - { name = "lupa" }, -] - -[[package]] -name = "fastmcp" -version = "2.14.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "authlib" }, - { name = "cyclopts" }, - { name = "exceptiongroup" }, - { name = "httpx" }, - { name = "jsonref" }, - { name = "jsonschema-path" }, - { name = "mcp" }, - { name = "openapi-pydantic" }, - { name = "packaging" }, - { name = "platformdirs" }, - { name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] }, - { name = "pydantic", extra = ["email"] }, - { name = "pydocket" }, - { name = "pyperclip" }, - { name = "python-dotenv" }, - { name = "rich" }, - { name = "uvicorn" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/a9/a57d5e5629ebd4ef82b495a7f8e346ce29ef80cc86b15c8c40570701b94d/fastmcp-2.14.4.tar.gz", hash = "sha256:c01f19845c2adda0a70d59525c9193be64a6383014c8d40ce63345ac664053ff", size = 8302239, upload-time = "2026-01-22T17:29:37.024Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/41/c4d407e2218fd60d84acb6cc5131d28ff876afecf325e3fd9d27b8318581/fastmcp-2.14.4-py3-none-any.whl", hash = "sha256:5858cff5e4c8ea8107f9bca2609d71d6256e0fce74495912f6e51625e466c49a", size = 417788, upload-time = "2026-01-22T17:29:35.159Z" }, -] - -[[package]] -name = "ffind" -version = "1.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/03/97fca9e84aa4f4e484884a8ca21fd4e2d07a7906a2228044f80fa28c1a21/ffind-1.6.1.tar.gz", hash = "sha256:1715b6b718eb53ec0b7e9877399b894a48ace1faa2f6a3d772376b7b0a181feb", size = 10234, upload-time = "2025-03-22T17:23:02.76Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/59/ae/5b13349f7f5f9977f2f3cbc26ba09098db2b1242b715a67df4fa2a75e372/ffind-1.6.1-py3-none-any.whl", hash = "sha256:6d79c604087f53fe0e1e3dc4d75a4cf9d4a424a7143289b5f2b9ea061b9a266a", size = 8689, upload-time = "2025-03-22T17:23:01.885Z" }, -] - -[[package]] -name = "grep-ast" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pathspec" }, - { name = "tree-sitter-language-pack" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/67/82/a87079945a7c15d242cb586ae22e17952132439eaa9c878ec5fbdc61c54d/grep_ast-0.9.0.tar.gz", hash = "sha256:620a242a4493e6721338d1c9a6c234ae651f8774f4924a6dcf90f6865d4b2ee3", size = 14125, upload-time = "2025-05-08T01:08:28.371Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/79/29f1373b2ce1eec37c03aefbc17194c2470d8b61ede288e5043231825999/grep_ast-0.9.0-py3-none-any.whl", hash = "sha256:a3973dca99f1abc026a01bbbc70e00a63860c8ff94a56182ff18b089836826d7", size = 13918, upload-time = "2025-05-08T01:08:27.481Z" }, -] - -[[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 = "hanzo-async" -version = "0.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiofiles" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/be/1b/dcd448eac4461973442bc20dd25189360e85ed7ae1c78fafbfce6d88ffc2/hanzo_async-0.1.1.tar.gz", hash = "sha256:05d41974823d27d3557db791705095a49f1cefc901c6ec686120bb9bbd85f0ee", size = 8619, upload-time = "2026-01-05T01:19:19.37Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/70/363dac59048d70653ce0d07ff37d5521f49b5b6ce30d6aba8c31bf31154f/hanzo_async-0.1.1-py3-none-any.whl", hash = "sha256:8f551b7b57e96b4f4c5b7e05d7781eb154a7a788c824c7a36bd69f607532cda2", size = 8649, upload-time = "2026-01-05T01:19:18.268Z" }, -] - -[[package]] -name = "hanzo-tools-fs" -version = "0.3.1" -source = { editable = "." } -dependencies = [ - { name = "fastmcp" }, - { name = "ffind" }, - { name = "grep-ast" }, - { name = "hanzo-async" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "watchdog" }, -] - -[package.optional-dependencies] -dev = [ - { name = "pytest" }, - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "fastmcp", specifier = ">=2.14.1" }, - { name = "ffind", specifier = ">=1.3.0" }, - { name = "grep-ast", specifier = ">=0.8.1" }, - { name = "hanzo-async", specifier = ">=0.1.0" }, - { name = "mcp", specifier = ">=1.25.0" }, - { name = "pydantic", specifier = ">=2.12.5" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.14.0" }, - { name = "watchdog", specifier = ">=6.0.0" }, -] -provides-extras = ["dev"] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, -] - -[[package]] -name = "jaraco-context" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" }, -] - -[[package]] -name = "jaraco-functools" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, -] - -[[package]] -name = "jsonref" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[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.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "keyring" -version = "25.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, -] - -[[package]] -name = "lupa" -version = "2.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/1c/191c3e6ec6502e3dbe25a53e27f69a5daeac3e56de1f73c0138224171ead/lupa-2.6.tar.gz", hash = "sha256:9a770a6e89576be3447668d7ced312cd6fd41d3c13c2462c9dc2c2ab570e45d9", size = 7240282, upload-time = "2025-10-24T07:20:29.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/86/ce243390535c39d53ea17ccf0240815e6e457e413e40428a658ea4ee4b8d/lupa-2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47ce718817ef1cc0c40d87c3d5ae56a800d61af00fbc0fad1ca9be12df2f3b56", size = 951707, upload-time = "2025-10-24T07:18:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/cedea5e6cbeb54396fdcc55f6b741696f3f036d23cfaf986d50d680446da/lupa-2.6-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:7aba985b15b101495aa4b07112cdc08baa0c545390d560ad5cfde2e9e34f4d58", size = 1916703, upload-time = "2025-10-24T07:18:05.6Z" }, - { url = "https://files.pythonhosted.org/packages/24/be/3d6b5f9a8588c01a4d88129284c726017b2089f3a3fd3ba8bd977292fea0/lupa-2.6-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:b766f62f95b2739f2248977d29b0722e589dcf4f0ccfa827ccbd29f0148bd2e5", size = 985152, upload-time = "2025-10-24T07:18:08.561Z" }, - { url = "https://files.pythonhosted.org/packages/eb/23/9f9a05beee5d5dce9deca4cb07c91c40a90541fc0a8e09db4ee670da550f/lupa-2.6-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:00a934c23331f94cb51760097ebfab14b005d55a6b30a2b480e3c53dd2fa290d", size = 1159599, upload-time = "2025-10-24T07:18:10.346Z" }, - { url = "https://files.pythonhosted.org/packages/40/4e/e7c0583083db9d7f1fd023800a9767d8e4391e8330d56c2373d890ac971b/lupa-2.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21de9f38bd475303e34a042b7081aabdf50bd9bafd36ce4faea2f90fd9f15c31", size = 1038686, upload-time = "2025-10-24T07:18:12.112Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/5a4f7d959d4feba5e203ff0c31889e74d1ca3153122be4a46dca7d92bf7c/lupa-2.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf3bda96d3fc41237e964a69c23647d50d4e28421111360274d4799832c560e9", size = 2071956, upload-time = "2025-10-24T07:18:14.572Z" }, - { url = "https://files.pythonhosted.org/packages/92/34/2f4f13ca65d01169b1720176aedc4af17bc19ee834598c7292db232cb6dc/lupa-2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a76ead245da54801a81053794aa3975f213221f6542d14ec4b859ee2e7e0323", size = 1057199, upload-time = "2025-10-24T07:18:16.379Z" }, - { url = "https://files.pythonhosted.org/packages/35/2a/5f7d2eebec6993b0dcd428e0184ad71afb06a45ba13e717f6501bfed1da3/lupa-2.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8dd0861741caa20886ddbda0a121d8e52fb9b5bb153d82fa9bba796962bf30e8", size = 1173693, upload-time = "2025-10-24T07:18:18.153Z" }, - { url = "https://files.pythonhosted.org/packages/e4/29/089b4d2f8e34417349af3904bb40bec40b65c8731f45e3fd8d497ca573e5/lupa-2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:239e63948b0b23023f81d9a19a395e768ed3da6a299f84e7963b8f813f6e3f9c", size = 2164394, upload-time = "2025-10-24T07:18:20.403Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1b/79c17b23c921f81468a111cad843b076a17ef4b684c4a8dff32a7969c3f0/lupa-2.6-cp312-cp312-win32.whl", hash = "sha256:325894e1099499e7a6f9c351147661a2011887603c71086d36fe0f964d52d1ce", size = 1420647, upload-time = "2025-10-24T07:18:23.368Z" }, - { url = "https://files.pythonhosted.org/packages/b8/15/5121e68aad3584e26e1425a5c9a79cd898f8a152292059e128c206ee817c/lupa-2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c735a1ce8ee60edb0fe71d665f1e6b7c55c6021f1d340eb8c865952c602cd36f", size = 1688529, upload-time = "2025-10-24T07:18:25.523Z" }, - { url = "https://files.pythonhosted.org/packages/28/1d/21176b682ca5469001199d8b95fa1737e29957a3d185186e7a8b55345f2e/lupa-2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:663a6e58a0f60e7d212017d6678639ac8df0119bc13c2145029dcba084391310", size = 947232, upload-time = "2025-10-24T07:18:27.878Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4c/d327befb684660ca13cf79cd1f1d604331808f9f1b6fb6bf57832f8edf80/lupa-2.6-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:d1f5afda5c20b1f3217a80e9bc1b77037f8a6eb11612fd3ada19065303c8f380", size = 1908625, upload-time = "2025-10-24T07:18:29.944Z" }, - { url = "https://files.pythonhosted.org/packages/66/8e/ad22b0a19454dfd08662237a84c792d6d420d36b061f239e084f29d1a4f3/lupa-2.6-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:26f2b3c085fe76e9119e48c1013c1cccdc1f51585d456858290475aa38e7089e", size = 981057, upload-time = "2025-10-24T07:18:31.553Z" }, - { url = "https://files.pythonhosted.org/packages/5c/48/74859073ab276bd0566c719f9ca0108b0cfc1956ca0d68678d117d47d155/lupa-2.6-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:60d2f902c7b96fb8ab98493dcff315e7bb4d0b44dc9dd76eb37de575025d5685", size = 1156227, upload-time = "2025-10-24T07:18:33.981Z" }, - { url = "https://files.pythonhosted.org/packages/09/6c/0e9ded061916877253c2266074060eb71ed99fb21d73c8c114a76725bce2/lupa-2.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a02d25dee3a3250967c36590128d9220ae02f2eda166a24279da0b481519cbff", size = 1035752, upload-time = "2025-10-24T07:18:36.32Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ef/f8c32e454ef9f3fe909f6c7d57a39f950996c37a3deb7b391fec7903dab7/lupa-2.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eae1ee16b886b8914ff292dbefbf2f48abfbdee94b33a88d1d5475e02423203", size = 2069009, upload-time = "2025-10-24T07:18:38.072Z" }, - { url = "https://files.pythonhosted.org/packages/53/dc/15b80c226a5225815a890ee1c11f07968e0aba7a852df41e8ae6fe285063/lupa-2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0edd5073a4ee74ab36f74fe61450148e6044f3952b8d21248581f3c5d1a58be", size = 1056301, upload-time = "2025-10-24T07:18:40.165Z" }, - { url = "https://files.pythonhosted.org/packages/31/14/2086c1425c985acfb30997a67e90c39457122df41324d3c179d6ee2292c6/lupa-2.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c53ee9f22a8a17e7d4266ad48e86f43771951797042dd51d1494aaa4f5f3f0a", size = 1170673, upload-time = "2025-10-24T07:18:42.426Z" }, - { url = "https://files.pythonhosted.org/packages/10/e5/b216c054cf86576c0191bf9a9f05de6f7e8e07164897d95eea0078dca9b2/lupa-2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de7c0f157a9064a400d828789191a96da7f4ce889969a588b87ec80de9b14772", size = 2162227, upload-time = "2025-10-24T07:18:46.112Z" }, - { url = "https://files.pythonhosted.org/packages/59/2f/33ecb5bedf4f3bc297ceacb7f016ff951331d352f58e7e791589609ea306/lupa-2.6-cp313-cp313-win32.whl", hash = "sha256:ee9523941ae0a87b5b703417720c5d78f72d2f5bc23883a2ea80a949a3ed9e75", size = 1419558, upload-time = "2025-10-24T07:18:48.371Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b4/55e885834c847ea610e111d87b9ed4768f0afdaeebc00cd46810f25029f6/lupa-2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b1335a5835b0a25ebdbc75cf0bda195e54d133e4d994877ef025e218c2e59db9", size = 1683424, upload-time = "2025-10-24T07:18:50.976Z" }, - { url = "https://files.pythonhosted.org/packages/66/9d/d9427394e54d22a35d1139ef12e845fd700d4872a67a34db32516170b746/lupa-2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dcb6d0a3264873e1653bc188499f48c1fb4b41a779e315eba45256cfe7bc33c1", size = 953818, upload-time = "2025-10-24T07:18:53.378Z" }, - { url = "https://files.pythonhosted.org/packages/10/41/27bbe81953fb2f9ecfced5d9c99f85b37964cfaf6aa8453bb11283983721/lupa-2.6-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:a37e01f2128f8c36106726cb9d360bac087d58c54b4522b033cc5691c584db18", size = 1915850, upload-time = "2025-10-24T07:18:55.259Z" }, - { url = "https://files.pythonhosted.org/packages/a3/98/f9ff60db84a75ba8725506bbf448fb085bc77868a021998ed2a66d920568/lupa-2.6-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:458bd7e9ff3c150b245b0fcfbb9bd2593d1152ea7f0a7b91c1d185846da033fe", size = 982344, upload-time = "2025-10-24T07:18:57.05Z" }, - { url = "https://files.pythonhosted.org/packages/41/f7/f39e0f1c055c3b887d86b404aaf0ca197b5edfd235a8b81b45b25bac7fc3/lupa-2.6-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:052ee82cac5206a02df77119c325339acbc09f5ce66967f66a2e12a0f3211cad", size = 1156543, upload-time = "2025-10-24T07:18:59.251Z" }, - { url = "https://files.pythonhosted.org/packages/9e/9c/59e6cffa0d672d662ae17bd7ac8ecd2c89c9449dee499e3eb13ca9cd10d9/lupa-2.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96594eca3c87dd07938009e95e591e43d554c1dbd0385be03c100367141db5a8", size = 1047974, upload-time = "2025-10-24T07:19:01.449Z" }, - { url = "https://files.pythonhosted.org/packages/23/c6/a04e9cef7c052717fcb28fb63b3824802488f688391895b618e39be0f684/lupa-2.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8faddd9d198688c8884091173a088a8e920ecc96cda2ffed576a23574c4b3f6", size = 2073458, upload-time = "2025-10-24T07:19:03.369Z" }, - { url = "https://files.pythonhosted.org/packages/e6/10/824173d10f38b51fc77785228f01411b6ca28826ce27404c7c912e0e442c/lupa-2.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:daebb3a6b58095c917e76ba727ab37b27477fb926957c825205fbda431552134", size = 1067683, upload-time = "2025-10-24T07:19:06.2Z" }, - { url = "https://files.pythonhosted.org/packages/b6/dc/9692fbcf3c924d9c4ece2d8d2f724451ac2e09af0bd2a782db1cef34e799/lupa-2.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f3154e68972befe0f81564e37d8142b5d5d79931a18309226a04ec92487d4ea3", size = 1171892, upload-time = "2025-10-24T07:19:08.544Z" }, - { url = "https://files.pythonhosted.org/packages/84/ff/e318b628d4643c278c96ab3ddea07fc36b075a57383c837f5b11e537ba9d/lupa-2.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e4dadf77b9fedc0bfa53417cc28dc2278a26d4cbd95c29f8927ad4d8fe0a7ef9", size = 2166641, upload-time = "2025-10-24T07:19:10.485Z" }, - { url = "https://files.pythonhosted.org/packages/12/f7/a6f9ec2806cf2d50826980cdb4b3cffc7691dc6f95e13cc728846d5cb793/lupa-2.6-cp314-cp314-win32.whl", hash = "sha256:cb34169c6fa3bab3e8ac58ca21b8a7102f6a94b6a5d08d3636312f3f02fafd8f", size = 1456857, upload-time = "2025-10-24T07:19:37.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/de/df71896f25bdc18360fdfa3b802cd7d57d7fede41a0e9724a4625b412c85/lupa-2.6-cp314-cp314-win_amd64.whl", hash = "sha256:b74f944fe46c421e25d0f8692aef1e842192f6f7f68034201382ac440ef9ea67", size = 1731191, upload-time = "2025-10-24T07:19:40.281Z" }, - { url = "https://files.pythonhosted.org/packages/47/3c/a1f23b01c54669465f5f4c4083107d496fbe6fb45998771420e9aadcf145/lupa-2.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0e21b716408a21ab65723f8841cf7f2f37a844b7a965eeabb785e27fca4099cf", size = 999343, upload-time = "2025-10-24T07:19:12.519Z" }, - { url = "https://files.pythonhosted.org/packages/c5/6d/501994291cb640bfa2ccf7f554be4e6914afa21c4026bd01bff9ca8aac57/lupa-2.6-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:589db872a141bfff828340079bbdf3e9a31f2689f4ca0d88f97d9e8c2eae6142", size = 2000730, upload-time = "2025-10-24T07:19:14.869Z" }, - { url = "https://files.pythonhosted.org/packages/53/a5/457ffb4f3f20469956c2d4c4842a7675e884efc895b2f23d126d23e126cc/lupa-2.6-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:cd852a91a4a9d4dcbb9a58100f820a75a425703ec3e3f049055f60b8533b7953", size = 1021553, upload-time = "2025-10-24T07:19:17.123Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/36bb5a5d0960f2a5c7c700e0819abb76fd9bf9c1d8a66e5106416d6e9b14/lupa-2.6-cp314-cp314t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:0334753be028358922415ca97a64a3048e4ed155413fc4eaf87dd0a7e2752983", size = 1133275, upload-time = "2025-10-24T07:19:20.51Z" }, - { url = "https://files.pythonhosted.org/packages/19/86/202ff4429f663013f37d2229f6176ca9f83678a50257d70f61a0a97281bf/lupa-2.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:661d895cd38c87658a34780fac54a690ec036ead743e41b74c3fb81a9e65a6aa", size = 1038441, upload-time = "2025-10-24T07:19:22.509Z" }, - { url = "https://files.pythonhosted.org/packages/a7/42/d8125f8e420714e5b52e9c08d88b5329dfb02dcca731b4f21faaee6cc5b5/lupa-2.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aa58454ccc13878cc177c62529a2056be734da16369e451987ff92784994ca7", size = 2058324, upload-time = "2025-10-24T07:19:24.979Z" }, - { url = "https://files.pythonhosted.org/packages/2b/2c/47bf8b84059876e877a339717ddb595a4a7b0e8740bacae78ba527562e1c/lupa-2.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1425017264e470c98022bba8cff5bd46d054a827f5df6b80274f9cc71dafd24f", size = 1060250, upload-time = "2025-10-24T07:19:27.262Z" }, - { url = "https://files.pythonhosted.org/packages/c2/06/d88add2b6406ca1bdec99d11a429222837ca6d03bea42ca75afa169a78cb/lupa-2.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:224af0532d216e3105f0a127410f12320f7c5f1aa0300bdf9646b8d9afb0048c", size = 1151126, upload-time = "2025-10-24T07:19:29.522Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a0/89e6a024c3b4485b89ef86881c9d55e097e7cb0bdb74efb746f2fa6a9a76/lupa-2.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9abb98d5a8fd27c8285302e82199f0e56e463066f88f619d6594a450bf269d80", size = 2153693, upload-time = "2025-10-24T07:19:31.379Z" }, - { url = "https://files.pythonhosted.org/packages/b6/36/a0f007dc58fc1bbf51fb85dcc82fcb1f21b8c4261361de7dab0e3d8521ef/lupa-2.6-cp314-cp314t-win32.whl", hash = "sha256:1849efeba7a8f6fb8aa2c13790bee988fd242ae404bd459509640eeea3d1e291", size = 1590104, upload-time = "2025-10-24T07:19:33.514Z" }, - { url = "https://files.pythonhosted.org/packages/7d/5e/db903ce9cf82c48d6b91bf6d63ae4c8d0d17958939a4e04ba6b9f38b8643/lupa-2.6-cp314-cp314t-win_amd64.whl", hash = "sha256:fc1498d1a4fc028bc521c26d0fad4ca00ed63b952e32fb95949bda76a04bad52", size = 1913818, upload-time = "2025-10-24T07:19:36.039Z" }, -] - -[[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 = "mcp" -version = "1.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyjwt", extra = ["crypto"] }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, -] - -[[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 = "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 = "opentelemetry-api" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, -] - -[[package]] -name = "opentelemetry-exporter-prometheus" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-sdk" }, - { name = "prometheus-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/14/39/7dafa6fff210737267bed35a8855b6ac7399b9e582b8cf1f25f842517012/opentelemetry_exporter_prometheus-0.60b1.tar.gz", hash = "sha256:a4011b46906323f71724649d301b4dc188aaa068852e814f4df38cc76eac616b", size = 14976, upload-time = "2025-12-11T13:32:42.944Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/0d/4be6bf5477a3eb3d917d2f17d3c0b6720cd6cb97898444a61d43cc983f5c/opentelemetry_exporter_prometheus-0.60b1-py3-none-any.whl", hash = "sha256:49f59178de4f4590e3cef0b8b95cf6e071aae70e1f060566df5546fad773b8fd", size = 13019, upload-time = "2025-12-11T13:32:23.974Z" }, -] - -[[package]] -name = "opentelemetry-instrumentation" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "packaging" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/0f/7e6b713ac117c1f5e4e3300748af699b9902a2e5e34c9cf443dde25a01fa/opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a", size = 31706, upload-time = "2025-12-11T13:36:42.515Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096, upload-time = "2025-12-11T13:35:33.067Z" }, -] - -[[package]] -name = "opentelemetry-sdk" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, -] - -[[package]] -name = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[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 = "1.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, -] - -[[package]] -name = "pathvalidate" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262, upload-time = "2025-06-15T09:07:20.736Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305, upload-time = "2025-06-15T09:07:19.117Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, -] - -[[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 = "prometheus-client" -version = "0.24.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616, upload-time = "2026-01-14T15:26:26.965Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057, upload-time = "2026-01-14T15:26:24.42Z" }, -] - -[[package]] -name = "py-key-value-aio" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "py-key-value-shared" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/ce/3136b771dddf5ac905cc193b461eb67967cf3979688c6696e1f2cdcde7ea/py_key_value_aio-0.3.0.tar.gz", hash = "sha256:858e852fcf6d696d231266da66042d3355a7f9871650415feef9fca7a6cd4155", size = 50801, upload-time = "2025-11-17T16:50:04.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/10/72f6f213b8f0bce36eff21fda0a13271834e9eeff7f9609b01afdc253c79/py_key_value_aio-0.3.0-py3-none-any.whl", hash = "sha256:1c781915766078bfd608daa769fefb97e65d1d73746a3dfb640460e322071b64", size = 96342, upload-time = "2025-11-17T16:50:03.801Z" }, -] - -[package.optional-dependencies] -disk = [ - { name = "diskcache" }, - { name = "pathvalidate" }, -] -keyring = [ - { name = "keyring" }, -] -memory = [ - { name = "cachetools" }, -] -redis = [ - { name = "redis" }, -] - -[[package]] -name = "py-key-value-shared" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/e4/1971dfc4620a3a15b4579fe99e024f5edd6e0967a71154771a059daff4db/py_key_value_shared-0.3.0.tar.gz", hash = "sha256:8fdd786cf96c3e900102945f92aa1473138ebe960ef49da1c833790160c28a4b", size = 11666, upload-time = "2025-11-17T16:50:06.849Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e4/b8b0a03ece72f47dce2307d36e1c34725b7223d209fc679315ffe6a4e2c3/py_key_value_shared-0.3.0-py3-none-any.whl", hash = "sha256:5b0efba7ebca08bb158b1e93afc2f07d30b8f40c2fc12ce24a4c0d84f42f9298", size = 19560, upload-time = "2025-11-17T16:50:05.954Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[package.optional-dependencies] -email = [ - { name = "email-validator" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, -] - -[[package]] -name = "pydantic-settings" -version = "2.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, -] - -[[package]] -name = "pydocket" -version = "0.16.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cloudpickle" }, - { name = "fakeredis", extra = ["lua"] }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-prometheus" }, - { name = "opentelemetry-instrumentation" }, - { name = "prometheus-client" }, - { name = "py-key-value-aio", extra = ["memory", "redis"] }, - { name = "python-json-logger" }, - { name = "redis" }, - { name = "rich" }, - { name = "typer" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/00/26befe5f58df7cd1aeda4a8d10bc7d1908ffd86b80fd995e57a2a7b3f7bd/pydocket-0.16.6.tar.gz", hash = "sha256:b96c96ad7692827214ed4ff25fcf941ec38371314db5dcc1ae792b3e9d3a0294", size = 299054, upload-time = "2026-01-09T22:09:15.405Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/3f/7483e5a6dc6326b6e0c640619b5c5bd1d6e3c20e54d58f5fb86267cef00e/pydocket-0.16.6-py3-none-any.whl", hash = "sha256:683d21e2e846aa5106274e7d59210331b242d7fb0dce5b08d3b82065663ed183", size = 67697, upload-time = "2026-01-09T22:09:13.436Z" }, -] - -[[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 = "pyjwt" -version = "2.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, -] - -[package.optional-dependencies] -crypto = [ - { name = "cryptography" }, -] - -[[package]] -name = "pyperclip" -version = "1.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, -] - -[[package]] -name = "pytest" -version = "9.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, -] - -[[package]] -name = "python-json-logger" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, -] - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { 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 = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "redis" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/c8/983d5c6579a411d8a99bc5823cc5712768859b5ce2c8afe1a65b37832c81/redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c", size = 4796669, upload-time = "2025-11-19T15:54:39.961Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/f0/8956f8a86b20d7bb9d6ac0187cf4cd54d8065bc9a1a09eb8011d4d326596/redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b", size = 354159, upload-time = "2025-11-19T15:54:38.064Z" }, -] - -[[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 = "rich" -version = "14.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" }, -] - -[[package]] -name = "rich-rst" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, -] - -[[package]] -name = "ruff" -version = "0.14.14" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/06/f71e3a86b2df0dfa2d2f72195941cd09b44f87711cb7fa5193732cb9a5fc/ruff-0.14.14.tar.gz", hash = "sha256:2d0f819c9a90205f3a867dbbd0be083bee9912e170fd7d9704cc8ae45824896b", size = 4515732, upload-time = "2026-01-22T22:30:17.527Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/89/20a12e97bc6b9f9f68343952da08a8099c57237aef953a56b82711d55edd/ruff-0.14.14-py3-none-linux_armv6l.whl", hash = "sha256:7cfe36b56e8489dee8fbc777c61959f60ec0f1f11817e8f2415f429552846aed", size = 10467650, upload-time = "2026-01-22T22:30:08.578Z" }, - { url = "https://files.pythonhosted.org/packages/a3/b1/c5de3fd2d5a831fcae21beda5e3589c0ba67eec8202e992388e4b17a6040/ruff-0.14.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6006a0082336e7920b9573ef8a7f52eec837add1265cc74e04ea8a4368cd704c", size = 10883245, upload-time = "2026-01-22T22:30:04.155Z" }, - { url = "https://files.pythonhosted.org/packages/b8/7c/3c1db59a10e7490f8f6f8559d1db8636cbb13dccebf18686f4e3c9d7c772/ruff-0.14.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:026c1d25996818f0bf498636686199d9bd0d9d6341c9c2c3b62e2a0198b758de", size = 10231273, upload-time = "2026-01-22T22:30:34.642Z" }, - { url = "https://files.pythonhosted.org/packages/a1/6e/5e0e0d9674be0f8581d1f5e0f0a04761203affce3232c1a1189d0e3b4dad/ruff-0.14.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f666445819d31210b71e0a6d1c01e24447a20b85458eea25a25fe8142210ae0e", size = 10585753, upload-time = "2026-01-22T22:30:31.781Z" }, - { url = "https://files.pythonhosted.org/packages/23/09/754ab09f46ff1884d422dc26d59ba18b4e5d355be147721bb2518aa2a014/ruff-0.14.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c0f18b922c6d2ff9a5e6c3ee16259adc513ca775bcf82c67ebab7cbd9da5bc8", size = 10286052, upload-time = "2026-01-22T22:30:24.827Z" }, - { url = "https://files.pythonhosted.org/packages/c8/cc/e71f88dd2a12afb5f50733851729d6b571a7c3a35bfdb16c3035132675a0/ruff-0.14.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1629e67489c2dea43e8658c3dba659edbfd87361624b4040d1df04c9740ae906", size = 11043637, upload-time = "2026-01-22T22:30:13.239Z" }, - { url = "https://files.pythonhosted.org/packages/67/b2/397245026352494497dac935d7f00f1468c03a23a0c5db6ad8fc49ca3fb2/ruff-0.14.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:27493a2131ea0f899057d49d303e4292b2cae2bb57253c1ed1f256fbcd1da480", size = 12194761, upload-time = "2026-01-22T22:30:22.542Z" }, - { url = "https://files.pythonhosted.org/packages/5b/06/06ef271459f778323112c51b7587ce85230785cd64e91772034ddb88f200/ruff-0.14.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ff589aab3f5b539e35db38425da31a57521efd1e4ad1ae08fc34dbe30bd7df", size = 12005701, upload-time = "2026-01-22T22:30:20.499Z" }, - { url = "https://files.pythonhosted.org/packages/41/d6/99364514541cf811ccc5ac44362f88df66373e9fec1b9d1c4cc830593fe7/ruff-0.14.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc12d74eef0f29f51775f5b755913eb523546b88e2d733e1d701fe65144e89b", size = 11282455, upload-time = "2026-01-22T22:29:59.679Z" }, - { url = "https://files.pythonhosted.org/packages/ca/71/37daa46f89475f8582b7762ecd2722492df26421714a33e72ccc9a84d7a5/ruff-0.14.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb8481604b7a9e75eff53772496201690ce2687067e038b3cc31aaf16aa0b974", size = 11215882, upload-time = "2026-01-22T22:29:57.032Z" }, - { url = "https://files.pythonhosted.org/packages/2c/10/a31f86169ec91c0705e618443ee74ede0bdd94da0a57b28e72db68b2dbac/ruff-0.14.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:14649acb1cf7b5d2d283ebd2f58d56b75836ed8c6f329664fa91cdea19e76e66", size = 11180549, upload-time = "2026-01-22T22:30:27.175Z" }, - { url = "https://files.pythonhosted.org/packages/fd/1e/c723f20536b5163adf79bdd10c5f093414293cdf567eed9bdb7b83940f3f/ruff-0.14.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8058d2145566510790eab4e2fad186002e288dec5e0d343a92fe7b0bc1b3e13", size = 10543416, upload-time = "2026-01-22T22:30:01.964Z" }, - { url = "https://files.pythonhosted.org/packages/3e/34/8a84cea7e42c2d94ba5bde1d7a4fae164d6318f13f933d92da6d7c2041ff/ruff-0.14.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e651e977a79e4c758eb807f0481d673a67ffe53cfa92209781dfa3a996cf8412", size = 10285491, upload-time = "2026-01-22T22:30:29.51Z" }, - { url = "https://files.pythonhosted.org/packages/55/ef/b7c5ea0be82518906c978e365e56a77f8de7678c8bb6651ccfbdc178c29f/ruff-0.14.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cc8b22da8d9d6fdd844a68ae937e2a0adf9b16514e9a97cc60355e2d4b219fc3", size = 10733525, upload-time = "2026-01-22T22:30:06.499Z" }, - { url = "https://files.pythonhosted.org/packages/6a/5b/aaf1dfbcc53a2811f6cc0a1759de24e4b03e02ba8762daabd9b6bd8c59e3/ruff-0.14.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:16bc890fb4cc9781bb05beb5ab4cd51be9e7cb376bf1dd3580512b24eb3fda2b", size = 11315626, upload-time = "2026-01-22T22:30:36.848Z" }, - { url = "https://files.pythonhosted.org/packages/2c/aa/9f89c719c467dfaf8ad799b9bae0df494513fb21d31a6059cb5870e57e74/ruff-0.14.14-py3-none-win32.whl", hash = "sha256:b530c191970b143375b6a68e6f743800b2b786bbcf03a7965b06c4bf04568167", size = 10502442, upload-time = "2026-01-22T22:30:38.93Z" }, - { url = "https://files.pythonhosted.org/packages/87/44/90fa543014c45560cae1fffc63ea059fb3575ee6e1cb654562197e5d16fb/ruff-0.14.14-py3-none-win_amd64.whl", hash = "sha256:3dde1435e6b6fe5b66506c1dff67a421d0b7f6488d466f651c07f4cab3bf20fd", size = 11630486, upload-time = "2026-01-22T22:30:10.852Z" }, - { url = "https://files.pythonhosted.org/packages/9e/6a/40fee331a52339926a92e17ae748827270b288a35ef4a15c9c8f2ec54715/ruff-0.14.14-py3-none-win_arm64.whl", hash = "sha256:56e6981a98b13a32236a72a8da421d7839221fa308b223b9283312312e5ac76c", size = 10920448, upload-time = "2026-01-22T22:30:15.417Z" }, -] - -[[package]] -name = "secretstorage" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[package]] -name = "sortedcontainers" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, -] - -[[package]] -name = "sse-starlette" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "starlette" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/00d280c03ffd39aaee0e86ec81e2d3b9253036a0f93f51d10503adef0e65/sse_starlette-3.2.0.tar.gz", hash = "sha256:8127594edfb51abe44eac9c49e59b0b01f1039d0c7461c6fd91d4e03b70da422", size = 27253, upload-time = "2026-01-17T13:11:05.62Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/7f/832f015020844a8b8f7a9cbc103dd76ba8e3875004c41e08440ea3a2b41a/sse_starlette-3.2.0-py3-none-any.whl", hash = "sha256:5876954bd51920fc2cd51baee47a080eb88a37b5b784e615abb0b283f801cdbf", size = 12763, upload-time = "2026-01-17T13:11:03.775Z" }, -] - -[[package]] -name = "starlette" -version = "0.52.1" -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/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, -] - -[[package]] -name = "tree-sitter" -version = "0.25.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/7c/0350cfc47faadc0d3cf7d8237a4e34032b3014ddf4a12ded9933e1648b55/tree-sitter-0.25.2.tar.gz", hash = "sha256:fe43c158555da46723b28b52e058ad444195afd1db3ca7720c59a254544e9c20", size = 177961, upload-time = "2025-09-25T17:37:59.751Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/9e/20c2a00a862f1c2897a436b17edb774e831b22218083b459d0d081c9db33/tree_sitter-0.25.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ddabfff809ffc983fc9963455ba1cecc90295803e06e140a4c83e94c1fa3d960", size = 146941, upload-time = "2025-09-25T17:37:34.813Z" }, - { url = "https://files.pythonhosted.org/packages/ef/04/8512e2062e652a1016e840ce36ba1cc33258b0dcc4e500d8089b4054afec/tree_sitter-0.25.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c0c0ab5f94938a23fe81928a21cc0fac44143133ccc4eb7eeb1b92f84748331c", size = 137699, upload-time = "2025-09-25T17:37:36.349Z" }, - { url = "https://files.pythonhosted.org/packages/47/8a/d48c0414db19307b0fb3bb10d76a3a0cbe275bb293f145ee7fba2abd668e/tree_sitter-0.25.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd12d80d91d4114ca097626eb82714618dcdfacd6a5e0955216c6485c350ef99", size = 607125, upload-time = "2025-09-25T17:37:37.725Z" }, - { url = "https://files.pythonhosted.org/packages/39/d1/b95f545e9fc5001b8a78636ef942a4e4e536580caa6a99e73dd0a02e87aa/tree_sitter-0.25.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b43a9e4c89d4d0839de27cd4d6902d33396de700e9ff4c5ab7631f277a85ead9", size = 635418, upload-time = "2025-09-25T17:37:38.922Z" }, - { url = "https://files.pythonhosted.org/packages/de/4d/b734bde3fb6f3513a010fa91f1f2875442cdc0382d6a949005cd84563d8f/tree_sitter-0.25.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbb1706407c0e451c4f8cc016fec27d72d4b211fdd3173320b1ada7a6c74c3ac", size = 631250, upload-time = "2025-09-25T17:37:40.039Z" }, - { url = "https://files.pythonhosted.org/packages/46/f2/5f654994f36d10c64d50a192239599fcae46677491c8dd53e7579c35a3e3/tree_sitter-0.25.2-cp312-cp312-win_amd64.whl", hash = "sha256:6d0302550bbe4620a5dc7649517c4409d74ef18558276ce758419cf09e578897", size = 127156, upload-time = "2025-09-25T17:37:41.132Z" }, - { url = "https://files.pythonhosted.org/packages/67/23/148c468d410efcf0a9535272d81c258d840c27b34781d625f1f627e2e27d/tree_sitter-0.25.2-cp312-cp312-win_arm64.whl", hash = "sha256:0c8b6682cac77e37cfe5cf7ec388844957f48b7bd8d6321d0ca2d852994e10d5", size = 113984, upload-time = "2025-09-25T17:37:42.074Z" }, - { url = "https://files.pythonhosted.org/packages/8c/67/67492014ce32729b63d7ef318a19f9cfedd855d677de5773476caf771e96/tree_sitter-0.25.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0628671f0de69bb279558ef6b640bcfc97864fe0026d840f872728a86cd6b6cd", size = 146926, upload-time = "2025-09-25T17:37:43.041Z" }, - { url = "https://files.pythonhosted.org/packages/4e/9c/a278b15e6b263e86c5e301c82a60923fa7c59d44f78d7a110a89a413e640/tree_sitter-0.25.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f5ddcd3e291a749b62521f71fc953f66f5fd9743973fd6dd962b092773569601", size = 137712, upload-time = "2025-09-25T17:37:44.039Z" }, - { url = "https://files.pythonhosted.org/packages/54/9a/423bba15d2bf6473ba67846ba5244b988cd97a4b1ea2b146822162256794/tree_sitter-0.25.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd88fbb0f6c3a0f28f0a68d72df88e9755cf5215bae146f5a1bdc8362b772053", size = 607873, upload-time = "2025-09-25T17:37:45.477Z" }, - { url = "https://files.pythonhosted.org/packages/ed/4c/b430d2cb43f8badfb3a3fa9d6cd7c8247698187b5674008c9d67b2a90c8e/tree_sitter-0.25.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b878e296e63661c8e124177cc3084b041ba3f5936b43076d57c487822426f614", size = 636313, upload-time = "2025-09-25T17:37:46.68Z" }, - { url = "https://files.pythonhosted.org/packages/9d/27/5f97098dbba807331d666a0997662e82d066e84b17d92efab575d283822f/tree_sitter-0.25.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d77605e0d353ba3fe5627e5490f0fbfe44141bafa4478d88ef7954a61a848dae", size = 631370, upload-time = "2025-09-25T17:37:47.993Z" }, - { url = "https://files.pythonhosted.org/packages/d4/3c/87caaed663fabc35e18dc704cd0e9800a0ee2f22bd18b9cbe7c10799895d/tree_sitter-0.25.2-cp313-cp313-win_amd64.whl", hash = "sha256:463c032bd02052d934daa5f45d183e0521ceb783c2548501cf034b0beba92c9b", size = 127157, upload-time = "2025-09-25T17:37:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/d5/23/f8467b408b7988aff4ea40946a4bd1a2c1a73d17156a9d039bbaff1e2ceb/tree_sitter-0.25.2-cp313-cp313-win_arm64.whl", hash = "sha256:b3f63a1796886249bd22c559a5944d64d05d43f2be72961624278eff0dcc5cb8", size = 113975, upload-time = "2025-09-25T17:37:49.922Z" }, - { url = "https://files.pythonhosted.org/packages/07/e3/d9526ba71dfbbe4eba5e51d89432b4b333a49a1e70712aa5590cd22fc74f/tree_sitter-0.25.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:65d3c931013ea798b502782acab986bbf47ba2c452610ab0776cf4a8ef150fc0", size = 146776, upload-time = "2025-09-25T17:37:50.898Z" }, - { url = "https://files.pythonhosted.org/packages/42/97/4bd4ad97f85a23011dd8a535534bb1035c4e0bac1234d58f438e15cff51f/tree_sitter-0.25.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bda059af9d621918efb813b22fb06b3fe00c3e94079c6143fcb2c565eb44cb87", size = 137732, upload-time = "2025-09-25T17:37:51.877Z" }, - { url = "https://files.pythonhosted.org/packages/b6/19/1e968aa0b1b567988ed522f836498a6a9529a74aab15f09dd9ac1e41f505/tree_sitter-0.25.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eac4e8e4c7060c75f395feec46421eb61212cb73998dbe004b7384724f3682ab", size = 609456, upload-time = "2025-09-25T17:37:52.925Z" }, - { url = "https://files.pythonhosted.org/packages/48/b6/cf08f4f20f4c9094006ef8828555484e842fc468827ad6e56011ab668dbd/tree_sitter-0.25.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:260586381b23be33b6191a07cea3d44ecbd6c01aa4c6b027a0439145fcbc3358", size = 636772, upload-time = "2025-09-25T17:37:54.647Z" }, - { url = "https://files.pythonhosted.org/packages/57/e2/d42d55bf56360987c32bc7b16adb06744e425670b823fb8a5786a1cea991/tree_sitter-0.25.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7d2ee1acbacebe50ba0f85fff1bc05e65d877958f00880f49f9b2af38dce1af0", size = 631522, upload-time = "2025-09-25T17:37:55.833Z" }, - { url = "https://files.pythonhosted.org/packages/03/87/af9604ebe275a9345d88c3ace0cf2a1341aa3f8ef49dd9fc11662132df8a/tree_sitter-0.25.2-cp314-cp314-win_amd64.whl", hash = "sha256:4973b718fcadfb04e59e746abfbb0288694159c6aeecd2add59320c03368c721", size = 130864, upload-time = "2025-09-25T17:37:57.453Z" }, - { url = "https://files.pythonhosted.org/packages/a6/6e/e64621037357acb83d912276ffd30a859ef117f9c680f2e3cb955f47c680/tree_sitter-0.25.2-cp314-cp314-win_arm64.whl", hash = "sha256:b8d4429954a3beb3e844e2872610d2a4800ba4eb42bb1990c6a4b1949b18459f", size = 117470, upload-time = "2025-09-25T17:37:58.431Z" }, -] - -[[package]] -name = "tree-sitter-c-sharp" -version = "0.23.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/22/85/a61c782afbb706a47d990eaee6977e7c2bd013771c5bf5c81c617684f286/tree_sitter_c_sharp-0.23.1.tar.gz", hash = "sha256:322e2cfd3a547a840375276b2aea3335fa6458aeac082f6c60fec3f745c967eb", size = 1317728, upload-time = "2024-11-11T05:25:32.535Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/04/f6c2df4c53a588ccd88d50851155945cff8cd887bd70c175e00aaade7edf/tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2b612a6e5bd17bb7fa2aab4bb6fc1fba45c94f09cb034ab332e45603b86e32fd", size = 372235, upload-time = "2024-11-11T05:25:19.424Z" }, - { url = "https://files.pythonhosted.org/packages/99/10/1aa9486f1e28fc22810fa92cbdc54e1051e7f5536a5e5b5e9695f609b31e/tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a8b98f62bc53efcd4d971151950c9b9cd5cbe3bacdb0cd69fdccac63350d83e", size = 419046, upload-time = "2024-11-11T05:25:20.679Z" }, - { url = "https://files.pythonhosted.org/packages/0f/21/13df29f8fcb9ba9f209b7b413a4764b673dfd58989a0dd67e9c7e19e9c2e/tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:986e93d845a438ec3c4416401aa98e6a6f6631d644bbbc2e43fcb915c51d255d", size = 415999, upload-time = "2024-11-11T05:25:22.359Z" }, - { url = "https://files.pythonhosted.org/packages/ca/72/fc6846795bcdae2f8aa94cc8b1d1af33d634e08be63e294ff0d6794b1efc/tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8024e466b2f5611c6dc90321f232d8584893c7fb88b75e4a831992f877616d2", size = 402830, upload-time = "2024-11-11T05:25:24.198Z" }, - { url = "https://files.pythonhosted.org/packages/fe/3a/b6028c5890ce6653807d5fa88c72232c027c6ceb480dbeb3b186d60e5971/tree_sitter_c_sharp-0.23.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7f9bf876866835492281d336b9e1f9626ab668737f74e914c31d285261507da7", size = 397880, upload-time = "2024-11-11T05:25:25.937Z" }, - { url = "https://files.pythonhosted.org/packages/47/d2/4facaa34b40f8104d8751746d0e1cd2ddf0beb9f1404b736b97f372bd1f3/tree_sitter_c_sharp-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:ae9a9e859e8f44e2b07578d44f9a220d3fa25b688966708af6aa55d42abeebb3", size = 377562, upload-time = "2024-11-11T05:25:27.539Z" }, - { url = "https://files.pythonhosted.org/packages/d8/88/3cf6bd9959d94d1fec1e6a9c530c5f08ff4115a474f62aedb5fedb0f7241/tree_sitter_c_sharp-0.23.1-cp39-abi3-win_arm64.whl", hash = "sha256:c81548347a93347be4f48cb63ec7d60ef4b0efa91313330e69641e49aa5a08c5", size = 375157, upload-time = "2024-11-11T05:25:30.839Z" }, -] - -[[package]] -name = "tree-sitter-embedded-template" -version = "0.25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/a7/77729fefab8b1b5690cfc54328f2f629d1c076d16daf32c96ba39d3a3a3a/tree_sitter_embedded_template-0.25.0.tar.gz", hash = "sha256:7d72d5e8a1d1d501a7c90e841b51f1449a90cc240be050e4fb85c22dab991d50", size = 14114, upload-time = "2025-08-29T00:42:51.078Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/9d/3e3c8ee0c019d3bace728300a1ca807c03df39e66cc51e9a5e7c9d1e1909/tree_sitter_embedded_template-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fa0d06467199aeb33fb3d6fa0665bf9b7d5a32621ffdaf37fd8249f8a8050649", size = 10266, upload-time = "2025-08-29T00:42:44.148Z" }, - { url = "https://files.pythonhosted.org/packages/e8/ab/6d4e43b736b2a895d13baea3791dc8ce7245bedf4677df9e7deb22e23a2a/tree_sitter_embedded_template-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc7aacbc2985a5d7e7fe7334f44dffe24c38fb0a8295c4188a04cf21a3d64a73", size = 10650, upload-time = "2025-08-29T00:42:45.147Z" }, - { url = "https://files.pythonhosted.org/packages/9f/97/ea3d1ea4b320fe66e0468b9f6602966e544c9fe641882484f9105e50ee0c/tree_sitter_embedded_template-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7c88c3dd8b94b3c9efe8ae071ff6b1b936a27ac5f6e651845c3b9631fa4c1c2", size = 18268, upload-time = "2025-08-29T00:42:46.03Z" }, - { url = "https://files.pythonhosted.org/packages/64/40/0f42ca894a8f7c298cf336080046ccc14c10e8f4ea46d455f640193181b2/tree_sitter_embedded_template-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:025f7ca84218dcd8455efc901bdbcc2689fb694f3a636c0448e322a23d4bc96b", size = 19068, upload-time = "2025-08-29T00:42:46.699Z" }, - { url = "https://files.pythonhosted.org/packages/d0/2a/0b720bcae7c2dd0a44889c09e800a2f8eb08c496dede9f2b97683506c4c3/tree_sitter_embedded_template-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b5dc1aef6ffa3fae621fe037d85dd98948b597afba20df29d779c426be813ee5", size = 18518, upload-time = "2025-08-29T00:42:47.694Z" }, - { url = "https://files.pythonhosted.org/packages/14/8a/d745071afa5e8bdf5b381cf84c4dc6be6c79dee6af8e0ff07476c3d8e4aa/tree_sitter_embedded_template-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d0a35cfe634c44981a516243bc039874580e02a2990669313730187ce83a5bc6", size = 18267, upload-time = "2025-08-29T00:42:48.635Z" }, - { url = "https://files.pythonhosted.org/packages/5d/74/728355e594fca140f793f234fdfec195366b6956b35754d00ea97ca18b21/tree_sitter_embedded_template-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:3e05a4ac013d54505e75ae48e1a0e9db9aab19949fe15d9f4c7345b11a84a069", size = 13049, upload-time = "2025-08-29T00:42:49.589Z" }, - { url = "https://files.pythonhosted.org/packages/d8/de/afac475e694d0e626b0808f3c86339c349cd15c5163a6a16a53cc11cf892/tree_sitter_embedded_template-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:2751d402179ac0e83f2065b249d8fe6df0718153f1636bcb6a02bde3e5730db9", size = 11978, upload-time = "2025-08-29T00:42:50.226Z" }, -] - -[[package]] -name = "tree-sitter-language-pack" -version = "0.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tree-sitter" }, - { name = "tree-sitter-c-sharp" }, - { name = "tree-sitter-embedded-template" }, - { name = "tree-sitter-yaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c1/83/d1bc738d6f253f415ee54a8afb99640f47028871436f53f2af637c392c4f/tree_sitter_language_pack-0.13.0.tar.gz", hash = "sha256:032034c5e27b1f6e00730b9e7c2dbc8203b4700d0c681fd019d6defcf61183ec", size = 51353370, upload-time = "2025-11-26T14:01:04.586Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/38/aec1f450ae5c4796de8345442f297fcf8912c7d2e00a66d3236ff0f825ed/tree_sitter_language_pack-0.13.0-cp310-abi3-macosx_10_15_universal2.whl", hash = "sha256:0e7eae812b40a2dc8a12eb2f5c55e130eb892706a0bee06215dd76affeb00d07", size = 32991857, upload-time = "2025-11-26T14:00:51.459Z" }, - { url = "https://files.pythonhosted.org/packages/90/09/11f51c59ede786dccddd2d348d5d24a1d99c54117d00f88b477f5fae4bd5/tree_sitter_language_pack-0.13.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:7fdacf383418a845b20772118fcb53ad245f9c5d409bd07dae16acec65151756", size = 20092989, upload-time = "2025-11-26T14:00:54.202Z" }, - { url = "https://files.pythonhosted.org/packages/72/9d/644db031047ab1a70fc5cb6a79a4d4067080fac628375b2320752d2d7b58/tree_sitter_language_pack-0.13.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:0d4f261fce387ae040dae7e4d1c1aca63d84c88320afcc0961c123bec0be8377", size = 19952029, upload-time = "2025-11-26T14:00:56.699Z" }, - { url = "https://files.pythonhosted.org/packages/48/92/5fd749bbb3f5e4538492c77de7bc51a5e479fec6209464ddc25be9153b13/tree_sitter_language_pack-0.13.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:78f369dc4d456c5b08d659939e662c2f9b9fba8c0ec5538a1f973e01edfcf04d", size = 19944614, upload-time = "2025-11-26T14:00:59.381Z" }, - { url = "https://files.pythonhosted.org/packages/97/59/2287f07723c063475d6657babed0d5569f4b499e393ab51354d529c3e7b5/tree_sitter_language_pack-0.13.0-cp310-abi3-win_amd64.whl", hash = "sha256:1cdbc88a03dacd47bec69e56cc20c48eace1fbb6f01371e89c3ee6a2e8f34db1", size = 16896852, upload-time = "2025-11-26T14:01:01.788Z" }, -] - -[[package]] -name = "tree-sitter-yaml" -version = "0.7.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/b6/941d356ac70c90b9d2927375259e3a4204f38f7499ec6e7e8a95b9664689/tree_sitter_yaml-0.7.2.tar.gz", hash = "sha256:756db4c09c9d9e97c81699e8f941cb8ce4e51104927f6090eefe638ee567d32c", size = 84882, upload-time = "2025-10-07T14:40:36.071Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/29/c0b8dbff302c49ff4284666ffb6f2f21145006843bb4c3a9a85d0ec0b7ae/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:7e269ddcfcab8edb14fbb1f1d34eed1e1e26888f78f94eedfe7cc98c60f8bc9f", size = 43898, upload-time = "2025-10-07T14:40:29.486Z" }, - { url = "https://files.pythonhosted.org/packages/18/0d/15a5add06b3932b5e4ce5f5e8e179197097decfe82a0ef000952c8b98216/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:0807b7966e23ddf7dddc4545216e28b5a58cdadedcecca86b8d8c74271a07870", size = 44691, upload-time = "2025-10-07T14:40:30.369Z" }, - { url = "https://files.pythonhosted.org/packages/72/92/c4b896c90d08deb8308fadbad2210fdcc4c66c44ab4292eac4e80acb4b61/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1a5c60c98b6c4c037aae023569f020d0c489fad8dc26fdfd5510363c9c29a41", size = 91430, upload-time = "2025-10-07T14:40:31.16Z" }, - { url = "https://files.pythonhosted.org/packages/89/59/61f1fed31eb6d46ff080b8c0d53658cf29e10263f41ef5fe34768908037a/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88636d19d0654fd24f4f242eaaafa90f6f5ebdba8a62e4b32d251ed156c51a2a", size = 92428, upload-time = "2025-10-07T14:40:31.954Z" }, - { url = "https://files.pythonhosted.org/packages/e3/62/a33a04d19b7f9a0ded780b9c9fcc6279e37c5d00b89b00425bb807a22cc2/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1d2e8f0bb14aa4537320952d0f9607eef3021d5aada8383c34ebeece17db1e06", size = 90580, upload-time = "2025-10-07T14:40:33.037Z" }, - { url = "https://files.pythonhosted.org/packages/6c/e7/9525defa7b30792623f56b1fba9bbba361752348875b165b8975b87398fd/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:74ca712c50fc9d7dbc68cb36b4a7811d6e67a5466b5a789f19bf8dd6084ef752", size = 90455, upload-time = "2025-10-07T14:40:33.778Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d6/8d1e1ace03db3b02e64e91daf21d1347941d1bbecc606a5473a1a605250d/tree_sitter_yaml-0.7.2-cp310-abi3-win_amd64.whl", hash = "sha256:7587b5ca00fc4f9a548eff649697a3b395370b2304b399ceefa2087d8a6c9186", size = 45514, upload-time = "2025-10-07T14:40:34.562Z" }, - { url = "https://files.pythonhosted.org/packages/d8/c7/dcf3ea1c4f5da9b10353b9af4455d756c92d728a8f58f03c480d3ef0ead5/tree_sitter_yaml-0.7.2-cp310-abi3-win_arm64.whl", hash = "sha256:f63c227b18e7ce7587bce124578f0bbf1f890ac63d3e3cd027417574273642c4", size = 44065, upload-time = "2025-10-07T14:40:35.337Z" }, -] - -[[package]] -name = "typer" -version = "0.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.40.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, -] - -[[package]] -name = "watchdog" -version = "6.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, - { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, - { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, - { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, - { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, -] - -[[package]] -name = "websockets" -version = "16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, - { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, - { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, - { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, - { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, - { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, - { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, - { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, - { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, - { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, -] - -[[package]] -name = "wrapt" -version = "1.17.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998, upload-time = "2025-08-12T05:51:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020, upload-time = "2025-08-12T05:51:35.906Z" }, - { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098, upload-time = "2025-08-12T05:51:57.474Z" }, - { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036, upload-time = "2025-08-12T05:52:34.784Z" }, - { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156, upload-time = "2025-08-12T05:52:13.599Z" }, - { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102, upload-time = "2025-08-12T05:52:14.56Z" }, - { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732, upload-time = "2025-08-12T05:52:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705, upload-time = "2025-08-12T05:53:07.123Z" }, - { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877, upload-time = "2025-08-12T05:53:05.436Z" }, - { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885, upload-time = "2025-08-12T05:52:54.367Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, - { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, - { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, - { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, - { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, - { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, - { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, - { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, - { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, - { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, - { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, - { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, - { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, - { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, - { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, - { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, - { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, - { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, - { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, -] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] diff --git a/pkg/hanzo-tools-iam/README.md b/pkg/hanzo-tools-iam/README.md deleted file mode 100644 index 13046276f..000000000 --- a/pkg/hanzo-tools-iam/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# hanzo-tools-iam - -MCP tool package for hanzo-mcp. Provides native iam management via the Hanzo platform. - -## Installation - -```bash -pip install hanzo-tools-iam -``` - -Part of the [hanzo-mcp](https://pypi.org/project/hanzo-mcp/) ecosystem. diff --git a/pkg/hanzo-tools-iam/hanzo_tools/__init__.py b/pkg/hanzo-tools-iam/hanzo_tools/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-tools-iam/hanzo_tools/iam/__init__.py b/pkg/hanzo-tools-iam/hanzo_tools/iam/__init__.py deleted file mode 100644 index 28568ed1d..000000000 --- a/pkg/hanzo-tools-iam/hanzo_tools/iam/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Hanzo IAM Tools โ€” identity and access management via MCP.""" - -from .iam_tool import IAMTool - -TOOLS = [IAMTool] - -__all__ = ["IAMTool", "TOOLS"] diff --git a/pkg/hanzo-tools-iam/hanzo_tools/iam/iam_tool.py b/pkg/hanzo-tools-iam/hanzo_tools/iam/iam_tool.py deleted file mode 100644 index 71acca59e..000000000 --- a/pkg/hanzo-tools-iam/hanzo_tools/iam/iam_tool.py +++ /dev/null @@ -1,614 +0,0 @@ -"""MCP tool for Hanzo IAM โ€” identity and access management. - -Full control over users, organizations, roles, permissions, providers, -applications, tokens, sessions, invitations, and audit records. - -Auth: Uses HanzoSession from hanzo-tools-auth for Bearer JWT tokens. -Backend: Casdoor IAM at hanzo.id/api/ -""" - -from __future__ import annotations - -import os -import json -import logging -from typing import Any, Annotated, final - -import httpx -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core.base import BaseTool - -logger = logging.getLogger(__name__) - -IAM_BASE_URL = os.getenv("IAM_URL", "https://hanzo.id") - -DESCRIPTION = """Hanzo IAM โ€” identity and access management. - -Requires authentication via `hanzo login` (stored at ~/.hanzo/auth/token.json). - -User actions: -- users: List users (params: owner) -- user: Get user by ID (params: id) -- create_user: Create a user (params: owner, name, email, password, display_name) -- update_user: Update a user (params: id, plus fields to update) -- delete_user: Delete a user (params: id) - -Organization actions: -- orgs: List organizations -- org: Get organization details (params: id) - -Role and permission actions: -- roles: List roles -- role: Get role details (params: id) -- permissions: List permissions -- enforce: Check permission (params: owner, model, resource, action) - -Provider and application actions: -- providers: List auth providers -- apps: List applications - -Token and session actions: -- tokens: List tokens -- sessions: List sessions - -Invitation actions: -- invitations: List invitations -- invite: Send invitation (params: email, org) - -Audit and system actions: -- records: List audit records -- system_info: Get IAM system info -- health: Health check -""" - - -def _get_session(): - """Get HanzoSession singleton.""" - from hanzo_tools.auth.session import HanzoSession - return HanzoSession.get() - - -def _iam_url(path: str) -> str: - """Build full IAM API URL.""" - return f"{IAM_BASE_URL}/api/{path.lstrip('/')}" - - -def _auth_headers(token: str) -> dict[str, str]: - """Build auth headers with Bearer token.""" - return { - "Authorization": f"Bearer {token}", - "Content-Type": "application/json", - "User-Agent": "hanzo-mcp/0.1", - } - - -async def _iam_get(path: str, params: dict[str, Any] | None = None) -> Any: - """GET request to IAM API.""" - session = _get_session() - token = session.get_iam_token() - if not token: - raise RuntimeError("Not authenticated. Run 'hanzo login' first.") - - async with httpx.AsyncClient(timeout=30.0) as client: - resp = await client.get( - _iam_url(path), - headers=_auth_headers(token), - params=params, - ) - resp.raise_for_status() - return resp.json() - - -async def _iam_post(path: str, body: dict[str, Any] | None = None) -> Any: - """POST request to IAM API.""" - session = _get_session() - token = session.get_iam_token() - if not token: - raise RuntimeError("Not authenticated. Run 'hanzo login' first.") - - async with httpx.AsyncClient(timeout=30.0) as client: - resp = await client.post( - _iam_url(path), - headers=_auth_headers(token), - json=body or {}, - ) - resp.raise_for_status() - return resp.json() - - -async def _iam_delete(path: str, body: dict[str, Any] | None = None) -> Any: - """DELETE request to IAM API.""" - session = _get_session() - token = session.get_iam_token() - if not token: - raise RuntimeError("Not authenticated. Run 'hanzo login' first.") - - async with httpx.AsyncClient(timeout=30.0) as client: - resp = await client.post( - _iam_url(path), - headers=_auth_headers(token), - json=body or {}, - ) - resp.raise_for_status() - return resp.json() - - -@final -class IAMTool(BaseTool): - """MCP tool for Hanzo IAM operations.""" - - @property - def name(self) -> str: - return "iam" - - @property - def description(self) -> str: - return DESCRIPTION - - async def call( - self, - ctx: MCPContext, - action: str = "health", - id: str | None = None, - owner: str | None = None, - name: str | None = None, - email: str | None = None, - password: str | None = None, - display_name: str | None = None, - org: str | None = None, - model: str | None = None, - resource: str | None = None, - permission_action: str | None = None, - **kwargs: Any, - ) -> str: - try: - # User actions - if action == "users": - return await self._users(owner) - elif action == "user": - return await self._user(id) - elif action == "create_user": - return await self._create_user(owner, name, email, password, display_name) - elif action == "update_user": - return await self._update_user(id, name, email, display_name, **kwargs) - elif action == "delete_user": - return await self._delete_user(id) - - # Organization actions - elif action == "orgs": - return await self._orgs() - elif action == "org": - return await self._org(id) - - # Role and permission actions - elif action == "roles": - return await self._roles() - elif action == "role": - return await self._role(id) - elif action == "permissions": - return await self._permissions() - elif action == "enforce": - return await self._enforce(owner, model, resource, permission_action) - - # Provider and application actions - elif action == "providers": - return await self._providers() - elif action == "apps": - return await self._apps() - - # Token and session actions - elif action == "tokens": - return await self._tokens() - elif action == "sessions": - return await self._sessions() - - # Invitation actions - elif action == "invitations": - return await self._invitations() - elif action == "invite": - return await self._invite(email, org) - - # Audit and system actions - elif action == "records": - return await self._records() - elif action == "system_info": - return await self._system_info() - elif action == "health": - return await self._health() - - else: - return json.dumps({ - "error": f"Unknown action: {action}", - "available": [ - "users", "user", "create_user", "update_user", "delete_user", - "orgs", "org", - "roles", "role", "permissions", "enforce", - "providers", "apps", - "tokens", "sessions", - "invitations", "invite", - "records", "system_info", "health", - ], - }) - except RuntimeError as e: - return json.dumps({"error": str(e)}) - except httpx.HTTPStatusError as e: - body = e.response.text - try: - body = e.response.json() - except Exception: - pass - return json.dumps({"error": f"IAM API error {e.response.status_code}", "detail": body}) - except Exception as e: - logger.exception(f"IAM tool error: {e}") - return json.dumps({"error": f"IAM error: {e}"}) - - # -- User actions -------------------------------------------------------- - - async def _users(self, owner: str | None) -> str: - owner = owner or "hanzo" - data = await _iam_get("get-users", params={"owner": owner}) - users = data if isinstance(data, list) else [] - result = [] - for u in users: - result.append({ - "id": u.get("id"), - "name": u.get("name"), - "email": u.get("email"), - "displayName": u.get("displayName"), - "createdTime": u.get("createdTime"), - }) - return json.dumps({"owner": owner, "count": len(result), "users": result}, indent=2) - - async def _user(self, id: str | None) -> str: - if not id: - return json.dumps({"error": "Required: id (user ID, format: org/username)"}) - data = await _iam_get("get-user", params={"id": id}) - return json.dumps(data, indent=2) - - async def _create_user( - self, - owner: str | None, - name: str | None, - email: str | None, - password: str | None, - display_name: str | None, - ) -> str: - if not name or not email: - return json.dumps({"error": "Required: name, email. Optional: owner, password, display_name"}) - - user_data = { - "owner": owner or "hanzo", - "name": name, - "email": email, - "displayName": display_name or name, - } - if password: - user_data["password"] = password - - data = await _iam_post("add-user", body={"user": user_data}) - return json.dumps({"action": "created", "result": data}, indent=2) - - async def _update_user( - self, - id: str | None, - name: str | None, - email: str | None, - display_name: str | None, - **kwargs: Any, - ) -> str: - if not id: - return json.dumps({"error": "Required: id (user ID, format: org/username)"}) - - # Fetch current user first - current = await _iam_get("get-user", params={"id": id}) - if not isinstance(current, dict): - return json.dumps({"error": f"User not found: {id}"}) - - # Apply updates - if name is not None: - current["name"] = name - if email is not None: - current["email"] = email - if display_name is not None: - current["displayName"] = display_name - for k, v in kwargs.items(): - if v is not None: - current[k] = v - - data = await _iam_post("update-user", body={"user": current}) - return json.dumps({"action": "updated", "id": id, "result": data}, indent=2) - - async def _delete_user(self, id: str | None) -> str: - if not id: - return json.dumps({"error": "Required: id (user ID, format: org/username)"}) - - # Fetch user to get full object for deletion - current = await _iam_get("get-user", params={"id": id}) - if not isinstance(current, dict): - return json.dumps({"error": f"User not found: {id}"}) - - data = await _iam_delete("delete-user", body={"user": current}) - return json.dumps({"action": "deleted", "id": id, "result": data}, indent=2) - - # -- Organization actions ------------------------------------------------ - - async def _orgs(self) -> str: - data = await _iam_get("get-organizations", params={"owner": "admin"}) - orgs = data if isinstance(data, list) else [] - result = [] - for o in orgs: - result.append({ - "name": o.get("name"), - "displayName": o.get("displayName"), - "websiteUrl": o.get("websiteUrl"), - "createdTime": o.get("createdTime"), - }) - return json.dumps({"count": len(result), "organizations": result}, indent=2) - - async def _org(self, id: str | None) -> str: - if not id: - return json.dumps({"error": "Required: id (organization ID, format: admin/org-name)"}) - data = await _iam_get("get-organization", params={"id": id}) - return json.dumps(data, indent=2) - - # -- Role and permission actions ----------------------------------------- - - async def _roles(self) -> str: - data = await _iam_get("get-roles", params={"owner": "hanzo"}) - roles = data if isinstance(data, list) else [] - result = [] - for r in roles: - result.append({ - "name": r.get("name"), - "displayName": r.get("displayName"), - "users": len(r.get("users", [])), - "roles": len(r.get("roles", [])), - }) - return json.dumps({"count": len(result), "roles": result}, indent=2) - - async def _role(self, id: str | None) -> str: - if not id: - return json.dumps({"error": "Required: id (role ID, format: org/role-name)"}) - data = await _iam_get("get-role", params={"id": id}) - return json.dumps(data, indent=2) - - async def _permissions(self) -> str: - data = await _iam_get("get-permissions", params={"owner": "hanzo"}) - perms = data if isinstance(data, list) else [] - result = [] - for p in perms: - result.append({ - "name": p.get("name"), - "displayName": p.get("displayName"), - "resources": p.get("resources", []), - "actions": p.get("actions", []), - "effect": p.get("effect"), - }) - return json.dumps({"count": len(result), "permissions": result}, indent=2) - - async def _enforce( - self, - owner: str | None, - model: str | None, - resource: str | None, - action: str | None, - ) -> str: - if not model or not resource or not action: - return json.dumps({"error": "Required: model, resource, permission_action. Optional: owner"}) - - data = await _iam_get("enforce", params={ - "owner": owner or "hanzo", - "model": model, - "resource": resource, - "action": action, - }) - return json.dumps({ - "allowed": data, - "model": model, - "resource": resource, - "action": action, - }, indent=2) - - # -- Provider and application actions ------------------------------------ - - async def _providers(self) -> str: - data = await _iam_get("get-providers", params={"owner": "admin"}) - providers = data if isinstance(data, list) else [] - result = [] - for p in providers: - result.append({ - "name": p.get("name"), - "displayName": p.get("displayName"), - "type": p.get("type"), - "category": p.get("category"), - }) - return json.dumps({"count": len(result), "providers": result}, indent=2) - - async def _apps(self) -> str: - data = await _iam_get("get-applications", params={"owner": "admin"}) - apps = data if isinstance(data, list) else [] - result = [] - for a in apps: - result.append({ - "name": a.get("name"), - "displayName": a.get("displayName"), - "organization": a.get("organization"), - "clientId": a.get("clientId"), - }) - return json.dumps({"count": len(result), "applications": result}, indent=2) - - # -- Token and session actions ------------------------------------------- - - async def _tokens(self) -> str: - data = await _iam_get("get-tokens", params={"owner": "admin"}) - tokens = data if isinstance(data, list) else [] - result = [] - for t in tokens: - result.append({ - "name": t.get("name"), - "user": t.get("user"), - "application": t.get("application"), - "createdTime": t.get("createdTime"), - "expiresIn": t.get("expiresIn"), - }) - return json.dumps({"count": len(result), "tokens": result}, indent=2) - - async def _sessions(self) -> str: - data = await _iam_get("get-sessions", params={"owner": "admin"}) - sessions = data if isinstance(data, list) else [] - result = [] - for s in sessions: - result.append({ - "name": s.get("name"), - "application": s.get("application"), - "createdTime": s.get("createdTime"), - "sessionId": s.get("sessionId", []), - }) - return json.dumps({"count": len(result), "sessions": result}, indent=2) - - # -- Invitation actions -------------------------------------------------- - - async def _invitations(self) -> str: - data = await _iam_get("get-invitations", params={"owner": "admin"}) - invitations = data if isinstance(data, list) else [] - result = [] - for i in invitations: - result.append({ - "name": i.get("name"), - "email": i.get("email"), - "state": i.get("state"), - "createdTime": i.get("createdTime"), - }) - return json.dumps({"count": len(result), "invitations": result}, indent=2) - - async def _invite(self, email: str | None, org: str | None) -> str: - if not email: - return json.dumps({"error": "Required: email. Optional: org"}) - - invitation = { - "owner": "admin", - "name": email.replace("@", "-at-").replace(".", "-"), - "email": email, - "organization": org or "hanzo", - } - data = await _iam_post("add-invitation", body={"invitation": invitation}) - return json.dumps({"action": "invited", "email": email, "result": data}, indent=2) - - # -- Audit and system actions -------------------------------------------- - - async def _records(self) -> str: - data = await _iam_get("get-records", params={"owner": "admin"}) - records = data if isinstance(data, list) else [] - result = [] - for r in records[:50]: # Limit to 50 most recent - result.append({ - "name": r.get("name"), - "method": r.get("method"), - "requestUri": r.get("requestUri"), - "action": r.get("action"), - "createdTime": r.get("createdTime"), - "user": r.get("user"), - "ip": r.get("ip"), - }) - return json.dumps({"count": len(result), "records": result}, indent=2) - - async def _system_info(self) -> str: - data = await _iam_get("get-system-info") - return json.dumps(data, indent=2) - - async def _health(self) -> str: - try: - async with httpx.AsyncClient(timeout=10.0) as client: - resp = await client.get(f"{IAM_BASE_URL}/api/health") - return json.dumps({ - "status": "ok" if resp.status_code == 200 else "error", - "code": resp.status_code, - "url": IAM_BASE_URL, - }, indent=2) - except Exception as e: - return json.dumps({"status": "error", "error": str(e), "url": IAM_BASE_URL}) - - # -- Registration -------------------------------------------------------- - - def register(self, mcp_server: FastMCP) -> None: - """Register IAM tool with explicit parameters.""" - tool_instance = self - - @mcp_server.tool( - name="iam", - description=DESCRIPTION, - ) - async def iam( - action: Annotated[ - str, - Field( - description=( - "Action to perform. " - "Users: users, user, create_user, update_user, delete_user. " - "Orgs: orgs, org. " - "Roles: roles, role, permissions, enforce. " - "Auth: providers, apps, tokens, sessions. " - "Invitations: invitations, invite. " - "System: records, system_info, health." - ), - ), - ] = "health", - id: Annotated[ - str | None, - Field(description="Entity ID (format: org/name for users, roles, etc.)"), - ] = None, - owner: Annotated[ - str | None, - Field(description="Organization owner (default: hanzo)"), - ] = None, - name: Annotated[ - str | None, - Field(description="Name for create/update operations"), - ] = None, - email: Annotated[ - str | None, - Field(description="Email for user creation or invitations"), - ] = None, - password: Annotated[ - str | None, - Field(description="Password for user creation"), - ] = None, - display_name: Annotated[ - str | None, - Field(description="Display name for create/update operations"), - ] = None, - org: Annotated[ - str | None, - Field(description="Organization name for invitations"), - ] = None, - model: Annotated[ - str | None, - Field(description="Permission model name (for enforce action)"), - ] = None, - resource: Annotated[ - str | None, - Field(description="Resource path (for enforce action)"), - ] = None, - permission_action: Annotated[ - str | None, - Field(description="Permission action to check (for enforce action)"), - ] = None, - ctx: MCPContext = None, - ) -> str: - return await tool_instance.call( - ctx, - action=action, - id=id, - owner=owner, - name=name, - email=email, - password=password, - display_name=display_name, - org=org, - model=model, - resource=resource, - permission_action=permission_action, - ) diff --git a/pkg/hanzo-tools-iam/pyproject.toml b/pkg/hanzo-tools-iam/pyproject.toml deleted file mode 100644 index cab5ffb03..000000000 --- a/pkg/hanzo-tools-iam/pyproject.toml +++ /dev/null @@ -1,32 +0,0 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[project] -name = "hanzo-tools-iam" -version = "0.1.0" -description = "Hanzo MCP tool for IAM โ€” identity, access management, users, orgs, roles, and permissions" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "mcp", "iam", "identity", "access", "tools"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", -] - -dependencies = [ - "hanzo-tools-core>=0.1.0", - "hanzo-tools-auth>=0.1.0", - "httpx>=0.27.0", -] - -[project.entry-points."hanzo.tools"] -iam = "hanzo_tools.iam:TOOLS" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] diff --git a/pkg/hanzo-tools-iam/uv.lock b/pkg/hanzo-tools-iam/uv.lock deleted file mode 100644 index 7c7898c06..000000000 --- a/pkg/hanzo-tools-iam/uv.lock +++ /dev/null @@ -1,1924 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" - -[[package]] -name = "aiofile" -version = "3.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "caio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/67/e2/d7cb819de8df6b5c1968a2756c3cb4122d4fa2b8fc768b53b7c9e5edb646/aiofile-3.9.0.tar.gz", hash = "sha256:e5ad718bb148b265b6df1b3752c4d1d83024b93da9bd599df74b9d9ffcf7919b", size = 17943, upload-time = "2024-10-08T10:39:35.846Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/50/25/da1f0b4dd970e52bf5a36c204c107e11a0c6d3ed195eba0bfbc664c312b2/aiofile-3.9.0-py3-none-any.whl", hash = "sha256:ce2f6c1571538cbdfa0143b04e16b208ecb0e9cb4148e528af8a640ed51cc8aa", size = 19539, upload-time = "2024-10-08T10:39:32.955Z" }, -] - -[[package]] -name = "aiohappyeyeballs" -version = "2.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, -] - -[[package]] -name = "aiohttp" -version = "3.13.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohappyeyeballs" }, - { name = "aiosignal" }, - { name = "attrs" }, - { name = "frozenlist" }, - { name = "multidict" }, - { name = "propcache" }, - { name = "yarl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876, upload-time = "2026-03-31T21:57:36.319Z" }, - { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557, upload-time = "2026-03-31T21:57:38.236Z" }, - { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258, upload-time = "2026-03-31T21:57:39.923Z" }, - { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199, upload-time = "2026-03-31T21:57:41.938Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013, upload-time = "2026-03-31T21:57:43.904Z" }, - { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501, upload-time = "2026-03-31T21:57:46.285Z" }, - { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981, upload-time = "2026-03-31T21:57:48.734Z" }, - { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934, upload-time = "2026-03-31T21:57:51.171Z" }, - { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671, upload-time = "2026-03-31T21:57:53.326Z" }, - { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219, upload-time = "2026-03-31T21:57:55.385Z" }, - { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049, upload-time = "2026-03-31T21:57:57.341Z" }, - { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557, upload-time = "2026-03-31T21:57:59.626Z" }, - { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931, upload-time = "2026-03-31T21:58:01.972Z" }, - { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125, upload-time = "2026-03-31T21:58:04.007Z" }, - { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427, upload-time = "2026-03-31T21:58:06.337Z" }, - { url = "https://files.pythonhosted.org/packages/98/de/cf2f44ff98d307e72fb97d5f5bbae3bfcb442f0ea9790c0bf5c5c2331404/aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3", size = 433534, upload-time = "2026-03-31T21:58:08.712Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ca/eadf6f9c8fa5e31d40993e3db153fb5ed0b11008ad5d9de98a95045bed84/aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1", size = 460446, upload-time = "2026-03-31T21:58:10.945Z" }, - { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930, upload-time = "2026-03-31T21:58:13.155Z" }, - { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927, upload-time = "2026-03-31T21:58:15.073Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141, upload-time = "2026-03-31T21:58:17.009Z" }, - { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476, upload-time = "2026-03-31T21:58:18.925Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507, upload-time = "2026-03-31T21:58:21.094Z" }, - { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465, upload-time = "2026-03-31T21:58:23.159Z" }, - { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523, upload-time = "2026-03-31T21:58:25.59Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113, upload-time = "2026-03-31T21:58:27.624Z" }, - { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351, upload-time = "2026-03-31T21:58:29.918Z" }, - { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205, upload-time = "2026-03-31T21:58:32.214Z" }, - { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618, upload-time = "2026-03-31T21:58:34.728Z" }, - { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185, upload-time = "2026-03-31T21:58:36.909Z" }, - { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311, upload-time = "2026-03-31T21:58:39.38Z" }, - { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147, upload-time = "2026-03-31T21:58:41.476Z" }, - { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356, upload-time = "2026-03-31T21:58:44.049Z" }, - { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637, upload-time = "2026-03-31T21:58:46.167Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896, upload-time = "2026-03-31T21:58:48.119Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721, upload-time = "2026-03-31T21:58:50.229Z" }, - { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663, upload-time = "2026-03-31T21:58:52.232Z" }, - { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094, upload-time = "2026-03-31T21:58:54.566Z" }, - { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701, upload-time = "2026-03-31T21:58:56.864Z" }, - { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360, upload-time = "2026-03-31T21:58:59.072Z" }, - { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023, upload-time = "2026-03-31T21:59:01.776Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795, upload-time = "2026-03-31T21:59:04.568Z" }, - { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405, upload-time = "2026-03-31T21:59:07.221Z" }, - { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082, upload-time = "2026-03-31T21:59:09.484Z" }, - { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346, upload-time = "2026-03-31T21:59:12.068Z" }, - { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891, upload-time = "2026-03-31T21:59:14.552Z" }, - { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113, upload-time = "2026-03-31T21:59:17.068Z" }, - { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088, upload-time = "2026-03-31T21:59:19.541Z" }, - { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976, upload-time = "2026-03-31T21:59:22.311Z" }, - { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444, upload-time = "2026-03-31T21:59:24.635Z" }, - { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128, upload-time = "2026-03-31T21:59:27.291Z" }, - { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029, upload-time = "2026-03-31T21:59:29.429Z" }, - { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758, upload-time = "2026-03-31T21:59:31.547Z" }, - { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883, upload-time = "2026-03-31T21:59:34.098Z" }, - { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668, upload-time = "2026-03-31T21:59:36.497Z" }, - { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461, upload-time = "2026-03-31T21:59:38.723Z" }, - { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661, upload-time = "2026-03-31T21:59:41.187Z" }, - { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800, upload-time = "2026-03-31T21:59:43.84Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382, upload-time = "2026-03-31T21:59:46.187Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724, upload-time = "2026-03-31T21:59:48.656Z" }, - { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027, upload-time = "2026-03-31T21:59:51.284Z" }, - { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644, upload-time = "2026-03-31T21:59:53.753Z" }, - { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630, upload-time = "2026-03-31T21:59:56.239Z" }, - { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403, upload-time = "2026-03-31T21:59:59.103Z" }, - { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924, upload-time = "2026-03-31T22:00:02.116Z" }, - { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119, upload-time = "2026-03-31T22:00:04.756Z" }, - { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072, upload-time = "2026-03-31T22:00:07.494Z" }, - { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819, upload-time = "2026-03-31T22:00:10.277Z" }, - { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" }, -] - -[[package]] -name = "aiosignal" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "frozenlist" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, -] - -[[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.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, -] - -[[package]] -name = "attrs" -version = "26.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, -] - -[[package]] -name = "authlib" -version = "1.7.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "joserfc" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/98/7d93f30d029643c0275dbc0bd6d5a6f670661ee6c9a94d93af7ab4887600/authlib-1.7.2.tar.gz", hash = "sha256:2cea25fefcd4e7173bdf1372c0afc265c8034b23a8cd5dcb6a9164b826c64231", size = 176511, upload-time = "2026-05-06T08:10:23.116Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/95/adcb68e20c34162e9135f370d6e31737719c2b6f94bc953fe7ed1f10fe21/authlib-1.7.2-py2.py3-none-any.whl", hash = "sha256:3e1faedc9d87e7d56a164eca3ccb6ace0d61b94abe83e92242f8dc8bba9b4a9f", size = 259548, upload-time = "2026-05-06T08:10:21.436Z" }, -] - -[[package]] -name = "beartype" -version = "0.22.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, -] - -[[package]] -name = "cachetools" -version = "7.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ff/e2/85f227594656000ff4d8adadae91a21f536d4a84c6c716a86bd6685874be/cachetools-7.1.1.tar.gz", hash = "sha256:27bdf856d68fd3c71c26c01b5edc312124ed427524d1ddb31aa2b7746fe20d4b", size = 40202, upload-time = "2026-05-03T20:00:29.391Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/0f/f897abe4ea0a8c408ae65c8c83bffab4936ad65d6032d4fb4cd35bbdc3ee/cachetools-7.1.1-py3-none-any.whl", hash = "sha256:0335cd7a0952d2b22327441fb0628139e234c565559eeb91a8a4ac7551c5353d", size = 16775, upload-time = "2026-05-03T20:00:27.857Z" }, -] - -[[package]] -name = "caio" -version = "0.9.25" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/92/88/b8527e1b00c1811db339a1df8bd1ae49d146fcea9d6a5c40e3a80aaeb38d/caio-0.9.25.tar.gz", hash = "sha256:16498e7f81d1d0f5a4c0ad3f2540e65fe25691376e0a5bd367f558067113ed10", size = 26781, upload-time = "2025-12-26T15:21:36.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/25/79c98ebe12df31548ba4eaf44db11b7cad6b3e7b4203718335620939083c/caio-0.9.25-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fb7ff95af4c31ad3f03179149aab61097a71fd85e05f89b4786de0359dffd044", size = 36983, upload-time = "2025-12-26T15:21:36.075Z" }, - { url = "https://files.pythonhosted.org/packages/a3/2b/21288691f16d479945968a0a4f2856818c1c5be56881d51d4dac9b255d26/caio-0.9.25-cp312-cp312-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:97084e4e30dfa598449d874c4d8e0c8d5ea17d2f752ef5e48e150ff9d240cd64", size = 82012, upload-time = "2025-12-26T15:22:20.983Z" }, - { url = "https://files.pythonhosted.org/packages/03/c4/8a1b580875303500a9c12b9e0af58cb82e47f5bcf888c2457742a138273c/caio-0.9.25-cp312-cp312-manylinux_2_34_aarch64.whl", hash = "sha256:4fa69eba47e0f041b9d4f336e2ad40740681c43e686b18b191b6c5f4c5544bfb", size = 81502, upload-time = "2026-03-04T22:08:22.381Z" }, - { url = "https://files.pythonhosted.org/packages/d1/1c/0fe770b8ffc8362c48134d1592d653a81a3d8748d764bec33864db36319d/caio-0.9.25-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:6bebf6f079f1341d19f7386db9b8b1f07e8cc15ae13bfdaff573371ba0575d69", size = 80200, upload-time = "2026-03-04T22:08:23.382Z" }, - { url = "https://files.pythonhosted.org/packages/31/57/5e6ff127e6f62c9f15d989560435c642144aa4210882f9494204bc892305/caio-0.9.25-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d6c2a3411af97762a2b03840c3cec2f7f728921ff8adda53d7ea2315a8563451", size = 36979, upload-time = "2025-12-26T15:21:35.484Z" }, - { url = "https://files.pythonhosted.org/packages/a3/9f/f21af50e72117eb528c422d4276cbac11fb941b1b812b182e0a9c70d19c5/caio-0.9.25-cp313-cp313-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0998210a4d5cd5cb565b32ccfe4e53d67303f868a76f212e002a8554692870e6", size = 81900, upload-time = "2025-12-26T15:22:21.919Z" }, - { url = "https://files.pythonhosted.org/packages/9c/12/c39ae2a4037cb10ad5eb3578eb4d5f8c1a2575c62bba675f3406b7ef0824/caio-0.9.25-cp313-cp313-manylinux_2_34_aarch64.whl", hash = "sha256:1a177d4777141b96f175fe2c37a3d96dec7911ed9ad5f02bac38aaa1c936611f", size = 81523, upload-time = "2026-03-04T22:08:25.187Z" }, - { url = "https://files.pythonhosted.org/packages/22/59/f8f2e950eb4f1a5a3883e198dca514b9d475415cb6cd7b78b9213a0dd45a/caio-0.9.25-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:9ed3cfb28c0e99fec5e208c934e5c157d0866aa9c32aa4dc5e9b6034af6286b7", size = 80243, upload-time = "2026-03-04T22:08:26.449Z" }, - { url = "https://files.pythonhosted.org/packages/69/ca/a08fdc7efdcc24e6a6131a93c85be1f204d41c58f474c42b0670af8c016b/caio-0.9.25-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fab6078b9348e883c80a5e14b382e6ad6aabbc4429ca034e76e730cf464269db", size = 36978, upload-time = "2025-12-26T15:21:41.055Z" }, - { url = "https://files.pythonhosted.org/packages/5e/6c/d4d24f65e690213c097174d26eda6831f45f4734d9d036d81790a27e7b78/caio-0.9.25-cp314-cp314-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:44a6b58e52d488c75cfaa5ecaa404b2b41cc965e6c417e03251e868ecd5b6d77", size = 81832, upload-time = "2025-12-26T15:22:22.757Z" }, - { url = "https://files.pythonhosted.org/packages/87/a4/e534cf7d2d0e8d880e25dd61e8d921ffcfe15bd696734589826f5a2df727/caio-0.9.25-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:628a630eb7fb22381dd8e3c8ab7f59e854b9c806639811fc3f4310c6bd711d79", size = 81565, upload-time = "2026-03-04T22:08:27.483Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ed/bf81aeac1d290017e5e5ac3e880fd56ee15e50a6d0353986799d1bc5cfd5/caio-0.9.25-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:0ba16aa605ccb174665357fc729cf500679c2d94d5f1458a6f0d5ca48f2060a7", size = 80071, upload-time = "2026-03-04T22:08:28.751Z" }, - { url = "https://files.pythonhosted.org/packages/86/93/1f76c8d1bafe3b0614e06b2195784a3765bbf7b0a067661af9e2dd47fc33/caio-0.9.25-py3-none-any.whl", hash = "sha256:06c0bb02d6b929119b1cfbe1ca403c768b2013a369e2db46bfa2a5761cf82e40", size = 19087, upload-time = "2025-12-26T15:22:00.221Z" }, -] - -[[package]] -name = "certifi" -version = "2026.4.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, - { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, - { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, - { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, - { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, - { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, - { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, - { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, - { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, - { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, - { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, - { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, - { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, - { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, - { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, - { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, - { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, - { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, - { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, - { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, - { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, - { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, - { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, - { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, - { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, - { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, - { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, - { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, - { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, - { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, - { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, - { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, - { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, - { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, - { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, - { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, - { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, - { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, - { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, - { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, - { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, - { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, - { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, - { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, - { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, -] - -[[package]] -name = "click" -version = "8.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, -] - -[[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 = "cryptography" -version = "48.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" }, - { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" }, - { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" }, - { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" }, - { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" }, - { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" }, - { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" }, - { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" }, - { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" }, - { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" }, - { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" }, - { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" }, - { url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" }, - { url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" }, - { url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" }, - { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" }, - { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" }, - { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" }, - { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" }, - { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" }, - { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" }, - { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" }, - { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" }, - { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" }, - { url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" }, - { url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" }, - { url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" }, - { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" }, - { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" }, - { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" }, - { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" }, - { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" }, - { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" }, - { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" }, - { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" }, - { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" }, - { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" }, - { url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" }, - { url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" }, -] - -[[package]] -name = "cyclopts" -version = "4.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "docstring-parser" }, - { name = "rich" }, - { name = "rich-rst" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a4/c3/d3f095120329616cc364af2bedcffd518d4db18c978f2f6c892d29e6af2f/cyclopts-4.12.0.tar.gz", hash = "sha256:86bfb5b35cb078decc1cca6c1be41f9a0e6202dc43b4f6056d5cfc6d1f4a69d1", size = 176123, upload-time = "2026-05-13T13:26:31.243Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/d0/17b6b7d5f64dea337a7a409a1e4e0eeceda724046b9acd158fd1aa2f2328/cyclopts-4.12.0-py3-none-any.whl", hash = "sha256:ee03d2b9ef790d866cb3823a7e54b2be5252c82d34536579846fce068b30c38f", size = 213706, upload-time = "2026-05-13T13:26:29.744Z" }, -] - -[[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.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/4d/f332313098c1de1b2d2ff91cf2674415cc7cddab2ca1b01ae29774bd5fdf/docstring_parser-0.18.0.tar.gz", hash = "sha256:292510982205c12b1248696f44959db3cdd1740237a968ea1e2e7a900eeb2015", size = 29341, upload-time = "2026-04-14T04:09:19.867Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/5f/ed01f9a3cdffbd5a008556fc7b2a08ddb1cc6ace7effa7340604b1d16699/docstring_parser-0.18.0-py3-none-any.whl", hash = "sha256:b3fcbed555c47d8479be0796ef7e19c2670d428d72e96da63f3a40122860374b", size = 22484, upload-time = "2026-04-14T04:09:18.638Z" }, -] - -[[package]] -name = "docutils" -version = "0.22.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, -] - -[[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.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "fastmcp" -version = "3.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp-slim", extra = ["client", "server"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1a/b8/aff9378edc9438916ce5f06ac31bc1d60dd99e1a82411e186f1c38e20a8b/fastmcp-3.3.0.tar.gz", hash = "sha256:48c7fffdb6865cb9658ac02c2ff589caaaa3cd68e2cc37ed51402fa46e46c2eb", size = 28804871, upload-time = "2026-05-15T02:04:59.357Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/bd/4f21d58764e1f1e1237e29bc58bb1b5863fde2297796c8e96301d334fe2d/fastmcp-3.3.0-py3-none-any.whl", hash = "sha256:e6b1dc391a9fcc4b6a7f0bb7c2481d1aac8d03eefe818908c69909795a39d51b", size = 7903, upload-time = "2026-05-15T02:05:02.24Z" }, -] - -[[package]] -name = "fastmcp-slim" -version = "3.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "platformdirs" }, - { name = "pydantic", extra = ["email"] }, - { name = "pydantic-settings" }, - { name = "python-dotenv" }, - { name = "rich" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/db/6e/f4ba7d4f5d586ee85b7e2dd4feff89112de0e4cedc6a5fda794adb6b99c2/fastmcp_slim-3.3.0.tar.gz", hash = "sha256:56fd0077226b8dbf0bba253f9baaad5d97a3f74eb62750d6997128486b91b0ba", size = 567972, upload-time = "2026-05-15T02:04:34.015Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/36/fa34dd253426e03e4f68918ca700758a22f3600fb6d4dc5a260b3d152c82/fastmcp_slim-3.3.0-py3-none-any.whl", hash = "sha256:7478c5220e06e5ff4f9cb5270e46c6b6d0f44ec0d79372a572b0a71195baa69a", size = 739366, upload-time = "2026-05-15T02:04:32.528Z" }, -] - -[package.optional-dependencies] -client = [ - { name = "authlib" }, - { name = "exceptiongroup" }, - { name = "httpx" }, - { name = "mcp" }, - { name = "opentelemetry-api" }, - { name = "py-key-value-aio", extra = ["filetree", "keyring", "memory"] }, -] -server = [ - { name = "authlib" }, - { name = "cyclopts" }, - { name = "exceptiongroup" }, - { name = "griffelib" }, - { name = "httpx" }, - { name = "jsonref" }, - { name = "jsonschema-path" }, - { name = "mcp" }, - { name = "openapi-pydantic" }, - { name = "opentelemetry-api" }, - { name = "packaging" }, - { name = "py-key-value-aio", extra = ["filetree", "keyring", "memory"] }, - { name = "pyperclip" }, - { name = "python-multipart" }, - { name = "pyyaml" }, - { name = "uncalled-for" }, - { name = "uvicorn" }, - { name = "watchfiles" }, - { name = "websockets" }, -] - -[[package]] -name = "frozenlist" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, - { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, - { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, - { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, - { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, - { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, - { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, - { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, - { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, - { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, - { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, - { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, - { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, - { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, - { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, - { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, - { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, - { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, - { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, - { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, - { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, - { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, - { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, - { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, - { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, - { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, - { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, - { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, - { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, - { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, - { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, - { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, - { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, - { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, - { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, - { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, - { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, - { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, - { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, - { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, - { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, - { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, - { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, - { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, - { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, - { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, - { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, - { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, - { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, - { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, - { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, - { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, - { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, - { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, - { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, - { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, - { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, - { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, - { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, - { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, - { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, - { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, - { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, -] - -[[package]] -name = "griffelib" -version = "2.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/82/74f4a3310cdabfbb10da554c3a672847f1ed33c6f61dd472681ce7f1fe67/griffelib-2.0.2.tar.gz", hash = "sha256:3cf20b3bc470e83763ffbf236e0076b1211bac1bc67de13daf494640f2de707e", size = 166461, upload-time = "2026-03-27T11:34:51.091Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/8c/c9138d881c79aa0ea9ed83cbd58d5ca75624378b38cee225dcf5c42cc91f/griffelib-2.0.2-py3-none-any.whl", hash = "sha256:925c857658fb1ba40c0772c37acbc2ab650bd794d9c1b9726922e36ea4117ea1", size = 142357, upload-time = "2026-03-27T11:34:46.275Z" }, -] - -[[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 = "hanzo-iam" -version = "1.29.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "cryptography" }, - { name = "pyjwt" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3e/4b/11d440a3a99e5b7967ae1a9d56f4bee7c9879f7e2a56a9a1398a3d931064/hanzo_iam-1.29.0.tar.gz", hash = "sha256:5979db89b791be181c259d103822424f389be5d82bff677bee9fad3f213f2578", size = 25123, upload-time = "2025-04-09T18:51:53.64Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/26/bc5dbd90e5fd0f2666646c2b4831cc4fef6867bbad8091da496851fe600c/hanzo_iam-1.29.0-py2.py3-none-any.whl", hash = "sha256:22aba50d91d642843570fd73853783cc26bad7ac618c778d2df49e54bd43bed9", size = 47149, upload-time = "2025-04-09T18:51:52.134Z" }, -] - -[[package]] -name = "hanzo-tools" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/3e/2d94dc54e202bdb11f6e4597dd68eebc554d2b92fffb4f6918cdf3f91fe2/hanzo_tools-0.3.0.tar.gz", hash = "sha256:d00cb3212a707e22f9bb5a21f0f9eb34a74f22ff2b5f24e2f8b6321f9880e2fb", size = 10929, upload-time = "2025-12-27T18:56:38.843Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/07/6ebbcf371aafa5f2d171de2916ef92c73978b927b34a8863af53e1b1a80b/hanzo_tools-0.3.0-py3-none-any.whl", hash = "sha256:c7b0f6f7c3089f06329bc1aaca39fbce4b7108fdbd048e2bbc450a3aff9941f2", size = 11928, upload-time = "2025-12-27T18:56:37.528Z" }, -] - -[[package]] -name = "hanzo-tools-auth" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-iam" }, - { name = "hanzo-tools-core" }, - { name = "httpx" }, - { name = "pyjwt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/07/0c8a54dae8052ebe7880a7a482142a885738f03b830fae05f7930181dea2/hanzo_tools_auth-0.1.0.tar.gz", hash = "sha256:27fa0d7efeda058cf1c6e4ad593f1da73bf3d1e501f59685178b37da28cab1ad", size = 6150, upload-time = "2026-02-25T05:57:54.598Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/63/6b82a88840210518ce9a3eefa9f96dd6febb9656d49c68c5734a863aed6a/hanzo_tools_auth-0.1.0-py3-none-any.whl", hash = "sha256:38e75848892179dd5e3da184605d0dc7488070833cad21872a012e651a1b9f70", size = 7386, upload-time = "2026-02-25T05:57:51.519Z" }, -] - -[[package]] -name = "hanzo-tools-core" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/19/fe28273c3e6de3872eebb37c76bd873e7fc9e9c290da3316bc1abac1fda2/hanzo_tools_core-0.3.0.tar.gz", hash = "sha256:7351b37c33cc0bba08fea2c697f832b4fdd1a31336ee4fc946194636ac8341c5", size = 17451, upload-time = "2026-02-21T19:58:30.28Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/36/f2/a0bdbc43b160a32dae34eac683779a5aa70d326665b758a4972101fb0b44/hanzo_tools_core-0.3.0-py3-none-any.whl", hash = "sha256:75bba9a3fb6f9203e9bd6c14fff86fdebbd50b0b7b44f6fb663ddfccbba25bf3", size = 18252, upload-time = "2026-02-21T19:58:29.008Z" }, -] - -[[package]] -name = "hanzo-tools-iam" -version = "0.1.0" -source = { editable = "." } -dependencies = [ - { name = "hanzo-tools-auth" }, - { name = "hanzo-tools-core" }, - { name = "httpx" }, -] - -[package.metadata] -requires-dist = [ - { name = "hanzo-tools-auth", specifier = ">=0.1.0" }, - { name = "hanzo-tools-core", specifier = ">=0.1.0" }, - { name = "httpx", specifier = ">=0.27.0" }, -] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, -] - -[[package]] -name = "idna" -version = "3.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, -] - -[[package]] -name = "jaraco-context" -version = "6.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/50/4763cd07e722bb6285316d390a164bc7e479db9d90daa769f22578f698b4/jaraco_context-6.1.2.tar.gz", hash = "sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3", size = 16801, upload-time = "2026-03-20T22:13:33.922Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/58/bc8954bda5fcda97bd7c19be11b85f91973d67a706ed4a3aec33e7de22db/jaraco_context-6.1.2-py3-none-any.whl", hash = "sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535", size = 7871, upload-time = "2026-03-20T22:13:32.808Z" }, -] - -[[package]] -name = "jaraco-functools" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, -] - -[[package]] -name = "joserfc" -version = "1.6.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3b/dc/5f768c2e391e9afabe5d18e3221346deb5fb6338565f1ccc9e7c6d7befdd/joserfc-1.6.5.tar.gz", hash = "sha256:1482a7db78fb4602e44ed89e51b599d052e091288c7c532c5b694e20149dec48", size = 231881, upload-time = "2026-05-06T04:58:13.408Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/3b/ad1cb22e75c963b1f07c8a2329bf47227ce7e4361df5eb2fb101b2ce33ef/joserfc-1.6.5-py3-none-any.whl", hash = "sha256:e9878a0f8243fe7b95e11fdda81374ca9f7a689e302751579d3dfdeec559675e", size = 70464, upload-time = "2026-05-06T04:58:11.668Z" }, -] - -[[package]] -name = "jsonref" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[package]] -name = "jsonschema-path" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pathable" }, - { name = "pyyaml" }, - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/01/86/cfee6dd25843bec0760f456599a4f7e7e40221a934b9229fda0662c859bc/jsonschema_path-0.4.6.tar.gz", hash = "sha256:c89eb635f4d497c9ac328eeff359c489755838806a7d033510a692e9576f5c4b", size = 15302, upload-time = "2026-04-27T18:57:08.412Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/43/3d3065c05a04bb550c143bfbb8e4fd7022cd327e1082bf257bac74923783/jsonschema_path-0.4.6-py3-none-any.whl", hash = "sha256:451354b5311fa955c3144e6e4e255388c751c0121c5570ec5bb9291dd42d08c9", size = 19565, upload-time = "2026-04-27T18:57:06.792Z" }, -] - -[[package]] -name = "jsonschema-specifications" -version = "2025.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "keyring" -version = "25.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, -] - -[[package]] -name = "markdown-it-py" -version = "4.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, -] - -[[package]] -name = "mcp" -version = "1.27.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyjwt", extra = ["crypto"] }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/83/d1efe7c2980d8a3afa476f4e3d42d53dd54c0ab94c27bee5d755b45c8b73/mcp-1.27.1.tar.gz", hash = "sha256:0f47e1820f8f8f941466b39749eb1d1839a04caddca2bc60e9d46e8a99914924", size = 608458, upload-time = "2026-05-08T16:50:12.601Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/73/42d9596facebdb533b7f0b86c1b0364ef350d1f8ba78b1052e8a58b48b65/mcp-1.27.1-py3-none-any.whl", hash = "sha256:1af3c4203b329430fde7a87b4fcb6392a041f5cb851fd68fc674016ab4e7c06f", size = 216260, upload-time = "2026-05-08T16:50:10.547Z" }, -] - -[[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 = "11.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/f7/139d22fef48ac78127d18e01d80cf1be40236ae489769d17f35c3d425293/more_itertools-11.0.2.tar.gz", hash = "sha256:392a9e1e362cbc106a2457d37cabf9b36e5e12efd4ebff1654630e76597df804", size = 144659, upload-time = "2026-04-09T15:01:33.297Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/98/6af411189d9413534c3eb691182bff1f5c6d44ed2f93f2edfe52a1bbceb8/more_itertools-11.0.2-py3-none-any.whl", hash = "sha256:6e35b35f818b01f691643c6c611bc0902f2e92b46c18fffa77ae1e7c46e912e4", size = 71939, upload-time = "2026-04-09T15:01:32.21Z" }, -] - -[[package]] -name = "multidict" -version = "6.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, - { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, - { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, - { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, - { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, - { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, - { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, - { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, - { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, - { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, - { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, - { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, - { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, - { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, - { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, - { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, - { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, - { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, - { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, - { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, - { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, - { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, - { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, - { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, - { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, - { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, - { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, - { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, - { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, - { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, - { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, - { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, - { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, - { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, - { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, - { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, - { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, - { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, - { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, - { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, - { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, - { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, - { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, - { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, - { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, - { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, - { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, - { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, - { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, - { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, - { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, - { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, - { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, - { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, - { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, - { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, - { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, - { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, - { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, - { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, - { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, - { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, - { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, - { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, - { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, - { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, - { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, - { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, - { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, - { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, - { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, - { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, - { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, - { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, - { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, - { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, - { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, -] - -[[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 = "opentelemetry-api" -version = "1.41.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fa/fc/b7564cbef36601aef0d6c9bc01f7badb64be8e862c2e1c3c5c3b43b53e4f/opentelemetry_api-1.41.1.tar.gz", hash = "sha256:0ad1814d73b875f84494387dae86ce0b12c68556331ce6ce8fe789197c949621", size = 71416, upload-time = "2026-04-24T13:15:38.262Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/29/59/3e7118ed140f76b0982ba4321bdaed1997a0473f9720de2d10788a577033/opentelemetry_api-1.41.1-py3-none-any.whl", hash = "sha256:a22df900e75c76dc08440710e51f52f1aa6b451b429298896023e60db5b3139f", size = 69007, upload-time = "2026-04-24T13:15:15.662Z" }, -] - -[[package]] -name = "packaging" -version = "26.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, -] - -[[package]] -name = "pathable" -version = "0.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/55/b748445cb4ea6b125626f15379be7c96d1035d4fa3e8fee362fa92298abf/pathable-0.5.0.tar.gz", hash = "sha256:d81938348a1cacb525e7c75166270644782c0fb9c8cecc16be033e71427e0ef1", size = 16655, upload-time = "2026-02-20T08:47:00.748Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/96/5a770e5c461462575474468e5af931cff9de036e7c2b4fea23c1c58d2cbe/pathable-0.5.0-py3-none-any.whl", hash = "sha256:646e3d09491a6351a0c82632a09c02cdf70a252e73196b36d8a15ba0a114f0a6", size = 16867, upload-time = "2026-02-20T08:46:59.536Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.9.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, -] - -[[package]] -name = "propcache" -version = "0.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/cb/e27bc2b2737a0bb49962b275efa051e8f1c35a936df7d5139b6b658b7dc9/propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba", size = 95887, upload-time = "2026-05-08T21:00:11.277Z" }, - { url = "https://files.pythonhosted.org/packages/e6/13/b8ae04c59392f8d11c6cd9fb4011d1dc7c86b81225c770280300e259ffe1/propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a", size = 54654, upload-time = "2026-05-08T21:00:12.604Z" }, - { url = "https://files.pythonhosted.org/packages/2c/7d/49777a3e20b55863d4794384a38acd460c04157b0a00f8602b0d508b8431/propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf", size = 55190, upload-time = "2026-05-08T21:00:13.935Z" }, - { url = "https://files.pythonhosted.org/packages/44/c7/085d0cd63062e84044e3f05797749c3f8e3938ff3aeb0eb2f69d43fafc91/propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144", size = 59995, upload-time = "2026-05-08T21:00:15.526Z" }, - { url = "https://files.pythonhosted.org/packages/9c/42/32cf8e3009e92b2645cf1e944f701e8ea4e924dffde1ee26db860bcbf7e4/propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9", size = 63422, upload-time = "2026-05-08T21:00:16.824Z" }, - { url = "https://files.pythonhosted.org/packages/9e/1b/f112433f99fc979431b87a39ef169e3f8df070d99a72792c56d6937ac48b/propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42", size = 64342, upload-time = "2026-05-08T21:00:18.362Z" }, - { url = "https://files.pythonhosted.org/packages/14/15/5574111ae50dd6e879456888c0eadd4c5a869959775854e18e18a6b345f3/propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476", size = 61639, upload-time = "2026-05-08T21:00:19.692Z" }, - { url = "https://files.pythonhosted.org/packages/cc/da/4d775080b1490c0ae604acda868bd71aabe3a89ed16f2aa4339eb8a283e7/propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba", size = 61588, upload-time = "2026-05-08T21:00:21.155Z" }, - { url = "https://files.pythonhosted.org/packages/04/ac/f076982cbe2195ee9cf32de5a1e46951d9fb399fc207f390562dd0fd8fb2/propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a", size = 60029, upload-time = "2026-05-08T21:00:22.713Z" }, - { url = "https://files.pythonhosted.org/packages/70/60/189be62e0dd898dce3b331e1b8c7a543cd3a405ac0c81fe8ee8a9d5d77e1/propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64", size = 56774, upload-time = "2026-05-08T21:00:24.001Z" }, - { url = "https://files.pythonhosted.org/packages/ea/9e/93377b9c7939c1ffae98f878dee955efadfd638078bc86dbc21f9d52f651/propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913", size = 63532, upload-time = "2026-05-08T21:00:25.545Z" }, - { url = "https://files.pythonhosted.org/packages/14/f9/590ef6cfb9b8028d516d287812ece32bb0bc5f11fbb9c8bf6b2e6313fec8/propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1", size = 61592, upload-time = "2026-05-08T21:00:27.186Z" }, - { url = "https://files.pythonhosted.org/packages/b4/5e/70958b3034c297a630bba2f17ca7abc2d5f39a803ad7e370ab79d1ecd022/propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33", size = 64788, upload-time = "2026-05-08T21:00:28.8Z" }, - { url = "https://files.pythonhosted.org/packages/12/fd/77fe5936d8c3086ca9048f7f415f122ed82e53884a9ec193646b42deef06/propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a", size = 62514, upload-time = "2026-05-08T21:00:30.098Z" }, - { url = "https://files.pythonhosted.org/packages/cf/74/66bd798b5b3be70aa1b391f5cc9d6a0a5532d7fd3b19ec0b213e72e6ad9d/propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031", size = 39018, upload-time = "2026-05-08T21:00:31.622Z" }, - { url = "https://files.pythonhosted.org/packages/61/7c/5c0d34aa3024694d6dcb9271cdbdd08c4e47c1c0ad95ec7e7bc74cdea145/propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42", size = 42322, upload-time = "2026-05-08T21:00:32.918Z" }, - { url = "https://files.pythonhosted.org/packages/4d/91/875812f1a3feb20ceba818ef39fbe4d92f1081e04ac815c822496d0d038b/propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84", size = 38172, upload-time = "2026-05-08T21:00:35.124Z" }, - { url = "https://files.pythonhosted.org/packages/c5/09/f049e45385503fe67db75a6b6186a7b9f0c3930366dc960522c312a825b1/propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a", size = 94457, upload-time = "2026-05-08T21:00:36.355Z" }, - { url = "https://files.pythonhosted.org/packages/6b/65/83d1d05655baf63113731bd5a1008435e14f8d1e5a06cbe4ec5b23ad7a31/propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117", size = 53835, upload-time = "2026-05-08T21:00:38.072Z" }, - { url = "https://files.pythonhosted.org/packages/a9/12/a6ba6482bb5ea3260c000c9b20881c95fa11c6b30173715668259f844ed7/propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098", size = 54545, upload-time = "2026-05-08T21:00:39.319Z" }, - { url = "https://files.pythonhosted.org/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4", size = 59886, upload-time = "2026-05-08T21:00:40.621Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e4/5d7663dc8235956c8f5281698a3af1d351d8820341ddd890f59d9a9127f2/propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e", size = 63261, upload-time = "2026-05-08T21:00:41.775Z" }, - { url = "https://files.pythonhosted.org/packages/4a/4a/15a03adee24d6350da4292caeac44c34c033d2afe5e87eb370f38854560f/propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7", size = 64184, upload-time = "2026-05-08T21:00:43.018Z" }, - { url = "https://files.pythonhosted.org/packages/8b/c6/979176efdaa3d239e36d503d5af63a0a773b36662ed8f52e5b6a6d9fd40e/propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d", size = 61534, upload-time = "2026-05-08T21:00:44.507Z" }, - { url = "https://files.pythonhosted.org/packages/c8/22/63e8cd1bae4c2d2be6493b6b7d10566ddafad88137cfbc99964a1119853c/propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a", size = 61500, upload-time = "2026-05-08T21:00:45.796Z" }, - { url = "https://files.pythonhosted.org/packages/60/5a/28e5d9acbac1cc9ccb67045e8c1b943aa8d79fdf39c93bd73cacd68008ea/propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2", size = 59994, upload-time = "2026-05-08T21:00:47.093Z" }, - { url = "https://files.pythonhosted.org/packages/f3/40/db650677f554a95b9c01a7c9d93d629e93a15562f5deb4573c9ee136fed2/propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa", size = 56884, upload-time = "2026-05-08T21:00:48.376Z" }, - { url = "https://files.pythonhosted.org/packages/80/45/70b39b89516ff8b96bf732fa6fded8cef20f293cb1508690101c3c07ec51/propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853", size = 63464, upload-time = "2026-05-08T21:00:49.954Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e2/fa59d3a89eac5534293124af4f1d0d0ada091ce4a0ab4610ce03fd2bdd8d/propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a", size = 61588, upload-time = "2026-05-08T21:00:51.281Z" }, - { url = "https://files.pythonhosted.org/packages/0b/97/efb547a55c4bc7381cfb202d6a2239ac621045277bc1ea5dfd3a7f0516c0/propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704", size = 64667, upload-time = "2026-05-08T21:00:52.602Z" }, - { url = "https://files.pythonhosted.org/packages/92/56/f5c7d9b4b7595d5127da38974d791b2153f3d1eae6c674af3583ace92ad3/propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4", size = 62463, upload-time = "2026-05-08T21:00:54.303Z" }, - { url = "https://files.pythonhosted.org/packages/bd/3b/484a3a65fc9f9f60c41dcd17b428bace5389544e2c680994534a20755066/propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d", size = 38621, upload-time = "2026-05-08T21:00:55.808Z" }, - { url = "https://files.pythonhosted.org/packages/1c/fd/3f0f10dba4dabad3bf53102be007abf55481067952bde0fdddff439e7c61/propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757", size = 41649, upload-time = "2026-05-08T21:00:57.061Z" }, - { url = "https://files.pythonhosted.org/packages/90/ec/6ce619cc32bb500a482f811f9cd509368b4e58e638d13f2c68f370d6b475/propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f", size = 37636, upload-time = "2026-05-08T21:00:58.646Z" }, - { url = "https://files.pythonhosted.org/packages/1b/82/c1d268bbbf2ef981c5bf0fbbe746db617c66e3bcefe431a1aa8943fbe23a/propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d", size = 98872, upload-time = "2026-05-08T21:00:59.889Z" }, - { url = "https://files.pythonhosted.org/packages/f4/d4/52c871e73e864e6b34c0e2d58ac1ec5ccd149497ddc7ad2137ae98323a35/propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa", size = 56257, upload-time = "2026-05-08T21:01:01.195Z" }, - { url = "https://files.pythonhosted.org/packages/67/f0/9b90ca2a210b3d09bcfcd96ecd0f55545c091535abce2a45de2775cfd357/propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94", size = 56696, upload-time = "2026-05-08T21:01:02.941Z" }, - { url = "https://files.pythonhosted.org/packages/9d/0e/6e9d4ba07c8e56e21ddec1e75f12148142b21ca83a51871babce095334f4/propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164", size = 62378, upload-time = "2026-05-08T21:01:04.475Z" }, - { url = "https://files.pythonhosted.org/packages/65/19/c10badaa463dde8a27ce884f8ee2ec37e6035b7c9f5ff0c8f74f06f08dac/propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f", size = 65283, upload-time = "2026-05-08T21:01:05.959Z" }, - { url = "https://files.pythonhosted.org/packages/b0/b6/93bea99ca80e19cef6512a8580e5b7857bbe09422d9daa7fd4ef5723306c/propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c", size = 66616, upload-time = "2026-05-08T21:01:07.228Z" }, - { url = "https://files.pythonhosted.org/packages/83/e4/5c7462e50625f051f37fb38b8224f7639f667184bbd34424ec83819bb1b7/propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc", size = 63773, upload-time = "2026-05-08T21:01:08.514Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b6/99238894047b13c823be25027e736626cd414a52a5e30d2c3347c2733529/propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f", size = 63664, upload-time = "2026-05-08T21:01:09.874Z" }, - { url = "https://files.pythonhosted.org/packages/85/1e/a3a1a63116a2b8edb415a8bb9a6f0c34bd03830b1e18e8ce2904e1dc1cf4/propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb", size = 62643, upload-time = "2026-05-08T21:01:11.132Z" }, - { url = "https://files.pythonhosted.org/packages/e4/03/893cf147de2fc6543c5eaa07ad833170e7e2a2385725bbebe8c0503723bb/propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751", size = 59595, upload-time = "2026-05-08T21:01:12.387Z" }, - { url = "https://files.pythonhosted.org/packages/86/3b/04c1a2e12c57766568ba75ba72b3bf2042818d4c1425fab6fc07155c7cff/propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836", size = 65711, upload-time = "2026-05-08T21:01:13.676Z" }, - { url = "https://files.pythonhosted.org/packages/1c/34/80f8d0099f8d6bacc4de1624c85672681c8cd1149ca2da0e38fd120b817f/propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f", size = 64247, upload-time = "2026-05-08T21:01:14.936Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1a/8b08f3a5f1037e9e370c55883ceeeee0f6dd0416fb2d2d67b8bfc91f2a79/propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55", size = 67102, upload-time = "2026-05-08T21:01:16.281Z" }, - { url = "https://files.pythonhosted.org/packages/34/68/8bdb7bb7756d76e005490649d10e4a8369e610c74d619f71e1aedf889e9c/propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568", size = 64964, upload-time = "2026-05-08T21:01:17.57Z" }, - { url = "https://files.pythonhosted.org/packages/0a/aa/50fb0b5d3968b61a510926ff8b8465f1d6e976b3ab74496d7a4b9fc42515/propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191", size = 42546, upload-time = "2026-05-08T21:01:18.946Z" }, - { url = "https://files.pythonhosted.org/packages/ae/4c/0ddbae64321bd4a95bcbfc19307238016b5b1fee645c84626c8d539e5b74/propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7", size = 46330, upload-time = "2026-05-08T21:01:20.162Z" }, - { url = "https://files.pythonhosted.org/packages/00/d9/9cddc8efb78d8af264c5ec9f6d10b62f57c515feda8d321595f56010fb23/propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96", size = 40521, upload-time = "2026-05-08T21:01:21.399Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ea/23ee535d90ce8bcc465a3028eb3cc0ce3bd1005f4bb27710b30587de798d/propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999", size = 94662, upload-time = "2026-05-08T21:01:22.683Z" }, - { url = "https://files.pythonhosted.org/packages/b5/06/c5a52f419b5d8972f8d46a7577476090d8e3263ff589ce40b5ca4968d5be/propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e", size = 53928, upload-time = "2026-05-08T21:01:23.986Z" }, - { url = "https://files.pythonhosted.org/packages/63/b1/4260d67d6bd85e58a66b72d54ce15d5de789b6f3870cc6bedf8ff9667401/propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539", size = 54650, upload-time = "2026-05-08T21:01:25.305Z" }, - { url = "https://files.pythonhosted.org/packages/70/06/2f46c318e3307cd7a6a7481def374ce838c0fe20084b39dd54b0879d0e99/propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e", size = 59912, upload-time = "2026-05-08T21:01:26.545Z" }, - { url = "https://files.pythonhosted.org/packages/4c/29/fe1aebec2ce57ab985a9c382bded1124431f85078113aa222c5d278430d4/propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979", size = 63300, upload-time = "2026-05-08T21:01:27.937Z" }, - { url = "https://files.pythonhosted.org/packages/b4/18/2334b26768b6c82be8c69e83671b767d5ef426aa09b0cba6c2ea47816774/propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80", size = 64208, upload-time = "2026-05-08T21:01:29.484Z" }, - { url = "https://files.pythonhosted.org/packages/2b/76/7f1bfd6afff4c5e38e36a3c6d68eb5f4b7311ea80baf693db78d95b603c4/propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825", size = 61633, upload-time = "2026-05-08T21:01:31.068Z" }, - { url = "https://files.pythonhosted.org/packages/c4/46/b3ff8aba2b4953a3e50de2cf72f1b5748b8eca93b15f3dc2c84339084c09/propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39", size = 61724, upload-time = "2026-05-08T21:01:32.374Z" }, - { url = "https://files.pythonhosted.org/packages/c5/01/814cfcafbcff954f94c01cf30e097ddc88a076b5440fbcf4570753437d40/propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4", size = 60069, upload-time = "2026-05-08T21:01:33.67Z" }, - { url = "https://files.pythonhosted.org/packages/da/68/5c6f7622d510cc666a300687e06fd060c1a43361c0c9b20d284f06d8096a/propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5", size = 57099, upload-time = "2026-05-08T21:01:34.915Z" }, - { url = "https://files.pythonhosted.org/packages/55/27/9cb0b4c679124085327957d42521c99dba04c88c90c3e55a6f0b633ebccc/propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702", size = 63391, upload-time = "2026-05-08T21:01:36.231Z" }, - { url = "https://files.pythonhosted.org/packages/f0/9d/7258aaa5bdf60fc6f27591eef6fe52768cb0beda7140be477c8b12c9794a/propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3", size = 61626, upload-time = "2026-05-08T21:01:37.545Z" }, - { url = "https://files.pythonhosted.org/packages/8e/0d/41c602003e8a9b16fe1e7eadf62c7bfba9d5474370b24200bf48b315f45f/propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5", size = 64781, upload-time = "2026-05-08T21:01:38.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f3/38e66b1856e9bd079deea015bc4a55f7767c0e4db2f7dcf69e7e680ba4ce/propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4", size = 62570, upload-time = "2026-05-08T21:01:40.415Z" }, - { url = "https://files.pythonhosted.org/packages/95/ca/bbfe9b910ce57dde8bb4876b4520fc02a4e89497c10de26be936758a3aaa/propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0", size = 39436, upload-time = "2026-05-08T21:01:41.654Z" }, - { url = "https://files.pythonhosted.org/packages/61/d2/45c9defbaa1ea297035d9d4cce9e8f80daafbf19319c6007f157c6256ea9/propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c", size = 42373, upload-time = "2026-05-08T21:01:43.041Z" }, - { url = "https://files.pythonhosted.org/packages/44/68/9ea5103f41d5217d7d6ec24db90018e23aebec070c3f9a6e54d12b841fd8/propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0", size = 38554, upload-time = "2026-05-08T21:01:44.336Z" }, - { url = "https://files.pythonhosted.org/packages/8a/81/fadf555f42d3b762eea8a53950b0489fdc0aa9da5f8ed9e10ce0a4e01b48/propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb", size = 99395, upload-time = "2026-05-08T21:01:45.883Z" }, - { url = "https://files.pythonhosted.org/packages/f5/c9/c61e134a686949cf7971af3a390148b1156f7be81c73bc0cd12c873e2d48/propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078", size = 56653, upload-time = "2026-05-08T21:01:47.307Z" }, - { url = "https://files.pythonhosted.org/packages/cb/73/daf935ea7048ddd7ec8eec5345b4a40b619d2d178b3c0a0900796bc3c794/propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa", size = 56914, upload-time = "2026-05-08T21:01:48.573Z" }, - { url = "https://files.pythonhosted.org/packages/79/9f/aba959b435ea18617edd7cf0a7ad0b9c574b8fc7e3d2cd55fb59cb255d33/propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917", size = 62567, upload-time = "2026-05-08T21:01:49.903Z" }, - { url = "https://files.pythonhosted.org/packages/6c/a1/859942de9a791ff42f6141736f5b37749b8f53e65edfa49638c67dd67e6a/propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe", size = 65542, upload-time = "2026-05-08T21:01:51.204Z" }, - { url = "https://files.pythonhosted.org/packages/b5/61/315bc0fd6c0fc7f80a528b8afd209e5fc4a875ea79571b91b8f50f442907/propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03", size = 66845, upload-time = "2026-05-08T21:01:52.539Z" }, - { url = "https://files.pythonhosted.org/packages/47/f7/9f8122e3132e8e354ac41975ef8f1099be7d5a16bc7ae562734e993665c0/propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335", size = 63985, upload-time = "2026-05-08T21:01:53.847Z" }, - { url = "https://files.pythonhosted.org/packages/c8/54/c317819ec157cbf6f35df9df9657a6f82daf34d5faf15948b2f639c2192e/propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285", size = 63999, upload-time = "2026-05-08T21:01:55.179Z" }, - { url = "https://files.pythonhosted.org/packages/5a/56/387e3f7dfce0a9233df41fb888aa1c30222cb4bbbf09537c02dd9bd85fe2/propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837", size = 62779, upload-time = "2026-05-08T21:01:57.489Z" }, - { url = "https://files.pythonhosted.org/packages/a1/9c/596784cb5824ed61ee960d3f8655a3f0993e107c6e98ab6c818b7fb92ccb/propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8", size = 59796, upload-time = "2026-05-08T21:01:58.736Z" }, - { url = "https://files.pythonhosted.org/packages/c2/3d/1a6cfa1726a48542c1e8784a0761421476a5b68e09b7f36bf95eb954aaba/propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366", size = 66023, upload-time = "2026-05-08T21:02:00.228Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0e/05fd6990369477076e4e280bcb970de760fddf0161a46e988bc95f7940ec/propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56", size = 64448, upload-time = "2026-05-08T21:02:01.888Z" }, - { url = "https://files.pythonhosted.org/packages/cd/86/5f8da315a4309c62c10c0b2516b17492d5d3bbe1bb862b96604db67e2a37/propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d", size = 67329, upload-time = "2026-05-08T21:02:03.484Z" }, - { url = "https://files.pythonhosted.org/packages/da/d3/3368efe79ab21f0cdf86ef49895811c9cc933131d4cde1f28a624e22e712/propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2", size = 65172, upload-time = "2026-05-08T21:02:04.745Z" }, - { url = "https://files.pythonhosted.org/packages/d5/07/127e8b0bacfb325396196f9d976a22453049b89b9b2b08477cc3145faa44/propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821", size = 43813, upload-time = "2026-05-08T21:02:06.025Z" }, - { url = "https://files.pythonhosted.org/packages/88/fb/46dad6c0ae49ed230ab1b16c890c2b6314e2403e6c412976f4a72d64a527/propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370", size = 47764, upload-time = "2026-05-08T21:02:07.353Z" }, - { url = "https://files.pythonhosted.org/packages/e7/c4/a47d0a63aa309d10d59ede6e9d4cff03a344a79d1f0f4cd0cd74997b53e0/propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6", size = 41140, upload-time = "2026-05-08T21:02:09.065Z" }, - { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, -] - -[[package]] -name = "py-key-value-aio" -version = "0.4.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/04/3c/0397c072a38d4bc580994b42e0c90c5f44f679303489e4376289534735e5/py_key_value_aio-0.4.4.tar.gz", hash = "sha256:e3012e6243ed7cc09bb05457bd4d03b1ba5c2b1ca8700096b3927db79ffbbe55", size = 92300, upload-time = "2026-02-16T21:21:43.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/69/f1b537ee70b7def42d63124a539ed3026a11a3ffc3086947a1ca6e861868/py_key_value_aio-0.4.4-py3-none-any.whl", hash = "sha256:18e17564ecae61b987f909fc2cd41ee2012c84b4b1dcb8c055cf8b4bc1bf3f5d", size = 152291, upload-time = "2026-02-16T21:21:44.241Z" }, -] - -[package.optional-dependencies] -filetree = [ - { name = "aiofile" }, - { name = "anyio" }, -] -keyring = [ - { name = "keyring" }, -] -memory = [ - { name = "cachetools" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pydantic" -version = "2.13.4" -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/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, -] - -[package.optional-dependencies] -email = [ - { name = "email-validator" }, -] - -[[package]] -name = "pydantic-core" -version = "2.46.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, - { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, - { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, - { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, - { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, - { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, - { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, - { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, - { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, - { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, - { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, - { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, - { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, - { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, - { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, - { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, - { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, - { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, - { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, - { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, - { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, - { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, - { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, - { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, - { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, - { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, - { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, - { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, - { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, - { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, - { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, - { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, - { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, - { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, - { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, - { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, - { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, - { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, - { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, - { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, - { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, - { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, - { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, - { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, - { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, - { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, - { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, - { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, - { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, - { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, - { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, - { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, - { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, - { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, - { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, - { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, - { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, - { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, -] - -[[package]] -name = "pydantic-settings" -version = "2.14.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/07/60/1d1e59c9c90d54591469ada7d268251f71c24bdb765f1a8a832cee8c6653/pydantic_settings-2.14.1.tar.gz", hash = "sha256:e874d3bec7e787b0c9958277956ed9b4dd5de6a80e162188fdaff7c5e26fd5fa", size = 235551, upload-time = "2026-05-08T13:40:06.542Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/8d/f1af3832f5e6eb13ba94ee809e72b8ecb5eef226d27ee0bef7d963d943c7/pydantic_settings-2.14.1-py3-none-any.whl", hash = "sha256:6e3c7edfd8277687cdc598f56e5cff0e9bfff0910a3749deaa8d4401c3a2b9de", size = 60964, upload-time = "2026-05-08T13:40:04.958Z" }, -] - -[[package]] -name = "pygments" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, -] - -[[package]] -name = "pyjwt" -version = "2.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, -] - -[package.optional-dependencies] -crypto = [ - { name = "cryptography" }, -] - -[[package]] -name = "pyperclip" -version = "1.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.28" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/54/a85eb421fbdd5007bc5af39d0f4ed9fa609e0fedbfdc2adcf0b34526870e/python_multipart-0.0.28.tar.gz", hash = "sha256:8550da197eac0f7ab748961fc9509b999fa2662ea25cef857f05249f6893c0f8", size = 45314, upload-time = "2026-05-10T11:05:16.596Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/a2/43bbc5860b5034e2af4ef99a0e04d726ff329c43e192ef3abaa8d7ecfce5/python_multipart-0.0.28-py3-none-any.whl", hash = "sha256:10faac07eb966c3f48dc415f9dee46c04cb10d58d30a35677db8027c825ed9b6", size = 29438, upload-time = "2026-05-10T11:05:15.052Z" }, -] - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { 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 = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "referencing" -version = "0.37.0" -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/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, -] - -[[package]] -name = "requests" -version = "2.34.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, -] - -[[package]] -name = "rich" -version = "15.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, -] - -[[package]] -name = "rich-rst" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, -] - -[[package]] -name = "secretstorage" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, -] - -[[package]] -name = "sse-starlette" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "starlette" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f7/2b/58abc2d1fd397e7dde08e947e05c884d8ef2f78d5e2588c17a12d42d6994/sse_starlette-3.4.4.tar.gz", hash = "sha256:07e0fa0460138baf25cdd5fb28683472c3995dc1642225191b3832d62526bcb0", size = 31819, upload-time = "2026-05-12T17:37:17.019Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/67/805710444ea8cc75fbf70b920ed431a560c4bf9c57f7d5a3117213189399/sse_starlette-3.4.4-py3-none-any.whl", hash = "sha256:3f4dd50d8aed2771a091f3a83000323fc3844541c16b4fe585ae2420cc6df973", size = 16514, upload-time = "2026-05-12T17:37:15.601Z" }, -] - -[[package]] -name = "starlette" -version = "1.0.0" -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/81/69/17425771797c36cded50b7fe44e850315d039f28b15901ab44839e70b593/starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149", size = 2655289, upload-time = "2026-03-22T18:29:46.779Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "uncalled-for" -version = "0.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/82/345cc927f7fbdae6065e7768759932fcc827fc20b29b45dfbafa2f1f7da4/uncalled_for-0.3.2.tar.gz", hash = "sha256:89f5dbcd71e2b8f47c030b1fa302e6cce2ec795d1ac565eeb6525c5fe55cb8a2", size = 50032, upload-time = "2026-05-06T13:38:25.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/25/2c87754f3a9e692315f7b811244090e68f362979fc8886b3fbd2985a1d8c/uncalled_for-0.3.2-py3-none-any.whl", hash = "sha256:0ff60b142c7d1f8070bde9d42afaa70aedc77dcc10998c227687e9c15713418e", size = 11444, upload-time = "2026-05-06T13:38:24.025Z" }, -] - -[[package]] -name = "urllib3" -version = "2.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.47.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f6/b1/8e7077a8641086aea449e1b5752a570f1b5906c64e0a33cd6d93b63a066b/uvicorn-0.47.0.tar.gz", hash = "sha256:7c9a0ea1a9414106bbab7324609c162d8fa0cdcdcb703060987269d77c7bb533", size = 90582, upload-time = "2026-05-14T18:16:54.455Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/15/41/ac2dfdbc1f60c7af4f994c7a335cfa7040c01642b605d65f611cecc2a1e4/uvicorn-0.47.0-py3-none-any.whl", hash = "sha256:2c5715bc12d1892d84752049f400cd1c3cb018514967fdfeb97640443a6a9432", size = 71301, upload-time = "2026-05-14T18:16:51.762Z" }, -] - -[[package]] -name = "watchfiles" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" }, - { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" }, - { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" }, - { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" }, - { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" }, - { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" }, - { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" }, - { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" }, - { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" }, - { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" }, - { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, - { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, - { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, - { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, - { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, - { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, - { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, - { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, - { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, - { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, - { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, - { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, - { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, - { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, - { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, - { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, - { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, - { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, - { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, - { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, - { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, - { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, - { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, - { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, - { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, - { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, - { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, - { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, - { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, - { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, - { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, -] - -[[package]] -name = "websockets" -version = "16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, - { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, - { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, - { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, - { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, - { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, - { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, - { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, - { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, - { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, -] - -[[package]] -name = "yarl" -version = "1.23.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "multidict" }, - { name = "propcache" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/8a/94615bc31022f711add374097ad4144d569e95ff3c38d39215d07ac153a0/yarl-1.23.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860", size = 124737, upload-time = "2026-03-01T22:05:12.897Z" }, - { url = "https://files.pythonhosted.org/packages/e3/6f/c6554045d59d64052698add01226bc867b52fe4a12373415d7991fdca95d/yarl-1.23.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069", size = 87029, upload-time = "2026-03-01T22:05:14.376Z" }, - { url = "https://files.pythonhosted.org/packages/19/2a/725ecc166d53438bc88f76822ed4b1e3b10756e790bafd7b523fe97c322d/yarl-1.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25", size = 86310, upload-time = "2026-03-01T22:05:15.71Z" }, - { url = "https://files.pythonhosted.org/packages/99/30/58260ed98e6ff7f90ba84442c1ddd758c9170d70327394a6227b310cd60f/yarl-1.23.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", size = 97587, upload-time = "2026-03-01T22:05:17.384Z" }, - { url = "https://files.pythonhosted.org/packages/76/0a/8b08aac08b50682e65759f7f8dde98ae8168f72487e7357a5d684c581ef9/yarl-1.23.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", size = 92528, upload-time = "2026-03-01T22:05:18.804Z" }, - { url = "https://files.pythonhosted.org/packages/52/07/0b7179101fe5f8385ec6c6bb5d0cb9f76bd9fb4a769591ab6fb5cdbfc69a/yarl-1.23.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", size = 105339, upload-time = "2026-03-01T22:05:20.235Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8a/36d82869ab5ec829ca8574dfcb92b51286fcfb1e9c7a73659616362dc880/yarl-1.23.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", size = 105061, upload-time = "2026-03-01T22:05:22.268Z" }, - { url = "https://files.pythonhosted.org/packages/66/3e/868e5c3364b6cee19ff3e1a122194fa4ce51def02c61023970442162859e/yarl-1.23.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", size = 100132, upload-time = "2026-03-01T22:05:23.638Z" }, - { url = "https://files.pythonhosted.org/packages/cf/26/9c89acf82f08a52cb52d6d39454f8d18af15f9d386a23795389d1d423823/yarl-1.23.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", size = 99289, upload-time = "2026-03-01T22:05:25.749Z" }, - { url = "https://files.pythonhosted.org/packages/6f/54/5b0db00d2cb056922356104468019c0a132e89c8d3ab67d8ede9f4483d2a/yarl-1.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", size = 96950, upload-time = "2026-03-01T22:05:27.318Z" }, - { url = "https://files.pythonhosted.org/packages/f6/40/10fa93811fd439341fad7e0718a86aca0de9548023bbb403668d6555acab/yarl-1.23.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", size = 93960, upload-time = "2026-03-01T22:05:28.738Z" }, - { url = "https://files.pythonhosted.org/packages/bc/d2/8ae2e6cd77d0805f4526e30ec43b6f9a3dfc542d401ac4990d178e4bf0cf/yarl-1.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", size = 104703, upload-time = "2026-03-01T22:05:30.438Z" }, - { url = "https://files.pythonhosted.org/packages/2f/0c/b3ceacf82c3fe21183ce35fa2acf5320af003d52bc1fcf5915077681142e/yarl-1.23.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", size = 98325, upload-time = "2026-03-01T22:05:31.835Z" }, - { url = "https://files.pythonhosted.org/packages/9d/e0/12900edd28bdab91a69bd2554b85ad7b151f64e8b521fe16f9ad2f56477a/yarl-1.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", size = 105067, upload-time = "2026-03-01T22:05:33.358Z" }, - { url = "https://files.pythonhosted.org/packages/15/61/74bb1182cf79c9bbe4eb6b1f14a57a22d7a0be5e9cedf8e2d5c2086474c3/yarl-1.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", size = 100285, upload-time = "2026-03-01T22:05:35.4Z" }, - { url = "https://files.pythonhosted.org/packages/69/7f/cd5ef733f2550de6241bd8bd8c3febc78158b9d75f197d9c7baa113436af/yarl-1.23.0-cp312-cp312-win32.whl", hash = "sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d", size = 82359, upload-time = "2026-03-01T22:05:36.811Z" }, - { url = "https://files.pythonhosted.org/packages/f5/be/25216a49daeeb7af2bec0db22d5e7df08ed1d7c9f65d78b14f3b74fd72fc/yarl-1.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e", size = 87674, upload-time = "2026-03-01T22:05:38.171Z" }, - { url = "https://files.pythonhosted.org/packages/d2/35/aeab955d6c425b227d5b7247eafb24f2653fedc32f95373a001af5dfeb9e/yarl-1.23.0-cp312-cp312-win_arm64.whl", hash = "sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9", size = 81879, upload-time = "2026-03-01T22:05:40.006Z" }, - { url = "https://files.pythonhosted.org/packages/9a/4b/a0a6e5d0ee8a2f3a373ddef8a4097d74ac901ac363eea1440464ccbe0898/yarl-1.23.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", size = 123796, upload-time = "2026-03-01T22:05:41.412Z" }, - { url = "https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", size = 86547, upload-time = "2026-03-01T22:05:42.841Z" }, - { url = "https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", size = 85854, upload-time = "2026-03-01T22:05:44.85Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f4/4e30b250927ffdab4db70da08b9b8d2194d7c7b400167b8fbeca1e4701ca/yarl-1.23.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", size = 98351, upload-time = "2026-03-01T22:05:46.836Z" }, - { url = "https://files.pythonhosted.org/packages/86/fc/4118c5671ea948208bdb1492d8b76bdf1453d3e73df051f939f563e7dcc5/yarl-1.23.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", size = 92711, upload-time = "2026-03-01T22:05:48.316Z" }, - { url = "https://files.pythonhosted.org/packages/56/11/1ed91d42bd9e73c13dc9e7eb0dd92298d75e7ac4dd7f046ad0c472e231cd/yarl-1.23.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", size = 106014, upload-time = "2026-03-01T22:05:50.028Z" }, - { url = "https://files.pythonhosted.org/packages/ce/c9/74e44e056a23fbc33aca71779ef450ca648a5bc472bdad7a82339918f818/yarl-1.23.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", size = 105557, upload-time = "2026-03-01T22:05:51.416Z" }, - { url = "https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", size = 101559, upload-time = "2026-03-01T22:05:52.872Z" }, - { url = "https://files.pythonhosted.org/packages/72/59/c5b8d94b14e3d3c2a9c20cb100119fd534ab5a14b93673ab4cc4a4141ea5/yarl-1.23.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", size = 100502, upload-time = "2026-03-01T22:05:54.954Z" }, - { url = "https://files.pythonhosted.org/packages/77/4f/96976cb54cbfc5c9fd73ed4c51804f92f209481d1fb190981c0f8a07a1d7/yarl-1.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", size = 98027, upload-time = "2026-03-01T22:05:56.409Z" }, - { url = "https://files.pythonhosted.org/packages/63/6e/904c4f476471afdbad6b7e5b70362fb5810e35cd7466529a97322b6f5556/yarl-1.23.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", size = 95369, upload-time = "2026-03-01T22:05:58.141Z" }, - { url = "https://files.pythonhosted.org/packages/9d/40/acfcdb3b5f9d68ef499e39e04d25e141fe90661f9d54114556cf83be8353/yarl-1.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", size = 105565, upload-time = "2026-03-01T22:06:00.286Z" }, - { url = "https://files.pythonhosted.org/packages/5e/c6/31e28f3a6ba2869c43d124f37ea5260cac9c9281df803c354b31f4dd1f3c/yarl-1.23.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", size = 99813, upload-time = "2026-03-01T22:06:01.712Z" }, - { url = "https://files.pythonhosted.org/packages/08/1f/6f65f59e72d54aa467119b63fc0b0b1762eff0232db1f4720cd89e2f4a17/yarl-1.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", size = 105632, upload-time = "2026-03-01T22:06:03.188Z" }, - { url = "https://files.pythonhosted.org/packages/a3/c4/18b178a69935f9e7a338127d5b77d868fdc0f0e49becd286d51b3a18c61d/yarl-1.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", size = 101895, upload-time = "2026-03-01T22:06:04.651Z" }, - { url = "https://files.pythonhosted.org/packages/8f/54/f5b870b5505663911dba950a8e4776a0dbd51c9c54c0ae88e823e4b874a0/yarl-1.23.0-cp313-cp313-win32.whl", hash = "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", size = 82356, upload-time = "2026-03-01T22:06:06.04Z" }, - { url = "https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", size = 87515, upload-time = "2026-03-01T22:06:08.107Z" }, - { url = "https://files.pythonhosted.org/packages/00/fd/7e1c66efad35e1649114fa13f17485f62881ad58edeeb7f49f8c5e748bf9/yarl-1.23.0-cp313-cp313-win_arm64.whl", hash = "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", size = 81785, upload-time = "2026-03-01T22:06:10.181Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fc/119dd07004f17ea43bb91e3ece6587759edd7519d6b086d16bfbd3319982/yarl-1.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", size = 130719, upload-time = "2026-03-01T22:06:11.708Z" }, - { url = "https://files.pythonhosted.org/packages/e6/0d/9f2348502fbb3af409e8f47730282cd6bc80dec6630c1e06374d882d6eb2/yarl-1.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", size = 89690, upload-time = "2026-03-01T22:06:13.429Z" }, - { url = "https://files.pythonhosted.org/packages/50/93/e88f3c80971b42cfc83f50a51b9d165a1dbf154b97005f2994a79f212a07/yarl-1.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", size = 89851, upload-time = "2026-03-01T22:06:15.53Z" }, - { url = "https://files.pythonhosted.org/packages/1c/07/61c9dd8ba8f86473263b4036f70fb594c09e99c0d9737a799dfd8bc85651/yarl-1.23.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", size = 95874, upload-time = "2026-03-01T22:06:17.553Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e9/f9ff8ceefba599eac6abddcfb0b3bee9b9e636e96dbf54342a8577252379/yarl-1.23.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", size = 88710, upload-time = "2026-03-01T22:06:19.004Z" }, - { url = "https://files.pythonhosted.org/packages/eb/78/0231bfcc5d4c8eec220bc2f9ef82cb4566192ea867a7c5b4148f44f6cbcd/yarl-1.23.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", size = 101033, upload-time = "2026-03-01T22:06:21.203Z" }, - { url = "https://files.pythonhosted.org/packages/cd/9b/30ea5239a61786f18fd25797151a17fbb3be176977187a48d541b5447dd4/yarl-1.23.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", size = 100817, upload-time = "2026-03-01T22:06:22.738Z" }, - { url = "https://files.pythonhosted.org/packages/62/e2/a4980481071791bc83bce2b7a1a1f7adcabfa366007518b4b845e92eeee3/yarl-1.23.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", size = 97482, upload-time = "2026-03-01T22:06:24.21Z" }, - { url = "https://files.pythonhosted.org/packages/e5/1e/304a00cf5f6100414c4b5a01fc7ff9ee724b62158a08df2f8170dfc72a2d/yarl-1.23.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", size = 95949, upload-time = "2026-03-01T22:06:25.697Z" }, - { url = "https://files.pythonhosted.org/packages/68/03/093f4055ed4cae649ac53bca3d180bd37102e9e11d048588e9ab0c0108d0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", size = 95839, upload-time = "2026-03-01T22:06:27.309Z" }, - { url = "https://files.pythonhosted.org/packages/b9/28/4c75ebb108f322aa8f917ae10a8ffa4f07cae10a8a627b64e578617df6a0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", size = 90696, upload-time = "2026-03-01T22:06:29.048Z" }, - { url = "https://files.pythonhosted.org/packages/23/9c/42c2e2dd91c1a570402f51bdf066bfdb1241c2240ba001967bad778e77b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", size = 100865, upload-time = "2026-03-01T22:06:30.525Z" }, - { url = "https://files.pythonhosted.org/packages/74/05/1bcd60a8a0a914d462c305137246b6f9d167628d73568505fce3f1cb2e65/yarl-1.23.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", size = 96234, upload-time = "2026-03-01T22:06:32.692Z" }, - { url = "https://files.pythonhosted.org/packages/90/b2/f52381aac396d6778ce516b7bc149c79e65bfc068b5de2857ab69eeea3b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", size = 100295, upload-time = "2026-03-01T22:06:34.268Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/638bae5bbf1113a659b2435d8895474598afe38b4a837103764f603aba56/yarl-1.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", size = 97784, upload-time = "2026-03-01T22:06:35.864Z" }, - { url = "https://files.pythonhosted.org/packages/80/25/a3892b46182c586c202629fc2159aa13975d3741d52ebd7347fd501d48d5/yarl-1.23.0-cp313-cp313t-win32.whl", hash = "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", size = 88313, upload-time = "2026-03-01T22:06:37.39Z" }, - { url = "https://files.pythonhosted.org/packages/43/68/8c5b36aa5178900b37387937bc2c2fe0e9505537f713495472dcf6f6fccc/yarl-1.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", size = 94932, upload-time = "2026-03-01T22:06:39.579Z" }, - { url = "https://files.pythonhosted.org/packages/c6/cc/d79ba8292f51f81f4dc533a8ccfb9fc6992cabf0998ed3245de7589dc07c/yarl-1.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", size = 84786, upload-time = "2026-03-01T22:06:41.988Z" }, - { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455, upload-time = "2026-03-01T22:06:43.615Z" }, - { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752, upload-time = "2026-03-01T22:06:45.425Z" }, - { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291, upload-time = "2026-03-01T22:06:46.974Z" }, - { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026, upload-time = "2026-03-01T22:06:48.459Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355, upload-time = "2026-03-01T22:06:50.306Z" }, - { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417, upload-time = "2026-03-01T22:06:52.1Z" }, - { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422, upload-time = "2026-03-01T22:06:54.424Z" }, - { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915, upload-time = "2026-03-01T22:06:55.895Z" }, - { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690, upload-time = "2026-03-01T22:06:58.21Z" }, - { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750, upload-time = "2026-03-01T22:06:59.794Z" }, - { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685, upload-time = "2026-03-01T22:07:01.375Z" }, - { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009, upload-time = "2026-03-01T22:07:03.151Z" }, - { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033, upload-time = "2026-03-01T22:07:04.701Z" }, - { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483, upload-time = "2026-03-01T22:07:06.231Z" }, - { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175, upload-time = "2026-03-01T22:07:08.4Z" }, - { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871, upload-time = "2026-03-01T22:07:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093, upload-time = "2026-03-01T22:07:11.501Z" }, - { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384, upload-time = "2026-03-01T22:07:13.069Z" }, - { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019, upload-time = "2026-03-01T22:07:14.903Z" }, - { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894, upload-time = "2026-03-01T22:07:17.372Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979, upload-time = "2026-03-01T22:07:19.361Z" }, - { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943, upload-time = "2026-03-01T22:07:21.266Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786, upload-time = "2026-03-01T22:07:23.129Z" }, - { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307, upload-time = "2026-03-01T22:07:24.689Z" }, - { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904, upload-time = "2026-03-01T22:07:26.287Z" }, - { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728, upload-time = "2026-03-01T22:07:27.906Z" }, - { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964, upload-time = "2026-03-01T22:07:30.198Z" }, - { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882, upload-time = "2026-03-01T22:07:32.132Z" }, - { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797, upload-time = "2026-03-01T22:07:34.404Z" }, - { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023, upload-time = "2026-03-01T22:07:35.953Z" }, - { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227, upload-time = "2026-03-01T22:07:37.594Z" }, - { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302, upload-time = "2026-03-01T22:07:39.334Z" }, - { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202, upload-time = "2026-03-01T22:07:41.717Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558, upload-time = "2026-03-01T22:07:43.433Z" }, - { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610, upload-time = "2026-03-01T22:07:45.773Z" }, - { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, - { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, -] - -[[package]] -name = "zipp" -version = "3.23.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/21/093488dfc7cc8964ded15ab726fad40f25fd3d788fd741cc1c5a17d78ee8/zipp-3.23.1.tar.gz", hash = "sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110", size = 25965, upload-time = "2026-04-13T23:21:46.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/8a/0861bec20485572fbddf3dfba2910e38fe249796cb73ecdeb74e07eeb8d3/zipp-3.23.1-py3-none-any.whl", hash = "sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc", size = 10378, upload-time = "2026-04-13T23:21:45.386Z" }, -] diff --git a/pkg/hanzo-tools-ide/README.md b/pkg/hanzo-tools-ide/README.md deleted file mode 100644 index 9fee6a068..000000000 --- a/pkg/hanzo-tools-ide/README.md +++ /dev/null @@ -1,84 +0,0 @@ -# hanzo-tools-ide - -IDE integration tool for VS Code, Cursor, Windsurf, JetBrains, and LSP-compatible editors. - -## Features - -- **Multi-IDE support**: VS Code, Cursor, Windsurf, JetBrains, Neovim -- **Full editor control**: Open, edit, navigate, refactor -- **Terminal integration**: Create and send commands to terminals -- **Diagnostics**: Access errors, warnings, and quick fixes -- **Agent-friendly**: Designed for AI agent workflows - -## Installation - -```bash -pip install hanzo-tools-ide -``` - -## Requirements - -Install the Hanzo extension in your IDE: -- **VS Code/Cursor/Windsurf**: Install "Hanzo AI" extension -- **JetBrains**: Install "Hanzo AI" plugin -- **Neovim**: Install hanzo.nvim plugin - -## Usage - -```python -from hanzo_tools.ide import IdeTool - -ide = IdeTool() - -# Connect to IDE (auto-detects) -await ide.call(action="connect") - -# Open file -await ide.call(action="open", path="/src/main.py", line=42) - -# Insert text -await ide.call(action="insert", text="# TODO: fix", line=10) - -# Go to definition -await ide.call(action="go_to_definition", line=15, column=8) - -# Rename symbol -await ide.call(action="rename", new_name="betterName", line=10, column=5) - -# Run in terminal -await ide.call(action="terminal", command="npm test") - -# Get diagnostics -await ide.call(action="diagnostics", severity="error") - -# Execute VS Code command -await ide.call(action="command", command="editor.action.formatDocument") -``` - -## Actions - -| Action | Description | Parameters | -|--------|-------------|------------| -| connect | Connect to IDE | ide?: vscode\|cursor\|jetbrains | -| status | Check connection | - | -| open | Open file | path, line? | -| close | Close file | path? | -| save | Save file | path? | -| files | List open files | - | -| select | Set selection | line, column, end_line?, end_column? | -| insert | Insert text | text, line, column? | -| replace | Replace text | text, line, column, end_line, end_column | -| get_text | Get text | line?, end_line? | -| go_to_definition | Navigate | line, column | -| find_references | Find refs | line, column | -| rename | Rename symbol | new_name, line, column | -| format | Format doc | - | -| diagnostics | Get errors | severity? | -| quick_fix | Apply fix | line, column, index? | -| command | Execute cmd | command, args? | -| terminal | Terminal | command?, name? | -| search | Search | query, include?, exclude? | - -## License - -MIT - Hanzo Industries Inc diff --git a/pkg/hanzo-tools-ide/hanzo_tools/__init__.py b/pkg/hanzo-tools-ide/hanzo_tools/__init__.py deleted file mode 100644 index 68ff254a7..000000000 --- a/pkg/hanzo-tools-ide/hanzo_tools/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Namespace package - see PEP 420 -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-ide/hanzo_tools/ide/__init__.py b/pkg/hanzo-tools-ide/hanzo_tools/ide/__init__.py deleted file mode 100644 index cf5b15f8c..000000000 --- a/pkg/hanzo-tools-ide/hanzo_tools/ide/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -hanzo-tools-ide: IDE integration for VS Code, JetBrains, and LSP editors. - -Provides AI agents with direct control over IDEs: -- File operations (open, save, close) -- Editor operations (select, insert, replace) -- Navigation (go to definition, find references) -- Refactoring (rename, extract, format) -- Terminal control (create, send commands) -- Diagnostics (errors, warnings, quick fixes) - -Usage: - ide(action="open", path="/path/to/file.py") - ide(action="insert", text="# comment", line=10) - ide(action="go_to_definition", line=15, column=8) - ide(action="rename", new_name="better_name", line=10, column=5) - ide(action="terminal", command="npm test") -""" - -from .ide_tool import IdeTool, IdeConnection - -TOOLS = [IdeTool()] - -__all__ = ["IdeTool", "IdeConnection", "TOOLS"] diff --git a/pkg/hanzo-tools-ide/hanzo_tools/ide/ide_tool.py b/pkg/hanzo-tools-ide/hanzo_tools/ide/ide_tool.py deleted file mode 100644 index 4f03f32e7..000000000 --- a/pkg/hanzo-tools-ide/hanzo_tools/ide/ide_tool.py +++ /dev/null @@ -1,519 +0,0 @@ -""" -IDE Integration Tool for VS Code, JetBrains, and LSP-compatible editors. - -Architecture: -- VS Code: Connect via Hanzo extension (WebSocket on port 9225) -- JetBrains: Connect via Gateway protocol -- Neovim: Connect via RPC socket -- Generic: Connect via LSP - -VS Code Extension API surfaces used: -- vscode.window: Active editor, terminals, notifications -- vscode.workspace: Files, folders, configuration -- vscode.commands: Execute any VS Code command -- vscode.languages: Diagnostics, completion, hover - -The Hanzo VS Code extension acts as a bridge, exposing these APIs -via WebSocket for external AI agent control. -""" - -from __future__ import annotations - -import json -import uuid -import asyncio -import logging -from enum import Enum -from typing import Any -from dataclasses import field, dataclass - -try: - import aiohttp - - AIOHTTP_AVAILABLE = True -except ImportError: - AIOHTTP_AVAILABLE = False - -try: - import websockets - - WEBSOCKETS_AVAILABLE = True -except ImportError: - WEBSOCKETS_AVAILABLE = False - -from hanzo_tools.core import BaseTool - -logger = logging.getLogger(__name__) - - -class IdeType(str, Enum): - """Supported IDE types.""" - - VSCODE = "vscode" - JETBRAINS = "jetbrains" - NEOVIM = "neovim" - CURSOR = "cursor" - WINDSURF = "windsurf" - GENERIC = "generic" - - -@dataclass -class IdeConnection: - """Connection state for an IDE.""" - - ide_type: IdeType - endpoint: str - websocket: Any = None - connected: bool = False - workspace_path: str | None = None - active_file: str | None = None - pending_requests: dict[str, asyncio.Future] = field(default_factory=dict) - - async def connect(self) -> bool: - """Establish connection to IDE.""" - if not WEBSOCKETS_AVAILABLE: - logger.warning("websockets not available") - return False - - try: - self.websocket = await websockets.connect( - self.endpoint, - ping_interval=30, - ping_timeout=10, - ) - self.connected = True - - # Start message receiver - asyncio.create_task(self._receive_loop()) - - return True - except Exception as e: - logger.debug(f"Failed to connect to {self.ide_type}: {e}") - return False - - async def disconnect(self) -> None: - """Close connection.""" - if self.websocket: - await self.websocket.close() - self.connected = False - - async def send(self, action: str, **params: Any) -> dict[str, Any]: - """Send request and wait for response.""" - if not self.connected or not self.websocket: - return {"error": "Not connected to IDE"} - - request_id = str(uuid.uuid4()) - - message = { - "id": request_id, - "action": action, - "params": params, - } - - future: asyncio.Future[dict] = asyncio.get_event_loop().create_future() - self.pending_requests[request_id] = future - - try: - await self.websocket.send(json.dumps(message)) - result = await asyncio.wait_for(future, timeout=30.0) - return result - except asyncio.TimeoutError: - return {"error": "Request timed out"} - finally: - self.pending_requests.pop(request_id, None) - - async def _receive_loop(self) -> None: - """Receive messages from IDE.""" - try: - async for message in self.websocket: - data = json.loads(message) - request_id = data.get("id") - - if request_id and request_id in self.pending_requests: - self.pending_requests[request_id].set_result(data) - else: - # Event notification - await self._handle_event(data) - except Exception as e: - logger.debug(f"Receive loop error: {e}") - self.connected = False - - async def _handle_event(self, data: dict) -> None: - """Handle event notification from IDE.""" - event_type = data.get("event") - if event_type == "activeEditorChanged": - self.active_file = data.get("path") - elif event_type == "workspaceChanged": - self.workspace_path = data.get("path") - - -class IdeManager: - """Manages IDE connections.""" - - _instance: IdeManager | None = None - - def __init__(self) -> None: - self.connections: dict[IdeType, IdeConnection] = {} - self.default_ide: IdeType | None = None - - @classmethod - def get_instance(cls) -> IdeManager: - """Get singleton instance.""" - if cls._instance is None: - cls._instance = cls() - return cls._instance - - async def connect(self, ide_type: IdeType = IdeType.VSCODE) -> IdeConnection: - """Connect to an IDE.""" - if ide_type in self.connections and self.connections[ide_type].connected: - return self.connections[ide_type] - - # Default endpoints for each IDE type - endpoints = { - IdeType.VSCODE: "ws://localhost:9225", - IdeType.CURSOR: "ws://localhost:9226", - IdeType.WINDSURF: "ws://localhost:9227", - IdeType.JETBRAINS: "ws://localhost:63342/api/hanzo", - IdeType.NEOVIM: "ws://localhost:9228", - } - - endpoint = endpoints.get(ide_type, endpoints[IdeType.VSCODE]) - - conn = IdeConnection(ide_type=ide_type, endpoint=endpoint) - if await conn.connect(): - self.connections[ide_type] = conn - if self.default_ide is None: - self.default_ide = ide_type - return conn - - return conn - - async def auto_connect(self) -> IdeConnection | None: - """Auto-detect and connect to available IDE.""" - for ide_type in [IdeType.VSCODE, IdeType.CURSOR, IdeType.WINDSURF]: - conn = await self.connect(ide_type) - if conn.connected: - return conn - return None - - def get_connection(self, ide_type: IdeType | None = None) -> IdeConnection | None: - """Get existing connection.""" - ide_type = ide_type or self.default_ide - if ide_type: - return self.connections.get(ide_type) - return None - - -class IdeTool(BaseTool): - """ - IDE integration tool for controlling VS Code, JetBrains, and other editors. - - Connects to the Hanzo IDE extension (VS Code, Cursor, Windsurf) via WebSocket. - Provides full control over editor operations for AI agents. - - Actions: - - connect: Connect to IDE (auto-detects VS Code, Cursor, Windsurf) - - status: Check connection status - - open: Open file in editor - - close: Close file - - save: Save current file - - files: List open files - - select: Set selection in editor - - insert: Insert text at position - - replace: Replace text in range - - get_text: Get text from file/selection - - go_to_definition: Navigate to symbol definition - - find_references: Find all references to symbol - - rename: Rename symbol across workspace - - format: Format document - - diagnostics: Get errors and warnings - - quick_fix: Apply quick fix - - command: Execute VS Code command - - terminal: Create/send to terminal - - search: Search in workspace - """ - - name = "ide" - - @property - def description(self) -> str: - return """IDE control for VS Code, Cursor, Windsurf, and JetBrains. - -ACTIONS: -- connect: Connect to IDE (ide?: vscode|cursor|windsurf|jetbrains) -- status: Check connection status -- open: Open file (path: string, line?: int) -- close: Close file (path?: string) -- save: Save file (path?: string) -- files: List open files -- select: Set selection (line: int, column: int, end_line?: int, end_column?: int) -- insert: Insert text (text: string, line: int, column?: int) -- replace: Replace text (text: string, line: int, column: int, end_line: int, end_column: int) -- get_text: Get text (line?: int, end_line?: int) -- go_to_definition: Go to definition (line: int, column: int) -- find_references: Find references (line: int, column: int) -- rename: Rename symbol (new_name: string, line: int, column: int) -- format: Format document -- diagnostics: Get diagnostics (severity?: error|warning|info) -- quick_fix: Apply quick fix (line: int, column: int, index?: int) -- command: Execute command (command: string, args?: list) -- terminal: Terminal (command?: string, name?: string) -- search: Search workspace (query: string, include?: string, exclude?: string) - -EXAMPLES: - ide(action="connect") - ide(action="open", path="/src/main.py", line=42) - ide(action="insert", text="# TODO: fix this", line=10) - ide(action="rename", new_name="better_name", line=15, column=8) - ide(action="terminal", command="npm test") - ide(action="command", command="workbench.action.toggleSidebarVisibility")""" - - def __init__(self) -> None: - super().__init__() - self.manager = IdeManager.get_instance() - - async def call( - self, - action: str = "status", - ide: str | None = None, - path: str | None = None, - line: int | None = None, - column: int | None = None, - end_line: int | None = None, - end_column: int | None = None, - text: str | None = None, - new_name: str | None = None, - command: str | None = None, - args: list[Any] | None = None, - query: str | None = None, - include: str | None = None, - exclude: str | None = None, - name: str | None = None, - severity: str | None = None, - index: int | None = None, - **kwargs: Any, - ) -> str: - """Execute IDE action.""" - - action = action.lower() - - # Connection management - if action == "connect": - return await self._connect(ide) - elif action == "status": - return self._status() - - # Get connection - ide_type = IdeType(ide) if ide else None - conn = self.manager.get_connection(ide_type) - - if not conn or not conn.connected: - # Try auto-connect - conn = await self.manager.auto_connect() - if not conn or not conn.connected: - return "Not connected to IDE. Use ide(action='connect') first or install Hanzo IDE extension." - - # File operations - if action == "open": - if not path: - return "Error: path required for open action" - result = await conn.send("openFile", path=path, line=line) - return self._format_result(result, f"Opened {path}") - - elif action == "close": - result = await conn.send("closeFile", path=path) - return self._format_result(result, "File closed") - - elif action == "save": - result = await conn.send("saveFile", path=path) - return self._format_result(result, "File saved") - - elif action == "files": - result = await conn.send("listOpenFiles") - return self._format_result(result) - - # Editor operations - elif action == "select": - if line is None: - return "Error: line required for select action" - result = await conn.send( - "setSelection", - line=line, - column=column or 0, - endLine=end_line, - endColumn=end_column, - ) - return self._format_result(result, "Selection set") - - elif action == "insert": - if text is None or line is None: - return "Error: text and line required for insert action" - result = await conn.send( - "insertText", - text=text, - line=line, - column=column or 0, - ) - return self._format_result(result, "Text inserted") - - elif action == "replace": - if text is None: - return "Error: text required for replace action" - result = await conn.send( - "replaceText", - text=text, - line=line, - column=column, - endLine=end_line, - endColumn=end_column, - ) - return self._format_result(result, "Text replaced") - - elif action in ("get_text", "text"): - result = await conn.send( - "getText", - line=line, - endLine=end_line, - ) - return self._format_result(result) - - # Navigation - elif action in ("go_to_definition", "definition"): - if line is None or column is None: - return "Error: line and column required" - result = await conn.send( - "goToDefinition", - line=line, - column=column, - ) - return self._format_result(result) - - elif action in ("find_references", "references"): - if line is None or column is None: - return "Error: line and column required" - result = await conn.send( - "findReferences", - line=line, - column=column, - ) - return self._format_result(result) - - # Refactoring - elif action == "rename": - if new_name is None or line is None or column is None: - return "Error: new_name, line, and column required" - result = await conn.send( - "rename", - newName=new_name, - line=line, - column=column, - ) - return self._format_result(result, f"Renamed to {new_name}") - - elif action == "format": - result = await conn.send("formatDocument") - return self._format_result(result, "Document formatted") - - # Diagnostics - elif action == "diagnostics": - result = await conn.send("getDiagnostics", severity=severity) - return self._format_result(result) - - elif action == "quick_fix": - if line is None or column is None: - return "Error: line and column required" - result = await conn.send( - "applyQuickFix", - line=line, - column=column, - index=index or 0, - ) - return self._format_result(result, "Quick fix applied") - - # Commands - elif action == "command": - if not command: - return "Error: command required" - result = await conn.send( - "executeCommand", - command=command, - args=args or [], - ) - return self._format_result(result) - - # Terminal - elif action == "terminal": - if command: - result = await conn.send( - "sendToTerminal", - command=command, - name=name, - ) - else: - result = await conn.send( - "createTerminal", - name=name, - ) - return self._format_result(result) - - # Search - elif action == "search": - if not query: - return "Error: query required" - result = await conn.send( - "searchWorkspace", - query=query, - include=include, - exclude=exclude, - ) - return self._format_result(result) - - else: - return f"Unknown action: {action}" - - async def _connect(self, ide: str | None) -> str: - """Connect to IDE.""" - ide_type = IdeType(ide) if ide else None - - if ide_type: - conn = await self.manager.connect(ide_type) - else: - conn = await self.manager.auto_connect() - - if conn and conn.connected: - return f"Connected to {conn.ide_type.value}" - return "Failed to connect. Ensure Hanzo IDE extension is installed and running." - - def _status(self) -> str: - """Get connection status.""" - if not self.manager.connections: - return "No IDE connections" - - lines = ["IDE Connections:"] - for ide_type, conn in self.manager.connections.items(): - status = "connected" if conn.connected else "disconnected" - default = " (default)" if ide_type == self.manager.default_ide else "" - lines.append(f" {ide_type.value}: {status}{default}") - if conn.workspace_path: - lines.append(f" Workspace: {conn.workspace_path}") - if conn.active_file: - lines.append(f" Active: {conn.active_file}") - - return "\n".join(lines) - - def _format_result(self, result: dict, success_msg: str | None = None) -> str: - """Format result from IDE.""" - if "error" in result: - return f"Error: {result['error']}" - - if success_msg and result.get("success", True): - return success_msg - - # Format result data - data = result.get("data") or result.get("result") - if data: - if isinstance(data, str): - return data - return json.dumps(data, indent=2) - - return str(result) diff --git a/pkg/hanzo-tools-ide/pyproject.toml b/pkg/hanzo-tools-ide/pyproject.toml deleted file mode 100644 index 7653fb3d5..000000000 --- a/pkg/hanzo-tools-ide/pyproject.toml +++ /dev/null @@ -1,43 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-ide" -version = "0.1.0" -description = "IDE integration tool for VS Code, JetBrains, and LSP-compatible editors" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -keywords = ["mcp", "ide", "vscode", "jetbrains", "lsp", "editor"] -dependencies = [ - "hanzo-tools-core>=0.1.0", - "aiohttp>=3.9.0", - "websockets>=12.0", -] - -[project.optional-dependencies] -lsp = ["pygls>=1.3.0"] -dev = [ - "pytest>=7.0.0", - "pytest-asyncio>=0.23.0", -] - -[project.urls] -"Homepage" = "https://github.com/hanzoai/python-sdk" - -[project.entry-points."hanzo.tools"] -ide = "hanzo_tools.ide:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] - -[tool.setuptools.package-data] -hanzo_tools = ["py.typed"] diff --git a/pkg/hanzo-tools-ingress/README.md b/pkg/hanzo-tools-ingress/README.md deleted file mode 100644 index 8d30abb7f..000000000 --- a/pkg/hanzo-tools-ingress/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# hanzo-tools-ingress - -MCP tool package for hanzo-mcp. Provides native ingress management via the Hanzo platform. - -## Installation - -```bash -pip install hanzo-tools-ingress -``` - -Part of the [hanzo-mcp](https://pypi.org/project/hanzo-mcp/) ecosystem. diff --git a/pkg/hanzo-tools-ingress/hanzo_tools/__init__.py b/pkg/hanzo-tools-ingress/hanzo_tools/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-tools-ingress/hanzo_tools/ingress/__init__.py b/pkg/hanzo-tools-ingress/hanzo_tools/ingress/__init__.py deleted file mode 100644 index 2ea426c7e..000000000 --- a/pkg/hanzo-tools-ingress/hanzo_tools/ingress/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Hanzo Ingress Tools -- Traefik inspection and PaaS domain management via MCP.""" - -from .ingress_tool import IngressTool - -TOOLS = [IngressTool] - -__all__ = ["IngressTool", "TOOLS"] diff --git a/pkg/hanzo-tools-ingress/hanzo_tools/ingress/ingress_tool.py b/pkg/hanzo-tools-ingress/hanzo_tools/ingress/ingress_tool.py deleted file mode 100644 index fdaf365b2..000000000 --- a/pkg/hanzo-tools-ingress/hanzo_tools/ingress/ingress_tool.py +++ /dev/null @@ -1,309 +0,0 @@ -"""MCP tool for Traefik ingress inspection and PaaS domain management. - -Traefik actions query the Traefik API for routers, services, middlewares, -entrypoints, and dashboard overview. Domain actions use the PaaS API to -manage custom domains, verify DNS, and check TLS certificate status. - -Auth: Uses HanzoSession from hanzo-tools-auth for authenticated API calls. -""" - -from __future__ import annotations - -import json -import logging -from typing import Any, Annotated, final - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core.base import BaseTool - -logger = logging.getLogger(__name__) - -DESCRIPTION = """Traefik ingress inspection and PaaS domain management. - -Requires authentication via `hanzo login` (stored at ~/.hanzo/auth/token.json). - -Traefik actions: -- routers: List HTTP routers -- services: List HTTP services -- middlewares: List middlewares -- entrypoints: List entrypoints -- overview: Dashboard overview stats -- tcp_routers: List TCP routers - -Domain actions (PaaS): -- domains: List custom domains for a project -- add_domain: Add a custom domain (params: project_id, domain) -- remove_domain: Remove a custom domain (params: project_id, domain) -- verify_domain: Verify domain DNS (params: project_id, domain) -- tls_status: Get TLS certificate status (params: domain) -""" - - -def _get_session(): - """Get HanzoSession singleton.""" - from hanzo_tools.auth.session import HanzoSession - return HanzoSession.get() - - -@final -class IngressTool(BaseTool): - """MCP tool for Traefik ingress and domain operations.""" - - @property - def name(self) -> str: - return "ingress" - - @property - def description(self) -> str: - return DESCRIPTION - - async def call( - self, - ctx: MCPContext, - action: str = "overview", - project_id: str | None = None, - domain: str | None = None, - **kwargs: Any, - ) -> str: - try: - # Traefik actions - if action == "routers": - return await self._routers() - elif action == "services": - return await self._services() - elif action == "middlewares": - return await self._middlewares() - elif action == "entrypoints": - return await self._entrypoints() - elif action == "overview": - return await self._overview() - elif action == "tcp_routers": - return await self._tcp_routers() - # Domain actions - elif action == "domains": - return await self._domains(project_id) - elif action == "add_domain": - return await self._add_domain(project_id, domain) - elif action == "remove_domain": - return await self._remove_domain(project_id, domain) - elif action == "verify_domain": - return await self._verify_domain(project_id, domain) - elif action == "tls_status": - return await self._tls_status(domain) - else: - return json.dumps({ - "error": f"Unknown action: {action}", - "available": [ - "routers", "services", "middlewares", "entrypoints", - "overview", "tcp_routers", - "domains", "add_domain", "remove_domain", - "verify_domain", "tls_status", - ], - }) - except RuntimeError as e: - return json.dumps({"error": str(e)}) - except Exception as e: - logger.exception(f"Ingress tool error: {e}") - return json.dumps({"error": f"Ingress error: {e}"}) - - # -- Traefik actions ----------------------------------------------------- - - async def _traefik_get(self, path: str) -> Any: - """GET from the Traefik API via the PaaS gateway.""" - session = _get_session() - paas = session.get_paas_client() - return paas.get(f"/v1/ingress{path}") - - async def _routers(self) -> str: - data = await self._traefik_get("/http/routers") - routers = data if isinstance(data, list) else [] - result = [] - for r in routers: - result.append({ - "name": r.get("name"), - "rule": r.get("rule"), - "service": r.get("service"), - "entryPoints": r.get("entryPoints"), - "status": r.get("status"), - "tls": bool(r.get("tls")), - "provider": r.get("provider"), - }) - return json.dumps({"count": len(result), "routers": result}, indent=2) - - async def _services(self) -> str: - data = await self._traefik_get("/http/services") - services = data if isinstance(data, list) else [] - result = [] - for s in services: - result.append({ - "name": s.get("name"), - "type": s.get("type"), - "status": s.get("status"), - "provider": s.get("provider"), - "servers": s.get("loadBalancer", {}).get("servers") if isinstance(s.get("loadBalancer"), dict) else None, - }) - return json.dumps({"count": len(result), "services": result}, indent=2) - - async def _middlewares(self) -> str: - data = await self._traefik_get("/http/middlewares") - middlewares = data if isinstance(data, list) else [] - result = [] - for m in middlewares: - result.append({ - "name": m.get("name"), - "type": m.get("type"), - "status": m.get("status"), - "provider": m.get("provider"), - }) - return json.dumps({"count": len(result), "middlewares": result}, indent=2) - - async def _entrypoints(self) -> str: - data = await self._traefik_get("/entrypoints") - entrypoints = data if isinstance(data, list) else [] - result = [] - for ep in entrypoints: - result.append({ - "name": ep.get("name"), - "address": ep.get("address"), - "protocol": ep.get("protocol"), - }) - return json.dumps({"count": len(result), "entrypoints": result}, indent=2) - - async def _overview(self) -> str: - data = await self._traefik_get("/overview") - return json.dumps(data, indent=2) - - async def _tcp_routers(self) -> str: - data = await self._traefik_get("/tcp/routers") - routers = data if isinstance(data, list) else [] - result = [] - for r in routers: - result.append({ - "name": r.get("name"), - "rule": r.get("rule"), - "service": r.get("service"), - "entryPoints": r.get("entryPoints"), - "status": r.get("status"), - "tls": bool(r.get("tls")), - }) - return json.dumps({"count": len(result), "tcp_routers": result}, indent=2) - - # -- Domain actions (PaaS) ----------------------------------------------- - - async def _domains(self, project_id: str | None) -> str: - if not project_id: - return json.dumps({"error": "Required: project_id"}) - - session = _get_session() - paas = session.get_paas_client() - data = paas.get(f"/v1/project/{project_id}/domain") - domains = data if isinstance(data, list) else [] - result = [] - for d in domains: - result.append({ - "id": d.get("id"), - "domain": d.get("domain"), - "verified": d.get("verified"), - "tls": d.get("tls"), - "created_at": d.get("createdAt"), - }) - return json.dumps({ - "project_id": project_id, - "count": len(result), - "domains": result, - }, indent=2) - - async def _add_domain(self, project_id: str | None, domain: str | None) -> str: - if not project_id or not domain: - return json.dumps({"error": "Required: project_id and domain"}) - - session = _get_session() - paas = session.get_paas_client() - result = paas.post(f"/v1/project/{project_id}/domain", json={"domain": domain}) - return json.dumps({ - "action": "add_domain", - "project_id": project_id, - "domain": domain, - "result": result, - }, indent=2) - - async def _remove_domain(self, project_id: str | None, domain: str | None) -> str: - if not project_id or not domain: - return json.dumps({"error": "Required: project_id and domain"}) - - session = _get_session() - paas = session.get_paas_client() - result = paas.delete(f"/v1/project/{project_id}/domain/{domain}") - return json.dumps({ - "action": "remove_domain", - "project_id": project_id, - "domain": domain, - "result": result, - }, indent=2) - - async def _verify_domain(self, project_id: str | None, domain: str | None) -> str: - if not project_id or not domain: - return json.dumps({"error": "Required: project_id and domain"}) - - session = _get_session() - paas = session.get_paas_client() - result = paas.post(f"/v1/project/{project_id}/domain/{domain}/verify") - return json.dumps({ - "action": "verify_domain", - "project_id": project_id, - "domain": domain, - "result": result, - }, indent=2) - - async def _tls_status(self, domain: str | None) -> str: - if not domain: - return json.dumps({"error": "Required: domain"}) - - session = _get_session() - paas = session.get_paas_client() - result = paas.get(f"/v1/domain/{domain}/tls") - return json.dumps({ - "domain": domain, - "tls": result, - }, indent=2) - - # -- Registration -------------------------------------------------------- - - def register(self, mcp_server: FastMCP) -> None: - """Register ingress tool with explicit parameters.""" - tool_instance = self - - @mcp_server.tool( - name="ingress", - description=DESCRIPTION, - ) - async def ingress( - action: Annotated[ - str, - Field( - description=( - "Action to perform. " - "Traefik: routers, services, middlewares, entrypoints, overview, tcp_routers. " - "Domains: domains, add_domain, remove_domain, verify_domain, tls_status." - ), - ), - ] = "overview", - project_id: Annotated[ - str | None, - Field(description="Project ID (for domain actions)"), - ] = None, - domain: Annotated[ - str | None, - Field(description="Domain name (for add_domain, remove_domain, verify_domain, tls_status)"), - ] = None, - ctx: MCPContext = None, - ) -> str: - return await tool_instance.call( - ctx, - action=action, - project_id=project_id, - domain=domain, - ) diff --git a/pkg/hanzo-tools-ingress/pyproject.toml b/pkg/hanzo-tools-ingress/pyproject.toml deleted file mode 100644 index be1f5d540..000000000 --- a/pkg/hanzo-tools-ingress/pyproject.toml +++ /dev/null @@ -1,32 +0,0 @@ -[project] -name = "hanzo-tools-ingress" -version = "0.1.0" -description = "Hanzo MCP tool for Traefik ingress inspection and PaaS domain management" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "mcp", "ingress", "traefik", "domains", "tools"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", -] - -dependencies = [ - "hanzo-tools-core>=0.1.0", - "hanzo-tools-auth>=0.1.0", - "httpx>=0.27.0", -] - -[project.entry-points."hanzo.tools"] -ingress = "hanzo_tools.ingress:TOOLS" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] diff --git a/pkg/hanzo-tools-jupyter/README.md b/pkg/hanzo-tools-jupyter/README.md deleted file mode 100644 index 79095361d..000000000 --- a/pkg/hanzo-tools-jupyter/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# hanzo-tools-jupyter - -Jupyter notebook tools for Hanzo MCP. - -## Installation - -```bash -pip install hanzo-tools-jupyter -``` - -## Tools - -### jupyter - Notebook Operations -Read and edit Jupyter notebooks. - -**Read notebook:** -```python -jupyter(action="read", path="/path/to/notebook.ipynb") -jupyter(action="read", path="/path/to/notebook.ipynb", cell=5) # Specific cell -``` - -**Edit cell:** -```python -jupyter( - action="edit", - path="/path/to/notebook.ipynb", - cell=5, - content="print('Hello, World!')" -) -``` - -**Insert cell:** -```python -jupyter( - action="insert", - path="/path/to/notebook.ipynb", - after_cell=5, - content="# New cell", - cell_type="markdown" -) -``` - -**Delete cell:** -```python -jupyter( - action="delete", - path="/path/to/notebook.ipynb", - cell=5 -) -``` - -**List cells:** -```python -jupyter(action="list", path="/path/to/notebook.ipynb") -``` - -## Cell Types - -- `code` - Python code cell -- `markdown` - Markdown text cell -- `raw` - Raw text cell - -## License - -MIT diff --git a/pkg/hanzo-tools-jupyter/hanzo_tools/__init__.py b/pkg/hanzo-tools-jupyter/hanzo_tools/__init__.py deleted file mode 100644 index e1b06939a..000000000 --- a/pkg/hanzo-tools-jupyter/hanzo_tools/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Hanzo tools namespace package.""" - -import pkgutil - -__path__ = pkgutil.extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-jupyter/hanzo_tools/jupyter/__init__.py b/pkg/hanzo-tools-jupyter/hanzo_tools/jupyter/__init__.py deleted file mode 100644 index 51e5b567e..000000000 --- a/pkg/hanzo-tools-jupyter/hanzo_tools/jupyter/__init__.py +++ /dev/null @@ -1,85 +0,0 @@ -"""Jupyter notebook tools for Hanzo AI. - -Tools: -- jupyter: Read, edit, and execute Jupyter notebooks - -Install: - pip install hanzo-tools-jupyter - -Usage: - from hanzo_tools.jupyter import register_tools, TOOLS - - # Register with MCP server - register_tools(mcp_server, permission_manager) -""" - -from hanzo_tools.core import BaseTool, ToolRegistry, PermissionManager - -from .jupyter import JupyterTool - -# Export list for tool discovery -TOOLS = [JupyterTool] - -# Read-only tools (for agent sandboxing) - jupyter is read-only by default -READ_ONLY_TOOLS = [JupyterTool] - -__all__ = [ - "JupyterTool", - "register_tools", - "get_read_only_jupyter_tools", - "TOOLS", - "READ_ONLY_TOOLS", -] - - -def get_read_only_jupyter_tools(permission_manager) -> list: - """Get read-only jupyter tools for sandboxed agents. - - Returns tools that can read jupyter notebooks: - - jupyter: Read and analyze notebook contents - - Args: - permission_manager: PermissionManager instance - - Returns: - List of instantiated read-only tools - """ - tools = [] - for tool_class in READ_ONLY_TOOLS: - try: - tools.append(tool_class(permission_manager)) - except TypeError: - tools.append(tool_class()) - return tools - - -def register_tools( - mcp_server, - permission_manager: PermissionManager, - enabled_tools: dict[str, bool] | None = None, -) -> list[BaseTool]: - """Register Jupyter notebook tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - permission_manager: Permission manager for access control - enabled_tools: Dict of tool_name -> enabled state - - Returns: - List of registered tools - """ - enabled = enabled_tools or {} - registered = [] - - for tool_class in TOOLS: - tool_name = ( - tool_class.name - if hasattr(tool_class, "name") - else tool_class.__name__.lower() - ) - if enabled.get(tool_name, True): # Enabled by default - tool = tool_class(permission_manager) - ToolRegistry.register_tool(mcp_server, tool) - registered.append(tool) - - return registered diff --git a/pkg/hanzo-tools-jupyter/hanzo_tools/jupyter/base.py b/pkg/hanzo-tools-jupyter/hanzo_tools/jupyter/base.py deleted file mode 100644 index 343b75058..000000000 --- a/pkg/hanzo-tools-jupyter/hanzo_tools/jupyter/base.py +++ /dev/null @@ -1,295 +0,0 @@ -"""Base functionality for Jupyter notebook tools. - -This module provides common functionality for Jupyter notebook tools, including notebook parsing, -cell processing, and output formatting. -""" - -import re -import json -from abc import ABC -from typing import Any, final -from pathlib import Path - -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - ToolContext, - FileSystemTool as FilesystemBaseTool, - create_tool_context, -) - -# Pattern to match ANSI escape sequences -ANSI_ESCAPE_PATTERN = re.compile(r"\x1B\[[0-9;]*[a-zA-Z]") - - -# Function to clean ANSI escape codes from text -def clean_ansi_escapes(text: str) -> str: - """Remove ANSI escape sequences from text. - - Args: - text: Text containing ANSI escape sequences - - Returns: - Text with ANSI escape sequences removed - """ - return ANSI_ESCAPE_PATTERN.sub("", text) - - -@final -class NotebookOutputImage: - """Representation of an image output in a notebook cell.""" - - def __init__(self, image_data: str, media_type: str): - """Initialize a notebook output image. - - Args: - image_data: Base64-encoded image data - media_type: Media type of the image (e.g., "image/png") - """ - self.image_data = image_data - self.media_type = media_type - - -@final -class NotebookCellOutput: - """Representation of an output from a notebook cell.""" - - def __init__( - self, - output_type: str, - text: str | None = None, - image: NotebookOutputImage | None = None, - ): - """Initialize a notebook cell output. - - Args: - output_type: Type of output - text: Text output (if any) - image: Image output (if any) - """ - self.output_type = output_type - self.text = text - self.image = image - - -@final -class NotebookCellSource: - """Representation of a source cell from a notebook.""" - - def __init__( - self, - cell_index: int, - cell_type: str, - source: str, - language: str, - execution_count: int | None = None, - outputs: list[NotebookCellOutput] | None = None, - ): - """Initialize a notebook cell source. - - Args: - cell_index: Index of the cell in the notebook - cell_type: Type of cell (code or markdown) - source: Source code or text of the cell - language: Programming language of the cell - execution_count: Execution count of the cell (if any) - outputs: Outputs from the cell (if any) - """ - self.cell_index = cell_index - self.cell_type = cell_type - self.source = source - self.language = language - self.execution_count = execution_count - self.outputs = outputs or [] - - -class JupyterBaseTool(FilesystemBaseTool, ABC): - """Base class for Jupyter notebook tools. - - Provides common functionality for working with Jupyter notebooks, including - parsing, cell extraction, and output formatting. - """ - - def create_tool_context(self, ctx: MCPContext) -> ToolContext: - """Create a tool context with the tool name. - - Args: - ctx: MCP context - - Returns: - Tool context - """ - tool_ctx = create_tool_context(ctx) - return tool_ctx - - def set_tool_context_info(self, tool_ctx: ToolContext) -> None: - """Set the tool info on the context. - - Args: - tool_ctx: Tool context - """ - tool_ctx.set_tool_info(self.name) - - async def parse_notebook( - self, file_path: Path - ) -> tuple[dict[str, Any], list[NotebookCellSource]]: - """Parse a Jupyter notebook file. - - Args: - file_path: Path to the notebook file - - Returns: - Tuple of (notebook_data, processed_cells) - """ - with open(file_path, "r", encoding="utf-8") as f: - content = f.read() - notebook = json.loads(content) - - # Get notebook language - language = ( - notebook.get("metadata", {}).get("language_info", {}).get("name", "python") - ) - cells = notebook.get("cells", []) - processed_cells = [] - - for i, cell in enumerate(cells): - cell_type = cell.get("cell_type", "code") - - # Skip if not code or markdown - if cell_type not in ["code", "markdown"]: - continue - - # Get source - source = cell.get("source", "") - if isinstance(source, list): - source = "".join(source) - - # Get execution count for code cells - execution_count = None - if cell_type == "code": - execution_count = cell.get("execution_count") - - # Process outputs for code cells - outputs = [] - if cell_type == "code" and "outputs" in cell: - for output in cell["outputs"]: - output_type = output.get("output_type", "") - - # Process different output types - if output_type == "stream": - text = output.get("text", "") - if isinstance(text, list): - text = "".join(text) - outputs.append( - NotebookCellOutput(output_type="stream", text=text) - ) - - elif output_type in ["execute_result", "display_data"]: - # Process text output - text = None - if "data" in output and "text/plain" in output["data"]: - text_data = output["data"]["text/plain"] - if isinstance(text_data, list): - text = "".join(text_data) - else: - text = text_data - - # Process image output - image = None - if "data" in output: - if "image/png" in output["data"]: - image = NotebookOutputImage( - image_data=output["data"]["image/png"], - media_type="image/png", - ) - elif "image/jpeg" in output["data"]: - image = NotebookOutputImage( - image_data=output["data"]["image/jpeg"], - media_type="image/jpeg", - ) - - outputs.append( - NotebookCellOutput( - output_type=output_type, text=text, image=image - ) - ) - - elif output_type == "error": - # Format error traceback - ename = output.get("ename", "") - evalue = output.get("evalue", "") - traceback = output.get("traceback", []) - - # Handle raw text strings and lists of strings - if isinstance(traceback, list): - # Clean ANSI escape codes and join the list but preserve the formatting - clean_traceback = [ - clean_ansi_escapes(line) for line in traceback - ] - traceback_text = "\n".join(clean_traceback) - else: - traceback_text = clean_ansi_escapes(str(traceback)) - - error_text = f"{ename}: {evalue}\n{traceback_text}" - outputs.append( - NotebookCellOutput(output_type="error", text=error_text) - ) - - # Create cell object - processed_cell = NotebookCellSource( - cell_index=i, - cell_type=cell_type, - source=source, - language=language, - execution_count=execution_count, - outputs=outputs, - ) - - processed_cells.append(processed_cell) - - return notebook, processed_cells - - def format_notebook_cells(self, cells: list[NotebookCellSource]) -> str: - """Format notebook cells as a readable string. - - Args: - cells: List of processed notebook cells - - Returns: - Formatted string representation of the cells - """ - result = [] - for cell in cells: - # Format the cell header - cell_header = f"Cell [{cell.cell_index}] {cell.cell_type}" - if cell.execution_count is not None: - cell_header += f" (execution_count: {cell.execution_count})" - if cell.cell_type == "code" and cell.language != "python": - cell_header += f" [{cell.language}]" - - # Add cell to result - result.append(f"{cell_header}:") - result.append(f"```{cell.language if cell.cell_type == 'code' else ''}") - result.append(cell.source) - result.append("```") - - # Add outputs if any - if cell.outputs: - result.append("Outputs:") - for output in cell.outputs: - if output.output_type == "error": - result.append("Error:") - result.append("```") - result.append(output.text) - result.append("```") - elif output.text: - result.append("Output:") - result.append("```") - result.append(output.text) - result.append("```") - if output.image: - result.append(f"[Image output: {output.image.media_type}]") - - result.append("") # Empty line between cells - - return "\n".join(result) diff --git a/pkg/hanzo-tools-jupyter/hanzo_tools/jupyter/jupyter.py b/pkg/hanzo-tools-jupyter/hanzo_tools/jupyter/jupyter.py deleted file mode 100644 index 4ee334fb7..000000000 --- a/pkg/hanzo-tools-jupyter/hanzo_tools/jupyter/jupyter.py +++ /dev/null @@ -1,443 +0,0 @@ -"""Unified Jupyter notebook tool.""" - -from typing import ( - Any, - Dict, - Unpack, - Optional, - Annotated, - TypedDict, - final, - override, -) -from pathlib import Path - -import nbformat -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import auto_timeout - -from .base import JupyterBaseTool - -# Parameter types -Action = Annotated[ - str, - Field( - description="Action to perform: read (default), edit, create, delete, execute", - default="read", - ), -] - -NotebookPath = Annotated[ - str, - Field( - description="Path to the Jupyter notebook file (.ipynb)", - ), -] - -CellId = Annotated[ - Optional[str], - Field( - description="Cell ID for targeted operations", - default=None, - ), -] - -CellIndex = Annotated[ - Optional[int], - Field( - description="Cell index (0-based) for operations", - default=None, - ), -] - -CellType = Annotated[ - Optional[str], - Field( - description="Cell type: code or markdown", - default=None, - ), -] - -Source = Annotated[ - Optional[str], - Field( - description="New source content for cell", - default=None, - ), -] - -EditMode = Annotated[ - str, - Field( - description="Edit mode: replace (default), insert, delete", - default="replace", - ), -] - - -class NotebookParams(TypedDict, total=False): - """Parameters for notebook tool.""" - - action: str - notebook_path: str - cell_id: Optional[str] - cell_index: Optional[int] - cell_type: Optional[str] - source: Optional[str] - edit_mode: str - - -@final -class JupyterTool(JupyterBaseTool): - """Tool for Jupyter notebook operations.""" - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "jupyter" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Jupyter notebooks. Actions: read (default), edit, create, delete, execute. - -Usage: -jupyter "path/to/notebook.ipynb" -jupyter "notebook.ipynb" --cell-index 2 -jupyter --action edit "notebook.ipynb" --cell-index 0 --source "print('Hello')" -jupyter --action create "new.ipynb" -""" - - @override - @auto_timeout("jupyter") - async def call( - self, - ctx: MCPContext, - **params: Unpack[NotebookParams], - ) -> str: - """Execute notebook operation.""" - tool_ctx = self.create_tool_context(ctx) - - # Extract parameters - action = params.get("action", "read") - notebook_path = params.get("notebook_path") - - if not notebook_path: - return "Error: notebook_path is required" - - # Validate path - path_validation = self.validate_path(notebook_path) - if path_validation.is_error: - await tool_ctx.error(path_validation.error_message) - return f"Error: {path_validation.error_message}" - - # Check permissions - allowed, error_msg = await self.check_path_allowed(notebook_path, tool_ctx) - if not allowed: - return error_msg - - # Route to appropriate handler - if action == "read": - return await self._handle_read(notebook_path, params, tool_ctx) - elif action == "edit": - return await self._handle_edit(notebook_path, params, tool_ctx) - elif action == "create": - return await self._handle_create(notebook_path, tool_ctx) - elif action == "delete": - return await self._handle_delete(notebook_path, params, tool_ctx) - elif action == "execute": - return await self._handle_execute(notebook_path, params, tool_ctx) - else: - return f"Error: Unknown action '{action}'. Valid actions: read, edit, create, delete, execute" - - async def _handle_read( - self, notebook_path: str, params: Dict[str, Any], tool_ctx - ) -> str: - """Read notebook or specific cell.""" - exists, error_msg = await self.check_path_exists(notebook_path, tool_ctx) - if not exists: - return error_msg - - try: - nb = self.read_notebook(notebook_path) - - # Check if specific cell requested - cell_id = params.get("cell_id") - cell_index = params.get("cell_index") - - if cell_id: - # Find cell by ID - for i, cell in enumerate(nb.cells): - if cell.get("id") == cell_id: - return self._format_cell(cell, i) - return f"Error: Cell with ID '{cell_id}' not found" - - elif cell_index is not None: - # Get cell by index - if 0 <= cell_index < len(nb.cells): - return self._format_cell(nb.cells[cell_index], cell_index) - else: - return f"Error: Cell index {cell_index} out of range (notebook has {len(nb.cells)} cells)" - - else: - # Return all cells - return self.format_notebook(nb) - - except Exception as e: - await tool_ctx.error(f"Failed to read notebook: {str(e)}") - return f"Error reading notebook: {str(e)}" - - async def _handle_edit( - self, notebook_path: str, params: Dict[str, Any], tool_ctx - ) -> str: - """Edit notebook cell.""" - exists, error_msg = await self.check_path_exists(notebook_path, tool_ctx) - if not exists: - return error_msg - - source = params.get("source") - edit_mode = params.get("edit_mode", "replace") - - # Only require source for non-delete operations - if edit_mode != "delete" and not source: - return "Error: source is required for edit action" - cell_id = params.get("cell_id") - cell_index = params.get("cell_index") - cell_type = params.get("cell_type") - - try: - nb = self.read_notebook(notebook_path) - - if edit_mode == "insert": - # Insert new cell - new_cell = ( - nbformat.v4.new_code_cell(source) - if cell_type != "markdown" - else nbformat.v4.new_markdown_cell(source) - ) - - if cell_index is not None: - nb.cells.insert(cell_index, new_cell) - else: - nb.cells.append(new_cell) - - self.write_notebook(nb, notebook_path) - return f"Successfully inserted new cell at index {cell_index if cell_index is not None else len(nb.cells) - 1}" - - elif edit_mode == "delete": - # Delete cell - if cell_id: - for i, cell in enumerate(nb.cells): - if cell.get("id") == cell_id: - nb.cells.pop(i) - self.write_notebook(nb, notebook_path) - return f"Successfully deleted cell with ID '{cell_id}'" - return f"Error: Cell with ID '{cell_id}' not found" - - elif cell_index is not None: - if 0 <= cell_index < len(nb.cells): - nb.cells.pop(cell_index) - self.write_notebook(nb, notebook_path) - return f"Successfully deleted cell at index {cell_index}" - else: - return f"Error: Cell index {cell_index} out of range" - else: - return "Error: cell_id or cell_index required for delete" - - else: # replace - # Replace cell content - if cell_id: - for cell in nb.cells: - if cell.get("id") == cell_id: - cell["source"] = source - if cell_type: - cell["cell_type"] = cell_type - self.write_notebook(nb, notebook_path) - return f"Successfully updated cell with ID '{cell_id}'" - return f"Error: Cell with ID '{cell_id}' not found" - - elif cell_index is not None: - if 0 <= cell_index < len(nb.cells): - nb.cells[cell_index]["source"] = source - if cell_type: - nb.cells[cell_index]["cell_type"] = cell_type - self.write_notebook(nb, notebook_path) - return f"Successfully updated cell at index {cell_index}" - else: - return f"Error: Cell index {cell_index} out of range" - else: - return "Error: cell_id or cell_index required for replace" - - except Exception as e: - await tool_ctx.error(f"Failed to edit notebook: {str(e)}") - return f"Error editing notebook: {str(e)}" - - async def _handle_create(self, notebook_path: str, tool_ctx) -> str: - """Create new notebook.""" - # Check if already exists - path = Path(notebook_path) - if path.exists(): - return f"Error: Notebook already exists at {notebook_path}" - - try: - # Create new notebook - nb = nbformat.v4.new_notebook() - - # Ensure parent directory exists - path.parent.mkdir(parents=True, exist_ok=True) - - # Write notebook - self.write_notebook(nb, notebook_path) - return f"Successfully created notebook at {notebook_path}" - - except Exception as e: - await tool_ctx.error(f"Failed to create notebook: {str(e)}") - return f"Error creating notebook: {str(e)}" - - async def _handle_delete( - self, notebook_path: str, params: Dict[str, Any], tool_ctx - ) -> str: - """Delete notebook or cell.""" - # If cell specified, delegate to edit with delete mode - if params.get("cell_id") or params.get("cell_index") is not None: - params["edit_mode"] = "delete" - return await self._handle_edit(notebook_path, params, tool_ctx) - - # Otherwise, delete entire notebook - exists, error_msg = await self.check_path_exists(notebook_path, tool_ctx) - if not exists: - return error_msg - - try: - Path(notebook_path).unlink() - return f"Successfully deleted notebook {notebook_path}" - except Exception as e: - await tool_ctx.error(f"Failed to delete notebook: {str(e)}") - return f"Error deleting notebook: {str(e)}" - - async def _handle_execute( - self, notebook_path: str, params: Dict[str, Any], tool_ctx - ) -> str: - """Execute notebook cells using nbclient.""" - try: - import nbclient - from nbclient import NotebookClient - - nb = nbformat.read(notebook_path, as_version=4) - - # Create a notebook client with default kernel - client = NotebookClient( - nb, - timeout=params.get("timeout", 600), - kernel_name=params.get("kernel_name", "python3"), - ) - - # Execute the notebook - await client.async_execute() - - # Save the executed notebook - nbformat.write(nb, notebook_path) - - return f"Successfully executed all cells in {notebook_path}" - except ImportError: - return "Error: nbclient not installed. Install with: pip install nbclient" - except Exception as e: - return f"Error executing notebook: {str(e)}" - - def _format_cell(self, cell: dict, index: int) -> str: - """Format a single cell for display.""" - output = [f"Cell {index} ({cell.get('cell_type', 'unknown')})"] - if cell.get("id"): - output.append(f"ID: {cell.get('id')}") - output.append("-" * 40) - # Get source content - source = cell.get("source", "") - if isinstance(source, list): - source = "".join(source) - output.append(source) - - if cell.get("cell_type") == "code" and cell.get("outputs"): - output.append("\nOutputs:") - for out in cell.get("outputs", []): - out_type = out.get("output_type", "") - - if out_type == "stream": - text = out.get("text", "") - if isinstance(text, list): - text = "".join(text) - name = out.get("name", "stdout") - output.append(f"[{name}]: {text}") - - elif out_type == "execute_result": - exec_count = out.get("execution_count", "?") - data = out.get("data", {}) - # Try to get plain text representation - if "text/plain" in data: - text_data = data["text/plain"] - if isinstance(text_data, list): - text_data = "".join(text_data) - output.append(f"[Out {exec_count}]: {text_data}") - else: - output.append(f"[Out {exec_count}]: {data}") - - elif out_type == "error": - ename = out.get("ename", "Error") - evalue = out.get("evalue", "") - output.append(f"[Error]: {ename}: {evalue}") - # Include traceback if available - traceback = out.get("traceback", []) - if traceback: - output.append("Traceback:") - for line in traceback: - output.append(f" {line}") - - return "\n".join(output) - - def read_notebook(self, notebook_path: str) -> Any: - """Read a notebook from disk using nbformat. - - Args: - notebook_path: Path to the notebook file - - Returns: - Notebook object - """ - with open(notebook_path, "r") as f: - return nbformat.read(f, as_version=4) - - def write_notebook(self, nb: Any, notebook_path: str) -> None: - """Write a notebook to disk using nbformat. - - Args: - nb: Notebook object to write - notebook_path: Path to write the notebook to - """ - with open(notebook_path, "w") as f: - nbformat.write(nb, f) - - def format_notebook(self, nb: Any) -> str: - """Format an entire notebook for display. - - Args: - nb: Notebook object - - Returns: - Formatted string representation of the notebook - """ - output = [] - output.append(f"Notebook with {len(nb.cells)} cells") - output.append("=" * 50) - - for i, cell in enumerate(nb.cells): - output.append("") - output.append(self._format_cell(cell, i)) - - return "\n".join(output) - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-jupyter/hanzo_tools/jupyter/notebook_edit.py b/pkg/hanzo-tools-jupyter/hanzo_tools/jupyter/notebook_edit.py deleted file mode 100644 index 3ccd7faf0..000000000 --- a/pkg/hanzo-tools-jupyter/hanzo_tools/jupyter/notebook_edit.py +++ /dev/null @@ -1,320 +0,0 @@ -"""Edit notebook tool implementation. - -This module provides the NoteBookEditTool for editing Jupyter notebook files. -""" - -import json -from typing import Any, Unpack, Literal, Annotated, TypedDict, final, override -from pathlib import Path - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import auto_timeout - -from .base import JupyterBaseTool - -NotebookPath = Annotated[ - str, - Field( - description="The absolute path to the Jupyter notebook file to edit (must be absolute, not relative)", - ), -] - -CellNumber = Annotated[ - int, - Field( - description="The index of the cell to edit (0-based)", - ge=0, - ), -] - -NewSource = Annotated[ - str, - Field( - description="The new source for the cell", - default="", - ), -] - -CellType = Annotated[ - Literal["code", "markdown"], - Field( - description="The of the cell (code or markdown). If not specified, it defaults to the current cell type. If using edit_mode=insert, this is required.", - default="code", - ), -] - -EditMode = Annotated[ - Literal["replace", "insert", "delete"], - Field( - description="The of edit to make (replace, insert, delete). Defaults to replace.", - default="replace", - ), -] - - -class NotebookEditToolParams(TypedDict): - """Parameters for the NotebookEditTool. - - Attributes: - notebook_path: The absolute path to the Jupyter notebook file to edit (must be absolute, not relative) - cell_number: The index of the cell to edit (0-based) - new_source: The new source for the cell - cell_type: The of the cell (code or markdown). If not specified, it defaults to the current cell type. If using edit_mode=insert, this is required. - edit_mode: The of edit to make (replace, insert, delete). Defaults to replace. - """ - - notebook_path: NotebookPath - cell_number: CellNumber - new_source: NewSource - cell_type: CellType - edit_mode: EditMode - - -@final -class NoteBookEditTool(JupyterBaseTool): - """Tool for editing Jupyter notebook files.""" - - @property - @override - def name(self) -> str: - """Get the tool name. - - Returns: - Tool name - """ - return "notebook_edit" - - @property - @override - def description(self) -> str: - """Get the tool description. - - Returns: - Tool description - """ - return "Completely replaces the contents of a specific cell in a Jupyter notebook (.ipynb file) with new source. Jupyter notebooks are interactive documents that combine code, text, and visualizations, commonly used for data analysis and scientific computing. The notebook_path parameter must be an absolute path, not a relative path. The cell_number is 0-indexed. Use edit_mode=insert to add a new cell at the index specified by cell_number. Use edit_mode=delete to delete the cell at the index specified by cell_number." - - @override - @auto_timeout("notebook_edit") - async def call( - self, - ctx: MCPContext, - **params: Unpack[NotebookEditToolParams], - ) -> str: - """Execute the tool with the given parameters. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Tool result - """ - tool_ctx = self.create_tool_context(ctx) - self.set_tool_context_info(tool_ctx) - - # Extract parameters - notebook_path = params.get("notebook_path") - cell_number = params.get("cell_number") - new_source = params.get("new_source") - cell_type = params.get("cell_type") - edit_mode = params.get("edit_mode", "replace") - - path_validation = self.validate_path(notebook_path) - if path_validation.is_error: - await tool_ctx.error(path_validation.error_message) - return f"Error: {path_validation.error_message}" - - # Validate edit_mode - if edit_mode not in ["replace", "insert", "delete"]: - await tool_ctx.error("Edit mode must be replace, insert, or delete") - return "Error: Edit mode must be replace, insert, or delete" - - # In insert mode, cell_type is required - if edit_mode == "insert" and cell_type is None: - await tool_ctx.error("Cell type is required when using insert mode") - return "Error: Cell type is required when using insert mode" - - # Don't validate new_source for delete mode - if edit_mode != "delete" and not new_source: - await tool_ctx.error( - "New source is required for replace or insert operations" - ) - return "Error: New source is required for replace or insert operations" - - await tool_ctx.info( - f"Editing notebook: {notebook_path} (cell: {cell_number}, mode: {edit_mode})" - ) - - # Check if path is allowed - if not self.is_path_allowed(notebook_path): - await tool_ctx.error( - f"Access denied - path outside allowed directories: {notebook_path}" - ) - return f"Error: Access denied - path outside allowed directories: {notebook_path}" - - try: - file_path = Path(notebook_path) - - if not file_path.exists(): - await tool_ctx.error(f"File does not exist: {notebook_path}") - return f"Error: File does not exist: {notebook_path}" - - if not file_path.is_file(): - await tool_ctx.error(f"Path is not a file: {notebook_path}") - return f"Error: Path is not a file: {notebook_path}" - - # Check file extension - if file_path.suffix.lower() != ".ipynb": - await tool_ctx.error(f"File is not a Jupyter notebook: {notebook_path}") - return f"Error: File is not a Jupyter notebook: {notebook_path}" - - # Read and parse the notebook - try: - with open(file_path, "r", encoding="utf-8") as f: - content = f.read() - notebook = json.loads(content) - except json.JSONDecodeError: - await tool_ctx.error(f"Invalid notebook format: {notebook_path}") - return f"Error: Invalid notebook format: {notebook_path}" - except UnicodeDecodeError: - await tool_ctx.error(f"Cannot read notebook file: {notebook_path}") - return f"Error: Cannot read notebook file: {notebook_path}" - - # Check cell_number is valid - cells = notebook.get("cells", []) - - if edit_mode == "insert": - if cell_number > len(cells): - await tool_ctx.error( - f"Cell number {cell_number} is out of bounds for insert (max: {len(cells)})" - ) - return f"Error: Cell number {cell_number} is out of bounds for insert (max: {len(cells)})" - else: # replace or delete - if cell_number >= len(cells): - await tool_ctx.error( - f"Cell number {cell_number} is out of bounds (max: {len(cells) - 1})" - ) - return f"Error: Cell number {cell_number} is out of bounds (max: {len(cells) - 1})" - - # Get notebook language (needed for context but not directly used in this block) - _ = ( - notebook.get("metadata", {}) - .get("language_info", {}) - .get("name", "python") - ) - - # Perform the requested operation - if edit_mode == "replace": - # Get the target cell - target_cell = cells[cell_number] - - # Store previous contents for reporting - old_type = target_cell.get("cell_type", "code") - old_source = target_cell.get("source", "") - - # Fix for old_source which might be a list of strings - if isinstance(old_source, list): - old_source = "".join([str(item) for item in old_source]) - - # Update source - target_cell["source"] = new_source - - # Update type if specified - if cell_type is not None: - target_cell["cell_type"] = cell_type - - # If changing to markdown, remove code-specific fields - if cell_type == "markdown": - if "outputs" in target_cell: - del target_cell["outputs"] - if "execution_count" in target_cell: - del target_cell["execution_count"] - - # If code cell, reset execution - if target_cell["cell_type"] == "code": - target_cell["outputs"] = [] - target_cell["execution_count"] = None - - change_description = f"Replaced cell {cell_number}" - if cell_type is not None and cell_type != old_type: - change_description += ( - f" (changed type from {old_type} to {cell_type})" - ) - - elif edit_mode == "insert": - # Create new cell - new_cell: dict[str, Any] = { - "cell_type": cell_type, - "source": new_source, - "metadata": {}, - } - - # Add code-specific fields - if cell_type == "code": - new_cell["outputs"] = [] - new_cell["execution_count"] = None - - # Insert the cell - cells.insert(cell_number, new_cell) - change_description = ( - f"Inserted new {cell_type} cell at position {cell_number}" - ) - - else: # delete - # Store deleted cell info for reporting - deleted_cell = cells[cell_number] - deleted_type = deleted_cell.get("cell_type", "code") - - # Remove the cell - del cells[cell_number] - change_description = ( - f"Deleted {deleted_type} cell at position {cell_number}" - ) - - # Write the updated notebook back to file - with open(file_path, "w", encoding="utf-8") as f: - json.dump(notebook, f, indent=1) - - await tool_ctx.info( - f"Successfully edited notebook: {notebook_path} - {change_description}" - ) - return ( - f"Successfully edited notebook: {notebook_path} - {change_description}" - ) - except Exception as e: - await tool_ctx.error(f"Error editing notebook: {str(e)}") - return f"Error editing notebook: {str(e)}" - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register this edit notebook tool with the MCP server. - - Creates a wrapper function with explicitly defined parameters that match - the tool's parameter schema and registers it with the MCP server. - - Args: - mcp_server: The FastMCP server instance - """ - tool_self = self # Create a reference to self for use in the closure - - @mcp_server.tool(name=self.name, description=self.description) - async def notebook_edit( - notebook_path: NotebookPath, - cell_number: CellNumber, - new_source: NewSource, - cell_type: CellType, - edit_mode: EditMode, - ctx: MCPContext, - ) -> str: - return await tool_self.call( - ctx, - notebook_path=notebook_path, - cell_number=cell_number, - new_source=new_source, - cell_type=cell_type, - edit_mode=edit_mode, - ) diff --git a/pkg/hanzo-tools-jupyter/hanzo_tools/jupyter/notebook_read.py b/pkg/hanzo-tools-jupyter/hanzo_tools/jupyter/notebook_read.py deleted file mode 100644 index f810b0223..000000000 --- a/pkg/hanzo-tools-jupyter/hanzo_tools/jupyter/notebook_read.py +++ /dev/null @@ -1,150 +0,0 @@ -"""Read notebook tool implementation. - -This module provides the NotebookReadTool for reading Jupyter notebook files. -""" - -import json -from typing import Unpack, Annotated, TypedDict, final, override -from pathlib import Path - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import auto_timeout - -from .base import JupyterBaseTool - -NotebookPath = Annotated[ - str, - Field( - description="The absolute path to the Jupyter notebook file to read (must be absolute, not relative)", - ), -] - - -class NotebookReadToolParams(TypedDict): - """Parameters for the NotebookReadTool. - - Attributes: - notebook_path: The absolute path to the Jupyter notebook file to read (must be absolute, not relative) - """ - - notebook_path: NotebookPath - - -@final -class NotebookReadTool(JupyterBaseTool): - """Tool for reading Jupyter notebook files.""" - - @property - @override - def name(self) -> str: - """Get the tool name. - - Returns: - Tool name - """ - return "notebook_read" - - @property - @override - def description(self) -> str: - """Get the tool description. - - Returns: - Tool description - """ - return "Reads a Jupyter notebook (.ipynb file) and returns all of the cells with their outputs. Jupyter notebooks are interactive documents that combine code, text, and visualizations, commonly used for data analysis and scientific computing. The notebook_path parameter must be an absolute path, not a relative path." - - @override - @auto_timeout("notebook_read") - async def call( - self, - ctx: MCPContext, - **params: Unpack[NotebookReadToolParams], - ) -> str: - """Execute the tool with the given parameters. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Tool result - """ - tool_ctx = self.create_tool_context(ctx) - self.set_tool_context_info(tool_ctx) - - # Extract parameters - notebook_path: NotebookPath = params["notebook_path"] - - # Validate path parameter - path_validation = self.validate_path(notebook_path) - if path_validation.is_error: - await tool_ctx.error(path_validation.error_message) - return f"Error: {path_validation.error_message}" - - await tool_ctx.info(f"Reading notebook: {notebook_path}") - - # Check if path is allowed - if not self.is_path_allowed(notebook_path): - await tool_ctx.error( - f"Access denied - path outside allowed directories: {notebook_path}" - ) - return f"Error: Access denied - path outside allowed directories: {notebook_path}" - - try: - file_path = Path(notebook_path) - - if not file_path.exists(): - await tool_ctx.error(f"File does not exist: {notebook_path}") - return f"Error: File does not exist: {notebook_path}" - - if not file_path.is_file(): - await tool_ctx.error(f"Path is not a file: {notebook_path}") - return f"Error: Path is not a file: {notebook_path}" - - # Check file extension - if file_path.suffix.lower() != ".ipynb": - await tool_ctx.error(f"File is not a Jupyter notebook: {notebook_path}") - return f"Error: File is not a Jupyter notebook: {notebook_path}" - - # Read and parse the notebook - try: - # This will read the file, so we don't need to read it separately - _, processed_cells = await self.parse_notebook(file_path) - - # Format the notebook content as a readable string - result = self.format_notebook_cells(processed_cells) - - await tool_ctx.info( - f"Successfully read notebook: {notebook_path} ({len(processed_cells)} cells)" - ) - return result - except json.JSONDecodeError: - await tool_ctx.error(f"Invalid notebook format: {notebook_path}") - return f"Error: Invalid notebook format: {notebook_path}" - except UnicodeDecodeError: - await tool_ctx.error(f"Cannot read notebook file: {notebook_path}") - return f"Error: Cannot read notebook file: {notebook_path}" - except Exception as e: - await tool_ctx.error(f"Error reading notebook: {str(e)}") - return f"Error reading notebook: {str(e)}" - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register this read notebook tool with the MCP server. - - Creates a wrapper function with explicitly defined parameters that match - the tool's parameter schema and registers it with the MCP server. - - Args: - mcp_server: The FastMCP server instance - """ - - tool_self = self # Create a reference to self for use in the closure - - @mcp_server.tool(name=self.name, description=self.description) - async def notebook_read(notebook_path: NotebookPath, ctx: MCPContext) -> str: - return await tool_self.call(ctx, notebook_path=notebook_path) diff --git a/pkg/hanzo-tools-jupyter/pyproject.toml b/pkg/hanzo-tools-jupyter/pyproject.toml deleted file mode 100644 index af338f2bf..000000000 --- a/pkg/hanzo-tools-jupyter/pyproject.toml +++ /dev/null @@ -1,42 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-jupyter" -version = "0.2.0" -description = "Jupyter notebook tools for Hanzo AI - read, edit, execute notebooks" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -keywords = ["hanzo", "tools", "jupyter", "notebook", "mcp", "ai"] -dependencies = [ - "hanzo-tools>=0.3.0", - "mcp>=1.25.0", - "fastmcp>=2.14.1", - "pydantic>=2.12.5", - "nbformat>=5.0.0", -] - -[project.optional-dependencies] -full = ["nbclient>=0.10.0", "jupyter-client>=8.0.0"] -dev = ["pytest>=7.0.0", "ruff>=0.14.0"] - -[project.urls] -"Homepage" = "https://github.com/hanzoai/python-sdk" - -[project.entry-points."hanzo.tools"] -jupyter = "hanzo_tools.jupyter:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] - -[tool.setuptools.package-data] -hanzo_tools = ["py.typed"] diff --git a/pkg/hanzo-tools-jupyter/tests/test_jupyter_tools.py b/pkg/hanzo-tools-jupyter/tests/test_jupyter_tools.py deleted file mode 100644 index c5576b128..000000000 --- a/pkg/hanzo-tools-jupyter/tests/test_jupyter_tools.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Tests for hanzo-tools-jupyter.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import jupyter - - assert jupyter is not None - - def test_import_tools(self): - from hanzo_tools.jupyter import TOOLS - - assert len(TOOLS) > 0 diff --git a/pkg/hanzo-tools-kms/README.md b/pkg/hanzo-tools-kms/README.md deleted file mode 100644 index 5714e774b..000000000 --- a/pkg/hanzo-tools-kms/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# hanzo-tools-kms - -KMS secret management MCP tool for Hanzo platform. diff --git a/pkg/hanzo-tools-kms/hanzo_tools/__init__.py b/pkg/hanzo-tools-kms/hanzo_tools/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-tools-kms/hanzo_tools/kms/__init__.py b/pkg/hanzo-tools-kms/hanzo_tools/kms/__init__.py deleted file mode 100644 index d3dbcd3e4..000000000 --- a/pkg/hanzo-tools-kms/hanzo_tools/kms/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Hanzo KMS Tools โ€” secret management via MCP.""" - -from .kms_tool import KMSTool - -TOOLS = [KMSTool] - -__all__ = ["KMSTool", "TOOLS"] diff --git a/pkg/hanzo-tools-kms/hanzo_tools/kms/kms_tool.py b/pkg/hanzo-tools-kms/hanzo_tools/kms/kms_tool.py deleted file mode 100644 index a7c726ac8..000000000 --- a/pkg/hanzo-tools-kms/hanzo_tools/kms/kms_tool.py +++ /dev/null @@ -1,296 +0,0 @@ -"""MCP tool for Hanzo KMS secret management. - -Wraps the hanzo-kms client to provide secret CRUD operations -via MCP. Auth uses HANZO_KMS_CLIENT_ID / HANZO_KMS_CLIENT_SECRET -environment variables (same pattern as hanzo-cli). -""" - -from __future__ import annotations - -import os -import json -import logging -from typing import Any, Annotated, final - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core.base import BaseTool - -logger = logging.getLogger(__name__) - -DESCRIPTION = """Manage secrets via Hanzo KMS. - -Requires HANZO_KMS_CLIENT_ID and HANZO_KMS_CLIENT_SECRET environment variables. - -Actions: -- list: List all secrets in a project/environment (values masked) -- get: Get a single secret value -- set: Create or update a secret -- delete: Remove a secret -- inject: Output secrets as export/dotenv/json format -""" - - -def _get_kms_client() -> Any: - """Build a KMS client from environment.""" - from hanzo_kms import KMSClient, ClientSettings - - kms_url = os.getenv("HANZO_KMS_URL", "https://kms.hanzo.ai") - client_id = os.getenv("HANZO_KMS_CLIENT_ID", "") - client_secret = os.getenv("HANZO_KMS_CLIENT_SECRET", "") - - if client_id and client_secret: - from hanzo_kms import UniversalAuthMethod, AuthenticationOptions - - settings = ClientSettings( - site_url=kms_url, - auth=AuthenticationOptions( - universal_auth=UniversalAuthMethod( - client_id=client_id, - client_secret=client_secret, - ) - ), - ) - return KMSClient(settings=settings) - - # Fall back to default env-based construction - return KMSClient() - - -@final -class KMSTool(BaseTool): - """MCP tool for KMS secret management.""" - - @property - def name(self) -> str: - return "kms" - - @property - def description(self) -> str: - return DESCRIPTION - - async def call( - self, - ctx: MCPContext, - action: str = "list", - project: str | None = None, - environment: str | None = None, - secret_name: str | None = None, - secret_value: str | None = None, - path: str = "/", - format: str = "export", - reveal: bool = False, - **kwargs: Any, - ) -> str: - if action == "list": - return await self._list(project, environment, path) - elif action == "get": - return await self._get(project, environment, secret_name, path, reveal) - elif action == "set": - return await self._set(project, environment, secret_name, secret_value, path) - elif action == "delete": - return await self._delete(project, environment, secret_name, path) - elif action == "inject": - return await self._inject(project, environment, path, format) - else: - return json.dumps({"error": f"Unknown action: {action}. Use: list, get, set, delete, inject"}) - - async def _list(self, project: str | None, env: str | None, path: str) -> str: - if not project or not env: - return json.dumps({"error": "Required: project and environment"}) - - client = _get_kms_client() - try: - secrets = client.list_secrets(project_id=project, environment=env, path=path) - finally: - client.close() - - if not secrets: - return json.dumps({"message": f"No secrets found in {project}/{env}", "secrets": []}) - - rows = [] - for s in secrets: - val = s.secret_value - masked = f"{val[:4]}***" if len(val) > 4 else "***" - rows.append({ - "key": s.secret_key, - "value": masked, - "version": s.version, - "updated": str(s.updated_at)[:19] if s.updated_at else None, - }) - - return json.dumps({ - "project": project, - "environment": env, - "path": path, - "count": len(rows), - "secrets": rows, - }, indent=2) - - async def _get( - self, project: str | None, env: str | None, name: str | None, path: str, reveal: bool - ) -> str: - if not project or not env or not name: - return json.dumps({"error": "Required: project, environment, and secret_name"}) - - client = _get_kms_client() - try: - secret = client.get_secret( - project_id=project, environment=env, secret_name=name, path=path - ) - finally: - client.close() - - val = secret.secret_value - if not reveal: - val = f"{val[:4]}***" if len(val) > 4 else "***" - - return json.dumps({ - "key": secret.secret_key, - "value": val, - "version": secret.version, - "type": secret.type, - "environment": secret.environment, - "comment": secret.secret_comment or None, - "revealed": reveal, - }, indent=2) - - async def _set( - self, project: str | None, env: str | None, name: str | None, value: str | None, path: str - ) -> str: - if not project or not env or not name or not value: - return json.dumps({"error": "Required: project, environment, secret_name, and secret_value"}) - - client = _get_kms_client() - try: - # Try update first, create if it doesn't exist - try: - secret = client.update_secret( - project_id=project, - environment=env, - secret_name=name, - secret_value=value, - ) - return json.dumps({ - "action": "updated", - "key": name, - "version": secret.version, - }) - except Exception: - secret = client.create_secret( - project_id=project, - environment=env, - secret_name=name, - secret_value=value, - ) - return json.dumps({ - "action": "created", - "key": name, - }) - finally: - client.close() - - async def _delete( - self, project: str | None, env: str | None, name: str | None, path: str - ) -> str: - if not project or not env or not name: - return json.dumps({"error": "Required: project, environment, and secret_name"}) - - client = _get_kms_client() - try: - client.delete_secret( - project_id=project, - environment=env, - secret_name=name, - path=path, - ) - finally: - client.close() - - return json.dumps({"action": "deleted", "key": name}) - - async def _inject(self, project: str | None, env: str | None, path: str, fmt: str) -> str: - if not project or not env: - return json.dumps({"error": "Required: project and environment"}) - - client = _get_kms_client() - try: - secrets = client.list_secrets(project_id=project, environment=env, path=path) - finally: - client.close() - - if not secrets: - return json.dumps({"message": "No secrets found", "output": ""}) - - if fmt == "json": - data = {s.secret_key: s.secret_value for s in secrets} - return json.dumps(data, indent=2) - elif fmt == "dotenv": - lines = [] - for s in secrets: - val = s.secret_value.replace('"', '\\"') - lines.append(f'{s.secret_key}="{val}"') - return "\n".join(lines) - else: # export - lines = [] - for s in secrets: - val = s.secret_value.replace("'", "'\\''") - lines.append(f"export {s.secret_key}='{val}'") - return "\n".join(lines) - - def register(self, mcp_server: FastMCP) -> None: - """Register KMS tool with explicit parameters.""" - tool_instance = self - - @mcp_server.tool( - name="kms", - description=DESCRIPTION, - ) - async def kms( - action: Annotated[ - str, - Field(description="Action: list, get, set, delete, inject"), - ] = "list", - project: Annotated[ - str | None, - Field(description="KMS project ID or slug"), - ] = None, - environment: Annotated[ - str | None, - Field(description="Environment: dev, staging, production"), - ] = None, - secret_name: Annotated[ - str | None, - Field(description="Secret key name (for get/set/delete)"), - ] = None, - secret_value: Annotated[ - str | None, - Field(description="Secret value (for set)"), - ] = None, - path: Annotated[ - str, - Field(description="Secret path prefix (default: /)"), - ] = "/", - format: Annotated[ - str, - Field(description="Output format for inject: export, dotenv, json"), - ] = "export", - reveal: Annotated[ - bool, - Field(description="Show full secret value (default: masked)"), - ] = False, - ctx: MCPContext = None, - ) -> str: - return await tool_instance.call( - ctx, - action=action, - project=project, - environment=environment, - secret_name=secret_name, - secret_value=secret_value, - path=path, - format=format, - reveal=reveal, - ) diff --git a/pkg/hanzo-tools-kms/pyproject.toml b/pkg/hanzo-tools-kms/pyproject.toml deleted file mode 100644 index 7236bf1e9..000000000 --- a/pkg/hanzo-tools-kms/pyproject.toml +++ /dev/null @@ -1,31 +0,0 @@ -[project] -name = "hanzo-tools-kms" -version = "0.1.0" -description = "KMS secret management MCP tool โ€” list, get, set, delete, inject secrets via Hanzo KMS" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "mcp", "kms", "secrets", "tools"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", -] - -dependencies = [ - "hanzo-tools-core>=0.1.0", - "hanzo-kms>=0.1.0", -] - -[project.entry-points."hanzo.tools"] -kms = "hanzo_tools.kms:TOOLS" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] diff --git a/pkg/hanzo-tools-llm/README.md b/pkg/hanzo-tools-llm/README.md deleted file mode 100644 index f4b6255bc..000000000 --- a/pkg/hanzo-tools-llm/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# hanzo-tools-llm - -LLM interaction tools for Hanzo AI MCP. - -## Tools - -- `llm` - Core LLM interaction with multiple providers -- `unified_llm` - Unified LLM interface -- `consensus` - Multi-model consensus for higher accuracy -- `llm_manage` - Model management and provider configuration - -## Installation - -```bash -pip install hanzo-tools-llm - -# With all LLM providers -pip install hanzo-tools-llm[full] -``` - -## Supported Providers - -- OpenAI (GPT-4, GPT-4o, etc.) -- Anthropic (Claude 4, Claude 3.5) -- Together AI -- Ollama (local models) - -## Usage - -```python -from hanzo_tools.llm import TOOLS, LLM_AVAILABLE, register_tools - -if LLM_AVAILABLE: - register_tools(mcp_server) -``` - -## Part of hanzo-tools - -This package is part of the modular [hanzo-tools](../hanzo-tools) ecosystem. diff --git a/pkg/hanzo-tools-llm/hanzo_tools/__init__.py b/pkg/hanzo-tools-llm/hanzo_tools/__init__.py deleted file mode 100644 index f4f8ea812..000000000 --- a/pkg/hanzo-tools-llm/hanzo_tools/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Hanzo Tools namespace package.""" - -__path__ = __import__("pkgutil").extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-llm/hanzo_tools/llm/__init__.py b/pkg/hanzo-tools-llm/hanzo_tools/llm/__init__.py deleted file mode 100644 index 169289921..000000000 --- a/pkg/hanzo-tools-llm/hanzo_tools/llm/__init__.py +++ /dev/null @@ -1,69 +0,0 @@ -"""LLM tools for Hanzo AI. - -Tools: -- llm: Unified LLM interface with model management -- consensus: Multi-model consensus - -Install: - pip install hanzo-tools-llm[full] -""" - -import logging - -logger = logging.getLogger(__name__) - -# Lazy imports for heavy dependencies -_tools = [] - -try: - from .llm_unified import UnifiedLLMTool - - # Alias for backwards compatibility - LLMTool = UnifiedLLMTool - _tools.append(UnifiedLLMTool) -except ImportError as e: - logger.debug(f"UnifiedLLMTool not available: {e}") - UnifiedLLMTool = None - LLMTool = None - -try: - from .consensus_tool import ConsensusTool - - _tools.append(ConsensusTool) -except ImportError as e: - logger.debug(f"ConsensusTool not available: {e}") - ConsensusTool = None - -TOOLS = _tools -LLM_AVAILABLE = len(_tools) > 0 - -__all__ = [ - "TOOLS", - "LLM_AVAILABLE", - "LLMTool", - "UnifiedLLMTool", - "ConsensusTool", - "register_tools", -] - - -def register_tools(mcp_server, enabled_tools: dict[str, bool] | None = None): - """Register LLM tools with MCP server.""" - from hanzo_tools.core import ToolRegistry - - enabled = enabled_tools or {} - registered = [] - - for tool_class in TOOLS: - if tool_class is None: - continue - tool_name = getattr(tool_class, "name", tool_class.__name__.lower()) - if enabled.get(tool_name, True): - try: - tool = tool_class() - ToolRegistry.register_tool(mcp_server, tool) - registered.append(tool) - except Exception as e: - logger.warning(f"Failed to register {tool_name}: {e}") - - return registered diff --git a/pkg/hanzo-tools-llm/hanzo_tools/llm/consensus_tool.py b/pkg/hanzo-tools-llm/hanzo_tools/llm/consensus_tool.py deleted file mode 100644 index ca300213b..000000000 --- a/pkg/hanzo-tools-llm/hanzo_tools/llm/consensus_tool.py +++ /dev/null @@ -1,131 +0,0 @@ -"""Consensus tool using Metastable protocol.""" - -import asyncio -from typing import List, Optional, Annotated, final, override - -from pydantic import Field -from hanzo_consensus import Result as ConsensusResult, run as run_consensus -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context - -from .llm_unified import LLMTool - - -@final -class ConsensusTool(BaseTool): - """Metastable consensus across multiple LLMs. - - https://github.com/luxfi/consensus - """ - - name = "consensus" - - DEFAULT_MODELS = [ - "gpt-4o-mini", - "claude-3-5-sonnet-20241022", - "gemini/gemini-1.5-pro", - ] - - def __init__(self): - self.llm_tool = LLMTool() - - @property - @override - def description(self) -> str: - return """Metastable consensus across multiple LLMs. - -Two-phase protocol: -- Phase I (Sampling): Models propose, k-peer sampling, confidence -- Phase II (Finality): Threshold aggregation, winner synthesis - -Usage: - consensus --prompt "Best approach?" --models '["gpt-4", "claude-3-5-sonnet"]' - consensus --prompt "Review this" --rounds 2 --k 2 - -Reference: https://github.com/luxfi/consensus -""" - - @override - @auto_timeout("consensus") - async def call( - self, - ctx: MCPContext, - prompt: Annotated[str, Field(description="Query")] = "", - models: Annotated[Optional[List[str]], Field(description="Models")] = None, - rounds: Annotated[int, Field(description="Sampling rounds")] = 3, - k: Annotated[int, Field(description="Sample size")] = 3, - alpha: Annotated[float, Field(description="Agreement")] = 0.6, - beta_1: Annotated[float, Field(description="Preference")] = 0.5, - beta_2: Annotated[float, Field(description="Decision")] = 0.8, - temperature: Annotated[float, Field(description="Temperature")] = 0.7, - **kwargs, - ) -> str: - """Run Metastable consensus.""" - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - if not prompt: - return "Error: prompt required" - - model_list = models or self.DEFAULT_MODELS - - # Filter to available models - available = [] - for m in model_list: - provider = self.llm_tool._get_provider_for_model(m) - if provider in self.llm_tool.available_providers: - available.append(m) - - if not available: - return "Error: No models available" - - await tool_ctx.info( - f"Metastable consensus: {len(available)} models, {rounds} rounds" - ) - - # Execute adapter - async def execute(model: str, model_prompt: str) -> ConsensusResult: - try: - import time - - start = time.time() - result = await self.llm_tool.call( - ctx, - model=model, - prompt=model_prompt, - temperature=temperature, - ) - ms = int((time.time() - start) * 1000) - return ConsensusResult(id=model, output=result, ok=True, ms=ms) - except Exception as e: - return ConsensusResult(id=model, output="", ok=False, error=str(e)) - - # Run consensus - state = await run_consensus( - prompt=prompt, - participants=available, - execute=execute, - rounds=rounds, - k=min(k, len(available)), - alpha=alpha, - beta_1=beta_1, - beta_2=beta_2, - ) - - lines = [ - "=== Metastable Consensus ===", - f"Models: {', '.join(available)}", - f"Rounds: {rounds}, k={k}", - f"Winner: {state.winner}", - f"Finalized: {state.finalized}", - "", - "=== Synthesis ===", - state.synthesis or "(no synthesis)", - ] - - return "\n".join(lines) - - def register(self, mcp_server) -> None: - """Register with MCP server.""" - pass diff --git a/pkg/hanzo-tools-llm/hanzo_tools/llm/llm_manage.py b/pkg/hanzo-tools-llm/hanzo_tools/llm/llm_manage.py deleted file mode 100644 index 19392e2b2..000000000 --- a/pkg/hanzo-tools-llm/hanzo_tools/llm/llm_manage.py +++ /dev/null @@ -1,455 +0,0 @@ -"""LLM management tool for enabling/disabling LLM providers.""" - -import json -from typing import Unpack, Optional, Annotated, TypedDict, final, override -from pathlib import Path - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, ToolContext, auto_timeout, create_tool_context - -from .llm_unified import LLMTool - -Action = Annotated[ - str, - Field( - description="Action to perform: list, enable, disable, test", - min_length=1, - ), -] - -Provider = Annotated[ - Optional[str], - Field( - description="Provider name (for enable/disable/test actions)", - default=None, - ), -] - -Model = Annotated[ - Optional[str], - Field( - description="Model to test (for test action)", - default=None, - ), -] - - -class LLMManageParams(TypedDict, total=False): - """Parameters for LLM management tool.""" - - action: str - provider: Optional[str] - model: Optional[str] - - -@final -class LLMManageTool(BaseTool): - """Tool for managing LLM providers.""" - - def __init__(self): - """Initialize the LLM management tool.""" - self.llm_tool = LLMTool() - self.config_file = Path.home() / ".hanzo" / "llm" / "providers.json" - self.config_file.parent.mkdir(parents=True, exist_ok=True) - self._load_config() - - def _load_config(self): - """Load provider configuration.""" - if self.config_file.exists(): - try: - with open(self.config_file, "r") as f: - self.config = json.load(f) - except Exception: - self.config = {"disabled_providers": []} - else: - self.config = {"disabled_providers": []} - - def _save_config(self): - """Save provider configuration.""" - with open(self.config_file, "w") as f: - json.dump(self.config, f, indent=2) - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "llm_manage" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Manage LLM providers and test configurations. - -Actions: -- list: Show all providers and their status -- models: List all available models (optionally filtered by provider) -- enable: Enable a provider's tools -- disable: Disable a provider's tools -- test: Test a model to verify it works - -Examples: -- llm_manage --action list -- llm_manage --action models -- llm_manage --action models --provider openai -- llm_manage --action enable --provider openai -- llm_manage --action disable --provider perplexity -- llm_manage --action test --model "gpt-4" -- llm_manage --action test --provider groq --model "mixtral" - -Providers are automatically detected based on environment variables: -- OpenAI: OPENAI_API_KEY -- Anthropic: ANTHROPIC_API_KEY or CLAUDE_API_KEY -- Google: GOOGLE_API_KEY or GEMINI_API_KEY -- Groq: GROQ_API_KEY -- Mistral: MISTRAL_API_KEY -- Perplexity: PERPLEXITY_API_KEY -- And many more... -""" - - @override - @auto_timeout("llm_manage") - async def call( - self, - ctx: MCPContext, - **params: Unpack[LLMManageParams], - ) -> str: - """Manage LLM providers. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Result of the management action - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - action = params.get("action") - if not action: - return "Error: action is required (list, enable, disable, test, models)" - - provider = params.get("provider") - model = params.get("model") - - # Handle different actions - if action == "list": - return self._list_providers() - elif action == "models": - return self._list_all_models(provider) - elif action == "enable": - return self._enable_provider(provider) - elif action == "disable": - return self._disable_provider(provider) - elif action == "test": - return await self._test_model(ctx, provider, model) - else: - return f"Error: Invalid action '{action}'. Must be one of: list, models, enable, disable, test" - - def _list_providers(self) -> str: - """List all providers and their status.""" - output = ["=== LLM Providers ==="] - output.append("") - - # Get all possible providers - all_providers = sorted(LLMTool.API_KEY_ENV_VARS.keys()) - available_providers = self.llm_tool.available_providers - disabled_providers = self.config.get("disabled_providers", []) - - # Categorize providers - active = [] - available_but_disabled = [] - no_api_key = [] - - for provider in all_providers: - if provider in available_providers: - if provider in disabled_providers: - available_but_disabled.append(provider) - else: - active.append(provider) - else: - no_api_key.append(provider) - - # Show active providers - if active: - output.append("โœ… Active Providers (API key found, enabled):") - for provider in active: - env_vars = available_providers.get(provider, []) - output.append(f" - {provider}: {', '.join(env_vars)}") - - # Show example models - examples = self._get_example_models(provider) - if examples: - output.append(f" Models: {', '.join(examples[:3])}") - output.append("") - - # Show disabled providers - if available_but_disabled: - output.append("โš ๏ธ Available but Disabled (API key found, disabled):") - for provider in available_but_disabled: - env_vars = available_providers.get(provider, []) - output.append(f" - {provider}: {', '.join(env_vars)}") - output.append( - f" Use: llm_manage --action enable --provider {provider}" - ) - output.append("") - - # Show providers without API keys - if no_api_key: - output.append("โŒ No API Key Found:") - for provider in no_api_key[:10]: # Show first 10 - env_vars = LLMTool.API_KEY_ENV_VARS.get(provider, []) - output.append(f" - {provider}: Set one of {', '.join(env_vars)}") - if len(no_api_key) > 10: - output.append(f" ... and {len(no_api_key) - 10} more") - output.append("") - - # Summary - output.append("=== Summary ===") - output.append(f"Total providers: {len(all_providers)}") - output.append(f"Active: {len(active)}") - output.append(f"Disabled: {len(available_but_disabled)}") - output.append(f"No API key: {len(no_api_key)}") - - # Show available tools - if active: - output.append("\n=== Available LLM Tools ===") - output.append("- llm: Universal LLM tool (all providers)") - output.append("- consensus: Query multiple models in parallel") - - provider_tools = [] - for provider in active: - if provider in [ - "openai", - "anthropic", - "google", - "groq", - "mistral", - "perplexity", - ]: - tool_name = "gemini" if provider == "google" else provider - provider_tools.append(tool_name) - - if provider_tools: - output.append(f"- Provider tools: {', '.join(provider_tools)}") - - return "\n".join(output) - - def _enable_provider(self, provider: Optional[str]) -> str: - """Enable a provider.""" - if not provider: - return "Error: provider is required for enable action" - - if provider not in self.llm_tool.available_providers: - env_vars = LLMTool.API_KEY_ENV_VARS.get(provider, []) - if env_vars: - return f"Error: No API key found for {provider}. Set one of: {', '.join(env_vars)}" - else: - return f"Error: Unknown provider '{provider}'" - - disabled = self.config.get("disabled_providers", []) - if provider in disabled: - disabled.remove(provider) - self.config["disabled_providers"] = disabled - self._save_config() - return f"Successfully enabled {provider}" - else: - return f"{provider} is already enabled" - - def _disable_provider(self, provider: Optional[str]) -> str: - """Disable a provider.""" - if not provider: - return "Error: provider is required for disable action" - - disabled = self.config.get("disabled_providers", []) - if provider not in disabled: - disabled.append(provider) - self.config["disabled_providers"] = disabled - self._save_config() - return f"Successfully disabled {provider}. Its tools will no longer be available." - else: - return f"{provider} is already disabled" - - def _list_all_models(self, provider: Optional[str] = None) -> str: - """List all available models from LLM.""" - try: - from .llm_unified import LLMTool - - all_models = LLMTool.get_all_models() - - if not all_models: - return "No models available or LLM not installed" - - output = ["=== Available LLM Models ==="] - - if provider: - # Show models for specific provider - provider_lower = provider.lower() - models = all_models.get(provider_lower, []) - - if not models: - return f"No models found for provider '{provider}'" - - output.append(f"\n{provider.upper()} ({len(models)} models):") - output.append("-" * 40) - - # Show first 50 models - for model in models[:50]: - output.append(f" {model}") - - if len(models) > 50: - output.append(f" ... and {len(models) - 50} more") - else: - # Show summary of all providers - total_models = sum(len(models) for models in all_models.values()) - output.append(f"Total models available: {total_models}") - output.append("") - - # Show providers with counts - for provider_name, models in sorted(all_models.items()): - if models: - output.append(f"{provider_name}: {len(models)} models") - - output.append( - "\nUse 'llm_manage --action models --provider ' to see models for a specific provider" - ) - - # Show recommended models - output.append("\n=== Recommended Models ===") - recommended = { - "OpenAI": ["gpt-4o", "gpt-4o-mini", "gpt-3.5-turbo"], - "Anthropic": [ - "claude-3-opus-20240229", - "claude-3-5-sonnet-20241022", - "claude-3-haiku-20240307", - ], - "Google": ["gemini/gemini-1.5-pro", "gemini/gemini-1.5-flash"], - "Groq": [ - "groq/llama3-70b-8192", - "groq/llama3-8b-8192", - "groq/gemma2-9b-it", - ], - "Mistral": [ - "mistral/mistral-large-latest", - "mistral/mistral-medium", - ], - } - - for provider_name, models in recommended.items(): - available = LLMTool().available_providers - provider_key = provider_name.lower() - - if provider_key in available: - output.append(f"\n{provider_name} (โœ… API key found):") - for model in models: - output.append(f" - {model}") - else: - output.append(f"\n{provider_name} (โŒ No API key):") - for model in models: - output.append(f" - {model}") - - return "\n".join(output) - - except Exception as e: - return f"Error listing models: {str(e)}" - - async def _test_model( - self, ctx: MCPContext, provider: Optional[str], model: Optional[str] - ) -> str: - """Test a model to verify it works.""" - if not model and not provider: - return "Error: Either model or provider is required for test action" - - # Determine model to test - if model: - test_model = model - else: - # Use default model for provider - default_models = { - "openai": "gpt-3.5-turbo", - "anthropic": "claude-3-haiku-20240307", - "google": "gemini/gemini-pro", - "groq": "groq/mixtral-8x7b-32768", - "mistral": "mistral/mistral-tiny", - "perplexity": "perplexity/sonar-small-online", - } - test_model = default_models.get(provider) - if not test_model: - return f"Error: No default model for provider '{provider}'. Please specify a model." - - # Test the model - test_prompt = "Hello! Please respond with 'OK' if you can hear me." - - output = [f"Testing model: {test_model}"] - output.append(f"Prompt: {test_prompt}") - output.append("") - - try: - # Call the LLM - params = { - "model": test_model, - "prompt": test_prompt, - "max_tokens": 10, - "temperature": 0, - } - - response = await self.llm_tool.call(ctx, **params) - - if response.startswith("Error:"): - output.append("โŒ Test failed:") - output.append(response) - else: - output.append("โœ… Test successful!") - output.append(f"Response: {response}") - output.append("") - output.append(f"Model '{test_model}' is working correctly.") - - # Show provider info - detected_provider = self.llm_tool._get_provider_for_model(test_model) - if detected_provider: - output.append(f"Provider: {detected_provider}") - - except Exception as e: - output.append("โŒ Test failed with exception:") - output.append(str(e)) - - return "\n".join(output) - - def _get_example_models(self, provider: str) -> list[str]: - """Get example models for a provider.""" - examples = { - "openai": ["gpt-4o", "gpt-4", "gpt-3.5-turbo", "o1-preview"], - "anthropic": [ - "claude-3-opus-20240229", - "claude-3-sonnet-20240229", - "claude-3-haiku-20240307", - ], - "google": [ - "gemini/gemini-pro", - "gemini/gemini-1.5-pro", - "gemini/gemini-1.5-flash", - ], - "groq": [ - "groq/mixtral-8x7b-32768", - "groq/llama3-70b-8192", - "groq/llama3-8b-8192", - ], - "mistral": [ - "mistral/mistral-large-latest", - "mistral/mistral-medium", - "mistral/mistral-small", - ], - "perplexity": [ - "perplexity/sonar-medium-online", - "perplexity/sonar-small-online", - ], - } - return examples.get(provider, []) - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-llm/hanzo_tools/llm/llm_unified.py b/pkg/hanzo-tools-llm/hanzo_tools/llm/llm_unified.py deleted file mode 100644 index b9dace8ae..000000000 --- a/pkg/hanzo-tools-llm/hanzo_tools/llm/llm_unified.py +++ /dev/null @@ -1,915 +0,0 @@ -"""Unified LLM tool with multiple actions including consensus mode.""" - -import os -import json -import asyncio -from typing import ( - Any, - Dict, - List, - Unpack, - Optional, - Annotated, - TypedDict, - final, - override, -) -from pathlib import Path - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, ToolContext, auto_timeout, create_tool_context - -# Check if llm is available -try: - import llm - - LLM_AVAILABLE = True -except ImportError: - LLM_AVAILABLE = False - - -# Parameter types -Action = Annotated[ - str, - Field( - description="Action to perform: query, consensus, list, models, enable, disable, test", - default="query", - ), -] - -Model = Annotated[ - Optional[str], - Field( - description="Model name (e.g., gpt-4, claude-3-opus-20240229)", - default=None, - ), -] - -Models = Annotated[ - Optional[List[str]], - Field( - description="List of models for consensus mode", - default=None, - ), -] - -Prompt = Annotated[ - Optional[str], - Field( - description="The prompt to send to the LLM", - default=None, - ), -] - -SystemPrompt = Annotated[ - Optional[str], - Field( - description="System prompt to set context", - default=None, - ), -] - -Temperature = Annotated[ - float, - Field( - description="Temperature for response randomness (0-2)", - default=0.7, - ), -] - -MaxTokens = Annotated[ - Optional[int], - Field( - description="Maximum tokens in response", - default=None, - ), -] - -JsonMode = Annotated[ - bool, - Field( - description="Request JSON formatted response", - default=False, - ), -] - -Stream = Annotated[ - bool, - Field( - description="Stream the response", - default=False, - ), -] - -Provider = Annotated[ - Optional[str], - Field( - description="Provider name for list/enable/disable actions", - default=None, - ), -] - -IncludeRaw = Annotated[ - bool, - Field( - description="Include raw responses in consensus mode", - default=False, - ), -] - -JudgeModel = Annotated[ - Optional[str], - Field( - description="Model to use as judge/aggregator in consensus", - default=None, - ), -] - -DevilsAdvocate = Annotated[ - bool, - Field( - description="Enable devil's advocate mode (10th model critiques others)", - default=False, - ), -] - -ConsensusSize = Annotated[ - Optional[int], - Field( - description="Number of models to use in consensus (default: 3)", - default=None, - ), -] - - -class LLMParams(TypedDict, total=False): - """Parameters for LLM tool.""" - - action: str - model: Optional[str] - models: Optional[List[str]] - prompt: Optional[str] - system_prompt: Optional[str] - temperature: float - max_tokens: Optional[int] - json_mode: bool - stream: bool - provider: Optional[str] - include_raw: bool - judge_model: Optional[str] - devils_advocate: bool - consensus_size: Optional[int] - - -@final -class UnifiedLLMTool(BaseTool): - """Unified LLM tool with multiple actions.""" - - # Config file for settings - CONFIG_FILE = Path.home() / ".hanzo" / "mcp" / "llm_config.json" - - # Default consensus models in order of preference - DEFAULT_CONSENSUS_MODELS = [ - "gpt-4o", # OpenAI's latest - "claude-3-opus-20240229", # Claude's most capable - "gemini/gemini-1.5-pro", # Google's best - "groq/llama3-70b-8192", # Fast Groq - "mistral/mistral-large-latest", # Mistral's best - "perplexity/llama-3.1-sonar-large-128k-chat", # Perplexity with search - ] - - # API key environment variables - API_KEY_ENV_VARS = { - "openai": ["OPENAI_API_KEY"], - "anthropic": ["ANTHROPIC_API_KEY", "CLAUDE_API_KEY"], - "google": ["GOOGLE_API_KEY", "GEMINI_API_KEY"], - "groq": ["GROQ_API_KEY"], - "mistral": ["MISTRAL_API_KEY"], - "perplexity": ["PERPLEXITY_API_KEY", "PERPLEXITYAI_API_KEY"], - "together": ["TOGETHER_API_KEY", "TOGETHERAI_API_KEY"], - "cohere": ["COHERE_API_KEY"], - "replicate": ["REPLICATE_API_KEY"], - "huggingface": ["HUGGINGFACE_API_KEY", "HF_TOKEN"], - "bedrock": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"], - "vertex": ["GOOGLE_APPLICATION_CREDENTIALS"], - "azure": ["AZURE_API_KEY"], - "voyage": ["VOYAGE_API_KEY"], - "deepseek": ["DEEPSEEK_API_KEY"], - } - - def __init__(self): - """Initialize the unified LLM tool.""" - self.available_providers = self._detect_available_providers() - self.config = self._load_config() - - def _detect_available_providers(self) -> Dict[str, List[str]]: - """Detect which providers have API keys configured.""" - available = {} - - for provider, env_vars in self.API_KEY_ENV_VARS.items(): - for var in env_vars: - if os.getenv(var): - available[provider] = env_vars - break - - return available - - def _load_config(self) -> Dict[str, Any]: - """Load configuration from file.""" - if self.CONFIG_FILE.exists(): - try: - with open(self.CONFIG_FILE, "r") as f: - return json.load(f) - except Exception: - pass - - # Default config - return { - "disabled_providers": [], - "consensus_models": None, # Use defaults if None - "default_judge_model": "gpt-4o", - "consensus_size": 3, - } - - def _save_config(self): - """Save configuration to file.""" - self.CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True) - with open(self.CONFIG_FILE, "w") as f: - json.dump(self.config, f, indent=2) - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "llm" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - available = list(self.available_providers.keys()) - - return f"""Query LLMs. Default: single query. Actions: consensus, list, models, test. - -Usage: -llm "What is the capital of France?" -llm "Explain this code" --model gpt-4o -llm --action consensus "Is this approach correct?" --devils-advocate -llm --action models --provider openai - -Available: {", ".join(available) if available else "None"}""" - - @override - @auto_timeout("llm_unified") - async def call( - self, - ctx: MCPContext, - **params: Unpack[LLMParams], - ) -> str: - """Execute LLM action.""" - # Create tool context only if we have a proper MCP context - tool_ctx = None - try: - if hasattr(ctx, "client") and ctx.client and hasattr(ctx.client, "server"): - tool_ctx = create_tool_context(ctx) - if tool_ctx: - await tool_ctx.set_tool_info(self.name) - except Exception: - # Running in test mode without MCP context - pass - - if not LLM_AVAILABLE: - return ( - "Error: LLM is not installed. Install it with: pip install llm" - ) - - # Extract action - action = params.get("action", "query") - - # Route to appropriate handler - if action == "query": - return await self._handle_query(tool_ctx, params) - elif action == "consensus": - return await self._handle_consensus(tool_ctx, params) - elif action == "list": - return self._handle_list() - elif action == "models": - return self._handle_models(params.get("provider")) - elif action == "enable": - return self._handle_enable(params.get("provider")) - elif action == "disable": - return self._handle_disable(params.get("provider")) - elif action == "test": - return await self._handle_test( - tool_ctx, params.get("model"), params.get("provider") - ) - else: - return f"Error: Unknown action '{action}'. Valid actions: query, consensus, list, models, enable, disable, test" - - async def _handle_query(self, tool_ctx, params: Dict[str, Any]) -> str: - """Handle single model query.""" - model = params.get("model") - prompt = params.get("prompt") - - if not prompt: - return "Error: prompt is required for query action" - - # Auto-select model if not specified - if not model: - if self.available_providers: - # Use first available model - if "openai" in self.available_providers: - model = "gpt-4o-mini" - elif "anthropic" in self.available_providers: - model = "claude-3-haiku-20240307" - elif "google" in self.available_providers: - model = "gemini/gemini-1.5-flash" - else: - # Use first provider's default - provider = list(self.available_providers.keys())[0] - model = f"{provider}/default" - else: - return "Error: No model specified and no API keys found" - - # Check if we have API key for this model - provider = self._get_provider_for_model(model) - if provider and provider not in self.available_providers: - env_vars = self.API_KEY_ENV_VARS.get(provider, []) - return f"Error: No API key found for {provider}. Set one of: {', '.join(env_vars)}" - - # Build messages - messages = [] - if params.get("system_prompt"): - messages.append({"role": "system", "content": params["system_prompt"]}) - messages.append({"role": "user", "content": prompt}) - - # Build kwargs - kwargs = { - "model": model, - "messages": messages, - "temperature": params.get("temperature", 0.7), - } - - if params.get("max_tokens"): - kwargs["max_tokens"] = params["max_tokens"] - - if params.get("json_mode"): - kwargs["response_format"] = {"type": "json_object"} - - if params.get("stream"): - kwargs["stream"] = True - - try: - if tool_ctx: - await tool_ctx.info(f"Querying {model}...") - - if kwargs.get("stream"): - # Handle streaming response - response_text = "" - async for chunk in await llm.acompletion(**kwargs): - if chunk.choices[0].delta.content: - response_text += chunk.choices[0].delta.content - return response_text - else: - # Regular response - response = await llm.acompletion(**kwargs) - return response.choices[0].message.content - - except Exception as e: - error_msg = str(e) - if "model_not_found" in error_msg or "does not exist" in error_msg: - return f"Error: Model '{model}' not found. Use 'llm --action models' to see available models." - else: - return f"Error calling LLM: {error_msg}" - - async def _handle_consensus(self, tool_ctx, params: Dict[str, Any]) -> str: - """Handle consensus mode with multiple models.""" - prompt = params.get("prompt") - if not prompt: - return "Error: prompt is required for consensus action" - - # Determine models to use - models = params.get("models") - if not models: - # Use configured or default models - consensus_size = params.get("consensus_size") or self.config.get( - "consensus_size", 3 - ) - models = self._get_consensus_models(consensus_size) - - if not models: - return "Error: No models available for consensus. Set API keys for at least 2 providers." - - if len(models) < 2: - return "Error: Consensus requires at least 2 models" - - # Check for devil's advocate mode - devils_advocate = params.get("devils_advocate", False) - if devils_advocate and len(models) < 3: - return "Error: Devil's advocate mode requires at least 3 models" - - if tool_ctx: - await tool_ctx.info(f"Running consensus with {len(models)} models...") - - # Query models in parallel - system_prompt = params.get("system_prompt") - temperature = params.get("temperature", 0.7) - max_tokens = params.get("max_tokens") - - # Split models if using devil's advocate - if devils_advocate: - consensus_models = models[:-1] - devil_model = models[-1] - else: - consensus_models = models - devil_model = None - - # Query consensus models - responses = await self._query_models_parallel( - consensus_models, prompt, system_prompt, temperature, max_tokens, tool_ctx - ) - - # Get devil's advocate response if enabled - devil_response = None - if devil_model: - # Create devil's advocate prompt - responses_text = "\n\n".join( - [ - f"Model {i + 1}: {resp['response']}" - for i, resp in enumerate(responses) - if resp["response"] - ] - ) - - devil_prompt = f"""You are a critical analyst. Review these responses to the question below and provide a devil's advocate perspective. Challenge assumptions, point out weaknesses, and suggest alternative viewpoints. - -Original Question: {prompt} - -Responses from other models: -{responses_text} - -Provide your critical analysis:""" - - devil_result = await self._query_single_model( - devil_model, devil_prompt, system_prompt, temperature, max_tokens - ) - - if devil_result["success"]: - devil_response = { - "model": devil_model, - "response": devil_result["response"], - "time_ms": devil_result["time_ms"], - } - - # Aggregate responses - judge_model = params.get("judge_model") or self.config.get( - "default_judge_model", "gpt-4o" - ) - include_raw = params.get("include_raw", False) - - return await self._aggregate_consensus( - responses, prompt, judge_model, include_raw, devil_response, tool_ctx - ) - - def _handle_list(self) -> str: - """List available providers.""" - output = ["=== LLM Providers ==="] - - # Get all possible providers - all_providers = sorted(self.API_KEY_ENV_VARS.keys()) - disabled = self.config.get("disabled_providers", []) - - output.append(f"Total providers: {len(all_providers)}") - output.append(f"Available: {len(self.available_providers)}") - output.append(f"Disabled: {len(disabled)}\n") - - for provider in all_providers: - status_parts = [] - - # Check if API key exists - if provider in self.available_providers: - status_parts.append("โœ… API key found") - else: - status_parts.append("โŒ No API key") - - # Check if disabled - if provider in disabled: - status_parts.append("๐Ÿšซ Disabled") - - # Show environment variables - env_vars = self.API_KEY_ENV_VARS.get(provider, []) - status = " | ".join(status_parts) - - output.append(f"{provider}: {status}") - output.append(f" Environment variables: {', '.join(env_vars)}") - - output.append( - "\nUse 'llm --action enable/disable --provider ' to manage providers" - ) - - return "\n".join(output) - - def _handle_models(self, provider: Optional[str] = None) -> str: - """List available models.""" - try: - all_models = self._get_all_models() - - if not all_models: - return "No models available or LLM not properly initialized" - - output = ["=== Available LLM Models ==="] - - if provider: - # Show models for specific provider - provider_lower = provider.lower() - models = all_models.get(provider_lower, []) - - if not models: - return f"No models found for provider '{provider}'" - - output.append(f"\n{provider.upper()} ({len(models)} models):") - output.append("-" * 40) - - # Show first 50 models - for model in models[:50]: - output.append(f" {model}") - - if len(models) > 50: - output.append(f" ... and {len(models) - 50} more") - else: - # Show summary of all providers - total_models = sum(len(models) for models in all_models.values()) - output.append(f"Total models available: {total_models}") - output.append("") - - # Show providers with counts - for provider_name, models in sorted(all_models.items()): - if models: - available = ( - "โœ…" if provider_name in self.available_providers else "โŒ" - ) - output.append( - f"{available} {provider_name}: {len(models)} models" - ) - - output.append( - "\nUse 'llm --action models --provider ' to see specific models" - ) - - return "\n".join(output) - - except Exception as e: - return f"Error listing models: {str(e)}" - - def _handle_enable(self, provider: Optional[str]) -> str: - """Enable a provider.""" - if not provider: - return "Error: provider is required for enable action" - - provider = provider.lower() - disabled = self.config.get("disabled_providers", []) - - if provider in disabled: - disabled.remove(provider) - self.config["disabled_providers"] = disabled - self._save_config() - return f"Successfully enabled {provider}" - else: - return f"{provider} is already enabled" - - def _handle_disable(self, provider: Optional[str]) -> str: - """Disable a provider.""" - if not provider: - return "Error: provider is required for disable action" - - provider = provider.lower() - disabled = self.config.get("disabled_providers", []) - - if provider not in disabled: - disabled.append(provider) - self.config["disabled_providers"] = disabled - self._save_config() - return f"Successfully disabled {provider}" - else: - return f"{provider} is already disabled" - - async def _handle_test( - self, tool_ctx, model: Optional[str], provider: Optional[str] - ) -> str: - """Test a model or provider.""" - if not model and not provider: - return "Error: Either model or provider is required for test action" - - # If provider specified, test its default model - if provider and not model: - provider = provider.lower() - if provider == "openai": - model = "gpt-3.5-turbo" - elif provider == "anthropic": - model = "claude-3-haiku-20240307" - elif provider == "google": - model = "gemini/gemini-1.5-flash" - elif provider == "groq": - model = "groq/llama3-8b-8192" - else: - model = f"{provider}/default" - - # Test the model - test_prompt = "Say 'Hello from Hanzo MCP!' in exactly 5 words." - - try: - if tool_ctx: - await tool_ctx.info(f"Testing {model}...") - - response = await llm.acompletion( - model=model, - messages=[{"role": "user", "content": test_prompt}], - temperature=0, - max_tokens=20, - ) - - result = response.choices[0].message.content - return f"โœ… {model} is working!\nResponse: {result}" - - except Exception as e: - return f"โŒ {model} failed: {str(e)}" - - def _get_consensus_models(self, size: int) -> List[str]: - """Get models for consensus based on availability.""" - # Use configured models if set - configured = self.config.get("consensus_models") - if configured: - return configured[:size] - - # Otherwise, build list from available providers - models = [] - disabled = self.config.get("disabled_providers", []) - - # Try default models first - for model in self.DEFAULT_CONSENSUS_MODELS: - if len(models) >= size: - break - - provider = self._get_provider_for_model(model) - if ( - provider - and provider in self.available_providers - and provider not in disabled - ): - models.append(model) - - # If still need more, add from available providers - if len(models) < size: - for provider in self.available_providers: - if provider in disabled: - continue - - if provider == "openai" and "gpt-4o" not in models: - models.append("gpt-4o") - elif provider == "anthropic" and "claude-3-opus-20240229" not in models: - models.append("claude-3-opus-20240229") - elif provider == "google" and "gemini/gemini-1.5-pro" not in models: - models.append("gemini/gemini-1.5-pro") - - if len(models) >= size: - break - - return models - - async def _query_models_parallel( - self, - models: List[str], - prompt: str, - system_prompt: Optional[str], - temperature: float, - max_tokens: Optional[int], - tool_ctx, - ) -> List[Dict[str, Any]]: - """Query multiple models in parallel.""" - - async def query_with_info(model: str) -> Dict[str, Any]: - result = await self._query_single_model( - model, prompt, system_prompt, temperature, max_tokens - ) - return { - "model": model, - "response": result.get("response"), - "success": result.get("success", False), - "error": result.get("error"), - "time_ms": result.get("time_ms", 0), - } - - # Run all queries in parallel - tasks = [query_with_info(model) for model in models] - results = await asyncio.gather(*tasks) - - # Report results - successful = sum(1 for r in results if r["success"]) - if tool_ctx: - await tool_ctx.info(f"Completed {successful}/{len(models)} model queries") - - return results - - async def _query_single_model( - self, - model: str, - prompt: str, - system_prompt: Optional[str], - temperature: float, - max_tokens: Optional[int], - ) -> Dict[str, Any]: - """Query a single model and return result with metadata.""" - import time - - start_time = time.time() - - try: - messages = [] - if system_prompt: - messages.append({"role": "system", "content": system_prompt}) - messages.append({"role": "user", "content": prompt}) - - kwargs = { - "model": model, - "messages": messages, - "temperature": temperature, - } - if max_tokens: - kwargs["max_tokens"] = max_tokens - - response = await llm.acompletion(**kwargs) - - return { - "success": True, - "response": response.choices[0].message.content, - "time_ms": int((time.time() - start_time) * 1000), - } - - except Exception as e: - return { - "success": False, - "error": str(e), - "time_ms": int((time.time() - start_time) * 1000), - } - - async def _aggregate_consensus( - self, - responses: List[Dict[str, Any]], - original_prompt: str, - judge_model: str, - include_raw: bool, - devil_response: Optional[Dict[str, Any]], - tool_ctx, - ) -> str: - """Aggregate consensus responses using a judge model.""" - # Prepare response data - successful_responses = [r for r in responses if r["success"]] - - if not successful_responses: - return "Error: All models failed to respond" - - # Format responses for aggregation - responses_text = "\n\n".join( - [ - f"Model: {r['model']}\nResponse: {r['response']}" - for r in successful_responses - ] - ) - - if devil_response: - responses_text += f"\n\nDevil's Advocate ({devil_response['model']}):\n{devil_response['response']}" - - # Create aggregation prompt - aggregation_prompt = f"""Analyze the following responses from multiple AI models to this question: - - -{original_prompt} - - - -{responses_text} - - -Please provide: -1. A synthesis of the key points where models agree -2. Notable differences or disagreements between responses -3. A balanced conclusion incorporating the best insights -{f"4. Evaluation of the devil's advocate critique" if devil_response else ""} - -Be concise and highlight the most important findings.""" - - # Get aggregation - try: - if tool_ctx: - await tool_ctx.info(f"Aggregating responses with {judge_model}...") - - judge_result = await self._query_single_model( - judge_model, aggregation_prompt, None, 0.3, None - ) - - if not judge_result["success"]: - return f"Error: Judge model failed: {judge_result.get('error', 'Unknown error')}" - - # Format output - output = [ - f"=== Consensus Analysis ({len(successful_responses)} models) ===\n" - ] - output.append(judge_result["response"]) - - # Add model list - output.append( - f"\nModels consulted: {', '.join([r['model'] for r in successful_responses])}" - ) - if devil_response: - output.append(f"Devil's Advocate: {devil_response['model']}") - - # Add timing info - avg_time = sum(r["time_ms"] for r in responses) / len(responses) - output.append(f"\nAverage response time: {avg_time:.0f}ms") - - # Include raw responses if requested - if include_raw: - output.append("\n\n=== Raw Responses ===") - for r in successful_responses: - output.append(f"\n{r['model']}:") - output.append("-" * 40) - output.append(r["response"]) - - if devil_response: - output.append(f"\nDevil's Advocate ({devil_response['model']}):") - output.append("-" * 40) - output.append(devil_response["response"]) - - return "\n".join(output) - - except Exception as e: - return f"Error during aggregation: {str(e)}" - - def _get_provider_for_model(self, model: str) -> Optional[str]: - """Determine the provider for a given model.""" - model_lower = model.lower() - - # Check explicit provider prefix - if "/" in model: - return model.split("/")[0] - - # Check model prefixes - if model_lower.startswith("gpt"): - return "openai" - elif model_lower.startswith("claude"): - return "anthropic" - elif model_lower.startswith("gemini"): - return "google" - elif model_lower.startswith("command"): - return "cohere" - - # Default to OpenAI - return "openai" - - def _get_all_models(self) -> Dict[str, List[str]]: - """Get all available models from LLM.""" - try: - import llm - - # Get all models - all_models = llm.model_list - - # Organize by provider - providers = {} - - for model in all_models: - # Extract provider - if "/" in model: - provider = model.split("/")[0] - elif model.startswith("gpt"): - provider = "openai" - elif model.startswith("claude"): - provider = "anthropic" - elif model.startswith("gemini"): - provider = "google" - elif model.startswith("command"): - provider = "cohere" - else: - provider = "other" - - if provider not in providers: - providers[provider] = [] - providers[provider].append(model) - - # Sort models within each provider - for provider in providers: - providers[provider] = sorted(providers[provider]) - - return providers - except Exception: - return {} - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass - - -# Alias for backwards compatibility -LLMTool = UnifiedLLMTool diff --git a/pkg/hanzo-tools-llm/hanzo_tools/llm/provider_tools.py b/pkg/hanzo-tools-llm/hanzo_tools/llm/provider_tools.py deleted file mode 100644 index 2bc3da343..000000000 --- a/pkg/hanzo-tools-llm/hanzo_tools/llm/provider_tools.py +++ /dev/null @@ -1,413 +0,0 @@ -"""Provider-specific LLM tools.""" - -from typing import Dict, Unpack, Optional, Annotated, TypedDict, final, override - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout - -from .llm_unified import LLMTool - -Prompt = Annotated[ - str, - Field( - description="The prompt or question to send to the model", - min_length=1, - ), -] - -Model = Annotated[ - Optional[str], - Field( - description="Specific model variant (defaults to provider's best model)", - default=None, - ), -] - -SystemPrompt = Annotated[ - Optional[str], - Field( - description="System prompt to set context", - default=None, - ), -] - -Temperature = Annotated[ - float, - Field( - description="Temperature for response randomness (0.0-2.0)", - default=0.7, - ), -] - -MaxTokens = Annotated[ - Optional[int], - Field( - description="Maximum tokens in response", - default=None, - ), -] - -JsonMode = Annotated[ - bool, - Field( - description="Request JSON formatted response", - default=False, - ), -] - - -class ProviderToolParams(TypedDict, total=False): - """Parameters for provider-specific tools.""" - - prompt: str - model: Optional[str] - system_prompt: Optional[str] - temperature: float - max_tokens: Optional[int] - json_mode: bool - - -class BaseProviderTool(BaseTool): - """Base class for provider-specific LLM tools.""" - - def __init__( - self, provider: str, default_model: str, model_variants: Dict[str, str] - ): - """Initialize provider tool. - - Args: - provider: Provider name - default_model: Default model to use - model_variants: Map of short names to full model names - """ - self.provider = provider - self.default_model = default_model - self.model_variants = model_variants - self.llm_tool = LLMTool() - self.is_available = provider in self.llm_tool.available_providers - - def get_full_model_name(self, model: Optional[str]) -> str: - """Get full model name from short name or default.""" - if not model: - return self.default_model - - # Check if it's a short name - if model in self.model_variants: - return self.model_variants[model] - - # Return as-is if not found (assume full name) - return model - - @override - @auto_timeout("provider_tools") - async def call( - self, - ctx: MCPContext, - **params: Unpack[ProviderToolParams], - ) -> str: - """Call the provider's LLM.""" - if not self.is_available: - env_vars = LLMTool.API_KEY_ENV_VARS.get(self.provider, []) - return f"Error: {self.provider.title()} API key not found. Set one of: {', '.join(env_vars)}" - - # Get full model name - model = self.get_full_model_name(params.get("model")) - - # Prepare LLM tool parameters - llm_params = { - "model": model, - "prompt": params["prompt"], - } - - # Add optional parameters - if "system_prompt" in params: - llm_params["system_prompt"] = params["system_prompt"] - if "temperature" in params: - llm_params["temperature"] = params["temperature"] - if "max_tokens" in params: - llm_params["max_tokens"] = params["max_tokens"] - if "json_mode" in params: - llm_params["json_mode"] = params["json_mode"] - - # Call the LLM tool - return await self.llm_tool.call(ctx, **llm_params) - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass - - -@final -class OpenAITool(BaseProviderTool): - """OpenAI-specific LLM tool.""" - - def __init__(self): - super().__init__( - provider="openai", - default_model="gpt-4o", - model_variants={ - "4o": "gpt-4o", - "4": "gpt-4", - "3.5": "gpt-3.5-turbo", - "o1": "o1-preview", - "o1-mini": "o1-mini", - "4-turbo": "gpt-4-turbo-preview", - "4-vision": "gpt-4-vision-preview", - }, - ) - - @property - @override - def name(self) -> str: - return "openai" - - @property - @override - def description(self) -> str: - status = "โœ“ Available" if self.is_available else "โœ— No API key" - return f"""Query OpenAI models directly ({status}). - -Models: -- 4o (default): GPT-4o - Latest and most capable -- 4: GPT-4 - Advanced reasoning -- 3.5: GPT-3.5 Turbo - Fast and efficient -- o1: O1 Preview - Chain of thought reasoning -- o1-mini: O1 Mini - Smaller reasoning model - -Examples: -- openai --prompt "Explain quantum computing" -- openai --model 4 --prompt "Write a Python function" -- openai --model o1 --prompt "Solve this step by step" -""" - - -@final -class AnthropicTool(BaseProviderTool): - """Anthropic-specific LLM tool.""" - - def __init__(self): - super().__init__( - provider="anthropic", - default_model="claude-3-sonnet-20240229", - model_variants={ - "opus": "claude-3-opus-20240229", - "sonnet": "claude-3-sonnet-20240229", - "haiku": "claude-3-haiku-20240307", - "2.1": "claude-2.1", - "2": "claude-2", - "instant": "claude-instant-1.2", - }, - ) - - @property - @override - def name(self) -> str: - return "anthropic" - - @property - @override - def description(self) -> str: - status = "โœ“ Available" if self.is_available else "โœ— No API key" - return f"""Query Anthropic Claude models directly ({status}). - -Models: -- sonnet (default): Claude 3 Sonnet - Balanced performance -- opus: Claude 3 Opus - Most capable -- haiku: Claude 3 Haiku - Fast and efficient -- 2.1: Claude 2.1 - Previous generation -- instant: Claude Instant - Very fast - -Examples: -- anthropic --prompt "Analyze this code" -- anthropic --model opus --prompt "Write a detailed essay" -- anthropic --model haiku --prompt "Quick question" -""" - - -@final -class GeminiTool(BaseProviderTool): - """Google Gemini-specific LLM tool.""" - - def __init__(self): - super().__init__( - provider="google", - default_model="gemini/gemini-pro", - model_variants={ - "pro": "gemini/gemini-pro", - "pro-vision": "gemini/gemini-pro-vision", - "1.5-pro": "gemini/gemini-1.5-pro-latest", - "1.5-flash": "gemini/gemini-1.5-flash-latest", - "ultra": "gemini/gemini-ultra", - }, - ) - - @property - @override - def name(self) -> str: - return "gemini" - - @property - @override - def description(self) -> str: - status = "โœ“ Available" if self.is_available else "โœ— No API key" - return f"""Query Google Gemini models directly ({status}). - -Models: -- pro (default): Gemini Pro - Balanced model -- 1.5-pro: Gemini 1.5 Pro - Advanced with long context -- 1.5-flash: Gemini 1.5 Flash - Fast and efficient -- pro-vision: Gemini Pro Vision - Multimodal -- ultra: Gemini Ultra - Most capable (if available) - -Examples: -- gemini --prompt "Explain this concept" -- gemini --model 1.5-pro --prompt "Analyze this long document" -- gemini --model 1.5-flash --prompt "Quick task" -""" - - -@final -class GroqTool(BaseProviderTool): - """Groq-specific LLM tool.""" - - def __init__(self): - super().__init__( - provider="groq", - default_model="groq/mixtral-8x7b-32768", - model_variants={ - "mixtral": "groq/mixtral-8x7b-32768", - "llama3-70b": "groq/llama3-70b-8192", - "llama3-8b": "groq/llama3-8b-8192", - "llama2-70b": "groq/llama2-70b-4096", - "gemma-7b": "groq/gemma-7b-it", - }, - ) - - @property - @override - def name(self) -> str: - return "groq" - - @property - @override - def description(self) -> str: - status = "โœ“ Available" if self.is_available else "โœ— No API key" - return f"""Query Groq LPU models - ultra-fast inference ({status}). - -Models: -- mixtral (default): Mixtral 8x7B - High quality -- llama3-70b: Llama 3 70B - Very capable -- llama3-8b: Llama 3 8B - Fast and efficient -- llama2-70b: Llama 2 70B - Previous gen -- gemma-7b: Google Gemma 7B - Efficient - -Examples: -- groq --prompt "Fast response needed" -- groq --model llama3-70b --prompt "Complex reasoning" -- groq --model gemma-7b --prompt "Quick task" -""" - - -@final -class MistralTool(BaseProviderTool): - """Mistral-specific LLM tool.""" - - def __init__(self): - super().__init__( - provider="mistral", - default_model="mistral/mistral-medium", - model_variants={ - "tiny": "mistral/mistral-tiny", - "small": "mistral/mistral-small-latest", - "medium": "mistral/mistral-medium-latest", - "large": "mistral/mistral-large-latest", - "embed": "mistral/mistral-embed", - }, - ) - - @property - @override - def name(self) -> str: - return "mistral" - - @property - @override - def description(self) -> str: - status = "โœ“ Available" if self.is_available else "โœ— No API key" - return f"""Query Mistral AI models directly ({status}). - -Models: -- medium (default): Mistral Medium - Balanced -- large: Mistral Large - Most capable -- small: Mistral Small - Efficient -- tiny: Mistral Tiny - Very fast - -Examples: -- mistral --prompt "Explain this" -- mistral --model large --prompt "Complex analysis" -- mistral --model tiny --prompt "Quick response" -""" - - -@final -class PerplexityTool(BaseProviderTool): - """Perplexity-specific LLM tool.""" - - def __init__(self): - super().__init__( - provider="perplexity", - default_model="perplexity/sonar-medium-online", - model_variants={ - "sonar-small": "perplexity/sonar-small-online", - "sonar-medium": "perplexity/sonar-medium-online", - "sonar-small-chat": "perplexity/sonar-small-chat", - "sonar-medium-chat": "perplexity/sonar-medium-chat", - }, - ) - - @property - @override - def name(self) -> str: - return "perplexity" - - @property - @override - def description(self) -> str: - status = "โœ“ Available" if self.is_available else "โœ— No API key" - return f"""Query Perplexity models with internet access ({status}). - -Models: -- sonar-medium (default): Online search + reasoning -- sonar-small: Faster online search -- sonar-medium-chat: Chat without search -- sonar-small-chat: Fast chat without search - -Examples: -- perplexity --prompt "Latest news about AI" -- perplexity --model sonar-small --prompt "Quick fact check" -- perplexity --model sonar-medium-chat --prompt "Explain without search" -""" - - -# Export all provider tools -PROVIDER_TOOLS = [ - OpenAITool, - AnthropicTool, - GeminiTool, - GroqTool, - MistralTool, - PerplexityTool, -] - - -def create_provider_tools() -> list[BaseTool]: - """Create instances of all provider tools.""" - tools = [] - for tool_class in PROVIDER_TOOLS: - tool = tool_class() - # Only include tools with available API keys - if tool.is_available: - tools.append(tool) - return tools diff --git a/pkg/hanzo-tools-llm/pyproject.toml b/pkg/hanzo-tools-llm/pyproject.toml deleted file mode 100644 index 47e919ccf..000000000 --- a/pkg/hanzo-tools-llm/pyproject.toml +++ /dev/null @@ -1,29 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-llm" -version = "0.2.0" -description = "LLM tools for Hanzo AI - model management, consensus, providers" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "tools", "llm", "mcp", "ai"] -dependencies = [ - "hanzo-tools>=0.3.0", - "hanzo-consensus>=0.1.0", - "mcp>=1.25.0", - "fastmcp>=2.14.1", - "pydantic>=2.12.5", -] - -[project.optional-dependencies] -full = ["hanzo-llm>=1.0.0", "openai>=1.0.0", "anthropic>=0.40.0"] - -[project.entry-points."hanzo.tools"] -llm = "hanzo_tools.llm:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] diff --git a/pkg/hanzo-tools-llm/tests/test_llm_tools.py b/pkg/hanzo-tools-llm/tests/test_llm_tools.py deleted file mode 100644 index 701b0f477..000000000 --- a/pkg/hanzo-tools-llm/tests/test_llm_tools.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Tests for hanzo-tools-llm.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import llm - - assert llm is not None - - def test_import_tools(self): - from hanzo_tools.llm import TOOLS - - assert len(TOOLS) > 0 diff --git a/pkg/hanzo-tools-lsp/README.md b/pkg/hanzo-tools-lsp/README.md deleted file mode 100644 index 8ecc1a1b8..000000000 --- a/pkg/hanzo-tools-lsp/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# hanzo-tools-lsp - -Language Server Protocol tools for Hanzo MCP. - -## Installation - -```bash -pip install hanzo-tools-lsp -``` - -## Tools - -### lsp - Language Server Protocol -Code intelligence via LSP servers. - -**Go to definition:** -```python -lsp(action="definition", file="/path/to/file.py", line=10, character=15) -``` - -**Find references:** -```python -lsp(action="references", file="/path/to/file.py", line=10, character=15) -``` - -**Hover information:** -```python -lsp(action="hover", file="/path/to/file.py", line=10, character=15) -``` - -**Code completion:** -```python -lsp(action="completion", file="/path/to/file.py", line=10, character=15) -``` - -**Diagnostics:** -```python -lsp(action="diagnostics", file="/path/to/file.py") -``` - -**Rename symbol:** -```python -lsp(action="rename", file="/path/to/file.py", line=10, character=15, new_name="newName") -``` - -**Check status:** -```python -lsp(action="status") # Check LSP server status -``` - -## Supported Languages - -- Go (gopls) -- Python (pyright) -- TypeScript/JavaScript (typescript-language-server) -- Rust (rust-analyzer) -- Java (jdtls) -- C/C++ (clangd) -- Ruby (solargraph) -- Lua (lua-language-server) - -Language servers are automatically installed as needed. - -## License - -MIT diff --git a/pkg/hanzo-tools-lsp/hanzo_tools/__init__.py b/pkg/hanzo-tools-lsp/hanzo_tools/__init__.py deleted file mode 100644 index 004279ba9..000000000 --- a/pkg/hanzo-tools-lsp/hanzo_tools/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Hanzo tools namespace package.""" - -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-lsp/hanzo_tools/lsp/__init__.py b/pkg/hanzo-tools-lsp/hanzo_tools/lsp/__init__.py deleted file mode 100644 index 2b6e52106..000000000 --- a/pkg/hanzo-tools-lsp/hanzo_tools/lsp/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Language Server Protocol tools for Hanzo MCP. - -This package provides LSP-based code intelligence: -- LSPTool: Go-to-definition, find references, rename, hover, completion -""" - -from mcp.server import FastMCP - -from hanzo_tools.core import BaseTool, ToolRegistry -from hanzo_tools.lsp.lsp_tool import LSPTool, create_lsp_tool - -__all__ = [ - "LSPTool", - "create_lsp_tool", - "get_lsp_tools", - "register_lsp_tools", - "TOOLS", -] - - -def get_lsp_tools() -> list[BaseTool]: - """Create instances of all LSP tools. - - Returns: - List of LSP tool instances - """ - return [LSPTool()] - - -def register_lsp_tools(mcp_server: FastMCP) -> list[BaseTool]: - """Register all LSP tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - - Returns: - List of registered tools - """ - tools = get_lsp_tools() - ToolRegistry.register_tools(mcp_server, tools) - return tools - - -# TOOLS list for entry point discovery -TOOLS = [LSPTool] diff --git a/pkg/hanzo-tools-lsp/hanzo_tools/lsp/lsp_tool.py b/pkg/hanzo-tools-lsp/hanzo_tools/lsp/lsp_tool.py deleted file mode 100644 index 0601e7b16..000000000 --- a/pkg/hanzo-tools-lsp/hanzo_tools/lsp/lsp_tool.py +++ /dev/null @@ -1,1305 +0,0 @@ -"""Language Server Protocol (LSP) tool for code intelligence. - -This tool provides on-demand LSP configuration and installation for various -programming languages. It automatically installs language servers as needed -and provides code intelligence features like go-to-definition, find references, -rename symbol, and diagnostics. -""" - -import os -import json -import uuid -import atexit -import shutil -import asyncio -import logging -import tempfile -from typing import Any, Dict, List, Optional -from pathlib import Path -from dataclasses import field, dataclass -from urllib.parse import unquote, urlparse - -from hanzo_tools.core import BaseTool, MCPResourceDocument - -# LSP server configurations -LSP_SERVERS = { - "go": { - "name": "gopls", - "install_cmd": ["go", "install", "golang.org/x/tools/gopls@latest"], - "check_cmd": ["gopls", "version"], - "start_cmd": ["gopls", "serve", "-mode=stdio"], - "root_markers": ["go.work", "go.mod", "go.sum"], - "file_extensions": [".go"], - "capabilities": [ - "definition", - "references", - "rename", - "diagnostics", - "hover", - "completion", - ], - "env": {"GOWORK": "auto"}, - }, - "python": { - "name": "pyright", - "install_cmd": ["npm", "install", "-g", "pyright"], - "check_cmd": ["pyright-langserver", "--version"], - "start_cmd": ["pyright-langserver", "--stdio"], - "root_markers": [ - "pyproject.toml", - "setup.py", - "requirements.txt", - "pyrightconfig.json", - ], - "file_extensions": [".py", ".pyi"], - "capabilities": [ - "definition", - "references", - "rename", - "diagnostics", - "hover", - "completion", - "typeDefinition", - ], - }, - "typescript": { - "name": "typescript-language-server", - "install_cmd": [ - "npm", - "install", - "-g", - "typescript", - "typescript-language-server", - ], - "check_cmd": ["typescript-language-server", "--version"], - "start_cmd": ["typescript-language-server", "--stdio"], - "root_markers": ["tsconfig.json", "package.json"], - "file_extensions": [".ts", ".tsx", ".js", ".jsx"], - "capabilities": [ - "definition", - "references", - "rename", - "diagnostics", - "hover", - "completion", - ], - }, - "rust": { - "name": "rust-analyzer", - "install_cmd": ["rustup", "component", "add", "rust-analyzer"], - "check_cmd": ["rust-analyzer", "--version"], - "start_cmd": ["rust-analyzer"], - "root_markers": ["Cargo.toml"], - "file_extensions": [".rs"], - "capabilities": [ - "definition", - "references", - "rename", - "diagnostics", - "hover", - "completion", - "inlay_hints", - ], - }, - "java": { - "name": "jdtls", - "install_cmd": ["brew", "install", "jdtls"], - "check_cmd": ["jdtls", "--version"], - "start_cmd": ["jdtls"], - "root_markers": ["pom.xml", "build.gradle", "build.gradle.kts"], - "file_extensions": [".java"], - "capabilities": [ - "definition", - "references", - "rename", - "diagnostics", - "hover", - "completion", - ], - }, - "cpp": { - "name": "clangd", - "install_cmd": ["brew", "install", "llvm"], - "check_cmd": ["clangd", "--version"], - "start_cmd": ["clangd"], - "root_markers": ["compile_commands.json", "CMakeLists.txt"], - "file_extensions": [".cpp", ".cc", ".cxx", ".c", ".h", ".hpp"], - "capabilities": [ - "definition", - "references", - "rename", - "diagnostics", - "hover", - "completion", - ], - }, - "ruby": { - "name": "solargraph", - "install_cmd": ["gem", "install", "solargraph"], - "check_cmd": ["solargraph", "--version"], - "start_cmd": ["solargraph", "stdio"], - "root_markers": ["Gemfile", ".solargraph.yml"], - "file_extensions": [".rb"], - "capabilities": [ - "definition", - "references", - "diagnostics", - "hover", - "completion", - ], - }, - "lua": { - "name": "lua-language-server", - "install_cmd": ["brew", "install", "lua-language-server"], - "check_cmd": ["lua-language-server", "--version"], - "start_cmd": ["lua-language-server"], - "root_markers": [".luarc.json"], - "file_extensions": [".lua"], - "capabilities": [ - "definition", - "references", - "rename", - "diagnostics", - "hover", - "completion", - ], - }, -} - - -# Global LSP server registry - singleton per language:root_uri -_GLOBAL_SERVERS: Dict[str, "LSPServer"] = {} -_GLOBAL_LOCK = asyncio.Lock() -_CLEANUP_REGISTERED = False - -logger = logging.getLogger(__name__) - - -def _cleanup_all_servers(): - """Cleanup all LSP servers on process exit.""" - for server in _GLOBAL_SERVERS.values(): - if server.process and server.process.returncode is None: - try: - server.process.terminate() - except Exception: - pass - _GLOBAL_SERVERS.clear() - - -@dataclass -class LSPServer: - """Represents an LSP server instance.""" - - language: str - process: Optional[asyncio.subprocess.Process] - config: Dict[str, Any] - root_uri: str - initialized: bool = False - request_id: int = field(default=0) - pending_responses: Dict[int, asyncio.Future] = field(default_factory=dict) - lock: asyncio.Lock = field(default_factory=asyncio.Lock) - - def next_id(self) -> int: - """Get next request ID.""" - self.request_id += 1 - return self.request_id - - -class LSPTool(BaseTool): - """Language Server Protocol tool for code intelligence.""" - - name = "lsp" - - @property - def description(self) -> str: - return """Language Server Protocol tool for code intelligence. - - Actions: - - definition: Go to definition of symbol at position - - references: Find all references to symbol - - rename: Rename symbol across codebase - - diagnostics: Get errors and warnings for file - - hover: Get hover information at position - - completion: Get code completions at position - - code_action: Run LSP code actions - - organize_imports: Organize imports for a file - - status: Check LSP server status - - The tool automatically installs language servers as needed. - Supported languages: Go, Python, TypeScript/JavaScript, Rust, Java, C/C++, Ruby, Lua - """ - - def __init__(self): - super().__init__() - global _CLEANUP_REGISTERED - if not _CLEANUP_REGISTERED: - atexit.register(_cleanup_all_servers) - _CLEANUP_REGISTERED = True - - def _get_language_from_file(self, file_path: str) -> Optional[str]: - """Detect language from file extension.""" - ext = Path(file_path).suffix.lower() - for lang, config in LSP_SERVERS.items(): - if ext in config["file_extensions"]: - return lang - return None - - def _find_project_root(self, file_path: str, language: str) -> str: - """Find project root based on language markers.""" - path = Path(file_path).resolve() - if language == "go": - try: - return str(self._find_go_workspace_root(path)) - except FileNotFoundError: - return str(path.parent) - markers = LSP_SERVERS[language]["root_markers"] - for parent in path.parents: - for marker in markers: - if (parent / marker).is_file(): - return str(parent) - return str(path.parent) - - async def _check_lsp_installed(self, language: str) -> bool: - """Check if LSP server is installed.""" - config = LSP_SERVERS.get(language) - if not config: - return False - try: - result = await asyncio.create_subprocess_exec( - *config["check_cmd"], - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - await result.communicate() - return result.returncode == 0 - except (FileNotFoundError, OSError): - return False - - async def _install_lsp(self, language: str) -> bool: - """Install LSP server for language.""" - config = LSP_SERVERS.get(language) - if not config: - return False - logger.info(f"Installing {config['name']} for {language}") - try: - installer = config["install_cmd"][0] - if not shutil.which(installer): - logger.error(f"Installer {installer} not found") - return False - result = await asyncio.create_subprocess_exec( - *config["install_cmd"], - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await result.communicate() - if result.returncode != 0: - logger.error(f"Installation failed: {stderr.decode()}") - return False - logger.info(f"Successfully installed {config['name']}") - return True - except Exception as e: - logger.error(f"Installation error: {e}") - return False - - async def _ensure_lsp_running( - self, language: str, root_uri: str - ) -> Optional[LSPServer]: - """Ensure LSP server is running for language.""" - server_key = f"{language}:{root_uri}" - if server_key in _GLOBAL_SERVERS: - server = _GLOBAL_SERVERS[server_key] - if server.process and server.process.returncode is None: - return server - - async with _GLOBAL_LOCK: - if server_key in _GLOBAL_SERVERS: - server = _GLOBAL_SERVERS[server_key] - if server.process and server.process.returncode is None: - return server - del _GLOBAL_SERVERS[server_key] - - if not await self._check_lsp_installed(language): - if not await self._install_lsp(language): - return None - - config = LSP_SERVERS[language] - try: - env = os.environ.copy() - env.update(config.get("env", {})) - process = await asyncio.create_subprocess_exec( - *config["start_cmd"], - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - cwd=root_uri, - env=env, - ) - server = LSPServer( - language=language, process=process, config=config, root_uri=root_uri - ) - if not await self._initialize_lsp(server): - if server.process: - server.process.terminate() - return None - _GLOBAL_SERVERS[server_key] = server - logger.info( - f"Started global LSP server: {config['name']} for {root_uri}" - ) - return server - except Exception as e: - logger.error(f"Failed to start LSP: {e}") - return None - - async def _initialize_lsp(self, server: LSPServer) -> bool: - """Send initialize request to LSP server.""" - root_uri = Path(server.root_uri).resolve().as_uri() - init_params = { - "processId": os.getpid(), - "rootUri": root_uri, - "rootPath": server.root_uri, - "capabilities": { - "workspace": {"workspaceFolders": True, "applyEdit": True}, - "textDocument": { - "synchronization": {"dynamicRegistration": True, "didSave": True}, - "completion": {"completionItem": {"snippetSupport": True}}, - "hover": {"contentFormat": ["markdown", "plaintext"]}, - "definition": {"dynamicRegistration": True, "linkSupport": True}, - "references": {"dynamicRegistration": True}, - "rename": {"dynamicRegistration": True, "prepareSupport": True}, - }, - }, - "workspaceFolders": [{"uri": root_uri, "name": Path(server.root_uri).name}], - } - request = { - "jsonrpc": "2.0", - "id": server.next_id(), - "method": "initialize", - "params": init_params, - } - response = await self._send_request(server, request, timeout=60.0) - if not response or "error" in response: - logger.error(f"Failed to initialize LSP: {response}") - return False - await self._send_notification(server, "initialized", {}) - server.initialized = True - return True - - async def _read_lsp_message( - self, server: LSPServer, timeout: float = 30.0 - ) -> Optional[Dict[str, Any]]: - """Read a single LSP message from server stdout.""" - if not server.process or not server.process.stdout: - return None - try: - headers = {} - while True: - line = await asyncio.wait_for( - server.process.stdout.readline(), timeout=timeout - ) - if not line: - return None - line_str = line.decode("utf-8").strip() - if not line_str: - break - if ":" in line_str: - key, value = line_str.split(":", 1) - headers[key.strip().lower()] = value.strip() - content_length = int(headers.get("content-length", 0)) - if content_length == 0: - return None - content = await asyncio.wait_for( - server.process.stdout.read(content_length), timeout=timeout - ) - return json.loads(content.decode("utf-8")) - except asyncio.TimeoutError: - return None - except Exception as e: - logger.error(f"Error reading LSP message: {e}") - return None - - async def _send_request( - self, server: LSPServer, request: Dict[str, Any], timeout: float = 30.0 - ) -> Optional[Dict[str, Any]]: - """Send JSON-RPC request to LSP server and wait for response.""" - if ( - not server.process - or server.process.returncode is not None - or not server.process.stdin - ): - return None - import time - - async with server.lock: - try: - request_str = json.dumps(request) - content = request_str.encode("utf-8") - header = f"Content-Length: {len(content)}\r\n\r\n" - server.process.stdin.write(header.encode("utf-8") + content) - await server.process.stdin.drain() - request_id = request.get("id") - start_time = time.monotonic() - deadline = start_time + timeout - while time.monotonic() < deadline: - remaining = max(0.1, deadline - time.monotonic()) - response = await self._read_lsp_message(server, timeout=remaining) - if response is None: - if server.process.returncode is not None: - return None - continue - if "id" in response and response["id"] == request_id: - return response - return None - except Exception as e: - logger.error(f"LSP communication error: {e}") - return None - - async def _send_notification( - self, server: LSPServer, method: str, params: Dict[str, Any] - ) -> bool: - """Send JSON-RPC notification (no response expected).""" - if ( - not server.process - or server.process.returncode is not None - or not server.process.stdin - ): - return False - try: - notification = {"jsonrpc": "2.0", "method": method, "params": params} - content = json.dumps(notification).encode("utf-8") - header = f"Content-Length: {len(content)}\r\n\r\n" - server.process.stdin.write(header.encode("utf-8") + content) - await server.process.stdin.drain() - return True - except Exception as e: - logger.error(f"Failed to send notification: {e}") - return False - - async def run( - self, - action: str, - file: str, - line: Optional[int] = None, - character: Optional[int] = None, - new_name: Optional[str] = None, - apply_edits: bool = False, - **kwargs, - ) -> MCPResourceDocument: - """Execute LSP action.""" - valid_actions = [ - "definition", - "references", - "rename", - "diagnostics", - "hover", - "completion", - "code_action", - "organize_imports", - "status", - ] - if action not in valid_actions: - return MCPResourceDocument( - data={ - "error": f"Invalid action. Must be one of: {', '.join(valid_actions)}" - } - ) - - language = self._get_language_from_file(file) - if not language: - return MCPResourceDocument( - data={ - "error": f"Unsupported file type: {file}", - "supported_languages": list(LSP_SERVERS.keys()), - } - ) - - capabilities = LSP_SERVERS[language]["capabilities"] - if action not in capabilities and action not in [ - "status", - "organize_imports", - "code_action", - ]: - return MCPResourceDocument( - data={ - "error": f"Action '{action}' not supported for {language}", - "supported_actions": capabilities, - } - ) - - if action == "status": - installed = await self._check_lsp_installed(language) - return MCPResourceDocument( - data={ - "language": language, - "lsp_server": LSP_SERVERS[language]["name"], - "installed": installed, - "capabilities": capabilities, - } - ) - - root_uri = self._find_project_root(file, language) - server = await self._ensure_lsp_running(language, root_uri) - if not server: - return MCPResourceDocument( - data={ - "error": f"Failed to start LSP server for {language}", - "install_command": " ".join(LSP_SERVERS[language]["install_cmd"]), - } - ) - - result = await self._execute_lsp_action( - server, - action, - file, - line, - character, - new_name, - apply_edits, - range_spec=kwargs.get("range"), - only=kwargs.get("only"), - ) - return MCPResourceDocument(data=result) - - async def call(self, **kwargs) -> str: - """Tool interface for MCP - converts result to JSON string.""" - result = await self.run(**kwargs) - return result.to_json_string() - - def register(self, mcp_server) -> None: - """Register tool with MCP server.""" - - @mcp_server.tool(name=self.name, description=self.description) - async def lsp_handler( - action: str, - file: str, - line: Optional[int] = None, - character: Optional[int] = None, - new_name: Optional[str] = None, - apply_edits: bool = False, - only: Optional[List[str]] = None, - range: Optional[Dict[str, Dict[str, int]]] = None, - ) -> str: - return await self.call( - action=action, - file=file, - line=line, - character=character, - new_name=new_name, - apply_edits=apply_edits, - only=only, - range=range, - ) - - def _path_to_uri(self, path: str) -> str: - return Path(path).resolve().as_uri() - - def _uri_to_path(self, uri: str) -> str: - if uri.startswith("file://"): - parsed = urlparse(uri) - path = unquote(parsed.path) - if ( - path.startswith("/") - and len(path) >= 3 - and path[2] == ":" - and path[1].isalpha() - ): - path = path[1:] - return path - return uri - - def _find_go_workspace_root(self, start: Path) -> Path: - p = start.resolve() - if p.is_file(): - p = p.parent - for d in (p, *p.parents): - work = d / "go.work" - if work.is_file(): - return d - for d in (p, *p.parents): - mod = d / "go.mod" - if mod.is_file(): - return d - raise FileNotFoundError(f"no go.work or go.mod found above {start}") - - def _is_within_root(self, path: str, root_dir: str) -> bool: - try: - Path(path).resolve().relative_to(Path(root_dir).resolve()) - return True - except Exception: - return False - - async def _open_document(self, server: LSPServer, file_path: str) -> bool: - """Notify LSP server that a document is opened.""" - abs_path = str(Path(file_path).resolve()) - try: - with open(abs_path, "r", encoding="utf-8") as f: - content = f.read() - except Exception as e: - logger.error(f"Failed to read file {file_path}: {e}") - return False - language_id = server.language - if server.language == "typescript": - ext = Path(file_path).suffix.lower() - if ext in [".tsx", ".jsx"]: - language_id = "typescriptreact" if "ts" in ext else "javascriptreact" - elif ext in [".js", ".mjs", ".cjs"]: - language_id = "javascript" - params = { - "textDocument": { - "uri": self._path_to_uri(abs_path), - "languageId": language_id, - "version": 1, - "text": content, - } - } - return await self._send_notification(server, "textDocument/didOpen", params) - - def _parse_location(self, location: Dict[str, Any]) -> Dict[str, Any]: - """Parse LSP Location into a readable format.""" - uri = location.get("uri", "") - file_path = self._uri_to_path(uri) - range_info = location.get("range", {}) - start = range_info.get("start", {}) - end = range_info.get("end", {}) - return { - "file": file_path, - "start": { - "line": start.get("line", 0) + 1, - "character": start.get("character", 0), - }, - "end": { - "line": end.get("line", 0) + 1, - "character": end.get("character", 0), - }, - } - - def _utf16_index_to_py_index(self, text: str, utf16_index: int) -> int: - if utf16_index <= 0: - return 0 - units = 0 - for idx, ch in enumerate(text): - units += 2 if ord(ch) > 0xFFFF else 1 - if units >= utf16_index: - return idx + 1 - return len(text) - - def _utf16_len(self, text: str) -> int: - units = 0 - for ch in text: - units += 2 if ord(ch) > 0xFFFF else 1 - return units - - def _lsp_position_to_offset( - self, lines: List[str], line: int, character: int - ) -> int: - if line < 0: - return 0 - if line >= len(lines): - return sum(len(l) for l in lines) - offset = sum(len(l) for l in lines[:line]) - line_text = lines[line] - return offset + self._utf16_index_to_py_index(line_text, character) - - def _file_range_for_code_action(self, file_path: str) -> Dict[str, Dict[str, int]]: - try: - with open(file_path, "r", encoding="utf-8") as f: - content = f.read() - except Exception: - return { - "start": {"line": 0, "character": 0}, - "end": {"line": 0, "character": 0}, - } - if not content: - return { - "start": {"line": 0, "character": 0}, - "end": {"line": 0, "character": 0}, - } - lines = content.splitlines() - last_line_index = len(lines) - 1 - end_char = self._utf16_len(lines[last_line_index]) - return { - "start": {"line": 0, "character": 0}, - "end": {"line": last_line_index, "character": end_char}, - } - - def _apply_text_edits(self, file_path: str, edits: List[Dict[str, Any]]) -> None: - with open(file_path, "r", encoding="utf-8") as f: - content = f.read() - content = self._render_text_edits(content, edits) - with open(file_path, "w", encoding="utf-8") as f: - f.write(content) - - def _render_text_edits(self, content: str, edits: List[Dict[str, Any]]) -> str: - lines = content.splitlines(keepends=True) - normalized = [] - for edit in edits: - range_info = edit.get("range", {}) - start = range_info.get("start", {}) - end = range_info.get("end", {}) - start_offset = self._lsp_position_to_offset( - lines, start.get("line", 0), start.get("character", 0) - ) - end_offset = self._lsp_position_to_offset( - lines, end.get("line", 0), end.get("character", 0) - ) - normalized.append( - ( - start.get("line", 0), - start.get("character", 0), - start_offset, - end_offset, - edit.get("newText", ""), - ) - ) - normalized.sort(key=lambda item: (item[0], item[1]), reverse=True) - for _, _, start_offset, end_offset, new_text in normalized: - content = content[:start_offset] + new_text + content[end_offset:] - return content - - def _workspace_edit_files(self, edit: Dict[str, Any]) -> List[str]: - files: List[str] = [] - if "documentChanges" in edit: - for change in edit["documentChanges"]: - if "kind" in change: - if change.get("kind") == "rename": - files.append(self._uri_to_path(change.get("oldUri", ""))) - files.append(self._uri_to_path(change.get("newUri", ""))) - elif change.get("kind") in ["create", "delete"]: - files.append(self._uri_to_path(change.get("uri", ""))) - elif "textDocument" in change: - files.append(self._uri_to_path(change["textDocument"]["uri"])) - elif "changes" in edit: - for uri in edit["changes"].keys(): - files.append(self._uri_to_path(uri)) - return sorted({f for f in files if f}) - - def _apply_workspace_edit( - self, edit: Dict[str, Any], root_dir: str - ) -> tuple[list[str], list[str]]: - applied: list[str] = [] - errors: list[str] = [] - root_dir = str(Path(root_dir).resolve()) - - changes = edit.get("documentChanges") - if changes is None: - changes = [ - {"textDocument": {"uri": uri}, "edits": edits} - for uri, edits in edit.get("changes", {}).items() - ] - - if not changes: - return applied, errors - - backup_dir = Path(tempfile.mkdtemp(prefix="hanzo-lsp-", dir=root_dir)) - backups: Dict[str, str] = {} - mtimes: Dict[str, float] = {} - created: set[str] = set() - - def backup_path(path: str) -> str: - rel = os.path.relpath(path, root_dir) - dst = backup_dir / rel - dst.parent.mkdir(parents=True, exist_ok=True) - return str(dst) - - try: - # Preflight: collect backups - for change in changes: - if "kind" in change: - kind = change.get("kind") - if kind == "rename": - old_path = self._uri_to_path(change.get("oldUri", "")) - new_path = self._uri_to_path(change.get("newUri", "")) - if not self._is_within_root( - old_path, root_dir - ) or not self._is_within_root(new_path, root_dir): - raise RuntimeError( - f"rename outside workspace root: {old_path} -> {new_path}" - ) - if os.path.exists(old_path) and old_path not in backups: - backups[old_path] = backup_path(old_path) - shutil.copy2(old_path, backups[old_path]) - mtimes[old_path] = os.path.getmtime(old_path) - if os.path.exists(new_path) and new_path not in backups: - backups[new_path] = backup_path(new_path) - shutil.copy2(new_path, backups[new_path]) - mtimes[new_path] = os.path.getmtime(new_path) - elif kind == "create": - file_path = self._uri_to_path(change.get("uri", "")) - if not self._is_within_root(file_path, root_dir): - raise RuntimeError( - f"create outside workspace root: {file_path}" - ) - if os.path.exists(file_path) and file_path not in backups: - backups[file_path] = backup_path(file_path) - shutil.copy2(file_path, backups[file_path]) - mtimes[file_path] = os.path.getmtime(file_path) - elif kind == "delete": - file_path = self._uri_to_path(change.get("uri", "")) - if not self._is_within_root(file_path, root_dir): - raise RuntimeError( - f"delete outside workspace root: {file_path}" - ) - if os.path.exists(file_path) and file_path not in backups: - backups[file_path] = backup_path(file_path) - shutil.copy2(file_path, backups[file_path]) - mtimes[file_path] = os.path.getmtime(file_path) - elif "textDocument" in change and "edits" in change: - file_path = self._uri_to_path(change["textDocument"]["uri"]) - if not self._is_within_root(file_path, root_dir): - raise RuntimeError(f"edit outside workspace root: {file_path}") - if os.path.exists(file_path) and file_path not in backups: - backups[file_path] = backup_path(file_path) - shutil.copy2(file_path, backups[file_path]) - mtimes[file_path] = os.path.getmtime(file_path) - - # Apply changes in order - for change in changes: - if "kind" in change: - kind = change.get("kind") - if kind == "rename": - old_path = self._uri_to_path(change.get("oldUri", "")) - new_path = self._uri_to_path(change.get("newUri", "")) - options = change.get("options", {}) - if os.path.exists(new_path): - if options.get("ignoreIfExists"): - continue - if not options.get("overwrite"): - raise RuntimeError(f"rename target exists: {new_path}") - Path(new_path).parent.mkdir(parents=True, exist_ok=True) - shutil.move(old_path, new_path) - applied.append(new_path) - continue - if kind == "create": - file_path = self._uri_to_path(change.get("uri", "")) - options = change.get("options", {}) - if os.path.exists(file_path): - if options.get("ignoreIfExists"): - continue - if not options.get("overwrite"): - raise RuntimeError(f"create target exists: {file_path}") - Path(file_path).parent.mkdir(parents=True, exist_ok=True) - with open(file_path, "w", encoding="utf-8") as f: - f.write(change.get("content", "")) - created.add(file_path) - applied.append(file_path) - continue - if kind == "delete": - file_path = self._uri_to_path(change.get("uri", "")) - options = change.get("options", {}) - if not os.path.exists(file_path): - if options.get("ignoreIfNotExists"): - continue - raise RuntimeError(f"delete target missing: {file_path}") - if os.path.isdir(file_path): - if not options.get("recursive"): - raise RuntimeError( - f"delete target is directory: {file_path}" - ) - shutil.rmtree(file_path) - else: - os.unlink(file_path) - applied.append(file_path) - continue - raise RuntimeError(f"unsupported documentChange: {kind}") - - if "textDocument" in change and "edits" in change: - file_path = self._uri_to_path(change["textDocument"]["uri"]) - if file_path in mtimes and os.path.exists(file_path): - if os.path.getmtime(file_path) != mtimes[file_path]: - raise RuntimeError(f"conflict detected for {file_path}") - with open(file_path, "r", encoding="utf-8") as f: - content = f.read() - updated = self._render_text_edits(content, change["edits"]) - temp_path = f"{file_path}.hanzo_tmp_{uuid.uuid4().hex}" - with open(temp_path, "w", encoding="utf-8") as f: - f.write(updated) - os.replace(temp_path, file_path) - applied.append(file_path) - else: - raise RuntimeError( - f"unsupported documentChange: {change.get('kind', 'unknown')}" - ) - - except Exception as exc: - errors.append(str(exc)) - # Rollback - for path, backup in backups.items(): - try: - Path(path).parent.mkdir(parents=True, exist_ok=True) - shutil.copy2(backup, path) - except Exception: - pass - for path in created: - if path not in backups: - try: - if os.path.isdir(path): - shutil.rmtree(path) - else: - os.unlink(path) - except Exception: - pass - applied = [] - finally: - shutil.rmtree(backup_dir, ignore_errors=True) - - return applied, errors - - async def _execute_lsp_action( - self, - server: LSPServer, - action: str, - file: str, - line: Optional[int], - character: Optional[int], - new_name: Optional[str], - apply_edits: bool, - range_spec: Optional[Dict[str, Any]] = None, - only: Optional[List[str]] = None, - ) -> Dict[str, Any]: - """Execute specific LSP action.""" - abs_path = str(Path(file).resolve()) - uri = self._path_to_uri(abs_path) - await self._open_document(server, file) - position = { - "line": (line - 1) if line else 0, - "character": character if character else 0, - } - - if action == "definition": - request = { - "jsonrpc": "2.0", - "id": server.next_id(), - "method": "textDocument/definition", - "params": {"textDocument": {"uri": uri}, "position": position}, - } - response = await self._send_request(server, request) - if response and "result" in response: - result = response["result"] - if result is None: - return { - "action": "definition", - "file": file, - "result": None, - "message": "No definition found", - } - if isinstance(result, list): - return { - "action": "definition", - "file": file, - "definitions": [self._parse_location(loc) for loc in result], - } - elif isinstance(result, dict): - return { - "action": "definition", - "file": file, - "definition": self._parse_location(result), - } - return { - "action": "definition", - "file": file, - "error": response.get("error") if response else "No response", - } - - elif action == "references": - request = { - "jsonrpc": "2.0", - "id": server.next_id(), - "method": "textDocument/references", - "params": { - "textDocument": {"uri": uri}, - "position": position, - "context": {"includeDeclaration": True}, - }, - } - response = await self._send_request(server, request) - if response and "result" in response: - result = response["result"] - refs = ( - [self._parse_location(loc) for loc in result] - if isinstance(result, list) - else [] - ) - return { - "action": "references", - "file": file, - "references": refs, - "count": len(refs), - } - return { - "action": "references", - "file": file, - "error": response.get("error") if response else "No response", - } - - elif action == "rename": - if not new_name: - return { - "action": "rename", - "error": "new_name is required for rename action", - } - request = { - "jsonrpc": "2.0", - "id": server.next_id(), - "method": "textDocument/rename", - "params": { - "textDocument": {"uri": uri}, - "position": position, - "newName": new_name, - }, - } - response = await self._send_request(server, request) - if response and "result" in response: - result = response["result"] - if result is None: - return { - "action": "rename", - "file": file, - "error": "Rename not possible at this location", - } - changes = {} - if "changes" in result: - for file_uri, edits in result["changes"].items(): - file_path = self._uri_to_path(file_uri) - changes[file_path] = [ - { - "range": self._parse_location( - {"uri": file_uri, "range": edit["range"]} - ), - "newText": edit["newText"], - } - for edit in edits - ] - applied = [] - apply_errors: List[str] = [] - touched_files = self._workspace_edit_files(result) - if apply_edits: - applied, apply_errors = self._apply_workspace_edit( - result, server.root_uri - ) - payload: Dict[str, Any] = { - "action": "rename", - "file": file, - "new_name": new_name, - "changes": changes, - "files_affected": len(changes), - "touched_files": touched_files, - } - if apply_edits: - payload["applied_files"] = applied - if apply_errors: - payload["apply_errors"] = apply_errors - return payload - return { - "action": "rename", - "file": file, - "error": response.get("error") if response else "No response", - } - - elif action == "hover": - request = { - "jsonrpc": "2.0", - "id": server.next_id(), - "method": "textDocument/hover", - "params": {"textDocument": {"uri": uri}, "position": position}, - } - response = await self._send_request(server, request) - if response and "result" in response: - result = response["result"] - if result is None: - return { - "action": "hover", - "file": file, - "result": None, - "message": "No hover info", - } - contents = result.get("contents", "") - if isinstance(contents, dict): - hover_text = contents.get("value", str(contents)) - elif isinstance(contents, list): - hover_text = "\n".join( - c.get("value", str(c)) if isinstance(c, dict) else str(c) - for c in contents - ) - else: - hover_text = str(contents) - return { - "action": "hover", - "file": file, - "position": {"line": line, "character": character}, - "contents": hover_text, - } - return { - "action": "hover", - "file": file, - "error": response.get("error") if response else "No response", - } - - elif action == "completion": - request = { - "jsonrpc": "2.0", - "id": server.next_id(), - "method": "textDocument/completion", - "params": {"textDocument": {"uri": uri}, "position": position}, - } - response = await self._send_request(server, request, timeout=10.0) - if response and "result" in response: - result = response["result"] - if result is None: - return { - "action": "completion", - "file": file, - "completions": [], - "count": 0, - } - items = result if isinstance(result, list) else result.get("items", []) - completions = [ - { - "label": item.get("label", ""), - "kind": item.get("kind", 0), - "detail": item.get("detail", ""), - } - for item in items[:50] - ] - return { - "action": "completion", - "file": file, - "position": {"line": line, "character": character}, - "completions": completions, - "count": len(completions), - } - return { - "action": "completion", - "file": file, - "error": response.get("error") if response else "No response", - } - - elif action == "code_action": - action_range = range_spec or self._file_range_for_code_action(abs_path) - context: Dict[str, Any] = {} - if only: - context["only"] = only - request = { - "jsonrpc": "2.0", - "id": server.next_id(), - "method": "textDocument/codeAction", - "params": { - "textDocument": {"uri": uri}, - "range": action_range, - "context": context, - }, - } - response = await self._send_request(server, request) - if response and "result" in response: - actions = response["result"] or [] - edits_applied: list[str] = [] - apply_errors: list[str] = [] - touched_files: list[str] = [] - commands: list[Dict[str, Any]] = [] - edits_seen = 0 - for action_item in actions: - if "edit" in action_item: - edits_seen += 1 - touched_files.extend( - self._workspace_edit_files(action_item["edit"]) - ) - if apply_edits: - applied, errors = self._apply_workspace_edit( - action_item["edit"], server.root_uri - ) - edits_applied.extend(applied) - apply_errors.extend(errors) - if "command" in action_item: - commands.append(action_item["command"]) - payload: Dict[str, Any] = { - "action": "code_action", - "file": file, - "edits_found": edits_seen, - "touched_files": sorted(set(touched_files)), - "commands": commands, - } - if apply_edits: - payload["applied_files"] = edits_applied - if apply_errors: - payload["apply_errors"] = apply_errors - return payload - return { - "action": "code_action", - "file": file, - "error": response.get("error") if response else "No response", - } - - elif action == "organize_imports": - file_range = self._file_range_for_code_action(abs_path) - request = { - "jsonrpc": "2.0", - "id": server.next_id(), - "method": "textDocument/codeAction", - "params": { - "textDocument": {"uri": uri}, - "range": file_range, - "context": {"only": ["source.organizeImports"]}, - }, - } - response = await self._send_request(server, request) - if response and "result" in response: - actions = response["result"] or [] - edits_applied: list[str] = [] - apply_errors: list[str] = [] - edits_seen = 0 - touched_files: list[str] = [] - for action_item in actions: - kind = action_item.get("kind") - if kind and kind != "source.organizeImports": - continue - if "edit" in action_item: - edits_seen += 1 - touched_files.extend( - self._workspace_edit_files(action_item["edit"]) - ) - if apply_edits: - applied, errors = self._apply_workspace_edit( - action_item["edit"], server.root_uri - ) - edits_applied.extend(applied) - apply_errors.extend(errors) - payload: Dict[str, Any] = { - "action": "organize_imports", - "file": file, - "edits_found": edits_seen, - "touched_files": sorted(set(touched_files)), - } - if apply_edits: - payload["applied_files"] = edits_applied - if apply_errors: - payload["apply_errors"] = apply_errors - return payload - return { - "action": "organize_imports", - "file": file, - "error": response.get("error") if response else "No response", - } - - elif action == "diagnostics": - return { - "action": "diagnostics", - "file": file, - "note": "Diagnostics are push-based; use language-specific tools (go vet, pylint, etc.) for on-demand checking", - } - - return {"error": f"Unknown action: {action}"} - - async def cleanup(self): - """Clean up LSP servers.""" - async with _GLOBAL_LOCK: - for server in list(_GLOBAL_SERVERS.values()): - if server.process and server.process.returncode is None: - try: - server.process.terminate() - await asyncio.wait_for(server.process.wait(), timeout=5.0) - except Exception: - pass - _GLOBAL_SERVERS.clear() - - -def create_lsp_tool(): - """Factory function to create LSP tool.""" - return LSPTool() diff --git a/pkg/hanzo-tools-lsp/pyproject.toml b/pkg/hanzo-tools-lsp/pyproject.toml deleted file mode 100644 index 0b7498a4e..000000000 --- a/pkg/hanzo-tools-lsp/pyproject.toml +++ /dev/null @@ -1,25 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-lsp" -version = "0.2.0" -description = "Language Server Protocol tools for code intelligence" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "tools", "lsp", "language-server", "mcp", "ai"] -dependencies = [ - "hanzo-tools>=0.3.0", - "mcp>=1.25.0", - "pydantic>=2.12.5", -] - -[project.entry-points."hanzo.tools"] -lsp = "hanzo_tools.lsp:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] diff --git a/pkg/hanzo-tools-lsp/tests/test_lsp_tools.py b/pkg/hanzo-tools-lsp/tests/test_lsp_tools.py deleted file mode 100644 index 7cd946eb9..000000000 --- a/pkg/hanzo-tools-lsp/tests/test_lsp_tools.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Tests for hanzo-tools-lsp.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import lsp - - assert lsp is not None - - def test_import_tools(self): - from hanzo_tools.lsp import TOOLS - - assert len(TOOLS) > 0 - - def test_import_lsp_tool(self): - from hanzo_tools.lsp import LspTool - - assert LspTool.name == "lsp" - - -class TestLspTool: - """Tests for LspTool.""" - - @pytest.fixture - def tool(self): - from hanzo_tools.lsp import LspTool - - return LspTool() - - def test_has_description(self, tool): - assert tool.description diff --git a/pkg/hanzo-tools-mcp/README.md b/pkg/hanzo-tools-mcp/README.md deleted file mode 100644 index 13769be66..000000000 --- a/pkg/hanzo-tools-mcp/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# hanzo-tools-mcp - -MCP server management tools for Hanzo AI. - -## Tools - -- `mcp` - Unified MCP server management (list, add, remove, enable, disable, restart) -- `mcp_add` - Add new MCP servers -- `mcp_remove` - Remove MCP servers -- `mcp_stats` - MCP server statistics - -## Installation - -```bash -pip install hanzo-tools-mcp -``` - -## Usage - -```python -from hanzo_tools.mcp_tools import TOOLS, register_tools - -# Register with MCP server -register_tools(mcp_server) -``` - -## Part of hanzo-tools - -This package is part of the modular [hanzo-tools](../hanzo-tools) ecosystem. diff --git a/pkg/hanzo-tools-mcp/hanzo_tools/__init__.py b/pkg/hanzo-tools-mcp/hanzo_tools/__init__.py deleted file mode 100644 index e1b06939a..000000000 --- a/pkg/hanzo-tools-mcp/hanzo_tools/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Hanzo tools namespace package.""" - -import pkgutil - -__path__ = pkgutil.extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/__init__.py b/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/__init__.py deleted file mode 100644 index 21a5d5cd8..000000000 --- a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/__init__.py +++ /dev/null @@ -1,121 +0,0 @@ -"""MCP management tools for Hanzo AI. - -Tools: -- mcp: MCP server management -- mcp_add: Add MCP servers -- mcp_remove: Remove MCP servers -- mcp_stats: MCP statistics -- proxy: Dynamic MCP proxy for external servers (platform, github, cloudflare, etc.) - -Install: - pip install hanzo-tools-mcp -""" - -import logging - -logger = logging.getLogger(__name__) - -_tools = [] - -try: - from .mcp_tool import MCPTool - - _tools.append(MCPTool) -except ImportError as e: - logger.debug(f"MCPTool not available: {e}") - MCPTool = None - -try: - from .mcp_add import McpAddTool - - _tools.append(McpAddTool) -except ImportError as e: - logger.debug(f"McpAddTool not available: {e}") - McpAddTool = None - -try: - from .mcp_remove import McpRemoveTool - - _tools.append(McpRemoveTool) -except ImportError as e: - logger.debug(f"McpRemoveTool not available: {e}") - McpRemoveTool = None - -try: - from .mcp_stats import McpStatsTool - - _tools.append(McpStatsTool) -except ImportError as e: - logger.debug(f"McpStatsTool not available: {e}") - McpStatsTool = None - -try: - from .proxy_tool import ProxyTool - - _tools.append(ProxyTool) -except ImportError as e: - logger.debug(f"ProxyTool not available: {e}") - ProxyTool = None - -# Proxy utilities for programmatic use -try: - from .mcp_proxy import ( - BUILTIN_SERVERS, - ProxiedTool, - MCPServerConfig, - MCPProxyRegistry, - MCPServerConnection, - call_mcp_tool, - enable_mcp_server, - ) -except ImportError as e: - logger.debug(f"MCP proxy utilities not available: {e}") - MCPProxyRegistry = None - MCPServerConfig = None - MCPServerConnection = None - ProxiedTool = None - enable_mcp_server = None - call_mcp_tool = None - BUILTIN_SERVERS = {} - -TOOLS = _tools - -__all__ = [ - "TOOLS", - "MCPTool", - "McpAddTool", - "McpRemoveTool", - "McpStatsTool", - "ProxyTool", - # Proxy utilities - "MCPProxyRegistry", - "MCPServerConfig", - "MCPServerConnection", - "ProxiedTool", - "enable_mcp_server", - "call_mcp_tool", - "BUILTIN_SERVERS", - "register_tools", -] - - -def register_tools(mcp_server, enabled_tools: dict[str, bool] | None = None): - """Register MCP tools with MCP server.""" - from hanzo_tools.core import ToolRegistry - - enabled = enabled_tools or {} - registered = [] - - for tool_class in TOOLS: - if tool_class is None: - continue - tool_name = getattr(tool_class, "name", tool_class.__name__.lower()) - if enabled.get(tool_name, True): - try: - tool = tool_class() - ToolRegistry.register_tool(mcp_server, tool) - registered.append(tool) - except Exception as e: - logger.warning(f"Failed to register {tool_name}: {e}") - - return registered diff --git a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/mcp_add.py b/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/mcp_add.py deleted file mode 100644 index 65c96eee2..000000000 --- a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/mcp_add.py +++ /dev/null @@ -1,265 +0,0 @@ -"""Add MCP servers dynamically.""" - -import json -import shutil -from typing import Any, Dict, Unpack, Optional, Annotated, TypedDict, final, override -from pathlib import Path - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, ToolContext, auto_timeout, create_tool_context - -ServerCommand = Annotated[ - str, - Field( - description="Server command (e.g., 'uvx mcp-server-git', 'npx @modelcontextprotocol/server-filesystem')", - min_length=1, - ), -] - -ServerName = Annotated[ - str, - Field( - description="Unique name for the server", - min_length=1, - ), -] - -Args = Annotated[ - Optional[str], - Field( - description="Additional arguments for the server", - default=None, - ), -] - -Env = Annotated[ - Optional[Dict[str, str]], - Field( - description="Environment variables for the server", - default=None, - ), -] - -AutoStart = Annotated[ - bool, - Field( - description="Automatically start the server after adding", - default=True, - ), -] - - -class McpAddParams(TypedDict, total=False): - """Parameters for MCP add tool.""" - - command: str - name: str - args: Optional[str] - env: Optional[Dict[str, str]] - auto_start: bool - - -@final -class McpAddTool(BaseTool): - """Tool for adding MCP servers dynamically.""" - - # Class variable to store added servers - _mcp_servers: Dict[str, Dict[str, Any]] = {} - _config_file = Path.home() / ".hanzo" / "mcp" / "servers.json" - - def __init__(self): - """Initialize the MCP add tool.""" - # Load existing servers from config - self._load_servers() - - @classmethod - def _load_servers(cls): - """Load servers from config file.""" - if cls._config_file.exists(): - try: - with open(cls._config_file, "r") as f: - cls._mcp_servers = json.load(f) - except Exception: - cls._mcp_servers = {} - - @classmethod - def _save_servers(cls): - """Save servers to config file.""" - cls._config_file.parent.mkdir(parents=True, exist_ok=True) - with open(cls._config_file, "w") as f: - json.dump(cls._mcp_servers, f, indent=2) - - @classmethod - def get_servers(cls) -> Dict[str, Dict[str, Any]]: - """Get all registered MCP servers.""" - return cls._mcp_servers.copy() - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "mcp_add" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Add MCP (Model Context Protocol) servers dynamically. - -This allows adding new MCP servers that provide additional tools. -Servers can be from npm packages or Python packages. - -Common MCP servers: -- @modelcontextprotocol/server-filesystem - File system access -- @modelcontextprotocol/server-github - GitHub integration -- @modelcontextprotocol/server-gitlab - GitLab integration -- @modelcontextprotocol/server-postgres - PostgreSQL access -- @modelcontextprotocol/server-sqlite - SQLite access -- mcp-server-git - Git operations -- mcp-server-docker - Docker management -- mcp-server-kubernetes - K8s management - -Examples: -- mcp_add --command "npx @modelcontextprotocol/server-filesystem" --name filesystem --args "/path/to/allow" -- mcp_add --command "uvx mcp-server-git" --name git --args "--repository /path/to/repo" -- mcp_add --command "npx @modelcontextprotocol/server-github" --name github --env '{"GITHUB_TOKEN": "..."}' - -Use 'mcp_stats' to see all added servers and their status. -""" - - @override - @auto_timeout("mcp_add") - async def call( - self, - ctx: MCPContext, - **params: Unpack[McpAddParams], - ) -> str: - """Add an MCP server. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Result of adding the server - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - command = params.get("command") - if not command: - return "Error: command is required" - - name = params.get("name") - if not name: - return "Error: name is required" - - args = params.get("args") - env = params.get("env", {}) - auto_start = params.get("auto_start", True) - - # Check if server already exists - if name in self._mcp_servers: - return f"Error: Server '{name}' already exists. Use mcp_remove to remove it first." - - # Parse command to determine type - server_type = "unknown" - if command.startswith("npx"): - server_type = "node" - elif command.startswith("uvx") or command.startswith("python"): - server_type = "python" - elif command.startswith("node"): - server_type = "node" - - # Build full command - full_command = [command] - if args: - import shlex - - # If command contains spaces, split it first - if " " in command: - full_command = shlex.split(command) - full_command.extend(shlex.split(args)) - else: - if " " in command: - import shlex - - full_command = shlex.split(command) - - await tool_ctx.info( - f"Adding MCP server '{name}' with command: {' '.join(full_command)}" - ) - - # Create server configuration - server_config = { - "command": full_command, - "name": name, - "type": server_type, - "env": env, - "status": "stopped", - "process_id": None, - "tools": [], - "resources": [], - "prompts": [], - } - - # Test if command is valid - if auto_start: - try: - # Try to start the server briefly to validate - test_env = {**env} if env else {} - - # Quick test to see if command exists - test_cmd = full_command[0] - if test_cmd == "npx": - if not shutil.which("npx"): - return "Error: npx not found. Install Node.js first." - elif test_cmd == "uvx": - if not shutil.which("uvx"): - return "Error: uvx not found. Install uv first." - - # Server is validated and ready to be used - # The actual connection happens when tools are invoked - server_config["status"] = "ready" - - except Exception as e: - await tool_ctx.error(f"Failed to validate server: {str(e)}") - server_config["status"] = "error" - server_config["error"] = str(e) - - # Add server to registry - self._mcp_servers[name] = server_config - self._save_servers() - - output = [ - f"Successfully added MCP server '{name}':", - f" Type: {server_type}", - f" Command: {' '.join(full_command)}", - f" Status: {server_config['status']}", - ] - - if env: - output.append(f" Environment: {list(env.keys())}") - - output.extend( - [ - "", - "Use 'mcp_stats' to see server details.", - f"Use 'mcp_remove --name {name}' to remove this server.", - ] - ) - - # Note: In a real implementation, we would: - # 1. Start the MCP server process - # 2. Connect to it via stdio/HTTP - # 3. Query its capabilities (tools, resources, prompts) - # 4. Register those with our MCP server - - return "\n".join(output) - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/mcp_proxy.py b/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/mcp_proxy.py deleted file mode 100644 index ee1b1b462..000000000 --- a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/mcp_proxy.py +++ /dev/null @@ -1,601 +0,0 @@ -"""Dynamic MCP Proxy for lazy-loading external MCP servers. - -This module provides functionality to: -1. Connect to external MCP servers on-demand -2. Discover their tools dynamically -3. Proxy tool calls through hanzo-mcp -4. Support lazy loading - only start when needed - -Example servers that can be proxied: -- platform-mcp (Hanzo Platform) -- @modelcontextprotocol/server-github (GitHub) -- @modelcontextprotocol/server-cloudflare (Cloudflare) -""" - -import os -import json -import shutil -import asyncio -import logging -import subprocess -from typing import Any, Dict, List, Callable, Optional, Awaitable -from pathlib import Path -from dataclasses import field, dataclass - -logger = logging.getLogger(__name__) - - -@dataclass -class MCPServerConfig: - """Configuration for an external MCP server.""" - - name: str - command: List[str] - env: Dict[str, str] = field(default_factory=dict) - working_dir: Optional[str] = None - description: str = "" - auto_start: bool = False - lazy_load: bool = True - auth_required: bool = False - auth_env_var: Optional[str] = None - auth_url: Optional[str] = None - - -@dataclass -class ProxiedTool: - """A tool proxied from an external MCP server.""" - - name: str - description: str - input_schema: Dict[str, Any] - server_name: str - - -class MCPServerConnection: - """Connection to an external MCP server.""" - - def __init__(self, config: MCPServerConfig): - self.config = config - self.process: Optional[subprocess.Popen] = None - self.tools: List[ProxiedTool] = [] - self.resources: List[Dict[str, Any]] = [] - self.prompts: List[Dict[str, Any]] = [] - self._reader: Optional[asyncio.StreamReader] = None - self._writer: Optional[asyncio.StreamWriter] = None - self._request_id = 0 - self._pending_requests: Dict[int, asyncio.Future] = {} - self._read_task: Optional[asyncio.Task] = None - - async def connect(self) -> bool: - """Connect to the MCP server.""" - if self.process is not None: - return True - - # Check auth if required - if self.config.auth_required and self.config.auth_env_var: - if not os.environ.get(self.config.auth_env_var): - logger.warning( - f"MCP server '{self.config.name}' requires {self.config.auth_env_var} to be set" - ) - return False - - # Prepare environment - env = os.environ.copy() - env.update(self.config.env) - - # Process environment variable references - for key, value in list(env.items()): - if ( - isinstance(value, str) - and value.startswith("${") - and value.endswith("}") - ): - var_name = value[2:-1] - env[key] = os.environ.get(var_name, "") - - try: - # Start the MCP server process - self.process = await asyncio.create_subprocess_exec( - *self.config.command, - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - env=env, - cwd=self.config.working_dir, - ) - - self._reader = self.process.stdout - self._writer = self.process.stdin - - # Start reading responses - self._read_task = asyncio.create_task(self._read_loop()) - - # Initialize the connection - await self._initialize() - - # Discover tools - await self._discover_tools() - - logger.info( - f"Connected to MCP server '{self.config.name}' with {len(self.tools)} tools" - ) - return True - - except Exception as e: - logger.error(f"Failed to connect to MCP server '{self.config.name}': {e}") - await self.disconnect() - return False - - async def disconnect(self): - """Disconnect from the MCP server.""" - if self._read_task: - self._read_task.cancel() - try: - await self._read_task - except asyncio.CancelledError: - pass - self._read_task = None - - if self.process: - self.process.terminate() - try: - await asyncio.wait_for(self.process.wait(), timeout=5.0) - except asyncio.TimeoutError: - self.process.kill() - self.process = None - - self._reader = None - self._writer = None - self.tools = [] - - async def _read_loop(self): - """Read responses from the MCP server.""" - try: - while True: - if not self._reader: - break - - # Read content-length header - header = await self._reader.readline() - if not header: - break - - # Handle JSON-RPC over stdio (newline-delimited) - line = header.decode("utf-8").strip() - if not line: - continue - - try: - response = json.loads(line) - - # Handle response - if "id" in response and response["id"] in self._pending_requests: - future = self._pending_requests.pop(response["id"]) - if "error" in response: - future.set_exception(Exception(response["error"])) - else: - future.set_result(response.get("result")) - - except json.JSONDecodeError: - continue - - except asyncio.CancelledError: - pass - except Exception as e: - logger.error(f"Error reading from MCP server: {e}") - - async def _send_request(self, method: str, params: Optional[Dict] = None) -> Any: - """Send a JSON-RPC request to the MCP server.""" - if not self._writer: - raise ConnectionError("Not connected to MCP server") - - self._request_id += 1 - request_id = self._request_id - - request = { - "jsonrpc": "2.0", - "id": request_id, - "method": method, - } - if params: - request["params"] = params - - # Create future for response - future: asyncio.Future = asyncio.get_event_loop().create_future() - self._pending_requests[request_id] = future - - # Send request - request_json = json.dumps(request) + "\n" - self._writer.write(request_json.encode("utf-8")) - await self._writer.drain() - - # Wait for response with timeout - try: - result = await asyncio.wait_for(future, timeout=30.0) - return result - except asyncio.TimeoutError: - self._pending_requests.pop(request_id, None) - raise TimeoutError(f"Request to MCP server timed out: {method}") - - async def _initialize(self): - """Initialize the MCP connection.""" - result = await self._send_request( - "initialize", - { - "protocolVersion": "2024-11-05", - "capabilities": { - "roots": {"listChanged": True}, - }, - "clientInfo": { - "name": "hanzo-mcp-proxy", - "version": "1.0.0", - }, - }, - ) - - # Send initialized notification - if self._writer: - notification = {"jsonrpc": "2.0", "method": "notifications/initialized"} - self._writer.write((json.dumps(notification) + "\n").encode("utf-8")) - await self._writer.drain() - - return result - - async def _discover_tools(self): - """Discover tools from the MCP server.""" - try: - result = await self._send_request("tools/list") - - self.tools = [] - for tool in result.get("tools", []): - self.tools.append( - ProxiedTool( - name=tool["name"], - description=tool.get("description", ""), - input_schema=tool.get("inputSchema", {}), - server_name=self.config.name, - ) - ) - - except Exception as e: - logger.warning(f"Failed to discover tools from '{self.config.name}': {e}") - - async def call_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Any: - """Call a tool on the MCP server.""" - result = await self._send_request( - "tools/call", - { - "name": tool_name, - "arguments": arguments, - }, - ) - return result - - -class MCPProxyRegistry: - """Registry for managing external MCP server proxies.""" - - _instance: Optional["MCPProxyRegistry"] = None - - CONFIG_FILE = Path.home() / ".hanzo" / "mcp" / "proxy.json" - - # Pre-configured servers - BUILTIN_SERVERS: Dict[str, MCPServerConfig] = { - "platform": MCPServerConfig( - name="platform", - command=["npx", "--yes", "@hanzo/platform-mcp"], - env={"PLATFORM_API_KEY": "${PLATFORM_API_KEY}"}, - description="Hanzo Platform - deploy and manage applications", - auth_required=True, - auth_env_var="PLATFORM_API_KEY", - auth_url="https://platform.hanzo.ai/settings/api-keys", - lazy_load=True, - ), - "github": MCPServerConfig( - name="github", - command=["npx", "--yes", "@modelcontextprotocol/server-github"], - env={"GITHUB_TOKEN": "${GITHUB_TOKEN}"}, - description="GitHub API - repositories, issues, PRs", - auth_required=True, - auth_env_var="GITHUB_TOKEN", - lazy_load=True, - ), - "cloudflare": MCPServerConfig( - name="cloudflare", - command=["npx", "--yes", "mcp-server-cloudflare"], - env={"CLOUDFLARE_API_TOKEN": "${CLOUDFLARE_API_TOKEN}"}, - description="Cloudflare API - DNS, Workers, R2, KV", - auth_required=True, - auth_env_var="CLOUDFLARE_API_TOKEN", - lazy_load=True, - ), - "filesystem": MCPServerConfig( - name="filesystem", - command=["npx", "--yes", "@modelcontextprotocol/server-filesystem", "/tmp"], - description="File system access", - lazy_load=True, - ), - "postgres": MCPServerConfig( - name="postgres", - command=["npx", "--yes", "@modelcontextprotocol/server-postgres"], - env={"DATABASE_URL": "${DATABASE_URL}"}, - description="PostgreSQL database access", - auth_required=True, - auth_env_var="DATABASE_URL", - lazy_load=True, - ), - "sqlite": MCPServerConfig( - name="sqlite", - command=["uvx", "mcp-server-sqlite"], - description="SQLite database access", - lazy_load=True, - ), - "docker": MCPServerConfig( - name="docker", - command=["uvx", "mcp-server-docker"], - description="Docker container management", - lazy_load=True, - ), - "kubernetes": MCPServerConfig( - name="kubernetes", - command=["uvx", "mcp-server-kubernetes"], - description="Kubernetes cluster management", - lazy_load=True, - ), - } - - def __init__(self): - self._connections: Dict[str, MCPServerConnection] = {} - self._custom_servers: Dict[str, MCPServerConfig] = {} - self._load_config() - - @classmethod - def get_instance(cls) -> "MCPProxyRegistry": - """Get singleton instance.""" - if cls._instance is None: - cls._instance = MCPProxyRegistry() - return cls._instance - - def _load_config(self): - """Load custom server configurations.""" - if self.CONFIG_FILE.exists(): - try: - with open(self.CONFIG_FILE, "r") as f: - data = json.load(f) - for name, config in data.get("servers", {}).items(): - self._custom_servers[name] = MCPServerConfig( - name=name, - command=config.get("command", []), - env=config.get("env", {}), - working_dir=config.get("working_dir"), - description=config.get("description", ""), - auto_start=config.get("auto_start", False), - lazy_load=config.get("lazy_load", True), - auth_required=config.get("auth_required", False), - auth_env_var=config.get("auth_env_var"), - auth_url=config.get("auth_url"), - ) - except Exception as e: - logger.warning(f"Failed to load proxy config: {e}") - - def _save_config(self): - """Save custom server configurations.""" - self.CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True) - - servers = {} - for name, config in self._custom_servers.items(): - servers[name] = { - "command": config.command, - "env": config.env, - "working_dir": config.working_dir, - "description": config.description, - "auto_start": config.auto_start, - "lazy_load": config.lazy_load, - "auth_required": config.auth_required, - "auth_env_var": config.auth_env_var, - "auth_url": config.auth_url, - } - - with open(self.CONFIG_FILE, "w") as f: - json.dump({"servers": servers}, f, indent=2) - - def get_server_config(self, name: str) -> Optional[MCPServerConfig]: - """Get server configuration by name.""" - if name in self._custom_servers: - return self._custom_servers[name] - if name in self.BUILTIN_SERVERS: - return self.BUILTIN_SERVERS[name] - return None - - def list_servers(self) -> List[Dict[str, Any]]: - """List all available servers.""" - servers = [] - - # Add builtin servers - for name, config in self.BUILTIN_SERVERS.items(): - is_connected = name in self._connections - auth_configured = True - if config.auth_required and config.auth_env_var: - auth_configured = bool(os.environ.get(config.auth_env_var)) - - servers.append( - { - "name": name, - "description": config.description, - "builtin": True, - "connected": is_connected, - "auth_required": config.auth_required, - "auth_configured": auth_configured, - "auth_env_var": config.auth_env_var, - "auth_url": config.auth_url, - "tool_count": ( - len(self._connections[name].tools) if is_connected else 0 - ), - } - ) - - # Add custom servers - for name, config in self._custom_servers.items(): - if name in self.BUILTIN_SERVERS: - continue - - is_connected = name in self._connections - auth_configured = True - if config.auth_required and config.auth_env_var: - auth_configured = bool(os.environ.get(config.auth_env_var)) - - servers.append( - { - "name": name, - "description": config.description, - "builtin": False, - "connected": is_connected, - "auth_required": config.auth_required, - "auth_configured": auth_configured, - "auth_env_var": config.auth_env_var, - "auth_url": config.auth_url, - "tool_count": ( - len(self._connections[name].tools) if is_connected else 0 - ), - } - ) - - return servers - - async def enable_server(self, name: str) -> Dict[str, Any]: - """Enable and connect to an MCP server. - - This starts the server if not running and discovers its tools. - """ - config = self.get_server_config(name) - if not config: - return {"success": False, "error": f"Server '{name}' not found"} - - # Check auth - if config.auth_required and config.auth_env_var: - if not os.environ.get(config.auth_env_var): - return { - "success": False, - "error": f"Authentication required. Set {config.auth_env_var}", - "auth_url": config.auth_url, - } - - # Check if already connected - if name in self._connections: - conn = self._connections[name] - return { - "success": True, - "message": f"Already connected to '{name}'", - "tools": [ - {"name": t.name, "description": t.description} for t in conn.tools - ], - } - - # Create and connect - conn = MCPServerConnection(config) - if await conn.connect(): - self._connections[name] = conn - return { - "success": True, - "message": f"Connected to '{name}'", - "tools": [ - {"name": t.name, "description": t.description} for t in conn.tools - ], - } - else: - return {"success": False, "error": f"Failed to connect to '{name}'"} - - async def disable_server(self, name: str) -> Dict[str, Any]: - """Disable and disconnect from an MCP server.""" - if name not in self._connections: - return {"success": False, "error": f"Server '{name}' is not connected"} - - conn = self._connections.pop(name) - await conn.disconnect() - - return {"success": True, "message": f"Disconnected from '{name}'"} - - def add_server(self, config: MCPServerConfig) -> Dict[str, Any]: - """Add a custom MCP server configuration.""" - self._custom_servers[config.name] = config - self._save_config() - return {"success": True, "message": f"Added server '{config.name}'"} - - def remove_server(self, name: str) -> Dict[str, Any]: - """Remove a custom MCP server configuration.""" - if name not in self._custom_servers: - if name in self.BUILTIN_SERVERS: - return { - "success": False, - "error": f"Cannot remove builtin server '{name}'", - } - return {"success": False, "error": f"Server '{name}' not found"} - - # Disconnect if connected - if name in self._connections: - asyncio.create_task(self.disable_server(name)) - - del self._custom_servers[name] - self._save_config() - return {"success": True, "message": f"Removed server '{name}'"} - - def get_all_proxied_tools(self) -> List[ProxiedTool]: - """Get all tools from connected servers.""" - tools = [] - for conn in self._connections.values(): - tools.extend(conn.tools) - return tools - - async def call_proxied_tool( - self, server_name: str, tool_name: str, arguments: Dict[str, Any] - ) -> Any: - """Call a tool on a proxied server. - - If lazy_load is enabled, the server will be connected on first use. - """ - # Lazy load if not connected - if server_name not in self._connections: - config = self.get_server_config(server_name) - if config and config.lazy_load: - result = await self.enable_server(server_name) - if not result.get("success"): - raise ConnectionError(result.get("error", "Failed to connect")) - else: - raise ConnectionError(f"Server '{server_name}' is not connected") - - conn = self._connections.get(server_name) - if not conn: - raise ConnectionError(f"Server '{server_name}' is not connected") - - return await conn.call_tool(tool_name, arguments) - - -# Convenience function for lazy loading -async def enable_mcp_server(name: str) -> Dict[str, Any]: - """Enable an MCP server by name. - - This is a convenience function for lazy-loading MCP servers. - - Example: - result = await enable_mcp_server("platform") - if result["success"]: - print(f"Enabled with {len(result['tools'])} tools") - """ - registry = MCPProxyRegistry.get_instance() - return await registry.enable_server(name) - - -async def call_mcp_tool(server: str, tool: str, **kwargs) -> Any: - """Call a tool on an MCP server. - - This is a convenience function that handles lazy loading. - - Example: - result = await call_mcp_tool("platform", "application-list") - """ - registry = MCPProxyRegistry.get_instance() - return await registry.call_proxied_tool(server, tool, kwargs) - - -# Export builtin servers for convenience -BUILTIN_SERVERS = MCPProxyRegistry.BUILTIN_SERVERS diff --git a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/mcp_remove.py b/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/mcp_remove.py deleted file mode 100644 index 552ba6522..000000000 --- a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/mcp_remove.py +++ /dev/null @@ -1,140 +0,0 @@ -"""Remove MCP servers.""" - -from typing import Unpack, Annotated, TypedDict, final, override - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, ToolContext, auto_timeout, create_tool_context - -from .mcp_add import McpAddTool - -ServerName = Annotated[ - str, - Field( - description="Name of the server to remove", - min_length=1, - ), -] - -Force = Annotated[ - bool, - Field( - description="Force removal even if server is running", - default=False, - ), -] - - -class McpRemoveParams(TypedDict, total=False): - """Parameters for MCP remove tool.""" - - name: str - force: bool - - -@final -class McpRemoveTool(BaseTool): - """Tool for removing MCP servers.""" - - def __init__(self): - """Initialize the MCP remove tool.""" - pass - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "mcp_remove" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Remove previously added MCP servers. - -This removes MCP servers that were added with mcp_add. -If the server is running, it will be stopped first. - -Examples: -- mcp_remove --name filesystem -- mcp_remove --name github --force - -Use 'mcp_stats' to see all servers before removing. -""" - - @override - @auto_timeout("mcp_remove") - async def call( - self, - ctx: MCPContext, - **params: Unpack[McpRemoveParams], - ) -> str: - """Remove an MCP server. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Result of removing the server - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - name = params.get("name") - if not name: - return "Error: name is required" - - force = params.get("force", False) - - # Get current servers - servers = McpAddTool.get_servers() - - if name not in servers: - return f"Error: Server '{name}' not found. Use 'mcp_stats' to see available servers." - - server = servers[name] - - await tool_ctx.info(f"Removing MCP server '{name}'") - - # Check if server is running - if server.get("status") == "running" and server.get("process_id"): - if not force: - return f"Error: Server '{name}' is currently running. Use --force to remove anyway." - else: - # Stop the server process if it's running - process_id = server.get("process_id") - if process_id: - try: - import os - import signal - - os.kill(process_id, signal.SIGTERM) - await tool_ctx.info( - f"Stopped running server '{name}' (PID: {process_id})" - ) - except ProcessLookupError: - await tool_ctx.info( - f"Server '{name}' process not found (already stopped)" - ) - - # Remove from registry - del McpAddTool._mcp_servers[name] - McpAddTool._save_servers() - - output = [ - f"Successfully removed MCP server '{name}'", - f" Type: {server.get('type', 'unknown')}", - f" Command: {' '.join(server.get('command', []))}", - ] - - if server.get("tools"): - output.append(f" Tools removed: {len(server['tools'])}") - - return "\n".join(output) - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/mcp_stats.py b/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/mcp_stats.py deleted file mode 100644 index 5f81b1341..000000000 --- a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/mcp_stats.py +++ /dev/null @@ -1,167 +0,0 @@ -"""MCP server statistics.""" - -from typing import Unpack, TypedDict, final, override - -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, ToolContext, auto_timeout, create_tool_context - -from .mcp_add import McpAddTool - - -class McpStatsParams(TypedDict, total=False): - """Parameters for MCP stats tool.""" - - pass - - -@final -class McpStatsTool(BaseTool): - """Tool for showing MCP server statistics.""" - - def __init__(self): - """Initialize the MCP stats tool.""" - pass - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "mcp_stats" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Show statistics about added MCP servers. - -Displays: -- Total number of servers -- Server types (Python, Node.js) -- Server status (running, stopped, error) -- Available tools from each server -- Resource usage per server - -Example: -- mcp_stats -""" - - @override - @auto_timeout("mcp_stats") - async def call( - self, - ctx: MCPContext, - **params: Unpack[McpStatsParams], - ) -> str: - """Get MCP server statistics. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - MCP server statistics - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Get all servers - servers = McpAddTool.get_servers() - - if not servers: - return ( - "No MCP servers have been added yet.\n\nUse 'mcp_add' to add servers." - ) - - output = [] - output.append("=== MCP Server Statistics ===") - output.append(f"Total Servers: {len(servers)}") - output.append("") - - # Count by type - type_counts = {} - status_counts = {} - total_tools = 0 - total_resources = 0 - - for server in servers.values(): - # Count types - server_type = server.get("type", "unknown") - type_counts[server_type] = type_counts.get(server_type, 0) + 1 - - # Count status - status = server.get("status", "unknown") - status_counts[status] = status_counts.get(status, 0) + 1 - - # Count tools and resources - total_tools += len(server.get("tools", [])) - total_resources += len(server.get("resources", [])) - - # Server types - output.append("Server Types:") - for stype, count in sorted(type_counts.items()): - output.append(f" {stype}: {count}") - output.append("") - - # Server status - output.append("Server Status:") - for status, count in sorted(status_counts.items()): - output.append(f" {status}: {count}") - output.append("") - - # Tools and resources - output.append(f"Total Tools Available: {total_tools}") - output.append(f"Total Resources Available: {total_resources}") - output.append("") - - # Individual server details - output.append("=== Server Details ===") - - for name, server in sorted(servers.items()): - output.append(f"\n{name}:") - output.append(f" Type: {server.get('type', 'unknown')}") - output.append(f" Status: {server.get('status', 'unknown')}") - output.append(f" Command: {' '.join(server.get('command', []))}") - - if server.get("process_id"): - output.append(f" Process ID: {server['process_id']}") - - if server.get("error"): - output.append(f" Error: {server['error']}") - - tools = server.get("tools", []) - if tools: - output.append(f" Tools ({len(tools)}):") - for tool in tools[:5]: # Show first 5 - output.append(f" - {tool}") - if len(tools) > 5: - output.append(f" ... and {len(tools) - 5} more") - - resources = server.get("resources", []) - if resources: - output.append(f" Resources ({len(resources)}):") - for resource in resources[:5]: # Show first 5 - output.append(f" - {resource}") - if len(resources) > 5: - output.append(f" ... and {len(resources) - 5} more") - - if server.get("env"): - output.append(f" Environment vars: {list(server['env'].keys())}") - - # Common MCP servers hint - output.append("\n=== Available MCP Servers ===") - output.append("Common servers you can add:") - output.append(" - @modelcontextprotocol/server-filesystem") - output.append(" - @modelcontextprotocol/server-github") - output.append(" - mcp-server-git") - output.append(" - @modelcontextprotocol/server-postgres") - output.append(" - @modelcontextprotocol/server-browser-use") - output.append(" - @modelcontextprotocol/server-iterm2") - output.append(" - @modelcontextprotocol/server-linear") - output.append(" - @modelcontextprotocol/server-slack") - - return "\n".join(output) - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/mcp_tool.py b/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/mcp_tool.py deleted file mode 100644 index 488c3cda2..000000000 --- a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/mcp_tool.py +++ /dev/null @@ -1,534 +0,0 @@ -"""Unified MCP tool for managing MCP servers.""" - -import os -import json -import signal -import subprocess -from typing import ( - Any, - Dict, - List, - Unpack, - Optional, - Annotated, - TypedDict, - final, - override, -) -from pathlib import Path - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, ToolContext, auto_timeout, create_tool_context - -# Parameter types -Action = Annotated[ - str, - Field( - description="Action to perform: list, add, remove, enable, disable, restart, config", - default="list", - ), -] - -Name = Annotated[ - Optional[str], - Field( - description="MCP server name", - default=None, - ), -] - -Command = Annotated[ - Optional[str], - Field( - description="Command to run the MCP server", - default=None, - ), -] - -Args = Annotated[ - Optional[List[str]], - Field( - description="Arguments for the MCP server command", - default=None, - ), -] - -Env = Annotated[ - Optional[Dict[str, str]], - Field( - description="Environment variables for the MCP server", - default=None, - ), -] - -ConfigKey = Annotated[ - Optional[str], - Field( - description="Configuration key to get/set", - default=None, - ), -] - -ConfigValue = Annotated[ - Optional[Any], - Field( - description="Configuration value to set", - default=None, - ), -] - -AutoStart = Annotated[ - bool, - Field( - description="Auto-start server when Hanzo AI starts", - default=True, - ), -] - - -class MCPParams(TypedDict, total=False): - """Parameters for MCP tool.""" - - action: str - name: Optional[str] - command: Optional[str] - args: Optional[List[str]] - env: Optional[Dict[str, str]] - config_key: Optional[str] - config_value: Optional[Any] - auto_start: bool - - -@final -class MCPTool(BaseTool): - """Tool for managing MCP servers.""" - - # Config file - CONFIG_FILE = Path.home() / ".hanzo" / "mcp" / "servers.json" - - # Running servers tracking - _running_servers: Dict[str, subprocess.Popen] = {} - - def __init__(self): - """Initialize the MCP management tool.""" - self.config = self._load_config() - - # Auto-start servers if configured - self._auto_start_servers() - - def _load_config(self) -> Dict[str, Any]: - """Load MCP server configuration.""" - if self.CONFIG_FILE.exists(): - try: - with open(self.CONFIG_FILE, "r") as f: - return json.load(f) - except Exception: - pass - - # Default configuration with some examples - return { - "servers": { - # Example configurations (disabled by default) - "filesystem": { - "command": "npx", - "args": ["@modelcontextprotocol/server-filesystem", "/tmp"], - "env": {}, - "enabled": False, - "auto_start": False, - "description": "MCP filesystem server for /tmp access", - }, - "github": { - "command": "npx", - "args": ["@modelcontextprotocol/server-github"], - "env": {"GITHUB_TOKEN": "${GITHUB_TOKEN}"}, - "enabled": False, - "auto_start": False, - "description": "GitHub API access via MCP", - }, - "postgres": { - "command": "npx", - "args": [ - "@modelcontextprotocol/server-postgres", - "postgresql://localhost/db", - ], - "env": {}, - "enabled": False, - "auto_start": False, - "description": "PostgreSQL database access", - }, - }, - "global_env": {}, - "log_dir": str(Path.home() / ".hanzo" / "mcp" / "logs"), - } - - def _save_config(self): - """Save configuration.""" - self.CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True) - with open(self.CONFIG_FILE, "w") as f: - json.dump(self.config, f, indent=2) - - def _auto_start_servers(self): - """Auto-start servers configured for auto-start.""" - for name, server_config in self.config.get("servers", {}).items(): - if server_config.get("enabled", False) and server_config.get( - "auto_start", False - ): - self._start_server(name, server_config) - - def _start_server(self, name: str, config: Dict[str, Any]) -> bool: - """Start an MCP server.""" - if name in self._running_servers: - return False # Already running - - try: - # Prepare environment - env = os.environ.copy() - env.update(self.config.get("global_env", {})) - - # Process server-specific env vars - server_env = config.get("env", {}) - for key, value in server_env.items(): - # Replace ${VAR} with actual environment variable - if value.startswith("${") and value.endswith("}"): - var_name = value[2:-1] - if var_name in os.environ: - value = os.environ[var_name] - env[key] = value - - # Prepare command - cmd = [config["command"]] + config.get("args", []) - - # Create log directory - log_dir = Path( - self.config.get("log_dir", str(Path.home() / ".hanzo" / "mcp" / "logs")) - ) - log_dir.mkdir(parents=True, exist_ok=True) - - # Start process - log_file = log_dir / f"{name}.log" - with open(log_file, "a") as log: - process = subprocess.Popen( - cmd, - env=env, - stdout=log, - stderr=subprocess.STDOUT, - preexec_fn=os.setsid if os.name != "nt" else None, - ) - - self._running_servers[name] = process - return True - - except Exception: - return False - - def _stop_server(self, name: str) -> bool: - """Stop an MCP server.""" - if name not in self._running_servers: - return False - - process = self._running_servers[name] - try: - if os.name == "nt": - process.terminate() - else: - os.killpg(os.getpgid(process.pid), signal.SIGTERM) - - process.wait(timeout=5) - except Exception: - # Force kill if needed - try: - if os.name == "nt": - process.kill() - else: - os.killpg(os.getpgid(process.pid), signal.SIGKILL) - except Exception: - pass - - del self._running_servers[name] - return True - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "mcp" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - servers = self.config.get("servers", {}) - enabled = sum(1 for s in servers.values() if s.get("enabled", False)) - running = len(self._running_servers) - - return f"""Manage MCP servers. Actions: list (default), add, remove, enable, disable, restart, config. - -Usage: -mcp -mcp --action add --name github --command npx --args '["@modelcontextprotocol/server-github"]' -mcp --action enable --name github - -Status: {enabled} enabled, {running} running""" - - @override - @auto_timeout("mcp") - async def call( - self, - ctx: MCPContext, - **params: Unpack[MCPParams], - ) -> str: - """Execute MCP management action.""" - # Create tool context only if we have a proper MCP context - tool_ctx = None - try: - if hasattr(ctx, "client") and ctx.client and hasattr(ctx.client, "server"): - tool_ctx = create_tool_context(ctx) - if tool_ctx: - await tool_ctx.set_tool_info(self.name) - except Exception: - pass - - # Extract action - action = params.get("action", "list") - - # Route to appropriate handler - if action == "list": - return self._handle_list() - elif action == "add": - return self._handle_add(params) - elif action == "remove": - return self._handle_remove(params.get("name")) - elif action == "enable": - return self._handle_enable(params.get("name")) - elif action == "disable": - return self._handle_disable(params.get("name")) - elif action == "restart": - return self._handle_restart(params.get("name")) - elif action == "config": - return self._handle_config( - params.get("config_key"), params.get("config_value") - ) - else: - return f"Error: Unknown action '{action}'. Valid actions: list, add, remove, enable, disable, restart, config" - - def _handle_list(self) -> str: - """List all MCP servers.""" - servers = self.config.get("servers", {}) - - if not servers: - return "No MCP servers configured. Use 'mcp --action add' to add one." - - output = ["=== MCP Servers ==="] - output.append( - f"Total: {len(servers)} | Enabled: {sum(1 for s in servers.values() if s.get('enabled', False))} | Running: {len(self._running_servers)}" - ) - output.append("") - - for name, config in sorted(servers.items()): - status_parts = [] - - # Check if enabled - if config.get("enabled", False): - status_parts.append("โœ… Enabled") - else: - status_parts.append("โŒ Disabled") - - # Check if running - if name in self._running_servers: - process = self._running_servers[name] - if process.poll() is None: - status_parts.append("๐ŸŸข Running") - else: - status_parts.append("๐Ÿ”ด Stopped") - del self._running_servers[name] - else: - status_parts.append("โšซ Not running") - - # Auto-start status - if config.get("auto_start", False): - status_parts.append("๐Ÿš€ Auto-start") - - status = " | ".join(status_parts) - - output.append(f"{name}: {status}") - if config.get("description"): - output.append(f" Description: {config['description']}") - output.append( - f" Command: {config['command']} {' '.join(config.get('args', []))}" - ) - - if config.get("env"): - env_str = ", ".join([f"{k}={v}" for k, v in config["env"].items()]) - output.append(f" Environment: {env_str}") - - output.append("\nUse 'mcp --action enable --name ' to enable a server") - output.append("Use 'mcp --action add' to add a new server") - - return "\n".join(output) - - def _handle_add(self, params: Dict[str, Any]) -> str: - """Add a new MCP server.""" - name = params.get("name") - command = params.get("command") - - if not name: - return "Error: name is required for add action" - if not command: - return "Error: command is required for add action" - - servers = self.config.get("servers", {}) - if name in servers: - return f"Error: Server '{name}' already exists. Use a different name or remove it first." - - # Create server config - server_config = { - "command": command, - "args": params.get("args", []), - "env": params.get("env", {}), - "enabled": False, - "auto_start": params.get("auto_start", True), - "description": params.get("description", ""), - } - - servers[name] = server_config - self.config["servers"] = servers - self._save_config() - - return f"Successfully added MCP server '{name}'. Use 'mcp --action enable --name {name}' to enable it." - - def _handle_remove(self, name: Optional[str]) -> str: - """Remove an MCP server.""" - if not name: - return "Error: name is required for remove action" - - servers = self.config.get("servers", {}) - if name not in servers: - return f"Error: Server '{name}' not found" - - # Stop if running - if name in self._running_servers: - self._stop_server(name) - - del servers[name] - self.config["servers"] = servers - self._save_config() - - return f"Successfully removed MCP server '{name}'" - - def _handle_enable(self, name: Optional[str]) -> str: - """Enable an MCP server.""" - if not name: - return "Error: name is required for enable action" - - servers = self.config.get("servers", {}) - if name not in servers: - return f"Error: Server '{name}' not found" - - servers[name]["enabled"] = True - self.config["servers"] = servers - self._save_config() - - # Start if auto-start is enabled - if servers[name].get("auto_start", False): - if self._start_server(name, servers[name]): - return f"Successfully enabled and started MCP server '{name}'" - else: - return f"Enabled MCP server '{name}' but failed to start it. Check the configuration." - - return f"Successfully enabled MCP server '{name}'" - - def _handle_disable(self, name: Optional[str]) -> str: - """Disable an MCP server.""" - if not name: - return "Error: name is required for disable action" - - servers = self.config.get("servers", {}) - if name not in servers: - return f"Error: Server '{name}' not found" - - # Stop if running - if name in self._running_servers: - self._stop_server(name) - - servers[name]["enabled"] = False - self.config["servers"] = servers - self._save_config() - - return f"Successfully disabled MCP server '{name}'" - - def _handle_restart(self, name: Optional[str]) -> str: - """Restart an MCP server.""" - if not name: - return "Error: name is required for restart action" - - servers = self.config.get("servers", {}) - if name not in servers: - return f"Error: Server '{name}' not found" - - if not servers[name].get("enabled", False): - return f"Error: Server '{name}' is not enabled" - - # Stop if running - if name in self._running_servers: - self._stop_server(name) - - # Start again - if self._start_server(name, servers[name]): - return f"Successfully restarted MCP server '{name}'" - else: - return f"Failed to restart MCP server '{name}'. Check the configuration." - - def _handle_config(self, key: Optional[str], value: Optional[Any]) -> str: - """Get or set configuration values.""" - if not key: - # Show all config - return json.dumps(self.config, indent=2) - - # Parse nested keys (e.g., "servers.github.auto_start") - keys = key.split(".") - - if value is None: - # Get value - current = self.config - for k in keys: - if isinstance(current, dict) and k in current: - current = current[k] - else: - return f"Configuration key '{key}' not found" - - return ( - json.dumps(current, indent=2) - if isinstance(current, (dict, list)) - else str(current) - ) - else: - # Set value - # Navigate to parent - current = self.config - for k in keys[:-1]: - if k not in current: - current[k] = {} - current = current[k] - - # Parse value if it looks like JSON - if ( - isinstance(value, str) - and value.startswith("{") - or value.startswith("[") - ): - try: - value = json.loads(value) - except Exception: - pass - - # Set the value - current[keys[-1]] = value - self._save_config() - - return f"Successfully set {key} = {json.dumps(value) if isinstance(value, (dict, list)) else value}" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/platform_auth.py b/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/platform_auth.py deleted file mode 100644 index 6dc7a6357..000000000 --- a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/platform_auth.py +++ /dev/null @@ -1,384 +0,0 @@ -"""Platform authentication utilities for Hanzo Platform MCP. - -This module provides utilities for authenticating with Hanzo Platform, -including: -- Getting API keys -- Getting deployment tokens -- Setting up CI/CD credentials - -Usage: - # Interactive login - hanzo-platform login - - # Get deploy token for CI - hanzo-platform deploy-token --app console - - # Check authentication status - hanzo-platform status -""" - -import os -import sys -import json -import asyncio -import webbrowser -from typing import Any, Dict, Optional -from pathlib import Path -from dataclasses import dataclass - -import httpx - - -@dataclass -class PlatformConfig: - """Configuration for Platform API.""" - - base_url: str = "https://platform.hanzo.ai" - api_key: Optional[str] = None - org_id: Optional[str] = None - - @classmethod - def load(cls) -> "PlatformConfig": - """Load configuration from file and environment.""" - config = cls() - - # Load from file - config_file = Path.home() / ".hanzo" / "platform" / "config.json" - if config_file.exists(): - try: - with open(config_file, "r") as f: - data = json.load(f) - config.base_url = data.get("base_url", config.base_url) - config.api_key = data.get("api_key") - config.org_id = data.get("org_id") - except Exception: - pass - - # Environment overrides - if os.environ.get("PLATFORM_URL"): - config.base_url = os.environ["PLATFORM_URL"] - if os.environ.get("PLATFORM_API_KEY"): - config.api_key = os.environ["PLATFORM_API_KEY"] - if os.environ.get("PLATFORM_ORG_ID"): - config.org_id = os.environ["PLATFORM_ORG_ID"] - - return config - - def save(self): - """Save configuration to file.""" - config_file = Path.home() / ".hanzo" / "platform" / "config.json" - config_file.parent.mkdir(parents=True, exist_ok=True) - - data = { - "base_url": self.base_url, - "api_key": self.api_key, - "org_id": self.org_id, - } - - with open(config_file, "w") as f: - json.dump(data, f, indent=2) - - # Set restrictive permissions - try: - import stat - - config_file.chmod(stat.S_IRUSR | stat.S_IWUSR) # 0600 - except Exception: - pass - - -class PlatformClient: - """Client for Hanzo Platform API.""" - - def __init__(self, config: Optional[PlatformConfig] = None): - self.config = config or PlatformConfig.load() - self._client: Optional[httpx.AsyncClient] = None - - async def __aenter__(self): - self._client = httpx.AsyncClient( - base_url=self.config.base_url, - headers=self._get_headers(), - timeout=30.0, - ) - return self - - async def __aexit__(self, *args): - if self._client: - await self._client.aclose() - - def _get_headers(self) -> Dict[str, str]: - headers = { - "Content-Type": "application/json", - "Accept": "application/json", - } - if self.config.api_key: - headers["x-api-key"] = self.config.api_key - return headers - - async def check_auth(self) -> Dict[str, Any]: - """Check if authentication is valid.""" - if not self._client: - raise RuntimeError("Client not initialized") - - try: - response = await self._client.get("/api/v1/user/me") - if response.status_code == 200: - return {"authenticated": True, "user": response.json()} - return {"authenticated": False, "error": "Invalid API key"} - except Exception as e: - return {"authenticated": False, "error": str(e)} - - async def list_projects(self) -> Dict[str, Any]: - """List all projects.""" - if not self._client: - raise RuntimeError("Client not initialized") - - response = await self._client.get("/api/v1/projects") - return response.json() - - async def list_applications(self, project_id: str) -> Dict[str, Any]: - """List applications in a project.""" - if not self._client: - raise RuntimeError("Client not initialized") - - response = await self._client.get(f"/api/v1/projects/{project_id}/applications") - return response.json() - - async def get_deploy_token(self, app_id: str) -> Optional[str]: - """Get or create a deploy token for an application.""" - if not self._client: - raise RuntimeError("Client not initialized") - - # Get application details including refresh token - response = await self._client.get(f"/api/v1/applications/{app_id}") - if response.status_code != 200: - return None - - app_data = response.json() - return app_data.get("application", {}).get("refreshToken") - - async def create_api_key(self, name: str = "hanzo-mcp") -> Optional[str]: - """Create a new API key.""" - if not self._client: - raise RuntimeError("Client not initialized") - - response = await self._client.post( - "/api/v1/api-keys", - json={"name": name, "expiresAt": None}, - ) - - if response.status_code == 201: - data = response.json() - return data.get("apiKey", {}).get("key") - return None - - -async def interactive_login() -> bool: - """Interactive login flow.""" - print("=== Hanzo Platform Login ===") - print() - - config = PlatformConfig.load() - - # Check if already logged in - if config.api_key: - async with PlatformClient(config) as client: - result = await client.check_auth() - if result.get("authenticated"): - user = result.get("user", {}) - print(f"Already logged in as: {user.get('email', 'Unknown')}") - confirm = input("Re-authenticate? [y/N]: ").strip().lower() - if confirm != "y": - return True - - print(f"Opening {config.base_url}/settings/api-keys in your browser...") - print() - - # Try to open browser - try: - webbrowser.open(f"{config.base_url}/settings/api-keys") - except Exception: - print( - f"Could not open browser. Please visit: {config.base_url}/settings/api-keys" - ) - - print("1. Create a new API key in the Platform dashboard") - print("2. Copy the API key") - print() - - api_key = input("Paste your API key here: ").strip() - - if not api_key: - print("Error: No API key provided") - return False - - # Validate the API key - config.api_key = api_key - - async with PlatformClient(config) as client: - result = await client.check_auth() - - if result.get("authenticated"): - user = result.get("user", {}) - print(f"\nโœ… Successfully authenticated as: {user.get('email')}") - - # Save the configuration - config.save() - - # Also set in environment for current session - os.environ["PLATFORM_API_KEY"] = api_key - - print(f"\nConfiguration saved to: ~/.hanzo/platform/config.json") - print(f"For CI/CD, set: PLATFORM_API_KEY={api_key[:8]}...") - return True - else: - print(f"\nโŒ Authentication failed: {result.get('error')}") - return False - - -async def get_deploy_token_cli(app_name: str) -> Optional[str]: - """Get deploy token for an application.""" - config = PlatformConfig.load() - - if not config.api_key: - print("Error: Not logged in. Run 'hanzo-platform login' first.") - return None - - async with PlatformClient(config) as client: - # List projects to find the app - projects = await client.list_projects() - - for project in projects.get("projects", []): - apps = await client.list_applications(project["id"]) - - for app in apps.get("applications", []): - if app.get("name") == app_name or app.get("id") == app_name: - token = await client.get_deploy_token(app["id"]) - if token: - return token - - print(f"Error: Application '{app_name}' not found") - return None - - -async def status() -> Dict[str, Any]: - """Check authentication status and available resources.""" - config = PlatformConfig.load() - - result = { - "configured": bool(config.api_key), - "base_url": config.base_url, - "authenticated": False, - "user": None, - "projects": [], - } - - if not config.api_key: - return result - - async with PlatformClient(config) as client: - auth_result = await client.check_auth() - result["authenticated"] = auth_result.get("authenticated", False) - result["user"] = auth_result.get("user") - - if result["authenticated"]: - try: - projects = await client.list_projects() - result["projects"] = projects.get("projects", []) - except Exception: - pass - - return result - - -def cli(): - """CLI entry point.""" - import argparse - - parser = argparse.ArgumentParser( - description="Hanzo Platform authentication", - prog="hanzo-platform", - ) - - subparsers = parser.add_subparsers(dest="command") - - # Login command - subparsers.add_parser("login", help="Interactive login") - - # Status command - subparsers.add_parser("status", help="Check authentication status") - - # Deploy token command - deploy_parser = subparsers.add_parser( - "deploy-token", help="Get deploy token for app" - ) - deploy_parser.add_argument("--app", required=True, help="Application name or ID") - deploy_parser.add_argument("--output", choices=["plain", "json"], default="plain") - - # Config command - config_parser = subparsers.add_parser("config", help="Show/set configuration") - config_parser.add_argument("--set-url", help="Set Platform URL") - config_parser.add_argument("--set-key", help="Set API key") - - args = parser.parse_args() - - if args.command == "login": - success = asyncio.run(interactive_login()) - sys.exit(0 if success else 1) - - elif args.command == "status": - result = asyncio.run(status()) - - if result["authenticated"]: - user = result.get("user", {}) - print(f"โœ… Authenticated as: {user.get('email', 'Unknown')}") - print(f" Platform: {result['base_url']}") - print(f" Projects: {len(result.get('projects', []))}") - - for project in result.get("projects", []): - print(f" - {project.get('name', 'Unknown')}") - else: - print("โŒ Not authenticated") - print(f" Platform: {result['base_url']}") - print("\nRun 'hanzo-platform login' to authenticate") - sys.exit(0) - - elif args.command == "deploy-token": - token = asyncio.run(get_deploy_token_cli(args.app)) - if token: - if args.output == "json": - print(json.dumps({"token": token})) - else: - print(token) - sys.exit(0) - sys.exit(1) - - elif args.command == "config": - config = PlatformConfig.load() - - if args.set_url: - config.base_url = args.set_url - config.save() - print(f"Set Platform URL to: {args.set_url}") - - if args.set_key: - config.api_key = args.set_key - config.save() - print(f"Set API key: {args.set_key[:8]}...") - - if not args.set_url and not args.set_key: - print(f"Platform URL: {config.base_url}") - print( - f"API Key: {'****' + config.api_key[-4:] if config.api_key else 'Not set'}" - ) - print(f"Org ID: {config.org_id or 'Not set'}") - - sys.exit(0) - - else: - parser.print_help() - sys.exit(1) - - -if __name__ == "__main__": - cli() diff --git a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/proxy_tool.py b/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/proxy_tool.py deleted file mode 100644 index 9f060cd44..000000000 --- a/pkg/hanzo-tools-mcp/hanzo_tools/mcp_tools/proxy_tool.py +++ /dev/null @@ -1,381 +0,0 @@ -"""Proxy Tool - Enable/disable external MCP servers dynamically. - -This tool allows dynamically enabling external MCP servers like: -- platform (Hanzo Platform) -- github (GitHub API) -- cloudflare (Cloudflare API) -- filesystem, postgres, sqlite, docker, kubernetes, etc. - -When enabled, tools from these servers become available through hanzo-mcp. -""" - -import os -import json -from typing import ( - Any, - Dict, - List, - Unpack, - Optional, - Annotated, - TypedDict, - final, - override, -) - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context - -from .mcp_proxy import MCPServerConfig, MCPProxyRegistry - -# Parameter types -Action = Annotated[ - str, - Field( - description="Action: list, enable, disable, add, remove, call, auth", - default="list", - ), -] - -ServerName = Annotated[ - Optional[str], - Field( - description="MCP server name (e.g., platform, github, cloudflare)", - default=None, - ), -] - -ToolName = Annotated[ - Optional[str], - Field( - description="Tool name to call (for 'call' action)", - default=None, - ), -] - -ToolArgs = Annotated[ - Optional[Dict[str, Any]], - Field( - description="Arguments for tool call (JSON object)", - default=None, - ), -] - -Command = Annotated[ - Optional[str], - Field( - description="Command to run the MCP server (for 'add' action)", - default=None, - ), -] - -AuthToken = Annotated[ - Optional[str], - Field( - description="API token for authentication (for 'auth' action)", - default=None, - ), -] - - -class ProxyToolParams(TypedDict, total=False): - """Parameters for the proxy tool.""" - - action: str - server: Optional[str] - tool: Optional[str] - args: Optional[Dict[str, Any]] - command: Optional[str] - token: Optional[str] - env: Optional[Dict[str, str]] - description: Optional[str] - - -@final -class ProxyTool(BaseTool): - """Tool for managing external MCP server proxies.""" - - def __init__(self): - """Initialize the proxy tool.""" - self._registry = MCPProxyRegistry.get_instance() - - @property - @override - def name(self) -> str: - return "proxy" - - @property - @override - def description(self) -> str: - servers = self._registry.list_servers() - connected = sum(1 for s in servers if s.get("connected")) - total_tools = sum(s.get("tool_count", 0) for s in servers) - - return f"""Manage external MCP server proxies. Actions: list, enable, disable, add, remove, call, auth. - -Connected: {connected}/{len(servers)} servers, {total_tools} tools available. - -Built-in servers: platform, github, cloudflare, filesystem, postgres, sqlite, docker, kubernetes - -Usage: - proxy # List all servers - proxy --action enable --server platform # Enable Platform MCP - proxy --action disable --server github # Disable GitHub MCP - proxy --action auth --server platform --token # Configure auth - proxy --action call --server platform --tool application-list # Call tool""" - - @override - @auto_timeout("proxy") - async def call( - self, - ctx: MCPContext, - **params: Unpack[ProxyToolParams], - ) -> str: - """Execute proxy action.""" - tool_ctx = None - try: - if hasattr(ctx, "client") and ctx.client: - tool_ctx = create_tool_context(ctx) - if tool_ctx: - await tool_ctx.set_tool_info(self.name) - except Exception: - pass - - action = params.get("action", "list") - - if action == "list": - return self._handle_list() - elif action == "enable": - return await self._handle_enable(params.get("server")) - elif action == "disable": - return await self._handle_disable(params.get("server")) - elif action == "add": - return self._handle_add(params) - elif action == "remove": - return self._handle_remove(params.get("server")) - elif action == "call": - return await self._handle_call( - params.get("server"), - params.get("tool"), - params.get("args") or {}, - ) - elif action == "auth": - return self._handle_auth(params.get("server"), params.get("token")) - else: - return f"Unknown action: {action}. Valid: list, enable, disable, add, remove, call, auth" - - def _handle_list(self) -> str: - """List all available MCP servers.""" - servers = self._registry.list_servers() - - if not servers: - return "No MCP servers available." - - output = ["=== MCP Server Proxies ===", ""] - - # Group by status - connected = [s for s in servers if s.get("connected")] - available = [ - s - for s in servers - if not s.get("connected") and s.get("auth_configured", True) - ] - needs_auth = [ - s - for s in servers - if not s.get("connected") and not s.get("auth_configured", True) - ] - - if connected: - output.append("๐ŸŸข Connected:") - for s in connected: - output.append( - f" {s['name']}: {s['description']} ({s['tool_count']} tools)" - ) - output.append("") - - if available: - output.append("โšช Available (ready to enable):") - for s in available: - output.append(f" {s['name']}: {s['description']}") - output.append("") - - if needs_auth: - output.append("๐Ÿ” Needs authentication:") - for s in needs_auth: - auth_info = f"Set {s.get('auth_env_var', 'API key')}" - if s.get("auth_url"): - auth_info += f" or visit {s['auth_url']}" - output.append(f" {s['name']}: {s['description']}") - output.append(f" โ†’ {auth_info}") - output.append("") - - output.append("Commands:") - output.append(" proxy --action enable --server ") - output.append(" proxy --action auth --server --token ") - - return "\n".join(output) - - async def _handle_enable(self, server: Optional[str]) -> str: - """Enable an MCP server.""" - if not server: - return "Error: --server is required" - - result = await self._registry.enable_server(server) - - if result.get("success"): - tools = result.get("tools", []) - if tools: - tool_list = "\n".join( - [f" - {t['name']}: {t['description']}" for t in tools[:10]] - ) - if len(tools) > 10: - tool_list += f"\n ... and {len(tools) - 10} more" - return f"โœ… Enabled '{server}' with {len(tools)} tools:\n{tool_list}" - return f"โœ… {result.get('message', f'Enabled {server}')}" - else: - error = result.get("error", "Unknown error") - if result.get("auth_url"): - return f"โŒ {error}\n\nGet your API key at: {result['auth_url']}\nThen run: proxy --action auth --server {server} --token " - return f"โŒ {error}" - - async def _handle_disable(self, server: Optional[str]) -> str: - """Disable an MCP server.""" - if not server: - return "Error: --server is required" - - result = await self._registry.disable_server(server) - - if result.get("success"): - return f"โœ… {result.get('message', f'Disabled {server}')}" - else: - return f"โŒ {result.get('error', 'Unknown error')}" - - def _handle_add(self, params: Dict[str, Any]) -> str: - """Add a custom MCP server.""" - server = params.get("server") - command = params.get("command") - - if not server: - return "Error: --server (name) is required" - if not command: - return "Error: --command is required" - - # Parse command - import shlex - - cmd_parts = shlex.split(command) - - config = MCPServerConfig( - name=server, - command=cmd_parts, - env=params.get("env", {}), - description=params.get("description", "Custom MCP server"), - lazy_load=True, - ) - - result = self._registry.add_server(config) - - if result.get("success"): - return f"โœ… Added server '{server}'. Use 'proxy --action enable --server {server}' to connect." - else: - return f"โŒ {result.get('error', 'Unknown error')}" - - def _handle_remove(self, server: Optional[str]) -> str: - """Remove a custom MCP server.""" - if not server: - return "Error: --server is required" - - result = self._registry.remove_server(server) - - if result.get("success"): - return f"โœ… {result.get('message', f'Removed {server}')}" - else: - return f"โŒ {result.get('error', 'Unknown error')}" - - async def _handle_call( - self, server: Optional[str], tool: Optional[str], args: Dict[str, Any] - ) -> str: - """Call a tool on a proxied server.""" - if not server: - return "Error: --server is required" - if not tool: - return "Error: --tool is required" - - try: - result = await self._registry.call_proxied_tool(server, tool, args) - return ( - json.dumps(result, indent=2) - if isinstance(result, (dict, list)) - else str(result) - ) - except Exception as e: - return f"โŒ Error calling {server}/{tool}: {e}" - - def _handle_auth(self, server: Optional[str], token: Optional[str]) -> str: - """Configure authentication for a server.""" - if not server: - return "Error: --server is required" - - config = self._registry.get_server_config(server) - if not config: - return f"Error: Server '{server}' not found" - - if not config.auth_required: - return f"Server '{server}' does not require authentication" - - env_var = config.auth_env_var - if not env_var: - return f"Server '{server}' has no auth configuration" - - if token: - # Set the environment variable for this session - os.environ[env_var] = token - - # Also save to credentials file - creds_file = self._registry.CONFIG_FILE.parent / "credentials.json" - creds_file.parent.mkdir(parents=True, exist_ok=True) - - creds = {} - if creds_file.exists(): - try: - with open(creds_file, "r") as f: - creds = json.load(f) - except Exception: - pass - - creds[server] = { - "env_var": env_var, - "token": token, # In production, this should be encrypted - } - - with open(creds_file, "w") as f: - json.dump(creds, f, indent=2) - - # Set file permissions - try: - import stat - - creds_file.chmod(stat.S_IRUSR | stat.S_IWUSR) # 0600 - except Exception: - pass - - return f"โœ… Configured authentication for '{server}'.\nNow run: proxy --action enable --server {server}" - else: - # Show auth instructions - return f"""To authenticate with '{server}': - -1. Get your API key: - {config.auth_url or "Check the service documentation"} - -2. Set it: - proxy --action auth --server {server} --token YOUR_API_KEY - - Or set environment variable: - export {env_var}=YOUR_API_KEY""" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-mcp/pyproject.toml b/pkg/hanzo-tools-mcp/pyproject.toml deleted file mode 100644 index 060ee82d2..000000000 --- a/pkg/hanzo-tools-mcp/pyproject.toml +++ /dev/null @@ -1,32 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-mcp" -version = "0.3.0" -description = "MCP management tools for Hanzo AI - server management, dynamic proxy for external MCPs" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "tools", "mcp", "ai", "proxy", "platform"] -dependencies = [ - "hanzo-tools>=0.3.0", - "mcp>=1.25.0", - "fastmcp>=2.14.1", - "pydantic>=2.12.5", - "httpx>=0.27.0", -] - -[project.optional-dependencies] -platform = ["httpx>=0.27.0"] - -[project.scripts] -hanzo-platform = "hanzo_tools.mcp_tools.platform_auth:cli" - -[project.entry-points."hanzo.tools"] -mcp_tools = "hanzo_tools.mcp_tools:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] diff --git a/pkg/hanzo-tools-mcp/tests/test_mcp_tools.py b/pkg/hanzo-tools-mcp/tests/test_mcp_tools.py deleted file mode 100644 index c3b636419..000000000 --- a/pkg/hanzo-tools-mcp/tests/test_mcp_tools.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Tests for hanzo-tools-mcp.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import mcp_tools - - assert mcp_tools is not None - - def test_import_tools(self): - from hanzo_tools.mcp_tools import TOOLS - - assert len(TOOLS) > 0 diff --git a/pkg/hanzo-tools-mcp/uv.lock b/pkg/hanzo-tools-mcp/uv.lock deleted file mode 100644 index a8634a01b..000000000 --- a/pkg/hanzo-tools-mcp/uv.lock +++ /dev/null @@ -1,1517 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" - -[[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.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, -] - -[[package]] -name = "attrs" -version = "25.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, -] - -[[package]] -name = "authlib" -version = "1.6.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/9b/b1661026ff24bc641b76b78c5222d614776b0c085bcfdac9bd15a1cb4b35/authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e", size = 164894, upload-time = "2025-12-12T08:01:41.464Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/51/321e821856452f7386c4e9df866f196720b1ad0c5ea1623ea7399969ae3b/authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd", size = 244005, upload-time = "2025-12-12T08:01:40.209Z" }, -] - -[[package]] -name = "beartype" -version = "0.22.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, -] - -[[package]] -name = "cachetools" -version = "6.2.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/39/91/d9ae9a66b01102a18cd16db0cf4cd54187ffe10f0865cc80071a4104fbb3/cachetools-6.2.6.tar.gz", hash = "sha256:16c33e1f276b9a9c0b49ab5782d901e3ad3de0dd6da9bf9bcd29ac5672f2f9e6", size = 32363, upload-time = "2026-01-27T20:32:59.956Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/45/f458fa2c388e79dd9d8b9b0c99f1d31b568f27388f2fdba7bb66bbc0c6ed/cachetools-6.2.6-py3-none-any.whl", hash = "sha256:8c9717235b3c651603fff0076db52d6acbfd1b338b8ed50256092f7ce9c85bda", size = 11668, upload-time = "2026-01-27T20:32:58.527Z" }, -] - -[[package]] -name = "certifi" -version = "2026.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, - { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, - { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, - { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, - { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, - { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, - { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, - { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, - { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, -] - -[[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 = "cryptography" -version = "46.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/19/f748958276519adf6a0c1e79e7b8860b4830dda55ccdf29f2719b5fc499c/cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59", size = 749301, upload-time = "2026-01-28T00:24:37.379Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/99/157aae7949a5f30d51fcb1a9851e8ebd5c74bf99b5285d8bb4b8b9ee641e/cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485", size = 7173686, upload-time = "2026-01-28T00:23:07.515Z" }, - { url = "https://files.pythonhosted.org/packages/87/91/874b8910903159043b5c6a123b7e79c4559ddd1896e38967567942635778/cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc", size = 4275871, upload-time = "2026-01-28T00:23:09.439Z" }, - { url = "https://files.pythonhosted.org/packages/c0/35/690e809be77896111f5b195ede56e4b4ed0435b428c2f2b6d35046fbb5e8/cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0", size = 4423124, upload-time = "2026-01-28T00:23:11.529Z" }, - { url = "https://files.pythonhosted.org/packages/1a/5b/a26407d4f79d61ca4bebaa9213feafdd8806dc69d3d290ce24996d3cfe43/cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa", size = 4277090, upload-time = "2026-01-28T00:23:13.123Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d8/4bb7aec442a9049827aa34cee1aa83803e528fa55da9a9d45d01d1bb933e/cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81", size = 4947652, upload-time = "2026-01-28T00:23:14.554Z" }, - { url = "https://files.pythonhosted.org/packages/2b/08/f83e2e0814248b844265802d081f2fac2f1cbe6cd258e72ba14ff006823a/cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255", size = 4455157, upload-time = "2026-01-28T00:23:16.443Z" }, - { url = "https://files.pythonhosted.org/packages/0a/05/19d849cf4096448779d2dcc9bb27d097457dac36f7273ffa875a93b5884c/cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e", size = 3981078, upload-time = "2026-01-28T00:23:17.838Z" }, - { url = "https://files.pythonhosted.org/packages/e6/89/f7bac81d66ba7cde867a743ea5b37537b32b5c633c473002b26a226f703f/cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c", size = 4276213, upload-time = "2026-01-28T00:23:19.257Z" }, - { url = "https://files.pythonhosted.org/packages/da/9f/7133e41f24edd827020ad21b068736e792bc68eecf66d93c924ad4719fb3/cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32", size = 4912190, upload-time = "2026-01-28T00:23:21.244Z" }, - { url = "https://files.pythonhosted.org/packages/a6/f7/6d43cbaddf6f65b24816e4af187d211f0bc536a29961f69faedc48501d8e/cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616", size = 4454641, upload-time = "2026-01-28T00:23:22.866Z" }, - { url = "https://files.pythonhosted.org/packages/9e/4f/ebd0473ad656a0ac912a16bd07db0f5d85184924e14fc88feecae2492834/cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0", size = 4405159, upload-time = "2026-01-28T00:23:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d1/f7/7923886f32dc47e27adeff8246e976d77258fd2aa3efdd1754e4e323bf49/cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0", size = 4666059, upload-time = "2026-01-28T00:23:26.766Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a7/0fca0fd3591dffc297278a61813d7f661a14243dd60f499a7a5b48acb52a/cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5", size = 3026378, upload-time = "2026-01-28T00:23:28.317Z" }, - { url = "https://files.pythonhosted.org/packages/2d/12/652c84b6f9873f0909374864a57b003686c642ea48c84d6c7e2c515e6da5/cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b", size = 3478614, upload-time = "2026-01-28T00:23:30.275Z" }, - { url = "https://files.pythonhosted.org/packages/b9/27/542b029f293a5cce59349d799d4d8484b3b1654a7b9a0585c266e974a488/cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908", size = 7116417, upload-time = "2026-01-28T00:23:31.958Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f5/559c25b77f40b6bf828eabaf988efb8b0e17b573545edb503368ca0a2a03/cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da", size = 4264508, upload-time = "2026-01-28T00:23:34.264Z" }, - { url = "https://files.pythonhosted.org/packages/49/a1/551fa162d33074b660dc35c9bc3616fefa21a0e8c1edd27b92559902e408/cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829", size = 4409080, upload-time = "2026-01-28T00:23:35.793Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/4d8d129a755f5d6df1bbee69ea2f35ebfa954fa1847690d1db2e8bca46a5/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2", size = 4270039, upload-time = "2026-01-28T00:23:37.263Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f5/ed3fcddd0a5e39321e595e144615399e47e7c153a1fb8c4862aec3151ff9/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085", size = 4926748, upload-time = "2026-01-28T00:23:38.884Z" }, - { url = "https://files.pythonhosted.org/packages/43/ae/9f03d5f0c0c00e85ecb34f06d3b79599f20630e4db91b8a6e56e8f83d410/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b", size = 4442307, upload-time = "2026-01-28T00:23:40.56Z" }, - { url = "https://files.pythonhosted.org/packages/8b/22/e0f9f2dae8040695103369cf2283ef9ac8abe4d51f68710bec2afd232609/cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd", size = 3959253, upload-time = "2026-01-28T00:23:42.827Z" }, - { url = "https://files.pythonhosted.org/packages/01/5b/6a43fcccc51dae4d101ac7d378a8724d1ba3de628a24e11bf2f4f43cba4d/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2", size = 4269372, upload-time = "2026-01-28T00:23:44.655Z" }, - { url = "https://files.pythonhosted.org/packages/17/b7/0f6b8c1dd0779df2b526e78978ff00462355e31c0a6f6cff8a3e99889c90/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e", size = 4891908, upload-time = "2026-01-28T00:23:46.48Z" }, - { url = "https://files.pythonhosted.org/packages/83/17/259409b8349aa10535358807a472c6a695cf84f106022268d31cea2b6c97/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f", size = 4441254, upload-time = "2026-01-28T00:23:48.403Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fe/e4a1b0c989b00cee5ffa0764401767e2d1cf59f45530963b894129fd5dce/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82", size = 4396520, upload-time = "2026-01-28T00:23:50.26Z" }, - { url = "https://files.pythonhosted.org/packages/b3/81/ba8fd9657d27076eb40d6a2f941b23429a3c3d2f56f5a921d6b936a27bc9/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c", size = 4651479, upload-time = "2026-01-28T00:23:51.674Z" }, - { url = "https://files.pythonhosted.org/packages/00/03/0de4ed43c71c31e4fe954edd50b9d28d658fef56555eba7641696370a8e2/cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061", size = 3001986, upload-time = "2026-01-28T00:23:53.485Z" }, - { url = "https://files.pythonhosted.org/packages/5c/70/81830b59df7682917d7a10f833c4dab2a5574cd664e86d18139f2b421329/cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7", size = 3468288, upload-time = "2026-01-28T00:23:55.09Z" }, - { url = "https://files.pythonhosted.org/packages/56/f7/f648fdbb61d0d45902d3f374217451385edc7e7768d1b03ff1d0e5ffc17b/cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab", size = 7169583, upload-time = "2026-01-28T00:23:56.558Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cc/8f3224cbb2a928de7298d6ed4790f5ebc48114e02bdc9559196bfb12435d/cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef", size = 4275419, upload-time = "2026-01-28T00:23:58.364Z" }, - { url = "https://files.pythonhosted.org/packages/17/43/4a18faa7a872d00e4264855134ba82d23546c850a70ff209e04ee200e76f/cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d", size = 4419058, upload-time = "2026-01-28T00:23:59.867Z" }, - { url = "https://files.pythonhosted.org/packages/ee/64/6651969409821d791ba12346a124f55e1b76f66a819254ae840a965d4b9c/cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973", size = 4278151, upload-time = "2026-01-28T00:24:01.731Z" }, - { url = "https://files.pythonhosted.org/packages/20/0b/a7fce65ee08c3c02f7a8310cc090a732344066b990ac63a9dfd0a655d321/cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4", size = 4939441, upload-time = "2026-01-28T00:24:03.175Z" }, - { url = "https://files.pythonhosted.org/packages/db/a7/20c5701e2cd3e1dfd7a19d2290c522a5f435dd30957d431dcb531d0f1413/cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af", size = 4451617, upload-time = "2026-01-28T00:24:05.403Z" }, - { url = "https://files.pythonhosted.org/packages/00/dc/3e16030ea9aa47b63af6524c354933b4fb0e352257c792c4deeb0edae367/cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263", size = 3977774, upload-time = "2026-01-28T00:24:06.851Z" }, - { url = "https://files.pythonhosted.org/packages/42/c8/ad93f14118252717b465880368721c963975ac4b941b7ef88f3c56bf2897/cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095", size = 4277008, upload-time = "2026-01-28T00:24:08.926Z" }, - { url = "https://files.pythonhosted.org/packages/00/cf/89c99698151c00a4631fbfcfcf459d308213ac29e321b0ff44ceeeac82f1/cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b", size = 4903339, upload-time = "2026-01-28T00:24:12.009Z" }, - { url = "https://files.pythonhosted.org/packages/03/c3/c90a2cb358de4ac9309b26acf49b2a100957e1ff5cc1e98e6c4996576710/cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019", size = 4451216, upload-time = "2026-01-28T00:24:13.975Z" }, - { url = "https://files.pythonhosted.org/packages/96/2c/8d7f4171388a10208671e181ca43cdc0e596d8259ebacbbcfbd16de593da/cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4", size = 4404299, upload-time = "2026-01-28T00:24:16.169Z" }, - { url = "https://files.pythonhosted.org/packages/e9/23/cbb2036e450980f65c6e0a173b73a56ff3bccd8998965dea5cc9ddd424a5/cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b", size = 4664837, upload-time = "2026-01-28T00:24:17.629Z" }, - { url = "https://files.pythonhosted.org/packages/0a/21/f7433d18fe6d5845329cbdc597e30caf983229c7a245bcf54afecc555938/cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc", size = 3009779, upload-time = "2026-01-28T00:24:20.198Z" }, - { url = "https://files.pythonhosted.org/packages/3a/6a/bd2e7caa2facffedf172a45c1a02e551e6d7d4828658c9a245516a598d94/cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976", size = 3466633, upload-time = "2026-01-28T00:24:21.851Z" }, -] - -[[package]] -name = "cyclopts" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "docstring-parser" }, - { name = "rich" }, - { name = "rich-rst" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d4/93/6085aa89c3fff78a5180987354538d72e43b0db27e66a959302d0c07821a/cyclopts-4.5.1.tar.gz", hash = "sha256:fadc45304763fd9f5d6033727f176898d17a1778e194436964661a005078a3dd", size = 162075, upload-time = "2026-01-25T15:23:54.07Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/7c/996760c30f1302704af57c66ff2d723f7d656d0d0b93563b5528a51484bb/cyclopts-4.5.1-py3-none-any.whl", hash = "sha256:0642c93601e554ca6b7b9abd81093847ea4448b2616280f2a0952416574e8c7a", size = 199807, upload-time = "2026-01-25T15:23:55.219Z" }, -] - -[[package]] -name = "diskcache" -version = "5.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, -] - -[[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.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, -] - -[[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.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "fakeredis" -version = "2.33.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "redis" }, - { name = "sortedcontainers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/f9/57464119936414d60697fcbd32f38909bb5688b616ae13de6e98384433e0/fakeredis-2.33.0.tar.gz", hash = "sha256:d7bc9a69d21df108a6451bbffee23b3eba432c21a654afc7ff2d295428ec5770", size = 175187, upload-time = "2025-12-16T19:45:52.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/78/a850fed8aeef96d4a99043c90b818b2ed5419cd5b24a4049fd7cfb9f1471/fakeredis-2.33.0-py3-none-any.whl", hash = "sha256:de535f3f9ccde1c56672ab2fdd6a8efbc4f2619fc2f1acc87b8737177d71c965", size = 119605, upload-time = "2025-12-16T19:45:51.08Z" }, -] - -[package.optional-dependencies] -lua = [ - { name = "lupa" }, -] - -[[package]] -name = "fastmcp" -version = "2.14.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "authlib" }, - { name = "cyclopts" }, - { name = "exceptiongroup" }, - { name = "httpx" }, - { name = "jsonref" }, - { name = "jsonschema-path" }, - { name = "mcp" }, - { name = "openapi-pydantic" }, - { name = "packaging" }, - { name = "platformdirs" }, - { name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] }, - { name = "pydantic", extra = ["email"] }, - { name = "pydocket" }, - { name = "pyperclip" }, - { name = "python-dotenv" }, - { name = "rich" }, - { name = "uvicorn" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/a9/a57d5e5629ebd4ef82b495a7f8e346ce29ef80cc86b15c8c40570701b94d/fastmcp-2.14.4.tar.gz", hash = "sha256:c01f19845c2adda0a70d59525c9193be64a6383014c8d40ce63345ac664053ff", size = 8302239, upload-time = "2026-01-22T17:29:37.024Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/41/c4d407e2218fd60d84acb6cc5131d28ff876afecf325e3fd9d27b8318581/fastmcp-2.14.4-py3-none-any.whl", hash = "sha256:5858cff5e4c8ea8107f9bca2609d71d6256e0fce74495912f6e51625e466c49a", size = 417788, upload-time = "2026-01-22T17:29:35.159Z" }, -] - -[[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 = "hanzo-tools" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/3e/2d94dc54e202bdb11f6e4597dd68eebc554d2b92fffb4f6918cdf3f91fe2/hanzo_tools-0.3.0.tar.gz", hash = "sha256:d00cb3212a707e22f9bb5a21f0f9eb34a74f22ff2b5f24e2f8b6321f9880e2fb", size = 10929, upload-time = "2025-12-27T18:56:38.843Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/07/6ebbcf371aafa5f2d171de2916ef92c73978b927b34a8863af53e1b1a80b/hanzo_tools-0.3.0-py3-none-any.whl", hash = "sha256:c7b0f6f7c3089f06329bc1aaca39fbce4b7108fdbd048e2bbc450a3aff9941f2", size = 11928, upload-time = "2025-12-27T18:56:37.528Z" }, -] - -[[package]] -name = "hanzo-tools-mcp" -version = "0.3.0" -source = { editable = "." } -dependencies = [ - { name = "fastmcp" }, - { name = "hanzo-tools" }, - { name = "httpx" }, - { name = "mcp" }, - { name = "pydantic" }, -] - -[package.optional-dependencies] -platform = [ - { name = "httpx" }, -] - -[package.metadata] -requires-dist = [ - { name = "fastmcp", specifier = ">=2.14.1" }, - { name = "hanzo-tools", specifier = ">=0.3.0" }, - { name = "httpx", specifier = ">=0.27.0" }, - { name = "httpx", marker = "extra == 'platform'", specifier = ">=0.27.0" }, - { name = "mcp", specifier = ">=1.25.0" }, - { name = "pydantic", specifier = ">=2.12.5" }, -] -provides-extras = ["platform"] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, -] - -[[package]] -name = "jaraco-context" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" }, -] - -[[package]] -name = "jaraco-functools" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, -] - -[[package]] -name = "jsonref" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[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.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "keyring" -version = "25.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, -] - -[[package]] -name = "lupa" -version = "2.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/1c/191c3e6ec6502e3dbe25a53e27f69a5daeac3e56de1f73c0138224171ead/lupa-2.6.tar.gz", hash = "sha256:9a770a6e89576be3447668d7ced312cd6fd41d3c13c2462c9dc2c2ab570e45d9", size = 7240282, upload-time = "2025-10-24T07:20:29.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/86/ce243390535c39d53ea17ccf0240815e6e457e413e40428a658ea4ee4b8d/lupa-2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47ce718817ef1cc0c40d87c3d5ae56a800d61af00fbc0fad1ca9be12df2f3b56", size = 951707, upload-time = "2025-10-24T07:18:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/cedea5e6cbeb54396fdcc55f6b741696f3f036d23cfaf986d50d680446da/lupa-2.6-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:7aba985b15b101495aa4b07112cdc08baa0c545390d560ad5cfde2e9e34f4d58", size = 1916703, upload-time = "2025-10-24T07:18:05.6Z" }, - { url = "https://files.pythonhosted.org/packages/24/be/3d6b5f9a8588c01a4d88129284c726017b2089f3a3fd3ba8bd977292fea0/lupa-2.6-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:b766f62f95b2739f2248977d29b0722e589dcf4f0ccfa827ccbd29f0148bd2e5", size = 985152, upload-time = "2025-10-24T07:18:08.561Z" }, - { url = "https://files.pythonhosted.org/packages/eb/23/9f9a05beee5d5dce9deca4cb07c91c40a90541fc0a8e09db4ee670da550f/lupa-2.6-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:00a934c23331f94cb51760097ebfab14b005d55a6b30a2b480e3c53dd2fa290d", size = 1159599, upload-time = "2025-10-24T07:18:10.346Z" }, - { url = "https://files.pythonhosted.org/packages/40/4e/e7c0583083db9d7f1fd023800a9767d8e4391e8330d56c2373d890ac971b/lupa-2.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21de9f38bd475303e34a042b7081aabdf50bd9bafd36ce4faea2f90fd9f15c31", size = 1038686, upload-time = "2025-10-24T07:18:12.112Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/5a4f7d959d4feba5e203ff0c31889e74d1ca3153122be4a46dca7d92bf7c/lupa-2.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf3bda96d3fc41237e964a69c23647d50d4e28421111360274d4799832c560e9", size = 2071956, upload-time = "2025-10-24T07:18:14.572Z" }, - { url = "https://files.pythonhosted.org/packages/92/34/2f4f13ca65d01169b1720176aedc4af17bc19ee834598c7292db232cb6dc/lupa-2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a76ead245da54801a81053794aa3975f213221f6542d14ec4b859ee2e7e0323", size = 1057199, upload-time = "2025-10-24T07:18:16.379Z" }, - { url = "https://files.pythonhosted.org/packages/35/2a/5f7d2eebec6993b0dcd428e0184ad71afb06a45ba13e717f6501bfed1da3/lupa-2.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8dd0861741caa20886ddbda0a121d8e52fb9b5bb153d82fa9bba796962bf30e8", size = 1173693, upload-time = "2025-10-24T07:18:18.153Z" }, - { url = "https://files.pythonhosted.org/packages/e4/29/089b4d2f8e34417349af3904bb40bec40b65c8731f45e3fd8d497ca573e5/lupa-2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:239e63948b0b23023f81d9a19a395e768ed3da6a299f84e7963b8f813f6e3f9c", size = 2164394, upload-time = "2025-10-24T07:18:20.403Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1b/79c17b23c921f81468a111cad843b076a17ef4b684c4a8dff32a7969c3f0/lupa-2.6-cp312-cp312-win32.whl", hash = "sha256:325894e1099499e7a6f9c351147661a2011887603c71086d36fe0f964d52d1ce", size = 1420647, upload-time = "2025-10-24T07:18:23.368Z" }, - { url = "https://files.pythonhosted.org/packages/b8/15/5121e68aad3584e26e1425a5c9a79cd898f8a152292059e128c206ee817c/lupa-2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c735a1ce8ee60edb0fe71d665f1e6b7c55c6021f1d340eb8c865952c602cd36f", size = 1688529, upload-time = "2025-10-24T07:18:25.523Z" }, - { url = "https://files.pythonhosted.org/packages/28/1d/21176b682ca5469001199d8b95fa1737e29957a3d185186e7a8b55345f2e/lupa-2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:663a6e58a0f60e7d212017d6678639ac8df0119bc13c2145029dcba084391310", size = 947232, upload-time = "2025-10-24T07:18:27.878Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4c/d327befb684660ca13cf79cd1f1d604331808f9f1b6fb6bf57832f8edf80/lupa-2.6-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:d1f5afda5c20b1f3217a80e9bc1b77037f8a6eb11612fd3ada19065303c8f380", size = 1908625, upload-time = "2025-10-24T07:18:29.944Z" }, - { url = "https://files.pythonhosted.org/packages/66/8e/ad22b0a19454dfd08662237a84c792d6d420d36b061f239e084f29d1a4f3/lupa-2.6-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:26f2b3c085fe76e9119e48c1013c1cccdc1f51585d456858290475aa38e7089e", size = 981057, upload-time = "2025-10-24T07:18:31.553Z" }, - { url = "https://files.pythonhosted.org/packages/5c/48/74859073ab276bd0566c719f9ca0108b0cfc1956ca0d68678d117d47d155/lupa-2.6-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:60d2f902c7b96fb8ab98493dcff315e7bb4d0b44dc9dd76eb37de575025d5685", size = 1156227, upload-time = "2025-10-24T07:18:33.981Z" }, - { url = "https://files.pythonhosted.org/packages/09/6c/0e9ded061916877253c2266074060eb71ed99fb21d73c8c114a76725bce2/lupa-2.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a02d25dee3a3250967c36590128d9220ae02f2eda166a24279da0b481519cbff", size = 1035752, upload-time = "2025-10-24T07:18:36.32Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ef/f8c32e454ef9f3fe909f6c7d57a39f950996c37a3deb7b391fec7903dab7/lupa-2.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eae1ee16b886b8914ff292dbefbf2f48abfbdee94b33a88d1d5475e02423203", size = 2069009, upload-time = "2025-10-24T07:18:38.072Z" }, - { url = "https://files.pythonhosted.org/packages/53/dc/15b80c226a5225815a890ee1c11f07968e0aba7a852df41e8ae6fe285063/lupa-2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0edd5073a4ee74ab36f74fe61450148e6044f3952b8d21248581f3c5d1a58be", size = 1056301, upload-time = "2025-10-24T07:18:40.165Z" }, - { url = "https://files.pythonhosted.org/packages/31/14/2086c1425c985acfb30997a67e90c39457122df41324d3c179d6ee2292c6/lupa-2.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c53ee9f22a8a17e7d4266ad48e86f43771951797042dd51d1494aaa4f5f3f0a", size = 1170673, upload-time = "2025-10-24T07:18:42.426Z" }, - { url = "https://files.pythonhosted.org/packages/10/e5/b216c054cf86576c0191bf9a9f05de6f7e8e07164897d95eea0078dca9b2/lupa-2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de7c0f157a9064a400d828789191a96da7f4ce889969a588b87ec80de9b14772", size = 2162227, upload-time = "2025-10-24T07:18:46.112Z" }, - { url = "https://files.pythonhosted.org/packages/59/2f/33ecb5bedf4f3bc297ceacb7f016ff951331d352f58e7e791589609ea306/lupa-2.6-cp313-cp313-win32.whl", hash = "sha256:ee9523941ae0a87b5b703417720c5d78f72d2f5bc23883a2ea80a949a3ed9e75", size = 1419558, upload-time = "2025-10-24T07:18:48.371Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b4/55e885834c847ea610e111d87b9ed4768f0afdaeebc00cd46810f25029f6/lupa-2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b1335a5835b0a25ebdbc75cf0bda195e54d133e4d994877ef025e218c2e59db9", size = 1683424, upload-time = "2025-10-24T07:18:50.976Z" }, - { url = "https://files.pythonhosted.org/packages/66/9d/d9427394e54d22a35d1139ef12e845fd700d4872a67a34db32516170b746/lupa-2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dcb6d0a3264873e1653bc188499f48c1fb4b41a779e315eba45256cfe7bc33c1", size = 953818, upload-time = "2025-10-24T07:18:53.378Z" }, - { url = "https://files.pythonhosted.org/packages/10/41/27bbe81953fb2f9ecfced5d9c99f85b37964cfaf6aa8453bb11283983721/lupa-2.6-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:a37e01f2128f8c36106726cb9d360bac087d58c54b4522b033cc5691c584db18", size = 1915850, upload-time = "2025-10-24T07:18:55.259Z" }, - { url = "https://files.pythonhosted.org/packages/a3/98/f9ff60db84a75ba8725506bbf448fb085bc77868a021998ed2a66d920568/lupa-2.6-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:458bd7e9ff3c150b245b0fcfbb9bd2593d1152ea7f0a7b91c1d185846da033fe", size = 982344, upload-time = "2025-10-24T07:18:57.05Z" }, - { url = "https://files.pythonhosted.org/packages/41/f7/f39e0f1c055c3b887d86b404aaf0ca197b5edfd235a8b81b45b25bac7fc3/lupa-2.6-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:052ee82cac5206a02df77119c325339acbc09f5ce66967f66a2e12a0f3211cad", size = 1156543, upload-time = "2025-10-24T07:18:59.251Z" }, - { url = "https://files.pythonhosted.org/packages/9e/9c/59e6cffa0d672d662ae17bd7ac8ecd2c89c9449dee499e3eb13ca9cd10d9/lupa-2.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96594eca3c87dd07938009e95e591e43d554c1dbd0385be03c100367141db5a8", size = 1047974, upload-time = "2025-10-24T07:19:01.449Z" }, - { url = "https://files.pythonhosted.org/packages/23/c6/a04e9cef7c052717fcb28fb63b3824802488f688391895b618e39be0f684/lupa-2.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8faddd9d198688c8884091173a088a8e920ecc96cda2ffed576a23574c4b3f6", size = 2073458, upload-time = "2025-10-24T07:19:03.369Z" }, - { url = "https://files.pythonhosted.org/packages/e6/10/824173d10f38b51fc77785228f01411b6ca28826ce27404c7c912e0e442c/lupa-2.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:daebb3a6b58095c917e76ba727ab37b27477fb926957c825205fbda431552134", size = 1067683, upload-time = "2025-10-24T07:19:06.2Z" }, - { url = "https://files.pythonhosted.org/packages/b6/dc/9692fbcf3c924d9c4ece2d8d2f724451ac2e09af0bd2a782db1cef34e799/lupa-2.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f3154e68972befe0f81564e37d8142b5d5d79931a18309226a04ec92487d4ea3", size = 1171892, upload-time = "2025-10-24T07:19:08.544Z" }, - { url = "https://files.pythonhosted.org/packages/84/ff/e318b628d4643c278c96ab3ddea07fc36b075a57383c837f5b11e537ba9d/lupa-2.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e4dadf77b9fedc0bfa53417cc28dc2278a26d4cbd95c29f8927ad4d8fe0a7ef9", size = 2166641, upload-time = "2025-10-24T07:19:10.485Z" }, - { url = "https://files.pythonhosted.org/packages/12/f7/a6f9ec2806cf2d50826980cdb4b3cffc7691dc6f95e13cc728846d5cb793/lupa-2.6-cp314-cp314-win32.whl", hash = "sha256:cb34169c6fa3bab3e8ac58ca21b8a7102f6a94b6a5d08d3636312f3f02fafd8f", size = 1456857, upload-time = "2025-10-24T07:19:37.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/de/df71896f25bdc18360fdfa3b802cd7d57d7fede41a0e9724a4625b412c85/lupa-2.6-cp314-cp314-win_amd64.whl", hash = "sha256:b74f944fe46c421e25d0f8692aef1e842192f6f7f68034201382ac440ef9ea67", size = 1731191, upload-time = "2025-10-24T07:19:40.281Z" }, - { url = "https://files.pythonhosted.org/packages/47/3c/a1f23b01c54669465f5f4c4083107d496fbe6fb45998771420e9aadcf145/lupa-2.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0e21b716408a21ab65723f8841cf7f2f37a844b7a965eeabb785e27fca4099cf", size = 999343, upload-time = "2025-10-24T07:19:12.519Z" }, - { url = "https://files.pythonhosted.org/packages/c5/6d/501994291cb640bfa2ccf7f554be4e6914afa21c4026bd01bff9ca8aac57/lupa-2.6-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:589db872a141bfff828340079bbdf3e9a31f2689f4ca0d88f97d9e8c2eae6142", size = 2000730, upload-time = "2025-10-24T07:19:14.869Z" }, - { url = "https://files.pythonhosted.org/packages/53/a5/457ffb4f3f20469956c2d4c4842a7675e884efc895b2f23d126d23e126cc/lupa-2.6-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:cd852a91a4a9d4dcbb9a58100f820a75a425703ec3e3f049055f60b8533b7953", size = 1021553, upload-time = "2025-10-24T07:19:17.123Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/36bb5a5d0960f2a5c7c700e0819abb76fd9bf9c1d8a66e5106416d6e9b14/lupa-2.6-cp314-cp314t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:0334753be028358922415ca97a64a3048e4ed155413fc4eaf87dd0a7e2752983", size = 1133275, upload-time = "2025-10-24T07:19:20.51Z" }, - { url = "https://files.pythonhosted.org/packages/19/86/202ff4429f663013f37d2229f6176ca9f83678a50257d70f61a0a97281bf/lupa-2.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:661d895cd38c87658a34780fac54a690ec036ead743e41b74c3fb81a9e65a6aa", size = 1038441, upload-time = "2025-10-24T07:19:22.509Z" }, - { url = "https://files.pythonhosted.org/packages/a7/42/d8125f8e420714e5b52e9c08d88b5329dfb02dcca731b4f21faaee6cc5b5/lupa-2.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aa58454ccc13878cc177c62529a2056be734da16369e451987ff92784994ca7", size = 2058324, upload-time = "2025-10-24T07:19:24.979Z" }, - { url = "https://files.pythonhosted.org/packages/2b/2c/47bf8b84059876e877a339717ddb595a4a7b0e8740bacae78ba527562e1c/lupa-2.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1425017264e470c98022bba8cff5bd46d054a827f5df6b80274f9cc71dafd24f", size = 1060250, upload-time = "2025-10-24T07:19:27.262Z" }, - { url = "https://files.pythonhosted.org/packages/c2/06/d88add2b6406ca1bdec99d11a429222837ca6d03bea42ca75afa169a78cb/lupa-2.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:224af0532d216e3105f0a127410f12320f7c5f1aa0300bdf9646b8d9afb0048c", size = 1151126, upload-time = "2025-10-24T07:19:29.522Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a0/89e6a024c3b4485b89ef86881c9d55e097e7cb0bdb74efb746f2fa6a9a76/lupa-2.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9abb98d5a8fd27c8285302e82199f0e56e463066f88f619d6594a450bf269d80", size = 2153693, upload-time = "2025-10-24T07:19:31.379Z" }, - { url = "https://files.pythonhosted.org/packages/b6/36/a0f007dc58fc1bbf51fb85dcc82fcb1f21b8c4261361de7dab0e3d8521ef/lupa-2.6-cp314-cp314t-win32.whl", hash = "sha256:1849efeba7a8f6fb8aa2c13790bee988fd242ae404bd459509640eeea3d1e291", size = 1590104, upload-time = "2025-10-24T07:19:33.514Z" }, - { url = "https://files.pythonhosted.org/packages/7d/5e/db903ce9cf82c48d6b91bf6d63ae4c8d0d17958939a4e04ba6b9f38b8643/lupa-2.6-cp314-cp314t-win_amd64.whl", hash = "sha256:fc1498d1a4fc028bc521c26d0fad4ca00ed63b952e32fb95949bda76a04bad52", size = 1913818, upload-time = "2025-10-24T07:19:36.039Z" }, -] - -[[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 = "mcp" -version = "1.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyjwt", extra = ["crypto"] }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, -] - -[[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 = "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 = "opentelemetry-api" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, -] - -[[package]] -name = "opentelemetry-exporter-prometheus" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-sdk" }, - { name = "prometheus-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/14/39/7dafa6fff210737267bed35a8855b6ac7399b9e582b8cf1f25f842517012/opentelemetry_exporter_prometheus-0.60b1.tar.gz", hash = "sha256:a4011b46906323f71724649d301b4dc188aaa068852e814f4df38cc76eac616b", size = 14976, upload-time = "2025-12-11T13:32:42.944Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/0d/4be6bf5477a3eb3d917d2f17d3c0b6720cd6cb97898444a61d43cc983f5c/opentelemetry_exporter_prometheus-0.60b1-py3-none-any.whl", hash = "sha256:49f59178de4f4590e3cef0b8b95cf6e071aae70e1f060566df5546fad773b8fd", size = 13019, upload-time = "2025-12-11T13:32:23.974Z" }, -] - -[[package]] -name = "opentelemetry-instrumentation" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "packaging" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/0f/7e6b713ac117c1f5e4e3300748af699b9902a2e5e34c9cf443dde25a01fa/opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a", size = 31706, upload-time = "2025-12-11T13:36:42.515Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096, upload-time = "2025-12-11T13:35:33.067Z" }, -] - -[[package]] -name = "opentelemetry-sdk" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, -] - -[[package]] -name = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[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 = "pathvalidate" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262, upload-time = "2025-06-15T09:07:20.736Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305, upload-time = "2025-06-15T09:07:19.117Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, -] - -[[package]] -name = "prometheus-client" -version = "0.24.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616, upload-time = "2026-01-14T15:26:26.965Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057, upload-time = "2026-01-14T15:26:24.42Z" }, -] - -[[package]] -name = "py-key-value-aio" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "py-key-value-shared" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/ce/3136b771dddf5ac905cc193b461eb67967cf3979688c6696e1f2cdcde7ea/py_key_value_aio-0.3.0.tar.gz", hash = "sha256:858e852fcf6d696d231266da66042d3355a7f9871650415feef9fca7a6cd4155", size = 50801, upload-time = "2025-11-17T16:50:04.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/10/72f6f213b8f0bce36eff21fda0a13271834e9eeff7f9609b01afdc253c79/py_key_value_aio-0.3.0-py3-none-any.whl", hash = "sha256:1c781915766078bfd608daa769fefb97e65d1d73746a3dfb640460e322071b64", size = 96342, upload-time = "2025-11-17T16:50:03.801Z" }, -] - -[package.optional-dependencies] -disk = [ - { name = "diskcache" }, - { name = "pathvalidate" }, -] -keyring = [ - { name = "keyring" }, -] -memory = [ - { name = "cachetools" }, -] -redis = [ - { name = "redis" }, -] - -[[package]] -name = "py-key-value-shared" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/e4/1971dfc4620a3a15b4579fe99e024f5edd6e0967a71154771a059daff4db/py_key_value_shared-0.3.0.tar.gz", hash = "sha256:8fdd786cf96c3e900102945f92aa1473138ebe960ef49da1c833790160c28a4b", size = 11666, upload-time = "2025-11-17T16:50:06.849Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e4/b8b0a03ece72f47dce2307d36e1c34725b7223d209fc679315ffe6a4e2c3/py_key_value_shared-0.3.0-py3-none-any.whl", hash = "sha256:5b0efba7ebca08bb158b1e93afc2f07d30b8f40c2fc12ce24a4c0d84f42f9298", size = 19560, upload-time = "2025-11-17T16:50:05.954Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[package.optional-dependencies] -email = [ - { name = "email-validator" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, -] - -[[package]] -name = "pydantic-settings" -version = "2.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, -] - -[[package]] -name = "pydocket" -version = "0.16.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cloudpickle" }, - { name = "fakeredis", extra = ["lua"] }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-prometheus" }, - { name = "opentelemetry-instrumentation" }, - { name = "prometheus-client" }, - { name = "py-key-value-aio", extra = ["memory", "redis"] }, - { name = "python-json-logger" }, - { name = "redis" }, - { name = "rich" }, - { name = "typer" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/00/26befe5f58df7cd1aeda4a8d10bc7d1908ffd86b80fd995e57a2a7b3f7bd/pydocket-0.16.6.tar.gz", hash = "sha256:b96c96ad7692827214ed4ff25fcf941ec38371314db5dcc1ae792b3e9d3a0294", size = 299054, upload-time = "2026-01-09T22:09:15.405Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/3f/7483e5a6dc6326b6e0c640619b5c5bd1d6e3c20e54d58f5fb86267cef00e/pydocket-0.16.6-py3-none-any.whl", hash = "sha256:683d21e2e846aa5106274e7d59210331b242d7fb0dce5b08d3b82065663ed183", size = 67697, upload-time = "2026-01-09T22:09:13.436Z" }, -] - -[[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 = "pyjwt" -version = "2.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, -] - -[package.optional-dependencies] -crypto = [ - { name = "cryptography" }, -] - -[[package]] -name = "pyperclip" -version = "1.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, -] - -[[package]] -name = "python-json-logger" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, -] - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { 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 = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "redis" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/c8/983d5c6579a411d8a99bc5823cc5712768859b5ce2c8afe1a65b37832c81/redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c", size = 4796669, upload-time = "2025-11-19T15:54:39.961Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/f0/8956f8a86b20d7bb9d6ac0187cf4cd54d8065bc9a1a09eb8011d4d326596/redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b", size = 354159, upload-time = "2025-11-19T15:54:38.064Z" }, -] - -[[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 = "rich" -version = "14.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/84/4831f881aa6ff3c976f6d6809b58cdfa350593ffc0dc3c58f5f6586780fb/rich-14.3.1.tar.gz", hash = "sha256:b8c5f568a3a749f9290ec6bddedf835cec33696bfc1e48bcfecb276c7386e4b8", size = 230125, upload-time = "2026-01-24T21:40:44.847Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/2a/a1810c8627b9ec8c57ec5ec325d306701ae7be50235e8fd81266e002a3cc/rich-14.3.1-py3-none-any.whl", hash = "sha256:da750b1aebbff0b372557426fb3f35ba56de8ef954b3190315eb64076d6fb54e", size = 309952, upload-time = "2026-01-24T21:40:42.969Z" }, -] - -[[package]] -name = "rich-rst" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, -] - -[[package]] -name = "secretstorage" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[package]] -name = "sortedcontainers" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, -] - -[[package]] -name = "sse-starlette" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "starlette" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/00d280c03ffd39aaee0e86ec81e2d3b9253036a0f93f51d10503adef0e65/sse_starlette-3.2.0.tar.gz", hash = "sha256:8127594edfb51abe44eac9c49e59b0b01f1039d0c7461c6fd91d4e03b70da422", size = 27253, upload-time = "2026-01-17T13:11:05.62Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/7f/832f015020844a8b8f7a9cbc103dd76ba8e3875004c41e08440ea3a2b41a/sse_starlette-3.2.0-py3-none-any.whl", hash = "sha256:5876954bd51920fc2cd51baee47a080eb88a37b5b784e615abb0b283f801cdbf", size = 12763, upload-time = "2026-01-17T13:11:03.775Z" }, -] - -[[package]] -name = "starlette" -version = "0.52.1" -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/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, -] - -[[package]] -name = "typer" -version = "0.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.40.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, -] - -[[package]] -name = "websockets" -version = "16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, - { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, - { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, - { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, - { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, - { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, - { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, - { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, - { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, - { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, -] - -[[package]] -name = "wrapt" -version = "1.17.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998, upload-time = "2025-08-12T05:51:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020, upload-time = "2025-08-12T05:51:35.906Z" }, - { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098, upload-time = "2025-08-12T05:51:57.474Z" }, - { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036, upload-time = "2025-08-12T05:52:34.784Z" }, - { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156, upload-time = "2025-08-12T05:52:13.599Z" }, - { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102, upload-time = "2025-08-12T05:52:14.56Z" }, - { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732, upload-time = "2025-08-12T05:52:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705, upload-time = "2025-08-12T05:53:07.123Z" }, - { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877, upload-time = "2025-08-12T05:53:05.436Z" }, - { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885, upload-time = "2025-08-12T05:52:54.367Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, - { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, - { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, - { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, - { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, - { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, - { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, - { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, - { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, - { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, - { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, - { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, - { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, - { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, - { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, - { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, - { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, - { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, - { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, -] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] diff --git a/pkg/hanzo-tools-memory/README.md b/pkg/hanzo-tools-memory/README.md deleted file mode 100644 index af987454d..000000000 --- a/pkg/hanzo-tools-memory/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# hanzo-tools-memory - -Memory and knowledge management tools for Hanzo MCP. - -## Installation - -```bash -pip install hanzo-tools-memory -``` - -## Tools - -### memory - Memory Management -Single tool surface with action-based operations. - -**Recall memories:** -```python -memory(action="recall", query="project architecture") -memory(action="recall", queries=["user preferences", "coding standards"]) -``` - -**Create memories:** -```python -memory(action="create", content="User prefers dark mode") -``` - -**Update memories:** -```python -memory(action="update", id="mem_123", content="Updated fact") -``` - -**Delete memories:** -```python -memory(action="delete", ids=["mem_123", "mem_456"]) -``` - -**Knowledge bases:** -```python -memory(action="facts", query="API endpoints", kb="api_docs") -memory(action="store", facts=["Rate limit: 100/hour"], kb="api_docs") -memory(action="kb", kb_action="list") # List knowledge bases -``` - -**Summarize:** -```python -memory( - action="summarize", - content="Long discussion about API design...", - tags=["api", "design"] -) -``` - -**Backend selection (optional):** -```python -memory(action="recall", query="project auth flow", backend="auto") # local-first (default) -memory(action="recall", query="project auth flow", backend="local") # markdown only -memory(action="recall", query="project auth flow", backend="cloud") # cloud/vector only -memory(action="recall", query="project auth flow", backend="hybrid") # local + cloud -``` - -## Scopes - -- `session` - Current session only -- `project` - Project-specific (default) -- `global` - Across all projects - -## License - -MIT diff --git a/pkg/hanzo-tools-memory/hanzo_tools/__init__.py b/pkg/hanzo-tools-memory/hanzo_tools/__init__.py deleted file mode 100644 index 004279ba9..000000000 --- a/pkg/hanzo-tools-memory/hanzo_tools/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Hanzo tools namespace package.""" - -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-memory/hanzo_tools/memory/__init__.py b/pkg/hanzo-tools-memory/hanzo_tools/memory/__init__.py deleted file mode 100644 index 014416d9d..000000000 --- a/pkg/hanzo-tools-memory/hanzo_tools/memory/__init__.py +++ /dev/null @@ -1,44 +0,0 @@ -"""Memory tools for Hanzo MCP. - -External MCP surface is intentionally one tool: `memory`. -Default backend is local markdown context + session persistence, with optional -hanzo-memory backends for cloud/vector/graph storage. -""" - -from mcp.server import FastMCP - -from hanzo_tools.core import BaseTool, ToolRegistry -from hanzo_tools.memory.memory_tool import MemoryTool - -__all__ = [ - "MemoryTool", - # Registration helpers - "get_memory_tools", - "register_memory_tools", - "TOOLS", -] - - -def get_memory_tools( - user_id: str = "default", - project_id: str = "default", - **kwargs, -) -> list[BaseTool]: - """Create memory tool instances (single-tool surface).""" - return [MemoryTool(user_id=user_id, project_id=project_id, **kwargs)] - - -def register_memory_tools( - mcp_server: FastMCP, - user_id: str = "default", - project_id: str = "default", - **kwargs, -) -> list[BaseTool]: - """Register memory tools with the MCP server.""" - tools = get_memory_tools(user_id=user_id, project_id=project_id, **kwargs) - ToolRegistry.register_tools(mcp_server, tools) - return tools - - -# TOOLS list for entry point discovery โ€” single `memory` tool surface -TOOLS = [MemoryTool] diff --git a/pkg/hanzo-tools-memory/hanzo_tools/memory/markdown_memory.py b/pkg/hanzo-tools-memory/hanzo_tools/memory/markdown_memory.py deleted file mode 100644 index 36ef7a51c..000000000 --- a/pkg/hanzo-tools-memory/hanzo_tools/memory/markdown_memory.py +++ /dev/null @@ -1,344 +0,0 @@ -"""Lightweight markdown-based memory backend. - -No backend required โ€” reads from local .md files (MEMORY.md, LLM.md, CLAUDE.md, etc.) -and provides simple in-memory storage for the current session. - -This is the default backend when hanzo-memory is not installed. -""" - -from __future__ import annotations - -import re -from uuid import uuid4 -from typing import Dict, List, Optional -from pathlib import Path -from datetime import datetime -from dataclasses import field, dataclass - -# Priority instruction files (highest to lowest precedence within each directory) -PRIMARY_CONTEXT_FILES = [ - "AGENTS.md", - "CLAUDE.md", - "GEMINI.md", - "LLM.md", -] - -# Additional markdown context files -ADDITIONAL_CONTEXT_FILES = [ - "MEMORY.md", - "QWEN.md", - "AI.md", - "CONTEXT.md", - "INSTRUCTIONS.md", -] - -# Editor/assistant rule files -STATIC_RULE_FILES = [ - ".cursorrules", - ".github/copilot-instructions.md", -] - -RULE_GLOBS = [ - ".cursor/rules/*.md", - ".cursor/rules/*.mdc", - ".github/instructions/*.instructions.md", -] - -# Per-scope memory files we write to -SCOPE_MEMORY_FILES = { - "global": Path.home() / ".claude" / "MEMORY.md", - "project": None, # Determined by CWD - "session": None, # In-memory only -} - - -@dataclass -class MarkdownMemory: - """Simple memory entry.""" - - memory_id: str - content: str - scope: str - created_at: str - source: str = "user" # "user" or "file" - tags: List[str] = field(default_factory=list) - - -@dataclass -class MarkdownFact: - """Simple fact entry.""" - - fact_id: str - statement: str - kb_name: str - scope: str - created_at: str - - -class MarkdownMemoryBackend: - """Lightweight memory backend using local markdown files. - - - Reads local-first context from AGENTS.md, CLAUDE.md, GEMINI.md, LLM.md, etc. - - Detects Cursor/Copilot rule files (.cursorrules, .cursor/rules/*, .github/copilot-instructions.md) - - Stores new memories in MEMORY.md files (per scope) - - Session memories are in-process only - - No embedding search โ€” uses simple keyword matching - """ - - def __init__(self) -> None: - self._session_memories: List[MarkdownMemory] = [] - self._session_facts: List[MarkdownFact] = [] - self._file_memories: List[MarkdownMemory] = [] - self._loaded = False - - def _ensure_loaded(self) -> None: - """Lazily load memories from markdown files.""" - if self._loaded: - return - self._loaded = True - self._file_memories = [] - - # Scan current directory chain for context files (local-first) - scan_dirs = self._get_scan_dirs() - for d in scan_dirs: - for fpath in self._iter_context_files(d): - try: - content = fpath.read_text(encoding="utf-8", errors="ignore") - # Split into sections/chunks - chunks = self._chunk_markdown(content) - for chunk in chunks: - if chunk.strip(): - self._file_memories.append( - MarkdownMemory( - memory_id=str(uuid4()), - content=chunk.strip(), - scope="global", - created_at=datetime.now().isoformat(), - source=str(fpath), - ) - ) - except Exception: - pass - - def _iter_context_files(self, directory: Path) -> List[Path]: - """Collect context and rule files in deterministic priority order.""" - ordered: List[Path] = [] - seen: set[Path] = set() - - def add_if_valid(path: Path) -> None: - if path in seen: - return - if not path.exists() or not path.is_file(): - return - try: - if path.stat().st_size <= 0: - return - except OSError: - return - seen.add(path) - ordered.append(path) - - for fname in PRIMARY_CONTEXT_FILES: - add_if_valid(directory / fname) - - for fname in ADDITIONAL_CONTEXT_FILES: - add_if_valid(directory / fname) - - for fname in STATIC_RULE_FILES: - add_if_valid(directory / fname) - - for pattern in RULE_GLOBS: - for rule_path in sorted(directory.glob(pattern)): - add_if_valid(rule_path) - - return ordered - - def _get_scan_dirs(self) -> List[Path]: - """Get directories to scan for context files.""" - dirs: List[Path] = [] - cwd = Path.cwd() - - # Walk up from CWD (max 4 levels) - current = cwd - for _ in range(4): - dirs.append(current) - if current.parent == current: - break - current = current.parent - - # Add home/.claude - home_claude = Path.home() / ".claude" - if home_claude.exists(): - dirs.append(home_claude) - - return dirs - - def _chunk_markdown(self, content: str, max_chunk: int = 800) -> List[str]: - """Split markdown into meaningful chunks by heading.""" - # Split by ## headings first, then by size - sections = re.split(r"\n(?=#{1,3} )", content) - chunks: List[str] = [] - for section in sections: - if len(section) <= max_chunk: - chunks.append(section) - else: - # Split large sections into paragraphs - paras = section.split("\n\n") - current = "" - for para in paras: - if len(current) + len(para) > max_chunk and current: - chunks.append(current) - current = para - else: - current = (current + "\n\n" + para).strip() - if current: - chunks.append(current) - return chunks - - def _keyword_score(self, text: str, query: str) -> float: - """Simple keyword relevance score (0-1).""" - query_words = set(query.lower().split()) - text_lower = text.lower() - matches = sum(1 for w in query_words if w in text_lower and len(w) > 2) - return matches / max(len(query_words), 1) - - def search_memories( - self, - queries: List[str], - limit: int = 10, - scope: Optional[str] = None, - ) -> List[MarkdownMemory]: - """Search memories by keyword relevance.""" - self._ensure_loaded() - - all_memories = self._file_memories + self._session_memories - if scope and scope != "global": - # Filter by scope - all_memories = [m for m in all_memories if m.scope == scope] - - # Score and rank - scored: List[tuple[float, MarkdownMemory]] = [] - for memory in all_memories: - best_score = max( - self._keyword_score(memory.content, q) for q in queries - ) - if best_score > 0: - scored.append((best_score, memory)) - - scored.sort(key=lambda x: x[0], reverse=True) - return [m for _, m in scored[:limit]] - - def add_memory(self, content: str, scope: str = "project") -> MarkdownMemory: - """Add a new memory (session or persisted).""" - memory = MarkdownMemory( - memory_id=str(uuid4()), - content=content, - scope=scope, - created_at=datetime.now().isoformat(), - source="user", - ) - self._session_memories.append(memory) - - # Persist to appropriate MEMORY.md - if scope != "session": - self._append_to_memory_file(content, scope) - - return memory - - def _get_memory_file(self, scope: str) -> Optional[Path]: - """Get the path to the appropriate MEMORY.md file.""" - if scope == "global": - path = Path.home() / ".claude" / "MEMORY.md" - path.parent.mkdir(parents=True, exist_ok=True) - return path - else: # project - # Find project root (has .git, package.json, pyproject.toml, etc.) - cwd = Path.cwd() - root_markers = [".git", "package.json", "pyproject.toml", "go.mod"] - current = cwd - for _ in range(4): - if any((current / m).exists() for m in root_markers): - return current / "MEMORY.md" - if current.parent == current: - break - current = current.parent - return cwd / "MEMORY.md" - - def _append_to_memory_file(self, content: str, scope: str) -> None: - """Append a memory entry to the appropriate MEMORY.md file.""" - path = self._get_memory_file(scope) - if path is None: - return - try: - timestamp = datetime.now().strftime("%Y-%m-%d %H:%M") - entry = f"\n- [{timestamp}] {content}\n" - with open(path, "a", encoding="utf-8") as f: - f.write(entry) - except Exception: - pass - - def update_memory(self, memory_id: str, new_content: str) -> Optional[MarkdownMemory]: - """Update a session memory by ID.""" - for m in self._session_memories: - if m.memory_id == memory_id: - m.content = new_content - return m - return None - - def delete_memory(self, memory_id: str) -> bool: - """Remove a session memory.""" - before = len(self._session_memories) - self._session_memories = [m for m in self._session_memories if m.memory_id != memory_id] - return len(self._session_memories) < before - - # --- Facts API (session-only, lightweight) --- - - def recall_facts( - self, - queries: List[str], - kb_name: Optional[str] = None, - limit: int = 10, - ) -> List[MarkdownFact]: - """Search facts by keyword.""" - facts = self._session_facts - if kb_name: - facts = [f for f in facts if f.kb_name == kb_name] - - scored: List[tuple[float, MarkdownFact]] = [] - for fact in facts: - best = max(self._keyword_score(fact.statement, q) for q in queries) - if best > 0: - scored.append((best, fact)) - - scored.sort(key=lambda x: x[0], reverse=True) - return [f for _, f in scored[:limit]] - - def store_fact(self, statement: str, kb_name: str = "general", scope: str = "project") -> MarkdownFact: - """Store a fact.""" - fact = MarkdownFact( - fact_id=str(uuid4()), - statement=statement, - kb_name=kb_name, - scope=scope, - created_at=datetime.now().isoformat(), - ) - self._session_facts.append(fact) - return fact - - def delete_fact(self, fact_id: str) -> bool: - """Remove a fact.""" - before = len(self._session_facts) - self._session_facts = [f for f in self._session_facts if f.fact_id != fact_id] - return len(self._session_facts) < before - - -# Singleton -_backend: Optional[MarkdownMemoryBackend] = None - - -def get_markdown_backend() -> MarkdownMemoryBackend: - """Get the singleton markdown memory backend.""" - global _backend - if _backend is None: - _backend = MarkdownMemoryBackend() - return _backend diff --git a/pkg/hanzo-tools-memory/hanzo_tools/memory/memory_tool.py b/pkg/hanzo-tools-memory/hanzo_tools/memory/memory_tool.py deleted file mode 100644 index 3338e8bce..000000000 --- a/pkg/hanzo-tools-memory/hanzo_tools/memory/memory_tool.py +++ /dev/null @@ -1,759 +0,0 @@ -"""Memory tool for Hanzo MCP. - -Single `memory` tool with action parameter for all memory operations. -""" - -from typing import ( - TYPE_CHECKING, - Any, - Dict, - List, - Literal, - Optional, - Annotated, - final, - override, -) - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context - -if TYPE_CHECKING: - from hanzo_memory.services.memory import MemoryService - -# Lazy loading -MEMORY_AVAILABLE: Optional[bool] = None -_memory_service: Optional["MemoryService"] = None - - -def _check_memory_available() -> bool: - """Check if hanzo-memory is available.""" - global MEMORY_AVAILABLE - if MEMORY_AVAILABLE is None: - try: - import hanzo_memory # noqa: F401 - - MEMORY_AVAILABLE = True - except ImportError: - MEMORY_AVAILABLE = False - return MEMORY_AVAILABLE - - -def _get_lazy_memory_service() -> "MemoryService": - """Get memory service lazily. Returns None if hanzo-memory is not available.""" - global _memory_service - if _memory_service is None: - if not _check_memory_available(): - return None # type: ignore[return-value] - from hanzo_memory.services.memory import get_memory_service - - _memory_service = get_memory_service() - return _memory_service - - -Action = Annotated[ - Literal[ - "recall", # Search memories - "create", # Store new memories - "update", # Update existing memories - "delete", # Delete memories - "manage", # Atomic create/update/delete - "facts", # Recall facts from knowledge bases - "store", # Store facts in knowledge bases - "summarize", # Summarize and store - "kb", # Manage knowledge bases - "list", # List memories/knowledge bases - "search", # Search memories (alias for recall) - "stats", # Memory statistics - "clear", # Clear all memories - "export", # Export memories as JSON - "import", # Import memories from JSON - "merge", # Merge duplicate memories - "tag", # Add tags to memory - "untag", # Remove tags from memory - "namespaces", # List namespaces/scopes - "history", # Memory change history - ], - Field(description="Memory action to perform"), -] - -BackendMode = Annotated[ - Literal["auto", "local", "cloud", "hybrid"], - Field( - description=( - "Backend mode: auto/local-first (default), local-only, " - "cloud-only, or hybrid(local+cloud)" - ) - ), -] - - -@final -class MemoryTool(BaseTool): - """Memory tool for all memory operations. - - Consolidates legacy memory/facts/knowledge tool variants behind one surface. - """ - - name = "memory" - - def __init__(self, user_id: str = "default", project_id: str = "default", **kwargs): - """Initialize memory tool.""" - self.user_id = user_id - self.project_id = project_id - - @property - @override - def description(self) -> str: - return """Memory management tool. - -Actions: -- recall: Search and retrieve memories -- create: Store new memories -- update: Update existing memories -- delete: Remove memories -- manage: Atomic operations (create + update + delete) -- facts: Recall facts from knowledge bases -- store: Store facts in knowledge bases -- summarize: Summarize information and store -- kb: Create/list/manage knowledge bases -- list: List all memories or knowledge bases -- backend: Defaults to local-first markdown (`auto`), with optional cloud/vector backend - -Examples: - memory recall --query "user preferences" - memory create --content "User prefers dark mode" --tags ["preferences"] - memory update --id mem_123 --content "Updated content" - memory delete --id mem_123 - memory facts --query "coding standards" --kb "project_docs" - memory store --fact "Python uses 4-space indentation" --kb "coding" - memory kb --action create --name "project_notes" - memory list --type memories -""" - - @override - @auto_timeout("memory") - async def call( - self, - ctx: MCPContext, - action: str = "list", - # Common params - backend: str = "auto", - query: Optional[str] = None, - queries: Optional[List[str]] = None, - content: Optional[str] = None, - id: Optional[str] = None, - ids: Optional[List[str]] = None, - tags: Optional[List[str]] = None, - metadata: Optional[Dict[str, Any]] = None, - scope: str = "session", - limit: int = 10, - # Knowledge base params - kb: Optional[str] = None, - fact: Optional[str] = None, - facts: Optional[List[str]] = None, - # Manage params - create_list: Optional[List[Dict]] = None, - update_list: Optional[List[Dict]] = None, - delete_ids: Optional[List[str]] = None, - # KB management - kb_action: Optional[str] = None, - name: Optional[str] = None, - # List params - list_type: str = "memories", - **kwargs, - ) -> str: - """Execute memory operation.""" - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - try: - if action == "recall": - return await self._recall( - queries or ([query] if query else []), scope, limit, backend - ) - elif action == "create": - return await self._create(content, tags, metadata, scope, backend) - elif action == "update": - return await self._update(id, content, tags, metadata) - elif action == "delete": - return await self._delete(ids or ([id] if id else [])) - elif action == "manage": - return await self._manage(create_list, update_list, delete_ids) - elif action == "facts": - return await self._facts_lookup( - queries or ([query] if query else []), kb, limit - ) - elif action == "store": - return await self._facts_store( - facts or ([fact] if fact else []), kb, metadata - ) - elif action == "summarize": - return await self._summarize(content, tags, scope) - elif action == "kb": - return await self._manage_kb(kb_action or "list", name) - elif action == "list": - return await self._list(list_type, scope, limit) - elif action == "search": - return await self._recall( - queries or ([query] if query else []), scope, limit, backend - ) - elif action == "stats": - return await self._stats(scope) - elif action == "clear": - return await self._clear(scope) - elif action == "export": - return await self._export(scope, limit) - elif action == "import": - return await self._import(content) - elif action == "merge": - return await self._merge(ids or []) - elif action == "tag": - return await self._tag(id, tags or []) - elif action == "untag": - return await self._untag(id, tags or []) - elif action == "namespaces": - return await self._namespaces() - elif action == "history": - return await self._history(id, limit) - else: - return f"Unknown action: {action}. Use: recall, create, update, delete, manage, facts, store, summarize, kb, list, search, stats, clear, export, import, merge, tag, untag, namespaces, history" - - except ImportError as e: - return f"Memory service not available: {e}" - except Exception as e: - return f"Memory operation failed: {e}" - - async def _recall( - self, - queries: List[str], - scope: str, - limit: int, - backend_mode: str, - ) -> str: - """Search and retrieve memories.""" - if not queries: - return "Error: query or queries required for recall" - - from hanzo_tools.memory.markdown_memory import get_markdown_backend - - mode = (backend_mode or "auto").lower() - use_local = mode in ("auto", "local", "hybrid") - use_cloud = mode in ("auto", "cloud", "hybrid") and _check_memory_available() - - combined: List[tuple[str, str, str, str]] = [] - seen_ids: set[str] = set() - seen_content: set[str] = set() - - if use_local: - local_backend = get_markdown_backend() - local_results = local_backend.search_memories( - queries=queries, limit=limit, scope=scope - ) - for mem in local_results: - mem_id = str(getattr(mem, "memory_id", "")) - content = str(getattr(mem, "content", "")) - source = str(getattr(mem, "source", "local")) - if mem_id and mem_id in seen_ids: - continue - if content and content in seen_content: - continue - if mem_id: - seen_ids.add(mem_id) - if content: - seen_content.add(content) - combined.append(("local", mem_id, content, source)) - - if use_cloud: - cloud_backend = _get_lazy_memory_service() - if cloud_backend is not None: - for q in queries: - cloud_results = cloud_backend.search_memories( - user_id=self.user_id, - query=q, - project_id=self.project_id, - limit=limit, - ) - for mem in cloud_results: - mem_id = str(getattr(mem, "memory_id", "")) - content = str(getattr(mem, "content", "")) - if mem_id and mem_id in seen_ids: - continue - if content and content in seen_content: - continue - if mem_id: - seen_ids.add(mem_id) - if content: - seen_content.add(content) - score = getattr(mem, "similarity_score", None) - score_text = f" score={score:.3f}" if isinstance(score, (int, float)) else "" - combined.append(("cloud", mem_id, content, f"vector{score_text}")) - - if not combined: - if mode in ("cloud", "hybrid") and not _check_memory_available(): - return "No memories found. Cloud backend unavailable; using local markdown only." - return "No memories found matching the query." - - lines = [f"Found {min(len(combined), limit)} memories (mode={mode}):"] - for source_kind, mem_id, content, source in combined[:limit]: - tag = f"{source_kind}:{source}" if source else source_kind - id_text = mem_id[:8] if mem_id else "no-id" - preview = content[:100] if content else "(empty)" - lines.append(f" [{id_text}] {preview} [{tag}]") - return "\n".join(lines) - - async def _create( - self, - content: Optional[str], - tags: Optional[List[str]], - metadata: Optional[Dict], - scope: str, - backend_mode: str, - ) -> str: - """Store new memory.""" - if not content: - return "Error: content required for create" - - mode = (backend_mode or "auto").lower() - use_local = mode in ("auto", "local", "hybrid") - use_cloud = mode in ("auto", "cloud", "hybrid") and _check_memory_available() - - results: List[str] = [] - - from hanzo_tools.memory.markdown_memory import get_markdown_backend - - if use_local: - local_backend = get_markdown_backend() - mem = local_backend.add_memory(content=content, scope=scope) - results.append(f"local:{mem.memory_id}") - - if use_cloud: - cloud_backend = _get_lazy_memory_service() - if cloud_backend is not None: - cloud_memory = cloud_backend.create_memory( - user_id=self.user_id, - project_id=self.project_id, - content=content, - metadata=metadata or {"scope": scope, "source": "memory_tool"}, - ) - results.append(f"cloud:{cloud_memory.memory_id}") - - if not results: - if mode in ("cloud", "hybrid"): - return "Memory not created: cloud backend unavailable." - return "Memory not created." - - return f"Created memory ({mode}): " + ", ".join(results) - - async def _update( - self, - id: Optional[str], - content: Optional[str], - tags: Optional[List[str]], - metadata: Optional[Dict], - ) -> str: - """Update existing memory.""" - if not id: - return "Error: id required for update" - - from hanzo_tools.memory.markdown_memory import get_markdown_backend - result = get_markdown_backend().update_memory(id, content or "") - return f"Updated memory: {id}" if result else f"Memory {id} not found" - - async def _delete(self, ids: List[str]) -> str: - """Delete memories.""" - if not ids: - return "Error: id or ids required for delete" - - from hanzo_tools.memory.markdown_memory import get_markdown_backend - backend = get_markdown_backend() - count = sum(1 for mid in ids if backend.delete_memory(mid)) - return f"Deleted {count} of {len(ids)} memories" - - async def _manage( - self, - create_list: Optional[List[Dict]], - update_list: Optional[List[Dict]], - delete_ids: Optional[List[str]], - ) -> str: - """Atomic memory operations.""" - from hanzo_tools.memory.markdown_memory import get_markdown_backend - backend = get_markdown_backend() - results = [] - - if create_list: - for item in create_list: - mem = backend.add_memory(content=item["content"], scope="project") - results.append(f"Created: {mem.memory_id}") - - if update_list: - for item in update_list: - result = backend.update_memory(item["id"], item.get("statement", item.get("content", ""))) - results.append(f"Updated: {item['id']}" if result else f"Not found: {item['id']}") - - if delete_ids: - for mid in delete_ids: - backend.delete_memory(mid) - results.append(f"Deleted: {mid}") - - if not results: - return "No operations specified. Provide create_list, update_list, or delete_ids." - return "\n".join(results) - - async def _facts_lookup( - self, queries: List[str], kb: Optional[str], limit: int - ) -> str: - """Search knowledge bases (falls back to local markdown).""" - if not queries: - return "Error: query required for facts" - - try: - from hanzo_memory.services.knowledge import get_knowledge_service - - ks = get_knowledge_service() - - results = [] - for q in queries: - facts = await ks.recall(query=q, knowledge_base=kb, limit=limit) - results.extend(facts) - - if not results: - return "No facts found." - - lines = [f"Found {len(results)} facts:"] - for fact in results[:limit]: - lines.append(f" โ€ข {fact.content[:100]}...") - return "\n".join(lines) - - except ImportError: - # Fall back to local markdown backend - from hanzo_tools.memory.markdown_memory import get_markdown_backend - - backend = get_markdown_backend() - results = backend.search_memories( - queries=queries, limit=limit, scope="project" - ) - if not results: - return "No facts found." - - lines = [f"Found {len(results)} facts:"] - for mem in results[:limit]: - lines.append(f" โ€ข {mem.content[:100]}...") - return "\n".join(lines) - - async def _facts_store( - self, - facts: List[str], - kb: Optional[str], - metadata: Optional[Dict], - ) -> str: - """Store facts in knowledge base (falls back to local markdown).""" - if not facts: - return "Error: fact or facts required" - - try: - from hanzo_memory.services.knowledge import get_knowledge_service - - ks = get_knowledge_service() - - stored = [] - for fact in facts: - result = await ks.store( - content=fact, - knowledge_base=kb or "default", - metadata=metadata or {}, - ) - stored.append(result.id) - - return f"Stored {len(stored)} facts in '{kb or 'default'}'" - - except ImportError: - # Fall back to local markdown backend - from hanzo_tools.memory.markdown_memory import get_markdown_backend - - backend = get_markdown_backend() - stored = [] - for fact in facts: - mem = backend.add_memory(content=fact, scope="session") - stored.append(mem.memory_id) - return f"Stored {len(stored)} fact(s) locally" - - async def _summarize( - self, - content: Optional[str], - tags: Optional[List[str]], - scope: str, - ) -> str: - """Summarize and store.""" - if not content: - return "Error: content required for summarize" - - from hanzo_tools.memory.markdown_memory import get_markdown_backend - mem = get_markdown_backend().add_memory(content=f"[Summary] {content}", scope=scope) - return f"Stored summary: {mem.memory_id}" - - async def _manage_kb(self, action: str, name: Optional[str]) -> str: - """Manage knowledge bases.""" - try: - from hanzo_memory.services.knowledge import get_knowledge_service - - ks = get_knowledge_service() - - if action == "list": - kbs = await ks.list_knowledge_bases() - if not kbs: - return "No knowledge bases found." - lines = ["Knowledge bases:"] - for kb in kbs: - lines.append(f" โ€ข {kb.name}: {kb.description or 'No description'}") - return "\n".join(lines) - - elif action == "create": - if not name: - return "Error: name required for kb create" - await ks.create_knowledge_base(name=name) - return f"Created knowledge base: {name}" - - elif action == "delete": - if not name: - return "Error: name required for kb delete" - await ks.delete_knowledge_base(name=name) - return f"Deleted knowledge base: {name}" - - else: - return f"Unknown kb action: {action}. Use: list, create, delete" - - except ImportError: - return "Knowledge service not available" - - async def _list(self, list_type: str, scope: str, limit: int) -> str: - """List memories or knowledge bases.""" - if list_type == "kb": - return await self._manage_kb("list", None) - - from hanzo_tools.memory.markdown_memory import get_markdown_backend - backend = get_markdown_backend() - backend._ensure_loaded() - memories = list(backend._file_memories) + list(backend._session_memories) - if scope != "global": - memories = [m for m in memories if getattr(m, "scope", "global") == scope] - - if not memories: - return "No memories found." - - lines = [f"Memories ({min(len(memories), limit)} of {len(memories)}):"] - for mem in memories[:limit]: - lines.append(f" [{mem.memory_id[:8]}] {mem.content[:60]}...") - return "\n".join(lines) - - async def _stats(self, scope: str) -> str: - """Get memory statistics.""" - from hanzo_tools.memory.markdown_memory import get_markdown_backend - backend = get_markdown_backend() - backend._ensure_loaded() - file_count = len(backend._file_memories) - session_count = len(backend._session_memories) - total = file_count + session_count - return f"Memory stats:\n Total: {total}\n File-backed: {file_count}\n Session: {session_count}\n Scope: {scope}" - - async def _clear(self, scope: str) -> str: - """Clear all memories in scope.""" - from hanzo_tools.memory.markdown_memory import get_markdown_backend - backend = get_markdown_backend() - backend._ensure_loaded() - if scope == "session": - count = len(backend._session_memories) - backend._session_memories.clear() - return f"Cleared {count} session memories" - return "Clear only supported for session scope. File-backed memories must be deleted individually." - - async def _export(self, scope: str, limit: int) -> str: - """Export memories as JSON.""" - import json as json_mod - - from hanzo_tools.memory.markdown_memory import get_markdown_backend - backend = get_markdown_backend() - backend._ensure_loaded() - memories = list(backend._file_memories) + list(backend._session_memories) - items = [] - for mem in memories[:limit]: - items.append({ - "id": mem.memory_id, - "content": mem.content, - "scope": getattr(mem, "scope", "global"), - "source": getattr(mem, "source", "local"), - }) - return json_mod.dumps({"memories": items, "count": len(items)}, indent=2, default=str) - - async def _import(self, content: Optional[str]) -> str: - """Import memories from JSON.""" - import json as json_mod - if not content: - return "Error: content (JSON) required for import" - try: - data = json_mod.loads(content) - items = data if isinstance(data, list) else data.get("memories", []) - from hanzo_tools.memory.markdown_memory import get_markdown_backend - backend = get_markdown_backend() - count = 0 - for item in items: - if isinstance(item, dict) and "content" in item: - backend.add_memory(content=item["content"], scope=item.get("scope", "session")) - count += 1 - return f"Imported {count} memories" - except Exception as e: - return f"Import error: {e}" - - async def _merge(self, ids: List[str]) -> str: - """Merge duplicate memories.""" - if len(ids) < 2: - return "Error: at least 2 IDs required for merge" - from hanzo_tools.memory.markdown_memory import get_markdown_backend - backend = get_markdown_backend() - backend._ensure_loaded() - contents = [] - for mid in ids: - for mem in list(backend._file_memories) + list(backend._session_memories): - if mem.memory_id == mid or mem.memory_id.startswith(mid): - contents.append(mem.content) - break - if len(contents) < 2: - return "Error: could not find enough memories to merge" - merged = "\n---\n".join(contents) - mem = backend.add_memory(content=f"[Merged] {merged}", scope="session") - # Delete originals - for mid in ids: - backend.delete_memory(mid) - return f"Merged {len(ids)} memories into {mem.memory_id}" - - async def _tag(self, id: Optional[str], tags: List[str]) -> str: - """Add tags to a memory.""" - if not id: - return "Error: id required for tag" - if not tags: - return "Error: tags list required" - from hanzo_tools.memory.markdown_memory import get_markdown_backend - backend = get_markdown_backend() - backend._ensure_loaded() - for mem in list(backend._file_memories) + list(backend._session_memories): - if mem.memory_id == id or mem.memory_id.startswith(id): - existing = getattr(mem, 'tags', []) or [] - mem.tags = list(set(existing + tags)) - return f"Tagged {id}: {mem.tags}" - return f"Memory {id} not found" - - async def _untag(self, id: Optional[str], tags: List[str]) -> str: - """Remove tags from a memory.""" - if not id: - return "Error: id required for untag" - if not tags: - return "Error: tags list required" - from hanzo_tools.memory.markdown_memory import get_markdown_backend - backend = get_markdown_backend() - backend._ensure_loaded() - for mem in list(backend._file_memories) + list(backend._session_memories): - if mem.memory_id == id or mem.memory_id.startswith(id): - existing = getattr(mem, 'tags', []) or [] - mem.tags = [t for t in existing if t not in tags] - return f"Untagged {id}: removed {tags}" - return f"Memory {id} not found" - - async def _namespaces(self) -> str: - """List available namespaces/scopes.""" - from hanzo_tools.memory.markdown_memory import get_markdown_backend - backend = get_markdown_backend() - backend._ensure_loaded() - scopes = set() - for mem in list(backend._file_memories) + list(backend._session_memories): - scopes.add(getattr(mem, 'scope', 'global')) - return f"Namespaces: {', '.join(sorted(scopes)) or 'none'}" - - async def _history(self, id: Optional[str], limit: int) -> str: - """Get memory change history (limited - returns current state).""" - if not id: - return "Error: id required for history" - from hanzo_tools.memory.markdown_memory import get_markdown_backend - backend = get_markdown_backend() - backend._ensure_loaded() - for mem in list(backend._file_memories) + list(backend._session_memories): - if mem.memory_id == id or mem.memory_id.startswith(id): - return f"Memory {id}:\n Content: {mem.content[:200]}\n Created: {getattr(mem, 'created_at', 'unknown')}\n (Full history tracking requires cloud backend)" - return f"Memory {id} not found" - - def register(self, mcp_server: FastMCP) -> None: - """Register with MCP server.""" - tool_instance = self - - @mcp_server.tool() - async def memory( - action: Action = "list", - backend: BackendMode = "auto", - query: Annotated[Optional[str], Field(description="Search query")] = None, - queries: Annotated[ - Optional[List[str]], Field(description="Multiple queries") - ] = None, - content: Annotated[ - Optional[str], Field(description="Memory content") - ] = None, - id: Annotated[Optional[str], Field(description="Memory ID")] = None, - ids: Annotated[ - Optional[List[str]], Field(description="Multiple IDs") - ] = None, - tags: Annotated[Optional[List[str]], Field(description="Tags")] = None, - metadata: Annotated[Optional[Dict], Field(description="Metadata")] = None, - scope: Annotated[ - str, Field(description="Scope: session, project, global") - ] = "session", - limit: Annotated[int, Field(description="Max results")] = 10, - kb: Annotated[ - Optional[str], Field(description="Knowledge base name") - ] = None, - fact: Annotated[Optional[str], Field(description="Fact to store")] = None, - facts: Annotated[ - Optional[List[str]], Field(description="Multiple facts") - ] = None, - create_list: Annotated[ - Optional[List[Dict]], Field(description="Items to create") - ] = None, - update_list: Annotated[ - Optional[List[Dict]], Field(description="Items to update") - ] = None, - delete_ids: Annotated[ - Optional[List[str]], Field(description="IDs to delete") - ] = None, - kb_action: Annotated[ - Optional[str], Field(description="KB action: list, create, delete") - ] = None, - name: Annotated[Optional[str], Field(description="KB name")] = None, - list_type: Annotated[ - str, Field(description="List type: memories, kb") - ] = "memories", - ctx: MCPContext = None, - ) -> str: - """Memory management: recall, create, update, delete, facts, kb.""" - return await tool_instance.call( - ctx, - action=action, - backend=backend, - query=query, - queries=queries, - content=content, - id=id, - ids=ids, - tags=tags, - metadata=metadata, - scope=scope, - limit=limit, - kb=kb, - fact=fact, - facts=facts, - create_list=create_list, - update_list=update_list, - delete_ids=delete_ids, - kb_action=kb_action, - name=name, - list_type=list_type, - ) diff --git a/pkg/hanzo-tools-memory/pyproject.toml b/pkg/hanzo-tools-memory/pyproject.toml deleted file mode 100644 index 40fea18b5..000000000 --- a/pkg/hanzo-tools-memory/pyproject.toml +++ /dev/null @@ -1,28 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-memory" -version = "0.2.2" -description = "Memory and knowledge base tools for Hanzo MCP" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "tools", "memory", "knowledge", "mcp", "ai"] -dependencies = [ - "hanzo-tools>=0.3.0", - "mcp>=1.25.0", - "pydantic>=2.12.5", -] - -[project.optional-dependencies] -full = ["hanzo-memory>=0.1.0"] - -[project.entry-points."hanzo.tools"] -memory = "hanzo_tools.memory:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] diff --git a/pkg/hanzo-tools-memory/tests/test_memory_surface_and_discovery.py b/pkg/hanzo-tools-memory/tests/test_memory_surface_and_discovery.py deleted file mode 100644 index 2b329bd14..000000000 --- a/pkg/hanzo-tools-memory/tests/test_memory_surface_and_discovery.py +++ /dev/null @@ -1,74 +0,0 @@ -"""Tests for memory tool surface and local context discovery.""" - -from pathlib import Path - -from hanzo_tools.memory import TOOLS -from hanzo_tools.memory.markdown_memory import MarkdownMemoryBackend - - -def _write(path: Path, content: str) -> None: - path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(content, encoding="utf-8") - - -def test_memory_surface_is_single_tool() -> None: - """Entry-point surface should expose only one MCP tool: memory.""" - assert len(TOOLS) == 1 - tool = TOOLS[0]() - assert tool.name == "memory" - - -def test_context_file_priority_and_rule_detection(tmp_path: Path, monkeypatch) -> None: - """Discover AGENTS->CLAUDE->GEMINI->LLM first and detect editor rule files.""" - _write(tmp_path / "AGENTS.md", "# agents") - _write(tmp_path / "CLAUDE.md", "# claude") - _write(tmp_path / "GEMINI.md", "# gemini") - _write(tmp_path / "LLM.md", "# llm") - _write(tmp_path / "MEMORY.md", "# memory") - _write(tmp_path / ".cursorrules", "cursor rules") - _write(tmp_path / ".cursor" / "rules" / "team.mdc", "cursor team rules") - _write( - tmp_path / ".github" / "copilot-instructions.md", - "# copilot instructions", - ) - _write( - tmp_path / ".github" / "instructions" / "python.instructions.md", - "# python instructions", - ) - - monkeypatch.chdir(tmp_path) - monkeypatch.setenv("HOME", str(tmp_path / "home")) - - backend = MarkdownMemoryBackend() - files = backend._iter_context_files(tmp_path) - rel_paths = [str(p.relative_to(tmp_path)) for p in files] - - assert rel_paths[:4] == ["AGENTS.md", "CLAUDE.md", "GEMINI.md", "LLM.md"] - assert ".cursorrules" in rel_paths - assert ".cursor/rules/team.mdc" in rel_paths - assert ".github/copilot-instructions.md" in rel_paths - assert ".github/instructions/python.instructions.md" in rel_paths - - -def test_scan_is_local_first_then_parent(tmp_path: Path, monkeypatch) -> None: - """Current directory files should be loaded before parent directory files.""" - repo_root = tmp_path / "repo" - project_dir = repo_root / "services" / "api" - _write(repo_root / "AGENTS.md", "# root agents") - _write(project_dir / "AGENTS.md", "# local agents") - - monkeypatch.chdir(project_dir) - monkeypatch.setenv("HOME", str(tmp_path / "home")) - - backend = MarkdownMemoryBackend() - backend._ensure_loaded() - - sources = [ - Path(m.source) - for m in backend._file_memories - if m.source.endswith("AGENTS.md") - ] - - assert sources - assert sources[0] == project_dir / "AGENTS.md" - assert repo_root / "AGENTS.md" in sources diff --git a/pkg/hanzo-tools-memory/tests/test_memory_tools.py b/pkg/hanzo-tools-memory/tests/test_memory_tools.py deleted file mode 100644 index e05464229..000000000 --- a/pkg/hanzo-tools-memory/tests/test_memory_tools.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Tests for hanzo-tools-memory.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import memory - - assert memory is not None - - def test_import_tools(self): - from hanzo_tools.memory import TOOLS - - assert len(TOOLS) > 0 - - -class TestMemoryTool: - """Tests for MemoryTool.""" - - @pytest.fixture - def tool(self): - from hanzo_tools.memory import TOOLS - - return TOOLS[0]() if TOOLS else None - - def test_has_description(self, tool): - if tool: - assert tool.description diff --git a/pkg/hanzo-tools-mpc/README.md b/pkg/hanzo-tools-mpc/README.md deleted file mode 100644 index d73f6ef27..000000000 --- a/pkg/hanzo-tools-mpc/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# hanzo-tools-mpc - -MCP tool package for hanzo-mcp. Provides native mpc management via the Hanzo platform. - -## Installation - -```bash -pip install hanzo-tools-mpc -``` - -Part of the [hanzo-mcp](https://pypi.org/project/hanzo-mcp/) ecosystem. diff --git a/pkg/hanzo-tools-mpc/hanzo_tools/__init__.py b/pkg/hanzo-tools-mpc/hanzo_tools/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-tools-mpc/hanzo_tools/mpc/__init__.py b/pkg/hanzo-tools-mpc/hanzo_tools/mpc/__init__.py deleted file mode 100644 index c9f319e5d..000000000 --- a/pkg/hanzo-tools-mpc/hanzo_tools/mpc/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Hanzo MPC Tools -- threshold signing, vaults, wallets, and policies via MCP.""" - -from .mpc_tool import MPCTool - -TOOLS = [MPCTool] - -__all__ = ["MPCTool", "TOOLS"] diff --git a/pkg/hanzo-tools-mpc/hanzo_tools/mpc/mpc_tool.py b/pkg/hanzo-tools-mpc/hanzo_tools/mpc/mpc_tool.py deleted file mode 100644 index 100e4c8fc..000000000 --- a/pkg/hanzo-tools-mpc/hanzo_tools/mpc/mpc_tool.py +++ /dev/null @@ -1,563 +0,0 @@ -"""MCP tool for MPC threshold signing -- vaults, wallets, transactions, policies. - -Provides a unified interface for managing MPC (Multi-Party Computation) -threshold signing operations including vault management, wallet keygen, -transaction signing, approval workflows, and cross-chain bridge signing. - -Auth: Uses HanzoSession from hanzo-tools-auth for authenticated API calls. -""" - -from __future__ import annotations - -import json -import logging -from typing import Any, Annotated, final - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core.base import BaseTool - -logger = logging.getLogger(__name__) - -DESCRIPTION = """MPC threshold signing -- vaults, wallets, transactions, policies. - -Requires authentication via `hanzo login` (stored at ~/.hanzo/auth/token.json). - -Status: -- status: MPC service health and status - -Vault actions: -- vaults: List vaults -- vault: Get vault details (params: vault_id) -- create_vault: Create a new vault (params: name, description) - -Wallet actions: -- wallets: List wallets in a vault (params: vault_id) -- wallet: Get wallet details (params: wallet_id) -- create_wallet: Create wallet / initiate keygen (params: vault_id, name, curve, threshold, parties) - -Transaction actions: -- transactions: List transactions -- transaction: Get transaction details (params: transaction_id) -- create_transaction: Create a signing request (params: wallet_id, type, chain, to, amount) -- approve: Approve a transaction (params: transaction_id) -- reject: Reject a transaction (params: transaction_id) -- broadcast: Broadcast a signed transaction (params: transaction_id) - -Policy actions: -- policies: List signing policies - -Smart wallet actions: -- smart_wallets: List smart contract wallets - -Whitelist actions: -- whitelist: List whitelisted addresses - -Bridge actions: -- bridge_sign: Sign a cross-chain bridge transaction (params: wallet_id, source_chain, dest_chain, token, amount, recipient) -""" - - -def _get_session(): - """Get HanzoSession singleton.""" - from hanzo_tools.auth.session import HanzoSession - return HanzoSession.get() - - -@final -class MPCTool(BaseTool): - """MCP tool for MPC threshold signing operations.""" - - @property - def name(self) -> str: - return "mpc" - - @property - def description(self) -> str: - return DESCRIPTION - - async def call( - self, - ctx: MCPContext, - action: str = "status", - vault_id: str | None = None, - wallet_id: str | None = None, - transaction_id: str | None = None, - name: str | None = None, - description: str | None = None, - curve: str | None = None, - threshold: int | None = None, - parties: int | None = None, - type: str | None = None, - chain: str | None = None, - to: str | None = None, - amount: str | None = None, - source_chain: str | None = None, - dest_chain: str | None = None, - token: str | None = None, - recipient: str | None = None, - **kwargs: Any, - ) -> str: - try: - # Status - if action == "status": - return await self._status() - # Vault actions - elif action == "vaults": - return await self._vaults() - elif action == "vault": - return await self._vault(vault_id) - elif action == "create_vault": - return await self._create_vault(name, description) - # Wallet actions - elif action == "wallets": - return await self._wallets(vault_id) - elif action == "wallet": - return await self._wallet(wallet_id) - elif action == "create_wallet": - return await self._create_wallet(vault_id, name, curve, threshold, parties) - # Transaction actions - elif action == "transactions": - return await self._transactions() - elif action == "transaction": - return await self._transaction(transaction_id) - elif action == "create_transaction": - return await self._create_transaction(wallet_id, type, chain, to, amount) - elif action == "approve": - return await self._approve(transaction_id) - elif action == "reject": - return await self._reject(transaction_id) - elif action == "broadcast": - return await self._broadcast(transaction_id) - # Policy / smart wallet / whitelist - elif action == "policies": - return await self._policies() - elif action == "smart_wallets": - return await self._smart_wallets() - elif action == "whitelist": - return await self._whitelist() - # Bridge - elif action == "bridge_sign": - return await self._bridge_sign( - wallet_id, source_chain, dest_chain, token, amount, recipient, - ) - else: - return json.dumps({ - "error": f"Unknown action: {action}", - "available": [ - "status", - "vaults", "vault", "create_vault", - "wallets", "wallet", "create_wallet", - "transactions", "transaction", "create_transaction", - "approve", "reject", "broadcast", - "policies", "smart_wallets", "whitelist", - "bridge_sign", - ], - }) - except RuntimeError as e: - return json.dumps({"error": str(e)}) - except Exception as e: - logger.exception(f"MPC tool error: {e}") - return json.dumps({"error": f"MPC error: {e}"}) - - # -- Helpers ------------------------------------------------------------- - - async def _mpc_get(self, path: str) -> Any: - """GET from the MPC API via the PaaS gateway.""" - session = _get_session() - paas = session.get_paas_client() - return paas.get(f"/v1/mpc{path}") - - async def _mpc_post(self, path: str, body: dict | None = None) -> Any: - """POST to the MPC API via the PaaS gateway.""" - session = _get_session() - paas = session.get_paas_client() - return paas.post(f"/v1/mpc{path}", json=body or {}) - - async def _mpc_delete(self, path: str) -> Any: - """DELETE from the MPC API via the PaaS gateway.""" - session = _get_session() - paas = session.get_paas_client() - return paas.delete(f"/v1/mpc{path}") - - # -- Status -------------------------------------------------------------- - - async def _status(self) -> str: - data = await self._mpc_get("/status") - return json.dumps(data, indent=2) - - # -- Vault actions ------------------------------------------------------- - - async def _vaults(self) -> str: - data = await self._mpc_get("/vaults") - vaults = data if isinstance(data, list) else [] - result = [] - for v in vaults: - result.append({ - "id": v.get("id"), - "name": v.get("name"), - "description": v.get("description"), - "wallet_count": v.get("walletCount"), - "created_at": v.get("createdAt"), - }) - return json.dumps({"count": len(result), "vaults": result}, indent=2) - - async def _vault(self, vault_id: str | None) -> str: - if not vault_id: - return json.dumps({"error": "Required: vault_id"}) - - data = await self._mpc_get(f"/vaults/{vault_id}") - return json.dumps(data, indent=2) - - async def _create_vault(self, name: str | None, description: str | None) -> str: - if not name: - return json.dumps({"error": "Required: name"}) - - body = {"name": name} - if description: - body["description"] = description - - result = await self._mpc_post("/vaults", body) - return json.dumps({ - "action": "create_vault", - "result": result, - }, indent=2) - - # -- Wallet actions ------------------------------------------------------ - - async def _wallets(self, vault_id: str | None) -> str: - if not vault_id: - return json.dumps({"error": "Required: vault_id"}) - - data = await self._mpc_get(f"/vaults/{vault_id}/wallets") - wallets = data if isinstance(data, list) else [] - result = [] - for w in wallets: - result.append({ - "id": w.get("id"), - "name": w.get("name"), - "curve": w.get("curve"), - "threshold": w.get("threshold"), - "parties": w.get("parties"), - "address": w.get("address"), - "status": w.get("status"), - "created_at": w.get("createdAt"), - }) - return json.dumps({ - "vault_id": vault_id, - "count": len(result), - "wallets": result, - }, indent=2) - - async def _wallet(self, wallet_id: str | None) -> str: - if not wallet_id: - return json.dumps({"error": "Required: wallet_id"}) - - data = await self._mpc_get(f"/wallets/{wallet_id}") - return json.dumps(data, indent=2) - - async def _create_wallet( - self, - vault_id: str | None, - name: str | None, - curve: str | None, - threshold: int | None, - parties: int | None, - ) -> str: - if not vault_id or not name: - return json.dumps({"error": "Required: vault_id and name"}) - - body: dict[str, Any] = {"name": name} - if curve: - body["curve"] = curve - if threshold is not None: - body["threshold"] = threshold - if parties is not None: - body["parties"] = parties - - result = await self._mpc_post(f"/vaults/{vault_id}/wallets", body) - return json.dumps({ - "action": "create_wallet", - "vault_id": vault_id, - "result": result, - }, indent=2) - - # -- Transaction actions ------------------------------------------------- - - async def _transactions(self) -> str: - data = await self._mpc_get("/transactions") - txns = data if isinstance(data, list) else [] - result = [] - for t in txns: - result.append({ - "id": t.get("id"), - "wallet_id": t.get("walletId"), - "type": t.get("type"), - "chain": t.get("chain"), - "status": t.get("status"), - "to": t.get("to"), - "amount": t.get("amount"), - "created_at": t.get("createdAt"), - }) - return json.dumps({"count": len(result), "transactions": result}, indent=2) - - async def _transaction(self, transaction_id: str | None) -> str: - if not transaction_id: - return json.dumps({"error": "Required: transaction_id"}) - - data = await self._mpc_get(f"/transactions/{transaction_id}") - return json.dumps(data, indent=2) - - async def _create_transaction( - self, - wallet_id: str | None, - type: str | None, - chain: str | None, - to: str | None, - amount: str | None, - ) -> str: - if not wallet_id: - return json.dumps({"error": "Required: wallet_id"}) - - body: dict[str, Any] = {"walletId": wallet_id} - if type: - body["type"] = type - if chain: - body["chain"] = chain - if to: - body["to"] = to - if amount: - body["amount"] = amount - - result = await self._mpc_post("/transactions", body) - return json.dumps({ - "action": "create_transaction", - "result": result, - }, indent=2) - - async def _approve(self, transaction_id: str | None) -> str: - if not transaction_id: - return json.dumps({"error": "Required: transaction_id"}) - - result = await self._mpc_post(f"/transactions/{transaction_id}/approve") - return json.dumps({ - "action": "approve", - "transaction_id": transaction_id, - "result": result, - }, indent=2) - - async def _reject(self, transaction_id: str | None) -> str: - if not transaction_id: - return json.dumps({"error": "Required: transaction_id"}) - - result = await self._mpc_post(f"/transactions/{transaction_id}/reject") - return json.dumps({ - "action": "reject", - "transaction_id": transaction_id, - "result": result, - }, indent=2) - - async def _broadcast(self, transaction_id: str | None) -> str: - if not transaction_id: - return json.dumps({"error": "Required: transaction_id"}) - - result = await self._mpc_post(f"/transactions/{transaction_id}/broadcast") - return json.dumps({ - "action": "broadcast", - "transaction_id": transaction_id, - "result": result, - }, indent=2) - - # -- Policies / Smart Wallets / Whitelist -------------------------------- - - async def _policies(self) -> str: - data = await self._mpc_get("/policies") - policies = data if isinstance(data, list) else [] - result = [] - for p in policies: - result.append({ - "id": p.get("id"), - "name": p.get("name"), - "type": p.get("type"), - "rules": p.get("rules"), - "created_at": p.get("createdAt"), - }) - return json.dumps({"count": len(result), "policies": result}, indent=2) - - async def _smart_wallets(self) -> str: - data = await self._mpc_get("/smart-wallets") - wallets = data if isinstance(data, list) else [] - result = [] - for w in wallets: - result.append({ - "id": w.get("id"), - "name": w.get("name"), - "chain": w.get("chain"), - "address": w.get("address"), - "type": w.get("type"), - "status": w.get("status"), - }) - return json.dumps({"count": len(result), "smart_wallets": result}, indent=2) - - async def _whitelist(self) -> str: - data = await self._mpc_get("/whitelist") - addresses = data if isinstance(data, list) else [] - result = [] - for a in addresses: - result.append({ - "id": a.get("id"), - "address": a.get("address"), - "chain": a.get("chain"), - "label": a.get("label"), - "created_at": a.get("createdAt"), - }) - return json.dumps({"count": len(result), "addresses": result}, indent=2) - - # -- Bridge signing ------------------------------------------------------ - - async def _bridge_sign( - self, - wallet_id: str | None, - source_chain: str | None, - dest_chain: str | None, - token: str | None, - amount: str | None, - recipient: str | None, - ) -> str: - if not wallet_id or not source_chain or not dest_chain: - return json.dumps({"error": "Required: wallet_id, source_chain, dest_chain"}) - - body: dict[str, Any] = { - "walletId": wallet_id, - "sourceChain": source_chain, - "destChain": dest_chain, - } - if token: - body["token"] = token - if amount: - body["amount"] = amount - if recipient: - body["recipient"] = recipient - - result = await self._mpc_post("/bridge/sign", body) - return json.dumps({ - "action": "bridge_sign", - "wallet_id": wallet_id, - "source_chain": source_chain, - "dest_chain": dest_chain, - "result": result, - }, indent=2) - - # -- Registration -------------------------------------------------------- - - def register(self, mcp_server: FastMCP) -> None: - """Register MPC tool with explicit parameters.""" - tool_instance = self - - @mcp_server.tool( - name="mpc", - description=DESCRIPTION, - ) - async def mpc( - action: Annotated[ - str, - Field( - description=( - "Action to perform. " - "Status: status. " - "Vaults: vaults, vault, create_vault. " - "Wallets: wallets, wallet, create_wallet. " - "Transactions: transactions, transaction, create_transaction, approve, reject, broadcast. " - "Policies: policies. Smart wallets: smart_wallets. Whitelist: whitelist. " - "Bridge: bridge_sign." - ), - ), - ] = "status", - vault_id: Annotated[ - str | None, - Field(description="Vault ID (for vault, wallets, create_wallet)"), - ] = None, - wallet_id: Annotated[ - str | None, - Field(description="Wallet ID (for wallet, create_transaction, bridge_sign)"), - ] = None, - transaction_id: Annotated[ - str | None, - Field(description="Transaction ID (for transaction, approve, reject, broadcast)"), - ] = None, - name: Annotated[ - str | None, - Field(description="Name (for create_vault, create_wallet)"), - ] = None, - description: Annotated[ - str | None, - Field(description="Description (for create_vault)"), - ] = None, - curve: Annotated[ - str | None, - Field(description="Elliptic curve: secp256k1, ed25519 (for create_wallet)"), - ] = None, - threshold: Annotated[ - int | None, - Field(description="Signing threshold (for create_wallet)"), - ] = None, - parties: Annotated[ - int | None, - Field(description="Number of parties (for create_wallet)"), - ] = None, - type: Annotated[ - str | None, - Field(description="Transaction type: transfer, contract_call (for create_transaction)"), - ] = None, - chain: Annotated[ - str | None, - Field(description="Chain identifier (for create_transaction)"), - ] = None, - to: Annotated[ - str | None, - Field(description="Destination address (for create_transaction)"), - ] = None, - amount: Annotated[ - str | None, - Field(description="Amount as string (for create_transaction, bridge_sign)"), - ] = None, - source_chain: Annotated[ - str | None, - Field(description="Source chain (for bridge_sign)"), - ] = None, - dest_chain: Annotated[ - str | None, - Field(description="Destination chain (for bridge_sign)"), - ] = None, - token: Annotated[ - str | None, - Field(description="Token identifier (for bridge_sign)"), - ] = None, - recipient: Annotated[ - str | None, - Field(description="Recipient address (for bridge_sign)"), - ] = None, - ctx: MCPContext = None, - ) -> str: - return await tool_instance.call( - ctx, - action=action, - vault_id=vault_id, - wallet_id=wallet_id, - transaction_id=transaction_id, - name=name, - description=description, - curve=curve, - threshold=threshold, - parties=parties, - type=type, - chain=chain, - to=to, - amount=amount, - source_chain=source_chain, - dest_chain=dest_chain, - token=token, - recipient=recipient, - ) diff --git a/pkg/hanzo-tools-mpc/pyproject.toml b/pkg/hanzo-tools-mpc/pyproject.toml deleted file mode 100644 index 5ab44fe5b..000000000 --- a/pkg/hanzo-tools-mpc/pyproject.toml +++ /dev/null @@ -1,32 +0,0 @@ -[project] -name = "hanzo-tools-mpc" -version = "0.1.0" -description = "Hanzo MCP tool for MPC threshold signing -- vaults, wallets, transactions, policies" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "mcp", "mpc", "threshold", "signing", "wallets", "tools"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", -] - -dependencies = [ - "hanzo-tools-core>=0.1.0", - "hanzo-tools-auth>=0.1.0", - "httpx>=0.27.0", -] - -[project.entry-points."hanzo.tools"] -mpc = "hanzo_tools.mpc:TOOLS" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] diff --git a/pkg/hanzo-tools-net/README.md b/pkg/hanzo-tools-net/README.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-tools-net/hanzo_tools/__init__.py b/pkg/hanzo-tools-net/hanzo_tools/__init__.py deleted file mode 100644 index 946984951..000000000 --- a/pkg/hanzo-tools-net/hanzo_tools/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Namespace package -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-net/hanzo_tools/net/__init__.py b/pkg/hanzo-tools-net/hanzo_tools/net/__init__.py deleted file mode 100644 index 7f3685ebc..000000000 --- a/pkg/hanzo-tools-net/hanzo_tools/net/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -"""Network tools for Hanzo AI (HIP-0300). - -Tools: -- net: Unified network tool (HIP-0300) - - search: Web search (Query โ†’ [URL, title, snippet]) - - fetch: Retrieve URL content (URL โ†’ {text, mime}) - - download: Save URL with assets (URL โ†’ Path) - - crawl: Recursive site mirror (URL, depth โ†’ [Path]) - - head: Get headers only - -Effect lattice position: NONDETERMINISTIC_EFFECT -All operations involve network I/O. - -Install: - pip install hanzo-tools-net - pip install hanzo-tools-net[full] # With beautifulsoup4 - -Usage: - from hanzo_tools.net import register_tools, TOOLS - - # Register with MCP server - register_tools(mcp_server) - - # Or access the unified tool - from hanzo_tools.net import NetTool -""" - -from hanzo_tools.core import BaseTool, ToolRegistry - -from .fetch_tool import FetchTool, fetch_tool - -# Backward compat -NetTool = FetchTool -net_tool = fetch_tool - -# Export list for tool discovery - HIP-0300 unified tool -TOOLS = [FetchTool] - -__all__ = [ - "FetchTool", - "fetch_tool", - "NetTool", - "net_tool", - "register_tools", - "TOOLS", -] - - -def register_tools(mcp_server, **kwargs) -> list[BaseTool]: - """Register net tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - **kwargs: Additional options (cwd, etc.) - - Returns: - List of registered tool instances - """ - cwd = kwargs.get("cwd") - tool = FetchTool(cwd=cwd) - ToolRegistry.register_tool(mcp_server, tool) - return [tool] diff --git a/pkg/hanzo-tools-net/hanzo_tools/net/fetch_tool.py b/pkg/hanzo-tools-net/hanzo_tools/net/fetch_tool.py deleted file mode 100644 index 5d6fd750d..000000000 --- a/pkg/hanzo-tools-net/hanzo_tools/net/fetch_tool.py +++ /dev/null @@ -1,546 +0,0 @@ -"""Network tool for HIP-0300 architecture. - -This module provides the 'fetch' tool for network operations: -- search: Query โ†’ [URL, title, snippet] -- fetch: URL โ†’ {text, mime, status} -- download: URL โ†’ Path (with assets) -- crawl: URL + depth โ†’ [Path] (recursive mirror) - -Effect lattice position: NONDETERMINISTIC_EFFECT -Network operations are inherently non-deterministic. -""" - -import asyncio -import os -import re -from pathlib import Path -from typing import ClassVar -from urllib.parse import urljoin, urlparse - -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - InvalidParamsError, - ToolError, - content_hash, -) - - -class FetchTool(BaseTool): - """Network operations tool (HIP-0300). - - Handles all network operations: - - search: Web search query - - fetch: Single URL retrieval - - download: Save page with assets - - crawl: Recursive site mirror - - Effect: NONDETERMINISTIC_EFFECT - """ - - name: ClassVar[str] = "fetch" - VERSION: ClassVar[str] = "0.1.0" - - def __init__(self, cwd: str | None = None): - super().__init__() - self.cwd = cwd or os.getcwd() - self._client = None - self._register_net_actions() - - @property - def description(self) -> str: - return """Network operations tool (HIP-0300). - -Actions: -- search: Web search (Query โ†’ [URL, title, snippet]) -- fetch: Retrieve URL content (URL โ†’ {text, mime, status}) -- download: Save page with assets (URL โ†’ Path) -- crawl: Recursive site mirror (URL, depth โ†’ [Path]) - -Effect: NONDETERMINISTIC_EFFECT (network I/O) -""" - - async def _get_client(self): - """Get or create HTTP client.""" - if self._client is None: - try: - import httpx - - self._client = httpx.AsyncClient( - follow_redirects=True, - timeout=30.0, - headers={"User-Agent": "Mozilla/5.0 (compatible; HanzoBot/1.0)"}, - ) - except ImportError: - raise ToolError( - code="INTERNAL_ERROR", - message="httpx not installed. Run: pip install httpx", - ) - return self._client - - def _extract_text(self, html: str) -> str: - """Extract text from HTML.""" - try: - from bs4 import BeautifulSoup - - soup = BeautifulSoup(html, "lxml") - # Remove script and style elements - for script in soup(["script", "style"]): - script.decompose() - return soup.get_text(separator="\n", strip=True) - except ImportError: - # Fallback: simple regex - text = re.sub( - r"]*>.*?", "", html, flags=re.DOTALL | re.IGNORECASE - ) - text = re.sub( - r"]*>.*?", "", text, flags=re.DOTALL | re.IGNORECASE - ) - text = re.sub(r"<[^>]+>", " ", text) - text = re.sub(r"\s+", " ", text) - return text.strip() - - def _extract_links(self, html: str, base_url: str) -> list[str]: - """Extract links from HTML.""" - try: - from bs4 import BeautifulSoup - - soup = BeautifulSoup(html, "lxml") - links = [] - for a in soup.find_all("a", href=True): - href = a["href"] - full_url = urljoin(base_url, href) - links.append(full_url) - return links - except ImportError: - # Fallback: regex - pattern = r'href=["\']([^"\']+)["\']' - matches = re.findall(pattern, html) - return [urljoin(base_url, m) for m in matches] - - def _register_net_actions(self): - """Register all network actions.""" - - @self.action("search", "Web search query") - async def search( - ctx: MCPContext, - query: str, - engine: str = "duckduckgo", - limit: int = 10, - ) -> dict: - """Perform web search. - - Args: - query: Search query - engine: Search engine (duckduckgo, google) - limit: Max results - - Returns: - {results: [{url, title, snippet}]} - - Effect: NONDETERMINISTIC_EFFECT - """ - client = await self._get_client() - - if engine == "duckduckgo": - # DuckDuckGo HTML search (no API key needed) - url = f"https://html.duckduckgo.com/html/?q={query}" - response = await client.get(url) - - results = [] - try: - from bs4 import BeautifulSoup - - soup = BeautifulSoup(response.text, "lxml") - for result in soup.select(".result")[:limit]: - title_el = result.select_one(".result__title") - snippet_el = result.select_one(".result__snippet") - link_el = result.select_one(".result__url") - - if title_el and link_el: - results.append( - { - "title": title_el.get_text(strip=True), - "url": link_el.get("href", ""), - "snippet": ( - snippet_el.get_text(strip=True) - if snippet_el - else "" - ), - } - ) - except ImportError: - # Fallback without beautifulsoup - results = [ - { - "note": "Install beautifulsoup4 for better parsing", - "raw_length": len(response.text), - } - ] - - else: - raise InvalidParamsError( - f"Unknown engine: {engine}", - param="engine", - expected="duckduckgo", - ) - - return { - "results": results, - "query": query, - "engine": engine, - "count": len(results), - } - - @self.action("fetch", "Retrieve URL content") - async def fetch( - ctx: MCPContext, - url: str, - extract_text: bool = False, - headers: dict | None = None, - ) -> dict: - """Fetch content from URL. - - Args: - url: URL to fetch - extract_text: Extract text from HTML - headers: Additional headers - - Returns: - {text, mime, status, headers, hash} - - Effect: NONDETERMINISTIC_EFFECT - """ - client = await self._get_client() - - try: - response = await client.get(url, headers=headers) - except Exception as e: - raise ToolError( - code="INTERNAL_ERROR", - message=f"Network error: {e}", - details={"url": url}, - ) - - content_type = response.headers.get("content-type", "") - mime = content_type.split(";")[0].strip() - - # Determine if text or binary - is_text = mime.startswith("text/") or mime in [ - "application/json", - "application/xml", - "application/javascript", - ] - - if is_text: - text = response.text - if extract_text and "html" in mime: - text = self._extract_text(text) - return { - "text": text, - "mime": mime, - "status": response.status_code, - "hash": content_hash(text), - "size": len(response.content), - "url": str(response.url), - } - else: - # Binary content - return { - "binary": True, - "mime": mime, - "status": response.status_code, - "hash": content_hash(response.content), - "size": len(response.content), - "url": str(response.url), - } - - @self.action("download", "Save URL to file") - async def download( - ctx: MCPContext, - url: str, - dest: str | None = None, - assets: bool = False, - ) -> dict: - """Download URL to local file. - - Args: - url: URL to download - dest: Destination path (auto-generated if not specified) - assets: Download page assets (images, css, js) - - Returns: - {path, size, mime} - - Effect: NONDETERMINISTIC_EFFECT - """ - client = await self._get_client() - - try: - response = await client.get(url) - except Exception as e: - raise ToolError( - code="INTERNAL_ERROR", - message=f"Download failed: {e}", - ) - - # Generate destination path - if not dest: - parsed = urlparse(url) - filename = Path(parsed.path).name or "index.html" - dest = str(Path(self.cwd) / filename) - - dest_path = Path(dest) - dest_path.parent.mkdir(parents=True, exist_ok=True) - - # Save content - dest_path.write_bytes(response.content) - - result = { - "path": str(dest_path), - "size": len(response.content), - "mime": response.headers.get("content-type", "").split(";")[0], - "url": url, - } - - # Download assets if requested - if assets and "html" in result["mime"]: - downloaded_assets = [] - html = response.text - links = [] - - # Extract asset URLs - try: - from bs4 import BeautifulSoup - - soup = BeautifulSoup(html, "lxml") - - for tag in soup.find_all(["img", "link", "script"]): - src = tag.get("src") or tag.get("href") - if src and not src.startswith("data:"): - links.append(urljoin(url, src)) - except ImportError: - pass - - # Download assets - assets_dir = dest_path.parent / f"{dest_path.stem}_assets" - assets_dir.mkdir(exist_ok=True) - - for asset_url in links[:50]: # Limit to 50 assets - try: - asset_resp = await client.get(asset_url) - asset_name = Path(urlparse(asset_url).path).name - asset_path = assets_dir / asset_name - asset_path.write_bytes(asset_resp.content) - downloaded_assets.append(str(asset_path)) - except Exception: - pass - - result["assets"] = downloaded_assets - result["assets_count"] = len(downloaded_assets) - - return result - - @self.action("crawl", "Recursive site mirror") - async def crawl( - ctx: MCPContext, - url: str, - dest: str, - depth: int = 2, - same_host: bool = True, - limit: int = 100, - ) -> dict: - """Crawl and mirror a website. - - Args: - url: Starting URL - dest: Destination directory - depth: Maximum crawl depth - same_host: Only crawl same hostname - limit: Maximum pages to download - - Returns: - {pages: [Path], count} - - Effect: NONDETERMINISTIC_EFFECT - """ - client = await self._get_client() - parsed_start = urlparse(url) - start_host = parsed_start.netloc - - dest_path = Path(dest) - dest_path.mkdir(parents=True, exist_ok=True) - - visited = set() - pages = [] - queue = [(url, 0)] # (url, depth) - - while queue and len(pages) < limit: - current_url, current_depth = queue.pop(0) - - if current_url in visited: - continue - - if current_depth > depth: - continue - - parsed = urlparse(current_url) - if same_host and parsed.netloc != start_host: - continue - - visited.add(current_url) - - try: - response = await client.get(current_url) - except Exception: - continue - - # Generate filename - path_parts = parsed.path.strip("/").split("/") - if not path_parts or not path_parts[-1]: - path_parts.append("index.html") - elif "." not in path_parts[-1]: - path_parts[-1] += ".html" - - file_path = dest_path / "/".join(path_parts) - file_path.parent.mkdir(parents=True, exist_ok=True) - file_path.write_bytes(response.content) - pages.append(str(file_path)) - - # Extract links for further crawling - if "html" in response.headers.get("content-type", ""): - links = self._extract_links(response.text, current_url) - for link in links: - if link not in visited: - queue.append((link, current_depth + 1)) - - return { - "pages": pages, - "count": len(pages), - "dest": str(dest_path), - "depth": depth, - } - - @self.action("head", "Get URL headers only") - async def head( - ctx: MCPContext, - url: str, - ) -> dict: - """Get HTTP headers without downloading body. - - Args: - url: URL to check - - Returns: - {status, headers, size?, mime?} - - Effect: NONDETERMINISTIC_EFFECT - """ - client = await self._get_client() - - try: - response = await client.head(url) - except Exception as e: - raise ToolError( - code="INTERNAL_ERROR", - message=f"Request failed: {e}", - ) - - headers = dict(response.headers) - result = { - "status": response.status_code, - "headers": headers, - "url": str(response.url), - } - - if "content-length" in headers: - result["size"] = int(headers["content-length"]) - if "content-type" in headers: - result["mime"] = headers["content-type"].split(";")[0].strip() - - return result - - @self.action("request", "Full HTTP request with method/headers/body") - async def request( - ctx: MCPContext, - url: str, - method: str = "GET", - headers: dict | None = None, - body: str | None = None, - timeout: int = 30, - ) -> dict: - """HTTP request with full control over method, headers, body. - - Unlike 'fetch' which focuses on text extraction, 'request' returns - the raw response with status, headers, and body. - - Effect: NONDETERMINISTIC_EFFECT - """ - client = await self._get_client() - - try: - response = await client.request( - method, - url, - headers=headers, - content=body, - timeout=timeout, - ) - except Exception as e: - raise ToolError( - code="INTERNAL_ERROR", - message=f"Request failed: {e}", - details={"url": url, "method": method}, - ) - - content_type = response.headers.get("content-type", "") - resp_headers = dict(response.headers) - - if "json" in content_type: - try: - body_data = response.json() - except Exception: - body_data = response.text - else: - body_data = response.text[:50000] if len(response.text) > 50000 else response.text - - return { - "status": response.status_code, - "headers": resp_headers, - "body": body_data, - } - - @self.action("open", "Open URL in browser") - async def open_url( - ctx: MCPContext, - url: str, - ) -> dict: - """Open URL in the system default browser. - - Effect: NONDETERMINISTIC_EFFECT - """ - import platform - - system = platform.system().lower() - if system == "darwin": - cmd = ["open", url] - elif system == "linux": - cmd = ["xdg-open", url] - elif system == "windows": - cmd = ["start", url] - else: - cmd = ["xdg-open", url] - - proc = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - await proc.communicate() - - return {"url": url, "opened": True} - - -# Singleton -fetch_tool = FetchTool diff --git a/pkg/hanzo-tools-net/pyproject.toml b/pkg/hanzo-tools-net/pyproject.toml deleted file mode 100644 index 3f774c3cf..000000000 --- a/pkg/hanzo-tools-net/pyproject.toml +++ /dev/null @@ -1,71 +0,0 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[project] -name = "hanzo-tools-net" -version = "0.1.2" -description = "Network tools for Hanzo AI (HIP-0300)" -readme = "README.md" -license = "MIT" -requires-python = ">=3.12" -authors = [ - { name = "Hanzo AI Team", email = "ai@hanzo.ai" }, -] -keywords = [ - "hanzo", - "mcp", - "tools", - "network", - "fetch", - "crawl", - "search", -] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", -] -dependencies = [ - "hanzo-tools>=0.3.0", - "mcp>=1.0.0", - "httpx>=0.25.0", -] - -[project.optional-dependencies] -full = [ - "beautifulsoup4>=4.12.0", - "lxml>=5.0.0", -] -dev = [ - "pytest>=8.0.0", - "pytest-asyncio>=0.23.0", - "ruff>=0.1.0", -] - -[project.entry-points."hanzo.tools"] -net = "hanzo_tools.net:TOOLS" - -[project.urls] -Homepage = "https://github.com/hanzoai/python-sdk" -Documentation = "https://docs.hanzo.ai" -Repository = "https://github.com/hanzoai/python-sdk" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] - -[tool.ruff] -line-length = 100 -target-version = "py310" - -[tool.ruff.lint] -select = ["E", "F", "I", "UP"] -ignore = ["E501"] - -[tool.pytest.ini_options] -asyncio_mode = "auto" -testpaths = ["tests"] diff --git a/pkg/hanzo-tools-paas/README.md b/pkg/hanzo-tools-paas/README.md deleted file mode 100644 index 66de5f100..000000000 --- a/pkg/hanzo-tools-paas/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# hanzo-tools-paas - -Hanzo PaaS MCP tool โ€” deployments, environments, logs, IAM, and cloud services. diff --git a/pkg/hanzo-tools-paas/hanzo_tools/__init__.py b/pkg/hanzo-tools-paas/hanzo_tools/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-tools-paas/hanzo_tools/paas/__init__.py b/pkg/hanzo-tools-paas/hanzo_tools/paas/__init__.py deleted file mode 100644 index aed62ff8c..000000000 --- a/pkg/hanzo-tools-paas/hanzo_tools/paas/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Hanzo PaaS Tools โ€” deployments, IAM, and cloud services via MCP.""" - -from .paas_tool import PaaSTool - -TOOLS = [PaaSTool] - -__all__ = ["PaaSTool", "TOOLS"] diff --git a/pkg/hanzo-tools-paas/hanzo_tools/paas/paas_tool.py b/pkg/hanzo-tools-paas/hanzo_tools/paas/paas_tool.py deleted file mode 100644 index 6f5254401..000000000 --- a/pkg/hanzo-tools-paas/hanzo_tools/paas/paas_tool.py +++ /dev/null @@ -1,314 +0,0 @@ -"""MCP tool for Hanzo PaaS โ€” deployments, IAM, and cloud services. - -Combines PaaS deployments, IAM user/org management, and cloud services -into a single MCP tool to avoid tool proliferation. - -Auth: Uses HanzoSession from hanzo-tools-auth to exchange IAM tokens -for PaaS sessions and access IAM APIs. -""" - -from __future__ import annotations - -import json -import logging -from typing import Any, Annotated, final - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core.base import BaseTool - -logger = logging.getLogger(__name__) - -DESCRIPTION = """Hanzo PaaS โ€” deployments, IAM, and cloud services. - -Requires authentication via `hanzo login` (stored at ~/.hanzo/auth/token.json). - -PaaS actions: -- deployments: List containers/deployments in a project environment -- deploy: Get details of a specific deployment -- logs: View container logs -- redeploy: Trigger a rolling restart of a container -- env: List environments for a project - -IAM actions: -- whoami: Current user info -- users: List users in the organization -- orgs: List organizations -- roles: List roles - -Cloud actions: -- services: List managed services (cluster info) -- projects: List projects in an organization -""" - - -def _get_session(): - """Get HanzoSession singleton.""" - from hanzo_tools.auth.session import HanzoSession - return HanzoSession.get() - - -@final -class PaaSTool(BaseTool): - """MCP tool for Hanzo PaaS operations.""" - - @property - def name(self) -> str: - return "paas" - - @property - def description(self) -> str: - return DESCRIPTION - - async def call( - self, - ctx: MCPContext, - action: str = "whoami", - org: str | None = None, - project: str | None = None, - environment: str | None = None, - container: str | None = None, - **kwargs: Any, - ) -> str: - try: - # IAM actions - if action == "whoami": - return await self._whoami() - elif action == "users": - return await self._users() - elif action == "orgs": - return await self._orgs() - elif action == "roles": - return await self._roles() - # PaaS actions - elif action == "projects": - return await self._projects(org) - elif action == "env": - return await self._envs(org, project) - elif action == "deployments": - return await self._deployments(org, project, environment) - elif action == "deploy": - return await self._deploy_detail(org, project, environment, container) - elif action == "logs": - return await self._logs(org, project, environment, container) - elif action == "redeploy": - return await self._redeploy(org, project, environment, container) - # Cloud actions - elif action == "services": - return await self._services() - else: - return json.dumps({ - "error": f"Unknown action: {action}", - "available": [ - "whoami", "users", "orgs", "roles", - "projects", "env", "deployments", "deploy", "logs", "redeploy", - "services", - ], - }) - except RuntimeError as e: - return json.dumps({"error": str(e)}) - except Exception as e: - logger.exception(f"PaaS tool error: {e}") - return json.dumps({"error": f"PaaS error: {e}"}) - - # -- IAM actions --------------------------------------------------------- - - async def _whoami(self) -> str: - session = _get_session() - if not session.is_authenticated(): - return json.dumps({"error": "Not authenticated. Run 'hanzo login' first."}) - - token = session.get_iam_token() - if token: - try: - import jwt - - claims = jwt.decode(token, options={"verify_signature": False}) - return json.dumps({ - "sub": claims.get("sub"), - "name": claims.get("name"), - "email": claims.get("email"), - "organization": claims.get("owner"), - "iss": claims.get("iss"), - }, indent=2) - except Exception: - pass - - info = session.get_token_info() - return json.dumps(info, indent=2) - - async def _users(self) -> str: - session = _get_session() - iam = session.get_iam_client() - users = iam.get_users() - result = [] - for u in users: - result.append({ - "id": getattr(u, "id", None), - "name": getattr(u, "name", None), - "email": getattr(u, "email", None), - "display_name": getattr(u, "display_name", None), - }) - return json.dumps({"count": len(result), "users": result}, indent=2) - - async def _orgs(self) -> str: - session = _get_session() - iam = session.get_iam_client() - orgs = iam.get_organizations() - return json.dumps({"count": len(orgs), "organizations": orgs}, indent=2) - - async def _roles(self) -> str: - session = _get_session() - iam = session.get_iam_client() - roles = iam.get_roles() - return json.dumps({"count": len(roles), "roles": roles}, indent=2) - - # -- PaaS actions -------------------------------------------------------- - - async def _projects(self, org: str | None) -> str: - if not org: - return json.dumps({"error": "Required: org (organization ID)"}) - - session = _get_session() - paas = session.get_paas_client() - projects = paas.get(f"/v1/org/{org}/project") - return json.dumps({"org": org, "count": len(projects), "projects": projects}, indent=2) - - async def _envs(self, org: str | None, project: str | None) -> str: - if not org or not project: - return json.dumps({"error": "Required: org and project"}) - - session = _get_session() - paas = session.get_paas_client() - envs = paas.get(f"/v1/org/{org}/project/{project}/env") - return json.dumps({"org": org, "project": project, "environments": envs}, indent=2) - - async def _deployments(self, org: str | None, project: str | None, env: str | None) -> str: - if not org or not project or not env: - return json.dumps({"error": "Required: org, project, and environment"}) - - session = _get_session() - paas = session.get_paas_client() - containers = paas.get(f"/v1/org/{org}/project/{project}/env/{env}/container") - - result = [] - for c in containers if isinstance(containers, list) else []: - result.append({ - "id": c.get("id"), - "name": c.get("name"), - "image": c.get("image"), - "status": c.get("status"), - "replicas": c.get("replicas"), - }) - - return json.dumps({ - "org": org, - "project": project, - "environment": env, - "count": len(result), - "containers": result, - }, indent=2) - - async def _deploy_detail( - self, org: str | None, project: str | None, env: str | None, container: str | None - ) -> str: - if not org or not project or not env or not container: - return json.dumps({"error": "Required: org, project, environment, and container"}) - - session = _get_session() - paas = session.get_paas_client() - data = paas.get(f"/v1/org/{org}/project/{project}/env/{env}/container/{container}") - return json.dumps(data, indent=2) - - async def _logs( - self, org: str | None, project: str | None, env: str | None, container: str | None - ) -> str: - if not org or not project or not env or not container: - return json.dumps({"error": "Required: org, project, environment, and container"}) - - session = _get_session() - paas = session.get_paas_client() - logs = paas.get(f"/v1/org/{org}/project/{project}/env/{env}/container/{container}/logs") - if isinstance(logs, dict): - return json.dumps(logs, indent=2) - return str(logs) - - async def _redeploy( - self, org: str | None, project: str | None, env: str | None, container: str | None - ) -> str: - if not org or not project or not env or not container: - return json.dumps({"error": "Required: org, project, environment, and container"}) - - session = _get_session() - paas = session.get_paas_client() - - # Fetch current config and PUT it back to trigger rolling restart - current = paas.get(f"/v1/org/{org}/project/{project}/env/{env}/container/{container}") - result = paas.put( - f"/v1/org/{org}/project/{project}/env/{env}/container/{container}", - json=current, - ) - return json.dumps({"action": "redeployed", "container": container, "result": result}, indent=2) - - # -- Cloud actions ------------------------------------------------------- - - async def _services(self) -> str: - session = _get_session() - paas = session.get_paas_client() - info = paas.get("/v1/cluster/info") - templates = paas.get("/v1/cluster/templates") - return json.dumps({ - "cluster": info, - "templates": templates, - }, indent=2) - - # -- Registration -------------------------------------------------------- - - def register(self, mcp_server: FastMCP) -> None: - """Register PaaS tool with explicit parameters.""" - tool_instance = self - - @mcp_server.tool( - name="paas", - description=DESCRIPTION, - ) - async def paas( - action: Annotated[ - str, - Field( - description=( - "Action to perform. " - "IAM: whoami, users, orgs, roles. " - "PaaS: projects, env, deployments, deploy, logs, redeploy. " - "Cloud: services." - ), - ), - ] = "whoami", - org: Annotated[ - str | None, - Field(description="Organization ID (for PaaS actions)"), - ] = None, - project: Annotated[ - str | None, - Field(description="Project ID (for PaaS actions)"), - ] = None, - environment: Annotated[ - str | None, - Field(description="Environment ID (for PaaS actions)"), - ] = None, - container: Annotated[ - str | None, - Field(description="Container/deployment ID (for deploy, logs, redeploy)"), - ] = None, - ctx: MCPContext = None, - ) -> str: - return await tool_instance.call( - ctx, - action=action, - org=org, - project=project, - environment=environment, - container=container, - ) diff --git a/pkg/hanzo-tools-paas/pyproject.toml b/pkg/hanzo-tools-paas/pyproject.toml deleted file mode 100644 index c2023cd8f..000000000 --- a/pkg/hanzo-tools-paas/pyproject.toml +++ /dev/null @@ -1,33 +0,0 @@ -[project] -name = "hanzo-tools-paas" -version = "0.1.0" -description = "Hanzo PaaS MCP tool โ€” deployments, environments, logs, IAM, and cloud services" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "mcp", "paas", "iam", "deploy", "tools"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", -] - -dependencies = [ - "hanzo-tools-core>=0.1.0", - "hanzo-tools-auth>=0.1.0", - "hanzo-iam>=1.30.0", - "httpx>=0.27.0", -] - -[project.entry-points."hanzo.tools"] -paas = "hanzo_tools.paas:TOOLS" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] diff --git a/pkg/hanzo-tools-paas/tests/test_paas_tool.py b/pkg/hanzo-tools-paas/tests/test_paas_tool.py deleted file mode 100644 index 58311e8d0..000000000 --- a/pkg/hanzo-tools-paas/tests/test_paas_tool.py +++ /dev/null @@ -1,202 +0,0 @@ -"""PaaSTool test suite โ€” action routing, validation, auth, error handling.""" - -import base64 -import json -from unittest.mock import MagicMock, patch - -import pytest - -from hanzo_tools.paas.paas_tool import PaaSTool - - -@pytest.fixture -def tool(): - return PaaSTool() - - -@pytest.fixture -def ctx(): - return MagicMock() - - -def _mock_session(**overrides): - session = MagicMock() - session.is_authenticated.return_value = overrides.get("authenticated", True) - session.get_iam_token.return_value = overrides.get("token", None) - session.get_token_info.return_value = overrides.get("token_info", {}) - if "paas_get" in overrides: - session.get_paas_client.return_value.get.return_value = overrides["paas_get"] - return session - - -def _fake_jwt(claims: dict) -> str: - header = base64.urlsafe_b64encode(b'{"alg":"none"}').rstrip(b"=").decode() - payload = base64.urlsafe_b64encode(json.dumps(claims).encode()).rstrip(b"=").decode() - return f"{header}.{payload}." - - -class TestProperties: - def test_name(self, tool): - assert tool.name == "paas" - - def test_description_sections(self, tool): - for section in ("PaaS actions", "IAM actions", "Cloud actions"): - assert section in tool.description - - -class TestActionRouting: - @pytest.mark.asyncio - async def test_unknown_action_returns_error(self, tool, ctx): - result = json.loads(await tool.call(ctx, action="bogus")) - assert "Unknown action" in result["error"] - - @pytest.mark.asyncio - async def test_unknown_action_lists_available(self, tool, ctx): - result = json.loads(await tool.call(ctx, action="bogus")) - expected = {"whoami", "users", "orgs", "roles", "projects", "env", - "deployments", "deploy", "logs", "redeploy", "services"} - assert set(result["available"]) == expected - - @pytest.mark.asyncio - async def test_default_action_is_whoami(self, tool, ctx): - session = _mock_session(authenticated=False) - with patch("hanzo_tools.paas.paas_tool._get_session", return_value=session): - result = json.loads(await tool.call(ctx)) - assert "Not authenticated" in result["error"] - - -class TestWhoami: - @pytest.mark.asyncio - async def test_unauthenticated(self, tool, ctx): - session = _mock_session(authenticated=False) - with patch("hanzo_tools.paas.paas_tool._get_session", return_value=session): - result = json.loads(await tool.call(ctx, action="whoami")) - assert "Not authenticated" in result["error"] - - @pytest.mark.asyncio - async def test_jwt_decode(self, tool, ctx): - token = _fake_jwt({"sub": "u1", "name": "Z", "email": "z@hanzo.ai", "owner": "hanzo"}) - session = _mock_session(token=token) - with patch("hanzo_tools.paas.paas_tool._get_session", return_value=session): - result = json.loads(await tool.call(ctx, action="whoami")) - assert result["sub"] == "u1" - assert result["email"] == "z@hanzo.ai" - assert result["organization"] == "hanzo" - - @pytest.mark.asyncio - async def test_fallback_to_token_info(self, tool, ctx): - session = _mock_session(token=None, token_info={"sub": "fallback"}) - with patch("hanzo_tools.paas.paas_tool._get_session", return_value=session): - result = json.loads(await tool.call(ctx, action="whoami")) - assert result["sub"] == "fallback" - - -class TestValidation: - """Required-parameter checks for PaaS actions.""" - - @pytest.mark.asyncio - async def test_projects_needs_org(self, tool, ctx): - with patch("hanzo_tools.paas.paas_tool._get_session"): - result = json.loads(await tool.call(ctx, action="projects")) - assert "error" in result - - @pytest.mark.asyncio - async def test_env_needs_org_and_project(self, tool, ctx): - with patch("hanzo_tools.paas.paas_tool._get_session"): - for kwargs in [ - {"org": "o"}, - {"project": "p"}, - {}, - ]: - result = json.loads(await tool.call(ctx, action="env", **kwargs)) - assert "error" in result - - @pytest.mark.asyncio - async def test_deployments_needs_three(self, tool, ctx): - with patch("hanzo_tools.paas.paas_tool._get_session"): - result = json.loads(await tool.call(ctx, action="deployments", org="o", project="p")) - assert "error" in result - - @pytest.mark.asyncio - async def test_deploy_needs_container(self, tool, ctx): - with patch("hanzo_tools.paas.paas_tool._get_session"): - result = json.loads(await tool.call(ctx, action="deploy", org="o", project="p", environment="e")) - assert "error" in result - - @pytest.mark.asyncio - async def test_logs_needs_container(self, tool, ctx): - with patch("hanzo_tools.paas.paas_tool._get_session"): - result = json.loads(await tool.call(ctx, action="logs", org="o", project="p", environment="e")) - assert "error" in result - - @pytest.mark.asyncio - async def test_redeploy_needs_container(self, tool, ctx): - with patch("hanzo_tools.paas.paas_tool._get_session"): - result = json.loads(await tool.call(ctx, action="redeploy", org="o", project="p", environment="e")) - assert "error" in result - - -class TestProjects: - @pytest.mark.asyncio - async def test_returns_projects(self, tool, ctx): - projects = [{"id": "p1", "name": "web"}, {"id": "p2", "name": "api"}] - session = _mock_session(paas_get=projects) - with patch("hanzo_tools.paas.paas_tool._get_session", return_value=session): - result = json.loads(await tool.call(ctx, action="projects", org="hanzo")) - assert result["org"] == "hanzo" - assert result["count"] == 2 - session.get_paas_client.return_value.get.assert_called_once_with("/v1/org/hanzo/project") - - @pytest.mark.asyncio - async def test_empty_projects(self, tool, ctx): - session = _mock_session(paas_get=[]) - with patch("hanzo_tools.paas.paas_tool._get_session", return_value=session): - result = json.loads(await tool.call(ctx, action="projects", org="hanzo")) - assert result["count"] == 0 - - -class TestDeployments: - @pytest.mark.asyncio - async def test_returns_containers(self, tool, ctx): - containers = [ - {"id": "c1", "name": "web", "image": "nginx:latest", "status": "running", "replicas": 2}, - ] - session = _mock_session(paas_get=containers) - with patch("hanzo_tools.paas.paas_tool._get_session", return_value=session): - result = json.loads(await tool.call( - ctx, action="deployments", org="o", project="p", environment="prod", - )) - assert result["count"] == 1 - assert result["containers"][0]["name"] == "web" - assert result["containers"][0]["status"] == "running" - - @pytest.mark.asyncio - async def test_non_list_containers(self, tool, ctx): - session = _mock_session(paas_get={"error": "not found"}) - with patch("hanzo_tools.paas.paas_tool._get_session", return_value=session): - result = json.loads(await tool.call( - ctx, action="deployments", org="o", project="p", environment="prod", - )) - assert result["count"] == 0 - - -class TestErrorHandling: - @pytest.mark.asyncio - async def test_runtime_error(self, tool, ctx): - with patch("hanzo_tools.paas.paas_tool._get_session", side_effect=RuntimeError("auth failed")): - result = json.loads(await tool.call(ctx, action="whoami")) - assert "auth failed" in result["error"] - - @pytest.mark.asyncio - async def test_generic_exception(self, tool, ctx): - with patch("hanzo_tools.paas.paas_tool._get_session", side_effect=TypeError("bad")): - result = json.loads(await tool.call(ctx, action="whoami")) - assert "bad" in result["error"] - - @pytest.mark.asyncio - async def test_api_error_in_projects(self, tool, ctx): - session = MagicMock() - session.get_paas_client.return_value.get.side_effect = ConnectionError("timeout") - with patch("hanzo_tools.paas.paas_tool._get_session", return_value=session): - result = json.loads(await tool.call(ctx, action="projects", org="hanzo")) - assert "timeout" in result["error"] diff --git a/pkg/hanzo-tools-plan/README.md b/pkg/hanzo-tools-plan/README.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-tools-plan/hanzo_tools/__init__.py b/pkg/hanzo-tools-plan/hanzo_tools/__init__.py deleted file mode 100644 index 946984951..000000000 --- a/pkg/hanzo-tools-plan/hanzo_tools/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Namespace package -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-plan/hanzo_tools/plan/__init__.py b/pkg/hanzo-tools-plan/hanzo_tools/plan/__init__.py deleted file mode 100644 index 2f15483be..000000000 --- a/pkg/hanzo-tools-plan/hanzo_tools/plan/__init__.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Plan orchestration tools for Hanzo AI (HIP-0300). - -Tools: -- plan: Unified orchestration tool (HIP-0300) - - intent: Parse NL โ†’ IntentIR - - route: IntentIR โ†’ Plan (canonical chain) - - compose: Plan โ†’ ExecGraph - - chains: List available canonical chains - -This is the "permissive input โ†’ strict output" layer. -Turns natural language into canonical operator chains. - -Effect lattice position: PURE -All operations are safe to cache and parallelize. - -Install: - pip install hanzo-tools-plan - -Usage: - from hanzo_tools.plan import register_tools, TOOLS - - # Register with MCP server - register_tools(mcp_server) - - # Or access the unified tool - from hanzo_tools.plan import PlanTool -""" - -from hanzo_tools.core import BaseTool, ToolRegistry - -from .plan_tool import PlanTool, plan_tool - -# Export list for tool discovery - HIP-0300 unified tool -TOOLS = [PlanTool] - -__all__ = [ - "PlanTool", - "plan_tool", - "register_tools", - "TOOLS", -] - - -def register_tools(mcp_server, **kwargs) -> list[BaseTool]: - """Register plan tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - **kwargs: Additional options - - Returns: - List of registered tool instances - """ - tool = PlanTool() - ToolRegistry.register_tool(mcp_server, tool) - return [tool] diff --git a/pkg/hanzo-tools-plan/hanzo_tools/plan/plan_tool.py b/pkg/hanzo-tools-plan/hanzo_tools/plan/plan_tool.py deleted file mode 100644 index 43dce28b9..000000000 --- a/pkg/hanzo-tools-plan/hanzo_tools/plan/plan_tool.py +++ /dev/null @@ -1,863 +0,0 @@ -"""Unified plan orchestration tool for HIP-0300 architecture. - -This module provides a single unified 'plan' tool that handles orchestration: -- intent: Parse natural language โ†’ IntentIR -- route: Map IntentIR โ†’ Plan (canonical operator chain) -- compose: Plan โ†’ ExecGraph (typed DAG for execution) -- execute: Run ExecGraph with policy gates (optional, returns audit log) - -Following Unix philosophy: one tool for the Orchestration axis. -Turns "permissive input" into "strict canonical chains." - -Effect lattice: -- intent, route, compose: NONDETERMINISTIC (LLM-based NL understanding) -- execute: NONDETERMINISTIC (audit-friendly) -""" - -import re -from dataclasses import dataclass, field -from datetime import datetime, timezone -from enum import Enum -from typing import Any, ClassVar - -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - InvalidParamsError, - content_hash, -) - - -class EffectClass(str, Enum): - """Effect lattice for operators.""" - - PURE = "pure" - DETERMINISTIC = "deterministic" - NONDETERMINISTIC = "nondeterministic" - - -class StepStatus(str, Enum): - """Status for plan steps (Rust parity).""" - - PENDING = "pending" - IN_PROGRESS = "in_progress" - COMPLETED = "completed" - - -@dataclass -class TrackedStep: - """A tracked plan step with status (Rust parity).""" - - step: str - status: StepStatus = StepStatus.PENDING - - -@dataclass -class TrackedPlan: - """A tracked plan with name and steps (Rust parity).""" - - name: str | None = None - steps: list[TrackedStep] = field(default_factory=list) - created_at: str | None = None - updated_at: str | None = None - - -class Scope(str, Enum): - """Scope lattice for operators.""" - - SPAN = "span" - FILE = "file" - PACKAGE = "package" - REPO = "repo" - WORKSPACE = "workspace" - - -@dataclass -class IntentIR: - """Intermediate representation for parsed intent.""" - - category: str # navigate, edit, refactor, test, deploy, etc. - action: str # find, read, rename, run, etc. - target: str | None = None - params: dict[str, Any] = field(default_factory=dict) - confidence: float = 1.0 - raw: str = "" - - -@dataclass -class PlanNode: - """Single node in execution plan.""" - - id: str - tool: str - action: str - params: dict[str, Any] = field(default_factory=dict) - depends_on: list[str] = field(default_factory=list) - effect: EffectClass = EffectClass.PURE - scope: Scope = Scope.FILE - cache_key: str | None = None - - -@dataclass -class Plan: - """Execution plan as typed DAG.""" - - nodes: list[PlanNode] = field(default_factory=list) - policy_gates: list[str] = field(default_factory=list) # Nodes requiring approval - metadata: dict[str, Any] = field(default_factory=dict) - - -@dataclass -class ExecGraph: - """Compiled execution graph ready for execution.""" - - plan: Plan - execution_order: list[str] # Topologically sorted node IDs - parallelizable: list[list[str]] # Groups that can run in parallel - audit_log: list[dict] = field(default_factory=list) - - -# Intent routing patterns -INTENT_PATTERNS = [ - # Navigation / Understanding - (r"(where|find|locate)\s+(is\s+)?(.+)", "navigate", "find"), - (r"(who|what)\s+(calls|uses)\s+(.+)", "navigate", "references"), - (r"(what|explain|describe)\s+(does\s+)?(.+)\s+(do|mean)", "navigate", "explain"), - (r"(go\s+to|jump\s+to)\s+(definition\s+of\s+)?(.+)", "navigate", "definition"), - (r"(show|list)\s+(symbols|functions|classes)", "navigate", "symbols"), - # Edit / Transform - (r"(edit|change|modify|update)\s+(.+)", "edit", "transform"), - (r"(rename|refactor)\s+(.+)\s+(to|as)\s+(.+)", "refactor", "rename"), - (r"(add|create|insert)\s+(.+)", "edit", "create"), - (r"(remove|delete)\s+(.+)", "edit", "delete"), - (r"(fix|repair|correct)\s+(.+)", "edit", "fix"), - # Test / Validate - (r"(run|execute)\s+(tests?|specs?)", "test", "run"), - (r"(check|verify|validate)\s+(.+)", "test", "validate"), - (r"(lint|typecheck|format)\s+(.+)?", "test", "lint"), - (r"(why|debug)\s+(is|are|did)\s+(.+)\s+(failing|broken|wrong)", "test", "debug"), - # VCS - (r"(commit|save)\s+(changes?)?", "vcs", "commit"), - (r"(diff|compare|show\s+changes)", "vcs", "diff"), - (r"(log|history|blame)\s*(.+)?", "vcs", "log"), - # General - (r"(help|how\s+do\s+i)", "meta", "help"), -] - -# Canonical chains for intents -CANONICAL_CHAINS = { - ("navigate", "find"): [ - {"tool": "fs", "action": "search"}, - ], - ("navigate", "references"): [ - {"tool": "code", "action": "references"}, - {"tool": "code", "action": "summarize"}, - ], - ("navigate", "definition"): [ - {"tool": "code", "action": "definition"}, - {"tool": "fs", "action": "read"}, - ], - ("navigate", "symbols"): [ - {"tool": "code", "action": "symbols"}, - ], - ("navigate", "explain"): [ - {"tool": "fs", "action": "read"}, - {"tool": "code", "action": "parse"}, - {"tool": "code", "action": "summarize"}, - ], - ("edit", "transform"): [ - {"tool": "fs", "action": "read"}, - {"tool": "code", "action": "transform"}, - {"tool": "code", "action": "summarize"}, # Review patch - {"tool": "fs", "action": "patch", "policy_gate": True}, - {"tool": "test", "action": "run"}, - ], - ("refactor", "rename"): [ - {"tool": "code", "action": "references"}, - {"tool": "code", "action": "transform"}, - {"tool": "code", "action": "summarize"}, - {"tool": "fs", "action": "patch", "policy_gate": True}, - {"tool": "test", "action": "run"}, - ], - ("test", "run"): [ - {"tool": "test", "action": "run"}, - {"tool": "code", "action": "summarize"}, - ], - ("test", "debug"): [ - {"tool": "test", "action": "run"}, - {"tool": "code", "action": "summarize"}, - {"tool": "code", "action": "references"}, - ], - ("vcs", "commit"): [ - {"tool": "vcs", "action": "diff", "params": {"staged": True}}, - {"tool": "code", "action": "summarize"}, - {"tool": "vcs", "action": "commit", "policy_gate": True}, - ], - ("vcs", "diff"): [ - {"tool": "vcs", "action": "diff"}, - {"tool": "code", "action": "summarize"}, - ], -} - - -class PlanTool(BaseTool): - """Unified plan orchestration tool (HIP-0300). - - Handles all orchestration operations: - - intent: Parse NL โ†’ IntentIR - - route: IntentIR โ†’ Plan - - compose: Plan โ†’ ExecGraph - - update: Track plan with step status (Rust parity) - - execute: Run with audit (optional) - - This is the "permissive input โ†’ strict output" layer. - """ - - name: ClassVar[str] = "plan" - VERSION: ClassVar[str] = "0.2.0" - - # In-memory tracked plans (supports multiple named plans) - _tracked_plan: TrackedPlan | None = None - _plans: dict[str, TrackedPlan] = {} - _plan_counter: int = 0 - - def __init__(self): - super().__init__() - self._tracked_plan = None - self._plans = {} - self._plan_counter = 0 - self._register_plan_actions() - - @property - def description(self) -> str: - return """Unified plan orchestration tool (HIP-0300). - -Actions: -- create: Create a new plan with name and steps -- show: Show a plan by name -- update: Track plan with step status -- get: Get current tracked plan -- list: List all plans -- next: Get next pending step -- archive: Archive a plan -- add_step: Add a step to existing plan -- remove_step: Remove a step from plan -- estimate: Estimate plan completion -- visualize: Text visualization of plan progress -- clone: Clone an existing plan -- cancel: Cancel a plan -- notes: Add/view plan notes -- progress: Get progress percentage -- clear: Clear tracked plan -- intent: Parse natural language โ†’ IntentIR -- route: Map IntentIR โ†’ Plan -- compose: Plan โ†’ ExecGraph -- chains: List canonical chains - -Turns permissive natural language input into strict canonical operator chains. -""" - - def _parse_intent(self, nl: str) -> IntentIR: - """Parse natural language to IntentIR.""" - nl_lower = nl.lower().strip() - - for pattern, category, action in INTENT_PATTERNS: - match = re.search(pattern, nl_lower, re.IGNORECASE) - if match: - # Extract target from capture groups - groups = match.groups() - target = groups[-1] if groups else None - - return IntentIR( - category=category, - action=action, - target=target, - confidence=0.8, - raw=nl, - ) - - # Fallback: ambiguous - return IntentIR( - category="unknown", - action="unknown", - target=None, - confidence=0.3, - raw=nl, - ) - - def _build_plan(self, intent: IntentIR, policy: dict | None = None) -> Plan: - """Build execution plan from intent.""" - chain_key = (intent.category, intent.action) - chain = CANONICAL_CHAINS.get(chain_key, []) - - if not chain: - # Unknown intent - return empty plan - return Plan( - metadata={ - "intent": intent.category, - "action": intent.action, - "warning": "No canonical chain found", - } - ) - - nodes = [] - policy_gates = [] - prev_id = None - - for i, step in enumerate(chain): - node_id = f"step_{i}" - node = PlanNode( - id=node_id, - tool=step["tool"], - action=step["action"], - params=step.get("params", {}), - depends_on=[prev_id] if prev_id else [], - effect=( - EffectClass.DETERMINISTIC - if step.get("policy_gate") - else EffectClass.PURE - ), - ) - - # Add target from intent - if intent.target and i == 0: - if "path" not in node.params: - node.params["target"] = intent.target - - nodes.append(node) - - if step.get("policy_gate"): - policy_gates.append(node_id) - - prev_id = node_id - - return Plan( - nodes=nodes, - policy_gates=policy_gates, - metadata={ - "intent": intent.category, - "action": intent.action, - "target": intent.target, - }, - ) - - def _compile_graph(self, plan: Plan) -> ExecGraph: - """Compile plan into execution graph.""" - # Topological sort - _node_map = {n.id: n for n in plan.nodes} - in_degree = {n.id: len(n.depends_on) for n in plan.nodes} - execution_order = [] - parallelizable = [] - - # Kahn's algorithm - ready = [nid for nid, deg in in_degree.items() if deg == 0] - - while ready: - # All ready nodes can run in parallel - if len(ready) > 1: - parallelizable.append(ready.copy()) - execution_order.extend(ready) - - next_ready = [] - for nid in ready: - for node in plan.nodes: - if nid in node.depends_on: - in_degree[node.id] -= 1 - if in_degree[node.id] == 0: - next_ready.append(node.id) - - ready = next_ready - - return ExecGraph( - plan=plan, - execution_order=execution_order, - parallelizable=parallelizable, - ) - - def _register_plan_actions(self): - """Register all plan actions.""" - - @self.action( - "update", "Update tracked plan with steps and status (Rust parity)" - ) - async def update( - ctx: MCPContext, - name: str | None = None, - plan: list[dict] | None = None, - ) -> dict: - """Update the tracked plan with steps and their status. - - This matches the Rust update_plan tool schema exactly. - - Args: - name: Optional plan title (2-5 words) - plan: List of steps, each with: - - step: Step description (required) - - status: "pending" | "in_progress" | "completed" - - Returns: - {"message": "Plan updated", "plan": {...}} - - Example: - plan(action="update", name="Fix auth bug", plan=[ - {"step": "Identify the root cause", "status": "completed"}, - {"step": "Write failing test", "status": "in_progress"}, - {"step": "Implement fix", "status": "pending"}, - {"step": "Verify all tests pass", "status": "pending"} - ]) - """ - now = datetime.now(timezone.utc).isoformat() - - # Parse steps - steps = [] - if plan: - for item in plan: - step_text = item.get("step", "") - status_str = item.get("status", "pending") - try: - status = StepStatus(status_str) - except ValueError: - status = StepStatus.PENDING - steps.append(TrackedStep(step=step_text, status=status)) - - # Update or create tracked plan - if self._tracked_plan is None: - self._tracked_plan = TrackedPlan( - name=name, - steps=steps, - created_at=now, - updated_at=now, - ) - else: - if name is not None: - self._tracked_plan.name = name - if steps: - self._tracked_plan.steps = steps - self._tracked_plan.updated_at = now - - return { - "message": "Plan updated", - "plan": { - "name": self._tracked_plan.name, - "steps": [ - {"step": s.step, "status": s.status.value} - for s in self._tracked_plan.steps - ], - "created_at": self._tracked_plan.created_at, - "updated_at": self._tracked_plan.updated_at, - }, - } - - @self.action("get", "Get current tracked plan") - async def get( - ctx: MCPContext, - ) -> dict: - """Get the current tracked plan. - - Returns: - Current plan with name, steps, and timestamps - """ - if self._tracked_plan is None: - return {"plan": None, "message": "No plan currently tracked"} - - return { - "plan": { - "name": self._tracked_plan.name, - "steps": [ - {"step": s.step, "status": s.status.value} - for s in self._tracked_plan.steps - ], - "created_at": self._tracked_plan.created_at, - "updated_at": self._tracked_plan.updated_at, - }, - "progress": { - "total": len(self._tracked_plan.steps), - "completed": sum( - 1 - for s in self._tracked_plan.steps - if s.status == StepStatus.COMPLETED - ), - "in_progress": sum( - 1 - for s in self._tracked_plan.steps - if s.status == StepStatus.IN_PROGRESS - ), - "pending": sum( - 1 - for s in self._tracked_plan.steps - if s.status == StepStatus.PENDING - ), - }, - } - - @self.action("clear", "Clear tracked plan") - async def clear( - ctx: MCPContext, - ) -> dict: - """Clear the current tracked plan.""" - self._tracked_plan = None - return {"message": "Plan cleared"} - - @self.action("intent", "Parse natural language to IntentIR") - async def intent( - ctx: MCPContext, - nl: str, - ) -> dict: - """Parse natural language input to structured IntentIR. - - Args: - nl: Natural language input - - Returns: - IntentIR with category, action, target, confidence - - Effect: NONDETERMINISTIC (LLM-based, regex fallback) - Cache: hash(nl) - """ - parsed = self._parse_intent(nl) - - return { - "intent_ir": { - "category": parsed.category, - "action": parsed.action, - "target": parsed.target, - "params": parsed.params, - "confidence": parsed.confidence, - }, - "raw": nl, - "hash": content_hash(nl), - } - - @self.action("route", "Map IntentIR to Plan") - async def route( - ctx: MCPContext, - intent_ir: dict | None = None, - nl: str | None = None, - policy: dict | None = None, - ) -> dict: - """Route intent to canonical operator chain. - - Args: - intent_ir: Parsed intent (from plan.intent) - nl: Raw NL (will parse if intent_ir not provided) - policy: Policy constraints - - Returns: - Plan with nodes, policy_gates, metadata - - Effect: NONDETERMINISTIC - """ - if intent_ir: - intent = IntentIR( - category=intent_ir.get("category", "unknown"), - action=intent_ir.get("action", "unknown"), - target=intent_ir.get("target"), - params=intent_ir.get("params", {}), - confidence=intent_ir.get("confidence", 1.0), - ) - elif nl: - intent = self._parse_intent(nl) - else: - raise InvalidParamsError("intent_ir or nl required") - - plan = self._build_plan(intent, policy) - - return { - "plan": { - "nodes": [ - { - "id": n.id, - "tool": n.tool, - "action": n.action, - "params": n.params, - "depends_on": n.depends_on, - "effect": n.effect.value, - } - for n in plan.nodes - ], - "policy_gates": plan.policy_gates, - "metadata": plan.metadata, - }, - "chain_length": len(plan.nodes), - "requires_approval": len(plan.policy_gates) > 0, - } - - @self.action("compose", "Compile Plan to ExecGraph") - async def compose( - ctx: MCPContext, - plan: dict, - ) -> dict: - """Compile plan into execution-ready graph. - - Args: - plan: Plan dict (from plan.route) - - Returns: - ExecGraph with execution_order, parallelizable groups - - Effect: PURE - """ - # Reconstruct Plan - nodes = [ - PlanNode( - id=n["id"], - tool=n["tool"], - action=n["action"], - params=n.get("params", {}), - depends_on=n.get("depends_on", []), - effect=EffectClass(n.get("effect", "pure")), - ) - for n in plan.get("nodes", []) - ] - - plan_obj = Plan( - nodes=nodes, - policy_gates=plan.get("policy_gates", []), - metadata=plan.get("metadata", {}), - ) - - graph = self._compile_graph(plan_obj) - - return { - "exec_graph": { - "execution_order": graph.execution_order, - "parallelizable": graph.parallelizable, - "policy_gates": plan_obj.policy_gates, - "total_steps": len(graph.execution_order), - }, - "plan": plan, - "ready_for_execution": True, - } - - @self.action("chains", "List available canonical chains") - async def chains( - ctx: MCPContext, - category: str | None = None, - ) -> dict: - """List available canonical operator chains.""" - result = {} - for (cat, action), chain in CANONICAL_CHAINS.items(): - if category and cat != category: - continue - key = f"{cat}.{action}" - result[key] = { - "steps": len(chain), - "tools": [s["tool"] for s in chain], - "has_policy_gate": any(s.get("policy_gate") for s in chain), - } - return {"chains": result, "total": len(result)} - - # --- TS parity actions below --- - - def _get_plan(self, name: str | None = None) -> TrackedPlan | None: - if name and name in self._plans: - return self._plans[name] - return self._tracked_plan - - def _save_plan(self, plan: TrackedPlan): - now = datetime.now(timezone.utc).isoformat() - plan.updated_at = now - if plan.name: - self._plans[plan.name] = plan - self._tracked_plan = plan - - def _plan_to_dict(self, plan: TrackedPlan) -> dict: - return { - "name": plan.name, - "steps": [{"step": s.step, "status": s.status.value} for s in plan.steps], - "created_at": plan.created_at, - "updated_at": plan.updated_at, - } - - @self.action("create", "Create a new plan") - async def create( - ctx: MCPContext, - name: str = "", - steps: list[str] | None = None, - **kwargs, - ) -> dict: - """Create a new named plan with steps.""" - if not name: - self._plan_counter += 1 - name = f"plan-{self._plan_counter}" - now = datetime.now(timezone.utc).isoformat() - tracked_steps = [TrackedStep(step=s) for s in (steps or [])] - plan = TrackedPlan(name=name, steps=tracked_steps, created_at=now, updated_at=now) - _save_plan(plan) - return {"message": f"Created plan '{name}'", "plan": _plan_to_dict(plan)} - - @self.action("show", "Show a plan by name") - async def show(ctx: MCPContext, name: str = "", **kwargs) -> dict: - """Show plan details.""" - plan = _get_plan(name or None) - if not plan: - return {"error": f"Plan '{name}' not found" if name else "No active plan"} - return {"plan": _plan_to_dict(plan)} - - @self.action("list", "List all plans") - async def list_plans(ctx: MCPContext, **kwargs) -> dict: - """List all tracked plans.""" - plans = [] - for n, p in self._plans.items(): - completed = sum(1 for s in p.steps if s.status == StepStatus.COMPLETED) - plans.append({"name": n, "steps": len(p.steps), "completed": completed}) - return {"plans": plans, "total": len(plans)} - - @self.action("next", "Get next pending step") - async def next_step(ctx: MCPContext, name: str = "", **kwargs) -> dict: - """Get the next pending step from a plan.""" - plan = _get_plan(name or None) - if not plan: - return {"error": "No active plan"} - for i, s in enumerate(plan.steps): - if s.status == StepStatus.PENDING: - return {"index": i, "step": s.step, "status": s.status.value} - return {"message": "All steps completed"} - - @self.action("archive", "Archive a plan") - async def archive(ctx: MCPContext, name: str = "", **kwargs) -> dict: - """Archive (remove) a plan.""" - target = name or (self._tracked_plan.name if self._tracked_plan else None) - if target and target in self._plans: - del self._plans[target] - if self._tracked_plan and self._tracked_plan.name == target: - self._tracked_plan = None - return {"message": f"Archived plan '{target}'"} - return {"error": f"Plan '{target}' not found"} - - @self.action("add_step", "Add a step to a plan") - async def add_step( - ctx: MCPContext, - step: str = "", - name: str = "", - position: int = -1, - **kwargs, - ) -> dict: - """Add a step to an existing plan.""" - if not step: - return {"error": "step text required"} - plan = _get_plan(name or None) - if not plan: - return {"error": "No active plan"} - new_step = TrackedStep(step=step) - if position >= 0 and position <= len(plan.steps): - plan.steps.insert(position, new_step) - else: - plan.steps.append(new_step) - _save_plan(plan) - return {"message": f"Added step to '{plan.name}'", "plan": _plan_to_dict(plan)} - - @self.action("remove_step", "Remove a step from a plan") - async def remove_step( - ctx: MCPContext, - index: int = -1, - name: str = "", - **kwargs, - ) -> dict: - """Remove a step by index.""" - plan = _get_plan(name or None) - if not plan: - return {"error": "No active plan"} - if index < 0 or index >= len(plan.steps): - return {"error": f"Invalid index {index}, plan has {len(plan.steps)} steps"} - removed = plan.steps.pop(index) - _save_plan(plan) - return {"message": f"Removed step: {removed.step}", "plan": _plan_to_dict(plan)} - - @self.action("estimate", "Estimate plan completion") - async def estimate(ctx: MCPContext, name: str = "", **kwargs) -> dict: - """Estimate plan completion based on step progress.""" - plan = _get_plan(name or None) - if not plan: - return {"error": "No active plan"} - total = len(plan.steps) - if total == 0: - return {"progress": 0, "remaining": 0, "total": 0} - completed = sum(1 for s in plan.steps if s.status == StepStatus.COMPLETED) - in_progress = sum(1 for s in plan.steps if s.status == StepStatus.IN_PROGRESS) - pct = round((completed + in_progress * 0.5) / total * 100, 1) - return { - "total": total, - "completed": completed, - "in_progress": in_progress, - "pending": total - completed - in_progress, - "progress_pct": pct, - } - - @self.action("visualize", "Text visualization of plan progress") - async def visualize(ctx: MCPContext, name: str = "", **kwargs) -> str: - """Render a text progress bar for the plan.""" - plan = _get_plan(name or None) - if not plan: - return "No active plan" - lines = [f"Plan: {plan.name or '(unnamed)'}"] - for i, s in enumerate(plan.steps): - icon = {"completed": "[x]", "in_progress": "[~]", "pending": "[ ]"}.get(s.status.value, "[ ]") - lines.append(f" {i+1}. {icon} {s.step}") - total = len(plan.steps) - done = sum(1 for s in plan.steps if s.status == StepStatus.COMPLETED) - bar_len = 20 - filled = int(bar_len * done / total) if total else 0 - bar = "โ–ˆ" * filled + "โ–‘" * (bar_len - filled) - lines.append(f"\n [{bar}] {done}/{total}") - return "\n".join(lines) - - @self.action("clone", "Clone an existing plan") - async def clone_plan(ctx: MCPContext, name: str = "", new_name: str = "", **kwargs) -> dict: - """Clone a plan with a new name, resetting step status.""" - plan = _get_plan(name or None) - if not plan: - return {"error": "No active plan to clone"} - if not new_name: - self._plan_counter += 1 - new_name = f"{plan.name or 'plan'}-copy-{self._plan_counter}" - now = datetime.now(timezone.utc).isoformat() - new_steps = [TrackedStep(step=s.step) for s in plan.steps] - new_plan = TrackedPlan(name=new_name, steps=new_steps, created_at=now, updated_at=now) - _save_plan(new_plan) - return {"message": f"Cloned to '{new_name}'", "plan": _plan_to_dict(new_plan)} - - @self.action("cancel", "Cancel a plan") - async def cancel(ctx: MCPContext, name: str = "", **kwargs) -> dict: - """Cancel a plan (archive with cancelled status).""" - target = name or (self._tracked_plan.name if self._tracked_plan else None) - if target and target in self._plans: - del self._plans[target] - if self._tracked_plan and self._tracked_plan.name == target: - self._tracked_plan = None - return {"message": f"Cancelled plan '{target}'"} - return {"error": f"Plan '{target}' not found"} - - @self.action("notes", "Add or view plan notes") - async def notes(ctx: MCPContext, name: str = "", note: str = "", **kwargs) -> dict: - """Add a note to a plan or view existing notes.""" - plan = _get_plan(name or None) - if not plan: - return {"error": "No active plan"} - if not hasattr(plan, '_notes'): - plan._notes = [] - if note: - plan._notes.append({"text": note, "at": datetime.now(timezone.utc).isoformat()}) - _save_plan(plan) - return {"message": "Note added", "notes": plan._notes} - return {"notes": getattr(plan, '_notes', [])} - - @self.action("progress", "Get progress percentage") - async def progress(ctx: MCPContext, name: str = "", **kwargs) -> dict: - """Get plan progress as percentage.""" - plan = _get_plan(name or None) - if not plan: - return {"error": "No active plan"} - total = len(plan.steps) - if total == 0: - return {"progress": 100, "message": "Empty plan"} - completed = sum(1 for s in plan.steps if s.status == StepStatus.COMPLETED) - return { - "progress": round(completed / total * 100, 1), - "completed": completed, - "total": total, - } - -# Singleton -plan_tool = PlanTool diff --git a/pkg/hanzo-tools-plan/pyproject.toml b/pkg/hanzo-tools-plan/pyproject.toml deleted file mode 100644 index e11077b7b..000000000 --- a/pkg/hanzo-tools-plan/pyproject.toml +++ /dev/null @@ -1,66 +0,0 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[project] -name = "hanzo-tools-plan" -version = "0.1.2" -description = "Plan orchestration tools for Hanzo AI (HIP-0300)" -readme = "README.md" -license = "MIT" -requires-python = ">=3.12" -authors = [ - { name = "Hanzo AI Team", email = "ai@hanzo.ai" }, -] -keywords = [ - "hanzo", - "mcp", - "tools", - "plan", - "orchestration", - "dag", - "intent", -] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", -] -dependencies = [ - "hanzo-tools-core>=0.1.0", - "mcp>=1.0.0", -] - -[project.optional-dependencies] -dev = [ - "pytest>=8.0.0", - "pytest-asyncio>=0.23.0", - "ruff>=0.1.0", -] - -[project.entry-points."hanzo.tools"] -plan = "hanzo_tools.plan:TOOLS" - -[project.urls] -Homepage = "https://github.com/hanzoai/python-sdk" -Documentation = "https://docs.hanzo.ai" -Repository = "https://github.com/hanzoai/python-sdk" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] - -[tool.ruff] -line-length = 100 -target-version = "py310" - -[tool.ruff.lint] -select = ["E", "F", "I", "UP"] -ignore = ["E501"] - -[tool.pytest.ini_options] -asyncio_mode = "auto" -testpaths = ["tests"] diff --git a/pkg/hanzo-tools-plan/uv.lock b/pkg/hanzo-tools-plan/uv.lock deleted file mode 100644 index 3e0e612ad..000000000 --- a/pkg/hanzo-tools-plan/uv.lock +++ /dev/null @@ -1,1524 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" - -[[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.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, -] - -[[package]] -name = "attrs" -version = "25.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, -] - -[[package]] -name = "authlib" -version = "1.6.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/dc/ed1681bf1339dd6ea1ce56136bad4baabc6f7ad466e375810702b0237047/authlib-1.6.7.tar.gz", hash = "sha256:dbf10100011d1e1b34048c9d120e83f13b35d69a826ae762b93d2fb5aafc337b", size = 164950, upload-time = "2026-02-06T14:04:14.171Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/00/3ed12264094ec91f534fae429945efbaa9f8c666f3aa7061cc3b2a26a0cd/authlib-1.6.7-py2.py3-none-any.whl", hash = "sha256:c637340d9a02789d2efa1d003a7437d10d3e565237bcb5fcbc6c134c7b95bab0", size = 244115, upload-time = "2026-02-06T14:04:12.141Z" }, -] - -[[package]] -name = "beartype" -version = "0.22.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, -] - -[[package]] -name = "cachetools" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/af/df70e9b65bc77a1cbe0768c0aa4617147f30f8306ded98c1744bcdc0ae1e/cachetools-7.0.0.tar.gz", hash = "sha256:a9abf18ff3b86c7d05b27ead412e235e16ae045925e531fae38d5fada5ed5b08", size = 35796, upload-time = "2026-02-01T18:59:47.411Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/df/2dd32cce20cbcf6f2ec456b58d44368161ad28320729f64e5e1d5d7bd0ae/cachetools-7.0.0-py3-none-any.whl", hash = "sha256:d52fef60e6e964a1969cfb61ccf6242a801b432790fe520d78720d757c81cbd2", size = 13487, upload-time = "2026-02-01T18:59:45.981Z" }, -] - -[[package]] -name = "certifi" -version = "2026.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, - { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, - { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, - { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, - { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, - { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, - { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, - { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, - { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, -] - -[[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 = "croniter" -version = "6.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil" }, - { name = "pytz" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ad/2f/44d1ae153a0e27be56be43465e5cb39b9650c781e001e7864389deb25090/croniter-6.0.0.tar.gz", hash = "sha256:37c504b313956114a983ece2c2b07790b1f1094fe9d81cc94739214748255577", size = 64481, upload-time = "2024-12-17T17:17:47.32Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/4b/290b4c3efd6417a8b0c284896de19b1d5855e6dbdb97d2a35e68fa42de85/croniter-6.0.0-py2.py3-none-any.whl", hash = "sha256:2f878c3856f17896979b2a4379ba1f09c83e374931ea15cc835c5dd2eee9b368", size = 25468, upload-time = "2024-12-17T17:17:45.359Z" }, -] - -[[package]] -name = "cryptography" -version = "46.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/19/f748958276519adf6a0c1e79e7b8860b4830dda55ccdf29f2719b5fc499c/cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59", size = 749301, upload-time = "2026-01-28T00:24:37.379Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/99/157aae7949a5f30d51fcb1a9851e8ebd5c74bf99b5285d8bb4b8b9ee641e/cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485", size = 7173686, upload-time = "2026-01-28T00:23:07.515Z" }, - { url = "https://files.pythonhosted.org/packages/87/91/874b8910903159043b5c6a123b7e79c4559ddd1896e38967567942635778/cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc", size = 4275871, upload-time = "2026-01-28T00:23:09.439Z" }, - { url = "https://files.pythonhosted.org/packages/c0/35/690e809be77896111f5b195ede56e4b4ed0435b428c2f2b6d35046fbb5e8/cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0", size = 4423124, upload-time = "2026-01-28T00:23:11.529Z" }, - { url = "https://files.pythonhosted.org/packages/1a/5b/a26407d4f79d61ca4bebaa9213feafdd8806dc69d3d290ce24996d3cfe43/cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa", size = 4277090, upload-time = "2026-01-28T00:23:13.123Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d8/4bb7aec442a9049827aa34cee1aa83803e528fa55da9a9d45d01d1bb933e/cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81", size = 4947652, upload-time = "2026-01-28T00:23:14.554Z" }, - { url = "https://files.pythonhosted.org/packages/2b/08/f83e2e0814248b844265802d081f2fac2f1cbe6cd258e72ba14ff006823a/cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255", size = 4455157, upload-time = "2026-01-28T00:23:16.443Z" }, - { url = "https://files.pythonhosted.org/packages/0a/05/19d849cf4096448779d2dcc9bb27d097457dac36f7273ffa875a93b5884c/cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e", size = 3981078, upload-time = "2026-01-28T00:23:17.838Z" }, - { url = "https://files.pythonhosted.org/packages/e6/89/f7bac81d66ba7cde867a743ea5b37537b32b5c633c473002b26a226f703f/cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c", size = 4276213, upload-time = "2026-01-28T00:23:19.257Z" }, - { url = "https://files.pythonhosted.org/packages/da/9f/7133e41f24edd827020ad21b068736e792bc68eecf66d93c924ad4719fb3/cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32", size = 4912190, upload-time = "2026-01-28T00:23:21.244Z" }, - { url = "https://files.pythonhosted.org/packages/a6/f7/6d43cbaddf6f65b24816e4af187d211f0bc536a29961f69faedc48501d8e/cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616", size = 4454641, upload-time = "2026-01-28T00:23:22.866Z" }, - { url = "https://files.pythonhosted.org/packages/9e/4f/ebd0473ad656a0ac912a16bd07db0f5d85184924e14fc88feecae2492834/cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0", size = 4405159, upload-time = "2026-01-28T00:23:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d1/f7/7923886f32dc47e27adeff8246e976d77258fd2aa3efdd1754e4e323bf49/cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0", size = 4666059, upload-time = "2026-01-28T00:23:26.766Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a7/0fca0fd3591dffc297278a61813d7f661a14243dd60f499a7a5b48acb52a/cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5", size = 3026378, upload-time = "2026-01-28T00:23:28.317Z" }, - { url = "https://files.pythonhosted.org/packages/2d/12/652c84b6f9873f0909374864a57b003686c642ea48c84d6c7e2c515e6da5/cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b", size = 3478614, upload-time = "2026-01-28T00:23:30.275Z" }, - { url = "https://files.pythonhosted.org/packages/b9/27/542b029f293a5cce59349d799d4d8484b3b1654a7b9a0585c266e974a488/cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908", size = 7116417, upload-time = "2026-01-28T00:23:31.958Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f5/559c25b77f40b6bf828eabaf988efb8b0e17b573545edb503368ca0a2a03/cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da", size = 4264508, upload-time = "2026-01-28T00:23:34.264Z" }, - { url = "https://files.pythonhosted.org/packages/49/a1/551fa162d33074b660dc35c9bc3616fefa21a0e8c1edd27b92559902e408/cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829", size = 4409080, upload-time = "2026-01-28T00:23:35.793Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/4d8d129a755f5d6df1bbee69ea2f35ebfa954fa1847690d1db2e8bca46a5/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2", size = 4270039, upload-time = "2026-01-28T00:23:37.263Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f5/ed3fcddd0a5e39321e595e144615399e47e7c153a1fb8c4862aec3151ff9/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085", size = 4926748, upload-time = "2026-01-28T00:23:38.884Z" }, - { url = "https://files.pythonhosted.org/packages/43/ae/9f03d5f0c0c00e85ecb34f06d3b79599f20630e4db91b8a6e56e8f83d410/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b", size = 4442307, upload-time = "2026-01-28T00:23:40.56Z" }, - { url = "https://files.pythonhosted.org/packages/8b/22/e0f9f2dae8040695103369cf2283ef9ac8abe4d51f68710bec2afd232609/cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd", size = 3959253, upload-time = "2026-01-28T00:23:42.827Z" }, - { url = "https://files.pythonhosted.org/packages/01/5b/6a43fcccc51dae4d101ac7d378a8724d1ba3de628a24e11bf2f4f43cba4d/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2", size = 4269372, upload-time = "2026-01-28T00:23:44.655Z" }, - { url = "https://files.pythonhosted.org/packages/17/b7/0f6b8c1dd0779df2b526e78978ff00462355e31c0a6f6cff8a3e99889c90/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e", size = 4891908, upload-time = "2026-01-28T00:23:46.48Z" }, - { url = "https://files.pythonhosted.org/packages/83/17/259409b8349aa10535358807a472c6a695cf84f106022268d31cea2b6c97/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f", size = 4441254, upload-time = "2026-01-28T00:23:48.403Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fe/e4a1b0c989b00cee5ffa0764401767e2d1cf59f45530963b894129fd5dce/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82", size = 4396520, upload-time = "2026-01-28T00:23:50.26Z" }, - { url = "https://files.pythonhosted.org/packages/b3/81/ba8fd9657d27076eb40d6a2f941b23429a3c3d2f56f5a921d6b936a27bc9/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c", size = 4651479, upload-time = "2026-01-28T00:23:51.674Z" }, - { url = "https://files.pythonhosted.org/packages/00/03/0de4ed43c71c31e4fe954edd50b9d28d658fef56555eba7641696370a8e2/cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061", size = 3001986, upload-time = "2026-01-28T00:23:53.485Z" }, - { url = "https://files.pythonhosted.org/packages/5c/70/81830b59df7682917d7a10f833c4dab2a5574cd664e86d18139f2b421329/cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7", size = 3468288, upload-time = "2026-01-28T00:23:55.09Z" }, - { url = "https://files.pythonhosted.org/packages/56/f7/f648fdbb61d0d45902d3f374217451385edc7e7768d1b03ff1d0e5ffc17b/cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab", size = 7169583, upload-time = "2026-01-28T00:23:56.558Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cc/8f3224cbb2a928de7298d6ed4790f5ebc48114e02bdc9559196bfb12435d/cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef", size = 4275419, upload-time = "2026-01-28T00:23:58.364Z" }, - { url = "https://files.pythonhosted.org/packages/17/43/4a18faa7a872d00e4264855134ba82d23546c850a70ff209e04ee200e76f/cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d", size = 4419058, upload-time = "2026-01-28T00:23:59.867Z" }, - { url = "https://files.pythonhosted.org/packages/ee/64/6651969409821d791ba12346a124f55e1b76f66a819254ae840a965d4b9c/cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973", size = 4278151, upload-time = "2026-01-28T00:24:01.731Z" }, - { url = "https://files.pythonhosted.org/packages/20/0b/a7fce65ee08c3c02f7a8310cc090a732344066b990ac63a9dfd0a655d321/cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4", size = 4939441, upload-time = "2026-01-28T00:24:03.175Z" }, - { url = "https://files.pythonhosted.org/packages/db/a7/20c5701e2cd3e1dfd7a19d2290c522a5f435dd30957d431dcb531d0f1413/cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af", size = 4451617, upload-time = "2026-01-28T00:24:05.403Z" }, - { url = "https://files.pythonhosted.org/packages/00/dc/3e16030ea9aa47b63af6524c354933b4fb0e352257c792c4deeb0edae367/cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263", size = 3977774, upload-time = "2026-01-28T00:24:06.851Z" }, - { url = "https://files.pythonhosted.org/packages/42/c8/ad93f14118252717b465880368721c963975ac4b941b7ef88f3c56bf2897/cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095", size = 4277008, upload-time = "2026-01-28T00:24:08.926Z" }, - { url = "https://files.pythonhosted.org/packages/00/cf/89c99698151c00a4631fbfcfcf459d308213ac29e321b0ff44ceeeac82f1/cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b", size = 4903339, upload-time = "2026-01-28T00:24:12.009Z" }, - { url = "https://files.pythonhosted.org/packages/03/c3/c90a2cb358de4ac9309b26acf49b2a100957e1ff5cc1e98e6c4996576710/cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019", size = 4451216, upload-time = "2026-01-28T00:24:13.975Z" }, - { url = "https://files.pythonhosted.org/packages/96/2c/8d7f4171388a10208671e181ca43cdc0e596d8259ebacbbcfbd16de593da/cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4", size = 4404299, upload-time = "2026-01-28T00:24:16.169Z" }, - { url = "https://files.pythonhosted.org/packages/e9/23/cbb2036e450980f65c6e0a173b73a56ff3bccd8998965dea5cc9ddd424a5/cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b", size = 4664837, upload-time = "2026-01-28T00:24:17.629Z" }, - { url = "https://files.pythonhosted.org/packages/0a/21/f7433d18fe6d5845329cbdc597e30caf983229c7a245bcf54afecc555938/cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc", size = 3009779, upload-time = "2026-01-28T00:24:20.198Z" }, - { url = "https://files.pythonhosted.org/packages/3a/6a/bd2e7caa2facffedf172a45c1a02e551e6d7d4828658c9a245516a598d94/cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976", size = 3466633, upload-time = "2026-01-28T00:24:21.851Z" }, -] - -[[package]] -name = "cyclopts" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "docstring-parser" }, - { name = "rich" }, - { name = "rich-rst" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d4/93/6085aa89c3fff78a5180987354538d72e43b0db27e66a959302d0c07821a/cyclopts-4.5.1.tar.gz", hash = "sha256:fadc45304763fd9f5d6033727f176898d17a1778e194436964661a005078a3dd", size = 162075, upload-time = "2026-01-25T15:23:54.07Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/7c/996760c30f1302704af57c66ff2d723f7d656d0d0b93563b5528a51484bb/cyclopts-4.5.1-py3-none-any.whl", hash = "sha256:0642c93601e554ca6b7b9abd81093847ea4448b2616280f2a0952416574e8c7a", size = 199807, upload-time = "2026-01-25T15:23:55.219Z" }, -] - -[[package]] -name = "diskcache" -version = "5.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, -] - -[[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.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, -] - -[[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.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "fakeredis" -version = "2.33.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "redis" }, - { name = "sortedcontainers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/f9/57464119936414d60697fcbd32f38909bb5688b616ae13de6e98384433e0/fakeredis-2.33.0.tar.gz", hash = "sha256:d7bc9a69d21df108a6451bbffee23b3eba432c21a654afc7ff2d295428ec5770", size = 175187, upload-time = "2025-12-16T19:45:52.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/78/a850fed8aeef96d4a99043c90b818b2ed5419cd5b24a4049fd7cfb9f1471/fakeredis-2.33.0-py3-none-any.whl", hash = "sha256:de535f3f9ccde1c56672ab2fdd6a8efbc4f2619fc2f1acc87b8737177d71c965", size = 119605, upload-time = "2025-12-16T19:45:51.08Z" }, -] - -[package.optional-dependencies] -lua = [ - { name = "lupa" }, -] - -[[package]] -name = "fastmcp" -version = "2.14.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "authlib" }, - { name = "cyclopts" }, - { name = "exceptiongroup" }, - { name = "httpx" }, - { name = "jsonref" }, - { name = "jsonschema-path" }, - { name = "mcp" }, - { name = "openapi-pydantic" }, - { name = "packaging" }, - { name = "platformdirs" }, - { name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] }, - { name = "pydantic", extra = ["email"] }, - { name = "pydocket" }, - { name = "pyperclip" }, - { name = "python-dotenv" }, - { name = "rich" }, - { name = "uvicorn" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3b/32/982678d44f13849530a74ab101ed80e060c2ee6cf87471f062dcf61705fd/fastmcp-2.14.5.tar.gz", hash = "sha256:38944dc582c541d55357082bda2241cedb42cd3a78faea8a9d6a2662c62a42d7", size = 8296329, upload-time = "2026-02-03T15:35:21.005Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/c1/1a35ec68ff76ea8443aa115b18bcdee748a4ada2124537ee90522899ff9f/fastmcp-2.14.5-py3-none-any.whl", hash = "sha256:d81e8ec813f5089d3624bec93944beaefa86c0c3a4ef1111cbef676a761ebccf", size = 417784, upload-time = "2026-02-03T15:35:18.489Z" }, -] - -[[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 = "hanzo-tools-core" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a9/67/eabf2d355819b9c948a9898ed537fa014a5de0422de66e0ea4203faae6be/hanzo_tools_core-0.2.0.tar.gz", hash = "sha256:ab5352056d3db1d42aadd94d81635eb835476194562b07b497f327f6d334c058", size = 11441, upload-time = "2025-12-26T14:46:12.281Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/16/c1a705434afc5936156eadedcc29e1200fb4871e7a1b83d5efeebefbfcda/hanzo_tools_core-0.2.0-py3-none-any.whl", hash = "sha256:e07cdc3692003e30a40082be3750a0670b2adf612e3e7a56dd741d3b532b8090", size = 11026, upload-time = "2025-12-26T14:46:11.514Z" }, -] - -[[package]] -name = "hanzo-tools-plan" -version = "0.1.0" -source = { editable = "." } -dependencies = [ - { name = "hanzo-tools-core" }, - { name = "mcp" }, -] - -[package.optional-dependencies] -dev = [ - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "hanzo-tools-core", specifier = ">=0.1.0" }, - { name = "mcp", specifier = ">=1.0.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, - { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.0" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" }, -] -provides-extras = ["dev"] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, -] - -[[package]] -name = "jaraco-context" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" }, -] - -[[package]] -name = "jaraco-functools" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, -] - -[[package]] -name = "jsonref" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[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.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "keyring" -version = "25.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, -] - -[[package]] -name = "lupa" -version = "2.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/1c/191c3e6ec6502e3dbe25a53e27f69a5daeac3e56de1f73c0138224171ead/lupa-2.6.tar.gz", hash = "sha256:9a770a6e89576be3447668d7ced312cd6fd41d3c13c2462c9dc2c2ab570e45d9", size = 7240282, upload-time = "2025-10-24T07:20:29.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/86/ce243390535c39d53ea17ccf0240815e6e457e413e40428a658ea4ee4b8d/lupa-2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47ce718817ef1cc0c40d87c3d5ae56a800d61af00fbc0fad1ca9be12df2f3b56", size = 951707, upload-time = "2025-10-24T07:18:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/cedea5e6cbeb54396fdcc55f6b741696f3f036d23cfaf986d50d680446da/lupa-2.6-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:7aba985b15b101495aa4b07112cdc08baa0c545390d560ad5cfde2e9e34f4d58", size = 1916703, upload-time = "2025-10-24T07:18:05.6Z" }, - { url = "https://files.pythonhosted.org/packages/24/be/3d6b5f9a8588c01a4d88129284c726017b2089f3a3fd3ba8bd977292fea0/lupa-2.6-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:b766f62f95b2739f2248977d29b0722e589dcf4f0ccfa827ccbd29f0148bd2e5", size = 985152, upload-time = "2025-10-24T07:18:08.561Z" }, - { url = "https://files.pythonhosted.org/packages/eb/23/9f9a05beee5d5dce9deca4cb07c91c40a90541fc0a8e09db4ee670da550f/lupa-2.6-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:00a934c23331f94cb51760097ebfab14b005d55a6b30a2b480e3c53dd2fa290d", size = 1159599, upload-time = "2025-10-24T07:18:10.346Z" }, - { url = "https://files.pythonhosted.org/packages/40/4e/e7c0583083db9d7f1fd023800a9767d8e4391e8330d56c2373d890ac971b/lupa-2.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21de9f38bd475303e34a042b7081aabdf50bd9bafd36ce4faea2f90fd9f15c31", size = 1038686, upload-time = "2025-10-24T07:18:12.112Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/5a4f7d959d4feba5e203ff0c31889e74d1ca3153122be4a46dca7d92bf7c/lupa-2.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf3bda96d3fc41237e964a69c23647d50d4e28421111360274d4799832c560e9", size = 2071956, upload-time = "2025-10-24T07:18:14.572Z" }, - { url = "https://files.pythonhosted.org/packages/92/34/2f4f13ca65d01169b1720176aedc4af17bc19ee834598c7292db232cb6dc/lupa-2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a76ead245da54801a81053794aa3975f213221f6542d14ec4b859ee2e7e0323", size = 1057199, upload-time = "2025-10-24T07:18:16.379Z" }, - { url = "https://files.pythonhosted.org/packages/35/2a/5f7d2eebec6993b0dcd428e0184ad71afb06a45ba13e717f6501bfed1da3/lupa-2.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8dd0861741caa20886ddbda0a121d8e52fb9b5bb153d82fa9bba796962bf30e8", size = 1173693, upload-time = "2025-10-24T07:18:18.153Z" }, - { url = "https://files.pythonhosted.org/packages/e4/29/089b4d2f8e34417349af3904bb40bec40b65c8731f45e3fd8d497ca573e5/lupa-2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:239e63948b0b23023f81d9a19a395e768ed3da6a299f84e7963b8f813f6e3f9c", size = 2164394, upload-time = "2025-10-24T07:18:20.403Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1b/79c17b23c921f81468a111cad843b076a17ef4b684c4a8dff32a7969c3f0/lupa-2.6-cp312-cp312-win32.whl", hash = "sha256:325894e1099499e7a6f9c351147661a2011887603c71086d36fe0f964d52d1ce", size = 1420647, upload-time = "2025-10-24T07:18:23.368Z" }, - { url = "https://files.pythonhosted.org/packages/b8/15/5121e68aad3584e26e1425a5c9a79cd898f8a152292059e128c206ee817c/lupa-2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c735a1ce8ee60edb0fe71d665f1e6b7c55c6021f1d340eb8c865952c602cd36f", size = 1688529, upload-time = "2025-10-24T07:18:25.523Z" }, - { url = "https://files.pythonhosted.org/packages/28/1d/21176b682ca5469001199d8b95fa1737e29957a3d185186e7a8b55345f2e/lupa-2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:663a6e58a0f60e7d212017d6678639ac8df0119bc13c2145029dcba084391310", size = 947232, upload-time = "2025-10-24T07:18:27.878Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4c/d327befb684660ca13cf79cd1f1d604331808f9f1b6fb6bf57832f8edf80/lupa-2.6-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:d1f5afda5c20b1f3217a80e9bc1b77037f8a6eb11612fd3ada19065303c8f380", size = 1908625, upload-time = "2025-10-24T07:18:29.944Z" }, - { url = "https://files.pythonhosted.org/packages/66/8e/ad22b0a19454dfd08662237a84c792d6d420d36b061f239e084f29d1a4f3/lupa-2.6-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:26f2b3c085fe76e9119e48c1013c1cccdc1f51585d456858290475aa38e7089e", size = 981057, upload-time = "2025-10-24T07:18:31.553Z" }, - { url = "https://files.pythonhosted.org/packages/5c/48/74859073ab276bd0566c719f9ca0108b0cfc1956ca0d68678d117d47d155/lupa-2.6-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:60d2f902c7b96fb8ab98493dcff315e7bb4d0b44dc9dd76eb37de575025d5685", size = 1156227, upload-time = "2025-10-24T07:18:33.981Z" }, - { url = "https://files.pythonhosted.org/packages/09/6c/0e9ded061916877253c2266074060eb71ed99fb21d73c8c114a76725bce2/lupa-2.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a02d25dee3a3250967c36590128d9220ae02f2eda166a24279da0b481519cbff", size = 1035752, upload-time = "2025-10-24T07:18:36.32Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ef/f8c32e454ef9f3fe909f6c7d57a39f950996c37a3deb7b391fec7903dab7/lupa-2.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eae1ee16b886b8914ff292dbefbf2f48abfbdee94b33a88d1d5475e02423203", size = 2069009, upload-time = "2025-10-24T07:18:38.072Z" }, - { url = "https://files.pythonhosted.org/packages/53/dc/15b80c226a5225815a890ee1c11f07968e0aba7a852df41e8ae6fe285063/lupa-2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0edd5073a4ee74ab36f74fe61450148e6044f3952b8d21248581f3c5d1a58be", size = 1056301, upload-time = "2025-10-24T07:18:40.165Z" }, - { url = "https://files.pythonhosted.org/packages/31/14/2086c1425c985acfb30997a67e90c39457122df41324d3c179d6ee2292c6/lupa-2.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c53ee9f22a8a17e7d4266ad48e86f43771951797042dd51d1494aaa4f5f3f0a", size = 1170673, upload-time = "2025-10-24T07:18:42.426Z" }, - { url = "https://files.pythonhosted.org/packages/10/e5/b216c054cf86576c0191bf9a9f05de6f7e8e07164897d95eea0078dca9b2/lupa-2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de7c0f157a9064a400d828789191a96da7f4ce889969a588b87ec80de9b14772", size = 2162227, upload-time = "2025-10-24T07:18:46.112Z" }, - { url = "https://files.pythonhosted.org/packages/59/2f/33ecb5bedf4f3bc297ceacb7f016ff951331d352f58e7e791589609ea306/lupa-2.6-cp313-cp313-win32.whl", hash = "sha256:ee9523941ae0a87b5b703417720c5d78f72d2f5bc23883a2ea80a949a3ed9e75", size = 1419558, upload-time = "2025-10-24T07:18:48.371Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b4/55e885834c847ea610e111d87b9ed4768f0afdaeebc00cd46810f25029f6/lupa-2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b1335a5835b0a25ebdbc75cf0bda195e54d133e4d994877ef025e218c2e59db9", size = 1683424, upload-time = "2025-10-24T07:18:50.976Z" }, - { url = "https://files.pythonhosted.org/packages/66/9d/d9427394e54d22a35d1139ef12e845fd700d4872a67a34db32516170b746/lupa-2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dcb6d0a3264873e1653bc188499f48c1fb4b41a779e315eba45256cfe7bc33c1", size = 953818, upload-time = "2025-10-24T07:18:53.378Z" }, - { url = "https://files.pythonhosted.org/packages/10/41/27bbe81953fb2f9ecfced5d9c99f85b37964cfaf6aa8453bb11283983721/lupa-2.6-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:a37e01f2128f8c36106726cb9d360bac087d58c54b4522b033cc5691c584db18", size = 1915850, upload-time = "2025-10-24T07:18:55.259Z" }, - { url = "https://files.pythonhosted.org/packages/a3/98/f9ff60db84a75ba8725506bbf448fb085bc77868a021998ed2a66d920568/lupa-2.6-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:458bd7e9ff3c150b245b0fcfbb9bd2593d1152ea7f0a7b91c1d185846da033fe", size = 982344, upload-time = "2025-10-24T07:18:57.05Z" }, - { url = "https://files.pythonhosted.org/packages/41/f7/f39e0f1c055c3b887d86b404aaf0ca197b5edfd235a8b81b45b25bac7fc3/lupa-2.6-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:052ee82cac5206a02df77119c325339acbc09f5ce66967f66a2e12a0f3211cad", size = 1156543, upload-time = "2025-10-24T07:18:59.251Z" }, - { url = "https://files.pythonhosted.org/packages/9e/9c/59e6cffa0d672d662ae17bd7ac8ecd2c89c9449dee499e3eb13ca9cd10d9/lupa-2.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96594eca3c87dd07938009e95e591e43d554c1dbd0385be03c100367141db5a8", size = 1047974, upload-time = "2025-10-24T07:19:01.449Z" }, - { url = "https://files.pythonhosted.org/packages/23/c6/a04e9cef7c052717fcb28fb63b3824802488f688391895b618e39be0f684/lupa-2.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8faddd9d198688c8884091173a088a8e920ecc96cda2ffed576a23574c4b3f6", size = 2073458, upload-time = "2025-10-24T07:19:03.369Z" }, - { url = "https://files.pythonhosted.org/packages/e6/10/824173d10f38b51fc77785228f01411b6ca28826ce27404c7c912e0e442c/lupa-2.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:daebb3a6b58095c917e76ba727ab37b27477fb926957c825205fbda431552134", size = 1067683, upload-time = "2025-10-24T07:19:06.2Z" }, - { url = "https://files.pythonhosted.org/packages/b6/dc/9692fbcf3c924d9c4ece2d8d2f724451ac2e09af0bd2a782db1cef34e799/lupa-2.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f3154e68972befe0f81564e37d8142b5d5d79931a18309226a04ec92487d4ea3", size = 1171892, upload-time = "2025-10-24T07:19:08.544Z" }, - { url = "https://files.pythonhosted.org/packages/84/ff/e318b628d4643c278c96ab3ddea07fc36b075a57383c837f5b11e537ba9d/lupa-2.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e4dadf77b9fedc0bfa53417cc28dc2278a26d4cbd95c29f8927ad4d8fe0a7ef9", size = 2166641, upload-time = "2025-10-24T07:19:10.485Z" }, - { url = "https://files.pythonhosted.org/packages/12/f7/a6f9ec2806cf2d50826980cdb4b3cffc7691dc6f95e13cc728846d5cb793/lupa-2.6-cp314-cp314-win32.whl", hash = "sha256:cb34169c6fa3bab3e8ac58ca21b8a7102f6a94b6a5d08d3636312f3f02fafd8f", size = 1456857, upload-time = "2025-10-24T07:19:37.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/de/df71896f25bdc18360fdfa3b802cd7d57d7fede41a0e9724a4625b412c85/lupa-2.6-cp314-cp314-win_amd64.whl", hash = "sha256:b74f944fe46c421e25d0f8692aef1e842192f6f7f68034201382ac440ef9ea67", size = 1731191, upload-time = "2025-10-24T07:19:40.281Z" }, - { url = "https://files.pythonhosted.org/packages/47/3c/a1f23b01c54669465f5f4c4083107d496fbe6fb45998771420e9aadcf145/lupa-2.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0e21b716408a21ab65723f8841cf7f2f37a844b7a965eeabb785e27fca4099cf", size = 999343, upload-time = "2025-10-24T07:19:12.519Z" }, - { url = "https://files.pythonhosted.org/packages/c5/6d/501994291cb640bfa2ccf7f554be4e6914afa21c4026bd01bff9ca8aac57/lupa-2.6-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:589db872a141bfff828340079bbdf3e9a31f2689f4ca0d88f97d9e8c2eae6142", size = 2000730, upload-time = "2025-10-24T07:19:14.869Z" }, - { url = "https://files.pythonhosted.org/packages/53/a5/457ffb4f3f20469956c2d4c4842a7675e884efc895b2f23d126d23e126cc/lupa-2.6-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:cd852a91a4a9d4dcbb9a58100f820a75a425703ec3e3f049055f60b8533b7953", size = 1021553, upload-time = "2025-10-24T07:19:17.123Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/36bb5a5d0960f2a5c7c700e0819abb76fd9bf9c1d8a66e5106416d6e9b14/lupa-2.6-cp314-cp314t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:0334753be028358922415ca97a64a3048e4ed155413fc4eaf87dd0a7e2752983", size = 1133275, upload-time = "2025-10-24T07:19:20.51Z" }, - { url = "https://files.pythonhosted.org/packages/19/86/202ff4429f663013f37d2229f6176ca9f83678a50257d70f61a0a97281bf/lupa-2.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:661d895cd38c87658a34780fac54a690ec036ead743e41b74c3fb81a9e65a6aa", size = 1038441, upload-time = "2025-10-24T07:19:22.509Z" }, - { url = "https://files.pythonhosted.org/packages/a7/42/d8125f8e420714e5b52e9c08d88b5329dfb02dcca731b4f21faaee6cc5b5/lupa-2.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aa58454ccc13878cc177c62529a2056be734da16369e451987ff92784994ca7", size = 2058324, upload-time = "2025-10-24T07:19:24.979Z" }, - { url = "https://files.pythonhosted.org/packages/2b/2c/47bf8b84059876e877a339717ddb595a4a7b0e8740bacae78ba527562e1c/lupa-2.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1425017264e470c98022bba8cff5bd46d054a827f5df6b80274f9cc71dafd24f", size = 1060250, upload-time = "2025-10-24T07:19:27.262Z" }, - { url = "https://files.pythonhosted.org/packages/c2/06/d88add2b6406ca1bdec99d11a429222837ca6d03bea42ca75afa169a78cb/lupa-2.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:224af0532d216e3105f0a127410f12320f7c5f1aa0300bdf9646b8d9afb0048c", size = 1151126, upload-time = "2025-10-24T07:19:29.522Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a0/89e6a024c3b4485b89ef86881c9d55e097e7cb0bdb74efb746f2fa6a9a76/lupa-2.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9abb98d5a8fd27c8285302e82199f0e56e463066f88f619d6594a450bf269d80", size = 2153693, upload-time = "2025-10-24T07:19:31.379Z" }, - { url = "https://files.pythonhosted.org/packages/b6/36/a0f007dc58fc1bbf51fb85dcc82fcb1f21b8c4261361de7dab0e3d8521ef/lupa-2.6-cp314-cp314t-win32.whl", hash = "sha256:1849efeba7a8f6fb8aa2c13790bee988fd242ae404bd459509640eeea3d1e291", size = 1590104, upload-time = "2025-10-24T07:19:33.514Z" }, - { url = "https://files.pythonhosted.org/packages/7d/5e/db903ce9cf82c48d6b91bf6d63ae4c8d0d17958939a4e04ba6b9f38b8643/lupa-2.6-cp314-cp314t-win_amd64.whl", hash = "sha256:fc1498d1a4fc028bc521c26d0fad4ca00ed63b952e32fb95949bda76a04bad52", size = 1913818, upload-time = "2025-10-24T07:19:36.039Z" }, -] - -[[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 = "mcp" -version = "1.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyjwt", extra = ["crypto"] }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, -] - -[[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 = "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 = "opentelemetry-api" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, -] - -[[package]] -name = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[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 = "pathvalidate" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262, upload-time = "2025-06-15T09:07:20.736Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305, upload-time = "2025-06-15T09:07:19.117Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, -] - -[[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 = "prometheus-client" -version = "0.24.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616, upload-time = "2026-01-14T15:26:26.965Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057, upload-time = "2026-01-14T15:26:24.42Z" }, -] - -[[package]] -name = "py-key-value-aio" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "py-key-value-shared" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/ce/3136b771dddf5ac905cc193b461eb67967cf3979688c6696e1f2cdcde7ea/py_key_value_aio-0.3.0.tar.gz", hash = "sha256:858e852fcf6d696d231266da66042d3355a7f9871650415feef9fca7a6cd4155", size = 50801, upload-time = "2025-11-17T16:50:04.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/10/72f6f213b8f0bce36eff21fda0a13271834e9eeff7f9609b01afdc253c79/py_key_value_aio-0.3.0-py3-none-any.whl", hash = "sha256:1c781915766078bfd608daa769fefb97e65d1d73746a3dfb640460e322071b64", size = 96342, upload-time = "2025-11-17T16:50:03.801Z" }, -] - -[package.optional-dependencies] -disk = [ - { name = "diskcache" }, - { name = "pathvalidate" }, -] -keyring = [ - { name = "keyring" }, -] -memory = [ - { name = "cachetools" }, -] -redis = [ - { name = "redis" }, -] - -[[package]] -name = "py-key-value-shared" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/e4/1971dfc4620a3a15b4579fe99e024f5edd6e0967a71154771a059daff4db/py_key_value_shared-0.3.0.tar.gz", hash = "sha256:8fdd786cf96c3e900102945f92aa1473138ebe960ef49da1c833790160c28a4b", size = 11666, upload-time = "2025-11-17T16:50:06.849Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e4/b8b0a03ece72f47dce2307d36e1c34725b7223d209fc679315ffe6a4e2c3/py_key_value_shared-0.3.0-py3-none-any.whl", hash = "sha256:5b0efba7ebca08bb158b1e93afc2f07d30b8f40c2fc12ce24a4c0d84f42f9298", size = 19560, upload-time = "2025-11-17T16:50:05.954Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[package.optional-dependencies] -email = [ - { name = "email-validator" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, -] - -[[package]] -name = "pydantic-settings" -version = "2.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, -] - -[[package]] -name = "pydocket" -version = "0.17.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cloudpickle" }, - { name = "croniter" }, - { name = "fakeredis", extra = ["lua"] }, - { name = "opentelemetry-api" }, - { name = "prometheus-client" }, - { name = "py-key-value-aio", extra = ["memory", "redis"] }, - { name = "python-json-logger" }, - { name = "redis" }, - { name = "rich" }, - { name = "typer" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/73/26/ac23ead3725475468b50b486939bf5feda27180050a614a7407344a0af0e/pydocket-0.17.5.tar.gz", hash = "sha256:19a6976d8fd11c1acf62feb0291a339e06beaefa100f73dd38c6499760ad3e62", size = 334829, upload-time = "2026-01-30T18:44:39.702Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/98/73427d065c067a99de6afbe24df3d90cf20d63152ceb42edff2b6e829d4c/pydocket-0.17.5-py3-none-any.whl", hash = "sha256:544d7c2625a33e52528ac24db25794841427dfc2cf30b9c558ac387c77746241", size = 93355, upload-time = "2026-01-30T18:44:37.972Z" }, -] - -[[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 = "pyjwt" -version = "2.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, -] - -[package.optional-dependencies] -crypto = [ - { name = "cryptography" }, -] - -[[package]] -name = "pyperclip" -version = "1.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, -] - -[[package]] -name = "pytest" -version = "9.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, -] - -[[package]] -name = "pytest-asyncio" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, -] - -[[package]] -name = "python-json-logger" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, -] - -[[package]] -name = "pytz" -version = "2025.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, -] - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { 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 = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "redis" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/c8/983d5c6579a411d8a99bc5823cc5712768859b5ce2c8afe1a65b37832c81/redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c", size = 4796669, upload-time = "2025-11-19T15:54:39.961Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/f0/8956f8a86b20d7bb9d6ac0187cf4cd54d8065bc9a1a09eb8011d4d326596/redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b", size = 354159, upload-time = "2025-11-19T15:54:38.064Z" }, -] - -[[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 = "rich" -version = "14.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" }, -] - -[[package]] -name = "rich-rst" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, -] - -[[package]] -name = "ruff" -version = "0.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c8/39/5cee96809fbca590abea6b46c6d1c586b49663d1d2830a751cc8fc42c666/ruff-0.15.0.tar.gz", hash = "sha256:6bdea47cdbea30d40f8f8d7d69c0854ba7c15420ec75a26f463290949d7f7e9a", size = 4524893, upload-time = "2026-02-03T17:53:35.357Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/88/3fd1b0aa4b6330d6aaa63a285bc96c9f71970351579152d231ed90914586/ruff-0.15.0-py3-none-linux_armv6l.whl", hash = "sha256:aac4ebaa612a82b23d45964586f24ae9bc23ca101919f5590bdb368d74ad5455", size = 10354332, upload-time = "2026-02-03T17:52:54.892Z" }, - { url = "https://files.pythonhosted.org/packages/72/f6/62e173fbb7eb75cc29fe2576a1e20f0a46f671a2587b5f604bfb0eaf5f6f/ruff-0.15.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dcd4be7cc75cfbbca24a98d04d0b9b36a270d0833241f776b788d59f4142b14d", size = 10767189, upload-time = "2026-02-03T17:53:19.778Z" }, - { url = "https://files.pythonhosted.org/packages/99/e4/968ae17b676d1d2ff101d56dc69cf333e3a4c985e1ec23803df84fc7bf9e/ruff-0.15.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d747e3319b2bce179c7c1eaad3d884dc0a199b5f4d5187620530adf9105268ce", size = 10075384, upload-time = "2026-02-03T17:53:29.241Z" }, - { url = "https://files.pythonhosted.org/packages/a2/bf/9843c6044ab9e20af879c751487e61333ca79a2c8c3058b15722386b8cae/ruff-0.15.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:650bd9c56ae03102c51a5e4b554d74d825ff3abe4db22b90fd32d816c2e90621", size = 10481363, upload-time = "2026-02-03T17:52:43.332Z" }, - { url = "https://files.pythonhosted.org/packages/55/d9/4ada5ccf4cd1f532db1c8d44b6f664f2208d3d93acbeec18f82315e15193/ruff-0.15.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6664b7eac559e3048223a2da77769c2f92b43a6dfd4720cef42654299a599c9", size = 10187736, upload-time = "2026-02-03T17:53:00.522Z" }, - { url = "https://files.pythonhosted.org/packages/86/e2/f25eaecd446af7bb132af0a1d5b135a62971a41f5366ff41d06d25e77a91/ruff-0.15.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f811f97b0f092b35320d1556f3353bf238763420ade5d9e62ebd2b73f2ff179", size = 10968415, upload-time = "2026-02-03T17:53:15.705Z" }, - { url = "https://files.pythonhosted.org/packages/e7/dc/f06a8558d06333bf79b497d29a50c3a673d9251214e0d7ec78f90b30aa79/ruff-0.15.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761ec0a66680fab6454236635a39abaf14198818c8cdf691e036f4bc0f406b2d", size = 11809643, upload-time = "2026-02-03T17:53:23.031Z" }, - { url = "https://files.pythonhosted.org/packages/dd/45/0ece8db2c474ad7df13af3a6d50f76e22a09d078af63078f005057ca59eb/ruff-0.15.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:940f11c2604d317e797b289f4f9f3fa5555ffe4fb574b55ed006c3d9b6f0eb78", size = 11234787, upload-time = "2026-02-03T17:52:46.432Z" }, - { url = "https://files.pythonhosted.org/packages/8a/d9/0e3a81467a120fd265658d127db648e4d3acfe3e4f6f5d4ea79fac47e587/ruff-0.15.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcbca3d40558789126da91d7ef9a7c87772ee107033db7191edefa34e2c7f1b4", size = 11112797, upload-time = "2026-02-03T17:52:49.274Z" }, - { url = "https://files.pythonhosted.org/packages/b2/cb/8c0b3b0c692683f8ff31351dfb6241047fa873a4481a76df4335a8bff716/ruff-0.15.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9a121a96db1d75fa3eb39c4539e607f628920dd72ff1f7c5ee4f1b768ac62d6e", size = 11033133, upload-time = "2026-02-03T17:53:33.105Z" }, - { url = "https://files.pythonhosted.org/packages/f8/5e/23b87370cf0f9081a8c89a753e69a4e8778805b8802ccfe175cc410e50b9/ruff-0.15.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5298d518e493061f2eabd4abd067c7e4fb89e2f63291c94332e35631c07c3662", size = 10442646, upload-time = "2026-02-03T17:53:06.278Z" }, - { url = "https://files.pythonhosted.org/packages/e1/9a/3c94de5ce642830167e6d00b5c75aacd73e6347b4c7fc6828699b150a5ee/ruff-0.15.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afb6e603d6375ff0d6b0cee563fa21ab570fd15e65c852cb24922cef25050cf1", size = 10195750, upload-time = "2026-02-03T17:53:26.084Z" }, - { url = "https://files.pythonhosted.org/packages/30/15/e396325080d600b436acc970848d69df9c13977942fb62bb8722d729bee8/ruff-0.15.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77e515f6b15f828b94dc17d2b4ace334c9ddb7d9468c54b2f9ed2b9c1593ef16", size = 10676120, upload-time = "2026-02-03T17:53:09.363Z" }, - { url = "https://files.pythonhosted.org/packages/8d/c9/229a23d52a2983de1ad0fb0ee37d36e0257e6f28bfd6b498ee2c76361874/ruff-0.15.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6f6e80850a01eb13b3e42ee0ebdf6e4497151b48c35051aab51c101266d187a3", size = 11201636, upload-time = "2026-02-03T17:52:57.281Z" }, - { url = "https://files.pythonhosted.org/packages/6f/b0/69adf22f4e24f3677208adb715c578266842e6e6a3cc77483f48dd999ede/ruff-0.15.0-py3-none-win32.whl", hash = "sha256:238a717ef803e501b6d51e0bdd0d2c6e8513fe9eec14002445134d3907cd46c3", size = 10465945, upload-time = "2026-02-03T17:53:12.591Z" }, - { url = "https://files.pythonhosted.org/packages/51/ad/f813b6e2c97e9b4598be25e94a9147b9af7e60523b0cb5d94d307c15229d/ruff-0.15.0-py3-none-win_amd64.whl", hash = "sha256:dd5e4d3301dc01de614da3cdffc33d4b1b96fb89e45721f1598e5532ccf78b18", size = 11564657, upload-time = "2026-02-03T17:52:51.893Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b0/2d823f6e77ebe560f4e397d078487e8d52c1516b331e3521bc75db4272ca/ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a", size = 10865753, upload-time = "2026-02-03T17:53:03.014Z" }, -] - -[[package]] -name = "secretstorage" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[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 = "sortedcontainers" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, -] - -[[package]] -name = "sse-starlette" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "starlette" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/00d280c03ffd39aaee0e86ec81e2d3b9253036a0f93f51d10503adef0e65/sse_starlette-3.2.0.tar.gz", hash = "sha256:8127594edfb51abe44eac9c49e59b0b01f1039d0c7461c6fd91d4e03b70da422", size = 27253, upload-time = "2026-01-17T13:11:05.62Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/7f/832f015020844a8b8f7a9cbc103dd76ba8e3875004c41e08440ea3a2b41a/sse_starlette-3.2.0-py3-none-any.whl", hash = "sha256:5876954bd51920fc2cd51baee47a080eb88a37b5b784e615abb0b283f801cdbf", size = 12763, upload-time = "2026-01-17T13:11:03.775Z" }, -] - -[[package]] -name = "starlette" -version = "0.52.1" -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/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, -] - -[[package]] -name = "typer" -version = "0.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.40.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, -] - -[[package]] -name = "websockets" -version = "16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, - { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, - { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, - { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, - { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, - { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, - { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, - { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, - { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, - { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, -] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] diff --git a/pkg/hanzo-tools-reasoning/README.md b/pkg/hanzo-tools-reasoning/README.md deleted file mode 100644 index 1e14a9ea0..000000000 --- a/pkg/hanzo-tools-reasoning/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# hanzo-tools-reasoning - -Reasoning and analysis tools for Hanzo MCP. - -## Installation - -```bash -pip install hanzo-tools-reasoning -``` - -## Tools - -### think - Structured Reasoning -Record thoughts for complex reasoning or brainstorming. - -```python -think(thought="Analyzing the architecture: The codebase uses...") -``` - -**Use cases:** -- Bug exploration and fix brainstorming -- Test failure analysis -- Complex refactoring planning -- Feature design decisions -- Issue investigation - -### critic - Critical Analysis -Play devil's advocate and ensure high standards. - -```python -critic(analysis="Review this implementation for bugs and edge cases...") -``` - -**What it checks:** -- Potential bugs and edge cases -- Error handling gaps -- Test coverage issues -- Performance problems -- Security vulnerabilities -- Code quality and design - -**Example output:** -``` -Code Review Analysis: -- Implementation Issues: - * No error handling for network failures - * Race condition in concurrent updates - -- Test Coverage Gaps: - * No tests for error scenarios - * Missing edge case: empty input - -- Recommendations: - 1. Add retry logic with exponential backoff - 2. Use database transactions -``` - -## License - -MIT diff --git a/pkg/hanzo-tools-reasoning/hanzo_tools/__init__.py b/pkg/hanzo-tools-reasoning/hanzo_tools/__init__.py deleted file mode 100644 index 004279ba9..000000000 --- a/pkg/hanzo-tools-reasoning/hanzo_tools/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Hanzo tools namespace package.""" - -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-reasoning/hanzo_tools/reasoning/__init__.py b/pkg/hanzo-tools-reasoning/hanzo_tools/reasoning/__init__.py deleted file mode 100644 index 2ff898b0c..000000000 --- a/pkg/hanzo-tools-reasoning/hanzo_tools/reasoning/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Reasoning and critical analysis tools for Hanzo MCP. - -This package provides tools for structured reasoning: -- ThinkTool: Structured thinking and brainstorming -- CriticTool: Critical analysis and code review -""" - -from mcp.server import FastMCP - -from hanzo_tools.core import BaseTool, ToolRegistry -from hanzo_tools.reasoning.think_tool import ThinkTool -from hanzo_tools.reasoning.critic_tool import CriticTool - -__all__ = [ - "ThinkTool", - "CriticTool", - "get_reasoning_tools", - "register_reasoning_tools", - "TOOLS", -] - - -def get_reasoning_tools() -> list[BaseTool]: - """Create instances of all reasoning tools. - - Returns: - List of reasoning tool instances - """ - return [ThinkTool(), CriticTool()] - - -def register_reasoning_tools(mcp_server: FastMCP) -> list[BaseTool]: - """Register all reasoning tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - - Returns: - List of registered tools - """ - tools = get_reasoning_tools() - ToolRegistry.register_tools(mcp_server, tools) - return tools - - -# TOOLS list for entry point discovery -TOOLS = [ThinkTool, CriticTool] diff --git a/pkg/hanzo-tools-reasoning/hanzo_tools/reasoning/critic_tool.py b/pkg/hanzo-tools-reasoning/hanzo_tools/reasoning/critic_tool.py deleted file mode 100644 index 2011780ff..000000000 --- a/pkg/hanzo-tools-reasoning/hanzo_tools/reasoning/critic_tool.py +++ /dev/null @@ -1,184 +0,0 @@ -"""Critic tool implementation. - -This module provides the CriticTool for Claude to engage in critical analysis and code review. -""" - -from typing import Unpack, Annotated, TypedDict, final, override - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context - -Analysis = Annotated[ - str, - Field( - description="The critical analysis to perform - code review, error detection, or improvement suggestions", - min_length=1, - ), -] - - -class CriticToolParams(TypedDict): - """Parameters for the CriticTool. - - Attributes: - analysis: The critical analysis to perform - """ - - analysis: Analysis - - -@final -class CriticTool(BaseTool): - """Tool for Claude to engage in critical analysis and play devil's advocate.""" - - name = "critic" - - @property - @override - def description(self) -> str: - """Get the tool description. - - Returns: - Tool description - """ - return """Use this tool to perform critical analysis, play devil's advocate, and ensure high standards. -This tool forces a critical thinking mode that reviews all code for errors, improvements, and edge cases. -It ensures tests are run, tests pass, and maintains high quality standards. - -This is your inner critic that: -- Always questions assumptions -- Looks for potential bugs and edge cases -- Ensures proper error handling -- Verifies test coverage -- Checks for performance issues -- Reviews security implications -- Suggests improvements and refactoring -- Ensures code follows best practices -- Questions design decisions -- Looks for missing documentation - -Common use cases: -1. Before finalizing any code changes - review for bugs, edge cases, and improvements -2. After implementing a feature - critically analyze if it truly solves the problem -3. When tests pass too easily - question if tests are comprehensive enough -4. Before marking a task complete - ensure all quality standards are met -5. When something seems too simple - look for hidden complexity or missing requirements -6. After fixing a bug - analyze if the fix addresses root cause or just symptoms - - -Code Review Analysis: -- Implementation Issues: - * No error handling for network failures in API calls - * Missing validation for user input boundaries - * Race condition possible in concurrent updates - * Memory leak potential in event listener registration - -- Test Coverage Gaps: - * No tests for error scenarios - * Missing edge case: empty array input - * No performance benchmarks for large datasets - * Integration tests don't cover authentication failures - -- Security Concerns: - * SQL injection vulnerability in query construction - * Missing rate limiting on public endpoints - * Sensitive data logged in debug mode - -- Performance Issues: - * O(nยฒ) algorithm where O(n log n) is possible - * Database queries in a loop (N+1 problem) - * No caching for expensive computations - -- Code Quality: - * Functions too long and doing multiple things - * Inconsistent naming conventions - * Missing type annotations - * No documentation for complex algorithms - -- Design Flaws: - * Tight coupling between modules - * Hard-coded configuration values - * No abstraction for external dependencies - * Violates single responsibility principle - -Recommendations: -1. Add comprehensive error handling with retry logic -2. Implement input validation with clear error messages -3. Use database transactions to prevent race conditions -4. Add memory cleanup in component unmount -5. Parameterize SQL queries to prevent injection -6. Implement rate limiting middleware -7. Use environment variables for sensitive config -8. Refactor algorithm to use sorting approach -9. Batch database queries -10. Add memoization for expensive calculations -""" - - def __init__(self) -> None: - """Initialize the critic tool.""" - pass - - @override - @auto_timeout("critic") - async def call( - self, - ctx: MCPContext, - **params: Unpack[CriticToolParams], - ) -> str: - """Execute the tool with the given parameters. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Tool result - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - analysis = params.get("analysis") - - # Validate required analysis parameter - if not analysis: - await tool_ctx.error( - "Parameter 'analysis' is required but was None or empty" - ) - return "Error: Parameter 'analysis' is required but was None or empty" - - if analysis.strip() == "": - await tool_ctx.error("Parameter 'analysis' cannot be empty") - return "Error: Parameter 'analysis' cannot be empty" - - # Log the critical analysis - await tool_ctx.info("Critical analysis recorded") - - # Return confirmation with reminder to act on the analysis - return """Critical analysis complete. Remember to: -1. Address all identified issues before proceeding -2. Run comprehensive tests to verify fixes -3. Ensure all tests pass with proper coverage -4. Document any design decisions or trade-offs -5. Consider the analysis points in your implementation - -Continue with improvements based on this critical review.""" - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register this critic tool with the MCP server. - - Creates a wrapper function with explicitly defined parameters that match - the tool's parameter schema and registers it with the MCP server. - - Args: - mcp_server: The FastMCP server instance - """ - tool_self = self # Create a reference to self for use in the closure - - @mcp_server.tool(name=self.name, description=self.description) - async def critic(analysis: Analysis, ctx: MCPContext) -> str: - return await tool_self.call(ctx, analysis=analysis) diff --git a/pkg/hanzo-tools-reasoning/hanzo_tools/reasoning/think_tool.py b/pkg/hanzo-tools-reasoning/hanzo_tools/reasoning/think_tool.py deleted file mode 100644 index 3cdd33ab5..000000000 --- a/pkg/hanzo-tools-reasoning/hanzo_tools/reasoning/think_tool.py +++ /dev/null @@ -1,226 +0,0 @@ -"""Unified thinking tool implementation (HIP-0300). - -This module provides the ThinkTool with action-based dispatch for -all reasoning operations: think, critic, review, consensus, summarize, -classify, explain, translate, compare, chain, agent, embed. -""" - -from typing import ClassVar - -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool - - -class ThinkTool(BaseTool): - """Unified thinking and reasoning tool (HIP-0300). - - Actions: - - think: Structured thinking and brainstorming (default) - - critic: Critical analysis and devil's advocate - - review: Code review and quality analysis - - consensus: Multi-perspective consensus reasoning - - agent: Agent-style step-by-step reasoning - - summarize: Summarize content concisely - - classify: Classify content into categories - - explain: Explain a concept clearly - - translate: Translate between formats/languages - - compare: Compare multiple items - - chain: Chain-of-thought multi-step reasoning - - embed: Generate embedding representation (placeholder) - """ - - name: ClassVar[str] = "think" - VERSION: ClassVar[str] = "0.3.0" - - def __init__(self) -> None: - super().__init__() - self._register_think_actions() - - @property - def description(self) -> str: - return """Unified reasoning tool (HIP-0300). - -Actions: -- think: Structured thinking and brainstorming (default) -- critic: Critical analysis, devil's advocate -- review: Code review and quality check -- consensus: Multi-perspective reasoning -- agent: Step-by-step agent reasoning -- summarize: Summarize content -- classify: Classify into categories -- explain: Explain a concept -- translate: Translate between formats -- compare: Compare items -- chain: Chain-of-thought reasoning -- embed: Embedding representation (placeholder) -""" - - def _register_think_actions(self): - """Register all thinking actions.""" - - @self.action("think", "Structured thinking and brainstorming") - async def think(ctx: MCPContext, thought: str = "", **kwargs) -> str: - """Record a structured thought process. - - Args: - thought: The thought to record - """ - if not thought or not thought.strip(): - return "Error: 'thought' parameter is required" - return "Thinking recorded. Continue with your next action based on this analysis." - - @self.action("critic", "Critical analysis and devil's advocate") - async def critic(ctx: MCPContext, analysis: str = "", thought: str = "", **kwargs) -> str: - """Perform critical analysis. - - Args: - analysis: The critical analysis to perform - thought: Alternative param name for the analysis - """ - text = analysis or thought - if not text or not text.strip(): - return "Error: 'analysis' or 'thought' parameter is required" - return """Critical analysis complete. Remember to: -1. Address all identified issues before proceeding -2. Run comprehensive tests to verify fixes -3. Ensure all tests pass with proper coverage -4. Document any design decisions or trade-offs -5. Consider the analysis points in your implementation""" - - @self.action("review", "Code review and quality analysis") - async def review(ctx: MCPContext, code: str = "", thought: str = "", **kwargs) -> str: - """Perform code review. - - Args: - code: Code or description to review - thought: Alternative param name - """ - text = code or thought - if not text or not text.strip(): - return "Error: 'code' or 'thought' parameter is required" - return "Code review recorded. Apply findings to improve quality, correctness, and maintainability." - - @self.action("consensus", "Multi-perspective consensus reasoning") - async def consensus(ctx: MCPContext, topic: str = "", perspectives: int = 3, thought: str = "", **kwargs) -> str: - """Reason from multiple perspectives to reach consensus. - - Args: - topic: The topic to reason about - perspectives: Number of perspectives to consider - thought: Alternative param name - """ - text = topic or thought - if not text or not text.strip(): - return "Error: 'topic' or 'thought' parameter is required" - return f"Consensus reasoning recorded ({perspectives} perspectives). Use the synthesized viewpoint to guide decisions." - - @self.action("agent", "Agent-style step-by-step reasoning") - async def agent(ctx: MCPContext, goal: str = "", thought: str = "", **kwargs) -> str: - """Perform agent-style reasoning with goal decomposition. - - Args: - goal: The goal to reason about - thought: Alternative param name - """ - text = goal or thought - if not text or not text.strip(): - return "Error: 'goal' or 'thought' parameter is required" - return "Agent reasoning recorded. Execute the planned steps sequentially." - - @self.action("summarize", "Summarize content concisely") - async def summarize(ctx: MCPContext, content: str = "", thought: str = "", max_length: int = 0, **kwargs) -> str: - """Summarize content. - - Args: - content: Content to summarize - thought: Alternative param name - max_length: Optional max length hint - """ - text = content or thought - if not text or not text.strip(): - return "Error: 'content' or 'thought' parameter is required" - return "Summary recorded. Use the condensed version for communication or documentation." - - @self.action("classify", "Classify content into categories") - async def classify(ctx: MCPContext, content: str = "", categories: str = "", thought: str = "", **kwargs) -> str: - """Classify content into categories. - - Args: - content: Content to classify - categories: Comma-separated category options - thought: Alternative param name - """ - text = content or thought - if not text or not text.strip(): - return "Error: 'content' or 'thought' parameter is required" - return "Classification recorded. Apply the categorization to guide next steps." - - @self.action("explain", "Explain a concept clearly") - async def explain(ctx: MCPContext, concept: str = "", audience: str = "developer", thought: str = "", **kwargs) -> str: - """Explain a concept. - - Args: - concept: Concept to explain - audience: Target audience (developer, beginner, expert) - thought: Alternative param name - """ - text = concept or thought - if not text or not text.strip(): - return "Error: 'concept' or 'thought' parameter is required" - return f"Explanation recorded (audience: {audience}). Use for documentation or communication." - - @self.action("translate", "Translate between formats or languages") - async def translate(ctx: MCPContext, content: str = "", target: str = "", thought: str = "", **kwargs) -> str: - """Translate content between formats or languages. - - Args: - content: Content to translate - target: Target format or language - thought: Alternative param name - """ - text = content or thought - if not text or not text.strip(): - return "Error: 'content' or 'thought' parameter is required" - target_desc = f" to {target}" if target else "" - return f"Translation{target_desc} recorded. Apply the translated version." - - @self.action("compare", "Compare multiple items") - async def compare(ctx: MCPContext, items: str = "", criteria: str = "", thought: str = "", **kwargs) -> str: - """Compare items against criteria. - - Args: - items: Items to compare (comma-separated or description) - criteria: Comparison criteria - thought: Alternative param name - """ - text = items or thought - if not text or not text.strip(): - return "Error: 'items' or 'thought' parameter is required" - return "Comparison recorded. Use the analysis to make an informed decision." - - @self.action("chain", "Chain-of-thought multi-step reasoning") - async def chain(ctx: MCPContext, steps: str = "", thought: str = "", **kwargs) -> str: - """Perform chain-of-thought reasoning. - - Args: - steps: The reasoning steps - thought: Alternative param name - """ - text = steps or thought - if not text or not text.strip(): - return "Error: 'steps' or 'thought' parameter is required" - return "Chain-of-thought reasoning recorded. Follow the logical progression to reach the conclusion." - - @self.action("embed", "Generate embedding representation (placeholder)") - async def embed(ctx: MCPContext, content: str = "", thought: str = "", **kwargs) -> str: - """Placeholder for embedding generation. - - Args: - content: Content to embed - thought: Alternative param name - """ - text = content or thought - if not text or not text.strip(): - return "Error: 'content' or 'thought' parameter is required" - return "Embedding placeholder recorded. Use a dedicated embedding service for production vectors." diff --git a/pkg/hanzo-tools-reasoning/pyproject.toml b/pkg/hanzo-tools-reasoning/pyproject.toml deleted file mode 100644 index e8a88ebc5..000000000 --- a/pkg/hanzo-tools-reasoning/pyproject.toml +++ /dev/null @@ -1,25 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-reasoning" -version = "0.2.0" -description = "Reasoning and critical analysis tools for Hanzo MCP" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "tools", "reasoning", "thinking", "critic", "mcp", "ai"] -dependencies = [ - "hanzo-tools>=0.3.0", - "mcp>=1.25.0", - "pydantic>=2.12.5", -] - -[project.entry-points."hanzo.tools"] -reasoning = "hanzo_tools.reasoning:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] diff --git a/pkg/hanzo-tools-reasoning/tests/test_reasoning_tools.py b/pkg/hanzo-tools-reasoning/tests/test_reasoning_tools.py deleted file mode 100644 index cdf547304..000000000 --- a/pkg/hanzo-tools-reasoning/tests/test_reasoning_tools.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Tests for hanzo-tools-reasoning.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import reasoning - - assert reasoning is not None - - def test_import_tools(self): - from hanzo_tools.reasoning import TOOLS - - assert len(TOOLS) >= 2 # think, critic - - def test_import_think_tool(self): - from hanzo_tools.reasoning import ThinkTool - - assert ThinkTool.name == "think" - - def test_import_critic_tool(self): - from hanzo_tools.reasoning import CriticTool - - assert CriticTool.name == "critic" - - -class TestThinkTool: - """Tests for ThinkTool.""" - - @pytest.fixture - def tool(self): - from hanzo_tools.reasoning import ThinkTool - - return ThinkTool() - - def test_has_description(self, tool): - assert tool.description - assert "think" in tool.description.lower() - - -class TestCriticTool: - """Tests for CriticTool.""" - - @pytest.fixture - def tool(self): - from hanzo_tools.reasoning import CriticTool - - return CriticTool() - - def test_has_description(self, tool): - assert tool.description - assert "critic" in tool.description.lower() diff --git a/pkg/hanzo-tools-refactor/README.md b/pkg/hanzo-tools-refactor/README.md deleted file mode 100644 index 66afb1ba7..000000000 --- a/pkg/hanzo-tools-refactor/README.md +++ /dev/null @@ -1,89 +0,0 @@ -# hanzo-tools-refactor - -Advanced refactoring tools for Hanzo MCP. - -## Installation - -```bash -pip install hanzo-tools-refactor -``` - -## Tools - -### refactor - Code Refactoring -AST/LSP-based refactoring with parallel processing. - -**Rename symbol:** -```python -refactor(action="rename", file="/f.py", line=10, column=5, new_name="newName") -``` - -**Batch rename:** -```python -refactor( - action="rename_batch", - renames=[{"old": "foo", "new": "bar"}], - path="./src" -) -``` - -**Extract function:** -```python -refactor( - action="extract_function", - file="/f.py", - start_line=10, - end_line=20, - new_name="extracted_function" -) -``` - -**Extract variable:** -```python -refactor( - action="extract_variable", - file="/f.py", - line=10, - column=5, - new_name="extracted_var" -) -``` - -**Inline:** -```python -refactor(action="inline", file="/f.py", line=10, column=5) -``` - -**Move:** -```python -refactor(action="move", file="/f.py", line=10, target_file="/new.py") -``` - -**Change signature:** -```python -refactor( - action="change_signature", - file="/f.py", - line=10, - add_parameter={"name": "x", "default": "None"} -) -``` - -**Find references:** -```python -refactor(action="find_references", file="/f.py", line=10, column=5) -``` - -**Organize imports:** -```python -refactor(action="organize_imports", file="/f.py") -``` - -## Options - -- `preview=True` - Preview changes without applying -- `parallel=True` - Process files in parallel (default) - -## License - -MIT diff --git a/pkg/hanzo-tools-refactor/hanzo_tools/__init__.py b/pkg/hanzo-tools-refactor/hanzo_tools/__init__.py deleted file mode 100644 index e1b06939a..000000000 --- a/pkg/hanzo-tools-refactor/hanzo_tools/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Hanzo tools namespace package.""" - -import pkgutil - -__path__ = pkgutil.extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-refactor/hanzo_tools/refactor/__init__.py b/pkg/hanzo-tools-refactor/hanzo_tools/refactor/__init__.py deleted file mode 100644 index 67214a48a..000000000 --- a/pkg/hanzo-tools-refactor/hanzo_tools/refactor/__init__.py +++ /dev/null @@ -1,70 +0,0 @@ -"""Refactoring tools for Hanzo AI. - -Tools: -- refactor: Advanced code refactoring with LSP/AST support - -Actions: -- rename: Rename symbols across codebase -- rename_batch: Batch rename multiple symbols -- extract_function: Extract code to new function -- extract_variable: Extract expression to variable -- inline: Inline variables or functions -- move: Move symbols between files -- change_signature: Modify function signatures -- find_references: Find all symbol references -- organize_imports: Sort and organize imports - -Install: - pip install hanzo-tools-refactor - -Usage: - from hanzo_tools.refactor import register_tools, TOOLS - - # Register with MCP server - register_tools(mcp_server) - - # Or access tool directly - from hanzo_tools.refactor import RefactorTool -""" - -from hanzo_tools.refactor.refactor_tool import RefactorTool, create_refactor_tool - -# Export list for tool discovery -TOOLS = [RefactorTool] - -__all__ = [ - "RefactorTool", - "create_refactor_tool", - "register_tools", - "TOOLS", -] - - -def register_tools(mcp_server, enabled_tools: dict[str, bool] | None = None): - """Register all refactor tools with the MCP server. - - Args: - mcp_server: FastMCP server instance - enabled_tools: Dict of tool_name -> enabled state - - Returns: - List of registered tool instances - """ - from hanzo_tools.core import ToolRegistry - - enabled = enabled_tools or {} - registered = [] - - for tool_class in TOOLS: - tool_name = ( - tool_class.name - if hasattr(tool_class, "name") - else tool_class.__name__.lower() - ) - - if enabled.get(tool_name, True): # Enabled by default - tool = tool_class() - ToolRegistry.register_tool(mcp_server, tool) - registered.append(tool) - - return registered diff --git a/pkg/hanzo-tools-refactor/hanzo_tools/refactor/refactor_tool.py b/pkg/hanzo-tools-refactor/hanzo_tools/refactor/refactor_tool.py deleted file mode 100644 index c554222cc..000000000 --- a/pkg/hanzo-tools-refactor/hanzo_tools/refactor/refactor_tool.py +++ /dev/null @@ -1,2520 +0,0 @@ -"""Advanced refactoring tool using LSP and AST analysis. - -This module provides powerful code refactoring capabilities that leverage -language server protocols and tree-sitter AST parsing for accurate transformations. - -PERFORMANCE FEATURES: -- Parallel file processing with configurable concurrency -- Ripgrep integration for fast initial file scanning -- Batch file edits with atomic operations -- Smart caching to avoid redundant I/O -- Streaming results for large codebases -""" - -import os -import re -import json -import shutil -import asyncio -import logging -import subprocess -from typing import Any, Set, Dict, List, Tuple, Optional, AsyncIterator -from pathlib import Path -from collections import defaultdict -from dataclasses import field, dataclass -from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor - -from hanzo_tools.core import BaseTool -from hanzo_tools.core.types import MCPResourceDocument - -# Try importing tree-sitter for AST analysis -try: - import tree_sitter - import tree_sitter_go - import tree_sitter_rust - import tree_sitter_python - import tree_sitter_javascript - import tree_sitter_typescript - - TREESITTER_AVAILABLE = True -except ImportError: - TREESITTER_AVAILABLE = False - -logger = logging.getLogger(__name__) - -# Performance tuning constants -MAX_CONCURRENT_FILES = 32 # Max files to process in parallel -MAX_CONCURRENT_EDITS = 16 # Max files to edit in parallel -RIPGREP_BATCH_SIZE = 1000 # Max results per ripgrep call -FILE_READ_CHUNK_SIZE = 1024 * 1024 # 1MB chunks for large files - - -@dataclass -class RefactorLocation: - """Represents a location in source code.""" - - file: str - line: int - column: int - end_line: int = 0 - end_column: int = 0 - text: str = "" - context: str = "" - - -@dataclass -class RefactorChange: - """Represents a single change to be applied.""" - - file: str - line: int - column: int - end_line: int - end_column: int - old_text: str - new_text: str - description: str = "" - - -@dataclass -class RefactorResult: - """Result of a refactoring operation.""" - - success: bool - action: str - files_changed: int = 0 - changes_applied: int = 0 - changes: List[Dict[str, Any]] = field(default_factory=list) - errors: List[str] = field(default_factory=list) - preview: List[Dict[str, Any]] = field(default_factory=list) - message: str = "" - stats: Dict[str, Any] = field(default_factory=dict) - - -@dataclass -class FileCache: - """Cache for file contents to avoid redundant I/O.""" - - content: str - lines: List[str] - mtime: float - - -class RefactorTool(BaseTool): - """Advanced refactoring tool with LSP and AST support. - - PERFORMANCE OPTIMIZED for large codebases: - - Parallel file scanning with ripgrep - - Concurrent AST parsing with thread pool - - Batch file edits with atomic writes - - Smart caching to minimize I/O - - Actions: - - rename: Rename a symbol across the entire codebase - - rename_batch: Rename multiple symbols in one operation - - extract_function: Extract a code block into a new function - - extract_variable: Extract an expression into a variable - - inline: Inline a variable or function at all usage sites - - move: Move a symbol to another file - - change_signature: Modify a function's signature and update all calls - - find_references: Find all references to a symbol - - organize_imports: Sort and organize import statements - - Example usage: - - 1. Rename a function across codebase: - refactor("rename", file="main.py", line=10, column=5, new_name="betterName") - - 2. Batch rename multiple symbols: - refactor("rename_batch", renames=[ - {"old": "oldFunc", "new": "newFunc"}, - {"old": "OldClass", "new": "NewClass"} - ], path="/project/src") - - 3. Extract code to function: - refactor("extract_function", file="utils.py", start_line=20, end_line=30, - new_name="processData") - - 4. Inline a variable: - refactor("inline", file="app.py", line=15, column=8) - - 5. Find all references: - refactor("find_references", file="models.py", line=25, column=10) - """ - - name = "refactor" - description = """Advanced refactoring with LSP/AST. FAST parallel processing for large codebases. - -Actions: rename, rename_batch, extract_function, extract_variable, inline, move, change_signature, find_references, organize_imports. - -Rename symbol: refactor("rename", file="main.py", line=10, column=5, new_name="newName") -Batch rename: refactor("rename_batch", renames=[{"old": "foo", "new": "bar"}], path="./src") -Change signature: refactor("change_signature", file="f.py", line=10, add_parameter={"name": "x", "default": "None"}) -Find references: refactor("find_references", file="f.py", line=10, column=5)""" - - def __init__(self, max_workers: int = MAX_CONCURRENT_FILES): - super().__init__() - self.max_workers = max_workers - self.parsers: Dict[str, Any] = {} - self._file_cache: Dict[str, FileCache] = {} - self._cache_lock = asyncio.Lock() - self._ripgrep_available = shutil.which("rg") is not None - self._init_parsers() - - def _init_parsers(self): - """Initialize tree-sitter parsers for supported languages.""" - if not TREESITTER_AVAILABLE: - return - - language_mapping = { - ".py": (tree_sitter_python, "python"), - ".js": (tree_sitter_javascript, "javascript"), - ".jsx": (tree_sitter_javascript, "javascript"), - ".ts": (tree_sitter_typescript.typescript, "typescript"), - ".tsx": (tree_sitter_typescript.tsx, "tsx"), - ".go": (tree_sitter_go, "go"), - ".rs": (tree_sitter_rust, "rust"), - } - - for ext, (module, name) in language_mapping.items(): - try: - parser = tree_sitter.Parser() - if hasattr(module, "language"): - parser.set_language(module.language()) - self.parsers[ext] = parser - except Exception as e: - logger.debug(f"Failed to initialize parser for {ext}: {e}") - - def _get_parser(self, file_path: str) -> Optional[Any]: - """Get parser for file type.""" - ext = Path(file_path).suffix.lower() - return self.parsers.get(ext) - - def _get_language(self, file_path: str) -> str: - """Get language from file extension.""" - ext_to_lang = { - ".py": "python", - ".js": "javascript", - ".jsx": "javascript", - ".ts": "typescript", - ".tsx": "typescript", - ".go": "go", - ".rs": "rust", - ".java": "java", - ".cpp": "cpp", - ".c": "c", - ".h": "c", - ".hpp": "cpp", - ".rb": "ruby", - ".lua": "lua", - ".sol": "solidity", - } - ext = Path(file_path).suffix.lower() - return ext_to_lang.get(ext, "unknown") - - def _find_project_root(self, file_path: str) -> str: - """Find project root from file path.""" - markers = [ - ".git", - "package.json", - "go.mod", - "Cargo.toml", - "pyproject.toml", - "setup.py", - ] - path = Path(file_path).resolve() - - if path.suffix.lower() == ".go": - for parent in path.parents: - if (parent / "go.work").is_file(): - return str(parent) - - for parent in path.parents: - for marker in markers: - if (parent / marker).is_file(): - return str(parent) - return str(path.parent) - - async def _get_file_cached(self, file_path: str) -> Optional[FileCache]: - """Get file contents from cache or read from disk.""" - try: - mtime = os.path.getmtime(file_path) - - async with self._cache_lock: - if file_path in self._file_cache: - cached = self._file_cache[file_path] - if cached.mtime == mtime: - return cached - - # Read file in thread pool to not block - loop = asyncio.get_event_loop() - content = await loop.run_in_executor(None, self._read_file_sync, file_path) - if content is None: - return None - - cache_entry = FileCache( - content=content, - lines=content.split("\n"), - mtime=mtime, - ) - - async with self._cache_lock: - self._file_cache[file_path] = cache_entry - - return cache_entry - - except Exception as e: - logger.debug(f"Failed to read {file_path}: {e}") - return None - - def _read_file_sync(self, file_path: str) -> Optional[str]: - """Synchronous file read for thread pool.""" - try: - with open(file_path, "r", encoding="utf-8", errors="ignore") as f: - return f.read() - except Exception: - return None - - async def _invalidate_cache(self, file_path: str): - """Invalidate cache for a file after modification.""" - async with self._cache_lock: - self._file_cache.pop(file_path, None) - - async def run( - self, - action: str, - file: Optional[str] = None, - line: Optional[int] = None, - column: Optional[int] = None, - end_line: Optional[int] = None, - end_column: Optional[int] = None, - new_name: Optional[str] = None, - start_line: Optional[int] = None, - target_file: Optional[str] = None, - preview: bool = False, - path: Optional[str] = None, - renames: Optional[List[Dict[str, str]]] = None, - parallel: bool = True, - **kwargs, - ) -> MCPResourceDocument: - """Execute refactoring action. - - Args: - action: Refactoring action to perform - file: Source file path - line: Line number (1-indexed) - column: Column position (0-indexed) - end_line: End line for range selections - end_column: End column for range selections - new_name: New name for rename/extract operations - start_line: Start line for extract operations - target_file: Target file for move operations - preview: If True, only show what would change without applying - path: Project path for batch operations - renames: List of {old, new} pairs for batch rename - parallel: Enable parallel processing (default: True) - """ - import time - - start_time = time.time() - - valid_actions = [ - "rename", - "rename_batch", - "extract_function", - "extract_variable", - "inline", - "move", - "change_signature", - "find_references", - "organize_imports", - ] - - if action not in valid_actions: - return MCPResourceDocument( - data={ - "error": f"Invalid action. Must be one of: {', '.join(valid_actions)}" - } - ) - - # Resolve file path if provided - file_path = str(Path(file).resolve()) if file else None - if file_path and not Path(file_path).exists(): - return MCPResourceDocument(data={"error": f"File not found: {file}"}) - - # Route to appropriate handler - if action == "rename": - result = await self._rename( - file_path, line, column, new_name, preview, parallel - ) - elif action == "rename_batch": - result = await self._rename_batch( - renames or [], path or ".", preview, parallel - ) - elif action == "extract_function": - sl = start_line or line - el = end_line or line - result = await self._extract_function(file_path, sl, el, new_name, preview) - elif action == "extract_variable": - result = await self._extract_variable( - file_path, line, column, end_line, end_column, new_name, preview - ) - elif action == "inline": - result = await self._inline(file_path, line, column, preview, parallel) - elif action == "move": - result = await self._move(file_path, line, column, target_file, preview) - elif action == "change_signature": - result = await self._change_signature( - file_path, line, column, kwargs, preview - ) - elif action == "find_references": - result = await self._find_references(file_path, line, column, parallel) - elif action == "organize_imports": - result = await self._organize_imports(file_path, preview) - else: - result = RefactorResult( - success=False, - action=action, - errors=[f"Action {action} not implemented"], - ) - - # Add timing stats - elapsed = time.time() - start_time - result.stats["elapsed_seconds"] = round(elapsed, 3) - - return MCPResourceDocument(data=self._result_to_dict(result)) - - def _result_to_dict(self, result: RefactorResult) -> Dict[str, Any]: - """Convert RefactorResult to dictionary.""" - return { - "success": result.success, - "action": result.action, - "files_changed": result.files_changed, - "changes_applied": result.changes_applied, - "changes": result.changes, - "errors": result.errors, - "preview": result.preview, - "message": result.message, - "stats": result.stats, - } - - # ==================== FAST REFERENCE FINDING ==================== - - async def _find_references_ripgrep( - self, identifier: str, project_root: str, extensions: List[str] - ) -> List[RefactorLocation]: - """Use ripgrep for blazing fast reference finding.""" - if not self._ripgrep_available: - return [] - - # Build ripgrep command with word boundaries - cmd = [ - "rg", - "--json", - "--word-regexp", - "--max-count", - str(RIPGREP_BATCH_SIZE), - "--no-ignore-vcs", # Respect .gitignore - ] - - # Add file type filters - for ext in extensions: - cmd.extend(["--glob", f"*{ext}"]) - - # Exclude common non-source directories - for exclude in [ - ".git", - "node_modules", - "__pycache__", - "venv", - ".venv", - "dist", - "build", - ".tox", - ".eggs", - ]: - cmd.extend(["--glob", f"!{exclude}/**"]) - - cmd.append(identifier) - cmd.append(project_root) - - try: - proc = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, _ = await proc.communicate() - - references = [] - for line in stdout.decode("utf-8", errors="ignore").split("\n"): - if not line.strip(): - continue - try: - data = json.loads(line) - if data.get("type") == "match": - match_data = data.get("data", {}) - path_data = match_data.get("path", {}) - file_path = ( - path_data.get("text", "") - if isinstance(path_data, dict) - else str(path_data) - ) - - lines_data = match_data.get("lines", {}) - context = ( - lines_data.get("text", "").strip() - if isinstance(lines_data, dict) - else "" - ) - - line_num = match_data.get("line_number", 0) - - # Get column from submatches - submatches = match_data.get("submatches", []) - for submatch in submatches: - col = submatch.get("start", 0) - references.append( - RefactorLocation( - file=file_path, - line=line_num, - column=col, - text=identifier, - context=context, - ) - ) - except json.JSONDecodeError: - continue - - return references - - except Exception as e: - logger.warning(f"Ripgrep failed: {e}") - return [] - - async def _find_all_references_parallel( - self, file_path: str, identifier: str, project_root: str - ) -> List[RefactorLocation]: - """Find all references using parallel processing.""" - language = self._get_language(file_path) - - # Get extensions for this language - source_extensions = { - "python": [".py"], - "javascript": [".js", ".jsx", ".mjs"], - "typescript": [".ts", ".tsx"], - "go": [".go"], - "rust": [".rs"], - "java": [".java"], - "cpp": [".cpp", ".cc", ".cxx", ".c", ".h", ".hpp"], - } - extensions = source_extensions.get(language, [Path(file_path).suffix]) - - # Try ripgrep first (much faster) - if self._ripgrep_available: - references = await self._find_references_ripgrep( - identifier, project_root, extensions - ) - if references: - return references - - # Fall back to parallel file scanning - return await self._find_references_parallel_scan( - identifier, project_root, extensions - ) - - async def _find_references_parallel_scan( - self, identifier: str, project_root: str, extensions: List[str] - ) -> List[RefactorLocation]: - """Parallel file scanning fallback when ripgrep unavailable.""" - # Get all source files - files_to_scan = [] - skip_dirs = { - ".git", - "node_modules", - "__pycache__", - "venv", - ".venv", - "dist", - "build", - } - - for root, dirs, files in os.walk(project_root): - # Prune directories in-place - dirs[:] = [d for d in dirs if d not in skip_dirs] - - for file in files: - if any(file.endswith(ext) for ext in extensions): - files_to_scan.append(os.path.join(root, file)) - - if not files_to_scan: - return [] - - # Process files in parallel batches - semaphore = asyncio.Semaphore(self.max_workers) - pattern = re.compile(rf"\b{re.escape(identifier)}\b") - - async def scan_file(file_path: str) -> List[RefactorLocation]: - async with semaphore: - cache = await self._get_file_cached(file_path) - if not cache: - return [] - - refs = [] - for i, line in enumerate(cache.lines): - for match in pattern.finditer(line): - refs.append( - RefactorLocation( - file=file_path, - line=i + 1, - column=match.start(), - text=identifier, - context=line.strip(), - ) - ) - return refs - - # Run all file scans concurrently - tasks = [scan_file(f) for f in files_to_scan] - results = await asyncio.gather(*tasks, return_exceptions=True) - - # Flatten results - all_refs = [] - for result in results: - if isinstance(result, list): - all_refs.extend(result) - - return all_refs - - # ==================== RENAME OPERATIONS ==================== - - async def _rename( - self, - file_path: Optional[str], - line: Optional[int], - column: Optional[int], - new_name: Optional[str], - preview: bool, - parallel: bool = True, - ) -> RefactorResult: - """Rename a symbol across the codebase.""" - if not file_path or not line or column is None: - return RefactorResult( - success=False, - action="rename", - errors=["file, line and column are required for rename"], - ) - if not new_name: - return RefactorResult( - success=False, action="rename", errors=["new_name is required"] - ) - - # Get the identifier at the position - cache = await self._get_file_cached(file_path) - if not cache or line > len(cache.lines): - return RefactorResult( - success=False, - action="rename", - errors=[f"Cannot read file or line {line} out of range"], - ) - - target_line = cache.lines[line - 1] - old_name = self._get_identifier_at(target_line, column) - - if not old_name: - return RefactorResult( - success=False, - action="rename", - errors=[f"No identifier found at line {line}, column {column}"], - ) - - # Find all references - project_root = self._find_project_root(file_path) - references = await self._find_all_references_parallel( - file_path, old_name, project_root - ) - - if not references: - return RefactorResult( - success=False, - action="rename", - errors=[f"No references found for '{old_name}'"], - ) - - # Group by file for efficient batch edits - changes_by_file: Dict[str, List[RefactorChange]] = defaultdict(list) - for ref in references: - change = RefactorChange( - file=ref.file, - line=ref.line, - column=ref.column, - end_line=ref.line, - end_column=ref.column + len(old_name), - old_text=old_name, - new_text=new_name, - ) - changes_by_file[ref.file].append(change) - - if preview: - preview_data = [] - for file, changes in changes_by_file.items(): - for change in changes[:50]: # Limit preview per file - preview_data.append( - { - "file": file, - "line": change.line, - "column": change.column, - "old": change.old_text, - "new": change.new_text, - } - ) - - return RefactorResult( - success=True, - action="rename", - files_changed=len(changes_by_file), - changes_applied=len(references), - preview=preview_data, - message=f"Would rename {len(references)} occurrences of '{old_name}' to '{new_name}' across {len(changes_by_file)} files", - stats={ - "files_scanned": len(changes_by_file), - "references_found": len(references), - }, - ) - - # Apply changes in parallel - result = await self._apply_changes_parallel(changes_by_file, parallel) - result.action = "rename" - result.message = f"Renamed {result.changes_applied} occurrences of '{old_name}' to '{new_name}'" - return result - - async def _rename_batch( - self, - renames: List[Dict[str, str]], - path: str, - preview: bool, - parallel: bool = True, - ) -> RefactorResult: - """Batch rename multiple symbols in one operation.""" - if not renames: - return RefactorResult( - success=False, - action="rename_batch", - errors=["renames list is required"], - ) - - project_root = str(Path(path).resolve()) - if not Path(project_root).exists(): - return RefactorResult( - success=False, action="rename_batch", errors=[f"Path not found: {path}"] - ) - - all_changes: Dict[str, List[RefactorChange]] = defaultdict(list) - total_refs = 0 - - # Find references for all symbols in parallel - async def find_refs_for_rename( - rename: Dict[str, str], - ) -> Tuple[str, str, List[RefactorLocation]]: - old = rename.get("old", "") - new = rename.get("new", "") - if not old or not new: - return old, new, [] - - # Find any source file to determine language - sample_file = None - for root, _, files in os.walk(project_root): - for f in files: - if f.endswith((".py", ".js", ".ts", ".go", ".rs")): - sample_file = os.path.join(root, f) - break - if sample_file: - break - - if sample_file: - refs = await self._find_all_references_parallel( - sample_file, old, project_root - ) - else: - refs = [] - - return old, new, refs - - # Run all reference searches in parallel - tasks = [find_refs_for_rename(r) for r in renames] - results = await asyncio.gather(*tasks) - - for old_name, new_name, references in results: - total_refs += len(references) - for ref in references: - change = RefactorChange( - file=ref.file, - line=ref.line, - column=ref.column, - end_line=ref.line, - end_column=ref.column + len(old_name), - old_text=old_name, - new_text=new_name, - ) - all_changes[ref.file].append(change) - - if preview: - preview_data = [] - for file, changes in list(all_changes.items())[ - :20 - ]: # Limit files in preview - for change in changes[:10]: - preview_data.append( - { - "file": file, - "line": change.line, - "old": change.old_text, - "new": change.new_text, - } - ) - - return RefactorResult( - success=True, - action="rename_batch", - files_changed=len(all_changes), - changes_applied=total_refs, - preview=preview_data, - message=f"Would apply {total_refs} renames across {len(all_changes)} files", - stats={ - "renames_requested": len(renames), - "references_found": total_refs, - }, - ) - - # Apply all changes in parallel - result = await self._apply_changes_parallel(all_changes, parallel) - result.action = "rename_batch" - result.message = f"Applied {result.changes_applied} renames across {result.files_changed} files" - result.stats["renames_requested"] = len(renames) - return result - - # ==================== PARALLEL FILE EDITING ==================== - - async def _apply_changes_parallel( - self, - changes_by_file: Dict[str, List[RefactorChange]], - parallel: bool = True, - ) -> RefactorResult: - """Apply changes to multiple files in parallel.""" - if not changes_by_file: - return RefactorResult( - success=True, action="apply", message="No changes to apply" - ) - - semaphore = asyncio.Semaphore(MAX_CONCURRENT_EDITS if parallel else 1) - results: List[Tuple[str, bool, int, Optional[str]]] = [] - - async def apply_to_file( - file_path: str, changes: List[RefactorChange] - ) -> Tuple[str, bool, int, Optional[str]]: - async with semaphore: - try: - count = await self._apply_file_changes_atomic(file_path, changes) - await self._invalidate_cache(file_path) - return (file_path, True, count, None) - except Exception as e: - return (file_path, False, 0, str(e)) - - # Run all file edits concurrently - if parallel: - tasks = [apply_to_file(f, c) for f, c in changes_by_file.items()] - results = await asyncio.gather(*tasks) - else: - for f, c in changes_by_file.items(): - results.append(await apply_to_file(f, c)) - - # Aggregate results - files_changed = 0 - changes_applied = 0 - errors = [] - change_details = [] - - for file_path, success, count, error in results: - if success: - files_changed += 1 - changes_applied += count - change_details.append({"file": file_path, "changes": count}) - else: - errors.append(f"{file_path}: {error}") - - return RefactorResult( - success=len(errors) == 0, - action="apply", - files_changed=files_changed, - changes_applied=changes_applied, - changes=change_details, - errors=errors, - stats={"files_attempted": len(changes_by_file)}, - ) - - async def _apply_file_changes_atomic( - self, file_path: str, changes: List[RefactorChange] - ) -> int: - """Apply changes to a single file atomically.""" - cache = await self._get_file_cached(file_path) - if not cache: - raise Exception("Cannot read file") - - lines = cache.lines.copy() - - # Sort changes by position (reverse order to maintain positions) - changes.sort(key=lambda c: (c.line, c.column), reverse=True) - - applied = 0 - for change in changes: - if change.line > len(lines): - continue - - line = lines[change.line - 1] - # Verify the old text matches - actual = line[change.column : change.column + len(change.old_text)] - if actual == change.old_text: - new_line = ( - line[: change.column] - + change.new_text - + line[change.column + len(change.old_text) :] - ) - lines[change.line - 1] = new_line - applied += 1 - - # Write atomically (write to temp, then rename) - content = "\n".join(lines) - temp_path = f"{file_path}.tmp" - - loop = asyncio.get_event_loop() - await loop.run_in_executor(None, self._write_file_sync, temp_path, content) - await loop.run_in_executor(None, os.replace, temp_path, file_path) - - return applied - - def _write_file_sync(self, path: str, content: str): - """Synchronous file write for thread pool.""" - with open(path, "w", encoding="utf-8") as f: - f.write(content) - - # ==================== FIND REFERENCES ==================== - - async def _find_references( - self, - file_path: Optional[str], - line: Optional[int], - column: Optional[int], - parallel: bool = True, - ) -> RefactorResult: - """Find all references to a symbol.""" - if not file_path or not line or column is None: - return RefactorResult( - success=False, - action="find_references", - errors=["file, line and column are required"], - ) - - cache = await self._get_file_cached(file_path) - if not cache or line > len(cache.lines): - return RefactorResult( - success=False, action="find_references", errors=["Cannot read file"] - ) - - identifier = self._get_identifier_at(cache.lines[line - 1], column) - if not identifier: - return RefactorResult( - success=False, - action="find_references", - errors=["No identifier found at position"], - ) - - project_root = self._find_project_root(file_path) - references = await self._find_all_references_parallel( - file_path, identifier, project_root - ) - - ref_data = [ - {"file": r.file, "line": r.line, "column": r.column, "context": r.context} - for r in references - ] - - return RefactorResult( - success=True, - action="find_references", - changes=ref_data, - message=f"Found {len(references)} references to '{identifier}'", - stats={ - "references_found": len(references), - "using_ripgrep": self._ripgrep_available, - }, - ) - - # ==================== EXTRACT OPERATIONS ==================== - - async def _extract_function( - self, - file_path: Optional[str], - start_line: Optional[int], - end_line: Optional[int], - new_name: Optional[str], - preview: bool, - ) -> RefactorResult: - """Extract a code block into a new function.""" - if not file_path: - return RefactorResult( - success=False, action="extract_function", errors=["file is required"] - ) - if not new_name: - return RefactorResult( - success=False, - action="extract_function", - errors=["new_name is required for extract_function"], - ) - if not start_line or not end_line: - return RefactorResult( - success=False, - action="extract_function", - errors=["start_line and end_line are required"], - ) - - cache = await self._get_file_cached(file_path) - if not cache: - return RefactorResult( - success=False, action="extract_function", errors=["Cannot read file"] - ) - - if start_line < 1 or end_line > len(cache.lines): - return RefactorResult( - success=False, - action="extract_function", - errors=["Line range out of bounds"], - ) - - # Extract the code block - extracted_lines = cache.lines[start_line - 1 : end_line] - extracted_code = "\n".join(extracted_lines) - - # Detect language and indentation - language = self._get_language(file_path) - base_indent = ( - self._get_indentation(extracted_lines[0]) if extracted_lines else "" - ) - - # Find variables used in the block - used_vars = self._find_used_variables(extracted_code, language) - defined_vars = self._find_defined_variables(extracted_code, language) - - # Parameters are used but not defined in the block - params = list(used_vars - defined_vars) - - # Build the new function - new_function = self._build_function( - new_name, params, extracted_code, language, base_indent - ) - - # Build the function call - function_call = self._build_function_call( - new_name, params, language, base_indent - ) - - if preview: - return RefactorResult( - success=True, - action="extract_function", - preview=[ - { - "type": "new_function", - "code": new_function, - "insert_at": f"Before line {start_line}", - }, - { - "type": "replacement", - "lines": f"{start_line}-{end_line}", - "old_code": extracted_code, - "new_code": function_call, - }, - ], - message=f"Would extract lines {start_line}-{end_line} to function '{new_name}'", - ) - - # Apply the extraction - try: - new_lines = cache.lines[: start_line - 1] - new_lines.append(new_function) - new_lines.append("") - new_lines.append(function_call) - new_lines.extend(cache.lines[end_line:]) - - loop = asyncio.get_event_loop() - await loop.run_in_executor( - None, self._write_file_sync, file_path, "\n".join(new_lines) - ) - await self._invalidate_cache(file_path) - - return RefactorResult( - success=True, - action="extract_function", - files_changed=1, - changes_applied=1, - message=f"Extracted lines {start_line}-{end_line} to function '{new_name}'", - ) - except Exception as e: - return RefactorResult( - success=False, action="extract_function", errors=[str(e)] - ) - - async def _extract_variable( - self, - file_path: Optional[str], - line: Optional[int], - column: Optional[int], - end_line: Optional[int], - end_column: Optional[int], - new_name: Optional[str], - preview: bool, - ) -> RefactorResult: - """Extract an expression into a variable.""" - if not file_path or not line or column is None: - return RefactorResult( - success=False, - action="extract_variable", - errors=["file, line and column are required"], - ) - if not new_name: - return RefactorResult( - success=False, - action="extract_variable", - errors=["new_name is required"], - ) - - cache = await self._get_file_cached(file_path) - if not cache or line > len(cache.lines): - return RefactorResult( - success=False, action="extract_variable", errors=["Cannot read file"] - ) - - target_line = cache.lines[line - 1] - el = end_line or line - ec = end_column or len(target_line) - - # Extract the expression - if line == el: - expression = target_line[column:ec] - else: - expression_lines = [target_line[column:]] - for i in range(line, el - 1): - expression_lines.append(cache.lines[i]) - expression_lines.append(cache.lines[el - 1][:ec]) - expression = "\n".join(expression_lines) - - language = self._get_language(file_path) - indent = self._get_indentation(target_line) - - var_decl = self._build_variable_declaration( - new_name, expression, language, indent - ) - - if preview: - return RefactorResult( - success=True, - action="extract_variable", - preview=[ - {"type": "insert", "line": line, "code": var_decl}, - {"type": "replace", "expression": expression, "with": new_name}, - ], - message=f"Would extract expression to variable '{new_name}'", - ) - - try: - lines = cache.lines.copy() - new_line = target_line[:column] + new_name + target_line[ec:] - lines[line - 1] = new_line - lines.insert(line - 1, var_decl) - - loop = asyncio.get_event_loop() - await loop.run_in_executor( - None, self._write_file_sync, file_path, "\n".join(lines) - ) - await self._invalidate_cache(file_path) - - return RefactorResult( - success=True, - action="extract_variable", - files_changed=1, - changes_applied=1, - message=f"Extracted expression to variable '{new_name}'", - ) - except Exception as e: - return RefactorResult( - success=False, action="extract_variable", errors=[str(e)] - ) - - # ==================== INLINE OPERATION ==================== - - async def _inline( - self, - file_path: Optional[str], - line: Optional[int], - column: Optional[int], - preview: bool, - parallel: bool = True, - ) -> RefactorResult: - """Inline a variable or function at all usage sites.""" - if not file_path or not line or column is None: - return RefactorResult( - success=False, - action="inline", - errors=["file, line and column are required"], - ) - - cache = await self._get_file_cached(file_path) - if not cache or line > len(cache.lines): - return RefactorResult( - success=False, action="inline", errors=["Cannot read file"] - ) - - target_line = cache.lines[line - 1] - identifier = self._get_identifier_at(target_line, column) - - if not identifier: - return RefactorResult( - success=False, - action="inline", - errors=["No identifier found at position"], - ) - - language = self._get_language(file_path) - definition = self._find_definition(cache.content, identifier, language) - - if not definition: - return RefactorResult( - success=False, - action="inline", - errors=[f"Could not find definition for '{identifier}'"], - ) - - project_root = self._find_project_root(file_path) - usages = await self._find_all_references_parallel( - file_path, identifier, project_root - ) - - # Filter out the definition itself - usages = [ - u - for u in usages - if not (u.file == file_path and u.line == definition["line"]) - ] - - if not usages: - return RefactorResult( - success=False, - action="inline", - errors=[f"No usages found for '{identifier}'"], - ) - - inline_value = definition["value"] - - if preview: - preview_data = [ - { - "file": u.file, - "line": u.line, - "replace": identifier, - "with": inline_value, - } - for u in usages[:20] - ] - return RefactorResult( - success=True, - action="inline", - preview=preview_data, - message=f"Would inline {len(usages)} usages of '{identifier}' with '{inline_value}'", - stats={"usages_found": len(usages)}, - ) - - # Build changes - changes_by_file: Dict[str, List[RefactorChange]] = defaultdict(list) - for usage in usages: - change = RefactorChange( - file=usage.file, - line=usage.line, - column=usage.column, - end_line=usage.line, - end_column=usage.column + len(identifier), - old_text=identifier, - new_text=inline_value, - ) - changes_by_file[usage.file].append(change) - - result = await self._apply_changes_parallel(changes_by_file, parallel) - - # Remove the original definition - try: - lines = cache.lines.copy() - del lines[definition["line"] - 1] - loop = asyncio.get_event_loop() - await loop.run_in_executor( - None, self._write_file_sync, file_path, "\n".join(lines) - ) - await self._invalidate_cache(file_path) - except Exception as e: - result.errors.append(f"Failed to remove definition: {str(e)}") - - result.action = "inline" - result.message = f"Inlined {result.changes_applied} usages of '{identifier}'" - return result - - # ==================== MOVE OPERATION ==================== - - async def _move( - self, - file_path: Optional[str], - line: Optional[int], - column: Optional[int], - target_file: Optional[str], - preview: bool, - ) -> RefactorResult: - """Move a symbol to another file.""" - if not file_path: - return RefactorResult( - success=False, action="move", errors=["file is required"] - ) - if not target_file: - return RefactorResult( - success=False, action="move", errors=["target_file is required"] - ) - if not line: - return RefactorResult( - success=False, action="move", errors=["line is required"] - ) - - cache = await self._get_file_cached(file_path) - if not cache: - return RefactorResult( - success=False, action="move", errors=["Cannot read file"] - ) - - identifier = self._get_identifier_at(cache.lines[line - 1], column or 0) - if not identifier: - return RefactorResult( - success=False, action="move", errors=["No identifier found"] - ) - - language = self._get_language(file_path) - block = self._find_definition_block(cache.content, identifier, line, language) - - if not block: - return RefactorResult( - success=False, - action="move", - errors=[f"Could not find definition block for '{identifier}'"], - ) - - if preview: - return RefactorResult( - success=True, - action="move", - preview=[ - { - "action": "remove_from", - "file": file_path, - "lines": f"{block['start']}-{block['end']}", - }, - { - "action": "add_to", - "file": target_file, - "code": block["code"][:200] + "...", - }, - ], - message=f"Would move '{identifier}' from {file_path} to {target_file}", - ) - - errors = [] - loop = asyncio.get_event_loop() - - # Add to target file - try: - if Path(target_file).exists(): - target_cache = await self._get_file_cached(target_file) - target_content = target_cache.content if target_cache else "" - target_content = target_content.rstrip() + "\n\n" + block["code"] + "\n" - else: - target_content = block["code"] + "\n" - - await loop.run_in_executor( - None, self._write_file_sync, target_file, target_content - ) - await self._invalidate_cache(target_file) - except Exception as e: - errors.append(f"Failed to add to target file: {str(e)}") - - # Remove from source file - try: - new_lines = cache.lines[: block["start"] - 1] + cache.lines[block["end"] :] - await loop.run_in_executor( - None, self._write_file_sync, file_path, "\n".join(new_lines) - ) - await self._invalidate_cache(file_path) - except Exception as e: - errors.append(f"Failed to remove from source file: {str(e)}") - - return RefactorResult( - success=len(errors) == 0, - action="move", - files_changed=2 if not errors else 0, - changes_applied=1 if not errors else 0, - errors=errors, - message=f"Moved '{identifier}' to {target_file}", - ) - - async def _change_signature( - self, - file_path: Optional[str], - line: Optional[int], - column: Optional[int], - changes: Dict[str, Any], - preview: bool, - ) -> RefactorResult: - """Change a function's signature and update all call sites. - - Supported changes (pass in kwargs): - - add_parameter: {"name": "param", "type": "str", "default": "''", "position": 0} - - remove_parameter: {"name": "param"} or {"index": 0} - - rename_parameter: {"old": "oldName", "new": "newName"} - - reorder_parameters: [0, 2, 1, 3] # new order by index - - change_default: {"name": "param", "default": "newDefault"} - """ - if not file_path or not line: - return RefactorResult( - success=False, - action="change_signature", - errors=["file and line are required to locate the function"], - ) - - cache = await self._get_file_cached(file_path) - if not cache or line > len(cache.lines): - return RefactorResult( - success=False, action="change_signature", errors=["Cannot read file"] - ) - - language = self._get_language(file_path) - - # Parse the function signature at the given line - func_info = self._parse_function_signature(cache.lines, line, language) - if not func_info: - return RefactorResult( - success=False, - action="change_signature", - errors=[f"No function signature found at line {line}"], - ) - - func_name = func_info["name"] - params = func_info[ - "params" - ] # List of {"name": str, "type": str|None, "default": str|None} - signature_line = func_info["line"] - signature_end_line = func_info.get("end_line", signature_line) - - # Apply the signature changes - new_params, param_mapping, errors = self._apply_signature_changes( - params, changes, language - ) - if errors: - return RefactorResult( - success=False, action="change_signature", errors=errors - ) - - # Find all call sites in the project - project_root = self._find_project_root(file_path) - call_sites = await self._find_function_calls(file_path, func_name, project_root) - - # Build the new signature - new_signature = self._build_signature( - func_name, new_params, language, func_info.get("decorators", []) - ) - - # Build changes for all call sites - changes_by_file: Dict[str, List[RefactorChange]] = defaultdict(list) - - # First, change the function definition - old_sig_lines = cache.lines[signature_line - 1 : signature_end_line] - old_sig = "\n".join(old_sig_lines) - - changes_by_file[file_path].append( - RefactorChange( - file=file_path, - line=signature_line, - column=0, - end_line=signature_end_line, - end_column=( - len(cache.lines[signature_end_line - 1]) - if signature_end_line <= len(cache.lines) - else 0 - ), - old_text=old_sig, - new_text=new_signature, - description="Update function signature", - ) - ) - - # Then update all call sites - call_changes = await self._update_call_sites( - call_sites, func_name, params, new_params, param_mapping, changes, language - ) - for call_change in call_changes: - changes_by_file[call_change.file].append(call_change) - - total_changes = sum(len(c) for c in changes_by_file.values()) - - if preview: - preview_data = [ - { - "type": "signature", - "file": file_path, - "line": signature_line, - "old": old_sig.strip(), - "new": new_signature.strip(), - } - ] - for call_change in call_changes[:20]: - preview_data.append( - { - "type": "call_site", - "file": call_change.file, - "line": call_change.line, - "old": call_change.old_text, - "new": call_change.new_text, - } - ) - - return RefactorResult( - success=True, - action="change_signature", - files_changed=len(changes_by_file), - changes_applied=total_changes, - preview=preview_data, - message=f"Would update signature of '{func_name}' and {len(call_changes)} call sites", - stats={ - "call_sites_found": len(call_sites), - "params_before": len(params), - "params_after": len(new_params), - }, - ) - - # Apply all changes - result = await self._apply_signature_changes_to_files( - changes_by_file, - cache, - file_path, - signature_line, - signature_end_line, - new_signature, - ) - result.action = "change_signature" - result.message = ( - f"Updated signature of '{func_name}' and {len(call_changes)} call sites" - ) - result.stats = {"call_sites_updated": len(call_changes)} - return result - - def _parse_function_signature( - self, lines: List[str], line_num: int, language: str - ) -> Optional[Dict[str, Any]]: - """Parse a function signature at the given line.""" - if line_num > len(lines): - return None - - line = lines[line_num - 1] - - if language == "python": - # Handle multiline signatures - full_sig = line - end_line = line_num - - # Check if signature spans multiple lines - if "(" in line and ")" not in line: - paren_count = line.count("(") - line.count(")") - while paren_count > 0 and end_line < len(lines): - end_line += 1 - full_sig += "\n" + lines[end_line - 1] - paren_count += lines[end_line - 1].count("(") - lines[ - end_line - 1 - ].count(")") - - # Check for decorators above - decorators = [] - check_line = line_num - 2 - while check_line >= 0 and lines[check_line].strip().startswith("@"): - decorators.insert(0, lines[check_line]) - check_line -= 1 - - # Parse: def func_name(params): - match = re.match( - r"^\s*(async\s+)?def\s+(\w+)\s*\(([^)]*)\)", full_sig.replace("\n", " ") - ) - if not match: - return None - - is_async = bool(match.group(1)) - func_name = match.group(2) - params_str = match.group(3).strip() - - params = self._parse_python_params(params_str) - - return { - "name": func_name, - "params": params, - "line": line_num, - "end_line": end_line, - "is_async": is_async, - "decorators": decorators, - "indent": self._get_indentation(line), - } - - elif language in ["javascript", "typescript"]: - # Parse: function name(params) or name = (params) => or name(params) { - patterns = [ - r"^\s*(async\s+)?function\s+(\w+)\s*\(([^)]*)\)", # function declaration - r"^\s*(async\s+)?(\w+)\s*[=:]\s*(?:async\s+)?\(([^)]*)\)\s*=>", # arrow function - r"^\s*(async\s+)?(\w+)\s*\(([^)]*)\)\s*{", # method shorthand - ] - - for pattern in patterns: - match = re.match(pattern, line) - if match: - is_async = bool(match.group(1)) - func_name = match.group(2) - params_str = match.group(3).strip() - params = self._parse_js_params(params_str, language == "typescript") - - return { - "name": func_name, - "params": params, - "line": line_num, - "end_line": line_num, - "is_async": is_async, - "decorators": [], - "indent": self._get_indentation(line), - } - - elif language == "go": - # Parse: func (receiver) name(params) (returns) { - match = re.match(r"^\s*func\s+(?:\([^)]+\)\s+)?(\w+)\s*\(([^)]*)\)", line) - if match: - func_name = match.group(1) - params_str = match.group(2).strip() - params = self._parse_go_params(params_str) - - return { - "name": func_name, - "params": params, - "line": line_num, - "end_line": line_num, - "is_async": False, - "decorators": [], - "indent": self._get_indentation(line), - } - - return None - - def _parse_python_params(self, params_str: str) -> List[Dict[str, Any]]: - """Parse Python function parameters.""" - if not params_str.strip(): - return [] - - params = [] - # Handle complex default values with nested parens/brackets - depth = 0 - current = "" - - for char in params_str + ",": - if char in "([{": - depth += 1 - current += char - elif char in ")]}": - depth -= 1 - current += char - elif char == "," and depth == 0: - param = current.strip() - if param: - params.append(self._parse_single_python_param(param)) - current = "" - else: - current += char - - return params - - def _parse_single_python_param(self, param: str) -> Dict[str, Any]: - """Parse a single Python parameter.""" - result = {"name": "", "type": None, "default": None} - - # Check for default value - if "=" in param: - parts = param.split("=", 1) - param_part = parts[0].strip() - result["default"] = parts[1].strip() - else: - param_part = param.strip() - - # Check for type annotation - if ":" in param_part: - name_part, type_part = param_part.split(":", 1) - result["name"] = name_part.strip() - result["type"] = type_part.strip() - else: - result["name"] = param_part - - return result - - def _parse_js_params( - self, params_str: str, typescript: bool = False - ) -> List[Dict[str, Any]]: - """Parse JavaScript/TypeScript function parameters.""" - if not params_str.strip(): - return [] - - params = [] - for param in params_str.split(","): - param = param.strip() - if not param: - continue - - result = {"name": "", "type": None, "default": None} - - # Check for default - if "=" in param: - parts = param.split("=", 1) - param = parts[0].strip() - result["default"] = parts[1].strip() - - # Check for TypeScript type - if ":" in param and typescript: - parts = param.split(":", 1) - result["name"] = parts[0].strip() - result["type"] = parts[1].strip() - else: - result["name"] = param - - params.append(result) - - return params - - def _parse_go_params(self, params_str: str) -> List[Dict[str, Any]]: - """Parse Go function parameters.""" - if not params_str.strip(): - return [] - - params = [] - for param in params_str.split(","): - param = param.strip() - if not param: - continue - - parts = param.split() - if len(parts) >= 2: - params.append( - {"name": parts[0], "type": " ".join(parts[1:]), "default": None} - ) - elif len(parts) == 1: - # Type only (named later) or name only - params.append({"name": parts[0], "type": None, "default": None}) - - return params - - def _apply_signature_changes( - self, - params: List[Dict[str, Any]], - changes: Dict[str, Any], - language: str, - ) -> Tuple[List[Dict[str, Any]], Dict[int, int], List[str]]: - """Apply signature changes and return new params, mapping, and errors.""" - new_params = [p.copy() for p in params] - param_mapping: Dict[int, int] = { - i: i for i in range(len(params)) - } # old_index -> new_index - errors = [] - - # Add parameter - if "add_parameter" in changes: - add = changes["add_parameter"] - new_param = { - "name": add.get("name", "newParam"), - "type": add.get("type"), - "default": add.get("default"), - } - position = add.get("position", len(new_params)) - new_params.insert(position, new_param) - # Update mapping for params after insertion - for old_idx in list(param_mapping.keys()): - if param_mapping[old_idx] >= position: - param_mapping[old_idx] += 1 - - # Remove parameter - if "remove_parameter" in changes: - remove = changes["remove_parameter"] - idx = None - if "index" in remove: - idx = remove["index"] - elif "name" in remove: - for i, p in enumerate(new_params): - if p["name"] == remove["name"]: - idx = i - break - - if idx is not None and 0 <= idx < len(new_params): - del new_params[idx] - # Update mapping - for old_idx in list(param_mapping.keys()): - if param_mapping[old_idx] == idx: - param_mapping[old_idx] = -1 # Removed - elif param_mapping[old_idx] > idx: - param_mapping[old_idx] -= 1 - else: - errors.append(f"Parameter to remove not found") - - # Rename parameter - if "rename_parameter" in changes: - rename = changes["rename_parameter"] - old_name = rename.get("old") - new_name = rename.get("new") - found = False - for p in new_params: - if p["name"] == old_name: - p["name"] = new_name - found = True - break - if not found: - errors.append(f"Parameter '{old_name}' not found for rename") - - # Reorder parameters - if "reorder_parameters" in changes: - order = changes["reorder_parameters"] # List of indices - if len(order) == len(new_params): - reordered = [new_params[i] for i in order] - new_params = reordered - # Update mapping - inverse_order = {old: new for new, old in enumerate(order)} - for old_idx in param_mapping: - if param_mapping[old_idx] >= 0: - param_mapping[old_idx] = inverse_order.get( - param_mapping[old_idx], param_mapping[old_idx] - ) - else: - errors.append( - f"Reorder list length {len(order)} doesn't match param count {len(new_params)}" - ) - - # Change default - if "change_default" in changes: - cd = changes["change_default"] - param_name = cd.get("name") - new_default = cd.get("default") - found = False - for p in new_params: - if p["name"] == param_name: - p["default"] = new_default - found = True - break - if not found: - errors.append(f"Parameter '{param_name}' not found for default change") - - return new_params, param_mapping, errors - - def _build_signature( - self, - func_name: str, - params: List[Dict[str, Any]], - language: str, - decorators: List[str] = [], - ) -> str: - """Build a function signature string.""" - if language == "python": - param_strs = [] - for p in params: - s = p["name"] - if p.get("type"): - s += f": {p['type']}" - if p.get("default") is not None: - s += f" = {p['default']}" - param_strs.append(s) - - sig = f"def {func_name}({', '.join(param_strs)}):" - if decorators: - sig = "\n".join(decorators) + "\n" + sig - return sig - - elif language in ["javascript", "typescript"]: - param_strs = [] - for p in params: - s = p["name"] - if language == "typescript" and p.get("type"): - s += f": {p['type']}" - if p.get("default") is not None: - s += f" = {p['default']}" - param_strs.append(s) - return f"function {func_name}({', '.join(param_strs)}) {{" - - elif language == "go": - param_strs = [] - for p in params: - if p.get("type"): - param_strs.append(f"{p['name']} {p['type']}") - else: - param_strs.append(p["name"]) - return f"func {func_name}({', '.join(param_strs)}) {{" - - return f"function {func_name}() {{" - - async def _find_function_calls( - self, - file_path: str, - func_name: str, - project_root: str, - ) -> List[RefactorLocation]: - """Find all call sites of a function.""" - # Use the same reference finding but filter for actual calls (with parens) - all_refs = await self._find_all_references_parallel( - file_path, func_name, project_root - ) - - # Filter to only call sites (references followed by parenthesis) - call_sites = [] - for ref in all_refs: - # Check if this reference is a function call - cache = await self._get_file_cached(ref.file) - if not cache or ref.line > len(cache.lines): - continue - - line = cache.lines[ref.line - 1] - # Check if there's a '(' after the identifier - end_col = ref.column + len(func_name) - remaining = line[end_col:].lstrip() - if remaining.startswith("("): - call_sites.append(ref) - - return call_sites - - async def _update_call_sites( - self, - call_sites: List[RefactorLocation], - func_name: str, - old_params: List[Dict[str, Any]], - new_params: List[Dict[str, Any]], - param_mapping: Dict[int, int], - changes: Dict[str, Any], - language: str, - ) -> List[RefactorChange]: - """Update all call sites with the new signature.""" - call_changes = [] - - for site in call_sites: - cache = await self._get_file_cached(site.file) - if not cache or site.line > len(cache.lines): - continue - - line = cache.lines[site.line - 1] - - # Find the full call expression (handle multiline calls) - call_start = site.column - call_text, call_end = self._extract_call_expression( - cache.lines, site.line - 1, call_start - ) - - if not call_text: - continue - - # Parse the call arguments - args = self._parse_call_arguments(call_text, func_name) - - # Apply changes to arguments - new_args = self._transform_arguments( - args, old_params, new_params, param_mapping, changes - ) - - # Rebuild the call - new_call = f"{func_name}({', '.join(new_args)})" - - if new_call != call_text: - call_changes.append( - RefactorChange( - file=site.file, - line=site.line, - column=call_start, - end_line=site.line, # Simplified - assuming single line - end_column=call_start + len(call_text), - old_text=call_text, - new_text=new_call, - description=f"Update call to {func_name}", - ) - ) - - return call_changes - - def _extract_call_expression( - self, - lines: List[str], - line_idx: int, - start_col: int, - ) -> Tuple[Optional[str], int]: - """Extract a function call expression, handling multiline.""" - line = lines[line_idx] - - # Find the opening paren - paren_start = line.find("(", start_col) - if paren_start < 0: - return None, 0 - - # Find matching close paren - paren_count = 1 - pos = paren_start + 1 - current_line = line_idx - call_text = line[start_col : paren_start + 1] - - while paren_count > 0: - if pos >= len(lines[current_line]): - current_line += 1 - if current_line >= len(lines): - return None, 0 - pos = 0 - call_text += "\n" + lines[current_line][:pos] - continue - - char = lines[current_line][pos] - call_text += char if current_line == line_idx or pos > 0 else "" - - if char == "(": - paren_count += 1 - elif char == ")": - paren_count -= 1 - pos += 1 - - # Include the final character - if current_line == line_idx: - call_text = line[start_col : paren_start + 1 + (pos - paren_start - 1)] - - # Simple single-line extraction - end = line.find(")", paren_start) - if end >= 0: - return line[start_col : end + 1], end + 1 - - return None, 0 - - def _parse_call_arguments(self, call_text: str, func_name: str) -> List[str]: - """Parse arguments from a function call.""" - # Extract content between parentheses - match = re.match( - rf"{re.escape(func_name)}\s*\((.+)\)\s*$", call_text, re.DOTALL - ) - if not match: - # Try simpler pattern - start = call_text.find("(") - end = call_text.rfind(")") - if start >= 0 and end > start: - args_str = call_text[start + 1 : end] - else: - return [] - else: - args_str = match.group(1) - - if not args_str.strip(): - return [] - - # Parse respecting nested parens/brackets - args = [] - depth = 0 - current = "" - in_string = None - - for char in args_str: - if in_string: - current += char - if char == in_string and (len(current) < 2 or current[-2] != "\\"): - in_string = None - elif char in "\"'": - current += char - in_string = char - elif char in "([{": - depth += 1 - current += char - elif char in ")]}": - depth -= 1 - current += char - elif char == "," and depth == 0: - args.append(current.strip()) - current = "" - else: - current += char - - if current.strip(): - args.append(current.strip()) - - return args - - def _transform_arguments( - self, - args: List[str], - old_params: List[Dict[str, Any]], - new_params: List[Dict[str, Any]], - param_mapping: Dict[int, int], - changes: Dict[str, Any], - ) -> List[str]: - """Transform call arguments based on signature changes.""" - # Start with empty new args list - new_args: List[Optional[str]] = [None] * len(new_params) - - # Map old args to new positions - for old_idx, arg in enumerate(args): - if old_idx in param_mapping: - new_idx = param_mapping[old_idx] - if new_idx >= 0 and new_idx < len(new_args): - new_args[new_idx] = arg - - # Fill in defaults for new parameters - for i, param in enumerate(new_params): - if new_args[i] is None: - if param.get("default") is not None: - new_args[i] = param["default"] - else: - new_args[i] = f"/* TODO: {param['name']} */" - - return [a for a in new_args if a is not None] - - async def _apply_signature_changes_to_files( - self, - changes_by_file: Dict[str, List[RefactorChange]], - cache: FileCache, - file_path: str, - sig_line: int, - sig_end_line: int, - new_signature: str, - ) -> RefactorResult: - """Apply signature changes, handling the definition specially.""" - errors = [] - files_changed = 0 - changes_applied = 0 - - loop = asyncio.get_event_loop() - - for target_file, file_changes in changes_by_file.items(): - try: - target_cache = await self._get_file_cached(target_file) - if not target_cache: - continue - - lines = target_cache.lines.copy() - - # Sort changes by position (reverse) - file_changes.sort(key=lambda c: (c.line, c.column), reverse=True) - - for change in file_changes: - # Handle signature change specially (may span multiple lines) - if target_file == file_path and change.line == sig_line: - # Replace signature lines - lines = ( - lines[: sig_line - 1] - + [new_signature] - + lines[sig_end_line:] - ) - changes_applied += 1 - else: - # Normal single-line change - if change.line <= len(lines): - line = lines[change.line - 1] - new_line = ( - line[: change.column] - + change.new_text - + line[change.end_column :] - ) - lines[change.line - 1] = new_line - changes_applied += 1 - - # Write back - await loop.run_in_executor( - None, self._write_file_sync, target_file, "\n".join(lines) - ) - await self._invalidate_cache(target_file) - files_changed += 1 - - except Exception as e: - errors.append(f"{target_file}: {str(e)}") - - return RefactorResult( - success=len(errors) == 0, - action="change_signature", - files_changed=files_changed, - changes_applied=changes_applied, - errors=errors, - ) - - async def _organize_imports( - self, - file_path: Optional[str], - preview: bool, - ) -> RefactorResult: - """Organize and sort import statements.""" - if not file_path: - return RefactorResult( - success=False, action="organize_imports", errors=["file is required"] - ) - - cache = await self._get_file_cached(file_path) - if not cache: - return RefactorResult( - success=False, action="organize_imports", errors=["Cannot read file"] - ) - - language = self._get_language(file_path) - - if language == "python": - organized = self._organize_python_imports(cache.content) - elif language in ["javascript", "typescript"]: - organized = self._organize_js_imports(cache.content) - else: - return RefactorResult( - success=False, - action="organize_imports", - errors=[f"organize_imports not supported for {language}"], - ) - - if organized == cache.content: - return RefactorResult( - success=True, - action="organize_imports", - message="Imports are already organized", - ) - - if preview: - return RefactorResult( - success=True, - action="organize_imports", - preview=[ - {"file": file_path, "changes": "Import statements reorganized"} - ], - message="Would reorganize import statements", - ) - - loop = asyncio.get_event_loop() - await loop.run_in_executor(None, self._write_file_sync, file_path, organized) - await self._invalidate_cache(file_path) - - return RefactorResult( - success=True, - action="organize_imports", - files_changed=1, - changes_applied=1, - message="Organized import statements", - ) - - # ==================== HELPER METHODS ==================== - - def _get_identifier_at(self, line: str, column: int) -> Optional[str]: - """Get the identifier at a specific column in a line.""" - if column >= len(line): - column = max(0, len(line) - 1) - if column < 0 or not line: - return None - - start = column - while start > 0 and (line[start - 1].isalnum() or line[start - 1] == "_"): - start -= 1 - - end = column - while end < len(line) and (line[end].isalnum() or line[end] == "_"): - end += 1 - - if start == end: - return None - - identifier = line[start:end] - return ( - identifier - if identifier and (identifier[0].isalpha() or identifier[0] == "_") - else None - ) - - def _get_indentation(self, line: str) -> str: - """Get the indentation of a line.""" - match = re.match(r"^(\s*)", line) - return match.group(1) if match else "" - - def _find_used_variables(self, code: str, language: str) -> Set[str]: - """Find variables used in a code block.""" - identifiers = set(re.findall(r"\b([a-zA-Z_][a-zA-Z0-9_]*)\b", code)) - keywords = { - "if", - "else", - "for", - "while", - "def", - "class", - "return", - "import", - "from", - "in", - "and", - "or", - "not", - "True", - "False", - "None", - "async", - "await", - "try", - "except", - "finally", - "with", - "as", - "is", - "lambda", - "yield", - "break", - "continue", - "pass", - "raise", - "global", - "nonlocal", - "assert", - "del", - "function", - "const", - "let", - "var", - "new", - "this", - "super", - "extends", - "func", - "type", - "struct", - "interface", - "package", - "fn", - "mut", - "pub", - } - return identifiers - keywords - - def _find_defined_variables(self, code: str, language: str) -> Set[str]: - """Find variables defined in a code block.""" - if language == "python": - return set( - re.findall(r"^\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*=", code, re.MULTILINE) - ) - elif language in ["javascript", "typescript"]: - return set( - re.findall(r"(?:let|const|var)\s+([a-zA-Z_][a-zA-Z0-9_]*)", code) - ) - elif language == "go": - return set(re.findall(r"([a-zA-Z_][a-zA-Z0-9_]*)\s*:=", code)) - return set() - - def _build_function( - self, name: str, params: List[str], body: str, language: str, base_indent: str - ) -> str: - """Build a function definition.""" - params_str = ", ".join(params) - - if language == "python": - body_lines = body.split("\n") - if body_lines: - min_indent = min( - (len(self._get_indentation(l)) for l in body_lines if l.strip()), - default=0, - ) - dedented = "\n".join( - l[min_indent:] if l.strip() else "" for l in body_lines - ) - indented_body = "\n".join( - f" {l}" if l.strip() else "" for l in dedented.split("\n") - ) - else: - indented_body = " pass" - return f"def {name}({params_str}):\n{indented_body}" - elif language in ["javascript", "typescript"]: - return f"function {name}({params_str}) {{\n{body}\n}}" - elif language == "go": - return f"func {name}({params_str}) {{\n{body}\n}}" - return f"function {name}({params_str}) {{\n{body}\n}}" - - def _build_function_call( - self, name: str, params: List[str], language: str, indent: str - ) -> str: - """Build a function call.""" - params_str = ", ".join(params) - return f"{indent}{name}({params_str})" - - def _build_variable_declaration( - self, name: str, value: str, language: str, indent: str - ) -> str: - """Build a variable declaration.""" - if language == "python": - return f"{indent}{name} = {value}" - elif language in ["javascript", "typescript"]: - return f"{indent}const {name} = {value};" - elif language == "go": - return f"{indent}{name} := {value}" - return f"{indent}{name} = {value}" - - def _find_definition( - self, content: str, identifier: str, language: str - ) -> Optional[Dict[str, Any]]: - """Find the definition of a variable.""" - lines = content.split("\n") - - if language == "python": - pattern = rf"^\s*{re.escape(identifier)}\s*=\s*(.+?)$" - elif language in ["javascript", "typescript"]: - pattern = ( - rf"^\s*(?:const|let|var)\s+{re.escape(identifier)}\s*=\s*(.+?);?\s*$" - ) - elif language == "go": - pattern = rf"^\s*{re.escape(identifier)}\s*:=\s*(.+?)$" - else: - pattern = rf"^\s*{re.escape(identifier)}\s*=\s*(.+?)$" - - for i, line in enumerate(lines): - match = re.match(pattern, line) - if match: - return {"line": i + 1, "value": match.group(1).strip()} - - return None - - def _find_definition_block( - self, content: str, identifier: str, start_line: int, language: str - ) -> Optional[Dict[str, Any]]: - """Find the full definition block for an identifier.""" - lines = content.split("\n") - - if language == "python": - for i in range(max(0, start_line - 5), min(len(lines), start_line + 5)): - line = lines[i] - if re.match( - rf"^\s*(def|class)\s+{re.escape(identifier)}\s*[(\[]", line - ): - base_indent = len(self._get_indentation(line)) - end_line = i + 1 - while end_line < len(lines): - next_line = lines[end_line] - if ( - next_line.strip() - and len(self._get_indentation(next_line)) <= base_indent - ): - break - end_line += 1 - - return { - "start": i + 1, - "end": end_line, - "code": "\n".join(lines[i:end_line]), - } - return None - - def _organize_python_imports(self, content: str) -> str: - """Organize Python import statements.""" - lines = content.split("\n") - imports = [] - from_imports = [] - other_lines = [] - import_section_ended = False - - for line in lines: - stripped = line.strip() - if not import_section_ended: - if stripped.startswith("import "): - imports.append(line) - elif stripped.startswith("from "): - from_imports.append(line) - elif stripped and not stripped.startswith("#"): - import_section_ended = True - other_lines.append(line) - else: - if imports or from_imports: - import_section_ended = True - other_lines.append(line) - else: - other_lines.append(line) - - imports.sort(key=lambda x: x.strip().lower()) - from_imports.sort(key=lambda x: x.strip().lower()) - - result = [] - if imports: - result.extend(imports) - if from_imports: - if imports: - result.append("") - result.extend(from_imports) - if imports or from_imports: - result.append("") - result.extend(other_lines) - - return "\n".join(result) - - def _organize_js_imports(self, content: str) -> str: - """Organize JavaScript/TypeScript import statements.""" - lines = content.split("\n") - imports = [] - other_lines = [] - in_imports = True - - for line in lines: - stripped = line.strip() - if in_imports and stripped.startswith("import "): - imports.append(line) - elif in_imports and stripped == "": - continue - else: - in_imports = False - other_lines.append(line) - - imports.sort(key=lambda x: x.strip().lower()) - - result = imports + [""] + other_lines if imports else other_lines - return "\n".join(result) - - async def call(self, **kwargs) -> str: - """Tool interface for MCP - converts result to JSON string.""" - result = await self.run(**kwargs) - return result.to_json_string() - - def register(self, mcp_server) -> None: - """Register tool with MCP server.""" - - @mcp_server.tool(name=self.name, description=self.description) - async def refactor_handler( - action: str, - file: Optional[str] = None, - line: Optional[int] = None, - column: Optional[int] = None, - end_line: Optional[int] = None, - end_column: Optional[int] = None, - new_name: Optional[str] = None, - start_line: Optional[int] = None, - target_file: Optional[str] = None, - preview: bool = False, - path: Optional[str] = None, - renames: Optional[List[Dict[str, str]]] = None, - parallel: bool = True, - ) -> str: - """Execute refactoring action.""" - return await self.call( - action=action, - file=file, - line=line, - column=column, - end_line=end_line, - end_column=end_column, - new_name=new_name, - start_line=start_line, - target_file=target_file, - preview=preview, - path=path, - renames=renames, - parallel=parallel, - ) - - -# Factory function -def create_refactor_tool(max_workers: int = MAX_CONCURRENT_FILES): - """Factory function to create refactoring tool.""" - return RefactorTool(max_workers=max_workers) diff --git a/pkg/hanzo-tools-refactor/pyproject.toml b/pkg/hanzo-tools-refactor/pyproject.toml deleted file mode 100644 index d0b9b70a3..000000000 --- a/pkg/hanzo-tools-refactor/pyproject.toml +++ /dev/null @@ -1,48 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-refactor" -version = "0.2.0" -description = "Refactoring tools for Hanzo AI - rename, extract, inline, move with LSP/AST" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -keywords = ["hanzo", "tools", "refactor", "lsp", "ast", "mcp", "ai"] -dependencies = [ - "hanzo-tools>=0.3.0", - "mcp>=1.25.0", - "fastmcp>=2.14.1", - "pydantic>=2.12.5", -] - -[project.optional-dependencies] -full = [ - "tree-sitter>=0.24.0", - "tree-sitter-python>=0.23.6", - "tree-sitter-javascript>=0.23.1", - "tree-sitter-typescript>=0.23.2", - "tree-sitter-go>=0.23.4", - "tree-sitter-rust>=0.23.2", -] -dev = ["pytest>=7.0.0", "ruff>=0.14.0"] - -[project.urls] -"Homepage" = "https://github.com/hanzoai/python-sdk" - -[project.entry-points."hanzo.tools"] -refactor = "hanzo_tools.refactor:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] - -[tool.setuptools.package-data] -hanzo_tools = ["py.typed"] diff --git a/pkg/hanzo-tools-refactor/tests/test_refactor_tools.py b/pkg/hanzo-tools-refactor/tests/test_refactor_tools.py deleted file mode 100644 index bdb45fc33..000000000 --- a/pkg/hanzo-tools-refactor/tests/test_refactor_tools.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Tests for hanzo-tools-refactor.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import refactor - - assert refactor is not None - - def test_import_tools(self): - from hanzo_tools.refactor import TOOLS - - assert len(TOOLS) > 0 - - def test_import_refactor_tool(self): - from hanzo_tools.refactor import RefactorTool - - assert RefactorTool.name == "refactor" - - -class TestRefactorTool: - """Tests for RefactorTool.""" - - @pytest.fixture - def tool(self): - from hanzo_tools.refactor import RefactorTool - - return RefactorTool() - - def test_has_description(self, tool): - assert tool.description - assert "refactor" in tool.description.lower() diff --git a/pkg/hanzo-tools-repl/README.md b/pkg/hanzo-tools-repl/README.md deleted file mode 100644 index fe7dd594c..000000000 --- a/pkg/hanzo-tools-repl/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# hanzo-tools-repl - -Multi-language REPL tool with Jupyter kernel backend for interactive code evaluation. - -## Features - -- **Multi-language support**: Python, Node.js/TypeScript, Bash, Ruby, Go, Rust -- **Persistent sessions**: Maintain state across evaluations -- **Jupyter kernels**: Uses standard Jupyter infrastructure -- **Agent-friendly**: Designed for AI agent workflows - -## Installation - -```bash -pip install hanzo-tools-repl - -# With all kernels -pip install hanzo-tools-repl[full] -``` - -## Usage - -```python -from hanzo_tools.repl import ReplTool - -repl = ReplTool() - -# Start a Python session -await repl.call(action="start", language="python") - -# Evaluate code -result = await repl.call(action="eval", code="x = 1 + 1\nprint(x)") -# Output: [1] 2 - -# Node.js/TypeScript -await repl.call(action="start", language="node") -result = await repl.call(action="eval", code="const arr = [1,2,3].map(n => n*2)") - -# List sessions -await repl.call(action="list") - -# Get history -await repl.call(action="history", limit=10) - -# Stop session -await repl.call(action="stop") -``` - -## Supported Languages - -| Language | Kernel | Install | -|----------|--------|---------| -| Python | ipykernel | `pip install ipykernel` | -| Node.js/TypeScript | tslab | `npm install -g tslab && tslab install` | -| Bash | bash_kernel | `pip install bash_kernel` | -| Ruby | iruby | `gem install iruby` | -| Go | gophernotes | See gophernotes docs | -| Rust | evcxr | `cargo install evcxr_jupyter` | - -## License - -MIT - Hanzo Industries Inc diff --git a/pkg/hanzo-tools-repl/hanzo_tools/__init__.py b/pkg/hanzo-tools-repl/hanzo_tools/__init__.py deleted file mode 100644 index 68ff254a7..000000000 --- a/pkg/hanzo-tools-repl/hanzo_tools/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Namespace package - see PEP 420 -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-repl/hanzo_tools/repl/__init__.py b/pkg/hanzo-tools-repl/hanzo_tools/repl/__init__.py deleted file mode 100644 index 9b07a00e6..000000000 --- a/pkg/hanzo-tools-repl/hanzo_tools/repl/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -hanzo-tools-repl: Multi-language REPL with Jupyter kernel backend. - -Provides interactive code evaluation for agents across multiple languages: -- Python (ipykernel) -- Node.js/TypeScript (tslab) -- Bash (bash_kernel) -- And any language with a Jupyter kernel - -Usage: - repl(action="start", language="python") # Start kernel - repl(action="eval", code="print('hello')") # Evaluate code - repl(action="eval", code="const x = 1", language="typescript") - repl(action="history") # Get history - repl(action="stop") # Stop kernel - repl(action="list") # List kernels -""" - -from .repl_tool import ReplTool, KernelManager - -TOOLS = [ReplTool()] - -__all__ = ["ReplTool", "KernelManager", "TOOLS"] diff --git a/pkg/hanzo-tools-repl/hanzo_tools/repl/repl_tool.py b/pkg/hanzo-tools-repl/hanzo_tools/repl/repl_tool.py deleted file mode 100644 index 70aafbe91..000000000 --- a/pkg/hanzo-tools-repl/hanzo_tools/repl/repl_tool.py +++ /dev/null @@ -1,490 +0,0 @@ -""" -Unified REPL Tool with Multi-Language Jupyter Kernel Support. - -Provides persistent interactive sessions for code evaluation across languages. -Uses Jupyter kernels as backend for consistent cross-language experience. -""" - -from __future__ import annotations - -import uuid -import asyncio -from typing import Any -from datetime import datetime -from dataclasses import field, dataclass - -# Jupyter client imports -try: - from jupyter_client import KernelManager as JupyterKernelManager - from jupyter_client.asynchronous import AsyncKernelClient - - JUPYTER_AVAILABLE = True -except ImportError: - JUPYTER_AVAILABLE = False - JupyterKernelManager = None - AsyncKernelClient = None - -from hanzo_tools.core import BaseTool - -# Language to kernel mapping -LANGUAGE_KERNELS: dict[str, str] = { - # Python variants - "python": "python3", - "python3": "python3", - "py": "python3", - "ipython": "python3", - # JavaScript/TypeScript - "javascript": "tslab", - "js": "tslab", - "typescript": "tslab", - "ts": "tslab", - "node": "tslab", - # Shell - "bash": "bash", - "sh": "bash", - "shell": "bash", - "zsh": "bash", - # Other languages (require kernel installation) - "ruby": "ruby", - "go": "gophernotes", - "rust": "evcxr", - "julia": "julia-1.10", - "r": "ir", -} - - -@dataclass -class ExecutionResult: - """Result of code execution.""" - - success: bool - output: str - error: str | None = None - execution_count: int = 0 - data: dict[str, Any] = field(default_factory=dict) - duration_ms: float = 0 - - -@dataclass -class KernelSession: - """Represents an active kernel session.""" - - id: str - language: str - kernel_name: str - manager: Any # JupyterKernelManager - client: Any # AsyncKernelClient - created_at: datetime = field(default_factory=datetime.now) - execution_count: int = 0 - history: list[tuple[str, ExecutionResult]] = field(default_factory=list) - - -class KernelManager: - """Manages multiple Jupyter kernel sessions.""" - - _instance: KernelManager | None = None - - def __init__(self) -> None: - self.sessions: dict[str, KernelSession] = {} - self.default_session: str | None = None - self._lock = asyncio.Lock() - - @classmethod - def get_instance(cls) -> KernelManager: - """Get singleton instance.""" - if cls._instance is None: - cls._instance = cls() - return cls._instance - - async def start_kernel( - self, - language: str = "python", - session_id: str | None = None, - ) -> KernelSession: - """Start a new kernel session.""" - if not JUPYTER_AVAILABLE: - raise RuntimeError( - "jupyter-client not installed. Install with: pip install hanzo-tools-repl[full]" - ) - - kernel_name = LANGUAGE_KERNELS.get(language.lower(), language) - session_id = session_id or f"{language}_{uuid.uuid4().hex[:8]}" - - async with self._lock: - if session_id in self.sessions: - return self.sessions[session_id] - - # Create kernel manager - km = JupyterKernelManager(kernel_name=kernel_name) - km.start_kernel() - - # Get async client - client = km.client() - client.start_channels() - - # Wait for kernel ready - await asyncio.sleep(0.5) - - session = KernelSession( - id=session_id, - language=language, - kernel_name=kernel_name, - manager=km, - client=client, - ) - - self.sessions[session_id] = session - - if self.default_session is None: - self.default_session = session_id - - return session - - async def execute( - self, - code: str, - session_id: str | None = None, - timeout: float = 30.0, - ) -> ExecutionResult: - """Execute code in a kernel session.""" - session_id = session_id or self.default_session - - if not session_id or session_id not in self.sessions: - # Auto-start default Python kernel - session = await self.start_kernel("python") - session_id = session.id - - session = self.sessions[session_id] - start_time = datetime.now() - - try: - # Execute code - msg_id = session.client.execute(code) - - output_parts: list[str] = [] - error_output: str | None = None - data: dict[str, Any] = {} - - # Collect results - while True: - try: - msg = await asyncio.wait_for( - asyncio.to_thread( - session.client.get_iopub_msg, timeout=timeout - ), - timeout=timeout, - ) - except asyncio.TimeoutError: - break - - msg_type = msg.get("msg_type", "") - content = msg.get("content", {}) - - if msg_type == "stream": - output_parts.append(content.get("text", "")) - - elif msg_type == "execute_result": - data = content.get("data", {}) - if "text/plain" in data: - output_parts.append(data["text/plain"]) - - elif msg_type == "display_data": - data.update(content.get("data", {})) - if "text/plain" in data: - output_parts.append(data["text/plain"]) - - elif msg_type == "error": - error_output = "\n".join(content.get("traceback", [])) - - elif msg_type == "status": - if content.get("execution_state") == "idle": - break - - duration = (datetime.now() - start_time).total_seconds() * 1000 - session.execution_count += 1 - - result = ExecutionResult( - success=error_output is None, - output="".join(output_parts), - error=error_output, - execution_count=session.execution_count, - data=data, - duration_ms=duration, - ) - - # Add to history - session.history.append((code, result)) - - return result - - except Exception as e: - return ExecutionResult( - success=False, - output="", - error=str(e), - ) - - async def stop_kernel(self, session_id: str | None = None) -> bool: - """Stop a kernel session.""" - session_id = session_id or self.default_session - - if not session_id or session_id not in self.sessions: - return False - - async with self._lock: - session = self.sessions.pop(session_id) - session.client.stop_channels() - session.manager.shutdown_kernel() - - if self.default_session == session_id: - self.default_session = next(iter(self.sessions), None) - - return True - - async def stop_all(self) -> int: - """Stop all kernel sessions.""" - count = len(self.sessions) - for session_id in list(self.sessions.keys()): - await self.stop_kernel(session_id) - return count - - def list_sessions(self) -> list[dict[str, Any]]: - """List all active sessions.""" - return [ - { - "id": s.id, - "language": s.language, - "kernel": s.kernel_name, - "created": s.created_at.isoformat(), - "executions": s.execution_count, - "is_default": s.id == self.default_session, - } - for s in self.sessions.values() - ] - - def get_history( - self, - session_id: str | None = None, - limit: int = 20, - ) -> list[dict[str, Any]]: - """Get execution history for a session.""" - session_id = session_id or self.default_session - - if not session_id or session_id not in self.sessions: - return [] - - session = self.sessions[session_id] - history = session.history[-limit:] - - return [ - { - "index": i + 1, - "code": code, - "success": result.success, - "output": result.output[:500] if result.output else None, - "error": result.error[:200] if result.error else None, - } - for i, (code, result) in enumerate(history) - ] - - @staticmethod - def list_available_kernels() -> list[dict[str, str]]: - """List available Jupyter kernels.""" - if not JUPYTER_AVAILABLE: - return [] - - try: - from jupyter_client.kernelspec import find_kernel_specs - - specs = find_kernel_specs() - return [{"name": name, "path": path} for name, path in specs.items()] - except Exception: - return [] - - -class ReplTool(BaseTool): - """ - Multi-language REPL with Jupyter kernel backend. - - Provides interactive code evaluation for agents across languages: - - Python, Node.js/TypeScript, Bash, and any Jupyter kernel - - Actions: - - start: Start a new kernel session - - eval: Execute code in a session - - stop: Stop a kernel session - - list: List active sessions - - history: Get execution history - - kernels: List available kernels - - Examples: - repl(action="start", language="python") - repl(action="eval", code="x = 1 + 1; print(x)") - repl(action="eval", code="console.log('hello')", language="node") - repl(action="history", limit=10) - repl(action="stop") - """ - - name = "repl" - - @property - def description(self) -> str: - return """Multi-language REPL with persistent Jupyter kernel sessions. - -ACTIONS: -- start: Start kernel (language: python|node|typescript|bash|ruby|go|rust) -- eval: Execute code (code: string, language?: string, session?: string) -- stop: Stop kernel (session?: string) -- list: List active sessions -- history: Get execution history (session?: string, limit?: int) -- kernels: List available Jupyter kernels - -LANGUAGES: python, node/javascript/typescript, bash/shell, ruby, go, rust - -EXAMPLES: - repl(action="start", language="python") - repl(action="eval", code="print('hello')") - repl(action="eval", code="const x = [1,2,3].map(n => n*2)", language="node") - repl(action="history") - repl(action="stop")""" - - def __init__(self) -> None: - super().__init__() - self.manager = KernelManager.get_instance() - - async def call( - self, - action: str = "eval", - code: str | None = None, - language: str = "python", - session: str | None = None, - limit: int = 20, - timeout: float = 30.0, - **kwargs: Any, - ) -> str: - """Execute REPL action.""" - - action = action.lower() - - if action == "start": - return await self._start(language, session) - - elif action in ("eval", "execute", "run"): - if not code: - return "Error: code parameter required for eval action" - return await self._eval(code, language, session, timeout) - - elif action == "stop": - return await self._stop(session) - - elif action == "list": - return self._list() - - elif action == "history": - return self._history(session, limit) - - elif action == "kernels": - return self._kernels() - - else: - return f"Unknown action: {action}. Use: start, eval, stop, list, history, kernels" - - async def _start(self, language: str, session_id: str | None) -> str: - """Start a new kernel session.""" - try: - session = await self.manager.start_kernel(language, session_id) - return f"""Kernel started: - Session: {session.id} - Language: {session.language} - Kernel: {session.kernel_name} - -Use repl(action="eval", code="...") to execute code.""" - except Exception as e: - return f"Error starting kernel: {e}" - - async def _eval( - self, - code: str, - language: str, - session_id: str | None, - timeout: float, - ) -> str: - """Execute code in a session.""" - # Auto-start kernel if needed - if not self.manager.sessions: - await self.manager.start_kernel(language) - elif session_id is None and language != "python": - # Start language-specific kernel if not exists - lang_sessions = [ - s - for s in self.manager.sessions.values() - if s.language.lower() == language.lower() - ] - if not lang_sessions: - await self.manager.start_kernel(language) - - result = await self.manager.execute(code, session_id, timeout) - - if result.success: - output = result.output or "(no output)" - return f"""[{result.execution_count}] {output}""" - else: - return f"""[{result.execution_count}] Error: -{result.error}""" - - async def _stop(self, session_id: str | None) -> str: - """Stop a kernel session.""" - if session_id == "all": - count = await self.manager.stop_all() - return f"Stopped {count} kernel(s)" - - success = await self.manager.stop_kernel(session_id) - if success: - return f"Kernel stopped: {session_id or 'default'}" - return "No active kernel to stop" - - def _list(self) -> str: - """List active sessions.""" - sessions = self.manager.list_sessions() - if not sessions: - return "No active REPL sessions" - - lines = ["Active REPL sessions:"] - for s in sessions: - default = " (default)" if s["is_default"] else "" - lines.append( - f" {s['id']}: {s['language']} ({s['kernel']}) - {s['executions']} executions{default}" - ) - return "\n".join(lines) - - def _history(self, session_id: str | None, limit: int) -> str: - """Get execution history.""" - history = self.manager.get_history(session_id, limit) - if not history: - return "No execution history" - - lines = ["Execution history:"] - for h in history: - status = "OK" if h["success"] else "ERR" - code_preview = h["code"][:50].replace("\n", "\\n") - if len(h["code"]) > 50: - code_preview += "..." - lines.append(f" [{h['index']}] {status}: {code_preview}") - return "\n".join(lines) - - def _kernels(self) -> str: - """List available kernels.""" - kernels = self.manager.list_available_kernels() - if not kernels: - return "No Jupyter kernels found. Install with: pip install ipykernel" - - lines = ["Available Jupyter kernels:"] - for k in kernels: - lines.append(f" {k['name']}") - - lines.append("\nSupported language aliases:") - for lang, kernel in sorted(set((v, k) for k, v in LANGUAGE_KERNELS.items())): - aliases = [k for k, v in LANGUAGE_KERNELS.items() if v == lang] - lines.append(f" {lang}: {', '.join(aliases)}") - - return "\n".join(lines) diff --git a/pkg/hanzo-tools-repl/pyproject.toml b/pkg/hanzo-tools-repl/pyproject.toml deleted file mode 100644 index 220b84d3a..000000000 --- a/pkg/hanzo-tools-repl/pyproject.toml +++ /dev/null @@ -1,51 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-repl" -version = "0.1.0" -description = "Multi-language REPL tool with Jupyter kernel backend for interactive code evaluation" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -keywords = ["mcp", "repl", "jupyter", "interactive", "code", "evaluation"] -dependencies = [ - "hanzo-tools-core>=0.1.0", - "jupyter-client>=8.6.0", - "jupyter-core>=5.7.0", -] - -[project.optional-dependencies] -kernels = [ - "ipykernel>=6.29.0", # Python kernel - "bash_kernel>=0.9.0", # Bash kernel - "typescript-kernel>=0.1.0", # TypeScript (via ts-node) -] -node = ["tslab>=1.0.0"] # Node.js/TypeScript Jupyter kernel -full = [ - "hanzo-tools-repl[kernels,node]", -] -dev = [ - "pytest>=7.0.0", - "pytest-asyncio>=0.23.0", -] - -[project.urls] -"Homepage" = "https://github.com/hanzoai/python-sdk" - -[project.entry-points."hanzo.tools"] -repl = "hanzo_tools.repl:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] - -[tool.setuptools.package-data] -hanzo_tools = ["py.typed"] diff --git a/pkg/hanzo-tools-shell/README.md b/pkg/hanzo-tools-shell/README.md deleted file mode 100644 index 26f22831b..000000000 --- a/pkg/hanzo-tools-shell/README.md +++ /dev/null @@ -1,98 +0,0 @@ -# hanzo-tools-shell - -Shell execution tools for Hanzo MCP with DAG support and Shellflow DSL. - -## Installation - -```bash -pip install hanzo-tools-shell -``` - -## Tools - -### dag - DAG Execution Engine -Execute commands with directed acyclic graph semantics. - -```python -# Serial execution (default) -dag(["ls", "pwd", "git status"]) - -# Parallel execution -dag(["npm install", "cargo build"], parallel=True) - -# Mixed DAG with parallel blocks -dag([ - "mkdir -p dist", - {"parallel": ["cp a.txt dist/", "cp b.txt dist/"]}, - "zip -r out.zip dist/" -]) - -# Tool invocations -dag([{"tool": "search", "input": {"pattern": "TODO"}}]) - -# Named nodes with dependencies -dag([ - {"id": "build", "run": "make build"}, - {"id": "test", "run": "make test", "after": ["build"]}, -]) -``` - -### zsh - Primary Shell with Shellflow DSL -Execute shell commands with optional Shellflow syntax. - -```python -# Simple command -zsh("ls -la") - -# Shellflow DSL syntax -zsh("mkdir dist ; { cp a dist/ & cp b dist/ } ; zip out") - -# With shell parameter -zsh("echo $BASH_VERSION", shell="bash") -``` - -**Shellflow Syntax:** -- `A ; B` - Sequential execution -- `{ A & B }` - Parallel execution -- `A ; { B & C } ; D` - Mixed DAG - -### ps - Process Management -Monitor and control background processes. - -```python -ps() # List all processes -ps(id="abc123") # Get specific process -ps(kill="abc123") # Kill process (SIGTERM) -ps(logs="abc123", n=50) # Last 50 lines of output -``` - -### Additional Tools -- **shell** - Smart shell (zsh > bash fallback) -- **bash** - Explicit bash execution -- **npx** - Node package execution with auto-backgrounding -- **uvx** - Python package execution with auto-backgrounding -- **open** - Open files/URLs in system apps -- **curl** - HTTP client without shell escaping issues -- **jq** - JSON processor -- **wget** - File/site downloads - -## Auto-Backgrounding - -Commands that exceed the timeout (default 60s) are automatically backgrounded: - -```python -dag(["long-running-command"], timeout=30) -# If command exceeds 30s, it continues in background -# Use ps --logs to view output -``` - -## Performance - -Shellflow DSL is optimized for high throughput: -- Simple commands: ~7M ops/sec -- Sequential: ~2.2M ops/sec -- Mixed DAG: ~100k ops/sec - -## License - -MIT diff --git a/pkg/hanzo-tools-shell/hanzo_tools/__init__.py b/pkg/hanzo-tools-shell/hanzo_tools/__init__.py deleted file mode 100644 index f4f8ea812..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Hanzo Tools namespace package.""" - -__path__ = __import__("pkgutil").extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/__init__.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/__init__.py deleted file mode 100644 index 5b755e782..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/__init__.py +++ /dev/null @@ -1,379 +0,0 @@ -"""Shell tools package for Hanzo AI. - -Minimal, orthogonal shell execution with uvloop for high performance. - -SHELL DETECTION: -By default, only the user's active shell is exposed to MCP. This avoids -cluttering the tool list with shells the user doesn't use. - -Detection order: -1. HANZO_MCP_SHELL env var (e.g., "zsh", "bash", "fish") -2. HANZO_MCP_FORCE_SHELL env var (e.g., "/opt/homebrew/bin/zsh") -3. --shell CLI flag (e.g., --shell=/path/to/fish) -4. Invoking shell (the shell that launched this process) -5. Login shell (from passwd/Directory Services) -6. $SHELL environment variable - -On macOS with Homebrew, this will prefer /opt/homebrew/bin/zsh over /bin/zsh. -If detection fails, falls back to 'shell' tool (smart auto-detect). - -Core tools: -- : Your active shell (zsh, bash, fish, or dash) -- ps: Process management (list, kill, logs) - -HTTP/Data tools: -- curl: HTTP client without shell escaping issues -- jq: JSON processor without shell escaping issues -- wget: File/site downloads with mirroring support - -Convenience tools: -- npx: Node package execution with auto-backgrounding -- uvx: Python package execution with auto-backgrounding -- open: Open files/URLs in system apps - -Auto-backgrounding: Commands exceeding timeout automatically background. -Configure via: export HANZO_AUTO_BACKGROUND_TIMEOUT=30 (default: 30s) - export HANZO_AUTO_BACKGROUND_TIMEOUT=0 (disabled) -Use ps --logs to view, ps --kill to stop. - -Performance: Uses uvloop on macOS/Linux for 2-4x faster async I/O. -Falls back to standard asyncio on Windows. -""" - -# Use hanzo-async for unified async I/O and uvloop configuration -from hanzo_async import using_uvloop, configure_loop - -# Auto-configure uvloop on import -configure_loop() -_using_uvloop = using_uvloop() - -from mcp.server import FastMCP - -from hanzo_tools.core import BaseTool, ToolRegistry, PermissionManager -from hanzo_tools.shell.jq_tool import JqTool -from hanzo_tools.shell.ps_tool import PsTool, ps_tool - -# Core tools -from hanzo_tools.shell.cmd_tool import ( - CmdNode, - CmdTool, - CmdResult, - NodeStatus, - cmd_tool, - create_cmd_tool, -) - -# Backwards compatibility - DagTool is now CmdTool -from hanzo_tools.shell.dag_tool import DagNode, DagTool, DagResult, create_dag_tool -from hanzo_tools.shell.npx_tool import NpxTool, npx_tool -from hanzo_tools.shell.truncate import ( - truncate_lines, - estimate_tokens, - truncate_response, -) -from hanzo_tools.shell.uvx_tool import UvxTool, uvx_tool - -# HTTP/Data tools (no shell escaping issues) -from hanzo_tools.shell.curl_tool import CurlTool - -# HIP-0300: Unified exec tool -from hanzo_tools.shell.exec_tool import ExecTool, exec_tool - -# Convenience tools -from hanzo_tools.shell.open_tool import OpenTool, open_tool -from hanzo_tools.shell.shellflow import ( - parse as parse_shellflow, - compile as compile_shellflow, -) -from hanzo_tools.shell.wget_tool import WgetTool -from hanzo_tools.shell.shell_tools import ( - CshTool, - KshTool, - ZshTool, - BashTool, - DashTool, - FishTool, - TcshTool, - ShellTool, - csh_tool, - ksh_tool, - zsh_tool, - bash_tool, - dash_tool, - fish_tool, - tcsh_tool, - shell_tool, -) - -# Base classes -from hanzo_tools.shell.base_process import ( - AUTO_BACKGROUND_TIMEOUT, - ShellExecutor, - BaseBinaryTool, - BaseScriptTool, - ProcessManager, - BaseProcessTool, - AutoBackgroundExecutor, - get_shell_executor, -) - -# Shell detection -from hanzo_tools.shell.shell_detect import ( - SUPPORTED_SHELLS, - ShellInfo, - detect_shells, - get_active_shell, - clear_shell_cache, - resolve_shell_path, - get_shell_tool_class, - get_cached_active_shell, -) - -# Session management -from hanzo_tools.shell.session_storage import ( - SessionInfo, - SessionStorage, - clear_all_sessions, - cleanup_expired_sessions, -) - - -def _get_detected_shell_tools() -> list: - """ - Get shell tools list with only the detected active shell. - - This reduces tool clutter by only exposing the user's configured shell. - Override with: - - HANZO_MCP_SHELL=bash (shell name) - - HANZO_MCP_FORCE_SHELL=/path/to/shell (full path) - - HANZO_MCP_ALL_SHELLS=1 (expose all shells) - """ - import os - - # Allow exposing all shells via env var (for debugging/testing) - if os.environ.get("HANZO_MCP_ALL_SHELLS", "").lower() in ("1", "true", "yes"): - return [ - ZshTool, - BashTool, - FishTool, - DashTool, - KshTool, - TcshTool, - CshTool, - PsTool, - NpxTool, - UvxTool, - OpenTool, - CurlTool, - JqTool, - WgetTool, - ] - - # Detect active shell - shell_name, shell_path = get_cached_active_shell() - - # Get the appropriate shell tool class - shell_tool_class = get_shell_tool_class(shell_name) - - # Base tools (always included) - no cmd, just the detected shell - # ExecTool = HIP-0300 unified exec tool (action-routed: exec, ps, kill, logs) - tools = [ExecTool, PsTool, NpxTool, UvxTool, OpenTool, CurlTool, JqTool, WgetTool] - - # Add detected shell tool, or fall back to ShellTool (smart auto-detect) - if shell_tool_class: - tools.insert(0, shell_tool_class) # Put detected shell first - else: - tools.insert(0, ShellTool) # Fallback to smart shell - - return tools - - -# Tools list for entry point discovery -# Only the detected shell is included (not all 4 shell variants) -# Override with HANZO_MCP_ALL_SHELLS=1 to expose all shells -TOOLS = _get_detected_shell_tools() - -__all__ = [ - # uvloop status - "_using_uvloop", - # HIP-0300: Unified exec tool - "ExecTool", - "exec_tool", - # Base classes - "ProcessManager", - "AutoBackgroundExecutor", - "ShellExecutor", - "get_shell_executor", - "BaseProcessTool", - "BaseBinaryTool", - "BaseScriptTool", - # Session management - "SessionStorage", - "SessionInfo", - "cleanup_expired_sessions", - "clear_all_sessions", - # Utilities - "truncate_response", - "truncate_lines", - "estimate_tokens", - # Shell detection - "ShellInfo", - "detect_shells", - "get_active_shell", - "get_cached_active_shell", - "clear_shell_cache", - "get_shell_tool_class", - "resolve_shell_path", - "SUPPORTED_SHELLS", - # Legacy tools - "CmdTool", - "CmdResult", - "CmdNode", - "NodeStatus", - "cmd_tool", - "create_cmd_tool", - "PsTool", - "ps_tool", - "ZshTool", - "zsh_tool", - "ShellTool", - "shell_tool", - "BashTool", - "bash_tool", - "FishTool", - "fish_tool", - "DashTool", - "dash_tool", - "KshTool", - "ksh_tool", - "TcshTool", - "tcsh_tool", - "CshTool", - "csh_tool", - # Backwards compatibility - "DagTool", - "DagResult", - "DagNode", - "create_dag_tool", - # Convenience tools - "OpenTool", - "open_tool", - "NpxTool", - "npx_tool", - "UvxTool", - "uvx_tool", - # HTTP/Data tools - "CurlTool", - "JqTool", - "WgetTool", - # Registration - "TOOLS", - "get_shell_tools", - "register_shell_tools", -] - - -def get_shell_tools( - permission_manager: PermissionManager, - all_tools: dict[str, BaseTool] | None = None, - shell_override: str | None = None, -) -> list[BaseTool]: - """Create instances of shell tools. - - Only the user's detected/configured shell is included to reduce tool clutter. - - Args: - permission_manager: Permission manager for access control - all_tools: Dict of all registered tools (for tool invocations) - shell_override: Override shell (name like "zsh" or path like "/path/to/fish") - - Returns: - List of shell tool instances - """ - import os - - # Set permission manager for convenience tools - npx_tool.permission_manager = permission_manager - uvx_tool.permission_manager = permission_manager - - # Base tools list (no cmd - just the detected shell and utilities) - tools = [ - ps_tool, # Process management - npx_tool, # Node packages - uvx_tool, # Python packages - open_tool, # Open files/URLs - ] - - # Check for all-shells mode - if os.environ.get("HANZO_MCP_ALL_SHELLS", "").lower() in ("1", "true", "yes"): - tools.insert(0, ZshTool(tools=all_tools or {})) - tools.insert(1, BashTool(tools=all_tools or {})) - tools.insert(2, FishTool(tools=all_tools or {})) - tools.insert(3, DashTool(tools=all_tools or {})) - tools.insert(4, KshTool(tools=all_tools or {})) - tools.insert(5, TcshTool(tools=all_tools or {})) - tools.insert(6, CshTool(tools=all_tools or {})) - return tools - - # Determine which shell to expose - if shell_override: - # Handle path override (e.g., --shell=/opt/homebrew/bin/fish) - if "/" in shell_override: - shell_name = os.path.basename(shell_override) - os.environ["HANZO_MCP_FORCE_SHELL"] = shell_override - else: - shell_name = shell_override.lower() - else: - shell_name, _ = get_cached_active_shell() - - # Create and add detected shell tool, or fall back to ShellTool - shell_tool_class = get_shell_tool_class(shell_name) - if shell_tool_class: - shell_instance = shell_tool_class(tools=all_tools or {}) - tools.insert(0, shell_instance) # Put detected shell first - else: - tools.insert(0, ShellTool(tools=all_tools or {})) # Fallback to smart shell - - return tools - - -def register_shell_tools( - mcp_server: FastMCP, - permission_manager: PermissionManager, - all_tools: dict[str, BaseTool] | None = None, - shell_override: str | None = None, -) -> list[BaseTool]: - """Register shell tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - permission_manager: Permission manager for access control - all_tools: Dict of all registered tools (for cmd tool invocations) - shell_override: Override shell (name like "zsh" or path like "/path/to/fish") - - Returns: - List of registered tools - """ - tools = get_shell_tools(permission_manager, all_tools, shell_override) - ToolRegistry.register_tools(mcp_server, tools) - return tools - - -def register_tools(mcp_server: FastMCP, **kwargs) -> list[BaseTool]: - """Register all shell tools with the MCP server. - - This is the standard entry point called by the tool discovery system. - - Supports: - - shell_override: Override detected shell (name or path) - - permission_manager: Permission manager instance - - all_tools: Dict of all registered tools - """ - from hanzo_tools.core import PermissionManager - - permission_manager = kwargs.get("permission_manager") or PermissionManager() - all_tools = kwargs.get("all_tools") - shell_override = kwargs.get("shell_override") or kwargs.get("shell") - return register_shell_tools( - mcp_server, permission_manager, all_tools, shell_override - ) diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/base_process.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/base_process.py deleted file mode 100644 index fe6eacc65..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/base_process.py +++ /dev/null @@ -1,701 +0,0 @@ -"""Base classes for process execution tools. - -All process execution uses asyncio.subprocess for consistency. -All file I/O uses hanzo_async for non-blocking operations with uvloop support. - -Auto-backgrounding timeout can be configured via: - export HANZO_AUTO_BACKGROUND_TIMEOUT=30 # seconds (default: 30) - export HANZO_AUTO_BACKGROUND_TIMEOUT=0 # disable auto-backgrounding -""" - -import os -import time -import uuid -import asyncio -import tempfile -from abc import abstractmethod -from typing import Any, Dict, List, Tuple, Optional, override -from pathlib import Path - -from hanzo_async import mkdir, write_file, append_file - -from hanzo_tools.core import BaseTool, PermissionManager -from hanzo_tools.shell.truncate import truncate_response - -# Configurable auto-background timeout (seconds) -# Set via HANZO_AUTO_BACKGROUND_TIMEOUT env var -# 0 or negative = disabled (never auto-background, use 24h timeout) -_raw_timeout = float(os.getenv("HANZO_AUTO_BACKGROUND_TIMEOUT", "30")) -AUTO_BACKGROUND_TIMEOUT = ( - _raw_timeout if _raw_timeout > 0 else 86400.0 -) # 24 hours if disabled - - -class ProcessManager: - """Singleton manager for background processes. - - All processes are asyncio.subprocess.Process instances. - """ - - _instance = None - _processes: Dict[str, asyncio.subprocess.Process] = {} - _logs: Dict[str, str] = {} - _log_dir = Path(tempfile.gettempdir()) / "hanzo_mcp_logs" - _initialized: bool = False - - def __new__(cls): - if cls._instance is None: - cls._instance = super().__new__(cls) - return cls._instance - - async def _ensure_log_dir(self) -> None: - """Ensure log directory exists (async-safe).""" - if not self._initialized: - await mkdir(self._log_dir, parents=True, exist_ok=True) - self._initialized = True - - def add_process( - self, process_id: str, process: asyncio.subprocess.Process, log_file: str - ) -> None: - """Add a process to track.""" - self._processes[process_id] = process - self._logs[process_id] = log_file - - def get_process(self, process_id: str) -> Optional[asyncio.subprocess.Process]: - """Get a tracked process.""" - return self._processes.get(process_id) - - def remove_process(self, process_id: str) -> None: - """Remove a process from tracking.""" - self._processes.pop(process_id, None) - self._logs.pop(process_id, None) - - def list_processes(self) -> Dict[str, Dict[str, Any]]: - """List all tracked processes. - - Note: Uses list(items()) to create snapshot before cleanup, - preventing RuntimeError from dict modification during iteration. - Completed processes are cleaned up after being reported. - """ - result = {} - for pid, proc in list(self._processes.items()): - is_running = proc.returncode is None - - if is_running: - result[pid] = { - "pid": proc.pid, - "running": True, - "log_file": self._logs.get(pid), - } - else: - result[pid] = { - "pid": proc.pid, - "running": False, - "return_code": proc.returncode, - "log_file": self._logs.get(pid), - } - self.remove_process(pid) - return result - - def get_log_file(self, process_id: str) -> Optional[Path]: - """Get log file path for a process.""" - log_path = self._logs.get(process_id) - return Path(log_path) if log_path else None - - @property - def log_dir(self) -> Path: - """Get the log directory.""" - return self._log_dir - - async def create_log_file(self, process_id: str) -> Path: - """Create a log file for a process (async-safe).""" - await self._ensure_log_dir() - log_file = self._log_dir / f"{process_id}.log" - await write_file(log_file, "") - return log_file - - def mark_completed(self, process_id: str, return_code: int) -> None: - """Mark a process as completed.""" - self.remove_process(process_id) - - -class AutoBackgroundExecutor: - """Executor that automatically backgrounds long-running processes. - - IMPORTANT: Always backgrounds after AUTO_BACKGROUND_TIMEOUT to keep - the agent loop responsive. Longer timeouts only affect the background - process lifetime, not the foreground wait time. - - Configure via: - export HANZO_AUTO_BACKGROUND_TIMEOUT=30 # 30s timeout (default) - export HANZO_AUTO_BACKGROUND_TIMEOUT=0 # disabled (never auto-background) - """ - - DEFAULT_TIMEOUT = AUTO_BACKGROUND_TIMEOUT - MAX_FOREGROUND_TIMEOUT = AUTO_BACKGROUND_TIMEOUT - - def __init__( - self, process_manager: ProcessManager, timeout: float = DEFAULT_TIMEOUT - ): - """Initialize the auto-background executor.""" - self.process_manager = process_manager - self.default_timeout = timeout - - async def execute_with_auto_background( - self, - cmd_args: list[str], - tool_name: str, - cwd: Optional[Path] = None, - env: Optional[dict[str, str]] = None, - timeout: Optional[float] = None, - ) -> Tuple[str, bool, Optional[str]]: - """Execute a command with automatic backgrounding if it takes too long. - - Returns: - Tuple of (output/status, was_backgrounded, process_id) - - Note: Always backgrounds after MAX_FOREGROUND_TIMEOUT (30s) to keep - the agent loop responsive. The passed timeout is stored for reference - but doesn't extend foreground wait time. - """ - # ALWAYS cap at MAX_FOREGROUND_TIMEOUT to keep agent loop responsive - # Longer timeouts only affect background process, not foreground wait - requested_timeout = timeout if timeout is not None else self.default_timeout - effective_timeout = min(requested_timeout, self.MAX_FOREGROUND_TIMEOUT) - - # Fast path for tests - still async but with shorter timeout - if os.getenv("HANZO_MCP_FAST_TESTS") == "1": - try: - proc = await asyncio.create_subprocess_exec( - *cmd_args, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - cwd=str(cwd) if cwd else None, - env=env, - ) - stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=30) - if proc.returncode != 0: - return ( - f"Command failed with exit code {proc.returncode}:\n" - f"{stdout.decode('utf-8', errors='replace')}" - f"{stderr.decode('utf-8', errors='replace')}", - False, - None, - ) - return stdout.decode("utf-8", errors="replace"), False, None - except asyncio.TimeoutError: - return "Command timed out in test mode", False, None - except Exception as e: - return f"Error executing command: {e}", False, None - - # Generate process ID - process_id = f"{tool_name}_{uuid.uuid4().hex[:8]}" - - # Create log file - log_file = await self.process_manager.create_log_file(process_id) - - # Start the process - process = await asyncio.create_subprocess_exec( - *cmd_args, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.STDOUT, - cwd=cwd, - env=env, - ) - - # Track in process manager - self.process_manager.add_process(process_id, process, str(log_file)) - - # Try to wait for completion with timeout - start_time = time.time() - output_lines = [] - - try: - - async def read_output(): - if process.stdout: - async for line in process.stdout: - line_str = line.decode("utf-8", errors="replace") - output_lines.append(line_str) - await append_file(log_file, line_str) - - async def wait_for_process(): - return await process.wait() - - read_task = asyncio.create_task(read_output()) - wait_task = asyncio.create_task(wait_for_process()) - - done, pending = await asyncio.wait( - [read_task, wait_task], - timeout=effective_timeout, - return_when=asyncio.FIRST_COMPLETED, - ) - - if wait_task in done: - return_code = await wait_task - - try: - await asyncio.wait_for(read_task, timeout=0.5) - except asyncio.TimeoutError: - read_task.cancel() - try: - await read_task - except asyncio.CancelledError: - pass - - self.process_manager.mark_completed(process_id, return_code) - - output = "".join(output_lines) - if return_code != 0: - return ( - f"Command failed with exit code {return_code}:\n{output}", - False, - None, - ) - else: - return output, False, None - - else: - # Timeout - background the process - for task in pending: - task.cancel() - - asyncio.create_task( - self._background_reader(process, process_id, log_file) - ) - - elapsed = time.time() - start_time - partial_output = "".join(output_lines[-50:]) - - return ( - f"Process automatically backgrounded after {elapsed:.1f}s\n" - f"Process ID: {process_id}\n" - f"Log file: {log_file}\n\n" - f"Use 'ps --logs {process_id}' to view full output\n" - f"Use 'ps --kill {process_id}' to stop the process\n\n" - f"=== Last output ===\n{partial_output}", - True, - process_id, - ) - - except Exception as e: - self.process_manager.mark_completed(process_id, -1) - return f"Error executing command: {str(e)}", False, None - - async def _background_reader(self, process, process_id: str, log_file: Path): - """Continue reading output from a backgrounded process.""" - try: - if process.stdout: - async for line in process.stdout: - await append_file(log_file, line.decode("utf-8", errors="replace")) - - return_code = await process.wait() - self.process_manager.mark_completed(process_id, return_code) - - await append_file( - log_file, - f"\n\n=== Process completed with exit code {return_code} ===\n", - ) - - except Exception as e: - await append_file( - log_file, f"\n\n=== Background reader error: {str(e)} ===\n" - ) - self.process_manager.mark_completed(process_id, -1) - - -class BaseProcessTool(BaseTool): - """Base class for all process execution tools.""" - - def __init__(self, permission_manager: Optional[PermissionManager] = None): - """Initialize the process tool.""" - super().__init__() - self.permission_manager = permission_manager - self.process_manager = ProcessManager() - self.auto_background_executor = AutoBackgroundExecutor(self.process_manager) - - @abstractmethod - def get_command_args(self, command: str, **kwargs) -> List[str]: - """Get the command arguments for subprocess.""" - pass - - @abstractmethod - def get_tool_name(self) -> str: - """Get the name of the tool being used.""" - pass - - async def execute_sync( - self, - command: str, - cwd: Optional[Path] = None, - env: Optional[Dict[str, str]] = None, - timeout: Optional[int] = None, - **kwargs, - ) -> str: - """Execute a command with auto-backgrounding after 2 minutes.""" - if self.permission_manager and cwd: - if not self.permission_manager.is_path_allowed(str(cwd)): - raise PermissionError(f"Access denied to path: {cwd}") - - cmd_args = self.get_command_args(command, **kwargs) - - process_env = os.environ.copy() - if env: - process_env.update(env) - - output, was_backgrounded, process_id = ( - await self.auto_background_executor.execute_with_auto_background( - cmd_args=cmd_args, - tool_name=self.get_tool_name(), - cwd=cwd, - env=process_env, - timeout=float(timeout) if timeout is not None else None, - ) - ) - - if was_backgrounded: - return output - else: - if output.startswith("Command failed"): - raise RuntimeError(output) - max_tokens = int(os.environ.get("HANZO_MCP_MAX_RESPONSE_TOKENS", "25000")) - return truncate_response( - output, - max_tokens=max_tokens, - truncation_message=f"\n\n[Command output truncated due to {max_tokens} token limit.]", - ) - - async def execute_background( - self, - command: str, - cwd: Optional[Path] = None, - env: Optional[Dict[str, str]] = None, - **kwargs, - ) -> Dict[str, Any]: - """Execute a command in the background.""" - if self.permission_manager and cwd: - if not self.permission_manager.is_path_allowed(str(cwd)): - raise PermissionError(f"Access denied to path: {cwd}") - - process_id = f"{self.get_tool_name()}_{uuid.uuid4().hex[:8]}" - log_file = self.process_manager.log_dir / f"{process_id}.log" - - cmd_args = self.get_command_args(command, **kwargs) - - process_env = os.environ.copy() - if env: - process_env.update(env) - - process = await asyncio.create_subprocess_exec( - *cmd_args, - cwd=cwd, - env=process_env, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.STDOUT, - ) - - self.process_manager.add_process(process_id, process, str(log_file)) - asyncio.create_task(self._write_output_to_log(process, log_file, process_id)) - - return { - "process_id": process_id, - "pid": process.pid, - "log_file": str(log_file), - "status": "started", - } - - async def _write_output_to_log( - self, process: asyncio.subprocess.Process, log_file: Path, process_id: str - ) -> None: - """Write process output to log file in background.""" - try: - # Clear log file - await write_file(log_file, "") - if process.stdout: - async for line in process.stdout: - await append_file(log_file, line.decode("utf-8", errors="replace")) - - return_code = await process.wait() - - await append_file( - log_file, f"\n=== Process completed with exit code {return_code} ===\n" - ) - - self.process_manager.mark_completed(process_id, return_code) - - except Exception as e: - await append_file(log_file, f"\n=== Error: {str(e)} ===\n") - self.process_manager.mark_completed(process_id, -1) - - -class BaseBinaryTool(BaseProcessTool): - """Base class for binary execution tools (like npx, uvx).""" - - @abstractmethod - def get_binary_name(self) -> str: - """Get the name of the binary to execute.""" - pass - - @override - def get_command_args(self, command: str, **kwargs) -> List[str]: - """Get command arguments for binary execution.""" - cmd_args = [self.get_binary_name()] - - if "flags" in kwargs: - cmd_args.extend(kwargs["flags"]) - - cmd_args.append(command) - - if "args" in kwargs: - if isinstance(kwargs["args"], str): - cmd_args.extend(kwargs["args"].split()) - else: - cmd_args.extend(kwargs["args"]) - - return cmd_args - - @override - def get_tool_name(self) -> str: - """Get the tool name (same as binary name by default).""" - return self.get_binary_name() - - -class BaseScriptTool(BaseProcessTool): - """Base class for script execution tools (like bash, python).""" - - @abstractmethod - def get_interpreter(self) -> str: - """Get the interpreter to use.""" - pass - - @abstractmethod - def get_script_flags(self) -> List[str]: - """Get default flags for the interpreter.""" - pass - - @override - def get_command_args(self, command: str, **kwargs) -> List[str]: - """Get command arguments for script execution.""" - cmd_args = [self.get_interpreter()] - cmd_args.extend(self.get_script_flags()) - cmd_args.append(command) - return cmd_args - - @override - def get_tool_name(self) -> str: - """Get the tool name (interpreter name by default).""" - return self.get_interpreter() - - -# Shared singleton for shell execution -_shell_executor: Optional["ShellExecutor"] = None - - -class ShellExecutor: - """Shared async shell executor for all shell tools. - - Ensures consistent auto-backgrounding behavior across dag, zsh, shell, bash tools. - Uses a singleton pattern to share process management state. - - Configure via: - export HANZO_AUTO_BACKGROUND_TIMEOUT=30 # 30s timeout (default) - export HANZO_AUTO_BACKGROUND_TIMEOUT=0 # disabled (never auto-background) - """ - - DEFAULT_TIMEOUT = AUTO_BACKGROUND_TIMEOUT - - _instance: Optional["ShellExecutor"] = None - - def __new__(cls) -> "ShellExecutor": - if cls._instance is None: - cls._instance = super().__new__(cls) - cls._instance._initialized = False - return cls._instance - - def __init__(self) -> None: - if self._initialized: - return - self._initialized = True - self._process_manager = ProcessManager() - - @property - def process_manager(self) -> ProcessManager: - return self._process_manager - - async def run_shell( - self, - command: str, - shell: str, - cwd: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - timeout: float = DEFAULT_TIMEOUT, - tool_name: str = "shell", - ) -> Tuple[str, str, int, bool, Optional[str]]: - """Run a shell command with auto-backgrounding on timeout. - - Args: - command: Shell command to execute - shell: Shell binary path (e.g., /bin/zsh) - cwd: Working directory - env: Environment variables - timeout: Timeout before auto-backgrounding (default: 30s) - tool_name: Tool name for process ID prefix - - Returns: - Tuple of (stdout, stderr, exit_code, was_backgrounded, process_id) - """ - # Cap timeout at 30s for foreground wait - keep agent loop responsive - effective_timeout = min(timeout, self.DEFAULT_TIMEOUT) - - run_env = os.environ.copy() - if env: - run_env.update(env) - - shell_name = os.path.basename(shell) - process_id = f"{tool_name}_{uuid.uuid4().hex[:8]}" - log_file = await self._process_manager.create_log_file(process_id) - - try: - # Build shell invocation args per platform - shell_lower = os.path.basename(shell).lower() - if shell_lower in ("cmd", "cmd.exe"): - shell_args = [shell, "/c", command] - elif shell_lower in ("powershell", "powershell.exe", "pwsh", "pwsh.exe"): - shell_args = [shell, "-Command", command] - else: - shell_args = [shell, "-c", command] - - proc = await asyncio.create_subprocess_exec( - *shell_args, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - cwd=cwd, - env=run_env, - # Maps to CREATE_NEW_PROCESS_GROUP on Windows - start_new_session=True, - ) - - stdout_chunks: list[bytes] = [] - stderr_chunks: list[bytes] = [] - - async def read_stdout(): - if proc.stdout: - while True: - chunk = await proc.stdout.read(8192) - if not chunk: - break - stdout_chunks.append(chunk) - - async def read_stderr(): - if proc.stderr: - while True: - chunk = await proc.stderr.read(8192) - if not chunk: - break - stderr_chunks.append(chunk) - - try: - # Read streams and wait for process with timeout - await asyncio.wait_for( - asyncio.gather(read_stdout(), read_stderr(), proc.wait()), - timeout=effective_timeout, - ) - - exit_code = proc.returncode or 0 - return ( - b"".join(stdout_chunks).decode("utf-8", errors="replace"), - b"".join(stderr_chunks).decode("utf-8", errors="replace"), - exit_code, - False, # Not backgrounded - None, # No process_id (completed) - ) - - except asyncio.TimeoutError: - # Background the process - don't kill it - partial_stdout = b"".join(stdout_chunks).decode( - "utf-8", errors="replace" - ) - partial_stderr = b"".join(stderr_chunks).decode( - "utf-8", errors="replace" - ) - - await write_file( - log_file, - f"[{shell_name}] Command backgrounded after {effective_timeout}s timeout\n" - f"[{shell_name}] Command: {command}\n" - f"[{shell_name}] PID: {proc.pid}\n" + "-" * 40 + "\n" - f"{partial_stdout}{partial_stderr}", - ) - - self._process_manager.add_process(process_id, proc, str(log_file)) - asyncio.create_task( - self._capture_background_output(proc, log_file, process_id) - ) - - return ( - f"[backgrounded] Process {process_id} (PID {proc.pid}) running in background.\n" - f"Use: ps --logs {process_id} # view output\n" - f"Use: ps --kill {process_id} # stop process", - "", - 0, - True, # Was backgrounded - process_id, - ) - - except Exception as e: - # Clean up on error - kill process if it exists - try: - if proc and proc.returncode is None: - proc.kill() - await proc.wait() - except Exception: - pass - return ( - "", - str(e), - 1, - False, - None, - ) - - async def _capture_background_output( - self, - proc: asyncio.subprocess.Process, - log_file: Path, - process_id: str, - ) -> None: - """Capture output from backgrounded process to log file.""" - try: - - async def read_stream(stream, prefix: str) -> None: - if stream: - while True: - line = await stream.readline() - if not line: - break - await append_file( - log_file, - f"{prefix}{line.decode('utf-8', errors='replace')}", - ) - - await asyncio.gather( - read_stream(proc.stdout, ""), - read_stream(proc.stderr, "[stderr] "), - ) - - await proc.wait() - await append_file( - log_file, f"\n[shell] Process exited with code {proc.returncode}\n" - ) - - self._process_manager.mark_completed(process_id, proc.returncode or 0) - except Exception: - self._process_manager.mark_completed(process_id, -1) - - -def get_shell_executor() -> ShellExecutor: - """Get the shared shell executor singleton.""" - global _shell_executor - if _shell_executor is None: - _shell_executor = ShellExecutor() - return _shell_executor diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/cmd_tool.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/cmd_tool.py deleted file mode 100644 index 43c7046b9..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/cmd_tool.py +++ /dev/null @@ -1,561 +0,0 @@ -"""Cmd tool - unified execution graph (DAG) for command execution. - -The primary command execution tool for hanzo-mcp. Run commands with: -- Serial execution (default) -- Parallel execution -- Mixed execution graphs -- Tool invocations -- Auto-backgrounding at 45s -""" - -import os -import sys -import shutil -import asyncio -from enum import Enum -from typing import Any, Dict, List, Union, Optional, Annotated, override -from datetime import datetime -from dataclasses import field, dataclass - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context -from hanzo_tools.shell.base_process import get_shell_executor - - -class NodeStatus(Enum): - """Execution status of a command node.""" - - PENDING = "pending" - RUNNING = "running" - SUCCESS = "success" - FAILED = "failed" - SKIPPED = "skipped" - - -@dataclass -class CmdResult: - """Result from a single command execution.""" - - node_id: str - command: str - stdout: str - stderr: str - status: NodeStatus - exit_code: int - duration_ms: int = 0 - node_type: str = "shell" - - -@dataclass -class CmdNode: - """A node in the execution graph.""" - - id: str - command: Union[str, Dict[str, Any]] - depends_on: List[str] = field(default_factory=list) - status: NodeStatus = NodeStatus.PENDING - result: Optional[CmdResult] = None - - -Command = Union[str, Dict[str, Any], List[Any]] - - -class CmdTool(BaseTool): - """Unified command execution with DAG support. - - The primary tool for running shell commands. Supports: - - Simple commands: cmd("ls -la") - - Serial execution: cmd(["ls", "pwd", "git status"]) - - Parallel execution: cmd(["npm i", "cargo build"], parallel=True) - - Mixed DAG: cmd(["mkdir dist", {"parallel": ["cp a", "cp b"]}, "zip out"]) - - Tool invocations: cmd([{"tool": "search", "input": {"pattern": "TODO"}}]) - - Named deps: cmd([{"id": "a", "run": "...", "after": ["b"]}]) - - Auto-backgrounds commands after 45s. Uses zsh by default. - """ - - name = "cmd" - - def __init__( - self, - tools: Optional[Dict[str, BaseTool]] = None, - default_shell: str = "zsh", - ): - """Initialize command execution tool.""" - super().__init__() - self.tools = tools or {} - self.default_shell = self._resolve_shell(default_shell) - - def _resolve_shell(self, preferred: str) -> str: - """Resolve shell - prefer zsh, fallback to bash/fish/sh. - - Supports: zsh, bash, fish, sh, dash, ksh, tcsh, csh - On Windows: pwsh > powershell > cmd.exe - """ - # Allow override via environment - force_shell = os.environ.get("HANZO_MCP_FORCE_SHELL") - if force_shell: - return force_shell - - # On Windows, prefer PowerShell/cmd - if sys.platform == "win32": - for shell in ("pwsh", "powershell", "cmd"): - found = shutil.which(shell) - if found: - return found - return "cmd.exe" - - # Shell priority: modern shells first - shell_priority = ["zsh", "bash", "fish", "dash", "sh"] - search_paths = [ - "/opt/homebrew/bin", - "/usr/local/bin", - "/bin", - "/usr/bin", - "/usr/local/fish/bin", # Common fish location - ] - - for shell in shell_priority: - for prefix in search_paths: - full_path = f"{prefix}/{shell}" - if os.path.isfile(full_path) and os.access(full_path, os.X_OK): - return full_path - found = shutil.which(shell) - if found: - return found - - return "sh" - - @property - @override - def description(self) -> str: - shell_name = os.path.basename(self.default_shell) - return f"""Execute commands with DAG support (shell: {shell_name}). - -SIMPLE: - cmd("ls -la") # Single command - cmd("A ; B ; C") # Sequential via shell - -ARRAYS: - cmd(["a", "b", "c"]) # Sequential - cmd(["a", "b"], parallel=True) # All parallel - cmd(["a", ["b", "c"], "d"]) # Nested = parallel - -DAG: - cmd([ - "mkdir dist", - {{"parallel": ["cp a dist/", "cp b dist/"]}}, - "zip -r out.zip dist/" - ]) - -TOOL INVOCATION: - cmd([{{"tool": "search", "input": {{"pattern": "TODO"}}}}]) - -OPTIONS: - parallel: Run ALL top-level commands concurrently - shell: Use different shell (zsh, bash, fish, dash, sh) - strict: Stop on first error - quiet: Suppress stdout - timeout: Per-command timeout (default: 45s) - cwd: Working directory - env: Environment variables - -AUTO-BACKGROUNDING: Commands exceeding 45s auto-background. -Use ps --logs to view, ps --kill to stop.""" - - async def _run_shell( - self, - cmd: str, - cwd: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - timeout: int = 30, - shell: Optional[str] = None, - ) -> CmdResult: - """Run a shell command with auto-backgrounding on timeout. - - Uses the shared ShellExecutor for consistent 45s auto-backgrounding. - """ - start_time = datetime.now() - node_id = f"shell_{id(cmd)}" - executor = get_shell_executor() - use_shell = shell or self.default_shell - - stdout, stderr, exit_code, was_backgrounded, process_id = ( - await executor.run_shell( - command=cmd, - shell=use_shell, - cwd=cwd, - env=env, - timeout=float(timeout), - tool_name="cmd", - ) - ) - - duration = int((datetime.now() - start_time).total_seconds() * 1000) - - if was_backgrounded: - return CmdResult( - node_id=node_id, - command=cmd, - stdout=stdout, - stderr=stderr, - status=NodeStatus.SUCCESS, # Backgrounded = success from caller's POV - exit_code=0, - duration_ms=duration, - node_type="shell", - ) - - return CmdResult( - node_id=node_id, - command=cmd, - stdout=stdout, - stderr=stderr, - status=NodeStatus.SUCCESS if exit_code == 0 else NodeStatus.FAILED, - exit_code=exit_code, - duration_ms=duration, - node_type="shell", - ) - - async def _run_tool( - self, tool_name: str, tool_input: Dict[str, Any], ctx: MCPContext - ) -> CmdResult: - """Run an MCP tool invocation.""" - start_time = datetime.now() - node_id = f"tool_{tool_name}" - - if tool_name not in self.tools: - return CmdResult( - node_id=node_id, - command=f"tool:{tool_name}", - stdout="", - stderr=f"Tool '{tool_name}' not found. Available: {list(self.tools.keys())}", - status=NodeStatus.FAILED, - exit_code=1, - duration_ms=0, - node_type="tool", - ) - - try: - tool = self.tools[tool_name] - result = await tool.call(ctx, **tool_input) - duration = int((datetime.now() - start_time).total_seconds() * 1000) - - return CmdResult( - node_id=node_id, - command=f"tool:{tool_name}", - stdout=str(result) if result else "", - stderr="", - status=NodeStatus.SUCCESS, - exit_code=0, - duration_ms=duration, - node_type="tool", - ) - except Exception as e: - duration = int((datetime.now() - start_time).total_seconds() * 1000) - return CmdResult( - node_id=node_id, - command=f"tool:{tool_name}", - stdout="", - stderr=str(e), - status=NodeStatus.FAILED, - exit_code=1, - duration_ms=duration, - node_type="tool", - ) - - async def _execute_node( - self, - cmd: Command, - ctx: MCPContext, - cwd: Optional[str], - env: Optional[Dict[str, str]], - timeout: int, - shell: Optional[str] = None, - ) -> CmdResult: - """Execute a single command node. - - Supports: - - str: Single command - - list: Nested array = parallel execution - - dict with "parallel": Explicit parallel block - - dict with "tool": Tool invocation - - dict with "run": Command wrapper - """ - - if isinstance(cmd, str): - return await self._run_shell(cmd, cwd, env, timeout, shell) - - # Nested array = parallel execution - if isinstance(cmd, list): - tasks = [self._execute_node(c, ctx, cwd, env, timeout, shell) for c in cmd] - results = await asyncio.gather(*tasks, return_exceptions=True) - - combined_stdout = [] - combined_stderr = [] - all_success = True - total_duration = 0 - - for i, r in enumerate(results): - if isinstance(r, Exception): - combined_stderr.append(f"[{i}] Error: {r}") - all_success = False - else: - if r.stdout: - combined_stdout.append(f"[{i}] {r.stdout.rstrip()}") - if r.stderr: - combined_stderr.append(f"[{i}] {r.stderr.rstrip()}") - if r.status != NodeStatus.SUCCESS: - all_success = False - total_duration = max(total_duration, r.duration_ms) - - return CmdResult( - node_id=f"parallel_{len(cmd)}", - command=f"parallel[{len(cmd)} tasks]", - stdout="\n".join(combined_stdout), - stderr="\n".join(combined_stderr), - status=NodeStatus.SUCCESS if all_success else NodeStatus.FAILED, - exit_code=0 if all_success else 1, - duration_ms=total_duration, - node_type="parallel", - ) - - if isinstance(cmd, dict): - if "tool" in cmd: - return await self._run_tool(cmd["tool"], cmd.get("input", {}), ctx) - - if "parallel" in cmd: - parallel_cmds = cmd["parallel"] - tasks = [ - self._execute_node(c, ctx, cwd, env, timeout, shell) - for c in parallel_cmds - ] - results = await asyncio.gather(*tasks, return_exceptions=True) - - combined_stdout = [] - combined_stderr = [] - all_success = True - total_duration = 0 - - for i, r in enumerate(results): - if isinstance(r, Exception): - combined_stderr.append(f"[{i}] Error: {r}") - all_success = False - else: - if r.stdout: - combined_stdout.append(f"[{i}] {r.stdout.rstrip()}") - if r.stderr: - combined_stderr.append(f"[{i}] {r.stderr.rstrip()}") - if r.status != NodeStatus.SUCCESS: - all_success = False - total_duration = max(total_duration, r.duration_ms) - - return CmdResult( - node_id=f"parallel_{len(parallel_cmds)}", - command=f"parallel[{len(parallel_cmds)} tasks]", - stdout="\n".join(combined_stdout), - stderr="\n".join(combined_stderr), - status=NodeStatus.SUCCESS if all_success else NodeStatus.FAILED, - exit_code=0 if all_success else 1, - duration_ms=total_duration, - node_type="parallel", - ) - - if "run" in cmd: - run_cmd = cmd["run"] - return await self._execute_node(run_cmd, ctx, cwd, env, timeout, shell) - - return CmdResult( - node_id="unknown", - command=str(cmd), - stdout="", - stderr=f"Unknown command format: {cmd}", - status=NodeStatus.FAILED, - exit_code=1, - duration_ms=0, - node_type="unknown", - ) - - return CmdResult( - node_id="unknown", - command=str(cmd), - stdout="", - stderr=f"Unknown command type: {type(cmd).__name__}", - status=NodeStatus.FAILED, - exit_code=1, - duration_ms=0, - node_type="unknown", - ) - - def _format_output(self, results: List[CmdResult], quiet: bool = False) -> str: - """Format command execution results.""" - output_parts = [] - total_duration = 0 - failed_count = 0 - - for r in results: - total_duration += r.duration_ms - - if r.status == NodeStatus.FAILED: - failed_count += 1 - - if r.stdout and not quiet: - output_parts.append(r.stdout.rstrip()) - - if r.stderr: - output_parts.append(f"[stderr] {r.stderr.rstrip()}") - - if len(results) > 1: - status = "โœ“" if failed_count == 0 else f"โœ— ({failed_count} failed)" - output_parts.append( - f"\n[cmd] {len(results)} nodes, {total_duration}ms, {status}" - ) - - return "\n".join(output_parts) if output_parts else "(no output)" - - @override - @auto_timeout("cmd") - async def call( - self, - ctx: MCPContext, - command: Optional[str] = None, - commands: Optional[List[Command]] = None, - parallel: bool = False, - shell: Optional[str] = None, - cwd: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - timeout: int = 30, - strict: bool = False, - quiet: bool = False, - **kwargs, - ) -> str: - """Execute commands with optional DAG semantics. - - Args: - command: Single command string (simple mode) - commands: List of commands for DAG mode - parallel: Run all commands concurrently - shell: Shell to use (default: zsh) - cwd: Working directory - env: Additional environment variables - timeout: Per-command timeout in seconds (default: 45) - strict: Stop on first error - quiet: Suppress stdout - """ - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Single command mode - if command and not commands: - result = await self._run_shell(command, cwd, env, timeout, shell) - if result.status == NodeStatus.SUCCESS: - return result.stdout if result.stdout else "(no output)" - return ( - f"{result.stdout}\n[stderr] {result.stderr}" - if result.stderr - else result.stdout - ) - - # DAG mode - if not commands: - return "Error: Provide either 'command' (string) or 'commands' (list)" - - results: List[CmdResult] = [] - - if parallel: - tasks = [ - self._execute_node(cmd, ctx, cwd, env, timeout, shell) - for cmd in commands - ] - results = await asyncio.gather(*tasks, return_exceptions=True) - results = [ - ( - r - if not isinstance(r, Exception) - else CmdResult( - node_id=f"error_{i}", - command=str(commands[i]), - stdout="", - stderr=str(r), - status=NodeStatus.FAILED, - exit_code=1, - duration_ms=0, - node_type="error", - ) - ) - for i, r in enumerate(results) - ] - else: - for cmd in commands: - result = await self._execute_node(cmd, ctx, cwd, env, timeout, shell) - results.append(result) - - if strict and result.status == NodeStatus.FAILED: - break - - return self._format_output(results, quiet) - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register cmd tool with MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def cmd_handler( - command: Annotated[ - Optional[str], - Field(description="Single command to execute", default=None), - ] = None, - commands: Annotated[ - Optional[List[Any]], - Field(description="List of commands for DAG execution", default=None), - ] = None, - parallel: Annotated[ - bool, Field(description="Run all commands in parallel", default=False) - ] = False, - shell: Annotated[ - Optional[str], Field(description="Shell: zsh, bash, sh", default=None) - ] = None, - cwd: Annotated[ - Optional[str], Field(description="Working directory", default=None) - ] = None, - env: Annotated[ - Optional[Dict[str, str]], - Field(description="Environment variables", default=None), - ] = None, - timeout: Annotated[ - int, Field(description="Timeout per command (seconds)", default=30) - ] = 30, - strict: Annotated[ - bool, Field(description="Stop on first error", default=False) - ] = False, - quiet: Annotated[ - bool, Field(description="Suppress stdout", default=False) - ] = False, - ctx: MCPContext = None, - ) -> str: - return await tool_self.call( - ctx, - command=command, - commands=commands, - parallel=parallel, - shell=shell, - cwd=cwd, - env=env, - timeout=timeout, - strict=strict, - quiet=quiet, - ) - - -def create_cmd_tool( - tools: Optional[Dict[str, BaseTool]] = None, default_shell: str = "zsh" -) -> CmdTool: - """Factory to create command execution tool.""" - return CmdTool(tools, default_shell) - - -# Singleton instance -cmd_tool = CmdTool() diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/curl_tool.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/curl_tool.py deleted file mode 100644 index fba92f8a5..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/curl_tool.py +++ /dev/null @@ -1,196 +0,0 @@ -"""Curl tool - HTTP client without shell escaping issues. - -Provides a clean interface for HTTP requests, avoiding shell quoting problems. -""" - -import json -import asyncio -from typing import Literal, Optional, Annotated, final, override - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context - -Method = Annotated[ - Literal["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"], - Field(description="HTTP method"), -] - - -@final -class CurlTool(BaseTool): - """HTTP client tool - curl without shell escaping nightmares. - - Clean interface for HTTP requests with proper JSON handling. - """ - - name = "curl" - - @property - @override - def description(self) -> str: - return """HTTP client - make requests without shell escaping issues. - -Examples: - curl --url "https://api.example.com/health" - curl --url "https://api.example.com/data" --method POST --json '{"key": "value"}' - curl --url "https://api.example.com/auth" --headers '{"Authorization": "Bearer token"}' - -Parameters: - url: Request URL (required) - method: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS (default: GET) - json: JSON body (will be serialized properly) - data: Raw body data - headers: Additional headers as JSON object - timeout: Request timeout in seconds (default: 30) - follow_redirects: Follow redirects (default: true) - verbose: Include response headers (default: false) - -Returns formatted response with status, headers (if verbose), and body. -""" - - @override - @auto_timeout("curl") - async def call( - self, - ctx: MCPContext, - url: str, - method: str = "GET", - json_body: Optional[str] = None, - data: Optional[str] = None, - headers: Optional[str] = None, - timeout: int = 30, - follow_redirects: bool = True, - verbose: bool = False, - **kwargs, - ) -> str: - """Make HTTP request.""" - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Build curl command - cmd = ["curl", "-s", "-S"] # Silent but show errors - - # Method - if method != "GET": - cmd.extend(["-X", method]) - - # Follow redirects - if follow_redirects: - cmd.append("-L") - - # Timeout - cmd.extend(["--max-time", str(timeout)]) - - # Include headers in output if verbose - if verbose: - cmd.append("-i") - - # Headers - default_headers = {} - if headers: - try: - parsed_headers = json.loads(headers) - if isinstance(parsed_headers, dict): - default_headers.update(parsed_headers) - except json.JSONDecodeError: - return f"Error: Invalid headers JSON: {headers}" - - # JSON body - if json_body: - default_headers.setdefault("Content-Type", "application/json") - # Parse and re-serialize to ensure valid JSON - try: - parsed_json = json.loads(json_body) - serialized = json.dumps(parsed_json) - cmd.extend(["-d", serialized]) - except json.JSONDecodeError: - return f"Error: Invalid JSON body: {json_body}" - elif data: - cmd.extend(["-d", data]) - - # Add all headers - for key, value in default_headers.items(): - cmd.extend(["-H", f"{key}: {value}"]) - - # URL (must be last) - cmd.append(url) - - try: - proc = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - - stdout, stderr = await asyncio.wait_for( - proc.communicate(), - timeout=timeout + 5, # Give curl time to timeout first - ) - - output = stdout.decode("utf-8", errors="replace") - errors = stderr.decode("utf-8", errors="replace") - - if proc.returncode != 0: - return f"curl failed (exit {proc.returncode}):\n{errors}\n{output}" - - # Try to pretty-print JSON responses - if not verbose: # Don't try to parse if headers included - try: - parsed = json.loads(output) - return json.dumps(parsed, indent=2) - except json.JSONDecodeError: - pass - - return output - - except asyncio.TimeoutError: - return f"Request timed out after {timeout}s" - except FileNotFoundError: - return "Error: curl not found. Install curl to use this tool." - except Exception as e: - return f"Error: {e}" - - def register(self, mcp_server: FastMCP) -> None: - """Register with MCP server.""" - tool_instance = self - - @mcp_server.tool() - async def curl( - url: Annotated[str, Field(description="Request URL")], - method: Method = "GET", - json: Annotated[ - Optional[str], - Field(description="JSON body (will be properly escaped)"), - ] = None, - data: Annotated[Optional[str], Field(description="Raw body data")] = None, - headers: Annotated[ - Optional[str], Field(description="Headers as JSON object") - ] = None, - timeout: Annotated[int, Field(description="Timeout in seconds")] = 30, - follow_redirects: Annotated[ - bool, Field(description="Follow redirects") - ] = True, - verbose: Annotated[ - bool, Field(description="Include response headers") - ] = False, - ctx: MCPContext = None, - ) -> str: - """HTTP client - curl without shell escaping issues. - - Make HTTP requests with proper JSON handling. - No more shell quoting nightmares! - """ - return await tool_instance.call( - ctx, - url=url, - method=method, - json_body=json, - data=data, - headers=headers, - timeout=timeout, - follow_redirects=follow_redirects, - verbose=verbose, - ) diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/dag_tool.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/dag_tool.py deleted file mode 100644 index ca59a81cd..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/dag_tool.py +++ /dev/null @@ -1,530 +0,0 @@ -"""DAG execution tool - directed acyclic graph for command execution. - -Run commands/tools with proper dependency ordering using DAG semantics. -Supports serial (default), parallel, and complex mixed execution graphs. -""" - -import os -import sys -import uuid -import shutil -import asyncio -from enum import Enum -from typing import Any, Dict, List, Union, Optional, Annotated, override -from pathlib import Path -from datetime import datetime -from dataclasses import field, dataclass - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context -from hanzo_tools.shell.base_process import get_shell_executor - - -class NodeStatus(Enum): - """Execution status of a DAG node.""" - - PENDING = "pending" - RUNNING = "running" - SUCCESS = "success" - FAILED = "failed" - SKIPPED = "skipped" - - -@dataclass -class DagResult: - """Result from a single DAG node execution.""" - - node_id: str - command: str - stdout: str - stderr: str - status: NodeStatus - exit_code: int - duration_ms: int = 0 - node_type: str = "shell" - - -@dataclass -class DagNode: - """A node in the execution DAG.""" - - id: str - command: Union[str, Dict[str, Any]] - depends_on: List[str] = field(default_factory=list) - status: NodeStatus = NodeStatus.PENDING - result: Optional[DagResult] = None - - -Command = Union[str, Dict[str, Any], List[Any]] - - -class DagTool(BaseTool): - """DAG-based execution with proper dependency ordering. - - Execute commands (shell or tools) with directed acyclic graph semantics. - Supports serial, parallel, and complex mixed execution patterns. - - USAGE PATTERNS: - - 1. Serial (default) - sequential execution: - dag(["ls", "pwd", "git status"]) - - 2. Parallel - concurrent execution: - dag(["npm install", "cargo build"], parallel=True) - - 3. DAG - mixed serial and parallel blocks: - dag([ - "mkdir -p dist", - {"parallel": [ - "cp manifest.json dist/", - "cp -rf assets/ dist/", - ]}, - "zip -r package.zip dist/" - ]) - - 4. Tool invocations - not just shell: - dag([ - {"tool": "read", "input": {"file_path": "config.json"}}, - {"tool": "search", "input": {"pattern": "TODO"}}, - ]) - - 5. Named nodes with explicit dependencies: - dag([ - {"id": "setup", "run": "mkdir -p dist"}, - {"id": "copy", "run": "cp *.txt dist/", "after": ["setup"]}, - {"id": "test", "run": "pytest", "after": ["setup"]}, - {"id": "package", "run": "tar -czf out.tar.gz dist/", "after": ["copy", "test"]}, - ]) - - Uses zsh for shell execution. - """ - - name = "dag" - - def __init__( - self, tools: Optional[Dict[str, BaseTool]] = None, default_shell: str = "zsh" - ): - """Initialize DAG execution tool.""" - super().__init__() - self.tools = tools or {} - self.default_shell = self._resolve_shell(default_shell) - - def _resolve_shell(self, preferred: str) -> str: - """Resolve shell - prefer zsh, fallback to bash. On Windows, use pwsh/cmd.""" - if sys.platform == "win32": - for shell in ("pwsh", "powershell", "cmd"): - found = shutil.which(shell) - if found: - return found - return "cmd.exe" - - shell_priority = ["zsh", "bash"] - search_paths = [ - "/opt/homebrew/bin", - "/usr/local/bin", - "/bin", - "/usr/bin", - ] - - for shell in shell_priority: - for prefix in search_paths: - full_path = f"{prefix}/{shell}" - if os.path.isfile(full_path) and os.access(full_path, os.X_OK): - return full_path - found = shutil.which(shell) - if found: - return found - - return "sh" - - @property - @override - def description(self) -> str: - return """DAG execution - run commands with dependency ordering. - -Execute shell commands or tools with DAG (directed acyclic graph) semantics. -Supports serial, parallel, and complex mixed execution patterns. - -MODES: - -Serial (default): dag(["ls", "pwd"]) - Commands run in sequence. - -Parallel: dag(["npm install", "cargo build"], parallel=True) - Commands run concurrently. - -Mixed DAG: - dag([ - "mkdir -p dist", - {"parallel": ["cp a.txt dist/", "cp b.txt dist/"]}, - "zip -r out.zip dist/" - ]) - -Tool invocations: - dag([{"tool": "search", "input": {"pattern": "TODO"}}]) - -Named with dependencies: - dag([ - {"id": "build", "run": "make build"}, - {"id": "test", "run": "make test", "after": ["build"]}, - ]) - -Uses zsh for shell execution. - -AUTO-BACKGROUNDING: Commands that exceed timeout are automatically -backgrounded. Use ps tool to monitor: ps --logs , ps --kill """ - - async def _run_shell( - self, - cmd: str, - shell: str, - cwd: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - timeout: int = 30, - ) -> DagResult: - """Run a shell command with auto-backgrounding on timeout. - - Uses the shared ShellExecutor for consistent 45s auto-backgrounding. - """ - start_time = datetime.now() - node_id = f"shell_{id(cmd)}" - executor = get_shell_executor() - - stdout, stderr, exit_code, was_backgrounded, process_id = ( - await executor.run_shell( - command=cmd, - shell=shell, - cwd=cwd, - env=env, - timeout=float(timeout), - tool_name="dag", - ) - ) - - duration = int((datetime.now() - start_time).total_seconds() * 1000) - - if was_backgrounded: - return DagResult( - node_id=node_id, - command=cmd, - stdout=stdout, - stderr=stderr, - status=NodeStatus.SUCCESS, # Backgrounded = success from caller's POV - exit_code=0, - duration_ms=duration, - node_type="shell", - ) - - return DagResult( - node_id=node_id, - command=cmd, - stdout=stdout, - stderr=stderr, - status=NodeStatus.SUCCESS if exit_code == 0 else NodeStatus.FAILED, - exit_code=exit_code, - duration_ms=duration, - node_type="shell", - ) - - async def _run_tool( - self, tool_name: str, tool_input: Dict[str, Any], ctx: MCPContext - ) -> DagResult: - """Run an MCP tool invocation.""" - start_time = datetime.now() - node_id = f"tool_{tool_name}" - - if tool_name not in self.tools: - return DagResult( - node_id=node_id, - command=f"tool:{tool_name}", - stdout="", - stderr=f"Tool '{tool_name}' not found. Available: {list(self.tools.keys())}", - status=NodeStatus.FAILED, - exit_code=1, - duration_ms=0, - node_type="tool", - ) - - try: - tool = self.tools[tool_name] - result = await tool.call(ctx, **tool_input) - duration = int((datetime.now() - start_time).total_seconds() * 1000) - - return DagResult( - node_id=node_id, - command=f"tool:{tool_name}", - stdout=str(result) if result else "", - stderr="", - status=NodeStatus.SUCCESS, - exit_code=0, - duration_ms=duration, - node_type="tool", - ) - except Exception as e: - duration = int((datetime.now() - start_time).total_seconds() * 1000) - return DagResult( - node_id=node_id, - command=f"tool:{tool_name}", - stdout="", - stderr=str(e), - status=NodeStatus.FAILED, - exit_code=1, - duration_ms=duration, - node_type="tool", - ) - - async def _execute_node( - self, - cmd: Command, - ctx: MCPContext, - shell: str, - cwd: Optional[str], - env: Optional[Dict[str, str]], - timeout: int, - ) -> DagResult: - """Execute a single DAG node.""" - - if isinstance(cmd, str): - return await self._run_shell(cmd, shell, cwd, env, timeout) - - # Nested array = auto-parallel (e.g., ["a", ["b", "c"], "d"] runs b,c in parallel) - if isinstance(cmd, list): - tasks = [self._execute_node(c, ctx, shell, cwd, env, timeout) for c in cmd] - results = await asyncio.gather(*tasks, return_exceptions=True) - - combined_stdout = [] - combined_stderr = [] - all_success = True - total_duration = 0 - - for i, r in enumerate(results): - if isinstance(r, Exception): - combined_stderr.append(f"[{i}] Error: {r}") - all_success = False - else: - if r.stdout: - combined_stdout.append(f"[{i}] {r.stdout.rstrip()}") - if r.stderr: - combined_stderr.append(f"[{i}] {r.stderr.rstrip()}") - if r.status != NodeStatus.SUCCESS: - all_success = False - total_duration = max(total_duration, r.duration_ms) - - return DagResult( - node_id=f"auto_parallel_{len(cmd)}", - command=f"parallel[{len(cmd)} tasks]", - stdout="\n".join(combined_stdout), - stderr="\n".join(combined_stderr), - status=NodeStatus.SUCCESS if all_success else NodeStatus.FAILED, - exit_code=0 if all_success else 1, - duration_ms=total_duration, - node_type="parallel", - ) - - if isinstance(cmd, dict): - if "tool" in cmd: - return await self._run_tool(cmd["tool"], cmd.get("input", {}), ctx) - - if "parallel" in cmd: - parallel_cmds = cmd["parallel"] - tasks = [ - self._execute_node(c, ctx, shell, cwd, env, timeout) - for c in parallel_cmds - ] - results = await asyncio.gather(*tasks, return_exceptions=True) - - combined_stdout = [] - combined_stderr = [] - all_success = True - total_duration = 0 - - for i, r in enumerate(results): - if isinstance(r, Exception): - combined_stderr.append(f"[{i}] Error: {r}") - all_success = False - else: - if r.stdout: - combined_stdout.append(f"[{i}] {r.stdout.rstrip()}") - if r.stderr: - combined_stderr.append(f"[{i}] {r.stderr.rstrip()}") - if r.status != NodeStatus.SUCCESS: - all_success = False - total_duration = max(total_duration, r.duration_ms) - - return DagResult( - node_id=f"parallel_{len(parallel_cmds)}", - command=f"parallel[{len(parallel_cmds)} tasks]", - stdout="\n".join(combined_stdout), - stderr="\n".join(combined_stderr), - status=NodeStatus.SUCCESS if all_success else NodeStatus.FAILED, - exit_code=0 if all_success else 1, - duration_ms=total_duration, - node_type="parallel", - ) - - if "run" in cmd: - run_cmd = cmd["run"] - return await self._execute_node(run_cmd, ctx, shell, cwd, env, timeout) - - return DagResult( - node_id="unknown", - command=str(cmd), - stdout="", - stderr=f"Unknown command format: {cmd}", - status=NodeStatus.FAILED, - exit_code=1, - duration_ms=0, - node_type="unknown", - ) - - return DagResult( - node_id="unknown", - command=str(cmd), - stdout="", - stderr=f"Unknown command type: {type(cmd).__name__}", - status=NodeStatus.FAILED, - exit_code=1, - duration_ms=0, - node_type="unknown", - ) - - @override - @auto_timeout("dag") - async def call( - self, - ctx: MCPContext, - commands: List[Command], - parallel: bool = False, - shell: Optional[str] = None, - cwd: Optional[str] = None, - env: Optional[Dict[str, str]] = None, - timeout: int = 30, - strict: bool = False, - quiet: bool = False, - **kwargs, - ) -> str: - """Execute commands with DAG semantics.""" - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - shell = shell or self.default_shell - results: List[DagResult] = [] - - if parallel: - tasks = [ - self._execute_node(cmd, ctx, shell, cwd, env, timeout) - for cmd in commands - ] - results = await asyncio.gather(*tasks, return_exceptions=True) - results = [ - ( - r - if not isinstance(r, Exception) - else DagResult( - node_id=f"error_{i}", - command=str(commands[i]), - stdout="", - stderr=str(r), - status=NodeStatus.FAILED, - exit_code=1, - duration_ms=0, - node_type="error", - ) - ) - for i, r in enumerate(results) - ] - else: - for cmd in commands: - result = await self._execute_node(cmd, ctx, shell, cwd, env, timeout) - results.append(result) - - if strict and result.status == NodeStatus.FAILED: - break - - return self._format_output(results, quiet) - - def _format_output(self, results: List[DagResult], quiet: bool = False) -> str: - """Format DAG execution results.""" - output_parts = [] - total_duration = 0 - failed_count = 0 - - for r in results: - total_duration += r.duration_ms - - if r.status == NodeStatus.FAILED: - failed_count += 1 - - if r.stdout and not quiet: - output_parts.append(r.stdout.rstrip()) - - if r.stderr: - output_parts.append(f"[stderr] {r.stderr.rstrip()}") - - if len(results) > 1: - status = "โœ“" if failed_count == 0 else f"โœ— ({failed_count} failed)" - output_parts.append( - f"\n[dag] {len(results)} nodes, {total_duration}ms, {status}" - ) - - return "\n".join(output_parts) if output_parts else "(no output)" - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register DAG tool with MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def dag_handler( - commands: Annotated[ - List[Any], - Field( - description="Commands to execute (strings, tool dicts, or parallel blocks)" - ), - ], - parallel: Annotated[ - bool, Field(description="Run all commands in parallel", default=False) - ] = False, - shell: Annotated[ - Optional[str], - Field(description="Shell to use (default: zsh)", default=None), - ] = None, - cwd: Annotated[ - Optional[str], Field(description="Working directory", default=None) - ] = None, - env: Annotated[ - Optional[Dict[str, str]], - Field(description="Environment variables", default=None), - ] = None, - timeout: Annotated[ - int, Field(description="Timeout per command (seconds)", default=30) - ] = 30, - strict: Annotated[ - bool, Field(description="Stop on first error", default=False) - ] = False, - quiet: Annotated[ - bool, Field(description="Suppress stdout", default=False) - ] = False, - ctx: MCPContext = None, - ) -> str: - return await tool_self.call( - ctx, - commands=commands, - parallel=parallel, - shell=shell, - cwd=cwd, - env=env, - timeout=timeout, - strict=strict, - quiet=quiet, - ) - - -def create_dag_tool( - tools: Optional[Dict[str, BaseTool]] = None, default_shell: str = "zsh" -) -> DagTool: - """Factory to create DAG execution tool.""" - return DagTool(tools, default_shell) diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/exec_tool.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/exec_tool.py deleted file mode 100644 index ee69aeca7..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/exec_tool.py +++ /dev/null @@ -1,486 +0,0 @@ -"""Unified process tool for HIP-0300 architecture. - -This module provides a single unified 'exec' tool that handles all process operations: -- exec: Execute commands (the ONE execution primitive) -- ps: List processes -- kill: Kill processes -- logs: Get process logs - -Following Unix philosophy: one tool for the Execution axis. -All command execution goes through proc.exec. -""" - -import os -import json -import signal -import asyncio -from typing import Any, Dict, ClassVar, Optional -from pathlib import Path -from datetime import datetime - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - ToolError, - NotFoundError, - InvalidParamsError, -) -from hanzo_tools.shell.base_process import ( - AUTO_BACKGROUND_TIMEOUT, - ProcessManager, - get_shell_executor, -) - - -class ExecTool(BaseTool): - """Unified process execution tool (HIP-0300). - - Handles all process operations on a single axis: - - exec: Execute commands - - ps: List processes - - kill: Kill processes - - logs: Get process logs - - CRITICAL: exec is the ONE execution primitive. - All command execution goes through proc.exec. - """ - - name: ClassVar[str] = "exec" - VERSION: ClassVar[str] = "0.12.0" - - def __init__(self, tools: Optional[Dict[str, Any]] = None): - super().__init__() - self.tools = tools or {} - self.process_manager = ProcessManager() - self._shell = self._resolve_shell() - self._register_proc_actions() - - def _resolve_shell(self) -> str: - """Resolve shell - prefer zsh, fallback to bash/sh. On Windows: pwsh/cmd.""" - import sys - import shutil - - force_shell = os.environ.get("HANZO_MCP_FORCE_SHELL") - if force_shell: - return force_shell - - # On Windows, prefer PowerShell/cmd - if sys.platform == "win32": - for shell in ("pwsh", "powershell", "cmd"): - found = shutil.which(shell) - if found: - return found - return "cmd.exe" - - for shell in ["zsh", "bash", "fish", "dash", "sh"]: - for prefix in ["/opt/homebrew/bin", "/usr/local/bin", "/bin", "/usr/bin"]: - full_path = f"{prefix}/{shell}" - if os.path.isfile(full_path) and os.access(full_path, os.X_OK): - return full_path - found = shutil.which(shell) - if found: - return found - - return "sh" - - @property - def description(self) -> str: - shell_name = os.path.basename(self._shell) - return f"""Unified process execution tool (HIP-0300). - -Actions: -- exec: Execute command (shell: {shell_name}) -- wait: Wait for background process to complete (Rust parity) -- ps: List processes -- kill: Kill process -- logs: Get process logs - -Returns: {{proc_id, exit_code, stdout_ref, stderr_ref}} -Auto-backgrounds commands after {AUTO_BACKGROUND_TIMEOUT}s. -""" - - def _register_proc_actions(self): - """Register all process actions.""" - - @self.action("exec", "Execute command") - async def exec_cmd( - ctx: MCPContext, - command: str | list[str], - cwd: str | None = None, - workdir: str | None = None, # Rust parity alias - env: dict | None = None, - timeout: int | None = None, - shell: str | None = None, - ) -> dict: - """Execute a command. - - This is the ONE execution primitive per HIP-0300. - - Args: - command: Command to execute (string or array of strings for Rust parity) - cwd: Working directory - workdir: Alias for cwd (Rust parity) - env: Environment variables - timeout: Timeout in seconds (default: auto-background at 45s) - shell: Shell to use (default: detected shell) - - Returns: - proc_id, exit_code, stdout_ref, stderr_ref - """ - if not command: - raise InvalidParamsError("Command is required", param="command") - - # Support array format for Rust parity - if isinstance(command, list): - # Join array into shell command - import shlex - - command = " ".join(shlex.quote(arg) for arg in command) - - # Support workdir alias for Rust parity - if workdir and not cwd: - cwd = workdir - - # Resolve shell - shell_path = shell or self._shell - - # Build environment - process_env = os.environ.copy() - if env: - process_env.update(env) - - # Get shell executor - executor = get_shell_executor() - - try: - # Execute with auto-background support via run_shell - stdout_str, stderr_str, exit_code, was_bg, proc_id = ( - await executor.run_shell( - command, - shell=shell_path, - cwd=cwd, - env=process_env, - timeout=float(timeout or AUTO_BACKGROUND_TIMEOUT), - tool_name="exec", - ) - ) - - # Check if backgrounded - if was_bg: - return { - "proc_id": proc_id or "unknown", - "exit_code": None, - "stdout_ref": f"proc:{proc_id}:stdout", - "stderr_ref": f"proc:{proc_id}:stderr", - "status": "running", - "message": f"Command backgrounded after {timeout or AUTO_BACKGROUND_TIMEOUT}s. Use exec(action='logs', proc_id='{proc_id}') to view output.", - } - - # Command completed - return { - "proc_id": proc_id, - "exit_code": exit_code, - "stdout": stdout_str, - "stderr": stderr_str, - "status": "success" if exit_code == 0 else "failed", - } - - except asyncio.TimeoutError: - raise ToolError( - code="TIMEOUT", - message=f"Command timed out after {timeout}s", - ) - except Exception as e: - raise ToolError( - code="INTERNAL_ERROR", - message=f"Execution failed: {e}", - ) - - @self.action("wait", "Wait for background process to complete (Rust parity)") - async def wait( - ctx: MCPContext, - proc_id: str, - timeout_ms: int | None = None, - ) -> dict: - """Wait for a background process to complete. - - This matches the Rust wait tool schema. - - Args: - proc_id: Process ID to wait for (call_id in Rust) - timeout_ms: Maximum wait time in milliseconds (default: 600000, max: 3600000) - - Returns: - Process result when complete or timeout - """ - if not proc_id: - raise InvalidParamsError("proc_id is required", param="proc_id") - - # Default and max timeout - max_timeout_ms = 3600000 # 1 hour max (matches Rust) - default_timeout_ms = 600000 # 10 minutes default - timeout = timeout_ms or default_timeout_ms - timeout = min(timeout, max_timeout_ms) - timeout_sec = timeout / 1000 - - # Get process info - procs = self.process_manager.list_processes() - if proc_id not in procs: - raise NotFoundError(f"Process not found: {proc_id}") - - info = procs[proc_id] - - # If already completed, return immediately - if not info.get("running", False): - log_file = info.get("log_file") - output = "" - if log_file and Path(log_file).exists(): - try: - from hanzo_async import read_file - - output = await read_file( - log_file, encoding="utf-8", errors="replace" - ) - except Exception: - pass - - return { - "proc_id": proc_id, - "exit_code": info.get("return_code", 0), - "output": output, - "status": "completed", - } - - # Poll until complete or timeout - start_time = datetime.now() - poll_interval = 0.5 # 500ms - - while True: - elapsed = (datetime.now() - start_time).total_seconds() - if elapsed >= timeout_sec: - return { - "proc_id": proc_id, - "exit_code": None, - "output": "", - "status": "timeout", - "message": f"Timed out after {timeout_ms}ms", - } - - # Check if process completed - procs = self.process_manager.list_processes() - if proc_id not in procs: - raise NotFoundError(f"Process disappeared: {proc_id}") - - info = procs[proc_id] - if not info.get("running", False): - # Process completed - log_file = info.get("log_file") - output = "" - if log_file and Path(log_file).exists(): - try: - from hanzo_async import read_file - - output = await read_file( - log_file, encoding="utf-8", errors="replace" - ) - except Exception: - pass - - return { - "proc_id": proc_id, - "exit_code": info.get("return_code", 0), - "output": output, - "status": "completed", - "duration_ms": int(elapsed * 1000), - } - - await asyncio.sleep(poll_interval) - - @self.action("ps", "List processes") - async def ps( - ctx: MCPContext, - proc_id: str | None = None, - filter: str | None = None, - ) -> dict: - """List tracked processes. - - Args: - proc_id: Get specific process - filter: Filter by command pattern - - Returns: - List of processes with status - """ - processes = [] - - for pid, info in self.process_manager.list_processes().items(): - # Filter by proc_id - if proc_id and pid != proc_id: - continue - - # Filter by command pattern - cmd = info.get("cmd", "") - if filter and filter.lower() not in cmd.lower(): - continue - - processes.append( - { - "proc_id": pid, - "pid": info.get("pid"), - "command": cmd, - "running": info.get("running", False), - "exit_code": info.get("return_code"), - "started": info.get("started"), - "log_file": info.get("log_file"), - } - ) - - if proc_id and not processes: - raise NotFoundError(f"Process not found: {proc_id}") - - return { - "processes": processes, - "total": len(processes), - } - - @self.action("kill", "Kill process") - async def kill( - ctx: MCPContext, - proc_id: str, - signal: str | int = "TERM", - ) -> dict: - """Kill a process. - - Args: - proc_id: Process ID to kill - signal: Signal to send (default: TERM) - - Returns: - Success status - """ - if not proc_id: - raise InvalidParamsError("proc_id is required", param="proc_id") - - # Find process - procs = self.process_manager.list_processes() - if proc_id not in procs: - raise NotFoundError(f"Process not found: {proc_id}") - - info = procs[proc_id] - pid = info.get("pid") - - if not pid: - raise ToolError( - code="INTERNAL_ERROR", - message="Process has no PID", - ) - - # Resolve signal - import sys as _sys - - if isinstance(signal, str): - signal_map = { - "TERM": 15, - "KILL": 9, - "INT": 2, - "HUP": 1, - "QUIT": 3, - } - sig_num = signal_map.get(signal.upper(), 15) - else: - sig_num = signal - - try: - if _sys.platform == "win32": - # On Windows, os.kill() only supports SIGTERM (terminate) - # and signal 0 (existence check). Use terminate semantics. - import subprocess - subprocess.call( - ["taskkill", "/F", "/PID", str(pid)], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) - else: - os.kill(pid, sig_num) - return { - "proc_id": proc_id, - "pid": pid, - "signal": sig_num, - "killed": True, - } - except ProcessLookupError: - return { - "proc_id": proc_id, - "pid": pid, - "signal": sig_num, - "killed": False, - "message": "Process already terminated", - } - except PermissionError: - raise ToolError( - code="PERMISSION_DENIED", - message=f"Cannot kill process {pid}: permission denied", - ) - - @self.action("logs", "Get process logs") - async def logs( - ctx: MCPContext, - proc_id: str, - tail: int = 100, - since: str | None = None, - ) -> dict: - """Get process stdout/stderr. - - Args: - proc_id: Process ID - tail: Number of lines (default: 100) - since: ISO timestamp to filter from - - Returns: - stdout and stderr content - """ - if not proc_id: - raise InvalidParamsError("proc_id is required", param="proc_id") - - procs = self.process_manager.list_processes() - if proc_id not in procs: - raise NotFoundError(f"Process not found: {proc_id}") - - info = procs[proc_id] - log_file = info.get("log_file") - - if not log_file or not Path(log_file).exists(): - return { - "proc_id": proc_id, - "stdout": "", - "stderr": "", - "message": "No log file available", - } - - try: - from hanzo_async import read_file - - content = await read_file(log_file, encoding="utf-8", errors="replace") - - # Apply tail - lines = content.splitlines() - if tail and len(lines) > tail: - lines = lines[-tail:] - - return { - "proc_id": proc_id, - "output": "\n".join(lines), - "running": info.get("running", False), - "exit_code": info.get("return_code"), - "total_lines": len(content.splitlines()), - } - except Exception as e: - raise ToolError( - code="INTERNAL_ERROR", - message=f"Failed to read logs: {e}", - ) - -# Backward compatibility -exec_tool = ExecTool diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/jq_tool.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/jq_tool.py deleted file mode 100644 index 847881b16..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/jq_tool.py +++ /dev/null @@ -1,171 +0,0 @@ -"""JQ tool - JSON processing without shell escaping issues. - -Provides a clean interface for jq queries, avoiding shell quoting problems. -""" - -import json -import asyncio -from typing import Optional, Annotated, final, override - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context - - -@final -class JqTool(BaseTool): - """JQ tool - JSON processing without shell escaping nightmares. - - Clean interface for jq queries with proper handling. - """ - - name = "jq" - - @property - @override - def description(self) -> str: - return """JSON processor - jq without shell escaping issues. - -Examples: - jq --filter ".result.data" --input '{"result": {"data": [1,2,3]}}' - jq --filter ".[] | select(.active)" --file data.json - jq --filter "keys" --input '{"a": 1, "b": 2}' - jq --filter '.checks | to_entries[] | select(.value.error != null)' --file health.json - -Parameters: - filter: jq filter expression (required) - input: JSON input as string - file: Path to JSON file (alternative to input) - raw: Output raw strings without quotes (default: false) - compact: Compact output (default: false) - slurp: Read entire input as single array (default: false) - sort_keys: Sort object keys (default: false) - -The filter is passed directly to jq without shell interpretation, -so you don't need to escape special characters like ! or | -""" - - @override - @auto_timeout("jq") - async def call( - self, - ctx: MCPContext, - filter: str, - input: Optional[str] = None, - file: Optional[str] = None, - raw: bool = False, - compact: bool = False, - slurp: bool = False, - sort_keys: bool = False, - **kwargs, - ) -> str: - """Process JSON with jq.""" - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - if not input and not file: - return "Error: Either 'input' or 'file' is required" - - # Build jq command - cmd = ["jq"] - - # Options - if raw: - cmd.append("-r") - if compact: - cmd.append("-c") - if slurp: - cmd.append("-s") - if sort_keys: - cmd.append("-S") - - # Filter (passed as argument, not through shell) - cmd.append(filter) - - # File input - if file: - cmd.append(file) - - try: - if input: - # Validate input is valid JSON first - try: - json.loads(input) - except json.JSONDecodeError as e: - return f"Error: Invalid JSON input: {e}" - - proc = await asyncio.create_subprocess_exec( - *cmd, - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await asyncio.wait_for( - proc.communicate(input=input.encode("utf-8")), - timeout=30, - ) - else: - proc = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await asyncio.wait_for( - proc.communicate(), - timeout=30, - ) - - output = stdout.decode("utf-8", errors="replace") - errors = stderr.decode("utf-8", errors="replace") - - if proc.returncode != 0: - # Provide helpful error message - if "syntax error" in errors.lower(): - return f"jq syntax error in filter:\n {filter}\n\nError: {errors}" - return f"jq failed (exit {proc.returncode}):\n{errors}" - - return output.rstrip() - - except asyncio.TimeoutError: - return "jq timed out after 30s" - except FileNotFoundError: - return "Error: jq not found. Install jq: brew install jq" - except Exception as e: - return f"Error: {e}" - - def register(self, mcp_server: FastMCP) -> None: - """Register with MCP server.""" - tool_instance = self - - @mcp_server.tool() - async def jq( - filter: Annotated[str, Field(description="jq filter expression")], - input: Annotated[ - Optional[str], Field(description="JSON input string") - ] = None, - file: Annotated[ - Optional[str], Field(description="Path to JSON file") - ] = None, - raw: Annotated[bool, Field(description="Output raw strings")] = False, - compact: Annotated[bool, Field(description="Compact output")] = False, - slurp: Annotated[bool, Field(description="Read as single array")] = False, - sort_keys: Annotated[bool, Field(description="Sort object keys")] = False, - ctx: MCPContext = None, - ) -> str: - """JSON processor - jq without shell escaping issues. - - Process JSON with jq filters. No shell quoting nightmares! - Supports all standard jq operations. - """ - return await tool_instance.call( - ctx, - filter=filter, - input=input, - file=file, - raw=raw, - compact=compact, - slurp=slurp, - sort_keys=sort_keys, - ) diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/npx_tool.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/npx_tool.py deleted file mode 100644 index 99a410181..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/npx_tool.py +++ /dev/null @@ -1,92 +0,0 @@ -"""NPX tool for both sync and background execution.""" - -from typing import Optional, override -from pathlib import Path - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import auto_timeout -from hanzo_tools.shell.base_process import BaseBinaryTool - - -class NpxTool(BaseBinaryTool): - """Tool for running npx commands.""" - - name = "npx" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Run npx packages with automatic backgrounding for long-running processes. - -Commands that run for more than 2 minutes will automatically continue in the background. - -Usage: -npx create-react-app my-app -npx http-server -p 8080 # Auto-backgrounds after 2 minutes -npx prettier --write "**/*.js" -npx json-server db.json # Auto-backgrounds if needed""" - - @override - def get_binary_name(self) -> str: - """Get the binary name.""" - return "npx" - - @override - async def run( - self, - ctx: MCPContext, - package: str, - args: str = "", - cwd: Optional[str] = None, - yes: bool = True, - ) -> str: - """Run an npx command with auto-backgrounding.""" - work_dir = Path(cwd).resolve() if cwd else Path.cwd() - - flags = [] - if yes: - flags.append("-y") - - full_args = args.split() if args else [] - - return await self.execute_sync( - package, - cwd=work_dir, - flags=flags, - args=full_args, - timeout=None, - ) - - def register(self, server: FastMCP) -> None: - """Register the tool with the MCP server.""" - tool_self = self - - @server.tool(name=self.name, description=self.description) - async def npx( - ctx: MCPContext, - package: str, - args: str = "", - cwd: Optional[str] = None, - yes: bool = True, - ) -> str: - return await tool_self.run( - ctx, package=package, args=args, cwd=cwd, yes=yes - ) - - @auto_timeout("npx") - async def call(self, ctx: MCPContext, **params) -> str: - """Call the tool with arguments.""" - return await self.run( - ctx, - package=params["package"], - args=params.get("args", ""), - cwd=params.get("cwd"), - yes=params.get("yes", True), - ) - - -# Create tool instance -npx_tool = NpxTool() diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/open_tool.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/open_tool.py deleted file mode 100644 index 86dc92e0d..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/open_tool.py +++ /dev/null @@ -1,117 +0,0 @@ -"""Open files or URLs in the default application.""" - -import asyncio -import platform -import webbrowser -from typing import override -from pathlib import Path -from urllib.parse import urlparse - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout - - -class OpenTool(BaseTool): - """Tool for opening files or URLs in the default application.""" - - name = "open" - - def register(self, server: FastMCP) -> None: - """Register the tool with the MCP server.""" - tool_self = self - - @server.tool(name=self.name, description=self.description) - async def open(path: str, ctx: MCPContext) -> str: - return await tool_self.run(ctx, path) - - @auto_timeout("open") - async def call(self, ctx: MCPContext, **params) -> str: - """Call the tool with arguments.""" - return await self.run(ctx, params["path"]) - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Open files or URLs. Platform-aware. - -Usage: -open https://example.com -open ./document.pdf -open /path/to/image.png""" - - async def _run_opener(self, cmd: list[str], file_path: str) -> bool: - """Run an opener command asynchronously (fire-and-forget pattern).""" - try: - proc = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.DEVNULL, - stderr=asyncio.subprocess.DEVNULL, - ) - try: - await asyncio.wait_for(proc.wait(), timeout=5) - return proc.returncode == 0 - except asyncio.TimeoutError: - # Process is still running, which is fine for opener commands - return True - except Exception: - return False - - async def run(self, ctx: MCPContext, path: str) -> str: - """Open a file or URL in the default application.""" - parsed = urlparse(path) - is_url = parsed.scheme in ("http", "https", "ftp", "file") - - if is_url: - try: - # Run webbrowser.open in executor to avoid blocking - loop = asyncio.get_running_loop() - await loop.run_in_executor(None, webbrowser.open, path) - return f"Opened URL in browser: {path}" - except Exception as e: - raise RuntimeError(f"Failed to open URL: {e}") - - file_path = Path(path).expanduser().resolve() - - # Check file exists asynchronously - loop = asyncio.get_running_loop() - exists = await loop.run_in_executor(None, file_path.exists) - if not exists: - raise RuntimeError(f"File not found: {file_path}") - - system = platform.system().lower() - - try: - if system == "darwin": # macOS - if await self._run_opener(["open", str(file_path)], str(file_path)): - return f"Opened file: {file_path}" - raise RuntimeError("Failed to open file with 'open' command") - elif system == "linux": - # Try xdg-open first - if await self._run_opener(["xdg-open", str(file_path)], str(file_path)): - return f"Opened file: {file_path}" - # Fallback to other openers - for opener in ["gnome-open", "kde-open", "exo-open"]: - if await self._run_opener([opener, str(file_path)], str(file_path)): - return f"Opened file: {file_path}" - raise RuntimeError("No suitable file opener found on Linux") - elif system == "windows": - import os - - # Run os.startfile in executor to avoid blocking - loop = asyncio.get_running_loop() - await loop.run_in_executor(None, os.startfile, str(file_path)) - return f"Opened file: {file_path}" - else: - raise RuntimeError(f"Unsupported platform: {system}") - - except RuntimeError: - raise - except Exception as e: - raise RuntimeError(f"Error opening file: {e}") - - -# Create tool instance -open_tool = OpenTool() diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/ps_tool.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/ps_tool.py deleted file mode 100644 index dd8932fec..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/ps_tool.py +++ /dev/null @@ -1,284 +0,0 @@ -"""Process management tool (ps). - -List, monitor, and control background processes system-wide. -""" - -import sys -import signal -from typing import Any, Dict, List, Literal, Optional, Annotated, override -from pathlib import Path -from datetime import datetime -from dataclasses import dataclass - -import psutil -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout - - -@dataclass -class ProcessInfo: - """Process information.""" - - pid: int - name: str - username: str - status: str - cpu_percent: float - memory_mb: float - cmdline: str - create_time: datetime - - def to_dict(self) -> Dict[str, Any]: - """Convert to dictionary.""" - return { - "pid": self.pid, - "name": self.name, - "username": self.username, - "status": self.status, - "cpu_percent": self.cpu_percent, - "memory_mb": self.memory_mb, - "cmdline": self.cmdline, - "create_time": self.create_time.isoformat(), - } - - -class PsTool(BaseTool): - """Process management - list, kill, and monitor system processes. - - USAGE: - - ps(action="list") # List all processes (top by CPU) - ps(action="list", sort_by="memory") # List all processes (top by RAM) - ps(action="list", user="root") # List processes for user - ps(action="kill", pid=1234) # Kill process 1234 (SIGTERM) - ps(action="kill", pid=1234, sig=9) # Kill with SIGKILL - ps(action="get", pid=1234) # Get info for specific PID - """ - - name = "ps" - - @property - @override - def description(self) -> str: - return """Process management - list, kill, and monitor system processes. - -USAGE: - ps(action="list") # List top processes by CPU - ps(action="list", sort_by="memory") # List top processes by Memory - ps(action="list", limit=20) # List top 20 processes - ps(action="get", pid=1234) # Get info for process 1234 - ps(action="kill", pid=1234) # Kill process 1234 (SIGTERM) -""" - - def _get_process_info(self, proc: psutil.Process) -> Optional[ProcessInfo]: - """Get process info safely.""" - try: - with proc.oneshot(): - return ProcessInfo( - pid=proc.pid, - name=proc.name(), - username=proc.username(), - status=proc.status(), - cpu_percent=proc.cpu_percent(), - memory_mb=proc.memory_info().rss / 1024 / 1024, - cmdline=" ".join(proc.cmdline()), - create_time=datetime.fromtimestamp(proc.create_time()), - ) - except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): - return None - - def _list_processes( - self, - sort_by: Literal["cpu", "memory", "pid", "name"] = "cpu", - limit: int = 50, - user: Optional[str] = None, - ) -> List[ProcessInfo]: - """List system processes.""" - processes = [] - for proc in psutil.process_iter( - [ - "pid", - "name", - "username", - "status", - "cpu_percent", - "memory_info", - "cmdline", - "create_time", - ] - ): - try: - # Filter by user if requested - if user and proc.info["username"] != user: - continue - - info = ProcessInfo( - pid=proc.info["pid"], - name=proc.info["name"] or "", - username=proc.info["username"] or "", - status=proc.info["status"] or "", - cpu_percent=proc.info["cpu_percent"] or 0.0, - memory_mb=( - (proc.info["memory_info"].rss / 1024 / 1024) - if proc.info["memory_info"] - else 0.0 - ), - cmdline=" ".join(proc.info["cmdline"] or []), - create_time=datetime.fromtimestamp(proc.info["create_time"]), - ) - processes.append(info) - except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): - continue - - # Sort - if sort_by == "cpu": - processes.sort(key=lambda x: x.cpu_percent, reverse=True) - elif sort_by == "memory": - processes.sort(key=lambda x: x.memory_mb, reverse=True) - elif sort_by == "pid": - processes.sort(key=lambda x: x.pid) - elif sort_by == "name": - processes.sort(key=lambda x: x.name.lower()) - - return processes[:limit] - - def _kill_process(self, pid: int, sig: int = signal.SIGTERM) -> str: - """Kill a process.""" - try: - process = psutil.Process(pid) - process.send_signal(sig) - - try: - sig_name = signal.Signals(sig).name - except Exception: - sig_name = str(sig) - - return f"Sent signal {sig_name} to PID {pid} ({process.name()})" - except psutil.NoSuchProcess: - return f"Process with PID {pid} not found" - except psutil.AccessDenied: - return f"Access denied to kill PID {pid}" - except Exception as e: - return f"Error killing PID {pid}: {e}" - - def _format_list(self, processes: List[ProcessInfo]) -> str: - """Format process list for display.""" - if not processes: - return "No processes found" - - # Headers - header = f"{'PID':<8} {'USER':<15} {'%CPU':<6} {'MEM(MB)':<10} {'STATUS':<10} {'COMMAND'}" - lines = [header, "-" * len(header)] - - for p in processes: - cmd = p.cmdline - if len(cmd) > 50: - cmd = cmd[:47] + "..." - - line = f"{p.pid:<8} {p.username[:14]:<15} {p.cpu_percent:<6.1f} {p.memory_mb:<10.1f} {p.status[:9]:<10} {cmd}" - lines.append(line) - - return "\n".join(lines) - - @override - @auto_timeout("ps") - async def call( - self, - ctx: MCPContext, - action: Literal["list", "kill", "get"] = "list", - pid: Optional[int] = None, - sig: int = 15, - sort_by: Literal["cpu", "memory", "pid", "name"] = "cpu", - limit: int = 50, - user: Optional[str] = None, - **kwargs, - ) -> str: - """Process management. - - Args: - ctx: MCP context - action: Action to perform (list, kill, get) - pid: Process ID for kill/get - sig: Signal number for kill (default: 15/SIGTERM) - sort_by: Sort field for list (cpu, memory, pid, name) - limit: Limit number of results for list (default: 50) - user: Filter by username - """ - if action == "kill": - if pid is None: - return "Error: pid is required for kill action" - return self._kill_process(pid, sig) - - elif action == "get": - if pid is None: - return "Error: pid is required for get action" - try: - proc = psutil.Process(pid) - info = self._get_process_info(proc) - if not info: - return f"Process {pid} not found or access denied" - - return ( - f"PID: {info.pid}\n" - f"Name: {info.name}\n" - f"User: {info.username}\n" - f"Status: {info.status}\n" - f"CPU: {info.cpu_percent}%\n" - f"Memory: {info.memory_mb:.1f} MB\n" - f"Created: {info.create_time}\n" - f"Command: {info.cmdline}" - ) - except psutil.NoSuchProcess: - return f"Process {pid} not found" - - else: # list - processes = self._list_processes(sort_by, limit, user) - return self._format_list(processes) - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register ps tool with MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def ps( - action: Annotated[ - Literal["list", "kill", "get"], - Field(description="Action to perform", default="list"), - ] = "list", - pid: Annotated[ - Optional[int], - Field(description="Process ID for kill/get", default=None), - ] = None, - sig: Annotated[ - int, - Field(description="Signal for kill (default: 15/SIGTERM)", default=15), - ] = 15, - sort_by: Annotated[ - Literal["cpu", "memory", "pid", "name"], - Field(description="Sort field for list", default="cpu"), - ] = "cpu", - limit: Annotated[ - int, Field(description="Number of processes to list", default=50) - ] = 50, - user: Annotated[ - Optional[str], Field(description="Filter by username", default=None) - ] = None, - ctx: MCPContext = None, - ) -> str: - return await tool_self.call( - ctx, - action=action, - pid=pid, - sig=sig, - sort_by=sort_by, - limit=limit, - user=user, - ) - - -# Singleton instance -ps_tool = PsTool() diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/session_storage.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/session_storage.py deleted file mode 100644 index 8226f72fa..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/session_storage.py +++ /dev/null @@ -1,286 +0,0 @@ -"""Session storage for managing shell sessions. - -This module provides session management with cleanup capabilities -for shell processes and background tasks. - -Usage: - from hanzo_tools.shell.session_storage import SessionStorage - - # Clean up expired sessions (older than max_age_seconds) - SessionStorage.cleanup_expired_sessions(max_age_seconds=300) - - # Clear all active sessions - cleared = SessionStorage.clear_all_sessions() -""" - -from __future__ import annotations - -import os -import time -import signal -import threading -from typing import Dict, ClassVar, Optional -from pathlib import Path -from dataclasses import field, dataclass - -from hanzo_tools.shell.base_process import ProcessManager - - -@dataclass -class SessionInfo: - """Information about a shell session.""" - - session_id: str - pid: int - command: str - created_at: float = field(default_factory=time.time) - last_activity: float = field(default_factory=time.time) - log_file: Optional[Path] = None - - -class SessionStorage: - """Singleton storage for managing shell sessions. - - Provides centralized tracking and cleanup of shell processes - to prevent resource leaks and orphaned processes. - """ - - _instance: ClassVar[Optional["SessionStorage"]] = None - _lock: ClassVar[threading.Lock] = threading.Lock() - _sessions: ClassVar[Dict[str, SessionInfo]] = {} - _process_manager: ClassVar[Optional[ProcessManager]] = None - - def __new__(cls) -> "SessionStorage": - """Singleton pattern for session storage.""" - if cls._instance is None: - with cls._lock: - if cls._instance is None: - cls._instance = super().__new__(cls) - cls._process_manager = ProcessManager() - return cls._instance - - @classmethod - def add_session( - cls, - session_id: str, - pid: int, - command: str, - log_file: Optional[Path] = None, - ) -> SessionInfo: - """Add a new session to storage. - - Args: - session_id: Unique identifier for the session - pid: Process ID of the session - command: Command that started the session - log_file: Optional path to session log file - - Returns: - SessionInfo for the new session - """ - with cls._lock: - session = SessionInfo( - session_id=session_id, - pid=pid, - command=command, - log_file=log_file, - ) - cls._sessions[session_id] = session - return session - - @classmethod - def remove_session(cls, session_id: str) -> Optional[SessionInfo]: - """Remove a session from storage. - - Args: - session_id: Session identifier to remove - - Returns: - Removed SessionInfo or None if not found - """ - with cls._lock: - return cls._sessions.pop(session_id, None) - - @classmethod - def get_session(cls, session_id: str) -> Optional[SessionInfo]: - """Get a session by ID. - - Args: - session_id: Session identifier - - Returns: - SessionInfo or None if not found - """ - with cls._lock: - return cls._sessions.get(session_id) - - @classmethod - def list_sessions(cls) -> Dict[str, SessionInfo]: - """List all active sessions. - - Returns: - Dictionary of session_id -> SessionInfo - """ - with cls._lock: - return cls._sessions.copy() - - @classmethod - def update_activity(cls, session_id: str) -> bool: - """Update last activity timestamp for a session. - - Args: - session_id: Session to update - - Returns: - True if session exists and was updated - """ - with cls._lock: - if session_id in cls._sessions: - cls._sessions[session_id].last_activity = time.time() - return True - return False - - @classmethod - def cleanup_expired_sessions(cls, max_age_seconds: int = 300) -> int: - """Clean up sessions older than max_age_seconds. - - This method terminates processes for expired sessions and - removes them from storage. - - Args: - max_age_seconds: Maximum session age in seconds (default: 5 minutes) - - Returns: - Number of sessions cleaned up - """ - current_time = time.time() - expired_ids = [] - - with cls._lock: - for session_id, session in cls._sessions.items(): - age = current_time - session.last_activity - if age > max_age_seconds: - expired_ids.append(session_id) - - cleaned = 0 - for session_id in expired_ids: - if cls._terminate_session(session_id): - cleaned += 1 - - return cleaned - - @classmethod - def clear_all_sessions(cls) -> int: - """Clear all active sessions. - - Terminates all tracked processes and clears storage. - - Returns: - Number of sessions cleared - """ - with cls._lock: - session_ids = list(cls._sessions.keys()) - - cleared = 0 - for session_id in session_ids: - if cls._terminate_session(session_id): - cleared += 1 - - # Also clean up any processes tracked by ProcessManager - if cls._process_manager: - for proc_id in list(cls._process_manager.list_processes().keys()): - proc = cls._process_manager.get_process(proc_id) - if proc and proc.returncode is None: - try: - proc.terminate() - cleared += 1 - except Exception: - pass - - return cleared - - @classmethod - def _terminate_session(cls, session_id: str) -> bool: - """Terminate a session's process and remove from storage. - - Args: - session_id: Session to terminate - - Returns: - True if session was terminated successfully - """ - session = cls.remove_session(session_id) - if session is None: - return False - - try: - # Try to terminate the process gracefully - os.kill(session.pid, signal.SIGTERM) - - # Give it a moment to terminate - time.sleep(0.1) - - # Force kill if still running - try: - os.kill(session.pid, 0) # Check if process exists - # SIGKILL doesn't exist on Windows; SIGTERM already calls - # TerminateProcess there, so only force-kill on Unix - if hasattr(signal, "SIGKILL"): - os.kill(session.pid, signal.SIGKILL) - else: - os.kill(session.pid, signal.SIGTERM) - except OSError: - pass # Process already terminated - - return True - - except OSError: - # Process doesn't exist or permission denied - return True - except Exception: - return False - - @classmethod - def get_stats(cls) -> dict: - """Get session storage statistics. - - Returns: - Dictionary with stats about sessions - """ - with cls._lock: - sessions = list(cls._sessions.values()) - - if not sessions: - return { - "total_sessions": 0, - "oldest_session_age": 0, - "newest_session_age": 0, - } - - current_time = time.time() - ages = [current_time - s.last_activity for s in sessions] - - return { - "total_sessions": len(sessions), - "oldest_session_age": max(ages) if ages else 0, - "newest_session_age": min(ages) if ages else 0, - } - - -# Module-level convenience functions -def cleanup_expired_sessions(max_age_seconds: int = 300) -> int: - """Clean up expired sessions.""" - return SessionStorage.cleanup_expired_sessions(max_age_seconds) - - -def clear_all_sessions() -> int: - """Clear all sessions.""" - return SessionStorage.clear_all_sessions() - - -__all__ = [ - "SessionStorage", - "SessionInfo", - "cleanup_expired_sessions", - "clear_all_sessions", -] diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/shell_detect.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/shell_detect.py deleted file mode 100644 index 2b3998abc..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/shell_detect.py +++ /dev/null @@ -1,433 +0,0 @@ -"""Shell detection for hanzo-mcp. - -Detects the user's active shell and login shell to expose only -the relevant shell tool via MCP. On Mac with Homebrew, this will -detect and use /opt/homebrew/bin/zsh if that's what the user has -configured. - -Environment variables for override: -- HANZO_MCP_SHELL: Force a specific shell (e.g., "zsh", "bash") -- HANZO_MCP_FORCE_SHELL: Force a specific shell path (e.g., "/opt/homebrew/bin/zsh") -""" - -from __future__ import annotations - -import os -import shlex -import shutil -import platform -import subprocess -from typing import Dict, List, Tuple, Optional -from dataclasses import dataclass - -# Common shells (names you might see from `ps -o comm=`) and typical full paths. -KNOWN_SHELL_NAMES = { - "zsh", - "bash", - "fish", - "sh", - "ksh", - "tcsh", - "csh", - "dash", - "nu", - "xonsh", - "pwsh", - "powershell", -} -KNOWN_SHELL_PATH_BASENAMES = KNOWN_SHELL_NAMES | {"busybox"} # sometimes sh is busybox - -# Shells we support with dedicated tools -SUPPORTED_SHELLS = {"zsh", "bash", "fish", "dash", "ksh", "tcsh", "csh"} - - -@dataclass(frozen=True) -class ShellInfo: - """Information about detected shells.""" - - # "Login shell" as configured on the user account (what ssh/login uses). - login_shell: Optional[str] - # The shell that appears to have invoked this process (best-effort). - invoking_shell: Optional[str] - # $SHELL (often, but not always, matches login shell) - env_shell: Optional[str] - # Debug evidence for logging / telemetry. - evidence: Dict[str, str] - - -def _run(cmd: List[str]) -> Tuple[int, str, str]: - """Run a command and return (returncode, stdout, stderr).""" - try: - p = subprocess.run( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - check=False, - ) - return p.returncode, p.stdout.strip(), p.stderr.strip() - except Exception as e: - return 127, "", f"{type(e).__name__}: {e}" - - -def _which(cmd: str) -> Optional[str]: - """Find command in PATH.""" - paths = os.environ.get("PATH", "").split(os.pathsep) - for d in paths: - p = os.path.join(d, cmd) - if os.path.isfile(p) and os.access(p, os.X_OK): - return p - return None - - -def _parse_passwd_line(line: str) -> Optional[str]: - """Parse passwd format: name:pw:uid:gid:gecos:dir:shell""" - parts = line.split(":") - if len(parts) >= 7: - shell = parts[6].strip() - return shell or None - return None - - -def get_login_shell() -> Tuple[Optional[str], Dict[str, str]]: - """ - Tries multiple sources to determine the *account login shell*. - Order of preference: - 1) Python's pwd database (POSIX) - 2) macOS: `id -P user` (NSS/passwd view) - 3) Linux: `getent passwd user` - """ - evidence: Dict[str, str] = {} - user = os.environ.get("USER") or os.environ.get("LOGNAME") or "" - - # POSIX: pwd module uses NSS; on macOS it generally reflects Directory Services. - if os.name == "posix": - try: - import pwd - - if user: - shell = pwd.getpwnam(user).pw_shell - evidence["pwd.getpwnam"] = shell - if shell: - return shell, evidence - except Exception as e: - evidence["pwd.getpwnam_error"] = f"{type(e).__name__}: {e}" - - system = platform.system().lower() - - # macOS: `id -P user` prints passwd-style record. - if system == "darwin" and user: - rc, out, err = _run(["id", "-P", user]) - evidence["id_-P_rc"] = str(rc) - if err: - evidence["id_-P_err"] = err - if rc == 0 and out: - evidence["id_-P_out"] = out - # id -P output is colon-separated, shell is the last field. - shell = out.split(":")[-1].strip() or None - if shell: - return shell, evidence - - # Linux/BSD: getent is the most robust CLI view into NSS. - if user and _which("getent"): - rc, out, err = _run(["getent", "passwd", user]) - evidence["getent_rc"] = str(rc) - if err: - evidence["getent_err"] = err - if rc == 0 and out: - evidence["getent_out"] = out - shell = _parse_passwd_line(out) - if shell: - return shell, evidence - - # As a last resort: $SHELL (not authoritative for login shell) - env_shell = os.environ.get("SHELL") - if env_shell: - evidence["fallback_env_SHELL"] = env_shell - return env_shell, evidence - - -def _ps_comm(pid: int) -> Optional[str]: - """Get executable name for a process.""" - rc, out, _ = _run(["ps", "-p", str(pid), "-o", "comm="]) - if rc == 0 and out: - return out.strip() - return None - - -def _ps_args(pid: int) -> Optional[str]: - """Get full command line for a process.""" - rc, out, _ = _run(["ps", "-p", str(pid), "-o", "args="]) - if rc == 0 and out: - return out.strip() - return None - - -def get_invoking_shell(max_hops: int = 12) -> Tuple[Optional[str], Dict[str, str]]: - """ - Best-effort guess of the *shell that invoked this process* by walking parent PIDs. - This is useful if hanzo-mcp is launched from a user's interactive shell. - If your process is daemonized / launched by a service manager, this may return None. - """ - evidence: Dict[str, str] = {} - if os.name != "posix": - return None, {"note": "invoking shell detection is POSIX-only"} - - pid = os.getpid() - ppid = os.getppid() - evidence["self_pid"] = str(pid) - evidence["self_ppid"] = str(ppid) - - # Walk parent chain looking for a known shell. - cur = ppid - for hop in range(max_hops): - comm = _ps_comm(cur) or "" - args = _ps_args(cur) or "" - evidence[f"hop_{hop}_pid"] = str(cur) - if comm: - evidence[f"hop_{hop}_comm"] = comm - if args: - evidence[f"hop_{hop}_args"] = args - - base = os.path.basename(comm).strip() - if base in KNOWN_SHELL_PATH_BASENAMES: - # Try to return a plausible full path if args contains one. - if os.path.isabs(comm): - return comm, evidence - # Pull first token from args if it looks like a path to shell. - try: - argv0 = shlex.split(args)[0] - except Exception: - argv0 = "" - if argv0 and ( - os.path.isabs(argv0) or os.path.basename(argv0) in KNOWN_SHELL_NAMES - ): - return argv0, evidence - return comm, evidence - - # Next parent: use `ps -o ppid=` to get parent of parent. - rc, out, _ = _run(["ps", "-p", str(cur), "-o", "ppid="]) - if rc != 0 or not out.strip(): - break - next_ppid = int(out.strip()) - if next_ppid <= 1 or next_ppid == cur: - break - cur = next_ppid - - return None, evidence - - -def normalize_shell(shell: Optional[str]) -> Optional[str]: - """Normalize shell path - return as-is if valid.""" - if not shell: - return None - shell = shell.strip() - return shell or None - - -def shell_basename(shell: Optional[str]) -> Optional[str]: - """Get the basename of a shell path (e.g., '/opt/homebrew/bin/zsh' -> 'zsh').""" - if not shell: - return None - return os.path.basename(shell).strip() or None - - -def choose_default_shell(info: ShellInfo) -> str: - """ - Policy: prefer invoking shell if detected; else login shell; else $SHELL; else /bin/sh. - """ - for candidate in (info.invoking_shell, info.login_shell, info.env_shell): - c = normalize_shell(candidate) - if c: - return c - # Very conservative fallback - return "C:\\Windows\\System32\\cmd.exe" if os.name == "nt" else "/bin/sh" - - -def detect_shells() -> ShellInfo: - """Detect all shell information.""" - login_shell, login_ev = get_login_shell() - invoking_shell, inv_ev = get_invoking_shell() - env_shell = os.environ.get("SHELL") - - evidence: Dict[str, str] = {} - evidence.update({f"login.{k}": v for k, v in login_ev.items()}) - evidence.update({f"invoking.{k}": v for k, v in inv_ev.items()}) - if env_shell: - evidence["env.SHELL"] = env_shell - - return ShellInfo( - login_shell=normalize_shell(login_shell), - invoking_shell=normalize_shell(invoking_shell), - env_shell=normalize_shell(env_shell), - evidence=evidence, - ) - - -def resolve_shell_path(shell_name: str) -> Optional[str]: - """ - Resolve a shell name to its full path, preferring Homebrew on macOS. - - Args: - shell_name: Shell name (e.g., 'zsh', 'bash') - - Returns: - Full path to shell or None if not found - """ - # Check environment override first - force_shell = os.environ.get("HANZO_MCP_FORCE_SHELL") - if force_shell: - if os.path.isfile(force_shell) and os.access(force_shell, os.X_OK): - return force_shell - - # On Windows, skip Unix-specific paths and use shutil.which directly - if os.name == "nt": - return shutil.which(shell_name) - - # Priority paths - Homebrew first on macOS - search_paths = [ - f"/opt/homebrew/bin/{shell_name}", # Apple Silicon Homebrew - f"/usr/local/bin/{shell_name}", # Intel Homebrew - f"/bin/{shell_name}", # System - f"/usr/bin/{shell_name}", # System alternative - ] - - for path in search_paths: - if os.path.isfile(path) and os.access(path, os.X_OK): - return path - - # Fallback to which - return shutil.which(shell_name) - - -def get_active_shell() -> Tuple[str, str]: - """ - Get the user's active shell name and path. - - Respects environment overrides: - - HANZO_MCP_SHELL: Force shell name (e.g., "zsh") - - HANZO_MCP_FORCE_SHELL: Force shell path (e.g., "/opt/homebrew/bin/zsh") - - Returns: - Tuple of (shell_name, shell_path) - e.g., ("zsh", "/opt/homebrew/bin/zsh") - """ - # Check for explicit override - override_shell = os.environ.get("HANZO_MCP_SHELL") - if override_shell: - override_shell = override_shell.strip().lower() - if override_shell in SUPPORTED_SHELLS: - path = resolve_shell_path(override_shell) - if path: - return override_shell, path - - # Check for explicit path override - force_path = os.environ.get("HANZO_MCP_FORCE_SHELL") - if force_path and os.path.isfile(force_path) and os.access(force_path, os.X_OK): - name = shell_basename(force_path) - if name and name in SUPPORTED_SHELLS: - return name, force_path - # If it's an unknown shell, still use it but expose as "shell" - return name or "shell", force_path - - # Detect from environment - info = detect_shells() - chosen_path = choose_default_shell(info) - chosen_name = shell_basename(chosen_path) - - # Map to supported shell name - if chosen_name in SUPPORTED_SHELLS: - # Resolve to best available path (prefer Homebrew) - best_path = resolve_shell_path(chosen_name) - return chosen_name, best_path or chosen_path - - # Fallback: if detected shell isn't supported, default to zsh (or pwsh on Windows) - if os.name == "nt": - for fallback in ["pwsh", "powershell", "cmd"]: - path = shutil.which(fallback) - if path: - return fallback, path - return "cmd", "cmd.exe" - - for fallback in ["zsh", "bash", "fish", "dash"]: - path = resolve_shell_path(fallback) - if path: - return fallback, path - - # Ultimate fallback - return "sh", "/bin/sh" - - -def get_shell_tool_class(shell_name: str): - """ - Get the appropriate shell tool class for the given shell name. - - Args: - shell_name: Shell name (e.g., 'zsh', 'bash') - - Returns: - Shell tool class or None if not supported - """ - # Import here to avoid circular imports - from hanzo_tools.shell.shell_tools import ( - CshTool, - KshTool, - ZshTool, - BashTool, - DashTool, - FishTool, - TcshTool, - ) - - shell_map = { - "zsh": ZshTool, - "bash": BashTool, - "fish": FishTool, - "dash": DashTool, - "ksh": KshTool, - "ksh93": KshTool, # Alias - "pdksh": KshTool, # Alias - "mksh": KshTool, # Alias - "tcsh": TcshTool, - "csh": CshTool, - } - - return shell_map.get(shell_name.lower()) - - -# Cache for shell detection (avoid repeated subprocess calls) -_cached_shell: Optional[Tuple[str, str]] = None - - -def get_cached_active_shell() -> Tuple[str, str]: - """Get cached active shell info (detects once per process).""" - global _cached_shell - if _cached_shell is None: - _cached_shell = get_active_shell() - return _cached_shell - - -def clear_shell_cache(): - """Clear the shell detection cache (useful for testing).""" - global _cached_shell - _cached_shell = None - - -if __name__ == "__main__": - info = detect_shells() - shell_name, shell_path = get_active_shell() - - print("=== Shell Detection ===") - print(f"login_shell : {info.login_shell}") - print(f"invoking_shell: {info.invoking_shell}") - print(f"env_shell : {info.env_shell}") - print(f"") - print(f"=== Active Shell ===") - print(f"name : {shell_name}") - print(f"path : {shell_path}") - print(f"") - print(f"=== Environment ===") - print(f"HANZO_MCP_SHELL : {os.environ.get('HANZO_MCP_SHELL', '(not set)')}") - print( - f"HANZO_MCP_FORCE_SHELL: {os.environ.get('HANZO_MCP_FORCE_SHELL', '(not set)')}" - ) diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/shell_tools.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/shell_tools.py deleted file mode 100644 index 636b3b47f..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/shell_tools.py +++ /dev/null @@ -1,806 +0,0 @@ -"""Shell tool shims - zsh, bash, fish, dash, ksh, tcsh, csh, shell wrappers over cmd. - -These are thin wrappers that set the shell and delegate to CmdTool. -Use cmd directly for full control. - -Supported shells: -- zsh: Z shell (default on macOS, popular on Linux) -- bash: Bourne-Again shell (most common, default on most Linux) -- fish: Friendly Interactive Shell (modern, user-friendly) -- dash: Debian Almquist Shell (fast POSIX shell, Ubuntu's /bin/sh) -- ksh: KornShell (enterprise Unix, efficient scripting) -- tcsh: TENEX C Shell (enhanced csh with command completion) -- csh: C Shell (older, C-like syntax) -- shell: Auto-selects best available (zsh > bash > fish > dash > ksh > sh) -""" - -import os -import sys -import shutil -from typing import Any, Dict, List, Optional, Annotated, override - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context -from hanzo_tools.shell.cmd_tool import CmdTool, Command - - -class ZshTool(CmdTool): - """Zsh shell - thin wrapper over cmd with shell=zsh. - - Usage: - zsh("ls -la") # Single command - zsh(["ls", "pwd"]) # Sequential - zsh(["a", "b"], parallel=True) # Parallel - """ - - name = "zsh" - - def __init__(self, tools: Optional[Dict[str, BaseTool]] = None): - """Initialize with zsh as default shell.""" - super().__init__(tools=tools, default_shell="zsh") - - def _resolve_shell(self, preferred: str) -> str: - """Resolve zsh shell path. Falls back to bash, then platform default.""" - force_shell = os.environ.get("HANZO_MCP_FORCE_SHELL") - if force_shell: - return force_shell - - # On Windows, prefer powershell/cmd over Unix shells - if sys.platform == "win32": - for shell in ("pwsh", "powershell", "cmd"): - found = shutil.which(shell) - if found: - return found - return "cmd.exe" - - search_paths = [ - "/opt/homebrew/bin/zsh", - "/usr/local/bin/zsh", - "/bin/zsh", - "/usr/bin/zsh", - ] - - for path in search_paths: - if os.path.isfile(path) and os.access(path, os.X_OK): - return path - - found = shutil.which("zsh") - if found: - return found - - # Fallback to bash if zsh not found - return shutil.which("bash") or "sh" - - @property - @override - def description(self) -> str: - shell_name = os.path.basename(self.default_shell) - return f"""Zsh shell (using: {shell_name}). - -SIMPLE: - zsh("ls -la") # Single command - zsh("A ; B ; C") # Sequential via shell - -ARRAYS: - zsh(["a", "b", "c"]) # Sequential - zsh(["a", "b"], parallel=True) # All parallel - zsh(["a", ["b", "c"], "d"]) # Nested = parallel - -OPTIONS: - parallel: Run ALL commands concurrently - strict: Stop on first error - quiet: Suppress stdout - timeout: Per-command timeout (default: 45s) - cwd: Working directory - env: Environment variables - -AUTO-BACKGROUNDING: Commands exceeding 45s auto-background. -Use ps --logs to view, ps --kill to stop.""" - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register zsh tool with MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def zsh_handler( - command: Annotated[ - Optional[str], - Field(description="Single command to execute", default=None), - ] = None, - commands: Annotated[ - Optional[List[Any]], - Field(description="List of commands for DAG execution", default=None), - ] = None, - parallel: Annotated[ - bool, Field(description="Run all commands in parallel", default=False) - ] = False, - cwd: Annotated[ - Optional[str], Field(description="Working directory", default=None) - ] = None, - env: Annotated[ - Optional[Dict[str, str]], - Field(description="Environment variables", default=None), - ] = None, - timeout: Annotated[ - int, Field(description="Timeout per command (seconds)", default=30) - ] = 30, - strict: Annotated[ - bool, Field(description="Stop on first error", default=False) - ] = False, - quiet: Annotated[ - bool, Field(description="Suppress stdout", default=False) - ] = False, - ctx: MCPContext = None, - ) -> str: - return await tool_self.call( - ctx, - command=command, - commands=commands, - parallel=parallel, - shell=None, # Use default (zsh) - cwd=cwd, - env=env, - timeout=timeout, - strict=strict, - quiet=quiet, - ) - - -class BashTool(CmdTool): - """Bash shell - thin wrapper over cmd with shell=bash.""" - - name = "bash" - - def __init__(self, tools: Optional[Dict[str, BaseTool]] = None): - """Initialize with bash as default shell.""" - super().__init__(tools=tools, default_shell="bash") - - def _resolve_shell(self, preferred: str) -> str: - """Resolve bash shell path.""" - force_shell = os.environ.get("HANZO_MCP_FORCE_SHELL") - if force_shell: - return force_shell - - found = shutil.which("bash") - if found: - return found - - return "sh" - - @property - @override - def description(self) -> str: - return """Bash shell - thin wrapper over cmd. - -USAGE: - bash("ls -la") # Single command - bash(["a", "b", "c"]) # Sequential - bash(["a", "b"], parallel=True) # Parallel - -AUTO-BACKGROUNDING: Commands exceeding 45s auto-background.""" - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register bash tool with MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def bash_handler( - command: Annotated[ - Optional[str], - Field(description="Single command to execute", default=None), - ] = None, - commands: Annotated[ - Optional[List[Any]], Field(description="List of commands", default=None) - ] = None, - parallel: Annotated[ - bool, Field(description="Run in parallel", default=False) - ] = False, - cwd: Annotated[ - Optional[str], Field(description="Working directory", default=None) - ] = None, - env: Annotated[ - Optional[Dict[str, str]], - Field(description="Environment variables", default=None), - ] = None, - timeout: Annotated[ - int, Field(description="Timeout (seconds)", default=30) - ] = 30, - strict: Annotated[ - bool, Field(description="Stop on first error", default=False) - ] = False, - quiet: Annotated[ - bool, Field(description="Suppress stdout", default=False) - ] = False, - ctx: MCPContext = None, - ) -> str: - return await tool_self.call( - ctx, - command=command, - commands=commands, - parallel=parallel, - shell=None, # Use default (bash) - cwd=cwd, - env=env, - timeout=timeout, - strict=strict, - quiet=quiet, - ) - - -class FishTool(CmdTool): - """Fish shell - thin wrapper over cmd with shell=fish.""" - - name = "fish" - - def __init__(self, tools: Optional[Dict[str, BaseTool]] = None): - """Initialize with fish as default shell.""" - super().__init__(tools=tools, default_shell="fish") - - def _resolve_shell(self, preferred: str) -> str: - """Resolve fish shell path.""" - force_shell = os.environ.get("HANZO_MCP_FORCE_SHELL") - if force_shell: - return force_shell - - search_paths = [ - "/opt/homebrew/bin/fish", - "/usr/local/bin/fish", - "/usr/bin/fish", - ] - - for path in search_paths: - if os.path.isfile(path) and os.access(path, os.X_OK): - return path - - found = shutil.which("fish") - if found: - return found - - # Fallback to zsh/bash if fish not found - return shutil.which("zsh") or shutil.which("bash") or "sh" - - @property - @override - def description(self) -> str: - shell_name = os.path.basename(self.default_shell) - return f"""Fish shell (using: {shell_name}). - -USAGE: - fish("ls -la") # Single command - fish(["a", "b", "c"]) # Sequential - fish(["a", "b"], parallel=True) # Parallel - -AUTO-BACKGROUNDING: Commands exceeding 45s auto-background.""" - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register fish tool with MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def fish_handler( - command: Annotated[ - Optional[str], - Field(description="Single command to execute", default=None), - ] = None, - commands: Annotated[ - Optional[List[Any]], Field(description="List of commands", default=None) - ] = None, - parallel: Annotated[ - bool, Field(description="Run in parallel", default=False) - ] = False, - cwd: Annotated[ - Optional[str], Field(description="Working directory", default=None) - ] = None, - env: Annotated[ - Optional[Dict[str, str]], - Field(description="Environment variables", default=None), - ] = None, - timeout: Annotated[ - int, Field(description="Timeout (seconds)", default=30) - ] = 30, - strict: Annotated[ - bool, Field(description="Stop on first error", default=False) - ] = False, - quiet: Annotated[ - bool, Field(description="Suppress stdout", default=False) - ] = False, - ctx: MCPContext = None, - ) -> str: - return await tool_self.call( - ctx, - command=command, - commands=commands, - parallel=parallel, - shell=None, # Use default (fish) - cwd=cwd, - env=env, - timeout=timeout, - strict=strict, - quiet=quiet, - ) - - -class DashTool(CmdTool): - """Dash shell - fast POSIX-compliant shell (Ubuntu's /bin/sh).""" - - name = "dash" - - def __init__(self, tools: Optional[Dict[str, BaseTool]] = None): - """Initialize with dash as default shell.""" - super().__init__(tools=tools, default_shell="dash") - - def _resolve_shell(self, preferred: str) -> str: - """Resolve dash shell path.""" - force_shell = os.environ.get("HANZO_MCP_FORCE_SHELL") - if force_shell: - return force_shell - - # dash is often /bin/sh on Debian/Ubuntu - search_paths = [ - "/bin/dash", - "/usr/bin/dash", - ] - - for path in search_paths: - if os.path.isfile(path) and os.access(path, os.X_OK): - return path - - found = shutil.which("dash") - if found: - return found - - # Fallback to sh (which may be dash on Ubuntu) - return "sh" - - @property - @override - def description(self) -> str: - return """Dash shell - fast POSIX-compliant shell. - -Dash is the Debian Almquist Shell, a POSIX-compliant shell that's -faster than bash for scripts. It's the default /bin/sh on Ubuntu/Debian. - -USAGE: - dash("ls -la") # Single command - dash(["a", "b", "c"]) # Sequential - dash(["a", "b"], parallel=True) # Parallel - -AUTO-BACKGROUNDING: Commands exceeding 45s auto-background.""" - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register dash tool with MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def dash_handler( - command: Annotated[ - Optional[str], - Field(description="Single command to execute", default=None), - ] = None, - commands: Annotated[ - Optional[List[Any]], Field(description="List of commands", default=None) - ] = None, - parallel: Annotated[ - bool, Field(description="Run in parallel", default=False) - ] = False, - cwd: Annotated[ - Optional[str], Field(description="Working directory", default=None) - ] = None, - env: Annotated[ - Optional[Dict[str, str]], - Field(description="Environment variables", default=None), - ] = None, - timeout: Annotated[ - int, Field(description="Timeout (seconds)", default=30) - ] = 30, - strict: Annotated[ - bool, Field(description="Stop on first error", default=False) - ] = False, - quiet: Annotated[ - bool, Field(description="Suppress stdout", default=False) - ] = False, - ctx: MCPContext = None, - ) -> str: - return await tool_self.call( - ctx, - command=command, - commands=commands, - parallel=parallel, - shell=None, # Use default (dash) - cwd=cwd, - env=env, - timeout=timeout, - strict=strict, - quiet=quiet, - ) - - -class KshTool(CmdTool): - """KornShell - enterprise Unix shell with efficient scripting.""" - - name = "ksh" - - def __init__(self, tools: Optional[Dict[str, BaseTool]] = None): - """Initialize with ksh as default shell.""" - super().__init__(tools=tools, default_shell="ksh") - - def _resolve_shell(self, preferred: str) -> str: - """Resolve ksh shell path.""" - force_shell = os.environ.get("HANZO_MCP_FORCE_SHELL") - if force_shell: - return force_shell - - search_paths = [ - "/bin/ksh", - "/usr/bin/ksh", - "/usr/local/bin/ksh", - "/opt/homebrew/bin/ksh", - ] - - for path in search_paths: - if os.path.isfile(path) and os.access(path, os.X_OK): - return path - - found = shutil.which("ksh") - if found: - return found - - # Also try ksh93 and pdksh - for variant in ["ksh93", "pdksh", "mksh"]: - found = shutil.which(variant) - if found: - return found - - return "sh" - - @property - @override - def description(self) -> str: - return """KornShell (ksh) - enterprise Unix shell. - -KornShell is historically important in enterprise Unix environments. -Efficient for scripting, still used on some commercial Unix systems. - -USAGE: - ksh("ls -la") # Single command - ksh(["a", "b", "c"]) # Sequential - ksh(["a", "b"], parallel=True) # Parallel - -AUTO-BACKGROUNDING: Commands exceeding 45s auto-background.""" - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register ksh tool with MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def ksh_handler( - command: Annotated[ - Optional[str], - Field(description="Single command to execute", default=None), - ] = None, - commands: Annotated[ - Optional[List[Any]], Field(description="List of commands", default=None) - ] = None, - parallel: Annotated[ - bool, Field(description="Run in parallel", default=False) - ] = False, - cwd: Annotated[ - Optional[str], Field(description="Working directory", default=None) - ] = None, - env: Annotated[ - Optional[Dict[str, str]], - Field(description="Environment variables", default=None), - ] = None, - timeout: Annotated[ - int, Field(description="Timeout (seconds)", default=30) - ] = 30, - strict: Annotated[ - bool, Field(description="Stop on first error", default=False) - ] = False, - quiet: Annotated[ - bool, Field(description="Suppress stdout", default=False) - ] = False, - ctx: MCPContext = None, - ) -> str: - return await tool_self.call( - ctx, - command=command, - commands=commands, - parallel=parallel, - shell=None, - cwd=cwd, - env=env, - timeout=timeout, - strict=strict, - quiet=quiet, - ) - - -class TcshTool(CmdTool): - """TENEX C Shell - enhanced csh with command completion.""" - - name = "tcsh" - - def __init__(self, tools: Optional[Dict[str, BaseTool]] = None): - """Initialize with tcsh as default shell.""" - super().__init__(tools=tools, default_shell="tcsh") - - def _resolve_shell(self, preferred: str) -> str: - """Resolve tcsh shell path.""" - force_shell = os.environ.get("HANZO_MCP_FORCE_SHELL") - if force_shell: - return force_shell - - search_paths = [ - "/bin/tcsh", - "/usr/bin/tcsh", - "/usr/local/bin/tcsh", - "/opt/homebrew/bin/tcsh", - ] - - for path in search_paths: - if os.path.isfile(path) and os.access(path, os.X_OK): - return path - - found = shutil.which("tcsh") - if found: - return found - - # Fallback to csh - return shutil.which("csh") or "sh" - - @property - @override - def description(self) -> str: - return """TENEX C Shell (tcsh) - enhanced C shell. - -Tcsh is an enhanced version of csh with command completion, -command-line editing, and other improvements. Mostly legacy now -but still encountered in some environments. - -USAGE: - tcsh("ls -la") # Single command - tcsh(["a", "b", "c"]) # Sequential - tcsh(["a", "b"], parallel=True) # Parallel - -AUTO-BACKGROUNDING: Commands exceeding 45s auto-background.""" - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register tcsh tool with MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def tcsh_handler( - command: Annotated[ - Optional[str], - Field(description="Single command to execute", default=None), - ] = None, - commands: Annotated[ - Optional[List[Any]], Field(description="List of commands", default=None) - ] = None, - parallel: Annotated[ - bool, Field(description="Run in parallel", default=False) - ] = False, - cwd: Annotated[ - Optional[str], Field(description="Working directory", default=None) - ] = None, - env: Annotated[ - Optional[Dict[str, str]], - Field(description="Environment variables", default=None), - ] = None, - timeout: Annotated[ - int, Field(description="Timeout (seconds)", default=30) - ] = 30, - strict: Annotated[ - bool, Field(description="Stop on first error", default=False) - ] = False, - quiet: Annotated[ - bool, Field(description="Suppress stdout", default=False) - ] = False, - ctx: MCPContext = None, - ) -> str: - return await tool_self.call( - ctx, - command=command, - commands=commands, - parallel=parallel, - shell=None, - cwd=cwd, - env=env, - timeout=timeout, - strict=strict, - quiet=quiet, - ) - - -class CshTool(CmdTool): - """C Shell - older shell with C-like syntax.""" - - name = "csh" - - def __init__(self, tools: Optional[Dict[str, BaseTool]] = None): - """Initialize with csh as default shell.""" - super().__init__(tools=tools, default_shell="csh") - - def _resolve_shell(self, preferred: str) -> str: - """Resolve csh shell path.""" - force_shell = os.environ.get("HANZO_MCP_FORCE_SHELL") - if force_shell: - return force_shell - - search_paths = [ - "/bin/csh", - "/usr/bin/csh", - "/usr/local/bin/csh", - ] - - for path in search_paths: - if os.path.isfile(path) and os.access(path, os.X_OK): - return path - - found = shutil.which("csh") - if found: - return found - - # Fallback to tcsh (often linked to csh) - return shutil.which("tcsh") or "sh" - - @property - @override - def description(self) -> str: - return """C Shell (csh) - shell with C-like syntax. - -The C shell is an older shell with syntax reminiscent of C. -Mostly legacy now but still encountered in some environments. -Consider using tcsh (enhanced csh) or bash instead. - -USAGE: - csh("ls -la") # Single command - csh(["a", "b", "c"]) # Sequential - csh(["a", "b"], parallel=True) # Parallel - -AUTO-BACKGROUNDING: Commands exceeding 45s auto-background.""" - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register csh tool with MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def csh_handler( - command: Annotated[ - Optional[str], - Field(description="Single command to execute", default=None), - ] = None, - commands: Annotated[ - Optional[List[Any]], Field(description="List of commands", default=None) - ] = None, - parallel: Annotated[ - bool, Field(description="Run in parallel", default=False) - ] = False, - cwd: Annotated[ - Optional[str], Field(description="Working directory", default=None) - ] = None, - env: Annotated[ - Optional[Dict[str, str]], - Field(description="Environment variables", default=None), - ] = None, - timeout: Annotated[ - int, Field(description="Timeout (seconds)", default=30) - ] = 30, - strict: Annotated[ - bool, Field(description="Stop on first error", default=False) - ] = False, - quiet: Annotated[ - bool, Field(description="Suppress stdout", default=False) - ] = False, - ctx: MCPContext = None, - ) -> str: - return await tool_self.call( - ctx, - command=command, - commands=commands, - parallel=parallel, - shell=None, - cwd=cwd, - env=env, - timeout=timeout, - strict=strict, - quiet=quiet, - ) - - -class ShellTool(CmdTool): - """Smart shell - auto-selects best available shell (zsh > bash > fish > dash > ksh > sh).""" - - name = "shell" - - def __init__(self, tools: Optional[Dict[str, BaseTool]] = None): - """Initialize with best available shell.""" - super().__init__( - tools=tools, default_shell="zsh" - ) # Will resolve to best available - - @property - @override - def description(self) -> str: - shell_name = os.path.basename(self.default_shell) - return f"""Smart shell (auto-selected: {shell_name}). - -Automatically uses the best available shell: - zsh > bash > fish > dash > sh - -USAGE: - shell("ls -la") # Single command - shell(["a", "b", "c"]) # Sequential - shell(["a", "b"], parallel=True) # Parallel - -AUTO-BACKGROUNDING: Commands exceeding 45s auto-background.""" - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register shell tool with MCP server.""" - tool_self = self - - @mcp_server.tool(name=self.name, description=self.description) - async def shell_handler( - command: Annotated[ - Optional[str], - Field(description="Single command to execute", default=None), - ] = None, - commands: Annotated[ - Optional[List[Any]], Field(description="List of commands", default=None) - ] = None, - parallel: Annotated[ - bool, Field(description="Run in parallel", default=False) - ] = False, - cwd: Annotated[ - Optional[str], Field(description="Working directory", default=None) - ] = None, - env: Annotated[ - Optional[Dict[str, str]], - Field(description="Environment variables", default=None), - ] = None, - timeout: Annotated[ - int, Field(description="Timeout (seconds)", default=30) - ] = 30, - strict: Annotated[ - bool, Field(description="Stop on first error", default=False) - ] = False, - quiet: Annotated[ - bool, Field(description="Suppress stdout", default=False) - ] = False, - ctx: MCPContext = None, - ) -> str: - return await tool_self.call( - ctx, - command=command, - commands=commands, - parallel=parallel, - shell=None, # Use default - cwd=cwd, - env=env, - timeout=timeout, - strict=strict, - quiet=quiet, - ) - - -# Singleton instances -zsh_tool = ZshTool() -bash_tool = BashTool() -fish_tool = FishTool() -dash_tool = DashTool() -ksh_tool = KshTool() -tcsh_tool = TcshTool() -csh_tool = CshTool() -shell_tool = ShellTool() diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/shellflow.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/shellflow.py deleted file mode 100644 index 1f8b7f291..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/shellflow.py +++ /dev/null @@ -1,504 +0,0 @@ -"""Shellflow - minimal DSL for DAG execution. - -Syntax: - A ; B ; C โ†’ sequential (do) - { A & B & C } โ†’ parallel (all) - A ; { B & C } ; D โ†’ mixed - -Compiles to JSON AST: - {"type": "do", "steps": ["A", {"type": "all", "steps": ["B", "C"]}, "D"]} - -Examples: - mkdir -p dist ; { cp a.txt dist/ & cp b.txt dist/ } ; zip -r out.zip dist/ - -Performance: - Optimized for high throughput with: - - Precompiled regex patterns - - Local variable caching - - Fast-path for simple commands - - LRU cache for repeated patterns - - Full type annotations for mypyc compilation - - Compile with mypyc for 2-5x speedup: - mypyc hanzo_tools/shell/shellflow.py -""" - -from __future__ import annotations - -import re -from typing import Any, Dict, List, Final, Tuple, Union -from functools import lru_cache # noqa: TID251 - not using Stainless SDK - -# Type aliases with full annotations -ASTDict = Dict[str, Any] -ASTNode = Union[str, ASTDict] -ASTTuple = Tuple[str, Tuple[Any, ...]] -TokenList = List[str] -CommandList = List[Any] - -# Constants -TYPE_DO: Final[str] = "do" -TYPE_ALL: Final[str] = "all" - -# Precompiled regex for performance -# Match: quoted strings, braces, semicolon, shell operators, single & -_TOKEN_PATTERN: Final[str] = r""" - (?P"(?:[^"\\]|\\.)*") # Double-quoted string - |(?P'(?:[^'\\]|\\.)*') # Single-quoted string - |(?P\{) # Left brace - |(?P\}) # Right brace - |(?P;) # Semicolon - |(?P&&|\|\|) # Shell operators (keep together) - |(?P&) # Single & (parallel separator) - |(?P[^{};'"&|]+) # Other text (no & or |) - |(?P\|(?!\|)) # Single | (not ||) -""" - -_TOKEN_RE: Final = re.compile(_TOKEN_PATTERN, re.VERBOSE) - -# Preserved token types (tuple for faster `in` check than frozenset for small n) -_PRESERVED_KINDS: Final[Tuple[str, ...]] = ("dquote", "squote", "shell_op", "pipe") - -# Local reference for hot path -_finditer = _TOKEN_RE.finditer - - -def parse(source: str) -> ASTDict: - """Parse shellflow source to AST. - - Args: - source: Shellflow source string - - Returns: - AST in object form: {"type": "do"|"all", "steps": [...]} - """ - # Strip and fast-path empty - source = source.strip() - if not source: - return {"type": TYPE_DO, "steps": []} - - # Fast path: single simple command (no operators) - # Using 'in' is faster than regex for simple checks - if ";" not in source and "{" not in source and "&" not in source: - return {"type": TYPE_DO, "steps": [source]} - - # Tokenize and parse - tokens: TokenList = _tokenize_fast(source) - ast: ASTDict = _parse_tokens(tokens) - return _normalize(ast) - - -def _tokenize_fast(source: str) -> TokenList: - """Tokenize shellflow into commands and operators. - - Optimized with local variable caching and type hints for mypyc. - """ - tokens: TokenList = [] - tokens_append = tokens.append - current_parts: List[str] = [] - parts_append = current_parts.append - depth: int = 0 - - for match in _finditer(source): - kind: str | None = match.lastgroup - value: str = match.group() - - if kind in _PRESERVED_KINDS: - parts_append(value) - elif kind == "lbrace": - if depth == 0 and current_parts: - cmd: str = "".join(current_parts).strip() - if cmd: - tokens_append(cmd) - current_parts = [] - parts_append = current_parts.append - depth += 1 - parts_append(value) - elif kind == "rbrace": - parts_append(value) - depth -= 1 - if depth == 0: - block: str = "".join(current_parts).strip() - if block: - tokens_append(block) - current_parts = [] - parts_append = current_parts.append - elif kind == "semi" and depth == 0: - cmd = "".join(current_parts).strip() - if cmd: - tokens_append(cmd) - tokens_append(";") - current_parts = [] - parts_append = current_parts.append - elif kind == "and" and depth == 0: - cmd = "".join(current_parts).strip() - if cmd: - tokens_append(cmd) - tokens_append("&") - current_parts = [] - parts_append = current_parts.append - else: - parts_append(value) - - # Flush remaining - if current_parts: - cmd = "".join(current_parts).strip() - if cmd: - tokens_append(cmd) - - return tokens - - -def _tokenize_parallel_fast(source: str) -> TokenList: - """Tokenize content inside { } splitting on & (but not &&). - - Optimized version with full type hints. - """ - tokens: TokenList = [] - tokens_append = tokens.append - current_parts: List[str] = [] - parts_append = current_parts.append - depth: int = 0 - - for match in _finditer(source): - kind: str | None = match.lastgroup - value: str = match.group() - - if kind in _PRESERVED_KINDS: - parts_append(value) - elif kind == "lbrace": - depth += 1 - parts_append(value) - elif kind == "rbrace": - depth -= 1 - parts_append(value) - elif kind == "and" and depth == 0: - cmd: str = "".join(current_parts).strip() - if cmd: - tokens_append(cmd) - current_parts = [] - parts_append = current_parts.append - else: - parts_append(value) - - if current_parts: - cmd = "".join(current_parts).strip() - if cmd: - tokens_append(cmd) - - return tokens - - -def _parse_tokens(tokens: TokenList) -> ASTDict: - """Parse token list to AST.""" - if not tokens: - return {"type": TYPE_DO, "steps": []} - - # Check if this is a single block - if len(tokens) == 1: - t: str = tokens[0] - if t.startswith("{") and t.endswith("}"): - inner: str = t[1:-1].strip() - inner_tokens: TokenList = _tokenize_parallel_fast(inner) - steps: List[ASTNode] = [ - _parse_single(tok) for tok in inner_tokens if tok and tok != "&" - ] - return {"type": TYPE_ALL, "steps": steps} - - # Parse as sequential - steps = [] - steps_append = steps.append - - for token in tokens: - if token == ";": - continue - elif token.startswith("{") and token.endswith("}"): - inner = token[1:-1].strip() - inner_tokens = _tokenize_parallel_fast(inner) - inner_steps: List[ASTNode] = [ - _parse_single(tok) for tok in inner_tokens if tok and tok != "&" - ] - if len(inner_steps) == 1: - steps_append(inner_steps[0]) - else: - steps_append({"type": TYPE_ALL, "steps": inner_steps}) - else: - steps_append(token) - - if len(steps) == 1: - s: ASTNode = steps[0] - if isinstance(s, dict): - return s - return {"type": TYPE_DO, "steps": steps} - - return {"type": TYPE_DO, "steps": steps} - - -def _parse_single(token: str) -> ASTNode: - """Parse a single token (command or nested block).""" - token = token.strip() - if token.startswith("{") and token.endswith("}"): - inner: str = token[1:-1].strip() - inner_tokens: TokenList = _tokenize_parallel_fast(inner) - steps: List[ASTNode] = [_parse_single(t) for t in inner_tokens if t] - return {"type": TYPE_ALL, "steps": steps} - return token - - -def _normalize(ast: ASTNode) -> ASTDict: - """Normalize AST: flatten nested do/all, drop singletons.""" - if isinstance(ast, str): - return {"type": TYPE_DO, "steps": [ast]} - - node_type: str = ast.get("type", TYPE_DO) - steps: List[ASTNode] = ast.get("steps", []) - - # Recursively normalize children - normalized_steps: List[ASTNode] = [] - normalized_append = normalized_steps.append - - for step in steps: - if isinstance(step, dict): - step = _normalize(step) - # Flatten same-type nesting - step_type: str = step.get("type", "") - if step_type == node_type: - normalized_steps.extend(step.get("steps", [])) - else: - normalized_append(step) - elif step: - normalized_append(step) - - # Drop singleton wrappers - if len(normalized_steps) == 1: - ns: ASTNode = normalized_steps[0] - if isinstance(ns, dict): - return ns - return {"type": TYPE_DO, "steps": normalized_steps} - - if not normalized_steps: - return {"type": TYPE_DO, "steps": []} - - return {"type": node_type, "steps": normalized_steps} - - -def to_sexp(ast: ASTNode) -> Any: - """Convert object-form AST to S-expression form. - - {"type": "do", "steps": ["A", {"type": "all", "steps": ["B", "C"]}]} - โ†’ ["do", "A", ["all", "B", "C"]] - """ - if isinstance(ast, str): - return ast - - node_type: str = ast.get("type", TYPE_DO) - steps: List[ASTNode] = ast.get("steps", []) - - return [node_type] + [to_sexp(s) for s in steps] - - -def from_sexp(sexp: Any) -> ASTNode: - """Convert S-expression to object-form AST. - - ["do", "A", ["all", "B", "C"]] - โ†’ {"type": "do", "steps": ["A", {"type": "all", "steps": ["B", "C"]}]} - """ - if isinstance(sexp, str): - return sexp - - if not sexp: - return {"type": TYPE_DO, "steps": []} - - node_type: str = sexp[0] - steps: List[ASTNode] = [from_sexp(s) for s in sexp[1:]] - - return {"type": node_type, "steps": steps} - - -def to_commands(ast: ASTNode) -> CommandList: - """Convert AST to commands list for existing DAG executor. - - Transforms: - - {"type": "do", "steps": [...]} โ†’ [...] (serial) - - {"type": "all", "steps": [...]} โ†’ [[...]] (nested = parallel) - - str โ†’ [str] - """ - if isinstance(ast, str): - return [ast] - - node_type: str = ast.get("type", TYPE_DO) - steps: List[ASTNode] = ast.get("steps", []) - - if node_type == TYPE_ALL: - # Parallel: return as nested list - result: CommandList = [] - result_append = result.append - for step in steps: - if isinstance(step, str): - result_append(step) - else: - result.extend(to_commands(step)) - return [result] - - # Sequential (do) - result = [] - result_append = result.append - - for step in steps: - if isinstance(step, str): - result_append(step) - elif isinstance(step, dict): - step_type: str = step.get("type", "") - if step_type == TYPE_ALL: - parallel_cmds: CommandList = [] - p_append = parallel_cmds.append - for s in step.get("steps", []): - if isinstance(s, str): - p_append(s) - else: - parallel_cmds.extend(to_commands(s)) - result_append(parallel_cmds) - else: - result.extend(to_commands(step)) - - return result - - -def render_ascii(ast: ASTNode, indent: int = 0) -> str: - """Render AST as ASCII tree.""" - prefix: str = " " * indent - - if isinstance(ast, str): - return f"{prefix}โ””โ”€ {ast}\n" - - node_type: str = ast.get("type", TYPE_DO) - steps: List[ASTNode] = ast.get("steps", []) - - lines: List[str] = [f"{prefix}{'โ”œโ”€' if indent else ''}[{node_type}]\n"] - lines_append = lines.append - - num_steps: int = len(steps) - for i, step in enumerate(steps): - is_last: bool = i == num_steps - 1 - if isinstance(step, str): - marker: str = "โ””โ”€" if is_last else "โ”œโ”€" - lines_append(f"{prefix} {marker} {step}\n") - else: - lines_append(render_ascii(step, indent + 1)) - - return "".join(lines) - - -# Cached compile for repeated patterns -@lru_cache(maxsize=256) -def _parse_cached(source: str) -> ASTTuple: - """Cached version of parse that returns a hashable tuple form.""" - ast: ASTDict = parse(source) - return _dict_to_tuple(ast) - - -def _dict_to_tuple(d: ASTNode) -> ASTTuple | str: - """Convert AST dict to hashable tuple.""" - if isinstance(d, str): - return d - node_type: str = d.get("type", TYPE_DO) - steps: List[ASTNode] = d.get("steps", []) - return (node_type, tuple(_dict_to_tuple(s) for s in steps)) - - -def _tuple_to_dict(t: ASTTuple | str) -> ASTNode: - """Convert tuple back to AST dict.""" - if isinstance(t, str): - return t - return {"type": t[0], "steps": [_tuple_to_dict(s) for s in t[1]]} - - -def compile(source: str, format: str = "commands", cached: bool = False) -> Any: - """Compile shellflow to various formats. - - Args: - source: Shellflow source string - format: Output format - "ast", "sexp", "commands", "ascii" - cached: Use LRU cache for repeated patterns (default: False) - - Returns: - Compiled output in requested format - """ - ast: ASTDict - if cached: - ast_tuple: ASTTuple | str = _parse_cached(source) - result: ASTNode = _tuple_to_dict(ast_tuple) - ast = ( - result if isinstance(result, dict) else {"type": TYPE_DO, "steps": [result]} - ) - else: - ast = parse(source) - - if format == "ast": - return ast - elif format == "sexp": - return to_sexp(ast) - elif format == "commands": - return to_commands(ast) - elif format == "ascii": - return render_ascii(ast) - else: - raise ValueError(f"Unknown format: {format}") - - -# ============================================================================ -# Inline optimized versions for hot paths -# These use minimal function calls and are designed for mypyc compilation -# ============================================================================ - - -def parse_fast(source: str) -> ASTDict: - """Ultra-fast parse for simple sequential commands. - - Falls back to full parse for complex syntax. - """ - source = source.strip() - if not source: - return {"type": TYPE_DO, "steps": []} - - # Fast path: no special characters at all - if ";" not in source and "{" not in source and "&" not in source: - return {"type": TYPE_DO, "steps": [source]} - - # Fast path: simple sequential (only semicolons, no braces or ampersands) - if "{" not in source and "&" not in source: - # Split on semicolon, strip each part - parts: List[str] = [p.strip() for p in source.split(";") if p.strip()] - if parts: - return {"type": TYPE_DO, "steps": parts} - return {"type": TYPE_DO, "steps": []} - - # Fall back to full parse - return parse(source) - - -def compile_to_commands_fast(source: str) -> CommandList: - """Compile directly to commands list, optimized path. - - Skips intermediate representations when possible. - """ - source = source.strip() - if not source: - return [] - - # Fast path: single command - if ";" not in source and "{" not in source and "&" not in source: - return [source] - - # Fast path: simple sequential - if "{" not in source and "&" not in source: - return [p.strip() for p in source.split(";") if p.strip()] - - # Full compilation - ast: ASTDict = parse(source) - return to_commands(ast) - - -# Aliases for backwards compatibility -_tokenize = _tokenize_fast -_tokenize_parallel = _tokenize_parallel_fast diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/truncate.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/truncate.py deleted file mode 100644 index 6f6ed4b9e..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/truncate.py +++ /dev/null @@ -1,98 +0,0 @@ -"""Response truncation utilities for shell tools. - -Ensures shell command output doesn't exceed token limits. -""" - -import tiktoken - - -def estimate_tokens(text: str, model: str = "gpt-4") -> int: - """Estimate the number of tokens in a text string. - - Args: - text: The text to estimate tokens for - model: The model to use for token estimation (default: gpt-4) - - Returns: - Estimated number of tokens - """ - try: - encoding = tiktoken.encoding_for_model(model) - except KeyError: - encoding = tiktoken.get_encoding("cl100k_base") - - return len(encoding.encode(text)) - - -def truncate_response( - response: str, - max_tokens: int = 20000, - truncation_message: str = "\n\n[Response truncated due to length. Please use pagination, filtering, or limit parameters to see more.]", -) -> str: - """Truncate a response to fit within token limits. - - Args: - response: The response text to truncate - max_tokens: Maximum number of tokens allowed (default: 20000) - truncation_message: Message to append when truncating - - Returns: - Truncated response if needed, original response otherwise - """ - # Quick check - if response is short, no need to count tokens - if len(response) < max_tokens * 2: # Rough estimate: 1 token โ‰ˆ 2-4 chars - return response - - # Estimate tokens - token_count = estimate_tokens(response) - - # If within limit, return as-is - if token_count <= max_tokens: - return response - - # Need to truncate - left, right = 0, len(response) - truncation_msg_tokens = estimate_tokens(truncation_message) - target_tokens = max_tokens - truncation_msg_tokens - - while left < right - 1: - mid = (left + right) // 2 - mid_tokens = estimate_tokens(response[:mid]) - - if mid_tokens <= target_tokens: - left = mid - else: - right = mid - - # Find a good break point (newline or space) - truncate_at = left - for i in range(min(100, left), -1, -1): - if response[left - i] in "\n ": - truncate_at = left - i - break - - return response[:truncate_at] + truncation_message - - -def truncate_lines( - response: str, - max_lines: int = 1000, - truncation_message: str = "\n\n[Response truncated to {max_lines} lines. Please use pagination or filtering to see more.]", -) -> str: - """Truncate a response by number of lines. - - Args: - response: The response text to truncate - max_lines: Maximum number of lines allowed (default: 1000) - truncation_message: Message template to append when truncating - - Returns: - Truncated response if needed, original response otherwise - """ - lines = response.split("\n") - - if len(lines) <= max_lines: - return response - - truncated = "\n".join(lines[:max_lines]) - return truncated + truncation_message.format(max_lines=max_lines) diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/uvx_tool.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/uvx_tool.py deleted file mode 100644 index 329f1328a..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/uvx_tool.py +++ /dev/null @@ -1,92 +0,0 @@ -"""UVX tool for both sync and background execution.""" - -from typing import Optional, override -from pathlib import Path - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import auto_timeout -from hanzo_tools.shell.base_process import BaseBinaryTool - - -class UvxTool(BaseBinaryTool): - """Tool for running uvx commands.""" - - name = "uvx" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Run Python packages with uvx with automatic backgrounding for long-running processes. - -Commands that run for more than 2 minutes will automatically continue in the background. - -Usage: -uvx ruff check . -uvx mkdocs serve # Auto-backgrounds after 2 minutes -uvx black --check src/ -uvx jupyter lab --port 8888 # Auto-backgrounds if needed""" - - @override - def get_binary_name(self) -> str: - """Get the binary name.""" - return "uvx" - - @override - async def run( - self, - ctx: MCPContext, - package: str, - args: str = "", - cwd: Optional[str] = None, - python: Optional[str] = None, - ) -> str: - """Run a uvx command with auto-backgrounding.""" - work_dir = Path(cwd).resolve() if cwd else Path.cwd() - - flags = [] - if python: - flags.extend(["--python", python]) - - full_args = args.split() if args else [] - - return await self.execute_sync( - package, - cwd=work_dir, - flags=flags, - args=full_args, - timeout=None, - ) - - def register(self, server: FastMCP) -> None: - """Register the tool with the MCP server.""" - tool_self = self - - @server.tool(name=self.name, description=self.description) - async def uvx( - ctx: MCPContext, - package: str, - args: str = "", - cwd: Optional[str] = None, - python: Optional[str] = None, - ) -> str: - return await tool_self.run( - ctx, package=package, args=args, cwd=cwd, python=python - ) - - @auto_timeout("uvx") - async def call(self, ctx: MCPContext, **params) -> str: - """Call the tool with arguments.""" - return await self.run( - ctx, - package=params["package"], - args=params.get("args", ""), - cwd=params.get("cwd"), - python=params.get("python"), - ) - - -# Create tool instance -uvx_tool = UvxTool() diff --git a/pkg/hanzo-tools-shell/hanzo_tools/shell/wget_tool.py b/pkg/hanzo-tools-shell/hanzo_tools/shell/wget_tool.py deleted file mode 100644 index 5134c8125..000000000 --- a/pkg/hanzo-tools-shell/hanzo_tools/shell/wget_tool.py +++ /dev/null @@ -1,261 +0,0 @@ -"""Wget tool - reliable file and site downloads. - -Provides a clean interface for wget with proper handling of recursive downloads. -""" - -import os -import asyncio -from typing import Optional, Annotated, final, override - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, auto_timeout, create_tool_context - - -@final -class WgetTool(BaseTool): - """Wget tool - reliable downloads without shell escaping issues. - - Supports single file downloads and full site mirroring. - """ - - name = "wget" - - @property - @override - def description(self) -> str: - return """Download files and mirror websites reliably. - -Examples: - wget --url "https://example.com/file.zip" - wget --url "https://example.com/file.zip" --output "/tmp/myfile.zip" - wget --url "https://docs.example.com" --mirror true - wget --url "https://docs.example.com" --recursive true --depth 2 - wget --url "https://example.com" --recursive true --accept "*.pdf,*.doc" - -Parameters: - url: URL to download (required) - output: Output file or directory - mirror: Mirror site for offline viewing (sets recursive + timestamps) - recursive: Download recursively - depth: Maximum recursion depth (default: 5 for recursive) - accept: Accept only files matching pattern (e.g., "*.pdf,*.html") - reject: Reject files matching pattern - domains: Limit to specific domains - no_parent: Don't ascend to parent directory - continue_download: Continue partial downloads - timeout: Timeout in seconds (default: 300 for mirrors, 60 for files) - quiet: Suppress output - -Returns status and download summary. -""" - - @override - @auto_timeout("wget") - async def call( - self, - ctx: MCPContext, - url: str, - output: Optional[str] = None, - mirror: bool = False, - recursive: bool = False, - depth: Optional[int] = None, - accept: Optional[str] = None, - reject: Optional[str] = None, - domains: Optional[str] = None, - no_parent: bool = True, - continue_download: bool = False, - timeout: Optional[int] = None, - quiet: bool = False, - **kwargs, - ) -> str: - """Download files or mirror websites.""" - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Build wget command - cmd = ["wget"] - - # Mirror mode (most common for site downloads) - if mirror: - cmd.extend( - [ - "--mirror", # Turn on mirroring - "--convert-links", # Convert links for offline viewing - "--adjust-extension", # Add .html extension - "--page-requisites", # Get all assets (css, js, images) - "--no-host-directories", # Don't create host directory - ] - ) - recursive = True # Mirror implies recursive - - # Recursive download - if recursive: - cmd.append("-r") - if depth is not None: - cmd.extend(["-l", str(depth)]) - elif not mirror: - cmd.extend(["-l", "5"]) # Default depth limit - - # No parent directory traversal (safety) - if no_parent: - cmd.append("--no-parent") - - # Continue partial downloads - if continue_download: - cmd.append("-c") - - # Accept/reject patterns - if accept: - cmd.extend(["-A", accept]) - if reject: - cmd.extend(["-R", reject]) - - # Domain restrictions - if domains: - cmd.extend(["-D", domains]) - - # Output location - if output: - if recursive or mirror: - cmd.extend(["-P", output]) # Directory for recursive - else: - cmd.extend(["-O", output]) # File for single download - - # Timeout - actual_timeout = timeout or (300 if (mirror or recursive) else 60) - cmd.extend(["--timeout", str(min(actual_timeout, 30))]) # Per-request timeout - cmd.extend(["--tries", "3"]) # Retry count - cmd.extend(["--waitretry", "1"]) # Wait between retries - - # Progress - if quiet: - cmd.append("-q") - else: - cmd.append("--progress=dot:mega") # Show progress for large files - - # User agent (avoid blocks) - cmd.extend( - [ - "--user-agent", - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36", - ] - ) - - # URL (last) - cmd.append(url) - - try: - # Check if output is a directory (non-blocking) - cwd = None - if output: - loop = asyncio.get_event_loop() - is_dir = await loop.run_in_executor(None, os.path.isdir, output) - if is_dir: - cwd = output - - proc = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - cwd=cwd, - ) - - stdout, stderr = await asyncio.wait_for( - proc.communicate(), - timeout=actual_timeout + 30, - ) - - output_text = stdout.decode("utf-8", errors="replace") - errors = stderr.decode("utf-8", errors="replace") - - # Combine stdout and stderr (wget uses stderr for progress) - combined = f"{errors}\n{output_text}".strip() - - if proc.returncode != 0: - return f"wget failed (exit {proc.returncode}):\n{combined}" - - # Summarize success - lines = combined.split("\n") - summary_lines = [ - l - for l in lines - if any( - x in l.lower() - for x in ["saved", "downloaded", "finished", "total", "retrieved"] - ) - ] - - if summary_lines: - return "Download complete:\n" + "\n".join(summary_lines[-5:]) - return f"Download complete:\n{combined[-500:]}" - - except asyncio.TimeoutError: - return f"Download timed out after {actual_timeout}s" - except FileNotFoundError: - return "Error: wget not found. Install wget: brew install wget" - except Exception as e: - return f"Error: {e}" - - def register(self, mcp_server: FastMCP) -> None: - """Register with MCP server.""" - tool_instance = self - - @mcp_server.tool() - async def wget( - url: Annotated[str, Field(description="URL to download")], - output: Annotated[ - Optional[str], Field(description="Output file or directory") - ] = None, - mirror: Annotated[ - bool, Field(description="Mirror site for offline viewing") - ] = False, - recursive: Annotated[ - bool, Field(description="Download recursively") - ] = False, - depth: Annotated[ - Optional[int], Field(description="Max recursion depth") - ] = None, - accept: Annotated[ - Optional[str], Field(description="Accept pattern (e.g. '*.pdf')") - ] = None, - reject: Annotated[ - Optional[str], Field(description="Reject pattern") - ] = None, - domains: Annotated[ - Optional[str], Field(description="Limit to domains") - ] = None, - no_parent: Annotated[ - bool, Field(description="Don't go to parent directories") - ] = True, - continue_download: Annotated[ - bool, Field(description="Continue partial downloads") - ] = False, - timeout: Annotated[ - Optional[int], Field(description="Timeout in seconds") - ] = None, - quiet: Annotated[bool, Field(description="Suppress output")] = False, - ctx: MCPContext = None, - ) -> str: - """Download files and mirror websites reliably. - - Supports single file downloads and full site mirroring. - Handles retries, timeouts, and link conversion automatically. - """ - return await tool_instance.call( - ctx, - url=url, - output=output, - mirror=mirror, - recursive=recursive, - depth=depth, - accept=accept, - reject=reject, - domains=domains, - no_parent=no_parent, - continue_download=continue_download, - timeout=timeout, - quiet=quiet, - ) diff --git a/pkg/hanzo-tools-shell/pyproject.toml b/pkg/hanzo-tools-shell/pyproject.toml deleted file mode 100644 index 9d0cabc0f..000000000 --- a/pkg/hanzo-tools-shell/pyproject.toml +++ /dev/null @@ -1,31 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-shell" -version = "0.6.5" -description = "Shell/command execution tools with auto-detection - only exposes your active shell (zsh, bash, fish, dash, ksh, tcsh, csh)" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "tools", "shell", "mcp", "ai", "dag", "zsh"] -dependencies = [ - "hanzo-tools>=0.3.0", - "hanzo-async>=0.1.0", # Unified async I/O with uvloop - "mcp>=1.25.0", - "pydantic>=2.12.5", - "tiktoken>=0.8.0", - "psutil>=6.1.1", -] - -[project.entry-points."hanzo.tools"] -shell = "hanzo_tools.shell:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] - -[tool.setuptools.package-data] -"*" = ["py.typed"] diff --git a/pkg/hanzo-tools-shell/tests/test_ps_tool_v2.py b/pkg/hanzo-tools-shell/tests/test_ps_tool_v2.py deleted file mode 100644 index 7a90a0c0c..000000000 --- a/pkg/hanzo-tools-shell/tests/test_ps_tool_v2.py +++ /dev/null @@ -1,47 +0,0 @@ -import os -import signal - -import psutil -import pytest -from hanzo_tools.shell.ps_tool import PsTool, ps_tool - - -@pytest.mark.asyncio -async def test_ps_list(): - """Test listing processes.""" - # List by CPU - result = await ps_tool.call(None, action="list", sort_by="cpu", limit=10) - assert "PID" in result - assert "COMMAND" in result - assert len(result.splitlines()) > 2 # Header + separator + at least one process - - # List by Memory - result_mem = await ps_tool.call(None, action="list", sort_by="memory", limit=10) - assert "PID" in result_mem - - -@pytest.mark.asyncio -async def test_ps_get_self(): - """Test getting info for the current process.""" - my_pid = os.getpid() - result = await ps_tool.call(None, action="get", pid=my_pid) - assert f"PID: {my_pid}" in result - assert "Status: running" in result or "Status: sleeping" in result - - -@pytest.mark.asyncio -async def test_ps_get_invalid(): - """Test getting info for non-existent process.""" - # Max PID is usually 32768 or 4194304, so 99999999 is likely safe - result = await ps_tool.call(None, action="get", pid=99999999) - assert "not found" in result - - -@pytest.mark.asyncio -async def test_ps_kill_error(): - """Test killing a non-existent process.""" - result = await ps_tool.call(None, action="kill", pid=99999999) - assert "not found" in result - - -# Note: We don't test successful kill to avoid killing random processes or the test runner itself. diff --git a/pkg/hanzo-tools-shell/tests/test_shellflow.py b/pkg/hanzo-tools-shell/tests/test_shellflow.py deleted file mode 100644 index 942ed5670..000000000 --- a/pkg/hanzo-tools-shell/tests/test_shellflow.py +++ /dev/null @@ -1,435 +0,0 @@ -"""Comprehensive tests for Shellflow DSL parser and execution.""" - -import time -import asyncio -from typing import Any - -import pytest -from hanzo_tools.shell.shellflow import ( - parse, - compile, - to_sexp, - _tokenize, - from_sexp, - _normalize, - to_commands, - render_ascii, -) - - -class TestTokenizer: - """Tests for shellflow tokenization.""" - - def test_simple_command(self): - assert _tokenize("ls -la") == ["ls -la"] - - def test_sequential_commands(self): - tokens = _tokenize("A ; B ; C") - assert tokens == ["A", ";", "B", ";", "C"] - - def test_parallel_block(self): - tokens = _tokenize("{ A & B & C }") - assert tokens == ["{ A & B & C }"] - - def test_mixed_syntax(self): - tokens = _tokenize("A ; { B & C } ; D") - assert tokens == ["A", ";", "{ B & C }", ";", "D"] - - def test_nested_braces(self): - tokens = _tokenize("A ; { B ; { C & D } } ; E") - assert tokens == ["A", ";", "{ B ; { C & D } }", ";", "E"] - - def test_empty_input(self): - assert _tokenize("") == [] - assert _tokenize(" ") == [] - - def test_commands_with_semicolons_in_strings(self): - # Commands containing semicolons in quoted strings - tokens = _tokenize('echo "a;b" ; echo c') - assert len(tokens) == 3 # echo "a;b", ;, echo c - - def test_commands_with_pipes(self): - tokens = _tokenize("cat file | grep pattern ; echo done") - assert tokens == ["cat file | grep pattern", ";", "echo done"] - - def test_commands_with_and_or(self): - tokens = _tokenize("cmd1 && cmd2 || cmd3 ; cmd4") - assert tokens == ["cmd1 && cmd2 || cmd3", ";", "cmd4"] - - -class TestParser: - """Tests for shellflow parsing to AST.""" - - def test_simple_command(self): - ast = parse("ls -la") - assert ast == {"type": "do", "steps": ["ls -la"]} - - def test_sequential(self): - ast = parse("A ; B ; C") - assert ast == {"type": "do", "steps": ["A", "B", "C"]} - - def test_parallel(self): - ast = parse("{ A & B & C }") - assert ast == {"type": "all", "steps": ["A", "B", "C"]} - - def test_mixed_dag(self): - ast = parse("setup ; { task1 & task2 } ; cleanup") - assert ast == { - "type": "do", - "steps": [ - "setup", - {"type": "all", "steps": ["task1", "task2"]}, - "cleanup", - ], - } - - def test_complex_dag(self): - ast = parse("A ; { B & C & D } ; { E & F } ; G") - assert ast == { - "type": "do", - "steps": [ - "A", - {"type": "all", "steps": ["B", "C", "D"]}, - {"type": "all", "steps": ["E", "F"]}, - "G", - ], - } - - def test_empty_input(self): - ast = parse("") - assert ast == {"type": "do", "steps": []} - - def test_whitespace_handling(self): - ast = parse(" A ; B ; C ") - assert ast == {"type": "do", "steps": ["A", "B", "C"]} - - def test_single_parallel_task(self): - ast = parse("{ A }") - # Single item in parallel should be normalized - assert ast["steps"] == ["A"] or ast == "A" - - def test_real_world_example(self): - src = "mkdir -p dist ; { cp a.txt dist/ & cp b.txt dist/ & cp c.txt dist/ } ; zip -r out.zip dist/" - ast = parse(src) - assert ast["type"] == "do" - assert len(ast["steps"]) == 3 - assert ast["steps"][0] == "mkdir -p dist" - assert ast["steps"][1]["type"] == "all" - assert len(ast["steps"][1]["steps"]) == 3 - assert ast["steps"][2] == "zip -r out.zip dist/" - - -class TestNormalization: - """Tests for AST normalization.""" - - def test_flatten_nested_do(self): - ast = {"type": "do", "steps": [{"type": "do", "steps": ["A", "B"]}, "C"]} - normalized = _normalize(ast) - assert normalized == {"type": "do", "steps": ["A", "B", "C"]} - - def test_flatten_nested_all(self): - ast = {"type": "all", "steps": [{"type": "all", "steps": ["A", "B"]}, "C"]} - normalized = _normalize(ast) - assert normalized == {"type": "all", "steps": ["A", "B", "C"]} - - def test_singleton_do(self): - ast = {"type": "do", "steps": ["A"]} - normalized = _normalize(ast) - # Singleton should be kept as-is for consistency - assert "A" in str(normalized) - - def test_empty_steps(self): - ast = {"type": "do", "steps": []} - normalized = _normalize(ast) - assert normalized == {"type": "do", "steps": []} - - def test_deeply_nested(self): - ast = { - "type": "do", - "steps": [ - {"type": "do", "steps": [{"type": "do", "steps": ["A"]}]}, - "B", - ], - } - normalized = _normalize(ast) - assert "A" in str(normalized) - assert "B" in str(normalized) - - -class TestSexpConversion: - """Tests for S-expression conversion.""" - - def test_to_sexp_simple(self): - ast = {"type": "do", "steps": ["A", "B", "C"]} - sexp = to_sexp(ast) - assert sexp == ["do", "A", "B", "C"] - - def test_to_sexp_nested(self): - ast = { - "type": "do", - "steps": ["A", {"type": "all", "steps": ["B", "C"]}, "D"], - } - sexp = to_sexp(ast) - assert sexp == ["do", "A", ["all", "B", "C"], "D"] - - def test_from_sexp_simple(self): - sexp = ["do", "A", "B", "C"] - ast = from_sexp(sexp) - assert ast == {"type": "do", "steps": ["A", "B", "C"]} - - def test_from_sexp_nested(self): - sexp = ["do", "A", ["all", "B", "C"], "D"] - ast = from_sexp(sexp) - assert ast == { - "type": "do", - "steps": ["A", {"type": "all", "steps": ["B", "C"]}, "D"], - } - - def test_roundtrip(self): - original = { - "type": "do", - "steps": ["A", {"type": "all", "steps": ["B", "C"]}, "D"], - } - sexp = to_sexp(original) - recovered = from_sexp(sexp) - assert recovered == original - - -class TestToCommands: - """Tests for conversion to executor commands format.""" - - def test_sequential(self): - ast = {"type": "do", "steps": ["A", "B", "C"]} - cmds = to_commands(ast) - assert cmds == ["A", "B", "C"] - - def test_parallel(self): - ast = {"type": "all", "steps": ["A", "B", "C"]} - cmds = to_commands(ast) - assert cmds == [["A", "B", "C"]] - - def test_mixed(self): - ast = { - "type": "do", - "steps": ["A", {"type": "all", "steps": ["B", "C"]}, "D"], - } - cmds = to_commands(ast) - assert cmds == ["A", ["B", "C"], "D"] - - def test_complex_dag(self): - ast = { - "type": "do", - "steps": [ - "setup", - {"type": "all", "steps": ["task1", "task2", "task3"]}, - {"type": "all", "steps": ["cleanup1", "cleanup2"]}, - "done", - ], - } - cmds = to_commands(ast) - assert cmds == [ - "setup", - ["task1", "task2", "task3"], - ["cleanup1", "cleanup2"], - "done", - ] - - -class TestCompile: - """Tests for compile convenience function.""" - - def test_compile_to_ast(self): - result = compile("A ; B ; C", format="ast") - assert result == {"type": "do", "steps": ["A", "B", "C"]} - - def test_compile_to_sexp(self): - result = compile("A ; B ; C", format="sexp") - assert result == ["do", "A", "B", "C"] - - def test_compile_to_commands(self): - result = compile("A ; { B & C } ; D", format="commands") - assert result == ["A", ["B", "C"], "D"] - - def test_compile_to_ascii(self): - result = compile("A ; B", format="ascii") - assert "[do]" in result - assert "A" in result - assert "B" in result - - def test_compile_invalid_format(self): - with pytest.raises(ValueError): - compile("A", format="invalid") - - -class TestRenderAscii: - """Tests for ASCII rendering.""" - - def test_simple(self): - ast = {"type": "do", "steps": ["A", "B"]} - output = render_ascii(ast) - assert "[do]" in output - assert "A" in output - assert "B" in output - - def test_nested(self): - ast = { - "type": "do", - "steps": ["A", {"type": "all", "steps": ["B", "C"]}, "D"], - } - output = render_ascii(ast) - assert "[do]" in output - assert "[all]" in output - - -class TestEdgeCases: - """Edge case and error handling tests.""" - - def test_many_sequential(self): - src = " ; ".join([f"cmd{i}" for i in range(100)]) - ast = parse(src) - assert len(ast["steps"]) == 100 - - def test_many_parallel(self): - src = "{ " + " & ".join([f"cmd{i}" for i in range(100)]) + " }" - ast = parse(src) - assert len(ast["steps"]) == 100 - - def test_deeply_nested_parallel(self): - src = "A ; { B & { C & D } } ; E" - ast = parse(src) - # Should handle nested parallel blocks - assert ast["type"] == "do" - - def test_unicode_commands(self): - ast = parse("echo ไฝ ๅฅฝ ; echo ะผะธั€ ; echo ๐Ÿš€") - assert len(ast["steps"]) == 3 - - def test_long_commands(self): - long_cmd = "echo " + "x" * 10000 - ast = parse(long_cmd) - assert long_cmd in ast["steps"][0] or ast["steps"][0] == long_cmd - - -class TestPerformance: - """Performance benchmarks.""" - - def test_parse_speed_simple(self): - """Should parse simple commands very fast.""" - src = "ls -la" - start = time.perf_counter() - for _ in range(10000): - parse(src) - elapsed = time.perf_counter() - start - ops_per_sec = 10000 / elapsed - print(f"\nSimple parse: {ops_per_sec:.0f} ops/sec") - assert ops_per_sec > 10000 # At least 10k ops/sec - - def test_parse_speed_mixed(self): - """Should parse mixed DAGs reasonably fast.""" - src = "mkdir -p dist ; { cp a dist/ & cp b dist/ & cp c dist/ } ; zip out.zip dist/" - start = time.perf_counter() - for _ in range(1000): - parse(src) - elapsed = time.perf_counter() - start - ops_per_sec = 1000 / elapsed - print(f"\nMixed DAG parse: {ops_per_sec:.0f} ops/sec") - assert ops_per_sec > 1000 # At least 1k ops/sec - - def test_compile_speed(self): - """Should compile to commands fast.""" - src = "A ; { B & C & D } ; E" - start = time.perf_counter() - for _ in range(10000): - compile(src, format="commands") - elapsed = time.perf_counter() - start - ops_per_sec = 10000 / elapsed - print(f"\nFull compile: {ops_per_sec:.0f} ops/sec") - assert ops_per_sec > 5000 # At least 5k ops/sec - - def test_large_dag(self): - """Should handle large DAGs efficiently.""" - # 100 sequential with 10 parallel each - parts = [] - for i in range(100): - parallel = "{ " + " & ".join([f"task{i}_{j}" for j in range(10)]) + " }" - parts.append(parallel) - src = " ; ".join(parts) - - start = time.perf_counter() - ast = parse(src) - cmds = to_commands(ast) - elapsed = time.perf_counter() - start - - print(f"\nLarge DAG (100x10): {elapsed * 1000:.2f}ms") - assert elapsed < 0.1 # Should complete in under 100ms - assert len(cmds) == 100 - - -@pytest.mark.asyncio -class TestIntegration: - """Integration tests with actual shell execution.""" - - async def test_execute_sequential(self): - from hanzo_tools.shell import ZshTool - - zsh = ZshTool() - result = await zsh.call(None, command="echo A ; echo B ; echo C") - assert "A" in result - assert "B" in result - assert "C" in result - - async def test_execute_parallel(self): - from hanzo_tools.shell import ZshTool - - zsh = ZshTool() - result = await zsh.call(None, command="{ echo A & echo B & echo C }") - assert "A" in result - assert "B" in result - assert "C" in result - - async def test_execute_mixed(self): - from hanzo_tools.shell import ZshTool - - zsh = ZshTool() - result = await zsh.call( - None, command="echo start ; { echo A & echo B } ; echo end" - ) - assert "start" in result - assert "A" in result - assert "B" in result - assert "end" in result - - async def test_execute_with_bash(self): - from hanzo_tools.shell import BashTool - - bash = BashTool() - # Use proper bash syntax (no zsh-style { } backgrounding) - result = await bash.call( - None, command="echo start && echo A && echo B && echo end" - ) - assert "start" in result - assert "end" in result - - async def test_parallel_execution_time(self): - """Parallel should be faster than sequential for sleep commands.""" - from hanzo_tools.shell import ZshTool - - zsh = ZshTool() - - # Parallel: should take ~0.1s - start = time.perf_counter() - await zsh.call(None, command="{ sleep 0.1 & sleep 0.1 & sleep 0.1 }") - parallel_time = time.perf_counter() - start - - # Sequential: should take ~0.3s - start = time.perf_counter() - await zsh.call(None, command="sleep 0.1 ; sleep 0.1 ; sleep 0.1") - sequential_time = time.perf_counter() - start - - print(f"\nParallel: {parallel_time:.2f}s, Sequential: {sequential_time:.2f}s") - # Parallel should be at least 2x faster - assert parallel_time < sequential_time * 0.7 - - -if __name__ == "__main__": - pytest.main([__file__, "-v", "-s"]) diff --git a/pkg/hanzo-tools-shell/uv.lock b/pkg/hanzo-tools-shell/uv.lock deleted file mode 100644 index e309108fa..000000000 --- a/pkg/hanzo-tools-shell/uv.lock +++ /dev/null @@ -1,1696 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" - -[[package]] -name = "aiofiles" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/c3/534eac40372d8ee36ef40df62ec129bee4fdb5ad9706e58a29be53b2c970/aiofiles-25.1.0.tar.gz", hash = "sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2", size = 46354, upload-time = "2025-10-09T20:51:04.358Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/8a/340a1555ae33d7354dbca4faa54948d76d89a27ceef032c8c3bc661d003e/aiofiles-25.1.0-py3-none-any.whl", hash = "sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695", size = 14668, upload-time = "2025-10-09T20:51:03.174Z" }, -] - -[[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.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, -] - -[[package]] -name = "attrs" -version = "25.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, -] - -[[package]] -name = "authlib" -version = "1.6.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/9b/b1661026ff24bc641b76b78c5222d614776b0c085bcfdac9bd15a1cb4b35/authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e", size = 164894, upload-time = "2025-12-12T08:01:41.464Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/51/321e821856452f7386c4e9df866f196720b1ad0c5ea1623ea7399969ae3b/authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd", size = 244005, upload-time = "2025-12-12T08:01:40.209Z" }, -] - -[[package]] -name = "beartype" -version = "0.22.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, -] - -[[package]] -name = "cachetools" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/af/df70e9b65bc77a1cbe0768c0aa4617147f30f8306ded98c1744bcdc0ae1e/cachetools-7.0.0.tar.gz", hash = "sha256:a9abf18ff3b86c7d05b27ead412e235e16ae045925e531fae38d5fada5ed5b08", size = 35796, upload-time = "2026-02-01T18:59:47.411Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/df/2dd32cce20cbcf6f2ec456b58d44368161ad28320729f64e5e1d5d7bd0ae/cachetools-7.0.0-py3-none-any.whl", hash = "sha256:d52fef60e6e964a1969cfb61ccf6242a801b432790fe520d78720d757c81cbd2", size = 13487, upload-time = "2026-02-01T18:59:45.981Z" }, -] - -[[package]] -name = "certifi" -version = "2026.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, - { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, - { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, - { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, - { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, - { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, - { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, - { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, - { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, -] - -[[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 = "cryptography" -version = "46.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/19/f748958276519adf6a0c1e79e7b8860b4830dda55ccdf29f2719b5fc499c/cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59", size = 749301, upload-time = "2026-01-28T00:24:37.379Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/99/157aae7949a5f30d51fcb1a9851e8ebd5c74bf99b5285d8bb4b8b9ee641e/cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485", size = 7173686, upload-time = "2026-01-28T00:23:07.515Z" }, - { url = "https://files.pythonhosted.org/packages/87/91/874b8910903159043b5c6a123b7e79c4559ddd1896e38967567942635778/cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc", size = 4275871, upload-time = "2026-01-28T00:23:09.439Z" }, - { url = "https://files.pythonhosted.org/packages/c0/35/690e809be77896111f5b195ede56e4b4ed0435b428c2f2b6d35046fbb5e8/cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0", size = 4423124, upload-time = "2026-01-28T00:23:11.529Z" }, - { url = "https://files.pythonhosted.org/packages/1a/5b/a26407d4f79d61ca4bebaa9213feafdd8806dc69d3d290ce24996d3cfe43/cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa", size = 4277090, upload-time = "2026-01-28T00:23:13.123Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d8/4bb7aec442a9049827aa34cee1aa83803e528fa55da9a9d45d01d1bb933e/cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81", size = 4947652, upload-time = "2026-01-28T00:23:14.554Z" }, - { url = "https://files.pythonhosted.org/packages/2b/08/f83e2e0814248b844265802d081f2fac2f1cbe6cd258e72ba14ff006823a/cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255", size = 4455157, upload-time = "2026-01-28T00:23:16.443Z" }, - { url = "https://files.pythonhosted.org/packages/0a/05/19d849cf4096448779d2dcc9bb27d097457dac36f7273ffa875a93b5884c/cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e", size = 3981078, upload-time = "2026-01-28T00:23:17.838Z" }, - { url = "https://files.pythonhosted.org/packages/e6/89/f7bac81d66ba7cde867a743ea5b37537b32b5c633c473002b26a226f703f/cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c", size = 4276213, upload-time = "2026-01-28T00:23:19.257Z" }, - { url = "https://files.pythonhosted.org/packages/da/9f/7133e41f24edd827020ad21b068736e792bc68eecf66d93c924ad4719fb3/cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32", size = 4912190, upload-time = "2026-01-28T00:23:21.244Z" }, - { url = "https://files.pythonhosted.org/packages/a6/f7/6d43cbaddf6f65b24816e4af187d211f0bc536a29961f69faedc48501d8e/cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616", size = 4454641, upload-time = "2026-01-28T00:23:22.866Z" }, - { url = "https://files.pythonhosted.org/packages/9e/4f/ebd0473ad656a0ac912a16bd07db0f5d85184924e14fc88feecae2492834/cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0", size = 4405159, upload-time = "2026-01-28T00:23:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d1/f7/7923886f32dc47e27adeff8246e976d77258fd2aa3efdd1754e4e323bf49/cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0", size = 4666059, upload-time = "2026-01-28T00:23:26.766Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a7/0fca0fd3591dffc297278a61813d7f661a14243dd60f499a7a5b48acb52a/cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5", size = 3026378, upload-time = "2026-01-28T00:23:28.317Z" }, - { url = "https://files.pythonhosted.org/packages/2d/12/652c84b6f9873f0909374864a57b003686c642ea48c84d6c7e2c515e6da5/cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b", size = 3478614, upload-time = "2026-01-28T00:23:30.275Z" }, - { url = "https://files.pythonhosted.org/packages/b9/27/542b029f293a5cce59349d799d4d8484b3b1654a7b9a0585c266e974a488/cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908", size = 7116417, upload-time = "2026-01-28T00:23:31.958Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f5/559c25b77f40b6bf828eabaf988efb8b0e17b573545edb503368ca0a2a03/cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da", size = 4264508, upload-time = "2026-01-28T00:23:34.264Z" }, - { url = "https://files.pythonhosted.org/packages/49/a1/551fa162d33074b660dc35c9bc3616fefa21a0e8c1edd27b92559902e408/cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829", size = 4409080, upload-time = "2026-01-28T00:23:35.793Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/4d8d129a755f5d6df1bbee69ea2f35ebfa954fa1847690d1db2e8bca46a5/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2", size = 4270039, upload-time = "2026-01-28T00:23:37.263Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f5/ed3fcddd0a5e39321e595e144615399e47e7c153a1fb8c4862aec3151ff9/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085", size = 4926748, upload-time = "2026-01-28T00:23:38.884Z" }, - { url = "https://files.pythonhosted.org/packages/43/ae/9f03d5f0c0c00e85ecb34f06d3b79599f20630e4db91b8a6e56e8f83d410/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b", size = 4442307, upload-time = "2026-01-28T00:23:40.56Z" }, - { url = "https://files.pythonhosted.org/packages/8b/22/e0f9f2dae8040695103369cf2283ef9ac8abe4d51f68710bec2afd232609/cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd", size = 3959253, upload-time = "2026-01-28T00:23:42.827Z" }, - { url = "https://files.pythonhosted.org/packages/01/5b/6a43fcccc51dae4d101ac7d378a8724d1ba3de628a24e11bf2f4f43cba4d/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2", size = 4269372, upload-time = "2026-01-28T00:23:44.655Z" }, - { url = "https://files.pythonhosted.org/packages/17/b7/0f6b8c1dd0779df2b526e78978ff00462355e31c0a6f6cff8a3e99889c90/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e", size = 4891908, upload-time = "2026-01-28T00:23:46.48Z" }, - { url = "https://files.pythonhosted.org/packages/83/17/259409b8349aa10535358807a472c6a695cf84f106022268d31cea2b6c97/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f", size = 4441254, upload-time = "2026-01-28T00:23:48.403Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fe/e4a1b0c989b00cee5ffa0764401767e2d1cf59f45530963b894129fd5dce/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82", size = 4396520, upload-time = "2026-01-28T00:23:50.26Z" }, - { url = "https://files.pythonhosted.org/packages/b3/81/ba8fd9657d27076eb40d6a2f941b23429a3c3d2f56f5a921d6b936a27bc9/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c", size = 4651479, upload-time = "2026-01-28T00:23:51.674Z" }, - { url = "https://files.pythonhosted.org/packages/00/03/0de4ed43c71c31e4fe954edd50b9d28d658fef56555eba7641696370a8e2/cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061", size = 3001986, upload-time = "2026-01-28T00:23:53.485Z" }, - { url = "https://files.pythonhosted.org/packages/5c/70/81830b59df7682917d7a10f833c4dab2a5574cd664e86d18139f2b421329/cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7", size = 3468288, upload-time = "2026-01-28T00:23:55.09Z" }, - { url = "https://files.pythonhosted.org/packages/56/f7/f648fdbb61d0d45902d3f374217451385edc7e7768d1b03ff1d0e5ffc17b/cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab", size = 7169583, upload-time = "2026-01-28T00:23:56.558Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cc/8f3224cbb2a928de7298d6ed4790f5ebc48114e02bdc9559196bfb12435d/cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef", size = 4275419, upload-time = "2026-01-28T00:23:58.364Z" }, - { url = "https://files.pythonhosted.org/packages/17/43/4a18faa7a872d00e4264855134ba82d23546c850a70ff209e04ee200e76f/cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d", size = 4419058, upload-time = "2026-01-28T00:23:59.867Z" }, - { url = "https://files.pythonhosted.org/packages/ee/64/6651969409821d791ba12346a124f55e1b76f66a819254ae840a965d4b9c/cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973", size = 4278151, upload-time = "2026-01-28T00:24:01.731Z" }, - { url = "https://files.pythonhosted.org/packages/20/0b/a7fce65ee08c3c02f7a8310cc090a732344066b990ac63a9dfd0a655d321/cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4", size = 4939441, upload-time = "2026-01-28T00:24:03.175Z" }, - { url = "https://files.pythonhosted.org/packages/db/a7/20c5701e2cd3e1dfd7a19d2290c522a5f435dd30957d431dcb531d0f1413/cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af", size = 4451617, upload-time = "2026-01-28T00:24:05.403Z" }, - { url = "https://files.pythonhosted.org/packages/00/dc/3e16030ea9aa47b63af6524c354933b4fb0e352257c792c4deeb0edae367/cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263", size = 3977774, upload-time = "2026-01-28T00:24:06.851Z" }, - { url = "https://files.pythonhosted.org/packages/42/c8/ad93f14118252717b465880368721c963975ac4b941b7ef88f3c56bf2897/cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095", size = 4277008, upload-time = "2026-01-28T00:24:08.926Z" }, - { url = "https://files.pythonhosted.org/packages/00/cf/89c99698151c00a4631fbfcfcf459d308213ac29e321b0ff44ceeeac82f1/cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b", size = 4903339, upload-time = "2026-01-28T00:24:12.009Z" }, - { url = "https://files.pythonhosted.org/packages/03/c3/c90a2cb358de4ac9309b26acf49b2a100957e1ff5cc1e98e6c4996576710/cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019", size = 4451216, upload-time = "2026-01-28T00:24:13.975Z" }, - { url = "https://files.pythonhosted.org/packages/96/2c/8d7f4171388a10208671e181ca43cdc0e596d8259ebacbbcfbd16de593da/cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4", size = 4404299, upload-time = "2026-01-28T00:24:16.169Z" }, - { url = "https://files.pythonhosted.org/packages/e9/23/cbb2036e450980f65c6e0a173b73a56ff3bccd8998965dea5cc9ddd424a5/cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b", size = 4664837, upload-time = "2026-01-28T00:24:17.629Z" }, - { url = "https://files.pythonhosted.org/packages/0a/21/f7433d18fe6d5845329cbdc597e30caf983229c7a245bcf54afecc555938/cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc", size = 3009779, upload-time = "2026-01-28T00:24:20.198Z" }, - { url = "https://files.pythonhosted.org/packages/3a/6a/bd2e7caa2facffedf172a45c1a02e551e6d7d4828658c9a245516a598d94/cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976", size = 3466633, upload-time = "2026-01-28T00:24:21.851Z" }, -] - -[[package]] -name = "cyclopts" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "docstring-parser" }, - { name = "rich" }, - { name = "rich-rst" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d4/93/6085aa89c3fff78a5180987354538d72e43b0db27e66a959302d0c07821a/cyclopts-4.5.1.tar.gz", hash = "sha256:fadc45304763fd9f5d6033727f176898d17a1778e194436964661a005078a3dd", size = 162075, upload-time = "2026-01-25T15:23:54.07Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/7c/996760c30f1302704af57c66ff2d723f7d656d0d0b93563b5528a51484bb/cyclopts-4.5.1-py3-none-any.whl", hash = "sha256:0642c93601e554ca6b7b9abd81093847ea4448b2616280f2a0952416574e8c7a", size = 199807, upload-time = "2026-01-25T15:23:55.219Z" }, -] - -[[package]] -name = "diskcache" -version = "5.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, -] - -[[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.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, -] - -[[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.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "fakeredis" -version = "2.33.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "redis" }, - { name = "sortedcontainers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/f9/57464119936414d60697fcbd32f38909bb5688b616ae13de6e98384433e0/fakeredis-2.33.0.tar.gz", hash = "sha256:d7bc9a69d21df108a6451bbffee23b3eba432c21a654afc7ff2d295428ec5770", size = 175187, upload-time = "2025-12-16T19:45:52.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/78/a850fed8aeef96d4a99043c90b818b2ed5419cd5b24a4049fd7cfb9f1471/fakeredis-2.33.0-py3-none-any.whl", hash = "sha256:de535f3f9ccde1c56672ab2fdd6a8efbc4f2619fc2f1acc87b8737177d71c965", size = 119605, upload-time = "2025-12-16T19:45:51.08Z" }, -] - -[package.optional-dependencies] -lua = [ - { name = "lupa" }, -] - -[[package]] -name = "fastmcp" -version = "2.14.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "authlib" }, - { name = "cyclopts" }, - { name = "exceptiongroup" }, - { name = "httpx" }, - { name = "jsonref" }, - { name = "jsonschema-path" }, - { name = "mcp" }, - { name = "openapi-pydantic" }, - { name = "packaging" }, - { name = "platformdirs" }, - { name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] }, - { name = "pydantic", extra = ["email"] }, - { name = "pydocket" }, - { name = "pyperclip" }, - { name = "python-dotenv" }, - { name = "rich" }, - { name = "uvicorn" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/a9/a57d5e5629ebd4ef82b495a7f8e346ce29ef80cc86b15c8c40570701b94d/fastmcp-2.14.4.tar.gz", hash = "sha256:c01f19845c2adda0a70d59525c9193be64a6383014c8d40ce63345ac664053ff", size = 8302239, upload-time = "2026-01-22T17:29:37.024Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/41/c4d407e2218fd60d84acb6cc5131d28ff876afecf325e3fd9d27b8318581/fastmcp-2.14.4-py3-none-any.whl", hash = "sha256:5858cff5e4c8ea8107f9bca2609d71d6256e0fce74495912f6e51625e466c49a", size = 417788, upload-time = "2026-01-22T17:29:35.159Z" }, -] - -[[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 = "hanzo-async" -version = "0.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiofiles" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/be/1b/dcd448eac4461973442bc20dd25189360e85ed7ae1c78fafbfce6d88ffc2/hanzo_async-0.1.1.tar.gz", hash = "sha256:05d41974823d27d3557db791705095a49f1cefc901c6ec686120bb9bbd85f0ee", size = 8619, upload-time = "2026-01-05T01:19:19.37Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/70/363dac59048d70653ce0d07ff37d5521f49b5b6ce30d6aba8c31bf31154f/hanzo_async-0.1.1-py3-none-any.whl", hash = "sha256:8f551b7b57e96b4f4c5b7e05d7781eb154a7a788c824c7a36bd69f607532cda2", size = 8649, upload-time = "2026-01-05T01:19:18.268Z" }, -] - -[[package]] -name = "hanzo-tools" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/3e/2d94dc54e202bdb11f6e4597dd68eebc554d2b92fffb4f6918cdf3f91fe2/hanzo_tools-0.3.0.tar.gz", hash = "sha256:d00cb3212a707e22f9bb5a21f0f9eb34a74f22ff2b5f24e2f8b6321f9880e2fb", size = 10929, upload-time = "2025-12-27T18:56:38.843Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/07/6ebbcf371aafa5f2d171de2916ef92c73978b927b34a8863af53e1b1a80b/hanzo_tools-0.3.0-py3-none-any.whl", hash = "sha256:c7b0f6f7c3089f06329bc1aaca39fbce4b7108fdbd048e2bbc450a3aff9941f2", size = 11928, upload-time = "2025-12-27T18:56:37.528Z" }, -] - -[[package]] -name = "hanzo-tools-shell" -version = "0.6.2" -source = { editable = "." } -dependencies = [ - { name = "hanzo-async" }, - { name = "hanzo-tools" }, - { name = "mcp" }, - { name = "psutil" }, - { name = "pydantic" }, - { name = "tiktoken" }, -] - -[package.metadata] -requires-dist = [ - { name = "hanzo-async", specifier = ">=0.1.0" }, - { name = "hanzo-tools", specifier = ">=0.3.0" }, - { name = "mcp", specifier = ">=1.25.0" }, - { name = "psutil", specifier = ">=6.1.1" }, - { name = "pydantic", specifier = ">=2.12.5" }, - { name = "tiktoken", specifier = ">=0.8.0" }, -] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, -] - -[[package]] -name = "jaraco-context" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" }, -] - -[[package]] -name = "jaraco-functools" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, -] - -[[package]] -name = "jsonref" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[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.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "keyring" -version = "25.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, -] - -[[package]] -name = "lupa" -version = "2.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/1c/191c3e6ec6502e3dbe25a53e27f69a5daeac3e56de1f73c0138224171ead/lupa-2.6.tar.gz", hash = "sha256:9a770a6e89576be3447668d7ced312cd6fd41d3c13c2462c9dc2c2ab570e45d9", size = 7240282, upload-time = "2025-10-24T07:20:29.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/86/ce243390535c39d53ea17ccf0240815e6e457e413e40428a658ea4ee4b8d/lupa-2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47ce718817ef1cc0c40d87c3d5ae56a800d61af00fbc0fad1ca9be12df2f3b56", size = 951707, upload-time = "2025-10-24T07:18:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/cedea5e6cbeb54396fdcc55f6b741696f3f036d23cfaf986d50d680446da/lupa-2.6-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:7aba985b15b101495aa4b07112cdc08baa0c545390d560ad5cfde2e9e34f4d58", size = 1916703, upload-time = "2025-10-24T07:18:05.6Z" }, - { url = "https://files.pythonhosted.org/packages/24/be/3d6b5f9a8588c01a4d88129284c726017b2089f3a3fd3ba8bd977292fea0/lupa-2.6-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:b766f62f95b2739f2248977d29b0722e589dcf4f0ccfa827ccbd29f0148bd2e5", size = 985152, upload-time = "2025-10-24T07:18:08.561Z" }, - { url = "https://files.pythonhosted.org/packages/eb/23/9f9a05beee5d5dce9deca4cb07c91c40a90541fc0a8e09db4ee670da550f/lupa-2.6-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:00a934c23331f94cb51760097ebfab14b005d55a6b30a2b480e3c53dd2fa290d", size = 1159599, upload-time = "2025-10-24T07:18:10.346Z" }, - { url = "https://files.pythonhosted.org/packages/40/4e/e7c0583083db9d7f1fd023800a9767d8e4391e8330d56c2373d890ac971b/lupa-2.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21de9f38bd475303e34a042b7081aabdf50bd9bafd36ce4faea2f90fd9f15c31", size = 1038686, upload-time = "2025-10-24T07:18:12.112Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/5a4f7d959d4feba5e203ff0c31889e74d1ca3153122be4a46dca7d92bf7c/lupa-2.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf3bda96d3fc41237e964a69c23647d50d4e28421111360274d4799832c560e9", size = 2071956, upload-time = "2025-10-24T07:18:14.572Z" }, - { url = "https://files.pythonhosted.org/packages/92/34/2f4f13ca65d01169b1720176aedc4af17bc19ee834598c7292db232cb6dc/lupa-2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a76ead245da54801a81053794aa3975f213221f6542d14ec4b859ee2e7e0323", size = 1057199, upload-time = "2025-10-24T07:18:16.379Z" }, - { url = "https://files.pythonhosted.org/packages/35/2a/5f7d2eebec6993b0dcd428e0184ad71afb06a45ba13e717f6501bfed1da3/lupa-2.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8dd0861741caa20886ddbda0a121d8e52fb9b5bb153d82fa9bba796962bf30e8", size = 1173693, upload-time = "2025-10-24T07:18:18.153Z" }, - { url = "https://files.pythonhosted.org/packages/e4/29/089b4d2f8e34417349af3904bb40bec40b65c8731f45e3fd8d497ca573e5/lupa-2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:239e63948b0b23023f81d9a19a395e768ed3da6a299f84e7963b8f813f6e3f9c", size = 2164394, upload-time = "2025-10-24T07:18:20.403Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1b/79c17b23c921f81468a111cad843b076a17ef4b684c4a8dff32a7969c3f0/lupa-2.6-cp312-cp312-win32.whl", hash = "sha256:325894e1099499e7a6f9c351147661a2011887603c71086d36fe0f964d52d1ce", size = 1420647, upload-time = "2025-10-24T07:18:23.368Z" }, - { url = "https://files.pythonhosted.org/packages/b8/15/5121e68aad3584e26e1425a5c9a79cd898f8a152292059e128c206ee817c/lupa-2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c735a1ce8ee60edb0fe71d665f1e6b7c55c6021f1d340eb8c865952c602cd36f", size = 1688529, upload-time = "2025-10-24T07:18:25.523Z" }, - { url = "https://files.pythonhosted.org/packages/28/1d/21176b682ca5469001199d8b95fa1737e29957a3d185186e7a8b55345f2e/lupa-2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:663a6e58a0f60e7d212017d6678639ac8df0119bc13c2145029dcba084391310", size = 947232, upload-time = "2025-10-24T07:18:27.878Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4c/d327befb684660ca13cf79cd1f1d604331808f9f1b6fb6bf57832f8edf80/lupa-2.6-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:d1f5afda5c20b1f3217a80e9bc1b77037f8a6eb11612fd3ada19065303c8f380", size = 1908625, upload-time = "2025-10-24T07:18:29.944Z" }, - { url = "https://files.pythonhosted.org/packages/66/8e/ad22b0a19454dfd08662237a84c792d6d420d36b061f239e084f29d1a4f3/lupa-2.6-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:26f2b3c085fe76e9119e48c1013c1cccdc1f51585d456858290475aa38e7089e", size = 981057, upload-time = "2025-10-24T07:18:31.553Z" }, - { url = "https://files.pythonhosted.org/packages/5c/48/74859073ab276bd0566c719f9ca0108b0cfc1956ca0d68678d117d47d155/lupa-2.6-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:60d2f902c7b96fb8ab98493dcff315e7bb4d0b44dc9dd76eb37de575025d5685", size = 1156227, upload-time = "2025-10-24T07:18:33.981Z" }, - { url = "https://files.pythonhosted.org/packages/09/6c/0e9ded061916877253c2266074060eb71ed99fb21d73c8c114a76725bce2/lupa-2.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a02d25dee3a3250967c36590128d9220ae02f2eda166a24279da0b481519cbff", size = 1035752, upload-time = "2025-10-24T07:18:36.32Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ef/f8c32e454ef9f3fe909f6c7d57a39f950996c37a3deb7b391fec7903dab7/lupa-2.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eae1ee16b886b8914ff292dbefbf2f48abfbdee94b33a88d1d5475e02423203", size = 2069009, upload-time = "2025-10-24T07:18:38.072Z" }, - { url = "https://files.pythonhosted.org/packages/53/dc/15b80c226a5225815a890ee1c11f07968e0aba7a852df41e8ae6fe285063/lupa-2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0edd5073a4ee74ab36f74fe61450148e6044f3952b8d21248581f3c5d1a58be", size = 1056301, upload-time = "2025-10-24T07:18:40.165Z" }, - { url = "https://files.pythonhosted.org/packages/31/14/2086c1425c985acfb30997a67e90c39457122df41324d3c179d6ee2292c6/lupa-2.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c53ee9f22a8a17e7d4266ad48e86f43771951797042dd51d1494aaa4f5f3f0a", size = 1170673, upload-time = "2025-10-24T07:18:42.426Z" }, - { url = "https://files.pythonhosted.org/packages/10/e5/b216c054cf86576c0191bf9a9f05de6f7e8e07164897d95eea0078dca9b2/lupa-2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de7c0f157a9064a400d828789191a96da7f4ce889969a588b87ec80de9b14772", size = 2162227, upload-time = "2025-10-24T07:18:46.112Z" }, - { url = "https://files.pythonhosted.org/packages/59/2f/33ecb5bedf4f3bc297ceacb7f016ff951331d352f58e7e791589609ea306/lupa-2.6-cp313-cp313-win32.whl", hash = "sha256:ee9523941ae0a87b5b703417720c5d78f72d2f5bc23883a2ea80a949a3ed9e75", size = 1419558, upload-time = "2025-10-24T07:18:48.371Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b4/55e885834c847ea610e111d87b9ed4768f0afdaeebc00cd46810f25029f6/lupa-2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b1335a5835b0a25ebdbc75cf0bda195e54d133e4d994877ef025e218c2e59db9", size = 1683424, upload-time = "2025-10-24T07:18:50.976Z" }, - { url = "https://files.pythonhosted.org/packages/66/9d/d9427394e54d22a35d1139ef12e845fd700d4872a67a34db32516170b746/lupa-2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dcb6d0a3264873e1653bc188499f48c1fb4b41a779e315eba45256cfe7bc33c1", size = 953818, upload-time = "2025-10-24T07:18:53.378Z" }, - { url = "https://files.pythonhosted.org/packages/10/41/27bbe81953fb2f9ecfced5d9c99f85b37964cfaf6aa8453bb11283983721/lupa-2.6-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:a37e01f2128f8c36106726cb9d360bac087d58c54b4522b033cc5691c584db18", size = 1915850, upload-time = "2025-10-24T07:18:55.259Z" }, - { url = "https://files.pythonhosted.org/packages/a3/98/f9ff60db84a75ba8725506bbf448fb085bc77868a021998ed2a66d920568/lupa-2.6-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:458bd7e9ff3c150b245b0fcfbb9bd2593d1152ea7f0a7b91c1d185846da033fe", size = 982344, upload-time = "2025-10-24T07:18:57.05Z" }, - { url = "https://files.pythonhosted.org/packages/41/f7/f39e0f1c055c3b887d86b404aaf0ca197b5edfd235a8b81b45b25bac7fc3/lupa-2.6-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:052ee82cac5206a02df77119c325339acbc09f5ce66967f66a2e12a0f3211cad", size = 1156543, upload-time = "2025-10-24T07:18:59.251Z" }, - { url = "https://files.pythonhosted.org/packages/9e/9c/59e6cffa0d672d662ae17bd7ac8ecd2c89c9449dee499e3eb13ca9cd10d9/lupa-2.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96594eca3c87dd07938009e95e591e43d554c1dbd0385be03c100367141db5a8", size = 1047974, upload-time = "2025-10-24T07:19:01.449Z" }, - { url = "https://files.pythonhosted.org/packages/23/c6/a04e9cef7c052717fcb28fb63b3824802488f688391895b618e39be0f684/lupa-2.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8faddd9d198688c8884091173a088a8e920ecc96cda2ffed576a23574c4b3f6", size = 2073458, upload-time = "2025-10-24T07:19:03.369Z" }, - { url = "https://files.pythonhosted.org/packages/e6/10/824173d10f38b51fc77785228f01411b6ca28826ce27404c7c912e0e442c/lupa-2.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:daebb3a6b58095c917e76ba727ab37b27477fb926957c825205fbda431552134", size = 1067683, upload-time = "2025-10-24T07:19:06.2Z" }, - { url = "https://files.pythonhosted.org/packages/b6/dc/9692fbcf3c924d9c4ece2d8d2f724451ac2e09af0bd2a782db1cef34e799/lupa-2.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f3154e68972befe0f81564e37d8142b5d5d79931a18309226a04ec92487d4ea3", size = 1171892, upload-time = "2025-10-24T07:19:08.544Z" }, - { url = "https://files.pythonhosted.org/packages/84/ff/e318b628d4643c278c96ab3ddea07fc36b075a57383c837f5b11e537ba9d/lupa-2.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e4dadf77b9fedc0bfa53417cc28dc2278a26d4cbd95c29f8927ad4d8fe0a7ef9", size = 2166641, upload-time = "2025-10-24T07:19:10.485Z" }, - { url = "https://files.pythonhosted.org/packages/12/f7/a6f9ec2806cf2d50826980cdb4b3cffc7691dc6f95e13cc728846d5cb793/lupa-2.6-cp314-cp314-win32.whl", hash = "sha256:cb34169c6fa3bab3e8ac58ca21b8a7102f6a94b6a5d08d3636312f3f02fafd8f", size = 1456857, upload-time = "2025-10-24T07:19:37.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/de/df71896f25bdc18360fdfa3b802cd7d57d7fede41a0e9724a4625b412c85/lupa-2.6-cp314-cp314-win_amd64.whl", hash = "sha256:b74f944fe46c421e25d0f8692aef1e842192f6f7f68034201382ac440ef9ea67", size = 1731191, upload-time = "2025-10-24T07:19:40.281Z" }, - { url = "https://files.pythonhosted.org/packages/47/3c/a1f23b01c54669465f5f4c4083107d496fbe6fb45998771420e9aadcf145/lupa-2.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0e21b716408a21ab65723f8841cf7f2f37a844b7a965eeabb785e27fca4099cf", size = 999343, upload-time = "2025-10-24T07:19:12.519Z" }, - { url = "https://files.pythonhosted.org/packages/c5/6d/501994291cb640bfa2ccf7f554be4e6914afa21c4026bd01bff9ca8aac57/lupa-2.6-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:589db872a141bfff828340079bbdf3e9a31f2689f4ca0d88f97d9e8c2eae6142", size = 2000730, upload-time = "2025-10-24T07:19:14.869Z" }, - { url = "https://files.pythonhosted.org/packages/53/a5/457ffb4f3f20469956c2d4c4842a7675e884efc895b2f23d126d23e126cc/lupa-2.6-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:cd852a91a4a9d4dcbb9a58100f820a75a425703ec3e3f049055f60b8533b7953", size = 1021553, upload-time = "2025-10-24T07:19:17.123Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/36bb5a5d0960f2a5c7c700e0819abb76fd9bf9c1d8a66e5106416d6e9b14/lupa-2.6-cp314-cp314t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:0334753be028358922415ca97a64a3048e4ed155413fc4eaf87dd0a7e2752983", size = 1133275, upload-time = "2025-10-24T07:19:20.51Z" }, - { url = "https://files.pythonhosted.org/packages/19/86/202ff4429f663013f37d2229f6176ca9f83678a50257d70f61a0a97281bf/lupa-2.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:661d895cd38c87658a34780fac54a690ec036ead743e41b74c3fb81a9e65a6aa", size = 1038441, upload-time = "2025-10-24T07:19:22.509Z" }, - { url = "https://files.pythonhosted.org/packages/a7/42/d8125f8e420714e5b52e9c08d88b5329dfb02dcca731b4f21faaee6cc5b5/lupa-2.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aa58454ccc13878cc177c62529a2056be734da16369e451987ff92784994ca7", size = 2058324, upload-time = "2025-10-24T07:19:24.979Z" }, - { url = "https://files.pythonhosted.org/packages/2b/2c/47bf8b84059876e877a339717ddb595a4a7b0e8740bacae78ba527562e1c/lupa-2.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1425017264e470c98022bba8cff5bd46d054a827f5df6b80274f9cc71dafd24f", size = 1060250, upload-time = "2025-10-24T07:19:27.262Z" }, - { url = "https://files.pythonhosted.org/packages/c2/06/d88add2b6406ca1bdec99d11a429222837ca6d03bea42ca75afa169a78cb/lupa-2.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:224af0532d216e3105f0a127410f12320f7c5f1aa0300bdf9646b8d9afb0048c", size = 1151126, upload-time = "2025-10-24T07:19:29.522Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a0/89e6a024c3b4485b89ef86881c9d55e097e7cb0bdb74efb746f2fa6a9a76/lupa-2.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9abb98d5a8fd27c8285302e82199f0e56e463066f88f619d6594a450bf269d80", size = 2153693, upload-time = "2025-10-24T07:19:31.379Z" }, - { url = "https://files.pythonhosted.org/packages/b6/36/a0f007dc58fc1bbf51fb85dcc82fcb1f21b8c4261361de7dab0e3d8521ef/lupa-2.6-cp314-cp314t-win32.whl", hash = "sha256:1849efeba7a8f6fb8aa2c13790bee988fd242ae404bd459509640eeea3d1e291", size = 1590104, upload-time = "2025-10-24T07:19:33.514Z" }, - { url = "https://files.pythonhosted.org/packages/7d/5e/db903ce9cf82c48d6b91bf6d63ae4c8d0d17958939a4e04ba6b9f38b8643/lupa-2.6-cp314-cp314t-win_amd64.whl", hash = "sha256:fc1498d1a4fc028bc521c26d0fad4ca00ed63b952e32fb95949bda76a04bad52", size = 1913818, upload-time = "2025-10-24T07:19:36.039Z" }, -] - -[[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 = "mcp" -version = "1.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyjwt", extra = ["crypto"] }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, -] - -[[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 = "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 = "opentelemetry-api" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, -] - -[[package]] -name = "opentelemetry-exporter-prometheus" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-sdk" }, - { name = "prometheus-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/14/39/7dafa6fff210737267bed35a8855b6ac7399b9e582b8cf1f25f842517012/opentelemetry_exporter_prometheus-0.60b1.tar.gz", hash = "sha256:a4011b46906323f71724649d301b4dc188aaa068852e814f4df38cc76eac616b", size = 14976, upload-time = "2025-12-11T13:32:42.944Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/0d/4be6bf5477a3eb3d917d2f17d3c0b6720cd6cb97898444a61d43cc983f5c/opentelemetry_exporter_prometheus-0.60b1-py3-none-any.whl", hash = "sha256:49f59178de4f4590e3cef0b8b95cf6e071aae70e1f060566df5546fad773b8fd", size = 13019, upload-time = "2025-12-11T13:32:23.974Z" }, -] - -[[package]] -name = "opentelemetry-instrumentation" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "packaging" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/0f/7e6b713ac117c1f5e4e3300748af699b9902a2e5e34c9cf443dde25a01fa/opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a", size = 31706, upload-time = "2025-12-11T13:36:42.515Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096, upload-time = "2025-12-11T13:35:33.067Z" }, -] - -[[package]] -name = "opentelemetry-sdk" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, -] - -[[package]] -name = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[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 = "pathvalidate" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262, upload-time = "2025-06-15T09:07:20.736Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305, upload-time = "2025-06-15T09:07:19.117Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, -] - -[[package]] -name = "prometheus-client" -version = "0.24.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616, upload-time = "2026-01-14T15:26:26.965Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057, upload-time = "2026-01-14T15:26:24.42Z" }, -] - -[[package]] -name = "psutil" -version = "7.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, - { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, - { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, - { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, - { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, - { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, - { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, - { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, - { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, - { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, - { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, - { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, - { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, - { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, - { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, - { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, - { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, - { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, - { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, -] - -[[package]] -name = "py-key-value-aio" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "py-key-value-shared" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/ce/3136b771dddf5ac905cc193b461eb67967cf3979688c6696e1f2cdcde7ea/py_key_value_aio-0.3.0.tar.gz", hash = "sha256:858e852fcf6d696d231266da66042d3355a7f9871650415feef9fca7a6cd4155", size = 50801, upload-time = "2025-11-17T16:50:04.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/10/72f6f213b8f0bce36eff21fda0a13271834e9eeff7f9609b01afdc253c79/py_key_value_aio-0.3.0-py3-none-any.whl", hash = "sha256:1c781915766078bfd608daa769fefb97e65d1d73746a3dfb640460e322071b64", size = 96342, upload-time = "2025-11-17T16:50:03.801Z" }, -] - -[package.optional-dependencies] -disk = [ - { name = "diskcache" }, - { name = "pathvalidate" }, -] -keyring = [ - { name = "keyring" }, -] -memory = [ - { name = "cachetools" }, -] -redis = [ - { name = "redis" }, -] - -[[package]] -name = "py-key-value-shared" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/e4/1971dfc4620a3a15b4579fe99e024f5edd6e0967a71154771a059daff4db/py_key_value_shared-0.3.0.tar.gz", hash = "sha256:8fdd786cf96c3e900102945f92aa1473138ebe960ef49da1c833790160c28a4b", size = 11666, upload-time = "2025-11-17T16:50:06.849Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e4/b8b0a03ece72f47dce2307d36e1c34725b7223d209fc679315ffe6a4e2c3/py_key_value_shared-0.3.0-py3-none-any.whl", hash = "sha256:5b0efba7ebca08bb158b1e93afc2f07d30b8f40c2fc12ce24a4c0d84f42f9298", size = 19560, upload-time = "2025-11-17T16:50:05.954Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[package.optional-dependencies] -email = [ - { name = "email-validator" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, -] - -[[package]] -name = "pydantic-settings" -version = "2.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, -] - -[[package]] -name = "pydocket" -version = "0.16.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cloudpickle" }, - { name = "fakeredis", extra = ["lua"] }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-prometheus" }, - { name = "opentelemetry-instrumentation" }, - { name = "prometheus-client" }, - { name = "py-key-value-aio", extra = ["memory", "redis"] }, - { name = "python-json-logger" }, - { name = "redis" }, - { name = "rich" }, - { name = "typer" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/00/26befe5f58df7cd1aeda4a8d10bc7d1908ffd86b80fd995e57a2a7b3f7bd/pydocket-0.16.6.tar.gz", hash = "sha256:b96c96ad7692827214ed4ff25fcf941ec38371314db5dcc1ae792b3e9d3a0294", size = 299054, upload-time = "2026-01-09T22:09:15.405Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/3f/7483e5a6dc6326b6e0c640619b5c5bd1d6e3c20e54d58f5fb86267cef00e/pydocket-0.16.6-py3-none-any.whl", hash = "sha256:683d21e2e846aa5106274e7d59210331b242d7fb0dce5b08d3b82065663ed183", size = 67697, upload-time = "2026-01-09T22:09:13.436Z" }, -] - -[[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 = "pyjwt" -version = "2.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, -] - -[package.optional-dependencies] -crypto = [ - { name = "cryptography" }, -] - -[[package]] -name = "pyperclip" -version = "1.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, -] - -[[package]] -name = "python-json-logger" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, -] - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { 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 = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "redis" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/c8/983d5c6579a411d8a99bc5823cc5712768859b5ce2c8afe1a65b37832c81/redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c", size = 4796669, upload-time = "2025-11-19T15:54:39.961Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/f0/8956f8a86b20d7bb9d6ac0187cf4cd54d8065bc9a1a09eb8011d4d326596/redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b", size = 354159, upload-time = "2025-11-19T15:54:38.064Z" }, -] - -[[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 = "regex" -version = "2026.1.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/86/07d5056945f9ec4590b518171c4254a5925832eb727b56d3c38a7476f316/regex-2026.1.15.tar.gz", hash = "sha256:164759aa25575cbc0651bef59a0b18353e54300d79ace8084c818ad8ac72b7d5", size = 414811, upload-time = "2026-01-14T23:18:02.775Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/81/10d8cf43c807d0326efe874c1b79f22bfb0fb226027b0b19ebc26d301408/regex-2026.1.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4c8fcc5793dde01641a35905d6731ee1548f02b956815f8f1cab89e515a5bdf1", size = 489398, upload-time = "2026-01-14T23:14:43.741Z" }, - { url = "https://files.pythonhosted.org/packages/90/b0/7c2a74e74ef2a7c32de724658a69a862880e3e4155cba992ba04d1c70400/regex-2026.1.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bfd876041a956e6a90ad7cdb3f6a630c07d491280bfeed4544053cd434901681", size = 291339, upload-time = "2026-01-14T23:14:45.183Z" }, - { url = "https://files.pythonhosted.org/packages/19/4d/16d0773d0c818417f4cc20aa0da90064b966d22cd62a8c46765b5bd2d643/regex-2026.1.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9250d087bc92b7d4899ccd5539a1b2334e44eee85d848c4c1aef8e221d3f8c8f", size = 289003, upload-time = "2026-01-14T23:14:47.25Z" }, - { url = "https://files.pythonhosted.org/packages/c6/e4/1fc4599450c9f0863d9406e944592d968b8d6dfd0d552a7d569e43bceada/regex-2026.1.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8a154cf6537ebbc110e24dabe53095e714245c272da9c1be05734bdad4a61aa", size = 798656, upload-time = "2026-01-14T23:14:48.77Z" }, - { url = "https://files.pythonhosted.org/packages/b2/e6/59650d73a73fa8a60b3a590545bfcf1172b4384a7df2e7fe7b9aab4e2da9/regex-2026.1.15-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8050ba2e3ea1d8731a549e83c18d2f0999fbc99a5f6bd06b4c91449f55291804", size = 864252, upload-time = "2026-01-14T23:14:50.528Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ab/1d0f4d50a1638849a97d731364c9a80fa304fec46325e48330c170ee8e80/regex-2026.1.15-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf065240704cb8951cc04972cf107063917022511273e0969bdb34fc173456c", size = 912268, upload-time = "2026-01-14T23:14:52.952Z" }, - { url = "https://files.pythonhosted.org/packages/dd/df/0d722c030c82faa1d331d1921ee268a4e8fb55ca8b9042c9341c352f17fa/regex-2026.1.15-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c32bef3e7aeee75746748643667668ef941d28b003bfc89994ecf09a10f7a1b5", size = 803589, upload-time = "2026-01-14T23:14:55.182Z" }, - { url = "https://files.pythonhosted.org/packages/66/23/33289beba7ccb8b805c6610a8913d0131f834928afc555b241caabd422a9/regex-2026.1.15-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d5eaa4a4c5b1906bd0d2508d68927f15b81821f85092e06f1a34a4254b0e1af3", size = 775700, upload-time = "2026-01-14T23:14:56.707Z" }, - { url = "https://files.pythonhosted.org/packages/e7/65/bf3a42fa6897a0d3afa81acb25c42f4b71c274f698ceabd75523259f6688/regex-2026.1.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:86c1077a3cc60d453d4084d5b9649065f3bf1184e22992bd322e1f081d3117fb", size = 787928, upload-time = "2026-01-14T23:14:58.312Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f5/13bf65864fc314f68cdd6d8ca94adcab064d4d39dbd0b10fef29a9da48fc/regex-2026.1.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:2b091aefc05c78d286657cd4db95f2e6313375ff65dcf085e42e4c04d9c8d410", size = 858607, upload-time = "2026-01-14T23:15:00.657Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/040e589834d7a439ee43fb0e1e902bc81bd58a5ba81acffe586bb3321d35/regex-2026.1.15-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:57e7d17f59f9ebfa9667e6e5a1c0127b96b87cb9cede8335482451ed00788ba4", size = 763729, upload-time = "2026-01-14T23:15:02.248Z" }, - { url = "https://files.pythonhosted.org/packages/9b/84/6921e8129687a427edf25a34a5594b588b6d88f491320b9de5b6339a4fcb/regex-2026.1.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c6c4dcdfff2c08509faa15d36ba7e5ef5fcfab25f1e8f85a0c8f45bc3a30725d", size = 850697, upload-time = "2026-01-14T23:15:03.878Z" }, - { url = "https://files.pythonhosted.org/packages/8a/87/3d06143d4b128f4229158f2de5de6c8f2485170c7221e61bf381313314b2/regex-2026.1.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf8ff04c642716a7f2048713ddc6278c5fd41faa3b9cab12607c7abecd012c22", size = 789849, upload-time = "2026-01-14T23:15:06.102Z" }, - { url = "https://files.pythonhosted.org/packages/77/69/c50a63842b6bd48850ebc7ab22d46e7a2a32d824ad6c605b218441814639/regex-2026.1.15-cp312-cp312-win32.whl", hash = "sha256:82345326b1d8d56afbe41d881fdf62f1926d7264b2fc1537f99ae5da9aad7913", size = 266279, upload-time = "2026-01-14T23:15:07.678Z" }, - { url = "https://files.pythonhosted.org/packages/f2/36/39d0b29d087e2b11fd8191e15e81cce1b635fcc845297c67f11d0d19274d/regex-2026.1.15-cp312-cp312-win_amd64.whl", hash = "sha256:4def140aa6156bc64ee9912383d4038f3fdd18fee03a6f222abd4de6357ce42a", size = 277166, upload-time = "2026-01-14T23:15:09.257Z" }, - { url = "https://files.pythonhosted.org/packages/28/32/5b8e476a12262748851fa8ab1b0be540360692325975b094e594dfebbb52/regex-2026.1.15-cp312-cp312-win_arm64.whl", hash = "sha256:c6c565d9a6e1a8d783c1948937ffc377dd5771e83bd56de8317c450a954d2056", size = 270415, upload-time = "2026-01-14T23:15:10.743Z" }, - { url = "https://files.pythonhosted.org/packages/f8/2e/6870bb16e982669b674cce3ee9ff2d1d46ab80528ee6bcc20fb2292efb60/regex-2026.1.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e69d0deeb977ffe7ed3d2e4439360089f9c3f217ada608f0f88ebd67afb6385e", size = 489164, upload-time = "2026-01-14T23:15:13.962Z" }, - { url = "https://files.pythonhosted.org/packages/dc/67/9774542e203849b0286badf67199970a44ebdb0cc5fb739f06e47ada72f8/regex-2026.1.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3601ffb5375de85a16f407854d11cca8fe3f5febbe3ac78fb2866bb220c74d10", size = 291218, upload-time = "2026-01-14T23:15:15.647Z" }, - { url = "https://files.pythonhosted.org/packages/b2/87/b0cda79f22b8dee05f774922a214da109f9a4c0eca5da2c9d72d77ea062c/regex-2026.1.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4c5ef43b5c2d4114eb8ea424bb8c9cec01d5d17f242af88b2448f5ee81caadbc", size = 288895, upload-time = "2026-01-14T23:15:17.788Z" }, - { url = "https://files.pythonhosted.org/packages/3b/6a/0041f0a2170d32be01ab981d6346c83a8934277d82c780d60b127331f264/regex-2026.1.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:968c14d4f03e10b2fd960f1d5168c1f0ac969381d3c1fcc973bc45fb06346599", size = 798680, upload-time = "2026-01-14T23:15:19.342Z" }, - { url = "https://files.pythonhosted.org/packages/58/de/30e1cfcdbe3e891324aa7568b7c968771f82190df5524fabc1138cb2d45a/regex-2026.1.15-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56a5595d0f892f214609c9f76b41b7428bed439d98dc961efafdd1354d42baae", size = 864210, upload-time = "2026-01-14T23:15:22.005Z" }, - { url = "https://files.pythonhosted.org/packages/64/44/4db2f5c5ca0ccd40ff052ae7b1e9731352fcdad946c2b812285a7505ca75/regex-2026.1.15-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf650f26087363434c4e560011f8e4e738f6f3e029b85d4904c50135b86cfa5", size = 912358, upload-time = "2026-01-14T23:15:24.569Z" }, - { url = "https://files.pythonhosted.org/packages/79/b6/e6a5665d43a7c42467138c8a2549be432bad22cbd206f5ec87162de74bd7/regex-2026.1.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18388a62989c72ac24de75f1449d0fb0b04dfccd0a1a7c1c43af5eb503d890f6", size = 803583, upload-time = "2026-01-14T23:15:26.526Z" }, - { url = "https://files.pythonhosted.org/packages/e7/53/7cd478222169d85d74d7437e74750005e993f52f335f7c04ff7adfda3310/regex-2026.1.15-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d220a2517f5893f55daac983bfa9fe998a7dbcaee4f5d27a88500f8b7873788", size = 775782, upload-time = "2026-01-14T23:15:29.352Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b5/75f9a9ee4b03a7c009fe60500fe550b45df94f0955ca29af16333ef557c5/regex-2026.1.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9c08c2fbc6120e70abff5d7f28ffb4d969e14294fb2143b4b5c7d20e46d1714", size = 787978, upload-time = "2026-01-14T23:15:31.295Z" }, - { url = "https://files.pythonhosted.org/packages/72/b3/79821c826245bbe9ccbb54f6eadb7879c722fd3e0248c17bfc90bf54e123/regex-2026.1.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7ef7d5d4bd49ec7364315167a4134a015f61e8266c6d446fc116a9ac4456e10d", size = 858550, upload-time = "2026-01-14T23:15:33.558Z" }, - { url = "https://files.pythonhosted.org/packages/4a/85/2ab5f77a1c465745bfbfcb3ad63178a58337ae8d5274315e2cc623a822fa/regex-2026.1.15-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e42844ad64194fa08d5ccb75fe6a459b9b08e6d7296bd704460168d58a388f3", size = 763747, upload-time = "2026-01-14T23:15:35.206Z" }, - { url = "https://files.pythonhosted.org/packages/6d/84/c27df502d4bfe2873a3e3a7cf1bdb2b9cc10284d1a44797cf38bed790470/regex-2026.1.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cfecdaa4b19f9ca534746eb3b55a5195d5c95b88cac32a205e981ec0a22b7d31", size = 850615, upload-time = "2026-01-14T23:15:37.523Z" }, - { url = "https://files.pythonhosted.org/packages/7d/b7/658a9782fb253680aa8ecb5ccbb51f69e088ed48142c46d9f0c99b46c575/regex-2026.1.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:08df9722d9b87834a3d701f3fca570b2be115654dbfd30179f30ab2f39d606d3", size = 789951, upload-time = "2026-01-14T23:15:39.582Z" }, - { url = "https://files.pythonhosted.org/packages/fc/2a/5928af114441e059f15b2f63e188bd00c6529b3051c974ade7444b85fcda/regex-2026.1.15-cp313-cp313-win32.whl", hash = "sha256:d426616dae0967ca225ab12c22274eb816558f2f99ccb4a1d52ca92e8baf180f", size = 266275, upload-time = "2026-01-14T23:15:42.108Z" }, - { url = "https://files.pythonhosted.org/packages/4f/16/5bfbb89e435897bff28cf0352a992ca719d9e55ebf8b629203c96b6ce4f7/regex-2026.1.15-cp313-cp313-win_amd64.whl", hash = "sha256:febd38857b09867d3ed3f4f1af7d241c5c50362e25ef43034995b77a50df494e", size = 277145, upload-time = "2026-01-14T23:15:44.244Z" }, - { url = "https://files.pythonhosted.org/packages/56/c1/a09ff7392ef4233296e821aec5f78c51be5e91ffde0d163059e50fd75835/regex-2026.1.15-cp313-cp313-win_arm64.whl", hash = "sha256:8e32f7896f83774f91499d239e24cebfadbc07639c1494bb7213983842348337", size = 270411, upload-time = "2026-01-14T23:15:45.858Z" }, - { url = "https://files.pythonhosted.org/packages/3c/38/0cfd5a78e5c6db00e6782fdae70458f89850ce95baa5e8694ab91d89744f/regex-2026.1.15-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ec94c04149b6a7b8120f9f44565722c7ae31b7a6d2275569d2eefa76b83da3be", size = 492068, upload-time = "2026-01-14T23:15:47.616Z" }, - { url = "https://files.pythonhosted.org/packages/50/72/6c86acff16cb7c959c4355826bbf06aad670682d07c8f3998d9ef4fee7cd/regex-2026.1.15-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40c86d8046915bb9aeb15d3f3f15b6fd500b8ea4485b30e1bbc799dab3fe29f8", size = 292756, upload-time = "2026-01-14T23:15:49.307Z" }, - { url = "https://files.pythonhosted.org/packages/4e/58/df7fb69eadfe76526ddfce28abdc0af09ffe65f20c2c90932e89d705153f/regex-2026.1.15-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:726ea4e727aba21643205edad8f2187ec682d3305d790f73b7a51c7587b64bdd", size = 291114, upload-time = "2026-01-14T23:15:51.484Z" }, - { url = "https://files.pythonhosted.org/packages/ed/6c/a4011cd1cf96b90d2cdc7e156f91efbd26531e822a7fbb82a43c1016678e/regex-2026.1.15-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cb740d044aff31898804e7bf1181cc72c03d11dfd19932b9911ffc19a79070a", size = 807524, upload-time = "2026-01-14T23:15:53.102Z" }, - { url = "https://files.pythonhosted.org/packages/1d/25/a53ffb73183f69c3e9f4355c4922b76d2840aee160af6af5fac229b6201d/regex-2026.1.15-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05d75a668e9ea16f832390d22131fe1e8acc8389a694c8febc3e340b0f810b93", size = 873455, upload-time = "2026-01-14T23:15:54.956Z" }, - { url = "https://files.pythonhosted.org/packages/66/0b/8b47fc2e8f97d9b4a851736f3890a5f786443aa8901061c55f24c955f45b/regex-2026.1.15-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d991483606f3dbec93287b9f35596f41aa2e92b7c2ebbb935b63f409e243c9af", size = 915007, upload-time = "2026-01-14T23:15:57.041Z" }, - { url = "https://files.pythonhosted.org/packages/c2/fa/97de0d681e6d26fabe71968dbee06dd52819e9a22fdce5dac7256c31ed84/regex-2026.1.15-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:194312a14819d3e44628a44ed6fea6898fdbecb0550089d84c403475138d0a09", size = 812794, upload-time = "2026-01-14T23:15:58.916Z" }, - { url = "https://files.pythonhosted.org/packages/22/38/e752f94e860d429654aa2b1c51880bff8dfe8f084268258adf9151cf1f53/regex-2026.1.15-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe2fda4110a3d0bc163c2e0664be44657431440722c5c5315c65155cab92f9e5", size = 781159, upload-time = "2026-01-14T23:16:00.817Z" }, - { url = "https://files.pythonhosted.org/packages/e9/a7/d739ffaef33c378fc888302a018d7f81080393d96c476b058b8c64fd2b0d/regex-2026.1.15-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:124dc36c85d34ef2d9164da41a53c1c8c122cfb1f6e1ec377a1f27ee81deb794", size = 795558, upload-time = "2026-01-14T23:16:03.267Z" }, - { url = "https://files.pythonhosted.org/packages/3e/c4/542876f9a0ac576100fc73e9c75b779f5c31e3527576cfc9cb3009dcc58a/regex-2026.1.15-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1774cd1981cd212506a23a14dba7fdeaee259f5deba2df6229966d9911e767a", size = 868427, upload-time = "2026-01-14T23:16:05.646Z" }, - { url = "https://files.pythonhosted.org/packages/fc/0f/d5655bea5b22069e32ae85a947aa564912f23758e112cdb74212848a1a1b/regex-2026.1.15-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:b5f7d8d2867152cdb625e72a530d2ccb48a3d199159144cbdd63870882fb6f80", size = 769939, upload-time = "2026-01-14T23:16:07.542Z" }, - { url = "https://files.pythonhosted.org/packages/20/06/7e18a4fa9d326daeda46d471a44ef94201c46eaa26dbbb780b5d92cbfdda/regex-2026.1.15-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:492534a0ab925d1db998defc3c302dae3616a2fc3fe2e08db1472348f096ddf2", size = 854753, upload-time = "2026-01-14T23:16:10.395Z" }, - { url = "https://files.pythonhosted.org/packages/3b/67/dc8946ef3965e166f558ef3b47f492bc364e96a265eb4a2bb3ca765c8e46/regex-2026.1.15-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c661fc820cfb33e166bf2450d3dadbda47c8d8981898adb9b6fe24e5e582ba60", size = 799559, upload-time = "2026-01-14T23:16:12.347Z" }, - { url = "https://files.pythonhosted.org/packages/a5/61/1bba81ff6d50c86c65d9fd84ce9699dd106438ee4cdb105bf60374ee8412/regex-2026.1.15-cp313-cp313t-win32.whl", hash = "sha256:99ad739c3686085e614bf77a508e26954ff1b8f14da0e3765ff7abbf7799f952", size = 268879, upload-time = "2026-01-14T23:16:14.049Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5e/cef7d4c5fb0ea3ac5c775fd37db5747f7378b29526cc83f572198924ff47/regex-2026.1.15-cp313-cp313t-win_amd64.whl", hash = "sha256:32655d17905e7ff8ba5c764c43cb124e34a9245e45b83c22e81041e1071aee10", size = 280317, upload-time = "2026-01-14T23:16:15.718Z" }, - { url = "https://files.pythonhosted.org/packages/b4/52/4317f7a5988544e34ab57b4bde0f04944c4786128c933fb09825924d3e82/regex-2026.1.15-cp313-cp313t-win_arm64.whl", hash = "sha256:b2a13dd6a95e95a489ca242319d18fc02e07ceb28fa9ad146385194d95b3c829", size = 271551, upload-time = "2026-01-14T23:16:17.533Z" }, - { url = "https://files.pythonhosted.org/packages/52/0a/47fa888ec7cbbc7d62c5f2a6a888878e76169170ead271a35239edd8f0e8/regex-2026.1.15-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:d920392a6b1f353f4aa54328c867fec3320fa50657e25f64abf17af054fc97ac", size = 489170, upload-time = "2026-01-14T23:16:19.835Z" }, - { url = "https://files.pythonhosted.org/packages/ac/c4/d000e9b7296c15737c9301708e9e7fbdea009f8e93541b6b43bdb8219646/regex-2026.1.15-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b5a28980a926fa810dbbed059547b02783952e2efd9c636412345232ddb87ff6", size = 291146, upload-time = "2026-01-14T23:16:21.541Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b6/921cc61982e538682bdf3bdf5b2c6ab6b34368da1f8e98a6c1ddc503c9cf/regex-2026.1.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:621f73a07595d83f28952d7bd1e91e9d1ed7625fb7af0064d3516674ec93a2a2", size = 288986, upload-time = "2026-01-14T23:16:23.381Z" }, - { url = "https://files.pythonhosted.org/packages/ca/33/eb7383dde0bbc93f4fb9d03453aab97e18ad4024ac7e26cef8d1f0a2cff0/regex-2026.1.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d7d92495f47567a9b1669c51fc8d6d809821849063d168121ef801bbc213846", size = 799098, upload-time = "2026-01-14T23:16:25.088Z" }, - { url = "https://files.pythonhosted.org/packages/27/56/b664dccae898fc8d8b4c23accd853f723bde0f026c747b6f6262b688029c/regex-2026.1.15-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8dd16fba2758db7a3780a051f245539c4451ca20910f5a5e6ea1c08d06d4a76b", size = 864980, upload-time = "2026-01-14T23:16:27.297Z" }, - { url = "https://files.pythonhosted.org/packages/16/40/0999e064a170eddd237bae9ccfcd8f28b3aa98a38bf727a086425542a4fc/regex-2026.1.15-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1e1808471fbe44c1a63e5f577a1d5f02fe5d66031dcbdf12f093ffc1305a858e", size = 911607, upload-time = "2026-01-14T23:16:29.235Z" }, - { url = "https://files.pythonhosted.org/packages/07/78/c77f644b68ab054e5a674fb4da40ff7bffb2c88df58afa82dbf86573092d/regex-2026.1.15-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0751a26ad39d4f2ade8fe16c59b2bf5cb19eb3d2cd543e709e583d559bd9efde", size = 803358, upload-time = "2026-01-14T23:16:31.369Z" }, - { url = "https://files.pythonhosted.org/packages/27/31/d4292ea8566eaa551fafc07797961c5963cf5235c797cc2ae19b85dfd04d/regex-2026.1.15-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0f0c7684c7f9ca241344ff95a1de964f257a5251968484270e91c25a755532c5", size = 775833, upload-time = "2026-01-14T23:16:33.141Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b2/cff3bf2fea4133aa6fb0d1e370b37544d18c8350a2fa118c7e11d1db0e14/regex-2026.1.15-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:74f45d170a21df41508cb67165456538425185baaf686281fa210d7e729abc34", size = 788045, upload-time = "2026-01-14T23:16:35.005Z" }, - { url = "https://files.pythonhosted.org/packages/8d/99/2cb9b69045372ec877b6f5124bda4eb4253bc58b8fe5848c973f752bc52c/regex-2026.1.15-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f1862739a1ffb50615c0fde6bae6569b5efbe08d98e59ce009f68a336f64da75", size = 859374, upload-time = "2026-01-14T23:16:36.919Z" }, - { url = "https://files.pythonhosted.org/packages/09/16/710b0a5abe8e077b1729a562d2f297224ad079f3a66dce46844c193416c8/regex-2026.1.15-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:453078802f1b9e2b7303fb79222c054cb18e76f7bdc220f7530fdc85d319f99e", size = 763940, upload-time = "2026-01-14T23:16:38.685Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/7585c8e744e40eb3d32f119191969b91de04c073fca98ec14299041f6e7e/regex-2026.1.15-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:a30a68e89e5a218b8b23a52292924c1f4b245cb0c68d1cce9aec9bbda6e2c160", size = 850112, upload-time = "2026-01-14T23:16:40.646Z" }, - { url = "https://files.pythonhosted.org/packages/af/d6/43e1dd85df86c49a347aa57c1f69d12c652c7b60e37ec162e3096194a278/regex-2026.1.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9479cae874c81bf610d72b85bb681a94c95722c127b55445285fb0e2c82db8e1", size = 789586, upload-time = "2026-01-14T23:16:42.799Z" }, - { url = "https://files.pythonhosted.org/packages/93/38/77142422f631e013f316aaae83234c629555729a9fbc952b8a63ac91462a/regex-2026.1.15-cp314-cp314-win32.whl", hash = "sha256:d639a750223132afbfb8f429c60d9d318aeba03281a5f1ab49f877456448dcf1", size = 271691, upload-time = "2026-01-14T23:16:44.671Z" }, - { url = "https://files.pythonhosted.org/packages/4a/a9/ab16b4649524ca9e05213c1cdbb7faa85cc2aa90a0230d2f796cbaf22736/regex-2026.1.15-cp314-cp314-win_amd64.whl", hash = "sha256:4161d87f85fa831e31469bfd82c186923070fc970b9de75339b68f0c75b51903", size = 280422, upload-time = "2026-01-14T23:16:46.607Z" }, - { url = "https://files.pythonhosted.org/packages/be/2a/20fd057bf3521cb4791f69f869635f73e0aaf2b9ad2d260f728144f9047c/regex-2026.1.15-cp314-cp314-win_arm64.whl", hash = "sha256:91c5036ebb62663a6b3999bdd2e559fd8456d17e2b485bf509784cd31a8b1705", size = 273467, upload-time = "2026-01-14T23:16:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/ad/77/0b1e81857060b92b9cad239104c46507dd481b3ff1fa79f8e7f865aae38a/regex-2026.1.15-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ee6854c9000a10938c79238de2379bea30c82e4925a371711af45387df35cab8", size = 492073, upload-time = "2026-01-14T23:16:51.154Z" }, - { url = "https://files.pythonhosted.org/packages/70/f3/f8302b0c208b22c1e4f423147e1913fd475ddd6230565b299925353de644/regex-2026.1.15-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c2b80399a422348ce5de4fe40c418d6299a0fa2803dd61dc0b1a2f28e280fcf", size = 292757, upload-time = "2026-01-14T23:16:53.08Z" }, - { url = "https://files.pythonhosted.org/packages/bf/f0/ef55de2460f3b4a6da9d9e7daacd0cb79d4ef75c64a2af316e68447f0df0/regex-2026.1.15-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:dca3582bca82596609959ac39e12b7dad98385b4fefccb1151b937383cec547d", size = 291122, upload-time = "2026-01-14T23:16:55.383Z" }, - { url = "https://files.pythonhosted.org/packages/cf/55/bb8ccbacabbc3a11d863ee62a9f18b160a83084ea95cdfc5d207bfc3dd75/regex-2026.1.15-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef71d476caa6692eea743ae5ea23cde3260677f70122c4d258ca952e5c2d4e84", size = 807761, upload-time = "2026-01-14T23:16:57.251Z" }, - { url = "https://files.pythonhosted.org/packages/8f/84/f75d937f17f81e55679a0509e86176e29caa7298c38bd1db7ce9c0bf6075/regex-2026.1.15-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c243da3436354f4af6c3058a3f81a97d47ea52c9bd874b52fd30274853a1d5df", size = 873538, upload-time = "2026-01-14T23:16:59.349Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d9/0da86327df70349aa8d86390da91171bd3ca4f0e7c1d1d453a9c10344da3/regex-2026.1.15-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8355ad842a7c7e9e5e55653eade3b7d1885ba86f124dd8ab1f722f9be6627434", size = 915066, upload-time = "2026-01-14T23:17:01.607Z" }, - { url = "https://files.pythonhosted.org/packages/2a/5e/f660fb23fc77baa2a61aa1f1fe3a4eea2bbb8a286ddec148030672e18834/regex-2026.1.15-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f192a831d9575271a22d804ff1a5355355723f94f31d9eef25f0d45a152fdc1a", size = 812938, upload-time = "2026-01-14T23:17:04.366Z" }, - { url = "https://files.pythonhosted.org/packages/69/33/a47a29bfecebbbfd1e5cd3f26b28020a97e4820f1c5148e66e3b7d4b4992/regex-2026.1.15-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:166551807ec20d47ceaeec380081f843e88c8949780cd42c40f18d16168bed10", size = 781314, upload-time = "2026-01-14T23:17:06.378Z" }, - { url = "https://files.pythonhosted.org/packages/65/ec/7ec2bbfd4c3f4e494a24dec4c6943a668e2030426b1b8b949a6462d2c17b/regex-2026.1.15-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f9ca1cbdc0fbfe5e6e6f8221ef2309988db5bcede52443aeaee9a4ad555e0dac", size = 795652, upload-time = "2026-01-14T23:17:08.521Z" }, - { url = "https://files.pythonhosted.org/packages/46/79/a5d8651ae131fe27d7c521ad300aa7f1c7be1dbeee4d446498af5411b8a9/regex-2026.1.15-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b30bcbd1e1221783c721483953d9e4f3ab9c5d165aa709693d3f3946747b1aea", size = 868550, upload-time = "2026-01-14T23:17:10.573Z" }, - { url = "https://files.pythonhosted.org/packages/06/b7/25635d2809664b79f183070786a5552dd4e627e5aedb0065f4e3cf8ee37d/regex-2026.1.15-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2a8d7b50c34578d0d3bf7ad58cde9652b7d683691876f83aedc002862a35dc5e", size = 769981, upload-time = "2026-01-14T23:17:12.871Z" }, - { url = "https://files.pythonhosted.org/packages/16/8b/fc3fcbb2393dcfa4a6c5ffad92dc498e842df4581ea9d14309fcd3c55fb9/regex-2026.1.15-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9d787e3310c6a6425eb346be4ff2ccf6eece63017916fd77fe8328c57be83521", size = 854780, upload-time = "2026-01-14T23:17:14.837Z" }, - { url = "https://files.pythonhosted.org/packages/d0/38/dde117c76c624713c8a2842530be9c93ca8b606c0f6102d86e8cd1ce8bea/regex-2026.1.15-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:619843841e220adca114118533a574a9cd183ed8a28b85627d2844c500a2b0db", size = 799778, upload-time = "2026-01-14T23:17:17.369Z" }, - { url = "https://files.pythonhosted.org/packages/e3/0d/3a6cfa9ae99606afb612d8fb7a66b245a9d5ff0f29bb347c8a30b6ad561b/regex-2026.1.15-cp314-cp314t-win32.whl", hash = "sha256:e90b8db97f6f2c97eb045b51a6b2c5ed69cedd8392459e0642d4199b94fabd7e", size = 274667, upload-time = "2026-01-14T23:17:19.301Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b2/297293bb0742fd06b8d8e2572db41a855cdf1cae0bf009b1cb74fe07e196/regex-2026.1.15-cp314-cp314t-win_amd64.whl", hash = "sha256:5ef19071f4ac9f0834793af85bd04a920b4407715624e40cb7a0631a11137cdf", size = 284386, upload-time = "2026-01-14T23:17:21.231Z" }, - { url = "https://files.pythonhosted.org/packages/95/e4/a3b9480c78cf8ee86626cb06f8d931d74d775897d44201ccb813097ae697/regex-2026.1.15-cp314-cp314t-win_arm64.whl", hash = "sha256:ca89c5e596fc05b015f27561b3793dc2fa0917ea0d7507eebb448efd35274a70", size = 274837, upload-time = "2026-01-14T23:17:23.146Z" }, -] - -[[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 = "rich" -version = "14.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" }, -] - -[[package]] -name = "rich-rst" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, -] - -[[package]] -name = "secretstorage" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[package]] -name = "sortedcontainers" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, -] - -[[package]] -name = "sse-starlette" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "starlette" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/00d280c03ffd39aaee0e86ec81e2d3b9253036a0f93f51d10503adef0e65/sse_starlette-3.2.0.tar.gz", hash = "sha256:8127594edfb51abe44eac9c49e59b0b01f1039d0c7461c6fd91d4e03b70da422", size = 27253, upload-time = "2026-01-17T13:11:05.62Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/7f/832f015020844a8b8f7a9cbc103dd76ba8e3875004c41e08440ea3a2b41a/sse_starlette-3.2.0-py3-none-any.whl", hash = "sha256:5876954bd51920fc2cd51baee47a080eb88a37b5b784e615abb0b283f801cdbf", size = 12763, upload-time = "2026-01-17T13:11:03.775Z" }, -] - -[[package]] -name = "starlette" -version = "0.52.1" -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/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, -] - -[[package]] -name = "tiktoken" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "regex" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806, upload-time = "2025-10-06T20:22:45.419Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/85/be65d39d6b647c79800fd9d29241d081d4eeb06271f383bb87200d74cf76/tiktoken-0.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b97f74aca0d78a1ff21b8cd9e9925714c15a9236d6ceacf5c7327c117e6e21e8", size = 1050728, upload-time = "2025-10-06T20:21:52.756Z" }, - { url = "https://files.pythonhosted.org/packages/4a/42/6573e9129bc55c9bf7300b3a35bef2c6b9117018acca0dc760ac2d93dffe/tiktoken-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b90f5ad190a4bb7c3eb30c5fa32e1e182ca1ca79f05e49b448438c3e225a49b", size = 994049, upload-time = "2025-10-06T20:21:53.782Z" }, - { url = "https://files.pythonhosted.org/packages/66/c5/ed88504d2f4a5fd6856990b230b56d85a777feab84e6129af0822f5d0f70/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:65b26c7a780e2139e73acc193e5c63ac754021f160df919add909c1492c0fb37", size = 1129008, upload-time = "2025-10-06T20:21:54.832Z" }, - { url = "https://files.pythonhosted.org/packages/f4/90/3dae6cc5436137ebd38944d396b5849e167896fc2073da643a49f372dc4f/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:edde1ec917dfd21c1f2f8046b86348b0f54a2c0547f68149d8600859598769ad", size = 1152665, upload-time = "2025-10-06T20:21:56.129Z" }, - { url = "https://files.pythonhosted.org/packages/a3/fe/26df24ce53ffde419a42f5f53d755b995c9318908288c17ec3f3448313a3/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:35a2f8ddd3824608b3d650a000c1ef71f730d0c56486845705a8248da00f9fe5", size = 1194230, upload-time = "2025-10-06T20:21:57.546Z" }, - { url = "https://files.pythonhosted.org/packages/20/cc/b064cae1a0e9fac84b0d2c46b89f4e57051a5f41324e385d10225a984c24/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83d16643edb7fa2c99eff2ab7733508aae1eebb03d5dfc46f5565862810f24e3", size = 1254688, upload-time = "2025-10-06T20:21:58.619Z" }, - { url = "https://files.pythonhosted.org/packages/81/10/b8523105c590c5b8349f2587e2fdfe51a69544bd5a76295fc20f2374f470/tiktoken-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffc5288f34a8bc02e1ea7047b8d041104791d2ddbf42d1e5fa07822cbffe16bd", size = 878694, upload-time = "2025-10-06T20:21:59.876Z" }, - { url = "https://files.pythonhosted.org/packages/00/61/441588ee21e6b5cdf59d6870f86beb9789e532ee9718c251b391b70c68d6/tiktoken-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3", size = 1050802, upload-time = "2025-10-06T20:22:00.96Z" }, - { url = "https://files.pythonhosted.org/packages/1f/05/dcf94486d5c5c8d34496abe271ac76c5b785507c8eae71b3708f1ad9b45a/tiktoken-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160", size = 993995, upload-time = "2025-10-06T20:22:02.788Z" }, - { url = "https://files.pythonhosted.org/packages/a0/70/5163fe5359b943f8db9946b62f19be2305de8c3d78a16f629d4165e2f40e/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa", size = 1128948, upload-time = "2025-10-06T20:22:03.814Z" }, - { url = "https://files.pythonhosted.org/packages/0c/da/c028aa0babf77315e1cef357d4d768800c5f8a6de04d0eac0f377cb619fa/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be", size = 1151986, upload-time = "2025-10-06T20:22:05.173Z" }, - { url = "https://files.pythonhosted.org/packages/a0/5a/886b108b766aa53e295f7216b509be95eb7d60b166049ce2c58416b25f2a/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a", size = 1194222, upload-time = "2025-10-06T20:22:06.265Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f8/4db272048397636ac7a078d22773dd2795b1becee7bc4922fe6207288d57/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3", size = 1255097, upload-time = "2025-10-06T20:22:07.403Z" }, - { url = "https://files.pythonhosted.org/packages/8e/32/45d02e2e0ea2be3a9ed22afc47d93741247e75018aac967b713b2941f8ea/tiktoken-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697", size = 879117, upload-time = "2025-10-06T20:22:08.418Z" }, - { url = "https://files.pythonhosted.org/packages/ce/76/994fc868f88e016e6d05b0da5ac24582a14c47893f4474c3e9744283f1d5/tiktoken-0.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16", size = 1050309, upload-time = "2025-10-06T20:22:10.939Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b8/57ef1456504c43a849821920d582a738a461b76a047f352f18c0b26c6516/tiktoken-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a", size = 993712, upload-time = "2025-10-06T20:22:12.115Z" }, - { url = "https://files.pythonhosted.org/packages/72/90/13da56f664286ffbae9dbcfadcc625439142675845baa62715e49b87b68b/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27", size = 1128725, upload-time = "2025-10-06T20:22:13.541Z" }, - { url = "https://files.pythonhosted.org/packages/05/df/4f80030d44682235bdaecd7346c90f67ae87ec8f3df4a3442cb53834f7e4/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb", size = 1151875, upload-time = "2025-10-06T20:22:14.559Z" }, - { url = "https://files.pythonhosted.org/packages/22/1f/ae535223a8c4ef4c0c1192e3f9b82da660be9eb66b9279e95c99288e9dab/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e", size = 1194451, upload-time = "2025-10-06T20:22:15.545Z" }, - { url = "https://files.pythonhosted.org/packages/78/a7/f8ead382fce0243cb625c4f266e66c27f65ae65ee9e77f59ea1653b6d730/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25", size = 1253794, upload-time = "2025-10-06T20:22:16.624Z" }, - { url = "https://files.pythonhosted.org/packages/93/e0/6cc82a562bc6365785a3ff0af27a2a092d57c47d7a81d9e2295d8c36f011/tiktoken-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f", size = 878777, upload-time = "2025-10-06T20:22:18.036Z" }, - { url = "https://files.pythonhosted.org/packages/72/05/3abc1db5d2c9aadc4d2c76fa5640134e475e58d9fbb82b5c535dc0de9b01/tiktoken-0.12.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a90388128df3b3abeb2bfd1895b0681412a8d7dc644142519e6f0a97c2111646", size = 1050188, upload-time = "2025-10-06T20:22:19.563Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7b/50c2f060412202d6c95f32b20755c7a6273543b125c0985d6fa9465105af/tiktoken-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:da900aa0ad52247d8794e307d6446bd3cdea8e192769b56276695d34d2c9aa88", size = 993978, upload-time = "2025-10-06T20:22:20.702Z" }, - { url = "https://files.pythonhosted.org/packages/14/27/bf795595a2b897e271771cd31cb847d479073497344c637966bdf2853da1/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:285ba9d73ea0d6171e7f9407039a290ca77efcdb026be7769dccc01d2c8d7fff", size = 1129271, upload-time = "2025-10-06T20:22:22.06Z" }, - { url = "https://files.pythonhosted.org/packages/f5/de/9341a6d7a8f1b448573bbf3425fa57669ac58258a667eb48a25dfe916d70/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:d186a5c60c6a0213f04a7a802264083dea1bbde92a2d4c7069e1a56630aef830", size = 1151216, upload-time = "2025-10-06T20:22:23.085Z" }, - { url = "https://files.pythonhosted.org/packages/75/0d/881866647b8d1be4d67cb24e50d0c26f9f807f994aa1510cb9ba2fe5f612/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:604831189bd05480f2b885ecd2d1986dc7686f609de48208ebbbddeea071fc0b", size = 1194860, upload-time = "2025-10-06T20:22:24.602Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1e/b651ec3059474dab649b8d5b69f5c65cd8fcd8918568c1935bd4136c9392/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8f317e8530bb3a222547b85a58583238c8f74fd7a7408305f9f63246d1a0958b", size = 1254567, upload-time = "2025-10-06T20:22:25.671Z" }, - { url = "https://files.pythonhosted.org/packages/80/57/ce64fd16ac390fafde001268c364d559447ba09b509181b2808622420eec/tiktoken-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:399c3dd672a6406719d84442299a490420b458c44d3ae65516302a99675888f3", size = 921067, upload-time = "2025-10-06T20:22:26.753Z" }, - { url = "https://files.pythonhosted.org/packages/ac/a4/72eed53e8976a099539cdd5eb36f241987212c29629d0a52c305173e0a68/tiktoken-0.12.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2c714c72bc00a38ca969dae79e8266ddec999c7ceccd603cc4f0d04ccd76365", size = 1050473, upload-time = "2025-10-06T20:22:27.775Z" }, - { url = "https://files.pythonhosted.org/packages/e6/d7/0110b8f54c008466b19672c615f2168896b83706a6611ba6e47313dbc6e9/tiktoken-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cbb9a3ba275165a2cb0f9a83f5d7025afe6b9d0ab01a22b50f0e74fee2ad253e", size = 993855, upload-time = "2025-10-06T20:22:28.799Z" }, - { url = "https://files.pythonhosted.org/packages/5f/77/4f268c41a3957c418b084dd576ea2fad2e95da0d8e1ab705372892c2ca22/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:dfdfaa5ffff8993a3af94d1125870b1d27aed7cb97aa7eb8c1cefdbc87dbee63", size = 1129022, upload-time = "2025-10-06T20:22:29.981Z" }, - { url = "https://files.pythonhosted.org/packages/4e/2b/fc46c90fe5028bd094cd6ee25a7db321cb91d45dc87531e2bdbb26b4867a/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:584c3ad3d0c74f5269906eb8a659c8bfc6144a52895d9261cdaf90a0ae5f4de0", size = 1150736, upload-time = "2025-10-06T20:22:30.996Z" }, - { url = "https://files.pythonhosted.org/packages/28/c0/3c7a39ff68022ddfd7d93f3337ad90389a342f761c4d71de99a3ccc57857/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:54c891b416a0e36b8e2045b12b33dd66fb34a4fe7965565f1b482da50da3e86a", size = 1194908, upload-time = "2025-10-06T20:22:32.073Z" }, - { url = "https://files.pythonhosted.org/packages/ab/0d/c1ad6f4016a3968c048545f5d9b8ffebf577774b2ede3e2e352553b685fe/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5edb8743b88d5be814b1a8a8854494719080c28faaa1ccbef02e87354fe71ef0", size = 1253706, upload-time = "2025-10-06T20:22:33.385Z" }, - { url = "https://files.pythonhosted.org/packages/af/df/c7891ef9d2712ad774777271d39fdef63941ffba0a9d59b7ad1fd2765e57/tiktoken-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f61c0aea5565ac82e2ec50a05e02a6c44734e91b51c10510b084ea1b8e633a71", size = 920667, upload-time = "2025-10-06T20:22:34.444Z" }, -] - -[[package]] -name = "typer" -version = "0.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.40.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, -] - -[[package]] -name = "websockets" -version = "16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, - { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, - { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, - { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, - { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, - { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, - { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, - { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, - { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, - { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, -] - -[[package]] -name = "wrapt" -version = "1.17.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998, upload-time = "2025-08-12T05:51:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020, upload-time = "2025-08-12T05:51:35.906Z" }, - { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098, upload-time = "2025-08-12T05:51:57.474Z" }, - { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036, upload-time = "2025-08-12T05:52:34.784Z" }, - { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156, upload-time = "2025-08-12T05:52:13.599Z" }, - { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102, upload-time = "2025-08-12T05:52:14.56Z" }, - { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732, upload-time = "2025-08-12T05:52:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705, upload-time = "2025-08-12T05:53:07.123Z" }, - { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877, upload-time = "2025-08-12T05:53:05.436Z" }, - { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885, upload-time = "2025-08-12T05:52:54.367Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, - { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, - { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, - { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, - { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, - { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, - { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, - { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, - { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, - { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, - { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, - { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, - { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, - { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, - { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, - { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, - { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, - { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, - { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, -] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] diff --git a/pkg/hanzo-tools-team/README.md b/pkg/hanzo-tools-team/README.md deleted file mode 100644 index a22050b90..000000000 --- a/pkg/hanzo-tools-team/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# hanzo-tools-team - -MCP tool package for hanzo-mcp. Provides native team management via the Hanzo platform. - -## Installation - -```bash -pip install hanzo-tools-team -``` - -Part of the [hanzo-mcp](https://pypi.org/project/hanzo-mcp/) ecosystem. diff --git a/pkg/hanzo-tools-team/hanzo_tools/__init__.py b/pkg/hanzo-tools-team/hanzo_tools/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-tools-team/hanzo_tools/team/__init__.py b/pkg/hanzo-tools-team/hanzo_tools/team/__init__.py deleted file mode 100644 index 529c6d281..000000000 --- a/pkg/hanzo-tools-team/hanzo_tools/team/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Hanzo Team Tools โ€” workspace and member management via MCP.""" - -from .team_tool import TeamTool - -TOOLS = [TeamTool] - -__all__ = ["TeamTool", "TOOLS"] diff --git a/pkg/hanzo-tools-team/hanzo_tools/team/team_tool.py b/pkg/hanzo-tools-team/hanzo_tools/team/team_tool.py deleted file mode 100644 index 83a9f47c4..000000000 --- a/pkg/hanzo-tools-team/hanzo_tools/team/team_tool.py +++ /dev/null @@ -1,294 +0,0 @@ -"""MCP tool for Hanzo Team โ€” workspace and member management. - -Manage workspaces, members, and invitations for collaborative -Hanzo Team accounts. - -Auth: Uses HanzoSession from hanzo-tools-auth for Bearer JWT tokens. -Backend: Hanzo Team service. -""" - -from __future__ import annotations - -import os -import json -import logging -from typing import Any, Annotated, final - -import httpx -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core.base import BaseTool - -logger = logging.getLogger(__name__) - -TEAM_BASE_URL = os.getenv("HANZO_TEAM_URL", "https://api.hanzo.ai/team") - -DESCRIPTION = """Hanzo Team โ€” workspace and member management. - -Requires authentication via `hanzo login` (stored at ~/.hanzo/auth/token.json). - -Actions: -- workspaces: List all workspaces -- workspace: Get workspace details (params: workspace_id) -- create_workspace: Create a new workspace (params: name) -- delete_workspace: Delete a workspace (params: workspace_id) -- members: List workspace members (params: workspace_id) -- invite: Invite a member to a workspace (params: workspace_id, email, role) -- account: Get current account info -""" - - -def _get_session(): - """Get HanzoSession singleton.""" - from hanzo_tools.auth.session import HanzoSession - return HanzoSession.get() - - -def _team_url(path: str) -> str: - """Build full Team API URL.""" - return f"{TEAM_BASE_URL}/{path.lstrip('/')}" - - -def _auth_headers(token: str) -> dict[str, str]: - """Build auth headers with Bearer token.""" - return { - "Authorization": f"Bearer {token}", - "Content-Type": "application/json", - "User-Agent": "hanzo-mcp/0.1", - } - - -def _get_token() -> str: - """Get Bearer token from session or raise.""" - session = _get_session() - token = session.get_iam_token() - if not token: - raise RuntimeError("Not authenticated. Run 'hanzo login' first.") - return token - - -async def _team_get(path: str, params: dict[str, Any] | None = None) -> Any: - """GET request to Team API.""" - token = _get_token() - async with httpx.AsyncClient(timeout=30.0) as client: - resp = await client.get( - _team_url(path), - headers=_auth_headers(token), - params=params, - ) - resp.raise_for_status() - return resp.json() - - -async def _team_post(path: str, body: dict[str, Any] | None = None) -> Any: - """POST request to Team API.""" - token = _get_token() - async with httpx.AsyncClient(timeout=30.0) as client: - resp = await client.post( - _team_url(path), - headers=_auth_headers(token), - json=body or {}, - ) - resp.raise_for_status() - return resp.json() - - -async def _team_delete(path: str) -> Any: - """DELETE request to Team API.""" - token = _get_token() - async with httpx.AsyncClient(timeout=30.0) as client: - resp = await client.delete( - _team_url(path), - headers=_auth_headers(token), - ) - resp.raise_for_status() - if not resp.content or resp.status_code == 204: - return {} - return resp.json() - - -@final -class TeamTool(BaseTool): - """MCP tool for Hanzo Team operations.""" - - @property - def name(self) -> str: - return "team" - - @property - def description(self) -> str: - return DESCRIPTION - - async def call( - self, - ctx: MCPContext, - action: str = "account", - workspace_id: str | None = None, - name: str | None = None, - email: str | None = None, - role: str | None = None, - **kwargs: Any, - ) -> str: - try: - if action == "workspaces": - return await self._workspaces() - elif action == "workspace": - return await self._workspace(workspace_id) - elif action == "create_workspace": - return await self._create_workspace(name) - elif action == "delete_workspace": - return await self._delete_workspace(workspace_id) - elif action == "members": - return await self._members(workspace_id) - elif action == "invite": - return await self._invite(workspace_id, email, role) - elif action == "account": - return await self._account() - else: - return json.dumps({ - "error": f"Unknown action: {action}", - "available": [ - "workspaces", "workspace", "create_workspace", - "delete_workspace", "members", "invite", "account", - ], - }) - except RuntimeError as e: - return json.dumps({"error": str(e)}) - except httpx.HTTPStatusError as e: - body = e.response.text - try: - body = e.response.json() - except Exception: - pass - return json.dumps({"error": f"Team API error {e.response.status_code}", "detail": body}) - except Exception as e: - logger.exception(f"Team tool error: {e}") - return json.dumps({"error": f"Team error: {e}"}) - - # -- Workspace actions --------------------------------------------------- - - async def _workspaces(self) -> str: - data = await _team_get("workspaces") - workspaces = data if isinstance(data, list) else data.get("workspaces", []) - result = [] - for w in workspaces: - result.append({ - "id": w.get("id"), - "name": w.get("name"), - "slug": w.get("slug"), - "createdAt": w.get("createdAt"), - "memberCount": w.get("memberCount"), - }) - return json.dumps({"count": len(result), "workspaces": result}, indent=2) - - async def _workspace(self, workspace_id: str | None) -> str: - if not workspace_id: - return json.dumps({"error": "Required: workspace_id"}) - data = await _team_get(f"workspaces/{workspace_id}") - return json.dumps(data, indent=2) - - async def _create_workspace(self, name: str | None) -> str: - if not name: - return json.dumps({"error": "Required: name"}) - data = await _team_post("workspaces", body={"name": name}) - return json.dumps({"action": "created", "result": data}, indent=2) - - async def _delete_workspace(self, workspace_id: str | None) -> str: - if not workspace_id: - return json.dumps({"error": "Required: workspace_id"}) - data = await _team_delete(f"workspaces/{workspace_id}") - return json.dumps({"action": "deleted", "workspace_id": workspace_id, "result": data}, indent=2) - - # -- Member actions ------------------------------------------------------ - - async def _members(self, workspace_id: str | None) -> str: - if not workspace_id: - return json.dumps({"error": "Required: workspace_id"}) - data = await _team_get(f"workspaces/{workspace_id}/members") - members = data if isinstance(data, list) else data.get("members", []) - result = [] - for m in members: - result.append({ - "id": m.get("id"), - "name": m.get("name"), - "email": m.get("email"), - "role": m.get("role"), - "joinedAt": m.get("joinedAt"), - }) - return json.dumps({ - "workspace_id": workspace_id, - "count": len(result), - "members": result, - }, indent=2) - - async def _invite(self, workspace_id: str | None, email: str | None, role: str | None) -> str: - if not workspace_id or not email: - return json.dumps({"error": "Required: workspace_id, email. Optional: role"}) - body: dict[str, Any] = {"email": email} - if role: - body["role"] = role - data = await _team_post(f"workspaces/{workspace_id}/invitations", body=body) - return json.dumps({ - "action": "invited", - "workspace_id": workspace_id, - "email": email, - "role": role or "member", - "result": data, - }, indent=2) - - # -- Account action ------------------------------------------------------ - - async def _account(self) -> str: - data = await _team_get("account") - return json.dumps(data, indent=2) - - # -- Registration -------------------------------------------------------- - - def register(self, mcp_server: FastMCP) -> None: - """Register Team tool with explicit parameters.""" - tool_instance = self - - @mcp_server.tool( - name="team", - description=DESCRIPTION, - ) - async def team( - action: Annotated[ - str, - Field( - description=( - "Action to perform. " - "Workspaces: workspaces, workspace, create_workspace, delete_workspace. " - "Members: members, invite. " - "Account: account." - ), - ), - ] = "account", - workspace_id: Annotated[ - str | None, - Field(description="Workspace ID (for workspace, delete_workspace, members, invite)"), - ] = None, - name: Annotated[ - str | None, - Field(description="Workspace name (for create_workspace)"), - ] = None, - email: Annotated[ - str | None, - Field(description="Email address (for invite)"), - ] = None, - role: Annotated[ - str | None, - Field(description="Member role (for invite, e.g. admin, member, viewer)"), - ] = None, - ctx: MCPContext = None, - ) -> str: - return await tool_instance.call( - ctx, - action=action, - workspace_id=workspace_id, - name=name, - email=email, - role=role, - ) diff --git a/pkg/hanzo-tools-team/pyproject.toml b/pkg/hanzo-tools-team/pyproject.toml deleted file mode 100644 index 660100b00..000000000 --- a/pkg/hanzo-tools-team/pyproject.toml +++ /dev/null @@ -1,32 +0,0 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[project] -name = "hanzo-tools-team" -version = "0.1.0" -description = "Hanzo MCP tool for Team โ€” workspaces, members, and invitations" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "mcp", "team", "workspace", "collaboration", "tools"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", -] - -dependencies = [ - "hanzo-tools-core>=0.1.0", - "hanzo-tools-auth>=0.1.0", - "httpx>=0.27.0", -] - -[project.entry-points."hanzo.tools"] -team = "hanzo_tools.team:TOOLS" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] diff --git a/pkg/hanzo-tools-test/README.md b/pkg/hanzo-tools-test/README.md deleted file mode 100644 index c521f4bb5..000000000 --- a/pkg/hanzo-tools-test/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# hanzo-tools-test - -Test and validation tools for Hanzo AI (HIP-0300). - -## Installation - -```bash -pip install hanzo-tools-test -``` diff --git a/pkg/hanzo-tools-test/hanzo_tools/__init__.py b/pkg/hanzo-tools-test/hanzo_tools/__init__.py deleted file mode 100644 index 946984951..000000000 --- a/pkg/hanzo-tools-test/hanzo_tools/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Namespace package -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-test/hanzo_tools/test/__init__.py b/pkg/hanzo-tools-test/hanzo_tools/test/__init__.py deleted file mode 100644 index 885b1f3e2..000000000 --- a/pkg/hanzo-tools-test/hanzo_tools/test/__init__.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Test and validation tools for Hanzo AI (HIP-0300). - -Tools: -- test: Unified test/validation tool (HIP-0300) - - run: Execute test/lint/typecheck - - detect: Auto-detect available tools - -Kinds: test | lint | typecheck - -Effect lattice position: NONDETERMINISTIC_EFFECT -Wraps process execution with structured Report output. - -Install: - pip install hanzo-tools-test - -Usage: - from hanzo_tools.test import register_tools, TOOLS - - # Register with MCP server - register_tools(mcp_server) - - # Or access the unified tool - from hanzo_tools.test import TestTool -""" - -from hanzo_tools.core import BaseTool, ToolRegistry - -from .test_tool import TestTool, test_tool - -# Export list for tool discovery - HIP-0300 unified tool -TOOLS = [TestTool] - -__all__ = [ - "TestTool", - "test_tool", - "register_tools", - "TOOLS", -] - - -def register_tools(mcp_server, **kwargs) -> list[BaseTool]: - """Register test tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - **kwargs: Additional options (cwd, etc.) - - Returns: - List of registered tool instances - """ - cwd = kwargs.get("cwd") - tool = TestTool(cwd=cwd) - ToolRegistry.register_tool(mcp_server, tool) - return [tool] diff --git a/pkg/hanzo-tools-test/hanzo_tools/test/test_tool.py b/pkg/hanzo-tools-test/hanzo_tools/test/test_tool.py deleted file mode 100644 index 8c9ce9e55..000000000 --- a/pkg/hanzo-tools-test/hanzo_tools/test/test_tool.py +++ /dev/null @@ -1,556 +0,0 @@ -"""Unified validation tool for HIP-0300 architecture. - -This module provides a single unified 'test' tool for three distinct feedback loops: - -1. CHECK (fast, incremental, per-file) - - Static properties: syntax, types, lint rules - - Substrate: LSP / linters - - Output: Diagnostics + quickfix edits - - Composition: Check โ†’ Fix โ†’ Apply โ†’ Check - -2. BUILD (slower, whole-project) - - Validates: linkability, packaging, compile graph - - Substrate: build system - - Output: logs + artifacts - - Composition: Build โ†’ ParseErrors โ†’ Patch โ†’ Build - -3. TEST (executes code, validates behavior) - - Validates: runtime behavior, specs, invariants - - Substrate: test runner - - Output: pass/fail + traces - - Composition: Test โ†’ Locate(failure) โ†’ Patch โ†’ Test - -Effect lattice position: NONDETERMINISTIC_EFFECT -Representation: Report (Diagnostics | BuildResult | TestResult) -Scope: Buffer โ†’ File โ†’ Package โ†’ Repo -""" - -import asyncio -import json -import os -import re -from dataclasses import dataclass, field -from pathlib import Path -from typing import Any, ClassVar, Literal - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - InvalidParamsError, - ToolError, - content_hash, -) - -# Test runner detection and commands -TEST_RUNNERS = { - # Python - "pytest": { - "detect": ["pytest.ini", "pyproject.toml", "setup.py"], - "cmd": ["pytest", "-v", "--tb=short"], - "parse": "pytest", - }, - "unittest": { - "detect": ["test*.py"], - "cmd": ["python", "-m", "unittest", "discover", "-v"], - "parse": "unittest", - }, - # JavaScript/TypeScript - "jest": { - "detect": ["jest.config.js", "jest.config.ts", "package.json"], - "cmd": ["npx", "jest", "--json"], - "parse": "jest", - }, - "vitest": { - "detect": ["vitest.config.ts", "vitest.config.js"], - "cmd": ["npx", "vitest", "run", "--reporter=json"], - "parse": "vitest", - }, - "mocha": { - "detect": [".mocharc.js", ".mocharc.json"], - "cmd": ["npx", "mocha", "--reporter", "json"], - "parse": "mocha", - }, - # Go - "go_test": { - "detect": ["go.mod"], - "cmd": ["go", "test", "-v", "-json", "./..."], - "parse": "go_test", - }, - # Rust - "cargo_test": { - "detect": ["Cargo.toml"], - "cmd": ["cargo", "test", "--", "--format=json", "-Z", "unstable-options"], - "parse": "cargo_test", - }, -} - -# Build tools -BUILD_TOOLS = { - # Python - "pip": { - "detect": ["pyproject.toml", "setup.py"], - "cmd": ["pip", "install", "-e", "."], - "parse": "pip", - }, - "poetry": { - "detect": ["poetry.lock"], - "cmd": ["poetry", "build"], - "parse": "poetry", - }, - # JavaScript/TypeScript - "npm": { - "detect": ["package.json"], - "cmd": ["npm", "run", "build"], - "parse": "npm", - }, - "pnpm": { - "detect": ["pnpm-lock.yaml"], - "cmd": ["pnpm", "build"], - "parse": "pnpm", - }, - # Go - "go_build": { - "detect": ["go.mod"], - "cmd": ["go", "build", "./..."], - "parse": "go_build", - }, - # Rust - "cargo_build": { - "detect": ["Cargo.toml"], - "cmd": ["cargo", "build"], - "parse": "cargo_build", - }, - # Make - "make": { - "detect": ["Makefile"], - "cmd": ["make"], - "parse": "make", - }, -} - -# Check tools (fast, incremental linting/typechecking) -CHECK_TOOLS = { - # Python - "ruff": { - "detect": ["ruff.toml", "pyproject.toml"], - "cmd": ["ruff", "check", "--output-format=json"], - "parse": "ruff", - }, - "mypy": { - "detect": ["mypy.ini", "pyproject.toml"], - "cmd": ["mypy", "--output=json"], - "parse": "mypy", - }, - "pylint": { - "detect": [".pylintrc", "pyproject.toml"], - "cmd": ["pylint", "--output-format=json"], - "parse": "pylint", - }, - # JavaScript/TypeScript - "eslint": { - "detect": [".eslintrc", ".eslintrc.js", ".eslintrc.json"], - "cmd": ["npx", "eslint", "--format=json"], - "parse": "eslint", - }, - "tsc": { - "detect": ["tsconfig.json"], - "cmd": ["npx", "tsc", "--noEmit"], - "parse": "tsc", - }, - # Go - "golangci-lint": { - "detect": [".golangci.yml", ".golangci.yaml"], - "cmd": ["golangci-lint", "run", "--out-format=json"], - "parse": "golangci", - }, - # Rust - "cargo_clippy": { - "detect": ["Cargo.toml"], - "cmd": ["cargo", "clippy", "--message-format=json"], - "parse": "clippy", - }, -} - - -@dataclass -class TestResult: - """Single test result.""" - - name: str - status: Literal["pass", "fail", "skip", "error"] - duration_ms: float = 0 - message: str | None = None - location: dict | None = None # {file, line} - - -@dataclass -class Report: - """Structured test/lint report.""" - - kind: str # test, lint, typecheck - tool: str - passed: bool - total: int = 0 - passed_count: int = 0 - failed_count: int = 0 - skipped_count: int = 0 - error_count: int = 0 - duration_ms: float = 0 - results: list[TestResult] = field(default_factory=list) - raw_output: str = "" - exit_code: int = 0 - - -class TestTool(BaseTool): - """Unified test/validation tool (HIP-0300). - - Handles all assurance operations: - - run: Execute test/lint/typecheck - - report: Format results - - Wraps proc.run with structured Report output. - Effect: NONDETERMINISTIC_EFFECT - """ - - name: ClassVar[str] = "test" - VERSION: ClassVar[str] = "0.1.0" - - def __init__(self, cwd: str | None = None): - super().__init__() - self.cwd = cwd or os.getcwd() - self._register_test_actions() - - @property - def description(self) -> str: - return """Unified validation tool (HIP-0300). - -Three feedback loops: -- check: Fast, incremental (lint/typecheck) โ†’ Diagnostics -- build: Whole-project compilation โ†’ BuildResult -- test: Runtime behavior validation โ†’ TestResult - -Actions: -- run: Execute check/build/test with structured Report output -- detect: Auto-detect available tools for each loop - -Compositions: -- Check loop: check โ†’ fix โ†’ apply โ†’ check -- Build loop: build โ†’ locate(errors) โ†’ patch โ†’ build -- Test loop: test โ†’ locate(failure) โ†’ patch โ†’ test - -Effect: NONDETERMINISTIC_EFFECT -""" - - def _detect_runner(self, cwd: str, kind: str) -> tuple[str, list[str]] | None: - """Auto-detect appropriate runner for the loop type. - - Args: - cwd: Working directory - kind: check | build | test - """ - if kind == "test": - tools = TEST_RUNNERS - elif kind == "build": - tools = BUILD_TOOLS - else: # check (lint/typecheck) - tools = CHECK_TOOLS - - for name, config in tools.items(): - for detect_file in config["detect"]: - if "*" in detect_file: - # Glob pattern - if list(Path(cwd).glob(detect_file)): - return name, config["cmd"] - else: - if (Path(cwd) / detect_file).exists(): - return name, config["cmd"] - - return None - - def _parse_pytest_output(self, output: str, exit_code: int) -> Report: - """Parse pytest output.""" - results = [] - total = passed = failed = skipped = errors = 0 - - # Parse summary line - _summary_match = re.search( - r"(\d+) passed.*?(\d+) failed.*?(\d+) error|" - r"(\d+) passed.*?(\d+) skipped|" - r"(\d+) passed", - output, - ) - - # Parse individual test results - for match in re.finditer( - r"(PASSED|FAILED|SKIPPED|ERROR)\s+(\S+)::", - output, - ): - status_map = { - "PASSED": "pass", - "FAILED": "fail", - "SKIPPED": "skip", - "ERROR": "error", - } - status = status_map.get(match.group(1), "error") - name = match.group(2) - - results.append(TestResult(name=name, status=status)) - - total += 1 - if status == "pass": - passed += 1 - elif status == "fail": - failed += 1 - elif status == "skip": - skipped += 1 - else: - errors += 1 - - return Report( - kind="test", - tool="pytest", - passed=exit_code == 0, - total=total, - passed_count=passed, - failed_count=failed, - skipped_count=skipped, - error_count=errors, - results=results, - raw_output=output, - exit_code=exit_code, - ) - - def _parse_generic_output( - self, output: str, exit_code: int, kind: str, tool: str - ) -> Report: - """Generic output parser.""" - # Try JSON parsing first - try: - data = json.loads(output) - # Handle various JSON formats - if isinstance(data, dict): - if "testResults" in data: # Jest - results = [] - for suite in data.get("testResults", []): - for test in suite.get("assertionResults", []): - results.append( - TestResult( - name=test.get("fullName", ""), - status=( - "pass" - if test.get("status") == "passed" - else "fail" - ), - ) - ) - return Report( - kind=kind, - tool=tool, - passed=data.get("success", exit_code == 0), - total=len(results), - passed_count=sum(1 for r in results if r.status == "pass"), - failed_count=sum(1 for r in results if r.status == "fail"), - results=results, - raw_output=output, - exit_code=exit_code, - ) - except json.JSONDecodeError: - pass - - # Fallback: line-based parsing - lines = output.strip().split("\n") - error_lines = [ - line for line in lines if "error" in line.lower() or "fail" in line.lower() - ] - - return Report( - kind=kind, - tool=tool, - passed=exit_code == 0 and len(error_lines) == 0, - total=len(lines), - failed_count=len(error_lines), - raw_output=output, - exit_code=exit_code, - ) - - def _register_test_actions(self): - """Register all test actions.""" - - @self.action("run", "Execute check/build/test loop") - async def run( - ctx: MCPContext, - kind: str = "test", - selector: str | None = None, - cwd: str | None = None, - tool: str | None = None, - timeout: int = 300, - ) -> dict: - """Run validation loop and return structured Report. - - Args: - kind: check | build | test - - check: Fast incremental (lint/typecheck) โ†’ Diagnostics - - build: Whole-project compilation โ†’ BuildResult - - test: Runtime behavior validation โ†’ TestResult - selector: File/target selector (e.g., "test_foo.py", "src/") - cwd: Working directory - tool: Specific tool to use (auto-detect if not specified) - timeout: Timeout in seconds - - Returns: - Report with pass/fail, counts, results - - Effect: NONDETERMINISTIC_EFFECT - """ - work_dir = cwd or self.cwd - - # Detect or use specified tool - if tool: - if kind == "test": - tools = TEST_RUNNERS - elif kind == "build": - tools = BUILD_TOOLS - else: - tools = CHECK_TOOLS - if tool not in tools: - raise InvalidParamsError(f"Unknown tool: {tool}") - cmd = tools[tool]["cmd"].copy() - tool_name = tool - else: - detected = self._detect_runner(work_dir, kind) - if not detected: - raise ToolError( - code="NOT_FOUND", - message=f"No {kind} runner detected in {work_dir}", - ) - tool_name, cmd = detected - cmd = cmd.copy() - - # Add selector if provided - if selector: - cmd.append(selector) - - # Run command - try: - proc = await asyncio.create_subprocess_exec( - *cmd, - cwd=work_dir, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await asyncio.wait_for( - proc.communicate(), - timeout=timeout, - ) - - output = stdout.decode("utf-8", errors="replace") - if stderr: - output += "\n" + stderr.decode("utf-8", errors="replace") - - exit_code = proc.returncode or 0 - - except asyncio.TimeoutError: - raise ToolError( - code="TIMEOUT", message=f"{kind} timed out after {timeout}s" - ) - except FileNotFoundError as e: - raise ToolError(code="NOT_FOUND", message=f"Tool not found: {e}") - - # Parse output - if tool_name == "pytest": - report = self._parse_pytest_output(output, exit_code) - else: - report = self._parse_generic_output(output, exit_code, kind, tool_name) - - return { - "report": { - "kind": report.kind, - "tool": report.tool, - "passed": report.passed, - "total": report.total, - "passed_count": report.passed_count, - "failed_count": report.failed_count, - "skipped_count": report.skipped_count, - "error_count": report.error_count, - "duration_ms": report.duration_ms, - "results": [ - { - "name": r.name, - "status": r.status, - "message": r.message, - } - for r in report.results[:50] # Limit results - ], - }, - "pass": report.passed, - "exit_code": exit_code, - "raw_ref": content_hash(output), # Reference to full output - } - - @self.action("detect", "Detect available test/lint tools") - async def detect( - ctx: MCPContext, - cwd: str | None = None, - ) -> dict: - """Detect available test runners and lint tools. - - Returns: - Dict of detected tools by category - """ - work_dir = cwd or self.cwd - detected = { - "check": [], # Fast incremental (lint/typecheck) - "build": [], # Whole-project compilation - "test": [], # Runtime behavior validation - } - - # Detect test runners - for name, config in TEST_RUNNERS.items(): - for detect_file in config["detect"]: - if "*" in detect_file: - if list(Path(work_dir).glob(detect_file)): - detected["test"].append(name) - break - else: - if (Path(work_dir) / detect_file).exists(): - detected["test"].append(name) - break - - # Detect build tools - for name, config in BUILD_TOOLS.items(): - for detect_file in config["detect"]: - if (Path(work_dir) / detect_file).exists(): - detected["build"].append(name) - break - - # Detect check tools (lint/typecheck) - for name, config in CHECK_TOOLS.items(): - for detect_file in config["detect"]: - if (Path(work_dir) / detect_file).exists(): - detected["check"].append(name) - break - - return { - "detected": detected, - "cwd": work_dir, - } - - def register(self, mcp_server: FastMCP) -> None: - """Register as 'test' tool with MCP server.""" - tool_name = self.name - tool_description = self.description - - @mcp_server.tool(name=tool_name, description=tool_description) - async def handler( - ctx: MCPContext, - action: str = "help", - **kwargs: Any, - ) -> str: - result = await self.call(ctx, action=action, **kwargs) - return json.dumps(result, indent=2, default=str) - - -# Singleton -test_tool = TestTool diff --git a/pkg/hanzo-tools-test/pyproject.toml b/pkg/hanzo-tools-test/pyproject.toml deleted file mode 100644 index 95622eca3..000000000 --- a/pkg/hanzo-tools-test/pyproject.toml +++ /dev/null @@ -1,66 +0,0 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[project] -name = "hanzo-tools-test" -version = "0.1.0" -description = "Test and validation tools for Hanzo AI (HIP-0300)" -readme = "README.md" -license = "MIT" -requires-python = ">=3.12" -authors = [ - { name = "Hanzo AI Team", email = "ai@hanzo.ai" }, -] -keywords = [ - "hanzo", - "mcp", - "tools", - "test", - "validation", - "lint", - "typecheck", -] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", -] -dependencies = [ - "hanzo-tools-core>=0.1.0", - "mcp>=1.0.0", -] - -[project.optional-dependencies] -dev = [ - "pytest>=8.0.0", - "pytest-asyncio>=0.23.0", - "ruff>=0.1.0", -] - -[project.entry-points."hanzo.tools"] -test = "hanzo_tools.test:TOOLS" - -[project.urls] -Homepage = "https://github.com/hanzoai/python-sdk" -Documentation = "https://docs.hanzo.ai" -Repository = "https://github.com/hanzoai/python-sdk" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] - -[tool.ruff] -line-length = 100 -target-version = "py310" - -[tool.ruff.lint] -select = ["E", "F", "I", "UP"] -ignore = ["E501"] - -[tool.pytest.ini_options] -asyncio_mode = "auto" -testpaths = ["tests"] diff --git a/pkg/hanzo-tools-todo/README.md b/pkg/hanzo-tools-todo/README.md deleted file mode 100644 index 27e8cbf29..000000000 --- a/pkg/hanzo-tools-todo/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# hanzo-tools-todo - -Task management tools for Hanzo MCP. - -## Installation - -```bash -pip install hanzo-tools-todo -``` - -## Tools - -### todo - Task Management -Manage todo items with status tracking. - -**List todos:** -```python -todo() # List all -todo(filter="in_progress") # Filter by status -``` - -**Add todo:** -```python -todo(action="add", content="Implement feature X") -todo(action="add", content="Critical bug", priority="high") -``` - -**Update todo:** -```python -todo(action="update", id="abc123", status="completed") -todo(action="update", id="abc123", content="Updated description") -``` - -**Remove todo:** -```python -todo(action="remove", id="abc123") -todo(action="clear") # Remove all -``` - -## Status Values - -- `pending` - Not started -- `in_progress` - Currently working -- `completed` - Done - -## Priority Values - -- `high` - Urgent -- `medium` - Normal (default) -- `low` - Can wait - -## License - -MIT diff --git a/pkg/hanzo-tools-todo/hanzo_tools/__init__.py b/pkg/hanzo-tools-todo/hanzo_tools/__init__.py deleted file mode 100644 index 004279ba9..000000000 --- a/pkg/hanzo-tools-todo/hanzo_tools/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Hanzo tools namespace package.""" - -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-todo/hanzo_tools/todo/__init__.py b/pkg/hanzo-tools-todo/hanzo_tools/todo/__init__.py deleted file mode 100644 index aea823cba..000000000 --- a/pkg/hanzo-tools-todo/hanzo_tools/todo/__init__.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Todo tools for Hanzo MCP. - -This package provides task management tools: -- TodoTool: Unified todo management with add, update, remove, list, clear operations -""" - -from mcp.server import FastMCP - -from hanzo_tools.core import BaseTool, ToolRegistry -from hanzo_tools.todo.base import TodoStorage, TodoBaseTool -from hanzo_tools.todo.tasks_tool import TasksTool - -# Backward compat -TodoTool = TasksTool - -__all__ = [ - "TasksTool", - "TodoTool", - "TodoStorage", - "TodoBaseTool", - "get_todo_tools", - "register_todo_tools", - "TOOLS", -] - - -def get_todo_tools() -> list[BaseTool]: - """Create instances of all todo tools. - - Returns: - List of todo tool instances - """ - return [TasksTool()] - - -def register_todo_tools( - mcp_server: FastMCP, - enabled_tools: dict[str, bool] | None = None, -) -> list[BaseTool]: - """Register todo tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - enabled_tools: Dictionary of individual tool enable states (default: None) - - Returns: - List of registered tools - """ - tools = get_todo_tools() - ToolRegistry.register_tools(mcp_server, tools) - return tools - - -# TOOLS list for entry point discovery -TOOLS = [TasksTool] diff --git a/pkg/hanzo-tools-todo/hanzo_tools/todo/base.py b/pkg/hanzo-tools-todo/hanzo_tools/todo/base.py deleted file mode 100644 index fa72447dc..000000000 --- a/pkg/hanzo-tools-todo/hanzo_tools/todo/base.py +++ /dev/null @@ -1,320 +0,0 @@ -"""Base functionality for todo tools. - -This module provides common functionality for todo tools, including in-memory storage -for managing todo lists across different Claude Desktop sessions. -""" - -import re -import time -from abc import ABC -from typing import Any, final - -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, ToolContext, create_tool_context - - -@final -class TodoStorage: - """In-memory storage for todo lists, separated by session ID. - - This class provides persistent storage for the lifetime of the MCP server process, - allowing different Claude Desktop conversations to maintain separate todo lists. - Each session stores both the todo list and a timestamp of when it was last updated. - """ - - # Class-level storage shared across all tool instances - # Structure: {session_id: {"todos": [...], "last_updated": timestamp}} - _sessions: dict[str, dict[str, Any]] = {} - - @classmethod - def get_todos(cls, session_id: str) -> list[dict[str, Any]]: - """Get the todo list for a specific session. - - Args: - session_id: Unique identifier for the Claude Desktop session - - Returns: - List of todo items for the session, empty list if session doesn't exist - """ - session_data = cls._sessions.get(session_id, {}) - return session_data.get("todos", []) - - @classmethod - def set_todos(cls, session_id: str, todos: list[dict[str, Any]]) -> None: - """Set the todo list for a specific session. - - Args: - session_id: Unique identifier for the Claude Desktop session - todos: Complete list of todo items to store - """ - cls._sessions[session_id] = {"todos": todos, "last_updated": time.time()} - - @classmethod - def get_session_count(cls) -> int: - """Get the number of active sessions. - - Returns: - Number of sessions with stored todos - """ - return len(cls._sessions) - - @classmethod - def get_all_session_ids(cls) -> list[str]: - """Get all active session IDs. - - Returns: - List of all session IDs with stored todos - """ - return list(cls._sessions.keys()) - - @classmethod - def delete_session(cls, session_id: str) -> bool: - """Delete a session and its todos. - - Args: - session_id: Session ID to delete - - Returns: - True if session was deleted, False if it didn't exist - """ - if session_id in cls._sessions: - del cls._sessions[session_id] - return True - return False - - @classmethod - def get_session_last_updated(cls, session_id: str) -> float | None: - """Get the last updated timestamp for a session. - - Args: - session_id: Session ID to check - - Returns: - Timestamp when session was last updated, or None if session doesn't exist - """ - session_data = cls._sessions.get(session_id) - if session_data: - return session_data.get("last_updated") - return None - - @classmethod - def find_latest_active_session(cls) -> str | None: - """Find the chronologically latest session with unfinished todos. - - Returns the session ID of the most recently updated session that has unfinished todos. - Returns None if no sessions have unfinished todos. - - Returns: - Session ID with unfinished todos that was most recently updated, or None if none found - """ - latest_session = None - latest_timestamp = 0 - - for session_id, session_data in cls._sessions.items(): - todos = session_data.get("todos", []) - # Check for unfinished todos - has_unfinished = any( - todo.get("status") in ["pending", "in_progress"] for todo in todos - ) - if has_unfinished: - last_updated = session_data.get("last_updated", 0) - if last_updated > latest_timestamp: - latest_timestamp = last_updated - latest_session = session_id - - return latest_session - - -class TodoBaseTool(BaseTool, ABC): - """Base class for todo tools. - - Provides common functionality for working with todo lists, including - session ID validation and todo structure validation. - """ - - def create_tool_context(self, ctx: MCPContext) -> ToolContext: - """Create a tool context with the tool name. - - Args: - ctx: MCP context - - Returns: - Tool context - """ - tool_ctx = create_tool_context(ctx) - return tool_ctx - - def set_tool_context_info(self, tool_ctx: ToolContext) -> None: - """Set the tool info on the context. - - Args: - tool_ctx: Tool context - """ - tool_ctx.set_tool_info(self.name) - - def normalize_todo_item(self, todo: dict[str, Any], index: int) -> dict[str, Any]: - """Normalize a single todo item by auto-generating missing required fields. - - Args: - todo: Todo item to normalize - index: Index of the todo item for generating unique IDs - - Returns: - Normalized todo item with all required fields - """ - normalized = dict(todo) # Create a copy - - # Auto-generate ID if missing or normalize existing ID to string - if "id" not in normalized or not str(normalized.get("id")).strip(): - normalized["id"] = f"todo-{index + 1}" - else: - # Ensure ID is stored as a string for consistency - normalized["id"] = str(normalized["id"]).strip() - - # Auto-generate priority if missing (but don't fix invalid values) - if "priority" not in normalized: - normalized["priority"] = "medium" - - # Ensure status defaults to pending if missing (but don't fix invalid values) - if "status" not in normalized: - normalized["status"] = "pending" - - return normalized - - def normalize_todos_list(self, todos: list[dict[str, Any]]) -> list[dict[str, Any]]: - """Normalize a list of todo items by auto-generating missing fields. - - Args: - todos: List of todo items to normalize - - Returns: - Normalized list of todo items with all required fields - """ - if not isinstance(todos, list): - return [] # Return empty list for invalid input - - normalized_todos = [] - used_ids = set() - - for i, todo in enumerate(todos): - if not isinstance(todo, dict): - continue # Skip invalid items - - normalized = self.normalize_todo_item(todo, i) - - # Don't auto-fix duplicate IDs - let validation catch them - used_ids.add(normalized["id"]) - normalized_todos.append(normalized) - - return normalized_todos - - def validate_session_id(self, session_id: str | None) -> tuple[bool, str]: - """Validate session ID format and security. - - Args: - session_id: Session ID to validate - - Returns: - Tuple of (is_valid, error_message) - """ - # Check for None or empty first - if session_id is None or session_id == "": - return False, "Session ID is required but was empty" - - # Check if it's a string - if not isinstance(session_id, str): - return False, "Session ID must be a string" - - # Check length (reasonable bounds) - if len(session_id) < 5: - return False, "Session ID too short (minimum 5 characters)" - - if len(session_id) > 100: - return False, "Session ID too long (maximum 100 characters)" - - # Check format - allow alphanumeric, hyphens, underscores - # This prevents path traversal and other security issues - if not re.match(r"^[a-zA-Z0-9_-]+$", session_id): - return ( - False, - "Session ID can only contain alphanumeric characters, hyphens, and underscores", - ) - - return True, "" - - def validate_todo_item(self, todo: dict[str, Any]) -> tuple[bool, str]: - """Validate a single todo item structure. - - Args: - todo: Todo item to validate - - Returns: - Tuple of (is_valid, error_message) - """ - if not isinstance(todo, dict): - return False, "Todo item must be an object" - - # Check required fields - required_fields = ["content", "status", "priority", "id"] - for field in required_fields: - if field not in todo: - return False, f"Todo item missing required field: {field}" - - # Validate content - content = todo.get("content") - if not isinstance(content, str) or not content.strip(): - return False, "Todo content must be a non-empty string" - - # Validate status - valid_statuses = ["pending", "in_progress", "completed"] - status = todo.get("status") - if status not in valid_statuses: - return False, f"Todo status must be one of: {', '.join(valid_statuses)}" - - # Validate priority - valid_priorities = ["high", "medium", "low"] - priority = todo.get("priority") - if priority not in valid_priorities: - return False, f"Todo priority must be one of: {', '.join(valid_priorities)}" - - # Validate ID - todo_id = todo.get("id") - if todo_id is None: - return False, "Todo id is required" - - # Accept string, int, or float IDs - if not isinstance(todo_id, (str, int, float)): - return False, "Todo id must be a string, integer, or number" - - # Convert to string and check if it's non-empty after stripping - todo_id_str = str(todo_id).strip() - if not todo_id_str: - return False, "Todo id must not be empty" - - return True, "" - - def validate_todos_list(self, todos: list[dict[str, Any]]) -> tuple[bool, str]: - """Validate a list of todo items. - - Args: - todos: List of todo items to validate - - Returns: - Tuple of (is_valid, error_message) - """ - if not isinstance(todos, list): - return False, "Todos must be a list" - - # Check each todo item - for i, todo in enumerate(todos): - is_valid, error_msg = self.validate_todo_item(todo) - if not is_valid: - return False, f"Todo item {i}: {error_msg}" - - # Check for duplicate IDs - todo_ids = [todo.get("id") for todo in todos] - if len(todo_ids) != len(set(todo_ids)): - return False, "Todo items must have unique IDs" - - return True, "" diff --git a/pkg/hanzo-tools-todo/hanzo_tools/todo/tasks_tool.py b/pkg/hanzo-tools-todo/hanzo_tools/todo/tasks_tool.py deleted file mode 100644 index 0b86a44ca..000000000 --- a/pkg/hanzo-tools-todo/hanzo_tools/todo/tasks_tool.py +++ /dev/null @@ -1,561 +0,0 @@ -"""Unified todo tool.""" - -import uuid -from typing import ( - Any, - Dict, - List, - Unpack, - Optional, - Annotated, - TypedDict, - final, - override, -) -from datetime import datetime - -from pydantic import Field -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import auto_timeout -from hanzo_tools.todo.base import TodoStorage, TodoBaseTool - -# Default session ID for the unified todo tool -DEFAULT_SESSION_ID = "default-session" - -# Parameter types -Action = Annotated[ - str, - Field( - description="Action to perform: list (default), add, update, remove, clear", - default="list", - ), -] - -Content = Annotated[ - Optional[str], - Field( - description="Todo content for add/update", - default=None, - ), -] - -TodoId = Annotated[ - Optional[str], - Field( - description="Todo ID for update/remove", - default=None, - ), -] - -Status = Annotated[ - Optional[str], - Field( - description="Status: pending, in_progress, completed", - default="pending", - ), -] - -Priority = Annotated[ - Optional[str], - Field( - description="Priority: high, medium, low", - default="medium", - ), -] - -Filter = Annotated[ - Optional[str], - Field( - description="Filter todos by status for list action", - default=None, - ), -] - - -class TodoParams(TypedDict, total=False): - """Parameters for todo tool.""" - - action: str - content: Optional[str] - id: Optional[str] - status: Optional[str] - priority: Optional[str] - filter: Optional[str] - - -@final -class TasksTool(TodoBaseTool): - """Unified todo management tool.""" - - name = "tasks" - - def read_todos(self) -> list[Dict[str, Any]]: - """Read todos from storage using default session.""" - return TodoStorage.get_todos(DEFAULT_SESSION_ID) - - def write_todos(self, todos: list[Dict[str, Any]]) -> None: - """Write todos to storage using default session.""" - TodoStorage.set_todos(DEFAULT_SESSION_ID, todos) - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Manage todos. Actions: list (default), add, update, remove, clear. - -Usage: -todo -todo "Fix the bug in authentication" -todo --action update --id abc123 --status completed -todo --action remove --id abc123 -todo --filter in_progress -""" - - @override - @auto_timeout("todo") - async def call( - self, - ctx: MCPContext, - **params: Unpack[TodoParams], - ) -> str: - """Execute todo operation.""" - tool_ctx = self.create_tool_context(ctx) - - # Extract action - action = params.get("action", "list") - - # Route to appropriate handler - if action == "list": - return await self._handle_list(params.get("filter"), tool_ctx) - elif action == "add": - return await self._handle_add(params, tool_ctx) - elif action == "update": - return await self._handle_update(params, tool_ctx) - elif action in ("remove", "delete"): - return await self._handle_remove(params.get("id"), tool_ctx) - elif action == "clear": - return await self._handle_clear(params.get("filter"), tool_ctx) - elif action == "stats": - return await self._handle_stats(tool_ctx) - elif action == "search": - return await self._handle_search(params, tool_ctx) - elif action == "batch": - return await self._handle_batch(params, tool_ctx) - elif action == "archive": - return await self._handle_archive(params.get("id"), tool_ctx) - elif action == "move": - return await self._handle_move(params, tool_ctx) - elif action == "prioritize": - return await self._handle_prioritize(params, tool_ctx) - elif action == "assign": - return await self._handle_assign(params, tool_ctx) - elif action == "subtasks": - return await self._handle_subtasks(params, tool_ctx) - elif action == "notes": - return await self._handle_notes(params, tool_ctx) - elif action == "export": - return await self._handle_export(params, tool_ctx) - elif action == "import": - return await self._handle_import(params, tool_ctx) - else: - return f"Error: Unknown action '{action}'. Valid actions: list, add, update, remove, delete, clear, stats, search, batch, archive, move, prioritize, assign, subtasks, notes, export, import" - - async def _handle_list(self, filter_status: Optional[str], tool_ctx) -> str: - """List todos.""" - todos = self.read_todos() - - if not todos: - return "No todos found. Use 'todo \"Your task here\"' to add one." - - # Apply filter if specified - if filter_status: - todos = [t for t in todos if t.get("status") == filter_status] - if not todos: - return f"No todos with status '{filter_status}'" - - # Group by status - by_status = {} - for todo in todos: - status = todo.get("status", "pending") - if status not in by_status: - by_status[status] = [] - by_status[status].append(todo) - - # Format output - output = ["=== Todo List ==="] - - # Show in order: in_progress, pending, completed - for status in ["in_progress", "pending", "completed"]: - if status in by_status: - output.append(f"\n{status.replace('_', ' ').title()}:") - for todo in by_status[status]: - priority_icon = {"high": "๐Ÿ”ด", "medium": "๐ŸŸก", "low": "๐ŸŸข"}.get( - todo.get("priority", "medium"), "โšช" - ) - output.append( - f"{priority_icon} [{todo['id'][:8]}] {todo['content']}" - ) - - # Summary - output.append( - f"\nTotal: {len(todos)} | In Progress: {len(by_status.get('in_progress', []))} | Pending: {len(by_status.get('pending', []))} | Completed: {len(by_status.get('completed', []))}" - ) - - return "\n".join(output) - - async def _handle_add(self, params: Dict[str, Any], tool_ctx) -> str: - """Add new todo.""" - content = params.get("content") - if not content: - return "Error: content is required for add action" - - todos = self.read_todos() - - new_todo = { - "id": str(uuid.uuid4()), - "content": content, - "status": params.get("status", "pending"), - "priority": params.get("priority", "medium"), - "created_at": datetime.now().isoformat(), - } - - todos.append(new_todo) - self.write_todos(todos) - - await tool_ctx.info(f"Added todo: {content}") - return f"Added todo [{new_todo['id'][:8]}]: {content}" - - async def _handle_update(self, params: Dict[str, Any], tool_ctx) -> str: - """Update existing todo.""" - todo_id = params.get("id") - if not todo_id: - return "Error: id is required for update action" - - todos = self.read_todos() - - # Find todo (support partial ID match) - todo_found = None - for todo in todos: - if todo["id"].startswith(todo_id): - todo_found = todo - break - - if not todo_found: - return f"Error: Todo with ID '{todo_id}' not found" - - # Update fields - if params.get("content"): - todo_found["content"] = params["content"] - if params.get("status"): - todo_found["status"] = params["status"] - if params.get("priority"): - todo_found["priority"] = params["priority"] - - todo_found["updated_at"] = datetime.now().isoformat() - - self.write_todos(todos) - - await tool_ctx.info(f"Updated todo: {todo_found['content']}") - return f"Updated todo [{todo_found['id'][:8]}]: {todo_found['content']} (status: {todo_found['status']})" - - async def _handle_remove(self, todo_id: Optional[str], tool_ctx) -> str: - """Remove todo.""" - if not todo_id: - return "Error: id is required for remove action" - - todos = self.read_todos() - - # Find and remove (support partial ID match) - removed = None - for i, todo in enumerate(todos): - if todo["id"].startswith(todo_id): - removed = todos.pop(i) - break - - if not removed: - return f"Error: Todo with ID '{todo_id}' not found" - - self.write_todos(todos) - - await tool_ctx.info(f"Removed todo: {removed['content']}") - return f"Removed todo [{removed['id'][:8]}]: {removed['content']}" - - async def _handle_stats(self, tool_ctx) -> str: - """Get todo statistics.""" - todos = self.read_todos() - if not todos: - return "No todos found." - - by_status = {} - by_priority = {} - for todo in todos: - s = todo.get("status", "pending") - p = todo.get("priority", "medium") - by_status[s] = by_status.get(s, 0) + 1 - by_priority[p] = by_priority.get(p, 0) + 1 - - lines = [f"Total: {len(todos)}"] - lines.append("By status:") - for s in ["in_progress", "pending", "completed"]: - if s in by_status: - lines.append(f" {s}: {by_status[s]}") - lines.append("By priority:") - for p in ["high", "medium", "low"]: - if p in by_priority: - lines.append(f" {p}: {by_priority[p]}") - return "\n".join(lines) - - async def _handle_search(self, params: Dict[str, Any], tool_ctx) -> str: - """Search todos by content.""" - query = params.get("content") or params.get("query") or "" - if not query: - return "Error: search query required (use content or query param)" - todos = self.read_todos() - query_lower = query.lower() - matches = [t for t in todos if query_lower in t.get("content", "").lower()] - if not matches: - return f"No todos matching '{query}'" - lines = [f"Found {len(matches)} matching todo(s):"] - for t in matches: - lines.append(f" [{t['id'][:8]}] {t['content']} ({t.get('status', 'pending')})") - return "\n".join(lines) - - async def _handle_batch(self, params: Dict[str, Any], tool_ctx) -> str: - """Batch update todos by status.""" - from_status = params.get("filter") or params.get("from_status") - to_status = params.get("status") or params.get("to_status") - if not from_status or not to_status: - return "Error: batch requires filter/from_status and status/to_status" - todos = self.read_todos() - count = 0 - for t in todos: - if t.get("status") == from_status: - t["status"] = to_status - t["updated_at"] = datetime.now().isoformat() - count += 1 - self.write_todos(todos) - return f"Batch updated {count} todo(s) from '{from_status}' to '{to_status}'" - - async def _handle_archive(self, todo_id: Optional[str], tool_ctx) -> str: - """Archive completed todos or a specific todo.""" - todos = self.read_todos() - if todo_id: - for t in todos: - if t["id"].startswith(todo_id): - t["status"] = "archived" - t["updated_at"] = datetime.now().isoformat() - self.write_todos(todos) - return f"Archived todo [{t['id'][:8]}]" - return f"Error: Todo '{todo_id}' not found" - # Archive all completed - count = 0 - for t in todos: - if t.get("status") == "completed": - t["status"] = "archived" - t["updated_at"] = datetime.now().isoformat() - count += 1 - self.write_todos(todos) - return f"Archived {count} completed todo(s)" - - async def _handle_move(self, params: Dict[str, Any], tool_ctx) -> str: - """Move a todo to a different position.""" - todo_id = params.get("id") - position = params.get("position") - if not todo_id: - return "Error: id required for move" - todos = self.read_todos() - idx = None - for i, t in enumerate(todos): - if t["id"].startswith(todo_id): - idx = i - break - if idx is None: - return f"Error: Todo '{todo_id}' not found" - item = todos.pop(idx) - try: - pos = int(position) if position is not None else 0 - except (ValueError, TypeError): - pos = 0 - pos = max(0, min(pos, len(todos))) - todos.insert(pos, item) - self.write_todos(todos) - return f"Moved todo [{item['id'][:8]}] to position {pos}" - - async def _handle_prioritize(self, params: Dict[str, Any], tool_ctx) -> str: - """Change todo priority.""" - todo_id = params.get("id") - priority = params.get("priority") - if not todo_id or not priority: - return "Error: id and priority required" - todos = self.read_todos() - for t in todos: - if t["id"].startswith(todo_id): - t["priority"] = priority - t["updated_at"] = datetime.now().isoformat() - self.write_todos(todos) - return f"Set priority of [{t['id'][:8]}] to '{priority}'" - return f"Error: Todo '{todo_id}' not found" - - async def _handle_assign(self, params: Dict[str, Any], tool_ctx) -> str: - """Assign a todo to someone.""" - todo_id = params.get("id") - assignee = params.get("assignee") or params.get("content") - if not todo_id: - return "Error: id required for assign" - todos = self.read_todos() - for t in todos: - if t["id"].startswith(todo_id): - t["assignee"] = assignee or "" - t["updated_at"] = datetime.now().isoformat() - self.write_todos(todos) - return f"Assigned [{t['id'][:8]}] to '{assignee or 'unassigned'}'" - return f"Error: Todo '{todo_id}' not found" - - async def _handle_subtasks(self, params: Dict[str, Any], tool_ctx) -> str: - """Manage subtasks of a todo.""" - todo_id = params.get("id") - content = params.get("content") - if not todo_id: - return "Error: id required for subtasks" - todos = self.read_todos() - for t in todos: - if t["id"].startswith(todo_id): - if "subtasks" not in t: - t["subtasks"] = [] - if content: - t["subtasks"].append({ - "id": str(uuid.uuid4()), - "content": content, - "status": "pending", - "created_at": datetime.now().isoformat(), - }) - self.write_todos(todos) - return f"Added subtask to [{t['id'][:8]}]: {content}" - else: - if not t["subtasks"]: - return f"No subtasks for [{t['id'][:8]}]" - lines = [f"Subtasks for [{t['id'][:8]}]:"] - for st in t["subtasks"]: - lines.append(f" [{st['id'][:8]}] {st['content']} ({st.get('status', 'pending')})") - return "\n".join(lines) - return f"Error: Todo '{todo_id}' not found" - - async def _handle_notes(self, params: Dict[str, Any], tool_ctx) -> str: - """Add or view notes on a todo.""" - todo_id = params.get("id") - content = params.get("content") - if not todo_id: - return "Error: id required for notes" - todos = self.read_todos() - for t in todos: - if t["id"].startswith(todo_id): - if "notes" not in t: - t["notes"] = [] - if content: - t["notes"].append({ - "text": content, - "created_at": datetime.now().isoformat(), - }) - self.write_todos(todos) - return f"Added note to [{t['id'][:8]}]" - else: - if not t["notes"]: - return f"No notes for [{t['id'][:8]}]" - lines = [f"Notes for [{t['id'][:8]}]:"] - for n in t["notes"]: - lines.append(f" [{n.get('created_at', '?')}] {n['text']}") - return "\n".join(lines) - return f"Error: Todo '{todo_id}' not found" - - async def _handle_export(self, params: Dict[str, Any], tool_ctx) -> str: - """Export todos as JSON.""" - import json - todos = self.read_todos() - fmt = params.get("format") or "json" - if fmt == "json": - return json.dumps({"todos": todos}, indent=2, default=str) - elif fmt == "markdown": - lines = ["# Todos"] - for t in todos: - status_icon = {"completed": "x", "in_progress": "~", "pending": " "}.get(t.get("status", "pending"), " ") - lines.append(f"- [{status_icon}] {t['content']}") - return "\n".join(lines) - return json.dumps({"todos": todos}, indent=2, default=str) - - async def _handle_import(self, params: Dict[str, Any], tool_ctx) -> str: - """Import todos from JSON.""" - import json - content = params.get("content") - if not content: - return "Error: content (JSON) required for import" - try: - data = json.loads(content) - items = data if isinstance(data, list) else data.get("todos", data.get("items", [])) - todos = self.read_todos() - count = 0 - for item in items: - if isinstance(item, dict) and "content" in item: - if "id" not in item: - item["id"] = str(uuid.uuid4()) - if "status" not in item: - item["status"] = "pending" - if "priority" not in item: - item["priority"] = "medium" - item["created_at"] = datetime.now().isoformat() - todos.append(item) - count += 1 - self.write_todos(todos) - return f"Imported {count} todo(s)" - except json.JSONDecodeError as e: - return f"Error: Invalid JSON: {e}" - - async def _handle_clear(self, filter_status: Optional[str], tool_ctx) -> str: - """Clear todos.""" - todos = self.read_todos() - - if filter_status: - # Clear only todos with specific status - original_count = len(todos) - todos = [t for t in todos if t.get("status") != filter_status] - removed_count = original_count - len(todos) - - if removed_count == 0: - return f"No todos with status '{filter_status}' to clear" - - self.write_todos(todos) - return f"Cleared {removed_count} todo(s) with status '{filter_status}'" - else: - # Clear all - if not todos: - return "No todos to clear" - - count = len(todos) - self.write_todos([]) - return f"Cleared all {count} todo(s)" - - @override - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server.""" - tool_self = self # Create a reference to self for use in the closure - - @mcp_server.tool(name=self.name, description=self.description) - async def todo( - action: Action = "list", - content: Content = None, - id: TodoId = None, - status: Status = None, - priority: Priority = None, - filter: Filter = None, - ctx: MCPContext = None, - ) -> str: - return await tool_self.call( - ctx, - action=action, - content=content, - id=id, - status=status, - priority=priority, - filter=filter, - ) diff --git a/pkg/hanzo-tools-todo/pyproject.toml b/pkg/hanzo-tools-todo/pyproject.toml deleted file mode 100644 index 46a92e8bf..000000000 --- a/pkg/hanzo-tools-todo/pyproject.toml +++ /dev/null @@ -1,25 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-todo" -version = "0.2.0" -description = "Task management tools for Hanzo MCP" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "tools", "todo", "task", "mcp", "ai"] -dependencies = [ - "hanzo-tools>=0.3.0", - "mcp>=1.25.0", - "pydantic>=2.12.5", -] - -[project.entry-points."hanzo.tools"] -todo = "hanzo_tools.todo:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] diff --git a/pkg/hanzo-tools-todo/tests/test_todo_tools.py b/pkg/hanzo-tools-todo/tests/test_todo_tools.py deleted file mode 100644 index fc586a8f0..000000000 --- a/pkg/hanzo-tools-todo/tests/test_todo_tools.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Tests for hanzo-tools-todo.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import todo - - assert todo is not None - - def test_import_tools(self): - from hanzo_tools.todo import TOOLS - - assert len(TOOLS) > 0 - - def test_import_todo_tool(self): - from hanzo_tools.todo import TodoTool - - assert TodoTool.name == "todo" - - -class TestTodoTool: - """Tests for TodoTool.""" - - @pytest.fixture - def tool(self): - from hanzo_tools.todo import TodoTool - - return TodoTool() - - def test_has_description(self, tool): - assert tool.description - assert "todo" in tool.description.lower() diff --git a/pkg/hanzo-tools-ui/Dockerfile b/pkg/hanzo-tools-ui/Dockerfile deleted file mode 100644 index cd57b9650..000000000 --- a/pkg/hanzo-tools-ui/Dockerfile +++ /dev/null @@ -1,28 +0,0 @@ -FROM python:3.12-slim AS builder - -WORKDIR /build -COPY pkg/hanzo-tools-ui/ ./pkg/hanzo-tools-ui/ -COPY pkg/hanzo-tools-core/ ./pkg/hanzo-tools-core/ - -RUN pip install --no-cache-dir \ - ./pkg/hanzo-tools-core \ - "./pkg/hanzo-tools-ui[server]" - -FROM python:3.12-slim - -RUN useradd -m -r hanzo -WORKDIR /app - -COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages -COPY --from=builder /usr/local/bin/hanzo-ui-registry /usr/local/bin/ - -USER hanzo -EXPOSE 8787 - -HEALTHCHECK --interval=30s --timeout=5s --retries=3 \ - CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8787/api/health')" - -ENV REGISTRY_HOST=0.0.0.0 \ - REGISTRY_PORT=8787 - -CMD ["python", "-m", "uvicorn", "hanzo_tools.ui.registry.server:app", "--host", "0.0.0.0", "--port", "8787"] diff --git a/pkg/hanzo-tools-ui/README.md b/pkg/hanzo-tools-ui/README.md deleted file mode 100644 index f015f0aa6..000000000 --- a/pkg/hanzo-tools-ui/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# hanzo-tools-ui - -UI component registry tools for Hanzo AI MCP. - -Browse, search, install, and manage UI components from Hanzo and other registries (shadcn/ui, Vue, Svelte, React Native). - -## Install - -```bash -pip install hanzo-tools-ui -``` - -## Usage - -```python -from hanzo_tools.ui import UiTool, TOOLS -``` diff --git a/pkg/hanzo-tools-ui/hanzo_tools/__init__.py b/pkg/hanzo-tools-ui/hanzo_tools/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-tools-ui/hanzo_tools/ui/__init__.py b/pkg/hanzo-tools-ui/hanzo_tools/ui/__init__.py deleted file mode 100644 index 76c351973..000000000 --- a/pkg/hanzo-tools-ui/hanzo_tools/ui/__init__.py +++ /dev/null @@ -1,67 +0,0 @@ -"""UI component registry tools for Hanzo AI (HIP-0300). - -Local-first: when ~/work/hanzo/ui exists, reads directly from disk. -Falls back to GitHub API for remote registries. - -Tools: -- ui: Unified UI component tool - - list_components: List available components - - get_component: Get component source code - - get_demo: Get component demo/example - - get_metadata: Get component metadata - - list_blocks: List UI blocks - - get_block: Get block implementation - - search: Search components - - get_structure: Browse repository structure - - install: Install component via CLI - - set_framework: Switch active framework - - get_framework: Show current and available frameworks - - create_composition: Scaffold from components - - list_packages: List all local UI packages - - read_file: Read any file from the UI repo - -Frameworks: hanzo (default), shadcn, react, svelte, vue, react-native - -Install: - pip install hanzo-tools-ui - -Usage: - from hanzo_tools.ui import register_tools, TOOLS -""" - -from hanzo_tools.core import BaseTool, ToolRegistry - -from .github_api import ( - FRAMEWORK_CONFIGS, - FRAMEWORK_NAMES, - GitHubAPIClient, -) -from .local_client import LocalUIClient -from .ui_tool import UiTool, ui_tool - -TOOLS = [UiTool] - -__all__ = [ - "UiTool", - "ui_tool", - "FRAMEWORK_CONFIGS", - "FRAMEWORK_NAMES", - "GitHubAPIClient", - "LocalUIClient", - "register_tools", - "TOOLS", -] - - -def register_tools(mcp_server, **kwargs) -> list[BaseTool]: - """Register UI tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - - Returns: - List of registered tool instances - """ - tool = UiTool() - ToolRegistry.register_tool(mcp_server, tool) - return [tool] diff --git a/pkg/hanzo-tools-ui/hanzo_tools/ui/github_api.py b/pkg/hanzo-tools-ui/hanzo_tools/ui/github_api.py deleted file mode 100644 index a0d27fe3d..000000000 --- a/pkg/hanzo-tools-ui/hanzo_tools/ui/github_api.py +++ /dev/null @@ -1,320 +0,0 @@ -"""GitHub API client for fetching UI components from framework repositories.""" - -import os -import time -from typing import Any - -import httpx - -GITHUB_API_BASE = "https://api.github.com" -GITHUB_RAW_BASE = "https://raw.githubusercontent.com" - - -class FrameworkConfig: - """Configuration for a UI framework repository.""" - - def __init__( - self, - owner: str, - repo: str, - branch: str, - components_path: str, - extension: str, - blocks_path: str | None = None, - examples_path: str | None = None, - ): - self.owner = owner - self.repo = repo - self.branch = branch - self.components_path = components_path - self.blocks_path = blocks_path - self.examples_path = examples_path - self.extension = extension - - -FRAMEWORK_CONFIGS: dict[str, FrameworkConfig] = { - "hanzo": FrameworkConfig( - owner="hanzoai", - repo="ui", - branch="main", - components_path="pkg/ui/primitives", - blocks_path="pkg/ui/primitives", - extension=".tsx", - ), - "hanzo-native": FrameworkConfig( - owner="hanzoai", - repo="ui-native", - branch="main", - components_path="packages/native/src/components", - extension=".tsx", - ), - "hanzo-vue": FrameworkConfig( - owner="hanzoai", - repo="ui-vue", - branch="main", - components_path="packages/vue/src/components", - extension=".vue", - ), - "hanzo-svelte": FrameworkConfig( - owner="hanzoai", - repo="ui-svelte", - branch="main", - components_path="packages/svelte/src/components", - extension=".svelte", - ), - "shadcn": FrameworkConfig( - owner="shadcn-ui", - repo="ui", - branch="main", - components_path="apps/v4/registry/new-york-v4/ui", - blocks_path="apps/v4/registry/new-york-v4/blocks", - examples_path="apps/v4/registry/new-york-v4/examples", - extension=".tsx", - ), - "react": FrameworkConfig( - owner="shadcn-ui", - repo="ui", - branch="main", - components_path="apps/v4/registry/new-york-v4/ui", - blocks_path="apps/v4/registry/new-york-v4/blocks", - examples_path="apps/v4/registry/new-york-v4/examples", - extension=".tsx", - ), - "svelte": FrameworkConfig( - owner="huntabyte", - repo="shadcn-svelte", - branch="main", - components_path="apps/www/src/lib/registry/new-york/ui", - blocks_path="apps/www/src/lib/registry/new-york/blocks", - extension=".svelte", - ), - "vue": FrameworkConfig( - owner="unovue", - repo="shadcn-vue", - branch="main", - components_path="apps/www/src/lib/registry/new-york/ui", - blocks_path="apps/www/src/lib/registry/new-york/blocks", - extension=".vue", - ), - "react-native": FrameworkConfig( - owner="founded-labs", - repo="react-native-reusables", - branch="main", - components_path="packages/reusables/src", - extension=".tsx", - ), -} - -FRAMEWORK_NAMES: dict[str, str] = { - "hanzo": "Hanzo UI (React)", - "hanzo-native": "Hanzo UI Native (React Native)", - "hanzo-vue": "Hanzo UI Vue", - "hanzo-svelte": "Hanzo UI Svelte", - "shadcn": "shadcn/ui", - "react": "shadcn/ui (React)", - "svelte": "Svelte (shadcn)", - "vue": "Vue (shadcn)", - "react-native": "React Native Reusables", -} - - -class GitHubAPIClient: - """GitHub API client with caching and rate limit tracking.""" - - CACHE_TTL = 15 * 60 # 15 minutes - - def __init__(self): - self._token = os.environ.get("GITHUB_TOKEN") or os.environ.get( - "GITHUB_PERSONAL_ACCESS_TOKEN" - ) - self._cache: dict[str, tuple[Any, float]] = {} - self._rate_limit_remaining = 60 - self._rate_limit_reset = time.time() - self._client: httpx.AsyncClient | None = None - - async def _get_client(self) -> httpx.AsyncClient: - if self._client is None: - headers = { - "User-Agent": "Hanzo-MCP-UI-Tool", - "Accept": "application/vnd.github.v3+json", - } - if self._token: - headers["Authorization"] = f"token {self._token}" - self._client = httpx.AsyncClient(headers=headers, timeout=30.0) - return self._client - - def _cache_get(self, key: str) -> Any | None: - entry = self._cache.get(key) - if entry is None: - return None - data, ts = entry - if time.time() - ts > self.CACHE_TTL: - del self._cache[key] - return None - return data - - def _cache_set(self, key: str, data: Any) -> None: - self._cache[key] = (data, time.time()) - - async def _api_request(self, url: str) -> Any: - if self._rate_limit_remaining <= 0 and time.time() < self._rate_limit_reset: - wait = int(self._rate_limit_reset - time.time()) - raise RuntimeError(f"GitHub API rate limit exceeded. Reset in {wait}s.") - - cached = self._cache_get(url) - if cached is not None: - return cached - - client = await self._get_client() - resp = await client.get(url) - - if "x-ratelimit-remaining" in resp.headers: - self._rate_limit_remaining = int(resp.headers["x-ratelimit-remaining"]) - if "x-ratelimit-reset" in resp.headers: - self._rate_limit_reset = int(resp.headers["x-ratelimit-reset"]) - - if resp.status_code == 200: - data = resp.json() - self._cache_set(url, data) - return data - elif resp.status_code == 403: - raise RuntimeError("GitHub API rate limit exceeded or authentication required") - elif resp.status_code == 404: - raise FileNotFoundError("Resource not found") - else: - raise RuntimeError(f"GitHub API error: {resp.status_code}") - - async def get_raw_content(self, owner: str, repo: str, path: str, branch: str) -> str: - url = f"{GITHUB_RAW_BASE}/{owner}/{repo}/{branch}/{path}" - cached = self._cache_get(url) - if cached is not None: - return cached - - client = await self._get_client() - resp = await client.get(url) - if resp.status_code == 200: - self._cache_set(url, resp.text) - return resp.text - elif resp.status_code == 404: - raise FileNotFoundError(f"File not found: {path}") - else: - raise RuntimeError(f"Failed to fetch: {resp.status_code}") - - async def get_directory_contents( - self, owner: str, repo: str, path: str, branch: str - ) -> list[dict]: - url = f"{GITHUB_API_BASE}/repos/{owner}/{repo}/contents/{path}?ref={branch}" - return await self._api_request(url) - - async def fetch_component(self, name: str, framework: str = "hanzo") -> str: - config = FRAMEWORK_CONFIGS[framework] - path = f"{config.components_path}/{name}{config.extension}" - try: - return await self.get_raw_content(config.owner, config.repo, path, config.branch) - except FileNotFoundError: - index_path = f"{config.components_path}/{name}/index{config.extension}" - try: - return await self.get_raw_content( - config.owner, config.repo, index_path, config.branch - ) - except FileNotFoundError: - raise FileNotFoundError(f"Component '{name}' not found in {framework} repository") - - async def fetch_component_demo(self, name: str, framework: str = "hanzo") -> str: - config = FRAMEWORK_CONFIGS[framework] - if not config.examples_path and framework != "hanzo": - raise RuntimeError(f"Demo/examples not available for {framework}") - - demo_path = ( - f"{config.components_path}/{name}/demo{config.extension}" - if framework == "hanzo" - else f"{config.examples_path}/{name}-demo{config.extension}" - ) - try: - return await self.get_raw_content(config.owner, config.repo, demo_path, config.branch) - except FileNotFoundError: - raise FileNotFoundError( - f"Demo for component '{name}' not found in {framework} repository" - ) - - async def fetch_component_metadata(self, name: str, framework: str = "hanzo") -> dict: - config = FRAMEWORK_CONFIGS[framework] - path = f"{config.components_path}/{name}/metadata.json" - try: - import json - - content = await self.get_raw_content(config.owner, config.repo, path, config.branch) - return json.loads(content) - except (FileNotFoundError, Exception): - return { - "name": name, - "framework": framework, - "extension": config.extension, - "path": f"{config.components_path}/{name}", - } - - async def fetch_block(self, name: str, framework: str = "hanzo") -> str: - config = FRAMEWORK_CONFIGS[framework] - if not config.blocks_path: - raise RuntimeError(f"Blocks not available for {framework}") - - path = f"{config.blocks_path}/{name}{config.extension}" - try: - return await self.get_raw_content(config.owner, config.repo, path, config.branch) - except FileNotFoundError: - index_path = f"{config.blocks_path}/{name}/index{config.extension}" - try: - return await self.get_raw_content( - config.owner, config.repo, index_path, config.branch - ) - except FileNotFoundError: - raise FileNotFoundError(f"Block '{name}' not found in {framework} repository") - - async def list_components(self, framework: str = "hanzo") -> list[dict]: - config = FRAMEWORK_CONFIGS[framework] - contents = await self.get_directory_contents( - config.owner, config.repo, config.components_path, config.branch - ) - return [ - {"name": item["name"].replace(config.extension, ""), "type": item["type"]} - for item in contents - if item["type"] == "dir" - or (item["type"] == "file" and item["name"].endswith(config.extension)) - ] - - async def list_blocks(self, framework: str = "hanzo") -> list[dict]: - config = FRAMEWORK_CONFIGS[framework] - if not config.blocks_path: - return [] - try: - contents = await self.get_directory_contents( - config.owner, config.repo, config.blocks_path, config.branch - ) - return [ - {"name": item["name"].replace(config.extension, ""), "type": item["type"]} - for item in contents - if item["type"] == "dir" - or (item["type"] == "file" and item["name"].endswith(config.extension)) - ] - except Exception: - return [] - - async def get_directory_structure(self, path: str, framework: str = "hanzo") -> dict: - config = FRAMEWORK_CONFIGS[framework] - contents = await self.get_directory_contents(config.owner, config.repo, path, config.branch) - children = [] - for item in contents: - entry = {"name": item["name"], "type": item["type"], "path": item["path"]} - if item["type"] == "file" and "size" in item: - entry["size"] = item["size"] - children.append(entry) - return {"path": path, "children": children} - - def get_rate_limit_info(self) -> dict: - return { - "remaining": self._rate_limit_remaining, - "reset": self._rate_limit_reset, - } - - def clear_cache(self) -> None: - self._cache.clear() diff --git a/pkg/hanzo-tools-ui/hanzo_tools/ui/local_client.py b/pkg/hanzo-tools-ui/hanzo_tools/ui/local_client.py deleted file mode 100644 index 157897f6a..000000000 --- a/pkg/hanzo-tools-ui/hanzo_tools/ui/local_client.py +++ /dev/null @@ -1,312 +0,0 @@ -"""Local filesystem client for reading UI components from ~/work/hanzo/ui. - -When the hanzo/ui repo is cloned locally, this client reads component source -directly from disk โ€” no GitHub API calls, no rate limits, always up-to-date -with the developer's working tree. -""" - -import json -import os -from pathlib import Path -from typing import Any - -import aiofiles - -# Default local repo path; overridable via HANZO_UI_PATH env var -DEFAULT_UI_PATH = os.path.expanduser("~/work/hanzo/ui") - -# Local package layout โ€” maps to the actual repo structure -LOCAL_PACKAGES: dict[str, dict[str, str]] = { - "ui": { - "primitives": "pkg/ui/primitives", - "src": "pkg/ui/src", - "blocks": "pkg/ui/primitives", # blocks live alongside primitives - "style": "pkg/ui/style", - "docs": "pkg/ui/docs", - "util": "pkg/ui/util", - }, - "react": { - "components": "pkg/react/src/components", - "hooks": "pkg/react/src/hooks", - }, - "commerce": { - "components": "pkg/commerce/components", - }, - "brand": { - "root": "pkg/brand", - }, - "gui": { - "root": "pkg/gui", - }, - "shop": { - "root": "pkg/shop", - }, - "checkout": { - "root": "pkg/checkout", - }, - "agent-ui": { - "root": "pkg/agent-ui", - }, - "tokens": { - "root": "pkg/tokens", - }, -} - -# Extensions to search for components -COMPONENT_EXTENSIONS = (".tsx", ".ts", ".jsx", ".js") - - -class LocalUIClient: - """Reads Hanzo UI components from the local filesystem.""" - - def __init__(self, ui_path: str | None = None): - self._ui_path = Path(ui_path or os.environ.get("HANZO_UI_PATH", DEFAULT_UI_PATH)) - - @property - def available(self) -> bool: - """Check if the local UI repo exists.""" - return (self._ui_path / "pkg" / "ui").is_dir() - - async def list_components(self, framework: str = "hanzo") -> list[dict]: - """List all components from the local primitives directory.""" - primitives_dir = self._ui_path / "pkg" / "ui" / "primitives" - if not primitives_dir.is_dir(): - return [] - - components = [] - for entry in sorted(primitives_dir.iterdir()): - if entry.name.startswith(("_", ".")): - continue - if entry.name.startswith("index"): - continue - if entry.is_dir(): - components.append( - { - "name": entry.name, - "type": "dir", - "path": str(entry.relative_to(self._ui_path)), - } - ) - elif entry.is_file() and entry.suffix in COMPONENT_EXTENSIONS: - components.append( - { - "name": entry.stem, - "type": "file", - "path": str(entry.relative_to(self._ui_path)), - } - ) - return components - - async def fetch_component(self, name: str, framework: str = "hanzo") -> str: - """Fetch component source code from local filesystem.""" - # Search in primitives first, then src modules - search_dirs = [ - self._ui_path / "pkg" / "ui" / "primitives", - self._ui_path / "pkg" / "ui" / "src", - self._ui_path / "pkg" / "react" / "src" / "components", - ] - - for base_dir in search_dirs: - if not base_dir.is_dir(): - continue - # Try direct file match - for ext in COMPONENT_EXTENSIONS: - candidate = base_dir / f"{name}{ext}" - if candidate.is_file(): - async with aiofiles.open(candidate) as f: - return await f.read() - - # Try directory with index file - comp_dir = base_dir / name - if comp_dir.is_dir(): - for ext in COMPONENT_EXTENSIONS: - index = comp_dir / f"index{ext}" - if index.is_file(): - async with aiofiles.open(index) as f: - return await f.read() - - # Search in src barrel exports - barrel = self._ui_path / "pkg" / "ui" / "src" / f"{name}.ts" - if barrel.is_file(): - async with aiofiles.open(barrel) as f: - return await f.read() - - raise FileNotFoundError(f"Component '{name}' not found locally in hanzo/ui") - - async def fetch_component_demo(self, name: str, framework: str = "hanzo") -> str: - """Fetch component demo from local filesystem.""" - # Check demo/ app directory - demo_dirs = [ - self._ui_path / "demo", - self._ui_path / "apps", - ] - for demo_dir in demo_dirs: - if not demo_dir.is_dir(): - continue - for ext in COMPONENT_EXTENSIONS: - candidate = demo_dir / f"{name}{ext}" - if candidate.is_file(): - async with aiofiles.open(candidate) as f: - return await f.read() - - # Check for demo file alongside component - primitives_dir = self._ui_path / "pkg" / "ui" / "primitives" - comp_dir = primitives_dir / name - if comp_dir.is_dir(): - for ext in COMPONENT_EXTENSIONS: - demo = comp_dir / f"demo{ext}" - if demo.is_file(): - async with aiofiles.open(demo) as f: - return await f.read() - - raise FileNotFoundError(f"Demo for '{name}' not found locally") - - async def fetch_component_metadata(self, name: str, framework: str = "hanzo") -> dict: - """Fetch component metadata from local filesystem.""" - primitives_dir = self._ui_path / "pkg" / "ui" / "primitives" - - # Check for metadata.json in component directory - meta_path = primitives_dir / name / "metadata.json" - if meta_path.is_file(): - async with aiofiles.open(meta_path) as f: - content = await f.read() - return json.loads(content) - - # Build metadata from file info - for ext in COMPONENT_EXTENSIONS: - comp_file = primitives_dir / f"{name}{ext}" - if comp_file.is_file(): - stat = comp_file.stat() - return { - "name": name, - "framework": "hanzo", - "path": str(comp_file.relative_to(self._ui_path)), - "size": stat.st_size, - "extension": ext, - "source": "local", - } - - return {"name": name, "framework": "hanzo", "source": "local"} - - async def list_blocks(self, framework: str = "hanzo") -> list[dict]: - """List blocks from local filesystem.""" - # Check index-blocks.ts for block exports - blocks_index = self._ui_path / "pkg" / "ui" / "primitives" / "index-blocks.ts" - if blocks_index.is_file(): - async with aiofiles.open(blocks_index) as f: - content = await f.read() - blocks = [] - for line in content.splitlines(): - if line.strip().startswith("export"): - # Extract component names from export lines - name = line.split("from")[-1].strip().strip("';\"").split("/")[-1] - if name: - blocks.append({"name": name, "type": "export"}) - return blocks - return [] - - async def fetch_block(self, name: str, framework: str = "hanzo") -> str: - """Fetch block source code.""" - return await self.fetch_component(name, framework) - - async def get_directory_structure(self, path: str, framework: str = "hanzo") -> dict: - """Browse directory structure of the local UI repo.""" - if path: - target = self._ui_path / path - else: - target = self._ui_path / "pkg" - - if not target.is_dir(): - raise FileNotFoundError(f"Directory not found: {path}") - - children = [] - for entry in sorted(target.iterdir()): - if entry.name.startswith(".") or entry.name == "node_modules": - continue - item: dict[str, Any] = { - "name": entry.name, - "type": "dir" if entry.is_dir() else "file", - "path": str(entry.relative_to(self._ui_path)), - } - if entry.is_file(): - item["size"] = entry.stat().st_size - children.append(item) - - return {"path": path or "pkg/", "children": children} - - async def list_packages(self) -> list[dict]: - """List all UI packages available locally.""" - pkg_dir = self._ui_path / "pkg" - if not pkg_dir.is_dir(): - return [] - - packages = [] - for entry in sorted(pkg_dir.iterdir()): - if not entry.is_dir() or entry.name.startswith("."): - continue - pkg_json = entry / "package.json" - info: dict[str, Any] = { - "name": entry.name, - "path": str(entry.relative_to(self._ui_path)), - } - if pkg_json.is_file(): - async with aiofiles.open(pkg_json) as f: - try: - data = json.loads(await f.read()) - info["version"] = data.get("version", "unknown") - info["description"] = data.get("description", "") - except json.JSONDecodeError: - pass - packages.append(info) - return packages - - async def read_file(self, path: str) -> str: - """Read any file from the UI repo by relative path.""" - target = self._ui_path / path - if not target.is_file(): - raise FileNotFoundError(f"File not found: {path}") - async with aiofiles.open(target) as f: - return await f.read() - - async def search_components(self, query: str) -> list[dict]: - """Search component files by name and content.""" - query_lower = query.lower() - results = [] - - primitives_dir = self._ui_path / "pkg" / "ui" / "primitives" - if not primitives_dir.is_dir(): - return results - - for entry in sorted(primitives_dir.iterdir()): - if entry.name.startswith(("_", ".")) or entry.name.startswith("index"): - continue - - name = entry.stem if entry.is_file() else entry.name - if query_lower in name.lower(): - results.append( - { - "name": name, - "type": "dir" if entry.is_dir() else "file", - "path": str(entry.relative_to(self._ui_path)), - "match": "name", - } - ) - - # Also search src modules - src_dir = self._ui_path / "pkg" / "ui" / "src" - if src_dir.is_dir(): - for entry in sorted(src_dir.iterdir()): - if entry.name.startswith(("_", ".")): - continue - name = entry.stem if entry.is_file() else entry.name - if query_lower in name.lower() and name not in [r["name"] for r in results]: - results.append( - { - "name": name, - "type": "dir" if entry.is_dir() else "file", - "path": str(entry.relative_to(self._ui_path)), - "match": "name", - } - ) - - return results diff --git a/pkg/hanzo-tools-ui/hanzo_tools/ui/registry/__init__.py b/pkg/hanzo-tools-ui/hanzo_tools/ui/registry/__init__.py deleted file mode 100644 index 530376da7..000000000 --- a/pkg/hanzo-tools-ui/hanzo_tools/ui/registry/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Hanzo UI Registry โ€” cached component server and client. - -Server: pre-fetches all component data from hanzoai/ui on GitHub, -serves cached responses via FastAPI. Deploy at ui.hanzo.ai. - -Client: fetches from the registry server instead of hitting GitHub -directly. Used by the UI tool as a middle-tier between local and GitHub. -""" - -from .client import RegistryClient - -__all__ = ["RegistryClient"] - -# Server imports are deferred to avoid requiring fastapi at import time. -# Use: from hanzo_tools.ui.registry.server import app diff --git a/pkg/hanzo-tools-ui/hanzo_tools/ui/registry/__main__.py b/pkg/hanzo-tools-ui/hanzo_tools/ui/registry/__main__.py deleted file mode 100644 index 24183e941..000000000 --- a/pkg/hanzo-tools-ui/hanzo_tools/ui/registry/__main__.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Run the Hanzo UI Registry server. - -Usage: - python -m hanzo_tools.ui.registry - # or - hanzo-ui-registry -""" - -import os - - -def main(): - import uvicorn - - host = os.environ.get("REGISTRY_HOST", "0.0.0.0") - port = int(os.environ.get("REGISTRY_PORT", "8787")) - dev = os.environ.get("REGISTRY_DEV", "") == "1" - - uvicorn.run( - "hanzo_tools.ui.registry.server:app", - host=host, - port=port, - reload=dev, - ) - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo-tools-ui/hanzo_tools/ui/registry/cache.py b/pkg/hanzo-tools-ui/hanzo_tools/ui/registry/cache.py deleted file mode 100644 index 3a44f3a96..000000000 --- a/pkg/hanzo-tools-ui/hanzo_tools/ui/registry/cache.py +++ /dev/null @@ -1,311 +0,0 @@ -"""Registry cache โ€” eagerly fetches all component data from GitHub. - -On startup, walks the hanzoai/ui repo and fetches every component's -source, metadata, and demo. Stores everything in memory. Refreshes -on a background timer. All lookups are O(1) dict reads. -""" - -import asyncio -import logging -import os -import time -from dataclasses import dataclass, field -from typing import Any - -from ..github_api import FRAMEWORK_CONFIGS, GitHubAPIClient - -logger = logging.getLogger(__name__) - -DEFAULT_REFRESH_INTERVAL = int(os.environ.get("REFRESH_INTERVAL", "900")) # 15 min -MAX_CONCURRENCY = 10 - - -@dataclass -class ComponentEntry: - name: str - type: str # "file" or "dir" - path: str = "" - source: str | None = None - metadata: dict | None = None - demo: str | None = None - category: str = "" - description: str = "" - - -@dataclass -class FrameworkData: - """All cached data for a single framework.""" - - components: dict[str, ComponentEntry] = field(default_factory=dict) - blocks: dict[str, ComponentEntry] = field(default_factory=dict) - directory_cache: dict[str, dict] = field(default_factory=dict) - - -class RegistryCache: - """In-memory cache of all UI component data from GitHub.""" - - def __init__( - self, - frameworks: list[str] | None = None, - refresh_interval: int = DEFAULT_REFRESH_INTERVAL, - ): - self._github = GitHubAPIClient() - self._frameworks = frameworks or ["hanzo"] - self._refresh_interval = refresh_interval - self._data: dict[str, FrameworkData] = {} - self._last_refresh: float = 0 - self._refresh_duration: float = 0 - self._refreshing = False - self._ready = False - self._task: asyncio.Task | None = None - - @property - def ready(self) -> bool: - return self._ready - - @property - def health(self) -> dict: - total_components = sum(len(d.components) for d in self._data.values()) - total_blocks = sum(len(d.blocks) for d in self._data.values()) - age = time.time() - self._last_refresh if self._last_refresh else None - return { - "status": "healthy" if self._ready else "warming", - "ready": self._ready, - "last_refresh": self._last_refresh, - "cache_age_seconds": round(age, 1) if age else None, - "refresh_duration_seconds": round(self._refresh_duration, 1), - "refreshing": self._refreshing, - "frameworks": self._frameworks, - "components_cached": total_components, - "blocks_cached": total_blocks, - } - - async def refresh(self) -> None: - """Full refresh: fetch all components for all frameworks.""" - if self._refreshing: - logger.info("Refresh already in progress, skipping") - return - - self._refreshing = True - start = time.time() - logger.info("Starting cache refresh for frameworks: %s", self._frameworks) - - try: - for framework in self._frameworks: - await self._refresh_framework(framework) - except Exception: - logger.exception("Cache refresh failed") - finally: - self._refreshing = False - self._last_refresh = time.time() - self._refresh_duration = time.time() - start - self._ready = True - total = sum(len(d.components) for d in self._data.values()) - logger.info( - "Cache refresh complete: %d components in %.1fs", - total, - self._refresh_duration, - ) - - async def _refresh_framework(self, framework: str) -> None: - """Refresh all data for a single framework.""" - if framework not in FRAMEWORK_CONFIGS: - logger.warning("Unknown framework: %s", framework) - return - - fw_data = FrameworkData() - sem = asyncio.Semaphore(MAX_CONCURRENCY) - - # Fetch component list - try: - comp_list = await self._github.list_components(framework) - except Exception: - logger.exception("Failed to list components for %s", framework) - return - - # Fetch source + metadata + demo for each component concurrently - async def fetch_one(comp: dict) -> ComponentEntry: - name = comp["name"] - entry = ComponentEntry( - name=name, - type=comp.get("type", "file"), - path=comp.get("path", ""), - ) - async with sem: - # Source - try: - entry.source = await self._github.fetch_component(name, framework) - except Exception: - logger.debug("No source for %s/%s", framework, name) - - # Metadata - try: - entry.metadata = await self._github.fetch_component_metadata(name, framework) - except Exception: - pass - - # Demo - try: - entry.demo = await self._github.fetch_component_demo(name, framework) - except Exception: - pass - - return entry - - entries = await asyncio.gather( - *[fetch_one(c) for c in comp_list], - return_exceptions=True, - ) - - for entry in entries: - if isinstance(entry, ComponentEntry): - fw_data.components[entry.name] = entry - elif isinstance(entry, Exception): - logger.debug("Component fetch error: %s", entry) - - # Fetch blocks - try: - block_list = await self._github.list_blocks(framework) - for block in block_list: - name = block["name"] - b_entry = ComponentEntry(name=name, type=block.get("type", "file")) - try: - async with sem: - b_entry.source = await self._github.fetch_block(name, framework) - except Exception: - pass - fw_data.blocks[name] = b_entry - except Exception: - logger.debug("No blocks for %s", framework) - - # Cache directory structure - config = FRAMEWORK_CONFIGS[framework] - try: - structure = await self._github.get_directory_structure( - config.components_path, framework - ) - fw_data.directory_cache[config.components_path] = structure - except Exception: - pass - - # Atomic swap - self._data[framework] = fw_data - logger.info( - "Cached %d components, %d blocks for %s", - len(fw_data.components), - len(fw_data.blocks), - framework, - ) - - async def start_background_refresh(self) -> None: - """Run refresh loop in the background.""" - while True: - await asyncio.sleep(self._refresh_interval) - try: - await self.refresh() - except Exception: - logger.exception("Background refresh failed") - - def start(self) -> None: - """Start the background refresh task.""" - if self._task is None: - self._task = asyncio.create_task(self.start_background_refresh()) - - def stop(self) -> None: - """Stop the background refresh task.""" - if self._task is not None: - self._task.cancel() - self._task = None - - # --- Lookup methods (all O(1)) --- - - def _fw(self, framework: str) -> FrameworkData: - return self._data.get(framework, FrameworkData()) - - def get_components(self, framework: str = "hanzo") -> list[dict]: - return [ - { - "name": e.name, - "type": e.type, - "path": e.path, - "has_source": e.source is not None, - "has_demo": e.demo is not None, - } - for e in self._fw(framework).components.values() - ] - - def get_component(self, name: str, framework: str = "hanzo") -> dict | None: - entry = self._fw(framework).components.get(name) - if entry is None: - return None - return { - "name": entry.name, - "type": entry.type, - "source": entry.source, - "metadata": entry.metadata, - "demo": entry.demo, - } - - def get_component_source(self, name: str, framework: str = "hanzo") -> str | None: - entry = self._fw(framework).components.get(name) - return entry.source if entry else None - - def get_component_demo(self, name: str, framework: str = "hanzo") -> str | None: - entry = self._fw(framework).components.get(name) - return entry.demo if entry else None - - def get_component_metadata(self, name: str, framework: str = "hanzo") -> dict | None: - entry = self._fw(framework).components.get(name) - return entry.metadata if entry else None - - def get_blocks(self, framework: str = "hanzo") -> list[dict]: - return [ - {"name": e.name, "type": e.type, "has_source": e.source is not None} - for e in self._fw(framework).blocks.values() - ] - - def get_block(self, name: str, framework: str = "hanzo") -> dict | None: - entry = self._fw(framework).blocks.get(name) - if entry is None: - return None - return {"name": entry.name, "source": entry.source} - - def search(self, query: str, framework: str = "hanzo") -> list[dict]: - q = query.lower() - results = [] - for entry in self._fw(framework).components.values(): - if ( - q in entry.name.lower() - or q in entry.description.lower() - or q in entry.category.lower() - ): - results.append({"name": entry.name, "type": entry.type, "match": "name"}) - return results - - def get_structure(self, path: str, framework: str = "hanzo") -> dict[str, Any] | None: - return self._fw(framework).directory_cache.get(path) - - def build_index(self, framework: str = "hanzo") -> dict: - """Build the full registry index (single-payload for client hydration).""" - components = {} - for name, entry in self._fw(framework).components.items(): - components[name] = { - "name": entry.name, - "type": entry.type, - "source": entry.source, - "metadata": entry.metadata, - "demo": entry.demo, - } - - blocks = {} - for name, entry in self._fw(framework).blocks.items(): - blocks[name] = {"name": entry.name, "source": entry.source} - - return { - "framework": framework, - "generated_at": self._last_refresh, - "components": components, - "blocks": blocks, - "total_components": len(components), - "total_blocks": len(blocks), - } diff --git a/pkg/hanzo-tools-ui/hanzo_tools/ui/registry/client.py b/pkg/hanzo-tools-ui/hanzo_tools/ui/registry/client.py deleted file mode 100644 index 1891b0a74..000000000 --- a/pkg/hanzo-tools-ui/hanzo_tools/ui/registry/client.py +++ /dev/null @@ -1,220 +0,0 @@ -"""Registry client โ€” fetches component data from the Hanzo UI site. - -Works with both: -- Static hosting (CF Pages, GitHub Pages): fetches pre-built JSON from /api/registry/ -- Server mode (Hanzo PaaS, dev): hits Next.js API routes at /api/registry/ - -Mirrors the GitHubAPIClient / LocalUIClient interface so it can be used -as a drop-in backend in UiTool. Has its own short TTL cache to avoid -hitting the server on every tool call during a session. -""" - -import os -import time -from typing import Any - -import httpx - -# Registry URLs by framework -REGISTRY_URLS: dict[str, str] = { - "hanzo": "https://ui.hanzo.ai", - "lux": "https://ui.lux.finance", -} -DEFAULT_REGISTRY_URL = "https://ui.hanzo.ai" -CLIENT_CACHE_TTL = 300 # 5 min local cache (server data is pre-built) - - -class RegistryClient: - """Client for the Hanzo UI registry (static or dynamic).""" - - def __init__(self, base_url: str | None = None): - self._base_url = ( - base_url or os.environ.get("HANZO_UI_REGISTRY_URL") or DEFAULT_REGISTRY_URL - ).rstrip("/") - self._client: httpx.AsyncClient | None = None - self._cache: dict[str, tuple[Any, float]] = {} - self._available: bool | None = None - # Full index cache โ€” hydrated on first use - self._index: dict | None = None - self._index_ts: float = 0 - - async def _get_client(self) -> httpx.AsyncClient: - if self._client is None: - self._client = httpx.AsyncClient( - base_url=self._base_url, - timeout=15.0, - follow_redirects=True, - headers={"User-Agent": "Hanzo-MCP-UI-Tool"}, - ) - return self._client - - def _cache_get(self, key: str) -> Any | None: - entry = self._cache.get(key) - if entry is None: - return None - data, ts = entry - if time.time() - ts > CLIENT_CACHE_TTL: - del self._cache[key] - return None - return data - - def _cache_set(self, key: str, data: Any) -> None: - self._cache[key] = (data, time.time()) - - async def _get(self, path: str, params: dict | None = None) -> Any: - cache_key = f"{path}:{params}" - cached = self._cache_get(cache_key) - if cached is not None: - return cached - - client = await self._get_client() - resp = await client.get(path, params=params) - resp.raise_for_status() - data = resp.json() - self._cache_set(cache_key, data) - return data - - async def check_available(self) -> bool: - """Check if the registry is reachable (tries static files first).""" - if self._available is not None: - return self._available - try: - client = await self._get_client() - # Try the static components.json (works on CF Pages / static hosting) - resp = await client.get("/api/registry/components.json", timeout=5.0) - if resp.status_code == 200: - self._available = True - return True - # Try server-mode health endpoint - resp = await client.get("/api/health", timeout=5.0) - self._available = resp.status_code == 200 - except Exception: - self._available = False - return self._available - - @property - def available(self) -> bool | None: - return self._available - - async def _ensure_index(self) -> dict: - """Fetch and cache the full index (single HTTP call, all components).""" - if self._index and time.time() - self._index_ts < CLIENT_CACHE_TTL: - return self._index - - try: - # Try static file first (CF Pages) - data = await self._get("/api/registry/index.json") - except Exception: - # Fallback to server-mode API route - data = await self._get("/api/registry/index") - - self._index = data - self._index_ts = time.time() - return data - - def _extract_source(self, component: dict) -> str | None: - """Extract source code from a registry component entry.""" - files = component.get("files", []) - if not files: - return None - first = files[0] - if isinstance(first, dict): - return first.get("content") - return None - - # --- Mirror of GitHubAPIClient / LocalUIClient interface --- - - async def list_components(self, framework: str = "hanzo") -> list[dict]: - try: - data = await self._get("/api/registry/components.json") - except Exception: - data = await self._get("/api/registry", {"type": "components:ui"}) - return data.get("components", []) - - async def fetch_component(self, name: str, framework: str = "hanzo") -> str: - try: - # Try static file (CF Pages) - data = await self._get(f"/api/registry/components/{name}.json") - except Exception: - # Fallback to API route - data = await self._get(f"/api/registry/components/{name}") - - source = self._extract_source(data) - if source is None: - raise FileNotFoundError(f"Component '{name}' source not available") - return source - - async def fetch_component_demo(self, name: str, framework: str = "hanzo") -> str: - try: - data = await self._get(f"/api/registry/components/{name}-demo.json") - except Exception: - try: - data = await self._get(f"/api/registry/components/{name}-demo") - except Exception: - raise FileNotFoundError(f"Demo for '{name}' not available") - - source = self._extract_source(data) - if source is None: - raise FileNotFoundError(f"Demo for '{name}' not available") - return source - - async def fetch_component_metadata(self, name: str, framework: str = "hanzo") -> dict: - try: - data = await self._get(f"/api/registry/components/{name}.json") - except Exception: - data = await self._get(f"/api/registry/components/{name}") - - return { - "name": data.get("name", name), - "type": data.get("type"), - "dependencies": data.get("dependencies", []), - "registryDependencies": data.get("registryDependencies", []), - "source": "registry", - } - - async def list_blocks(self, framework: str = "hanzo") -> list[dict]: - comps = await self.list_components(framework) - return [c for c in comps if "block" in c.get("type", "").lower()] - - async def fetch_block(self, name: str, framework: str = "hanzo") -> str: - return await self.fetch_component(name, framework) - - async def search_components(self, query: str, framework: str = "hanzo") -> list[dict]: - try: - # Try server-mode search - data = await self._get("/api/registry/search", {"q": query}) - return data.get("results", []) - except Exception: - pass - - # Fallback: search client-side using the static index - try: - data = await self._get("/api/registry/search-index.json") - except Exception: - data = await self.list_components(framework) - q = query.lower() - return [c for c in data if q in c.get("name", "").lower()] - - q = query.lower() - return [ - {"name": item["n"], "type": item.get("t", "")} - for item in data - if q in item.get("n", "").lower() - ] - - async def get_directory_structure(self, path: str, framework: str = "hanzo") -> dict: - # Not available as static โ€” return component list as structure - comps = await self.list_components(framework) - return { - "path": path or "/", - "children": [{"name": c["name"], "type": "file"} for c in comps], - } - - async def fetch_full_index(self, framework: str = "hanzo") -> dict: - """Fetch the full registry index โ€” all components in one payload.""" - return await self._ensure_index() - - async def close(self) -> None: - if self._client is not None: - await self._client.aclose() - self._client = None diff --git a/pkg/hanzo-tools-ui/hanzo_tools/ui/registry/server.py b/pkg/hanzo-tools-ui/hanzo_tools/ui/registry/server.py deleted file mode 100644 index b05c1d853..000000000 --- a/pkg/hanzo-tools-ui/hanzo_tools/ui/registry/server.py +++ /dev/null @@ -1,170 +0,0 @@ -"""Hanzo UI Registry Server โ€” FastAPI service serving cached component data. - -Deploy at ui.hanzo.ai. Pre-fetches all components from hanzoai/ui on GitHub, -serves instant cached responses. Refreshes every 15 minutes in background. - -Run: - python -m hanzo_tools.ui.registry - # or - uvicorn hanzo_tools.ui.registry.server:app --port 8787 -""" - -import logging -import os -from contextlib import asynccontextmanager - -from fastapi import FastAPI, HTTPException, Query -from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import JSONResponse - -from .cache import RegistryCache - -logger = logging.getLogger(__name__) - -# Configuration -FRAMEWORKS = os.environ.get("REGISTRY_FRAMEWORKS", "hanzo").split(",") -REFRESH_INTERVAL = int(os.environ.get("REFRESH_INTERVAL", "900")) - -# Global cache instance -_cache: RegistryCache | None = None - - -def get_cache() -> RegistryCache: - assert _cache is not None, "Cache not initialized" - return _cache - - -@asynccontextmanager -async def lifespan(app: FastAPI): - global _cache - _cache = RegistryCache(frameworks=FRAMEWORKS, refresh_interval=REFRESH_INTERVAL) - - logger.info("Starting initial cache refresh...") - await _cache.refresh() - logger.info("Initial refresh complete, starting background refresh loop") - _cache.start() - - yield - - _cache.stop() - logger.info("Registry server shutting down") - - -app = FastAPI( - title="Hanzo UI Registry", - description="Cached component registry for Hanzo UI", - version="0.1.0", - lifespan=lifespan, -) - -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_methods=["GET"], - allow_headers=["*"], -) - - -# --- API Endpoints --- - - -@app.get("/api/health") -async def health(): - cache = get_cache() - return cache.health - - -@app.get("/api/components") -async def list_components(framework: str = Query(default="hanzo")): - cache = get_cache() - components = cache.get_components(framework) - return {"framework": framework, "total": len(components), "components": components} - - -@app.get("/api/components/{name}") -async def get_component(name: str, framework: str = Query(default="hanzo")): - cache = get_cache() - comp = cache.get_component(name, framework) - if comp is None: - raise HTTPException(404, detail={"error": "Component not found", "name": name}) - return comp - - -@app.get("/api/components/{name}/source") -async def get_component_source(name: str, framework: str = Query(default="hanzo")): - cache = get_cache() - source = cache.get_component_source(name, framework) - if source is None: - raise HTTPException(404, detail={"error": "Component not found", "name": name}) - return {"name": name, "source": source} - - -@app.get("/api/components/{name}/demo") -async def get_component_demo(name: str, framework: str = Query(default="hanzo")): - cache = get_cache() - demo = cache.get_component_demo(name, framework) - if demo is None: - raise HTTPException(404, detail={"error": "Demo not found", "name": name}) - return {"name": name, "demo": demo} - - -@app.get("/api/components/{name}/metadata") -async def get_component_metadata(name: str, framework: str = Query(default="hanzo")): - cache = get_cache() - metadata = cache.get_component_metadata(name, framework) - if metadata is None: - raise HTTPException(404, detail={"error": "Component not found", "name": name}) - return {"name": name, "metadata": metadata} - - -@app.get("/api/blocks") -async def list_blocks(framework: str = Query(default="hanzo")): - cache = get_cache() - blocks = cache.get_blocks(framework) - return {"framework": framework, "total": len(blocks), "blocks": blocks} - - -@app.get("/api/blocks/{name}") -async def get_block(name: str, framework: str = Query(default="hanzo")): - cache = get_cache() - block = cache.get_block(name, framework) - if block is None: - raise HTTPException(404, detail={"error": "Block not found", "name": name}) - return block - - -@app.get("/api/search") -async def search_components( - q: str = Query(..., min_length=1), framework: str = Query(default="hanzo") -): - cache = get_cache() - results = cache.search(q, framework) - return {"query": q, "framework": framework, "results": results} - - -@app.get("/api/structure") -async def get_structure(path: str = Query(default=""), framework: str = Query(default="hanzo")): - cache = get_cache() - structure = cache.get_structure(path, framework) - if structure is None: - raise HTTPException(404, detail={"error": "Path not found in cache", "path": path}) - return structure - - -@app.get("/registry/index.json") -async def registry_index(framework: str = Query(default="hanzo")): - """Full registry manifest โ€” all components with source in one payload.""" - cache = get_cache() - index = cache.build_index(framework) - return JSONResponse(content=index) - - -@app.get("/") -async def root(): - return { - "service": "Hanzo UI Registry", - "docs": "/docs", - "health": "/api/health", - "components": "/api/components", - "index": "/registry/index.json", - } diff --git a/pkg/hanzo-tools-ui/hanzo_tools/ui/ui_tool.py b/pkg/hanzo-tools-ui/hanzo_tools/ui/ui_tool.py deleted file mode 100644 index 7307819e1..000000000 --- a/pkg/hanzo-tools-ui/hanzo_tools/ui/ui_tool.py +++ /dev/null @@ -1,579 +0,0 @@ -"""Unified UI component registry tool (HIP-0300). - -Single 'ui' tool for browsing, searching, installing, and managing -UI components from Hanzo and other registries. - -Backend priority: local disk โ†’ registry server โ†’ GitHub API - -Actions: -- list_components: List available components for a framework -- get_component: Get component source code -- get_demo: Get component demo/example -- get_metadata: Get component metadata -- list_blocks: List UI blocks -- get_block: Get block implementation -- search: Search components by name/description -- get_structure: Browse repository directory structure -- install: Install component via CLI -- set_framework: Switch active framework -- get_framework: Show current and available frameworks -- create_composition: Scaffold a composition from components -- list_packages: List all local UI packages (local only) -- read_file: Read any file from the UI repo (local only) -""" - -import asyncio -import logging -from typing import ClassVar - -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool - -from .github_api import ( - FRAMEWORK_CONFIGS, - FRAMEWORK_NAMES, - GitHubAPIClient, -) -from .local_client import LocalUIClient -from .registry.client import RegistryClient - -logger = logging.getLogger(__name__) - -# Module-level current framework state -_current_framework = "hanzo" - - -class UiTool(BaseTool): - """UI component registry tool (HIP-0300). - - Browse, search, install, and manage UI components from - Hanzo and other registries (shadcn/ui, Vue, Svelte, React Native). - - Backend priority: local โ†’ registry โ†’ GitHub API - """ - - name: ClassVar[str] = "ui" - VERSION: ClassVar[str] = "0.3.0" - - def __init__(self): - super().__init__() - self._github = GitHubAPIClient() - self._local = LocalUIClient() - self._registry = RegistryClient() - self._registry_checked = False - self._registry_available = False - - if self._local.available: - logger.info("UI tool: local hanzo/ui repo detected, using local-first mode") - self._register_ui_actions() - - def _use_local(self, framework: str) -> bool: - """Use local client for hanzo frameworks when repo is available.""" - return framework.startswith("hanzo") and self._local.available - - async def _use_registry(self) -> bool: - """Check if the registry server is available (cached after first check).""" - if not self._registry_checked: - self._registry_checked = True - self._registry_available = await self._registry.check_available() - if self._registry_available: - logger.info("UI tool: registry server available at %s", self._registry._base_url) - return self._registry_available - - async def _remote_client(self): - """Return registry client if available, else GitHub client.""" - if await self._use_registry(): - return self._registry, "registry" - return self._github, "github" - - @property - def description(self) -> str: - local_status = " (local repo detected)" if self._local.available else "" - return f"""UI component registry tool (HIP-0300){local_status}. - -Actions: -- list_components: List available components (framework, category) -- get_component: Get component source code (name, framework) -- get_demo: Get component demo/example (name, framework) -- get_metadata: Get component metadata (name, framework) -- list_blocks: List UI blocks (framework, category) -- get_block: Get block implementation (name, framework) -- search: Search components (query, framework) -- get_structure: Browse repo directory structure (path, framework) -- install: Install component via CLI (name, framework) -- set_framework: Switch active framework (framework) -- get_framework: Show current and available frameworks -- create_composition: Scaffold from components (name, components) -- list_packages: List all UI packages (hanzo local only) -- read_file: Read any file from the UI repo by path (hanzo local only) -- ask: Ask a question about UI components (RAG via Hanzo Cloud) -- semantic_search: Semantic search over components (Hanzo Cloud) -- index_status: Check search index status -- rebuild_index: Re-index components into Hanzo Cloud search - -Frameworks: hanzo (default), hanzo-native, hanzo-vue, hanzo-svelte, - shadcn, react, svelte, vue, react-native -""" - - def _fw_name(self, framework: str) -> str: - return FRAMEWORK_NAMES.get(framework, framework) - - def _register_ui_actions(self): - global _current_framework - - @self.action("list_components", "List available components") - async def list_components( - ctx: MCPContext, - framework: str | None = None, - category: str | None = None, - ) -> dict: - fw = framework or _current_framework - if fw not in FRAMEWORK_CONFIGS: - return { - "error": f"Unknown framework: {fw}. Available: {', '.join(FRAMEWORK_CONFIGS)}" - } - - if self._use_local(fw): - components = await self._local.list_components(fw) - source = "local" - else: - client, source = await self._remote_client() - components = await client.list_components(fw) - - if category: - components = [c for c in components if c.get("category") == category] - - return { - "framework": self._fw_name(fw), - "source": source, - "total": len(components), - "components": components, - } - - @self.action("get_component", "Get component source code") - async def get_component( - ctx: MCPContext, - name: str | None = None, - component: str | None = None, - framework: str | None = None, - ) -> dict: - comp_name = name or component - if not comp_name: - return {"error": "Component name is required"} - - fw = framework or _current_framework - - if self._use_local(fw): - try: - source = await self._local.fetch_component(comp_name, fw) - return { - "framework": self._fw_name(fw), - "component": comp_name, - "source": source, - "backend": "local", - } - except FileNotFoundError: - pass # Fall through to remote - - client, backend = await self._remote_client() - source = await client.fetch_component(comp_name, fw) - return { - "framework": self._fw_name(fw), - "component": comp_name, - "source": source, - "backend": backend, - } - - @self.action("get_demo", "Get component demo/example") - async def get_demo( - ctx: MCPContext, - name: str | None = None, - component: str | None = None, - framework: str | None = None, - ) -> dict: - comp_name = name or component - if not comp_name: - return {"error": "Component name is required"} - - fw = framework or _current_framework - - if self._use_local(fw): - try: - demo = await self._local.fetch_component_demo(comp_name, fw) - return { - "framework": self._fw_name(fw), - "component": comp_name, - "demo": demo, - "backend": "local", - } - except FileNotFoundError: - pass - - client, backend = await self._remote_client() - demo = await client.fetch_component_demo(comp_name, fw) - return { - "framework": self._fw_name(fw), - "component": comp_name, - "demo": demo, - "backend": backend, - } - - @self.action("get_metadata", "Get component metadata") - async def get_metadata( - ctx: MCPContext, - name: str | None = None, - component: str | None = None, - framework: str | None = None, - ) -> dict: - comp_name = name or component - if not comp_name: - return {"error": "Component name is required"} - - fw = framework or _current_framework - - if self._use_local(fw): - metadata = await self._local.fetch_component_metadata(comp_name, fw) - return { - "framework": self._fw_name(fw), - "component": comp_name, - "metadata": metadata, - } - - client, _ = await self._remote_client() - metadata = await client.fetch_component_metadata(comp_name, fw) - return { - "framework": self._fw_name(fw), - "component": comp_name, - "metadata": metadata, - } - - @self.action("list_blocks", "List UI blocks") - async def list_blocks( - ctx: MCPContext, - framework: str | None = None, - category: str | None = None, - ) -> dict: - fw = framework or _current_framework - - if self._use_local(fw): - blocks = await self._local.list_blocks(fw) - source = "local" - else: - client, source = await self._remote_client() - blocks = await client.list_blocks(fw) - - if category: - blocks = [b for b in blocks if b.get("category") == category] - - return { - "framework": self._fw_name(fw), - "source": source, - "total": len(blocks), - "blocks": blocks, - } - - @self.action("get_block", "Get block implementation") - async def get_block( - ctx: MCPContext, - name: str | None = None, - block: str | None = None, - framework: str | None = None, - ) -> dict: - block_name = name or block - if not block_name: - return {"error": "Block name is required"} - - fw = framework or _current_framework - - if self._use_local(fw): - try: - content = await self._local.fetch_block(block_name, fw) - return { - "framework": self._fw_name(fw), - "block": block_name, - "implementation": content, - "backend": "local", - } - except FileNotFoundError: - pass - - client, backend = await self._remote_client() - content = await client.fetch_block(block_name, fw) - return { - "framework": self._fw_name(fw), - "block": block_name, - "implementation": content, - "backend": backend, - } - - @self.action("search", "Search components") - async def search( - ctx: MCPContext, - query: str | None = None, - search: str | None = None, - framework: str | None = None, - ) -> dict: - q = query or search - if not q: - return {"error": "Search query is required"} - - fw = framework or _current_framework - - if self._use_local(fw): - matches = await self._local.search_components(q) - return { - "framework": self._fw_name(fw), - "query": q, - "source": "local", - "results": matches, - } - - client, source = await self._remote_client() - matches = await client.search_components(q, fw) - return { - "framework": self._fw_name(fw), - "query": q, - "source": source, - "results": matches, - } - - @self.action("get_structure", "Browse repository directory structure") - async def get_structure( - ctx: MCPContext, - path: str = "", - framework: str | None = None, - depth: int = 3, - ) -> dict: - fw = framework or _current_framework - - if self._use_local(fw): - structure = await self._local.get_directory_structure(path, fw) - return { - "framework": self._fw_name(fw), - "path": path or "pkg/", - "source": "local", - "structure": structure, - } - - client, source = await self._remote_client() - structure = await client.get_directory_structure(path, fw) - return { - "framework": self._fw_name(fw), - "path": path or "/", - "source": source, - "structure": structure, - } - - @self.action("install", "Install component via CLI") - async def install( - ctx: MCPContext, - name: str | None = None, - component: str | None = None, - framework: str | None = None, - overwrite: bool = False, - ) -> dict: - comp_name = name or component - if not comp_name: - return {"error": "Component name is required"} - - fw = framework or _current_framework - - if fw.startswith("hanzo"): - cmd = f"npx @hanzo/ui add {comp_name}" - elif fw in ("shadcn", "react"): - cmd = f"npx shadcn@latest add {comp_name}" - else: - return {"error": f"Installation not supported for framework: {fw}"} - - if overwrite: - cmd += " --overwrite" - - proc = await asyncio.create_subprocess_shell( - cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=60) - - return { - "framework": self._fw_name(fw), - "component": comp_name, - "command": cmd, - "output": stdout.decode() if stdout else "", - "warnings": stderr.decode() if stderr else "", - } - - @self.action("set_framework", "Switch active framework") - async def set_framework(ctx: MCPContext, framework: str | None = None) -> dict: - global _current_framework - if not framework: - return {"error": "Framework is required"} - if framework not in FRAMEWORK_CONFIGS: - return { - "error": f"Unknown framework: {framework}. Available: {', '.join(FRAMEWORK_CONFIGS)}" - } - _current_framework = framework - return { - "success": True, - "framework": self._fw_name(framework), - "message": f"Switched to {self._fw_name(framework)}", - } - - @self.action("get_framework", "Show current and available frameworks") - async def get_framework(ctx: MCPContext) -> dict: - registry_up = await self._use_registry() - return { - "current": self._fw_name(_current_framework), - "framework": _current_framework, - "local_available": self._local.available, - "registry_available": registry_up, - "available": [ - { - "key": key, - "name": FRAMEWORK_NAMES.get(key, key), - "has_registry": key.startswith("hanzo"), - } - for key in FRAMEWORK_CONFIGS - ], - } - - @self.action("create_composition", "Scaffold a composition from components") - async def create_composition( - ctx: MCPContext, - name: str | None = None, - components: list[str] | None = None, - description: str | None = None, - framework: str | None = None, - ) -> dict: - if not name: - return {"error": "Composition name is required"} - - fw = framework or _current_framework - comps = components or [] - - lines = ["/**", f" * {name}"] - if description: - lines.append(f" * {description}") - lines.append(f" * Framework: {self._fw_name(fw)}") - lines.append(f" * Components: {', '.join(comps)}") - lines.append(" */") - lines.append("") - - def pascal(s: str) -> str: - return "".join(p.capitalize() for p in s.split("-")) - - if fw.startswith("hanzo"): - for comp in comps: - lines.append(f'import {{ {pascal(comp)} }} from "@hanzo/ui/{comp}"') - elif fw in ("shadcn", "react"): - for comp in comps: - lines.append(f'import {{ {pascal(comp)} }} from "@/components/ui/{comp}"') - - lines.append("") - lines.append(f"export function {name}() {{") - lines.append(" return (") - lines.append('

          ') - for comp in comps: - lines.append(f" <{pascal(comp)} />") - lines.append("
          ") - lines.append(" )") - lines.append("}") - - code = "\n".join(lines) + "\n" - - return { - "framework": self._fw_name(fw), - "name": name, - "code": code, - "components": comps, - } - - @self.action("list_packages", "List all UI packages (local only)") - async def list_packages(ctx: MCPContext) -> dict: - if not self._local.available: - return { - "error": "Local hanzo/ui repo not found. Set HANZO_UI_PATH or clone to ~/work/hanzo/ui" - } - - packages = await self._local.list_packages() - return { - "source": "local", - "total": len(packages), - "packages": packages, - } - - @self.action("read_file", "Read any file from the UI repo by relative path") - async def read_file( - ctx: MCPContext, - path: str | None = None, - file: str | None = None, - ) -> dict: - file_path = path or file - if not file_path: - return {"error": "File path is required (relative to hanzo/ui root)"} - - if not self._local.available: - return { - "error": "Local hanzo/ui repo not found. Set HANZO_UI_PATH or clone to ~/work/hanzo/ui" - } - - content = await self._local.read_file(file_path) - return { - "path": file_path, - "source": "local", - "content": content, - } - - # --- Search / RAG actions (Hanzo Cloud backend) --- - - @self.action("ask", "Ask a question about UI components (RAG-powered via Hanzo Cloud)") - async def ask( - ctx: MCPContext, - question: str | None = None, - query: str | None = None, - ) -> dict: - q = question or query - if not q: - return {"error": "Question is required"} - - from .vector_index import chat_about_components - - return await chat_about_components(q) - - @self.action("semantic_search", "Semantic search over UI components (Hanzo Cloud)") - async def semantic_search( - ctx: MCPContext, - query: str | None = None, - question: str | None = None, - limit: int = 10, - tags: list[str] | None = None, - ) -> dict: - q = query or question - if not q: - return {"error": "Query is required"} - - from .vector_index import search_components - - results = await search_components(q, limit=limit, tags=tags) - return {"query": q, "results": results} - - @self.action("index_status", "Check search index status") - async def index_status(ctx: MCPContext) -> dict: - from .vector_index import get_index_stats - - return await get_index_stats() - - @self.action("rebuild_index", "Re-index UI components into Hanzo Cloud search") - async def rebuild_index(ctx: MCPContext) -> dict: - from .vector_index import index_from_local, index_from_registry - - if self._local.available: - return await index_from_local() - return await index_from_registry() - - # Inherits call() and register() from BaseTool โ€” action routing is automatic - - -# Singleton reference -ui_tool = UiTool diff --git a/pkg/hanzo-tools-ui/hanzo_tools/ui/vector_index.py b/pkg/hanzo-tools-ui/hanzo_tools/ui/vector_index.py deleted file mode 100644 index 983da2ed2..000000000 --- a/pkg/hanzo-tools-ui/hanzo_tools/ui/vector_index.py +++ /dev/null @@ -1,270 +0,0 @@ -"""UI component search via Hanzo Cloud search infrastructure. - -Uses the Hanzo Cloud RAG pipeline: -- POST /api/search-docs โ€” hybrid fulltext + vector search -- POST /api/chat-docs โ€” RAG chat with component context -- POST /api/index-docs โ€” index components into the search backend - -Authentication: publishable key (pk-*) for read, API key (hk-*) for write. -""" - -import logging -import os -from typing import Any - -import httpx - -logger = logging.getLogger(__name__) - -# Hanzo Cloud search endpoints -CLOUD_API = os.environ.get("HANZO_CLOUD_API", "https://cloud-api.hanzo.ai") -SEARCH_ENDPOINT = f"{CLOUD_API}/api/search-docs" -CHAT_ENDPOINT = f"{CLOUD_API}/api/chat-docs" -INDEX_ENDPOINT = f"{CLOUD_API}/api/index-docs" -STATS_ENDPOINT = f"{CLOUD_API}/api/search-docs/stats" - -# Search index for UI components -SEARCH_INDEX = os.environ.get("HANZO_UI_SEARCH_INDEX", "app-ui-hanzo-ai") - -# Keys -PUBLISHABLE_KEY = os.environ.get("HANZO_UI_SEARCH_KEY", "pk-hanzo-ui-search-2026") -ADMIN_KEY = os.environ.get("HANZO_SEARCH_ADMIN_KEY", "") - - -def _auth_headers(write: bool = False) -> dict[str, str]: - """Get auth headers โ€” publishable key for reads, admin key for writes.""" - key = ADMIN_KEY if write else PUBLISHABLE_KEY - if not key: - key = os.environ.get("HANZO_API_KEY", "") - return { - "Authorization": f"Bearer {key}", - "Content-Type": "application/json", - } - - -async def search_components( - query: str, - limit: int = 10, - tags: list[str] | None = None, -) -> list[dict]: - """Search UI components using Hanzo Cloud hybrid search. - - Uses both fulltext (Meilisearch) and vector (Qdrant) search. - """ - payload: dict[str, Any] = { - "query": query, - "index": SEARCH_INDEX, - "limit": limit, - } - if tags: - payload["tags"] = tags - - async with httpx.AsyncClient(timeout=15.0) as client: - resp = await client.post( - SEARCH_ENDPOINT, - headers=_auth_headers(), - json=payload, - ) - resp.raise_for_status() - return resp.json() - - -async def chat_about_components( - question: str, - history: list[dict] | None = None, - stream: bool = False, -) -> dict | Any: - """RAG chat โ€” ask questions about UI components. - - Uses Hanzo Cloud's /api/chat-docs which: - 1. Searches the component index for relevant context - 2. Passes context + question to the LLM - 3. Returns a grounded answer - """ - messages = history or [] - messages.append({"role": "user", "content": question}) - - payload = { - "messages": messages, - "index": SEARCH_INDEX, - "stream": stream, - "systemPrompt": ( - "You are a UI component expert for the Hanzo UI library. " - "Answer questions about components, their props, usage patterns, " - "and how to compose them. Be specific โ€” reference component names, " - "props, and show code examples when helpful. " - "The components are React/TypeScript using Radix UI primitives " - "and Tailwind CSS." - ), - } - - async with httpx.AsyncClient(timeout=30.0) as client: - resp = await client.post( - CHAT_ENDPOINT, - headers=_auth_headers(), - json=payload, - ) - resp.raise_for_status() - - if stream: - return resp.text # SSE stream - return resp.json() - - -async def index_components( - components: list[dict], - replace: bool = False, -) -> dict: - """Index UI components into Hanzo Cloud search. - - Each component becomes a document with: - - id: component name - - title: component name (human-readable) - - content: source code + metadata - - url: link to component page - - tag: component type (ui, block, example, etc.) - - section: package/category - """ - documents = [] - for comp in components: - name = comp["name"] - source = comp.get("source", "") - comp_type = comp.get("type", "components:ui") - deps = comp.get("dependencies", []) - reg_deps = comp.get("registryDependencies", []) - - # Build rich content for embedding - content_parts = [ - f"Component: {name}", - f"Type: {comp_type}", - ] - if deps: - content_parts.append(f"Dependencies: {', '.join(deps)}") - if reg_deps: - content_parts.append(f"Registry dependencies: {', '.join(reg_deps)}") - if source: - content_parts.append(f"\nSource code:\n{source}") - - documents.append( - { - "id": f"component-{name}", - "page_id": name, - "title": name.replace("-", " ").title(), - "url": f"https://ui.hanzo.ai/components/{name}", - "content": "\n".join(content_parts), - "section": comp_type.split(":")[-1] if ":" in comp_type else "ui", - "tag": comp_type, - } - ) - - admin_key = ADMIN_KEY or os.environ.get("HANZO_API_KEY", "") - if not admin_key: - return {"error": "HANZO_SEARCH_ADMIN_KEY or HANZO_API_KEY required for indexing"} - - payload: dict[str, Any] = { - "index": SEARCH_INDEX, - "documents": documents, - } - if replace: - payload["replace"] = True - - async with httpx.AsyncClient(timeout=60.0) as client: - resp = await client.post( - INDEX_ENDPOINT, - headers=_auth_headers(write=True), - json=payload, - ) - resp.raise_for_status() - return resp.json() - - -async def get_index_stats() -> dict: - """Get search index statistics.""" - async with httpx.AsyncClient(timeout=10.0) as client: - resp = await client.get( - STATS_ENDPOINT, - headers=_auth_headers(), - params={"index": SEARCH_INDEX}, - ) - resp.raise_for_status() - return resp.json() - - -async def index_from_local(ui_path: str | None = None) -> dict: - """Index all components from the local hanzo/ui repo into Hanzo Cloud.""" - from .local_client import LocalUIClient - - local = LocalUIClient(ui_path) - if not local.available: - return {"error": "Local hanzo/ui repo not found"} - - comp_list = await local.list_components() - components = [] - - for comp in comp_list: - name = comp["name"] - try: - source = await local.fetch_component(name) - components.append( - { - "name": name, - "type": comp.get("type", "file"), - "source": source, - } - ) - except Exception as e: - logger.debug("Skipping %s: %s", name, e) - - if not components: - return {"error": "No components found"} - - result = await index_components(components, replace=True) - return { - "indexed": len(components), - "source": "local", - **result, - } - - -async def index_from_registry(registry_url: str | None = None) -> dict: - """Index all components from the registry into Hanzo Cloud.""" - from .registry.client import RegistryClient - - client = RegistryClient(registry_url) - try: - index_data = await client.fetch_full_index() - except Exception as e: - return {"error": f"Failed to fetch registry: {e}"} - - raw_components = index_data.get("components", {}) - components = [] - - for name, data in raw_components.items(): - files = data.get("files", []) - source = "" - if files: - first = files[0] - if isinstance(first, dict): - source = first.get("content", "") - - components.append( - { - "name": name, - "type": data.get("type", ""), - "source": source, - "dependencies": data.get("dependencies", []), - "registryDependencies": data.get("registryDependencies", []), - } - ) - - await client.close() - - if not components: - return {"error": "No components in registry"} - - result = await index_components(components, replace=True) - return { - "indexed": len(components), - "source": "registry", - **result, - } diff --git a/pkg/hanzo-tools-ui/pyproject.toml b/pkg/hanzo-tools-ui/pyproject.toml deleted file mode 100644 index 86efcde40..000000000 --- a/pkg/hanzo-tools-ui/pyproject.toml +++ /dev/null @@ -1,73 +0,0 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[project] -name = "hanzo-tools-ui" -version = "0.2.0" -description = "UI component registry tools and server for Hanzo AI (HIP-0300)" -readme = "README.md" -license = "MIT" -requires-python = ">=3.12" -authors = [ - { name = "Hanzo AI Team", email = "ai@hanzo.ai" }, -] -keywords = [ - "hanzo", - "mcp", - "tools", - "ui", - "components", - "shadcn", - "registry", -] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.12", -] -dependencies = [ - "hanzo-tools-core>=0.1.0", - "mcp>=1.0.0", - "httpx>=0.25.0", - "aiofiles>=24.0.0", -] - -[project.optional-dependencies] -server = [ - "fastapi>=0.115.0", - "uvicorn[standard]>=0.34.0", -] -dev = [ - "pytest>=8.0.0", - "pytest-asyncio>=0.23.0", - "ruff>=0.1.0", -] - -[project.scripts] -hanzo-ui-registry = "hanzo_tools.ui.registry.__main__:main" - -[project.entry-points."hanzo.tools"] -ui = "hanzo_tools.ui:TOOLS" - -[project.urls] -Homepage = "https://github.com/hanzoai/python-sdk" -Documentation = "https://docs.hanzo.ai" -Repository = "https://github.com/hanzoai/python-sdk" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] - -[tool.ruff] -line-length = 100 -target-version = "py312" - -[tool.ruff.lint] -select = ["E", "F", "I", "UP"] -ignore = ["E501"] - -[tool.pytest.ini_options] -asyncio_mode = "auto" -testpaths = ["tests"] diff --git a/pkg/hanzo-tools-vcs/README.md b/pkg/hanzo-tools-vcs/README.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-tools-vcs/hanzo_tools/__init__.py b/pkg/hanzo-tools-vcs/hanzo_tools/__init__.py deleted file mode 100644 index 946984951..000000000 --- a/pkg/hanzo-tools-vcs/hanzo_tools/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Namespace package -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-vcs/hanzo_tools/vcs/__init__.py b/pkg/hanzo-tools-vcs/hanzo_tools/vcs/__init__.py deleted file mode 100644 index 401ab0268..000000000 --- a/pkg/hanzo-tools-vcs/hanzo_tools/vcs/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -"""Version control tools for Hanzo AI (HIP-0300). - -Tools: -- vcs: Unified version control tool (HIP-0300) - - status: Working tree status - - diff: Show differences (unified patch format) - - apply: Apply patch - - commit: Create commit - - branch: Branch operations (list, create, delete) - - checkout: Switch branches - - log: Commit history - -Outputs diffs in unified patch format for use with fs.apply_patch. - -Install: - pip install hanzo-tools-vcs - -Usage: - from hanzo_tools.vcs import register_tools, TOOLS - - # Register with MCP server - register_tools(mcp_server) - - # Or access the unified tool - from hanzo_tools.vcs import VcsTool -""" - -from hanzo_tools.core import BaseTool, ToolRegistry - -from .git_tool import GitTool, git_tool - -# Backward compat -VcsTool = GitTool -vcs_tool = git_tool - -# Export list for tool discovery - HIP-0300 unified tool -TOOLS = [GitTool] - -__all__ = [ - "GitTool", - "git_tool", - "VcsTool", - "vcs_tool", - "register_tools", - "TOOLS", -] - - -def register_tools(mcp_server, **kwargs) -> list[BaseTool]: - """Register vcs tools with the MCP server. - - Args: - mcp_server: The FastMCP server instance - **kwargs: Additional options (cwd, etc.) - - Returns: - List of registered tool instances - """ - cwd = kwargs.get("cwd") - tool = VcsTool(cwd=cwd) - ToolRegistry.register_tool(mcp_server, tool) - return [tool] diff --git a/pkg/hanzo-tools-vcs/hanzo_tools/vcs/git_tool.py b/pkg/hanzo-tools-vcs/hanzo_tools/vcs/git_tool.py deleted file mode 100644 index 205b32a84..000000000 --- a/pkg/hanzo-tools-vcs/hanzo_tools/vcs/git_tool.py +++ /dev/null @@ -1,712 +0,0 @@ -"""Unified version control tool for HIP-0300 architecture. - -This module provides a single unified 'git' tool that handles all version control operations: -- status: Working tree status -- diff: Show differences -- apply: Apply patch -- commit: Create commit -- branch: Branch operations -- checkout: Switch branches -- log: Commit history - -Following Unix philosophy: one tool for the Diffs + History axis. -Outputs diffs in unified patch format; integrates with fs.apply_patch. -""" - -import asyncio -import os -from typing import ClassVar - -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - InvalidParamsError, - ToolError, -) - - -class GitTool(BaseTool): - """Unified version control tool (HIP-0300). - - Handles all VCS operations on a single axis: - - status: Working tree status - - diff: Show differences - - apply: Apply patch - - commit: Create commit - - branch: Branch operations - - checkout: Switch branches - - log: Commit history - - Outputs diffs in unified patch format. - """ - - name: ClassVar[str] = "git" - VERSION: ClassVar[str] = "0.12.0" - - def __init__(self, cwd: str | None = None): - super().__init__() - self.cwd = cwd or os.getcwd() - self._register_vcs_actions() - - @property - def description(self) -> str: - return """Unified version control tool (HIP-0300). - -Actions: -- status: Working tree status -- diff: Show differences (unified patch format) -- apply: Apply patch -- commit: Create commit -- branch: Branch operations (list, create, delete) -- checkout: Switch branches -- log: Commit history - -Outputs diffs in unified patch format for use with fs.apply_patch. -""" - - async def _run_git( - self, - *args: str, - cwd: str | None = None, - check: bool = True, - ) -> tuple[str, str, int]: - """Run git command and return stdout, stderr, returncode.""" - cmd = ["git", *args] - work_dir = cwd or self.cwd - - try: - proc = await asyncio.create_subprocess_exec( - *cmd, - cwd=work_dir, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=30) - - stdout_str = stdout.decode("utf-8", errors="replace") - stderr_str = stderr.decode("utf-8", errors="replace") - - if check and proc.returncode != 0: - raise ToolError( - code="INTERNAL_ERROR", - message=f"git {args[0]} failed: {stderr_str}", - details={"returncode": proc.returncode}, - ) - - return stdout_str, stderr_str, proc.returncode - - except asyncio.TimeoutError: - raise ToolError( - code="TIMEOUT", - message=f"git {args[0]} timed out", - ) - except FileNotFoundError: - raise ToolError( - code="NOT_FOUND", - message="git not found in PATH", - ) - - def _register_vcs_actions(self): - """Register all VCS actions.""" - - @self.action("status", "Get working tree status") - async def status( - ctx: MCPContext, - cwd: str | None = None, - ) -> dict: - """Get working tree status. - - Returns lists of staged, unstaged, and untracked files. - """ - work_dir = cwd or self.cwd - - # Get status in porcelain format for parsing - stdout, _, _ = await self._run_git( - "status", - "--porcelain=v2", - "--branch", - cwd=work_dir, - ) - - branch = None - staged = [] - unstaged = [] - untracked = [] - - for line in stdout.splitlines(): - if line.startswith("# branch.head"): - branch = line.split()[-1] - elif line.startswith("1 ") or line.startswith("2 "): - # Changed entry - parts = line.split() - xy = parts[1] # XY status - path = parts[-1] - - if xy[0] != ".": - staged.append({"path": path, "status": xy[0]}) - if xy[1] != ".": - unstaged.append({"path": path, "status": xy[1]}) - elif line.startswith("? "): - # Untracked - path = line[2:] - untracked.append({"path": path}) - - return { - "branch": branch, - "staged": staged, - "unstaged": unstaged, - "untracked": untracked, - "clean": len(staged) == 0 - and len(unstaged) == 0 - and len(untracked) == 0, - } - - @self.action("diff", "Show differences") - async def diff( - ctx: MCPContext, - ref: str | None = None, - staged: bool = False, - path: str | None = None, - cwd: str | None = None, - ) -> dict: - """Show diff in unified patch format. - - Args: - ref: Commit or ref to compare against - staged: Show staged changes only - path: Limit to specific path - cwd: Working directory - - Returns: - Unified diff output - """ - work_dir = cwd or self.cwd - args = ["diff", "--no-color"] - - if staged: - args.append("--cached") - - if ref: - args.append(ref) - - if path: - args.extend(["--", path]) - - stdout, _, _ = await self._run_git(*args, cwd=work_dir) - - return { - "diff": stdout, - "format": "unified", - "ref": ref, - "staged": staged, - } - - @self.action("apply", "Apply patch") - async def apply( - ctx: MCPContext, - patch: str, - cwd: str | None = None, - check: bool = False, - ) -> dict: - """Apply a unified diff patch. - - Args: - patch: Unified diff patch content - cwd: Working directory - check: Only check if patch applies cleanly - - Returns: - Success status - """ - work_dir = cwd or self.cwd - args = ["apply"] - - if check: - args.append("--check") - - args.append("-") # Read from stdin - - try: - proc = await asyncio.create_subprocess_exec( - "git", - *args, - cwd=work_dir, - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await asyncio.wait_for( - proc.communicate(patch.encode()), - timeout=30, - ) - - if proc.returncode != 0: - raise ToolError( - code="CONFLICT", - message=f"Patch failed: {stderr.decode()}", - ) - - return { - "applied": not check, - "clean": True, - } - - except asyncio.TimeoutError: - raise ToolError(code="TIMEOUT", message="git apply timed out") - - @self.action("commit", "Create commit") - async def commit( - ctx: MCPContext, - message: str, - files: list[str] | None = None, - all: bool = False, - cwd: str | None = None, - ) -> dict: - """Create a commit. - - Args: - message: Commit message - files: Specific files to commit - all: Stage all modified files - cwd: Working directory - - Returns: - Commit hash and info - """ - work_dir = cwd or self.cwd - - # Stage files if specified - if files: - await self._run_git("add", *files, cwd=work_dir) - elif all: - await self._run_git("add", "-A", cwd=work_dir) - - # Create commit - args = ["commit", "-m", message] - stdout, _, _ = await self._run_git(*args, cwd=work_dir) - - # Get commit hash - hash_stdout, _, _ = await self._run_git( - "rev-parse", - "HEAD", - cwd=work_dir, - ) - - return { - "hash": hash_stdout.strip(), - "message": message, - "output": stdout, - } - - @self.action("branch", "Branch operations") - async def branch( - ctx: MCPContext, - op: str = "list", # list, create, delete - name: str | None = None, - cwd: str | None = None, - ) -> dict: - """Branch operations. - - Args: - op: Operation - list, create, delete - name: Branch name (for create/delete) - cwd: Working directory - - Returns: - Operation result - """ - work_dir = cwd or self.cwd - - if op == "list": - stdout, _, _ = await self._run_git( - "branch", - "-a", - "--format=%(refname:short)", - cwd=work_dir, - ) - branches = [b.strip() for b in stdout.splitlines() if b.strip()] - - # Get current branch - current, _, _ = await self._run_git( - "branch", - "--show-current", - cwd=work_dir, - ) - - return { - "branches": branches, - "current": current.strip(), - } - - elif op == "create": - if not name: - raise InvalidParamsError("Branch name required", param="name") - await self._run_git("branch", name, cwd=work_dir) - return {"created": name} - - elif op == "delete": - if not name: - raise InvalidParamsError("Branch name required", param="name") - await self._run_git("branch", "-d", name, cwd=work_dir) - return {"deleted": name} - - else: - raise InvalidParamsError( - f"Unknown operation: {op}", - param="op", - expected="list, create, or delete", - ) - - @self.action("checkout", "Switch branches") - async def checkout( - ctx: MCPContext, - ref: str, - create: bool = False, - cwd: str | None = None, - ) -> dict: - """Switch to a branch or commit. - - Args: - ref: Branch name or commit - create: Create branch if it doesn't exist - cwd: Working directory - - Returns: - Checkout result - """ - work_dir = cwd or self.cwd - args = ["checkout"] - - if create: - args.append("-b") - - args.append(ref) - - stdout, _, _ = await self._run_git(*args, cwd=work_dir) - - return { - "ref": ref, - "created": create, - } - - @self.action("log", "Commit history") - async def log( - ctx: MCPContext, - limit: int = 10, - path: str | None = None, - ref: str | None = None, - cwd: str | None = None, - ) -> dict: - """Get commit history. - - Args: - limit: Number of commits - path: Filter by path - ref: Starting ref - cwd: Working directory - - Returns: - List of commits - """ - work_dir = cwd or self.cwd - args = [ - "log", - f"-{limit}", - "--format=format:%H|%an|%ae|%at|%s", - ] - - if ref: - args.append(ref) - - if path: - args.extend(["--", path]) - - stdout, _, _ = await self._run_git(*args, cwd=work_dir) - - commits = [] - for line in stdout.splitlines(): - if not line: - continue - parts = line.split("|", 4) - if len(parts) >= 5: - commits.append( - { - "hash": parts[0], - "author": parts[1], - "email": parts[2], - "timestamp": int(parts[3]), - "message": parts[4], - } - ) - - return { - "commits": commits, - "total": len(commits), - } - - # โ”€โ”€ Advanced actions (TS parity) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - @self.action("blame", "Show line-by-line authorship") - async def blame(ctx: MCPContext, path: str, cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - stdout, _, _ = await self._run_git("blame", "--porcelain", path, cwd=work_dir) - return {"blame": stdout, "path": path} - - @self.action("show", "Show commit or object") - async def show(ctx: MCPContext, ref: str = "HEAD", cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - stdout, _, _ = await self._run_git("show", "--stat", ref, cwd=work_dir) - return {"output": stdout, "ref": ref} - - @self.action("stash", "Stash operations") - async def stash(ctx: MCPContext, op: str = "list", message: str | None = None, cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - if op == "push": - args = ["stash", "push"] - if message: - args.extend(["-m", message]) - stdout, _, _ = await self._run_git(*args, cwd=work_dir) - elif op == "pop": - stdout, _, _ = await self._run_git("stash", "pop", cwd=work_dir) - elif op == "drop": - stdout, _, _ = await self._run_git("stash", "drop", cwd=work_dir) - else: - stdout, _, _ = await self._run_git("stash", "list", cwd=work_dir) - return {"output": stdout, "op": op} - - @self.action("tag", "Tag operations") - async def tag(ctx: MCPContext, op: str = "list", name: str | None = None, message: str | None = None, ref: str | None = None, cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - if op == "create": - if not name: - raise InvalidParamsError("Tag name required", param="name") - args = ["tag"] - if message: - args.extend(["-a", name, "-m", message]) - else: - args.append(name) - if ref: - args.append(ref) - stdout, _, _ = await self._run_git(*args, cwd=work_dir) - return {"created": name} - elif op == "delete": - if not name: - raise InvalidParamsError("Tag name required", param="name") - stdout, _, _ = await self._run_git("tag", "-d", name, cwd=work_dir) - return {"deleted": name} - else: - stdout, _, _ = await self._run_git("tag", "-l", cwd=work_dir) - return {"tags": stdout.splitlines()} - - @self.action("remote", "Remote operations") - async def remote(ctx: MCPContext, op: str = "list", name: str | None = None, url: str | None = None, cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - if op == "add": - if not name or not url: - raise InvalidParamsError("name and url required", param="name") - stdout, _, _ = await self._run_git("remote", "add", name, url, cwd=work_dir) - return {"added": name, "url": url} - elif op == "remove": - if not name: - raise InvalidParamsError("Remote name required", param="name") - stdout, _, _ = await self._run_git("remote", "remove", name, cwd=work_dir) - return {"removed": name} - else: - stdout, _, _ = await self._run_git("remote", "-v", cwd=work_dir) - return {"remotes": stdout} - - @self.action("merge", "Merge branches") - async def merge(ctx: MCPContext, ref: str, message: str | None = None, no_ff: bool = False, cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - args = ["merge"] - if no_ff: - args.append("--no-ff") - if message: - args.extend(["-m", message]) - args.append(ref) - stdout, _, _ = await self._run_git(*args, cwd=work_dir, check=False) - return {"output": stdout, "ref": ref} - - @self.action("rebase", "Rebase commits") - async def rebase(ctx: MCPContext, ref: str | None = None, op: str = "start", cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - if op == "abort": - stdout, _, _ = await self._run_git("rebase", "--abort", cwd=work_dir) - elif op == "continue": - stdout, _, _ = await self._run_git("rebase", "--continue", cwd=work_dir) - else: - if not ref: - raise InvalidParamsError("ref required for rebase", param="ref") - stdout, _, _ = await self._run_git("rebase", ref, cwd=work_dir) - return {"output": stdout, "op": op} - - @self.action("cherry_pick", "Cherry-pick commits") - async def cherry_pick(ctx: MCPContext, ref: str, cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - stdout, _, _ = await self._run_git("cherry-pick", ref, cwd=work_dir) - return {"output": stdout, "ref": ref} - - @self.action("reset", "Reset HEAD") - async def reset(ctx: MCPContext, ref: str = "HEAD", mode: str = "mixed", cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - stdout, _, _ = await self._run_git("reset", f"--{mode}", ref, cwd=work_dir) - return {"output": stdout, "ref": ref, "mode": mode} - - @self.action("clean", "Remove untracked files") - async def clean(ctx: MCPContext, force: bool = False, directories: bool = False, dry_run: bool = True, cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - args = ["clean"] - if dry_run: - args.append("-n") - if force: - args.append("-f") - if directories: - args.append("-d") - stdout, _, _ = await self._run_git(*args, cwd=work_dir) - return {"output": stdout, "dry_run": dry_run} - - @self.action("init", "Initialize repository") - async def init(ctx: MCPContext, path: str | None = None, bare: bool = False, cwd: str | None = None) -> dict: - args = ["init"] - if bare: - args.append("--bare") - if path: - args.append(path) - stdout, _, _ = await self._run_git(*args, cwd=cwd or self.cwd) - return {"output": stdout} - - @self.action("clone", "Clone repository") - async def clone(ctx: MCPContext, url: str, path: str | None = None, depth: int | None = None, cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - args = ["clone"] - if depth: - args.extend(["--depth", str(depth)]) - args.append(url) - if path: - args.append(path) - stdout, _, _ = await self._run_git(*args, cwd=work_dir) - return {"output": stdout, "url": url} - - @self.action("fetch", "Fetch from remote") - async def fetch(ctx: MCPContext, remote: str = "origin", prune: bool = False, cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - args = ["fetch", remote] - if prune: - args.append("--prune") - stdout, _, _ = await self._run_git(*args, cwd=work_dir) - return {"output": stdout, "remote": remote} - - @self.action("pull", "Pull from remote") - async def pull(ctx: MCPContext, remote: str = "origin", branch: str | None = None, rebase: bool = False, cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - args = ["pull"] - if rebase: - args.append("--rebase") - args.append(remote) - if branch: - args.append(branch) - stdout, _, _ = await self._run_git(*args, cwd=work_dir) - return {"output": stdout} - - @self.action("push", "Push to remote") - async def push(ctx: MCPContext, remote: str = "origin", branch: str | None = None, force: bool = False, tags: bool = False, cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - args = ["push"] - if force: - args.append("--force-with-lease") - if tags: - args.append("--tags") - args.append(remote) - if branch: - args.append(branch) - stdout, _, _ = await self._run_git(*args, cwd=work_dir) - return {"output": stdout} - - @self.action("config", "Get/set config") - async def config(ctx: MCPContext, key: str | None = None, value: str | None = None, scope: str = "local", cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - if key and value: - stdout, _, _ = await self._run_git("config", f"--{scope}", key, value, cwd=work_dir) - return {"set": key, "value": value} - elif key: - stdout, _, _ = await self._run_git("config", "--get", key, cwd=work_dir, check=False) - return {"key": key, "value": stdout.strip()} - else: - stdout, _, _ = await self._run_git("config", "--list", f"--{scope}", cwd=work_dir) - return {"config": stdout} - - @self.action("worktree", "Worktree operations") - async def worktree(ctx: MCPContext, op: str = "list", path: str | None = None, branch: str | None = None, cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - if op == "add": - if not path: - raise InvalidParamsError("path required", param="path") - args = ["worktree", "add", path] - if branch: - args.extend(["-b", branch]) - stdout, _, _ = await self._run_git(*args, cwd=work_dir) - elif op == "remove": - if not path: - raise InvalidParamsError("path required", param="path") - stdout, _, _ = await self._run_git("worktree", "remove", path, cwd=work_dir) - else: - stdout, _, _ = await self._run_git("worktree", "list", cwd=work_dir) - return {"output": stdout, "op": op} - - @self.action("reflog", "Show reflog") - async def reflog(ctx: MCPContext, limit: int = 20, ref: str = "HEAD", cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - stdout, _, _ = await self._run_git("reflog", f"-{limit}", ref, cwd=work_dir) - return {"output": stdout} - - @self.action("shortlog", "Summarize commits by author") - async def shortlog(ctx: MCPContext, ref: str | None = None, cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - args = ["shortlog", "-sne"] - if ref: - args.append(ref) - stdout, _, _ = await self._run_git(*args, cwd=work_dir) - return {"output": stdout} - - @self.action("rev_parse", "Parse revision") - async def rev_parse(ctx: MCPContext, ref: str = "HEAD", cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - stdout, _, _ = await self._run_git("rev-parse", ref, cwd=work_dir) - return {"sha": stdout.strip(), "ref": ref} - - @self.action("describe", "Describe commit with tags") - async def describe(ctx: MCPContext, ref: str | None = None, tags: bool = True, cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - args = ["describe"] - if tags: - args.append("--tags") - args.append("--always") - if ref: - args.append(ref) - stdout, _, _ = await self._run_git(*args, cwd=work_dir, check=False) - return {"description": stdout.strip()} - - @self.action("bisect", "Binary search for bugs") - async def bisect(ctx: MCPContext, op: str = "start", ref: str | None = None, cwd: str | None = None) -> dict: - work_dir = cwd or self.cwd - if op == "start": - stdout, _, _ = await self._run_git("bisect", "start", cwd=work_dir) - elif op == "good": - args = ["bisect", "good"] - if ref: - args.append(ref) - stdout, _, _ = await self._run_git(*args, cwd=work_dir) - elif op == "bad": - args = ["bisect", "bad"] - if ref: - args.append(ref) - stdout, _, _ = await self._run_git(*args, cwd=work_dir) - elif op == "reset": - stdout, _, _ = await self._run_git("bisect", "reset", cwd=work_dir) - else: - stdout, _, _ = await self._run_git("bisect", "log", cwd=work_dir, check=False) - return {"output": stdout, "op": op} - -# Backward compatibility -git_tool = GitTool diff --git a/pkg/hanzo-tools-vcs/pyproject.toml b/pkg/hanzo-tools-vcs/pyproject.toml deleted file mode 100644 index 601fd5f8a..000000000 --- a/pkg/hanzo-tools-vcs/pyproject.toml +++ /dev/null @@ -1,65 +0,0 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[project] -name = "hanzo-tools-vcs" -version = "0.1.2" -description = "Unified version control tool for Hanzo AI (HIP-0300)" -readme = "README.md" -license = "MIT" -requires-python = ">=3.12" -authors = [ - { name = "Hanzo AI Team", email = "ai@hanzo.ai" }, -] -keywords = [ - "hanzo", - "mcp", - "tools", - "vcs", - "git", - "version-control", -] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", -] -dependencies = [ - "hanzo-tools>=0.3.0", - "mcp>=1.0.0", -] - -[project.optional-dependencies] -dev = [ - "pytest>=8.0.0", - "pytest-asyncio>=0.23.0", - "ruff>=0.1.0", -] - -[project.entry-points."hanzo.tools"] -vcs = "hanzo_tools.vcs:TOOLS" - -[project.urls] -Homepage = "https://github.com/hanzoai/python-sdk" -Documentation = "https://docs.hanzo.ai" -Repository = "https://github.com/hanzoai/python-sdk" - -[tool.hatch.build.targets.wheel] -packages = ["hanzo_tools"] - -[tool.ruff] -line-length = 100 -target-version = "py310" - -[tool.ruff.lint] -select = ["E", "F", "I", "UP"] -ignore = ["E501"] - -[tool.pytest.ini_options] -asyncio_mode = "auto" -testpaths = ["tests"] diff --git a/pkg/hanzo-tools-vector/README.md b/pkg/hanzo-tools-vector/README.md deleted file mode 100644 index 87e2913bb..000000000 --- a/pkg/hanzo-tools-vector/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# hanzo-tools-vector - -Vector search and embedding tools for Hanzo AI MCP. - -## Tools - -- `index` - Project indexing for semantic search -- `vector_index` - Create and manage vector embeddings -- `vector_search` - Semantic search across indexed content - -## Installation - -```bash -pip install hanzo-tools-vector - -# With embedding models -pip install hanzo-tools-vector[full] -``` - -## Features - -- Semantic code search -- Document embedding and retrieval -- Project-aware indexing -- Multiple embedding model support - -## Usage - -```python -from hanzo_tools.vector import TOOLS, VECTOR_AVAILABLE, register_tools - -if VECTOR_AVAILABLE: - register_tools(mcp_server, permission_manager) -``` - -## Part of hanzo-tools - -This package is part of the modular [hanzo-tools](../hanzo-tools) ecosystem. diff --git a/pkg/hanzo-tools-vector/hanzo_tools/__init__.py b/pkg/hanzo-tools-vector/hanzo_tools/__init__.py deleted file mode 100644 index f4f8ea812..000000000 --- a/pkg/hanzo-tools-vector/hanzo_tools/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Hanzo Tools namespace package.""" - -__path__ = __import__("pkgutil").extend_path(__path__, __name__) diff --git a/pkg/hanzo-tools-vector/hanzo_tools/vector/__init__.py b/pkg/hanzo-tools-vector/hanzo_tools/vector/__init__.py deleted file mode 100644 index dbd046dcb..000000000 --- a/pkg/hanzo-tools-vector/hanzo_tools/vector/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Vector/embedding tools for Hanzo AI. - -Tools: -- vector_index: Index documents for semantic search -- vector_search: Search indexed documents -- index: Project indexing - -Install: - pip install hanzo-tools-vector[full] -""" - -import logging - -logger = logging.getLogger(__name__) - -_tools = [] -VECTOR_AVAILABLE = False - -try: - from .index_tool import IndexTool - - _tools.append(IndexTool) - from .vector_index import VectorIndexTool - - _tools.append(VectorIndexTool) - from .vector_search import VectorSearchTool - - _tools.append(VectorSearchTool) - VECTOR_AVAILABLE = True -except ImportError as e: - logger.debug(f"Vector tools not available: {e}") - -TOOLS = _tools - -__all__ = [ - "TOOLS", - "VECTOR_AVAILABLE", - "register_tools", -] - -if VECTOR_AVAILABLE: - __all__.extend(["IndexTool", "VectorIndexTool", "VectorSearchTool"]) - - -def register_tools( - mcp_server, permission_manager=None, enabled_tools: dict[str, bool] | None = None -): - """Register vector tools with MCP server.""" - if not VECTOR_AVAILABLE: - logger.warning("Vector tools not available - missing dependencies") - return [] - - from hanzo_tools.core import ToolRegistry - - enabled = enabled_tools or {} - registered = [] - - for tool_class in TOOLS: - tool_name = getattr(tool_class, "name", tool_class.__name__.lower()) - if enabled.get(tool_name, True): - try: - tool = ( - tool_class(permission_manager) - if permission_manager - else tool_class() - ) - ToolRegistry.register_tool(mcp_server, tool) - registered.append(tool) - except Exception as e: - logger.warning(f"Failed to register {tool_name}: {e}") - - return registered diff --git a/pkg/hanzo-tools-vector/hanzo_tools/vector/ast_analyzer.py b/pkg/hanzo-tools-vector/hanzo_tools/vector/ast_analyzer.py deleted file mode 100644 index 7823c3a1d..000000000 --- a/pkg/hanzo-tools-vector/hanzo_tools/vector/ast_analyzer.py +++ /dev/null @@ -1,500 +0,0 @@ -"""AST analysis and symbol extraction for code understanding.""" - -import ast -import hashlib -from typing import Any, Dict, List, Optional -from pathlib import Path -from dataclasses import asdict, dataclass - -try: - import tree_sitter - import tree_sitter_python as tspython - - TREE_SITTER_AVAILABLE = True -except ImportError: - TREE_SITTER_AVAILABLE = False - - -@dataclass -class Symbol: - """Represents a code symbol (function, class, variable, etc.).""" - - name: str - type: str # function, class, variable, import, etc. - file_path: str - line_start: int - line_end: int - column_start: int - column_end: int - scope: str # global, class, function - parent: Optional[str] = None # parent class/function - docstring: Optional[str] = None - signature: Optional[str] = None - references: List[str] = None # Files that reference this symbol - - def __post_init__(self): - if self.references is None: - self.references = [] - - -@dataclass -class ASTNode: - """Represents an AST node with metadata.""" - - type: str - name: Optional[str] - line_start: int - line_end: int - column_start: int - column_end: int - children: List["ASTNode"] = None - parent: Optional[str] = None - - def __post_init__(self): - if self.children is None: - self.children = [] - - -@dataclass -class FileAST: - """Complete AST representation of a file.""" - - file_path: str - file_hash: str - language: str - symbols: List[Symbol] - ast_nodes: List[ASTNode] - imports: List[str] - exports: List[str] - dependencies: List[str] # Files this file depends on - - def to_dict(self) -> Dict[str, Any]: - """Convert to dictionary for storage.""" - return { - "file_path": self.file_path, - "file_hash": self.file_hash, - "language": self.language, - "symbols": [asdict(s) for s in self.symbols], - "ast_nodes": [asdict(n) for n in self.ast_nodes], - "imports": self.imports, - "exports": self.exports, - "dependencies": self.dependencies, - } - - -class ASTAnalyzer: - """Analyzes code files and extracts AST information and symbols.""" - - def __init__(self): - """Initialize the AST analyzer.""" - self.parsers = {} - self._setup_parsers() - - def _setup_parsers(self): - """Set up tree-sitter parsers for different languages.""" - if TREE_SITTER_AVAILABLE: - try: - # Python parser - self.parsers["python"] = tree_sitter.Language(tspython.language()) - except Exception as e: - import logging - - logger = logging.getLogger(__name__) - logger.warning(f"Could not initialize Python parser: {e}") - - def analyze_file(self, file_path: str) -> Optional[FileAST]: - """Analyze a file and extract AST information and symbols. - - Args: - file_path: Path to the file to analyze - - Returns: - FileAST object with extracted information, or None if analysis fails - """ - path = Path(file_path) - if not path.exists(): - return None - - # Determine language - language = self._detect_language(path) - if not language: - return None - - try: - # Read file content - content = path.read_text(encoding="utf-8") - file_hash = hashlib.sha256(content.encode()).hexdigest() - - # Extract symbols and AST - if language == "python": - return self._analyze_python_file(file_path, content, file_hash) - else: - # Generic analysis for other languages - return self._analyze_generic_file( - file_path, content, file_hash, language - ) - - except Exception as e: - import logging - - logger = logging.getLogger(__name__) - logger.error(f"Error analyzing file {file_path}: {e}") - return None - - def _detect_language(self, path: Path) -> Optional[str]: - """Detect programming language from file extension.""" - extension = path.suffix.lower() - - language_map = { - ".py": "python", - ".js": "javascript", - ".ts": "typescript", - ".jsx": "javascript", - ".tsx": "typescript", - ".java": "java", - ".cpp": "cpp", - ".c": "c", - ".h": "c", - ".hpp": "cpp", - ".rs": "rust", - ".go": "go", - ".rb": "ruby", - ".php": "php", - ".cs": "csharp", - ".swift": "swift", - ".kt": "kotlin", - ".scala": "scala", - ".clj": "clojure", - ".hs": "haskell", - ".ml": "ocaml", - ".elm": "elm", - ".dart": "dart", - ".lua": "lua", - ".r": "r", - ".m": "objective-c", - ".mm": "objective-cpp", - } - - return language_map.get(extension) - - def _analyze_python_file( - self, file_path: str, content: str, file_hash: str - ) -> FileAST: - """Analyze Python file using both AST and tree-sitter.""" - symbols = [] - ast_nodes = [] - imports = [] - exports = [] - dependencies = [] - - try: - # Parse with Python AST - tree = ast.parse(content) - - # Extract symbols using AST visitor - visitor = PythonSymbolExtractor(file_path) - visitor.visit(tree) - - symbols.extend(visitor.symbols) - imports.extend(visitor.imports) - exports.extend(visitor.exports) - dependencies.extend(visitor.dependencies) - - # If tree-sitter is available, get more detailed AST - if TREE_SITTER_AVAILABLE and "python" in self.parsers: - parser = tree_sitter.Parser(self.parsers["python"]) - ts_tree = parser.parse(content.encode()) - ast_nodes = self._extract_tree_sitter_nodes(ts_tree.root_node, content) - - except SyntaxError as e: - import logging - - logger = logging.getLogger(__name__) - logger.error(f"Syntax error in {file_path}: {e}") - except Exception as e: - import logging - - logger = logging.getLogger(__name__) - logger.error(f"Error parsing Python file {file_path}: {e}") - - return FileAST( - file_path=file_path, - file_hash=file_hash, - language="python", - symbols=symbols, - ast_nodes=ast_nodes, - imports=imports, - exports=exports, - dependencies=dependencies, - ) - - def _analyze_generic_file( - self, file_path: str, content: str, file_hash: str, language: str - ) -> FileAST: - """Generic analysis for non-Python files.""" - # For now, just basic line-based analysis - # Could be enhanced with language-specific parsers - - symbols = [] - ast_nodes = [] - imports = [] - exports = [] - dependencies = [] - - # Basic pattern matching for common constructs - lines = content.split("\n") - for i, line in enumerate(lines, 1): - line = line.strip() - - # Basic function detection (works for many C-style languages) - if language in ["javascript", "typescript", "java", "cpp", "c"]: - if ( - "function " in line - or line.startswith("def ") - or " function(" in line - ): - # Extract function name - parts = line.split() - for j, part in enumerate(parts): - if part == "function" and j + 1 < len(parts): - func_name = parts[j + 1].split("(")[0] - symbols.append( - Symbol( - name=func_name, - type="function", - file_path=file_path, - line_start=i, - line_end=i, - column_start=0, - column_end=len(line), - scope="global", - ) - ) - break - - # Basic import detection - if "import " in line or "#include " in line or "require(" in line: - imports.append(line) - - return FileAST( - file_path=file_path, - file_hash=file_hash, - language=language, - symbols=symbols, - ast_nodes=ast_nodes, - imports=imports, - exports=exports, - dependencies=dependencies, - ) - - def _extract_tree_sitter_nodes(self, node, content: str) -> List[ASTNode]: - """Extract AST nodes from tree-sitter parse tree.""" - nodes = [] - - def traverse(ts_node, parent_name=None): - node_name = None - - # Try to extract node name for named nodes - if ts_node.type in [ - "function_definition", - "class_definition", - "identifier", - ]: - for child in ts_node.children: - if child.type == "identifier": - start_byte = child.start_byte - end_byte = child.end_byte - node_name = content[start_byte:end_byte] - break - - ast_node = ASTNode( - type=ts_node.type, - name=node_name, - line_start=ts_node.start_point[0] + 1, - line_end=ts_node.end_point[0] + 1, - column_start=ts_node.start_point[1], - column_end=ts_node.end_point[1], - parent=parent_name, - ) - - nodes.append(ast_node) - - # Recursively process children - for child in ts_node.children: - traverse(child, node_name or parent_name) - - traverse(node) - return nodes - - -class PythonSymbolExtractor(ast.NodeVisitor): - """AST visitor for extracting Python symbols.""" - - def __init__(self, file_path: str): - self.file_path = file_path - self.symbols = [] - self.imports = [] - self.exports = [] - self.dependencies = [] - self.scope_stack = ["global"] - - def visit_FunctionDef(self, node): - """Visit function definitions.""" - scope = ".".join(self.scope_stack) - parent = self.scope_stack[-1] if len(self.scope_stack) > 1 else None - - # Extract docstring - docstring = None - if ( - node.body - and isinstance(node.body[0], ast.Expr) - and isinstance(node.body[0].value, ast.Constant) - and isinstance(node.body[0].value.value, str) - ): - docstring = node.body[0].value.value - - # Create function signature - args = [arg.arg for arg in node.args.args] - signature = f"{node.name}({', '.join(args)})" - - symbol = Symbol( - name=node.name, - type="function", - file_path=self.file_path, - line_start=node.lineno, - line_end=node.end_lineno or node.lineno, - column_start=node.col_offset, - column_end=node.end_col_offset or 0, - scope=scope, - parent=parent if parent != "global" else None, - docstring=docstring, - signature=signature, - ) - - self.symbols.append(symbol) - - # Enter function scope - self.scope_stack.append(node.name) - self.generic_visit(node) - self.scope_stack.pop() - - def visit_AsyncFunctionDef(self, node): - """Visit async function definitions.""" - self.visit_FunctionDef(node) # Same logic - - def visit_ClassDef(self, node): - """Visit class definitions.""" - scope = ".".join(self.scope_stack) - parent = self.scope_stack[-1] if len(self.scope_stack) > 1 else None - - # Extract docstring - docstring = None - if ( - node.body - and isinstance(node.body[0], ast.Expr) - and isinstance(node.body[0].value, ast.Constant) - and isinstance(node.body[0].value.value, str) - ): - docstring = node.body[0].value.value - - # Extract base classes - bases = [self._get_name(base) for base in node.bases] - signature = ( - f"class {node.name}({', '.join(bases)})" if bases else f"class {node.name}" - ) - - symbol = Symbol( - name=node.name, - type="class", - file_path=self.file_path, - line_start=node.lineno, - line_end=node.end_lineno or node.lineno, - column_start=node.col_offset, - column_end=node.end_col_offset or 0, - scope=scope, - parent=parent if parent != "global" else None, - docstring=docstring, - signature=signature, - ) - - self.symbols.append(symbol) - - # Enter class scope - self.scope_stack.append(node.name) - self.generic_visit(node) - self.scope_stack.pop() - - def visit_Import(self, node): - """Visit import statements.""" - for alias in node.names: - import_name = alias.name - self.imports.append(import_name) - if "." not in import_name: # Top-level module - self.dependencies.append(import_name) - - def visit_ImportFrom(self, node): - """Visit from...import statements.""" - if node.module: - self.imports.append(node.module) - if "." not in node.module: # Top-level module - self.dependencies.append(node.module) - - for alias in node.names: - if alias.name != "*": - import_item = ( - f"{node.module}.{alias.name}" if node.module else alias.name - ) - self.imports.append(import_item) - - def visit_Assign(self, node): - """Visit variable assignments.""" - scope = ".".join(self.scope_stack) - parent = self.scope_stack[-1] if len(self.scope_stack) > 1 else None - - for target in node.targets: - if isinstance(target, ast.Name): - symbol = Symbol( - name=target.id, - type="variable", - file_path=self.file_path, - line_start=node.lineno, - line_end=node.end_lineno or node.lineno, - column_start=node.col_offset, - column_end=node.end_col_offset or 0, - scope=scope, - parent=parent if parent != "global" else None, - ) - self.symbols.append(symbol) - - self.generic_visit(node) - - def _get_name(self, node): - """Extract name from AST node.""" - if isinstance(node, ast.Name): - return node.id - elif isinstance(node, ast.Attribute): - return f"{self._get_name(node.value)}.{node.attr}" - elif isinstance(node, ast.Constant): - return str(node.value) - else: - return str(node) - - -def create_symbol_embedding_text(symbol: Symbol) -> str: - """Create text representation of symbol for vector embedding.""" - parts = [ - f"Symbol: {symbol.name}", - f"Type: {symbol.type}", - f"Scope: {symbol.scope}", - ] - - if symbol.parent: - parts.append(f"Parent: {symbol.parent}") - - if symbol.signature: - parts.append(f"Signature: {symbol.signature}") - - if symbol.docstring: - parts.append(f"Documentation: {symbol.docstring}") - - return " | ".join(parts) diff --git a/pkg/hanzo-tools-vector/hanzo_tools/vector/git_ingester.py b/pkg/hanzo-tools-vector/hanzo_tools/vector/git_ingester.py deleted file mode 100644 index 97b6c7dbd..000000000 --- a/pkg/hanzo-tools-vector/hanzo_tools/vector/git_ingester.py +++ /dev/null @@ -1,476 +0,0 @@ -"""Git repository ingester for comprehensive code indexing. - -This module provides functionality to ingest entire git repositories including: -- Full git history and commit metadata -- File contents at different points in time -- AST analysis via tree-sitter -- Symbol extraction and cross-references -- Blame information for line-level attribution -""" - -import logging -import subprocess -from typing import Any, Dict, List, Optional -from pathlib import Path -from datetime import datetime -from dataclasses import dataclass - -from .ast_analyzer import ASTAnalyzer -from .infinity_store import InfinityVectorStore - -logger = logging.getLogger(__name__) - - -@dataclass -class GitCommit: - """Represents a git commit.""" - - hash: str - author: str - author_email: str - timestamp: int - message: str - files: List[Dict[str, str]] # [{'status': 'M', 'filename': 'main.py'}] - parent_hashes: List[str] - - -@dataclass -class GitFileHistory: - """History of a single file.""" - - file_path: str - commits: List[GitCommit] - current_content: Optional[str] - line_blame: Dict[int, Dict[str, Any]] # line_number -> blame info - - -class GitIngester: - """Ingests git repositories into vector store.""" - - def __init__(self, vector_store: InfinityVectorStore): - """Initialize the git ingester. - - Args: - vector_store: The vector store to ingest into - """ - self.vector_store = vector_store - self.ast_analyzer = ASTAnalyzer() - self._commit_cache: Dict[str, GitCommit] = {} - - def ingest_repository( - self, - repo_path: str, - branch: str = "HEAD", - include_history: bool = True, - include_diffs: bool = True, - include_blame: bool = True, - file_patterns: Optional[List[str]] = None, - ) -> Dict[str, Any]: - """Ingest an entire git repository. - - Args: - repo_path: Path to the git repository - branch: Branch to ingest (default: HEAD) - include_history: Whether to include commit history - include_diffs: Whether to include diff information - include_blame: Whether to include blame information - file_patterns: List of file patterns to include (e.g., ["*.py", "*.js"]) - - Returns: - Summary of ingestion results - """ - repo_path = Path(repo_path) - if not (repo_path / ".git").exists(): - raise ValueError(f"Not a git repository: {repo_path}") - - logger.info(f"Starting ingestion of repository: {repo_path}") - - results = { - "repository": str(repo_path), - "branch": branch, - "commits_processed": 0, - "commits_indexed": 0, - "files_indexed": 0, - "symbols_extracted": 0, - "diffs_indexed": 0, - "blame_entries": 0, - "errors": [], - } - - try: - # Get current branch/commit - current_commit = self._get_current_commit(repo_path) - results["current_commit"] = current_commit - - # Get list of files to process - files = self._get_repository_files(repo_path, file_patterns) - logger.info(f"Found {len(files)} files to process") - - # Process each file - for file_path in files: - try: - self._process_file( - repo_path, - file_path, - include_history=include_history, - include_blame=include_blame, - results=results, - ) - except Exception as e: - logger.error(f"Error processing {file_path}: {e}") - results["errors"].append(f"{file_path}: {str(e)}") - - # Process commit history if requested - if include_history: - commits = self._get_commit_history(repo_path, branch) - results["commits_processed"] = len(commits) - - for commit in commits: - self._index_commit(commit, include_diffs=include_diffs) - results["commits_indexed"] = results.get("commits_indexed", 0) + 1 - - if include_diffs: - results["diffs_indexed"] += len(commit.files) - - # Create repository metadata document - self._index_repository_metadata(repo_path, results) - - except Exception as e: - logger.error(f"Repository ingestion failed: {e}") - results["errors"].append(f"Fatal error: {str(e)}") - - logger.info(f"Ingestion complete: {results}") - return results - - def _get_current_commit(self, repo_path: Path) -> str: - """Get the current commit hash.""" - result = subprocess.run( - ["git", "rev-parse", "HEAD"], - cwd=repo_path, - capture_output=True, - text=True, - check=True, - ) - return result.stdout.strip() - - def _get_repository_files( - self, repo_path: Path, patterns: Optional[List[str]] = None - ) -> List[Path]: - """Get list of files in repository matching patterns.""" - # Use git ls-files to respect .gitignore - cmd = ["git", "ls-files"] - - result = subprocess.run( - cmd, cwd=repo_path, capture_output=True, text=True, check=True - ) - - files = [] - for line in result.stdout.strip().split("\n"): - if line: - file_path = repo_path / line - if file_path.exists(): - # Apply pattern filtering if specified - if patterns: - if any(file_path.match(pattern) for pattern in patterns): - files.append(file_path) - else: - files.append(file_path) - - return files - - def _get_commit_history( - self, repo_path: Path, branch: str = "HEAD", max_commits: int = 1000 - ) -> List[GitCommit]: - """Get commit history for the repository.""" - # Get commit list with basic info - result = subprocess.run( - [ - "git", - "log", - branch, - f"--max-count={max_commits}", - "--pretty=format:%H|%P|%an|%ae|%at|%s", - ], - cwd=repo_path, - capture_output=True, - text=True, - check=True, - ) - - commits = [] - for line in result.stdout.strip().split("\n"): - if line: - parts = line.split("|", 5) - if len(parts) >= 6: - commit_hash = parts[0] - parent_hashes = parts[1].split() if parts[1] else [] - - # Get file changes for this commit - files = self._get_commit_files(repo_path, commit_hash) - - commit = GitCommit( - hash=commit_hash, - parent_hashes=parent_hashes, - author=parts[2], - author_email=parts[3], - timestamp=int(parts[4]), - message=parts[5], - files=files, - ) - commits.append(commit) - self._commit_cache[commit_hash] = commit - - return commits - - def _get_commit_files( - self, repo_path: Path, commit_hash: str - ) -> List[Dict[str, str]]: - """Get list of files changed in a commit.""" - result = subprocess.run( - ["git", "show", "--name-status", "--format=", commit_hash], - cwd=repo_path, - capture_output=True, - text=True, - check=True, - ) - - files = [] - for line in result.stdout.strip().split("\n"): - if line and "\t" in line: - parts = line.split("\t", 1) - if len(parts) == 2: - files.append({"status": parts[0], "filename": parts[1]}) - - return files - - def _process_file( - self, - repo_path: Path, - file_path: Path, - include_history: bool, - include_blame: bool, - results: Dict[str, Any], - ): - """Process a single file.""" - relative_path = file_path.relative_to(repo_path) - - # Read current content - try: - content = file_path.read_text(encoding="utf-8") - except UnicodeDecodeError: - content = file_path.read_text(encoding="latin-1") - - # Get file metadata - metadata = { - "repository": str(repo_path), - "relative_path": str(relative_path), - "file_type": file_path.suffix, - "size": file_path.stat().st_size, - } - - # Add git history metadata if requested - if include_history: - history = self._get_file_history(repo_path, relative_path) - metadata["commit_count"] = len(history) - if history: - metadata["first_commit"] = history[-1]["hash"] - metadata["last_commit"] = history[0]["hash"] - metadata["last_modified"] = datetime.fromtimestamp( - history[0]["timestamp"] - ).isoformat() - - # Add blame information if requested - if include_blame: - blame_data = self._get_file_blame(repo_path, relative_path) - metadata["unique_authors"] = len( - set(b["author"] for b in blame_data.values()) - ) - - # Index the file content - doc_ids = self.vector_store.add_file( - str(file_path), chunk_size=1000, chunk_overlap=200, metadata=metadata - ) - results["files_indexed"] += 1 - - # Perform AST analysis for supported languages - if file_path.suffix in [".py", ".js", ".ts", ".java", ".cpp", ".c"]: - try: - file_ast = self.ast_analyzer.analyze_file(str(file_path)) - if file_ast: - # Store complete AST - self.vector_store._store_file_ast(file_ast) - - # Store individual symbols - self.vector_store._store_symbols(file_ast.symbols) - results["symbols_extracted"] += len(file_ast.symbols) - - # Store cross-references - self.vector_store._store_references(file_ast) - except Exception as e: - logger.warning(f"AST analysis failed for {file_path}: {e}") - - def _get_file_history( - self, repo_path: Path, file_path: Path - ) -> List[Dict[str, Any]]: - """Get commit history for a specific file.""" - result = subprocess.run( - [ - "git", - "log", - "--follow", - "--pretty=format:%H|%at|%an|%s", - "--", - str(file_path), - ], - cwd=repo_path, - capture_output=True, - text=True, - ) - - if result.returncode != 0: - return [] - - history = [] - for line in result.stdout.strip().split("\n"): - if line: - parts = line.split("|", 3) - if len(parts) >= 4: - history.append( - { - "hash": parts[0], - "timestamp": int(parts[1]), - "author": parts[2], - "message": parts[3], - } - ) - - return history - - def _get_file_blame( - self, repo_path: Path, file_path: Path - ) -> Dict[int, Dict[str, Any]]: - """Get blame information for a file.""" - result = subprocess.run( - ["git", "blame", "--line-porcelain", "--", str(file_path)], - cwd=repo_path, - capture_output=True, - text=True, - ) - - if result.returncode != 0: - return {} - - blame_data = {} - current_commit = None - current_line = None - author = None - timestamp = None - - for line in result.stdout.strip().split("\n"): - if line and not line.startswith("\t"): - parts = line.split(" ") - if len(parts) >= 3 and len(parts[0]) == 40: # SHA-1 hash - current_commit = parts[0] - current_line = int(parts[2]) - elif line.startswith("author "): - author = line[7:] - elif line.startswith("author-time "): - timestamp = int(line[12:]) - - # We have all the data for this line - if current_line and author: - blame_data[current_line] = { - "commit": current_commit, - "author": author, - "timestamp": timestamp, - } - - return blame_data - - def _index_commit(self, commit: GitCommit, include_diffs: bool = True): - """Index a single commit.""" - # Create commit document - commit_doc = f"""Git Commit: {commit.hash} -Author: {commit.author} <{commit.author_email}> -Date: {datetime.fromtimestamp(commit.timestamp).isoformat()} -Message: {commit.message} - -Files changed: {len(commit.files)} -""" - - for file_info in commit.files: - commit_doc += f"\n{file_info['status']}\t{file_info['filename']}" - - # Index commit - metadata = { - "type": "git_commit", - "commit_hash": commit.hash, - "author": commit.author, - "timestamp": commit.timestamp, - "file_count": len(commit.files), - } - - self.vector_store.add_document(commit_doc, metadata) - - # Index diffs if requested - if include_diffs: - for file_info in commit.files: - self._index_commit_diff(commit, file_info["filename"]) - - def _index_commit_diff(self, commit: GitCommit, filename: str): - """Index the diff for a specific file in a commit.""" - # This is a simplified version - in practice you'd want to - # parse the actual diff and store meaningful chunks - metadata = { - "type": "git_diff", - "commit_hash": commit.hash, - "filename": filename, - "author": commit.author, - "timestamp": commit.timestamp, - } - - # Create a document representing this change - diff_doc = f"""File: {filename} -Commit: {commit.hash} -Author: {commit.author} -Message: {commit.message} -""" - - self.vector_store.add_document(diff_doc, metadata) - - def _index_repository_metadata(self, repo_path: Path, results: Dict[str, Any]): - """Index overall repository metadata.""" - # Get repository info - remote_result = subprocess.run( - ["git", "remote", "get-url", "origin"], - cwd=repo_path, - capture_output=True, - text=True, - ) - - remote_url = ( - remote_result.stdout.strip() if remote_result.returncode == 0 else None - ) - - # Create repository summary document - repo_doc = f"""Repository: {repo_path.name} -Path: {repo_path} -Remote: {remote_url or "No remote"} -Current Commit: {results.get("current_commit", "Unknown")} - -Statistics: -- Files indexed: {results["files_indexed"]} -- Commits processed: {results["commits_processed"]} -- Symbols extracted: {results["symbols_extracted"]} -- Diffs indexed: {results["diffs_indexed"]} -""" - - metadata = { - "type": "repository", - "name": repo_path.name, - "path": str(repo_path), - "remote_url": remote_url, - **results, - } - - self.vector_store.add_document(repo_doc, metadata) diff --git a/pkg/hanzo-tools-vector/hanzo_tools/vector/index_tool.py b/pkg/hanzo-tools-vector/hanzo_tools/vector/index_tool.py deleted file mode 100644 index e12af5421..000000000 --- a/pkg/hanzo-tools-vector/hanzo_tools/vector/index_tool.py +++ /dev/null @@ -1,416 +0,0 @@ -"""Index tool for managing vector store indexing.""" - -import os -import time -from typing import Unpack, Annotated, TypedDict, final, override -from pathlib import Path - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import ( - BaseTool, - ToolContext, - PermissionManager, - auto_timeout, - create_tool_context, -) - -from .git_ingester import GitIngester -from .project_manager import ProjectVectorManager - -Path_str = Annotated[ - str, - Field( - description="Path to index (defaults to current working directory)", - min_length=1, - ), -] - -IncludeGitHistory = Annotated[ - bool, - Field( - description="Include git history in the index", - default=True, - ), -] - -FilePatterns = Annotated[ - list[str] | None, - Field( - description="File patterns to include (e.g., ['*.py', '*.js'])", - default=None, - ), -] - -ShowStats = Annotated[ - bool, - Field( - description="Show detailed statistics after indexing", - default=True, - ), -] - -Force = Annotated[ - bool, - Field( - description="Force re-indexing even if already indexed", - default=False, - ), -] - - -class IndexToolParams(TypedDict, total=False): - """Parameters for the index tool.""" - - path: str - include_git_history: bool - file_patterns: list[str] | None - show_stats: bool - force: bool - - -@final -class IndexTool(BaseTool): - """Tool for indexing files and git history into vector store.""" - - def __init__(self, permission_manager: PermissionManager): - """Initialize the index tool. - - Args: - permission_manager: Permission manager for access control - """ - self.permission_manager = permission_manager - self.project_manager = ProjectVectorManager(permission_manager) - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "index" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Index files and git history into the vector store for semantic search. - -This tool: -- Indexes all project files into a vector database -- Includes git history (commits, diffs, blame) when available -- Supports incremental updates -- Shows statistics about indexed content -- Automatically creates project-specific databases - -Usage: -- index: Index the current directory -- index --path /path/to/project: Index a specific path -- index --file-patterns "*.py" "*.js": Index only specific file types -- index --no-git-history: Skip git history indexing -- index --force: Force re-indexing of all files""" - - @override - @auto_timeout("index") - async def call( - self, - ctx: MCPContext, - **params: Unpack[IndexToolParams], - ) -> str: - """Execute the index tool. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Indexing result and statistics - """ - start_time = time.time() - tool_ctx = create_tool_context(ctx) - await tool_ctx.set_tool_info(self.name) - - # Extract parameters - path = params.get("path", os.getcwd()) - include_git_history = params.get("include_git_history", True) - file_patterns = params.get("file_patterns") - show_stats = params.get("show_stats", True) - force = params.get("force", False) - - # Resolve absolute path - abs_path = os.path.abspath(path) - - # Check permissions - if not self.permission_manager.is_path_allowed(abs_path): - return f"Permission denied: {abs_path}" - - # Check if path exists - if not os.path.exists(abs_path): - return f"Path does not exist: {abs_path}" - - await tool_ctx.info(f"Starting indexing of {abs_path}") - - try: - # Get or create vector store for this project - vector_store = self.project_manager.get_project_store(abs_path) - - # Check if already indexed (unless force) - if not force: - stats = await vector_store.get_stats() - if stats and stats.get("document_count", 0) > 0: - await tool_ctx.info( - "Project already indexed, use --force to re-index" - ) - if show_stats: - return self._format_stats( - stats, abs_path, time.time() - start_time - ) - return "Project is already indexed. Use --force to re-index." - - # Prepare file patterns - if file_patterns is None: - # Default patterns for code files - file_patterns = [ - "*.py", - "*.js", - "*.ts", - "*.jsx", - "*.tsx", - "*.java", - "*.cpp", - "*.c", - "*.h", - "*.hpp", - "*.go", - "*.rs", - "*.rb", - "*.php", - "*.swift", - "*.kt", - "*.scala", - "*.cs", - "*.vb", - "*.fs", - "*.sh", - "*.bash", - "*.zsh", - "*.fish", - "*.md", - "*.rst", - "*.txt", - "*.json", - "*.yaml", - "*.yml", - "*.toml", - "*.ini", - "*.cfg", - "*.conf", - "*.html", - "*.css", - "*.scss", - "*.sass", - "*.less", - "*.sql", - "*.graphql", - "*.proto", - "Dockerfile", - "Makefile", - "*.mk", - ".gitignore", - ".dockerignore", - "requirements.txt", - "package.json", - "Cargo.toml", - "go.mod", - "pom.xml", - ] - - # Clear existing index if force - if force: - await tool_ctx.info("Clearing existing index...") - await vector_store.clear() - - # Index files - await tool_ctx.info("Indexing files...") - indexed_files = 0 - total_size = 0 - errors = [] - - for pattern in file_patterns: - pattern_files = await self._find_files(abs_path, pattern) - for file_path in pattern_files: - try: - # Check file size (skip very large files) - file_size = os.path.getsize(file_path) - if file_size > 10 * 1024 * 1024: # 10MB - await tool_ctx.warning(f"Skipping large file: {file_path}") - continue - - # Read file content - try: - with open(file_path, "r", encoding="utf-8") as f: - content = f.read() - except UnicodeDecodeError: - # Skip binary files - continue - - # Index the file - rel_path = os.path.relpath(file_path, abs_path) - await vector_store.index_document( - content=content, - metadata={ - "type": "file", - "path": rel_path, - "absolute_path": file_path, - "size": file_size, - "extension": Path(file_path).suffix, - }, - ) - indexed_files += 1 - total_size += file_size - - if indexed_files % 100 == 0: - await tool_ctx.info(f"Indexed {indexed_files} files...") - - except Exception as e: - errors.append(f"{file_path}: {str(e)}") - - await tool_ctx.info( - f"Indexed {indexed_files} files ({total_size / 1024 / 1024:.1f} MB)" - ) - - # Index git history if requested - git_stats = {} - if include_git_history and os.path.exists(os.path.join(abs_path, ".git")): - await tool_ctx.info("Indexing git history...") - - git_ingester = GitIngester(vector_store) - git_stats = await git_ingester.ingest_repository( - repo_path=abs_path, - include_history=True, - include_diffs=True, - include_blame=True, - file_patterns=file_patterns, - ) - - await tool_ctx.info( - f"Indexed {git_stats.get('commits_indexed', 0)} commits, {git_stats.get('diffs_indexed', 0)} diffs" - ) - - # Get final statistics - if show_stats: - stats = await vector_store.get_stats() - stats.update( - { - "files_indexed": indexed_files, - "total_size_mb": total_size / 1024 / 1024, - "errors": len(errors), - **git_stats, - } - ) - result = self._format_stats(stats, abs_path, time.time() - start_time) - - if errors: - result += f"\n\nErrors ({len(errors)}):\n" - result += "\n".join(errors[:10]) # Show first 10 errors - if len(errors) > 10: - result += f"\n... and {len(errors) - 10} more errors" - - return result - else: - return f"Successfully indexed {indexed_files} files" - - except Exception as e: - await tool_ctx.error(f"Indexing failed: {str(e)}") - return f"Error during indexing: {str(e)}" - - async def _find_files(self, base_path: str, pattern: str) -> list[str]: - """Find files matching a pattern. - - Args: - base_path: Base directory to search - pattern: File pattern to match - - Returns: - List of matching file paths - """ - import glob - - # Use glob to find files - if pattern.startswith("*."): - # Extension pattern - files = glob.glob( - os.path.join(base_path, "**", pattern), - recursive=True, - ) - else: - # Exact filename - files = glob.glob( - os.path.join(base_path, "**", pattern), - recursive=True, - ) - - # Filter out hidden directories and common ignore patterns - filtered_files = [] - ignore_dirs = { - ".git", - "__pycache__", - "node_modules", - ".venv", - "venv", - "dist", - "build", - } - - for file_path in files: - # Check if any parent directory is in ignore list - parts = Path(file_path).parts - if any(part in ignore_dirs for part in parts): - continue - if any(part.startswith(".") and part != "." for part in parts[:-1]): - continue # Skip hidden directories (but allow hidden files like .gitignore) - filtered_files.append(file_path) - - return filtered_files - - def _format_stats(self, stats: dict, path: str, elapsed_time: float) -> str: - """Format statistics for display. - - Args: - stats: Statistics dictionary - path: Indexed path - elapsed_time: Time taken for indexing - - Returns: - Formatted statistics string - """ - result = f"=== Index Statistics for {path} ===\n\n" - - # Basic stats - result += f"Indexing completed in {elapsed_time:.1f} seconds\n\n" - - result += "Content Statistics:\n" - result += f" Documents: {stats.get('document_count', 0):,}\n" - result += f" Files indexed: {stats.get('files_indexed', 0):,}\n" - result += f" Total size: {stats.get('total_size_mb', 0):.1f} MB\n" - - if stats.get("commits_indexed", 0) > 0: - result += f"\nGit History:\n" - result += f" Commits: {stats.get('commits_indexed', 0):,}\n" - result += f" Diffs: {stats.get('diffs_indexed', 0):,}\n" - result += f" Blame entries: {stats.get('blame_entries', 0):,}\n" - - # Vector store info - result += f"\nVector Store:\n" - result += f" Database: {stats.get('database_name', 'default')}\n" - result += f" Table: {stats.get('table_name', 'documents')}\n" - result += f" Vectors: {stats.get('vector_count', stats.get('document_count', 0)):,}\n" - - if stats.get("errors", 0) > 0: - result += f"\nErrors: {stats.get('errors', 0)}\n" - - return result - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - # Tool registration is handled by the ToolRegistry - pass diff --git a/pkg/hanzo-tools-vector/hanzo_tools/vector/infinity_store.py b/pkg/hanzo-tools-vector/hanzo_tools/vector/infinity_store.py deleted file mode 100644 index 3ed502f47..000000000 --- a/pkg/hanzo-tools-vector/hanzo_tools/vector/infinity_store.py +++ /dev/null @@ -1,890 +0,0 @@ -"""Infinity vector database integration for Hanzo AI.""" - -import json -import hashlib -from typing import Any, Dict, List, Tuple, Optional -from pathlib import Path -from dataclasses import dataclass - -try: - import infinity_embedded - - INFINITY_AVAILABLE = True -except ImportError: - # Use mock implementation when infinity_embedded is not available - from . import mock_infinity as infinity_embedded - - INFINITY_AVAILABLE = True # Mock is always available - -from .ast_analyzer import Symbol, FileAST, ASTAnalyzer, create_symbol_embedding_text - - -@dataclass -class Document: - """Document representation for vector storage.""" - - id: str - content: str - metadata: Dict[str, Any] - file_path: Optional[str] = None - chunk_index: Optional[int] = None - - -@dataclass -class SearchResult: - """Search result from vector database.""" - - document: Document - score: float - distance: float - - -@dataclass -class SymbolSearchResult: - """Search result for symbols.""" - - symbol: Symbol - score: float - context_document: Optional[Document] = None - - -@dataclass -class UnifiedSearchResult: - """Search result combining text, vector, and symbol search.""" - - type: str # 'document', 'symbol', 'reference' - content: str - file_path: str - line_start: int - line_end: int - score: float - search_type: str # 'text', 'vector', 'symbol', 'ast' - metadata: Dict[str, Any] - - -class InfinityVectorStore: - """Local vector database using Infinity.""" - - def __init__( - self, - data_path: Optional[str] = None, - embedding_model: str = "text-embedding-3-small", - dimension: int = 1536, # Default for OpenAI text-embedding-3-small - ): - """Initialize the Infinity vector store. - - Args: - data_path: Path to store vector database (default: ~/.config/hanzo/vector-store) - embedding_model: Embedding model to use - dimension: Vector dimension (must match embedding model) - """ - if not INFINITY_AVAILABLE: - raise ImportError( - "infinity_embedded is required for vector store functionality" - ) - - # Set up data path - if data_path: - self.data_path = Path(data_path) - else: - from hanzo_mcp.config.settings import get_config_dir - - self.data_path = get_config_dir() / "vector-store" - - self.data_path.mkdir(parents=True, exist_ok=True) - - self.embedding_model = embedding_model - self.dimension = dimension - - # Initialize AST analyzer - self.ast_analyzer = ASTAnalyzer() - - # Connect to Infinity - self.infinity = infinity_embedded.connect(str(self.data_path)) - self.db = self.infinity.get_database("hanzo_mcp") - - # Initialize tables - self._initialize_tables() - - def _initialize_tables(self): - """Initialize database tables if they don't exist.""" - # Documents table - try: - self.documents_table = self.db.get_table("documents") - except Exception: - self.documents_table = self.db.create_table( - "documents", - { - "id": {"type": "varchar"}, - "content": {"type": "varchar"}, - "file_path": {"type": "varchar"}, - "chunk_index": {"type": "integer"}, - "metadata": {"type": "varchar"}, # JSON string - "embedding": {"type": f"vector,{self.dimension},float"}, - }, - ) - - # Symbols table for code symbols - try: - self.symbols_table = self.db.get_table("symbols") - except Exception: - self.symbols_table = self.db.create_table( - "symbols", - { - "id": {"type": "varchar"}, - "name": {"type": "varchar"}, - "type": {"type": "varchar"}, # function, class, variable, etc. - "file_path": {"type": "varchar"}, - "line_start": {"type": "integer"}, - "line_end": {"type": "integer"}, - "scope": {"type": "varchar"}, - "parent": {"type": "varchar"}, - "signature": {"type": "varchar"}, - "docstring": {"type": "varchar"}, - "metadata": {"type": "varchar"}, # JSON string - "embedding": {"type": f"vector,{self.dimension},float"}, - }, - ) - - # AST table for storing complete file ASTs - try: - self.ast_table = self.db.get_table("ast_files") - except Exception: - self.ast_table = self.db.create_table( - "ast_files", - { - "file_path": {"type": "varchar"}, - "file_hash": {"type": "varchar"}, - "language": {"type": "varchar"}, - "ast_data": {"type": "varchar"}, # JSON string of complete AST - "last_updated": {"type": "varchar"}, # ISO timestamp - }, - ) - - # References table for cross-file references - try: - self.references_table = self.db.get_table("references") - except Exception: - self.references_table = self.db.create_table( - "references", - { - "id": {"type": "varchar"}, - "source_file": {"type": "varchar"}, - "target_file": {"type": "varchar"}, - "symbol_name": {"type": "varchar"}, - "reference_type": { - "type": "varchar" - }, # import, call, inheritance, etc. - "line_number": {"type": "integer"}, - "metadata": {"type": "varchar"}, # JSON string - }, - ) - - def _generate_doc_id( - self, content: str, file_path: str = "", chunk_index: int = 0 - ) -> str: - """Generate a unique document ID.""" - content_hash = hashlib.sha256(content.encode()).hexdigest()[:16] - path_hash = hashlib.sha256(file_path.encode()).hexdigest()[:8] - return f"doc_{path_hash}_{chunk_index}_{content_hash}" - - def add_document( - self, - content: str, - metadata: Dict[str, Any] = None, - file_path: Optional[str] = None, - chunk_index: int = 0, - embedding: Optional[List[float]] = None, - ) -> str: - """Add a document to the vector store. - - Args: - content: Document content - metadata: Additional metadata - file_path: Source file path - chunk_index: Chunk index if document is part of larger file - embedding: Pre-computed embedding (if None, will compute) - - Returns: - Document ID - """ - doc_id = self._generate_doc_id(content, file_path or "", chunk_index) - - # Generate embedding if not provided - if embedding is None: - embedding = self._generate_embedding(content) - - # Prepare metadata - metadata = metadata or {} - metadata_json = json.dumps(metadata) - - # Insert document - self.documents_table.insert( - [ - { - "id": doc_id, - "content": content, - "file_path": file_path or "", - "chunk_index": chunk_index, - "metadata": metadata_json, - "embedding": embedding, - } - ] - ) - - return doc_id - - def add_file( - self, - file_path: str, - chunk_size: int = 1000, - chunk_overlap: int = 200, - metadata: Dict[str, Any] = None, - ) -> List[str]: - """Add a file to the vector store by chunking it. - - Args: - file_path: Path to the file to add - chunk_size: Maximum characters per chunk - chunk_overlap: Characters to overlap between chunks - metadata: Additional metadata for all chunks - - Returns: - List of document IDs for all chunks - """ - path = Path(file_path) - if not path.exists(): - raise FileNotFoundError(f"File not found: {file_path}") - - # Read file content - try: - content = path.read_text(encoding="utf-8") - except UnicodeDecodeError: - # Try with different encoding - content = path.read_text(encoding="latin-1") - - # Chunk the content - chunks = self._chunk_text(content, chunk_size, chunk_overlap) - - # Add metadata - file_metadata = metadata or {} - file_metadata.update( - { - "file_name": path.name, - "file_extension": path.suffix, - "file_size": path.stat().st_size, - } - ) - - # Add each chunk - doc_ids = [] - for i, chunk in enumerate(chunks): - chunk_metadata = file_metadata.copy() - chunk_metadata["chunk_number"] = i - chunk_metadata["total_chunks"] = len(chunks) - - doc_id = self.add_document( - content=chunk, - metadata=chunk_metadata, - file_path=str(path), - chunk_index=i, - ) - doc_ids.append(doc_id) - - return doc_ids - - def add_file_with_ast( - self, - file_path: str, - chunk_size: int = 1000, - chunk_overlap: int = 200, - metadata: Dict[str, Any] = None, - ) -> Tuple[List[str], Optional[FileAST]]: - """Add a file with full AST analysis and symbol extraction. - - Args: - file_path: Path to the file to add - chunk_size: Maximum characters per chunk for content - chunk_overlap: Characters to overlap between chunks - metadata: Additional metadata for all chunks - - Returns: - Tuple of (document IDs for content chunks, FileAST object) - """ - path = Path(file_path) - if not path.exists(): - raise FileNotFoundError(f"File not found: {file_path}") - - # First add file content using existing method - doc_ids = self.add_file(file_path, chunk_size, chunk_overlap, metadata) - - # Analyze AST and symbols - file_ast = self.ast_analyzer.analyze_file(file_path) - if not file_ast: - return doc_ids, None - - # Store complete AST - self._store_file_ast(file_ast) - - # Store individual symbols with embeddings - self._store_symbols(file_ast.symbols) - - # Store cross-references - self._store_references(file_ast) - - return doc_ids, file_ast - - def _store_file_ast(self, file_ast: FileAST): - """Store complete file AST information.""" - from datetime import datetime - - # Remove existing AST for this file - try: - self.ast_table.delete(f"file_path = '{file_ast.file_path}'") - except Exception: - pass - - # Insert new AST - self.ast_table.insert( - [ - { - "file_path": file_ast.file_path, - "file_hash": file_ast.file_hash, - "language": file_ast.language, - "ast_data": json.dumps(file_ast.to_dict()), - "last_updated": datetime.now().isoformat(), - } - ] - ) - - def _store_symbols(self, symbols: List[Symbol]): - """Store symbols with vector embeddings.""" - if not symbols: - return - - # Remove existing symbols for these files - file_paths = list(set(symbol.file_path for symbol in symbols)) - for file_path in file_paths: - try: - self.symbols_table.delete(f"file_path = '{file_path}'") - except Exception: - pass - - # Insert new symbols - symbol_records = [] - for symbol in symbols: - # Create embedding text for symbol - embedding_text = create_symbol_embedding_text(symbol) - embedding = self._generate_embedding(embedding_text) - - # Generate symbol ID - symbol_id = self._generate_symbol_id(symbol) - - # Prepare metadata - symbol_metadata = { - "references": symbol.references, - "embedding_text": embedding_text, - } - - symbol_records.append( - { - "id": symbol_id, - "name": symbol.name, - "type": symbol.type, - "file_path": symbol.file_path, - "line_start": symbol.line_start, - "line_end": symbol.line_end, - "scope": symbol.scope or "", - "parent": symbol.parent or "", - "signature": symbol.signature or "", - "docstring": symbol.docstring or "", - "metadata": json.dumps(symbol_metadata), - "embedding": embedding, - } - ) - - if symbol_records: - self.symbols_table.insert(symbol_records) - - def _store_references(self, file_ast: FileAST): - """Store cross-file references.""" - if not file_ast.dependencies: - return - - # Remove existing references for this file - try: - self.references_table.delete(f"source_file = '{file_ast.file_path}'") - except Exception: - pass - - # Insert new references - reference_records = [] - for i, dependency in enumerate(file_ast.dependencies): - ref_id = f"{file_ast.file_path}_{dependency}_{i}" - reference_records.append( - { - "id": ref_id, - "source_file": file_ast.file_path, - "target_file": dependency, - "symbol_name": dependency, - "reference_type": "import", - "line_number": 0, # Could be enhanced to track actual line numbers - "metadata": json.dumps({}), - } - ) - - if reference_records: - self.references_table.insert(reference_records) - - def _generate_symbol_id(self, symbol: Symbol) -> str: - """Generate unique symbol ID.""" - text = f"{symbol.file_path}_{symbol.type}_{symbol.name}_{symbol.line_start}" - return hashlib.sha256(text.encode()).hexdigest()[:16] - - def search_symbols( - self, - query: str, - symbol_type: Optional[str] = None, - file_path: Optional[str] = None, - limit: int = 10, - score_threshold: float = 0.0, - ) -> List[SymbolSearchResult]: - """Search for symbols using vector similarity. - - Args: - query: Search query - symbol_type: Filter by symbol type (function, class, variable, etc.) - file_path: Filter by file path - limit: Maximum number of results - score_threshold: Minimum similarity score - - Returns: - List of symbol search results - """ - # Generate query embedding - query_embedding = self._generate_embedding(query) - - # Build search query - search_query = self.symbols_table.output(["*"]).match_dense( - "embedding", - query_embedding, - "float", - "ip", # Inner product - limit * 2, # Get more results for filtering - ) - - # Apply filters - if symbol_type: - search_query = search_query.filter(f"type = '{symbol_type}'") - if file_path: - search_query = search_query.filter(f"file_path = '{file_path}'") - - search_results = search_query.to_pl() - - # Convert to SymbolSearchResult objects - results = [] - for row in search_results.iter_rows(named=True): - score = row.get("score", 0.0) - if score >= score_threshold: - # Parse metadata - try: - metadata = json.loads(row["metadata"]) - except Exception: - metadata = {} - - # Create Symbol object - symbol = Symbol( - name=row["name"], - type=row["type"], - file_path=row["file_path"], - line_start=row["line_start"], - line_end=row["line_end"], - column_start=0, # Not stored in table - column_end=0, # Not stored in table - scope=row["scope"], - parent=row["parent"] if row["parent"] else None, - docstring=row["docstring"] if row["docstring"] else None, - signature=row["signature"] if row["signature"] else None, - references=metadata.get("references", []), - ) - - results.append( - SymbolSearchResult( - symbol=symbol, - score=score, - ) - ) - - return results[:limit] - - def search_ast_nodes( - self, - file_path: str, - node_type: Optional[str] = None, - node_name: Optional[str] = None, - ) -> Optional[FileAST]: - """Search AST nodes within a specific file. - - Args: - file_path: File to search in - node_type: Filter by AST node type - node_name: Filter by node name - - Returns: - FileAST object if file found, None otherwise - """ - try: - results = ( - self.ast_table.output(["*"]) - .filter(f"file_path = '{file_path}'") - .to_pl() - ) - - if len(results) == 0: - return None - - row = next(results.iter_rows(named=True)) - ast_data = json.loads(row["ast_data"]) - - # Reconstruct FileAST object - file_ast = FileAST( - file_path=ast_data["file_path"], - file_hash=ast_data["file_hash"], - language=ast_data["language"], - symbols=[Symbol(**s) for s in ast_data["symbols"]], - ast_nodes=[], # Would need custom deserialization for ASTNode - imports=ast_data["imports"], - exports=ast_data["exports"], - dependencies=ast_data["dependencies"], - ) - - return file_ast - - except Exception as e: - import logging - - logger = logging.getLogger(__name__) - logger.error(f"Error searching AST nodes: {e}") - return None - - def get_file_references(self, file_path: str) -> List[Dict[str, Any]]: - """Get all files that reference the given file. - - Args: - file_path: File to find references for - - Returns: - List of reference information - """ - try: - results = ( - self.references_table.output(["*"]) - .filter(f"target_file = '{file_path}'") - .to_pl() - ) - - references = [] - for row in results.iter_rows(named=True): - references.append( - { - "source_file": row["source_file"], - "symbol_name": row["symbol_name"], - "reference_type": row["reference_type"], - "line_number": row["line_number"], - } - ) - - return references - - except Exception as e: - import logging - - logger = logging.getLogger(__name__) - logger.error(f"Error getting file references: {e}") - return [] - - def search( - self, - query: str, - limit: int = 10, - score_threshold: float = 0.0, - filters: Dict[str, Any] = None, - ) -> List[SearchResult]: - """Search for similar documents. - - Args: - query: Search query - limit: Maximum number of results - score_threshold: Minimum similarity score - filters: Metadata filters (not yet implemented) - - Returns: - List of search results - """ - # Generate query embedding - query_embedding = self._generate_embedding(query) - - # Perform vector search - search_results = ( - self.documents_table.output(["*"]) - .match_dense( - "embedding", - query_embedding, - "float", - "ip", # Inner product (cosine similarity) - limit, - ) - .to_pl() - ) - - # Convert to SearchResult objects - results = [] - for row in search_results.iter_rows(named=True): - # Parse metadata - try: - metadata = json.loads(row["metadata"]) - except Exception: - metadata = {} - - # Create document - document = Document( - id=row["id"], - content=row["content"], - metadata=metadata, - file_path=row["file_path"] if row["file_path"] else None, - chunk_index=row["chunk_index"], - ) - - # Score is the similarity (higher is better) - score = row.get("score", 0.0) - distance = 1.0 - score # Convert similarity to distance - - if score >= score_threshold: - results.append( - SearchResult( - document=document, - score=score, - distance=distance, - ) - ) - - return results - - def delete_document(self, doc_id: str) -> bool: - """Delete a document by ID. - - Args: - doc_id: Document ID to delete - - Returns: - True if document was deleted - """ - try: - self.documents_table.delete(f"id = '{doc_id}'") - return True - except Exception: - return False - - def delete_file(self, file_path: str) -> int: - """Delete all documents from a specific file. - - Args: - file_path: File path to delete documents for - - Returns: - Number of documents deleted - """ - try: - # Get count first - results = ( - self.documents_table.output(["id"]) - .filter(f"file_path = '{file_path}'") - .to_pl() - ) - count = len(results) - - # Delete all documents for this file - self.documents_table.delete(f"file_path = '{file_path}'") - return count - except Exception: - return 0 - - def list_files(self) -> List[Dict[str, Any]]: - """List all indexed files. - - Returns: - List of file information - """ - try: - results = self.documents_table.output(["file_path", "metadata"]).to_pl() - - files = {} - for row in results.iter_rows(named=True): - file_path = row["file_path"] - if file_path and file_path not in files: - try: - metadata = json.loads(row["metadata"]) - files[file_path] = { - "file_path": file_path, - "file_name": metadata.get( - "file_name", Path(file_path).name - ), - "file_size": metadata.get("file_size", 0), - "total_chunks": metadata.get("total_chunks", 1), - } - except Exception: - files[file_path] = { - "file_path": file_path, - "file_name": Path(file_path).name, - } - - return list(files.values()) - except Exception: - return [] - - def _chunk_text(self, text: str, chunk_size: int, overlap: int) -> List[str]: - """Split text into overlapping chunks.""" - if len(text) <= chunk_size: - return [text] - - chunks = [] - start = 0 - - while start < len(text): - end = start + chunk_size - - # Try to break at word boundary - if end < len(text): - # Look back for a good break point - break_point = end - for i in range(end - 100, start + 100, -1): - if i > 0 and text[i] in "\n\r.!?": - break_point = i + 1 - break - end = break_point - - chunk = text[start:end].strip() - if chunk: - chunks.append(chunk) - - start = max(start + chunk_size - overlap, end) - - return chunks - - def _generate_embedding(self, text: str) -> List[float]: - """Generate embedding for text. - - For now, this returns a dummy embedding. In a real implementation, - you would call an embedding API (OpenAI, Cohere, etc.) or use a local model. - """ - # This is a placeholder - you would implement actual embedding generation here - # For now, return a random embedding of the correct dimension - import random - - return [random.random() for _ in range(self.dimension)] - - async def get_stats(self) -> Dict[str, Any]: - """Get statistics about the vector store. - - Returns: - Dictionary with statistics - """ - try: - # Get document count - doc_count_result = self.documents_table.output(["count(*)"]).to_pl() - doc_count = doc_count_result.item(0, 0) if len(doc_count_result) > 0 else 0 - - # Get unique file count - file_result = self.documents_table.output(["file_path"]).to_pl() - unique_files = set() - for row in file_result.iter_rows(): - if row[0]: - unique_files.add(row[0]) - - # Get symbol count - symbol_count = 0 - try: - symbol_result = self.symbols_table.output(["count(*)"]).to_pl() - symbol_count = symbol_result.item(0, 0) if len(symbol_result) > 0 else 0 - except Exception: - pass - - # Get AST count - ast_count = 0 - try: - ast_result = self.ast_table.output(["count(*)"]).to_pl() - ast_count = ast_result.item(0, 0) if len(ast_result) > 0 else 0 - except Exception: - pass - - return { - "document_count": doc_count, - "vector_count": doc_count, # Each document has a vector - "unique_files": len(unique_files), - "symbol_count": symbol_count, - "ast_count": ast_count, - "database_name": self.db_name, - "table_name": "documents", - "dimension": self.dimension, - } - except Exception as e: - return { - "error": str(e), - "document_count": 0, - "vector_count": 0, - } - - async def clear(self) -> bool: - """Clear all data from the vector store. - - Returns: - True if successful - """ - try: - # Delete all records from all tables - self.documents_table.delete() - - try: - self.symbols_table.delete() - except Exception: - pass - - try: - self.ast_table.delete() - except Exception: - pass - - try: - self.references_table.delete() - except Exception: - pass - - return True - except Exception as e: - import logging - - logger = logging.getLogger(__name__) - logger.error(f"Error clearing vector store: {e}") - return False - - async def index_document( - self, - content: str, - metadata: Dict[str, Any] = None, - ) -> str: - """Async version of add_document for consistency. - - Args: - content: Document content - metadata: Additional metadata - - Returns: - Document ID - """ - file_path = metadata.get("path") if metadata else None - return self.add_document(content, metadata, file_path) - - def close(self): - """Close the database connection.""" - if hasattr(self, "infinity"): - self.infinity.disconnect() diff --git a/pkg/hanzo-tools-vector/hanzo_tools/vector/mock_infinity.py b/pkg/hanzo-tools-vector/hanzo_tools/vector/mock_infinity.py deleted file mode 100644 index 02889b61e..000000000 --- a/pkg/hanzo-tools-vector/hanzo_tools/vector/mock_infinity.py +++ /dev/null @@ -1,161 +0,0 @@ -"""Mock implementation of infinity_embedded for testing on unsupported platforms.""" - -import random -from typing import Any, Dict, List -from pathlib import Path - - -class MockTable: - """Mock implementation of an Infinity table.""" - - def __init__(self, name: str, schema: Dict[str, Any]): - self.name = name - self.schema = schema - self.data = [] - self._id_counter = 0 - - def insert(self, records: List[Dict[str, Any]]): - """Insert records into the table.""" - for record in records: - # Add an internal ID if not present - if "id" not in record: - record["_internal_id"] = self._id_counter - self._id_counter += 1 - self.data.append(record) - - def delete(self, condition: str): - """Delete records matching condition.""" - # Simple implementation - just clear for now - self.data = [r for r in self.data if not self._eval_condition(r, condition)] - - def output(self, columns: List[str]): - """Start a query chain.""" - return MockQuery(self, columns) - - def _eval_condition(self, record: Dict[str, Any], condition: str) -> bool: - """Evaluate a simple condition.""" - # Very basic implementation - if "=" in condition: - field, value = condition.split("=", 1) - field = field.strip() - value = value.strip().strip("'\"") - return str(record.get(field, "")) == value - return False - - -class MockQuery: - """Mock query builder.""" - - def __init__(self, table: MockTable, columns: List[str]): - self.table = table - self.columns = columns - self.filters = [] - self.vector_search = None - self.limit_value = None - - def filter(self, condition: str): - """Add a filter condition.""" - self.filters.append(condition) - return self - - def match_dense( - self, column: str, vector: List[float], dtype: str, metric: str, limit: int - ): - """Add vector search.""" - self.vector_search = { - "column": column, - "vector": vector, - "dtype": dtype, - "metric": metric, - "limit": limit, - } - self.limit_value = limit - return self - - def to_pl(self): - """Execute query and return polars-like result.""" - results = self.table.data.copy() - - # Apply filters - for condition in self.filters: - results = [r for r in results if self.table._eval_condition(r, condition)] - - # Apply vector search (mock similarity) - if self.vector_search: - # Add mock scores - for r in results: - r["score"] = random.uniform(0.5, 1.0) - # Sort by score - results.sort(key=lambda x: x.get("score", 0), reverse=True) - # Limit results - if self.limit_value: - results = results[: self.limit_value] - - # Return mock polars DataFrame - return MockDataFrame(results) - - -class MockDataFrame: - """Mock polars DataFrame.""" - - def __init__(self, data: List[Dict[str, Any]]): - self.data = data - - def __len__(self): - return len(self.data) - - def iter_rows(self, named: bool = False): - """Iterate over rows.""" - if named: - return iter(self.data) - else: - # Return tuples - if not self.data: - return iter([]) - keys = list(self.data[0].keys()) - return iter([tuple(row.get(k) for k in keys) for row in self.data]) - - -class MockDatabase: - """Mock implementation of an Infinity database.""" - - def __init__(self, name: str): - self.name = name - self.tables = {} - - def create_table(self, name: str, schema: Dict[str, Any]) -> MockTable: - """Create a new table.""" - table = MockTable(name, schema) - self.tables[name] = table - return table - - def get_table(self, name: str) -> MockTable: - """Get an existing table.""" - if name not in self.tables: - raise KeyError(f"Table {name} not found") - return self.tables[name] - - -class MockInfinity: - """Mock implementation of Infinity connection.""" - - def __init__(self, path: str): - self.path = Path(path) - self.databases = {} - # Ensure directory exists - self.path.mkdir(parents=True, exist_ok=True) - - def get_database(self, name: str) -> MockDatabase: - """Get or create a database.""" - if name not in self.databases: - self.databases[name] = MockDatabase(name) - return self.databases[name] - - def disconnect(self): - """Disconnect from Infinity.""" - pass - - -def connect(path: str) -> MockInfinity: - """Connect to Infinity (mock implementation).""" - return MockInfinity(path) diff --git a/pkg/hanzo-tools-vector/hanzo_tools/vector/project_manager.py b/pkg/hanzo-tools-vector/hanzo_tools/vector/project_manager.py deleted file mode 100644 index d050ff7fe..000000000 --- a/pkg/hanzo-tools-vector/hanzo_tools/vector/project_manager.py +++ /dev/null @@ -1,394 +0,0 @@ -"""Project-aware vector database management for Hanzo AI.""" - -import asyncio -from typing import Any, Dict, List, Tuple, Optional -from pathlib import Path -from dataclasses import dataclass -from concurrent.futures import ThreadPoolExecutor - -from .index_config import IndexScope, IndexConfig -from .infinity_store import SearchResult, InfinityVectorStore - - -@dataclass -class ProjectInfo: - """Information about a detected project.""" - - root_path: Path - llm_md_path: Path - db_path: Path - name: str - - -class ProjectVectorManager: - """Manages project-aware vector databases.""" - - def __init__( - self, - global_db_path: Optional[str] = None, - embedding_model: str = "text-embedding-3-small", - dimension: int = 1536, - ): - """Initialize the project vector manager. - - Args: - global_db_path: Path for global vector store (default: ~/.config/hanzo/db) - embedding_model: Embedding model to use - dimension: Vector dimension - """ - self.embedding_model = embedding_model - self.dimension = dimension - - # Set up index configuration - self.index_config = IndexConfig() - - # Set up global database path - if global_db_path: - self.global_db_path = Path(global_db_path) - else: - self.global_db_path = self.index_config.get_index_path("vector") - - self.global_db_path.mkdir(parents=True, exist_ok=True) - - # Cache for project info and vector stores - self.projects: Dict[str, ProjectInfo] = {} - self.vector_stores: Dict[str, InfinityVectorStore] = {} - self._global_store: Optional[InfinityVectorStore] = None - - # Thread pool for parallel operations - self.executor = ThreadPoolExecutor(max_workers=4) - - def _get_global_store(self) -> InfinityVectorStore: - """Get or create the global vector store.""" - if self._global_store is None: - self._global_store = InfinityVectorStore( - data_path=str(self.global_db_path), - embedding_model=self.embedding_model, - dimension=self.dimension, - ) - return self._global_store - - def detect_projects(self, search_paths: List[str]) -> List[ProjectInfo]: - """Detect projects by finding LLM.md files. - - Args: - search_paths: List of paths to search for projects - - Returns: - List of detected project information - """ - projects = [] - - for search_path in search_paths: - path = Path(search_path).resolve() - - # Search for LLM.md files - for llm_md_path in path.rglob("LLM.md"): - project_root = llm_md_path.parent - project_name = project_root.name - - # Create .hanzo/db directory in project - db_path = project_root / ".hanzo" / "db" - db_path.mkdir(parents=True, exist_ok=True) - - project_info = ProjectInfo( - root_path=project_root, - llm_md_path=llm_md_path, - db_path=db_path, - name=project_name, - ) - - projects.append(project_info) - - # Cache project info - project_key = str(project_root) - self.projects[project_key] = project_info - - return projects - - def get_project_for_path(self, file_path: str) -> Optional[ProjectInfo]: - """Find the project that contains a given file path. - - Args: - file_path: File path to check - - Returns: - Project info if found, None otherwise - """ - path = Path(file_path).resolve() - - # Check each known project - for project_key, project_info in self.projects.items(): - try: - # Check if path is within project root - path.relative_to(project_info.root_path) - return project_info - except ValueError: - # Path is not within this project - continue - - # Try to find project by walking up the directory tree - current_path = path.parent if path.is_file() else path - - while current_path != current_path.parent: # Stop at filesystem root - llm_md_path = current_path / "LLM.md" - if llm_md_path.exists(): - # Found a project, create and cache it - db_path = current_path / ".hanzo" / "db" - db_path.mkdir(parents=True, exist_ok=True) - - project_info = ProjectInfo( - root_path=current_path, - llm_md_path=llm_md_path, - db_path=db_path, - name=current_path.name, - ) - - project_key = str(current_path) - self.projects[project_key] = project_info - return project_info - - current_path = current_path.parent - - return None - - def get_vector_store( - self, project_info: Optional[ProjectInfo] = None - ) -> InfinityVectorStore: - """Get vector store for a project or global store. - - Args: - project_info: Project to get store for, None for global store - - Returns: - Vector store instance - """ - # Check indexing scope - if project_info: - scope = self.index_config.get_scope(str(project_info.root_path)) - if scope == IndexScope.GLOBAL: - # Even for project files, use global store if configured - return self._get_global_store() - else: - return self._get_global_store() - - # Use project-specific store - project_key = str(project_info.root_path) - - if project_key not in self.vector_stores: - # Get index path based on configuration - index_path = self.index_config.get_index_path( - "vector", str(project_info.root_path) - ) - index_path.mkdir(parents=True, exist_ok=True) - - self.vector_stores[project_key] = InfinityVectorStore( - data_path=str(index_path), - embedding_model=self.embedding_model, - dimension=self.dimension, - ) - - return self.vector_stores[project_key] - - def add_file_to_appropriate_store( - self, - file_path: str, - chunk_size: int = 1000, - chunk_overlap: int = 200, - metadata: Dict[str, Any] = None, - ) -> Tuple[List[str], Optional[ProjectInfo]]: - """Add a file to the appropriate vector store (project or global). - - Args: - file_path: Path to file to add - chunk_size: Chunk size for text splitting - chunk_overlap: Overlap between chunks - metadata: Additional metadata - - Returns: - Tuple of (document IDs, project info or None for global) - """ - # Check if indexing is enabled - if not self.index_config.is_indexing_enabled("vector"): - return [], None - - # Find project for this file - project_info = self.get_project_for_path(file_path) - - # Get appropriate vector store based on scope configuration - vector_store = self.get_vector_store(project_info) - - # Add file metadata - file_metadata = metadata or {} - if project_info: - file_metadata["project_name"] = project_info.name - file_metadata["project_root"] = str(project_info.root_path) - # Check actual scope used - scope = self.index_config.get_scope(str(project_info.root_path)) - file_metadata["index_scope"] = scope.value - else: - file_metadata["project_name"] = "global" - file_metadata["index_scope"] = "global" - - # Add file to store - doc_ids = vector_store.add_file( - file_path=file_path, - chunk_size=chunk_size, - chunk_overlap=chunk_overlap, - metadata=file_metadata, - ) - - return doc_ids, project_info - - async def search_all_projects( - self, - query: str, - limit_per_project: int = 5, - score_threshold: float = 0.0, - include_global: bool = True, - project_filter: Optional[List[str]] = None, - ) -> Dict[str, List[SearchResult]]: - """Search across all projects in parallel. - - Args: - query: Search query - limit_per_project: Maximum results per project - score_threshold: Minimum similarity score - include_global: Whether to include global store - project_filter: List of project names to search (None for all) - - Returns: - Dictionary mapping project names to search results - """ - search_tasks = [] - project_names = [] - - # Add global store if requested - if include_global: - global_store = self._get_global_store() - search_tasks.append( - asyncio.get_event_loop().run_in_executor( - self.executor, - lambda: global_store.search( - query, limit_per_project, score_threshold - ), - ) - ) - project_names.append("global") - - # Add project stores - for _project_key, project_info in self.projects.items(): - # Apply project filter - if project_filter and project_info.name not in project_filter: - continue - - vector_store = self.get_vector_store(project_info) - search_tasks.append( - asyncio.get_event_loop().run_in_executor( - self.executor, - lambda vs=vector_store: vs.search( - query, limit_per_project, score_threshold - ), - ) - ) - project_names.append(project_info.name) - - # Execute all searches in parallel - results = await asyncio.gather(*search_tasks, return_exceptions=True) - - # Combine results - combined_results = {} - for i, result in enumerate(results): - project_name = project_names[i] - if isinstance(result, Exception): - # Log error but continue - import logging - - logger = logging.getLogger(__name__) - logger.error(f"Error searching project {project_name}: {result}") - combined_results[project_name] = [] - else: - combined_results[project_name] = result - - return combined_results - - def search_project_by_path( - self, - file_path: str, - query: str, - limit: int = 10, - score_threshold: float = 0.0, - ) -> List[SearchResult]: - """Search the project containing a specific file path. - - Args: - file_path: File path to determine project - query: Search query - limit: Maximum results - score_threshold: Minimum similarity score - - Returns: - Search results from the appropriate project store - """ - project_info = self.get_project_for_path(file_path) - vector_store = self.get_vector_store(project_info) - - return vector_store.search( - query=query, - limit=limit, - score_threshold=score_threshold, - ) - - def get_project_stats(self) -> Dict[str, Dict[str, Any]]: - """Get statistics for all projects. - - Returns: - Dictionary mapping project names to stats - """ - stats = {} - - # Global store stats - try: - global_store = self._get_global_store() - global_files = global_store.list_files() - stats["global"] = { - "file_count": len(global_files), - "db_path": str(self.global_db_path), - } - except Exception as e: - stats["global"] = {"error": str(e)} - - # Project store stats - for _project_key, project_info in self.projects.items(): - try: - vector_store = self.get_vector_store(project_info) - project_files = vector_store.list_files() - stats[project_info.name] = { - "file_count": len(project_files), - "db_path": str(project_info.db_path), - "root_path": str(project_info.root_path), - "llm_md_exists": project_info.llm_md_path.exists(), - } - except Exception as e: - stats[project_info.name] = {"error": str(e)} - - return stats - - def cleanup(self): - """Close all vector stores and cleanup resources.""" - # Close all project stores - for vector_store in self.vector_stores.values(): - try: - vector_store.close() - except Exception: - pass - - # Close global store - if self._global_store: - try: - self._global_store.close() - except Exception: - pass - - # Shutdown executor - self.executor.shutdown(wait=False) diff --git a/pkg/hanzo-tools-vector/hanzo_tools/vector/vector.py b/pkg/hanzo-tools-vector/hanzo_tools/vector/vector.py deleted file mode 100644 index 775a36f00..000000000 --- a/pkg/hanzo-tools-vector/hanzo_tools/vector/vector.py +++ /dev/null @@ -1,329 +0,0 @@ -"""Unified vector store tool.""" - -from typing import ( - Any, - Dict, - Unpack, - Optional, - Annotated, - TypedDict, - final, - override, -) -from pathlib import Path - -from pydantic import Field -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, PermissionManager, auto_timeout - -from .project_manager import ProjectVectorManager - -# Parameter types -Action = Annotated[ - str, - Field( - description="Action: search (default), index, stats, clear", - default="search", - ), -] - -Query = Annotated[ - Optional[str], - Field( - description="Search query for semantic similarity", - default=None, - ), -] - -Path = Annotated[ - Optional[str], - Field( - description="Path to index or search within", - default=".", - ), -] - -Include = Annotated[ - Optional[str], - Field( - description="File pattern to include (e.g., '*.py')", - default=None, - ), -] - -Exclude = Annotated[ - Optional[str], - Field( - description="File pattern to exclude", - default=None, - ), -] - -Limit = Annotated[ - int, - Field( - description="Maximum results to return", - default=10, - ), -] - -IncludeGit = Annotated[ - bool, - Field( - description="Include git history in indexing", - default=True, - ), -] - -ForceReindex = Annotated[ - bool, - Field( - description="Force reindexing even if up to date", - default=False, - ), -] - - -class VectorParams(TypedDict, total=False): - """Parameters for vector tool.""" - - action: str - query: Optional[str] - path: Optional[str] - include: Optional[str] - exclude: Optional[str] - limit: int - include_git: bool - force_reindex: bool - - -@final -class VectorTool(BaseTool): - """Unified vector store tool for semantic search.""" - - def __init__( - self, - permission_manager: PermissionManager, - project_manager: ProjectVectorManager, - ): - """Initialize the vector tool.""" - super().__init__(permission_manager) - self.project_manager = project_manager - - @property - @override - def name(self) -> str: - """Get the tool name.""" - return "vector" - - @property - @override - def description(self) -> str: - """Get the tool description.""" - return """Semantic search with embeddings. Actions: search (default), index, stats, clear. - -Usage: -vector "find authentication logic" -vector --action index --path ./src --include "*.py" -vector --action stats -vector --action clear --path ./old_code -""" - - @override - @auto_timeout("vector") - async def call( - self, - ctx: MCPContext, - **params: Unpack[VectorParams], - ) -> str: - """Execute vector operation.""" - tool_ctx = self.create_tool_context(ctx) - - # Extract action - action = params.get("action", "search") - - # Route to appropriate handler - if action == "search": - return await self._handle_search(params, tool_ctx) - elif action == "index": - return await self._handle_index(params, tool_ctx) - elif action == "stats": - return await self._handle_stats(params, tool_ctx) - elif action == "clear": - return await self._handle_clear(params, tool_ctx) - else: - return f"Error: Unknown action '{action}'. Valid actions: search, index, stats, clear" - - async def _handle_search(self, params: Dict[str, Any], tool_ctx) -> str: - """Handle semantic search.""" - query = params.get("query") - if not query: - return "Error: query is required for search action" - - path = params.get("path", ".") - limit = params.get("limit", 10) - - # Validate path - allowed, error_msg = await self.check_path_allowed(path, tool_ctx) - if not allowed: - return error_msg - - try: - # Determine search scope - project = self.project_manager.get_project_for_path(path) - if not project: - return "Error: No indexed project found for this path. Run 'vector --action index' first." - - # Search - await tool_ctx.info(f"Searching for: {query}") - results = project.search(query, k=limit) - - if not results: - return f"No results found for: {query}" - - # Format results - output = [f"=== Vector Search Results for '{query}' ==="] - output.append(f"Found {len(results)} matches\n") - - for i, result in enumerate(results, 1): - score = result.get("score", 0) - file_path = result.get("file_path", "unknown") - content = result.get("content", "") - chunk_type = result.get("metadata", {}).get("type", "content") - - output.append(f"Result {i} - Score: {score:.1%}") - output.append(f"File: {file_path}") - if chunk_type != "content": - output.append(f"Type: {chunk_type}") - output.append("-" * 60) - - # Truncate content if too long - if len(content) > 300: - content = content[:300] + "..." - output.append(content) - output.append("") - - return "\n".join(output) - - except Exception as e: - await tool_ctx.error(f"Search failed: {str(e)}") - return f"Error during search: {str(e)}" - - async def _handle_index(self, params: Dict[str, Any], tool_ctx) -> str: - """Handle indexing files.""" - path = params.get("path", ".") - include = params.get("include") - exclude = params.get("exclude") - include_git = params.get("include_git", True) - force = params.get("force_reindex", False) - - # Validate path - allowed, error_msg = await self.check_path_allowed(path, tool_ctx) - if not allowed: - return error_msg - - try: - await tool_ctx.info(f"Indexing {path}...") - - # Get or create project - project = self.project_manager.get_or_create_project(path) - - # Index files - stats = await project.index_directory( - path, - include_pattern=include, - exclude_pattern=exclude, - force_reindex=force, - ) - - # Index git history if requested - if include_git and Path(path).joinpath(".git").exists(): - await tool_ctx.info("Indexing git history...") - git_stats = await project.index_git_history(path) - stats["git_commits"] = git_stats.get("commits_indexed", 0) - - # Format output - output = [f"=== Indexing Complete ==="] - output.append(f"Path: {path}") - output.append(f"Files indexed: {stats.get('files_indexed', 0)}") - output.append(f"Chunks created: {stats.get('chunks_created', 0)}") - if stats.get("git_commits"): - output.append(f"Git commits indexed: {stats['git_commits']}") - output.append( - f"Total documents: {project.get_stats().get('total_documents', 0)}" - ) - - return "\n".join(output) - - except Exception as e: - await tool_ctx.error(f"Indexing failed: {str(e)}") - return f"Error during indexing: {str(e)}" - - async def _handle_stats(self, params: Dict[str, Any], tool_ctx) -> str: - """Get vector store statistics.""" - path = params.get("path") - - try: - if path: - # Stats for specific project - project = self.project_manager.get_project_for_path(path) - if not project: - return f"No indexed project found for path: {path}" - - stats = project.get_stats() - output = [f"=== Vector Store Stats for {project.name} ==="] - else: - # Global stats - stats = self.project_manager.get_global_stats() - output = ["=== Global Vector Store Stats ==="] - - output.append(f"Total documents: {stats.get('total_documents', 0)}") - output.append(f"Total size: {stats.get('total_size_mb', 0):.1f} MB") - - if stats.get("projects"): - output.append(f"\nProjects indexed: {len(stats['projects'])}") - for proj in stats["projects"]: - output.append( - f" - {proj['name']}: {proj['documents']} docs, {proj['size_mb']:.1f} MB" - ) - - return "\n".join(output) - - except Exception as e: - await tool_ctx.error(f"Failed to get stats: {str(e)}") - return f"Error getting stats: {str(e)}" - - async def _handle_clear(self, params: Dict[str, Any], tool_ctx) -> str: - """Clear vector store.""" - path = params.get("path") - - if not path: - return "Error: path is required for clear action" - - # Validate path - allowed, error_msg = await self.check_path_allowed(path, tool_ctx) - if not allowed: - return error_msg - - try: - project = self.project_manager.get_project_for_path(path) - if not project: - return f"No indexed project found for path: {path}" - - # Get stats before clearing - stats = project.get_stats() - doc_count = stats.get("total_documents", 0) - - # Clear - project.clear() - - return f"Cleared {doc_count} documents from vector store for {project.name}" - - except Exception as e: - await tool_ctx.error(f"Failed to clear: {str(e)}") - return f"Error clearing vector store: {str(e)}" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server.""" - pass diff --git a/pkg/hanzo-tools-vector/hanzo_tools/vector/vector_index.py b/pkg/hanzo-tools-vector/hanzo_tools/vector/vector_index.py deleted file mode 100644 index adea9502b..000000000 --- a/pkg/hanzo-tools-vector/hanzo_tools/vector/vector_index.py +++ /dev/null @@ -1,119 +0,0 @@ -"""Vector indexing tool for adding documents to vector database.""" - -from typing import Dict, Unpack, Optional, TypedDict, final -from pathlib import Path - -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, PermissionManager, auto_timeout - -from .project_manager import ProjectVectorManager - - -class VectorIndexParams(TypedDict, total=False): - """Parameters for vector indexing operations.""" - - file_path: str - content: Optional[str] - chunk_size: Optional[int] - chunk_overlap: Optional[int] - metadata: Optional[Dict[str, str]] - - -@final -class VectorIndexTool(BaseTool): - """Tool for indexing documents in the vector database.""" - - def __init__( - self, - permission_manager: PermissionManager, - project_manager: ProjectVectorManager, - ): - """Initialize the vector index tool. - - Args: - permission_manager: Permission manager for access control - project_manager: Project-aware vector store manager - """ - self.permission_manager = permission_manager - self.project_manager = project_manager - - @property - def name(self) -> str: - """Get the tool name.""" - return "vector_index" - - @property - def description(self) -> str: - """Get the tool description.""" - return """Index documents in project-aware vector databases for semantic search. - -Can index individual text content or entire files. Files are automatically assigned -to the appropriate project database based on LLM.md detection or stored in the global -database. Files are chunked for optimal search performance. - -Projects are detected by finding LLM.md files, with databases stored in .hanzo/db -directories alongside them. Use this to build searchable knowledge bases per project.""" - - @auto_timeout("vector_index") - async def call( - self, - ctx: MCPContext, - **params: Unpack[VectorIndexParams], - ) -> str: - """Index content or files in the vector database. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Indexing result message - """ - file_path = params.get("file_path") - content = params.get("content") - chunk_size = params.get("chunk_size", 1000) - chunk_overlap = params.get("chunk_overlap", 200) - metadata = params.get("metadata", {}) - - if not file_path and not content: - return "Error: Either file_path or content must be provided" - - try: - if file_path: - # Validate file access - # Use permission manager's existing validation - if not self.permission_manager.is_path_allowed(file_path): - return f"Error: Access denied to path {file_path}" - - if not Path(file_path).exists(): - return f"Error: File does not exist: {file_path}" - - # Index file using project-aware manager - doc_ids, project_info = ( - self.project_manager.add_file_to_appropriate_store( - file_path=file_path, - chunk_size=chunk_size, - chunk_overlap=chunk_overlap, - metadata=metadata, - ) - ) - - file_name = Path(file_path).name - if project_info: - return f"Successfully indexed {file_name} with {len(doc_ids)} chunks in project '{project_info.name}'" - else: - return f"Successfully indexed {file_name} with {len(doc_ids)} chunks in global database" - - else: - # Index content directly in global store (no project context) - global_store = self.project_manager._get_global_store() - doc_id = global_store.add_document( - content=content, - metadata=metadata, - ) - - return f"Successfully indexed content as document {doc_id} in global database" - - except Exception as e: - return f"Error indexing content: {str(e)}" diff --git a/pkg/hanzo-tools-vector/hanzo_tools/vector/vector_search.py b/pkg/hanzo-tools-vector/hanzo_tools/vector/vector_search.py deleted file mode 100644 index 5e35f73a1..000000000 --- a/pkg/hanzo-tools-vector/hanzo_tools/vector/vector_search.py +++ /dev/null @@ -1,249 +0,0 @@ -"""Vector search tool for semantic document retrieval.""" - -import json -from typing import List, Unpack, Optional, TypedDict, final - -from mcp.server.fastmcp import Context as MCPContext - -from hanzo_tools.core import BaseTool, PermissionManager, auto_timeout - -from .project_manager import ProjectVectorManager - - -class VectorSearchParams(TypedDict, total=False): - """Parameters for vector search operations.""" - - query: str - limit: Optional[int] - score_threshold: Optional[float] - include_content: Optional[bool] - file_filter: Optional[str] - project_filter: Optional[List[str]] - search_scope: Optional[str] # "all", "global", "current", or specific project name - - -@final -class VectorSearchTool(BaseTool): - """Tool for semantic search in the vector database.""" - - def __init__( - self, - permission_manager: PermissionManager, - project_manager: ProjectVectorManager, - ): - """Initialize the vector search tool. - - Args: - permission_manager: Permission manager for access control - project_manager: Project-aware vector store manager - """ - self.permission_manager = permission_manager - self.project_manager = project_manager - - @property - def name(self) -> str: - """Get the tool name.""" - return "vector_search" - - @property - def description(self) -> str: - """Get the tool description.""" - return """Pure semantic/vector search using Infinity embedded database. - -Searches indexed documents using vector embeddings to find semantically similar content. -This is NOT keyword search - it finds documents based on meaning and context similarity. - -Features: -- Searches across project-specific vector databases -- Returns similarity scores (0-1, higher is better) -- Supports filtering by project or file -- Automatically detects projects via LLM.md files - -Use 'grep' for exact text/pattern matching, 'vector_search' for semantic similarity.""" - - @auto_timeout("vector_search") - async def call( - self, - ctx: MCPContext, - **params: Unpack[VectorSearchParams], - ) -> str: - """Search for similar documents in the vector database. - - Args: - ctx: MCP context - **params: Tool parameters - - Returns: - Search results formatted as text - """ - query = params.get("query") - if not query: - return "Error: query parameter is required" - - limit = params.get("limit", 10) - score_threshold = params.get("score_threshold", 0.0) - include_content = params.get("include_content", True) - file_filter = params.get("file_filter") - project_filter = params.get("project_filter") - search_scope = params.get("search_scope", "all") - - try: - # Determine search strategy based on scope - if search_scope == "all": - # Search across all projects - project_results = await self.project_manager.search_all_projects( - query=query, - limit_per_project=limit, - score_threshold=score_threshold, - include_global=True, - project_filter=project_filter, - ) - - # Combine and sort all results - all_results = [] - for project_name, results in project_results.items(): - for result in results: - # Add project info to metadata - result.document.metadata = result.document.metadata or {} - result.document.metadata["search_project"] = project_name - all_results.append(result) - - # Sort by score and limit - all_results.sort(key=lambda x: x.score, reverse=True) - results = all_results[:limit] - - elif search_scope == "global": - # Search only global store - global_store = self.project_manager._get_global_store() - results = global_store.search( - query=query, - limit=limit, - score_threshold=score_threshold, - ) - for result in results: - result.document.metadata = result.document.metadata or {} - result.document.metadata["search_project"] = "global" - - else: - # Search specific project or current context - if search_scope != "current": - # Search specific project by name - project_info = None - for _proj_key, proj_info in self.project_manager.projects.items(): - if proj_info.name == search_scope: - project_info = proj_info - break - - if project_info: - vector_store = self.project_manager.get_vector_store( - project_info - ) - results = vector_store.search( - query=query, - limit=limit, - score_threshold=score_threshold, - ) - for result in results: - result.document.metadata = result.document.metadata or {} - result.document.metadata["search_project"] = ( - project_info.name - ) - else: - return f"Project '{search_scope}' not found" - else: - # For "current", try to detect from working directory - import os - - current_dir = os.getcwd() - project_info = self.project_manager.get_project_for_path( - current_dir - ) - - if project_info: - vector_store = self.project_manager.get_vector_store( - project_info - ) - results = vector_store.search( - query=query, - limit=limit, - score_threshold=score_threshold, - ) - for result in results: - result.document.metadata = result.document.metadata or {} - result.document.metadata["search_project"] = ( - project_info.name - ) - else: - # Fall back to global store - global_store = self.project_manager._get_global_store() - results = global_store.search( - query=query, - limit=limit, - score_threshold=score_threshold, - ) - for result in results: - result.document.metadata = result.document.metadata or {} - result.document.metadata["search_project"] = "global" - - if not results: - return f"No results found for query: '{query}'" - - # Filter by file if requested - if file_filter: - results = [ - r for r in results if file_filter in (r.document.file_path or "") - ] - - # Format results - output_lines = [f"Found {len(results)} results for query: '{query}'\n"] - - for i, result in enumerate(results, 1): - doc = result.document - score_percent = result.score * 100 - - # Header with score and metadata - project_name = doc.metadata.get("search_project", "unknown") - header = f"Result {i} (Score: {score_percent:.1f}%) - Project: {project_name}" - if doc.file_path: - header += f" - {doc.file_path}" - if doc.chunk_index is not None: - header += f" [Chunk {doc.chunk_index}]" - - output_lines.append(header) - output_lines.append("-" * len(header)) - - # Add metadata if available - if doc.metadata: - relevant_metadata = { - k: v - for k, v in doc.metadata.items() - if k not in ["chunk_number", "total_chunks", "search_project"] - } - if relevant_metadata: - output_lines.append( - f"Metadata: {json.dumps(relevant_metadata, indent=2)}" - ) - - # Add content if requested - if include_content: - content = doc.content - if len(content) > 500: - content = content[:500] + "..." - output_lines.append(f"Content:\n{content}") - - output_lines.append("") # Empty line between results - - return "\n".join(output_lines) - - except Exception as e: - return f"Error searching vector database: {str(e)}" - - def register(self, mcp_server) -> None: - """Register this tool with the MCP server. - - Args: - mcp_server: The FastMCP server instance - """ - # This is a placeholder - the actual registration would happen - # through the MCP server's tool registration mechanism - pass diff --git a/pkg/hanzo-tools-vector/pyproject.toml b/pkg/hanzo-tools-vector/pyproject.toml deleted file mode 100644 index 9144e16d1..000000000 --- a/pkg/hanzo-tools-vector/pyproject.toml +++ /dev/null @@ -1,28 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools-vector" -version = "0.2.0" -description = "Vector/embedding tools for Hanzo AI - indexing, search, RAG" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -keywords = ["hanzo", "tools", "vector", "embeddings", "rag", "mcp", "ai"] -dependencies = [ - "hanzo-tools>=0.3.0", - "mcp>=1.25.0", - "fastmcp>=2.14.1", - "pydantic>=2.12.5", -] - -[project.optional-dependencies] -full = ["sentence-transformers>=2.0.0", "faiss-cpu>=1.7.0"] - -[project.entry-points."hanzo.tools"] -vector = "hanzo_tools.vector:TOOLS" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] diff --git a/pkg/hanzo-tools-vector/tests/test_vector_tools.py b/pkg/hanzo-tools-vector/tests/test_vector_tools.py deleted file mode 100644 index 0700c1ef4..000000000 --- a/pkg/hanzo-tools-vector/tests/test_vector_tools.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Tests for hanzo-tools-vector.""" - -import pytest - - -class TestImports: - """Test that all modules can be imported.""" - - def test_import_package(self): - from hanzo_tools import vector - - assert vector is not None - - def test_import_tools(self): - from hanzo_tools.vector import TOOLS - - assert len(TOOLS) > 0 diff --git a/pkg/hanzo-tools/README.md b/pkg/hanzo-tools/README.md deleted file mode 100644 index 1fe482c11..000000000 --- a/pkg/hanzo-tools/README.md +++ /dev/null @@ -1,222 +0,0 @@ -# hanzo-tools - -Core infrastructure and plugin framework for Hanzo AI's modular MCP tool system. - -## Install - -```bash -pip install hanzo-tools # Core only -pip install hanzo-tools[dev] # filesystem, shell, editor, lsp, refactor, todo, reasoning, config -pip install hanzo-tools[ai] # llm, agent, memory -pip install hanzo-tools[all] # Everything -``` - -Individual tool packages can be installed separately: - -```bash -pip install hanzo-tools-fs -pip install hanzo-tools-shell -pip install hanzo-tools-browser -pip install hanzo-tools-llm -pip install hanzo-tools-memory -pip install hanzo-tools-editor -pip install hanzo-tools-vector -# ... etc -``` - -## Usage - -### Register all discovered tools - -```python -from hanzo_tools import discover_tools, register_all -from mcp.server import FastMCP - -mcp = FastMCP("my-server") - -# Auto-discover and register all installed tool packages -registered = register_all(mcp) -``` - -### Register individual tool packages - -```python -from hanzo_tools.fs import register_tools as register_fs -from hanzo_tools.shell import register_tools as register_shell - -register_fs(mcp, permission_manager) -register_shell(mcp) -``` - -### Build a custom tool - -```python -from typing import Any -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext -from hanzo_tools.core import BaseTool, ToolRegistry, with_error_logging - -class WeatherTool(BaseTool): - @property - def name(self) -> str: - return "get_weather" - - @property - def description(self) -> str: - return "Get current weather for a location" - - @with_error_logging("get_weather") - async def call(self, ctx: MCPContext, **params: Any) -> str: - location = params["location"] - return f"Weather in {location}: 72ยฐF, sunny" - - def register(self, mcp_server: FastMCP) -> None: - @mcp_server.tool() - async def get_weather(location: str, ctx: MCPContext) -> str: - """Get current weather for a location.""" - return await self.call(ctx, location=location) - -# Register with the MCP server -ToolRegistry.register_tool(mcp, WeatherTool()) -``` - -### Filesystem tools with permissions - -```python -from hanzo_tools.core import FileSystemTool, PermissionManager - -pm = PermissionManager( - allowed_paths=["/home/user/projects"], - deny_patterns=[".git", "node_modules", ".env"], -) - -class ReadFileTool(FileSystemTool): - def __init__(self): - super().__init__(permission_manager=pm) - - @property - def name(self) -> str: - return "read_file" - - @property - def description(self) -> str: - return "Read a file" - - async def call(self, ctx, **params): - path = params["path"] - if not self.is_path_allowed(path): - return "Access denied" - return open(path).read() - - def register(self, mcp_server): - @mcp_server.tool() - async def read_file(path: str, ctx) -> str: - """Read a file.""" - return await self.call(ctx, path=path) -``` - -### Decorators - -```python -from hanzo_tools.core import auto_timeout, with_error_logging, handle_connection_errors - -@auto_timeout("search", timeout=120) -@with_error_logging("search") -@handle_connection_errors -async def search_files(pattern: str, path: str) -> str: - ... -``` - -Timeouts are configurable via environment variables: - -```bash -HANZO_TIMEOUT_SEARCH=300 # Override search timeout to 5 minutes -HANZO_TIMEOUT_BROWSER=600 # Override browser timeout to 10 minutes -``` - -### Tool enable/disable - -```python -from hanzo_tools.core import ToolRegistry - -# Disable a tool at runtime -ToolRegistry.set_tool_enabled("browser", False) - -# Check if a tool is enabled -if ToolRegistry.is_tool_enabled("browser"): - ... -``` - -Tool states persist to `~/.hanzo/mcp/tool_states.json`. - -## Architecture - -``` -hanzo-tools (core) -โ”œโ”€โ”€ BaseTool โ€” Abstract base for all tools -โ”œโ”€โ”€ FileSystemTool โ€” Base for filesystem tools with permissions -โ”œโ”€โ”€ ToolRegistry โ€” Enable/disable and registration -โ”œโ”€โ”€ ToolContext โ€” Execution context with logging/progress -โ”œโ”€โ”€ PermissionManager โ€” Path-based access control -โ”œโ”€โ”€ MCPResourceDocument โ€” Structured response formatting -โ”œโ”€โ”€ auto_timeout โ€” Configurable async timeouts -โ”œโ”€โ”€ with_error_logging โ€” Error logging to ~/.hanzo/mcp/logs/ -โ”œโ”€โ”€ handle_connection_errors โ€” Graceful disconnect handling -โ”œโ”€โ”€ validate_path_parameter โ€” Path validation -โ”œโ”€โ”€ discover_tools() โ€” Plugin discovery via entry points -โ””โ”€โ”€ register_all() โ€” Auto-register all discovered tools - -hanzo-tools-fs โ€” File read/write/search/glob -hanzo-tools-shell โ€” Shell command execution -hanzo-tools-browser โ€” Browser automation (Playwright) -hanzo-tools-editor โ€” Code editing with AST awareness -hanzo-tools-lsp โ€” Language Server Protocol integration -hanzo-tools-refactor โ€” Refactoring operations -hanzo-tools-llm โ€” LLM inference tools -hanzo-tools-agent โ€” Agent orchestration -hanzo-tools-memory โ€” Persistent memory/context -hanzo-tools-vector โ€” Vector DB operations -hanzo-tools-database โ€” Database query tools -hanzo-tools-jupyter โ€” Jupyter notebook tools -hanzo-tools-todo โ€” Task/todo management -hanzo-tools-reasoning โ€” Chain-of-thought reasoning -hanzo-tools-config โ€” Configuration management -hanzo-tools-mcp โ€” MCP protocol utilities -hanzo-tools-computer โ€” Computer use (screen/keyboard) -``` - -Tool packages register via `importlib.metadata` entry points (`group="hanzo.tools"`), enabling automatic discovery without explicit imports. - -## API - -### Core Classes - -| Class | Purpose | -|-------|---------| -| `BaseTool` | Abstract base โ€” implement `name`, `description`, `call()`, `register()` | -| `FileSystemTool` | Extends BaseTool with `permission_manager` and `validate_path()` | -| `ToolRegistry` | Class-level enable/disable with JSON persistence | -| `ToolContext` | Wraps MCP context with `info()`, `warning()`, `error()`, `progress()` | -| `PermissionManager` | Allowed paths + deny patterns for filesystem access control | -| `MCPResourceDocument` | Response type with `to_json_string()`, `to_readable_string()`, `to_dict()` | -| `ValidationResult` | Boolean result with optional error message | - -### Decorators - -| Decorator | Purpose | -|-----------|---------| -| `@auto_timeout(name, timeout=None)` | Async timeout with env var override (`HANZO_TIMEOUT_*`) | -| `@with_error_logging(name)` | Log errors to `~/.hanzo/mcp/logs/` and return friendly messages | -| `@handle_connection_errors` | Catch disconnects gracefully | -| `@retry(max_attempts, delay, backoff)` | Exponential backoff retry | - -### Top-level Functions - -| Function | Purpose | -|----------|---------| -| `discover_tools()` | Find all installed tool packages via entry points | -| `register_all(mcp, pm, enabled)` | Register all discovered tools with an MCP server | - -## License - -MIT diff --git a/pkg/hanzo-tools/hanzo_tools/__init__.py b/pkg/hanzo-tools/hanzo_tools/__init__.py deleted file mode 100644 index 9faff528f..000000000 --- a/pkg/hanzo-tools/hanzo_tools/__init__.py +++ /dev/null @@ -1,112 +0,0 @@ -"""Hanzo Tools - Modular tool packages for AI agents. - -Installation: - pip install hanzo-tools # Core only - pip install hanzo-tools[core] # filesystem, shell, todo - pip install hanzo-tools[dev] # core + editor, lsp, refactor - pip install hanzo-tools[ai] # llm, agent, memory - pip install hanzo-tools[all] # Everything - -Individual packages: - pip install hanzo-tools-fs - pip install hanzo-tools-shell - pip install hanzo-tools-browser - pip install hanzo-tools-llm - pip install hanzo-tools-database - pip install hanzo-tools-memory - pip install hanzo-tools-agent - pip install hanzo-tools-editor - pip install hanzo-tools-jupyter - pip install hanzo-tools-lsp - pip install hanzo-tools-refactor - pip install hanzo-tools-vector - pip install hanzo-tools-todo - -Usage: - from hanzo_tools.fs import register_tools as register_fs - from hanzo_tools.shell import register_tools as register_shell - - # Register with MCP server - register_fs(mcp_server, permission_manager) - register_shell(mcp_server) -""" - -__version__ = "0.1.0" - -# Namespace package -__path__ = __import__("pkgutil").extend_path(__path__, __name__) - - -def discover_tools(): - """Discover all installed tool packages. - - Uses entry points to find hanzo.tools plugins. - - Returns: - Dict of package_name -> tools list - """ - import importlib.metadata - - tools = {} - - try: - eps = importlib.metadata.entry_points(group="hanzo.tools") - for ep in eps: - try: - module = ep.load() - if hasattr(module, "TOOLS"): - tools[ep.name] = module.TOOLS - except Exception: - pass - except Exception: - pass - - return tools - - -def register_all(mcp_server, permission_manager=None, enabled_tools=None): - """Register all discovered tools with the MCP server. - - Args: - mcp_server: FastMCP server instance - permission_manager: Optional permission manager - enabled_tools: Dict of tool_name -> enabled state - - Returns: - List of registered tool instances - """ - import importlib.metadata - - registered = [] - enabled = enabled_tools or {} - - try: - eps = importlib.metadata.entry_points(group="hanzo.tools") - for ep in eps: - # Check if package is enabled - if not enabled.get(ep.name, True): - continue - - try: - module_parent = ep.value.rsplit(":", 1)[0] - module = __import__(module_parent, fromlist=["register_tools"]) - - if hasattr(module, "register_tools"): - try: - tools = module.register_tools( - mcp_server, - permission_manager=permission_manager, - enabled_tools=enabled, - ) - except TypeError: - # Some tools don't need permission_manager - tools = module.register_tools(mcp_server, enabled_tools=enabled) - - if tools: - registered.extend(tools) - except Exception as e: - print(f"Failed to register {ep.name}: {e}") - except Exception: - pass - - return registered diff --git a/pkg/hanzo-tools/hanzo_tools/core/__init__.py b/pkg/hanzo-tools/hanzo_tools/core/__init__.py deleted file mode 100644 index 597e43b33..000000000 --- a/pkg/hanzo-tools/hanzo_tools/core/__init__.py +++ /dev/null @@ -1,65 +0,0 @@ -"""Core infrastructure for Hanzo tool packages. - -Exports both: -- HIP-0300 unified tool surface (`BaseTool`, `ToolError`, `Paging`, etc.) -- Low-level base abstractions (`BaseToolABC`, `ToolRegistry`, `FileSystemTool`) -""" - -from hanzo_tools.core.base import ( - BaseTool as BaseToolABC, - ToolRegistry, - FileSystemTool, - with_error_logging, - handle_connection_errors, -) -from hanzo_tools.core.types import MCPResourceDocument -from hanzo_tools.core.context import ToolContext, create_tool_context -from hanzo_tools.core.unified import ( - Range, - Paging, - BaseTool, # Unified HIP-0300 base class - ErrorCode, - ToolError, - ActionHandler, - ConflictError, - NotFoundError, - InvalidParamsError, - file_uri, - content_hash, -) -from hanzo_tools.core.decorators import auto_timeout -from hanzo_tools.core.validation import ValidationResult, validate_path_parameter -from hanzo_tools.core.permissions import PermissionManager - -__all__ = [ - # Base classes - "BaseTool", - "BaseToolABC", - "FileSystemTool", - "ToolRegistry", - # HIP-0300 unified helpers - "ActionHandler", - "ErrorCode", - "ToolError", - "ConflictError", - "NotFoundError", - "InvalidParamsError", - "Paging", - "Range", - "content_hash", - "file_uri", - # Context - "ToolContext", - "create_tool_context", - # Permissions - "PermissionManager", - # Decorators - "auto_timeout", - "with_error_logging", - "handle_connection_errors", - # Validation - "ValidationResult", - "validate_path_parameter", - # Types - "MCPResourceDocument", -] diff --git a/pkg/hanzo-tools/hanzo_tools/core/base.py b/pkg/hanzo-tools/hanzo_tools/core/base.py deleted file mode 100644 index 85abc03dc..000000000 --- a/pkg/hanzo-tools/hanzo_tools/core/base.py +++ /dev/null @@ -1,251 +0,0 @@ -"""Base classes for Hanzo tool packages. - -Provides the foundation for all tool implementations: -- BaseTool: Abstract base class defining the tool interface -- FileSystemTool: Base class for filesystem operations -- ToolRegistry: Central registry for tool management -""" - -import inspect -import logging -import functools -from abc import ABC, abstractmethod -from typing import Any, Callable, ClassVar, final -from pathlib import Path -from collections.abc import Awaitable - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -logger = logging.getLogger(__name__) - - -def with_error_logging(tool_name: str) -> Callable: - """Decorator to add comprehensive error logging to tool functions. - - Args: - tool_name: Name of the tool for logging purposes - - Returns: - Decorator function - """ - log_dir = Path.home() / ".hanzo" / "mcp" / "logs" - log_dir.mkdir(parents=True, exist_ok=True) - - def decorator(func: Callable[..., Awaitable[str]]) -> Callable[..., Awaitable[str]]: - @functools.wraps(func) - async def wrapper(*args: Any, **kwargs: Any) -> str: - try: - return await func(*args, **kwargs) - except TypeError as e: - error_msg = str(e) - if "takes" in error_msg and "positional argument" in error_msg: - sig = inspect.signature(func) - logger.error( - f"Tool {tool_name} call signature mismatch: " - f"expected {func.__name__}{sig}, got args={args}, kwargs={kwargs}" - ) - logger.exception(f"Tool {tool_name} TypeError: {e}") - return f"Error executing tool '{tool_name}': {error_msg}\n\nCheck logs at ~/.hanzo/mcp/logs/ for details." - except Exception as e: - logger.exception(f"Tool {tool_name} error: {e}") - return f"Error executing tool '{tool_name}': {str(e)}\n\nCheck logs at ~/.hanzo/mcp/logs/ for details." - - return wrapper - - return decorator - - -def handle_connection_errors( - func: Callable[..., Awaitable[str]], -) -> Callable[..., Awaitable[str]]: - """Decorator to handle connection errors gracefully.""" - - @functools.wraps(func) - async def wrapper(*args: Any, **kwargs: Any) -> str: - try: - return await func(*args, **kwargs) - except Exception as e: - error_name = type(e).__name__ - if any( - name in error_name - for name in [ - "ClosedResourceError", - "ConnectionError", - "BrokenPipeError", - ] - ): - return f"Client disconnected during operation: {error_name}" - raise - - return wrapper - - -class BaseTool(ABC): - """Abstract base class for all Hanzo tools. - - All tool packages must implement this interface to be compatible - with the hanzo-mcp server and tool registry. - - Example: - class MyTool(BaseTool): - @property - def name(self) -> str: - return "my_tool" - - @property - def description(self) -> str: - return "Does something useful" - - async def call(self, ctx, **params) -> str: - return "Result" - - def register(self, mcp_server): - @mcp_server.tool() - async def my_tool(...): - return await self.call(...) - """ - - @property - @abstractmethod - def name(self) -> str: - """Get the tool name as it appears in the MCP server.""" - pass - - @property - @abstractmethod - def description(self) -> str: - """Get detailed description of the tool's purpose and usage.""" - pass - - @abstractmethod - async def call(self, ctx: MCPContext, **params: Any) -> Any: - """Execute the tool with the given parameters. - - Args: - ctx: MCP context for the tool call - **params: Tool parameters provided by the caller - - Returns: - Tool execution result - """ - pass - - @abstractmethod - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server. - - Must create a wrapper function with explicit parameters - that calls this tool's call method. - - Args: - mcp_server: The FastMCP server instance - """ - pass - - -class FileSystemTool(BaseTool, ABC): - """Base class for filesystem-related tools. - - Provides common functionality for working with files and directories, - including permission checking and path validation. - """ - - def __init__(self, permission_manager: "PermissionManager | None" = None) -> None: - """Initialize filesystem tool. - - Args: - permission_manager: Permission manager for access control (auto-created if None) - """ - if permission_manager is None: - from hanzo_tools.core.permissions import PermissionManager - - permission_manager = PermissionManager() - self.permission_manager = permission_manager - - def validate_path(self, path: str, param_name: str = "path") -> "ValidationResult": - """Validate a path parameter.""" - from hanzo_tools.core.validation import validate_path_parameter - - return validate_path_parameter(path, param_name) - - def is_path_allowed(self, path: str) -> bool: - """Check if a path is allowed according to permission settings.""" - return self.permission_manager.is_path_allowed(path) - - -@final -class ToolRegistry: - """Registry for Hanzo tools. - - Provides functionality for registering tool implementations - with an MCP server, with support for enable/disable states. - """ - - # Class-level storage for tool states - _enabled_tools: ClassVar[dict[str, bool]] = {} - _config_loaded: ClassVar[bool] = False - - @classmethod - def _load_config(cls) -> None: - """Load tool enable/disable states from config.""" - if cls._config_loaded: - return - - import json - - config_file = Path.home() / ".hanzo" / "mcp" / "tool_states.json" - if config_file.exists(): - try: - with open(config_file) as f: - cls._enabled_tools = json.load(f) - except Exception: - pass - cls._config_loaded = True - - @classmethod - def is_tool_enabled(cls, tool_name: str) -> bool: - """Check if a tool is enabled.""" - cls._load_config() - return cls._enabled_tools.get(tool_name, True) # Enabled by default - - @classmethod - def set_tool_enabled( - cls, tool_name: str, enabled: bool, persist: bool = True - ) -> None: - """Enable or disable a tool.""" - import json - - cls._load_config() - cls._enabled_tools[tool_name] = enabled - - if persist: - config_file = Path.home() / ".hanzo" / "mcp" / "tool_states.json" - config_file.parent.mkdir(parents=True, exist_ok=True) - with open(config_file, "w") as f: - json.dump(cls._enabled_tools, f, indent=2) - - @staticmethod - def register_tool(mcp_server: FastMCP, tool: BaseTool) -> None: - """Register a tool with the MCP server. - - Args: - mcp_server: The FastMCP server instance - tool: The tool to register - """ - if ToolRegistry.is_tool_enabled(tool.name): - tool.register(mcp_server) - logger.debug(f"Registered tool: {tool.name}") - else: - logger.debug(f"Skipped disabled tool: {tool.name}") - - @staticmethod - def register_tools(mcp_server: FastMCP, tools: list[BaseTool]) -> None: - """Register multiple tools with the MCP server.""" - for tool in tools: - ToolRegistry.register_tool(mcp_server, tool) - - -# Import PermissionManager type for type hints -from hanzo_tools.core.validation import ValidationResult -from hanzo_tools.core.permissions import PermissionManager diff --git a/pkg/hanzo-tools/hanzo_tools/core/context.py b/pkg/hanzo-tools/hanzo_tools/core/context.py deleted file mode 100644 index e31d6d236..000000000 --- a/pkg/hanzo-tools/hanzo_tools/core/context.py +++ /dev/null @@ -1,67 +0,0 @@ -"""Tool execution context utilities.""" - -import logging -from typing import Any, Optional -from dataclasses import field, dataclass - -from mcp.server.fastmcp import Context as MCPContext - -logger = logging.getLogger(__name__) - - -@dataclass -class ToolContext: - """Extended context for tool execution. - - Provides utilities for logging, progress reporting, - and accessing the MCP context. - """ - - mcp_ctx: MCPContext - tool_name: Optional[str] = None - metadata: dict[str, Any] = field(default_factory=dict) - - async def set_tool_info(self, tool_name: str) -> None: - """Set the current tool name for logging.""" - self.tool_name = tool_name - - async def info(self, message: str) -> None: - """Log an info message.""" - logger.info(f"[{self.tool_name or 'tool'}] {message}") - - async def warning(self, message: str) -> None: - """Log a warning message.""" - logger.warning(f"[{self.tool_name or 'tool'}] {message}") - - async def error(self, message: str) -> None: - """Log an error message.""" - logger.error(f"[{self.tool_name or 'tool'}] {message}") - - async def debug(self, message: str) -> None: - """Log a debug message.""" - logger.debug(f"[{self.tool_name or 'tool'}] {message}") - - async def progress(self, current: int, total: int, message: str = "") -> None: - """Report progress.""" - pct = (current / total * 100) if total > 0 else 0 - await self.info(f"Progress: {current}/{total} ({pct:.1f}%) {message}") - - def get(self, key: str, default: Any = None) -> Any: - """Get a metadata value.""" - return self.metadata.get(key, default) - - def set(self, key: str, value: Any) -> None: - """Set a metadata value.""" - self.metadata[key] = value - - -def create_tool_context(mcp_ctx: MCPContext) -> ToolContext: - """Create a ToolContext from an MCP context. - - Args: - mcp_ctx: The MCP context from the tool call - - Returns: - Extended ToolContext for tool execution - """ - return ToolContext(mcp_ctx=mcp_ctx) diff --git a/pkg/hanzo-tools/hanzo_tools/core/decorators.py b/pkg/hanzo-tools/hanzo_tools/core/decorators.py deleted file mode 100644 index b2868c49d..000000000 --- a/pkg/hanzo-tools/hanzo_tools/core/decorators.py +++ /dev/null @@ -1,103 +0,0 @@ -"""Decorators for tool functions.""" - -import os -import asyncio -import functools -from typing import Any, Callable, Optional -from collections.abc import Awaitable - -# Default timeouts per tool (can be overridden via env vars) -DEFAULT_TIMEOUTS: dict[str, int] = { - "read": 30, - "write": 60, - "edit": 60, - "search": 120, - "dag": 600, - "browser": 300, - "default": 120, -} - - -def get_timeout(tool_name: str) -> int: - """Get timeout for a tool. - - Checks environment variable HANZO_TIMEOUT_{TOOL_NAME} first, - then falls back to defaults. - """ - env_key = f"HANZO_TIMEOUT_{tool_name.upper()}" - if env_val := os.environ.get(env_key): - try: - return int(env_val) - except ValueError: - pass - - return DEFAULT_TIMEOUTS.get(tool_name, DEFAULT_TIMEOUTS["default"]) - - -def auto_timeout( - tool_name: str, - timeout: Optional[int] = None, -) -> Callable: - """Decorator to add automatic timeout to async tool functions. - - Args: - tool_name: Name of the tool (for logging and config) - timeout: Override timeout in seconds (optional) - - Returns: - Decorator that wraps the function with timeout handling - """ - - def decorator(func: Callable[..., Awaitable[Any]]) -> Callable[..., Awaitable[Any]]: - @functools.wraps(func) - async def wrapper(*args: Any, **kwargs: Any) -> Any: - effective_timeout = timeout or get_timeout(tool_name) - - try: - return await asyncio.wait_for( - func(*args, **kwargs), - timeout=effective_timeout, - ) - except asyncio.TimeoutError: - return f"Tool '{tool_name}' timed out after {effective_timeout}s" - - return wrapper - - return decorator - - -def retry( - max_attempts: int = 3, - delay: float = 1.0, - backoff: float = 2.0, - exceptions: tuple = (Exception,), -) -> Callable: - """Decorator to add retry logic to async functions. - - Args: - max_attempts: Maximum number of attempts - delay: Initial delay between retries (seconds) - backoff: Multiplier for delay after each attempt - exceptions: Tuple of exceptions to catch and retry - """ - - def decorator(func: Callable[..., Awaitable[Any]]) -> Callable[..., Awaitable[Any]]: - @functools.wraps(func) - async def wrapper(*args: Any, **kwargs: Any) -> Any: - current_delay = delay - last_exception = None - - for attempt in range(max_attempts): - try: - return await func(*args, **kwargs) - except exceptions as e: - last_exception = e - if attempt < max_attempts - 1: - await asyncio.sleep(current_delay) - current_delay *= backoff - - raise last_exception - - return wrapper - - return decorator diff --git a/pkg/hanzo-tools/hanzo_tools/core/permissions.py b/pkg/hanzo-tools/hanzo_tools/core/permissions.py deleted file mode 100644 index 228baf96e..000000000 --- a/pkg/hanzo-tools/hanzo_tools/core/permissions.py +++ /dev/null @@ -1,86 +0,0 @@ -"""Permission management for filesystem tools.""" - -import os -from typing import Optional -from pathlib import Path - - -class PermissionManager: - """Manages filesystem permissions for tools. - - Controls which paths tools are allowed to access. - """ - - def __init__( - self, - allowed_paths: Optional[list[str | Path]] = None, - deny_patterns: Optional[list[str]] = None, - ): - """Initialize permission manager. - - Args: - allowed_paths: List of allowed base paths (defaults to cwd) - deny_patterns: Patterns to deny (e.g., '.git', 'node_modules') - """ - self.allowed_paths: list[Path] = [] - if allowed_paths: - for p in allowed_paths: - self.allowed_paths.append(Path(p).resolve()) - else: - self.allowed_paths.append(Path.cwd()) - - self.deny_patterns = deny_patterns or [ - ".git", - "__pycache__", - ".pyc", - "node_modules", - ".env", - ".secrets", - ] - - def is_path_allowed(self, path: str | Path) -> bool: - """Check if a path is allowed. - - Args: - path: Path to check - - Returns: - True if path is within allowed paths and not denied - """ - try: - resolved = Path(path).resolve() - - # Check if path is under any allowed path - is_under_allowed = any( - self._is_subpath(resolved, allowed) for allowed in self.allowed_paths - ) - - if not is_under_allowed: - return False - - # Check deny patterns - path_str = str(resolved) - for pattern in self.deny_patterns: - if pattern in path_str: - return False - - return True - - except Exception: - return False - - def _is_subpath(self, path: Path, parent: Path) -> bool: - """Check if path is under parent.""" - try: - path.relative_to(parent) - return True - except ValueError: - return False - - def add_allowed_path(self, path: str | Path) -> None: - """Add a path to the allowed list.""" - self.allowed_paths.append(Path(path).resolve()) - - def add_deny_pattern(self, pattern: str) -> None: - """Add a deny pattern.""" - self.deny_patterns.append(pattern) diff --git a/pkg/hanzo-tools/hanzo_tools/core/types.py b/pkg/hanzo-tools/hanzo_tools/core/types.py deleted file mode 100644 index 578c829e4..000000000 --- a/pkg/hanzo-tools/hanzo_tools/core/types.py +++ /dev/null @@ -1,171 +0,0 @@ -"""Type definitions for Hanzo tool packages.""" - -import json -from typing import Any, Dict, List, Optional -from dataclasses import dataclass - - -@dataclass -class MCPResourceDocument: - """Resource document returned by MCP tools. - - Output format options: - - to_json_string(): Clean JSON format (default for structured data) - - to_readable_string(): Human-readable formatted text for display - - to_dict(): Full dict structure with data/metadata - """ - - data: Dict[str, Any] - metadata: Optional[Dict[str, Any]] = None - - def to_dict(self) -> Dict[str, Any]: - """Convert to dictionary format with data/metadata structure.""" - result = {"data": self.data} - if self.metadata: - result["metadata"] = self.metadata - return result - - def to_json_string(self) -> str: - """Convert to clean JSON string.""" - # Return wrapped in "result" for consistency - return json.dumps({"result": self.data}, indent=2) - - def to_readable_string(self) -> str: - """Convert to human-readable formatted string for display. - - Optimized for readability in Claude Code output panels. - """ - lines: List[str] = [] - - if isinstance(self.data, dict): - # Handle search/find results with "results" array - if "results" in self.data: - results = self.data["results"] - stats = self.data.get("stats", {}) - pagination = self.data.get("pagination", {}) - - # Header with stats - if stats: - query = stats.get("query", stats.get("pattern", "")) - total = stats.get("total", len(results)) - time_ms = stats.get("time_ms", {}) - if time_ms: - if isinstance(time_ms, dict): - total_time = sum(time_ms.values()) - else: - total_time = time_ms - lines.append( - f"# Search: '{query}' ({total} results, {total_time}ms)" - ) - else: - lines.append(f"# Found {total} results for '{query}'") - else: - lines.append(f"# Found {len(results)} results") - lines.append("") - - # Format each result - for i, result in enumerate(results[:50], 1): - if isinstance(result, dict): - # Common patterns for search results - file_path = result.get("file", result.get("path", "")) - line_num = result.get("line", result.get("line_number", "")) - match_text = result.get( - "match", result.get("text", result.get("content", "")) - ) - result_type = result.get("type", "") - - if file_path: - loc = f"{file_path}:{line_num}" if line_num else file_path - lines.append(f"{i}. {loc}") - if match_text: - # Truncate long matches for readability - preview = ( - match_text[:200] + "..." - if len(match_text) > 200 - else match_text - ) - lines.append(f" {preview}") - if result_type: - lines.append(f" [{result_type}]") - else: - lines.append(f"{i}. {json.dumps(result, default=str)}") - else: - lines.append(f"{i}. {result}") - - # Show pagination info - if pagination: - page = pagination.get("page", 1) - total = pagination.get("total", 0) - has_next = pagination.get("has_next", False) - if has_next and total > 0: - total_pages = (total // 50) + 1 - lines.append( - f"\n... page {page} of {total_pages} ({total} total)" - ) - - # Handle command execution results - elif ( - "output" in self.data or "stdout" in self.data or "stderr" in self.data - ): - # Shell command output - exit_code = self.data.get("exit_code", self.data.get("returncode", 0)) - stdout = self.data.get("output", self.data.get("stdout", "")) - stderr = self.data.get("stderr", "") - elapsed = self.data.get("elapsed", self.data.get("time_ms", "")) - - if exit_code == 0: - lines.append(f"โœ“ Command succeeded") - else: - lines.append(f"โœ— Command failed (exit {exit_code})") - - if elapsed: - lines.append( - f"Time: {elapsed}ms" - if isinstance(elapsed, (int, float)) - else f"Time: {elapsed}" - ) - lines.append("") - - if stdout: - lines.append(stdout.rstrip()) - if stderr: - lines.append("\n--- stderr ---") - lines.append(stderr.rstrip()) - - # Handle error results - elif "error" in self.data: - lines.append(f"Error: {self.data['error']}") - if "details" in self.data: - lines.append(f"Details: {self.data['details']}") - - # Generic dict - format as key-value pairs - else: - for key, value in self.data.items(): - if isinstance(value, (dict, list)): - lines.append(f"{key}:") - lines.append(json.dumps(value, indent=2, default=str)) - else: - lines.append(f"{key}: {value}") - - elif isinstance(self.data, list): - # List data - format as numbered items - for i, item in enumerate(self.data[:50], 1): - if isinstance(item, dict): - lines.append(f"{i}. {json.dumps(item, default=str)}") - else: - lines.append(f"{i}. {item}") - if len(self.data) > 50: - lines.append(f"\n... {len(self.data) - 50} more items") - - else: - # Scalar or other - just convert to string - lines.append(str(self.data)) - - # Add metadata footer if present - if self.metadata: - lines.append("") - lines.append("---") - for key, value in self.metadata.items(): - lines.append(f"{key}: {value}") - - return "\n".join(lines) diff --git a/pkg/hanzo-tools/hanzo_tools/core/unified.py b/pkg/hanzo-tools/hanzo_tools/core/unified.py deleted file mode 100644 index bdb88c78e..000000000 --- a/pkg/hanzo-tools/hanzo_tools/core/unified.py +++ /dev/null @@ -1,517 +0,0 @@ -"""Base tool class for HIP-0300 architecture. - -Provides the foundation for orthogonal, composable tools following Unix philosophy: -- Permissive input, strict output -- Action routing with alias resolution -- Unified response envelope with ok/data/error/meta -- Built-in help and schema actions -- Typed error codes with Unix exit code semantics -- Paging/cursor support for large results -- Composable via stable identifiers (uri, hash, range, ref) - -Design principles: -- Input parsing = permissive (accept many spellings/forms) -- Output + semantics = strict (one canonical shape) -- Aliases never appear in output; only canonical action names - -Reference: HIP-0300 Unified MCP Tools Architecture -""" - -import json -import hashlib -import inspect -from abc import abstractmethod -from typing import Any, Literal, TypeVar, Callable, ClassVar, get_type_hints -from dataclasses import field, dataclass -from collections.abc import Awaitable - -from mcp.server import FastMCP -from mcp.server.fastmcp import Context as MCPContext - -from .base import BaseTool as _BaseToolABC - -# Error codes for structured error handling -ErrorCode = Literal[ - "UNKNOWN_ACTION", # Action not found - "INVALID_PARAMS", # Invalid parameters - "NOT_FOUND", # Resource not found - "CONFLICT", # Precondition failed (e.g., base_hash mismatch) - "PERMISSION_DENIED", # Access denied - "TIMEOUT", # Operation timed out - "INTERNAL_ERROR", # Unexpected error -] - - -@dataclass -class Paging: - """Pagination info for large results.""" - - cursor: str | None = None - more: bool = False - total: int | None = None - - -@dataclass -class ToolError(Exception): - """Structured error for tool operations.""" - - code: ErrorCode - message: str - details: dict[str, Any] = field(default_factory=dict) - - -class ConflictError(ToolError): - """Precondition failed (e.g., base_hash mismatch).""" - - def __init__( - self, message: str, expected: str | None = None, actual: str | None = None - ): - details = {} - if expected: - details["expected"] = expected - if actual: - details["actual"] = actual - super().__init__(code="CONFLICT", message=message, details=details) - - -class NotFoundError(ToolError): - """Resource not found.""" - - def __init__(self, message: str, uri: str | None = None): - details = {"uri": uri} if uri else {} - super().__init__(code="NOT_FOUND", message=message, details=details) - - -class InvalidParamsError(ToolError): - """Invalid parameters.""" - - def __init__( - self, message: str, param: str | None = None, expected: str | None = None - ): - details = {} - if param: - details["param"] = param - if expected: - details["expected"] = expected - super().__init__(code="INVALID_PARAMS", message=message, details=details) - - -@dataclass -class ActionHandler: - """Metadata for a registered action handler.""" - - name: str - handler: Callable[..., Awaitable[Any]] - description: str - schema: dict[str, Any] | None = None - examples: list[str] = field(default_factory=list) - - -class BaseTool(_BaseToolABC): - """Base class for HIP-0300 unified tools with action routing. - - Design principles: - - Permissive input, strict output - - Action routing with alias resolution - - Unified response envelope: {ok, data, error, meta} - - Aliases never appear in output; only canonical action names - - Usage: - class FsTool(BaseTool): - name = "fs" - description = "Filesystem operations" - - def __init__(self): - super().__init__() - self._register_actions() - - def _register_actions(self): - @self.action("read", "Read file contents") - async def read(ctx, uri: str, range: dict | None = None) -> dict: - content = await read_file(uri) - return {"text": content, "hash": hash_content(content)} - - @self.action("apply_patch", "Edit file with precondition") - async def apply_patch(ctx, uri: str, patch: str, base_hash: str) -> dict: - if get_hash(uri) != base_hash: - raise ConflictError("base_hash mismatch", expected=base_hash) - # Apply patch... - return {"uri": uri, "hash": new_hash} - """ - - # Subclasses must define these - name: ClassVar[str] - - # Version for meta envelope - VERSION: ClassVar[str] = "0.12.0" - - # Param aliases for cross-implementation parity (e.g., {"path": "uri"}) - PARAM_ALIASES: ClassVar[dict[str, str]] = {} - - def __init__(self): - self._handlers: dict[str, ActionHandler] = {} - self._register_builtin_actions() - - def _register_builtin_actions(self): - """Register built-in help and schema actions.""" - - @self.action("help", "List available actions with descriptions") - async def help_action(ctx: MCPContext) -> dict: - actions = {} - for name, handler in self._handlers.items(): - if name not in ("help", "schema", "status"): - actions[name] = { - "description": handler.description, - "examples": handler.examples, - } - return {"actions": actions, "tool": self.name} - - @self.action("schema", "Get JSON Schema for action parameters") - async def schema_action( - ctx: MCPContext, action_name: str | None = None - ) -> dict: - if action_name: - handler = self._handlers.get(action_name) - if not handler: - raise ToolError( - code="UNKNOWN_ACTION", - message=f"Unknown action: {action_name}", - details={"available": list(self._handlers.keys())}, - ) - return {"action": action_name, "schema": handler.schema or {}} - - schemas = {} - for name, handler in self._handlers.items(): - if name not in ("help", "schema", "status"): - schemas[name] = handler.schema or {} - return {"schemas": schemas} - - @self.action("status", "Get tool status and version") - async def status_action(ctx: MCPContext) -> dict: - return { - "tool": self.name, - "version": self.VERSION, - "enabled": True, - "actions": list(self._handlers.keys()), - } - - def action( - self, - name: str, - description: str = "", - schema: dict[str, Any] | None = None, - examples: list[str] | None = None, - ) -> Callable: - """Decorator to register an action handler. - - Args: - name: Action name (e.g., "read", "apply_patch") - description: Human-readable description - schema: Optional JSON Schema for parameters - examples: Optional list of example usages - - Returns: - Decorator function - """ - - def decorator( - fn: Callable[..., Awaitable[Any]], - ) -> Callable[..., Awaitable[Any]]: - # Auto-generate schema from type hints if not provided - auto_schema = schema - if auto_schema is None: - auto_schema = self._generate_schema(fn) - - self._handlers[name] = ActionHandler( - name=name, - handler=fn, - description=description or fn.__doc__ or "", - schema=auto_schema, - examples=examples or [], - ) - return fn - - return decorator - - def _generate_schema(self, fn: Callable) -> dict[str, Any]: - """Generate JSON Schema from function signature and type hints.""" - try: - hints = get_type_hints(fn) - except Exception: - hints = {} - - sig = inspect.signature(fn) - properties = {} - required = [] - - for param_name, param in sig.parameters.items(): - if param_name in ("self", "ctx"): - continue - - param_schema: dict[str, Any] = {} - hint = hints.get(param_name) - - if hint is str: - param_schema["type"] = "string" - elif hint is int: - param_schema["type"] = "integer" - elif hint is float: - param_schema["type"] = "number" - elif hint is bool: - param_schema["type"] = "boolean" - elif hint is dict or ( - hasattr(hint, "__origin__") and hint.__origin__ is dict - ): - param_schema["type"] = "object" - elif hint is list or ( - hasattr(hint, "__origin__") and hint.__origin__ is list - ): - param_schema["type"] = "array" - else: - param_schema["type"] = "string" # Default to string - - properties[param_name] = param_schema - - if param.default is inspect.Parameter.empty: - required.append(param_name) - - return { - "type": "object", - "properties": properties, - "required": required, - } - - def _envelope( - self, - data: Any, - action: str | None = None, - paging: Paging | None = None, - backend: str | None = None, - trace_id: str | None = None, - ) -> dict[str, Any]: - """Wrap result in unified response envelope.""" - meta: dict[str, Any] = { - "tool": self.name, - "version": self.VERSION, - } - if action: - meta["action"] = action - if backend: - meta["backend"] = backend - if trace_id: - meta["trace_id"] = trace_id - - if paging: - meta["paging"] = { - "cursor": paging.cursor, - "more": paging.more, - } - if paging.total is not None: - meta["paging"]["total"] = paging.total - else: - meta["paging"] = {"cursor": None, "more": False} - - return { - "ok": True, - "data": data, - "error": None, - "meta": meta, - } - - def _error( - self, - code: ErrorCode, - message: str, - **details: Any, - ) -> dict[str, Any]: - """Create error response envelope.""" - error_dict: dict[str, Any] = { - "code": code, - "message": message, - } - if details: - error_dict.update(details) - - return { - "ok": False, - "data": None, - "error": error_dict, - "meta": {"tool": self.name, "version": self.VERSION}, - } - - @property - @abstractmethod - def description(self) -> str: - """Tool description for MCP registration.""" - pass - - async def call( - self, ctx: MCPContext, action: str = "help", **kwargs: Any - ) -> dict[str, Any]: - """Execute tool with action routing. - - Args: - ctx: MCP context - action: Action to execute (default: "help") - **kwargs: Action parameters (flat, matching TS wire format) - - Returns: - Unified response envelope - """ - # Unwrap kwargs wrapping from legacy clients: {"kwargs": {...}} โ†’ flat - if "kwargs" in kwargs and isinstance(kwargs["kwargs"], dict) and len(kwargs) == 1: - kwargs = kwargs["kwargs"] - - # Apply param aliases for cross-implementation parity - for alias, canonical in self.PARAM_ALIASES.items(): - if alias in kwargs and canonical not in kwargs: - kwargs[canonical] = kwargs.pop(alias) - - if action not in self._handlers: - return self._error( - "UNKNOWN_ACTION", - f"Unknown action: {action}", - available=list(self._handlers.keys()), - ) - - handler = self._handlers[action] - - try: - result = await handler.handler(ctx, **kwargs) - return self._envelope(result, action=action) - except ToolError as e: - return self._error(e.code, e.message, **e.details) - except Exception as e: - return self._error("INTERNAL_ERROR", str(e)) - - def register(self, mcp_server: FastMCP) -> None: - """Register this tool with the MCP server. - - Creates a single MCP tool with flat params matching TS wire format. - Bypasses FastMCP's function introspection to support arbitrary extra - parameters (action-specific params) as top-level fields. - - Wire format: {"action": "read", "path": "/tmp/foo"} (flat, same as TS) - """ - from pydantic import ConfigDict - from mcp.server.fastmcp.tools.base import Tool as FastMCPTool - from mcp.server.fastmcp.utilities.func_metadata import ( - ArgModelBase, - FuncMetadata, - ) - - tool_name = self.name - tool_description = ( - f"{self.description}\n\nActions: {', '.join(self._handlers.keys())}" - ) - - # Build combined JSON schema from all action handlers - all_properties: dict[str, Any] = { - "action": { - "type": "string", - "description": "Action to perform", - "default": "help", - } - } - for ah in self._handlers.values(): - if ah.schema and "properties" in ah.schema: - for k, v in ah.schema["properties"].items(): - if k not in all_properties and k != "ctx": - all_properties[k] = v - - parameters_schema = { - "type": "object", - "properties": all_properties, - "required": ["action"], - "additionalProperties": True, - } - - # Permissive Pydantic model that accepts any extra fields - class _FlexArgs(ArgModelBase): - model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True) - action: str = "help" - - def model_dump_one_level(self) -> dict[str, Any]: - result = super().model_dump_one_level() - if self.model_extra: - result.update(self.model_extra) - return result - - # Handler function โ€” receives flat validated args - tool_ref = self - - async def _handler(ctx: MCPContext, action: str = "help", **kwargs: Any) -> str: - result = await tool_ref.call(ctx, action=action, **kwargs) - return json.dumps(result, indent=2, default=str) - - metadata = FuncMetadata(arg_model=_FlexArgs) - - tool = FastMCPTool( - fn=_handler, - name=tool_name, - description=tool_description, - parameters=parameters_schema, - fn_metadata=metadata, - is_async=True, - context_kwarg="ctx", - ) - - # Register directly, bypassing Tool.from_function() introspection - mcp_server._tool_manager._tools[tool_name] = tool - - -# Utility functions for composability - - -def content_hash(content: str | bytes, algorithm: str = "sha256") -> str: - """Generate content hash for file identity.""" - if isinstance(content, str): - content = content.encode("utf-8") - h = hashlib.new(algorithm) - h.update(content) - return f"{algorithm}:{h.hexdigest()}" - - -def file_uri(path: str) -> str: - """Convert path to file:// URI.""" - from pathlib import Path - - abs_path = Path(path).resolve() - return f"file://{abs_path}" - - -@dataclass -class Range: - """Text range for composability. - - 0-based line and column numbers. - """ - - start_line: int - start_col: int = 0 - end_line: int | None = None - end_col: int | None = None - - def to_dict(self) -> dict[str, Any]: - """Convert to dict format for serialization.""" - result = { - "start": {"line": self.start_line, "col": self.start_col}, - } - if self.end_line is not None: - result["end"] = { - "line": self.end_line, - "col": self.end_col or 0, - } - return result - - @classmethod - def from_dict(cls, d: dict[str, Any]) -> "Range": - """Create Range from dict.""" - start = d.get("start", {}) - end = d.get("end") - return cls( - start_line=start.get("line", 0), - start_col=start.get("col", 0), - end_line=end.get("line") if end else None, - end_col=end.get("col") if end else None, - ) diff --git a/pkg/hanzo-tools/hanzo_tools/core/validation.py b/pkg/hanzo-tools/hanzo_tools/core/validation.py deleted file mode 100644 index d848f32a6..000000000 --- a/pkg/hanzo-tools/hanzo_tools/core/validation.py +++ /dev/null @@ -1,115 +0,0 @@ -"""Validation utilities for tool parameters.""" - -from typing import Optional -from pathlib import Path -from dataclasses import dataclass - - -@dataclass -class ValidationResult: - """Result of a validation check.""" - - is_valid: bool - error_message: Optional[str] = None - - def __bool__(self) -> bool: - return self.is_valid - - -def validate_path_parameter( - path: str, - param_name: str = "path", - must_exist: bool = False, - must_be_file: bool = False, - must_be_dir: bool = False, -) -> ValidationResult: - """Validate a path parameter. - - Args: - path: Path string to validate - param_name: Name of the parameter for error messages - must_exist: If True, path must exist - must_be_file: If True, path must be a file - must_be_dir: If True, path must be a directory - - Returns: - ValidationResult with status and error if any - """ - if not path: - return ValidationResult( - is_valid=False, - error_message=f"{param_name} is required", - ) - - if not path.strip(): - return ValidationResult( - is_valid=False, - error_message=f"{param_name} cannot be empty", - ) - - try: - p = Path(path) - - if must_exist and not p.exists(): - return ValidationResult( - is_valid=False, - error_message=f"{param_name} does not exist: {path}", - ) - - if must_be_file and p.exists() and not p.is_file(): - return ValidationResult( - is_valid=False, - error_message=f"{param_name} is not a file: {path}", - ) - - if must_be_dir and p.exists() and not p.is_dir(): - return ValidationResult( - is_valid=False, - error_message=f"{param_name} is not a directory: {path}", - ) - - return ValidationResult(is_valid=True) - - except Exception as e: - return ValidationResult( - is_valid=False, - error_message=f"Invalid {param_name}: {e}", - ) - - -def validate_string_parameter( - value: str, - param_name: str, - min_length: int = 0, - max_length: Optional[int] = None, - pattern: Optional[str] = None, -) -> ValidationResult: - """Validate a string parameter.""" - if not value: - return ValidationResult( - is_valid=False, - error_message=f"{param_name} is required", - ) - - if len(value) < min_length: - return ValidationResult( - is_valid=False, - error_message=f"{param_name} must be at least {min_length} characters", - ) - - if max_length and len(value) > max_length: - return ValidationResult( - is_valid=False, - error_message=f"{param_name} must be at most {max_length} characters", - ) - - if pattern: - import re - - if not re.match(pattern, value): - return ValidationResult( - is_valid=False, - error_message=f"{param_name} does not match required pattern", - ) - - return ValidationResult(is_valid=True) diff --git a/pkg/hanzo-tools/pyproject.toml b/pkg/hanzo-tools/pyproject.toml deleted file mode 100644 index d475aba61..000000000 --- a/pkg/hanzo-tools/pyproject.toml +++ /dev/null @@ -1,100 +0,0 @@ -[build-system] -requires = ["setuptools>=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-tools" -version = "0.3.0" -description = "Hanzo AI tools - core infrastructure and tool bundles" -readme = "README.md" -requires-python = ">=3.12" -license = { text = "MIT" } -authors = [{ name = "Hanzo Industries Inc", email = "dev@hanzo.ai" }] -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -keywords = ["hanzo", "tools", "mcp", "ai", "agent"] - -# Core dependencies (merged from hanzo-tools-core) -dependencies = [ - "mcp>=1.25.0", - "fastmcp>=2.14.1", - "pydantic>=2.12.5", - "typing-extensions>=4.13.0", - "aiofiles>=24.1.0", -] - -[project.optional-dependencies] -# Individual tool packages -filesystem = ["hanzo-tools-fs>=0.1.0"] -shell = ["hanzo-tools-shell>=0.5.4"] -browser = ["hanzo-tools-browser>=0.1.0"] -llm = ["hanzo-tools-llm>=0.1.0"] -database = ["hanzo-tools-database>=0.1.0"] -memory = ["hanzo-tools-memory>=0.1.0"] -agent = ["hanzo-tools-agent>=0.1.0"] -editor = ["hanzo-tools-editor>=0.1.0"] -jupyter = ["hanzo-tools-jupyter>=0.1.0"] -lsp = ["hanzo-tools-lsp>=0.1.0"] -refactor = ["hanzo-tools-refactor>=0.1.0"] -vector = ["hanzo-tools-vector>=0.1.0"] -todo = ["hanzo-tools-todo>=0.1.0"] -config = ["hanzo-tools-config>=0.1.0"] -mcp_tools = ["hanzo-tools-mcp>=0.1.0"] -reasoning = ["hanzo-tools-reasoning>=0.1.0"] -computer = ["hanzo-tools-computer>=0.1.0"] -code = ["hanzo-tools-code>=0.1.0"] -vcs = ["hanzo-tools-vcs>=0.1.0"] -net = ["hanzo-tools-net>=0.1.0"] - -# Bundles -dev = [ - "hanzo-tools-fs>=0.1.0", - "hanzo-tools-shell>=0.5.4", - "hanzo-tools-todo>=0.1.0", - "hanzo-tools-reasoning>=0.1.0", - "hanzo-tools-config>=0.1.0", - "hanzo-tools-editor>=0.1.0", - "hanzo-tools-lsp>=0.1.0", - "hanzo-tools-refactor>=0.1.0", -] -ai = [ - "hanzo-tools-llm>=0.1.0", - "hanzo-tools-agent>=0.1.0", - "hanzo-tools-memory>=0.1.0", -] -all = [ - "hanzo-tools-fs>=0.1.0", - "hanzo-tools-shell>=0.5.4", - "hanzo-tools-browser>=0.1.0", - "hanzo-tools-llm>=0.1.0", - "hanzo-tools-database>=0.1.0", - "hanzo-tools-memory>=0.1.0", - "hanzo-tools-agent>=0.1.0", - "hanzo-tools-editor>=0.1.0", - "hanzo-tools-jupyter>=0.1.0", - "hanzo-tools-lsp>=0.1.0", - "hanzo-tools-refactor>=0.1.0", - "hanzo-tools-vector>=0.1.0", - "hanzo-tools-todo>=0.1.0", - "hanzo-tools-reasoning>=0.1.0", - "hanzo-tools-config>=0.1.0", - "hanzo-tools-mcp>=0.1.0", - "hanzo-tools-computer>=0.1.0", - "hanzo-tools-code>=0.1.0", - "hanzo-tools-vcs>=0.1.0", - "hanzo-tools-net>=0.1.0", -] - -[project.urls] -"Homepage" = "https://github.com/hanzoai/python-sdk" -"Bug Tracker" = "https://github.com/hanzoai/python-sdk/issues" - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_tools*"] - -[tool.setuptools.package-data] -hanzo_tools = ["py.typed"] diff --git a/pkg/hanzo-web3/README.md b/pkg/hanzo-web3/README.md deleted file mode 100644 index 0f93ae41e..000000000 --- a/pkg/hanzo-web3/README.md +++ /dev/null @@ -1,171 +0,0 @@ -# Hanzo Web3 SDK - -Enterprise blockchain infrastructure SDK for Python. Multi-chain RPC, Token APIs, NFT APIs, Smart Wallets, Webhooks, and more. - -## Installation - -```bash -pip install hanzo-web3 - -# Or with uv -uv add hanzo-web3 -``` - -## Quick Start - -```python -from hanzo_web3 import Client - -# Initialize client -client = Client(api_key="hz_live_...") - -# Get latest block number -block = client.rpc.eth_block_number(chain="ethereum") -print(f"Latest block: {block}") - -# Get token balances -balances = client.tokens.get_balances("0x...", chain="polygon") -for token in balances: - print(f"{token['symbol']}: {token['balance_formatted']}") - -# Get NFTs owned -nfts = client.nfts.get_owned("0x...", chain="ethereum") -for nft in nfts: - print(f"{nft['name']} #{nft['token_id']}") -``` - -## Async Client - -```python -import asyncio -from hanzo_web3 import AsyncClient - -async def main(): - async with AsyncClient(api_key="hz_live_...") as client: - # Parallel requests - eth_block, polygon_block = await asyncio.gather( - client.rpc.eth_block_number(chain="ethereum"), - client.rpc.eth_block_number(chain="polygon"), - ) - print(f"ETH: {eth_block}, Polygon: {polygon_block}") - -asyncio.run(main()) -``` - -## Features - -### RPC API -Direct JSON-RPC access to 100+ chains: -```python -# Any RPC method -result = client.rpc.call("eth_getBalance", ["0x...", "latest"], chain="ethereum") - -# Convenience methods -balance = client.rpc.eth_get_balance("0x...", chain="ethereum") -``` - -### Token API -ERC-20 token data: -```python -# Get all token balances -balances = client.tokens.get_balances("0x...", chain="ethereum") - -# Get token metadata -metadata = client.tokens.get_metadata("0xA0b8...", chain="ethereum") -``` - -### NFT API -NFT collections and metadata: -```python -# Get owned NFTs -nfts = client.nfts.get_owned("0x...", chain="ethereum") - -# Get NFT metadata -metadata = client.nfts.get_metadata("0x...", "1234", chain="ethereum") -``` - -### Smart Wallets (ERC-4337) -Account abstraction: -```python -# Create smart wallet -wallet = client.wallets.create(owner="0x...", chain="base") -print(f"Smart wallet: {wallet['address']}") - -# Get wallet details -info = client.wallets.get(wallet['address'], chain="base") -``` - -### Webhooks -Real-time event notifications: -```python -# Create webhook -webhook = client.webhooks.create( - url="https://your-server.com/webhook", - event_type="ADDRESS_ACTIVITY", - chain="ethereum", - filters={"addresses": ["0x..."]} -) - -# List webhooks -webhooks = client.webhooks.list() - -# Delete webhook -client.webhooks.delete(webhook["id"]) -``` - -## Supported Chains - -| Chain | Networks | -|-------|----------| -| Ethereum | mainnet, sepolia, holesky | -| Polygon | mainnet, amoy | -| Arbitrum | mainnet, sepolia | -| Optimism | mainnet, sepolia | -| Base | mainnet, sepolia | -| Avalanche | mainnet, fuji | -| BNB Chain | mainnet, testnet | -| Lux | mainnet, testnet | -| Solana | mainnet, devnet | -| + 90 more | ... | - -## Environment Variables - -```bash -# API Key (alternative to passing in code) -export HANZO_WEB3_API_KEY=hz_live_... - -# Custom API URL (for white-label deployments) -export HANZO_WEB3_BASE_URL=https://api.lux.cloud -``` - -## Error Handling - -```python -from hanzo_web3 import Client -from hanzo_web3.exceptions import ( - AuthenticationError, - RateLimitError, - ChainNotSupportedError, -) - -try: - client = Client(api_key="invalid") - client.rpc.eth_block_number() -except AuthenticationError: - print("Invalid API key") -except RateLimitError as e: - print(f"Rate limited. Retry after {e.retry_after}s") -except ChainNotSupportedError as e: - print(f"Chain {e.chain} not supported") -``` - -## Links - -- [Documentation](https://docs.web3.hanzo.ai) -- [Dashboard](https://web3.hanzo.ai) -- [API Reference](https://docs.web3.hanzo.ai/api) -- [GitHub](https://github.com/hanzoai/python-sdk) - -## License - -Apache 2.0 - See [LICENSE](LICENSE) for details. diff --git a/pkg/hanzo-web3/pyproject.toml b/pkg/hanzo-web3/pyproject.toml deleted file mode 100644 index 4339795e0..000000000 --- a/pkg/hanzo-web3/pyproject.toml +++ /dev/null @@ -1,53 +0,0 @@ -[project] -name = "hanzo-web3" -version = "0.1.0" -description = "Hanzo Web3 - Enterprise Blockchain Infrastructure SDK" -authors = [ - {name = "Hanzo AI", email = "dev@hanzo.ai"}, -] -dependencies = [ - "httpx>=0.23.0", - "pydantic>=2.0.0", - "web3>=6.0.0", -] -readme = "README.md" -requires-python = ">=3.12" -keywords = ["blockchain", "web3", "ethereum", "rpc", "nft", "tokens", "hanzo", "erc4337"] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Internet :: WWW/HTTP", -] - -[project.optional-dependencies] -async = ["aiohttp>=3.9.0"] -all = [ - "aiohttp>=3.9.0", - "websockets>=12.0", -] - -[project.urls] -Homepage = "https://web3.hanzo.ai" -Repository = "https://github.com/hanzoai/python-sdk" -Documentation = "https://docs.web3.hanzo.ai" -"Bug Tracker" = "https://github.com/hanzoai/python-sdk/issues" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build] -include = [ - "src/hanzo_web3", -] - -[tool.hatch.build.targets.wheel] -packages = ["src/hanzo_web3"] diff --git a/pkg/hanzo-web3/src/hanzo_web3/__init__.py b/pkg/hanzo-web3/src/hanzo_web3/__init__.py deleted file mode 100644 index 692993cfe..000000000 --- a/pkg/hanzo-web3/src/hanzo_web3/__init__.py +++ /dev/null @@ -1,46 +0,0 @@ -"""Hanzo Web3 SDK - Enterprise Blockchain Infrastructure. - -Multi-chain RPC, Token APIs, NFT APIs, Smart Wallets, Webhooks, and more. - -Example: - >>> from hanzo_web3 import Client - >>> client = Client(api_key="hz_live_...") - >>> balance = await client.tokens.get_balance("0x...", chain="ethereum") -""" - -from hanzo_web3.types import ( - Chain, - Network, - Webhook, - NFTMetadata, - SmartWallet, - Transaction, - TokenBalance, -) -from hanzo_web3.client import Client, AsyncClient -from hanzo_web3.exceptions import ( - HanzoWeb3Error, - RateLimitError, - AuthenticationError, - ChainNotSupportedError, -) - -__version__ = "0.1.0" -__all__ = [ - # Clients - "Client", - "AsyncClient", - # Types - "Chain", - "Network", - "TokenBalance", - "NFTMetadata", - "Transaction", - "Webhook", - "SmartWallet", - # Exceptions - "HanzoWeb3Error", - "AuthenticationError", - "RateLimitError", - "ChainNotSupportedError", -] diff --git a/pkg/hanzo-web3/src/hanzo_web3/client.py b/pkg/hanzo-web3/src/hanzo_web3/client.py deleted file mode 100644 index 8b48267f9..000000000 --- a/pkg/hanzo-web3/src/hanzo_web3/client.py +++ /dev/null @@ -1,489 +0,0 @@ -"""Hanzo Web3 Client - Sync and Async clients for blockchain APIs.""" - -from __future__ import annotations - -import os -from typing import Any, Optional - -import httpx -from pydantic import BaseModel - -from hanzo_web3.types import Chain, Network -from hanzo_web3.exceptions import HanzoWeb3Error, AuthenticationError - - -class ClientConfig(BaseModel): - """Client configuration.""" - - api_key: str - base_url: str = "https://api.web3.hanzo.ai" - timeout: float = 30.0 - max_retries: int = 3 - - -class RPCClient: - """JSON-RPC client for blockchain interactions.""" - - def __init__(self, http_client: httpx.AsyncClient, config: ClientConfig): - self._http = http_client - self._config = config - - async def call( - self, - method: str, - params: list[Any] | None = None, - chain: str = "ethereum", - network: str = "mainnet", - ) -> Any: - """Execute JSON-RPC call. - - Args: - method: RPC method name (e.g., "eth_blockNumber") - params: RPC parameters - chain: Chain name (ethereum, polygon, arbitrum, etc.) - network: Network name (mainnet, sepolia, etc.) - - Returns: - RPC result - """ - response = await self._http.post( - f"{self._config.base_url}/v1/rpc/{chain}/{network}", - json={ - "jsonrpc": "2.0", - "id": 1, - "method": method, - "params": params or [], - }, - headers={"X-API-Key": self._config.api_key}, - ) - - if response.status_code == 401: - raise AuthenticationError("Invalid API key") - - data = response.json() - if "error" in data: - raise HanzoWeb3Error(f"RPC error: {data['error']}") - - return data.get("result") - - async def eth_block_number( - self, chain: str = "ethereum", network: str = "mainnet" - ) -> int: - """Get latest block number.""" - result = await self.call("eth_blockNumber", chain=chain, network=network) - return int(result, 16) - - async def eth_get_balance( - self, address: str, chain: str = "ethereum", network: str = "mainnet" - ) -> int: - """Get ETH balance for address.""" - result = await self.call( - "eth_getBalance", [address, "latest"], chain=chain, network=network - ) - return int(result, 16) - - -class TokensClient: - """Token API client.""" - - def __init__(self, http_client: httpx.AsyncClient, config: ClientConfig): - self._http = http_client - self._config = config - - async def get_balances( - self, - address: str, - chain: str = "ethereum", - network: str = "mainnet", - ) -> list[dict[str, Any]]: - """Get all token balances for address. - - Args: - address: Wallet address - chain: Chain name - network: Network name - - Returns: - List of token balances - """ - response = await self._http.get( - f"{self._config.base_url}/v1/tokens/{chain}/{network}/balances/{address}", - headers={"X-API-Key": self._config.api_key}, - ) - return response.json().get("balances", []) - - async def get_metadata( - self, - contract_address: str, - chain: str = "ethereum", - network: str = "mainnet", - ) -> dict[str, Any]: - """Get token metadata. - - Args: - contract_address: Token contract address - chain: Chain name - network: Network name - - Returns: - Token metadata (name, symbol, decimals, etc.) - """ - response = await self._http.get( - f"{self._config.base_url}/v1/tokens/{chain}/{network}/metadata/{contract_address}", - headers={"X-API-Key": self._config.api_key}, - ) - return response.json() - - -class NFTsClient: - """NFT API client.""" - - def __init__(self, http_client: httpx.AsyncClient, config: ClientConfig): - self._http = http_client - self._config = config - - async def get_owned( - self, - address: str, - chain: str = "ethereum", - network: str = "mainnet", - ) -> list[dict[str, Any]]: - """Get all NFTs owned by address. - - Args: - address: Wallet address - chain: Chain name - network: Network name - - Returns: - List of owned NFTs - """ - response = await self._http.get( - f"{self._config.base_url}/v1/nfts/{chain}/{network}/owned/{address}", - headers={"X-API-Key": self._config.api_key}, - ) - return response.json().get("nfts", []) - - async def get_metadata( - self, - contract_address: str, - token_id: str, - chain: str = "ethereum", - network: str = "mainnet", - ) -> dict[str, Any]: - """Get NFT metadata. - - Args: - contract_address: NFT contract address - token_id: Token ID - chain: Chain name - network: Network name - - Returns: - NFT metadata - """ - response = await self._http.get( - f"{self._config.base_url}/v1/nfts/{chain}/{network}/metadata/{contract_address}/{token_id}", - headers={"X-API-Key": self._config.api_key}, - ) - return response.json() - - -class WalletsClient: - """Smart Wallet (ERC-4337) client.""" - - def __init__(self, http_client: httpx.AsyncClient, config: ClientConfig): - self._http = http_client - self._config = config - - async def create( - self, - owner: str, - chain: str = "ethereum", - network: str = "mainnet", - ) -> dict[str, Any]: - """Create a new smart wallet. - - Args: - owner: Owner EOA address - chain: Chain name - network: Network name - - Returns: - Smart wallet details (address, etc.) - """ - response = await self._http.post( - f"{self._config.base_url}/v1/wallets/{chain}/{network}/create", - json={"owner": owner}, - headers={"X-API-Key": self._config.api_key}, - ) - return response.json() - - async def get( - self, - address: str, - chain: str = "ethereum", - network: str = "mainnet", - ) -> dict[str, Any]: - """Get smart wallet details. - - Args: - address: Smart wallet address - chain: Chain name - network: Network name - - Returns: - Wallet details - """ - response = await self._http.get( - f"{self._config.base_url}/v1/wallets/{chain}/{network}/{address}", - headers={"X-API-Key": self._config.api_key}, - ) - return response.json() - - -class WebhooksClient: - """Webhooks client.""" - - def __init__(self, http_client: httpx.AsyncClient, config: ClientConfig): - self._http = http_client - self._config = config - - async def create( - self, - url: str, - event_type: str, - chain: str = "ethereum", - network: str = "mainnet", - filters: dict[str, Any] | None = None, - ) -> dict[str, Any]: - """Create a webhook. - - Args: - url: Webhook URL - event_type: Event type (ADDRESS_ACTIVITY, NFT_ACTIVITY, etc.) - chain: Chain name - network: Network name - filters: Event filters - - Returns: - Webhook details - """ - response = await self._http.post( - f"{self._config.base_url}/v1/webhooks", - json={ - "url": url, - "event_type": event_type, - "chain": chain, - "network": network, - "filters": filters or {}, - }, - headers={"X-API-Key": self._config.api_key}, - ) - return response.json() - - async def list(self) -> list[dict[str, Any]]: - """List all webhooks. - - Returns: - List of webhooks - """ - response = await self._http.get( - f"{self._config.base_url}/v1/webhooks", - headers={"X-API-Key": self._config.api_key}, - ) - return response.json().get("webhooks", []) - - async def delete(self, webhook_id: str) -> bool: - """Delete a webhook. - - Args: - webhook_id: Webhook ID - - Returns: - True if deleted - """ - response = await self._http.delete( - f"{self._config.base_url}/v1/webhooks/{webhook_id}", - headers={"X-API-Key": self._config.api_key}, - ) - return response.status_code == 204 - - -class AsyncClient: - """Async Hanzo Web3 client. - - Example: - >>> client = AsyncClient(api_key="hz_live_...") - >>> balance = await client.tokens.get_balances("0x...") - """ - - def __init__( - self, - api_key: Optional[str] = None, - base_url: str = "https://api.web3.hanzo.ai", - timeout: float = 30.0, - ): - """Initialize async client. - - Args: - api_key: API key (or set HANZO_WEB3_API_KEY env var) - base_url: API base URL - timeout: Request timeout in seconds - """ - api_key = api_key or os.environ.get("HANZO_WEB3_API_KEY") - if not api_key: - raise AuthenticationError( - "API key required. Pass api_key or set HANZO_WEB3_API_KEY env var." - ) - - self._config = ClientConfig( - api_key=api_key, - base_url=base_url, - timeout=timeout, - ) - self._http = httpx.AsyncClient(timeout=timeout) - - # Initialize sub-clients - self.rpc = RPCClient(self._http, self._config) - self.tokens = TokensClient(self._http, self._config) - self.nfts = NFTsClient(self._http, self._config) - self.wallets = WalletsClient(self._http, self._config) - self.webhooks = WebhooksClient(self._http, self._config) - - async def __aenter__(self) -> "AsyncClient": - return self - - async def __aexit__(self, *args: Any) -> None: - await self._http.aclose() - - async def close(self) -> None: - """Close the client.""" - await self._http.aclose() - - -class Client: - """Sync Hanzo Web3 client (wrapper around AsyncClient). - - Example: - >>> client = Client(api_key="hz_live_...") - >>> balance = client.rpc.eth_block_number() - """ - - def __init__( - self, - api_key: Optional[str] = None, - base_url: str = "https://api.web3.hanzo.ai", - timeout: float = 30.0, - ): - """Initialize sync client. - - Args: - api_key: API key (or set HANZO_WEB3_API_KEY env var) - base_url: API base URL - timeout: Request timeout in seconds - """ - import asyncio - - self._async_client = AsyncClient( - api_key=api_key, - base_url=base_url, - timeout=timeout, - ) - self._loop = asyncio.new_event_loop() - - def _run(self, coro: Any) -> Any: - """Run async coroutine synchronously.""" - return self._loop.run_until_complete(coro) - - @property - def rpc(self) -> "SyncRPCClient": - return SyncRPCClient(self._async_client.rpc, self._run) - - @property - def tokens(self) -> "SyncTokensClient": - return SyncTokensClient(self._async_client.tokens, self._run) - - @property - def nfts(self) -> "SyncNFTsClient": - return SyncNFTsClient(self._async_client.nfts, self._run) - - @property - def wallets(self) -> "SyncWalletsClient": - return SyncWalletsClient(self._async_client.wallets, self._run) - - @property - def webhooks(self) -> "SyncWebhooksClient": - return SyncWebhooksClient(self._async_client.webhooks, self._run) - - def close(self) -> None: - """Close the client.""" - self._run(self._async_client.close()) - self._loop.close() - - -# Sync wrapper classes -class SyncRPCClient: - def __init__(self, async_client: RPCClient, runner: Any): - self._async = async_client - self._run = runner - - def call(self, method: str, params: list | None = None, **kwargs: Any) -> Any: - return self._run(self._async.call(method, params, **kwargs)) - - def eth_block_number(self, **kwargs: Any) -> int: - return self._run(self._async.eth_block_number(**kwargs)) - - def eth_get_balance(self, address: str, **kwargs: Any) -> int: - return self._run(self._async.eth_get_balance(address, **kwargs)) - - -class SyncTokensClient: - def __init__(self, async_client: TokensClient, runner: Any): - self._async = async_client - self._run = runner - - def get_balances(self, address: str, **kwargs: Any) -> list: - return self._run(self._async.get_balances(address, **kwargs)) - - def get_metadata(self, contract_address: str, **kwargs: Any) -> dict: - return self._run(self._async.get_metadata(contract_address, **kwargs)) - - -class SyncNFTsClient: - def __init__(self, async_client: NFTsClient, runner: Any): - self._async = async_client - self._run = runner - - def get_owned(self, address: str, **kwargs: Any) -> list: - return self._run(self._async.get_owned(address, **kwargs)) - - def get_metadata(self, contract_address: str, token_id: str, **kwargs: Any) -> dict: - return self._run(self._async.get_metadata(contract_address, token_id, **kwargs)) - - -class SyncWalletsClient: - def __init__(self, async_client: WalletsClient, runner: Any): - self._async = async_client - self._run = runner - - def create(self, owner: str, **kwargs: Any) -> dict: - return self._run(self._async.create(owner, **kwargs)) - - def get(self, address: str, **kwargs: Any) -> dict: - return self._run(self._async.get(address, **kwargs)) - - -class SyncWebhooksClient: - def __init__(self, async_client: WebhooksClient, runner: Any): - self._async = async_client - self._run = runner - - def create(self, url: str, event_type: str, **kwargs: Any) -> dict: - return self._run(self._async.create(url, event_type, **kwargs)) - - def list(self) -> list: - return self._run(self._async.list()) - - def delete(self, webhook_id: str) -> bool: - return self._run(self._async.delete(webhook_id)) diff --git a/pkg/hanzo-web3/src/hanzo_web3/exceptions.py b/pkg/hanzo-web3/src/hanzo_web3/exceptions.py deleted file mode 100644 index 7d5987c3d..000000000 --- a/pkg/hanzo-web3/src/hanzo_web3/exceptions.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Hanzo Web3 SDK exceptions.""" - - -class HanzoWeb3Error(Exception): - """Base exception for Hanzo Web3 SDK.""" - - pass - - -class AuthenticationError(HanzoWeb3Error): - """Invalid or missing API key.""" - - pass - - -class RateLimitError(HanzoWeb3Error): - """Rate limit exceeded.""" - - def __init__( - self, - message: str = "Rate limit exceeded", - retry_after: int | None = None, - ): - super().__init__(message) - self.retry_after = retry_after - - -class ChainNotSupportedError(HanzoWeb3Error): - """Chain or network not supported.""" - - def __init__(self, chain: str, network: str | None = None): - msg = f"Chain '{chain}' not supported" - if network: - msg = f"Network '{network}' on chain '{chain}' not supported" - super().__init__(msg) - self.chain = chain - self.network = network - - -class QuotaExceededError(HanzoWeb3Error): - """Compute unit quota exceeded.""" - - def __init__( - self, - message: str = "Compute unit quota exceeded", - current_usage: int | None = None, - limit: int | None = None, - ): - super().__init__(message) - self.current_usage = current_usage - self.limit = limit - - -class RPCError(HanzoWeb3Error): - """JSON-RPC error from the node.""" - - def __init__(self, code: int, message: str, data: str | None = None): - super().__init__(f"RPC Error {code}: {message}") - self.code = code - self.message = message - self.data = data - - -class WebhookError(HanzoWeb3Error): - """Webhook configuration or delivery error.""" - - pass - - -class WalletError(HanzoWeb3Error): - """Smart wallet operation error.""" - - pass - - -class TransactionError(HanzoWeb3Error): - """Transaction submission or execution error.""" - - def __init__(self, message: str, tx_hash: str | None = None): - super().__init__(message) - self.tx_hash = tx_hash diff --git a/pkg/hanzo-web3/src/hanzo_web3/types.py b/pkg/hanzo-web3/src/hanzo_web3/types.py deleted file mode 100644 index 6f98712b8..000000000 --- a/pkg/hanzo-web3/src/hanzo_web3/types.py +++ /dev/null @@ -1,144 +0,0 @@ -"""Hanzo Web3 SDK types.""" - -from __future__ import annotations - -from enum import Enum -from typing import Any, Optional - -from pydantic import BaseModel - - -class Chain(str, Enum): - """Supported blockchains.""" - - ETHEREUM = "ethereum" - POLYGON = "polygon" - ARBITRUM = "arbitrum" - OPTIMISM = "optimism" - BASE = "base" - AVALANCHE = "avalanche" - BNB = "bnb" - LUX = "lux" - SOLANA = "solana" - BITCOIN = "bitcoin" - - -class Network(str, Enum): - """Network types.""" - - MAINNET = "mainnet" - TESTNET = "testnet" - SEPOLIA = "sepolia" - GOERLI = "goerli" - HOLESKY = "holesky" - DEVNET = "devnet" - - -class TokenBalance(BaseModel): - """Token balance.""" - - contract_address: str - symbol: str - name: str - decimals: int - balance: str - balance_formatted: float - logo_uri: Optional[str] = None - price_usd: Optional[float] = None - value_usd: Optional[float] = None - - -class NFTMetadata(BaseModel): - """NFT metadata.""" - - contract_address: str - token_id: str - name: Optional[str] = None - description: Optional[str] = None - image_uri: Optional[str] = None - animation_uri: Optional[str] = None - external_uri: Optional[str] = None - attributes: list[dict[str, Any]] = [] - token_standard: str = "ERC721" - - -class Transaction(BaseModel): - """Transaction details.""" - - hash: str - block_number: int - block_hash: str - from_address: str - to_address: Optional[str] = None - value: str - gas: int - gas_price: str - gas_used: Optional[int] = None - nonce: int - status: int = 1 # 1 = success, 0 = failed - timestamp: Optional[int] = None - - -class Webhook(BaseModel): - """Webhook configuration.""" - - id: str - url: str - event_type: str - chain: str - network: str - filters: dict[str, Any] = {} - active: bool = True - created_at: str - - -class WebhookEventType(str, Enum): - """Webhook event types.""" - - ADDRESS_ACTIVITY = "ADDRESS_ACTIVITY" - MINED_TRANSACTION = "MINED_TRANSACTION" - NFT_ACTIVITY = "NFT_ACTIVITY" - TOKEN_TRANSFER = "TOKEN_TRANSFER" - INTERNAL_TRANSFER = "INTERNAL_TRANSFER" - NEW_BLOCK = "NEW_BLOCK" - - -class SmartWallet(BaseModel): - """Smart wallet (ERC-4337).""" - - address: str - owner: str - chain: str - network: str - factory: str - is_deployed: bool = False - nonce: int = 0 - created_at: str - - -class UserOperation(BaseModel): - """ERC-4337 User Operation.""" - - sender: str - nonce: str - init_code: str = "0x" - call_data: str - call_gas_limit: str - verification_gas_limit: str - pre_verification_gas: str - max_fee_per_gas: str - max_priority_fee_per_gas: str - paymaster_and_data: str = "0x" - signature: str = "0x" - - -class GasEstimate(BaseModel): - """Gas price estimate.""" - - chain: str - network: str - base_fee: str - max_fee: str - max_priority_fee: str - gas_price: str # Legacy gas price - timestamp: int diff --git a/pkg/hanzo-zap/hanzo_zap/__init__.py b/pkg/hanzo-zap/hanzo_zap/__init__.py deleted file mode 100644 index c0c1615aa..000000000 --- a/pkg/hanzo-zap/hanzo_zap/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -hanzo-zap - Zero-copy Agent Protocol SDK for Python - -1000x faster than MCP/JSON-RPC through binary wire protocol. -Includes PlaygroundClient for the Hanzo Playground control plane. - -Example: - >>> from hanzo_zap import ZapClient - >>> async with ZapClient.connect("zap://localhost:9999") as client: - ... tools = await client.list_tools() - ... result = await client.call_tool("read_file", {"path": "README.md"}) -""" - -from .types import ( - AgentEvent, - AgentInfo, - ApprovalPolicy, - ClientInfo, - CommitInfo, - EventMsg, - FileChange, - MessageType, - RealtimeAudioFrame, - SandboxPolicy, - ServerInfo, - Submission, - Tool, - ToolCall, - ToolResult, -) -from .client import ZapClient -from .server import ZapServer -from .cloud import CloudClient -from .playground import PlaygroundClient - -__version__ = "0.7.0" -__all__ = [ - # Wire protocol - "ZapClient", - "ZapServer", - # Cloud (luxfi/zap binary protocol โ€” compatible with Rust hanzo-zap) - "CloudClient", - # Playground - "PlaygroundClient", - # Core types - "ApprovalPolicy", - "ClientInfo", - "MessageType", - "SandboxPolicy", - "ServerInfo", - "Tool", - "ToolCall", - "ToolResult", - # Playground types - "AgentEvent", - "AgentInfo", - "CommitInfo", - "EventMsg", - "FileChange", - "RealtimeAudioFrame", - "Submission", -] diff --git a/pkg/hanzo-zap/hanzo_zap/client.py b/pkg/hanzo-zap/hanzo_zap/client.py deleted file mode 100644 index 9c260396a..000000000 --- a/pkg/hanzo-zap/hanzo_zap/client.py +++ /dev/null @@ -1,168 +0,0 @@ -""" -ZAP Client implementation. -""" - -from __future__ import annotations - -import asyncio -import json -import ssl -import struct -from typing import Any -from urllib.parse import urlparse - -from .types import ( - ClientInfo, - MessageType, - Resource, - ServerInfo, - Tool, - ToolCall, - ToolResult, -) - -MAX_MESSAGE_SIZE = 16 * 1024 * 1024 # 16MB - - -class ZapClient: - """ - ZAP Client for connecting to ZAP servers. - - Example: - >>> async with ZapClient.connect("zap://localhost:9999") as client: - ... tools = await client.list_tools() - ... result = await client.call_tool("read_file", {"path": "README.md"}) - """ - - def __init__( - self, - reader: asyncio.StreamReader, - writer: asyncio.StreamWriter, - ) -> None: - self._reader = reader - self._writer = writer - self._server_info: ServerInfo | None = None - self._request_id = 0 - - @classmethod - async def connect(cls, url: str) -> ZapClient: - """ - Connect to a ZAP server. - - Args: - url: Server URL (zap:// or zaps:// for TLS) - - Returns: - Connected ZapClient instance - """ - parsed = urlparse(url) - use_tls = parsed.scheme == "zaps" - host = parsed.hostname or "localhost" - port = parsed.port or 9999 - - ssl_ctx = ssl.create_default_context() if use_tls else None - - reader, writer = await asyncio.open_connection(host, port, ssl=ssl_ctx) - client = cls(reader, writer) - await client._handshake() - return client - - async def _handshake(self) -> None: - """Perform protocol handshake.""" - client_info = ClientInfo(name="hanzo-zap", version="0.6.1") - await self._send(MessageType.INIT, client_info.__dict__) - msg_type, payload = await self._recv() - - if msg_type != MessageType.INIT_ACK: - raise ConnectionError(f"Expected INIT_ACK, got {msg_type}") - - self._server_info = ServerInfo(**payload) - - @property - def server_info(self) -> ServerInfo | None: - """Get server info from handshake.""" - return self._server_info - - async def list_tools(self) -> list[Tool]: - """List available tools.""" - await self._send(MessageType.LIST_TOOLS, {}) - _, payload = await self._recv() - return [Tool(**t) for t in payload] - - async def call_tool(self, name: str, args: dict[str, Any]) -> ToolResult: - """Call a tool by name.""" - self._request_id += 1 - call = ToolCall(id=f"req-{self._request_id}", name=name, args=args) - await self._send(MessageType.CALL_TOOL, call.__dict__) - _, payload = await self._recv() - return ToolResult(**payload) - - async def batch( - self, calls: list[dict[str, Any]] - ) -> list[ToolResult]: - """Call multiple tools in a batch.""" - return [await self.call_tool(c["name"], c["args"]) for c in calls] - - async def list_resources(self) -> list[Resource]: - """List available resources.""" - await self._send(MessageType.LIST_RESOURCES, {}) - _, payload = await self._recv() - return [Resource(**r) for r in payload] - - async def read_resource(self, uri: str) -> dict[str, Any]: - """Read a resource by URI.""" - await self._send(MessageType.READ_RESOURCE, {"uri": uri}) - _, payload = await self._recv() - return payload - - async def ping(self) -> None: - """Send ping to check connection.""" - await self._send(MessageType.PING, {}) - msg_type, _ = await self._recv() - if msg_type != MessageType.PONG: - raise ConnectionError(f"Expected PONG, got {msg_type}") - - async def close(self) -> None: - """Close the connection.""" - self._writer.close() - await self._writer.wait_closed() - - async def __aenter__(self) -> ZapClient: - return self - - async def __aexit__(self, *args: Any) -> None: - await self.close() - - async def _send(self, msg_type: MessageType, payload: dict[str, Any]) -> None: - """Send a message with ZAP wire format.""" - payload_bytes = json.dumps(payload).encode("utf-8") if payload else b"" - total_len = 1 + len(payload_bytes) - - # Header: 4-byte LE length + 1-byte message type - header = struct.pack(" tuple[MessageType, Any]: - """Receive and parse a message.""" - # Read header - header = await self._reader.readexactly(5) - total_len, msg_type_byte = struct.unpack(" MAX_MESSAGE_SIZE: - raise ValueError(f"Message too large: {total_len}") - - # Read payload - payload_len = total_len - 1 - if payload_len > 0: - payload_bytes = await self._reader.readexactly(payload_len) - payload = json.loads(payload_bytes.decode("utf-8")) - else: - payload = {} - - msg_type = MessageType(msg_type_byte) - - if msg_type == MessageType.ERROR: - raise RuntimeError(payload.get("message", "Server error")) - - return msg_type, payload diff --git a/pkg/hanzo-zap/hanzo_zap/cloud.py b/pkg/hanzo-zap/hanzo_zap/cloud.py deleted file mode 100644 index 379334fbd..000000000 --- a/pkg/hanzo-zap/hanzo_zap/cloud.py +++ /dev/null @@ -1,175 +0,0 @@ -""" -ZAP Cloud Client โ€” speaks the luxfi/zap binary wire protocol. - -Connects to Hanzo Node (port 3692) or Engine via native binary transport. -Compatible with the Rust `hanzo-zap` crate server. - -Example: - >>> from hanzo_zap import CloudClient - >>> async with CloudClient.connect("localhost:3692") as client: - ... status, body, error = await client.call("chat.completions", "", body_bytes) -""" - -from __future__ import annotations - -import asyncio -import json -import ssl -import struct -from typing import Any - -from .wire import ( - REQ_FLAG_REQ, - REQ_FLAG_RESP, - Message, - build_cloud_request, - build_handshake, - parse_cloud_response, - parse_handshake, - read_frame, - write_frame, -) - -DEFAULT_ENDPOINT = "localhost:3692" -CLIENT_NODE_ID = "python-sdk" - - -class CloudClient: - """ - A client that speaks the luxfi/zap binary wire protocol. - - Supports both plain TCP (localhost) and TLS (remote endpoints). - """ - - def __init__( - self, - reader: asyncio.StreamReader, - writer: asyncio.StreamWriter, - peer_id: str, - ) -> None: - self._reader = reader - self._writer = writer - self._peer_id = peer_id - self._req_id = 0 - - @classmethod - async def connect( - cls, - endpoint: str | None = None, - *, - use_tls: bool | None = None, - node_id: str = CLIENT_NODE_ID, - ) -> "CloudClient": - """ - Connect to a ZAP endpoint, perform handshake. - - Args: - endpoint: "host:port" (default: localhost:3692) - use_tls: Force TLS on/off. None = auto-detect (TLS for non-localhost). - node_id: Client node ID for handshake. - """ - addr = endpoint or DEFAULT_ENDPOINT - host, _, port_str = addr.rpartition(":") - if not host: - host = addr - port_str = "3692" - port = int(port_str) - - # Auto-detect TLS: skip for localhost - if use_tls is None: - use_tls = host not in ("localhost", "127.0.0.1", "::1") - - ssl_ctx = None - if use_tls: - ssl_ctx = ssl.create_default_context() - - reader, writer = await asyncio.open_connection(host, port, ssl=ssl_ctx) - - # Send handshake - hs_bytes = build_handshake(node_id) - await write_frame(writer, hs_bytes) - - # Read handshake response - resp_data = await read_frame(reader) - resp_msg = Message.parse(resp_data) - peer_id = parse_handshake(resp_msg) - - return cls(reader, writer, peer_id) - - @property - def peer_id(self) -> str: - return self._peer_id - - async def call( - self, - method: str, - auth: str, - body: bytes, - ) -> tuple[int, bytes, str]: - """ - Send a MsgType 100 cloud service request and return (status, body, error). - """ - self._req_id = (self._req_id + 1) & 0xFFFFFFFF - req_id = self._req_id - - # Build ZAP message - msg_bytes = build_cloud_request(method, auth, body) - - # Wrap with 8-byte Call correlation header - wrapped = struct.pack(" dict[str, Any]: - """ - High-level: send an OpenAI-compatible chat completion request via ZAP. - - Returns the parsed JSON response dict. - """ - request_body: dict[str, Any] = {"model": model, "messages": messages} - request_body.update(kwargs) - - body_bytes = json.dumps(request_body).encode("utf-8") - auth = f"Bearer {auth_token}" if auth_token and not auth_token.startswith("Bearer ") else auth_token - - status, resp_body, error = await self.call("chat.completions", auth, body_bytes) - - if status != 200: - err_msg = error or resp_body.decode("utf-8", errors="replace") or f"ZAP status {status}" - raise RuntimeError(f"ZAP cloud error: {err_msg}") - - return json.loads(resp_body) - - async def close(self) -> None: - """Close the connection.""" - self._writer.close() - await self._writer.wait_closed() - - async def __aenter__(self) -> "CloudClient": - return self - - async def __aexit__(self, *args: Any) -> None: - await self.close() diff --git a/pkg/hanzo-zap/hanzo_zap/playground.py b/pkg/hanzo-zap/hanzo_zap/playground.py deleted file mode 100644 index cd38b58fc..000000000 --- a/pkg/hanzo-zap/hanzo_zap/playground.py +++ /dev/null @@ -1,182 +0,0 @@ -""" -Playground control plane client. - -Connects to the playground REST API for agent management, -event streaming, git operations, and human injection. -""" - -from __future__ import annotations - -import json -from typing import Any, AsyncIterator - -import httpx - - -class PlaygroundClient: - """Client for the Hanzo Playground control plane.""" - - def __init__( - self, - base_url: str = "http://localhost:8080", - token: str | None = None, - ) -> None: - self.base_url = base_url.rstrip("/") - self._client = httpx.AsyncClient( - base_url=self.base_url, - headers={"Authorization": f"Bearer {token}"} if token else {}, - timeout=30.0, - ) - - # --- Agent Discovery --- - - async def discover_agents(self, space_id: str) -> list[dict[str, Any]]: - """Discover agents in a space via gossip tracker.""" - resp = await self._client.get( - f"/api/v1/spaces/{space_id}/agents/discover" - ) - resp.raise_for_status() - return resp.json().get("agents") or [] - - # --- Agent Events (SSE) --- - - async def stream_events( - self, space_id: str - ) -> AsyncIterator[dict[str, Any]]: - """Stream all agent events in a space via SSE.""" - async with self._client.stream( - "GET", f"/api/v1/spaces/{space_id}/agents/events" - ) as resp: - async for line in resp.aiter_lines(): - if line.startswith("data: "): - try: - yield json.loads(line[6:]) - except json.JSONDecodeError: - continue - - async def stream_agent_events( - self, space_id: str, agent_id: str - ) -> AsyncIterator[dict[str, Any]]: - """Stream events for a specific agent via SSE.""" - async with self._client.stream( - "GET", - f"/api/v1/spaces/{space_id}/agents/{agent_id}/events", - ) as resp: - async for line in resp.aiter_lines(): - if line.startswith("data: "): - try: - yield json.loads(line[6:]) - except json.JSONDecodeError: - continue - - # --- Human Injection --- - - async def inject_message( - self, - space_id: str, - agent_id: str, - message: str, - sender_name: str = "human", - ) -> dict[str, Any]: - """Send a human message to a specific agent.""" - resp = await self._client.post( - f"/api/v1/spaces/{space_id}/agents/{agent_id}/inject", - json={"message": message, "sender_name": sender_name}, - ) - resp.raise_for_status() - return resp.json() - - async def broadcast_message( - self, - space_id: str, - message: str, - sender_name: str = "human", - ) -> dict[str, Any]: - """Broadcast a human message to all agents in a space.""" - resp = await self._client.post( - f"/api/v1/spaces/{space_id}/agents/broadcast", - json={"message": message, "sender_name": sender_name}, - ) - resp.raise_for_status() - return resp.json() - - # --- Git Operations --- - - async def git_status(self, space_id: str) -> dict[str, Any]: - """Get git status for a space.""" - resp = await self._client.get( - f"/api/v1/spaces/{space_id}/git/status" - ) - resp.raise_for_status() - return resp.json() - - async def git_clone( - self, space_id: str, url: str, branch: str = "main" - ) -> dict[str, Any]: - """Clone a git repository into a space.""" - resp = await self._client.post( - f"/api/v1/spaces/{space_id}/git/clone", - json={"url": url, "branch": branch}, - ) - resp.raise_for_status() - return resp.json() - - async def git_commit( - self, - space_id: str, - message: str, - files: list[str] | None = None, - author_name: str = "", - author_email: str = "", - ) -> dict[str, Any]: - """Create a git commit in a space.""" - body: dict[str, Any] = {"message": message} - if files: - body["files"] = files - if author_name: - body["author"] = {"name": author_name, "email": author_email} - resp = await self._client.post( - f"/api/v1/spaces/{space_id}/git/commit", json=body - ) - resp.raise_for_status() - return resp.json() - - async def git_log( - self, space_id: str, limit: int = 20 - ) -> list[dict[str, Any]]: - """Get git log for a space.""" - resp = await self._client.get( - f"/api/v1/spaces/{space_id}/git/log", params={"limit": limit} - ) - resp.raise_for_status() - return resp.json().get("commits", []) - - async def git_branches(self, space_id: str) -> list[dict[str, Any]]: - """List git branches in a space.""" - resp = await self._client.get( - f"/api/v1/spaces/{space_id}/git/branches" - ) - resp.raise_for_status() - return resp.json().get("branches", []) - - async def git_files( - self, space_id: str, path: str = "/" - ) -> list[dict[str, Any]]: - """Browse files in a space repository.""" - resp = await self._client.get( - f"/api/v1/spaces/{space_id}/git/files", params={"path": path} - ) - resp.raise_for_status() - return resp.json().get("files", []) - - # --- Lifecycle --- - - async def close(self) -> None: - """Close the underlying HTTP client.""" - await self._client.aclose() - - async def __aenter__(self) -> PlaygroundClient: - return self - - async def __aexit__(self, *args: Any) -> None: - await self.close() diff --git a/pkg/hanzo-zap/hanzo_zap/py.typed b/pkg/hanzo-zap/hanzo_zap/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzo-zap/hanzo_zap/server.py b/pkg/hanzo-zap/hanzo_zap/server.py deleted file mode 100644 index c5c1dd4db..000000000 --- a/pkg/hanzo-zap/hanzo_zap/server.py +++ /dev/null @@ -1,190 +0,0 @@ -""" -ZAP Server implementation. -""" - -from __future__ import annotations - -import asyncio -import json -import struct -from collections.abc import Awaitable, Callable -from typing import Any - -from .types import ( - MessageType, - ServerInfo, - Tool, - ToolResult, -) - -ToolHandler = Callable[[str, dict[str, Any]], Awaitable[Any] | Any] - - -class ZapServer: - """ - ZAP Server for hosting tools. - - Example: - >>> server = ZapServer(name="my-tools", version="1.0.0") - >>> @server.tool("greet", "Greet someone") - ... async def greet(name: str) -> str: - ... return f"Hello, {name}!" - >>> await server.serve(9999) - """ - - def __init__(self, name: str, version: str) -> None: - self._info = ServerInfo( - name=name, - version=version, - capabilities={"tools": True, "resources": False, "prompts": False}, - ) - self._tools: dict[str, tuple[Tool, ToolHandler]] = {} - self._server: asyncio.Server | None = None - - def register_tool( - self, - name: str, - description: str, - input_schema: dict[str, Any], - handler: ToolHandler, - ) -> None: - """Register a tool.""" - tool = Tool(name=name, description=description, input_schema=input_schema) - self._tools[name] = (tool, handler) - - def tool( - self, - name: str, - description: str, - input_schema: dict[str, Any] | None = None, - ) -> Callable[[ToolHandler], ToolHandler]: - """Decorator to register a tool.""" - - def decorator(handler: ToolHandler) -> ToolHandler: - self.register_tool( - name=name, - description=description, - input_schema=input_schema or {}, - handler=handler, - ) - return handler - - return decorator - - async def serve(self, port: int, host: str = "0.0.0.0") -> None: - """Start serving and block.""" - self._server = await asyncio.start_server( - self._handle_connection, host, port - ) - async with self._server: - await self._server.serve_forever() - - async def start(self, port: int, host: str = "0.0.0.0") -> None: - """Start serving in background.""" - self._server = await asyncio.start_server( - self._handle_connection, host, port - ) - await self._server.start_serving() - - async def stop(self) -> None: - """Stop the server.""" - if self._server: - self._server.close() - await self._server.wait_closed() - - async def _handle_connection( - self, - reader: asyncio.StreamReader, - writer: asyncio.StreamWriter, - ) -> None: - """Handle a client connection.""" - try: - while True: - # Read header - header = await reader.readexactly(5) - total_len, msg_type_byte = struct.unpack(" 10 * 1024 * 1024: - raise ValueError(f"Message too large: {total_len}") - payload_len = total_len - 1 - if payload_len > 0: - payload_bytes = await reader.readexactly(payload_len) - payload = json.loads(payload_bytes.decode("utf-8")) - else: - payload = {} - - msg_type = MessageType(msg_type_byte) - await self._handle_message(writer, msg_type, payload) - - except asyncio.IncompleteReadError: - pass # Connection closed cleanly - except Exception: - import logging - logging.getLogger(__name__).warning("ZAP connection error", exc_info=True) - finally: - writer.close() - await writer.wait_closed() - - async def _handle_message( - self, - writer: asyncio.StreamWriter, - msg_type: MessageType, - payload: dict[str, Any], - ) -> None: - """Handle a message from client.""" - try: - if msg_type == MessageType.INIT: - await self._send(writer, MessageType.INIT_ACK, self._info.__dict__) - - elif msg_type == MessageType.LIST_TOOLS: - tools = [t.__dict__ for t, _ in self._tools.values()] - await self._send(writer, MessageType.LIST_TOOLS_RESPONSE, tools) - - elif msg_type == MessageType.CALL_TOOL: - result = await self._execute_tool(payload) - await self._send(writer, MessageType.CALL_TOOL_RESPONSE, result.__dict__) - - elif msg_type == MessageType.PING: - await self._send(writer, MessageType.PONG, {}) - - else: - await self._send( - writer, - MessageType.ERROR, - {"message": f"Unknown message type: {msg_type}"}, - ) - - except Exception as e: - await self._send(writer, MessageType.ERROR, {"message": str(e)}) - - async def _execute_tool(self, call: dict[str, Any]) -> ToolResult: - """Execute a tool call.""" - name = call.get("name", "") - args = call.get("args", {}) - call_id = call.get("id", "") - - if name not in self._tools: - return ToolResult(id=call_id, content=None, error=f"Unknown tool: {name}") - - _, handler = self._tools[name] - try: - result = handler(name, args) - if asyncio.iscoroutine(result): - result = await result - return ToolResult(id=call_id, content=result) - except Exception as e: - return ToolResult(id=call_id, content=None, error=str(e)) - - async def _send( - self, - writer: asyncio.StreamWriter, - msg_type: MessageType, - payload: Any, - ) -> None: - """Send a message.""" - payload_bytes = json.dumps(payload).encode("utf-8") if payload else b"" - total_len = 1 + len(payload_bytes) - header = struct.pack(" "SandboxPolicy": - return cls(mode="danger-full-access") - - @classmethod - def read_only(cls) -> "SandboxPolicy": - return cls(mode="read-only") - - @classmethod - def workspace_write( - cls, - writable_roots: list[str] | None = None, - network_access: bool = False, - allow_git_writes: bool = False, - ) -> "SandboxPolicy": - return cls( - mode="workspace-write", - writable_roots=writable_roots or [], - network_access=network_access, - allow_git_writes=allow_git_writes, - ) - - -class MessageType(IntEnum): - """Wire protocol message types.""" - - # Handshake - INIT = 0x01 - INIT_ACK = 0x02 - - # Tools - LIST_TOOLS = 0x10 - LIST_TOOLS_RESPONSE = 0x11 - CALL_TOOL = 0x12 - CALL_TOOL_RESPONSE = 0x13 - - # Resources - LIST_RESOURCES = 0x20 - LIST_RESOURCES_RESPONSE = 0x21 - READ_RESOURCE = 0x22 - READ_RESOURCE_RESPONSE = 0x23 - - # Prompts - LIST_PROMPTS = 0x30 - LIST_PROMPTS_RESPONSE = 0x31 - GET_PROMPT = 0x32 - GET_PROMPT_RESPONSE = 0x33 - - # Control - PING = 0xF0 - PONG = 0xF1 - ERROR = 0xFF - - -@dataclass -class Tool: - """Tool definition.""" - - name: str - description: str - input_schema: dict[str, Any] = field(default_factory=dict) - - -@dataclass -class ToolCall: - """Tool call request.""" - - id: str - name: str - args: dict[str, Any] = field(default_factory=dict) - metadata: dict[str, Any] | None = None - - -@dataclass -class ToolResult: - """Tool execution result.""" - - id: str - content: Any = None - error: str | None = None - metadata: dict[str, Any] | None = None - - -@dataclass -class ServerInfo: - """Server capabilities and info.""" - - name: str - version: str - capabilities: dict[str, bool] = field( - default_factory=lambda: {"tools": True, "resources": False, "prompts": False} - ) - - -@dataclass -class ClientInfo: - """Client info sent during handshake.""" - - name: str - version: str - - -@dataclass -class Resource: - """Resource definition.""" - - uri: str - name: str - description: str | None = None - mime_type: str | None = None - - -@dataclass -class Prompt: - """Prompt definition.""" - - name: str - description: str | None = None - arguments: list[dict[str, Any]] | None = None - - -# --- Playground Protocol Types (SQ/EQ pattern from hanzo/dev) --- - - -@dataclass -class Submission: - """Submission Queue entry -- request from user/agent.""" - - id: str - op: dict[str, Any] # Op variant as dict with "type" discriminator - trace: dict[str, str] | None = None - - -@dataclass -class EventMsg: - """Event Queue entry -- response from agent runtime.""" - - type: str # "turn_started", "turn_completed", "agent_message", etc. - data: dict[str, Any] = field(default_factory=dict) - raw: bytes | None = None - - -@dataclass -class AgentEvent: - """Real-time event from an agent in a space.""" - - type: str - space_id: str - agent_id: str - agent_name: str = "" - timestamp: str = "" - data: dict[str, Any] = field(default_factory=dict) - - -@dataclass -class AgentInfo: - """Agent discovery info from gossip tracker.""" - - agent_id: str - did: str = "" - space_id: str = "" - display_name: str = "" - status: str = "offline" - capabilities: list[dict[str, Any]] = field(default_factory=list) - model: str = "" - - -@dataclass -class RealtimeAudioFrame: - """Audio frame for realtime conversation.""" - - data: str # base64 - sample_rate: int = 16000 - num_channels: int = 1 - - -@dataclass -class FileChange: - """Git file change.""" - - path: str - status: str # "added", "modified", "deleted" - - -@dataclass -class CommitInfo: - """Git commit info.""" - - hash: str - message: str - author: str - email: str - timestamp: str diff --git a/pkg/hanzo-zap/hanzo_zap/wire.py b/pkg/hanzo-zap/hanzo_zap/wire.py deleted file mode 100644 index f7d5c11a5..000000000 --- a/pkg/hanzo-zap/hanzo_zap/wire.py +++ /dev/null @@ -1,310 +0,0 @@ -""" -luxfi/zap binary wire protocol โ€” Python implementation. - -Compatible with the Rust `hanzo-zap` crate and Go `github.com/luxfi/zap` v0.2.0. - -Wire format: - Frame: [4-byte LE length][message bytes] - Message header (16 bytes): magic(4) + version(2) + flags(2) + root_offset(4) + size(4) - Object fields: inline primitives, (relOffset:i32 + length:u32) for text/bytes - relOffset is relative to the field's absolute position in the buffer. -""" - -from __future__ import annotations - -import struct - -# โ”€โ”€ Constants โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -ZAP_MAGIC = b"ZAP\x00" -HEADER_SIZE = 16 -VERSION = 1 -ALIGNMENT = 8 -MAX_MESSAGE_SIZE = 10 * 1024 * 1024 # 10 MB - -MSG_TYPE_CLOUD = 100 - -# Cloud request field byte offsets (each Text/Bytes = 8 bytes: relOffset + length) -CLOUD_REQ_METHOD = 0 -CLOUD_REQ_AUTH = 8 -CLOUD_REQ_BODY = 16 - -# Cloud response field byte offsets -# Layout: status(0:u32, 4 bytes) + body(4:Bytes, 8 bytes) + error(12:Text, 8 bytes) -CLOUD_RESP_STATUS = 0 # u32 inline (4 bytes) -CLOUD_RESP_BODY = 4 # (relOffset:i32 + length:u32) -CLOUD_RESP_ERROR = 12 # (relOffset:i32 + length:u32) - -# Call correlation flags -REQ_FLAG_REQ = 1 -REQ_FLAG_RESP = 2 - -# Handshake constants -HANDSHAKE_OBJ_SIZE = 64 -HANDSHAKE_ID_MAX = 60 -HANDSHAKE_ID_LEN_OFFSET = 60 - - -# โ”€โ”€ Message โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -class Message: - """Parsed ZAP binary message (owns the full buffer including header).""" - - __slots__ = ("_data",) - - def __init__(self, data: bytes | bytearray) -> None: - self._data = bytes(data) - - @classmethod - def parse(cls, data: bytes | bytearray) -> "Message": - if len(data) < HEADER_SIZE: - raise ValueError(f"ZAP message too short: {len(data)} < {HEADER_SIZE}") - if data[:4] != ZAP_MAGIC: - raise ValueError(f"Bad ZAP magic: {data[:4]!r}") - ver = struct.unpack_from(" bytes: - return self._data - - @property - def version(self) -> int: - return struct.unpack_from(" int: - return struct.unpack_from(" int: - return self.flags >> 8 - - @property - def root_offset(self) -> int: - """Absolute offset of the root object in the buffer.""" - return struct.unpack_from(" int: - return struct.unpack_from(" int: - """Read a u32 inline field from object at obj_offset.""" - pos = obj_offset + field_offset - if pos + 4 > len(data): - return 0 - return struct.unpack_from(" bytes: - """Read a Bytes field (relOffset:i32 + length:u32) from object.""" - pos = obj_offset + field_offset - if pos + 8 > len(data): - return b"" - rel_off = struct.unpack_from(" len(data): - return b"" - return data[abs_off:abs_off + length] - - -def obj_text(data: bytes, obj_offset: int, field_offset: int) -> str: - """Read a Text field from object.""" - b = obj_bytes(data, obj_offset, field_offset) - return b.decode("utf-8", errors="replace") if b else "" - - -# โ”€โ”€ Builder (matches Rust Builder exactly) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -def _align(pos: int) -> int: - return (pos + ALIGNMENT - 1) & ~(ALIGNMENT - 1) - - -class Builder: - """Builds a ZAP message in a single buffer, matching Rust Builder.""" - - def __init__(self, capacity: int = 256) -> None: - cap = max(capacity, 256) - self._buf = bytearray(cap) - self._buf[:4] = ZAP_MAGIC - struct.pack_into(" None: - needed = self._pos + n - if needed <= len(self._buf): - return - new_cap = max(len(self._buf) * 2, needed) - self._buf.extend(b"\x00" * (new_cap - len(self._buf))) - - def _align_pos(self) -> None: - padding = (ALIGNMENT - (self._pos % ALIGNMENT)) % ALIGNMENT - self._grow(padding) - for _ in range(padding): - self._buf[self._pos] = 0 - self._pos += 1 - - def start_object(self, data_size: int) -> "ObjectBuilder": - self._align_pos() - return ObjectBuilder(self, self._pos, data_size) - - def finish(self, flags: int = 0) -> bytes: - struct.pack_into(" None: - self._builder = builder - self._start = start_pos - self._data_size = data_size - self._deferred: list[tuple[int, bytes]] = [] # (field_offset, data) - - def _ensure_field(self, end_offset: int) -> None: - needed = self._start + end_offset - if needed > self._builder._pos: - self._builder._grow(needed - self._builder._pos) - for i in range(self._builder._pos, needed): - self._builder._buf[i] = 0 - self._builder._pos = needed - - def set_u32(self, field_offset: int, v: int) -> None: - self._ensure_field(field_offset + 4) - struct.pack_into(" None: - self._ensure_field(field_offset + 1) - self._builder._buf[self._start + field_offset] = v & 0xFF - - def set_bytes(self, field_offset: int, data: bytes) -> None: - self._ensure_field(field_offset + 8) - pos = self._start + field_offset - if not data: - struct.pack_into(" None: - self.set_bytes(field_offset, text.encode("utf-8")) - - def finish_as_root(self) -> None: - """Finalize and set as root object.""" - self._ensure_field(self._data_size) - for field_offset, data in self._deferred: - data_pos = self._builder._pos - self._builder._grow(len(data)) - start = self._builder._pos - self._builder._buf[start:start + len(data)] = data - self._builder._pos += len(data) - field_abs = self._start + field_offset - rel_offset = data_pos - field_abs - struct.pack_into(" bytes: - """Build a MsgType 100 cloud service request message.""" - b = Builder(len(body) + len(method) + len(auth) + 128) - obj = b.start_object(24) # 3 * 8 bytes - obj.set_text(CLOUD_REQ_METHOD, method) - obj.set_text(CLOUD_REQ_AUTH, auth) - obj.set_bytes(CLOUD_REQ_BODY, body) - obj.finish_as_root() - return b.finish(flags=MSG_TYPE_CLOUD << 8) - - -def build_cloud_response(status: int, body: bytes, error: str) -> bytes: - """Build a MsgType 100 cloud service response message.""" - b = Builder(len(body) + len(error) + 128) - obj = b.start_object(20) # u32(4) + Bytes(8) + Text(8) = 20 - obj.set_u32(CLOUD_RESP_STATUS, status) - obj.set_bytes(CLOUD_RESP_BODY, body) - obj.set_text(CLOUD_RESP_ERROR, error) - obj.finish_as_root() - return b.finish(flags=MSG_TYPE_CLOUD << 8) - - -def parse_cloud_request(msg: Message) -> tuple[str, str, bytes]: - """Parse a cloud request โ†’ (method, auth, body).""" - data = msg.bytes - off = msg.root_offset - method = obj_text(data, off, CLOUD_REQ_METHOD) - auth = obj_text(data, off, CLOUD_REQ_AUTH) - body = obj_bytes(data, off, CLOUD_REQ_BODY) - return method, auth, body - - -def parse_cloud_response(msg: Message) -> tuple[int, bytes, str]: - """Parse a cloud response โ†’ (status, body, error).""" - data = msg.bytes - off = msg.root_offset - status = obj_uint32(data, off, CLOUD_RESP_STATUS) - body = obj_bytes(data, off, CLOUD_RESP_BODY) - error = obj_text(data, off, CLOUD_RESP_ERROR) - return status, body, error - - -# โ”€โ”€ Handshake โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -def build_handshake(node_id: str) -> bytes: - """Build a handshake message (msg_type=0, 64-byte fixed object).""" - b = Builder(128) - obj = b.start_object(HANDSHAKE_OBJ_SIZE) - id_bytes = node_id.encode("utf-8")[:HANDSHAKE_ID_MAX] - for i, byte in enumerate(id_bytes): - obj.set_u8(i, byte) - obj.set_u32(HANDSHAKE_ID_LEN_OFFSET, len(id_bytes)) - obj.finish_as_root() - return b.finish() - - -def parse_handshake(msg: Message) -> str: - """Parse a handshake message โ†’ peer node ID.""" - data = msg.bytes - off = msg.root_offset - id_len = obj_uint32(data, off, HANDSHAKE_ID_LEN_OFFSET) - if id_len == 0: - return "" - start = off - end = start + min(id_len, HANDSHAKE_ID_MAX) - if end > len(data): - return "" - return data[start:end].decode("utf-8", errors="replace") - - -# โ”€โ”€ Frame I/O โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -async def read_frame(reader) -> bytes: - """Read a length-prefixed frame: [4-byte LE length][data].""" - len_buf = await reader.readexactly(4) - length = struct.unpack(" MAX_MESSAGE_SIZE: - raise ValueError(f"ZAP frame too large: {length}") - if length == 0: - return b"" - return await reader.readexactly(length) - - -async def write_frame(writer, data: bytes) -> None: - """Write a length-prefixed frame.""" - writer.write(struct.pack("=61.0.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "hanzo-zap" -version = "0.7.0" -description = "Zero-copy Agent Protocol (ZAP) SDK - 1000x faster than MCP" -readme = "README.md" -requires-python = ">=3.10" -license = { text = "MIT" } -authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Topic :: Software Development :: Libraries :: Python Modules", - "Typing :: Typed", -] -keywords = ["agent", "ai", "hanzo", "mcp", "protocol", "zap", "zero-copy"] -dependencies = [ - "httpx>=0.23.0", -] - -[project.urls] -Homepage = "https://github.com/hanzoai/python-sdk" -Repository = "https://github.com/hanzoai/python-sdk/tree/main/pkg/hanzo-zap" -Documentation = "https://hanzo.ai/docs/zap" - -[project.optional-dependencies] -crypto = ["luxcrypto>=0.1.0"] -dev = [ - "pytest>=7.0.0", - "pytest-asyncio>=0.26.0", -] - -[tool.setuptools.packages.find] -where = ["."] -include = ["hanzo_zap*"] - -[tool.setuptools.package-data] -hanzo_zap = ["py.typed"] diff --git a/pkg/hanzo-zap/tests/test_playground.py b/pkg/hanzo-zap/tests/test_playground.py deleted file mode 100644 index e4b54c2c4..000000000 --- a/pkg/hanzo-zap/tests/test_playground.py +++ /dev/null @@ -1,254 +0,0 @@ -"""Playground client and types test suite. - -Covers type construction, serialization, PlaygroundClient instantiation, -and URL/header configuration. -""" - -import json -from dataclasses import asdict - -import pytest -import httpx - -from hanzo_zap import ( - PlaygroundClient, - AgentEvent, - AgentInfo, - CommitInfo, - EventMsg, - FileChange, - RealtimeAudioFrame, - Submission, -) - - -# -- Playground types -------------------------------------------------------- - - -class TestSubmission: - def test_fields(self): - s = Submission(id="sub-1", op={"type": "inject", "message": "hi"}) - assert s.id == "sub-1" - assert s.op["type"] == "inject" - assert s.trace is None - - def test_with_trace(self): - s = Submission( - id="sub-2", - op={"type": "broadcast"}, - trace={"request_id": "abc"}, - ) - assert s.trace["request_id"] == "abc" - - def test_serializes(self): - s = Submission(id="sub-3", op={"type": "noop"}) - d = asdict(s) - assert d == {"id": "sub-3", "op": {"type": "noop"}, "trace": None} - - -class TestEventMsg: - def test_defaults(self): - e = EventMsg(type="turn_started") - assert e.type == "turn_started" - assert e.data == {} - assert e.raw is None - - def test_with_data(self): - e = EventMsg(type="agent_message", data={"text": "hello"}) - assert e.data["text"] == "hello" - - def test_with_raw(self): - e = EventMsg(type="binary", raw=b"\x00\x01") - assert e.raw == b"\x00\x01" - - def test_serializes(self): - e = EventMsg(type="turn_completed", data={"tokens": 42}) - d = asdict(e) - assert d["type"] == "turn_completed" - assert d["data"]["tokens"] == 42 - - -class TestAgentEvent: - def test_required_fields(self): - ae = AgentEvent(type="message", space_id="sp-1", agent_id="ag-1") - assert ae.type == "message" - assert ae.space_id == "sp-1" - assert ae.agent_id == "ag-1" - assert ae.agent_name == "" - assert ae.timestamp == "" - assert ae.data == {} - - def test_full(self): - ae = AgentEvent( - type="tool_call", - space_id="sp-1", - agent_id="ag-2", - agent_name="coder", - timestamp="2026-03-19T00:00:00Z", - data={"tool": "read_file", "path": "/tmp/x"}, - ) - assert ae.agent_name == "coder" - assert ae.data["tool"] == "read_file" - - -class TestAgentInfo: - def test_defaults(self): - ai = AgentInfo(agent_id="ag-1") - assert ai.agent_id == "ag-1" - assert ai.did == "" - assert ai.status == "offline" - assert ai.capabilities == [] - assert ai.model == "" - - def test_full(self): - ai = AgentInfo( - agent_id="ag-2", - did="did:key:abc", - space_id="sp-1", - display_name="Coder", - status="online", - capabilities=[{"name": "code"}], - model="zen-405b", - ) - assert ai.display_name == "Coder" - assert ai.status == "online" - assert len(ai.capabilities) == 1 - - def test_serializes(self): - ai = AgentInfo(agent_id="ag-3", model="zen-32b") - d = asdict(ai) - assert d["agent_id"] == "ag-3" - assert d["model"] == "zen-32b" - - -class TestRealtimeAudioFrame: - def test_defaults(self): - f = RealtimeAudioFrame(data="AAAA") - assert f.data == "AAAA" - assert f.sample_rate == 16000 - assert f.num_channels == 1 - - def test_custom(self): - f = RealtimeAudioFrame(data="base64data", sample_rate=44100, num_channels=2) - assert f.sample_rate == 44100 - assert f.num_channels == 2 - - -class TestFileChange: - def test_fields(self): - fc = FileChange(path="src/main.py", status="modified") - assert fc.path == "src/main.py" - assert fc.status == "modified" - - -class TestCommitInfo: - def test_fields(self): - ci = CommitInfo( - hash="abc123", - message="fix: resolve issue", - author="Test", - email="test@example.com", - timestamp="2026-03-19T00:00:00Z", - ) - assert ci.hash == "abc123" - assert ci.message == "fix: resolve issue" - assert ci.author == "Test" - assert ci.email == "test@example.com" - - def test_serializes(self): - ci = CommitInfo( - hash="def456", - message="feat: add playground", - author="Dev", - email="dev@hanzo.ai", - timestamp="2026-03-19T12:00:00Z", - ) - d = asdict(ci) - assert d["hash"] == "def456" - assert d["author"] == "Dev" - - -# -- PlaygroundClient -------------------------------------------------------- - - -class TestPlaygroundClient: - def test_default_url(self): - pc = PlaygroundClient() - assert pc.base_url == "http://localhost:8080" - - def test_custom_url(self): - pc = PlaygroundClient(base_url="https://playground.hanzo.ai/") - assert pc.base_url == "https://playground.hanzo.ai" - - def test_trailing_slash_stripped(self): - pc = PlaygroundClient(base_url="http://localhost:9090/") - assert pc.base_url == "http://localhost:9090" - - def test_no_auth_header_without_token(self): - pc = PlaygroundClient() - assert "Authorization" not in pc._client.headers - - def test_auth_header_with_token(self): - pc = PlaygroundClient(token="test-token-123") - assert pc._client.headers["Authorization"] == "Bearer test-token-123" - - def test_timeout(self): - pc = PlaygroundClient() - assert pc._client.timeout.connect == 30.0 - - @pytest.mark.asyncio - async def test_context_manager(self): - async with PlaygroundClient() as pc: - assert pc.base_url == "http://localhost:8080" - # client should be closed after exit - assert pc._client.is_closed - - -# -- Import sanity ----------------------------------------------------------- - - -class TestImports: - """Verify all new symbols are importable from hanzo_zap top-level.""" - - def test_playground_client(self): - from hanzo_zap import PlaygroundClient - assert PlaygroundClient is not None - - def test_all_types(self): - from hanzo_zap import ( - Submission, - EventMsg, - AgentEvent, - AgentInfo, - RealtimeAudioFrame, - FileChange, - CommitInfo, - ) - # All should be callable dataclasses - assert callable(Submission) - assert callable(EventMsg) - assert callable(AgentEvent) - assert callable(AgentInfo) - assert callable(RealtimeAudioFrame) - assert callable(FileChange) - assert callable(CommitInfo) - - def test_existing_exports_still_work(self): - from hanzo_zap import ( - ZapClient, - ZapServer, - ApprovalPolicy, - SandboxPolicy, - MessageType, - Tool, - ToolCall, - ToolResult, - ServerInfo, - ClientInfo, - ) - assert ZapClient is not None - assert ZapServer is not None - - def test_version_bumped(self): - import hanzo_zap - assert hanzo_zap.__version__ == "0.7.0" diff --git a/pkg/hanzo-zap/tests/test_wire.py b/pkg/hanzo-zap/tests/test_wire.py deleted file mode 100644 index 06b780b41..000000000 --- a/pkg/hanzo-zap/tests/test_wire.py +++ /dev/null @@ -1,340 +0,0 @@ -"""Tests for luxfi/zap binary wire protocol compatibility.""" -import asyncio -import struct - -import pytest - -from hanzo_zap.wire import ( - ZAP_MAGIC, - HEADER_SIZE, - VERSION, - MSG_TYPE_CLOUD, - REQ_FLAG_REQ, - REQ_FLAG_RESP, - CLOUD_REQ_METHOD, - CLOUD_REQ_AUTH, - CLOUD_REQ_BODY, - CLOUD_RESP_STATUS, - CLOUD_RESP_BODY, - CLOUD_RESP_ERROR, - MAX_MESSAGE_SIZE, - Message, - Builder, - ObjectBuilder, - build_cloud_request, - build_cloud_response, - parse_cloud_request, - parse_cloud_response, - build_handshake, - parse_handshake, - obj_uint32, - obj_bytes, - obj_text, - read_frame, - write_frame, -) - - -# โ”€โ”€ constants โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -def test_zap_magic(): - assert ZAP_MAGIC == b"ZAP\x00" - assert HEADER_SIZE == 16 - - -def test_version(): - assert VERSION == 1 - - -def test_msg_type_cloud(): - assert MSG_TYPE_CLOUD == 100 - - -def test_req_flags(): - assert REQ_FLAG_REQ == 1 - assert REQ_FLAG_RESP == 2 - - -def test_cloud_resp_offsets(): - """Cloud response layout: status(0:u32,4B) + body(4:Bytes,8B) + error(12:Text,8B).""" - assert CLOUD_RESP_STATUS == 0 - assert CLOUD_RESP_BODY == 4 - assert CLOUD_RESP_ERROR == 12 - - -# โ”€โ”€ Message parsing โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -def test_message_parse_rejects_short(): - with pytest.raises(ValueError, match="too short"): - Message.parse(b"ZAP") - - -def test_message_parse_rejects_bad_magic(): - bad = b"BAD\x00" + b"\x00" * 12 - with pytest.raises(ValueError, match="Bad ZAP magic"): - Message.parse(bad) - - -def test_message_parse_valid_header(): - header = bytearray(HEADER_SIZE) - header[:4] = ZAP_MAGIC - struct.pack_into("> 8 == MSG_TYPE_CLOUD - - -def test_builder_with_object(): - """Builder + ObjectBuilder produce parseable message.""" - b = Builder() - obj = b.start_object(4) - obj.set_u32(0, 0xDEADBEEF) - obj.finish_as_root() - result = b.finish() - msg = Message.parse(result) - assert msg.root_offset == HEADER_SIZE # object starts right after header - val = obj_uint32(msg.bytes, msg.root_offset, 0) - assert val == 0xDEADBEEF - - -def test_builder_text_field(): - """Text field with relative offset is readable.""" - b = Builder() - obj = b.start_object(8) - obj.set_text(0, "hello") - obj.finish_as_root() - result = b.finish() - msg = Message.parse(result) - text = obj_text(msg.bytes, msg.root_offset, 0) - assert text == "hello" - - -def test_builder_bytes_field(): - """Bytes field with relative offset is readable.""" - b = Builder() - obj = b.start_object(8) - payload = b"\x01\x02\x03\x04" - obj.set_bytes(0, payload) - obj.finish_as_root() - result = b.finish() - msg = Message.parse(result) - data = obj_bytes(msg.bytes, msg.root_offset, 0) - assert data == payload - - -def test_builder_mixed_fields(): - """Object with inline u32 + variable-length fields.""" - b = Builder() - obj = b.start_object(20) # u32(4) + Bytes(8) + Text(8) - obj.set_u32(0, 200) - obj.set_bytes(4, b"body-data") - obj.set_text(12, "error-msg") - obj.finish_as_root() - result = b.finish() - msg = Message.parse(result) - off = msg.root_offset - assert obj_uint32(msg.bytes, off, 0) == 200 - assert obj_bytes(msg.bytes, off, 4) == b"body-data" - assert obj_text(msg.bytes, off, 12) == "error-msg" - - -def test_builder_empty_bytes(): - """Empty bytes field roundtrips as empty.""" - b = Builder() - obj = b.start_object(8) - obj.set_bytes(0, b"") - obj.finish_as_root() - result = b.finish() - msg = Message.parse(result) - assert obj_bytes(msg.bytes, msg.root_offset, 0) == b"" - - -# โ”€โ”€ Handshake โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -def test_build_and_parse_handshake(): - hs_bytes = build_handshake("test-node") - msg = Message.parse(hs_bytes) - assert msg.msg_type == 0 - peer = parse_handshake(msg) - assert peer == "test-node" - - -def test_handshake_long_id_truncated(): - long_id = "x" * 100 - hs_bytes = build_handshake(long_id) - msg = Message.parse(hs_bytes) - peer = parse_handshake(msg) - assert peer == "x" * 60 - - -def test_handshake_empty_id(): - hs_bytes = build_handshake("") - msg = Message.parse(hs_bytes) - peer = parse_handshake(msg) - assert peer == "" - - -# โ”€โ”€ Cloud request/response โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -def test_build_cloud_request(): - body = b'{"model":"test","messages":[]}' - msg_bytes = build_cloud_request("chat.completions", "Bearer token123", body) - msg = Message.parse(msg_bytes) - assert msg.msg_type == MSG_TYPE_CLOUD - - -def test_cloud_request_roundtrip(): - """Build and parse a cloud request.""" - body = b'{"test":true}' - msg_bytes = build_cloud_request("completions", "Bearer abc", body) - msg = Message.parse(msg_bytes) - method, auth, req_body = parse_cloud_request(msg) - assert method == "completions" - assert auth == "Bearer abc" - assert req_body == body - - -def test_cloud_response_roundtrip(): - """Build and parse a cloud response.""" - resp_body = b'{"id":"test","choices":[]}' - msg_bytes = build_cloud_response(200, resp_body, "") - msg = Message.parse(msg_bytes) - status, body, error = parse_cloud_response(msg) - assert status == 200 - assert body == resp_body - assert error == "" - - -def test_cloud_response_error(): - msg_bytes = build_cloud_response(500, b"", "internal server error") - msg = Message.parse(msg_bytes) - status, body, error = parse_cloud_response(msg) - assert status == 500 - assert error == "internal server error" - - -def test_cloud_response_with_body_and_error(): - """Response with both body and error text.""" - resp_body = b'{"detail":"something"}' - msg_bytes = build_cloud_response(400, resp_body, "bad request") - msg = Message.parse(msg_bytes) - status, body, error = parse_cloud_response(msg) - assert status == 400 - assert body == resp_body - assert error == "bad request" - - -# โ”€โ”€ Frame I/O helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -async def _make_stream_pair(): - connected: asyncio.Future[tuple[asyncio.StreamReader, asyncio.StreamWriter]] = ( - asyncio.get_event_loop().create_future() - ) - - async def on_connect(r: asyncio.StreamReader, w: asyncio.StreamWriter) -> None: - connected.set_result((r, w)) - - server = await asyncio.start_server(on_connect, "127.0.0.1", 0) - port = server.sockets[0].getsockname()[1] - c_reader, c_writer = await asyncio.open_connection("127.0.0.1", port) - s_reader, s_writer = await connected - - async def cleanup() -> None: - for w in (c_writer, s_writer): - w.close() - await w.wait_closed() - server.close() - await server.wait_closed() - - return s_reader, c_writer, cleanup - - -@pytest.mark.asyncio -async def test_frame_io(): - reader, writer, cleanup = await _make_stream_pair() - try: - test_data = b"hello ZAP" - await write_frame(writer, test_data) - result = await read_frame(reader) - assert result == test_data - finally: - await cleanup() - - -@pytest.mark.asyncio -async def test_frame_io_empty(): - reader, writer, cleanup = await _make_stream_pair() - try: - await write_frame(writer, b"") - result = await read_frame(reader) - assert result == b"" - finally: - await cleanup() - - -@pytest.mark.asyncio -async def test_frame_io_large(): - reader, writer, cleanup = await _make_stream_pair() - try: - big_data = b"X" * 65536 - await write_frame(writer, big_data) - result = await read_frame(reader) - assert result == big_data - finally: - await cleanup() - - -@pytest.mark.asyncio -async def test_frame_rejects_oversized(): - reader, writer, cleanup = await _make_stream_pair() - try: - writer.write(struct.pack(" int: - srv = await asyncio.start_server(lambda r, w: None, "127.0.0.1", 0) - port = srv.sockets[0].getsockname()[1] - srv.close() - await srv.wait_closed() - return port - - -@pytest_asyncio.fixture -async def zap_fixture(): - server = ZapServer(name="test-tools", version="0.1.0") - - @server.tool("greet", "Greet someone", {"type": "object", "properties": {"name": {"type": "string"}}}) - async def greet(tool_name, args): - return f"Hello, {args.get('name', 'world')}!" - - @server.tool("add", "Add numbers") - async def add(tool_name, args): - return args["a"] + args["b"] - - @server.tool("fail", "Always fails") - async def fail(tool_name, args): - raise RuntimeError("intentional") - - @server.tool("echo", "Echo back") - def echo(tool_name, args): - return args.get("text", "") - - @server.tool("slow", "Simulate work") - async def slow(tool_name, args): - await asyncio.sleep(0.01) - return "done" - - port = await _free_port() - await server.start(port, "127.0.0.1") - yield server, port - await server.stop() - - -@pytest.mark.asyncio -class TestIntegration: - - async def test_handshake(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - assert client.server_info.name == "test-tools" - assert client.server_info.version == "0.1.0" - assert client.server_info.capabilities["tools"] is True - await client.close() - - async def test_list_tools(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - tools = await client.list_tools() - names = {t.name for t in tools} - assert names == {"greet", "add", "fail", "echo", "slow"} - for t in tools: - assert isinstance(t.description, str) - assert len(t.description) > 0 - await client.close() - - async def test_tool_schema_preserved(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - tools = await client.list_tools() - greet = next(t for t in tools if t.name == "greet") - assert greet.input_schema["type"] == "object" - assert "name" in greet.input_schema["properties"] - await client.close() - - async def test_call_tool(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - result = await client.call_tool("greet", {"name": "Hanzo"}) - assert result.content == "Hello, Hanzo!" - assert result.error is None - assert result.id.startswith("req-") - await client.close() - - async def test_call_tool_numeric(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - result = await client.call_tool("add", {"a": 10, "b": 32}) - assert result.content == 42 - await client.close() - - async def test_call_tool_error(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - result = await client.call_tool("fail", {}) - assert result.error == "intentional" - assert result.content is None - await client.close() - - async def test_call_sync_handler(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - result = await client.call_tool("echo", {"text": "abc"}) - assert result.content == "abc" - await client.close() - - async def test_call_slow_tool(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - result = await client.call_tool("slow", {}) - assert result.content == "done" - await client.close() - - async def test_unknown_tool(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - result = await client.call_tool("nonexistent", {}) - assert "Unknown tool" in result.error - await client.close() - - async def test_ping(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - await client.ping() - await client.close() - - async def test_sequential_calls(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - for i in range(5): - r = await client.call_tool("add", {"a": i, "b": i}) - assert r.content == i * 2 - await client.close() - - async def test_batch(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - results = await client.batch([ - {"name": "add", "args": {"a": 1, "b": 1}}, - {"name": "greet", "args": {"name": "batch"}}, - {"name": "echo", "args": {"text": "ok"}}, - ]) - assert len(results) == 3 - assert results[0].content == 2 - assert results[1].content == "Hello, batch!" - assert results[2].content == "ok" - await client.close() - - async def test_batch_with_error(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - results = await client.batch([ - {"name": "add", "args": {"a": 1, "b": 2}}, - {"name": "fail", "args": {}}, - ]) - assert results[0].content == 3 - assert results[0].error is None - assert results[1].error == "intentional" - await client.close() - - async def test_context_manager(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - async with client: - result = await client.call_tool("greet", {"name": "ctx"}) - assert result.content == "Hello, ctx!" - - async def test_request_ids_increment(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - r1 = await client.call_tool("echo", {"text": "a"}) - r2 = await client.call_tool("echo", {"text": "b"}) - # IDs should be sequential - id1 = int(r1.id.split("-")[1]) - id2 = int(r2.id.split("-")[1]) - assert id2 == id1 + 1 - await client.close() - - async def test_multiple_clients(self, zap_fixture): - _, port = zap_fixture - c1 = await ZapClient.connect(f"zap://127.0.0.1:{port}") - c2 = await ZapClient.connect(f"zap://127.0.0.1:{port}") - r1 = await c1.call_tool("add", {"a": 1, "b": 2}) - r2 = await c2.call_tool("add", {"a": 3, "b": 4}) - assert r1.content == 3 - assert r2.content == 7 - await c1.close() - await c2.close() - - async def test_large_payload(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - big_text = "x" * 100_000 - result = await client.call_tool("echo", {"text": big_text}) - assert result.content == big_text - await client.close() - - async def test_empty_args(self, zap_fixture): - _, port = zap_fixture - client = await ZapClient.connect(f"zap://127.0.0.1:{port}") - result = await client.call_tool("greet", {}) - assert result.content == "Hello, world!" - await client.close() diff --git a/pkg/hanzo/.hanzo_repl_history b/pkg/hanzo/.hanzo_repl_history deleted file mode 100644 index b4214bca0..000000000 --- a/pkg/hanzo/.hanzo_repl_history +++ /dev/null @@ -1,6 +0,0 @@ - -# 2025-08-06 17:15:25.433922 -+help - -# 2025-08-06 17:15:25.439435 -+exit diff --git a/pkg/hanzo/CHANGELOG.md b/pkg/hanzo/CHANGELOG.md deleted file mode 100644 index 5c7ad95fe..000000000 --- a/pkg/hanzo/CHANGELOG.md +++ /dev/null @@ -1,106 +0,0 @@ -# Changelog - -## [0.2.10] - 2024-08-07 - -### Dependencies -- Updated to hanzo-net v0.1.2 - -### Improvements in hanzo-net v0.1.2 -- Fixed async event loop issues ("coroutine 'main' was never awaited") -- Removed "tinychat" branding - now shows "Hanzo Chat" -- Proper handling when called from existing async context -- Fixed "Cannot run the event loop while another loop is running" error - -## [0.2.9] - 2024-08-07 - -### Fixed -- Fixed `hanzo net` command to properly pass arguments to hanzo-net -- Resolved argparse conflict when importing net module -- Improved handling of command-line arguments for both installed and source versions - -## [0.2.8] - 2024-08-07 - -### Changed -- Main command to run Hanzo Network is now `hanzo net` (was `hanzo node`) -- `hanzo node` remains as an alias for backward compatibility - -### Notes -- Use `hanzo net` to start the distributed AI compute node -- Use `hanzo network` for agent network management commands - -## [0.2.7] - 2024-08-06 - -### Major Update -- hanzo-net is now published to PyPI! Install with: `pip install hanzo-net` -- No longer requires local installation from GitHub -- Full remote support for distributed AI compute nodes - -### Improvements in hanzo-net v0.1.0 (now on PyPI) -- Changed "Exo Cluster" to "Hanzo Network" branding -- Removed "tinychat" label from Web Chat URL -- New HANZO ASCII art instead of exo branding -- Fixed model import path (resolves "Model type llama not supported" error) -- Fully responsive CLI interface that adapts to terminal width -- Dynamic centering of all UI elements -- Adaptive text formatting for narrow terminals - -### hanzo CLI Updates -- Improved dependency checking for hanzo/net -- Better error messages when hanzo/net is not found -- Automatic detection of hanzo/net venv - -## [0.2.6] - 2024-08-06 - -### Added -- Robust dependency checking for hanzo/net integration -- `net_check.py` utility for verifying hanzo/net installation -- Comprehensive tests for node command integration -- CI/CD workflows with GitHub Actions -- Automatic venv detection for hanzo/net - -### Improved -- Better error handling with clear installation instructions -- Smart Python executable detection (uses hanzo/net venv when available) -- More informative error messages when dependencies are missing -- Path handling for both installed and source versions of hanzo/net - -### Fixed -- Python path issues when running hanzo/net -- Dependency resolution for different Python environments -- Import errors when running from different directories - -### Testing -- Added unit tests for node command -- Created standalone test script for quick verification -- Set up automated testing in CI/CD pipeline - -## [0.2.5] - 2024-08-06 - -### Added -- Full integration with hanzo/net for distributed AI compute nodes -- `hanzo node` command now launches hanzo/net instances -- Automatic detection of hanzo/net from installed package or source -- WebUI at http://localhost:52415 and ChatGPT-compatible API endpoint -- Support for model selection via `--models` flag -- Network mode selection (mainnet/testnet/local) - -### Changed -- Default port for `hanzo node` changed to 52415 to match hanzo/net -- Network defaults to "local" for easier testing -- Improved compute node startup with better status messages - -### Fixed -- Removed double welcome message in interactive mode -- Fixed all import issues after package consolidation -- Corrected chat command execution in REPL mode - -## [0.2.4] - 2024-08-06 - -### Changed -- Consolidated package structure by merging hanzo-cli into main hanzo package -- Removed redundant hanzo-mcp-client package -- Cleaned up package structure from 15 to 8 active packages - -### Fixed -- Fixed all relative imports after package consolidation -- Updated CI/CD pipeline for new package structure \ No newline at end of file diff --git a/pkg/hanzo/README.md b/pkg/hanzo/README.md deleted file mode 100644 index 5d092c0b6..000000000 --- a/pkg/hanzo/README.md +++ /dev/null @@ -1,223 +0,0 @@ -# Hanzo CLI and Orchestration Tools - -[![PyPI](https://img.shields.io/pypi/v/hanzo.svg)](https://pypi.org/project/hanzo/) -[![Python Version](https://img.shields.io/pypi/pyversions/hanzo.svg)](https://pypi.org/project/hanzo/) - -Core CLI and orchestration tools for the Hanzo AI platform. - -## Installation - -```bash -pip install hanzo -``` - -## Features - -- **Interactive Chat**: Chat with AI models through CLI -- **Node Management**: Run local AI inference nodes -- **Router Control**: Manage LLM proxy router -- **REPL Interface**: Interactive Python REPL with AI -- **Batch Orchestration**: Orchestrate multiple AI tasks -- **Memory Management**: Persistent conversation memory - -## Usage - -### CLI Commands - -```bash -# Interactive chat -hanzo chat - -# Use specific model -hanzo chat --model gpt-4 - -# Use router (local proxy) -hanzo chat --router - -# Use cloud API -hanzo chat --cloud -``` - -### Node Management - -```bash -# Start local node -hanzo node start - -# Check status -hanzo node status - -# List available models -hanzo node models - -# Load specific model -hanzo node load llama2:7b - -# Stop node -hanzo node stop -``` - -### Router Management - -```bash -# Start router proxy -hanzo router start - -# Check router status -hanzo router status - -# List available models -hanzo router models - -# View configuration -hanzo router config - -# Stop router -hanzo router stop -``` - -### Interactive REPL - -```bash -# Start REPL -hanzo repl - -# In REPL: -> /help # Show help -> /models # List models -> /model gpt-4 # Switch model -> /clear # Clear context -> What is Python? # Ask questions -``` - -## Python API - -### Batch Orchestration - -```python -from hanzo.batch_orchestrator import BatchOrchestrator - -orchestrator = BatchOrchestrator() -results = await orchestrator.run_batch([ - "Summarize quantum computing", - "Explain machine learning", - "Define artificial intelligence" -]) -``` - -### Memory Management - -```python -from hanzo.memory_manager import MemoryManager - -memory = MemoryManager() -memory.add_to_context("user", "What is Python?") -memory.add_to_context("assistant", "Python is...") -context = memory.get_context() -``` - -### Fallback Handling - -```python -from hanzo.fallback_handler import FallbackHandler - -handler = FallbackHandler() -result = await handler.handle_with_fallback( - primary_fn=api_call, - fallback_fn=local_inference -) -``` - -## Configuration - -### Environment Variables - -```bash -# API settings -HANZO_API_KEY=your-api-key -HANZO_BASE_URL=https://api.hanzo.ai - -# Router settings -HANZO_ROUTER_URL=http://localhost:4000/v1 - -# Node settings -HANZO_NODE_URL=http://localhost:8000/v1 -HANZO_NODE_WORKERS=4 - -# Model preferences -HANZO_DEFAULT_MODEL=gpt-4 -HANZO_FALLBACK_MODEL=llama2:7b -``` - -### Configuration File - -Create `~/.hanzo/config.yaml`: - -```yaml -api: - key: your-api-key - base_url: https://api.hanzo.ai - -router: - url: http://localhost:4000/v1 - auto_start: true - -node: - url: http://localhost:8000/v1 - workers: 4 - models: - - llama2:7b - - mistral:7b - -models: - default: gpt-4 - fallback: llama2:7b -``` - -## Architecture - -### Components - -- **CLI**: Command-line interface (`cli.py`) -- **Chat**: Interactive chat interface (`commands/chat.py`) -- **Node**: Local AI node management (`commands/node.py`) -- **Router**: LLM proxy management (`commands/router.py`) -- **REPL**: Interactive Python REPL (`interactive/repl.py`) -- **Orchestrator**: Batch task orchestration (`batch_orchestrator.py`) -- **Memory**: Conversation memory (`memory_manager.py`) -- **Fallback**: Resilient API handling (`fallback_handler.py`) - -### Port Allocation - -- **4000**: Router (LLM proxy) -- **8000**: Node (local AI) -- **9550-9553**: Desktop app integration - -## Development - -### Setup - -```bash -cd pkg/hanzo -uv sync --all-extras -``` - -### Testing - -```bash -# Run tests -pytest tests/ - -# With coverage -pytest tests/ --cov=hanzo -``` - -### Building - -```bash -uv build -``` - -## License - -Apache License 2.0 \ No newline at end of file diff --git a/pkg/hanzo/pkg/hanzo/tests/test_tool_detector.py b/pkg/hanzo/pkg/hanzo/tests/test_tool_detector.py deleted file mode 100644 index 90836c79f..000000000 --- a/pkg/hanzo/pkg/hanzo/tests/test_tool_detector.py +++ /dev/null @@ -1,331 +0,0 @@ -"""Tests for AI tool detection functionality.""" - -import os -import sys -import unittest -from pathlib import Path -from unittest.mock import Mock, MagicMock, patch - -# Add src to path for imports -sys.path.insert(0, str(Path(__file__).parent.parent / "src")) - -from hanzo.tools.detector import AITool, ToolDetector - - -class TestToolDetector(unittest.TestCase): - """Test AI tool detection functionality.""" - - def setUp(self): - """Set up test fixtures.""" - self.detector = ToolDetector() - - def test_tool_initialization(self): - """Test that tools are properly initialized.""" - # Check that we have tools defined - self.assertGreater(len(self.detector.TOOLS), 0) - - # Check that each tool has required attributes - for tool in self.detector.TOOLS: - self.assertIsInstance(tool.name, str) - self.assertIsInstance(tool.display_name, str) - self.assertIsInstance(tool.provider, str) - self.assertIsInstance(tool.priority, int) - self.assertIsInstance(tool.detected, bool) - - def test_priority_ordering(self): - """Test that tools have correct priority ordering.""" - tools = self.detector.TOOLS - - # Find specific tools - hanzod = next((t for t in tools if t.name == "hanzod"), None) - hanzo_router = next((t for t in tools if t.name == "hanzo-router"), None) - claude_code = next((t for t in tools if t.name == "claude-code"), None) - - # Hanzo Node should have highest priority (0) - if hanzod: - self.assertEqual(hanzod.priority, 0) - - # Router should have high priority - if hanzo_router: - self.assertLessEqual(hanzo_router.priority, 2) - - # Claude Code should have reasonable priority - if claude_code: - self.assertLessEqual(claude_code.priority, 5) - - @patch("hanzo.tools.detector.httpx.post") - def test_hanzod_detection_success(self, mock_post): - """Test successful Hanzo Node detection.""" - # Mock successful response - mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = {"status": "ok"} - mock_post.return_value = mock_response - - hanzod = AITool( - name="hanzod", - command="hanzo node", - display_name="Hanzo Node", - provider="hanzo-local", - priority=0, - api_endpoint="http://localhost:3690/health", - ) - - result = self.detector.detect_tool(hanzod) - self.assertTrue(result) - self.assertTrue(hanzod.detected) - - @patch("hanzo.tools.detector.httpx.post") - def test_hanzod_detection_failure_404(self, mock_post): - """Test Hanzo Node detection fails on 404.""" - # Mock 404 response - mock_response = Mock() - mock_response.status_code = 404 - mock_post.return_value = mock_response - - hanzod = AITool( - name="hanzod", - command="hanzo node", - display_name="Hanzo Node", - provider="hanzo-local", - priority=0, - api_endpoint="http://localhost:3690/health", - ) - - result = self.detector.detect_tool(hanzod) - self.assertFalse(result) - self.assertFalse(hanzod.detected) - - @patch("hanzo.tools.detector.httpx.post") - def test_hanzod_detection_connection_refused(self, mock_post): - """Test Hanzo Node detection handles connection refused.""" - import httpx - - # Mock connection error - mock_post.side_effect = httpx.ConnectError("Connection refused") - - hanzod = AITool( - name="hanzod", - command="hanzo node", - display_name="Hanzo Node", - provider="hanzo-local", - priority=0, - api_endpoint="http://localhost:3690/health", - ) - - result = self.detector.detect_tool(hanzod) - self.assertFalse(result) - self.assertFalse(hanzod.detected) - - @patch("hanzo.tools.detector.shutil.which") - def test_command_detection(self, mock_which): - """Test command-based tool detection.""" - # Mock command exists - mock_which.return_value = "/usr/local/bin/claude" - - claude_tool = AITool( - name="claude-code", - command="claude", - display_name="Claude Code", - provider="anthropic", - priority=3, - check_command="claude", - ) - - result = self.detector.detect_tool(claude_tool) - self.assertTrue(result) - self.assertTrue(claude_tool.detected) - - # Mock command doesn't exist - mock_which.return_value = None - claude_tool.detected = False - - result = self.detector.detect_tool(claude_tool) - self.assertFalse(result) - self.assertFalse(claude_tool.detected) - - @patch.dict(os.environ, {"OPENAI_API_KEY": "test-key"}) - @patch("hanzo.tools.detector.shutil.which") - def test_env_var_detection(self, mock_which): - """Test environment variable based detection.""" - mock_which.return_value = "/usr/local/bin/openai" - - openai_tool = AITool( - name="openai", - command="openai", - display_name="OpenAI Codex", - provider="openai", - priority=4, - check_command="openai", - env_var="OPENAI_API_KEY", - ) - - result = self.detector.detect_tool(openai_tool) - self.assertTrue(result) - self.assertTrue(openai_tool.detected) - - @patch.dict(os.environ, {}, clear=True) # Clear all env vars - @patch("hanzo.tools.detector.shutil.which") - def test_env_var_missing(self, mock_which): - """Test that tool is still detected when command exists but env var is missing. - - The tool detection logic checks for command existence first, - and env_var is only a fallback. A tool can be "detected" but - may not be fully functional without the API key. - """ - mock_which.return_value = "/usr/local/bin/openai" - - openai_tool = AITool( - name="openai", - command="openai", - display_name="OpenAI Codex", - provider="openai", - priority=4, - check_command="openai", - env_var="OPENAI_API_KEY", - ) - - result = self.detector.detect_tool(openai_tool) - # Tool is detected because command exists, even without API key - self.assertTrue(result) - self.assertTrue(openai_tool.detected) - - @patch.dict(os.environ, {}, clear=True) # Clear all env vars - @patch("hanzo.tools.detector.shutil.which") - def test_no_command_no_env_var(self, mock_which): - """Test detection fails when neither command nor env var exists.""" - mock_which.return_value = None # Command doesn't exist - - openai_tool = AITool( - name="openai", - command="openai", - display_name="OpenAI Codex", - provider="openai", - priority=4, - check_command="openai", - env_var="OPENAI_API_KEY", - ) - - result = self.detector.detect_tool(openai_tool) - self.assertFalse(result) - self.assertFalse(openai_tool.detected) - - @patch("hanzo.tools.detector.subprocess.run") - @patch("hanzo.tools.detector.shutil.which") - def test_version_detection(self, mock_which, mock_run): - """Test version detection for tools.""" - mock_which.return_value = "/usr/local/bin/hanzo" - - # Mock version command output - mock_result = Mock() - mock_result.stdout = "hanzo version 0.3.23\n" - mock_result.returncode = 0 - mock_run.return_value = mock_result - - hanzo_tool = AITool( - name="hanzo-dev", - command="hanzo dev", - display_name="Hanzo Dev", - provider="hanzo", - priority=2, - check_command="hanzo", - ) - - result = self.detector.detect_tool(hanzo_tool) - self.assertTrue(result) - self.assertIn("0.3.23", hanzo_tool.version or "") - - @patch("hanzo.tools.detector.httpx.post") - @patch("hanzo.tools.detector.shutil.which") - def test_detect_all(self, mock_which, mock_post): - """Test detecting all available tools.""" - - # Mock some tools as available - def which_side_effect(cmd): - if cmd in ["claude", "hanzo", "openai"]: - return f"/usr/local/bin/{cmd}" - return None - - mock_which.side_effect = which_side_effect - - # Mock Hanzo Node as not running - import httpx - - mock_post.side_effect = httpx.ConnectError("Connection refused") - - # Set OpenAI API key for testing - with patch.dict(os.environ, {"OPENAI_API_KEY": "test-key"}): - detected_tools = self.detector.detect_all() - - # Should have detected some tools - self.assertGreater(len(detected_tools), 0) - - # Check that detected tools are marked as such - for tool in detected_tools: - if tool.name in ["claude-code", "hanzo-dev", "openai"]: - self.assertTrue(tool.detected, f"{tool.name} should be detected") - elif tool.name == "hanzod": - self.assertFalse(tool.detected, "hanzod should not be detected") - - def test_get_default_tool(self): - """Test getting the default tool.""" - # Create mock tools with different priorities (already sorted) - tools = [ - AITool("tool2", "cmd2", "Tool 2", "provider2", priority=2, detected=True), - AITool("tool4", "cmd4", "Tool 4", "provider4", priority=3, detected=True), - AITool("tool1", "cmd1", "Tool 1", "provider1", priority=5, detected=True), - ] - - self.detector.detected_tools = tools - - # Default should be the first tool (tool2 with priority 2) - default = self.detector.get_default_tool() - self.assertIsNotNone(default) - self.assertEqual(default.name, "tool2") - self.assertEqual(default.priority, 2) - - def test_get_default_tool_none_detected(self): - """Test get_default_tool returns None when no tools detected.""" - # Empty detected_tools list - self.detector.detected_tools = [] - - # Mock detect_all to return empty list - with patch.object(self.detector, "detect_all", return_value=[]): - default = self.detector.get_default_tool() - self.assertIsNone(default) - - @patch("hanzo.tools.detector.httpx.post") - def test_port_fallback_for_hanzod(self, mock_post): - """Test that Hanzo Node tries both port 3690 and 8000.""" - import httpx - - # First call to port 3690 fails, second to 8000 succeeds - responses = [ - httpx.ConnectError("Connection refused"), # Port 3690 fails - Mock(status_code=200), # Port 8000 succeeds - ] - mock_post.side_effect = responses - - hanzod = AITool( - name="hanzod", - command="hanzo node", - display_name="Hanzo Node", - provider="hanzo-local", - priority=0, - api_endpoint="http://localhost:3690/health", - ) - - result = self.detector.detect_tool(hanzod) - - # Should have tried both ports - self.assertEqual(mock_post.call_count, 2) - - # Check that both ports were tried - calls = mock_post.call_args_list - urls = [call[0][0] for call in calls] - self.assertIn("http://localhost:3690/v1/chat/completions", urls) - self.assertIn("http://localhost:8000/v1/chat/completions", urls) - - -if __name__ == "__main__": - unittest.main() diff --git a/pkg/hanzo/publish b/pkg/hanzo/publish deleted file mode 100755 index 8a6251e51..000000000 --- a/pkg/hanzo/publish +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash - -# Hanzo Package Publishing Script -# Version 0.2.5 - -set -e - -echo "==================================" -echo "Hanzo Package Publishing" -echo "Version: 0.2.5" -echo "==================================" -echo - -# Check if we're in the right directory -if [ ! -f "pyproject.toml" ]; then - echo "Error: pyproject.toml not found. Please run from pkg/hanzo directory." - exit 1 -fi - -# Clean previous builds -echo "Cleaning previous builds..." -rm -rf dist/ build/ *.egg-info 2>/dev/null || true - -# Build the package -echo "Building distribution packages..." -python -m build - -# Check the build -echo -echo "Build complete. Contents:" -ls -la dist/ - -# Test the package (optional) -echo -read -p "Do you want to test the package locally before uploading? (y/n) " -n 1 -r -echo -if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "Creating test virtual environment..." - python -m venv test_env - source test_env/bin/activate - pip install dist/hanzo-0.2.5-py3-none-any.whl - echo "Testing hanzo command..." - hanzo --version - deactivate - rm -rf test_env - echo "Test successful!" -fi - -# Upload to TestPyPI first (optional) -echo -read -p "Do you want to upload to TestPyPI first? (y/n) " -n 1 -r -echo -if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "Uploading to TestPyPI..." - python -m twine upload --repository testpypi dist/* - echo - echo "Package uploaded to TestPyPI." - echo "Test with: pip install --index-url https://test.pypi.org/simple/ hanzo" - echo - read -p "Continue to PyPI? (y/n) " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo "Stopping here. Package is on TestPyPI." - exit 0 - fi -fi - -# Upload to PyPI -echo -echo "Uploading to PyPI..." -echo "You will be prompted for your PyPI credentials or API token." -echo -python -m twine upload dist/* - -echo -echo "==================================" -echo "Package published successfully!" -echo "Install with: pip install hanzo" -echo "Current version: 0.2.5" -echo "==================================" \ No newline at end of file diff --git a/pkg/hanzo/publish.sh b/pkg/hanzo/publish.sh deleted file mode 100755 index 8a6251e51..000000000 --- a/pkg/hanzo/publish.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash - -# Hanzo Package Publishing Script -# Version 0.2.5 - -set -e - -echo "==================================" -echo "Hanzo Package Publishing" -echo "Version: 0.2.5" -echo "==================================" -echo - -# Check if we're in the right directory -if [ ! -f "pyproject.toml" ]; then - echo "Error: pyproject.toml not found. Please run from pkg/hanzo directory." - exit 1 -fi - -# Clean previous builds -echo "Cleaning previous builds..." -rm -rf dist/ build/ *.egg-info 2>/dev/null || true - -# Build the package -echo "Building distribution packages..." -python -m build - -# Check the build -echo -echo "Build complete. Contents:" -ls -la dist/ - -# Test the package (optional) -echo -read -p "Do you want to test the package locally before uploading? (y/n) " -n 1 -r -echo -if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "Creating test virtual environment..." - python -m venv test_env - source test_env/bin/activate - pip install dist/hanzo-0.2.5-py3-none-any.whl - echo "Testing hanzo command..." - hanzo --version - deactivate - rm -rf test_env - echo "Test successful!" -fi - -# Upload to TestPyPI first (optional) -echo -read -p "Do you want to upload to TestPyPI first? (y/n) " -n 1 -r -echo -if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "Uploading to TestPyPI..." - python -m twine upload --repository testpypi dist/* - echo - echo "Package uploaded to TestPyPI." - echo "Test with: pip install --index-url https://test.pypi.org/simple/ hanzo" - echo - read -p "Continue to PyPI? (y/n) " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo "Stopping here. Package is on TestPyPI." - exit 0 - fi -fi - -# Upload to PyPI -echo -echo "Uploading to PyPI..." -echo "You will be prompted for your PyPI credentials or API token." -echo -python -m twine upload dist/* - -echo -echo "==================================" -echo "Package published successfully!" -echo "Install with: pip install hanzo" -echo "Current version: 0.2.5" -echo "==================================" \ No newline at end of file diff --git a/pkg/hanzo/pyproject.toml b/pkg/hanzo/pyproject.toml deleted file mode 100644 index eb4067014..000000000 --- a/pkg/hanzo/pyproject.toml +++ /dev/null @@ -1,115 +0,0 @@ -[project] -name = "hanzo" -version = "0.4.2" -description = "Hanzo AI - Complete AI Infrastructure Platform with CLI, Router, MCP, and Agent Runtime" -authors = [ - {name = "Hanzo AI", email = "dev@hanzo.ai"}, -] -dependencies = [ - "click>=8.1.0", - "rich>=13.0.0", - "httpx>=0.23.0", - "pydantic>=2.0.0", - "pyyaml>=6.0", - "hanzo-cli>=0.2.0", - "hanzo-kms>=1.1.0", -] -readme = "README.md" -requires-python = ">=3.12" -keywords = ["ai", "cli", "hanzo", "agents", "llm", "mcp", "local-ai", "private-ai"] -classifiers = [ - "Development Status :: 4 - Beta", - "Environment :: Console", - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Scientific/Engineering :: Artificial Intelligence", -] - -[project.scripts] -hanzo = "hanzo.cli:main" - -[project.optional-dependencies] -all = [ - "hanzoai>=1.0.0", - "hanzo-mcp>=0.7.0", - "hanzo-agents>=0.1.0", - "hanzo-network>=0.1.3", - "hanzo-memory>=1.0.0", - # "hanzo-router>=1.74.3", # TODO: Publish hanzo-router to PyPI - "hanzo-aci>=0.2.8", - "prompt-toolkit>=3.0.0", - "openai>=1.0.0", - "anthropic>=0.25.0", - "qrcode>=7.4.2", -] -ai = [ - "hanzoai>=1.0.0", - "openai>=1.0.0", - "anthropic>=0.25.0", -] -router = [ - # "hanzo-router>=1.74.3", # TODO: Publish hanzo-router to PyPI -] -mcp = [ - "hanzo-mcp>=0.7.0", -] -agents = [ - "hanzo-agents>=0.1.0", - "hanzo-network>=0.1.3", -] -dev = [ - "hanzo-aci>=0.2.8", -] -interactive = [ - "prompt-toolkit>=3.0.0", - "qrcode>=7.4.2", -] - -# Infrastructure extras - simple names -vector = ["qdrant-client>=1.7.0"] -kv = ["redis>=5.0.0"] -documentdb = ["motor>=3.3.0", "pymongo>=4.6.0"] -storage = ["aiobotocore>=2.9.0", "botocore>=1.34.0"] -search = ["meilisearch-python-sdk>=3.0.0"] -pubsub = ["nats-py>=2.6.0"] -tasks = ["temporalio>=1.4.0"] -queues = ["redis>=5.0.0"] -cron = ["redis>=5.0.0", "croniter>=2.0.0"] -functions = ["httpx>=0.23.0"] -# All infrastructure -infra = [ - "qdrant-client>=1.7.0", - "redis>=5.0.0", - "motor>=3.3.0", - "pymongo>=4.6.0", - "aiobotocore>=2.9.0", - "botocore>=1.34.0", - "meilisearch-python-sdk>=3.0.0", - "nats-py>=2.6.0", - "temporalio>=1.4.0", - "croniter>=2.0.0", - "httpx>=0.23.0", -] - -[project.urls] -Homepage = "https://hanzo.ai" -Repository = "https://github.com/hanzoai/python-sdk" -Documentation = "https://docs.hanzo.ai/cli" -"Bug Tracker" = "https://github.com/hanzoai/python-sdk/issues" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build] -include = [ - "src/hanzo", -] - -[tool.hatch.build.targets.wheel] -packages = ["src/hanzo"] diff --git a/pkg/hanzo/pytest.ini b/pkg/hanzo/pytest.ini deleted file mode 100644 index 0f27965df..000000000 --- a/pkg/hanzo/pytest.ini +++ /dev/null @@ -1,12 +0,0 @@ -[pytest] -testpaths = tests -python_files = test_*.py -python_classes = Test* -python_functions = test_* -addopts = -v --tb=short -asyncio_mode = auto -markers = - integration: marks tests as integration tests (deselect with '-m "not integration"') -filterwarnings = - ignore::DeprecationWarning - ignore::PendingDeprecationWarning \ No newline at end of file diff --git a/pkg/hanzo/src/hanzo/__init__.py b/pkg/hanzo/src/hanzo/__init__.py deleted file mode 100644 index d6d665509..000000000 --- a/pkg/hanzo/src/hanzo/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Hanzo - Complete AI Infrastructure Platform with CLI, Router, MCP, and Agent Runtime.""" - -__version__ = "0.3.47" -__all__ = ["main", "cli", "__version__"] - -from .cli import cli, main diff --git a/pkg/hanzo/src/hanzo/__main__.py b/pkg/hanzo/src/hanzo/__main__.py deleted file mode 100644 index ceb24a528..000000000 --- a/pkg/hanzo/src/hanzo/__main__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Entry point for python -m hanzo_cli.""" - -from .cli import main - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo/src/hanzo/base_agent.py b/pkg/hanzo/src/hanzo/base_agent.py deleted file mode 100644 index 8db2930a9..000000000 --- a/pkg/hanzo/src/hanzo/base_agent.py +++ /dev/null @@ -1,526 +0,0 @@ -"""Base Agent - Unified foundation for all AI agent implementations. - -This module provides the single base class for all agent operations, -following DRY principles and ensuring consistent behavior across all agents. -""" - -from __future__ import annotations - -import os -import asyncio -import logging -from abc import ABC, abstractmethod -from typing import Any, Dict, List, Generic, TypeVar, Optional, Protocol -from pathlib import Path -from datetime import datetime -from dataclasses import field, dataclass - -from .model_registry import ModelConfig, registry - -logger = logging.getLogger(__name__) - - -# Type variables for generic context -TContext = TypeVar("TContext") -TResult = TypeVar("TResult") - - -class AgentContext(Protocol[TContext]): - """Protocol for agent execution context.""" - - async def log(self, message: str, level: str = "info") -> None: - """Log a message.""" - ... - - async def progress(self, message: str, percentage: Optional[float] = None) -> None: - """Report progress.""" - ... - - -@dataclass -class AgentConfig: - """Configuration for agent execution.""" - - model: str = "claude-3-5-sonnet-20241022" - timeout: int = 300 - max_retries: int = 3 - working_dir: Optional[Path] = None - environment: Dict[str, str] = field(default_factory=dict) - stream_output: bool = False - use_worktree: bool = False - - def __post_init__(self) -> None: - """Resolve model name and validate configuration.""" - self.model = registry.resolve(self.model) - if self.working_dir and not isinstance(self.working_dir, Path): - self.working_dir = Path(self.working_dir) - - -@dataclass -class AgentResult: - """Result from agent execution.""" - - success: bool - output: Optional[str] = None - error: Optional[str] = None - duration: Optional[float] = None - metadata: Dict[str, Any] = field(default_factory=dict) - - @property - def content(self) -> str: - """Get the primary content (output or error).""" - return self.output if self.success else (self.error or "Unknown error") - - -class BaseAgent(ABC, Generic[TContext, TResult]): - """Base class for all AI agents. - - This is the single foundation for all agent implementations, - ensuring consistent behavior and eliminating code duplication. - """ - - def __init__(self, config: Optional[AgentConfig] = None) -> None: - """Initialize agent with configuration. - - Args: - config: Agent configuration - """ - self.config = config or AgentConfig() - self._start_time: Optional[datetime] = None - self._end_time: Optional[datetime] = None - - @property - @abstractmethod - def name(self) -> str: - """Agent name.""" - ... - - @property - @abstractmethod - def description(self) -> str: - """Agent description.""" - ... - - async def execute( - self, - prompt: str, - context: Optional[TContext] = None, - **kwargs: Any, - ) -> AgentResult: - """Execute agent with prompt. - - Args: - prompt: The prompt or task - context: Execution context - **kwargs: Additional parameters - - Returns: - Agent execution result - """ - self._start_time = datetime.now() - - try: - # Setup environment - env = self._prepare_environment() - - # Log start - if context and hasattr(context, "log"): - await context.log( - f"Starting {self.name} with model {self.config.model}" - ) - - # Execute with retries - result = await self._execute_with_retries(prompt, context, env, **kwargs) - - # Calculate duration - self._end_time = datetime.now() - duration = (self._end_time - self._start_time).total_seconds() - - return AgentResult( - success=True, - output=result, - duration=duration, - metadata={"model": self.config.model, "agent": self.name}, - ) - - except Exception as e: - self._end_time = datetime.now() - duration = ( - (self._end_time - self._start_time).total_seconds() - if self._start_time - else None - ) - - logger.error(f"Agent {self.name} failed: {e}") - - return AgentResult( - success=False, - error=str(e), - duration=duration, - metadata={"model": self.config.model, "agent": self.name}, - ) - - def _prepare_environment(self) -> Dict[str, str]: - """Prepare environment variables for execution. - - Returns: - Environment variables dictionary - """ - env = os.environ.copy() - - # Add model-specific API key - model_config = registry.get(self.config.model) - if model_config and model_config.api_key_env: - key_var = model_config.api_key_env - if key_var in os.environ: - env[key_var] = os.environ[key_var] - - # Add Hanzo unified auth - if "HANZO_API_KEY" in os.environ: - env["HANZO_API_KEY"] = os.environ["HANZO_API_KEY"] - - # Add custom environment - env.update(self.config.environment) - - return env - - async def _execute_with_retries( - self, - prompt: str, - context: Optional[TContext], - env: Dict[str, str], - **kwargs: Any, - ) -> str: - """Execute with retry logic. - - Args: - prompt: The prompt - context: Execution context - env: Environment variables - **kwargs: Additional parameters - - Returns: - Execution output - - Raises: - Exception: If all retries fail - """ - last_error = None - - for attempt in range(self.config.max_retries): - try: - # Call the implementation - result = await self._execute_impl(prompt, context, env, **kwargs) - return result - - except asyncio.TimeoutError: - last_error = f"Timeout after {self.config.timeout} seconds" - if context and hasattr(context, "log"): - await context.log(f"Attempt {attempt + 1} timed out", "warning") - - except Exception as e: - last_error = str(e) - if context and hasattr(context, "log"): - await context.log(f"Attempt {attempt + 1} failed: {e}", "warning") - - # Don't retry on certain errors - if "unauthorized" in str(e).lower() or "forbidden" in str(e).lower(): - raise - - # Wait before retry (exponential backoff) - if attempt < self.config.max_retries - 1: - await asyncio.sleep(2**attempt) - - raise Exception( - f"All {self.config.max_retries} attempts failed. Last error: {last_error}" - ) - - @abstractmethod - async def _execute_impl( - self, - prompt: str, - context: Optional[TContext], - env: Dict[str, str], - **kwargs: Any, - ) -> str: - """Implementation-specific execution. - - Args: - prompt: The prompt - context: Execution context - env: Environment variables - **kwargs: Additional parameters - - Returns: - Execution output - """ - ... - - -class CLIAgent(BaseAgent[TContext, str]): - """Base class for CLI-based agents.""" - - @property - @abstractmethod - def cli_command(self) -> str: - """CLI command to execute.""" - ... - - def build_command(self, prompt: str, **kwargs: Any) -> List[str]: - """Build the CLI command. - - Args: - prompt: The prompt - **kwargs: Additional parameters - - Returns: - Command arguments list - """ - command = [self.cli_command] - - # Add model if specified - model_config = registry.get(self.config.model) - if model_config: - command.extend(["--model", model_config.full_name]) - - # Add prompt - command.append(prompt) - - return command - - async def _execute_impl( - self, - prompt: str, - context: Optional[TContext], - env: Dict[str, str], - **kwargs: Any, - ) -> str: - """Execute CLI command. - - Args: - prompt: The prompt - context: Execution context - env: Environment variables - **kwargs: Additional parameters - - Returns: - Command output - """ - command = self.build_command(prompt, **kwargs) - - # Execute command - process = await asyncio.create_subprocess_exec( - *command, - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - cwd=str(self.config.working_dir) if self.config.working_dir else None, - env=env, - ) - - # Handle timeout - try: - stdout, stderr = await asyncio.wait_for( - process.communicate(prompt.encode() if len(command) == 1 else None), - timeout=self.config.timeout, - ) - except asyncio.TimeoutError: - process.kill() - raise asyncio.TimeoutError( - f"Command timed out after {self.config.timeout} seconds" - ) - - # Check for errors - if process.returncode != 0: - error_msg = stderr.decode() if stderr else "Command failed" - raise Exception(error_msg) - - return stdout.decode() - - -class APIAgent(BaseAgent[TContext, str]): - """Base class for API-based agents.""" - - async def _execute_impl( - self, - prompt: str, - context: Optional[TContext], - env: Dict[str, str], - **kwargs: Any, - ) -> str: - """Execute via API. - - Args: - prompt: The prompt - context: Execution context - env: Environment variables - **kwargs: Additional parameters - - Returns: - API response - """ - # This would be implemented by specific API agents - # using the appropriate client library - raise NotImplementedError("API agents must implement _execute_impl") - - -class AgentOrchestrator: - """Orchestrator for managing multiple agents.""" - - def __init__(self, default_config: Optional[AgentConfig] = None) -> None: - """Initialize orchestrator. - - Args: - default_config: Default configuration for agents - """ - self.default_config = default_config or AgentConfig() - self._agents: Dict[str, BaseAgent] = {} - self._semaphore: Optional[asyncio.Semaphore] = None - - def register(self, agent: BaseAgent) -> None: - """Register an agent. - - Args: - agent: Agent to register - """ - self._agents[agent.name] = agent - - def get_agent(self, name: str) -> Optional[BaseAgent]: - """Get agent by name. - - Args: - name: Agent name - - Returns: - Agent instance or None - """ - return self._agents.get(name) - - async def execute_single( - self, - agent_name: str, - prompt: str, - context: Optional[Any] = None, - **kwargs: Any, - ) -> AgentResult: - """Execute single agent. - - Args: - agent_name: Name of agent to use - prompt: The prompt - context: Execution context - **kwargs: Additional parameters - - Returns: - Execution result - """ - agent = self.get_agent(agent_name) - if not agent: - return AgentResult( - success=False, - error=f"Agent '{agent_name}' not found", - ) - - return await agent.execute(prompt, context, **kwargs) - - async def execute_parallel( - self, - tasks: List[Dict[str, Any]], - max_concurrent: int = 5, - ) -> List[AgentResult]: - """Execute multiple agents in parallel. - - Args: - tasks: List of task definitions - max_concurrent: Maximum concurrent executions - - Returns: - List of results - """ - self._semaphore = asyncio.Semaphore(max_concurrent) - - async def run_with_semaphore(task: Dict[str, Any]) -> AgentResult: - async with self._semaphore: - return await self.execute_single( - task["agent"], - task["prompt"], - task.get("context"), - **task.get("kwargs", {}), - ) - - return await asyncio.gather( - *[run_with_semaphore(task) for task in tasks], - return_exceptions=False, - ) - - async def execute_consensus( - self, - prompt: str, - agents: List[str], - threshold: float = 0.66, - ) -> Dict[str, Any]: - """Execute consensus operation with multiple agents. - - Args: - prompt: The prompt - agents: List of agent names - threshold: Agreement threshold - - Returns: - Consensus results - """ - # Execute all agents in parallel - tasks = [{"agent": agent, "prompt": prompt} for agent in agents] - results = await self.execute_parallel(tasks) - - # Analyze consensus - successful = [r for r in results if r.success] - agreement = len(successful) / len(results) if results else 0 - - return { - "consensus_reached": agreement >= threshold, - "agreement_score": agreement, - "individual_results": results, - "agents_used": agents, - } - - async def execute_chain( - self, - initial_prompt: str, - agents: List[str], - ) -> List[AgentResult]: - """Execute agents in a chain, passing output forward. - - Args: - initial_prompt: Initial prompt - agents: List of agent names - - Returns: - List of results from each step - """ - results = [] - current_prompt = initial_prompt - - for agent_name in agents: - result = await self.execute_single(agent_name, current_prompt) - results.append(result) - - if result.success and result.output: - # Use output as input for next agent - current_prompt = f"Review and improve:\n{result.output}" - else: - # Chain broken - break - - return results - - -__all__ = [ - "AgentContext", - "AgentConfig", - "AgentResult", - "BaseAgent", - "CLIAgent", - "APIAgent", - "AgentOrchestrator", -] diff --git a/pkg/hanzo/src/hanzo/batch_orchestrator.py b/pkg/hanzo/src/hanzo/batch_orchestrator.py deleted file mode 100644 index 7fa16e56e..000000000 --- a/pkg/hanzo/src/hanzo/batch_orchestrator.py +++ /dev/null @@ -1,1029 +0,0 @@ -"""Batch Orchestrator for Hanzo Dev - Unified Parallel Agent Execution. - -This module provides a single, DRY implementation for all batch operations, -consensus mechanisms, and critic chains using the unified base classes. -""" - -import re -import json -import asyncio -import logging -import subprocess -from typing import Any, Dict, List, Callable, Optional, AsyncIterator -from pathlib import Path -from datetime import datetime -from dataclasses import field, dataclass - -from rich.panel import Panel -from rich.table import Table -from rich.console import Console -from rich.progress import TaskID, Progress, BarColumn, TextColumn, SpinnerColumn - -try: - # Try to import from hanzo-mcp if available - from hanzo_mcp.core.base_agent import AgentConfig, AgentResult, AgentOrchestrator - from hanzo_mcp.core.model_registry import registry -except ImportError: - # Fall back to local imports if hanzo-mcp is not installed - from .base_agent import AgentConfig, AgentResult, AgentOrchestrator - from .model_registry import registry - -logger = logging.getLogger(__name__) -console = Console() - - -@dataclass -class BatchTask: - """Represents a single task in a batch operation.""" - - id: str - description: str - file_path: Optional[Path] = None - agent_model: str = field(default_factory=lambda: registry.resolve("claude")) - status: str = "pending" # pending, running, completed, failed - result: Optional[AgentResult] = None - start_time: Optional[datetime] = None - end_time: Optional[datetime] = None - error: Optional[str] = None # Direct error message for exceptions - - def duration(self) -> Optional[float]: - """Calculate task duration in seconds.""" - if self.start_time and self.end_time: - return (self.end_time - self.start_time).total_seconds() - return None - - @property - def success(self) -> bool: - """Check if task succeeded.""" - return ( - self.status == "completed" - and self.result - and getattr(self.result, "success", True) - ) - - def get_error(self) -> Optional[str]: - """Get error message from direct error or result.""" - if self.error: - return self.error - if self.result and hasattr(self.result, "success") and not self.result.success: - return getattr(self.result, "error", None) - return None - - -@dataclass -class BatchConfig: - """Configuration for batch operations.""" - - batch_size: int = 5 # Default concurrent tasks - agent_model: str = field(default_factory=lambda: registry.resolve("claude")) - operation: str = "" - target_pattern: str = "**/*" # File pattern - max_retries: int = 3 - timeout_seconds: int = 300 - stream_results: bool = True - use_mcp_tools: bool = True - use_worktrees: bool = False # Use git worktrees for parallel editing - worktree_base: str = ".worktrees" # Base dir for worktrees - - # Consensus and critic features - consensus_mode: bool = False - consensus_models: List[str] = field(default_factory=list) - consensus_threshold: float = 0.66 # Agreement threshold - critic_mode: bool = False - critic_models: List[str] = field(default_factory=list) - critic_chain: bool = False # Chain critics sequentially - - def __post_init__(self) -> None: - """Resolve all model names using registry.""" - self.agent_model = registry.resolve(self.agent_model) - self.consensus_models = [registry.resolve(m) for m in self.consensus_models] - self.critic_models = [registry.resolve(m) for m in self.critic_models] - - @classmethod - def from_command(cls, command: str) -> "BatchConfig": - """Parse batch command syntax. - - Examples: - batch:5 add copyright to all files # Defaults to Claude - batch:100 agent:claude add copyright to all files - batch:50 agent:codex fix typing in *.py - batch:5 worktree:true parallel edits # Use git worktrees - - consensus:3 agent:gemini,claude,codex review code - consensus:3 llm:gpt-5,opus-4.1,sonnet-4.1 analyze - - critic:3 agent:claude,codex,gemini review implementation - critic:3 chain:true progressive review # Chain critics - """ - config = cls() - - # Parse consensus mode - consensus_match = re.search(r"consensus:(\d+)", command) - if consensus_match: - config.consensus_mode = True - config.batch_size = int(consensus_match.group(1)) - - # Parse consensus agents/models - agent_list_match = re.search(r"agent:([a-zA-Z0-9,\-_.]+)", command) - llm_list_match = re.search(r"llm:([a-zA-Z0-9,\-_.]+)", command) - - if agent_list_match: - agents = agent_list_match.group(1).split(",") - config.consensus_models = agents # Will be resolved in __post_init__ - elif llm_list_match: - models = llm_list_match.group(1).split(",") - config.consensus_models = models # Will be resolved in __post_init__ - - # Parse critic mode - critic_match = re.search(r"critic:(\d+)", command) - if critic_match: - config.critic_mode = True - config.batch_size = int(critic_match.group(1)) - - # Parse critic chain option - chain_match = re.search(r"chain:(true|false)", command) - if chain_match: - config.critic_chain = chain_match.group(1) == "true" - - # Parse critic agents/models - agent_list_match = re.search(r"agent:([a-zA-Z0-9,\-_.]+)", command) - if agent_list_match: - agents = agent_list_match.group(1).split(",") - config.critic_models = agents # Will be resolved in __post_init__ - - # Parse batch size (if not consensus/critic) - if not config.consensus_mode and not config.critic_mode: - batch_match = re.search(r"batch:(\d+)", command) - if batch_match: - config.batch_size = int(batch_match.group(1)) - - # Parse single agent model (for regular batch) - if not config.consensus_mode and not config.critic_mode: - agent_match = re.search(r"agent:(\w+)", command) - if agent_match: - config.agent_model = agent_match.group( - 1 - ) # Will be resolved in __post_init__ - - # Parse worktree option - worktree_match = re.search(r"worktree:(true|false)", command) - if worktree_match: - config.use_worktrees = worktree_match.group(1) == "true" - - # Parse file pattern - pattern_match = re.search(r"files:([^\s]+)", command) - if pattern_match: - config.target_pattern = pattern_match.group(1) - - # Extract operation (remove all config parts) - operation = command - operation = re.sub(r"(batch|consensus|critic):\d+\s*", "", operation) - operation = re.sub(r"agent:[a-zA-Z0-9,\-_.]+\s*", "", operation) - operation = re.sub(r"llm:[a-zA-Z0-9,\-_.]+\s*", "", operation) - operation = re.sub(r"chain:(true|false)\s*", "", operation) - operation = re.sub(r"worktree:(true|false)\s*", "", operation) - operation = re.sub(r"files:[^\s]+\s*", "", operation) - config.operation = operation.strip() - - # Trigger __post_init__ to resolve model names - config.__post_init__() - - return config - - -class BatchOrchestrator: - """Orchestrates parallel batch operations using unified agent system.""" - - def __init__( - self, - mcp_client: Optional[Any] = None, - hanzo_client: Optional[Any] = None, - ): - """Initialize batch orchestrator. - - Args: - mcp_client: MCP client for tool access - hanzo_client: Hanzo client for AI operations - """ - self.mcp_client = mcp_client - self.hanzo_client = hanzo_client - self.agent_orchestrator = AgentOrchestrator() - self.active_tasks: Dict[str, BatchTask] = {} - self.completed_tasks: List[BatchTask] = [] - self.failed_tasks: List[BatchTask] = [] - self._task_counter = 0 - self._progress: Optional[Progress] = None - self._worktrees: Dict[str, Path] = {} # Track worktrees - - def _generate_task_id(self) -> str: - """Generate unique task ID.""" - self._task_counter += 1 - return f"task_{self._task_counter:04d}" - - async def _setup_worktree( - self, task_id: str, config: BatchConfig - ) -> Optional[Path]: - """Setup git worktree for parallel editing. - - Args: - task_id: Task identifier - config: Batch configuration - - Returns: - Path to worktree or None if not using worktrees - """ - if not config.use_worktrees: - return None - - try: - # Create worktree directory - worktree_path = Path(config.worktree_base) / task_id - worktree_path.parent.mkdir(parents=True, exist_ok=True) - - # Create worktree - import subprocess - - result = subprocess.run( - ["git", "worktree", "add", str(worktree_path), "HEAD"], - capture_output=True, - text=True, - ) - - if result.returncode != 0: - logger.error(f"Failed to create worktree: {result.stderr}") - return None - - self._worktrees[task_id] = worktree_path - return worktree_path - - except Exception as e: - logger.error(f"Error setting up worktree: {e}") - return None - - async def _cleanup_worktree(self, task_id: str) -> None: - """Cleanup git worktree after task completion. - - Args: - task_id: Task identifier - """ - if task_id not in self._worktrees: - return - - try: - worktree_path = self._worktrees[task_id] - - # Remove worktree - import subprocess - - subprocess.run( - ["git", "worktree", "remove", str(worktree_path), "--force"], - capture_output=True, - ) - - del self._worktrees[task_id] - - except Exception as e: - logger.error(f"Error cleaning up worktree: {e}") - - async def _find_target_files(self, pattern: str) -> List[Path]: - """Find files matching the target pattern. - - Args: - pattern: Glob pattern for files - - Returns: - List of matching file paths - """ - if self.mcp_client: - # Use MCP find tool - try: - result = await self.mcp_client.call_tool("find", {"pattern": pattern}) - if isinstance(result, str): - # Parse file paths from result - files = [] - for line in result.split("\n"): - if line.strip(): - files.append(Path(line.strip())) - return files - except Exception as e: - logger.error(f"MCP find failed: {e}") - - # Fallback to Path.glob - base_path = Path.cwd() - return list(base_path.glob(pattern)) - - async def _execute_agent_task( - self, - task: BatchTask, - config: BatchConfig, - progress_task: Optional[TaskID] = None, - ) -> None: - """Execute a single agent task. - - Args: - task: The task to execute - config: Batch configuration - progress_task: Optional progress bar task ID - """ - task.status = "running" - task.start_time = datetime.now() - worktree_path = None - - try: - # Setup worktree if needed - worktree_path = await self._setup_worktree(task.id, config) - - # Build the prompt for the agent - prompt = config.operation - if task.file_path: - # If using worktree, use the worktree path - if worktree_path: - file_path = worktree_path / task.file_path.relative_to(Path.cwd()) - prompt = f"{config.operation} for file: {file_path}" - else: - prompt = f"{config.operation} for file: {task.file_path}" - - # Use MCP agent tool if available - if self.mcp_client and config.use_mcp_tools: - # If using worktree, set working directory context - context = {} - if worktree_path: - context["working_dir"] = str(worktree_path) - - result = await self.mcp_client.call_tool( - "agent", - { - "prompt": prompt, - "model": config.agent_model, - "max_iterations": 5, - **context, - }, - ) - task.result = result - - # Use Hanzo client for direct AI calls - elif self.hanzo_client: - response = await self.hanzo_client.chat.completions.create( - model=config.agent_model, - messages=[ - { - "role": "system", - "content": "You are a helpful coding assistant.", - }, - {"role": "user", "content": prompt}, - ], - stream=False, - ) - task.result = response.choices[0].message.content - - else: - # Simulate agent execution for testing - await asyncio.sleep(0.1) # Simulate work - task.result = f"Completed: {prompt}" - - task.status = "completed" - - # If using worktree, merge changes back - if worktree_path and task.status == "completed": - await self._merge_worktree_changes(task.id, worktree_path) - - except asyncio.TimeoutError: - task.status = "failed" - task.error = "Task timed out" - - except Exception as e: - task.status = "failed" - task.error = str(e) - logger.error(f"Task {task.id} failed: {e}") - - finally: - task.end_time = datetime.now() - - # Cleanup worktree - if worktree_path: - await self._cleanup_worktree(task.id) - - # Update progress if available - if self._progress and progress_task is not None: - self._progress.update(progress_task, advance=1) - - # Stream result if enabled - if config.stream_results: - await self._stream_result(task) - - async def _merge_worktree_changes(self, task_id: str, worktree_path: Path) -> None: - """Merge changes from worktree back to main branch. - - Args: - task_id: Task identifier - worktree_path: Path to worktree - """ - try: - import subprocess - - # Stage and commit changes in worktree - subprocess.run( - ["git", "add", "-A"], - cwd=worktree_path, - capture_output=True, - ) - - subprocess.run( - ["git", "commit", "-m", f"Task {task_id}: Automated changes"], - cwd=worktree_path, - capture_output=True, - ) - - # Cherry-pick to main branch - result = subprocess.run( - ["git", "rev-parse", "HEAD"], - cwd=worktree_path, - capture_output=True, - text=True, - ) - - if result.returncode == 0: - commit_hash = result.stdout.strip() - subprocess.run( - ["git", "cherry-pick", commit_hash], - capture_output=True, - ) - - except Exception as e: - logger.error(f"Error merging worktree changes: {e}") - - async def _stream_result(self, task: BatchTask) -> None: - """Stream task result to console. - - Args: - task: Completed task to stream - """ - status_color = "green" if task.status == "completed" else "red" - status_icon = "โœ“" if task.status == "completed" else "โœ—" - - # Create result panel - content = task.result if task.status == "completed" else task.error - panel = Panel( - content or "No output", - title=f"[{status_color}]{status_icon}[/{status_color}] {task.id}: {task.description}", - border_style=status_color, - ) - console.print(panel) - - async def _execute_consensus( - self, - prompt: str, - models: List[str], - config: BatchConfig, - ) -> Dict[str, Any]: - """Execute consensus operation with multiple models. - - Args: - prompt: The prompt to send to all models - models: List of model names - config: Batch configuration - - Returns: - Consensus result with individual responses - """ - responses = [] - - # Execute with all models in parallel - async def get_response(model: str) -> Dict[str, Any]: - try: - if self.mcp_client: - result = await self.mcp_client.call_tool( - "llm", - { - "prompt": prompt, - "model": model, - }, - ) - return {"model": model, "response": result, "success": True} - elif self.hanzo_client: - response = await self.hanzo_client.chat.completions.create( - model=model, - messages=[{"role": "user", "content": prompt}], - ) - return { - "model": model, - "response": response.choices[0].message.content, - "success": True, - } - else: - logger.warning( - f"No API client configured - returning empty response for {model}" - ) - return { - "model": model, - "response": "", - "success": False, - "error": "No API client configured (set HANZO_API_KEY or use MCP)", - } - except Exception as e: - return {"model": model, "error": str(e), "success": False} - - # Get all responses in parallel - responses = await asyncio.gather(*[get_response(model) for model in models]) - - # Analyze consensus - successful_responses = [r for r in responses if r["success"]] - agreement_score = len(successful_responses) / len(models) if models else 0 - - # Simple consensus: majority agreement or summarize - consensus_result = { - "consensus_reached": agreement_score >= config.consensus_threshold, - "agreement_score": agreement_score, - "individual_responses": responses, - "models_used": models, - } - - # If consensus reached, combine insights - if consensus_result["consensus_reached"] and successful_responses: - combined = "\n\n".join( - [f"[{r['model']}]: {r['response']}" for r in successful_responses] - ) - consensus_result["combined_response"] = combined - - return consensus_result - - async def _execute_critic_chain( - self, - initial_content: str, - models: List[str], - config: BatchConfig, - ) -> Dict[str, Any]: - """Execute critic chain with sequential review. - - Args: - initial_content: Content to review - models: List of critic models - config: Batch configuration - - Returns: - Chain of critic reviews - """ - reviews = [] - current_content = initial_content - - for i, model in enumerate(models): - # Build critic prompt - if i == 0: - prompt = f"Please review the following:\n\n{current_content}" - else: - prompt = f"""Please review the following, taking into account previous reviews: - -Original content: -{initial_content} - -Previous reviews: -{chr(10).join([f"[{r['model']}]: {r['review']}" for r in reviews])} - -Provide your critical analysis:""" - - try: - if self.mcp_client: - # Use critic tool if available - result = await self.mcp_client.call_tool( - "critic", - { - "analysis": prompt, - "model": model, - }, - ) - review = result - else: - # Fallback to LLM - if self.hanzo_client: - response = await self.hanzo_client.chat.completions.create( - model=model, - messages=[ - { - "role": "system", - "content": "You are a thorough code critic.", - }, - {"role": "user", "content": prompt}, - ], - ) - review = response.choices[0].message.content - else: - logger.warning(f"No API client configured for critic {model}") - review = f"[No API client configured for {model}]" - - reviews.append( - { - "model": model, - "review": review, - "iteration": i + 1, - } - ) - - # Update content for next critic - current_content = review - - except Exception as e: - reviews.append( - { - "model": model, - "error": str(e), - "iteration": i + 1, - } - ) - - return { - "critic_chain": reviews, - "final_review": ( - reviews[-1]["review"] if reviews and "review" in reviews[-1] else None - ), - "models_used": models, - "chain_length": len(reviews), - } - - async def execute_batch( - self, - command: str, - stream_callback: Optional[Callable[[str], None]] = None, - ) -> Dict[str, Any]: - """Execute batch operation with parallel agents. - - Args: - command: Batch command to execute - stream_callback: Optional callback for streaming results - - Returns: - Summary of batch execution results - """ - # Parse configuration - config = BatchConfig.from_command(command) - - # Handle consensus mode - if config.consensus_mode: - console.print(f"[bold cyan]Consensus Configuration:[/bold cyan]") - console.print(f" Models: {', '.join(config.consensus_models)}") - console.print(f" Operation: {config.operation}") - console.print(f" Threshold: {config.consensus_threshold}") - - result = await self._execute_consensus( - config.operation, - config.consensus_models, - config, - ) - - # Display consensus result - if result["consensus_reached"]: - console.print("[bold green]โœ“ Consensus reached![/bold green]") - else: - console.print("[bold yellow]โš  No consensus[/bold yellow]") - - console.print(f"Agreement Score: {result['agreement_score']:.1%}") - - for resp in result["individual_responses"]: - if resp["success"]: - console.print( - Panel( - ( - resp["response"][:500] + "..." - if len(resp.get("response", "")) > 500 - else resp.get("response", "") - ), - title=f"[cyan]{resp['model']}[/cyan]", - ) - ) - - return result - - # Handle critic mode - elif config.critic_mode: - console.print(f"[bold cyan]Critic Configuration:[/bold cyan]") - console.print(f" Models: {', '.join(config.critic_models)}") - console.print(f" Chain Mode: {config.critic_chain}") - console.print(f" Operation: {config.operation}") - - if config.critic_chain: - result = await self._execute_critic_chain( - config.operation, - config.critic_models, - config, - ) - - # Display critic chain - for review in result["critic_chain"]: - if "review" in review: - console.print( - Panel( - ( - review["review"][:500] + "..." - if len(review["review"]) > 500 - else review["review"] - ), - title=f"[cyan]Critic {review['iteration']}: {review['model']}[/cyan]", - ) - ) - - return result - else: - # Parallel critics (use consensus mechanism) - result = await self._execute_consensus( - f"Please provide critical review: {config.operation}", - config.critic_models, - config, - ) - return result - - # Regular batch mode - console.print(f"[bold cyan]Batch Configuration:[/bold cyan]") - console.print(f" Batch Size: {config.batch_size}") - console.print(f" Agent Model: {config.agent_model}") - console.print(f" Operation: {config.operation}") - console.print(f" Target Pattern: {config.target_pattern}") - - # Find target files - target_files = await self._find_target_files(config.target_pattern) - console.print(f"[bold]Found {len(target_files)} files to process[/bold]") - - # Create tasks - tasks = [] - for file_path in target_files: - task = BatchTask( - id=self._generate_task_id(), - description=f"{config.operation} - {file_path.name}", - file_path=file_path, - agent_model=config.agent_model, - ) - tasks.append(task) - self.active_tasks[task.id] = task - - # Setup concurrency control - self._semaphore = asyncio.Semaphore(config.batch_size) - - # Create progress bar - with Progress( - SpinnerColumn(), - TextColumn("[progress.description]{task.description}"), - BarColumn(), - TextColumn("[progress.percentage]{task.percentage:>3.0f}%"), - console=console, - ) as progress: - self._progress = progress - progress_task = progress.add_task( - f"Processing {len(tasks)} tasks...", - total=len(tasks), - ) - - # Execute tasks with concurrency limit - async def run_with_semaphore(task: BatchTask): - async with self._semaphore: - await self._execute_agent_task(task, config, progress_task) - - # Run all tasks - await asyncio.gather( - *[run_with_semaphore(task) for task in tasks], - return_exceptions=True, - ) - - # Collect results - for task in tasks: - if task.status == "completed": - self.completed_tasks.append(task) - else: - self.failed_tasks.append(task) - del self.active_tasks[task.id] - - # Generate summary - total_duration = sum( - t.duration() or 0 for t in self.completed_tasks + self.failed_tasks - ) - - summary = { - "total_tasks": len(tasks), - "completed": len(self.completed_tasks), - "failed": len(self.failed_tasks), - "total_duration": total_duration, - "average_duration": total_duration / len(tasks) if tasks else 0, - "batch_size": config.batch_size, - "agent_model": config.agent_model, - } - - # Display summary - self._display_summary(summary) - - return summary - - def _display_summary(self, summary: Dict[str, Any]) -> None: - """Display execution summary. - - Args: - summary: Execution summary data - """ - table = Table(title="Batch Execution Summary", show_header=False) - table.add_column("Metric", style="cyan") - table.add_column("Value", style="white") - - table.add_row("Total Tasks", str(summary["total_tasks"])) - table.add_row("Completed", f"[green]{summary['completed']}[/green]") - table.add_row("Failed", f"[red]{summary['failed']}[/red]") - table.add_row("Total Duration", f"{summary['total_duration']:.2f}s") - table.add_row("Average Duration", f"{summary['average_duration']:.2f}s") - table.add_row("Batch Size", str(summary["batch_size"])) - table.add_row("Agent Model", summary["agent_model"]) - - console.print(table) - - async def stream_batch_results(self) -> AsyncIterator[BatchTask]: - """Stream batch results as they complete. - - Yields: - Completed batch tasks - """ - while self.active_tasks: - for task_id, task in list(self.active_tasks.items()): - if task.status in ["completed", "failed"]: - yield task - del self.active_tasks[task_id] - await asyncio.sleep(0.1) - - def get_status(self) -> Dict[str, Any]: - """Get current orchestrator status. - - Returns: - Status information - """ - return { - "active": len(self.active_tasks), - "completed": len(self.completed_tasks), - "failed": len(self.failed_tasks), - "active_tasks": [ - { - "id": task.id, - "description": task.description, - "status": task.status, - "duration": task.duration(), - } - for task in self.active_tasks.values() - ], - } - - -class MetaAIOrchestrator: - """Meta AI orchestrator that manages other AI agents.""" - - def __init__( - self, - primary_model: str = "claude-3-5-sonnet-20241022", - mcp_client: Optional[Any] = None, - ): - """Initialize meta AI orchestrator. - - Args: - primary_model: Primary model for meta reasoning - mcp_client: MCP client for tool access - """ - self.primary_model = primary_model - self.mcp_client = mcp_client - self.batch_orchestrator = BatchOrchestrator(mcp_client=mcp_client) - self.agent_pool: Dict[str, Any] = {} - self.task_queue: asyncio.Queue = asyncio.Queue() - self.results_queue: asyncio.Queue = asyncio.Queue() - - async def parse_and_execute(self, command: str) -> Dict[str, Any]: - """Parse natural language command and execute appropriate action. - - Args: - command: Natural language command - - Returns: - Execution results - """ - # Check if it's a batch command - if "batch:" in command or command.startswith("batch"): - return await self.batch_orchestrator.execute_batch(command) - - # Use meta AI to understand intent - intent = await self._analyze_intent(command) - - if intent["type"] == "batch_operation": - # Convert natural language to batch syntax - batch_command = self._build_batch_command(intent) - return await self.batch_orchestrator.execute_batch(batch_command) - - elif intent["type"] == "single_task": - # Execute single agent task - return await self._execute_single_task(intent) - - else: - return {"error": f"Unknown command type: {intent['type']}"} - - async def _analyze_intent(self, command: str) -> Dict[str, Any]: - """Analyze user intent from natural language. - - Args: - command: User command - - Returns: - Intent analysis - """ - # Use primary model to analyze intent - prompt = f""" - Analyze the following command and determine the intent: - Command: {command} - - Determine: - 1. Is this a batch operation (multiple files/tasks)? - 2. What is the main operation? - 3. What agent/model should be used? - 4. What are the target files/patterns? - - Return as JSON. - """ - - if self.mcp_client: - result = await self.mcp_client.call_tool( - "llm", - { - "prompt": prompt, - "model": self.primary_model, - "response_format": "json", - }, - ) - try: - return json.loads(result) - except Exception: - pass - - # Fallback intent detection - if any(word in command.lower() for word in ["all", "every", "each", "files"]): - return { - "type": "batch_operation", - "operation": command, - "model": "claude-3-5-sonnet-20241022", - "pattern": "**/*", - } - else: - return { - "type": "single_task", - "operation": command, - "model": "claude-3-5-sonnet-20241022", - } - - def _build_batch_command(self, intent: Dict[str, Any]) -> str: - """Build batch command from intent. - - Args: - intent: Analyzed intent - - Returns: - Batch command string - """ - batch_size = intent.get("batch_size", 10) - model = intent.get("model", "claude") - operation = intent.get("operation", "") - pattern = intent.get("pattern", "**/*") - - # Map model names - model_short = { - "claude-3-5-sonnet-20241022": "claude", - "gpt-4-turbo": "codex", - "gemini-1.5-pro": "gemini", - }.get(model, model) - - return f"batch:{batch_size} agent:{model_short} files:{pattern} {operation}" - - async def _execute_single_task(self, intent: Dict[str, Any]) -> Dict[str, Any]: - """Execute single agent task. - - Args: - intent: Task intent - - Returns: - Execution result - """ - task = BatchTask( - id=self.batch_orchestrator._generate_task_id(), - description=intent["operation"], - agent_model=intent.get("model", self.primary_model), - ) - - config = BatchConfig( - batch_size=1, - agent_model=task.agent_model, - operation=intent["operation"], - ) - - await self.batch_orchestrator._execute_agent_task(task, config) - - return { - "task_id": task.id, - "status": task.status, - "result": task.result, - "error": task.error, - "duration": task.duration(), - } - - -# Export main classes -__all__ = [ - "BatchTask", - "BatchConfig", - "BatchOrchestrator", - "MetaAIOrchestrator", -] diff --git a/pkg/hanzo/src/hanzo/cli.py b/pkg/hanzo/src/hanzo/cli.py deleted file mode 100644 index 100cebc64..000000000 --- a/pkg/hanzo/src/hanzo/cli.py +++ /dev/null @@ -1,665 +0,0 @@ -"""Main CLI entry point for Hanzo.""" - -import os -import sys -import shutil -import subprocess -from typing import Optional -from pathlib import Path - -import click -from rich.console import Console - -from .commands import ( - cx, - fn, - kv, - ml, - dns, - doc, - env, - iam, - k8s, - mcp, - run, - auth, - auto, - base, - chat, - flow, - jobs, - node, - o11y, - agent, - cloud, - miner, - tasks, - tools, - config, - events, - growth, - pubsub, - queues, - router, - search, - vector, - install, - network, - secrets, - storage, - platform, - git_provider, -) -from .utils.output import console - -# Version -__version__ = "0.3.48" - -HANZO_BIN = Path.home() / ".hanzo" / "bin" - - -def _find_binary(*names: str) -> str | None: - """Search PATH and ~/.hanzo/bin for the first matching binary.""" - for name in names: - path = shutil.which(name) - if path: - return path - candidate = HANZO_BIN / name - if candidate.is_file() and os.access(candidate, os.X_OK): - return str(candidate) - return None - - -def _auto_install_npm(package: str) -> bool: - """Install an npm package globally. Returns True on success.""" - npm = shutil.which("npm") or shutil.which("pnpm") - if not npm: - return False - try: - console.print(f"[cyan]Installing {package}...[/cyan]") - subprocess.run( - [npm, "install", "-g", package], - check=True, - capture_output=True, - text=True, - timeout=120, - ) - console.print(f"[green]Installed {package}[/green]") - return True - except (subprocess.CalledProcessError, subprocess.TimeoutExpired): - return False - - -def _auto_install_pip(package: str) -> bool: - """Install a Python package via uv or pip. Returns True on success.""" - uv = shutil.which("uv") - if uv: - cmd = [uv, "pip", "install", package] - else: - pip = shutil.which("pip3") or shutil.which("pip") - if not pip: - return False - cmd = [pip, "install", package] - try: - console.print(f"[cyan]Installing {package}...[/cyan]") - subprocess.run(cmd, check=True, capture_output=True, text=True, timeout=120) - console.print(f"[green]Installed {package}[/green]") - return True - except (subprocess.CalledProcessError, subprocess.TimeoutExpired): - return False - - -def _passthrough(binary: str, args: tuple[str, ...]) -> None: - """Replace the current process with the given binary.""" - os.execvp(binary, [binary] + list(args)) - - -@click.group(invoke_without_command=True) -@click.version_option(version=__version__, prog_name="hanzo") -@click.option("--verbose", "-v", is_flag=True, help="Verbose output") -@click.option("--json", is_flag=True, help="JSON output format") -@click.option("--config", "-c", type=click.Path(), help="Config file path") -@click.pass_context -def cli(ctx, verbose: bool, json: bool, config: Optional[str]): - """Hanzo AI - Unified CLI for infrastructure, deployments, and services.""" - # Ensure context object exists - ctx.ensure_object(dict) - ctx.obj["verbose"] = verbose - ctx.obj["json"] = json - ctx.obj["config"] = config - ctx.obj["console"] = console - - if ctx.invoked_subcommand is None: - click.echo(ctx.get_help()) - - -# Register command groups -cli.add_command(agent.agent_group) -cli.add_command(auth.auth_group) -cli.add_command(auto.auto_group) -cli.add_command(base.base_group) -cli.add_command(node.cluster) -cli.add_command(cloud.cloud_group) -cli.add_command(config.config_group) -cli.add_command(cx.cx_group) -cli.add_command(dns.dns_group) -cli.add_command(doc.doc_group) -cli.add_command(env.env_group) -cli.add_command(events.events_group) -cli.add_command(flow.flow_group) -cli.add_command(fn.fn_group) -cli.add_command(git_provider.git_group) -cli.add_command(growth.growth_group) -cli.add_command(iam.iam_group) -cli.add_command(install.install_group) -cli.add_command(jobs.jobs_group) -cli.add_command(k8s.k8s_group) -cli.add_command(kv.kv_group) -cli.add_command(mcp.mcp_group) -cli.add_command(miner.miner_group) -cli.add_command(ml.ml_group) -cli.add_command(chat.chat_command) -cli.add_command(network.network_group) -cli.add_command(o11y.o11y_group) -cli.add_command(platform.platform_group) -cli.add_command(pubsub.pubsub_group) -cli.add_command(queues.queues_group) -cli.add_command(router.router_group) -cli.add_command(run.run_group) -cli.add_command(search.search_group) -cli.add_command(secrets.secrets_group) -cli.add_command(storage.storage_group) # primary: hanzo s3 -cli.add_command(storage.storage_group, name="storage") # alias: hanzo storage -cli.add_command(tasks.tasks_group) -cli.add_command(tools.tools_group) -cli.add_command(vector.vector_group) - -# Aliases -cli.add_command(doc.doc_group, name="docdb") # docdb alias for doc -cli.add_command(fn.fn_group, name="fn") # fn alias for function - - -# Quick aliases -@cli.command() -@click.argument("prompt", nargs=-1, required=True) -@click.option("--model", "-m", default="llama-3.2-3b", help="Model to use") -@click.option("--local/--cloud", default=True, help="Use local or cloud model") -@click.pass_context -def ask(ctx, prompt: tuple, model: str, local: bool): - """Quick question to AI (alias for 'hanzo chat --once').""" - import asyncio - - prompt_text = " ".join(prompt) - asyncio.run(chat.ask_once(ctx, prompt_text, model, local)) - - -# Observability quick commands -@cli.command() -@click.argument("query", required=False) -@click.option("--source", "-s", help="Log source/service") -@click.option("--follow", "-f", is_flag=True, help="Follow logs") -@click.option("--level", "-l", type=click.Choice(["debug", "info", "warn", "error"])) -@click.option("--limit", "-n", default=100, help="Max results") -def log(query: str, source: str, follow: bool, level: str, limit: int): - """View service logs (shortcut for 'hanzo o11y log'). - - \b - Examples: - hanzo log # Show recent logs - hanzo log "error" -s my-api # Search for errors - hanzo log -f -s my-api # Tail logs - """ - if follow: - console.print( - f"[cyan]Tailing log{' for ' + source if source else ''}...[/cyan]" - ) - console.print("[dim]Press Ctrl+C to stop[/dim]") - elif query: - console.print(f"[cyan]Searching log for '{query}'...[/cyan]") - else: - console.print("[cyan]Recent log entries:[/cyan]") - console.print("[dim]No log entries found[/dim]") - - -@cli.command() -@click.argument("query", required=False) -@click.option("--service", "-s", help="Filter by service") -@click.option("--range", "-r", default="1h", help="Time range") -def metric(query: str, service: str, range: str): - """View service metric (shortcut for 'hanzo o11y metric'). - - \b - Examples: - hanzo metric # Show key metric - hanzo metric "http_requests_total" # Query specific metric - hanzo metric -s my-api -r 24h # Service metric for 24h - """ - console.print( - f"[cyan]Metric{' for ' + service if service else ''} (last {range}):[/cyan]" - ) - console.print("[dim]No metric found[/dim]") - - -@cli.command() -@click.argument("trace_id", required=False) -@click.option("--service", "-s", help="Filter by service") -@click.option("--min-duration", "-d", help="Min duration (e.g., 100ms)") -def trace(trace_id: str, service: str, min_duration: str): - """View distributed trace (shortcut for 'hanzo o11y trace'). - - \b - Examples: - hanzo trace # Recent trace - hanzo trace abc123 # Show specific trace - hanzo trace -s my-api -d 1s # Slow trace for service - """ - if trace_id: - console.print(f"[cyan]Trace {trace_id}:[/cyan]") - else: - console.print( - f"[cyan]Recent trace{' for ' + service if service else ''}:[/cyan]" - ) - console.print("[dim]No trace found[/dim]") - - -# Top-level login/logout shortcuts -@cli.command() -@click.option("--email", "-e", help="Email address") -@click.option("--password", "-p", help="Password (not recommended, use prompt)") -@click.option("--api-key", "-k", help="API key for direct authentication") -@click.option("--web", "-w", is_flag=True, help="Login via browser (device code flow)") -@click.option("--headless", is_flag=True, help="Headless mode - don't open browser") -@click.pass_context -def login(ctx, email, password, api_key, web, headless): - """Login to Hanzo AI (shortcut for 'hanzo auth login'). - - \b - Examples: - hanzo login # Interactive device code flow - hanzo login --web # Open browser for authentication - hanzo login -k sk-xxx # Direct API key authentication - hanzo login -e user@example.com # Email/password login - """ - ctx.invoke( - auth.login, - email=email, - password=password, - api_key=api_key, - web=web, - headless=headless, - ) - - -@cli.command() -@click.pass_context -def logout(ctx): - """Logout from Hanzo AI (shortcut for 'hanzo auth logout').""" - ctx.invoke(auth.logout) - - -@cli.command() -@click.pass_context -def whoami(ctx): - """Show current user (shortcut for 'hanzo auth whoami').""" - ctx.invoke(auth.whoami) - - -# Alias observe -> o11y -cli.add_command(o11y.o11y_group, name="observe") - - -# Alias deploy -> run -cli.add_command(run.run_group, name="deploy") - - -@cli.command() -@click.option("--name", "-n", default="hanzo-local", help="Node name") -@click.option("--port", "-p", default=8000, help="API port") -@click.pass_context -def serve(ctx, name: str, port: int): - """Start local AI node (alias for 'hanzo node start').""" - import asyncio - - asyncio.run(node.start_node(ctx, name, port)) - - -@cli.command( - context_settings={ - "ignore_unknown_options": True, - "allow_extra_args": True, - "allow_interspersed_args": False, - }, -) -@click.argument("args", nargs=-1, type=click.UNPROCESSED) -def net(args: tuple[str, ...]): - """Start a Hanzo Network compute node (delegates to hanzod). - - All arguments are passed through to the hanzod binary. - - \b - Examples: - hanzo net Start compute node - hanzo net --port 8080 Start on custom port - hanzo net --help Show hanzod help - """ - binary = _find_binary("hanzod") - if not binary: - console.print("[yellow]hanzod not found. Installing...[/yellow]") - if _auto_install_npm("@hanzo/hanzod"): - binary = _find_binary("hanzod") - if not binary: - console.print("[red]Failed to install hanzod.[/red]") - console.print("Install manually: curl -fsSL https://hanzo.sh | bash") - raise SystemExit(1) - _passthrough(binary, args) - - -@cli.command( - context_settings={ - "ignore_unknown_options": True, - "allow_extra_args": True, - "allow_interspersed_args": False, - }, -) -@click.argument("args", nargs=-1, type=click.UNPROCESSED) -def dev(args: tuple[str, ...]): - """Hanzo Dev โ€” AI coding assistant (delegates to @hanzo/dev). - - All arguments are passed through to the hanzo-dev binary. - - \b - Examples: - hanzo dev Start Hanzo Dev - hanzo dev mcp list List MCP servers - hanzo dev mcp add Add MCP server - hanzo dev mcp remove Remove MCP server - hanzo dev --help Show dev binary help - """ - binary = _find_binary("dev", "hanzo-dev") - if not binary: - console.print("[yellow]hanzo-dev not found. Installing @hanzo/dev...[/yellow]") - if _auto_install_npm("@hanzo/dev"): - binary = _find_binary("dev", "hanzo-dev") - if not binary: - console.print("[red]Failed to install @hanzo/dev.[/red]") - console.print("Install manually: npm install -g @hanzo/dev") - console.print(" or: curl -fsSL https://hanzo.sh | bash") - raise SystemExit(1) - _passthrough(binary, args) - - -cli.add_command(net, name="node") # hanzo node = hanzo net - - -@cli.command() -@click.pass_context -def dashboard(ctx): - """Open interactive dashboard.""" - from .interactive.dashboard import run_dashboard - - run_dashboard() - - -@cli.command() -@click.option("--json", "json_output", is_flag=True, help="JSON output format") -@click.pass_context -def doctor(ctx, json_output: bool): - """Show installed Hanzo tools, versions, and system info. - - Checks all Hanzo CLI tools installed via uv, cargo, npm, or homebrew. - """ - import shutil - import platform - - from rich.panel import Panel - from rich.table import Table - - console = ctx.obj.get("console", Console()) - - if json_output: - import json as json_module - - result = {"tools": [], "system": {}} - - # System info - console.print( - Panel.fit( - f"[bold cyan]Hanzo Doctor[/bold cyan]\n[dim]System: {platform.system()} {platform.machine()}[/dim]", - border_style="cyan", - ) - ) - console.print() - - # Check uv tools - tools_found = [] - - console.print("[bold]Python Tools (uv):[/bold]") - try: - result_uv = subprocess.run( - ["uv", "tool", "list"], capture_output=True, text=True, timeout=10 - ) - if result_uv.returncode == 0: - table = Table(show_header=True, header_style="bold") - table.add_column("Package", style="cyan") - table.add_column("Version", style="green") - table.add_column("Path", style="dim") - - for line in result_uv.stdout.strip().split("\n"): - if line.startswith("hanzo"): - parts = line.split() - if len(parts) >= 2: - name, version = parts[0], parts[1] - path = shutil.which(name) or f"~/.local/bin/{name}" - table.add_row(name, version, path) - tools_found.append( - { - "name": name, - "version": version, - "path": path, - "source": "uv", - } - ) - - if tools_found: - console.print(table) - else: - console.print(" [dim](none installed)[/dim]") - else: - console.print(" [dim](uv not available)[/dim]") - except (subprocess.TimeoutExpired, FileNotFoundError): - console.print(" [dim](uv not installed)[/dim]") - - console.print() - - # Check for other AI CLI tools - console.print("[bold]AI CLI Tools:[/bold]") - ai_tools = [ - ("claude", "Claude Code"), - ("gemini", "Gemini CLI"), - ("codex", "OpenAI Codex"), - ("cursor", "Cursor"), - ("aider", "Aider"), - ] - - ai_table = Table(show_header=True, header_style="bold") - ai_table.add_column("Tool", style="cyan") - ai_table.add_column("Version", style="green") - ai_table.add_column("Path", style="dim") - - ai_found = False - for cmd, name in ai_tools: - path = shutil.which(cmd) - if path: - ai_found = True - try: - ver_result = subprocess.run( - [cmd, "--version"], capture_output=True, text=True, timeout=5 - ) - version = ( - ver_result.stdout.strip().split("\n")[0] - if ver_result.returncode == 0 - else "?" - ) - except Exception: - version = "?" - ai_table.add_row(name, version, path) - - if ai_found: - console.print(ai_table) - else: - console.print(" [dim](none detected)[/dim]") - - console.print() - - # Check API keys - console.print("[bold]API Keys:[/bold]") - api_keys = [ - "ANTHROPIC_API_KEY", - "OPENAI_API_KEY", - "GOOGLE_API_KEY", - "GEMINI_API_KEY", - "XAI_API_KEY", - "GROQ_API_KEY", - "GITHUB_TOKEN", - "HF_TOKEN", - ] - - keys_found = [] - for key in api_keys: - if os.environ.get(key): - keys_found.append(key) - console.print(f" [green]โœ“[/green] {key}") - - if not keys_found: - console.print(" [dim](none set)[/dim]") - - console.print() - console.print("[bold green]โœ“[/bold green] Doctor check complete") - - if json_output: - result["tools"] = tools_found - result["system"] = { - "platform": platform.system(), - "arch": platform.machine(), - "python": platform.python_version(), - } - print(json_module.dumps(result, indent=2)) - - -@cli.command() -@click.option("--all", "upgrade_all", is_flag=True, help="Upgrade all Hanzo tools") -@click.option("--force", "-f", is_flag=True, help="Force reinstall") -@click.argument("packages", nargs=-1) -@click.pass_context -def update(ctx, upgrade_all: bool, force: bool, packages: tuple): - """Update Hanzo CLI tools to latest versions. - - \b - Examples: - hanzo update # Update hanzo and hanzo-mcp - hanzo update --all # Update all Hanzo tools - hanzo update hanzo-mcp # Update specific package - hanzo update --force # Force reinstall - """ - console = ctx.obj.get("console", Console()) - - # Default packages to update - default_packages = ["hanzo", "hanzo-mcp"] - all_packages = [ - "hanzo", - "hanzo-mcp", - "hanzo-agents", - "hanzo-memory", - "hanzo-network", - ] - - if packages: - to_update = list(packages) - elif upgrade_all: - to_update = all_packages - else: - to_update = default_packages - - console.print("[bold cyan]Updating Hanzo CLI tools...[/bold cyan]") - console.print() - - # Check if uv is available - import shutil - - if not shutil.which("uv"): - console.print("[red]Error:[/red] uv is not installed") - console.print("Install with: curl -LsSf https://astral.sh/uv/install.sh | sh") - return - - success = [] - failed = [] - - for pkg in to_update: - console.print(f" Updating [cyan]{pkg}[/cyan]...", end=" ") - try: - cmd = ["uv", "tool", "upgrade" if not force else "install", pkg] - if force: - cmd.append("--force") - - result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) - - if result.returncode == 0: - # Get new version - ver_result = subprocess.run( - ["uv", "tool", "list"], capture_output=True, text=True, timeout=10 - ) - version = "?" - for line in ver_result.stdout.split("\n"): - if line.startswith(pkg + " "): - version = line.split()[1] if len(line.split()) > 1 else "?" - break - - console.print(f"[green]โœ“[/green] {version}") - success.append(pkg) - else: - # Try install if upgrade failed (package not installed) - if "not installed" in result.stderr.lower(): - install_result = subprocess.run( - ["uv", "tool", "install", pkg], - capture_output=True, - text=True, - timeout=120, - ) - if install_result.returncode == 0: - console.print("[green]โœ“[/green] installed") - success.append(pkg) - else: - console.print(f"[red]โœ—[/red] {install_result.stderr.strip()}") - failed.append(pkg) - else: - console.print(f"[red]โœ—[/red] {result.stderr.strip()}") - failed.append(pkg) - - except subprocess.TimeoutExpired: - console.print("[red]โœ—[/red] timeout") - failed.append(pkg) - except Exception as e: - console.print(f"[red]โœ—[/red] {e}") - failed.append(pkg) - - console.print() - if success: - console.print(f"[bold green]โœ“[/bold green] Updated: {', '.join(success)}") - if failed: - console.print(f"[bold red]โœ—[/bold red] Failed: {', '.join(failed)}") - - -def main(): - """Main entry point.""" - try: - cli(auto_envvar_prefix="HANZO") - except KeyboardInterrupt: - console.print("\n[yellow]Interrupted by user[/yellow]") - sys.exit(0) - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo/src/hanzo/commands/__init__.py b/pkg/hanzo/src/hanzo/commands/__init__.py deleted file mode 100644 index aea745ae8..000000000 --- a/pkg/hanzo/src/hanzo/commands/__init__.py +++ /dev/null @@ -1,44 +0,0 @@ -"""Command modules for Hanzo CLI.""" - -from . import flow, install - -__all__ = [ - "agent", - "auth", - "auto", - "base", - "chat", - "cluster", - "cloud", - "config", - "cx", - "dns", - "doc", - "env", - "events", - "flow", - "fn", - "growth", - "install", - "k8s", - "iam", - "jobs", - "kv", - "mcp", - "miner", - "ml", - "network", - "node", - "o11y", - "platform", - "pubsub", - "queues", - "router", - "run", - "search", - "secrets", - "storage", - "tasks", - "tools", - "vector", -] diff --git a/pkg/hanzo/src/hanzo/commands/agent.py b/pkg/hanzo/src/hanzo/commands/agent.py deleted file mode 100644 index 2455bf0de..000000000 --- a/pkg/hanzo/src/hanzo/commands/agent.py +++ /dev/null @@ -1,122 +0,0 @@ -"""Agent management commands.""" - -import asyncio -from typing import Optional - -import click -from rich.table import Table - -from ..utils.output import console, handle_errors - - -@click.group(name="agent") -def agent_group(): - """Manage AI agents.""" - pass - - -@agent_group.command() -@click.option("--name", "-n", required=True, help="Agent name") -@click.option("--model", "-m", default="llama-3.2-3b", help="Model to use") -@click.option("--description", "-d", help="Agent description") -@click.option("--local/--cloud", default=True, help="Use local or cloud model") -@click.pass_context -@handle_errors -async def create(ctx, name: str, model: str, description: Optional[str], local: bool): - """Create a new agent.""" - try: - from hanzoai.agents import create_agent - except ImportError: - console.print("[red]Error:[/red] hanzo-agents not installed") - console.print("Install with: pip install hanzo[agents]") - return - - base_url = "http://localhost:8000" if local else None - - with console.status(f"Creating agent '{name}'..."): - agent = create_agent(name=name, model=model, base_url=base_url) - - console.print(f"[green]โœ“[/green] Created agent: {name}") - console.print(f" Model: {model}") - console.print(f" Mode: {'local' if local else 'cloud'}") - - -@agent_group.command() -@click.pass_context -def list(ctx): - """List available agents.""" - table = Table(title="Available Agents") - table.add_column("Name", style="cyan", no_wrap=True) - table.add_column("Model", style="green") - table.add_column("Status", style="yellow") - table.add_column("Description") - - # Try to get agents from registry - agents = [] - try: - from hanzoai.agents import list_agents - - agents = list_agents() - except ImportError: - console.print("[dim]Install hanzo-agents for full registry support[/dim]") - except Exception as e: - console.print(f"[dim]Could not connect to registry: {e}[/dim]") - - if not agents: - console.print("[yellow]No agents registered. Create one with:[/yellow]") - console.print(" hanzo agent create --name myagent --model llama-3.2-3b") - return - - for agent in agents: - table.add_row( - agent.get("name", "unknown"), - agent.get("model", "unknown"), - agent.get("status", "unknown"), - agent.get("description", ""), - ) - - console.print(table) - - -@agent_group.command() -@click.argument("agents", nargs=-1, required=True) -@click.option("--task", "-t", required=True, help="Task to execute") -@click.option("--parallel", "-p", is_flag=True, help="Run agents in parallel") -@click.option("--timeout", type=int, help="Timeout in seconds") -@click.pass_context -@handle_errors -async def run(ctx, agents: tuple, task: str, parallel: bool, timeout: Optional[int]): - """Run a task with specified agents.""" - try: - from hanzoai.agents import create_network - except ImportError: - console.print("[red]Error:[/red] hanzo-agents not installed") - console.print("Install with: pip install hanzo[agents]") - return - - agent_list = list(agents) - - with console.status(f"Running task with {len(agent_list)} agents..."): - # Create network with agents - network = create_network(agents=agent_list) - - # Run task - result = ( - await asyncio.wait_for(network.run(task), timeout=timeout) - if timeout - else await network.run(task) - ) - - console.print("[green]Task completed![/green]") - console.print(result) - - -@agent_group.command() -@click.argument("agent") -@click.pass_context -def delete(ctx, agent: str): - """Delete an agent.""" - if click.confirm(f"Delete agent '{agent}'?"): - console.print(f"[yellow]Deleted agent: {agent}[/yellow]") - else: - console.print("Cancelled") diff --git a/pkg/hanzo/src/hanzo/commands/auth.py b/pkg/hanzo/src/hanzo/commands/auth.py deleted file mode 100644 index e3b626bd4..000000000 --- a/pkg/hanzo/src/hanzo/commands/auth.py +++ /dev/null @@ -1,841 +0,0 @@ -"""Authentication commands for Hanzo CLI.""" - -import os -import json -import secrets -import threading -import webbrowser -from typing import Optional -from pathlib import Path -from datetime import datetime -from http.server import HTTPServer, BaseHTTPRequestHandler -from urllib.parse import parse_qs, urlparse, urlencode - -import click -from rich import box -from rich.panel import Panel -from rich.table import Table - -from ..utils.output import console - -# OAuth constants -IAM_URL = "https://hanzo.id" -IAM_CLIENT_ID = "app-hanzo" -CALLBACK_PORT = 1456 -CALLBACK_PATH = "/callback" -CALLBACK_URI = f"http://localhost:{CALLBACK_PORT}{CALLBACK_PATH}" - - -class AuthManager: - """Manage Hanzo authentication.""" - - def __init__(self): - self.config_dir = Path.home() / ".hanzo" - self.auth_file = self.config_dir / "auth.json" - - def load_auth(self) -> dict: - """Load authentication data.""" - if self.auth_file.exists(): - try: - return json.loads(self.auth_file.read_text()) - except Exception: - pass - return {} - - def save_auth(self, auth: dict): - """Save authentication data.""" - self.config_dir.mkdir(exist_ok=True) - self.auth_file.write_text(json.dumps(auth, indent=2)) - - def is_authenticated(self) -> bool: - """Check if authenticated.""" - if os.getenv("HANZO_API_KEY"): - return True - auth = self.load_auth() - return bool( - auth.get("api_key") - or auth.get("logged_in") - or auth.get("token") - or auth.get("tokens", {}).get("access_token") - ) - - def get_api_key(self) -> Optional[str]: - """Get API key or access token.""" - if os.getenv("HANZO_API_KEY"): - return os.getenv("HANZO_API_KEY") - auth = self.load_auth() - return ( - auth.get("api_key") - or auth.get("token") - or auth.get("tokens", {}).get("access_token") - ) - - -@click.group(name="auth") -def auth_group(): - """Manage Hanzo authentication. - - \b - Login & Identity: - hanzo auth login # Login to Hanzo - hanzo auth logout # Logout - hanzo auth status # Show auth status - hanzo auth whoami # Show current user - - \b - For managing users, orgs, teams, and API keys: - hanzo iam users list # List users - hanzo iam orgs list # List organizations - hanzo iam teams list # List teams - hanzo iam keys list # List API keys - """ - pass - - -@auth_group.command() -@click.option("--api-key", "-k", help="API key for direct authentication") -@click.option("--device-code", is_flag=True, help="Device code flow (for SSH/headless)") -@click.option("--headless", is_flag=True, help="Don't open browser automatically") -@click.pass_context -def login(ctx, api_key: str, device_code: bool, headless: bool): - """Login to Hanzo AI. - - Opens your browser to hanzo.id where you can sign in with - email/password, GitHub, Google, or any configured provider. - - \b - Examples: - hanzo auth login # Browser login (default) - hanzo auth login --device-code # Device code for SSH/headless - hanzo auth login -k sk-xxx # Direct API key - """ - auth_mgr = AuthManager() - - try: - if api_key: - auth = auth_mgr.load_auth() - auth.update( - { - "api_key": api_key, - "logged_in": True, - "last_login": datetime.now().isoformat(), - } - ) - auth_mgr.save_auth(auth) - console.print(f"You are now logged in with API key {api_key[:8]}***.") - - elif device_code: - _login_device_code(auth_mgr, headless) - - else: - _login_browser_oauth(auth_mgr) - - except Exception as e: - console.print(f"[red]Login failed: {e}[/red]") - - -def _get_iam_url(auth_mgr: AuthManager) -> str: - """Get the IAM URL from env or stored config.""" - existing = auth_mgr.load_auth() - return os.getenv("IAM_URL", existing.get("iam_url", IAM_URL)) - - -def _decode_jwt_claims(token: str) -> dict: - """Decode JWT payload without verification (for extracting email/name).""" - import base64 - - try: - parts = token.split(".") - if len(parts) < 2: - return {} - payload = parts[1] - # Add padding - payload += "=" * (4 - len(payload) % 4) - decoded = base64.urlsafe_b64decode(payload) - return json.loads(decoded) - except Exception: - return {} - - -def _login_browser_oauth(auth_mgr: AuthManager): - """Browser-based OAuth login using only stdlib (like gcloud auth login).""" - import urllib.request - - iam_url = _get_iam_url(auth_mgr) - state = secrets.token_urlsafe(32) - - # Result container for the callback handler - auth_result = {"code": None, "error": None} - server_ready = threading.Event() - - class CallbackHandler(BaseHTTPRequestHandler): - def do_GET(self): - parsed = urlparse(self.path) - if parsed.path != CALLBACK_PATH: - self.send_response(404) - self.end_headers() - return - - params = parse_qs(parsed.query) - - # Verify state - if params.get("state", [None])[0] != state: - self.send_response(400) - self.end_headers() - self.wfile.write(b"Invalid state parameter.") - auth_result["error"] = "Invalid state" - return - - if "error" in params: - self.send_response(400) - self.end_headers() - msg = params.get("error_description", params["error"])[0] - self.wfile.write(msg.encode()) - auth_result["error"] = msg - return - - auth_result["code"] = params.get("code", [None])[0] - - self.send_response(200) - self.send_header("Content-Type", "text/html") - self.end_headers() - self.wfile.write( - b"

          Authentication successful!

          " - b"

          You can close this window and return to the terminal.

          " - b"" - ) - - def log_message(self, format, *args): - pass # Suppress server logs - - # Start local callback server - server = HTTPServer(("localhost", CALLBACK_PORT), CallbackHandler) - server.timeout = 120 # 2 minute timeout - - def serve(): - server_ready.set() - server.handle_request() # Handle exactly one request - - server_thread = threading.Thread(target=serve, daemon=True) - server_thread.start() - server_ready.wait() - - # Build OAuth authorize URL - params = { - "client_id": IAM_CLIENT_ID, - "redirect_uri": CALLBACK_URI, - "response_type": "code", - "scope": "openid profile email", - "state": state, - } - authorize_url = f"{iam_url}/oauth/authorize?{urlencode(params)}" - - console.print("Your browser has been opened to visit:\n") - console.print(f" {authorize_url}\n") - - webbrowser.open(authorize_url) - - # Wait for callback - server_thread.join(timeout=120) - server.server_close() - - if auth_result["error"]: - console.print(f"[red]Login failed: {auth_result['error']}[/red]") - return - - if not auth_result["code"]: - console.print("[red]Login timed out. Please try again.[/red]") - return - - # Exchange authorization code for tokens - token_url = f"{iam_url}/oauth/token" - token_data = urlencode( - { - "client_id": IAM_CLIENT_ID, - "code": auth_result["code"], - "grant_type": "authorization_code", - "redirect_uri": CALLBACK_URI, - } - ).encode() - - req = urllib.request.Request( # noqa: S310 - token_url, - data=token_data, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - method="POST", - ) - - try: - with urllib.request.urlopen(req, timeout=30) as resp: # noqa: S310 - data = json.loads(resp.read().decode()) - except Exception as e: - console.print(f"[red]Token exchange failed: {e}[/red]") - return - - access_token = data.get("access_token", "") - id_token = data.get("id_token", "") - - # Decode JWT to get user info - claims = _decode_jwt_claims(id_token or access_token) - user_email = claims.get("email", claims.get("name", "")) - - # Save auth - auth = auth_mgr.load_auth() - auth.update( - { - "token": access_token, - "id_token": id_token, - "email": user_email, - "logged_in": True, - "last_login": datetime.now().isoformat(), - } - ) - auth_mgr.save_auth(auth) - - if user_email: - console.print(f"You are now logged in as [{user_email}].") - else: - console.print("You are now logged in.") - console.print("Your credentials have been saved to: ~/.hanzo/auth.json") - - -def _login_device_code(auth_mgr: AuthManager, headless: bool): - """Device code login flow (for SSH/headless).""" - import time - import urllib.request - - iam_url = _get_iam_url(auth_mgr) - - # Step 1: Request device code - device_req_data = json.dumps( - { - "client_id": IAM_CLIENT_ID, - "scope": "openid profile email", - } - ).encode() - - req = urllib.request.Request( # noqa: S310 - f"{iam_url}/api/device/code", - data=device_req_data, - headers={"Content-Type": "application/json"}, - method="POST", - ) - - try: - with urllib.request.urlopen(req, timeout=30) as resp: # noqa: S310 - data = json.loads(resp.read().decode()) - except Exception as e: - console.print(f"[red]Failed to request device code: {e}[/red]") - return - - device_code = data["device_code"] - user_code = data["user_code"] - verification_url = data.get("verification_uri", f"{iam_url}/device") - verification_url_complete = data.get( - "verification_uri_complete", f"{verification_url}?user_code={user_code}" - ) - expires_in = data.get("expires_in", 300) - interval = data.get("interval", 5) - - # Step 2: Show instructions - console.print(f"\nTo sign in, visit: [cyan]{verification_url}[/cyan]") - console.print(f"Enter code: [bold yellow]{user_code}[/bold yellow]\n") - - if not headless: - try: - webbrowser.open(verification_url_complete) - console.print("[dim](Browser opened automatically)[/dim]\n") - except Exception: - pass - - # Step 3: Poll for completion - start_time = time.time() - while time.time() - start_time < expires_in: - time.sleep(interval) - - poll_data = json.dumps( - { - "client_id": IAM_CLIENT_ID, - "device_code": device_code, - "grant_type": "urn:ietf:params:oauth:grant-type:device_code", - } - ).encode() - - poll_req = urllib.request.Request( # noqa: S310 - f"{iam_url}/oauth/token", - data=poll_data, - headers={"Content-Type": "application/json"}, - method="POST", - ) - - try: - with urllib.request.urlopen(poll_req, timeout=30) as resp: # noqa: S310 - token_data = json.loads(resp.read().decode()) - - access_token = token_data.get("access_token", "") - id_token = token_data.get("id_token", "") - - claims = _decode_jwt_claims(id_token or access_token) - user_email = claims.get("email", claims.get("name", "")) - - auth = auth_mgr.load_auth() - auth.update( - { - "token": access_token, - "id_token": id_token, - "email": user_email, - "logged_in": True, - "last_login": datetime.now().isoformat(), - } - ) - auth_mgr.save_auth(auth) - - if user_email: - console.print(f"You are now logged in as [{user_email}].") - else: - console.print("You are now logged in.") - return - - except urllib.error.HTTPError as e: - if e.code == 400: - try: - error_data = json.loads(e.read().decode()) - error = error_data.get("error", "") - if error == "authorization_pending": - continue - elif error == "slow_down": - interval += 5 - continue - elif error == "expired_token": - console.print( - "[red]Device code expired. Please try again.[/red]" - ) - return - elif error == "access_denied": - console.print("[red]Authentication denied.[/red]") - return - except Exception: - continue - else: - continue - except Exception: - continue - - console.print("[red]Authentication timed out. Please try again.[/red]") - - -@auth_group.command() -@click.pass_context -def logout(ctx): - """Logout from Hanzo AI.""" - auth_mgr = AuthManager() - - if not auth_mgr.is_authenticated(): - console.print("[yellow]Not logged in[/yellow]") - return - - try: - # Clear login state but preserve IAM config - auth = auth_mgr.load_auth() - preserved = {} - for key in ( - "iam_url", - "iam_client_id", - "iam_client_secret", - "iam_org", - "iam_app", - ): - if key in auth: - preserved[key] = auth[key] - auth_mgr.save_auth(preserved) - - console.print("[green]โœ“[/green] Logged out successfully") - - except Exception as e: - console.print(f"[red]Logout failed: {e}[/red]") - - -@auth_group.command() -@click.pass_context -def status(ctx): - """Show authentication status.""" - auth_mgr = AuthManager() - - # Create status table - table = Table(title="Authentication Status", box=box.ROUNDED) - table.add_column("Property", style="cyan") - table.add_column("Value", style="white") - - if auth_mgr.is_authenticated(): - auth = auth_mgr.load_auth() - - table.add_row("Status", "โœ… Authenticated") - - # Show auth method - if os.getenv("HANZO_API_KEY"): - table.add_row("Method", "Environment Variable") - api_key = os.getenv("HANZO_API_KEY") - table.add_row("API Key", f"{api_key[:8]}...{api_key[-4:]}") - elif auth.get("api_key"): - table.add_row("Method", "API Key") - table.add_row("API Key", f"{auth['api_key'][:8]}...") - elif auth.get("token") or auth.get("tokens", {}).get("access_token"): - table.add_row("Method", "IAM OAuth") - token = auth.get("token") or auth.get("tokens", {}).get("access_token", "") - if token: - claims = _decode_jwt_claims(token) - email = claims.get("email", "") - if email: - table.add_row("User", email) - elif auth.get("email"): - table.add_row("Method", "Email/Password") - table.add_row("Email", auth["email"]) - - if auth.get("last_login"): - table.add_row("Last Login", auth["last_login"]) - - # Show current org if set - if auth.get("current_org"): - table.add_row("Organization", auth["current_org"]) - - else: - table.add_row("Status", "โŒ Not authenticated") - table.add_row("Action", "Run 'hanzo auth login' to authenticate") - - console.print(table) - - -@auth_group.command() -def whoami(): - """Show current user information.""" - auth_mgr = AuthManager() - - if not auth_mgr.is_authenticated(): - console.print("[yellow]Not logged in[/yellow]") - console.print("[dim]Run 'hanzo auth login' to authenticate[/dim]") - return - - auth = auth_mgr.load_auth() - lines = [] - - # Try to get email from stored data or JWT claims - email = auth.get("email", "") - if not email: - token = auth.get("token") or auth.get("tokens", {}).get("access_token", "") - if token: - claims = _decode_jwt_claims(token) - email = claims.get("email", "") - name = claims.get("displayName") or claims.get("name", "") - if name: - lines.append(f"[cyan]Name:[/cyan] {name}") - - if email: - lines.append(f"[cyan]Email:[/cyan] {email}") - - if os.getenv("HANZO_API_KEY"): - lines.append("[cyan]Auth:[/cyan] API key (env)") - elif auth.get("api_key"): - lines.append(f"[cyan]Auth:[/cyan] API key ({auth['api_key'][:8]}...)") - elif auth.get("token") or auth.get("tokens", {}).get("access_token"): - lines.append("[cyan]Auth:[/cyan] IAM OAuth") - - if auth.get("last_login"): - lines.append(f"[cyan]Last Login:[/cyan] {auth['last_login']}") - - content = "\n".join(lines) if lines else "[dim]No user information available[/dim]" - console.print( - Panel(content, title="[bold cyan]User Information[/bold cyan]", box=box.ROUNDED) - ) - - -@auth_group.command(name="set-key") -@click.argument("api_key") -def set_key(api_key: str): - """Set API key for authentication.""" - auth_mgr = AuthManager() - - auth = auth_mgr.load_auth() - auth["api_key"] = api_key - auth["logged_in"] = True - auth["last_login"] = datetime.now().isoformat() - - auth_mgr.save_auth(auth) - - console.print("[green]โœ“[/green] API key saved successfully") - console.print("[dim]You can now use Hanzo Cloud services[/dim]") - - -# ============================================================================ -# Context management (org / project / env selection) -# ============================================================================ - - -@auth_group.group(name="context", invoke_without_command=True) -@click.pass_context -def context_group(ctx): - """Manage active org/project/env context. - - \b - Examples: - hanzo auth context # Show current context - hanzo auth context set --org hanzo # Set active org/project/env - hanzo auth context list # List available orgs & projects - """ - if ctx.invoked_subcommand is None: - _print_context() - - -@context_group.command(name="show") -def context_show(): - """Show current context.""" - _print_context() - - -def _print_context(): - from ..utils.api_client import load_context - - ctx = load_context() - if not ctx: - console.print("[yellow]No context set.[/yellow]") - console.print( - "[dim]Run 'hanzo auth context set --org ORG --project PROJECT --env ENV'[/dim]" - ) - return - - table = Table(title="Active Context", box=box.ROUNDED) - table.add_column("Property", style="cyan") - table.add_column("Value", style="white") - - for key in ( - "org_id", - "org_name", - "project_id", - "project_name", - "env_id", - "env_name", - ): - if ctx.get(key): - label = key.replace("_", " ").title() - table.add_row(label, ctx[key]) - - console.print(table) - - -@context_group.command(name="set") -@click.option("--org", "-o", required=True, help="Organization ID or name") -@click.option("--project", "-p", help="Project ID or name") -@click.option("--env", "-e", help="Environment ID or name") -def context_set(org: str, project: str, env: str): - """Set active org/project/env context. - - \b - Examples: - hanzo auth context set --org hanzo - hanzo auth context set --org hanzo --project myapp --env development - """ - from ..utils.api_client import ( - PaaSClient, - env_url, - org_url, - project_url, - save_context, - ) - - try: - client = PaaSClient() - except SystemExit: - return - - ctx = {} - - # Resolve org - with console.status("Resolving organization..."): - data = client.get(org_url()) - if data is None: - return - - orgs = data if isinstance(data, list) else data.get("orgs", data.get("data", [])) - matched_org = None - for o in orgs: - oid = o.get("_id") or o.get("iid") or o.get("id", "") - oname = o.get("name", "") - if org in (oid, oname): - matched_org = o - break - - if not matched_org: - console.print(f"[red]Organization '{org}' not found.[/red]") - return - - ctx["org_id"] = ( - matched_org.get("_id") or matched_org.get("iid") or matched_org.get("id") - ) - ctx["org_name"] = matched_org.get("name", org) - console.print(f"[green]โœ“[/green] Organization: {ctx['org_name']}") - - # Resolve project (if given) - if project: - with console.status("Resolving project..."): - data = client.get(project_url(ctx["org_id"])) - if data is None: - save_context(ctx) - return - - projects = ( - data - if isinstance(data, list) - else data.get("projects", data.get("data", [])) - ) - matched_proj = None - for p in projects: - pid = p.get("_id") or p.get("iid") or p.get("id", "") - pname = p.get("name", "") - if project in (pid, pname): - matched_proj = p - break - - if not matched_proj: - console.print( - f"[yellow]Project '{project}' not found. Saving org-only context.[/yellow]" - ) - save_context(ctx) - return - - ctx["project_id"] = ( - matched_proj.get("_id") or matched_proj.get("iid") or matched_proj.get("id") - ) - ctx["project_name"] = matched_proj.get("name", project) - console.print(f"[green]โœ“[/green] Project: {ctx['project_name']}") - - # Resolve env (if given and project was resolved) - if env and ctx.get("project_id"): - with console.status("Resolving environment..."): - data = client.get(env_url(ctx["org_id"], ctx["project_id"])) - if data is None: - save_context(ctx) - return - - envs = ( - data - if isinstance(data, list) - else data.get("environments", data.get("data", [])) - ) - matched_env = None - for e in envs: - eid = e.get("_id") or e.get("iid") or e.get("id", "") - ename = e.get("name", "") - if env in (eid, ename): - matched_env = e - break - - if not matched_env: - console.print( - f"[yellow]Environment '{env}' not found. Saving org+project context.[/yellow]" - ) - save_context(ctx) - return - - ctx["env_id"] = ( - matched_env.get("_id") or matched_env.get("iid") or matched_env.get("id") - ) - ctx["env_name"] = matched_env.get("name", env) - console.print(f"[green]โœ“[/green] Environment: {ctx['env_name']}") - - save_context(ctx) - console.print("[green]โœ“[/green] Context saved to ~/.hanzo/context.json") - - -@context_group.command(name="list") -def context_list(): - """List available orgs, projects, and environments.""" - from ..utils.api_client import ( - PaaSClient, - env_url, - org_url, - project_url, - load_context, - ) - - try: - client = PaaSClient() - except SystemExit: - return - - current = load_context() - - # List orgs - with console.status("Fetching organizations..."): - data = client.get(org_url()) - if data is None: - return - - orgs = data if isinstance(data, list) else data.get("orgs", data.get("data", [])) - - table = Table(title="Organizations", box=box.ROUNDED) - table.add_column("", style="green", width=2) - table.add_column("ID", style="cyan") - table.add_column("Name", style="white") - - for o in orgs: - oid = o.get("_id") or o.get("iid") or o.get("id", "") - oname = o.get("name", "") - active = "โ—" if oid == current.get("org_id") else "" - table.add_row(active, oid, oname) - - console.print(table) - - # List projects for active org - if current.get("org_id"): - with console.status("Fetching projects..."): - data = client.get(project_url(current["org_id"])) - - if data: - projects = ( - data - if isinstance(data, list) - else data.get("projects", data.get("data", [])) - ) - if projects: - ptable = Table( - title=f"Projects in {current.get('org_name', current['org_id'])}", - box=box.ROUNDED, - ) - ptable.add_column("", style="green", width=2) - ptable.add_column("ID", style="cyan") - ptable.add_column("Name", style="white") - - for p in projects: - pid = p.get("_id") or p.get("iid") or p.get("id", "") - pname = p.get("name", "") - active = "โ—" if pid == current.get("project_id") else "" - ptable.add_row(active, pid, pname) - - console.print(ptable) - - # List envs for active project - if current.get("org_id") and current.get("project_id"): - with console.status("Fetching environments..."): - data = client.get(env_url(current["org_id"], current["project_id"])) - - if data: - envs = ( - data - if isinstance(data, list) - else data.get("environments", data.get("data", [])) - ) - if envs: - etable = Table( - title=f"Environments in {current.get('project_name', current['project_id'])}", - box=box.ROUNDED, - ) - etable.add_column("", style="green", width=2) - etable.add_column("ID", style="cyan") - etable.add_column("Name", style="white") - - for e in envs: - eid = e.get("_id") or e.get("iid") or e.get("id", "") - ename = e.get("name", "") - active = "โ—" if eid == current.get("env_id") else "" - etable.add_row(active, eid, ename) - - console.print(etable) diff --git a/pkg/hanzo/src/hanzo/commands/auto.py b/pkg/hanzo/src/hanzo/commands/auto.py deleted file mode 100644 index 4da552f1f..000000000 --- a/pkg/hanzo/src/hanzo/commands/auto.py +++ /dev/null @@ -1,687 +0,0 @@ -"""Hanzo Auto - AI-powered workflow automation CLI. - -Based on Activepieces with 280+ integrations. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -AUTO_URL = os.getenv("HANZO_AUTO_URL", "https://auto.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(AUTO_URL, method, path, **kwargs) - - -@click.group(name="auto") -def auto_group(): - """Hanzo Auto - AI-powered workflow automation. - - \b - Build automations visually with drag-and-drop: - - \b - Workflows: - hanzo auto flows list # List all flows - hanzo auto flows create # Create new flow - hanzo auto flows run # Run a flow - - \b - Pieces (Integrations): - hanzo auto pieces list # List available pieces - hanzo auto pieces install # Install a piece - - \b - Connections: - hanzo auto connections list # List connections - hanzo auto connections add # Add connection - - \b - Local Development: - hanzo auto init # Initialize project - hanzo auto dev # Start dev server - hanzo auto deploy # Deploy flows - """ - pass - - -# ============================================================================ -# Flows -# ============================================================================ - - -@auto_group.group() -def flows(): - """Manage automation flows.""" - pass - - -@flows.command(name="list") -@click.option( - "--status", type=click.Choice(["active", "inactive", "all"]), default="all" -) -def flows_list(status: str): - """List all automation flows.""" - params = {} - if status != "all": - params["status"] = status - - resp = _request("get", "/v1/flows", params=params) - data = check_response(resp) - items = data.get("flows", data.get("items", [])) - - table = Table(title="Automation Flows", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Status", style="green") - table.add_column("Trigger", style="white") - table.add_column("Last Run", style="dim") - table.add_column("Runs", style="dim") - - for f in items: - f_status = f.get("status", "inactive") - style = "green" if f_status == "active" else "yellow" - table.add_row( - f.get("name", ""), - f"[{style}]{f_status}[/{style}]", - f.get("trigger_type", "-"), - str(f.get("last_run_at", ""))[:19], - str(f.get("run_count", 0)), - ) - - console.print(table) - if not items: - console.print( - "[dim]No flows found. Create one with 'hanzo auto flows create'[/dim]" - ) - - -@flows.command(name="create") -@click.option("--name", "-n", prompt=True, help="Flow name") -@click.option( - "--trigger", - "-t", - type=click.Choice(["webhook", "schedule", "manual"]), - default="manual", -) -def flows_create(name: str, trigger: str): - """Create a new automation flow.""" - body = {"name": name, "trigger_type": trigger} - - resp = _request("post", "/v1/flows", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Flow '{name}' created with {trigger} trigger") - console.print(f" ID: {data.get('id', '-')}") - console.print() - console.print("Next steps:") - console.print(" 1. [cyan]hanzo auto dev[/cyan] - Start visual editor") - console.print(" 2. Add steps to your flow") - console.print(" 3. [cyan]hanzo auto deploy[/cyan] - Deploy to production") - - -@flows.command(name="run") -@click.argument("flow_name") -@click.option("--input", "-i", help="JSON input for the flow") -def flows_run(flow_name: str, input: str): - """Run an automation flow.""" - body = {} - if input: - body["input"] = json.loads(input) - - console.print(f"[cyan]Running flow '{flow_name}'...[/cyan]") - resp = _request("post", f"/v1/flows/{flow_name}/run", json=body) - data = check_response(resp) - - run_status = data.get("status", "completed") - style = "green" if run_status in ("completed", "success") else "red" - console.print(f"[{style}]โœ“[/{style}] Flow {run_status}") - console.print(f" Run ID: {data.get('id', data.get('run_id', '-'))}") - if data.get("duration_ms"): - console.print(f" Duration: {data['duration_ms']}ms") - - -@flows.command(name="delete") -@click.argument("flow_name") -def flows_delete(flow_name: str): - """Delete an automation flow.""" - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete flow '{flow_name}'?[/red]"): - return - - resp = _request("delete", f"/v1/flows/{flow_name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Flow '{flow_name}' deleted") - - -@flows.command(name="enable") -@click.argument("flow_name") -def flows_enable(flow_name: str): - """Enable an automation flow.""" - resp = _request("post", f"/v1/flows/{flow_name}/enable") - check_response(resp) - console.print(f"[green]โœ“[/green] Flow '{flow_name}' enabled") - - -@flows.command(name="disable") -@click.argument("flow_name") -def flows_disable(flow_name: str): - """Disable an automation flow.""" - resp = _request("post", f"/v1/flows/{flow_name}/disable") - check_response(resp) - console.print(f"[green]โœ“[/green] Flow '{flow_name}' disabled") - - -# ============================================================================ -# Pieces (Integrations) -# ============================================================================ - - -@auto_group.group() -def pieces(): - """Manage automation pieces (integrations).""" - pass - - -@pieces.command(name="list") -@click.option("--category", "-c", help="Filter by category") -@click.option("--search", "-s", help="Search pieces") -def pieces_list(category: str, search: str): - """List available pieces.""" - params = {} - if category: - params["category"] = category - if search: - params["search"] = search - - resp = _request("get", "/v1/pieces", params=params) - data = check_response(resp) - items = data.get("pieces", data.get("items", [])) - - table = Table(title="Available Pieces", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Category", style="white") - table.add_column("Version", style="dim") - table.add_column("Installed", style="green") - - for p in items: - table.add_row( - p.get("name", ""), - p.get("category", "-"), - p.get("version", "-"), - "โœ“" if p.get("installed") else "", - ) - - console.print(table) - if not items: - console.print("[dim]No pieces found[/dim]") - - -@pieces.command(name="install") -@click.argument("piece_name") -def pieces_install(piece_name: str): - """Install a piece.""" - console.print(f"[cyan]Installing piece '{piece_name}'...[/cyan]") - resp = _request("post", f"/v1/pieces/{piece_name}/install") - check_response(resp) - console.print(f"[green]โœ“[/green] Piece '{piece_name}' installed") - - -@pieces.command(name="uninstall") -@click.argument("piece_name") -def pieces_uninstall(piece_name: str): - """Uninstall a piece.""" - resp = _request("post", f"/v1/pieces/{piece_name}/uninstall") - check_response(resp) - console.print(f"[green]โœ“[/green] Piece '{piece_name}' uninstalled") - - -# ============================================================================ -# Connections -# ============================================================================ - - -@auto_group.group() -def connections(): - """Manage connections to external services.""" - pass - - -@connections.command(name="list") -def connections_list(): - """List all connections.""" - resp = _request("get", "/v1/connections") - data = check_response(resp) - items = data.get("connections", data.get("items", [])) - - table = Table(title="Connections", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Piece", style="white") - table.add_column("Status", style="green") - table.add_column("Created", style="dim") - - for c in items: - c_status = c.get("status", "active") - style = "green" if c_status == "active" else "yellow" - table.add_row( - c.get("name", ""), - c.get("piece", "-"), - f"[{style}]{c_status}[/{style}]", - str(c.get("created_at", ""))[:19], - ) - - console.print(table) - if not items: - console.print( - "[dim]No connections found. Add one with 'hanzo auto connections add'[/dim]" - ) - - -@connections.command(name="add") -@click.argument("piece_name") -@click.option("--name", "-n", help="Connection name") -def connections_add(piece_name: str, name: str): - """Add a new connection.""" - body = {"piece": piece_name} - if name: - body["name"] = name - - resp = _request("post", "/v1/connections", json=body) - data = check_response(resp) - - auth_url = data.get("auth_url") - if auth_url: - console.print(f"[cyan]Authenticate at:[/cyan] {auth_url}") - console.print(f"[green]โœ“[/green] Connection added") - console.print(f" ID: {data.get('id', '-')}") - - -@connections.command(name="delete") -@click.argument("connection_name") -def connections_delete(connection_name: str): - """Delete a connection.""" - resp = _request("delete", f"/v1/connections/{connection_name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Connection '{connection_name}' deleted") - - -# ============================================================================ -# Development -# ============================================================================ - - -@auto_group.command() -def init(): - """Initialize Hanzo Auto project.""" - from pathlib import Path - - project_dir = Path.cwd() / ".hanzo" / "auto" - project_dir.mkdir(parents=True, exist_ok=True) - - (project_dir / "flows").mkdir(exist_ok=True) - (project_dir / "pieces").mkdir(exist_ok=True) - - console.print("[green]โœ“[/green] Hanzo Auto initialized") - console.print() - console.print("Next steps:") - console.print(" 1. [cyan]hanzo auto dev[/cyan] - Start development server") - console.print(" 2. Open http://localhost:8080 to build flows visually") - - -@auto_group.command() -@click.option("--port", "-p", default=8080, help="Port to run on") -def dev(port: int): - """Start local development server.""" - console.print( - f"[cyan]Starting Hanzo Auto development server on port {port}...[/cyan]" - ) - console.print() - console.print(f" [cyan]Visual Editor:[/cyan] http://localhost:{port}") - console.print(f" [cyan]API:[/cyan] http://localhost:{port}/api") - console.print() - console.print("Press Ctrl+C to stop") - - -@auto_group.command() -@click.option("--all", "deploy_all", is_flag=True, help="Deploy all flows") -@click.argument("flow_name", required=False) -def deploy(flow_name: str, deploy_all: bool): - """Deploy flows to production.""" - if not flow_name and not deploy_all: - raise click.ClickException("Specify a flow name or use --all") - - body = {} - if deploy_all: - body["all"] = True - console.print("[cyan]Deploying all flows...[/cyan]") - else: - body["flow"] = flow_name - console.print(f"[cyan]Deploying flow '{flow_name}'...[/cyan]") - - resp = _request("post", "/v1/flows/deploy", json=body) - data = check_response(resp) - console.print(f"[green]โœ“[/green] Deployed {data.get('deployed', 1)} flow(s)") - - -# ============================================================================ -# Runs (Execution History) -# ============================================================================ - - -@auto_group.group() -def runs(): - """View flow execution history.""" - pass - - -@runs.command(name="list") -@click.option("--flow", "-f", help="Filter by flow") -@click.option( - "--status", - type=click.Choice(["success", "failed", "running", "all"]), - default="all", -) -@click.option("--limit", "-n", default=50, help="Max results") -def runs_list(flow: str, status: str, limit: int): - """List flow runs.""" - params = {"limit": limit} - if flow: - params["flow"] = flow - if status != "all": - params["status"] = status - - resp = _request("get", "/v1/runs", params=params) - data = check_response(resp) - items = data.get("runs", data.get("items", [])) - - table = Table(title="Flow Runs", box=box.ROUNDED) - table.add_column("Run ID", style="cyan") - table.add_column("Flow", style="white") - table.add_column("Status", style="green") - table.add_column("Duration", style="yellow") - table.add_column("Started", style="dim") - - for r in items: - r_status = r.get("status", "unknown") - status_style = { - "success": "green", - "completed": "green", - "failed": "red", - "running": "cyan", - }.get(r_status, "white") - - table.add_row( - str(r.get("id", ""))[:16], - r.get("flow", "-"), - f"[{status_style}]{r_status}[/{status_style}]", - f"{r.get('duration_ms', '-')}ms" if r.get("duration_ms") else "-", - str(r.get("started_at", ""))[:19], - ) - - console.print(table) - if not items: - console.print("[dim]No runs found[/dim]") - - -@runs.command(name="show") -@click.argument("run_id") -def runs_show(run_id: str): - """Show run details.""" - resp = _request("get", f"/v1/runs/{run_id}") - data = check_response(resp) - - run_status = data.get("status", "unknown") - status_style = ( - "green" - if run_status in ("success", "completed") - else "red" if run_status == "failed" else "cyan" - ) - - console.print( - Panel( - f"[cyan]Run ID:[/cyan] {data.get('id', run_id)}\n" - f"[cyan]Flow:[/cyan] {data.get('flow', '-')}\n" - f"[cyan]Status:[/cyan] [{status_style}]{run_status}[/{status_style}]\n" - f"[cyan]Duration:[/cyan] {data.get('duration_ms', '-')}ms\n" - f"[cyan]Steps:[/cyan] {data.get('step_count', 0)}\n" - f"[cyan]Started:[/cyan] {str(data.get('started_at', ''))[:19]}", - title="Run Details", - border_style="cyan", - ) - ) - - if data.get("error"): - console.print(f"\n[red]Error:[/red] {data['error']}") - - -@runs.command(name="logs") -@click.argument("run_id") -@click.option("--step", "-s", help="Specific step") -def runs_logs(run_id: str, step: str): - """View run logs.""" - params = {} - if step: - params["step"] = step - - resp = _request("get", f"/v1/runs/{run_id}/logs", params=params) - data = check_response(resp) - lines = data.get("logs", data.get("lines", [])) - - console.print(f"[cyan]Logs for run {run_id}:[/cyan]") - for line in lines: - if isinstance(line, dict): - ts = str(line.get("timestamp", ""))[:19] - level = line.get("level", "info") - msg = line.get("message", "") - style = ( - "red" if level == "error" else "yellow" if level == "warn" else "dim" - ) - console.print(f"[dim]{ts}[/dim] [{style}]{level}[/{style}] {msg}") - else: - console.print(str(line)) - - if not lines: - console.print("[dim]No logs available[/dim]") - - -@runs.command(name="retry") -@click.argument("run_id") -def runs_retry(run_id: str): - """Retry a failed run.""" - resp = _request("post", f"/v1/runs/{run_id}/retry") - data = check_response(resp) - console.print(f"[green]โœ“[/green] Run restarted") - console.print(f" New Run ID: {data.get('id', data.get('run_id', '-'))}") - - -# ============================================================================ -# Templates -# ============================================================================ - - -@auto_group.group() -def templates(): - """Pre-built automation templates.""" - pass - - -@templates.command(name="list") -@click.option("--category", "-c", help="Filter by category") -def templates_list(category: str): - """List available templates.""" - params = {} - if category: - params["category"] = category - - resp = _request("get", "/v1/templates", params=params) - data = check_response(resp) - items = data.get("templates", data.get("items", [])) - - table = Table(title="Automation Templates", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Category", style="white") - table.add_column("Pieces", style="green") - table.add_column("Description", style="dim") - - for t in items: - table.add_row( - t.get("name", ""), - t.get("category", "-"), - str(t.get("piece_count", 0)), - t.get("description", "-"), - ) - - console.print(table) - if not items: - console.print("[dim]No templates found[/dim]") - - -@templates.command(name="use") -@click.argument("template_name") -@click.option("--name", "-n", help="Flow name") -def templates_use(template_name: str, name: str): - """Create flow from template.""" - body = {"template": template_name} - if name: - body["name"] = name - - resp = _request("post", "/v1/flows/from-template", json=body) - data = check_response(resp) - - flow_name = data.get("name", name or template_name.lower().replace(" ", "-")) - console.print(f"[green]โœ“[/green] Flow '{flow_name}' created from template") - console.print(f" ID: {data.get('id', '-')}") - console.print("Configure connections with: hanzo auto connections add ") - - -# ============================================================================ -# Webhooks -# ============================================================================ - - -@auto_group.group() -def webhooks(): - """Manage webhook triggers.""" - pass - - -@webhooks.command(name="list") -def webhooks_list(): - """List webhook endpoints.""" - resp = _request("get", "/v1/webhooks") - data = check_response(resp) - items = data.get("webhooks", data.get("items", [])) - - table = Table(title="Webhooks", box=box.ROUNDED) - table.add_column("Flow", style="cyan") - table.add_column("URL", style="white") - table.add_column("Method", style="green") - table.add_column("Calls", style="dim") - - for w in items: - table.add_row( - w.get("flow", ""), - w.get("url", "-"), - w.get("method", "POST"), - str(w.get("call_count", 0)), - ) - - console.print(table) - if not items: - console.print("[dim]No webhooks found[/dim]") - - -@webhooks.command(name="test") -@click.argument("flow_name") -@click.option("--data", "-d", help="JSON payload") -def webhooks_test(flow_name: str, data: str): - """Test webhook endpoint.""" - body = {} - if data: - body = json.loads(data) - - console.print(f"[cyan]Testing webhook for '{flow_name}'...[/cyan]") - resp = _request("post", f"/v1/webhooks/{flow_name}/test", json=body) - result = check_response(resp) - - console.print(f"[green]โœ“[/green] Webhook triggered successfully") - console.print(f" Status: {result.get('status_code', '-')}") - if result.get("run_id"): - console.print(f" Run ID: {result['run_id']}") - - -# ============================================================================ -# AI Actions -# ============================================================================ - - -@auto_group.group() -def ai(): - """AI-powered automation actions.""" - pass - - -@ai.command(name="generate") -@click.option("--prompt", "-p", required=True, help="What to automate") -@click.option("--name", "-n", help="Flow name") -def ai_generate(prompt: str, name: str): - """Generate automation flow with AI. - - \b - Examples: - hanzo auto ai generate -p "When I get an email from a customer, summarize it and post to Slack" - hanzo auto ai generate -p "Every morning, send me a summary of GitHub issues" - """ - body = {"prompt": prompt} - if name: - body["name"] = name - - console.print("[cyan]Generating flow from prompt...[/cyan]") - resp = _request("post", "/v1/ai/generate", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Flow generated") - console.print(f" Name: {data.get('name', '-')}") - console.print(f" Steps: {data.get('step_count', 0)}") - if data.get("pieces"): - console.print(f" Pieces: {', '.join(data['pieces'])}") - - -@ai.command(name="suggest") -@click.argument("flow_name") -def ai_suggest(flow_name: str): - """Get AI suggestions to improve a flow.""" - console.print(f"[cyan]Analyzing flow '{flow_name}'...[/cyan]") - resp = _request("post", f"/v1/ai/suggest", json={"flow": flow_name}) - data = check_response(resp) - - suggestions = data.get("suggestions", []) - if suggestions: - console.print() - console.print("[cyan]Suggestions:[/cyan]") - for i, s in enumerate(suggestions, 1): - console.print(f" {i}. {s}") - else: - console.print("[dim]No suggestions at this time[/dim]") - - -@ai.command(name="explain") -@click.argument("flow_name") -def ai_explain(flow_name: str): - """Get AI explanation of what a flow does.""" - console.print(f"[cyan]Explaining flow '{flow_name}'...[/cyan]") - resp = _request("post", f"/v1/ai/explain", json={"flow": flow_name}) - data = check_response(resp) - - explanation = data.get("explanation", "No explanation available") - console.print() - console.print(explanation) diff --git a/pkg/hanzo/src/hanzo/commands/base.py b/pkg/hanzo/src/hanzo/commands/base.py deleted file mode 100644 index 408f6ad61..000000000 --- a/pkg/hanzo/src/hanzo/commands/base.py +++ /dev/null @@ -1,1399 +0,0 @@ -"""Hanzo Base - Complete backend-as-a-service CLI. - -Full Supabase-compatible CLI with Hanzo extensions: -- Database (PostgreSQL with pgvector) -- Auth (users, providers, SSO) -- Storage (S3-compatible buckets) -- Realtime (websockets, presence, broadcast) -- Edge Functions (Deno/V8 runtime) -- Commerce (products, orders, checkout) -- Analytics (events, funnels, cohorts) -""" - -import os -import json -import subprocess -from typing import Optional -from pathlib import Path -from datetime import datetime - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table -from rich.prompt import Prompt, Confirm -from rich.syntax import Syntax - -from ..utils.output import console - -HANZO_API_URL = os.getenv("HANZO_API_URL", "https://api.hanzo.ai") -HANZO_BASE_URL = os.getenv("HANZO_BASE_URL", "https://base.hanzo.ai") - - -def get_api_key() -> Optional[str]: - """Get Hanzo API key.""" - if os.getenv("HANZO_API_KEY"): - return os.getenv("HANZO_API_KEY") - auth_file = Path.home() / ".hanzo" / "auth.json" - if auth_file.exists(): - try: - return json.loads(auth_file.read_text()).get("api_key") - except Exception: - pass - return None - - -def get_project_config() -> dict: - """Load project configuration.""" - config_file = Path.cwd() / "hanzo" / "config.toml" - if config_file.exists(): - import tomllib - - return tomllib.loads(config_file.read_text()) - return {} - - -def get_linked_project() -> Optional[str]: - """Get linked project ID.""" - link_file = Path.cwd() / ".hanzo" / "project.json" - if link_file.exists(): - try: - return json.loads(link_file.read_text()).get("project_id") - except Exception: - pass - return None - - -def save_linked_project(project_id: str, project_name: str): - """Save linked project.""" - link_dir = Path.cwd() / ".hanzo" - link_dir.mkdir(exist_ok=True) - (link_dir / "project.json").write_text( - json.dumps( - { - "project_id": project_id, - "project_name": project_name, - "linked_at": datetime.utcnow().isoformat(), - }, - indent=2, - ) - ) - - -def api_request(method: str, path: str, **kwargs) -> httpx.Response: - """Make authenticated API request.""" - api_key = get_api_key() - if not api_key: - raise click.ClickException("Not authenticated. Run 'hanzo auth login' first.") - - headers = kwargs.pop("headers", {}) - headers["Authorization"] = f"Bearer {api_key}" - - with httpx.Client(timeout=60) as client: - return getattr(client, method)( - f"{HANZO_API_URL}{path}", headers=headers, **kwargs - ) - - -def service_request(base_url: str, method: str, path: str, **kwargs) -> httpx.Response: - """Make authenticated request to a Hanzo service. - - Shared utility for all service CLIs (vector, kv, search, etc). - Each service file defines its own SERVICE_URL and calls this. - """ - api_key = get_api_key() - if not api_key: - raise click.ClickException("Not authenticated. Run 'hanzo login' first.") - - headers = kwargs.pop("headers", {}) - headers["Authorization"] = f"Bearer {api_key}" - - try: - with httpx.Client(timeout=60) as client: - return getattr(client, method)( - f"{base_url}{path}", headers=headers, **kwargs - ) - except httpx.ConnectError: - raise click.ClickException(f"Could not connect to {base_url}") - - -def check_response(resp: httpx.Response) -> dict: - """Check API response and return JSON data or raise error.""" - if resp.status_code >= 400: - try: - error = resp.json() - msg = error.get("message", error.get("error", resp.text)) - except Exception: - msg = resp.text - raise click.ClickException(f"API error ({resp.status_code}): {msg}") - if resp.status_code == 204: - return {} - try: - return resp.json() - except Exception: - return {} - - -# ============================================================================ -# Main base group -# ============================================================================ - - -@click.group(name="base") -def base_group(): - """Hanzo Base - Complete backend-as-a-service. - - \b - Supabase-compatible with Hanzo extensions: - - \b - Core: - hanzo base init # Initialize new project - hanzo base start # Start local development - hanzo base stop # Stop local services - hanzo base status # Show service status - - \b - Database: - hanzo base db push # Push migrations - hanzo base db pull # Pull remote schema - hanzo base db reset # Reset database - hanzo base db diff # Diff local vs remote - - \b - Auth: - hanzo base auth users list # List users - hanzo base auth providers # Manage auth providers - - \b - Storage: - hanzo base storage buckets # Manage buckets - hanzo base storage objects # Manage objects - - \b - Realtime: - hanzo base realtime channels # Manage channels - hanzo base realtime inspect # Inspect connections - - \b - Functions: - hanzo base functions deploy # Deploy edge functions - hanzo base functions serve # Local development - - \b - Hanzo Extensions: - hanzo base commerce # Products, orders, checkout - hanzo base analytics # Events, funnels, cohorts - """ - pass - - -# ============================================================================ -# Project management -# ============================================================================ - - -@base_group.command() -@click.option("--name", prompt="Project name", help="Project name") -@click.option("--org", help="Organization ID") -@click.option("--region", default="us-west-2", help="Region") -def init(name: str, org: Optional[str], region: str): - """Initialize a new Hanzo Base project.""" - project_dir = Path.cwd() / "hanzo" - - if project_dir.exists(): - if not Confirm.ask("[yellow]hanzo/ directory exists. Reinitialize?[/yellow]"): - return - - console.print(f"[cyan]Initializing Hanzo Base project '{name}'...[/cyan]") - - # Create directory structure - (project_dir / "migrations").mkdir(parents=True, exist_ok=True) - (project_dir / "functions").mkdir(exist_ok=True) - (project_dir / "seed").mkdir(exist_ok=True) - - # Create config.toml - config = f"""# Hanzo Base Configuration -# https://docs.hanzo.ai/base/config - -[project] -name = "{name}" -region = "{region}" - -[db] -port = 54322 -shadow_port = 54320 -major_version = 15 - -[studio] -enabled = true -port = 54323 - -[auth] -enabled = true -site_url = "http://localhost:3000" -jwt_expiry = 3600 -enable_signup = true - -[auth.email] -enable_signup = true -enable_confirmations = false - -[storage] -enabled = true -file_size_limit = "50MiB" - -[realtime] -enabled = true -max_channels = 100 - -[functions] -enabled = true -verify_jwt = true - -[analytics] -enabled = true - -[commerce] -enabled = false -""" - (project_dir / "config.toml").write_text(config) - - # Create seed.sql - (project_dir / "seed" / "seed.sql").write_text( - """-- Seed data for development --- Add your seed data here - --- Example: --- INSERT INTO public.profiles (id, username) VALUES --- ('00000000-0000-0000-0000-000000000001', 'alice'), --- ('00000000-0000-0000-0000-000000000002', 'bob'); -""" - ) - - # Create initial migration - migration_dir = project_dir / "migrations" / "00000000000000_init" - migration_dir.mkdir(exist_ok=True) - (migration_dir / "up.sql").write_text( - """-- Initial schema --- Enable extensions -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -CREATE EXTENSION IF NOT EXISTS "vector"; - --- Create profiles table -CREATE TABLE IF NOT EXISTS public.profiles ( - id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE, - username TEXT UNIQUE, - full_name TEXT, - avatar_url TEXT, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- Enable RLS -ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; - --- RLS policies -CREATE POLICY "Public profiles are viewable by everyone" - ON public.profiles FOR SELECT - USING (true); - -CREATE POLICY "Users can update own profile" - ON public.profiles FOR UPDATE - USING (auth.uid() = id); -""" - ) - - # Create example function - func_dir = project_dir / "functions" / "hello" - func_dir.mkdir(exist_ok=True) - (func_dir / "index.ts").write_text( - """import { serve } from "https://deno.land/std@0.168.0/http/server.ts" - -serve(async (req) => { - const { name } = await req.json() - const data = { - message: `Hello ${name || 'World'}!`, - } - - return new Response( - JSON.stringify(data), - { headers: { "Content-Type": "application/json" } }, - ) -}) -""" - ) - - # Create .gitignore - gitignore = Path.cwd() / ".gitignore" - if gitignore.exists(): - content = gitignore.read_text() - if ".hanzo" not in content: - gitignore.write_text(content + "\n# Hanzo\n.hanzo/\n") - else: - gitignore.write_text("# Hanzo\n.hanzo/\n") - - console.print("[green]โœ“ Project initialized![/green]") - console.print() - console.print("Next steps:") - console.print(" 1. [cyan]hanzo base start[/cyan] - Start local development") - console.print(" 2. [cyan]hanzo base link[/cyan] - Link to remote project") - console.print(" 3. [cyan]hanzo base db push[/cyan] - Push migrations") - - -@base_group.command() -@click.option("--project", help="Project ID or name to link") -def link(project: Optional[str]): - """Link to a remote Hanzo Base project.""" - try: - resp = api_request("get", "/v1/base/projects") - if resp.status_code >= 400: - raise click.ClickException(resp.text) - - projects = resp.json().get("projects", []) - - if not projects: - console.print("[yellow]No projects found. Create one first.[/yellow]") - console.print("Run: hanzo base projects create") - return - - if project: - # Find by ID or name - matched = next( - (p for p in projects if p["id"] == project or p["name"] == project), - None, - ) - if not matched: - raise click.ClickException(f"Project '{project}' not found") - selected = matched - else: - # Interactive selection - console.print("[cyan]Select a project to link:[/cyan]") - for i, p in enumerate(projects, 1): - console.print(f" {i}. {p['name']} ({p['id'][:8]}...)") - - choice = Prompt.ask("Enter number", default="1") - selected = projects[int(choice) - 1] - - save_linked_project(selected["id"], selected["name"]) - console.print(f"[green]โœ“ Linked to project '{selected['name']}'[/green]") - - except httpx.ConnectError: - raise click.ClickException("Could not connect to Hanzo API") - - -@base_group.command() -def start(): - """Start local Hanzo Base services.""" - console.print("[cyan]Starting Hanzo Base local development...[/cyan]") - - # Check for Docker - try: - subprocess.run(["docker", "info"], capture_output=True, check=True) - except (subprocess.CalledProcessError, FileNotFoundError): - raise click.ClickException( - "Docker is required. Install from https://docker.com" - ) - - # Start services via docker compose - compose_file = Path.cwd() / "hanzo" / "docker-compose.yml" - - if not compose_file.exists(): - # Generate docker-compose.yml - compose_content = """version: "3.8" -services: - db: - image: supabase/postgres:15.1.0.117 - ports: - - "54322:5432" - environment: - POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres - volumes: - - db-data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres"] - interval: 5s - timeout: 5s - retries: 5 - - studio: - image: supabase/studio:20240101 - ports: - - "54323:3000" - environment: - STUDIO_PG_META_URL: http://meta:8080 - SUPABASE_URL: http://kong:8000 - SUPABASE_ANON_KEY: ${ANON_KEY} - depends_on: - - db - - auth: - image: supabase/gotrue:v2.143.0 - ports: - - "54321:9999" - environment: - GOTRUE_DB_DATABASE_URL: postgres://postgres:postgres@db:5432/postgres - GOTRUE_SITE_URL: http://localhost:3000 - GOTRUE_JWT_SECRET: ${JWT_SECRET} - depends_on: - db: - condition: service_healthy - - storage: - image: supabase/storage-api:v0.43.11 - ports: - - "54324:5000" - environment: - DATABASE_URL: postgres://postgres:postgres@db:5432/postgres - STORAGE_BACKEND: file - depends_on: - db: - condition: service_healthy - - realtime: - image: supabase/realtime:v2.25.50 - ports: - - "54325:4000" - environment: - DB_HOST: db - DB_PORT: 5432 - DB_USER: postgres - DB_PASSWORD: postgres - DB_NAME: postgres - depends_on: - db: - condition: service_healthy - - functions: - image: supabase/edge-runtime:v1.33.5 - ports: - - "54326:9000" - volumes: - - ./functions:/home/deno/functions - environment: - VERIFY_JWT: "false" - -volumes: - db-data: -""" - compose_file.write_text(compose_content) - - console.print(" Starting PostgreSQL...") - console.print(" Starting Auth...") - console.print(" Starting Storage...") - console.print(" Starting Realtime...") - console.print(" Starting Functions...") - console.print(" Starting Studio...") - - # Actually start (would run docker compose up -d) - # subprocess.run(["docker", "compose", "-f", str(compose_file), "up", "-d"]) - - console.print() - console.print("[green]โœ“ Hanzo Base started![/green]") - console.print() - console.print(" [cyan]Studio:[/cyan] http://localhost:54323") - console.print(" [cyan]API:[/cyan] http://localhost:54321") - console.print( - " [cyan]Database:[/cyan] postgresql://postgres:postgres@localhost:54322/postgres" - ) - console.print(" [cyan]Realtime:[/cyan] ws://localhost:54325") - - -@base_group.command() -def stop(): - """Stop local Hanzo Base services.""" - console.print("[cyan]Stopping Hanzo Base services...[/cyan]") - # subprocess.run(["docker", "compose", "-f", "hanzo/docker-compose.yml", "down"]) - console.print("[green]โœ“ Services stopped[/green]") - - -@base_group.command() -def status(): - """Show status of Hanzo Base services.""" - project_id = get_linked_project() - - table = Table(title="Hanzo Base Status", box=box.ROUNDED) - table.add_column("Service", style="cyan") - table.add_column("Local", style="green") - table.add_column("Remote", style="yellow") - - services = [ - ("Database", "localhost:54322", "db.hanzo.ai"), - ("Auth", "localhost:54321", "auth.hanzo.ai"), - ("Storage", "localhost:54324", "storage.hanzo.ai"), - ("Realtime", "localhost:54325", "realtime.hanzo.ai"), - ("Functions", "localhost:54326", "functions.hanzo.ai"), - ("Studio", "localhost:54323", "studio.hanzo.ai"), - ] - - for name, local, remote in services: - table.add_row( - name, f"โ— {local}", f"โ— {remote}" if project_id else "โ—‹ Not linked" - ) - - console.print(table) - - if project_id: - console.print(f"\n[dim]Linked to project: {project_id}[/dim]") - else: - console.print( - "\n[dim]Run 'hanzo base link' to connect to a remote project[/dim]" - ) - - -# ============================================================================ -# Database commands -# ============================================================================ - - -@base_group.group() -def db(): - """Manage database and migrations.""" - pass - - -@db.command() -@click.option("--local", is_flag=True, help="Reset local database only") -def reset(local: bool): - """Reset database to clean state.""" - if not Confirm.ask("[red]This will delete all data. Continue?[/red]"): - return - - console.print("[cyan]Resetting database...[/cyan]") - console.print("[green]โœ“ Database reset[/green]") - - -@db.command() -@click.option("--dry-run", is_flag=True, help="Show changes without applying") -def push(dry_run: bool): - """Push local migrations to remote database.""" - project_id = get_linked_project() - if not project_id: - raise click.ClickException("No project linked. Run 'hanzo base link' first.") - - console.print("[cyan]Pushing migrations...[/cyan]") - - migrations_dir = Path.cwd() / "hanzo" / "migrations" - if not migrations_dir.exists(): - console.print("[yellow]No migrations found[/yellow]") - return - - migrations = sorted(migrations_dir.iterdir()) - console.print(f"Found {len(migrations)} migrations") - - if dry_run: - console.print("[dim]Dry run - no changes applied[/dim]") - else: - console.print("[green]โœ“ Migrations pushed[/green]") - - -@db.command() -def pull(): - """Pull remote schema to local migrations.""" - project_id = get_linked_project() - if not project_id: - raise click.ClickException("No project linked. Run 'hanzo base link' first.") - - console.print("[cyan]Pulling remote schema...[/cyan]") - console.print("[green]โœ“ Schema pulled to hanzo/migrations/[/green]") - - -@db.command() -def diff(): - """Show diff between local and remote schema.""" - console.print("[cyan]Comparing schemas...[/cyan]") - console.print("[dim]No differences found[/dim]") - - -@db.command() -@click.option("--schema", default="public", help="Schema to lint") -def lint(schema: str): - """Lint database schema for issues.""" - console.print(f"[cyan]Linting schema '{schema}'...[/cyan]") - console.print("[green]โœ“ No issues found[/green]") - - -@db.command() -@click.option("--file", "-f", help="Output file") -def dump(file: Optional[str]): - """Dump database schema.""" - output = file or "schema.sql" - console.print(f"[cyan]Dumping schema to {output}...[/cyan]") - console.print(f"[green]โœ“ Schema dumped to {output}[/green]") - - -@db.group() -def migrations(): - """Manage database migrations.""" - pass - - -@migrations.command(name="list") -def migrations_list(): - """List all migrations.""" - migrations_dir = Path.cwd() / "hanzo" / "migrations" - - if not migrations_dir.exists(): - console.print("[yellow]No migrations directory[/yellow]") - return - - table = Table(title="Migrations", box=box.ROUNDED) - table.add_column("Version", style="cyan") - table.add_column("Name", style="white") - table.add_column("Status", style="green") - - for m in sorted(migrations_dir.iterdir()): - if m.is_dir(): - parts = m.name.split("_", 1) - version = parts[0] - name = parts[1] if len(parts) > 1 else "" - table.add_row(version, name, "โœ“ Applied") - - console.print(table) - - -@migrations.command(name="new") -@click.argument("name") -def migrations_new(name: str): - """Create a new migration.""" - migrations_dir = Path.cwd() / "hanzo" / "migrations" - migrations_dir.mkdir(parents=True, exist_ok=True) - - timestamp = datetime.utcnow().strftime("%Y%m%d%H%M%S") - migration_name = f"{timestamp}_{name}" - migration_dir = migrations_dir / migration_name - migration_dir.mkdir() - - (migration_dir / "up.sql").write_text(f"-- Migration: {name}\n\n") - (migration_dir / "down.sql").write_text(f"-- Rollback: {name}\n\n") - - console.print(f"[green]โœ“ Created migration: {migration_name}[/green]") - - -@migrations.command(name="up") -@click.option("--target", help="Target migration version") -def migrations_up(target: Optional[str]): - """Apply pending migrations.""" - console.print("[cyan]Applying migrations...[/cyan]") - console.print("[green]โœ“ Migrations applied[/green]") - - -@migrations.command(name="down") -@click.option("--target", help="Target migration version") -def migrations_down(target: Optional[str]): - """Rollback migrations.""" - console.print("[cyan]Rolling back migrations...[/cyan]") - console.print("[green]โœ“ Migrations rolled back[/green]") - - -# ============================================================================ -# Auth commands -# ============================================================================ - - -@base_group.group() -def auth(): - """Manage authentication.""" - pass - - -@auth.group() -def users(): - """Manage users.""" - pass - - -@users.command(name="list") -@click.option("--limit", default=50, help="Max users to list") -def users_list(limit: int): - """List all users.""" - project_id = get_linked_project() - if not project_id: - raise click.ClickException("No project linked") - - try: - resp = api_request( - "get", f"/v1/base/{project_id}/auth/users", params={"limit": limit} - ) - users = resp.json().get("users", []) - - table = Table(title="Users", box=box.ROUNDED) - table.add_column("ID", style="cyan") - table.add_column("Email", style="white") - table.add_column("Created", style="dim") - table.add_column("Last Sign In", style="dim") - - for u in users: - table.add_row( - u.get("id", "")[:8] + "...", - u.get("email", ""), - u.get("created_at", "")[:10], - u.get("last_sign_in_at", "-")[:10] if u.get("last_sign_in_at") else "-", - ) - - console.print(table) - - except httpx.ConnectError: - raise click.ClickException("Could not connect to API") - - -@users.command(name="create") -@click.option("--email", prompt=True, help="User email") -@click.option("--password", prompt=True, hide_input=True, help="User password") -def users_create(email: str, password: str): - """Create a new user.""" - project_id = get_linked_project() - if not project_id: - raise click.ClickException("No project linked") - - console.print(f"[cyan]Creating user {email}...[/cyan]") - console.print(f"[green]โœ“ User created[/green]") - - -@users.command(name="delete") -@click.argument("user_id") -def users_delete(user_id: str): - """Delete a user.""" - if not Confirm.ask(f"[red]Delete user {user_id}?[/red]"): - return - - console.print("[green]โœ“ User deleted[/green]") - - -@auth.command() -def providers(): - """List configured auth providers.""" - table = Table(title="Auth Providers", box=box.ROUNDED) - table.add_column("Provider", style="cyan") - table.add_column("Status", style="green") - table.add_column("Client ID", style="dim") - - providers = [ - ("Email", "Enabled", "-"), - ("Google", "Enabled", "xxx...xxx"), - ("GitHub", "Enabled", "xxx...xxx"), - ("Apple", "Disabled", "-"), - ("Discord", "Disabled", "-"), - ("Twitter", "Disabled", "-"), - ] - - for name, status, client in providers: - style = "green" if status == "Enabled" else "dim" - table.add_row(name, f"[{style}]{status}[/{style}]", client) - - console.print(table) - - -# ============================================================================ -# Storage commands -# ============================================================================ - - -@base_group.group() -def storage(): - """Manage file storage.""" - pass - - -@storage.group() -def buckets(): - """Manage storage buckets.""" - pass - - -@buckets.command(name="list") -def buckets_list(): - """List all buckets.""" - project_id = get_linked_project() - if not project_id: - raise click.ClickException("No project linked") - - table = Table(title="Storage Buckets", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Public", style="green") - table.add_column("Size", style="white") - table.add_column("Files", style="dim") - - # Mock data - table.add_row("avatars", "Yes", "12.5 MB", "245") - table.add_row("uploads", "No", "1.2 GB", "1,024") - table.add_row("public", "Yes", "500 MB", "89") - - console.print(table) - - -@buckets.command(name="create") -@click.argument("name") -@click.option("--public", is_flag=True, help="Make bucket public") -def buckets_create(name: str, public: bool): - """Create a new bucket.""" - console.print(f"[cyan]Creating bucket '{name}'...[/cyan]") - console.print(f"[green]โœ“ Bucket '{name}' created[/green]") - - -@buckets.command(name="delete") -@click.argument("name") -@click.option("--force", is_flag=True, help="Delete even if not empty") -def buckets_delete(name: str, force: bool): - """Delete a bucket.""" - if not Confirm.ask(f"[red]Delete bucket '{name}'?[/red]"): - return - console.print(f"[green]โœ“ Bucket '{name}' deleted[/green]") - - -@storage.group() -def objects(): - """Manage storage objects.""" - pass - - -@objects.command(name="list") -@click.argument("bucket") -@click.option("--prefix", help="Filter by prefix") -def objects_list(bucket: str, prefix: Optional[str]): - """List objects in a bucket.""" - table = Table(title=f"Objects in '{bucket}'", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Size", style="white") - table.add_column("Modified", style="dim") - - console.print(table) - - -@objects.command(name="upload") -@click.argument("bucket") -@click.argument("file", type=click.Path(exists=True)) -@click.option("--path", help="Remote path") -def objects_upload(bucket: str, file: str, path: Optional[str]): - """Upload a file to a bucket.""" - console.print(f"[cyan]Uploading {file} to {bucket}...[/cyan]") - console.print("[green]โœ“ File uploaded[/green]") - - -@objects.command(name="delete") -@click.argument("bucket") -@click.argument("path") -def objects_delete(bucket: str, path: str): - """Delete an object.""" - console.print(f"[green]โœ“ Object deleted[/green]") - - -# ============================================================================ -# Realtime commands -# ============================================================================ - - -@base_group.group() -def realtime(): - """Manage realtime subscriptions.""" - pass - - -@realtime.command(name="channels") -def realtime_channels(): - """List active realtime channels.""" - table = Table(title="Realtime Channels", box=box.ROUNDED) - table.add_column("Channel", style="cyan") - table.add_column("Type", style="white") - table.add_column("Subscribers", style="green") - table.add_column("Messages/s", style="dim") - - # Mock data - table.add_row("room:lobby", "broadcast", "12", "45") - table.add_row("presence:online", "presence", "89", "12") - table.add_row("db:public:messages", "postgres_changes", "5", "3") - - console.print(table) - - -@realtime.command(name="inspect") -@click.argument("channel") -def realtime_inspect(channel: str): - """Inspect a realtime channel.""" - console.print( - Panel( - f"[cyan]Channel:[/cyan] {channel}\n" - f"[cyan]Type:[/cyan] broadcast\n" - f"[cyan]Subscribers:[/cyan] 12\n" - f"[cyan]Created:[/cyan] 2024-01-15 10:30:00\n" - f"[cyan]Messages/min:[/cyan] 2,700", - title="Channel Details", - border_style="cyan", - ) - ) - - -@realtime.command(name="broadcast") -@click.argument("channel") -@click.argument("event") -@click.option("--payload", "-p", help="JSON payload") -def realtime_broadcast(channel: str, event: str, payload: Optional[str]): - """Broadcast a message to a channel.""" - console.print(f"[cyan]Broadcasting to {channel}...[/cyan]") - console.print(f"[green]โœ“ Message sent[/green]") - - -# ============================================================================ -# Functions commands -# ============================================================================ - - -@base_group.group() -def functions(): - """Manage edge functions.""" - pass - - -@functions.command(name="list") -def functions_list(): - """List all edge functions.""" - funcs_dir = Path.cwd() / "hanzo" / "functions" - - table = Table(title="Edge Functions", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Status", style="green") - table.add_column("Last Deploy", style="dim") - - if funcs_dir.exists(): - for f in funcs_dir.iterdir(): - if f.is_dir() and (f / "index.ts").exists(): - table.add_row(f.name, "โ— Deployed", "2024-01-15") - - console.print(table) - - -@functions.command(name="new") -@click.argument("name") -def functions_new(name: str): - """Create a new edge function.""" - func_dir = Path.cwd() / "hanzo" / "functions" / name - func_dir.mkdir(parents=True, exist_ok=True) - - (func_dir / "index.ts").write_text( - f"""import {{ serve }} from "https://deno.land/std@0.168.0/http/server.ts" - -serve(async (req) => {{ - const data = {{ - message: "Hello from {name}!", - }} - - return new Response( - JSON.stringify(data), - {{ headers: {{ "Content-Type": "application/json" }} }}, - ) -}}) -""" - ) - - console.print(f"[green]โœ“ Created function: {name}[/green]") - console.print(f" Edit: hanzo/functions/{name}/index.ts") - - -@functions.command(name="serve") -@click.option("--port", default=54326, help="Local port") -def functions_serve(port: int): - """Serve functions locally.""" - console.print(f"[cyan]Starting functions server on port {port}...[/cyan]") - console.print(f" Functions available at http://localhost:{port}/") - - -@functions.command(name="deploy") -@click.argument("name", required=False) -@click.option("--all", "deploy_all", is_flag=True, help="Deploy all functions") -def functions_deploy(name: Optional[str], deploy_all: bool): - """Deploy edge function(s).""" - if not name and not deploy_all: - raise click.ClickException("Specify function name or use --all") - - if deploy_all: - console.print("[cyan]Deploying all functions...[/cyan]") - else: - console.print(f"[cyan]Deploying function '{name}'...[/cyan]") - - console.print("[green]โœ“ Functions deployed[/green]") - - -@functions.command(name="delete") -@click.argument("name") -def functions_delete(name: str): - """Delete an edge function.""" - if not Confirm.ask(f"[red]Delete function '{name}'?[/red]"): - return - console.print(f"[green]โœ“ Function '{name}' deleted[/green]") - - -# ============================================================================ -# Secrets commands -# ============================================================================ - - -@base_group.group() -def secrets(): - """Manage secrets and environment variables.""" - pass - - -@secrets.command(name="list") -def secrets_list(): - """List all secrets.""" - table = Table(title="Secrets", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Updated", style="dim") - - table.add_row("STRIPE_SECRET_KEY", "2024-01-10") - table.add_row("OPENAI_API_KEY", "2024-01-08") - table.add_row("SENDGRID_API_KEY", "2024-01-05") - - console.print(table) - - -@secrets.command(name="set") -@click.argument("name") -@click.option("--value", prompt=True, hide_input=True, help="Secret value") -def secrets_set(name: str, value: str): - """Set a secret.""" - console.print(f"[green]โœ“ Secret '{name}' set[/green]") - - -@secrets.command(name="unset") -@click.argument("name") -def secrets_unset(name: str): - """Unset a secret.""" - console.print(f"[green]โœ“ Secret '{name}' removed[/green]") - - -# ============================================================================ -# Projects commands -# ============================================================================ - - -@base_group.group() -def projects(): - """Manage Hanzo Base projects.""" - pass - - -@projects.command(name="list") -def projects_list(): - """List all projects.""" - try: - resp = api_request("get", "/v1/base/projects") - projects = resp.json().get("projects", []) - - table = Table(title="Projects", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("ID", style="dim") - table.add_column("Region", style="white") - table.add_column("Status", style="green") - - for p in projects: - table.add_row( - p.get("name", ""), - p.get("id", "")[:12] + "...", - p.get("region", ""), - p.get("status", ""), - ) - - console.print(table) - - except httpx.ConnectError: - raise click.ClickException("Could not connect to API") - - -@projects.command(name="create") -@click.option("--name", prompt=True, help="Project name") -@click.option("--org", help="Organization ID") -@click.option("--region", default="us-west-2", help="Region") -def projects_create(name: str, org: Optional[str], region: str): - """Create a new project.""" - console.print(f"[cyan]Creating project '{name}'...[/cyan]") - console.print("[green]โœ“ Project created[/green]") - console.print() - console.print("Run 'hanzo base link' to connect this directory") - - -@projects.command(name="delete") -@click.argument("project_id") -def projects_delete(project_id: str): - """Delete a project.""" - if not Confirm.ask( - f"[red]Delete project {project_id}? This cannot be undone.[/red]" - ): - return - console.print("[green]โœ“ Project deleted[/green]") - - -# ============================================================================ -# Organizations commands -# ============================================================================ - - -@base_group.group() -def orgs(): - """Manage organizations.""" - pass - - -@orgs.command(name="list") -def orgs_list(): - """List organizations.""" - table = Table(title="Organizations", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("ID", style="dim") - table.add_column("Role", style="green") - - table.add_row("Hanzo AI", "org_xxx...", "Owner") - - console.print(table) - - -# ============================================================================ -# Generate commands -# ============================================================================ - - -@base_group.group() -def gen(): - """Generate types and keys.""" - pass - - -@gen.command(name="types") -@click.option( - "--lang", type=click.Choice(["typescript", "python", "go"]), default="typescript" -) -@click.option("--output", "-o", default="types", help="Output directory") -def gen_types(lang: str, output: str): - """Generate types from database schema.""" - console.print(f"[cyan]Generating {lang} types...[/cyan]") - console.print(f"[green]โœ“ Types generated to {output}/[/green]") - - -@gen.command(name="keys") -def gen_keys(): - """Generate new API keys.""" - project_id = get_linked_project() - if not project_id: - raise click.ClickException("No project linked") - - console.print("[cyan]Generating new API keys...[/cyan]") - console.print() - console.print("[yellow]ANON KEY:[/yellow]") - console.print(" eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...") - console.print() - console.print("[yellow]SERVICE KEY:[/yellow]") - console.print(" eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...") - - -# ============================================================================ -# Studio command -# ============================================================================ - - -@base_group.command() -def studio(): - """Open Hanzo Base Studio in browser.""" - import webbrowser - - project_id = get_linked_project() - if project_id: - url = f"https://studio.hanzo.ai/project/{project_id}" - else: - url = "http://localhost:54323" - - console.print(f"[cyan]Opening Studio: {url}[/cyan]") - webbrowser.open(url) - - -# ============================================================================ -# Commerce commands (Hanzo extension) -# ============================================================================ - - -@base_group.group() -def commerce(): - """Manage commerce (Hanzo extension).""" - pass - - -@commerce.group() -def products(): - """Manage products.""" - pass - - -@products.command(name="list") -def products_list(): - """List all products.""" - table = Table(title="Products", box=box.ROUNDED) - table.add_column("ID", style="cyan") - table.add_column("Name", style="white") - table.add_column("Price", style="green") - table.add_column("Stock", style="dim") - - console.print(table) - - -@products.command(name="create") -@click.option("--name", prompt=True) -@click.option("--price", prompt=True, type=float) -@click.option("--description", default="") -def products_create(name: str, price: float, description: str): - """Create a product.""" - console.print(f"[green]โœ“ Product '{name}' created[/green]") - - -@products.command(name="delete") -@click.argument("product_id") -def products_delete(product_id: str): - """Delete a product.""" - console.print(f"[green]โœ“ Product deleted[/green]") - - -@commerce.group() -def orders(): - """Manage orders.""" - pass - - -@orders.command(name="list") -@click.option("--status", help="Filter by status") -def orders_list(status: Optional[str]): - """List orders.""" - table = Table(title="Orders", box=box.ROUNDED) - table.add_column("ID", style="cyan") - table.add_column("Customer", style="white") - table.add_column("Total", style="green") - table.add_column("Status", style="yellow") - table.add_column("Created", style="dim") - - console.print(table) - - -@orders.command(name="show") -@click.argument("order_id") -def orders_show(order_id: str): - """Show order details.""" - console.print( - Panel( - f"[cyan]Order ID:[/cyan] {order_id}\n" - f"[cyan]Status:[/cyan] Pending\n" - f"[cyan]Total:[/cyan] $99.00\n" - f"[cyan]Items:[/cyan] 2", - title="Order Details", - border_style="cyan", - ) - ) - - -@commerce.command(name="checkout") -def commerce_checkout(): - """Show checkout configuration.""" - console.print( - Panel( - "[cyan]Checkout URL:[/cyan] https://checkout.hanzo.ai/xxx\n" - "[cyan]Success URL:[/cyan] https://example.com/success\n" - "[cyan]Cancel URL:[/cyan] https://example.com/cancel\n" - "[cyan]Payment Methods:[/cyan] card, apple_pay, google_pay", - title="Checkout Configuration", - border_style="cyan", - ) - ) - - -# ============================================================================ -# Analytics commands (Hanzo extension) -# ============================================================================ - - -@base_group.group() -def analytics(): - """Manage analytics (Hanzo extension).""" - pass - - -@analytics.command(name="events") -@click.option("--limit", default=100, help="Number of events") -def analytics_events(limit: int): - """List recent events.""" - table = Table(title="Recent Events", box=box.ROUNDED) - table.add_column("Event", style="cyan") - table.add_column("User", style="white") - table.add_column("Properties", style="dim") - table.add_column("Time", style="dim") - - console.print(table) - - -@analytics.command(name="track") -@click.argument("event_name") -@click.option("--user", "-u", help="User ID") -@click.option("--props", "-p", help="JSON properties") -def analytics_track(event_name: str, user: Optional[str], props: Optional[str]): - """Track an event.""" - console.print(f"[green]โœ“ Event '{event_name}' tracked[/green]") - - -@analytics.group() -def funnels(): - """Manage conversion funnels.""" - pass - - -@funnels.command(name="list") -def funnels_list(): - """List all funnels.""" - table = Table(title="Funnels", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Steps", style="white") - table.add_column("Conversion", style="green") - - table.add_row("Signup Flow", "4", "23.5%") - table.add_row("Purchase Flow", "3", "12.8%") - - console.print(table) - - -@funnels.command(name="show") -@click.argument("name") -def funnels_show(name: str): - """Show funnel details.""" - console.print(f"[cyan]Funnel: {name}[/cyan]") - - -@analytics.group() -def cohorts(): - """Manage user cohorts.""" - pass - - -@cohorts.command(name="list") -def cohorts_list(): - """List all cohorts.""" - table = Table(title="Cohorts", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Users", style="white") - table.add_column("Created", style="dim") - - table.add_row("Power Users", "1,234", "2024-01-10") - table.add_row("New Signups (7d)", "567", "2024-01-15") - - console.print(table) - - -@analytics.command(name="dashboard") -def analytics_dashboard(): - """Open analytics dashboard.""" - import webbrowser - - project_id = get_linked_project() - if project_id: - url = f"https://analytics.hanzo.ai/project/{project_id}" - else: - url = "https://analytics.hanzo.ai" - - console.print(f"[cyan]Opening Analytics: {url}[/cyan]") - webbrowser.open(url) diff --git a/pkg/hanzo/src/hanzo/commands/chat.py b/pkg/hanzo/src/hanzo/commands/chat.py deleted file mode 100644 index 5ed04ac02..000000000 --- a/pkg/hanzo/src/hanzo/commands/chat.py +++ /dev/null @@ -1,249 +0,0 @@ -"""Chat command for interactive AI conversations.""" - -import os -import asyncio -from typing import Optional - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.markdown import Markdown - -from ..utils.output import console - - -@click.command(name="chat") -@click.option("--model", "-m", default="llama-3.2-3b", help="Model to use") -@click.option("--local/--cloud", default=True, help="Use local or cloud model") -@click.option("--once", is_flag=True, help="Single question mode") -@click.option("--system", "-s", help="System prompt") -@click.option( - "--repl", is_flag=True, help="Start full REPL interface (like Claude Code)" -) -@click.option("--ipython", is_flag=True, help="Use IPython REPL interface") -@click.option("--tui", is_flag=True, help="Use beautiful TUI interface") -@click.argument("prompt", nargs=-1) -@click.pass_context -def chat_command( - ctx, - model: str, - local: bool, - once: bool, - system: Optional[str], - repl: bool, - ipython: bool, - tui: bool, - prompt: tuple, -): - """Interactive AI chat.""" - # Check if REPL mode requested - if repl or ipython or tui: - try: - import os - import sys - - # Set up environment - if model: - os.environ["HANZO_DEFAULT_MODEL"] = model - if local: - os.environ["HANZO_USE_LOCAL"] = "true" - if system: - os.environ["HANZO_SYSTEM_PROMPT"] = system - - if ipython: - from hanzo_dev.ipython_repl import main - - sys.exit(main()) - elif tui: - from hanzo_dev.textual_repl import main - - sys.exit(main()) - else: - from hanzo_dev.cli import main - - sys.exit(main()) - except ImportError: - console.print("[red]Error:[/red] hanzo-dev not installed") - console.print("Install with: pip install hanzo-dev") - return - - prompt_text = " ".join(prompt) if prompt else None - - if once or prompt_text: - # Single question mode - asyncio.run(ask_once(ctx, prompt_text or "Hello", model, local, system)) - else: - # Interactive chat - asyncio.run(interactive_chat(ctx, model, local, system)) - - -async def ask_once( - ctx, prompt: str, model: str, local: bool, system: Optional[str] = None -): - """Ask a single question.""" - messages = [] - if system: - messages.append({"role": "system", "content": system}) - messages.append({"role": "user", "content": prompt}) - - try: - if local: - # Try router first, then fall back to local node - base_urls = [ - "http://localhost:4000", # Hanzo router default port - "http://localhost:8000", # Local node port - ] - - base_url = None - for url in base_urls: - try: - async with httpx.AsyncClient() as client: - await client.get(f"{url}/health", timeout=1.0) - base_url = url - break - except (httpx.ConnectError, httpx.TimeoutException): - continue - - if not base_url: - console.print( - "[yellow]No local AI server running.[/yellow]\n" - "Start one of:\n" - " โ€ข Hanzo router: hanzo router start\n" - " โ€ข Local node: hanzo serve" - ) - return - - # Make request to local node - async with httpx.AsyncClient() as client: - response = await client.post( - f"{base_url}/v1/chat/completions", - json={"model": model, "messages": messages, "stream": False}, - ) - response.raise_for_status() - result = response.json() - content = result["choices"][0]["message"]["content"] - else: - # Use cloud API - try: - # Try different import paths - try: - from hanzoai import completion - except ImportError: - try: - from pkg.hanzoai import completion - except ImportError: - # Fallback to using llm directly - import llm - - def completion(**kwargs): - import os - - api_key = os.getenv("HANZO_API_KEY") - if api_key: - kwargs["api_key"] = api_key - kwargs["api_base"] = "https://api.hanzo.ai/v1" - return llm.completion(**kwargs) - - result = completion( - model=f"anthropic/{model}" if "claude" in model else model, - messages=messages, - ) - content = result.choices[0].message.content - except ImportError as e: - console.print(f"[red]Error:[/red] Missing dependencies: {e}") - console.print("Install with: pip install llm") - return - - # Display response - if ctx.obj.get("json"): - console.print_json(data={"response": content}) - else: - console.print(Markdown(content)) - - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - - -async def interactive_chat(ctx, model: str, local: bool, system: Optional[str]): - """Run interactive chat session.""" - from prompt_toolkit import PromptSession - from prompt_toolkit.history import FileHistory - - console.print( - f"[cyan]Chat session started[/cyan] (model: {model}, mode: {'local' if local else 'cloud'})" - ) - console.print("Type 'exit' or Ctrl+D to quit\n") - - session = PromptSession(history=FileHistory(".hanzo_chat_history")) - messages = [] - - if system: - messages.append({"role": "system", "content": system}) - - while True: - try: - # Get user input - user_input = await session.prompt_async("You: ") - - if user_input.lower() in ["exit", "quit"]: - break - - # Add to messages - messages.append({"role": "user", "content": user_input}) - - # Get response - console.print("AI: ", end="") - with console.status(""): - if local: - # Use local node - async with httpx.AsyncClient() as client: - response = await client.post( - "http://localhost:8000/v1/chat/completions", - json={ - "model": model, - "messages": messages, - "stream": False, - }, - ) - response.raise_for_status() - result = response.json() - content = result["choices"][0]["message"]["content"] - else: - # Use cloud API - try: - from hanzoai import completion - except ImportError: - try: - from pkg.hanzoai import completion - except ImportError: - # Fallback to using llm directly - import llm - - def completion(**kwargs): - import os - - api_key = os.getenv("HANZO_API_KEY") - if api_key: - kwargs["api_key"] = api_key - kwargs["api_base"] = "https://api.hanzo.ai/v1" - return llm.completion(**kwargs) - - result = completion( - model=f"anthropic/{model}" if "claude" in model else model, - messages=messages, - ) - content = result.choices[0].message.content - - # Display and save response - console.print(Markdown(content)) - messages.append({"role": "assistant", "content": content}) - console.print() - - except KeyboardInterrupt: - console.print("\n[yellow]Interrupted. Exiting...[/yellow]") - break - except EOFError: - break - except Exception as e: - console.print(f"\n[red]Error: {e}[/red]\n") diff --git a/pkg/hanzo/src/hanzo/commands/cloud.py b/pkg/hanzo/src/hanzo/commands/cloud.py deleted file mode 100644 index de7e3d17e..000000000 --- a/pkg/hanzo/src/hanzo/commands/cloud.py +++ /dev/null @@ -1,1010 +0,0 @@ -"""Cloud infrastructure management commands for Hanzo CLI. - -Follows gcloud idioms: hanzo cloud - -Canonical verbs: - - list, describe, create, delete, update - - connect, env, status (Hanzo-specific) - -Lifecycle verbs (resource-dependent): - - start, stop, restart (stateful services) - - enable, disable (functions, cron) - - pause, resume (queues) -""" - -import os -import json -from typing import Optional -from pathlib import Path - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table -from rich.prompt import Confirm - -from ..utils.output import console - -HANZO_API_URL = os.getenv("HANZO_API_URL", "https://api.hanzo.ai") - -# Service definitions with lifecycle capabilities -SERVICES = { - "vector": { - "name": "Vector Database", - "description": "Qdrant vector similarity search", - "env_prefix": "QDRANT", - "default_port": 6333, - "lifecycle": ["start", "stop", "restart"], - }, - "kv": { - "name": "Key-Value Store", - "description": "Redis/Valkey for caching and state", - "env_prefix": "REDIS", - "default_port": 6379, - "lifecycle": ["start", "stop", "restart"], - }, - "documentdb": { - "name": "Document Database", - "description": "MongoDB for document storage", - "env_prefix": "MONGODB", - "default_port": 27017, - "lifecycle": ["start", "stop", "restart"], - }, - "storage": { - "name": "Object Storage", - "description": "S3-compatible storage (MinIO)", - "env_prefix": "S3", - "default_port": 9000, - "lifecycle": ["start", "stop", "restart"], - }, - "search": { - "name": "Full-Text Search", - "description": "Meilisearch for fast search", - "env_prefix": "MEILI", - "default_port": 7700, - "lifecycle": ["start", "stop", "restart"], - }, - "pubsub": { - "name": "Pub/Sub Messaging", - "description": "NATS for event streaming", - "env_prefix": "NATS", - "default_port": 4222, - "lifecycle": ["start", "stop", "restart"], - }, - "tasks": { - "name": "Workflow Engine", - "description": "Temporal for durable workflows", - "env_prefix": "TEMPORAL", - "default_port": 7233, - "lifecycle": ["start", "stop", "restart"], - }, - "queues": { - "name": "Job Queues", - "description": "Distributed work queues", - "env_prefix": "QUEUE", - "default_port": 6379, - "lifecycle": ["pause", "resume"], - }, - "cron": { - "name": "Scheduled Jobs", - "description": "Cron-based job scheduling", - "env_prefix": "CRON", - "default_port": 6379, - "lifecycle": ["enable", "disable"], - }, - "functions": { - "name": "Serverless Functions", - "description": "Nuclio function runtime", - "env_prefix": "NUCLIO", - "default_port": 8070, - "lifecycle": ["enable", "disable"], - }, -} - -SERVICE_NAMES = tuple(SERVICES.keys()) - - -def get_api_key() -> Optional[str]: - """Get Hanzo API key from env or auth file.""" - if os.getenv("HANZO_API_KEY"): - return os.getenv("HANZO_API_KEY") - - auth_file = Path.home() / ".hanzo" / "auth.json" - if auth_file.exists(): - try: - auth = json.loads(auth_file.read_text()) - return auth.get("api_key") - except Exception: - pass - return None - - -def get_cloud_config() -> dict: - """Load cloud configuration.""" - config_file = Path.home() / ".hanzo" / "cloud.json" - if config_file.exists(): - try: - return json.loads(config_file.read_text()) - except Exception: - pass - return {"instances": {}} - - -def save_cloud_config(config: dict): - """Save cloud configuration.""" - config_dir = Path.home() / ".hanzo" - config_dir.mkdir(exist_ok=True) - config_file = config_dir / "cloud.json" - config_file.write_text(json.dumps(config, indent=2)) - - -# ============================================================================ -# Main cloud group -# ============================================================================ - - -@click.group(name="cloud") -def cloud_group(): - """Manage Hanzo Cloud infrastructure. - - \b - Structure: hanzo cloud - - \b - Discovery: - hanzo cloud services list # Available service types - hanzo cloud instances list # Your provisioned instances - - \b - Per-service commands: - hanzo cloud vector create # Create a vector DB - hanzo cloud vector describe # Show instance details - hanzo cloud vector delete # Delete instance - hanzo cloud vector connect # Connection details - hanzo cloud vector env # Export env vars - hanzo cloud vector status # Health check - - \b - Services: vector, kv, documentdb, storage, search, - pubsub, tasks, queues, cron, functions - """ - pass - - -# ============================================================================ -# Services subgroup - list available service types -# ============================================================================ - - -@cloud_group.group(name="services") -def services_group(): - """Manage available service types.""" - pass - - -@services_group.command(name="list") -def services_list(): - """List available infrastructure service types.""" - table = Table(title="Available Services", box=box.ROUNDED) - table.add_column("Service", style="cyan") - table.add_column("Name", style="white") - table.add_column("Description", style="dim") - table.add_column("Lifecycle", style="yellow") - - for key, info in SERVICES.items(): - lifecycle = ", ".join(info.get("lifecycle", [])) - table.add_row(key, info["name"], info["description"], lifecycle or "-") - - console.print(table) - - -# ============================================================================ -# Instances subgroup - list/describe provisioned instances -# ============================================================================ - - -@cloud_group.group(name="instances") -def instances_group(): - """Manage provisioned instances.""" - pass - - -@instances_group.command(name="list") -@click.option("--format", "fmt", type=click.Choice(["table", "json"]), default="table") -def instances_list(fmt: str): - """List all provisioned instances.""" - config = get_cloud_config() - instances = config.get("instances", {}) - - if fmt == "json": - console.print_json(json.dumps(instances)) - return - - if not instances: - console.print("[yellow]No instances provisioned.[/yellow]") - console.print("Run 'hanzo cloud create' to get started.") - return - - table = Table(title="Provisioned Instances", box=box.ROUNDED) - table.add_column("Service", style="cyan") - table.add_column("Name", style="white") - table.add_column("Region", style="dim") - table.add_column("Tier", style="green") - table.add_column("Status", style="yellow") - - for svc_name, svc_config in instances.items(): - table.add_row( - svc_name, - svc_config.get("name", "default"), - svc_config.get("region", "us-west-2"), - svc_config.get("tier", "free"), - svc_config.get("status", "unknown"), - ) - - console.print(table) - - -@instances_group.command(name="describe") -@click.argument("service", type=click.Choice(SERVICE_NAMES)) -@click.option("--name", default="default", help="Instance name") -def instances_describe(service: str, name: str): - """Describe a provisioned instance.""" - config = get_cloud_config() - instances = config.get("instances", {}) - - if service not in instances: - console.print(f"[yellow]No {service} instance found.[/yellow]") - return - - svc_config = instances[service] - info = SERVICES[service] - - console.print( - Panel( - f"[cyan]Service:[/cyan] {info['name']}\n" - f"[cyan]Name:[/cyan] {svc_config.get('name', 'default')}\n" - f"[cyan]ID:[/cyan] {svc_config.get('id', 'N/A')}\n" - f"[cyan]URL:[/cyan] {svc_config.get('url', 'N/A')}\n" - f"[cyan]Host:[/cyan] {svc_config.get('host', 'N/A')}\n" - f"[cyan]Port:[/cyan] {svc_config.get('port', 'N/A')}\n" - f"[cyan]Tier:[/cyan] {svc_config.get('tier', 'free')}\n" - f"[cyan]Region:[/cyan] {svc_config.get('region', 'N/A')}\n" - f"[cyan]Status:[/cyan] {svc_config.get('status', 'unknown')}", - title=f"[bold]{service}[/bold]", - border_style="cyan", - ) - ) - - -# ============================================================================ -# Operations subgroup - async operation tracking -# ============================================================================ - - -@cloud_group.group(name="operations") -def operations_group(): - """Track async operations.""" - pass - - -@operations_group.command(name="list") -def operations_list(): - """List recent operations.""" - api_key = get_api_key() - if not api_key: - console.print("[red]Not authenticated. Run 'hanzo auth login' first.[/red]") - return - - try: - with httpx.Client(timeout=30) as client: - resp = client.get( - f"{HANZO_API_URL}/v1/cloud/operations", - headers={"Authorization": f"Bearer {api_key}"}, - ) - - if resp.status_code >= 400: - console.print(f"[red]Error: {resp.text}[/red]") - return - - data = resp.json() - operations = data.get("operations", []) - - if not operations: - console.print("[dim]No recent operations.[/dim]") - return - - table = Table(title="Operations", box=box.ROUNDED) - table.add_column("ID", style="cyan") - table.add_column("Type", style="white") - table.add_column("Resource", style="dim") - table.add_column("Status", style="yellow") - - for op in operations: - table.add_row( - op.get("id", "")[:12], - op.get("type", ""), - op.get("resource", ""), - op.get("status", ""), - ) - - console.print(table) - - except httpx.ConnectError: - console.print("[red]Could not connect to Hanzo API.[/red]") - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - - -@operations_group.command(name="describe") -@click.argument("operation_id") -def operations_describe(operation_id: str): - """Describe an operation.""" - api_key = get_api_key() - if not api_key: - console.print("[red]Not authenticated.[/red]") - return - - try: - with httpx.Client(timeout=30) as client: - resp = client.get( - f"{HANZO_API_URL}/v1/cloud/operations/{operation_id}", - headers={"Authorization": f"Bearer {api_key}"}, - ) - - if resp.status_code >= 400: - console.print(f"[red]Error: {resp.text}[/red]") - return - - data = resp.json() - console.print_json(json.dumps(data, indent=2)) - - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - - -@operations_group.command(name="wait") -@click.argument("operation_id") -@click.option("--timeout", default=300, help="Timeout in seconds") -def operations_wait(operation_id: str, timeout: int): - """Wait for an operation to complete.""" - api_key = get_api_key() - if not api_key: - console.print("[red]Not authenticated.[/red]") - return - - import time - - start = time.time() - - with console.status(f"Waiting for operation {operation_id[:12]}..."): - while time.time() - start < timeout: - try: - with httpx.Client(timeout=30) as client: - resp = client.get( - f"{HANZO_API_URL}/v1/cloud/operations/{operation_id}", - headers={"Authorization": f"Bearer {api_key}"}, - ) - - if resp.status_code >= 400: - console.print(f"[red]Error: {resp.text}[/red]") - return - - data = resp.json() - status = data.get("status", "") - - if status in ("done", "completed", "succeeded"): - console.print("[green]โœ“ Operation completed[/green]") - return - elif status in ("failed", "error"): - console.print( - f"[red]โœ— Operation failed: {data.get('error', 'Unknown error')}[/red]" - ) - return - - time.sleep(2) - - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - return - - console.print("[yellow]Timeout waiting for operation[/yellow]") - - -# ============================================================================ -# Per-service command factory -# ============================================================================ - - -def create_service_group(service_key: str, service_info: dict): - """Create a command group for a specific service.""" - - @click.group(name=service_key) - def service_group(): - pass - - service_group.__doc__ = f"Manage {service_info['name']} instances." - - # create command - @service_group.command(name="create") - @click.option("--name", default="default", help="Instance name") - @click.option( - "--tier", type=click.Choice(["free", "pro", "enterprise"]), default="free" - ) - @click.option("--region", default="us-west-2", help="Deployment region") - def create(name: str, tier: str, region: str): - """Create a new instance.""" - _create_instance(service_key, name, tier, region) - - # describe command - @service_group.command(name="describe") - @click.option("--name", default="default", help="Instance name") - def describe(name: str): - """Show instance details.""" - _describe_instance(service_key, name) - - # list command - @service_group.command(name="list") - def list_cmd(): - """List instances of this service type.""" - _list_service_instances(service_key) - - # delete command - @service_group.command(name="delete") - @click.option("--name", default="default", help="Instance name") - @click.option("--force", is_flag=True, help="Skip confirmation") - def delete(name: str, force: bool): - """Delete an instance.""" - _delete_instance(service_key, name, force) - - # connect command - @service_group.command(name="connect") - @click.option("--name", default="default", help="Instance name") - def connect(name: str): - """Show connection details.""" - _connect_instance(service_key, name) - - # env command - @service_group.command(name="env") - @click.option("--name", default="default", help="Instance name") - @click.option( - "--shell", - type=click.Choice(["bash", "zsh", "fish", "powershell"]), - default="bash", - ) - @click.option("--export", "do_export", is_flag=True, help="Print export statements") - def env(name: str, shell: str, do_export: bool): - """Show/export environment variables.""" - _env_instance(service_key, name, shell, do_export) - - # status command - @service_group.command(name="status") - @click.option("--name", default="default", help="Instance name") - def status(name: str): - """Check instance health.""" - _status_instance(service_key, name) - - # update command - @service_group.command(name="update") - @click.option("--name", default="default", help="Instance name") - @click.option("--tier", type=click.Choice(["free", "pro", "enterprise"])) - def update(name: str, tier: Optional[str]): - """Update instance configuration.""" - _update_instance(service_key, name, tier) - - # Add lifecycle commands based on service capabilities - lifecycle = service_info.get("lifecycle", []) - - if "start" in lifecycle: - - @service_group.command(name="start") - @click.option("--name", default="default") - def start(name: str): - """Start a stopped instance.""" - _lifecycle_action(service_key, name, "start") - - if "stop" in lifecycle: - - @service_group.command(name="stop") - @click.option("--name", default="default") - def stop(name: str): - """Stop a running instance.""" - _lifecycle_action(service_key, name, "stop") - - if "restart" in lifecycle: - - @service_group.command(name="restart") - @click.option("--name", default="default") - def restart(name: str): - """Restart an instance.""" - _lifecycle_action(service_key, name, "restart") - - if "enable" in lifecycle: - - @service_group.command(name="enable") - @click.option("--name", default="default") - def enable(name: str): - """Enable the service.""" - _lifecycle_action(service_key, name, "enable") - - if "disable" in lifecycle: - - @service_group.command(name="disable") - @click.option("--name", default="default") - def disable(name: str): - """Disable the service.""" - _lifecycle_action(service_key, name, "disable") - - if "pause" in lifecycle: - - @service_group.command(name="pause") - @click.option("--name", default="default") - def pause(name: str): - """Pause the service (retain data).""" - _lifecycle_action(service_key, name, "pause") - - if "resume" in lifecycle: - - @service_group.command(name="resume") - @click.option("--name", default="default") - def resume(name: str): - """Resume a paused service.""" - _lifecycle_action(service_key, name, "resume") - - return service_group - - -# ============================================================================ -# Implementation functions -# ============================================================================ - - -def _create_instance(service: str, name: str, tier: str, region: str): - """Create a new instance.""" - api_key = get_api_key() - if not api_key: - console.print("[red]Not authenticated. Run 'hanzo auth login' first.[/red]") - return - - info = SERVICES[service] - console.print(f"[cyan]Creating {info['name']} instance '{name}'...[/cyan]") - - try: - with httpx.Client(timeout=60) as client: - resp = client.post( - f"{HANZO_API_URL}/v1/cloud/{service}", - headers={"Authorization": f"Bearer {api_key}"}, - json={ - "name": name, - "tier": tier, - "region": region, - }, - ) - - if resp.status_code == 401: - console.print( - "[red]Authentication failed. Run 'hanzo auth login'.[/red]" - ) - return - - if resp.status_code == 402: - console.print("[yellow]Upgrade required for this tier.[/yellow]") - console.print("Visit https://hanzo.ai/pricing to upgrade.") - return - - if resp.status_code == 409: - console.print( - f"[yellow]Instance '{name}' already exists. Use 'update' to modify.[/yellow]" - ) - return - - if resp.status_code >= 400: - console.print(f"[red]Error: {resp.text}[/red]") - return - - data = resp.json() - - # Save to config - config = get_cloud_config() - config["instances"][service] = { - "id": data.get("id"), - "name": name, - "url": data.get("url"), - "host": data.get("host"), - "port": data.get("port"), - "credentials": data.get("credentials", {}), - "tier": tier, - "region": region, - "status": "running", - } - save_cloud_config(config) - - console.print(f"[green]โœ“ {info['name']} created successfully![/green]") - console.print() - console.print(f"[cyan]URL:[/cyan] {data.get('url')}") - console.print() - console.print( - f"[dim]Run 'hanzo cloud {service} env --export' for environment variables[/dim]" - ) - - except httpx.ConnectError: - console.print("[red]Could not connect to Hanzo API.[/red]") - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - - -def _describe_instance(service: str, name: str): - """Describe an instance.""" - config = get_cloud_config() - instances = config.get("instances", {}) - - if service not in instances: - console.print(f"[yellow]No {service} instance found.[/yellow]") - console.print(f"Run 'hanzo cloud {service} create' to create one.") - return - - svc_config = instances[service] - info = SERVICES[service] - - console.print( - Panel( - f"[cyan]Service:[/cyan] {info['name']}\n" - f"[cyan]Name:[/cyan] {svc_config.get('name', 'default')}\n" - f"[cyan]ID:[/cyan] {svc_config.get('id', 'N/A')}\n" - f"[cyan]URL:[/cyan] {svc_config.get('url', 'N/A')}\n" - f"[cyan]Host:[/cyan] {svc_config.get('host', 'N/A')}\n" - f"[cyan]Port:[/cyan] {svc_config.get('port', 'N/A')}\n" - f"[cyan]Tier:[/cyan] {svc_config.get('tier', 'free')}\n" - f"[cyan]Region:[/cyan] {svc_config.get('region', 'N/A')}\n" - f"[cyan]Status:[/cyan] {svc_config.get('status', 'unknown')}", - title=f"[bold]{service}[/bold]", - border_style="cyan", - ) - ) - - -def _list_service_instances(service: str): - """List instances of a specific service type.""" - config = get_cloud_config() - instances = config.get("instances", {}) - - if service not in instances: - console.print(f"[yellow]No {service} instances.[/yellow]") - console.print(f"Run 'hanzo cloud {service} create' to create one.") - return - - svc_config = instances[service] - info = SERVICES[service] - - table = Table(title=f"{info['name']} Instances", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Region", style="dim") - table.add_column("Tier", style="green") - table.add_column("Status", style="yellow") - - table.add_row( - svc_config.get("name", "default"), - svc_config.get("region", "us-west-2"), - svc_config.get("tier", "free"), - svc_config.get("status", "unknown"), - ) - - console.print(table) - - -def _delete_instance(service: str, name: str, force: bool): - """Delete an instance.""" - config = get_cloud_config() - instances = config.get("instances", {}) - - if service not in instances: - console.print(f"[yellow]{service} not provisioned.[/yellow]") - return - - info = SERVICES[service] - - if not force: - if not Confirm.ask( - f"[red]Delete {info['name']} '{name}'? This cannot be undone.[/red]" - ): - console.print("Cancelled.") - return - - api_key = get_api_key() - if not api_key: - console.print("[red]Not authenticated.[/red]") - return - - try: - with httpx.Client(timeout=30) as client: - svc_id = instances[service].get("id") - resp = client.delete( - f"{HANZO_API_URL}/v1/cloud/{service}/{svc_id}", - headers={"Authorization": f"Bearer {api_key}"}, - ) - - if resp.status_code >= 400 and resp.status_code != 404: - console.print(f"[red]Error: {resp.text}[/red]") - return - - # Remove from config - del config["instances"][service] - save_cloud_config(config) - - console.print(f"[green]โœ“ {info['name']} deleted.[/green]") - - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - - -def _connect_instance(service: str, name: str): - """Show connection details.""" - config = get_cloud_config() - instances = config.get("instances", {}) - - if service not in instances: - console.print(f"[yellow]No {service} instance found.[/yellow]") - return - - svc_config = instances[service] - info = SERVICES[service] - - console.print( - Panel( - f"[cyan]URL:[/cyan] {svc_config.get('url', 'N/A')}\n" - f"[cyan]Host:[/cyan] {svc_config.get('host', 'N/A')}\n" - f"[cyan]Port:[/cyan] {svc_config.get('port', 'N/A')}", - title=f"[bold]{info['name']} Connection[/bold]", - border_style="cyan", - ) - ) - - -def _env_instance(service: str, name: str, shell: str, do_export: bool): - """Show/export environment variables.""" - config = get_cloud_config() - instances = config.get("instances", {}) - - if service not in instances: - console.print(f"[yellow]No {service} instance found.[/yellow]") - return - - svc_config = instances[service] - info = SERVICES[service] - prefix = info["env_prefix"] - - env_vars = [] - if svc_config.get("url"): - env_vars.append((f"{prefix}_URL", svc_config["url"])) - if svc_config.get("host"): - env_vars.append((f"{prefix}_HOST", svc_config["host"])) - if svc_config.get("port"): - env_vars.append((f"{prefix}_PORT", str(svc_config["port"]))) - - creds = svc_config.get("credentials", {}) - if creds.get("api_key"): - env_vars.append((f"{prefix}_API_KEY", creds["api_key"])) - if creds.get("password"): - env_vars.append((f"{prefix}_PASSWORD", creds["password"])) - if creds.get("username"): - env_vars.append((f"{prefix}_USERNAME", creds["username"])) - - if do_export: - if shell in ("bash", "zsh"): - for key, value in env_vars: - console.print(f'export {key}="{value}"') - elif shell == "fish": - for key, value in env_vars: - console.print(f'set -gx {key} "{value}"') - elif shell == "powershell": - for key, value in env_vars: - console.print(f'$env:{key} = "{value}"') - else: - table = Table(title=f"{info['name']} Environment", box=box.ROUNDED) - table.add_column("Variable", style="cyan") - table.add_column("Value", style="green") - - for key, value in env_vars: - if "KEY" in key or "PASSWORD" in key or "SECRET" in key: - display = value[:8] + "..." if len(value) > 8 else "***" - else: - display = value - table.add_row(key, display) - - console.print(table) - console.print() - console.print( - f"[dim]Run 'hanzo cloud {service} env --export' for export statements[/dim]" - ) - - -def _status_instance(service: str, name: str): - """Check instance health.""" - config = get_cloud_config() - instances = config.get("instances", {}) - - if service not in instances: - console.print(f"[yellow]No {service} instance found.[/yellow]") - return - - svc_config = instances[service] - info = SERVICES[service] - api_key = get_api_key() - - try: - with httpx.Client(timeout=10) as client: - resp = client.get( - f"{HANZO_API_URL}/v1/cloud/{service}/{svc_config.get('id')}/health", - headers={"Authorization": f"Bearer {api_key}"} if api_key else {}, - ) - - if resp.status_code == 200: - data = resp.json() - status = "[green]โ— Healthy[/green]" - latency = f"{data.get('latency_ms', '?')}ms" - else: - status = "[yellow]โ—‹ Unknown[/yellow]" - latency = "-" - except Exception: - status = "[red]โœ— Unreachable[/red]" - latency = "-" - - console.print(f"{info['name']}: {status} ({latency})") - - -def _update_instance(service: str, name: str, tier: Optional[str]): - """Update instance configuration.""" - if not tier: - console.print("[yellow]No updates specified.[/yellow]") - console.print("Use --tier to change the service tier.") - return - - api_key = get_api_key() - if not api_key: - console.print("[red]Not authenticated.[/red]") - return - - config = get_cloud_config() - instances = config.get("instances", {}) - - if service not in instances: - console.print(f"[yellow]No {service} instance found.[/yellow]") - return - - svc_config = instances[service] - info = SERVICES[service] - - try: - with httpx.Client(timeout=30) as client: - resp = client.patch( - f"{HANZO_API_URL}/v1/cloud/{service}/{svc_config.get('id')}", - headers={"Authorization": f"Bearer {api_key}"}, - json={"tier": tier} if tier else {}, - ) - - if resp.status_code >= 400: - console.print(f"[red]Error: {resp.text}[/red]") - return - - # Update local config - if tier: - config["instances"][service]["tier"] = tier - save_cloud_config(config) - - console.print(f"[green]โœ“ {info['name']} updated.[/green]") - - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - - -def _lifecycle_action(service: str, name: str, action: str): - """Execute a lifecycle action.""" - api_key = get_api_key() - if not api_key: - console.print("[red]Not authenticated.[/red]") - return - - config = get_cloud_config() - instances = config.get("instances", {}) - - if service not in instances: - console.print(f"[yellow]No {service} instance found.[/yellow]") - return - - svc_config = instances[service] - info = SERVICES[service] - - console.print(f"[cyan]{action.capitalize()}ing {info['name']}...[/cyan]") - - try: - with httpx.Client(timeout=60) as client: - resp = client.post( - f"{HANZO_API_URL}/v1/cloud/{service}/{svc_config.get('id')}/{action}", - headers={"Authorization": f"Bearer {api_key}"}, - ) - - if resp.status_code >= 400: - console.print(f"[red]Error: {resp.text}[/red]") - return - - # Update local status - status_map = { - "start": "running", - "stop": "stopped", - "restart": "running", - "enable": "enabled", - "disable": "disabled", - "pause": "paused", - "resume": "running", - } - config["instances"][service]["status"] = status_map.get(action, "unknown") - save_cloud_config(config) - - console.print(f"[green]โœ“ {info['name']} {action}ed.[/green]") - - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - - -# ============================================================================ -# Register all service groups -# ============================================================================ - -for svc_key, svc_info in SERVICES.items(): - service_cmd = create_service_group(svc_key, svc_info) - cloud_group.add_command(service_cmd) - - -# ============================================================================ -# Init command -# ============================================================================ - - -@cloud_group.command(name="init") -def init(): - """Initialize infrastructure from hanzo.yaml config file.""" - config_paths = [ - Path.cwd() / "hanzo.yaml", - Path.cwd() / "hanzo.yml", - Path.cwd() / ".hanzo.yaml", - ] - - config_file = None - for p in config_paths: - if p.exists(): - config_file = p - break - - if not config_file: - console.print("[yellow]No hanzo.yaml found in current directory.[/yellow]") - console.print() - console.print("Create one with:") - console.print() - console.print("[cyan]# hanzo.yaml[/cyan]") - console.print("cloud:") - console.print(" vector: true") - console.print(" kv: true") - console.print(" search: true") - return - - import yaml - - try: - config = yaml.safe_load(config_file.read_text()) - except Exception as e: - console.print(f"[red]Error parsing {config_file}: {e}[/red]") - return - - cloud_config = config.get("cloud", {}) - if not cloud_config: - console.print("[yellow]No 'cloud' section in config file.[/yellow]") - return - - console.print(f"[cyan]Initializing cloud services from {config_file}...[/cyan]") - - for service, enabled in cloud_config.items(): - if service in SERVICES and enabled: - console.print(f" Creating {service}...") - _create_instance(service, "default", "free", "us-west-2") - - console.print("[green]โœ“ Cloud infrastructure initialized![/green]") diff --git a/pkg/hanzo/src/hanzo/commands/config.py b/pkg/hanzo/src/hanzo/commands/config.py deleted file mode 100644 index 615e17546..000000000 --- a/pkg/hanzo/src/hanzo/commands/config.py +++ /dev/null @@ -1,241 +0,0 @@ -"""Configuration management commands.""" - -import json - -import yaml -import click -from rich.syntax import Syntax - -from ..utils.output import console - - -@click.group(name="config") -def config_group(): - """Manage Hanzo configuration.""" - pass - - -@config_group.command() -@click.option("--global", "is_global", is_flag=True, help="Show global config") -@click.option("--local", "is_local", is_flag=True, help="Show local config") -@click.option("--system", is_flag=True, help="Show system config") -@click.pass_context -def show(ctx, is_global: bool, is_local: bool, system: bool): - """Show configuration.""" - from ..utils.config import load_config, get_config_paths - - # Determine which configs to show - show_all = not (is_global or is_local or system) - - configs = {} - paths = get_config_paths() - - if show_all or system: - if paths["system"].exists(): - configs["System"] = (paths["system"], load_config(paths["system"])) - - if show_all or is_global: - if paths["global"].exists(): - configs["Global"] = (paths["global"], load_config(paths["global"])) - - if show_all or is_local: - if paths["local"].exists(): - configs["Local"] = (paths["local"], load_config(paths["local"])) - - # Merge and show - if configs: - for name, (path, config) in configs.items(): - console.print(f"[cyan]{name} Config:[/cyan] {path}") - - # Pretty print config - if config: - syntax = Syntax( - yaml.dump(config, default_flow_style=False), - "yaml", - theme="monokai", - line_numbers=False, - ) - console.print(syntax) - else: - console.print("[dim]Empty[/dim]") - console.print() - else: - console.print("[yellow]No configuration found[/yellow]") - console.print("Create one with: hanzo config set ") - - -@config_group.command() -@click.argument("key") -@click.argument("value") -@click.option("--global", "is_global", is_flag=True, help="Set in global config") -@click.option("--local", "is_local", is_flag=True, help="Set in local config") -@click.pass_context -def set(ctx, key: str, value: str, is_global: bool, is_local: bool): - """Set configuration value.""" - from ..utils.config import load_config, save_config, get_config_paths - - # Determine target config - paths = get_config_paths() - - if is_local: - config_path = paths["local"] - config_name = "local" - else: # Default to global - config_path = paths["global"] - config_name = "global" - - # Load existing config - config = load_config(config_path) if config_path.exists() else {} - - # Parse value - try: - # Try to parse as JSON first - parsed_value = json.loads(value) - except json.JSONDecodeError: - # Check for boolean strings - if value.lower() == "true": - parsed_value = True - elif value.lower() == "false": - parsed_value = False - else: - # Keep as string - parsed_value = value - - # Set value (support nested keys with dot notation) - keys = key.split(".") - current = config - - for k in keys[:-1]: - if k not in current: - current[k] = {} - current = current[k] - - current[keys[-1]] = parsed_value - - # Save config - save_config(config_path, config) - - console.print( - f"[green]โœ“[/green] Set {key} = {parsed_value} in {config_name} config" - ) - - -@config_group.command() -@click.argument("key") -@click.option("--global", "is_global", is_flag=True, help="Get from global config") -@click.option("--local", "is_local", is_flag=True, help="Get from local config") -@click.pass_context -def get(ctx, key: str, is_global: bool, is_local: bool): - """Get configuration value.""" - from ..utils.config import get_config_value - - scope = "local" if is_local else ("global" if is_global else None) - value = get_config_value(key, scope=scope) - - if value is not None: - if isinstance(value, (dict, list)): - console.print_json(data=value) - else: - console.print(value) - else: - console.print(f"[yellow]Key not found: {key}[/yellow]") - - -@config_group.command() -@click.argument("key") -@click.option("--global", "is_global", is_flag=True, help="Unset from global config") -@click.option("--local", "is_local", is_flag=True, help="Unset from local config") -@click.pass_context -def unset(ctx, key: str, is_global: bool, is_local: bool): - """Unset configuration value.""" - from ..utils.config import load_config, save_config, get_config_paths - - # Determine target config - paths = get_config_paths() - - if is_local: - config_path = paths["local"] - config_name = "local" - else: # Default to global - config_path = paths["global"] - config_name = "global" - - if not config_path.exists(): - console.print(f"[yellow]No {config_name} config found[/yellow]") - return - - # Load config - config = load_config(config_path) - - # Remove value (support nested keys) - keys = key.split(".") - current = config - - try: - for k in keys[:-1]: - current = current[k] - - if keys[-1] in current: - del current[keys[-1]] - save_config(config_path, config) - console.print(f"[green]โœ“[/green] Unset {key} from {config_name} config") - else: - console.print(f"[yellow]Key not found: {key}[/yellow]") - except KeyError: - console.print(f"[yellow]Key not found: {key}[/yellow]") - - -@config_group.command() -@click.option("--system", is_flag=True, help="Edit system config") -@click.option("--global", "is_global", is_flag=True, help="Edit global config") -@click.option("--local", "is_local", is_flag=True, help="Edit local config") -@click.pass_context -def edit(ctx, system: bool, is_global: bool, is_local: bool): - """Edit configuration file in editor.""" - import os - import subprocess - - from ..utils.config import get_config_paths - - # Determine which config to edit - paths = get_config_paths() - - if system: - config_path = paths["system"] - elif is_local: - config_path = paths["local"] - else: # Default to global - config_path = paths["global"] - - # Ensure file exists - if not config_path.exists(): - config_path.parent.mkdir(parents=True, exist_ok=True) - config_path.write_text("# Hanzo configuration\n") - - # Get editor - editor = os.environ.get("EDITOR", "vi") - - # Open in editor - try: - subprocess.run([editor, str(config_path)], check=True) - console.print(f"[green]โœ“[/green] Edited {config_path}") - except subprocess.CalledProcessError: - console.print(f"[red]Failed to open editor[/red]") - except FileNotFoundError: - console.print(f"[red]Editor not found: {editor}[/red]") - console.print("Set EDITOR environment variable to specify editor") - - -@config_group.command() -@click.pass_context -def init(ctx): - """Initialize configuration.""" - from ..utils.config import init_config - - try: - paths = init_config() - console.print("[green]โœ“[/green] Initialized configuration") - console.print(f" Global: {paths['global']}") - console.print(f" Local: {paths.get('local', 'Not in project')}") - except Exception as e: - console.print(f"[red]Failed to initialize config: {e}[/red]") diff --git a/pkg/hanzo/src/hanzo/commands/cx.py b/pkg/hanzo/src/hanzo/commands/cx.py deleted file mode 100644 index 581c0c7c3..000000000 --- a/pkg/hanzo/src/hanzo/commands/cx.py +++ /dev/null @@ -1,646 +0,0 @@ -"""Hanzo CX - Customer experience and operations CLI. - -Support, CRM, and ERP. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -SERVICE_URL = os.getenv("HANZO_CX_URL", "https://cx.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(SERVICE_URL, method, path, **kwargs) - - -@click.group(name="cx") -def cx_group(): - """Hanzo CX - Customer experience and operations. - - \b - Support (Inbox): - hanzo cx inbox list # List conversations - hanzo cx inbox assign # Assign conversation - hanzo cx inbox reply # Reply to conversation - - \b - CRM: - hanzo cx contacts list # List contacts - hanzo cx deals list # List deals - hanzo cx pipelines list # List pipelines - - \b - ERP: - hanzo cx invoices list # List invoices - hanzo cx orders list # List orders - """ - pass - - -# ============================================================================ -# Inbox (Support) -# ============================================================================ - - -@cx_group.group() -def inbox(): - """Manage support inbox.""" - pass - - -@inbox.command(name="list") -@click.option( - "--status", - type=click.Choice(["open", "pending", "resolved", "all"]), - default="open", -) -@click.option( - "--channel", type=click.Choice(["email", "chat", "social", "all"]), default="all" -) -@click.option("--limit", "-n", default=50, help="Max results") -def inbox_list(status: str, channel: str, limit: int): - """List inbox conversations.""" - params: dict = {"limit": limit} - if status != "all": - params["status"] = status - if channel != "all": - params["channel"] = channel - resp = _request("get", "/v1/inbox/conversations", params=params) - data = check_response(resp) - - table = Table(title="Support Inbox", box=box.ROUNDED) - table.add_column("ID", style="cyan") - table.add_column("Subject", style="white") - table.add_column("Channel", style="dim") - table.add_column("Status", style="green") - table.add_column("Assignee", style="dim") - table.add_column("Updated", style="dim") - - for c in data.get("conversations", []): - st = c.get("status", "open") - st_style = {"open": "yellow", "pending": "cyan", "resolved": "green"}.get( - st, "white" - ) - table.add_row( - c.get("id", "")[:12], - c.get("subject", ""), - c.get("channel", ""), - f"[{st_style}]{st}[/{st_style}]", - c.get("assignee", "unassigned"), - c.get("updated_at", ""), - ) - - console.print(table) - - -@inbox.command(name="show") -@click.argument("conversation_id") -def inbox_show(conversation_id: str): - """Show conversation details.""" - resp = _request("get", f"/v1/inbox/conversations/{conversation_id}") - data = check_response(resp) - - info = ( - f"[cyan]ID:[/cyan] {data.get('id', conversation_id)}\n" - f"[cyan]Subject:[/cyan] {data.get('subject', 'N/A')}\n" - f"[cyan]Customer:[/cyan] {data.get('customer_email', 'N/A')}\n" - f"[cyan]Status:[/cyan] {data.get('status', 'N/A')}\n" - f"[cyan]Channel:[/cyan] {data.get('channel', 'N/A')}\n" - f"[cyan]Messages:[/cyan] {data.get('message_count', 0)}\n" - f"[cyan]Assignee:[/cyan] {data.get('assignee', 'unassigned')}" - ) - console.print(Panel(info, title="Conversation", border_style="cyan")) - - messages = data.get("messages", []) - if messages: - console.print() - for msg in messages: - sender = msg.get("sender", "unknown") - style = "cyan" if msg.get("is_agent") else "white" - console.print( - f"[{style}]{sender}[/{style}] [dim]{msg.get('created_at', '')}[/dim]" - ) - console.print(f" {msg.get('body', '')}") - console.print() - - -@inbox.command(name="assign") -@click.argument("conversation_id") -@click.option("--agent", "-a", required=True, help="Agent email or ID") -def inbox_assign(conversation_id: str, agent: str): - """Assign conversation to agent.""" - resp = _request( - "post", - f"/v1/inbox/conversations/{conversation_id}/assign", - json={"agent": agent}, - ) - check_response(resp) - console.print(f"[green]โœ“[/green] Conversation assigned to {agent}") - - -@inbox.command(name="reply") -@click.argument("conversation_id") -@click.option("--message", "-m", prompt=True, help="Reply message") -def inbox_reply(conversation_id: str, message: str): - """Reply to a conversation.""" - resp = _request( - "post", - f"/v1/inbox/conversations/{conversation_id}/reply", - json={"body": message}, - ) - check_response(resp) - console.print(f"[green]โœ“[/green] Reply sent") - - -@inbox.command(name="resolve") -@click.argument("conversation_id") -def inbox_resolve(conversation_id: str): - """Mark conversation as resolved.""" - resp = _request("post", f"/v1/inbox/conversations/{conversation_id}/resolve") - check_response(resp) - console.print(f"[green]โœ“[/green] Conversation resolved") - - -@inbox.command(name="reopen") -@click.argument("conversation_id") -def inbox_reopen(conversation_id: str): - """Reopen a resolved conversation.""" - resp = _request("post", f"/v1/inbox/conversations/{conversation_id}/reopen") - check_response(resp) - console.print(f"[green]โœ“[/green] Conversation reopened") - - -# ============================================================================ -# Contacts (CRM) -# ============================================================================ - - -@cx_group.group() -def contacts(): - """Manage CRM contacts.""" - pass - - -@contacts.command(name="list") -@click.option("--search", "-s", help="Search contacts") -@click.option("--limit", "-n", default=50, help="Max results") -def contacts_list(search: str, limit: int): - """List contacts.""" - params: dict = {"limit": limit} - if search: - params["search"] = search - resp = _request("get", "/v1/contacts", params=params) - data = check_response(resp) - - table = Table(title="Contacts", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Email", style="white") - table.add_column("Company", style="dim") - table.add_column("Status", style="green") - table.add_column("Created", style="dim") - - for c in data.get("contacts", []): - table.add_row( - c.get("name", ""), - c.get("email", ""), - c.get("company", ""), - c.get("status", "active"), - c.get("created_at", ""), - ) - - console.print(table) - - -@contacts.command(name="show") -@click.argument("contact_id") -def contacts_show(contact_id: str): - """Show contact details.""" - resp = _request("get", f"/v1/contacts/{contact_id}") - data = check_response(resp) - - info = ( - f"[cyan]Name:[/cyan] {data.get('name', 'N/A')}\n" - f"[cyan]Email:[/cyan] {data.get('email', 'N/A')}\n" - f"[cyan]Company:[/cyan] {data.get('company', 'N/A')}\n" - f"[cyan]Phone:[/cyan] {data.get('phone', 'N/A')}\n" - f"[cyan]Deals:[/cyan] {data.get('deal_count', 0)} (${data.get('deal_value', 0):,.0f})" - ) - console.print(Panel(info, title="Contact Details", border_style="cyan")) - - -@contacts.command(name="create") -@click.option("--name", "-n", prompt=True, help="Contact name") -@click.option("--email", "-e", prompt=True, help="Email") -@click.option("--company", "-c", help="Company") -@click.option("--phone", "-p", help="Phone") -def contacts_create(name: str, email: str, company: str, phone: str): - """Create a contact.""" - payload: dict = {"name": name, "email": email} - if company: - payload["company"] = company - if phone: - payload["phone"] = phone - resp = _request("post", "/v1/contacts", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Contact '{name}' created") - - -@contacts.command(name="update") -@click.argument("contact_id") -@click.option("--name", "-n", help="Contact name") -@click.option("--email", "-e", help="Email") -@click.option("--company", "-c", help="Company") -@click.option("--phone", "-p", help="Phone") -def contacts_update(contact_id: str, name: str, email: str, company: str, phone: str): - """Update a contact.""" - payload: dict = {} - if name: - payload["name"] = name - if email: - payload["email"] = email - if company: - payload["company"] = company - if phone: - payload["phone"] = phone - if not payload: - raise click.ClickException("Provide at least one field to update") - resp = _request("patch", f"/v1/contacts/{contact_id}", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Contact updated") - - -@contacts.command(name="delete") -@click.argument("contact_id") -def contacts_delete(contact_id: str): - """Delete a contact.""" - resp = _request("delete", f"/v1/contacts/{contact_id}") - check_response(resp) - console.print(f"[green]โœ“[/green] Contact deleted") - - -# ============================================================================ -# Deals (CRM) -# ============================================================================ - - -@cx_group.group() -def deals(): - """Manage CRM deals.""" - pass - - -@deals.command(name="list") -@click.option("--pipeline", "-p", help="Filter by pipeline") -@click.option("--stage", "-s", help="Filter by stage") -@click.option("--limit", "-n", default=50, help="Max results") -def deals_list(pipeline: str, stage: str, limit: int): - """List deals.""" - params: dict = {"limit": limit} - if pipeline: - params["pipeline"] = pipeline - if stage: - params["stage"] = stage - resp = _request("get", "/v1/deals", params=params) - data = check_response(resp) - - table = Table(title="Deals", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Value", style="green") - table.add_column("Stage", style="white") - table.add_column("Contact", style="dim") - table.add_column("Close Date", style="dim") - - for d in data.get("deals", []): - table.add_row( - d.get("name", ""), - f"${d.get('value', 0):,.0f}", - d.get("stage", ""), - d.get("contact_name", ""), - d.get("close_date", ""), - ) - - console.print(table) - - -@deals.command(name="show") -@click.argument("deal_id") -def deals_show(deal_id: str): - """Show deal details.""" - resp = _request("get", f"/v1/deals/{deal_id}") - data = check_response(resp) - - info = ( - f"[cyan]Deal:[/cyan] {data.get('name', 'N/A')}\n" - f"[cyan]Value:[/cyan] ${data.get('value', 0):,.0f}\n" - f"[cyan]Stage:[/cyan] {data.get('stage', 'N/A')}\n" - f"[cyan]Pipeline:[/cyan] {data.get('pipeline', 'default')}\n" - f"[cyan]Contact:[/cyan] {data.get('contact_name', 'N/A')}\n" - f"[cyan]Close Date:[/cyan] {data.get('close_date', 'N/A')}" - ) - console.print(Panel(info, title="Deal Details", border_style="cyan")) - - -@deals.command(name="create") -@click.option("--name", "-n", prompt=True, help="Deal name") -@click.option("--value", "-v", type=float, prompt=True, help="Deal value") -@click.option("--contact", "-c", required=True, help="Contact ID") -@click.option("--pipeline", "-p", default="default", help="Pipeline") -@click.option("--stage", "-s", help="Initial stage") -def deals_create(name: str, value: float, contact: str, pipeline: str, stage: str): - """Create a deal.""" - payload: dict = { - "name": name, - "value": value, - "contact_id": contact, - "pipeline": pipeline, - } - if stage: - payload["stage"] = stage - resp = _request("post", "/v1/deals", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Deal '{name}' created (${value:,.0f})") - - -@deals.command(name="move") -@click.argument("deal_id") -@click.option("--stage", "-s", required=True, help="Target stage") -def deals_move(deal_id: str, stage: str): - """Move deal to a stage.""" - resp = _request("patch", f"/v1/deals/{deal_id}", json={"stage": stage}) - check_response(resp) - console.print(f"[green]โœ“[/green] Deal moved to '{stage}'") - - -@deals.command(name="delete") -@click.argument("deal_id") -def deals_delete(deal_id: str): - """Delete a deal.""" - resp = _request("delete", f"/v1/deals/{deal_id}") - check_response(resp) - console.print(f"[green]โœ“[/green] Deal deleted") - - -# ============================================================================ -# Pipelines (CRM) -# ============================================================================ - - -@cx_group.group() -def pipelines(): - """Manage sales pipelines.""" - pass - - -@pipelines.command(name="list") -def pipelines_list(): - """List pipelines.""" - resp = _request("get", "/v1/pipelines") - data = check_response(resp) - - table = Table(title="Pipelines", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Stages", style="white") - table.add_column("Deals", style="dim") - table.add_column("Value", style="green") - - for p in data.get("pipelines", []): - stages = " โ†’ ".join(p.get("stages", [])) - table.add_row( - p.get("name", ""), - stages, - str(p.get("deal_count", 0)), - f"${p.get('total_value', 0):,.0f}", - ) - - console.print(table) - - -@pipelines.command(name="create") -@click.option("--name", "-n", prompt=True, help="Pipeline name") -@click.option("--stages", "-s", required=True, help="Comma-separated stages") -def pipelines_create(name: str, stages: str): - """Create a pipeline.""" - stage_list = [s.strip() for s in stages.split(",")] - resp = _request("post", "/v1/pipelines", json={"name": name, "stages": stage_list}) - check_response(resp) - console.print(f"[green]โœ“[/green] Pipeline '{name}' created") - - -@pipelines.command(name="delete") -@click.argument("name") -def pipelines_delete(name: str): - """Delete a pipeline.""" - resp = _request("delete", f"/v1/pipelines/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Pipeline '{name}' deleted") - - -# ============================================================================ -# Invoices (ERP) -# ============================================================================ - - -@cx_group.group() -def invoices(): - """Manage invoices.""" - pass - - -@invoices.command(name="list") -@click.option( - "--status", - type=click.Choice(["draft", "sent", "paid", "overdue", "all"]), - default="all", -) -@click.option("--limit", "-n", default=50, help="Max results") -def invoices_list(status: str, limit: int): - """List invoices.""" - params: dict = {"limit": limit} - if status != "all": - params["status"] = status - resp = _request("get", "/v1/invoices", params=params) - data = check_response(resp) - - table = Table(title="Invoices", box=box.ROUNDED) - table.add_column("Number", style="cyan") - table.add_column("Customer", style="white") - table.add_column("Amount", style="green") - table.add_column("Status", style="yellow") - table.add_column("Due Date", style="dim") - - for inv in data.get("invoices", []): - st = inv.get("status", "draft") - st_style = {"paid": "green", "overdue": "red", "sent": "cyan"}.get(st, "yellow") - table.add_row( - inv.get("number", ""), - inv.get("customer_name", ""), - f"${inv.get('amount', 0):,.2f}", - f"[{st_style}]{st}[/{st_style}]", - inv.get("due_date", ""), - ) - - console.print(table) - - -@invoices.command(name="create") -@click.option("--customer", "-c", required=True, help="Customer ID") -@click.option("--amount", "-a", type=float, required=True, help="Amount") -@click.option("--due", "-d", help="Due date (YYYY-MM-DD)") -@click.option("--items", help="Line items JSON") -def invoices_create(customer: str, amount: float, due: str, items: str): - """Create an invoice.""" - payload: dict = {"customer_id": customer, "amount": amount} - if due: - payload["due_date"] = due - if items: - payload["items"] = json.loads(items) - resp = _request("post", "/v1/invoices", json=payload) - data = check_response(resp) - console.print( - f"[green]โœ“[/green] Invoice {data.get('number', '')} created for ${amount:,.2f}" - ) - - -@invoices.command(name="send") -@click.argument("invoice_number") -def invoices_send(invoice_number: str): - """Send an invoice.""" - resp = _request("post", f"/v1/invoices/{invoice_number}/send") - check_response(resp) - console.print(f"[green]โœ“[/green] Invoice {invoice_number} sent") - - -@invoices.command(name="mark-paid") -@click.argument("invoice_number") -@click.option("--payment-method", help="Payment method used") -def invoices_mark_paid(invoice_number: str, payment_method: str): - """Mark invoice as paid.""" - payload: dict = {} - if payment_method: - payload["payment_method"] = payment_method - resp = _request("post", f"/v1/invoices/{invoice_number}/pay", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Invoice {invoice_number} marked as paid") - - -@invoices.command(name="delete") -@click.argument("invoice_number") -def invoices_delete(invoice_number: str): - """Delete an invoice.""" - resp = _request("delete", f"/v1/invoices/{invoice_number}") - check_response(resp) - console.print(f"[green]โœ“[/green] Invoice {invoice_number} deleted") - - -# ============================================================================ -# Orders (ERP) -# ============================================================================ - - -@cx_group.group() -def orders(): - """Manage orders.""" - pass - - -@orders.command(name="list") -@click.option( - "--status", - type=click.Choice(["pending", "processing", "shipped", "delivered", "all"]), - default="all", -) -@click.option("--limit", "-n", default=50, help="Max results") -def orders_list(status: str, limit: int): - """List orders.""" - params: dict = {"limit": limit} - if status != "all": - params["status"] = status - resp = _request("get", "/v1/orders", params=params) - data = check_response(resp) - - table = Table(title="Orders", box=box.ROUNDED) - table.add_column("Order #", style="cyan") - table.add_column("Customer", style="white") - table.add_column("Total", style="green") - table.add_column("Status", style="yellow") - table.add_column("Date", style="dim") - - for o in data.get("orders", []): - st = o.get("status", "pending") - st_style = { - "delivered": "green", - "shipped": "cyan", - "processing": "yellow", - }.get(st, "white") - table.add_row( - o.get("order_number", ""), - o.get("customer_name", ""), - f"${o.get('total', 0):,.2f}", - f"[{st_style}]{st}[/{st_style}]", - o.get("created_at", ""), - ) - - console.print(table) - - -@orders.command(name="show") -@click.argument("order_id") -def orders_show(order_id: str): - """Show order details.""" - resp = _request("get", f"/v1/orders/{order_id}") - data = check_response(resp) - - info = ( - f"[cyan]Order #:[/cyan] {data.get('order_number', order_id)}\n" - f"[cyan]Customer:[/cyan] {data.get('customer_name', 'N/A')}\n" - f"[cyan]Total:[/cyan] ${data.get('total', 0):,.2f}\n" - f"[cyan]Status:[/cyan] {data.get('status', 'N/A')}\n" - f"[cyan]Items:[/cyan] {data.get('item_count', 0)}" - ) - console.print(Panel(info, title="Order Details", border_style="cyan")) - - items = data.get("items", []) - if items: - table = Table(title="Order Items", box=box.ROUNDED) - table.add_column("Product", style="white") - table.add_column("Qty", style="dim") - table.add_column("Price", style="green") - table.add_column("Subtotal", style="green") - - for item in items: - table.add_row( - item.get("product_name", ""), - str(item.get("quantity", 0)), - f"${item.get('unit_price', 0):,.2f}", - f"${item.get('subtotal', 0):,.2f}", - ) - - console.print(table) - - -@orders.command(name="update-status") -@click.argument("order_id") -@click.option( - "--status", - "-s", - type=click.Choice(["processing", "shipped", "delivered"]), - required=True, -) -@click.option("--tracking", "-t", help="Tracking number") -def orders_update_status(order_id: str, status: str, tracking: str): - """Update order status.""" - payload: dict = {"status": status} - if tracking: - payload["tracking_number"] = tracking - resp = _request("patch", f"/v1/orders/{order_id}", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Order {order_id} status updated to '{status}'") diff --git a/pkg/hanzo/src/hanzo/commands/dns/__init__.py b/pkg/hanzo/src/hanzo/commands/dns/__init__.py deleted file mode 100644 index 65d820bc3..000000000 --- a/pkg/hanzo/src/hanzo/commands/dns/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Hanzo DNS โ€” Multi-provider DNS management. - -Unified DNS interface that routes to any configured provider: -Cloudflare, CoreDNS, Route53, GoDaddy, DigitalOcean, etc. - -All providers queried in parallel, results merged. -""" - -from .cli import dns_group - -__all__ = ["dns_group"] diff --git a/pkg/hanzo/src/hanzo/commands/dns/cli.py b/pkg/hanzo/src/hanzo/commands/dns/cli.py deleted file mode 100644 index 8f9dcfa32..000000000 --- a/pkg/hanzo/src/hanzo/commands/dns/cli.py +++ /dev/null @@ -1,445 +0,0 @@ -"""Hanzo DNS CLI โ€” multi-provider DNS management. - -Queries all configured DNS providers in parallel, merges results. - -Usage: - hanzo dns zones # List zones from all providers - hanzo dns list -z hanzo.ai # List records (all providers) - hanzo dns list -z hanzo.ai --type CNAME # Filter by record type - hanzo dns add -z lux.financial app 1.2.3.4 # Add A record - hanzo dns add -z lux.financial app tgt --type CNAME --provider cloudflare - hanzo dns rm -z lux.financial app # Remove record - hanzo dns update hanzo.ai 1.2.3.4 5.6.7.8 # Batch update IPs - hanzo dns providers # Show configured providers -""" - -from __future__ import annotations - -from typing import Any -from concurrent.futures import ThreadPoolExecutor, as_completed - -import click - -# Import providers to trigger registration -from . import ( - coredns as _cd, # noqa: F401 - cloudflare as _cf, # noqa: F401 -) -from .provider import ( - DNSZone, - DNSRecord, - get_provider, - list_providers as _list_provider_names, - load_dns_config, - require_providers, - get_active_providers, -) - - -def _run_parallel(providers, method: str, *args, **kwargs) -> list[Any]: - """Run a method on all providers in parallel, collect results.""" - results: list[Any] = [] - - if len(providers) == 1: - try: - val = getattr(providers[0], method)(*args, **kwargs) - if isinstance(val, list): - results.extend(val) - else: - results.append((providers[0].name, val)) - except Exception as e: - results.append((providers[0].name, e)) - return results - - with ThreadPoolExecutor(max_workers=len(providers)) as pool: - futures = {} - for p in providers: - fut = pool.submit(getattr(p, method), *args, **kwargs) - futures[fut] = p.name - - for fut in as_completed(futures): - pname = futures[fut] - try: - val = fut.result() - if isinstance(val, list): - results.extend(val) - else: - results.append((pname, val)) - except Exception as e: - results.append((pname, e)) - - return results - - -def _resolve_provider(provider_name: str | None, providers: list): - """Pick a single provider by name, or first available.""" - if provider_name: - for p in providers: - if p.name == provider_name: - return p - raise click.ClickException( - f"Provider '{provider_name}' not configured. " - f"Active: {', '.join(p.name for p in providers)}" - ) - return providers[0] - - -# โ”€โ”€ Click group โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@click.group("dns") -def dns_group() -> None: - """Hanzo DNS โ€” multi-provider DNS management. - - \b - Queries all configured DNS providers in parallel. - Supports: Cloudflare, CoreDNS, and more. - - \b - Commands: - hanzo dns providers Show configured providers - hanzo dns zones List all zones - hanzo dns list -z hanzo.ai List records - hanzo dns add -z lux.financial app 1.2.3.4 Add A record - hanzo dns rm -z lux.financial app Remove record - hanzo dns update hanzo.ai OLD NEW Batch update IPs - - \b - Config: ~/.hanzo/credentials.json - {"dns": {"cloudflare": {"email": "...", "api_key": "..."}}} - {"dns": {"coredns": {"endpoint": "...", "api_key": "..."}}} - """ - - -# โ”€โ”€ providers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dns_group.command("providers") -def dns_providers() -> None: - """Show configured DNS providers and their status.""" - from rich.table import Table - - from ...utils.output import console - - configs = load_dns_config() - available = _list_provider_names() - - table = Table(title="DNS Providers") - table.add_column("Provider", style="cyan") - table.add_column("Status", style="green") - table.add_column("Details", style="dim") - - for name in available: - if name in configs: - cfg = configs[name] - detail_parts = [] - if "email" in cfg: - detail_parts.append(f"email={cfg['email']}") - if "endpoint" in cfg: - detail_parts.append(f"endpoint={cfg['endpoint']}") - detail = ", ".join(detail_parts) if detail_parts else "configured" - table.add_row(name, "[green]active[/green]", detail) - else: - table.add_row(name, "[dim]not configured[/dim]", "") - - console.print(table) - - if not configs: - console.print( - f"\n[yellow]No providers configured.[/yellow]\n" - f"Add credentials to ~/.hanzo/credentials.json" - ) - - -# โ”€โ”€ zones โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dns_group.command("zones") -@click.option("--provider", "-p", default=None, help="Filter by provider.") -def dns_zones(provider: str | None) -> None: - """List all DNS zones across all providers.""" - from rich.table import Table - - from ...utils.output import console - - providers = require_providers() - if provider: - providers = [p for p in providers if p.name == provider] - if not providers: - raise click.ClickException(f"Provider '{provider}' not configured.") - - results = _run_parallel(providers, "list_zones") - - zones: list[DNSZone] = [r for r in results if isinstance(r, DNSZone)] - errors = [ - r for r in results if isinstance(r, tuple) and isinstance(r[1], Exception) - ] - - if not zones and not errors: - console.print("[yellow]No zones found.[/yellow]") - return - - if zones: - table = Table(title="DNS Zones") - table.add_column("Name", style="cyan") - table.add_column("Provider", style="magenta") - table.add_column("ID", style="dim") - table.add_column("Status", style="green") - table.add_column("Plan", style="white") - - for z in sorted(zones, key=lambda x: x.name): - table.add_row(z.name, z.provider, z.id[:16], z.status, z.plan) - - console.print(table) - - for pname, err in errors: - console.print(f" [red]{pname}:[/red] {err}") - - -# โ”€โ”€ list โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dns_group.command("list") -@click.option("--zone", "-z", required=True, help="Zone name (e.g. hanzo.ai).") -@click.option( - "--type", "record_type", default=None, help="Record type filter (A, CNAME, etc.)." -) -@click.option("--provider", "-p", default=None, help="Filter by provider.") -def dns_list(zone: str, record_type: str | None, provider: str | None) -> None: - """List DNS records for a zone (queries all providers in parallel).""" - from rich.table import Table - - from ...utils.output import console - - providers = require_providers() - if provider: - providers = [p for p in providers if p.name == provider] - if not providers: - raise click.ClickException(f"Provider '{provider}' not configured.") - - results = _run_parallel(providers, "list_records", zone, record_type) - - records: list[DNSRecord] = [r for r in results if isinstance(r, DNSRecord)] - errors = [ - r for r in results if isinstance(r, tuple) and isinstance(r[1], Exception) - ] - - if not records: - label = f" {record_type}" if record_type else "" - console.print(f"[yellow]No{label} records found for {zone}.[/yellow]") - for pname, err in errors: - console.print(f" [red]{pname}:[/red] {err}") - return - - table = Table(title=f"DNS Records โ€” {zone}") - table.add_column("Type", style="white", min_width=6) - table.add_column("Name", style="cyan", min_width=25) - table.add_column("Content", style="white") - table.add_column("TTL", justify="right") - table.add_column("Proxied", justify="center") - table.add_column("Provider", style="magenta") - table.add_column("ID", style="dim") - - for r in sorted(records, key=lambda x: (x.provider, x.type, x.name)): - proxied = "[green]yes[/green]" if r.proxied else "no" - table.add_row( - r.type, - r.name, - r.content, - r.ttl_display, - proxied, - r.provider, - r.id[:12], - ) - - console.print(table) - console.print(f"\n{len(records)} record(s)") - - for pname, err in errors: - console.print(f" [red]{pname}:[/red] {err}") - - -# โ”€โ”€ add โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dns_group.command("add") -@click.option("--zone", "-z", required=True, help="Zone name (e.g. lux.financial).") -@click.argument("name") -@click.argument("content") -@click.option("--type", "record_type", default="A", help="Record type (default: A).") -@click.option( - "--proxied/--no-proxy", default=True, help="Proxy (default: on, CF only)." -) -@click.option("--ttl", default=1, help="TTL (1 = auto).") -@click.option( - "--provider", "-p", default=None, help="Target provider (default: first active)." -) -def dns_add( - zone: str, - name: str, - content: str, - record_type: str, - proxied: bool, - ttl: int, - provider: str | None, -) -> None: - """Add a DNS record. - - \b - Examples: - hanzo dns add -z lux.financial app 1.2.3.4 # A record - hanzo dns add -z lux.financial app tgt --type CNAME # CNAME - hanzo dns add -z hanzo.ai api 10.0.0.1 --no-proxy # Unproxied A - hanzo dns add -z hanzo.ai app 1.2.3.4 -p cloudflare # Specific provider - """ - from ...utils.output import console - - providers = require_providers() - target = _resolve_provider(provider, providers) - - rec = target.add_record(zone, name, content, record_type, proxied, ttl) - if rec: - console.print( - f"[green]โœ“[/green] Created {record_type.upper()} record via [magenta]{target.name}[/magenta]" - ) - console.print(f" Name: {rec.name}") - console.print(f" Content: {content}") - console.print(f" Proxied: {'yes' if rec.proxied else 'no'}") - console.print(f" ID: {rec.id}") - - -# โ”€โ”€ rm โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dns_group.command("rm") -@click.option("--zone", "-z", required=True, help="Zone name.") -@click.argument("name") -@click.option("--type", "record_type", default=None, help="Record type filter.") -@click.option("--force", "-f", is_flag=True, help="Skip confirmation.") -@click.option("--provider", "-p", default=None, help="Target provider (default: all).") -def dns_rm( - zone: str, name: str, record_type: str | None, force: bool, provider: str | None -) -> None: - """Remove DNS record(s) matching NAME. - - \b - By default searches all providers. Use --provider to target one. - - \b - Examples: - hanzo dns rm -z lux.financial app # All providers - hanzo dns rm -z lux.financial app --type A # Only A records - hanzo dns rm -z lux.financial app -p cloudflare # CF only - hanzo dns rm -z lux.financial app -f # Skip confirmation - """ - from ...utils.output import console - - providers = require_providers() - if provider: - providers = [p for p in providers if p.name == provider] - if not providers: - raise click.ClickException(f"Provider '{provider}' not configured.") - - # Preview what will be deleted - full_name = name if name.endswith(zone) else f"{name}.{zone}" - results = _run_parallel(providers, "list_records", zone, record_type) - matching = [r for r in results if isinstance(r, DNSRecord) and r.name == full_name] - - if not matching: - console.print(f"[yellow]No records found for {full_name}[/yellow]") - return - - console.print(f"Found [cyan]{len(matching)}[/cyan] record(s) for {full_name}:") - for r in matching: - console.print(f" [{r.provider}] {r.type:6} {r.name} -> {r.content}") - - if not force: - click.confirm("Delete these records?", abort=True) - - total_deleted = 0 - for p in providers: - deleted = p.remove_records(zone, name, record_type) - total_deleted += deleted - - console.print(f"[green]โœ“[/green] Deleted {total_deleted} record(s)") - - -# โ”€โ”€ update (batch IP swap) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dns_group.command("update") -@click.argument("zone") -@click.argument("old_ip") -@click.argument("new_ip") -@click.option("--type", "record_type", default="A", help="Record type (default: A).") -@click.option("--dry-run", is_flag=True, help="Show changes without applying.") -@click.option("--provider", "-p", default=None, help="Target provider (default: all).") -def dns_update( - zone: str, - old_ip: str, - new_ip: str, - record_type: str, - dry_run: bool, - provider: str | None, -) -> None: - """Batch update records: replace OLD_IP with NEW_IP across all providers. - - \b - Examples: - hanzo dns update hanzo.ai 1.2.3.4 5.6.7.8 --dry-run - hanzo dns update lux.network 10.0.0.1 10.0.0.2 - hanzo dns update hanzo.ai 1.2.3.4 5.6.7.8 -p cloudflare - """ - from rich.table import Table - - from ...utils.output import console - - providers = require_providers() - if provider: - providers = [p for p in providers if p.name == provider] - if not providers: - raise click.ClickException(f"Provider '{provider}' not configured.") - - # Preview: find matching records across all providers - results = _run_parallel(providers, "list_records", zone, record_type) - matching = [r for r in results if isinstance(r, DNSRecord) and r.content == old_ip] - - if not matching: - console.print( - f"[yellow]No {record_type} records found in {zone} " - f"pointing to {old_ip}.[/yellow]" - ) - return - - console.print( - f"Found [cyan]{len(matching)}[/cyan] {record_type} record(s) " - f"pointing to [red]{old_ip}[/red]\n" - ) - - table = Table(title="Records to Update") - table.add_column("Provider", style="magenta") - table.add_column("Name", style="cyan") - table.add_column("Current", style="red") - table.add_column("New", style="green") - - for r in matching: - table.add_row(r.provider, r.name, old_ip, new_ip) - - console.print(table) - - if dry_run: - console.print("\n[yellow]Dry run โ€” no changes applied.[/yellow]") - return - - total_updated = 0 - total_failed = 0 - - for p in providers: - updated, failed = p.update_records(zone, old_ip, new_ip, record_type) - total_updated += updated - total_failed += failed - - console.print( - f"\n[green]{total_updated} updated[/green]" - + (f", [red]{total_failed} failed[/red]" if total_failed else "") - ) diff --git a/pkg/hanzo/src/hanzo/commands/dns/cloudflare.py b/pkg/hanzo/src/hanzo/commands/dns/cloudflare.py deleted file mode 100644 index 2057251f7..000000000 --- a/pkg/hanzo/src/hanzo/commands/dns/cloudflare.py +++ /dev/null @@ -1,199 +0,0 @@ -"""Cloudflare DNS provider.""" - -from __future__ import annotations - -from typing import Any - -import click - -from .provider import DNSZone, DNSRecord, DNSProvider, register_provider - -CF_API = "https://api.cloudflare.com/client/v4" - - -class CloudflareProvider(DNSProvider): - name = "cloudflare" - - def __init__(self, config: dict[str, Any]) -> None: - self.email = config.get("email", "") - self.api_key = config.get("api_key", "") - if not self.email or not self.api_key: - raise ValueError("Cloudflare requires 'email' and 'api_key'") - - def _headers(self) -> dict[str, str]: - return { - "X-Auth-Email": self.email, - "X-Auth-Key": self.api_key, - "Content-Type": "application/json", - } - - def _client(self): - import httpx - - return httpx.Client(headers=self._headers(), timeout=30.0) - - def _get(self, client, path: str, params: dict[str, Any] | None = None) -> Any: - resp = client.get(f"{CF_API}{path}", params=params) - data = resp.json() - if not data.get("success"): - errors = data.get("errors", []) - msg = "; ".join(e.get("message", str(e)) for e in errors) - raise click.ClickException(f"Cloudflare API error: {msg}") - return data - - def _resolve_zone_id(self, client, zone_name: str) -> str: - data = self._get(client, "/zones", params={"name": zone_name, "per_page": "1"}) - zones = data.get("result", []) - if not zones: - raise click.ClickException( - f"Zone '{zone_name}' not found in Cloudflare account." - ) - return zones[0]["id"] - - # โ”€โ”€ Interface implementation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_zones(self) -> list[DNSZone]: - with self._client() as client: - data = self._get(client, "/zones", params={"per_page": "50"}) - - return [ - DNSZone( - id=z.get("id", ""), - name=z.get("name", ""), - status=z.get("status", ""), - plan=z.get("plan", {}).get("name", ""), - provider=self.name, - raw=z, - ) - for z in data.get("result", []) - ] - - def list_records( - self, zone: str, record_type: str | None = None - ) -> list[DNSRecord]: - with self._client() as client: - zone_id = self._resolve_zone_id(client, zone) - - params: dict[str, str] = {"per_page": "100"} - if record_type: - params["type"] = record_type - - data = self._get(client, f"/zones/{zone_id}/dns_records", params=params) - - return [ - DNSRecord( - id=r.get("id", ""), - zone=zone, - type=r.get("type", ""), - name=r.get("name", ""), - content=r.get("content", ""), - ttl=r.get("ttl", 1), - proxied=r.get("proxied", False), - provider=self.name, - raw=r, - ) - for r in data.get("result", []) - ] - - def add_record( - self, - zone: str, - name: str, - content: str, - record_type: str = "A", - proxied: bool = True, - ttl: int = 1, - ) -> DNSRecord | None: - full_name = name if name.endswith(zone) else f"{name}.{zone}" - - with self._client() as client: - zone_id = self._resolve_zone_id(client, zone) - - payload = { - "type": record_type.upper(), - "name": full_name, - "content": content, - "ttl": ttl, - "proxied": proxied, - } - - resp = client.post(f"{CF_API}/zones/{zone_id}/dns_records", json=payload) - result = resp.json() - - if result.get("success"): - rec = result.get("result", {}) - return DNSRecord( - id=rec.get("id", ""), - zone=zone, - type=record_type.upper(), - name=rec.get("name", full_name), - content=content, - ttl=ttl, - proxied=proxied, - provider=self.name, - raw=rec, - ) - else: - errors = result.get("errors", []) - msg = "; ".join(e.get("message", str(e)) for e in errors) - raise click.ClickException(f"Cloudflare: {msg}") - - def remove_records( - self, zone: str, name: str, record_type: str | None = None - ) -> int: - full_name = name if name.endswith(zone) else f"{name}.{zone}" - - with self._client() as client: - zone_id = self._resolve_zone_id(client, zone) - - params: dict[str, str] = {"name": full_name, "per_page": "100"} - if record_type: - params["type"] = record_type - - data = self._get(client, f"/zones/{zone_id}/dns_records", params=params) - records = data.get("result", []) - - deleted = 0 - for r in records: - resp = client.delete(f"{CF_API}/zones/{zone_id}/dns_records/{r['id']}") - if resp.json().get("success"): - deleted += 1 - return deleted - - def update_records( - self, zone: str, old_content: str, new_content: str, record_type: str = "A" - ) -> tuple[int, int]: - with self._client() as client: - zone_id = self._resolve_zone_id(client, zone) - - data = self._get( - client, - f"/zones/{zone_id}/dns_records", - params={ - "type": record_type, - "content": old_content, - "per_page": "100", - }, - ) - - records = data.get("result", []) - updated = 0 - failed = 0 - - for r in records: - try: - resp = client.patch( - f"{CF_API}/zones/{zone_id}/dns_records/{r['id']}", - json={"content": new_content}, - ) - if resp.json().get("success"): - updated += 1 - else: - failed += 1 - except Exception: - failed += 1 - - return updated, failed - - -register_provider("cloudflare", CloudflareProvider) diff --git a/pkg/hanzo/src/hanzo/commands/dns/coredns.py b/pkg/hanzo/src/hanzo/commands/dns/coredns.py deleted file mode 100644 index 2a7a8413d..000000000 --- a/pkg/hanzo/src/hanzo/commands/dns/coredns.py +++ /dev/null @@ -1,173 +0,0 @@ -"""CoreDNS (Hanzo DNS) provider. - -Manages DNS via Hanzo's CoreDNS API (K8s-native DNS for Hanzo infrastructure). -Endpoint: configurable, defaults to https://dns.hanzo.ai/api/v1 -""" - -from __future__ import annotations - -from typing import Any - -import click - -from .provider import DNSZone, DNSRecord, DNSProvider, register_provider - -DEFAULT_ENDPOINT = "https://dns.hanzo.ai/api/v1" - - -class CoreDNSProvider(DNSProvider): - name = "coredns" - - def __init__(self, config: dict[str, Any]) -> None: - self.endpoint = config.get("endpoint", DEFAULT_ENDPOINT).rstrip("/") - self.api_key = config.get("api_key", "") - self.token = config.get("token", self.api_key) - if not self.token: - raise ValueError("CoreDNS requires 'api_key' or 'token'") - - def _headers(self) -> dict[str, str]: - return { - "Authorization": f"Bearer {self.token}", - "Content-Type": "application/json", - } - - def _client(self): - import httpx - - return httpx.Client(headers=self._headers(), timeout=30.0) - - def _get(self, client, path: str, params: dict[str, Any] | None = None) -> Any: - resp = client.get(f"{self.endpoint}{path}", params=params) - resp.raise_for_status() - return resp.json() - - # โ”€โ”€ Interface implementation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_zones(self) -> list[DNSZone]: - with self._client() as client: - data = self._get(client, "/zones") - - zones = ( - data - if isinstance(data, list) - else data.get("zones", data.get("result", [])) - ) - return [ - DNSZone( - id=z.get("id", z.get("name", "")), - name=z.get("name", ""), - status=z.get("status", "active"), - plan="CoreDNS", - provider=self.name, - raw=z, - ) - for z in zones - ] - - def list_records( - self, zone: str, record_type: str | None = None - ) -> list[DNSRecord]: - with self._client() as client: - params: dict[str, str] = {} - if record_type: - params["type"] = record_type - - data = self._get(client, f"/zones/{zone}/records", params=params) - - records = ( - data - if isinstance(data, list) - else data.get("records", data.get("result", [])) - ) - return [ - DNSRecord( - id=r.get("id", ""), - zone=zone, - type=r.get("type", ""), - name=r.get("name", ""), - content=r.get("content", r.get("value", "")), - ttl=r.get("ttl", 1), - proxied=False, - provider=self.name, - raw=r, - ) - for r in records - ] - - def add_record( - self, - zone: str, - name: str, - content: str, - record_type: str = "A", - proxied: bool = True, - ttl: int = 1, - ) -> DNSRecord | None: - full_name = name if name.endswith(zone) else f"{name}.{zone}" - - with self._client() as client: - payload = { - "type": record_type.upper(), - "name": full_name, - "content": content, - "ttl": ttl, - } - - resp = client.post(f"{self.endpoint}/zones/{zone}/records", json=payload) - resp.raise_for_status() - rec = resp.json() - - return DNSRecord( - id=rec.get("id", ""), - zone=zone, - type=record_type.upper(), - name=rec.get("name", full_name), - content=content, - ttl=ttl, - proxied=False, - provider=self.name, - raw=rec, - ) - - def remove_records( - self, zone: str, name: str, record_type: str | None = None - ) -> int: - full_name = name if name.endswith(zone) else f"{name}.{zone}" - - records = self.list_records(zone, record_type) - matching = [r for r in records if r.name == full_name] - - with self._client() as client: - deleted = 0 - for r in matching: - try: - resp = client.delete(f"{self.endpoint}/zones/{zone}/records/{r.id}") - resp.raise_for_status() - deleted += 1 - except Exception: - pass - return deleted - - def update_records( - self, zone: str, old_content: str, new_content: str, record_type: str = "A" - ) -> tuple[int, int]: - records = self.list_records(zone, record_type) - matching = [r for r in records if r.content == old_content] - - with self._client() as client: - updated = 0 - failed = 0 - for r in matching: - try: - resp = client.patch( - f"{self.endpoint}/zones/{zone}/records/{r.id}", - json={"content": new_content}, - ) - resp.raise_for_status() - updated += 1 - except Exception: - failed += 1 - return updated, failed - - -register_provider("coredns", CoreDNSProvider) diff --git a/pkg/hanzo/src/hanzo/commands/dns/provider.py b/pkg/hanzo/src/hanzo/commands/dns/provider.py deleted file mode 100644 index c2296fe8f..000000000 --- a/pkg/hanzo/src/hanzo/commands/dns/provider.py +++ /dev/null @@ -1,187 +0,0 @@ -"""DNS provider abstraction and registry.""" - -from __future__ import annotations - -import sys -import json -from abc import ABC, abstractmethod -from typing import Any -from pathlib import Path -from dataclasses import field, dataclass - -CREDENTIALS_FILE = Path.home() / ".hanzo" / "credentials.json" - - -@dataclass -class DNSRecord: - """Normalized DNS record across all providers.""" - - id: str - zone: str - type: str - name: str - content: str - ttl: int = 1 - proxied: bool = False - provider: str = "" - raw: dict[str, Any] = field(default_factory=dict) - - @property - def ttl_display(self) -> str: - return "Auto" if self.ttl == 1 else str(self.ttl) - - -@dataclass -class DNSZone: - """Normalized DNS zone across all providers.""" - - id: str - name: str - status: str = "" - plan: str = "" - provider: str = "" - raw: dict[str, Any] = field(default_factory=dict) - - -class DNSProvider(ABC): - """Abstract DNS provider interface.""" - - name: str = "unknown" - - @abstractmethod - def list_zones(self) -> list[DNSZone]: - """List all DNS zones.""" - - @abstractmethod - def list_records( - self, zone: str, record_type: str | None = None - ) -> list[DNSRecord]: - """List DNS records for a zone.""" - - @abstractmethod - def add_record( - self, - zone: str, - name: str, - content: str, - record_type: str = "A", - proxied: bool = True, - ttl: int = 1, - ) -> DNSRecord | None: - """Add a DNS record. Returns the created record or None on failure.""" - - @abstractmethod - def remove_records( - self, zone: str, name: str, record_type: str | None = None - ) -> int: - """Remove DNS records matching name. Returns count deleted.""" - - @abstractmethod - def update_records( - self, zone: str, old_content: str, new_content: str, record_type: str = "A" - ) -> tuple[int, int]: - """Batch update records. Returns (updated, failed) counts.""" - - -# โ”€โ”€ Provider registry โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -_REGISTRY: dict[str, type[DNSProvider]] = {} - - -def register_provider(name: str, cls: type[DNSProvider]) -> None: - _REGISTRY[name] = cls - - -def get_provider(name: str, config: dict[str, Any]) -> DNSProvider: - cls = _REGISTRY.get(name) - if cls is None: - raise ValueError( - f"Unknown DNS provider: {name}. " - f"Available: {', '.join(sorted(_REGISTRY.keys()))}" - ) - return cls(config) - - -def list_providers() -> list[str]: - return sorted(_REGISTRY.keys()) - - -# โ”€โ”€ Config loading โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -def load_dns_config() -> dict[str, dict[str, Any]]: - """Load all DNS provider configs from ~/.hanzo/credentials.json. - - Supports both legacy format and new multi-provider format: - - Legacy: - {"cloudflare": {"email": "...", "api_key": "..."}} - - Multi-provider: - {"dns": {"cloudflare": {...}, "coredns": {...}, "route53": {...}}} - - Both can coexist โ€” legacy cloudflare key is merged into dns.cloudflare. - """ - if not CREDENTIALS_FILE.exists(): - return {} - - try: - data = json.loads(CREDENTIALS_FILE.read_text()) - except (json.JSONDecodeError, OSError): - return {} - - providers: dict[str, dict[str, Any]] = {} - - # New format: dns.{provider} - dns_section = data.get("dns", {}) - if isinstance(dns_section, dict): - for name, cfg in dns_section.items(): - if isinstance(cfg, dict) and cfg: - providers[name] = cfg - - # Legacy: top-level cloudflare key - cf_legacy = data.get("cloudflare", {}) - if ( - isinstance(cf_legacy, dict) - and cf_legacy.get("email") - and cf_legacy.get("api_key") - ): - providers.setdefault("cloudflare", {}).update(cf_legacy) - - return providers - - -def get_active_providers() -> list[DNSProvider]: - """Load and instantiate all configured DNS providers.""" - configs = load_dns_config() - providers = [] - - for name, cfg in configs.items(): - if name not in _REGISTRY: - continue - try: - providers.append(get_provider(name, cfg)) - except Exception: - pass - - return providers - - -def require_providers() -> list[DNSProvider]: - """Like get_active_providers but exits if none configured.""" - from ...utils.output import console - - providers = get_active_providers() - if not providers: - console.print( - "[red]No DNS providers configured.[/red]\n" - f"Add credentials to {CREDENTIALS_FILE}:\n\n" - ' {{"dns": {{"cloudflare": {{"email": "...", "api_key": "..."}}}}}}\n\n' - "Or legacy format:\n" - ' {{"cloudflare": {{"email": "...", "api_key": "..."}}}}\n\n' - f"Supported providers: {', '.join(list_providers())}" - ) - sys.exit(1) - - return providers diff --git a/pkg/hanzo/src/hanzo/commands/doc.py b/pkg/hanzo/src/hanzo/commands/doc.py deleted file mode 100644 index 7ae1fa3fc..000000000 --- a/pkg/hanzo/src/hanzo/commands/doc.py +++ /dev/null @@ -1,632 +0,0 @@ -"""Hanzo Doc - Document database CLI. - -MongoDB-compatible document database with global distribution. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -DOC_URL = os.getenv("HANZO_DOC_URL", "https://doc.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(DOC_URL, method, path, **kwargs) - - -@click.group(name="doc") -def doc_group(): - """Hanzo Doc - Document database (MongoDB-compatible). - - \b - Databases: - hanzo doc create # Create database - hanzo doc list # List databases - hanzo doc delete # Delete database - - \b - Collections: - hanzo doc collections list # List collections - hanzo doc collections create # Create collection - hanzo doc collections drop # Drop collection - - \b - Data: - hanzo doc find # Query documents - hanzo doc insert # Insert document - hanzo doc update # Update documents - hanzo doc delete-docs # Delete documents - - \b - Indexes: - hanzo doc indexes list # List indexes - hanzo doc indexes create # Create index - hanzo doc indexes drop # Drop index - """ - pass - - -# ============================================================================ -# Database Management -# ============================================================================ - - -@doc_group.command(name="create") -@click.argument("name") -@click.option("--region", "-r", multiple=True, help="Regions for replication") -@click.option( - "--tier", - "-t", - type=click.Choice(["free", "standard", "dedicated"]), - default="standard", -) -def doc_create(name: str, region: tuple, tier: str): - """Create a document database.""" - payload = {"name": name, "tier": tier} - if region: - payload["regions"] = list(region) - - resp = _request("post", "/v1/databases", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Database '{name}' created") - console.print(f" ID: {data.get('id', '-')}") - console.print(f" Tier: {tier}") - if region: - console.print(f" Regions: {', '.join(region)}") - console.print( - f" Connection: {data.get('connection_string', f'mongodb://doc.hanzo.ai/{name}')}" - ) - - -@doc_group.command(name="list") -def doc_list(): - """List document databases.""" - resp = _request("get", "/v1/databases") - data = check_response(resp) - dbs = data.get("databases", data.get("items", [])) - - table = Table(title="Document Databases", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Tier", style="white") - table.add_column("Collections", style="green") - table.add_column("Size", style="yellow") - table.add_column("Status", style="dim") - - for db in dbs: - status = db.get("status", "unknown") - style = "green" if status == "running" else "yellow" - table.add_row( - db.get("name", ""), - db.get("tier", "-"), - str(db.get("collection_count", 0)), - db.get("size", "0 B"), - f"[{style}]โ— {status}[/{style}]", - ) - - console.print(table) - if not dbs: - console.print( - "[dim]No databases found. Create one with 'hanzo doc create'[/dim]" - ) - - -@doc_group.command(name="describe") -@click.argument("name") -def doc_describe(name: str): - """Show database details.""" - resp = _request("get", f"/v1/databases/{name}") - data = check_response(resp) - - status = data.get("status", "unknown") - style = "green" if status == "running" else "yellow" - - console.print( - Panel( - f"[cyan]Name:[/cyan] {data.get('name', name)}\n" - f"[cyan]Tier:[/cyan] {data.get('tier', '-')}\n" - f"[cyan]Status:[/cyan] [{style}]โ— {status}[/{style}]\n" - f"[cyan]Collections:[/cyan] {data.get('collection_count', 0)}\n" - f"[cyan]Documents:[/cyan] {data.get('document_count', 0):,}\n" - f"[cyan]Size:[/cyan] {data.get('size', '0 B')}\n" - f"[cyan]Regions:[/cyan] {', '.join(data.get('regions', ['-']))}\n" - f"[cyan]Connection:[/cyan] {data.get('connection_string', f'mongodb://doc.hanzo.ai/{name}')}", - title="Database Details", - border_style="cyan", - ) - ) - - -@doc_group.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True) -def doc_delete(name: str, force: bool): - """Delete a document database.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete database '{name}'?[/red]"): - return - - resp = _request("delete", f"/v1/databases/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Database '{name}' deleted") - - -@doc_group.command(name="connect") -@click.argument("name") -def doc_connect(name: str): - """Get connection string.""" - resp = _request("get", f"/v1/databases/{name}") - data = check_response(resp) - conn = data.get( - "connection_string", f"mongodb://doc.hanzo.ai/{name}?authSource=admin" - ) - console.print(f"[cyan]Connection string for '{name}':[/cyan]") - console.print(conn) - - -# ============================================================================ -# Collections -# ============================================================================ - - -@doc_group.group() -def collections(): - """Manage collections.""" - pass - - -@collections.command(name="list") -@click.option("--db", "-d", default="default", help="Database name") -def collections_list(db: str): - """List collections in a database.""" - resp = _request("get", f"/v1/databases/{db}/collections") - data = check_response(resp) - items = data.get("collections", data.get("items", [])) - - table = Table(title=f"Collections in '{db}'", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Documents", style="green") - table.add_column("Size", style="yellow") - table.add_column("Indexes", style="dim") - - for c in items: - table.add_row( - c.get("name", ""), - str(c.get("document_count", 0)), - c.get("size", "0 B"), - str(c.get("index_count", 0)), - ) - - console.print(table) - if not items: - console.print("[dim]No collections found[/dim]") - - -@collections.command(name="create") -@click.argument("name") -@click.option("--db", "-d", default="default", help="Database name") -@click.option("--capped", is_flag=True, help="Create capped collection") -@click.option("--size", "-s", help="Max size for capped collection") -@click.option("--validator", "-v", help="JSON schema validator") -def collections_create(name: str, db: str, capped: bool, size: str, validator: str): - """Create a collection.""" - payload = {"name": name} - if capped: - payload["capped"] = True - if size: - payload["max_size"] = size - if validator: - payload["validator"] = json.loads(validator) - - resp = _request("post", f"/v1/databases/{db}/collections", json=payload) - check_response(resp) - - console.print(f"[green]โœ“[/green] Collection '{name}' created in '{db}'") - if capped: - console.print(f" Capped: Yes (max {size})") - - -@collections.command(name="drop") -@click.argument("name") -@click.option("--db", "-d", default="default") -@click.option("--force", "-f", is_flag=True) -def collections_drop(name: str, db: str, force: bool): - """Drop a collection.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Drop collection '{name}'?[/red]"): - return - - resp = _request("delete", f"/v1/databases/{db}/collections/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Collection '{name}' dropped") - - -@collections.command(name="stats") -@click.argument("name") -@click.option("--db", "-d", default="default") -def collections_stats(name: str, db: str): - """Show collection statistics.""" - resp = _request("get", f"/v1/databases/{db}/collections/{name}/stats") - data = check_response(resp) - - console.print( - Panel( - f"[cyan]Collection:[/cyan] {data.get('name', name)}\n" - f"[cyan]Documents:[/cyan] {data.get('document_count', 0):,}\n" - f"[cyan]Size:[/cyan] {data.get('size', '0 B')}\n" - f"[cyan]Avg doc size:[/cyan] {data.get('avg_doc_size', '0 B')}\n" - f"[cyan]Indexes:[/cyan] {data.get('index_count', 0)}\n" - f"[cyan]Index size:[/cyan] {data.get('index_size', '0 B')}", - title="Collection Statistics", - border_style="cyan", - ) - ) - - -# ============================================================================ -# Data Operations -# ============================================================================ - - -@doc_group.command(name="find") -@click.argument("collection") -@click.option("--db", "-d", default="default", help="Database name") -@click.option("--query", "-q", default="{}", help="Query filter (JSON)") -@click.option("--projection", "-p", help="Field projection") -@click.option("--sort", "-s", help="Sort order") -@click.option("--limit", "-n", default=20, help="Max results") -@click.option("--skip", type=int, help="Skip documents") -def doc_find( - collection: str, - db: str, - query: str, - projection: str, - sort: str, - limit: int, - skip: int, -): - """Query documents in a collection. - - \b - Examples: - hanzo doc find users - hanzo doc find users -q '{"status": "active"}' - hanzo doc find users -q '{"age": {"$gt": 21}}' -s '{"name": 1}' - """ - payload = {"filter": json.loads(query), "limit": limit} - if projection: - payload["projection"] = json.loads(projection) - if sort: - payload["sort"] = json.loads(sort) - if skip: - payload["skip"] = skip - - resp = _request( - "post", f"/v1/databases/{db}/collections/{collection}/find", json=payload - ) - data = check_response(resp) - docs = data.get("documents", data.get("items", [])) - - console.print(f"[cyan]Results from {db}.{collection}:[/cyan]") - for doc in docs: - console.print(json.dumps(doc, indent=2, default=str)) - - total = data.get("total", len(docs)) - if not docs: - console.print("[dim]No documents found[/dim]") - else: - console.print(f"\n[dim]{total} total, showing {len(docs)}[/dim]") - - -@doc_group.command(name="insert") -@click.argument("collection") -@click.option("--db", "-d", default="default") -@click.option("--doc", help="Document JSON") -@click.option("--file", "-f", type=click.Path(exists=True), help="Document file") -def doc_insert(collection: str, db: str, doc: str, file: str): - """Insert a document.""" - if file: - from pathlib import Path - - document = json.loads(Path(file).read_text()) - elif doc: - document = json.loads(doc) - else: - raise click.ClickException("Provide --doc or --file") - - resp = _request( - "post", - f"/v1/databases/{db}/collections/{collection}/insert", - json={"document": document}, - ) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Inserted document into '{collection}'") - console.print(f" _id: {data.get('inserted_id', data.get('_id', '-'))}") - - -@doc_group.command(name="update") -@click.argument("collection") -@click.option("--db", "-d", default="default") -@click.option("--filter", "-q", required=True, help="Query filter") -@click.option("--set", "set_fields", multiple=True, help="Fields to set") -@click.option("--unset", "unset_fields", multiple=True, help="Fields to unset") -@click.option("--upsert", is_flag=True, help="Insert if not found") -def doc_update( - collection: str, - db: str, - filter: str, - set_fields: tuple, - unset_fields: tuple, - upsert: bool, -): - """Update documents. - - \b - Examples: - hanzo doc update users -q '{"status": "pending"}' --set status=active - hanzo doc update users -q '{"_id": "..."}' --set name=John --set age=30 - """ - update_doc = {} - if set_fields: - set_dict = {} - for field in set_fields: - k, v = field.split("=", 1) - set_dict[k] = v - update_doc["$set"] = set_dict - if unset_fields: - update_doc["$unset"] = {f: "" for f in unset_fields} - - payload = { - "filter": json.loads(filter), - "update": update_doc, - "upsert": upsert, - } - - resp = _request( - "post", f"/v1/databases/{db}/collections/{collection}/update", json=payload - ) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Updated documents in '{collection}'") - console.print( - f" Matched: {data.get('matched_count', 0)}, Modified: {data.get('modified_count', 0)}" - ) - if upsert and data.get("upserted_id"): - console.print(f" Upserted: {data['upserted_id']}") - - -@doc_group.command(name="delete-docs") -@click.argument("collection") -@click.option("--db", "-d", default="default") -@click.option("--filter", "-q", required=True, help="Query filter") -@click.option("--force", "-f", is_flag=True) -def doc_delete_docs(collection: str, db: str, filter: str, force: bool): - """Delete documents matching filter.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask("[red]Delete matching documents?[/red]"): - return - - payload = {"filter": json.loads(filter)} - resp = _request( - "post", f"/v1/databases/{db}/collections/{collection}/delete", json=payload - ) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Deleted documents from '{collection}'") - console.print(f" Deleted: {data.get('deleted_count', 0)}") - - -@doc_group.command(name="count") -@click.argument("collection") -@click.option("--db", "-d", default="default") -@click.option("--query", "-q", default="{}", help="Query filter") -def doc_count(collection: str, db: str, query: str): - """Count documents.""" - payload = {"filter": json.loads(query)} - resp = _request( - "post", f"/v1/databases/{db}/collections/{collection}/count", json=payload - ) - data = check_response(resp) - console.print(f"[cyan]Count in {db}.{collection}:[/cyan] {data.get('count', 0):,}") - - -# ============================================================================ -# Indexes -# ============================================================================ - - -@doc_group.group() -def indexes(): - """Manage indexes.""" - pass - - -@indexes.command(name="list") -@click.argument("collection") -@click.option("--db", "-d", default="default") -def indexes_list(collection: str, db: str): - """List indexes on a collection.""" - resp = _request("get", f"/v1/databases/{db}/collections/{collection}/indexes") - data = check_response(resp) - items = data.get("indexes", []) - - table = Table(title=f"Indexes on '{collection}'", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Keys", style="white") - table.add_column("Type", style="yellow") - table.add_column("Size", style="dim") - - for idx in items: - keys = idx.get("keys", idx.get("key", {})) - keys_str = ( - ", ".join(f"{k}: {v}" for k, v in keys.items()) - if isinstance(keys, dict) - else str(keys) - ) - table.add_row( - idx.get("name", "-"), - keys_str, - idx.get("type", "standard"), - idx.get("size", "-"), - ) - - console.print(table) - - -@indexes.command(name="create") -@click.argument("collection") -@click.option("--db", "-d", default="default") -@click.option( - "--keys", "-k", required=True, help="Index keys (e.g., 'field:1' or 'field:-1')" -) -@click.option("--name", "-n", help="Index name") -@click.option("--unique", "-u", is_flag=True, help="Unique index") -@click.option("--sparse", is_flag=True, help="Sparse index") -@click.option("--ttl", help="TTL in seconds") -def indexes_create( - collection: str, db: str, keys: str, name: str, unique: bool, sparse: bool, ttl: str -): - """Create an index. - - \b - Examples: - hanzo doc indexes create users -k email:1 --unique - hanzo doc indexes create logs -k createdAt:1 --ttl 86400 - hanzo doc indexes create products -k 'category:1,price:-1' - """ - key_dict = {} - for pair in keys.split(","): - parts = pair.strip().split(":") - key_dict[parts[0]] = int(parts[1]) if len(parts) > 1 else 1 - - payload = {"keys": key_dict} - if name: - payload["name"] = name - if unique: - payload["unique"] = True - if sparse: - payload["sparse"] = True - if ttl: - payload["expire_after_seconds"] = int(ttl) - - resp = _request( - "post", f"/v1/databases/{db}/collections/{collection}/indexes", json=payload - ) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Index created on '{collection}'") - console.print(f" Name: {data.get('name', name or '-')}") - console.print(f" Keys: {keys}") - if unique: - console.print(" Unique: Yes") - - -@indexes.command(name="drop") -@click.argument("collection") -@click.argument("index_name") -@click.option("--db", "-d", default="default") -def indexes_drop(collection: str, index_name: str, db: str): - """Drop an index.""" - resp = _request( - "delete", f"/v1/databases/{db}/collections/{collection}/indexes/{index_name}" - ) - check_response(resp) - console.print(f"[green]โœ“[/green] Index '{index_name}' dropped from '{collection}'") - - -# ============================================================================ -# Admin -# ============================================================================ - - -@doc_group.command(name="backup") -@click.argument("name") -@click.option("--output", "-o", help="Output path") -def doc_backup(name: str, output: str): - """Create database backup.""" - payload = {"database": name} - if output: - payload["output"] = output - - resp = _request("post", "/v1/backups", json=payload) - data = check_response(resp) - console.print(f"[green]โœ“[/green] Backup created for '{name}'") - console.print(f" Backup ID: {data.get('id', '-')}") - - -@doc_group.command(name="restore") -@click.argument("backup_id") -@click.option("--to", "-t", help="Target database name") -def doc_restore(backup_id: str, to: str): - """Restore from backup.""" - payload = {"backup_id": backup_id} - if to: - payload["target_database"] = to - - resp = _request("post", "/v1/backups/restore", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Restored from backup '{backup_id}'") - - -@doc_group.command(name="users") -@click.argument("action", type=click.Choice(["list", "create", "delete"])) -@click.option("--db", "-d", default="default") -@click.option("--username", "-u", help="Username") -@click.option("--role", "-r", help="Role (read, readWrite, dbAdmin)") -def doc_users(action: str, db: str, username: str, role: str): - """Manage database users.""" - if action == "list": - resp = _request("get", f"/v1/databases/{db}/users") - data = check_response(resp) - users = data.get("users", []) - - table = Table(title=f"Users for '{db}'", box=box.ROUNDED) - table.add_column("Username", style="cyan") - table.add_column("Role", style="green") - table.add_column("Created", style="dim") - - for u in users: - table.add_row( - u.get("username", ""), - u.get("role", "-"), - str(u.get("created_at", ""))[:19], - ) - - console.print(table) - if not users: - console.print("[dim]No users found[/dim]") - - elif action == "create": - if not username or not role: - raise click.ClickException("--username and --role required for create") - resp = _request( - "post", - f"/v1/databases/{db}/users", - json={"username": username, "role": role}, - ) - data = check_response(resp) - console.print(f"[green]โœ“[/green] User '{username}' created with role '{role}'") - if data.get("password"): - console.print(f" Password: {data['password']}") - - elif action == "delete": - if not username: - raise click.ClickException("--username required for delete") - resp = _request("delete", f"/v1/databases/{db}/users/{username}") - check_response(resp) - console.print(f"[green]โœ“[/green] User '{username}' deleted") diff --git a/pkg/hanzo/src/hanzo/commands/env.py b/pkg/hanzo/src/hanzo/commands/env.py deleted file mode 100644 index d4b60dfde..000000000 --- a/pkg/hanzo/src/hanzo/commands/env.py +++ /dev/null @@ -1,192 +0,0 @@ -"""Hanzo Env - Environment management CLI. - -Environment configuration and switching. -""" - -import click -from rich import box -from rich.panel import Panel -from rich.table import Table -from rich.syntax import Syntax - -from ..utils.output import console - - -@click.group(name="env") -def env_group(): - """Hanzo Env - Environment management. - - \b - Environments: - hanzo env list # List environments - hanzo env create # Create environment - hanzo env use # Switch environment - hanzo env current # Show current environment - hanzo env delete # Delete environment - - \b - Variables: - hanzo env vars # List env variables - hanzo env set # Set variable - hanzo env unset # Unset variable - hanzo env diff # Compare environments - """ - pass - - -# ============================================================================ -# Environment Management -# ============================================================================ - - -@env_group.command(name="list") -@click.option("--project", "-p", help="Project ID") -def env_list(project: str): - """List all environments.""" - table = Table(title="Environments", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Status", style="green") - table.add_column("URL", style="dim") - table.add_column("Variables", style="yellow") - table.add_column("Updated", style="dim") - - table.add_row( - "development", "[green]โ—[/green] active", "dev.app.hanzo.ai", "12", "2024-01-15" - ) - table.add_row( - "staging", "[yellow]โ—[/yellow] idle", "staging.app.hanzo.ai", "12", "2024-01-14" - ) - table.add_row( - "production", "[green]โ—[/green] active", "app.hanzo.ai", "15", "2024-01-13" - ) - - console.print(table) - - -@env_group.command(name="create") -@click.argument("name") -@click.option("--from", "from_env", help="Clone from existing environment") -@click.option("--description", "-d", help="Environment description") -def env_create(name: str, from_env: str, description: str): - """Create a new environment.""" - console.print(f"[green]โœ“[/green] Environment '{name}' created") - if from_env: - console.print(f" Cloned from: {from_env}") - console.print(f" URL: {name}.app.hanzo.ai") - - -@env_group.command(name="use") -@click.argument("name") -def env_use(name: str): - """Switch to an environment.""" - console.print(f"[green]โœ“[/green] Switched to environment '{name}'") - - -@env_group.command(name="current") -def env_current(): - """Show current environment.""" - console.print( - Panel( - "[cyan]Environment:[/cyan] development\n" - "[cyan]Project:[/cyan] my-app\n" - "[cyan]URL:[/cyan] dev.app.hanzo.ai\n" - "[cyan]Variables:[/cyan] 12", - title="Current Environment", - border_style="cyan", - ) - ) - - -@env_group.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True, help="Skip confirmation") -def env_delete(name: str, force: bool): - """Delete an environment.""" - if name == "production": - console.print("[red]Error: Cannot delete production environment[/red]") - return - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete environment '{name}'?[/red]"): - return - console.print(f"[green]โœ“[/green] Environment '{name}' deleted") - - -# ============================================================================ -# Environment Variables -# ============================================================================ - - -@env_group.command(name="vars") -@click.option("--env", "-e", default="development", help="Environment name") -@click.option("--reveal", "-r", is_flag=True, help="Show secret values") -def env_vars(env: str, reveal: bool): - """List environment variables.""" - table = Table(title=f"Variables: {env}", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Value", style="white") - table.add_column("Source", style="dim") - - table.add_row( - "DATABASE_URL", "โ—โ—โ—โ—โ—โ—โ—โ—" if not reveal else "postgres://...", "secret" - ) - table.add_row("API_KEY", "โ—โ—โ—โ—โ—โ—โ—โ—" if not reveal else "sk-...", "secret") - table.add_row("LOG_LEVEL", "debug", "config") - table.add_row("NODE_ENV", "development", "config") - - console.print(table) - - -@env_group.command(name="set") -@click.argument("name") -@click.argument("value") -@click.option("--env", "-e", default="development", help="Environment name") -@click.option("--secret", "-s", is_flag=True, help="Mark as secret") -def env_set(name: str, value: str, env: str, secret: bool): - """Set an environment variable.""" - console.print(f"[green]โœ“[/green] Set {name}={value if not secret else 'โ—โ—โ—โ—โ—โ—โ—โ—'}") - console.print(f" Environment: {env}") - - -@env_group.command(name="unset") -@click.argument("name") -@click.option("--env", "-e", default="development", help="Environment name") -def env_unset(name: str, env: str): - """Unset an environment variable.""" - console.print(f"[green]โœ“[/green] Unset {name}") - console.print(f" Environment: {env}") - - -@env_group.command(name="diff") -@click.argument("env1") -@click.argument("env2") -def env_diff(env1: str, env2: str): - """Compare two environments.""" - console.print(f"[cyan]Comparing {env1} vs {env2}:[/cyan]\n") - - table = Table(box=box.SIMPLE) - table.add_column("Variable", style="cyan") - table.add_column(env1, style="green") - table.add_column(env2, style="yellow") - table.add_column("Status", style="white") - - table.add_row("LOG_LEVEL", "debug", "info", "[yellow]changed[/yellow]") - table.add_row("FEATURE_FLAG", "true", "false", "[yellow]changed[/yellow]") - table.add_row("NEW_VAR", "-", "value", "[green]added[/green]") - table.add_row("OLD_VAR", "value", "-", "[red]removed[/red]") - - console.print(table) - - -@env_group.command(name="push") -@click.option("--from", "from_env", required=True, help="Source environment") -@click.option("--to", "to_env", required=True, help="Target environment") -@click.option("--vars", "-v", multiple=True, help="Specific variables to push") -@click.option("--dry-run", is_flag=True, help="Show what would be pushed") -def env_push(from_env: str, to_env: str, vars: tuple, dry_run: bool): - """Push variables from one environment to another.""" - if dry_run: - console.print(f"[dim]Dry run - would push from {from_env} to {to_env}[/dim]") - return - console.print(f"[green]โœ“[/green] Pushed variables from {from_env} to {to_env}") diff --git a/pkg/hanzo/src/hanzo/commands/events.py b/pkg/hanzo/src/hanzo/commands/events.py deleted file mode 100644 index f58f4800a..000000000 --- a/pkg/hanzo/src/hanzo/commands/events.py +++ /dev/null @@ -1,697 +0,0 @@ -"""Hanzo Events - Eventing control plane CLI. - -Unified eventing layer for schemas, routing, replay, and DLQ. -Abstracts over streaming/pubsub/mq transports. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -EVENTS_URL = os.getenv("HANZO_EVENTS_URL", "https://events.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(EVENTS_URL, method, path, **kwargs) - - -@click.group(name="events") -def events_group(): - """Hanzo Events - Event-driven architecture. - - \b - Event Buses: - hanzo events bus create # Create event bus - hanzo events bus list # List buses - hanzo events bus delete # Delete bus - - \b - Schemas: - hanzo events schema register # Register schema - hanzo events schema get # Get schema - hanzo events schema validate # Validate event - - \b - Streams: - hanzo events stream create # Create stream - hanzo events stream list # List streams - hanzo events stream delete # Delete stream - - \b - Routes: - hanzo events route create # Create route - hanzo events route list # List routes - hanzo events route delete # Delete route - - \b - Operations: - hanzo events publish # Publish event - hanzo events tail # Tail stream - hanzo events replay # Replay events - hanzo events dlq # Dead letter queue - """ - pass - - -# ============================================================================ -# Event Bus Management -# ============================================================================ - - -@events_group.group() -def bus(): - """Manage event buses.""" - pass - - -@bus.command(name="create") -@click.argument("name") -@click.option( - "--backend", "-b", type=click.Choice(["kafka", "pubsub", "redis"]), default="kafka" -) -@click.option("--region", "-r", multiple=True, help="Regions for replication") -def bus_create(name: str, backend: str, region: tuple): - """Create an event bus.""" - payload = {"name": name, "backend": backend} - if region: - payload["regions"] = list(region) - - resp = _request("post", "/v1/buses", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Event bus '{name}' created") - console.print(f" ID: {data.get('id', '-')}") - console.print(f" Backend: {backend}") - if region: - console.print(f" Regions: {', '.join(region)}") - - -@bus.command(name="list") -def bus_list(): - """List event buses.""" - resp = _request("get", "/v1/buses") - data = check_response(resp) - buses = data.get("buses", data.get("items", [])) - - table = Table(title="Event Buses", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Backend", style="white") - table.add_column("Streams", style="green") - table.add_column("Routes", style="yellow") - table.add_column("Status", style="dim") - - for b in buses: - status = b.get("status", "unknown") - style = "green" if status == "running" else "yellow" - table.add_row( - b.get("name", ""), - b.get("backend", "-"), - str(b.get("stream_count", 0)), - str(b.get("route_count", 0)), - f"[{style}]โ— {status}[/{style}]", - ) - - console.print(table) - if not buses: - console.print( - "[dim]No event buses found. Create one with 'hanzo events bus create'[/dim]" - ) - - -@bus.command(name="describe") -@click.argument("name") -def bus_describe(name: str): - """Show event bus details.""" - resp = _request("get", f"/v1/buses/{name}") - data = check_response(resp) - - status = data.get("status", "unknown") - style = "green" if status == "running" else "yellow" - regions = data.get("regions", []) - - console.print( - Panel( - f"[cyan]Name:[/cyan] {data.get('name', name)}\n" - f"[cyan]Backend:[/cyan] {data.get('backend', '-')}\n" - f"[cyan]Status:[/cyan] [{style}]โ— {status}[/{style}]\n" - f"[cyan]Streams:[/cyan] {data.get('stream_count', 0)}\n" - f"[cyan]Routes:[/cyan] {data.get('route_count', 0)}\n" - f"[cyan]Events/day:[/cyan] {data.get('events_per_day', 0):,}\n" - f"[cyan]Regions:[/cyan] {', '.join(regions) if regions else '-'}", - title="Event Bus Details", - border_style="cyan", - ) - ) - - -@bus.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True) -def bus_delete(name: str, force: bool): - """Delete an event bus.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete event bus '{name}'?[/red]"): - return - - resp = _request("delete", f"/v1/buses/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Event bus '{name}' deleted") - - -# ============================================================================ -# Schemas -# ============================================================================ - - -@events_group.group() -def schema(): - """Manage event schemas.""" - pass - - -@schema.command(name="register") -@click.argument("name") -@click.option("--file", "-f", type=click.Path(exists=True), help="Schema file") -@click.option( - "--format", "fmt", type=click.Choice(["json", "avro", "protobuf"]), default="json" -) -@click.option("--version", "-v", help="Schema version") -def schema_register(name: str, file: str, fmt: str, version: str): - """Register an event schema.""" - payload = {"name": name, "format": fmt} - if version: - payload["version"] = version - if file: - from pathlib import Path - - payload["definition"] = Path(file).read_text() - - resp = _request("post", "/v1/schemas", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Schema '{name}' registered") - console.print(f" Format: {fmt}") - console.print(f" Version: {data.get('version', version or '1')}") - - -@schema.command(name="get") -@click.argument("name") -@click.option("--version", "-v", help="Specific version") -def schema_get(name: str, version: str): - """Get schema definition.""" - params = {} - if version: - params["version"] = version - - resp = _request("get", f"/v1/schemas/{name}", params=params) - data = check_response(resp) - - console.print( - Panel( - f"[cyan]Name:[/cyan] {data.get('name', name)}\n" - f"[cyan]Format:[/cyan] {data.get('format', '-')}\n" - f"[cyan]Version:[/cyan] {data.get('version', '-')}\n" - f"[cyan]Definition:[/cyan]\n{data.get('definition', '{}')}", - title="Schema", - border_style="cyan", - ) - ) - - -@schema.command(name="list") -def schema_list(): - """List all schemas.""" - resp = _request("get", "/v1/schemas") - data = check_response(resp) - schemas = data.get("schemas", data.get("items", [])) - - table = Table(title="Event Schemas", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Format", style="white") - table.add_column("Version", style="green") - table.add_column("Updated", style="dim") - - for s in schemas: - table.add_row( - s.get("name", ""), - s.get("format", "-"), - str(s.get("version", "-")), - str(s.get("updated_at", ""))[:19], - ) - - console.print(table) - if not schemas: - console.print("[dim]No schemas found[/dim]") - - -@schema.command(name="validate") -@click.option("--schema", "-s", required=True, help="Schema name") -@click.option("--file", "-f", type=click.Path(exists=True), help="Event file") -@click.option("--data", "-d", help="Event JSON data") -def schema_validate(schema: str, file: str, data: str): - """Validate event against schema.""" - if file: - from pathlib import Path - - event_data = Path(file).read_text() - elif data: - event_data = data - else: - raise click.ClickException("Provide --file or --data") - - payload = {"schema": schema, "event": json.loads(event_data)} - resp = _request("post", "/v1/schemas/validate", json=payload) - result = check_response(resp) - - if result.get("valid", True): - console.print(f"[green]โœ“[/green] Event is valid against schema '{schema}'") - else: - console.print(f"[red]โœ—[/red] Validation failed:") - for err in result.get("errors", []): - console.print(f" - {err}") - - -# ============================================================================ -# Streams -# ============================================================================ - - -@events_group.group() -def stream(): - """Manage event streams.""" - pass - - -@stream.command(name="create") -@click.argument("name") -@click.option("--bus", "-b", default="default", help="Event bus") -@click.option("--schema", "-s", help="Schema to enforce") -@click.option("--partitions", "-p", default=3, help="Number of partitions") -@click.option("--retention", "-r", default="7d", help="Retention period") -def stream_create(name: str, bus: str, schema: str, partitions: int, retention: str): - """Create an event stream.""" - payload = {"name": name, "partitions": partitions, "retention": retention} - if schema: - payload["schema"] = schema - - resp = _request("post", f"/v1/buses/{bus}/streams", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Stream '{name}' created") - console.print(f" Bus: {bus}") - console.print(f" Partitions: {partitions}") - console.print(f" Retention: {retention}") - if schema: - console.print(f" Schema: {schema}") - - -@stream.command(name="list") -@click.option("--bus", "-b", help="Filter by bus") -def stream_list(bus: str): - """List event streams.""" - path = f"/v1/buses/{bus}/streams" if bus else "/v1/streams" - resp = _request("get", path) - data = check_response(resp) - streams = data.get("streams", data.get("items", [])) - - table = Table(title="Event Streams", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Bus", style="white") - table.add_column("Partitions", style="green") - table.add_column("Retention", style="yellow") - table.add_column("Events/day", style="dim") - - for s in streams: - table.add_row( - s.get("name", ""), - s.get("bus", "-"), - str(s.get("partitions", 0)), - s.get("retention", "-"), - str(s.get("events_per_day", 0)), - ) - - console.print(table) - if not streams: - console.print("[dim]No streams found[/dim]") - - -@stream.command(name="describe") -@click.argument("name") -def stream_describe(name: str): - """Show stream details.""" - resp = _request("get", f"/v1/streams/{name}") - data = check_response(resp) - - console.print( - Panel( - f"[cyan]Stream:[/cyan] {data.get('name', name)}\n" - f"[cyan]Bus:[/cyan] {data.get('bus', '-')}\n" - f"[cyan]Partitions:[/cyan] {data.get('partitions', 0)}\n" - f"[cyan]Retention:[/cyan] {data.get('retention', '-')}\n" - f"[cyan]Schema:[/cyan] {data.get('schema', '-')}\n" - f"[cyan]Events/day:[/cyan] {data.get('events_per_day', 0):,}\n" - f"[cyan]Consumers:[/cyan] {data.get('consumer_count', 0)}", - title="Stream Details", - border_style="cyan", - ) - ) - - -@stream.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True) -def stream_delete(name: str, force: bool): - """Delete an event stream.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete stream '{name}'?[/red]"): - return - - resp = _request("delete", f"/v1/streams/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Stream '{name}' deleted") - - -# ============================================================================ -# Routes -# ============================================================================ - - -@events_group.group() -def route(): - """Manage event routes.""" - pass - - -@route.command(name="create") -@click.argument("name") -@click.option("--from", "from_stream", required=True, help="Source stream") -@click.option( - "--to", - required=True, - help="Target: service:, task:, queue:, webhook:", -) -@click.option("--filter", "-f", help="Filter expression") -@click.option("--transform", "-t", help="Transform expression") -def route_create(name: str, from_stream: str, to: str, filter: str, transform: str): - """Create an event route. - - \b - Examples: - hanzo events route create notify --from orders --to service:notifications - hanzo events route create etl --from users --to task:sync-db - hanzo events route create webhook --from payments --to webhook:https://... - """ - payload = {"name": name, "source": from_stream, "target": to} - if filter: - payload["filter"] = filter - if transform: - payload["transform"] = transform - - resp = _request("post", "/v1/routes", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Route '{name}' created") - console.print(f" From: {from_stream}") - console.print(f" To: {to}") - if filter: - console.print(f" Filter: {filter}") - - -@route.command(name="list") -@click.option("--stream", "-s", help="Filter by stream") -def route_list(stream: str): - """List event routes.""" - params = {} - if stream: - params["stream"] = stream - - resp = _request("get", "/v1/routes", params=params) - data = check_response(resp) - routes = data.get("routes", data.get("items", [])) - - table = Table(title="Event Routes", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("From", style="white") - table.add_column("To", style="yellow") - table.add_column("Filter", style="dim") - table.add_column("Status", style="green") - - for r in routes: - status = r.get("status", "active") - style = "green" if status == "active" else "yellow" - table.add_row( - r.get("name", ""), - r.get("source", "-"), - r.get("target", "-"), - r.get("filter", "-") or "-", - f"[{style}]{status}[/{style}]", - ) - - console.print(table) - if not routes: - console.print("[dim]No routes found[/dim]") - - -@route.command(name="describe") -@click.argument("name") -def route_describe(name: str): - """Show route details.""" - resp = _request("get", f"/v1/routes/{name}") - data = check_response(resp) - - console.print( - Panel( - f"[cyan]Name:[/cyan] {data.get('name', name)}\n" - f"[cyan]Source:[/cyan] {data.get('source', '-')}\n" - f"[cyan]Target:[/cyan] {data.get('target', '-')}\n" - f"[cyan]Filter:[/cyan] {data.get('filter', '-')}\n" - f"[cyan]Transform:[/cyan] {data.get('transform', '-')}\n" - f"[cyan]Status:[/cyan] {data.get('status', '-')}\n" - f"[cyan]Events routed:[/cyan] {data.get('events_routed', 0):,}", - title="Route Details", - border_style="cyan", - ) - ) - - -@route.command(name="delete") -@click.argument("name") -def route_delete(name: str): - """Delete an event route.""" - resp = _request("delete", f"/v1/routes/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Route '{name}' deleted") - - -@route.command(name="pause") -@click.argument("name") -def route_pause(name: str): - """Pause an event route.""" - resp = _request("post", f"/v1/routes/{name}/pause") - check_response(resp) - console.print(f"[green]โœ“[/green] Route '{name}' paused") - - -@route.command(name="resume") -@click.argument("name") -def route_resume(name: str): - """Resume an event route.""" - resp = _request("post", f"/v1/routes/{name}/resume") - check_response(resp) - console.print(f"[green]โœ“[/green] Route '{name}' resumed") - - -# ============================================================================ -# Operations -# ============================================================================ - - -@events_group.command(name="publish") -@click.option("--stream", "-s", required=True, help="Target stream") -@click.option("--data", "-d", help="Event JSON data") -@click.option("--file", "-f", type=click.Path(exists=True), help="Event file") -@click.option("--key", "-k", help="Partition key") -def events_publish(stream: str, data: str, file: str, key: str): - """Publish an event to a stream.""" - if file: - from pathlib import Path - - event_data = json.loads(Path(file).read_text()) - elif data: - event_data = json.loads(data) - else: - raise click.ClickException("Provide --data or --file") - - payload = {"data": event_data} - if key: - payload["key"] = key - - resp = _request("post", f"/v1/streams/{stream}/publish", json=payload) - result = check_response(resp) - - console.print(f"[green]โœ“[/green] Event published to '{stream}'") - console.print(f" Event ID: {result.get('event_id', result.get('id', '-'))}") - if key: - console.print(f" Key: {key}") - - -@events_group.command(name="tail") -@click.argument("stream") -@click.option( - "--from", "from_pos", type=click.Choice(["latest", "earliest"]), default="latest" -) -@click.option("--filter", "-f", help="Filter expression") -@click.option("--limit", "-n", type=int, help="Max events") -def events_tail(stream: str, from_pos: str, filter: str, limit: int): - """Tail events from a stream.""" - params = {"position": from_pos} - if filter: - params["filter"] = filter - if limit: - params["limit"] = limit - - resp = _request("get", f"/v1/streams/{stream}/tail", params=params) - data = check_response(resp) - events = data.get("events", []) - - for evt in events: - console.print( - f"[dim]{evt.get('timestamp', '')}[/dim] " - f"[cyan]{evt.get('id', '')}[/cyan] " - f"{json.dumps(evt.get('data', {}), default=str)}" - ) - - if not events: - console.print(f"[dim]No events in '{stream}' (position: {from_pos})[/dim]") - elif not limit: - console.print("[dim]Press Ctrl+C to stop[/dim]") - - -@events_group.command(name="replay") -@click.option("--stream", "-s", required=True, help="Stream to replay") -@click.option( - "--from", "from_pos", required=True, help="Start: earliest, timestamp, offset" -) -@click.option("--to", "to_pos", help="End: latest, timestamp, offset") -@click.option("--target", "-t", help="Target route or consumer") -@click.option("--dry-run", is_flag=True, help="Show what would be replayed") -def events_replay(stream: str, from_pos: str, to_pos: str, target: str, dry_run: bool): - """Replay events from a stream.""" - payload = {"stream": stream, "from": from_pos} - if to_pos: - payload["to"] = to_pos - if target: - payload["target"] = target - if dry_run: - payload["dry_run"] = True - - resp = _request("post", "/v1/replay", json=payload) - data = check_response(resp) - - if dry_run: - console.print( - f"[dim]Dry run: {data.get('event_count', 0)} events would be replayed[/dim]" - ) - else: - console.print(f"[green]โœ“[/green] Replay complete") - console.print(f" Events replayed: {data.get('replayed', 0):,}") - if data.get("duration"): - console.print(f" Duration: {data['duration']}") - - -# ============================================================================ -# Dead Letter Queue -# ============================================================================ - - -@events_group.group() -def dlq(): - """Manage dead letter queue.""" - pass - - -@dlq.command(name="list") -@click.option("--stream", "-s", help="Filter by stream") -@click.option("--limit", "-n", default=20, help="Max events") -def dlq_list(stream: str, limit: int): - """List dead letter events.""" - params = {"limit": limit} - if stream: - params["stream"] = stream - - resp = _request("get", "/v1/dlq", params=params) - data = check_response(resp) - events = data.get("events", data.get("items", [])) - - table = Table(title="Dead Letter Queue", box=box.ROUNDED) - table.add_column("Event ID", style="cyan") - table.add_column("Stream", style="white") - table.add_column("Error", style="red") - table.add_column("Attempts", style="yellow") - table.add_column("Failed At", style="dim") - - for e in events: - table.add_row( - str(e.get("event_id", e.get("id", "")))[:24], - e.get("stream", "-"), - str(e.get("error", "-"))[:40], - str(e.get("attempts", 0)), - str(e.get("failed_at", ""))[:19], - ) - - console.print(table) - if not events: - console.print("[dim]No dead letter events[/dim]") - - -@dlq.command(name="retry") -@click.option("--stream", "-s", help="Stream to retry") -@click.option("--event-id", "-e", help="Specific event ID") -@click.option("--all", "retry_all", is_flag=True, help="Retry all DLQ events") -def dlq_retry(stream: str, event_id: str, retry_all: bool): - """Retry dead letter events.""" - payload = {} - if retry_all: - payload["all"] = True - elif event_id: - payload["event_id"] = event_id - elif stream: - payload["stream"] = stream - else: - raise click.ClickException("Provide --event-id, --stream, or --all") - - resp = _request("post", "/v1/dlq/retry", json=payload) - data = check_response(resp) - console.print(f"[green]โœ“[/green] Retried {data.get('retried', 0)} event(s)") - - -@dlq.command(name="purge") -@click.option("--stream", "-s", help="Stream to purge") -@click.option("--force", "-f", is_flag=True) -def dlq_purge(stream: str, force: bool): - """Purge dead letter events.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask("[red]Purge DLQ events?[/red]"): - return - - payload = {} - if stream: - payload["stream"] = stream - - resp = _request("post", "/v1/dlq/purge", json=payload) - data = check_response(resp) - console.print(f"[green]โœ“[/green] Purged {data.get('purged', 0)} DLQ event(s)") diff --git a/pkg/hanzo/src/hanzo/commands/flow.py b/pkg/hanzo/src/hanzo/commands/flow.py deleted file mode 100644 index b1a9e2a05..000000000 --- a/pkg/hanzo/src/hanzo/commands/flow.py +++ /dev/null @@ -1,606 +0,0 @@ -"""Hanzo Flow - Visual LLM workflow builder CLI. - -Build and deploy LLM applications with a visual interface (Langflow-compatible). -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -FLOW_URL = os.getenv("HANZO_FLOW_URL", "https://flow.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(FLOW_URL, method, path, **kwargs) - - -@click.group(name="flow") -def flow_group(): - """Hanzo Flow - Visual LLM workflow builder (Langflow-compatible). - - \b - Flows: - hanzo flow create # Create LLM flow - hanzo flow list # List flows - hanzo flow run # Run a flow - hanzo flow export # Export flow definition - - \b - Components: - hanzo flow components # List available components - hanzo flow custom # Manage custom components - - \b - Development: - hanzo flow dev # Start visual editor - hanzo flow deploy # Deploy to production - - \b - API: - hanzo flow api # Manage flow APIs - hanzo flow playground # Interactive playground - """ - pass - - -# ============================================================================ -# Flow Management -# ============================================================================ - - -@flow_group.command(name="create") -@click.argument("name") -@click.option("--template", "-t", help="Start from template") -@click.option("--description", "-d", help="Flow description") -def flow_create(name: str, template: str, description: str): - """Create a new LLM flow. - - \b - Examples: - hanzo flow create chatbot - hanzo flow create rag-assistant --template rag - hanzo flow create summarizer --template chain - """ - body = {"name": name} - if template: - body["template"] = template - if description: - body["description"] = description - - resp = _request("post", "/v1/flows", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Flow '{name}' created") - console.print(f" ID: {data.get('id', '-')}") - if template: - console.print(f" Template: {template}") - console.print() - console.print("Next steps:") - console.print(" 1. [cyan]hanzo flow dev[/cyan] - Open visual editor") - console.print(" 2. Add components to your flow") - console.print(f" 3. [cyan]hanzo flow deploy {name}[/cyan] - Deploy to production") - - -@flow_group.command(name="list") -@click.option( - "--status", type=click.Choice(["deployed", "draft", "all"]), default="all" -) -def flow_list(status: str): - """List LLM flows.""" - params = {} - if status != "all": - params["status"] = status - - resp = _request("get", "/v1/flows", params=params) - data = check_response(resp) - items = data.get("flows", data.get("items", [])) - - table = Table(title="LLM Flows", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Components", style="green") - table.add_column("Status", style="yellow") - table.add_column("Endpoint", style="white") - table.add_column("Updated", style="dim") - - for f in items: - f_status = f.get("status", "draft") - style = "green" if f_status == "deployed" else "yellow" - table.add_row( - f.get("name", ""), - str(f.get("component_count", 0)), - f"[{style}]{f_status}[/{style}]", - f.get("endpoint", "-"), - str(f.get("updated_at", ""))[:19], - ) - - console.print(table) - if not items: - console.print("[dim]No flows found. Create one with 'hanzo flow create'[/dim]") - - -@flow_group.command(name="describe") -@click.argument("name") -def flow_describe(name: str): - """Show flow details.""" - resp = _request("get", f"/v1/flows/{name}") - data = check_response(resp) - - status = data.get("status", "draft") - status_style = "green" if status == "deployed" else "yellow" - - console.print( - Panel( - f"[cyan]Name:[/cyan] {data.get('name', name)}\n" - f"[cyan]Status:[/cyan] [{status_style}]{status}[/{status_style}]\n" - f"[cyan]Components:[/cyan] {data.get('component_count', 0)}\n" - f"[cyan]Endpoint:[/cyan] {data.get('endpoint', '-')}\n" - f"[cyan]Calls (24h):[/cyan] {data.get('calls_24h', 0):,}\n" - f"[cyan]Avg Latency:[/cyan] {data.get('avg_latency_ms', 0)}ms\n" - f"[cyan]Created:[/cyan] {str(data.get('created_at', ''))[:19]}", - title="Flow Details", - border_style="cyan", - ) - ) - - -@flow_group.command(name="run") -@click.argument("name") -@click.option("--input", "-i", "input_data", required=True, help="Input JSON or text") -@click.option("--stream", "-s", is_flag=True, help="Stream output") -@click.option("--verbose", "-v", is_flag=True, help="Show component outputs") -def flow_run(name: str, input_data: str, stream: bool, verbose: bool): - """Run a flow locally. - - \b - Examples: - hanzo flow run chatbot -i "What is machine learning?" - hanzo flow run rag -i '{"query": "How do I reset my password?"}' - hanzo flow run summarizer -i @document.txt --stream - """ - body = {"input": input_data} - if stream: - body["stream"] = True - if verbose: - body["verbose"] = True - - console.print(f"[cyan]Running flow '{name}'...[/cyan]") - resp = _request("post", f"/v1/flows/{name}/run", json=body) - data = check_response(resp) - - if verbose and data.get("steps"): - for step in data["steps"]: - console.print( - f" [dim]โ†’ {step.get('name', 'step')}: {step.get('status', 'done')}[/dim]" - ) - - console.print() - console.print("[green]Output:[/green]") - output = data.get("output", data.get("result", "")) - console.print(str(output)) - - -@flow_group.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True) -def flow_delete(name: str, force: bool): - """Delete a flow.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete flow '{name}'?[/red]"): - return - - resp = _request("delete", f"/v1/flows/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Flow '{name}' deleted") - - -@flow_group.command(name="export") -@click.argument("name") -@click.option("--output", "-o", help="Output file") -@click.option("--format", "fmt", type=click.Choice(["json", "yaml"]), default="json") -def flow_export(name: str, output: str, fmt: str): - """Export flow definition.""" - resp = _request("get", f"/v1/flows/{name}/export", params={"format": fmt}) - data = check_response(resp) - - out_file = output or f"{name}.{fmt}" - with open(out_file, "w") as f: - if fmt == "json": - json.dump(data, f, indent=2, default=str) - else: - f.write(str(data.get("yaml", data.get("definition", "")))) - - console.print(f"[green]โœ“[/green] Flow exported to '{out_file}'") - - -@flow_group.command(name="import") -@click.argument("file") -@click.option("--name", "-n", help="Override flow name") -def flow_import(file: str, name: str): - """Import flow from file.""" - with open(file) as f: - definition = json.load(f) - - body = {"definition": definition} - if name: - body["name"] = name - - console.print(f"[cyan]Importing flow from '{file}'...[/cyan]") - resp = _request("post", "/v1/flows/import", json=body) - data = check_response(resp) - console.print( - f"[green]โœ“[/green] Flow '{data.get('name', name or 'imported')}' imported" - ) - - -# ============================================================================ -# Components -# ============================================================================ - - -@flow_group.group() -def components(): - """Manage flow components.""" - pass - - -@components.command(name="list") -@click.option("--category", "-c", help="Filter by category") -@click.option("--search", "-s", help="Search components") -def components_list(category: str, search: str): - """List available components.""" - params = {} - if category: - params["category"] = category - if search: - params["search"] = search - - resp = _request("get", "/v1/components", params=params) - data = check_response(resp) - items = data.get("components", data.get("items", [])) - - table = Table(title="Flow Components", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Category", style="white") - table.add_column("Description", style="dim") - - for c in items: - table.add_row( - c.get("name", ""), - c.get("category", "-"), - c.get("description", "-"), - ) - - console.print(table) - if not items: - console.print("[dim]No components found[/dim]") - - -@components.command(name="describe") -@click.argument("name") -def components_describe(name: str): - """Show component details.""" - resp = _request("get", f"/v1/components/{name}") - data = check_response(resp) - - inputs = ", ".join(data.get("inputs", [])) or "-" - outputs = ", ".join(data.get("outputs", [])) or "-" - config = ", ".join(data.get("config", [])) or "-" - - console.print( - Panel( - f"[cyan]Name:[/cyan] {data.get('name', name)}\n" - f"[cyan]Category:[/cyan] {data.get('category', '-')}\n" - f"[cyan]Inputs:[/cyan] {inputs}\n" - f"[cyan]Outputs:[/cyan] {outputs}\n" - f"[cyan]Config:[/cyan] {config}\n" - f"[cyan]Description:[/cyan] {data.get('description', '-')}", - title="Component Details", - border_style="cyan", - ) - ) - - -# ============================================================================ -# Custom Components -# ============================================================================ - - -@flow_group.group() -def custom(): - """Manage custom components.""" - pass - - -@custom.command(name="create") -@click.argument("name") -@click.option( - "--template", "-t", type=click.Choice(["tool", "chain", "agent"]), default="tool" -) -def custom_create(name: str, template: str): - """Create a custom component.""" - body = {"name": name, "template": template} - - resp = _request("post", "/v1/components/custom", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Custom component '{name}' created") - console.print(f" Template: {template}") - console.print(f" File: {data.get('file', f'components/{name}.py')}") - - -@custom.command(name="list") -def custom_list(): - """List custom components.""" - resp = _request("get", "/v1/components/custom") - data = check_response(resp) - items = data.get("components", data.get("items", [])) - - table = Table(title="Custom Components", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Type", style="white") - table.add_column("File", style="dim") - - for c in items: - table.add_row( - c.get("name", ""), - c.get("type", "-"), - c.get("file", "-"), - ) - - console.print(table) - if not items: - console.print("[dim]No custom components found[/dim]") - - -@custom.command(name="publish") -@click.argument("name") -def custom_publish(name: str): - """Publish component to registry.""" - console.print(f"[cyan]Publishing component '{name}'...[/cyan]") - resp = _request("post", f"/v1/components/custom/{name}/publish") - check_response(resp) - console.print(f"[green]โœ“[/green] Component published") - - -# ============================================================================ -# Development -# ============================================================================ - - -@flow_group.command(name="dev") -@click.option("--port", "-p", default=7860, help="Port to run on") -@click.option("--flow", "-f", help="Open specific flow") -def flow_dev(port: int, flow: str): - """Start visual flow editor.""" - console.print(f"[cyan]Starting Hanzo Flow editor on port {port}...[/cyan]") - console.print() - console.print(f" [cyan]Editor:[/cyan] http://localhost:{port}") - if flow: - console.print(f" [cyan]Flow:[/cyan] {flow}") - console.print() - console.print("Press Ctrl+C to stop") - - -@flow_group.command(name="deploy") -@click.argument("name") -@click.option("--env", "-e", default="production", help="Environment") -def flow_deploy(name: str, env: str): - """Deploy flow to production.""" - console.print(f"[cyan]Deploying flow '{name}' to {env}...[/cyan]") - resp = _request("post", f"/v1/flows/{name}/deploy", json={"environment": env}) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Flow deployed") - console.print(f" Endpoint: {data.get('endpoint', f'{FLOW_URL}/{name}')}") - if data.get("api_key"): - console.print(f" API Key: {data['api_key'][:12]}***") - - -@flow_group.command(name="undeploy") -@click.argument("name") -def flow_undeploy(name: str): - """Undeploy a flow.""" - resp = _request("post", f"/v1/flows/{name}/undeploy") - check_response(resp) - console.print(f"[green]โœ“[/green] Flow '{name}' undeployed") - - -# ============================================================================ -# API Management -# ============================================================================ - - -@flow_group.group() -def api(): - """Manage flow APIs.""" - pass - - -@api.command(name="keys") -@click.argument("flow_name") -def api_keys(flow_name: str): - """List API keys for a flow.""" - resp = _request("get", f"/v1/flows/{flow_name}/keys") - data = check_response(resp) - items = data.get("keys", data.get("items", [])) - - table = Table(title=f"API Keys for '{flow_name}'", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Key", style="white") - table.add_column("Created", style="dim") - table.add_column("Last Used", style="dim") - - for k in items: - table.add_row( - k.get("name", ""), - k.get("key_prefix", "****"), - str(k.get("created_at", ""))[:19], - str(k.get("last_used_at", "-"))[:19], - ) - - console.print(table) - if not items: - console.print("[dim]No API keys found[/dim]") - - -@api.command(name="create-key") -@click.argument("flow_name") -@click.option("--name", "-n", default="default", help="Key name") -def api_create_key(flow_name: str, name: str): - """Create API key for a flow.""" - resp = _request("post", f"/v1/flows/{flow_name}/keys", json={"name": name}) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] API key created for '{flow_name}'") - console.print(f" Name: {name}") - console.print(f" Key: {data.get('key', '-')}") - console.print("[yellow]Save this key - it won't be shown again[/yellow]") - - -@api.command(name="revoke-key") -@click.argument("flow_name") -@click.argument("key_name") -def api_revoke_key(flow_name: str, key_name: str): - """Revoke an API key.""" - resp = _request("delete", f"/v1/flows/{flow_name}/keys/{key_name}") - check_response(resp) - console.print(f"[green]โœ“[/green] API key '{key_name}' revoked") - - -@api.command(name="test") -@click.argument("flow_name") -@click.option("--input", "-i", "input_data", required=True, help="Test input") -def api_test(flow_name: str, input_data: str): - """Test flow API endpoint.""" - console.print(f"[cyan]Testing API for '{flow_name}'...[/cyan]") - resp = _request("post", f"/v1/flows/{flow_name}/test", json={"input": input_data}) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] API responded successfully") - console.print(f" Status: {data.get('status_code', 200)}") - console.print(f" Latency: {data.get('latency_ms', '-')}ms") - if data.get("output"): - console.print(f" Output: {json.dumps(data['output'], default=str)[:200]}") - - -# ============================================================================ -# Playground -# ============================================================================ - - -@flow_group.command(name="playground") -@click.argument("name") -@click.option("--port", "-p", default=7861, help="Port") -def flow_playground(name: str, port: int): - """Open interactive playground for a flow.""" - console.print(f"[cyan]Opening playground for '{name}'...[/cyan]") - console.print(f" URL: http://localhost:{port}/playground/{name}") - - -# ============================================================================ -# Templates -# ============================================================================ - - -@flow_group.group() -def templates(): - """Pre-built flow templates.""" - pass - - -@templates.command(name="list") -def templates_list(): - """List available templates.""" - resp = _request("get", "/v1/templates") - data = check_response(resp) - items = data.get("templates", data.get("items", [])) - - table = Table(title="Flow Templates", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Description", style="white") - table.add_column("Components", style="dim") - - for t in items: - table.add_row( - t.get("name", ""), - t.get("description", "-"), - t.get("components", "-"), - ) - - console.print(table) - if not items: - console.print("[dim]No templates found[/dim]") - - -@templates.command(name="use") -@click.argument("template") -@click.option("--name", "-n", required=True, help="Flow name") -def templates_use(template: str, name: str): - """Create flow from template.""" - console.print(f"[cyan]Creating flow '{name}' from template '{template}'...[/cyan]") - resp = _request( - "post", "/v1/flows/from-template", json={"template": template, "name": name} - ) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Flow created") - console.print(f" ID: {data.get('id', '-')}") - console.print() - console.print("Next steps:") - console.print(f" 1. [cyan]hanzo flow dev -f {name}[/cyan] - Edit in visual editor") - console.print(f" 2. [cyan]hanzo flow deploy {name}[/cyan] - Deploy to production") - - -# ============================================================================ -# Versions & History -# ============================================================================ - - -@flow_group.command(name="versions") -@click.argument("name") -def flow_versions(name: str): - """List flow versions.""" - resp = _request("get", f"/v1/flows/{name}/versions") - data = check_response(resp) - items = data.get("versions", data.get("items", [])) - - table = Table(title=f"Versions of '{name}'", box=box.ROUNDED) - table.add_column("Version", style="cyan") - table.add_column("Status", style="green") - table.add_column("Created", style="dim") - table.add_column("Note", style="dim") - - for v in items: - v_status = v.get("status", "inactive") - style = "green" if v_status == "active" else "dim" - table.add_row( - str(v.get("version", "")), - f"[{style}]{v_status}[/{style}]", - str(v.get("created_at", ""))[:19], - v.get("note", "-"), - ) - - console.print(table) - if not items: - console.print("[dim]No versions found[/dim]") - - -@flow_group.command(name="rollback") -@click.argument("name") -@click.option("--version", "-v", required=True, help="Target version") -def flow_rollback(name: str, version: str): - """Rollback to a previous version.""" - console.print(f"[cyan]Rolling back '{name}' to version {version}...[/cyan]") - resp = _request("post", f"/v1/flows/{name}/rollback", json={"version": version}) - check_response(resp) - console.print(f"[green]โœ“[/green] Rolled back successfully") diff --git a/pkg/hanzo/src/hanzo/commands/fn.py b/pkg/hanzo/src/hanzo/commands/fn.py deleted file mode 100644 index e6cb8c890..000000000 --- a/pkg/hanzo/src/hanzo/commands/fn.py +++ /dev/null @@ -1,690 +0,0 @@ -"""Hanzo Functions - Serverless functions CLI. - -Deploy and manage serverless functions with automatic scaling. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -FN_URL = os.getenv("HANZO_FN_URL", "https://fn.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(FN_URL, method, path, **kwargs) - - -@click.group(name="function") -def fn_group(): - """Hanzo Functions - Serverless compute. - - \b - Functions: - hanzo function create # Create function - hanzo function list # List functions - hanzo function deploy # Deploy function - hanzo function delete # Delete function - - \b - Triggers: - hanzo function triggers add # Add trigger (http, event, cron) - hanzo function triggers list # List triggers - hanzo function triggers rm # Remove trigger - - \b - Logs & Monitoring: - hanzo function logs # View function logs - hanzo function invoke # Invoke function - hanzo function stats # Function metrics - - \b - Configuration: - hanzo function env # Manage environment variables - hanzo function secrets # Bind secrets - - \b - Alias: hanzo fn - """ - pass - - -# ============================================================================ -# Function Management -# ============================================================================ - - -@fn_group.command(name="create") -@click.argument("name") -@click.option( - "--runtime", - "-r", - type=click.Choice( - ["python3.12", "python3.11", "node20", "node18", "go1.22", "rust", "deno"] - ), - default="python3.12", -) -@click.option("--memory", "-m", default="256", help="Memory in MB (128-4096)") -@click.option("--timeout", "-t", default=30, help="Timeout in seconds (1-900)") -@click.option("--region", help="Deployment region") -@click.option("--from", "source", help="Source: directory, git URL, or template") -def fn_create( - name: str, runtime: str, memory: str, timeout: int, region: str, source: str -): - """Create a new function. - - \b - Examples: - hanzo fn create my-api --runtime python3.12 - hanzo fn create processor --runtime node20 --memory 512 - hanzo fn create handler --from ./src/handler - hanzo fn create api --from github.com/user/repo - """ - body = {"name": name, "runtime": runtime, "memory": int(memory), "timeout": timeout} - if region: - body["region"] = region - if source: - body["source"] = source - - resp = _request("post", "/v1/functions", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Function '{name}' created") - console.print(f" ID: {data.get('id', '-')}") - console.print(f" Runtime: {runtime}") - console.print(f" Memory: {memory}MB") - console.print(f" Timeout: {timeout}s") - if region: - console.print(f" Region: {region}") - if source: - console.print(f" Source: {source}") - - -@fn_group.command(name="list") -@click.option("--region", "-r", help="Filter by region") -@click.option("--runtime", help="Filter by runtime") -def fn_list(region: str, runtime: str): - """List functions.""" - params = {} - if region: - params["region"] = region - if runtime: - params["runtime"] = runtime - - resp = _request("get", "/v1/functions", params=params) - data = check_response(resp) - items = data.get("functions", data.get("items", [])) - - table = Table(title="Functions", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Runtime", style="white") - table.add_column("Memory", style="green") - table.add_column("Triggers", style="yellow") - table.add_column("Last Deploy", style="dim") - table.add_column("Status", style="dim") - - for f in items: - f_status = f.get("status", "inactive") - style = "green" if f_status == "active" else "yellow" - table.add_row( - f.get("name", ""), - f.get("runtime", "-"), - f"{f.get('memory', 256)}MB", - str(f.get("trigger_count", 0)), - str(f.get("last_deployed_at", ""))[:19], - f"[{style}]{f_status}[/{style}]", - ) - - console.print(table) - if not items: - console.print( - "[dim]No functions found. Create one with 'hanzo fn create'[/dim]" - ) - - -@fn_group.command(name="describe") -@click.argument("name") -def fn_describe(name: str): - """Show function details.""" - resp = _request("get", f"/v1/functions/{name}") - data = check_response(resp) - - status = data.get("status", "inactive") - status_style = "green" if status == "active" else "yellow" - - console.print( - Panel( - f"[cyan]Name:[/cyan] {data.get('name', name)}\n" - f"[cyan]Runtime:[/cyan] {data.get('runtime', '-')}\n" - f"[cyan]Memory:[/cyan] {data.get('memory', 256)} MB\n" - f"[cyan]Timeout:[/cyan] {data.get('timeout', 30)}s\n" - f"[cyan]Status:[/cyan] [{status_style}]{status}[/{status_style}]\n" - f"[cyan]Triggers:[/cyan] {data.get('trigger_count', 0)}\n" - f"[cyan]Invocations (24h):[/cyan] {data.get('invocations_24h', 0):,}\n" - f"[cyan]Avg Duration:[/cyan] {data.get('avg_duration_ms', 0)}ms\n" - f"[cyan]Endpoint:[/cyan] {data.get('endpoint', f'{FN_URL}/{name}')}", - title="Function Details", - border_style="cyan", - ) - ) - - -@fn_group.command(name="deploy") -@click.argument("name") -@click.option("--from", "source", help="Source directory or file") -@click.option("--entry", "-e", help="Entry point (e.g., main.handler)") -@click.option("--build", is_flag=True, help="Build before deploying") -@click.option("--force", "-f", is_flag=True, help="Force deploy even if no changes") -def fn_deploy(name: str, source: str, entry: str, build: bool, force: bool): - """Deploy a function. - - \b - Examples: - hanzo fn deploy my-api # Deploy from current dir - hanzo fn deploy my-api --from ./src # Deploy from specific dir - hanzo fn deploy my-api --entry main.handler # Specify entry point - hanzo fn deploy my-api --build # Build and deploy - """ - body = {} - if source: - body["source"] = source - if entry: - body["entry_point"] = entry - if build: - body["build"] = True - if force: - body["force"] = True - - console.print(f"[cyan]Deploying '{name}'...[/cyan]") - resp = _request("post", f"/v1/functions/{name}/deploy", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Function '{name}' deployed") - console.print(f" Version: {data.get('version', '-')}") - console.print(f" Endpoint: {data.get('endpoint', f'{FN_URL}/{name}')}") - - -@fn_group.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True) -def fn_delete(name: str, force: bool): - """Delete a function.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete function '{name}'?[/red]"): - return - - resp = _request("delete", f"/v1/functions/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Function '{name}' deleted") - - -# ============================================================================ -# Triggers -# ============================================================================ - - -@fn_group.group() -def triggers(): - """Manage function triggers.""" - pass - - -@triggers.command(name="add") -@click.argument("function") -@click.option( - "--type", - "trigger_type", - type=click.Choice(["http", "event", "cron", "queue", "storage"]), - required=True, -) -@click.option("--path", "-p", help="HTTP path (for http triggers)") -@click.option("--method", "-m", multiple=True, help="HTTP methods (for http triggers)") -@click.option("--event", "-e", help="Event type (for event triggers)") -@click.option("--bus", "-b", help="Event bus (for event triggers)") -@click.option("--schedule", "-s", help="Cron expression (for cron triggers)") -@click.option("--queue", "-q", help="Queue name (for queue triggers)") -@click.option("--bucket", help="Storage bucket (for storage triggers)") -def triggers_add( - function: str, - trigger_type: str, - path: str, - method: tuple, - event: str, - bus: str, - schedule: str, - queue: str, - bucket: str, -): - """Add a trigger to a function. - - \b - Examples: - hanzo fn triggers add my-api --type http --path /users --method GET POST - hanzo fn triggers add processor --type event --event user.created --bus main - hanzo fn triggers add cleanup --type cron --schedule "0 0 * * *" - hanzo fn triggers add worker --type queue --queue tasks - hanzo fn triggers add upload-handler --type storage --bucket uploads - """ - body = {"type": trigger_type} - if trigger_type == "http": - body["path"] = path or "/" - body["methods"] = list(method) if method else ["GET"] - elif trigger_type == "event": - body["event"] = event - body["bus"] = bus or "default" - elif trigger_type == "cron": - body["schedule"] = schedule - elif trigger_type == "queue": - body["queue"] = queue - elif trigger_type == "storage": - body["bucket"] = bucket - - resp = _request("post", f"/v1/functions/{function}/triggers", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Trigger added to '{function}'") - console.print(f" Type: {trigger_type}") - console.print(f" ID: {data.get('id', '-')}") - if trigger_type == "http": - console.print(f" Path: {body.get('path', '/')}") - console.print(f" Methods: {', '.join(body.get('methods', ['GET']))}") - elif trigger_type == "event": - console.print(f" Event: {event}") - console.print(f" Bus: {body.get('bus', 'default')}") - elif trigger_type == "cron": - console.print(f" Schedule: {schedule}") - elif trigger_type == "queue": - console.print(f" Queue: {queue}") - elif trigger_type == "storage": - console.print(f" Bucket: {bucket}") - - -@triggers.command(name="list") -@click.argument("function") -def triggers_list(function: str): - """List triggers for a function.""" - resp = _request("get", f"/v1/functions/{function}/triggers") - data = check_response(resp) - items = data.get("triggers", data.get("items", [])) - - table = Table(title=f"Triggers for '{function}'", box=box.ROUNDED) - table.add_column("ID", style="cyan") - table.add_column("Type", style="white") - table.add_column("Source", style="green") - table.add_column("Status", style="dim") - - for t in items: - source = t.get( - "path", - t.get("event", t.get("schedule", t.get("queue", t.get("bucket", "-")))), - ) - t_status = t.get("status", "active") - style = "green" if t_status == "active" else "yellow" - table.add_row( - str(t.get("id", ""))[:16], - t.get("type", "-"), - source, - f"[{style}]{t_status}[/{style}]", - ) - - console.print(table) - if not items: - console.print("[dim]No triggers found[/dim]") - - -@triggers.command(name="rm") -@click.argument("function") -@click.argument("trigger_id") -def triggers_rm(function: str, trigger_id: str): - """Remove a trigger.""" - resp = _request("delete", f"/v1/functions/{function}/triggers/{trigger_id}") - check_response(resp) - console.print(f"[green]โœ“[/green] Trigger '{trigger_id}' removed from '{function}'") - - -# ============================================================================ -# Invocation & Logs -# ============================================================================ - - -@fn_group.command(name="invoke") -@click.argument("name") -@click.option("--data", "-d", help="JSON payload") -@click.option("--file", "-f", "payload_file", help="File containing payload") -@click.option("--async", "async_invoke", is_flag=True, help="Async invocation") -@click.option("--tail", is_flag=True, help="Tail logs after invocation") -def fn_invoke(name: str, data: str, payload_file: str, async_invoke: bool, tail: bool): - """Invoke a function. - - \b - Examples: - hanzo fn invoke my-api - hanzo fn invoke my-api -d '{"user": "test"}' - hanzo fn invoke my-api -f payload.json - hanzo fn invoke my-api --async - """ - body = {} - if data: - body["data"] = json.loads(data) - elif payload_file: - with open(payload_file) as f: - body["data"] = json.load(f) - - if async_invoke: - body["async"] = True - - console.print(f"[cyan]Invoking '{name}'...[/cyan]") - resp = _request("post", f"/v1/functions/{name}/invoke", json=body) - result = check_response(resp) - - if async_invoke: - console.print("[green]โœ“[/green] Function invoked asynchronously") - console.print(f" Request ID: {result.get('request_id', '-')}") - else: - console.print("[green]โœ“[/green] Function executed") - console.print(f" Duration: {result.get('duration_ms', '-')}ms") - console.print(f" Status: {result.get('status_code', '-')}") - if result.get("body"): - console.print(f" Response: {json.dumps(result['body'], default=str)}") - - if tail and result.get("request_id"): - log_resp = _request( - "get", - f"/v1/functions/{name}/logs", - params={"request_id": result["request_id"]}, - ) - log_data = check_response(log_resp) - for line in log_data.get("logs", []): - console.print(str(line)) - - -@fn_group.command(name="logs") -@click.argument("name") -@click.option("--follow", "-f", is_flag=True, help="Follow logs") -@click.option("--since", "-s", default="1h", help="Time range (e.g., 1h, 24h, 7d)") -@click.option("--filter", "log_filter", help="Filter expression") -@click.option("--limit", "-n", default=100, help="Max log entries") -def fn_logs(name: str, follow: bool, since: str, log_filter: str, limit: int): - """View function logs. - - \b - Examples: - hanzo fn logs my-api - hanzo fn logs my-api -f # Follow/tail logs - hanzo fn logs my-api --since 24h # Last 24 hours - hanzo fn logs my-api --filter error # Filter for errors - """ - params = {"since": since, "limit": limit} - if follow: - params["follow"] = "true" - if log_filter: - params["filter"] = log_filter - - resp = _request("get", f"/v1/functions/{name}/logs", params=params) - data = check_response(resp) - lines = data.get("logs", data.get("lines", [])) - - if follow: - console.print(f"[cyan]Tailing logs for '{name}'...[/cyan]") - console.print("[dim]Press Ctrl+C to stop[/dim]") - else: - console.print(f"[cyan]Logs for '{name}' (last {since}):[/cyan]") - - for line in lines: - if isinstance(line, dict): - ts = str(line.get("timestamp", ""))[:19] - level = line.get("level", "info") - msg = line.get("message", "") - style = ( - "red" if level == "error" else "yellow" if level == "warn" else "dim" - ) - console.print(f"[dim]{ts}[/dim] [{style}]{level}[/{style}] {msg}") - else: - console.print(str(line)) - - if not lines: - console.print("[dim]No log entries found[/dim]") - - -@fn_group.command(name="stats") -@click.argument("name") -@click.option("--range", "-r", "time_range", default="24h", help="Time range") -def fn_stats(name: str, time_range: str): - """Show function statistics.""" - resp = _request("get", f"/v1/functions/{name}/stats", params={"range": time_range}) - data = check_response(resp) - - console.print( - Panel( - f"[cyan]Function:[/cyan] {name}\n" - f"[cyan]Time Range:[/cyan] {time_range}\n\n" - f"[cyan]Invocations:[/cyan] {data.get('invocations', 0):,}\n" - f"[cyan]Errors:[/cyan] {data.get('errors', 0)} ({data.get('error_rate', 0)}%)\n" - f"[cyan]Avg Duration:[/cyan] {data.get('avg_duration_ms', 0)}ms\n" - f"[cyan]P95 Duration:[/cyan] {data.get('p95_duration_ms', 0)}ms\n" - f"[cyan]Cold Starts:[/cyan] {data.get('cold_starts', 0)}\n" - f"[cyan]Memory Peak:[/cyan] {data.get('memory_peak_mb', 0)} MB", - title="Function Statistics", - border_style="cyan", - ) - ) - - -# ============================================================================ -# Environment & Secrets -# ============================================================================ - - -@fn_group.group() -def env(): - """Manage function environment variables.""" - pass - - -@env.command(name="set") -@click.argument("function") -@click.argument("key") -@click.argument("value") -def env_set(function: str, key: str, value: str): - """Set an environment variable.""" - resp = _request("put", f"/v1/functions/{function}/env/{key}", json={"value": value}) - check_response(resp) - console.print(f"[green]โœ“[/green] Set {key} for '{function}'") - - -@env.command(name="get") -@click.argument("function") -@click.argument("key", required=False) -def env_get(function: str, key: str): - """Get environment variables.""" - if key: - resp = _request("get", f"/v1/functions/{function}/env/{key}") - data = check_response(resp) - console.print(f"[cyan]{key}=[/cyan]{data.get('value', '(not set)')}") - else: - resp = _request("get", f"/v1/functions/{function}/env") - data = check_response(resp) - env_vars = data.get("env", data.get("variables", {})) - console.print(f"[cyan]Environment for '{function}':[/cyan]") - if env_vars: - for k, v in env_vars.items(): - console.print(f" {k}={v}") - else: - console.print("[dim]No environment variables set[/dim]") - - -@env.command(name="rm") -@click.argument("function") -@click.argument("key") -def env_rm(function: str, key: str): - """Remove an environment variable.""" - resp = _request("delete", f"/v1/functions/{function}/env/{key}") - check_response(resp) - console.print(f"[green]โœ“[/green] Removed {key} from '{function}'") - - -@fn_group.group() -def secrets(): - """Manage function secrets binding.""" - pass - - -@secrets.command(name="bind") -@click.argument("function") -@click.argument("secret") -@click.option("--as", "env_name", help="Environment variable name") -def secrets_bind(function: str, secret: str, env_name: str): - """Bind a secret to a function. - - \b - Examples: - hanzo fn secrets bind my-api db-password - hanzo fn secrets bind my-api api-key --as API_KEY - """ - body = {"secret": secret} - if env_name: - body["env_name"] = env_name - - resp = _request("post", f"/v1/functions/{function}/secrets", json=body) - check_response(resp) - console.print(f"[green]โœ“[/green] Bound secret '{secret}' to '{function}'") - if env_name: - console.print(f" As: {env_name}") - - -@secrets.command(name="unbind") -@click.argument("function") -@click.argument("secret") -def secrets_unbind(function: str, secret: str): - """Unbind a secret from a function.""" - resp = _request("delete", f"/v1/functions/{function}/secrets/{secret}") - check_response(resp) - console.print(f"[green]โœ“[/green] Unbound secret '{secret}' from '{function}'") - - -@secrets.command(name="list") -@click.argument("function") -def secrets_list(function: str): - """List secrets bound to a function.""" - resp = _request("get", f"/v1/functions/{function}/secrets") - data = check_response(resp) - items = data.get("secrets", data.get("items", [])) - - table = Table(title=f"Secrets for '{function}'", box=box.ROUNDED) - table.add_column("Secret", style="cyan") - table.add_column("Env Var", style="white") - table.add_column("Version", style="dim") - - for s in items: - table.add_row( - s.get("secret", ""), - s.get("env_name", "-"), - str(s.get("version", "-")), - ) - - console.print(table) - if not items: - console.print("[dim]No secrets bound[/dim]") - - -# ============================================================================ -# Versions & Rollback -# ============================================================================ - - -@fn_group.command(name="versions") -@click.argument("name") -def fn_versions(name: str): - """List function versions.""" - resp = _request("get", f"/v1/functions/{name}/versions") - data = check_response(resp) - items = data.get("versions", data.get("items", [])) - - table = Table(title=f"Versions of '{name}'", box=box.ROUNDED) - table.add_column("Version", style="cyan") - table.add_column("Deployed", style="white") - table.add_column("Traffic", style="green") - table.add_column("Status", style="dim") - - for v in items: - v_status = v.get("status", "inactive") - style = "green" if v_status == "active" else "dim" - table.add_row( - str(v.get("version", "")), - str(v.get("deployed_at", ""))[:19], - f"{v.get('traffic', 0)}%", - f"[{style}]{v_status}[/{style}]", - ) - - console.print(table) - if not items: - console.print("[dim]No versions found[/dim]") - - -@fn_group.command(name="rollback") -@click.argument("name") -@click.option("--version", "-v", help="Target version") -def fn_rollback(name: str, version: str): - """Rollback to a previous version.""" - body = {} - if version: - body["version"] = version - - console.print(f"[cyan]Rolling back '{name}'...[/cyan]") - resp = _request("post", f"/v1/functions/{name}/rollback", json=body) - data = check_response(resp) - console.print( - f"[green]โœ“[/green] Rolled back to {data.get('version', version or 'previous')}" - ) - - -# ============================================================================ -# Traffic Splitting -# ============================================================================ - - -@fn_group.command(name="traffic") -@click.argument("name") -@click.option( - "--version", "-v", multiple=True, help="Version:weight pairs (e.g., v1:90 v2:10)" -) -def fn_traffic(name: str, version: tuple): - """Configure traffic splitting between versions. - - \b - Examples: - hanzo fn traffic my-api -v v1:90 -v v2:10 # 90/10 split - hanzo fn traffic my-api -v v2:100 # 100% to v2 - """ - if version: - splits = {} - for v in version: - ver, weight = v.split(":") - splits[ver] = int(weight) - - resp = _request("put", f"/v1/functions/{name}/traffic", json={"splits": splits}) - check_response(resp) - - console.print(f"[green]โœ“[/green] Traffic configured for '{name}'") - for v in version: - console.print(f" {v}") - else: - resp = _request("get", f"/v1/functions/{name}/traffic") - data = check_response(resp) - splits = data.get("splits", {}) - - console.print(f"[cyan]Traffic configuration for '{name}':[/cyan]") - if splits: - for ver, weight in splits.items(): - console.print(f" {ver}: {weight}%") - else: - console.print("[dim]Single version active (100%)[/dim]") diff --git a/pkg/hanzo/src/hanzo/commands/git_provider.py b/pkg/hanzo/src/hanzo/commands/git_provider.py deleted file mode 100644 index fcbafdd25..000000000 --- a/pkg/hanzo/src/hanzo/commands/git_provider.py +++ /dev/null @@ -1,344 +0,0 @@ -"""Git provider integration for Hanzo CLI. - -Connect git providers (GitHub, GitLab, Bitbucket) and link repos to containers. -""" - -import click -from rich import box -from rich.table import Table - -from ..utils.output import console - - -@click.group(name="git") -def git_group(): - """Manage git provider connections and repo links. - - \b - Providers: - hanzo git connect github # Connect GitHub account - hanzo git providers # List connected providers - hanzo git disconnect ID # Remove a provider - - \b - Repos: - hanzo git repos # List repos from connected provider - hanzo git branches REPO # List branches - hanzo git link REPO # Link repo to container - """ - pass - - -def _get_client(): - from ..utils.api_client import PaaSClient - - try: - return PaaSClient(timeout=30) - except SystemExit: - return None - - -# ============================================================================ -# Provider management -# ============================================================================ - - -@git_group.command(name="connect") -@click.argument("provider", type=click.Choice(["github", "gitlab", "bitbucket"])) -@click.option("--token", "-t", help="Personal access token (alternative to OAuth)") -def git_connect(provider, token): - """Connect a git provider. - - \b - Examples: - hanzo git connect github # OAuth flow - hanzo git connect github --token ghp_xxx # PAT - hanzo git connect gitlab --token glpat_xxx - """ - from ..utils.api_client import git_url - - client = _get_client() - if not client: - return - - if token: - payload = { - "provider": provider, - "accessToken": token, - } - console.print(f"[cyan]Connecting {provider} with token...[/cyan]") - result = client.post(git_url(), payload) - if result is None: - return - - pid = result.get("_id") or result.get("id", "") - console.print(f"[green]โœ“[/green] {provider} connected") - if pid: - console.print(f" Provider ID: {pid}") - else: - # OAuth flow - redirect to PaaS OAuth endpoint - from ..utils.api_client import PLATFORM_API_URL - - oauth_url = f"{PLATFORM_API_URL}/v1/user/git/connect/{provider}" - console.print(f"[cyan]Opening browser for {provider} OAuth...[/cyan]") - console.print(f"\n {oauth_url}\n") - - try: - import webbrowser - - webbrowser.open(oauth_url) - console.print("[dim]Complete the OAuth flow in your browser.[/dim]") - console.print("[dim]Then run 'hanzo git providers' to verify.[/dim]") - except Exception: - console.print( - "[yellow]Could not open browser. Visit the URL above manually.[/yellow]" - ) - - -@git_group.command(name="providers") -def git_providers(): - """List connected git providers.""" - from ..utils.api_client import git_url - - client = _get_client() - if not client: - return - - data = client.get(git_url()) - if data is None: - return - - providers = ( - data if isinstance(data, list) else data.get("providers", data.get("data", [])) - ) - - if not providers: - console.print("[dim]No git providers connected.[/dim]") - console.print("[dim]Run 'hanzo git connect github' to get started.[/dim]") - return - - table = Table(title="Git Providers", box=box.ROUNDED) - table.add_column("ID", style="cyan") - table.add_column("Provider", style="white") - table.add_column("Username", style="green") - table.add_column("Status", style="dim") - - for p in providers: - pid = p.get("_id") or p.get("id", "") - prov = p.get("provider", p.get("gitProviderId", "")) - user = p.get("username", p.get("providerUserId", "")) - status = p.get("status", "connected") - table.add_row(str(pid)[:12], prov, user, status) - - console.print(table) - - -@git_group.command(name="disconnect") -@click.argument("provider_id") -@click.option("--force", "-f", is_flag=True, help="Skip confirmation") -def git_disconnect(provider_id, force): - """Disconnect a git provider.""" - from ..utils.api_client import git_url - - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Disconnect git provider '{provider_id}'?[/red]"): - return - - client = _get_client() - if not client: - return - - result = client.delete(git_url(provider_id)) - if result is None: - return - - console.print(f"[green]โœ“[/green] Git provider disconnected") - - -# ============================================================================ -# Repos & Branches -# ============================================================================ - - -@git_group.command(name="repos") -@click.option("--provider", "-p", help="Provider ID (uses first connected if omitted)") -def git_repos(provider): - """List repos from connected git provider.""" - from ..utils.api_client import git_url - - client = _get_client() - if not client: - return - - # If no provider specified, find the first one - if not provider: - data = client.get(git_url()) - if data is None: - return - providers = ( - data - if isinstance(data, list) - else data.get("providers", data.get("data", [])) - ) - if not providers: - console.print( - "[yellow]No git providers connected. Run 'hanzo git connect github' first.[/yellow]" - ) - return - provider = str(providers[0].get("_id") or providers[0].get("id", "")) - - data = client.get(f"{git_url(provider)}/repo") - if data is None: - return - - repos = data if isinstance(data, list) else data.get("repos", data.get("data", [])) - - if not repos: - console.print("[dim]No repos found.[/dim]") - return - - table = Table(title="Repositories", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Full Name", style="white") - table.add_column("Default Branch", style="green") - table.add_column("Private", style="dim") - - for r in repos: - rname = r.get("name", "") - rfull = r.get("fullName", r.get("full_name", "")) - rbranch = r.get("defaultBranch", r.get("default_branch", "main")) - rprivate = "Yes" if r.get("private", False) else "No" - table.add_row(rname, rfull, rbranch, rprivate) - - console.print(table) - - -@git_group.command(name="branches") -@click.argument("repo") -@click.option("--provider", "-p", help="Provider ID") -def git_branches(repo, provider): - """List branches for a repo.""" - from ..utils.api_client import git_url - - client = _get_client() - if not client: - return - - if not provider: - data = client.get(git_url()) - if data is None: - return - providers = ( - data - if isinstance(data, list) - else data.get("providers", data.get("data", [])) - ) - if not providers: - console.print("[yellow]No git providers connected.[/yellow]") - return - provider = str(providers[0].get("_id") or providers[0].get("id", "")) - - data = client.get(f"{git_url(provider)}/repo/branch", params={"repo": repo}) - if data is None: - return - - branches = ( - data if isinstance(data, list) else data.get("branches", data.get("data", [])) - ) - - if not branches: - console.print(f"[dim]No branches found for '{repo}'.[/dim]") - return - - table = Table(title=f"Branches for {repo}", box=box.ROUNDED) - table.add_column("Branch", style="cyan") - table.add_column("Default", style="green") - - for b in branches: - bname = b.get("name", b) if isinstance(b, dict) else str(b) - is_default = b.get("default", False) if isinstance(b, dict) else False - table.add_row(bname, "Yes" if is_default else "") - - console.print(table) - - -@git_group.command(name="link") -@click.argument("repo") -@click.option("--container", "-c", required=True, help="Container name to link to") -@click.option("--branch", "-b", default="main", help="Branch to deploy from") -@click.option("--provider", "-p", help="Provider ID") -def git_link(repo, container, branch, provider): - """Link a git repo to a container (sets up webhook for auto-deploy). - - \b - Examples: - hanzo git link org/repo --container my-app - hanzo git link org/repo --container api --branch develop - """ - from ..utils.api_client import git_url, container_url, require_context - - client = _get_client() - if not client: - return - - try: - ctx = require_context() - except SystemExit: - return - - # Resolve provider if not given - if not provider: - data = client.get(git_url()) - if data is None: - return - providers = ( - data - if isinstance(data, list) - else data.get("providers", data.get("data", [])) - ) - if not providers: - console.print("[yellow]No git providers connected.[/yellow]") - return - provider = str(providers[0].get("_id") or providers[0].get("id", "")) - - # Find the container - base_url = container_url(ctx["org_id"], ctx["project_id"], ctx["env_id"]) - data = client.get(base_url) - if data is None: - return - - containers = ( - data if isinstance(data, list) else data.get("containers", data.get("data", [])) - ) - cid = None - for c in containers: - cname = c.get("name", "") - if cname == container: - cid = c.get("_id") or c.get("iid") or c.get("id", "") - break - - if not cid: - console.print(f"[yellow]Container '{container}' not found.[/yellow]") - return - - # Update container to use repo source - payload = { - "repoOrRegistry": "repo", - "repo": { - "url": repo, - "branch": branch, - "gitProviderId": provider, - "connected": True, - }, - } - - result = client.put(f"{base_url}/{cid}", payload) - if result is None: - return - - console.print(f"[green]โœ“[/green] Linked '{repo}' -> '{container}'") - console.print(f" Branch: {branch}") - console.print(f" Auto-deploy: enabled") - console.print(f"[dim]Push to '{branch}' to trigger a build.[/dim]") diff --git a/pkg/hanzo/src/hanzo/commands/growth.py b/pkg/hanzo/src/hanzo/commands/growth.py deleted file mode 100644 index fd9542f22..000000000 --- a/pkg/hanzo/src/hanzo/commands/growth.py +++ /dev/null @@ -1,693 +0,0 @@ -"""Hanzo Growth - Analytics, experiments, and engagement CLI. - -Product analytics, feature flags, A/B testing, lifecycle messaging. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -SERVICE_URL = os.getenv("HANZO_GROWTH_URL", "https://growth.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(SERVICE_URL, method, path, **kwargs) - - -@click.group(name="growth") -def growth_group(): - """Hanzo Growth - Analytics and experimentation. - - \b - Product Analytics (Insights): - hanzo growth events list # List events - hanzo growth events track # Track an event - hanzo growth funnels list # List funnels - - \b - Web Analytics: - hanzo growth web list # List tracked sites - hanzo growth web stats # View traffic stats - - \b - Experiments: - hanzo growth flags list # List feature flags - hanzo growth flags create # Create feature flag - hanzo growth tests list # List A/B tests - - \b - Engagement: - hanzo growth campaigns list # List campaigns - hanzo growth campaigns send # Send campaign - """ - pass - - -# ============================================================================ -# Events (Product Analytics) -# ============================================================================ - - -@growth_group.group() -def events(): - """Manage event tracking.""" - pass - - -@events.command(name="list") -@click.option("--limit", "-n", default=50, help="Number of events") -@click.option("--event", "-e", help="Filter by event name") -@click.option("--user", "-u", help="Filter by user ID") -def events_list(limit: int, event: str, user: str): - """List recent events.""" - params: dict = {"limit": limit} - if event: - params["event"] = event - if user: - params["user"] = user - resp = _request("get", "/v1/events", params=params) - data = check_response(resp) - - table = Table(title="Recent Events", box=box.ROUNDED) - table.add_column("Event", style="cyan") - table.add_column("User", style="white") - table.add_column("Properties", style="dim") - table.add_column("Time", style="dim") - - for e in data.get("events", []): - props = e.get("properties", {}) - props_str = ( - ", ".join(f"{k}={v}" for k, v in props.items()) - if isinstance(props, dict) - else str(props) - ) - table.add_row( - e.get("event", ""), - e.get("user_id", ""), - props_str[:60], - e.get("timestamp", ""), - ) - - console.print(table) - - -@events.command(name="track") -@click.argument("event_name") -@click.option("--user", "-u", help="User ID") -@click.option("--props", "-p", help="JSON properties") -def events_track(event_name: str, user: str, props: str): - """Track an event.""" - payload: dict = {"event": event_name} - if user: - payload["user_id"] = user - if props: - payload["properties"] = json.loads(props) - resp = _request("post", "/v1/events", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Event '{event_name}' tracked") - - -@events.command(name="schema") -def events_schema(): - """Show event schema.""" - resp = _request("get", "/v1/events/schema") - data = check_response(resp) - - table = Table(title="Event Schema", box=box.ROUNDED) - table.add_column("Event", style="cyan") - table.add_column("Properties", style="white") - table.add_column("Count", style="dim") - - for s in data.get("schemas", []): - props = ", ".join(s.get("properties", [])) - table.add_row( - s.get("event", ""), - props[:60], - str(s.get("count", 0)), - ) - - console.print(table) - - -# ============================================================================ -# Funnels -# ============================================================================ - - -@growth_group.group() -def funnels(): - """Manage conversion funnels.""" - pass - - -@funnels.command(name="list") -def funnels_list(): - """List all funnels.""" - resp = _request("get", "/v1/funnels") - data = check_response(resp) - - table = Table(title="Funnels", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Steps", style="white") - table.add_column("Conversion", style="green") - table.add_column("Users", style="dim") - - for f in data.get("funnels", []): - conv = f.get("conversion_rate") - conv_str = f"{conv:.1f}%" if conv is not None else "-" - table.add_row( - f.get("name", ""), - str(f.get("step_count", 0)), - conv_str, - str(f.get("user_count", 0)), - ) - - console.print(table) - - -@funnels.command(name="show") -@click.argument("funnel_name") -@click.option("--period", "-p", default="30d", help="Time period") -def funnels_show(funnel_name: str, period: str): - """Show funnel details.""" - resp = _request("get", f"/v1/funnels/{funnel_name}", params={"period": period}) - data = check_response(resp) - - info = ( - f"[cyan]Funnel:[/cyan] {data.get('name', funnel_name)}\n" - f"[cyan]Steps:[/cyan] {data.get('step_count', 0)}\n" - f"[cyan]Conversion:[/cyan] {data.get('conversion_rate', 0):.1f}%\n" - f"[cyan]Period:[/cyan] {period}" - ) - console.print(Panel(info, title="Funnel Details", border_style="cyan")) - - steps = data.get("steps", []) - if steps: - table = Table(title="Funnel Steps", box=box.ROUNDED) - table.add_column("Step", style="cyan") - table.add_column("Event", style="white") - table.add_column("Users", style="dim") - table.add_column("Conversion", style="green") - table.add_column("Drop-off", style="red") - - for i, step in enumerate(steps, 1): - table.add_row( - str(i), - step.get("event", ""), - str(step.get("users", 0)), - f"{step.get('conversion', 0):.1f}%", - f"{step.get('dropoff', 0):.1f}%", - ) - - console.print(table) - - -@funnels.command(name="create") -@click.option("--name", "-n", prompt=True, help="Funnel name") -@click.option("--steps", "-s", required=True, help="Comma-separated event names") -def funnels_create(name: str, steps: str): - """Create a funnel.""" - step_list = [s.strip() for s in steps.split(",")] - resp = _request("post", "/v1/funnels", json={"name": name, "steps": step_list}) - check_response(resp) - console.print( - f"[green]โœ“[/green] Funnel '{name}' created with {len(step_list)} steps" - ) - - -@funnels.command(name="delete") -@click.argument("funnel_name") -def funnels_delete(funnel_name: str): - """Delete a funnel.""" - resp = _request("delete", f"/v1/funnels/{funnel_name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Funnel '{funnel_name}' deleted") - - -# ============================================================================ -# Web Analytics -# ============================================================================ - - -@growth_group.group() -def web(): - """Manage web analytics.""" - pass - - -@web.command(name="list") -def web_list(): - """List tracked websites.""" - resp = _request("get", "/v1/web/sites") - data = check_response(resp) - - table = Table(title="Tracked Websites", box=box.ROUNDED) - table.add_column("Domain", style="cyan") - table.add_column("Visitors", style="white") - table.add_column("Pageviews", style="white") - table.add_column("Status", style="green") - - for site in data.get("sites", []): - st = site.get("status", "active") - st_style = "green" if st == "active" else "yellow" - table.add_row( - site.get("domain", ""), - str(site.get("visitors", 0)), - str(site.get("pageviews", 0)), - f"[{st_style}]{st}[/{st_style}]", - ) - - console.print(table) - - -@web.command(name="add") -@click.argument("domain") -def web_add(domain: str): - """Add a website to track.""" - resp = _request("post", "/v1/web/sites", json={"domain": domain}) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Website '{domain}' added") - console.print() - - script_id = data.get("site_id", domain) - console.print("[dim]Add this script to your website:[/dim]") - console.print( - Panel( - f'', - border_style="dim", - ) - ) - - -@web.command(name="stats") -@click.argument("domain") -@click.option("--period", "-p", default="7d", help="Time period (e.g., 7d, 30d)") -def web_stats(domain: str, period: str): - """View website statistics.""" - resp = _request("get", f"/v1/web/sites/{domain}/stats", params={"period": period}) - data = check_response(resp) - - info = ( - f"[cyan]Domain:[/cyan] {data.get('domain', domain)}\n" - f"[cyan]Period:[/cyan] {period}\n" - f"[cyan]Visitors:[/cyan] {data.get('visitors', 0):,}\n" - f"[cyan]Pageviews:[/cyan] {data.get('pageviews', 0):,}\n" - f"[cyan]Bounce Rate:[/cyan] {data.get('bounce_rate', 0):.0f}%\n" - f"[cyan]Avg Duration:[/cyan] {data.get('avg_duration', 'N/A')}" - ) - console.print(Panel(info, title="Website Stats", border_style="cyan")) - - pages = data.get("top_pages", []) - if pages: - table = Table(title="Top Pages", box=box.ROUNDED) - table.add_column("Page", style="cyan") - table.add_column("Views", style="white") - table.add_column("Visitors", style="dim") - - for p in pages[:10]: - table.add_row( - p.get("path", ""), str(p.get("views", 0)), str(p.get("visitors", 0)) - ) - - console.print(table) - - -@web.command(name="remove") -@click.argument("domain") -def web_remove(domain: str): - """Remove a tracked website.""" - resp = _request("delete", f"/v1/web/sites/{domain}") - check_response(resp) - console.print(f"[green]โœ“[/green] Website '{domain}' removed") - - -# ============================================================================ -# Feature Flags -# ============================================================================ - - -@growth_group.group() -def flags(): - """Manage feature flags.""" - pass - - -@flags.command(name="list") -def flags_list(): - """List all feature flags.""" - resp = _request("get", "/v1/flags") - data = check_response(resp) - - table = Table(title="Feature Flags", box=box.ROUNDED) - table.add_column("Key", style="cyan") - table.add_column("Status", style="green") - table.add_column("Rollout", style="white") - table.add_column("Updated", style="dim") - - for f in data.get("flags", []): - enabled = f.get("enabled", False) - status_str = "[green]enabled[/green]" if enabled else "[dim]disabled[/dim]" - table.add_row( - f.get("key", ""), - status_str, - f"{f.get('rollout', 0)}%", - f.get("updated_at", ""), - ) - - console.print(table) - - -@flags.command(name="create") -@click.option("--key", "-k", prompt=True, help="Flag key") -@click.option("--description", "-d", help="Description") -@click.option("--rollout", "-r", default=0, help="Rollout percentage") -def flags_create(key: str, description: str, rollout: int): - """Create a feature flag.""" - payload: dict = {"key": key, "rollout": rollout} - if description: - payload["description"] = description - resp = _request("post", "/v1/flags", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Feature flag '{key}' created") - - -@flags.command(name="enable") -@click.argument("flag_key") -@click.option("--rollout", "-r", default=100, help="Rollout percentage") -def flags_enable(flag_key: str, rollout: int): - """Enable a feature flag.""" - resp = _request( - "patch", f"/v1/flags/{flag_key}", json={"enabled": True, "rollout": rollout} - ) - check_response(resp) - console.print(f"[green]โœ“[/green] Flag '{flag_key}' enabled at {rollout}%") - - -@flags.command(name="disable") -@click.argument("flag_key") -def flags_disable(flag_key: str): - """Disable a feature flag.""" - resp = _request("patch", f"/v1/flags/{flag_key}", json={"enabled": False}) - check_response(resp) - console.print(f"[green]โœ“[/green] Flag '{flag_key}' disabled") - - -@flags.command(name="delete") -@click.argument("flag_key") -def flags_delete(flag_key: str): - """Delete a feature flag.""" - resp = _request("delete", f"/v1/flags/{flag_key}") - check_response(resp) - console.print(f"[green]โœ“[/green] Flag '{flag_key}' deleted") - - -# ============================================================================ -# A/B Tests -# ============================================================================ - - -@growth_group.group() -def tests(): - """Manage A/B tests.""" - pass - - -@tests.command(name="list") -@click.option( - "--status", - type=click.Choice(["running", "completed", "draft", "all"]), - default="all", -) -def tests_list(status: str): - """List A/B tests.""" - params: dict = {} - if status != "all": - params["status"] = status - resp = _request("get", "/v1/tests", params=params) - data = check_response(resp) - - table = Table(title="A/B Tests", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Status", style="green") - table.add_column("Variants", style="white") - table.add_column("Traffic", style="dim") - table.add_column("Winner", style="yellow") - - for t in data.get("tests", []): - st = t.get("status", "draft") - st_style = {"running": "green", "completed": "cyan", "draft": "dim"}.get( - st, "white" - ) - winner = t.get("winner", "-") - table.add_row( - t.get("name", ""), - f"[{st_style}]{st}[/{st_style}]", - str(t.get("variant_count", 0)), - f"{t.get('traffic_percent', 0)}%", - winner, - ) - - console.print(table) - - -@tests.command(name="create") -@click.option("--name", "-n", prompt=True, help="Test name") -@click.option( - "--variants", "-v", default="control,treatment", help="Comma-separated variants" -) -@click.option("--metric", "-m", required=True, help="Primary metric") -@click.option("--traffic", "-t", default=100, help="Traffic percentage") -def tests_create(name: str, variants: str, metric: str, traffic: int): - """Create an A/B test.""" - variant_list = [v.strip() for v in variants.split(",")] - resp = _request( - "post", - "/v1/tests", - json={ - "name": name, - "variants": variant_list, - "primary_metric": metric, - "traffic_percent": traffic, - }, - ) - check_response(resp) - console.print( - f"[green]โœ“[/green] A/B test '{name}' created with {len(variant_list)} variants" - ) - - -@tests.command(name="start") -@click.argument("test_name") -def tests_start(test_name: str): - """Start an A/B test.""" - resp = _request("post", f"/v1/tests/{test_name}/start") - check_response(resp) - console.print(f"[green]โœ“[/green] A/B test '{test_name}' started") - - -@tests.command(name="stop") -@click.argument("test_name") -def tests_stop(test_name: str): - """Stop an A/B test.""" - resp = _request("post", f"/v1/tests/{test_name}/stop") - check_response(resp) - console.print(f"[green]โœ“[/green] A/B test '{test_name}' stopped") - - -@tests.command(name="results") -@click.argument("test_name") -@click.option( - "--format", "-f", "fmt", type=click.Choice(["table", "json"]), default="table" -) -def tests_results(test_name: str, fmt: str): - """View A/B test results.""" - resp = _request("get", f"/v1/tests/{test_name}/results") - data = check_response(resp) - - if fmt == "json": - console.print(json.dumps(data, indent=2)) - return - - info = ( - f"[cyan]Test:[/cyan] {data.get('name', test_name)}\n" - f"[cyan]Status:[/cyan] {data.get('status', 'unknown')}\n" - f"[cyan]Participants:[/cyan] {data.get('participant_count', 0):,}\n" - f"[cyan]Confidence:[/cyan] {data.get('confidence', 0):.0f}%" - ) - console.print(Panel(info, title="Test Results", border_style="cyan")) - - variants = data.get("variants", []) - if variants: - table = Table(title="Variant Results", box=box.ROUNDED) - table.add_column("Variant", style="cyan") - table.add_column("Users", style="white") - table.add_column("Conversions", style="green") - table.add_column("Rate", style="yellow") - table.add_column("Lift", style="dim") - - for v in variants: - is_winner = v.get("is_winner", False) - name_str = ( - f"[bold]{v.get('name', '')}[/bold] *" - if is_winner - else v.get("name", "") - ) - table.add_row( - name_str, - str(v.get("users", 0)), - str(v.get("conversions", 0)), - f"{v.get('conversion_rate', 0):.1f}%", - f"{v.get('lift', 0):+.1f}%" if v.get("lift") else "-", - ) - - console.print(table) - - -@tests.command(name="delete") -@click.argument("test_name") -def tests_delete(test_name: str): - """Delete an A/B test.""" - resp = _request("delete", f"/v1/tests/{test_name}") - check_response(resp) - console.print(f"[green]โœ“[/green] A/B test '{test_name}' deleted") - - -# ============================================================================ -# Campaigns (Engagement) -# ============================================================================ - - -@growth_group.group() -def campaigns(): - """Manage engagement campaigns.""" - pass - - -@campaigns.command(name="list") -@click.option( - "--status", - type=click.Choice(["active", "draft", "completed", "all"]), - default="all", -) -def campaigns_list(status: str): - """List campaigns.""" - params: dict = {} - if status != "all": - params["status"] = status - resp = _request("get", "/v1/campaigns", params=params) - data = check_response(resp) - - table = Table(title="Campaigns", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Type", style="white") - table.add_column("Status", style="green") - table.add_column("Sent", style="dim") - table.add_column("Opens", style="dim") - - for c in data.get("campaigns", []): - st = c.get("status", "draft") - st_style = {"active": "green", "completed": "cyan", "draft": "dim"}.get( - st, "white" - ) - table.add_row( - c.get("name", ""), - c.get("type", ""), - f"[{st_style}]{st}[/{st_style}]", - str(c.get("sent_count", 0)), - str(c.get("open_count", 0)), - ) - - console.print(table) - - -@campaigns.command(name="create") -@click.option("--name", "-n", prompt=True, help="Campaign name") -@click.option( - "--type", - "-t", - "campaign_type", - type=click.Choice(["email", "push", "sms", "in-app"]), - default="email", -) -@click.option("--segment", "-s", help="Target segment") -@click.option("--subject", help="Email subject") -@click.option("--body", "-b", help="Message body") -def campaigns_create( - name: str, campaign_type: str, segment: str, subject: str, body: str -): - """Create a campaign.""" - payload: dict = {"name": name, "type": campaign_type} - if segment: - payload["segment"] = segment - if subject: - payload["subject"] = subject - if body: - payload["body"] = body - resp = _request("post", "/v1/campaigns", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Campaign '{name}' created") - - -@campaigns.command(name="send") -@click.argument("campaign_name") -@click.option("--schedule", help="Schedule time (ISO format)") -def campaigns_send(campaign_name: str, schedule: str): - """Send or schedule a campaign.""" - payload: dict = {} - if schedule: - payload["scheduled_at"] = schedule - resp = _request("post", f"/v1/campaigns/{campaign_name}/send", json=payload) - check_response(resp) - if schedule: - console.print( - f"[green]โœ“[/green] Campaign '{campaign_name}' scheduled for {schedule}" - ) - else: - console.print(f"[green]โœ“[/green] Campaign '{campaign_name}' sent") - - -@campaigns.command(name="stats") -@click.argument("campaign_name") -def campaigns_stats(campaign_name: str): - """View campaign statistics.""" - resp = _request("get", f"/v1/campaigns/{campaign_name}/stats") - data = check_response(resp) - - sent = data.get("sent", 0) - delivered = data.get("delivered", 0) - opens = data.get("opens", 0) - clicks = data.get("clicks", 0) - conversions = data.get("conversions", 0) - - delivery_rate = (delivered / sent * 100) if sent > 0 else 0 - open_rate = (opens / delivered * 100) if delivered > 0 else 0 - click_rate = (clicks / delivered * 100) if delivered > 0 else 0 - conv_rate = (conversions / delivered * 100) if delivered > 0 else 0 - - info = ( - f"[cyan]Campaign:[/cyan] {data.get('name', campaign_name)}\n" - f"[cyan]Sent:[/cyan] {sent:,}\n" - f"[cyan]Delivered:[/cyan] {delivered:,} ({delivery_rate:.1f}%)\n" - f"[cyan]Opens:[/cyan] {opens:,} ({open_rate:.1f}%)\n" - f"[cyan]Clicks:[/cyan] {clicks:,} ({click_rate:.1f}%)\n" - f"[cyan]Conversions:[/cyan] {conversions:,} ({conv_rate:.1f}%)" - ) - console.print(Panel(info, title="Campaign Stats", border_style="cyan")) - - -@campaigns.command(name="delete") -@click.argument("campaign_name") -def campaigns_delete(campaign_name: str): - """Delete a campaign.""" - resp = _request("delete", f"/v1/campaigns/{campaign_name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Campaign '{campaign_name}' deleted") diff --git a/pkg/hanzo/src/hanzo/commands/iam.py b/pkg/hanzo/src/hanzo/commands/iam.py deleted file mode 100644 index c706b2b0f..000000000 --- a/pkg/hanzo/src/hanzo/commands/iam.py +++ /dev/null @@ -1,1387 +0,0 @@ -"""Hanzo IAM - Identity and Access Management CLI. - -Real API calls to Casdoor-based IAM at hanzo.id. -Manages users, organizations, providers, roles, and applications. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from ..utils.output import console - -# ============================================================================ -# IAM Client Helper -# ============================================================================ - - -def _get_iam_url() -> str: - return os.getenv("IAM_URL", "https://hanzo.id") - - -def _get_iam_credentials() -> tuple[str, str]: - """Get client_id and client_secret from env or defaults.""" - client_id = os.getenv("IAM_CLIENT_ID", "") - client_secret = os.getenv("IAM_CLIENT_SECRET", "") - if not client_id or not client_secret: - # Try loading from auth file - from pathlib import Path - - auth_file = Path.home() / ".hanzo" / "auth.json" - if auth_file.exists(): - try: - auth = json.loads(auth_file.read_text()) - client_id = client_id or auth.get("iam_client_id", "") - client_secret = client_secret or auth.get("iam_client_secret", "") - except Exception: - pass - return client_id, client_secret - - -def _iam_request( - method: str, - path: str, - params: dict | None = None, - json_body: dict | None = None, - auth_params: bool = True, -) -> dict: - """Make authenticated request to IAM API.""" - url = _get_iam_url().rstrip("/") + path - client_id, client_secret = _get_iam_credentials() - - if not client_id or not client_secret: - console.print("[red]Error:[/red] IAM credentials not configured") - console.print("Set IAM_CLIENT_ID and IAM_CLIENT_SECRET environment variables") - console.print("Or run: hanzo iam configure") - raise SystemExit(1) - - if params is None: - params = {} - - if auth_params: - params["clientId"] = client_id - params["clientSecret"] = client_secret - - try: - with httpx.Client(timeout=30.0) as http: - if method == "GET": - resp = http.get(url, params=params) - else: - resp = http.post(url, params=params, json=json_body) - resp.raise_for_status() - return resp.json() - except httpx.ConnectError: - console.print(f"[red]Error:[/red] Cannot connect to IAM at {_get_iam_url()}") - raise SystemExit(1) - except httpx.HTTPStatusError as e: - console.print(f"[red]Error:[/red] IAM returned {e.response.status_code}") - try: - detail = e.response.json() - console.print(f" {detail.get('msg', detail)}") - except Exception: - console.print(f" {e.response.text[:200]}") - raise SystemExit(1) - - -def _check_response(data: dict, action: str) -> None: - """Check API response and print error if failed.""" - if data.get("status") == "error": - console.print(f"[red]Error:[/red] {data.get('msg', f'Failed to {action}')}") - raise SystemExit(1) - - -# ============================================================================ -# Main IAM Group -# ============================================================================ - - -@click.group(name="iam") -def iam_group(): - """Hanzo IAM - Identity and Access Management. - - \b - Configure: - hanzo iam configure # Set IAM credentials - hanzo iam status # Check IAM connection - - \b - Users: - hanzo iam users list # List users - hanzo iam users get NAME # Get user details - hanzo iam users create # Create user - hanzo iam users delete NAME # Delete user - - \b - Organizations: - hanzo iam orgs list # List organizations - hanzo iam orgs get NAME # Get organization details - - \b - Providers: - hanzo iam providers list # List auth providers - hanzo iam providers get NAME # Get provider details - - \b - Applications: - hanzo iam apps list # List applications - hanzo iam apps get NAME # Get application details - - \b - Roles: - hanzo iam roles list # List roles - - \b - Password: - hanzo iam set-password USER # Set/reset user password - hanzo iam enforce-hashing # Enforce argon2id hashing org-wide - - \b - Admin: - hanzo iam login # Login as user (masquerade) - """ - pass - - -# ============================================================================ -# Configure -# ============================================================================ - - -@iam_group.command() -@click.option("--url", "-u", help="IAM server URL (default: https://hanzo.id)") -@click.option("--client-id", "-i", help="OAuth2 client ID") -@click.option("--client-secret", "-s", help="OAuth2 client secret") -@click.option("--org", "-o", default="hanzo", help="Default organization") -def configure(url: str, client_id: str, client_secret: str, org: str): - """Configure IAM credentials for CLI access. - - \b - Examples: - hanzo iam configure -i MY_CLIENT_ID -s MY_SECRET - hanzo iam configure --url https://iam.hanzo.ai - """ - from pathlib import Path - - from rich.prompt import Prompt - - auth_file = Path.home() / ".hanzo" / "auth.json" - auth = {} - if auth_file.exists(): - try: - auth = json.loads(auth_file.read_text()) - except Exception: - pass - - if not url: - url = Prompt.ask("IAM URL", default=auth.get("iam_url", "https://hanzo.id")) - if not client_id: - client_id = Prompt.ask("Client ID", default=auth.get("iam_client_id", "")) - if not client_secret: - client_secret = Prompt.ask( - "Client Secret", password=True, default=auth.get("iam_client_secret", "") - ) - - auth["iam_url"] = url - auth["iam_client_id"] = client_id - auth["iam_client_secret"] = client_secret - auth["iam_org"] = org - - Path.home().joinpath(".hanzo").mkdir(exist_ok=True) - auth_file.write_text(json.dumps(auth, indent=2)) - - console.print("[green]โœ“[/green] IAM credentials saved to ~/.hanzo/auth.json") - - # Test connection - try: - os.environ["IAM_CLIENT_ID"] = client_id - os.environ["IAM_CLIENT_SECRET"] = client_secret - os.environ["IAM_URL"] = url - data = _iam_request("GET", "/api/get-account", auth_params=True) - if data.get("status") != "error": - console.print("[green]โœ“[/green] Connected to IAM successfully") - else: - console.print( - "[yellow]โš [/yellow] Connected but got error: " - + data.get("msg", "unknown") - ) - except SystemExit: - console.print( - "[yellow]โš [/yellow] Could not verify connection (credentials saved anyway)" - ) - - -@iam_group.command(name="status") -def iam_status(): - """Check IAM connection and credentials.""" - url = _get_iam_url() - client_id, client_secret = _get_iam_credentials() - - table = Table(title="IAM Status", box=box.ROUNDED) - table.add_column("Property", style="cyan") - table.add_column("Value", style="white") - - table.add_row("IAM URL", url) - table.add_row( - "Client ID", client_id[:12] + "..." if client_id else "[red]Not set[/red]" - ) - table.add_row( - "Client Secret", - "****" + client_secret[-4:] if client_secret else "[red]Not set[/red]", - ) - - if client_id and client_secret: - try: - data = _iam_request("GET", "/.well-known/openid-configuration") - table.add_row("Connection", "[green]OK[/green]") - table.add_row("Issuer", str(data.get("issuer", "unknown"))) - except SystemExit: - table.add_row("Connection", "[red]Failed[/red]") - else: - table.add_row("Connection", "[yellow]No credentials[/yellow]") - - console.print(table) - - -# ============================================================================ -# Users -# ============================================================================ - - -@iam_group.group() -def users(): - """Manage IAM users.""" - pass - - -@users.command(name="list") -@click.option("--org", "-o", default="hanzo", help="Organization name") -def users_list(org: str): - """List users in organization.""" - data = _iam_request("GET", "/api/get-users", params={"owner": org}) - _check_response(data, "list users") - - users_data = data.get("data") if isinstance(data, dict) else data - if not users_data: - console.print("[dim]No users found[/dim]") - return - - table = Table(title=f"Users ({org})", box=box.ROUNDED) - table.add_column("Username", style="cyan") - table.add_column("Display Name", style="white") - table.add_column("Email", style="green") - table.add_column("Phone", style="dim") - table.add_column("Admin", style="yellow") - table.add_column("Status", style="white") - - for u in users_data: - status = "๐ŸŸข" if not u.get("isForbidden") and not u.get("isDeleted") else "๐Ÿ”ด" - admin = "โœ“" if u.get("isAdmin") else "" - table.add_row( - u.get("name", ""), - u.get("displayName", ""), - u.get("email", ""), - u.get("phone", ""), - admin, - status, - ) - - console.print(table) - console.print(f"[dim]Total: {len(users_data)} users[/dim]") - - -@users.command(name="get") -@click.argument("username") -@click.option("--org", "-o", default="hanzo", help="Organization name") -def users_get(username: str, org: str): - """Get user details.""" - data = _iam_request("GET", "/api/get-user", params={"id": f"{org}/{username}"}) - _check_response(data, "get user") - - user = data.get("data") if isinstance(data, dict) else data - if not user: - console.print(f"[red]User '{username}' not found[/red]") - return - - lines = [ - f"[cyan]Username:[/cyan] {user.get('name', '')}", - f"[cyan]Display Name:[/cyan] {user.get('displayName', '')}", - f"[cyan]Email:[/cyan] {user.get('email', '')}", - f"[cyan]Phone:[/cyan] {user.get('phone', '')}", - f"[cyan]Organization:[/cyan] {user.get('owner', '')}", - f"[cyan]Admin:[/cyan] {'Yes' if user.get('isAdmin') else 'No'}", - f"[cyan]Forbidden:[/cyan] {'Yes' if user.get('isForbidden') else 'No'}", - f"[cyan]Email Verified:[/cyan] {'Yes' if user.get('emailVerified') else 'No'}", - f"[cyan]Signup App:[/cyan] {user.get('signupApplication', '')}", - f"[cyan]Created:[/cyan] {user.get('createdTime', '')}", - f"[cyan]Updated:[/cyan] {user.get('updatedTime', '')}", - ] - - # Show roles/groups if present - if user.get("roles"): - lines.append(f"[cyan]Roles:[/cyan] {', '.join(str(r) for r in user['roles'])}") - if user.get("groups"): - lines.append( - f"[cyan]Groups:[/cyan] {', '.join(str(g) for g in user['groups'])}" - ) - - console.print( - Panel("\n".join(lines), title=f"User: {username}", border_style="cyan") - ) - - -@users.command(name="create") -@click.option("--username", "-u", required=True, help="Username") -@click.option("--email", "-e", help="Email address") -@click.option("--password", "-p", help="Password") -@click.option("--name", "-n", help="Display name") -@click.option("--phone", help="Phone number") -@click.option("--org", "-o", default="hanzo", help="Organization") -@click.option("--admin", is_flag=True, help="Make admin") -def users_create( - username: str, - email: str, - password: str, - name: str, - phone: str, - org: str, - admin: bool, -): - """Create a new user.""" - user_obj = { - "owner": org, - "name": username, - "displayName": name or username, - "email": email or "", - "phone": phone or "", - "password": "", - "isAdmin": admin, - "type": "normal-user", - } - - data = _iam_request("POST", "/api/add-user", json_body=user_obj) - _check_response(data, "create user") - - console.print(f"[green]โœ“[/green] User '{username}' created in org '{org}'") - if email: - console.print(f" Email: {email}") - - # Set password via set-password API (proper hashing) - if password: - client_id, client_secret = _get_iam_credentials() - try: - with httpx.Client(timeout=30.0) as http: - resp = http.post( - f"{_get_iam_url().rstrip('/')}/api/set-password", - params={"clientId": client_id, "clientSecret": client_secret}, - data={ - "userOwner": org, - "userName": username, - "oldPassword": "", - "newPassword": password, - }, - ) - if resp.status_code == 200 and resp.json().get("status") == "ok": - console.print("[green]โœ“[/green] Password set") - else: - console.print( - f"[yellow]โš [/yellow] Password may not have been set: {resp.json().get('msg', '')}" - ) - except Exception as e: - console.print(f"[yellow]โš [/yellow] Could not set password: {e}") - - -@users.command(name="update") -@click.argument("username") -@click.option("--email", "-e", help="New email") -@click.option("--name", "-n", help="New display name") -@click.option("--phone", help="New phone") -@click.option("--password", "-p", help="New password") -@click.option("--admin/--no-admin", default=None, help="Set admin status") -@click.option("--forbidden/--no-forbidden", default=None, help="Ban/unban user") -@click.option("--org", "-o", default="hanzo", help="Organization") -def users_update( - username: str, - email: str, - name: str, - phone: str, - password: str, - admin: bool, - forbidden: bool, - org: str, -): - """Update user fields.""" - # First get existing user - get_data = _iam_request("GET", "/api/get-user", params={"id": f"{org}/{username}"}) - _check_response(get_data, "get user") - user_obj = get_data.get("data", get_data) - if not user_obj: - console.print(f"[red]User '{username}' not found[/red]") - return - - # Apply updates (except password which uses set-password API) - new_password = password - if email is not None: - user_obj["email"] = email - if name is not None: - user_obj["displayName"] = name - if phone is not None: - user_obj["phone"] = phone - if admin is not None: - user_obj["isAdmin"] = admin - if forbidden is not None: - user_obj["isForbidden"] = forbidden - - data = _iam_request( - "POST", - "/api/update-user", - params={"id": f"{org}/{username}"}, - json_body=user_obj, - ) - _check_response(data, "update user") - - console.print(f"[green]โœ“[/green] User '{username}' updated") - - # Set password via proper set-password API - if new_password is not None: - client_id, client_secret = _get_iam_credentials() - try: - with httpx.Client(timeout=30.0) as http: - resp = http.post( - f"{_get_iam_url().rstrip('/')}/api/set-password", - params={"clientId": client_id, "clientSecret": client_secret}, - data={ - "userOwner": org, - "userName": username, - "oldPassword": "", - "newPassword": new_password, - }, - ) - if resp.status_code == 200 and resp.json().get("status") == "ok": - console.print("[green]โœ“[/green] Password updated") - else: - console.print( - f"[yellow]โš [/yellow] Password update issue: {resp.json().get('msg', '')}" - ) - except Exception as e: - console.print(f"[yellow]โš [/yellow] Could not update password: {e}") - - -@users.command(name="delete") -@click.argument("username") -@click.option("--org", "-o", default="hanzo", help="Organization") -@click.option("--yes", "-y", is_flag=True, help="Skip confirmation") -def users_delete(username: str, org: str, yes: bool): - """Delete a user.""" - if not yes: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete user '{username}' from '{org}'?[/red]"): - return - - user_obj = {"owner": org, "name": username} - data = _iam_request("POST", "/api/delete-user", json_body=user_obj) - _check_response(data, "delete user") - - console.print(f"[green]โœ“[/green] User '{username}' deleted") - - -@users.command(name="count") -@click.option("--org", "-o", default="hanzo", help="Organization name") -def users_count(org: str): - """Get user count in organization.""" - data = _iam_request("GET", "/api/get-user-count", params={"owner": org}) - _check_response(data, "count users") - - count = data.get("data", data) if isinstance(data, dict) else data - console.print(f"Users in '{org}': [bold]{count}[/bold]") - - -# ============================================================================ -# Organizations -# ============================================================================ - - -@iam_group.group() -def orgs(): - """Manage IAM organizations.""" - pass - - -@orgs.command(name="list") -def orgs_list(): - """List all organizations.""" - data = _iam_request("GET", "/api/get-organizations", params={"owner": "admin"}) - _check_response(data, "list organizations") - - orgs_data = data.get("data") if isinstance(data, dict) else data - if not orgs_data: - console.print("[dim]No organizations found[/dim]") - return - - table = Table(title="Organizations", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Display Name", style="white") - table.add_column("Website", style="dim") - table.add_column("Password Type", style="dim") - table.add_column("Created", style="dim") - - for o in orgs_data: - table.add_row( - o.get("name", ""), - o.get("displayName", ""), - o.get("websiteUrl", ""), - o.get("passwordType", ""), - o.get("createdTime", "")[:10] if o.get("createdTime") else "", - ) - - console.print(table) - - -@orgs.command(name="get") -@click.argument("name") -def orgs_get(name: str): - """Get organization details.""" - data = _iam_request("GET", "/api/get-organization", params={"id": f"admin/{name}"}) - _check_response(data, "get organization") - - org = data.get("data") if isinstance(data, dict) else data - if not org: - console.print(f"[red]Organization '{name}' not found[/red]") - return - - lines = [ - f"[cyan]Name:[/cyan] {org.get('name', '')}", - f"[cyan]Display Name:[/cyan] {org.get('displayName', '')}", - f"[cyan]Website:[/cyan] {org.get('websiteUrl', '')}", - f"[cyan]Favicon:[/cyan] {org.get('favicon', '')}", - f"[cyan]Password Type:[/cyan] {org.get('passwordType', '')}", - f"[cyan]Phone Prefix:[/cyan] {org.get('phonePrefix', '')}", - f"[cyan]Default Avatar:[/cyan] {org.get('defaultAvatar', '')}", - f"[cyan]Master Password:[/cyan] {'Set' if org.get('masterPassword') else 'Not set'}", - f"[cyan]Init Score:[/cyan] {org.get('initScore', 0)}", - f"[cyan]MFA Enabled:[/cyan] {', '.join(org.get('mfaItems', [])) if org.get('mfaItems') else 'None'}", - f"[cyan]Created:[/cyan] {org.get('createdTime', '')}", - ] - - console.print( - Panel("\n".join(lines), title=f"Organization: {name}", border_style="cyan") - ) - - -@orgs.command(name="create") -@click.option("--name", "-n", required=True, help="Organization name (slug)") -@click.option("--display-name", "-d", help="Display name") -@click.option("--website", "-w", help="Website URL") -def orgs_create(name: str, display_name: str, website: str): - """Create a new organization.""" - org_obj = { - "owner": "admin", - "name": name, - "displayName": display_name or name, - "websiteUrl": website or "", - "passwordType": "bcrypt", - } - - data = _iam_request("POST", "/api/add-organization", json_body=org_obj) - _check_response(data, "create organization") - - console.print(f"[green]โœ“[/green] Organization '{name}' created") - - -@orgs.command(name="delete") -@click.argument("name") -@click.option("--yes", "-y", is_flag=True, help="Skip confirmation") -def orgs_delete(name: str, yes: bool): - """Delete an organization.""" - if not yes: - from rich.prompt import Confirm - - if not Confirm.ask( - f"[red]Delete organization '{name}'? This cannot be undone.[/red]" - ): - return - - org_obj = {"owner": "admin", "name": name} - data = _iam_request("POST", "/api/delete-organization", json_body=org_obj) - _check_response(data, "delete organization") - - console.print(f"[green]โœ“[/green] Organization '{name}' deleted") - - -# ============================================================================ -# Providers -# ============================================================================ - - -@iam_group.group() -def providers(): - """Manage authentication providers (OAuth, SAML, etc.).""" - pass - - -@providers.command(name="list") -@click.option("--owner", default="admin", help="Provider owner") -def providers_list(owner: str): - """List authentication providers.""" - data = _iam_request("GET", "/api/get-providers", params={"owner": owner}) - _check_response(data, "list providers") - - provs = data.get("data") if isinstance(data, dict) else data - if not provs: - console.print("[dim]No providers found[/dim]") - return - - table = Table(title="Providers", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Display Name", style="white") - table.add_column("Type", style="green") - table.add_column("Category", style="yellow") - table.add_column("Client ID", style="dim") - - for p in provs: - table.add_row( - p.get("name", ""), - p.get("displayName", ""), - p.get("type", ""), - p.get("category", ""), - (p.get("clientId", "")[:16] + "...") if p.get("clientId") else "", - ) - - console.print(table) - - -@providers.command(name="get") -@click.argument("name") -@click.option("--owner", default="admin", help="Provider owner") -def providers_get(name: str, owner: str): - """Get provider details.""" - data = _iam_request("GET", "/api/get-provider", params={"id": f"{owner}/{name}"}) - _check_response(data, "get provider") - - prov = data.get("data") if isinstance(data, dict) else data - if not prov: - console.print(f"[red]Provider '{name}' not found[/red]") - return - - lines = [ - f"[cyan]Name:[/cyan] {prov.get('name', '')}", - f"[cyan]Display Name:[/cyan] {prov.get('displayName', '')}", - f"[cyan]Type:[/cyan] {prov.get('type', '')}", - f"[cyan]Category:[/cyan] {prov.get('category', '')}", - f"[cyan]Client ID:[/cyan] {prov.get('clientId', '')}", - f"[cyan]Client Secret:[/cyan] {'****' + prov.get('clientSecret', '')[-4:] if prov.get('clientSecret') else 'Not set'}", - f"[cyan]Provider URL:[/cyan] {prov.get('providerUrl', '')}", - f"[cyan]Scopes:[/cyan] {prov.get('scopes', '')}", - f"[cyan]Created:[/cyan] {prov.get('createdTime', '')}", - ] - - console.print( - Panel("\n".join(lines), title=f"Provider: {name}", border_style="cyan") - ) - - -@providers.command(name="create") -@click.option("--name", "-n", required=True, help="Provider name") -@click.option( - "--type", - "-t", - "ptype", - required=True, - type=click.Choice( - [ - "Google", - "GitHub", - "Facebook", - "Twitter", - "Apple", - "Microsoft", - "Discord", - "Slack", - "WeChat", - "SAML", - "OIDC", - "CAS", - "LDAP", - ] - ), - help="Provider type", -) -@click.option("--display-name", "-d", help="Display name") -@click.option("--client-id", "-i", required=True, help="OAuth client ID") -@click.option("--client-secret", "-s", required=True, help="OAuth client secret") -@click.option("--scopes", help="OAuth scopes") -@click.option("--owner", default="admin", help="Provider owner") -def providers_create( - name: str, - ptype: str, - display_name: str, - client_id: str, - client_secret: str, - scopes: str, - owner: str, -): - """Create an authentication provider.""" - prov_obj = { - "owner": owner, - "name": name, - "displayName": display_name or f"{ptype} Login", - "type": ptype, - "category": "OAuth", - "clientId": client_id, - "clientSecret": client_secret, - "scopes": scopes or "", - } - - data = _iam_request("POST", "/api/add-provider", json_body=prov_obj) - _check_response(data, "create provider") - - console.print(f"[green]โœ“[/green] Provider '{name}' ({ptype}) created") - - -@providers.command(name="delete") -@click.argument("name") -@click.option("--owner", default="admin", help="Provider owner") -@click.option("--yes", "-y", is_flag=True, help="Skip confirmation") -def providers_delete(name: str, owner: str, yes: bool): - """Delete an authentication provider.""" - if not yes: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete provider '{name}'?[/red]"): - return - - prov_obj = {"owner": owner, "name": name} - data = _iam_request("POST", "/api/delete-provider", json_body=prov_obj) - _check_response(data, "delete provider") - - console.print(f"[green]โœ“[/green] Provider '{name}' deleted") - - -# ============================================================================ -# Applications -# ============================================================================ - - -@iam_group.group() -def apps(): - """Manage IAM applications.""" - pass - - -@apps.command(name="list") -@click.option("--org", "-o", default="admin", help="Organization/owner") -def apps_list(org: str): - """List applications.""" - data = _iam_request("GET", "/api/get-applications", params={"owner": org}) - _check_response(data, "list applications") - - apps_data = data.get("data") if isinstance(data, dict) else data - if not apps_data: - console.print("[dim]No applications found[/dim]") - return - - table = Table(title="Applications", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Display Name", style="white") - table.add_column("Organization", style="green") - table.add_column("Client ID", style="dim") - table.add_column("Providers", style="yellow") - - for a in apps_data: - providers_count = len(a.get("providers", [])) - table.add_row( - a.get("name", ""), - a.get("displayName", ""), - a.get("organization", ""), - ( - a.get("clientId", "")[:20] + "..." - if len(a.get("clientId", "")) > 20 - else a.get("clientId", "") - ), - str(providers_count), - ) - - console.print(table) - - -@apps.command(name="get") -@click.argument("name") -@click.option("--org", "-o", default="admin", help="Organization/owner") -def apps_get(name: str, org: str): - """Get application details.""" - data = _iam_request("GET", "/api/get-application", params={"id": f"{org}/{name}"}) - _check_response(data, "get application") - - app = data.get("data") if isinstance(data, dict) else data - if not app: - console.print(f"[red]Application '{name}' not found[/red]") - return - - lines = [ - f"[cyan]Name:[/cyan] {app.get('name', '')}", - f"[cyan]Display Name:[/cyan] {app.get('displayName', '')}", - f"[cyan]Organization:[/cyan] {app.get('organization', '')}", - f"[cyan]Client ID:[/cyan] {app.get('clientId', '')}", - f"[cyan]Client Secret:[/cyan] {'****' + app.get('clientSecret', '')[-4:] if app.get('clientSecret') else 'N/A'}", - f"[cyan]Homepage:[/cyan] {app.get('homepageUrl', '')}", - f"[cyan]Redirect URIs:[/cyan] {', '.join(app.get('redirectUris') or [])}", - f"[cyan]Grant Types:[/cyan] {', '.join(app.get('grantTypes') or [])}", - f"[cyan]Token Expiry:[/cyan] {app.get('expireInHours', '?')} hours", - f"[cyan]Enable Password:[/cyan] {app.get('enablePassword', '?')}", - f"[cyan]Enable Signup:[/cyan] {app.get('enableSignUp', '?')}", - ] - - # Show providers - providers_list = app.get("providers", []) - if providers_list: - lines.append(f"[cyan]Providers:[/cyan]") - for p in providers_list: - pname = p.get("name", "") if isinstance(p, dict) else str(p) - lines.append(f" - {pname}") - - # Show signup items - signup_items = app.get("signupItems", []) - if signup_items: - lines.append(f"[cyan]Signup Items:[/cyan]") - for s in signup_items: - sname = s.get("name", "") if isinstance(s, dict) else str(s) - required = s.get("required", False) if isinstance(s, dict) else False - lines.append(f" - {sname} {'(required)' if required else '(optional)'}") - - console.print( - Panel("\n".join(lines), title=f"Application: {name}", border_style="cyan") - ) - - -# ============================================================================ -# Roles -# ============================================================================ - - -@iam_group.group() -def roles(): - """Manage IAM roles.""" - pass - - -@roles.command(name="list") -@click.option("--org", "-o", default="hanzo", help="Organization") -def roles_list(org: str): - """List roles in organization.""" - data = _iam_request("GET", "/api/get-roles", params={"owner": org}) - _check_response(data, "list roles") - - roles_data = data.get("data") if isinstance(data, dict) else data - if not roles_data: - console.print("[dim]No roles found[/dim]") - return - - table = Table(title=f"Roles ({org})", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Display Name", style="white") - table.add_column("Users", style="green") - table.add_column("Domains", style="dim") - table.add_column("Created", style="dim") - - for r in roles_data: - users_count = len(r.get("users", [])) - domains = ", ".join(r.get("domains", [])) - table.add_row( - r.get("name", ""), - r.get("displayName", ""), - str(users_count), - domains, - r.get("createdTime", "")[:10] if r.get("createdTime") else "", - ) - - console.print(table) - - -@roles.command(name="get") -@click.argument("name") -@click.option("--org", "-o", default="hanzo", help="Organization") -def roles_get(name: str, org: str): - """Get role details.""" - data = _iam_request("GET", "/api/get-role", params={"id": f"{org}/{name}"}) - _check_response(data, "get role") - - role = data.get("data") if isinstance(data, dict) else data - if not role: - console.print(f"[red]Role '{name}' not found[/red]") - return - - lines = [ - f"[cyan]Name:[/cyan] {role.get('name', '')}", - f"[cyan]Display Name:[/cyan] {role.get('displayName', '')}", - f"[cyan]Description:[/cyan] {role.get('description', '')}", - ] - - users_list = role.get("users", []) - if users_list: - lines.append(f"[cyan]Users ({len(users_list)}):[/cyan]") - for u in users_list: - lines.append(f" - {u}") - - roles_sub = role.get("roles", []) - if roles_sub: - lines.append(f"[cyan]Sub-Roles:[/cyan]") - for sr in roles_sub: - lines.append(f" - {sr}") - - console.print(Panel("\n".join(lines), title=f"Role: {name}", border_style="cyan")) - - -# ============================================================================ -# Password Management -# ============================================================================ - - -@iam_group.command(name="set-password") -@click.argument("username") -@click.option("--password", "-p", help="New password (prompted if not provided)") -@click.option("--org", "-o", default="hanzo", help="Organization") -def set_password(username: str, password: str, org: str): - """Set or reset a user's password. - - \b - Uses the IAM /api/set-password endpoint which handles - server-side hashing (argon2id). Passwords are NEVER stored - in plaintext. - - \b - Examples: - hanzo iam set-password alice - hanzo iam set-password alice -p 'NewSecurePass123!' - hanzo iam set-password admin -o built-in - """ - from rich.prompt import Prompt - - if not password: - password = Prompt.ask("New password", password=True) - confirm = Prompt.ask("Confirm password", password=True) - if password != confirm: - console.print("[red]Error:[/red] Passwords do not match") - raise SystemExit(1) - - if len(password) < 8: - console.print("[red]Error:[/red] Password must be at least 8 characters") - raise SystemExit(1) - - client_id, client_secret = _get_iam_credentials() - if not client_id or not client_secret: - console.print("[red]Error:[/red] IAM credentials not configured") - console.print("Run: hanzo iam configure") - raise SystemExit(1) - - url = _get_iam_url().rstrip("/") - try: - with httpx.Client(timeout=30.0) as http: - resp = http.post( - f"{url}/api/set-password", - params={"clientId": client_id, "clientSecret": client_secret}, - data={ - "userOwner": org, - "userName": username, - "oldPassword": "", - "newPassword": password, - }, - ) - resp.raise_for_status() - data = resp.json() - except httpx.ConnectError: - console.print(f"[red]Error:[/red] Cannot connect to IAM at {url}") - raise SystemExit(1) - except httpx.HTTPStatusError as e: - console.print(f"[red]Error:[/red] IAM returned {e.response.status_code}") - try: - detail = e.response.json() - console.print(f" {detail.get('msg', detail)}") - except Exception: - console.print(f" {e.response.text[:200]}") - raise SystemExit(1) - - if data.get("status") == "ok": - console.print(f"[green]โœ“[/green] Password set for '{username}' in org '{org}'") - else: - console.print(f"[red]Error:[/red] {data.get('msg', 'Failed to set password')}") - raise SystemExit(1) - - -@iam_group.command(name="enforce-hashing") -@click.option("--org", "-o", default="hanzo", help="Organization to enforce on") -@click.option( - "--algorithm", - "-a", - default="argon2id", - type=click.Choice(["argon2id", "bcrypt"]), - help="Hash algorithm", -) -@click.option("--all-orgs", is_flag=True, help="Enforce on all organizations") -def enforce_hashing(org: str, algorithm: str, all_orgs: bool): - """Enforce password hashing on IAM organizations. - - \b - Sets the organization passwordType to argon2id (default) or bcrypt, - preventing any plaintext password storage. Also audits existing users - and reports any with plaintext passwords. - - \b - Examples: - hanzo iam enforce-hashing - hanzo iam enforce-hashing --all-orgs - hanzo iam enforce-hashing -o built-in -a bcrypt - """ - orgs_to_update = [] - - if all_orgs: - data = _iam_request("GET", "/api/get-organizations", params={"owner": "admin"}) - _check_response(data, "list organizations") - orgs_to_update = data.get("data", []) - else: - data = _iam_request( - "GET", "/api/get-organization", params={"id": f"admin/{org}"} - ) - _check_response(data, "get organization") - org_obj = data.get("data", data) if isinstance(data, dict) else data - if org_obj: - orgs_to_update = [org_obj] - - if not orgs_to_update: - console.print("[red]No organizations found[/red]") - raise SystemExit(1) - - updated = 0 - for org_obj in orgs_to_update: - org_name = org_obj.get("name", "") - current_type = org_obj.get("passwordType", "") - - if current_type == algorithm: - console.print(f" [dim]{org_name}: already using {algorithm}[/dim]") - continue - - org_obj["passwordType"] = algorithm - update_data = _iam_request( - "POST", - "/api/update-organization", - params={"id": f"admin/{org_name}"}, - json_body=org_obj, - ) - if update_data.get("status") == "error": - console.print( - f" [red]โœ— {org_name}: {update_data.get('msg', 'failed')}[/red]" - ) - else: - console.print( - f" [green]โœ“ {org_name}: {current_type or 'plain'} โ†’ {algorithm}[/green]" - ) - updated += 1 - - console.print(f"\n[bold]{updated} organization(s) updated to {algorithm}[/bold]") - - # Audit users for plaintext passwords - console.print("\n[bold]Auditing users for plaintext passwords...[/bold]") - plaintext_count = 0 - for org_obj in orgs_to_update: - org_name = org_obj.get("name", "") - users_data = _iam_request("GET", "/api/get-users", params={"owner": org_name}) - users_list = users_data.get("data", []) if isinstance(users_data, dict) else [] - for user in users_list: - pw_type = user.get("passwordType", "") - if pw_type == "plain" or (pw_type == "" and user.get("password", "")): - console.print( - f" [yellow]โš  {org_name}/{user.get('name', '?')}: password_type='{pw_type}' โ€” needs rehash[/yellow]" - ) - plaintext_count += 1 - - if plaintext_count == 0: - console.print(" [green]โœ“ No plaintext passwords found[/green]") - else: - console.print( - f"\n [yellow]โš  {plaintext_count} user(s) have plaintext passwords[/yellow]" - ) - console.print(" Reset them with: hanzo iam set-password USERNAME") - - -# ============================================================================ -# Admin: Login / Masquerade -# ============================================================================ - - -@iam_group.command(name="login") -@click.option("--username", "-u", help="Username or email") -@click.option("--password", "-p", help="Password") -@click.option("--org", "-o", default="hanzo", help="Organization") -@click.option("--app", "-a", default="app-hanzo", help="Application name") -def iam_login(username: str, password: str, org: str, app: str): - """Login as a user via IAM (email/password). - - \b - Uses the Casdoor /api/login endpoint directly. - Stores the resulting token in ~/.hanzo/auth.json. - - \b - Examples: - hanzo iam login -u admin@hanzo.ai - hanzo iam login -u z -p mypassword --org hanzo - """ - from pathlib import Path - from datetime import datetime - - from rich.prompt import Prompt - - if not username: - username = Prompt.ask("Username or email") - if not password: - password = Prompt.ask("Password", password=True) - - url = _get_iam_url().rstrip("/") - - try: - with httpx.Client(timeout=30.0) as http: - resp = http.post( - f"{url}/api/login", - json={ - "type": "token", - "username": username, - "password": password, - "organization": org, - "application": app, - }, - ) - resp.raise_for_status() - data = resp.json() - except httpx.ConnectError: - console.print(f"[red]Error:[/red] Cannot connect to IAM at {url}") - return - except httpx.HTTPStatusError as e: - console.print(f"[red]Error:[/red] Login failed ({e.response.status_code})") - return - - if data.get("status") != "ok": - console.print(f"[red]Login failed:[/red] {data.get('msg', 'Unknown error')}") - return - - token = data.get("data", "") - console.print(f"[green]โœ“[/green] Logged in as {username}") - - # Save to auth.json - auth_file = Path.home() / ".hanzo" / "auth.json" - auth = {} - if auth_file.exists(): - try: - auth = json.loads(auth_file.read_text()) - except Exception: - pass - - auth["logged_in"] = True - auth["email"] = username - auth["token"] = token - auth["iam_org"] = org - auth["iam_app"] = app - auth["last_login"] = datetime.now().isoformat() - - Path.home().joinpath(".hanzo").mkdir(exist_ok=True) - auth_file.write_text(json.dumps(auth, indent=2)) - - console.print("[green]โœ“[/green] Access token saved to ~/.hanzo/auth.json") - - # Also try password grant for refresh token - client_id, client_secret = _get_iam_credentials() - if client_id and client_secret: - try: - with httpx.Client(timeout=30.0) as http: - token_resp = http.post( - f"{url}/oauth/token", - data={ - "grant_type": "password", - "client_id": client_id, - "client_secret": client_secret, - "username": username, - "password": password, - "scope": "openid profile email", - }, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - ) - if token_resp.status_code == 200: - token_data = token_resp.json() - if token_data.get("refresh_token"): - auth["refresh_token"] = token_data["refresh_token"] - auth_file.write_text(json.dumps(auth, indent=2)) - console.print("[green]โœ“[/green] Refresh token obtained") - except Exception: - pass # Refresh token is optional - - -# ============================================================================ -# Admin: Raw API Call -# ============================================================================ - - -@iam_group.command(name="api") -@click.argument("endpoint") -@click.option("--method", "-m", default="GET", type=click.Choice(["GET", "POST"])) -@click.option("--data", "-d", "body", help="JSON body for POST requests") -@click.option("--param", "-p", multiple=True, help="Extra params (key=value)") -def iam_api(endpoint: str, method: str, body: str, param: tuple): - """Make raw API call to IAM server. - - \b - Examples: - hanzo iam api /api/get-users --param owner=hanzo - hanzo iam api /api/get-application --param id=hanzo/app-hanzo - hanzo iam api /api/get-providers --param owner=admin - """ - extra_params = {} - for p in param: - if "=" in p: - k, v = p.split("=", 1) - extra_params[k] = v - - json_body = None - if body: - try: - json_body = json.loads(body) - except json.JSONDecodeError: - console.print("[red]Error:[/red] Invalid JSON body") - return - - if not endpoint.startswith("/"): - endpoint = "/" + endpoint - - data = _iam_request(method, endpoint, params=extra_params, json_body=json_body) - - # Pretty print the response - console.print_json(json.dumps(data, indent=2, default=str)) - - -# ============================================================================ -# Tokens -# ============================================================================ - - -@iam_group.group() -def tokens(): - """Manage and inspect tokens.""" - pass - - -@tokens.command(name="inspect") -@click.argument("token", required=False) -def tokens_inspect(token: str): - """Inspect a JWT token (decode without verification).""" - import base64 - - if not token: - # Try to get from auth.json - from pathlib import Path - - auth_file = Path.home() / ".hanzo" / "auth.json" - if auth_file.exists(): - auth = json.loads(auth_file.read_text()) - token = auth.get("token", "") - - if not token: - console.print( - "[red]No token provided and none found in ~/.hanzo/auth.json[/red]" - ) - return - - parts = token.split(".") - if len(parts) != 3: - console.print("[red]Invalid JWT format (expected 3 parts)[/red]") - return - - # Decode header and payload (without verification) - for label, part in [("Header", parts[0]), ("Payload", parts[1])]: - # Add padding - padded = part + "=" * (4 - len(part) % 4) - try: - decoded = base64.urlsafe_b64decode(padded) - data = json.loads(decoded) - console.print(f"\n[bold cyan]{label}:[/bold cyan]") - console.print_json(json.dumps(data, indent=2, default=str)) - except Exception as e: - console.print(f"[red]Error decoding {label}: {e}[/red]") - - # Show expiry info - try: - padded = parts[1] + "=" * (4 - len(parts[1]) % 4) - payload = json.loads(base64.urlsafe_b64decode(padded)) - if "exp" in payload: - from datetime import datetime - - exp = datetime.fromtimestamp(payload["exp"]) - now = datetime.now() - if now > exp: - console.print(f"\n[red]Token EXPIRED at {exp}[/red]") - else: - delta = exp - now - console.print( - f"\n[green]Token valid until {exp} ({delta} remaining)[/green]" - ) - except Exception: - pass - - -@tokens.command(name="exchange") -@click.argument("code") -@click.option( - "--redirect-uri", "-r", default="", help="Redirect URI used in authorization" -) -def tokens_exchange(code: str, redirect_uri: str): - """Exchange authorization code for access token.""" - client_id, client_secret = _get_iam_credentials() - if not client_id: - console.print("[red]Error:[/red] No client credentials configured") - return - - url = _get_iam_url().rstrip("/") - try: - with httpx.Client(timeout=30.0) as http: - resp = http.post( - f"{url}/oauth/token", - data={ - "grant_type": "authorization_code", - "client_id": client_id, - "client_secret": client_secret, - "code": code, - "redirect_uri": redirect_uri, - }, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - ) - resp.raise_for_status() - data = resp.json() - except Exception as e: - console.print(f"[red]Error:[/red] {e}") - return - - if data.get("access_token"): - console.print("[green]โœ“[/green] Token exchange successful") - console.print(f" Access Token: {data['access_token'][:30]}...") - if data.get("refresh_token"): - console.print(f" Refresh Token: {data['refresh_token'][:30]}...") - console.print(f" Expires In: {data.get('expires_in', '?')}s") - - # Save to auth.json - from pathlib import Path - - auth_file = Path.home() / ".hanzo" / "auth.json" - auth = {} - if auth_file.exists(): - try: - auth = json.loads(auth_file.read_text()) - except Exception: - pass - auth["token"] = data["access_token"] - if data.get("refresh_token"): - auth["refresh_token"] = data["refresh_token"] - auth_file.write_text(json.dumps(auth, indent=2)) - console.print("[green]โœ“[/green] Token saved to ~/.hanzo/auth.json") - else: - console.print("[red]Token exchange failed[/red]") - console.print_json(json.dumps(data, indent=2, default=str)) diff --git a/pkg/hanzo/src/hanzo/commands/install.py b/pkg/hanzo/src/hanzo/commands/install.py deleted file mode 100644 index ea4475731..000000000 --- a/pkg/hanzo/src/hanzo/commands/install.py +++ /dev/null @@ -1,1663 +0,0 @@ -"""Hanzo Install - Cross-platform tool installation CLI. - -Install Hanzo tools from PyPI, npm, cargo, and GitHub releases. -""" - -import os -import sys -import shutil -import platform -import subprocess -from typing import Dict, List, Optional -from pathlib import Path - -import click -from rich import box -from rich.panel import Panel -from rich.table import Table -from rich.progress import Progress, BarColumn, TextColumn, SpinnerColumn - -from ..utils.output import console - -# ============================================================================ -# Tool Registry -# ============================================================================ - -TOOLS = { - # Python tools (PyPI) - "cli": { - "name": "Hanzo CLI", - "description": "Python CLI for Hanzo AI platform", - "source": "pypi", - "package": "hanzo", - "extras": ["all"], - "binary": "hanzo", - "language": "python", - }, - "mcp": { - "name": "Hanzo MCP", - "description": "Model Context Protocol server", - "source": "pypi", - "package": "hanzo-mcp", - "extras": ["tools-all"], - "binary": "hanzo-mcp", - "language": "python", - }, - "agents": { - "name": "Hanzo Agents", - "description": "Multi-agent orchestration framework", - "source": "pypi", - "package": "hanzo-agents", - "binary": "hanzo-agents", - "language": "python", - }, - "ai": { - "name": "Hanzo AI SDK", - "description": "Python SDK for Hanzo AI APIs", - "source": "pypi", - "package": "hanzoai", - "binary": None, - "language": "python", - }, - # JavaScript/TypeScript tools (npm) - "cli-js": { - "name": "Hanzo CLI (JS)", - "description": "JavaScript CLI for container runtime", - "source": "npm", - "package": "@hanzoai/cli", - "binary": "hanzo-js", - "language": "javascript", - }, - "mcp-js": { - "name": "Hanzo MCP (JS)", - "description": "JavaScript MCP implementation", - "source": "npm", - "package": "@anthropic/mcp", # Uses official MCP - "binary": None, - "language": "javascript", - }, - # Rust tools (cargo/GitHub releases) - "node": { - "name": "Hanzo Node", - "description": "Rust-based AI compute node", - "source": "github", - "repo": "hanzoai/node", - "binary": "hanzo-node", - "language": "rust", - "cargo": "hanzo-node", - }, - "dev": { - "name": "Hanzo Dev", - "description": "Rust AI coding assistant (Codex)", - "source": "github", - "repo": "hanzoai/dev", - "binary": "hanzo-dev", - "language": "rust", - "cargo": "hanzo-dev", - }, - "mcp-rs": { - "name": "Hanzo MCP (Rust)", - "description": "High-performance Rust MCP server", - "source": "github", - "repo": "hanzoai/mcp-rs", - "binary": "hanzo-mcp-rs", - "language": "rust", - "cargo": "hanzo-mcp", - }, - # Go tools (go install) - "router": { - "name": "Hanzo Router", - "description": "LLM Gateway/Router (LLM proxy)", - "source": "docker", - "image": "ghcr.io/hanzoai/llm:latest", - "binary": None, - "language": "go", - }, -} - -# Tool bundles -BUNDLES = { - "minimal": ["cli"], - "python": ["cli", "mcp", "agents", "ai"], - "rust": ["node", "dev", "mcp-rs"], - "javascript": ["cli-js", "mcp-js"], - "full": ["cli", "mcp", "agents", "ai", "node", "dev"], - "dev": ["cli", "mcp", "dev"], - "cloud": ["cli", "mcp", "router"], -} - - -def get_arch() -> str: - """Get system architecture for binary downloads.""" - machine = platform.machine().lower() - if machine in ("x86_64", "amd64"): - return "x86_64" - elif machine in ("arm64", "aarch64"): - return "aarch64" - elif machine in ("arm", "armv7l"): - return "arm" - return machine - - -def get_os() -> str: - """Get OS name for binary downloads.""" - system = platform.system().lower() - if system == "darwin": - return "apple-darwin" - elif system == "linux": - return "unknown-linux-gnu" - elif system == "windows": - return "pc-windows-msvc" - return system - - -def get_install_dir() -> Path: - """Get installation directory for binaries.""" - # Check for custom install dir - if custom_dir := os.environ.get("HANZO_INSTALL_DIR"): - return Path(custom_dir) - - # Default to ~/.hanzo/bin - return Path.home() / ".hanzo" / "bin" - - -def ensure_path_configured(): - """Ensure ~/.hanzo/bin is in PATH.""" - install_dir = get_install_dir() - install_dir.mkdir(parents=True, exist_ok=True) - - path = os.environ.get("PATH", "") - if str(install_dir) not in path: - shell = os.environ.get("SHELL", "/bin/bash") - if "zsh" in shell: - rc_file = Path.home() / ".zshrc" - elif "bash" in shell: - rc_file = Path.home() / ".bashrc" - else: - rc_file = Path.home() / ".profile" - - export_line = f'\nexport PATH="$HOME/.hanzo/bin:$PATH"\n' - - # Check if already configured - if rc_file.exists(): - content = rc_file.read_text() - if ".hanzo/bin" not in content: - with open(rc_file, "a") as f: - f.write(export_line) - return True - return False - - -@click.group(name="install") -def install_group(): - """Hanzo Install - Tool installation manager. - - \b - Quick Install: - hanzo install all # Install all tools - hanzo install cli # Install Python CLI - hanzo install node # Install Rust node - - \b - Bundles: - hanzo install --bundle python # Python tools (cli, mcp, agents, ai) - hanzo install --bundle rust # Rust tools (node, dev, mcp-rs) - hanzo install --bundle dev # Development tools - - \b - Management: - hanzo install list # List installed tools - hanzo install update # Update all tools - hanzo install uninstall # Remove a tool - - \b - Environment Variables: - HANZO_INSTALL_DIR # Custom install directory - HANZO_PREFER_RUST # Prefer Rust implementations - HANZO_PREFER_SOURCE # Build from source vs binaries - """ - pass - - -@install_group.command(name="list") -@click.option("--installed", "-i", is_flag=True, help="Show only installed tools") -@click.option("--available", "-a", is_flag=True, help="Show only available tools") -def install_list(installed: bool, available: bool): - """List all Hanzo tools.""" - table = Table(title="Hanzo Tools", box=box.ROUNDED) - table.add_column("Tool", style="cyan") - table.add_column("Name", style="white") - table.add_column("Language", style="yellow") - table.add_column("Source", style="green") - table.add_column("Installed", style="green") - table.add_column("Version", style="dim") - - for tool_id, tool in TOOLS.items(): - # Check if installed - is_installed = False - version = "-" - - if binary := tool.get("binary"): - is_installed = shutil.which(binary) is not None - if is_installed: - try: - result = subprocess.run( - [binary, "--version"], capture_output=True, text=True, timeout=5 - ) - version = ( - result.stdout.strip().split()[-1] - if result.returncode == 0 - else "?" - ) - except Exception: - version = "?" - - if installed and not is_installed: - continue - if available and is_installed: - continue - - table.add_row( - tool_id, - tool["name"], - tool["language"], - tool["source"], - "โœ“" if is_installed else "", - version, - ) - - console.print(table) - - console.print() - console.print("[cyan]Bundles:[/cyan]") - for bundle_id, tools in BUNDLES.items(): - console.print(f" {bundle_id}: {', '.join(tools)}") - - -@install_group.command(name="tool") -@click.argument("tool_name") -@click.option("--version", "-v", help="Specific version") -@click.option("--source", "-s", is_flag=True, help="Build from source") -@click.option("--force", "-f", is_flag=True, help="Force reinstall") -def install_tool(tool_name: str, version: str, source: bool, force: bool): - """Install a specific tool. - - \b - Examples: - hanzo install tool cli - hanzo install tool node --version 0.1.0 - hanzo install tool dev --source - """ - if tool_name == "all": - # Install all tools - for tid in TOOLS: - _install_single_tool(tid, version, source, force) - return - - if tool_name not in TOOLS: - console.print(f"[red]Unknown tool: {tool_name}[/red]") - console.print(f"Available: {', '.join(TOOLS.keys())}") - return - - _install_single_tool(tool_name, version, source, force) - - -def _install_single_tool(tool_id: str, version: str, source: bool, force: bool): - """Install a single tool.""" - tool = TOOLS[tool_id] - - console.print(f"[cyan]Installing {tool['name']}...[/cyan]") - - try: - if tool["source"] == "pypi": - _install_pypi(tool, version, force) - elif tool["source"] == "npm": - _install_npm(tool, version, force) - elif tool["source"] == "github": - if source or os.environ.get("HANZO_PREFER_SOURCE"): - _install_cargo(tool, version, force) - else: - _install_github_release(tool, version, force) - elif tool["source"] == "docker": - _install_docker(tool, version, force) - else: - console.print(f"[yellow]Unknown source: {tool['source']}[/yellow]") - return - - console.print(f"[green]โœ“[/green] {tool['name']} installed") - - except Exception as e: - console.print(f"[red]Failed to install {tool['name']}: {e}[/red]") - - -def _install_pypi(tool: dict, version: str, force: bool): - """Install from PyPI using uvx/pip.""" - package = tool["package"] - extras = tool.get("extras", []) - - if extras: - package = f"{package}[{','.join(extras)}]" - if version: - package = f"{package}=={version}" - - # Prefer uvx/uv, fallback to pip - if shutil.which("uv"): - cmd = ["uv", "pip", "install"] - if force: - cmd.append("--force-reinstall") - cmd.append(package) - else: - cmd = [sys.executable, "-m", "pip", "install"] - if force: - cmd.append("--force-reinstall") - cmd.append(package) - - subprocess.run(cmd, check=True) - - -def _install_npm(tool: dict, version: str, force: bool): - """Install from npm.""" - package = tool["package"] - if version: - package = f"{package}@{version}" - - cmd = ["npm", "install", "-g"] - if force: - cmd.append("--force") - cmd.append(package) - - subprocess.run(cmd, check=True) - - -def _install_cargo(tool: dict, version: str, force: bool): - """Install from cargo (build from source).""" - package = tool.get("cargo", tool["package"]) - - cmd = ["cargo", "install"] - if force: - cmd.append("--force") - if version: - cmd.extend(["--version", version]) - cmd.append(package) - - subprocess.run(cmd, check=True) - - -def _install_github_release(tool: dict, version: str, force: bool): - """Install pre-built binary from GitHub releases.""" - import tarfile - import zipfile - import tempfile - import urllib.request - - repo = tool["repo"] - binary = tool["binary"] - - # Get latest release if no version specified - if not version: - api_url = f"https://api.github.com/repos/{repo}/releases/latest" - with urllib.request.urlopen(api_url) as response: # noqa: S310 - import json - - data = json.loads(response.read()) - version = data["tag_name"].lstrip("v") - - # Determine asset name - arch = get_arch() - os_name = get_os() - - # Common patterns for release assets - patterns = [ - f"{binary}-{version}-{arch}-{os_name}", - f"{binary}-{arch}-{os_name}", - f"{binary}-{os_name}-{arch}", - ] - - # Get release assets - api_url = f"https://api.github.com/repos/{repo}/releases/tags/v{version}" - try: - with urllib.request.urlopen(api_url) as response: # noqa: S310 - import json - - data = json.loads(response.read()) - except Exception: - api_url = f"https://api.github.com/repos/{repo}/releases/tags/{version}" - with urllib.request.urlopen(api_url) as response: # noqa: S310 - import json - - data = json.loads(response.read()) - - # Find matching asset - download_url = None - for asset in data.get("assets", []): - name = asset["name"].lower() - for pattern in patterns: - if pattern.lower() in name: - download_url = asset["browser_download_url"] - break - if download_url: - break - - if not download_url: - console.print( - f"[yellow]No pre-built binary found, building from source...[/yellow]" - ) - _install_cargo(tool, version, force) - return - - # Download and extract - install_dir = get_install_dir() - install_dir.mkdir(parents=True, exist_ok=True) - - with tempfile.TemporaryDirectory() as tmpdir: - tmppath = Path(tmpdir) - archive_path = tmppath / "archive" - - console.print(f" Downloading from {download_url}...") - urllib.request.urlretrieve(download_url, archive_path) # noqa: S310 - - # Extract - if download_url.endswith(".tar.gz") or download_url.endswith(".tgz"): - with tarfile.open(archive_path, "r:gz") as tar: - tar.extractall(tmppath) # noqa: S202 - elif download_url.endswith(".zip"): - with zipfile.ZipFile(archive_path, "r") as z: - z.extractall(tmppath) # noqa: S202 - else: - # Assume it's a raw binary - shutil.copy(archive_path, install_dir / binary) - if sys.platform != "win32": - os.chmod(install_dir / binary, 0o755) # noqa: S103 - return - - # Find the binary in extracted files - for f in tmppath.rglob("*"): - if f.is_file() and f.name == binary: - shutil.copy(f, install_dir / binary) - if sys.platform != "win32": - os.chmod(install_dir / binary, 0o755) # noqa: S103 - break - - ensure_path_configured() - - -def _install_docker(tool: dict, version: str, force: bool): - """Pull Docker image.""" - image = tool["image"] - if version: - image = image.replace(":latest", f":{version}") - - subprocess.run(["docker", "pull", image], check=True) - - -@install_group.command(name="bundle") -@click.argument("bundle_name") -@click.option("--force", "-f", is_flag=True, help="Force reinstall") -def install_bundle(bundle_name: str, force: bool): - """Install a bundle of tools. - - \b - Bundles: - minimal - Just the Python CLI - python - All Python tools (cli, mcp, agents, ai) - rust - All Rust tools (node, dev, mcp-rs) - javascript - All JS tools (cli-js, mcp-js) - full - Everything - dev - Development tools (cli, mcp, dev) - cloud - Cloud deployment tools - """ - if bundle_name not in BUNDLES: - console.print(f"[red]Unknown bundle: {bundle_name}[/red]") - console.print(f"Available: {', '.join(BUNDLES.keys())}") - return - - tools = BUNDLES[bundle_name] - console.print(f"[cyan]Installing bundle '{bundle_name}': {', '.join(tools)}[/cyan]") - console.print() - - for tool_id in tools: - _install_single_tool(tool_id, None, False, force) - console.print() - - -@install_group.command(name="update") -@click.option("--tool", "-t", help="Update specific tool") -def install_update(tool: str): - """Update installed tools.""" - if tool: - if tool not in TOOLS: - console.print(f"[red]Unknown tool: {tool}[/red]") - return - _install_single_tool(tool, None, False, True) - else: - console.print("[cyan]Updating all installed tools...[/cyan]") - for tool_id, tool_info in TOOLS.items(): - if binary := tool_info.get("binary"): - if shutil.which(binary): - _install_single_tool(tool_id, None, False, True) - - -@install_group.command(name="uninstall") -@click.argument("tool_name") -def install_uninstall(tool_name: str): - """Uninstall a tool.""" - if tool_name not in TOOLS: - console.print(f"[red]Unknown tool: {tool_name}[/red]") - return - - tool = TOOLS[tool_name] - - try: - if tool["source"] == "pypi": - cmd = [sys.executable, "-m", "pip", "uninstall", "-y", tool["package"]] - subprocess.run(cmd, check=True) - elif tool["source"] == "npm": - subprocess.run(["npm", "uninstall", "-g", tool["package"]], check=True) - elif tool["source"] == "github": - # Remove binary - install_dir = get_install_dir() - binary_path = install_dir / tool["binary"] - if binary_path.exists(): - binary_path.unlink() - - console.print(f"[green]โœ“[/green] {tool['name']} uninstalled") - except Exception as e: - console.print(f"[red]Failed to uninstall: {e}[/red]") - - -@install_group.command(name="script") -@click.option("--output", "-o", help="Output file (default: stdout)") -def install_script(output: str): - """Generate install.sh script for quick installation. - - \b - Usage: - curl -fsSL https://hanzo.sh/install | bash - hanzo install script > install.sh - """ - script = """#!/usr/bin/env bash -# Hanzo AI - Universal Installer -# https://hanzo.ai -# -# Usage: -# curl -fsSL https://hanzo.sh/install | bash -# curl -fsSL https://hanzo.sh/install | bash -s -- --bundle rust -# HANZO_PREFER_RUST=1 curl -fsSL https://hanzo.sh/install | bash - -set -euo pipefail - -# Colors -RED='\\033[0;31m' -GREEN='\\033[0;32m' -CYAN='\\033[0;36m' -NC='\\033[0m' - -info() { echo -e "${CYAN}$1${NC}"; } -success() { echo -e "${GREEN}โœ“ $1${NC}"; } -error() { echo -e "${RED}โœ— $1${NC}"; exit 1; } - -# Configuration -BUNDLE="${HANZO_BUNDLE:-minimal}" -INSTALL_DIR="${HANZO_INSTALL_DIR:-$HOME/.hanzo/bin}" -PREFER_RUST="${HANZO_PREFER_RUST:-0}" - -# Parse args -while [[ $# -gt 0 ]]; do - case $1 in - --bundle) BUNDLE="$2"; shift 2 ;; - --rust) PREFER_RUST=1; shift ;; - --dir) INSTALL_DIR="$2"; shift 2 ;; - *) shift ;; - esac -done - -info "Hanzo AI Installer" -info "==================" -echo "" -info "Bundle: $BUNDLE" -info "Install dir: $INSTALL_DIR" -echo "" - -# Create install directory -mkdir -p "$INSTALL_DIR" - -# Detect OS and arch -OS=$(uname -s | tr '[:upper:]' '[:lower:]') -ARCH=$(uname -m) - -case "$ARCH" in - x86_64|amd64) ARCH="x86_64" ;; - arm64|aarch64) ARCH="aarch64" ;; - *) error "Unsupported architecture: $ARCH" ;; -esac - -case "$OS" in - darwin) OS_NAME="apple-darwin" ;; - linux) OS_NAME="unknown-linux-gnu" ;; - *) error "Unsupported OS: $OS" ;; -esac - -# Check for uv/uvx -install_uv() { - if ! command -v uv &> /dev/null; then - info "Installing uv (Python package manager)..." - curl -LsSf https://astral.sh/uv/install.sh | sh - export PATH="$HOME/.cargo/bin:$PATH" - fi - success "uv available" -} - -# Install Python tools -install_python() { - install_uv - - info "Installing Python tools..." - - case "$BUNDLE" in - minimal|python|full|dev|cloud) - uv pip install hanzo[all] - success "hanzo CLI installed" - ;; - esac - - case "$BUNDLE" in - python|full|dev|cloud) - uv pip install hanzo-mcp[tools-all] - success "hanzo-mcp installed" - ;; - esac - - case "$BUNDLE" in - python|full) - uv pip install hanzo-agents - success "hanzo-agents installed" - ;; - esac -} - -# Install Rust tools from GitHub releases -install_rust_binary() { - local repo="$1" - local binary="$2" - local version="${3:-latest}" - - info "Installing $binary..." - - if [[ "$version" == "latest" ]]; then - version=$(curl -s "https://api.github.com/repos/$repo/releases/latest" | grep '"tag_name"' | cut -d'"' -f4) - fi - - local url="https://github.com/$repo/releases/download/$version/${binary}-${ARCH}-${OS_NAME}.tar.gz" - - if curl -fsSL "$url" -o "/tmp/${binary}.tar.gz" 2>/dev/null; then - tar -xzf "/tmp/${binary}.tar.gz" -C "$INSTALL_DIR" - chmod +x "$INSTALL_DIR/$binary" - success "$binary installed" - else - info "Pre-built binary not found, building from source..." - if command -v cargo &> /dev/null; then - cargo install --git "https://github.com/$repo" - success "$binary installed (from source)" - else - error "Cargo not found. Install Rust: https://rustup.rs" - fi - fi -} - -# Install Rust tools -install_rust() { - case "$BUNDLE" in - rust|full) - install_rust_binary "hanzoai/node" "hanzo-node" - install_rust_binary "hanzoai/dev" "hanzo-dev" - ;; - esac - - case "$BUNDLE" in - dev) - install_rust_binary "hanzoai/dev" "hanzo-dev" - ;; - esac -} - -# Add to PATH -configure_path() { - local shell_rc="" - - case "$SHELL" in - */zsh) shell_rc="$HOME/.zshrc" ;; - */bash) shell_rc="$HOME/.bashrc" ;; - *) shell_rc="$HOME/.profile" ;; - esac - - if ! grep -q ".hanzo/bin" "$shell_rc" 2>/dev/null; then - echo 'export PATH="$HOME/.hanzo/bin:$PATH"' >> "$shell_rc" - info "Added ~/.hanzo/bin to PATH in $shell_rc" - fi -} - -# Main -main() { - install_python - - if [[ "$PREFER_RUST" == "1" ]] || [[ "$BUNDLE" == "rust" ]] || [[ "$BUNDLE" == "full" ]]; then - install_rust - fi - - configure_path - - echo "" - success "Hanzo AI installed successfully!" - echo "" - info "Run: source ~/.zshrc # or restart your terminal" - info "Then: hanzo --help" -} - -main "$@" -""" - - if output: - with open(output, "w") as f: - f.write(script) - if sys.platform != "win32": - os.chmod(output, 0o755) # noqa: S103 - console.print(f"[green]โœ“[/green] Install script written to {output}") - else: - console.print(script) - - -@install_group.command(name="doctor") -def install_doctor(): - """Check installation health and dependencies.""" - console.print("[cyan]Hanzo Installation Health Check[/cyan]") - console.print() - - checks = [ - ("Python", "python3 --version", "3.12+"), - ("uv", "uv --version", "0.4+"), - ("Node.js", "node --version", "18+"), - ("npm", "npm --version", "9+"), - ("Rust", "rustc --version", "1.75+"), - ("Cargo", "cargo --version", "1.75+"), - ("Docker", "docker --version", "24+"), - ("Git", "git --version", "2.30+"), - ] - - for name, cmd, required in checks: - try: - result = subprocess.run( - cmd.split(), capture_output=True, text=True, timeout=5 - ) - if result.returncode == 0: - version = result.stdout.strip().split()[-1] - console.print(f" [green]โœ“[/green] {name}: {version}") - else: - console.print(f" [yellow]![/yellow] {name}: not found (optional)") - except Exception: - console.print(f" [yellow]![/yellow] {name}: not found") - - console.print() - - # Check Hanzo tools - console.print("[cyan]Hanzo Tools:[/cyan]") - for tool_id, tool in TOOLS.items(): - if binary := tool.get("binary"): - if shutil.which(binary): - console.print(f" [green]โœ“[/green] {tool['name']} ({binary})") - else: - console.print(f" [dim]โ—‹[/dim] {tool['name']} (not installed)") - - console.print() - - # Check PATH - install_dir = get_install_dir() - if str(install_dir) in os.environ.get("PATH", ""): - console.print(f"[green]โœ“[/green] {install_dir} is in PATH") - else: - console.print(f"[yellow]![/yellow] {install_dir} is NOT in PATH") - console.print(f' Add to your shell config: export PATH="{install_dir}:$PATH"') - - -# ============================================================================ -# IDE / Browser / AI App Integration -# ============================================================================ - -# MCP config paths for various apps -MCP_CONFIG_PATHS = { - "claude": { - "macos": "~/Library/Application Support/Claude/claude_desktop_config.json", - "linux": "~/.config/claude/claude_desktop_config.json", - "windows": "%APPDATA%/Claude/claude_desktop_config.json", - }, - "vscode": { - "macos": "~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json", - "linux": "~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json", - "windows": "%APPDATA%/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json", - }, - "cursor": { - "macos": "~/Library/Application Support/Cursor/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json", - "linux": "~/.config/Cursor/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json", - "windows": "%APPDATA%/Cursor/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json", - }, - "antigravity": { - "macos": "~/.gemini/antigravity/mcp_config.json", - "linux": "~/.gemini/antigravity/mcp_config.json", - "windows": "%USERPROFILE%/.gemini/antigravity/mcp_config.json", - }, - "copilot": { - "macos": "~/.copilot/mcp-config.json", - "linux": "~/.copilot/mcp-config.json", - "windows": "%USERPROFILE%/.copilot/mcp-config.json", - }, - "jan": { - "macos": "~/Library/Application Support/Jan/data/mcp_config.json", - "linux": "~/.config/jan/data/mcp_config.json", - "windows": "%APPDATA%/Jan/data/mcp_config.json", - }, - "trae": { - "macos": "~/Library/Application Support/Trae/User/mcp.json", - "linux": "~/.config/Trae/User/mcp.json", - "windows": "%APPDATA%/Trae/User/mcp.json", - }, - "5ire": { - "macos": "~/Library/Application Support/5ire/mcp.json", - "linux": "~/.config/5ire/mcp.json", - "windows": "%APPDATA%/5ire/mcp.json", - }, -} - - -def get_mcp_config_path(app: str) -> Optional[Path]: - """Get MCP config path for an app.""" - if app not in MCP_CONFIG_PATHS: - return None - - system = platform.system().lower() - os_key = {"darwin": "macos", "linux": "linux", "windows": "windows"}.get(system) - if not os_key: - return None - - path_str = MCP_CONFIG_PATHS[app].get(os_key) - if not path_str: - return None - - return Path(os.path.expandvars(os.path.expanduser(path_str))) - - -def get_mcp_server_config() -> dict: - """Get hanzo-mcp server configuration for MCP config files.""" - return {"command": "uvx", "args": ["--upgrade", "hanzo-mcp@latest"], "env": {}} - - -@install_group.command(name="ide") -@click.argument("target", required=False) -@click.option("--all", "install_all", is_flag=True, help="Install to all detected IDEs") -@click.option("--list", "list_only", is_flag=True, help="List available IDEs") -def install_ide(target: str, install_all: bool, list_only: bool): - """Install Hanzo MCP to IDEs (VS Code, Cursor, Antigravity, etc.). - - \b - Examples: - hanzo install ide # List detected IDEs - hanzo install ide claude # Install to Claude Desktop - hanzo install ide vscode # Install to VS Code - hanzo install ide --all # Install to all detected IDEs - - \b - Supported IDEs: - claude - Claude Desktop - vscode - Visual Studio Code (via Cline extension) - cursor - Cursor IDE (via Cline extension) - antigravity - Antigravity IDE (VS Code based) - copilot - GitHub Copilot - jan - Jan AI - trae - Trae IDE - 5ire - 5ire AI - """ - import json - - if list_only or (not target and not install_all): - # List detected IDEs - console.print("[bold]Detected IDEs with MCP support:[/bold]") - console.print() - - for app_name in MCP_CONFIG_PATHS: - config_path = get_mcp_config_path(app_name) - if config_path: - exists = config_path.exists() - parent_exists = config_path.parent.exists() - - if exists: - # Check if hanzo-mcp already configured - try: - with open(config_path) as f: - config = json.load(f) - has_hanzo = "hanzo" in config.get( - "mcpServers", {} - ) or "hanzo-mcp" in config.get("mcpServers", {}) - status = ( - "[green]โœ“ hanzo-mcp configured[/green]" - if has_hanzo - else "[yellow]โ—‹ no hanzo-mcp[/yellow]" - ) - except Exception: - status = "[dim]โ—‹ config exists[/dim]" - console.print(f" [green]โœ“[/green] {app_name}: {status}") - elif parent_exists: - console.print( - f" [yellow]โ—‹[/yellow] {app_name}: [dim]app installed, no MCP config[/dim]" - ) - else: - console.print( - f" [dim]โ—‹[/dim] {app_name}: [dim]not installed[/dim]" - ) - - console.print() - console.print("[dim]Run: hanzo install ide to configure[/dim]") - return - - targets = list(MCP_CONFIG_PATHS.keys()) if install_all else [target] - - for app in targets: - if app not in MCP_CONFIG_PATHS: - console.print(f"[red]Unknown IDE: {app}[/red]") - console.print(f"Available: {', '.join(MCP_CONFIG_PATHS.keys())}") - continue - - config_path = get_mcp_config_path(app) - if not config_path: - console.print(f"[yellow]![/yellow] {app}: not supported on this platform") - continue - - console.print(f"[cyan]Configuring {app}...[/cyan]") - - # Create parent directory if needed - config_path.parent.mkdir(parents=True, exist_ok=True) - - # Load or create config - if config_path.exists(): - try: - with open(config_path) as f: - config = json.load(f) - except json.JSONDecodeError: - config = {} - else: - config = {} - - # Ensure mcpServers key exists - if "mcpServers" not in config: - config["mcpServers"] = {} - - # Add hanzo-mcp server - config["mcpServers"]["hanzo"] = get_mcp_server_config() - - # Write config - with open(config_path, "w") as f: - json.dump(config, f, indent=2) - - console.print(f" [green]โœ“[/green] {app} configured: {config_path}") - - console.print() - console.print("[bold green]โœ“[/bold green] MCP configuration complete") - console.print("[dim]Restart your IDE to load hanzo-mcp[/dim]") - - -@install_group.command(name="browser") -@click.argument("browser", required=False) -@click.option( - "--list", "list_only", is_flag=True, help="List browser extension install URLs" -) -def install_browser(browser: str, list_only: bool): - """Install Hanzo browser extension. - - \b - Examples: - hanzo install browser # List browser extension links - hanzo install browser chrome # Open Chrome Web Store - hanzo install browser firefox # Open Firefox Add-ons - - \b - Supported browsers: - chrome - Google Chrome / Chromium - firefox - Mozilla Firefox - safari - Apple Safari - edge - Microsoft Edge - """ - import webbrowser - - extension_urls = { - "chrome": "https://chrome.google.com/webstore/detail/hanzo-ai/placeholder", - "firefox": "https://addons.mozilla.org/en-US/firefox/addon/hanzo-ai/", - "safari": "https://apps.apple.com/app/hanzo-ai/placeholder", - "edge": "https://microsoftedge.microsoft.com/addons/detail/hanzo-ai/placeholder", - } - - # Dev install instructions - dev_instructions = { - "chrome": "chrome://extensions โ†’ Enable Developer Mode โ†’ Load unpacked โ†’ Select extension folder", - "firefox": "about:debugging โ†’ This Firefox โ†’ Load Temporary Add-on โ†’ Select manifest.json", - "safari": "Safari โ†’ Preferences โ†’ Advanced โ†’ Enable Develop menu โ†’ Develop โ†’ Allow Unsigned Extensions", - "edge": "edge://extensions โ†’ Enable Developer Mode โ†’ Load unpacked โ†’ Select extension folder", - } - - if list_only or not browser: - console.print("[bold]Hanzo Browser Extension[/bold]") - console.print() - - # Check which browsers are available - browsers_found = [] - - # macOS browser detection - if platform.system() == "Darwin": - browser_paths = { - "chrome": "/Applications/Google Chrome.app", - "firefox": "/Applications/Firefox.app", - "safari": "/Applications/Safari.app", - "edge": "/Applications/Microsoft Edge.app", - } - for name, path in browser_paths.items(): - if Path(path).exists(): - browsers_found.append(name) - - console.print("[cyan]Install from store (recommended):[/cyan]") - for name, url in extension_urls.items(): - detected = " [green](detected)[/green]" if name in browsers_found else "" - console.print(f" {name}: {url}{detected}") - - console.print() - console.print("[cyan]Developer install (for testing):[/cyan]") - for name, instruction in dev_instructions.items(): - console.print(f" {name}:") - console.print(f" {instruction}") - - console.print() - console.print("[dim]Extension source: ~/work/hanzo/extension[/dim]") - return - - if browser.lower() not in extension_urls: - console.print(f"[red]Unknown browser: {browser}[/red]") - console.print(f"Available: {', '.join(extension_urls.keys())}") - return - - url = extension_urls[browser.lower()] - console.print(f"[cyan]Opening {browser} extension page...[/cyan]") - webbrowser.open(url) - console.print(f"[green]โœ“[/green] Opened: {url}") - - -@install_group.command(name="ai") -@click.argument("app", required=False) -@click.option("--list", "list_only", is_flag=True, help="List AI apps") -def install_ai(app: str, list_only: bool): - """Configure Hanzo MCP for AI apps (Claude Desktop, Jan, etc.). - - This is an alias for 'hanzo install ide' focused on AI applications. - - \b - Examples: - hanzo install ai # List AI apps - hanzo install ai claude # Configure Claude Desktop - hanzo install ai jan # Configure Jan AI - """ - # Delegate to install_ide - from click import Context - - ctx = Context(install_ide) - ctx.invoke( - install_ide, target=app, install_all=False, list_only=list_only or not app - ) - - -@install_group.command(name="all") -@click.option("--force", "-f", is_flag=True, help="Force reinstall") -def install_all_cmd(force: bool): - """Install everything: tools + IDE integrations + browser extension info. - - \b - This command: - 1. Installs all Python tools (cli, mcp, agents) - 2. Configures all detected IDEs with hanzo-mcp - 3. Shows browser extension installation instructions - """ - console.print( - Panel.fit( - "[bold cyan]Hanzo Full Installation[/bold cyan]\n" - "[dim]Installing tools, IDE integrations, and browser extension[/dim]", - border_style="cyan", - ) - ) - console.print() - - # 1. Install Python tools - console.print("[bold]1. Installing CLI Tools[/bold]") - from click import Context - - for tool_id in ["cli", "mcp", "agents"]: - _install_single_tool(tool_id, None, False, force) - - console.print() - - # 2. Configure all detected IDEs - console.print("[bold]2. Configuring IDE Integrations[/bold]") - import json - - configured = [] - for app_name in MCP_CONFIG_PATHS: - config_path = get_mcp_config_path(app_name) - if config_path and config_path.parent.exists(): - # Create or update config - config_path.parent.mkdir(parents=True, exist_ok=True) - - if config_path.exists(): - try: - with open(config_path) as f: - config = json.load(f) - except Exception: - config = {} - else: - config = {} - - if "mcpServers" not in config: - config["mcpServers"] = {} - - config["mcpServers"]["hanzo"] = get_mcp_server_config() - - with open(config_path, "w") as f: - json.dump(config, f, indent=2) - - configured.append(app_name) - console.print(f" [green]โœ“[/green] {app_name}") - - if not configured: - console.print(" [dim](no IDEs detected)[/dim]") - - console.print() - - # 3. Browser extension info - console.print("[bold]3. Browser Extension[/bold]") - console.print(" Install from: https://chrome.google.com/webstore/detail/hanzo-ai/") - console.print(" Or load unpacked from: ~/work/hanzo/extension") - - console.print() - console.print("[bold green]โœ“[/bold green] Full installation complete!") - console.print() - console.print("[dim]Next steps:[/dim]") - console.print(" 1. Restart your IDEs to load hanzo-mcp") - console.print(" 2. Install browser extension (optional)") - console.print(" 3. Run: hanzo doctor # to verify installation") - - -# ============================================================================ -# Nanobrowser - Lightweight Agent Browser -# ============================================================================ - -NANOBROWSER_PATH = Path.home() / "work" / "nanobrowser" / "nanobrowser" - - -@install_group.command(name="nanobrowser") -@click.option("--build", "-b", is_flag=True, help="Build from source") -@click.option("--dev", is_flag=True, help="Run in development mode") -@click.option( - "--open", "open_browser", is_flag=True, help="Open Chrome with extension loaded" -) -def install_nanobrowser(build: bool, dev: bool, open_browser: bool): - """Install/build Nanobrowser - lightweight AI browser for agents. - - \b - Nanobrowser is a Chrome extension for AI web automation. - It provides a multi-agent system (Planner + Navigator) that - can browse the web autonomously using LLMs. - - \b - Examples: - hanzo install nanobrowser # Check status - hanzo install nanobrowser --build # Build from source - hanzo install nanobrowser --dev # Run dev server - hanzo install nanobrowser --open # Load in Chrome - - \b - LLM Support: - - OpenAI (GPT-4, GPT-4o) - - Anthropic (Claude Sonnet, Haiku) - - Google (Gemini) - - Ollama (local models) - - Groq, Cerebras, Llama - """ - nanobrowser_dir = NANOBROWSER_PATH - - if not nanobrowser_dir.exists(): - console.print("[red]Nanobrowser not found[/red]") - console.print(f"Expected at: {nanobrowser_dir}") - console.print() - console.print("Clone it with:") - console.print( - f" git clone https://github.com/nanobrowser/nanobrowser {nanobrowser_dir}" - ) - return - - dist_dir = nanobrowser_dir / "dist" - - if build or dev: - console.print("[cyan]Building Nanobrowser...[/cyan]") - - # Install dependencies - console.print(" Installing dependencies...") - result = subprocess.run( - ["pnpm", "install"], - cwd=str(nanobrowser_dir), - capture_output=True, - text=True, - ) - if result.returncode != 0: - console.print(f"[red]Failed to install dependencies: {result.stderr}[/red]") - return - - if dev: - console.print(" Starting dev server...") - console.print("[dim]Press Ctrl+C to stop[/dim]") - subprocess.run(["pnpm", "dev"], cwd=str(nanobrowser_dir)) - else: - console.print(" Building extension...") - result = subprocess.run( - ["pnpm", "build"], - cwd=str(nanobrowser_dir), - capture_output=True, - text=True, - ) - if result.returncode != 0: - console.print(f"[red]Build failed: {result.stderr}[/red]") - return - console.print(f"[green]โœ“[/green] Built to: {dist_dir}") - - elif open_browser: - if not dist_dir.exists(): - console.print( - "[yellow]Extension not built yet. Run with --build first.[/yellow]" - ) - return - - # Open Chrome with extension loaded (macOS) - if platform.system() == "Darwin": - chrome_path = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" - if not Path(chrome_path).exists(): - console.print("[red]Chrome not found at default location[/red]") - return - - console.print("[cyan]Opening Chrome with Nanobrowser extension...[/cyan]") - subprocess.Popen( - [ - chrome_path, - f"--load-extension={dist_dir}", - "--new-window", - ] - ) - console.print("[green]โœ“[/green] Chrome launched with Nanobrowser") - else: - console.print("[yellow]Auto-open not supported on this platform[/yellow]") - console.print(f"Manually load the extension from: {dist_dir}") - - else: - # Status check - console.print("[bold]Nanobrowser Status[/bold]") - console.print(f" Location: {nanobrowser_dir}") - console.print( - f" Built: {'[green]โœ“[/green]' if dist_dir.exists() else '[yellow]โ—‹[/yellow] (run --build)'}" - ) - - # Get version from package.json - package_json = nanobrowser_dir / "package.json" - if package_json.exists(): - import json - - with open(package_json) as f: - pkg = json.load(f) - console.print(f" Version: {pkg.get('version', 'unknown')}") - - console.print() - console.print("[cyan]Installation Steps:[/cyan]") - console.print(" 1. Build: hanzo install nanobrowser --build") - console.print(" 2. Open Chrome: chrome://extensions") - console.print(" 3. Enable 'Developer mode'") - console.print(" 4. Click 'Load unpacked'") - console.print(f" 5. Select: {dist_dir}") - console.print() - console.print("[cyan]Or auto-load:[/cyan]") - console.print(" hanzo install nanobrowser --open") - - -# ============================================================================ -# Runtime - Sandboxed Agent Execution Environment -# ============================================================================ - -RUNTIME_PATH = Path.home() / "work" / "hanzo" / "runtime" - - -@install_group.command(name="runtime") -@click.option("--setup", "-s", is_flag=True, help="Set up the runtime environment") -@click.option( - "--sdk", type=click.Choice(["python", "typescript", "both"]), help="Install SDK" -) -@click.option( - "--computer-use", is_flag=True, help="Set up VNC desktop for computer use" -) -def install_runtime(setup: bool, sdk: str, computer_use: bool): - """Configure Hanzo Runtime - sandboxed agent execution. - - \b - The Hanzo Runtime provides: - - Sub-90ms sandbox creation - - Isolated code execution for AI-generated code - - Computer Use (VNC desktop control) - - File, Git, LSP, and Execute APIs - - OCI/Docker compatibility - - \b - Examples: - hanzo install runtime # Check status - hanzo install runtime --setup # Initialize runtime - hanzo install runtime --sdk python # Install Python SDK - hanzo install runtime --computer-use # Set up desktop control - - \b - SDKs: - pip install hanzo-runtime # Python - npm install @hanzo/runtime # TypeScript - """ - runtime_dir = RUNTIME_PATH - - if sdk: - console.print(f"[cyan]Installing Runtime SDK ({sdk})...[/cyan]") - - if sdk in ("python", "both"): - if shutil.which("uv"): - subprocess.run(["uv", "pip", "install", "hanzo-runtime"], check=True) - else: - subprocess.run( - [sys.executable, "-m", "pip", "install", "hanzo-runtime"], - check=True, - ) - console.print("[green]โœ“[/green] Python SDK installed") - - if sdk in ("typescript", "both"): - subprocess.run(["npm", "install", "-g", "@hanzo/runtime"], check=True) - console.print("[green]โœ“[/green] TypeScript SDK installed") - - return - - if computer_use: - console.print("[cyan]Setting up Computer Use environment...[/cyan]") - console.print() - - # Check Docker - if not shutil.which("docker"): - console.print("[red]Docker is required for Computer Use[/red]") - console.print("Install: https://docs.docker.com/get-docker/") - return - - # Show Dockerfile info - dockerfile_path = runtime_dir / "hack" / "computer-use" / "Dockerfile" - if dockerfile_path.exists(): - console.print("[green]โœ“[/green] Computer Use Dockerfile found") - console.print(f" Path: {dockerfile_path}") - else: - console.print("[yellow]![/yellow] Dockerfile not found") - - console.print() - console.print("[cyan]Computer Use provides:[/cyan]") - console.print(" - Xvfb (virtual display)") - console.print(" - XFCE4 desktop environment") - console.print(" - x11vnc (VNC server on port 5901)") - console.print(" - noVNC (web access on port 6901)") - console.print(" - xdotool, xautomation (mouse/keyboard)") - console.print(" - Chromium browser") - console.print() - console.print("[cyan]Build and run:[/cyan]") - console.print(f" cd {runtime_dir / 'hack' / 'computer-use'}") - console.print(" docker build -t hanzo-computer-use .") - console.print(" docker run -p 5901:5901 -p 6901:6901 hanzo-computer-use") - console.print() - console.print("Then access: http://localhost:6901 (noVNC web client)") - return - - if setup: - console.print("[cyan]Setting up Hanzo Runtime...[/cyan]") - - if not runtime_dir.exists(): - console.print(f"[red]Runtime not found at: {runtime_dir}[/red]") - return - - # Build Go binaries - console.print(" Building CLI...") - cli_dir = runtime_dir / "apps" / "cli" - if cli_dir.exists(): - result = subprocess.run( - [ - "go", - "build", - "-o", - str(Path.home() / ".hanzo" / "bin" / "hanzo-runtime"), - ".", - ], - cwd=str(cli_dir), - capture_output=True, - text=True, - ) - if result.returncode == 0: - console.print("[green]โœ“[/green] CLI built") - else: - console.print(f"[yellow]![/yellow] CLI build failed: {result.stderr}") - - return - - # Status check - console.print("[bold]Hanzo Runtime Status[/bold]") - - if runtime_dir.exists(): - console.print(f" [green]โœ“[/green] Source: {runtime_dir}") - - # Check components - components = { - "CLI": runtime_dir / "apps" / "cli", - "Daemon": runtime_dir / "apps" / "daemon", - "Runner": runtime_dir / "apps" / "runner", - "Python SDK": runtime_dir / "libs" / "sdk-python", - "TypeScript SDK": runtime_dir / "libs" / "sdk-typescript", - "Computer Use": runtime_dir / "libs" / "computer-use", - } - - console.print() - console.print("[cyan]Components:[/cyan]") - for name, path in components.items(): - status = "[green]โœ“[/green]" if path.exists() else "[dim]โ—‹[/dim]" - console.print(f" {status} {name}") - - # Check SDKs installed - console.print() - console.print("[cyan]Installed SDKs:[/cyan]") - - # Python SDK - try: - result = subprocess.run( - [ - sys.executable, - "-c", - "import hanzo_runtime; print(hanzo_runtime.__version__)", - ], - capture_output=True, - text=True, - ) - if result.returncode == 0: - console.print(f" [green]โœ“[/green] Python: {result.stdout.strip()}") - else: - console.print(" [dim]โ—‹[/dim] Python: not installed") - except Exception: - console.print(" [dim]โ—‹[/dim] Python: not installed") - - # TypeScript SDK - result = subprocess.run( - ["npm", "list", "-g", "@hanzo/runtime"], capture_output=True, text=True - ) - if "@hanzo/runtime" in result.stdout: - console.print(f" [green]โœ“[/green] TypeScript: installed") - else: - console.print(" [dim]โ—‹[/dim] TypeScript: not installed") - - else: - console.print(f" [red]โ—‹[/red] Not found: {runtime_dir}") - - console.print() - console.print("[dim]Install SDKs:[/dim]") - console.print(" pip install hanzo-runtime") - console.print(" npm install @hanzo/runtime") - - -# ============================================================================ -# Cas Infrastructure (Casdoor, Casibase, Casvisor) -# ============================================================================ - -CAS_PATH = Path.home() / "work" / "cas" - - -@install_group.command(name="cas") -@click.argument("component", required=False) -@click.option("--setup", "-s", is_flag=True, help="Set up the component") -@click.option("--start", is_flag=True, help="Start the service") -def install_cas(component: str, setup: bool, start: bool): - """Configure Cas infrastructure (auth, AI platform, VM management). - - \b - Components: - casdoor - Authentication/IAM system (Single Sign-On) - casibase - AI Cloud OS with MCP/A2A support - casvisor - Cloud operating system (VM/machine management) - - \b - Examples: - hanzo install cas # Check status - hanzo install cas casdoor --setup # Set up Casdoor - hanzo install cas casibase --start # Start Casibase - - \b - Casibase Features: - - AI knowledge base management - - MCP (Model Context Protocol) server - - A2A (Agent-to-Agent) management - - Supports ChatGPT, Claude, Llama, Ollama, etc. - """ - cas_dir = CAS_PATH - - components_info = { - "casdoor": { - "name": "Casdoor", - "description": "Authentication/IAM - Single Sign-On", - "path": cas_dir / "casdoor", - "port": 8000, - "docs": "https://casdoor.org", - }, - "casibase": { - "name": "Casibase", - "description": "AI Cloud OS - Knowledge base + MCP/A2A", - "path": cas_dir / "casibase", - "port": 14000, - "docs": "https://casibase.org", - }, - "casvisor": { - "name": "Casvisor", - "description": "Cloud OS - VM/Machine management", - "path": cas_dir / "casvisor", - "port": 16001, - "docs": "https://casvisor.org", - }, - } - - if component: - if component not in components_info: - console.print(f"[red]Unknown component: {component}[/red]") - console.print(f"Available: {', '.join(components_info.keys())}") - return - - info = components_info[component] - comp_path = info["path"] - - if not comp_path.exists(): - console.print(f"[red]{info['name']} not found at: {comp_path}[/red]") - console.print( - f"Clone: git clone https://github.com/{component}/{component} {comp_path}" - ) - return - - if setup: - console.print(f"[cyan]Setting up {info['name']}...[/cyan]") - console.print() - - # Check for Go - if not shutil.which("go"): - console.print("[red]Go is required[/red]") - console.print("Install: https://go.dev/dl/") - return - - # Build - console.print(" Building...") - result = subprocess.run( - ["go", "build", "."], cwd=str(comp_path), capture_output=True, text=True - ) - if result.returncode == 0: - console.print(f"[green]โœ“[/green] {info['name']} built") - else: - console.print(f"[red]Build failed: {result.stderr}[/red]") - return - - # Frontend - web_dir = comp_path / "web" - if web_dir.exists(): - console.print(" Building frontend...") - subprocess.run(["yarn", "install"], cwd=str(web_dir), check=False) - subprocess.run(["yarn", "build"], cwd=str(web_dir), check=False) - - console.print() - console.print(f"[green]โœ“[/green] {info['name']} setup complete") - console.print(f"Run: cd {comp_path} && ./{component}") - - elif start: - console.print(f"[cyan]Starting {info['name']}...[/cyan]") - binary = comp_path / component - if not binary.exists(): - console.print(f"[yellow]Not built. Run with --setup first.[/yellow]") - return - subprocess.Popen([str(binary)], cwd=str(comp_path)) - console.print(f"[green]โœ“[/green] Started on port {info['port']}") - console.print(f" Open: http://localhost:{info['port']}") - - else: - console.print(f"[bold]{info['name']}[/bold]") - console.print(f" Description: {info['description']}") - console.print(f" Path: {comp_path}") - console.print(f" Port: {info['port']}") - console.print(f" Docs: {info['docs']}") - - else: - # Status check - console.print("[bold]Cas Infrastructure[/bold]") - console.print() - - for comp_id, info in components_info.items(): - path = info["path"] - exists = path.exists() - binary = path / comp_id if exists else None - built = binary and binary.exists() if binary else False - - status = "[green]โœ“[/green]" if exists else "[dim]โ—‹[/dim]" - build_status = ( - " [green](built)[/green]" - if built - else " [yellow](not built)[/yellow]" if exists else "" - ) - - console.print( - f" {status} {info['name']}: {info['description']}{build_status}" - ) - - console.print() - console.print( - "[dim]Set up a component: hanzo install cas --setup[/dim]" - ) diff --git a/pkg/hanzo/src/hanzo/commands/jobs.py b/pkg/hanzo/src/hanzo/commands/jobs.py deleted file mode 100644 index 2c0fcc42c..000000000 --- a/pkg/hanzo/src/hanzo/commands/jobs.py +++ /dev/null @@ -1,343 +0,0 @@ -"""Hanzo Jobs - Background jobs and cron CLI. - -Job scheduling, execution, and management. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -JOBS_URL = os.getenv("HANZO_JOBS_URL", "https://jobs.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(JOBS_URL, method, path, **kwargs) - - -@click.group(name="jobs") -def jobs_group(): - """Hanzo Jobs - Background jobs and scheduling. - - \b - Jobs: - hanzo jobs run # Run a job - hanzo jobs list # List jobs - hanzo jobs logs # View job logs - hanzo jobs cancel # Cancel a job - hanzo jobs retry # Retry failed job - - \b - Cron: - hanzo jobs cron list # List scheduled jobs - hanzo jobs cron create # Create cron schedule - hanzo jobs cron pause # Pause schedule - hanzo jobs cron resume # Resume schedule - hanzo jobs cron run-now # Trigger immediately - """ - pass - - -# ============================================================================ -# Job Operations -# ============================================================================ - - -@jobs_group.command(name="run") -@click.argument("name") -@click.option("--payload", "-p", help="JSON payload") -@click.option("--queue", "-q", default="default", help="Queue name") -@click.option("--priority", type=int, default=5, help="Priority (1-10)") -@click.option("--delay", "-d", help="Delay before execution (e.g., 5m, 1h)") -@click.option("--wait", "-w", is_flag=True, help="Wait for completion") -def jobs_run( - name: str, payload: str, queue: str, priority: int, delay: str, wait: bool -): - """Run a background job.""" - body = {"name": name, "queue": queue, "priority": priority} - if payload: - body["payload"] = json.loads(payload) - if delay: - body["delay"] = delay - - resp = _request("post", "/v1/jobs", json=body) - data = check_response(resp) - - job_id = data.get("id", data.get("job_id", "-")) - console.print(f"[green]โœ“[/green] Job '{name}' queued") - console.print(f" ID: {job_id}") - console.print(f" Queue: {queue}") - console.print(f" Priority: {priority}") - if delay: - console.print(f" Delay: {delay}") - - if wait: - console.print("[dim]Waiting for completion...[/dim]") - resp = _request("get", f"/v1/jobs/{job_id}/wait", timeout=300) - result = check_response(resp) - status = result.get("status", "unknown") - style = "green" if status == "completed" else "red" - console.print(f" Status: [{style}]{status}[/{style}]") - if result.get("duration_ms"): - console.print(f" Duration: {result['duration_ms']}ms") - - -@jobs_group.command(name="list") -@click.option( - "--status", - "-s", - type=click.Choice(["pending", "active", "completed", "failed", "all"]), - default="all", -) -@click.option("--queue", "-q", help="Filter by queue") -@click.option("--limit", "-n", default=20, help="Max results") -def jobs_list(status: str, queue: str, limit: int): - """List jobs.""" - params = {"limit": limit} - if status != "all": - params["status"] = status - if queue: - params["queue"] = queue - - resp = _request("get", "/v1/jobs", params=params) - data = check_response(resp) - jobs = data.get("jobs", data.get("items", [])) - - table = Table(title="Jobs", box=box.ROUNDED) - table.add_column("ID", style="cyan") - table.add_column("Name", style="white") - table.add_column("Status", style="green") - table.add_column("Queue", style="dim") - table.add_column("Started", style="dim") - table.add_column("Duration", style="dim") - - for j in jobs: - j_status = j.get("status", "unknown") - status_style = { - "pending": "yellow", - "active": "cyan", - "completed": "green", - "failed": "red", - }.get(j_status, "white") - - table.add_row( - str(j.get("id", ""))[:16], - j.get("name", "-"), - f"[{status_style}]{j_status}[/{status_style}]", - j.get("queue", "-"), - str(j.get("started_at", ""))[:19], - f"{j.get('duration_ms', '-')}ms" if j.get("duration_ms") else "-", - ) - - console.print(table) - if not jobs: - console.print("[dim]No jobs found[/dim]") - - -@jobs_group.command(name="logs") -@click.argument("job_id") -@click.option("--follow", "-f", is_flag=True, help="Follow logs") -@click.option("--tail", "-n", default=100, help="Number of lines") -def jobs_logs(job_id: str, follow: bool, tail: int): - """View job logs.""" - params = {"tail": tail} - if follow: - params["follow"] = "true" - - resp = _request("get", f"/v1/jobs/{job_id}/logs", params=params) - data = check_response(resp) - lines = data.get("logs", data.get("lines", [])) - - console.print(f"[cyan]Logs for job {job_id}:[/cyan]") - for line in lines: - if isinstance(line, dict): - ts = str(line.get("timestamp", ""))[:19] - level = line.get("level", "info") - msg = line.get("message", "") - style = ( - "red" if level == "error" else "yellow" if level == "warn" else "dim" - ) - console.print(f"[dim]{ts}[/dim] [{style}]{level}[/{style}] {msg}") - else: - console.print(str(line)) - - if not lines: - console.print("[dim]No logs available[/dim]") - - -@jobs_group.command(name="cancel") -@click.argument("job_id") -@click.option("--force", "-f", is_flag=True, help="Force cancellation") -def jobs_cancel(job_id: str, force: bool): - """Cancel a running job.""" - payload = {} - if force: - payload["force"] = True - - resp = _request("post", f"/v1/jobs/{job_id}/cancel", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Job '{job_id}' cancelled") - - -@jobs_group.command(name="retry") -@click.argument("job_id") -def jobs_retry(job_id: str): - """Retry a failed job.""" - resp = _request("post", f"/v1/jobs/{job_id}/retry") - data = check_response(resp) - new_id = data.get("id", data.get("job_id", "-")) - console.print(f"[green]โœ“[/green] Job '{job_id}' requeued") - console.print(f" New ID: {new_id}") - - -@jobs_group.command(name="describe") -@click.argument("job_id") -def jobs_describe(job_id: str): - """Show job details.""" - resp = _request("get", f"/v1/jobs/{job_id}") - data = check_response(resp) - - status = data.get("status", "unknown") - status_style = { - "pending": "yellow", - "active": "cyan", - "completed": "green", - "failed": "red", - }.get(status, "white") - - console.print( - Panel( - f"[cyan]ID:[/cyan] {data.get('id', job_id)}\n" - f"[cyan]Name:[/cyan] {data.get('name', '-')}\n" - f"[cyan]Status:[/cyan] [{status_style}]{status}[/{status_style}]\n" - f"[cyan]Queue:[/cyan] {data.get('queue', '-')}\n" - f"[cyan]Priority:[/cyan] {data.get('priority', '-')}\n" - f"[cyan]Attempts:[/cyan] {data.get('attempts', 0)}/{data.get('max_attempts', 3)}\n" - f"[cyan]Started:[/cyan] {str(data.get('started_at', '-'))[:19]}\n" - f"[cyan]Duration:[/cyan] {data.get('duration_ms', '-')}ms\n" - f"[cyan]Payload:[/cyan] {json.dumps(data.get('payload', {}), indent=2, default=str)}", - title="Job Details", - border_style="cyan", - ) - ) - if data.get("error"): - console.print(f"\n[red]Error:[/red] {data['error']}") - - -# ============================================================================ -# Cron Operations -# ============================================================================ - - -@jobs_group.group() -def cron(): - """Manage scheduled jobs (cron).""" - pass - - -@cron.command(name="list") -@click.option( - "--status", "-s", type=click.Choice(["active", "paused", "all"]), default="all" -) -def cron_list(status: str): - """List cron schedules.""" - params = {} - if status != "all": - params["status"] = status - - resp = _request("get", "/v1/cron", params=params) - data = check_response(resp) - items = data.get("schedules", data.get("items", [])) - - table = Table(title="Cron Schedules", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Schedule", style="white") - table.add_column("Status", style="green") - table.add_column("Last Run", style="dim") - table.add_column("Next Run", style="yellow") - - for c in items: - c_status = c.get("status", "active") - style = "green" if c_status == "active" else "yellow" - table.add_row( - c.get("name", ""), - c.get("schedule", "-"), - f"[{style}]{c_status}[/{style}]", - str(c.get("last_run_at", "-"))[:19], - str(c.get("next_run_at", "-"))[:19], - ) - - console.print(table) - if not items: - console.print( - "[dim]No cron schedules found. Create one with 'hanzo jobs cron create'[/dim]" - ) - - -@cron.command(name="create") -@click.argument("name") -@click.option( - "--schedule", "-s", required=True, help="Cron expression (e.g., '0 * * * *')" -) -@click.option("--job", "-j", required=True, help="Job name to run") -@click.option("--payload", "-p", help="JSON payload") -@click.option("--timezone", "-tz", default="UTC", help="Timezone") -def cron_create(name: str, schedule: str, job: str, payload: str, timezone: str): - """Create a cron schedule.""" - body = {"name": name, "schedule": schedule, "job": job, "timezone": timezone} - if payload: - body["payload"] = json.loads(payload) - - resp = _request("post", "/v1/cron", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Cron schedule '{name}' created") - console.print(f" Schedule: {schedule}") - console.print(f" Job: {job}") - console.print(f" Timezone: {timezone}") - if data.get("next_run_at"): - console.print(f" Next run: {data['next_run_at']}") - - -@cron.command(name="delete") -@click.argument("name") -def cron_delete(name: str): - """Delete a cron schedule.""" - resp = _request("delete", f"/v1/cron/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Cron schedule '{name}' deleted") - - -@cron.command(name="pause") -@click.argument("name") -def cron_pause(name: str): - """Pause a cron schedule.""" - resp = _request("post", f"/v1/cron/{name}/pause") - check_response(resp) - console.print(f"[green]โœ“[/green] Cron schedule '{name}' paused") - - -@cron.command(name="resume") -@click.argument("name") -def cron_resume(name: str): - """Resume a paused cron schedule.""" - resp = _request("post", f"/v1/cron/{name}/resume") - check_response(resp) - console.print(f"[green]โœ“[/green] Cron schedule '{name}' resumed") - - -@cron.command(name="run-now") -@click.argument("name") -def cron_run_now(name: str): - """Trigger a cron job immediately.""" - resp = _request("post", f"/v1/cron/{name}/trigger") - data = check_response(resp) - console.print(f"[green]โœ“[/green] Cron job '{name}' triggered") - console.print(f" Job ID: {data.get('job_id', data.get('id', '-'))}") diff --git a/pkg/hanzo/src/hanzo/commands/k8s.py b/pkg/hanzo/src/hanzo/commands/k8s.py deleted file mode 100644 index 2cb82a78f..000000000 --- a/pkg/hanzo/src/hanzo/commands/k8s.py +++ /dev/null @@ -1,1058 +0,0 @@ -"""Hanzo K8s - Kubernetes cluster and fleet management. - -Manage Kubernetes clusters, deployments, and fleet operations via PaaS API. -""" - -import click -from rich import box -from rich.panel import Panel -from rich.table import Table - -from ..utils.output import console - -# ============================================================================ -# Helpers -# ============================================================================ - - -def _get_client(timeout: int = 30): - from ..utils.api_client import PaaSClient - - try: - return PaaSClient(timeout=timeout) - except SystemExit: - return None - - -def _get_ctx(fields=("org_id", "project_id", "env_id")): - from ..utils.api_client import require_context - - try: - return require_context(fields) - except SystemExit: - return None - - -def _container_base(): - from ..utils.api_client import container_url - - client = _get_client(timeout=60) - if not client: - return None - ctx = _get_ctx() - if not ctx: - return None - url = container_url(ctx["org_id"], ctx["project_id"], ctx["env_id"]) - return client, ctx, url - - -def _find_container(client, base_url: str, name: str): - from ..utils.api_client import find_container - - return find_container(client, base_url, name) - - -# ============================================================================ -# Main group -# ============================================================================ - - -@click.group(name="k8s") -def k8s_group(): - """Hanzo K8s - Kubernetes cluster management. - - \b - Clusters: - hanzo k8s cluster create # Create cluster - hanzo k8s cluster list # List clusters - hanzo k8s cluster delete # Delete cluster - hanzo k8s cluster kubeconfig # Get kubeconfig - - \b - Fleet: - hanzo k8s fleet create # Create fleet - hanzo k8s fleet add # Add cluster to fleet - hanzo k8s fleet deploy # Deploy to fleet - - \b - Workloads: - hanzo k8s deploy # Deploy application - hanzo k8s services # Manage services - hanzo k8s pods # List pods - - \b - Configuration: - hanzo k8s config # Manage configs/secrets - hanzo k8s ingress # Manage ingress - """ - pass - - -# ============================================================================ -# Cluster Management -# ============================================================================ - - -@k8s_group.group() -def cluster(): - """Manage Kubernetes clusters.""" - pass - - -@cluster.command(name="create") -@click.argument("name") -@click.option("--region", "-r", help="Region") -@click.option( - "--version", "-v", "k8s_version", default="1.29", help="Kubernetes version" -) -@click.option("--nodes", "-n", default=3, help="Number of nodes") -@click.option("--node-type", "-t", default="standard-2", help="Node type") -@click.option("--ha", is_flag=True, help="High availability control plane") -def cluster_create(name, region, k8s_version, nodes, node_type, ha): - """Create a Kubernetes cluster. - - \b - Examples: - hanzo k8s cluster create prod --nodes 5 --ha - hanzo k8s cluster create dev --nodes 2 --node-type small - hanzo k8s cluster create staging -r us-west-2 --version 1.29 - """ - from ..utils.api_client import cluster_url - - client = _get_client(timeout=120) - if not client: - return - - console.print(f"[cyan]Creating cluster '{name}'...[/cyan]") - - payload = { - "name": name, - "version": k8s_version, - "nodeCount": nodes, - "nodeType": node_type, - "ha": ha, - } - if region: - payload["region"] = region - - result = client.post(cluster_url(), payload) - if result is None: - return - - console.print(f"[green]โœ“[/green] Cluster '{name}' created") - console.print(f" Kubernetes: v{k8s_version}") - console.print(f" Nodes: {nodes}") - console.print(f" Node type: {node_type}") - console.print(f" HA: {'Yes' if ha else 'No'}") - if region: - console.print(f" Region: {region}") - - cid = result.get("_id") or result.get("id", "") - if cid: - console.print(f" Cluster ID: {cid}") - - -@cluster.command(name="list") -@click.option("--region", "-r", help="Filter by region") -def cluster_list(region): - """List Kubernetes clusters.""" - from ..utils.api_client import cluster_url - - client = _get_client() - if not client: - return - - data = client.get(cluster_url()) - if data is None: - return - - clusters = ( - data if isinstance(data, list) else data.get("clusters", data.get("data", [])) - ) - - if not clusters: - console.print( - "[dim]No clusters found. Create one with 'hanzo k8s cluster create'[/dim]" - ) - return - - table = Table(title="Kubernetes Clusters", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Version", style="white") - table.add_column("Nodes", style="green") - table.add_column("Region", style="yellow") - table.add_column("Status", style="dim") - - for c in clusters: - cname = c.get("name", "") - cver = c.get("version", c.get("k8sVersion", "")) - cnodes = str(c.get("nodeCount", c.get("nodes", "?"))) - cregion = c.get("region", "") - cstatus = c.get("status", "unknown") - - if region and cregion != region: - continue - - table.add_row(cname, cver, cnodes, cregion, cstatus) - - console.print(table) - - -@cluster.command(name="describe") -@click.argument("name") -def cluster_describe(name): - """Show cluster details.""" - from ..utils.api_client import cluster_url - - client = _get_client() - if not client: - return - - data = client.get(cluster_url(name)) - if data is None: - return - - version = data.get("version", data.get("k8sVersion", "?")) - nodes = data.get("nodeCount", data.get("nodes", "?")) - region = data.get("region", "?") - status = data.get("status", "unknown") - created = data.get("createdAt", data.get("created", "?")) - api_server = data.get("apiServer", data.get("endpoint", "")) - ha = data.get("ha", False) - - console.print( - Panel( - f"[cyan]Name:[/cyan] {name}\n" - f"[cyan]Kubernetes:[/cyan] v{version}\n" - f"[cyan]Status:[/cyan] {status}\n" - f"[cyan]Nodes:[/cyan] {nodes}\n" - f"[cyan]Region:[/cyan] {region}\n" - f"[cyan]Created:[/cyan] {created}\n" - f"[cyan]API Server:[/cyan] {api_server}\n" - f"[cyan]Control Plane:[/cyan] {'HA' if ha else 'Standard'}", - title="Cluster Details", - border_style="cyan", - ) - ) - - -@cluster.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True) -def cluster_delete(name, force): - """Delete a cluster.""" - from ..utils.api_client import cluster_url - - if not force: - from rich.prompt import Confirm - - if not Confirm.ask( - f"[red]Delete cluster '{name}'? This cannot be undone.[/red]" - ): - return - - client = _get_client(timeout=60) - if not client: - return - - result = client.delete(cluster_url(name)) - if result is None: - return - - console.print(f"[green]โœ“[/green] Cluster '{name}' deleted") - - -@cluster.command(name="kubeconfig") -@click.argument("name") -@click.option("--output", "-o", help="Output file (default: stdout)") -@click.option("--merge", is_flag=True, help="Merge into ~/.kube/config") -def cluster_kubeconfig(name, output, merge): - """Get cluster kubeconfig. - - \b - Examples: - hanzo k8s cluster kubeconfig prod # Print to stdout - hanzo k8s cluster kubeconfig prod -o config # Save to file - hanzo k8s cluster kubeconfig prod --merge # Merge into ~/.kube/config - """ - from pathlib import Path - - from ..utils.api_client import cluster_url - - client = _get_client() - if not client: - return - - data = client.get(f"{cluster_url(name)}/kubeconfig") - if data is None: - # Fall back to cluster detail (some APIs embed kubeconfig) - data = client.get(cluster_url(name)) - if data is None: - return - - kubeconfig = data.get("kubeconfig", data.get("config", "")) - if not kubeconfig: - console.print("[yellow]Kubeconfig not available for this cluster.[/yellow]") - return - - if isinstance(kubeconfig, dict): - import json - - kubeconfig = json.dumps(kubeconfig, indent=2) - - if merge: - kube_dir = Path.home() / ".kube" - kube_dir.mkdir(exist_ok=True) - kube_file = kube_dir / "config" - # Simple append for now - a real merge would parse YAML - with open(kube_file, "a") as f: - f.write(f"\n---\n{kubeconfig}") - console.print(f"[green]โœ“[/green] Merged '{name}' into ~/.kube/config") - console.print(f" Context: hanzo-{name}") - elif output: - Path(output).write_text(kubeconfig) - console.print(f"[green]โœ“[/green] Kubeconfig saved to '{output}'") - else: - console.print(kubeconfig) - - -@cluster.command(name="scale") -@click.argument("name") -@click.option("--nodes", "-n", type=int, required=True, help="Target node count") -@click.option("--pool", "-p", default="default", help="Node pool name") -def cluster_scale(name, nodes, pool): - """Scale cluster nodes.""" - from ..utils.api_client import cluster_url - - client = _get_client(timeout=60) - if not client: - return - - console.print(f"[cyan]Scaling '{name}' to {nodes} nodes...[/cyan]") - - result = client.put(cluster_url(name), {"nodeCount": nodes, "pool": pool}) - if result is None: - return - - console.print(f"[green]โœ“[/green] Cluster scaled") - console.print(f" Pool: {pool}") - console.print(f" Nodes: {nodes}") - - -@cluster.command(name="upgrade") -@click.argument("name") -@click.option("--version", "-v", "k8s_version", help="Target Kubernetes version") -@click.option("--dry-run", is_flag=True, help="Show upgrade plan") -def cluster_upgrade(name, k8s_version, dry_run): - """Upgrade cluster Kubernetes version.""" - from ..utils.api_client import cluster_url - - client = _get_client(timeout=120) - if not client: - return - - if dry_run: - # Get current version first - data = client.get(cluster_url(name)) - if data is None: - return - current = data.get("version", data.get("k8sVersion", "?")) - target = k8s_version or "latest" - console.print(f"[cyan]Upgrade plan for '{name}':[/cyan]") - console.print(f" Current: v{current}") - console.print(f" Target: v{target}") - console.print(" Steps: control-plane -> node-pools") - return - - console.print(f"[cyan]Upgrading '{name}'...[/cyan]") - payload = {} - if k8s_version: - payload["version"] = k8s_version - - result = client.put(cluster_url(name), payload) - if result is None: - return - - console.print(f"[green]โœ“[/green] Cluster upgraded to v{k8s_version or 'latest'}") - - -# ============================================================================ -# Fleet Management -# ============================================================================ - - -@k8s_group.group() -def fleet(): - """Manage cluster fleets.""" - pass - - -@fleet.command(name="create") -@click.argument("name") -@click.option("--description", "-d", help="Fleet description") -def fleet_create(name, description): - """Create a fleet for multi-cluster management.""" - console.print(f"[green]โœ“[/green] Fleet '{name}' created") - if description: - console.print(f" Description: {description}") - console.print( - "[dim]Fleet management is handled at the cluster level in the current PaaS release.[/dim]" - ) - - -@fleet.command(name="list") -def fleet_list(): - """List fleets.""" - from ..utils.api_client import cluster_url - - client = _get_client() - if not client: - return - - # Fleets map to clusters for now - data = client.get(cluster_url()) - if data is None: - return - - clusters = ( - data if isinstance(data, list) else data.get("clusters", data.get("data", [])) - ) - - if not clusters: - console.print("[dim]No fleets found[/dim]") - return - - table = Table(title="Fleets (Clusters)", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Nodes", style="green") - table.add_column("Region", style="yellow") - table.add_column("Status", style="dim") - - for c in clusters: - table.add_row( - c.get("name", ""), - str(c.get("nodeCount", "?")), - c.get("region", ""), - c.get("status", "unknown"), - ) - - console.print(table) - - -@fleet.command(name="add") -@click.argument("fleet") -@click.argument("cluster_name", metavar="CLUSTER") -@click.option("--labels", "-l", multiple=True, help="Labels (key=value)") -def fleet_add(fleet, cluster_name, labels): - """Add a cluster to a fleet.""" - console.print(f"[green]โœ“[/green] Added '{cluster_name}' to fleet '{fleet}'") - if labels: - console.print(f" Labels: {', '.join(labels)}") - - -@fleet.command(name="remove") -@click.argument("fleet") -@click.argument("cluster_name", metavar="CLUSTER") -def fleet_remove(fleet, cluster_name): - """Remove a cluster from a fleet.""" - console.print(f"[green]โœ“[/green] Removed '{cluster_name}' from fleet '{fleet}'") - - -@fleet.command(name="deploy") -@click.argument("fleet") -@click.option("--manifest", "-f", required=True, help="Manifest file or directory") -@click.option("--selector", "-l", help="Cluster selector (labels)") -@click.option( - "--strategy", type=click.Choice(["rolling", "all", "canary"]), default="rolling" -) -def fleet_deploy(fleet, manifest, selector, strategy): - """Deploy workloads across fleet. - - \b - Examples: - hanzo k8s fleet deploy prod -f app.yaml - hanzo k8s fleet deploy prod -f ./manifests/ -l env=prod - hanzo k8s fleet deploy prod -f app.yaml --strategy canary - """ - console.print(f"[cyan]Deploying to fleet '{fleet}'...[/cyan]") - console.print(f" Manifest: {manifest}") - console.print(f" Strategy: {strategy}") - console.print("[dim]Fleet deployment applies manifests via the cluster API.[/dim]") - - -# ============================================================================ -# Workloads -# ============================================================================ - - -@k8s_group.command(name="deploy") -@click.argument("name") -@click.option("--image", "-i", required=True, help="Container image") -@click.option("--replicas", "-r", default=1, help="Number of replicas") -@click.option("--namespace", "-n", default="default", help="Namespace") -@click.option("--port", "-p", type=int, help="Container port") -@click.option("--env", "-e", multiple=True, help="Environment variables (KEY=value)") -def k8s_deploy(name, image, replicas, namespace, port, env): - """Deploy an application to current context. - - \b - Examples: - hanzo k8s deploy my-app -i nginx:latest - hanzo k8s deploy api -i myapp:v1 -r 3 -p 8080 - hanzo k8s deploy worker -i worker:v1 -e DB_HOST=db.local - """ - info = _container_base() - if not info: - return - client, ctx, base_url = info - - # Check if exists - existing, existing_id = _find_container(client, base_url, name) - - variables = [] - for v in env: - if "=" in v: - k, val = v.split("=", 1) - variables.append({"name": k, "value": val}) - - if existing: - payload = { - "repoOrRegistry": "registry", - "registry": {"image": image}, - "deploymentConfig": {"desiredReplicas": replicas}, - } - if port: - payload["networking"] = {"containerPort": port} - if variables: - payload["variables"] = variables - - console.print(f"[cyan]Updating deployment '{name}'...[/cyan]") - result = client.put(f"{base_url}/{existing_id}", payload) - else: - payload = { - "name": name, - "type": "deployment", - "repoOrRegistry": "registry", - "registry": {"image": image}, - "deploymentConfig": {"desiredReplicas": replicas}, - } - if port: - payload["networking"] = {"containerPort": port} - if variables: - payload["variables"] = variables - - console.print(f"[cyan]Deploying '{name}'...[/cyan]") - result = client.post(base_url, payload) - - if result is None: - return - - console.print(f"[green]โœ“[/green] Deployment {'updated' if existing else 'created'}") - console.print(f" Image: {image}") - console.print(f" Replicas: {replicas}") - if port: - console.print(f" Port: {port}") - - -@k8s_group.command(name="pods") -@click.option("--name", "-N", help="Container/service name (uses context env)") -@click.option("--all-namespaces", "-A", is_flag=True, help="All containers") -def k8s_pods(name, all_namespaces): - """List pods for containers in current context.""" - info = _container_base() - if not info: - return - client, ctx, base_url = info - - if name: - # Get pods for specific container - _, cid = _find_container(client, base_url, name) - if not cid: - console.print(f"[yellow]Container '{name}' not found.[/yellow]") - return - - data = client.get(f"{base_url}/{cid}/pods") - if data is None: - return - - pods = ( - data if isinstance(data, list) else data.get("pods", data.get("data", [])) - ) - title = f"Pods for '{name}'" - else: - # List all containers, then get pods - data = client.get(base_url) - if data is None: - return - - containers = ( - data - if isinstance(data, list) - else data.get("containers", data.get("data", [])) - ) - pods = [] - for c in containers: - cid = c.get("_id") or c.get("iid") or c.get("id", "") - cname = c.get("name", "") - pod_data = client.get(f"{base_url}/{cid}/pods") - if pod_data: - pod_list = ( - pod_data - if isinstance(pod_data, list) - else pod_data.get("pods", pod_data.get("data", [])) - ) - for p in pod_list: - p["_container_name"] = cname - pods.extend(pod_list) - title = "All Pods" - - if not pods: - console.print("[dim]No pods found[/dim]") - return - - table = Table(title=title, box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Container", style="white") - table.add_column("Ready", style="green") - table.add_column("Status", style="yellow") - table.add_column("Age", style="dim") - - for p in pods: - pname = p.get("name", p.get("metadata", {}).get("name", "")) - pcont = p.get("_container_name", p.get("containerName", "")) - pstatus = p.get("status", p.get("phase", "Unknown")) - pready = p.get("ready", "?") - page = p.get("age", p.get("startTime", "")) - - table.add_row(pname, pcont, str(pready), pstatus, str(page)) - - console.print(table) - - -@k8s_group.command(name="services") -@click.option("--all-namespaces", "-A", is_flag=True, help="All containers") -def k8s_services(all_namespaces): - """List services (containers) in current context.""" - info = _container_base() - if not info: - return - client, ctx, base_url = info - - data = client.get(base_url) - if data is None: - return - - containers = ( - data if isinstance(data, list) else data.get("containers", data.get("data", [])) - ) - - if not containers: - console.print("[dim]No services found[/dim]") - return - - table = Table(title="Services", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Type", style="white") - table.add_column("Port", style="green") - table.add_column("Replicas", style="yellow") - table.add_column("Image/Repo", style="dim") - - for c in containers: - cname = c.get("name", "") - ctype = c.get("type", "deployment") - port = str((c.get("networking") or {}).get("containerPort", "")) - desired = str(c.get("deploymentConfig", {}).get("desiredReplicas", "?")) - - img = "" - if c.get("repoOrRegistry") == "registry": - img = (c.get("registry") or {}).get("image", "") - elif c.get("repoOrRegistry") == "repo": - img = (c.get("repo") or {}).get("url", "") - - table.add_row(cname, ctype, port, desired, img) - - console.print(table) - - -@k8s_group.command(name="logs") -@click.argument("name") -@click.option("--container", help="Container name (if multiple)") -@click.option("--follow", "-f", is_flag=True, help="Follow logs") -@click.option("--tail", "-t", default=100, help="Lines to show") -def k8s_logs(name, container, follow, tail): - """View container/pod logs.""" - info = _container_base() - if not info: - return - client, ctx, base_url = info - - _, cid = _find_container(client, base_url, name) - if not cid: - console.print(f"[yellow]Container '{name}' not found.[/yellow]") - return - - if follow: - console.print(f"[cyan]Tailing logs for '{name}'...[/cyan]") - console.print("[dim]Press Ctrl+C to stop[/dim]") - - data = client.get(f"{base_url}/{cid}/logs") - if data is None: - console.print("[dim]No logs found[/dim]") - return - - logs = data.get("logs", data.get("data", "")) - if isinstance(logs, list): - for line in logs[-tail:]: - console.print(line) - elif isinstance(logs, str): - for line in logs.split("\n")[-tail:]: - if line: - console.print(line) - else: - console.print("[dim]No logs found[/dim]") - - -@k8s_group.command(name="exec") -@click.argument("pod") -@click.argument("command", nargs=-1) -@click.option("--container", help="Container name") -@click.option("--stdin", "-i", is_flag=True, help="Interactive") -@click.option("--tty", "-t", is_flag=True, help="Allocate TTY") -def k8s_exec(pod, command, container, stdin, tty): - """Execute command in pod. - - \b - Examples: - hanzo k8s exec my-pod -- ls -la - hanzo k8s exec my-pod -it -- /bin/bash - """ - cmd = " ".join(command) if command else "/bin/sh" - console.print(f"[cyan]Executing in '{pod}': {cmd}[/cyan]") - console.print("[yellow]Remote exec requires direct kubectl access.[/yellow]") - console.print( - "[dim]Use 'hanzo k8s cluster kubeconfig NAME --merge' then 'kubectl exec'[/dim]" - ) - - -# ============================================================================ -# Configuration -# ============================================================================ - - -@k8s_group.group() -def config(): - """Manage ConfigMaps and Secrets.""" - pass - - -@config.command(name="create") -@click.argument("name") -@click.option("--from-literal", "-l", multiple=True, help="Key=value pairs") -@click.option("--from-file", "-f", multiple=True, help="Files to include") -@click.option("--secret", "-s", is_flag=True, help="Create as Secret") -def config_create(name, from_literal, from_file, secret): - """Create ConfigMap or Secret via container variables.""" - info = _container_base() - if not info: - return - client, ctx, base_url = info - - kind = "Secret" if secret else "ConfigMap" - - # Map config to container variables - variables = [] - for lit in from_literal: - if "=" in lit: - k, v = lit.split("=", 1) - variables.append({"name": k, "value": v}) - - if not variables: - console.print("[yellow]No key=value pairs specified.[/yellow]") - return - - console.print(f"[cyan]Creating {kind} '{name}' as container variables...[/cyan]") - - # Find or create a config container - existing, existing_id = _find_container(client, base_url, name) - if existing: - result = client.put(f"{base_url}/{existing_id}", {"variables": variables}) - else: - payload = { - "name": name, - "type": "deployment", - "variables": variables, - } - result = client.post(base_url, payload) - - if result is None: - return - - console.print(f"[green]โœ“[/green] {kind} '{name}' created ({len(variables)} keys)") - - -@config.command(name="list") -@click.option("--secrets", "-s", is_flag=True, help="List Secrets instead") -def config_list(secrets): - """List ConfigMaps or Secrets (container variables).""" - info = _container_base() - if not info: - return - client, ctx, base_url = info - - data = client.get(base_url) - if data is None: - return - - containers = ( - data if isinstance(data, list) else data.get("containers", data.get("data", [])) - ) - kind = "Secrets" if secrets else "ConfigMaps" - - table = Table(title=f"Container Variables ({kind})", box=box.ROUNDED) - table.add_column("Container", style="cyan") - table.add_column("Variables", style="green") - - for c in containers: - cname = c.get("name", "") - variables = c.get("variables", []) - if variables: - table.add_row(cname, str(len(variables))) - - console.print(table) - - -# ============================================================================ -# Ingress -# ============================================================================ - - -@k8s_group.group() -def ingress(): - """Manage Ingress resources.""" - pass - - -@ingress.command(name="create") -@click.argument("name") -@click.option("--host", "-h", required=True, help="Hostname") -@click.option("--service", "-s", required=True, help="Backend service (container name)") -@click.option("--port", "-p", type=int, default=80, help="Service port") -@click.option("--tls", is_flag=True, help="Enable TLS") -@click.option("--tls-secret", help="TLS secret name") -def ingress_create(name, host, service, port, tls, tls_secret): - """Create an Ingress via container networking config. - - \b - Examples: - hanzo k8s ingress create web -h app.example.com -s web-svc - hanzo k8s ingress create api -h api.example.com -s api-svc --tls - """ - info = _container_base() - if not info: - return - client, ctx, base_url = info - - _, cid = _find_container(client, base_url, service) - if not cid: - console.print(f"[yellow]Service '{service}' not found.[/yellow]") - return - - networking = { - "containerPort": port, - "customDomain": host, - "tcpProxy": {"enabled": not tls}, - } - if tls: - networking["tlsSecret"] = tls_secret or f"{name}-tls" - - result = client.put(f"{base_url}/{cid}", {"networking": networking}) - if result is None: - return - - console.print(f"[green]โœ“[/green] Ingress '{name}' created") - console.print(f" Host: {host}") - console.print(f" Backend: {service}:{port}") - if tls: - console.print(" TLS: enabled") - - -@ingress.command(name="list") -@click.option("--all-namespaces", "-A", is_flag=True, help="All namespaces") -def ingress_list(all_namespaces): - """List Ingress resources (containers with custom domains).""" - info = _container_base() - if not info: - return - client, ctx, base_url = info - - data = client.get(base_url) - if data is None: - return - - containers = ( - data if isinstance(data, list) else data.get("containers", data.get("data", [])) - ) - - table = Table(title="Ingress (Custom Domains)", box=box.ROUNDED) - table.add_column("Container", style="cyan") - table.add_column("Domain", style="white") - table.add_column("Port", style="green") - table.add_column("TLS", style="yellow") - - found = False - for c in containers: - networking = c.get("networking", {}) - domain = networking.get("customDomain", "") - if domain: - found = True - port = str(networking.get("containerPort", "")) - tls_secret = networking.get("tlsSecret", "") - table.add_row( - c.get("name", ""), domain, port, "Yes" if tls_secret else "No" - ) - - if found: - console.print(table) - else: - console.print("[dim]No ingress resources found[/dim]") - - -# ============================================================================ -# Node Pools -# ============================================================================ - - -@k8s_group.group() -def nodepool(): - """Manage node pools.""" - pass - - -@nodepool.command(name="create") -@click.argument("name") -@click.option("--cluster", "-c", required=True, help="Cluster name") -@click.option("--nodes", "-n", default=3, help="Number of nodes") -@click.option("--node-type", "-t", default="standard-2", help="Node type") -@click.option("--labels", "-l", multiple=True, help="Node labels (key=value)") -@click.option("--taints", multiple=True, help="Node taints") -@click.option("--gpu", is_flag=True, help="GPU nodes") -def nodepool_create(name, cluster, nodes, node_type, labels, taints, gpu): - """Create a node pool. - - \b - Examples: - hanzo k8s nodepool create workers -c prod -n 5 - hanzo k8s nodepool create gpu-pool -c prod --gpu --node-type gpu-large - """ - from ..utils.api_client import cluster_url - - client = _get_client(timeout=60) - if not client: - return - - payload = { - "name": name, - "nodeCount": nodes, - "nodeType": node_type, - "gpu": gpu, - } - if labels: - payload["labels"] = dict(l.split("=", 1) for l in labels if "=" in l) - if taints: - payload["taints"] = list(taints) - - result = client.post(f"{cluster_url(cluster)}/nodepools", payload) - if result is None: - return - - console.print(f"[green]โœ“[/green] Node pool '{name}' created in '{cluster}'") - console.print(f" Nodes: {nodes}") - console.print(f" Type: {node_type}") - if gpu: - console.print(" GPU: enabled") - - -@nodepool.command(name="list") -@click.option("--cluster", "-c", required=True, help="Cluster name") -def nodepool_list(cluster): - """List node pools.""" - from ..utils.api_client import cluster_url - - client = _get_client() - if not client: - return - - data = client.get(f"{cluster_url(cluster)}/nodepools") - if data is None: - return - - pools = ( - data if isinstance(data, list) else data.get("nodePools", data.get("data", [])) - ) - - if not pools: - console.print("[dim]No node pools found[/dim]") - return - - table = Table(title=f"Node Pools in '{cluster}'", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Nodes", style="green") - table.add_column("Type", style="white") - table.add_column("Status", style="dim") - - for p in pools: - table.add_row( - p.get("name", ""), - str(p.get("nodeCount", "?")), - p.get("nodeType", ""), - p.get("status", "unknown"), - ) - - console.print(table) - - -@nodepool.command(name="scale") -@click.argument("name") -@click.option("--cluster", "-c", required=True, help="Cluster name") -@click.option("--nodes", "-n", type=int, required=True, help="Target node count") -def nodepool_scale(name, cluster, nodes): - """Scale a node pool.""" - from ..utils.api_client import cluster_url - - client = _get_client(timeout=60) - if not client: - return - - console.print(f"[cyan]Scaling pool '{name}' to {nodes} nodes...[/cyan]") - result = client.put( - f"{cluster_url(cluster)}/nodepools/{name}", {"nodeCount": nodes} - ) - if result is None: - return - - console.print(f"[green]โœ“[/green] Node pool scaled") - - -@nodepool.command(name="delete") -@click.argument("name") -@click.option("--cluster", "-c", required=True, help="Cluster name") -@click.option("--force", "-f", is_flag=True) -def nodepool_delete(name, cluster, force): - """Delete a node pool.""" - from ..utils.api_client import cluster_url - - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete node pool '{name}'?[/red]"): - return - - client = _get_client() - if not client: - return - - result = client.delete(f"{cluster_url(cluster)}/nodepools/{name}") - if result is None: - return - - console.print(f"[green]โœ“[/green] Node pool '{name}' deleted") diff --git a/pkg/hanzo/src/hanzo/commands/kv.py b/pkg/hanzo/src/hanzo/commands/kv.py deleted file mode 100644 index 470293554..000000000 --- a/pkg/hanzo/src/hanzo/commands/kv.py +++ /dev/null @@ -1,284 +0,0 @@ -"""Hanzo KV - Key-value store CLI. - -Redis-compatible key-value storage with global replication. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -KV_URL = os.getenv("HANZO_KV_URL", "https://kv.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(KV_URL, method, path, **kwargs) - - -@click.group(name="kv") -def kv_group(): - """Hanzo KV - Global key-value store. - - \b - Stores: - hanzo kv create # Create KV store - hanzo kv list # List stores - hanzo kv delete # Delete store - - \b - Operations: - hanzo kv get # Get value - hanzo kv set # Set value - hanzo kv del # Delete key - hanzo kv keys # List keys - - \b - Batch: - hanzo kv mget # Multi-get - hanzo kv mset # Multi-set - """ - pass - - -# ============================================================================ -# Store Management -# ============================================================================ - - -@kv_group.command(name="create") -@click.argument("name") -@click.option("--region", "-r", multiple=True, help="Regions for replication") -@click.option("--max-size", default="1GB", help="Max store size") -@click.option( - "--eviction", type=click.Choice(["lru", "lfu", "ttl", "none"]), default="lru" -) -def kv_create(name: str, region: tuple, max_size: str, eviction: str): - """Create a KV store.""" - payload = {"name": name, "max_size": max_size, "eviction_policy": eviction} - if region: - payload["regions"] = list(region) - - resp = _request("post", "/v1/stores", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] KV store '{name}' created") - console.print(f" ID: {data.get('id', '-')}") - console.print(f" Max size: {max_size}") - console.print(f" Eviction: {eviction}") - if region: - console.print(f" Regions: {', '.join(region)}") - - -@kv_group.command(name="list") -def kv_list(): - """List KV stores.""" - resp = _request("get", "/v1/stores") - data = check_response(resp) - stores = data.get("stores", data.get("items", [])) - - table = Table(title="KV Stores", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Region", style="white") - table.add_column("Keys", style="green") - table.add_column("Size", style="yellow") - - for s in stores: - regions = s.get("regions", []) - table.add_row( - s.get("name", ""), - ", ".join(regions) if regions else s.get("region", "-"), - str(s.get("key_count", 0)), - s.get("size", "0 B"), - ) - - console.print(table) - if not stores: - console.print( - "[dim]No KV stores found. Create one with 'hanzo kv create'[/dim]" - ) - - -@kv_group.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True, help="Skip confirmation") -def kv_delete(name: str, force: bool): - """Delete a KV store.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete KV store '{name}'?[/red]"): - return - - resp = _request("delete", f"/v1/stores/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] KV store '{name}' deleted") - - -# ============================================================================ -# Key-Value Operations -# ============================================================================ - - -@kv_group.command(name="get") -@click.argument("key") -@click.option("--store", "-s", default="default", help="KV store name") -def kv_get(key: str, store: str): - """Get a value by key.""" - resp = _request("get", f"/v1/stores/{store}/keys/{key}") - if resp.status_code == 404: - console.print(f"[dim]Key '{key}' not found in store '{store}'[/dim]") - return - data = check_response(resp) - value = data.get("value", "") - console.print(f"[cyan]{key}[/cyan] = {value}") - - -@kv_group.command(name="set") -@click.argument("key") -@click.argument("value") -@click.option("--store", "-s", default="default", help="KV store name") -@click.option("--ttl", "-t", help="TTL (e.g., 1h, 7d)") -@click.option("--nx", is_flag=True, help="Only set if not exists") -def kv_set(key: str, value: str, store: str, ttl: str, nx: bool): - """Set a key-value pair.""" - payload = {"key": key, "value": value} - if ttl: - payload["ttl"] = ttl - if nx: - payload["nx"] = True - - resp = _request("put", f"/v1/stores/{store}/keys/{key}", json=payload) - data = check_response(resp) - - if nx and not data.get("set", True): - console.print(f"[yellow]Key '{key}' already exists (NX mode)[/yellow]") - else: - console.print(f"[green]โœ“[/green] Set '{key}' in store '{store}'") - if ttl: - console.print(f" TTL: {ttl}") - - -@kv_group.command(name="del") -@click.argument("keys", nargs=-1, required=True) -@click.option("--store", "-s", default="default", help="KV store name") -def kv_del(keys: tuple, store: str): - """Delete one or more keys.""" - resp = _request("post", f"/v1/stores/{store}/delete", json={"keys": list(keys)}) - data = check_response(resp) - console.print(f"[green]โœ“[/green] Deleted {data.get('deleted', len(keys))} key(s)") - - -@kv_group.command(name="keys") -@click.option("--store", "-s", default="default", help="KV store name") -@click.option("--pattern", "-p", default="*", help="Key pattern") -@click.option("--limit", "-n", default=100, help="Max keys") -def kv_keys(store: str, pattern: str, limit: int): - """List keys matching pattern.""" - resp = _request( - "get", f"/v1/stores/{store}/keys", params={"pattern": pattern, "limit": limit} - ) - data = check_response(resp) - keys = data.get("keys", []) - - console.print(f"[cyan]Keys matching '{pattern}':[/cyan]") - for k in keys: - if isinstance(k, dict): - console.print(f" {k.get('key', k.get('name', ''))}") - else: - console.print(f" {k}") - - if not keys: - console.print("[dim]No keys found[/dim]") - else: - console.print(f"\n[dim]{len(keys)} key(s)[/dim]") - - -@kv_group.command(name="mget") -@click.argument("keys", nargs=-1, required=True) -@click.option("--store", "-s", default="default") -def kv_mget(keys: tuple, store: str): - """Get multiple keys.""" - resp = _request("post", f"/v1/stores/{store}/mget", json={"keys": list(keys)}) - data = check_response(resp) - results = data.get("results", data.get("values", [])) - - table = Table(box=box.SIMPLE) - table.add_column("Key", style="cyan") - table.add_column("Value", style="white") - - for r in results: - if isinstance(r, dict): - table.add_row(r.get("key", ""), str(r.get("value", "(nil)"))) - else: - table.add_row("-", str(r)) - - console.print(table) - - -@kv_group.command(name="mset") -@click.argument("pairs", nargs=-1, required=True) -@click.option("--store", "-s", default="default") -def kv_mset(pairs: tuple, store: str): - """Set multiple key-value pairs (key1 val1 key2 val2 ...).""" - if len(pairs) % 2 != 0: - raise click.ClickException("Pairs must be even: key1 val1 key2 val2 ...") - - items = [] - for i in range(0, len(pairs), 2): - items.append({"key": pairs[i], "value": pairs[i + 1]}) - - resp = _request("post", f"/v1/stores/{store}/mset", json={"items": items}) - data = check_response(resp) - console.print(f"[green]โœ“[/green] Set {data.get('set', len(items))} key(s)") - - -@kv_group.command(name="ttl") -@click.argument("key") -@click.option("--store", "-s", default="default") -@click.option("--set", "set_ttl", help="Set new TTL") -def kv_ttl(key: str, store: str, set_ttl: str): - """Get or set TTL for a key.""" - if set_ttl: - resp = _request( - "put", f"/v1/stores/{store}/keys/{key}/ttl", json={"ttl": set_ttl} - ) - check_response(resp) - console.print(f"[green]โœ“[/green] Set TTL for '{key}' to {set_ttl}") - else: - resp = _request("get", f"/v1/stores/{store}/keys/{key}/ttl") - data = check_response(resp) - ttl_val = data.get("ttl", -1) - if ttl_val == -1: - console.print(f"[dim]Key '{key}' has no TTL (persistent)[/dim]") - elif ttl_val == -2: - console.print(f"[dim]Key '{key}' does not exist[/dim]") - else: - console.print(f"[cyan]TTL for '{key}':[/cyan] {ttl_val}s") - - -@kv_group.command(name="stats") -@click.option("--store", "-s", default="default") -def kv_stats(store: str): - """Show store statistics.""" - resp = _request("get", f"/v1/stores/{store}/stats") - data = check_response(resp) - - console.print( - Panel( - f"[cyan]Store:[/cyan] {data.get('name', store)}\n" - f"[cyan]Keys:[/cyan] {data.get('key_count', 0):,}\n" - f"[cyan]Memory:[/cyan] {data.get('memory', '0 B')}\n" - f"[cyan]Hit rate:[/cyan] {data.get('hit_rate', 0):.1f}%\n" - f"[cyan]Ops/sec:[/cyan] {data.get('ops_per_sec', 0):,}\n" - f"[cyan]Eviction policy:[/cyan] {data.get('eviction_policy', '-')}", - title="KV Statistics", - border_style="cyan", - ) - ) diff --git a/pkg/hanzo/src/hanzo/commands/mcp.py b/pkg/hanzo/src/hanzo/commands/mcp.py deleted file mode 100644 index 597299079..000000000 --- a/pkg/hanzo/src/hanzo/commands/mcp.py +++ /dev/null @@ -1,255 +0,0 @@ -"""MCP (Model Context Protocol) commands.""" - -import json - -import click -from rich.table import Table - -from ..utils.output import console - - -@click.group(name="mcp") -def mcp_group(): - """Manage MCP servers and tools.""" - pass - - -@mcp_group.command() -@click.option("--name", "-n", default="hanzo-mcp", help="Server name") -@click.option( - "--transport", - "-t", - type=click.Choice(["stdio", "sse"]), - default="stdio", - help="Transport protocol", -) -@click.option("--allow-path", "-p", multiple=True, help="Allowed paths") -@click.option("--enable-agent", is_flag=True, help="Enable agent tools") -@click.option("--host", default="127.0.0.1", help="Host for SSE transport") -@click.option("--port", default=3000, type=int, help="Port for SSE transport") -@click.pass_context -def serve( - ctx, - name: str, - transport: str, - allow_path: tuple, - enable_agent: bool, - host: str, - port: int, -): - """Start MCP server.""" - try: - from hanzoai.mcp import run_mcp_server - except ImportError: - console.print("[red]Error:[/red] hanzo-mcp not installed") - console.print("Install with: pip install hanzo[mcp]") - return - - allowed_paths = list(allow_path) if allow_path else ["."] - - console.print(f"[cyan]Starting MCP server[/cyan]") - console.print(f" Name: {name}") - console.print(f" Transport: {transport}") - console.print(f" Allowed paths: {', '.join(allowed_paths)}") - - if transport == "sse": - console.print(f" Endpoint: http://{host}:{port}") - - try: - run_mcp_server( - name=name, - transport=transport, - allowed_paths=allowed_paths, - enable_agent_tool=enable_agent, - host=host, - port=port, - ) - except KeyboardInterrupt: - console.print("\n[yellow]Server stopped[/yellow]") - - -@mcp_group.command() -@click.option("--category", "-c", help="Filter by category") -@click.pass_context -async def tools(ctx, category: str): - """List available MCP tools.""" - try: - from hanzoai.mcp import create_server - except ImportError: - console.print("[red]Error:[/red] hanzo-mcp not installed") - console.print("Install with: pip install hanzo[mcp]") - return - - with console.status("Loading tools..."): - server = create_server(enable_all_tools=True) - tools_list = await server.mcp.list_tools() - - # Group by category if available - categories = {} - for tool in tools_list: - cat = getattr(tool, "category", "general") - if category and cat != category: - continue - if cat not in categories: - categories[cat] = [] - categories[cat].append(tool) - - # Display tools - for cat, tools in sorted(categories.items()): - table = Table( - title=f"{cat.title()} Tools" if len(categories) > 1 else "MCP Tools" - ) - table.add_column("Name", style="cyan", no_wrap=True) - table.add_column("Description") - - for tool in sorted(tools, key=lambda t: t.name): - table.add_row(tool.name, tool.description) - - console.print(table) - if len(categories) > 1: - console.print() - - -@mcp_group.command() -@click.argument("tool") -@click.option("--arg", "-a", multiple=True, help="Tool arguments (key=value)") -@click.option("--json-args", "-j", help="JSON arguments") -@click.pass_context -async def run(ctx, tool: str, arg: tuple, json_args: str): - """Run an MCP tool.""" - try: - from hanzoai.mcp import create_server - except ImportError: - console.print("[red]Error:[/red] hanzo-mcp not installed") - console.print("Install with: pip install hanzo[mcp]") - return - - # Parse arguments - args = {} - - if json_args: - try: - args = json.loads(json_args) - except json.JSONDecodeError as e: - console.print(f"[red]Invalid JSON: {e}[/red]") - return - else: - for a in arg: - if "=" not in a: - console.print(f"[red]Invalid argument format: {a}[/red]") - console.print("Use: --arg key=value") - return - key, value = a.split("=", 1) - - # Try to parse value as JSON first - try: - args[key] = json.loads(value) - except Exception: - args[key] = value - - # Create server and run tool - with console.status(f"Running tool '{tool}'..."): - server = create_server(enable_all_tools=True) - - # Find tool - tools_list = await server.mcp.list_tools() - tool_obj = None - for t in tools_list: - if t.name == tool: - tool_obj = t - break - - if not tool_obj: - console.print(f"[red]Tool not found: {tool}[/red]") - console.print("Use 'hanzo mcp tools' to list available tools") - return - - # Run tool - try: - from mcp.server.fastmcp import Context - - context = Context() # CLI context (no request_context) - - # Get tool function - tool_func = server.mcp._tool_map.get(tool) - if tool_func: - result = await tool_func(**args) - else: - console.print(f"[red]Tool function not found: {tool}[/red]") - return - - except Exception as e: - console.print(f"[red]Tool error: {e}[/red]") - return - - # Display result - if isinstance(result, str): - try: - # Try to parse as JSON for pretty printing - data = json.loads(result) - console.print_json(data=data) - except Exception: - # Display as text - console.print(result) - else: - console.print(result) - - -@mcp_group.command() -@click.option( - "--path", - "-p", - default="~/.config/claude/claude_desktop_config.json", - help="Config file path", -) -@click.pass_context -def install(ctx, path: str): - """Install MCP server in Claude Desktop.""" - try: - import hanzoai.mcp - except ImportError: - console.print("[red]Error:[/red] hanzo-mcp not installed") - console.print("Install with: pip install hanzo[mcp]") - return - - import os - import json - from pathlib import Path - - config_path = Path(os.path.expanduser(path)) - - # Create config - config = { - "mcpServers": { - "hanzo-mcp": { - "command": "hanzo", - "args": ["mcp", "serve", "--transport", "stdio"], - } - } - } - - # Check if file exists - if config_path.exists(): - try: - with open(config_path, "r") as f: - existing = json.load(f) - - if "mcpServers" not in existing: - existing["mcpServers"] = {} - - existing["mcpServers"]["hanzo-mcp"] = config["mcpServers"]["hanzo-mcp"] - config = existing - except Exception as e: - console.print( - f"[yellow]Warning: Could not read existing config: {e}[/yellow]" - ) - - # Write config - config_path.parent.mkdir(parents=True, exist_ok=True) - - with open(config_path, "w") as f: - json.dump(config, f, indent=2) - - console.print(f"[green]โœ“[/green] Installed hanzo-mcp in Claude Desktop") - console.print(f" Config: {config_path}") - console.print("\nRestart Claude Desktop for changes to take effect") diff --git a/pkg/hanzo/src/hanzo/commands/miner.py b/pkg/hanzo/src/hanzo/commands/miner.py deleted file mode 100644 index 3286f673d..000000000 --- a/pkg/hanzo/src/hanzo/commands/miner.py +++ /dev/null @@ -1,336 +0,0 @@ -"""Mining commands for distributed AI compute.""" - -import click -from rich.table import Table -from rich.progress import Progress, TextColumn, SpinnerColumn - -from ..utils.output import console - - -@click.group(name="miner") -def miner_group(): - """Manage Hanzo AI mining (contribute compute).""" - pass - - -@miner_group.command() -@click.option("--name", "-n", help="Miner name (auto-generated if not provided)") -@click.option("--wallet", "-w", help="Wallet address for rewards") -@click.option("--models", "-m", multiple=True, help="Models to support") -@click.option( - "--device", - type=click.Choice(["cpu", "gpu", "auto"]), - default="auto", - help="Device to use", -) -@click.option("--network", default="mainnet", help="Network to join") -@click.option("--min-stake", type=float, help="Minimum stake requirement") -@click.pass_context -async def start( - ctx, - name: str, - wallet: str, - models: tuple, - device: str, - network: str, - min_stake: float, -): - """Start mining (contribute compute to network).""" - try: - from hanzo_miner import HanzoMiner - except ImportError: - console.print("[red]Error:[/red] hanzo-miner not installed") - console.print("Install with: pip install hanzo[miner]") - return - - # Check wallet - if not wallet: - console.print("[yellow]Warning:[/yellow] No wallet address provided") - console.print("You won't earn rewards without a wallet") - if not click.confirm("Continue without wallet?"): - return - - miner = HanzoMiner( - name=name, wallet=wallet, device=device, network=network, min_stake=min_stake - ) - - with Progress( - SpinnerColumn(), - TextColumn("[progress.description]{task.description}"), - console=console, - ) as progress: - task = progress.add_task("Starting miner...", total=None) - - try: - # Start miner - await miner.start(models=list(models) if models else None) - progress.update(task, completed=True) - except Exception as e: - progress.stop() - console.print(f"[red]Failed to start miner: {e}[/red]") - return - - console.print(f"[green]โœ“[/green] Miner started") - console.print(f" Name: {miner.name}") - console.print(f" Network: {network}") - console.print(f" Device: {device}") - if wallet: - console.print(f" Wallet: {wallet[:8]}...{wallet[-4:]}") - - # Show initial stats - stats = await miner.get_stats() - if stats: - console.print("\n[cyan]Initial Stats:[/cyan]") - console.print(f" Jobs completed: {stats.get('jobs_completed', 0)}") - console.print(f" Tokens earned: {stats.get('tokens_earned', 0)}") - console.print(f" Uptime: {stats.get('uptime', '0s')}") - - console.print("\nPress Ctrl+C to stop mining\n") - console.print("[dim]Logs:[/dim]") - - try: - # Stream logs and stats - async for event in miner.stream_events(): - if event["type"] == "log": - console.print(event["message"], end="") - elif event["type"] == "job": - console.print( - f"[green]Job completed:[/green] {event['job_id']} (+{event['tokens']} tokens)" - ) - elif event["type"] == "error": - console.print(f"[red]Error:[/red] {event['message']}") - except KeyboardInterrupt: - console.print("\n[yellow]Stopping miner...[/yellow]") - await miner.stop() - - # Show final stats - final_stats = await miner.get_stats() - if final_stats: - console.print("\n[cyan]Session Summary:[/cyan]") - console.print(f" Jobs completed: {final_stats.get('jobs_completed', 0)}") - console.print(f" Tokens earned: {final_stats.get('tokens_earned', 0)}") - console.print( - f" Average job time: {final_stats.get('avg_job_time', 'N/A')}" - ) - - console.print("[green]โœ“[/green] Miner stopped") - - -@miner_group.command() -@click.option("--name", "-n", help="Miner name") -@click.pass_context -async def stop(ctx, name: str): - """Stop mining.""" - try: - from hanzo_miner import HanzoMiner - except ImportError: - console.print("[red]Error:[/red] hanzo-miner not installed") - return - - if name: - miner = HanzoMiner.get_by_name(name) - if miner: - console.print(f"[yellow]Stopping miner '{name}'...[/yellow]") - await miner.stop() - console.print(f"[green]โœ“[/green] Miner stopped") - else: - console.print(f"[red]Miner not found: {name}[/red]") - else: - # Stop all miners - miners = HanzoMiner.get_all() - if miners: - if click.confirm(f"Stop all {len(miners)} miners?"): - for miner in miners: - await miner.stop() - console.print(f"[green]โœ“[/green] Stopped {len(miners)} miners") - else: - console.print("[yellow]No miners running[/yellow]") - - -@miner_group.command() -@click.option("--name", "-n", help="Miner name") -@click.option("--detailed", "-d", is_flag=True, help="Show detailed stats") -@click.pass_context -async def status(ctx, name: str, detailed: bool): - """Show mining status.""" - try: - from hanzo_miner import HanzoMiner - except ImportError: - console.print("[red]Error:[/red] hanzo-miner not installed") - return - - if name: - # Show specific miner - miner = HanzoMiner.get_by_name(name) - if not miner: - console.print(f"[red]Miner not found: {name}[/red]") - return - - miners = [miner] - else: - # Show all miners - miners = HanzoMiner.get_all() - - if not miners: - console.print("[yellow]No miners running[/yellow]") - console.print("Start mining with: hanzo miner start") - return - - # Create table - table = Table(title="Active Miners") - table.add_column("Name", style="cyan") - table.add_column("Status", style="green") - table.add_column("Device", style="yellow") - table.add_column("Jobs", style="blue") - table.add_column("Tokens", style="magenta") - table.add_column("Uptime", style="white") - - for miner in miners: - stats = await miner.get_stats() - table.add_row( - miner.name, - stats.get("status", "unknown"), - stats.get("device", "unknown"), - str(stats.get("jobs_completed", 0)), - f"{stats.get('tokens_earned', 0):.2f}", - stats.get("uptime", "0s"), - ) - - console.print(table) - - if detailed and len(miners) == 1: - # Show detailed stats for single miner - miner = miners[0] - stats = await miner.get_stats() - - console.print("\n[cyan]Detailed Statistics:[/cyan]") - console.print(f" Network: {stats.get('network', 'unknown')}") - console.print(f" Wallet: {stats.get('wallet', 'Not set')}") - console.print(f" Models: {', '.join(stats.get('models', []))}") - console.print( - f" Memory: {stats.get('memory_used', 0)} / {stats.get('memory_total', 0)} MB" - ) - console.print(f" CPU: {stats.get('cpu_percent', 0)}%") - - if gpu := stats.get("gpu"): - console.print( - f" GPU: {gpu['name']} ({gpu['memory_used']} / {gpu['memory_total']} MB)" - ) - - console.print(f"\n[cyan]Performance:[/cyan]") - console.print(f" Average job time: {stats.get('avg_job_time', 'N/A')}") - console.print(f" Success rate: {stats.get('success_rate', 0)}%") - console.print(f" Tokens/hour: {stats.get('tokens_per_hour', 0):.2f}") - - -@miner_group.command() -@click.option("--network", default="mainnet", help="Network to check") -@click.pass_context -async def leaderboard(ctx, network: str): - """Show mining leaderboard.""" - try: - from hanzo_miner import get_leaderboard - except ImportError: - console.print("[red]Error:[/red] hanzo-miner not installed") - return - - with console.status("Loading leaderboard..."): - try: - leaders = await get_leaderboard(network=network) - except Exception as e: - console.print(f"[red]Failed to load leaderboard: {e}[/red]") - return - - if not leaders: - console.print("[yellow]No data available[/yellow]") - return - - table = Table(title=f"Mining Leaderboard - {network}") - table.add_column("Rank", style="cyan") - table.add_column("Miner", style="green") - table.add_column("Jobs", style="yellow") - table.add_column("Tokens", style="magenta") - table.add_column("Success Rate", style="blue") - - for i, leader in enumerate(leaders[:20], 1): - table.add_row( - str(i), - leader["name"], - str(leader["jobs"]), - f"{leader['tokens']:.2f}", - f"{leader['success_rate']}%", - ) - - console.print(table) - - -@miner_group.command() -@click.option("--wallet", "-w", required=True, help="Wallet address") -@click.option("--network", default="mainnet", help="Network") -@click.pass_context -async def earnings(ctx, wallet: str, network: str): - """Check mining earnings.""" - try: - from hanzo_miner import check_earnings - except ImportError: - console.print("[red]Error:[/red] hanzo-miner not installed") - return - - with console.status("Checking earnings..."): - try: - data = await check_earnings(wallet=wallet, network=network) - except Exception as e: - console.print(f"[red]Failed to check earnings: {e}[/red]") - return - - console.print(f"[cyan]Earnings for {wallet[:8]}...{wallet[-4:]}[/cyan]") - console.print(f" Network: {network}") - console.print(f" Total earned: {data.get('total_earned', 0):.2f} tokens") - console.print(f" Available: {data.get('available', 0):.2f} tokens") - console.print(f" Pending: {data.get('pending', 0):.2f} tokens") - - if history := data.get("recent_jobs"): - console.print("\n[cyan]Recent Jobs:[/cyan]") - for job in history[:5]: - console.print( - f" โ€ข {job['timestamp']}: +{job['tokens']} tokens ({job['model']})" - ) - - -@miner_group.command() -@click.argument("amount", type=float) -@click.option("--wallet", "-w", required=True, help="Wallet address") -@click.option("--to", required=True, help="Destination address") -@click.option("--network", default="mainnet", help="Network") -@click.pass_context -async def withdraw(ctx, amount: float, wallet: str, to: str, network: str): - """Withdraw mining earnings.""" - try: - from hanzo_miner import withdraw_earnings - except ImportError: - console.print("[red]Error:[/red] hanzo-miner not installed") - return - - # Confirm withdrawal - console.print(f"[yellow]Withdrawal Request:[/yellow]") - console.print(f" Amount: {amount} tokens") - console.print(f" From: {wallet[:8]}...{wallet[-4:]}") - console.print(f" To: {to[:8]}...{to[-4:]}") - console.print(f" Network: {network}") - - if not click.confirm("Proceed with withdrawal?"): - return - - with console.status("Processing withdrawal..."): - try: - result = await withdraw_earnings( - wallet=wallet, amount=amount, destination=to, network=network - ) - - console.print(f"[green]โœ“[/green] Withdrawal successful") - console.print(f" Transaction: {result['tx_hash']}") - console.print(f" Amount: {result['amount']} tokens") - console.print(f" Fee: {result['fee']} tokens") - - except Exception as e: - console.print(f"[red]Withdrawal failed: {e}[/red]") diff --git a/pkg/hanzo/src/hanzo/commands/ml.py b/pkg/hanzo/src/hanzo/commands/ml.py deleted file mode 100644 index 355855ee1..000000000 --- a/pkg/hanzo/src/hanzo/commands/ml.py +++ /dev/null @@ -1,1220 +0,0 @@ -"""Hanzo ML - Machine learning platform CLI. - -End-to-end MLOps: notebooks, pipelines, training, serving, registry. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -ML_URL = os.getenv("HANZO_ML_URL", "https://ml.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(ML_URL, method, path, **kwargs) - - -@click.group(name="ml") -def ml_group(): - """Hanzo ML - End-to-end machine learning platform (Kubeflow-compatible). - - \b - Develop: - hanzo ml notebooks # Jupyter notebook management - hanzo ml datasets # Versioned dataset management - - \b - Train: - hanzo ml training # Training jobs - hanzo ml pipelines # ML pipelines (Kubeflow Pipelines) - hanzo ml experiments # Experiment tracking - hanzo ml tune # Hyperparameter tuning (Katib) - - \b - Features: - hanzo ml features # Feature store management - - \b - AutoML: - hanzo ml automl # Automated machine learning - - \b - Serve: - hanzo ml serving # Model serving (KServe) - hanzo ml registry # Model registry - """ - pass - - -# ============================================================================ -# Notebooks -# ============================================================================ - - -@ml_group.group() -def notebooks(): - """Manage Jupyter notebooks.""" - pass - - -@notebooks.command(name="list") -def notebooks_list(): - """List all notebooks.""" - resp = _request("get", "/v1/notebooks") - data = check_response(resp) - items = data.get("notebooks", data.get("items", [])) - - table = Table(title="Notebooks", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Instance", style="white") - table.add_column("Status", style="green") - table.add_column("GPU", style="yellow") - table.add_column("Created", style="dim") - - for n in items: - n_status = n.get("status", "stopped") - style = "green" if n_status == "running" else "yellow" - table.add_row( - n.get("name", ""), - n.get("instance", "-"), - f"[{style}]{n_status}[/{style}]", - n.get("gpu", "None"), - str(n.get("created_at", ""))[:19], - ) - - console.print(table) - if not items: - console.print( - "[dim]No notebooks found. Create one with 'hanzo ml notebooks create'[/dim]" - ) - - -@notebooks.command(name="create") -@click.option("--name", "-n", prompt=True, help="Notebook name") -@click.option("--instance", "-i", default="cpu-small", help="Instance type") -@click.option("--gpu", is_flag=True, help="Enable GPU") -def notebooks_create(name: str, instance: str, gpu: bool): - """Create a new notebook instance.""" - body = {"name": name, "instance": instance, "gpu": gpu} - - console.print(f"[cyan]Creating notebook '{name}'...[/cyan]") - resp = _request("post", "/v1/notebooks", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Notebook '{name}' created") - console.print(f" Instance: {instance}") - console.print(f" GPU: {'Yes' if gpu else 'No'}") - if data.get("url"): - console.print(f" URL: {data['url']}") - - -@notebooks.command(name="start") -@click.argument("name") -def notebooks_start(name: str): - """Start a notebook.""" - resp = _request("post", f"/v1/notebooks/{name}/start") - check_response(resp) - console.print(f"[green]โœ“[/green] Notebook '{name}' started") - - -@notebooks.command(name="stop") -@click.argument("name") -def notebooks_stop(name: str): - """Stop a notebook.""" - resp = _request("post", f"/v1/notebooks/{name}/stop") - check_response(resp) - console.print(f"[green]โœ“[/green] Notebook '{name}' stopped") - - -@notebooks.command(name="delete") -@click.argument("name") -def notebooks_delete(name: str): - """Delete a notebook.""" - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete notebook '{name}'?[/red]"): - return - - resp = _request("delete", f"/v1/notebooks/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Notebook '{name}' deleted") - - -# ============================================================================ -# Training -# ============================================================================ - - -@ml_group.group() -def training(): - """Manage training jobs.""" - pass - - -@training.command(name="list") -@click.option( - "--status", - type=click.Choice(["running", "completed", "failed", "all"]), - default="all", -) -def training_list(status: str): - """List training jobs.""" - params = {} - if status != "all": - params["status"] = status - - resp = _request("get", "/v1/training", params=params) - data = check_response(resp) - items = data.get("jobs", data.get("items", [])) - - table = Table(title="Training Jobs", box=box.ROUNDED) - table.add_column("ID", style="cyan") - table.add_column("Name", style="white") - table.add_column("Framework", style="white") - table.add_column("Status", style="green") - table.add_column("Duration", style="dim") - table.add_column("GPU", style="yellow") - - for j in items: - j_status = j.get("status", "unknown") - status_style = {"running": "cyan", "completed": "green", "failed": "red"}.get( - j_status, "white" - ) - table.add_row( - str(j.get("id", ""))[:16], - j.get("name", "-"), - j.get("framework", "-"), - f"[{status_style}]{j_status}[/{status_style}]", - f"{j.get('duration_ms', '-')}ms" if j.get("duration_ms") else "-", - j.get("gpu", "-"), - ) - - console.print(table) - if not items: - console.print("[dim]No training jobs found[/dim]") - - -@training.command(name="create") -@click.option("--name", "-n", prompt=True, help="Job name") -@click.option( - "--framework", - "-f", - type=click.Choice(["pytorch", "tensorflow", "xgboost"]), - default="pytorch", -) -@click.option("--script", "-s", required=True, help="Training script path") -@click.option("--gpu", "-g", default="1", help="Number of GPUs") -@click.option("--instance", "-i", default="gpu-a10g", help="Instance type") -def training_create(name: str, framework: str, script: str, gpu: str, instance: str): - """Create a training job.""" - body = { - "name": name, - "framework": framework, - "script": script, - "gpu": gpu, - "instance": instance, - } - - console.print(f"[cyan]Creating training job '{name}'...[/cyan]") - resp = _request("post", "/v1/training", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Training job '{name}' started") - console.print(f" ID: {data.get('id', '-')}") - console.print(f" Framework: {framework}") - console.print(f" Script: {script}") - console.print(f" Instance: {instance} ({gpu} GPUs)") - - -@training.command(name="logs") -@click.argument("job_id") -@click.option("--follow", "-f", is_flag=True, help="Follow logs") -def training_logs(job_id: str, follow: bool): - """View training job logs.""" - params = {} - if follow: - params["follow"] = "true" - - resp = _request("get", f"/v1/training/{job_id}/logs", params=params) - data = check_response(resp) - lines = data.get("logs", data.get("lines", [])) - - console.print(f"[cyan]Logs for job {job_id}:[/cyan]") - for line in lines: - if isinstance(line, dict): - console.print( - f"[dim]{str(line.get('timestamp', ''))[:19]}[/dim] {line.get('message', '')}" - ) - else: - console.print(str(line)) - - if not lines: - console.print("[dim]No logs available[/dim]") - - -@training.command(name="stop") -@click.argument("job_id") -def training_stop(job_id: str): - """Stop a training job.""" - resp = _request("post", f"/v1/training/{job_id}/stop") - check_response(resp) - console.print(f"[green]โœ“[/green] Training job '{job_id}' stopped") - - -# ============================================================================ -# Pipelines -# ============================================================================ - - -@ml_group.group() -def pipelines(): - """Manage ML pipelines.""" - pass - - -@pipelines.command(name="list") -def pipelines_list(): - """List all pipelines.""" - resp = _request("get", "/v1/pipelines") - data = check_response(resp) - items = data.get("pipelines", data.get("items", [])) - - table = Table(title="ML Pipelines", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Version", style="white") - table.add_column("Status", style="green") - table.add_column("Last Run", style="dim") - - for p in items: - p_status = p.get("status", "idle") - style = "green" if p_status == "active" else "dim" - table.add_row( - p.get("name", ""), - str(p.get("version", "-")), - f"[{style}]{p_status}[/{style}]", - str(p.get("last_run_at", ""))[:19], - ) - - console.print(table) - if not items: - console.print("[dim]No pipelines found[/dim]") - - -@pipelines.command(name="create") -@click.option("--name", "-n", prompt=True, help="Pipeline name") -@click.option("--file", "-f", required=True, help="Pipeline definition file") -def pipelines_create(name: str, file: str): - """Create a pipeline from definition file.""" - with open(file) as f: - definition = json.load(f) - - resp = _request( - "post", "/v1/pipelines", json={"name": name, "definition": definition} - ) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Pipeline '{name}' created from {file}") - console.print(f" ID: {data.get('id', '-')}") - - -@pipelines.command(name="run") -@click.argument("pipeline_name") -@click.option("--params", "-p", help="JSON parameters") -def pipelines_run(pipeline_name: str, params: str): - """Run a pipeline.""" - body = {} - if params: - body["params"] = json.loads(params) - - console.print(f"[cyan]Running pipeline '{pipeline_name}'...[/cyan]") - resp = _request("post", f"/v1/pipelines/{pipeline_name}/run", json=body) - data = check_response(resp) - console.print(f"[green]โœ“[/green] Pipeline run started") - console.print(f" Run ID: {data.get('id', data.get('run_id', '-'))}") - - -# ============================================================================ -# Serving -# ============================================================================ - - -@ml_group.group() -def serving(): - """Manage model serving.""" - pass - - -@serving.command(name="list") -def serving_list(): - """List model deployments.""" - resp = _request("get", "/v1/serving") - data = check_response(resp) - items = data.get("deployments", data.get("items", [])) - - table = Table(title="Model Deployments", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Model", style="white") - table.add_column("Version", style="white") - table.add_column("Status", style="green") - table.add_column("Replicas", style="dim") - table.add_column("Endpoint", style="dim") - - for d in items: - d_status = d.get("status", "unknown") - style = "green" if d_status == "ready" else "yellow" - table.add_row( - d.get("name", ""), - d.get("model", "-"), - str(d.get("version", "-")), - f"[{style}]{d_status}[/{style}]", - str(d.get("replicas", 0)), - d.get("endpoint", "-"), - ) - - console.print(table) - if not items: - console.print("[dim]No deployments found[/dim]") - - -@serving.command(name="deploy") -@click.option("--name", "-n", prompt=True, help="Deployment name") -@click.option("--model", "-m", required=True, help="Model path or registry URI") -@click.option("--replicas", "-r", default=1, help="Number of replicas") -@click.option("--gpu", is_flag=True, help="Enable GPU inference") -def serving_deploy(name: str, model: str, replicas: int, gpu: bool): - """Deploy a model for inference.""" - body = {"name": name, "model": model, "replicas": replicas, "gpu": gpu} - - console.print(f"[cyan]Deploying model '{name}'...[/cyan]") - resp = _request("post", "/v1/serving", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Model deployed") - console.print(f" Endpoint: {data.get('endpoint', '-')}") - console.print(f" Replicas: {replicas}") - console.print(f" GPU: {'Yes' if gpu else 'No'}") - - -@serving.command(name="scale") -@click.argument("name") -@click.option("--replicas", "-r", required=True, type=int, help="Target replicas") -def serving_scale(name: str, replicas: int): - """Scale a deployment.""" - resp = _request("put", f"/v1/serving/{name}/scale", json={"replicas": replicas}) - check_response(resp) - console.print(f"[green]โœ“[/green] Deployment '{name}' scaled to {replicas} replicas") - - -@serving.command(name="delete") -@click.argument("name") -def serving_delete(name: str): - """Delete a deployment.""" - resp = _request("delete", f"/v1/serving/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Deployment '{name}' deleted") - - -# ============================================================================ -# Registry -# ============================================================================ - - -@ml_group.group() -def registry(): - """Manage model registry.""" - pass - - -@registry.command(name="list") -@click.option("--model", "-m", help="Filter by model name") -def registry_list(model: str): - """List registered models.""" - params = {} - if model: - params["model"] = model - - resp = _request("get", "/v1/registry", params=params) - data = check_response(resp) - items = data.get("models", data.get("items", [])) - - table = Table(title="Model Registry", box=box.ROUNDED) - table.add_column("Model", style="cyan") - table.add_column("Version", style="white") - table.add_column("Stage", style="green") - table.add_column("Framework", style="dim") - table.add_column("Created", style="dim") - - for m in items: - stage = m.get("stage", "none") - style = ( - "green" - if stage == "production" - else "yellow" if stage == "staging" else "dim" - ) - table.add_row( - m.get("name", ""), - str(m.get("version", "-")), - f"[{style}]{stage}[/{style}]", - m.get("framework", "-"), - str(m.get("created_at", ""))[:19], - ) - - console.print(table) - if not items: - console.print("[dim]No models registered[/dim]") - - -@registry.command(name="push") -@click.option("--name", "-n", required=True, help="Model name") -@click.option("--path", "-p", required=True, help="Model path") -@click.option("--framework", "-f", default="pytorch", help="Framework") -@click.option("--version", "-v", help="Version (auto-incremented if not specified)") -def registry_push(name: str, path: str, framework: str, version: str): - """Push a model to the registry.""" - body = {"name": name, "path": path, "framework": framework} - if version: - body["version"] = version - - console.print(f"[cyan]Pushing model '{name}'...[/cyan]") - resp = _request("post", "/v1/registry", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Model '{name}' pushed to registry") - console.print(f" Version: {data.get('version', '-')}") - console.print(f" Framework: {framework}") - - -@registry.command(name="pull") -@click.argument("model_uri") -@click.option("--output", "-o", default=".", help="Output directory") -def registry_pull(model_uri: str, output: str): - """Pull a model from the registry.""" - console.print(f"[cyan]Pulling model {model_uri}...[/cyan]") - resp = _request( - "post", "/v1/registry/pull", json={"uri": model_uri, "output": output} - ) - data = check_response(resp) - console.print(f"[green]โœ“[/green] Model downloaded to {data.get('path', output)}") - - -@registry.command(name="promote") -@click.argument("model_uri") -@click.option( - "--stage", "-s", type=click.Choice(["staging", "production"]), required=True -) -def registry_promote(model_uri: str, stage: str): - """Promote a model version to a stage.""" - resp = _request( - "post", "/v1/registry/promote", json={"uri": model_uri, "stage": stage} - ) - check_response(resp) - console.print(f"[green]โœ“[/green] Model promoted to {stage}") - - -# ============================================================================ -# Experiments (Kubeflow-style experiment tracking) -# ============================================================================ - - -@ml_group.group() -def experiments(): - """Manage ML experiments (Kubeflow-style).""" - pass - - -@experiments.command(name="create") -@click.argument("name") -@click.option("--description", "-d", help="Experiment description") -@click.option("--namespace", "-n", default="default", help="Namespace") -def experiments_create(name: str, description: str, namespace: str): - """Create an experiment.""" - body = {"name": name, "namespace": namespace} - if description: - body["description"] = description - - resp = _request("post", "/v1/experiments", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Experiment '{name}' created") - console.print(f" ID: {data.get('id', '-')}") - if description: - console.print(f" Description: {description}") - - -@experiments.command(name="list") -@click.option("--namespace", "-n", default="default", help="Namespace") -def experiments_list(namespace: str): - """List experiments.""" - resp = _request("get", "/v1/experiments", params={"namespace": namespace}) - data = check_response(resp) - items = data.get("experiments", data.get("items", [])) - - table = Table(title="Experiments", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Runs", style="green") - table.add_column("Best Metric", style="yellow") - table.add_column("Created", style="dim") - - for e in items: - table.add_row( - e.get("name", ""), - str(e.get("run_count", 0)), - str(e.get("best_metric", "-")), - str(e.get("created_at", ""))[:19], - ) - - console.print(table) - if not items: - console.print("[dim]No experiments found[/dim]") - - -@experiments.command(name="runs") -@click.argument("experiment") -@click.option( - "--status", - type=click.Choice(["running", "completed", "failed", "all"]), - default="all", -) -def experiments_runs(experiment: str, status: str): - """List runs in an experiment.""" - params = {} - if status != "all": - params["status"] = status - - resp = _request("get", f"/v1/experiments/{experiment}/runs", params=params) - data = check_response(resp) - items = data.get("runs", data.get("items", [])) - - table = Table(title=f"Runs in '{experiment}'", box=box.ROUNDED) - table.add_column("Run ID", style="cyan") - table.add_column("Status", style="green") - table.add_column("Metrics", style="yellow") - table.add_column("Duration", style="dim") - - for r in items: - r_status = r.get("status", "unknown") - style = {"running": "cyan", "completed": "green", "failed": "red"}.get( - r_status, "white" - ) - metrics = ( - json.dumps(r.get("metrics", {}), default=str)[:40] - if r.get("metrics") - else "-" - ) - table.add_row( - str(r.get("id", ""))[:16], - f"[{style}]{r_status}[/{style}]", - metrics, - f"{r.get('duration_ms', '-')}ms" if r.get("duration_ms") else "-", - ) - - console.print(table) - if not items: - console.print("[dim]No runs found[/dim]") - - -@experiments.command(name="compare") -@click.argument("run_ids", nargs=-1, required=True) -def experiments_compare(run_ids: tuple): - """Compare experiment runs.""" - resp = _request("post", "/v1/experiments/compare", json={"run_ids": list(run_ids)}) - data = check_response(resp) - - console.print(f"[cyan]Comparing {len(run_ids)} runs...[/cyan]") - table = Table(title="Run Comparison", box=box.ROUNDED) - table.add_column("Metric", style="cyan") - for run_id in run_ids: - table.add_column(run_id[:8], style="white") - - for metric in data.get("metrics", []): - row = [metric.get("name", "")] - for run_id in run_ids: - row.append(str(metric.get("values", {}).get(run_id, "-"))) - table.add_row(*row) - - console.print(table) - - -@experiments.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True) -def experiments_delete(name: str, force: bool): - """Delete an experiment.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete experiment '{name}' and all runs?[/red]"): - return - - resp = _request("delete", f"/v1/experiments/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Experiment '{name}' deleted") - - -# ============================================================================ -# Hyperparameter Tuning (Katib-style) -# ============================================================================ - - -@ml_group.group() -def tune(): - """Hyperparameter tuning (Katib-style AutoML).""" - pass - - -@tune.command(name="create") -@click.argument("name") -@click.option("--experiment", "-e", required=True, help="Parent experiment") -@click.option("--objective", "-o", required=True, help="Metric to optimize") -@click.option( - "--goal", "-g", type=click.Choice(["minimize", "maximize"]), default="minimize" -) -@click.option( - "--algorithm", - "-a", - type=click.Choice(["random", "grid", "bayesian", "hyperband", "tpe"]), - default="bayesian", -) -@click.option("--max-trials", "-m", default=10, help="Maximum trials") -@click.option("--parallel-trials", "-p", default=2, help="Parallel trials") -@click.option("--config", "-c", help="Tuning config file (YAML)") -def tune_create( - name: str, - experiment: str, - objective: str, - goal: str, - algorithm: str, - max_trials: int, - parallel_trials: int, - config: str, -): - """Create a hyperparameter tuning job. - - \b - Examples: - hanzo ml tune create hpo-1 -e my-exp -o val_loss --algorithm bayesian - hanzo ml tune create grid-search -e exp -o accuracy -g maximize -a grid -m 100 - hanzo ml tune create custom -e exp -o f1 -c tuning.yaml - """ - body = { - "name": name, - "experiment": experiment, - "objective": objective, - "goal": goal, - "algorithm": algorithm, - "max_trials": max_trials, - "parallel_trials": parallel_trials, - } - if config: - with open(config) as f: - body["config"] = json.load(f) - - console.print(f"[cyan]Creating tuning job '{name}'...[/cyan]") - resp = _request("post", "/v1/tune", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Tuning job created") - console.print(f" ID: {data.get('id', '-')}") - console.print(f" Experiment: {experiment}") - console.print(f" Objective: {objective} ({goal})") - console.print(f" Algorithm: {algorithm}") - console.print(f" Max trials: {max_trials}") - - -@tune.command(name="list") -@click.option("--experiment", "-e", help="Filter by experiment") -def tune_list(experiment: str): - """List tuning jobs.""" - params = {} - if experiment: - params["experiment"] = experiment - - resp = _request("get", "/v1/tune", params=params) - data = check_response(resp) - items = data.get("jobs", data.get("items", [])) - - table = Table(title="Tuning Jobs", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Algorithm", style="white") - table.add_column("Trials", style="green") - table.add_column("Best", style="yellow") - table.add_column("Status", style="dim") - - for j in items: - j_status = j.get("status", "unknown") - style = {"running": "cyan", "completed": "green", "failed": "red"}.get( - j_status, "white" - ) - table.add_row( - j.get("name", ""), - j.get("algorithm", "-"), - f"{j.get('completed_trials', 0)}/{j.get('max_trials', 0)}", - str(j.get("best_metric", "-")), - f"[{style}]{j_status}[/{style}]", - ) - - console.print(table) - if not items: - console.print("[dim]No tuning jobs found[/dim]") - - -@tune.command(name="trials") -@click.argument("name") -@click.option("--best", "-b", type=int, help="Show top N trials") -def tune_trials(name: str, best: int): - """List trials in a tuning job.""" - params = {} - if best: - params["top"] = best - - resp = _request("get", f"/v1/tune/{name}/trials", params=params) - data = check_response(resp) - items = data.get("trials", data.get("items", [])) - - table = Table(title=f"Trials for '{name}'", box=box.ROUNDED) - table.add_column("Trial", style="cyan") - table.add_column("Parameters", style="white") - table.add_column("Metric", style="yellow") - table.add_column("Status", style="dim") - - for t in items: - params_str = json.dumps(t.get("parameters", {}), default=str)[:40] - table.add_row( - str(t.get("id", "")), - params_str, - str(t.get("metric", "-")), - t.get("status", "-"), - ) - - console.print(table) - if not items: - console.print("[dim]No trials found[/dim]") - - -@tune.command(name="best") -@click.argument("name") -def tune_best(name: str): - """Get best trial parameters.""" - resp = _request("get", f"/v1/tune/{name}/best") - data = check_response(resp) - - console.print(f"[cyan]Best trial for '{name}':[/cyan]") - if data.get("parameters"): - console.print(f" Metric: {data.get('metric', '-')}") - console.print(f" Parameters:") - for k, v in data["parameters"].items(): - console.print(f" {k}: {v}") - else: - console.print("[dim]No trials completed[/dim]") - - -@tune.command(name="stop") -@click.argument("name") -def tune_stop(name: str): - """Stop a tuning job.""" - resp = _request("post", f"/v1/tune/{name}/stop") - check_response(resp) - console.print(f"[green]โœ“[/green] Tuning job '{name}' stopped") - - -# ============================================================================ -# Feature Store -# ============================================================================ - - -@ml_group.group() -def features(): - """Manage feature store.""" - pass - - -@features.command(name="create") -@click.argument("name") -@click.option("--description", "-d", help="Feature group description") -@click.option("--schema", "-s", help="Schema file (YAML/JSON)") -@click.option("--source", help="Data source (table, stream)") -def features_create(name: str, description: str, schema: str, source: str): - """Create a feature group.""" - body = {"name": name} - if description: - body["description"] = description - if schema: - with open(schema) as f: - body["schema"] = json.load(f) - if source: - body["source"] = source - - resp = _request("post", "/v1/features", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Feature group '{name}' created") - console.print(f" ID: {data.get('id', '-')}") - - -@features.command(name="list") -def features_list(): - """List feature groups.""" - resp = _request("get", "/v1/features") - data = check_response(resp) - items = data.get("feature_groups", data.get("items", [])) - - table = Table(title="Feature Groups", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Features", style="green") - table.add_column("Entities", style="white") - table.add_column("Updated", style="dim") - - for fg in items: - table.add_row( - fg.get("name", ""), - str(fg.get("feature_count", 0)), - fg.get("entity", "-"), - str(fg.get("updated_at", ""))[:19], - ) - - console.print(table) - if not items: - console.print("[dim]No feature groups found[/dim]") - - -@features.command(name="describe") -@click.argument("name") -def features_describe(name: str): - """Show feature group details.""" - resp = _request("get", f"/v1/features/{name}") - data = check_response(resp) - - console.print( - Panel( - f"[cyan]Name:[/cyan] {data.get('name', name)}\n" - f"[cyan]Features:[/cyan] {data.get('feature_count', 0)}\n" - f"[cyan]Entities:[/cyan] {data.get('entity', '-')}\n" - f"[cyan]Source:[/cyan] {data.get('source', '-')}\n" - f"[cyan]Updated:[/cyan] {str(data.get('updated_at', ''))[:19]}", - title="Feature Group", - border_style="cyan", - ) - ) - - -@features.command(name="ingest") -@click.argument("name") -@click.option( - "--from", "source", required=True, help="Data source (file, table, stream)" -) -@click.option("--mode", type=click.Choice(["append", "overwrite"]), default="append") -def features_ingest(name: str, source: str, mode: str): - """Ingest data into feature group.""" - console.print(f"[cyan]Ingesting into '{name}'...[/cyan]") - resp = _request( - "post", f"/v1/features/{name}/ingest", json={"source": source, "mode": mode} - ) - data = check_response(resp) - console.print(f"[green]โœ“[/green] Ingestion complete") - console.print(f" Records: {data.get('records', 0)}") - - -@features.command(name="get") -@click.argument("feature_group") -@click.option("--entity", "-e", required=True, help="Entity ID(s)") -@click.option("--features", "-f", help="Specific features (comma-separated)") -@click.option("--timestamp", "-t", help="Point-in-time (for historical features)") -def features_get(feature_group: str, entity: str, features: str, timestamp: str): - """Get feature values.""" - params = {"entity": entity} - if features: - params["features"] = features - if timestamp: - params["timestamp"] = timestamp - - resp = _request("get", f"/v1/features/{feature_group}/values", params=params) - data = check_response(resp) - values = data.get("values", data.get("features", {})) - - console.print(f"[cyan]Features for '{entity}':[/cyan]") - if values: - for k, v in values.items(): - console.print(f" {k}: {v}") - else: - console.print("[dim]No features found[/dim]") - - -@features.command(name="materialize") -@click.argument("name") -@click.option("--start", "-s", help="Start time") -@click.option("--end", "-e", help="End time") -def features_materialize(name: str, start: str, end: str): - """Materialize features to online store.""" - body = {} - if start: - body["start"] = start - if end: - body["end"] = end - - console.print(f"[cyan]Materializing '{name}'...[/cyan]") - resp = _request("post", f"/v1/features/{name}/materialize", json=body) - data = check_response(resp) - console.print(f"[green]โœ“[/green] Features materialized to online store") - console.print(f" Records: {data.get('records', 0)}") - - -# ============================================================================ -# Datasets -# ============================================================================ - - -@ml_group.group() -def datasets(): - """Manage ML datasets.""" - pass - - -@datasets.command(name="create") -@click.argument("name") -@click.option("--from", "source", required=True, help="Source path or URI") -@click.option( - "--format", - "fmt", - type=click.Choice(["csv", "parquet", "tfrecord", "jsonl"]), - help="Data format", -) -@click.option("--split", help="Train/val/test split (e.g., 80:10:10)") -def datasets_create(name: str, source: str, fmt: str, split: str): - """Create a versioned dataset.""" - body = {"name": name, "source": source} - if fmt: - body["format"] = fmt - if split: - body["split"] = split - - resp = _request("post", "/v1/datasets", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Dataset '{name}' created") - console.print(f" Version: {data.get('version', '1')}") - console.print(f" Source: {source}") - if split: - console.print(f" Split: {split}") - - -@datasets.command(name="list") -def datasets_list(): - """List datasets.""" - resp = _request("get", "/v1/datasets") - data = check_response(resp) - items = data.get("datasets", data.get("items", [])) - - table = Table(title="Datasets", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Version", style="white") - table.add_column("Size", style="green") - table.add_column("Format", style="yellow") - table.add_column("Created", style="dim") - - for d in items: - table.add_row( - d.get("name", ""), - str(d.get("version", "-")), - d.get("size", "-"), - d.get("format", "-"), - str(d.get("created_at", ""))[:19], - ) - - console.print(table) - if not items: - console.print("[dim]No datasets found[/dim]") - - -@datasets.command(name="versions") -@click.argument("name") -def datasets_versions(name: str): - """List dataset versions.""" - resp = _request("get", f"/v1/datasets/{name}/versions") - data = check_response(resp) - items = data.get("versions", data.get("items", [])) - - table = Table(title=f"Versions of '{name}'", box=box.ROUNDED) - table.add_column("Version", style="cyan") - table.add_column("Size", style="green") - table.add_column("Created", style="dim") - - for v in items: - table.add_row( - str(v.get("version", "")), - v.get("size", "-"), - str(v.get("created_at", ""))[:19], - ) - - console.print(table) - if not items: - console.print("[dim]No versions found[/dim]") - - -# ============================================================================ -# AutoML -# ============================================================================ - - -@ml_group.group() -def automl(): - """Automated Machine Learning.""" - pass - - -@automl.command(name="create") -@click.argument("name") -@click.option("--dataset", "-d", required=True, help="Training dataset") -@click.option("--target", "-t", required=True, help="Target column") -@click.option( - "--task", - type=click.Choice(["classification", "regression", "forecasting", "nlp", "vision"]), - required=True, -) -@click.option("--time-limit", default=3600, help="Time limit in seconds") -@click.option("--metric", "-m", help="Optimization metric") -def automl_create( - name: str, dataset: str, target: str, task: str, time_limit: int, metric: str -): - """Create an AutoML job. - - \b - Examples: - hanzo ml automl create fraud-detect -d transactions -t is_fraud --task classification - hanzo ml automl create sales-forecast -d sales -t revenue --task forecasting - hanzo ml automl create sentiment -d reviews -t label --task nlp --time-limit 7200 - """ - body = { - "name": name, - "dataset": dataset, - "target": target, - "task": task, - "time_limit": time_limit, - } - if metric: - body["metric"] = metric - - console.print(f"[cyan]Creating AutoML job '{name}'...[/cyan]") - resp = _request("post", "/v1/automl", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] AutoML job started") - console.print(f" ID: {data.get('id', '-')}") - console.print(f" Dataset: {dataset}") - console.print(f" Target: {target}") - console.print(f" Task: {task}") - console.print(f" Time limit: {time_limit}s") - - -@automl.command(name="list") -def automl_list(): - """List AutoML jobs.""" - resp = _request("get", "/v1/automl") - data = check_response(resp) - items = data.get("jobs", data.get("items", [])) - - table = Table(title="AutoML Jobs", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Task", style="white") - table.add_column("Best Model", style="green") - table.add_column("Metric", style="yellow") - table.add_column("Status", style="dim") - - for j in items: - j_status = j.get("status", "unknown") - style = {"running": "cyan", "completed": "green", "failed": "red"}.get( - j_status, "white" - ) - table.add_row( - j.get("name", ""), - j.get("task", "-"), - j.get("best_model", "-"), - str(j.get("best_metric", "-")), - f"[{style}]{j_status}[/{style}]", - ) - - console.print(table) - if not items: - console.print("[dim]No AutoML jobs found[/dim]") - - -@automl.command(name="status") -@click.argument("name") -def automl_status(name: str): - """Show AutoML job status.""" - resp = _request("get", f"/v1/automl/{name}") - data = check_response(resp) - - status = data.get("status", "unknown") - status_style = {"running": "cyan", "completed": "green", "failed": "red"}.get( - status, "yellow" - ) - - console.print( - Panel( - f"[cyan]Job:[/cyan] {data.get('name', name)}\n" - f"[cyan]Status:[/cyan] [{status_style}]{status}[/{status_style}]\n" - f"[cyan]Progress:[/cyan] {data.get('progress', 0)}%\n" - f"[cyan]Models tried:[/cyan] {data.get('models_tried', 0)}\n" - f"[cyan]Best so far:[/cyan] {data.get('best_metric', '-')}\n" - f"[cyan]Time remaining:[/cyan] {data.get('time_remaining', '-')}", - title="AutoML Status", - border_style="cyan", - ) - ) - - -@automl.command(name="leaderboard") -@click.argument("name") -@click.option("--top", "-n", default=10, help="Show top N models") -def automl_leaderboard(name: str, top: int): - """Show AutoML leaderboard.""" - resp = _request("get", f"/v1/automl/{name}/leaderboard", params={"top": top}) - data = check_response(resp) - items = data.get("models", data.get("items", [])) - - table = Table(title=f"Leaderboard for '{name}'", box=box.ROUNDED) - table.add_column("Rank", style="cyan") - table.add_column("Model", style="white") - table.add_column("Metric", style="green") - table.add_column("Training Time", style="dim") - - for i, m in enumerate(items, 1): - table.add_row( - str(i), - m.get("model", "-"), - str(m.get("metric", "-")), - f"{m.get('training_time_s', '-')}s" if m.get("training_time_s") else "-", - ) - - console.print(table) - if not items: - console.print("[dim]No models trained yet[/dim]") - - -@automl.command(name="deploy") -@click.argument("name") -@click.option("--model", "-m", help="Specific model (default: best)") -@click.option("--endpoint", "-e", help="Endpoint name") -def automl_deploy(name: str, model: str, endpoint: str): - """Deploy best AutoML model.""" - body = {} - if model: - body["model"] = model - if endpoint: - body["endpoint"] = endpoint - - console.print(f"[cyan]Deploying best model from '{name}'...[/cyan]") - resp = _request("post", f"/v1/automl/{name}/deploy", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Model deployed") - console.print(f" Endpoint: {data.get('endpoint', '-')}") - console.print(f" Model: {data.get('model', '-')}") diff --git a/pkg/hanzo/src/hanzo/commands/network.py b/pkg/hanzo/src/hanzo/commands/network.py deleted file mode 100644 index a70e43df5..000000000 --- a/pkg/hanzo/src/hanzo/commands/network.py +++ /dev/null @@ -1,859 +0,0 @@ -"""Network commands for agent networks.""" - -import click -from rich.table import Table -from rich.progress import Progress, TextColumn, SpinnerColumn - -from ..utils.output import console - - -@click.group(name="network") -def network_group(): - """Manage agent networks.""" - pass - - -@network_group.command() -@click.argument("prompt") -@click.option("--agents", "-a", type=int, default=3, help="Number of agents") -@click.option("--model", "-m", help="Model to use") -@click.option( - "--mode", - type=click.Choice(["local", "distributed", "hybrid"]), - default="hybrid", - help="Execution mode", -) -@click.option("--consensus", is_flag=True, help="Require consensus") -@click.option("--timeout", "-t", type=int, default=300, help="Timeout in seconds") -@click.pass_context -async def dispatch( - ctx, prompt: str, agents: int, model: str, mode: str, consensus: bool, timeout: int -): - """Dispatch work to agent network.""" - try: - from hanzo_network import NetworkDispatcher - except ImportError: - console.print("[red]Error:[/red] hanzo-network not installed") - console.print("Install with: pip install hanzo[network]") - return - - dispatcher = NetworkDispatcher(mode=mode) - - with Progress( - SpinnerColumn(), - TextColumn("[progress.description]{task.description}"), - console=console, - ) as progress: - task = progress.add_task("Dispatching to network...", total=None) - - try: - # Create job - job = await dispatcher.create_job( - prompt=prompt, - num_agents=agents, - model=model, - consensus=consensus, - timeout=timeout, - ) - - progress.update(task, description=f"Job {job['id']} - Finding agents...") - - # Execute job - result = await dispatcher.execute_job(job) - - progress.update(task, completed=True) - - except Exception as e: - progress.stop() - console.print(f"[red]Dispatch failed: {e}[/red]") - return - - # Show results - console.print(f"\n[green]โœ“[/green] Job completed") - console.print(f" ID: {result['job_id']}") - console.print(f" Agents: {result['num_agents']}") - console.print(f" Duration: {result['duration']}s") - - if consensus: - console.print(f" Consensus: {result.get('consensus_reached', False)}") - - console.print("\n[cyan]Results:[/cyan]") - - if consensus and result.get("consensus_result"): - console.print(result["consensus_result"]) - else: - for i, agent_result in enumerate(result["agent_results"], 1): - console.print(f"\n[yellow]Agent {i} ({agent_result['agent_id']}):[/yellow]") - console.print(agent_result["result"]) - - -@network_group.command() -@click.option( - "--mode", - type=click.Choice(["local", "distributed", "all"]), - default="all", - help="Network mode", -) -@click.pass_context -async def agents(ctx, mode: str): - """List available agents in network.""" - try: - from hanzo_network import get_network_agents - except ImportError: - console.print("[red]Error:[/red] hanzo-network not installed") - return - - with console.status("Discovering agents..."): - try: - agents = await get_network_agents(mode=mode) - except Exception as e: - console.print(f"[red]Failed to discover agents: {e}[/red]") - return - - if not agents: - console.print("[yellow]No agents found[/yellow]") - if mode == "local": - console.print("Start local agents with: hanzo agent start") - return - - # Group by type - local_agents = [a for a in agents if a["type"] == "local"] - network_agents = [a for a in agents if a["type"] == "network"] - - if local_agents: - table = Table(title="Local Agents") - table.add_column("ID", style="cyan") - table.add_column("Name", style="green") - table.add_column("Model", style="yellow") - table.add_column("Status", style="blue") - table.add_column("Jobs", style="magenta") - - for agent in local_agents: - table.add_row( - agent["id"][:8], - agent["name"], - agent.get("model", "default"), - agent["status"], - str(agent.get("jobs_completed", 0)), - ) - - console.print(table) - - if network_agents: - table = Table(title="Network Agents") - table.add_column("ID", style="cyan") - table.add_column("Location", style="green") - table.add_column("Model", style="yellow") - table.add_column("Latency", style="blue") - table.add_column("Cost", style="magenta") - - for agent in network_agents: - table.add_row( - agent["id"][:8], - agent.get("location", "unknown"), - agent.get("model", "various"), - f"{agent.get('latency', 0)}ms", - f"${agent.get('cost_per_token', 0):.4f}", - ) - - console.print(table) - - -@network_group.command() -@click.option("--active", is_flag=True, help="Show only active jobs") -@click.option("--limit", "-n", type=int, default=10, help="Number of jobs to show") -@click.pass_context -async def jobs(ctx, active: bool, limit: int): - """List network jobs.""" - try: - from hanzo_network import get_network_jobs - except ImportError: - console.print("[red]Error:[/red] hanzo-network not installed") - return - - with console.status("Loading jobs..."): - try: - jobs = await get_network_jobs(active_only=active, limit=limit) - except Exception as e: - console.print(f"[red]Failed to load jobs: {e}[/red]") - return - - if not jobs: - console.print("[yellow]No jobs found[/yellow]") - return - - table = Table(title="Network Jobs") - table.add_column("ID", style="cyan") - table.add_column("Status", style="green") - table.add_column("Agents", style="yellow") - table.add_column("Created", style="blue") - table.add_column("Duration", style="magenta") - - for job in jobs: - table.add_row( - job["id"][:8], - job["status"], - str(job["num_agents"]), - job["created_at"], - f"{job.get('duration', 0)}s" if job.get("duration") else "-", - ) - - console.print(table) - - -@network_group.command() -@click.argument("job_id") -@click.pass_context -async def job(ctx, job_id: str): - """Show job details.""" - try: - from hanzo_network import get_job_details - except ImportError: - console.print("[red]Error:[/red] hanzo-network not installed") - return - - with console.status("Loading job details..."): - try: - job = await get_job_details(job_id) - except Exception as e: - console.print(f"[red]Failed to load job: {e}[/red]") - return - - console.print(f"[cyan]Job {job_id}[/cyan]") - console.print(f" Status: {job['status']}") - console.print(f" Created: {job['created_at']}") - console.print(f" Agents: {job['num_agents']}") - console.print(f" Mode: {job['mode']}") - - if job["status"] == "completed": - console.print(f" Duration: {job['duration']}s") - console.print(f" Cost: ${job.get('total_cost', 0):.4f}") - - console.print(f"\n[cyan]Prompt:[/cyan]") - console.print(job["prompt"]) - - if job["status"] == "completed" and job.get("results"): - console.print("\n[cyan]Results:[/cyan]") - for i, result in enumerate(job["results"], 1): - console.print(f"\n[yellow]Agent {i}:[/yellow]") - console.print(result["content"]) - - -@network_group.command() -@click.option("--name", "-n", default="default", help="Swarm name") -@click.option("--agents", "-a", type=int, default=5, help="Number of agents") -@click.option("--model", "-m", help="Model to use") -@click.pass_context -async def swarm(ctx, name: str, agents: int, model: str): - """Start a local agent swarm.""" - try: - from hanzo_network import LocalSwarm - except ImportError: - console.print("[red]Error:[/red] hanzo-network not installed") - return - - swarm = LocalSwarm(name=name, size=agents, model=model) - - with Progress( - SpinnerColumn(), - TextColumn("[progress.description]{task.description}"), - console=console, - ) as progress: - task = progress.add_task("Starting swarm...", total=None) - - try: - await swarm.start() - progress.update(task, completed=True) - except Exception as e: - progress.stop() - console.print(f"[red]Failed to start swarm: {e}[/red]") - return - - console.print(f"[green]โœ“[/green] Swarm '{name}' started with {agents} agents") - console.print("Use 'hanzo network dispatch --mode local' to send work to swarm") - console.print("\nPress Ctrl+C to stop swarm") - - try: - # Keep swarm running - await swarm.run_forever() - except KeyboardInterrupt: - console.print("\n[yellow]Stopping swarm...[/yellow]") - await swarm.stop() - console.print("[green]โœ“[/green] Swarm stopped") - - -@network_group.command() -@click.pass_context -async def stats(ctx): - """Show network statistics.""" - try: - from hanzo_network import get_network_stats - except ImportError: - console.print("[red]Error:[/red] hanzo-network not installed") - return - - with console.status("Loading network stats..."): - try: - stats = await get_network_stats() - except Exception as e: - console.print(f"[red]Failed to load stats: {e}[/red]") - return - - console.print("[cyan]Network Statistics[/cyan]") - console.print(f" Total agents: {stats['total_agents']}") - console.print(f" Active agents: {stats['active_agents']}") - console.print(f" Total jobs: {stats['total_jobs']}") - console.print(f" Active jobs: {stats['active_jobs']}") - console.print(f" Success rate: {stats['success_rate']}%") - - console.print(f"\n[cyan]Performance:[/cyan]") - console.print(f" Average latency: {stats['avg_latency']}ms") - console.print(f" Average job time: {stats['avg_job_time']}s") - console.print(f" Throughput: {stats['throughput']} jobs/min") - - console.print(f"\n[cyan]Economics:[/cyan]") - console.print(f" Total tokens: {stats['total_tokens']:,}") - console.print(f" Average cost: ${stats['avg_cost']:.4f}/job") - console.print(f" Total cost: ${stats['total_cost']:.2f}") - - -@network_group.command() -@click.option("--enable/--disable", default=True, help="Enable or disable discovery") -@click.pass_context -async def discovery(ctx, enable: bool): - """Configure network discovery.""" - try: - from hanzo_network import configure_discovery - except ImportError: - console.print("[red]Error:[/red] hanzo-network not installed") - return - - try: - await configure_discovery(enabled=enable) - - if enable: - console.print("[green]โœ“[/green] Network discovery enabled") - console.print("Your agents will be discoverable by the network") - else: - console.print("[green]โœ“[/green] Network discovery disabled") - console.print("Your agents will only be accessible locally") - - except Exception as e: - console.print(f"[red]Failed to configure discovery: {e}[/red]") - - -@network_group.command() -@click.option("--json", is_flag=True, help="Output as JSON") -@click.pass_context -def topology(ctx, json: bool): - """Show local device topology and GPU information.""" - try: - from hanzo_network.topology.topology import Topology - from hanzo_network.topology.device_capabilities import device_capabilities - except ImportError: - console.print("[red]Error:[/red] hanzo-network not installed") - console.print("Install with: pip install hanzo[network]") - return - - # Get device capabilities - caps = device_capabilities() - - if json: - import json as json_lib - - console.print(json_lib.dumps(caps.to_dict(), indent=2)) - return - - # Display in nice table format - console.print("\n[cyan]Device Topology[/cyan]") - console.print("=" * 60) - - table = Table(title="Local Device Information") - table.add_column("Property", style="cyan", no_wrap=True) - table.add_column("Value", style="green") - - table.add_row("Model", caps.model) - table.add_row("Chip/GPU", caps.chip) - table.add_row("Memory", f"{caps.memory:,} MB") - table.add_row("FP32 Performance", f"{caps.flops.fp32:.2f} TFLOPS") - table.add_row("FP16 Performance", f"{caps.flops.fp16:.2f} TFLOPS") - table.add_row("INT8 Performance", f"{caps.flops.int8:.2f} TFLOPS") - - console.print(table) - console.print() - - # Show GPU availability - gpu_available = caps.flops.fp32 > 0 and ( - "GPU" in caps.chip.upper() - or "NVIDIA" in caps.chip.upper() - or "AMD" in caps.chip.upper() - or "APPLE M" in caps.chip.upper() # Apple Silicon has integrated GPU - ) - - if gpu_available: - console.print( - "[green]โœ“[/green] GPU/Accelerator detected and available for AI workloads" - ) - if "APPLE M" in caps.chip.upper(): - console.print( - " [dim]Apple Silicon Neural Engine available for on-device AI[/dim]" - ) - else: - console.print("[yellow]โš [/yellow] No GPU detected - using CPU only") - console.print(" To enable GPU support:") - console.print(" - NVIDIA: Install CUDA and nvidia-smi") - console.print(" - AMD: Install ROCm and rocm-smi") - console.print() - - # Show QR code for easy device connection - console.print("[bold cyan]Connect Other Devices:[/bold cyan]") - try: - import json as json_lib - import socket - - import qrcode - - # Get local IP - try multiple methods - local_ip = "localhost" - try: - # Method 1: Connect to external address to find local IP - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - s.connect(("8.8.8.8", 80)) - local_ip = s.getsockname()[0] - s.close() - except Exception: - try: - # Method 2: Use hostname - hostname = socket.gethostname() - local_ip = socket.gethostbyname(hostname) - except Exception: - # Method 3: Default to localhost - local_ip = "127.0.0.1" - - # Generate connection data - connection_data = json_lib.dumps( - { - "node_id": f"{caps.model.lower().replace(' ', '-')}", - "model": caps.model, - "chip": caps.chip, - "memory": caps.memory, - "flops": { - "fp32": caps.flops.fp32, - "fp16": caps.flops.fp16, - "int8": caps.flops.int8, - }, - "host": local_ip, - "port": 8080, # Default port for device connection - } - ) - - # Generate QR code - qr = qrcode.QRCode( - version=1, - error_correction=qrcode.constants.ERROR_CORRECT_L, - box_size=1, - border=1, - ) - qr.add_data(connection_data) - qr.make(fit=True) - - console.print(f"[dim]Scan this QR code from another device to connect:[/dim]") - qr.print_ascii(invert=True) - console.print(f"\n[dim]Or run on other device:[/dim]") - console.print(f" [cyan]hanzo network join '{connection_data}'[/cyan]") - console.print(f"\n[dim]Device IP:[/dim] {local_ip}") - except Exception as e: - console.print(f"[dim]QR code generation failed: {e}[/dim]") - - console.print() - - -@network_group.command(name="topology-add") -@click.argument("node_id") -@click.option("--model", "-m", required=True, help="Device model name") -@click.option("--chip", "-c", required=True, help="Chip/GPU name") -@click.option("--memory", type=int, required=True, help="Memory in MB") -@click.option("--fp32", type=float, default=0, help="FP32 TFLOPS") -@click.option("--fp16", type=float, default=0, help="FP16 TFLOPS") -@click.option("--int8", type=float, default=0, help="INT8 TFLOPS") -@click.option("--host", help="Host/IP address for remote access") -@click.option("--port", type=int, help="Port for remote access") -@click.option("--qr", is_flag=True, help="Generate QR code for easy device joining") -@click.pass_context -def topology_add( - ctx, - node_id: str, - model: str, - chip: str, - memory: int, - fp32: float, - fp16: float, - int8: float, - host: str, - port: int, - qr: bool, -): - """Add a GPU/device node to the network topology.""" - try: - import os - import json as json_lib - - from hanzo_network.topology.topology import Topology - from hanzo_network.topology.device_capabilities import ( - DeviceFlops, - DeviceCapabilities, - ) - except ImportError: - console.print("[red]Error:[/red] hanzo-network not installed") - console.print("Install with: pip install hanzo[network]") - return - - # Create device capabilities - caps = DeviceCapabilities( - model=model, - chip=chip, - memory=memory, - flops=DeviceFlops(fp32=fp32, fp16=fp16, int8=int8), - ) - - # Load or create topology - topology_file = os.path.expanduser("~/.hanzo/topology.json") - os.makedirs(os.path.dirname(topology_file), exist_ok=True) - - topo = Topology() - if os.path.exists(topology_file): - with open(topology_file, "r") as f: - data = json_lib.load(f) - # Reconstruct topology from JSON - for nid, ncaps in data.get("nodes", {}).items(): - topo.update_node(nid, DeviceCapabilities.model_validate(ncaps)) - - # Add new node - topo.update_node(node_id, caps) - - # Save topology - with open(topology_file, "w") as f: - json_lib.dump(topo.to_json(), f, indent=2) - - console.print(f"[green]โœ“[/green] Added node '{node_id}' to topology") - console.print(f" Model: {model}") - console.print(f" Chip: {chip}") - console.print(f" Memory: {memory:,} MB") - console.print(f" Performance: {fp32:.2f} TFLOPS (FP32)") - console.print(f"\nTopology saved to: {topology_file}") - - # Generate QR code if requested - if qr or (host and port): - try: - import qrcode - except ImportError: - console.print( - "\n[yellow]QR code generation requires qrcode library[/yellow]" - ) - console.print("Install with: pip install qrcode[pil]") - return - - # Create connection info - connection_info = { - "node_id": node_id, - "model": model, - "chip": chip, - "memory": memory, - "flops": {"fp32": fp32, "fp16": fp16, "int8": int8}, - } - - if host: - connection_info["host"] = host - if port: - connection_info["port"] = port - - # Generate QR code data - qr_data = json_lib.dumps(connection_info) - - # Create QR code - qr_obj = qrcode.QRCode( - version=1, - error_correction=qrcode.constants.ERROR_CORRECT_L, - box_size=10, - border=4, - ) - qr_obj.add_data(qr_data) - qr_obj.make(fit=True) - - # Print QR code to terminal - console.print("\n[cyan]QR Code for Device Join:[/cyan]") - qr_obj.print_ascii(invert=True) - - # Also print connection command - console.print("\n[cyan]Connection Info:[/cyan]") - if host and port: - console.print(f" Host: {host}:{port}") - console.print(f"\nScan QR code or run:") - console.print(f" hanzo network join '{qr_data}'") - console.print() - - -@network_group.command(name="join") -@click.argument("connection_data") -@click.pass_context -def join_network(ctx, connection_data: str): - """Join a GPU node to the network using QR code data or connection info.""" - try: - import os - import json as json_lib - - from hanzo_network.topology.topology import Topology - from hanzo_network.topology.device_capabilities import ( - DeviceFlops, - DeviceCapabilities, - ) - except ImportError: - console.print("[red]Error:[/red] hanzo-network not installed") - console.print("Install with: pip install hanzo[network]") - return - - try: - # Parse connection data - conn_info = json_lib.loads(connection_data) - except json_lib.JSONDecodeError: - console.print("[red]Error:[/red] Invalid connection data") - console.print("Expected JSON format from QR code") - return - - # Extract node information - node_id = conn_info.get("node_id") - model = conn_info.get("model") - chip = conn_info.get("chip") - memory = conn_info.get("memory") - flops_data = conn_info.get("flops", {}) - host = conn_info.get("host") - port = conn_info.get("port") - - if not all([node_id, model, chip, memory]): - console.print("[red]Error:[/red] Missing required node information") - return - - # Create device capabilities - caps = DeviceCapabilities( - model=model, - chip=chip, - memory=memory, - flops=DeviceFlops( - fp32=flops_data.get("fp32", 0), - fp16=flops_data.get("fp16", 0), - int8=flops_data.get("int8", 0), - ), - ) - - # Load or create topology - topology_file = os.path.expanduser("~/.hanzo/topology.json") - os.makedirs(os.path.dirname(topology_file), exist_ok=True) - - topo = Topology() - if os.path.exists(topology_file): - with open(topology_file, "r") as f: - data = json_lib.load(f) - for nid, ncaps in data.get("nodes", {}).items(): - topo.update_node(nid, DeviceCapabilities.model_validate(ncaps)) - - # Add new node - topo.update_node(node_id, caps) - - # Save topology - with open(topology_file, "w") as f: - json_lib.dump(topo.to_json(), f, indent=2) - - console.print(f"[green]โœ“[/green] Joined node '{node_id}' to network") - console.print(f" Model: {model}") - console.print(f" Chip: {chip}") - if host and port: - console.print(f" Location: {host}:{port}") - console.print(f" Performance: {flops_data.get('fp32', 0):.2f} TFLOPS (FP32)") - console.print(f"\nTopology updated: {topology_file}") - - -@network_group.command(name="models") -@click.option("--endpoint", help="Gateway endpoint (default: gateway.hanzo.ai)") -@click.pass_context -def models(ctx, endpoint: str): - """List models available on gateway.hanzo.ai.""" - import httpx - - if not endpoint: - from hanzo.orchestrator_config import get_default_router_endpoint - - endpoint = get_default_router_endpoint() - - console.print(f"\n[cyan]Models Available on {endpoint}[/cyan]") - console.print("=" * 60) - - try: - # Try to fetch models from gateway - response = httpx.get(f"{endpoint}/v1/models", timeout=5.0) - - if response.status_code == 200: - data = response.json() - all_models = data.get("data", []) - - # Filter out embedding models - only show LLMs - embedding_keywords = ["embedding", "voyage", "embed", "text-embedding"] - models_list = [ - model - for model in all_models - if not any( - keyword in model.get("id", "").lower() - for keyword in embedding_keywords - ) - ] - - if models_list: - # Create table - table = Table(title=f"Gateway LLMs (Chat Models)") - table.add_column("#", style="dim") - table.add_column("Model ID", style="cyan") - table.add_column("Tier", style="yellow") - table.add_column("Provider", style="green") - - for i, model in enumerate(models_list, 1): - model_id = model.get("id", "unknown") - - # All gateway models are FREE! ๐ŸŽ‰ - tier = "Free" - - # Determine provider - if "gpt" in model_id or "openai" in model_id: - provider = "OpenAI" - elif "claude" in model_id or "anthropic" in model_id: - provider = "Anthropic" - elif "gemini" in model_id or "google" in model_id: - provider = "Google" - elif "llama" in model_id: - provider = "Meta" - elif "mistral" in model_id or "mixtral" in model_id: - provider = "Mistral" - elif "qwen" in model_id or "alibaba" in model_id: - provider = "Alibaba" - elif "deepseek" in model_id: - provider = "DeepSeek" - else: - provider = model.get("owned_by", "Various") - - table.add_row(str(i), model_id, tier, provider) - - console.print(table) - console.print( - f"\n[dim]Total LLMs: {len(models_list)} (embedding models filtered out)[/dim]" - ) - else: - console.print("[yellow]No models available on gateway[/yellow]") - else: - # Show static list of known models - console.print( - "[yellow]Could not fetch live models, showing known models:[/yellow]\n" - ) - _show_static_models() - except Exception as e: - console.print(f"[yellow]Error fetching models: {e}[/yellow]\n") - _show_static_models() - - console.print("\n[dim]To use a model:[/dim]") - console.print(" [cyan]hanzo dev --model llama3-8b-instruct[/cyan]") - console.print( - "\n[dim cyan]Free tier:[/dim cyan] [green]Most models available without login![/green]" - ) - console.print( - "[dim cyan]Premium models:[/dim cyan] [yellow]gpt-4, claude-3-opus, o1-preview[/yellow] - [cyan]hanzo auth login[/cyan]" - ) - console.print() - - -def _show_static_models(): - """Show static list of known gateway models.""" - table = Table(title="Known Gateway Models") - table.add_column("#", style="dim") - table.add_column("Model ID", style="cyan") - table.add_column("Tier", style="yellow") - table.add_column("Provider", style="green") - - models = [ - # Free tier - ("gpt-4o-mini", "Free", "OpenAI"), - ("gpt-3.5-turbo", "Free", "OpenAI"), - ("llama-3.1-8b", "Free", "Meta"), - ("llama-3.2-3b", "Free", "Meta"), - # Premium (requires login) - ("gpt-4o", "Premium", "OpenAI"), - ("gpt-4-turbo", "Premium", "OpenAI"), - ("claude-3-5-sonnet", "Premium", "Anthropic"), - ("claude-3-opus", "Premium", "Anthropic"), - ("claude-3-sonnet", "Premium", "Anthropic"), - ("claude-3-haiku", "Premium", "Anthropic"), - ("gemini-pro", "Premium", "Google"), - ("llama-3.1-70b", "Premium", "Meta"), - ("mixtral-8x7b", "Premium", "Mistral"), - ("qwen-2.5-72b", "Premium", "Alibaba"), - ] - - for i, (model_id, tier, provider) in enumerate(models, 1): - table.add_row(str(i), model_id, tier, provider) - - console.print(table) - - -@network_group.command(name="topology-list") -@click.option("--json", is_flag=True, help="Output as JSON") -@click.pass_context -def topology_list(ctx, json: bool): - """List all nodes in the network topology.""" - try: - import os - import json as json_lib - - from hanzo_network.topology.topology import Topology - from hanzo_network.topology.device_capabilities import DeviceCapabilities - except ImportError: - console.print("[red]Error:[/red] hanzo-network not installed") - console.print("Install with: pip install hanzo[network]") - return - - topology_file = os.path.expanduser("~/.hanzo/topology.json") - - if not os.path.exists(topology_file): - console.print("[yellow]No topology file found[/yellow]") - console.print(f"Create one by adding nodes with: hanzo network topology-add") - return - - # Load topology - with open(topology_file, "r") as f: - data = json_lib.load(f) - - if json: - console.print(json_lib.dumps(data, indent=2)) - return - - nodes = data.get("nodes", {}) - if not nodes: - console.print("[yellow]No nodes in topology[/yellow]") - return - - # Display nodes table - table = Table(title="Network Topology Nodes") - table.add_column("Node ID", style="cyan", no_wrap=True) - table.add_column("Model", style="green") - table.add_column("Chip/GPU", style="yellow") - table.add_column("Memory", style="blue") - table.add_column("FP32", style="magenta") - - for node_id, caps in nodes.items(): - table.add_row( - node_id, - caps.get("model", "Unknown"), - caps.get("chip", "Unknown"), - f"{caps.get('memory', 0):,} MB", - f"{caps.get('flops', {}).get('fp32', 0):.2f} TF", - ) - - console.print("\n") - console.print(table) - console.print(f"\nTopology file: {topology_file}") - console.print() diff --git a/pkg/hanzo/src/hanzo/commands/node.py b/pkg/hanzo/src/hanzo/commands/node.py deleted file mode 100644 index 647909874..000000000 --- a/pkg/hanzo/src/hanzo/commands/node.py +++ /dev/null @@ -1,459 +0,0 @@ -"""Node management commands.""" - -from typing import List, Optional - -import click -from rich.table import Table -from rich.progress import Progress, TextColumn, SpinnerColumn - -from ..utils.output import console - - -@click.group(name="node") -def cluster(): - """Manage local AI node.""" - pass - - -@cluster.command() -@click.option("--name", "-n", default="hanzo-local", help="Node name") -@click.option("--port", "-p", default=8000, type=int, help="API port") -@click.option("--models", "-m", multiple=True, help="Models to load") -@click.option( - "--device", - type=click.Choice(["cpu", "gpu", "auto"]), - default="auto", - help="Device to use", -) -@click.pass_context -async def start(ctx, name: str, port: int, models: tuple, device: str): - """Start local AI node.""" - await start_node(ctx, name, port, list(models) if models else None, device) - - -async def start_node( - ctx, name: str, port: int, models: Optional[List[str]] = None, device: str = "auto" -): - """Start a local node via hanzo-cluster.""" - try: - from hanzo_cluster import HanzoCluster - except ImportError: - console.print("[red]Error:[/red] hanzo-cluster not installed") - console.print("Install with: pip install hanzo[cluster]") - return - - node = HanzoCluster(name=name, port=port, device=device) - - with Progress( - SpinnerColumn(), - TextColumn("[progress.description]{task.description}"), - console=console, - ) as progress: - task = progress.add_task("Starting node...", total=None) - - try: - await node.start(models=models) - progress.update(task, completed=True) - except Exception as e: - progress.stop() - console.print(f"[red]Failed to start node: {e}[/red]") - return - - console.print(f"[green]โœ“[/green] Node started at http://localhost:{port}") - console.print("Press Ctrl+C to stop\n") - - # Show node info - info = await node.info() - console.print("[cyan]Node Information:[/cyan]") - console.print(f" Name: {info.get('name', name)}") - console.print(f" Port: {info.get('port', port)}") - console.print(f" Device: {info.get('device', device)}") - console.print(f" Workers: {info.get('nodes', 1)}") - if models := info.get("models", models): - console.print(f" Models: {', '.join(models)}") - - console.print("\n[dim]Logs:[/dim]") - - try: - # Stream logs - async for log in node.stream_logs(): - console.print(log, end="") - except KeyboardInterrupt: - console.print("\n[yellow]Stopping node...[/yellow]") - await node.stop() - console.print("[green]โœ“[/green] Node stopped") - - -@cluster.command() -@click.option("--name", "-n", default="hanzo-local", help="Node name") -@click.pass_context -async def stop(ctx, name: str): - """Stop local AI node.""" - try: - from hanzo_cluster import HanzoCluster - except ImportError: - console.print("[red]Error:[/red] hanzo-cluster not installed") - return - - node = HanzoCluster(name=name) - - console.print("[yellow]Stopping node...[/yellow]") - try: - await node.stop() - console.print("[green]โœ“[/green] Node stopped") - except Exception as e: - console.print(f"[red]Failed to stop node: {e}[/red]") - - -@cluster.command() -@click.option("--name", "-n", default="hanzo-local", help="Node name") -@click.pass_context -async def status(ctx, name: str): - """Show node status.""" - try: - from hanzo_cluster import HanzoCluster - except ImportError: - console.print("[red]Error:[/red] hanzo-cluster not installed") - return - - node = HanzoCluster(name=name) - - try: - status = await node.status() - - if status.get("running"): - console.print("[green]โœ“[/green] Node is running") - - # Show node info - console.print("\n[cyan]Node Information:[/cyan]") - console.print(f" Name: {status.get('name', name)}") - console.print(f" Workers: {status.get('nodes', 0)}") - console.print(f" Status: {status.get('state', 'unknown')}") - - # Show models - if models := status.get("models", []): - console.print("\n[cyan]Available Models:[/cyan]") - for model in models: - console.print(f" โ€ข {model}") - - # Show worker details - if workers := status.get("node_details", []): - console.print("\n[cyan]Workers:[/cyan]") - for worker in workers: - console.print( - f" โ€ข {worker.get('name', 'unknown')} ({worker.get('state', 'unknown')})" - ) - if device := worker.get("device"): - console.print(f" Device: {device}") - else: - console.print("[yellow]![/yellow] Node is not running") - console.print("Start with: hanzo node start") - - except Exception as e: - console.print(f"[red]Error checking status: {e}[/red]") - - -@cluster.command() -@click.option("--name", "-n", default="hanzo-local", help="Node name") -@click.pass_context -async def models(ctx, name: str): - """List available models.""" - try: - from hanzo_cluster import HanzoCluster - except ImportError: - console.print("[red]Error:[/red] hanzo-cluster not installed") - return - - node = HanzoCluster(name=name) - - try: - models = await node.list_models() - - if models: - table = Table(title="Available Models") - table.add_column("Model ID", style="cyan") - table.add_column("Type", style="green") - table.add_column("Status", style="yellow") - table.add_column("Worker", style="blue") - - for model in models: - table.add_row( - model.get("id", "unknown"), - model.get("type", "model"), - model.get("status", "unknown"), - model.get("node", "local"), - ) - - console.print(table) - else: - console.print("[yellow]No models loaded[/yellow]") - console.print("Load models with: hanzo node load ") - - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - - -@cluster.command() -@click.argument("model") -@click.option("--name", "-n", default="hanzo-local", help="Node name") -@click.option("--worker", help="Target worker (default: auto-select)") -@click.pass_context -async def load(ctx, model: str, name: str, worker: str = None): - """Load a model into the node.""" - try: - from hanzo_cluster import HanzoCluster - except ImportError: - console.print("[red]Error:[/red] hanzo-cluster not installed") - return - - node = HanzoCluster(name=name) - - with console.status(f"Loading model '{model}'..."): - try: - result = await node.load_model(model, node=worker) - console.print(f"[green]โœ“[/green] Loaded model: {model}") - if worker_name := result.get("node"): - console.print(f" Worker: {worker_name}") - except Exception as e: - console.print(f"[red]Failed to load model: {e}[/red]") - - -@cluster.command() -@click.argument("model") -@click.option("--name", "-n", default="hanzo-local", help="Node name") -@click.pass_context -async def unload(ctx, model: str, name: str): - """Unload a model from the node.""" - try: - from hanzo_cluster import HanzoCluster - except ImportError: - console.print("[red]Error:[/red] hanzo-cluster not installed") - return - - node = HanzoCluster(name=name) - - if click.confirm(f"Unload model '{model}'?"): - with console.status(f"Unloading model '{model}'..."): - try: - await node.unload_model(model) - console.print(f"[green]โœ“[/green] Unloaded model: {model}") - except Exception as e: - console.print(f"[red]Failed to unload model: {e}[/red]") - - -@cluster.group(name="worker") -def worker_group(): - """Manage node workers.""" - pass - - -@worker_group.command(name="start") -@click.option("--name", "-n", default="worker-1", help="Worker name") -@click.option("--node", "-nd", default="hanzo-local", help="Node to join") -@click.option( - "--device", - type=click.Choice(["cpu", "gpu", "auto"]), - default="auto", - help="Device to use", -) -@click.option( - "--port", "-p", type=int, help="Worker port (auto-assigned if not specified)" -) -@click.option("--blockchain", is_flag=True, help="Enable blockchain features") -@click.option("--network", is_flag=True, help="Enable network discovery") -@click.pass_context -async def worker_start( - ctx, - name: str, - node: str, - device: str, - port: int, - blockchain: bool, - network: bool, -): - """Start this machine as a worker in the node.""" - try: - from hanzo_cluster import HanzoNode - - if blockchain or network: - from hanzo_network import HanzoNetwork - except ImportError: - console.print("[red]Error:[/red] Required packages not installed") - console.print("Install with: pip install hanzo[cluster,network]") - return - - worker = HanzoNode(name=name, device=device, port=port) - - with Progress( - SpinnerColumn(), - TextColumn("[progress.description]{task.description}"), - console=console, - ) as progress: - task = progress.add_task(f"Starting worker '{name}'...", total=None) - - try: - # Start the worker - await worker.start(cluster=node) - - # Enable blockchain/network features if requested - if blockchain or network: - network_mgr = HanzoNetwork(node=worker) - if blockchain: - await network_mgr.enable_blockchain() - if network: - await network_mgr.enable_discovery() - - progress.update(task, completed=True) - except Exception as e: - progress.stop() - console.print(f"[red]Failed to start worker: {e}[/red]") - return - - console.print(f"[green]โœ“[/green] Worker '{name}' started") - console.print(f" Node: {node}") - console.print(f" Device: {device}") - if port: - console.print(f" Port: {port}") - if blockchain: - console.print(" [cyan]Blockchain enabled[/cyan]") - if network: - console.print(" [cyan]Network discovery enabled[/cyan]") - - console.print("\nPress Ctrl+C to stop\n") - console.print("[dim]Logs:[/dim]") - - try: - # Stream logs - async for log in worker.stream_logs(): - console.print(log, end="") - except KeyboardInterrupt: - console.print("\n[yellow]Stopping worker...[/yellow]") - await worker.stop() - console.print("[green]โœ“[/green] Worker stopped") - - -@worker_group.command(name="stop") -@click.option("--name", "-n", help="Worker name") -@click.option("--all", is_flag=True, help="Stop all workers") -@click.pass_context -async def worker_stop(ctx, name: str, all: bool): - """Stop a worker.""" - try: - from hanzo_cluster import HanzoNode - except ImportError: - console.print("[red]Error:[/red] hanzo-cluster not installed") - return - - if all: - if click.confirm("Stop all workers?"): - console.print("[yellow]Stopping all workers...[/yellow]") - try: - await HanzoNode.stop_all() - console.print("[green]โœ“[/green] All workers stopped") - except Exception as e: - console.print(f"[red]Failed to stop workers: {e}[/red]") - elif name: - worker = HanzoNode(name=name) - console.print(f"[yellow]Stopping worker '{name}'...[/yellow]") - try: - await worker.stop() - console.print(f"[green]โœ“[/green] Worker stopped") - except Exception as e: - console.print(f"[red]Failed to stop worker: {e}[/red]") - else: - console.print("[red]Error:[/red] Specify --name or --all") - - -@worker_group.command(name="list") -@click.option("--node", "-nd", help="Filter by node") -@click.pass_context -async def worker_list(ctx, node: str): - """List all workers.""" - try: - from hanzo_cluster import HanzoNode - except ImportError: - console.print("[red]Error:[/red] hanzo-cluster not installed") - return - - try: - workers = await HanzoNode.list_nodes(cluster=node) - - if workers: - table = Table(title="Node Workers") - table.add_column("Name", style="cyan") - table.add_column("Node", style="green") - table.add_column("Device", style="yellow") - table.add_column("Status", style="blue") - table.add_column("Models", style="magenta") - - for worker in workers: - table.add_row( - worker.get("name", "unknown"), - worker.get("cluster", "unknown"), - worker.get("device", "unknown"), - worker.get("status", "unknown"), - str(len(worker.get("models", []))), - ) - - console.print(table) - else: - console.print("[yellow]No workers found[/yellow]") - console.print("Start a worker with: hanzo node worker start") - - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - - -@worker_group.command(name="info") -@click.argument("name") -@click.pass_context -async def worker_info(ctx, name: str): - """Show detailed worker information.""" - try: - from hanzo_cluster import HanzoNode - except ImportError: - console.print("[red]Error:[/red] hanzo-cluster not installed") - return - - worker = HanzoNode(name=name) - - try: - info = await worker.info() - - console.print(f"[cyan]Worker: {name}[/cyan]") - console.print(f" Node: {info.get('cluster', 'unknown')}") - console.print(f" Status: {info.get('status', 'unknown')}") - console.print(f" Device: {info.get('device', 'unknown')}") - - if uptime := info.get("uptime"): - console.print(f" Uptime: {uptime}") - - if resources := info.get("resources"): - console.print("\n[cyan]Resources:[/cyan]") - console.print(f" CPU: {resources.get('cpu_percent', 'N/A')}%") - console.print( - f" Memory: {resources.get('memory_used', 'N/A')} / {resources.get('memory_total', 'N/A')}" - ) - if gpu := resources.get("gpu"): - console.print( - f" GPU: {gpu.get('name', 'N/A')} ({gpu.get('memory_used', 'N/A')} / {gpu.get('memory_total', 'N/A')})" - ) - - if models := info.get("models"): - console.print("\n[cyan]Loaded Models:[/cyan]") - for model in models: - console.print(f" โ€ข {model}") - - if network := info.get("network"): - console.print("\n[cyan]Network:[/cyan]") - console.print( - f" Blockchain: {'enabled' if network.get('blockchain') else 'disabled'}" - ) - console.print( - f" Discovery: {'enabled' if network.get('discovery') else 'disabled'}" - ) - if peers := network.get("peers"): - console.print(f" Peers: {len(peers)}") - - except Exception as e: - console.print(f"[red]Error: {e}[/red]") diff --git a/pkg/hanzo/src/hanzo/commands/o11y.py b/pkg/hanzo/src/hanzo/commands/o11y.py deleted file mode 100644 index e829c4d80..000000000 --- a/pkg/hanzo/src/hanzo/commands/o11y.py +++ /dev/null @@ -1,1434 +0,0 @@ -"""Hanzo Observability - Metrics, logs, traces CLI. - -Full visibility into your systems. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -SERVICE_URL = os.getenv("HANZO_O11Y_URL", "https://o11y.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(SERVICE_URL, method, path, **kwargs) - - -@click.group(name="o11y") -def o11y_group(): - """Hanzo Observability - Metrics, logs, traces, and LLM monitoring. - - \b - Infrastructure Observability: - hanzo o11y metrics list # List metric series - hanzo o11y logs search # Search logs - hanzo o11y traces list # List distributed traces - hanzo o11y dashboards list # List dashboards - hanzo o11y alerts list # List alert rules - - \b - LLM Observability (Langfuse-style): - hanzo o11y prompts list # Manage prompt templates - hanzo o11y generations list # Track LLM generations - hanzo o11y sessions list # Track conversation sessions - hanzo o11y llm costs # LLM cost analysis - - \b - Evaluations & Scoring: - hanzo o11y evals create # Create evaluation runs - hanzo o11y scores list # View scores & feedback - hanzo o11y datasets list # Manage eval datasets - - \b - Alias: hanzo observe - """ - pass - - -# ============================================================================ -# Metrics -# ============================================================================ - - -@o11y_group.group() -def metrics(): - """Manage metrics and time-series data.""" - pass - - -@metrics.command(name="list") -@click.option("--filter", "-f", help="Filter metric names") -@click.option("--limit", "-n", default=100, help="Max results") -def metrics_list(filter: str, limit: int): - """List available metric series.""" - params = {"limit": limit} - if filter: - params["filter"] = filter - resp = _request("get", "/v1/metrics", params=params) - data = check_response(resp) - - table = Table(title="Metrics", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Type", style="white") - table.add_column("Labels", style="dim") - table.add_column("Points", style="dim") - - for m in data.get("metrics", []): - table.add_row( - m.get("name", ""), - m.get("type", ""), - ", ".join(m.get("labels", [])), - str(m.get("points", 0)), - ) - - console.print(table) - - -@metrics.command(name="query") -@click.argument("promql") -@click.option("--start", "-s", help="Start time") -@click.option("--end", "-e", help="End time") -@click.option("--step", default="1m", help="Query step") -def metrics_query(promql: str, start: str, end: str, step: str): - """Query metrics using PromQL.""" - payload = {"query": promql, "step": step} - if start: - payload["start"] = start - if end: - payload["end"] = end - resp = _request("post", "/v1/metrics/query", json=payload) - data = check_response(resp) - - console.print(f"[cyan]Query:[/cyan] {promql}") - console.print(f"[cyan]Step:[/cyan] {step}") - console.print() - - results = data.get("result", []) - if not results: - console.print("[dim]No data points returned[/dim]") - return - - table = Table(title="Query Results", box=box.ROUNDED) - table.add_column("Metric", style="cyan") - table.add_column("Value", style="yellow") - table.add_column("Timestamp", style="dim") - - for r in results: - labels = r.get("metric", {}) - label_str = ", ".join(f"{k}={v}" for k, v in labels.items()) - for ts, val in r.get("values", []): - table.add_row(label_str, str(val), str(ts)) - - console.print(table) - - -@metrics.command(name="export") -@click.argument("promql") -@click.option( - "--format", "-f", "fmt", type=click.Choice(["json", "csv"]), default="json" -) -@click.option("--output", "-o", help="Output file") -@click.option("--start", "-s", help="Start time") -@click.option("--end", "-e", help="End time") -def metrics_export(promql: str, fmt: str, output: str, start: str, end: str): - """Export metrics to file.""" - payload = {"query": promql, "format": fmt} - if start: - payload["start"] = start - if end: - payload["end"] = end - resp = _request("post", "/v1/metrics/export", json=payload) - data = check_response(resp) - - content = data.get("data", "") - if output: - with open(output, "w") as f: - f.write( - content if isinstance(content, str) else json.dumps(content, indent=2) - ) - console.print(f"[green]โœ“[/green] Exported to {output}") - else: - console.print( - content if isinstance(content, str) else json.dumps(content, indent=2) - ) - - -# ============================================================================ -# Logs -# ============================================================================ - - -@o11y_group.group() -def logs(): - """Search and analyze logs.""" - pass - - -@logs.command(name="search") -@click.argument("query") -@click.option("--source", "-s", help="Log source") -@click.option("--level", "-l", type=click.Choice(["debug", "info", "warn", "error"])) -@click.option("--limit", "-n", default=100, help="Max results") -@click.option("--start", help="Start time") -@click.option("--end", help="End time") -def logs_search(query: str, source: str, level: str, limit: int, start: str, end: str): - """Search logs.""" - payload: dict = {"query": query, "limit": limit} - if source: - payload["source"] = source - if level: - payload["level"] = level - if start: - payload["start"] = start - if end: - payload["end"] = end - resp = _request("post", "/v1/logs/search", json=payload) - data = check_response(resp) - - entries = data.get("logs", []) - if not entries: - console.print("[dim]No matching logs found[/dim]") - return - - table = Table(title="Log Results", box=box.ROUNDED) - table.add_column("Time", style="dim") - table.add_column("Level", style="yellow") - table.add_column("Source", style="cyan") - table.add_column("Message", style="white") - - for entry in entries: - lvl = entry.get("level", "info") - lvl_style = { - "error": "red", - "warn": "yellow", - "info": "green", - "debug": "dim", - }.get(lvl, "white") - table.add_row( - entry.get("timestamp", ""), - f"[{lvl_style}]{lvl}[/{lvl_style}]", - entry.get("source", ""), - entry.get("message", ""), - ) - - console.print(table) - - -@logs.command(name="tail") -@click.option("--source", "-s", help="Log source") -@click.option("--filter", "-f", help="Filter expression") -@click.option("--level", "-l", type=click.Choice(["debug", "info", "warn", "error"])) -def logs_tail(source: str, filter: str, level: str): - """Tail live logs.""" - params: dict = {} - if source: - params["source"] = source - if filter: - params["filter"] = filter - if level: - params["level"] = level - - console.print("[cyan]Tailing logs... (Ctrl+C to stop)[/cyan]") - - try: - url = f"{SERVICE_URL}/v1/logs/tail" - from .base import get_api_key - - api_key = get_api_key() - if not api_key: - raise click.ClickException("Not authenticated. Run 'hanzo login' first.") - - with httpx.Client(timeout=None) as client: - with client.stream( - "GET", - url, - params=params, - headers={"Authorization": f"Bearer {api_key}"}, - ) as stream: - for line in stream.iter_lines(): - if line: - try: - entry = json.loads(line) - lvl = entry.get("level", "info") - lvl_style = { - "error": "red", - "warn": "yellow", - "info": "green", - "debug": "dim", - }.get(lvl, "white") - console.print( - f"[dim]{entry.get('timestamp', '')}[/dim] [{lvl_style}]{lvl}[/{lvl_style}] [cyan]{entry.get('source', '')}[/cyan] {entry.get('message', '')}" - ) - except json.JSONDecodeError: - console.print(line) - except KeyboardInterrupt: - console.print("\n[dim]Stopped tailing logs[/dim]") - except httpx.ConnectError: - raise click.ClickException(f"Could not connect to {SERVICE_URL}") - - -@logs.command(name="sources") -def logs_sources(): - """List log sources.""" - resp = _request("get", "/v1/logs/sources") - data = check_response(resp) - - table = Table(title="Log Sources", box=box.ROUNDED) - table.add_column("Source", style="cyan") - table.add_column("Type", style="white") - table.add_column("Status", style="green") - table.add_column("Volume", style="dim") - - for src in data.get("sources", []): - status = src.get("status", "active") - style = "green" if status == "active" else "yellow" - table.add_row( - src.get("name", ""), - src.get("type", ""), - f"[{style}]{status}[/{style}]", - src.get("volume", ""), - ) - - console.print(table) - - -# ============================================================================ -# Traces -# ============================================================================ - - -@o11y_group.group() -def traces(): - """Analyze distributed traces.""" - pass - - -@traces.command(name="list") -@click.option("--service", "-s", help="Filter by service") -@click.option("--operation", "-o", help="Filter by operation") -@click.option("--min-duration", help="Minimum duration (e.g., 100ms)") -@click.option("--limit", "-n", default=20, help="Max traces") -def traces_list(service: str, operation: str, min_duration: str, limit: int): - """List recent traces.""" - params: dict = {"limit": limit} - if service: - params["service"] = service - if operation: - params["operation"] = operation - if min_duration: - params["min_duration"] = min_duration - resp = _request("get", "/v1/traces", params=params) - data = check_response(resp) - - table = Table(title="Traces", box=box.ROUNDED) - table.add_column("Trace ID", style="cyan") - table.add_column("Service", style="white") - table.add_column("Operation", style="white") - table.add_column("Duration", style="yellow") - table.add_column("Spans", style="dim") - table.add_column("Status", style="green") - - for t in data.get("traces", []): - status = t.get("status", "ok") - style = "green" if status == "ok" else "red" - table.add_row( - t.get("trace_id", "")[:16], - t.get("service", ""), - t.get("operation", ""), - t.get("duration", ""), - str(t.get("spans", 0)), - f"[{style}]{status}[/{style}]", - ) - - console.print(table) - - -@traces.command(name="show") -@click.argument("trace_id") -def traces_show(trace_id: str): - """Show trace details.""" - resp = _request("get", f"/v1/traces/{trace_id}") - data = check_response(resp) - - info = ( - f"[cyan]Trace ID:[/cyan] {data.get('trace_id', trace_id)}\n" - f"[cyan]Duration:[/cyan] {data.get('duration', 'N/A')}\n" - f"[cyan]Spans:[/cyan] {data.get('span_count', 0)}\n" - f"[cyan]Services:[/cyan] {', '.join(data.get('services', []))}\n" - f"[cyan]Status:[/cyan] {data.get('status', 'ok')}" - ) - console.print(Panel(info, title="Trace Details", border_style="cyan")) - - spans = data.get("spans", []) - if spans: - table = Table(title="Spans", box=box.ROUNDED) - table.add_column("Span ID", style="cyan") - table.add_column("Service", style="white") - table.add_column("Operation", style="white") - table.add_column("Duration", style="yellow") - table.add_column("Status", style="green") - - for s in spans: - st = s.get("status", "ok") - st_style = "green" if st == "ok" else "red" - table.add_row( - s.get("span_id", "")[:12], - s.get("service", ""), - s.get("operation", ""), - s.get("duration", ""), - f"[{st_style}]{st}[/{st_style}]", - ) - - console.print(table) - - -@traces.command(name="services") -def traces_services(): - """List traced services.""" - resp = _request("get", "/v1/traces/services") - data = check_response(resp) - - table = Table(title="Services", box=box.ROUNDED) - table.add_column("Service", style="cyan") - table.add_column("Operations", style="white") - table.add_column("Avg Duration", style="yellow") - table.add_column("Error Rate", style="red") - - for svc in data.get("services", []): - table.add_row( - svc.get("name", ""), - str(svc.get("operations", 0)), - svc.get("avg_duration", ""), - svc.get("error_rate", ""), - ) - - console.print(table) - - -# ============================================================================ -# Dashboards -# ============================================================================ - - -@o11y_group.group() -def dashboards(): - """Manage observability dashboards.""" - pass - - -@dashboards.command(name="list") -def dashboards_list(): - """List all dashboards.""" - resp = _request("get", "/v1/dashboards") - data = check_response(resp) - - table = Table(title="Dashboards", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Panels", style="white") - table.add_column("Updated", style="dim") - - for d in data.get("dashboards", []): - table.add_row( - d.get("name", ""), - str(d.get("panel_count", 0)), - d.get("updated_at", ""), - ) - - console.print(table) - - -@dashboards.command(name="create") -@click.option("--name", "-n", prompt=True, help="Dashboard name") -@click.option("--file", "-f", help="Import from JSON file") -def dashboards_create(name: str, file: str): - """Create a new dashboard.""" - payload: dict = {"name": name} - if file: - with open(file) as f: - payload["config"] = json.load(f) - resp = _request("post", "/v1/dashboards", json=payload) - data = check_response(resp) - - dashboard_id = data.get("id", "") - console.print(f"[green]โœ“[/green] Dashboard '{name}' created") - console.print(f"[dim]View at: {SERVICE_URL}/dashboards/{dashboard_id}[/dim]") - - -@dashboards.command(name="delete") -@click.argument("name") -def dashboards_delete(name: str): - """Delete a dashboard.""" - resp = _request("delete", f"/v1/dashboards/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Dashboard '{name}' deleted") - - -@dashboards.command(name="open") -@click.argument("name") -def dashboards_open(name: str): - """Open dashboard in browser.""" - import webbrowser - - url = f"{SERVICE_URL}/dashboards/{name}" - console.print(f"[cyan]Opening: {url}[/cyan]") - webbrowser.open(url) - - -# ============================================================================ -# Alerts -# ============================================================================ - - -@o11y_group.group() -def alerts(): - """Manage alert rules.""" - pass - - -@alerts.command(name="list") -@click.option( - "--status", - type=click.Choice(["firing", "pending", "inactive", "all"]), - default="all", -) -def alerts_list(status: str): - """List alert rules.""" - params: dict = {} - if status != "all": - params["status"] = status - resp = _request("get", "/v1/alerts", params=params) - data = check_response(resp) - - table = Table(title="Alert Rules", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Condition", style="white") - table.add_column("Status", style="green") - table.add_column("Severity", style="yellow") - table.add_column("Last Fired", style="dim") - - for a in data.get("alerts", []): - st = a.get("status", "inactive") - st_style = {"firing": "red", "pending": "yellow"}.get(st, "green") - sev = a.get("severity", "warning") - sev_style = {"critical": "red", "warning": "yellow"}.get(sev, "dim") - table.add_row( - a.get("name", ""), - a.get("condition", ""), - f"[{st_style}]{st}[/{st_style}]", - f"[{sev_style}]{sev}[/{sev_style}]", - a.get("last_fired", "never"), - ) - - console.print(table) - - -@alerts.command(name="create") -@click.option("--name", "-n", prompt=True, help="Alert name") -@click.option("--condition", "-c", required=True, help="PromQL condition") -@click.option( - "--severity", - "-s", - type=click.Choice(["critical", "warning", "info"]), - default="warning", -) -@click.option("--channel", help="Notification channel") -@click.option( - "--for-duration", "for_duration", default="5m", help="Duration before firing" -) -def alerts_create( - name: str, condition: str, severity: str, channel: str, for_duration: str -): - """Create an alert rule.""" - payload: dict = { - "name": name, - "condition": condition, - "severity": severity, - "for": for_duration, - } - if channel: - payload["channel"] = channel - resp = _request("post", "/v1/alerts", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Alert rule '{name}' created") - - -@alerts.command(name="silence") -@click.argument("alert_name") -@click.option("--duration", "-d", default="1h", help="Silence duration") -@click.option("--comment", "-c", help="Reason for silencing") -def alerts_silence(alert_name: str, duration: str, comment: str): - """Silence an alert.""" - payload: dict = {"duration": duration} - if comment: - payload["comment"] = comment - resp = _request("post", f"/v1/alerts/{alert_name}/silence", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Alert '{alert_name}' silenced for {duration}") - - -@alerts.command(name="delete") -@click.argument("alert_name") -def alerts_delete(alert_name: str): - """Delete an alert rule.""" - resp = _request("delete", f"/v1/alerts/{alert_name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Alert rule '{alert_name}' deleted") - - -# ============================================================================ -# LLM Observability (Langfuse-style) -# ============================================================================ - - -@o11y_group.group() -def prompts(): - """Manage LLM prompt templates (Langfuse-style).""" - pass - - -@prompts.command(name="list") -@click.option("--label", "-l", help="Filter by label") -@click.option("--limit", "-n", default=50, help="Max results") -def prompts_list(label: str, limit: int): - """List prompt templates.""" - params: dict = {"limit": limit} - if label: - params["label"] = label - resp = _request("get", "/v1/prompts", params=params) - data = check_response(resp) - - table = Table(title="Prompt Templates", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Version", style="white") - table.add_column("Label", style="green") - table.add_column("Model", style="yellow") - table.add_column("Updated", style="dim") - - for p in data.get("prompts", []): - table.add_row( - p.get("name", ""), - f"v{p.get('version', '1')}", - p.get("label", ""), - p.get("model", ""), - p.get("updated_at", ""), - ) - - console.print(table) - - -@prompts.command(name="create") -@click.argument("name") -@click.option("--template", "-t", required=True, help="Prompt template") -@click.option("--model", "-m", help="Default model") -@click.option("--config", "-c", help="Model config JSON") -@click.option("--label", "-l", default="latest", help="Version label") -def prompts_create(name: str, template: str, model: str, config: str, label: str): - """Create a prompt template.""" - payload: dict = {"name": name, "template": template, "label": label} - if model: - payload["model"] = model - if config: - payload["config"] = json.loads(config) - resp = _request("post", "/v1/prompts", json=payload) - data = check_response(resp) - console.print( - f"[green]โœ“[/green] Prompt '{name}' created (v{data.get('version', '1')})" - ) - if model: - console.print(f" Model: {model}") - - -@prompts.command(name="get") -@click.argument("name") -@click.option("--version", "-v", type=int, help="Specific version") -@click.option("--label", "-l", help="Get by label (production, staging)") -def prompts_get(name: str, version: int, label: str): - """Get a prompt template.""" - params: dict = {} - if version: - params["version"] = version - if label: - params["label"] = label - resp = _request("get", f"/v1/prompts/{name}", params=params) - data = check_response(resp) - - info = ( - f"[cyan]Name:[/cyan] {data.get('name', name)}\n" - f"[cyan]Version:[/cyan] v{data.get('version', '1')}\n" - f"[cyan]Label:[/cyan] {data.get('label', '')}\n" - f"[cyan]Model:[/cyan] {data.get('model', 'N/A')}\n" - f"[cyan]Template:[/cyan]\n {data.get('template', '')}" - ) - console.print(Panel(info, title="Prompt Template", border_style="cyan")) - - if data.get("config"): - console.print(f"\n[cyan]Config:[/cyan]") - console.print(json.dumps(data["config"], indent=2)) - - -@prompts.command(name="promote") -@click.argument("name") -@click.option("--version", "-v", required=True, type=int, help="Version to promote") -@click.option("--to", "to_label", required=True, help="Target label") -def prompts_promote(name: str, version: int, to_label: str): - """Promote a prompt version to a label.""" - resp = _request( - "post", - f"/v1/prompts/{name}/promote", - json={"version": version, "label": to_label}, - ) - check_response(resp) - console.print(f"[green]โœ“[/green] Promoted '{name}' v{version} to {to_label}") - - -@prompts.command(name="history") -@click.argument("name") -def prompts_history(name: str): - """Show prompt version history.""" - resp = _request("get", f"/v1/prompts/{name}/versions") - data = check_response(resp) - - table = Table(title=f"History for '{name}'", box=box.ROUNDED) - table.add_column("Version", style="cyan") - table.add_column("Label", style="green") - table.add_column("Author", style="white") - table.add_column("Created", style="dim") - table.add_column("Note", style="dim") - - for v in data.get("versions", []): - table.add_row( - f"v{v.get('version', '')}", - v.get("label", ""), - v.get("author", ""), - v.get("created_at", ""), - v.get("note", ""), - ) - - console.print(table) - - -# ============================================================================ -# Generations (LLM Call Tracking) -# ============================================================================ - - -@o11y_group.group() -def generations(): - """Track LLM generations and calls (Langfuse-style).""" - pass - - -@generations.command(name="list") -@click.option("--model", "-m", help="Filter by model") -@click.option("--prompt", "-p", help="Filter by prompt name") -@click.option("--user", "-u", help="Filter by user ID") -@click.option("--limit", "-n", default=50, help="Max results") -def generations_list(model: str, prompt: str, user: str, limit: int): - """List LLM generations.""" - params: dict = {"limit": limit} - if model: - params["model"] = model - if prompt: - params["prompt"] = prompt - if user: - params["user"] = user - resp = _request("get", "/v1/generations", params=params) - data = check_response(resp) - - table = Table(title="Generations", box=box.ROUNDED) - table.add_column("ID", style="cyan") - table.add_column("Model", style="white") - table.add_column("Prompt", style="white") - table.add_column("Tokens", style="yellow") - table.add_column("Cost", style="green") - table.add_column("Latency", style="dim") - table.add_column("Time", style="dim") - - for g in data.get("generations", []): - total_tokens = g.get("input_tokens", 0) + g.get("output_tokens", 0) - table.add_row( - g.get("id", "")[:12], - g.get("model", ""), - g.get("prompt_name", ""), - str(total_tokens), - f"${g.get('cost', 0):.4f}", - g.get("latency", ""), - g.get("created_at", ""), - ) - - console.print(table) - - -@generations.command(name="show") -@click.argument("generation_id") -def generations_show(generation_id: str): - """Show generation details.""" - resp = _request("get", f"/v1/generations/{generation_id}") - data = check_response(resp) - - info = ( - f"[cyan]Generation ID:[/cyan] {data.get('id', generation_id)}\n" - f"[cyan]Trace ID:[/cyan] {data.get('trace_id', 'N/A')}\n" - f"[cyan]Model:[/cyan] {data.get('model', 'N/A')}\n" - f"[cyan]Prompt:[/cyan] {data.get('prompt_name', 'N/A')} v{data.get('prompt_version', '?')}\n" - f"[cyan]Input Tokens:[/cyan] {data.get('input_tokens', 0)}\n" - f"[cyan]Output Tokens:[/cyan] {data.get('output_tokens', 0)}\n" - f"[cyan]Total Cost:[/cyan] ${data.get('cost', 0):.4f}\n" - f"[cyan]Latency:[/cyan] {data.get('latency', 'N/A')}\n" - f"[cyan]Finish Reason:[/cyan] {data.get('finish_reason', 'N/A')}" - ) - console.print(Panel(info, title="Generation Details", border_style="cyan")) - - if data.get("input"): - console.print("\n[cyan]Input:[/cyan]") - console.print(data["input"]) - if data.get("output"): - console.print("\n[cyan]Output:[/cyan]") - console.print(data["output"]) - - -@generations.command(name="stats") -@click.option("--range", "-r", "time_range", default="7d", help="Time range") -@click.option("--by", type=click.Choice(["model", "prompt", "user"]), default="model") -def generations_stats(time_range: str, by: str): - """Show generation statistics.""" - resp = _request( - "get", "/v1/generations/stats", params={"range": time_range, "group_by": by} - ) - data = check_response(resp) - - console.print(f"[cyan]Generation Statistics (last {time_range}):[/cyan]") - console.print() - - table = Table(title=f"By {by.title()}", box=box.ROUNDED) - table.add_column(by.title(), style="cyan") - table.add_column("Calls", style="white") - table.add_column("Tokens", style="yellow") - table.add_column("Cost", style="green") - table.add_column("Avg Latency", style="dim") - - for s in data.get("stats", []): - table.add_row( - s.get("key", ""), - str(s.get("calls", 0)), - str(s.get("total_tokens", 0)), - f"${s.get('total_cost', 0):.2f}", - s.get("avg_latency", ""), - ) - - console.print(table) - - total = data.get("total", {}) - if total: - console.print(f"\n[bold]Total Cost:[/bold] ${total.get('cost', 0):.2f}") - - -# ============================================================================ -# Evaluations (Langfuse-style) -# ============================================================================ - - -@o11y_group.group() -def evals(): - """Manage LLM evaluations (Langfuse-style).""" - pass - - -@evals.command(name="list") -@click.option( - "--status", - type=click.Choice(["pending", "running", "completed", "all"]), - default="all", -) -def evals_list(status: str): - """List evaluations.""" - params: dict = {} - if status != "all": - params["status"] = status - resp = _request("get", "/v1/evals", params=params) - data = check_response(resp) - - table = Table(title="Evaluations", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Dataset", style="white") - table.add_column("Prompt", style="white") - table.add_column("Status", style="green") - table.add_column("Score", style="yellow") - table.add_column("Created", style="dim") - - for e in data.get("evals", []): - st = e.get("status", "pending") - st_style = { - "completed": "green", - "running": "cyan", - "pending": "yellow", - "failed": "red", - }.get(st, "white") - score = f"{e.get('score', 0):.2f}" if e.get("score") is not None else "-" - table.add_row( - e.get("name", ""), - e.get("dataset", ""), - e.get("prompt", ""), - f"[{st_style}]{st}[/{st_style}]", - score, - e.get("created_at", ""), - ) - - console.print(table) - - -@evals.command(name="create") -@click.option("--name", "-n", required=True, help="Evaluation name") -@click.option("--dataset", "-d", required=True, help="Dataset to use") -@click.option("--prompt", "-p", required=True, help="Prompt to evaluate") -@click.option("--scorer", "-s", multiple=True, help="Scorer(s) to use") -def evals_create(name: str, dataset: str, prompt: str, scorer: tuple): - """Create an evaluation run.""" - payload: dict = {"name": name, "dataset": dataset, "prompt": prompt} - if scorer: - payload["scorers"] = list(scorer) - resp = _request("post", "/v1/evals", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Evaluation '{name}' created") - console.print(f" ID: {data.get('id', '')}") - console.print(f" Run with: hanzo o11y evals run {name}") - - -@evals.command(name="run") -@click.argument("name") -@click.option("--parallel", "-p", default=5, help="Parallel executions") -def evals_run(name: str, parallel: int): - """Run an evaluation.""" - resp = _request("post", f"/v1/evals/{name}/run", json={"parallel": parallel}) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Evaluation '{name}' started") - console.print(f" Run ID: {data.get('run_id', '')}") - console.print(f" Parallelism: {parallel}") - - -@evals.command(name="results") -@click.argument("name") -@click.option( - "--format", "-f", "fmt", type=click.Choice(["table", "json"]), default="table" -) -def evals_results(name: str, fmt: str): - """Show evaluation results.""" - resp = _request("get", f"/v1/evals/{name}/results") - data = check_response(resp) - - if fmt == "json": - console.print(json.dumps(data, indent=2)) - return - - info = ( - f"[cyan]Evaluation:[/cyan] {data.get('name', name)}\n" - f"[cyan]Status:[/cyan] {data.get('status', 'unknown')}\n" - f"[cyan]Samples:[/cyan] {data.get('sample_count', 0)}\n" - f"[cyan]Avg Score:[/cyan] {data.get('avg_score', 0):.2f}\n" - f"[cyan]Duration:[/cyan] {data.get('duration', 'N/A')}" - ) - console.print(Panel(info, title="Evaluation Results", border_style="cyan")) - - scores = data.get("scorer_results", []) - if scores: - table = Table(title="Scorer Breakdown", box=box.ROUNDED) - table.add_column("Scorer", style="cyan") - table.add_column("Mean", style="yellow") - table.add_column("Median", style="yellow") - table.add_column("Min", style="dim") - table.add_column("Max", style="dim") - - for s in scores: - table.add_row( - s.get("name", ""), - f"{s.get('mean', 0):.2f}", - f"{s.get('median', 0):.2f}", - f"{s.get('min', 0):.2f}", - f"{s.get('max', 0):.2f}", - ) - - console.print(table) - - -# ============================================================================ -# Scores (Metrics & Feedback) -# ============================================================================ - - -@o11y_group.group() -def scores(): - """Manage scores and feedback (Langfuse-style).""" - pass - - -@scores.command(name="list") -@click.option("--trace", "-t", help="Filter by trace ID") -@click.option("--name", "-n", help="Filter by score name") -@click.option( - "--source", type=click.Choice(["api", "human", "model", "all"]), default="all" -) -@click.option("--limit", default=50, help="Max results") -def scores_list(trace: str, name: str, source: str, limit: int): - """List scores.""" - params: dict = {"limit": limit} - if trace: - params["trace_id"] = trace - if name: - params["name"] = name - if source != "all": - params["source"] = source - resp = _request("get", "/v1/scores", params=params) - data = check_response(resp) - - table = Table(title="Scores", box=box.ROUNDED) - table.add_column("Trace ID", style="cyan") - table.add_column("Name", style="white") - table.add_column("Value", style="yellow") - table.add_column("Source", style="green") - table.add_column("Comment", style="dim") - table.add_column("Created", style="dim") - - for s in data.get("scores", []): - table.add_row( - s.get("trace_id", "")[:16], - s.get("name", ""), - f"{s.get('value', 0):.2f}", - s.get("source", ""), - s.get("comment", ""), - s.get("created_at", ""), - ) - - console.print(table) - - -@scores.command(name="add") -@click.option("--trace", "-t", required=True, help="Trace ID") -@click.option("--name", "-n", required=True, help="Score name") -@click.option("--value", "-v", type=float, required=True, help="Score value (0-1)") -@click.option("--comment", "-c", help="Optional comment") -@click.option( - "--source", "-s", type=click.Choice(["api", "human", "model"]), default="api" -) -def scores_add(trace: str, name: str, value: float, comment: str, source: str): - """Add a score to a trace.""" - payload: dict = {"trace_id": trace, "name": name, "value": value, "source": source} - if comment: - payload["comment"] = comment - resp = _request("post", "/v1/scores", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Score added") - console.print(f" Trace: {trace}") - console.print(f" {name}: {value}") - - -@scores.command(name="stats") -@click.option("--name", "-n", help="Score name") -@click.option("--range", "-r", "time_range", default="7d", help="Time range") -def scores_stats(name: str, time_range: str): - """Show score statistics.""" - params: dict = {"range": time_range} - if name: - params["name"] = name - resp = _request("get", "/v1/scores/stats", params=params) - data = check_response(resp) - - console.print(f"[cyan]Score Statistics (last {time_range}):[/cyan]") - console.print() - - table = Table(title="Score Summary", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Count", style="white") - table.add_column("Mean", style="yellow") - table.add_column("Median", style="yellow") - table.add_column("Min", style="dim") - table.add_column("Max", style="dim") - - for s in data.get("stats", []): - table.add_row( - s.get("name", ""), - str(s.get("count", 0)), - f"{s.get('mean', 0):.2f}", - f"{s.get('median', 0):.2f}", - f"{s.get('min', 0):.2f}", - f"{s.get('max', 0):.2f}", - ) - - console.print(table) - - -# ============================================================================ -# Datasets (Test Data for Evals) -# ============================================================================ - - -@o11y_group.group() -def datasets(): - """Manage evaluation datasets (Langfuse-style).""" - pass - - -@datasets.command(name="list") -def datasets_list(): - """List datasets.""" - resp = _request("get", "/v1/datasets") - data = check_response(resp) - - table = Table(title="Datasets", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Items", style="white") - table.add_column("Runs", style="green") - table.add_column("Last Run", style="dim") - table.add_column("Updated", style="dim") - - for d in data.get("datasets", []): - table.add_row( - d.get("name", ""), - str(d.get("item_count", 0)), - str(d.get("run_count", 0)), - d.get("last_run_at", ""), - d.get("updated_at", ""), - ) - - console.print(table) - - -@datasets.command(name="create") -@click.argument("name") -@click.option("--description", "-d", help="Dataset description") -def datasets_create(name: str, description: str): - """Create a dataset.""" - payload: dict = {"name": name} - if description: - payload["description"] = description - resp = _request("post", "/v1/datasets", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Dataset '{name}' created") - - -@datasets.command(name="delete") -@click.argument("name") -def datasets_delete(name: str): - """Delete a dataset.""" - resp = _request("delete", f"/v1/datasets/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Dataset '{name}' deleted") - - -@datasets.command(name="add-item") -@click.argument("dataset_name") -@click.option("--input", "-i", "input_data", required=True, help="Input JSON") -@click.option("--expected", "-e", help="Expected output") -@click.option("--metadata", "-m", help="Metadata JSON") -def datasets_add_item(dataset_name: str, input_data: str, expected: str, metadata: str): - """Add an item to a dataset.""" - payload: dict = {"input": json.loads(input_data)} - if expected: - payload["expected_output"] = expected - if metadata: - payload["metadata"] = json.loads(metadata) - resp = _request("post", f"/v1/datasets/{dataset_name}/items", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Item added to '{dataset_name}'") - - -@datasets.command(name="import") -@click.argument("dataset_name") -@click.option( - "--file", "-f", "file_path", required=True, help="File to import (JSON/CSV)" -) -def datasets_import(dataset_name: str, file_path: str): - """Import items from file.""" - with open(file_path) as f: - content = f.read() - - if file_path.endswith(".csv"): - resp = _request( - "post", - f"/v1/datasets/{dataset_name}/import", - content=content, - headers={"Content-Type": "text/csv"}, - ) - else: - resp = _request( - "post", f"/v1/datasets/{dataset_name}/import", json=json.loads(content) - ) - data = check_response(resp) - console.print( - f"[green]โœ“[/green] Imported {data.get('count', '?')} items to '{dataset_name}'" - ) - - -@datasets.command(name="export") -@click.argument("dataset_name") -@click.option("--output", "-o", help="Output file") -@click.option( - "--format", "-f", "fmt", type=click.Choice(["json", "csv"]), default="json" -) -def datasets_export(dataset_name: str, output: str, fmt: str): - """Export dataset to file.""" - resp = _request( - "get", f"/v1/datasets/{dataset_name}/export", params={"format": fmt} - ) - data = check_response(resp) - - out_file = output or f"{dataset_name}.{fmt}" - content = data.get("data", "") - with open(out_file, "w") as f: - f.write(content if isinstance(content, str) else json.dumps(content, indent=2)) - console.print(f"[green]โœ“[/green] Exported to '{out_file}'") - - -# ============================================================================ -# Sessions (Conversation Tracking) -# ============================================================================ - - -@o11y_group.group() -def sessions(): - """Track conversation sessions (Langfuse-style).""" - pass - - -@sessions.command(name="list") -@click.option("--user", "-u", help="Filter by user ID") -@click.option("--limit", "-n", default=50, help="Max results") -def sessions_list(user: str, limit: int): - """List sessions.""" - params: dict = {"limit": limit} - if user: - params["user"] = user - resp = _request("get", "/v1/sessions", params=params) - data = check_response(resp) - - table = Table(title="Sessions", box=box.ROUNDED) - table.add_column("Session ID", style="cyan") - table.add_column("User", style="white") - table.add_column("Traces", style="green") - table.add_column("Duration", style="yellow") - table.add_column("Started", style="dim") - - for s in data.get("sessions", []): - table.add_row( - s.get("id", "")[:16], - s.get("user_id", ""), - str(s.get("trace_count", 0)), - s.get("duration", ""), - s.get("created_at", ""), - ) - - console.print(table) - - -@sessions.command(name="show") -@click.argument("session_id") -def sessions_show(session_id: str): - """Show session details.""" - resp = _request("get", f"/v1/sessions/{session_id}") - data = check_response(resp) - - info = ( - f"[cyan]Session ID:[/cyan] {data.get('id', session_id)}\n" - f"[cyan]User:[/cyan] {data.get('user_id', 'N/A')}\n" - f"[cyan]Traces:[/cyan] {data.get('trace_count', 0)}\n" - f"[cyan]Generations:[/cyan] {data.get('generation_count', 0)}\n" - f"[cyan]Total Tokens:[/cyan] {data.get('total_tokens', 0):,}\n" - f"[cyan]Total Cost:[/cyan] ${data.get('total_cost', 0):.2f}\n" - f"[cyan]Duration:[/cyan] {data.get('duration', 'N/A')}\n" - f"[cyan]Avg Score:[/cyan] {data.get('avg_score', 0):.2f}" - ) - console.print(Panel(info, title="Session Details", border_style="cyan")) - - -@sessions.command(name="traces") -@click.argument("session_id") -def sessions_traces(session_id: str): - """List traces in a session.""" - resp = _request("get", f"/v1/sessions/{session_id}/traces") - data = check_response(resp) - - table = Table(title=f"Traces in Session '{session_id[:16]}'", box=box.ROUNDED) - table.add_column("Trace ID", style="cyan") - table.add_column("Name", style="white") - table.add_column("Generations", style="green") - table.add_column("Tokens", style="yellow") - table.add_column("Score", style="yellow") - table.add_column("Time", style="dim") - - for t in data.get("traces", []): - score = f"{t.get('score', 0):.2f}" if t.get("score") is not None else "-" - table.add_row( - t.get("id", "")[:16], - t.get("name", ""), - str(t.get("generation_count", 0)), - str(t.get("total_tokens", 0)), - score, - t.get("created_at", ""), - ) - - console.print(table) - - -# ============================================================================ -# LLM Traces (Enhanced for Langfuse) -# ============================================================================ - - -@o11y_group.group() -def llm(): - """LLM-specific observability (Langfuse-style).""" - pass - - -@llm.command(name="traces") -@click.option("--user", "-u", help="Filter by user ID") -@click.option("--name", "-n", help="Filter by trace name") -@click.option("--session", "-s", help="Filter by session ID") -@click.option("--limit", default=50, help="Max results") -def llm_traces(user: str, name: str, session: str, limit: int): - """List LLM traces.""" - params: dict = {"limit": limit} - if user: - params["user"] = user - if name: - params["name"] = name - if session: - params["session_id"] = session - resp = _request("get", "/v1/llm/traces", params=params) - data = check_response(resp) - - table = Table(title="LLM Traces", box=box.ROUNDED) - table.add_column("Trace ID", style="cyan") - table.add_column("Name", style="white") - table.add_column("User", style="white") - table.add_column("Generations", style="green") - table.add_column("Tokens", style="yellow") - table.add_column("Cost", style="green") - table.add_column("Duration", style="dim") - - for t in data.get("traces", []): - table.add_row( - t.get("id", "")[:16], - t.get("name", ""), - t.get("user_id", ""), - str(t.get("generation_count", 0)), - str(t.get("total_tokens", 0)), - f"${t.get('total_cost', 0):.4f}", - t.get("duration", ""), - ) - - console.print(table) - - -@llm.command(name="costs") -@click.option("--range", "-r", "time_range", default="30d", help="Time range") -@click.option( - "--by", type=click.Choice(["model", "user", "prompt", "day"]), default="model" -) -def llm_costs(time_range: str, by: str): - """Show LLM cost analysis.""" - resp = _request( - "get", "/v1/llm/costs", params={"range": time_range, "group_by": by} - ) - data = check_response(resp) - - console.print(f"[cyan]LLM Cost Analysis (last {time_range}):[/cyan]") - console.print() - - table = Table(title=f"Costs by {by.title()}", box=box.ROUNDED) - table.add_column(by.title(), style="cyan") - table.add_column("Calls", style="white") - table.add_column("Input Tokens", style="yellow") - table.add_column("Output Tokens", style="yellow") - table.add_column("Cost", style="green") - - for item in data.get("costs", []): - table.add_row( - item.get("key", ""), - str(item.get("calls", 0)), - str(item.get("input_tokens", 0)), - str(item.get("output_tokens", 0)), - f"${item.get('cost', 0):.2f}", - ) - - console.print(table) - total = data.get("total_cost", 0) - console.print(f"\n[bold]Total Cost:[/bold] ${total:.2f}") - - -@llm.command(name="latency") -@click.option("--range", "-r", "time_range", default="24h", help="Time range") -@click.option( - "--by", type=click.Choice(["model", "prompt", "endpoint"]), default="model" -) -def llm_latency(time_range: str, by: str): - """Show LLM latency analysis.""" - resp = _request( - "get", "/v1/llm/latency", params={"range": time_range, "group_by": by} - ) - data = check_response(resp) - - console.print(f"[cyan]LLM Latency Analysis (last {time_range}):[/cyan]") - console.print() - - table = Table(title=f"Latency by {by.title()}", box=box.ROUNDED) - table.add_column(by.title(), style="cyan") - table.add_column("Calls", style="white") - table.add_column("P50", style="yellow") - table.add_column("P90", style="yellow") - table.add_column("P99", style="red") - - for item in data.get("latency", []): - table.add_row( - item.get("key", ""), - str(item.get("calls", 0)), - item.get("p50", ""), - item.get("p90", ""), - item.get("p99", ""), - ) - - console.print(table) - - -@llm.command(name="errors") -@click.option("--range", "-r", "time_range", default="24h", help="Time range") -@click.option("--model", "-m", help="Filter by model") -def llm_errors(time_range: str, model: str): - """Show LLM error analysis.""" - params: dict = {"range": time_range} - if model: - params["model"] = model - resp = _request("get", "/v1/llm/errors", params=params) - data = check_response(resp) - - console.print(f"[cyan]LLM Errors (last {time_range}):[/cyan]") - console.print() - - table = Table(title="Error Summary", box=box.ROUNDED) - table.add_column("Error Type", style="cyan") - table.add_column("Count", style="red") - table.add_column("Model", style="white") - table.add_column("Last Seen", style="dim") - - for e in data.get("errors", []): - table.add_row( - e.get("type", ""), - str(e.get("count", 0)), - e.get("model", ""), - e.get("last_seen", ""), - ) - - console.print(table) - - total = sum(e.get("count", 0) for e in data.get("errors", [])) - if total: - console.print(f"\n[bold]Total Errors:[/bold] {total}") diff --git a/pkg/hanzo/src/hanzo/commands/platform.py b/pkg/hanzo/src/hanzo/commands/platform.py deleted file mode 100644 index cd248cae4..000000000 --- a/pkg/hanzo/src/hanzo/commands/platform.py +++ /dev/null @@ -1,706 +0,0 @@ -"""Hanzo Platform - Infrastructure and security CLI. - -Edge, HKE, networking, tunnel, DNS, guard. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -SERVICE_URL = os.getenv("HANZO_PLATFORM_URL", "https://platform.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(SERVICE_URL, method, path, **kwargs) - - -@click.group(name="platform") -def platform_group(): - """Hanzo Platform - Infrastructure and security. - - \b - Edge & CDN: - hanzo platform edge deploy # Deploy to edge - hanzo platform edge list # List edge deployments - - \b - Kubernetes (HKE): - hanzo platform hke list # List clusters - hanzo platform hke create # Create cluster - - \b - Networking: - hanzo platform tunnel share # Share localhost - hanzo platform dns list # List DNS records - - \b - Security: - hanzo platform guard enable # Enable LLM safety layer - hanzo platform kms list # List secrets - """ - pass - - -# ============================================================================ -# Edge -# ============================================================================ - - -@platform_group.group() -def edge(): - """Manage edge deployments and CDN.""" - pass - - -@edge.command(name="list") -def edge_list(): - """List edge deployments.""" - resp = _request("get", "/v1/edge/deployments") - data = check_response(resp) - - table = Table(title="Edge Deployments", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("URL", style="white") - table.add_column("Regions", style="dim") - table.add_column("Status", style="green") - - for d in data.get("deployments", []): - st = d.get("status", "active") - st_style = "green" if st == "active" else "yellow" - table.add_row( - d.get("name", ""), - d.get("url", ""), - ", ".join(d.get("regions", [])), - f"[{st_style}]{st}[/{st_style}]", - ) - - console.print(table) - - -@edge.command(name="deploy") -@click.option("--name", "-n", prompt=True, help="Deployment name") -@click.option("--dir", "-d", "source_dir", default=".", help="Directory to deploy") -@click.option( - "--regions", "-r", default="all", help="Target regions (comma-separated or 'all')" -) -def edge_deploy(name: str, source_dir: str, regions: str): - """Deploy to edge locations.""" - region_list = ( - [r.strip() for r in regions.split(",")] if regions != "all" else ["all"] - ) - resp = _request( - "post", - "/v1/edge/deployments", - json={ - "name": name, - "source_dir": source_dir, - "regions": region_list, - }, - ) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Deployed '{name}' to edge") - console.print(f"[dim]URL: {data.get('url', f'https://{name}.edge.hanzo.ai')}[/dim]") - - -@edge.command(name="purge") -@click.argument("name") -@click.option("--path", "-p", help="Specific path to purge") -def edge_purge(name: str, path: str): - """Purge edge cache.""" - payload: dict = {} - if path: - payload["path"] = path - resp = _request("post", f"/v1/edge/deployments/{name}/purge", json=payload) - check_response(resp) - if path: - console.print(f"[green]โœ“[/green] Purged cache for {path}") - else: - console.print(f"[green]โœ“[/green] Purged all cache for {name}") - - -@edge.command(name="delete") -@click.argument("name") -def edge_delete(name: str): - """Delete an edge deployment.""" - resp = _request("delete", f"/v1/edge/deployments/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Edge deployment '{name}' deleted") - - -@edge.command(name="stats") -@click.argument("name") -@click.option("--period", "-p", default="24h", help="Time period") -def edge_stats(name: str, period: str): - """Show edge deployment statistics.""" - resp = _request( - "get", f"/v1/edge/deployments/{name}/stats", params={"period": period} - ) - data = check_response(resp) - - info = ( - f"[cyan]Deployment:[/cyan] {name}\n" - f"[cyan]Requests:[/cyan] {data.get('requests', 0):,}\n" - f"[cyan]Bandwidth:[/cyan] {data.get('bandwidth', 'N/A')}\n" - f"[cyan]Cache Hit Rate:[/cyan] {data.get('cache_hit_rate', 0):.0f}%\n" - f"[cyan]Avg Latency:[/cyan] {data.get('avg_latency', 'N/A')}" - ) - console.print(Panel(info, title="Edge Stats", border_style="cyan")) - - -# ============================================================================ -# HKE (Kubernetes) -# ============================================================================ - - -@platform_group.group() -def hke(): - """Manage Hanzo Kubernetes Engine clusters.""" - pass - - -@hke.command(name="list") -def hke_list(): - """List Kubernetes clusters.""" - resp = _request("get", "/v1/hke/clusters") - data = check_response(resp) - - table = Table(title="HKE Clusters", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Version", style="white") - table.add_column("Nodes", style="white") - table.add_column("Status", style="green") - table.add_column("Region", style="dim") - - for c in data.get("clusters", []): - st = c.get("status", "running") - st_style = {"running": "green", "provisioning": "yellow", "error": "red"}.get( - st, "white" - ) - table.add_row( - c.get("name", ""), - c.get("version", ""), - str(c.get("node_count", 0)), - f"[{st_style}]{st}[/{st_style}]", - c.get("region", ""), - ) - - console.print(table) - - -@hke.command(name="create") -@click.option("--name", "-n", prompt=True, help="Cluster name") -@click.option("--version", "-v", default="1.29", help="Kubernetes version") -@click.option("--nodes", default=3, help="Number of nodes") -@click.option("--region", "-r", default="us-west-2", help="Region") -@click.option("--gpu", is_flag=True, help="Enable GPU node pool") -@click.option("--node-size", default="s-4vcpu-8gb", help="Node size") -def hke_create( - name: str, version: str, nodes: int, region: str, gpu: bool, node_size: str -): - """Create a Kubernetes cluster.""" - resp = _request( - "post", - "/v1/hke/clusters", - json={ - "name": name, - "version": version, - "node_count": nodes, - "region": region, - "gpu": gpu, - "node_size": node_size, - }, - ) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Cluster '{name}' creation initiated") - console.print(f" Version: {version}") - console.print(f" Nodes: {nodes}") - console.print(f" Region: {region}") - console.print(f" GPU: {'Yes' if gpu else 'No'}") - console.print(f" ID: {data.get('id', '')}") - - -@hke.command(name="delete") -@click.argument("name") -@click.option("--confirm", "confirmed", is_flag=True, help="Skip confirmation") -def hke_delete(name: str, confirmed: bool): - """Delete a Kubernetes cluster.""" - if not confirmed: - from rich.prompt import Confirm - - if not Confirm.ask( - f"[red]Delete cluster '{name}'? This cannot be undone.[/red]" - ): - return - resp = _request("delete", f"/v1/hke/clusters/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Cluster '{name}' deletion initiated") - - -@hke.command(name="kubeconfig") -@click.argument("name") -@click.option("--output", "-o", help="Output file path") -def hke_kubeconfig(name: str, output: str): - """Get kubeconfig for a cluster.""" - resp = _request("get", f"/v1/hke/clusters/{name}/kubeconfig") - data = check_response(resp) - - kubeconfig = data.get("kubeconfig", "") - if output: - with open(output, "w") as f: - f.write(kubeconfig) - console.print(f"[green]โœ“[/green] Kubeconfig saved to {output}") - else: - import os as _os - - kube_path = _os.path.expanduser("~/.kube/config") - with open(kube_path, "w") as f: - f.write(kubeconfig) - console.print(f"[green]โœ“[/green] Kubeconfig saved to ~/.kube/config") - - -@hke.command(name="scale") -@click.argument("name") -@click.option("--nodes", "-n", required=True, type=int, help="Target nodes") -def hke_scale(name: str, nodes: int): - """Scale cluster nodes.""" - resp = _request( - "post", f"/v1/hke/clusters/{name}/scale", json={"node_count": nodes} - ) - check_response(resp) - console.print(f"[green]โœ“[/green] Cluster '{name}' scaling to {nodes} nodes") - - -@hke.command(name="upgrade") -@click.argument("name") -@click.option("--version", "-v", required=True, help="Target Kubernetes version") -def hke_upgrade(name: str, version: str): - """Upgrade cluster Kubernetes version.""" - resp = _request( - "post", f"/v1/hke/clusters/{name}/upgrade", json={"version": version} - ) - check_response(resp) - console.print(f"[green]โœ“[/green] Cluster '{name}' upgrading to {version}") - - -# ============================================================================ -# Tunnel -# ============================================================================ - - -@platform_group.group() -def tunnel(): - """Manage secure tunnels.""" - pass - - -@tunnel.command(name="share") -@click.argument("target") -@click.option("--name", "-n", help="Subdomain name") -@click.option("--auth", is_flag=True, help="Require authentication") -def tunnel_share(target: str, name: str, auth: bool): - """Share local service via secure tunnel.""" - payload: dict = {"target": target} - if name: - payload["subdomain"] = name - if auth: - payload["auth_required"] = True - - resp = _request("post", "/v1/tunnels", json=payload) - data = check_response(resp) - - subdomain = data.get("subdomain", name or "random") - url = data.get("url", f"https://{subdomain}.tunnel.hanzo.ai") - - console.print(f"[green]โœ“[/green] Tunnel active") - console.print(f" [cyan]Public URL:[/cyan] {url}") - console.print(f" [cyan]Target:[/cyan] {target}") - if auth: - console.print(f" [cyan]Auth:[/cyan] Required") - console.print() - console.print("Press Ctrl+C to stop") - - try: - tunnel_id = data.get("id", "") - import time - - while True: - time.sleep(30) - _request("post", f"/v1/tunnels/{tunnel_id}/keepalive") - except KeyboardInterrupt: - _request("delete", f"/v1/tunnels/{tunnel_id}") - console.print("\n[dim]Tunnel closed[/dim]") - - -@tunnel.command(name="list") -def tunnel_list(): - """List active tunnels.""" - resp = _request("get", "/v1/tunnels") - data = check_response(resp) - - table = Table(title="Active Tunnels", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Target", style="white") - table.add_column("URL", style="dim") - table.add_column("Status", style="green") - - for t in data.get("tunnels", []): - st = t.get("status", "active") - st_style = "green" if st == "active" else "yellow" - table.add_row( - t.get("subdomain", ""), - t.get("target", ""), - t.get("url", ""), - f"[{st_style}]{st}[/{st_style}]", - ) - - console.print(table) - - -@tunnel.command(name="stop") -@click.argument("name") -def tunnel_stop(name: str): - """Stop a tunnel.""" - resp = _request("delete", f"/v1/tunnels/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Tunnel '{name}' stopped") - - -# ============================================================================ -# DNS -# ============================================================================ - - -@platform_group.group() -def dns(): - """Manage DNS records.""" - pass - - -@dns.command(name="zones") -def dns_zones(): - """List DNS zones.""" - resp = _request("get", "/v1/dns/zones") - data = check_response(resp) - - table = Table(title="DNS Zones", box=box.ROUNDED) - table.add_column("Zone", style="cyan") - table.add_column("Records", style="white") - table.add_column("Status", style="green") - - for z in data.get("zones", []): - table.add_row( - z.get("name", ""), - str(z.get("record_count", 0)), - z.get("status", "active"), - ) - - console.print(table) - - -@dns.command(name="list") -@click.argument("zone", required=False) -def dns_list(zone: str): - """List DNS records.""" - path = f"/v1/dns/zones/{zone}/records" if zone else "/v1/dns/records" - resp = _request("get", path) - data = check_response(resp) - - table = Table(title=f"DNS Records{' for ' + zone if zone else ''}", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Type", style="white") - table.add_column("Value", style="dim") - table.add_column("TTL", style="dim") - table.add_column("Proxied", style="green") - - for r in data.get("records", []): - proxied = r.get("proxied", False) - table.add_row( - r.get("name", ""), - r.get("type", ""), - r.get("value", ""), - str(r.get("ttl", 300)), - "[green]yes[/green]" if proxied else "[dim]no[/dim]", - ) - - console.print(table) - - -@dns.command(name="create") -@click.option("--zone", "-z", required=True, help="DNS zone") -@click.option("--name", "-n", required=True, help="Record name") -@click.option( - "--type", "-t", "record_type", required=True, help="Record type (A, CNAME, etc.)" -) -@click.option("--value", "-v", required=True, help="Record value") -@click.option("--ttl", default=300, help="TTL in seconds") -@click.option("--proxied", is_flag=True, help="Enable proxy") -def dns_create( - zone: str, name: str, record_type: str, value: str, ttl: int, proxied: bool -): - """Create a DNS record.""" - resp = _request( - "post", - f"/v1/dns/zones/{zone}/records", - json={ - "name": name, - "type": record_type, - "value": value, - "ttl": ttl, - "proxied": proxied, - }, - ) - check_response(resp) - console.print( - f"[green]โœ“[/green] DNS record created: {name}.{zone} {record_type} {value}" - ) - - -@dns.command(name="delete") -@click.option("--zone", "-z", required=True) -@click.option("--name", "-n", required=True) -@click.option("--type", "-t", "record_type", required=True) -def dns_delete(zone: str, name: str, record_type: str): - """Delete a DNS record.""" - resp = _request( - "delete", - f"/v1/dns/zones/{zone}/records", - params={"name": name, "type": record_type}, - ) - check_response(resp) - console.print(f"[green]โœ“[/green] DNS record deleted: {name}.{zone} {record_type}") - - -# ============================================================================ -# Guard (LLM Safety) -# ============================================================================ - - -@platform_group.group() -def guard(): - """Manage LLM safety layer.""" - pass - - -@guard.command(name="enable") -@click.option("--project", "-p", help="Project ID") -@click.option( - "--mode", - "-m", - type=click.Choice(["block", "warn", "log"]), - default="block", - help="Enforcement mode", -) -def guard_enable(project: str, mode: str): - """Enable LLM guard for project.""" - payload: dict = {"enabled": True, "mode": mode} - if project: - payload["project_id"] = project - resp = _request("post", "/v1/guard/config", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Guard enabled (mode: {mode})") - console.print("[dim]All LLM requests will be scanned for:[/dim]") - console.print(" - Prompt injection") - console.print(" - PII leakage") - console.print(" - Harmful content") - - -@guard.command(name="disable") -@click.option("--project", "-p", help="Project ID") -def guard_disable(project: str): - """Disable LLM guard.""" - payload: dict = {"enabled": False} - if project: - payload["project_id"] = project - resp = _request("post", "/v1/guard/config", json=payload) - check_response(resp) - console.print("[yellow]Guard disabled[/yellow]") - - -@guard.command(name="status") -@click.option("--project", "-p", help="Project ID") -def guard_status(project: str): - """Show guard status and stats.""" - params: dict = {} - if project: - params["project_id"] = project - resp = _request("get", "/v1/guard/status", params=params) - data = check_response(resp) - - enabled = data.get("enabled", False) - status_str = "[green]Active[/green]" if enabled else "[red]Disabled[/red]" - - info = ( - f"[cyan]Status:[/cyan] {status_str}\n" - f"[cyan]Mode:[/cyan] {data.get('mode', 'N/A')}\n" - f"[cyan]Requests scanned:[/cyan] {data.get('requests_scanned', 0):,}\n" - f"[cyan]Threats blocked:[/cyan] {data.get('threats_blocked', 0):,}\n" - f"[cyan]PII detected:[/cyan] {data.get('pii_detected', 0):,}\n" - f"[cyan]Injections caught:[/cyan] {data.get('injections_caught', 0):,}" - ) - console.print(Panel(info, title="Guard Status", border_style="cyan")) - - -@guard.command(name="logs") -@click.option("--limit", "-n", default=50, help="Number of logs") -@click.option( - "--type", - "-t", - "log_type", - type=click.Choice(["all", "blocked", "warned", "pii", "injection"]), - default="all", -) -def guard_logs(limit: int, log_type: str): - """View guard logs.""" - params: dict = {"limit": limit} - if log_type != "all": - params["type"] = log_type - resp = _request("get", "/v1/guard/logs", params=params) - data = check_response(resp) - - table = Table(title="Guard Logs", box=box.ROUNDED) - table.add_column("Time", style="dim") - table.add_column("Type", style="yellow") - table.add_column("Action", style="green") - table.add_column("Details", style="white") - - for entry in data.get("logs", []): - action = entry.get("action", "log") - action_style = {"blocked": "red", "warned": "yellow"}.get(action, "green") - table.add_row( - entry.get("timestamp", ""), - entry.get("type", ""), - f"[{action_style}]{action}[/{action_style}]", - entry.get("details", ""), - ) - - console.print(table) - - -@guard.command(name="rules") -def guard_rules(): - """List guard rules.""" - resp = _request("get", "/v1/guard/rules") - data = check_response(resp) - - table = Table(title="Guard Rules", box=box.ROUNDED) - table.add_column("Rule", style="cyan") - table.add_column("Category", style="white") - table.add_column("Action", style="green") - table.add_column("Status", style="dim") - - for r in data.get("rules", []): - enabled = r.get("enabled", True) - status_str = "[green]active[/green]" if enabled else "[dim]disabled[/dim]" - table.add_row( - r.get("name", ""), - r.get("category", ""), - r.get("action", "block"), - status_str, - ) - - console.print(table) - - -# ============================================================================ -# KMS (Secrets) -# ============================================================================ - - -@platform_group.group() -def kms(): - """Manage secrets and encryption.""" - pass - - -@kms.command(name="list") -@click.option("--workspace", "-w", help="Workspace ID") -def kms_list(workspace: str): - """List secrets.""" - params: dict = {} - if workspace: - params["workspace"] = workspace - resp = _request("get", "/v1/kms/secrets", params=params) - data = check_response(resp) - - table = Table(title="Secrets", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Version", style="white") - table.add_column("Updated", style="dim") - - for s in data.get("secrets", []): - table.add_row( - s.get("name", ""), - str(s.get("version", 1)), - s.get("updated_at", ""), - ) - - console.print(table) - - -@kms.command(name="set") -@click.argument("name") -@click.option("--value", "-v", prompt=True, hide_input=True, help="Secret value") -@click.option("--workspace", "-w", help="Workspace ID") -def kms_set(name: str, value: str, workspace: str): - """Set a secret.""" - payload: dict = {"name": name, "value": value} - if workspace: - payload["workspace"] = workspace - resp = _request("post", "/v1/kms/secrets", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Secret '{name}' set") - - -@kms.command(name="get") -@click.argument("name") -@click.option("--workspace", "-w", help="Workspace ID") -@click.option("--reveal", is_flag=True, help="Show actual value") -def kms_get(name: str, workspace: str, reveal: bool): - """Get a secret value.""" - params: dict = {} - if workspace: - params["workspace"] = workspace - resp = _request("get", f"/v1/kms/secrets/{name}", params=params) - data = check_response(resp) - - if reveal: - console.print(f"[yellow]Secret value:[/yellow] {data.get('value', '')}") - else: - console.print(f"[yellow]Secret value:[/yellow] ********") - console.print("[dim]Use --reveal to show actual value[/dim]") - - -@kms.command(name="delete") -@click.argument("name") -@click.option("--workspace", "-w", help="Workspace ID") -def kms_delete(name: str, workspace: str): - """Delete a secret.""" - params: dict = {} - if workspace: - params["workspace"] = workspace - resp = _request("delete", f"/v1/kms/secrets/{name}", params=params) - check_response(resp) - console.print(f"[green]โœ“[/green] Secret '{name}' deleted") - - -@kms.command(name="rotate") -@click.argument("name") -@click.option("--workspace", "-w", help="Workspace ID") -def kms_rotate(name: str, workspace: str): - """Rotate a secret.""" - payload: dict = {} - if workspace: - payload["workspace"] = workspace - resp = _request("post", f"/v1/kms/secrets/{name}/rotate", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Secret '{name}' rotated") diff --git a/pkg/hanzo/src/hanzo/commands/pubsub.py b/pkg/hanzo/src/hanzo/commands/pubsub.py deleted file mode 100644 index aa4d3bc0e..000000000 --- a/pkg/hanzo/src/hanzo/commands/pubsub.py +++ /dev/null @@ -1,423 +0,0 @@ -"""Hanzo Pub/Sub - Event streaming and messaging CLI. - -Topics, subscriptions, publish, consume. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -PUBSUB_URL = os.getenv("HANZO_PUBSUB_URL", "https://pubsub.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(PUBSUB_URL, method, path, **kwargs) - - -@click.group(name="pubsub") -def pubsub_group(): - """Hanzo Pub/Sub - Event streaming and messaging. - - \b - Topics: - hanzo pubsub topics list # List topics - hanzo pubsub topics create # Create topic - hanzo pubsub topics delete # Delete topic - - \b - Subscriptions: - hanzo pubsub subs list # List subscriptions - hanzo pubsub subs create # Create subscription - hanzo pubsub subs delete # Delete subscription - - \b - Messages: - hanzo pubsub publish # Publish message - hanzo pubsub pull # Pull messages - hanzo pubsub ack # Acknowledge messages - hanzo pubsub seek # Seek to timestamp/snapshot - """ - pass - - -# ============================================================================ -# Topics -# ============================================================================ - - -@pubsub_group.group() -def topics(): - """Manage pub/sub topics.""" - pass - - -@topics.command(name="list") -@click.option("--project", "-p", help="Project ID") -def topics_list(project: str): - """List all topics.""" - params = {} - if project: - params["project"] = project - - resp = _request("get", "/v1/topics", params=params) - data = check_response(resp) - items = data.get("topics", data.get("items", [])) - - table = Table(title="Topics", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Subscriptions", style="white") - table.add_column("Messages/day", style="green") - table.add_column("Retention", style="dim") - table.add_column("Created", style="dim") - - for t in items: - table.add_row( - t.get("name", ""), - str(t.get("subscription_count", 0)), - str(t.get("messages_per_day", 0)), - t.get("retention", "-"), - str(t.get("created_at", ""))[:10], - ) - - console.print(table) - if not items: - console.print( - "[dim]No topics found. Create one with 'hanzo pubsub topics create'[/dim]" - ) - - -@topics.command(name="create") -@click.argument("name") -@click.option("--retention", "-r", default="7d", help="Message retention period") -@click.option("--schema", "-s", help="Schema for message validation") -def topics_create(name: str, retention: str, schema: str): - """Create a topic.""" - payload = {"name": name, "retention": retention} - if schema: - payload["schema"] = schema - - resp = _request("post", "/v1/topics", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Topic '{name}' created") - console.print(f" ID: {data.get('id', '-')}") - console.print(f" Retention: {retention}") - if schema: - console.print(f" Schema: {schema}") - - -@topics.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True, help="Skip confirmation") -def topics_delete(name: str, force: bool): - """Delete a topic.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete topic '{name}' and all subscriptions?[/red]"): - return - - resp = _request("delete", f"/v1/topics/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Topic '{name}' deleted") - - -@topics.command(name="describe") -@click.argument("name") -def topics_describe(name: str): - """Show topic details.""" - resp = _request("get", f"/v1/topics/{name}") - data = check_response(resp) - - console.print( - Panel( - f"[cyan]Topic:[/cyan] {data.get('name', name)}\n" - f"[cyan]Subscriptions:[/cyan] {data.get('subscription_count', 0)}\n" - f"[cyan]Messages/day:[/cyan] {data.get('messages_per_day', 0):,}\n" - f"[cyan]Retention:[/cyan] {data.get('retention', '-')}\n" - f"[cyan]Schema:[/cyan] {data.get('schema', 'None')}\n" - f"[cyan]Created:[/cyan] {str(data.get('created_at', ''))[:19]}", - title="Topic Details", - border_style="cyan", - ) - ) - - -# ============================================================================ -# Subscriptions -# ============================================================================ - - -@pubsub_group.group() -def subs(): - """Manage subscriptions.""" - pass - - -@subs.command(name="list") -@click.option("--topic", "-t", help="Filter by topic") -def subs_list(topic: str): - """List subscriptions.""" - params = {} - if topic: - params["topic"] = topic - - resp = _request("get", "/v1/subscriptions", params=params) - data = check_response(resp) - items = data.get("subscriptions", data.get("items", [])) - - table = Table(title="Subscriptions", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Topic", style="white") - table.add_column("Type", style="green") - table.add_column("Pending", style="yellow") - table.add_column("Ack Deadline", style="dim") - - for s in items: - sub_type = "push" if s.get("push_endpoint") else "pull" - table.add_row( - s.get("name", ""), - s.get("topic", "-"), - sub_type, - str(s.get("pending_count", 0)), - f"{s.get('ack_deadline', 10)}s", - ) - - console.print(table) - if not items: - console.print("[dim]No subscriptions found[/dim]") - - -@subs.command(name="create") -@click.argument("name") -@click.option("--topic", "-t", required=True, help="Topic to subscribe to") -@click.option("--push-endpoint", help="Push endpoint URL") -@click.option("--ack-deadline", "-a", default=10, help="Ack deadline in seconds") -@click.option("--filter", "-f", help="Message filter expression") -def subs_create( - name: str, topic: str, push_endpoint: str, ack_deadline: int, filter: str -): - """Create a subscription.""" - payload = {"name": name, "topic": topic, "ack_deadline": ack_deadline} - if push_endpoint: - payload["push_endpoint"] = push_endpoint - if filter: - payload["filter"] = filter - - resp = _request("post", "/v1/subscriptions", json=payload) - data = check_response(resp) - - sub_type = "push" if push_endpoint else "pull" - console.print(f"[green]โœ“[/green] Subscription '{name}' created") - console.print(f" Topic: {topic}") - console.print(f" Type: {sub_type}") - console.print(f" Ack deadline: {ack_deadline}s") - if push_endpoint: - console.print(f" Push endpoint: {push_endpoint}") - if filter: - console.print(f" Filter: {filter}") - - -@subs.command(name="delete") -@click.argument("name") -def subs_delete(name: str): - """Delete a subscription.""" - resp = _request("delete", f"/v1/subscriptions/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Subscription '{name}' deleted") - - -@subs.command(name="describe") -@click.argument("name") -def subs_describe(name: str): - """Show subscription details.""" - resp = _request("get", f"/v1/subscriptions/{name}") - data = check_response(resp) - - sub_type = "Push" if data.get("push_endpoint") else "Pull" - console.print( - Panel( - f"[cyan]Subscription:[/cyan] {data.get('name', name)}\n" - f"[cyan]Topic:[/cyan] {data.get('topic', '-')}\n" - f"[cyan]Type:[/cyan] {sub_type}\n" - f"[cyan]Pending messages:[/cyan] {data.get('pending_count', 0):,}\n" - f"[cyan]Ack deadline:[/cyan] {data.get('ack_deadline', 10)}s\n" - f"[cyan]Filter:[/cyan] {data.get('filter', 'None')}", - title="Subscription Details", - border_style="cyan", - ) - ) - - -# ============================================================================ -# Publish / Pull / Ack -# ============================================================================ - - -@pubsub_group.command() -@click.argument("topic") -@click.option("--message", "-m", required=True, help="Message data") -@click.option("--attributes", "-a", multiple=True, help="Attributes (key=value)") -def publish(topic: str, message: str, attributes: tuple): - """Publish a message to a topic.""" - payload = {"data": message} - if attributes: - attrs = {} - for a in attributes: - k, v = a.split("=", 1) - attrs[k] = v - payload["attributes"] = attrs - - resp = _request("post", f"/v1/topics/{topic}/publish", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Message published to '{topic}'") - console.print(f" Message ID: {data.get('message_id', data.get('id', '-'))}") - if attributes: - console.print(f" Attributes: {', '.join(attributes)}") - - -@pubsub_group.command() -@click.argument("subscription") -@click.option("--max-messages", "-n", default=10, help="Max messages to pull") -@click.option("--wait", "-w", is_flag=True, help="Wait for messages") -@click.option("--auto-ack", is_flag=True, help="Automatically acknowledge messages") -def pull(subscription: str, max_messages: int, wait: bool, auto_ack: bool): - """Pull messages from a subscription.""" - params = {"max_messages": max_messages} - if wait: - params["wait"] = "true" - - resp = _request("post", f"/v1/subscriptions/{subscription}/pull", json=params) - data = check_response(resp) - messages = data.get("messages", []) - - for msg in messages: - console.print( - f"[dim]{msg.get('publish_time', '')}[/dim] " - f"[cyan]{msg.get('message_id', msg.get('id', ''))}[/cyan] " - f"{msg.get('data', '')}" - ) - if msg.get("attributes"): - console.print(f" [dim]attrs: {json.dumps(msg['attributes'])}[/dim]") - - if auto_ack and messages: - ack_ids = [m.get("ack_id") for m in messages if m.get("ack_id")] - if ack_ids: - _request( - "post", - f"/v1/subscriptions/{subscription}/ack", - json={"ack_ids": ack_ids}, - ) - console.print(f"[dim]Auto-acknowledged {len(ack_ids)} message(s)[/dim]") - - if not messages: - console.print("[dim]No messages available[/dim]") - - -@pubsub_group.command() -@click.argument("subscription") -@click.option( - "--ack-ids", "-a", multiple=True, required=True, help="Ack IDs to acknowledge" -) -def ack(subscription: str, ack_ids: tuple): - """Acknowledge messages.""" - resp = _request( - "post", f"/v1/subscriptions/{subscription}/ack", json={"ack_ids": list(ack_ids)} - ) - check_response(resp) - console.print(f"[green]โœ“[/green] Acknowledged {len(ack_ids)} message(s)") - - -@pubsub_group.command() -@click.argument("subscription") -@click.option("--time", "-t", help="Seek to timestamp (RFC3339)") -@click.option("--snapshot", "-s", help="Seek to snapshot") -def seek(subscription: str, time: str, snapshot: str): - """Seek subscription to a point in time or snapshot.""" - if not time and not snapshot: - raise click.ClickException("Specify --time or --snapshot") - - payload = {} - if time: - payload["time"] = time - if snapshot: - payload["snapshot"] = snapshot - - resp = _request("post", f"/v1/subscriptions/{subscription}/seek", json=payload) - check_response(resp) - - if time: - console.print( - f"[green]โœ“[/green] Subscription '{subscription}' seeked to {time}" - ) - else: - console.print( - f"[green]โœ“[/green] Subscription '{subscription}' seeked to snapshot '{snapshot}'" - ) - - -# ============================================================================ -# Snapshots -# ============================================================================ - - -@pubsub_group.group() -def snapshots(): - """Manage subscription snapshots.""" - pass - - -@snapshots.command(name="list") -def snapshots_list(): - """List snapshots.""" - resp = _request("get", "/v1/snapshots") - data = check_response(resp) - items = data.get("snapshots", data.get("items", [])) - - table = Table(title="Snapshots", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Subscription", style="white") - table.add_column("Created", style="dim") - table.add_column("Expires", style="dim") - - for s in items: - table.add_row( - s.get("name", ""), - s.get("subscription", "-"), - str(s.get("created_at", ""))[:19], - str(s.get("expires_at", ""))[:19], - ) - - console.print(table) - if not items: - console.print("[dim]No snapshots found[/dim]") - - -@snapshots.command(name="create") -@click.argument("name") -@click.option("--subscription", "-s", required=True, help="Subscription to snapshot") -def snapshots_create(name: str, subscription: str): - """Create a snapshot of a subscription.""" - resp = _request( - "post", "/v1/snapshots", json={"name": name, "subscription": subscription} - ) - check_response(resp) - console.print(f"[green]โœ“[/green] Snapshot '{name}' created from '{subscription}'") - - -@snapshots.command(name="delete") -@click.argument("name") -def snapshots_delete(name: str): - """Delete a snapshot.""" - resp = _request("delete", f"/v1/snapshots/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Snapshot '{name}' deleted") diff --git a/pkg/hanzo/src/hanzo/commands/queues.py b/pkg/hanzo/src/hanzo/commands/queues.py deleted file mode 100644 index 7476ea632..000000000 --- a/pkg/hanzo/src/hanzo/commands/queues.py +++ /dev/null @@ -1,431 +0,0 @@ -"""Hanzo Queues - Task and message queues CLI. - -BullMQ-compatible job and message queues. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -QUEUES_URL = os.getenv("HANZO_QUEUES_URL", "https://queues.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(QUEUES_URL, method, path, **kwargs) - - -@click.group(name="queues") -def queues_group(): - """Hanzo Queues - Task and message queues. - - \b - Queues: - hanzo queues list # List queues - hanzo queues create # Create queue - hanzo queues stats # Queue statistics - - \b - Jobs: - hanzo queues push # Push job to queue - hanzo queues pop # Pop job from queue - hanzo queues peek # Peek at next job - - \b - Management: - hanzo queues retry # Retry failed jobs - hanzo queues dlq # Dead letter queue management - hanzo queues drain # Drain a queue - """ - pass - - -# ============================================================================ -# Queue Management -# ============================================================================ - - -@queues_group.command(name="list") -@click.option("--project", "-p", help="Project ID") -def queues_list(project: str): - """List all queues.""" - params = {} - if project: - params["project"] = project - - resp = _request("get", "/v1/queues", params=params) - data = check_response(resp) - items = data.get("queues", data.get("items", [])) - - table = Table(title="Queues", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Pending", style="yellow") - table.add_column("Active", style="green") - table.add_column("Completed", style="dim") - table.add_column("Failed", style="red") - table.add_column("Delayed", style="dim") - - for q in items: - table.add_row( - q.get("name", ""), - str(q.get("pending", 0)), - str(q.get("active", 0)), - str(q.get("completed", 0)), - str(q.get("failed", 0)), - str(q.get("delayed", 0)), - ) - - console.print(table) - if not items: - console.print( - "[dim]No queues found. Create one with 'hanzo queues create'[/dim]" - ) - - -@queues_group.command(name="create") -@click.argument("name") -@click.option("--concurrency", "-c", default=10, help="Max concurrent workers") -@click.option("--rate-limit", "-r", help="Rate limit (e.g., '100/m')") -@click.option("--retry", default=3, help="Max retry attempts") -@click.option("--backoff", default="exponential", help="Backoff strategy") -def queues_create( - name: str, concurrency: int, rate_limit: str, retry: int, backoff: str -): - """Create a queue.""" - payload = { - "name": name, - "concurrency": concurrency, - "max_retries": retry, - "backoff": backoff, - } - if rate_limit: - payload["rate_limit"] = rate_limit - - resp = _request("post", "/v1/queues", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Queue '{name}' created") - console.print(f" ID: {data.get('id', '-')}") - console.print(f" Concurrency: {concurrency}") - console.print(f" Max retries: {retry}") - console.print(f" Backoff: {backoff}") - if rate_limit: - console.print(f" Rate limit: {rate_limit}") - - -@queues_group.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True, help="Force delete with pending jobs") -def queues_delete(name: str, force: bool): - """Delete a queue.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete queue '{name}'?[/red]"): - return - - resp = _request( - "delete", f"/v1/queues/{name}", params={"force": str(force).lower()} - ) - check_response(resp) - console.print(f"[green]โœ“[/green] Queue '{name}' deleted") - - -@queues_group.command(name="stats") -@click.argument("name") -def queues_stats(name: str): - """Show queue statistics.""" - resp = _request("get", f"/v1/queues/{name}/stats") - data = check_response(resp) - - console.print( - Panel( - f"[cyan]Queue:[/cyan] {data.get('name', name)}\n" - f"[cyan]Pending:[/cyan] {data.get('pending', 0):,}\n" - f"[cyan]Active:[/cyan] {data.get('active', 0)}\n" - f"[cyan]Completed:[/cyan] {data.get('completed', 0):,} (last 24h)\n" - f"[cyan]Failed:[/cyan] {data.get('failed', 0)}\n" - f"[cyan]Delayed:[/cyan] {data.get('delayed', 0)}\n" - f"[cyan]Avg process time:[/cyan] {data.get('avg_process_time_ms', 0)}ms\n" - f"[cyan]Throughput:[/cyan] {data.get('throughput_per_min', 0):,}/min", - title="Queue Statistics", - border_style="cyan", - ) - ) - - -# ============================================================================ -# Job Operations -# ============================================================================ - - -@queues_group.command(name="push") -@click.argument("queue") -@click.option("--data", "-d", required=True, help="Job data (JSON)") -@click.option("--name", "-n", help="Job name") -@click.option( - "--priority", "-p", type=int, default=0, help="Job priority (higher = more urgent)" -) -@click.option("--delay", help="Delay before processing (e.g., '5m', '1h')") -@click.option("--attempts", "-a", default=3, help="Max attempts") -def queues_push( - queue: str, data: str, name: str, priority: int, delay: str, attempts: int -): - """Push a job to a queue.""" - payload = {"data": json.loads(data), "priority": priority, "max_attempts": attempts} - if name: - payload["name"] = name - if delay: - payload["delay"] = delay - - resp = _request("post", f"/v1/queues/{queue}/jobs", json=payload) - result = check_response(resp) - - console.print(f"[green]โœ“[/green] Job pushed to '{queue}'") - console.print(f" Job ID: {result.get('id', result.get('job_id', '-'))}") - if name: - console.print(f" Name: {name}") - if delay: - console.print(f" Delay: {delay}") - console.print(f" Priority: {priority}") - - -@queues_group.command(name="pop") -@click.argument("queue") -@click.option("--count", "-n", default=1, help="Number of jobs to pop") -@click.option("--wait", "-w", is_flag=True, help="Wait for jobs if none available") -def queues_pop(queue: str, count: int, wait: bool): - """Pop jobs from a queue (for workers).""" - params = {"count": count} - if wait: - params["wait"] = "true" - - resp = _request("post", f"/v1/queues/{queue}/pop", json=params) - data = check_response(resp) - jobs = data.get("jobs", []) - - for job in jobs: - console.print( - Panel( - f"[cyan]ID:[/cyan] {job.get('id', '-')}\n" - f"[cyan]Name:[/cyan] {job.get('name', '-')}\n" - f"[cyan]Data:[/cyan] {json.dumps(job.get('data', {}), default=str)}", - border_style="cyan", - ) - ) - - if not jobs: - console.print("[dim]No jobs available[/dim]") - - -@queues_group.command(name="peek") -@click.argument("queue") -@click.option("--count", "-n", default=5, help="Number of jobs to peek") -def queues_peek(queue: str, count: int): - """Peek at jobs without removing them.""" - resp = _request( - "get", f"/v1/queues/{queue}/jobs", params={"limit": count, "status": "pending"} - ) - data = check_response(resp) - jobs = data.get("jobs", data.get("items", [])) - - table = Table(title=f"Next {count} Jobs in '{queue}'", box=box.ROUNDED) - table.add_column("Job ID", style="cyan") - table.add_column("Name", style="white") - table.add_column("Priority", style="yellow") - table.add_column("Attempts", style="dim") - table.add_column("Created", style="dim") - - for j in jobs: - table.add_row( - str(j.get("id", ""))[:16], - j.get("name", "-"), - str(j.get("priority", 0)), - f"{j.get('attempts', 0)}/{j.get('max_attempts', 3)}", - str(j.get("created_at", ""))[:19], - ) - - console.print(table) - if not jobs: - console.print("[dim]No pending jobs[/dim]") - - -@queues_group.command(name="get") -@click.argument("queue") -@click.argument("job_id") -def queues_get(queue: str, job_id: str): - """Get job details.""" - resp = _request("get", f"/v1/queues/{queue}/jobs/{job_id}") - data = check_response(resp) - - status = data.get("status", "unknown") - status_style = { - "pending": "yellow", - "active": "cyan", - "completed": "green", - "failed": "red", - "delayed": "dim", - }.get(status, "white") - - console.print( - Panel( - f"[cyan]Job ID:[/cyan] {data.get('id', job_id)}\n" - f"[cyan]Queue:[/cyan] {queue}\n" - f"[cyan]Name:[/cyan] {data.get('name', '-')}\n" - f"[cyan]Status:[/cyan] [{status_style}]{status}[/{status_style}]\n" - f"[cyan]Attempts:[/cyan] {data.get('attempts', 0)}/{data.get('max_attempts', 3)}\n" - f"[cyan]Created:[/cyan] {str(data.get('created_at', ''))[:19]}\n" - f"[cyan]Processed:[/cyan] {str(data.get('processed_at', '-'))[:19]}\n" - f"[cyan]Duration:[/cyan] {data.get('duration_ms', '-')}ms\n" - f"[cyan]Data:[/cyan] {json.dumps(data.get('data', {}), indent=2, default=str)}", - title="Job Details", - border_style="cyan", - ) - ) - - -# ============================================================================ -# Retry / DLQ / Drain -# ============================================================================ - - -@queues_group.command(name="retry") -@click.argument("queue") -@click.option("--job-id", "-j", help="Specific job ID to retry") -@click.option("--all-failed", is_flag=True, help="Retry all failed jobs") -@click.option("--count", "-n", type=int, help="Retry N failed jobs") -def queues_retry(queue: str, job_id: str, all_failed: bool, count: int): - """Retry failed jobs.""" - if not job_id and not all_failed and not count: - raise click.ClickException("Specify --job-id, --all-failed, or --count") - - payload = {} - if job_id: - payload["job_id"] = job_id - if all_failed: - payload["all_failed"] = True - if count: - payload["count"] = count - - resp = _request("post", f"/v1/queues/{queue}/retry", json=payload) - data = check_response(resp) - console.print(f"[green]โœ“[/green] {data.get('retried', 0)} job(s) queued for retry") - - -@queues_group.group() -def dlq(): - """Dead letter queue management.""" - pass - - -@dlq.command(name="list") -@click.argument("queue") -@click.option("--limit", "-n", default=20, help="Max jobs to show") -def dlq_list(queue: str, limit: int): - """List jobs in dead letter queue.""" - resp = _request("get", f"/v1/queues/{queue}/dlq", params={"limit": limit}) - data = check_response(resp) - jobs = data.get("jobs", data.get("items", [])) - - table = Table(title=f"Dead Letter Queue: {queue}", box=box.ROUNDED) - table.add_column("Job ID", style="cyan") - table.add_column("Name", style="white") - table.add_column("Error", style="red") - table.add_column("Attempts", style="yellow") - table.add_column("Failed At", style="dim") - - for j in jobs: - table.add_row( - str(j.get("id", ""))[:16], - j.get("name", "-"), - str(j.get("error", "-"))[:40], - str(j.get("attempts", 0)), - str(j.get("failed_at", ""))[:19], - ) - - console.print(table) - if not jobs: - console.print("[dim]No jobs in DLQ[/dim]") - - -@dlq.command(name="retry") -@click.argument("queue") -@click.option("--job-id", "-j", help="Specific job to retry") -@click.option("--all", "-a", "all_jobs", is_flag=True, help="Retry all DLQ jobs") -def dlq_retry(queue: str, job_id: str, all_jobs: bool): - """Retry jobs from dead letter queue.""" - if not job_id and not all_jobs: - raise click.ClickException("Specify --job-id or --all") - - payload = {} - if job_id: - payload["job_id"] = job_id - if all_jobs: - payload["all"] = True - - resp = _request("post", f"/v1/queues/{queue}/dlq/retry", json=payload) - data = check_response(resp) - console.print( - f"[green]โœ“[/green] {data.get('retried', 0)} DLQ job(s) moved back to queue" - ) - - -@dlq.command(name="purge") -@click.argument("queue") -def dlq_purge(queue: str): - """Purge all jobs from dead letter queue.""" - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Purge all DLQ jobs for '{queue}'?[/red]"): - return - - resp = _request("post", f"/v1/queues/{queue}/dlq/purge") - data = check_response(resp) - console.print( - f"[green]โœ“[/green] Purged {data.get('purged', 0)} DLQ job(s) for '{queue}'" - ) - - -@queues_group.command(name="drain") -@click.argument("queue") -@click.option("--delayed", is_flag=True, help="Also drain delayed jobs") -def queues_drain(queue: str, delayed: bool): - """Drain all jobs from a queue.""" - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Drain all jobs from '{queue}'?[/red]"): - return - - payload = {"delayed": delayed} - resp = _request("post", f"/v1/queues/{queue}/drain", json=payload) - data = check_response(resp) - console.print( - f"[green]โœ“[/green] Drained {data.get('drained', 0)} job(s) from '{queue}'" - ) - if delayed: - console.print("[dim]Delayed jobs also removed[/dim]") - - -@queues_group.command(name="pause") -@click.argument("queue") -def queues_pause(queue: str): - """Pause queue processing.""" - resp = _request("post", f"/v1/queues/{queue}/pause") - check_response(resp) - console.print(f"[green]โœ“[/green] Queue '{queue}' paused") - - -@queues_group.command(name="resume") -@click.argument("queue") -def queues_resume(queue: str): - """Resume queue processing.""" - resp = _request("post", f"/v1/queues/{queue}/resume") - check_response(resp) - console.print(f"[green]โœ“[/green] Queue '{queue}' resumed") diff --git a/pkg/hanzo/src/hanzo/commands/repl.py b/pkg/hanzo/src/hanzo/commands/repl.py deleted file mode 100644 index 5d2cc636c..000000000 --- a/pkg/hanzo/src/hanzo/commands/repl.py +++ /dev/null @@ -1,194 +0,0 @@ -"""REPL command for interactive AI sessions.""" - -import os -import sys - -import click - -from ..utils.output import console - - -@click.group(name="repl") -def repl_group(): - """Interactive REPL for AI and MCP tools.""" - pass - - -@repl_group.command() -@click.option("--model", "-m", help="Default model to use") -@click.option("--local/--cloud", default=False, help="Use local cluster") -@click.option("--ipython", is_flag=True, help="Use IPython interface") -@click.option("--tui", is_flag=True, help="Use TUI interface") -@click.option("--voice", is_flag=True, help="Enable voice mode") -@click.pass_context -def start(ctx, model: str, local: bool, ipython: bool, tui: bool, voice: bool): - """Start interactive REPL (like Claude Code in terminal).""" - try: - # Set up environment - if model: - os.environ["HANZO_DEFAULT_MODEL"] = model - if local: - os.environ["HANZO_USE_LOCAL"] = "true" - if voice: - os.environ["HANZO_ENABLE_VOICE"] = "true" - - if ipython: - from hanzo_dev.ipython_repl import main - elif tui: - from hanzo_dev.textual_repl import main - else: - from hanzo_dev.cli import main - - console.print("[cyan]Starting Hanzo REPL...[/cyan]") - console.print("All MCP tools available. Type 'help' for commands.\n") - - sys.exit(main()) - - except ImportError: - console.print("[red]Error:[/red] hanzo-dev not installed") - console.print("Install with: pip install hanzo-dev") - console.print("\nFeatures:") - console.print(" โ€ข Direct access to 70+ MCP tools") - console.print(" โ€ข Chat with AI that can use tools") - console.print(" โ€ข IPython magic commands") - console.print(" โ€ข Beautiful TUI interface") - console.print(" โ€ข Voice mode (optional)") - - -@repl_group.command() -@click.pass_context -def info(ctx): - """Show REPL information and status.""" - try: - import hanzo_mcp - from hanzo_dev import __version__ - - console.print("[cyan]Hanzo Dev[/cyan]") - console.print(f" Version: {__version__}") - console.print(f" MCP Tools: {len(hanzo_mcp.get_all_tools())}") - - # Check available interfaces - interfaces = [] - try: - import IPython - - interfaces.append("IPython") - except ImportError: - pass - - try: - import textual - - interfaces.append("TUI") - except ImportError: - pass - - try: - import speech_recognition - - interfaces.append("Voice") - except ImportError: - pass - - console.print(f" Interfaces: {', '.join(interfaces) or 'Basic'}") - - # Check LLM providers - providers = [] - if os.environ.get("OPENAI_API_KEY"): - providers.append("OpenAI") - if os.environ.get("ANTHROPIC_API_KEY"): - providers.append("Anthropic") - if os.environ.get("HANZO_API_KEY"): - providers.append("Hanzo AI") - - console.print(f" Providers: {', '.join(providers) or 'None configured'}") - - if not providers: - console.print("\n[yellow]No LLM providers configured[/yellow]") - console.print( - "Set one of: OPENAI_API_KEY, ANTHROPIC_API_KEY, HANZO_API_KEY" - ) - - except ImportError: - console.print("[red]Error:[/red] hanzo-dev not installed") - - -@repl_group.command() -@click.option( - "--interface", type=click.Choice(["all", "ipython", "tui", "voice"]), default="all" -) -@click.pass_context -def install_extras(ctx, interface: str): - """Install optional REPL components.""" - import subprocess - - packages = { - "ipython": ["ipython>=8.0.0", "jupyter>=1.0.0"], - "tui": ["textual>=0.41.0", "textual-dev>=1.2.0"], - "voice": ["speechrecognition>=3.10.0", "pyttsx3>=2.90", "pyaudio>=0.2.11"], - } - - if interface == "all": - to_install = [] - for pkgs in packages.values(): - to_install.extend(pkgs) - else: - to_install = packages.get(interface, []) - - if to_install: - console.print(f"[cyan]Installing {interface} components...[/cyan]") - cmd = [sys.executable, "-m", "pip", "install"] + to_install - - try: - subprocess.run(cmd, check=True) - console.print(f"[green]โœ“[/green] Installed {interface} components") - except subprocess.CalledProcessError: - console.print(f"[red]Failed to install components[/red]") - console.print("Try manually: pip install hanzo-dev[voice]") - - -@repl_group.command() -@click.argument("command", nargs=-1, required=True) -@click.option("--model", "-m", help="Model to use") -@click.pass_context -def exec(ctx, command: tuple, model: str): - """Execute a command in REPL and exit.""" - try: - import asyncio - - from hanzo_dev import create_repl - - repl = create_repl(model=model) - command_str = " ".join(command) - - async def run(): - result = await repl.execute(command_str) - console.print(result) - - asyncio.run(run()) - - except ImportError as e: - console.print(f"[red]Import Error:[/red] {e}") - console.print("[yellow]Note:[/yellow] hanzo-dev may not be installed correctly") - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - - -@repl_group.command() -@click.pass_context -def demo(ctx): - """Run REPL demo showcasing features.""" - try: - from hanzo_dev.demos import run_demo - - console.print("[cyan]Running Hanzo Dev demo...[/cyan]\n") - run_demo() - - except ImportError: - console.print("[red]Error:[/red] hanzo-dev not installed") - console.print("\nThe demo would show:") - console.print(" โ€ข File operations with MCP tools") - console.print(" โ€ข Code search and analysis") - console.print(" โ€ข AI chat with tool usage") - console.print(" โ€ข IPython magic commands") - console.print(" โ€ข Voice interaction (if available)") diff --git a/pkg/hanzo/src/hanzo/commands/router.py b/pkg/hanzo/src/hanzo/commands/router.py deleted file mode 100644 index 3726aceb4..000000000 --- a/pkg/hanzo/src/hanzo/commands/router.py +++ /dev/null @@ -1,168 +0,0 @@ -"""Router command for starting Hanzo router proxy.""" - -import os -import sys -import subprocess -from typing import Optional -from pathlib import Path - -import click - -from ..utils.output import console - - -@click.group(name="router") -def router_group(): - """Manage Hanzo router (LLM proxy).""" - pass - - -@router_group.command(name="start") -@click.option("--port", "-p", default=4000, help="Port to run router on") -@click.option("--config", "-c", help="Config file path") -@click.option("--detach", "-d", is_flag=True, help="Run in background") -@click.pass_context -def start_router(ctx, port: int, config: Optional[str], detach: bool): - """Start the Hanzo router proxy server.""" - # Find router directory - router_paths = [ - Path.home() / "work" / "hanzo" / "router", - Path.home() / "hanzo" / "router", - Path.cwd().parent / "router", - ] - - router_dir = None - for path in router_paths: - if path.exists() and (path / "llm" / "proxy" / "proxy_server.py").exists(): - router_dir = path - break - - if not router_dir: - console.print("[red]Error:[/red] Hanzo router not found") - console.print("\nPlease clone the router:") - console.print( - " git clone https://github.com/hanzoai/router.git ~/work/hanzo/router" - ) - return - - console.print(f"[green]โœ“[/green] Found router at {router_dir}") - - # Prepare environment - env = os.environ.copy() - env["PYTHONPATH"] = str(router_dir) + ":" + env.get("PYTHONPATH", "") - - # Build command - cmd = [ - sys.executable, - "-m", - "llm.proxy.proxy_server", - "--port", - str(port), - ] - - if config: - # Use provided config - config_path = Path(config) - if not config_path.exists(): - console.print(f"[red]Error:[/red] Config file not found: {config}") - return - cmd.extend(["--config", str(config_path)]) - else: - # Check for default config - default_config = router_dir / "config.yaml" - if default_config.exists(): - cmd.extend(["--config", str(default_config)]) - console.print(f"[dim]Using config: {default_config}[/dim]") - - console.print(f"\n[bold cyan]Starting Hanzo Router on port {port}[/bold cyan]") - console.print(f"API endpoint: http://localhost:{port}/v1") - console.print("\nPress Ctrl+C to stop\n") - - try: - # Change to router directory and run - os.chdir(router_dir) - - if detach: - # Run in background - process = subprocess.Popen( - cmd, - env=env, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - start_new_session=True, - ) - console.print( - f"[green]โœ“[/green] Router started in background (PID: {process.pid})" - ) - console.print(f"Check status: curl http://localhost:{port}/health") - else: - # Run in foreground - subprocess.run(cmd, env=env) - except KeyboardInterrupt: - console.print("\n[yellow]Router stopped[/yellow]") - except Exception as e: - console.print(f"[red]Error starting router: {e}[/red]") - - -@router_group.command(name="stop") -@click.option("--port", "-p", default=4000, help="Port router is running on") -def stop_router(port: int): - """Stop the router.""" - import signal - - import psutil - - found = False - for proc in psutil.process_iter(["pid", "cmdline"]): - try: - cmdline = proc.info["cmdline"] - if ( - cmdline - and "proxy_server" in " ".join(cmdline) - and str(port) in " ".join(cmdline) - ): - console.print( - f"[yellow]Stopping router (PID: {proc.info['pid']})[/yellow]" - ) - proc.send_signal(signal.SIGTERM) - proc.wait(timeout=5) - found = True - console.print("[green]โœ“[/green] Router stopped") - except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.TimeoutExpired): - continue - - if not found: - console.print(f"[yellow]No router found on port {port}[/yellow]") - - -@router_group.command(name="status") -@click.option("--port", "-p", default=4000, help="Port to check") -def router_status(port: int): - """Check router status.""" - import httpx - - try: - response = httpx.get(f"http://localhost:{port}/health", timeout=2.0) - if response.status_code == 200: - console.print(f"[green]โœ“[/green] Router is running on port {port}") - - # Try to get models - try: - models_response = httpx.get( - f"http://localhost:{port}/models", timeout=2.0 - ) - if models_response.status_code == 200: - data = models_response.json() - if "data" in data: - console.print(f"Available models: {len(data['data'])}") - except Exception: - pass - else: - console.print( - f"[yellow]Router responding but unhealthy (status: {response.status_code})[/yellow]" - ) - except httpx.ConnectError: - console.print(f"[red]Router not running on port {port}[/red]") - console.print("\nStart with: hanzo router start") - except Exception as e: - console.print(f"[red]Error checking router: {e}[/red]") diff --git a/pkg/hanzo/src/hanzo/commands/run.py b/pkg/hanzo/src/hanzo/commands/run.py deleted file mode 100644 index 1bda2adbc..000000000 --- a/pkg/hanzo/src/hanzo/commands/run.py +++ /dev/null @@ -1,630 +0,0 @@ -"""Hanzo Run - Service lifecycle management CLI. - -Deploy and manage containers on the PaaS platform. -""" - -import os - -import click -from rich import box -from rich.panel import Panel -from rich.table import Table - -from ..utils.output import console - - -@click.group(name="run") -def run_group(): - """Hanzo Run - Deploy and manage services. - - \b - Services: - hanzo run service # Deploy/update a service - hanzo run job # Run one-off job - hanzo run function # Invoke a function - - \b - Lifecycle: - hanzo run status # Check deployment status - hanzo run logs # View service logs - hanzo run scale # Scale service - - \b - Traffic: - hanzo run promote # Promote to next stage - hanzo run rollback # Rollback deployment - hanzo run traffic # Adjust traffic split - - Aliases: 'hanzo deploy' redirects here. - """ - pass - - -# ============================================================================ -# Helpers -# ============================================================================ - - -def _get_client(): - from ..utils.api_client import PaaSClient - - try: - return PaaSClient(timeout=60) - except SystemExit: - return None - - -def _get_ctx(): - from ..utils.api_client import require_context - - try: - return require_context() - except SystemExit: - return None - - -def _container_base(): - from ..utils.api_client import container_url - - client = _get_client() - if not client: - return None - ctx = _get_ctx() - if not ctx: - return None - url = container_url(ctx["org_id"], ctx["project_id"], ctx["env_id"]) - return client, ctx, url - - -def _find_container(client, base_url: str, name: str): - from ..utils.api_client import find_container - - return find_container(client, base_url, name) - - -# ============================================================================ -# Service Deployment -# ============================================================================ - - -@run_group.command(name="service") -@click.argument("name", required=False) -@click.option("--image", "-i", help="Container image") -@click.option("--source", "-s", help="Git repo URL (source build)") -@click.option( - "--env", "-e", "env_name", help="Override environment (uses context by default)" -) -@click.option("--replicas", "-r", default=1, help="Number of replicas") -@click.option("--port", "-p", default=8080, help="Service port") -@click.option("--cpu", default="0.5", help="CPU cores") -@click.option("--memory", default="512Mi", help="Memory") -@click.option("--wait", "-w", is_flag=True, help="Wait for deployment") -@click.option("--var", "-V", multiple=True, help="Env vars (KEY=value)") -def run_service(name, image, source, env_name, replicas, port, cpu, memory, wait, var): - """Deploy or update a service. - - \b - Examples: - hanzo run service my-api --image my-api:v1.2 - hanzo run service --source https://github.com/org/repo - hanzo run service my-api --replicas 3 --cpu 1 --memory 1Gi - hanzo run service my-api -V DB_HOST=db.local -V SECRET=xxx - """ - from ..utils.api_client import container_url - - if not name: - name = os.path.basename(os.getcwd()) - - info = _container_base() - if not info: - return - client, ctx, base_url = info - - # Check if container already exists - existing, existing_id = _find_container(client, base_url, name) - - # Build env vars list - variables = [] - for v in var: - if "=" in v: - k, val = v.split("=", 1) - variables.append({"name": k, "value": val}) - - if existing: - # Update existing container - console.print(f"[cyan]Updating service '{name}'...[/cyan]") - payload = {} - if image: - payload["repoOrRegistry"] = "registry" - payload["registry"] = {"image": image} - if replicas: - payload["deploymentConfig"] = {"desiredReplicas": replicas} - if port: - payload["networking"] = {"containerPort": port} - if variables: - payload["variables"] = variables - - result = client.put(f"{base_url}/{existing_id}", payload) - if result is None: - return - - console.print(f"[green]โœ“[/green] Service '{name}' updated") - else: - # Create new container - console.print(f"[cyan]Deploying service '{name}'...[/cyan]") - payload = { - "name": name, - "type": "deployment", - "networking": {"containerPort": port}, - "deploymentConfig": {"desiredReplicas": replicas}, - } - - if image: - payload["repoOrRegistry"] = "registry" - payload["registry"] = {"image": image} - elif source: - payload["repoOrRegistry"] = "repo" - payload["repo"] = {"url": source, "branch": "main"} - - if variables: - payload["variables"] = variables - - result = client.post(base_url, payload) - if result is None: - return - - console.print(f"[green]โœ“[/green] Service '{name}' deployed") - cid = result.get("_id") or result.get("iid") or result.get("id", "") - if cid: - console.print(f" Container ID: {cid}") - - if image: - console.print(f" Image: {image}") - elif source: - console.print(f" Source: {source}") - console.print(f" Replicas: {replicas}") - - # Trigger build if source - if source and not existing: - cid = ( - (result or {}).get("_id") - or (result or {}).get("iid") - or (result or {}).get("id", "") - ) - if cid: - console.print("[cyan]Triggering build...[/cyan]") - client.post(f"{base_url}/{cid}/trigger") - - if wait: - console.print("[dim]Waiting for deployment to be ready...[/dim]") - import time - - for _ in range(60): - cid = (result or {}).get("_id") or existing_id - if cid: - data = client.get(f"{base_url}/{cid}") - if ( - data - and data.get("status", {}).get("availableReplicas", 0) >= replicas - ): - console.print("[green]โœ“[/green] Deployment ready") - return - time.sleep(2) - console.print("[yellow]Timed out waiting for deployment[/yellow]") - - -@run_group.command(name="job") -@click.argument("name") -@click.option("--image", "-i", help="Container image") -@click.option("--command", "-c", "cmd", help="Command to run") -@click.option("--wait", "-w", is_flag=True, help="Wait for completion") -@click.option("--timeout", "-t", default="1h", help="Job timeout") -def run_job(name, image, cmd, wait, timeout): - """Run a one-off job.""" - info = _container_base() - if not info: - return - client, ctx, base_url = info - - payload = { - "name": name, - "type": "cronjob", - "repoOrRegistry": "registry", - } - if image: - payload["registry"] = {"image": image} - if cmd: - payload["deploymentConfig"] = {"command": cmd} - - console.print(f"[cyan]Starting job '{name}'...[/cyan]") - result = client.post(base_url, payload) - if result is None: - return - - cid = result.get("_id") or result.get("iid") or result.get("id", "") - console.print(f"[green]โœ“[/green] Job started") - console.print(f" Container ID: {cid}") - console.print(f" Logs: hanzo run logs {name}") - - -@run_group.command(name="function") -@click.argument("name") -@click.option("--payload", "-p", help="JSON payload") -@click.option("--async", "async_", is_flag=True, help="Invoke asynchronously") -def run_function(name, payload, async_): - """Invoke a function.""" - info = _container_base() - if not info: - return - client, ctx, base_url = info - - _, cid = _find_container(client, base_url, name) - if not cid: - console.print(f"[yellow]Function '{name}' not found.[/yellow]") - return - - console.print(f"[cyan]Invoking function '{name}'...[/cyan]") - result = client.post(f"{base_url}/{cid}/trigger") - if result is None: - return - - if async_: - console.print("[green]โœ“[/green] Function invoked asynchronously") - else: - console.print("[green]โœ“[/green] Function invoked") - - -# ============================================================================ -# Status & Logs -# ============================================================================ - - -@run_group.command(name="status") -@click.argument("name", required=False) -def run_status(name): - """Check deployment status.""" - info = _container_base() - if not info: - return - client, ctx, base_url = info - - if name: - # Show single container - container, cid = _find_container(client, base_url, name) - if not container: - console.print(f"[yellow]Service '{name}' not found.[/yellow]") - return - - status_info = container.get("status", {}) - replicas = container.get("deploymentConfig", {}).get("desiredReplicas", "?") - avail = status_info.get("availableReplicas", "?") - state = status_info.get("conditions", [{}]) - state_str = ( - "[green]โ— Running[/green]" if avail else "[yellow]โ—‹ Pending[/yellow]" - ) - - img = "" - if container.get("repoOrRegistry") == "registry": - img = (container.get("registry") or {}).get("image", "") - elif container.get("repoOrRegistry") == "repo": - img = (container.get("repo") or {}).get("url", "") - - port = (container.get("networking") or {}).get("containerPort", "") - - console.print( - Panel( - f"[cyan]Service:[/cyan] {container.get('name', name)}\n" - f"[cyan]Status:[/cyan] {state_str}\n" - f"[cyan]Replicas:[/cyan] {avail}/{replicas}\n" - f"[cyan]Image:[/cyan] {img}\n" - f"[cyan]Port:[/cyan] {port}\n" - f"[cyan]ID:[/cyan] {cid}", - title="Service Status", - border_style="cyan", - ) - ) - else: - # List all containers - data = client.get(base_url) - if data is None: - return - - from ..utils.api_client import extract_list - - containers = extract_list(data, "containers") - - if not containers: - console.print("[dim]No services deployed in current context.[/dim]") - console.print( - "[dim]Deploy with: hanzo run service NAME --image IMAGE[/dim]" - ) - return - - table = Table(title="Services", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Type", style="white") - table.add_column("Status", style="green") - table.add_column("Replicas", style="white") - table.add_column("Image/Repo", style="dim") - - for c in containers: - cname = c.get("name", "") - ctype = c.get("type", "deployment") - status_info = c.get("status", {}) - desired = c.get("deploymentConfig", {}).get("desiredReplicas", "?") - avail = status_info.get("availableReplicas", 0) - state = "Running" if avail else "Pending" - - img = "" - if c.get("repoOrRegistry") == "registry": - img = (c.get("registry") or {}).get("image", "") - elif c.get("repoOrRegistry") == "repo": - img = (c.get("repo") or {}).get("url", "") - - table.add_row(cname, ctype, state, f"{avail}/{desired}", img) - - console.print(table) - - -@run_group.command(name="logs") -@click.argument("name") -@click.option("--follow", "-f", is_flag=True, help="Follow logs") -@click.option("--tail", "-n", default=100, help="Number of lines") -@click.option("--since", "-s", help="Since time (e.g., 1h, 30m)") -def run_logs(name, follow, tail, since): - """View service logs.""" - info = _container_base() - if not info: - return - client, ctx, base_url = info - - _, cid = _find_container(client, base_url, name) - if not cid: - console.print(f"[yellow]Service '{name}' not found.[/yellow]") - return - - params = {"tail": tail} - if since: - params["since"] = since - - console.print(f"[cyan]Logs for {name}:[/cyan]") - - data = client.get(f"{base_url}/{cid}/logs") - if data is None: - console.print("[dim]No logs available.[/dim]") - return - - logs = data.get("logs", data.get("data", "")) - if isinstance(logs, list): - for line in logs[-tail:]: - console.print(line) - elif isinstance(logs, str): - for line in logs.split("\n")[-tail:]: - console.print(line) - else: - console.print("[dim]No logs available.[/dim]") - - if follow: - console.print( - "[dim]Follow mode is not yet supported via API. Use 'hanzo k8s logs' for streaming.[/dim]" - ) - - -# ============================================================================ -# Scaling & Traffic -# ============================================================================ - - -@run_group.command(name="scale") -@click.argument("name") -@click.option("--replicas", "-r", type=int, help="Number of replicas") -@click.option("--cpu", help="CPU cores") -@click.option("--memory", help="Memory") -def run_scale(name, replicas, cpu, memory): - """Scale a service.""" - info = _container_base() - if not info: - return - client, ctx, base_url = info - - _, cid = _find_container(client, base_url, name) - if not cid: - console.print(f"[yellow]Service '{name}' not found.[/yellow]") - return - - payload = {} - if replicas is not None: - payload["deploymentConfig"] = {"desiredReplicas": replicas} - if cpu or memory: - resources = {} - if cpu: - resources["cpu"] = cpu - if memory: - resources["memory"] = memory - payload["resources"] = resources - - if not payload: - console.print("[yellow]No scaling changes specified.[/yellow]") - return - - result = client.put(f"{base_url}/{cid}", payload) - if result is None: - return - - changes = [] - if replicas is not None: - changes.append(f"replicas={replicas}") - if cpu: - changes.append(f"cpu={cpu}") - if memory: - changes.append(f"memory={memory}") - - console.print(f"[green]โœ“[/green] Scaled '{name}': {', '.join(changes)}") - - -@run_group.command(name="promote") -@click.argument("name") -@click.option("--from", "from_env", required=True, help="Source environment") -@click.option("--to", "to_env", required=True, help="Target environment") -def run_promote(name, from_env, to_env): - """Promote service to next environment. - - Copies container config from source environment to target. - """ - from ..utils.api_client import PaaSClient, container_url, require_context - - try: - client = PaaSClient(timeout=60) - ctx = require_context(("org_id", "project_id")) - except SystemExit: - return - - # Find container in source env - src_url = container_url(ctx["org_id"], ctx["project_id"], from_env) - container, _ = _find_container(client, src_url, name) - if not container: - console.print(f"[yellow]Service '{name}' not found in '{from_env}'.[/yellow]") - return - - # Create/update in target env - dst_url = container_url(ctx["org_id"], ctx["project_id"], to_env) - existing, existing_id = _find_container(client, dst_url, name) - - payload = { - "name": container.get("name", name), - "type": container.get("type", "deployment"), - "repoOrRegistry": container.get("repoOrRegistry", "registry"), - } - if container.get("registry"): - payload["registry"] = container["registry"] - if container.get("repo"): - payload["repo"] = container["repo"] - if container.get("deploymentConfig"): - payload["deploymentConfig"] = container["deploymentConfig"] - if container.get("networking"): - payload["networking"] = container["networking"] - if container.get("variables"): - payload["variables"] = container["variables"] - - if existing: - result = client.put(f"{dst_url}/{existing_id}", payload) - else: - result = client.post(dst_url, payload) - - if result is None: - return - - console.print(f"[green]โœ“[/green] Promoted '{name}' from {from_env} to {to_env}") - - -@run_group.command(name="rollback") -@click.argument("name") -@click.option("--version", "-v", help="Version to rollback to (pipeline run)") -def run_rollback(name, version): - """Rollback to previous deployment.""" - info = _container_base() - if not info: - return - client, ctx, base_url = info - - _, cid = _find_container(client, base_url, name) - if not cid: - console.print(f"[yellow]Service '{name}' not found.[/yellow]") - return - - # Get pipelines to find the previous run - pipelines = client.get(f"{base_url}/{cid}/pipelines") - if pipelines is None: - console.print("[yellow]No pipeline history found.[/yellow]") - return - - runs = ( - pipelines - if isinstance(pipelines, list) - else pipelines.get("runs", pipelines.get("data", [])) - ) - - if version: - # Find specific run - target = None - for r in runs: - if str(r.get("runNumber", "")) == version or r.get("_id", "") == version: - target = r - break - if not target: - console.print(f"[yellow]Pipeline run '{version}' not found.[/yellow]") - return - elif len(runs) >= 2: - target = runs[1] # Previous run - else: - console.print("[yellow]No previous deployment to rollback to.[/yellow]") - return - - # Trigger re-run of that pipeline - console.print(f"[cyan]Rolling back '{name}'...[/cyan]") - result = client.post(f"{base_url}/{cid}/trigger") - if result is None: - return - - console.print(f"[green]โœ“[/green] Rolled back '{name}'") - - -@run_group.command(name="traffic") -@click.argument("name") -@click.option("--version", "-v", multiple=True, help="Version:weight pairs") -def run_traffic(name, version): - """Adjust traffic split between versions. - - \b - Examples: - hanzo run traffic my-api -v v1:90 -v v2:10 - hanzo run traffic my-api -v v2:100 # Full cutover - """ - if not version: - console.print( - "[yellow]Traffic splitting requires PaaS ingress configuration.[/yellow]" - ) - console.print("[dim]Specify version weights: -v v1:90 -v v2:10[/dim]") - return - - # Traffic splitting is typically handled at the ingress/service mesh level - console.print(f"[cyan]Updating traffic split for '{name}':[/cyan]") - for v in version: - parts = v.split(":") - if len(parts) == 2: - console.print(f" {parts[0]}: {parts[1]}%") - - console.print( - "[yellow]Traffic splitting requires ingress controller support.[/yellow]" - ) - console.print("[dim]Configure via 'hanzo k8s ingress' or your service mesh.[/dim]") - - -@run_group.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True, help="Skip confirmation") -def run_delete(name, force): - """Delete a deployed service.""" - info = _container_base() - if not info: - return - client, ctx, base_url = info - - _, cid = _find_container(client, base_url, name) - if not cid: - console.print(f"[yellow]Service '{name}' not found.[/yellow]") - return - - if not force: - from rich.prompt import Confirm - - if not Confirm.ask( - f"[red]Delete service '{name}'? This cannot be undone.[/red]" - ): - return - - result = client.delete(f"{base_url}/{cid}") - if result is None: - return - - console.print(f"[green]โœ“[/green] Service '{name}' deleted") diff --git a/pkg/hanzo/src/hanzo/commands/search.py b/pkg/hanzo/src/hanzo/commands/search.py deleted file mode 100644 index f7d31c4ad..000000000 --- a/pkg/hanzo/src/hanzo/commands/search.py +++ /dev/null @@ -1,601 +0,0 @@ -"""Hanzo Search - Search engine CLI. - -Hybrid search with lexical (BM25) and vector (semantic) capabilities. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -SEARCH_URL = os.getenv("HANZO_SEARCH_URL", "https://search.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(SEARCH_URL, method, path, **kwargs) - - -@click.group(name="search") -def search_group(): - """Hanzo Search - Hybrid search engine. - - \b - Engines: - hanzo search create # Create search engine - hanzo search list # List engines - hanzo search delete # Delete engine - - \b - Indexes: - hanzo search index create # Create index - hanzo search index list # List indexes - hanzo search index delete # Delete index - hanzo search index mapping # Manage mappings - - \b - Data: - hanzo search ingest # Ingest documents - hanzo search query # Search documents - hanzo search reindex # Reindex data - - \b - Pipelines: - hanzo search pipeline create # Create ingest pipeline - hanzo search pipeline list # List pipelines - """ - pass - - -# ============================================================================ -# Engine Management -# ============================================================================ - - -@search_group.command(name="create") -@click.argument("name") -@click.option( - "--mode", "-m", type=click.Choice(["lexical", "vector", "hybrid"]), default="hybrid" -) -@click.option("--region", "-r", help="Region") -@click.option( - "--tier", - "-t", - type=click.Choice(["free", "standard", "dedicated"]), - default="standard", -) -def search_create(name: str, mode: str, region: str, tier: str): - """Create a search engine.""" - payload = {"name": name, "mode": mode, "tier": tier} - if region: - payload["region"] = region - - resp = _request("post", "/v1/engines", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Search engine '{name}' created") - console.print(f" ID: {data.get('id', '-')}") - console.print(f" Mode: {mode}") - console.print(f" Tier: {tier}") - if region: - console.print(f" Region: {region}") - - -@search_group.command(name="list") -def search_list(): - """List search engines.""" - resp = _request("get", "/v1/engines") - data = check_response(resp) - engines = data.get("engines", data.get("items", [])) - - table = Table(title="Search Engines", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Mode", style="white") - table.add_column("Indexes", style="green") - table.add_column("Documents", style="yellow") - table.add_column("Status", style="dim") - - for e in engines: - status = e.get("status", "unknown") - style = "green" if status == "running" else "yellow" - table.add_row( - e.get("name", ""), - e.get("mode", "-"), - str(e.get("index_count", 0)), - str(e.get("document_count", 0)), - f"[{style}]โ— {status}[/{style}]", - ) - - console.print(table) - if not engines: - console.print( - "[dim]No search engines found. Create one with 'hanzo search create'[/dim]" - ) - - -@search_group.command(name="describe") -@click.argument("name") -def search_describe(name: str): - """Show search engine details.""" - resp = _request("get", f"/v1/engines/{name}") - data = check_response(resp) - - status = data.get("status", "unknown") - style = "green" if status == "running" else "yellow" - - console.print( - Panel( - f"[cyan]Name:[/cyan] {data.get('name', name)}\n" - f"[cyan]Mode:[/cyan] {data.get('mode', '-')}\n" - f"[cyan]Status:[/cyan] [{style}]โ— {status}[/{style}]\n" - f"[cyan]Indexes:[/cyan] {data.get('index_count', 0)}\n" - f"[cyan]Documents:[/cyan] {data.get('document_count', 0):,}\n" - f"[cyan]Size:[/cyan] {data.get('size', '0 B')}\n" - f"[cyan]Queries/day:[/cyan] {data.get('queries_per_day', 0):,}\n" - f"[cyan]Endpoint:[/cyan] {data.get('endpoint', f'{SEARCH_URL}/{name}')}", - title="Search Engine Details", - border_style="cyan", - ) - ) - - -@search_group.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True) -def search_delete(name: str, force: bool): - """Delete a search engine.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete search engine '{name}'?[/red]"): - return - - resp = _request("delete", f"/v1/engines/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Search engine '{name}' deleted") - - -# ============================================================================ -# Index Management -# ============================================================================ - - -@search_group.group() -def index(): - """Manage search indexes.""" - pass - - -@index.command(name="create") -@click.argument("name") -@click.option("--engine", "-e", default="default", help="Search engine") -@click.option( - "--mode", - "-m", - type=click.Choice(["lexical", "vector", "hybrid"]), - help="Override engine mode", -) -@click.option("--mapping", help="Mapping JSON or file") -@click.option("--shards", "-s", default=1, help="Number of shards") -@click.option("--replicas", "-r", default=1, help="Number of replicas") -def index_create( - name: str, engine: str, mode: str, mapping: str, shards: int, replicas: int -): - """Create a search index.""" - payload = {"name": name, "shards": shards, "replicas": replicas} - if mode: - payload["mode"] = mode - if mapping: - from pathlib import Path - - if Path(mapping).exists(): - payload["mapping"] = json.loads(Path(mapping).read_text()) - else: - payload["mapping"] = json.loads(mapping) - - resp = _request("post", f"/v1/engines/{engine}/indexes", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Index '{name}' created in '{engine}'") - console.print(f" Shards: {shards}") - console.print(f" Replicas: {replicas}") - - -@index.command(name="list") -@click.option("--engine", "-e", default="default") -def index_list(engine: str): - """List indexes in an engine.""" - resp = _request("get", f"/v1/engines/{engine}/indexes") - data = check_response(resp) - items = data.get("indexes", data.get("items", [])) - - table = Table(title=f"Indexes in '{engine}'", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Mode", style="white") - table.add_column("Documents", style="green") - table.add_column("Size", style="yellow") - table.add_column("Health", style="dim") - - for idx in items: - health = idx.get("health", "unknown") - style = ( - "green" if health == "green" else "yellow" if health == "yellow" else "red" - ) - table.add_row( - idx.get("name", ""), - idx.get("mode", "-"), - str(idx.get("document_count", 0)), - idx.get("size", "0 B"), - f"[{style}]โ— {health}[/{style}]", - ) - - console.print(table) - if not items: - console.print("[dim]No indexes found[/dim]") - - -@index.command(name="describe") -@click.argument("name") -@click.option("--engine", "-e", default="default") -def index_describe(name: str, engine: str): - """Show index details.""" - resp = _request("get", f"/v1/engines/{engine}/indexes/{name}") - data = check_response(resp) - - console.print( - Panel( - f"[cyan]Index:[/cyan] {data.get('name', name)}\n" - f"[cyan]Engine:[/cyan] {engine}\n" - f"[cyan]Mode:[/cyan] {data.get('mode', '-')}\n" - f"[cyan]Documents:[/cyan] {data.get('document_count', 0):,}\n" - f"[cyan]Size:[/cyan] {data.get('size', '0 B')}\n" - f"[cyan]Shards:[/cyan] {data.get('shards', 1)}\n" - f"[cyan]Replicas:[/cyan] {data.get('replicas', 1)}", - title="Index Details", - border_style="cyan", - ) - ) - - -@index.command(name="delete") -@click.argument("name") -@click.option("--engine", "-e", default="default") -@click.option("--force", "-f", is_flag=True) -def index_delete(name: str, engine: str, force: bool): - """Delete an index.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete index '{name}'?[/red]"): - return - - resp = _request("delete", f"/v1/engines/{engine}/indexes/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Index '{name}' deleted") - - -@index.command(name="mapping") -@click.argument("name") -@click.option("--engine", "-e", default="default") -@click.option("--get", "get_mapping", is_flag=True, help="Get current mapping") -@click.option("--set", "set_mapping", help="Set mapping (JSON or file)") -def index_mapping(name: str, engine: str, get_mapping: bool, set_mapping: str): - """Get or set index mapping.""" - if set_mapping: - from pathlib import Path - - if Path(set_mapping).exists(): - mapping_data = json.loads(Path(set_mapping).read_text()) - else: - mapping_data = json.loads(set_mapping) - - resp = _request( - "put", f"/v1/engines/{engine}/indexes/{name}/mapping", json=mapping_data - ) - check_response(resp) - console.print(f"[green]โœ“[/green] Mapping updated for '{name}'") - else: - resp = _request("get", f"/v1/engines/{engine}/indexes/{name}/mapping") - data = check_response(resp) - console.print(f"[cyan]Mapping for '{name}':[/cyan]") - console.print(json.dumps(data.get("mapping", data), indent=2)) - - -# ============================================================================ -# Data Operations -# ============================================================================ - - -@search_group.command(name="ingest") -@click.option("--index", "-i", required=True, help="Target index") -@click.option("--from", "source", required=True, help="Source: file, s3://, storage://") -@click.option( - "--format", - "fmt", - type=click.Choice(["jsonl", "json", "csv", "parquet"]), - default="jsonl", -) -@click.option("--pipeline", "-p", help="Ingest pipeline to apply") -@click.option("--batch-size", "-b", default=1000, help="Batch size") -@click.option("--engine", "-e", default="default") -def search_ingest( - index: str, source: str, fmt: str, pipeline: str, batch_size: int, engine: str -): - """Ingest documents into an index. - - \b - Examples: - hanzo search ingest -i products --from products.jsonl - hanzo search ingest -i logs --from s3://bucket/logs/*.jsonl - hanzo search ingest -i docs --from storage://mybucket/docs.parquet - """ - console.print(f"[cyan]Ingesting into '{index}' from '{source}'...[/cyan]") - - payload = { - "source": source, - "format": fmt, - "batch_size": batch_size, - } - if pipeline: - payload["pipeline"] = pipeline - - resp = _request( - "post", f"/v1/engines/{engine}/indexes/{index}/ingest", json=payload - ) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Ingestion complete") - console.print(f" Documents: {data.get('ingested', 0):,}") - console.print(f" Errors: {data.get('errors', 0)}") - if data.get("duration"): - console.print(f" Duration: {data['duration']}") - - -@search_group.command(name="query") -@click.option("--index", "-i", required=True, help="Index to search") -@click.option("--q", required=True, help="Query string") -@click.option("--filter", "-f", help="Filter expression") -@click.option("--topk", "-k", default=10, help="Max results") -@click.option("--mode", "-m", type=click.Choice(["lexical", "vector", "hybrid"])) -@click.option("--vector-field", help="Field for vector search") -@click.option("--json", "as_json", is_flag=True, help="Output as JSON") -@click.option("--engine", "-e", default="default") -def search_query( - index: str, - q: str, - filter: str, - topk: int, - mode: str, - vector_field: str, - as_json: bool, - engine: str, -): - """Search documents. - - \b - Examples: - hanzo search query -i products -q "wireless headphones" -k 20 - hanzo search query -i docs -q "machine learning" --mode vector - hanzo search query -i users -q "john" -f "status=active" - """ - payload = {"query": q, "top_k": topk} - if filter: - payload["filter"] = filter - if mode: - payload["mode"] = mode - if vector_field: - payload["vector_field"] = vector_field - - resp = _request("post", f"/v1/engines/{engine}/indexes/{index}/query", json=payload) - data = check_response(resp) - hits = data.get("hits", data.get("results", [])) - - if as_json: - click.echo(json.dumps(data, indent=2, default=str)) - return - - table = Table(title=f"Results for '{q}' in '{index}'", box=box.ROUNDED) - table.add_column("#", style="dim") - table.add_column("ID", style="cyan") - table.add_column("Score", style="green") - table.add_column("Source", style="white") - - for i, hit in enumerate(hits, 1): - source = hit.get("_source", hit.get("document", {})) - source_str = json.dumps(source, default=str)[:80] if source else "-" - table.add_row( - str(i), - str(hit.get("_id", hit.get("id", "")))[:24], - f"{hit.get('_score', hit.get('score', 0)):.4f}", - source_str, - ) - - console.print(table) - total = data.get("total", len(hits)) - console.print( - f"[dim]{total} total hit(s), showing top {min(topk, len(hits))}[/dim]" - ) - - -@search_group.command(name="reindex") -@click.option("--from", "source_idx", required=True, help="Source index") -@click.option("--to", "dest_idx", required=True, help="Destination index") -@click.option("--query", "-q", help="Filter query") -@click.option("--pipeline", "-p", help="Transform pipeline") -@click.option("--engine", "-e", default="default") -def search_reindex( - source_idx: str, dest_idx: str, query: str, pipeline: str, engine: str -): - """Reindex documents.""" - console.print(f"[cyan]Reindexing from '{source_idx}' to '{dest_idx}'...[/cyan]") - - payload = {"source": source_idx, "destination": dest_idx} - if query: - payload["query"] = query - if pipeline: - payload["pipeline"] = pipeline - - resp = _request("post", f"/v1/engines/{engine}/reindex", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Reindex complete") - console.print(f" Documents: {data.get('reindexed', 0):,}") - if data.get("duration"): - console.print(f" Duration: {data['duration']}") - - -# ============================================================================ -# Pipelines -# ============================================================================ - - -@search_group.group() -def pipeline(): - """Manage ingest pipelines.""" - pass - - -@pipeline.command(name="create") -@click.argument("name") -@click.option("--engine", "-e", default="default") -@click.option("--config", "-c", help="Pipeline config (JSON or file)") -def pipeline_create(name: str, engine: str, config: str): - """Create an ingest pipeline.""" - payload = {"name": name} - if config: - from pathlib import Path - - if Path(config).exists(): - payload["config"] = json.loads(Path(config).read_text()) - else: - payload["config"] = json.loads(config) - - resp = _request("post", f"/v1/engines/{engine}/pipelines", json=payload) - data = check_response(resp) - console.print(f"[green]โœ“[/green] Pipeline '{name}' created") - console.print(f" ID: {data.get('id', '-')}") - - -@pipeline.command(name="list") -@click.option("--engine", "-e", default="default") -def pipeline_list(engine: str): - """List ingest pipelines.""" - resp = _request("get", f"/v1/engines/{engine}/pipelines") - data = check_response(resp) - items = data.get("pipelines", data.get("items", [])) - - table = Table(title="Ingest Pipelines", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Processors", style="white") - table.add_column("Description", style="dim") - - for p in items: - processors = p.get("processors", []) - table.add_row( - p.get("name", ""), - str(len(processors)), - p.get("description", "-"), - ) - - console.print(table) - if not items: - console.print("[dim]No pipelines found[/dim]") - - -@pipeline.command(name="describe") -@click.argument("name") -@click.option("--engine", "-e", default="default") -def pipeline_describe(name: str, engine: str): - """Show pipeline details.""" - resp = _request("get", f"/v1/engines/{engine}/pipelines/{name}") - data = check_response(resp) - - console.print( - Panel( - f"[cyan]Name:[/cyan] {data.get('name', name)}\n" - f"[cyan]Description:[/cyan] {data.get('description', '-')}\n" - f"[cyan]Processors:[/cyan] {len(data.get('processors', []))}\n" - f"[cyan]Config:[/cyan]\n{json.dumps(data.get('config', {}), indent=2)}", - title="Pipeline Details", - border_style="cyan", - ) - ) - - -@pipeline.command(name="delete") -@click.argument("name") -@click.option("--engine", "-e", default="default") -def pipeline_delete(name: str, engine: str): - """Delete a pipeline.""" - resp = _request("delete", f"/v1/engines/{engine}/pipelines/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Pipeline '{name}' deleted") - - -@pipeline.command(name="test") -@click.argument("name") -@click.option("--doc", "-d", help="Test document (JSON)") -@click.option("--engine", "-e", default="default") -def pipeline_test(name: str, doc: str, engine: str): - """Test a pipeline with sample document.""" - payload = {} - if doc: - payload["document"] = json.loads(doc) - - resp = _request("post", f"/v1/engines/{engine}/pipelines/{name}/test", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Pipeline test passed") - if data.get("output"): - console.print(f" Output: {json.dumps(data['output'], indent=2, default=str)}") - - -# ============================================================================ -# Admin -# ============================================================================ - - -@search_group.command(name="stats") -@click.option("--engine", "-e", default="default") -def search_stats(engine: str): - """Show search engine statistics.""" - resp = _request("get", f"/v1/engines/{engine}/stats") - data = check_response(resp) - - console.print( - Panel( - f"[cyan]Engine:[/cyan] {data.get('name', engine)}\n" - f"[cyan]Indexes:[/cyan] {data.get('index_count', 0)}\n" - f"[cyan]Documents:[/cyan] {data.get('document_count', 0):,}\n" - f"[cyan]Size:[/cyan] {data.get('size', '0 B')}\n" - f"[cyan]Queries/day:[/cyan] {data.get('queries_per_day', 0):,}\n" - f"[cyan]Avg latency:[/cyan] {data.get('avg_latency_ms', 0)}ms", - title="Search Statistics", - border_style="cyan", - ) - ) - - -@search_group.command(name="backup") -@click.option("--engine", "-e", default="default") -@click.option("--index", "-i", help="Specific index") -@click.option("--output", "-o", help="Output location") -def search_backup(engine: str, index: str, output: str): - """Backup search data.""" - payload = {"engine": engine} - if index: - payload["index"] = index - if output: - payload["output"] = output - - resp = _request("post", f"/v1/engines/{engine}/backups", json=payload) - data = check_response(resp) - console.print(f"[green]โœ“[/green] Backup created") - console.print(f" Backup ID: {data.get('id', '-')}") - console.print(f" Size: {data.get('size', '-')}") diff --git a/pkg/hanzo/src/hanzo/commands/secrets.py b/pkg/hanzo/src/hanzo/commands/secrets.py deleted file mode 100644 index 2e91e3df7..000000000 --- a/pkg/hanzo/src/hanzo/commands/secrets.py +++ /dev/null @@ -1,315 +0,0 @@ -"""Hanzo Secrets - Secret management CLI. - -Secure secret storage with versioning and rotation via Hanzo KMS. -""" - -import os -import sys -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -KMS_URL = os.getenv("HANZO_KMS_URL", "https://kms.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(KMS_URL, method, path, **kwargs) - - -@click.group(name="secrets") -def secrets_group(): - """Hanzo Secrets - Secure secret management. - - \b - Operations: - hanzo secrets set # Set a secret - hanzo secrets get # Get secret value - hanzo secrets list # List secrets (names only) - hanzo secrets unset # Delete a secret - hanzo secrets rotate # Rotate a secret - - \b - Versions: - hanzo secrets versions # List secret versions - hanzo secrets rollback # Rollback to previous version - - \b - Access: - hanzo secrets grant # Grant access to secret - hanzo secrets revoke # Revoke access - hanzo secrets audit # View access logs - """ - pass - - -# ============================================================================ -# Secret Operations -# ============================================================================ - - -@secrets_group.command(name="set") -@click.argument("name") -@click.option("--value", "-v", help="Secret value (or use stdin)") -@click.option("--file", "-f", type=click.Path(exists=True), help="Read value from file") -@click.option("--env", "-e", help="Environment (dev/staging/prod)") -@click.option("--description", "-d", help="Secret description") -def secrets_set(name: str, value: str, file: str, env: str, description: str): - """Set a secret value. - - \b - Examples: - hanzo secrets set API_KEY --value sk-abc123 - echo "secret" | hanzo secrets set DB_PASSWORD - hanzo secrets set CERT --file ./cert.pem - """ - if file: - from pathlib import Path - - value = Path(file).read_text().strip() - elif not value: - if not sys.stdin.isatty(): - value = sys.stdin.read().strip() - else: - value = click.prompt("Secret value", hide_input=True) - - payload = {"key": name, "value": value} - if env: - payload["environment"] = env - if description: - payload["description"] = description - - resp = _request("post", "/v1/secrets", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Secret '{name}' set") - console.print(f" Version: {data.get('version', 1)}") - if env: - console.print(f" Environment: {env}") - - -@secrets_group.command(name="get") -@click.argument("name") -@click.option("--env", "-e", help="Environment (dev/staging/prod)") -@click.option("--version", "-v", help="Specific version") -@click.option("--plain", is_flag=True, help="Output value only (no formatting)") -def secrets_get(name: str, env: str, version: str, plain: bool): - """Get a secret value.""" - params = {} - if env: - params["environment"] = env - if version: - params["version"] = version - - resp = _request("get", f"/v1/secrets/{name}", params=params) - data = check_response(resp) - - if plain: - click.echo(data.get("value", "")) - else: - console.print( - Panel( - f"[cyan]Name:[/cyan] {name}\n" - f"[cyan]Value:[/cyan] {data.get('value', '')}\n" - f"[cyan]Version:[/cyan] {data.get('version', '-')}\n" - f"[cyan]Environment:[/cyan] {data.get('environment', 'default')}\n" - f"[cyan]Updated:[/cyan] {data.get('updated_at', '-')}", - title="Secret", - border_style="cyan", - ) - ) - - -@secrets_group.command(name="list") -@click.option("--env", "-e", help="Filter by environment") -@click.option("--prefix", "-p", help="Filter by prefix") -def secrets_list(env: str, prefix: str): - """List all secrets (names only, not values).""" - params = {} - if env: - params["environment"] = env - if prefix: - params["prefix"] = prefix - - resp = _request("get", "/v1/secrets", params=params) - data = check_response(resp) - items = data.get("secrets", data.get("items", [])) - - table = Table(title="Secrets", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Environment", style="white") - table.add_column("Version", style="green") - table.add_column("Updated", style="dim") - - for s in items: - table.add_row( - s.get("key", s.get("name", "")), - s.get("environment", "default"), - str(s.get("version", "-")), - str(s.get("updated_at", "-"))[:19], - ) - - console.print(table) - if not items: - console.print( - "[dim]No secrets found. Create one with 'hanzo secrets set'[/dim]" - ) - - -@secrets_group.command(name="unset") -@click.argument("name") -@click.option("--env", "-e", help="Environment (dev/staging/prod)") -@click.option("--force", "-f", is_flag=True, help="Skip confirmation") -def secrets_unset(name: str, env: str, force: bool): - """Delete a secret.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete secret '{name}'?[/red]"): - return - - params = {} - if env: - params["environment"] = env - - resp = _request("delete", f"/v1/secrets/{name}", params=params) - check_response(resp) - console.print(f"[green]โœ“[/green] Secret '{name}' deleted") - - -@secrets_group.command(name="rotate") -@click.argument("name") -@click.option("--env", "-e", help="Environment") -def secrets_rotate(name: str, env: str): - """Rotate a secret (generate new value).""" - payload = {} - if env: - payload["environment"] = env - - resp = _request("post", f"/v1/secrets/{name}/rotate", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Secret '{name}' rotated") - console.print(f" New version: {data.get('version', '-')}") - console.print(f" Previous version expires: {data.get('previous_expires', '24h')}") - - -# ============================================================================ -# Version Management -# ============================================================================ - - -@secrets_group.command(name="versions") -@click.argument("name") -@click.option("--env", "-e", help="Environment") -def secrets_versions(name: str, env: str): - """List secret versions.""" - params = {} - if env: - params["environment"] = env - - resp = _request("get", f"/v1/secrets/{name}/versions", params=params) - data = check_response(resp) - versions = data.get("versions", []) - - table = Table(title=f"Versions: {name}", box=box.ROUNDED) - table.add_column("Version", style="cyan") - table.add_column("Status", style="green") - table.add_column("Created", style="dim") - table.add_column("Created By", style="dim") - - for v in versions: - status = "โ— Active" if v.get("active") else "โ—‹ Inactive" - style = "green" if v.get("active") else "dim" - table.add_row( - str(v.get("version", "")), - f"[{style}]{status}[/{style}]", - str(v.get("created_at", ""))[:19], - v.get("created_by", "-"), - ) - - console.print(table) - if not versions: - console.print(f"[dim]No versions found for '{name}'[/dim]") - - -@secrets_group.command(name="rollback") -@click.argument("name") -@click.option("--version", "-v", required=True, help="Version to rollback to") -@click.option("--env", "-e", help="Environment") -def secrets_rollback(name: str, version: str, env: str): - """Rollback to a previous secret version.""" - payload = {"version": int(version)} - if env: - payload["environment"] = env - - resp = _request("post", f"/v1/secrets/{name}/rollback", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Rolled back '{name}' to version {version}") - - -# ============================================================================ -# Access Control -# ============================================================================ - - -@secrets_group.command(name="grant") -@click.argument("name") -@click.option("--to", "-t", required=True, help="Service or user to grant access") -@click.option( - "--role", "-r", default="read", type=click.Choice(["read", "write", "admin"]) -) -def secrets_grant(name: str, to: str, role: str): - """Grant access to a secret.""" - resp = _request( - "post", f"/v1/secrets/{name}/access", json={"principal": to, "role": role} - ) - check_response(resp) - console.print(f"[green]โœ“[/green] Granted {role} access to '{name}' for {to}") - - -@secrets_group.command(name="revoke") -@click.argument("name") -@click.option("--from", "from_", required=True, help="Service or user to revoke") -def secrets_revoke(name: str, from_: str): - """Revoke access to a secret.""" - resp = _request("delete", f"/v1/secrets/{name}/access/{from_}") - check_response(resp) - console.print(f"[green]โœ“[/green] Revoked access to '{name}' from {from_}") - - -@secrets_group.command(name="audit") -@click.argument("name", required=False) -@click.option("--limit", "-n", default=50, help="Number of entries") -def secrets_audit(name: str, limit: int): - """View secret access logs.""" - path = f"/v1/secrets/{name}/audit" if name else "/v1/secrets/audit" - resp = _request("get", path, params={"limit": limit}) - data = check_response(resp) - entries = data.get("entries", data.get("logs", [])) - - table = Table(title="Secret Access Log", box=box.ROUNDED) - table.add_column("Time", style="dim") - table.add_column("Secret", style="cyan") - table.add_column("Action", style="white") - table.add_column("Actor", style="green") - table.add_column("IP", style="dim") - - for e in entries: - table.add_row( - str(e.get("timestamp", ""))[:19], - e.get("secret", e.get("key", "-")), - e.get("action", "-"), - e.get("actor", e.get("principal", "-")), - e.get("ip", e.get("source_ip", "-")), - ) - - console.print(table) - if not entries: - console.print("[dim]No access logs found[/dim]") diff --git a/pkg/hanzo/src/hanzo/commands/storage.py b/pkg/hanzo/src/hanzo/commands/storage.py deleted file mode 100644 index 24ee5cb19..000000000 --- a/pkg/hanzo/src/hanzo/commands/storage.py +++ /dev/null @@ -1,715 +0,0 @@ -"""Hanzo Storage โ€” S3-compatible object storage CLI. - -Connects to s3.hanzo.ai (S3-compatible API) using stored credentials. - -Environment variables: - HANZO_S3_ENDPOINT โ€” S3 endpoint (default: https://s3.hanzo.ai) - HANZO_S3_REGION โ€” Region (default: us-east-1) - AWS_ACCESS_KEY_ID โ€” S3 access key (or from hanzo auth) - AWS_SECRET_ACCESS_KEY โ€” S3 secret key (or from hanzo auth) -""" - -from __future__ import annotations - -import os -import sys -import json -from typing import Any -from pathlib import Path - -import click -from rich import box -from rich.table import Table -from rich.progress import Progress, TextColumn, SpinnerColumn - -from ..utils.output import console - -S3_ENDPOINT = os.getenv("HANZO_S3_ENDPOINT", "https://s3.hanzo.ai") -S3_REGION = os.getenv("HANZO_S3_REGION", "us-east-1") - - -def _get_s3_client(): - """Create an S3 client connected to s3.hanzo.ai.""" - try: - import boto3 - from botocore.config import Config - except ImportError: - console.print( - "[red]boto3 not installed.[/red] Run: pip install 'hanzo[storage]'" - ) - raise SystemExit(1) - - # Try to get credentials from env or hanzo auth - access_key = os.getenv("AWS_ACCESS_KEY_ID") - secret_key = os.getenv("AWS_SECRET_ACCESS_KEY") - - if not access_key or not secret_key: - # Try loading from hanzo auth token - token_file = Path.home() / ".hanzo" / "auth" / "s3_credentials.json" - if token_file.exists(): - try: - creds = json.loads(token_file.read_text()) - access_key = creds.get("access_key_id") - secret_key = creds.get("secret_access_key") - except (json.JSONDecodeError, OSError): - pass - - if not access_key or not secret_key: - console.print("[red]S3 credentials not configured.[/red]") - console.print("Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, or run:") - console.print(" hanzo storage auth") - raise SystemExit(1) - - return boto3.client( - "s3", - endpoint_url=S3_ENDPOINT, - region_name=S3_REGION, - aws_access_key_id=access_key, - aws_secret_access_key=secret_key, - config=Config(signature_version="s3v4"), - ) - - -def _format_size(size_bytes: int) -> str: - """Format bytes to human-readable size.""" - for unit in ("B", "KB", "MB", "GB", "TB"): - if abs(size_bytes) < 1024: - return f"{size_bytes:.1f} {unit}" - size_bytes /= 1024 - return f"{size_bytes:.1f} PB" - - -def _parse_s3_path(path: str) -> tuple[str, str]: - """Parse 's3://bucket/key' or 'bucket/key' into (bucket, key).""" - path = path.removeprefix("s3://") - parts = path.split("/", 1) - bucket = parts[0] - key = parts[1] if len(parts) > 1 else "" - return bucket, key - - -@click.group(name="s3") -def storage_group(): - """Hanzo S3 โ€” S3-compatible object storage (s3.hanzo.ai). - - \b - Buckets: - hanzo s3 buckets list List buckets - hanzo s3 buckets create Create bucket - hanzo s3 buckets delete Delete bucket - - \b - Objects: - hanzo s3 ls List objects - hanzo s3 cp Copy files (upload/download) - hanzo s3 mv Move/rename objects - hanzo s3 rm Delete objects - hanzo s3 sync Sync directories - - \b - Sharing: - hanzo s3 presign Generate presigned URL - hanzo s3 public Make object public - - \b - Endpoint: s3.hanzo.ai (S3-compatible) - """ - pass - - -# ============================================================================ -# Auth -# ============================================================================ - - -@storage_group.command(name="auth") -@click.option("--access-key", prompt="Access Key ID", help="S3 access key ID") -@click.option( - "--secret-key", - prompt="Secret Access Key", - hide_input=True, - help="S3 secret access key", -) -@click.option("--endpoint", default=S3_ENDPOINT, help="S3 endpoint URL") -def storage_auth(access_key: str, secret_key: str, endpoint: str): - """Configure S3 credentials for hanzo storage. - - Credentials are stored at ~/.hanzo/auth/s3_credentials.json - """ - creds_dir = Path.home() / ".hanzo" / "auth" - creds_dir.mkdir(parents=True, exist_ok=True) - creds_file = creds_dir / "s3_credentials.json" - creds_file.write_text( - json.dumps( - { - "access_key_id": access_key, - "secret_access_key": secret_key, - "endpoint": endpoint, - }, - indent=2, - ) - ) - creds_file.chmod(0o600) - console.print(f"[green]S3 credentials saved to {creds_file}[/green]") - - -# ============================================================================ -# Bucket Management -# ============================================================================ - - -@storage_group.group() -def buckets(): - """Manage storage buckets.""" - pass - - -@buckets.command(name="list") -@click.option("--json-output", "json_out", is_flag=True, help="Output as JSON") -def buckets_list(json_out: bool): - """List all buckets.""" - s3 = _get_s3_client() - response = s3.list_buckets() - bucket_list = response.get("Buckets", []) - - if json_out: - click.echo( - json.dumps( - [ - {"name": b["Name"], "created": b["CreationDate"].isoformat()} - for b in bucket_list - ], - indent=2, - ) - ) - return - - if not bucket_list: - console.print( - "[dim]No buckets found. Create one with 'hanzo storage buckets create'[/dim]" - ) - return - - table = Table(title=f"Buckets ({len(bucket_list)})", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Created", style="dim") - - for b in bucket_list: - table.add_row(b["Name"], b["CreationDate"].strftime("%Y-%m-%d %H:%M:%S")) - - console.print(table) - - -@buckets.command(name="create") -@click.argument("name") -@click.option("--region", "-r", default=S3_REGION, help="Bucket region") -def buckets_create(name: str, region: str): - """Create a bucket.""" - s3 = _get_s3_client() - create_config = {} - if region and region != "us-east-1": - create_config["CreateBucketConfiguration"] = {"LocationConstraint": region} - s3.create_bucket(Bucket=name, **create_config) - console.print(f"[green]Bucket '{name}' created.[/green]") - - -@buckets.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True, help="Delete even if not empty") -@click.option("--yes", "-y", is_flag=True, help="Skip confirmation") -def buckets_delete(name: str, force: bool, yes: bool): - """Delete a bucket.""" - if not yes: - click.confirm(f"Delete bucket '{name}'?", abort=True) - - s3 = _get_s3_client() - - if force: - # Delete all objects first - paginator = s3.get_paginator("list_objects_v2") - for page in paginator.paginate(Bucket=name): - objects = page.get("Contents", []) - if objects: - s3.delete_objects( - Bucket=name, - Delete={"Objects": [{"Key": o["Key"]} for o in objects]}, - ) - - s3.delete_bucket(Bucket=name) - console.print(f"[green]Bucket '{name}' deleted.[/green]") - - -# ============================================================================ -# Object Operations -# ============================================================================ - - -@storage_group.command(name="ls") -@click.argument("path", default="") -@click.option("--recursive", "-r", is_flag=True, help="List recursively") -@click.option("--human", "-h", is_flag=True, help="Human-readable sizes") -@click.option("--json-output", "json_out", is_flag=True, help="Output as JSON") -def storage_ls(path: str, recursive: bool, human: bool, json_out: bool): - """List objects in a bucket. - - \b - PATH format: bucket/prefix or s3://bucket/prefix - - \b - Examples: - hanzo storage ls mybucket List top-level objects - hanzo storage ls mybucket/images/ -r List recursively - hanzo storage ls s3://mybucket -h Human-readable sizes - """ - if not path: - console.print("[dim]Usage: hanzo storage ls [/prefix][/dim]") - return - - bucket, prefix = _parse_s3_path(path) - s3 = _get_s3_client() - - all_objects = [] - all_prefixes = [] - - paginator = s3.get_paginator("list_objects_v2") - params: dict[str, Any] = {"Bucket": bucket} - if prefix: - params["Prefix"] = prefix - if not recursive: - params["Delimiter"] = "/" - - for page in paginator.paginate(**params): - all_objects.extend(page.get("Contents", [])) - all_prefixes.extend(page.get("CommonPrefixes", [])) - - if json_out: - items = [] - for p in all_prefixes: - items.append({"key": p["Prefix"], "type": "directory"}) - for o in all_objects: - items.append( - { - "key": o["Key"], - "size": o["Size"], - "modified": o["LastModified"].isoformat(), - "type": "file", - } - ) - click.echo(json.dumps(items, indent=2)) - return - - if not all_objects and not all_prefixes: - console.print("[dim]No objects found[/dim]") - return - - table = Table(box=box.SIMPLE) - table.add_column("Name", style="cyan") - table.add_column("Size", style="green", justify="right") - table.add_column("Modified", style="dim") - - for p in all_prefixes: - table.add_row(f"[bold]{p['Prefix']}[/bold]", "DIR", "") - - for o in all_objects: - size_str = _format_size(o["Size"]) if human else str(o["Size"]) - modified = o["LastModified"].strftime("%Y-%m-%d %H:%M:%S") - table.add_row(o["Key"], size_str, modified) - - console.print(table) - total = sum(o["Size"] for o in all_objects) - console.print( - f"[dim]{len(all_objects)} object(s), {len(all_prefixes)} prefix(es), " - f"total {_format_size(total)}[/dim]" - ) - - -@storage_group.command(name="cp") -@click.argument("source") -@click.argument("dest") -@click.option("--recursive", "-r", is_flag=True, help="Copy recursively") -def storage_cp(source: str, dest: str, recursive: bool): - """Copy files to/from storage. - - \b - Examples: - hanzo storage cp file.txt mybucket/ Upload - hanzo storage cp mybucket/file.txt ./ Download - hanzo storage cp -r ./dir mybucket/prefix/ Upload directory - hanzo storage cp mybucket/a.txt mybucket/b.txt Server-side copy - """ - s3 = _get_s3_client() - is_s3_src = ( - source.startswith("s3://") or "/" in source and not os.path.exists(source) - ) - is_s3_dst = ( - dest.startswith("s3://") - or "/" in dest - and not os.path.exists(os.path.dirname(dest) or ".") - ) - - # Heuristic: if source exists locally, it's an upload - if os.path.exists(source) and not source.startswith("s3://"): - is_s3_src = False - - if not is_s3_src and is_s3_dst: - # Upload - bucket, key = _parse_s3_path(dest) - local_path = Path(source) - - if recursive and local_path.is_dir(): - files = list(local_path.rglob("*")) - files = [f for f in files if f.is_file()] - with Progress( - SpinnerColumn(), - TextColumn("[progress.description]{task.description}"), - console=console, - ) as progress: - task = progress.add_task( - f"Uploading {len(files)} files...", total=len(files) - ) - for f in files: - rel = f.relative_to(local_path) - obj_key = f"{key}{rel}" if key else str(rel) - s3.upload_file(str(f), bucket, obj_key) - progress.advance(task) - console.print( - f"[green]Uploaded {len(files)} files to {bucket}/{key}[/green]" - ) - else: - if not key or key.endswith("/"): - key = key + local_path.name - s3.upload_file(str(local_path), bucket, key) - console.print(f"[green]Uploaded {source} -> s3://{bucket}/{key}[/green]") - - elif is_s3_src and not is_s3_dst: - # Download - bucket, key = _parse_s3_path(source) - local_path = Path(dest) - - if recursive: - paginator = s3.get_paginator("list_objects_v2") - objects = [] - for page in paginator.paginate(Bucket=bucket, Prefix=key): - objects.extend(page.get("Contents", [])) - - if not objects: - console.print("[yellow]No objects found to download.[/yellow]") - return - - local_path.mkdir(parents=True, exist_ok=True) - with Progress( - SpinnerColumn(), - TextColumn("[progress.description]{task.description}"), - console=console, - ) as progress: - task = progress.add_task( - f"Downloading {len(objects)} files...", total=len(objects) - ) - for obj in objects: - rel_key = obj["Key"].removeprefix(key).lstrip("/") - if not rel_key: - continue - dest_file = local_path / rel_key - dest_file.parent.mkdir(parents=True, exist_ok=True) - s3.download_file(bucket, obj["Key"], str(dest_file)) - progress.advance(task) - console.print(f"[green]Downloaded {len(objects)} files to {dest}[/green]") - else: - if local_path.is_dir(): - filename = key.rsplit("/", 1)[-1] if "/" in key else key - local_path = local_path / filename - local_path.parent.mkdir(parents=True, exist_ok=True) - s3.download_file(bucket, key, str(local_path)) - console.print( - f"[green]Downloaded s3://{bucket}/{key} -> {local_path}[/green]" - ) - - elif is_s3_src and is_s3_dst: - # Server-side copy - src_bucket, src_key = _parse_s3_path(source) - dst_bucket, dst_key = _parse_s3_path(dest) - s3.copy_object( - CopySource={"Bucket": src_bucket, "Key": src_key}, - Bucket=dst_bucket, - Key=dst_key, - ) - console.print( - f"[green]Copied s3://{src_bucket}/{src_key} -> s3://{dst_bucket}/{dst_key}[/green]" - ) - else: - console.print( - "[red]At least one path must be an S3 path (bucket/key or s3://bucket/key).[/red]" - ) - raise SystemExit(1) - - -@storage_group.command(name="mv") -@click.argument("source") -@click.argument("dest") -def storage_mv(source: str, dest: str): - """Move or rename objects in storage. - - \b - Examples: - hanzo storage mv mybucket/old.txt mybucket/new.txt Rename - hanzo storage mv mybucket/a.txt other-bucket/a.txt Move between buckets - """ - s3 = _get_s3_client() - src_bucket, src_key = _parse_s3_path(source) - dst_bucket, dst_key = _parse_s3_path(dest) - - # Copy then delete - s3.copy_object( - CopySource={"Bucket": src_bucket, "Key": src_key}, - Bucket=dst_bucket, - Key=dst_key, - ) - s3.delete_object(Bucket=src_bucket, Key=src_key) - console.print( - f"[green]Moved s3://{src_bucket}/{src_key} -> s3://{dst_bucket}/{dst_key}[/green]" - ) - - -@storage_group.command(name="rm") -@click.argument("path") -@click.option("--recursive", "-r", is_flag=True, help="Delete recursively") -@click.option("--yes", "-y", is_flag=True, help="Skip confirmation") -def storage_rm(path: str, recursive: bool, yes: bool): - """Delete objects from storage. - - \b - Examples: - hanzo storage rm mybucket/file.txt Delete single object - hanzo storage rm -r mybucket/prefix/ Delete all under prefix - """ - bucket, key = _parse_s3_path(path) - s3 = _get_s3_client() - - if recursive: - # Count objects first - paginator = s3.get_paginator("list_objects_v2") - objects = [] - for page in paginator.paginate(Bucket=bucket, Prefix=key): - objects.extend(page.get("Contents", [])) - - if not objects: - console.print("[yellow]No objects found to delete.[/yellow]") - return - - if not yes: - click.confirm( - f"Delete {len(objects)} object(s) under '{path}'?", abort=True - ) - - # Delete in batches of 1000 (S3 limit) - for i in range(0, len(objects), 1000): - batch = objects[i : i + 1000] - s3.delete_objects( - Bucket=bucket, - Delete={"Objects": [{"Key": o["Key"]} for o in batch]}, - ) - console.print(f"[green]Deleted {len(objects)} object(s).[/green]") - else: - if not yes: - click.confirm(f"Delete '{path}'?", abort=True) - s3.delete_object(Bucket=bucket, Key=key) - console.print(f"[green]Deleted s3://{bucket}/{key}[/green]") - - -@storage_group.command(name="sync") -@click.argument("source") -@click.argument("dest") -@click.option( - "--delete", "delete_extra", is_flag=True, help="Delete files not in source" -) -@click.option("--dry-run", is_flag=True, help="Show what would be done") -def storage_sync(source: str, dest: str, delete_extra: bool, dry_run: bool): - """Sync directories with storage. - - \b - Examples: - hanzo storage sync ./build mybucket/assets/ Upload (sync local -> S3) - hanzo storage sync mybucket/assets/ ./local/ Download (sync S3 -> local) - """ - s3 = _get_s3_client() - src_local = os.path.exists(source) and not source.startswith("s3://") - - if src_local: - # Upload sync: local -> S3 - bucket, prefix = _parse_s3_path(dest) - local_path = Path(source) - - # Get local files - local_files = {} - for f in local_path.rglob("*"): - if f.is_file(): - rel = str(f.relative_to(local_path)) - local_files[rel] = f - - # Get remote files - remote_files: dict[str, dict] = {} - paginator = s3.get_paginator("list_objects_v2") - for page in paginator.paginate(Bucket=bucket, Prefix=prefix): - for obj in page.get("Contents", []): - rel_key = obj["Key"].removeprefix(prefix).lstrip("/") - if rel_key: - remote_files[rel_key] = obj - - # Determine what to upload - to_upload = [] - for rel, f in local_files.items(): - if rel not in remote_files: - to_upload.append(rel) - else: - # Compare by size (quick check) - if f.stat().st_size != remote_files[rel]["Size"]: - to_upload.append(rel) - - to_delete = [] - if delete_extra: - to_delete = [k for k in remote_files if k not in local_files] - - if dry_run: - for rel in to_upload: - console.print(f" [green]upload[/green] {rel}") - for rel in to_delete: - console.print(f" [red]delete[/red] {rel}") - console.print( - f"[dim]Would upload {len(to_upload)}, delete {len(to_delete)}[/dim]" - ) - return - - for rel in to_upload: - obj_key = f"{prefix}{rel}" if prefix else rel - s3.upload_file(str(local_files[rel]), bucket, obj_key) - - if to_delete: - for i in range(0, len(to_delete), 1000): - batch = to_delete[i : i + 1000] - s3.delete_objects( - Bucket=bucket, - Delete={"Objects": [{"Key": f"{prefix}{k}"} for k in batch]}, - ) - - console.print( - f"[green]Synced: {len(to_upload)} uploaded, " - f"{len(to_delete)} deleted[/green]" - ) - else: - # Download sync: S3 -> local - bucket, prefix = _parse_s3_path(source) - local_path = Path(dest) - local_path.mkdir(parents=True, exist_ok=True) - - # Get remote files - remote_files: dict[str, dict] = {} - paginator = s3.get_paginator("list_objects_v2") - for page in paginator.paginate(Bucket=bucket, Prefix=prefix): - for obj in page.get("Contents", []): - rel_key = obj["Key"].removeprefix(prefix).lstrip("/") - if rel_key: - remote_files[rel_key] = obj - - # Get local files - local_files_set = set() - for f in local_path.rglob("*"): - if f.is_file(): - local_files_set.add(str(f.relative_to(local_path))) - - to_download = [] - for rel, obj in remote_files.items(): - dest_file = local_path / rel - if not dest_file.exists(): - to_download.append(rel) - elif dest_file.stat().st_size != obj["Size"]: - to_download.append(rel) - - to_delete = [] - if delete_extra: - to_delete = [k for k in local_files_set if k not in remote_files] - - if dry_run: - for rel in to_download: - console.print(f" [green]download[/green] {rel}") - for rel in to_delete: - console.print(f" [red]delete[/red] {rel}") - console.print( - f"[dim]Would download {len(to_download)}, delete {len(to_delete)}[/dim]" - ) - return - - for rel in to_download: - dest_file = local_path / rel - dest_file.parent.mkdir(parents=True, exist_ok=True) - s3.download_file(bucket, remote_files[rel]["Key"], str(dest_file)) - - for rel in to_delete: - (local_path / rel).unlink() - - console.print( - f"[green]Synced: {len(to_download)} downloaded, " - f"{len(to_delete)} deleted[/green]" - ) - - -@storage_group.command(name="presign") -@click.argument("path") -@click.option( - "--expires", - "-e", - default=3600, - type=int, - help="Expiration in seconds (default: 3600)", -) -@click.option("--method", "-m", default="GET", type=click.Choice(["GET", "PUT"])) -def storage_presign(path: str, expires: int, method: str): - """Generate a presigned URL for an object. - - \b - Examples: - hanzo storage presign mybucket/file.txt 1-hour GET URL - hanzo storage presign mybucket/file.txt -e 86400 24-hour URL - hanzo storage presign mybucket/upload.txt -m PUT Upload URL - """ - bucket, key = _parse_s3_path(path) - s3 = _get_s3_client() - - client_method = "get_object" if method == "GET" else "put_object" - url = s3.generate_presigned_url( - ClientMethod=client_method, - Params={"Bucket": bucket, "Key": key}, - ExpiresIn=expires, - ) - console.print(f"[cyan]Presigned URL ({method}, expires in {expires}s):[/cyan]") - click.echo(url) - - -@storage_group.command(name="public") -@click.argument("path") -@click.option("--recursive", "-r", is_flag=True, help="Apply to all objects in prefix") -def storage_public(path: str, recursive: bool): - """Make object(s) publicly accessible via CDN. - - Sets the ACL to public-read and prints the CDN URL. - - \b - Examples: - hanzo storage public mybucket/image.png Single object - hanzo storage public -r mybucket/assets/ All under prefix - """ - bucket, key = _parse_s3_path(path) - s3 = _get_s3_client() - - if recursive: - paginator = s3.get_paginator("list_objects_v2") - count = 0 - for page in paginator.paginate(Bucket=bucket, Prefix=key): - for obj in page.get("Contents", []): - s3.put_object_acl(Bucket=bucket, Key=obj["Key"], ACL="public-read") - count += 1 - console.print(f"[green]Made {count} object(s) public.[/green]") - console.print(f" CDN: https://cdn.hanzo.ai/{bucket}/{key}") - else: - s3.put_object_acl(Bucket=bucket, Key=key, ACL="public-read") - console.print(f"[green]Made '{key}' public.[/green]") - console.print(f" URL: https://cdn.hanzo.ai/{bucket}/{key}") diff --git a/pkg/hanzo/src/hanzo/commands/tasks.py b/pkg/hanzo/src/hanzo/commands/tasks.py deleted file mode 100644 index 6d268dec6..000000000 --- a/pkg/hanzo/src/hanzo/commands/tasks.py +++ /dev/null @@ -1,595 +0,0 @@ -"""Hanzo Tasks - Task orchestration CLI. - -Developer-facing task graphs, schedules, triggers, and runbooks. -Built on top of jobs for execution substrate. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -TASKS_URL = os.getenv("HANZO_TASKS_URL", "https://tasks.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(TASKS_URL, method, path, **kwargs) - - -@click.group(name="tasks") -def tasks_group(): - """Hanzo Tasks - Workflow orchestration. - - \b - Tasks: - hanzo tasks create # Create a task - hanzo tasks list # List tasks - hanzo tasks describe # Task details - hanzo tasks delete # Delete task - hanzo tasks run # Run task manually - - \b - Schedules: - hanzo tasks schedule set # Set cron schedule - hanzo tasks schedule list # List schedules - hanzo tasks schedule rm # Remove schedule - - \b - Runs: - hanzo tasks runs list # List task runs - hanzo tasks runs logs # View run logs - hanzo tasks runs cancel # Cancel a run - hanzo tasks runs retry # Retry a run - - \b - Triggers: - hanzo tasks triggers add # Add trigger - hanzo tasks triggers list # List triggers - hanzo tasks triggers rm # Remove trigger - """ - pass - - -# ============================================================================ -# Task Management -# ============================================================================ - - -@tasks_group.command(name="create") -@click.argument("name") -@click.option("--image", "-i", help="Container image to run") -@click.option("--cmd", "-c", help="Command to execute") -@click.option("--function", "-f", help="Function to invoke") -@click.option("--env", "-e", multiple=True, help="Environment variables") -@click.option("--timeout", "-t", default="1h", help="Task timeout") -@click.option("--retries", "-r", default=3, help="Max retries on failure") -def tasks_create( - name: str, - image: str, - cmd: str, - function: str, - env: tuple, - timeout: str, - retries: int, -): - """Create a task definition. - - \b - Examples: - hanzo tasks create etl --image my-etl:v1 --timeout 2h - hanzo tasks create backup --cmd "pg_dump..." --retries 5 - hanzo tasks create notify --function notifications.send - """ - body = {"name": name, "timeout": timeout, "max_retries": retries} - if image: - body["image"] = image - if cmd: - body["command"] = cmd - if function: - body["function"] = function - if env: - env_dict = {} - for e in env: - k, v = e.split("=", 1) - env_dict[k] = v - body["env"] = env_dict - - resp = _request("post", "/v1/tasks", json=body) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Task '{name}' created") - console.print(f" ID: {data.get('id', '-')}") - if image: - console.print(f" Image: {image}") - elif cmd: - console.print(f" Command: {cmd}") - elif function: - console.print(f" Function: {function}") - console.print(f" Timeout: {timeout}") - console.print(f" Retries: {retries}") - - -@tasks_group.command(name="list") -@click.option( - "--status", "-s", type=click.Choice(["active", "disabled", "all"]), default="all" -) -def tasks_list(status: str): - """List all tasks.""" - params = {} - if status != "all": - params["status"] = status - - resp = _request("get", "/v1/tasks", params=params) - data = check_response(resp) - items = data.get("tasks", data.get("items", [])) - - table = Table(title="Tasks", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Type", style="white") - table.add_column("Schedule", style="yellow") - table.add_column("Triggers", style="green") - table.add_column("Last Run", style="dim") - table.add_column("Status", style="dim") - - for t in items: - t_status = t.get("status", "active") - style = "green" if t_status == "active" else "yellow" - task_type = ( - "image" - if t.get("image") - else "function" if t.get("function") else "command" - ) - table.add_row( - t.get("name", ""), - task_type, - t.get("schedule", "-"), - str(t.get("trigger_count", 0)), - str(t.get("last_run_at", "-"))[:19], - f"[{style}]{t_status}[/{style}]", - ) - - console.print(table) - if not items: - console.print("[dim]No tasks found. Create one with 'hanzo tasks create'[/dim]") - - -@tasks_group.command(name="describe") -@click.argument("name") -def tasks_describe(name: str): - """Show task details.""" - resp = _request("get", f"/v1/tasks/{name}") - data = check_response(resp) - - status = data.get("status", "active") - status_style = "green" if status == "active" else "yellow" - - console.print( - Panel( - f"[cyan]Name:[/cyan] {data.get('name', name)}\n" - f"[cyan]Type:[/cyan] {'image' if data.get('image') else 'function' if data.get('function') else 'command'}\n" - f"[cyan]Image:[/cyan] {data.get('image', '-')}\n" - f"[cyan]Timeout:[/cyan] {data.get('timeout', '-')}\n" - f"[cyan]Retries:[/cyan] {data.get('max_retries', 3)}\n" - f"[cyan]Schedule:[/cyan] {data.get('schedule', '-')}\n" - f"[cyan]Triggers:[/cyan] {data.get('trigger_count', 0)}\n" - f"[cyan]Status:[/cyan] [{status_style}]{status}[/{status_style}]\n" - f"[cyan]Last run:[/cyan] {str(data.get('last_run_at', '-'))[:19]}\n" - f"[cyan]Next run:[/cyan] {str(data.get('next_run_at', '-'))[:19]}", - title="Task Details", - border_style="cyan", - ) - ) - - -@tasks_group.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True, help="Skip confirmation") -def tasks_delete(name: str, force: bool): - """Delete a task.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete task '{name}'?[/red]"): - return - - resp = _request("delete", f"/v1/tasks/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Task '{name}' deleted") - - -@tasks_group.command(name="run") -@click.argument("name") -@click.option("--input", "-i", "input_data", help="JSON input data") -@click.option("--wait", "-w", is_flag=True, help="Wait for completion") -@click.option("--env", "-e", multiple=True, help="Override env vars") -def tasks_run(name: str, input_data: str, wait: bool, env: tuple): - """Run a task manually.""" - body = {} - if input_data: - body["input"] = json.loads(input_data) - if env: - env_dict = {} - for e in env: - k, v = e.split("=", 1) - env_dict[k] = v - body["env"] = env_dict - - resp = _request("post", f"/v1/tasks/{name}/run", json=body) - data = check_response(resp) - - run_id = data.get("id", data.get("run_id", "-")) - console.print(f"[green]โœ“[/green] Task '{name}' started") - console.print(f" Run ID: {run_id}") - - if wait: - console.print("[dim]Waiting for completion...[/dim]") - resp = _request("get", f"/v1/tasks/{name}/runs/{run_id}/wait", timeout=300) - result = check_response(resp) - run_status = result.get("status", "unknown") - style = "green" if run_status == "success" else "red" - console.print(f" Status: [{style}]{run_status}[/{style}]") - if result.get("duration_ms"): - console.print(f" Duration: {result['duration_ms']}ms") - - -@tasks_group.command(name="enable") -@click.argument("name") -def tasks_enable(name: str): - """Enable a task.""" - resp = _request("post", f"/v1/tasks/{name}/enable") - check_response(resp) - console.print(f"[green]โœ“[/green] Task '{name}' enabled") - - -@tasks_group.command(name="disable") -@click.argument("name") -def tasks_disable(name: str): - """Disable a task.""" - resp = _request("post", f"/v1/tasks/{name}/disable") - check_response(resp) - console.print(f"[green]โœ“[/green] Task '{name}' disabled") - - -# ============================================================================ -# Schedules -# ============================================================================ - - -@tasks_group.group() -def schedule(): - """Manage task schedules.""" - pass - - -@schedule.command(name="set") -@click.argument("task") -@click.option("--cron", "-c", help="Cron expression (e.g., '0 2 * * *')") -@click.option("--every", "-e", help="Interval (e.g., 5m, 1h, 1d)") -@click.option("--timezone", "-tz", default="UTC", help="Timezone") -def schedule_set(task: str, cron: str, every: str, timezone: str): - """Set schedule for a task.""" - if not cron and not every: - raise click.ClickException("Specify --cron or --every") - - body = {"timezone": timezone} - if cron: - body["cron"] = cron - if every: - body["interval"] = every - - resp = _request("put", f"/v1/tasks/{task}/schedule", json=body) - check_response(resp) - - if cron: - console.print(f"[green]โœ“[/green] Scheduled '{task}' with cron: {cron}") - elif every: - console.print(f"[green]โœ“[/green] Scheduled '{task}' every {every}") - console.print(f" Timezone: {timezone}") - - -@schedule.command(name="list") -def schedule_list(): - """List all schedules.""" - resp = _request("get", "/v1/schedules") - data = check_response(resp) - items = data.get("schedules", data.get("items", [])) - - table = Table(title="Schedules", box=box.ROUNDED) - table.add_column("Task", style="cyan") - table.add_column("Schedule", style="white") - table.add_column("Timezone", style="dim") - table.add_column("Next Run", style="yellow") - table.add_column("Status", style="green") - - for s in items: - s_status = s.get("status", "active") - style = "green" if s_status == "active" else "yellow" - table.add_row( - s.get("task", ""), - s.get("cron", s.get("interval", "-")), - s.get("timezone", "UTC"), - str(s.get("next_run_at", "-"))[:19], - f"[{style}]{s_status}[/{style}]", - ) - - console.print(table) - if not items: - console.print("[dim]No schedules found[/dim]") - - -@schedule.command(name="rm") -@click.argument("task") -def schedule_rm(task: str): - """Remove schedule from a task.""" - resp = _request("delete", f"/v1/tasks/{task}/schedule") - check_response(resp) - console.print(f"[green]โœ“[/green] Removed schedule from '{task}'") - - -@schedule.command(name="pause") -@click.argument("task") -def schedule_pause(task: str): - """Pause a schedule.""" - resp = _request("post", f"/v1/tasks/{task}/schedule/pause") - check_response(resp) - console.print(f"[green]โœ“[/green] Paused schedule for '{task}'") - - -@schedule.command(name="resume") -@click.argument("task") -def schedule_resume(task: str): - """Resume a schedule.""" - resp = _request("post", f"/v1/tasks/{task}/schedule/resume") - check_response(resp) - console.print(f"[green]โœ“[/green] Resumed schedule for '{task}'") - - -# ============================================================================ -# Runs -# ============================================================================ - - -@tasks_group.group() -def runs(): - """Manage task runs.""" - pass - - -@runs.command(name="list") -@click.argument("task", required=False) -@click.option( - "--status", - "-s", - type=click.Choice(["running", "success", "failed", "all"]), - default="all", -) -@click.option("--limit", "-n", default=20, help="Max results") -def runs_list(task: str, status: str, limit: int): - """List task runs.""" - params = {"limit": limit} - if status != "all": - params["status"] = status - - path = f"/v1/tasks/{task}/runs" if task else "/v1/runs" - resp = _request("get", path, params=params) - data = check_response(resp) - items = data.get("runs", data.get("items", [])) - - title = f"Runs: {task}" if task else "All Runs" - table = Table(title=title, box=box.ROUNDED) - table.add_column("Run ID", style="cyan") - table.add_column("Task", style="white") - table.add_column("Status", style="green") - table.add_column("Started", style="dim") - table.add_column("Duration", style="dim") - table.add_column("Trigger", style="dim") - - for r in items: - r_status = r.get("status", "unknown") - status_style = { - "running": "cyan", - "success": "green", - "failed": "red", - }.get(r_status, "white") - - table.add_row( - str(r.get("id", ""))[:16], - r.get("task", "-"), - f"[{status_style}]{r_status}[/{status_style}]", - str(r.get("started_at", ""))[:19], - f"{r.get('duration_ms', '-')}ms" if r.get("duration_ms") else "-", - r.get("trigger", "-"), - ) - - console.print(table) - if not items: - console.print("[dim]No runs found[/dim]") - - -@runs.command(name="logs") -@click.argument("run_id") -@click.option("--follow", "-f", is_flag=True, help="Follow logs") -@click.option("--tail", "-n", default=100, help="Number of lines") -def runs_logs(run_id: str, follow: bool, tail: int): - """View run logs.""" - params = {"tail": tail} - if follow: - params["follow"] = "true" - - resp = _request("get", f"/v1/runs/{run_id}/logs", params=params) - data = check_response(resp) - lines = data.get("logs", data.get("lines", [])) - - console.print(f"[cyan]Logs for run {run_id}:[/cyan]") - for line in lines: - if isinstance(line, dict): - ts = str(line.get("timestamp", ""))[:19] - level = line.get("level", "info") - msg = line.get("message", "") - style = ( - "red" if level == "error" else "yellow" if level == "warn" else "dim" - ) - console.print(f"[dim]{ts}[/dim] [{style}]{level}[/{style}] {msg}") - else: - console.print(str(line)) - - if not lines: - console.print("[dim]No logs available[/dim]") - - -@runs.command(name="cancel") -@click.argument("run_id") -def runs_cancel(run_id: str): - """Cancel a running task.""" - resp = _request("post", f"/v1/runs/{run_id}/cancel") - check_response(resp) - console.print(f"[green]โœ“[/green] Run '{run_id}' cancelled") - - -@runs.command(name="retry") -@click.argument("run_id") -def runs_retry(run_id: str): - """Retry a failed run.""" - resp = _request("post", f"/v1/runs/{run_id}/retry") - data = check_response(resp) - new_run_id = data.get("id", data.get("run_id", "-")) - console.print(f"[green]โœ“[/green] Run '{run_id}' retried") - console.print(f" New Run ID: {new_run_id}") - - -# ============================================================================ -# Triggers -# ============================================================================ - - -@tasks_group.group() -def triggers(): - """Manage task triggers.""" - pass - - -@triggers.command(name="add") -@click.argument("task") -@click.option( - "--on", - "trigger_type", - required=True, - type=click.Choice(["event", "queue", "topic", "http", "schedule"]), - help="Trigger type", -) -@click.option( - "--source", - "-s", - required=True, - help="Trigger source (event name, queue name, etc.)", -) -@click.option("--filter", "-f", help="Event filter expression") -def triggers_add(task: str, trigger_type: str, source: str, filter: str): - """Add a trigger to a task. - - \b - Examples: - hanzo tasks triggers add etl --on queue --source incoming-data - hanzo tasks triggers add notify --on topic --source orders.created - hanzo tasks triggers add webhook --on http --source /api/trigger - hanzo tasks triggers add sync --on event --source user.signup - """ - body = {"type": trigger_type, "source": source} - if filter: - body["filter"] = filter - - resp = _request("post", f"/v1/tasks/{task}/triggers", json=body) - check_response(resp) - - console.print(f"[green]โœ“[/green] Added {trigger_type} trigger to '{task}'") - console.print(f" Source: {source}") - if filter: - console.print(f" Filter: {filter}") - - -@triggers.command(name="list") -@click.argument("task", required=False) -def triggers_list(task: str): - """List triggers.""" - path = f"/v1/tasks/{task}/triggers" if task else "/v1/triggers" - resp = _request("get", path) - data = check_response(resp) - items = data.get("triggers", data.get("items", [])) - - table = Table(title="Triggers", box=box.ROUNDED) - table.add_column("Task", style="cyan") - table.add_column("Type", style="white") - table.add_column("Source", style="yellow") - table.add_column("Filter", style="dim") - table.add_column("Status", style="green") - - for t in items: - t_status = t.get("status", "active") - style = "green" if t_status == "active" else "yellow" - table.add_row( - t.get("task", "-"), - t.get("type", "-"), - t.get("source", "-"), - t.get("filter", "-"), - f"[{style}]{t_status}[/{style}]", - ) - - console.print(table) - if not items: - console.print("[dim]No triggers found[/dim]") - - -@triggers.command(name="rm") -@click.argument("task") -@click.option("--source", "-s", help="Specific trigger source to remove") -@click.option("--all", "remove_all", is_flag=True, help="Remove all triggers") -def triggers_rm(task: str, source: str, remove_all: bool): - """Remove triggers from a task.""" - if not source and not remove_all: - raise click.ClickException("Specify --source or --all") - - if remove_all: - resp = _request("delete", f"/v1/tasks/{task}/triggers") - check_response(resp) - console.print(f"[green]โœ“[/green] Removed all triggers from '{task}'") - elif source: - resp = _request("delete", f"/v1/tasks/{task}/triggers/{source}") - check_response(resp) - console.print(f"[green]โœ“[/green] Removed trigger '{source}' from '{task}'") - - -@triggers.command(name="pause") -@click.argument("task") -@click.option("--source", "-s", help="Specific trigger") -def triggers_pause(task: str, source: str): - """Pause triggers.""" - path = ( - f"/v1/tasks/{task}/triggers/{source}/pause" - if source - else f"/v1/tasks/{task}/triggers/pause" - ) - resp = _request("post", path) - check_response(resp) - console.print(f"[green]โœ“[/green] Paused trigger(s) for '{task}'") - - -@triggers.command(name="resume") -@click.argument("task") -@click.option("--source", "-s", help="Specific trigger") -def triggers_resume(task: str, source: str): - """Resume triggers.""" - path = ( - f"/v1/tasks/{task}/triggers/{source}/resume" - if source - else f"/v1/tasks/{task}/triggers/resume" - ) - resp = _request("post", path) - check_response(resp) - console.print(f"[green]โœ“[/green] Resumed trigger(s) for '{task}'") diff --git a/pkg/hanzo/src/hanzo/commands/tools.py b/pkg/hanzo/src/hanzo/commands/tools.py deleted file mode 100644 index 58bce34e6..000000000 --- a/pkg/hanzo/src/hanzo/commands/tools.py +++ /dev/null @@ -1,320 +0,0 @@ -"""Tools management commands.""" - -import click -from rich.table import Table -from rich.syntax import Syntax - -from ..utils.output import console - - -@click.group(name="tools") -def tools_group(): - """Manage Hanzo tools and plugins.""" - pass - - -@tools_group.command(name="list") -@click.option("--category", "-c", help="Filter by category") -@click.option("--installed", is_flag=True, help="Show only installed tools") -@click.pass_context -async def list_tools(ctx, category: str, installed: bool): - """List available tools.""" - try: - from hanzo_tools import get_tool_registry - except ImportError: - console.print("[red]Error:[/red] hanzo-tools not installed") - console.print("Install with: pip install hanzo[tools]") - return - - registry = get_tool_registry() - - with console.status("Loading tools..."): - try: - tools = await registry.list_tools( - category=category, installed_only=installed - ) - except Exception as e: - console.print(f"[red]Failed to load tools: {e}[/red]") - return - - if not tools: - console.print("[yellow]No tools found[/yellow]") - return - - # Group by category - categories = {} - for tool in tools: - cat = tool.get("category", "uncategorized") - if cat not in categories: - categories[cat] = [] - categories[cat].append(tool) - - # Display tools - for cat, cat_tools in sorted(categories.items()): - table = Table(title=f"{cat.title()} Tools") - table.add_column("Name", style="cyan") - table.add_column("Version", style="green") - table.add_column("Description", style="white") - table.add_column("Status", style="yellow") - - for tool in sorted(cat_tools, key=lambda t: t["name"]): - table.add_row( - tool["name"], - tool.get("version", "latest"), - tool.get("description", ""), - "installed" if tool.get("installed") else "available", - ) - - console.print(table) - if len(categories) > 1: - console.print() - - -@tools_group.command() -@click.argument("tool_name") -@click.option("--version", "-v", help="Specific version to install") -@click.pass_context -async def install(ctx, tool_name: str, version: str): - """Install a tool.""" - try: - from hanzo_tools import get_tool_registry - except ImportError: - console.print("[red]Error:[/red] hanzo-tools not installed") - return - - registry = get_tool_registry() - - with console.status(f"Installing {tool_name}..."): - try: - result = await registry.install_tool(name=tool_name, version=version) - - console.print( - f"[green]โœ“[/green] Installed {tool_name} v{result['version']}" - ) - - if deps := result.get("dependencies_installed"): - console.print(f" Dependencies: {', '.join(deps)}") - - if config := result.get("post_install_message"): - console.print(f"\n[yellow]Configuration:[/yellow]") - console.print(config) - - except Exception as e: - console.print(f"[red]Failed to install {tool_name}: {e}[/red]") - - -@tools_group.command() -@click.argument("tool_name") -@click.pass_context -async def uninstall(ctx, tool_name: str): - """Uninstall a tool.""" - try: - from hanzo_tools import get_tool_registry - except ImportError: - console.print("[red]Error:[/red] hanzo-tools not installed") - return - - registry = get_tool_registry() - - if click.confirm(f"Uninstall {tool_name}?"): - with console.status(f"Uninstalling {tool_name}..."): - try: - await registry.uninstall_tool(tool_name) - console.print(f"[green]โœ“[/green] Uninstalled {tool_name}") - except Exception as e: - console.print(f"[red]Failed to uninstall {tool_name}: {e}[/red]") - - -@tools_group.command() -@click.argument("tool_name") -@click.option("--version", "-v", help="Target version") -@click.pass_context -async def update(ctx, tool_name: str, version: str): - """Update a tool.""" - try: - from hanzo_tools import get_tool_registry - except ImportError: - console.print("[red]Error:[/red] hanzo-tools not installed") - return - - registry = get_tool_registry() - - with console.status(f"Updating {tool_name}..."): - try: - result = await registry.update_tool(name=tool_name, version=version) - - console.print(f"[green]โœ“[/green] Updated {tool_name}") - console.print(f" Previous: v{result['previous_version']}") - console.print(f" Current: v{result['current_version']}") - - except Exception as e: - console.print(f"[red]Failed to update {tool_name}: {e}[/red]") - - -@tools_group.command() -@click.argument("tool_name") -@click.pass_context -async def info(ctx, tool_name: str): - """Show tool information.""" - try: - from hanzo_tools import get_tool_registry - except ImportError: - console.print("[red]Error:[/red] hanzo-tools not installed") - return - - registry = get_tool_registry() - - with console.status(f"Loading {tool_name} info..."): - try: - info = await registry.get_tool_info(tool_name) - except Exception as e: - console.print(f"[red]Failed to get info: {e}[/red]") - return - - console.print(f"[cyan]{info['name']}[/cyan]") - console.print(f" Version: {info['version']}") - console.print(f" Category: {info['category']}") - console.print(f" Author: {info.get('author', 'Unknown')}") - console.print(f" License: {info.get('license', 'Unknown')}") - - if desc := info.get("description"): - console.print(f"\n{desc}") - - if features := info.get("features"): - console.print("\n[cyan]Features:[/cyan]") - for feature in features: - console.print(f" โ€ข {feature}") - - if deps := info.get("dependencies"): - console.print("\n[cyan]Dependencies:[/cyan]") - for dep in deps: - console.print(f" โ€ข {dep}") - - if usage := info.get("usage_example"): - console.print("\n[cyan]Usage Example:[/cyan]") - syntax = Syntax(usage, "python", theme="monokai", line_numbers=False) - console.print(syntax) - - -@tools_group.command() -@click.argument("tool_name") -@click.argument("args", nargs=-1) -@click.option("--json", "-j", is_flag=True, help="Output as JSON") -@click.pass_context -async def run(ctx, tool_name: str, args: tuple, json: bool): - """Run a tool directly.""" - try: - from hanzo_tools import run_tool - except ImportError: - console.print("[red]Error:[/red] hanzo-tools not installed") - return - - # Parse arguments - tool_args = {} - for arg in args: - if "=" in arg: - key, value = arg.split("=", 1) - tool_args[key] = value - else: - console.print(f"[red]Invalid argument format: {arg}[/red]") - console.print("Use: key=value") - return - - with console.status(f"Running {tool_name}..."): - try: - result = await run_tool(name=tool_name, args=tool_args) - - if json: - console.print_json(data=result) - else: - if isinstance(result, str): - console.print(result) - elif isinstance(result, dict): - for key, value in result.items(): - console.print(f"{key}: {value}") - else: - console.print(result) - - except Exception as e: - console.print(f"[red]Tool execution failed: {e}[/red]") - - -@tools_group.command() -@click.option("--check", is_flag=True, help="Check for updates only") -@click.pass_context -async def upgrade(ctx, check: bool): - """Upgrade all tools.""" - try: - from hanzo_tools import get_tool_registry - except ImportError: - console.print("[red]Error:[/red] hanzo-tools not installed") - return - - registry = get_tool_registry() - - with console.status("Checking for updates..."): - try: - updates = await registry.check_updates() - except Exception as e: - console.print(f"[red]Failed to check updates: {e}[/red]") - return - - if not updates: - console.print("[green]โœ“[/green] All tools are up to date") - return - - # Show available updates - table = Table(title="Available Updates") - table.add_column("Tool", style="cyan") - table.add_column("Current", style="yellow") - table.add_column("Latest", style="green") - table.add_column("Changes", style="white") - - for update in updates: - table.add_row( - update["name"], - update["current_version"], - update["latest_version"], - update.get("changelog_summary", ""), - ) - - console.print(table) - - if check: - return - - # Perform updates - if click.confirm(f"Update {len(updates)} tools?"): - for update in updates: - with console.status(f"Updating {update['name']}..."): - try: - await registry.update_tool(update["name"]) - console.print(f"[green]โœ“[/green] Updated {update['name']}") - except Exception as e: - console.print(f"[red]Failed to update {update['name']}: {e}[/red]") - - -@tools_group.command() -@click.argument("name") -@click.option("--template", "-t", help="Tool template to use") -@click.pass_context -async def create(ctx, name: str, template: str): - """Create a new custom tool.""" - try: - from hanzo_tools import create_tool_template - except ImportError: - console.print("[red]Error:[/red] hanzo-tools not installed") - return - - with console.status(f"Creating tool '{name}'..."): - try: - path = await create_tool_template(name=name, template=template or "basic") - - console.print(f"[green]โœ“[/green] Created tool template at: {path}") - console.print("\nNext steps:") - console.print("1. Edit the tool implementation") - console.print("2. Test with: hanzo tools run {name}") - console.print("3. Package with: hanzo tools package {name}") - - except Exception as e: - console.print(f"[red]Failed to create tool: {e}[/red]") diff --git a/pkg/hanzo/src/hanzo/commands/vector.py b/pkg/hanzo/src/hanzo/commands/vector.py deleted file mode 100644 index a1fd2d055..000000000 --- a/pkg/hanzo/src/hanzo/commands/vector.py +++ /dev/null @@ -1,665 +0,0 @@ -"""Hanzo Vector - Vector database CLI. - -Purpose-built vector database for AI/ML embeddings and similarity search. -""" - -import os -import json - -import click -import httpx -from rich import box -from rich.panel import Panel -from rich.table import Table - -from .base import check_response, service_request -from ..utils.output import console - -VECTOR_URL = os.getenv("HANZO_VECTOR_URL", "https://vector.hanzo.ai") - - -def _request(method: str, path: str, **kwargs) -> httpx.Response: - return service_request(VECTOR_URL, method, path, **kwargs) - - -@click.group(name="vector") -def vector_group(): - """Hanzo Vector - AI-native vector database. - - \b - Databases: - hanzo vector create # Create vector database - hanzo vector list # List databases - hanzo vector delete # Delete database - - \b - Collections: - hanzo vector collections # Manage collections - - \b - Data: - hanzo vector upsert # Insert/update vectors - hanzo vector query # Similarity search - hanzo vector delete-vectors # Delete vectors - - \b - Indexes: - hanzo vector indexes # Manage vector indexes - """ - pass - - -# ============================================================================ -# Database Management -# ============================================================================ - - -@vector_group.command(name="create") -@click.argument("name") -@click.option("--region", "-r", help="Region") -@click.option( - "--tier", - "-t", - type=click.Choice(["free", "standard", "dedicated"]), - default="standard", -) -@click.option( - "--metric", - "-m", - type=click.Choice(["cosine", "euclidean", "dotproduct"]), - default="cosine", -) -def vector_create(name: str, region: str, tier: str, metric: str): - """Create a vector database.""" - payload = {"name": name, "tier": tier, "default_metric": metric} - if region: - payload["region"] = region - - resp = _request("post", "/v1/databases", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Vector database '{name}' created") - console.print(f" ID: {data.get('id', '-')}") - console.print(f" Tier: {tier}") - console.print(f" Default metric: {metric}") - if region: - console.print(f" Region: {region}") - - -@vector_group.command(name="list") -def vector_list(): - """List vector databases.""" - resp = _request("get", "/v1/databases") - data = check_response(resp) - dbs = data.get("databases", data.get("items", [])) - - table = Table(title="Vector Databases", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Collections", style="green") - table.add_column("Vectors", style="yellow") - table.add_column("Dimensions", style="white") - table.add_column("Status", style="dim") - - for db in dbs: - status = db.get("status", "unknown") - style = "green" if status == "running" else "yellow" - table.add_row( - db.get("name", ""), - str(db.get("collection_count", 0)), - str(db.get("vector_count", 0)), - str(db.get("dimensions", "-")), - f"[{style}]โ— {status}[/{style}]", - ) - - console.print(table) - if not dbs: - console.print( - "[dim]No vector databases found. Create one with 'hanzo vector create'[/dim]" - ) - - -@vector_group.command(name="describe") -@click.argument("name") -def vector_describe(name: str): - """Show vector database details.""" - resp = _request("get", f"/v1/databases/{name}") - data = check_response(resp) - - status = data.get("status", "unknown") - style = "green" if status == "running" else "yellow" - - console.print( - Panel( - f"[cyan]Name:[/cyan] {data.get('name', name)}\n" - f"[cyan]Status:[/cyan] [{style}]โ— {status}[/{style}]\n" - f"[cyan]Collections:[/cyan] {data.get('collection_count', 0)}\n" - f"[cyan]Total vectors:[/cyan] {data.get('vector_count', 0):,}\n" - f"[cyan]Storage:[/cyan] {data.get('storage', '0 B')}\n" - f"[cyan]Default metric:[/cyan] {data.get('default_metric', 'cosine')}\n" - f"[cyan]Region:[/cyan] {data.get('region', '-')}\n" - f"[cyan]Endpoint:[/cyan] {data.get('endpoint', f'{VECTOR_URL}/{name}')}", - title="Vector Database Details", - border_style="cyan", - ) - ) - - -@vector_group.command(name="delete") -@click.argument("name") -@click.option("--force", "-f", is_flag=True) -def vector_delete(name: str, force: bool): - """Delete a vector database.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete vector database '{name}'?[/red]"): - return - - resp = _request("delete", f"/v1/databases/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Vector database '{name}' deleted") - - -# ============================================================================ -# Collections -# ============================================================================ - - -@vector_group.group() -def collections(): - """Manage vector collections.""" - pass - - -@collections.command(name="create") -@click.argument("name") -@click.option("--db", "-d", default="default", help="Database name") -@click.option("--dimension", "-dim", type=int, required=True, help="Vector dimension") -@click.option( - "--metric", "-m", type=click.Choice(["cosine", "euclidean", "dotproduct"]) -) -@click.option( - "--index-type", "-i", type=click.Choice(["hnsw", "ivf", "flat"]), default="hnsw" -) -def collections_create( - name: str, db: str, dimension: int, metric: str, index_type: str -): - """Create a vector collection. - - \b - Examples: - hanzo vector collections create embeddings --dim 1536 - hanzo vector collections create images --dim 512 --metric euclidean - """ - payload = {"name": name, "dimension": dimension, "index_type": index_type} - if metric: - payload["metric"] = metric - - resp = _request("post", f"/v1/databases/{db}/collections", json=payload) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Collection '{name}' created") - console.print(f" ID: {data.get('id', '-')}") - console.print(f" Dimension: {dimension}") - console.print(f" Index type: {index_type}") - if metric: - console.print(f" Metric: {metric}") - - -@collections.command(name="list") -@click.option("--db", "-d", default="default") -def collections_list(db: str): - """List collections.""" - resp = _request("get", f"/v1/databases/{db}/collections") - data = check_response(resp) - items = data.get("collections", data.get("items", [])) - - table = Table(title=f"Collections in '{db}'", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Dimension", style="white") - table.add_column("Vectors", style="green") - table.add_column("Metric", style="yellow") - table.add_column("Index", style="dim") - - for c in items: - table.add_row( - c.get("name", ""), - str(c.get("dimension", "-")), - str(c.get("vector_count", 0)), - c.get("metric", "cosine"), - c.get("index_type", "hnsw"), - ) - - console.print(table) - if not items: - console.print("[dim]No collections found[/dim]") - - -@collections.command(name="describe") -@click.argument("name") -@click.option("--db", "-d", default="default") -def collections_describe(name: str, db: str): - """Show collection details.""" - resp = _request("get", f"/v1/databases/{db}/collections/{name}") - data = check_response(resp) - - console.print( - Panel( - f"[cyan]Collection:[/cyan] {data.get('name', name)}\n" - f"[cyan]Dimension:[/cyan] {data.get('dimension', '-')}\n" - f"[cyan]Vectors:[/cyan] {data.get('vector_count', 0):,}\n" - f"[cyan]Metric:[/cyan] {data.get('metric', 'cosine')}\n" - f"[cyan]Index:[/cyan] {data.get('index_type', 'hnsw')}\n" - f"[cyan]Storage:[/cyan] {data.get('storage', '0 B')}", - title="Collection Details", - border_style="cyan", - ) - ) - - -@collections.command(name="delete") -@click.argument("name") -@click.option("--db", "-d", default="default") -@click.option("--force", "-f", is_flag=True) -def collections_delete(name: str, db: str, force: bool): - """Delete a collection.""" - if not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete collection '{name}'?[/red]"): - return - - resp = _request("delete", f"/v1/databases/{db}/collections/{name}") - check_response(resp) - console.print(f"[green]โœ“[/green] Collection '{name}' deleted") - - -# ============================================================================ -# Vector Operations -# ============================================================================ - - -@vector_group.command(name="upsert") -@click.option("--collection", "-c", required=True, help="Collection name") -@click.option("--db", "-d", default="default") -@click.option("--from", "source", required=True, help="Source: file, jsonl, parquet") -@click.option("--id-field", help="Field to use as ID") -@click.option("--vector-field", default="embedding", help="Field containing vectors") -@click.option( - "--embed", help="Embed text using model (e.g., openai:text-embedding-3-small)" -) -@click.option("--batch-size", "-b", default=100, help="Batch size") -def vector_upsert( - collection: str, - db: str, - source: str, - id_field: str, - vector_field: str, - embed: str, - batch_size: int, -): - """Upsert vectors into a collection. - - \b - Examples: - hanzo vector upsert -c docs --from embeddings.jsonl - hanzo vector upsert -c products --from data.jsonl --embed openai:text-embedding-3-small - """ - from pathlib import Path - - source_path = Path(source) - if not source_path.exists(): - raise click.ClickException(f"Source file not found: {source}") - - console.print(f"[cyan]Upserting into '{collection}' from '{source}'...[/cyan]") - - vectors = [] - with open(source_path) as f: - for line in f: - line = line.strip() - if line: - vectors.append(json.loads(line)) - - total = 0 - errors = 0 - for i in range(0, len(vectors), batch_size): - batch = vectors[i : i + batch_size] - payload = { - "vectors": batch, - "vector_field": vector_field, - } - if id_field: - payload["id_field"] = id_field - if embed: - payload["embed_model"] = embed - - resp = _request( - "post", f"/v1/databases/{db}/collections/{collection}/upsert", json=payload - ) - result = check_response(resp) - total += result.get("upserted", len(batch)) - errors += result.get("errors", 0) - console.print( - f" Batch {i // batch_size + 1}: {result.get('upserted', len(batch))} upserted" - ) - - console.print(f"[green]โœ“[/green] Upsert complete") - console.print(f" Vectors: {total}") - console.print(f" Errors: {errors}") - - -@vector_group.command(name="query") -@click.option("--collection", "-c", required=True, help="Collection name") -@click.option("--db", "-d", default="default") -@click.option("--text", "-t", help="Text to embed and search") -@click.option("--vector", "-v", help="Vector to search (JSON array)") -@click.option("--topk", "-k", default=10, help="Number of results") -@click.option("--filter", "-f", help="Metadata filter") -@click.option("--include-vectors", is_flag=True, help="Include vectors in results") -@click.option("--include-metadata", is_flag=True, default=True, help="Include metadata") -@click.option("--embed", help="Embedding model for text queries") -def vector_query( - collection: str, - db: str, - text: str, - vector: str, - topk: int, - filter: str, - include_vectors: bool, - include_metadata: bool, - embed: str, -): - """Query similar vectors. - - \b - Examples: - hanzo vector query -c docs -t "machine learning" -k 20 - hanzo vector query -c images -v "[0.1, 0.2, ...]" --filter "category=animals" - """ - if not text and not vector: - raise click.ClickException("Provide --text or --vector for query") - - payload = { - "top_k": topk, - "include_vectors": include_vectors, - "include_metadata": include_metadata, - } - if text: - payload["text"] = text - if vector: - payload["vector"] = json.loads(vector) - if filter: - payload["filter"] = filter - if embed: - payload["embed_model"] = embed - - resp = _request( - "post", f"/v1/databases/{db}/collections/{collection}/query", json=payload - ) - data = check_response(resp) - matches = data.get("matches", data.get("results", [])) - - table = Table(title=f"Results from '{collection}'", box=box.ROUNDED) - table.add_column("#", style="dim") - table.add_column("ID", style="cyan") - table.add_column("Score", style="green") - table.add_column("Metadata", style="white") - - for i, m in enumerate(matches, 1): - metadata = m.get("metadata", {}) - meta_str = json.dumps(metadata, default=str)[:80] if metadata else "-" - table.add_row( - str(i), - str(m.get("id", ""))[:24], - f"{m.get('score', 0):.4f}", - meta_str, - ) - - console.print(table) - if not matches: - console.print("[dim]No results found[/dim]") - - -@vector_group.command(name="fetch") -@click.argument("ids", nargs=-1, required=True) -@click.option("--collection", "-c", required=True) -@click.option("--db", "-d", default="default") -@click.option("--include-vectors", is_flag=True) -def vector_fetch(ids: tuple, collection: str, db: str, include_vectors: bool): - """Fetch vectors by ID.""" - payload = {"ids": list(ids), "include_vectors": include_vectors} - - resp = _request( - "post", f"/v1/databases/{db}/collections/{collection}/fetch", json=payload - ) - data = check_response(resp) - vectors = data.get("vectors", []) - - for v in vectors: - console.print( - Panel( - f"[cyan]ID:[/cyan] {v.get('id', '-')}\n" - f"[cyan]Metadata:[/cyan] {json.dumps(v.get('metadata', {}), indent=2, default=str)}", - border_style="cyan", - ) - ) - if include_vectors and v.get("values"): - dims = v["values"][:5] - console.print( - f" Vector: [{', '.join(f'{d:.4f}' for d in dims)}, ...] ({len(v['values'])} dims)" - ) - - if not vectors: - console.print("[dim]No vectors found[/dim]") - - -@vector_group.command(name="delete-vectors") -@click.option("--collection", "-c", required=True) -@click.option("--db", "-d", default="default") -@click.option("--ids", help="Comma-separated IDs to delete") -@click.option("--filter", "-f", help="Delete by filter") -@click.option("--all", "delete_all", is_flag=True, help="Delete all vectors") -@click.option("--force", is_flag=True) -def vector_delete_vectors( - collection: str, db: str, ids: str, filter: str, delete_all: bool, force: bool -): - """Delete vectors from a collection.""" - if delete_all and not force: - from rich.prompt import Confirm - - if not Confirm.ask(f"[red]Delete ALL vectors from '{collection}'?[/red]"): - return - - payload = {} - if ids: - payload["ids"] = [i.strip() for i in ids.split(",")] - if filter: - payload["filter"] = filter - if delete_all: - payload["delete_all"] = True - - resp = _request( - "post", f"/v1/databases/{db}/collections/{collection}/delete", json=payload - ) - data = check_response(resp) - console.print( - f"[green]โœ“[/green] Deleted {data.get('deleted', 0)} vector(s) from '{collection}'" - ) - - -# ============================================================================ -# Indexes -# ============================================================================ - - -@vector_group.group() -def indexes(): - """Manage vector indexes.""" - pass - - -@indexes.command(name="list") -@click.argument("collection") -@click.option("--db", "-d", default="default") -def indexes_list(collection: str, db: str): - """List indexes on a collection.""" - resp = _request("get", f"/v1/databases/{db}/collections/{collection}/indexes") - data = check_response(resp) - items = data.get("indexes", []) - - table = Table(title=f"Indexes on '{collection}'", box=box.ROUNDED) - table.add_column("Name", style="cyan") - table.add_column("Type", style="white") - table.add_column("Metric", style="yellow") - table.add_column("Parameters", style="dim") - - for idx in items: - params = idx.get("parameters", {}) - param_str = ", ".join(f"{k}={v}" for k, v in params.items()) if params else "-" - table.add_row( - idx.get("name", "-"), - idx.get("type", "-"), - idx.get("metric", "-"), - param_str, - ) - - console.print(table) - if not items: - console.print("[dim]No indexes found[/dim]") - - -@indexes.command(name="create") -@click.argument("collection") -@click.option("--db", "-d", default="default") -@click.option("--name", "-n", help="Index name") -@click.option( - "--type", "idx_type", type=click.Choice(["hnsw", "ivf", "flat"]), default="hnsw" -) -@click.option( - "--metric", "-m", type=click.Choice(["cosine", "euclidean", "dotproduct"]) -) -@click.option("--hnsw-m", type=int, default=16, help="HNSW M parameter") -@click.option("--hnsw-ef", type=int, default=200, help="HNSW efConstruction") -@click.option("--ivf-nlist", type=int, default=100, help="IVF number of lists") -def indexes_create( - collection: str, - db: str, - name: str, - idx_type: str, - metric: str, - hnsw_m: int, - hnsw_ef: int, - ivf_nlist: int, -): - """Create a vector index. - - \b - Examples: - hanzo vector indexes create embeddings --type hnsw --hnsw-m 32 - hanzo vector indexes create images --type ivf --ivf-nlist 256 - """ - payload = {"type": idx_type} - if name: - payload["name"] = name - if metric: - payload["metric"] = metric - - if idx_type == "hnsw": - payload["parameters"] = {"m": hnsw_m, "ef_construction": hnsw_ef} - elif idx_type == "ivf": - payload["parameters"] = {"nlist": ivf_nlist} - - resp = _request( - "post", f"/v1/databases/{db}/collections/{collection}/indexes", json=payload - ) - data = check_response(resp) - - console.print(f"[green]โœ“[/green] Index created on '{collection}'") - console.print(f" Name: {data.get('name', name or 'default')}") - console.print(f" Type: {idx_type}") - if idx_type == "hnsw": - console.print(f" M: {hnsw_m}, efConstruction: {hnsw_ef}") - elif idx_type == "ivf": - console.print(f" nlist: {ivf_nlist}") - - -@indexes.command(name="rebuild") -@click.argument("collection") -@click.option("--db", "-d", default="default") -@click.option("--name", "-n", help="Specific index name") -def indexes_rebuild(collection: str, db: str, name: str): - """Rebuild vector index.""" - payload = {} - if name: - payload["index_name"] = name - - console.print(f"[cyan]Rebuilding index for '{collection}'...[/cyan]") - resp = _request( - "post", - f"/v1/databases/{db}/collections/{collection}/indexes/rebuild", - json=payload, - ) - data = check_response(resp) - console.print(f"[green]โœ“[/green] Index rebuilt ({data.get('duration', '-')})") - - -# ============================================================================ -# Admin -# ============================================================================ - - -@vector_group.command(name="stats") -@click.option("--db", "-d", default="default") -def vector_stats(db: str): - """Show database statistics.""" - resp = _request("get", f"/v1/databases/{db}/stats") - data = check_response(resp) - - console.print( - Panel( - f"[cyan]Database:[/cyan] {data.get('name', db)}\n" - f"[cyan]Collections:[/cyan] {data.get('collection_count', 0)}\n" - f"[cyan]Total vectors:[/cyan] {data.get('vector_count', 0):,}\n" - f"[cyan]Storage:[/cyan] {data.get('storage', '0 B')}\n" - f"[cyan]Queries/day:[/cyan] {data.get('queries_per_day', 0):,}\n" - f"[cyan]Avg latency:[/cyan] {data.get('avg_latency_ms', 0)}ms", - title="Vector Statistics", - border_style="cyan", - ) - ) - - -@vector_group.command(name="bind") -@click.option("--service", "-s", required=True, help="Service to bind to") -@click.option("--db", "-d", default="default") -@click.option("--env", "-e", help="Environment") -def vector_bind(service: str, db: str, env: str): - """Bind database to a service.""" - payload = {"service": service, "database": db} - if env: - payload["environment"] = env - - resp = _request("post", "/v1/bindings", json=payload) - check_response(resp) - console.print(f"[green]โœ“[/green] Bound '{db}' to service '{service}'") - if env: - console.print(f" Environment: {env}") - - -@vector_group.command(name="backup") -@click.option("--db", "-d", default="default") -@click.option("--collection", "-c", help="Specific collection") -@click.option("--output", "-o", help="Output location") -def vector_backup(db: str, collection: str, output: str): - """Backup vector data.""" - payload = {"database": db} - if collection: - payload["collection"] = collection - if output: - payload["output"] = output - - resp = _request("post", "/v1/backups", json=payload) - data = check_response(resp) - console.print(f"[green]โœ“[/green] Backup created") - console.print(f" Backup ID: {data.get('id', '-')}") - console.print(f" Size: {data.get('size', '-')}") diff --git a/pkg/hanzo/src/hanzo/dev.py b/pkg/hanzo/src/hanzo/dev.py deleted file mode 100644 index 948b16b49..000000000 --- a/pkg/hanzo/src/hanzo/dev.py +++ /dev/null @@ -1,2998 +0,0 @@ -"""Hanzo Dev - System 2 Thinking Meta-AI for Managing Claude Code Runtime. - -This module provides a sophisticated orchestration layer that: -1. Acts as a System 2 thinking agent (deliberative, analytical) -2. Manages Claude Code runtime lifecycle -3. Provides persistence and recovery mechanisms -4. Includes health checks and auto-restart capabilities -5. Integrates with REPL for interactive control -""" - -import os -import sys -import json -import time -import shutil -import signal -import asyncio -import logging -import subprocess -from enum import Enum -from typing import Any, Dict, List, Union, Callable, Optional -from pathlib import Path -from datetime import datetime -from dataclasses import asdict, dataclass - -from rich.live import Live -from rich.panel import Panel -from rich.table import Table -from rich.layout import Layout -from rich.console import Console -from rich.progress import Progress, TextColumn, SpinnerColumn - -# Setup logging first -logger = logging.getLogger(__name__) -console = Console() - -# Import GRPO for training-free learning -try: - from hanzoai.grpo import ( - ExperienceManager, - EnhancedTrajectory, - EnhancedDeepSeekAdapter, - EnhancedSemanticExtractor, - ) - - GRPO_AVAILABLE = True -except ImportError: - GRPO_AVAILABLE = False - logger.warning("hanzoai.grpo not available - GRPO learning disabled") - -# Import hanzo-network for agent orchestration -try: - from hanzo_network import ( - LOCAL_COMPUTE_AVAILABLE, - Agent, - Router, - Network, - ModelConfig, - NetworkState, - ModelProvider, - DistributedNetwork, - create_agent, - create_router, - create_network, - create_routing_agent, - create_distributed_network, - ) - - NETWORK_AVAILABLE = True -except ImportError: - NETWORK_AVAILABLE = False - logger.warning("hanzo-network not available, using basic orchestration") - - # Provide fallback implementations - class Agent: - """Fallback Agent class when hanzo-network is not available.""" - - def __init__(self, name: str, model: str = "gpt-4", **kwargs): - self.name = name - self.model = model - self.config = kwargs - - class Network: - """Fallback Network class.""" - - def __init__(self): - self.agents = [] - - class Router: - """Fallback Router class.""" - - def __init__(self): - pass - - class NetworkState: - """Fallback NetworkState class.""" - - pass - - class ModelConfig: - """Fallback ModelConfig class.""" - - def __init__(self, **kwargs): - # Accept all kwargs and store as attributes - for key, value in kwargs.items(): - setattr(self, key, value) - - class ModelProvider: - """Fallback ModelProvider class.""" - - OPENAI = "openai" - ANTHROPIC = "anthropic" - LOCAL = "local" - - LOCAL_COMPUTE_AVAILABLE = False - - -class AgentState(Enum): - """State of an AI agent.""" - - IDLE = "idle" - THINKING = "thinking" # System 2 deliberation - EXECUTING = "executing" - STUCK = "stuck" - CRASHED = "crashed" - RECOVERING = "recovering" - - -class RuntimeState(Enum): - """State of Claude Code runtime.""" - - NOT_STARTED = "not_started" - STARTING = "starting" - RUNNING = "running" - RESPONDING = "responding" - NOT_RESPONDING = "not_responding" - CRASHED = "crashed" - RESTARTING = "restarting" - - -@dataclass -class AgentContext: - """Context for agent decision making.""" - - task: str - goal: str - constraints: List[str] - success_criteria: List[str] - max_attempts: int = 3 - timeout_seconds: int = 300 - checkpoint_interval: int = 60 - - -@dataclass -class RuntimeHealth: - """Health status of Claude Code runtime.""" - - state: RuntimeState - last_response: datetime - response_time_ms: float - memory_usage_mb: float - cpu_percent: float - error_count: int - restart_count: int - - -@dataclass -class ThinkingResult: - """Result of System 2 thinking process.""" - - decision: str - reasoning: List[str] - confidence: float - alternatives: List[str] - risks: List[str] - next_steps: List[str] - - -class HanzoDevOrchestrator: - """Main orchestrator for Hanzo Dev System 2 thinking.""" - - def __init__( - self, - workspace_dir: str = "~/.hanzo/dev", - claude_code_path: Optional[str] = None, - enable_grpo: bool = True, - ): - """Initialize the orchestrator. - - Args: - workspace_dir: Directory for persistence and checkpoints - claude_code_path: Path to Claude Code executable - enable_grpo: Enable Training-Free GRPO learning (default: True) - """ - self.workspace_dir = Path(workspace_dir).expanduser() - self.workspace_dir.mkdir(parents=True, exist_ok=True) - - self.claude_code_path = claude_code_path or self._find_claude_code() - self.state_file = self.workspace_dir / "orchestrator_state.json" - self.checkpoint_dir = self.workspace_dir / "checkpoints" - self.checkpoint_dir.mkdir(exist_ok=True) - - self.agent_state = AgentState.IDLE - self.runtime_health = RuntimeHealth( - state=RuntimeState.NOT_STARTED, - last_response=datetime.now(), - response_time_ms=0, - memory_usage_mb=0, - cpu_percent=0, - error_count=0, - restart_count=0, - ) - - self.current_context: Optional[AgentContext] = None - self.claude_process: Optional[subprocess.Popen] = None - self.thinking_history: List[ThinkingResult] = [] - self._shutdown = False - - # Initialize GRPO for training-free learning - self.grpo_enabled = enable_grpo and GRPO_AVAILABLE - if self.grpo_enabled: - grpo_dir = self.workspace_dir / "grpo" - grpo_dir.mkdir(exist_ok=True) - - self.experience_manager = ExperienceManager( - str(grpo_dir / "experience_library.json") - ) - - # Initialize with DeepSeek if API key available - deepseek_key = os.getenv("DEEPSEEK_API_KEY") - if deepseek_key: - from hanzoai.grpo import EnhancedLLMClient - - self.grpo_llm = EnhancedLLMClient(api_key=deepseek_key) - self.grpo_extractor = EnhancedSemanticExtractor( - llm_client=self.grpo_llm, - cache_dir=str(grpo_dir / "cache"), - ) - logger.info("GRPO learning enabled with DeepSeek") - else: - self.grpo_enabled = False - logger.warning("GRPO disabled: DEEPSEEK_API_KEY not set") - else: - logger.info("GRPO learning disabled") - - def _find_claude_code(self) -> str: - """Find Claude Code executable.""" - import shutil - - # Check common locations - possible_paths = [ - "/usr/local/bin/claude", - "/opt/claude/claude", - "~/.local/bin/claude", - "claude", # Rely on PATH - ] - - for path in possible_paths: - expanded = Path(path).expanduser() - if expanded.exists() or ( - path == "claude" and shutil.which(path) is not None - ): - return str(expanded) if expanded.exists() else path - - raise RuntimeError("Claude Code not found. Please specify path.") - - async def think(self, problem: str, context: Dict[str, Any]) -> ThinkingResult: - """System 2 thinking process - deliberative and analytical. - - This implements slow, deliberate thinking: - 1. Analyze the problem thoroughly - 2. Consider multiple approaches - 3. Evaluate risks and trade-offs - 4. Make a reasoned decision - """ - self.agent_state = AgentState.THINKING - console.print("[yellow]๐Ÿค” Engaging System 2 thinking...[/yellow]") - - # Simulate deep thinking process - reasoning = [] - alternatives = [] - risks = [] - - # Step 1: Problem decomposition - reasoning.append(f"Decomposing problem: {problem}") - sub_problems = self._decompose_problem(problem) - reasoning.append(f"Identified {len(sub_problems)} sub-problems") - - # Step 2: Generate alternatives - for sub in sub_problems: - alt = f"Approach for '{sub}': {self._generate_approach(sub, context)}" - alternatives.append(alt) - - # Step 3: Risk assessment - risks = self._assess_risks(problem, alternatives, context) - - # Step 4: Decision synthesis - decision = self._synthesize_decision(problem, alternatives, risks, context) - confidence = self._calculate_confidence(decision, risks) - - # Step 5: Plan next steps - next_steps = self._plan_next_steps(decision, context) - - result = ThinkingResult( - decision=decision, - reasoning=reasoning, - confidence=confidence, - alternatives=alternatives, - risks=risks, - next_steps=next_steps, - ) - - self.thinking_history.append(result) - self.agent_state = AgentState.IDLE - - return result - - def _decompose_problem(self, problem: str) -> List[str]: - """Decompose a problem into sub-problems.""" - # Simple heuristic decomposition - sub_problems = [] - - # Check for common patterns - if "and" in problem.lower(): - parts = problem.split(" and ") - sub_problems.extend(parts) - - if "then" in problem.lower(): - parts = problem.split(" then ") - sub_problems.extend(parts) - - if not sub_problems: - sub_problems = [problem] - - return sub_problems - - def _generate_approach(self, sub_problem: str, context: Dict[str, Any]) -> str: - """Generate an approach for a sub-problem.""" - # Heuristic approach generation - if "stuck" in sub_problem.lower(): - return "Analyze error logs, restart with verbose mode, try alternative approach" - elif "slow" in sub_problem.lower(): - return "Profile performance, optimize bottlenecks, consider caching" - elif "error" in sub_problem.lower(): - return "Examine stack trace, validate inputs, add error handling" - else: - return "Execute standard workflow with monitoring" - - def _assess_risks( - self, problem: str, alternatives: List[str], context: Dict[str, Any] - ) -> List[str]: - """Assess risks of different approaches.""" - risks = [] - - if "restart" in str(alternatives).lower(): - risks.append("Restarting may lose current state") - - if "force" in str(alternatives).lower(): - risks.append("Forcing operations may cause data corruption") - - if context.get("error_count", 0) > 5: - risks.append("High error rate indicates systemic issue") - - return risks - - def _synthesize_decision( - self, - problem: str, - alternatives: List[str], - risks: List[str], - context: Dict[str, Any], - ) -> str: - """Synthesize a decision from analysis.""" - if len(risks) > 2: - return ( - "Proceed cautiously with incremental approach and rollback capability" - ) - elif alternatives: - return f"Execute primary approach: {alternatives[0]}" - else: - return "Gather more information before proceeding" - - def _calculate_confidence(self, decision: str, risks: List[str]) -> float: - """Calculate confidence in decision.""" - base_confidence = 0.8 - risk_penalty = len(risks) * 0.1 - return max(0.2, min(1.0, base_confidence - risk_penalty)) - - def _plan_next_steps(self, decision: str, context: Dict[str, Any]) -> List[str]: - """Plan concrete next steps.""" - steps = [] - - if "cautiously" in decision.lower(): - steps.append("Create checkpoint before proceeding") - steps.append("Enable verbose logging") - - steps.append("Execute decision with monitoring") - steps.append("Validate results against success criteria") - steps.append("Report outcome and update state") - - return steps - - async def start_claude_runtime(self, resume: bool = False) -> bool: - """Start or resume Claude Code runtime. - - Args: - resume: Whether to resume from checkpoint - """ - if self.claude_process and self.claude_process.poll() is None: - console.print("[yellow]Claude Code already running[/yellow]") - return True - - self.runtime_health.state = RuntimeState.STARTING - console.print("[cyan]Starting Claude Code runtime...[/cyan]") - - try: - # Load checkpoint if resuming - checkpoint_file = None - if resume: - checkpoint_file = self._get_latest_checkpoint() - if checkpoint_file: - console.print( - f"[green]Resuming from checkpoint: {checkpoint_file.name}[/green]" - ) - - # Prepare command - cmd = [self.claude_code_path] - if checkpoint_file: - cmd.extend(["--resume", str(checkpoint_file)]) - - # Start process with proper signal handling - self.claude_process = subprocess.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - stdin=subprocess.PIPE, - text=True, - preexec_fn=os.setsid if hasattr(os, "setsid") else None, - ) - - # Wait for startup - await asyncio.sleep(2) - - if self.claude_process.poll() is None: - self.runtime_health.state = RuntimeState.RUNNING - self.runtime_health.last_response = datetime.now() - console.print("[green]โœ“ Claude Code runtime started[/green]") - return True - else: - self.runtime_health.state = RuntimeState.CRASHED - console.print("[red]โœ— Claude Code failed to start[/red]") - return False - - except Exception as e: - console.print(f"[red]Error starting Claude Code: {e}[/red]") - self.runtime_health.state = RuntimeState.CRASHED - return False - - def _get_latest_checkpoint(self) -> Optional[Path]: - """Get the latest checkpoint file.""" - checkpoints = list(self.checkpoint_dir.glob("checkpoint_*.json")) - if checkpoints: - return max(checkpoints, key=lambda p: p.stat().st_mtime) - return None - - async def health_check(self) -> bool: - """Check health of Claude Code runtime.""" - if not self.claude_process: - self.runtime_health.state = RuntimeState.NOT_STARTED - return False - - # Check if process is alive - if self.claude_process.poll() is not None: - self.runtime_health.state = RuntimeState.CRASHED - self.runtime_health.error_count += 1 - return False - - # Check process health via stdout/stderr activity - try: - start_time = time.time() - - # Check process is alive and responsive - if self.claude_process.stdout: - # Non-blocking check for output - await asyncio.sleep(0.1) - - response_time = (time.time() - start_time) * 1000 - self.runtime_health.response_time_ms = response_time - self.runtime_health.last_response = datetime.now() - - if response_time > 5000: - self.runtime_health.state = RuntimeState.NOT_RESPONDING - return False - else: - self.runtime_health.state = RuntimeState.RUNNING - return True - - except Exception as e: - logger.error(f"Health check failed: {e}") - self.runtime_health.state = RuntimeState.NOT_RESPONDING - self.runtime_health.error_count += 1 - return False - - async def restart_if_needed(self) -> bool: - """Restart Claude Code if it's stuck or crashed.""" - if self.runtime_health.state in [ - RuntimeState.CRASHED, - RuntimeState.NOT_RESPONDING, - ]: - console.print("[yellow]Claude Code needs restart...[/yellow]") - - # Kill existing process - if self.claude_process: - try: - if hasattr(os, "killpg"): - os.killpg(os.getpgid(self.claude_process.pid), signal.SIGTERM) - else: - self.claude_process.terminate() - await asyncio.sleep(2) - if self.claude_process.poll() is None: - self.claude_process.kill() - except Exception: - pass - - self.runtime_health.restart_count += 1 - self.runtime_health.state = RuntimeState.RESTARTING - - # Start with resume - return await self.start_claude_runtime(resume=True) - - return True - - async def create_checkpoint(self, name: Optional[str] = None) -> Path: - """Create a checkpoint of current state.""" - checkpoint_name = name or f"checkpoint_{int(time.time())}" - checkpoint_file = self.checkpoint_dir / f"{checkpoint_name}.json" - - checkpoint_data = { - "timestamp": datetime.now().isoformat(), - "agent_state": self.agent_state.value, - "runtime_health": asdict(self.runtime_health), - "current_context": ( - asdict(self.current_context) if self.current_context else None - ), - "thinking_history": [ - asdict(t) for t in self.thinking_history[-10:] - ], # Last 10 - } - - with open(checkpoint_file, "w") as f: - json.dump(checkpoint_data, f, indent=2, default=str) - - console.print(f"[green]โœ“ Checkpoint saved: {checkpoint_file.name}[/green]") - return checkpoint_file - - async def restore_checkpoint(self, checkpoint_file: Path) -> bool: - """Restore from a checkpoint.""" - try: - with open(checkpoint_file, "r") as f: - data = json.load(f) - - self.agent_state = AgentState(data["agent_state"]) - # Restore other state as needed - - console.print( - f"[green]โœ“ Restored from checkpoint: {checkpoint_file.name}[/green]" - ) - return True - except Exception as e: - console.print(f"[red]Failed to restore checkpoint: {e}[/red]") - return False - - async def monitor_loop(self): - """Main monitoring loop.""" - console.print("[cyan]Starting monitoring loop...[/cyan]") - - while not self._shutdown: - try: - # Health check - healthy = await self.health_check() - - if not healthy: - console.print( - f"[yellow]Health check failed. State: {self.runtime_health.state.value}[/yellow]" - ) - - # Use System 2 thinking to decide what to do - thinking_result = await self.think( - f"Claude Code is {self.runtime_health.state.value}", - {"health": asdict(self.runtime_health)}, - ) - - console.print(f"[cyan]Decision: {thinking_result.decision}[/cyan]") - console.print( - f"[cyan]Confidence: {thinking_result.confidence:.2f}[/cyan]" - ) - - # Execute decision - if thinking_result.confidence > 0.6: - await self.restart_if_needed() - - # Create periodic checkpoints - if int(time.time()) % 300 == 0: # Every 5 minutes - await self.create_checkpoint() - - await asyncio.sleep(10) # Check every 10 seconds - - except Exception as e: - logger.error(f"Monitor loop error: {e}") - await asyncio.sleep(10) - - async def execute_task(self, context: AgentContext) -> bool: - """Execute a task with System 2 oversight. - - Args: - context: The task context - """ - self.current_context = context - self.agent_state = AgentState.EXECUTING - - console.print(f"[cyan]Executing task: {context.task}[/cyan]") - console.print(f"[cyan]Goal: {context.goal}[/cyan]") - - attempts = 0 - while attempts < context.max_attempts: - attempts += 1 - console.print(f"[yellow]Attempt {attempts}/{context.max_attempts}[/yellow]") - - try: - # Start Claude if needed - if self.runtime_health.state != RuntimeState.RUNNING: - await self.start_claude_runtime(resume=attempts > 1) - - # Execute task via Claude Code subprocess - start_time = time.time() - - # Wait for Claude process to complete task - await asyncio.sleep(2) # Initial polling delay - - # Check success criteria - success = self._evaluate_success(context) - - if success: - console.print("[green]โœ“ Task completed successfully[/green]") - self.agent_state = AgentState.IDLE - return True - else: - console.print("[yellow]Task not yet complete[/yellow]") - - # Use System 2 thinking to decide next action - thinking_result = await self.think( - f"Task '{context.task}' incomplete after attempt {attempts}", - {"context": asdict(context), "attempts": attempts}, - ) - - if thinking_result.confidence < 0.4: - console.print("[red]Low confidence, aborting task[/red]") - break - - except asyncio.TimeoutError: - console.print("[red]Task timed out[/red]") - self.agent_state = AgentState.STUCK - - except Exception as e: - console.print(f"[red]Task error: {e}[/red]") - self.runtime_health.error_count += 1 - - self.agent_state = AgentState.IDLE - return False - - def _evaluate_success(self, context: AgentContext) -> bool: - """Evaluate if success criteria are met based on context.""" - # Check if task has explicit success criteria - if context.success_criteria: - # Would analyze Claude's output against criteria - return False # Requires thinking to verify - return False # No criteria = incomplete - - def shutdown(self): - """Shutdown the orchestrator.""" - self._shutdown = True - - if self.claude_process: - try: - self.claude_process.terminate() - self.claude_process.wait(timeout=5) - except Exception: - self.claude_process.kill() - - console.print("[green]โœ“ Orchestrator shutdown complete[/green]") - - async def learn_from_interactions( - self, - query: str, - responses: List[str], - rewards: List[float], - groundtruth: Optional[str] = None, - ) -> int: - """Learn from agent interactions using Training-Free GRPO. - - Args: - query: The task/query that was processed - responses: List of agent responses (G trajectories) - rewards: Reward scores for each response (0.0 to 1.0) - groundtruth: Optional ground truth answer for evaluation - - Returns: - Number of experiences learned - """ - if not self.grpo_enabled: - logger.debug("GRPO learning disabled, skipping") - return 0 - - try: - console.print("[cyan]๐Ÿง  Learning from interactions with GRPO...[/cyan]") - - # Create trajectories from interactions - trajectories = [ - EnhancedTrajectory( - query=query, - output=response, - reward=reward, - groundtruth=groundtruth, - ) - for response, reward in zip(responses, rewards, strict=False) - ] - - # Stage 1: Summarize trajectories - summarized = self.grpo_extractor.summarize_trajectories( - trajectories, use_groundtruth=(groundtruth is not None) - ) - - # Stage 2: Extract group advantages - advantages = self.grpo_extractor.extract_group_advantages( - summarized, - self.experience_manager.experiences, - use_groundtruth=(groundtruth is not None), - ) - - # Stage 3: Consolidate into experience library - operations = self.grpo_extractor.consolidate_batch_experiences( - advantages, self.experience_manager.experiences - ) - - # Apply operations to experience library - experiences_before = len(self.experience_manager.experiences) - self.experience_manager.apply_operations(operations) - experiences_after = len(self.experience_manager.experiences) - - # Save updated library - self.experience_manager.save( - str(self.workspace_dir / "grpo" / "experience_library.json") - ) - - learned_count = experiences_after - experiences_before - console.print( - f"[green]โœ“ Learned {learned_count} new experiences (total: {experiences_after})[/green]" - ) - - return learned_count - - except Exception as e: - logger.error(f"GRPO learning failed: {e}") - console.print(f"[yellow]โš  GRPO learning failed: {e}[/yellow]") - return 0 - - def get_relevant_experiences(self, query: str, top_k: int = 5) -> List[str]: - """Retrieve relevant experiences from the library for a query. - - Args: - query: The current task/query - top_k: Number of top experiences to retrieve - - Returns: - List of relevant experience strings - """ - if not self.grpo_enabled: - return [] - - # Simple keyword matching for now - # In production, would use semantic similarity - query_lower = query.lower() - relevant = [] - - for exp in self.experience_manager.experiences: - # Score based on keyword overlap - exp_lower = exp.lower() - if any(word in exp_lower for word in query_lower.split()): - relevant.append(exp) - - return relevant[:top_k] - - -class HanzoDevREPL: - """REPL interface for driving Hanzo Dev orchestrator.""" - - def __init__(self, orchestrator: HanzoDevOrchestrator): - self.orchestrator = orchestrator - self.commands = { - "start": self.cmd_start, - "stop": self.cmd_stop, - "restart": self.cmd_restart, - "status": self.cmd_status, - "think": self.cmd_think, - "execute": self.cmd_execute, - "checkpoint": self.cmd_checkpoint, - "restore": self.cmd_restore, - "monitor": self.cmd_monitor, - "help": self.cmd_help, - "exit": self.cmd_exit, - } - - # Initialize memory manager - from .memory_manager import MemoryManager - - workspace = getattr(orchestrator, "workspace_dir", "/tmp/hanzo") - self.memory_manager = MemoryManager(workspace) - - async def run(self): - """Run the REPL.""" - from rich.box import Box - from rich.text import Text - from rich.align import Align - from rich.panel import Panel - from rich.console import Group - from prompt_toolkit import prompt - from prompt_toolkit.styles import Style - - # Define Claude-like style for prompt_toolkit - claude_style = Style.from_dict( - { - "": "#333333", # Default text color - "prompt": "#666666", # Gray prompt arrow - } - ) - - # Use a predefined box style that's similar to Claude - from rich.box import ROUNDED - - LIGHT_GRAY_BOX = ROUNDED - - # Header - console.print() - console.print( - Panel( - "[bold cyan]Hanzo Dev - AI Chat[/bold cyan]\n" - "[dim]Chat naturally or use /commands โ€ข Type /help for available commands[/dim]", - box=LIGHT_GRAY_BOX, - style="dim white", - padding=(0, 1), - ) - ) - console.print() - - # Check for available API keys and show status - from .fallback_handler import FallbackHandler - - handler = FallbackHandler() - if not handler.fallback_order: - console.print("[yellow]โš ๏ธ No API keys detected[/yellow]") - console.print( - "[dim]Set OPENAI_API_KEY or ANTHROPIC_API_KEY to enable AI[/dim]" - ) - console.print() - else: - primary = handler.fallback_order[0][1] - console.print(f"[green]โœ… Using {primary} for AI responses[/green]") - console.print() - - while True: - try: - # Simple prompt without box borders to avoid rendering issues - try: - # Add spacing to prevent UI cutoff at bottom - user_input = await asyncio.get_event_loop().run_in_executor( - None, - input, - "โ€บ ", # Clean prompt - ) - console.print() # Add spacing after input - - except EOFError: - console.print() # New line before exit - break - except KeyboardInterrupt: - console.print("\n[yellow]Interrupted. Exiting...[/yellow]") - break - - if not user_input: - continue - - # Check for special commands - if user_input.startswith("/"): - # Handle slash commands like Claude Desktop - parts = user_input[1:].strip().split(maxsplit=1) - cmd = parts[0].lower() - args = parts[1] if len(parts) > 1 else "" - - if cmd in self.commands: - await self.commands[cmd](args) - else: - console.print(f"[yellow]Unknown command: /{cmd}[/yellow]") - console.print("Type /help for available commands") - - elif user_input.startswith("#"): - # Handle memory/context commands - from .memory_manager import handle_memory_command - - handled = handle_memory_command( - user_input, self.memory_manager, console - ) - if not handled: - console.print( - "[yellow]Unknown memory command. Use #memory help[/yellow]" - ) - - else: - # Natural chat - send directly to AI agents - await self.chat_with_agents(user_input) - - except KeyboardInterrupt: - console.print("\n[yellow]Interrupted. Exiting...[/yellow]") - break - except Exception as e: - console.print(f"[red]Error: {e}[/red]") - - async def cmd_start(self, args: str): - """Start Claude Code runtime.""" - resume = "--resume" in args - success = await self.orchestrator.start_claude_runtime(resume=resume) - if success: - console.print("[green]Runtime started successfully[/green]") - else: - console.print("[red]Failed to start runtime[/red]") - - async def cmd_stop(self, args: str): - """Stop Claude Code runtime.""" - if self.orchestrator.claude_process: - self.orchestrator.claude_process.terminate() - console.print("[yellow]Runtime stopped[/yellow]") - else: - console.print("[yellow]Runtime not running[/yellow]") - - async def cmd_restart(self, args: str): - """Restart Claude Code runtime.""" - await self.cmd_stop("") - await asyncio.sleep(1) - await self.cmd_start("--resume") - - async def cmd_status(self, args: str): - """Show current status.""" - table = Table(title="Hanzo Dev Status") - table.add_column("Property", style="cyan") - table.add_column("Value", style="white") - - table.add_row("Agent State", self.orchestrator.agent_state.value) - table.add_row("Runtime State", self.orchestrator.runtime_health.state.value) - table.add_row( - "Last Response", str(self.orchestrator.runtime_health.last_response) - ) - table.add_row( - "Response Time", - f"{self.orchestrator.runtime_health.response_time_ms:.2f}ms", - ) - table.add_row("Error Count", str(self.orchestrator.runtime_health.error_count)) - table.add_row( - "Restart Count", str(self.orchestrator.runtime_health.restart_count) - ) - - console.print(table) - - async def cmd_think(self, args: str): - """Trigger System 2 thinking.""" - if not args: - console.print("[red]Usage: think [/red]") - return - - result = await self.orchestrator.think(args, {}) - - console.print(f"\n[bold cyan]Thinking Result:[/bold cyan]") - console.print(f"Decision: {result.decision}") - console.print(f"Confidence: {result.confidence:.2f}") - console.print(f"Reasoning: {', '.join(result.reasoning)}") - console.print(f"Risks: {', '.join(result.risks)}") - console.print(f"Next Steps: {', '.join(result.next_steps)}") - - async def cmd_execute(self, args: str): - """Execute a task.""" - if not args: - console.print("[red]Usage: execute [/red]") - return - - context = AgentContext( - task=args, - goal="Complete the specified task", - constraints=["Stay within resource limits", "Maintain data integrity"], - success_criteria=["Task output is valid", "No errors occurred"], - ) - - success = await self.orchestrator.execute_task(context) - if success: - console.print("[green]Task executed successfully[/green]") - else: - console.print("[red]Task execution failed[/red]") - - async def cmd_checkpoint(self, args: str): - """Create a checkpoint.""" - checkpoint = await self.orchestrator.create_checkpoint(args if args else None) - console.print(f"[green]Checkpoint created: {checkpoint.name}[/green]") - - async def cmd_restore(self, args: str): - """Restore from checkpoint.""" - if not args: - # Show available checkpoints - checkpoints = list( - self.orchestrator.checkpoint_dir.glob("checkpoint_*.json") - ) - if checkpoints: - console.print("[cyan]Available checkpoints:[/cyan]") - for cp in checkpoints: - console.print(f" - {cp.name}") - else: - console.print("[yellow]No checkpoints available[/yellow]") - return - - checkpoint_file = self.orchestrator.checkpoint_dir / args - if checkpoint_file.exists(): - success = await self.orchestrator.restore_checkpoint(checkpoint_file) - if success: - console.print("[green]Checkpoint restored[/green]") - else: - console.print(f"[red]Checkpoint not found: {args}[/red]") - - async def cmd_monitor(self, args: str): - """Start monitoring loop.""" - console.print("[cyan]Starting monitor mode (Ctrl+C to stop)...[/cyan]") - try: - await self.orchestrator.monitor_loop() - except KeyboardInterrupt: - console.print("\n[yellow]Monitor stopped[/yellow]") - - async def cmd_help(self, args: str): - """Show help.""" - help_text = """ -[bold cyan]Hanzo Dev - AI Chat Interface[/bold cyan] - -[bold]Just chat naturally! Type anything and press Enter.[/bold] - -Examples: - > Write a Python REST API - > Help me debug this error - > Explain how async/await works - -[bold]Slash Commands:[/bold] - /help - Show this help - /status - Show agent status - /think - Trigger deep thinking - /execute - Execute specific task - /checkpoint - Save current state - /restore - Restore from checkpoint - /monitor - Start monitoring - /exit - Exit chat - -[bold]Memory Commands (like Claude Desktop):[/bold] - #remember - Store in memory - #forget - Remove from memory - #memory - Show memory - #context - Show context -""" - console.print(help_text) - - async def cmd_exit(self, args: str): - """Exit the REPL.""" - self.orchestrator.shutdown() - console.print("[green]Goodbye![/green]") - sys.exit(0) - - async def chat_with_agents(self, message: str): - """Send message to AI agents for natural chat.""" - try: - # Add message to memory - self.memory_manager.add_message("user", message) - - # Get memory context - memory_context = self.memory_manager.summarize_for_ai() - - # Enhance message with context - if memory_context: - enhanced_message = f"{memory_context}\n\nUser: {message}" - else: - enhanced_message = message - - # Try smart fallback if no specific model configured - if ( - not hasattr(self.orchestrator, "orchestrator_model") - or self.orchestrator.orchestrator_model == "auto" - ): - # Use streaming if available - from .streaming import stream_with_fallback - - response = await stream_with_fallback(enhanced_message, console) - - if response: - # Save AI response to memory - self.memory_manager.add_message("assistant", response) - # Response already displayed by streaming handler - return - else: - console.print( - "[red]No AI options available. Please configure API keys or install tools.[/red]" - ) - return - - # For codex and other CLI tools, go straight to direct API chat - if hasattr(self.orchestrator, "orchestrator_model"): - model = self.orchestrator.orchestrator_model - if model in [ - "codex", - "openai-cli", - "openai-codex", - "claude", - "claude-code", - "claude-desktop", - "gemini", - "gemini-cli", - "google-gemini", - "hanzo-ide", - "hanzo-dev-ide", - "ide", - "codestral", - "codestral-free", - "free", - "mistral-free", - "starcoder", - "starcoder2", - "free-starcoder", - ] or model.startswith("local:"): - # Use direct API/CLI chat for these models - await self._direct_api_chat(message) - return - - # Show thinking indicator for network orchestrators - console.print("[dim]Thinking...[/dim]") - - # Check if we have a network orchestrator with actual AI - if hasattr(self.orchestrator, "execute_with_network"): - # Use the network orchestrator (GPT-4, GPT-5, etc.) - result = await self.orchestrator.execute_with_network( - task=message, context={"mode": "chat", "interactive": True} - ) - - if result.get("output"): - # Display AI response in a styled panel - console.print() - from rich.panel import Panel - - console.print( - Panel( - result["output"], - title="[bold cyan]AI Response[/bold cyan]", - title_align="left", - border_style="dim cyan", - padding=(1, 2), - ) - ) - elif result.get("error"): - console.print(f"\n[red]Error:[/red] {result['error']}") - else: - console.print("\n[yellow]No response from agent[/yellow]") - - elif hasattr(self.orchestrator, "execute_with_critique"): - # Use multi-Claude orchestrator - but now it will use real AI! - result = await self.orchestrator.execute_with_critique(message) - - if result.get("output"): - # Display AI response in a styled panel - console.print() - from rich.panel import Panel - - console.print( - Panel( - result["output"], - title="[bold cyan]AI Response[/bold cyan]", - title_align="left", - border_style="dim cyan", - padding=(1, 2), - ) - ) - else: - console.print("\n[yellow]No response from agent[/yellow]") - - else: - # Fallback to direct API call if available - await self._direct_api_chat(message) - - except Exception as e: - console.print(f"[red]Error connecting to AI: {e}[/red]") - console.print("[yellow]Make sure you have API keys configured:[/yellow]") - console.print(" โ€ข OPENAI_API_KEY for GPT models") - console.print(" โ€ข ANTHROPIC_API_KEY for Claude") - console.print(" โ€ข Or use --orchestrator local:llama3.2 for local models") - - async def _direct_api_chat(self, message: str): - """Direct API chat fallback when network orchestrator isn't available.""" - import os - - # Check for CLI tools and free/local options first - if self.orchestrator.orchestrator_model in [ - "codex", - "openai-cli", - "openai-codex", - ]: - # Use OpenAI CLI (Codex) - await self._use_openai_cli(message) - return - elif self.orchestrator.orchestrator_model in [ - "claude", - "claude-code", - "claude-desktop", - ]: - # Use Claude Desktop/Code - await self._use_claude_cli(message) - return - elif self.orchestrator.orchestrator_model in [ - "gemini", - "gemini-cli", - "google-gemini", - ]: - # Use Gemini CLI - await self._use_gemini_cli(message) - return - elif self.orchestrator.orchestrator_model in [ - "hanzo-ide", - "hanzo-dev-ide", - "ide", - ]: - # Use Hanzo Dev IDE from ~/work/hanzo/ide - await self._use_hanzo_ide(message) - return - elif self.orchestrator.orchestrator_model in [ - "codestral", - "codestral-free", - "free", - "mistral-free", - ]: - # Use free Mistral Codestral API - await self._use_free_codestral(message) - return - elif self.orchestrator.orchestrator_model in [ - "starcoder", - "starcoder2", - "free-starcoder", - ]: - # Use free StarCoder via HuggingFace - await self._use_free_starcoder(message) - return - elif self.orchestrator.orchestrator_model.startswith("local:"): - # Use local model via Ollama or LM Studio - await self._use_local_model(message) - return - - # Use the fallback handler to intelligently try available options - from .fallback_handler import smart_chat - - response = await smart_chat(message, console=console) - - if response: - from rich.panel import Panel - - console.print() - console.print( - Panel( - response, - title="[bold cyan]AI Response[/bold cyan]", - title_align="left", - border_style="dim cyan", - padding=(1, 2), - ) - ) - return - - # Try OpenAI first explicitly (in case fallback handler missed it) - openai_key = os.environ.get("OPENAI_API_KEY") or os.getenv("OPENAI_API_KEY") - if openai_key: - try: - from openai import AsyncOpenAI - - client = AsyncOpenAI() - response = await client.chat.completions.create( - model=self.orchestrator.orchestrator_model or "gpt-4", - messages=[ - { - "role": "system", - "content": "You are a helpful AI coding assistant.", - }, - {"role": "user", "content": message}, - ], - temperature=0.7, - max_tokens=2000, - ) - - if response.choices: - from rich.panel import Panel - - console.print() - console.print( - Panel( - response.choices[0].message.content, - title="[bold cyan]GPT-4[/bold cyan]", - title_align="left", - border_style="dim cyan", - padding=(1, 2), - ) - ) - return - - except Exception as e: - console.print(f"[yellow]OpenAI error: {e}[/yellow]") - - # Try Anthropic - if os.getenv("ANTHROPIC_API_KEY"): - try: - from anthropic import AsyncAnthropic - - client = AsyncAnthropic() - response = await client.messages.create( - model="claude-3-5-sonnet-20241022", - messages=[{"role": "user", "content": message}], - max_tokens=2000, - ) - - if response.content: - from rich.panel import Panel - - console.print() - console.print( - Panel( - response.content[0].text, - title="[bold cyan]Claude[/bold cyan]", - title_align="left", - border_style="dim cyan", - padding=(1, 2), - ) - ) - return - - except Exception as e: - console.print(f"[yellow]Anthropic error: {e}[/yellow]") - - # No API keys available - console.print("[red]No AI API keys configured![/red]") - console.print( - "[yellow]Try these options that don't need your API key:[/yellow]" - ) - console.print("\n[bold]CLI Tools (use existing tools):[/bold]") - console.print( - " โ€ข hanzo dev --orchestrator codex # OpenAI CLI (if installed)" - ) - console.print( - " โ€ข hanzo dev --orchestrator claude # Claude Desktop (if installed)" - ) - console.print( - " โ€ข hanzo dev --orchestrator gemini # Gemini CLI (if installed)" - ) - console.print( - " โ€ข hanzo dev --orchestrator hanzo-ide # Hanzo IDE from ~/work/hanzo/ide" - ) - console.print("\n[bold]Free APIs (rate limited):[/bold]") - console.print( - " โ€ข hanzo dev --orchestrator codestral # Free Mistral Codestral" - ) - console.print(" โ€ข hanzo dev --orchestrator starcoder # Free StarCoder") - console.print("\n[bold]Local Models (unlimited):[/bold]") - console.print(" โ€ข hanzo dev --orchestrator local:llama3.2 # Via Ollama") - console.print(" โ€ข hanzo dev --orchestrator local:codellama # Via Ollama") - console.print(" โ€ข hanzo dev --orchestrator local:mistral # Via Ollama") - console.print("\n[dim]Or set API keys for full access:[/dim]") - console.print(" โ€ข export OPENAI_API_KEY=sk-...") - console.print(" โ€ข export ANTHROPIC_API_KEY=sk-ant-...") - - async def _use_free_codestral(self, message: str): - """Use free Mistral Codestral API (no API key needed for trial).""" - try: - import httpx - - console.print("[dim]Using free Codestral API (rate limited)...[/dim]") - - async with httpx.AsyncClient() as client: - # Mistral offers free tier with rate limits - response = await client.post( - "https://api.mistral.ai/v1/chat/completions", - headers={ - "Content-Type": "application/json", - # Free tier doesn't need API key for limited usage - }, - json={ - "model": "codestral-latest", - "messages": [ - { - "role": "system", - "content": "You are Codestral, an AI coding assistant.", - }, - {"role": "user", "content": message}, - ], - "temperature": 0.7, - "max_tokens": 2000, - }, - timeout=30.0, - ) - - if response.status_code == 200: - data = response.json() - if data.get("choices"): - console.print( - f"[cyan]Codestral:[/cyan] {data['choices'][0]['message']['content']}" - ) - else: - console.print( - "[yellow]Free tier limit reached. Try local models instead:[/yellow]" - ) - console.print( - " โ€ข Install Ollama: curl -fsSL https://ollama.com/install.sh | sh" - ) - console.print(" โ€ข Run: ollama pull codellama") - console.print(" โ€ข Use: hanzo dev --orchestrator local:codellama") - - except Exception as e: - console.print(f"[red]Codestral error: {e}[/red]") - console.print("[yellow]Try local models instead (no limits):[/yellow]") - console.print(" โ€ข hanzo dev --orchestrator local:codellama") - - async def _use_free_starcoder(self, message: str): - """Use free StarCoder via HuggingFace Inference API.""" - try: - import httpx - - console.print("[dim]Using free StarCoder API...[/dim]") - - async with httpx.AsyncClient() as client: - # HuggingFace offers free inference API - response = await client.post( - "https://api-inference.huggingface.co/models/bigcode/starcoder2-15b", - headers={ - "Content-Type": "application/json", - }, - json={ - "inputs": f"<|system|>You are StarCoder, an AI coding assistant.<|end|>\n<|user|>{message}<|end|>\n<|assistant|>", - "parameters": { - "temperature": 0.7, - "max_new_tokens": 2000, - "return_full_text": False, - }, - }, - timeout=30.0, - ) - - if response.status_code == 200: - data = response.json() - if isinstance(data, list) and data: - console.print( - f"[cyan]StarCoder:[/cyan] {data[0].get('generated_text', '')}" - ) - else: - console.print( - "[yellow]API limit reached. Install local models:[/yellow]" - ) - console.print(" โ€ข brew install ollama") - console.print(" โ€ข ollama pull starcoder2") - console.print(" โ€ข hanzo dev --orchestrator local:starcoder2") - - except Exception as e: - console.print(f"[red]StarCoder error: {e}[/red]") - - async def _use_openai_cli(self, message: str): - """Use OpenAI CLI (Codex) - the official OpenAI CLI tool.""" - try: - import json - import shutil - import subprocess - - console.print("[dim]Using OpenAI CLI (Codex)...[/dim]") - - # Check if openai CLI is installed - if not shutil.which("openai"): - console.print("[red]OpenAI CLI not installed![/red]") - console.print("[yellow]To install:[/yellow]") - console.print(" โ€ข pip install openai-cli") - console.print(" โ€ข openai login") - console.print("Then use: hanzo dev --orchestrator codex") - return - - # Use openai CLI to chat - correct syntax - cmd = [ - "openai", - "api", - "chat.completions.create", - "-m", - "gpt-4", - "-g", - message, - ] - - process = subprocess.Popen( - cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True - ) - - stdout, stderr = process.communicate(timeout=30) - - if process.returncode == 0 and stdout: - console.print(f"[cyan]Codex:[/cyan] {stdout.strip()}") - else: - console.print(f"[red]OpenAI CLI error: {stderr}[/red]") - - except subprocess.TimeoutExpired: - console.print("[yellow]OpenAI CLI timed out[/yellow]") - except Exception as e: - console.print(f"[red]Error using OpenAI CLI: {e}[/red]") - - async def _use_claude_cli(self, message: str): - """Use Claude Desktop/Code CLI.""" - try: - import os - import shutil - import subprocess - - console.print("[dim]Using Claude Desktop...[/dim]") - - # Check for Claude Code or Claude Desktop - claude_paths = [ - "/usr/local/bin/claude", - "/Applications/Claude.app/Contents/MacOS/Claude", - os.path.expanduser("~/Applications/Claude.app/Contents/MacOS/Claude"), - "claude", # In PATH - ] - - claude_path = None - for path in claude_paths: - if os.path.exists(path) or shutil.which(path) is not None: - claude_path = path - break - - if not claude_path: - console.print("[red]Claude Desktop not found![/red]") - console.print("[yellow]To install:[/yellow]") - console.print(" โ€ข Download from https://claude.ai/desktop") - console.print(" โ€ข Or: brew install --cask claude") - console.print("Then use: hanzo dev --orchestrator claude") - return - - # Send message to Claude via CLI or AppleScript on macOS - if sys.platform == "darwin": - # Use AppleScript to interact with Claude Desktop - # Escape quotes for AppleScript - escaped_message = message.replace('"', '\\"') - script = f""" - tell application "Claude" - activate - delay 0.5 - tell application "System Events" - keystroke "{escaped_message}" - key code 36 -- Enter key - end tell - end tell - """ - - subprocess.run(["osascript", "-e", script]) - console.print( - "[cyan]Sent to Claude Desktop. Check the app for response.[/cyan]" - ) - else: - # Try direct CLI invocation - process = subprocess.Popen( - [claude_path, "--message", message], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - ) - - stdout, stderr = process.communicate(timeout=30) - - if stdout: - console.print(f"[cyan]Claude:[/cyan] {stdout.strip()}") - - except Exception as e: - console.print(f"[red]Error using Claude Desktop: {e}[/red]") - - async def _use_gemini_cli(self, message: str): - """Use Gemini CLI.""" - try: - import shutil - import subprocess - - console.print("[dim]Using Gemini CLI...[/dim]") - - # Check if gemini CLI is installed - if not shutil.which("gemini"): - console.print("[red]Gemini CLI not installed![/red]") - console.print("[yellow]To install:[/yellow]") - console.print(" โ€ข pip install google-generativeai-cli") - console.print(" โ€ข gemini configure") - console.print(" โ€ข Set GOOGLE_API_KEY environment variable") - console.print("Then use: hanzo dev --orchestrator gemini") - return - - # Use gemini CLI - cmd = ["gemini", "chat", message] - - process = subprocess.Popen( - cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True - ) - - stdout, stderr = process.communicate(timeout=30) - - if process.returncode == 0 and stdout: - console.print(f"[cyan]Gemini:[/cyan] {stdout.strip()}") - else: - console.print(f"[red]Gemini CLI error: {stderr}[/red]") - - except subprocess.TimeoutExpired: - console.print("[yellow]Gemini CLI timed out[/yellow]") - except Exception as e: - console.print(f"[red]Error using Gemini CLI: {e}[/red]") - - async def _use_hanzo_ide(self, message: str): - """Use Hanzo Dev IDE from ~/work/hanzo/ide.""" - try: - import os - import subprocess - - console.print("[dim]Using Hanzo Dev IDE...[/dim]") - - # Check if Hanzo IDE exists - ide_path = os.path.expanduser("~/work/hanzo/ide") - if not os.path.exists(ide_path): - console.print("[red]Hanzo Dev IDE not found![/red]") - console.print("[yellow]Expected location: ~/work/hanzo/ide[/yellow]") - console.print("To set up:") - console.print( - " โ€ข git clone https://github.com/hanzoai/ide ~/work/hanzo/ide" - ) - console.print(" โ€ข cd ~/work/hanzo/ide && npm install") - return - - # Check for the CLI entry point - cli_paths = [ - os.path.join(ide_path, "bin", "hanzo-ide"), - os.path.join(ide_path, "hanzo-ide"), - os.path.join(ide_path, "cli.js"), - os.path.join(ide_path, "index.js"), - ] - - cli_path = None - for path in cli_paths: - if os.path.exists(path): - cli_path = path - break - - if not cli_path: - # Try to run with npm/node - package_json = os.path.join(ide_path, "package.json") - if os.path.exists(package_json): - # Run via npm - cmd = ["npm", "run", "chat", "--", message] - cwd = ide_path - else: - console.print("[red]Hanzo IDE CLI not found![/red]") - return - else: - # Run the CLI directly - if cli_path.endswith(".js"): - cmd = ["node", cli_path, "chat", message] - else: - cmd = [cli_path, "chat", message] - cwd = None - - process = subprocess.Popen( - cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, cwd=cwd - ) - - stdout, stderr = process.communicate(timeout=30) - - if process.returncode == 0 and stdout: - console.print(f"[cyan]Hanzo IDE:[/cyan] {stdout.strip()}") - else: - if stderr: - console.print(f"[yellow]Hanzo IDE: {stderr}[/yellow]") - else: - console.print("[yellow]Hanzo IDE: No response[/yellow]") - - except subprocess.TimeoutExpired: - console.print("[yellow]Hanzo IDE timed out[/yellow]") - except Exception as e: - console.print(f"[red]Error using Hanzo IDE: {e}[/red]") - - async def _use_local_model(self, message: str): - """Use local model via Ollama or LM Studio.""" - import httpx - - model_name = self.orchestrator.orchestrator_model.replace("local:", "") - - # Try Ollama first (default port 11434) - try: - console.print(f"[dim]Using local {model_name} via Ollama...[/dim]") - - async with httpx.AsyncClient() as client: - response = await client.post( - "http://localhost:11434/api/chat", - json={ - "model": model_name, - "messages": [ - { - "role": "system", - "content": "You are a helpful AI coding assistant.", - }, - {"role": "user", "content": message}, - ], - "stream": False, - }, - timeout=60.0, - ) - - if response.status_code == 200: - data = response.json() - if data.get("message"): - console.print( - f"[cyan]{model_name}:[/cyan] {data['message']['content']}" - ) - return - - except Exception: - pass - - # Try LM Studio (default port 1234) - try: - console.print(f"[dim]Trying LM Studio...[/dim]") - - async with httpx.AsyncClient() as client: - response = await client.post( - "http://localhost:1234/v1/chat/completions", - json={ - "model": model_name, - "messages": [ - { - "role": "system", - "content": "You are a helpful AI coding assistant.", - }, - {"role": "user", "content": message}, - ], - "temperature": 0.7, - "max_tokens": 2000, - }, - timeout=60.0, - ) - - if response.status_code == 200: - data = response.json() - if data.get("choices"): - console.print( - f"[cyan]{model_name}:[/cyan] {data['choices'][0]['message']['content']}" - ) - return - - except Exception: - pass - - # Neither worked - console.print(f"[red]Local model '{model_name}' not available[/red]") - console.print("[yellow]To use local models:[/yellow]") - console.print("\nOption 1 - Ollama (recommended):") - console.print(" โ€ข Install: curl -fsSL https://ollama.com/install.sh | sh") - console.print(f" โ€ข Pull model: ollama pull {model_name}") - console.print(" โ€ข It will auto-start when you use hanzo dev") - console.print("\nOption 2 - LM Studio:") - console.print(" โ€ข Download from https://lmstudio.ai") - console.print(f" โ€ข Load {model_name} model") - console.print(" โ€ข Start local server (port 1234)") - - async def handle_memory_command(self, command: str): - """Handle memory/context commands starting with #.""" - parts = command.split(maxsplit=1) - cmd = parts[0].lower() if parts else "" - args = parts[1] if len(parts) > 1 else "" - - if cmd == "remember": - if args: - console.print(f"[green]โœ“ Remembered: {args}[/green]") - else: - console.print("[yellow]Usage: #remember [/yellow]") - elif cmd == "forget": - if args: - console.print(f"[yellow]โœ“ Forgot: {args}[/yellow]") - else: - console.print("[yellow]Usage: #forget [/yellow]") - elif cmd == "memory": - console.print("[cyan]Current Memory:[/cyan]") - console.print(" โ€ข Working on Hanzo Python SDK") - console.print(" โ€ข Using GPT-4 orchestrator") - elif cmd == "context": - console.print("[cyan]Current Context:[/cyan]") - console.print(f" โ€ข Directory: {os.getcwd()}") - console.print(f" โ€ข Model: {self.orchestrator.orchestrator_model}") - else: - console.print(f"[yellow]Unknown: #{cmd}[/yellow]") - console.print("Try: #memory, #remember, #forget, #context") - - -async def run_dev_orchestrator(**kwargs): - """Run the Hanzo Dev orchestrator with multi-agent networking. - - This is the main entry point from the CLI that sets up: - 1. Configurable orchestrator (GPT-5, GPT-4, Claude, Codex, etc.) - 2. Multiple worker agents (Claude instances for implementation) - 3. Critic agents for System 2 thinking - 4. MCP tool networking between instances - 5. Code quality guardrails - 6. Router-based or direct model access - """ - workspace = kwargs.get("workspace", "~/.hanzo/dev") - orchestrator_model = kwargs.get("orchestrator_model", "gpt-5") - orchestrator_config = kwargs.get("orchestrator_config", None) # New config object - claude_path = kwargs.get("claude_path") - monitor = kwargs.get("monitor", False) - repl = kwargs.get("repl", True) - instances = kwargs.get("instances", 2) - mcp_tools = kwargs.get("mcp_tools", True) - network_mode = kwargs.get("network_mode", True) - guardrails = kwargs.get("guardrails", True) - use_network = kwargs.get("use_network", True) # Use hanzo-network if available - use_hanzo_net = kwargs.get("use_hanzo_net", False) # Use hanzo/net for local AI - hanzo_net_port = kwargs.get("hanzo_net_port", 52415) - console_obj = kwargs.get("console", console) - - console_obj.print(f"[bold cyan]Hanzo Dev - AI Coding OS[/bold cyan]") - - # Check if we should use network mode - # For now, disable network mode since hanzo-network isn't available - if False and use_network and NETWORK_AVAILABLE: - console_obj.print( - f"[cyan]Mode: Network Orchestration with hanzo-network[/cyan]" - ) - console_obj.print(f"Orchestrator: {orchestrator_model}") - console_obj.print(f"Workers: {instances} agents") - console_obj.print(f"Critics: {max(1, instances // 2)} agents") - console_obj.print(f"MCP Tools: {'Enabled' if mcp_tools else 'Disabled'}") - console_obj.print(f"Guardrails: {'Enabled' if guardrails else 'Disabled'}\n") - - # Create network orchestrator with configurable LLM - orchestrator = NetworkOrchestrator( - workspace_dir=workspace, - orchestrator_model=orchestrator_model, - num_workers=instances, - enable_mcp=mcp_tools, - enable_networking=network_mode, - enable_guardrails=guardrails, - use_hanzo_net=use_hanzo_net, - hanzo_net_port=hanzo_net_port, - console=console_obj, - ) - - # Initialize the network - success = await orchestrator.initialize() - if not success: - console_obj.print("[red]Failed to initialize network[/red]") - return - else: - # Fallback to API mode - console_obj.print(f"[cyan]Mode: AI Chat[/cyan]") - console_obj.print(f"Model: {orchestrator_model}") - console_obj.print(f"MCP Tools: {'Enabled' if mcp_tools else 'Disabled'}") - console_obj.print(f"Guardrails: {'Enabled' if guardrails else 'Disabled'}\n") - - orchestrator = MultiClaudeOrchestrator( - workspace_dir=workspace, - claude_path=claude_path, - num_instances=instances, - enable_mcp=mcp_tools, - enable_networking=network_mode, - enable_guardrails=guardrails, - console=console_obj, - orchestrator_model=orchestrator_model, - ) - - # Initialize instances - await orchestrator.initialize() - - if monitor: - # Start monitoring mode - await orchestrator.monitor_loop() - elif repl: - # Start REPL interface - repl_interface = HanzoDevREPL(orchestrator) - await repl_interface.run() - else: - # Run once - await asyncio.sleep(10) - orchestrator.shutdown() - - -class NetworkOrchestrator(HanzoDevOrchestrator): - """Advanced orchestrator using hanzo-network with configurable LLM (GPT-5, Claude, local, etc.).""" - - def __init__( - self, - workspace_dir: str, - orchestrator_model: str = "gpt-5", - num_workers: int = 2, - enable_mcp: bool = True, - enable_networking: bool = True, - enable_guardrails: bool = True, - use_hanzo_net: bool = False, - hanzo_net_port: int = 52415, - console: Console = console, - ): - """Initialize network orchestrator with configurable LLM. - - Args: - workspace_dir: Workspace directory - orchestrator_model: Model to use for orchestration (e.g., "gpt-5", "gpt-4", "claude-3-5-sonnet", "local:llama3.2") - num_workers: Number of worker agents (Claude instances) - enable_mcp: Enable MCP tools - enable_networking: Enable agent networking - enable_guardrails: Enable quality guardrails - use_hanzo_net: Use hanzo/net for local orchestration - hanzo_net_port: Port for hanzo/net (default 52415) - console: Console for output - """ - super().__init__(workspace_dir) - self.orchestrator_model = orchestrator_model - self.num_workers = num_workers - self.enable_mcp = enable_mcp - self.enable_networking = enable_networking - self.enable_guardrails = enable_guardrails - self.use_hanzo_net = use_hanzo_net - self.hanzo_net_port = hanzo_net_port - self.console = console - - # Agent network components - self.orchestrator_agent = None - self.worker_agents = [] - self.critic_agents = [] - self.agent_network = None - self.hanzo_net_process = None - - # Check if we can use hanzo-network - if not NETWORK_AVAILABLE: - self.console.print( - "[yellow]Warning: hanzo-network not available, falling back to basic mode[/yellow]" - ) - - async def initialize(self): - """Initialize the agent network with orchestrator and workers.""" - if not NETWORK_AVAILABLE: - self.console.print( - "[red]Cannot initialize network mode without hanzo-network[/red]" - ) - return False - - # Start hanzo net if requested for local orchestration - if self.use_hanzo_net or self.orchestrator_model.startswith("local:"): - await self._start_hanzo_net() - - self.console.print( - f"[cyan]Initializing agent network with {self.orchestrator_model} orchestrator...[/cyan]" - ) - - # Create orchestrator agent (GPT-5, local, or other model) - self.orchestrator_agent = await self._create_orchestrator_agent() - - # Create worker agents (Claude instances for implementation) - for i in range(self.num_workers): - worker = await self._create_worker_agent(i) - self.worker_agents.append(worker) - - # Add local workers if using hanzo net (for cost optimization) - if self.use_hanzo_net or self.orchestrator_model.startswith("local:"): - # Add 1-2 local workers for simple tasks - num_local_workers = min(2, self.num_workers) - for i in range(num_local_workers): - local_worker = await self._create_local_worker_agent(i) - self.worker_agents.append(local_worker) - self.console.print( - f"[green]Added {num_local_workers} local workers for cost optimization[/green]" - ) - - # Create critic agents for System 2 thinking - if self.enable_guardrails: - for i in range(max(1, self.num_workers // 2)): - critic = await self._create_critic_agent(i) - self.critic_agents.append(critic) - - # Create the agent network - all_agents = [self.orchestrator_agent] + self.worker_agents + self.critic_agents - - # Create router based on configuration - if self.use_hanzo_net or self.orchestrator_model.startswith("local:"): - # Use cost-optimized router that prefers local models - router = await self._create_cost_optimized_router() - else: - # Use intelligent router with orchestrator making decisions - router = await self._create_intelligent_router() - - # Create the network - self.agent_network = create_network( - agents=all_agents, - router=router, - default_agent=( - self.orchestrator_agent.name if self.orchestrator_agent else None - ), - ) - - self.console.print( - f"[green]โœ“ Agent network initialized with {len(all_agents)} agents[/green]" - ) - return True - - async def _start_hanzo_net(self): - """Start hanzo net for local AI orchestration.""" - self.console.print( - "[cyan]Starting hanzo/net for local AI orchestration...[/cyan]" - ) - - # Check if hanzo net is already running - import socket - - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - result = sock.connect_ex(("localhost", self.hanzo_net_port)) - sock.close() - - if result == 0: - self.console.print( - f"[yellow]hanzo/net already running on port {self.hanzo_net_port}[/yellow]" - ) - return - - # Start hanzo net - try: - # Determine model to serve based on orchestrator model - model = "llama-3.2-3b" # Default - if ":" in self.orchestrator_model: - model = self.orchestrator_model.split(":")[1] - - cmd = [ - "hanzo", - "net", - "--port", - str(self.hanzo_net_port), - "--models", - model, - "--network", - "local", - ] - - self.hanzo_net_process = subprocess.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - preexec_fn=os.setsid if hasattr(os, "setsid") else None, - ) - - # Wait for it to start - await asyncio.sleep(3) - - if self.hanzo_net_process.poll() is None: - self.console.print( - f"[green]โœ“ hanzo/net started on port {self.hanzo_net_port} with model {model}[/green]" - ) - else: - self.console.print("[red]Failed to start hanzo/net[/red]") - - except Exception as e: - self.console.print(f"[red]Error starting hanzo/net: {e}[/red]") - - async def _create_orchestrator_agent(self) -> Agent: - """Create the orchestrator agent (GPT-5, local, or configured model).""" - # Check if using local model via hanzo/net - if self.orchestrator_model.startswith("local:"): - # Use local model via hanzo/net - model_name = self.orchestrator_model.split(":")[1] - - # Import local network helpers - from hanzo_network.local_network import create_local_agent - - orchestrator = create_local_agent( - name="local_orchestrator", - description=f"Local {model_name} orchestrator via hanzo/net", - system=self._get_orchestrator_system_prompt(), - local_model=model_name, - base_url=f"http://localhost:{self.hanzo_net_port}", - tools=[], - ) - - self.console.print( - f"[green]โœ“ Created local {model_name} orchestrator via hanzo/net[/green]" - ) - return orchestrator - - # Parse model string to get provider and model - model_name = self.orchestrator_model - provider = "openai" # Default to OpenAI - use string - api_key = None - - # Determine provider from model name - if model_name.startswith("deepseek"): - provider = "deepseek" - api_key = os.getenv("DEEPSEEK_API_KEY") - elif model_name.startswith("gpt") or model_name == "codex": - provider = "openai" - api_key = os.getenv("OPENAI_API_KEY") - elif model_name.startswith("claude"): - provider = "anthropic" - api_key = os.getenv("ANTHROPIC_API_KEY") - elif model_name.startswith("gemini"): - provider = "google" - api_key = os.getenv("GOOGLE_API_KEY") - elif model_name.startswith("local:"): - provider = "local" - model_name = model_name.replace("local:", "") - - # Create model config based on what's available - if NETWORK_AVAILABLE: - # Real ModelConfig may have different signature - try: - model_config = ModelConfig( - name=model_name, - provider=provider, - ) - # Set api_key separately if supported - if hasattr(model_config, "api_key"): - model_config.api_key = api_key - except TypeError: - # Fallback to simple string if ModelConfig doesn't work - model_config = model_name - else: - # Use our fallback ModelConfig - model_config = ModelConfig( - name=model_name, - provider=provider, - api_key=api_key, - ) - - # Create orchestrator with strategic system prompt - orchestrator = create_agent( - name="orchestrator", - description=f"{self.orchestrator_model} powered meta-orchestrator for AI coding", - model=model_config, - system=self._get_orchestrator_system_prompt(), - tools=[], # Orchestrator tools will be added - ) - - self.console.print( - f"[green]โœ“ Created {self.orchestrator_model} orchestrator[/green]" - ) - return orchestrator - - def _get_orchestrator_system_prompt(self) -> str: - """Get the system prompt for the orchestrator.""" - return """You are an advanced AI orchestrator managing a network of specialized agents. - Your responsibilities: - 1. Strategic Planning: Break down complex tasks into manageable subtasks - 2. Agent Coordination: Delegate work to appropriate specialist agents - 3. Quality Control: Ensure code quality through critic agents - 4. System 2 Thinking: Invoke deliberative reasoning for complex decisions - 5. Resource Management: Optimize agent usage for cost and performance - - Available agents: - - Worker agents: Claude instances for code implementation and MCP tool usage - - Critic agents: Review and improve code quality - - Local agents: Fast, cost-effective for simple tasks - - Decision framework: - - Complex reasoning โ†’ Use your advanced capabilities - - Code implementation โ†’ Delegate to worker agents - - Quality review โ†’ Invoke critic agents - - Simple tasks โ†’ Use local agents if available - - Always maintain high code quality standards and prevent degradation.""" - - async def _create_worker_agent(self, index: int) -> Agent: - """Create a worker agent (Claude for implementation).""" - worker = create_agent( - name=f"worker_{index}", - description=f"Claude worker agent {index} for code implementation", - model=( - "claude-3-5-sonnet-20241022" - if NETWORK_AVAILABLE - else ModelConfig( - provider="anthropic", - name="claude-3-5-sonnet-20241022", - api_key=os.getenv("ANTHROPIC_API_KEY"), - ) - ), - system="""You are a Claude worker agent specialized in code implementation. - - Your capabilities: - - Write and modify code - - Use MCP tools for file operations - - Execute commands and tests - - Debug and fix issues - - Follow best practices and maintain code quality.""", - tools=[], # MCP tools will be added if enabled - ) - - self.console.print(f" Created worker agent {index}") - return worker - - async def _create_local_worker_agent(self, index: int) -> Agent: - """Create a local worker agent for simple tasks (cost optimization).""" - from hanzo_network.local_network import create_local_agent - - worker = create_local_agent( - name=f"local_worker_{index}", - description=f"Local worker agent {index} for simple tasks", - system="""You are a local worker agent optimized for simple tasks. - - Your capabilities: - - Simple code transformations - - Basic file operations - - Quick validation checks - - Pattern matching - - You handle simple tasks to reduce API costs.""", - local_model="llama-3.2-3b", - base_url=f"http://localhost:{self.hanzo_net_port}", - tools=[], - ) - - self.console.print(f" Created local worker agent {index}") - return worker - - async def _create_critic_agent(self, index: int) -> Agent: - """Create a critic agent for code review.""" - # Use a different model for critics for diversity - critic_model = "gpt-4" if index % 2 == 0 else "claude-3-5-sonnet-20241022" - - critic = create_agent( - name=f"critic_{index}", - description=f"Critic agent {index} for code quality assurance", - model=critic_model, # Just pass the model name string - system="""You are a critic agent focused on code quality and best practices. - - Review code for: - 1. Correctness and bug detection - 2. Performance optimization opportunities - 3. Security vulnerabilities - 4. Maintainability and readability - 5. Best practices and design patterns - - Provide constructive feedback with specific improvement suggestions.""", - tools=[], - ) - - self.console.print(f" Created critic agent {index} ({critic_model})") - return critic - - async def _create_cost_optimized_router(self) -> Router: - """Create a cost-optimized router that prefers local models.""" - from hanzo_network.core.router import Router - - class CostOptimizedRouter(Router): - """Router that minimizes costs by using local models when possible.""" - - def __init__(self, orchestrator_agent, worker_agents, critic_agents): - super().__init__() - self.orchestrator = orchestrator_agent - self.workers = worker_agents - self.critics = critic_agents - self.local_workers = [w for w in worker_agents if "local" in w.name] - self.api_workers = [w for w in worker_agents if "local" not in w.name] - - async def route(self, prompt: str, state=None) -> str: - """Route based on task complexity and cost optimization.""" - prompt_lower = prompt.lower() - - # Simple tasks โ†’ Local workers - simple_keywords = [ - "list", - "check", - "validate", - "format", - "rename", - "count", - "find", - ] - if ( - any(keyword in prompt_lower for keyword in simple_keywords) - and self.local_workers - ): - return self.local_workers[0].name - - # Complex implementation โ†’ API workers (Claude) - complex_keywords = [ - "implement", - "refactor", - "debug", - "optimize", - "design", - "architect", - ] - if ( - any(keyword in prompt_lower for keyword in complex_keywords) - and self.api_workers - ): - return self.api_workers[0].name - - # Review tasks โ†’ Critics - review_keywords = [ - "review", - "critique", - "analyze", - "improve", - "validate code", - ] - if ( - any(keyword in prompt_lower for keyword in review_keywords) - and self.critics - ): - return self.critics[0].name - - # Strategic decisions โ†’ Orchestrator - strategic_keywords = [ - "plan", - "decide", - "strategy", - "coordinate", - "organize", - ] - if any(keyword in prompt_lower for keyword in strategic_keywords): - return self.orchestrator.name - - # Default: Try local first, then API - if self.local_workers: - # For shorter prompts, try local first - if len(prompt) < 500: - return self.local_workers[0].name - - # Fall back to API workers for complex tasks - return ( - self.api_workers[0].name - if self.api_workers - else self.orchestrator.name - ) - - # Create the cost-optimized router - router = CostOptimizedRouter( - self.orchestrator_agent, self.worker_agents, self.critic_agents - ) - - self.console.print( - "[green]โœ“ Created cost-optimized router (local models preferred)[/green]" - ) - return router - - async def _create_intelligent_router(self) -> Router: - """Create an intelligent router using the orchestrator for decisions.""" - if self.orchestrator_agent: - # Create routing agent that uses orchestrator for decisions - router = create_routing_agent( - name="router", - description="Intelligent task router", - agent=self.orchestrator_agent, - system="""Route tasks to the most appropriate agent based on: - - 1. Task complexity and requirements - 2. Agent capabilities and specialization - 3. Current workload and availability - 4. Cost/performance optimization - - Routing strategy: - - Strategic decisions โ†’ Stay with orchestrator - - Implementation tasks โ†’ Route to workers - - Review tasks โ†’ Route to critics - - Parallel work โ†’ Split across multiple agents - - Return the name of the best agent for the task.""", - ) - else: - # Fallback to basic router - router = create_router( - agents=self.worker_agents + self.critic_agents, - default=self.worker_agents[0].name if self.worker_agents else None, - ) - - return router - - async def execute_with_network( - self, task: str, context: Optional[Dict] = None - ) -> Dict: - """Execute a task using the agent network. - - Args: - task: Task description - context: Optional context - - Returns: - Execution result - """ - if not self.agent_network: - self.console.print("[red]Agent network not initialized[/red]") - return {"error": "Network not initialized"} - - self.console.print(f"[cyan]Executing task with agent network: {task}[/cyan]") - - # Create network state - state = NetworkState() - state.add_message("user", task) - - if context: - state.metadata.update(context) - - # Run the network - try: - result = await self.agent_network.run(prompt=task, state=state) - - # If guardrails enabled, validate result - if self.enable_guardrails and self.critic_agents: - validated = await self._validate_with_critics(result, task) - if validated.get("improvements"): - self.console.print("[yellow]Applied critic improvements[/yellow]") - return validated - - return result - - except Exception as e: - self.console.print(f"[red]Network execution error: {e}[/red]") - return {"error": str(e)} - - async def _validate_with_critics(self, result: Dict, original_task: str) -> Dict: - """Validate and potentially improve result using critic agents.""" - if not self.critic_agents: - return result - - # Get first critic to review - critic = self.critic_agents[0] - - review_prompt = f""" - Review this solution: - - Task: {original_task} - Solution: {result.get("output", "")} - - Provide specific improvements if needed. - """ - - review = await critic.run(review_prompt) - - # Check if improvements suggested - if "improve" in str(review.get("output", "")).lower(): - result["improvements"] = review.get("output") - - return result - - def shutdown(self): - """Shutdown the network orchestrator and hanzo net if running.""" - # Stop hanzo net if we started it - if self.hanzo_net_process: - try: - self.console.print("[yellow]Stopping hanzo/net...[/yellow]") - if hasattr(os, "killpg"): - os.killpg(os.getpgid(self.hanzo_net_process.pid), signal.SIGTERM) - else: - self.hanzo_net_process.terminate() - self.hanzo_net_process.wait(timeout=5) - self.console.print("[green]โœ“ hanzo/net stopped[/green]") - except Exception: - try: - self.hanzo_net_process.kill() - except Exception: - pass - - # Call parent shutdown - super().shutdown() - - -class MultiClaudeOrchestrator(HanzoDevOrchestrator): - """Extended orchestrator for multiple Claude instances with MCP networking.""" - - def __init__( - self, - workspace_dir: str, - claude_path: str, - num_instances: int, - enable_mcp: bool, - enable_networking: bool, - enable_guardrails: bool, - console: Console, - orchestrator_model: str = "gpt-4", - ): - super().__init__(workspace_dir, claude_path) - self.num_instances = num_instances - self.enable_mcp = enable_mcp - self.enable_networking = enable_networking - self.enable_guardrails = enable_guardrails - self.console = console - self.orchestrator_model = orchestrator_model # Add this for chat interface - - # Store multiple Claude instances - self.claude_instances = [] - self.instance_configs = [] - - async def initialize(self): - """Initialize all Claude instances with MCP networking.""" - # Check if Claude is available first - claude_available = False - try: - import shutil - - if self.claude_code_path and Path(self.claude_code_path).exists(): - claude_available = True - elif shutil.which("claude"): - claude_available = True - except Exception: - pass - - if not claude_available: - # Skip Claude instance initialization - will use API fallback silently - return - - self.console.print("[cyan]Initializing Claude instances...[/cyan]") - - for i in range(self.num_instances): - role = "primary" if i == 0 else f"critic_{i}" - config = await self._create_instance_config(i, role) - self.instance_configs.append(config) - - self.console.print( - f" [{i + 1}/{self.num_instances}] {role} instance configured" - ) - - # If networking enabled, configure MCP connections between instances - if self.enable_networking: - await self._setup_mcp_networking() - - # Start all instances - for i, config in enumerate(self.instance_configs): - success = await self._start_claude_instance(i, config) - if success: - self.console.print(f"[green]โœ“ Instance {i} started[/green]") - else: - # Don't show error, just skip silently - pass - - async def _create_instance_config(self, index: int, role: str) -> Dict: - """Create configuration for a Claude instance.""" - base_port = 8000 - mcp_port = 9000 - - config = { - "index": index, - "role": role, - "workspace": self.workspace_dir / f"instance_{index}", - "port": base_port + index, - "mcp_port": mcp_port + index, - "mcp_config": {}, - "env": {}, - } - - # Create workspace directory - config["workspace"].mkdir(parents=True, exist_ok=True) - - # Configure MCP tools if enabled - if self.enable_mcp: - config["mcp_config"] = await self._create_mcp_config(index, role) - - return config - - async def _create_mcp_config(self, index: int, role: str) -> Dict: - """Create MCP configuration for an instance.""" - mcp_config = { - "mcpServers": { - "hanzo-mcp": { - "command": "python", - "args": ["-m", "hanzo_mcp"], - "env": {"INSTANCE_ID": str(index), "INSTANCE_ROLE": role}, - } - } - } - - # Add file system tools - mcp_config["mcpServers"]["filesystem"] = { - "command": "npx", - "args": ["-y", "@modelcontextprotocol/server-filesystem"], - "env": {"ALLOWED_DIRECTORIES": str(self.workspace_dir)}, - } - - return mcp_config - - async def _setup_mcp_networking(self): - """Set up MCP networking between Claude instances.""" - self.console.print( - "[cyan]Setting up MCP networking between instances...[/cyan]" - ) - - # Each instance gets MCP servers for all other instances - for i, config in enumerate(self.instance_configs): - for j, other_config in enumerate(self.instance_configs): - if i != j: - # Add other instance as MCP server - server_name = f"claude_instance_{j}" - config["mcp_config"]["mcpServers"][server_name] = { - "command": "python", - "args": [ - "-m", - "hanzo_mcp.bridge", - "--target-port", - str(other_config["port"]), - "--instance-id", - str(j), - "--role", - other_config["role"], - ], - "env": {"SOURCE_INSTANCE": str(i), "TARGET_INSTANCE": str(j)}, - } - - # Save MCP config - mcp_config_file = config["workspace"] / "mcp_config.json" - with open(mcp_config_file, "w") as f: - json.dump(config["mcp_config"], f, indent=2) - - config["env"]["MCP_CONFIG_PATH"] = str(mcp_config_file) - - async def _start_claude_instance(self, index: int, config: Dict) -> bool: - """Start a single Claude instance.""" - try: - cmd = [self.claude_code_path or "claude"] - - # Add configuration flags - if config.get("env", {}).get("MCP_CONFIG_PATH"): - cmd.extend(["--mcp-config", config["env"]["MCP_CONFIG_PATH"]]) - - # Set up environment - env = os.environ.copy() - env.update(config.get("env", {})) - - # Start process - process = subprocess.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - stdin=subprocess.PIPE, - env=env, - cwd=str(config["workspace"]), - preexec_fn=os.setsid if hasattr(os, "setsid") else None, - ) - - self.claude_instances.append( - { - "index": index, - "role": config["role"], - "process": process, - "config": config, - "health": RuntimeHealth( - state=RuntimeState.RUNNING, - last_response=datetime.now(), - response_time_ms=0, - memory_usage_mb=0, - cpu_percent=0, - error_count=0, - restart_count=0, - ), - } - ) - - return True - - except Exception as e: - logger.error(f"Failed to start instance {index}: {e}") - return False - - async def execute_with_critique(self, task: str) -> Dict: - """Execute a task with System 2 critique. - - 1. Primary instance executes the task - 2. Critic instance(s) review and suggest improvements - 3. Primary incorporates feedback if confidence is high - """ - self.console.print(f"[cyan]Executing with System 2 thinking: {task}[/cyan]") - - # Check if instances are initialized - if not self.claude_instances: - # No instances started, use fallback handler for smart routing - from .fallback_handler import smart_chat - - response = await smart_chat(task, console=self.console) - if response: - return {"output": response, "success": True} - # If smart_chat fails, try direct API as last resort - return await self._call_api_model(task) - - # Step 1: Primary execution - primary = self.claude_instances[0] - result = await self._send_to_instance(primary, task) - - if self.num_instances < 2: - return result - - # Step 2: Critic review - critiques = [] - for critic in self.claude_instances[1:]: - critique_prompt = f""" - Review this code/solution and provide constructive criticism: - - Task: {task} - Solution: {result.get("output", "")} - - Evaluate for: - 1. Correctness - 2. Performance - 3. Security - 4. Maintainability - 5. Best practices - - Suggest specific improvements. - """ - - critique = await self._send_to_instance(critic, critique_prompt) - critiques.append(critique) - - # Step 3: Incorporate feedback if valuable - if critiques and self.enable_guardrails: - improvement_prompt = f""" - Original task: {task} - Original solution: {result.get("output", "")} - - Critiques received: - {json.dumps(critiques, indent=2)} - - Incorporate the valid suggestions and produce an improved solution. - """ - - improved = await self._send_to_instance(primary, improvement_prompt) - - # Validate improvement didn't degrade quality - if await self._validate_improvement(result, improved): - self.console.print( - "[green]โœ“ Solution improved with System 2 feedback[/green]" - ) - return improved - else: - self.console.print( - "[yellow]โš  Keeping original solution (improvement validation failed)[/yellow]" - ) - - return result - - async def _send_to_instance(self, instance: Dict, prompt: str) -> Dict: - """Send a prompt to a specific Claude instance using configured model.""" - # Simple direct approach - use the configured orchestrator model - if self.orchestrator_model == "codex": - # Use OpenAI CLI - return await self._call_openai_cli(prompt) - elif self.orchestrator_model in ["claude", "claude-code", "claude-desktop"]: - # Use Claude Desktop - return await self._call_claude_cli(prompt) - elif self.orchestrator_model in ["gemini", "gemini-cli"]: - # Use Gemini CLI - return await self._call_gemini_cli(prompt) - elif self.orchestrator_model.startswith("local:"): - # Use local model - return await self._call_local_model(prompt) - else: - # Try API-based models - return await self._call_api_model(prompt) - - async def _call_openai_cli(self, prompt: str) -> Dict: - """Call OpenAI CLI and return structured response.""" - try: - import subprocess - - result = subprocess.run( - [ - "openai", - "api", - "chat.completions.create", - "-m", - "gpt-4", - "-g", - prompt, - ], - capture_output=True, - text=True, - timeout=30, - ) - if result.returncode == 0 and result.stdout: - return {"output": result.stdout.strip(), "success": True} - except Exception as e: - logger.error(f"OpenAI CLI error: {e}") - return { - "output": "OpenAI CLI not available. Install with: pip install openai-cli", - "success": False, - } - - async def _call_claude_cli(self, prompt: str) -> Dict: - """Call Claude Desktop and return structured response.""" - try: - import sys - import subprocess - - if sys.platform == "darwin": - # macOS - use AppleScript - script = f'tell application "Claude" to activate' - subprocess.run(["osascript", "-e", script]) - return { - "output": "Sent to Claude Desktop. Check app for response.", - "success": True, - } - except Exception as e: - logger.error(f"Claude CLI error: {e}") - return { - "output": "Claude Desktop not available. Install from https://claude.ai/desktop", - "success": False, - } - - async def _call_gemini_cli(self, prompt: str) -> Dict: - """Call Gemini CLI and return structured response.""" - try: - import subprocess - - result = subprocess.run( - ["gemini", "chat", prompt], capture_output=True, text=True, timeout=30 - ) - if result.returncode == 0 and result.stdout: - return {"output": result.stdout.strip(), "success": True} - except Exception as e: - logger.error(f"Gemini CLI error: {e}") - return { - "output": "Gemini CLI not available. Install with: pip install google-generativeai-cli", - "success": False, - } - - async def _call_local_model(self, prompt: str) -> Dict: - """Call local model via Ollama and return structured response.""" - try: - import httpx - - model_name = self.orchestrator_model.replace("local:", "") - - async with httpx.AsyncClient() as client: - response = await client.post( - "http://localhost:11434/api/chat", - json={ - "model": model_name, - "messages": [{"role": "user", "content": prompt}], - "stream": False, - }, - timeout=60.0, - ) - - if response.status_code == 200: - data = response.json() - if data.get("message"): - return {"output": data["message"]["content"], "success": True} - except Exception as e: - logger.error(f"Local model error: {e}") - return { - "output": f"Local model not available. Install Ollama and run: ollama pull {self.orchestrator_model.replace('local:', '')}", - "success": False, - } - - async def _call_api_model(self, prompt: str) -> Dict: - """Call API-based model and return structured response.""" - import os - - # Try OpenAI first (check environment variable properly) - openai_key = os.environ.get("OPENAI_API_KEY") or os.getenv("OPENAI_API_KEY") - if openai_key: - try: - from openai import AsyncOpenAI - - client = AsyncOpenAI(api_key=openai_key) - response = await client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": prompt}], - max_tokens=2000, - ) - if response.choices: - return { - "output": response.choices[0].message.content, - "success": True, - } - except Exception as e: - logger.error(f"OpenAI API error: {e}") - - # Try Anthropic - anthropic_key = os.environ.get("ANTHROPIC_API_KEY") or os.getenv( - "ANTHROPIC_API_KEY" - ) - if anthropic_key: - try: - from anthropic import AsyncAnthropic - - client = AsyncAnthropic(api_key=anthropic_key) - response = await client.messages.create( - model="claude-3-5-sonnet-20241022", - messages=[{"role": "user", "content": prompt}], - max_tokens=2000, - ) - if response.content: - return {"output": response.content[0].text, "success": True} - except Exception as e: - logger.error(f"Anthropic API error: {e}") - - # Try fallback handler as last resort - from .fallback_handler import smart_chat - - response = await smart_chat( - prompt, console=None - ) # No console to avoid duplicate messages - if response: - return {"output": response, "success": True} - - return { - "output": "No API keys configured. Set OPENAI_API_KEY or ANTHROPIC_API_KEY", - "success": False, - } - - async def _validate_improvement(self, original: Dict, improved: Dict) -> bool: - """Validate that an improvement doesn't degrade quality.""" - if not self.enable_guardrails: - return True - - # Basic structural validation - if not improved: - return False - if improved.get("error"): - return False - - # Guardrails passed - return True - - def shutdown(self): - """Shutdown all Claude instances.""" - self.console.print("[yellow]Shutting down all instances...[/yellow]") - - for instance in self.claude_instances: - try: - process = instance["process"] - if hasattr(os, "killpg"): - os.killpg(os.getpgid(process.pid), signal.SIGTERM) - else: - process.terminate() - process.wait(timeout=5) - except Exception: - try: - instance["process"].kill() - except Exception: - pass - - self.console.print("[green]โœ“ All instances shut down[/green]") - - -async def main(): - """Main entry point for hanzo-dev.""" - import argparse - - parser = argparse.ArgumentParser( - description="Hanzo Dev - System 2 Meta-AI Orchestrator" - ) - parser.add_argument( - "--workspace", default="~/.hanzo/dev", help="Workspace directory" - ) - parser.add_argument("--claude-path", help="Path to Claude Code executable") - parser.add_argument("--monitor", action="store_true", help="Start in monitor mode") - parser.add_argument("--repl", action="store_true", help="Start REPL interface") - parser.add_argument( - "--instances", type=int, default=2, help="Number of Claude instances" - ) - parser.add_argument("--no-mcp", action="store_true", help="Disable MCP tools") - parser.add_argument( - "--no-network", action="store_true", help="Disable instance networking" - ) - parser.add_argument( - "--no-guardrails", action="store_true", help="Disable guardrails" - ) - - args = parser.parse_args() - - await run_dev_orchestrator( - workspace=args.workspace, - claude_path=args.claude_path, - monitor=args.monitor, - repl=args.repl or not args.monitor, - instances=args.instances, - mcp_tools=not args.no_mcp, - network_mode=not args.no_network, - guardrails=not args.no_guardrails, - ) - - -if __name__ == "__main__": - try: - asyncio.run(main()) - except KeyboardInterrupt: - console.print("\n[yellow]Interrupted. Exiting...[/yellow]") - import sys - - sys.exit(0) diff --git a/pkg/hanzo/src/hanzo/fallback_handler.py b/pkg/hanzo/src/hanzo/fallback_handler.py deleted file mode 100644 index 5e415b5f2..000000000 --- a/pkg/hanzo/src/hanzo/fallback_handler.py +++ /dev/null @@ -1,303 +0,0 @@ -""" -Intelligent fallback handler for Hanzo Dev. -Automatically tries available AI options when primary fails. -""" - -import os -import shutil -import subprocess -from typing import Any, Dict, Optional -from pathlib import Path - - -class FallbackHandler: - """Handles automatic fallback to available AI options.""" - - def __init__(self): - self.available_options = self._detect_available_options() - self.fallback_order = self._determine_fallback_order() - - def _detect_available_options(self) -> Dict[str, bool]: - """Detect which AI options are available.""" - options = { - "deepseek_api": bool(os.getenv("DEEPSEEK_API_KEY")), # Added DeepSeek - "openai_api": bool(os.getenv("OPENAI_API_KEY")), - "anthropic_api": bool(os.getenv("ANTHROPIC_API_KEY")), - "google_api": bool( - os.getenv("GOOGLE_API_KEY") or os.getenv("GEMINI_API_KEY") - ), - "openai_cli": shutil.which("openai") is not None, - "claude_cli": shutil.which("claude") is not None, - "gemini_cli": shutil.which("gemini") is not None, - "ollama": self._check_ollama(), - "hanzo_ide": Path.home().joinpath("work/hanzo/ide").exists(), - "free_apis": True, # Always available (Codestral, StarCoder) - } - return options - - def _check_ollama(self) -> bool: - """Check if Ollama is running and has models.""" - try: - import httpx - - with httpx.Client(timeout=2.0) as client: - response = client.get("http://localhost:11434/api/tags") - if response.status_code == 200: - data = response.json() - return len(data.get("models", [])) > 0 - except Exception: - pass - return False - - def _determine_fallback_order(self) -> list: - """Determine the order of fallback options based on availability.""" - order = [] - - # Priority 1: API keys (fastest, most reliable) - # DeepSeek first for cost efficiency ($0.14/M vs $10+/M for GPT-4) - if self.available_options["deepseek_api"]: - order.append(("deepseek_api", "deepseek-chat")) - if self.available_options["openai_api"]: - order.append(("openai_api", "gpt-4")) - if self.available_options["anthropic_api"]: - order.append(("anthropic_api", "claude-3-5-sonnet")) - if self.available_options["google_api"]: - order.append(("google_api", "gemini-pro")) - - # Priority 2: CLI tools (no API key needed) - if self.available_options["openai_cli"]: - order.append(("openai_cli", "codex")) - if self.available_options["claude_cli"]: - order.append(("claude_cli", "claude-desktop")) - if self.available_options["gemini_cli"]: - order.append(("gemini_cli", "gemini")) - - # Priority 3: Local models (free, but requires setup) - if self.available_options["ollama"]: - order.append(("ollama", "local:llama3.2")) - if self.available_options["hanzo_ide"]: - order.append(("hanzo_ide", "hanzo-ide")) - - # Priority 4: Free cloud APIs (rate limited) - if self.available_options["free_apis"]: - order.append(("free_api", "codestral-free")) - order.append(("free_api", "starcoder2")) - - return order - - def get_best_option(self) -> Optional[tuple]: - """Get the best available AI option.""" - if self.fallback_order: - return self.fallback_order[0] - return None - - def get_next_option(self, failed_option: str) -> Optional[tuple]: - """Get the next fallback option after one fails.""" - for i, (option_type, model) in enumerate(self.fallback_order): - if model == failed_option and i + 1 < len(self.fallback_order): - return self.fallback_order[i + 1] - return None - - def suggest_setup(self) -> str: - """Suggest setup instructions for unavailable options.""" - suggestions = [] - - if not self.available_options["deepseek_api"]: - suggestions.append( - "โ€ข Set DEEPSEEK_API_KEY for cost-effective DeepSeek access ($0.14/M tokens)" - ) - - if not self.available_options["openai_api"]: - suggestions.append("โ€ข Set OPENAI_API_KEY for GPT-4/GPT-5 access") - - if not self.available_options["anthropic_api"]: - suggestions.append("โ€ข Set ANTHROPIC_API_KEY for Claude access") - - if not self.available_options["ollama"]: - suggestions.append( - "โ€ข Install Ollama: curl -fsSL https://ollama.com/install.sh | sh" - ) - suggestions.append(" Then run: ollama pull llama3.2") - - if not self.available_options["openai_cli"]: - suggestions.append("โ€ข Install OpenAI CLI: pip install openai-cli") - - if not self.available_options["claude_cli"]: - suggestions.append( - "โ€ข Install Claude Desktop from https://claude.ai/download" - ) - - return ( - "\n".join(suggestions) if suggestions else "All AI options are available!" - ) - - def print_status(self, console): - """Print the current status of available AI options.""" - from rich.table import Table - - table = Table( - title="Available AI Options", show_header=True, header_style="bold magenta" - ) - table.add_column("Option", style="cyan", width=20) - table.add_column("Status", width=10) - table.add_column("Model", width=20) - - status_map = { - "deepseek_api": ("DeepSeek API", "deepseek-chat"), # Added DeepSeek - "openai_api": ("OpenAI API", "gpt-4"), - "anthropic_api": ("Anthropic API", "claude-3-5"), - "google_api": ("Google API", "gemini-pro"), - "openai_cli": ("OpenAI CLI", "codex"), - "claude_cli": ("Claude Desktop", "claude"), - "gemini_cli": ("Gemini CLI", "gemini"), - "ollama": ("Ollama Local", "llama3.2"), - "hanzo_ide": ("Hanzo IDE", "hanzo-dev"), - "free_apis": ("Free APIs", "codestral/starcoder"), - } - - for key, available in self.available_options.items(): - if key in status_map: - name, model = status_map[key] - status = "โœ…" if available else "โŒ" - table.add_row(name, status, model if available else "Not available") - - console.print(table) - - if self.fallback_order: - console.print( - f"\n[green]Primary option: {self.fallback_order[0][1]}[/green]" - ) - if len(self.fallback_order) > 1: - fallbacks = ", ".join([opt[1] for opt in self.fallback_order[1:]]) - console.print(f"[yellow]Fallback options: {fallbacks}[/yellow]") - else: - console.print("\n[red]No AI options available![/red]") - console.print("\n[yellow]Setup suggestions:[/yellow]") - console.print(self.suggest_setup()) - - -async def smart_chat(message: str, console=None) -> Optional[str]: - """ - Smart chat that automatically tries available AI options. - Returns the AI response or None if all options fail. - """ - from .rate_limiter import smart_limiter - - handler = FallbackHandler() - - if console: - console.print("\n[dim]Detecting available AI options...[/dim]") - - best_option = handler.get_best_option() - if not best_option: - if console: - handler.print_status(console) - return None - - option_type, model = best_option - - # Try the primary option with rate limiting - try: - if option_type == "deepseek_api": - # DeepSeek API (OpenAI-compatible) - from openai import AsyncOpenAI - - async def call_deepseek(): - client = AsyncOpenAI( - api_key=os.getenv("DEEPSEEK_API_KEY"), - base_url="https://api.deepseek.com/v1", - ) - response = await client.chat.completions.create( - model="deepseek-chat", - messages=[{"role": "user", "content": message}], - max_tokens=500, - ) - return response.choices[0].message.content - - return await smart_limiter.execute_with_limit("deepseek", call_deepseek) - - elif option_type == "openai_api": - - async def call_openai(): - from openai import AsyncOpenAI - - client = AsyncOpenAI() - response = await client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": message}], - max_tokens=500, - ) - return response.choices[0].message.content - - return await smart_limiter.execute_with_limit("openai", call_openai) - - elif option_type == "anthropic_api": - from anthropic import AsyncAnthropic - - client = AsyncAnthropic() - response = await client.messages.create( - model="claude-3-5-sonnet-20241022", - messages=[{"role": "user", "content": message}], - max_tokens=500, - ) - return response.content[0].text - - elif option_type == "openai_cli": - # Use OpenAI CLI - result = subprocess.run( - [ - "openai", - "api", - "chat.completions.create", - "-m", - "gpt-4", - "-g", - message, - ], - capture_output=True, - text=True, - timeout=30, - ) - if result.returncode == 0: - return result.stdout.strip() - - elif option_type == "ollama": - # Use Ollama - import httpx - - async with httpx.AsyncClient() as client: - response = await client.post( - "http://localhost:11434/api/generate", - json={"model": "llama3.2", "prompt": message, "stream": False}, - timeout=30.0, - ) - if response.status_code == 200: - return response.json().get("response", "") - - elif option_type == "free_api": - # Try free Codestral API - import httpx - - async with httpx.AsyncClient() as client: - response = await client.post( - "https://codestral.mistral.ai/v1/fim/completions", - headers={"Content-Type": "application/json"}, - json={"prompt": message, "suffix": "", "max_tokens": 500}, - timeout=30.0, - ) - if response.status_code == 200: - return response.json().get("choices", [{}])[0].get("text", "") - - except Exception as e: - if console: - console.print(f"[yellow]Primary option {model} failed: {e}[/yellow]") - console.print("[dim]Trying fallback...[/dim]") - - # Try next fallback - next_option = handler.get_next_option(model) - if next_option: - # Recursively try the next option - handler.fallback_order.remove(best_option) - return await smart_chat(message, console) - - return None diff --git a/pkg/hanzo/src/hanzo/infra/__init__.py b/pkg/hanzo/src/hanzo/infra/__init__.py deleted file mode 100644 index 9d8a8b13f..000000000 --- a/pkg/hanzo/src/hanzo/infra/__init__.py +++ /dev/null @@ -1,446 +0,0 @@ -"""Hanzo Infrastructure SDK - Unified client for Hanzo OSS infrastructure. - -Provides async clients for all Hanzo infrastructure services: -- Vector: Qdrant vector database -- KV: Redis/Valkey key-value store -- DocumentDB: MongoDB document database -- Storage: S3/MinIO object storage -- Search: Meilisearch full-text search -- PubSub: NATS messaging -- Tasks: Temporal workflows -- Queues: Distributed work queues -- Cron: Scheduled jobs -- Functions: Nuclio serverless functions - -Example: - ```python - from hanzo.infra import HanzoInfra - - - async def main(): - # Initialize from environment variables - infra = await HanzoInfra.from_env() - - # Use individual clients - await infra.kv.set("key", "value") - await infra.vector.upsert("embeddings", points) - - # Or with context manager - async with await HanzoInfra.from_env() as infra: - results = await infra.search.search("products", "query") - - # Selective initialization - infra = await HanzoInfra.from_env(services=["kv", "vector"]) - ``` -""" - -from __future__ import annotations - -import os -from typing import Any, Optional, Sequence - -from .kv import KVClient, KVConfig -from .cron import CronJob, CronClient, CronConfig, CronExecution -from .tasks import TasksClient, TasksConfig, WorkflowHandle, WorkflowExecution -from .pubsub import Message, PubSubClient, PubSubConfig, Subscription -from .queues import Job, JobStatus, QueueStats, QueuesClient, QueuesConfig -from .search import SearchHit, SearchClient, SearchConfig, SearchResult -from .vector import ScoredPoint, VectorPoint, VectorClient, VectorConfig -from .storage import ( - ObjectInfo, - PresignedUrl, - UploadResult, - StorageClient, - StorageConfig, -) -from .functions import ( - FunctionSpec, - InvokeResult, - FunctionStatus, - FunctionsClient, - FunctionsConfig, -) -from .documentdb import ( - Document, - DeleteResult, - UpdateResult, - DocumentDBClient, - DocumentDBConfig, -) - -__all__ = [ - # Main class - "HanzoInfra", - # Vector (Qdrant) - "VectorClient", - "VectorConfig", - "VectorPoint", - "ScoredPoint", - # KV (Redis/Valkey) - "KVClient", - "KVConfig", - # DocumentDB (MongoDB) - "DocumentDBClient", - "DocumentDBConfig", - "Document", - "UpdateResult", - "DeleteResult", - # Storage (S3/MinIO) - "StorageClient", - "StorageConfig", - "ObjectInfo", - "UploadResult", - "PresignedUrl", - # Search (Meilisearch) - "SearchClient", - "SearchConfig", - "SearchHit", - "SearchResult", - # PubSub (NATS) - "PubSubClient", - "PubSubConfig", - "Message", - "Subscription", - # Tasks (Temporal) - "TasksClient", - "TasksConfig", - "WorkflowHandle", - "WorkflowExecution", - # Queues - "QueuesClient", - "QueuesConfig", - "Job", - "JobStatus", - "QueueStats", - # Cron - "CronClient", - "CronConfig", - "CronJob", - "CronExecution", - # Functions (Nuclio) - "FunctionsClient", - "FunctionsConfig", - "FunctionSpec", - "FunctionStatus", - "InvokeResult", -] - - -ALL_SERVICES = [ - "vector", - "kv", - "documentdb", - "storage", - "search", - "pubsub", - "tasks", - "queues", - "cron", - "functions", -] - - -class HanzoInfra: - """Unified client for Hanzo infrastructure services. - - Provides a single entry point for all infrastructure services, - with lazy initialization and configurable service selection. - - Attributes: - vector: Qdrant vector database client. - kv: Redis/Valkey key-value client. - documentdb: MongoDB document database client. - storage: S3/MinIO object storage client. - search: Meilisearch full-text search client. - pubsub: NATS messaging client. - tasks: Temporal workflow client. - queues: Distributed work queue client. - cron: Scheduled jobs client. - functions: Nuclio serverless functions client. - """ - - def __init__( - self, - vector: Optional[VectorClient] = None, - kv: Optional[KVClient] = None, - documentdb: Optional[DocumentDBClient] = None, - storage: Optional[StorageClient] = None, - search: Optional[SearchClient] = None, - pubsub: Optional[PubSubClient] = None, - tasks: Optional[TasksClient] = None, - queues: Optional[QueuesClient] = None, - cron: Optional[CronClient] = None, - functions: Optional[FunctionsClient] = None, - ) -> None: - """Initialize HanzoInfra with individual clients. - - Args: - vector: Qdrant vector client. - kv: Redis/Valkey client. - documentdb: MongoDB client. - storage: S3/MinIO client. - search: Meilisearch client. - pubsub: NATS client. - tasks: Temporal client. - queues: Work queue client. - cron: Cron scheduler client. - functions: Nuclio functions client. - """ - self._vector = vector - self._kv = kv - self._documentdb = documentdb - self._storage = storage - self._search = search - self._pubsub = pubsub - self._tasks = tasks - self._queues = queues - self._cron = cron - self._functions = functions - - @property - def vector(self) -> VectorClient: - """Get vector client (Qdrant).""" - if self._vector is None: - raise RuntimeError( - "Vector client not initialized. Initialize with services=['vector']" - ) - return self._vector - - @property - def kv(self) -> KVClient: - """Get key-value client (Redis/Valkey).""" - if self._kv is None: - raise RuntimeError( - "KV client not initialized. Initialize with services=['kv']" - ) - return self._kv - - @property - def documentdb(self) -> DocumentDBClient: - """Get document database client (MongoDB).""" - if self._documentdb is None: - raise RuntimeError( - "DocumentDB client not initialized. Initialize with services=['documentdb']" - ) - return self._documentdb - - @property - def storage(self) -> StorageClient: - """Get object storage client (S3/MinIO).""" - if self._storage is None: - raise RuntimeError( - "Storage client not initialized. Initialize with services=['storage']" - ) - return self._storage - - @property - def search(self) -> SearchClient: - """Get search client (Meilisearch).""" - if self._search is None: - raise RuntimeError( - "Search client not initialized. Initialize with services=['search']" - ) - return self._search - - @property - def pubsub(self) -> PubSubClient: - """Get pub/sub client (NATS).""" - if self._pubsub is None: - raise RuntimeError( - "PubSub client not initialized. Initialize with services=['pubsub']" - ) - return self._pubsub - - @property - def tasks(self) -> TasksClient: - """Get tasks/workflow client (Temporal).""" - if self._tasks is None: - raise RuntimeError( - "Tasks client not initialized. Initialize with services=['tasks']" - ) - return self._tasks - - @property - def queues(self) -> QueuesClient: - """Get work queue client.""" - if self._queues is None: - raise RuntimeError( - "Queues client not initialized. Initialize with services=['queues']" - ) - return self._queues - - @property - def cron(self) -> CronClient: - """Get cron scheduler client.""" - if self._cron is None: - raise RuntimeError( - "Cron client not initialized. Initialize with services=['cron']" - ) - return self._cron - - @property - def functions(self) -> FunctionsClient: - """Get serverless functions client (Nuclio).""" - if self._functions is None: - raise RuntimeError( - "Functions client not initialized. Initialize with services=['functions']" - ) - return self._functions - - @classmethod - async def from_env( - cls, - services: Optional[Sequence[str]] = None, - connect: bool = True, - ) -> HanzoInfra: - """Create HanzoInfra from environment variables. - - Args: - services: List of services to initialize. If None, initializes all. - Valid values: vector, kv, documentdb, storage, search, pubsub, - tasks, queues, cron, functions. - connect: Automatically connect to services. - - Returns: - Configured HanzoInfra instance. - - Example: - ```python - # Initialize all services - infra = await HanzoInfra.from_env() - - # Initialize only specific services - infra = await HanzoInfra.from_env(services=["kv", "vector"]) - ``` - """ - services_to_init = set(services) if services else set(ALL_SERVICES) - - # Validate service names - invalid = services_to_init - set(ALL_SERVICES) - if invalid: - raise ValueError(f"Invalid services: {invalid}. Valid: {ALL_SERVICES}") - - clients: dict[str, Any] = {} - - # Initialize requested services - if "vector" in services_to_init: - clients["vector"] = VectorClient(VectorConfig.from_env()) - - if "kv" in services_to_init: - clients["kv"] = KVClient(KVConfig.from_env()) - - if "documentdb" in services_to_init: - clients["documentdb"] = DocumentDBClient(DocumentDBConfig.from_env()) - - if "storage" in services_to_init: - clients["storage"] = StorageClient(StorageConfig.from_env()) - - if "search" in services_to_init: - clients["search"] = SearchClient(SearchConfig.from_env()) - - if "pubsub" in services_to_init: - clients["pubsub"] = PubSubClient(PubSubConfig.from_env()) - - if "tasks" in services_to_init: - clients["tasks"] = TasksClient(TasksConfig.from_env()) - - if "queues" in services_to_init: - clients["queues"] = QueuesClient(QueuesConfig.from_env()) - - if "cron" in services_to_init: - clients["cron"] = CronClient(CronConfig.from_env()) - - if "functions" in services_to_init: - clients["functions"] = FunctionsClient(FunctionsConfig.from_env()) - - infra = cls(**clients) - - if connect: - await infra.connect() - - return infra - - async def connect(self) -> None: - """Connect all initialized services.""" - if self._vector: - await self._vector.connect() - if self._kv: - await self._kv.connect() - if self._documentdb: - await self._documentdb.connect() - if self._storage: - await self._storage.connect() - if self._search: - await self._search.connect() - if self._pubsub: - await self._pubsub.connect() - if self._tasks: - await self._tasks.connect() - if self._queues: - await self._queues.connect() - if self._cron: - await self._cron.connect() - if self._functions: - await self._functions.connect() - - async def close(self) -> None: - """Close all service connections.""" - if self._vector: - await self._vector.close() - if self._kv: - await self._kv.close() - if self._documentdb: - await self._documentdb.close() - if self._storage: - await self._storage.close() - if self._search: - await self._search.close() - if self._pubsub: - await self._pubsub.close() - if self._tasks: - await self._tasks.close() - if self._queues: - await self._queues.close() - if self._cron: - await self._cron.close() - if self._functions: - await self._functions.close() - - async def health_check(self) -> dict[str, bool]: - """Check health of all initialized services. - - Returns: - Dict mapping service name to health status. - """ - results = {} - - if self._vector: - results["vector"] = await self._vector.health_check() - if self._kv: - results["kv"] = await self._kv.health_check() - if self._documentdb: - results["documentdb"] = await self._documentdb.health_check() - if self._storage: - results["storage"] = await self._storage.health_check() - if self._search: - results["search"] = await self._search.health_check() - if self._pubsub: - results["pubsub"] = await self._pubsub.health_check() - if self._tasks: - results["tasks"] = await self._tasks.health_check() - if self._queues: - results["queues"] = await self._queues.health_check() - if self._cron: - results["cron"] = await self._cron.health_check() - if self._functions: - results["functions"] = await self._functions.health_check() - - return results - - async def __aenter__(self) -> HanzoInfra: - """Async context manager entry.""" - return self - - async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: - """Async context manager exit.""" - await self.close() diff --git a/pkg/hanzo/src/hanzo/infra/cron.py b/pkg/hanzo/src/hanzo/infra/cron.py deleted file mode 100644 index 31fa84a5d..000000000 --- a/pkg/hanzo/src/hanzo/infra/cron.py +++ /dev/null @@ -1,646 +0,0 @@ -"""Scheduled jobs (cron) client for Hanzo infrastructure. - -Provides async interface for scheduled/recurring jobs, -built on top of Redis for distributed scheduling. -""" - -from __future__ import annotations - -import os -import json -import asyncio -import hashlib -from typing import Any, Callable, Optional, Awaitable -from datetime import datetime, timedelta -from dataclasses import field, dataclass - -from pydantic import Field, BaseModel - - -class CronConfig(BaseModel): - """Configuration for cron scheduler connection.""" - - host: str = Field(default="localhost", description="Redis server host") - port: int = Field(default=6379, description="Redis server port") - password: Optional[str] = Field(default=None, description="Redis password") - db: int = Field(default=2, description="Redis database number") - url: Optional[str] = Field(default=None, description="Full Redis URL") - prefix: str = Field(default="hanzo:cron", description="Key prefix for cron") - lock_timeout: int = Field(default=60, description="Lock timeout in seconds") - timezone: str = Field(default="UTC", description="Default timezone") - - @classmethod - def from_env(cls) -> CronConfig: - """Create config from environment variables. - - Environment variables: - CRON_REDIS_HOST: Server host (default: localhost) - CRON_REDIS_PORT: Server port (default: 6379) - CRON_REDIS_PASSWORD: Authentication password - CRON_REDIS_DB: Database number (default: 2) - CRON_REDIS_URL: Full URL (overrides host/port) - CRON_PREFIX: Key prefix (default: hanzo:cron) - CRON_TIMEZONE: Default timezone (default: UTC) - """ - return cls( - host=os.getenv("CRON_REDIS_HOST") or os.getenv("REDIS_HOST", "localhost"), - port=int(os.getenv("CRON_REDIS_PORT") or os.getenv("REDIS_PORT", "6379")), - password=os.getenv("CRON_REDIS_PASSWORD") or os.getenv("REDIS_PASSWORD"), - db=int(os.getenv("CRON_REDIS_DB", "2")), - url=os.getenv("CRON_REDIS_URL") or os.getenv("REDIS_URL"), - prefix=os.getenv("CRON_PREFIX", "hanzo:cron"), - timezone=os.getenv("CRON_TIMEZONE", "UTC"), - ) - - -@dataclass -class CronJob: - """A scheduled job definition.""" - - id: str - name: str - schedule: str # Cron expression or interval - handler: str # Handler name/identifier - args: dict[str, Any] = field(default_factory=dict) - enabled: bool = True - timezone: str = "UTC" - last_run: Optional[datetime] = None - next_run: Optional[datetime] = None - run_count: int = 0 - error_count: int = 0 - last_error: Optional[str] = None - metadata: dict[str, Any] = field(default_factory=dict) - - def to_dict(self) -> dict[str, Any]: - """Convert to dictionary for storage.""" - return { - "id": self.id, - "name": self.name, - "schedule": self.schedule, - "handler": self.handler, - "args": self.args, - "enabled": self.enabled, - "timezone": self.timezone, - "last_run": self.last_run.isoformat() if self.last_run else None, - "next_run": self.next_run.isoformat() if self.next_run else None, - "run_count": self.run_count, - "error_count": self.error_count, - "last_error": self.last_error, - "metadata": self.metadata, - } - - @classmethod - def from_dict(cls, data: dict[str, Any]) -> CronJob: - """Create from dictionary.""" - return cls( - id=data["id"], - name=data["name"], - schedule=data["schedule"], - handler=data["handler"], - args=data.get("args", {}), - enabled=data.get("enabled", True), - timezone=data.get("timezone", "UTC"), - last_run=( - datetime.fromisoformat(data["last_run"]) - if data.get("last_run") - else None - ), - next_run=( - datetime.fromisoformat(data["next_run"]) - if data.get("next_run") - else None - ), - run_count=data.get("run_count", 0), - error_count=data.get("error_count", 0), - last_error=data.get("last_error"), - metadata=data.get("metadata", {}), - ) - - -@dataclass -class CronExecution: - """Record of a job execution.""" - - job_id: str - started_at: datetime - completed_at: Optional[datetime] = None - success: bool = False - result: Any = None - error: Optional[str] = None - duration_ms: int = 0 - - -CronHandler = Callable[[CronJob], Awaitable[Any]] - - -class CronClient: - """Async client for scheduled job management. - - Implements distributed cron scheduling on top of Redis, - with support for cron expressions, intervals, and distributed locking. - - Example: - ```python - client = CronClient(CronConfig.from_env()) - await client.connect() - - # Register a job - await client.register( - "cleanup", - "daily-cleanup", - schedule="0 2 * * *", # 2 AM daily - handler="cleanup_old_files", - args={"days": 30}, - ) - - # Or with interval - await client.register( - "heartbeat", - "service-heartbeat", - schedule="@every 5m", # Every 5 minutes - handler="send_heartbeat", - ) - - # Run the scheduler (in a worker) - handlers = { - "cleanup_old_files": cleanup_handler, - "send_heartbeat": heartbeat_handler, - } - await client.run_scheduler(handlers) - - # Manually trigger a job - await client.trigger("cleanup") - - # List jobs - jobs = await client.list_jobs() - for job in jobs: - print(f"{job.name}: next run at {job.next_run}") - ``` - """ - - def __init__(self, config: Optional[CronConfig] = None) -> None: - """Initialize cron client. - - Args: - config: Cron configuration. If None, loads from environment. - """ - self.config = config or CronConfig.from_env() - self._redis: Any = None - self._running = False - - async def connect(self) -> None: - """Establish connection to Redis.""" - try: - import redis.asyncio as redis - except ImportError as e: - raise ImportError( - "redis is required for CronClient. Install with: pip install redis" - ) from e - - if self.config.url: - self._redis = redis.from_url( - self.config.url, - decode_responses=True, - ) - else: - self._redis = redis.Redis( - host=self.config.host, - port=self.config.port, - password=self.config.password, - db=self.config.db, - decode_responses=True, - ) - - async def close(self) -> None: - """Close the connection.""" - self._running = False - if self._redis: - await self._redis.aclose() - self._redis = None - - async def health_check(self) -> bool: - """Check if Redis is healthy. - - Returns: - True if Redis is responding. - """ - if not self._redis: - return False - try: - await self._redis.ping() - return True - except Exception: - return False - - def _key(self, *parts: str) -> str: - """Build a Redis key with prefix.""" - return ":".join([self.config.prefix, *parts]) - - def _parse_schedule(self, schedule: str) -> datetime: - """Parse a schedule and return the next run time. - - Supports: - - Cron expressions: "* * * * *" (min hour day month weekday) - - Intervals: "@every 5m", "@every 1h", "@every 30s" - - Named schedules: "@hourly", "@daily", "@weekly", "@monthly" - """ - now = datetime.utcnow() - - # Handle interval syntax - if schedule.startswith("@every "): - interval_str = schedule[7:] - return now + self._parse_interval(interval_str) - - # Handle named schedules - named = { - "@hourly": "0 * * * *", - "@daily": "0 0 * * *", - "@weekly": "0 0 * * 0", - "@monthly": "0 0 1 * *", - "@yearly": "0 0 1 1 *", - "@annually": "0 0 1 1 *", - } - if schedule in named: - schedule = named[schedule] - - # Parse cron expression - try: - from croniter import croniter - except ImportError: - # Fallback: simple interval-based scheduling - # Default to hourly if croniter not available - return now + timedelta(hours=1) - - cron = croniter(schedule, now) - return cron.get_next(datetime) - - def _parse_interval(self, interval: str) -> timedelta: - """Parse an interval string like '5m', '1h', '30s'.""" - units = { - "s": 1, - "m": 60, - "h": 3600, - "d": 86400, - "w": 604800, - } - - if not interval: - return timedelta(hours=1) - - unit = interval[-1].lower() - if unit not in units: - raise ValueError(f"Unknown interval unit: {unit}") - - try: - value = int(interval[:-1]) - except ValueError: - raise ValueError(f"Invalid interval value: {interval[:-1]}") - - return timedelta(seconds=value * units[unit]) - - # Job management - - async def register( - self, - job_id: str, - name: str, - schedule: str, - handler: str, - args: Optional[dict[str, Any]] = None, - timezone: Optional[str] = None, - enabled: bool = True, - metadata: Optional[dict[str, Any]] = None, - ) -> CronJob: - """Register a scheduled job. - - Args: - job_id: Unique job identifier. - name: Human-readable job name. - schedule: Cron expression or interval. - handler: Handler name/identifier. - args: Arguments to pass to handler. - timezone: Job timezone. - enabled: Whether job is enabled. - metadata: Additional metadata. - - Returns: - Created CronJob. - """ - next_run = self._parse_schedule(schedule) if enabled else None - - job = CronJob( - id=job_id, - name=name, - schedule=schedule, - handler=handler, - args=args or {}, - enabled=enabled, - timezone=timezone or self.config.timezone, - next_run=next_run, - metadata=metadata or {}, - ) - - # Store job - job_key = self._key("job", job_id) - await self._redis.set(job_key, json.dumps(job.to_dict())) - - # Add to jobs set - jobs_key = self._key("jobs") - await self._redis.sadd(jobs_key, job_id) - - # Add to schedule - if enabled and next_run: - schedule_key = self._key("schedule") - await self._redis.zadd(schedule_key, {job_id: next_run.timestamp()}) - - return job - - async def unregister(self, job_id: str) -> bool: - """Unregister a job. - - Args: - job_id: Job ID to remove. - - Returns: - True if job was removed. - """ - job_key = self._key("job", job_id) - jobs_key = self._key("jobs") - schedule_key = self._key("schedule") - - existed = await self._redis.delete(job_key) - await self._redis.srem(jobs_key, job_id) - await self._redis.zrem(schedule_key, job_id) - - return existed > 0 - - async def get_job(self, job_id: str) -> Optional[CronJob]: - """Get a job by ID. - - Args: - job_id: Job ID. - - Returns: - CronJob or None. - """ - job_key = self._key("job", job_id) - data = await self._redis.get(job_key) - if not data: - return None - return CronJob.from_dict(json.loads(data)) - - async def list_jobs(self) -> list[CronJob]: - """List all registered jobs. - - Returns: - List of CronJob objects. - """ - jobs_key = self._key("jobs") - job_ids = await self._redis.smembers(jobs_key) - - jobs = [] - for job_id in job_ids: - job = await self.get_job(job_id) - if job: - jobs.append(job) - - return sorted(jobs, key=lambda j: j.name) - - async def enable_job(self, job_id: str) -> bool: - """Enable a job. - - Args: - job_id: Job ID. - - Returns: - True if job was enabled. - """ - job = await self.get_job(job_id) - if not job: - return False - - job.enabled = True - job.next_run = self._parse_schedule(job.schedule) - - # Update job - job_key = self._key("job", job_id) - await self._redis.set(job_key, json.dumps(job.to_dict())) - - # Add to schedule - schedule_key = self._key("schedule") - await self._redis.zadd(schedule_key, {job_id: job.next_run.timestamp()}) - - return True - - async def disable_job(self, job_id: str) -> bool: - """Disable a job. - - Args: - job_id: Job ID. - - Returns: - True if job was disabled. - """ - job = await self.get_job(job_id) - if not job: - return False - - job.enabled = False - job.next_run = None - - # Update job - job_key = self._key("job", job_id) - await self._redis.set(job_key, json.dumps(job.to_dict())) - - # Remove from schedule - schedule_key = self._key("schedule") - await self._redis.zrem(schedule_key, job_id) - - return True - - async def trigger(self, job_id: str) -> Optional[CronExecution]: - """Manually trigger a job execution. - - Args: - job_id: Job ID to trigger. - - Returns: - Execution result or None if job not found. - """ - job = await self.get_job(job_id) - if not job: - return None - - # Schedule for immediate execution - schedule_key = self._key("schedule") - await self._redis.zadd(schedule_key, {job_id: datetime.utcnow().timestamp()}) - - return CronExecution( - job_id=job_id, - started_at=datetime.utcnow(), - ) - - # Scheduler - - async def _acquire_lock(self, job_id: str) -> bool: - """Acquire a distributed lock for a job.""" - lock_key = self._key("lock", job_id) - lock_value = hashlib.md5( - f"{os.getpid()}-{datetime.utcnow().isoformat()}".encode() - ).hexdigest() - - acquired = await self._redis.set( - lock_key, - lock_value, - ex=self.config.lock_timeout, - nx=True, - ) - return acquired is not None - - async def _release_lock(self, job_id: str) -> None: - """Release a distributed lock.""" - lock_key = self._key("lock", job_id) - await self._redis.delete(lock_key) - - async def _execute_job( - self, - job: CronJob, - handlers: dict[str, CronHandler], - ) -> CronExecution: - """Execute a job.""" - execution = CronExecution( - job_id=job.id, - started_at=datetime.utcnow(), - ) - - handler = handlers.get(job.handler) - if not handler: - execution.error = f"Handler not found: {job.handler}" - return execution - - try: - result = await handler(job) - execution.success = True - execution.result = result - except Exception as e: - execution.success = False - execution.error = str(e) - - execution.completed_at = datetime.utcnow() - execution.duration_ms = int( - (execution.completed_at - execution.started_at).total_seconds() * 1000 - ) - - # Update job stats - job.last_run = execution.started_at - job.run_count += 1 - if not execution.success: - job.error_count += 1 - job.last_error = execution.error - - # Schedule next run - job.next_run = self._parse_schedule(job.schedule) - - # Save job - job_key = self._key("job", job.id) - await self._redis.set(job_key, json.dumps(job.to_dict())) - - # Update schedule - schedule_key = self._key("schedule") - await self._redis.zadd(schedule_key, {job.id: job.next_run.timestamp()}) - - return execution - - async def run_scheduler( - self, - handlers: dict[str, CronHandler], - poll_interval: float = 1.0, - ) -> None: - """Run the cron scheduler (blocking). - - Args: - handlers: Map of handler names to functions. - poll_interval: Seconds between schedule checks. - """ - self._running = True - schedule_key = self._key("schedule") - - while self._running: - try: - now = datetime.utcnow().timestamp() - - # Get jobs due to run - due_jobs = await self._redis.zrangebyscore(schedule_key, "-inf", now) - - for job_id in due_jobs: - # Try to acquire lock - if not await self._acquire_lock(job_id): - continue # Another worker has this job - - try: - job = await self.get_job(job_id) - if job and job.enabled: - await self._execute_job(job, handlers) - finally: - await self._release_lock(job_id) - - await asyncio.sleep(poll_interval) - - except asyncio.CancelledError: - break - except Exception: - # Log error but keep running - await asyncio.sleep(poll_interval) - - def stop_scheduler(self) -> None: - """Stop the scheduler.""" - self._running = False - - # History (optional - for debugging) - - async def get_recent_executions( - self, - job_id: str, - limit: int = 10, - ) -> list[CronExecution]: - """Get recent executions for a job. - - Args: - job_id: Job ID. - limit: Maximum executions to return. - - Returns: - List of recent executions (most recent first). - """ - history_key = self._key("history", job_id) - entries = await self._redis.lrange(history_key, 0, limit - 1) - - executions = [] - for entry in entries: - data = json.loads(entry) - executions.append( - CronExecution( - job_id=data["job_id"], - started_at=datetime.fromisoformat(data["started_at"]), - completed_at=( - datetime.fromisoformat(data["completed_at"]) - if data.get("completed_at") - else None - ), - success=data.get("success", False), - result=data.get("result"), - error=data.get("error"), - duration_ms=data.get("duration_ms", 0), - ) - ) - - return executions - - async def __aenter__(self) -> CronClient: - """Async context manager entry.""" - await self.connect() - return self - - async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: - """Async context manager exit.""" - await self.close() diff --git a/pkg/hanzo/src/hanzo/infra/documentdb.py b/pkg/hanzo/src/hanzo/infra/documentdb.py deleted file mode 100644 index e7f8eb5f4..000000000 --- a/pkg/hanzo/src/hanzo/infra/documentdb.py +++ /dev/null @@ -1,572 +0,0 @@ -"""MongoDB document database client wrapper for Hanzo infrastructure. - -Provides async interface to MongoDB for document storage and querying, -supporting both MongoDB Atlas and self-hosted deployments. -""" - -from __future__ import annotations - -import os -from typing import Any, TypeVar, Optional, Sequence -from datetime import datetime -from dataclasses import field, dataclass - -from pydantic import Field, BaseModel - -T = TypeVar("T", bound=dict[str, Any]) - - -class DocumentDBConfig(BaseModel): - """Configuration for MongoDB connection.""" - - host: str = Field(default="localhost", description="MongoDB server host") - port: int = Field(default=27017, description="MongoDB server port") - username: Optional[str] = Field(default=None, description="Authentication username") - password: Optional[str] = Field(default=None, description="Authentication password") - database: str = Field(default="hanzo", description="Default database name") - uri: Optional[str] = Field( - default=None, description="Full connection URI (overrides host/port)" - ) - auth_source: str = Field(default="admin", description="Authentication database") - replica_set: Optional[str] = Field(default=None, description="Replica set name") - tls: bool = Field(default=False, description="Use TLS/SSL") - server_selection_timeout_ms: int = Field( - default=5000, description="Server selection timeout" - ) - connect_timeout_ms: int = Field(default=5000, description="Connection timeout") - socket_timeout_ms: int = Field(default=30000, description="Socket timeout") - - @classmethod - def from_env(cls) -> DocumentDBConfig: - """Create config from environment variables. - - Environment variables: - MONGODB_HOST: Server host (default: localhost) - MONGODB_PORT: Server port (default: 27017) - MONGODB_USERNAME: Authentication username - MONGODB_PASSWORD: Authentication password - MONGODB_DATABASE: Database name (default: hanzo) - MONGODB_URI: Full connection URI (overrides host/port) - MONGODB_TLS: Use TLS (default: false) - """ - return cls( - host=os.getenv("MONGODB_HOST", "localhost"), - port=int(os.getenv("MONGODB_PORT", "27017")), - username=os.getenv("MONGODB_USERNAME"), - password=os.getenv("MONGODB_PASSWORD"), - database=os.getenv("MONGODB_DATABASE", "hanzo"), - uri=os.getenv("MONGODB_URI"), - tls=os.getenv("MONGODB_TLS", "").lower() in ("true", "1", "yes"), - ) - - -@dataclass -class Document: - """A MongoDB document with metadata.""" - - id: str - data: dict[str, Any] - collection: str - created_at: Optional[datetime] = None - updated_at: Optional[datetime] = None - - -@dataclass -class QueryResult: - """Result of a query operation.""" - - documents: list[dict[str, Any]] - count: int - has_more: bool = False - - -@dataclass -class UpdateResult: - """Result of an update operation.""" - - matched_count: int - modified_count: int - upserted_id: Optional[str] = None - - -@dataclass -class DeleteResult: - """Result of a delete operation.""" - - deleted_count: int - - -class DocumentDBClient: - """Async client for MongoDB document database. - - Wraps motor (async MongoDB driver) with Hanzo conventions. - - Example: - ```python - client = DocumentDBClient(DocumentDBConfig.from_env()) - await client.connect() - - # Insert documents - doc_id = await client.insert_one("users", {"name": "Alice", "email": "alice@example.com"}) - - # Find documents - users = await client.find("users", {"name": "Alice"}) - - # Update documents - result = await client.update_one("users", {"_id": doc_id}, {"$set": {"status": "active"}}) - - # Aggregate - pipeline = [{"$group": {"_id": "$status", "count": {"$sum": 1}}}] - stats = await client.aggregate("users", pipeline) - ``` - """ - - def __init__(self, config: Optional[DocumentDBConfig] = None) -> None: - """Initialize document database client. - - Args: - config: MongoDB configuration. If None, loads from environment. - """ - self.config = config or DocumentDBConfig.from_env() - self._client: Any = None - self._db: Any = None - - async def connect(self) -> None: - """Establish connection to MongoDB server.""" - try: - from motor.motor_asyncio import AsyncIOMotorClient - except ImportError as e: - raise ImportError( - "motor is required for DocumentDBClient. Install with: pip install motor" - ) from e - - if self.config.uri: - self._client = AsyncIOMotorClient( - self.config.uri, - serverSelectionTimeoutMS=self.config.server_selection_timeout_ms, - connectTimeoutMS=self.config.connect_timeout_ms, - socketTimeoutMS=self.config.socket_timeout_ms, - ) - else: - self._client = AsyncIOMotorClient( - host=self.config.host, - port=self.config.port, - username=self.config.username, - password=self.config.password, - authSource=self.config.auth_source, - replicaSet=self.config.replica_set, - tls=self.config.tls, - serverSelectionTimeoutMS=self.config.server_selection_timeout_ms, - connectTimeoutMS=self.config.connect_timeout_ms, - socketTimeoutMS=self.config.socket_timeout_ms, - ) - - self._db = self._client[self.config.database] - - async def close(self) -> None: - """Close the connection.""" - if self._client: - self._client.close() - self._client = None - self._db = None - - async def health_check(self) -> bool: - """Check if MongoDB server is healthy. - - Returns: - True if server is reachable and responding. - """ - if not self._client: - return False - try: - await self._client.admin.command("ping") - return True - except Exception: - return False - - def _get_collection(self, collection: str) -> Any: - """Get a collection object.""" - return self._db[collection] - - # Insert operations - - async def insert_one(self, collection: str, document: dict[str, Any]) -> str: - """Insert a single document. - - Args: - collection: Collection name. - document: Document to insert. - - Returns: - Inserted document ID as string. - """ - coll = self._get_collection(collection) - result = await coll.insert_one(document) - return str(result.inserted_id) - - async def insert_many( - self, collection: str, documents: Sequence[dict[str, Any]] - ) -> list[str]: - """Insert multiple documents. - - Args: - collection: Collection name. - documents: Documents to insert. - - Returns: - List of inserted document IDs. - """ - coll = self._get_collection(collection) - result = await coll.insert_many(list(documents)) - return [str(id_) for id_ in result.inserted_ids] - - # Find operations - - async def find_one( - self, - collection: str, - filter: dict[str, Any], - projection: Optional[dict[str, Any]] = None, - ) -> Optional[dict[str, Any]]: - """Find a single document. - - Args: - collection: Collection name. - filter: Query filter. - projection: Fields to include/exclude. - - Returns: - Document or None if not found. - """ - coll = self._get_collection(collection) - return await coll.find_one(filter, projection) - - async def find( - self, - collection: str, - filter: dict[str, Any], - projection: Optional[dict[str, Any]] = None, - sort: Optional[list[tuple[str, int]]] = None, - skip: int = 0, - limit: int = 0, - ) -> list[dict[str, Any]]: - """Find documents matching a filter. - - Args: - collection: Collection name. - filter: Query filter. - projection: Fields to include/exclude. - sort: Sort specification [(field, direction), ...]. - skip: Number of documents to skip. - limit: Maximum documents to return (0 = no limit). - - Returns: - List of matching documents. - """ - coll = self._get_collection(collection) - cursor = coll.find(filter, projection) - - if sort: - cursor = cursor.sort(sort) - if skip: - cursor = cursor.skip(skip) - if limit: - cursor = cursor.limit(limit) - - return await cursor.to_list(length=limit if limit else None) - - async def find_by_id( - self, - collection: str, - id: str, - projection: Optional[dict[str, Any]] = None, - ) -> Optional[dict[str, Any]]: - """Find a document by ID. - - Args: - collection: Collection name. - id: Document ID. - projection: Fields to include/exclude. - - Returns: - Document or None if not found. - """ - from bson import ObjectId - - return await self.find_one(collection, {"_id": ObjectId(id)}, projection) - - # Update operations - - async def update_one( - self, - collection: str, - filter: dict[str, Any], - update: dict[str, Any], - upsert: bool = False, - ) -> UpdateResult: - """Update a single document. - - Args: - collection: Collection name. - filter: Query filter. - update: Update operations. - upsert: Insert if not found. - - Returns: - Update result with counts. - """ - coll = self._get_collection(collection) - result = await coll.update_one(filter, update, upsert=upsert) - return UpdateResult( - matched_count=result.matched_count, - modified_count=result.modified_count, - upserted_id=str(result.upserted_id) if result.upserted_id else None, - ) - - async def update_many( - self, - collection: str, - filter: dict[str, Any], - update: dict[str, Any], - upsert: bool = False, - ) -> UpdateResult: - """Update multiple documents. - - Args: - collection: Collection name. - filter: Query filter. - update: Update operations. - upsert: Insert if not found. - - Returns: - Update result with counts. - """ - coll = self._get_collection(collection) - result = await coll.update_many(filter, update, upsert=upsert) - return UpdateResult( - matched_count=result.matched_count, - modified_count=result.modified_count, - upserted_id=str(result.upserted_id) if result.upserted_id else None, - ) - - async def replace_one( - self, - collection: str, - filter: dict[str, Any], - replacement: dict[str, Any], - upsert: bool = False, - ) -> UpdateResult: - """Replace a single document. - - Args: - collection: Collection name. - filter: Query filter. - replacement: New document. - upsert: Insert if not found. - - Returns: - Update result with counts. - """ - coll = self._get_collection(collection) - result = await coll.replace_one(filter, replacement, upsert=upsert) - return UpdateResult( - matched_count=result.matched_count, - modified_count=result.modified_count, - upserted_id=str(result.upserted_id) if result.upserted_id else None, - ) - - # Delete operations - - async def delete_one(self, collection: str, filter: dict[str, Any]) -> DeleteResult: - """Delete a single document. - - Args: - collection: Collection name. - filter: Query filter. - - Returns: - Delete result with count. - """ - coll = self._get_collection(collection) - result = await coll.delete_one(filter) - return DeleteResult(deleted_count=result.deleted_count) - - async def delete_many( - self, collection: str, filter: dict[str, Any] - ) -> DeleteResult: - """Delete multiple documents. - - Args: - collection: Collection name. - filter: Query filter. - - Returns: - Delete result with count. - """ - coll = self._get_collection(collection) - result = await coll.delete_many(filter) - return DeleteResult(deleted_count=result.deleted_count) - - async def delete_by_id(self, collection: str, id: str) -> DeleteResult: - """Delete a document by ID. - - Args: - collection: Collection name. - id: Document ID. - - Returns: - Delete result with count. - """ - from bson import ObjectId - - return await self.delete_one(collection, {"_id": ObjectId(id)}) - - # Aggregation - - async def aggregate( - self, - collection: str, - pipeline: list[dict[str, Any]], - ) -> list[dict[str, Any]]: - """Run an aggregation pipeline. - - Args: - collection: Collection name. - pipeline: Aggregation pipeline stages. - - Returns: - List of aggregation results. - """ - coll = self._get_collection(collection) - cursor = coll.aggregate(pipeline) - return await cursor.to_list(length=None) - - # Count operations - - async def count_documents( - self, - collection: str, - filter: Optional[dict[str, Any]] = None, - ) -> int: - """Count documents matching a filter. - - Args: - collection: Collection name. - filter: Query filter (empty for all). - - Returns: - Number of matching documents. - """ - coll = self._get_collection(collection) - return await coll.count_documents(filter or {}) - - async def estimated_document_count(self, collection: str) -> int: - """Get estimated document count (faster than count_documents). - - Args: - collection: Collection name. - - Returns: - Estimated number of documents. - """ - coll = self._get_collection(collection) - return await coll.estimated_document_count() - - # Index operations - - async def create_index( - self, - collection: str, - keys: list[tuple[str, int]], - unique: bool = False, - name: Optional[str] = None, - ) -> str: - """Create an index. - - Args: - collection: Collection name. - keys: Index keys [(field, direction), ...]. - unique: Unique index. - name: Index name. - - Returns: - Created index name. - """ - coll = self._get_collection(collection) - return await coll.create_index(keys, unique=unique, name=name) - - async def drop_index(self, collection: str, name: str) -> None: - """Drop an index. - - Args: - collection: Collection name. - name: Index name. - """ - coll = self._get_collection(collection) - await coll.drop_index(name) - - async def list_indexes(self, collection: str) -> list[dict[str, Any]]: - """List all indexes on a collection. - - Args: - collection: Collection name. - - Returns: - List of index specifications. - """ - coll = self._get_collection(collection) - cursor = coll.list_indexes() - return await cursor.to_list(length=None) - - # Collection operations - - async def list_collections(self) -> list[str]: - """List all collections in the database. - - Returns: - List of collection names. - """ - return await self._db.list_collection_names() - - async def create_collection(self, name: str) -> None: - """Create a new collection. - - Args: - name: Collection name. - """ - await self._db.create_collection(name) - - async def drop_collection(self, name: str) -> None: - """Drop a collection. - - Args: - name: Collection name. - """ - await self._db.drop_collection(name) - - # Database operations - - def use_database(self, name: str) -> None: - """Switch to a different database. - - Args: - name: Database name. - """ - self._db = self._client[name] - - async def list_databases(self) -> list[str]: - """List all databases. - - Returns: - List of database names. - """ - return await self._client.list_database_names() - - async def __aenter__(self) -> DocumentDBClient: - """Async context manager entry.""" - await self.connect() - return self - - async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: - """Async context manager exit.""" - await self.close() diff --git a/pkg/hanzo/src/hanzo/infra/functions.py b/pkg/hanzo/src/hanzo/infra/functions.py deleted file mode 100644 index ea99aeeb8..000000000 --- a/pkg/hanzo/src/hanzo/infra/functions.py +++ /dev/null @@ -1,552 +0,0 @@ -"""Nuclio serverless functions client wrapper for Hanzo infrastructure. - -Provides async interface to Nuclio for deploying and invoking -serverless functions with auto-scaling and GPU support. -""" - -from __future__ import annotations - -import os -from typing import Any, Optional -from datetime import datetime -from dataclasses import field, dataclass - -from pydantic import Field, BaseModel - - -class FunctionsConfig(BaseModel): - """Configuration for Nuclio connection.""" - - dashboard_url: str = Field( - default="http://localhost:8070", description="Nuclio dashboard URL" - ) - api_key: Optional[str] = Field( - default=None, description="API key for authentication" - ) - namespace: str = Field(default="nuclio", description="Kubernetes namespace") - default_runtime: str = Field( - default="python:3.11", description="Default function runtime" - ) - default_handler: str = Field( - default="main:handler", description="Default function handler" - ) - registry: Optional[str] = Field(default=None, description="Container registry URL") - timeout: float = Field(default=30.0, description="Request timeout in seconds") - - @classmethod - def from_env(cls) -> FunctionsConfig: - """Create config from environment variables. - - Environment variables: - NUCLIO_DASHBOARD_URL: Dashboard URL (default: http://localhost:8070) - NUCLIO_API_KEY: API key for authentication - NUCLIO_NAMESPACE: Kubernetes namespace (default: nuclio) - NUCLIO_RUNTIME: Default runtime (default: python:3.11) - NUCLIO_REGISTRY: Container registry URL - """ - return cls( - dashboard_url=os.getenv("NUCLIO_DASHBOARD_URL", "http://localhost:8070"), - api_key=os.getenv("NUCLIO_API_KEY"), - namespace=os.getenv("NUCLIO_NAMESPACE", "nuclio"), - default_runtime=os.getenv("NUCLIO_RUNTIME", "python:3.11"), - registry=os.getenv("NUCLIO_REGISTRY"), - ) - - -@dataclass -class FunctionSpec: - """Specification for a Nuclio function.""" - - name: str - handler: str = "main:handler" - runtime: str = "python:3.11" - code: Optional[str] = None # Inline code - code_path: Optional[str] = None # Path to code directory - image: Optional[str] = None # Pre-built image - env: dict[str, str] = field(default_factory=dict) - min_replicas: int = 0 - max_replicas: int = 10 - target_cpu: int = 75 # CPU utilization target for scaling - triggers: dict[str, Any] = field(default_factory=dict) - resources: dict[str, Any] = field(default_factory=dict) - build_commands: list[str] = field(default_factory=list) - requirements: list[str] = field(default_factory=list) # pip requirements - labels: dict[str, str] = field(default_factory=dict) - annotations: dict[str, str] = field(default_factory=dict) - - def to_nuclio_spec(self) -> dict[str, Any]: - """Convert to Nuclio API spec format.""" - spec: dict[str, Any] = { - "spec": { - "handler": self.handler, - "runtime": self.runtime, - "minReplicas": self.min_replicas, - "maxReplicas": self.max_replicas, - "targetCPU": self.target_cpu, - "env": [{"name": k, "value": v} for k, v in self.env.items()], - }, - "metadata": { - "name": self.name, - "labels": self.labels, - "annotations": self.annotations, - }, - } - - if self.code: - spec["spec"]["build"] = { - "functionSourceCode": self.code, - } - elif self.code_path: - spec["spec"]["build"] = { - "path": self.code_path, - } - elif self.image: - spec["spec"]["image"] = self.image - - if self.build_commands: - spec["spec"]["build"] = spec["spec"].get("build", {}) - spec["spec"]["build"]["commands"] = self.build_commands - - if self.requirements: - # Add pip install to build commands - pip_cmd = f"pip install {' '.join(self.requirements)}" - commands = spec["spec"].get("build", {}).get("commands", []) - commands.insert(0, pip_cmd) - spec["spec"]["build"] = spec["spec"].get("build", {}) - spec["spec"]["build"]["commands"] = commands - - if self.triggers: - spec["spec"]["triggers"] = self.triggers - - if self.resources: - spec["spec"]["resources"] = self.resources - - return spec - - -@dataclass -class FunctionStatus: - """Status of a deployed function.""" - - name: str - state: str # ready, building, error, etc. - replicas: int = 0 - version: str = "" - invoke_url: Optional[str] = None - internal_url: Optional[str] = None - created_at: Optional[datetime] = None - updated_at: Optional[datetime] = None - message: Optional[str] = None - - -@dataclass -class InvokeResult: - """Result of a function invocation.""" - - status_code: int - body: Any - headers: dict[str, str] = field(default_factory=dict) - duration_ms: int = 0 - - -class FunctionsClient: - """Async client for Nuclio serverless functions. - - Wraps Nuclio HTTP API for deploying, managing, and invoking - serverless functions with Hanzo conventions. - - Example: - ```python - client = FunctionsClient(FunctionsConfig.from_env()) - await client.connect() - - # Deploy a function - spec = FunctionSpec( - name="hello", - code=''' - def handler(context, event): - return "Hello, " + event.body.decode() - ''', - runtime="python:3.11", - ) - await client.deploy(spec) - - # Invoke the function - result = await client.invoke("hello", body=b"World") - print(result.body) # "Hello, World" - - # List functions - functions = await client.list_functions() - for fn in functions: - print(f"{fn.name}: {fn.state}") - ``` - """ - - def __init__(self, config: Optional[FunctionsConfig] = None) -> None: - """Initialize functions client. - - Args: - config: Nuclio configuration. If None, loads from environment. - """ - self.config = config or FunctionsConfig.from_env() - self._client: Any = None - - async def connect(self) -> None: - """Establish connection (validates connectivity).""" - try: - import httpx - except ImportError as e: - raise ImportError( - "httpx is required for FunctionsClient. Install with: pip install httpx" - ) from e - - self._client = httpx.AsyncClient( - base_url=self.config.dashboard_url, - timeout=self.config.timeout, - headers=self._get_headers(), - ) - - def _get_headers(self) -> dict[str, str]: - """Get request headers.""" - headers = { - "Content-Type": "application/json", - } - if self.config.api_key: - headers["X-nuclio-project-name"] = "default" - headers["X-v3io-session-key"] = self.config.api_key - return headers - - async def close(self) -> None: - """Close the connection.""" - if self._client: - await self._client.aclose() - self._client = None - - async def health_check(self) -> bool: - """Check if Nuclio is healthy. - - Returns: - True if Nuclio dashboard is reachable. - """ - if not self._client: - return False - try: - response = await self._client.get("/api/projects") - return response.status_code == 200 - except Exception: - return False - - # Function management - - async def deploy( - self, - spec: FunctionSpec, - wait: bool = True, - timeout: float = 300.0, - ) -> FunctionStatus: - """Deploy a function. - - Args: - spec: Function specification. - wait: Wait for deployment to complete. - timeout: Deployment timeout in seconds. - - Returns: - Function status after deployment. - """ - import asyncio - - # Create/update function - nuclio_spec = spec.to_nuclio_spec() - response = await self._client.post( - f"/api/functions/{spec.name}", - json=nuclio_spec, - params={"namespace": self.config.namespace}, - ) - - if response.status_code not in (200, 201, 202): - raise Exception(f"Failed to deploy function: {response.text}") - - if wait: - # Poll until ready or timeout - start_time = datetime.utcnow() - while (datetime.utcnow() - start_time).total_seconds() < timeout: - status = await self.get_function(spec.name) - if status.state in ("ready", "imported"): - return status - if status.state == "error": - raise Exception(f"Function deployment failed: {status.message}") - await asyncio.sleep(2) - - raise TimeoutError(f"Function deployment timed out after {timeout}s") - - return await self.get_function(spec.name) - - async def delete_function(self, name: str) -> bool: - """Delete a function. - - Args: - name: Function name. - - Returns: - True if function was deleted. - """ - response = await self._client.delete( - f"/api/functions/{name}", - params={"namespace": self.config.namespace}, - ) - return response.status_code in (200, 204) - - async def get_function(self, name: str) -> FunctionStatus: - """Get function status. - - Args: - name: Function name. - - Returns: - Function status. - """ - response = await self._client.get( - f"/api/functions/{name}", - params={"namespace": self.config.namespace}, - ) - - if response.status_code == 404: - raise Exception(f"Function not found: {name}") - - data = response.json() - status = data.get("status", {}) - metadata = data.get("metadata", {}) - - return FunctionStatus( - name=name, - state=status.get("state", "unknown"), - replicas=status.get("replicas", 0), - version=metadata.get("version", ""), - invoke_url=status.get("httpPort"), - internal_url=( - status.get("internalInvocationUrls", [None])[0] - if status.get("internalInvocationUrls") - else None - ), - message=status.get("message"), - ) - - async def list_functions(self) -> list[FunctionStatus]: - """List all functions. - - Returns: - List of function statuses. - """ - response = await self._client.get( - "/api/functions", - params={"namespace": self.config.namespace}, - ) - - if response.status_code != 200: - raise Exception(f"Failed to list functions: {response.text}") - - functions = [] - for name, data in response.json().items(): - status = data.get("status", {}) - metadata = data.get("metadata", {}) - functions.append( - FunctionStatus( - name=name, - state=status.get("state", "unknown"), - replicas=status.get("replicas", 0), - version=metadata.get("version", ""), - invoke_url=status.get("httpPort"), - message=status.get("message"), - ) - ) - - return functions - - # Function invocation - - async def invoke( - self, - name: str, - body: bytes = b"", - method: str = "POST", - path: str = "", - headers: Optional[dict[str, str]] = None, - query: Optional[dict[str, str]] = None, - ) -> InvokeResult: - """Invoke a function. - - Args: - name: Function name. - body: Request body. - method: HTTP method. - path: Request path within function. - headers: Additional headers. - query: Query parameters. - - Returns: - Invocation result. - """ - import time - - # Get function invoke URL - status = await self.get_function(name) - if not status.invoke_url and not status.internal_url: - raise Exception(f"Function {name} has no invoke URL") - - # Determine invoke URL - if status.internal_url: - invoke_url = status.internal_url - else: - # Construct URL from dashboard URL and port - base = self.config.dashboard_url.rsplit(":", 1)[0] - invoke_url = f"{base}:{status.invoke_url}" - - # Build request URL - url = f"{invoke_url}/{path.lstrip('/')}" if path else invoke_url - - # Prepare headers - req_headers = headers or {} - - start = time.monotonic() - response = await self._client.request( - method=method, - url=url, - content=body, - headers=req_headers, - params=query, - ) - duration = int((time.monotonic() - start) * 1000) - - # Parse response - try: - response_body = response.json() - except Exception: - response_body = response.text - - return InvokeResult( - status_code=response.status_code, - body=response_body, - headers=dict(response.headers), - duration_ms=duration, - ) - - async def invoke_async( - self, - name: str, - body: bytes = b"", - headers: Optional[dict[str, str]] = None, - ) -> str: - """Invoke a function asynchronously. - - Args: - name: Function name. - body: Request body. - headers: Additional headers. - - Returns: - Invocation ID for checking status. - """ - req_headers = headers or {} - req_headers["X-Nuclio-Function-Async"] = "true" - - result = await self.invoke(name, body, headers=req_headers) - - # Return invocation ID from response - return result.headers.get("X-Nuclio-Invoke-Id", "") - - # Scaling - - async def scale_function( - self, - name: str, - replicas: Optional[int] = None, - min_replicas: Optional[int] = None, - max_replicas: Optional[int] = None, - ) -> FunctionStatus: - """Scale a function. - - Args: - name: Function name. - replicas: Fixed replica count (overrides auto-scaling). - min_replicas: Minimum replicas for auto-scaling. - max_replicas: Maximum replicas for auto-scaling. - - Returns: - Updated function status. - """ - # Get current function - response = await self._client.get( - f"/api/functions/{name}", - params={"namespace": self.config.namespace}, - ) - - if response.status_code == 404: - raise Exception(f"Function not found: {name}") - - data = response.json() - spec = data.get("spec", {}) - - # Update scaling config - if replicas is not None: - spec["replicas"] = replicas - if min_replicas is not None: - spec["minReplicas"] = min_replicas - if max_replicas is not None: - spec["maxReplicas"] = max_replicas - - # Update function - data["spec"] = spec - response = await self._client.put( - f"/api/functions/{name}", - json=data, - params={"namespace": self.config.namespace}, - ) - - if response.status_code not in (200, 202): - raise Exception(f"Failed to scale function: {response.text}") - - return await self.get_function(name) - - # Logs - - async def get_logs( - self, - name: str, - since: Optional[datetime] = None, - follow: bool = False, - ): - """Get function logs. - - Args: - name: Function name. - since: Get logs since this time. - follow: Stream logs (not implemented in basic version). - - Yields: - Log lines. - """ - params: dict[str, Any] = { - "namespace": self.config.namespace, - "function": name, - } - if since: - params["since"] = since.isoformat() - - response = await self._client.get("/api/logs", params=params) - - if response.status_code != 200: - raise Exception(f"Failed to get logs: {response.text}") - - for line in response.text.split("\n"): - if line.strip(): - yield line - - async def __aenter__(self) -> FunctionsClient: - """Async context manager entry.""" - await self.connect() - return self - - async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: - """Async context manager exit.""" - await self.close() diff --git a/pkg/hanzo/src/hanzo/infra/kv.py b/pkg/hanzo/src/hanzo/infra/kv.py deleted file mode 100644 index 56d394fa1..000000000 --- a/pkg/hanzo/src/hanzo/infra/kv.py +++ /dev/null @@ -1,533 +0,0 @@ -"""Redis/Valkey key-value store client wrapper for Hanzo infrastructure. - -Provides async interface to Redis/Valkey for caching, sessions, -and general key-value storage. -""" - -from __future__ import annotations - -import os -import json -from typing import Any, Union, Optional -from datetime import timedelta -from dataclasses import dataclass - -from pydantic import Field, BaseModel - - -class KVConfig(BaseModel): - """Configuration for Redis/Valkey connection.""" - - host: str = Field(default="localhost", description="Redis server host") - port: int = Field(default=6379, description="Redis server port") - password: Optional[str] = Field(default=None, description="Redis password") - db: int = Field(default=0, description="Redis database number") - url: Optional[str] = Field( - default=None, description="Full Redis URL (overrides host/port)" - ) - ssl: bool = Field(default=False, description="Use SSL/TLS") - socket_timeout: float = Field(default=5.0, description="Socket timeout in seconds") - socket_connect_timeout: float = Field(default=5.0, description="Connection timeout") - max_connections: int = Field(default=10, description="Max pool connections") - decode_responses: bool = Field( - default=True, description="Decode responses as strings" - ) - - @classmethod - def from_env(cls) -> KVConfig: - """Create config from environment variables. - - Environment variables: - REDIS_HOST: Server host (default: localhost) - REDIS_PORT: Server port (default: 6379) - REDIS_PASSWORD: Authentication password - REDIS_DB: Database number (default: 0) - REDIS_URL: Full URL (overrides host/port) - REDIS_SSL: Use SSL (default: false) - """ - return cls( - host=os.getenv("REDIS_HOST", "localhost"), - port=int(os.getenv("REDIS_PORT", "6379")), - password=os.getenv("REDIS_PASSWORD"), - db=int(os.getenv("REDIS_DB", "0")), - url=os.getenv("REDIS_URL"), - ssl=os.getenv("REDIS_SSL", "").lower() in ("true", "1", "yes"), - ) - - -@dataclass -class KVResult: - """Result of a KV operation with metadata.""" - - key: str - value: Any - ttl: Optional[int] = None # TTL in seconds, None if no expiry - - -class KVClient: - """Async client for Redis/Valkey key-value store. - - Wraps redis-py with async methods and Hanzo conventions. - Supports both Redis and Valkey (Redis-compatible). - - Example: - ```python - client = KVClient(KVConfig.from_env()) - await client.connect() - - # Basic operations - await client.set("key", "value", ttl=3600) - value = await client.get("key") - - # JSON operations - await client.set_json("config", {"theme": "dark"}) - config = await client.get_json("config") - - # Hash operations - await client.hset("user:1", {"name": "Alice", "email": "alice@example.com"}) - user = await client.hgetall("user:1") - ``` - """ - - def __init__(self, config: Optional[KVConfig] = None) -> None: - """Initialize KV client. - - Args: - config: Redis configuration. If None, loads from environment. - """ - self.config = config or KVConfig.from_env() - self._client: Any = None - - async def connect(self) -> None: - """Establish connection to Redis server.""" - try: - import redis.asyncio as redis - except ImportError as e: - raise ImportError( - "redis is required for KVClient. Install with: pip install redis" - ) from e - - if self.config.url: - self._client = redis.from_url( - self.config.url, - decode_responses=self.config.decode_responses, - socket_timeout=self.config.socket_timeout, - socket_connect_timeout=self.config.socket_connect_timeout, - max_connections=self.config.max_connections, - ) - else: - self._client = redis.Redis( - host=self.config.host, - port=self.config.port, - password=self.config.password, - db=self.config.db, - ssl=self.config.ssl, - decode_responses=self.config.decode_responses, - socket_timeout=self.config.socket_timeout, - socket_connect_timeout=self.config.socket_connect_timeout, - max_connections=self.config.max_connections, - ) - - async def close(self) -> None: - """Close the connection.""" - if self._client: - await self._client.aclose() - self._client = None - - async def health_check(self) -> bool: - """Check if Redis server is healthy. - - Returns: - True if server is reachable and responding. - """ - if not self._client: - return False - try: - await self._client.ping() - return True - except Exception: - return False - - # Basic string operations - - async def get(self, key: str) -> Optional[str]: - """Get a string value. - - Args: - key: Key to retrieve. - - Returns: - Value or None if not found. - """ - return await self._client.get(key) - - async def set( - self, - key: str, - value: str, - ttl: Optional[Union[int, timedelta]] = None, - nx: bool = False, - xx: bool = False, - ) -> bool: - """Set a string value. - - Args: - key: Key to set. - value: Value to store. - ttl: Time to live in seconds or timedelta. - nx: Only set if key does not exist. - xx: Only set if key already exists. - - Returns: - True if successful. - """ - ex = ttl if isinstance(ttl, int) else None - px = int(ttl.total_seconds() * 1000) if isinstance(ttl, timedelta) else None - - result = await self._client.set(key, value, ex=ex, px=px, nx=nx, xx=xx) - return result is not None - - async def delete(self, *keys: str) -> int: - """Delete one or more keys. - - Args: - keys: Keys to delete. - - Returns: - Number of keys deleted. - """ - return await self._client.delete(*keys) - - async def exists(self, *keys: str) -> int: - """Check if keys exist. - - Args: - keys: Keys to check. - - Returns: - Number of keys that exist. - """ - return await self._client.exists(*keys) - - async def expire(self, key: str, ttl: Union[int, timedelta]) -> bool: - """Set expiration on a key. - - Args: - key: Key to expire. - ttl: Time to live in seconds or timedelta. - - Returns: - True if expiration was set. - """ - seconds = ttl if isinstance(ttl, int) else int(ttl.total_seconds()) - return await self._client.expire(key, seconds) - - async def ttl(self, key: str) -> int: - """Get time to live for a key. - - Args: - key: Key to check. - - Returns: - TTL in seconds, -1 if no expiry, -2 if key doesn't exist. - """ - return await self._client.ttl(key) - - # JSON operations - - async def get_json(self, key: str) -> Optional[Any]: - """Get a JSON value. - - Args: - key: Key to retrieve. - - Returns: - Parsed JSON value or None if not found. - """ - value = await self.get(key) - if value is None: - return None - return json.loads(value) - - async def set_json( - self, - key: str, - value: Any, - ttl: Optional[Union[int, timedelta]] = None, - ) -> bool: - """Set a JSON value. - - Args: - key: Key to set. - value: Value to serialize as JSON. - ttl: Time to live. - - Returns: - True if successful. - """ - return await self.set(key, json.dumps(value), ttl=ttl) - - # Hash operations - - async def hget(self, name: str, key: str) -> Optional[str]: - """Get a hash field value. - - Args: - name: Hash name. - key: Field key. - - Returns: - Field value or None. - """ - return await self._client.hget(name, key) - - async def hset(self, name: str, mapping: dict[str, Any]) -> int: - """Set hash fields. - - Args: - name: Hash name. - mapping: Field-value pairs. - - Returns: - Number of fields added. - """ - return await self._client.hset(name, mapping=mapping) - - async def hgetall(self, name: str) -> dict[str, str]: - """Get all hash fields. - - Args: - name: Hash name. - - Returns: - Dictionary of field-value pairs. - """ - return await self._client.hgetall(name) - - async def hdel(self, name: str, *keys: str) -> int: - """Delete hash fields. - - Args: - name: Hash name. - keys: Fields to delete. - - Returns: - Number of fields deleted. - """ - return await self._client.hdel(name, *keys) - - # List operations - - async def lpush(self, key: str, *values: str) -> int: - """Push values to the head of a list. - - Args: - key: List key. - values: Values to push. - - Returns: - Length of list after push. - """ - return await self._client.lpush(key, *values) - - async def rpush(self, key: str, *values: str) -> int: - """Push values to the tail of a list. - - Args: - key: List key. - values: Values to push. - - Returns: - Length of list after push. - """ - return await self._client.rpush(key, *values) - - async def lpop( - self, key: str, count: Optional[int] = None - ) -> Optional[Union[str, list[str]]]: - """Pop values from the head of a list. - - Args: - key: List key. - count: Number of values to pop. - - Returns: - Popped value(s) or None. - """ - return await self._client.lpop(key, count) - - async def rpop( - self, key: str, count: Optional[int] = None - ) -> Optional[Union[str, list[str]]]: - """Pop values from the tail of a list. - - Args: - key: List key. - count: Number of values to pop. - - Returns: - Popped value(s) or None. - """ - return await self._client.rpop(key, count) - - async def lrange(self, key: str, start: int, end: int) -> list[str]: - """Get a range of list elements. - - Args: - key: List key. - start: Start index. - end: End index (-1 for last). - - Returns: - List of elements. - """ - return await self._client.lrange(key, start, end) - - async def llen(self, key: str) -> int: - """Get list length. - - Args: - key: List key. - - Returns: - Length of list. - """ - return await self._client.llen(key) - - # Set operations - - async def sadd(self, key: str, *members: str) -> int: - """Add members to a set. - - Args: - key: Set key. - members: Members to add. - - Returns: - Number of members added. - """ - return await self._client.sadd(key, *members) - - async def srem(self, key: str, *members: str) -> int: - """Remove members from a set. - - Args: - key: Set key. - members: Members to remove. - - Returns: - Number of members removed. - """ - return await self._client.srem(key, *members) - - async def smembers(self, key: str) -> set[str]: - """Get all members of a set. - - Args: - key: Set key. - - Returns: - Set of members. - """ - return await self._client.smembers(key) - - async def sismember(self, key: str, member: str) -> bool: - """Check if value is a set member. - - Args: - key: Set key. - member: Member to check. - - Returns: - True if member exists. - """ - return await self._client.sismember(key, member) - - # Atomic operations - - async def incr(self, key: str, amount: int = 1) -> int: - """Increment a value. - - Args: - key: Key to increment. - amount: Amount to increment by. - - Returns: - New value. - """ - return await self._client.incrby(key, amount) - - async def decr(self, key: str, amount: int = 1) -> int: - """Decrement a value. - - Args: - key: Key to decrement. - amount: Amount to decrement by. - - Returns: - New value. - """ - return await self._client.decrby(key, amount) - - # Pub/Sub (basic - for full pub/sub use PubSubClient) - - async def publish(self, channel: str, message: str) -> int: - """Publish a message to a channel. - - Args: - channel: Channel name. - message: Message to publish. - - Returns: - Number of subscribers that received the message. - """ - return await self._client.publish(channel, message) - - # Keys operations - - async def keys(self, pattern: str = "*") -> list[str]: - """Find keys matching a pattern. - - Args: - pattern: Glob-style pattern. - - Returns: - List of matching keys. - """ - return await self._client.keys(pattern) - - async def scan( - self, - cursor: int = 0, - match: Optional[str] = None, - count: int = 100, - ) -> tuple[int, list[str]]: - """Incrementally iterate keys. - - Args: - cursor: Cursor position. - match: Pattern to match. - count: Approximate number per iteration. - - Returns: - Tuple of (next_cursor, keys). - """ - return await self._client.scan(cursor, match=match, count=count) - - async def flushdb(self) -> bool: - """Flush the current database. - - Returns: - True if successful. - """ - await self._client.flushdb() - return True - - async def __aenter__(self) -> KVClient: - """Async context manager entry.""" - await self.connect() - return self - - async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: - """Async context manager exit.""" - await self.close() diff --git a/pkg/hanzo/src/hanzo/infra/pubsub.py b/pkg/hanzo/src/hanzo/infra/pubsub.py deleted file mode 100644 index 1916ebf84..000000000 --- a/pkg/hanzo/src/hanzo/infra/pubsub.py +++ /dev/null @@ -1,553 +0,0 @@ -"""NATS pub/sub client wrapper for Hanzo infrastructure. - -Provides async interface to NATS for messaging, pub/sub, -request/reply, and streaming with JetStream. -""" - -from __future__ import annotations - -import os -from typing import Any, Callable, Optional, Sequence, Awaitable, AsyncIterator -from datetime import datetime -from dataclasses import field, dataclass - -from pydantic import Field, BaseModel - - -class PubSubConfig(BaseModel): - """Configuration for NATS connection.""" - - servers: list[str] = Field( - default=["nats://localhost:4222"], description="NATS server URLs" - ) - user: Optional[str] = Field(default=None, description="Username for authentication") - password: Optional[str] = Field( - default=None, description="Password for authentication" - ) - token: Optional[str] = Field(default=None, description="Token for authentication") - nkey_seed: Optional[str] = Field( - default=None, description="NKey seed for authentication" - ) - tls: bool = Field(default=False, description="Use TLS") - name: str = Field(default="hanzo-client", description="Client name") - connect_timeout: float = Field( - default=5.0, description="Connection timeout in seconds" - ) - reconnect_time_wait: float = Field( - default=2.0, description="Time between reconnect attempts" - ) - max_reconnect_attempts: int = Field( - default=60, description="Max reconnection attempts" - ) - ping_interval: float = Field(default=120.0, description="Ping interval in seconds") - max_outstanding_pings: int = Field(default=2, description="Max outstanding pings") - - @classmethod - def from_env(cls) -> PubSubConfig: - """Create config from environment variables. - - Environment variables: - NATS_SERVERS: Comma-separated list of NATS URLs (default: nats://localhost:4222) - NATS_USER: Username for authentication - NATS_PASSWORD: Password for authentication - NATS_TOKEN: Token for authentication - NATS_NKEY_SEED: NKey seed for authentication - NATS_TLS: Use TLS (default: false) - """ - servers_str = os.getenv("NATS_SERVERS", "nats://localhost:4222") - servers = [s.strip() for s in servers_str.split(",")] - - return cls( - servers=servers, - user=os.getenv("NATS_USER"), - password=os.getenv("NATS_PASSWORD"), - token=os.getenv("NATS_TOKEN"), - nkey_seed=os.getenv("NATS_NKEY_SEED"), - tls=os.getenv("NATS_TLS", "").lower() in ("true", "1", "yes"), - ) - - -@dataclass -class Message: - """A NATS message.""" - - subject: str - data: bytes - reply: Optional[str] = None - headers: dict[str, str] = field(default_factory=dict) - timestamp: Optional[datetime] = None - - -@dataclass -class Subscription: - """A NATS subscription handle.""" - - subject: str - queue: Optional[str] = None - _sub: Any = None - - async def unsubscribe(self) -> None: - """Unsubscribe from the subject.""" - if self._sub: - await self._sub.unsubscribe() - - async def drain(self) -> None: - """Drain the subscription before unsubscribing.""" - if self._sub: - await self._sub.drain() - - -MessageHandler = Callable[[Message], Awaitable[None]] - - -class PubSubClient: - """Async client for NATS messaging. - - Wraps nats-py with Hanzo conventions for pub/sub, request/reply, - and JetStream operations. - - Example: - ```python - client = PubSubClient(PubSubConfig.from_env()) - await client.connect() - - - # Simple pub/sub - async def handler(msg: Message): - print(f"Received: {msg.data.decode()}") - - - sub = await client.subscribe("events.>", handler) - await client.publish("events.user.created", b"user123") - - # Request/reply - response = await client.request("api.users.get", b"user123", timeout=5.0) - - # JetStream (persistent messaging) - await client.js_create_stream("EVENTS", subjects=["events.*"]) - await client.js_publish("events.user", b"data") - ``` - """ - - def __init__(self, config: Optional[PubSubConfig] = None) -> None: - """Initialize pub/sub client. - - Args: - config: NATS configuration. If None, loads from environment. - """ - self.config = config or PubSubConfig.from_env() - self._nc: Any = None - self._js: Any = None - - async def connect(self) -> None: - """Establish connection to NATS server.""" - try: - import nats - except ImportError as e: - raise ImportError( - "nats-py is required for PubSubClient. Install with: pip install nats-py" - ) from e - - connect_opts: dict[str, Any] = { - "servers": self.config.servers, - "name": self.config.name, - "connect_timeout": self.config.connect_timeout, - "reconnect_time_wait": self.config.reconnect_time_wait, - "max_reconnect_attempts": self.config.max_reconnect_attempts, - "ping_interval": self.config.ping_interval, - "max_outstanding_pings": self.config.max_outstanding_pings, - } - - if self.config.user and self.config.password: - connect_opts["user"] = self.config.user - connect_opts["password"] = self.config.password - elif self.config.token: - connect_opts["token"] = self.config.token - elif self.config.nkey_seed: - connect_opts["nkeys_seed"] = self.config.nkey_seed - - self._nc = await nats.connect(**connect_opts) - - async def close(self) -> None: - """Close the connection.""" - if self._nc: - await self._nc.drain() - self._nc = None - self._js = None - - async def health_check(self) -> bool: - """Check if NATS connection is healthy. - - Returns: - True if connected and responding. - """ - if not self._nc: - return False - return self._nc.is_connected - - # Core pub/sub operations - - async def publish( - self, - subject: str, - data: bytes, - reply: Optional[str] = None, - headers: Optional[dict[str, str]] = None, - ) -> None: - """Publish a message to a subject. - - Args: - subject: Subject to publish to. - data: Message payload. - reply: Optional reply subject. - headers: Optional message headers. - """ - await self._nc.publish(subject, data, reply=reply, headers=headers) - - async def subscribe( - self, - subject: str, - handler: MessageHandler, - queue: Optional[str] = None, - ) -> Subscription: - """Subscribe to a subject. - - Args: - subject: Subject pattern (supports wildcards * and >). - handler: Async callback for received messages. - queue: Queue group for load balancing. - - Returns: - Subscription handle for unsubscribing. - """ - - async def _wrapper(msg: Any) -> None: - wrapped = Message( - subject=msg.subject, - data=msg.data, - reply=msg.reply, - headers=dict(msg.headers) if msg.headers else {}, - ) - await handler(wrapped) - - sub = await self._nc.subscribe(subject, cb=_wrapper, queue=queue or "") - return Subscription(subject=subject, queue=queue, _sub=sub) - - async def subscribe_iter( - self, - subject: str, - queue: Optional[str] = None, - ) -> AsyncIterator[Message]: - """Subscribe to a subject with async iteration. - - Args: - subject: Subject pattern. - queue: Queue group for load balancing. - - Yields: - Messages as they arrive. - """ - sub = await self._nc.subscribe(subject, queue=queue or "") - try: - async for msg in sub.messages: - yield Message( - subject=msg.subject, - data=msg.data, - reply=msg.reply, - headers=dict(msg.headers) if msg.headers else {}, - ) - finally: - await sub.unsubscribe() - - # Request/reply pattern - - async def request( - self, - subject: str, - data: bytes, - timeout: float = 5.0, - headers: Optional[dict[str, str]] = None, - ) -> Message: - """Send a request and wait for a response. - - Args: - subject: Subject to send request to. - data: Request payload. - timeout: Response timeout in seconds. - headers: Optional request headers. - - Returns: - Response message. - - Raises: - TimeoutError: If no response within timeout. - """ - response = await self._nc.request( - subject, data, timeout=timeout, headers=headers - ) - return Message( - subject=response.subject, - data=response.data, - reply=response.reply, - headers=dict(response.headers) if response.headers else {}, - ) - - async def respond( - self, - request: Message, - data: bytes, - headers: Optional[dict[str, str]] = None, - ) -> None: - """Send a response to a request message. - - Args: - request: Original request message. - data: Response payload. - headers: Optional response headers. - """ - if request.reply: - await self.publish(request.reply, data, headers=headers) - - # JetStream operations - - def _get_js(self) -> Any: - """Get JetStream context, creating if needed.""" - if self._js is None: - self._js = self._nc.jetstream() - return self._js - - async def js_create_stream( - self, - name: str, - subjects: Sequence[str], - storage: str = "file", - retention: str = "limits", - max_msgs: int = -1, - max_bytes: int = -1, - max_age: int = 0, # seconds, 0 = unlimited - max_msg_size: int = -1, - duplicate_window: int = 120, # seconds - replicas: int = 1, - ) -> None: - """Create a JetStream stream. - - Args: - name: Stream name. - subjects: Subjects to capture. - storage: Storage type (file, memory). - retention: Retention policy (limits, interest, workqueue). - max_msgs: Max messages (-1 = unlimited). - max_bytes: Max bytes (-1 = unlimited). - max_age: Max age in seconds (0 = unlimited). - max_msg_size: Max message size (-1 = unlimited). - duplicate_window: Duplicate detection window in seconds. - replicas: Number of replicas. - """ - from nats.js.api import ( - StorageType, - StreamConfig, - RetentionPolicy, - ) - - storage_map = { - "file": StorageType.FILE, - "memory": StorageType.MEMORY, - } - retention_map = { - "limits": RetentionPolicy.LIMITS, - "interest": RetentionPolicy.INTEREST, - "workqueue": RetentionPolicy.WORK_QUEUE, - } - - config = StreamConfig( - name=name, - subjects=list(subjects), - storage=storage_map.get(storage, StorageType.FILE), - retention=retention_map.get(retention, RetentionPolicy.LIMITS), - max_msgs=max_msgs, - max_bytes=max_bytes, - max_age=max_age * 1_000_000_000 if max_age else 0, # nanoseconds - max_msg_size=max_msg_size, - duplicate_window=duplicate_window * 1_000_000_000, # nanoseconds - num_replicas=replicas, - ) - - js = self._get_js() - await js.add_stream(config) - - async def js_delete_stream(self, name: str) -> None: - """Delete a JetStream stream. - - Args: - name: Stream name. - """ - js = self._get_js() - await js.delete_stream(name) - - async def js_stream_info(self, name: str) -> dict[str, Any]: - """Get stream information. - - Args: - name: Stream name. - - Returns: - Stream info dict. - """ - js = self._get_js() - info = await js.stream_info(name) - return { - "name": info.config.name, - "subjects": info.config.subjects, - "messages": info.state.messages, - "bytes": info.state.bytes, - "first_seq": info.state.first_seq, - "last_seq": info.state.last_seq, - } - - async def js_publish( - self, - subject: str, - data: bytes, - headers: Optional[dict[str, str]] = None, - msg_id: Optional[str] = None, - expect_stream: Optional[str] = None, - ) -> int: - """Publish a message to JetStream. - - Args: - subject: Subject to publish to. - data: Message payload. - headers: Optional message headers. - msg_id: Message ID for deduplication. - expect_stream: Expected stream name. - - Returns: - Sequence number of published message. - """ - js = self._get_js() - ack = await js.publish( - subject, - data, - headers=headers, - ) - return ack.seq - - async def js_create_consumer( - self, - stream: str, - name: str, - durable: bool = True, - filter_subjects: Optional[Sequence[str]] = None, - ack_policy: str = "explicit", - max_deliver: int = -1, - ack_wait: int = 30, # seconds - ) -> None: - """Create a JetStream consumer. - - Args: - stream: Stream name. - name: Consumer name. - durable: Durable consumer (survives disconnects). - filter_subjects: Filter to specific subjects. - ack_policy: Ack policy (none, all, explicit). - max_deliver: Max redelivery attempts (-1 = unlimited). - ack_wait: Ack wait time in seconds. - """ - from nats.js.api import AckPolicy, ConsumerConfig - - ack_map = { - "none": AckPolicy.NONE, - "all": AckPolicy.ALL, - "explicit": AckPolicy.EXPLICIT, - } - - config = ConsumerConfig( - durable_name=name if durable else None, - name=name, - filter_subjects=list(filter_subjects) if filter_subjects else None, - ack_policy=ack_map.get(ack_policy, AckPolicy.EXPLICIT), - max_deliver=max_deliver, - ack_wait=ack_wait * 1_000_000_000, # nanoseconds - ) - - js = self._get_js() - await js.add_consumer(stream, config) - - async def js_subscribe( - self, - stream: str, - consumer: str, - handler: MessageHandler, - ) -> Subscription: - """Subscribe to a JetStream consumer. - - Args: - stream: Stream name. - consumer: Consumer name. - handler: Message handler. - - Returns: - Subscription handle. - """ - - async def _wrapper(msg: Any) -> None: - wrapped = Message( - subject=msg.subject, - data=msg.data, - reply=msg.reply, - headers=dict(msg.headers) if msg.headers else {}, - ) - await handler(wrapped) - await msg.ack() - - js = self._get_js() - sub = await js.pull_subscribe(durable=consumer, stream=stream) - # Note: Pull subscribe works differently, using fetch - return Subscription(subject=f"{stream}.{consumer}", _sub=sub) - - async def js_fetch( - self, - stream: str, - consumer: str, - batch: int = 1, - timeout: float = 5.0, - ) -> list[Message]: - """Fetch messages from a JetStream consumer. - - Args: - stream: Stream name. - consumer: Consumer name. - batch: Number of messages to fetch. - timeout: Fetch timeout in seconds. - - Returns: - List of messages. - """ - js = self._get_js() - sub = await js.pull_subscribe(durable=consumer, stream=stream) - - try: - msgs = await sub.fetch(batch, timeout=timeout) - result = [] - for msg in msgs: - result.append( - Message( - subject=msg.subject, - data=msg.data, - reply=msg.reply, - headers=dict(msg.headers) if msg.headers else {}, - ) - ) - await msg.ack() - return result - except Exception: - return [] - - async def __aenter__(self) -> PubSubClient: - """Async context manager entry.""" - await self.connect() - return self - - async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: - """Async context manager exit.""" - await self.close() diff --git a/pkg/hanzo/src/hanzo/infra/queues.py b/pkg/hanzo/src/hanzo/infra/queues.py deleted file mode 100644 index c99f7e549..000000000 --- a/pkg/hanzo/src/hanzo/infra/queues.py +++ /dev/null @@ -1,654 +0,0 @@ -"""Work queue client for Hanzo infrastructure. - -Provides async interface for distributed work queues, -built on top of Redis/Valkey for simplicity and reliability. -""" - -from __future__ import annotations - -import os -import json -import uuid -import asyncio -from enum import Enum -from typing import Any, Callable, Optional, Awaitable -from datetime import datetime, timedelta -from dataclasses import field, dataclass - -from pydantic import Field, BaseModel - - -class QueuesConfig(BaseModel): - """Configuration for work queue connection.""" - - host: str = Field(default="localhost", description="Redis server host") - port: int = Field(default=6379, description="Redis server port") - password: Optional[str] = Field(default=None, description="Redis password") - db: int = Field(default=1, description="Redis database number") - url: Optional[str] = Field(default=None, description="Full Redis URL") - prefix: str = Field(default="hanzo:queue", description="Key prefix for queues") - default_timeout: int = Field( - default=300, description="Default job timeout in seconds" - ) - default_ttl: int = Field(default=86400, description="Default result TTL in seconds") - - @classmethod - def from_env(cls) -> QueuesConfig: - """Create config from environment variables. - - Environment variables: - QUEUE_REDIS_HOST: Server host (default: localhost) - QUEUE_REDIS_PORT: Server port (default: 6379) - QUEUE_REDIS_PASSWORD: Authentication password - QUEUE_REDIS_DB: Database number (default: 1) - QUEUE_REDIS_URL: Full URL (overrides host/port) - QUEUE_PREFIX: Key prefix (default: hanzo:queue) - """ - return cls( - host=os.getenv("QUEUE_REDIS_HOST") or os.getenv("REDIS_HOST", "localhost"), - port=int(os.getenv("QUEUE_REDIS_PORT") or os.getenv("REDIS_PORT", "6379")), - password=os.getenv("QUEUE_REDIS_PASSWORD") or os.getenv("REDIS_PASSWORD"), - db=int(os.getenv("QUEUE_REDIS_DB", "1")), - url=os.getenv("QUEUE_REDIS_URL") or os.getenv("REDIS_URL"), - prefix=os.getenv("QUEUE_PREFIX", "hanzo:queue"), - ) - - -class JobStatus(str, Enum): - """Status of a queued job.""" - - PENDING = "pending" - RUNNING = "running" - COMPLETED = "completed" - FAILED = "failed" - RETRYING = "retrying" - CANCELLED = "cancelled" - EXPIRED = "expired" - - -@dataclass -class Job: - """A work queue job.""" - - id: str - queue: str - name: str - args: dict[str, Any] = field(default_factory=dict) - status: JobStatus = JobStatus.PENDING - result: Any = None - error: Optional[str] = None - created_at: Optional[datetime] = None - started_at: Optional[datetime] = None - completed_at: Optional[datetime] = None - attempts: int = 0 - max_attempts: int = 3 - timeout: int = 300 # seconds - ttl: int = 86400 # result TTL in seconds - priority: int = 0 # higher = more priority - metadata: dict[str, Any] = field(default_factory=dict) - - def to_dict(self) -> dict[str, Any]: - """Convert to dictionary for storage.""" - return { - "id": self.id, - "queue": self.queue, - "name": self.name, - "args": self.args, - "status": self.status.value, - "result": self.result, - "error": self.error, - "created_at": self.created_at.isoformat() if self.created_at else None, - "started_at": self.started_at.isoformat() if self.started_at else None, - "completed_at": ( - self.completed_at.isoformat() if self.completed_at else None - ), - "attempts": self.attempts, - "max_attempts": self.max_attempts, - "timeout": self.timeout, - "ttl": self.ttl, - "priority": self.priority, - "metadata": self.metadata, - } - - @classmethod - def from_dict(cls, data: dict[str, Any]) -> Job: - """Create from dictionary.""" - return cls( - id=data["id"], - queue=data["queue"], - name=data["name"], - args=data.get("args", {}), - status=JobStatus(data.get("status", "pending")), - result=data.get("result"), - error=data.get("error"), - created_at=( - datetime.fromisoformat(data["created_at"]) - if data.get("created_at") - else None - ), - started_at=( - datetime.fromisoformat(data["started_at"]) - if data.get("started_at") - else None - ), - completed_at=( - datetime.fromisoformat(data["completed_at"]) - if data.get("completed_at") - else None - ), - attempts=data.get("attempts", 0), - max_attempts=data.get("max_attempts", 3), - timeout=data.get("timeout", 300), - ttl=data.get("ttl", 86400), - priority=data.get("priority", 0), - metadata=data.get("metadata", {}), - ) - - -@dataclass -class QueueStats: - """Statistics for a queue.""" - - name: str - pending: int = 0 - running: int = 0 - completed: int = 0 - failed: int = 0 - - -JobHandler = Callable[[Job], Awaitable[Any]] - - -class QueuesClient: - """Async client for distributed work queues. - - Implements a simple but reliable work queue on top of Redis, - with support for priorities, retries, and result storage. - - Example: - ```python - client = QueuesClient(QueuesConfig.from_env()) - await client.connect() - - # Enqueue jobs - job_id = await client.enqueue( - "emails", - "send_welcome", - args={"user_id": "123", "email": "user@example.com"}, - priority=10, - ) - - # Get job status - job = await client.get_job(job_id) - print(f"Status: {job.status}") - - - # Process jobs (in a worker) - async def handler(job: Job) -> str: - # Do work... - return "sent" - - - await client.process("emails", handler) - - # Get result - result = await client.get_result(job_id) - ``` - """ - - def __init__(self, config: Optional[QueuesConfig] = None) -> None: - """Initialize queues client. - - Args: - config: Queue configuration. If None, loads from environment. - """ - self.config = config or QueuesConfig.from_env() - self._redis: Any = None - - async def connect(self) -> None: - """Establish connection to Redis.""" - try: - import redis.asyncio as redis - except ImportError as e: - raise ImportError( - "redis is required for QueuesClient. Install with: pip install redis" - ) from e - - if self.config.url: - self._redis = redis.from_url( - self.config.url, - decode_responses=True, - ) - else: - self._redis = redis.Redis( - host=self.config.host, - port=self.config.port, - password=self.config.password, - db=self.config.db, - decode_responses=True, - ) - - async def close(self) -> None: - """Close the connection.""" - if self._redis: - await self._redis.aclose() - self._redis = None - - async def health_check(self) -> bool: - """Check if Redis is healthy. - - Returns: - True if Redis is responding. - """ - if not self._redis: - return False - try: - await self._redis.ping() - return True - except Exception: - return False - - def _key(self, *parts: str) -> str: - """Build a Redis key with prefix.""" - return ":".join([self.config.prefix, *parts]) - - # Job operations - - async def enqueue( - self, - queue: str, - name: str, - args: Optional[dict[str, Any]] = None, - job_id: Optional[str] = None, - priority: int = 0, - delay: Optional[timedelta] = None, - timeout: Optional[int] = None, - max_attempts: int = 3, - ttl: Optional[int] = None, - metadata: Optional[dict[str, Any]] = None, - ) -> str: - """Enqueue a job. - - Args: - queue: Queue name. - name: Job name/type. - args: Job arguments. - job_id: Custom job ID (auto-generated if not specified). - priority: Job priority (higher = more priority). - delay: Delay before job becomes available. - timeout: Job timeout in seconds. - max_attempts: Maximum retry attempts. - ttl: Result TTL in seconds. - metadata: Additional metadata. - - Returns: - Job ID. - """ - job = Job( - id=job_id or f"{name}-{uuid.uuid4().hex[:12]}", - queue=queue, - name=name, - args=args or {}, - status=JobStatus.PENDING, - created_at=datetime.utcnow(), - max_attempts=max_attempts, - timeout=timeout or self.config.default_timeout, - ttl=ttl or self.config.default_ttl, - priority=priority, - metadata=metadata or {}, - ) - - # Store job data - job_key = self._key("job", job.id) - await self._redis.set(job_key, json.dumps(job.to_dict())) - - # Add to queue - queue_key = self._key("queue", queue) - score = -priority # Redis ZSET is ascending, we want descending priority - - if delay: - # Delayed job - use scheduled set - scheduled_key = self._key("scheduled", queue) - execute_at = datetime.utcnow() + delay - await self._redis.zadd(scheduled_key, {job.id: execute_at.timestamp()}) - else: - # Immediate job - await self._redis.zadd(queue_key, {job.id: score}) - - return job.id - - async def get_job(self, job_id: str) -> Optional[Job]: - """Get a job by ID. - - Args: - job_id: Job ID. - - Returns: - Job or None if not found. - """ - job_key = self._key("job", job_id) - data = await self._redis.get(job_key) - if not data: - return None - return Job.from_dict(json.loads(data)) - - async def get_result(self, job_id: str, timeout: float = 0) -> Any: - """Get a job's result. - - Args: - job_id: Job ID. - timeout: Seconds to wait for result (0 = don't wait). - - Returns: - Job result or None. - """ - job = await self.get_job(job_id) - if not job: - return None - - if job.status == JobStatus.COMPLETED: - return job.result - - if timeout > 0 and job.status in (JobStatus.PENDING, JobStatus.RUNNING): - # Poll for completion - end_time = datetime.utcnow() + timedelta(seconds=timeout) - while datetime.utcnow() < end_time: - await asyncio.sleep(0.5) - job = await self.get_job(job_id) - if job and job.status == JobStatus.COMPLETED: - return job.result - if job and job.status == JobStatus.FAILED: - raise Exception(f"Job failed: {job.error}") - - return None - - async def cancel_job(self, job_id: str) -> bool: - """Cancel a pending job. - - Args: - job_id: Job ID. - - Returns: - True if cancelled, False if job not found or already processed. - """ - job = await self.get_job(job_id) - if not job: - return False - - if job.status not in (JobStatus.PENDING, JobStatus.RETRYING): - return False - - # Remove from queue - queue_key = self._key("queue", job.queue) - await self._redis.zrem(queue_key, job_id) - - # Update status - job.status = JobStatus.CANCELLED - job_key = self._key("job", job_id) - await self._redis.set(job_key, json.dumps(job.to_dict())) - - return True - - async def retry_job(self, job_id: str) -> bool: - """Retry a failed job. - - Args: - job_id: Job ID. - - Returns: - True if job was requeued. - """ - job = await self.get_job(job_id) - if not job: - return False - - if job.status != JobStatus.FAILED: - return False - - # Reset and requeue - job.status = JobStatus.RETRYING - job.error = None - job.started_at = None - job.completed_at = None - - job_key = self._key("job", job_id) - await self._redis.set(job_key, json.dumps(job.to_dict())) - - queue_key = self._key("queue", job.queue) - await self._redis.zadd(queue_key, {job_id: -job.priority}) - - return True - - # Queue operations - - async def get_queue_stats(self, queue: str) -> QueueStats: - """Get queue statistics. - - Args: - queue: Queue name. - - Returns: - Queue statistics. - """ - queue_key = self._key("queue", queue) - running_key = self._key("running", queue) - - pending = await self._redis.zcard(queue_key) - running = await self._redis.scard(running_key) - - # Count completed/failed from recent jobs (expensive, use sparingly) - completed = 0 - failed = 0 - - return QueueStats( - name=queue, - pending=pending, - running=running, - completed=completed, - failed=failed, - ) - - async def list_queues(self) -> list[str]: - """List all queues. - - Returns: - List of queue names. - """ - pattern = self._key("queue", "*") - keys = await self._redis.keys(pattern) - prefix_len = len(self._key("queue", "")) - return [k[prefix_len:] for k in keys] - - async def purge_queue(self, queue: str) -> int: - """Remove all jobs from a queue. - - Args: - queue: Queue name. - - Returns: - Number of jobs removed. - """ - queue_key = self._key("queue", queue) - count = await self._redis.zcard(queue_key) - await self._redis.delete(queue_key) - return count - - # Worker operations - - async def dequeue( - self, - queue: str, - timeout: float = 0, - ) -> Optional[Job]: - """Dequeue a job for processing. - - Args: - queue: Queue name. - timeout: Seconds to wait for a job (0 = don't wait). - - Returns: - Job or None if queue is empty. - """ - queue_key = self._key("queue", queue) - running_key = self._key("running", queue) - - # Move scheduled jobs that are ready - await self._move_scheduled_jobs(queue) - - # Try to get a job - result = await self._redis.zpopmin(queue_key, 1) - if not result: - if timeout > 0: - # Wait for job with blocking pop - result = await self._redis.bzpopmin(queue_key, timeout) - if result: - _, job_id, _ = result - else: - return None - else: - return None - else: - job_id = result[0][0] - - # Get job data - job = await self.get_job(job_id) - if not job: - return None - - # Mark as running - job.status = JobStatus.RUNNING - job.started_at = datetime.utcnow() - job.attempts += 1 - - job_key = self._key("job", job_id) - await self._redis.set(job_key, json.dumps(job.to_dict())) - - # Add to running set - await self._redis.sadd(running_key, job_id) - - return job - - async def _move_scheduled_jobs(self, queue: str) -> None: - """Move scheduled jobs that are ready to the main queue.""" - scheduled_key = self._key("scheduled", queue) - queue_key = self._key("queue", queue) - - now = datetime.utcnow().timestamp() - - # Get jobs that are ready - ready = await self._redis.zrangebyscore(scheduled_key, "-inf", now) - - for job_id in ready: - job = await self.get_job(job_id) - if job: - # Move to main queue - await self._redis.zrem(scheduled_key, job_id) - await self._redis.zadd(queue_key, {job_id: -job.priority}) - - async def complete_job( - self, - job_id: str, - result: Any = None, - ) -> None: - """Mark a job as completed. - - Args: - job_id: Job ID. - result: Job result. - """ - job = await self.get_job(job_id) - if not job: - return - - job.status = JobStatus.COMPLETED - job.result = result - job.completed_at = datetime.utcnow() - - # Update job - job_key = self._key("job", job_id) - await self._redis.set(job_key, json.dumps(job.to_dict())) - await self._redis.expire(job_key, job.ttl) - - # Remove from running - running_key = self._key("running", job.queue) - await self._redis.srem(running_key, job_id) - - async def fail_job( - self, - job_id: str, - error: str, - ) -> None: - """Mark a job as failed. - - Args: - job_id: Job ID. - error: Error message. - """ - job = await self.get_job(job_id) - if not job: - return - - # Check if should retry - if job.attempts < job.max_attempts: - job.status = JobStatus.RETRYING - job.error = error - - # Requeue with backoff - delay = min(60 * (2**job.attempts), 3600) # Exponential backoff, max 1 hour - queue_key = self._key("queue", job.queue) - scheduled_key = self._key("scheduled", job.queue) - execute_at = datetime.utcnow() + timedelta(seconds=delay) - await self._redis.zadd(scheduled_key, {job_id: execute_at.timestamp()}) - else: - job.status = JobStatus.FAILED - job.error = error - job.completed_at = datetime.utcnow() - - # Update job - job_key = self._key("job", job_id) - await self._redis.set(job_key, json.dumps(job.to_dict())) - - # Remove from running - running_key = self._key("running", job.queue) - await self._redis.srem(running_key, job_id) - - async def process( - self, - queue: str, - handler: JobHandler, - batch_size: int = 1, - poll_interval: float = 1.0, - ) -> None: - """Process jobs from a queue (blocking). - - Args: - queue: Queue name. - handler: Async job handler function. - batch_size: Jobs to process before checking for more. - poll_interval: Seconds between queue checks. - """ - while True: - for _ in range(batch_size): - job = await self.dequeue(queue, timeout=poll_interval) - if not job: - break - - try: - result = await asyncio.wait_for( - handler(job), - timeout=job.timeout, - ) - await self.complete_job(job.id, result) - except asyncio.TimeoutError: - await self.fail_job(job.id, "Job timed out") - except Exception as e: - await self.fail_job(job.id, str(e)) - - async def __aenter__(self) -> QueuesClient: - """Async context manager entry.""" - await self.connect() - return self - - async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: - """Async context manager exit.""" - await self.close() diff --git a/pkg/hanzo/src/hanzo/infra/search.py b/pkg/hanzo/src/hanzo/infra/search.py deleted file mode 100644 index 22a65d8cf..000000000 --- a/pkg/hanzo/src/hanzo/infra/search.py +++ /dev/null @@ -1,513 +0,0 @@ -"""Meilisearch client wrapper for Hanzo infrastructure. - -Provides async interface to Meilisearch for full-text search -with typo tolerance, filtering, and faceted search. -""" - -from __future__ import annotations - -import os -from typing import Any, Optional, Sequence -from dataclasses import field, dataclass - -from pydantic import Field, BaseModel - - -class SearchConfig(BaseModel): - """Configuration for Meilisearch connection.""" - - host: str = Field(default="localhost", description="Meilisearch server host") - port: int = Field(default=7700, description="Meilisearch server port") - api_key: Optional[str] = Field(default=None, description="Master or API key") - url: Optional[str] = Field( - default=None, description="Full URL (overrides host/port)" - ) - timeout: float = Field(default=30.0, description="Request timeout in seconds") - ssl: bool = Field(default=False, description="Use HTTPS") - - @classmethod - def from_env(cls) -> SearchConfig: - """Create config from environment variables. - - Environment variables: - MEILISEARCH_HOST: Server host (default: localhost) - MEILISEARCH_PORT: Server port (default: 7700) - MEILISEARCH_API_KEY: API key for authentication - MEILISEARCH_URL: Full URL (overrides host/port) - MEILISEARCH_SSL: Use HTTPS (default: false) - """ - return cls( - host=os.getenv("MEILISEARCH_HOST", "localhost"), - port=int(os.getenv("MEILISEARCH_PORT", "7700")), - api_key=os.getenv("MEILISEARCH_API_KEY") or os.getenv("MEILI_MASTER_KEY"), - url=os.getenv("MEILISEARCH_URL"), - ssl=os.getenv("MEILISEARCH_SSL", "").lower() in ("true", "1", "yes"), - ) - - @property - def effective_url(self) -> str: - """Get the effective URL to connect to.""" - if self.url: - return self.url - protocol = "https" if self.ssl else "http" - return f"{protocol}://{self.host}:{self.port}" - - -@dataclass -class SearchHit: - """A single search result.""" - - id: str - document: dict[str, Any] - score: Optional[float] = None - highlights: dict[str, Any] = field(default_factory=dict) - - -@dataclass -class SearchResult: - """Result of a search query.""" - - hits: list[SearchHit] - query: str - processing_time_ms: int - total_hits: int - offset: int = 0 - limit: int = 20 - facet_distribution: dict[str, dict[str, int]] = field(default_factory=dict) - - -@dataclass -class IndexStats: - """Statistics for an index.""" - - number_of_documents: int - is_indexing: bool - field_distribution: dict[str, int] = field(default_factory=dict) - - -class SearchClient: - """Async client for Meilisearch full-text search. - - Wraps meilisearch-python-sdk with async methods and Hanzo conventions. - - Example: - ```python - client = SearchClient(SearchConfig.from_env()) - await client.connect() - - # Create index - await client.create_index("products", primary_key="id") - - # Add documents - docs = [{"id": "1", "name": "Widget", "price": 99}] - await client.add_documents("products", docs) - - # Search - results = await client.search("products", "widget", filters="price > 50") - for hit in results.hits: - print(hit.document["name"]) - ``` - """ - - def __init__(self, config: Optional[SearchConfig] = None) -> None: - """Initialize search client. - - Args: - config: Meilisearch configuration. If None, loads from environment. - """ - self.config = config or SearchConfig.from_env() - self._client: Any = None - - async def connect(self) -> None: - """Establish connection to Meilisearch server.""" - try: - from meilisearch_python_sdk import AsyncClient - except ImportError as e: - raise ImportError( - "meilisearch-python-sdk is required for SearchClient. Install with: pip install meilisearch-python-sdk" - ) from e - - self._client = AsyncClient( - self.config.effective_url, - api_key=self.config.api_key, - timeout=int(self.config.timeout), - ) - - async def close(self) -> None: - """Close the connection.""" - if self._client: - await self._client.aclose() - self._client = None - - async def health_check(self) -> bool: - """Check if Meilisearch server is healthy. - - Returns: - True if server is reachable and healthy. - """ - if not self._client: - return False - try: - health = await self._client.health() - return health.status == "available" - except Exception: - return False - - # Index operations - - async def create_index( - self, - name: str, - primary_key: Optional[str] = None, - ) -> None: - """Create a new index. - - Args: - name: Index name (uid). - primary_key: Document primary key field. - """ - task = await self._client.create_index(uid=name, primary_key=primary_key) - await self._client.wait_for_task(task.task_uid) - - async def delete_index(self, name: str) -> None: - """Delete an index. - - Args: - name: Index name to delete. - """ - task = await self._client.index(name).delete() - await self._client.wait_for_task(task.task_uid) - - async def index_exists(self, name: str) -> bool: - """Check if an index exists. - - Args: - name: Index name. - - Returns: - True if index exists. - """ - try: - await self._client.get_index(name) - return True - except Exception: - return False - - async def list_indexes(self) -> list[str]: - """List all indexes. - - Returns: - List of index names. - """ - indexes = await self._client.get_indexes() - return [idx.uid for idx in indexes] - - async def get_index_stats(self, name: str) -> IndexStats: - """Get statistics for an index. - - Args: - name: Index name. - - Returns: - Index statistics. - """ - index = self._client.index(name) - stats = await index.get_stats() - return IndexStats( - number_of_documents=stats.number_of_documents, - is_indexing=stats.is_indexing, - field_distribution=stats.field_distribution or {}, - ) - - # Document operations - - async def add_documents( - self, - index: str, - documents: Sequence[dict[str, Any]], - primary_key: Optional[str] = None, - ) -> None: - """Add or update documents. - - Args: - index: Index name. - documents: Documents to add. - primary_key: Override primary key for this operation. - """ - idx = self._client.index(index) - task = await idx.add_documents(list(documents), primary_key=primary_key) - await self._client.wait_for_task(task.task_uid) - - async def update_documents( - self, - index: str, - documents: Sequence[dict[str, Any]], - primary_key: Optional[str] = None, - ) -> None: - """Update documents (partial update). - - Args: - index: Index name. - documents: Documents with updates. - primary_key: Override primary key for this operation. - """ - idx = self._client.index(index) - task = await idx.update_documents(list(documents), primary_key=primary_key) - await self._client.wait_for_task(task.task_uid) - - async def delete_document(self, index: str, document_id: str) -> None: - """Delete a document by ID. - - Args: - index: Index name. - document_id: Document ID to delete. - """ - idx = self._client.index(index) - task = await idx.delete_document(document_id) - await self._client.wait_for_task(task.task_uid) - - async def delete_documents(self, index: str, document_ids: list[str]) -> None: - """Delete multiple documents by ID. - - Args: - index: Index name. - document_ids: Document IDs to delete. - """ - idx = self._client.index(index) - task = await idx.delete_documents(document_ids) - await self._client.wait_for_task(task.task_uid) - - async def delete_all_documents(self, index: str) -> None: - """Delete all documents in an index. - - Args: - index: Index name. - """ - idx = self._client.index(index) - task = await idx.delete_all_documents() - await self._client.wait_for_task(task.task_uid) - - async def get_document( - self, - index: str, - document_id: str, - fields: Optional[list[str]] = None, - ) -> Optional[dict[str, Any]]: - """Get a document by ID. - - Args: - index: Index name. - document_id: Document ID. - fields: Fields to retrieve (all if not specified). - - Returns: - Document or None if not found. - """ - idx = self._client.index(index) - try: - return await idx.get_document(document_id, fields=fields) - except Exception: - return None - - async def get_documents( - self, - index: str, - offset: int = 0, - limit: int = 20, - fields: Optional[list[str]] = None, - ) -> list[dict[str, Any]]: - """Get documents from an index. - - Args: - index: Index name. - offset: Number of documents to skip. - limit: Maximum documents to return. - fields: Fields to retrieve (all if not specified). - - Returns: - List of documents. - """ - idx = self._client.index(index) - result = await idx.get_documents(offset=offset, limit=limit, fields=fields) - return result.results - - # Search operations - - async def search( - self, - index: str, - query: str, - offset: int = 0, - limit: int = 20, - filters: Optional[str] = None, - facets: Optional[list[str]] = None, - attributes_to_retrieve: Optional[list[str]] = None, - attributes_to_highlight: Optional[list[str]] = None, - sort: Optional[list[str]] = None, - show_matches_position: bool = False, - show_ranking_score: bool = False, - ) -> SearchResult: - """Search documents in an index. - - Args: - index: Index name. - query: Search query. - offset: Number of results to skip. - limit: Maximum results to return. - filters: Filter expression (e.g., "price > 50 AND category = 'tech'"). - facets: Fields to get facet distribution for. - attributes_to_retrieve: Fields to include in results. - attributes_to_highlight: Fields to highlight matches in. - sort: Sort expressions (e.g., ["price:asc", "name:desc"]). - show_matches_position: Include match positions. - show_ranking_score: Include ranking score. - - Returns: - Search results with hits and metadata. - """ - idx = self._client.index(index) - result = await idx.search( - query, - offset=offset, - limit=limit, - filter=filters, - facets=facets, - attributes_to_retrieve=attributes_to_retrieve, - attributes_to_highlight=attributes_to_highlight, - sort=sort, - show_matches_position=show_matches_position, - show_ranking_score=show_ranking_score, - ) - - hits = [] - for hit in result.hits: - # Handle both dict and object responses - if isinstance(hit, dict): - doc = hit - formatted = hit.get("_formatted", {}) - score = hit.get("_rankingScore") - else: - doc = hit.__dict__ if hasattr(hit, "__dict__") else {} - formatted = getattr(hit, "_formatted", {}) or {} - score = getattr(hit, "_rankingScore", None) - - # Extract ID from document - doc_id = doc.get("id") or doc.get("_id") or str(hash(str(doc))) - - hits.append( - SearchHit( - id=str(doc_id), - document=doc, - score=score, - highlights=formatted, - ) - ) - - return SearchResult( - hits=hits, - query=query, - processing_time_ms=result.processing_time_ms, - total_hits=result.estimated_total_hits or len(hits), - offset=offset, - limit=limit, - facet_distribution=result.facet_distribution or {}, - ) - - # Settings operations - - async def get_settings(self, index: str) -> dict[str, Any]: - """Get index settings. - - Args: - index: Index name. - - Returns: - Current settings. - """ - idx = self._client.index(index) - settings = await idx.get_settings() - return settings.__dict__ if hasattr(settings, "__dict__") else dict(settings) - - async def update_settings( - self, - index: str, - settings: dict[str, Any], - ) -> None: - """Update index settings. - - Args: - index: Index name. - settings: Settings to update. - """ - idx = self._client.index(index) - task = await idx.update_settings(settings) - await self._client.wait_for_task(task.task_uid) - - async def update_searchable_attributes( - self, - index: str, - attributes: list[str], - ) -> None: - """Set searchable attributes. - - Args: - index: Index name. - attributes: Ordered list of searchable attributes. - """ - idx = self._client.index(index) - task = await idx.update_searchable_attributes(attributes) - await self._client.wait_for_task(task.task_uid) - - async def update_filterable_attributes( - self, - index: str, - attributes: list[str], - ) -> None: - """Set filterable attributes. - - Args: - index: Index name. - attributes: List of filterable attributes. - """ - idx = self._client.index(index) - task = await idx.update_filterable_attributes(attributes) - await self._client.wait_for_task(task.task_uid) - - async def update_sortable_attributes( - self, - index: str, - attributes: list[str], - ) -> None: - """Set sortable attributes. - - Args: - index: Index name. - attributes: List of sortable attributes. - """ - idx = self._client.index(index) - task = await idx.update_sortable_attributes(attributes) - await self._client.wait_for_task(task.task_uid) - - async def update_ranking_rules( - self, - index: str, - rules: list[str], - ) -> None: - """Set ranking rules. - - Args: - index: Index name. - rules: Ordered list of ranking rules. - """ - idx = self._client.index(index) - task = await idx.update_ranking_rules(rules) - await self._client.wait_for_task(task.task_uid) - - async def __aenter__(self) -> SearchClient: - """Async context manager entry.""" - await self.connect() - return self - - async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: - """Async context manager exit.""" - await self.close() diff --git a/pkg/hanzo/src/hanzo/infra/storage.py b/pkg/hanzo/src/hanzo/infra/storage.py deleted file mode 100644 index 68bc76a7f..000000000 --- a/pkg/hanzo/src/hanzo/infra/storage.py +++ /dev/null @@ -1,541 +0,0 @@ -"""S3/MinIO object storage client wrapper for Hanzo infrastructure. - -Provides async interface to S3-compatible object storage including -AWS S3, MinIO, and other compatible services. -""" - -from __future__ import annotations - -import os -from io import BytesIO -from typing import Any, Union, BinaryIO, Optional, AsyncIterator -from datetime import datetime -from dataclasses import field, dataclass - -from pydantic import Field, BaseModel - - -class StorageConfig(BaseModel): - """Configuration for S3/MinIO connection.""" - - endpoint_url: Optional[str] = Field( - default=None, description="Endpoint URL (required for MinIO)" - ) - region: str = Field(default="us-east-1", description="AWS region") - access_key: Optional[str] = Field(default=None, description="Access key ID") - secret_key: Optional[str] = Field(default=None, description="Secret access key") - bucket: str = Field(default="hanzo", description="Default bucket name") - use_ssl: bool = Field(default=True, description="Use SSL/TLS") - verify_ssl: bool = Field(default=True, description="Verify SSL certificates") - session_token: Optional[str] = Field( - default=None, description="Session token (for temporary credentials)" - ) - addressing_style: str = Field( - default="auto", description="Path or virtual addressing style" - ) - - @classmethod - def from_env(cls) -> StorageConfig: - """Create config from environment variables. - - Environment variables: - S3_ENDPOINT_URL: Endpoint URL (for MinIO/custom S3) - S3_REGION / AWS_REGION: AWS region (default: us-east-1) - S3_ACCESS_KEY / AWS_ACCESS_KEY_ID: Access key - S3_SECRET_KEY / AWS_SECRET_ACCESS_KEY: Secret key - S3_BUCKET: Default bucket name (default: hanzo) - S3_USE_SSL: Use SSL (default: true) - """ - return cls( - endpoint_url=os.getenv("S3_ENDPOINT_URL") or os.getenv("MINIO_ENDPOINT"), - region=os.getenv("S3_REGION") or os.getenv("AWS_REGION", "us-east-1"), - access_key=os.getenv("S3_ACCESS_KEY") or os.getenv("AWS_ACCESS_KEY_ID"), - secret_key=os.getenv("S3_SECRET_KEY") or os.getenv("AWS_SECRET_ACCESS_KEY"), - bucket=os.getenv("S3_BUCKET", "hanzo"), - use_ssl=os.getenv("S3_USE_SSL", "true").lower() in ("true", "1", "yes"), - session_token=os.getenv("AWS_SESSION_TOKEN"), - ) - - -@dataclass -class ObjectInfo: - """Metadata about a stored object.""" - - key: str - size: int - etag: str - last_modified: datetime - content_type: Optional[str] = None - metadata: dict[str, str] = field(default_factory=dict) - - -@dataclass -class UploadResult: - """Result of an upload operation.""" - - key: str - etag: str - version_id: Optional[str] = None - - -@dataclass -class PresignedUrl: - """A presigned URL for temporary access.""" - - url: str - expires_in: int # seconds - - -class StorageClient: - """Async client for S3-compatible object storage. - - Wraps aiobotocore/boto3 for async S3 operations with Hanzo conventions. - Supports AWS S3, MinIO, and other S3-compatible services. - - Example: - ```python - client = StorageClient(StorageConfig.from_env()) - await client.connect() - - # Upload - await client.put_object("files/doc.txt", b"Hello, World!") - - # Download - data = await client.get_object("files/doc.txt") - - # List objects - async for obj in client.list_objects("files/"): - print(obj.key, obj.size) - - # Presigned URLs - url = await client.presign_get("files/doc.txt", expires_in=3600) - ``` - """ - - def __init__(self, config: Optional[StorageConfig] = None) -> None: - """Initialize storage client. - - Args: - config: S3 configuration. If None, loads from environment. - """ - self.config = config or StorageConfig.from_env() - self._session: Any = None - self._client: Any = None - self._exit_stack: Any = None - - async def connect(self) -> None: - """Establish connection to S3 service.""" - try: - from contextlib import AsyncExitStack - - from aiobotocore.session import get_session - except ImportError as e: - raise ImportError( - "aiobotocore is required for StorageClient. Install with: pip install aiobotocore" - ) from e - - self._session = get_session() - self._exit_stack = AsyncExitStack() - - client_kwargs: dict[str, Any] = { - "service_name": "s3", - "region_name": self.config.region, - } - - if self.config.endpoint_url: - client_kwargs["endpoint_url"] = self.config.endpoint_url - - if self.config.access_key and self.config.secret_key: - client_kwargs["aws_access_key_id"] = self.config.access_key - client_kwargs["aws_secret_access_key"] = self.config.secret_key - - if self.config.session_token: - client_kwargs["aws_session_token"] = self.config.session_token - - client_kwargs["config"] = self._get_client_config() - - ctx_manager = self._session.create_client(**client_kwargs) - self._client = await self._exit_stack.enter_async_context(ctx_manager) - - def _get_client_config(self) -> Any: - """Get boto client config.""" - from botocore.config import Config - - return Config( - signature_version="s3v4", - s3={"addressing_style": self.config.addressing_style}, - ) - - async def close(self) -> None: - """Close the connection.""" - if self._exit_stack: - await self._exit_stack.aclose() - self._exit_stack = None - self._client = None - self._session = None - - async def health_check(self) -> bool: - """Check if S3 service is healthy. - - Returns: - True if service is reachable. - """ - if not self._client: - return False - try: - await self._client.list_buckets() - return True - except Exception: - return False - - # Bucket operations - - async def create_bucket(self, bucket: Optional[str] = None) -> None: - """Create a bucket. - - Args: - bucket: Bucket name (uses default if not specified). - """ - bucket_name = bucket or self.config.bucket - create_config = {} - if self.config.region != "us-east-1": - create_config["CreateBucketConfiguration"] = { - "LocationConstraint": self.config.region - } - await self._client.create_bucket(Bucket=bucket_name, **create_config) - - async def delete_bucket(self, bucket: Optional[str] = None) -> None: - """Delete a bucket. - - Args: - bucket: Bucket name (uses default if not specified). - """ - bucket_name = bucket or self.config.bucket - await self._client.delete_bucket(Bucket=bucket_name) - - async def bucket_exists(self, bucket: Optional[str] = None) -> bool: - """Check if a bucket exists. - - Args: - bucket: Bucket name (uses default if not specified). - - Returns: - True if bucket exists. - """ - bucket_name = bucket or self.config.bucket - try: - await self._client.head_bucket(Bucket=bucket_name) - return True - except Exception: - return False - - async def list_buckets(self) -> list[str]: - """List all buckets. - - Returns: - List of bucket names. - """ - response = await self._client.list_buckets() - return [b["Name"] for b in response.get("Buckets", [])] - - # Object operations - - async def put_object( - self, - key: str, - data: Union[bytes, BinaryIO, BytesIO], - content_type: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, - bucket: Optional[str] = None, - ) -> UploadResult: - """Upload an object. - - Args: - key: Object key. - data: Object data as bytes or file-like object. - content_type: Content type (auto-detected if not specified). - metadata: Custom metadata. - bucket: Bucket name (uses default if not specified). - - Returns: - Upload result with etag. - """ - bucket_name = bucket or self.config.bucket - - put_kwargs: dict[str, Any] = { - "Bucket": bucket_name, - "Key": key, - "Body": data, - } - - if content_type: - put_kwargs["ContentType"] = content_type - if metadata: - put_kwargs["Metadata"] = metadata - - response = await self._client.put_object(**put_kwargs) - return UploadResult( - key=key, - etag=response.get("ETag", "").strip('"'), - version_id=response.get("VersionId"), - ) - - async def get_object( - self, - key: str, - bucket: Optional[str] = None, - ) -> bytes: - """Download an object. - - Args: - key: Object key. - bucket: Bucket name (uses default if not specified). - - Returns: - Object data as bytes. - """ - bucket_name = bucket or self.config.bucket - response = await self._client.get_object(Bucket=bucket_name, Key=key) - async with response["Body"] as stream: - return await stream.read() - - async def get_object_stream( - self, - key: str, - bucket: Optional[str] = None, - ) -> AsyncIterator[bytes]: - """Stream an object in chunks. - - Args: - key: Object key. - bucket: Bucket name (uses default if not specified). - - Yields: - Chunks of object data. - """ - bucket_name = bucket or self.config.bucket - response = await self._client.get_object(Bucket=bucket_name, Key=key) - async with response["Body"] as stream: - async for chunk in stream.iter_chunks(): - yield chunk[0] # iter_chunks returns (chunk, final) - - async def delete_object( - self, - key: str, - bucket: Optional[str] = None, - ) -> None: - """Delete an object. - - Args: - key: Object key. - bucket: Bucket name (uses default if not specified). - """ - bucket_name = bucket or self.config.bucket - await self._client.delete_object(Bucket=bucket_name, Key=key) - - async def delete_objects( - self, - keys: list[str], - bucket: Optional[str] = None, - ) -> list[str]: - """Delete multiple objects. - - Args: - keys: Object keys to delete. - bucket: Bucket name (uses default if not specified). - - Returns: - List of deleted keys. - """ - bucket_name = bucket or self.config.bucket - response = await self._client.delete_objects( - Bucket=bucket_name, - Delete={"Objects": [{"Key": k} for k in keys]}, - ) - return [d["Key"] for d in response.get("Deleted", [])] - - async def head_object( - self, - key: str, - bucket: Optional[str] = None, - ) -> ObjectInfo: - """Get object metadata without downloading. - - Args: - key: Object key. - bucket: Bucket name (uses default if not specified). - - Returns: - Object info with metadata. - """ - bucket_name = bucket or self.config.bucket - response = await self._client.head_object(Bucket=bucket_name, Key=key) - return ObjectInfo( - key=key, - size=response.get("ContentLength", 0), - etag=response.get("ETag", "").strip('"'), - last_modified=response.get("LastModified", datetime.now()), - content_type=response.get("ContentType"), - metadata=response.get("Metadata", {}), - ) - - async def object_exists( - self, - key: str, - bucket: Optional[str] = None, - ) -> bool: - """Check if an object exists. - - Args: - key: Object key. - bucket: Bucket name (uses default if not specified). - - Returns: - True if object exists. - """ - try: - await self.head_object(key, bucket) - return True - except Exception: - return False - - async def list_objects( - self, - prefix: str = "", - bucket: Optional[str] = None, - max_keys: int = 1000, - ) -> AsyncIterator[ObjectInfo]: - """List objects with a prefix. - - Args: - prefix: Key prefix filter. - bucket: Bucket name (uses default if not specified). - max_keys: Maximum keys per request. - - Yields: - Object info for each matching object. - """ - bucket_name = bucket or self.config.bucket - continuation_token = None - - while True: - kwargs: dict[str, Any] = { - "Bucket": bucket_name, - "Prefix": prefix, - "MaxKeys": max_keys, - } - if continuation_token: - kwargs["ContinuationToken"] = continuation_token - - response = await self._client.list_objects_v2(**kwargs) - - for obj in response.get("Contents", []): - yield ObjectInfo( - key=obj["Key"], - size=obj["Size"], - etag=obj["ETag"].strip('"'), - last_modified=obj["LastModified"], - ) - - if not response.get("IsTruncated"): - break - continuation_token = response.get("NextContinuationToken") - - # Copy operations - - async def copy_object( - self, - source_key: str, - dest_key: str, - source_bucket: Optional[str] = None, - dest_bucket: Optional[str] = None, - ) -> UploadResult: - """Copy an object. - - Args: - source_key: Source object key. - dest_key: Destination object key. - source_bucket: Source bucket (uses default if not specified). - dest_bucket: Destination bucket (uses default if not specified). - - Returns: - Upload result for the copy. - """ - src_bucket = source_bucket or self.config.bucket - dst_bucket = dest_bucket or self.config.bucket - - response = await self._client.copy_object( - Bucket=dst_bucket, - Key=dest_key, - CopySource={"Bucket": src_bucket, "Key": source_key}, - ) - return UploadResult( - key=dest_key, - etag=response.get("CopyObjectResult", {}).get("ETag", "").strip('"'), - version_id=response.get("VersionId"), - ) - - # Presigned URLs - - async def presign_get( - self, - key: str, - expires_in: int = 3600, - bucket: Optional[str] = None, - ) -> PresignedUrl: - """Generate a presigned URL for download. - - Args: - key: Object key. - expires_in: URL validity in seconds (default: 1 hour). - bucket: Bucket name (uses default if not specified). - - Returns: - Presigned URL. - """ - bucket_name = bucket or self.config.bucket - url = await self._client.generate_presigned_url( - "get_object", - Params={"Bucket": bucket_name, "Key": key}, - ExpiresIn=expires_in, - ) - return PresignedUrl(url=url, expires_in=expires_in) - - async def presign_put( - self, - key: str, - expires_in: int = 3600, - content_type: Optional[str] = None, - bucket: Optional[str] = None, - ) -> PresignedUrl: - """Generate a presigned URL for upload. - - Args: - key: Object key. - expires_in: URL validity in seconds (default: 1 hour). - content_type: Required content type. - bucket: Bucket name (uses default if not specified). - - Returns: - Presigned URL. - """ - bucket_name = bucket or self.config.bucket - params: dict[str, Any] = {"Bucket": bucket_name, "Key": key} - if content_type: - params["ContentType"] = content_type - - url = await self._client.generate_presigned_url( - "put_object", - Params=params, - ExpiresIn=expires_in, - ) - return PresignedUrl(url=url, expires_in=expires_in) - - async def __aenter__(self) -> StorageClient: - """Async context manager entry.""" - await self.connect() - return self - - async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: - """Async context manager exit.""" - await self.close() diff --git a/pkg/hanzo/src/hanzo/infra/tasks.py b/pkg/hanzo/src/hanzo/infra/tasks.py deleted file mode 100644 index d1abda543..000000000 --- a/pkg/hanzo/src/hanzo/infra/tasks.py +++ /dev/null @@ -1,540 +0,0 @@ -"""Temporal workflow client wrapper for Hanzo infrastructure. - -Provides async interface to Temporal for durable workflows, -activities, and task orchestration. -""" - -from __future__ import annotations - -import os -from typing import Any, TypeVar, Callable, Optional, Sequence -from datetime import datetime, timedelta -from dataclasses import field, dataclass - -from pydantic import Field, BaseModel - -T = TypeVar("T") - - -class TasksConfig(BaseModel): - """Configuration for Temporal connection.""" - - host: str = Field(default="localhost", description="Temporal server host") - port: int = Field(default=7233, description="Temporal server port") - namespace: str = Field(default="default", description="Temporal namespace") - target: Optional[str] = Field( - default=None, description="Full target address (overrides host/port)" - ) - tls: bool = Field(default=False, description="Use TLS") - tls_cert_path: Optional[str] = Field( - default=None, description="Path to TLS certificate" - ) - tls_key_path: Optional[str] = Field(default=None, description="Path to TLS key") - api_key: Optional[str] = Field( - default=None, description="API key for Temporal Cloud" - ) - identity: str = Field(default="hanzo-client", description="Client identity") - data_converter: Optional[str] = Field( - default=None, description="Custom data converter class" - ) - - @classmethod - def from_env(cls) -> TasksConfig: - """Create config from environment variables. - - Environment variables: - TEMPORAL_HOST: Server host (default: localhost) - TEMPORAL_PORT: Server port (default: 7233) - TEMPORAL_NAMESPACE: Namespace (default: default) - TEMPORAL_TARGET: Full target address (overrides host/port) - TEMPORAL_TLS: Use TLS (default: false) - TEMPORAL_TLS_CERT: Path to TLS certificate - TEMPORAL_TLS_KEY: Path to TLS key - TEMPORAL_API_KEY: API key for Temporal Cloud - """ - return cls( - host=os.getenv("TEMPORAL_HOST", "localhost"), - port=int(os.getenv("TEMPORAL_PORT", "7233")), - namespace=os.getenv("TEMPORAL_NAMESPACE", "default"), - target=os.getenv("TEMPORAL_TARGET"), - tls=os.getenv("TEMPORAL_TLS", "").lower() in ("true", "1", "yes"), - tls_cert_path=os.getenv("TEMPORAL_TLS_CERT"), - tls_key_path=os.getenv("TEMPORAL_TLS_KEY"), - api_key=os.getenv("TEMPORAL_API_KEY"), - ) - - @property - def effective_target(self) -> str: - """Get the effective target address.""" - return self.target or f"{self.host}:{self.port}" - - -@dataclass -class WorkflowExecution: - """Information about a workflow execution.""" - - workflow_id: str - run_id: str - workflow_type: str - status: str - start_time: Optional[datetime] = None - close_time: Optional[datetime] = None - history_length: int = 0 - memo: dict[str, Any] = field(default_factory=dict) - search_attributes: dict[str, Any] = field(default_factory=dict) - - -@dataclass -class WorkflowHandle: - """Handle to a running workflow.""" - - workflow_id: str - run_id: Optional[str] = None - _handle: Any = None - - async def result(self) -> Any: - """Wait for and return the workflow result.""" - if self._handle: - return await self._handle.result() - return None - - async def cancel(self) -> None: - """Request cancellation of the workflow.""" - if self._handle: - await self._handle.cancel() - - async def terminate(self, reason: str = "") -> None: - """Terminate the workflow.""" - if self._handle: - await self._handle.terminate(reason) - - async def signal(self, name: str, *args: Any) -> None: - """Send a signal to the workflow.""" - if self._handle: - await self._handle.signal(name, *args) - - async def query(self, name: str, *args: Any) -> Any: - """Query the workflow state.""" - if self._handle: - return await self._handle.query(name, *args) - return None - - async def describe(self) -> WorkflowExecution: - """Get workflow execution details.""" - if self._handle: - desc = await self._handle.describe() - return WorkflowExecution( - workflow_id=desc.id, - run_id=desc.run_id, - workflow_type=desc.workflow_type, - status=str(desc.status), - start_time=desc.start_time, - close_time=desc.close_time, - ) - return WorkflowExecution( - workflow_id=self.workflow_id, - run_id=self.run_id or "", - workflow_type="", - status="UNKNOWN", - ) - - -class TasksClient: - """Async client for Temporal workflow orchestration. - - Wraps temporalio SDK with Hanzo conventions for workflow - execution, activities, and task management. - - Example: - ```python - client = TasksClient(TasksConfig.from_env()) - await client.connect() - - # Start a workflow - handle = await client.start_workflow( - "ProcessOrder", - args={"order_id": "123"}, - id="order-123", - task_queue="orders", - ) - - # Wait for result - result = await handle.result() - - # Query workflow state - state = await handle.query("get_status") - - # Send signal - await handle.signal("approve", {"user": "admin"}) - - # List workflows - async for wf in client.list_workflows("WorkflowType = 'ProcessOrder'"): - print(wf.workflow_id, wf.status) - ``` - """ - - def __init__(self, config: Optional[TasksConfig] = None) -> None: - """Initialize tasks client. - - Args: - config: Temporal configuration. If None, loads from environment. - """ - self.config = config or TasksConfig.from_env() - self._client: Any = None - - async def connect(self) -> None: - """Establish connection to Temporal server.""" - try: - from temporalio.client import Client, TLSConfig - except ImportError as e: - raise ImportError( - "temporalio is required for TasksClient. Install with: pip install temporalio" - ) from e - - connect_kwargs: dict[str, Any] = { - "target_host": self.config.effective_target, - "namespace": self.config.namespace, - "identity": self.config.identity, - } - - if self.config.tls: - tls_config = TLSConfig() - if self.config.tls_cert_path and self.config.tls_key_path: - with open(self.config.tls_cert_path, "rb") as f: - cert = f.read() - with open(self.config.tls_key_path, "rb") as f: - key = f.read() - tls_config = TLSConfig(client_cert=cert, client_private_key=key) - connect_kwargs["tls"] = tls_config - - if self.config.api_key: - connect_kwargs["api_key"] = self.config.api_key - - self._client = await Client.connect(**connect_kwargs) - - async def close(self) -> None: - """Close the connection.""" - # Temporal client doesn't require explicit close - self._client = None - - async def health_check(self) -> bool: - """Check if Temporal server is healthy. - - Returns: - True if server is reachable. - """ - if not self._client: - return False - try: - # Try to get system info - await self._client.service_client.check_health() - return True - except Exception: - # Fallback: try listing workflows with limit 1 - try: - async for _ in self._client.list_workflows(query="", page_size=1): - break - return True - except Exception: - return False - - # Workflow operations - - async def start_workflow( - self, - workflow: str, - args: Any = None, - id: Optional[str] = None, - task_queue: str = "default", - execution_timeout: Optional[timedelta] = None, - run_timeout: Optional[timedelta] = None, - task_timeout: Optional[timedelta] = None, - id_reuse_policy: str = "allow_duplicate", - retry_policy: Optional[dict[str, Any]] = None, - cron_schedule: Optional[str] = None, - memo: Optional[dict[str, Any]] = None, - search_attributes: Optional[dict[str, Any]] = None, - ) -> WorkflowHandle: - """Start a workflow execution. - - Args: - workflow: Workflow type name. - args: Workflow arguments. - id: Workflow ID (auto-generated if not specified). - task_queue: Task queue for the workflow. - execution_timeout: Max workflow execution time. - run_timeout: Max single run time. - task_timeout: Max workflow task time. - id_reuse_policy: ID reuse policy (allow_duplicate, allow_duplicate_failed_only, reject_duplicate, terminate_if_running). - retry_policy: Retry configuration. - cron_schedule: Cron schedule expression. - memo: Workflow memo fields. - search_attributes: Custom search attributes. - - Returns: - Handle to the started workflow. - """ - import uuid - - from temporalio.client import WorkflowIDReusePolicy - from temporalio.common import RetryPolicy - - policy_map = { - "allow_duplicate": WorkflowIDReusePolicy.ALLOW_DUPLICATE, - "allow_duplicate_failed_only": WorkflowIDReusePolicy.ALLOW_DUPLICATE_FAILED_ONLY, - "reject_duplicate": WorkflowIDReusePolicy.REJECT_DUPLICATE, - "terminate_if_running": WorkflowIDReusePolicy.TERMINATE_IF_RUNNING, - } - - start_kwargs: dict[str, Any] = { - "workflow": workflow, - "arg": args, - "id": id or f"{workflow}-{uuid.uuid4().hex[:8]}", - "task_queue": task_queue, - "id_reuse_policy": policy_map.get( - id_reuse_policy, WorkflowIDReusePolicy.ALLOW_DUPLICATE - ), - } - - if execution_timeout: - start_kwargs["execution_timeout"] = execution_timeout - if run_timeout: - start_kwargs["run_timeout"] = run_timeout - if task_timeout: - start_kwargs["task_timeout"] = task_timeout - if cron_schedule: - start_kwargs["cron_schedule"] = cron_schedule - if memo: - start_kwargs["memo"] = memo - if search_attributes: - start_kwargs["search_attributes"] = search_attributes - - if retry_policy: - start_kwargs["retry_policy"] = RetryPolicy( - initial_interval=timedelta( - seconds=retry_policy.get("initial_interval", 1) - ), - maximum_interval=timedelta( - seconds=retry_policy.get("maximum_interval", 100) - ), - backoff_coefficient=retry_policy.get("backoff_coefficient", 2.0), - maximum_attempts=retry_policy.get("maximum_attempts", 0), - ) - - handle = await self._client.start_workflow(**start_kwargs) - return WorkflowHandle( - workflow_id=handle.id, - run_id=handle.result_run_id, - _handle=handle, - ) - - async def execute_workflow( - self, - workflow: str, - args: Any = None, - id: Optional[str] = None, - task_queue: str = "default", - **kwargs: Any, - ) -> Any: - """Start a workflow and wait for its result. - - Args: - workflow: Workflow type name. - args: Workflow arguments. - id: Workflow ID. - task_queue: Task queue. - **kwargs: Additional start_workflow arguments. - - Returns: - Workflow result. - """ - handle = await self.start_workflow(workflow, args, id, task_queue, **kwargs) - return await handle.result() - - async def get_workflow_handle( - self, - workflow_id: str, - run_id: Optional[str] = None, - ) -> WorkflowHandle: - """Get a handle to an existing workflow. - - Args: - workflow_id: Workflow ID. - run_id: Optional specific run ID. - - Returns: - Workflow handle. - """ - handle = self._client.get_workflow_handle(workflow_id, run_id=run_id) - return WorkflowHandle( - workflow_id=workflow_id, - run_id=run_id, - _handle=handle, - ) - - async def list_workflows( - self, - query: str = "", - page_size: int = 100, - ): - """List workflow executions. - - Args: - query: Temporal list filter query. - page_size: Results per page. - - Yields: - WorkflowExecution for each matching workflow. - """ - async for wf in self._client.list_workflows(query=query, page_size=page_size): - yield WorkflowExecution( - workflow_id=wf.id, - run_id=wf.run_id, - workflow_type=wf.workflow_type, - status=str(wf.status), - start_time=wf.start_time, - close_time=wf.close_time, - memo=dict(wf.memo) if wf.memo else {}, - search_attributes=( - dict(wf.search_attributes) if wf.search_attributes else {} - ), - ) - - async def count_workflows(self, query: str = "") -> int: - """Count workflow executions matching a query. - - Args: - query: Temporal list filter query. - - Returns: - Number of matching workflows. - """ - count = 0 - async for _ in self._client.list_workflows(query=query): - count += 1 - return count - - # Schedule operations - - async def create_schedule( - self, - schedule_id: str, - workflow: str, - args: Any = None, - task_queue: str = "default", - cron: Optional[str] = None, - interval: Optional[timedelta] = None, - calendar: Optional[dict[str, Any]] = None, - start_at: Optional[datetime] = None, - end_at: Optional[datetime] = None, - jitter: Optional[timedelta] = None, - memo: Optional[dict[str, Any]] = None, - ) -> None: - """Create a workflow schedule. - - Args: - schedule_id: Unique schedule ID. - workflow: Workflow type name. - args: Workflow arguments. - task_queue: Task queue. - cron: Cron expression (e.g., "0 * * * *"). - interval: Interval between runs. - calendar: Calendar-based schedule spec. - start_at: Schedule start time. - end_at: Schedule end time. - jitter: Random jitter to add to scheduled times. - memo: Schedule memo fields. - """ - from temporalio.client import ( - Schedule, - ScheduleSpec, - ScheduleCalendarSpec, - ScheduleIntervalSpec, - ScheduleActionStartWorkflow, - ) - - specs = [] - if cron: - specs.append(ScheduleSpec(cron_expressions=[cron])) - if interval: - specs.append(ScheduleSpec(intervals=[ScheduleIntervalSpec(every=interval)])) - if calendar: - specs.append(ScheduleSpec(calendars=[ScheduleCalendarSpec(**calendar)])) - - if not specs: - raise ValueError( - "At least one of cron, interval, or calendar must be specified" - ) - - schedule_spec = ( - specs[0] - if len(specs) == 1 - else ScheduleSpec( - cron_expressions=[cron] if cron else None, - intervals=[ScheduleIntervalSpec(every=interval)] if interval else None, - calendars=[ScheduleCalendarSpec(**calendar)] if calendar else None, - start_at=start_at, - end_at=end_at, - jitter=jitter, - ) - ) - - await self._client.create_schedule( - schedule_id, - Schedule( - action=ScheduleActionStartWorkflow( - workflow, - arg=args, - task_queue=task_queue, - ), - spec=schedule_spec, - ), - memo=memo, - ) - - async def delete_schedule(self, schedule_id: str) -> None: - """Delete a schedule. - - Args: - schedule_id: Schedule ID to delete. - """ - handle = self._client.get_schedule_handle(schedule_id) - await handle.delete() - - async def pause_schedule(self, schedule_id: str, note: str = "") -> None: - """Pause a schedule. - - Args: - schedule_id: Schedule ID to pause. - note: Optional note about why paused. - """ - handle = self._client.get_schedule_handle(schedule_id) - await handle.pause(note=note) - - async def unpause_schedule(self, schedule_id: str, note: str = "") -> None: - """Unpause a schedule. - - Args: - schedule_id: Schedule ID to unpause. - note: Optional note. - """ - handle = self._client.get_schedule_handle(schedule_id) - await handle.unpause(note=note) - - async def trigger_schedule(self, schedule_id: str) -> None: - """Trigger a schedule immediately. - - Args: - schedule_id: Schedule ID to trigger. - """ - handle = self._client.get_schedule_handle(schedule_id) - await handle.trigger() - - async def __aenter__(self) -> TasksClient: - """Async context manager entry.""" - await self.connect() - return self - - async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: - """Async context manager exit.""" - await self.close() diff --git a/pkg/hanzo/src/hanzo/infra/vector.py b/pkg/hanzo/src/hanzo/infra/vector.py deleted file mode 100644 index 9f5019607..000000000 --- a/pkg/hanzo/src/hanzo/infra/vector.py +++ /dev/null @@ -1,353 +0,0 @@ -"""Qdrant vector database client wrapper for Hanzo infrastructure. - -Provides async interface to Qdrant for vector similarity search, -supporting both cloud and local deployments. -""" - -from __future__ import annotations - -import os -from typing import Any, Optional, Sequence -from dataclasses import field, dataclass - -from pydantic import Field, BaseModel - - -class VectorConfig(BaseModel): - """Configuration for Qdrant vector database connection.""" - - host: str = Field(default="localhost", description="Qdrant server host") - port: int = Field(default=6333, description="Qdrant REST API port") - grpc_port: int = Field(default=6334, description="Qdrant gRPC port") - api_key: Optional[str] = Field(default=None, description="API key for Qdrant Cloud") - url: Optional[str] = Field( - default=None, description="Full URL (overrides host/port)" - ) - https: bool = Field(default=False, description="Use HTTPS") - timeout: float = Field(default=30.0, description="Request timeout in seconds") - prefer_grpc: bool = Field(default=True, description="Prefer gRPC over REST") - - @classmethod - def from_env(cls) -> VectorConfig: - """Create config from environment variables. - - Environment variables: - QDRANT_HOST: Server host (default: localhost) - QDRANT_PORT: REST API port (default: 6333) - QDRANT_GRPC_PORT: gRPC port (default: 6334) - QDRANT_API_KEY: API key for authentication - QDRANT_URL: Full URL (overrides host/port) - QDRANT_HTTPS: Use HTTPS (default: false) - """ - return cls( - host=os.getenv("QDRANT_HOST", "localhost"), - port=int(os.getenv("QDRANT_PORT", "6333")), - grpc_port=int(os.getenv("QDRANT_GRPC_PORT", "6334")), - api_key=os.getenv("QDRANT_API_KEY"), - url=os.getenv("QDRANT_URL"), - https=os.getenv("QDRANT_HTTPS", "").lower() in ("true", "1", "yes"), - ) - - -@dataclass -class VectorPoint: - """A point in vector space with optional payload.""" - - id: str | int - vector: list[float] - payload: dict[str, Any] = field(default_factory=dict) - - -@dataclass -class ScoredPoint: - """A search result with similarity score.""" - - id: str | int - score: float - payload: dict[str, Any] = field(default_factory=dict) - vector: Optional[list[float]] = None - - -class VectorClient: - """Async client for Qdrant vector database. - - Wraps qdrant-client with async methods and Hanzo conventions. - - Example: - ```python - client = VectorClient(VectorConfig.from_env()) - await client.connect() - - # Create collection - await client.create_collection("embeddings", vector_size=1536) - - # Upsert vectors - points = [VectorPoint(id="doc1", vector=[0.1] * 1536, payload={"text": "hello"})] - await client.upsert("embeddings", points) - - # Search - results = await client.search("embeddings", query_vector=[0.1] * 1536, limit=10) - ``` - """ - - def __init__(self, config: Optional[VectorConfig] = None) -> None: - """Initialize vector client. - - Args: - config: Qdrant configuration. If None, loads from environment. - """ - self.config = config or VectorConfig.from_env() - self._client: Any = None - self._async_client: Any = None - - async def connect(self) -> None: - """Establish connection to Qdrant server.""" - try: - from qdrant_client import AsyncQdrantClient - except ImportError as e: - raise ImportError( - "qdrant-client is required for VectorClient. Install with: pip install qdrant-client" - ) from e - - if self.config.url: - self._async_client = AsyncQdrantClient( - url=self.config.url, - api_key=self.config.api_key, - timeout=self.config.timeout, - prefer_grpc=self.config.prefer_grpc, - ) - else: - self._async_client = AsyncQdrantClient( - host=self.config.host, - port=self.config.port, - grpc_port=self.config.grpc_port, - api_key=self.config.api_key, - https=self.config.https, - timeout=self.config.timeout, - prefer_grpc=self.config.prefer_grpc, - ) - - async def close(self) -> None: - """Close the connection.""" - if self._async_client: - await self._async_client.close() - self._async_client = None - - async def health_check(self) -> bool: - """Check if Qdrant server is healthy. - - Returns: - True if server is reachable and healthy. - """ - if not self._async_client: - return False - try: - await self._async_client.get_collections() - return True - except Exception: - return False - - async def create_collection( - self, - name: str, - vector_size: int, - distance: str = "Cosine", - on_disk: bool = False, - ) -> None: - """Create a new collection. - - Args: - name: Collection name. - vector_size: Dimension of vectors. - distance: Distance metric (Cosine, Euclid, Dot). - on_disk: Store vectors on disk instead of RAM. - """ - from qdrant_client.models import Distance, VectorParams - - distance_map = { - "Cosine": Distance.COSINE, - "Euclid": Distance.EUCLID, - "Dot": Distance.DOT, - } - - await self._async_client.create_collection( - collection_name=name, - vectors_config=VectorParams( - size=vector_size, - distance=distance_map.get(distance, Distance.COSINE), - on_disk=on_disk, - ), - ) - - async def delete_collection(self, name: str) -> None: - """Delete a collection. - - Args: - name: Collection name to delete. - """ - await self._async_client.delete_collection(collection_name=name) - - async def collection_exists(self, name: str) -> bool: - """Check if a collection exists. - - Args: - name: Collection name. - - Returns: - True if collection exists. - """ - return await self._async_client.collection_exists(collection_name=name) - - async def list_collections(self) -> list[str]: - """List all collections. - - Returns: - List of collection names. - """ - result = await self._async_client.get_collections() - return [c.name for c in result.collections] - - async def upsert( - self, - collection: str, - points: Sequence[VectorPoint], - ) -> None: - """Insert or update vectors. - - Args: - collection: Collection name. - points: Points to upsert. - """ - from qdrant_client.models import PointStruct - - qdrant_points = [ - PointStruct(id=p.id, vector=p.vector, payload=p.payload) for p in points - ] - await self._async_client.upsert( - collection_name=collection, points=qdrant_points - ) - - async def search( - self, - collection: str, - query_vector: list[float], - limit: int = 10, - score_threshold: Optional[float] = None, - filter_conditions: Optional[dict[str, Any]] = None, - with_vectors: bool = False, - ) -> list[ScoredPoint]: - """Search for similar vectors. - - Args: - collection: Collection name. - query_vector: Query vector. - limit: Maximum results to return. - score_threshold: Minimum similarity score. - filter_conditions: Qdrant filter conditions. - with_vectors: Include vectors in results. - - Returns: - List of scored points sorted by similarity. - """ - from qdrant_client.models import Filter - - qdrant_filter = None - if filter_conditions: - qdrant_filter = Filter(**filter_conditions) - - results = await self._async_client.search( - collection_name=collection, - query_vector=query_vector, - limit=limit, - score_threshold=score_threshold, - query_filter=qdrant_filter, - with_vectors=with_vectors, - ) - - return [ - ScoredPoint( - id=r.id, - score=r.score, - payload=r.payload or {}, - vector=r.vector if with_vectors else None, - ) - for r in results - ] - - async def delete( - self, - collection: str, - ids: Optional[Sequence[str | int]] = None, - filter_conditions: Optional[dict[str, Any]] = None, - ) -> None: - """Delete vectors by ID or filter. - - Args: - collection: Collection name. - ids: Point IDs to delete. - filter_conditions: Filter conditions for deletion. - """ - from qdrant_client.models import Filter, PointIdsList - - if ids: - await self._async_client.delete( - collection_name=collection, - points_selector=PointIdsList(points=list(ids)), - ) - elif filter_conditions: - await self._async_client.delete( - collection_name=collection, - points_selector=Filter(**filter_conditions), - ) - - async def get( - self, - collection: str, - ids: Sequence[str | int], - with_vectors: bool = False, - ) -> list[VectorPoint]: - """Retrieve vectors by ID. - - Args: - collection: Collection name. - ids: Point IDs to retrieve. - with_vectors: Include vectors in results. - - Returns: - List of retrieved points. - """ - results = await self._async_client.retrieve( - collection_name=collection, - ids=list(ids), - with_vectors=with_vectors, - ) - - return [ - VectorPoint( - id=r.id, - vector=r.vector if with_vectors and r.vector else [], - payload=r.payload or {}, - ) - for r in results - ] - - async def count(self, collection: str) -> int: - """Count points in a collection. - - Args: - collection: Collection name. - - Returns: - Number of points. - """ - info = await self._async_client.get_collection(collection_name=collection) - return info.points_count or 0 - - async def __aenter__(self) -> VectorClient: - """Async context manager entry.""" - await self.connect() - return self - - async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: - """Async context manager exit.""" - await self.close() diff --git a/pkg/hanzo/src/hanzo/interactive/__init__.py b/pkg/hanzo/src/hanzo/interactive/__init__.py deleted file mode 100644 index e6f978f36..000000000 --- a/pkg/hanzo/src/hanzo/interactive/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Interactive modules for Hanzo CLI.""" - -__all__ = ["repl", "dashboard"] diff --git a/pkg/hanzo/src/hanzo/interactive/dashboard.py b/pkg/hanzo/src/hanzo/interactive/dashboard.py deleted file mode 100644 index c0e2b8d7d..000000000 --- a/pkg/hanzo/src/hanzo/interactive/dashboard.py +++ /dev/null @@ -1,210 +0,0 @@ -"""Dashboard interface for Hanzo CLI.""" - -import os -from typing import Any, Dict, List, Optional -from datetime import datetime - -from rich.live import Live -from rich.text import Text -from rich.panel import Panel -from rich.table import Table -from rich.layout import Layout -from rich.console import Console - - -def _get_cluster_status() -> Optional[Dict[str, Any]]: - """Get cluster status from API. Returns None if not connected.""" - try: - import json - import urllib.request - - endpoint = os.getenv("HANZO_CLUSTER_URL", "http://localhost:8000") - req = urllib.request.urlopen(f"{endpoint}/health", timeout=2) # noqa: S310 - return json.loads(req.read().decode()) - except Exception: - return None - - -def _get_agents() -> List[Dict[str, Any]]: - """Get agents from registry. Returns empty list if not available.""" - try: - from hanzoai.agents import list_agents - - return list_agents() or [] - except Exception: - return [] - - -def _get_jobs() -> List[Dict[str, Any]]: - """Get recent jobs. Returns empty list if not available.""" - try: - import json - import urllib.request - - endpoint = os.getenv("HANZO_CLUSTER_URL", "http://localhost:8000") - req = urllib.request.urlopen( # noqa: S310 - f"{endpoint}/jobs?limit=5", timeout=2 - ) - return json.loads(req.read().decode()).get("jobs", []) - except Exception: - return [] - - -def _get_logs() -> List[str]: - """Get recent logs. Returns empty list if not available.""" - try: - import json - import urllib.request - - endpoint = os.getenv("HANZO_CLUSTER_URL", "http://localhost:8000") - req = urllib.request.urlopen( # noqa: S310 - f"{endpoint}/logs?limit=5", timeout=2 - ) - return json.loads(req.read().decode()).get("logs", []) - except Exception: - return [] - - -def run_dashboard(refresh_rate: float = 1.0): - """Run the interactive dashboard.""" - console = Console() - - layout = Layout() - layout.split_column( - Layout(name="header", size=3), - Layout(name="body"), - Layout(name="footer", size=3), - ) - - layout["body"].split_row(Layout(name="left"), Layout(name="right")) - layout["left"].split_column(Layout(name="cluster", size=10), Layout(name="agents")) - layout["right"].split_column(Layout(name="jobs", size=15), Layout(name="logs")) - - def get_header() -> Panel: - """Get header with connection status.""" - cluster = _get_cluster_status() - header_text = Text() - header_text.append("Hanzo AI Dashboard", style="bold cyan") - - if cluster: - header_text.append(" [CONNECTED]", style="bold green") - border = "green" - else: - header_text.append(" [DISCONNECTED]", style="bold red") - border = "red" - - return Panel(header_text, border_style=border) - - def get_cluster_panel() -> Panel: - """Get cluster status panel.""" - table = Table(show_header=False, box=None) - table.add_column("Key", style="cyan") - table.add_column("Value", style="white") - - cluster = _get_cluster_status() - if cluster: - table.add_row( - "Status", f"[green]{cluster.get('status', 'unknown')}[/green]" - ) - table.add_row("Nodes", str(cluster.get("nodes", 0))) - table.add_row("Models", ", ".join(cluster.get("models", [])) or "none") - table.add_row("Port", str(cluster.get("port", "-"))) - border = "green" - else: - table.add_row("Status", "[red]Not connected[/red]") - table.add_row("", "[dim]Set HANZO_CLUSTER_URL[/dim]") - border = "dim" - - return Panel(table, title="Cluster", border_style=border) - - def get_agents_panel() -> Panel: - """Get agents panel.""" - table = Table() - table.add_column("ID", style="cyan") - table.add_column("Name", style="green") - table.add_column("Status", style="yellow") - table.add_column("Jobs", style="magenta") - - agents = _get_agents() - if agents: - for agent in agents[:5]: - table.add_row( - agent.get("id", "-")[:4], - agent.get("name", "unknown"), - agent.get("status", "unknown"), - str(agent.get("jobs", 0)), - ) - border = "blue" - else: - table.add_row("-", "[dim]No agents[/dim]", "-", "-") - border = "dim" - - return Panel(table, title="Agents", border_style=border) - - def get_jobs_panel() -> Panel: - """Get jobs panel.""" - table = Table() - table.add_column("ID", style="cyan", width=8) - table.add_column("Type", style="green") - table.add_column("Status", style="yellow") - - jobs = _get_jobs() - if jobs: - for job in jobs[:5]: - table.add_row( - job.get("id", "-")[:6], - job.get("type", "unknown"), - job.get("status", "unknown"), - ) - border = "yellow" - else: - table.add_row("-", "[dim]No jobs[/dim]", "-") - border = "dim" - - return Panel(table, title="Recent Jobs", border_style=border) - - def get_logs_panel() -> Panel: - """Get logs panel.""" - logs = _get_logs() - if logs: - log_text = "\n".join( - f"[dim]{log.get('time', '')}[/dim] {log.get('message', '')}" - for log in logs[:5] - ) - border = "dim" - else: - log_text = "[dim]No logs available[/dim]" - border = "dim" - - return Panel(log_text, title="Logs", border_style=border) - - layout["footer"].update( - Panel( - "[bold]Q[/bold] Quit [bold]R[/bold] Refresh [bold]C[/bold] Clear", - border_style="dim", - ) - ) - - # Initial update - layout["header"].update(get_header()) - layout["cluster"].update(get_cluster_panel()) - layout["agents"].update(get_agents_panel()) - layout["jobs"].update(get_jobs_panel()) - layout["logs"].update(get_logs_panel()) - - try: - with Live(layout, refresh_per_second=1 / refresh_rate, screen=True): - while True: - import time - - time.sleep(refresh_rate) - - # Update all panels with real data - layout["header"].update(get_header()) - layout["cluster"].update(get_cluster_panel()) - layout["agents"].update(get_agents_panel()) - layout["jobs"].update(get_jobs_panel()) - layout["logs"].update(get_logs_panel()) - - except KeyboardInterrupt: - console.print("\n[yellow]Dashboard closed[/yellow]") diff --git a/pkg/hanzo/src/hanzo/interactive/enhanced_repl.py b/pkg/hanzo/src/hanzo/interactive/enhanced_repl.py deleted file mode 100644 index fbb56d4ba..000000000 --- a/pkg/hanzo/src/hanzo/interactive/enhanced_repl.py +++ /dev/null @@ -1,1069 +0,0 @@ -"""Enhanced REPL with model selection and authentication.""" - -import os -import json -import asyncio -from typing import Any, Dict, Optional -from pathlib import Path -from datetime import datetime - -import httpx -from rich import box -from rich.text import Text -from rich.panel import Panel -from rich.table import Table -from rich.console import Console -from rich.markdown import Markdown -from prompt_toolkit import PromptSession -from prompt_toolkit.history import FileHistory -from prompt_toolkit.completion import WordCompleter -from prompt_toolkit.auto_suggest import AutoSuggestFromHistory -from prompt_toolkit.formatted_text import HTML - -try: - from ..tools.detector import AITool, ToolDetector -except ImportError: - ToolDetector = None - AITool = None - -try: - from .model_selector import QuickModelSelector, BackgroundTaskManager -except ImportError: - QuickModelSelector = None - BackgroundTaskManager = None - -try: - from .todo_manager import TodoManager -except ImportError: - TodoManager = None - - -class EnhancedHanzoREPL: - """Enhanced REPL with model selection and authentication.""" - - # Available models - MODELS = { - # OpenAI - "gpt-4": "OpenAI GPT-4", - "gpt-4-turbo": "OpenAI GPT-4 Turbo", - "gpt-3.5-turbo": "OpenAI GPT-3.5 Turbo", - # Anthropic - "claude-3-opus": "Anthropic Claude 3 Opus", - "claude-3-sonnet": "Anthropic Claude 3 Sonnet", - "claude-3-haiku": "Anthropic Claude 3 Haiku", - "claude-2.1": "Anthropic Claude 2.1", - # Google - "gemini-pro": "Google Gemini Pro", - "gemini-pro-vision": "Google Gemini Pro Vision", - # Meta - "llama2-70b": "Meta Llama 2 70B", - "llama2-13b": "Meta Llama 2 13B", - "llama2-7b": "Meta Llama 2 7B", - "codellama-34b": "Meta Code Llama 34B", - # Mistral - "mistral-medium": "Mistral Medium", - "mistral-small": "Mistral Small", - "mixtral-8x7b": "Mixtral 8x7B", - # Local models - "local:llama2": "Local Llama 2", - "local:mistral": "Local Mistral", - "local:phi-2": "Local Phi-2", - } - - def get_all_models(self): - """Get all available models including detected tools.""" - models = dict(self.MODELS) - - # Add detected tools as models - if self.detected_tools: - for tool in self.detected_tools: - models[f"tool:{tool.name}"] = f"{tool.display_name} (Tool)" - - return models - - def __init__(self, console: Optional[Console] = None): - self.console = console or Console() - self.config_dir = Path.home() / ".hanzo" - self.config_file = self.config_dir / "config.json" - self.auth_file = self.config_dir / "auth.json" - - # Load configuration - self.config = self.load_config() - self.auth = self.load_auth() - - # Initialize tool detector - self.tool_detector = ToolDetector(console) if ToolDetector else None - self.detected_tools = [] - self.current_tool = None - self.failed_tools = set() # Track tools that have failed this session - - # Initialize background task manager - self.task_manager = ( - BackgroundTaskManager(console) if BackgroundTaskManager else None - ) - - # Initialize todo manager - self.todo_manager = TodoManager(console) if TodoManager else None - - # Detect available tools and set default - if self.tool_detector: - self.detected_tools = self.tool_detector.detect_all() - default_tool = self.tool_detector.get_default_tool() - - # If Claude Code is available, use it as default - if default_tool: - self.current_model = f"tool:{default_tool.name}" - self.current_tool = default_tool - self.console.print( - f"[green]โœ“ Detected {default_tool.display_name} as default AI assistant[/green]" - ) - else: - # Fallback to regular models - self.current_model = self.config.get("default_model", "gpt-3.5-turbo") - else: - # No tool detector, use regular models - self.current_model = self.config.get("default_model", "gpt-3.5-turbo") - - # Setup session - self.session = PromptSession( - history=FileHistory(str(self.config_dir / ".repl_history")), - auto_suggest=AutoSuggestFromHistory(), - ) - - # Commands - self.commands = { - "help": self.show_help, - "exit": self.exit_repl, - "quit": self.exit_repl, - "clear": self.clear_screen, - "status": self.show_status, - "model": self.change_model, - "models": self.list_models, - "tools": self.list_tools, - "agents": self.list_tools, # Alias for tools - "login": self.login, - "logout": self.logout, - "config": self.show_config, - "tasks": self.show_tasks, - "kill": self.kill_task, - "quick": self.quick_model_select, - "todo": self.manage_todos, - "todos": self.manage_todos, # Alias - } - - self.running = False - - def load_config(self) -> Dict[str, Any]: - """Load configuration from file.""" - if self.config_file.exists(): - try: - return json.loads(self.config_file.read_text()) - except Exception: - pass - return {} - - def save_config(self): - """Save configuration to file.""" - self.config_dir.mkdir(exist_ok=True) - self.config_file.write_text(json.dumps(self.config, indent=2)) - - def load_auth(self) -> Dict[str, Any]: - """Load authentication data.""" - if self.auth_file.exists(): - try: - return json.loads(self.auth_file.read_text()) - except Exception: - pass - return {} - - def save_auth(self): - """Save authentication data.""" - self.config_dir.mkdir(exist_ok=True) - self.auth_file.write_text(json.dumps(self.auth, indent=2)) - - def get_prompt(self) -> str: - """Get the simple prompt.""" - # We'll use a simple > prompt, the box is handled by prompt_toolkit - return "> " - - def is_authenticated(self) -> bool: - """Check if user is authenticated.""" - # Check for API key - if os.getenv("HANZO_API_KEY"): - return True - - # Check auth file - if self.auth.get("api_key"): - return True - - # Check if logged in - if self.auth.get("logged_in"): - return True - - return False - - def get_model_info(self): - """Get current model info string.""" - model = self.current_model - - # Check if using a tool - if model.startswith("tool:"): - if self.current_tool: - return f"[dim cyan]agent: {self.current_tool.display_name}[/dim cyan]" - else: - tool_name = model.replace("tool:", "") - return f"[dim cyan]agent: {tool_name}[/dim cyan]" - - # Determine provider from model name - if model.startswith("gpt"): - provider = "openai" - elif model.startswith("claude"): - provider = "anthropic" - elif model.startswith("gemini"): - provider = "google" - elif model.startswith("llama") or model.startswith("codellama"): - provider = "meta" - elif model.startswith("mistral") or model.startswith("mixtral"): - provider = "mistral" - elif model.startswith("local:"): - provider = "local" - else: - provider = "unknown" - - return f"[dim]model: {provider}/{model}[/dim]" - - async def run(self): - """Run the enhanced REPL.""" - self.running = True - - # Setup completer - commands = list(self.commands.keys()) - models = list(self.MODELS.keys()) - cli_commands = [ - "chat", - "ask", - "agent", - "node", - "mcp", - "network", - "auth", - "config", - "tools", - "miner", - "serve", - "net", - "dev", - "router", - ] - - completer = WordCompleter( - commands + models + cli_commands, - ignore_case=True, - ) - - while self.running: - try: - # Show model info above prompt - self.console.print(self.get_model_info()) - - # Get input with simple prompt - command = await self.session.prompt_async( - self.get_prompt(), - completer=completer, - vi_mode=True, # Enable vi mode for better navigation - ) - - if not command.strip(): - continue - - # Handle slash commands - if command.startswith("/"): - await self.handle_slash_command(command[1:]) - continue - - # Parse command - parts = command.strip().split(maxsplit=1) - cmd = parts[0].lower() - args = parts[1] if len(parts) > 1 else "" - - # Execute command - if cmd in self.commands: - await self.commands[cmd](args) - elif cmd in cli_commands: - await self.execute_command(cmd, args) - else: - # Treat as chat message - await self.chat_with_ai(command) - - except KeyboardInterrupt: - continue - except EOFError: - break - except Exception as e: - self.console.print(f"[red]Error: {e}[/red]") - - async def handle_slash_command(self, command: str): - """Handle slash commands like /model, /status, etc.""" - parts = command.strip().split(maxsplit=1) - cmd = parts[0].lower() - args = parts[1] if len(parts) > 1 else "" - - # Map slash commands to regular commands - slash_map = { - "m": "model", - "s": "status", - "h": "help", - "q": "quit", - "c": "clear", - "models": "models", - "login": "login", - "logout": "logout", - "todo": "todo", - "todos": "todos", - "t": "todo", # Shortcut for todo - } - - mapped_cmd = slash_map.get(cmd, cmd) - - if mapped_cmd in self.commands: - await self.commands[mapped_cmd](args) - else: - self.console.print(f"[yellow]Unknown command: /{cmd}[/yellow]") - self.console.print("[dim]Type /help for available commands[/dim]") - - async def show_status(self, args: str = ""): - """Show comprehensive status.""" - # Create status table - table = Table(title="System Status", box=box.ROUNDED) - table.add_column("Component", style="cyan") - table.add_column("Status", style="green") - table.add_column("Details", style="dim") - - # Authentication status - if self.is_authenticated(): - auth_status = "โœ… Authenticated" - auth_details = self.auth.get("email", "API Key configured") - else: - auth_status = "โŒ Not authenticated" - auth_details = "Run /login to authenticate" - table.add_row("Authentication", auth_status, auth_details) - - # Current model - model_name = self.MODELS.get(self.current_model, self.current_model) - table.add_row("Current Model", f"๐Ÿค– {self.current_model}", model_name) - - # Router status - try: - response = httpx.get("http://localhost:4000/health", timeout=1) - router_status = ( - "โœ… Running" if response.status_code == 200 else "โš ๏ธ Unhealthy" - ) - router_details = "Port 4000" - except Exception: - router_status = "โŒ Offline" - router_details = "Run 'hanzo router start'" - table.add_row("Router", router_status, router_details) - - # Node status - try: - response = httpx.get("http://localhost:3690/health", timeout=1) - node_status = "โœ… Running" if response.status_code == 200 else "โš ๏ธ Unhealthy" - node_details = "Port 3690" - except Exception: - node_status = "โŒ Offline" - node_details = "Run 'hanzo node start'" - table.add_row("Node", node_status, node_details) - - # API endpoints - if os.getenv("HANZO_API_KEY"): - api_status = "โœ… Configured" - api_details = "Using Hanzo Cloud API" - else: - api_status = "โš ๏ธ Not configured" - api_details = "Set HANZO_API_KEY environment variable" - table.add_row("Cloud API", api_status, api_details) - - self.console.print(table) - - # Show additional info - if self.auth.get("last_login"): - self.console.print(f"\n[dim]Last login: {self.auth['last_login']}[/dim]") - - async def change_model(self, args: str = ""): - """Change the current model or tool.""" - if not args: - # Show model selection menu - await self.list_models("") - self.console.print("\n[cyan]Enter model/tool name or number:[/cyan]") - - # Get selection - try: - selection = await self.session.prompt_async("> ") - - # Handle numeric selection - if selection.isdigit(): - num = int(selection) - - # Check if it's a tool selection - if self.detected_tools and num <= len(self.detected_tools): - tool = self.detected_tools[num - 1] - args = f"tool:{tool.name}" - else: - # It's a model selection - model_idx = ( - num - len(self.detected_tools) - 1 - if self.detected_tools - else num - 1 - ) - models_list = list(self.MODELS.keys()) - if 0 <= model_idx < len(models_list): - args = models_list[model_idx] - else: - self.console.print("[red]Invalid selection[/red]") - return - else: - args = selection - except (KeyboardInterrupt, EOFError): - return - - # Check if it's a tool - if ( - args.startswith("tool:") or args in [t.name for t in self.detected_tools] - if self.detected_tools - else False - ): - # Handle tool selection - tool_name = args.replace("tool:", "") if args.startswith("tool:") else args - - # Find the tool - tool = None - for t in self.detected_tools: - if t.name == tool_name or t.display_name.lower() == tool_name.lower(): - tool = t - break - - if tool: - self.current_model = f"tool:{tool.name}" - self.current_tool = tool - self.config["default_model"] = self.current_model - self.save_config() - self.console.print(f"[green]โœ… Switched to {tool.display_name}[/green]") - else: - self.console.print(f"[red]Tool not found: {tool_name}[/red]") - self.console.print("[dim]Use /tools to see available tools[/dim]") - - # Regular model - accept any model name (all gateway models are free!) - else: - self.current_model = args - self.current_tool = None - self.config["default_model"] = args - self.save_config() - - # Get pretty name if it's a known model - model_name = self.MODELS.get(args, args) - self.console.print(f"[green]โœ… Switched to {model_name}[/green]") - - # Show hint if it's a gateway model (not in our predefined list) - if args not in self.MODELS and not args.startswith("local:"): - self.console.print( - "[dim]Using gateway model - all models free! Use /models to see full list.[/dim]" - ) - - async def list_tools(self, args: str = ""): - """List available AI tools.""" - if self.tool_detector: - self.tool_detector.show_available_tools() - else: - self.console.print("[yellow]Tool detection not available[/yellow]") - - async def list_models(self, args: str = ""): - """List available models from gateway.hanzo.ai.""" - # Show tools first if available - if self.detected_tools: - self.console.print( - "[bold cyan]AI Coding Assistants (Detected):[/bold cyan]" - ) - for i, tool in enumerate(self.detected_tools, 1): - marker = "โ†’" if self.current_model == f"tool:{tool.name}" else " " - self.console.print( - f" {marker} {i}. {tool.display_name} ({tool.provider})" - ) - self.console.print() - - # Try to fetch live models from gateway - models_list = [] - try: - from hanzo.orchestrator_config import get_default_router_endpoint - - endpoint = get_default_router_endpoint() - - response = httpx.get(f"{endpoint}/v1/models", timeout=5.0) - if response.status_code == 200: - data = response.json() - all_models = data.get("data", []) - - # Filter out embedding models - only show chat/completion LLMs - embedding_keywords = ["embedding", "voyage", "embed", "text-embedding"] - models_list = [ - model - for model in all_models - if not any( - keyword in model.get("id", "").lower() - for keyword in embedding_keywords - ) - ] - except Exception: - pass - - # Fallback to static models if gateway fetch fails - if not models_list: - models_list = [ - {"id": model_id, "name": model_name} - for model_id, model_name in self.MODELS.items() - ] - - # Create table - table = Table(title="Gateway LLMs (Chat Models)", box=box.ROUNDED) - table.add_column("#", style="dim") - table.add_column("Model ID", style="cyan") - table.add_column("Tier", style="white") - table.add_column("Provider", style="yellow") - - start_idx = len(self.detected_tools) + 1 if self.detected_tools else 1 - - for i, model in enumerate(models_list, start_idx): - model_id = model.get("id", "unknown") - - # All gateway models are FREE! ๐ŸŽ‰ - tier = "[green]Free[/green]" - - # Get provider - if "gpt" in model_id or "openai" in model_id: - provider = "OpenAI" - elif "claude" in model_id or "anthropic" in model_id: - provider = "Anthropic" - elif "gemini" in model_id or "google" in model_id: - provider = "Google" - elif "llama" in model_id: - provider = "Meta" - elif "mistral" in model_id or "mixtral" in model_id: - provider = "Mistral" - elif "qwen" in model_id or "alibaba" in model_id: - provider = "Alibaba" - elif "deepseek" in model_id: - provider = "DeepSeek" - elif "local:" in model_id: - provider = "Local" - else: - provider = "Other" - - # Highlight current model - if model_id == self.current_model: - table.add_row( - str(i), f"[bold green]โ†’ {model_id}[/bold green]", tier, provider - ) - else: - table.add_row(str(i), model_id, tier, provider) - - self.console.print(table) - self.console.print( - "\n[dim cyan]Free tier:[/dim cyan] [green]Most models available without login![/green]" - ) - self.console.print( - "[dim cyan]Premium models:[/dim cyan] [yellow]gpt-4, claude-3-opus, o1-preview[/yellow] - [cyan]hanzo auth login[/cyan]" - ) - self.console.print( - "\n[dim]Use /model or /model to switch[/dim]" - ) - - async def login(self, args: str = ""): - """Login to Hanzo.""" - self.console.print("[cyan]Hanzo Authentication[/cyan]\n") - - # Check if already logged in - if self.is_authenticated(): - self.console.print("[yellow]Already authenticated[/yellow]") - if self.auth.get("email"): - self.console.print(f"Logged in as: {self.auth['email']}") - return - - # Get credentials - try: - # Email - email = await self.session.prompt_async("Email: ") - - # Password (hidden) - from prompt_toolkit import prompt - - password = prompt("Password: ", is_password=True) - - # Store credentials locally (API validates on first use) - self.console.print("\n[dim]Saving credentials...[/dim]") - - # Save auth - self.auth["email"] = email - self.auth["logged_in"] = True - self.auth["last_login"] = datetime.now().isoformat() - self.save_auth() - - self.console.print("[green]โœ… Successfully logged in![/green]") - - except (KeyboardInterrupt, EOFError): - self.console.print("\n[yellow]Login cancelled[/yellow]") - - async def logout(self, args: str = ""): - """Logout from Hanzo.""" - if not self.is_authenticated(): - self.console.print("[yellow]Not logged in[/yellow]") - return - - # Clear auth - self.auth = {} - self.save_auth() - - # Clear environment variable if set - if "HANZO_API_KEY" in os.environ: - del os.environ["HANZO_API_KEY"] - - self.console.print("[green]โœ… Successfully logged out[/green]") - - async def show_config(self, args: str = ""): - """Show current configuration.""" - config_text = json.dumps(self.config, indent=2) - self.console.print(Panel(config_text, title="Configuration", box=box.ROUNDED)) - - async def show_help(self, args: str = ""): - """Show enhanced help.""" - help_text = """ -# Hanzo Enhanced REPL - -## Slash Commands: -- `/todo [cmd]` - Manage todos (see `/todo help`) -- `/model [name]` - Change AI model (or `/m`) -- `/models` - List available models -- `/tools` - List available AI tools -- `/quick` - Quick model selector (arrow keys) -- `/tasks` - Show background tasks -- `/kill [id]` - Kill background task -- `/status` - Show system status (or `/s`) -- `/login` - Login to Hanzo Cloud -- `/logout` - Logout from Hanzo -- `/config` - Show configuration -- `/help` - Show this help (or `/h`) -- `/clear` - Clear screen (or `/c`) -- `/quit` - Exit REPL (or `/q`) - -## Quick Model Selection: -- Press โ†“ arrow key for quick model selector -- Use โ†‘/โ†“ to navigate, Enter to select -- Esc to cancel - -## Model Selection: -- Use `/model gpt-4` to switch to GPT-4 -- Use `/model 3` to select model by number -- Current model shown in prompt: `hanzo [gpt] >` - -## Authentication: -- ๐Ÿ”“ = Authenticated (logged in or API key set) -- ๐Ÿ”’ = Not authenticated -- Use `/login` to authenticate with Hanzo Cloud - -## Tips: -- Type any message to chat with current model -- Use Tab for command completion -- Use Up/Down arrows for history -""" - self.console.print(Markdown(help_text)) - - async def clear_screen(self, args: str = ""): - """Clear the screen.""" - self.console.clear() - - async def exit_repl(self, args: str = ""): - """Exit the REPL.""" - self.running = False - self.console.print("[yellow]Goodbye! ๐Ÿ‘‹[/yellow]") - - async def execute_command(self, cmd: str, args: str): - """Execute a CLI command.""" - # Import here to avoid circular imports - import subprocess - - full_cmd = f"hanzo {cmd} {args}".strip() - self.console.print(f"[dim]Executing: {full_cmd}[/dim]") - - try: - result = subprocess.run( - full_cmd, shell=True, capture_output=True, text=True - ) - - if result.stdout: - self.console.print(result.stdout) - if result.stderr: - self.console.print(f"[red]{result.stderr}[/red]") - - except Exception as e: - self.console.print(f"[red]Error executing command: {e}[/red]") - - async def chat_with_ai(self, message: str): - """Chat with AI using current model or tool.""" - # Check if using a tool - if self.current_model.startswith("tool:") and self.current_tool: - # Skip if this tool has already failed this session - if self.current_tool.name in self.failed_tools: - # Automatically use the first working tool - for tool in self.detected_tools: - if tool.name not in self.failed_tools: - self.current_tool = tool - self.current_model = f"tool:{tool.name}" - self.console.print( - f"[yellow]Switched to {tool.display_name}[/yellow]" - ) - break - - # Try the current tool - if self.current_tool.name not in self.failed_tools: - self.console.print( - f"[dim]Using {self.current_tool.display_name}...[/dim]" - ) - success, output = self.tool_detector.execute_with_tool( - self.current_tool, message - ) - - if success: - self.console.print(output) - return - else: - # Mark this tool as failed for the session - self.failed_tools.add(self.current_tool.name) - self.console.print(f"[red]Error: {output}[/red]") - - # Try to find next available tool - found_working = False - if self.tool_detector and self.tool_detector.detected_tools: - for fallback_tool in self.tool_detector.detected_tools: - if fallback_tool.name not in self.failed_tools: - self.console.print( - f"[yellow]Trying {fallback_tool.display_name}...[/yellow]" - ) - success, output = self.tool_detector.execute_with_tool( - fallback_tool, message - ) - if success: - self.console.print(output) - # Automatically switch to this working tool - self.current_tool = fallback_tool - self.current_model = f"tool:{fallback_tool.name}" - self.config["default_model"] = self.current_model - self.save_config() - self.console.print( - f"\n[green]Switched to {fallback_tool.display_name} (now default)[/green]" - ) - found_working = True - return - else: - # Mark as failed - self.failed_tools.add(fallback_tool.name) - self.console.print( - f"[red]{fallback_tool.display_name} also failed[/red]" - ) - - if not found_working: - # Final fallback to cloud model - self.console.print(f"[yellow]Falling back to cloud model...[/yellow]") - await self.execute_command( - "ask", f"--cloud --model gpt-3.5-turbo {message}" - ) - else: - # Use regular model through hanzo ask - await self.execute_command( - "ask", f"--cloud --model {self.current_model} {message}" - ) - - async def quick_model_select(self, args: str = ""): - """Quick model selector with arrow keys.""" - if not QuickModelSelector: - self.console.print("[yellow]Quick selector not available[/yellow]") - return - - # Prepare tools and models - tools = ( - [(f"tool:{t.name}", t.display_name) for t in self.detected_tools] - if self.detected_tools - else [] - ) - models = list(self.MODELS.items()) - - selector = QuickModelSelector(models, tools, self.current_model) - selected = await selector.run() - - if selected: - # Change to selected model - await self.change_model(selected) - - async def show_tasks(self, args: str = ""): - """Show background tasks.""" - if self.task_manager: - self.task_manager.list_tasks() - else: - self.console.print("[yellow]Task manager not available[/yellow]") - - async def kill_task(self, args: str = ""): - """Kill a background task.""" - if not self.task_manager: - self.console.print("[yellow]Task manager not available[/yellow]") - return - - if args: - if args.lower() == "all": - self.task_manager.kill_all() - else: - self.task_manager.kill_task(args) - else: - # Show tasks and prompt for selection - self.task_manager.list_tasks() - self.console.print( - "\n[cyan]Enter task ID to kill (or 'all' for all tasks):[/cyan]" - ) - try: - task_id = await self.session.prompt_async("> ") - if task_id: - if task_id.lower() == "all": - self.task_manager.kill_all() - else: - self.task_manager.kill_task(task_id) - except (KeyboardInterrupt, EOFError): - pass - - async def manage_todos(self, args: str = ""): - """Manage todos.""" - if not self.todo_manager: - self.console.print("[yellow]Todo manager not available[/yellow]") - return - - # Parse command - parts = args.strip().split(maxsplit=1) - - if not parts: - # Show todos - self.todo_manager.display_todos() - return - - subcommand = parts[0].lower() - rest = parts[1] if len(parts) > 1 else "" - - # Handle subcommands - if subcommand in ["add", "a", "+"]: - # Add todo - if rest: - # Quick add - try: - todo = self.todo_manager.quick_add(rest) - self.console.print( - f"[green]โœ… Added todo: {todo.title} (ID: {todo.id})[/green]" - ) - except ValueError as e: - self.console.print(f"[red]Error: {e}[/red]") - else: - # Interactive add - await self.add_todo_interactive() - - elif subcommand in ["list", "ls", "l"]: - # List todos with optional filter - filter_parts = rest.split() - status = None - priority = None - tag = None - - for i in range(0, len(filter_parts), 2): - if i + 1 < len(filter_parts): - key = filter_parts[i] - value = filter_parts[i + 1] - - if key in ["status", "s"]: - status = value - elif key in ["priority", "p"]: - priority = value - elif key in ["tag", "t"]: - tag = value - - todos = self.todo_manager.list_todos( - status=status, priority=priority, tag=tag - ) - title = "Filtered Todos" if (status or priority or tag) else "All Todos" - self.todo_manager.display_todos(todos, title) - - elif subcommand in ["done", "d", "complete", "finish"]: - # Mark as done - if rest: - todo = self.todo_manager.update_todo(rest, status="done") - if todo: - self.console.print( - f"[green]โœ… Marked as done: {todo.title}[/green]" - ) - else: - self.console.print(f"[red]Todo not found: {rest}[/red]") - else: - self.console.print("[yellow]Usage: /todo done [/yellow]") - - elif subcommand in ["start", "begin", "progress"]: - # Mark as in progress - if rest: - todo = self.todo_manager.update_todo(rest, status="in_progress") - if todo: - self.console.print(f"[cyan]๐Ÿ”„ Started: {todo.title}[/cyan]") - else: - self.console.print(f"[red]Todo not found: {rest}[/red]") - else: - self.console.print("[yellow]Usage: /todo start [/yellow]") - - elif subcommand in ["cancel", "x"]: - # Cancel todo - if rest: - todo = self.todo_manager.update_todo(rest, status="cancelled") - if todo: - self.console.print(f"[red]โŒ Cancelled: {todo.title}[/red]") - else: - self.console.print(f"[red]Todo not found: {rest}[/red]") - else: - self.console.print("[yellow]Usage: /todo cancel [/yellow]") - - elif subcommand in ["delete", "del", "rm", "remove"]: - # Delete todo - if rest: - if self.todo_manager.delete_todo(rest): - self.console.print(f"[green]โœ… Deleted todo: {rest}[/green]") - else: - self.console.print(f"[red]Todo not found: {rest}[/red]") - else: - self.console.print("[yellow]Usage: /todo delete [/yellow]") - - elif subcommand in ["view", "show", "detail"]: - # View todo detail - if rest: - todo = self.todo_manager.get_todo(rest) - if todo: - self.todo_manager.display_todo_detail(todo) - else: - self.console.print(f"[red]Todo not found: {rest}[/red]") - else: - self.console.print("[yellow]Usage: /todo view [/yellow]") - - elif subcommand in ["stats", "statistics"]: - # Show statistics - self.todo_manager.display_statistics() - - elif subcommand in ["clear", "reset"]: - # Clear all todos (with confirmation) - try: - confirm = await self.session.prompt_async( - "Are you sure you want to delete ALL todos? (yes/no): " - ) - if confirm.lower() in ["yes", "y"]: - self.todo_manager.todos = [] - self.todo_manager.save_todos() - self.console.print("[green]โœ… All todos cleared[/green]") - else: - self.console.print("[yellow]Cancelled[/yellow]") - except (KeyboardInterrupt, EOFError): - self.console.print("[yellow]Cancelled[/yellow]") - - elif subcommand in ["help", "h", "?"]: - # Show todo help - self.show_todo_help() - - else: - # Unknown subcommand, treat as quick add - try: - todo = self.todo_manager.quick_add(args) - self.console.print( - f"[green]โœ… Added todo: {todo.title} (ID: {todo.id})[/green]" - ) - except ValueError: - self.console.print( - f"[yellow]Unknown todo command: {subcommand}[/yellow]" - ) - self.console.print("[dim]Use /todo help for available commands[/dim]") - - async def add_todo_interactive(self): - """Add todo interactively.""" - try: - # Get title - title = await self.session.prompt_async("Title: ") - if not title: - self.console.print("[yellow]Cancelled[/yellow]") - return - - # Get description - description = await self.session.prompt_async("Description (optional): ") - - # Get priority - priority = await self.session.prompt_async( - "Priority (low/medium/high/urgent) [medium]: " - ) - if not priority: - priority = "medium" - - # Get tags - tags_input = await self.session.prompt_async( - "Tags (comma-separated, optional): " - ) - tags = ( - [t.strip() for t in tags_input.split(",") if t.strip()] - if tags_input - else [] - ) - - # Get due date - due_date = await self.session.prompt_async("Due date (optional): ") - - # Add todo - todo = self.todo_manager.add_todo( - title=title, - description=description, - priority=priority, - tags=tags, - due_date=due_date if due_date else None, - ) - - self.console.print( - f"[green]โœ… Added todo: {todo.title} (ID: {todo.id})[/green]" - ) - - except (KeyboardInterrupt, EOFError): - self.console.print("[yellow]Cancelled[/yellow]") - - def show_todo_help(self): - """Show todo help.""" - help_text = """ -[bold cyan]Todo Management[/bold cyan] - -[bold]Quick Add:[/bold] - /todo Buy milk #shopping !high @tomorrow - Format: title #tag1 #tag2 !priority @due_date - -[bold]Commands:[/bold] - /todo - List all todos - /todo add - Quick add todo - /todo list [filters] - List with filters - /todo done - Mark as done - /todo start - Mark as in progress - /todo cancel - Cancel todo - /todo delete - Delete todo - /todo view - View todo details - /todo stats - Show statistics - /todo clear - Clear all todos - /todo help - Show this help - -[bold]List Filters:[/bold] - /todo list status todo - /todo list priority high - /todo list tag work - -[bold]Shortcuts:[/bold] - /todo a = add - /todo ls = list - /todo d = done - /todo rm = delete -""" - self.console.print(help_text) diff --git a/pkg/hanzo/src/hanzo/interactive/model_selector.py b/pkg/hanzo/src/hanzo/interactive/model_selector.py deleted file mode 100644 index c9fb211b4..000000000 --- a/pkg/hanzo/src/hanzo/interactive/model_selector.py +++ /dev/null @@ -1,159 +0,0 @@ -"""Quick model selector with arrow key navigation.""" - -from typing import List, Tuple, Optional - -from rich.console import Console -from prompt_toolkit import Application -from prompt_toolkit.widgets import Label -from prompt_toolkit.key_binding import KeyBindings -from prompt_toolkit.layout.layout import Layout -from prompt_toolkit.layout.controls import FormattedTextControl -from prompt_toolkit.layout.containers import HSplit, Window - - -class QuickModelSelector: - """Quick model selector with arrow navigation.""" - - def __init__( - self, models: List[Tuple[str, str]], tools: List[Tuple[str, str]], current: str - ): - self.models = models - self.tools = tools - self.current = current - self.all_items = tools + models # Tools first, then models - self.selected_index = 0 - - # Find current selection - for i, (item_id, _) in enumerate(self.all_items): - if item_id == current: - self.selected_index = i - break - - def get_display_lines(self) -> List[str]: - """Get display lines for the selector.""" - lines = [] - - if self.tools: - lines.append("AI Coding Assistants:") - for i, (tool_id, tool_name) in enumerate(self.tools): - marker = "โ†’ " if i == self.selected_index else " " - lines.append(f"{marker}{tool_name}") - - if self.models: - if self.tools: - lines.append("") # Empty line - lines.append("Language Models:") - - tool_count = len(self.tools) - for i, (model_id, model_name) in enumerate(self.models): - actual_idx = tool_count + i - marker = "โ†’ " if actual_idx == self.selected_index else " " - lines.append(f"{marker}{model_name}") - - return lines - - def move_up(self): - """Move selection up.""" - if self.selected_index > 0: - self.selected_index -= 1 - - def move_down(self): - """Move selection down.""" - if self.selected_index < len(self.all_items) - 1: - self.selected_index += 1 - - def get_selected(self) -> Tuple[str, str]: - """Get the selected item.""" - if 0 <= self.selected_index < len(self.all_items): - return self.all_items[self.selected_index] - return None, None - - async def run(self) -> Optional[str]: - """Run the selector and return selected model/tool ID.""" - kb = KeyBindings() - - @kb.add("up") - def _(event): - self.move_up() - event.app.invalidate() - - @kb.add("down") - def _(event): - self.move_down() - event.app.invalidate() - - @kb.add("enter") - def _(event): - event.app.exit(result=self.get_selected()[0]) - - @kb.add("c-c") - @kb.add("escape") - def _(event): - event.app.exit(result=None) - - def get_text(): - lines = self.get_display_lines() - lines.append("") - lines.append("โ†‘/โ†“: Navigate Enter: Select Esc: Cancel") - return "\n".join(lines) - - layout = Layout(Window(FormattedTextControl(get_text), wrap_lines=False)) - - app = Application( - layout=layout, key_bindings=kb, full_screen=False, mouse_support=True - ) - - return await app.run_async() - - -class BackgroundTaskManager: - """Manage background tasks.""" - - def __init__(self, console: Optional[Console] = None): - self.console = console or Console() - self.tasks = {} # task_id -> process - self.next_id = 1 - - def add_task(self, name: str, process): - """Add a background task.""" - task_id = f"task_{self.next_id}" - self.next_id += 1 - self.tasks[task_id] = {"name": name, "process": process, "started": True} - return task_id - - def list_tasks(self): - """List all background tasks.""" - if not self.tasks: - self.console.print("[dim]No background tasks running[/dim]") - return - - self.console.print("[bold]Background Tasks:[/bold]") - for task_id, task in self.tasks.items(): - status = "๐ŸŸข Running" if task["process"].poll() is None else "๐Ÿ”ด Stopped" - self.console.print(f" {task_id}: {task['name']} - {status}") - - def kill_task(self, task_id: str): - """Kill a background task.""" - if task_id in self.tasks: - task = self.tasks[task_id] - if task["process"].poll() is None: - task["process"].terminate() - self.console.print( - f"[yellow]Terminated {task_id}: {task['name']}[/yellow]" - ) - else: - self.console.print(f"[dim]Task {task_id} already stopped[/dim]") - del self.tasks[task_id] - else: - self.console.print(f"[red]Task {task_id} not found[/red]") - - def kill_all(self): - """Kill all background tasks.""" - if not self.tasks: - self.console.print("[dim]No tasks to kill[/dim]") - return - - for task_id in list(self.tasks.keys()): - self.kill_task(task_id) - - self.console.print("[green]All tasks terminated[/green]") diff --git a/pkg/hanzo/src/hanzo/interactive/repl.py b/pkg/hanzo/src/hanzo/interactive/repl.py deleted file mode 100644 index 89f70fe83..000000000 --- a/pkg/hanzo/src/hanzo/interactive/repl.py +++ /dev/null @@ -1,236 +0,0 @@ -"""Interactive REPL for Hanzo CLI.""" - -from typing import Optional -from pathlib import Path - -from rich.console import Console -from rich.markdown import Markdown -from prompt_toolkit import PromptSession -from prompt_toolkit.history import FileHistory -from prompt_toolkit.completion import WordCompleter -from prompt_toolkit.auto_suggest import AutoSuggestFromHistory - - -class HanzoREPL: - """Interactive REPL for Hanzo CLI.""" - - def __init__(self, console: Optional[Console] = None): - self.console = console or Console() - self.session = PromptSession( - history=FileHistory(".hanzo_dev_history"), - auto_suggest=AutoSuggestFromHistory(), - ) - self.commands = { - "help": self.show_help, - "exit": self.exit_repl, - "quit": self.exit_repl, - "clear": self.clear_screen, - "status": self.show_status, - } - self.running = False - - async def run(self): - """Run the REPL.""" - self.running = True - # Don't print welcome message here since it's already printed in cli.py - - # Set up command completer - cli_commands = [ - "chat", - "ask", - "agent", - "node", - "mcp", - "network", - "auth", - "config", - "tools", - "miner", - "serve", - "net", - "dev", - "router", - ] - completer = WordCompleter( - list(self.commands.keys()) + cli_commands, - ignore_case=True, - ) - - while self.running: - try: - # Get input with simple prompt - command = await self.session.prompt_async("> ", completer=completer) - - if not command.strip(): - continue - - # Parse command - parts = command.strip().split(maxsplit=1) - cmd = parts[0].lower() - args = parts[1] if len(parts) > 1 else "" - - # Execute command - if cmd in self.commands: - await self.commands[cmd](args) - elif cmd in [ - "chat", - "ask", - "agent", - "node", - "mcp", - "network", - "auth", - "config", - "tools", - "miner", - "serve", - "net", - "dev", - "router", - ]: - # Execute known CLI commands - await self.execute_command(cmd, args) - else: - # Treat as chat message if not a known command - await self.chat_with_ai(command) - - except KeyboardInterrupt: - continue - except EOFError: - break - except Exception as e: - self.console.print(f"[red]Error: {e}[/red]") - - async def show_help(self, args: str = ""): - """Show help message.""" - help_text = """ -# Hanzo Interactive Mode - -## Built-in Commands: -- `help` - Show this help message -- `exit/quit` - Exit interactive mode -- `clear` - Clear the screen -- `status` - Show system status - -## CLI Commands: -All Hanzo CLI commands are available: -- `chat ` - Chat with AI -- `agent start` - Start an agent -- `node status` - Check node status -- `mcp tools` - List MCP tools -- `network agents` - List network agents - -## Examples: -``` -hanzo> chat How do I create a Python web server? -hanzo> agent list -hanzo> node start --models llama-3.2-3b -hanzo> mcp run read_file --arg path=README.md -``` - -## Tips: -- Use Tab for command completion -- Use โ†‘/โ†“ for command history -- Use Ctrl+R for reverse search -""" - self.console.print(Markdown(help_text)) - - def exit_repl(self, args: str = ""): - """Exit the REPL.""" - self.running = False - self.console.print("\n[yellow]Goodbye![/yellow]") - - def clear_screen(self, args: str = ""): - """Clear the screen.""" - self.console.clear() - - async def show_status(self, args: str = ""): - """Show system status.""" - status = { - "node": await self.check_node_status(), - "agents": await self.count_agents(), - "auth": self.check_auth_status(), - } - - self.console.print("[cyan]System Status:[/cyan]") - self.console.print(f" Node: {status['node']}") - self.console.print(f" Agents: {status['agents']}") - self.console.print(f" Auth: {status['auth']}") - - async def execute_command(self, cmd: str, args: str): - """Execute a CLI command.""" - import os - import sys - import shutil - import subprocess - - # Find hanzo executable - hanzo_cmd = shutil.which("hanzo") - if not hanzo_cmd: - # Try using Python module directly - hanzo_cmd = sys.executable - argv = [hanzo_cmd, "-m", "hanzo", cmd] - else: - argv = [hanzo_cmd, cmd] - - if args: - import shlex - - argv.extend(shlex.split(args)) - - # Execute as subprocess to avoid context issues - try: - result = subprocess.run( - argv, - capture_output=True, - text=True, - timeout=30, - env=os.environ.copy(), # Pass environment variables - ) - - if result.stdout: - self.console.print(result.stdout.rstrip()) - if result.stderr and result.returncode != 0: - self.console.print(f"[red]{result.stderr.rstrip()}[/red]") - - except subprocess.TimeoutExpired: - self.console.print("[red]Command timed out[/red]") - except FileNotFoundError: - self.console.print( - "[red]Command not found. Make sure 'hanzo' is installed.[/red]" - ) - except Exception as e: - self.console.print(f"[red]Command error: {e}[/red]") - - async def check_node_status(self) -> str: - """Check if node is running.""" - try: - import httpx - - async with httpx.AsyncClient() as client: - response = await client.get("http://localhost:8000/health", timeout=1.0) - return "running" if response.status_code == 200 else "not responding" - except Exception: - return "not running" - - async def count_agents(self) -> int: - """Count running agents.""" - # This would check actual agent status - return 0 - - def check_auth_status(self) -> str: - """Check authentication status.""" - import os - - if os.environ.get("HANZO_API_KEY"): - return "authenticated (API key)" - elif (Path.home() / ".hanzo" / "auth.json").exists(): - return "authenticated (saved)" - else: - return "not authenticated" - - async def chat_with_ai(self, message: str): - """Chat with AI when user types natural language.""" - # For natural language input, try to use it as a chat message - # Default to cloud mode to avoid needing local server - await self.execute_command("ask", f"--cloud {message}") diff --git a/pkg/hanzo/src/hanzo/interactive/todo_manager.py b/pkg/hanzo/src/hanzo/interactive/todo_manager.py deleted file mode 100644 index e5e015765..000000000 --- a/pkg/hanzo/src/hanzo/interactive/todo_manager.py +++ /dev/null @@ -1,456 +0,0 @@ -"""Native todo management for Hanzo REPL.""" - -import json -import uuid -from enum import Enum -from typing import Any, Dict, List, Optional -from pathlib import Path -from datetime import datetime - -from rich import box -from rich.text import Text -from rich.panel import Panel -from rich.table import Table -from rich.prompt import Prompt, Confirm -from rich.console import Console - - -class TodoPriority(Enum): - """Todo priority levels.""" - - LOW = "low" - MEDIUM = "medium" - HIGH = "high" - URGENT = "urgent" - - -class TodoStatus(Enum): - """Todo status.""" - - TODO = "todo" - IN_PROGRESS = "in_progress" - DONE = "done" - CANCELLED = "cancelled" - - -class Todo: - """Single todo item.""" - - def __init__( - self, - title: str, - description: str = "", - priority: TodoPriority = TodoPriority.MEDIUM, - status: TodoStatus = TodoStatus.TODO, - tags: List[str] = None, - due_date: Optional[str] = None, - id: Optional[str] = None, - created_at: Optional[str] = None, - updated_at: Optional[str] = None, - completed_at: Optional[str] = None, - ): - self.id = id or str(uuid.uuid4())[:8] - self.title = title - self.description = description - self.priority = priority - self.status = status - self.tags = tags or [] - self.due_date = due_date - self.created_at = created_at or datetime.now().isoformat() - self.updated_at = updated_at or datetime.now().isoformat() - self.completed_at = completed_at - - def to_dict(self) -> Dict[str, Any]: - """Convert to dictionary.""" - return { - "id": self.id, - "title": self.title, - "description": self.description, - "priority": self.priority.value, - "status": self.status.value, - "tags": self.tags, - "due_date": self.due_date, - "created_at": self.created_at, - "updated_at": self.updated_at, - "completed_at": self.completed_at, - } - - @classmethod - def from_dict(cls, data: Dict[str, Any]) -> "Todo": - """Create from dictionary.""" - return cls( - id=data.get("id"), - title=data["title"], - description=data.get("description", ""), - priority=TodoPriority(data.get("priority", "medium")), - status=TodoStatus(data.get("status", "todo")), - tags=data.get("tags", []), - due_date=data.get("due_date"), - created_at=data.get("created_at"), - updated_at=data.get("updated_at"), - completed_at=data.get("completed_at"), - ) - - -class TodoManager: - """Manage todos with persistent storage.""" - - def __init__(self, console: Optional[Console] = None): - self.console = console or Console() - self.config_dir = Path.home() / ".hanzo" - self.todos_file = self.config_dir / "todos.json" - self.todos: List[Todo] = [] - self.load_todos() - - def load_todos(self): - """Load todos from file.""" - if self.todos_file.exists(): - try: - data = json.loads(self.todos_file.read_text()) - self.todos = [Todo.from_dict(t) for t in data.get("todos", [])] - except Exception as e: - self.console.print(f"[red]Error loading todos: {e}[/red]") - self.todos = [] - else: - self.todos = [] - - def save_todos(self): - """Save todos to file.""" - self.config_dir.mkdir(exist_ok=True) - data = { - "todos": [t.to_dict() for t in self.todos], - "last_updated": datetime.now().isoformat(), - } - self.todos_file.write_text(json.dumps(data, indent=2)) - - def add_todo( - self, - title: str, - description: str = "", - priority: str = "medium", - tags: List[str] = None, - due_date: Optional[str] = None, - ) -> Todo: - """Add a new todo.""" - try: - priority_enum = TodoPriority(priority.lower()) - except ValueError: - priority_enum = TodoPriority.MEDIUM - - todo = Todo( - title=title, - description=description, - priority=priority_enum, - tags=tags or [], - due_date=due_date, - ) - - self.todos.append(todo) - self.save_todos() - - return todo - - def update_todo(self, todo_id: str, **kwargs) -> Optional[Todo]: - """Update a todo.""" - todo = self.get_todo(todo_id) - if not todo: - return None - - # Update fields - if "title" in kwargs: - todo.title = kwargs["title"] - if "description" in kwargs: - todo.description = kwargs["description"] - if "priority" in kwargs: - try: - todo.priority = TodoPriority(kwargs["priority"].lower()) - except ValueError: - pass - if "status" in kwargs: - try: - new_status = TodoStatus(kwargs["status"].lower()) - todo.status = new_status - - # Update completed timestamp - if new_status == TodoStatus.DONE: - todo.completed_at = datetime.now().isoformat() - elif todo.status == TodoStatus.DONE and new_status != TodoStatus.DONE: - todo.completed_at = None - except ValueError: - pass - if "tags" in kwargs: - todo.tags = kwargs["tags"] - if "due_date" in kwargs: - todo.due_date = kwargs["due_date"] - - todo.updated_at = datetime.now().isoformat() - self.save_todos() - - return todo - - def delete_todo(self, todo_id: str) -> bool: - """Delete a todo.""" - todo = self.get_todo(todo_id) - if todo: - self.todos.remove(todo) - self.save_todos() - return True - return False - - def get_todo(self, todo_id: str) -> Optional[Todo]: - """Get a todo by ID.""" - for todo in self.todos: - if todo.id == todo_id: - return todo - return None - - def list_todos( - self, - status: Optional[str] = None, - priority: Optional[str] = None, - tag: Optional[str] = None, - ) -> List[Todo]: - """List todos with optional filters.""" - filtered = self.todos - - # Filter by status - if status: - try: - status_enum = TodoStatus(status.lower()) - filtered = [t for t in filtered if t.status == status_enum] - except ValueError: - pass - - # Filter by priority - if priority: - try: - priority_enum = TodoPriority(priority.lower()) - filtered = [t for t in filtered if t.priority == priority_enum] - except ValueError: - pass - - # Filter by tag - if tag: - filtered = [t for t in filtered if tag in t.tags] - - # Sort by priority and status - priority_order = { - TodoPriority.URGENT: 0, - TodoPriority.HIGH: 1, - TodoPriority.MEDIUM: 2, - TodoPriority.LOW: 3, - } - - status_order = { - TodoStatus.IN_PROGRESS: 0, - TodoStatus.TODO: 1, - TodoStatus.DONE: 2, - TodoStatus.CANCELLED: 3, - } - - filtered.sort( - key=lambda t: ( - status_order.get(t.status, 999), - priority_order.get(t.priority, 999), - t.created_at, - ) - ) - - return filtered - - def display_todos(self, todos: Optional[List[Todo]] = None, title: str = "Todos"): - """Display todos in a nice table.""" - if todos is None: - todos = self.list_todos() - - if not todos: - self.console.print("[yellow]No todos found[/yellow]") - return - - # Create table - table = Table(title=title, box=box.ROUNDED) - table.add_column("ID", style="cyan", width=8) - table.add_column("Status", width=12) - table.add_column("Priority", width=8) - table.add_column("Title", style="white") - table.add_column("Tags", style="dim") - table.add_column("Due", style="yellow") - - for todo in todos: - # Status emoji - status_display = { - TodoStatus.TODO: "โญ• Todo", - TodoStatus.IN_PROGRESS: "๐Ÿ”„ In Progress", - TodoStatus.DONE: "โœ… Done", - TodoStatus.CANCELLED: "โŒ Cancelled", - }.get(todo.status, todo.status.value) - - # Priority color - priority_color = { - TodoPriority.URGENT: "red bold", - TodoPriority.HIGH: "red", - TodoPriority.MEDIUM: "yellow", - TodoPriority.LOW: "green", - }.get(todo.priority, "white") - - priority_display = ( - f"[{priority_color}]{todo.priority.value.upper()}[/{priority_color}]" - ) - - # Tags - tags_display = ", ".join(todo.tags) if todo.tags else "-" - - # Due date - due_display = todo.due_date if todo.due_date else "-" - - table.add_row( - todo.id, - status_display, - priority_display, - todo.title, - tags_display, - due_display, - ) - - self.console.print(table) - - # Summary - total = len(todos) - done = len([t for t in todos if t.status == TodoStatus.DONE]) - in_progress = len([t for t in todos if t.status == TodoStatus.IN_PROGRESS]) - todo_count = len([t for t in todos if t.status == TodoStatus.TODO]) - - summary = f"Total: {total} | Todo: {todo_count} | In Progress: {in_progress} | Done: {done}" - self.console.print(f"\n[dim]{summary}[/dim]") - - def display_todo_detail(self, todo: Todo): - """Display detailed view of a todo.""" - # Status color - status_color = { - TodoStatus.TODO: "yellow", - TodoStatus.IN_PROGRESS: "cyan", - TodoStatus.DONE: "green", - TodoStatus.CANCELLED: "red", - }.get(todo.status, "white") - - # Priority color - priority_color = { - TodoPriority.URGENT: "red bold", - TodoPriority.HIGH: "red", - TodoPriority.MEDIUM: "yellow", - TodoPriority.LOW: "green", - }.get(todo.priority, "white") - - # Build content - content = f""" -[bold]{todo.title}[/bold] - -[dim]ID:[/dim] {todo.id} -[dim]Status:[/dim] [{status_color}]{todo.status.value.replace("_", " ").title()}[/{status_color}] -[dim]Priority:[/dim] [{priority_color}]{todo.priority.value.upper()}[/{priority_color}] -[dim]Tags:[/dim] {", ".join(todo.tags) if todo.tags else "None"} -[dim]Due Date:[/dim] {todo.due_date if todo.due_date else "Not set"} - -[dim]Description:[/dim] -{todo.description if todo.description else "No description"} - -[dim]Created:[/dim] {todo.created_at} -[dim]Updated:[/dim] {todo.updated_at} -[dim]Completed:[/dim] {todo.completed_at if todo.completed_at else "Not completed"} -""" - - panel = Panel(content.strip(), title=f"Todo: {todo.title}", box=box.ROUNDED) - self.console.print(panel) - - def quick_add(self, text: str) -> Todo: - """Quick add todo from text. - - Format: title #tag1 #tag2 !priority @due_date - """ - import re - - # Extract tags (words starting with #) - tags = re.findall(r"#(\w+)", text) - text = re.sub(r"#\w+", "", text) - - # Extract priority (word after !) - priority_match = re.search(r"!(\w+)", text) - priority = priority_match.group(1) if priority_match else "medium" - text = re.sub(r"!\w+", "", text) - - # Extract due date (text after @) - due_match = re.search(r"@([^\s]+)", text) - due_date = due_match.group(1) if due_match else None - text = re.sub(r"@[^\s]+", "", text) - - # Clean up title - title = text.strip() - - if not title: - raise ValueError("Todo title cannot be empty") - - return self.add_todo( - title=title, priority=priority, tags=tags, due_date=due_date - ) - - def get_statistics(self) -> Dict[str, Any]: - """Get todo statistics.""" - total = len(self.todos) - - # Status counts - status_counts = {} - for status in TodoStatus: - count = len([t for t in self.todos if t.status == status]) - status_counts[status.value] = count - - # Priority counts - priority_counts = {} - for priority in TodoPriority: - count = len([t for t in self.todos if t.priority == priority]) - priority_counts[priority.value] = count - - # Tags - all_tags = set() - for todo in self.todos: - all_tags.update(todo.tags) - - # Completion rate - done = status_counts.get("done", 0) - completion_rate = (done / total * 100) if total > 0 else 0 - - return { - "total": total, - "status": status_counts, - "priority": priority_counts, - "tags": list(all_tags), - "completion_rate": completion_rate, - } - - def display_statistics(self): - """Display todo statistics.""" - stats = self.get_statistics() - - # Create stats panel - content = f""" -[bold cyan]Todo Statistics[/bold cyan] - -[bold]Total Todos:[/bold] {stats["total"]} -[bold]Completion Rate:[/bold] {stats["completion_rate"]:.1f}% - -[bold]By Status:[/bold] - โญ• Todo: {stats["status"].get("todo", 0)} - ๐Ÿ”„ In Progress: {stats["status"].get("in_progress", 0)} - โœ… Done: {stats["status"].get("done", 0)} - โŒ Cancelled: {stats["status"].get("cancelled", 0)} - -[bold]By Priority:[/bold] - ๐Ÿ”ด Urgent: {stats["priority"].get("urgent", 0)} - ๐ŸŸ  High: {stats["priority"].get("high", 0)} - ๐ŸŸก Medium: {stats["priority"].get("medium", 0)} - ๐ŸŸข Low: {stats["priority"].get("low", 0)} - -[bold]Tags:[/bold] {", ".join(stats["tags"]) if stats["tags"] else "None"} -""" - - panel = Panel(content.strip(), title="๐Ÿ“Š Statistics", box=box.ROUNDED) - self.console.print(panel) diff --git a/pkg/hanzo/src/hanzo/mcp_server.py b/pkg/hanzo/src/hanzo/mcp_server.py deleted file mode 100644 index b2dae0c19..000000000 --- a/pkg/hanzo/src/hanzo/mcp_server.py +++ /dev/null @@ -1,28 +0,0 @@ -"""MCP server entry point for hanzo/mcp command.""" - -import sys - -import click - - -def main(): - """Start the Hanzo MCP server. - - This wrapper defers to hanzo_mcp.cli:main so that the CLI can parse - transport flags and configure logging BEFORE importing any heavy modules, - preventing stdio protocol corruption. - """ - try: - from hanzo_mcp.cli import main as cli_main - - cli_main() - except ImportError: - click.echo( - "Error: hanzo-mcp is not installed. Please run: pip install hanzo[mcp] or pip install hanzo[all]", - err=True, - ) - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/pkg/hanzo/src/hanzo/memory_manager.py b/pkg/hanzo/src/hanzo/memory_manager.py deleted file mode 100644 index 4b1ee29c7..000000000 --- a/pkg/hanzo/src/hanzo/memory_manager.py +++ /dev/null @@ -1,448 +0,0 @@ -""" -Memory management system for Hanzo Dev. -Provides persistent context and memory like Claude Desktop. -""" - -import os -import json -import hashlib -from typing import Any, Dict, List, Optional -from pathlib import Path -from datetime import datetime -from dataclasses import asdict, dataclass - - -@dataclass -class MemoryItem: - """A single memory item.""" - - id: str - content: str - type: str # 'context', 'instruction', 'fact', 'code' - created_at: str - tags: List[str] - priority: int = 0 # Higher priority items are kept longer - - def to_dict(self) -> Dict: - return asdict(self) - - @classmethod - def from_dict(cls, data: Dict) -> "MemoryItem": - return cls(**data) - - -class MemoryManager: - """Manages persistent memory and context for AI conversations.""" - - def __init__(self, workspace_dir: str = None): - """Initialize memory manager.""" - if workspace_dir: - self.memory_dir = Path(workspace_dir) / ".hanzo" / "memory" - else: - self.memory_dir = Path.home() / ".hanzo" / "memory" - - self.memory_dir.mkdir(parents=True, exist_ok=True) - self.memory_file = self.memory_dir / "context.json" - self.session_file = self.memory_dir / "session.json" - - self.memories: List[MemoryItem] = [] - self.session_context: Dict[str, Any] = {} - - self.load_memories() - self.load_session() - - def load_memories(self): - """Load persistent memories from disk.""" - if self.memory_file.exists(): - try: - with open(self.memory_file, "r") as f: - data = json.load(f) - self.memories = [ - MemoryItem.from_dict(item) for item in data.get("memories", []) - ] - except Exception as e: - print(f"Error loading memories: {e}") - self.memories = [] - else: - # Initialize with default memories - self._init_default_memories() - - def _init_default_memories(self): - """Initialize with helpful default memories.""" - defaults = [ - MemoryItem( - id=self._generate_id("system"), - content="I am Hanzo Dev, an AI coding assistant with multiple orchestrator modes.", - type="instruction", - created_at=datetime.now().isoformat(), - tags=["system", "identity"], - priority=10, - ), - MemoryItem( - id=self._generate_id("capabilities"), - content="I can read/write files, search code, run commands, and use various AI models.", - type="fact", - created_at=datetime.now().isoformat(), - tags=["system", "capabilities"], - priority=9, - ), - MemoryItem( - id=self._generate_id("help"), - content="Use /help for commands, #memory for context management, or just chat naturally.", - type="instruction", - created_at=datetime.now().isoformat(), - tags=["system", "usage"], - priority=8, - ), - ] - self.memories = defaults - self.save_memories() - - def save_memories(self): - """Save memories to disk.""" - try: - data = { - "memories": [m.to_dict() for m in self.memories], - "updated_at": datetime.now().isoformat(), - } - with open(self.memory_file, "w") as f: - json.dump(data, f, indent=2) - except Exception as e: - print(f"Error saving memories: {e}") - - def load_session(self): - """Load current session context.""" - if self.session_file.exists(): - try: - with open(self.session_file, "r") as f: - self.session_context = json.load(f) - except Exception: - self.session_context = {} - else: - self.session_context = { - "started_at": datetime.now().isoformat(), - "messages": [], - "current_task": None, - "preferences": {}, - } - - def save_session(self): - """Save session context.""" - try: - with open(self.session_file, "w") as f: - json.dump(self.session_context, f, indent=2) - except Exception as e: - print(f"Error saving session: {e}") - - def add_memory( - self, - content: str, - type: str = "context", - tags: List[str] = None, - priority: int = 0, - ) -> str: - """Add a new memory item.""" - memory_id = self._generate_id(content) - - # Check if similar memory exists - for mem in self.memories: - if mem.content == content: - return mem.id # Don't duplicate - - memory = MemoryItem( - id=memory_id, - content=content, - type=type, - created_at=datetime.now().isoformat(), - tags=tags or [], - priority=priority, - ) - - self.memories.append(memory) - self.save_memories() - - return memory_id - - def remove_memory(self, memory_id: str) -> bool: - """Remove a memory by ID.""" - for i, mem in enumerate(self.memories): - if mem.id == memory_id: - del self.memories[i] - self.save_memories() - return True - return False - - def clear_memories(self, keep_system: bool = True): - """Clear all memories, optionally keeping system memories.""" - if keep_system: - self.memories = [m for m in self.memories if "system" in m.tags] - else: - self.memories = [] - self.save_memories() - - def get_memories( - self, type: str = None, tags: List[str] = None - ) -> List[MemoryItem]: - """Get memories filtered by type or tags.""" - result = self.memories - - if type: - result = [m for m in result if m.type == type] - - if tags: - result = [m for m in result if any(tag in m.tags for tag in tags)] - - # Sort by priority and creation date - result.sort(key=lambda m: (-m.priority, m.created_at), reverse=True) - - return result - - def get_context_string(self, max_tokens: int = 2000) -> str: - """Get a formatted context string for AI prompts.""" - # Sort memories by priority - sorted_memories = sorted(self.memories, key=lambda m: -m.priority) - - context_parts = [] - token_count = 0 - - for memory in sorted_memories: - # Rough token estimation (4 chars = 1 token) - memory_tokens = len(memory.content) // 4 - - if token_count + memory_tokens > max_tokens: - break - - if memory.type == "instruction": - context_parts.append(f"INSTRUCTION: {memory.content}") - elif memory.type == "fact": - context_parts.append(f"FACT: {memory.content}") - elif memory.type == "code": - context_parts.append(f"CODE CONTEXT:\n{memory.content}") - else: - context_parts.append(memory.content) - - token_count += memory_tokens - - return "\n\n".join(context_parts) - - def add_message(self, role: str, content: str): - """Add a message to session history.""" - self.session_context["messages"].append( - {"role": role, "content": content, "timestamp": datetime.now().isoformat()} - ) - - # Keep only last 50 messages - if len(self.session_context["messages"]) > 50: - self.session_context["messages"] = self.session_context["messages"][-50:] - - self.save_session() - - def get_recent_messages(self, count: int = 10) -> List[Dict]: - """Get recent messages from session.""" - return self.session_context["messages"][-count:] - - def set_preference(self, key: str, value: Any): - """Set a user preference.""" - self.session_context["preferences"][key] = value - self.save_session() - - def get_preference(self, key: str, default: Any = None) -> Any: - """Get a user preference.""" - return self.session_context["preferences"].get(key, default) - - def _generate_id(self, content: str) -> str: - """Generate a unique ID for a memory item.""" - hash_input = f"{content}{datetime.now().isoformat()}" - return hashlib.md5(hash_input.encode()).hexdigest()[:8] - - def summarize_for_ai(self) -> str: - """Create a summary suitable for AI context.""" - summary = [] - - # Add system memories - system_memories = self.get_memories(tags=["system"]) - if system_memories: - summary.append("SYSTEM CONTEXT:") - for mem in system_memories[:3]: # Top 3 system memories - summary.append(f"- {mem.content}") - - # Add recent instructions - instructions = self.get_memories(type="instruction") - if instructions: - summary.append("\nINSTRUCTIONS:") - for mem in instructions[:3]: # Top 3 instructions - summary.append(f"- {mem.content}") - - # Add important facts - facts = self.get_memories(type="fact") - if facts: - summary.append("\nKEY FACTS:") - for mem in facts[:5]: # Top 5 facts - summary.append(f"- {mem.content}") - - # Add current task if set - if self.session_context.get("current_task"): - summary.append(f"\nCURRENT TASK: {self.session_context['current_task']}") - - return "\n".join(summary) - - def export_memories(self, file_path: str): - """Export memories to a file.""" - data = { - "memories": [m.to_dict() for m in self.memories], - "session": self.session_context, - "exported_at": datetime.now().isoformat(), - } - - with open(file_path, "w") as f: - json.dump(data, f, indent=2) - - def import_memories(self, file_path: str): - """Import memories from a file.""" - with open(file_path, "r") as f: - data = json.load(f) - - # Merge memories (avoid duplicates) - existing_ids = {m.id for m in self.memories} - - for mem_data in data.get("memories", []): - if mem_data["id"] not in existing_ids: - self.memories.append(MemoryItem.from_dict(mem_data)) - - # Merge session preferences - if "session" in data and "preferences" in data["session"]: - self.session_context["preferences"].update(data["session"]["preferences"]) - - self.save_memories() - self.save_session() - - -def handle_memory_command(command: str, memory_manager: MemoryManager, console) -> bool: - """ - Handle #memory commands. - Returns True if command was handled, False otherwise. - """ - from rich.panel import Panel - from rich.table import Table - - parts = command.strip().split(maxsplit=2) - - if len(parts) == 1 or parts[1] == "show": - # Show current memories - memories = memory_manager.get_memories() - - if not memories: - console.print("[yellow]No memories stored.[/yellow]") - return True - - table = Table( - title="Current Memories", show_header=True, header_style="bold magenta" - ) - table.add_column("ID", style="cyan", width=10) - table.add_column("Type", width=12) - table.add_column("Content", width=50) - table.add_column("Priority", width=8) - - for mem in memories[:10]: # Show top 10 - content = mem.content[:47] + "..." if len(mem.content) > 50 else mem.content - table.add_row(mem.id, mem.type, content, str(mem.priority)) - - console.print(table) - - if len(memories) > 10: - console.print(f"[dim]... and {len(memories) - 10} more[/dim]") - - return True - - elif parts[1] == "add": - if len(parts) < 3: - console.print("[red]Usage: #memory add [/red]") - return True - - content = parts[2] - memory_id = memory_manager.add_memory(content, type="context") - console.print(f"[green]Added memory: {memory_id}[/green]") - return True - - elif parts[1] == "remove": - if len(parts) < 3: - console.print("[red]Usage: #memory remove [/red]") - return True - - memory_id = parts[2] - if memory_manager.remove_memory(memory_id): - console.print(f"[green]Removed memory: {memory_id}[/green]") - else: - console.print(f"[red]Memory not found: {memory_id}[/red]") - return True - - elif parts[1] == "clear": - memory_manager.clear_memories(keep_system=True) - console.print("[green]Cleared all non-system memories.[/green]") - return True - - elif parts[1] == "save": - memory_manager.save_memories() - memory_manager.save_session() - console.print("[green]Memories saved.[/green]") - return True - - elif parts[1] == "export": - if len(parts) < 3: - file_path = "hanzo_memories.json" - else: - file_path = parts[2] - - memory_manager.export_memories(file_path) - console.print(f"[green]Exported memories to {file_path}[/green]") - return True - - elif parts[1] == "import": - if len(parts) < 3: - console.print("[red]Usage: #memory import [/red]") - return True - - file_path = parts[2] - try: - memory_manager.import_memories(file_path) - console.print(f"[green]Imported memories from {file_path}[/green]") - except Exception as e: - console.print(f"[red]Error importing: {e}[/red]") - return True - - elif parts[1] == "context": - # Show AI context - context = memory_manager.summarize_for_ai() - console.print( - Panel( - context, - title="[bold cyan]AI Context[/bold cyan]", - title_align="left", - border_style="dim cyan", - ) - ) - return True - - elif parts[1] == "help": - help_text = """Memory Commands: -#memory [show] - Show current memories -#memory add - Add new memory -#memory remove - Remove memory by ID -#memory clear - Clear all memories (keep system) -#memory save - Save memories to disk -#memory export [file] - Export memories to file -#memory import - Import memories from file -#memory context - Show AI context summary -#memory help - Show this help""" - - console.print( - Panel( - help_text, - title="[bold cyan]Memory Help[/bold cyan]", - title_align="left", - border_style="dim cyan", - ) - ) - return True - - return False diff --git a/pkg/hanzo/src/hanzo/model_registry.py b/pkg/hanzo/src/hanzo/model_registry.py deleted file mode 100644 index a0dbf1df7..000000000 --- a/pkg/hanzo/src/hanzo/model_registry.py +++ /dev/null @@ -1,423 +0,0 @@ -"""Unified Model Registry - Single source of truth for all AI model mappings. - -This module provides a centralized registry for AI model configurations, -eliminating duplication and ensuring consistency across the codebase. -""" - -from __future__ import annotations - -from enum import Enum -from typing import Set, Dict, List, Optional -from dataclasses import field, dataclass - - -class ModelProvider(Enum): - """Enumeration of AI model providers.""" - - ANTHROPIC = "anthropic" - OPENAI = "openai" - GOOGLE = "google" - XAI = "xai" - OLLAMA = "ollama" - DEEPSEEK = "deepseek" - MISTRAL = "mistral" - META = "meta" - HANZO = "hanzo" - - -@dataclass(frozen=True) -class ModelConfig: - """Configuration for a single AI model.""" - - full_name: str - provider: ModelProvider - aliases: Set[str] = field(default_factory=set) - default_params: Dict[str, any] = field(default_factory=dict) - supports_vision: bool = False - supports_tools: bool = False - supports_streaming: bool = True - context_window: int = 8192 - max_output: int = 4096 - api_key_env: Optional[str] = None - cli_command: Optional[str] = None - - -class ModelRegistry: - """Centralized registry for all AI models. - - This is the single source of truth for model configurations, - ensuring no duplication across the codebase. - """ - - _instance: Optional[ModelRegistry] = None - _models: Dict[str, ModelConfig] = {} - - def __new__(cls) -> ModelRegistry: - """Singleton pattern to ensure single registry instance.""" - if cls._instance is None: - cls._instance = super().__new__(cls) - cls._instance._initialize_models() - return cls._instance - - def _initialize_models(self) -> None: - """Initialize all model configurations.""" - # Claude models - self._register( - ModelConfig( - full_name="claude-3-5-sonnet-20241022", - provider=ModelProvider.ANTHROPIC, - aliases={"claude", "cc", "claude-code", "sonnet", "sonnet-4.1"}, - supports_vision=True, - supports_tools=True, - context_window=200000, - max_output=8192, - api_key_env="ANTHROPIC_API_KEY", - cli_command="claude", - ) - ) - - self._register( - ModelConfig( - full_name="claude-opus-4-1-20250805", - provider=ModelProvider.ANTHROPIC, - aliases={"opus", "opus-4.1", "claude-opus"}, - supports_vision=True, - supports_tools=True, - context_window=200000, - max_output=8192, - api_key_env="ANTHROPIC_API_KEY", - cli_command="claude", - ) - ) - - self._register( - ModelConfig( - full_name="claude-3-haiku-20240307", - provider=ModelProvider.ANTHROPIC, - aliases={"haiku", "claude-haiku"}, - supports_vision=True, - supports_tools=True, - context_window=200000, - max_output=4096, - api_key_env="ANTHROPIC_API_KEY", - cli_command="claude", - ) - ) - - # OpenAI models - self._register( - ModelConfig( - full_name="gpt-4-turbo", - provider=ModelProvider.OPENAI, - aliases={"gpt4", "gpt-4", "codex"}, - supports_vision=True, - supports_tools=True, - context_window=128000, - max_output=4096, - api_key_env="OPENAI_API_KEY", - cli_command="openai", - ) - ) - - self._register( - ModelConfig( - full_name="gpt-5-turbo", - provider=ModelProvider.OPENAI, - aliases={"gpt5", "gpt-5"}, - supports_vision=True, - supports_tools=True, - context_window=256000, - max_output=16384, - api_key_env="OPENAI_API_KEY", - cli_command="openai", - ) - ) - - self._register( - ModelConfig( - full_name="o1-preview", - provider=ModelProvider.OPENAI, - aliases={"o1", "openai-o1"}, - supports_vision=False, - supports_tools=False, - context_window=128000, - max_output=32768, - api_key_env="OPENAI_API_KEY", - cli_command="openai", - ) - ) - - # Google models - self._register( - ModelConfig( - full_name="gemini-1.5-pro", - provider=ModelProvider.GOOGLE, - aliases={"gemini", "gemini-pro"}, - supports_vision=True, - supports_tools=True, - context_window=2000000, - max_output=8192, - api_key_env="GEMINI_API_KEY", - cli_command="gemini", - ) - ) - - self._register( - ModelConfig( - full_name="gemini-1.5-flash", - provider=ModelProvider.GOOGLE, - aliases={"gemini-flash", "flash"}, - supports_vision=True, - supports_tools=True, - context_window=1000000, - max_output=8192, - api_key_env="GEMINI_API_KEY", - cli_command="gemini", - ) - ) - - # xAI models - self._register( - ModelConfig( - full_name="grok-2", - provider=ModelProvider.XAI, - aliases={"grok", "xai-grok"}, - supports_vision=False, - supports_tools=True, - context_window=128000, - max_output=8192, - api_key_env="XAI_API_KEY", - cli_command="grok", - ) - ) - - # Ollama models - self._register( - ModelConfig( - full_name="ollama/llama-3.2-3b", - provider=ModelProvider.OLLAMA, - aliases={"llama", "llama-3.2", "llama3"}, - supports_vision=False, - supports_tools=False, - context_window=128000, - max_output=4096, - api_key_env=None, # Local model - cli_command="ollama", - ) - ) - - self._register( - ModelConfig( - full_name="ollama/mistral:7b", - provider=ModelProvider.MISTRAL, - aliases={"mistral", "mistral-7b"}, - supports_vision=False, - supports_tools=False, - context_window=32000, - max_output=4096, - api_key_env=None, # Local model - cli_command="ollama", - ) - ) - - # DeepSeek models - self._register( - ModelConfig( - full_name="deepseek-coder-v2", - provider=ModelProvider.DEEPSEEK, - aliases={"deepseek", "deepseek-coder"}, - supports_vision=False, - supports_tools=True, - context_window=128000, - max_output=8192, - api_key_env="DEEPSEEK_API_KEY", - cli_command="deepseek", - ) - ) - - def _register(self, config: ModelConfig) -> None: - """Register a model configuration. - - Args: - config: Model configuration to register - """ - # Register by full name - self._models[config.full_name] = config - - # Register all aliases - for alias in config.aliases: - self._models[alias.lower()] = config - - def get(self, model_name: str) -> Optional[ModelConfig]: - """Get model configuration by name or alias. - - Args: - model_name: Model name or alias - - Returns: - Model configuration or None if not found - """ - return self._models.get(model_name.lower()) - - def resolve(self, model_name: str) -> str: - """Resolve model name or alias to full model name. - - Args: - model_name: Model name or alias - - Returns: - Full model name, or original if not found - """ - config = self.get(model_name) - return config.full_name if config else model_name - - def get_by_provider(self, provider: ModelProvider) -> List[ModelConfig]: - """Get all models for a specific provider. - - Args: - provider: Model provider - - Returns: - List of model configurations - """ - seen = set() - results = [] - for config in self._models.values(): - if config.provider == provider and config.full_name not in seen: - seen.add(config.full_name) - results.append(config) - return results - - def get_models_supporting( - self, - vision: Optional[bool] = None, - tools: Optional[bool] = None, - streaming: Optional[bool] = None, - ) -> List[ModelConfig]: - """Get models supporting specific features. - - Args: - vision: Filter by vision support - tools: Filter by tool support - streaming: Filter by streaming support - - Returns: - List of matching model configurations - """ - seen = set() - results = [] - - for config in self._models.values(): - if config.full_name in seen: - continue - - if vision is not None and config.supports_vision != vision: - continue - if tools is not None and config.supports_tools != tools: - continue - if streaming is not None and config.supports_streaming != streaming: - continue - - seen.add(config.full_name) - results.append(config) - - return results - - def get_api_key_env(self, model_name: str) -> Optional[str]: - """Get the API key environment variable for a model. - - Args: - model_name: Model name or alias - - Returns: - Environment variable name or None - """ - config = self.get(model_name) - return config.api_key_env if config else None - - def get_cli_command(self, model_name: str) -> Optional[str]: - """Get the CLI command for a model. - - Args: - model_name: Model name or alias - - Returns: - CLI command or None - """ - config = self.get(model_name) - return config.cli_command if config else None - - def list_all_models(self) -> List[str]: - """List all unique model full names. - - Returns: - List of full model names - """ - seen = set() - for config in self._models.values(): - seen.add(config.full_name) - return sorted(list(seen)) - - def list_all_aliases(self) -> Dict[str, str]: - """List all aliases and their full names. - - Returns: - Dictionary mapping aliases to full names - """ - result = {} - for key, config in self._models.items(): - if key != config.full_name: - result[key] = config.full_name - return result - - -# Global singleton instance -registry = ModelRegistry() - - -# Convenience functions -def resolve_model(model_name: str) -> str: - """Resolve model name or alias to full model name. - - Args: - model_name: Model name or alias - - Returns: - Full model name - """ - return registry.resolve(model_name) - - -def get_model_config(model_name: str) -> Optional[ModelConfig]: - """Get model configuration. - - Args: - model_name: Model name or alias - - Returns: - Model configuration or None - """ - return registry.get(model_name) - - -def get_api_key_env(model_name: str) -> Optional[str]: - """Get API key environment variable for model. - - Args: - model_name: Model name or alias - - Returns: - Environment variable name or None - """ - return registry.get_api_key_env(model_name) - - -__all__ = [ - "ModelProvider", - "ModelConfig", - "ModelRegistry", - "registry", - "resolve_model", - "get_model_config", - "get_api_key_env", -] diff --git a/pkg/hanzo/src/hanzo/orchestrator_config.py b/pkg/hanzo/src/hanzo/orchestrator_config.py deleted file mode 100644 index a129a56cc..000000000 --- a/pkg/hanzo/src/hanzo/orchestrator_config.py +++ /dev/null @@ -1,391 +0,0 @@ -#!/usr/bin/env python3 -""" -Orchestrator Configuration for Hanzo Dev - -Supports multiple orchestration modes: -1. Router-based: Use hanzo-router to access any LLM -2. Direct model: Direct API access to specific models -3. Codex mode: Specialized code-focused orchestration -4. Hybrid: Combine router and direct access -""" - -import os -import socket -from enum import Enum -from typing import Any, Dict, List, Optional -from dataclasses import field, dataclass - - -def check_local_node( - host: str = "localhost", port: int = 4000, timeout: float = 0.5 -) -> bool: - """Check if hanzo-node is running locally. - - Args: - host: Host to check (default: localhost) - port: Port to check (default: 4000) - timeout: Connection timeout in seconds - - Returns: - True if node is reachable, False otherwise - """ - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(timeout) - result = sock.connect_ex((host, port)) - sock.close() - return result == 0 - except Exception: - return False - - -def is_authenticated() -> bool: - """Check if user is authenticated with Hanzo cloud.""" - api_key = os.getenv("HANZO_API_KEY") - return api_key is not None and len(api_key) > 0 - - -def get_auth_status() -> str: - """Get authentication status message.""" - if is_authenticated(): - return "authenticated (cloud account)" - return "free tier (no login)" - - -def get_default_router_endpoint() -> str: - """Get default router endpoint with smart detection. - - Priority: - 1. HANZO_ROUTER_URL environment variable (explicit override) - 2. Gateway (gateway.hanzo.ai) - free tier by default - - Upgrade paths: - - Login: `hanzo login` - Use cloud account with full features - - Local: `hanzo node start` - Private AI on your machine - - Returns: - Router endpoint URL - """ - # Check environment variable first (explicit override) - env_url = os.getenv("HANZO_ROUTER_URL") - if env_url: - return env_url - - # Default to free gateway - best UX for new users - # Users can login or run local node for more features - return "https://gateway.hanzo.ai" - - -class OrchestratorMode(Enum): - """Orchestration modes.""" - - ROUTER = "router" # Via hanzo-router (unified gateway) - DIRECT = "direct" # Direct model API access - CODEX = "codex" # Codex-specific mode - HYBRID = "hybrid" # Router + direct combination - LOCAL = "local" # Local models only - - -class ModelProvider(Enum): - """Model providers.""" - - OPENAI = "openai" - ANTHROPIC = "anthropic" - GOOGLE = "google" - MISTRAL = "mistral" - LOCAL = "local" - ROUTER = "router" # Via hanzo-router - CODEX = "codex" # OpenAI Codex - - -@dataclass -class ModelConfig: - """Configuration for a specific model.""" - - name: str # Model name (e.g., "gpt-5", "gpt-4o") - provider: ModelProvider # Provider type - endpoint: Optional[str] = None # Custom endpoint - api_key: Optional[str] = None # API key (if not using env) - context_window: int = 8192 # Context window size - max_output: int = 4096 # Max output tokens - temperature: float = 0.7 # Temperature setting - capabilities: List[str] = field(default_factory=list) # Model capabilities - cost_per_1k_input: float = 0.01 # Cost per 1K input tokens - cost_per_1k_output: float = 0.03 # Cost per 1K output tokens - supports_tools: bool = True # Supports function calling - supports_vision: bool = False # Supports image input - supports_streaming: bool = True # Supports streaming responses - - -@dataclass -class RouterConfig: - """Configuration for hanzo-router. - - Automatically detects and prioritizes: - 1. HANZO_ROUTER_URL env var - 2. Local hanzo-node (localhost:4000) - 3. Gateway (gateway.hanzo.ai) - """ - - endpoint: Optional[str] = None # Router endpoint (auto-detected if None) - api_key: Optional[str] = None # Router API key - model_preferences: List[str] = field(default_factory=list) # Preferred models - fallback_models: List[str] = field(default_factory=list) # Fallback models - load_balancing: bool = True # Enable load balancing - cache_enabled: bool = True # Enable response caching - retry_on_failure: bool = True # Retry failed requests - max_retries: int = 3 # Max retry attempts - - def __post_init__(self): - """Auto-detect endpoint if not provided.""" - if self.endpoint is None: - self.endpoint = get_default_router_endpoint() - - -@dataclass -class CodexConfig: - """Configuration for Codex mode.""" - - model: str = "code-davinci-002" # Codex model - mode: str = "code-review" # Mode: code-review, generation, completion - languages: List[str] = field(default_factory=lambda: ["python", "typescript", "go"]) - max_tokens: int = 8000 # Max tokens for Codex - stop_sequences: List[str] = field(default_factory=list) # Stop sequences - enable_comments: bool = True # Generate with comments - enable_docstrings: bool = True # Generate docstrings - enable_type_hints: bool = True # Generate type hints - - -@dataclass -class OrchestratorConfig: - """Complete orchestrator configuration.""" - - mode: OrchestratorMode = OrchestratorMode.ROUTER - primary_model: str = "gpt-5" # Primary orchestrator model - models: Dict[str, ModelConfig] = field(default_factory=dict) - router: Optional[RouterConfig] = None - codex: Optional[CodexConfig] = None - worker_models: List[str] = field(default_factory=list) # Worker agent models - critic_models: List[str] = field(default_factory=list) # Critic agent models - local_models: List[str] = field(default_factory=list) # Local models - enable_cost_optimization: bool = True # Enable cost optimization - cost_threshold: float = 0.10 # Cost threshold per request - prefer_local: bool = True # Prefer local models when possible - enable_caching: bool = True # Cache responses - enable_monitoring: bool = True # Monitor performance - debug: bool = False # Debug mode - - -# Predefined configurations -CONFIGS = { - "gpt-5-pro-codex": OrchestratorConfig( - mode=OrchestratorMode.HYBRID, - primary_model="gpt-5-pro", - models={ - "gpt-5-pro": ModelConfig( - name="gpt-5-pro", - provider=ModelProvider.OPENAI, - context_window=200000, - capabilities=["reasoning", "code", "analysis", "vision"], - cost_per_1k_input=0.20, - cost_per_1k_output=0.60, - supports_vision=True, - ), - "codex": ModelConfig( - name="code-davinci-002", - provider=ModelProvider.CODEX, - context_window=8000, - capabilities=["code_generation", "completion", "review"], - cost_per_1k_input=0.02, - cost_per_1k_output=0.02, - ), - }, - codex=CodexConfig( - model="code-davinci-002", - mode="code-review", - enable_comments=True, - enable_docstrings=True, - enable_type_hints=True, - ), - worker_models=["codex", "gpt-4o"], - critic_models=["gpt-5-pro"], - enable_cost_optimization=True, - ), - "router-based": OrchestratorConfig( - mode=OrchestratorMode.ROUTER, - primary_model="router:gpt-4o-mini", # Free on gateway - router=RouterConfig( - # Auto-detect: local node โ†’ gateway (free models) - model_preferences=["gpt-4o-mini", "gpt-3.5-turbo", "llama-3.1-8b"], - fallback_models=["gpt-4o", "claude-3-5-sonnet"], - load_balancing=True, - cache_enabled=True, - ), - worker_models=["router:gpt-4o-mini", "router:llama-3.1-8b"], - critic_models=["router:gpt-4o-mini"], - enable_cost_optimization=True, - ), - "direct-gpt5": OrchestratorConfig( - mode=OrchestratorMode.DIRECT, - primary_model="gpt-5", - models={ - "gpt-5": ModelConfig( - name="gpt-5-latest", - provider=ModelProvider.OPENAI, - endpoint="https://api.openai.com/v1", - context_window=128000, - capabilities=["reasoning", "code", "analysis"], - cost_per_1k_input=0.15, - cost_per_1k_output=0.45, - ), - }, - worker_models=["gpt-4o"], - critic_models=["gpt-5"], - enable_cost_optimization=False, # Use GPT-5 for everything - ), - "codex-focused": OrchestratorConfig( - mode=OrchestratorMode.CODEX, - primary_model="codex", - codex=CodexConfig( - model="code-davinci-002", - mode="code-generation", - languages=["python", "typescript", "rust", "go"], - max_tokens=8000, - enable_comments=True, - ), - worker_models=["codex"], - critic_models=["gpt-4o"], # Use GPT-4o for code review - enable_cost_optimization=True, - ), - "cost-optimized": OrchestratorConfig( - mode=OrchestratorMode.HYBRID, - primary_model="local:llama3.2", - models={ - "local:llama3.2": ModelConfig( - name="llama-3.2-3b", - provider=ModelProvider.LOCAL, - context_window=8192, - capabilities=["basic_reasoning", "simple_tasks"], - cost_per_1k_input=0.0, - cost_per_1k_output=0.0, - ), - }, - router=RouterConfig( - # Auto-detect: local node โ†’ gateway (free models) - model_preferences=["gpt-4o-mini", "llama-3.1-8b"], - fallback_models=["gpt-3.5-turbo"], - ), - worker_models=["local:llama3.2", "local:qwen2.5"], - critic_models=["router:gpt-4o-mini"], # Use free model for review - local_models=["llama3.2", "qwen2.5", "mistral"], - enable_cost_optimization=True, - prefer_local=True, - ), -} - - -def get_orchestrator_config(name: str) -> OrchestratorConfig: - """Get a predefined orchestrator configuration. - - Args: - name: Configuration name or model spec - - Returns: - OrchestratorConfig instance - """ - # Check predefined configs - if name in CONFIGS: - return CONFIGS[name] - - # Parse model spec (e.g., "router:gpt-5", "direct:gpt-4o", "codex") - if ":" in name: - mode, model = name.split(":", 1) - if mode == "router": - return OrchestratorConfig( - mode=OrchestratorMode.ROUTER, - primary_model=f"router:{model}", - router=RouterConfig( - model_preferences=[model], - ), - ) - elif mode == "direct": - return OrchestratorConfig( - mode=OrchestratorMode.DIRECT, - primary_model=model, - ) - elif mode == "local": - return OrchestratorConfig( - mode=OrchestratorMode.LOCAL, - primary_model=f"local:{model}", - local_models=[model], - enable_cost_optimization=True, - prefer_local=True, - ) - - # Default to router mode with specified model - return OrchestratorConfig( - mode=OrchestratorMode.ROUTER, - primary_model=name, - router=RouterConfig( - model_preferences=[name], - ), - ) - - -def list_available_configs() -> List[str]: - """List available orchestrator configurations.""" - return list(CONFIGS.keys()) + [ - "router:", - "direct:", - "local:", - "codex", - ] - - -# Export configuration builder -def build_custom_config( - mode: str = "router", - primary_model: str = "gpt-5", - use_router: bool = True, - use_codex: bool = False, - worker_models: Optional[List[str]] = None, - critic_models: Optional[List[str]] = None, - enable_cost_optimization: bool = True, - router_endpoint: Optional[str] = None, -) -> OrchestratorConfig: - """Build a custom orchestrator configuration. - - Args: - mode: Orchestration mode (router, direct, codex, hybrid, local) - primary_model: Primary orchestrator model - use_router: Use hanzo-router for model access - use_codex: Enable Codex for code tasks - worker_models: Worker agent models - critic_models: Critic agent models - enable_cost_optimization: Enable cost optimization - router_endpoint: Custom router endpoint (None for auto-detect) - - Returns: - Custom OrchestratorConfig - """ - config = OrchestratorConfig( - mode=OrchestratorMode(mode), - primary_model=primary_model, - worker_models=worker_models or ["gpt-4o", "claude-3-5"], - critic_models=critic_models or ["gpt-5"], - enable_cost_optimization=enable_cost_optimization, - ) - - if use_router: - config.router = RouterConfig( - endpoint=router_endpoint, # None triggers auto-detection in __post_init__ - model_preferences=[primary_model], - ) - - if use_codex: - config.codex = CodexConfig( - model="code-davinci-002", - mode="code-review", - ) - - return config diff --git a/pkg/hanzo/src/hanzo/rate_limiter.py b/pkg/hanzo/src/hanzo/rate_limiter.py deleted file mode 100644 index 710a67860..000000000 --- a/pkg/hanzo/src/hanzo/rate_limiter.py +++ /dev/null @@ -1,317 +0,0 @@ -""" -Rate limiting and error recovery for Hanzo Dev. -Prevents API overuse and handles failures gracefully. -""" - -import time -import random -import asyncio -from typing import Any, Dict, Callable, Optional -from datetime import datetime, timedelta -from collections import deque -from dataclasses import field, dataclass - - -@dataclass -class RateLimitConfig: - """Configuration for rate limiting.""" - - requests_per_minute: int = 20 - requests_per_hour: int = 100 - burst_size: int = 5 - cooldown_seconds: int = 60 - max_retries: int = 3 - backoff_base: float = 2.0 - jitter: bool = True - - -@dataclass -class RateLimitState: - """Current state of rate limiter.""" - - minute_requests: deque = field(default_factory=lambda: deque(maxlen=60)) - hour_requests: deque = field(default_factory=lambda: deque(maxlen=3600)) - last_request: Optional[datetime] = None - consecutive_errors: int = 0 - total_requests: int = 0 - total_errors: int = 0 - is_throttled: bool = False - throttle_until: Optional[datetime] = None - - -class RateLimiter: - """Rate limiter with error recovery.""" - - def __init__(self, config: RateLimitConfig = None): - """Initialize rate limiter.""" - self.config = config or RateLimitConfig() - self.states: Dict[str, RateLimitState] = {} - - def get_state(self, key: str = "default") -> RateLimitState: - """Get or create state for a key.""" - if key not in self.states: - self.states[key] = RateLimitState() - return self.states[key] - - async def check_rate_limit(self, key: str = "default") -> tuple[bool, float]: - """ - Check if request is allowed. - Returns (allowed, wait_seconds). - """ - state = self.get_state(key) - now = datetime.now() - - # Check if throttled - if state.is_throttled and state.throttle_until: - if now < state.throttle_until: - wait_seconds = (state.throttle_until - now).total_seconds() - return False, wait_seconds - else: - # Throttle period ended - state.is_throttled = False - state.throttle_until = None - - # Clean old requests - minute_ago = now - timedelta(minutes=1) - hour_ago = now - timedelta(hours=1) - - # Remove old requests from queues - while state.minute_requests and state.minute_requests[0] < minute_ago: - state.minute_requests.popleft() - - while state.hour_requests and state.hour_requests[0] < hour_ago: - state.hour_requests.popleft() - - # Check minute limit - if len(state.minute_requests) >= self.config.requests_per_minute: - # Calculate wait time - oldest = state.minute_requests[0] - wait_seconds = (oldest + timedelta(minutes=1) - now).total_seconds() - return False, max(0, wait_seconds) - - # Check hour limit - if len(state.hour_requests) >= self.config.requests_per_hour: - # Calculate wait time - oldest = state.hour_requests[0] - wait_seconds = (oldest + timedelta(hours=1) - now).total_seconds() - return False, max(0, wait_seconds) - - # Check burst limit - if state.last_request: - time_since_last = (now - state.last_request).total_seconds() - if time_since_last < 1.0 / self.config.burst_size: - wait_seconds = (1.0 / self.config.burst_size) - time_since_last - return False, wait_seconds - - return True, 0 - - async def acquire(self, key: str = "default") -> bool: - """ - Acquire a rate limit slot. - Waits if necessary. - """ - while True: - allowed, wait_seconds = await self.check_rate_limit(key) - - if allowed: - # Record request - state = self.get_state(key) - now = datetime.now() - state.minute_requests.append(now) - state.hour_requests.append(now) - state.last_request = now - state.total_requests += 1 - return True - - # Wait before retrying - if wait_seconds > 0: - await asyncio.sleep(min(wait_seconds, 5)) # Check every 5 seconds max - - def record_error(self, key: str = "default", error: Exception = None): - """Record an error for the key.""" - state = self.get_state(key) - state.consecutive_errors += 1 - state.total_errors += 1 - - # Implement exponential backoff on errors - if state.consecutive_errors >= 3: - # Throttle for increasing periods - backoff_minutes = min( - self.config.backoff_base ** (state.consecutive_errors - 2), - 60, # Max 1 hour - ) - state.is_throttled = True - state.throttle_until = datetime.now() + timedelta(minutes=backoff_minutes) - - def record_success(self, key: str = "default"): - """Record a successful request.""" - state = self.get_state(key) - state.consecutive_errors = 0 - - def get_status(self, key: str = "default") -> Dict[str, Any]: - """Get current status for monitoring.""" - state = self.get_state(key) - now = datetime.now() - - return { - "requests_last_minute": len(state.minute_requests), - "requests_last_hour": len(state.hour_requests), - "total_requests": state.total_requests, - "total_errors": state.total_errors, - "consecutive_errors": state.consecutive_errors, - "is_throttled": state.is_throttled, - "throttle_remaining": ( - (state.throttle_until - now).total_seconds() - if state.throttle_until and now < state.throttle_until - else 0 - ), - "minute_limit": self.config.requests_per_minute, - "hour_limit": self.config.requests_per_hour, - } - - -class ErrorRecovery: - """Error recovery with retries and fallback.""" - - def __init__(self, rate_limiter: RateLimiter = None): - """Initialize error recovery.""" - self.rate_limiter = rate_limiter or RateLimiter() - self.fallback_handlers: Dict[type, Callable] = {} - - def register_fallback(self, error_type: type, handler: Callable): - """Register a fallback handler for an error type.""" - self.fallback_handlers[error_type] = handler - - async def with_retry( - self, - func: Callable, - *args, - key: str = "default", - max_retries: Optional[int] = None, - **kwargs, - ) -> Any: - """ - Execute function with retry logic. - """ - max_retries = max_retries or self.rate_limiter.config.max_retries - last_error = None - - for attempt in range(max_retries): - try: - # Check rate limit - await self.rate_limiter.acquire(key) - - # Execute function - result = await func(*args, **kwargs) - - # Record success - self.rate_limiter.record_success(key) - - return result - - except Exception as e: - last_error = e - self.rate_limiter.record_error(key, e) - - # Check for fallback handler - for error_type, handler in self.fallback_handlers.items(): - if isinstance(e, error_type): - try: - return await handler(*args, **kwargs) - except Exception: - pass # Fallback failed, continue with retry - - # Calculate backoff - if attempt < max_retries - 1: - backoff = self.rate_limiter.config.backoff_base**attempt - - # Add jitter if configured - if self.rate_limiter.config.jitter: - backoff *= 0.5 + random.random() - - await asyncio.sleep(min(backoff, 60)) # Max 60 seconds - - # All retries failed - raise last_error or Exception("All retry attempts failed") - - async def with_circuit_breaker( - self, - func: Callable, - *args, - key: str = "default", - threshold: int = 5, - timeout: int = 60, - **kwargs, - ) -> Any: - """ - Execute function with circuit breaker pattern. - """ - state = self.rate_limiter.get_state(key) - - # Check if circuit is open - if state.is_throttled: - raise Exception(f"Circuit breaker open for {key}") - - try: - result = await self.with_retry(func, *args, key=key, **kwargs) - return result - - except Exception as e: - # Check if we should open the circuit - if state.consecutive_errors >= threshold: - state.is_throttled = True - state.throttle_until = datetime.now() + timedelta(seconds=timeout) - raise Exception(f"Circuit breaker triggered for {key}: {e}") - raise - - -class SmartRateLimiter: - """Smart rate limiter that adapts to API responses.""" - - def __init__(self): - """Initialize smart rate limiter.""" - self.limiters: Dict[str, RateLimiter] = {} - self.recovery = ErrorRecovery() - - # Default configs for known APIs - self.configs = { - "openai": RateLimitConfig( - requests_per_minute=60, requests_per_hour=1000, burst_size=10 - ), - "anthropic": RateLimitConfig( - requests_per_minute=50, requests_per_hour=1000, burst_size=5 - ), - "local": RateLimitConfig( - requests_per_minute=100, requests_per_hour=10000, burst_size=20 - ), - "free": RateLimitConfig( - requests_per_minute=10, requests_per_hour=100, burst_size=2 - ), - } - - def get_limiter(self, api_type: str) -> RateLimiter: - """Get or create limiter for API type.""" - if api_type not in self.limiters: - config = self.configs.get(api_type, RateLimitConfig()) - self.limiters[api_type] = RateLimiter(config) - return self.limiters[api_type] - - async def execute_with_limit( - self, api_type: str, func: Callable, *args, **kwargs - ) -> Any: - """Execute function with appropriate rate limiting.""" - limiter = self.get_limiter(api_type) - recovery = ErrorRecovery(limiter) - - return await recovery.with_retry(func, *args, key=api_type, **kwargs) - - def get_all_status(self) -> Dict[str, Dict[str, Any]]: - """Get status of all limiters.""" - return { - api_type: limiter.get_status() - for api_type, limiter in self.limiters.items() - } - - -# Global instance for easy use -smart_limiter = SmartRateLimiter() diff --git a/pkg/hanzo/src/hanzo/router/__init__.py b/pkg/hanzo/src/hanzo/router/__init__.py deleted file mode 100644 index 9900562e5..000000000 --- a/pkg/hanzo/src/hanzo/router/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Router module - re-exports from router package.""" - -try: - # Import directly from the installed router package - import router - from router import Router, embedding, aembedding, completion, acompletion - - # Re-export the entire router module - __all__ = [ - "router", - "Router", - "completion", - "acompletion", - "embedding", - "aembedding", - ] - - # Make router available as a submodule - import sys - - sys.modules["hanzo.router"] = router - -except ImportError as e: - # If router is not installed, provide helpful error - import sys - - print(f"Error importing router: {e}", file=sys.stderr) - print( - "Please install router from the main repository: pip install -e /Users/z/work/hanzo/router", - file=sys.stderr, - ) - - # Fallback: set to None when router not installed - router = None - Router = None - completion = None - acompletion = None - embedding = None - aembedding = None diff --git a/pkg/hanzo/src/hanzo/streaming.py b/pkg/hanzo/src/hanzo/streaming.py deleted file mode 100644 index 5b0787551..000000000 --- a/pkg/hanzo/src/hanzo/streaming.py +++ /dev/null @@ -1,292 +0,0 @@ -""" -Streaming response handler for Hanzo Dev. -Provides real-time feedback as AI generates responses. -""" - -import time -import asyncio -from typing import Callable, Optional, AsyncGenerator - -from rich.live import Live -from rich.panel import Panel -from rich.console import Console -from rich.markdown import Markdown - - -class StreamingHandler: - """Handles streaming responses from AI models.""" - - def __init__(self, console: Console = None): - """Initialize streaming handler.""" - self.console = console or Console() - self.current_response = "" - self.is_streaming = False - - async def stream_openai(self, client, messages: list, model: str = "gpt-4") -> str: - """Stream response from OpenAI API.""" - try: - stream = await client.chat.completions.create( - model=model, messages=messages, stream=True, max_tokens=1000 - ) - - self.current_response = "" - self.is_streaming = True - - with Live( - Panel( - "", - title="[bold cyan]AI Response[/bold cyan]", - title_align="left", - border_style="dim cyan", - ), - console=self.console, - refresh_per_second=10, - ) as live: - async for chunk in stream: - if chunk.choices[0].delta.content: - self.current_response += chunk.choices[0].delta.content - live.update( - Panel( - Markdown(self.current_response), - title="[bold cyan]AI Response[/bold cyan]", - title_align="left", - border_style="dim cyan", - padding=(1, 2), - ) - ) - - self.is_streaming = False - return self.current_response - - except Exception as e: - self.console.print(f"[red]Streaming error: {e}[/red]") - self.is_streaming = False - return None - - async def stream_anthropic( - self, client, messages: list, model: str = "claude-3-5-sonnet-20241022" - ) -> str: - """Stream response from Anthropic API.""" - try: - self.current_response = "" - self.is_streaming = True - - with Live( - Panel( - "", - title="[bold cyan]AI Response[/bold cyan]", - title_align="left", - border_style="dim cyan", - ), - console=self.console, - refresh_per_second=10, - ) as live: - async with client.messages.stream( - model=model, messages=messages, max_tokens=1000 - ) as stream: - async for text in stream.text_stream: - self.current_response += text - live.update( - Panel( - Markdown(self.current_response), - title="[bold cyan]AI Response[/bold cyan]", - title_align="left", - border_style="dim cyan", - padding=(1, 2), - ) - ) - - self.is_streaming = False - return self.current_response - - except Exception as e: - self.console.print(f"[red]Streaming error: {e}[/red]") - self.is_streaming = False - return None - - async def stream_ollama(self, message: str, model: str = "llama3.2") -> str: - """Stream response from Ollama local model.""" - import httpx - - try: - self.current_response = "" - self.is_streaming = True - - with Live( - Panel( - "", - title="[bold cyan]AI Response (Local)[/bold cyan]", - title_align="left", - border_style="dim cyan", - ), - console=self.console, - refresh_per_second=10, - ) as live: - async with httpx.AsyncClient() as client: - async with client.stream( - "POST", - "http://localhost:11434/api/generate", - json={"model": model, "prompt": message, "stream": True}, - timeout=60.0, - ) as response: - async for line in response.aiter_lines(): - if line: - import json - - data = json.loads(line) - if "response" in data: - self.current_response += data["response"] - live.update( - Panel( - Markdown(self.current_response), - title="[bold cyan]AI Response (Local)[/bold cyan]", - title_align="left", - border_style="dim cyan", - padding=(1, 2), - ) - ) - if data.get("done", False): - break - - self.is_streaming = False - return self.current_response - - except Exception as e: - self.console.print(f"[red]Ollama streaming error: {e}[/red]") - self.is_streaming = False - return None - - async def simulate_streaming(self, text: str, delay: float = 0.02) -> str: - """Simulate streaming for non-streaming APIs.""" - self.current_response = "" - self.is_streaming = True - - words = text.split() - - with Live( - Panel( - "", - title="[bold cyan]AI Response[/bold cyan]", - title_align="left", - border_style="dim cyan", - ), - console=self.console, - refresh_per_second=20, - ) as live: - for i, word in enumerate(words): - self.current_response += word - if i < len(words) - 1: - self.current_response += " " - - live.update( - Panel( - Markdown(self.current_response), - title="[bold cyan]AI Response[/bold cyan]", - title_align="left", - border_style="dim cyan", - padding=(1, 2), - ) - ) - await asyncio.sleep(delay) - - self.is_streaming = False - return self.current_response - - def stop_streaming(self): - """Stop current streaming operation.""" - self.is_streaming = False - if self.current_response: - self.console.print(f"\n[yellow]Streaming interrupted[/yellow]") - - -class TypewriterEffect: - """Provides typewriter effect for text output.""" - - def __init__(self, console: Console = None): - self.console = console or Console() - - async def type_text(self, text: str, speed: float = 0.03): - """Type text with typewriter effect.""" - for char in text: - self.console.print(char, end="") - await asyncio.sleep(speed) - self.console.print() # New line at end - - async def type_code(self, code: str, language: str = "python", speed: float = 0.01): - """Type code with syntax highlighting.""" - from rich.syntax import Syntax - - # Build up code progressively - current_code = "" - lines = code.split("\n") - - with Live(console=self.console, refresh_per_second=30) as live: - for line in lines: - for char in line: - current_code += char - syntax = Syntax( - current_code, language, theme="monokai", line_numbers=True - ) - live.update(syntax) - await asyncio.sleep(speed) - current_code += "\n" - syntax = Syntax( - current_code, language, theme="monokai", line_numbers=True - ) - live.update(syntax) - - -async def stream_with_fallback(message: str, console: Console = None) -> Optional[str]: - """ - Stream response with automatic fallback to available options. - """ - import os - - handler = StreamingHandler(console) - - # Try OpenAI streaming - if os.getenv("OPENAI_API_KEY"): - try: - from openai import AsyncOpenAI - - client = AsyncOpenAI() - return await handler.stream_openai( - client, [{"role": "user", "content": message}] - ) - except Exception as e: - if console: - console.print(f"[yellow]OpenAI streaming failed: {e}[/yellow]") - - # Try Anthropic streaming - if os.getenv("ANTHROPIC_API_KEY"): - try: - from anthropic import AsyncAnthropic - - client = AsyncAnthropic() - return await handler.stream_anthropic( - client, [{"role": "user", "content": message}] - ) - except Exception as e: - if console: - console.print(f"[yellow]Anthropic streaming failed: {e}[/yellow]") - - # Try Ollama streaming - try: - return await handler.stream_ollama(message) - except Exception: - pass - - # Fallback to non-streaming with simulated effect - if console: - console.print("[yellow]Falling back to non-streaming mode[/yellow]") - - # Get response from fallback handler - from .fallback_handler import smart_chat - - response = await smart_chat(message, console) - - if response: - # Simulate streaming - return await handler.simulate_streaming(response) - - return None diff --git a/pkg/hanzo/src/hanzo/tools/__init__.py b/pkg/hanzo/src/hanzo/tools/__init__.py deleted file mode 100644 index f97dcfd42..000000000 --- a/pkg/hanzo/src/hanzo/tools/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""AI tools detection and management.""" - -from .detector import AITool, ToolDetector - -__all__ = ["ToolDetector", "AITool"] diff --git a/pkg/hanzo/src/hanzo/tools/detector.py b/pkg/hanzo/src/hanzo/tools/detector.py deleted file mode 100644 index 0a754008b..000000000 --- a/pkg/hanzo/src/hanzo/tools/detector.py +++ /dev/null @@ -1,481 +0,0 @@ -"""Detect available AI coding tools and assistants.""" - -import os -import shutil -import subprocess -from typing import Dict, List, Tuple, Optional -from pathlib import Path -from dataclasses import dataclass - -import httpx -from rich import box -from rich.table import Table -from rich.console import Console - -# Import router detection -try: - from hanzo.orchestrator_config import check_local_node, get_default_router_endpoint -except ImportError: - # Fallback if not available - def get_default_router_endpoint(): - return "https://gateway.hanzo.ai" - - def check_local_node(): - return False - - -@dataclass -class AITool: - """Represents an AI coding tool.""" - - name: str - command: str - display_name: str - provider: str - priority: int # Lower is higher priority - check_command: Optional[str] = None - env_var: Optional[str] = None - api_endpoint: Optional[str] = None - detected: bool = False - version: Optional[str] = None - path: Optional[str] = None - - -class ToolDetector: - """Detect and manage available AI coding tools.""" - - # Define available tools with priority order - TOOLS = [ - # Hanzo Local Node - highest priority for privacy and local control - AITool( - name="hanzod", - command="hanzo node", - display_name="Hanzo Node (Local Private AI)", - provider="hanzo-local", - priority=0, # Highest priority - local and private - check_command=None, # Check via API endpoint - api_endpoint="http://localhost:3690/health", - env_var=None, - ), - AITool( - name="hanzo-router", - command="hanzo router", - display_name="Hanzo Router (LLM Proxy)", - provider="hanzo-router", - priority=1, - check_command=None, - api_endpoint="http://localhost:4000/health", - env_var=None, - ), - AITool( - name="claude-code", - command="claude", - display_name="Claude Code", - provider="anthropic", - priority=2, - check_command="claude --version", - env_var="ANTHROPIC_API_KEY", - ), - AITool( - name="hanzo-dev", - command="hanzo dev", - display_name="Hanzo Dev (Native)", - provider="hanzo", - priority=3, - check_command="hanzo --version", - env_var="HANZO_API_KEY", - ), - AITool( - name="openai-codex", - command="openai", - display_name="OpenAI Codex", - provider="openai", - priority=4, - check_command="openai --version", - env_var="OPENAI_API_KEY", - ), - AITool( - name="gemini-cli", - command="gemini", - display_name="Gemini CLI", - provider="google", - priority=5, - check_command="gemini --version", - env_var="GEMINI_API_KEY", - ), - AITool( - name="grok-cli", - command="grok", - display_name="Grok CLI", - provider="xai", - priority=6, - check_command="grok --version", - env_var="GROK_API_KEY", - ), - AITool( - name="openhands", - command="openhands", - display_name="OpenHands CLI", - provider="openhands", - priority=7, - check_command="openhands --version", - env_var=None, - ), - AITool( - name="cursor", - command="cursor", - display_name="Cursor AI", - provider="cursor", - priority=8, - check_command="cursor --version", - env_var=None, - ), - AITool( - name="codeium", - command="codeium", - display_name="Codeium", - provider="codeium", - priority=9, - check_command="codeium --version", - env_var="CODEIUM_API_KEY", - ), - AITool( - name="aider", - command="aider", - display_name="Aider", - provider="aider", - priority=10, - check_command="aider --version", - env_var=None, - ), - AITool( - name="continue", - command="continue", - display_name="Continue Dev", - provider="continue", - priority=11, - check_command="continue --version", - env_var=None, - ), - ] - - def __init__(self, console: Optional[Console] = None): - self.console = console or Console() - self.detected_tools: List[AITool] = [] - - def detect_all(self) -> List[AITool]: - """Detect all available AI tools.""" - self.detected_tools = [] - - for tool in self.TOOLS: - if self.detect_tool(tool): - self.detected_tools.append(tool) - - # Sort by priority - self.detected_tools.sort(key=lambda t: t.priority) - return self.detected_tools - - def detect_tool(self, tool: AITool) -> bool: - """Detect if a specific tool is available.""" - # Check API endpoint first (for services like hanzod) - if tool.api_endpoint: - # For Hanzo Router, only detect if local node is actually running - if tool.name == "hanzo-router": - if check_local_node(): - try: - response = httpx.get(tool.api_endpoint, timeout=0.5) - if response.status_code == 200: - tool.detected = True - tool.version = "Local Node" - return True - except Exception: - pass - # If no local node, router is not available (will use gateway instead) - return False - - # For Hanzo Node, directly test the chat endpoint since health may lie - if tool.name == "hanzod": - try: - # Try both ports in case configuration varies - for port in [3690, 8000]: - try: - # Check if the chat completions endpoint actually works - test_response = httpx.post( - f"http://localhost:{port}/v1/chat/completions", - json={ - "messages": [{"role": "user", "content": "test"}], - "model": "default", - "max_tokens": 1, - }, - timeout=1.0, - ) - # Only accept if we get a proper response (not 404, not connection error) - if test_response.status_code in [ - 200, - 400, - 422, - ]: # 400/422 means endpoint exists but params wrong - tool.detected = True - tool.version = f"Running (Port {port})" - tool.api_endpoint = ( - f"http://localhost:{port}/health" # Update port - ) - - # Try to get model info - try: - models_response = httpx.get( - f"http://localhost:{port}/v1/models", - timeout=0.5, - ) - if models_response.status_code == 200: - models = models_response.json().get("data", []) - if models: - tool.version = f"Running ({len(models)} models, Port {port})" - except Exception: - pass - - return True - except (httpx.ConnectError, httpx.TimeoutException): - # Connection refused or timeout - node not available on this port - continue - except Exception: - pass - # If we get here, Hanzo Node is not properly available - return False - else: - # For other services, check health endpoint - try: - response = httpx.get(tool.api_endpoint, timeout=1.0) - if response.status_code == 200: - tool.detected = True - tool.version = "Running" - return True - except Exception: - pass - - # Check if command exists - if tool.command: - tool.path = shutil.which(tool.command.split()[0]) - if tool.path: - tool.detected = True - - # Try to get version - if tool.check_command: - try: - result = subprocess.run( - tool.check_command.split(), - capture_output=True, - text=True, - timeout=2, - ) - if result.returncode == 0: - tool.version = result.stdout.strip().split()[-1] - except Exception: - pass - - return True - - # Check environment variable as fallback - if tool.env_var and os.getenv(tool.env_var): - tool.detected = True - return True - - return False - - def get_default_tool(self) -> Optional[AITool]: - """Get the default tool based on priority and availability.""" - if not self.detected_tools: - self.detect_all() - - if self.detected_tools: - return self.detected_tools[0] - return None - - def get_tool_by_name(self, name: str) -> Optional[AITool]: - """Get a specific tool by name.""" - for tool in self.TOOLS: - if tool.name == name or tool.display_name.lower() == name.lower(): - if self.detect_tool(tool): - return tool - return None - - def show_available_tools(self): - """Display available tools in a table.""" - self.detect_all() - - table = Table(title="Available AI Coding Tools", box=box.ROUNDED) - table.add_column("#", style="dim") - table.add_column("Tool", style="cyan") - table.add_column("Provider", style="yellow") - table.add_column("Status", style="green") - table.add_column("Version", style="blue") - table.add_column("Priority", style="magenta") - - for i, tool in enumerate(self.TOOLS, 1): - status = "โœ… Available" if tool.detected else "โŒ Not Found" - version = tool.version or "Unknown" if tool.detected else "-" - - # Highlight the default tool - if ( - tool.detected and tool == self.detected_tools[0] - if self.detected_tools - else False - ): - table.add_row( - str(i), - f"[bold green]โ†’ {tool.display_name}[/bold green]", - tool.provider, - status, - version, - str(tool.priority), - ) - else: - table.add_row( - str(i), - tool.display_name, - tool.provider, - status, - version, - str(tool.priority), - ) - - self.console.print(table) - - if self.detected_tools: - default = self.detected_tools[0] - self.console.print(f"\n[green]Default tool: {default.display_name}[/green]") - - # Special message for Hanzo Node - if default.name == "hanzod": - self.console.print( - "[cyan]๐Ÿ”’ Using local private AI - your data stays on your machine[/cyan]" - ) - self.console.print("[dim]Manage models with: hanzo node models[/dim]") - else: - self.console.print("\n[yellow]No AI coding tools detected.[/yellow]") - self.console.print( - "[dim]Start Hanzo Node for local AI: hanzo node start[/dim]" - ) - self.console.print("[dim]Or install Claude Code, OpenAI CLI, etc.[/dim]") - - def get_tool_command(self, tool: AITool, prompt: str) -> List[str]: - """Get the command to execute for a tool with a prompt.""" - if tool.name == "hanzod": - # Use the local Hanzo node API - return ["hanzo", "ask", "--local", prompt] - elif tool.name == "hanzo-router": - # Use the router proxy - return ["hanzo", "ask", "--router", prompt] - elif tool.name == "claude-code": - return ["claude", prompt] - elif tool.name == "hanzo-dev": - return ["hanzo", "dev", "--prompt", prompt] - elif tool.name == "openai-codex": - return [ - "openai", - "api", - "completions.create", - "-m", - "code-davinci-002", - "-p", - prompt, - ] - elif tool.name == "gemini-cli": - return ["gemini", "generate", "--prompt", prompt] - elif tool.name == "grok-cli": - return ["grok", "complete", prompt] - elif tool.name == "openhands": - return ["openhands", "run", prompt] - elif tool.name == "cursor": - return ["cursor", "--prompt", prompt] - elif tool.name == "aider": - return ["aider", "--message", prompt] - else: - return [tool.command, prompt] - - def execute_with_tool(self, tool: AITool, prompt: str) -> Tuple[bool, str]: - """Execute a prompt with a specific tool.""" - try: - # Special handling for Hanzo services - if tool.name == "hanzod": - # Use the local API directly - extract port from the detected endpoint - try: - # Extract port from api_endpoint (e.g., "http://localhost:3690/health") - import re - - port_match = re.search(r":(\d+)", tool.api_endpoint) - port = port_match.group(1) if port_match else "3690" - - response = httpx.post( - f"http://localhost:{port}/v1/chat/completions", - json={ - "messages": [{"role": "user", "content": prompt}], - "model": "default", # Use default model - "stream": False, - }, - timeout=30.0, - ) - if response.status_code == 200: - result = response.json() - return True, result.get("choices", [{}])[0].get( - "message", {} - ).get("content", "") - else: - return ( - False, - f"Hanzo Node returned {response.status_code}: {response.text}", - ) - except Exception as e: - return False, f"Hanzo Node error: {e}" - - elif tool.name == "hanzo-router": - # Use the router API (local or gateway) - try: - endpoint = get_default_router_endpoint() - response = httpx.post( - f"{endpoint}/v1/chat/completions", - json={ - "messages": [{"role": "user", "content": prompt}], - "model": "gpt-4o-mini", # Use free model by default - "stream": False, - }, - timeout=30.0, - ) - if response.status_code == 200: - result = response.json() - return True, result.get("choices", [{}])[0].get( - "message", {} - ).get("content", "") - except Exception as e: - return False, f"Router error: {e}" - - # Default command execution - command = self.get_tool_command(tool, prompt) - result = subprocess.run(command, capture_output=True, text=True, timeout=30) - - if result.returncode == 0: - return True, result.stdout - else: - return False, result.stderr or "Command failed" - except subprocess.TimeoutExpired: - return False, "Command timed out" - except Exception as e: - return False, str(e) - - def execute_with_fallback(self, prompt: str) -> Tuple[bool, str, AITool]: - """Execute with fallback through available tools.""" - if not self.detected_tools: - self.detect_all() - - for tool in self.detected_tools: - self.console.print(f"[dim]Trying {tool.display_name}...[/dim]") - success, output = self.execute_with_tool(tool, prompt) - - if success: - return True, output, tool - else: - self.console.print( - f"[yellow]{tool.display_name} failed: {output}[/yellow]" - ) - - return False, "No available tools could handle the request", None diff --git a/pkg/hanzo/src/hanzo/ui/__init__.py b/pkg/hanzo/src/hanzo/ui/__init__.py deleted file mode 100644 index 4ca007025..000000000 --- a/pkg/hanzo/src/hanzo/ui/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -Hanzo UI components for CLI. -""" - -from .startup import StartupUI, show_startup -from .inline_startup import show_status, show_inline_startup - -__all__ = ["show_startup", "StartupUI", "show_inline_startup", "show_status"] diff --git a/pkg/hanzo/src/hanzo/ui/inline_startup.py b/pkg/hanzo/src/hanzo/ui/inline_startup.py deleted file mode 100644 index e5c52baad..000000000 --- a/pkg/hanzo/src/hanzo/ui/inline_startup.py +++ /dev/null @@ -1,134 +0,0 @@ -""" -Inline startup notifications for Hanzo commands. -""" - -import os -import json -from typing import Optional -from pathlib import Path -from datetime import datetime, timedelta - -from rich import box -from rich.text import Text -from rich.panel import Panel -from rich.console import Console - -console = Console() - - -class InlineStartup: - """Lightweight inline startup notifications.""" - - def __init__(self): - self.config_dir = Path.home() / ".hanzo" - self.last_shown_file = self.config_dir / ".last_inline_shown" - self.show_interval = timedelta(hours=24) # Show once per day - - def should_show(self) -> bool: - """Check if we should show inline startup.""" - # Check environment variable - if os.environ.get("HANZO_NO_STARTUP") == "1": - return False - - # Check last shown time - if self.last_shown_file.exists(): - try: - last_shown = datetime.fromisoformat( - self.last_shown_file.read_text().strip() - ) - if datetime.now() - last_shown < self.show_interval: - return False - except Exception: - pass - - return True - - def mark_shown(self): - """Mark inline startup as shown.""" - self.config_dir.mkdir(exist_ok=True) - self.last_shown_file.write_text(datetime.now().isoformat()) - - def show_mini(self, command: str = None): - """Show mini inline startup.""" - if not self.should_show(): - return - - # Build message - message = Text() - message.append("โœจ ", style="yellow") - message.append("Hanzo AI ", style="bold cyan") - message.append("v0.3.23", style="green") - - # Add what's new teaser - message.append(" โ€ข ", style="dim") - message.append("What's new: ", style="dim") - message.append("Router management, improved docs", style="yellow dim") - - # Show panel - console.print( - Panel(message, box=box.MINIMAL, border_style="cyan", padding=(0, 1)) - ) - - self.mark_shown() - - def show_command_hint(self, command: str): - """Show command-specific hints.""" - hints = { - "chat": "๐Ÿ’ก Tip: Use --model to change AI model, --router for local proxy", - "node": "๐Ÿ’ก Tip: Run 'hanzo node start' to enable local AI inference", - "router": "๐Ÿ’ก Tip: Router provides unified access to 100+ LLM providers", - "repl": "๐Ÿ’ก Tip: REPL combines Python with AI assistance", - "agent": "๐Ÿ’ก Tip: Agents can work in parallel with 'hanzo agent swarm'", - } - - hint = hints.get(command) - if hint and os.environ.get("HANZO_SHOW_HINTS") != "0": - console.print(f"[dim]{hint}[/dim]") - - def show_status_bar(self): - """Show a compact status bar.""" - items = [] - - # Check router - try: - import httpx - - response = httpx.get("http://localhost:4000/health", timeout=0.5) - if response.status_code == 200: - items.append("[green]Router โœ“[/green]") - except Exception: - pass - - # Check node - try: - import httpx - - response = httpx.get("http://localhost:8000/health", timeout=0.5) - if response.status_code == 200: - items.append("[green]Node โœ“[/green]") - except Exception: - pass - - # Check API key - if os.environ.get("HANZO_API_KEY"): - items.append("[green]API โœ“[/green]") - else: - items.append("[yellow]API โš [/yellow]") - - if items: - status = " โ€ข ".join(items) - console.print(f"[dim]Status: {status}[/dim]") - - -def show_inline_startup(command: str = None): - """Show inline startup notification.""" - startup = InlineStartup() - startup.show_mini(command) - if command: - startup.show_command_hint(command) - - -def show_status(): - """Show compact status bar.""" - startup = InlineStartup() - startup.show_status_bar() diff --git a/pkg/hanzo/src/hanzo/ui/startup.py b/pkg/hanzo/src/hanzo/ui/startup.py deleted file mode 100644 index 713e7529f..000000000 --- a/pkg/hanzo/src/hanzo/ui/startup.py +++ /dev/null @@ -1,443 +0,0 @@ -""" -Hanzo startup UI and changelog integration. -""" - -import os -import json -import time -from typing import Any, Dict, List, Optional -from pathlib import Path -from datetime import datetime, timedelta - -import httpx -from rich import box -from rich.text import Text -from rich.align import Align -from rich.panel import Panel -from rich.table import Table -from rich.columns import Columns -from rich.console import Console -from rich.markdown import Markdown - -console = Console() - - -class StartupUI: - """Clean startup UI for Hanzo with changelog integration.""" - - def __init__(self): - self.config_dir = Path.home() / ".hanzo" - self.config_file = self.config_dir / "config.json" - self.changelog_cache = self.config_dir / "changelog_cache.json" - self.last_shown_file = self.config_dir / ".last_shown_version" - self.current_version = self._get_current_version() - - def _get_current_version(self) -> str: - """Get current Hanzo version.""" - try: - from hanzo import __version__ - - return __version__ - except Exception: - return "0.3.23" - - def _get_last_shown_version(self) -> Optional[str]: - """Get the last version shown to user.""" - if self.last_shown_file.exists(): - return self.last_shown_file.read_text().strip() - return None - - def _save_last_shown_version(self): - """Save current version as last shown.""" - self.config_dir.mkdir(exist_ok=True) - self.last_shown_file.write_text(self.current_version) - - def _fetch_changelog(self) -> List[Dict[str, Any]]: - """Fetch latest changelog from GitHub.""" - try: - # Check cache first - if self.changelog_cache.exists(): - cache_data = json.loads(self.changelog_cache.read_text()) - cache_time = datetime.fromisoformat(cache_data["timestamp"]) - if datetime.now() - cache_time < timedelta(hours=6): - return cache_data["entries"] - - # Fetch from GitHub - response = httpx.get( - "https://api.github.com/repos/hanzoai/python-sdk/releases", - headers={"Accept": "application/vnd.github.v3+json"}, - timeout=5, - ) - - if response.status_code == 200: - releases = response.json()[:5] # Last 5 releases - entries = [] - - for release in releases: - entries.append( - { - "version": release["tag_name"], - "date": release["published_at"][:10], - "highlights": self._parse_highlights(release["body"]), - } - ) - - # Cache the results - cache_data = { - "timestamp": datetime.now().isoformat(), - "entries": entries, - } - self.changelog_cache.write_text(json.dumps(cache_data)) - return entries - - except Exception: - pass - - # Fallback to static changelog - return self._get_static_changelog() - - def _parse_highlights(self, body: str) -> List[str]: - """Parse release highlights from markdown.""" - if not body: - return [] - - highlights = [] - lines = body.split("\n") - - for line in lines: - line = line.strip() - if line.startswith("- ") or line.startswith("* "): - highlight = line[2:].strip() - if len(highlight) > 80: - highlight = highlight[:77] + "..." - highlights.append(highlight) - if len(highlights) >= 3: - break - - return highlights - - def _get_static_changelog(self) -> List[Dict[str, Any]]: - """Get static changelog for offline mode.""" - return [ - { - "version": "v0.3.23", - "date": "2024-09-06", - "highlights": [ - "โœจ Added router management commands for LLM proxy control", - "๐ŸŽฏ Renamed cluster to node for better clarity", - "๐Ÿ“š Comprehensive documentation for all packages", - ], - }, - { - "version": "v0.3.22", - "date": "2024-09-05", - "highlights": [ - "๐Ÿš€ Improved MCP tool performance with batch operations", - "๐Ÿ”ง Fixed file permission handling in Windows", - "๐Ÿ’พ Added memory persistence for conversations", - ], - }, - ] - - def _create_welcome_panel(self) -> Panel: - """Create the welcome panel with branding.""" - # ASCII art logo - logo = """ - โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— - โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘โ•šโ•โ•โ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•”โ•โ•โ•โ–ˆโ–ˆโ•— - โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ - โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ - โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘ โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ• - โ•šโ•โ• โ•šโ•โ•โ•šโ•โ• โ•šโ•โ•โ•šโ•โ• โ•šโ•โ•โ•โ•โ•šโ•โ•โ•โ•โ•โ•โ• โ•šโ•โ•โ•โ•โ•โ• - """ - - # Create welcome text - welcome = Text() - welcome.append("Welcome to ", style="white") - welcome.append("Hanzo AI", style="bold cyan") - welcome.append(" โ€ข ", style="dim") - welcome.append(f"v{self.current_version}", style="green") - - # Add subtitle - subtitle = Text("Your AI Infrastructure Platform", style="italic dim") - - # Combine elements - content = Align.center(Text.from_ansi(logo) + "\n" + welcome + "\n" + subtitle) - - return Panel(content, box=box.DOUBLE, border_style="cyan", padding=(1, 2)) - - def _create_whats_new_panel(self) -> Optional[Panel]: - """Create What's New panel with recent changes.""" - last_shown = self._get_last_shown_version() - - # Only show if there's new content - if last_shown == self.current_version: - return None - - changelog = self._fetch_changelog() - if not changelog: - return None - - # Build content - content = Text() - content.append("๐ŸŽ‰ What's New\n\n", style="bold yellow") - - for entry in changelog[:2]: # Show last 2 versions - content.append(f" {entry['version']}", style="bold cyan") - content.append(f" ({entry['date']})\n", style="dim") - - for highlight in entry["highlights"][:2]: - content.append(f" โ€ข {highlight}\n", style="white") - - content.append("\n") - - return Panel( - content, - title="[yellow]Recent Updates[/yellow]", - box=box.ROUNDED, - border_style="yellow", - padding=(0, 1), - ) - - def _create_quick_start_panel(self) -> Panel: - """Create quick start tips panel.""" - tips = [ - ("chat", "Start interactive AI chat"), - ("node start", "Run local AI node"), - ("router start", "Start LLM proxy"), - ("repl", "Interactive Python + AI"), - ("help", "Show all commands"), - ] - - # Create table - table = Table(show_header=False, box=None, padding=(0, 2)) - table.add_column("Command", style="cyan") - table.add_column("Description", style="dim") - - for cmd, desc in tips: - table.add_row(f"hanzo {cmd}", desc) - - return Panel( - table, - title="[green]Quick Start[/green]", - box=box.ROUNDED, - border_style="green", - padding=(0, 1), - ) - - def _create_status_panel(self) -> Panel: - """Create status panel showing system state.""" - items = [] - - # Check router status - try: - response = httpx.get("http://localhost:4000/health", timeout=1) - router_status = ( - "๐ŸŸข Running" if response.status_code == 200 else "๐Ÿ”ด Offline" - ) - except Exception: - router_status = "โšซ Offline" - - # Check node status - try: - response = httpx.get("http://localhost:8000/health", timeout=1) - node_status = "๐ŸŸข Running" if response.status_code == 200 else "๐Ÿ”ด Offline" - except Exception: - node_status = "โšซ Offline" - - # Check API key - api_key = os.getenv("HANZO_API_KEY") - api_status = "๐ŸŸข Configured" if api_key else "๐ŸŸก Not Set" - - # Build status text - status = Text() - status.append("Router: ", style="bold") - status.append(f"{router_status} ", style="white") - status.append("Node: ", style="bold") - status.append(f"{node_status} ", style="white") - status.append("API: ", style="bold") - status.append(api_status, style="white") - - return Panel( - Align.center(status), box=box.ROUNDED, border_style="blue", padding=(0, 1) - ) - - def _create_qr_panel(self) -> Panel: - """Create compact QR code panel for device connection.""" - try: - import socket - - import qrcode - - # Get local IP - local_ip = "localhost" - try: - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - s.connect(("8.8.8.8", 80)) - local_ip = s.getsockname()[0] - s.close() - except Exception: - try: - hostname = socket.gethostname() - local_ip = socket.gethostbyname(hostname) - except Exception: - local_ip = "127.0.0.1" - - # Try to get device capabilities - try: - from hanzo_network.topology.device_capabilities import ( - device_capabilities, - ) - - caps = device_capabilities() - - # Generate connection data with GPU info - connection_data = json.dumps( - { - "id": f"{caps.model.lower().replace(' ', '-')}", - "host": local_ip, - "gpu": caps.chip, - "tflops": caps.flops.fp32, - } - ) - gpu_info = caps.chip[:20] - except Exception: - # Fallback - basic connection info - connection_data = json.dumps( - {"id": socket.gethostname(), "host": local_ip, "type": "hanzo-node"} - ) - gpu_info = "CPU" - - # Generate smallest possible QR code - qr = qrcode.QRCode(version=1, box_size=1, border=0) - qr.add_data(connection_data) - qr.make(fit=True) - - # Get compact ASCII representation using half blocks for smaller size - qr_text = qr.get_matrix() - qr_lines = [] - for i in range(0, len(qr_text), 2): - line = "" - for j in range(len(qr_text[i])): - top = qr_text[i][j] if i < len(qr_text) else False - bottom = qr_text[i + 1][j] if i + 1 < len(qr_text) else False - if top and bottom: - line += "โ–ˆ" - elif top: - line += "โ–€" - elif bottom: - line += "โ–„" - else: - line += " " - qr_lines.append(line) - qr_str = "\n".join(qr_lines) - - # Build compact content - content = Text() - content.append(qr_str + "\n", style="white") - content.append(f"{local_ip} โ€ข {gpu_info}", style="dim cyan") - - return Panel( - content, - title="[cyan]๐Ÿ“ฑ Scan to Join[/cyan]", - box=box.ROUNDED, - border_style="cyan", - padding=(0, 1), - ) - - except Exception: - # Absolute fallback - show IP only - content = Text() - content.append("QR unavailable\n", style="dim yellow") - content.append(f"{local_ip}", style="cyan") - - return Panel( - content, - title="[cyan]๐Ÿ“ฑ Device Info[/cyan]", - box=box.ROUNDED, - border_style="cyan", - padding=(0, 1), - ) - - def _check_for_updates(self) -> Optional[str]: - """Check if updates are available.""" - try: - response = httpx.get("https://pypi.org/pypi/hanzo/json", timeout=3) - if response.status_code == 200: - data = response.json() - latest = data["info"]["version"] - if latest != self.current_version: - return latest - except Exception: - pass - return None - - def show(self, minimal: bool = False): - """Display the startup UI.""" - console.clear() - - if minimal: - # Minimal mode - just show compact welcome - console.print( - Panel( - f"[bold cyan]Hanzo AI[/bold cyan] โ€ข v{self.current_version} โ€ข [dim]Type [cyan]hanzo help[/cyan] for commands[/dim]", - box=box.ROUNDED, - padding=(0, 1), - ) - ) - return - - # Full startup UI - panels = [] - - # Welcome panel - welcome = self._create_welcome_panel() - console.print(welcome) - - # What's New (if applicable) - whats_new = self._create_whats_new_panel() - if whats_new: - console.print(whats_new) - self._save_last_shown_version() - - # Quick start on left, status and QR on right - quick_start = self._create_quick_start_panel() - status = self._create_status_panel() - qr_panel = self._create_qr_panel() - - console.print(Columns([quick_start, status], equal=True, expand=True)) - console.print() - console.print(qr_panel) - - # Check for updates - latest = self._check_for_updates() - if latest: - console.print( - Panel( - f"[yellow]๐Ÿ“ฆ Update available:[/yellow] v{latest} โ†’ Run [cyan]pip install --upgrade hanzo[/cyan]", - box=box.ROUNDED, - border_style="yellow", - padding=(0, 1), - ) - ) - - # Footer - console.print( - Align.center( - Text("Get started with ", style="dim") - + Text("hanzo chat", style="bold cyan") - + Text(" or view docs at ", style="dim") - + Text("docs.hanzo.ai", style="blue underline") - ) - ) - console.print() - - -def show_startup(minimal: bool = False): - """Show the startup UI.""" - ui = StartupUI() - ui.show(minimal=minimal) - - -if __name__ == "__main__": - show_startup() diff --git a/pkg/hanzo/src/hanzo/utils/__init__.py b/pkg/hanzo/src/hanzo/utils/__init__.py deleted file mode 100644 index 24bd76b81..000000000 --- a/pkg/hanzo/src/hanzo/utils/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Utility modules for Hanzo CLI.""" - -__all__ = ["config", "output"] diff --git a/pkg/hanzo/src/hanzo/utils/api_client.py b/pkg/hanzo/src/hanzo/utils/api_client.py deleted file mode 100644 index fc988aac9..000000000 --- a/pkg/hanzo/src/hanzo/utils/api_client.py +++ /dev/null @@ -1,370 +0,0 @@ -"""PaaS API client for Hanzo CLI. - -Shared HTTP client used by k8s, run, and git commands to talk to -the PaaS platform API (platform.hanzo.ai). -""" - -import os -import json -import time -from typing import Optional -from pathlib import Path - -import httpx - -from .output import console - -PLATFORM_API_URL = os.getenv( - "PLATFORM_API_URL", - os.getenv("HANZO_PLATFORM_URL", "https://platform.hanzo.ai"), -) - -CONTEXT_FILE = Path.home() / ".hanzo" / "context.json" -AUTH_FILE = Path.home() / ".hanzo" / "auth.json" -SESSION_FILE = Path.home() / ".hanzo" / "paas_session.json" - - -# --------------------------------------------------------------------------- -# Auth helpers -# --------------------------------------------------------------------------- - - -def get_iam_token() -> Optional[str]: - """Get IAM bearer token from env or ~/.hanzo/auth.json.""" - token = os.getenv("HANZO_TOKEN") or os.getenv("HANZO_API_KEY") - if token: - return token - - if AUTH_FILE.exists(): - try: - auth = json.loads(AUTH_FILE.read_text()) - # CLI login stores flat "token", worker/IAM stores nested "tokens.access_token" - return ( - auth.get("token") - or auth.get("api_key") - or auth.get("tokens", {}).get("access_token") - ) - except Exception: - pass - return None - - -# Keep old name as alias -get_token = get_iam_token - - -def _load_session() -> dict: - """Load cached PaaS session tokens.""" - if SESSION_FILE.exists(): - try: - return json.loads(SESSION_FILE.read_text()) - except Exception: - pass - return {} - - -def _save_session(session: dict): - """Cache PaaS session tokens.""" - SESSION_FILE.parent.mkdir(parents=True, exist_ok=True) - SESSION_FILE.write_text(json.dumps(session, indent=2)) - - -def _exchange_iam_for_session(iam_token: str, timeout: int = 15) -> Optional[dict]: - """Exchange IAM token for PaaS session via POST /v1/auth/login.""" - url = f"{PLATFORM_API_URL}/v1/auth/login" - payload = {"provider": "hanzo", "accessToken": iam_token} - - try: - with httpx.Client(timeout=timeout) as client: - resp = client.post(url, json=payload) - except Exception as e: - console.print(f"[red]Failed to reach PaaS login: {e}[/red]") - return None - - if resp.status_code >= 400: - try: - body = resp.json() - msg = body.get("message") or body.get("error") or resp.text - except Exception: - msg = resp.text - console.print(f"[red]PaaS login failed ({resp.status_code}): {msg}[/red]") - return None - - data = resp.json() - at = data.get("at", "") - rt = data.get("rt", "") - if not at: - console.print("[red]PaaS login returned no session token.[/red]") - return None - - session = { - "access_token": at, - "refresh_token": rt, - "created_at": time.time(), - "platform_url": PLATFORM_API_URL, - } - _save_session(session) - return session - - -def _get_session_token(timeout: int = 15) -> Optional[str]: - """Get a valid PaaS session token, exchanging IAM token if needed.""" - # Check env for direct PaaS token override - paas_token = os.getenv("HANZO_PAAS_TOKEN") - if paas_token: - return paas_token - - # Check cached session (session tokens are short-lived: 5 min access, 4 hr refresh) - session = _load_session() - if session.get("access_token") and session.get("platform_url") == PLATFORM_API_URL: - age = time.time() - session.get("created_at", 0) - if age < 240: # Use cached if < 4 min old (access token lasts 5 min) - return session["access_token"] - - # Need fresh session โ€” get IAM token first - iam_token = get_iam_token() - if not iam_token: - return None - - session = _exchange_iam_for_session(iam_token, timeout=timeout) - if session: - return session["access_token"] - return None - - -def _require_token() -> str: - """Return PaaS session token or print error and raise SystemExit.""" - token = _get_session_token() - if not token: - console.print("[red]Not authenticated. Run 'hanzo auth login' first.[/red]") - raise SystemExit(1) - return token - - -# --------------------------------------------------------------------------- -# Context helpers (org / project / env selection) -# --------------------------------------------------------------------------- - - -def load_context() -> dict: - """Load active org/project/env context from ~/.hanzo/context.json.""" - if CONTEXT_FILE.exists(): - try: - return json.loads(CONTEXT_FILE.read_text()) - except Exception: - pass - return {} - - -def save_context(ctx: dict): - """Persist active context.""" - CONTEXT_FILE.parent.mkdir(parents=True, exist_ok=True) - CONTEXT_FILE.write_text(json.dumps(ctx, indent=2)) - - -def require_context(fields: tuple = ("org_id", "project_id", "env_id")) -> dict: - """Return context dict or error if required fields are missing.""" - ctx = load_context() - missing = [f for f in fields if not ctx.get(f)] - if missing: - console.print( - f"[red]Missing context: {', '.join(missing)}[/red]\n" - "Run 'hanzo auth context set --org ORG --project PROJECT --env ENV' first." - ) - raise SystemExit(1) - return ctx - - -# --------------------------------------------------------------------------- -# URL builders -# --------------------------------------------------------------------------- - - -def container_url( - org_id: str, - project_id: str, - env_id: str, - container_id: Optional[str] = None, -) -> str: - base = f"{PLATFORM_API_URL}/v1/org/{org_id}/project/{project_id}/env/{env_id}/container" - if container_id: - return f"{base}/{container_id}" - return base - - -def org_url(org_id: Optional[str] = None) -> str: - if org_id: - return f"{PLATFORM_API_URL}/v1/org/{org_id}" - return f"{PLATFORM_API_URL}/v1/org" - - -def project_url(org_id: str, project_id: Optional[str] = None) -> str: - base = f"{PLATFORM_API_URL}/v1/org/{org_id}/project" - if project_id: - return f"{base}/{project_id}" - return base - - -def env_url(org_id: str, project_id: str, env_id: Optional[str] = None) -> str: - base = f"{PLATFORM_API_URL}/v1/org/{org_id}/project/{project_id}/env" - if env_id: - return f"{base}/{env_id}" - return base - - -def cluster_url(cluster_name: Optional[str] = None) -> str: - base = f"{PLATFORM_API_URL}/v1/cluster" - if cluster_name: - return f"{base}/{cluster_name}" - return base - - -def git_url(provider_id: Optional[str] = None) -> str: - base = f"{PLATFORM_API_URL}/v1/user/git" - if provider_id: - return f"{base}/{provider_id}" - return base - - -# --------------------------------------------------------------------------- -# Response helpers -# --------------------------------------------------------------------------- - - -def extract_list(data, key: str) -> list: - """Extract list from API response (handles both direct lists and wrapped objects).""" - if isinstance(data, list): - return data - if isinstance(data, dict): - return data.get(key, data.get("data", [])) - return [] - - -def find_container(client, base_url: str, name: str): - """Find a container by name in the current environment. Returns (data, id).""" - data = client.get(base_url) - if data is None: - return None, None - for c in extract_list(data, "containers"): - cname = c.get("name", c.get("iid", "")) - cid = c.get("_id") or c.get("iid") or c.get("id", "") - if cname == name or cid == name: - return c, cid - return None, None - - -# --------------------------------------------------------------------------- -# PaaS HTTP client -# --------------------------------------------------------------------------- - - -class PaaSClient: - """HTTP client for PaaS API with auto session management.""" - - def __init__(self, timeout: int = 30): - self.token = _require_token() - self.timeout = timeout - self._refresh_token = _load_session().get("refresh_token", "") - - @property - def headers(self) -> dict: - h = {"Authorization": f"Bearer {self.token}"} - if self._refresh_token: - h["Refresh-Token"] = self._refresh_token - return h - - def get(self, url: str, **kwargs) -> Optional[dict]: - return self._request("GET", url, **kwargs) - - def post( - self, url: str, payload: Optional[dict] = None, **kwargs - ) -> Optional[dict]: - return self._request("POST", url, json=payload, **kwargs) - - def put(self, url: str, payload: Optional[dict] = None, **kwargs) -> Optional[dict]: - return self._request("PUT", url, json=payload, **kwargs) - - def delete(self, url: str, **kwargs) -> Optional[dict]: - return self._request("DELETE", url, **kwargs) - - def _request(self, method: str, url: str, **kwargs) -> Optional[dict]: - try: - with httpx.Client(timeout=self.timeout) as client: - resp = client.request(method, url, headers=self.headers, **kwargs) - - # If server refreshed our tokens, save them - new_at = resp.headers.get("Access-Token") - new_rt = resp.headers.get("Refresh-Token") - if new_at: - self.token = new_at - if new_rt: - self._refresh_token = new_rt - _save_session( - { - "access_token": new_at, - "refresh_token": self._refresh_token, - "created_at": time.time(), - "platform_url": PLATFORM_API_URL, - } - ) - - # On 401, try re-auth once - if resp.status_code == 401: - new_token = self._reauth() - if new_token: - resp = client.request( - method, url, headers=self.headers, **kwargs - ) - else: - return self._handle_response(resp) - - return self._handle_response(resp) - except httpx.ConnectError: - console.print(f"[red]Could not connect to {PLATFORM_API_URL}[/red]") - return None - except httpx.TimeoutException: - console.print("[red]Request timed out.[/red]") - return None - except Exception as e: - console.print(f"[red]Request error: {e}[/red]") - return None - - def _reauth(self) -> Optional[str]: - """Re-exchange IAM token for a fresh PaaS session.""" - iam_token = get_iam_token() - if not iam_token: - return None - session = _exchange_iam_for_session(iam_token, timeout=self.timeout) - if session: - self.token = session["access_token"] - self._refresh_token = session.get("refresh_token", "") - return self.token - return None - - @staticmethod - def _handle_response(resp: httpx.Response) -> Optional[dict]: - if resp.status_code == 401: - console.print("[red]Authentication failed. Run 'hanzo auth login'.[/red]") - return None - if resp.status_code == 403: - console.print("[red]Permission denied.[/red]") - return None - if resp.status_code == 404: - console.print("[yellow]Resource not found.[/yellow]") - return None - if resp.status_code >= 400: - try: - body = resp.json() - msg = body.get("message") or body.get("error") or resp.text - except Exception: - msg = resp.text - console.print(f"[red]API error ({resp.status_code}): {msg}[/red]") - return None - - if resp.status_code == 204: - return {} - - try: - return resp.json() - except Exception: - return {} diff --git a/pkg/hanzo/src/hanzo/utils/config.py b/pkg/hanzo/src/hanzo/utils/config.py deleted file mode 100644 index 546d51e66..000000000 --- a/pkg/hanzo/src/hanzo/utils/config.py +++ /dev/null @@ -1,172 +0,0 @@ -"""Configuration utilities for Hanzo CLI.""" - -import os -import json -from typing import Any, Dict, Optional -from pathlib import Path - -import yaml - - -def get_config_paths() -> Dict[str, Path]: - """Get configuration file paths.""" - paths = {} - - # System config - if os.name == "nt": # Windows - paths["system"] = ( - Path(os.environ.get("PROGRAMDATA", "C:\\ProgramData")) - / "hanzo" - / "config.yaml" - ) - else: # Unix-like - paths["system"] = Path("/etc/hanzo/config.yaml") - - # Global config (user) - config_home = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) - paths["global"] = config_home / "hanzo" / "config.yaml" - - # Local config (project) - cwd = Path.cwd() - for parent in [cwd] + list(cwd.parents): - config_file = parent / ".hanzo" / "config.yaml" - if config_file.exists(): - paths["local"] = config_file - break - else: - # Default local path even if it doesn't exist - paths["local"] = cwd / ".hanzo" / "config.yaml" - - return paths - - -def load_config(path: Path) -> Dict[str, Any]: - """Load configuration from file.""" - if not path.exists(): - return {} - - try: - with open(path, "r") as f: - if path.suffix == ".json": - return json.load(f) - else: - return yaml.safe_load(f) or {} - except Exception: - return {} - - -def save_config(path: Path, config: Dict[str, Any]): - """Save configuration to file.""" - path.parent.mkdir(parents=True, exist_ok=True) - - with open(path, "w") as f: - if path.suffix == ".json": - json.dump(config, f, indent=2) - else: - yaml.dump(config, f, default_flow_style=False) - - -def get_config_value(key: str, default: Any = None, scope: Optional[str] = None) -> Any: - """Get configuration value from merged configs.""" - paths = get_config_paths() - - # Load configs in priority order (local > global > system) - configs = [] - - if scope == "system" or scope is None: - if paths["system"].exists(): - configs.append(load_config(paths["system"])) - - if scope == "global" or scope is None: - if paths["global"].exists(): - configs.append(load_config(paths["global"])) - - if scope == "local" or scope is None: - if paths.get("local") and paths["local"].exists(): - configs.append(load_config(paths["local"])) - - # Merge configs (later ones override earlier) - merged = {} - for config in configs: - merged.update(config) - - # Get nested key - keys = key.split(".") - current = merged - - try: - for k in keys: - current = current[k] - return current - except (KeyError, TypeError): - return default - - -def set_config_value(key: str, value: Any, scope: str = "global"): - """Set configuration value.""" - paths = get_config_paths() - path = paths.get(scope, paths["global"]) - - config = load_config(path) if path.exists() else {} - - # Set nested key - keys = key.split(".") - current = config - - for k in keys[:-1]: - if k not in current: - current[k] = {} - current = current[k] - - current[keys[-1]] = value - - save_config(path, config) - - -def init_config() -> Dict[str, Path]: - """Initialize configuration structure.""" - paths = get_config_paths() - - # Create global config if it doesn't exist - if not paths["global"].exists(): - default_config = { - "default_model": "llama-3.2-3b", - "default_provider": "local", - "mcp": {"allowed_paths": [str(Path.home())], "enable_all_tools": True}, - "cluster": {"default_name": "hanzo-local", "default_port": 8000}, - } - save_config(paths["global"], default_config) - - return paths - - -def get_default_model() -> str: - """Get default model from config or environment.""" - return os.environ.get("HANZO_DEFAULT_MODEL") or get_config_value( - "default_model", "llama-3.2-3b" - ) - - -def get_api_key(provider: str) -> Optional[str]: - """Get API key for provider.""" - # Check environment first - env_map = { - "openai": "OPENAI_API_KEY", - "anthropic": "ANTHROPIC_API_KEY", - "hanzo": "HANZO_API_KEY", - "groq": "GROQ_API_KEY", - } - - if env_key := env_map.get(provider.lower()): - if key := os.environ.get(env_key): - return key - - # Check config - return get_config_value(f"api_keys.{provider}") - - -def is_local_preferred() -> bool: - """Check if local execution is preferred.""" - return os.environ.get("HANZO_USE_LOCAL", "").lower() == "true" or get_config_value( - "prefer_local", False - ) diff --git a/pkg/hanzo/src/hanzo/utils/net_check.py b/pkg/hanzo/src/hanzo/utils/net_check.py deleted file mode 100644 index d6d2ca2eb..000000000 --- a/pkg/hanzo/src/hanzo/utils/net_check.py +++ /dev/null @@ -1,113 +0,0 @@ -"""Utilities for checking hanzo/net availability and dependencies.""" - -import sys -import subprocess -from typing import Tuple, Optional -from pathlib import Path - - -def check_net_installation() -> Tuple[bool, Optional[str], Optional[str]]: - """Check if hanzo/net is available and properly configured. - - Returns: - Tuple of (is_available, net_path, python_exe) - """ - # First try to import as PyPI package (hanzo-net) - try: - import net - - return True, None, sys.executable - except ImportError: - pass - - # For development: check for hanzo/net in standard location - net_path = Path.home() / "work" / "hanzo" / "net" - if not net_path.exists(): - net_path = Path("/Users/z/work/hanzo/net") - - if not net_path.exists(): - return False, None, None - - # Check for venv - venv_python = net_path / ".venv" / "bin" / "python" - if venv_python.exists(): - # Check if venv has required packages - result = subprocess.run( - [str(venv_python), "-c", "import net, scapy, mlx, transformers"], - capture_output=True, - text=True, - ) - if result.returncode == 0: - return True, str(net_path), str(venv_python) - else: - # Venv exists but missing dependencies - return False, str(net_path), str(venv_python) - - # No venv, check system Python - result = subprocess.run( - [sys.executable, "-c", "import scapy"], capture_output=True, text=True - ) - - if result.returncode == 0: - return True, str(net_path), sys.executable - else: - return False, str(net_path), None - - -def install_net_dependencies(net_path: str, python_exe: str = None) -> bool: - """Install hanzo/net dependencies. - - Args: - net_path: Path to hanzo/net directory - python_exe: Python executable to use (optional) - - Returns: - True if installation successful - """ - if python_exe is None: - python_exe = sys.executable - - # Install dependencies - result = subprocess.run( - [python_exe, "-m", "pip", "install", "-e", net_path], - capture_output=True, - text=True, - ) - - return result.returncode == 0 - - -def get_missing_dependencies(python_exe: str = None) -> list: - """Check which dependencies are missing for hanzo/net. - - Args: - python_exe: Python executable to check (default: sys.executable) - - Returns: - List of missing package names - """ - if python_exe is None: - python_exe = sys.executable - - required_packages = [ - "scapy", - "mlx", - "mlx_lm", - "transformers", - "tinygrad", - "aiohttp", - "grpcio", - "pydantic", - "rich", - "tqdm", - ] - - missing = [] - for package in required_packages: - result = subprocess.run( - [python_exe, "-c", f"import {package}"], capture_output=True, text=True - ) - if result.returncode != 0: - missing.append(package) - - return missing diff --git a/pkg/hanzo/src/hanzo/utils/output.py b/pkg/hanzo/src/hanzo/utils/output.py deleted file mode 100644 index 0a1c107ae..000000000 --- a/pkg/hanzo/src/hanzo/utils/output.py +++ /dev/null @@ -1,108 +0,0 @@ -"""Output utilities for Hanzo CLI.""" - -from typing import Any, Callable -from functools import wraps - -from rich.theme import Theme -from rich.console import Console - -# Custom theme -hanzo_theme = Theme( - { - "info": "cyan", - "warning": "yellow", - "error": "red", - "success": "green", - "dim": "dim white", - "highlight": "bold cyan", - } -) - -# Global console instance -console = Console(theme=hanzo_theme) - - -def handle_errors(func: Callable) -> Callable: - """Decorator to handle errors in CLI commands.""" - - @wraps(func) - async def async_wrapper(*args, **kwargs): - try: - return await func(*args, **kwargs) - except KeyboardInterrupt: - console.print("\n[yellow]Interrupted[/yellow]") - except Exception as e: - console.print(f"[error]Error: {e}[/error]") - if console.is_debug: - console.print_exception() - - @wraps(func) - def sync_wrapper(*args, **kwargs): - try: - return func(*args, **kwargs) - except KeyboardInterrupt: - console.print("\n[yellow]Interrupted[/yellow]") - except Exception as e: - console.print(f"[error]Error: {e}[/error]") - if console.is_debug: - console.print_exception() - - if asyncio.iscoroutinefunction(func): - return async_wrapper - return sync_wrapper - - -def print_json(data: Any, indent: int = 2): - """Print JSON data with syntax highlighting.""" - console.print_json(data=data, indent=indent) - - -def print_table(data: list[dict], title: str = None): - """Print data as a table.""" - if not data: - console.print("[dim]No data[/dim]") - return - - from rich.table import Table - - table = Table(title=title) - - # Add columns from first row - for key in data[0].keys(): - table.add_column(key.replace("_", " ").title()) - - # Add rows - for row in data: - table.add_row(*[str(v) for v in row.values()]) - - console.print(table) - - -def confirm(message: str, default: bool = False) -> bool: - """Ask for confirmation.""" - from rich.prompt import Confirm - - return Confirm.ask(message, default=default) - - -def prompt(message: str, default: str = None, password: bool = False) -> str: - """Prompt for input.""" - from rich.prompt import Prompt - - return Prompt.ask(message, default=default, password=password) - - -def progress(description: str): - """Context manager for progress indicator.""" - return console.status(description) - - -# Export common methods -print = console.print -print_exception = console.print_exception -rule = console.rule -clear = console.clear - - -# Import asyncio for the decorator -import asyncio diff --git a/pkg/hanzo/test_node.py b/pkg/hanzo/test_node.py deleted file mode 100644 index 232e72758..000000000 --- a/pkg/hanzo/test_node.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python -"""Standalone test for hanzo node command.""" - -import sys - -# Add src to path -sys.path.insert(0, "src") - - -def test_imports(): - """Test that modules can be imported.""" - try: - from hanzo.utils.net_check import ( - check_net_installation, - get_missing_dependencies, - ) - - print("โœ“ Successfully imported net_check utilities") - - # Test the check function - is_available, net_path, python_exe = check_net_installation() - print(f"โœ“ Net available: {is_available}") - print(f" Net path: {net_path}") - print(f" Python: {python_exe}") - - if python_exe: - missing = get_missing_dependencies(python_exe) - if missing: - print(f" Missing deps: {', '.join(missing[:5])}") - else: - print(" All dependencies installed") - - return True - except Exception as e: - print(f"โœ— Import failed: {e}") - return False - - -def test_cli_import(): - """Test CLI import.""" - try: - print("โœ“ Successfully imported CLI") - return True - except Exception as e: - print(f"โœ— CLI import failed: {e}") - return False - - -if __name__ == "__main__": - print("Testing Hanzo Node Command Integration") - print("=" * 40) - - success = True - success = test_imports() and success - success = test_cli_import() and success - - print("=" * 40) - if success: - print("โœ“ All tests passed!") - sys.exit(0) - else: - print("โœ— Some tests failed") - sys.exit(1) diff --git a/pkg/hanzo/tests/__init__.py b/pkg/hanzo/tests/__init__.py deleted file mode 100644 index 9c8400efb..000000000 --- a/pkg/hanzo/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Hanzo test suite.""" diff --git a/pkg/hanzo/tests/test_batch_orchestrator.py b/pkg/hanzo/tests/test_batch_orchestrator.py deleted file mode 100644 index 57cc2ee99..000000000 --- a/pkg/hanzo/tests/test_batch_orchestrator.py +++ /dev/null @@ -1,418 +0,0 @@ -"""Test suite for batch orchestrator with parallel agent execution.""" - -import os -import shutil -import asyncio -import tempfile -from pathlib import Path -from datetime import datetime -from unittest.mock import Mock, AsyncMock, MagicMock, patch - -import pytest -from hanzo.batch_orchestrator import ( - BatchTask, - BatchConfig, - BatchOrchestrator, - MetaAIOrchestrator, -) - - -class TestBatchConfig: - """Test batch configuration parsing.""" - - def test_default_config(self): - """Test default configuration values.""" - config = BatchConfig() - assert config.batch_size == 5 - assert config.agent_model == "claude-3-5-sonnet-20241022" - assert config.use_worktrees == False - - def test_parse_simple_batch(self): - """Test parsing simple batch command.""" - config = BatchConfig.from_command("batch:10 add copyright to files") - assert config.batch_size == 10 - assert config.agent_model == "claude-3-5-sonnet-20241022" # Defaults to Claude - assert config.operation == "add copyright to files" - - def test_parse_with_agent(self): - """Test parsing with explicit agent.""" - config = BatchConfig.from_command("batch:5 agent:codex fix typing") - assert config.batch_size == 5 - assert config.agent_model == "gpt-4-turbo" - assert config.operation == "fix typing" - - def test_parse_with_worktree(self): - """Test parsing with worktree option.""" - config = BatchConfig.from_command("batch:3 worktree:true agent:gemini add docs") - assert config.batch_size == 3 - assert config.use_worktrees == True - assert config.agent_model == "gemini-1.5-pro" - assert config.operation == "add docs" - - def test_parse_with_files_pattern(self): - """Test parsing with file pattern.""" - config = BatchConfig.from_command("batch:5 files:*.py add type hints") - assert config.batch_size == 5 - assert config.target_pattern == "*.py" - assert config.operation == "add type hints" - - def test_agent_aliases(self): - """Test agent name aliases.""" - # Test Claude Code alias - config = BatchConfig.from_command("batch:5 agent:cc refactor") - assert config.agent_model == "claude-3-5-sonnet-20241022" - - # Test other aliases - config = BatchConfig.from_command("batch:5 agent:llama optimize") - assert config.agent_model == "ollama/llama-3.2-3b" - - config = BatchConfig.from_command("batch:5 agent:deepseek analyze") - assert config.agent_model == "deepseek-coder-v2" - - -class TestBatchTask: - """Test batch task functionality.""" - - def test_task_creation(self): - """Test task creation.""" - task = BatchTask( - id="task_001", - description="Add copyright", - file_path=Path("test.py"), - agent_model="claude-3-5-sonnet-20241022", - ) - assert task.id == "task_001" - assert task.status == "pending" - assert task.result is None - assert task.error is None - - def test_task_duration(self): - """Test task duration calculation.""" - task = BatchTask(id="test", description="test") - assert task.duration() is None - - task.start_time = datetime(2024, 1, 1, 10, 0, 0) - task.end_time = datetime(2024, 1, 1, 10, 0, 5) - assert task.duration() == 5.0 - - -class TestBatchOrchestrator: - """Test batch orchestrator functionality.""" - - @pytest.fixture - def mock_mcp_client(self): - """Create mock MCP client.""" - client = MagicMock() - client.call_tool = AsyncMock() - return client - - @pytest.fixture - def mock_hanzo_client(self): - """Create mock Hanzo client.""" - client = MagicMock() - client.chat.completions.create = AsyncMock() - return client - - @pytest.fixture - def orchestrator(self, mock_mcp_client, mock_hanzo_client): - """Create batch orchestrator instance.""" - return BatchOrchestrator( - mcp_client=mock_mcp_client, - hanzo_client=mock_hanzo_client, - ) - - @pytest.mark.asyncio - async def test_find_target_files_with_mcp(self, orchestrator, mock_mcp_client): - """Test finding files with MCP client.""" - mock_mcp_client.call_tool.return_value = "file1.py\nfile2.py\nfile3.py" - - files = await orchestrator._find_target_files("*.py") - - assert len(files) == 3 - assert Path("file1.py") in files - mock_mcp_client.call_tool.assert_called_once_with("find", {"pattern": "*.py"}) - - @pytest.mark.asyncio - async def test_execute_agent_task_with_mcp(self, orchestrator, mock_mcp_client): - """Test executing task with MCP agent.""" - task = BatchTask( - id="test_001", - description="Add copyright", - file_path=Path("test.py"), - ) - config = BatchConfig(operation="add copyright") - - mock_mcp_client.call_tool.return_value = "Copyright added successfully" - - await orchestrator._execute_agent_task(task, config) - - assert task.status == "completed" - assert task.result == "Copyright added successfully" - assert task.duration() is not None - - mock_mcp_client.call_tool.assert_called_once() - call_args = mock_mcp_client.call_tool.call_args[0] - assert call_args[0] == "agent" - assert "add copyright" in call_args[1]["prompt"] - - @pytest.mark.asyncio - async def test_execute_agent_task_with_hanzo(self, orchestrator, mock_hanzo_client): - """Test executing task with Hanzo client.""" - orchestrator.mcp_client = None # Disable MCP to use Hanzo - - task = BatchTask( - id="test_002", - description="Fix typing", - ) - config = BatchConfig(operation="fix typing", agent_model="gpt-4-turbo") - - # Mock Hanzo response - mock_response = MagicMock() - mock_response.choices = [MagicMock()] - mock_response.choices[0].message.content = "Typing fixed" - mock_hanzo_client.chat.completions.create.return_value = mock_response - - await orchestrator._execute_agent_task(task, config) - - assert task.status == "completed" - assert task.result == "Typing fixed" - - mock_hanzo_client.chat.completions.create.assert_called_once() - call_kwargs = mock_hanzo_client.chat.completions.create.call_args[1] - assert call_kwargs["model"] == "gpt-4-turbo" - - @pytest.mark.asyncio - async def test_execute_batch_simple(self, orchestrator, mock_mcp_client): - """Test executing simple batch operation.""" - # Mock file finding - mock_mcp_client.call_tool.side_effect = [ - "file1.py\nfile2.py", # Find files - "Task 1 complete", # Agent task 1 - "Task 2 complete", # Agent task 2 - ] - - summary = await orchestrator.execute_batch("batch:2 add copyright") - - assert summary["total_tasks"] == 2 - assert summary["completed"] == 2 - assert summary["failed"] == 0 - assert summary["batch_size"] == 2 - assert summary["agent_model"] == "claude-3-5-sonnet-20241022" - - @pytest.mark.asyncio - async def test_parallel_execution(self, orchestrator, mock_mcp_client): - """Test parallel task execution.""" - - # Create delay to test parallelism - async def delayed_response(*args, **kwargs): - await asyncio.sleep(0.1) - return f"Task complete" - - mock_mcp_client.call_tool.side_effect = [ - "file1.py\nfile2.py\nfile3.py\nfile4.py", # Find files - ] + [ - delayed_response - ] * 4 # 4 agent tasks - - import time - - start = time.time() - - summary = await orchestrator.execute_batch("batch:2 process files") - - duration = time.time() - start - - # With batch size 2, should take ~0.2s (2 batches of 0.1s each) - # Not 0.4s if sequential - assert duration < 0.3 - assert summary["completed"] == 4 - assert summary["batch_size"] == 2 - - @pytest.mark.asyncio - async def test_task_failure_handling(self, orchestrator, mock_mcp_client): - """Test handling of failed tasks.""" - mock_mcp_client.call_tool.side_effect = [ - "file1.py\nfile2.py", # Find files - "Success", # Task 1 succeeds - Exception("API error"), # Task 2 fails - ] - - summary = await orchestrator.execute_batch("batch:2 process") - - assert summary["completed"] == 1 - assert summary["failed"] == 1 - assert len(orchestrator.completed_tasks) == 1 - assert len(orchestrator.failed_tasks) == 1 - assert orchestrator.failed_tasks[0].error == "API error" - - -class TestGitWorktreeIntegration: - """Test git worktree integration.""" - - @pytest.fixture - def temp_git_repo(self): - """Create temporary git repository.""" - temp_dir = tempfile.mkdtemp() - os.chdir(temp_dir) - - # Initialize git repo - import subprocess - - subprocess.run(["git", "init"], capture_output=True) - subprocess.run( - ["git", "config", "user.email", "test@example.com"], capture_output=True - ) - subprocess.run(["git", "config", "user.name", "Test User"], capture_output=True) - - # Create initial commit - Path("test.txt").write_text("initial content") - subprocess.run(["git", "add", "."], capture_output=True) - subprocess.run(["git", "commit", "-m", "Initial commit"], capture_output=True) - - yield temp_dir - - # Cleanup - os.chdir("/") - shutil.rmtree(temp_dir, ignore_errors=True) - - @pytest.mark.asyncio - async def test_worktree_setup(self, temp_git_repo): - """Test worktree setup and cleanup.""" - orchestrator = BatchOrchestrator() - config = BatchConfig(use_worktrees=True) - - # Setup worktree - worktree_path = await orchestrator._setup_worktree("test_task", config) - - assert worktree_path is not None - assert worktree_path.exists() - assert (worktree_path / "test.txt").exists() - - # Cleanup worktree - await orchestrator._cleanup_worktree("test_task") - - assert not worktree_path.exists() - assert "test_task" not in orchestrator._worktrees - - @pytest.mark.asyncio - async def test_worktree_changes_merge(self, temp_git_repo): - """Test merging changes from worktree.""" - orchestrator = BatchOrchestrator() - config = BatchConfig(use_worktrees=True) - - # Setup worktree - worktree_path = await orchestrator._setup_worktree("test_task", config) - - # Make changes in worktree - (worktree_path / "new_file.txt").write_text("new content") - - # Merge changes - await orchestrator._merge_worktree_changes("test_task", worktree_path) - - # Cleanup - await orchestrator._cleanup_worktree("test_task") - - # Check changes are in main branch - assert Path("new_file.txt").exists() - assert Path("new_file.txt").read_text() == "new content" - - -class TestMetaAIOrchestrator: - """Test meta AI orchestrator.""" - - @pytest.fixture - def meta_orchestrator(self): - """Create meta AI orchestrator.""" - mock_mcp = MagicMock() - mock_mcp.call_tool = AsyncMock() - return MetaAIOrchestrator(mcp_client=mock_mcp) - - @pytest.mark.asyncio - async def test_parse_batch_command(self, meta_orchestrator): - """Test parsing batch commands.""" - # Mock file finding to avoid filesystem scan - meta_orchestrator.mcp_client.call_tool.return_value = "file1.py\nfile2.py" - - result = await meta_orchestrator.parse_and_execute( - "batch:5 files:*.py add copyright" - ) - - # Should recognize as batch command - assert "total_tasks" in result or "error" not in result - - @pytest.mark.asyncio - async def test_natural_language_to_batch(self, meta_orchestrator): - """Test converting natural language to batch.""" - # Mock intent analysis - meta_orchestrator.mcp_client.call_tool.return_value = """ - { - "type": "batch_operation", - "operation": "add copyright headers", - "model": "claude-3-5-sonnet-20241022", - "pattern": "*.py", - "batch_size": 10 - } - """ - - intent = await meta_orchestrator._analyze_intent( - "Add copyright headers to all Python files" - ) - - assert intent["type"] == "batch_operation" - - # Build batch command - batch_cmd = meta_orchestrator._build_batch_command(intent) - assert "batch:10" in batch_cmd - assert "claude" in batch_cmd - - @pytest.mark.asyncio - async def test_single_task_execution(self, meta_orchestrator): - """Test single task execution.""" - meta_orchestrator.mcp_client.call_tool.return_value = """ - { - "type": "single_task", - "operation": "explain this code", - "model": "claude-3-5-sonnet-20241022" - } - """ - - result = await meta_orchestrator.parse_and_execute("explain this code") - - # Should execute as single task - assert "task_id" in result or "error" not in result - - -@pytest.mark.integration -class TestBatchOrchestratorIntegration: - """Integration tests for batch orchestrator.""" - - @pytest.mark.asyncio - async def test_real_batch_execution(self): - """Test real batch execution with files.""" - # This test requires actual MCP client - pytest.skip("Requires MCP server running") - - from hanzo_mcp import MCPClient - - client = MCPClient() - orchestrator = BatchOrchestrator(mcp_client=client) - - # Create test files - test_dir = Path("test_batch") - test_dir.mkdir(exist_ok=True) - - for i in range(3): - (test_dir / f"file{i}.txt").write_text(f"Content {i}") - - try: - # Execute batch - summary = await orchestrator.execute_batch( - "batch:3 files:test_batch/*.txt add header" - ) - - assert summary["completed"] == 3 - assert summary["failed"] == 0 - - finally: - # Cleanup - shutil.rmtree(test_dir, ignore_errors=True) diff --git a/pkg/hanzo/tests/test_hanzo.py b/pkg/hanzo/tests/test_hanzo.py deleted file mode 100644 index 848aafc9a..000000000 --- a/pkg/hanzo/tests/test_hanzo.py +++ /dev/null @@ -1,80 +0,0 @@ -"""Basic tests for hanzo package.""" - -import os -import sys - -# Add src to path for testing -sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) - - -def test_import(): - """Test that hanzo package can be imported.""" - import hanzo - - assert hanzo is not None - - -def test_cli_exists(): - """Test that CLI module exists.""" - import hanzo.cli - - assert hanzo.cli is not None - - -def test_dev_module(): - """Test that dev module exists and has key functions.""" - from hanzo.dev import HanzoDevOrchestrator, run_dev_orchestrator - - assert run_dev_orchestrator is not None - assert HanzoDevOrchestrator is not None - - -def test_orchestrator_config(): - """Test orchestrator configuration module.""" - from hanzo.orchestrator_config import OrchestratorMode, get_orchestrator_config - - # Test getting a predefined config - config = get_orchestrator_config("gpt-4") - assert config is not None - assert config.primary_model == "gpt-4" - - # Test router mode - config = get_orchestrator_config("router:gpt-5") - assert config.mode == OrchestratorMode.ROUTER - assert config.primary_model == "router:gpt-5" - - -def test_memory_manager(): - """Test memory manager functionality.""" - from hanzo.memory_manager import MemoryManager - - manager = MemoryManager("/tmp/test_hanzo") - - # Test adding memory - memory_id = manager.add_memory("Test memory", type="fact") - assert memory_id is not None - - # Test retrieving memories - memories = manager.get_memories() - assert len(memories) > 0 - - # Test removing memory - success = manager.remove_memory(memory_id) - assert success is True - - -def test_fallback_handler(): - """Test fallback handler.""" - from hanzo.fallback_handler import FallbackHandler - - handler = FallbackHandler() - - # Should always have at least free APIs available - assert handler.available_options["free_apis"] is True - - # Should have fallback order - assert len(handler.fallback_order) > 0 - - # Should get best option - best = handler.get_best_option() - assert best is not None diff --git a/pkg/hanzo/uv.lock b/pkg/hanzo/uv.lock deleted file mode 100644 index 249f163ea..000000000 --- a/pkg/hanzo/uv.lock +++ /dev/null @@ -1,6320 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version < '3.13' and sys_platform == 'win32'", - "python_full_version < '3.13' and sys_platform == 'emscripten'", - "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] - -[[package]] -name = "aiobotocore" -version = "3.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "aioitertools" }, - { name = "botocore" }, - { name = "jmespath" }, - { name = "multidict" }, - { name = "python-dateutil" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f6/bc/00ac3f44a66661fb28f2425b056d5bd202c2269a686ab0a683bb0e0516f0/aiobotocore-3.1.1.tar.gz", hash = "sha256:a19a36b930a041aa21553d67ae8a6bc464e107806eee60af3c71502f1009826c", size = 122530, upload-time = "2026-01-20T17:00:29.067Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/84/d844b79acd9fe15ded60b614b7df04a12fad854ee1fbb8415d726ab1beeb/aiobotocore-3.1.1-py3-none-any.whl", hash = "sha256:a4e12a3bd099cd19dc2b2e9fe01a807131b46ebd0f83f509bda3cb243e988c32", size = 87667, upload-time = "2026-01-20T17:00:27.869Z" }, -] - -[[package]] -name = "aiocache" -version = "0.12.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7a/64/b945b8025a9d1e6e2138845f4022165d3b337f55f50984fbc6a4c0a1e355/aiocache-0.12.3.tar.gz", hash = "sha256:f528b27bf4d436b497a1d0d1a8f59a542c153ab1e37c3621713cb376d44c4713", size = 132196, upload-time = "2024-09-25T13:20:23.823Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/37/d7/15d67e05b235d1ed8c3ce61688fe4d84130e72af1657acadfaac3479f4cf/aiocache-0.12.3-py2.py3-none-any.whl", hash = "sha256:889086fc24710f431937b87ad3720a289f7fc31c4fd8b68e9f918b9bacd8270d", size = 28199, upload-time = "2024-09-25T13:20:22.688Z" }, -] - -[[package]] -name = "aiofiles" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/c3/534eac40372d8ee36ef40df62ec129bee4fdb5ad9706e58a29be53b2c970/aiofiles-25.1.0.tar.gz", hash = "sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2", size = 46354, upload-time = "2025-10-09T20:51:04.358Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/8a/340a1555ae33d7354dbca4faa54948d76d89a27ceef032c8c3bc661d003e/aiofiles-25.1.0-py3-none-any.whl", hash = "sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695", size = 14668, upload-time = "2025-10-09T20:51:03.174Z" }, -] - -[[package]] -name = "aiohappyeyeballs" -version = "2.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, -] - -[[package]] -name = "aiohttp" -version = "3.13.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohappyeyeballs" }, - { name = "aiosignal" }, - { name = "attrs" }, - { name = "frozenlist" }, - { name = "multidict" }, - { name = "propcache" }, - { name = "yarl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732, upload-time = "2026-01-03T17:30:14.23Z" }, - { url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293, upload-time = "2026-01-03T17:30:15.96Z" }, - { url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533, upload-time = "2026-01-03T17:30:17.431Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839, upload-time = "2026-01-03T17:30:19.422Z" }, - { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932, upload-time = "2026-01-03T17:30:21.756Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906, upload-time = "2026-01-03T17:30:23.932Z" }, - { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020, upload-time = "2026-01-03T17:30:26Z" }, - { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181, upload-time = "2026-01-03T17:30:27.554Z" }, - { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794, upload-time = "2026-01-03T17:30:29.254Z" }, - { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900, upload-time = "2026-01-03T17:30:31.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239, upload-time = "2026-01-03T17:30:32.703Z" }, - { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527, upload-time = "2026-01-03T17:30:34.695Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489, upload-time = "2026-01-03T17:30:36.864Z" }, - { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852, upload-time = "2026-01-03T17:30:39.433Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379, upload-time = "2026-01-03T17:30:41.081Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4a/1a3fee7c21350cac78e5c5cef711bac1b94feca07399f3d406972e2d8fcd/aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", size = 428253, upload-time = "2026-01-03T17:30:42.644Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b7/76175c7cb4eb73d91ad63c34e29fc4f77c9386bba4a65b53ba8e05ee3c39/aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", size = 455407, upload-time = "2026-01-03T17:30:44.195Z" }, - { url = "https://files.pythonhosted.org/packages/97/8a/12ca489246ca1faaf5432844adbfce7ff2cc4997733e0af120869345643a/aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", size = 734190, upload-time = "2026-01-03T17:30:45.832Z" }, - { url = "https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", size = 491783, upload-time = "2026-01-03T17:30:47.466Z" }, - { url = "https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", size = 490704, upload-time = "2026-01-03T17:30:49.373Z" }, - { url = "https://files.pythonhosted.org/packages/6d/40/a46b03ca03936f832bc7eaa47cfbb1ad012ba1be4790122ee4f4f8cba074/aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", size = 1720652, upload-time = "2026-01-03T17:30:50.974Z" }, - { url = "https://files.pythonhosted.org/packages/f7/7e/917fe18e3607af92657e4285498f500dca797ff8c918bd7d90b05abf6c2a/aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", size = 1692014, upload-time = "2026-01-03T17:30:52.729Z" }, - { url = "https://files.pythonhosted.org/packages/71/b6/cefa4cbc00d315d68973b671cf105b21a609c12b82d52e5d0c9ae61d2a09/aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", size = 1759777, upload-time = "2026-01-03T17:30:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e3/e06ee07b45e59e6d81498b591fc589629be1553abb2a82ce33efe2a7b068/aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", size = 1861276, upload-time = "2026-01-03T17:30:56.512Z" }, - { url = "https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", size = 1743131, upload-time = "2026-01-03T17:30:58.256Z" }, - { url = "https://files.pythonhosted.org/packages/04/98/3d21dde21889b17ca2eea54fdcff21b27b93f45b7bb94ca029c31ab59dc3/aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", size = 1556863, upload-time = "2026-01-03T17:31:00.445Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/da0c3ab1192eaf64782b03971ab4055b475d0db07b17eff925e8c93b3aa5/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", size = 1682793, upload-time = "2026-01-03T17:31:03.024Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0f/5802ada182f575afa02cbd0ec5180d7e13a402afb7c2c03a9aa5e5d49060/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", size = 1716676, upload-time = "2026-01-03T17:31:04.842Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8c/714d53bd8b5a4560667f7bbbb06b20c2382f9c7847d198370ec6526af39c/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", size = 1733217, upload-time = "2026-01-03T17:31:06.868Z" }, - { url = "https://files.pythonhosted.org/packages/7d/79/e2176f46d2e963facea939f5be2d26368ce543622be6f00a12844d3c991f/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", size = 1552303, upload-time = "2026-01-03T17:31:08.958Z" }, - { url = "https://files.pythonhosted.org/packages/ab/6a/28ed4dea1759916090587d1fe57087b03e6c784a642b85ef48217b0277ae/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", size = 1763673, upload-time = "2026-01-03T17:31:10.676Z" }, - { url = "https://files.pythonhosted.org/packages/e8/35/4a3daeb8b9fab49240d21c04d50732313295e4bd813a465d840236dd0ce1/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", size = 1721120, upload-time = "2026-01-03T17:31:12.575Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9f/d643bb3c5fb99547323e635e251c609fbbc660d983144cfebec529e09264/aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", size = 427383, upload-time = "2026-01-03T17:31:14.382Z" }, - { url = "https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", size = 453899, upload-time = "2026-01-03T17:31:15.958Z" }, - { url = "https://files.pythonhosted.org/packages/99/36/5b6514a9f5d66f4e2597e40dea2e3db271e023eb7a5d22defe96ba560996/aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808", size = 737238, upload-time = "2026-01-03T17:31:17.909Z" }, - { url = "https://files.pythonhosted.org/packages/f7/49/459327f0d5bcd8c6c9ca69e60fdeebc3622861e696490d8674a6d0cb90a6/aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415", size = 492292, upload-time = "2026-01-03T17:31:19.919Z" }, - { url = "https://files.pythonhosted.org/packages/e8/0b/b97660c5fd05d3495b4eb27f2d0ef18dc1dc4eff7511a9bf371397ff0264/aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f", size = 493021, upload-time = "2026-01-03T17:31:21.636Z" }, - { url = "https://files.pythonhosted.org/packages/54/d4/438efabdf74e30aeceb890c3290bbaa449780583b1270b00661126b8aae4/aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6", size = 1717263, upload-time = "2026-01-03T17:31:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/71/f2/7bddc7fd612367d1459c5bcf598a9e8f7092d6580d98de0e057eb42697ad/aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687", size = 1669107, upload-time = "2026-01-03T17:31:25.334Z" }, - { url = "https://files.pythonhosted.org/packages/00/5a/1aeaecca40e22560f97610a329e0e5efef5e0b5afdf9f857f0d93839ab2e/aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26", size = 1760196, upload-time = "2026-01-03T17:31:27.394Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f8/0ff6992bea7bd560fc510ea1c815f87eedd745fe035589c71ce05612a19a/aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a", size = 1843591, upload-time = "2026-01-03T17:31:29.238Z" }, - { url = "https://files.pythonhosted.org/packages/e3/d1/e30e537a15f53485b61f5be525f2157da719819e8377298502aebac45536/aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1", size = 1720277, upload-time = "2026-01-03T17:31:31.053Z" }, - { url = "https://files.pythonhosted.org/packages/84/45/23f4c451d8192f553d38d838831ebbc156907ea6e05557f39563101b7717/aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25", size = 1548575, upload-time = "2026-01-03T17:31:32.87Z" }, - { url = "https://files.pythonhosted.org/packages/6a/ed/0a42b127a43712eda7807e7892c083eadfaf8429ca8fb619662a530a3aab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603", size = 1679455, upload-time = "2026-01-03T17:31:34.76Z" }, - { url = "https://files.pythonhosted.org/packages/2e/b5/c05f0c2b4b4fe2c9d55e73b6d3ed4fd6c9dc2684b1d81cbdf77e7fad9adb/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a", size = 1687417, upload-time = "2026-01-03T17:31:36.699Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6b/915bc5dad66aef602b9e459b5a973529304d4e89ca86999d9d75d80cbd0b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926", size = 1729968, upload-time = "2026-01-03T17:31:38.622Z" }, - { url = "https://files.pythonhosted.org/packages/11/3b/e84581290a9520024a08640b63d07673057aec5ca548177a82026187ba73/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba", size = 1545690, upload-time = "2026-01-03T17:31:40.57Z" }, - { url = "https://files.pythonhosted.org/packages/f5/04/0c3655a566c43fd647c81b895dfe361b9f9ad6d58c19309d45cff52d6c3b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c", size = 1746390, upload-time = "2026-01-03T17:31:42.857Z" }, - { url = "https://files.pythonhosted.org/packages/1f/53/71165b26978f719c3419381514c9690bd5980e764a09440a10bb816ea4ab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43", size = 1702188, upload-time = "2026-01-03T17:31:44.984Z" }, - { url = "https://files.pythonhosted.org/packages/29/a7/cbe6c9e8e136314fa1980da388a59d2f35f35395948a08b6747baebb6aa6/aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1", size = 433126, upload-time = "2026-01-03T17:31:47.463Z" }, - { url = "https://files.pythonhosted.org/packages/de/56/982704adea7d3b16614fc5936014e9af85c0e34b58f9046655817f04306e/aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984", size = 459128, upload-time = "2026-01-03T17:31:49.2Z" }, - { url = "https://files.pythonhosted.org/packages/6c/2a/3c79b638a9c3d4658d345339d22070241ea341ed4e07b5ac60fb0f418003/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c", size = 769512, upload-time = "2026-01-03T17:31:51.134Z" }, - { url = "https://files.pythonhosted.org/packages/29/b9/3e5014d46c0ab0db8707e0ac2711ed28c4da0218c358a4e7c17bae0d8722/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592", size = 506444, upload-time = "2026-01-03T17:31:52.85Z" }, - { url = "https://files.pythonhosted.org/packages/90/03/c1d4ef9a054e151cd7839cdc497f2638f00b93cbe8043983986630d7a80c/aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f", size = 510798, upload-time = "2026-01-03T17:31:54.91Z" }, - { url = "https://files.pythonhosted.org/packages/ea/76/8c1e5abbfe8e127c893fe7ead569148a4d5a799f7cf958d8c09f3eedf097/aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29", size = 1868835, upload-time = "2026-01-03T17:31:56.733Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ac/984c5a6f74c363b01ff97adc96a3976d9c98940b8969a1881575b279ac5d/aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc", size = 1720486, upload-time = "2026-01-03T17:31:58.65Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9a/b7039c5f099c4eb632138728828b33428585031a1e658d693d41d07d89d1/aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2", size = 1847951, upload-time = "2026-01-03T17:32:00.989Z" }, - { url = "https://files.pythonhosted.org/packages/3c/02/3bec2b9a1ba3c19ff89a43a19324202b8eb187ca1e928d8bdac9bbdddebd/aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587", size = 1941001, upload-time = "2026-01-03T17:32:03.122Z" }, - { url = "https://files.pythonhosted.org/packages/37/df/d879401cedeef27ac4717f6426c8c36c3091c6e9f08a9178cc87549c537f/aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8", size = 1797246, upload-time = "2026-01-03T17:32:05.255Z" }, - { url = "https://files.pythonhosted.org/packages/8d/15/be122de1f67e6953add23335c8ece6d314ab67c8bebb3f181063010795a7/aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632", size = 1627131, upload-time = "2026-01-03T17:32:07.607Z" }, - { url = "https://files.pythonhosted.org/packages/12/12/70eedcac9134cfa3219ab7af31ea56bc877395b1ac30d65b1bc4b27d0438/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64", size = 1795196, upload-time = "2026-01-03T17:32:09.59Z" }, - { url = "https://files.pythonhosted.org/packages/32/11/b30e1b1cd1f3054af86ebe60df96989c6a414dd87e27ad16950eee420bea/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0", size = 1782841, upload-time = "2026-01-03T17:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/88/0d/d98a9367b38912384a17e287850f5695c528cff0f14f791ce8ee2e4f7796/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56", size = 1795193, upload-time = "2026-01-03T17:32:13.705Z" }, - { url = "https://files.pythonhosted.org/packages/43/a5/a2dfd1f5ff5581632c7f6a30e1744deda03808974f94f6534241ef60c751/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72", size = 1621979, upload-time = "2026-01-03T17:32:15.965Z" }, - { url = "https://files.pythonhosted.org/packages/fa/f0/12973c382ae7c1cccbc4417e129c5bf54c374dfb85af70893646e1f0e749/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df", size = 1822193, upload-time = "2026-01-03T17:32:18.219Z" }, - { url = "https://files.pythonhosted.org/packages/3c/5f/24155e30ba7f8c96918af1350eb0663e2430aad9e001c0489d89cd708ab1/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", size = 1769801, upload-time = "2026-01-03T17:32:20.25Z" }, - { url = "https://files.pythonhosted.org/packages/eb/f8/7314031ff5c10e6ece114da79b338ec17eeff3a079e53151f7e9f43c4723/aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", size = 466523, upload-time = "2026-01-03T17:32:22.215Z" }, - { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694, upload-time = "2026-01-03T17:32:24.546Z" }, -] - -[[package]] -name = "aioitertools" -version = "0.13.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/3c/53c4a17a05fb9ea2313ee1777ff53f5e001aefd5cc85aa2f4c2d982e1e38/aioitertools-0.13.0.tar.gz", hash = "sha256:620bd241acc0bbb9ec819f1ab215866871b4bbd1f73836a55f799200ee86950c", size = 19322, upload-time = "2025-11-06T22:17:07.609Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl", hash = "sha256:0be0292b856f08dfac90e31f4739432f4cb6d7520ab9eb73e143f4f2fa5259be", size = 24182, upload-time = "2025-11-06T22:17:06.502Z" }, -] - -[[package]] -name = "aiosignal" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "frozenlist" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, -] - -[[package]] -name = "annotated-doc" -version = "0.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, -] - -[[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 = "anthropic" -version = "0.77.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "docstring-parser" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/85/6cb5da3cf91de2eeea89726316e8c5c8c31e2d61ee7cb1233d7e95512c31/anthropic-0.77.0.tar.gz", hash = "sha256:ce36efeb80cb1e25430a88440dc0f9aa5c87f10d080ab70a1bdfd5c2c5fbedb4", size = 504575, upload-time = "2026-01-29T18:20:41.507Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/27/9df785d3f94df9ac72f43ee9e14b8120b37d992b18f4952774ed46145022/anthropic-0.77.0-py3-none-any.whl", hash = "sha256:65cc83a3c82ce622d5c677d0d7706c77d29dc83958c6b10286e12fda6ffb2651", size = 397867, upload-time = "2026-01-29T18:20:39.481Z" }, -] - -[[package]] -name = "anyio" -version = "4.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, -] - -[[package]] -name = "attrs" -version = "25.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, -] - -[[package]] -name = "audioop-lts" -version = "0.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/38/53/946db57842a50b2da2e0c1e34bd37f36f5aadba1a929a3971c5d7841dbca/audioop_lts-0.2.2.tar.gz", hash = "sha256:64d0c62d88e67b98a1a5e71987b7aa7b5bcffc7dcee65b635823dbdd0a8dbbd0", size = 30686, upload-time = "2025-08-05T16:43:17.409Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/d4/94d277ca941de5a507b07f0b592f199c22454eeaec8f008a286b3fbbacd6/audioop_lts-0.2.2-cp313-abi3-macosx_10_13_universal2.whl", hash = "sha256:fd3d4602dc64914d462924a08c1a9816435a2155d74f325853c1f1ac3b2d9800", size = 46523, upload-time = "2025-08-05T16:42:20.836Z" }, - { url = "https://files.pythonhosted.org/packages/f8/5a/656d1c2da4b555920ce4177167bfeb8623d98765594af59702c8873f60ec/audioop_lts-0.2.2-cp313-abi3-macosx_10_13_x86_64.whl", hash = "sha256:550c114a8df0aafe9a05442a1162dfc8fec37e9af1d625ae6060fed6e756f303", size = 27455, upload-time = "2025-08-05T16:42:22.283Z" }, - { url = "https://files.pythonhosted.org/packages/1b/83/ea581e364ce7b0d41456fb79d6ee0ad482beda61faf0cab20cbd4c63a541/audioop_lts-0.2.2-cp313-abi3-macosx_11_0_arm64.whl", hash = "sha256:9a13dc409f2564de15dd68be65b462ba0dde01b19663720c68c1140c782d1d75", size = 26997, upload-time = "2025-08-05T16:42:23.849Z" }, - { url = "https://files.pythonhosted.org/packages/b8/3b/e8964210b5e216e5041593b7d33e97ee65967f17c282e8510d19c666dab4/audioop_lts-0.2.2-cp313-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:51c916108c56aa6e426ce611946f901badac950ee2ddaf302b7ed35d9958970d", size = 85844, upload-time = "2025-08-05T16:42:25.208Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2e/0a1c52faf10d51def20531a59ce4c706cb7952323b11709e10de324d6493/audioop_lts-0.2.2-cp313-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47eba38322370347b1c47024defbd36374a211e8dd5b0dcbce7b34fdb6f8847b", size = 85056, upload-time = "2025-08-05T16:42:26.559Z" }, - { url = "https://files.pythonhosted.org/packages/75/e8/cd95eef479656cb75ab05dfece8c1f8c395d17a7c651d88f8e6e291a63ab/audioop_lts-0.2.2-cp313-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba7c3a7e5f23e215cb271516197030c32aef2e754252c4c70a50aaff7031a2c8", size = 93892, upload-time = "2025-08-05T16:42:27.902Z" }, - { url = "https://files.pythonhosted.org/packages/5c/1e/a0c42570b74f83efa5cca34905b3eef03f7ab09fe5637015df538a7f3345/audioop_lts-0.2.2-cp313-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:def246fe9e180626731b26e89816e79aae2276f825420a07b4a647abaa84becc", size = 96660, upload-time = "2025-08-05T16:42:28.9Z" }, - { url = "https://files.pythonhosted.org/packages/50/d5/8a0ae607ca07dbb34027bac8db805498ee7bfecc05fd2c148cc1ed7646e7/audioop_lts-0.2.2-cp313-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e160bf9df356d841bb6c180eeeea1834085464626dc1b68fa4e1d59070affdc3", size = 79143, upload-time = "2025-08-05T16:42:29.929Z" }, - { url = "https://files.pythonhosted.org/packages/12/17/0d28c46179e7910bfb0bb62760ccb33edb5de973052cb2230b662c14ca2e/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4b4cd51a57b698b2d06cb9993b7ac8dfe89a3b2878e96bc7948e9f19ff51dba6", size = 84313, upload-time = "2025-08-05T16:42:30.949Z" }, - { url = "https://files.pythonhosted.org/packages/84/ba/bd5d3806641564f2024e97ca98ea8f8811d4e01d9b9f9831474bc9e14f9e/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4a53aa7c16a60a6857e6b0b165261436396ef7293f8b5c9c828a3a203147ed4a", size = 93044, upload-time = "2025-08-05T16:42:31.959Z" }, - { url = "https://files.pythonhosted.org/packages/f9/5e/435ce8d5642f1f7679540d1e73c1c42d933331c0976eb397d1717d7f01a3/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:3fc38008969796f0f689f1453722a0f463da1b8a6fbee11987830bfbb664f623", size = 78766, upload-time = "2025-08-05T16:42:33.302Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3b/b909e76b606cbfd53875693ec8c156e93e15a1366a012f0b7e4fb52d3c34/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_s390x.whl", hash = "sha256:15ab25dd3e620790f40e9ead897f91e79c0d3ce65fe193c8ed6c26cffdd24be7", size = 87640, upload-time = "2025-08-05T16:42:34.854Z" }, - { url = "https://files.pythonhosted.org/packages/30/e7/8f1603b4572d79b775f2140d7952f200f5e6c62904585d08a01f0a70393a/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:03f061a1915538fd96272bac9551841859dbb2e3bf73ebe4a23ef043766f5449", size = 86052, upload-time = "2025-08-05T16:42:35.839Z" }, - { url = "https://files.pythonhosted.org/packages/b5/96/c37846df657ccdda62ba1ae2b6534fa90e2e1b1742ca8dcf8ebd38c53801/audioop_lts-0.2.2-cp313-abi3-win32.whl", hash = "sha256:3bcddaaf6cc5935a300a8387c99f7a7fbbe212a11568ec6cf6e4bc458c048636", size = 26185, upload-time = "2025-08-05T16:42:37.04Z" }, - { url = "https://files.pythonhosted.org/packages/34/a5/9d78fdb5b844a83da8a71226c7bdae7cc638861085fff7a1d707cb4823fa/audioop_lts-0.2.2-cp313-abi3-win_amd64.whl", hash = "sha256:a2c2a947fae7d1062ef08c4e369e0ba2086049a5e598fda41122535557012e9e", size = 30503, upload-time = "2025-08-05T16:42:38.427Z" }, - { url = "https://files.pythonhosted.org/packages/34/25/20d8fde083123e90c61b51afb547bb0ea7e77bab50d98c0ab243d02a0e43/audioop_lts-0.2.2-cp313-abi3-win_arm64.whl", hash = "sha256:5f93a5db13927a37d2d09637ccca4b2b6b48c19cd9eda7b17a2e9f77edee6a6f", size = 24173, upload-time = "2025-08-05T16:42:39.704Z" }, - { url = "https://files.pythonhosted.org/packages/58/a7/0a764f77b5c4ac58dc13c01a580f5d32ae8c74c92020b961556a43e26d02/audioop_lts-0.2.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:73f80bf4cd5d2ca7814da30a120de1f9408ee0619cc75da87d0641273d202a09", size = 47096, upload-time = "2025-08-05T16:42:40.684Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ed/ebebedde1a18848b085ad0fa54b66ceb95f1f94a3fc04f1cd1b5ccb0ed42/audioop_lts-0.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:106753a83a25ee4d6f473f2be6b0966fc1c9af7e0017192f5531a3e7463dce58", size = 27748, upload-time = "2025-08-05T16:42:41.992Z" }, - { url = "https://files.pythonhosted.org/packages/cb/6e/11ca8c21af79f15dbb1c7f8017952ee8c810c438ce4e2b25638dfef2b02c/audioop_lts-0.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fbdd522624141e40948ab3e8cdae6e04c748d78710e9f0f8d4dae2750831de19", size = 27329, upload-time = "2025-08-05T16:42:42.987Z" }, - { url = "https://files.pythonhosted.org/packages/84/52/0022f93d56d85eec5da6b9da6a958a1ef09e80c39f2cc0a590c6af81dcbb/audioop_lts-0.2.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:143fad0311e8209ece30a8dbddab3b65ab419cbe8c0dde6e8828da25999be911", size = 92407, upload-time = "2025-08-05T16:42:44.336Z" }, - { url = "https://files.pythonhosted.org/packages/87/1d/48a889855e67be8718adbc7a01f3c01d5743c325453a5e81cf3717664aad/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dfbbc74ec68a0fd08cfec1f4b5e8cca3d3cd7de5501b01c4b5d209995033cde9", size = 91811, upload-time = "2025-08-05T16:42:45.325Z" }, - { url = "https://files.pythonhosted.org/packages/98/a6/94b7213190e8077547ffae75e13ed05edc488653c85aa5c41472c297d295/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cfcac6aa6f42397471e4943e0feb2244549db5c5d01efcd02725b96af417f3fe", size = 100470, upload-time = "2025-08-05T16:42:46.468Z" }, - { url = "https://files.pythonhosted.org/packages/e9/e9/78450d7cb921ede0cfc33426d3a8023a3bda755883c95c868ee36db8d48d/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:752d76472d9804ac60f0078c79cdae8b956f293177acd2316cd1e15149aee132", size = 103878, upload-time = "2025-08-05T16:42:47.576Z" }, - { url = "https://files.pythonhosted.org/packages/4f/e2/cd5439aad4f3e34ae1ee852025dc6aa8f67a82b97641e390bf7bd9891d3e/audioop_lts-0.2.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:83c381767e2cc10e93e40281a04852facc4cd9334550e0f392f72d1c0a9c5753", size = 84867, upload-time = "2025-08-05T16:42:49.003Z" }, - { url = "https://files.pythonhosted.org/packages/68/4b/9d853e9076c43ebba0d411e8d2aa19061083349ac695a7d082540bad64d0/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c0022283e9556e0f3643b7c3c03f05063ca72b3063291834cca43234f20c60bb", size = 90001, upload-time = "2025-08-05T16:42:50.038Z" }, - { url = "https://files.pythonhosted.org/packages/58/26/4bae7f9d2f116ed5593989d0e521d679b0d583973d203384679323d8fa85/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a2d4f1513d63c795e82948e1305f31a6d530626e5f9f2605408b300ae6095093", size = 99046, upload-time = "2025-08-05T16:42:51.111Z" }, - { url = "https://files.pythonhosted.org/packages/b2/67/a9f4fb3e250dda9e9046f8866e9fa7d52664f8985e445c6b4ad6dfb55641/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:c9c8e68d8b4a56fda8c025e538e639f8c5953f5073886b596c93ec9b620055e7", size = 84788, upload-time = "2025-08-05T16:42:52.198Z" }, - { url = "https://files.pythonhosted.org/packages/70/f7/3de86562db0121956148bcb0fe5b506615e3bcf6e63c4357a612b910765a/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:96f19de485a2925314f5020e85911fb447ff5fbef56e8c7c6927851b95533a1c", size = 94472, upload-time = "2025-08-05T16:42:53.59Z" }, - { url = "https://files.pythonhosted.org/packages/f1/32/fd772bf9078ae1001207d2df1eef3da05bea611a87dd0e8217989b2848fa/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e541c3ef484852ef36545f66209444c48b28661e864ccadb29daddb6a4b8e5f5", size = 92279, upload-time = "2025-08-05T16:42:54.632Z" }, - { url = "https://files.pythonhosted.org/packages/4f/41/affea7181592ab0ab560044632571a38edaf9130b84928177823fbf3176a/audioop_lts-0.2.2-cp313-cp313t-win32.whl", hash = "sha256:d5e73fa573e273e4f2e5ff96f9043858a5e9311e94ffefd88a3186a910c70917", size = 26568, upload-time = "2025-08-05T16:42:55.627Z" }, - { url = "https://files.pythonhosted.org/packages/28/2b/0372842877016641db8fc54d5c88596b542eec2f8f6c20a36fb6612bf9ee/audioop_lts-0.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9191d68659eda01e448188f60364c7763a7ca6653ed3f87ebb165822153a8547", size = 30942, upload-time = "2025-08-05T16:42:56.674Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/baf2b9cc7e96c179bb4a54f30fcd83e6ecb340031bde68f486403f943768/audioop_lts-0.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c174e322bb5783c099aaf87faeb240c8d210686b04bd61dfd05a8e5a83d88969", size = 24603, upload-time = "2025-08-05T16:42:57.571Z" }, - { url = "https://files.pythonhosted.org/packages/5c/73/413b5a2804091e2c7d5def1d618e4837f1cb82464e230f827226278556b7/audioop_lts-0.2.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:f9ee9b52f5f857fbaf9d605a360884f034c92c1c23021fb90b2e39b8e64bede6", size = 47104, upload-time = "2025-08-05T16:42:58.518Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8c/daa3308dc6593944410c2c68306a5e217f5c05b70a12e70228e7dd42dc5c/audioop_lts-0.2.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:49ee1a41738a23e98d98b937a0638357a2477bc99e61b0f768a8f654f45d9b7a", size = 27754, upload-time = "2025-08-05T16:43:00.132Z" }, - { url = "https://files.pythonhosted.org/packages/4e/86/c2e0f627168fcf61781a8f72cab06b228fe1da4b9fa4ab39cfb791b5836b/audioop_lts-0.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5b00be98ccd0fc123dcfad31d50030d25fcf31488cde9e61692029cd7394733b", size = 27332, upload-time = "2025-08-05T16:43:01.666Z" }, - { url = "https://files.pythonhosted.org/packages/c7/bd/35dce665255434f54e5307de39e31912a6f902d4572da7c37582809de14f/audioop_lts-0.2.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6d2e0f9f7a69403e388894d4ca5ada5c47230716a03f2847cfc7bd1ecb589d6", size = 92396, upload-time = "2025-08-05T16:43:02.991Z" }, - { url = "https://files.pythonhosted.org/packages/2d/d2/deeb9f51def1437b3afa35aeb729d577c04bcd89394cb56f9239a9f50b6f/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9b0b8a03ef474f56d1a842af1a2e01398b8f7654009823c6d9e0ecff4d5cfbf", size = 91811, upload-time = "2025-08-05T16:43:04.096Z" }, - { url = "https://files.pythonhosted.org/packages/76/3b/09f8b35b227cee28cc8231e296a82759ed80c1a08e349811d69773c48426/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2b267b70747d82125f1a021506565bdc5609a2b24bcb4773c16d79d2bb260bbd", size = 100483, upload-time = "2025-08-05T16:43:05.085Z" }, - { url = "https://files.pythonhosted.org/packages/0b/15/05b48a935cf3b130c248bfdbdea71ce6437f5394ee8533e0edd7cfd93d5e/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0337d658f9b81f4cd0fdb1f47635070cc084871a3d4646d9de74fdf4e7c3d24a", size = 103885, upload-time = "2025-08-05T16:43:06.197Z" }, - { url = "https://files.pythonhosted.org/packages/83/80/186b7fce6d35b68d3d739f228dc31d60b3412105854edb975aa155a58339/audioop_lts-0.2.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:167d3b62586faef8b6b2275c3218796b12621a60e43f7e9d5845d627b9c9b80e", size = 84899, upload-time = "2025-08-05T16:43:07.291Z" }, - { url = "https://files.pythonhosted.org/packages/49/89/c78cc5ac6cb5828f17514fb12966e299c850bc885e80f8ad94e38d450886/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0d9385e96f9f6da847f4d571ce3cb15b5091140edf3db97276872647ce37efd7", size = 89998, upload-time = "2025-08-05T16:43:08.335Z" }, - { url = "https://files.pythonhosted.org/packages/4c/4b/6401888d0c010e586c2ca50fce4c903d70a6bb55928b16cfbdfd957a13da/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:48159d96962674eccdca9a3df280e864e8ac75e40a577cc97c5c42667ffabfc5", size = 99046, upload-time = "2025-08-05T16:43:09.367Z" }, - { url = "https://files.pythonhosted.org/packages/de/f8/c874ca9bb447dae0e2ef2e231f6c4c2b0c39e31ae684d2420b0f9e97ee68/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8fefe5868cd082db1186f2837d64cfbfa78b548ea0d0543e9b28935ccce81ce9", size = 84843, upload-time = "2025-08-05T16:43:10.749Z" }, - { url = "https://files.pythonhosted.org/packages/3e/c0/0323e66f3daebc13fd46b36b30c3be47e3fc4257eae44f1e77eb828c703f/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:58cf54380c3884fb49fdd37dfb7a772632b6701d28edd3e2904743c5e1773602", size = 94490, upload-time = "2025-08-05T16:43:12.131Z" }, - { url = "https://files.pythonhosted.org/packages/98/6b/acc7734ac02d95ab791c10c3f17ffa3584ccb9ac5c18fd771c638ed6d1f5/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:088327f00488cdeed296edd9215ca159f3a5a5034741465789cad403fcf4bec0", size = 92297, upload-time = "2025-08-05T16:43:13.139Z" }, - { url = "https://files.pythonhosted.org/packages/13/c3/c3dc3f564ce6877ecd2a05f8d751b9b27a8c320c2533a98b0c86349778d0/audioop_lts-0.2.2-cp314-cp314t-win32.whl", hash = "sha256:068aa17a38b4e0e7de771c62c60bbca2455924b67a8814f3b0dee92b5820c0b3", size = 27331, upload-time = "2025-08-05T16:43:14.19Z" }, - { url = "https://files.pythonhosted.org/packages/72/bb/b4608537e9ffcb86449091939d52d24a055216a36a8bf66b936af8c3e7ac/audioop_lts-0.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a5bf613e96f49712073de86f20dbdd4014ca18efd4d34ed18c75bd808337851b", size = 31697, upload-time = "2025-08-05T16:43:15.193Z" }, - { url = "https://files.pythonhosted.org/packages/f6/22/91616fe707a5c5510de2cac9b046a30defe7007ba8a0c04f9c08f27df312/audioop_lts-0.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:b492c3b040153e68b9fdaff5913305aaaba5bb433d8a7f73d5cf6a64ed3cc1dd", size = 25206, upload-time = "2025-08-05T16:43:16.444Z" }, -] - -[[package]] -name = "authlib" -version = "1.6.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/9b/b1661026ff24bc641b76b78c5222d614776b0c085bcfdac9bd15a1cb4b35/authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e", size = 164894, upload-time = "2025-12-12T08:01:41.464Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/51/321e821856452f7386c4e9df866f196720b1ad0c5ea1623ea7399969ae3b/authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd", size = 244005, upload-time = "2025-12-12T08:01:40.209Z" }, -] - -[[package]] -name = "backoff" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" }, -] - -[[package]] -name = "bcrypt" -version = "5.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/36/3329e2518d70ad8e2e5817d5a4cac6bba05a47767ec416c7d020a965f408/bcrypt-5.0.0.tar.gz", hash = "sha256:f748f7c2d6fd375cc93d3fba7ef4a9e3a092421b8dbf34d8d4dc06be9492dfdd", size = 25386, upload-time = "2025-09-25T19:50:47.829Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/85/3e65e01985fddf25b64ca67275bb5bdb4040bd1a53b66d355c6c37c8a680/bcrypt-5.0.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f3c08197f3039bec79cee59a606d62b96b16669cff3949f21e74796b6e3cd2be", size = 481806, upload-time = "2025-09-25T19:49:05.102Z" }, - { url = "https://files.pythonhosted.org/packages/44/dc/01eb79f12b177017a726cbf78330eb0eb442fae0e7b3dfd84ea2849552f3/bcrypt-5.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:200af71bc25f22006f4069060c88ed36f8aa4ff7f53e67ff04d2ab3f1e79a5b2", size = 268626, upload-time = "2025-09-25T19:49:06.723Z" }, - { url = "https://files.pythonhosted.org/packages/8c/cf/e82388ad5959c40d6afd94fb4743cc077129d45b952d46bdc3180310e2df/bcrypt-5.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:baade0a5657654c2984468efb7d6c110db87ea63ef5a4b54732e7e337253e44f", size = 271853, upload-time = "2025-09-25T19:49:08.028Z" }, - { url = "https://files.pythonhosted.org/packages/ec/86/7134b9dae7cf0efa85671651341f6afa695857fae172615e960fb6a466fa/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c58b56cdfb03202b3bcc9fd8daee8e8e9b6d7e3163aa97c631dfcfcc24d36c86", size = 269793, upload-time = "2025-09-25T19:49:09.727Z" }, - { url = "https://files.pythonhosted.org/packages/cc/82/6296688ac1b9e503d034e7d0614d56e80c5d1a08402ff856a4549cb59207/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4bfd2a34de661f34d0bda43c3e4e79df586e4716ef401fe31ea39d69d581ef23", size = 289930, upload-time = "2025-09-25T19:49:11.204Z" }, - { url = "https://files.pythonhosted.org/packages/d1/18/884a44aa47f2a3b88dd09bc05a1e40b57878ecd111d17e5bba6f09f8bb77/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ed2e1365e31fc73f1825fa830f1c8f8917ca1b3ca6185773b349c20fd606cec2", size = 272194, upload-time = "2025-09-25T19:49:12.524Z" }, - { url = "https://files.pythonhosted.org/packages/0e/8f/371a3ab33c6982070b674f1788e05b656cfbf5685894acbfef0c65483a59/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:83e787d7a84dbbfba6f250dd7a5efd689e935f03dd83b0f919d39349e1f23f83", size = 269381, upload-time = "2025-09-25T19:49:14.308Z" }, - { url = "https://files.pythonhosted.org/packages/b1/34/7e4e6abb7a8778db6422e88b1f06eb07c47682313997ee8a8f9352e5a6f1/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:137c5156524328a24b9fac1cb5db0ba618bc97d11970b39184c1d87dc4bf1746", size = 271750, upload-time = "2025-09-25T19:49:15.584Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1b/54f416be2499bd72123c70d98d36c6cd61a4e33d9b89562c22481c81bb30/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:38cac74101777a6a7d3b3e3cfefa57089b5ada650dce2baf0cbdd9d65db22a9e", size = 303757, upload-time = "2025-09-25T19:49:17.244Z" }, - { url = "https://files.pythonhosted.org/packages/13/62/062c24c7bcf9d2826a1a843d0d605c65a755bc98002923d01fd61270705a/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:d8d65b564ec849643d9f7ea05c6d9f0cd7ca23bdd4ac0c2dbef1104ab504543d", size = 306740, upload-time = "2025-09-25T19:49:18.693Z" }, - { url = "https://files.pythonhosted.org/packages/d5/c8/1fdbfc8c0f20875b6b4020f3c7dc447b8de60aa0be5faaf009d24242aec9/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:741449132f64b3524e95cd30e5cd3343006ce146088f074f31ab26b94e6c75ba", size = 334197, upload-time = "2025-09-25T19:49:20.523Z" }, - { url = "https://files.pythonhosted.org/packages/a6/c1/8b84545382d75bef226fbc6588af0f7b7d095f7cd6a670b42a86243183cd/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:212139484ab3207b1f0c00633d3be92fef3c5f0af17cad155679d03ff2ee1e41", size = 352974, upload-time = "2025-09-25T19:49:22.254Z" }, - { url = "https://files.pythonhosted.org/packages/10/a6/ffb49d4254ed085e62e3e5dd05982b4393e32fe1e49bb1130186617c29cd/bcrypt-5.0.0-cp313-cp313t-win32.whl", hash = "sha256:9d52ed507c2488eddd6a95bccee4e808d3234fa78dd370e24bac65a21212b861", size = 148498, upload-time = "2025-09-25T19:49:24.134Z" }, - { url = "https://files.pythonhosted.org/packages/48/a9/259559edc85258b6d5fc5471a62a3299a6aa37a6611a169756bf4689323c/bcrypt-5.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f6984a24db30548fd39a44360532898c33528b74aedf81c26cf29c51ee47057e", size = 145853, upload-time = "2025-09-25T19:49:25.702Z" }, - { url = "https://files.pythonhosted.org/packages/2d/df/9714173403c7e8b245acf8e4be8876aac64a209d1b392af457c79e60492e/bcrypt-5.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9fffdb387abe6aa775af36ef16f55e318dcda4194ddbf82007a6f21da29de8f5", size = 139626, upload-time = "2025-09-25T19:49:26.928Z" }, - { url = "https://files.pythonhosted.org/packages/f8/14/c18006f91816606a4abe294ccc5d1e6f0e42304df5a33710e9e8e95416e1/bcrypt-5.0.0-cp314-cp314t-macosx_10_12_universal2.whl", hash = "sha256:4870a52610537037adb382444fefd3706d96d663ac44cbb2f37e3919dca3d7ef", size = 481862, upload-time = "2025-09-25T19:49:28.365Z" }, - { url = "https://files.pythonhosted.org/packages/67/49/dd074d831f00e589537e07a0725cf0e220d1f0d5d8e85ad5bbff251c45aa/bcrypt-5.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48f753100931605686f74e27a7b49238122aa761a9aefe9373265b8b7aa43ea4", size = 268544, upload-time = "2025-09-25T19:49:30.39Z" }, - { url = "https://files.pythonhosted.org/packages/f5/91/50ccba088b8c474545b034a1424d05195d9fcbaaf802ab8bfe2be5a4e0d7/bcrypt-5.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f70aadb7a809305226daedf75d90379c397b094755a710d7014b8b117df1ebbf", size = 271787, upload-time = "2025-09-25T19:49:32.144Z" }, - { url = "https://files.pythonhosted.org/packages/aa/e7/d7dba133e02abcda3b52087a7eea8c0d4f64d3e593b4fffc10c31b7061f3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:744d3c6b164caa658adcb72cb8cc9ad9b4b75c7db507ab4bc2480474a51989da", size = 269753, upload-time = "2025-09-25T19:49:33.885Z" }, - { url = "https://files.pythonhosted.org/packages/33/fc/5b145673c4b8d01018307b5c2c1fc87a6f5a436f0ad56607aee389de8ee3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a28bc05039bdf3289d757f49d616ab3efe8cf40d8e8001ccdd621cd4f98f4fc9", size = 289587, upload-time = "2025-09-25T19:49:35.144Z" }, - { url = "https://files.pythonhosted.org/packages/27/d7/1ff22703ec6d4f90e62f1a5654b8867ef96bafb8e8102c2288333e1a6ca6/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:7f277a4b3390ab4bebe597800a90da0edae882c6196d3038a73adf446c4f969f", size = 272178, upload-time = "2025-09-25T19:49:36.793Z" }, - { url = "https://files.pythonhosted.org/packages/c8/88/815b6d558a1e4d40ece04a2f84865b0fef233513bd85fd0e40c294272d62/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:79cfa161eda8d2ddf29acad370356b47f02387153b11d46042e93a0a95127493", size = 269295, upload-time = "2025-09-25T19:49:38.164Z" }, - { url = "https://files.pythonhosted.org/packages/51/8c/e0db387c79ab4931fc89827d37608c31cc57b6edc08ccd2386139028dc0d/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a5393eae5722bcef046a990b84dff02b954904c36a194f6cfc817d7dca6c6f0b", size = 271700, upload-time = "2025-09-25T19:49:39.917Z" }, - { url = "https://files.pythonhosted.org/packages/06/83/1570edddd150f572dbe9fc00f6203a89fc7d4226821f67328a85c330f239/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f4c94dec1b5ab5d522750cb059bb9409ea8872d4494fd152b53cca99f1ddd8c", size = 334034, upload-time = "2025-09-25T19:49:41.227Z" }, - { url = "https://files.pythonhosted.org/packages/c9/f2/ea64e51a65e56ae7a8a4ec236c2bfbdd4b23008abd50ac33fbb2d1d15424/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0cae4cb350934dfd74c020525eeae0a5f79257e8a201c0c176f4b84fdbf2a4b4", size = 352766, upload-time = "2025-09-25T19:49:43.08Z" }, - { url = "https://files.pythonhosted.org/packages/d7/d4/1a388d21ee66876f27d1a1f41287897d0c0f1712ef97d395d708ba93004c/bcrypt-5.0.0-cp314-cp314t-win32.whl", hash = "sha256:b17366316c654e1ad0306a6858e189fc835eca39f7eb2cafd6aaca8ce0c40a2e", size = 152449, upload-time = "2025-09-25T19:49:44.971Z" }, - { url = "https://files.pythonhosted.org/packages/3f/61/3291c2243ae0229e5bca5d19f4032cecad5dfb05a2557169d3a69dc0ba91/bcrypt-5.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:92864f54fb48b4c718fc92a32825d0e42265a627f956bc0361fe869f1adc3e7d", size = 149310, upload-time = "2025-09-25T19:49:46.162Z" }, - { url = "https://files.pythonhosted.org/packages/3e/89/4b01c52ae0c1a681d4021e5dd3e45b111a8fb47254a274fa9a378d8d834b/bcrypt-5.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dd19cf5184a90c873009244586396a6a884d591a5323f0e8a5922560718d4993", size = 143761, upload-time = "2025-09-25T19:49:47.345Z" }, - { url = "https://files.pythonhosted.org/packages/84/29/6237f151fbfe295fe3e074ecc6d44228faa1e842a81f6d34a02937ee1736/bcrypt-5.0.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:fc746432b951e92b58317af8e0ca746efe93e66555f1b40888865ef5bf56446b", size = 494553, upload-time = "2025-09-25T19:49:49.006Z" }, - { url = "https://files.pythonhosted.org/packages/45/b6/4c1205dde5e464ea3bd88e8742e19f899c16fa8916fb8510a851fae985b5/bcrypt-5.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c2388ca94ffee269b6038d48747f4ce8df0ffbea43f31abfa18ac72f0218effb", size = 275009, upload-time = "2025-09-25T19:49:50.581Z" }, - { url = "https://files.pythonhosted.org/packages/3b/71/427945e6ead72ccffe77894b2655b695ccf14ae1866cd977e185d606dd2f/bcrypt-5.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:560ddb6ec730386e7b3b26b8b4c88197aaed924430e7b74666a586ac997249ef", size = 278029, upload-time = "2025-09-25T19:49:52.533Z" }, - { url = "https://files.pythonhosted.org/packages/17/72/c344825e3b83c5389a369c8a8e58ffe1480b8a699f46c127c34580c4666b/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d79e5c65dcc9af213594d6f7f1fa2c98ad3fc10431e7aa53c176b441943efbdd", size = 275907, upload-time = "2025-09-25T19:49:54.709Z" }, - { url = "https://files.pythonhosted.org/packages/0b/7e/d4e47d2df1641a36d1212e5c0514f5291e1a956a7749f1e595c07a972038/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2b732e7d388fa22d48920baa267ba5d97cca38070b69c0e2d37087b381c681fd", size = 296500, upload-time = "2025-09-25T19:49:56.013Z" }, - { url = "https://files.pythonhosted.org/packages/0f/c3/0ae57a68be2039287ec28bc463b82e4b8dc23f9d12c0be331f4782e19108/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0c8e093ea2532601a6f686edbc2c6b2ec24131ff5c52f7610dd64fa4553b5464", size = 278412, upload-time = "2025-09-25T19:49:57.356Z" }, - { url = "https://files.pythonhosted.org/packages/45/2b/77424511adb11e6a99e3a00dcc7745034bee89036ad7d7e255a7e47be7d8/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5b1589f4839a0899c146e8892efe320c0fa096568abd9b95593efac50a87cb75", size = 275486, upload-time = "2025-09-25T19:49:59.116Z" }, - { url = "https://files.pythonhosted.org/packages/43/0a/405c753f6158e0f3f14b00b462d8bca31296f7ecfc8fc8bc7919c0c7d73a/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:89042e61b5e808b67daf24a434d89bab164d4de1746b37a8d173b6b14f3db9ff", size = 277940, upload-time = "2025-09-25T19:50:00.869Z" }, - { url = "https://files.pythonhosted.org/packages/62/83/b3efc285d4aadc1fa83db385ec64dcfa1707e890eb42f03b127d66ac1b7b/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:e3cf5b2560c7b5a142286f69bde914494b6d8f901aaa71e453078388a50881c4", size = 310776, upload-time = "2025-09-25T19:50:02.393Z" }, - { url = "https://files.pythonhosted.org/packages/95/7d/47ee337dacecde6d234890fe929936cb03ebc4c3a7460854bbd9c97780b8/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f632fd56fc4e61564f78b46a2269153122db34988e78b6be8b32d28507b7eaeb", size = 312922, upload-time = "2025-09-25T19:50:04.232Z" }, - { url = "https://files.pythonhosted.org/packages/d6/3a/43d494dfb728f55f4e1cf8fd435d50c16a2d75493225b54c8d06122523c6/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:801cad5ccb6b87d1b430f183269b94c24f248dddbbc5c1f78b6ed231743e001c", size = 341367, upload-time = "2025-09-25T19:50:05.559Z" }, - { url = "https://files.pythonhosted.org/packages/55/ab/a0727a4547e383e2e22a630e0f908113db37904f58719dc48d4622139b5c/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3cf67a804fc66fc217e6914a5635000259fbbbb12e78a99488e4d5ba445a71eb", size = 359187, upload-time = "2025-09-25T19:50:06.916Z" }, - { url = "https://files.pythonhosted.org/packages/1b/bb/461f352fdca663524b4643d8b09e8435b4990f17fbf4fea6bc2a90aa0cc7/bcrypt-5.0.0-cp38-abi3-win32.whl", hash = "sha256:3abeb543874b2c0524ff40c57a4e14e5d3a66ff33fb423529c88f180fd756538", size = 153752, upload-time = "2025-09-25T19:50:08.515Z" }, - { url = "https://files.pythonhosted.org/packages/41/aa/4190e60921927b7056820291f56fc57d00d04757c8b316b2d3c0d1d6da2c/bcrypt-5.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:35a77ec55b541e5e583eb3436ffbbf53b0ffa1fa16ca6782279daf95d146dcd9", size = 150881, upload-time = "2025-09-25T19:50:09.742Z" }, - { url = "https://files.pythonhosted.org/packages/54/12/cd77221719d0b39ac0b55dbd39358db1cd1246e0282e104366ebbfb8266a/bcrypt-5.0.0-cp38-abi3-win_arm64.whl", hash = "sha256:cde08734f12c6a4e28dc6755cd11d3bdfea608d93d958fffbe95a7026ebe4980", size = 144931, upload-time = "2025-09-25T19:50:11.016Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ba/2af136406e1c3839aea9ecadc2f6be2bcd1eff255bd451dd39bcf302c47a/bcrypt-5.0.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0c418ca99fd47e9c59a301744d63328f17798b5947b0f791e9af3c1c499c2d0a", size = 495313, upload-time = "2025-09-25T19:50:12.309Z" }, - { url = "https://files.pythonhosted.org/packages/ac/ee/2f4985dbad090ace5ad1f7dd8ff94477fe089b5fab2040bd784a3d5f187b/bcrypt-5.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddb4e1500f6efdd402218ffe34d040a1196c072e07929b9820f363a1fd1f4191", size = 275290, upload-time = "2025-09-25T19:50:13.673Z" }, - { url = "https://files.pythonhosted.org/packages/e4/6e/b77ade812672d15cf50842e167eead80ac3514f3beacac8902915417f8b7/bcrypt-5.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7aeef54b60ceddb6f30ee3db090351ecf0d40ec6e2abf41430997407a46d2254", size = 278253, upload-time = "2025-09-25T19:50:15.089Z" }, - { url = "https://files.pythonhosted.org/packages/36/c4/ed00ed32f1040f7990dac7115f82273e3c03da1e1a1587a778d8cea496d8/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f0ce778135f60799d89c9693b9b398819d15f1921ba15fe719acb3178215a7db", size = 276084, upload-time = "2025-09-25T19:50:16.699Z" }, - { url = "https://files.pythonhosted.org/packages/e7/c4/fa6e16145e145e87f1fa351bbd54b429354fd72145cd3d4e0c5157cf4c70/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a71f70ee269671460b37a449f5ff26982a6f2ba493b3eabdd687b4bf35f875ac", size = 297185, upload-time = "2025-09-25T19:50:18.525Z" }, - { url = "https://files.pythonhosted.org/packages/24/b4/11f8a31d8b67cca3371e046db49baa7c0594d71eb40ac8121e2fc0888db0/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8429e1c410b4073944f03bd778a9e066e7fad723564a52ff91841d278dfc822", size = 278656, upload-time = "2025-09-25T19:50:19.809Z" }, - { url = "https://files.pythonhosted.org/packages/ac/31/79f11865f8078e192847d2cb526e3fa27c200933c982c5b2869720fa5fce/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:edfcdcedd0d0f05850c52ba3127b1fce70b9f89e0fe5ff16517df7e81fa3cbb8", size = 275662, upload-time = "2025-09-25T19:50:21.567Z" }, - { url = "https://files.pythonhosted.org/packages/d4/8d/5e43d9584b3b3591a6f9b68f755a4da879a59712981ef5ad2a0ac1379f7a/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:611f0a17aa4a25a69362dcc299fda5c8a3d4f160e2abb3831041feb77393a14a", size = 278240, upload-time = "2025-09-25T19:50:23.305Z" }, - { url = "https://files.pythonhosted.org/packages/89/48/44590e3fc158620f680a978aafe8f87a4c4320da81ed11552f0323aa9a57/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:db99dca3b1fdc3db87d7c57eac0c82281242d1eabf19dcb8a6b10eb29a2e72d1", size = 311152, upload-time = "2025-09-25T19:50:24.597Z" }, - { url = "https://files.pythonhosted.org/packages/5f/85/e4fbfc46f14f47b0d20493669a625da5827d07e8a88ee460af6cd9768b44/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:5feebf85a9cefda32966d8171f5db7e3ba964b77fdfe31919622256f80f9cf42", size = 313284, upload-time = "2025-09-25T19:50:26.268Z" }, - { url = "https://files.pythonhosted.org/packages/25/ae/479f81d3f4594456a01ea2f05b132a519eff9ab5768a70430fa1132384b1/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3ca8a166b1140436e058298a34d88032ab62f15aae1c598580333dc21d27ef10", size = 341643, upload-time = "2025-09-25T19:50:28.02Z" }, - { url = "https://files.pythonhosted.org/packages/df/d2/36a086dee1473b14276cd6ea7f61aef3b2648710b5d7f1c9e032c29b859f/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:61afc381250c3182d9078551e3ac3a41da14154fbff647ddf52a769f588c4172", size = 359698, upload-time = "2025-09-25T19:50:31.347Z" }, - { url = "https://files.pythonhosted.org/packages/c0/f6/688d2cd64bfd0b14d805ddb8a565e11ca1fb0fd6817175d58b10052b6d88/bcrypt-5.0.0-cp39-abi3-win32.whl", hash = "sha256:64d7ce196203e468c457c37ec22390f1a61c85c6f0b8160fd752940ccfb3a683", size = 153725, upload-time = "2025-09-25T19:50:34.384Z" }, - { url = "https://files.pythonhosted.org/packages/9f/b9/9d9a641194a730bda138b3dfe53f584d61c58cd5230e37566e83ec2ffa0d/bcrypt-5.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:64ee8434b0da054d830fa8e89e1c8bf30061d539044a39524ff7dec90481e5c2", size = 150912, upload-time = "2025-09-25T19:50:35.69Z" }, - { url = "https://files.pythonhosted.org/packages/27/44/d2ef5e87509158ad2187f4dd0852df80695bb1ee0cfe0a684727b01a69e0/bcrypt-5.0.0-cp39-abi3-win_arm64.whl", hash = "sha256:f2347d3534e76bf50bca5500989d6c1d05ed64b440408057a37673282c654927", size = 144953, upload-time = "2025-09-25T19:50:37.32Z" }, -] - -[[package]] -name = "beartype" -version = "0.22.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, -] - -[[package]] -name = "beautifulsoup4" -version = "4.14.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "soupsieve" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" }, -] - -[[package]] -name = "binaryornot" -version = "0.4.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "chardet" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a7/fe/7ebfec74d49f97fc55cd38240c7a7d08134002b1e14be8c3897c0dd5e49b/binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061", size = 371054, upload-time = "2017-08-03T15:55:25.08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/24/7e/f7b6f453e6481d1e233540262ccbfcf89adcd43606f44a028d7f5fae5eb2/binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4", size = 9006, upload-time = "2017-08-03T15:55:31.23Z" }, -] - -[[package]] -name = "botocore" -version = "1.42.30" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jmespath" }, - { name = "python-dateutil" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/44/38/23862628a0eb044c8b8b3d7a9ad1920b3bfd6bce6d746d5a871e8382c7e4/botocore-1.42.30.tar.gz", hash = "sha256:9bf1662b8273d5cc3828a49f71ca85abf4e021011c1f0a71f41a2ea5769a5116", size = 14891439, upload-time = "2026-01-16T20:37:13.77Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/8d/6d7b016383b1f74dd93611b1c5078bbaddaca901553ab886dcda87cae365/botocore-1.42.30-py3-none-any.whl", hash = "sha256:97070a438cac92430bb7b65f8ebd7075224f4a289719da4ee293d22d1e98db02", size = 14566340, upload-time = "2026-01-16T20:37:10.94Z" }, -] - -[[package]] -name = "build" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "os_name == 'nt'" }, - { name = "packaging" }, - { name = "pyproject-hooks" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/18/94eaffda7b329535d91f00fe605ab1f1e5cd68b2074d03f255c7d250687d/build-1.4.0.tar.gz", hash = "sha256:f1b91b925aa322be454f8330c6fb48b465da993d1e7e7e6fa35027ec49f3c936", size = 50054, upload-time = "2026-01-08T16:41:47.696Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl", hash = "sha256:6a07c1b8eb6f2b311b96fcbdbce5dab5fe637ffda0fd83c9cac622e927501596", size = 24141, upload-time = "2026-01-08T16:41:46.453Z" }, -] - -[[package]] -name = "cachetools" -version = "5.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" }, -] - -[[package]] -name = "camel-converter" -version = "5.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/dd/f8df35e1d2b6360a27c1613937bde2f68db41b8a67cd856f105d2031a3ed/camel_converter-5.0.0.tar.gz", hash = "sha256:97d0cf15a75b40abab288c526c71143b468376dd61690fd1cb0a49d9e471f111", size = 58926, upload-time = "2025-10-07T18:01:04.577Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/67/283543fc3ac6d6d8e9cd710e331ebf2323a284f66be304ac786c3b7bdeb4/camel_converter-5.0.0-py3-none-any.whl", hash = "sha256:5a64900bcba1ca047c504ee88cefa905a5bcac15cec06f6d6f9d1ebad46d4e80", size = 6254, upload-time = "2025-10-07T18:01:03.248Z" }, -] - -[package.optional-dependencies] -pydantic = [ - { name = "pydantic" }, -] - -[[package]] -name = "certifi" -version = "2026.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "chardet" -version = "5.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618, upload-time = "2023-08-01T19:23:02.662Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385, upload-time = "2023-08-01T19:23:00.661Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, - { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, - { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, - { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, - { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, - { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, - { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, - { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, - { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, -] - -[[package]] -name = "chromadb" -version = "1.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "bcrypt" }, - { name = "build" }, - { name = "grpcio" }, - { name = "httpx" }, - { name = "importlib-resources" }, - { name = "jsonschema" }, - { name = "kubernetes" }, - { name = "mmh3" }, - { name = "numpy" }, - { name = "onnxruntime" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-otlp-proto-grpc" }, - { name = "opentelemetry-sdk" }, - { name = "orjson" }, - { name = "overrides" }, - { name = "posthog" }, - { name = "pybase64" }, - { name = "pydantic" }, - { name = "pypika" }, - { name = "pyyaml" }, - { name = "rich" }, - { name = "tenacity" }, - { name = "tokenizers" }, - { name = "tqdm" }, - { name = "typer" }, - { name = "typing-extensions" }, - { name = "uvicorn", extra = ["standard"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/03/35/24479ac00e74b86e388854a573a9ebe6d41c51c37e03d00864bb967d861f/chromadb-1.4.1.tar.gz", hash = "sha256:3cceb83e0a7a3c2db0752ebf62e9cfe652da657594c093fe07e74022581a58eb", size = 2226347, upload-time = "2026-01-14T19:18:15.189Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/f0/7c815bb80a2aaa349757ed0c743fa7e85bbe16f612057b25cf1809456a32/chromadb-1.4.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:05d98ffe4a9a5549c9a78eee7624277f9d99c53200a01f1176ecb1d31ea3c819", size = 20313209, upload-time = "2026-01-14T19:18:12.111Z" }, - { url = "https://files.pythonhosted.org/packages/a1/4b/c16236d56bf6bf144edbe5a03c431b59ba089bd6f86baefa8ebc288bf8b8/chromadb-1.4.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:38336431c01562cffdb3ef693f22f7a88df5304f942e01ed66ee0bbaf08f35da", size = 19634405, upload-time = "2026-01-14T19:18:08.264Z" }, - { url = "https://files.pythonhosted.org/packages/70/9c/33c6c3036e30632c2b64d333e92af3972e6bef423a8285e0edc5f487d322/chromadb-1.4.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaaf9c7d4ddbbdc74bd7cac45d9729032020cc6e65a2b8f313257e6c949beed", size = 20276410, upload-time = "2026-01-14T19:18:00.226Z" }, - { url = "https://files.pythonhosted.org/packages/29/bc/0c6a6255cd55fe384c1bda6bebb47b5ff9d5c535d993fd3451e4a3fbe42f/chromadb-1.4.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad50fbb5799dcaef5ae7613be583a06b44b637283db066396490863266f48623", size = 21082323, upload-time = "2026-01-14T19:18:04.604Z" }, - { url = "https://files.pythonhosted.org/packages/79/be/5092571f87ddf08022a3d9434d3374d3f5aa20ebad1c75d63107c0c046d6/chromadb-1.4.1-cp39-abi3-win_amd64.whl", hash = "sha256:cedc9941dad1081eb9be89a7f5f66374715d4f99f731f1eb9da900636c501330", size = 21376957, upload-time = "2026-01-14T19:18:16.95Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, -] - -[[package]] -name = "cobble" -version = "0.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/7a/a507c709be2c96e1bb6102eb7b7f4026c5e5e223ef7d745a17d239e9d844/cobble-0.1.4.tar.gz", hash = "sha256:de38be1539992c8a06e569630717c485a5f91be2192c461ea2b220607dfa78aa", size = 3805, upload-time = "2024-06-01T18:11:09.528Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/e1/3714a2f371985215c219c2a70953d38e3eed81ef165aed061d21de0e998b/cobble-0.1.4-py3-none-any.whl", hash = "sha256:36c91b1655e599fd428e2b95fdd5f0da1ca2e9f1abb0bc871dec21a0e78a2b44", size = 3984, upload-time = "2024-06-01T18:11:07.911Z" }, -] - -[[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 = "coloredlogs" -version = "15.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "humanfriendly" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" }, -] - -[[package]] -name = "contourpy" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, - { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, - { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, - { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, - { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, - { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, - { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, - { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, - { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, - { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, - { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, - { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, - { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, - { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, - { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, - { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, - { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, - { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, - { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, - { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, - { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, - { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, - { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, - { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, - { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, - { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, - { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, - { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, - { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" }, - { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" }, - { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" }, - { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" }, - { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" }, - { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" }, - { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" }, - { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" }, - { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" }, - { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" }, - { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" }, - { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" }, - { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" }, - { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" }, - { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" }, - { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" }, - { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" }, -] - -[[package]] -name = "croniter" -version = "6.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil" }, - { name = "pytz" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ad/2f/44d1ae153a0e27be56be43465e5cb39b9650c781e001e7864389deb25090/croniter-6.0.0.tar.gz", hash = "sha256:37c504b313956114a983ece2c2b07790b1f1094fe9d81cc94739214748255577", size = 64481, upload-time = "2024-12-17T17:17:47.32Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/4b/290b4c3efd6417a8b0c284896de19b1d5855e6dbdb97d2a35e68fa42de85/croniter-6.0.0-py2.py3-none-any.whl", hash = "sha256:2f878c3856f17896979b2a4379ba1f09c83e374931ea15cc835c5dd2eee9b368", size = 25468, upload-time = "2024-12-17T17:17:45.359Z" }, -] - -[[package]] -name = "cryptography" -version = "46.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/19/f748958276519adf6a0c1e79e7b8860b4830dda55ccdf29f2719b5fc499c/cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59", size = 749301, upload-time = "2026-01-28T00:24:37.379Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/99/157aae7949a5f30d51fcb1a9851e8ebd5c74bf99b5285d8bb4b8b9ee641e/cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485", size = 7173686, upload-time = "2026-01-28T00:23:07.515Z" }, - { url = "https://files.pythonhosted.org/packages/87/91/874b8910903159043b5c6a123b7e79c4559ddd1896e38967567942635778/cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc", size = 4275871, upload-time = "2026-01-28T00:23:09.439Z" }, - { url = "https://files.pythonhosted.org/packages/c0/35/690e809be77896111f5b195ede56e4b4ed0435b428c2f2b6d35046fbb5e8/cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0", size = 4423124, upload-time = "2026-01-28T00:23:11.529Z" }, - { url = "https://files.pythonhosted.org/packages/1a/5b/a26407d4f79d61ca4bebaa9213feafdd8806dc69d3d290ce24996d3cfe43/cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa", size = 4277090, upload-time = "2026-01-28T00:23:13.123Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d8/4bb7aec442a9049827aa34cee1aa83803e528fa55da9a9d45d01d1bb933e/cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81", size = 4947652, upload-time = "2026-01-28T00:23:14.554Z" }, - { url = "https://files.pythonhosted.org/packages/2b/08/f83e2e0814248b844265802d081f2fac2f1cbe6cd258e72ba14ff006823a/cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255", size = 4455157, upload-time = "2026-01-28T00:23:16.443Z" }, - { url = "https://files.pythonhosted.org/packages/0a/05/19d849cf4096448779d2dcc9bb27d097457dac36f7273ffa875a93b5884c/cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e", size = 3981078, upload-time = "2026-01-28T00:23:17.838Z" }, - { url = "https://files.pythonhosted.org/packages/e6/89/f7bac81d66ba7cde867a743ea5b37537b32b5c633c473002b26a226f703f/cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c", size = 4276213, upload-time = "2026-01-28T00:23:19.257Z" }, - { url = "https://files.pythonhosted.org/packages/da/9f/7133e41f24edd827020ad21b068736e792bc68eecf66d93c924ad4719fb3/cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32", size = 4912190, upload-time = "2026-01-28T00:23:21.244Z" }, - { url = "https://files.pythonhosted.org/packages/a6/f7/6d43cbaddf6f65b24816e4af187d211f0bc536a29961f69faedc48501d8e/cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616", size = 4454641, upload-time = "2026-01-28T00:23:22.866Z" }, - { url = "https://files.pythonhosted.org/packages/9e/4f/ebd0473ad656a0ac912a16bd07db0f5d85184924e14fc88feecae2492834/cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0", size = 4405159, upload-time = "2026-01-28T00:23:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d1/f7/7923886f32dc47e27adeff8246e976d77258fd2aa3efdd1754e4e323bf49/cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0", size = 4666059, upload-time = "2026-01-28T00:23:26.766Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a7/0fca0fd3591dffc297278a61813d7f661a14243dd60f499a7a5b48acb52a/cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5", size = 3026378, upload-time = "2026-01-28T00:23:28.317Z" }, - { url = "https://files.pythonhosted.org/packages/2d/12/652c84b6f9873f0909374864a57b003686c642ea48c84d6c7e2c515e6da5/cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b", size = 3478614, upload-time = "2026-01-28T00:23:30.275Z" }, - { url = "https://files.pythonhosted.org/packages/b9/27/542b029f293a5cce59349d799d4d8484b3b1654a7b9a0585c266e974a488/cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908", size = 7116417, upload-time = "2026-01-28T00:23:31.958Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f5/559c25b77f40b6bf828eabaf988efb8b0e17b573545edb503368ca0a2a03/cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da", size = 4264508, upload-time = "2026-01-28T00:23:34.264Z" }, - { url = "https://files.pythonhosted.org/packages/49/a1/551fa162d33074b660dc35c9bc3616fefa21a0e8c1edd27b92559902e408/cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829", size = 4409080, upload-time = "2026-01-28T00:23:35.793Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/4d8d129a755f5d6df1bbee69ea2f35ebfa954fa1847690d1db2e8bca46a5/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2", size = 4270039, upload-time = "2026-01-28T00:23:37.263Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f5/ed3fcddd0a5e39321e595e144615399e47e7c153a1fb8c4862aec3151ff9/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085", size = 4926748, upload-time = "2026-01-28T00:23:38.884Z" }, - { url = "https://files.pythonhosted.org/packages/43/ae/9f03d5f0c0c00e85ecb34f06d3b79599f20630e4db91b8a6e56e8f83d410/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b", size = 4442307, upload-time = "2026-01-28T00:23:40.56Z" }, - { url = "https://files.pythonhosted.org/packages/8b/22/e0f9f2dae8040695103369cf2283ef9ac8abe4d51f68710bec2afd232609/cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd", size = 3959253, upload-time = "2026-01-28T00:23:42.827Z" }, - { url = "https://files.pythonhosted.org/packages/01/5b/6a43fcccc51dae4d101ac7d378a8724d1ba3de628a24e11bf2f4f43cba4d/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2", size = 4269372, upload-time = "2026-01-28T00:23:44.655Z" }, - { url = "https://files.pythonhosted.org/packages/17/b7/0f6b8c1dd0779df2b526e78978ff00462355e31c0a6f6cff8a3e99889c90/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e", size = 4891908, upload-time = "2026-01-28T00:23:46.48Z" }, - { url = "https://files.pythonhosted.org/packages/83/17/259409b8349aa10535358807a472c6a695cf84f106022268d31cea2b6c97/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f", size = 4441254, upload-time = "2026-01-28T00:23:48.403Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fe/e4a1b0c989b00cee5ffa0764401767e2d1cf59f45530963b894129fd5dce/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82", size = 4396520, upload-time = "2026-01-28T00:23:50.26Z" }, - { url = "https://files.pythonhosted.org/packages/b3/81/ba8fd9657d27076eb40d6a2f941b23429a3c3d2f56f5a921d6b936a27bc9/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c", size = 4651479, upload-time = "2026-01-28T00:23:51.674Z" }, - { url = "https://files.pythonhosted.org/packages/00/03/0de4ed43c71c31e4fe954edd50b9d28d658fef56555eba7641696370a8e2/cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061", size = 3001986, upload-time = "2026-01-28T00:23:53.485Z" }, - { url = "https://files.pythonhosted.org/packages/5c/70/81830b59df7682917d7a10f833c4dab2a5574cd664e86d18139f2b421329/cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7", size = 3468288, upload-time = "2026-01-28T00:23:55.09Z" }, - { url = "https://files.pythonhosted.org/packages/56/f7/f648fdbb61d0d45902d3f374217451385edc7e7768d1b03ff1d0e5ffc17b/cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab", size = 7169583, upload-time = "2026-01-28T00:23:56.558Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cc/8f3224cbb2a928de7298d6ed4790f5ebc48114e02bdc9559196bfb12435d/cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef", size = 4275419, upload-time = "2026-01-28T00:23:58.364Z" }, - { url = "https://files.pythonhosted.org/packages/17/43/4a18faa7a872d00e4264855134ba82d23546c850a70ff209e04ee200e76f/cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d", size = 4419058, upload-time = "2026-01-28T00:23:59.867Z" }, - { url = "https://files.pythonhosted.org/packages/ee/64/6651969409821d791ba12346a124f55e1b76f66a819254ae840a965d4b9c/cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973", size = 4278151, upload-time = "2026-01-28T00:24:01.731Z" }, - { url = "https://files.pythonhosted.org/packages/20/0b/a7fce65ee08c3c02f7a8310cc090a732344066b990ac63a9dfd0a655d321/cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4", size = 4939441, upload-time = "2026-01-28T00:24:03.175Z" }, - { url = "https://files.pythonhosted.org/packages/db/a7/20c5701e2cd3e1dfd7a19d2290c522a5f435dd30957d431dcb531d0f1413/cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af", size = 4451617, upload-time = "2026-01-28T00:24:05.403Z" }, - { url = "https://files.pythonhosted.org/packages/00/dc/3e16030ea9aa47b63af6524c354933b4fb0e352257c792c4deeb0edae367/cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263", size = 3977774, upload-time = "2026-01-28T00:24:06.851Z" }, - { url = "https://files.pythonhosted.org/packages/42/c8/ad93f14118252717b465880368721c963975ac4b941b7ef88f3c56bf2897/cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095", size = 4277008, upload-time = "2026-01-28T00:24:08.926Z" }, - { url = "https://files.pythonhosted.org/packages/00/cf/89c99698151c00a4631fbfcfcf459d308213ac29e321b0ff44ceeeac82f1/cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b", size = 4903339, upload-time = "2026-01-28T00:24:12.009Z" }, - { url = "https://files.pythonhosted.org/packages/03/c3/c90a2cb358de4ac9309b26acf49b2a100957e1ff5cc1e98e6c4996576710/cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019", size = 4451216, upload-time = "2026-01-28T00:24:13.975Z" }, - { url = "https://files.pythonhosted.org/packages/96/2c/8d7f4171388a10208671e181ca43cdc0e596d8259ebacbbcfbd16de593da/cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4", size = 4404299, upload-time = "2026-01-28T00:24:16.169Z" }, - { url = "https://files.pythonhosted.org/packages/e9/23/cbb2036e450980f65c6e0a173b73a56ff3bccd8998965dea5cc9ddd424a5/cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b", size = 4664837, upload-time = "2026-01-28T00:24:17.629Z" }, - { url = "https://files.pythonhosted.org/packages/0a/21/f7433d18fe6d5845329cbdc597e30caf983229c7a245bcf54afecc555938/cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc", size = 3009779, upload-time = "2026-01-28T00:24:20.198Z" }, - { url = "https://files.pythonhosted.org/packages/3a/6a/bd2e7caa2facffedf172a45c1a02e551e6d7d4828658c9a245516a598d94/cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976", size = 3466633, upload-time = "2026-01-28T00:24:21.851Z" }, -] - -[[package]] -name = "cuda-bindings" -version = "12.9.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-pathfinder", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/c1/dabe88f52c3e3760d861401bb994df08f672ec893b8f7592dc91626adcf3/cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8", size = 12151019, upload-time = "2025-10-21T14:51:43.167Z" }, - { url = "https://files.pythonhosted.org/packages/63/56/e465c31dc9111be3441a9ba7df1941fe98f4aa6e71e8788a3fb4534ce24d/cuda_bindings-12.9.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:32bdc5a76906be4c61eb98f546a6786c5773a881f3b166486449b5d141e4a39f", size = 11906628, upload-time = "2025-10-21T14:51:49.905Z" }, - { url = "https://files.pythonhosted.org/packages/a3/84/1e6be415e37478070aeeee5884c2022713c1ecc735e6d82d744de0252eee/cuda_bindings-12.9.4-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56e0043c457a99ac473ddc926fe0dc4046694d99caef633e92601ab52cbe17eb", size = 11925991, upload-time = "2025-10-21T14:51:56.535Z" }, - { url = "https://files.pythonhosted.org/packages/d1/af/6dfd8f2ed90b1d4719bc053ff8940e494640fe4212dc3dd72f383e4992da/cuda_bindings-12.9.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b72ee72a9cc1b531db31eebaaee5c69a8ec3500e32c6933f2d3b15297b53686", size = 11922703, upload-time = "2025-10-21T14:52:03.585Z" }, - { url = "https://files.pythonhosted.org/packages/6c/19/90ac264acc00f6df8a49378eedec9fd2db3061bf9263bf9f39fd3d8377c3/cuda_bindings-12.9.4-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d80bffc357df9988dca279734bc9674c3934a654cab10cadeed27ce17d8635ee", size = 11924658, upload-time = "2025-10-21T14:52:10.411Z" }, -] - -[[package]] -name = "cuda-pathfinder" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/02/4dbe7568a42e46582248942f54dc64ad094769532adbe21e525e4edf7bc4/cuda_pathfinder-1.3.3-py3-none-any.whl", hash = "sha256:9984b664e404f7c134954a771be8775dfd6180ea1e1aef4a5a37d4be05d9bbb1", size = 27154, upload-time = "2025-12-04T22:35:08.996Z" }, -] - -[[package]] -name = "cycler" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, -] - -[[package]] -name = "cyclopts" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "docstring-parser" }, - { name = "rich" }, - { name = "rich-rst" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d4/93/6085aa89c3fff78a5180987354538d72e43b0db27e66a959302d0c07821a/cyclopts-4.5.1.tar.gz", hash = "sha256:fadc45304763fd9f5d6033727f176898d17a1778e194436964661a005078a3dd", size = 162075, upload-time = "2026-01-25T15:23:54.07Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/7c/996760c30f1302704af57c66ff2d723f7d656d0d0b93563b5528a51484bb/cyclopts-4.5.1-py3-none-any.whl", hash = "sha256:0642c93601e554ca6b7b9abd81093847ea4448b2616280f2a0952416574e8c7a", size = 199807, upload-time = "2026-01-25T15:23:55.219Z" }, -] - -[[package]] -name = "defusedxml" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, -] - -[[package]] -name = "deprecation" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/d3/8ae2869247df154b64c1884d7346d412fed0c49df84db635aab2d1c40e62/deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", size = 173788, upload-time = "2020-04-20T14:23:38.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", size = 11178, upload-time = "2020-04-20T14:23:36.581Z" }, -] - -[[package]] -name = "diskcache" -version = "5.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, -] - -[[package]] -name = "distro" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, -] - -[[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.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, -] - -[[package]] -name = "durationpy" -version = "0.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/a4/e44218c2b394e31a6dd0d6b095c4e1f32d0be54c2a4b250032d717647bab/durationpy-0.10.tar.gz", hash = "sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba", size = 3335, upload-time = "2025-05-17T13:52:37.26Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/0d/9feae160378a3553fa9a339b0e9c1a048e147a4127210e286ef18b730f03/durationpy-0.10-py3-none-any.whl", hash = "sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286", size = 3922, upload-time = "2025-05-17T13:52:36.463Z" }, -] - -[[package]] -name = "ecdsa" -version = "0.19.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/1f/924e3caae75f471eae4b26bd13b698f6af2c44279f67af317439c2f4c46a/ecdsa-0.19.1.tar.gz", hash = "sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61", size = 201793, upload-time = "2025-03-13T11:52:43.25Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/a3/460c57f094a4a165c84a1341c373b0a4f5ec6ac244b998d5021aade89b77/ecdsa-0.19.1-py2.py3-none-any.whl", hash = "sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3", size = 150607, upload-time = "2025-03-13T11:52:41.757Z" }, -] - -[[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 = "et-xmlfile" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234, upload-time = "2024-10-25T17:25:40.039Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" }, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "fakeredis" -version = "2.33.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "redis" }, - { name = "sortedcontainers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/f9/57464119936414d60697fcbd32f38909bb5688b616ae13de6e98384433e0/fakeredis-2.33.0.tar.gz", hash = "sha256:d7bc9a69d21df108a6451bbffee23b3eba432c21a654afc7ff2d295428ec5770", size = 175187, upload-time = "2025-12-16T19:45:52.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/78/a850fed8aeef96d4a99043c90b818b2ed5419cd5b24a4049fd7cfb9f1471/fakeredis-2.33.0-py3-none-any.whl", hash = "sha256:de535f3f9ccde1c56672ab2fdd6a8efbc4f2619fc2f1acc87b8737177d71c965", size = 119605, upload-time = "2025-12-16T19:45:51.08Z" }, -] - -[package.optional-dependencies] -lua = [ - { name = "lupa" }, -] - -[[package]] -name = "fastapi" -version = "0.128.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-doc" }, - { name = "pydantic" }, - { name = "starlette" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/52/08/8c8508db6c7b9aae8f7175046af41baad690771c9bcde676419965e338c7/fastapi-0.128.0.tar.gz", hash = "sha256:1cc179e1cef10a6be60ffe429f79b829dce99d8de32d7acb7e6c8dfdf7f2645a", size = 365682, upload-time = "2025-12-27T15:21:13.714Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/05/5cbb59154b093548acd0f4c7c474a118eda06da25aa75c616b72d8fcd92a/fastapi-0.128.0-py3-none-any.whl", hash = "sha256:aebd93f9716ee3b4f4fcfe13ffb7cf308d99c9f3ab5622d8877441072561582d", size = 103094, upload-time = "2025-12-27T15:21:12.154Z" }, -] - -[[package]] -name = "fastembed" -version = "0.7.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, - { name = "loguru" }, - { name = "mmh3" }, - { name = "numpy" }, - { name = "onnxruntime" }, - { name = "pillow" }, - { name = "py-rust-stemmers" }, - { name = "requests" }, - { name = "tokenizers" }, - { name = "tqdm" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4c/c2/9c708680de1b54480161e0505f9d6d3d8eb47a1dc1a1f7f3c5106ba355d2/fastembed-0.7.4.tar.gz", hash = "sha256:8b8a4ea860ca295002f4754e8f5820a636e1065a9444959e18d5988d7f27093b", size = 68807, upload-time = "2025-12-05T12:08:10.447Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/3b/8da01492bc8b69184257d0c951bf0e77aec8ce110f06d8ce16c6ed9084f7/fastembed-0.7.4-py3-none-any.whl", hash = "sha256:79250a775f70bd6addb0e054204df042b5029ecae501e40e5bbd08e75844ad83", size = 108491, upload-time = "2025-12-05T12:08:09.059Z" }, -] - -[[package]] -name = "fastmcp" -version = "2.14.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "authlib" }, - { name = "cyclopts" }, - { name = "exceptiongroup" }, - { name = "httpx" }, - { name = "jsonref" }, - { name = "jsonschema-path" }, - { name = "mcp" }, - { name = "openapi-pydantic" }, - { name = "packaging" }, - { name = "platformdirs" }, - { name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] }, - { name = "pydantic", extra = ["email"] }, - { name = "pydocket" }, - { name = "pyperclip" }, - { name = "python-dotenv" }, - { name = "rich" }, - { name = "uvicorn" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/a9/a57d5e5629ebd4ef82b495a7f8e346ce29ef80cc86b15c8c40570701b94d/fastmcp-2.14.4.tar.gz", hash = "sha256:c01f19845c2adda0a70d59525c9193be64a6383014c8d40ce63345ac664053ff", size = 8302239, upload-time = "2026-01-22T17:29:37.024Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/41/c4d407e2218fd60d84acb6cc5131d28ff876afecf325e3fd9d27b8318581/fastmcp-2.14.4-py3-none-any.whl", hash = "sha256:5858cff5e4c8ea8107f9bca2609d71d6256e0fce74495912f6e51625e466c49a", size = 417788, upload-time = "2026-01-22T17:29:35.159Z" }, -] - -[[package]] -name = "fastuuid" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232, upload-time = "2025-10-19T22:19:22.402Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/a2/e78fcc5df65467f0d207661b7ef86c5b7ac62eea337c0c0fcedbeee6fb13/fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77e94728324b63660ebf8adb27055e92d2e4611645bf12ed9d88d30486471d0a", size = 510164, upload-time = "2025-10-19T22:31:45.635Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b3/c846f933f22f581f558ee63f81f29fa924acd971ce903dab1a9b6701816e/fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:caa1f14d2102cb8d353096bc6ef6c13b2c81f347e6ab9d6fbd48b9dea41c153d", size = 261837, upload-time = "2025-10-19T22:38:38.53Z" }, - { url = "https://files.pythonhosted.org/packages/54/ea/682551030f8c4fa9a769d9825570ad28c0c71e30cf34020b85c1f7ee7382/fastuuid-0.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d23ef06f9e67163be38cece704170486715b177f6baae338110983f99a72c070", size = 251370, upload-time = "2025-10-19T22:40:26.07Z" }, - { url = "https://files.pythonhosted.org/packages/14/dd/5927f0a523d8e6a76b70968e6004966ee7df30322f5fc9b6cdfb0276646a/fastuuid-0.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c9ec605ace243b6dbe3bd27ebdd5d33b00d8d1d3f580b39fdd15cd96fd71796", size = 277766, upload-time = "2025-10-19T22:37:23.779Z" }, - { url = "https://files.pythonhosted.org/packages/16/6e/c0fb547eef61293153348f12e0f75a06abb322664b34a1573a7760501336/fastuuid-0.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:808527f2407f58a76c916d6aa15d58692a4a019fdf8d4c32ac7ff303b7d7af09", size = 278105, upload-time = "2025-10-19T22:26:56.821Z" }, - { url = "https://files.pythonhosted.org/packages/2d/b1/b9c75e03b768f61cf2e84ee193dc18601aeaf89a4684b20f2f0e9f52b62c/fastuuid-0.14.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fb3c0d7fef6674bbeacdd6dbd386924a7b60b26de849266d1ff6602937675c8", size = 301564, upload-time = "2025-10-19T22:30:31.604Z" }, - { url = "https://files.pythonhosted.org/packages/fc/fa/f7395fdac07c7a54f18f801744573707321ca0cee082e638e36452355a9d/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab3f5d36e4393e628a4df337c2c039069344db5f4b9d2a3c9cea48284f1dd741", size = 459659, upload-time = "2025-10-19T22:31:32.341Z" }, - { url = "https://files.pythonhosted.org/packages/66/49/c9fd06a4a0b1f0f048aacb6599e7d96e5d6bc6fa680ed0d46bf111929d1b/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b9a0ca4f03b7e0b01425281ffd44e99d360e15c895f1907ca105854ed85e2057", size = 478430, upload-time = "2025-10-19T22:26:22.962Z" }, - { url = "https://files.pythonhosted.org/packages/be/9c/909e8c95b494e8e140e8be6165d5fc3f61fdc46198c1554df7b3e1764471/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3acdf655684cc09e60fb7e4cf524e8f42ea760031945aa8086c7eae2eeeabeb8", size = 450894, upload-time = "2025-10-19T22:27:01.647Z" }, - { url = "https://files.pythonhosted.org/packages/90/eb/d29d17521976e673c55ef7f210d4cdd72091a9ec6755d0fd4710d9b3c871/fastuuid-0.14.0-cp312-cp312-win32.whl", hash = "sha256:9579618be6280700ae36ac42c3efd157049fe4dd40ca49b021280481c78c3176", size = 154374, upload-time = "2025-10-19T22:29:19.879Z" }, - { url = "https://files.pythonhosted.org/packages/cc/fc/f5c799a6ea6d877faec0472d0b27c079b47c86b1cdc577720a5386483b36/fastuuid-0.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:d9e4332dc4ba054434a9594cbfaf7823b57993d7d8e7267831c3e059857cf397", size = 156550, upload-time = "2025-10-19T22:27:49.658Z" }, - { url = "https://files.pythonhosted.org/packages/a5/83/ae12dd39b9a39b55d7f90abb8971f1a5f3c321fd72d5aa83f90dc67fe9ed/fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77a09cb7427e7af74c594e409f7731a0cf887221de2f698e1ca0ebf0f3139021", size = 510720, upload-time = "2025-10-19T22:42:34.633Z" }, - { url = "https://files.pythonhosted.org/packages/53/b0/a4b03ff5d00f563cc7546b933c28cb3f2a07344b2aec5834e874f7d44143/fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:9bd57289daf7b153bfa3e8013446aa144ce5e8c825e9e366d455155ede5ea2dc", size = 262024, upload-time = "2025-10-19T22:30:25.482Z" }, - { url = "https://files.pythonhosted.org/packages/9c/6d/64aee0a0f6a58eeabadd582e55d0d7d70258ffdd01d093b30c53d668303b/fastuuid-0.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ac60fc860cdf3c3f327374db87ab8e064c86566ca8c49d2e30df15eda1b0c2d5", size = 251679, upload-time = "2025-10-19T22:36:14.096Z" }, - { url = "https://files.pythonhosted.org/packages/60/f5/a7e9cda8369e4f7919d36552db9b2ae21db7915083bc6336f1b0082c8b2e/fastuuid-0.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab32f74bd56565b186f036e33129da77db8be09178cd2f5206a5d4035fb2a23f", size = 277862, upload-time = "2025-10-19T22:36:23.302Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d3/8ce11827c783affffd5bd4d6378b28eb6cc6d2ddf41474006b8d62e7448e/fastuuid-0.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e678459cf4addaedd9936bbb038e35b3f6b2061330fd8f2f6a1d80414c0f87", size = 278278, upload-time = "2025-10-19T22:29:43.809Z" }, - { url = "https://files.pythonhosted.org/packages/a2/51/680fb6352d0bbade04036da46264a8001f74b7484e2fd1f4da9e3db1c666/fastuuid-0.14.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1e3cc56742f76cd25ecb98e4b82a25f978ccffba02e4bdce8aba857b6d85d87b", size = 301788, upload-time = "2025-10-19T22:36:06.825Z" }, - { url = "https://files.pythonhosted.org/packages/fa/7c/2014b5785bd8ebdab04ec857635ebd84d5ee4950186a577db9eff0fb8ff6/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cb9a030f609194b679e1660f7e32733b7a0f332d519c5d5a6a0a580991290022", size = 459819, upload-time = "2025-10-19T22:35:31.623Z" }, - { url = "https://files.pythonhosted.org/packages/01/d2/524d4ceeba9160e7a9bc2ea3e8f4ccf1ad78f3bde34090ca0c51f09a5e91/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:09098762aad4f8da3a888eb9ae01c84430c907a297b97166b8abc07b640f2995", size = 478546, upload-time = "2025-10-19T22:26:03.023Z" }, - { url = "https://files.pythonhosted.org/packages/bc/17/354d04951ce114bf4afc78e27a18cfbd6ee319ab1829c2d5fb5e94063ac6/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1383fff584fa249b16329a059c68ad45d030d5a4b70fb7c73a08d98fd53bcdab", size = 450921, upload-time = "2025-10-19T22:31:02.151Z" }, - { url = "https://files.pythonhosted.org/packages/fb/be/d7be8670151d16d88f15bb121c5b66cdb5ea6a0c2a362d0dcf30276ade53/fastuuid-0.14.0-cp313-cp313-win32.whl", hash = "sha256:a0809f8cc5731c066c909047f9a314d5f536c871a7a22e815cc4967c110ac9ad", size = 154559, upload-time = "2025-10-19T22:36:36.011Z" }, - { url = "https://files.pythonhosted.org/packages/22/1d/5573ef3624ceb7abf4a46073d3554e37191c868abc3aecd5289a72f9810a/fastuuid-0.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:0df14e92e7ad3276327631c9e7cec09e32572ce82089c55cb1bb8df71cf394ed", size = 156539, upload-time = "2025-10-19T22:33:35.898Z" }, - { url = "https://files.pythonhosted.org/packages/16/c9/8c7660d1fe3862e3f8acabd9be7fc9ad71eb270f1c65cce9a2b7a31329ab/fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b852a870a61cfc26c884af205d502881a2e59cc07076b60ab4a951cc0c94d1ad", size = 510600, upload-time = "2025-10-19T22:43:44.17Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f4/a989c82f9a90d0ad995aa957b3e572ebef163c5299823b4027986f133dfb/fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c7502d6f54cd08024c3ea9b3514e2d6f190feb2f46e6dbcd3747882264bb5f7b", size = 262069, upload-time = "2025-10-19T22:43:38.38Z" }, - { url = "https://files.pythonhosted.org/packages/da/6c/a1a24f73574ac995482b1326cf7ab41301af0fabaa3e37eeb6b3df00e6e2/fastuuid-0.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ca61b592120cf314cfd66e662a5b54a578c5a15b26305e1b8b618a6f22df714", size = 251543, upload-time = "2025-10-19T22:32:22.537Z" }, - { url = "https://files.pythonhosted.org/packages/1a/20/2a9b59185ba7a6c7b37808431477c2d739fcbdabbf63e00243e37bd6bf49/fastuuid-0.14.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa75b6657ec129d0abded3bec745e6f7ab642e6dba3a5272a68247e85f5f316f", size = 277798, upload-time = "2025-10-19T22:33:53.821Z" }, - { url = "https://files.pythonhosted.org/packages/ef/33/4105ca574f6ded0af6a797d39add041bcfb468a1255fbbe82fcb6f592da2/fastuuid-0.14.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8a0dfea3972200f72d4c7df02c8ac70bad1bb4c58d7e0ec1e6f341679073a7f", size = 278283, upload-time = "2025-10-19T22:29:02.812Z" }, - { url = "https://files.pythonhosted.org/packages/fe/8c/fca59f8e21c4deb013f574eae05723737ddb1d2937ce87cb2a5d20992dc3/fastuuid-0.14.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1bf539a7a95f35b419f9ad105d5a8a35036df35fdafae48fb2fd2e5f318f0d75", size = 301627, upload-time = "2025-10-19T22:35:54.985Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e2/f78c271b909c034d429218f2798ca4e89eeda7983f4257d7865976ddbb6c/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:9a133bf9cc78fdbd1179cb58a59ad0100aa32d8675508150f3658814aeefeaa4", size = 459778, upload-time = "2025-10-19T22:28:00.999Z" }, - { url = "https://files.pythonhosted.org/packages/1e/f0/5ff209d865897667a2ff3e7a572267a9ced8f7313919f6d6043aed8b1caa/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_i686.whl", hash = "sha256:f54d5b36c56a2d5e1a31e73b950b28a0d83eb0c37b91d10408875a5a29494bad", size = 478605, upload-time = "2025-10-19T22:36:21.764Z" }, - { url = "https://files.pythonhosted.org/packages/e0/c8/2ce1c78f983a2c4987ea865d9516dbdfb141a120fd3abb977ae6f02ba7ca/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:ec27778c6ca3393ef662e2762dba8af13f4ec1aaa32d08d77f71f2a70ae9feb8", size = 450837, upload-time = "2025-10-19T22:34:37.178Z" }, - { url = "https://files.pythonhosted.org/packages/df/60/dad662ec9a33b4a5fe44f60699258da64172c39bd041da2994422cdc40fe/fastuuid-0.14.0-cp314-cp314-win32.whl", hash = "sha256:e23fc6a83f112de4be0cc1990e5b127c27663ae43f866353166f87df58e73d06", size = 154532, upload-time = "2025-10-19T22:35:18.217Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/da4db31001e854025ffd26bc9ba0740a9cbba2c3259695f7c5834908b336/fastuuid-0.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:df61342889d0f5e7a32f7284e55ef95103f2110fee433c2ae7c2c0956d76ac8a", size = 156457, upload-time = "2025-10-19T22:33:44.579Z" }, -] - -[[package]] -name = "ffind" -version = "1.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/03/97fca9e84aa4f4e484884a8ca21fd4e2d07a7906a2228044f80fa28c1a21/ffind-1.6.1.tar.gz", hash = "sha256:1715b6b718eb53ec0b7e9877399b894a48ace1faa2f6a3d772376b7b0a181feb", size = 10234, upload-time = "2025-03-22T17:23:02.76Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/59/ae/5b13349f7f5f9977f2f3cbc26ba09098db2b1242b715a67df4fa2a75e372/ffind-1.6.1-py3-none-any.whl", hash = "sha256:6d79c604087f53fe0e1e3dc4d75a4cf9d4a424a7143289b5f2b9ea061b9a266a", size = 8689, upload-time = "2025-03-22T17:23:01.885Z" }, -] - -[[package]] -name = "filelock" -version = "3.20.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, -] - -[[package]] -name = "flake8" -version = "7.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mccabe" }, - { name = "pycodestyle" }, - { name = "pyflakes" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9b/af/fbfe3c4b5a657d79e5c47a2827a362f9e1b763336a52f926126aa6dc7123/flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872", size = 48326, upload-time = "2025-06-20T19:31:35.838Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/56/13ab06b4f93ca7cac71078fbe37fcea175d3216f31f85c3168a6bbd0bb9a/flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e", size = 57922, upload-time = "2025-06-20T19:31:34.425Z" }, -] - -[[package]] -name = "flatbuffers" -version = "25.12.19" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" }, -] - -[[package]] -name = "fonttools" -version = "4.61.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/ca/cf17b88a8df95691275a3d77dc0a5ad9907f328ae53acbe6795da1b2f5ed/fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69", size = 3565756, upload-time = "2025-12-12T17:31:24.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/16/7decaa24a1bd3a70c607b2e29f0adc6159f36a7e40eaba59846414765fd4/fonttools-4.61.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f3cb4a569029b9f291f88aafc927dd53683757e640081ca8c412781ea144565e", size = 2851593, upload-time = "2025-12-12T17:30:04.225Z" }, - { url = "https://files.pythonhosted.org/packages/94/98/3c4cb97c64713a8cf499b3245c3bf9a2b8fd16a3e375feff2aed78f96259/fonttools-4.61.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41a7170d042e8c0024703ed13b71893519a1a6d6e18e933e3ec7507a2c26a4b2", size = 2400231, upload-time = "2025-12-12T17:30:06.47Z" }, - { url = "https://files.pythonhosted.org/packages/b7/37/82dbef0f6342eb01f54bca073ac1498433d6ce71e50c3c3282b655733b31/fonttools-4.61.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10d88e55330e092940584774ee5e8a6971b01fc2f4d3466a1d6c158230880796", size = 4954103, upload-time = "2025-12-12T17:30:08.432Z" }, - { url = "https://files.pythonhosted.org/packages/6c/44/f3aeac0fa98e7ad527f479e161aca6c3a1e47bb6996b053d45226fe37bf2/fonttools-4.61.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15acc09befd16a0fb8a8f62bc147e1a82817542d72184acca9ce6e0aeda9fa6d", size = 5004295, upload-time = "2025-12-12T17:30:10.56Z" }, - { url = "https://files.pythonhosted.org/packages/14/e8/7424ced75473983b964d09f6747fa09f054a6d656f60e9ac9324cf40c743/fonttools-4.61.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e6bcdf33aec38d16508ce61fd81838f24c83c90a1d1b8c68982857038673d6b8", size = 4944109, upload-time = "2025-12-12T17:30:12.874Z" }, - { url = "https://files.pythonhosted.org/packages/c8/8b/6391b257fa3d0b553d73e778f953a2f0154292a7a7a085e2374b111e5410/fonttools-4.61.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5fade934607a523614726119164ff621e8c30e8fa1ffffbbd358662056ba69f0", size = 5093598, upload-time = "2025-12-12T17:30:15.79Z" }, - { url = "https://files.pythonhosted.org/packages/d9/71/fd2ea96cdc512d92da5678a1c98c267ddd4d8c5130b76d0f7a80f9a9fde8/fonttools-4.61.1-cp312-cp312-win32.whl", hash = "sha256:75da8f28eff26defba42c52986de97b22106cb8f26515b7c22443ebc9c2d3261", size = 2269060, upload-time = "2025-12-12T17:30:18.058Z" }, - { url = "https://files.pythonhosted.org/packages/80/3b/a3e81b71aed5a688e89dfe0e2694b26b78c7d7f39a5ffd8a7d75f54a12a8/fonttools-4.61.1-cp312-cp312-win_amd64.whl", hash = "sha256:497c31ce314219888c0e2fce5ad9178ca83fe5230b01a5006726cdf3ac9f24d9", size = 2319078, upload-time = "2025-12-12T17:30:22.862Z" }, - { url = "https://files.pythonhosted.org/packages/4b/cf/00ba28b0990982530addb8dc3e9e6f2fa9cb5c20df2abdda7baa755e8fe1/fonttools-4.61.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c56c488ab471628ff3bfa80964372fc13504ece601e0d97a78ee74126b2045c", size = 2846454, upload-time = "2025-12-12T17:30:24.938Z" }, - { url = "https://files.pythonhosted.org/packages/5a/ca/468c9a8446a2103ae645d14fee3f610567b7042aba85031c1c65e3ef7471/fonttools-4.61.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc492779501fa723b04d0ab1f5be046797fee17d27700476edc7ee9ae535a61e", size = 2398191, upload-time = "2025-12-12T17:30:27.343Z" }, - { url = "https://files.pythonhosted.org/packages/a3/4b/d67eedaed19def5967fade3297fed8161b25ba94699efc124b14fb68cdbc/fonttools-4.61.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:64102ca87e84261419c3747a0d20f396eb024bdbeb04c2bfb37e2891f5fadcb5", size = 4928410, upload-time = "2025-12-12T17:30:29.771Z" }, - { url = "https://files.pythonhosted.org/packages/b0/8d/6fb3494dfe61a46258cd93d979cf4725ded4eb46c2a4ca35e4490d84daea/fonttools-4.61.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c1b526c8d3f615a7b1867f38a9410849c8f4aef078535742198e942fba0e9bd", size = 4984460, upload-time = "2025-12-12T17:30:32.073Z" }, - { url = "https://files.pythonhosted.org/packages/f7/f1/a47f1d30b3dc00d75e7af762652d4cbc3dff5c2697a0dbd5203c81afd9c3/fonttools-4.61.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:41ed4b5ec103bd306bb68f81dc166e77409e5209443e5773cb4ed837bcc9b0d3", size = 4925800, upload-time = "2025-12-12T17:30:34.339Z" }, - { url = "https://files.pythonhosted.org/packages/a7/01/e6ae64a0981076e8a66906fab01539799546181e32a37a0257b77e4aa88b/fonttools-4.61.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b501c862d4901792adaec7c25b1ecc749e2662543f68bb194c42ba18d6eec98d", size = 5067859, upload-time = "2025-12-12T17:30:36.593Z" }, - { url = "https://files.pythonhosted.org/packages/73/aa/28e40b8d6809a9b5075350a86779163f074d2b617c15d22343fce81918db/fonttools-4.61.1-cp313-cp313-win32.whl", hash = "sha256:4d7092bb38c53bbc78e9255a59158b150bcdc115a1e3b3ce0b5f267dc35dd63c", size = 2267821, upload-time = "2025-12-12T17:30:38.478Z" }, - { url = "https://files.pythonhosted.org/packages/1a/59/453c06d1d83dc0951b69ef692d6b9f1846680342927df54e9a1ca91c6f90/fonttools-4.61.1-cp313-cp313-win_amd64.whl", hash = "sha256:21e7c8d76f62ab13c9472ccf74515ca5b9a761d1bde3265152a6dc58700d895b", size = 2318169, upload-time = "2025-12-12T17:30:40.951Z" }, - { url = "https://files.pythonhosted.org/packages/32/8f/4e7bf82c0cbb738d3c2206c920ca34ca74ef9dabde779030145d28665104/fonttools-4.61.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fff4f534200a04b4a36e7ae3cb74493afe807b517a09e99cb4faa89a34ed6ecd", size = 2846094, upload-time = "2025-12-12T17:30:43.511Z" }, - { url = "https://files.pythonhosted.org/packages/71/09/d44e45d0a4f3a651f23a1e9d42de43bc643cce2971b19e784cc67d823676/fonttools-4.61.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d9203500f7c63545b4ce3799319fe4d9feb1a1b89b28d3cb5abd11b9dd64147e", size = 2396589, upload-time = "2025-12-12T17:30:45.681Z" }, - { url = "https://files.pythonhosted.org/packages/89/18/58c64cafcf8eb677a99ef593121f719e6dcbdb7d1c594ae5a10d4997ca8a/fonttools-4.61.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa646ecec9528bef693415c79a86e733c70a4965dd938e9a226b0fc64c9d2e6c", size = 4877892, upload-time = "2025-12-12T17:30:47.709Z" }, - { url = "https://files.pythonhosted.org/packages/8a/ec/9e6b38c7ba1e09eb51db849d5450f4c05b7e78481f662c3b79dbde6f3d04/fonttools-4.61.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f35ad7805edba3aac1a3710d104592df59f4b957e30108ae0ba6c10b11dd75", size = 4972884, upload-time = "2025-12-12T17:30:49.656Z" }, - { url = "https://files.pythonhosted.org/packages/5e/87/b5339da8e0256734ba0dbbf5b6cdebb1dd79b01dc8c270989b7bcd465541/fonttools-4.61.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b931ae8f62db78861b0ff1ac017851764602288575d65b8e8ff1963fed419063", size = 4924405, upload-time = "2025-12-12T17:30:51.735Z" }, - { url = "https://files.pythonhosted.org/packages/0b/47/e3409f1e1e69c073a3a6fd8cb886eb18c0bae0ee13db2c8d5e7f8495e8b7/fonttools-4.61.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b148b56f5de675ee16d45e769e69f87623a4944f7443850bf9a9376e628a89d2", size = 5035553, upload-time = "2025-12-12T17:30:54.823Z" }, - { url = "https://files.pythonhosted.org/packages/bf/b6/1f6600161b1073a984294c6c031e1a56ebf95b6164249eecf30012bb2e38/fonttools-4.61.1-cp314-cp314-win32.whl", hash = "sha256:9b666a475a65f4e839d3d10473fad6d47e0a9db14a2f4a224029c5bfde58ad2c", size = 2271915, upload-time = "2025-12-12T17:30:57.913Z" }, - { url = "https://files.pythonhosted.org/packages/52/7b/91e7b01e37cc8eb0e1f770d08305b3655e4f002fc160fb82b3390eabacf5/fonttools-4.61.1-cp314-cp314-win_amd64.whl", hash = "sha256:4f5686e1fe5fce75d82d93c47a438a25bf0d1319d2843a926f741140b2b16e0c", size = 2323487, upload-time = "2025-12-12T17:30:59.804Z" }, - { url = "https://files.pythonhosted.org/packages/39/5c/908ad78e46c61c3e3ed70c3b58ff82ab48437faf84ec84f109592cabbd9f/fonttools-4.61.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:e76ce097e3c57c4bcb67c5aa24a0ecdbd9f74ea9219997a707a4061fbe2707aa", size = 2929571, upload-time = "2025-12-12T17:31:02.574Z" }, - { url = "https://files.pythonhosted.org/packages/bd/41/975804132c6dea64cdbfbaa59f3518a21c137a10cccf962805b301ac6ab2/fonttools-4.61.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9cfef3ab326780c04d6646f68d4b4742aae222e8b8ea1d627c74e38afcbc9d91", size = 2435317, upload-time = "2025-12-12T17:31:04.974Z" }, - { url = "https://files.pythonhosted.org/packages/b0/5a/aef2a0a8daf1ebaae4cfd83f84186d4a72ee08fd6a8451289fcd03ffa8a4/fonttools-4.61.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a75c301f96db737e1c5ed5fd7d77d9c34466de16095a266509e13da09751bd19", size = 4882124, upload-time = "2025-12-12T17:31:07.456Z" }, - { url = "https://files.pythonhosted.org/packages/80/33/d6db3485b645b81cea538c9d1c9219d5805f0877fda18777add4671c5240/fonttools-4.61.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:91669ccac46bbc1d09e9273546181919064e8df73488ea087dcac3e2968df9ba", size = 5100391, upload-time = "2025-12-12T17:31:09.732Z" }, - { url = "https://files.pythonhosted.org/packages/6c/d6/675ba631454043c75fcf76f0ca5463eac8eb0666ea1d7badae5fea001155/fonttools-4.61.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c33ab3ca9d3ccd581d58e989d67554e42d8d4ded94ab3ade3508455fe70e65f7", size = 4978800, upload-time = "2025-12-12T17:31:11.681Z" }, - { url = "https://files.pythonhosted.org/packages/7f/33/d3ec753d547a8d2bdaedd390d4a814e8d5b45a093d558f025c6b990b554c/fonttools-4.61.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:664c5a68ec406f6b1547946683008576ef8b38275608e1cee6c061828171c118", size = 5006426, upload-time = "2025-12-12T17:31:13.764Z" }, - { url = "https://files.pythonhosted.org/packages/b4/40/cc11f378b561a67bea850ab50063366a0d1dd3f6d0a30ce0f874b0ad5664/fonttools-4.61.1-cp314-cp314t-win32.whl", hash = "sha256:aed04cabe26f30c1647ef0e8fbb207516fd40fe9472e9439695f5c6998e60ac5", size = 2335377, upload-time = "2025-12-12T17:31:16.49Z" }, - { url = "https://files.pythonhosted.org/packages/e4/ff/c9a2b66b39f8628531ea58b320d66d951267c98c6a38684daa8f50fb02f8/fonttools-4.61.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2180f14c141d2f0f3da43f3a81bc8aa4684860f6b0e6f9e165a4831f24e6a23b", size = 2400613, upload-time = "2025-12-12T17:31:18.769Z" }, - { url = "https://files.pythonhosted.org/packages/c7/4e/ce75a57ff3aebf6fc1f4e9d508b8e5810618a33d900ad6c19eb30b290b97/fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371", size = 1148996, upload-time = "2025-12-12T17:31:21.03Z" }, -] - -[[package]] -name = "frozenlist" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, - { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, - { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, - { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, - { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, - { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, - { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, - { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, - { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, - { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, - { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, - { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, - { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, - { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, - { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, - { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, - { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, - { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, - { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, - { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, - { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, - { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, - { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, - { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, - { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, - { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, - { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, - { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, - { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, - { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, - { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, - { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, - { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, - { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, - { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, - { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, - { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, - { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, - { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, - { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, - { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, - { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, - { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, - { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, - { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, - { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, - { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, - { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, - { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, - { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, - { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, - { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, - { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, - { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, - { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, - { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, - { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, - { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, - { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, - { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, - { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, - { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, - { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, -] - -[[package]] -name = "fsspec" -version = "2026.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d5/7d/5df2650c57d47c57232af5ef4b4fdbff182070421e405e0d62c6cdbfaa87/fsspec-2026.1.0.tar.gz", hash = "sha256:e987cb0496a0d81bba3a9d1cee62922fb395e7d4c3b575e57f547953334fe07b", size = 310496, upload-time = "2026-01-09T15:21:35.562Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/c9/97cc5aae1648dcb851958a3ddf73ccd7dbe5650d95203ecb4d7720b4cdbf/fsspec-2026.1.0-py3-none-any.whl", hash = "sha256:cb76aa913c2285a3b49bdd5fc55b1d7c708d7208126b60f2eb8194fe1b4cbdcc", size = 201838, upload-time = "2026-01-09T15:21:34.041Z" }, -] - -[[package]] -name = "gitdb" -version = "4.0.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "smmap" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, -] - -[[package]] -name = "gitpython" -version = "3.1.46" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "gitdb" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", size = 215371, upload-time = "2026-01-01T15:37:32.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620, upload-time = "2026-01-01T15:37:30.574Z" }, -] - -[[package]] -name = "googleapis-common-protos" -version = "1.72.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e5/7b/adfd75544c415c487b33061fe7ae526165241c1ea133f9a9125a56b39fd8/googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5", size = 147433, upload-time = "2025-11-06T18:29:24.087Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515, upload-time = "2025-11-06T18:29:13.14Z" }, -] - -[[package]] -name = "grep-ast" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pathspec" }, - { name = "tree-sitter-language-pack" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/67/82/a87079945a7c15d242cb586ae22e17952132439eaa9c878ec5fbdc61c54d/grep_ast-0.9.0.tar.gz", hash = "sha256:620a242a4493e6721338d1c9a6c234ae651f8774f4924a6dcf90f6865d4b2ee3", size = 14125, upload-time = "2025-05-08T01:08:28.371Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/79/29f1373b2ce1eec37c03aefbc17194c2470d8b61ede288e5043231825999/grep_ast-0.9.0-py3-none-any.whl", hash = "sha256:a3973dca99f1abc026a01bbbc70e00a63860c8ff94a56182ff18b089836826d7", size = 13918, upload-time = "2025-05-08T01:08:27.481Z" }, -] - -[[package]] -name = "grpcio" -version = "1.76.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b6/e0/318c1ce3ae5a17894d5791e87aea147587c9e702f24122cc7a5c8bbaeeb1/grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73", size = 12785182, upload-time = "2025-10-21T16:23:12.106Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/05/8e29121994b8d959ffa0afd28996d452f291b48cfc0875619de0bde2c50c/grpcio-1.76.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:81fd9652b37b36f16138611c7e884eb82e0cec137c40d3ef7c3f9b3ed00f6ed8", size = 5799718, upload-time = "2025-10-21T16:21:17.939Z" }, - { url = "https://files.pythonhosted.org/packages/d9/75/11d0e66b3cdf998c996489581bdad8900db79ebd83513e45c19548f1cba4/grpcio-1.76.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:04bbe1bfe3a68bbfd4e52402ab7d4eb59d72d02647ae2042204326cf4bbad280", size = 11825627, upload-time = "2025-10-21T16:21:20.466Z" }, - { url = "https://files.pythonhosted.org/packages/28/50/2f0aa0498bc188048f5d9504dcc5c2c24f2eb1a9337cd0fa09a61a2e75f0/grpcio-1.76.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d388087771c837cdb6515539f43b9d4bf0b0f23593a24054ac16f7a960be16f4", size = 6359167, upload-time = "2025-10-21T16:21:23.122Z" }, - { url = "https://files.pythonhosted.org/packages/66/e5/bbf0bb97d29ede1d59d6588af40018cfc345b17ce979b7b45424628dc8bb/grpcio-1.76.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f8f757bebaaea112c00dba718fc0d3260052ce714e25804a03f93f5d1c6cc11", size = 7044267, upload-time = "2025-10-21T16:21:25.995Z" }, - { url = "https://files.pythonhosted.org/packages/f5/86/f6ec2164f743d9609691115ae8ece098c76b894ebe4f7c94a655c6b03e98/grpcio-1.76.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:980a846182ce88c4f2f7e2c22c56aefd515daeb36149d1c897f83cf57999e0b6", size = 6573963, upload-time = "2025-10-21T16:21:28.631Z" }, - { url = "https://files.pythonhosted.org/packages/60/bc/8d9d0d8505feccfdf38a766d262c71e73639c165b311c9457208b56d92ae/grpcio-1.76.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f92f88e6c033db65a5ae3d97905c8fea9c725b63e28d5a75cb73b49bda5024d8", size = 7164484, upload-time = "2025-10-21T16:21:30.837Z" }, - { url = "https://files.pythonhosted.org/packages/67/e6/5d6c2fc10b95edf6df9b8f19cf10a34263b7fd48493936fffd5085521292/grpcio-1.76.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4baf3cbe2f0be3289eb68ac8ae771156971848bb8aaff60bad42005539431980", size = 8127777, upload-time = "2025-10-21T16:21:33.577Z" }, - { url = "https://files.pythonhosted.org/packages/3f/c8/dce8ff21c86abe025efe304d9e31fdb0deaaa3b502b6a78141080f206da0/grpcio-1.76.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:615ba64c208aaceb5ec83bfdce7728b80bfeb8be97562944836a7a0a9647d882", size = 7594014, upload-time = "2025-10-21T16:21:41.882Z" }, - { url = "https://files.pythonhosted.org/packages/e0/42/ad28191ebf983a5d0ecef90bab66baa5a6b18f2bfdef9d0a63b1973d9f75/grpcio-1.76.0-cp312-cp312-win32.whl", hash = "sha256:45d59a649a82df5718fd9527ce775fd66d1af35e6d31abdcdc906a49c6822958", size = 3984750, upload-time = "2025-10-21T16:21:44.006Z" }, - { url = "https://files.pythonhosted.org/packages/9e/00/7bd478cbb851c04a48baccaa49b75abaa8e4122f7d86da797500cccdd771/grpcio-1.76.0-cp312-cp312-win_amd64.whl", hash = "sha256:c088e7a90b6017307f423efbb9d1ba97a22aa2170876223f9709e9d1de0b5347", size = 4704003, upload-time = "2025-10-21T16:21:46.244Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ed/71467ab770effc9e8cef5f2e7388beb2be26ed642d567697bb103a790c72/grpcio-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:26ef06c73eb53267c2b319f43e6634c7556ea37672029241a056629af27c10e2", size = 5807716, upload-time = "2025-10-21T16:21:48.475Z" }, - { url = "https://files.pythonhosted.org/packages/2c/85/c6ed56f9817fab03fa8a111ca91469941fb514e3e3ce6d793cb8f1e1347b/grpcio-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:45e0111e73f43f735d70786557dc38141185072d7ff8dc1829d6a77ac1471468", size = 11821522, upload-time = "2025-10-21T16:21:51.142Z" }, - { url = "https://files.pythonhosted.org/packages/ac/31/2b8a235ab40c39cbc141ef647f8a6eb7b0028f023015a4842933bc0d6831/grpcio-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83d57312a58dcfe2a3a0f9d1389b299438909a02db60e2f2ea2ae2d8034909d3", size = 6362558, upload-time = "2025-10-21T16:21:54.213Z" }, - { url = "https://files.pythonhosted.org/packages/bd/64/9784eab483358e08847498ee56faf8ff6ea8e0a4592568d9f68edc97e9e9/grpcio-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3e2a27c89eb9ac3d81ec8835e12414d73536c6e620355d65102503064a4ed6eb", size = 7049990, upload-time = "2025-10-21T16:21:56.476Z" }, - { url = "https://files.pythonhosted.org/packages/2b/94/8c12319a6369434e7a184b987e8e9f3b49a114c489b8315f029e24de4837/grpcio-1.76.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61f69297cba3950a524f61c7c8ee12e55c486cb5f7db47ff9dcee33da6f0d3ae", size = 6575387, upload-time = "2025-10-21T16:21:59.051Z" }, - { url = "https://files.pythonhosted.org/packages/15/0f/f12c32b03f731f4a6242f771f63039df182c8b8e2cf8075b245b409259d4/grpcio-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a15c17af8839b6801d554263c546c69c4d7718ad4321e3166175b37eaacca77", size = 7166668, upload-time = "2025-10-21T16:22:02.049Z" }, - { url = "https://files.pythonhosted.org/packages/ff/2d/3ec9ce0c2b1d92dd59d1c3264aaec9f0f7c817d6e8ac683b97198a36ed5a/grpcio-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25a18e9810fbc7e7f03ec2516addc116a957f8cbb8cbc95ccc80faa072743d03", size = 8124928, upload-time = "2025-10-21T16:22:04.984Z" }, - { url = "https://files.pythonhosted.org/packages/1a/74/fd3317be5672f4856bcdd1a9e7b5e17554692d3db9a3b273879dc02d657d/grpcio-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:931091142fd8cc14edccc0845a79248bc155425eee9a98b2db2ea4f00a235a42", size = 7589983, upload-time = "2025-10-21T16:22:07.881Z" }, - { url = "https://files.pythonhosted.org/packages/45/bb/ca038cf420f405971f19821c8c15bcbc875505f6ffadafe9ffd77871dc4c/grpcio-1.76.0-cp313-cp313-win32.whl", hash = "sha256:5e8571632780e08526f118f74170ad8d50fb0a48c23a746bef2a6ebade3abd6f", size = 3984727, upload-time = "2025-10-21T16:22:10.032Z" }, - { url = "https://files.pythonhosted.org/packages/41/80/84087dc56437ced7cdd4b13d7875e7439a52a261e3ab4e06488ba6173b0a/grpcio-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:f9f7bd5faab55f47231ad8dba7787866b69f5e93bc306e3915606779bbfb4ba8", size = 4702799, upload-time = "2025-10-21T16:22:12.709Z" }, - { url = "https://files.pythonhosted.org/packages/b4/46/39adac80de49d678e6e073b70204091e76631e03e94928b9ea4ecf0f6e0e/grpcio-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:ff8a59ea85a1f2191a0ffcc61298c571bc566332f82e5f5be1b83c9d8e668a62", size = 5808417, upload-time = "2025-10-21T16:22:15.02Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f5/a4531f7fb8b4e2a60b94e39d5d924469b7a6988176b3422487be61fe2998/grpcio-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06c3d6b076e7b593905d04fdba6a0525711b3466f43b3400266f04ff735de0cd", size = 11828219, upload-time = "2025-10-21T16:22:17.954Z" }, - { url = "https://files.pythonhosted.org/packages/4b/1c/de55d868ed7a8bd6acc6b1d6ddc4aa36d07a9f31d33c912c804adb1b971b/grpcio-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd5ef5932f6475c436c4a55e4336ebbe47bd3272be04964a03d316bbf4afbcbc", size = 6367826, upload-time = "2025-10-21T16:22:20.721Z" }, - { url = "https://files.pythonhosted.org/packages/59/64/99e44c02b5adb0ad13ab3adc89cb33cb54bfa90c74770f2607eea629b86f/grpcio-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b331680e46239e090f5b3cead313cc772f6caa7d0fc8de349337563125361a4a", size = 7049550, upload-time = "2025-10-21T16:22:23.637Z" }, - { url = "https://files.pythonhosted.org/packages/43/28/40a5be3f9a86949b83e7d6a2ad6011d993cbe9b6bd27bea881f61c7788b6/grpcio-1.76.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2229ae655ec4e8999599469559e97630185fdd53ae1e8997d147b7c9b2b72cba", size = 6575564, upload-time = "2025-10-21T16:22:26.016Z" }, - { url = "https://files.pythonhosted.org/packages/4b/a9/1be18e6055b64467440208a8559afac243c66a8b904213af6f392dc2212f/grpcio-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:490fa6d203992c47c7b9e4a9d39003a0c2bcc1c9aa3c058730884bbbb0ee9f09", size = 7176236, upload-time = "2025-10-21T16:22:28.362Z" }, - { url = "https://files.pythonhosted.org/packages/0f/55/dba05d3fcc151ce6e81327541d2cc8394f442f6b350fead67401661bf041/grpcio-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:479496325ce554792dba6548fae3df31a72cef7bad71ca2e12b0e58f9b336bfc", size = 8125795, upload-time = "2025-10-21T16:22:31.075Z" }, - { url = "https://files.pythonhosted.org/packages/4a/45/122df922d05655f63930cf42c9e3f72ba20aadb26c100ee105cad4ce4257/grpcio-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c9b93f79f48b03ada57ea24725d83a30284a012ec27eab2cf7e50a550cbbbcc", size = 7592214, upload-time = "2025-10-21T16:22:33.831Z" }, - { url = "https://files.pythonhosted.org/packages/4a/6e/0b899b7f6b66e5af39e377055fb4a6675c9ee28431df5708139df2e93233/grpcio-1.76.0-cp314-cp314-win32.whl", hash = "sha256:747fa73efa9b8b1488a95d0ba1039c8e2dca0f741612d80415b1e1c560febf4e", size = 4062961, upload-time = "2025-10-21T16:22:36.468Z" }, - { url = "https://files.pythonhosted.org/packages/19/41/0b430b01a2eb38ee887f88c1f07644a1df8e289353b78e82b37ef988fb64/grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e", size = 4834462, upload-time = "2025-10-21T16:22:39.772Z" }, -] - -[[package]] -name = "grpcio-tools" -version = "1.76.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "grpcio" }, - { name = "protobuf" }, - { name = "setuptools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a0/77/17d60d636ccd86a0db0eccc24d02967bbc3eea86b9db7324b04507ebaa40/grpcio_tools-1.76.0.tar.gz", hash = "sha256:ce80169b5e6adf3e8302f3ebb6cb0c3a9f08089133abca4b76ad67f751f5ad88", size = 5390807, upload-time = "2025-10-21T16:26:55.416Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/ca/a931c1439cabfe305c9afd07e233150cd0565aa062c20d1ee412ed188852/grpcio_tools-1.76.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:4ad555b8647de1ebaffb25170249f89057721ffb74f7da96834a07b4855bb46a", size = 2546852, upload-time = "2025-10-21T16:25:15.024Z" }, - { url = "https://files.pythonhosted.org/packages/4c/07/935cfbb7dccd602723482a86d43fbd992f91e9867bca0056a1e9f348473e/grpcio_tools-1.76.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:243af7c8fc7ff22a40a42eb8e0f6f66963c1920b75aae2a2ec503a9c3c8b31c1", size = 5841777, upload-time = "2025-10-21T16:25:17.425Z" }, - { url = "https://files.pythonhosted.org/packages/e4/92/8fcb5acebdccb647e0fa3f002576480459f6cf81e79692d7b3c4d6e29605/grpcio_tools-1.76.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8207b890f423142cc0025d041fb058f7286318df6a049565c27869d73534228b", size = 2594004, upload-time = "2025-10-21T16:25:19.809Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ea/64838e8113b7bfd4842b15c815a7354cb63242fdce9d6648d894b5d50897/grpcio_tools-1.76.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3dafa34c2626a6691d103877e8a145f54c34cf6530975f695b396ed2fc5c98f8", size = 2905563, upload-time = "2025-10-21T16:25:21.889Z" }, - { url = "https://files.pythonhosted.org/packages/a6/d6/53798827d821098219e58518b6db52161ce4985620850aa74ce3795da8a7/grpcio_tools-1.76.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:30f1d2dda6ece285b3d9084e94f66fa721ebdba14ae76b2bc4c581c8a166535c", size = 2656936, upload-time = "2025-10-21T16:25:24.369Z" }, - { url = "https://files.pythonhosted.org/packages/89/a3/d9c1cefc46a790eec520fe4e70e87279abb01a58b1a3b74cf93f62b824a2/grpcio_tools-1.76.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a889af059dc6dbb82d7b417aa581601316e364fe12eb54c1b8d95311ea50916d", size = 3109811, upload-time = "2025-10-21T16:25:26.711Z" }, - { url = "https://files.pythonhosted.org/packages/50/75/5997752644b73b5d59377d333a51c8a916606df077f5a487853e37dca289/grpcio_tools-1.76.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c3f2c3c44c56eb5d479ab178f0174595d0a974c37dade442f05bb73dfec02f31", size = 3658786, upload-time = "2025-10-21T16:25:28.819Z" }, - { url = "https://files.pythonhosted.org/packages/84/47/dcf8380df4bd7931ffba32fc6adc2de635b6569ca27fdec7121733797062/grpcio_tools-1.76.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:479ce02dff684046f909a487d452a83a96b4231f7c70a3b218a075d54e951f56", size = 3325144, upload-time = "2025-10-21T16:25:30.863Z" }, - { url = "https://files.pythonhosted.org/packages/04/88/ea3e5fdb874d8c2d04488e4b9d05056537fba70915593f0c283ac77df188/grpcio_tools-1.76.0-cp312-cp312-win32.whl", hash = "sha256:9ba4bb539936642a44418b38ee6c3e8823c037699e2cb282bd8a44d76a4be833", size = 993523, upload-time = "2025-10-21T16:25:32.594Z" }, - { url = "https://files.pythonhosted.org/packages/de/b1/ce7d59d147675ec191a55816be46bc47a343b5ff07279eef5817c09cc53e/grpcio_tools-1.76.0-cp312-cp312-win_amd64.whl", hash = "sha256:0cd489016766b05f9ed8a6b6596004b62c57d323f49593eac84add032a6d43f7", size = 1158493, upload-time = "2025-10-21T16:25:34.5Z" }, - { url = "https://files.pythonhosted.org/packages/13/01/b16fe73f129df49811d886dc99d3813a33cf4d1c6e101252b81c895e929f/grpcio_tools-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:ff48969f81858397ef33a36b326f2dbe2053a48b254593785707845db73c8f44", size = 2546312, upload-time = "2025-10-21T16:25:37.138Z" }, - { url = "https://files.pythonhosted.org/packages/25/17/2594c5feb76bb0b25bfbf91ec1075b276e1b2325e4bc7ea649a7b5dbf353/grpcio_tools-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa2f030fd0ef17926026ee8e2b700e388d3439155d145c568fa6b32693277613", size = 5839627, upload-time = "2025-10-21T16:25:40.082Z" }, - { url = "https://files.pythonhosted.org/packages/c7/c6/097b1aa26fbf72fb3cdb30138a2788529e4f10d8759de730a83f5c06726e/grpcio_tools-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bacbf3c54f88c38de8e28f8d9b97c90b76b105fb9ddef05d2c50df01b32b92af", size = 2592817, upload-time = "2025-10-21T16:25:42.301Z" }, - { url = "https://files.pythonhosted.org/packages/03/78/d1d985b48592a674509a85438c1a3d4c36304ddfc99d1b05d27233b51062/grpcio_tools-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0d4e4afe9a0e3c24fad2f1af45f98cf8700b2bfc4d790795756ba035d2ea7bdc", size = 2905186, upload-time = "2025-10-21T16:25:44.395Z" }, - { url = "https://files.pythonhosted.org/packages/b9/0e/770afbb47f0b5f594b93a7b46a95b892abda5eebe60efb511e96cee52170/grpcio_tools-1.76.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fbbd4e1fc5af98001ceef5e780e8c10921d94941c3809238081e73818ef707f1", size = 2656188, upload-time = "2025-10-21T16:25:46.942Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2b/017c2fcf4c5d3cf00cf7d5ce21eb88521de0d89bdcf26538ad2862ec6d07/grpcio_tools-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b05efe5a59883ab8292d596657273a60e0c3e4f5a9723c32feb9fc3a06f2f3ef", size = 3109141, upload-time = "2025-10-21T16:25:49.137Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5f/2495f88e3d50c6f2c2da2752bad4fa3a30c52ece6c9d8b0c636cd8b1430b/grpcio_tools-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:be483b90e62b7892eb71fa1fc49750bee5b2ee35b5ec99dd2b32bed4bedb5d71", size = 3657892, upload-time = "2025-10-21T16:25:52.362Z" }, - { url = "https://files.pythonhosted.org/packages/5e/1d/c4f39d31b19d9baf35d900bf3f969ce1c842f63a8560c8003ed2e5474760/grpcio_tools-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:630cd7fd3e8a63e20703a7ad816979073c2253e591b5422583c27cae2570de73", size = 3324778, upload-time = "2025-10-21T16:25:54.629Z" }, - { url = "https://files.pythonhosted.org/packages/b4/b6/35ee3a6e4af85a93da28428f81f4b29bcb36f6986b486ad71910fcc02e25/grpcio_tools-1.76.0-cp313-cp313-win32.whl", hash = "sha256:eb2567280f9f6da5444043f0e84d8408c7a10df9ba3201026b30e40ef3814736", size = 993084, upload-time = "2025-10-21T16:25:56.52Z" }, - { url = "https://files.pythonhosted.org/packages/f3/7a/5bd72344d86ee860e5920c9a7553cfe3bc7b1fce79f18c00ac2497f5799f/grpcio_tools-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:0071b1c0bd0f5f9d292dca4efab32c92725d418e57f9c60acdc33c0172af8b53", size = 1158151, upload-time = "2025-10-21T16:25:58.468Z" }, - { url = "https://files.pythonhosted.org/packages/f0/c0/aa20eebe8f3553b7851643e9c88d237c3a6ca30ade646897e25dbb27be99/grpcio_tools-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:c53c5719ef2a435997755abde3826ba4087174bd432aa721d8fac781fcea79e4", size = 2546297, upload-time = "2025-10-21T16:26:01.258Z" }, - { url = "https://files.pythonhosted.org/packages/d9/98/6af702804934443c1d0d4d27d21b990d92d22ddd1b6bec6b056558cbbffa/grpcio_tools-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:e3db1300d7282264639eeee7243f5de7e6a7c0283f8bf05d66c0315b7b0f0b36", size = 5839804, upload-time = "2025-10-21T16:26:05.495Z" }, - { url = "https://files.pythonhosted.org/packages/ea/8d/7725fa7b134ef8405ffe0a37c96eeb626e5af15d70e1bdac4f8f1abf842e/grpcio_tools-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b018a4b7455a7e8c16d0fdb3655a6ba6c9536da6de6c5d4f11b6bb73378165b", size = 2593922, upload-time = "2025-10-21T16:26:07.563Z" }, - { url = "https://files.pythonhosted.org/packages/de/ff/5b6b5012c79fa72f9107dc13f7226d9ce7e059ea639fd8c779e0dd284386/grpcio_tools-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ec6e4de3866e47cfde56607b1fae83ecc5aa546e06dec53de11f88063f4b5275", size = 2905327, upload-time = "2025-10-21T16:26:09.668Z" }, - { url = "https://files.pythonhosted.org/packages/24/01/2691d369ea462cd6b6c92544122885ca01f7fa5ac75dee023e975e675858/grpcio_tools-1.76.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b8da4d828883913f1852bdd67383713ae5c11842f6c70f93f31893eab530aead", size = 2656214, upload-time = "2025-10-21T16:26:11.773Z" }, - { url = "https://files.pythonhosted.org/packages/6a/e7/3f8856e6ec3dd492336a91572993344966f237b0e3819fbe96437b19d313/grpcio_tools-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5c120c2cf4443121800e7f9bcfe2e94519fa25f3bb0b9882359dd3b252c78a7b", size = 3109889, upload-time = "2025-10-21T16:26:15.058Z" }, - { url = "https://files.pythonhosted.org/packages/f3/e4/ce5248072e47db276dc7e069e93978dcde490c959788ce7cce8081d0bfdc/grpcio_tools-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8b7df5591d699cd9076065f1f15049e9c3597e0771bea51c8c97790caf5e4197", size = 3657939, upload-time = "2025-10-21T16:26:17.34Z" }, - { url = "https://files.pythonhosted.org/packages/f6/df/81ff88af93c52135e425cd5ec9fe8b186169c7d5f9e0409bdf2bbedc3919/grpcio_tools-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a25048c5f984d33e3f5b6ad7618e98736542461213ade1bd6f2fcfe8ce804e3d", size = 3324752, upload-time = "2025-10-21T16:26:20.092Z" }, - { url = "https://files.pythonhosted.org/packages/35/3d/f6b83044afbf6522254a3b509515a00fed16a819c87731a478dbdd1d35c1/grpcio_tools-1.76.0-cp314-cp314-win32.whl", hash = "sha256:4b77ce6b6c17869858cfe14681ad09ed3a8a80e960e96035de1fd87f78158740", size = 1015578, upload-time = "2025-10-21T16:26:22.517Z" }, - { url = "https://files.pythonhosted.org/packages/95/4d/31236cddb7ffb09ba4a49f4f56d2608fec3bbb21c7a0a975d93bca7cd22e/grpcio_tools-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:2ccd2c8d041351cc29d0fc4a84529b11ee35494a700b535c1f820b642f2a72fc", size = 1190242, upload-time = "2025-10-21T16:26:25.296Z" }, -] - -[[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 = "h2" -version = "4.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hpack" }, - { name = "hyperframe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026, upload-time = "2025-08-23T18:12:19.778Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779, upload-time = "2025-08-23T18:12:17.779Z" }, -] - -[[package]] -name = "hanzo" -version = "0.4.1" -source = { editable = "." } -dependencies = [ - { name = "click" }, - { name = "hanzo-cli" }, - { name = "hanzo-kms" }, - { name = "httpx" }, - { name = "pydantic" }, - { name = "pyyaml" }, - { name = "rich" }, -] - -[package.optional-dependencies] -agents = [ - { name = "hanzo-agents" }, - { name = "hanzo-network" }, -] -ai = [ - { name = "anthropic" }, - { name = "hanzoai" }, - { name = "openai" }, -] -all = [ - { name = "anthropic" }, - { name = "hanzo-aci" }, - { name = "hanzo-agents" }, - { name = "hanzo-mcp" }, - { name = "hanzo-memory" }, - { name = "hanzo-network" }, - { name = "hanzoai" }, - { name = "openai" }, - { name = "prompt-toolkit" }, - { name = "qrcode" }, -] -cron = [ - { name = "croniter" }, - { name = "redis" }, -] -dev = [ - { name = "hanzo-aci" }, -] -documentdb = [ - { name = "motor" }, - { name = "pymongo" }, -] -functions = [ - { name = "httpx" }, -] -infra = [ - { name = "aiobotocore" }, - { name = "botocore" }, - { name = "croniter" }, - { name = "httpx" }, - { name = "meilisearch-python-sdk" }, - { name = "motor" }, - { name = "nats-py" }, - { name = "pymongo" }, - { name = "qdrant-client" }, - { name = "redis" }, - { name = "temporalio" }, -] -interactive = [ - { name = "prompt-toolkit" }, - { name = "qrcode" }, -] -kv = [ - { name = "redis" }, -] -mcp = [ - { name = "hanzo-mcp" }, -] -pubsub = [ - { name = "nats-py" }, -] -queues = [ - { name = "redis" }, -] -search = [ - { name = "meilisearch-python-sdk" }, -] -storage = [ - { name = "aiobotocore" }, - { name = "botocore" }, -] -tasks = [ - { name = "temporalio" }, -] -vector = [ - { name = "qdrant-client" }, -] - -[package.metadata] -requires-dist = [ - { name = "aiobotocore", marker = "extra == 'infra'", specifier = ">=2.9.0" }, - { name = "aiobotocore", marker = "extra == 'storage'", specifier = ">=2.9.0" }, - { name = "anthropic", marker = "extra == 'ai'", specifier = ">=0.25.0" }, - { name = "anthropic", marker = "extra == 'all'", specifier = ">=0.25.0" }, - { name = "botocore", marker = "extra == 'infra'", specifier = ">=1.34.0" }, - { name = "botocore", marker = "extra == 'storage'", specifier = ">=1.34.0" }, - { name = "click", specifier = ">=8.1.0" }, - { name = "croniter", marker = "extra == 'cron'", specifier = ">=2.0.0" }, - { name = "croniter", marker = "extra == 'infra'", specifier = ">=2.0.0" }, - { name = "hanzo-aci", marker = "extra == 'all'", specifier = ">=0.2.8" }, - { name = "hanzo-aci", marker = "extra == 'dev'", specifier = ">=0.2.8" }, - { name = "hanzo-agents", marker = "extra == 'agents'", specifier = ">=0.1.0" }, - { name = "hanzo-agents", marker = "extra == 'all'", specifier = ">=0.1.0" }, - { name = "hanzo-cli", specifier = ">=0.2.0" }, - { name = "hanzo-kms", specifier = ">=1.1.0" }, - { name = "hanzo-mcp", marker = "extra == 'all'", specifier = ">=0.7.0" }, - { name = "hanzo-mcp", marker = "extra == 'mcp'", specifier = ">=0.7.0" }, - { name = "hanzo-memory", marker = "extra == 'all'", specifier = ">=1.0.0" }, - { name = "hanzo-network", marker = "extra == 'agents'", specifier = ">=0.1.3" }, - { name = "hanzo-network", marker = "extra == 'all'", specifier = ">=0.1.3" }, - { name = "hanzoai", marker = "extra == 'ai'", specifier = ">=1.0.0" }, - { name = "hanzoai", marker = "extra == 'all'", specifier = ">=1.0.0" }, - { name = "httpx", specifier = ">=0.23.0" }, - { name = "httpx", marker = "extra == 'functions'", specifier = ">=0.23.0" }, - { name = "httpx", marker = "extra == 'infra'", specifier = ">=0.23.0" }, - { name = "meilisearch-python-sdk", marker = "extra == 'infra'", specifier = ">=3.0.0" }, - { name = "meilisearch-python-sdk", marker = "extra == 'search'", specifier = ">=3.0.0" }, - { name = "motor", marker = "extra == 'documentdb'", specifier = ">=3.3.0" }, - { name = "motor", marker = "extra == 'infra'", specifier = ">=3.3.0" }, - { name = "nats-py", marker = "extra == 'infra'", specifier = ">=2.6.0" }, - { name = "nats-py", marker = "extra == 'pubsub'", specifier = ">=2.6.0" }, - { name = "openai", marker = "extra == 'ai'", specifier = ">=1.0.0" }, - { name = "openai", marker = "extra == 'all'", specifier = ">=1.0.0" }, - { name = "prompt-toolkit", marker = "extra == 'all'", specifier = ">=3.0.0" }, - { name = "prompt-toolkit", marker = "extra == 'interactive'", specifier = ">=3.0.0" }, - { name = "pydantic", specifier = ">=2.0.0" }, - { name = "pymongo", marker = "extra == 'documentdb'", specifier = ">=4.6.0" }, - { name = "pymongo", marker = "extra == 'infra'", specifier = ">=4.6.0" }, - { name = "pyyaml", specifier = ">=6.0" }, - { name = "qdrant-client", marker = "extra == 'infra'", specifier = ">=1.7.0" }, - { name = "qdrant-client", marker = "extra == 'vector'", specifier = ">=1.7.0" }, - { name = "qrcode", marker = "extra == 'all'", specifier = ">=7.4.2" }, - { name = "qrcode", marker = "extra == 'interactive'", specifier = ">=7.4.2" }, - { name = "redis", marker = "extra == 'cron'", specifier = ">=5.0.0" }, - { name = "redis", marker = "extra == 'infra'", specifier = ">=5.0.0" }, - { name = "redis", marker = "extra == 'kv'", specifier = ">=5.0.0" }, - { name = "redis", marker = "extra == 'queues'", specifier = ">=5.0.0" }, - { name = "rich", specifier = ">=13.0.0" }, - { name = "temporalio", marker = "extra == 'infra'", specifier = ">=1.4.0" }, - { name = "temporalio", marker = "extra == 'tasks'", specifier = ">=1.4.0" }, -] -provides-extras = ["all", "ai", "router", "mcp", "agents", "dev", "interactive", "vector", "kv", "documentdb", "storage", "search", "pubsub", "tasks", "queues", "cron", "functions", "infra"] - -[[package]] -name = "hanzo-aci" -version = "0.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beautifulsoup4" }, - { name = "binaryornot" }, - { name = "cachetools" }, - { name = "charset-normalizer" }, - { name = "flake8" }, - { name = "gitpython" }, - { name = "grep-ast" }, - { name = "libcst" }, - { name = "mammoth" }, - { name = "markdownify" }, - { name = "matplotlib" }, - { name = "networkx" }, - { name = "openpyxl" }, - { name = "pandas" }, - { name = "pdfminer-six" }, - { name = "puremagic" }, - { name = "pydantic" }, - { name = "pydub" }, - { name = "pypdf" }, - { name = "pypdf2" }, - { name = "python-pptx" }, - { name = "rapidfuzz" }, - { name = "requests" }, - { name = "speechrecognition" }, - { name = "tree-sitter" }, - { name = "tree-sitter-language-pack" }, - { name = "whatthepatch" }, - { name = "xlrd" }, - { name = "youtube-transcript-api" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/21/9a/898971e284c5670035da4da9b8fe00105d5a7c689ea588aa650b0fd63b62/hanzo_aci-0.3.1.tar.gz", hash = "sha256:d6affc81f42e15d0acb3c96adcbba6a5a97ad4b85c4898a2c4ea5602ed882233", size = 78735, upload-time = "2025-07-26T00:11:27.585Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/6a/bf70f6663ec0ffec02c192b6ae358acf29eda7b2f66fdc29b5912c88e5fc/hanzo_aci-0.3.1-py3-none-any.whl", hash = "sha256:ae75916b08489a7c3b0add156a3ede7eb7fbebe261960222a80561c8a9b18d81", size = 95980, upload-time = "2025-07-26T00:11:25.776Z" }, -] - -[[package]] -name = "hanzo-agents" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "hanzo-tools-agent" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/85/fd/ea7dfae549c91c16cd40f89f874d22f738f257229f84a2b7d2b731863a7c/hanzo_agents-0.1.2.tar.gz", hash = "sha256:a3de7baeea28153268cd63dddcf0673c96b6e76ffe39b5819d3b8294a473a438", size = 3060, upload-time = "2026-01-22T05:49:10.572Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/69/2ed8106eb17cccafbc240f1fef5caf40162a5c4f3071dbee1f92c059402b/hanzo_agents-0.1.2-py3-none-any.whl", hash = "sha256:8e821d9d125ca87b13861c02c1513441ba0cd20a6433edc0c173db499c6b6a32", size = 3612, upload-time = "2026-01-22T05:49:09.782Z" }, -] - -[[package]] -name = "hanzo-async" -version = "0.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiofiles" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/be/1b/dcd448eac4461973442bc20dd25189360e85ed7ae1c78fafbfce6d88ffc2/hanzo_async-0.1.1.tar.gz", hash = "sha256:05d41974823d27d3557db791705095a49f1cefc901c6ec686120bb9bbd85f0ee", size = 8619, upload-time = "2026-01-05T01:19:19.37Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/70/363dac59048d70653ce0d07ff37d5521f49b5b6ce30d6aba8c31bf31154f/hanzo_async-0.1.1-py3-none-any.whl", hash = "sha256:8f551b7b57e96b4f4c5b7e05d7781eb154a7a788c824c7a36bd69f607532cda2", size = 8649, upload-time = "2026-01-05T01:19:18.268Z" }, -] - -[[package]] -name = "hanzo-cli" -version = "0.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "hanzo-iam" }, - { name = "hanzo-kms" }, - { name = "httpx" }, - { name = "pyjwt" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d9/c0/92c16d871661fed8d0ab091713b468263dd170b652fcc976c1e0c86f5fe0/hanzo_cli-0.2.1.tar.gz", hash = "sha256:d898072a62f427da4a8cc77c834e26b6c7638c108db959dfc7c59b066651d734", size = 23540, upload-time = "2026-02-21T21:48:30.459Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/63/88800ec47608397868b92667ecb90e69502df457b83266ec21fb892f7349/hanzo_cli-0.2.1-py3-none-any.whl", hash = "sha256:2d6442e1d1ccb4f5e6a5517ad0b502e4c514f6335f167906c0c8e6ae4be0d193", size = 27435, upload-time = "2026-02-21T21:48:29.607Z" }, -] - -[[package]] -name = "hanzo-iam" -version = "1.29.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "cryptography" }, - { name = "pyjwt" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3e/4b/11d440a3a99e5b7967ae1a9d56f4bee7c9879f7e2a56a9a1398a3d931064/hanzo_iam-1.29.0.tar.gz", hash = "sha256:5979db89b791be181c259d103822424f389be5d82bff677bee9fad3f213f2578", size = 25123, upload-time = "2025-04-09T18:51:53.64Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/26/bc5dbd90e5fd0f2666646c2b4831cc4fef6867bbad8091da496851fe600c/hanzo_iam-1.29.0-py2.py3-none-any.whl", hash = "sha256:22aba50d91d642843570fd73853783cc26bad7ac618c778d2df49e54bd43bed9", size = 47149, upload-time = "2025-04-09T18:51:52.134Z" }, -] - -[[package]] -name = "hanzo-kms" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "httpx" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/78/c459ae92072e55d94e6b0718d927fb2801c06ea8ef8a5cacc417c237b385/hanzo_kms-1.1.0.tar.gz", hash = "sha256:13242266012dcc2a1b48705b48704504409f21c13ec022c32385f952cf4b9f8b", size = 7553, upload-time = "2026-02-21T06:20:06.542Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/13/77301a5216f4f3c85689061e590f606086f32f26bb0ea7a86ce850223e04/hanzo_kms-1.1.0-py3-none-any.whl", hash = "sha256:19ec34ae131917e153feade770f4aa03366ba43a662fba040d3e31cd78dd2fd3", size = 10672, upload-time = "2026-02-21T06:20:05.56Z" }, -] - -[[package]] -name = "hanzo-mcp" -version = "0.11.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "hanzo-async" }, - { name = "hanzo-persona" }, - { name = "hanzo-tools" }, - { name = "hanzo-tools-agent" }, - { name = "hanzo-tools-api" }, - { name = "hanzo-tools-browser" }, - { name = "hanzo-tools-computer" }, - { name = "hanzo-tools-config" }, - { name = "hanzo-tools-fs" }, - { name = "hanzo-tools-llm" }, - { name = "hanzo-tools-lsp" }, - { name = "hanzo-tools-memory" }, - { name = "hanzo-tools-reasoning" }, - { name = "hanzo-tools-refactor" }, - { name = "hanzo-tools-shell" }, - { name = "hanzo-tools-todo" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "typing-extensions" }, - { name = "uvloop" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b0/17/872aafef395007caf1df4eefdbdb0c598a5898be98f07a50ae6b76c0d4d1/hanzo_mcp-0.11.7.tar.gz", hash = "sha256:49a781f5409973848824fafd23f5692e7f95b9e22c575ac2230ca83475438cb9", size = 259110, upload-time = "2026-01-22T23:48:53.445Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/52/0eb8a79e82f5fefe86326fda7264130001d8cb9557536a9aa6d7f275c298/hanzo_mcp-0.11.7-py3-none-any.whl", hash = "sha256:07ceca292a7eb7afd19ad5bdfbb7fe5085417f9c553150d1d335e48f7ef403dc", size = 200548, upload-time = "2026-01-22T23:48:51.646Z" }, -] - -[[package]] -name = "hanzo-memory" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiocache" }, - { name = "chromadb" }, - { name = "fastapi" }, - { name = "fastembed" }, - { name = "httpx" }, - { name = "lancedb" }, - { name = "litellm" }, - { name = "mcp" }, - { name = "numpy" }, - { name = "orjson" }, - { name = "passlib", extra = ["bcrypt"] }, - { name = "polars" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "python-jose", extra = ["cryptography"] }, - { name = "python-multipart" }, - { name = "redis" }, - { name = "rich" }, - { name = "scikit-learn" }, - { name = "sentence-transformers" }, - { name = "structlog" }, - { name = "tenacity" }, - { name = "tiktoken" }, - { name = "uvicorn", extra = ["standard"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9c/8a/9cbcff33dcbc3c05d8de53120c4b039bf5d41dfcf0d73c3846c15dcba936/hanzo_memory-1.0.1.tar.gz", hash = "sha256:8b31210c967cfb5fe2b109d804f67017bb2771dec508543e2de02563e898c3f0", size = 44623, upload-time = "2025-09-17T22:11:52.775Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/12/223b4c3a0c3fe336421a1b7dc4345bd6ef2d1b9fe1db2abe6de896aa0f44/hanzo_memory-1.0.1-py3-none-any.whl", hash = "sha256:fd0fea34a63d38b7e5dcc83bff23086d295bec8b0cec4c449d6338a8d0a17f09", size = 40932, upload-time = "2025-09-17T22:11:51.316Z" }, -] - -[[package]] -name = "hanzo-network" -version = "0.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "grpcio" }, - { name = "grpcio-tools" }, - { name = "hanzo-agents" }, - { name = "httpx" }, - { name = "protobuf" }, - { name = "psutil" }, - { name = "pydantic" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f2/32/6072acb13d43e511c33c468b4a90b12a56384cd18ac7a9183beceb310a1b/hanzo_network-0.1.3.tar.gz", hash = "sha256:ee58076eaaba3c80460c66b6cc8ff1aa42cf875559ba743029123ab87f2f486c", size = 83385, upload-time = "2025-08-10T21:45:14.984Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/c4/7464e0084f44990f9115f77a8cfd1b5b013e8541d2f0c9fd62357908f923/hanzo_network-0.1.3-py3-none-any.whl", hash = "sha256:6b85c74c5fa9712f039f356509c0f63fbc092591d9f91e98910f237cb85a6573", size = 127246, upload-time = "2025-08-10T21:45:13.755Z" }, -] - -[[package]] -name = "hanzo-persona" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/15/15ecb8b1825d614adaad211a7902cbc05c185ff94160c6445d67e73bb473/hanzo_persona-1.0.0.tar.gz", hash = "sha256:a74f58788bb72623697fbd72e7848c3d6ad306f630ae7007ddfcde7dfc0ef7a5", size = 13305, upload-time = "2025-12-26T18:46:38.055Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/50/c7/53bdab3e003e839a7d574c8532d686c60642e85818ea861926a08b0f7822/hanzo_persona-1.0.0-py3-none-any.whl", hash = "sha256:e3c5e2d214d79af5f6dc728fdef35c7cad180ff8d3bcc6ba9fca30cbba96b068", size = 14857, upload-time = "2025-12-26T18:46:36.731Z" }, -] - -[[package]] -name = "hanzo-tools" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/3e/2d94dc54e202bdb11f6e4597dd68eebc554d2b92fffb4f6918cdf3f91fe2/hanzo_tools-0.3.0.tar.gz", hash = "sha256:d00cb3212a707e22f9bb5a21f0f9eb34a74f22ff2b5f24e2f8b6321f9880e2fb", size = 10929, upload-time = "2025-12-27T18:56:38.843Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/07/6ebbcf371aafa5f2d171de2916ef92c73978b927b34a8863af53e1b1a80b/hanzo_tools-0.3.0-py3-none-any.whl", hash = "sha256:c7b0f6f7c3089f06329bc1aaca39fbce4b7108fdbd048e2bbc450a3aff9941f2", size = 11928, upload-time = "2025-12-27T18:56:37.528Z" }, -] - -[[package]] -name = "hanzo-tools-agent" -version = "0.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "hanzo-async" }, - { name = "hanzo-tools" }, - { name = "hanzo-tools-shell" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/db/a66da27139016535a58cad1b874dc757ddd6426726fca100bb98d07fed19/hanzo_tools_agent-0.3.1.tar.gz", hash = "sha256:ede2273174bb2d41791d823f0eb5c1d1f8c106a6ad1da4abf40418092d5d7624", size = 71757, upload-time = "2026-01-05T14:51:13.575Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/cf/ec37667cc32b5fa48c9fc1be37de0c8865f5392ffad5598c956684e81d8d/hanzo_tools_agent-0.3.1-py3-none-any.whl", hash = "sha256:ef0a7d7481047f61dc7dec1c2f0a92f9b63d9c29069138d400c77c18a5bde205", size = 84867, upload-time = "2026-01-05T14:51:12.593Z" }, -] - -[[package]] -name = "hanzo-tools-api" -version = "0.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiofiles" }, - { name = "hanzo-tools-core" }, - { name = "httpx" }, - { name = "pydantic" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b7/a9/4277bf9752f043e4b42831468b1ae4e85a628612b6d6e9ecdcece8f98e63/hanzo_tools_api-0.3.1.tar.gz", hash = "sha256:bab061526849f61234e0a2bdf1e8c02092374566125ad721abd322c3cf506b45", size = 204438, upload-time = "2026-01-20T07:12:31.863Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/5d/6f00bbd79ecc3b0313cf7bd52d7c986fe7a8fa74d3f0052216b9bfbc5476/hanzo_tools_api-0.3.1-py3-none-any.whl", hash = "sha256:3f9a26f3e195b6ec1c4ec049b7fb9003183a0717f8a217bf14156da43d6d6d15", size = 134747, upload-time = "2026-01-20T07:12:30.152Z" }, -] - -[[package]] -name = "hanzo-tools-browser" -version = "0.2.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "hanzo-tools" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a3/68/0d7c90069c91de27c8aee7fe1e465be1f376e7e4689e9d411796fa064009/hanzo_tools_browser-0.2.2.tar.gz", hash = "sha256:0f5abece40387e8ba7208d102e0d0a93d7891f2022349829869acb453ba6d627", size = 23985, upload-time = "2026-01-22T06:36:13.268Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/6b/a9e8fb6b657a009d70d1536072024503899bba89ed390e5b141aaf9e9c5a/hanzo_tools_browser-0.2.2-py3-none-any.whl", hash = "sha256:f251db5a3630c9c6ff04dc77409b65c3f81e34436818c734f475a076c769dd1b", size = 24027, upload-time = "2026-01-22T06:36:11.826Z" }, -] - -[[package]] -name = "hanzo-tools-computer" -version = "0.5.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools" }, - { name = "mcp" }, - { name = "numpy" }, - { name = "pillow" }, - { name = "pyautogui" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/bd/00d4e47a49c33b057ff443f33b8b836b7b0ccbeb10972d3d2944664bf63f/hanzo_tools_computer-0.5.2.tar.gz", hash = "sha256:cdec76dad07d01587ed5358ef1581ddcf83cb462b30e8eff2a2884905acef404", size = 109353, upload-time = "2026-01-12T04:45:45.22Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/90/143881ff12f14e10f87ae33a5c19087df74e9a71ecf131caa5159fd84403/hanzo_tools_computer-0.5.2-py3-none-any.whl", hash = "sha256:88baadcec8463d585cf7adc06cfa61acf13a08d967e81fd9a99805adb401c5d2", size = 31281, upload-time = "2026-01-12T04:45:43.86Z" }, -] - -[[package]] -name = "hanzo-tools-config" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "hanzo-tools-core" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/60/d8/a013cb956850f404f6e306fabe974ff9a7462d936f19f569ce7587fe70f1/hanzo_tools_config-0.2.0.tar.gz", hash = "sha256:fdbc63abc6e588e361a8c20e3b6b5947387161d168e945769b0efbe757128eaf", size = 8665, upload-time = "2025-12-26T18:29:59.387Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/eb/83e34ef9480890805bbf0e99795afbfa0816bc527c4f72104101d70f0f93/hanzo_tools_config-0.2.0-py3-none-any.whl", hash = "sha256:b130d13d5491ebea0a64230960c51c20dfaffafcd97dc84ab7e1a38a7da8d469", size = 9927, upload-time = "2025-12-26T18:29:58.532Z" }, -] - -[[package]] -name = "hanzo-tools-core" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a9/67/eabf2d355819b9c948a9898ed537fa014a5de0422de66e0ea4203faae6be/hanzo_tools_core-0.2.0.tar.gz", hash = "sha256:ab5352056d3db1d42aadd94d81635eb835476194562b07b497f327f6d334c058", size = 11441, upload-time = "2025-12-26T14:46:12.281Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/16/c1a705434afc5936156eadedcc29e1200fb4871e7a1b83d5efeebefbfcda/hanzo_tools_core-0.2.0-py3-none-any.whl", hash = "sha256:e07cdc3692003e30a40082be3750a0670b2adf612e3e7a56dd741d3b532b8090", size = 11026, upload-time = "2025-12-26T14:46:11.514Z" }, -] - -[[package]] -name = "hanzo-tools-fs" -version = "0.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "ffind" }, - { name = "grep-ast" }, - { name = "hanzo-async" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "watchdog" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6b/af/d5735a32339d6bf783a708bd9b294388d59003820a2f4648e006d4213112/hanzo_tools_fs-0.3.1.tar.gz", hash = "sha256:3751fa060ec8cccc26eb3dd2f9ed1771bea3a38373e7b84dac95c4a1067b37c5", size = 10968, upload-time = "2026-01-04T01:40:46.164Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/b2/4418ab22f8854992c786b145f87277a0a4ea2798b865af592a2725495348/hanzo_tools_fs-0.3.1-py3-none-any.whl", hash = "sha256:6892bf6ba2559cad90876276b7022bfc45923d86866ac08e83bbe86e0771081f", size = 14541, upload-time = "2026-01-04T01:40:44.447Z" }, -] - -[[package]] -name = "hanzo-tools-llm" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "hanzo-tools-core" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/e7/d90c92abfbb4ed5884e5183eda1e6ab65d9f7bd22d9a0cbcf2d5d3f321ed/hanzo_tools_llm-0.2.0.tar.gz", hash = "sha256:4ef06e5f8ab91e59b4404bd5b7a35657fb55c7ce62efa524ce99c5e320424703", size = 16839, upload-time = "2025-12-26T18:30:01.148Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/8a/42849884339e7b8a65b6db1ea421554bb50ee4e4abdb4fa40a9acc0e31b1/hanzo_tools_llm-0.2.0-py3-none-any.whl", hash = "sha256:4c57d77ce18358327647643f354f5489365b699789320dac3deb025c441509cb", size = 20882, upload-time = "2025-12-26T18:29:58.592Z" }, -] - -[[package]] -name = "hanzo-tools-lsp" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-core" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7e/aa/5a8f9e3310fb1ed1ec5ea48b2054e413ac9b62be6f5316b0303e7212c5e8/hanzo_tools_lsp-0.2.0.tar.gz", hash = "sha256:f19a52e9025a5f4ba96dc98a7072d117fa94e9f5a64c4055a5353c7841b639c0", size = 7913, upload-time = "2025-12-26T14:46:58.64Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/61/7771fa35a4013c123c289cd31cf9e599b014990fb39a8a3f3abee7673a8b/hanzo_tools_lsp-0.2.0-py3-none-any.whl", hash = "sha256:2e2926787efdd52d613823491e52dc599d3505a6954d1ea69ec0fd2a3dd4a371", size = 8468, upload-time = "2025-12-26T14:46:57.761Z" }, -] - -[[package]] -name = "hanzo-tools-memory" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-core" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e9/95/0b78652310c7ae312375c2cd5bb683a101f3a617b301e104592adf6e462f/hanzo_tools_memory-0.2.0.tar.gz", hash = "sha256:5faf7afd9804b6f67c06740ccfd6a41997f40582565b37a6c4b60e10811c914f", size = 8504, upload-time = "2025-12-26T14:46:40.826Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/c8/cea1e08fab38bb8082889b8e582bb49a8e5e7ee679541d4f60dc43ead354/hanzo_tools_memory-0.2.0-py3-none-any.whl", hash = "sha256:ce1d4c748c4c1326f8c4b019293c140bcc99c3b8d9baa3f6a37c9abe8df7544e", size = 10555, upload-time = "2025-12-26T14:46:36.382Z" }, -] - -[[package]] -name = "hanzo-tools-reasoning" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-core" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/90/c9/59ffa443cb3daeaf1d2009d417d3bf1cddb3cf2d7cede6610f9c7ae04162/hanzo_tools_reasoning-0.2.0.tar.gz", hash = "sha256:4d54a248ea92c42cc1c41195073d20fe3f65b2526d19c51b2f8dd84d25b0e8b2", size = 5381, upload-time = "2025-12-26T14:46:37.417Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/d4/1126584049f18085012f9d1fac3e44c81b43f4fb0ea2ea5c4971c5434d6e/hanzo_tools_reasoning-0.2.0-py3-none-any.whl", hash = "sha256:9d9ed9160bc778b087c30c6afdf0252cddeb9dd772f07b2542527629ab4871ad", size = 7209, upload-time = "2025-12-26T14:46:36.314Z" }, -] - -[[package]] -name = "hanzo-tools-refactor" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "hanzo-tools-core" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/31/c8317babfb160e8b002c047de65206bf30607f1eadb341d2d377cbf358e8/hanzo_tools_refactor-0.2.0.tar.gz", hash = "sha256:283b1bfc036de7dbba1e44c8332df9285385885af6d5f5f81d4b7ae1902c7f3f", size = 19653, upload-time = "2025-12-26T14:46:59.372Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/f4/8efcdcdb398e3c16242331c4a2321750ca3ea1dd51077811b4034215eb5f/hanzo_tools_refactor-0.2.0-py3-none-any.whl", hash = "sha256:b7cd5963ff1ad2b418397202ac6134dd1ad4b079563ff3755f9c935d6556cb51", size = 20019, upload-time = "2025-12-26T14:46:57.835Z" }, -] - -[[package]] -name = "hanzo-tools-shell" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-async" }, - { name = "hanzo-tools" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "tiktoken" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f9/06/5d8c57757f2f13fc879a7db2b6be43d57d908212b4bbdaf587c23470cc1d/hanzo_tools_shell-0.6.1.tar.gz", hash = "sha256:f437cf204d276bb8543dbc659d152d77ef9d04d634d1d551e216a4a4cf42d334", size = 39564, upload-time = "2026-01-12T03:21:55.948Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/65/db/fe26bc4cace2900e000644d61cb2a37502312a4e20082d85cde11e419ba0/hanzo_tools_shell-0.6.1-py3-none-any.whl", hash = "sha256:d0a7eef3385744dba2e5dd0a268a44884a3276ef2ea4d8c96cc06e43c226e866", size = 46161, upload-time = "2026-01-12T03:21:55.028Z" }, -] - -[[package]] -name = "hanzo-tools-todo" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools-core" }, - { name = "mcp" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d0/ca/81e888e55326c218863290e9c9b4a52b2f3512aefe2e38e89595f58c45e4/hanzo_tools_todo-0.2.0.tar.gz", hash = "sha256:85cfca809bb12bb9b6f7c913888acc5d6e9d5e1891aac1b26ef7c5679f160d03", size = 7180, upload-time = "2025-12-26T14:46:38.189Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/dc/3a34128bddba3ad5a0bae1824d123d9d64f3aba305d1637a6c89e230dd1b/hanzo_tools_todo-0.2.0-py3-none-any.whl", hash = "sha256:6a6a47decef663f1737cc447454da560212bdc3c4a9b7e331b9961f32f96a572", size = 8086, upload-time = "2025-12-26T14:46:36.459Z" }, -] - -[[package]] -name = "hanzoai" -version = "2.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "h11" }, - { name = "httpx" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "typing-extensions" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/dd/705e537428e4bdfcd325f42824c4db7e545328bafa24b7cdec62e9606e11/hanzoai-2.1.2.tar.gz", hash = "sha256:7afce7ac7eba44f4e3afacedbbd98db8ba72b16bb1c8eaeefb0542fd4eb8d614", size = 464589, upload-time = "2026-01-21T03:10:10.779Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/0f/4baa3dbd424686f348d204114498ab2b7c11f7eb5acebe5c864ec83c289b/hanzoai-2.1.2-py3-none-any.whl", hash = "sha256:0d0e4d1b458e9d94daec5a9475c405748067b6023297f8d7143a9d5278cc0055", size = 386563, upload-time = "2026-01-21T03:10:09.143Z" }, -] - -[[package]] -name = "hf-xet" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020, upload-time = "2025-10-24T19:04:32.129Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/a5/85ef910a0aa034a2abcfadc360ab5ac6f6bc4e9112349bd40ca97551cff0/hf_xet-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:ceeefcd1b7aed4956ae8499e2199607765fbd1c60510752003b6cc0b8413b649", size = 2861870, upload-time = "2025-10-24T19:04:11.422Z" }, - { url = "https://files.pythonhosted.org/packages/ea/40/e2e0a7eb9a51fe8828ba2d47fe22a7e74914ea8a0db68a18c3aa7449c767/hf_xet-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b70218dd548e9840224df5638fdc94bd033552963cfa97f9170829381179c813", size = 2717584, upload-time = "2025-10-24T19:04:09.586Z" }, - { url = "https://files.pythonhosted.org/packages/a5/7d/daf7f8bc4594fdd59a8a596f9e3886133fdc68e675292218a5e4c1b7e834/hf_xet-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d40b18769bb9a8bc82a9ede575ce1a44c75eb80e7375a01d76259089529b5dc", size = 3315004, upload-time = "2025-10-24T19:04:00.314Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ba/45ea2f605fbf6d81c8b21e4d970b168b18a53515923010c312c06cd83164/hf_xet-1.2.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd3a6027d59cfb60177c12d6424e31f4b5ff13d8e3a1247b3a584bf8977e6df5", size = 3222636, upload-time = "2025-10-24T19:03:58.111Z" }, - { url = "https://files.pythonhosted.org/packages/4a/1d/04513e3cab8f29ab8c109d309ddd21a2705afab9d52f2ba1151e0c14f086/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6de1fc44f58f6dd937956c8d304d8c2dea264c80680bcfa61ca4a15e7b76780f", size = 3408448, upload-time = "2025-10-24T19:04:20.951Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7c/60a2756d7feec7387db3a1176c632357632fbe7849fce576c5559d4520c7/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f182f264ed2acd566c514e45da9f2119110e48a87a327ca271027904c70c5832", size = 3503401, upload-time = "2025-10-24T19:04:22.549Z" }, - { url = "https://files.pythonhosted.org/packages/4e/64/48fffbd67fb418ab07451e4ce641a70de1c40c10a13e25325e24858ebe5a/hf_xet-1.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:293a7a3787e5c95d7be1857358a9130694a9c6021de3f27fa233f37267174382", size = 2900866, upload-time = "2025-10-24T19:04:33.461Z" }, - { url = "https://files.pythonhosted.org/packages/e2/51/f7e2caae42f80af886db414d4e9885fac959330509089f97cccb339c6b87/hf_xet-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:10bfab528b968c70e062607f663e21e34e2bba349e8038db546646875495179e", size = 2861861, upload-time = "2025-10-24T19:04:19.01Z" }, - { url = "https://files.pythonhosted.org/packages/6e/1d/a641a88b69994f9371bd347f1dd35e5d1e2e2460a2e350c8d5165fc62005/hf_xet-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a212e842647b02eb6a911187dc878e79c4aa0aa397e88dd3b26761676e8c1f8", size = 2717699, upload-time = "2025-10-24T19:04:17.306Z" }, - { url = "https://files.pythonhosted.org/packages/df/e0/e5e9bba7d15f0318955f7ec3f4af13f92e773fbb368c0b8008a5acbcb12f/hf_xet-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30e06daccb3a7d4c065f34fc26c14c74f4653069bb2b194e7f18f17cbe9939c0", size = 3314885, upload-time = "2025-10-24T19:04:07.642Z" }, - { url = "https://files.pythonhosted.org/packages/21/90/b7fe5ff6f2b7b8cbdf1bd56145f863c90a5807d9758a549bf3d916aa4dec/hf_xet-1.2.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:29c8fc913a529ec0a91867ce3d119ac1aac966e098cf49501800c870328cc090", size = 3221550, upload-time = "2025-10-24T19:04:05.55Z" }, - { url = "https://files.pythonhosted.org/packages/6f/cb/73f276f0a7ce46cc6a6ec7d6c7d61cbfe5f2e107123d9bbd0193c355f106/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e159cbfcfbb29f920db2c09ed8b660eb894640d284f102ada929b6e3dc410a", size = 3408010, upload-time = "2025-10-24T19:04:28.598Z" }, - { url = "https://files.pythonhosted.org/packages/b8/1e/d642a12caa78171f4be64f7cd9c40e3ca5279d055d0873188a58c0f5fbb9/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c91d5ae931510107f148874e9e2de8a16052b6f1b3ca3c1b12f15ccb491390f", size = 3503264, upload-time = "2025-10-24T19:04:30.397Z" }, - { url = "https://files.pythonhosted.org/packages/17/b5/33764714923fa1ff922770f7ed18c2daae034d21ae6e10dbf4347c854154/hf_xet-1.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:210d577732b519ac6ede149d2f2f34049d44e8622bf14eb3d63bbcd2d4b332dc", size = 2901071, upload-time = "2025-10-24T19:04:37.463Z" }, - { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099, upload-time = "2025-10-24T19:04:15.366Z" }, - { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178, upload-time = "2025-10-24T19:04:13.695Z" }, - { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214, upload-time = "2025-10-24T19:04:03.596Z" }, - { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054, upload-time = "2025-10-24T19:04:01.949Z" }, - { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812, upload-time = "2025-10-24T19:04:24.585Z" }, - { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920, upload-time = "2025-10-24T19:04:26.927Z" }, - { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735, upload-time = "2025-10-24T19:04:35.928Z" }, -] - -[[package]] -name = "hpack" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276, upload-time = "2025-01-22T21:44:58.347Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357, upload-time = "2025-01-22T21:44:56.92Z" }, -] - -[[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 = "httptools" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/7f/403e5d787dc4942316e515e949b0c8a013d84078a915910e9f391ba9b3ed/httptools-0.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:38e0c83a2ea9746ebbd643bdfb521b9aa4a91703e2cd705c20443405d2fd16a5", size = 206280, upload-time = "2025-10-10T03:54:39.274Z" }, - { url = "https://files.pythonhosted.org/packages/2a/0d/7f3fd28e2ce311ccc998c388dd1c53b18120fda3b70ebb022b135dc9839b/httptools-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f25bbaf1235e27704f1a7b86cd3304eabc04f569c828101d94a0e605ef7205a5", size = 110004, upload-time = "2025-10-10T03:54:40.403Z" }, - { url = "https://files.pythonhosted.org/packages/84/a6/b3965e1e146ef5762870bbe76117876ceba51a201e18cc31f5703e454596/httptools-0.7.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c15f37ef679ab9ecc06bfc4e6e8628c32a8e4b305459de7cf6785acd57e4d03", size = 517655, upload-time = "2025-10-10T03:54:41.347Z" }, - { url = "https://files.pythonhosted.org/packages/11/7d/71fee6f1844e6fa378f2eddde6c3e41ce3a1fb4b2d81118dd544e3441ec0/httptools-0.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fe6e96090df46b36ccfaf746f03034e5ab723162bc51b0a4cf58305324036f2", size = 511440, upload-time = "2025-10-10T03:54:42.452Z" }, - { url = "https://files.pythonhosted.org/packages/22/a5/079d216712a4f3ffa24af4a0381b108aa9c45b7a5cc6eb141f81726b1823/httptools-0.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362", size = 495186, upload-time = "2025-10-10T03:54:43.937Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9e/025ad7b65278745dee3bd0ebf9314934c4592560878308a6121f7f812084/httptools-0.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e99c7b90a29fd82fea9ef57943d501a16f3404d7b9ee81799d41639bdaae412c", size = 499192, upload-time = "2025-10-10T03:54:45.003Z" }, - { url = "https://files.pythonhosted.org/packages/6d/de/40a8f202b987d43afc4d54689600ff03ce65680ede2f31df348d7f368b8f/httptools-0.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3e14f530fefa7499334a79b0cf7e7cd2992870eb893526fb097d51b4f2d0f321", size = 86694, upload-time = "2025-10-10T03:54:45.923Z" }, - { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889, upload-time = "2025-10-10T03:54:47.089Z" }, - { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180, upload-time = "2025-10-10T03:54:48.052Z" }, - { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596, upload-time = "2025-10-10T03:54:48.919Z" }, - { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268, upload-time = "2025-10-10T03:54:49.993Z" }, - { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517, upload-time = "2025-10-10T03:54:51.066Z" }, - { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337, upload-time = "2025-10-10T03:54:52.196Z" }, - { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743, upload-time = "2025-10-10T03:54:53.448Z" }, - { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" }, - { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" }, - { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" }, - { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" }, - { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" }, - { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" }, - { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" }, -] - -[[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.optional-dependencies] -http2 = [ - { name = "h2" }, -] - -[[package]] -name = "httpx-sse" -version = "0.4.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, -] - -[[package]] -name = "huggingface-hub" -version = "1.3.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "fsspec" }, - { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, - { name = "httpx" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "shellingham" }, - { name = "tqdm" }, - { name = "typer-slim" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/3f/352efd52136bfd8aa9280c6d4a445869226ae2ccd49ddad4f62e90cfd168/huggingface_hub-1.3.7.tar.gz", hash = "sha256:5f86cd48f27131cdbf2882699cbdf7a67dd4cbe89a81edfdc31211f42e4a5fd1", size = 627537, upload-time = "2026-02-02T10:40:10.61Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/89/bfbfde252d649fae8d5f09b14a2870e5672ed160c1a6629301b3e5302621/huggingface_hub-1.3.7-py3-none-any.whl", hash = "sha256:8155ce937038fa3d0cb4347d752708079bc85e6d9eb441afb44c84bcf48620d2", size = 536728, upload-time = "2026-02-02T10:40:08.274Z" }, -] - -[[package]] -name = "humanfriendly" -version = "10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyreadline3", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" }, -] - -[[package]] -name = "hyperframe" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566, upload-time = "2025-01-22T21:41:49.302Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - -[[package]] -name = "importlib-resources" -version = "6.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, -] - -[[package]] -name = "jaraco-context" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" }, -] - -[[package]] -name = "jaraco-functools" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, -] - -[[package]] -name = "jiter" -version = "0.13.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/5e/4ec91646aee381d01cdb9974e30882c9cd3b8c5d1079d6b5ff4af522439a/jiter-0.13.0.tar.gz", hash = "sha256:f2839f9c2c7e2dffc1bc5929a510e14ce0a946be9365fd1219e7ef342dae14f4", size = 164847, upload-time = "2026-02-02T12:37:56.441Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/30/7687e4f87086829955013ca12a9233523349767f69653ebc27036313def9/jiter-0.13.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0a2bd69fc1d902e89925fc34d1da51b2128019423d7b339a45d9e99c894e0663", size = 307958, upload-time = "2026-02-02T12:35:57.165Z" }, - { url = "https://files.pythonhosted.org/packages/c3/27/e57f9a783246ed95481e6749cc5002a8a767a73177a83c63ea71f0528b90/jiter-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f917a04240ef31898182f76a332f508f2cc4b57d2b4d7ad2dbfebbfe167eb505", size = 318597, upload-time = "2026-02-02T12:35:58.591Z" }, - { url = "https://files.pythonhosted.org/packages/cf/52/e5719a60ac5d4d7c5995461a94ad5ef962a37c8bf5b088390e6fad59b2ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e2b199f446d3e82246b4fd9236d7cb502dc2222b18698ba0d986d2fecc6152", size = 348821, upload-time = "2026-02-02T12:36:00.093Z" }, - { url = "https://files.pythonhosted.org/packages/61/db/c1efc32b8ba4c740ab3fc2d037d8753f67685f475e26b9d6536a4322bcdd/jiter-0.13.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04670992b576fa65bd056dbac0c39fe8bd67681c380cb2b48efa885711d9d726", size = 364163, upload-time = "2026-02-02T12:36:01.937Z" }, - { url = "https://files.pythonhosted.org/packages/55/8a/fb75556236047c8806995671a18e4a0ad646ed255276f51a20f32dceaeec/jiter-0.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a1aff1fbdb803a376d4d22a8f63f8e7ccbce0b4890c26cc7af9e501ab339ef0", size = 483709, upload-time = "2026-02-02T12:36:03.41Z" }, - { url = "https://files.pythonhosted.org/packages/7e/16/43512e6ee863875693a8e6f6d532e19d650779d6ba9a81593ae40a9088ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b3fb8c2053acaef8580809ac1d1f7481a0a0bdc012fd7f5d8b18fb696a5a089", size = 370480, upload-time = "2026-02-02T12:36:04.791Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4c/09b93e30e984a187bc8aaa3510e1ec8dcbdcd71ca05d2f56aac0492453aa/jiter-0.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdaba7d87e66f26a2c45d8cbadcbfc4bf7884182317907baf39cfe9775bb4d93", size = 360735, upload-time = "2026-02-02T12:36:06.994Z" }, - { url = "https://files.pythonhosted.org/packages/1a/1b/46c5e349019874ec5dfa508c14c37e29864ea108d376ae26d90bee238cd7/jiter-0.13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b88d649135aca526da172e48083da915ec086b54e8e73a425ba50999468cc08", size = 391814, upload-time = "2026-02-02T12:36:08.368Z" }, - { url = "https://files.pythonhosted.org/packages/15/9e/26184760e85baee7162ad37b7912797d2077718476bf91517641c92b3639/jiter-0.13.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e404ea551d35438013c64b4f357b0474c7abf9f781c06d44fcaf7a14c69ff9e2", size = 513990, upload-time = "2026-02-02T12:36:09.993Z" }, - { url = "https://files.pythonhosted.org/packages/e9/34/2c9355247d6debad57a0a15e76ab1566ab799388042743656e566b3b7de1/jiter-0.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f4748aad1b4a93c8bdd70f604d0f748cdc0e8744c5547798acfa52f10e79228", size = 548021, upload-time = "2026-02-02T12:36:11.376Z" }, - { url = "https://files.pythonhosted.org/packages/ac/4a/9f2c23255d04a834398b9c2e0e665382116911dc4d06b795710503cdad25/jiter-0.13.0-cp312-cp312-win32.whl", hash = "sha256:0bf670e3b1445fc4d31612199f1744f67f889ee1bbae703c4b54dc097e5dd394", size = 203024, upload-time = "2026-02-02T12:36:12.682Z" }, - { url = "https://files.pythonhosted.org/packages/09/ee/f0ae675a957ae5a8f160be3e87acea6b11dc7b89f6b7ab057e77b2d2b13a/jiter-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:15db60e121e11fe186c0b15236bd5d18381b9ddacdcf4e659feb96fc6c969c92", size = 205424, upload-time = "2026-02-02T12:36:13.93Z" }, - { url = "https://files.pythonhosted.org/packages/1b/02/ae611edf913d3cbf02c97cdb90374af2082c48d7190d74c1111dde08bcdd/jiter-0.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:41f92313d17989102f3cb5dd533a02787cdb99454d494344b0361355da52fcb9", size = 186818, upload-time = "2026-02-02T12:36:15.308Z" }, - { url = "https://files.pythonhosted.org/packages/91/9c/7ee5a6ff4b9991e1a45263bfc46731634c4a2bde27dfda6c8251df2d958c/jiter-0.13.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1f8a55b848cbabf97d861495cd65f1e5c590246fabca8b48e1747c4dfc8f85bf", size = 306897, upload-time = "2026-02-02T12:36:16.748Z" }, - { url = "https://files.pythonhosted.org/packages/7c/02/be5b870d1d2be5dd6a91bdfb90f248fbb7dcbd21338f092c6b89817c3dbf/jiter-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f556aa591c00f2c45eb1b89f68f52441a016034d18b65da60e2d2875bbbf344a", size = 317507, upload-time = "2026-02-02T12:36:18.351Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/b25d2ec333615f5f284f3a4024f7ce68cfa0604c322c6808b2344c7f5d2b/jiter-0.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7e1d61da332ec412350463891923f960c3073cf1aae93b538f0bb4c8cd46efb", size = 350560, upload-time = "2026-02-02T12:36:19.746Z" }, - { url = "https://files.pythonhosted.org/packages/be/ec/74dcb99fef0aca9fbe56b303bf79f6bd839010cb18ad41000bf6cc71eec0/jiter-0.13.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3097d665a27bc96fd9bbf7f86178037db139f319f785e4757ce7ccbf390db6c2", size = 363232, upload-time = "2026-02-02T12:36:21.243Z" }, - { url = "https://files.pythonhosted.org/packages/1b/37/f17375e0bb2f6a812d4dd92d7616e41917f740f3e71343627da9db2824ce/jiter-0.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d01ecc3a8cbdb6f25a37bd500510550b64ddf9f7d64a107d92f3ccb25035d0f", size = 483727, upload-time = "2026-02-02T12:36:22.688Z" }, - { url = "https://files.pythonhosted.org/packages/77/d2/a71160a5ae1a1e66c1395b37ef77da67513b0adba73b993a27fbe47eb048/jiter-0.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed9bbc30f5d60a3bdf63ae76beb3f9db280d7f195dfcfa61af792d6ce912d159", size = 370799, upload-time = "2026-02-02T12:36:24.106Z" }, - { url = "https://files.pythonhosted.org/packages/01/99/ed5e478ff0eb4e8aa5fd998f9d69603c9fd3f32de3bd16c2b1194f68361c/jiter-0.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fbafb6e88256f4454de33c1f40203d09fc33ed19162a68b3b257b29ca7f663", size = 359120, upload-time = "2026-02-02T12:36:25.519Z" }, - { url = "https://files.pythonhosted.org/packages/16/be/7ffd08203277a813f732ba897352797fa9493faf8dc7995b31f3d9cb9488/jiter-0.13.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5467696f6b827f1116556cb0db620440380434591e93ecee7fd14d1a491b6daa", size = 390664, upload-time = "2026-02-02T12:36:26.866Z" }, - { url = "https://files.pythonhosted.org/packages/d1/84/e0787856196d6d346264d6dcccb01f741e5f0bd014c1d9a2ebe149caf4f3/jiter-0.13.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2d08c9475d48b92892583df9da592a0e2ac49bcd41fae1fec4f39ba6cf107820", size = 513543, upload-time = "2026-02-02T12:36:28.217Z" }, - { url = "https://files.pythonhosted.org/packages/65/50/ecbd258181c4313cf79bca6c88fb63207d04d5bf5e4f65174114d072aa55/jiter-0.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:aed40e099404721d7fcaf5b89bd3b4568a4666358bcac7b6b15c09fb6252ab68", size = 547262, upload-time = "2026-02-02T12:36:29.678Z" }, - { url = "https://files.pythonhosted.org/packages/27/da/68f38d12e7111d2016cd198161b36e1f042bd115c169255bcb7ec823a3bf/jiter-0.13.0-cp313-cp313-win32.whl", hash = "sha256:36ebfbcffafb146d0e6ffb3e74d51e03d9c35ce7c625c8066cdbfc7b953bdc72", size = 200630, upload-time = "2026-02-02T12:36:31.808Z" }, - { url = "https://files.pythonhosted.org/packages/25/65/3bd1a972c9a08ecd22eb3b08a95d1941ebe6938aea620c246cf426ae09c2/jiter-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:8d76029f077379374cf0dbc78dbe45b38dec4a2eb78b08b5194ce836b2517afc", size = 202602, upload-time = "2026-02-02T12:36:33.679Z" }, - { url = "https://files.pythonhosted.org/packages/15/fe/13bd3678a311aa67686bb303654792c48206a112068f8b0b21426eb6851e/jiter-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:bb7613e1a427cfcb6ea4544f9ac566b93d5bf67e0d48c787eca673ff9c9dff2b", size = 185939, upload-time = "2026-02-02T12:36:35.065Z" }, - { url = "https://files.pythonhosted.org/packages/49/19/a929ec002ad3228bc97ca01dbb14f7632fffdc84a95ec92ceaf4145688ae/jiter-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fa476ab5dd49f3bf3a168e05f89358c75a17608dbabb080ef65f96b27c19ab10", size = 316616, upload-time = "2026-02-02T12:36:36.579Z" }, - { url = "https://files.pythonhosted.org/packages/52/56/d19a9a194afa37c1728831e5fb81b7722c3de18a3109e8f282bfc23e587a/jiter-0.13.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade8cb6ff5632a62b7dbd4757d8c5573f7a2e9ae285d6b5b841707d8363205ef", size = 346850, upload-time = "2026-02-02T12:36:38.058Z" }, - { url = "https://files.pythonhosted.org/packages/36/4a/94e831c6bf287754a8a019cb966ed39ff8be6ab78cadecf08df3bb02d505/jiter-0.13.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9950290340acc1adaded363edd94baebcee7dabdfa8bee4790794cd5cfad2af6", size = 358551, upload-time = "2026-02-02T12:36:39.417Z" }, - { url = "https://files.pythonhosted.org/packages/a2/ec/a4c72c822695fa80e55d2b4142b73f0012035d9fcf90eccc56bc060db37c/jiter-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2b4972c6df33731aac0742b64fd0d18e0a69bc7d6e03108ce7d40c85fd9e3e6d", size = 201950, upload-time = "2026-02-02T12:36:40.791Z" }, - { url = "https://files.pythonhosted.org/packages/b6/00/393553ec27b824fbc29047e9c7cd4a3951d7fbe4a76743f17e44034fa4e4/jiter-0.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:701a1e77d1e593c1b435315ff625fd071f0998c5f02792038a5ca98899261b7d", size = 185852, upload-time = "2026-02-02T12:36:42.077Z" }, - { url = "https://files.pythonhosted.org/packages/6e/f5/f1997e987211f6f9bd71b8083047b316208b4aca0b529bb5f8c96c89ef3e/jiter-0.13.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:cc5223ab19fe25e2f0bf2643204ad7318896fe3729bf12fde41b77bfc4fafff0", size = 308804, upload-time = "2026-02-02T12:36:43.496Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8f/5482a7677731fd44881f0204981ce2d7175db271f82cba2085dd2212e095/jiter-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9776ebe51713acf438fd9b4405fcd86893ae5d03487546dae7f34993217f8a91", size = 318787, upload-time = "2026-02-02T12:36:45.071Z" }, - { url = "https://files.pythonhosted.org/packages/f3/b9/7257ac59778f1cd025b26a23c5520a36a424f7f1b068f2442a5b499b7464/jiter-0.13.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:879e768938e7b49b5e90b7e3fecc0dbec01b8cb89595861fb39a8967c5220d09", size = 353880, upload-time = "2026-02-02T12:36:47.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/87/719eec4a3f0841dad99e3d3604ee4cba36af4419a76f3cb0b8e2e691ad67/jiter-0.13.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:682161a67adea11e3aae9038c06c8b4a9a71023228767477d683f69903ebc607", size = 366702, upload-time = "2026-02-02T12:36:48.871Z" }, - { url = "https://files.pythonhosted.org/packages/d2/65/415f0a75cf6921e43365a1bc227c565cb949caca8b7532776e430cbaa530/jiter-0.13.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a13b68cd1cd8cc9de8f244ebae18ccb3e4067ad205220ef324c39181e23bbf66", size = 486319, upload-time = "2026-02-02T12:36:53.006Z" }, - { url = "https://files.pythonhosted.org/packages/54/a2/9e12b48e82c6bbc6081fd81abf915e1443add1b13d8fc586e1d90bb02bb8/jiter-0.13.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87ce0f14c6c08892b610686ae8be350bf368467b6acd5085a5b65441e2bf36d2", size = 372289, upload-time = "2026-02-02T12:36:54.593Z" }, - { url = "https://files.pythonhosted.org/packages/4e/c1/e4693f107a1789a239c759a432e9afc592366f04e901470c2af89cfd28e1/jiter-0.13.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c365005b05505a90d1c47856420980d0237adf82f70c4aff7aebd3c1cc143ad", size = 360165, upload-time = "2026-02-02T12:36:56.112Z" }, - { url = "https://files.pythonhosted.org/packages/17/08/91b9ea976c1c758240614bd88442681a87672eebc3d9a6dde476874e706b/jiter-0.13.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1317fdffd16f5873e46ce27d0e0f7f4f90f0cdf1d86bf6abeaea9f63ca2c401d", size = 389634, upload-time = "2026-02-02T12:36:57.495Z" }, - { url = "https://files.pythonhosted.org/packages/18/23/58325ef99390d6d40427ed6005bf1ad54f2577866594bcf13ce55675f87d/jiter-0.13.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c05b450d37ba0c9e21c77fef1f205f56bcee2330bddca68d344baebfc55ae0df", size = 514933, upload-time = "2026-02-02T12:36:58.909Z" }, - { url = "https://files.pythonhosted.org/packages/5b/25/69f1120c7c395fd276c3996bb8adefa9c6b84c12bb7111e5c6ccdcd8526d/jiter-0.13.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:775e10de3849d0631a97c603f996f518159272db00fdda0a780f81752255ee9d", size = 548842, upload-time = "2026-02-02T12:37:00.433Z" }, - { url = "https://files.pythonhosted.org/packages/18/05/981c9669d86850c5fbb0d9e62bba144787f9fba84546ba43d624ee27ef29/jiter-0.13.0-cp314-cp314-win32.whl", hash = "sha256:632bf7c1d28421c00dd8bbb8a3bac5663e1f57d5cd5ed962bce3c73bf62608e6", size = 202108, upload-time = "2026-02-02T12:37:01.718Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/cdcf54dd0b0341db7d25413229888a346c7130bd20820530905fdb65727b/jiter-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:f22ef501c3f87ede88f23f9b11e608581c14f04db59b6a801f354397ae13739f", size = 204027, upload-time = "2026-02-02T12:37:03.075Z" }, - { url = "https://files.pythonhosted.org/packages/fb/f9/724bcaaab7a3cd727031fe4f6995cb86c4bd344909177c186699c8dec51a/jiter-0.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:07b75fe09a4ee8e0c606200622e571e44943f47254f95e2436c8bdcaceb36d7d", size = 187199, upload-time = "2026-02-02T12:37:04.414Z" }, - { url = "https://files.pythonhosted.org/packages/62/92/1661d8b9fd6a3d7a2d89831db26fe3c1509a287d83ad7838831c7b7a5c7e/jiter-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:964538479359059a35fb400e769295d4b315ae61e4105396d355a12f7fef09f0", size = 318423, upload-time = "2026-02-02T12:37:05.806Z" }, - { url = "https://files.pythonhosted.org/packages/4f/3b/f77d342a54d4ebcd128e520fc58ec2f5b30a423b0fd26acdfc0c6fef8e26/jiter-0.13.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e104da1db1c0991b3eaed391ccd650ae8d947eab1480c733e5a3fb28d4313e40", size = 351438, upload-time = "2026-02-02T12:37:07.189Z" }, - { url = "https://files.pythonhosted.org/packages/76/b3/ba9a69f0e4209bd3331470c723c2f5509e6f0482e416b612431a5061ed71/jiter-0.13.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e3a5f0cde8ff433b8e88e41aa40131455420fb3649a3c7abdda6145f8cb7202", size = 364774, upload-time = "2026-02-02T12:37:08.579Z" }, - { url = "https://files.pythonhosted.org/packages/b3/16/6cdb31fa342932602458dbb631bfbd47f601e03d2e4950740e0b2100b570/jiter-0.13.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57aab48f40be1db920a582b30b116fe2435d184f77f0e4226f546794cedd9cf0", size = 487238, upload-time = "2026-02-02T12:37:10.066Z" }, - { url = "https://files.pythonhosted.org/packages/ed/b1/956cc7abaca8d95c13aa8d6c9b3f3797241c246cd6e792934cc4c8b250d2/jiter-0.13.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7772115877c53f62beeb8fd853cab692dbc04374ef623b30f997959a4c0e7e95", size = 372892, upload-time = "2026-02-02T12:37:11.656Z" }, - { url = "https://files.pythonhosted.org/packages/26/c4/97ecde8b1e74f67b8598c57c6fccf6df86ea7861ed29da84629cdbba76c4/jiter-0.13.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1211427574b17b633cfceba5040de8081e5abf114f7a7602f73d2e16f9fdaa59", size = 360309, upload-time = "2026-02-02T12:37:13.244Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d7/eabe3cf46715854ccc80be2cd78dd4c36aedeb30751dbf85a1d08c14373c/jiter-0.13.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7beae3a3d3b5212d3a55d2961db3c292e02e302feb43fce6a3f7a31b90ea6dfe", size = 389607, upload-time = "2026-02-02T12:37:14.881Z" }, - { url = "https://files.pythonhosted.org/packages/df/2d/03963fc0804e6109b82decfb9974eb92df3797fe7222428cae12f8ccaa0c/jiter-0.13.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e5562a0f0e90a6223b704163ea28e831bd3a9faa3512a711f031611e6b06c939", size = 514986, upload-time = "2026-02-02T12:37:16.326Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/8c83b45eb3eb1c1e18d841fe30b4b5bc5619d781267ca9bc03e005d8fd0a/jiter-0.13.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:6c26a424569a59140fb51160a56df13f438a2b0967365e987889186d5fc2f6f9", size = 548756, upload-time = "2026-02-02T12:37:17.736Z" }, - { url = "https://files.pythonhosted.org/packages/47/66/eea81dfff765ed66c68fd2ed8c96245109e13c896c2a5015c7839c92367e/jiter-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:24dc96eca9f84da4131cdf87a95e6ce36765c3b156fc9ae33280873b1c32d5f6", size = 201196, upload-time = "2026-02-02T12:37:19.101Z" }, - { url = "https://files.pythonhosted.org/packages/ff/32/4ac9c7a76402f8f00d00842a7f6b83b284d0cf7c1e9d4227bc95aa6d17fa/jiter-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0a8d76c7524087272c8ae913f5d9d608bd839154b62c4322ef65723d2e5bb0b8", size = 204215, upload-time = "2026-02-02T12:37:20.495Z" }, - { url = "https://files.pythonhosted.org/packages/f9/8e/7def204fea9f9be8b3c21a6f2dd6c020cf56c7d5ff753e0e23ed7f9ea57e/jiter-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2c26cf47e2cad140fa23b6d58d435a7c0161f5c514284802f25e87fddfe11024", size = 187152, upload-time = "2026-02-02T12:37:22.124Z" }, - { url = "https://files.pythonhosted.org/packages/80/60/e50fa45dd7e2eae049f0ce964663849e897300433921198aef94b6ffa23a/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:3d744a6061afba08dd7ae375dcde870cffb14429b7477e10f67e9e6d68772a0a", size = 305169, upload-time = "2026-02-02T12:37:50.376Z" }, - { url = "https://files.pythonhosted.org/packages/d2/73/a009f41c5eed71c49bec53036c4b33555afcdee70682a18c6f66e396c039/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:ff732bd0a0e778f43d5009840f20b935e79087b4dc65bd36f1cd0f9b04b8ff7f", size = 303808, upload-time = "2026-02-02T12:37:52.092Z" }, - { url = "https://files.pythonhosted.org/packages/c4/10/528b439290763bff3d939268085d03382471b442f212dca4ff5f12802d43/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab44b178f7981fcaea7e0a5df20e773c663d06ffda0198f1a524e91b2fde7e59", size = 337384, upload-time = "2026-02-02T12:37:53.582Z" }, - { url = "https://files.pythonhosted.org/packages/67/8a/a342b2f0251f3dac4ca17618265d93bf244a2a4d089126e81e4c1056ac50/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb00b6d26db67a05fe3e12c76edc75f32077fb51deed13822dc648fa373bc19", size = 343768, upload-time = "2026-02-02T12:37:55.055Z" }, -] - -[[package]] -name = "jmespath" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, -] - -[[package]] -name = "joblib" -version = "1.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, -] - -[[package]] -name = "jsonref" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[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.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "keyring" -version = "25.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, -] - -[[package]] -name = "kiwisolver" -version = "1.4.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/c9/13573a747838aeb1c76e3267620daa054f4152444d1f3d1a2324b78255b5/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ac5a486ac389dddcc5bef4f365b6ae3ffff2c433324fb38dd35e3fab7c957999", size = 123686, upload-time = "2025-08-10T21:26:10.034Z" }, - { url = "https://files.pythonhosted.org/packages/51/ea/2ecf727927f103ffd1739271ca19c424d0e65ea473fbaeea1c014aea93f6/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2ba92255faa7309d06fe44c3a4a97efe1c8d640c2a79a5ef728b685762a6fd2", size = 66460, upload-time = "2025-08-10T21:26:11.083Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/51f5464373ce2aeb5194508298a508b6f21d3867f499556263c64c621914/kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a2899935e724dd1074cb568ce7ac0dce28b2cd6ab539c8e001a8578eb106d14", size = 64952, upload-time = "2025-08-10T21:26:12.058Z" }, - { url = "https://files.pythonhosted.org/packages/70/90/6d240beb0f24b74371762873e9b7f499f1e02166a2d9c5801f4dbf8fa12e/kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f6008a4919fdbc0b0097089f67a1eb55d950ed7e90ce2cc3e640abadd2757a04", size = 1474756, upload-time = "2025-08-10T21:26:13.096Z" }, - { url = "https://files.pythonhosted.org/packages/12/42/f36816eaf465220f683fb711efdd1bbf7a7005a2473d0e4ed421389bd26c/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67bb8b474b4181770f926f7b7d2f8c0248cbcb78b660fdd41a47054b28d2a752", size = 1276404, upload-time = "2025-08-10T21:26:14.457Z" }, - { url = "https://files.pythonhosted.org/packages/2e/64/bc2de94800adc830c476dce44e9b40fd0809cddeef1fde9fcf0f73da301f/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2327a4a30d3ee07d2fbe2e7933e8a37c591663b96ce42a00bc67461a87d7df77", size = 1294410, upload-time = "2025-08-10T21:26:15.73Z" }, - { url = "https://files.pythonhosted.org/packages/5f/42/2dc82330a70aa8e55b6d395b11018045e58d0bb00834502bf11509f79091/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a08b491ec91b1d5053ac177afe5290adacf1f0f6307d771ccac5de30592d198", size = 1343631, upload-time = "2025-08-10T21:26:17.045Z" }, - { url = "https://files.pythonhosted.org/packages/22/fd/f4c67a6ed1aab149ec5a8a401c323cee7a1cbe364381bb6c9c0d564e0e20/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8fc5c867c22b828001b6a38d2eaeb88160bf5783c6cb4a5e440efc981ce286d", size = 2224963, upload-time = "2025-08-10T21:26:18.737Z" }, - { url = "https://files.pythonhosted.org/packages/45/aa/76720bd4cb3713314677d9ec94dcc21ced3f1baf4830adde5bb9b2430a5f/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3b3115b2581ea35bb6d1f24a4c90af37e5d9b49dcff267eeed14c3893c5b86ab", size = 2321295, upload-time = "2025-08-10T21:26:20.11Z" }, - { url = "https://files.pythonhosted.org/packages/80/19/d3ec0d9ab711242f56ae0dc2fc5d70e298bb4a1f9dfab44c027668c673a1/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858e4c22fb075920b96a291928cb7dea5644e94c0ee4fcd5af7e865655e4ccf2", size = 2487987, upload-time = "2025-08-10T21:26:21.49Z" }, - { url = "https://files.pythonhosted.org/packages/39/e9/61e4813b2c97e86b6fdbd4dd824bf72d28bcd8d4849b8084a357bc0dd64d/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed0fecd28cc62c54b262e3736f8bb2512d8dcfdc2bcf08be5f47f96bf405b145", size = 2291817, upload-time = "2025-08-10T21:26:22.812Z" }, - { url = "https://files.pythonhosted.org/packages/a0/41/85d82b0291db7504da3c2defe35c9a8a5c9803a730f297bd823d11d5fb77/kiwisolver-1.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:f68208a520c3d86ea51acf688a3e3002615a7f0238002cccc17affecc86a8a54", size = 73895, upload-time = "2025-08-10T21:26:24.37Z" }, - { url = "https://files.pythonhosted.org/packages/e2/92/5f3068cf15ee5cb624a0c7596e67e2a0bb2adee33f71c379054a491d07da/kiwisolver-1.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:2c1a4f57df73965f3f14df20b80ee29e6a7930a57d2d9e8491a25f676e197c60", size = 64992, upload-time = "2025-08-10T21:26:25.732Z" }, - { url = "https://files.pythonhosted.org/packages/31/c1/c2686cda909742ab66c7388e9a1a8521a59eb89f8bcfbee28fc980d07e24/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5d0432ccf1c7ab14f9949eec60c5d1f924f17c037e9f8b33352fa05799359b8", size = 123681, upload-time = "2025-08-10T21:26:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/ca/f0/f44f50c9f5b1a1860261092e3bc91ecdc9acda848a8b8c6abfda4a24dd5c/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2", size = 66464, upload-time = "2025-08-10T21:26:27.733Z" }, - { url = "https://files.pythonhosted.org/packages/2d/7a/9d90a151f558e29c3936b8a47ac770235f436f2120aca41a6d5f3d62ae8d/kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f", size = 64961, upload-time = "2025-08-10T21:26:28.729Z" }, - { url = "https://files.pythonhosted.org/packages/e9/e9/f218a2cb3a9ffbe324ca29a9e399fa2d2866d7f348ec3a88df87fc248fc5/kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098", size = 1474607, upload-time = "2025-08-10T21:26:29.798Z" }, - { url = "https://files.pythonhosted.org/packages/d9/28/aac26d4c882f14de59041636292bc838db8961373825df23b8eeb807e198/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5656aa670507437af0207645273ccdfee4f14bacd7f7c67a4306d0dcaeaf6eed", size = 1276546, upload-time = "2025-08-10T21:26:31.401Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ad/8bfc1c93d4cc565e5069162f610ba2f48ff39b7de4b5b8d93f69f30c4bed/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bfc08add558155345129c7803b3671cf195e6a56e7a12f3dde7c57d9b417f525", size = 1294482, upload-time = "2025-08-10T21:26:32.721Z" }, - { url = "https://files.pythonhosted.org/packages/da/f1/6aca55ff798901d8ce403206d00e033191f63d82dd708a186e0ed2067e9c/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:40092754720b174e6ccf9e845d0d8c7d8e12c3d71e7fc35f55f3813e96376f78", size = 1343720, upload-time = "2025-08-10T21:26:34.032Z" }, - { url = "https://files.pythonhosted.org/packages/d1/91/eed031876c595c81d90d0f6fc681ece250e14bf6998c3d7c419466b523b7/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497d05f29a1300d14e02e6441cf0f5ee81c1ff5a304b0d9fb77423974684e08b", size = 2224907, upload-time = "2025-08-10T21:26:35.824Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ec/4d1925f2e49617b9cca9c34bfa11adefad49d00db038e692a559454dfb2e/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdd1a81a1860476eb41ac4bc1e07b3f07259e6d55bbf739b79c8aaedcf512799", size = 2321334, upload-time = "2025-08-10T21:26:37.534Z" }, - { url = "https://files.pythonhosted.org/packages/43/cb/450cd4499356f68802750c6ddc18647b8ea01ffa28f50d20598e0befe6e9/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e6b93f13371d341afee3be9f7c5964e3fe61d5fa30f6a30eb49856935dfe4fc3", size = 2488313, upload-time = "2025-08-10T21:26:39.191Z" }, - { url = "https://files.pythonhosted.org/packages/71/67/fc76242bd99f885651128a5d4fa6083e5524694b7c88b489b1b55fdc491d/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d75aa530ccfaa593da12834b86a0724f58bff12706659baa9227c2ccaa06264c", size = 2291970, upload-time = "2025-08-10T21:26:40.828Z" }, - { url = "https://files.pythonhosted.org/packages/75/bd/f1a5d894000941739f2ae1b65a32892349423ad49c2e6d0771d0bad3fae4/kiwisolver-1.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d", size = 73894, upload-time = "2025-08-10T21:26:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/95/38/dce480814d25b99a391abbddadc78f7c117c6da34be68ca8b02d5848b424/kiwisolver-1.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:d4188e73af84ca82468f09cadc5ac4db578109e52acb4518d8154698d3a87ca2", size = 64995, upload-time = "2025-08-10T21:26:43.889Z" }, - { url = "https://files.pythonhosted.org/packages/e2/37/7d218ce5d92dadc5ebdd9070d903e0c7cf7edfe03f179433ac4d13ce659c/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5a0f2724dfd4e3b3ac5a82436a8e6fd16baa7d507117e4279b660fe8ca38a3a1", size = 126510, upload-time = "2025-08-10T21:26:44.915Z" }, - { url = "https://files.pythonhosted.org/packages/23/b0/e85a2b48233daef4b648fb657ebbb6f8367696a2d9548a00b4ee0eb67803/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b11d6a633e4ed84fc0ddafd4ebfd8ea49b3f25082c04ad12b8315c11d504dc1", size = 67903, upload-time = "2025-08-10T21:26:45.934Z" }, - { url = "https://files.pythonhosted.org/packages/44/98/f2425bc0113ad7de24da6bb4dae1343476e95e1d738be7c04d31a5d037fd/kiwisolver-1.4.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61874cdb0a36016354853593cffc38e56fc9ca5aa97d2c05d3dcf6922cd55a11", size = 66402, upload-time = "2025-08-10T21:26:47.101Z" }, - { url = "https://files.pythonhosted.org/packages/98/d8/594657886df9f34c4177cc353cc28ca7e6e5eb562d37ccc233bff43bbe2a/kiwisolver-1.4.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60c439763a969a6af93b4881db0eed8fadf93ee98e18cbc35bc8da868d0c4f0c", size = 1582135, upload-time = "2025-08-10T21:26:48.665Z" }, - { url = "https://files.pythonhosted.org/packages/5c/c6/38a115b7170f8b306fc929e166340c24958347308ea3012c2b44e7e295db/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92a2f997387a1b79a75e7803aa7ded2cfbe2823852ccf1ba3bcf613b62ae3197", size = 1389409, upload-time = "2025-08-10T21:26:50.335Z" }, - { url = "https://files.pythonhosted.org/packages/bf/3b/e04883dace81f24a568bcee6eb3001da4ba05114afa622ec9b6fafdc1f5e/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31d512c812daea6d8b3be3b2bfcbeb091dbb09177706569bcfc6240dcf8b41c", size = 1401763, upload-time = "2025-08-10T21:26:51.867Z" }, - { url = "https://files.pythonhosted.org/packages/9f/80/20ace48e33408947af49d7d15c341eaee69e4e0304aab4b7660e234d6288/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:52a15b0f35dad39862d376df10c5230155243a2c1a436e39eb55623ccbd68185", size = 1453643, upload-time = "2025-08-10T21:26:53.592Z" }, - { url = "https://files.pythonhosted.org/packages/64/31/6ce4380a4cd1f515bdda976a1e90e547ccd47b67a1546d63884463c92ca9/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a30fd6fdef1430fd9e1ba7b3398b5ee4e2887783917a687d86ba69985fb08748", size = 2330818, upload-time = "2025-08-10T21:26:55.051Z" }, - { url = "https://files.pythonhosted.org/packages/fa/e9/3f3fcba3bcc7432c795b82646306e822f3fd74df0ee81f0fa067a1f95668/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cc9617b46837c6468197b5945e196ee9ca43057bb7d9d1ae688101e4e1dddf64", size = 2419963, upload-time = "2025-08-10T21:26:56.421Z" }, - { url = "https://files.pythonhosted.org/packages/99/43/7320c50e4133575c66e9f7dadead35ab22d7c012a3b09bb35647792b2a6d/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:0ab74e19f6a2b027ea4f845a78827969af45ce790e6cb3e1ebab71bdf9f215ff", size = 2594639, upload-time = "2025-08-10T21:26:57.882Z" }, - { url = "https://files.pythonhosted.org/packages/65/d6/17ae4a270d4a987ef8a385b906d2bdfc9fce502d6dc0d3aea865b47f548c/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dba5ee5d3981160c28d5490f0d1b7ed730c22470ff7f6cc26cfcfaacb9896a07", size = 2391741, upload-time = "2025-08-10T21:26:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/2a/8f/8f6f491d595a9e5912971f3f863d81baddccc8a4d0c3749d6a0dd9ffc9df/kiwisolver-1.4.9-cp313-cp313t-win_arm64.whl", hash = "sha256:0749fd8f4218ad2e851e11cc4dc05c7cbc0cbc4267bdfdb31782e65aace4ee9c", size = 68646, upload-time = "2025-08-10T21:27:00.52Z" }, - { url = "https://files.pythonhosted.org/packages/6b/32/6cc0fbc9c54d06c2969faa9c1d29f5751a2e51809dd55c69055e62d9b426/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9928fe1eb816d11ae170885a74d074f57af3a0d65777ca47e9aeb854a1fba386", size = 123806, upload-time = "2025-08-10T21:27:01.537Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dd/2bfb1d4a4823d92e8cbb420fe024b8d2167f72079b3bb941207c42570bdf/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d0005b053977e7b43388ddec89fa567f43d4f6d5c2c0affe57de5ebf290dc552", size = 66605, upload-time = "2025-08-10T21:27:03.335Z" }, - { url = "https://files.pythonhosted.org/packages/f7/69/00aafdb4e4509c2ca6064646cba9cd4b37933898f426756adb2cb92ebbed/kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2635d352d67458b66fd0667c14cb1d4145e9560d503219034a18a87e971ce4f3", size = 64925, upload-time = "2025-08-10T21:27:04.339Z" }, - { url = "https://files.pythonhosted.org/packages/43/dc/51acc6791aa14e5cb6d8a2e28cefb0dc2886d8862795449d021334c0df20/kiwisolver-1.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:767c23ad1c58c9e827b649a9ab7809fd5fd9db266a9cf02b0e926ddc2c680d58", size = 1472414, upload-time = "2025-08-10T21:27:05.437Z" }, - { url = "https://files.pythonhosted.org/packages/3d/bb/93fa64a81db304ac8a246f834d5094fae4b13baf53c839d6bb6e81177129/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72d0eb9fba308b8311685c2268cf7d0a0639a6cd027d8128659f72bdd8a024b4", size = 1281272, upload-time = "2025-08-10T21:27:07.063Z" }, - { url = "https://files.pythonhosted.org/packages/70/e6/6df102916960fb8d05069d4bd92d6d9a8202d5a3e2444494e7cd50f65b7a/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f68e4f3eeca8fb22cc3d731f9715a13b652795ef657a13df1ad0c7dc0e9731df", size = 1298578, upload-time = "2025-08-10T21:27:08.452Z" }, - { url = "https://files.pythonhosted.org/packages/7c/47/e142aaa612f5343736b087864dbaebc53ea8831453fb47e7521fa8658f30/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d84cd4061ae292d8ac367b2c3fa3aad11cb8625a95d135fe93f286f914f3f5a6", size = 1345607, upload-time = "2025-08-10T21:27:10.125Z" }, - { url = "https://files.pythonhosted.org/packages/54/89/d641a746194a0f4d1a3670fb900d0dbaa786fb98341056814bc3f058fa52/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a60ea74330b91bd22a29638940d115df9dc00af5035a9a2a6ad9399ffb4ceca5", size = 2230150, upload-time = "2025-08-10T21:27:11.484Z" }, - { url = "https://files.pythonhosted.org/packages/aa/6b/5ee1207198febdf16ac11f78c5ae40861b809cbe0e6d2a8d5b0b3044b199/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ce6a3a4e106cf35c2d9c4fa17c05ce0b180db622736845d4315519397a77beaf", size = 2325979, upload-time = "2025-08-10T21:27:12.917Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ff/b269eefd90f4ae14dcc74973d5a0f6d28d3b9bb1afd8c0340513afe6b39a/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:77937e5e2a38a7b48eef0585114fe7930346993a88060d0bf886086d2aa49ef5", size = 2491456, upload-time = "2025-08-10T21:27:14.353Z" }, - { url = "https://files.pythonhosted.org/packages/fc/d4/10303190bd4d30de547534601e259a4fbf014eed94aae3e5521129215086/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:24c175051354f4a28c5d6a31c93906dc653e2bf234e8a4bbfb964892078898ce", size = 2294621, upload-time = "2025-08-10T21:27:15.808Z" }, - { url = "https://files.pythonhosted.org/packages/28/e0/a9a90416fce5c0be25742729c2ea52105d62eda6c4be4d803c2a7be1fa50/kiwisolver-1.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:0763515d4df10edf6d06a3c19734e2566368980d21ebec439f33f9eb936c07b7", size = 75417, upload-time = "2025-08-10T21:27:17.436Z" }, - { url = "https://files.pythonhosted.org/packages/1f/10/6949958215b7a9a264299a7db195564e87900f709db9245e4ebdd3c70779/kiwisolver-1.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:0e4e2bf29574a6a7b7f6cb5fa69293b9f96c928949ac4a53ba3f525dffb87f9c", size = 66582, upload-time = "2025-08-10T21:27:18.436Z" }, - { url = "https://files.pythonhosted.org/packages/ec/79/60e53067903d3bc5469b369fe0dfc6b3482e2133e85dae9daa9527535991/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d976bbb382b202f71c67f77b0ac11244021cfa3f7dfd9e562eefcea2df711548", size = 126514, upload-time = "2025-08-10T21:27:19.465Z" }, - { url = "https://files.pythonhosted.org/packages/25/d1/4843d3e8d46b072c12a38c97c57fab4608d36e13fe47d47ee96b4d61ba6f/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2489e4e5d7ef9a1c300a5e0196e43d9c739f066ef23270607d45aba368b91f2d", size = 67905, upload-time = "2025-08-10T21:27:20.51Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ae/29ffcbd239aea8b93108de1278271ae764dfc0d803a5693914975f200596/kiwisolver-1.4.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e2ea9f7ab7fbf18fffb1b5434ce7c69a07582f7acc7717720f1d69f3e806f90c", size = 66399, upload-time = "2025-08-10T21:27:21.496Z" }, - { url = "https://files.pythonhosted.org/packages/a1/ae/d7ba902aa604152c2ceba5d352d7b62106bedbccc8e95c3934d94472bfa3/kiwisolver-1.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b34e51affded8faee0dfdb705416153819d8ea9250bbbf7ea1b249bdeb5f1122", size = 1582197, upload-time = "2025-08-10T21:27:22.604Z" }, - { url = "https://files.pythonhosted.org/packages/f2/41/27c70d427eddb8bc7e4f16420a20fefc6f480312122a59a959fdfe0445ad/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8aacd3d4b33b772542b2e01beb50187536967b514b00003bdda7589722d2a64", size = 1390125, upload-time = "2025-08-10T21:27:24.036Z" }, - { url = "https://files.pythonhosted.org/packages/41/42/b3799a12bafc76d962ad69083f8b43b12bf4fe78b097b12e105d75c9b8f1/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7cf974dd4e35fa315563ac99d6287a1024e4dc2077b8a7d7cd3d2fb65d283134", size = 1402612, upload-time = "2025-08-10T21:27:25.773Z" }, - { url = "https://files.pythonhosted.org/packages/d2/b5/a210ea073ea1cfaca1bb5c55a62307d8252f531beb364e18aa1e0888b5a0/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85bd218b5ecfbee8c8a82e121802dcb519a86044c9c3b2e4aef02fa05c6da370", size = 1453990, upload-time = "2025-08-10T21:27:27.089Z" }, - { url = "https://files.pythonhosted.org/packages/5f/ce/a829eb8c033e977d7ea03ed32fb3c1781b4fa0433fbadfff29e39c676f32/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0856e241c2d3df4efef7c04a1e46b1936b6120c9bcf36dd216e3acd84bc4fb21", size = 2331601, upload-time = "2025-08-10T21:27:29.343Z" }, - { url = "https://files.pythonhosted.org/packages/e0/4b/b5e97eb142eb9cd0072dacfcdcd31b1c66dc7352b0f7c7255d339c0edf00/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9af39d6551f97d31a4deebeac6f45b156f9755ddc59c07b402c148f5dbb6482a", size = 2422041, upload-time = "2025-08-10T21:27:30.754Z" }, - { url = "https://files.pythonhosted.org/packages/40/be/8eb4cd53e1b85ba4edc3a9321666f12b83113a178845593307a3e7891f44/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:bb4ae2b57fc1d8cbd1cf7b1d9913803681ffa903e7488012be5b76dedf49297f", size = 2594897, upload-time = "2025-08-10T21:27:32.803Z" }, - { url = "https://files.pythonhosted.org/packages/99/dd/841e9a66c4715477ea0abc78da039832fbb09dac5c35c58dc4c41a407b8a/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aedff62918805fb62d43a4aa2ecd4482c380dc76cd31bd7c8878588a61bd0369", size = 2391835, upload-time = "2025-08-10T21:27:34.23Z" }, - { url = "https://files.pythonhosted.org/packages/0c/28/4b2e5c47a0da96896fdfdb006340ade064afa1e63675d01ea5ac222b6d52/kiwisolver-1.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:1fa333e8b2ce4d9660f2cda9c0e1b6bafcfb2457a9d259faa82289e73ec24891", size = 79988, upload-time = "2025-08-10T21:27:35.587Z" }, - { url = "https://files.pythonhosted.org/packages/80/be/3578e8afd18c88cdf9cb4cffde75a96d2be38c5a903f1ed0ceec061bd09e/kiwisolver-1.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:4a48a2ce79d65d363597ef7b567ce3d14d68783d2b2263d98db3d9477805ba32", size = 70260, upload-time = "2025-08-10T21:27:36.606Z" }, -] - -[[package]] -name = "kubernetes" -version = "35.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "durationpy" }, - { name = "python-dateutil" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "requests-oauthlib" }, - { name = "six" }, - { name = "urllib3" }, - { name = "websocket-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2c/8f/85bf51ad4150f64e8c665daf0d9dfe9787ae92005efb9a4d1cba592bd79d/kubernetes-35.0.0.tar.gz", hash = "sha256:3d00d344944239821458b9efd484d6df9f011da367ecb155dadf9513f05f09ee", size = 1094642, upload-time = "2026-01-16T01:05:27.76Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/70/05b685ea2dffcb2adbf3cdcea5d8865b7bc66f67249084cf845012a0ff13/kubernetes-35.0.0-py2.py3-none-any.whl", hash = "sha256:39e2b33b46e5834ef6c3985ebfe2047ab39135d41de51ce7641a7ca5b372a13d", size = 2017602, upload-time = "2026-01-16T01:05:25.991Z" }, -] - -[[package]] -name = "lance-namespace" -version = "0.4.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "lance-namespace-urllib3-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b4/b5/0c3c55cf336b1e90392c2e24ac833551659e8bb3c61644b2d94825eb31bd/lance_namespace-0.4.5.tar.gz", hash = "sha256:0aee0abed3a1fa762c2955c7d12bb3004cea5c82ba28f6fcb9fe79d0cc19e317", size = 9827, upload-time = "2026-01-07T19:20:23.005Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/88/173687dad72baf819223e3b506898e386bc88c26ff8da5e8013291e02daf/lance_namespace-0.4.5-py3-none-any.whl", hash = "sha256:cd1a4f789de03ba23a0c16f100b1464cca572a5d04e428917a54d09db912d548", size = 11703, upload-time = "2026-01-07T19:20:25.394Z" }, -] - -[[package]] -name = "lance-namespace-urllib3-client" -version = "0.4.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dateutil" }, - { name = "typing-extensions" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/a9/4e527c2f05704565618b239b0965f829d1a194837f01234af3f8e2f33d92/lance_namespace_urllib3_client-0.4.5.tar.gz", hash = "sha256:184deda8cf8700926d994618187053c644eb1f2866a4479e7b80843cacc92b1c", size = 159726, upload-time = "2026-01-07T19:20:24.025Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/86/0adee7190408a28dcc5a0562c674537457e3de59ee51d1c724ecdc4a9930/lance_namespace_urllib3_client-0.4.5-py3-none-any.whl", hash = "sha256:2ee154d616ba4721f0bfdf043d33c4fef2e79d380653e2f263058ab00fb4adf4", size = 277969, upload-time = "2026-01-07T19:20:26.597Z" }, -] - -[[package]] -name = "lancedb" -version = "0.27.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "deprecation" }, - { name = "lance-namespace" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pyarrow" }, - { name = "pydantic" }, - { name = "tqdm" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/35/135ee7e3de58389074ad49b389adb8f431dc3f0034afbed1a9122c223c68/lancedb-0.27.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8aea87c3002850e98e4ac095c165dd819edd69f7c50e418f13f5917d1b9e0dcb", size = 43540316, upload-time = "2026-01-26T23:56:19.228Z" }, - { url = "https://files.pythonhosted.org/packages/16/cf/ea458fa50ef29c1a0653e1af6ea0599e532180267f49ca0bcf0049b0d8e3/lancedb-0.27.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:382666cddfb8b87d1efef4797bbc92cb1c3263b9b40894e5194ed5ed4e4486d4", size = 45409178, upload-time = "2026-01-27T03:25:09.932Z" }, - { url = "https://files.pythonhosted.org/packages/ee/cd/30714b878ec876eda3ce88637d6ef8da44484a065ec050dcfba3ad888465/lancedb-0.27.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7835e84d92631ddc7e269c8a18691ec16f24fe32f0fd14138d76951f530c28b9", size = 48484253, upload-time = "2026-01-27T03:28:16.048Z" }, - { url = "https://files.pythonhosted.org/packages/69/c2/19c1b8b7b36a0445e31fa532619bb75c4e76a91fff0514439dea2c4194d6/lancedb-0.27.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5996f7e36ae4cf580693fae33f560a21f29640b1ae0e923dcd8efea65ee8a78e", size = 45427415, upload-time = "2026-01-27T03:23:26.822Z" }, - { url = "https://files.pythonhosted.org/packages/eb/f1/794e9bc8d2adc9130c55695979afb66b0121c9d2abacdd19ce112e201879/lancedb-0.27.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:37e80565729555f6fc390a623da4f26392c463ba35e7634b2e706c1f9ac77e47", size = 48531937, upload-time = "2026-01-27T03:27:57.455Z" }, - { url = "https://files.pythonhosted.org/packages/3d/96/fa3cb37a6ffe7b81073d8c74f7cb95204d0922ac1668b264685aa34add20/lancedb-0.27.1-cp39-abi3-win_amd64.whl", hash = "sha256:f2150a66758ce6fe3cff226ac1ffcac2d5f5e2c9b35bc4c2d5923abcebef98cc", size = 53374010, upload-time = "2026-01-27T03:57:13.434Z" }, -] - -[[package]] -name = "libcst" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4d/c4/5577b92173199299e0d32404aa92a156d353d6ec0f74148f6e418e0defef/libcst-1.5.0.tar.gz", hash = "sha256:8478abf21ae3861a073e898d80b822bd56e578886331b33129ba77fec05b8c24", size = 772970, upload-time = "2024-10-10T14:15:08.919Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/23/9cdb3362ad75490108a03abeaae8d7f7fb0d86586d806102ae9d9690d6b8/libcst-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:83bc5fbe34d33597af1d5ea113dcb9b5dd5afe5a5f4316bac4293464d5e3971a", size = 2108563, upload-time = "2024-10-10T14:14:30.717Z" }, - { url = "https://files.pythonhosted.org/packages/48/ec/4a1a34c3dbe6d51815700a0c14991f4124f10e82f9959d4fb5a9b0b06c74/libcst-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f10124bf99a0b075eae136ef0ce06204e5f6b8da4596a9c4853a0663e80ddf3", size = 2024056, upload-time = "2024-10-10T14:14:32.163Z" }, - { url = "https://files.pythonhosted.org/packages/da/b7/1976377c19f9477267daac2ea8e2d5a72ce12d5b523ff147d404fb7ae74e/libcst-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48e581af6127c5af4c9f483e5986d94f0c6b2366967ee134f0a8eba0aa4c8c12", size = 2199473, upload-time = "2024-10-10T14:14:35.486Z" }, - { url = "https://files.pythonhosted.org/packages/63/c4/e056f3f34642f294421bd4a4d4b40aeccaf153a456bcb4d7e54f4337143f/libcst-1.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dba93cca0a5c6d771ed444c44d21ce8ea9b277af7036cea3743677aba9fbbb8", size = 2251411, upload-time = "2024-10-10T14:14:37.44Z" }, - { url = "https://files.pythonhosted.org/packages/e8/d6/574fc6c8b0ca81586ee05f284ef6987730b841b31ce246ef9d3c45f17ec4/libcst-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b5c4d87721a7bab265c202575809b810815ab81d5e2e7a5d4417a087975840", size = 2323144, upload-time = "2024-10-10T14:14:39.103Z" }, - { url = "https://files.pythonhosted.org/packages/b1/92/5cb62834eec397f4b3218c03acc28b6b8470f87c8dad9e9b0fd738c3948c/libcst-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:b48bf71d52c1e891a0948465a94d9817b5fc1ec1a09603566af90585f3b11948", size = 2029603, upload-time = "2024-10-10T14:14:42.451Z" }, - { url = "https://files.pythonhosted.org/packages/60/5e/dd156f628fed03a273d995008f1669e1964727df6a8818bbedaac51f9ae5/libcst-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:88520b6dea59eaea0cae80f77c0a632604a82c5b2d23dedb4b5b34035cbf1615", size = 2108562, upload-time = "2024-10-10T14:14:44.894Z" }, - { url = "https://files.pythonhosted.org/packages/2c/54/f63bf0bd2d70179e0557c9474a0511e33e646d398945b5a01de36237ce60/libcst-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:208ea92d80b2eeed8cbc879d5f39f241582a5d56b916b1b65ed2be2f878a2425", size = 2024057, upload-time = "2024-10-10T14:14:47.41Z" }, - { url = "https://files.pythonhosted.org/packages/dc/37/ce62947fd7305fb501589e4b8f6e82e3cf61fca2d62392e281c17a2112f5/libcst-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4592872aaf5b7fa5c2727a7d73c0985261f1b3fe7eff51f4fd5b8174f30b4e2", size = 2199474, upload-time = "2024-10-10T14:14:49.1Z" }, - { url = "https://files.pythonhosted.org/packages/c9/95/b878c95af17f3e341ac5dc18e3160d45d86b2c05a0cafd866ceb0b766bbd/libcst-1.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2788b2b5838b78fe15df8e9fa6b6903195ea49b2d2ba43e8f423f6c90e4b69f", size = 2251410, upload-time = "2024-10-10T14:14:50.645Z" }, - { url = "https://files.pythonhosted.org/packages/e1/26/697b54aa839c4dc6ea2787d5e977ed4be0636149f85df1a0cba7a29bd188/libcst-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b5b5bcd3a9ba92840f27ad34eaa038acbee195ec337da39536c0a2efbbf28efd", size = 2323144, upload-time = "2024-10-10T14:14:53.023Z" }, - { url = "https://files.pythonhosted.org/packages/a0/9f/5b5481d716670ed5fbd8d06dfa94b7108272b645da2f2406eb909cb6a450/libcst-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:4d6acb0bdee1e55b44c6215c59755ec4693ac01e74bb1fde04c37358b378835d", size = 2029600, upload-time = "2024-10-10T14:14:54.815Z" }, -] - -[[package]] -name = "litellm" -version = "1.81.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "click" }, - { name = "fastuuid" }, - { name = "httpx" }, - { name = "importlib-metadata" }, - { name = "jinja2" }, - { name = "jsonschema" }, - { name = "openai" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "tiktoken" }, - { name = "tokenizers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2e/f3/194a2dca6cb3eddb89f4bc2920cf5e27542256af907c23be13c61fe7e021/litellm-1.81.6.tar.gz", hash = "sha256:f02b503dfb7d66d1c939f82e4db21aeec1d6e2ed1fe3f5cd02aaec3f792bc4ae", size = 13878107, upload-time = "2026-02-01T04:02:27.36Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/05/3516cc7386b220d388aa0bd833308c677e94eceb82b2756dd95e06f6a13f/litellm-1.81.6-py3-none-any.whl", hash = "sha256:573206ba194d49a1691370ba33f781671609ac77c35347f8a0411d852cf6341a", size = 12224343, upload-time = "2026-02-01T04:02:23.704Z" }, -] - -[[package]] -name = "loguru" -version = "0.7.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "win32-setctime", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, -] - -[[package]] -name = "lupa" -version = "2.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/1c/191c3e6ec6502e3dbe25a53e27f69a5daeac3e56de1f73c0138224171ead/lupa-2.6.tar.gz", hash = "sha256:9a770a6e89576be3447668d7ced312cd6fd41d3c13c2462c9dc2c2ab570e45d9", size = 7240282, upload-time = "2025-10-24T07:20:29.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/86/ce243390535c39d53ea17ccf0240815e6e457e413e40428a658ea4ee4b8d/lupa-2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47ce718817ef1cc0c40d87c3d5ae56a800d61af00fbc0fad1ca9be12df2f3b56", size = 951707, upload-time = "2025-10-24T07:18:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/cedea5e6cbeb54396fdcc55f6b741696f3f036d23cfaf986d50d680446da/lupa-2.6-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:7aba985b15b101495aa4b07112cdc08baa0c545390d560ad5cfde2e9e34f4d58", size = 1916703, upload-time = "2025-10-24T07:18:05.6Z" }, - { url = "https://files.pythonhosted.org/packages/24/be/3d6b5f9a8588c01a4d88129284c726017b2089f3a3fd3ba8bd977292fea0/lupa-2.6-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:b766f62f95b2739f2248977d29b0722e589dcf4f0ccfa827ccbd29f0148bd2e5", size = 985152, upload-time = "2025-10-24T07:18:08.561Z" }, - { url = "https://files.pythonhosted.org/packages/eb/23/9f9a05beee5d5dce9deca4cb07c91c40a90541fc0a8e09db4ee670da550f/lupa-2.6-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:00a934c23331f94cb51760097ebfab14b005d55a6b30a2b480e3c53dd2fa290d", size = 1159599, upload-time = "2025-10-24T07:18:10.346Z" }, - { url = "https://files.pythonhosted.org/packages/40/4e/e7c0583083db9d7f1fd023800a9767d8e4391e8330d56c2373d890ac971b/lupa-2.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21de9f38bd475303e34a042b7081aabdf50bd9bafd36ce4faea2f90fd9f15c31", size = 1038686, upload-time = "2025-10-24T07:18:12.112Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/5a4f7d959d4feba5e203ff0c31889e74d1ca3153122be4a46dca7d92bf7c/lupa-2.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf3bda96d3fc41237e964a69c23647d50d4e28421111360274d4799832c560e9", size = 2071956, upload-time = "2025-10-24T07:18:14.572Z" }, - { url = "https://files.pythonhosted.org/packages/92/34/2f4f13ca65d01169b1720176aedc4af17bc19ee834598c7292db232cb6dc/lupa-2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a76ead245da54801a81053794aa3975f213221f6542d14ec4b859ee2e7e0323", size = 1057199, upload-time = "2025-10-24T07:18:16.379Z" }, - { url = "https://files.pythonhosted.org/packages/35/2a/5f7d2eebec6993b0dcd428e0184ad71afb06a45ba13e717f6501bfed1da3/lupa-2.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8dd0861741caa20886ddbda0a121d8e52fb9b5bb153d82fa9bba796962bf30e8", size = 1173693, upload-time = "2025-10-24T07:18:18.153Z" }, - { url = "https://files.pythonhosted.org/packages/e4/29/089b4d2f8e34417349af3904bb40bec40b65c8731f45e3fd8d497ca573e5/lupa-2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:239e63948b0b23023f81d9a19a395e768ed3da6a299f84e7963b8f813f6e3f9c", size = 2164394, upload-time = "2025-10-24T07:18:20.403Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1b/79c17b23c921f81468a111cad843b076a17ef4b684c4a8dff32a7969c3f0/lupa-2.6-cp312-cp312-win32.whl", hash = "sha256:325894e1099499e7a6f9c351147661a2011887603c71086d36fe0f964d52d1ce", size = 1420647, upload-time = "2025-10-24T07:18:23.368Z" }, - { url = "https://files.pythonhosted.org/packages/b8/15/5121e68aad3584e26e1425a5c9a79cd898f8a152292059e128c206ee817c/lupa-2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c735a1ce8ee60edb0fe71d665f1e6b7c55c6021f1d340eb8c865952c602cd36f", size = 1688529, upload-time = "2025-10-24T07:18:25.523Z" }, - { url = "https://files.pythonhosted.org/packages/28/1d/21176b682ca5469001199d8b95fa1737e29957a3d185186e7a8b55345f2e/lupa-2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:663a6e58a0f60e7d212017d6678639ac8df0119bc13c2145029dcba084391310", size = 947232, upload-time = "2025-10-24T07:18:27.878Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4c/d327befb684660ca13cf79cd1f1d604331808f9f1b6fb6bf57832f8edf80/lupa-2.6-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:d1f5afda5c20b1f3217a80e9bc1b77037f8a6eb11612fd3ada19065303c8f380", size = 1908625, upload-time = "2025-10-24T07:18:29.944Z" }, - { url = "https://files.pythonhosted.org/packages/66/8e/ad22b0a19454dfd08662237a84c792d6d420d36b061f239e084f29d1a4f3/lupa-2.6-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:26f2b3c085fe76e9119e48c1013c1cccdc1f51585d456858290475aa38e7089e", size = 981057, upload-time = "2025-10-24T07:18:31.553Z" }, - { url = "https://files.pythonhosted.org/packages/5c/48/74859073ab276bd0566c719f9ca0108b0cfc1956ca0d68678d117d47d155/lupa-2.6-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:60d2f902c7b96fb8ab98493dcff315e7bb4d0b44dc9dd76eb37de575025d5685", size = 1156227, upload-time = "2025-10-24T07:18:33.981Z" }, - { url = "https://files.pythonhosted.org/packages/09/6c/0e9ded061916877253c2266074060eb71ed99fb21d73c8c114a76725bce2/lupa-2.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a02d25dee3a3250967c36590128d9220ae02f2eda166a24279da0b481519cbff", size = 1035752, upload-time = "2025-10-24T07:18:36.32Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ef/f8c32e454ef9f3fe909f6c7d57a39f950996c37a3deb7b391fec7903dab7/lupa-2.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eae1ee16b886b8914ff292dbefbf2f48abfbdee94b33a88d1d5475e02423203", size = 2069009, upload-time = "2025-10-24T07:18:38.072Z" }, - { url = "https://files.pythonhosted.org/packages/53/dc/15b80c226a5225815a890ee1c11f07968e0aba7a852df41e8ae6fe285063/lupa-2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0edd5073a4ee74ab36f74fe61450148e6044f3952b8d21248581f3c5d1a58be", size = 1056301, upload-time = "2025-10-24T07:18:40.165Z" }, - { url = "https://files.pythonhosted.org/packages/31/14/2086c1425c985acfb30997a67e90c39457122df41324d3c179d6ee2292c6/lupa-2.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c53ee9f22a8a17e7d4266ad48e86f43771951797042dd51d1494aaa4f5f3f0a", size = 1170673, upload-time = "2025-10-24T07:18:42.426Z" }, - { url = "https://files.pythonhosted.org/packages/10/e5/b216c054cf86576c0191bf9a9f05de6f7e8e07164897d95eea0078dca9b2/lupa-2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de7c0f157a9064a400d828789191a96da7f4ce889969a588b87ec80de9b14772", size = 2162227, upload-time = "2025-10-24T07:18:46.112Z" }, - { url = "https://files.pythonhosted.org/packages/59/2f/33ecb5bedf4f3bc297ceacb7f016ff951331d352f58e7e791589609ea306/lupa-2.6-cp313-cp313-win32.whl", hash = "sha256:ee9523941ae0a87b5b703417720c5d78f72d2f5bc23883a2ea80a949a3ed9e75", size = 1419558, upload-time = "2025-10-24T07:18:48.371Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b4/55e885834c847ea610e111d87b9ed4768f0afdaeebc00cd46810f25029f6/lupa-2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b1335a5835b0a25ebdbc75cf0bda195e54d133e4d994877ef025e218c2e59db9", size = 1683424, upload-time = "2025-10-24T07:18:50.976Z" }, - { url = "https://files.pythonhosted.org/packages/66/9d/d9427394e54d22a35d1139ef12e845fd700d4872a67a34db32516170b746/lupa-2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dcb6d0a3264873e1653bc188499f48c1fb4b41a779e315eba45256cfe7bc33c1", size = 953818, upload-time = "2025-10-24T07:18:53.378Z" }, - { url = "https://files.pythonhosted.org/packages/10/41/27bbe81953fb2f9ecfced5d9c99f85b37964cfaf6aa8453bb11283983721/lupa-2.6-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:a37e01f2128f8c36106726cb9d360bac087d58c54b4522b033cc5691c584db18", size = 1915850, upload-time = "2025-10-24T07:18:55.259Z" }, - { url = "https://files.pythonhosted.org/packages/a3/98/f9ff60db84a75ba8725506bbf448fb085bc77868a021998ed2a66d920568/lupa-2.6-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:458bd7e9ff3c150b245b0fcfbb9bd2593d1152ea7f0a7b91c1d185846da033fe", size = 982344, upload-time = "2025-10-24T07:18:57.05Z" }, - { url = "https://files.pythonhosted.org/packages/41/f7/f39e0f1c055c3b887d86b404aaf0ca197b5edfd235a8b81b45b25bac7fc3/lupa-2.6-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:052ee82cac5206a02df77119c325339acbc09f5ce66967f66a2e12a0f3211cad", size = 1156543, upload-time = "2025-10-24T07:18:59.251Z" }, - { url = "https://files.pythonhosted.org/packages/9e/9c/59e6cffa0d672d662ae17bd7ac8ecd2c89c9449dee499e3eb13ca9cd10d9/lupa-2.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96594eca3c87dd07938009e95e591e43d554c1dbd0385be03c100367141db5a8", size = 1047974, upload-time = "2025-10-24T07:19:01.449Z" }, - { url = "https://files.pythonhosted.org/packages/23/c6/a04e9cef7c052717fcb28fb63b3824802488f688391895b618e39be0f684/lupa-2.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8faddd9d198688c8884091173a088a8e920ecc96cda2ffed576a23574c4b3f6", size = 2073458, upload-time = "2025-10-24T07:19:03.369Z" }, - { url = "https://files.pythonhosted.org/packages/e6/10/824173d10f38b51fc77785228f01411b6ca28826ce27404c7c912e0e442c/lupa-2.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:daebb3a6b58095c917e76ba727ab37b27477fb926957c825205fbda431552134", size = 1067683, upload-time = "2025-10-24T07:19:06.2Z" }, - { url = "https://files.pythonhosted.org/packages/b6/dc/9692fbcf3c924d9c4ece2d8d2f724451ac2e09af0bd2a782db1cef34e799/lupa-2.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f3154e68972befe0f81564e37d8142b5d5d79931a18309226a04ec92487d4ea3", size = 1171892, upload-time = "2025-10-24T07:19:08.544Z" }, - { url = "https://files.pythonhosted.org/packages/84/ff/e318b628d4643c278c96ab3ddea07fc36b075a57383c837f5b11e537ba9d/lupa-2.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e4dadf77b9fedc0bfa53417cc28dc2278a26d4cbd95c29f8927ad4d8fe0a7ef9", size = 2166641, upload-time = "2025-10-24T07:19:10.485Z" }, - { url = "https://files.pythonhosted.org/packages/12/f7/a6f9ec2806cf2d50826980cdb4b3cffc7691dc6f95e13cc728846d5cb793/lupa-2.6-cp314-cp314-win32.whl", hash = "sha256:cb34169c6fa3bab3e8ac58ca21b8a7102f6a94b6a5d08d3636312f3f02fafd8f", size = 1456857, upload-time = "2025-10-24T07:19:37.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/de/df71896f25bdc18360fdfa3b802cd7d57d7fede41a0e9724a4625b412c85/lupa-2.6-cp314-cp314-win_amd64.whl", hash = "sha256:b74f944fe46c421e25d0f8692aef1e842192f6f7f68034201382ac440ef9ea67", size = 1731191, upload-time = "2025-10-24T07:19:40.281Z" }, - { url = "https://files.pythonhosted.org/packages/47/3c/a1f23b01c54669465f5f4c4083107d496fbe6fb45998771420e9aadcf145/lupa-2.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0e21b716408a21ab65723f8841cf7f2f37a844b7a965eeabb785e27fca4099cf", size = 999343, upload-time = "2025-10-24T07:19:12.519Z" }, - { url = "https://files.pythonhosted.org/packages/c5/6d/501994291cb640bfa2ccf7f554be4e6914afa21c4026bd01bff9ca8aac57/lupa-2.6-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:589db872a141bfff828340079bbdf3e9a31f2689f4ca0d88f97d9e8c2eae6142", size = 2000730, upload-time = "2025-10-24T07:19:14.869Z" }, - { url = "https://files.pythonhosted.org/packages/53/a5/457ffb4f3f20469956c2d4c4842a7675e884efc895b2f23d126d23e126cc/lupa-2.6-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:cd852a91a4a9d4dcbb9a58100f820a75a425703ec3e3f049055f60b8533b7953", size = 1021553, upload-time = "2025-10-24T07:19:17.123Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/36bb5a5d0960f2a5c7c700e0819abb76fd9bf9c1d8a66e5106416d6e9b14/lupa-2.6-cp314-cp314t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:0334753be028358922415ca97a64a3048e4ed155413fc4eaf87dd0a7e2752983", size = 1133275, upload-time = "2025-10-24T07:19:20.51Z" }, - { url = "https://files.pythonhosted.org/packages/19/86/202ff4429f663013f37d2229f6176ca9f83678a50257d70f61a0a97281bf/lupa-2.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:661d895cd38c87658a34780fac54a690ec036ead743e41b74c3fb81a9e65a6aa", size = 1038441, upload-time = "2025-10-24T07:19:22.509Z" }, - { url = "https://files.pythonhosted.org/packages/a7/42/d8125f8e420714e5b52e9c08d88b5329dfb02dcca731b4f21faaee6cc5b5/lupa-2.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aa58454ccc13878cc177c62529a2056be734da16369e451987ff92784994ca7", size = 2058324, upload-time = "2025-10-24T07:19:24.979Z" }, - { url = "https://files.pythonhosted.org/packages/2b/2c/47bf8b84059876e877a339717ddb595a4a7b0e8740bacae78ba527562e1c/lupa-2.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1425017264e470c98022bba8cff5bd46d054a827f5df6b80274f9cc71dafd24f", size = 1060250, upload-time = "2025-10-24T07:19:27.262Z" }, - { url = "https://files.pythonhosted.org/packages/c2/06/d88add2b6406ca1bdec99d11a429222837ca6d03bea42ca75afa169a78cb/lupa-2.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:224af0532d216e3105f0a127410f12320f7c5f1aa0300bdf9646b8d9afb0048c", size = 1151126, upload-time = "2025-10-24T07:19:29.522Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a0/89e6a024c3b4485b89ef86881c9d55e097e7cb0bdb74efb746f2fa6a9a76/lupa-2.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9abb98d5a8fd27c8285302e82199f0e56e463066f88f619d6594a450bf269d80", size = 2153693, upload-time = "2025-10-24T07:19:31.379Z" }, - { url = "https://files.pythonhosted.org/packages/b6/36/a0f007dc58fc1bbf51fb85dcc82fcb1f21b8c4261361de7dab0e3d8521ef/lupa-2.6-cp314-cp314t-win32.whl", hash = "sha256:1849efeba7a8f6fb8aa2c13790bee988fd242ae404bd459509640eeea3d1e291", size = 1590104, upload-time = "2025-10-24T07:19:33.514Z" }, - { url = "https://files.pythonhosted.org/packages/7d/5e/db903ce9cf82c48d6b91bf6d63ae4c8d0d17958939a4e04ba6b9f38b8643/lupa-2.6-cp314-cp314t-win_amd64.whl", hash = "sha256:fc1498d1a4fc028bc521c26d0fad4ca00ed63b952e32fb95949bda76a04bad52", size = 1913818, upload-time = "2025-10-24T07:19:36.039Z" }, -] - -[[package]] -name = "lxml" -version = "6.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426, upload-time = "2025-09-22T04:04:59.287Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/c8/8ff2bc6b920c84355146cd1ab7d181bc543b89241cfb1ebee824a7c81457/lxml-6.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a59f5448ba2ceccd06995c95ea59a7674a10de0810f2ce90c9006f3cbc044456", size = 8661887, upload-time = "2025-09-22T04:01:17.265Z" }, - { url = "https://files.pythonhosted.org/packages/37/6f/9aae1008083bb501ef63284220ce81638332f9ccbfa53765b2b7502203cf/lxml-6.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e8113639f3296706fbac34a30813929e29247718e88173ad849f57ca59754924", size = 4667818, upload-time = "2025-09-22T04:01:19.688Z" }, - { url = "https://files.pythonhosted.org/packages/f1/ca/31fb37f99f37f1536c133476674c10b577e409c0a624384147653e38baf2/lxml-6.0.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a8bef9b9825fa8bc816a6e641bb67219489229ebc648be422af695f6e7a4fa7f", size = 4950807, upload-time = "2025-09-22T04:01:21.487Z" }, - { url = "https://files.pythonhosted.org/packages/da/87/f6cb9442e4bada8aab5ae7e1046264f62fdbeaa6e3f6211b93f4c0dd97f1/lxml-6.0.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:65ea18d710fd14e0186c2f973dc60bb52039a275f82d3c44a0e42b43440ea534", size = 5109179, upload-time = "2025-09-22T04:01:23.32Z" }, - { url = "https://files.pythonhosted.org/packages/c8/20/a7760713e65888db79bbae4f6146a6ae5c04e4a204a3c48896c408cd6ed2/lxml-6.0.2-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c371aa98126a0d4c739ca93ceffa0fd7a5d732e3ac66a46e74339acd4d334564", size = 5023044, upload-time = "2025-09-22T04:01:25.118Z" }, - { url = "https://files.pythonhosted.org/packages/a2/b0/7e64e0460fcb36471899f75831509098f3fd7cd02a3833ac517433cb4f8f/lxml-6.0.2-cp312-cp312-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:700efd30c0fa1a3581d80a748157397559396090a51d306ea59a70020223d16f", size = 5359685, upload-time = "2025-09-22T04:01:27.398Z" }, - { url = "https://files.pythonhosted.org/packages/b9/e1/e5df362e9ca4e2f48ed6411bd4b3a0ae737cc842e96877f5bf9428055ab4/lxml-6.0.2-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c33e66d44fe60e72397b487ee92e01da0d09ba2d66df8eae42d77b6d06e5eba0", size = 5654127, upload-time = "2025-09-22T04:01:29.629Z" }, - { url = "https://files.pythonhosted.org/packages/c6/d1/232b3309a02d60f11e71857778bfcd4acbdb86c07db8260caf7d008b08f8/lxml-6.0.2-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90a345bbeaf9d0587a3aaffb7006aa39ccb6ff0e96a57286c0cb2fd1520ea192", size = 5253958, upload-time = "2025-09-22T04:01:31.535Z" }, - { url = "https://files.pythonhosted.org/packages/35/35/d955a070994725c4f7d80583a96cab9c107c57a125b20bb5f708fe941011/lxml-6.0.2-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:064fdadaf7a21af3ed1dcaa106b854077fbeada827c18f72aec9346847cd65d0", size = 4711541, upload-time = "2025-09-22T04:01:33.801Z" }, - { url = "https://files.pythonhosted.org/packages/1e/be/667d17363b38a78c4bd63cfd4b4632029fd68d2c2dc81f25ce9eb5224dd5/lxml-6.0.2-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fbc74f42c3525ac4ffa4b89cbdd00057b6196bcefe8bce794abd42d33a018092", size = 5267426, upload-time = "2025-09-22T04:01:35.639Z" }, - { url = "https://files.pythonhosted.org/packages/ea/47/62c70aa4a1c26569bc958c9ca86af2bb4e1f614e8c04fb2989833874f7ae/lxml-6.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6ddff43f702905a4e32bc24f3f2e2edfe0f8fde3277d481bffb709a4cced7a1f", size = 5064917, upload-time = "2025-09-22T04:01:37.448Z" }, - { url = "https://files.pythonhosted.org/packages/bd/55/6ceddaca353ebd0f1908ef712c597f8570cc9c58130dbb89903198e441fd/lxml-6.0.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6da5185951d72e6f5352166e3da7b0dc27aa70bd1090b0eb3f7f7212b53f1bb8", size = 4788795, upload-time = "2025-09-22T04:01:39.165Z" }, - { url = "https://files.pythonhosted.org/packages/cf/e8/fd63e15da5e3fd4c2146f8bbb3c14e94ab850589beab88e547b2dbce22e1/lxml-6.0.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:57a86e1ebb4020a38d295c04fc79603c7899e0df71588043eb218722dabc087f", size = 5676759, upload-time = "2025-09-22T04:01:41.506Z" }, - { url = "https://files.pythonhosted.org/packages/76/47/b3ec58dc5c374697f5ba37412cd2728f427d056315d124dd4b61da381877/lxml-6.0.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2047d8234fe735ab77802ce5f2297e410ff40f5238aec569ad7c8e163d7b19a6", size = 5255666, upload-time = "2025-09-22T04:01:43.363Z" }, - { url = "https://files.pythonhosted.org/packages/19/93/03ba725df4c3d72afd9596eef4a37a837ce8e4806010569bedfcd2cb68fd/lxml-6.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f91fd2b2ea15a6800c8e24418c0775a1694eefc011392da73bc6cef2623b322", size = 5277989, upload-time = "2025-09-22T04:01:45.215Z" }, - { url = "https://files.pythonhosted.org/packages/c6/80/c06de80bfce881d0ad738576f243911fccf992687ae09fd80b734712b39c/lxml-6.0.2-cp312-cp312-win32.whl", hash = "sha256:3ae2ce7d6fedfb3414a2b6c5e20b249c4c607f72cb8d2bb7cc9c6ec7c6f4e849", size = 3611456, upload-time = "2025-09-22T04:01:48.243Z" }, - { url = "https://files.pythonhosted.org/packages/f7/d7/0cdfb6c3e30893463fb3d1e52bc5f5f99684a03c29a0b6b605cfae879cd5/lxml-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:72c87e5ee4e58a8354fb9c7c84cbf95a1c8236c127a5d1b7683f04bed8361e1f", size = 4011793, upload-time = "2025-09-22T04:01:50.042Z" }, - { url = "https://files.pythonhosted.org/packages/ea/7b/93c73c67db235931527301ed3785f849c78991e2e34f3fd9a6663ffda4c5/lxml-6.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:61cb10eeb95570153e0c0e554f58df92ecf5109f75eacad4a95baa709e26c3d6", size = 3672836, upload-time = "2025-09-22T04:01:52.145Z" }, - { url = "https://files.pythonhosted.org/packages/53/fd/4e8f0540608977aea078bf6d79f128e0e2c2bba8af1acf775c30baa70460/lxml-6.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9b33d21594afab46f37ae58dfadd06636f154923c4e8a4d754b0127554eb2e77", size = 8648494, upload-time = "2025-09-22T04:01:54.242Z" }, - { url = "https://files.pythonhosted.org/packages/5d/f4/2a94a3d3dfd6c6b433501b8d470a1960a20ecce93245cf2db1706adf6c19/lxml-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c8963287d7a4c5c9a432ff487c52e9c5618667179c18a204bdedb27310f022f", size = 4661146, upload-time = "2025-09-22T04:01:56.282Z" }, - { url = "https://files.pythonhosted.org/packages/25/2e/4efa677fa6b322013035d38016f6ae859d06cac67437ca7dc708a6af7028/lxml-6.0.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1941354d92699fb5ffe6ed7b32f9649e43c2feb4b97205f75866f7d21aa91452", size = 4946932, upload-time = "2025-09-22T04:01:58.989Z" }, - { url = "https://files.pythonhosted.org/packages/ce/0f/526e78a6d38d109fdbaa5049c62e1d32fdd70c75fb61c4eadf3045d3d124/lxml-6.0.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb2f6ca0ae2d983ded09357b84af659c954722bbf04dea98030064996d156048", size = 5100060, upload-time = "2025-09-22T04:02:00.812Z" }, - { url = "https://files.pythonhosted.org/packages/81/76/99de58d81fa702cc0ea7edae4f4640416c2062813a00ff24bd70ac1d9c9b/lxml-6.0.2-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb2a12d704f180a902d7fa778c6d71f36ceb7b0d317f34cdc76a5d05aa1dd1df", size = 5019000, upload-time = "2025-09-22T04:02:02.671Z" }, - { url = "https://files.pythonhosted.org/packages/b5/35/9e57d25482bc9a9882cb0037fdb9cc18f4b79d85df94fa9d2a89562f1d25/lxml-6.0.2-cp313-cp313-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:6ec0e3f745021bfed19c456647f0298d60a24c9ff86d9d051f52b509663feeb1", size = 5348496, upload-time = "2025-09-22T04:02:04.904Z" }, - { url = "https://files.pythonhosted.org/packages/a6/8e/cb99bd0b83ccc3e8f0f528e9aa1f7a9965dfec08c617070c5db8d63a87ce/lxml-6.0.2-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:846ae9a12d54e368933b9759052d6206a9e8b250291109c48e350c1f1f49d916", size = 5643779, upload-time = "2025-09-22T04:02:06.689Z" }, - { url = "https://files.pythonhosted.org/packages/d0/34/9e591954939276bb679b73773836c6684c22e56d05980e31d52a9a8deb18/lxml-6.0.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef9266d2aa545d7374938fb5c484531ef5a2ec7f2d573e62f8ce722c735685fd", size = 5244072, upload-time = "2025-09-22T04:02:08.587Z" }, - { url = "https://files.pythonhosted.org/packages/8d/27/b29ff065f9aaca443ee377aff699714fcbffb371b4fce5ac4ca759e436d5/lxml-6.0.2-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:4077b7c79f31755df33b795dc12119cb557a0106bfdab0d2c2d97bd3cf3dffa6", size = 4718675, upload-time = "2025-09-22T04:02:10.783Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/f756f9c2cd27caa1a6ef8c32ae47aadea697f5c2c6d07b0dae133c244fbe/lxml-6.0.2-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a7c5d5e5f1081955358533be077166ee97ed2571d6a66bdba6ec2f609a715d1a", size = 5255171, upload-time = "2025-09-22T04:02:12.631Z" }, - { url = "https://files.pythonhosted.org/packages/61/46/bb85ea42d2cb1bd8395484fd72f38e3389611aa496ac7772da9205bbda0e/lxml-6.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8f8d0cbd0674ee89863a523e6994ac25fd5be9c8486acfc3e5ccea679bad2679", size = 5057175, upload-time = "2025-09-22T04:02:14.718Z" }, - { url = "https://files.pythonhosted.org/packages/95/0c/443fc476dcc8e41577f0af70458c50fe299a97bb6b7505bb1ae09aa7f9ac/lxml-6.0.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2cbcbf6d6e924c28f04a43f3b6f6e272312a090f269eff68a2982e13e5d57659", size = 4785688, upload-time = "2025-09-22T04:02:16.957Z" }, - { url = "https://files.pythonhosted.org/packages/48/78/6ef0b359d45bb9697bc5a626e1992fa5d27aa3f8004b137b2314793b50a0/lxml-6.0.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dfb874cfa53340009af6bdd7e54ebc0d21012a60a4e65d927c2e477112e63484", size = 5660655, upload-time = "2025-09-22T04:02:18.815Z" }, - { url = "https://files.pythonhosted.org/packages/ff/ea/e1d33808f386bc1339d08c0dcada6e4712d4ed8e93fcad5f057070b7988a/lxml-6.0.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fb8dae0b6b8b7f9e96c26fdd8121522ce5de9bb5538010870bd538683d30e9a2", size = 5247695, upload-time = "2025-09-22T04:02:20.593Z" }, - { url = "https://files.pythonhosted.org/packages/4f/47/eba75dfd8183673725255247a603b4ad606f4ae657b60c6c145b381697da/lxml-6.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:358d9adae670b63e95bc59747c72f4dc97c9ec58881d4627fe0120da0f90d314", size = 5269841, upload-time = "2025-09-22T04:02:22.489Z" }, - { url = "https://files.pythonhosted.org/packages/76/04/5c5e2b8577bc936e219becb2e98cdb1aca14a4921a12995b9d0c523502ae/lxml-6.0.2-cp313-cp313-win32.whl", hash = "sha256:e8cd2415f372e7e5a789d743d133ae474290a90b9023197fd78f32e2dc6873e2", size = 3610700, upload-time = "2025-09-22T04:02:24.465Z" }, - { url = "https://files.pythonhosted.org/packages/fe/0a/4643ccc6bb8b143e9f9640aa54e38255f9d3b45feb2cbe7ae2ca47e8782e/lxml-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:b30d46379644fbfc3ab81f8f82ae4de55179414651f110a1514f0b1f8f6cb2d7", size = 4010347, upload-time = "2025-09-22T04:02:26.286Z" }, - { url = "https://files.pythonhosted.org/packages/31/ef/dcf1d29c3f530577f61e5fe2f1bd72929acf779953668a8a47a479ae6f26/lxml-6.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:13dcecc9946dca97b11b7c40d29fba63b55ab4170d3c0cf8c0c164343b9bfdcf", size = 3671248, upload-time = "2025-09-22T04:02:27.918Z" }, - { url = "https://files.pythonhosted.org/packages/03/15/d4a377b385ab693ce97b472fe0c77c2b16ec79590e688b3ccc71fba19884/lxml-6.0.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:b0c732aa23de8f8aec23f4b580d1e52905ef468afb4abeafd3fec77042abb6fe", size = 8659801, upload-time = "2025-09-22T04:02:30.113Z" }, - { url = "https://files.pythonhosted.org/packages/c8/e8/c128e37589463668794d503afaeb003987373c5f94d667124ffd8078bbd9/lxml-6.0.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4468e3b83e10e0317a89a33d28f7aeba1caa4d1a6fd457d115dd4ffe90c5931d", size = 4659403, upload-time = "2025-09-22T04:02:32.119Z" }, - { url = "https://files.pythonhosted.org/packages/00/ce/74903904339decdf7da7847bb5741fc98a5451b42fc419a86c0c13d26fe2/lxml-6.0.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:abd44571493973bad4598a3be7e1d807ed45aa2adaf7ab92ab7c62609569b17d", size = 4966974, upload-time = "2025-09-22T04:02:34.155Z" }, - { url = "https://files.pythonhosted.org/packages/1f/d3/131dec79ce61c5567fecf82515bd9bc36395df42501b50f7f7f3bd065df0/lxml-6.0.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:370cd78d5855cfbffd57c422851f7d3864e6ae72d0da615fca4dad8c45d375a5", size = 5102953, upload-time = "2025-09-22T04:02:36.054Z" }, - { url = "https://files.pythonhosted.org/packages/3a/ea/a43ba9bb750d4ffdd885f2cd333572f5bb900cd2408b67fdda07e85978a0/lxml-6.0.2-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:901e3b4219fa04ef766885fb40fa516a71662a4c61b80c94d25336b4934b71c0", size = 5055054, upload-time = "2025-09-22T04:02:38.154Z" }, - { url = "https://files.pythonhosted.org/packages/60/23/6885b451636ae286c34628f70a7ed1fcc759f8d9ad382d132e1c8d3d9bfd/lxml-6.0.2-cp314-cp314-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:a4bf42d2e4cf52c28cc1812d62426b9503cdb0c87a6de81442626aa7d69707ba", size = 5352421, upload-time = "2025-09-22T04:02:40.413Z" }, - { url = "https://files.pythonhosted.org/packages/48/5b/fc2ddfc94ddbe3eebb8e9af6e3fd65e2feba4967f6a4e9683875c394c2d8/lxml-6.0.2-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2c7fdaa4d7c3d886a42534adec7cfac73860b89b4e5298752f60aa5984641a0", size = 5673684, upload-time = "2025-09-22T04:02:42.288Z" }, - { url = "https://files.pythonhosted.org/packages/29/9c/47293c58cc91769130fbf85531280e8cc7868f7fbb6d92f4670071b9cb3e/lxml-6.0.2-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98a5e1660dc7de2200b00d53fa00bcd3c35a3608c305d45a7bbcaf29fa16e83d", size = 5252463, upload-time = "2025-09-22T04:02:44.165Z" }, - { url = "https://files.pythonhosted.org/packages/9b/da/ba6eceb830c762b48e711ded880d7e3e89fc6c7323e587c36540b6b23c6b/lxml-6.0.2-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:dc051506c30b609238d79eda75ee9cab3e520570ec8219844a72a46020901e37", size = 4698437, upload-time = "2025-09-22T04:02:46.524Z" }, - { url = "https://files.pythonhosted.org/packages/a5/24/7be3f82cb7990b89118d944b619e53c656c97dc89c28cfb143fdb7cd6f4d/lxml-6.0.2-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8799481bbdd212470d17513a54d568f44416db01250f49449647b5ab5b5dccb9", size = 5269890, upload-time = "2025-09-22T04:02:48.812Z" }, - { url = "https://files.pythonhosted.org/packages/1b/bd/dcfb9ea1e16c665efd7538fc5d5c34071276ce9220e234217682e7d2c4a5/lxml-6.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9261bb77c2dab42f3ecd9103951aeca2c40277701eb7e912c545c1b16e0e4917", size = 5097185, upload-time = "2025-09-22T04:02:50.746Z" }, - { url = "https://files.pythonhosted.org/packages/21/04/a60b0ff9314736316f28316b694bccbbabe100f8483ad83852d77fc7468e/lxml-6.0.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:65ac4a01aba353cfa6d5725b95d7aed6356ddc0a3cd734de00124d285b04b64f", size = 4745895, upload-time = "2025-09-22T04:02:52.968Z" }, - { url = "https://files.pythonhosted.org/packages/d6/bd/7d54bd1846e5a310d9c715921c5faa71cf5c0853372adf78aee70c8d7aa2/lxml-6.0.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b22a07cbb82fea98f8a2fd814f3d1811ff9ed76d0fc6abc84eb21527596e7cc8", size = 5695246, upload-time = "2025-09-22T04:02:54.798Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/5643d6ab947bc371da21323acb2a6e603cedbe71cb4c99c8254289ab6f4e/lxml-6.0.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d759cdd7f3e055d6bc8d9bec3ad905227b2e4c785dc16c372eb5b5e83123f48a", size = 5260797, upload-time = "2025-09-22T04:02:57.058Z" }, - { url = "https://files.pythonhosted.org/packages/33/da/34c1ec4cff1eea7d0b4cd44af8411806ed943141804ac9c5d565302afb78/lxml-6.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:945da35a48d193d27c188037a05fec5492937f66fb1958c24fc761fb9d40d43c", size = 5277404, upload-time = "2025-09-22T04:02:58.966Z" }, - { url = "https://files.pythonhosted.org/packages/82/57/4eca3e31e54dc89e2c3507e1cd411074a17565fa5ffc437c4ae0a00d439e/lxml-6.0.2-cp314-cp314-win32.whl", hash = "sha256:be3aaa60da67e6153eb15715cc2e19091af5dc75faef8b8a585aea372507384b", size = 3670072, upload-time = "2025-09-22T04:03:38.05Z" }, - { url = "https://files.pythonhosted.org/packages/e3/e0/c96cf13eccd20c9421ba910304dae0f619724dcf1702864fd59dd386404d/lxml-6.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:fa25afbadead523f7001caf0c2382afd272c315a033a7b06336da2637d92d6ed", size = 4080617, upload-time = "2025-09-22T04:03:39.835Z" }, - { url = "https://files.pythonhosted.org/packages/d5/5d/b3f03e22b3d38d6f188ef044900a9b29b2fe0aebb94625ce9fe244011d34/lxml-6.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:063eccf89df5b24e361b123e257e437f9e9878f425ee9aae3144c77faf6da6d8", size = 3754930, upload-time = "2025-09-22T04:03:41.565Z" }, - { url = "https://files.pythonhosted.org/packages/5e/5c/42c2c4c03554580708fc738d13414801f340c04c3eff90d8d2d227145275/lxml-6.0.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:6162a86d86893d63084faaf4ff937b3daea233e3682fb4474db07395794fa80d", size = 8910380, upload-time = "2025-09-22T04:03:01.645Z" }, - { url = "https://files.pythonhosted.org/packages/bf/4f/12df843e3e10d18d468a7557058f8d3733e8b6e12401f30b1ef29360740f/lxml-6.0.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:414aaa94e974e23a3e92e7ca5b97d10c0cf37b6481f50911032c69eeb3991bba", size = 4775632, upload-time = "2025-09-22T04:03:03.814Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0c/9dc31e6c2d0d418483cbcb469d1f5a582a1cd00a1f4081953d44051f3c50/lxml-6.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48461bd21625458dd01e14e2c38dd0aea69addc3c4f960c30d9f59d7f93be601", size = 4975171, upload-time = "2025-09-22T04:03:05.651Z" }, - { url = "https://files.pythonhosted.org/packages/e7/2b/9b870c6ca24c841bdd887504808f0417aa9d8d564114689266f19ddf29c8/lxml-6.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:25fcc59afc57d527cfc78a58f40ab4c9b8fd096a9a3f964d2781ffb6eb33f4ed", size = 5110109, upload-time = "2025-09-22T04:03:07.452Z" }, - { url = "https://files.pythonhosted.org/packages/bf/0c/4f5f2a4dd319a178912751564471355d9019e220c20d7db3fb8307ed8582/lxml-6.0.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5179c60288204e6ddde3f774a93350177e08876eaf3ab78aa3a3649d43eb7d37", size = 5041061, upload-time = "2025-09-22T04:03:09.297Z" }, - { url = "https://files.pythonhosted.org/packages/12/64/554eed290365267671fe001a20d72d14f468ae4e6acef1e179b039436967/lxml-6.0.2-cp314-cp314t-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:967aab75434de148ec80597b75062d8123cadf2943fb4281f385141e18b21338", size = 5306233, upload-time = "2025-09-22T04:03:11.651Z" }, - { url = "https://files.pythonhosted.org/packages/7a/31/1d748aa275e71802ad9722df32a7a35034246b42c0ecdd8235412c3396ef/lxml-6.0.2-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d100fcc8930d697c6561156c6810ab4a508fb264c8b6779e6e61e2ed5e7558f9", size = 5604739, upload-time = "2025-09-22T04:03:13.592Z" }, - { url = "https://files.pythonhosted.org/packages/8f/41/2c11916bcac09ed561adccacceaedd2bf0e0b25b297ea92aab99fd03d0fa/lxml-6.0.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ca59e7e13e5981175b8b3e4ab84d7da57993eeff53c07764dcebda0d0e64ecd", size = 5225119, upload-time = "2025-09-22T04:03:15.408Z" }, - { url = "https://files.pythonhosted.org/packages/99/05/4e5c2873d8f17aa018e6afde417c80cc5d0c33be4854cce3ef5670c49367/lxml-6.0.2-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:957448ac63a42e2e49531b9d6c0fa449a1970dbc32467aaad46f11545be9af1d", size = 4633665, upload-time = "2025-09-22T04:03:17.262Z" }, - { url = "https://files.pythonhosted.org/packages/0f/c9/dcc2da1bebd6275cdc723b515f93edf548b82f36a5458cca3578bc899332/lxml-6.0.2-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b7fc49c37f1786284b12af63152fe1d0990722497e2d5817acfe7a877522f9a9", size = 5234997, upload-time = "2025-09-22T04:03:19.14Z" }, - { url = "https://files.pythonhosted.org/packages/9c/e2/5172e4e7468afca64a37b81dba152fc5d90e30f9c83c7c3213d6a02a5ce4/lxml-6.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e19e0643cc936a22e837f79d01a550678da8377d7d801a14487c10c34ee49c7e", size = 5090957, upload-time = "2025-09-22T04:03:21.436Z" }, - { url = "https://files.pythonhosted.org/packages/a5/b3/15461fd3e5cd4ddcb7938b87fc20b14ab113b92312fc97afe65cd7c85de1/lxml-6.0.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:1db01e5cf14345628e0cbe71067204db658e2fb8e51e7f33631f5f4735fefd8d", size = 4764372, upload-time = "2025-09-22T04:03:23.27Z" }, - { url = "https://files.pythonhosted.org/packages/05/33/f310b987c8bf9e61c4dd8e8035c416bd3230098f5e3cfa69fc4232de7059/lxml-6.0.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:875c6b5ab39ad5291588aed6925fac99d0097af0dd62f33c7b43736043d4a2ec", size = 5634653, upload-time = "2025-09-22T04:03:25.767Z" }, - { url = "https://files.pythonhosted.org/packages/70/ff/51c80e75e0bc9382158133bdcf4e339b5886c6ee2418b5199b3f1a61ed6d/lxml-6.0.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:cdcbed9ad19da81c480dfd6dd161886db6096083c9938ead313d94b30aadf272", size = 5233795, upload-time = "2025-09-22T04:03:27.62Z" }, - { url = "https://files.pythonhosted.org/packages/56/4d/4856e897df0d588789dd844dbed9d91782c4ef0b327f96ce53c807e13128/lxml-6.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:80dadc234ebc532e09be1975ff538d154a7fa61ea5031c03d25178855544728f", size = 5257023, upload-time = "2025-09-22T04:03:30.056Z" }, - { url = "https://files.pythonhosted.org/packages/0f/85/86766dfebfa87bea0ab78e9ff7a4b4b45225df4b4d3b8cc3c03c5cd68464/lxml-6.0.2-cp314-cp314t-win32.whl", hash = "sha256:da08e7bb297b04e893d91087df19638dc7a6bb858a954b0cc2b9f5053c922312", size = 3911420, upload-time = "2025-09-22T04:03:32.198Z" }, - { url = "https://files.pythonhosted.org/packages/fe/1a/b248b355834c8e32614650b8008c69ffeb0ceb149c793961dd8c0b991bb3/lxml-6.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:252a22982dca42f6155125ac76d3432e548a7625d56f5a273ee78a5057216eca", size = 4406837, upload-time = "2025-09-22T04:03:34.027Z" }, - { url = "https://files.pythonhosted.org/packages/92/aa/df863bcc39c5e0946263454aba394de8a9084dbaff8ad143846b0d844739/lxml-6.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:bb4c1847b303835d89d785a18801a883436cdfd5dc3d62947f9c49e24f0f5a2c", size = 3822205, upload-time = "2025-09-22T04:03:36.249Z" }, -] - -[[package]] -name = "mammoth" -version = "1.11.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cobble" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ed/3c/a58418d2af00f2da60d4a51e18cd0311307b72d48d2fffec36a97b4a5e44/mammoth-1.11.0.tar.gz", hash = "sha256:a0f59e442f34d5b6447f4b0999306cbf3e67aaabfa8cb516f878fb1456744637", size = 53142, upload-time = "2025-09-19T10:35:20.373Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/54/2e39566a131b13f6d8d193f974cb6a34e81bb7cc2fa6f7e03de067b36588/mammoth-1.11.0-py2.py3-none-any.whl", hash = "sha256:c077ab0d450bd7c0c6ecd529a23bf7e0fa8190c929e28998308ff4eada3f063b", size = 54752, upload-time = "2025-09-19T10:35:18.699Z" }, -] - -[[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 = "markdownify" -version = "1.2.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beautifulsoup4" }, - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3f/bc/c8c8eea5335341306b0fa7e1cb33c5e1c8d24ef70ddd684da65f41c49c92/markdownify-1.2.2.tar.gz", hash = "sha256:b274f1b5943180b031b699b199cbaeb1e2ac938b75851849a31fd0c3d6603d09", size = 18816, upload-time = "2025-11-16T19:21:18.565Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ce/f1e3e9d959db134cedf06825fae8d5b294bd368aacdd0831a3975b7c4d55/markdownify-1.2.2-py3-none-any.whl", hash = "sha256:3f02d3cc52714084d6e589f70397b6fc9f2f3a8531481bf35e8cc39f975e186a", size = 15724, upload-time = "2025-11-16T19:21:17.622Z" }, -] - -[[package]] -name = "markupsafe" -version = "3.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, - { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, - { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, - { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, - { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, - { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, - { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, - { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, - { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, - { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, - { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, - { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, - { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, - { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, - { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, - { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, - { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, - { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, - { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, - { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, - { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, - { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, - { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, - { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, - { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, - { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, - { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, - { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, - { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, - { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, - { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, - { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, - { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, - { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, - { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, - { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, - { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, - { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, - { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, - { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, - { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, - { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, - { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, - { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, - { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, -] - -[[package]] -name = "matplotlib" -version = "3.10.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "contourpy" }, - { name = "cycler" }, - { name = "fonttools" }, - { name = "kiwisolver" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pillow" }, - { name = "pyparsing" }, - { name = "python-dateutil" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/67/f997cdcbb514012eb0d10cd2b4b332667997fb5ebe26b8d41d04962fa0e6/matplotlib-3.10.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:64fcc24778ca0404ce0cb7b6b77ae1f4c7231cdd60e6778f999ee05cbd581b9a", size = 8260453, upload-time = "2025-12-10T22:55:30.709Z" }, - { url = "https://files.pythonhosted.org/packages/7e/65/07d5f5c7f7c994f12c768708bd2e17a4f01a2b0f44a1c9eccad872433e2e/matplotlib-3.10.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9a5ca4ac220a0cdd1ba6bcba3608547117d30468fefce49bb26f55c1a3d5c58", size = 8148321, upload-time = "2025-12-10T22:55:33.265Z" }, - { url = "https://files.pythonhosted.org/packages/3e/f3/c5195b1ae57ef85339fd7285dfb603b22c8b4e79114bae5f4f0fcf688677/matplotlib-3.10.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ab4aabc72de4ff77b3ec33a6d78a68227bf1123465887f9905ba79184a1cc04", size = 8716944, upload-time = "2025-12-10T22:55:34.922Z" }, - { url = "https://files.pythonhosted.org/packages/00/f9/7638f5cc82ec8a7aa005de48622eecc3ed7c9854b96ba15bd76b7fd27574/matplotlib-3.10.8-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24d50994d8c5816ddc35411e50a86ab05f575e2530c02752e02538122613371f", size = 9550099, upload-time = "2025-12-10T22:55:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/57/61/78cd5920d35b29fd2a0fe894de8adf672ff52939d2e9b43cb83cd5ce1bc7/matplotlib-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99eefd13c0dc3b3c1b4d561c1169e65fe47aab7b8158754d7c084088e2329466", size = 9613040, upload-time = "2025-12-10T22:55:38.715Z" }, - { url = "https://files.pythonhosted.org/packages/30/4e/c10f171b6e2f44d9e3a2b96efa38b1677439d79c99357600a62cc1e9594e/matplotlib-3.10.8-cp312-cp312-win_amd64.whl", hash = "sha256:dd80ecb295460a5d9d260df63c43f4afbdd832d725a531f008dad1664f458adf", size = 8142717, upload-time = "2025-12-10T22:55:41.103Z" }, - { url = "https://files.pythonhosted.org/packages/f1/76/934db220026b5fef85f45d51a738b91dea7d70207581063cd9bd8fafcf74/matplotlib-3.10.8-cp312-cp312-win_arm64.whl", hash = "sha256:3c624e43ed56313651bc18a47f838b60d7b8032ed348911c54906b130b20071b", size = 8012751, upload-time = "2025-12-10T22:55:42.684Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b9/15fd5541ef4f5b9a17eefd379356cf12175fe577424e7b1d80676516031a/matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3f2e409836d7f5ac2f1c013110a4d50b9f7edc26328c108915f9075d7d7a91b6", size = 8261076, upload-time = "2025-12-10T22:55:44.648Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a0/2ba3473c1b66b9c74dc7107c67e9008cb1782edbe896d4c899d39ae9cf78/matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56271f3dac49a88d7fca5060f004d9d22b865f743a12a23b1e937a0be4818ee1", size = 8148794, upload-time = "2025-12-10T22:55:46.252Z" }, - { url = "https://files.pythonhosted.org/packages/75/97/a471f1c3eb1fd6f6c24a31a5858f443891d5127e63a7788678d14e249aea/matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0a7f52498f72f13d4a25ea70f35f4cb60642b466cbb0a9be951b5bc3f45a486", size = 8718474, upload-time = "2025-12-10T22:55:47.864Z" }, - { url = "https://files.pythonhosted.org/packages/01/be/cd478f4b66f48256f42927d0acbcd63a26a893136456cd079c0cc24fbabf/matplotlib-3.10.8-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:646d95230efb9ca614a7a594d4fcacde0ac61d25e37dd51710b36477594963ce", size = 9549637, upload-time = "2025-12-10T22:55:50.048Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7c/8dc289776eae5109e268c4fb92baf870678dc048a25d4ac903683b86d5bf/matplotlib-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f89c151aab2e2e23cb3fe0acad1e8b82841fd265379c4cecd0f3fcb34c15e0f6", size = 9613678, upload-time = "2025-12-10T22:55:52.21Z" }, - { url = "https://files.pythonhosted.org/packages/64/40/37612487cc8a437d4dd261b32ca21fe2d79510fe74af74e1f42becb1bdb8/matplotlib-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:e8ea3e2d4066083e264e75c829078f9e149fa119d27e19acd503de65e0b13149", size = 8142686, upload-time = "2025-12-10T22:55:54.253Z" }, - { url = "https://files.pythonhosted.org/packages/66/52/8d8a8730e968185514680c2a6625943f70269509c3dcfc0dcf7d75928cb8/matplotlib-3.10.8-cp313-cp313-win_arm64.whl", hash = "sha256:c108a1d6fa78a50646029cb6d49808ff0fc1330fda87fa6f6250c6b5369b6645", size = 8012917, upload-time = "2025-12-10T22:55:56.268Z" }, - { url = "https://files.pythonhosted.org/packages/b5/27/51fe26e1062f298af5ef66343d8ef460e090a27fea73036c76c35821df04/matplotlib-3.10.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ad3d9833a64cf48cc4300f2b406c3d0f4f4724a91c0bd5640678a6ba7c102077", size = 8305679, upload-time = "2025-12-10T22:55:57.856Z" }, - { url = "https://files.pythonhosted.org/packages/2c/1e/4de865bc591ac8e3062e835f42dd7fe7a93168d519557837f0e37513f629/matplotlib-3.10.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:eb3823f11823deade26ce3b9f40dcb4a213da7a670013929f31d5f5ed1055b22", size = 8198336, upload-time = "2025-12-10T22:55:59.371Z" }, - { url = "https://files.pythonhosted.org/packages/c6/cb/2f7b6e75fb4dce87ef91f60cac4f6e34f4c145ab036a22318ec837971300/matplotlib-3.10.8-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d9050fee89a89ed57b4fb2c1bfac9a3d0c57a0d55aed95949eedbc42070fea39", size = 8731653, upload-time = "2025-12-10T22:56:01.032Z" }, - { url = "https://files.pythonhosted.org/packages/46/b3/bd9c57d6ba670a37ab31fb87ec3e8691b947134b201f881665b28cc039ff/matplotlib-3.10.8-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b44d07310e404ba95f8c25aa5536f154c0a8ec473303535949e52eb71d0a1565", size = 9561356, upload-time = "2025-12-10T22:56:02.95Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3d/8b94a481456dfc9dfe6e39e93b5ab376e50998cddfd23f4ae3b431708f16/matplotlib-3.10.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0a33deb84c15ede243aead39f77e990469fff93ad1521163305095b77b72ce4a", size = 9614000, upload-time = "2025-12-10T22:56:05.411Z" }, - { url = "https://files.pythonhosted.org/packages/bd/cd/bc06149fe5585ba800b189a6a654a75f1f127e8aab02fd2be10df7fa500c/matplotlib-3.10.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3a48a78d2786784cc2413e57397981fb45c79e968d99656706018d6e62e57958", size = 8220043, upload-time = "2025-12-10T22:56:07.551Z" }, - { url = "https://files.pythonhosted.org/packages/e3/de/b22cf255abec916562cc04eef457c13e58a1990048de0c0c3604d082355e/matplotlib-3.10.8-cp313-cp313t-win_arm64.whl", hash = "sha256:15d30132718972c2c074cd14638c7f4592bd98719e2308bccea40e0538bc0cb5", size = 8062075, upload-time = "2025-12-10T22:56:09.178Z" }, - { url = "https://files.pythonhosted.org/packages/3c/43/9c0ff7a2f11615e516c3b058e1e6e8f9614ddeca53faca06da267c48345d/matplotlib-3.10.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b53285e65d4fa4c86399979e956235deb900be5baa7fc1218ea67fbfaeaadd6f", size = 8262481, upload-time = "2025-12-10T22:56:10.885Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ca/e8ae28649fcdf039fda5ef554b40a95f50592a3c47e6f7270c9561c12b07/matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32f8dce744be5569bebe789e46727946041199030db8aeb2954d26013a0eb26b", size = 8151473, upload-time = "2025-12-10T22:56:12.377Z" }, - { url = "https://files.pythonhosted.org/packages/f1/6f/009d129ae70b75e88cbe7e503a12a4c0670e08ed748a902c2568909e9eb5/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf267add95b1c88300d96ca837833d4112756045364f5c734a2276038dae27d", size = 9553896, upload-time = "2025-12-10T22:56:14.432Z" }, - { url = "https://files.pythonhosted.org/packages/f5/26/4221a741eb97967bc1fd5e4c52b9aa5a91b2f4ec05b59f6def4d820f9df9/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2cf5bd12cecf46908f286d7838b2abc6c91cda506c0445b8223a7c19a00df008", size = 9824193, upload-time = "2025-12-10T22:56:16.29Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/3abf75f38605772cf48a9daf5821cd4f563472f38b4b828c6fba6fa6d06e/matplotlib-3.10.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:41703cc95688f2516b480f7f339d8851a6035f18e100ee6a32bc0b8536a12a9c", size = 9615444, upload-time = "2025-12-10T22:56:18.155Z" }, - { url = "https://files.pythonhosted.org/packages/93/a5/de89ac80f10b8dc615807ee1133cd99ac74082581196d4d9590bea10690d/matplotlib-3.10.8-cp314-cp314-win_amd64.whl", hash = "sha256:83d282364ea9f3e52363da262ce32a09dfe241e4080dcedda3c0db059d3c1f11", size = 8272719, upload-time = "2025-12-10T22:56:20.366Z" }, - { url = "https://files.pythonhosted.org/packages/69/ce/b006495c19ccc0a137b48083168a37bd056392dee02f87dba0472f2797fe/matplotlib-3.10.8-cp314-cp314-win_arm64.whl", hash = "sha256:2c1998e92cd5999e295a731bcb2911c75f597d937341f3030cc24ef2733d78a8", size = 8144205, upload-time = "2025-12-10T22:56:22.239Z" }, - { url = "https://files.pythonhosted.org/packages/68/d9/b31116a3a855bd313c6fcdb7226926d59b041f26061c6c5b1be66a08c826/matplotlib-3.10.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b5a2b97dbdc7d4f353ebf343744f1d1f1cca8aa8bfddb4262fcf4306c3761d50", size = 8305785, upload-time = "2025-12-10T22:56:24.218Z" }, - { url = "https://files.pythonhosted.org/packages/1e/90/6effe8103f0272685767ba5f094f453784057072f49b393e3ea178fe70a5/matplotlib-3.10.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f5c3e4da343bba819f0234186b9004faba952cc420fbc522dc4e103c1985908", size = 8198361, upload-time = "2025-12-10T22:56:26.787Z" }, - { url = "https://files.pythonhosted.org/packages/d7/65/a73188711bea603615fc0baecca1061429ac16940e2385433cc778a9d8e7/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f62550b9a30afde8c1c3ae450e5eb547d579dd69b25c2fc7a1c67f934c1717a", size = 9561357, upload-time = "2025-12-10T22:56:28.953Z" }, - { url = "https://files.pythonhosted.org/packages/f4/3d/b5c5d5d5be8ce63292567f0e2c43dde9953d3ed86ac2de0a72e93c8f07a1/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:495672de149445ec1b772ff2c9ede9b769e3cb4f0d0aa7fa730d7f59e2d4e1c1", size = 9823610, upload-time = "2025-12-10T22:56:31.455Z" }, - { url = "https://files.pythonhosted.org/packages/4d/4b/e7beb6bbd49f6bae727a12b270a2654d13c397576d25bd6786e47033300f/matplotlib-3.10.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:595ba4d8fe983b88f0eec8c26a241e16d6376fe1979086232f481f8f3f67494c", size = 9614011, upload-time = "2025-12-10T22:56:33.85Z" }, - { url = "https://files.pythonhosted.org/packages/7c/e6/76f2813d31f032e65f6f797e3f2f6e4aab95b65015924b1c51370395c28a/matplotlib-3.10.8-cp314-cp314t-win_amd64.whl", hash = "sha256:25d380fe8b1dc32cf8f0b1b448470a77afb195438bafdf1d858bfb876f3edf7b", size = 8362801, upload-time = "2025-12-10T22:56:36.107Z" }, - { url = "https://files.pythonhosted.org/packages/5d/49/d651878698a0b67f23aa28e17f45a6d6dd3d3f933fa29087fa4ce5947b5a/matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f", size = 8192560, upload-time = "2025-12-10T22:56:38.008Z" }, -] - -[[package]] -name = "mccabe" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, -] - -[[package]] -name = "mcp" -version = "1.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyjwt", extra = ["crypto"] }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, -] - -[[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 = "meilisearch-python-sdk" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiofiles" }, - { name = "camel-converter", extra = ["pydantic"] }, - { name = "httpx", extra = ["http2"] }, - { name = "pydantic" }, - { name = "pyjwt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/da/b3/45005b96481f002a7dab317a2271a444d58fc3a46953c98f6e080edf22ed/meilisearch_python_sdk-6.1.0.tar.gz", hash = "sha256:a73cfde02f4475e32b2da0d49c992522cce1cd99c0213efa57136d165d7cf9e5", size = 252950, upload-time = "2026-01-19T16:26:58.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/7e/feb2feb2d57d8c692bc6044404918c6ab55f0d38a6417dc202ca7eb0d7d3/meilisearch_python_sdk-6.1.0-py3-none-any.whl", hash = "sha256:e8bc76c29d0a8b8817c75f2e85caffe542ef61665b045a8ab27f37930adf5910", size = 74619, upload-time = "2026-01-19T16:27:01.873Z" }, -] - -[[package]] -name = "mmh3" -version = "5.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/af/f28c2c2f51f31abb4725f9a64bc7863d5f491f6539bd26aee2a1d21a649e/mmh3-5.2.0.tar.gz", hash = "sha256:1efc8fec8478e9243a78bb993422cf79f8ff85cb4cf6b79647480a31e0d950a8", size = 33582, upload-time = "2025-07-29T07:43:48.49Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/6a/d5aa7edb5c08e0bd24286c7d08341a0446f9a2fbbb97d96a8a6dd81935ee/mmh3-5.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:384eda9361a7bf83a85e09447e1feafe081034af9dd428893701b959230d84be", size = 56141, upload-time = "2025-07-29T07:42:13.456Z" }, - { url = "https://files.pythonhosted.org/packages/08/49/131d0fae6447bc4a7299ebdb1a6fb9d08c9f8dcf97d75ea93e8152ddf7ab/mmh3-5.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2c9da0d568569cc87315cb063486d761e38458b8ad513fedd3dc9263e1b81bcd", size = 40681, upload-time = "2025-07-29T07:42:14.306Z" }, - { url = "https://files.pythonhosted.org/packages/8f/6f/9221445a6bcc962b7f5ff3ba18ad55bba624bacdc7aa3fc0a518db7da8ec/mmh3-5.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86d1be5d63232e6eb93c50881aea55ff06eb86d8e08f9b5417c8c9b10db9db96", size = 40062, upload-time = "2025-07-29T07:42:15.08Z" }, - { url = "https://files.pythonhosted.org/packages/1e/d4/6bb2d0fef81401e0bb4c297d1eb568b767de4ce6fc00890bc14d7b51ecc4/mmh3-5.2.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bf7bee43e17e81671c447e9c83499f53d99bf440bc6d9dc26a841e21acfbe094", size = 97333, upload-time = "2025-07-29T07:42:16.436Z" }, - { url = "https://files.pythonhosted.org/packages/44/e0/ccf0daff8134efbb4fbc10a945ab53302e358c4b016ada9bf97a6bdd50c1/mmh3-5.2.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7aa18cdb58983ee660c9c400b46272e14fa253c675ed963d3812487f8ca42037", size = 103310, upload-time = "2025-07-29T07:42:17.796Z" }, - { url = "https://files.pythonhosted.org/packages/02/63/1965cb08a46533faca0e420e06aff8bbaf9690a6f0ac6ae6e5b2e4544687/mmh3-5.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9d032488fcec32d22be6542d1a836f00247f40f320844dbb361393b5b22773", size = 106178, upload-time = "2025-07-29T07:42:19.281Z" }, - { url = "https://files.pythonhosted.org/packages/c2/41/c883ad8e2c234013f27f92061200afc11554ea55edd1bcf5e1accd803a85/mmh3-5.2.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1861fb6b1d0453ed7293200139c0a9011eeb1376632e048e3766945b13313c5", size = 113035, upload-time = "2025-07-29T07:42:20.356Z" }, - { url = "https://files.pythonhosted.org/packages/df/b5/1ccade8b1fa625d634a18bab7bf08a87457e09d5ec8cf83ca07cbea9d400/mmh3-5.2.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:99bb6a4d809aa4e528ddfe2c85dd5239b78b9dd14be62cca0329db78505e7b50", size = 120784, upload-time = "2025-07-29T07:42:21.377Z" }, - { url = "https://files.pythonhosted.org/packages/77/1c/919d9171fcbdcdab242e06394464ccf546f7d0f3b31e0d1e3a630398782e/mmh3-5.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1f8d8b627799f4e2fcc7c034fed8f5f24dc7724ff52f69838a3d6d15f1ad4765", size = 99137, upload-time = "2025-07-29T07:42:22.344Z" }, - { url = "https://files.pythonhosted.org/packages/66/8a/1eebef5bd6633d36281d9fc83cf2e9ba1ba0e1a77dff92aacab83001cee4/mmh3-5.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b5995088dd7023d2d9f310a0c67de5a2b2e06a570ecfd00f9ff4ab94a67cde43", size = 98664, upload-time = "2025-07-29T07:42:23.269Z" }, - { url = "https://files.pythonhosted.org/packages/13/41/a5d981563e2ee682b21fb65e29cc0f517a6734a02b581359edd67f9d0360/mmh3-5.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1a5f4d2e59d6bba8ef01b013c472741835ad961e7c28f50c82b27c57748744a4", size = 106459, upload-time = "2025-07-29T07:42:24.238Z" }, - { url = "https://files.pythonhosted.org/packages/24/31/342494cd6ab792d81e083680875a2c50fa0c5df475ebf0b67784f13e4647/mmh3-5.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fd6e6c3d90660d085f7e73710eab6f5545d4854b81b0135a3526e797009dbda3", size = 110038, upload-time = "2025-07-29T07:42:25.629Z" }, - { url = "https://files.pythonhosted.org/packages/28/44/efda282170a46bb4f19c3e2b90536513b1d821c414c28469a227ca5a1789/mmh3-5.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c4a2f3d83879e3de2eb8cbf562e71563a8ed15ee9b9c2e77ca5d9f73072ac15c", size = 97545, upload-time = "2025-07-29T07:42:27.04Z" }, - { url = "https://files.pythonhosted.org/packages/68/8f/534ae319c6e05d714f437e7206f78c17e66daca88164dff70286b0e8ea0c/mmh3-5.2.0-cp312-cp312-win32.whl", hash = "sha256:2421b9d665a0b1ad724ec7332fb5a98d075f50bc51a6ff854f3a1882bd650d49", size = 40805, upload-time = "2025-07-29T07:42:28.032Z" }, - { url = "https://files.pythonhosted.org/packages/b8/f6/f6abdcfefcedab3c964868048cfe472764ed358c2bf6819a70dd4ed4ed3a/mmh3-5.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:72d80005b7634a3a2220f81fbeb94775ebd12794623bb2e1451701ea732b4aa3", size = 41597, upload-time = "2025-07-29T07:42:28.894Z" }, - { url = "https://files.pythonhosted.org/packages/15/fd/f7420e8cbce45c259c770cac5718badf907b302d3a99ec587ba5ce030237/mmh3-5.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:3d6bfd9662a20c054bc216f861fa330c2dac7c81e7fb8307b5e32ab5b9b4d2e0", size = 39350, upload-time = "2025-07-29T07:42:29.794Z" }, - { url = "https://files.pythonhosted.org/packages/d8/fa/27f6ab93995ef6ad9f940e96593c5dd24744d61a7389532b0fec03745607/mmh3-5.2.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:e79c00eba78f7258e5b354eccd4d7907d60317ced924ea4a5f2e9d83f5453065", size = 40874, upload-time = "2025-07-29T07:42:30.662Z" }, - { url = "https://files.pythonhosted.org/packages/11/9c/03d13bcb6a03438bc8cac3d2e50f80908d159b31a4367c2e1a7a077ded32/mmh3-5.2.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:956127e663d05edbeec54df38885d943dfa27406594c411139690485128525de", size = 42012, upload-time = "2025-07-29T07:42:31.539Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/0865d9765408a7d504f1789944e678f74e0888b96a766d578cb80b040999/mmh3-5.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:c3dca4cb5b946ee91b3d6bb700d137b1cd85c20827f89fdf9c16258253489044", size = 39197, upload-time = "2025-07-29T07:42:32.374Z" }, - { url = "https://files.pythonhosted.org/packages/3e/12/76c3207bd186f98b908b6706c2317abb73756d23a4e68ea2bc94825b9015/mmh3-5.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:e651e17bfde5840e9e4174b01e9e080ce49277b70d424308b36a7969d0d1af73", size = 39840, upload-time = "2025-07-29T07:42:33.227Z" }, - { url = "https://files.pythonhosted.org/packages/5d/0d/574b6cce5555c9f2b31ea189ad44986755eb14e8862db28c8b834b8b64dc/mmh3-5.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:9f64bf06f4bf623325fda3a6d02d36cd69199b9ace99b04bb2d7fd9f89688504", size = 40644, upload-time = "2025-07-29T07:42:34.099Z" }, - { url = "https://files.pythonhosted.org/packages/52/82/3731f8640b79c46707f53ed72034a58baad400be908c87b0088f1f89f986/mmh3-5.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ddc63328889bcaee77b743309e5c7d2d52cee0d7d577837c91b6e7cc9e755e0b", size = 56153, upload-time = "2025-07-29T07:42:35.031Z" }, - { url = "https://files.pythonhosted.org/packages/4f/34/e02dca1d4727fd9fdeaff9e2ad6983e1552804ce1d92cc796e5b052159bb/mmh3-5.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bb0fdc451fb6d86d81ab8f23d881b8d6e37fc373a2deae1c02d27002d2ad7a05", size = 40684, upload-time = "2025-07-29T07:42:35.914Z" }, - { url = "https://files.pythonhosted.org/packages/8f/36/3dee40767356e104967e6ed6d102ba47b0b1ce2a89432239b95a94de1b89/mmh3-5.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b29044e1ffdb84fe164d0a7ea05c7316afea93c00f8ed9449cf357c36fc4f814", size = 40057, upload-time = "2025-07-29T07:42:36.755Z" }, - { url = "https://files.pythonhosted.org/packages/31/58/228c402fccf76eb39a0a01b8fc470fecf21965584e66453b477050ee0e99/mmh3-5.2.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:58981d6ea9646dbbf9e59a30890cbf9f610df0e4a57dbfe09215116fd90b0093", size = 97344, upload-time = "2025-07-29T07:42:37.675Z" }, - { url = "https://files.pythonhosted.org/packages/34/82/fc5ce89006389a6426ef28e326fc065b0fbaaed230373b62d14c889f47ea/mmh3-5.2.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e5634565367b6d98dc4aa2983703526ef556b3688ba3065edb4b9b90ede1c54", size = 103325, upload-time = "2025-07-29T07:42:38.591Z" }, - { url = "https://files.pythonhosted.org/packages/09/8c/261e85777c6aee1ebd53f2f17e210e7481d5b0846cd0b4a5c45f1e3761b8/mmh3-5.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0271ac12415afd3171ab9a3c7cbfc71dee2c68760a7dc9d05bf8ed6ddfa3a7a", size = 106240, upload-time = "2025-07-29T07:42:39.563Z" }, - { url = "https://files.pythonhosted.org/packages/70/73/2f76b3ad8a3d431824e9934403df36c0ddacc7831acf82114bce3c4309c8/mmh3-5.2.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:45b590e31bc552c6f8e2150ff1ad0c28dd151e9f87589e7eaf508fbdd8e8e908", size = 113060, upload-time = "2025-07-29T07:42:40.585Z" }, - { url = "https://files.pythonhosted.org/packages/9f/b9/7ea61a34e90e50a79a9d87aa1c0b8139a7eaf4125782b34b7d7383472633/mmh3-5.2.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bdde97310d59604f2a9119322f61b31546748499a21b44f6715e8ced9308a6c5", size = 120781, upload-time = "2025-07-29T07:42:41.618Z" }, - { url = "https://files.pythonhosted.org/packages/0f/5b/ae1a717db98c7894a37aeedbd94b3f99e6472a836488f36b6849d003485b/mmh3-5.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc9c5f280438cf1c1a8f9abb87dc8ce9630a964120cfb5dd50d1e7ce79690c7a", size = 99174, upload-time = "2025-07-29T07:42:42.587Z" }, - { url = "https://files.pythonhosted.org/packages/e3/de/000cce1d799fceebb6d4487ae29175dd8e81b48e314cba7b4da90bcf55d7/mmh3-5.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c903e71fd8debb35ad2a4184c1316b3cb22f64ce517b4e6747f25b0a34e41266", size = 98734, upload-time = "2025-07-29T07:42:43.996Z" }, - { url = "https://files.pythonhosted.org/packages/79/19/0dc364391a792b72fbb22becfdeacc5add85cc043cd16986e82152141883/mmh3-5.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:eed4bba7ff8a0d37106ba931ab03bdd3915fbb025bcf4e1f0aa02bc8114960c5", size = 106493, upload-time = "2025-07-29T07:42:45.07Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b1/bc8c28e4d6e807bbb051fefe78e1156d7f104b89948742ad310612ce240d/mmh3-5.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1fdb36b940e9261aff0b5177c5b74a36936b902f473180f6c15bde26143681a9", size = 110089, upload-time = "2025-07-29T07:42:46.122Z" }, - { url = "https://files.pythonhosted.org/packages/3b/a2/d20f3f5c95e9c511806686c70d0a15479cc3941c5f322061697af1c1ff70/mmh3-5.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7303aab41e97adcf010a09efd8f1403e719e59b7705d5e3cfed3dd7571589290", size = 97571, upload-time = "2025-07-29T07:42:47.18Z" }, - { url = "https://files.pythonhosted.org/packages/7b/23/665296fce4f33488deec39a750ffd245cfc07aafb0e3ef37835f91775d14/mmh3-5.2.0-cp313-cp313-win32.whl", hash = "sha256:03e08c6ebaf666ec1e3d6ea657a2d363bb01effd1a9acfe41f9197decaef0051", size = 40806, upload-time = "2025-07-29T07:42:48.166Z" }, - { url = "https://files.pythonhosted.org/packages/59/b0/92e7103f3b20646e255b699e2d0327ce53a3f250e44367a99dc8be0b7c7a/mmh3-5.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:7fddccd4113e7b736706e17a239a696332360cbaddf25ae75b57ba1acce65081", size = 41600, upload-time = "2025-07-29T07:42:49.371Z" }, - { url = "https://files.pythonhosted.org/packages/99/22/0b2bd679a84574647de538c5b07ccaa435dbccc37815067fe15b90fe8dad/mmh3-5.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa0c966ee727aad5406d516375593c5f058c766b21236ab8985693934bb5085b", size = 39349, upload-time = "2025-07-29T07:42:50.268Z" }, - { url = "https://files.pythonhosted.org/packages/f7/ca/a20db059a8a47048aaf550da14a145b56e9c7386fb8280d3ce2962dcebf7/mmh3-5.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e5015f0bb6eb50008bed2d4b1ce0f2a294698a926111e4bb202c0987b4f89078", size = 39209, upload-time = "2025-07-29T07:42:51.559Z" }, - { url = "https://files.pythonhosted.org/packages/98/dd/e5094799d55c7482d814b979a0fd608027d0af1b274bfb4c3ea3e950bfd5/mmh3-5.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:e0f3ed828d709f5b82d8bfe14f8856120718ec4bd44a5b26102c3030a1e12501", size = 39843, upload-time = "2025-07-29T07:42:52.536Z" }, - { url = "https://files.pythonhosted.org/packages/f4/6b/7844d7f832c85400e7cc89a1348e4e1fdd38c5a38415bb5726bbb8fcdb6c/mmh3-5.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:f35727c5118aba95f0397e18a1a5b8405425581bfe53e821f0fb444cbdc2bc9b", size = 40648, upload-time = "2025-07-29T07:42:53.392Z" }, - { url = "https://files.pythonhosted.org/packages/1f/bf/71f791f48a21ff3190ba5225807cbe4f7223360e96862c376e6e3fb7efa7/mmh3-5.2.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bc244802ccab5220008cb712ca1508cb6a12f0eb64ad62997156410579a1770", size = 56164, upload-time = "2025-07-29T07:42:54.267Z" }, - { url = "https://files.pythonhosted.org/packages/70/1f/f87e3d34d83032b4f3f0f528c6d95a98290fcacf019da61343a49dccfd51/mmh3-5.2.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ff3d50dc3fe8a98059f99b445dfb62792b5d006c5e0b8f03c6de2813b8376110", size = 40692, upload-time = "2025-07-29T07:42:55.234Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e2/db849eaed07117086f3452feca8c839d30d38b830ac59fe1ce65af8be5ad/mmh3-5.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:37a358cc881fe796e099c1db6ce07ff757f088827b4e8467ac52b7a7ffdca647", size = 40068, upload-time = "2025-07-29T07:42:56.158Z" }, - { url = "https://files.pythonhosted.org/packages/df/6b/209af927207af77425b044e32f77f49105a0b05d82ff88af6971d8da4e19/mmh3-5.2.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b9a87025121d1c448f24f27ff53a5fe7b6ef980574b4a4f11acaabe702420d63", size = 97367, upload-time = "2025-07-29T07:42:57.037Z" }, - { url = "https://files.pythonhosted.org/packages/ca/e0/78adf4104c425606a9ce33fb351f790c76a6c2314969c4a517d1ffc92196/mmh3-5.2.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ba55d6ca32eeef8b2625e1e4bfc3b3db52bc63014bd7e5df8cc11bf2b036b12", size = 103306, upload-time = "2025-07-29T07:42:58.522Z" }, - { url = "https://files.pythonhosted.org/packages/a3/79/c2b89f91b962658b890104745b1b6c9ce38d50a889f000b469b91eeb1b9e/mmh3-5.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9ff37ba9f15637e424c2ab57a1a590c52897c845b768e4e0a4958084ec87f22", size = 106312, upload-time = "2025-07-29T07:42:59.552Z" }, - { url = "https://files.pythonhosted.org/packages/4b/14/659d4095528b1a209be90934778c5ffe312177d51e365ddcbca2cac2ec7c/mmh3-5.2.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a094319ec0db52a04af9fdc391b4d39a1bc72bc8424b47c4411afb05413a44b5", size = 113135, upload-time = "2025-07-29T07:43:00.745Z" }, - { url = "https://files.pythonhosted.org/packages/8d/6f/cd7734a779389a8a467b5c89a48ff476d6f2576e78216a37551a97e9e42a/mmh3-5.2.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c5584061fd3da584659b13587f26c6cad25a096246a481636d64375d0c1f6c07", size = 120775, upload-time = "2025-07-29T07:43:02.124Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ca/8256e3b96944408940de3f9291d7e38a283b5761fe9614d4808fcf27bd62/mmh3-5.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecbfc0437ddfdced5e7822d1ce4855c9c64f46819d0fdc4482c53f56c707b935", size = 99178, upload-time = "2025-07-29T07:43:03.182Z" }, - { url = "https://files.pythonhosted.org/packages/8a/32/39e2b3cf06b6e2eb042c984dab8680841ac2a0d3ca6e0bea30db1f27b565/mmh3-5.2.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:7b986d506a8e8ea345791897ba5d8ba0d9d8820cd4fc3e52dbe6de19388de2e7", size = 98738, upload-time = "2025-07-29T07:43:04.207Z" }, - { url = "https://files.pythonhosted.org/packages/61/d3/7bbc8e0e8cf65ebbe1b893ffa0467b7ecd1bd07c3bbf6c9db4308ada22ec/mmh3-5.2.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:38d899a156549da8ef6a9f1d6f7ef231228d29f8f69bce2ee12f5fba6d6fd7c5", size = 106510, upload-time = "2025-07-29T07:43:05.656Z" }, - { url = "https://files.pythonhosted.org/packages/10/99/b97e53724b52374e2f3859046f0eb2425192da356cb19784d64bc17bb1cf/mmh3-5.2.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d86651fa45799530885ba4dab3d21144486ed15285e8784181a0ab37a4552384", size = 110053, upload-time = "2025-07-29T07:43:07.204Z" }, - { url = "https://files.pythonhosted.org/packages/ac/62/3688c7d975ed195155671df68788c83fed6f7909b6ec4951724c6860cb97/mmh3-5.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c463d7c1c4cfc9d751efeaadd936bbba07b5b0ed81a012b3a9f5a12f0872bd6e", size = 97546, upload-time = "2025-07-29T07:43:08.226Z" }, - { url = "https://files.pythonhosted.org/packages/ca/3b/c6153250f03f71a8b7634cded82939546cdfba02e32f124ff51d52c6f991/mmh3-5.2.0-cp314-cp314-win32.whl", hash = "sha256:bb4fe46bdc6104fbc28db7a6bacb115ee6368ff993366bbd8a2a7f0076e6f0c0", size = 41422, upload-time = "2025-07-29T07:43:09.216Z" }, - { url = "https://files.pythonhosted.org/packages/74/01/a27d98bab083a435c4c07e9d1d720d4c8a578bf4c270bae373760b1022be/mmh3-5.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:7c7f0b342fd06044bedd0b6e72177ddc0076f54fd89ee239447f8b271d919d9b", size = 42135, upload-time = "2025-07-29T07:43:10.183Z" }, - { url = "https://files.pythonhosted.org/packages/cb/c9/dbba5507e95429b8b380e2ba091eff5c20a70a59560934dff0ad8392b8c8/mmh3-5.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:3193752fc05ea72366c2b63ff24b9a190f422e32d75fdeae71087c08fff26115", size = 39879, upload-time = "2025-07-29T07:43:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/b5/d1/c8c0ef839c17258b9de41b84f663574fabcf8ac2007b7416575e0f65ff6e/mmh3-5.2.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:69fc339d7202bea69ef9bd7c39bfdf9fdabc8e6822a01eba62fb43233c1b3932", size = 57696, upload-time = "2025-07-29T07:43:11.989Z" }, - { url = "https://files.pythonhosted.org/packages/2f/55/95e2b9ff201e89f9fe37036037ab61a6c941942b25cdb7b6a9df9b931993/mmh3-5.2.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:12da42c0a55c9d86ab566395324213c319c73ecb0c239fad4726324212b9441c", size = 41421, upload-time = "2025-07-29T07:43:13.269Z" }, - { url = "https://files.pythonhosted.org/packages/77/79/9be23ad0b7001a4b22752e7693be232428ecc0a35068a4ff5c2f14ef8b20/mmh3-5.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f7f9034c7cf05ddfaac8d7a2e63a3c97a840d4615d0a0e65ba8bdf6f8576e3be", size = 40853, upload-time = "2025-07-29T07:43:14.888Z" }, - { url = "https://files.pythonhosted.org/packages/ac/1b/96b32058eda1c1dee8264900c37c359a7325c1f11f5ff14fd2be8e24eff9/mmh3-5.2.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:11730eeb16dfcf9674fdea9bb6b8e6dd9b40813b7eb839bc35113649eef38aeb", size = 109694, upload-time = "2025-07-29T07:43:15.816Z" }, - { url = "https://files.pythonhosted.org/packages/8d/6f/a2ae44cd7dad697b6dea48390cbc977b1e5ca58fda09628cbcb2275af064/mmh3-5.2.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:932a6eec1d2e2c3c9e630d10f7128d80e70e2d47fe6b8c7ea5e1afbd98733e65", size = 117438, upload-time = "2025-07-29T07:43:16.865Z" }, - { url = "https://files.pythonhosted.org/packages/a0/08/bfb75451c83f05224a28afeaf3950c7b793c0b71440d571f8e819cfb149a/mmh3-5.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ca975c51c5028947bbcfc24966517aac06a01d6c921e30f7c5383c195f87991", size = 120409, upload-time = "2025-07-29T07:43:18.207Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ea/8b118b69b2ff8df568f742387d1a159bc654a0f78741b31437dd047ea28e/mmh3-5.2.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5b0b58215befe0f0e120b828f7645e97719bbba9f23b69e268ed0ac7adde8645", size = 125909, upload-time = "2025-07-29T07:43:19.39Z" }, - { url = "https://files.pythonhosted.org/packages/3e/11/168cc0b6a30650032e351a3b89b8a47382da541993a03af91e1ba2501234/mmh3-5.2.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29c2b9ce61886809d0492a274a5a53047742dea0f703f9c4d5d223c3ea6377d3", size = 135331, upload-time = "2025-07-29T07:43:20.435Z" }, - { url = "https://files.pythonhosted.org/packages/31/05/e3a9849b1c18a7934c64e831492c99e67daebe84a8c2f2c39a7096a830e3/mmh3-5.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a367d4741ac0103f8198c82f429bccb9359f543ca542b06a51f4f0332e8de279", size = 110085, upload-time = "2025-07-29T07:43:21.92Z" }, - { url = "https://files.pythonhosted.org/packages/d9/d5/a96bcc306e3404601418b2a9a370baec92af84204528ba659fdfe34c242f/mmh3-5.2.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:5a5dba98e514fb26241868f6eb90a7f7ca0e039aed779342965ce24ea32ba513", size = 111195, upload-time = "2025-07-29T07:43:23.066Z" }, - { url = "https://files.pythonhosted.org/packages/af/29/0fd49801fec5bff37198684e0849b58e0dab3a2a68382a357cfffb0fafc3/mmh3-5.2.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:941603bfd75a46023807511c1ac2f1b0f39cccc393c15039969806063b27e6db", size = 116919, upload-time = "2025-07-29T07:43:24.178Z" }, - { url = "https://files.pythonhosted.org/packages/2d/04/4f3c32b0a2ed762edca45d8b46568fc3668e34f00fb1e0a3b5451ec1281c/mmh3-5.2.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:132dd943451a7c7546978863d2f5a64977928410782e1a87d583cb60eb89e667", size = 123160, upload-time = "2025-07-29T07:43:25.26Z" }, - { url = "https://files.pythonhosted.org/packages/91/76/3d29eaa38821730633d6a240d36fa8ad2807e9dfd432c12e1a472ed211eb/mmh3-5.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f698733a8a494466432d611a8f0d1e026f5286dee051beea4b3c3146817e35d5", size = 110206, upload-time = "2025-07-29T07:43:26.699Z" }, - { url = "https://files.pythonhosted.org/packages/44/1c/ccf35892684d3a408202e296e56843743e0b4fb1629e59432ea88cdb3909/mmh3-5.2.0-cp314-cp314t-win32.whl", hash = "sha256:6d541038b3fc360ec538fc116de87462627944765a6750308118f8b509a8eec7", size = 41970, upload-time = "2025-07-29T07:43:27.666Z" }, - { url = "https://files.pythonhosted.org/packages/75/b2/b9e4f1e5adb5e21eb104588fcee2cd1eaa8308255173481427d5ecc4284e/mmh3-5.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e912b19cf2378f2967d0c08e86ff4c6c360129887f678e27e4dde970d21b3f4d", size = 43063, upload-time = "2025-07-29T07:43:28.582Z" }, - { url = "https://files.pythonhosted.org/packages/6a/fc/0e61d9a4e29c8679356795a40e48f647b4aad58d71bfc969f0f8f56fb912/mmh3-5.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:e7884931fe5e788163e7b3c511614130c2c59feffdc21112290a194487efb2e9", size = 40455, upload-time = "2025-07-29T07:43:29.563Z" }, -] - -[[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 = "motor" -version = "3.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pymongo" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/ae/96b88362d6a84cb372f7977750ac2a8aed7b2053eed260615df08d5c84f4/motor-3.7.1.tar.gz", hash = "sha256:27b4d46625c87928f331a6ca9d7c51c2f518ba0e270939d395bc1ddc89d64526", size = 280997, upload-time = "2025-05-14T18:56:33.653Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/9a/35e053d4f442addf751ed20e0e922476508ee580786546d699b0567c4c67/motor-3.7.1-py3-none-any.whl", hash = "sha256:8a63b9049e38eeeb56b4fdd57c3312a6d1f25d01db717fe7d82222393c410298", size = 74996, upload-time = "2025-05-14T18:56:31.665Z" }, -] - -[[package]] -name = "mouseinfo" -version = "0.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyperclip" }, - { name = "python3-xlib", marker = "sys_platform == 'linux'" }, - { name = "rubicon-objc", marker = "sys_platform == 'darwin'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/fa/b2ba8229b9381e8f6381c1dcae6f4159a7f72349e414ed19cfbbd1817173/MouseInfo-0.1.3.tar.gz", hash = "sha256:2c62fb8885062b8e520a3cce0a297c657adcc08c60952eb05bc8256ef6f7f6e7", size = 10850, upload-time = "2020-03-27T21:20:10.136Z" } - -[[package]] -name = "mpmath" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, -] - -[[package]] -name = "multidict" -version = "6.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, - { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, - { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, - { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, - { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, - { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, - { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, - { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, - { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, - { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, - { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, - { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, - { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, - { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, - { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, - { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, - { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, - { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, - { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, - { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, - { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, - { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, - { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, - { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, - { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, - { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, - { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, - { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, - { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, - { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, - { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, - { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, - { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, - { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, - { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, - { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, - { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, - { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, - { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, - { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, - { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, - { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, - { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, - { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, - { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, - { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, - { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, - { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, - { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, - { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, - { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, - { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, - { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, - { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, - { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, - { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, - { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, - { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, - { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, - { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, - { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, - { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, - { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, - { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, - { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, - { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, - { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, - { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, - { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, - { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, - { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, - { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, - { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, - { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, - { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, - { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, - { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, -] - -[[package]] -name = "nats-py" -version = "2.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/71/c5/2564d917503fe8d68fe630c74bf6b678fbc15c01b58f2565894761010f57/nats_py-2.12.0.tar.gz", hash = "sha256:2981ca4b63b8266c855573fa7871b1be741f1889fd429ee657e5ffc0971a38a1", size = 119821, upload-time = "2025-10-31T05:27:31.247Z" } - -[[package]] -name = "networkx" -version = "3.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, -] - -[[package]] -name = "nexus-rpc" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2e/f2/d54f5c03d8f4672ccc0875787a385f53dcb61f98a8ae594b5620e85b9cb3/nexus_rpc-1.3.0.tar.gz", hash = "sha256:e56d3b57b60d707ce7a72f83f23f106b86eca1043aa658e44582ab5ff30ab9ad", size = 75650, upload-time = "2025-12-08T22:59:13.002Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/74/0afd841de3199c148146c1d43b4bfb5605b2f1dc4c9a9087fe395091ea5a/nexus_rpc-1.3.0-py3-none-any.whl", hash = "sha256:aee0707b4861b22d8124ecb3f27d62dafbe8777dc50c66c91e49c006f971b92d", size = 28873, upload-time = "2025-12-08T22:59:12.024Z" }, -] - -[[package]] -name = "numpy" -version = "2.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/fd/0005efbd0af48e55eb3c7208af93f2862d4b1a56cd78e84309a2d959208d/numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae", size = 20723651, upload-time = "2026-01-31T23:13:10.135Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/6e/6f394c9c77668153e14d4da83bcc247beb5952f6ead7699a1a2992613bea/numpy-2.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:21982668592194c609de53ba4933a7471880ccbaadcc52352694a59ecc860b3a", size = 16667963, upload-time = "2026-01-31T23:10:52.147Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f8/55483431f2b2fd015ae6ed4fe62288823ce908437ed49db5a03d15151678/numpy-2.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40397bda92382fcec844066efb11f13e1c9a3e2a8e8f318fb72ed8b6db9f60f1", size = 14693571, upload-time = "2026-01-31T23:10:54.789Z" }, - { url = "https://files.pythonhosted.org/packages/2f/20/18026832b1845cdc82248208dd929ca14c9d8f2bac391f67440707fff27c/numpy-2.4.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b3a24467af63c67829bfaa61eecf18d5432d4f11992688537be59ecd6ad32f5e", size = 5203469, upload-time = "2026-01-31T23:10:57.343Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/2eb97c8a77daaba34eaa3fa7241a14ac5f51c46a6bd5911361b644c4a1e2/numpy-2.4.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:805cc8de9fd6e7a22da5aed858e0ab16be5a4db6c873dde1d7451c541553aa27", size = 6550820, upload-time = "2026-01-31T23:10:59.429Z" }, - { url = "https://files.pythonhosted.org/packages/b1/91/b97fdfd12dc75b02c44e26c6638241cc004d4079a0321a69c62f51470c4c/numpy-2.4.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d82351358ffbcdcd7b686b90742a9b86632d6c1c051016484fa0b326a0a1548", size = 15663067, upload-time = "2026-01-31T23:11:01.291Z" }, - { url = "https://files.pythonhosted.org/packages/f5/c6/a18e59f3f0b8071cc85cbc8d80cd02d68aa9710170b2553a117203d46936/numpy-2.4.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e35d3e0144137d9fdae62912e869136164534d64a169f86438bc9561b6ad49f", size = 16619782, upload-time = "2026-01-31T23:11:03.669Z" }, - { url = "https://files.pythonhosted.org/packages/b7/83/9751502164601a79e18847309f5ceec0b1446d7b6aa12305759b72cf98b2/numpy-2.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adb6ed2ad29b9e15321d167d152ee909ec73395901b70936f029c3bc6d7f4460", size = 17013128, upload-time = "2026-01-31T23:11:05.913Z" }, - { url = "https://files.pythonhosted.org/packages/61/c4/c4066322256ec740acc1c8923a10047818691d2f8aec254798f3dd90f5f2/numpy-2.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8906e71fd8afcb76580404e2a950caef2685df3d2a57fe82a86ac8d33cc007ba", size = 18345324, upload-time = "2026-01-31T23:11:08.248Z" }, - { url = "https://files.pythonhosted.org/packages/ab/af/6157aa6da728fa4525a755bfad486ae7e3f76d4c1864138003eb84328497/numpy-2.4.2-cp312-cp312-win32.whl", hash = "sha256:ec055f6dae239a6299cace477b479cca2fc125c5675482daf1dd886933a1076f", size = 5960282, upload-time = "2026-01-31T23:11:10.497Z" }, - { url = "https://files.pythonhosted.org/packages/92/0f/7ceaaeaacb40567071e94dbf2c9480c0ae453d5bb4f52bea3892c39dc83c/numpy-2.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:209fae046e62d0ce6435fcfe3b1a10537e858249b3d9b05829e2a05218296a85", size = 12314210, upload-time = "2026-01-31T23:11:12.176Z" }, - { url = "https://files.pythonhosted.org/packages/2f/a3/56c5c604fae6dd40fa2ed3040d005fca97e91bd320d232ac9931d77ba13c/numpy-2.4.2-cp312-cp312-win_arm64.whl", hash = "sha256:fbde1b0c6e81d56f5dccd95dd4a711d9b95df1ae4009a60887e56b27e8d903fa", size = 10220171, upload-time = "2026-01-31T23:11:14.684Z" }, - { url = "https://files.pythonhosted.org/packages/a1/22/815b9fe25d1d7ae7d492152adbc7226d3eff731dffc38fe970589fcaaa38/numpy-2.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25f2059807faea4b077a2b6837391b5d830864b3543627f381821c646f31a63c", size = 16663696, upload-time = "2026-01-31T23:11:17.516Z" }, - { url = "https://files.pythonhosted.org/packages/09/f0/817d03a03f93ba9c6c8993de509277d84e69f9453601915e4a69554102a1/numpy-2.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bd3a7a9f5847d2fb8c2c6d1c862fa109c31a9abeca1a3c2bd5a64572955b2979", size = 14688322, upload-time = "2026-01-31T23:11:19.883Z" }, - { url = "https://files.pythonhosted.org/packages/da/b4/f805ab79293c728b9a99438775ce51885fd4f31b76178767cfc718701a39/numpy-2.4.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8e4549f8a3c6d13d55041925e912bfd834285ef1dd64d6bc7d542583355e2e98", size = 5198157, upload-time = "2026-01-31T23:11:22.375Z" }, - { url = "https://files.pythonhosted.org/packages/74/09/826e4289844eccdcd64aac27d13b0fd3f32039915dd5b9ba01baae1f436c/numpy-2.4.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:aea4f66ff44dfddf8c2cffd66ba6538c5ec67d389285292fe428cb2c738c8aef", size = 6546330, upload-time = "2026-01-31T23:11:23.958Z" }, - { url = "https://files.pythonhosted.org/packages/19/fb/cbfdbfa3057a10aea5422c558ac57538e6acc87ec1669e666d32ac198da7/numpy-2.4.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3cd545784805de05aafe1dde61752ea49a359ccba9760c1e5d1c88a93bbf2b7", size = 15660968, upload-time = "2026-01-31T23:11:25.713Z" }, - { url = "https://files.pythonhosted.org/packages/04/dc/46066ce18d01645541f0186877377b9371b8fa8017fa8262002b4ef22612/numpy-2.4.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0d9b7c93578baafcbc5f0b83eaf17b79d345c6f36917ba0c67f45226911d499", size = 16607311, upload-time = "2026-01-31T23:11:28.117Z" }, - { url = "https://files.pythonhosted.org/packages/14/d9/4b5adfc39a43fa6bf918c6d544bc60c05236cc2f6339847fc5b35e6cb5b0/numpy-2.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f74f0f7779cc7ae07d1810aab8ac6b1464c3eafb9e283a40da7309d5e6e48fbb", size = 17012850, upload-time = "2026-01-31T23:11:30.888Z" }, - { url = "https://files.pythonhosted.org/packages/b7/20/adb6e6adde6d0130046e6fdfb7675cc62bc2f6b7b02239a09eb58435753d/numpy-2.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7ac672d699bf36275c035e16b65539931347d68b70667d28984c9fb34e07fa7", size = 18334210, upload-time = "2026-01-31T23:11:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/78/0e/0a73b3dff26803a8c02baa76398015ea2a5434d9b8265a7898a6028c1591/numpy-2.4.2-cp313-cp313-win32.whl", hash = "sha256:8e9afaeb0beff068b4d9cd20d322ba0ee1cecfb0b08db145e4ab4dd44a6b5110", size = 5958199, upload-time = "2026-01-31T23:11:35.385Z" }, - { url = "https://files.pythonhosted.org/packages/43/bc/6352f343522fcb2c04dbaf94cb30cca6fd32c1a750c06ad6231b4293708c/numpy-2.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:7df2de1e4fba69a51c06c28f5a3de36731eb9639feb8e1cf7e4a7b0daf4cf622", size = 12310848, upload-time = "2026-01-31T23:11:38.001Z" }, - { url = "https://files.pythonhosted.org/packages/6e/8d/6da186483e308da5da1cc6918ce913dcfe14ffde98e710bfeff2a6158d4e/numpy-2.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:0fece1d1f0a89c16b03442eae5c56dc0be0c7883b5d388e0c03f53019a4bfd71", size = 10221082, upload-time = "2026-01-31T23:11:40.392Z" }, - { url = "https://files.pythonhosted.org/packages/25/a1/9510aa43555b44781968935c7548a8926274f815de42ad3997e9e83680dd/numpy-2.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5633c0da313330fd20c484c78cdd3f9b175b55e1a766c4a174230c6b70ad8262", size = 14815866, upload-time = "2026-01-31T23:11:42.495Z" }, - { url = "https://files.pythonhosted.org/packages/36/30/6bbb5e76631a5ae46e7923dd16ca9d3f1c93cfa8d4ed79a129814a9d8db3/numpy-2.4.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d9f64d786b3b1dd742c946c42d15b07497ed14af1a1f3ce840cce27daa0ce913", size = 5325631, upload-time = "2026-01-31T23:11:44.7Z" }, - { url = "https://files.pythonhosted.org/packages/46/00/3a490938800c1923b567b3a15cd17896e68052e2145d8662aaf3e1ffc58f/numpy-2.4.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:b21041e8cb6a1eb5312dd1d2f80a94d91efffb7a06b70597d44f1bd2dfc315ab", size = 6646254, upload-time = "2026-01-31T23:11:46.341Z" }, - { url = "https://files.pythonhosted.org/packages/d3/e9/fac0890149898a9b609caa5af7455a948b544746e4b8fe7c212c8edd71f8/numpy-2.4.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00ab83c56211a1d7c07c25e3217ea6695e50a3e2f255053686b081dc0b091a82", size = 15720138, upload-time = "2026-01-31T23:11:48.082Z" }, - { url = "https://files.pythonhosted.org/packages/ea/5c/08887c54e68e1e28df53709f1893ce92932cc6f01f7c3d4dc952f61ffd4e/numpy-2.4.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fb882da679409066b4603579619341c6d6898fc83a8995199d5249f986e8e8f", size = 16655398, upload-time = "2026-01-31T23:11:50.293Z" }, - { url = "https://files.pythonhosted.org/packages/4d/89/253db0fa0e66e9129c745e4ef25631dc37d5f1314dad2b53e907b8538e6d/numpy-2.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66cb9422236317f9d44b67b4d18f44efe6e9c7f8794ac0462978513359461554", size = 17079064, upload-time = "2026-01-31T23:11:52.927Z" }, - { url = "https://files.pythonhosted.org/packages/2a/d5/cbade46ce97c59c6c3da525e8d95b7abe8a42974a1dc5c1d489c10433e88/numpy-2.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0f01dcf33e73d80bd8dc0f20a71303abbafa26a19e23f6b68d1aa9990af90257", size = 18379680, upload-time = "2026-01-31T23:11:55.22Z" }, - { url = "https://files.pythonhosted.org/packages/40/62/48f99ae172a4b63d981babe683685030e8a3df4f246c893ea5c6ef99f018/numpy-2.4.2-cp313-cp313t-win32.whl", hash = "sha256:52b913ec40ff7ae845687b0b34d8d93b60cb66dcee06996dd5c99f2fc9328657", size = 6082433, upload-time = "2026-01-31T23:11:58.096Z" }, - { url = "https://files.pythonhosted.org/packages/07/38/e054a61cfe48ad9f1ed0d188e78b7e26859d0b60ef21cd9de4897cdb5326/numpy-2.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5eea80d908b2c1f91486eb95b3fb6fab187e569ec9752ab7d9333d2e66bf2d6b", size = 12451181, upload-time = "2026-01-31T23:11:59.782Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a4/a05c3a6418575e185dd84d0b9680b6bb2e2dc3e4202f036b7b4e22d6e9dc/numpy-2.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fd49860271d52127d61197bb50b64f58454e9f578cb4b2c001a6de8b1f50b0b1", size = 10290756, upload-time = "2026-01-31T23:12:02.438Z" }, - { url = "https://files.pythonhosted.org/packages/18/88/b7df6050bf18fdcfb7046286c6535cabbdd2064a3440fca3f069d319c16e/numpy-2.4.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b", size = 16663092, upload-time = "2026-01-31T23:12:04.521Z" }, - { url = "https://files.pythonhosted.org/packages/25/7a/1fee4329abc705a469a4afe6e69b1ef7e915117747886327104a8493a955/numpy-2.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000", size = 14698770, upload-time = "2026-01-31T23:12:06.96Z" }, - { url = "https://files.pythonhosted.org/packages/fb/0b/f9e49ba6c923678ad5bc38181c08ac5e53b7a5754dbca8e581aa1a56b1ff/numpy-2.4.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1", size = 5208562, upload-time = "2026-01-31T23:12:09.632Z" }, - { url = "https://files.pythonhosted.org/packages/7d/12/d7de8f6f53f9bb76997e5e4c069eda2051e3fe134e9181671c4391677bb2/numpy-2.4.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74", size = 6543710, upload-time = "2026-01-31T23:12:11.969Z" }, - { url = "https://files.pythonhosted.org/packages/09/63/c66418c2e0268a31a4cf8a8b512685748200f8e8e8ec6c507ce14e773529/numpy-2.4.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a", size = 15677205, upload-time = "2026-01-31T23:12:14.33Z" }, - { url = "https://files.pythonhosted.org/packages/5d/6c/7f237821c9642fb2a04d2f1e88b4295677144ca93285fd76eff3bcba858d/numpy-2.4.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325", size = 16611738, upload-time = "2026-01-31T23:12:16.525Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a7/39c4cdda9f019b609b5c473899d87abff092fc908cfe4d1ecb2fcff453b0/numpy-2.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909", size = 17028888, upload-time = "2026-01-31T23:12:19.306Z" }, - { url = "https://files.pythonhosted.org/packages/da/b3/e84bb64bdfea967cc10950d71090ec2d84b49bc691df0025dddb7c26e8e3/numpy-2.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a", size = 18339556, upload-time = "2026-01-31T23:12:21.816Z" }, - { url = "https://files.pythonhosted.org/packages/88/f5/954a291bc1192a27081706862ac62bb5920fbecfbaa302f64682aa90beed/numpy-2.4.2-cp314-cp314-win32.whl", hash = "sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a", size = 6006899, upload-time = "2026-01-31T23:12:24.14Z" }, - { url = "https://files.pythonhosted.org/packages/05/cb/eff72a91b2efdd1bc98b3b8759f6a1654aa87612fc86e3d87d6fe4f948c4/numpy-2.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75", size = 12443072, upload-time = "2026-01-31T23:12:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/37/75/62726948db36a56428fce4ba80a115716dc4fad6a3a4352487f8bb950966/numpy-2.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05", size = 10494886, upload-time = "2026-01-31T23:12:28.488Z" }, - { url = "https://files.pythonhosted.org/packages/36/2f/ee93744f1e0661dc267e4b21940870cabfae187c092e1433b77b09b50ac4/numpy-2.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308", size = 14818567, upload-time = "2026-01-31T23:12:30.709Z" }, - { url = "https://files.pythonhosted.org/packages/a7/24/6535212add7d76ff938d8bdc654f53f88d35cddedf807a599e180dcb8e66/numpy-2.4.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef", size = 5328372, upload-time = "2026-01-31T23:12:32.962Z" }, - { url = "https://files.pythonhosted.org/packages/5e/9d/c48f0a035725f925634bf6b8994253b43f2047f6778a54147d7e213bc5a7/numpy-2.4.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d", size = 6649306, upload-time = "2026-01-31T23:12:34.797Z" }, - { url = "https://files.pythonhosted.org/packages/81/05/7c73a9574cd4a53a25907bad38b59ac83919c0ddc8234ec157f344d57d9a/numpy-2.4.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8", size = 15722394, upload-time = "2026-01-31T23:12:36.565Z" }, - { url = "https://files.pythonhosted.org/packages/35/fa/4de10089f21fc7d18442c4a767ab156b25c2a6eaf187c0db6d9ecdaeb43f/numpy-2.4.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5", size = 16653343, upload-time = "2026-01-31T23:12:39.188Z" }, - { url = "https://files.pythonhosted.org/packages/b8/f9/d33e4ffc857f3763a57aa85650f2e82486832d7492280ac21ba9efda80da/numpy-2.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e", size = 17078045, upload-time = "2026-01-31T23:12:42.041Z" }, - { url = "https://files.pythonhosted.org/packages/c8/b8/54bdb43b6225badbea6389fa038c4ef868c44f5890f95dd530a218706da3/numpy-2.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a", size = 18380024, upload-time = "2026-01-31T23:12:44.331Z" }, - { url = "https://files.pythonhosted.org/packages/a5/55/6e1a61ded7af8df04016d81b5b02daa59f2ea9252ee0397cb9f631efe9e5/numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443", size = 6153937, upload-time = "2026-01-31T23:12:47.229Z" }, - { url = "https://files.pythonhosted.org/packages/45/aa/fa6118d1ed6d776b0983f3ceac9b1a5558e80df9365b1c3aa6d42bf9eee4/numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236", size = 12631844, upload-time = "2026-01-31T23:12:48.997Z" }, - { url = "https://files.pythonhosted.org/packages/32/0a/2ec5deea6dcd158f254a7b372fb09cfba5719419c8d66343bab35237b3fb/numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181", size = 10565379, upload-time = "2026-01-31T23:12:51.345Z" }, -] - -[[package]] -name = "nvidia-cublas-cu12" -version = "12.8.4.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, -] - -[[package]] -name = "nvidia-cuda-cupti-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, -] - -[[package]] -name = "nvidia-cuda-nvrtc-cu12" -version = "12.8.93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, -] - -[[package]] -name = "nvidia-cuda-runtime-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, -] - -[[package]] -name = "nvidia-cudnn-cu12" -version = "9.10.2.21" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, -] - -[[package]] -name = "nvidia-cufft-cu12" -version = "11.3.3.83" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, -] - -[[package]] -name = "nvidia-cufile-cu12" -version = "1.13.1.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, -] - -[[package]] -name = "nvidia-curand-cu12" -version = "10.3.9.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, -] - -[[package]] -name = "nvidia-cusolver-cu12" -version = "11.7.3.90" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "nvidia-cusparse-cu12", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "nvidia-nvjitlink-cu12", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, -] - -[[package]] -name = "nvidia-cusparse-cu12" -version = "12.5.8.93" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, -] - -[[package]] -name = "nvidia-cusparselt-cu12" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, -] - -[[package]] -name = "nvidia-nccl-cu12" -version = "2.27.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, -] - -[[package]] -name = "nvidia-nvjitlink-cu12" -version = "12.8.93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, -] - -[[package]] -name = "nvidia-nvshmem-cu12" -version = "3.4.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095, upload-time = "2025-09-06T00:32:31.266Z" }, -] - -[[package]] -name = "nvidia-nvtx-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, -] - -[[package]] -name = "oauthlib" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" }, -] - -[[package]] -name = "onnxruntime" -version = "1.23.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coloredlogs" }, - { name = "flatbuffers" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "sympy" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/9e/f748cd64161213adeef83d0cb16cb8ace1e62fa501033acdd9f9341fff57/onnxruntime-1.23.2-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:b8f029a6b98d3cf5be564d52802bb50a8489ab73409fa9db0bf583eabb7c2321", size = 17195929, upload-time = "2025-10-22T03:47:36.24Z" }, - { url = "https://files.pythonhosted.org/packages/91/9d/a81aafd899b900101988ead7fb14974c8a58695338ab6a0f3d6b0100f30b/onnxruntime-1.23.2-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:218295a8acae83905f6f1aed8cacb8e3eb3bd7513a13fe4ba3b2664a19fc4a6b", size = 19157705, upload-time = "2025-10-22T03:46:40.415Z" }, - { url = "https://files.pythonhosted.org/packages/3c/35/4e40f2fba272a6698d62be2cd21ddc3675edfc1a4b9ddefcc4648f115315/onnxruntime-1.23.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76ff670550dc23e58ea9bc53b5149b99a44e63b34b524f7b8547469aaa0dcb8c", size = 15226915, upload-time = "2025-10-22T03:46:27.773Z" }, - { url = "https://files.pythonhosted.org/packages/ef/88/9cc25d2bafe6bc0d4d3c1db3ade98196d5b355c0b273e6a5dc09c5d5d0d5/onnxruntime-1.23.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f9b4ae77f8e3c9bee50c27bc1beede83f786fe1d52e99ac85aa8d65a01e9b77", size = 17382649, upload-time = "2025-10-22T03:47:02.782Z" }, - { url = "https://files.pythonhosted.org/packages/c0/b4/569d298f9fc4d286c11c45e85d9ffa9e877af12ace98af8cab52396e8f46/onnxruntime-1.23.2-cp312-cp312-win_amd64.whl", hash = "sha256:25de5214923ce941a3523739d34a520aac30f21e631de53bba9174dc9c004435", size = 13470528, upload-time = "2025-10-22T03:47:28.106Z" }, - { url = "https://files.pythonhosted.org/packages/3d/41/fba0cabccecefe4a1b5fc8020c44febb334637f133acefc7ec492029dd2c/onnxruntime-1.23.2-cp313-cp313-macosx_13_0_arm64.whl", hash = "sha256:2ff531ad8496281b4297f32b83b01cdd719617e2351ffe0dba5684fb283afa1f", size = 17196337, upload-time = "2025-10-22T03:46:35.168Z" }, - { url = "https://files.pythonhosted.org/packages/fe/f9/2d49ca491c6a986acce9f1d1d5fc2099108958cc1710c28e89a032c9cfe9/onnxruntime-1.23.2-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:162f4ca894ec3de1a6fd53589e511e06ecdc3ff646849b62a9da7489dee9ce95", size = 19157691, upload-time = "2025-10-22T03:46:43.518Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a1/428ee29c6eaf09a6f6be56f836213f104618fb35ac6cc586ff0f477263eb/onnxruntime-1.23.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45d127d6e1e9b99d1ebeae9bcd8f98617a812f53f46699eafeb976275744826b", size = 15226898, upload-time = "2025-10-22T03:46:30.039Z" }, - { url = "https://files.pythonhosted.org/packages/f2/2b/b57c8a2466a3126dbe0a792f56ad7290949b02f47b86216cd47d857e4b77/onnxruntime-1.23.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8bace4e0d46480fbeeb7bbe1ffe1f080e6663a42d1086ff95c1551f2d39e7872", size = 17382518, upload-time = "2025-10-22T03:47:05.407Z" }, - { url = "https://files.pythonhosted.org/packages/4a/93/aba75358133b3a941d736816dd392f687e7eab77215a6e429879080b76b6/onnxruntime-1.23.2-cp313-cp313-win_amd64.whl", hash = "sha256:1f9cc0a55349c584f083c1c076e611a7c35d5b867d5d6e6d6c823bf821978088", size = 13470276, upload-time = "2025-10-22T03:47:31.193Z" }, - { url = "https://files.pythonhosted.org/packages/7c/3d/6830fa61c69ca8e905f237001dbfc01689a4e4ab06147020a4518318881f/onnxruntime-1.23.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9d2385e774f46ac38f02b3a91a91e30263d41b2f1f4f26ae34805b2a9ddef466", size = 15229610, upload-time = "2025-10-22T03:46:32.239Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ca/862b1e7a639460f0ca25fd5b6135fb42cf9deea86d398a92e44dfda2279d/onnxruntime-1.23.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2b9233c4947907fd1818d0e581c049c41ccc39b2856cc942ff6d26317cee145", size = 17394184, upload-time = "2025-10-22T03:47:08.127Z" }, -] - -[[package]] -name = "openai" -version = "2.16.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/6c/e4c964fcf1d527fdf4739e7cc940c60075a4114d50d03871d5d5b1e13a88/openai-2.16.0.tar.gz", hash = "sha256:42eaa22ca0d8ded4367a77374104d7a2feafee5bd60a107c3c11b5243a11cd12", size = 629649, upload-time = "2026-01-27T23:28:02.579Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/16/83/0315bf2cfd75a2ce8a7e54188e9456c60cec6c0cf66728ed07bd9859ff26/openai-2.16.0-py3-none-any.whl", hash = "sha256:5f46643a8f42899a84e80c38838135d7038e7718333ce61396994f887b09a59b", size = 1068612, upload-time = "2026-01-27T23:28:00.356Z" }, -] - -[[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 = "openpyxl" -version = "3.1.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "et-xmlfile" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464, upload-time = "2024-06-28T14:03:44.161Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910, upload-time = "2024-06-28T14:03:41.161Z" }, -] - -[[package]] -name = "opentelemetry-api" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, -] - -[[package]] -name = "opentelemetry-exporter-otlp-proto-common" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-proto" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e9/9d/22d241b66f7bbde88a3bfa6847a351d2c46b84de23e71222c6aae25c7050/opentelemetry_exporter_otlp_proto_common-1.39.1.tar.gz", hash = "sha256:763370d4737a59741c89a67b50f9e39271639ee4afc999dadfe768541c027464", size = 20409, upload-time = "2025-12-11T13:32:40.885Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/02/ffc3e143d89a27ac21fd557365b98bd0653b98de8a101151d5805b5d4c33/opentelemetry_exporter_otlp_proto_common-1.39.1-py3-none-any.whl", hash = "sha256:08f8a5862d64cc3435105686d0216c1365dc5701f86844a8cd56597d0c764fde", size = 18366, upload-time = "2025-12-11T13:32:20.2Z" }, -] - -[[package]] -name = "opentelemetry-exporter-otlp-proto-grpc" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "googleapis-common-protos" }, - { name = "grpcio" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-otlp-proto-common" }, - { name = "opentelemetry-proto" }, - { name = "opentelemetry-sdk" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/53/48/b329fed2c610c2c32c9366d9dc597202c9d1e58e631c137ba15248d8850f/opentelemetry_exporter_otlp_proto_grpc-1.39.1.tar.gz", hash = "sha256:772eb1c9287485d625e4dbe9c879898e5253fea111d9181140f51291b5fec3ad", size = 24650, upload-time = "2025-12-11T13:32:41.429Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/a3/cc9b66575bd6597b98b886a2067eea2693408d2d5f39dad9ab7fc264f5f3/opentelemetry_exporter_otlp_proto_grpc-1.39.1-py3-none-any.whl", hash = "sha256:fa1c136a05c7e9b4c09f739469cbdb927ea20b34088ab1d959a849b5cc589c18", size = 19766, upload-time = "2025-12-11T13:32:21.027Z" }, -] - -[[package]] -name = "opentelemetry-exporter-prometheus" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-sdk" }, - { name = "prometheus-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/14/39/7dafa6fff210737267bed35a8855b6ac7399b9e582b8cf1f25f842517012/opentelemetry_exporter_prometheus-0.60b1.tar.gz", hash = "sha256:a4011b46906323f71724649d301b4dc188aaa068852e814f4df38cc76eac616b", size = 14976, upload-time = "2025-12-11T13:32:42.944Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/0d/4be6bf5477a3eb3d917d2f17d3c0b6720cd6cb97898444a61d43cc983f5c/opentelemetry_exporter_prometheus-0.60b1-py3-none-any.whl", hash = "sha256:49f59178de4f4590e3cef0b8b95cf6e071aae70e1f060566df5546fad773b8fd", size = 13019, upload-time = "2025-12-11T13:32:23.974Z" }, -] - -[[package]] -name = "opentelemetry-instrumentation" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "packaging" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/0f/7e6b713ac117c1f5e4e3300748af699b9902a2e5e34c9cf443dde25a01fa/opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a", size = 31706, upload-time = "2025-12-11T13:36:42.515Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096, upload-time = "2025-12-11T13:35:33.067Z" }, -] - -[[package]] -name = "opentelemetry-proto" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/1d/f25d76d8260c156c40c97c9ed4511ec0f9ce353f8108ca6e7561f82a06b2/opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8", size = 46152, upload-time = "2025-12-11T13:32:48.681Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/95/b40c96a7b5203005a0b03d8ce8cd212ff23f1793d5ba289c87a097571b18/opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007", size = 72535, upload-time = "2025-12-11T13:32:33.866Z" }, -] - -[[package]] -name = "opentelemetry-sdk" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, -] - -[[package]] -name = "orjson" -version = "3.11.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/45/b268004f745ede84e5798b48ee12b05129d19235d0e15267aa57dcdb400b/orjson-3.11.7.tar.gz", hash = "sha256:9b1a67243945819ce55d24a30b59d6a168e86220452d2c96f4d1f093e71c0c49", size = 6144992, upload-time = "2026-02-02T15:38:49.29Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/bf/76f4f1665f6983385938f0e2a5d7efa12a58171b8456c252f3bae8a4cf75/orjson-3.11.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bd03ea7606833655048dab1a00734a2875e3e86c276e1d772b2a02556f0d895f", size = 228545, upload-time = "2026-02-02T15:37:46.376Z" }, - { url = "https://files.pythonhosted.org/packages/79/53/6c72c002cb13b5a978a068add59b25a8bdf2800ac1c9c8ecdb26d6d97064/orjson-3.11.7-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:89e440ebc74ce8ab5c7bc4ce6757b4a6b1041becb127df818f6997b5c71aa60b", size = 125224, upload-time = "2026-02-02T15:37:47.697Z" }, - { url = "https://files.pythonhosted.org/packages/2c/83/10e48852865e5dd151bdfe652c06f7da484578ed02c5fca938e3632cb0b8/orjson-3.11.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ede977b5fe5ac91b1dffc0a517ca4542d2ec8a6a4ff7b2652d94f640796342a", size = 128154, upload-time = "2026-02-02T15:37:48.954Z" }, - { url = "https://files.pythonhosted.org/packages/6e/52/a66e22a2b9abaa374b4a081d410edab6d1e30024707b87eab7c734afe28d/orjson-3.11.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b7b1dae39230a393df353827c855a5f176271c23434cfd2db74e0e424e693e10", size = 123548, upload-time = "2026-02-02T15:37:50.187Z" }, - { url = "https://files.pythonhosted.org/packages/de/38/605d371417021359f4910c496f764c48ceb8997605f8c25bf1dfe58c0ebe/orjson-3.11.7-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed46f17096e28fb28d2975834836a639af7278aa87c84f68ab08fbe5b8bd75fa", size = 129000, upload-time = "2026-02-02T15:37:51.426Z" }, - { url = "https://files.pythonhosted.org/packages/44/98/af32e842b0ffd2335c89714d48ca4e3917b42f5d6ee5537832e069a4b3ac/orjson-3.11.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3726be79e36e526e3d9c1aceaadbfb4a04ee80a72ab47b3f3c17fefb9812e7b8", size = 141686, upload-time = "2026-02-02T15:37:52.607Z" }, - { url = "https://files.pythonhosted.org/packages/96/0b/fc793858dfa54be6feee940c1463370ece34b3c39c1ca0aa3845f5ba9892/orjson-3.11.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0724e265bc548af1dedebd9cb3d24b4e1c1e685a343be43e87ba922a5c5fff2f", size = 130812, upload-time = "2026-02-02T15:37:53.944Z" }, - { url = "https://files.pythonhosted.org/packages/dc/91/98a52415059db3f374757d0b7f0f16e3b5cd5976c90d1c2b56acaea039e6/orjson-3.11.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7745312efa9e11c17fbd3cb3097262d079da26930ae9ae7ba28fb738367cbad", size = 133440, upload-time = "2026-02-02T15:37:55.615Z" }, - { url = "https://files.pythonhosted.org/packages/dc/b6/cb540117bda61791f46381f8c26c8f93e802892830a6055748d3bb1925ab/orjson-3.11.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f904c24bdeabd4298f7a977ef14ca2a022ca921ed670b92ecd16ab6f3d01f867", size = 138386, upload-time = "2026-02-02T15:37:56.814Z" }, - { url = "https://files.pythonhosted.org/packages/63/1a/50a3201c334a7f17c231eee5f841342190723794e3b06293f26e7cf87d31/orjson-3.11.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b9fc4d0f81f394689e0814617aadc4f2ea0e8025f38c226cbf22d3b5ddbf025d", size = 408853, upload-time = "2026-02-02T15:37:58.291Z" }, - { url = "https://files.pythonhosted.org/packages/87/cd/8de1c67d0be44fdc22701e5989c0d015a2adf391498ad42c4dc589cd3013/orjson-3.11.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:849e38203e5be40b776ed2718e587faf204d184fc9a008ae441f9442320c0cab", size = 144130, upload-time = "2026-02-02T15:38:00.163Z" }, - { url = "https://files.pythonhosted.org/packages/0f/fe/d605d700c35dd55f51710d159fc54516a280923cd1b7e47508982fbb387d/orjson-3.11.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4682d1db3bcebd2b64757e0ddf9e87ae5f00d29d16c5cdf3a62f561d08cc3dd2", size = 134818, upload-time = "2026-02-02T15:38:01.507Z" }, - { url = "https://files.pythonhosted.org/packages/e4/e4/15ecc67edb3ddb3e2f46ae04475f2d294e8b60c1825fbe28a428b93b3fbd/orjson-3.11.7-cp312-cp312-win32.whl", hash = "sha256:f4f7c956b5215d949a1f65334cf9d7612dde38f20a95f2315deef167def91a6f", size = 127923, upload-time = "2026-02-02T15:38:02.75Z" }, - { url = "https://files.pythonhosted.org/packages/34/70/2e0855361f76198a3965273048c8e50a9695d88cd75811a5b46444895845/orjson-3.11.7-cp312-cp312-win_amd64.whl", hash = "sha256:bf742e149121dc5648ba0a08ea0871e87b660467ef168a3a5e53bc1fbd64bb74", size = 125007, upload-time = "2026-02-02T15:38:04.032Z" }, - { url = "https://files.pythonhosted.org/packages/68/40/c2051bd19fc467610fed469dc29e43ac65891571138f476834ca192bc290/orjson-3.11.7-cp312-cp312-win_arm64.whl", hash = "sha256:26c3b9132f783b7d7903bf1efb095fed8d4a3a85ec0d334ee8beff3d7a4749d5", size = 126089, upload-time = "2026-02-02T15:38:05.297Z" }, - { url = "https://files.pythonhosted.org/packages/89/25/6e0e52cac5aab51d7b6dcd257e855e1dec1c2060f6b28566c509b4665f62/orjson-3.11.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1d98b30cc1313d52d4af17d9c3d307b08389752ec5f2e5febdfada70b0f8c733", size = 228390, upload-time = "2026-02-02T15:38:06.8Z" }, - { url = "https://files.pythonhosted.org/packages/a5/29/a77f48d2fc8a05bbc529e5ff481fb43d914f9e383ea2469d4f3d51df3d00/orjson-3.11.7-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:d897e81f8d0cbd2abb82226d1860ad2e1ab3ff16d7b08c96ca00df9d45409ef4", size = 125189, upload-time = "2026-02-02T15:38:08.181Z" }, - { url = "https://files.pythonhosted.org/packages/89/25/0a16e0729a0e6a1504f9d1a13cdd365f030068aab64cec6958396b9969d7/orjson-3.11.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:814be4b49b228cfc0b3c565acf642dd7d13538f966e3ccde61f4f55be3e20785", size = 128106, upload-time = "2026-02-02T15:38:09.41Z" }, - { url = "https://files.pythonhosted.org/packages/66/da/a2e505469d60666a05ab373f1a6322eb671cb2ba3a0ccfc7d4bc97196787/orjson-3.11.7-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d06e5c5fed5caedd2e540d62e5b1c25e8c82431b9e577c33537e5fa4aa909539", size = 123363, upload-time = "2026-02-02T15:38:10.73Z" }, - { url = "https://files.pythonhosted.org/packages/23/bf/ed73f88396ea35c71b38961734ea4a4746f7ca0768bf28fd551d37e48dd0/orjson-3.11.7-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31c80ce534ac4ea3739c5ee751270646cbc46e45aea7576a38ffec040b4029a1", size = 129007, upload-time = "2026-02-02T15:38:12.138Z" }, - { url = "https://files.pythonhosted.org/packages/73/3c/b05d80716f0225fc9008fbf8ab22841dcc268a626aa550561743714ce3bf/orjson-3.11.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f50979824bde13d32b4320eedd513431c921102796d86be3eee0b58e58a3ecd1", size = 141667, upload-time = "2026-02-02T15:38:13.398Z" }, - { url = "https://files.pythonhosted.org/packages/61/e8/0be9b0addd9bf86abfc938e97441dcd0375d494594b1c8ad10fe57479617/orjson-3.11.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e54f3808e2b6b945078c41aa8d9b5834b28c50843846e97807e5adb75fa9705", size = 130832, upload-time = "2026-02-02T15:38:14.698Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ec/c68e3b9021a31d9ec15a94931db1410136af862955854ed5dd7e7e4f5bff/orjson-3.11.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12b80df61aab7b98b490fe9e4879925ba666fccdfcd175252ce4d9035865ace", size = 133373, upload-time = "2026-02-02T15:38:16.109Z" }, - { url = "https://files.pythonhosted.org/packages/d2/45/f3466739aaafa570cc8e77c6dbb853c48bf56e3b43738020e2661e08b0ac/orjson-3.11.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:996b65230271f1a97026fd0e6a753f51fbc0c335d2ad0c6201f711b0da32693b", size = 138307, upload-time = "2026-02-02T15:38:17.453Z" }, - { url = "https://files.pythonhosted.org/packages/e1/84/9f7f02288da1ffb31405c1be07657afd1eecbcb4b64ee2817b6fe0f785fa/orjson-3.11.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ab49d4b2a6a1d415ddb9f37a21e02e0d5dbfe10b7870b21bf779fc21e9156157", size = 408695, upload-time = "2026-02-02T15:38:18.831Z" }, - { url = "https://files.pythonhosted.org/packages/18/07/9dd2f0c0104f1a0295ffbe912bc8d63307a539b900dd9e2c48ef7810d971/orjson-3.11.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:390a1dce0c055ddf8adb6aa94a73b45a4a7d7177b5c584b8d1c1947f2ba60fb3", size = 144099, upload-time = "2026-02-02T15:38:20.28Z" }, - { url = "https://files.pythonhosted.org/packages/a5/66/857a8e4a3292e1f7b1b202883bcdeb43a91566cf59a93f97c53b44bd6801/orjson-3.11.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1eb80451a9c351a71dfaf5b7ccc13ad065405217726b59fdbeadbcc544f9d223", size = 134806, upload-time = "2026-02-02T15:38:22.186Z" }, - { url = "https://files.pythonhosted.org/packages/0a/5b/6ebcf3defc1aab3a338ca777214966851e92efb1f30dc7fc8285216e6d1b/orjson-3.11.7-cp313-cp313-win32.whl", hash = "sha256:7477aa6a6ec6139c5cb1cc7b214643592169a5494d200397c7fc95d740d5fcf3", size = 127914, upload-time = "2026-02-02T15:38:23.511Z" }, - { url = "https://files.pythonhosted.org/packages/00/04/c6f72daca5092e3117840a1b1e88dfc809cc1470cf0734890d0366b684a1/orjson-3.11.7-cp313-cp313-win_amd64.whl", hash = "sha256:b9f95dcdea9d4f805daa9ddf02617a89e484c6985fa03055459f90e87d7a0757", size = 124986, upload-time = "2026-02-02T15:38:24.836Z" }, - { url = "https://files.pythonhosted.org/packages/03/ba/077a0f6f1085d6b806937246860fafbd5b17f3919c70ee3f3d8d9c713f38/orjson-3.11.7-cp313-cp313-win_arm64.whl", hash = "sha256:800988273a014a0541483dc81021247d7eacb0c845a9d1a34a422bc718f41539", size = 126045, upload-time = "2026-02-02T15:38:26.216Z" }, - { url = "https://files.pythonhosted.org/packages/e9/1e/745565dca749813db9a093c5ebc4bac1a9475c64d54b95654336ac3ed961/orjson-3.11.7-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:de0a37f21d0d364954ad5de1970491d7fbd0fb1ef7417d4d56a36dc01ba0c0a0", size = 228391, upload-time = "2026-02-02T15:38:27.757Z" }, - { url = "https://files.pythonhosted.org/packages/46/19/e40f6225da4d3aa0c8dc6e5219c5e87c2063a560fe0d72a88deb59776794/orjson-3.11.7-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:c2428d358d85e8da9d37cba18b8c4047c55222007a84f97156a5b22028dfbfc0", size = 125188, upload-time = "2026-02-02T15:38:29.241Z" }, - { url = "https://files.pythonhosted.org/packages/9d/7e/c4de2babef2c0817fd1f048fd176aa48c37bec8aef53d2fa932983032cce/orjson-3.11.7-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c4bc6c6ac52cdaa267552544c73e486fecbd710b7ac09bc024d5a78555a22f6", size = 128097, upload-time = "2026-02-02T15:38:30.618Z" }, - { url = "https://files.pythonhosted.org/packages/eb/74/233d360632bafd2197f217eee7fb9c9d0229eac0c18128aee5b35b0014fe/orjson-3.11.7-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd0d68edd7dfca1b2eca9361a44ac9f24b078de3481003159929a0573f21a6bf", size = 123364, upload-time = "2026-02-02T15:38:32.363Z" }, - { url = "https://files.pythonhosted.org/packages/79/51/af79504981dd31efe20a9e360eb49c15f06df2b40e7f25a0a52d9ae888e8/orjson-3.11.7-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:623ad1b9548ef63886319c16fa317848e465a21513b31a6ad7b57443c3e0dcf5", size = 129076, upload-time = "2026-02-02T15:38:33.68Z" }, - { url = "https://files.pythonhosted.org/packages/67/e2/da898eb68b72304f8de05ca6715870d09d603ee98d30a27e8a9629abc64b/orjson-3.11.7-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6e776b998ac37c0396093d10290e60283f59cfe0fc3fccbd0ccc4bd04dd19892", size = 141705, upload-time = "2026-02-02T15:38:34.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/89/15364d92acb3d903b029e28d834edb8780c2b97404cbf7929aa6b9abdb24/orjson-3.11.7-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:652c6c3af76716f4a9c290371ba2e390ede06f6603edb277b481daf37f6f464e", size = 130855, upload-time = "2026-02-02T15:38:36.379Z" }, - { url = "https://files.pythonhosted.org/packages/c2/8b/ecdad52d0b38d4b8f514be603e69ccd5eacf4e7241f972e37e79792212ec/orjson-3.11.7-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a56df3239294ea5964adf074c54bcc4f0ccd21636049a2cf3ca9cf03b5d03cf1", size = 133386, upload-time = "2026-02-02T15:38:37.704Z" }, - { url = "https://files.pythonhosted.org/packages/b9/0e/45e1dcf10e17d0924b7c9162f87ec7b4ca79e28a0548acf6a71788d3e108/orjson-3.11.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bda117c4148e81f746655d5a3239ae9bd00cb7bc3ca178b5fc5a5997e9744183", size = 138295, upload-time = "2026-02-02T15:38:39.096Z" }, - { url = "https://files.pythonhosted.org/packages/63/d7/4d2e8b03561257af0450f2845b91fbd111d7e526ccdf737267108075e0ba/orjson-3.11.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:23d6c20517a97a9daf1d48b580fcdc6f0516c6f4b5038823426033690b4d2650", size = 408720, upload-time = "2026-02-02T15:38:40.634Z" }, - { url = "https://files.pythonhosted.org/packages/78/cf/d45343518282108b29c12a65892445fc51f9319dc3c552ceb51bb5905ed2/orjson-3.11.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8ff206156006da5b847c9304b6308a01e8cdbc8cce824e2779a5ba71c3def141", size = 144152, upload-time = "2026-02-02T15:38:42.262Z" }, - { url = "https://files.pythonhosted.org/packages/a9/3a/d6001f51a7275aacd342e77b735c71fa04125a3f93c36fee4526bc8c654e/orjson-3.11.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:962d046ee1765f74a1da723f4b33e3b228fe3a48bd307acce5021dfefe0e29b2", size = 134814, upload-time = "2026-02-02T15:38:43.627Z" }, - { url = "https://files.pythonhosted.org/packages/1d/d3/f19b47ce16820cc2c480f7f1723e17f6d411b3a295c60c8ad3aa9ff1c96a/orjson-3.11.7-cp314-cp314-win32.whl", hash = "sha256:89e13dd3f89f1c38a9c9eba5fbf7cdc2d1feca82f5f290864b4b7a6aac704576", size = 127997, upload-time = "2026-02-02T15:38:45.06Z" }, - { url = "https://files.pythonhosted.org/packages/12/df/172771902943af54bf661a8d102bdf2e7f932127968080632bda6054b62c/orjson-3.11.7-cp314-cp314-win_amd64.whl", hash = "sha256:845c3e0d8ded9c9271cd79596b9b552448b885b97110f628fb687aee2eed11c1", size = 124985, upload-time = "2026-02-02T15:38:46.388Z" }, - { url = "https://files.pythonhosted.org/packages/6f/1c/f2a8d8a1b17514660a614ce5f7aac74b934e69f5abc2700cc7ced882a009/orjson-3.11.7-cp314-cp314-win_arm64.whl", hash = "sha256:4a2e9c5be347b937a2e0203866f12bba36082e89b402ddb9e927d5822e43088d", size = 126038, upload-time = "2026-02-02T15:38:47.703Z" }, -] - -[[package]] -name = "overrides" -version = "7.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812, upload-time = "2024-01-27T21:01:33.423Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832, upload-time = "2024-01-27T21:01:31.393Z" }, -] - -[[package]] -name = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[package]] -name = "pandas" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "python-dateutil" }, - { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/de/da/b1dc0481ab8d55d0f46e343cfe67d4551a0e14fcee52bd38ca1bd73258d8/pandas-3.0.0.tar.gz", hash = "sha256:0facf7e87d38f721f0af46fe70d97373a37701b1c09f7ed7aeeb292ade5c050f", size = 4633005, upload-time = "2026-01-21T15:52:04.726Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/38/db33686f4b5fa64d7af40d96361f6a4615b8c6c8f1b3d334eee46ae6160e/pandas-3.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9803b31f5039b3c3b10cc858c5e40054adb4b29b4d81cb2fd789f4121c8efbcd", size = 10334013, upload-time = "2026-01-21T15:50:34.771Z" }, - { url = "https://files.pythonhosted.org/packages/a5/7b/9254310594e9774906bacdd4e732415e1f86ab7dbb4b377ef9ede58cd8ec/pandas-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14c2a4099cd38a1d18ff108168ea417909b2dea3bd1ebff2ccf28ddb6a74d740", size = 9874154, upload-time = "2026-01-21T15:50:36.67Z" }, - { url = "https://files.pythonhosted.org/packages/63/d4/726c5a67a13bc66643e66d2e9ff115cead482a44fc56991d0c4014f15aaf/pandas-3.0.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d257699b9a9960e6125686098d5714ac59d05222bef7a5e6af7a7fd87c650801", size = 10384433, upload-time = "2026-01-21T15:50:39.132Z" }, - { url = "https://files.pythonhosted.org/packages/bf/2e/9211f09bedb04f9832122942de8b051804b31a39cfbad199a819bb88d9f3/pandas-3.0.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:69780c98f286076dcafca38d8b8eee1676adf220199c0a39f0ecbf976b68151a", size = 10864519, upload-time = "2026-01-21T15:50:41.043Z" }, - { url = "https://files.pythonhosted.org/packages/00/8d/50858522cdc46ac88b9afdc3015e298959a70a08cd21e008a44e9520180c/pandas-3.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4a66384f017240f3858a4c8a7cf21b0591c3ac885cddb7758a589f0f71e87ebb", size = 11394124, upload-time = "2026-01-21T15:50:43.377Z" }, - { url = "https://files.pythonhosted.org/packages/86/3f/83b2577db02503cd93d8e95b0f794ad9d4be0ba7cb6c8bcdcac964a34a42/pandas-3.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be8c515c9bc33989d97b89db66ea0cececb0f6e3c2a87fcc8b69443a6923e95f", size = 11920444, upload-time = "2026-01-21T15:50:45.932Z" }, - { url = "https://files.pythonhosted.org/packages/64/2d/4f8a2f192ed12c90a0aab47f5557ece0e56b0370c49de9454a09de7381b2/pandas-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:a453aad8c4f4e9f166436994a33884442ea62aa8b27d007311e87521b97246e1", size = 9730970, upload-time = "2026-01-21T15:50:47.962Z" }, - { url = "https://files.pythonhosted.org/packages/d4/64/ff571be435cf1e643ca98d0945d76732c0b4e9c37191a89c8550b105eed1/pandas-3.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:da768007b5a33057f6d9053563d6b74dd6d029c337d93c6d0d22a763a5c2ecc0", size = 9041950, upload-time = "2026-01-21T15:50:50.422Z" }, - { url = "https://files.pythonhosted.org/packages/6f/fa/7f0ac4ca8877c57537aaff2a842f8760e630d8e824b730eb2e859ffe96ca/pandas-3.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b78d646249b9a2bc191040988c7bb524c92fa8534fb0898a0741d7e6f2ffafa6", size = 10307129, upload-time = "2026-01-21T15:50:52.877Z" }, - { url = "https://files.pythonhosted.org/packages/6f/11/28a221815dcea4c0c9414dfc845e34a84a6a7dabc6da3194498ed5ba4361/pandas-3.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bc9cba7b355cb4162442a88ce495e01cb605f17ac1e27d6596ac963504e0305f", size = 9850201, upload-time = "2026-01-21T15:50:54.807Z" }, - { url = "https://files.pythonhosted.org/packages/ba/da/53bbc8c5363b7e5bd10f9ae59ab250fc7a382ea6ba08e4d06d8694370354/pandas-3.0.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c9a1a149aed3b6c9bf246033ff91e1b02d529546c5d6fb6b74a28fea0cf4c70", size = 10354031, upload-time = "2026-01-21T15:50:57.463Z" }, - { url = "https://files.pythonhosted.org/packages/f7/a3/51e02ebc2a14974170d51e2410dfdab58870ea9bcd37cda15bd553d24dc4/pandas-3.0.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95683af6175d884ee89471842acfca29172a85031fccdabc35e50c0984470a0e", size = 10861165, upload-time = "2026-01-21T15:50:59.32Z" }, - { url = "https://files.pythonhosted.org/packages/a5/fe/05a51e3cac11d161472b8297bd41723ea98013384dd6d76d115ce3482f9b/pandas-3.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1fbbb5a7288719e36b76b4f18d46ede46e7f916b6c8d9915b756b0a6c3f792b3", size = 11359359, upload-time = "2026-01-21T15:51:02.014Z" }, - { url = "https://files.pythonhosted.org/packages/ee/56/ba620583225f9b85a4d3e69c01df3e3870659cc525f67929b60e9f21dcd1/pandas-3.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e8b9808590fa364416b49b2a35c1f4cf2785a6c156935879e57f826df22038e", size = 11912907, upload-time = "2026-01-21T15:51:05.175Z" }, - { url = "https://files.pythonhosted.org/packages/c9/8c/c6638d9f67e45e07656b3826405c5cc5f57f6fd07c8b2572ade328c86e22/pandas-3.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:98212a38a709feb90ae658cb6227ea3657c22ba8157d4b8f913cd4c950de5e7e", size = 9732138, upload-time = "2026-01-21T15:51:07.569Z" }, - { url = "https://files.pythonhosted.org/packages/7b/bf/bd1335c3bf1770b6d8fed2799993b11c4971af93bb1b729b9ebbc02ca2ec/pandas-3.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:177d9df10b3f43b70307a149d7ec49a1229a653f907aa60a48f1877d0e6be3be", size = 9033568, upload-time = "2026-01-21T15:51:09.484Z" }, - { url = "https://files.pythonhosted.org/packages/8e/c6/f5e2171914d5e29b9171d495344097d54e3ffe41d2d85d8115baba4dc483/pandas-3.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2713810ad3806767b89ad3b7b69ba153e1c6ff6d9c20f9c2140379b2a98b6c98", size = 10741936, upload-time = "2026-01-21T15:51:11.693Z" }, - { url = "https://files.pythonhosted.org/packages/51/88/9a0164f99510a1acb9f548691f022c756c2314aad0d8330a24616c14c462/pandas-3.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:15d59f885ee5011daf8335dff47dcb8a912a27b4ad7826dc6cbe809fd145d327", size = 10393884, upload-time = "2026-01-21T15:51:14.197Z" }, - { url = "https://files.pythonhosted.org/packages/e0/53/b34d78084d88d8ae2b848591229da8826d1e65aacf00b3abe34023467648/pandas-3.0.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24e6547fb64d2c92665dd2adbfa4e85fa4fd70a9c070e7cfb03b629a0bbab5eb", size = 10310740, upload-time = "2026-01-21T15:51:16.093Z" }, - { url = "https://files.pythonhosted.org/packages/5b/d3/bee792e7c3d6930b74468d990604325701412e55d7aaf47460a22311d1a5/pandas-3.0.0-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:48ee04b90e2505c693d3f8e8f524dab8cb8aaf7ddcab52c92afa535e717c4812", size = 10700014, upload-time = "2026-01-21T15:51:18.818Z" }, - { url = "https://files.pythonhosted.org/packages/55/db/2570bc40fb13aaed1cbc3fbd725c3a60ee162477982123c3adc8971e7ac1/pandas-3.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66f72fb172959af42a459e27a8d8d2c7e311ff4c1f7db6deb3b643dbc382ae08", size = 11323737, upload-time = "2026-01-21T15:51:20.784Z" }, - { url = "https://files.pythonhosted.org/packages/bc/2e/297ac7f21c8181b62a4cccebad0a70caf679adf3ae5e83cb676194c8acc3/pandas-3.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4a4a400ca18230976724a5066f20878af785f36c6756e498e94c2a5e5d57779c", size = 11771558, upload-time = "2026-01-21T15:51:22.977Z" }, - { url = "https://files.pythonhosted.org/packages/0a/46/e1c6876d71c14332be70239acce9ad435975a80541086e5ffba2f249bcf6/pandas-3.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:940eebffe55528074341a5a36515f3e4c5e25e958ebbc764c9502cfc35ba3faa", size = 10473771, upload-time = "2026-01-21T15:51:25.285Z" }, - { url = "https://files.pythonhosted.org/packages/c0/db/0270ad9d13c344b7a36fa77f5f8344a46501abf413803e885d22864d10bf/pandas-3.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:597c08fb9fef0edf1e4fa2f9828dd27f3d78f9b8c9b4a748d435ffc55732310b", size = 10312075, upload-time = "2026-01-21T15:51:28.5Z" }, - { url = "https://files.pythonhosted.org/packages/09/9f/c176f5e9717f7c91becfe0f55a52ae445d3f7326b4a2cf355978c51b7913/pandas-3.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:447b2d68ac5edcbf94655fe909113a6dba6ef09ad7f9f60c80477825b6c489fe", size = 9900213, upload-time = "2026-01-21T15:51:30.955Z" }, - { url = "https://files.pythonhosted.org/packages/d9/e7/63ad4cc10b257b143e0a5ebb04304ad806b4e1a61c5da25f55896d2ca0f4/pandas-3.0.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:debb95c77ff3ed3ba0d9aa20c3a2f19165cc7956362f9873fce1ba0a53819d70", size = 10428768, upload-time = "2026-01-21T15:51:33.018Z" }, - { url = "https://files.pythonhosted.org/packages/9e/0e/4e4c2d8210f20149fd2248ef3fff26623604922bd564d915f935a06dd63d/pandas-3.0.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fedabf175e7cd82b69b74c30adbaa616de301291a5231138d7242596fc296a8d", size = 10882954, upload-time = "2026-01-21T15:51:35.287Z" }, - { url = "https://files.pythonhosted.org/packages/c6/60/c9de8ac906ba1f4d2250f8a951abe5135b404227a55858a75ad26f84db47/pandas-3.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:412d1a89aab46889f3033a386912efcdfa0f1131c5705ff5b668dda88305e986", size = 11430293, upload-time = "2026-01-21T15:51:37.57Z" }, - { url = "https://files.pythonhosted.org/packages/a1/69/806e6637c70920e5787a6d6896fd707f8134c2c55cd761e7249a97b7dc5a/pandas-3.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e979d22316f9350c516479dd3a92252be2937a9531ed3a26ec324198a99cdd49", size = 11952452, upload-time = "2026-01-21T15:51:39.618Z" }, - { url = "https://files.pythonhosted.org/packages/cb/de/918621e46af55164c400ab0ef389c9d969ab85a43d59ad1207d4ddbe30a5/pandas-3.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:083b11415b9970b6e7888800c43c82e81a06cd6b06755d84804444f0007d6bb7", size = 9851081, upload-time = "2026-01-21T15:51:41.758Z" }, - { url = "https://files.pythonhosted.org/packages/91/a1/3562a18dd0bd8c73344bfa26ff90c53c72f827df119d6d6b1dacc84d13e3/pandas-3.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:5db1e62cb99e739fa78a28047e861b256d17f88463c76b8dafc7c1338086dca8", size = 9174610, upload-time = "2026-01-21T15:51:44.312Z" }, - { url = "https://files.pythonhosted.org/packages/ce/26/430d91257eaf366f1737d7a1c158677caaf6267f338ec74e3a1ec444111c/pandas-3.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:697b8f7d346c68274b1b93a170a70974cdc7d7354429894d5927c1effdcccd73", size = 10761999, upload-time = "2026-01-21T15:51:46.899Z" }, - { url = "https://files.pythonhosted.org/packages/ec/1a/954eb47736c2b7f7fe6a9d56b0cb6987773c00faa3c6451a43db4beb3254/pandas-3.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8cb3120f0d9467ed95e77f67a75e030b67545bcfa08964e349252d674171def2", size = 10410279, upload-time = "2026-01-21T15:51:48.89Z" }, - { url = "https://files.pythonhosted.org/packages/20/fc/b96f3a5a28b250cd1b366eb0108df2501c0f38314a00847242abab71bb3a/pandas-3.0.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33fd3e6baa72899746b820c31e4b9688c8e1b7864d7aec2de7ab5035c285277a", size = 10330198, upload-time = "2026-01-21T15:51:51.015Z" }, - { url = "https://files.pythonhosted.org/packages/90/b3/d0e2952f103b4fbef1ef22d0c2e314e74fc9064b51cee30890b5e3286ee6/pandas-3.0.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8942e333dc67ceda1095227ad0febb05a3b36535e520154085db632c40ad084", size = 10728513, upload-time = "2026-01-21T15:51:53.387Z" }, - { url = "https://files.pythonhosted.org/packages/76/81/832894f286df828993dc5fd61c63b231b0fb73377e99f6c6c369174cf97e/pandas-3.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:783ac35c4d0fe0effdb0d67161859078618b1b6587a1af15928137525217a721", size = 11345550, upload-time = "2026-01-21T15:51:55.329Z" }, - { url = "https://files.pythonhosted.org/packages/34/a0/ed160a00fb4f37d806406bc0a79a8b62fe67f29d00950f8d16203ff3409b/pandas-3.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:125eb901e233f155b268bbef9abd9afb5819db74f0e677e89a61b246228c71ac", size = 11799386, upload-time = "2026-01-21T15:51:57.457Z" }, - { url = "https://files.pythonhosted.org/packages/36/c8/2ac00d7255252c5e3cf61b35ca92ca25704b0188f7454ca4aec08a33cece/pandas-3.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b86d113b6c109df3ce0ad5abbc259fe86a1bd4adfd4a31a89da42f84f65509bb", size = 10873041, upload-time = "2026-01-21T15:52:00.034Z" }, - { url = "https://files.pythonhosted.org/packages/e6/3f/a80ac00acbc6b35166b42850e98a4f466e2c0d9c64054161ba9620f95680/pandas-3.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:1c39eab3ad38f2d7a249095f0a3d8f8c22cc0f847e98ccf5bbe732b272e2d9fa", size = 9441003, upload-time = "2026-01-21T15:52:02.281Z" }, -] - -[[package]] -name = "passlib" -version = "1.7.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/06/9da9ee59a67fae7761aab3ccc84fa4f3f33f125b370f1ccdb915bf967c11/passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04", size = 689844, upload-time = "2020-10-08T19:00:52.121Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/a4/ab6b7589382ca3df236e03faa71deac88cae040af60c071a78d254a62172/passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1", size = 525554, upload-time = "2020-10-08T19:00:49.856Z" }, -] - -[package.optional-dependencies] -bcrypt = [ - { name = "bcrypt" }, -] - -[[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 = "1.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, -] - -[[package]] -name = "pathvalidate" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262, upload-time = "2025-06-15T09:07:20.736Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305, upload-time = "2025-06-15T09:07:19.117Z" }, -] - -[[package]] -name = "pdfminer-six" -version = "20260107" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "charset-normalizer" }, - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/34/a4/5cec1112009f0439a5ca6afa8ace321f0ab2f48da3255b7a1c8953014670/pdfminer_six-20260107.tar.gz", hash = "sha256:96bfd431e3577a55a0efd25676968ca4ce8fd5b53f14565f85716ff363889602", size = 8512094, upload-time = "2026-01-07T13:29:12.937Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/8b/28c4eaec9d6b036a52cb44720408f26b1a143ca9bce76cc19e8f5de00ab4/pdfminer_six-20260107-py3-none-any.whl", hash = "sha256:366585ba97e80dffa8f00cebe303d2f381884d8637af4ce422f1df3ef38111a9", size = 6592252, upload-time = "2026-01-07T13:29:10.742Z" }, -] - -[[package]] -name = "pillow" -version = "11.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800, upload-time = "2025-07-01T09:14:17.648Z" }, - { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296, upload-time = "2025-07-01T09:14:19.828Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726, upload-time = "2025-07-03T13:10:04.448Z" }, - { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652, upload-time = "2025-07-03T13:10:10.391Z" }, - { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787, upload-time = "2025-07-01T09:14:21.63Z" }, - { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236, upload-time = "2025-07-01T09:14:23.321Z" }, - { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950, upload-time = "2025-07-01T09:14:25.237Z" }, - { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", size = 6723358, upload-time = "2025-07-01T09:14:27.053Z" }, - { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", size = 6275079, upload-time = "2025-07-01T09:14:30.104Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", size = 6986324, upload-time = "2025-07-01T09:14:31.899Z" }, - { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", size = 2423067, upload-time = "2025-07-01T09:14:33.709Z" }, - { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328, upload-time = "2025-07-01T09:14:35.276Z" }, - { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652, upload-time = "2025-07-01T09:14:37.203Z" }, - { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443, upload-time = "2025-07-01T09:14:39.344Z" }, - { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474, upload-time = "2025-07-01T09:14:41.843Z" }, - { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038, upload-time = "2025-07-01T09:14:44.008Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407, upload-time = "2025-07-03T13:10:15.628Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094, upload-time = "2025-07-03T13:10:21.857Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503, upload-time = "2025-07-01T09:14:45.698Z" }, - { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574, upload-time = "2025-07-01T09:14:47.415Z" }, - { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060, upload-time = "2025-07-01T09:14:49.636Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407, upload-time = "2025-07-01T09:14:51.962Z" }, - { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841, upload-time = "2025-07-01T09:14:54.142Z" }, - { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450, upload-time = "2025-07-01T09:14:56.436Z" }, - { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055, upload-time = "2025-07-01T09:14:58.072Z" }, - { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110, upload-time = "2025-07-01T09:14:59.79Z" }, - { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547, upload-time = "2025-07-01T09:15:01.648Z" }, - { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554, upload-time = "2025-07-03T13:10:27.018Z" }, - { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132, upload-time = "2025-07-03T13:10:33.01Z" }, - { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001, upload-time = "2025-07-01T09:15:03.365Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814, upload-time = "2025-07-01T09:15:05.655Z" }, - { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124, upload-time = "2025-07-01T09:15:07.358Z" }, - { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186, upload-time = "2025-07-01T09:15:09.317Z" }, - { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546, upload-time = "2025-07-01T09:15:11.311Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102, upload-time = "2025-07-01T09:15:13.164Z" }, - { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" }, - { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" }, - { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" }, - { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" }, - { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" }, - { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" }, - { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" }, - { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" }, - { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" }, - { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" }, - { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" }, - { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" }, - { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" }, - { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" }, - { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" }, - { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" }, - { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" }, - { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" }, - { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, -] - -[[package]] -name = "polars" -version = "1.37.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "polars-runtime-32" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/84/ae/dfebf31b9988c20998140b54d5b521f64ce08879f2c13d9b4d44d7c87e32/polars-1.37.1.tar.gz", hash = "sha256:0309e2a4633e712513401964b4d95452f124ceabf7aec6db50affb9ced4a274e", size = 715572, upload-time = "2026-01-12T23:27:03.267Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/75/ec73e38812bca7c2240aff481b9ddff20d1ad2f10dee4b3353f5eeaacdab/polars-1.37.1-py3-none-any.whl", hash = "sha256:377fed8939a2f1223c1563cfabdc7b4a3d6ff846efa1f2ddeb8644fafd9b1aff", size = 805749, upload-time = "2026-01-12T23:25:48.595Z" }, -] - -[[package]] -name = "polars-runtime-32" -version = "1.37.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/40/0b/addabe5e8d28a5a4c9887a08907be7ddc3fce892dc38f37d14b055438a57/polars_runtime_32-1.37.1.tar.gz", hash = "sha256:68779d4a691da20a5eb767d74165a8f80a2bdfbde4b54acf59af43f7fa028d8f", size = 2818945, upload-time = "2026-01-12T23:27:04.653Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/a2/e828ea9f845796de02d923edb790e408ca0b560cd68dbd74bb99a1b3c461/polars_runtime_32-1.37.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0b8d4d73ea9977d3731927740e59d814647c5198bdbe359bcf6a8bfce2e79771", size = 43499912, upload-time = "2026-01-12T23:25:51.182Z" }, - { url = "https://files.pythonhosted.org/packages/7e/46/81b71b7aa9e3703ee6e4ef1f69a87e40f58ea7c99212bf49a95071e99c8c/polars_runtime_32-1.37.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:c682bf83f5f352e5e02f5c16c652c48ca40442f07b236f30662b22217320ce76", size = 39695707, upload-time = "2026-01-12T23:25:54.289Z" }, - { url = "https://files.pythonhosted.org/packages/81/2e/20009d1fde7ee919e24040f5c87cb9d0e4f8e3f109b74ba06bc10c02459c/polars_runtime_32-1.37.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc82b5bbe70ca1a4b764eed1419f6336752d6ba9fc1245388d7f8b12438afa2c", size = 41467034, upload-time = "2026-01-12T23:25:56.925Z" }, - { url = "https://files.pythonhosted.org/packages/eb/21/9b55bea940524324625b1e8fd96233290303eb1bf2c23b54573487bbbc25/polars_runtime_32-1.37.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8362d11ac5193b994c7e9048ffe22ccfb976699cfbf6e128ce0302e06728894", size = 45142711, upload-time = "2026-01-12T23:26:00.817Z" }, - { url = "https://files.pythonhosted.org/packages/8c/25/c5f64461aeccdac6834a89f826d051ccd3b4ce204075e562c87a06ed2619/polars_runtime_32-1.37.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:04f5d5a2f013dca7391b7d8e7672fa6d37573a87f1d45d3dd5f0d9b5565a4b0f", size = 41638564, upload-time = "2026-01-12T23:26:04.186Z" }, - { url = "https://files.pythonhosted.org/packages/35/af/509d3cf6c45e764ccf856beaae26fc34352f16f10f94a7839b1042920a73/polars_runtime_32-1.37.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fbfde7c0ca8209eeaed546e4a32cca1319189aa61c5f0f9a2b4494262bd0c689", size = 44721136, upload-time = "2026-01-12T23:26:07.088Z" }, - { url = "https://files.pythonhosted.org/packages/af/d1/5c0a83a625f72beef59394bebc57d12637997632a4f9d3ab2ffc2cc62bbf/polars_runtime_32-1.37.1-cp310-abi3-win_amd64.whl", hash = "sha256:da3d3642ae944e18dd17109d2a3036cb94ce50e5495c5023c77b1599d4c861bc", size = 44948288, upload-time = "2026-01-12T23:26:10.214Z" }, - { url = "https://files.pythonhosted.org/packages/10/f3/061bb702465904b6502f7c9081daee34b09ccbaa4f8c94cf43a2a3b6dd6f/polars_runtime_32-1.37.1-cp310-abi3-win_arm64.whl", hash = "sha256:55f2c4847a8d2e267612f564de7b753a4bde3902eaabe7b436a0a4abf75949a0", size = 41001914, upload-time = "2026-01-12T23:26:12.997Z" }, -] - -[[package]] -name = "portalocker" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pywin32", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5e/77/65b857a69ed876e1951e88aaba60f5ce6120c33703f7cb61a3c894b8c1b6/portalocker-3.2.0.tar.gz", hash = "sha256:1f3002956a54a8c3730586c5c77bf18fae4149e07eaf1c29fc3faf4d5a3f89ac", size = 95644, upload-time = "2025-06-14T13:20:40.03Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/a6/38c8e2f318bf67d338f4d629e93b0b4b9af331f455f0390ea8ce4a099b26/portalocker-3.2.0-py3-none-any.whl", hash = "sha256:3cdc5f565312224bc570c49337bd21428bba0ef363bbcf58b9ef4a9f11779968", size = 22424, upload-time = "2025-06-14T13:20:38.083Z" }, -] - -[[package]] -name = "posthog" -version = "5.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "backoff" }, - { name = "distro" }, - { name = "python-dateutil" }, - { name = "requests" }, - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/48/20/60ae67bb9d82f00427946218d49e2e7e80fb41c15dc5019482289ec9ce8d/posthog-5.4.0.tar.gz", hash = "sha256:701669261b8d07cdde0276e5bc096b87f9e200e3b9589c5ebff14df658c5893c", size = 88076, upload-time = "2025-06-20T23:19:23.485Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/98/e480cab9a08d1c09b1c59a93dade92c1bb7544826684ff2acbfd10fcfbd4/posthog-5.4.0-py3-none-any.whl", hash = "sha256:284dfa302f64353484420b52d4ad81ff5c2c2d1d607c4e2db602ac72761831bd", size = 105364, upload-time = "2025-06-20T23:19:22.001Z" }, -] - -[[package]] -name = "prometheus-client" -version = "0.24.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616, upload-time = "2026-01-14T15:26:26.965Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057, upload-time = "2026-01-14T15:26:24.42Z" }, -] - -[[package]] -name = "prompt-toolkit" -version = "3.0.52" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wcwidth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, -] - -[[package]] -name = "propcache" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, - { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, - { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, - { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, - { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, - { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, - { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, - { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, - { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, - { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, - { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, - { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, - { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, - { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, - { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, - { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, - { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, - { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, - { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, - { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, - { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, - { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, - { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, - { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, - { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, - { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, - { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, - { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, - { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, - { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, - { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, - { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, - { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, - { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, - { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, - { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, - { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, - { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, - { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, - { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, - { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, - { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, - { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, - { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, - { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, - { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, - { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, - { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, - { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, - { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, - { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, - { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, - { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, - { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, - { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, -] - -[[package]] -name = "protobuf" -version = "6.33.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/25/7c72c307aafc96fa87062aa6291d9f7c94836e43214d43722e86037aac02/protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c", size = 444465, upload-time = "2026-01-29T21:51:33.494Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/79/af92d0a8369732b027e6d6084251dd8e782c685c72da161bd4a2e00fbabb/protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b", size = 425769, upload-time = "2026-01-29T21:51:21.751Z" }, - { url = "https://files.pythonhosted.org/packages/55/75/bb9bc917d10e9ee13dee8607eb9ab963b7cf8be607c46e7862c748aa2af7/protobuf-6.33.5-cp310-abi3-win_amd64.whl", hash = "sha256:3093804752167bcab3998bec9f1048baae6e29505adaf1afd14a37bddede533c", size = 437118, upload-time = "2026-01-29T21:51:24.022Z" }, - { url = "https://files.pythonhosted.org/packages/a2/6b/e48dfc1191bc5b52950246275bf4089773e91cb5ba3592621723cdddca62/protobuf-6.33.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:a5cb85982d95d906df1e2210e58f8e4f1e3cdc088e52c921a041f9c9a0386de5", size = 427766, upload-time = "2026-01-29T21:51:25.413Z" }, - { url = "https://files.pythonhosted.org/packages/4e/b1/c79468184310de09d75095ed1314b839eb2f72df71097db9d1404a1b2717/protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190", size = 324638, upload-time = "2026-01-29T21:51:26.423Z" }, - { url = "https://files.pythonhosted.org/packages/c5/f5/65d838092fd01c44d16037953fd4c2cc851e783de9b8f02b27ec4ffd906f/protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd", size = 339411, upload-time = "2026-01-29T21:51:27.446Z" }, - { url = "https://files.pythonhosted.org/packages/9b/53/a9443aa3ca9ba8724fdfa02dd1887c1bcd8e89556b715cfbacca6b63dbec/protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0", size = 323465, upload-time = "2026-01-29T21:51:28.925Z" }, - { url = "https://files.pythonhosted.org/packages/57/bf/2086963c69bdac3d7cff1cc7ff79b8ce5ea0bec6797a017e1be338a46248/protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02", size = 170687, upload-time = "2026-01-29T21:51:32.557Z" }, -] - -[[package]] -name = "psutil" -version = "7.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, - { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, - { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, - { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, - { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, - { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, - { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, - { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, - { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, - { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, - { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, - { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, - { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, - { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, - { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, - { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, - { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, - { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, - { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, -] - -[[package]] -name = "puremagic" -version = "1.30" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dd/7f/9998706bc516bdd664ccf929a1da6c6e5ee06e48f723ce45aae7cf3ff36e/puremagic-1.30.tar.gz", hash = "sha256:f9ff7ac157d54e9cf3bff1addfd97233548e75e685282d84ae11e7ffee1614c9", size = 314785, upload-time = "2025-07-04T18:48:36.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/ed/1e347d85d05b37a8b9a039ca832e5747e1e5248d0bd66042783ef48b4a37/puremagic-1.30-py3-none-any.whl", hash = "sha256:5eeeb2dd86f335b9cfe8e205346612197af3500c6872dffebf26929f56e9d3c1", size = 43304, upload-time = "2025-07-04T18:48:34.801Z" }, -] - -[[package]] -name = "py-key-value-aio" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "py-key-value-shared" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/ce/3136b771dddf5ac905cc193b461eb67967cf3979688c6696e1f2cdcde7ea/py_key_value_aio-0.3.0.tar.gz", hash = "sha256:858e852fcf6d696d231266da66042d3355a7f9871650415feef9fca7a6cd4155", size = 50801, upload-time = "2025-11-17T16:50:04.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/10/72f6f213b8f0bce36eff21fda0a13271834e9eeff7f9609b01afdc253c79/py_key_value_aio-0.3.0-py3-none-any.whl", hash = "sha256:1c781915766078bfd608daa769fefb97e65d1d73746a3dfb640460e322071b64", size = 96342, upload-time = "2025-11-17T16:50:03.801Z" }, -] - -[package.optional-dependencies] -disk = [ - { name = "diskcache" }, - { name = "pathvalidate" }, -] -keyring = [ - { name = "keyring" }, -] -memory = [ - { name = "cachetools" }, -] -redis = [ - { name = "redis" }, -] - -[[package]] -name = "py-key-value-shared" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/e4/1971dfc4620a3a15b4579fe99e024f5edd6e0967a71154771a059daff4db/py_key_value_shared-0.3.0.tar.gz", hash = "sha256:8fdd786cf96c3e900102945f92aa1473138ebe960ef49da1c833790160c28a4b", size = 11666, upload-time = "2025-11-17T16:50:06.849Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e4/b8b0a03ece72f47dce2307d36e1c34725b7223d209fc679315ffe6a4e2c3/py_key_value_shared-0.3.0-py3-none-any.whl", hash = "sha256:5b0efba7ebca08bb158b1e93afc2f07d30b8f40c2fc12ce24a4c0d84f42f9298", size = 19560, upload-time = "2025-11-17T16:50:05.954Z" }, -] - -[[package]] -name = "py-rust-stemmers" -version = "0.1.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/63/4fbc14810c32d2a884e2e94e406a7d5bf8eee53e1103f558433817230342/py_rust_stemmers-0.1.5.tar.gz", hash = "sha256:e9c310cfb5c2470d7c7c8a0484725965e7cab8b1237e106a0863d5741da3e1f7", size = 9388, upload-time = "2025-02-19T13:56:28.708Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e1/ea8ac92454a634b1bb1ee0a89c2f75a4e6afec15a8412527e9bbde8c6b7b/py_rust_stemmers-0.1.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:29772837126a28263bf54ecd1bc709dd569d15a94d5e861937813ce51e8a6df4", size = 286085, upload-time = "2025-02-19T13:55:23.871Z" }, - { url = "https://files.pythonhosted.org/packages/cb/32/fe1cc3d36a19c1ce39792b1ed151ddff5ee1d74c8801f0e93ff36e65f885/py_rust_stemmers-0.1.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d62410ada44a01e02974b85d45d82f4b4c511aae9121e5f3c1ba1d0bea9126b", size = 272021, upload-time = "2025-02-19T13:55:25.685Z" }, - { url = "https://files.pythonhosted.org/packages/0a/38/b8f94e5e886e7ab181361a0911a14fb923b0d05b414de85f427e773bf445/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b28ef729a4c83c7d9418be3c23c0372493fcccc67e86783ff04596ef8a208cdf", size = 310547, upload-time = "2025-02-19T13:55:26.891Z" }, - { url = "https://files.pythonhosted.org/packages/a9/08/62e97652d359b75335486f4da134a6f1c281f38bd3169ed6ecfb276448c3/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a979c3f4ff7ad94a0d4cf566ca7bfecebb59e66488cc158e64485cf0c9a7879f", size = 315237, upload-time = "2025-02-19T13:55:28.116Z" }, - { url = "https://files.pythonhosted.org/packages/1c/b9/fc0278432f288d2be4ee4d5cc80fd8013d604506b9b0503e8b8cae4ba1c3/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c3593d895453fa06bf70a7b76d6f00d06def0f91fc253fe4260920650c5e078", size = 324419, upload-time = "2025-02-19T13:55:29.211Z" }, - { url = "https://files.pythonhosted.org/packages/6b/5b/74e96eaf622fe07e83c5c389d101540e305e25f76a6d0d6fb3d9e0506db8/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:96ccc7fd042ffc3f7f082f2223bb7082ed1423aa6b43d5d89ab23e321936c045", size = 324792, upload-time = "2025-02-19T13:55:30.948Z" }, - { url = "https://files.pythonhosted.org/packages/4f/f7/b76816d7d67166e9313915ad486c21d9e7da0ac02703e14375bb1cb64b5a/py_rust_stemmers-0.1.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef18cfced2c9c676e0d7d172ba61c3fab2aa6969db64cc8f5ca33a7759efbefe", size = 488014, upload-time = "2025-02-19T13:55:32.066Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ed/7d9bed02f78d85527501f86a867cd5002d97deb791b9a6b1b45b00100010/py_rust_stemmers-0.1.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:541d4b5aa911381e3d37ec483abb6a2cf2351b4f16d5e8d77f9aa2722956662a", size = 575582, upload-time = "2025-02-19T13:55:34.005Z" }, - { url = "https://files.pythonhosted.org/packages/93/40/eafd1b33688e8e8ae946d1ef25c4dc93f5b685bd104b9c5573405d7e1d30/py_rust_stemmers-0.1.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ffd946a36e9ac17ca96821963663012e04bc0ee94d21e8b5ae034721070b436c", size = 493267, upload-time = "2025-02-19T13:55:35.294Z" }, - { url = "https://files.pythonhosted.org/packages/2f/6a/15135b69e4fd28369433eb03264d201b1b0040ba534b05eddeb02a276684/py_rust_stemmers-0.1.5-cp312-none-win_amd64.whl", hash = "sha256:6ed61e1207f3b7428e99b5d00c055645c6415bb75033bff2d06394cbe035fd8e", size = 209395, upload-time = "2025-02-19T13:55:36.519Z" }, - { url = "https://files.pythonhosted.org/packages/80/b8/030036311ec25952bf3083b6c105be5dee052a71aa22d5fbeb857ebf8c1c/py_rust_stemmers-0.1.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:398b3a843a9cd4c5d09e726246bc36f66b3d05b0a937996814e91f47708f5db5", size = 286086, upload-time = "2025-02-19T13:55:37.581Z" }, - { url = "https://files.pythonhosted.org/packages/ed/be/0465dcb3a709ee243d464e89231e3da580017f34279d6304de291d65ccb0/py_rust_stemmers-0.1.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4e308fc7687901f0c73603203869908f3156fa9c17c4ba010a7fcc98a7a1c5f2", size = 272019, upload-time = "2025-02-19T13:55:39.183Z" }, - { url = "https://files.pythonhosted.org/packages/ab/b6/76ca5b1f30cba36835938b5d9abee0c130c81833d51b9006264afdf8df3c/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f9efc4da5e734bdd00612e7506de3d0c9b7abc4b89d192742a0569d0d1fe749", size = 310545, upload-time = "2025-02-19T13:55:40.339Z" }, - { url = "https://files.pythonhosted.org/packages/56/8f/5be87618cea2fe2e70e74115a20724802bfd06f11c7c43514b8288eb6514/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cc2cc8d2b36bc05b8b06506199ac63d437360ae38caefd98cd19e479d35afd42", size = 315236, upload-time = "2025-02-19T13:55:41.55Z" }, - { url = "https://files.pythonhosted.org/packages/00/02/ea86a316aee0f0a9d1449ad4dbffff38f4cf0a9a31045168ae8b95d8bdf8/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a231dc6f0b2a5f12a080dfc7abd9e6a4ea0909290b10fd0a4620e5a0f52c3d17", size = 324419, upload-time = "2025-02-19T13:55:42.693Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fd/1612c22545dcc0abe2f30fc08f30a2332f2224dd536fa1508444a9ca0e39/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5845709d48afc8b29e248f42f92431155a3d8df9ba30418301c49c6072b181b0", size = 324794, upload-time = "2025-02-19T13:55:43.896Z" }, - { url = "https://files.pythonhosted.org/packages/66/18/8a547584d7edac9e7ac9c7bdc53228d6f751c0f70a317093a77c386c8ddc/py_rust_stemmers-0.1.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e48bfd5e3ce9d223bfb9e634dc1425cf93ee57eef6f56aa9a7120ada3990d4be", size = 488014, upload-time = "2025-02-19T13:55:45.088Z" }, - { url = "https://files.pythonhosted.org/packages/3b/87/4619c395b325e26048a6e28a365afed754614788ba1f49b2eefb07621a03/py_rust_stemmers-0.1.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:35d32f6e7bdf6fd90e981765e32293a8be74def807147dea9fdc1f65d6ce382f", size = 575582, upload-time = "2025-02-19T13:55:46.436Z" }, - { url = "https://files.pythonhosted.org/packages/98/6e/214f1a889142b7df6d716e7f3fea6c41e87bd6c29046aa57e175d452b104/py_rust_stemmers-0.1.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:191ea8bf922c984631ffa20bf02ef0ad7eec0465baeaed3852779e8f97c7e7a3", size = 493269, upload-time = "2025-02-19T13:55:49.057Z" }, - { url = "https://files.pythonhosted.org/packages/e1/b9/c5185df277576f995ae34418eb2b2ac12f30835412270f9e05c52face521/py_rust_stemmers-0.1.5-cp313-none-win_amd64.whl", hash = "sha256:e564c9efdbe7621704e222b53bac265b0e4fbea788f07c814094f0ec6b80adcf", size = 209397, upload-time = "2025-02-19T13:55:50.853Z" }, -] - -[[package]] -name = "pyarrow" -version = "23.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/33/ffd9c3eb087fa41dd79c3cf20c4c0ae3cdb877c4f8e1107a446006344924/pyarrow-23.0.0.tar.gz", hash = "sha256:180e3150e7edfcd182d3d9afba72f7cf19839a497cc76555a8dce998a8f67615", size = 1167185, upload-time = "2026-01-18T16:19:42.218Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/bd/c861d020831ee57609b73ea721a617985ece817684dc82415b0bc3e03ac3/pyarrow-23.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5961a9f646c232697c24f54d3419e69b4261ba8a8b66b0ac54a1851faffcbab8", size = 34189116, upload-time = "2026-01-18T16:15:28.054Z" }, - { url = "https://files.pythonhosted.org/packages/8c/23/7725ad6cdcbaf6346221391e7b3eecd113684c805b0a95f32014e6fa0736/pyarrow-23.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:632b3e7c3d232f41d64e1a4a043fb82d44f8a349f339a1188c6a0dd9d2d47d8a", size = 35803831, upload-time = "2026-01-18T16:15:33.798Z" }, - { url = "https://files.pythonhosted.org/packages/57/06/684a421543455cdc2944d6a0c2cc3425b028a4c6b90e34b35580c4899743/pyarrow-23.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:76242c846db1411f1d6c2cc3823be6b86b40567ee24493344f8226ba34a81333", size = 44436452, upload-time = "2026-01-18T16:15:41.598Z" }, - { url = "https://files.pythonhosted.org/packages/c6/6f/8f9eb40c2328d66e8b097777ddcf38494115ff9f1b5bc9754ba46991191e/pyarrow-23.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b73519f8b52ae28127000986bf228fda781e81d3095cd2d3ece76eb5cf760e1b", size = 47557396, upload-time = "2026-01-18T16:15:51.252Z" }, - { url = "https://files.pythonhosted.org/packages/10/6e/f08075f1472e5159553501fde2cc7bc6700944bdabe49a03f8a035ee6ccd/pyarrow-23.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:068701f6823449b1b6469120f399a1239766b117d211c5d2519d4ed5861f75de", size = 48147129, upload-time = "2026-01-18T16:16:00.299Z" }, - { url = "https://files.pythonhosted.org/packages/7d/82/d5a680cd507deed62d141cc7f07f7944a6766fc51019f7f118e4d8ad0fb8/pyarrow-23.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1801ba947015d10e23bca9dd6ef5d0e9064a81569a89b6e9a63b59224fd060df", size = 50596642, upload-time = "2026-01-18T16:16:08.502Z" }, - { url = "https://files.pythonhosted.org/packages/a9/26/4f29c61b3dce9fa7780303b86895ec6a0917c9af927101daaaf118fbe462/pyarrow-23.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:52265266201ec25b6839bf6bd4ea918ca6d50f31d13e1cf200b4261cd11dc25c", size = 27660628, upload-time = "2026-01-18T16:16:15.28Z" }, - { url = "https://files.pythonhosted.org/packages/66/34/564db447d083ec7ff93e0a883a597d2f214e552823bfc178a2d0b1f2c257/pyarrow-23.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:ad96a597547af7827342ffb3c503c8316e5043bb09b47a84885ce39394c96e00", size = 34184630, upload-time = "2026-01-18T16:16:22.141Z" }, - { url = "https://files.pythonhosted.org/packages/aa/3a/3999daebcb5e6119690c92a621c4d78eef2ffba7a0a1b56386d2875fcd77/pyarrow-23.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:b9edf990df77c2901e79608f08c13fbde60202334a4fcadb15c1f57bf7afee43", size = 35796820, upload-time = "2026-01-18T16:16:29.441Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ee/39195233056c6a8d0976d7d1ac1cd4fe21fb0ec534eca76bc23ef3f60e11/pyarrow-23.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:36d1b5bc6ddcaff0083ceec7e2561ed61a51f49cce8be079ee8ed406acb6fdef", size = 44438735, upload-time = "2026-01-18T16:16:38.79Z" }, - { url = "https://files.pythonhosted.org/packages/2c/41/6a7328ee493527e7afc0c88d105ecca69a3580e29f2faaeac29308369fd7/pyarrow-23.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4292b889cd224f403304ddda8b63a36e60f92911f89927ec8d98021845ea21be", size = 47557263, upload-time = "2026-01-18T16:16:46.248Z" }, - { url = "https://files.pythonhosted.org/packages/c6/ee/34e95b21ee84db494eae60083ddb4383477b31fb1fd19fd866d794881696/pyarrow-23.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dfd9e133e60eaa847fd80530a1b89a052f09f695d0b9c34c235ea6b2e0924cf7", size = 48153529, upload-time = "2026-01-18T16:16:53.412Z" }, - { url = "https://files.pythonhosted.org/packages/52/88/8a8d83cea30f4563efa1b7bf51d241331ee5cd1b185a7e063f5634eca415/pyarrow-23.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832141cc09fac6aab1cd3719951d23301396968de87080c57c9a7634e0ecd068", size = 50598851, upload-time = "2026-01-18T16:17:01.133Z" }, - { url = "https://files.pythonhosted.org/packages/c6/4c/2929c4be88723ba025e7b3453047dc67e491c9422965c141d24bab6b5962/pyarrow-23.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:7a7d067c9a88faca655c71bcc30ee2782038d59c802d57950826a07f60d83c4c", size = 27577747, upload-time = "2026-01-18T16:18:02.413Z" }, - { url = "https://files.pythonhosted.org/packages/64/52/564a61b0b82d72bd68ec3aef1adda1e3eba776f89134b9ebcb5af4b13cb6/pyarrow-23.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:ce9486e0535a843cf85d990e2ec5820a47918235183a5c7b8b97ed7e92c2d47d", size = 34446038, upload-time = "2026-01-18T16:17:07.861Z" }, - { url = "https://files.pythonhosted.org/packages/cc/c9/232d4f9855fd1de0067c8a7808a363230d223c83aeee75e0fe6eab851ba9/pyarrow-23.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:075c29aeaa685fd1182992a9ed2499c66f084ee54eea47da3eb76e125e06064c", size = 35921142, upload-time = "2026-01-18T16:17:15.401Z" }, - { url = "https://files.pythonhosted.org/packages/96/f2/60af606a3748367b906bb82d41f0032e059f075444445d47e32a7ff1df62/pyarrow-23.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:799965a5379589510d888be3094c2296efd186a17ca1cef5b77703d4d5121f53", size = 44490374, upload-time = "2026-01-18T16:17:23.93Z" }, - { url = "https://files.pythonhosted.org/packages/ff/2d/7731543050a678ea3a413955a2d5d80d2a642f270aa57a3cb7d5a86e3f46/pyarrow-23.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ef7cac8fe6fccd8b9e7617bfac785b0371a7fe26af59463074e4882747145d40", size = 47527896, upload-time = "2026-01-18T16:17:33.393Z" }, - { url = "https://files.pythonhosted.org/packages/5a/90/f3342553b7ac9879413aed46500f1637296f3c8222107523a43a1c08b42a/pyarrow-23.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15a414f710dc927132dd67c361f78c194447479555af57317066ee5116b90e9e", size = 48210401, upload-time = "2026-01-18T16:17:42.012Z" }, - { url = "https://files.pythonhosted.org/packages/f3/da/9862ade205ecc46c172b6ce5038a74b5151c7401e36255f15975a45878b2/pyarrow-23.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e0d2e6915eca7d786be6a77bf227fbc06d825a75b5b5fe9bcbef121dec32685", size = 50579677, upload-time = "2026-01-18T16:17:50.241Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4c/f11f371f5d4740a5dafc2e11c76bcf42d03dfdb2d68696da97de420b6963/pyarrow-23.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4b317ea6e800b5704e5e5929acb6e2dc13e9276b708ea97a39eb8b345aa2658b", size = 27631889, upload-time = "2026-01-18T16:17:56.55Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/15aec78bcf43a0c004067bd33eb5352836a29a49db8581fc56f2b6ca88b7/pyarrow-23.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:20b187ed9550d233a872074159f765f52f9d92973191cd4b93f293a19efbe377", size = 34213265, upload-time = "2026-01-18T16:18:07.904Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/deb2c594bbba41c37c5d9aa82f510376998352aa69dfcb886cb4b18ad80f/pyarrow-23.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:18ec84e839b493c3886b9b5e06861962ab4adfaeb79b81c76afbd8d84c7d5fda", size = 35819211, upload-time = "2026-01-18T16:18:13.94Z" }, - { url = "https://files.pythonhosted.org/packages/e0/e5/ee82af693cb7b5b2b74f6524cdfede0e6ace779d7720ebca24d68b57c36b/pyarrow-23.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:e438dd3f33894e34fd02b26bd12a32d30d006f5852315f611aa4add6c7fab4bc", size = 44502313, upload-time = "2026-01-18T16:18:20.367Z" }, - { url = "https://files.pythonhosted.org/packages/9c/86/95c61ad82236495f3c31987e85135926ba3ec7f3819296b70a68d8066b49/pyarrow-23.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:a244279f240c81f135631be91146d7fa0e9e840e1dfed2aba8483eba25cd98e6", size = 47585886, upload-time = "2026-01-18T16:18:27.544Z" }, - { url = "https://files.pythonhosted.org/packages/bb/6e/a72d901f305201802f016d015de1e05def7706fff68a1dedefef5dc7eff7/pyarrow-23.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c4692e83e42438dba512a570c6eaa42be2f8b6c0f492aea27dec54bdc495103a", size = 48207055, upload-time = "2026-01-18T16:18:35.425Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e5/5de029c537630ca18828db45c30e2a78da03675a70ac6c3528203c416fe3/pyarrow-23.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae7f30f898dfe44ea69654a35c93e8da4cef6606dc4c72394068fd95f8e9f54a", size = 50619812, upload-time = "2026-01-18T16:18:43.553Z" }, - { url = "https://files.pythonhosted.org/packages/59/8d/2af846cd2412e67a087f5bda4a8e23dfd4ebd570f777db2e8686615dafc1/pyarrow-23.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:5b86bb649e4112fb0614294b7d0a175c7513738876b89655605ebb87c804f861", size = 28263851, upload-time = "2026-01-18T16:19:38.567Z" }, - { url = "https://files.pythonhosted.org/packages/7b/7f/caab863e587041156f6786c52e64151b7386742c8c27140f637176e9230e/pyarrow-23.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:ebc017d765d71d80a3f8584ca0566b53e40464586585ac64176115baa0ada7d3", size = 34463240, upload-time = "2026-01-18T16:18:49.755Z" }, - { url = "https://files.pythonhosted.org/packages/c9/fa/3a5b8c86c958e83622b40865e11af0857c48ec763c11d472c87cd518283d/pyarrow-23.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:0800cc58a6d17d159df823f87ad66cefebf105b982493d4bad03ee7fab84b993", size = 35935712, upload-time = "2026-01-18T16:18:55.626Z" }, - { url = "https://files.pythonhosted.org/packages/c5/08/17a62078fc1a53decb34a9aa79cf9009efc74d63d2422e5ade9fed2f99e3/pyarrow-23.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:3a7c68c722da9bb5b0f8c10e3eae71d9825a4b429b40b32709df5d1fa55beb3d", size = 44503523, upload-time = "2026-01-18T16:19:03.958Z" }, - { url = "https://files.pythonhosted.org/packages/cc/70/84d45c74341e798aae0323d33b7c39194e23b1abc439ceaf60a68a7a969a/pyarrow-23.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:bd5556c24622df90551063ea41f559b714aa63ca953db884cfb958559087a14e", size = 47542490, upload-time = "2026-01-18T16:19:11.208Z" }, - { url = "https://files.pythonhosted.org/packages/61/d9/d1274b0e6f19e235de17441e53224f4716574b2ca837022d55702f24d71d/pyarrow-23.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:54810f6e6afc4ffee7c2e0051b61722fbea9a4961b46192dcfae8ea12fa09059", size = 48233605, upload-time = "2026-01-18T16:19:19.544Z" }, - { url = "https://files.pythonhosted.org/packages/39/07/e4e2d568cb57543d84482f61e510732820cddb0f47c4bb7df629abfed852/pyarrow-23.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:14de7d48052cf4b0ed174533eafa3cfe0711b8076ad70bede32cf59f744f0d7c", size = 50603979, upload-time = "2026-01-18T16:19:26.717Z" }, - { url = "https://files.pythonhosted.org/packages/72/9c/47693463894b610f8439b2e970b82ef81e9599c757bf2049365e40ff963c/pyarrow-23.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:427deac1f535830a744a4f04a6ac183a64fcac4341b3f618e693c41b7b98d2b0", size = 28338905, upload-time = "2026-01-18T16:19:32.93Z" }, -] - -[[package]] -name = "pyasn1" -version = "0.6.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/b6/6e630dff89739fcd427e3f72b3d905ce0acb85a45d4ec3e2678718a3487f/pyasn1-0.6.2.tar.gz", hash = "sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b", size = 146586, upload-time = "2026-01-16T18:04:18.534Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/b5/a96872e5184f354da9c84ae119971a0a4c221fe9b27a4d94bd43f2596727/pyasn1-0.6.2-py3-none-any.whl", hash = "sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf", size = 83371, upload-time = "2026-01-16T18:04:17.174Z" }, -] - -[[package]] -name = "pyautogui" -version = "0.9.54" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mouseinfo" }, - { name = "pygetwindow" }, - { name = "pymsgbox" }, - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, - { name = "pyscreeze" }, - { name = "python3-xlib", marker = "sys_platform == 'linux'" }, - { name = "pytweening" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/65/ff/cdae0a8c2118a0de74b6cf4cbcdcaf8fd25857e6c3f205ce4b1794b27814/PyAutoGUI-0.9.54.tar.gz", hash = "sha256:dd1d29e8fd118941cb193f74df57e5c6ff8e9253b99c7b04f39cfc69f3ae04b2", size = 61236, upload-time = "2023-05-24T20:11:32.972Z" } - -[[package]] -name = "pybase64" -version = "1.4.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/b8/4ed5c7ad5ec15b08d35cc79ace6145d5c1ae426e46435f4987379439dfea/pybase64-1.4.3.tar.gz", hash = "sha256:c2ed274c9e0ba9c8f9c4083cfe265e66dd679126cd9c2027965d807352f3f053", size = 137272, upload-time = "2025-12-06T13:27:04.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/a7/efcaa564f091a2af7f18a83c1c4875b1437db56ba39540451dc85d56f653/pybase64-1.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:18d85e5ab8b986bb32d8446aca6258ed80d1bafe3603c437690b352c648f5967", size = 38167, upload-time = "2025-12-06T13:23:16.821Z" }, - { url = "https://files.pythonhosted.org/packages/db/c7/c7ad35adff2d272bf2930132db2b3eea8c44bb1b1f64eb9b2b8e57cde7b4/pybase64-1.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3f5791a3491d116d0deaf4d83268f48792998519698f8751efb191eac84320e9", size = 31673, upload-time = "2025-12-06T13:23:17.835Z" }, - { url = "https://files.pythonhosted.org/packages/43/1b/9a8cab0042b464e9a876d5c65fe5127445a2436da36fda64899b119b1a1b/pybase64-1.4.3-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f0b3f200c3e06316f6bebabd458b4e4bcd4c2ca26af7c0c766614d91968dee27", size = 68210, upload-time = "2025-12-06T13:23:18.813Z" }, - { url = "https://files.pythonhosted.org/packages/62/f7/965b79ff391ad208b50e412b5d3205ccce372a2d27b7218ae86d5295b105/pybase64-1.4.3-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb632edfd132b3eaf90c39c89aa314beec4e946e210099b57d40311f704e11d4", size = 71599, upload-time = "2025-12-06T13:23:20.195Z" }, - { url = "https://files.pythonhosted.org/packages/03/4b/a3b5175130b3810bbb8ccfa1edaadbd3afddb9992d877c8a1e2f274b476e/pybase64-1.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:356ef1d74648ce997f5a777cf8f1aefecc1c0b4fe6201e0ef3ec8a08170e1b54", size = 59922, upload-time = "2025-12-06T13:23:21.487Z" }, - { url = "https://files.pythonhosted.org/packages/da/5d/c38d1572027fc601b62d7a407721688b04b4d065d60ca489912d6893e6cf/pybase64-1.4.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:c48361f90db32bacaa5518419d4eb9066ba558013aaf0c7781620279ecddaeb9", size = 56712, upload-time = "2025-12-06T13:23:22.77Z" }, - { url = "https://files.pythonhosted.org/packages/e7/d4/4e04472fef485caa8f561d904d4d69210a8f8fc1608ea15ebd9012b92655/pybase64-1.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:702bcaa16ae02139d881aeaef5b1c8ffb4a3fae062fe601d1e3835e10310a517", size = 59300, upload-time = "2025-12-06T13:23:24.543Z" }, - { url = "https://files.pythonhosted.org/packages/86/e7/16e29721b86734b881d09b7e23dfd7c8408ad01a4f4c7525f3b1088e25ec/pybase64-1.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:53d0ffe1847b16b647c6413d34d1de08942b7724273dd57e67dcbdb10c574045", size = 60278, upload-time = "2025-12-06T13:23:25.608Z" }, - { url = "https://files.pythonhosted.org/packages/b1/02/18515f211d7c046be32070709a8efeeef8a0203de4fd7521e6b56404731b/pybase64-1.4.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:9a1792e8b830a92736dae58f0c386062eb038dfe8004fb03ba33b6083d89cd43", size = 54817, upload-time = "2025-12-06T13:23:26.633Z" }, - { url = "https://files.pythonhosted.org/packages/e7/be/14e29d8e1a481dbff151324c96dd7b5d2688194bb65dc8a00ca0e1ad1e86/pybase64-1.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1d468b1b1ac5ad84875a46eaa458663c3721e8be5f155ade356406848d3701f6", size = 58611, upload-time = "2025-12-06T13:23:27.684Z" }, - { url = "https://files.pythonhosted.org/packages/b4/8a/a2588dfe24e1bbd742a554553778ab0d65fdf3d1c9a06d10b77047d142aa/pybase64-1.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e97b7bdbd62e71898cd542a6a9e320d9da754ff3ebd02cb802d69087ee94d468", size = 52404, upload-time = "2025-12-06T13:23:28.714Z" }, - { url = "https://files.pythonhosted.org/packages/27/fc/afcda7445bebe0cbc38cafdd7813234cdd4fc5573ff067f1abf317bb0cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b33aeaa780caaa08ffda87fc584d5eab61e3d3bbb5d86ead02161dc0c20d04bc", size = 68817, upload-time = "2025-12-06T13:23:30.079Z" }, - { url = "https://files.pythonhosted.org/packages/d3/3a/87c3201e555ed71f73e961a787241a2438c2bbb2ca8809c29ddf938a3157/pybase64-1.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c0efcf78f11cf866bed49caa7b97552bc4855a892f9cc2372abcd3ed0056f0d", size = 57854, upload-time = "2025-12-06T13:23:31.17Z" }, - { url = "https://files.pythonhosted.org/packages/fd/7d/931c2539b31a7b375e7d595b88401eeb5bd6c5ce1059c9123f9b608aaa14/pybase64-1.4.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:66e3791f2ed725a46593f8bd2761ff37d01e2cdad065b1dceb89066f476e50c6", size = 54333, upload-time = "2025-12-06T13:23:32.422Z" }, - { url = "https://files.pythonhosted.org/packages/de/5e/537601e02cc01f27e9d75f440f1a6095b8df44fc28b1eef2cd739aea8cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:72bb0b6bddadab26e1b069bb78e83092711a111a80a0d6b9edcb08199ad7299b", size = 56492, upload-time = "2025-12-06T13:23:33.515Z" }, - { url = "https://files.pythonhosted.org/packages/96/97/2a2e57acf8f5c9258d22aba52e71f8050e167b29ed2ee1113677c1b600c1/pybase64-1.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5b3365dbcbcdb0a294f0f50af0c0a16b27a232eddeeb0bceeefd844ef30d2a23", size = 70974, upload-time = "2025-12-06T13:23:36.27Z" }, - { url = "https://files.pythonhosted.org/packages/75/2e/a9e28941c6dab6f06e6d3f6783d3373044be9b0f9a9d3492c3d8d2260ac0/pybase64-1.4.3-cp312-cp312-win32.whl", hash = "sha256:7bca1ed3a5df53305c629ca94276966272eda33c0d71f862d2d3d043f1e1b91a", size = 33686, upload-time = "2025-12-06T13:23:37.848Z" }, - { url = "https://files.pythonhosted.org/packages/83/e3/507ab649d8c3512c258819c51d25c45d6e29d9ca33992593059e7b646a33/pybase64-1.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:9f2da8f56d9b891b18b4daf463a0640eae45a80af548ce435be86aa6eff3603b", size = 35833, upload-time = "2025-12-06T13:23:38.877Z" }, - { url = "https://files.pythonhosted.org/packages/bc/8a/6eba66cd549a2fc74bb4425fd61b839ba0ab3022d3c401b8a8dc2cc00c7a/pybase64-1.4.3-cp312-cp312-win_arm64.whl", hash = "sha256:0631d8a2d035de03aa9bded029b9513e1fee8ed80b7ddef6b8e9389ffc445da0", size = 31185, upload-time = "2025-12-06T13:23:39.908Z" }, - { url = "https://files.pythonhosted.org/packages/3a/50/b7170cb2c631944388fe2519507fe3835a4054a6a12a43f43781dae82be1/pybase64-1.4.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:ea4b785b0607d11950b66ce7c328f452614aefc9c6d3c9c28bae795dc7f072e1", size = 33901, upload-time = "2025-12-06T13:23:40.951Z" }, - { url = "https://files.pythonhosted.org/packages/48/8b/69f50578e49c25e0a26e3ee72c39884ff56363344b79fc3967f5af420ed6/pybase64-1.4.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:6a10b6330188c3026a8b9c10e6b9b3f2e445779cf16a4c453d51a072241c65a2", size = 40807, upload-time = "2025-12-06T13:23:42.006Z" }, - { url = "https://files.pythonhosted.org/packages/5c/8d/20b68f11adfc4c22230e034b65c71392e3e338b413bf713c8945bd2ccfb3/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:27fdff227a0c0e182e0ba37a99109645188978b920dfb20d8b9c17eeee370d0d", size = 30932, upload-time = "2025-12-06T13:23:43.348Z" }, - { url = "https://files.pythonhosted.org/packages/f7/79/b1b550ac6bff51a4880bf6e089008b2e1ca16f2c98db5e039a08ac3ad157/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2a8204f1fdfec5aa4184249b51296c0de95445869920c88123978304aad42df1", size = 31394, upload-time = "2025-12-06T13:23:44.317Z" }, - { url = "https://files.pythonhosted.org/packages/82/70/b5d7c5932bf64ee1ec5da859fbac981930b6a55d432a603986c7f509c838/pybase64-1.4.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:874fc2a3777de6baf6aa921a7aa73b3be98295794bea31bd80568a963be30767", size = 38078, upload-time = "2025-12-06T13:23:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/56/fe/e66fe373bce717c6858427670736d54297938dad61c5907517ab4106bd90/pybase64-1.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2dc64a94a9d936b8e3449c66afabbaa521d3cc1a563d6bbaaa6ffa4535222e4b", size = 38158, upload-time = "2025-12-06T13:23:46.872Z" }, - { url = "https://files.pythonhosted.org/packages/80/a9/b806ed1dcc7aed2ea3dd4952286319e6f3a8b48615c8118f453948e01999/pybase64-1.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e48f86de1c145116ccf369a6e11720ce696c2ec02d285f440dfb57ceaa0a6cb4", size = 31672, upload-time = "2025-12-06T13:23:47.88Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c9/24b3b905cf75e23a9a4deaf203b35ffcb9f473ac0e6d8257f91a05dfce62/pybase64-1.4.3-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1d45c8fe8fe82b65c36b227bb4a2cf623d9ada16bed602ce2d3e18c35285b72a", size = 68244, upload-time = "2025-12-06T13:23:49.026Z" }, - { url = "https://files.pythonhosted.org/packages/f8/cd/d15b0c3e25e5859fab0416dc5b96d34d6bd2603c1c96a07bb2202b68ab92/pybase64-1.4.3-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ad70c26ba091d8f5167e9d4e1e86a0483a5414805cdb598a813db635bd3be8b8", size = 71620, upload-time = "2025-12-06T13:23:50.081Z" }, - { url = "https://files.pythonhosted.org/packages/0d/31/4ca953cc3dcde2b3711d6bfd70a6f4ad2ca95a483c9698076ba605f1520f/pybase64-1.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e98310b7c43145221e7194ac9fa7fffc84763c87bfc5e2f59f9f92363475bdc1", size = 59930, upload-time = "2025-12-06T13:23:51.68Z" }, - { url = "https://files.pythonhosted.org/packages/60/55/e7f7bdcd0fd66e61dda08db158ffda5c89a306bbdaaf5a062fbe4e48f4a1/pybase64-1.4.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:398685a76034e91485a28aeebcb49e64cd663212fd697b2497ac6dfc1df5e671", size = 56425, upload-time = "2025-12-06T13:23:52.732Z" }, - { url = "https://files.pythonhosted.org/packages/cb/65/b592c7f921e51ca1aca3af5b0d201a98666d0a36b930ebb67e7c2ed27395/pybase64-1.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7e46400a6461187ccb52ed75b0045d937529e801a53a9cd770b350509f9e4d50", size = 59327, upload-time = "2025-12-06T13:23:53.856Z" }, - { url = "https://files.pythonhosted.org/packages/23/95/1613d2fb82dbb1548595ad4179f04e9a8451bfa18635efce18b631eabe3f/pybase64-1.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1b62b9f2f291d94f5e0b76ab499790b7dcc78a009d4ceea0b0428770267484b6", size = 60294, upload-time = "2025-12-06T13:23:54.937Z" }, - { url = "https://files.pythonhosted.org/packages/9d/73/40431f37f7d1b3eab4673e7946ff1e8f5d6bd425ec257e834dae8a6fc7b0/pybase64-1.4.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:f30ceb5fa4327809dede614be586efcbc55404406d71e1f902a6fdcf322b93b2", size = 54858, upload-time = "2025-12-06T13:23:56.031Z" }, - { url = "https://files.pythonhosted.org/packages/a7/84/f6368bcaf9f743732e002a9858646fd7a54f428490d427dd6847c5cfe89e/pybase64-1.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0d5f18ed53dfa1d4cf8b39ee542fdda8e66d365940e11f1710989b3cf4a2ed66", size = 58629, upload-time = "2025-12-06T13:23:57.12Z" }, - { url = "https://files.pythonhosted.org/packages/43/75/359532f9adb49c6b546cafc65c46ed75e2ccc220d514ba81c686fbd83965/pybase64-1.4.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:119d31aa4b58b85a8ebd12b63c07681a138c08dfc2fe5383459d42238665d3eb", size = 52448, upload-time = "2025-12-06T13:23:58.298Z" }, - { url = "https://files.pythonhosted.org/packages/92/6c/ade2ba244c3f33ed920a7ed572ad772eb0b5f14480b72d629d0c9e739a40/pybase64-1.4.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3cf0218b0e2f7988cf7d738a73b6a1d14f3be6ce249d7c0f606e768366df2cce", size = 68841, upload-time = "2025-12-06T13:23:59.886Z" }, - { url = "https://files.pythonhosted.org/packages/a0/51/b345139cd236be382f2d4d4453c21ee6299e14d2f759b668e23080f8663f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:12f4ee5e988bc5c0c1106b0d8fc37fb0508f12dab76bac1b098cb500d148da9d", size = 57910, upload-time = "2025-12-06T13:24:00.994Z" }, - { url = "https://files.pythonhosted.org/packages/1a/b8/9f84bdc4f1c4f0052489396403c04be2f9266a66b70c776001eaf0d78c1f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:937826bc7b6b95b594a45180e81dd4d99bd4dd4814a443170e399163f7ff3fb6", size = 54335, upload-time = "2025-12-06T13:24:02.046Z" }, - { url = "https://files.pythonhosted.org/packages/d0/c7/be63b617d284de46578a366da77ede39c8f8e815ed0d82c7c2acca560fab/pybase64-1.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:88995d1460971ef80b13e3e007afbe4b27c62db0508bc7250a2ab0a0b4b91362", size = 56486, upload-time = "2025-12-06T13:24:03.141Z" }, - { url = "https://files.pythonhosted.org/packages/5e/96/f252c8f9abd6ded3ef1ccd3cdbb8393a33798007f761b23df8de1a2480e6/pybase64-1.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:72326fe163385ed3e1e806dd579d47fde5d8a59e51297a60fc4e6cbc1b4fc4ed", size = 70978, upload-time = "2025-12-06T13:24:04.221Z" }, - { url = "https://files.pythonhosted.org/packages/af/51/0f5714af7aeef96e30f968e4371d75ad60558aaed3579d7c6c8f1c43c18a/pybase64-1.4.3-cp313-cp313-win32.whl", hash = "sha256:b1623730c7892cf5ed0d6355e375416be6ef8d53ab9b284f50890443175c0ac3", size = 33684, upload-time = "2025-12-06T13:24:05.29Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ad/0cea830a654eb08563fb8214150ef57546ece1cc421c09035f0e6b0b5ea9/pybase64-1.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:8369887590f1646a5182ca2fb29252509da7ae31d4923dbb55d3e09da8cc4749", size = 35832, upload-time = "2025-12-06T13:24:06.35Z" }, - { url = "https://files.pythonhosted.org/packages/b4/0d/eec2a8214989c751bc7b4cad1860eb2c6abf466e76b77508c0f488c96a37/pybase64-1.4.3-cp313-cp313-win_arm64.whl", hash = "sha256:860b86bca71e5f0237e2ab8b2d9c4c56681f3513b1bf3e2117290c1963488390", size = 31175, upload-time = "2025-12-06T13:24:07.419Z" }, - { url = "https://files.pythonhosted.org/packages/db/c9/e23463c1a2913686803ef76b1a5ae7e6fac868249a66e48253d17ad7232c/pybase64-1.4.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eb51db4a9c93215135dccd1895dca078e8785c357fabd983c9f9a769f08989a9", size = 38497, upload-time = "2025-12-06T13:24:08.873Z" }, - { url = "https://files.pythonhosted.org/packages/71/83/343f446b4b7a7579bf6937d2d013d82f1a63057cf05558e391ab6039d7db/pybase64-1.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a03ef3f529d85fd46b89971dfb00c634d53598d20ad8908fb7482955c710329d", size = 32076, upload-time = "2025-12-06T13:24:09.975Z" }, - { url = "https://files.pythonhosted.org/packages/46/fc/cb64964c3b29b432f54d1bce5e7691d693e33bbf780555151969ffd95178/pybase64-1.4.3-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:2e745f2ce760c6cf04d8a72198ef892015ddb89f6ceba489e383518ecbdb13ab", size = 72317, upload-time = "2025-12-06T13:24:11.129Z" }, - { url = "https://files.pythonhosted.org/packages/0a/b7/fab2240da6f4e1ad46f71fa56ec577613cf5df9dce2d5b4cfaa4edd0e365/pybase64-1.4.3-cp313-cp313t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fac217cd9de8581a854b0ac734c50fd1fa4b8d912396c1fc2fce7c230efe3a7", size = 75534, upload-time = "2025-12-06T13:24:12.433Z" }, - { url = "https://files.pythonhosted.org/packages/91/3b/3e2f2b6e68e3d83ddb9fa799f3548fb7449765daec9bbd005a9fbe296d7f/pybase64-1.4.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:da1ee8fa04b283873de2d6e8fa5653e827f55b86bdf1a929c5367aaeb8d26f8a", size = 65399, upload-time = "2025-12-06T13:24:13.928Z" }, - { url = "https://files.pythonhosted.org/packages/6b/08/476ac5914c3b32e0274a2524fc74f01cbf4f4af4513d054e41574eb018f6/pybase64-1.4.3-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:b0bf8e884ee822ca7b1448eeb97fa131628fe0ff42f60cae9962789bd562727f", size = 60487, upload-time = "2025-12-06T13:24:15.177Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b8/618a92915330cc9cba7880299b546a1d9dab1a21fd6c0292ee44a4fe608c/pybase64-1.4.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1bf749300382a6fd1f4f255b183146ef58f8e9cb2f44a077b3a9200dfb473a77", size = 63959, upload-time = "2025-12-06T13:24:16.854Z" }, - { url = "https://files.pythonhosted.org/packages/a5/52/af9d8d051652c3051862c442ec3861259c5cdb3fc69774bc701470bd2a59/pybase64-1.4.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:153a0e42329b92337664cfc356f2065248e6c9a1bd651bbcd6dcaf15145d3f06", size = 64874, upload-time = "2025-12-06T13:24:18.328Z" }, - { url = "https://files.pythonhosted.org/packages/e4/51/5381a7adf1f381bd184d33203692d3c57cf8ae9f250f380c3fecbdbe554b/pybase64-1.4.3-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:86ee56ac7f2184ca10217ed1c655c1a060273e233e692e9086da29d1ae1768db", size = 58572, upload-time = "2025-12-06T13:24:19.417Z" }, - { url = "https://files.pythonhosted.org/packages/e0/f0/578ee4ffce5818017de4fdf544e066c225bc435e73eb4793cde28a689d0b/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0e71a4db76726bf830b47477e7d830a75c01b2e9b01842e787a0836b0ba741e3", size = 63636, upload-time = "2025-12-06T13:24:20.497Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ad/8ae94814bf20159ea06310b742433e53d5820aa564c9fdf65bf2d79f8799/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2ba7799ec88540acd9861b10551d24656ca3c2888ecf4dba2ee0a71544a8923f", size = 56193, upload-time = "2025-12-06T13:24:21.559Z" }, - { url = "https://files.pythonhosted.org/packages/d1/31/6438cfcc3d3f0fa84d229fa125c243d5094e72628e525dfefadf3bcc6761/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2860299e4c74315f5951f0cf3e72ba0f201c3356c8a68f95a3ab4e620baf44e9", size = 72655, upload-time = "2025-12-06T13:24:22.673Z" }, - { url = "https://files.pythonhosted.org/packages/a3/0d/2bbc9e9c3fc12ba8a6e261482f03a544aca524f92eae0b4908c0a10ba481/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:bb06015db9151f0c66c10aae8e3603adab6b6cd7d1f7335a858161d92fc29618", size = 62471, upload-time = "2025-12-06T13:24:23.8Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0b/34d491e7f49c1dbdb322ea8da6adecda7c7cd70b6644557c6e4ca5c6f7c7/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:242512a070817272865d37c8909059f43003b81da31f616bb0c391ceadffe067", size = 58119, upload-time = "2025-12-06T13:24:24.994Z" }, - { url = "https://files.pythonhosted.org/packages/ce/17/c21d0cde2a6c766923ae388fc1f78291e1564b0d38c814b5ea8a0e5e081c/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5d8277554a12d3e3eed6180ebda62786bf9fc8d7bb1ee00244258f4a87ca8d20", size = 60791, upload-time = "2025-12-06T13:24:26.046Z" }, - { url = "https://files.pythonhosted.org/packages/92/b2/eaa67038916a48de12b16f4c384bcc1b84b7ec731b23613cb05f27673294/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f40b7ddd698fc1e13a4b64fbe405e4e0e1279e8197e37050e24154655f5f7c4e", size = 74701, upload-time = "2025-12-06T13:24:27.466Z" }, - { url = "https://files.pythonhosted.org/packages/42/10/abb7757c330bb869ebb95dab0c57edf5961ffbd6c095c8209cbbf75d117d/pybase64-1.4.3-cp313-cp313t-win32.whl", hash = "sha256:46d75c9387f354c5172582a9eaae153b53a53afeb9c19fcf764ea7038be3bd8b", size = 33965, upload-time = "2025-12-06T13:24:28.548Z" }, - { url = "https://files.pythonhosted.org/packages/63/a0/2d4e5a59188e9e6aed0903d580541aaea72dcbbab7bf50fb8b83b490b6c3/pybase64-1.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:d7344625591d281bec54e85cbfdab9e970f6219cac1570f2aa140b8c942ccb81", size = 36207, upload-time = "2025-12-06T13:24:29.646Z" }, - { url = "https://files.pythonhosted.org/packages/1f/05/95b902e8f567b4d4b41df768ccc438af618f8d111e54deaf57d2df46bd76/pybase64-1.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:28a3c60c55138e0028313f2eccd321fec3c4a0be75e57a8d3eb883730b1b0880", size = 31505, upload-time = "2025-12-06T13:24:30.687Z" }, - { url = "https://files.pythonhosted.org/packages/e4/80/4bd3dff423e5a91f667ca41982dc0b79495b90ec0c0f5d59aca513e50f8c/pybase64-1.4.3-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:015bb586a1ea1467f69d57427abe587469392215f59db14f1f5c39b52fdafaf5", size = 33835, upload-time = "2025-12-06T13:24:31.767Z" }, - { url = "https://files.pythonhosted.org/packages/45/60/a94d94cc1e3057f602e0b483c9ebdaef40911d84a232647a2fe593ab77bb/pybase64-1.4.3-cp314-cp314-android_24_x86_64.whl", hash = "sha256:d101e3a516f837c3dcc0e5a0b7db09582ebf99ed670865223123fb2e5839c6c0", size = 40673, upload-time = "2025-12-06T13:24:32.82Z" }, - { url = "https://files.pythonhosted.org/packages/e3/71/cf62b261d431857e8e054537a5c3c24caafa331de30daede7b2c6c558501/pybase64-1.4.3-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8f183ac925a48046abe047360fe3a1b28327afb35309892132fe1915d62fb282", size = 30939, upload-time = "2025-12-06T13:24:34.001Z" }, - { url = "https://files.pythonhosted.org/packages/24/3e/d12f92a3c1f7c6ab5d53c155bff9f1084ba997a37a39a4f781ccba9455f3/pybase64-1.4.3-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30bf3558e24dcce4da5248dcf6d73792adfcf4f504246967e9db155be4c439ad", size = 31401, upload-time = "2025-12-06T13:24:35.11Z" }, - { url = "https://files.pythonhosted.org/packages/9b/3d/9c27440031fea0d05146f8b70a460feb95d8b4e3d9ca8f45c972efb4c3d3/pybase64-1.4.3-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a674b419de318d2ce54387dd62646731efa32b4b590907800f0bd40675c1771d", size = 38075, upload-time = "2025-12-06T13:24:36.53Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d4/6c0e0cf0efd53c254173fbcd84a3d8fcbf5e0f66622473da425becec32a5/pybase64-1.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:720104fd7303d07bac302be0ff8f7f9f126f2f45c1edb4f48fdb0ff267e69fe1", size = 38257, upload-time = "2025-12-06T13:24:38.049Z" }, - { url = "https://files.pythonhosted.org/packages/50/eb/27cb0b610d5cd70f5ad0d66c14ad21c04b8db930f7139818e8fbdc14df4d/pybase64-1.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:83f1067f73fa5afbc3efc0565cecc6ed53260eccddef2ebe43a8ce2b99ea0e0a", size = 31685, upload-time = "2025-12-06T13:24:40.327Z" }, - { url = "https://files.pythonhosted.org/packages/db/26/b136a4b65e5c94ff06217f7726478df3f31ab1c777c2c02cf698e748183f/pybase64-1.4.3-cp314-cp314-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:b51204d349a4b208287a8aa5b5422be3baa88abf6cc8ff97ccbda34919bbc857", size = 68460, upload-time = "2025-12-06T13:24:41.735Z" }, - { url = "https://files.pythonhosted.org/packages/68/6d/84ce50e7ee1ae79984d689e05a9937b2460d4efa1e5b202b46762fb9036c/pybase64-1.4.3-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:30f2fd53efecbdde4bdca73a872a68dcb0d1bf8a4560c70a3e7746df973e1ef3", size = 71688, upload-time = "2025-12-06T13:24:42.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/57/6743e420416c3ff1b004041c85eb0ebd9c50e9cf05624664bfa1dc8b5625/pybase64-1.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0932b0c5cfa617091fd74f17d24549ce5de3628791998c94ba57be808078eeaf", size = 60040, upload-time = "2025-12-06T13:24:44.37Z" }, - { url = "https://files.pythonhosted.org/packages/3b/68/733324e28068a89119af2921ce548e1c607cc5c17d354690fc51c302e326/pybase64-1.4.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:acb61f5ab72bec808eb0d4ce8b87ec9f38d7d750cb89b1371c35eb8052a29f11", size = 56478, upload-time = "2025-12-06T13:24:45.815Z" }, - { url = "https://files.pythonhosted.org/packages/b5/9e/f3f4aa8cfe3357a3cdb0535b78eb032b671519d3ecc08c58c4c6b72b5a91/pybase64-1.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:2bc2d5bc15168f5c04c53bdfe5a1e543b2155f456ed1e16d7edce9ce73842021", size = 59463, upload-time = "2025-12-06T13:24:46.938Z" }, - { url = "https://files.pythonhosted.org/packages/aa/d1/53286038e1f0df1cf58abcf4a4a91b0f74ab44539c2547b6c31001ddd054/pybase64-1.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:8a7bc3cd23880bdca59758bcdd6f4ef0674f2393782763910a7466fab35ccb98", size = 60360, upload-time = "2025-12-06T13:24:48.039Z" }, - { url = "https://files.pythonhosted.org/packages/00/9a/5cc6ce95db2383d27ff4d790b8f8b46704d360d701ab77c4f655bcfaa6a7/pybase64-1.4.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ad15acf618880d99792d71e3905b0e2508e6e331b76a1b34212fa0f11e01ad28", size = 54999, upload-time = "2025-12-06T13:24:49.547Z" }, - { url = "https://files.pythonhosted.org/packages/64/e7/c3c1d09c3d7ae79e3aa1358c6d912d6b85f29281e47aa94fc0122a415a2f/pybase64-1.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448158d417139cb4851200e5fee62677ae51f56a865d50cda9e0d61bda91b116", size = 58736, upload-time = "2025-12-06T13:24:50.641Z" }, - { url = "https://files.pythonhosted.org/packages/db/d5/0baa08e3d8119b15b588c39f0d39fd10472f0372e3c54ca44649cbefa256/pybase64-1.4.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:9058c49b5a2f3e691b9db21d37eb349e62540f9f5fc4beabf8cbe3c732bead86", size = 52298, upload-time = "2025-12-06T13:24:51.791Z" }, - { url = "https://files.pythonhosted.org/packages/00/87/fc6f11474a1de7e27cd2acbb8d0d7508bda3efa73dfe91c63f968728b2a3/pybase64-1.4.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ce561724f6522907a66303aca27dce252d363fcd85884972d348f4403ba3011a", size = 69049, upload-time = "2025-12-06T13:24:53.253Z" }, - { url = "https://files.pythonhosted.org/packages/69/9d/7fb5566f669ac18b40aa5fc1c438e24df52b843c1bdc5da47d46d4c1c630/pybase64-1.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:63316560a94ac449fe86cb8b9e0a13714c659417e92e26a5cbf085cd0a0c838d", size = 57952, upload-time = "2025-12-06T13:24:54.342Z" }, - { url = "https://files.pythonhosted.org/packages/de/cc/ceb949232dbbd3ec4ee0190d1df4361296beceee9840390a63df8bc31784/pybase64-1.4.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7ecd796f2ac0be7b73e7e4e232b8c16422014de3295d43e71d2b19fd4a4f5368", size = 54484, upload-time = "2025-12-06T13:24:55.774Z" }, - { url = "https://files.pythonhosted.org/packages/a7/69/659f3c8e6a5d7b753b9c42a4bd9c42892a0f10044e9c7351a4148d413a33/pybase64-1.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d01e102a12fb2e1ed3dc11611c2818448626637857ec3994a9cf4809dfd23477", size = 56542, upload-time = "2025-12-06T13:24:57Z" }, - { url = "https://files.pythonhosted.org/packages/85/2c/29c9e6c9c82b72025f9676f9e82eb1fd2339ad038cbcbf8b9e2ac02798fc/pybase64-1.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ebff797a93c2345f22183f454fd8607a34d75eca5a3a4a969c1c75b304cee39d", size = 71045, upload-time = "2025-12-06T13:24:58.179Z" }, - { url = "https://files.pythonhosted.org/packages/b9/84/5a3dce8d7a0040a5c0c14f0fe1311cd8db872913fa04438071b26b0dac04/pybase64-1.4.3-cp314-cp314-win32.whl", hash = "sha256:28b2a1bb0828c0595dc1ea3336305cd97ff85b01c00d81cfce4f92a95fb88f56", size = 34200, upload-time = "2025-12-06T13:24:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/57/bc/ce7427c12384adee115b347b287f8f3cf65860b824d74fe2c43e37e81c1f/pybase64-1.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:33338d3888700ff68c3dedfcd49f99bfc3b887570206130926791e26b316b029", size = 36323, upload-time = "2025-12-06T13:25:01.708Z" }, - { url = "https://files.pythonhosted.org/packages/9a/1b/2b8ffbe9a96eef7e3f6a5a7be75995eebfb6faaedc85b6da6b233e50c778/pybase64-1.4.3-cp314-cp314-win_arm64.whl", hash = "sha256:62725669feb5acb186458da2f9353e88ae28ef66bb9c4c8d1568b12a790dfa94", size = 31584, upload-time = "2025-12-06T13:25:02.801Z" }, - { url = "https://files.pythonhosted.org/packages/ac/d8/6824c2e6fb45b8fa4e7d92e3c6805432d5edc7b855e3e8e1eedaaf6efb7c/pybase64-1.4.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:153fe29be038948d9372c3e77ae7d1cab44e4ba7d9aaf6f064dbeea36e45b092", size = 38601, upload-time = "2025-12-06T13:25:04.222Z" }, - { url = "https://files.pythonhosted.org/packages/ea/e5/10d2b3a4ad3a4850be2704a2f70cd9c0cf55725c8885679872d3bc846c67/pybase64-1.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f7fe3decaa7c4a9e162327ec7bd81ce183d2b16f23c6d53b606649c6e0203e9e", size = 32078, upload-time = "2025-12-06T13:25:05.362Z" }, - { url = "https://files.pythonhosted.org/packages/43/04/8b15c34d3c2282f1c1b0850f1113a249401b618a382646a895170bc9b5e7/pybase64-1.4.3-cp314-cp314t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:a5ae04ea114c86eb1da1f6e18d75f19e3b5ae39cb1d8d3cd87c29751a6a22780", size = 72474, upload-time = "2025-12-06T13:25:06.434Z" }, - { url = "https://files.pythonhosted.org/packages/42/00/f34b4d11278f8fdc68bc38f694a91492aa318f7c6f1bd7396197ac0f8b12/pybase64-1.4.3-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1755b3dce3a2a5c7d17ff6d4115e8bee4a1d5aeae74469db02e47c8f477147da", size = 75706, upload-time = "2025-12-06T13:25:07.636Z" }, - { url = "https://files.pythonhosted.org/packages/bb/5d/71747d4ad7fe16df4c4c852bdbdeb1f2cf35677b48d7c34d3011a7a6ad3a/pybase64-1.4.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb852f900e27ffc4ec1896817535a0fa19610ef8875a096b59f21d0aa42ff172", size = 65589, upload-time = "2025-12-06T13:25:08.809Z" }, - { url = "https://files.pythonhosted.org/packages/49/b1/d1e82bd58805bb5a3a662864800bab83a83a36ba56e7e3b1706c708002a5/pybase64-1.4.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:9cf21ea8c70c61eddab3421fbfce061fac4f2fb21f7031383005a1efdb13d0b9", size = 60670, upload-time = "2025-12-06T13:25:10.04Z" }, - { url = "https://files.pythonhosted.org/packages/15/67/16c609b7a13d1d9fc87eca12ba2dce5e67f949eeaab61a41bddff843cbb0/pybase64-1.4.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:afff11b331fdc27692fc75e85ae083340a35105cea1a3c4552139e2f0e0d174f", size = 64194, upload-time = "2025-12-06T13:25:11.48Z" }, - { url = "https://files.pythonhosted.org/packages/3c/11/37bc724e42960f0106c2d33dc957dcec8f760c91a908cc6c0df7718bc1a8/pybase64-1.4.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9a5143df542c1ce5c1f423874b948c4d689b3f05ec571f8792286197a39ba02", size = 64984, upload-time = "2025-12-06T13:25:12.645Z" }, - { url = "https://files.pythonhosted.org/packages/6e/66/b2b962a6a480dd5dae3029becf03ea1a650d326e39bf1c44ea3db78bb010/pybase64-1.4.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:d62e9861019ad63624b4a7914dff155af1cc5d6d79df3be14edcaedb5fdad6f9", size = 58750, upload-time = "2025-12-06T13:25:13.848Z" }, - { url = "https://files.pythonhosted.org/packages/2b/15/9b6d711035e29b18b2e1c03d47f41396d803d06ef15b6c97f45b75f73f04/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:84cfd4d92668ef5766cc42a9c9474b88960ac2b860767e6e7be255c6fddbd34a", size = 63816, upload-time = "2025-12-06T13:25:15.356Z" }, - { url = "https://files.pythonhosted.org/packages/b4/21/e2901381ed0df62e2308380f30d9c4d87d6b74e33a84faed3478d33a7197/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:60fc025437f9a7c2cc45e0c19ed68ed08ba672be2c5575fd9d98bdd8f01dd61f", size = 56348, upload-time = "2025-12-06T13:25:16.559Z" }, - { url = "https://files.pythonhosted.org/packages/c4/16/3d788388a178a0407aa814b976fe61bfa4af6760d9aac566e59da6e4a8b4/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:edc8446196f04b71d3af76c0bd1fe0a45066ac5bffecca88adb9626ee28c266f", size = 72842, upload-time = "2025-12-06T13:25:18.055Z" }, - { url = "https://files.pythonhosted.org/packages/a6/63/c15b1f8bd47ea48a5a2d52a4ec61f037062932ea6434ab916107b58e861e/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e99f6fa6509c037794da57f906ade271f52276c956d00f748e5b118462021d48", size = 62651, upload-time = "2025-12-06T13:25:19.191Z" }, - { url = "https://files.pythonhosted.org/packages/bd/b8/f544a2e37c778d59208966d4ef19742a0be37c12fc8149ff34483c176616/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d94020ef09f624d841aa9a3a6029df8cf65d60d7a6d5c8687579fa68bd679b65", size = 58295, upload-time = "2025-12-06T13:25:20.822Z" }, - { url = "https://files.pythonhosted.org/packages/03/99/1fae8a3b7ac181e36f6e7864a62d42d5b1f4fa7edf408c6711e28fba6b4d/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f64ce70d89942a23602dee910dec9b48e5edf94351e1b378186b74fcc00d7f66", size = 60960, upload-time = "2025-12-06T13:25:22.099Z" }, - { url = "https://files.pythonhosted.org/packages/9d/9e/cd4c727742345ad8384569a4466f1a1428f4e5cc94d9c2ab2f53d30be3fe/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8ea99f56e45c469818b9781903be86ba4153769f007ba0655fa3b46dc332803d", size = 74863, upload-time = "2025-12-06T13:25:23.442Z" }, - { url = "https://files.pythonhosted.org/packages/28/86/a236ecfc5b494e1e922da149689f690abc84248c7c1358f5605b8c9fdd60/pybase64-1.4.3-cp314-cp314t-win32.whl", hash = "sha256:343b1901103cc72362fd1f842524e3bb24978e31aea7ff11e033af7f373f66ab", size = 34513, upload-time = "2025-12-06T13:25:24.592Z" }, - { url = "https://files.pythonhosted.org/packages/56/ce/ca8675f8d1352e245eb012bfc75429ee9cf1f21c3256b98d9a329d44bf0f/pybase64-1.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:57aff6f7f9dea6705afac9d706432049642de5b01080d3718acc23af87c5af76", size = 36702, upload-time = "2025-12-06T13:25:25.72Z" }, - { url = "https://files.pythonhosted.org/packages/3b/30/4a675864877397179b09b720ee5fcb1cf772cf7bebc831989aff0a5f79c1/pybase64-1.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:e906aa08d4331e799400829e0f5e4177e76a3281e8a4bc82ba114c6b30e405c9", size = 31904, upload-time = "2025-12-06T13:25:26.826Z" }, - { url = "https://files.pythonhosted.org/packages/17/45/92322aec1b6979e789b5710f73c59f2172bc37c8ce835305434796824b7b/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:2baaa092f3475f3a9c87ac5198023918ea8b6c125f4c930752ab2cbe3cd1d520", size = 38746, upload-time = "2025-12-06T13:26:25.869Z" }, - { url = "https://files.pythonhosted.org/packages/11/94/f1a07402870388fdfc2ecec0c718111189732f7d0f2d7fe1386e19e8fad0/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:cde13c0764b1af07a631729f26df019070dad759981d6975527b7e8ecb465b6c", size = 32573, upload-time = "2025-12-06T13:26:27.792Z" }, - { url = "https://files.pythonhosted.org/packages/fa/8f/43c3bb11ca9bacf81cb0b7a71500bb65b2eda6d5fe07433c09b543de97f3/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5c29a582b0ea3936d02bd6fe9bf674ab6059e6e45ab71c78404ab2c913224414", size = 43461, upload-time = "2025-12-06T13:26:28.906Z" }, - { url = "https://files.pythonhosted.org/packages/2d/4c/2a5258329200be57497d3972b5308558c6de42e3749c6cc2aa1cbe34b25a/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b6b664758c804fa919b4f1257aa8cf68e95db76fc331de5f70bfc3a34655afe1", size = 36058, upload-time = "2025-12-06T13:26:30.092Z" }, - { url = "https://files.pythonhosted.org/packages/ea/6d/41faa414cde66ec023b0ca8402a8f11cb61731c3dc27c082909cbbd1f929/pybase64-1.4.3-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:f7537fa22ae56a0bf51e4b0ffc075926ad91c618e1416330939f7ef366b58e3b", size = 36231, upload-time = "2025-12-06T13:26:31.656Z" }, -] - -[[package]] -name = "pycodestyle" -version = "2.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/e0/abfd2a0d2efe47670df87f3e3a0e2edda42f055053c85361f19c0e2c1ca8/pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783", size = 39472, upload-time = "2025-06-20T18:49:48.75Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d", size = 31594, upload-time = "2025-06-20T18:49:47.491Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[package.optional-dependencies] -email = [ - { name = "email-validator" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, -] - -[[package]] -name = "pydantic-settings" -version = "2.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, -] - -[[package]] -name = "pydocket" -version = "0.16.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cloudpickle" }, - { name = "fakeredis", extra = ["lua"] }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-prometheus" }, - { name = "opentelemetry-instrumentation" }, - { name = "prometheus-client" }, - { name = "py-key-value-aio", extra = ["memory", "redis"] }, - { name = "python-json-logger" }, - { name = "redis" }, - { name = "rich" }, - { name = "typer" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/00/26befe5f58df7cd1aeda4a8d10bc7d1908ffd86b80fd995e57a2a7b3f7bd/pydocket-0.16.6.tar.gz", hash = "sha256:b96c96ad7692827214ed4ff25fcf941ec38371314db5dcc1ae792b3e9d3a0294", size = 299054, upload-time = "2026-01-09T22:09:15.405Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/3f/7483e5a6dc6326b6e0c640619b5c5bd1d6e3c20e54d58f5fb86267cef00e/pydocket-0.16.6-py3-none-any.whl", hash = "sha256:683d21e2e846aa5106274e7d59210331b242d7fb0dce5b08d3b82065663ed183", size = 67697, upload-time = "2026-01-09T22:09:13.436Z" }, -] - -[[package]] -name = "pydub" -version = "0.25.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/9a/e6bca0eed82db26562c73b5076539a4a08d3cffd19c3cc5913a3e61145fd/pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f", size = 38326, upload-time = "2021-03-10T02:09:54.659Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/53/d78dc063216e62fc55f6b2eebb447f6a4b0a59f55c8406376f76bf959b08/pydub-0.25.1-py2.py3-none-any.whl", hash = "sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6", size = 32327, upload-time = "2021-03-10T02:09:53.503Z" }, -] - -[[package]] -name = "pyflakes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/45/dc/fd034dc20b4b264b3d015808458391acbf9df40b1e54750ef175d39180b1/pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58", size = 64669, upload-time = "2025-06-20T18:45:27.834Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/2f/81d580a0fb83baeb066698975cb14a618bdbed7720678566f1b046a95fe8/pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f", size = 63551, upload-time = "2025-06-20T18:45:26.937Z" }, -] - -[[package]] -name = "pygetwindow" -version = "0.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyrect" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e1/70/c7a4f46dbf06048c6d57d9489b8e0f9c4c3d36b7479f03c5ca97eaa2541d/PyGetWindow-0.0.9.tar.gz", hash = "sha256:17894355e7d2b305cd832d717708384017c1698a90ce24f6f7fbf0242dd0a688", size = 9699, upload-time = "2020-10-04T02:12:50.806Z" } - -[[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 = "pyjwt" -version = "2.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, -] - -[package.optional-dependencies] -crypto = [ - { name = "cryptography" }, -] - -[[package]] -name = "pymongo" -version = "4.16.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "dnspython" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/65/9c/a4895c4b785fc9865a84a56e14b5bd21ca75aadc3dab79c14187cdca189b/pymongo-4.16.0.tar.gz", hash = "sha256:8ba8405065f6e258a6f872fe62d797a28f383a12178c7153c01ed04e845c600c", size = 2495323, upload-time = "2026-01-07T18:05:48.107Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/03/6dd7c53cbde98de469a3e6fb893af896dca644c476beb0f0c6342bcc368b/pymongo-4.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bd4911c40a43a821dfd93038ac824b756b6e703e26e951718522d29f6eb166a8", size = 917619, upload-time = "2026-01-07T18:04:19.173Z" }, - { url = "https://files.pythonhosted.org/packages/73/e1/328915f2734ea1f355dc9b0e98505ff670f5fab8be5e951d6ed70971c6aa/pymongo-4.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25a6b03a68f9907ea6ec8bc7cf4c58a1b51a18e23394f962a6402f8e46d41211", size = 917364, upload-time = "2026-01-07T18:04:20.861Z" }, - { url = "https://files.pythonhosted.org/packages/41/fe/4769874dd9812a1bc2880a9785e61eba5340da966af888dd430392790ae0/pymongo-4.16.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:91ac0cb0fe2bf17616c2039dac88d7c9a5088f5cb5829b27c9d250e053664d31", size = 1686901, upload-time = "2026-01-07T18:04:22.219Z" }, - { url = "https://files.pythonhosted.org/packages/fa/8d/15707b9669fdc517bbc552ac60da7124dafe7ac1552819b51e97ed4038b4/pymongo-4.16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf0ec79e8ca7077f455d14d915d629385153b6a11abc0b93283ed73a8013e376", size = 1723034, upload-time = "2026-01-07T18:04:24.055Z" }, - { url = "https://files.pythonhosted.org/packages/5b/af/3d5d16ff11d447d40c1472da1b366a31c7380d7ea2922a449c7f7f495567/pymongo-4.16.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2d0082631a7510318befc2b4fdab140481eb4b9dd62d9245e042157085da2a70", size = 1797161, upload-time = "2026-01-07T18:04:25.964Z" }, - { url = "https://files.pythonhosted.org/packages/fb/04/725ab8664eeec73ec125b5a873448d80f5d8cf2750aaaf804cbc538a50a5/pymongo-4.16.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85dc2f3444c346ea019a371e321ac868a4fab513b7a55fe368f0cc78de8177cc", size = 1780938, upload-time = "2026-01-07T18:04:28.745Z" }, - { url = "https://files.pythonhosted.org/packages/22/50/dd7e9095e1ca35f93c3c844c92eb6eb0bc491caeb2c9bff3b32fe3c9b18f/pymongo-4.16.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dabbf3c14de75a20cc3c30bf0c6527157224a93dfb605838eabb1a2ee3be008d", size = 1714342, upload-time = "2026-01-07T18:04:30.331Z" }, - { url = "https://files.pythonhosted.org/packages/03/c9/542776987d5c31ae8e93e92680ea2b6e5a2295f398b25756234cabf38a39/pymongo-4.16.0-cp312-cp312-win32.whl", hash = "sha256:60307bb91e0ab44e560fe3a211087748b2b5f3e31f403baf41f5b7b0a70bd104", size = 887868, upload-time = "2026-01-07T18:04:32.124Z" }, - { url = "https://files.pythonhosted.org/packages/2e/d4/b4045a7ccc5680fb496d01edf749c7a9367cc8762fbdf7516cf807ef679b/pymongo-4.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:f513b2c6c0d5c491f478422f6b5b5c27ac1af06a54c93ef8631806f7231bd92e", size = 907554, upload-time = "2026-01-07T18:04:33.685Z" }, - { url = "https://files.pythonhosted.org/packages/60/4c/33f75713d50d5247f2258405142c0318ff32c6f8976171c4fcae87a9dbdf/pymongo-4.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:dfc320f08ea9a7ec5b2403dc4e8150636f0d6150f4b9792faaae539c88e7db3b", size = 892971, upload-time = "2026-01-07T18:04:35.594Z" }, - { url = "https://files.pythonhosted.org/packages/47/84/148d8b5da8260f4679d6665196ae04ab14ffdf06f5fe670b0ab11942951f/pymongo-4.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d15f060bc6d0964a8bb70aba8f0cb6d11ae99715438f640cff11bbcf172eb0e8", size = 972009, upload-time = "2026-01-07T18:04:38.303Z" }, - { url = "https://files.pythonhosted.org/packages/1e/5e/9f3a8daf583d0adaaa033a3e3e58194d2282737dc164014ff33c7a081103/pymongo-4.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a19ea46a0fe71248965305a020bc076a163311aefbaa1d83e47d06fa30ac747", size = 971784, upload-time = "2026-01-07T18:04:39.669Z" }, - { url = "https://files.pythonhosted.org/packages/ad/f2/b6c24361fcde24946198573c0176406bfd5f7b8538335f3d939487055322/pymongo-4.16.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:311d4549d6bf1f8c61d025965aebb5ba29d1481dc6471693ab91610aaffbc0eb", size = 1947174, upload-time = "2026-01-07T18:04:41.368Z" }, - { url = "https://files.pythonhosted.org/packages/47/1a/8634192f98cf740b3d174e1018dd0350018607d5bd8ac35a666dc49c732b/pymongo-4.16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46ffb728d92dd5b09fc034ed91acf5595657c7ca17d4cf3751322cd554153c17", size = 1991727, upload-time = "2026-01-07T18:04:42.965Z" }, - { url = "https://files.pythonhosted.org/packages/5a/2f/0c47ac84572b28e23028a23a3798a1f725e1c23b0cf1c1424678d16aff42/pymongo-4.16.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:acda193f440dd88c2023cb00aa8bd7b93a9df59978306d14d87a8b12fe426b05", size = 2082497, upload-time = "2026-01-07T18:04:44.652Z" }, - { url = "https://files.pythonhosted.org/packages/ba/57/9f46ef9c862b2f0cf5ce798f3541c201c574128d31ded407ba4b3918d7b6/pymongo-4.16.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d9fdb386cf958e6ef6ff537d6149be7edb76c3268cd6833e6c36aa447e4443f", size = 2064947, upload-time = "2026-01-07T18:04:46.228Z" }, - { url = "https://files.pythonhosted.org/packages/b8/56/5421c0998f38e32288100a07f6cb2f5f9f352522157c901910cb2927e211/pymongo-4.16.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91899dd7fb9a8c50f09c3c1cf0cb73bfbe2737f511f641f19b9650deb61c00ca", size = 1980478, upload-time = "2026-01-07T18:04:48.017Z" }, - { url = "https://files.pythonhosted.org/packages/92/93/bfc448d025e12313a937d6e1e0101b50cc9751636b4b170e600fe3203063/pymongo-4.16.0-cp313-cp313-win32.whl", hash = "sha256:2cd60cd1e05de7f01927f8e25ca26b3ea2c09de8723241e5d3bcfdc70eaff76b", size = 934672, upload-time = "2026-01-07T18:04:49.538Z" }, - { url = "https://files.pythonhosted.org/packages/96/10/12710a5e01218d50c3dd165fd72c5ed2699285f77348a3b1a119a191d826/pymongo-4.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3ead8a0050c53eaa55935895d6919d393d0328ec24b2b9115bdbe881aa222673", size = 959237, upload-time = "2026-01-07T18:04:51.382Z" }, - { url = "https://files.pythonhosted.org/packages/0c/56/d288bcd1d05bc17ec69df1d0b1d67bc710c7c5dbef86033a5a4d2e2b08e6/pymongo-4.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:dbbc5b254c36c37d10abb50e899bc3939bbb7ab1e7c659614409af99bd3e7675", size = 940909, upload-time = "2026-01-07T18:04:52.904Z" }, - { url = "https://files.pythonhosted.org/packages/30/9e/4d343f8d0512002fce17915a89477b9f916bda1205729e042d8f23acf194/pymongo-4.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:8a254d49a9ffe9d7f888e3c677eed3729b14ce85abb08cd74732cead6ccc3c66", size = 1026634, upload-time = "2026-01-07T18:04:54.359Z" }, - { url = "https://files.pythonhosted.org/packages/c3/e3/341f88c5535df40c0450fda915f582757bb7d988cdfc92990a5e27c4c324/pymongo-4.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a1bf44e13cf2d44d2ea2e928a8140d5d667304abe1a61c4d55b4906f389fbe64", size = 1026252, upload-time = "2026-01-07T18:04:56.642Z" }, - { url = "https://files.pythonhosted.org/packages/af/64/9471b22eb98f0a2ca0b8e09393de048502111b2b5b14ab1bd9e39708aab5/pymongo-4.16.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f1c5f1f818b669875d191323a48912d3fcd2e4906410e8297bb09ac50c4d5ccc", size = 2207399, upload-time = "2026-01-07T18:04:58.255Z" }, - { url = "https://files.pythonhosted.org/packages/87/ac/47c4d50b25a02f21764f140295a2efaa583ee7f17992a5e5fa542b3a690f/pymongo-4.16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77cfd37a43a53b02b7bd930457c7994c924ad8bbe8dff91817904bcbf291b371", size = 2260595, upload-time = "2026-01-07T18:04:59.788Z" }, - { url = "https://files.pythonhosted.org/packages/ee/1b/0ce1ce9dd036417646b2fe6f63b58127acff3cf96eeb630c34ec9cd675ff/pymongo-4.16.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:36ef2fee50eee669587d742fb456e349634b4fcf8926208766078b089054b24b", size = 2366958, upload-time = "2026-01-07T18:05:01.942Z" }, - { url = "https://files.pythonhosted.org/packages/3e/3c/a5a17c0d413aa9d6c17bc35c2b472e9e79cda8068ba8e93433b5f43028e9/pymongo-4.16.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55f8d5a6fe2fa0b823674db2293f92d74cd5f970bc0360f409a1fc21003862d3", size = 2346081, upload-time = "2026-01-07T18:05:03.576Z" }, - { url = "https://files.pythonhosted.org/packages/65/19/f815533d1a88fb8a3b6c6e895bb085ffdae68ccb1e6ed7102202a307f8e2/pymongo-4.16.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9caacac0dd105e2555521002e2d17afc08665187017b466b5753e84c016628e6", size = 2246053, upload-time = "2026-01-07T18:05:05.459Z" }, - { url = "https://files.pythonhosted.org/packages/c6/88/4be3ec78828dc64b212c123114bd6ae8db5b7676085a7b43cc75d0131bd2/pymongo-4.16.0-cp314-cp314-win32.whl", hash = "sha256:c789236366525c3ee3cd6e4e450a9ff629a7d1f4d88b8e18a0aea0615fd7ecf8", size = 989461, upload-time = "2026-01-07T18:05:07.018Z" }, - { url = "https://files.pythonhosted.org/packages/af/5a/ab8d5af76421b34db483c9c8ebc3a2199fb80ae63dc7e18f4cf1df46306a/pymongo-4.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:2b0714d7764efb29bf9d3c51c964aed7c4c7237b341f9346f15ceaf8321fdb35", size = 1017803, upload-time = "2026-01-07T18:05:08.499Z" }, - { url = "https://files.pythonhosted.org/packages/f6/f4/98d68020728ac6423cf02d17cfd8226bf6cce5690b163d30d3f705e8297e/pymongo-4.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:12762e7cc0f8374a8cae3b9f9ed8dabb5d438c7b33329232dd9b7de783454033", size = 997184, upload-time = "2026-01-07T18:05:09.944Z" }, - { url = "https://files.pythonhosted.org/packages/50/00/dc3a271daf06401825b9c1f4f76f018182c7738281ea54b9762aea0560c1/pymongo-4.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1c01e8a7cd0ea66baf64a118005535ab5bf9f9eb63a1b50ac3935dccf9a54abe", size = 1083303, upload-time = "2026-01-07T18:05:11.702Z" }, - { url = "https://files.pythonhosted.org/packages/b8/4b/b5375ee21d12eababe46215011ebc63801c0d2c5ffdf203849d0d79f9852/pymongo-4.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4c4872299ebe315a79f7f922051061634a64fda95b6b17677ba57ef00b2ba2a4", size = 1083233, upload-time = "2026-01-07T18:05:13.182Z" }, - { url = "https://files.pythonhosted.org/packages/ee/e3/52efa3ca900622c7dcb56c5e70f15c906816d98905c22d2ee1f84d9a7b60/pymongo-4.16.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:78037d02389745e247fe5ab0bcad5d1ab30726eaac3ad79219c7d6bbb07eec53", size = 2527438, upload-time = "2026-01-07T18:05:14.981Z" }, - { url = "https://files.pythonhosted.org/packages/cb/96/43b1be151c734e7766c725444bcbfa1de6b60cc66bfb406203746839dd25/pymongo-4.16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c126fb72be2518395cc0465d4bae03125119136462e1945aea19840e45d89cfc", size = 2600399, upload-time = "2026-01-07T18:05:16.794Z" }, - { url = "https://files.pythonhosted.org/packages/e7/62/fa64a5045dfe3a1cd9217232c848256e7bc0136cffb7da4735c5e0d30e40/pymongo-4.16.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f3867dc225d9423c245a51eaac2cfcd53dde8e0a8d8090bb6aed6e31bd6c2d4f", size = 2720960, upload-time = "2026-01-07T18:05:18.498Z" }, - { url = "https://files.pythonhosted.org/packages/54/7b/01577eb97e605502821273a5bc16ce0fb0be5c978fe03acdbff471471202/pymongo-4.16.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f25001a955073b80510c0c3db0e043dbbc36904fd69e511c74e3d8640b8a5111", size = 2699344, upload-time = "2026-01-07T18:05:20.073Z" }, - { url = "https://files.pythonhosted.org/packages/55/68/6ef6372d516f703479c3b6cbbc45a5afd307173b1cbaccd724e23919bb1a/pymongo-4.16.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d9885aad05f82fd7ea0c9ca505d60939746b39263fa273d0125170da8f59098", size = 2577133, upload-time = "2026-01-07T18:05:22.052Z" }, - { url = "https://files.pythonhosted.org/packages/15/c7/b5337093bb01da852f945802328665f85f8109dbe91d81ea2afe5ff059b9/pymongo-4.16.0-cp314-cp314t-win32.whl", hash = "sha256:948152b30eddeae8355495f9943a3bf66b708295c0b9b6f467de1c620f215487", size = 1040560, upload-time = "2026-01-07T18:05:23.888Z" }, - { url = "https://files.pythonhosted.org/packages/96/8c/5b448cd1b103f3889d5713dda37304c81020ff88e38a826e8a75ddff4610/pymongo-4.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f6e42c1bc985d9beee884780ae6048790eb4cd565c46251932906bdb1630034a", size = 1075081, upload-time = "2026-01-07T18:05:26.874Z" }, - { url = "https://files.pythonhosted.org/packages/32/cd/ddc794cdc8500f6f28c119c624252fb6dfb19481c6d7ed150f13cf468a6d/pymongo-4.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6b2a20edb5452ac8daa395890eeb076c570790dfce6b7a44d788af74c2f8cf96", size = 1047725, upload-time = "2026-01-07T18:05:28.47Z" }, -] - -[[package]] -name = "pymsgbox" -version = "2.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/6a/e80da7594ee598a776972d09e2813df2b06b3bc29218f440631dfa7c78a8/pymsgbox-2.0.1.tar.gz", hash = "sha256:98d055c49a511dcc10fa08c3043e7102d468f5e4b3a83c6d3c61df722c7d798d", size = 20768, upload-time = "2025-09-09T00:38:56.863Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/3e/08c8cac81b2b2f7502746e6b9c8e5b0ec6432cd882c605560fc409aaf087/pymsgbox-2.0.1-py3-none-any.whl", hash = "sha256:5de8ec19bca2ca7e6c09d39c817c83f17c75cee80275235f43a9931db699f73b", size = 9994, upload-time = "2025-09-09T00:38:55.672Z" }, -] - -[[package]] -name = "pyobjc-core" -version = "12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/b6/d5612eb40be4fd5ef88c259339e6313f46ba67577a95d86c3470b951fce0/pyobjc_core-12.1.tar.gz", hash = "sha256:2bb3903f5387f72422145e1466b3ac3f7f0ef2e9960afa9bcd8961c5cbf8bd21", size = 1000532, upload-time = "2025-11-14T10:08:28.292Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/5a/6b15e499de73050f4a2c88fff664ae154307d25dc04da8fb38998a428358/pyobjc_core-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:818bcc6723561f207e5b5453efe9703f34bc8781d11ce9b8be286bb415eb4962", size = 678335, upload-time = "2025-11-14T09:32:20.107Z" }, - { url = "https://files.pythonhosted.org/packages/f4/d2/29e5e536adc07bc3d33dd09f3f7cf844bf7b4981820dc2a91dd810f3c782/pyobjc_core-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:01c0cf500596f03e21c23aef9b5f326b9fb1f8f118cf0d8b66749b6cf4cbb37a", size = 677370, upload-time = "2025-11-14T09:33:05.273Z" }, - { url = "https://files.pythonhosted.org/packages/1b/f0/4b4ed8924cd04e425f2a07269943018d43949afad1c348c3ed4d9d032787/pyobjc_core-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:177aaca84bb369a483e4961186704f64b2697708046745f8167e818d968c88fc", size = 719586, upload-time = "2025-11-14T09:33:53.302Z" }, - { url = "https://files.pythonhosted.org/packages/25/98/9f4ed07162de69603144ff480be35cd021808faa7f730d082b92f7ebf2b5/pyobjc_core-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:844515f5d86395b979d02152576e7dee9cc679acc0b32dc626ef5bda315eaa43", size = 670164, upload-time = "2025-11-14T09:34:37.458Z" }, - { url = "https://files.pythonhosted.org/packages/62/50/dc076965c96c7f0de25c0a32b7f8aa98133ed244deaeeacfc758783f1f30/pyobjc_core-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:453b191df1a4b80e756445b935491b974714456ae2cbae816840bd96f86db882", size = 712204, upload-time = "2025-11-14T09:35:24.148Z" }, -] - -[[package]] -name = "pyobjc-framework-cocoa" -version = "12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyobjc-core", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/02/a3/16ca9a15e77c061a9250afbae2eae26f2e1579eb8ca9462ae2d2c71e1169/pyobjc_framework_cocoa-12.1.tar.gz", hash = "sha256:5556c87db95711b985d5efdaaf01c917ddd41d148b1e52a0c66b1a2e2c5c1640", size = 2772191, upload-time = "2025-11-14T10:13:02.069Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/bf/ee4f27ec3920d5c6fc63c63e797c5b2cc4e20fe439217085d01ea5b63856/pyobjc_framework_cocoa-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:547c182837214b7ec4796dac5aee3aa25abc665757b75d7f44f83c994bcb0858", size = 384590, upload-time = "2025-11-14T09:41:17.336Z" }, - { url = "https://files.pythonhosted.org/packages/ad/31/0c2e734165abb46215797bd830c4bdcb780b699854b15f2b6240515edcc6/pyobjc_framework_cocoa-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5a3dcd491cacc2f5a197142b3c556d8aafa3963011110102a093349017705118", size = 384689, upload-time = "2025-11-14T09:41:41.478Z" }, - { url = "https://files.pythonhosted.org/packages/23/3b/b9f61be7b9f9b4e0a6db18b3c35c4c4d589f2d04e963e2174d38c6555a92/pyobjc_framework_cocoa-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:914b74328c22d8ca261d78c23ef2befc29776e0b85555973927b338c5734ca44", size = 388843, upload-time = "2025-11-14T09:42:05.719Z" }, - { url = "https://files.pythonhosted.org/packages/59/bb/f777cc9e775fc7dae77b569254570fe46eb842516b3e4fe383ab49eab598/pyobjc_framework_cocoa-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:03342a60fc0015bcdf9b93ac0b4f457d3938e9ef761b28df9564c91a14f0129a", size = 384932, upload-time = "2025-11-14T09:42:29.771Z" }, - { url = "https://files.pythonhosted.org/packages/58/27/b457b7b37089cad692c8aada90119162dfb4c4a16f513b79a8b2b022b33b/pyobjc_framework_cocoa-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6ba1dc1bfa4da42d04e93d2363491275fb2e2be5c20790e561c8a9e09b8cf2cc", size = 388970, upload-time = "2025-11-14T09:42:53.964Z" }, -] - -[[package]] -name = "pyobjc-framework-quartz" -version = "12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyobjc-core", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/94/18/cc59f3d4355c9456fc945eae7fe8797003c4da99212dd531ad1b0de8a0c6/pyobjc_framework_quartz-12.1.tar.gz", hash = "sha256:27f782f3513ac88ec9b6c82d9767eef95a5cf4175ce88a1e5a65875fee799608", size = 3159099, upload-time = "2025-11-14T10:21:24.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/9b/780f057e5962f690f23fdff1083a4cfda5a96d5b4d3bb49505cac4f624f2/pyobjc_framework_quartz-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7730cdce46c7e985535b5a42c31381af4aa6556e5642dc55b5e6597595e57a16", size = 218798, upload-time = "2025-11-14T10:00:01.236Z" }, - { url = "https://files.pythonhosted.org/packages/ba/2d/e8f495328101898c16c32ac10e7b14b08ff2c443a756a76fd1271915f097/pyobjc_framework_quartz-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:629b7971b1b43a11617f1460cd218bd308dfea247cd4ee3842eb40ca6f588860", size = 219206, upload-time = "2025-11-14T10:00:15.623Z" }, - { url = "https://files.pythonhosted.org/packages/67/43/b1f0ad3b842ab150a7e6b7d97f6257eab6af241b4c7d14cb8e7fde9214b8/pyobjc_framework_quartz-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:53b84e880c358ba1ddcd7e8d5ea0407d760eca58b96f0d344829162cda5f37b3", size = 224317, upload-time = "2025-11-14T10:00:30.703Z" }, - { url = "https://files.pythonhosted.org/packages/4a/00/96249c5c7e5aaca5f688ca18b8d8ad05cd7886ebd639b3c71a6a4cadbe75/pyobjc_framework_quartz-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:42d306b07f05ae7d155984503e0fb1b701fecd31dcc5c79fe8ab9790ff7e0de0", size = 219558, upload-time = "2025-11-14T10:00:45.476Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a6/708a55f3ff7a18c403b30a29a11dccfed0410485a7548c60a4b6d4cc0676/pyobjc_framework_quartz-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0cc08fddb339b2760df60dea1057453557588908e42bdc62184b6396ce2d6e9a", size = 224580, upload-time = "2025-11-14T10:01:00.091Z" }, -] - -[[package]] -name = "pyparsing" -version = "3.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, -] - -[[package]] -name = "pypdf" -version = "6.6.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/bb/a44bab1ac3c54dbcf653d7b8bcdee93dddb2d3bf025a3912cacb8149a2f2/pypdf-6.6.2.tar.gz", hash = "sha256:0a3ea3b3303982333404e22d8f75d7b3144f9cf4b2970b96856391a516f9f016", size = 5281850, upload-time = "2026-01-26T11:57:55.964Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/be/549aaf1dfa4ab4aed29b09703d2fb02c4366fc1f05e880948c296c5764b9/pypdf-6.6.2-py3-none-any.whl", hash = "sha256:44c0c9811cfb3b83b28f1c3d054531d5b8b81abaedee0d8cb403650d023832ba", size = 329132, upload-time = "2026-01-26T11:57:54.099Z" }, -] - -[[package]] -name = "pypdf2" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9f/bb/18dc3062d37db6c491392007dfd1a7f524bb95886eb956569ac38a23a784/PyPDF2-3.0.1.tar.gz", hash = "sha256:a74408f69ba6271f71b9352ef4ed03dc53a31aa404d29b5d31f53bfecfee1440", size = 227419, upload-time = "2022-12-31T10:36:13.13Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/5e/c86a5643653825d3c913719e788e41386bee415c2b87b4f955432f2de6b2/pypdf2-3.0.1-py3-none-any.whl", hash = "sha256:d16e4205cfee272fbdc0568b68d82be796540b1537508cef59388f839c191928", size = 232572, upload-time = "2022-12-31T10:36:10.327Z" }, -] - -[[package]] -name = "pyperclip" -version = "1.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, -] - -[[package]] -name = "pypika" -version = "0.51.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/9b/76b931b449fee149359bda6ffc3bf711a7a2a2e9bfd7a32c2668e2069018/pypika-0.51.0.tar.gz", hash = "sha256:ba71a4e4f320221727619401b49b93491c589d794d5347a97bf1e8dfaf8676bb", size = 80932, upload-time = "2026-02-01T18:18:44.103Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/1c/54b7a741a5e1bdd366d6767c28d74421c7191b2e2109d2b773d28d49ecc6/pypika-0.51.0-py2.py3-none-any.whl", hash = "sha256:219f14f2dcf3c0047e25bd47227d43e227fc59170ea9bb7d14f4e0945442ce3e", size = 60581, upload-time = "2026-02-01T18:18:42.187Z" }, -] - -[[package]] -name = "pyproject-hooks" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, -] - -[[package]] -name = "pyreadline3" -version = "3.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839, upload-time = "2024-09-19T02:40:10.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178, upload-time = "2024-09-19T02:40:08.598Z" }, -] - -[[package]] -name = "pyrect" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/04/2ba023d5f771b645f7be0c281cdacdcd939fe13d1deb331fc5ed1a6b3a98/PyRect-0.2.0.tar.gz", hash = "sha256:f65155f6df9b929b67caffbd57c0947c5ae5449d3b580d178074bffb47a09b78", size = 17219, upload-time = "2022-03-16T04:45:52.36Z" } - -[[package]] -name = "pyscreeze" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/f0/cb456ac4f1a73723d5b866933b7986f02bacea27516629c00f8e7da94c2d/pyscreeze-1.0.1.tar.gz", hash = "sha256:cf1662710f1b46aa5ff229ee23f367da9e20af4a78e6e365bee973cad0ead4be", size = 27826, upload-time = "2024-08-20T23:03:07.291Z" } - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, -] - -[[package]] -name = "python-jose" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ecdsa" }, - { name = "pyasn1" }, - { name = "rsa" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c6/77/3a1c9039db7124eb039772b935f2244fbb73fc8ee65b9acf2375da1c07bf/python_jose-3.5.0.tar.gz", hash = "sha256:fb4eaa44dbeb1c26dcc69e4bd7ec54a1cb8dd64d3b4d81ef08d90ff453f2b01b", size = 92726, upload-time = "2025-05-28T17:31:54.288Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/c3/0bd11992072e6a1c513b16500a5d07f91a24017c5909b02c72c62d7ad024/python_jose-3.5.0-py2.py3-none-any.whl", hash = "sha256:abd1202f23d34dfad2c3d28cb8617b90acf34132c7afd60abd0b0b7d3cb55771", size = 34624, upload-time = "2025-05-28T17:31:52.802Z" }, -] - -[package.optional-dependencies] -cryptography = [ - { name = "cryptography" }, -] - -[[package]] -name = "python-json-logger" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, -] - -[[package]] -name = "python-pptx" -version = "1.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "lxml" }, - { name = "pillow" }, - { name = "typing-extensions" }, - { name = "xlsxwriter" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/52/a9/0c0db8d37b2b8a645666f7fd8accea4c6224e013c42b1d5c17c93590cd06/python_pptx-1.0.2.tar.gz", hash = "sha256:479a8af0eaf0f0d76b6f00b0887732874ad2e3188230315290cd1f9dd9cc7095", size = 10109297, upload-time = "2024-08-07T17:33:37.772Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/4f/00be2196329ebbff56ce564aa94efb0fbc828d00de250b1980de1a34ab49/python_pptx-1.0.2-py3-none-any.whl", hash = "sha256:160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba", size = 472788, upload-time = "2024-08-07T17:33:28.192Z" }, -] - -[[package]] -name = "python3-xlib" -version = "0.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ef/c6/2c5999de3bb1533521f1101e8fe56fd9c266732f4d48011c7c69b29d12ae/python3-xlib-0.15.tar.gz", hash = "sha256:dc4245f3ae4aa5949c1d112ee4723901ade37a96721ba9645f2bfa56e5b383f8", size = 132828, upload-time = "2014-05-31T12:28:59.603Z" } - -[[package]] -name = "pytweening" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/0c/c16bc93ac2755bac0066a8ecbd2a2931a1735a6fffd99a2b9681c7e83e90/pytweening-1.2.0.tar.gz", hash = "sha256:243318b7736698066c5f362ec5c2b6434ecf4297c3c8e7caa8abfe6af4cac71b", size = 171241, upload-time = "2024-02-20T03:37:56.809Z" } - -[[package]] -name = "pytz" -version = "2025.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, -] - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { 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 = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "qdrant-client" -version = "1.16.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "grpcio" }, - { name = "httpx", extra = ["http2"] }, - { name = "numpy" }, - { name = "portalocker" }, - { name = "protobuf" }, - { name = "pydantic" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ca/7d/3cd10e26ae97b35cf856ca1dc67576e42414ae39502c51165bb36bb1dff8/qdrant_client-1.16.2.tar.gz", hash = "sha256:ca4ef5f9be7b5eadeec89a085d96d5c723585a391eb8b2be8192919ab63185f0", size = 331112, upload-time = "2025-12-12T10:58:30.866Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/13/8ce16f808297e16968269de44a14f4fef19b64d9766be1d6ba5ba78b579d/qdrant_client-1.16.2-py3-none-any.whl", hash = "sha256:442c7ef32ae0f005e88b5d3c0783c63d4912b97ae756eb5e052523be682f17d3", size = 377186, upload-time = "2025-12-12T10:58:29.282Z" }, -] - -[[package]] -name = "qrcode" -version = "8.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8f/b2/7fc2931bfae0af02d5f53b174e9cf701adbb35f39d69c2af63d4a39f81a9/qrcode-8.2.tar.gz", hash = "sha256:35c3f2a4172b33136ab9f6b3ef1c00260dd2f66f858f24d88418a015f446506c", size = 43317, upload-time = "2025-05-01T15:44:24.726Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/b8/d2d6d731733f51684bbf76bf34dab3b70a9148e8f2cef2bb544fccec681a/qrcode-8.2-py3-none-any.whl", hash = "sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f", size = 45986, upload-time = "2025-05-01T15:44:22.781Z" }, -] - -[[package]] -name = "rapidfuzz" -version = "3.14.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/28/9d808fe62375b9aab5ba92fa9b29371297b067c2790b2d7cda648b1e2f8d/rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f", size = 57863900, upload-time = "2025-11-01T11:54:52.321Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/8e/3c215e860b458cfbedb3ed73bc72e98eb7e0ed72f6b48099604a7a3260c2/rapidfuzz-3.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:685c93ea961d135893b5984a5a9851637d23767feabe414ec974f43babbd8226", size = 1945306, upload-time = "2025-11-01T11:53:06.452Z" }, - { url = "https://files.pythonhosted.org/packages/36/d9/31b33512015c899f4a6e6af64df8dfe8acddf4c8b40a4b3e0e6e1bcd00e5/rapidfuzz-3.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fa7c8f26f009f8c673fbfb443792f0cf8cf50c4e18121ff1e285b5e08a94fbdb", size = 1390788, upload-time = "2025-11-01T11:53:08.721Z" }, - { url = "https://files.pythonhosted.org/packages/a9/67/2ee6f8de6e2081ccd560a571d9c9063184fe467f484a17fa90311a7f4a2e/rapidfuzz-3.14.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57f878330c8d361b2ce76cebb8e3e1dc827293b6abf404e67d53260d27b5d941", size = 1374580, upload-time = "2025-11-01T11:53:10.164Z" }, - { url = "https://files.pythonhosted.org/packages/30/83/80d22997acd928eda7deadc19ccd15883904622396d6571e935993e0453a/rapidfuzz-3.14.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c5f545f454871e6af05753a0172849c82feaf0f521c5ca62ba09e1b382d6382", size = 3154947, upload-time = "2025-11-01T11:53:12.093Z" }, - { url = "https://files.pythonhosted.org/packages/5b/cf/9f49831085a16384695f9fb096b99662f589e30b89b4a589a1ebc1a19d34/rapidfuzz-3.14.3-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:07aa0b5d8863e3151e05026a28e0d924accf0a7a3b605da978f0359bb804df43", size = 1223872, upload-time = "2025-11-01T11:53:13.664Z" }, - { url = "https://files.pythonhosted.org/packages/c8/0f/41ee8034e744b871c2e071ef0d360686f5ccfe5659f4fd96c3ec406b3c8b/rapidfuzz-3.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73b07566bc7e010e7b5bd490fb04bb312e820970180df6b5655e9e6224c137db", size = 2392512, upload-time = "2025-11-01T11:53:15.109Z" }, - { url = "https://files.pythonhosted.org/packages/da/86/280038b6b0c2ccec54fb957c732ad6b41cc1fd03b288d76545b9cf98343f/rapidfuzz-3.14.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6de00eb84c71476af7d3110cf25d8fe7c792d7f5fa86764ef0b4ca97e78ca3ed", size = 2521398, upload-time = "2025-11-01T11:53:17.146Z" }, - { url = "https://files.pythonhosted.org/packages/fa/7b/05c26f939607dca0006505e3216248ae2de631e39ef94dd63dbbf0860021/rapidfuzz-3.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d7843a1abf0091773a530636fdd2a49a41bcae22f9910b86b4f903e76ddc82dc", size = 4259416, upload-time = "2025-11-01T11:53:19.34Z" }, - { url = "https://files.pythonhosted.org/packages/40/eb/9e3af4103d91788f81111af1b54a28de347cdbed8eaa6c91d5e98a889aab/rapidfuzz-3.14.3-cp312-cp312-win32.whl", hash = "sha256:dea97ac3ca18cd3ba8f3d04b5c1fe4aa60e58e8d9b7793d3bd595fdb04128d7a", size = 1709527, upload-time = "2025-11-01T11:53:20.949Z" }, - { url = "https://files.pythonhosted.org/packages/b8/63/d06ecce90e2cf1747e29aeab9f823d21e5877a4c51b79720b2d3be7848f8/rapidfuzz-3.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:b5100fd6bcee4d27f28f4e0a1c6b5127bc8ba7c2a9959cad9eab0bf4a7ab3329", size = 1538989, upload-time = "2025-11-01T11:53:22.428Z" }, - { url = "https://files.pythonhosted.org/packages/fc/6d/beee32dcda64af8128aab3ace2ccb33d797ed58c434c6419eea015fec779/rapidfuzz-3.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:4e49c9e992bc5fc873bd0fff7ef16a4405130ec42f2ce3d2b735ba5d3d4eb70f", size = 811161, upload-time = "2025-11-01T11:53:23.811Z" }, - { url = "https://files.pythonhosted.org/packages/e4/4f/0d94d09646853bd26978cb3a7541b6233c5760687777fa97da8de0d9a6ac/rapidfuzz-3.14.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dbcb726064b12f356bf10fffdb6db4b6dce5390b23627c08652b3f6e49aa56ae", size = 1939646, upload-time = "2025-11-01T11:53:25.292Z" }, - { url = "https://files.pythonhosted.org/packages/b6/eb/f96aefc00f3bbdbab9c0657363ea8437a207d7545ac1c3789673e05d80bd/rapidfuzz-3.14.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1704fc70d214294e554a2421b473779bcdeef715881c5e927dc0f11e1692a0ff", size = 1385512, upload-time = "2025-11-01T11:53:27.594Z" }, - { url = "https://files.pythonhosted.org/packages/26/34/71c4f7749c12ee223dba90017a5947e8f03731a7cc9f489b662a8e9e643d/rapidfuzz-3.14.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc65e72790ddfd310c2c8912b45106e3800fefe160b0c2ef4d6b6fec4e826457", size = 1373571, upload-time = "2025-11-01T11:53:29.096Z" }, - { url = "https://files.pythonhosted.org/packages/32/00/ec8597a64f2be301ce1ee3290d067f49f6a7afb226b67d5f15b56d772ba5/rapidfuzz-3.14.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e38c1305cffae8472572a0584d4ffc2f130865586a81038ca3965301f7c97c", size = 3156759, upload-time = "2025-11-01T11:53:30.777Z" }, - { url = "https://files.pythonhosted.org/packages/61/d5/b41eeb4930501cc899d5a9a7b5c9a33d85a670200d7e81658626dcc0ecc0/rapidfuzz-3.14.3-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:e195a77d06c03c98b3fc06b8a28576ba824392ce40de8c708f96ce04849a052e", size = 1222067, upload-time = "2025-11-01T11:53:32.334Z" }, - { url = "https://files.pythonhosted.org/packages/2a/7d/6d9abb4ffd1027c6ed837b425834f3bed8344472eb3a503ab55b3407c721/rapidfuzz-3.14.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1b7ef2f4b8583a744338a18f12c69693c194fb6777c0e9ada98cd4d9e8f09d10", size = 2394775, upload-time = "2025-11-01T11:53:34.24Z" }, - { url = "https://files.pythonhosted.org/packages/15/ce/4f3ab4c401c5a55364da1ffff8cc879fc97b4e5f4fa96033827da491a973/rapidfuzz-3.14.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a2135b138bcdcb4c3742d417f215ac2d8c2b87bde15b0feede231ae95f09ec41", size = 2526123, upload-time = "2025-11-01T11:53:35.779Z" }, - { url = "https://files.pythonhosted.org/packages/c1/4b/54f804975376a328f57293bd817c12c9036171d15cf7292032e3f5820b2d/rapidfuzz-3.14.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33a325ed0e8e1aa20c3e75f8ab057a7b248fdea7843c2a19ade0008906c14af0", size = 4262874, upload-time = "2025-11-01T11:53:37.866Z" }, - { url = "https://files.pythonhosted.org/packages/e9/b6/958db27d8a29a50ee6edd45d33debd3ce732e7209183a72f57544cd5fe22/rapidfuzz-3.14.3-cp313-cp313-win32.whl", hash = "sha256:8383b6d0d92f6cd008f3c9216535be215a064b2cc890398a678b56e6d280cb63", size = 1707972, upload-time = "2025-11-01T11:53:39.442Z" }, - { url = "https://files.pythonhosted.org/packages/07/75/fde1f334b0cec15b5946d9f84d73250fbfcc73c236b4bc1b25129d90876b/rapidfuzz-3.14.3-cp313-cp313-win_amd64.whl", hash = "sha256:e6b5e3036976f0fde888687d91be86d81f9ac5f7b02e218913c38285b756be6c", size = 1537011, upload-time = "2025-11-01T11:53:40.92Z" }, - { url = "https://files.pythonhosted.org/packages/2e/d7/d83fe001ce599dc7ead57ba1debf923dc961b6bdce522b741e6b8c82f55c/rapidfuzz-3.14.3-cp313-cp313-win_arm64.whl", hash = "sha256:7ba009977601d8b0828bfac9a110b195b3e4e79b350dcfa48c11269a9f1918a0", size = 810744, upload-time = "2025-11-01T11:53:42.723Z" }, - { url = "https://files.pythonhosted.org/packages/92/13/a486369e63ff3c1a58444d16b15c5feb943edd0e6c28a1d7d67cb8946b8f/rapidfuzz-3.14.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0a28add871425c2fe94358c6300bbeb0bc2ed828ca003420ac6825408f5a424", size = 1967702, upload-time = "2025-11-01T11:53:44.554Z" }, - { url = "https://files.pythonhosted.org/packages/f1/82/efad25e260b7810f01d6b69122685e355bed78c94a12784bac4e0beb2afb/rapidfuzz-3.14.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:010e12e2411a4854b0434f920e72b717c43f8ec48d57e7affe5c42ecfa05dd0e", size = 1410702, upload-time = "2025-11-01T11:53:46.066Z" }, - { url = "https://files.pythonhosted.org/packages/ba/1a/34c977b860cde91082eae4a97ae503f43e0d84d4af301d857679b66f9869/rapidfuzz-3.14.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cfc3d57abd83c734d1714ec39c88a34dd69c85474918ebc21296f1e61eb5ca8", size = 1382337, upload-time = "2025-11-01T11:53:47.62Z" }, - { url = "https://files.pythonhosted.org/packages/88/74/f50ea0e24a5880a9159e8fd256b84d8f4634c2f6b4f98028bdd31891d907/rapidfuzz-3.14.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89acb8cbb52904f763e5ac238083b9fc193bed8d1f03c80568b20e4cef43a519", size = 3165563, upload-time = "2025-11-01T11:53:49.216Z" }, - { url = "https://files.pythonhosted.org/packages/e8/7a/e744359404d7737049c26099423fc54bcbf303de5d870d07d2fb1410f567/rapidfuzz-3.14.3-cp313-cp313t-manylinux_2_31_armv7l.whl", hash = "sha256:7d9af908c2f371bfb9c985bd134e295038e3031e666e4b2ade1e7cb7f5af2f1a", size = 1214727, upload-time = "2025-11-01T11:53:50.883Z" }, - { url = "https://files.pythonhosted.org/packages/d3/2e/87adfe14ce75768ec6c2b8acd0e05e85e84be4be5e3d283cdae360afc4fe/rapidfuzz-3.14.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1f1925619627f8798f8c3a391d81071336942e5fe8467bc3c567f982e7ce2897", size = 2403349, upload-time = "2025-11-01T11:53:52.322Z" }, - { url = "https://files.pythonhosted.org/packages/70/17/6c0b2b2bff9c8b12e12624c07aa22e922b0c72a490f180fa9183d1ef2c75/rapidfuzz-3.14.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:152555187360978119e98ce3e8263d70dd0c40c7541193fc302e9b7125cf8f58", size = 2507596, upload-time = "2025-11-01T11:53:53.835Z" }, - { url = "https://files.pythonhosted.org/packages/c3/d1/87852a7cbe4da7b962174c749a47433881a63a817d04f3e385ea9babcd9e/rapidfuzz-3.14.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:52619d25a09546b8db078981ca88939d72caa6b8701edd8b22e16482a38e799f", size = 4273595, upload-time = "2025-11-01T11:53:55.961Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ab/1d0354b7d1771a28fa7fe089bc23acec2bdd3756efa2419f463e3ed80e16/rapidfuzz-3.14.3-cp313-cp313t-win32.whl", hash = "sha256:489ce98a895c98cad284f0a47960c3e264c724cb4cfd47a1430fa091c0c25204", size = 1757773, upload-time = "2025-11-01T11:53:57.628Z" }, - { url = "https://files.pythonhosted.org/packages/0b/0c/71ef356adc29e2bdf74cd284317b34a16b80258fa0e7e242dd92cc1e6d10/rapidfuzz-3.14.3-cp313-cp313t-win_amd64.whl", hash = "sha256:656e52b054d5b5c2524169240e50cfa080b04b1c613c5f90a2465e84888d6f15", size = 1576797, upload-time = "2025-11-01T11:53:59.455Z" }, - { url = "https://files.pythonhosted.org/packages/fe/d2/0e64fc27bb08d4304aa3d11154eb5480bcf5d62d60140a7ee984dc07468a/rapidfuzz-3.14.3-cp313-cp313t-win_arm64.whl", hash = "sha256:c7e40c0a0af02ad6e57e89f62bef8604f55a04ecae90b0ceeda591bbf5923317", size = 829940, upload-time = "2025-11-01T11:54:01.1Z" }, - { url = "https://files.pythonhosted.org/packages/32/6f/1b88aaeade83abc5418788f9e6b01efefcd1a69d65ded37d89cd1662be41/rapidfuzz-3.14.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:442125473b247227d3f2de807a11da6c08ccf536572d1be943f8e262bae7e4ea", size = 1942086, upload-time = "2025-11-01T11:54:02.592Z" }, - { url = "https://files.pythonhosted.org/packages/a0/2c/b23861347436cb10f46c2bd425489ec462790faaa360a54a7ede5f78de88/rapidfuzz-3.14.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ec0c8c0c3d4f97ced46b2e191e883f8c82dbbf6d5ebc1842366d7eff13cd5a6", size = 1386993, upload-time = "2025-11-01T11:54:04.12Z" }, - { url = "https://files.pythonhosted.org/packages/83/86/5d72e2c060aa1fbdc1f7362d938f6b237dff91f5b9fc5dd7cc297e112250/rapidfuzz-3.14.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2dc37bc20272f388b8c3a4eba4febc6e77e50a8f450c472def4751e7678f55e4", size = 1379126, upload-time = "2025-11-01T11:54:05.777Z" }, - { url = "https://files.pythonhosted.org/packages/c9/bc/ef2cee3e4d8b3fc22705ff519f0d487eecc756abdc7c25d53686689d6cf2/rapidfuzz-3.14.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dee362e7e79bae940a5e2b3f6d09c6554db6a4e301cc68343886c08be99844f1", size = 3159304, upload-time = "2025-11-01T11:54:07.351Z" }, - { url = "https://files.pythonhosted.org/packages/a0/36/dc5f2f62bbc7bc90be1f75eeaf49ed9502094bb19290dfb4747317b17f12/rapidfuzz-3.14.3-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:4b39921df948388a863f0e267edf2c36302983459b021ab928d4b801cbe6a421", size = 1218207, upload-time = "2025-11-01T11:54:09.641Z" }, - { url = "https://files.pythonhosted.org/packages/df/7e/8f4be75c1bc62f47edf2bbbe2370ee482fae655ebcc4718ac3827ead3904/rapidfuzz-3.14.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:beda6aa9bc44d1d81242e7b291b446be352d3451f8217fcb068fc2933927d53b", size = 2401245, upload-time = "2025-11-01T11:54:11.543Z" }, - { url = "https://files.pythonhosted.org/packages/05/38/f7c92759e1bb188dd05b80d11c630ba59b8d7856657baf454ff56059c2ab/rapidfuzz-3.14.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:6a014ba09657abfcfeed64b7d09407acb29af436d7fc075b23a298a7e4a6b41c", size = 2518308, upload-time = "2025-11-01T11:54:13.134Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ac/85820f70fed5ecb5f1d9a55f1e1e2090ef62985ef41db289b5ac5ec56e28/rapidfuzz-3.14.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:32eeafa3abce138bb725550c0e228fc7eaeec7059aa8093d9cbbec2b58c2371a", size = 4265011, upload-time = "2025-11-01T11:54:15.087Z" }, - { url = "https://files.pythonhosted.org/packages/46/a9/616930721ea9835c918af7cde22bff17f9db3639b0c1a7f96684be7f5630/rapidfuzz-3.14.3-cp314-cp314-win32.whl", hash = "sha256:adb44d996fc610c7da8c5048775b21db60dd63b1548f078e95858c05c86876a3", size = 1742245, upload-time = "2025-11-01T11:54:17.19Z" }, - { url = "https://files.pythonhosted.org/packages/06/8a/f2fa5e9635b1ccafda4accf0e38246003f69982d7c81f2faa150014525a4/rapidfuzz-3.14.3-cp314-cp314-win_amd64.whl", hash = "sha256:f3d15d8527e2b293e38ce6e437631af0708df29eafd7c9fc48210854c94472f9", size = 1584856, upload-time = "2025-11-01T11:54:18.764Z" }, - { url = "https://files.pythonhosted.org/packages/ef/97/09e20663917678a6d60d8e0e29796db175b1165e2079830430342d5298be/rapidfuzz-3.14.3-cp314-cp314-win_arm64.whl", hash = "sha256:576e4b9012a67e0bf54fccb69a7b6c94d4e86a9540a62f1a5144977359133583", size = 833490, upload-time = "2025-11-01T11:54:20.753Z" }, - { url = "https://files.pythonhosted.org/packages/03/1b/6b6084576ba87bf21877c77218a0c97ba98cb285b0c02eaaee3acd7c4513/rapidfuzz-3.14.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:cec3c0da88562727dd5a5a364bd9efeb535400ff0bfb1443156dd139a1dd7b50", size = 1968658, upload-time = "2025-11-01T11:54:22.25Z" }, - { url = "https://files.pythonhosted.org/packages/38/c0/fb02a0db80d95704b0a6469cc394e8c38501abf7e1c0b2afe3261d1510c2/rapidfuzz-3.14.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d1fa009f8b1100e4880868137e7bf0501422898f7674f2adcd85d5a67f041296", size = 1410742, upload-time = "2025-11-01T11:54:23.863Z" }, - { url = "https://files.pythonhosted.org/packages/a4/72/3fbf12819fc6afc8ec75a45204013b40979d068971e535a7f3512b05e765/rapidfuzz-3.14.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b86daa7419b5e8b180690efd1fdbac43ff19230803282521c5b5a9c83977655", size = 1382810, upload-time = "2025-11-01T11:54:25.571Z" }, - { url = "https://files.pythonhosted.org/packages/0f/18/0f1991d59bb7eee28922a00f79d83eafa8c7bfb4e8edebf4af2a160e7196/rapidfuzz-3.14.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7bd1816db05d6c5ffb3a4df0a2b7b56fb8c81ef584d08e37058afa217da91b1", size = 3166349, upload-time = "2025-11-01T11:54:27.195Z" }, - { url = "https://files.pythonhosted.org/packages/0d/f0/baa958b1989c8f88c78bbb329e969440cf330b5a01a982669986495bb980/rapidfuzz-3.14.3-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:33da4bbaf44e9755b0ce192597f3bde7372fe2e381ab305f41b707a95ac57aa7", size = 1214994, upload-time = "2025-11-01T11:54:28.821Z" }, - { url = "https://files.pythonhosted.org/packages/e4/a0/cd12ec71f9b2519a3954febc5740291cceabc64c87bc6433afcb36259f3b/rapidfuzz-3.14.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3fecce764cf5a991ee2195a844196da840aba72029b2612f95ac68a8b74946bf", size = 2403919, upload-time = "2025-11-01T11:54:30.393Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ce/019bd2176c1644098eced4f0595cb4b3ef52e4941ac9a5854f209d0a6e16/rapidfuzz-3.14.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:ecd7453e02cf072258c3a6b8e930230d789d5d46cc849503729f9ce475d0e785", size = 2508346, upload-time = "2025-11-01T11:54:32.048Z" }, - { url = "https://files.pythonhosted.org/packages/23/f8/be16c68e2c9e6c4f23e8f4adbb7bccc9483200087ed28ff76c5312da9b14/rapidfuzz-3.14.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea188aa00e9bcae8c8411f006a5f2f06c4607a02f24eab0d8dc58566aa911f35", size = 4274105, upload-time = "2025-11-01T11:54:33.701Z" }, - { url = "https://files.pythonhosted.org/packages/a1/d1/5ab148e03f7e6ec8cd220ccf7af74d3aaa4de26dd96df58936beb7cba820/rapidfuzz-3.14.3-cp314-cp314t-win32.whl", hash = "sha256:7ccbf68100c170e9a0581accbe9291850936711548c6688ce3bfb897b8c589ad", size = 1793465, upload-time = "2025-11-01T11:54:35.331Z" }, - { url = "https://files.pythonhosted.org/packages/cd/97/433b2d98e97abd9fff1c470a109b311669f44cdec8d0d5aa250aceaed1fb/rapidfuzz-3.14.3-cp314-cp314t-win_amd64.whl", hash = "sha256:9ec02e62ae765a318d6de38df609c57fc6dacc65c0ed1fd489036834fd8a620c", size = 1623491, upload-time = "2025-11-01T11:54:38.085Z" }, - { url = "https://files.pythonhosted.org/packages/e2/f6/e2176eb94f94892441bce3ddc514c179facb65db245e7ce3356965595b19/rapidfuzz-3.14.3-cp314-cp314t-win_arm64.whl", hash = "sha256:e805e52322ae29aa945baf7168b6c898120fbc16d2b8f940b658a5e9e3999253", size = 851487, upload-time = "2025-11-01T11:54:40.176Z" }, -] - -[[package]] -name = "redis" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/c8/983d5c6579a411d8a99bc5823cc5712768859b5ce2c8afe1a65b37832c81/redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c", size = 4796669, upload-time = "2025-11-19T15:54:39.961Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/f0/8956f8a86b20d7bb9d6ac0187cf4cd54d8065bc9a1a09eb8011d4d326596/redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b", size = 354159, upload-time = "2025-11-19T15:54:38.064Z" }, -] - -[[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 = "regex" -version = "2026.1.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/86/07d5056945f9ec4590b518171c4254a5925832eb727b56d3c38a7476f316/regex-2026.1.15.tar.gz", hash = "sha256:164759aa25575cbc0651bef59a0b18353e54300d79ace8084c818ad8ac72b7d5", size = 414811, upload-time = "2026-01-14T23:18:02.775Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/81/10d8cf43c807d0326efe874c1b79f22bfb0fb226027b0b19ebc26d301408/regex-2026.1.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4c8fcc5793dde01641a35905d6731ee1548f02b956815f8f1cab89e515a5bdf1", size = 489398, upload-time = "2026-01-14T23:14:43.741Z" }, - { url = "https://files.pythonhosted.org/packages/90/b0/7c2a74e74ef2a7c32de724658a69a862880e3e4155cba992ba04d1c70400/regex-2026.1.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bfd876041a956e6a90ad7cdb3f6a630c07d491280bfeed4544053cd434901681", size = 291339, upload-time = "2026-01-14T23:14:45.183Z" }, - { url = "https://files.pythonhosted.org/packages/19/4d/16d0773d0c818417f4cc20aa0da90064b966d22cd62a8c46765b5bd2d643/regex-2026.1.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9250d087bc92b7d4899ccd5539a1b2334e44eee85d848c4c1aef8e221d3f8c8f", size = 289003, upload-time = "2026-01-14T23:14:47.25Z" }, - { url = "https://files.pythonhosted.org/packages/c6/e4/1fc4599450c9f0863d9406e944592d968b8d6dfd0d552a7d569e43bceada/regex-2026.1.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8a154cf6537ebbc110e24dabe53095e714245c272da9c1be05734bdad4a61aa", size = 798656, upload-time = "2026-01-14T23:14:48.77Z" }, - { url = "https://files.pythonhosted.org/packages/b2/e6/59650d73a73fa8a60b3a590545bfcf1172b4384a7df2e7fe7b9aab4e2da9/regex-2026.1.15-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8050ba2e3ea1d8731a549e83c18d2f0999fbc99a5f6bd06b4c91449f55291804", size = 864252, upload-time = "2026-01-14T23:14:50.528Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ab/1d0f4d50a1638849a97d731364c9a80fa304fec46325e48330c170ee8e80/regex-2026.1.15-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf065240704cb8951cc04972cf107063917022511273e0969bdb34fc173456c", size = 912268, upload-time = "2026-01-14T23:14:52.952Z" }, - { url = "https://files.pythonhosted.org/packages/dd/df/0d722c030c82faa1d331d1921ee268a4e8fb55ca8b9042c9341c352f17fa/regex-2026.1.15-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c32bef3e7aeee75746748643667668ef941d28b003bfc89994ecf09a10f7a1b5", size = 803589, upload-time = "2026-01-14T23:14:55.182Z" }, - { url = "https://files.pythonhosted.org/packages/66/23/33289beba7ccb8b805c6610a8913d0131f834928afc555b241caabd422a9/regex-2026.1.15-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d5eaa4a4c5b1906bd0d2508d68927f15b81821f85092e06f1a34a4254b0e1af3", size = 775700, upload-time = "2026-01-14T23:14:56.707Z" }, - { url = "https://files.pythonhosted.org/packages/e7/65/bf3a42fa6897a0d3afa81acb25c42f4b71c274f698ceabd75523259f6688/regex-2026.1.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:86c1077a3cc60d453d4084d5b9649065f3bf1184e22992bd322e1f081d3117fb", size = 787928, upload-time = "2026-01-14T23:14:58.312Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f5/13bf65864fc314f68cdd6d8ca94adcab064d4d39dbd0b10fef29a9da48fc/regex-2026.1.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:2b091aefc05c78d286657cd4db95f2e6313375ff65dcf085e42e4c04d9c8d410", size = 858607, upload-time = "2026-01-14T23:15:00.657Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/040e589834d7a439ee43fb0e1e902bc81bd58a5ba81acffe586bb3321d35/regex-2026.1.15-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:57e7d17f59f9ebfa9667e6e5a1c0127b96b87cb9cede8335482451ed00788ba4", size = 763729, upload-time = "2026-01-14T23:15:02.248Z" }, - { url = "https://files.pythonhosted.org/packages/9b/84/6921e8129687a427edf25a34a5594b588b6d88f491320b9de5b6339a4fcb/regex-2026.1.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c6c4dcdfff2c08509faa15d36ba7e5ef5fcfab25f1e8f85a0c8f45bc3a30725d", size = 850697, upload-time = "2026-01-14T23:15:03.878Z" }, - { url = "https://files.pythonhosted.org/packages/8a/87/3d06143d4b128f4229158f2de5de6c8f2485170c7221e61bf381313314b2/regex-2026.1.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf8ff04c642716a7f2048713ddc6278c5fd41faa3b9cab12607c7abecd012c22", size = 789849, upload-time = "2026-01-14T23:15:06.102Z" }, - { url = "https://files.pythonhosted.org/packages/77/69/c50a63842b6bd48850ebc7ab22d46e7a2a32d824ad6c605b218441814639/regex-2026.1.15-cp312-cp312-win32.whl", hash = "sha256:82345326b1d8d56afbe41d881fdf62f1926d7264b2fc1537f99ae5da9aad7913", size = 266279, upload-time = "2026-01-14T23:15:07.678Z" }, - { url = "https://files.pythonhosted.org/packages/f2/36/39d0b29d087e2b11fd8191e15e81cce1b635fcc845297c67f11d0d19274d/regex-2026.1.15-cp312-cp312-win_amd64.whl", hash = "sha256:4def140aa6156bc64ee9912383d4038f3fdd18fee03a6f222abd4de6357ce42a", size = 277166, upload-time = "2026-01-14T23:15:09.257Z" }, - { url = "https://files.pythonhosted.org/packages/28/32/5b8e476a12262748851fa8ab1b0be540360692325975b094e594dfebbb52/regex-2026.1.15-cp312-cp312-win_arm64.whl", hash = "sha256:c6c565d9a6e1a8d783c1948937ffc377dd5771e83bd56de8317c450a954d2056", size = 270415, upload-time = "2026-01-14T23:15:10.743Z" }, - { url = "https://files.pythonhosted.org/packages/f8/2e/6870bb16e982669b674cce3ee9ff2d1d46ab80528ee6bcc20fb2292efb60/regex-2026.1.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e69d0deeb977ffe7ed3d2e4439360089f9c3f217ada608f0f88ebd67afb6385e", size = 489164, upload-time = "2026-01-14T23:15:13.962Z" }, - { url = "https://files.pythonhosted.org/packages/dc/67/9774542e203849b0286badf67199970a44ebdb0cc5fb739f06e47ada72f8/regex-2026.1.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3601ffb5375de85a16f407854d11cca8fe3f5febbe3ac78fb2866bb220c74d10", size = 291218, upload-time = "2026-01-14T23:15:15.647Z" }, - { url = "https://files.pythonhosted.org/packages/b2/87/b0cda79f22b8dee05f774922a214da109f9a4c0eca5da2c9d72d77ea062c/regex-2026.1.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4c5ef43b5c2d4114eb8ea424bb8c9cec01d5d17f242af88b2448f5ee81caadbc", size = 288895, upload-time = "2026-01-14T23:15:17.788Z" }, - { url = "https://files.pythonhosted.org/packages/3b/6a/0041f0a2170d32be01ab981d6346c83a8934277d82c780d60b127331f264/regex-2026.1.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:968c14d4f03e10b2fd960f1d5168c1f0ac969381d3c1fcc973bc45fb06346599", size = 798680, upload-time = "2026-01-14T23:15:19.342Z" }, - { url = "https://files.pythonhosted.org/packages/58/de/30e1cfcdbe3e891324aa7568b7c968771f82190df5524fabc1138cb2d45a/regex-2026.1.15-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56a5595d0f892f214609c9f76b41b7428bed439d98dc961efafdd1354d42baae", size = 864210, upload-time = "2026-01-14T23:15:22.005Z" }, - { url = "https://files.pythonhosted.org/packages/64/44/4db2f5c5ca0ccd40ff052ae7b1e9731352fcdad946c2b812285a7505ca75/regex-2026.1.15-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf650f26087363434c4e560011f8e4e738f6f3e029b85d4904c50135b86cfa5", size = 912358, upload-time = "2026-01-14T23:15:24.569Z" }, - { url = "https://files.pythonhosted.org/packages/79/b6/e6a5665d43a7c42467138c8a2549be432bad22cbd206f5ec87162de74bd7/regex-2026.1.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18388a62989c72ac24de75f1449d0fb0b04dfccd0a1a7c1c43af5eb503d890f6", size = 803583, upload-time = "2026-01-14T23:15:26.526Z" }, - { url = "https://files.pythonhosted.org/packages/e7/53/7cd478222169d85d74d7437e74750005e993f52f335f7c04ff7adfda3310/regex-2026.1.15-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d220a2517f5893f55daac983bfa9fe998a7dbcaee4f5d27a88500f8b7873788", size = 775782, upload-time = "2026-01-14T23:15:29.352Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b5/75f9a9ee4b03a7c009fe60500fe550b45df94f0955ca29af16333ef557c5/regex-2026.1.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9c08c2fbc6120e70abff5d7f28ffb4d969e14294fb2143b4b5c7d20e46d1714", size = 787978, upload-time = "2026-01-14T23:15:31.295Z" }, - { url = "https://files.pythonhosted.org/packages/72/b3/79821c826245bbe9ccbb54f6eadb7879c722fd3e0248c17bfc90bf54e123/regex-2026.1.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7ef7d5d4bd49ec7364315167a4134a015f61e8266c6d446fc116a9ac4456e10d", size = 858550, upload-time = "2026-01-14T23:15:33.558Z" }, - { url = "https://files.pythonhosted.org/packages/4a/85/2ab5f77a1c465745bfbfcb3ad63178a58337ae8d5274315e2cc623a822fa/regex-2026.1.15-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e42844ad64194fa08d5ccb75fe6a459b9b08e6d7296bd704460168d58a388f3", size = 763747, upload-time = "2026-01-14T23:15:35.206Z" }, - { url = "https://files.pythonhosted.org/packages/6d/84/c27df502d4bfe2873a3e3a7cf1bdb2b9cc10284d1a44797cf38bed790470/regex-2026.1.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cfecdaa4b19f9ca534746eb3b55a5195d5c95b88cac32a205e981ec0a22b7d31", size = 850615, upload-time = "2026-01-14T23:15:37.523Z" }, - { url = "https://files.pythonhosted.org/packages/7d/b7/658a9782fb253680aa8ecb5ccbb51f69e088ed48142c46d9f0c99b46c575/regex-2026.1.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:08df9722d9b87834a3d701f3fca570b2be115654dbfd30179f30ab2f39d606d3", size = 789951, upload-time = "2026-01-14T23:15:39.582Z" }, - { url = "https://files.pythonhosted.org/packages/fc/2a/5928af114441e059f15b2f63e188bd00c6529b3051c974ade7444b85fcda/regex-2026.1.15-cp313-cp313-win32.whl", hash = "sha256:d426616dae0967ca225ab12c22274eb816558f2f99ccb4a1d52ca92e8baf180f", size = 266275, upload-time = "2026-01-14T23:15:42.108Z" }, - { url = "https://files.pythonhosted.org/packages/4f/16/5bfbb89e435897bff28cf0352a992ca719d9e55ebf8b629203c96b6ce4f7/regex-2026.1.15-cp313-cp313-win_amd64.whl", hash = "sha256:febd38857b09867d3ed3f4f1af7d241c5c50362e25ef43034995b77a50df494e", size = 277145, upload-time = "2026-01-14T23:15:44.244Z" }, - { url = "https://files.pythonhosted.org/packages/56/c1/a09ff7392ef4233296e821aec5f78c51be5e91ffde0d163059e50fd75835/regex-2026.1.15-cp313-cp313-win_arm64.whl", hash = "sha256:8e32f7896f83774f91499d239e24cebfadbc07639c1494bb7213983842348337", size = 270411, upload-time = "2026-01-14T23:15:45.858Z" }, - { url = "https://files.pythonhosted.org/packages/3c/38/0cfd5a78e5c6db00e6782fdae70458f89850ce95baa5e8694ab91d89744f/regex-2026.1.15-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ec94c04149b6a7b8120f9f44565722c7ae31b7a6d2275569d2eefa76b83da3be", size = 492068, upload-time = "2026-01-14T23:15:47.616Z" }, - { url = "https://files.pythonhosted.org/packages/50/72/6c86acff16cb7c959c4355826bbf06aad670682d07c8f3998d9ef4fee7cd/regex-2026.1.15-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40c86d8046915bb9aeb15d3f3f15b6fd500b8ea4485b30e1bbc799dab3fe29f8", size = 292756, upload-time = "2026-01-14T23:15:49.307Z" }, - { url = "https://files.pythonhosted.org/packages/4e/58/df7fb69eadfe76526ddfce28abdc0af09ffe65f20c2c90932e89d705153f/regex-2026.1.15-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:726ea4e727aba21643205edad8f2187ec682d3305d790f73b7a51c7587b64bdd", size = 291114, upload-time = "2026-01-14T23:15:51.484Z" }, - { url = "https://files.pythonhosted.org/packages/ed/6c/a4011cd1cf96b90d2cdc7e156f91efbd26531e822a7fbb82a43c1016678e/regex-2026.1.15-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cb740d044aff31898804e7bf1181cc72c03d11dfd19932b9911ffc19a79070a", size = 807524, upload-time = "2026-01-14T23:15:53.102Z" }, - { url = "https://files.pythonhosted.org/packages/1d/25/a53ffb73183f69c3e9f4355c4922b76d2840aee160af6af5fac229b6201d/regex-2026.1.15-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05d75a668e9ea16f832390d22131fe1e8acc8389a694c8febc3e340b0f810b93", size = 873455, upload-time = "2026-01-14T23:15:54.956Z" }, - { url = "https://files.pythonhosted.org/packages/66/0b/8b47fc2e8f97d9b4a851736f3890a5f786443aa8901061c55f24c955f45b/regex-2026.1.15-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d991483606f3dbec93287b9f35596f41aa2e92b7c2ebbb935b63f409e243c9af", size = 915007, upload-time = "2026-01-14T23:15:57.041Z" }, - { url = "https://files.pythonhosted.org/packages/c2/fa/97de0d681e6d26fabe71968dbee06dd52819e9a22fdce5dac7256c31ed84/regex-2026.1.15-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:194312a14819d3e44628a44ed6fea6898fdbecb0550089d84c403475138d0a09", size = 812794, upload-time = "2026-01-14T23:15:58.916Z" }, - { url = "https://files.pythonhosted.org/packages/22/38/e752f94e860d429654aa2b1c51880bff8dfe8f084268258adf9151cf1f53/regex-2026.1.15-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe2fda4110a3d0bc163c2e0664be44657431440722c5c5315c65155cab92f9e5", size = 781159, upload-time = "2026-01-14T23:16:00.817Z" }, - { url = "https://files.pythonhosted.org/packages/e9/a7/d739ffaef33c378fc888302a018d7f81080393d96c476b058b8c64fd2b0d/regex-2026.1.15-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:124dc36c85d34ef2d9164da41a53c1c8c122cfb1f6e1ec377a1f27ee81deb794", size = 795558, upload-time = "2026-01-14T23:16:03.267Z" }, - { url = "https://files.pythonhosted.org/packages/3e/c4/542876f9a0ac576100fc73e9c75b779f5c31e3527576cfc9cb3009dcc58a/regex-2026.1.15-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1774cd1981cd212506a23a14dba7fdeaee259f5deba2df6229966d9911e767a", size = 868427, upload-time = "2026-01-14T23:16:05.646Z" }, - { url = "https://files.pythonhosted.org/packages/fc/0f/d5655bea5b22069e32ae85a947aa564912f23758e112cdb74212848a1a1b/regex-2026.1.15-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:b5f7d8d2867152cdb625e72a530d2ccb48a3d199159144cbdd63870882fb6f80", size = 769939, upload-time = "2026-01-14T23:16:07.542Z" }, - { url = "https://files.pythonhosted.org/packages/20/06/7e18a4fa9d326daeda46d471a44ef94201c46eaa26dbbb780b5d92cbfdda/regex-2026.1.15-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:492534a0ab925d1db998defc3c302dae3616a2fc3fe2e08db1472348f096ddf2", size = 854753, upload-time = "2026-01-14T23:16:10.395Z" }, - { url = "https://files.pythonhosted.org/packages/3b/67/dc8946ef3965e166f558ef3b47f492bc364e96a265eb4a2bb3ca765c8e46/regex-2026.1.15-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c661fc820cfb33e166bf2450d3dadbda47c8d8981898adb9b6fe24e5e582ba60", size = 799559, upload-time = "2026-01-14T23:16:12.347Z" }, - { url = "https://files.pythonhosted.org/packages/a5/61/1bba81ff6d50c86c65d9fd84ce9699dd106438ee4cdb105bf60374ee8412/regex-2026.1.15-cp313-cp313t-win32.whl", hash = "sha256:99ad739c3686085e614bf77a508e26954ff1b8f14da0e3765ff7abbf7799f952", size = 268879, upload-time = "2026-01-14T23:16:14.049Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5e/cef7d4c5fb0ea3ac5c775fd37db5747f7378b29526cc83f572198924ff47/regex-2026.1.15-cp313-cp313t-win_amd64.whl", hash = "sha256:32655d17905e7ff8ba5c764c43cb124e34a9245e45b83c22e81041e1071aee10", size = 280317, upload-time = "2026-01-14T23:16:15.718Z" }, - { url = "https://files.pythonhosted.org/packages/b4/52/4317f7a5988544e34ab57b4bde0f04944c4786128c933fb09825924d3e82/regex-2026.1.15-cp313-cp313t-win_arm64.whl", hash = "sha256:b2a13dd6a95e95a489ca242319d18fc02e07ceb28fa9ad146385194d95b3c829", size = 271551, upload-time = "2026-01-14T23:16:17.533Z" }, - { url = "https://files.pythonhosted.org/packages/52/0a/47fa888ec7cbbc7d62c5f2a6a888878e76169170ead271a35239edd8f0e8/regex-2026.1.15-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:d920392a6b1f353f4aa54328c867fec3320fa50657e25f64abf17af054fc97ac", size = 489170, upload-time = "2026-01-14T23:16:19.835Z" }, - { url = "https://files.pythonhosted.org/packages/ac/c4/d000e9b7296c15737c9301708e9e7fbdea009f8e93541b6b43bdb8219646/regex-2026.1.15-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b5a28980a926fa810dbbed059547b02783952e2efd9c636412345232ddb87ff6", size = 291146, upload-time = "2026-01-14T23:16:21.541Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b6/921cc61982e538682bdf3bdf5b2c6ab6b34368da1f8e98a6c1ddc503c9cf/regex-2026.1.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:621f73a07595d83f28952d7bd1e91e9d1ed7625fb7af0064d3516674ec93a2a2", size = 288986, upload-time = "2026-01-14T23:16:23.381Z" }, - { url = "https://files.pythonhosted.org/packages/ca/33/eb7383dde0bbc93f4fb9d03453aab97e18ad4024ac7e26cef8d1f0a2cff0/regex-2026.1.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d7d92495f47567a9b1669c51fc8d6d809821849063d168121ef801bbc213846", size = 799098, upload-time = "2026-01-14T23:16:25.088Z" }, - { url = "https://files.pythonhosted.org/packages/27/56/b664dccae898fc8d8b4c23accd853f723bde0f026c747b6f6262b688029c/regex-2026.1.15-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8dd16fba2758db7a3780a051f245539c4451ca20910f5a5e6ea1c08d06d4a76b", size = 864980, upload-time = "2026-01-14T23:16:27.297Z" }, - { url = "https://files.pythonhosted.org/packages/16/40/0999e064a170eddd237bae9ccfcd8f28b3aa98a38bf727a086425542a4fc/regex-2026.1.15-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1e1808471fbe44c1a63e5f577a1d5f02fe5d66031dcbdf12f093ffc1305a858e", size = 911607, upload-time = "2026-01-14T23:16:29.235Z" }, - { url = "https://files.pythonhosted.org/packages/07/78/c77f644b68ab054e5a674fb4da40ff7bffb2c88df58afa82dbf86573092d/regex-2026.1.15-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0751a26ad39d4f2ade8fe16c59b2bf5cb19eb3d2cd543e709e583d559bd9efde", size = 803358, upload-time = "2026-01-14T23:16:31.369Z" }, - { url = "https://files.pythonhosted.org/packages/27/31/d4292ea8566eaa551fafc07797961c5963cf5235c797cc2ae19b85dfd04d/regex-2026.1.15-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0f0c7684c7f9ca241344ff95a1de964f257a5251968484270e91c25a755532c5", size = 775833, upload-time = "2026-01-14T23:16:33.141Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b2/cff3bf2fea4133aa6fb0d1e370b37544d18c8350a2fa118c7e11d1db0e14/regex-2026.1.15-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:74f45d170a21df41508cb67165456538425185baaf686281fa210d7e729abc34", size = 788045, upload-time = "2026-01-14T23:16:35.005Z" }, - { url = "https://files.pythonhosted.org/packages/8d/99/2cb9b69045372ec877b6f5124bda4eb4253bc58b8fe5848c973f752bc52c/regex-2026.1.15-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f1862739a1ffb50615c0fde6bae6569b5efbe08d98e59ce009f68a336f64da75", size = 859374, upload-time = "2026-01-14T23:16:36.919Z" }, - { url = "https://files.pythonhosted.org/packages/09/16/710b0a5abe8e077b1729a562d2f297224ad079f3a66dce46844c193416c8/regex-2026.1.15-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:453078802f1b9e2b7303fb79222c054cb18e76f7bdc220f7530fdc85d319f99e", size = 763940, upload-time = "2026-01-14T23:16:38.685Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/7585c8e744e40eb3d32f119191969b91de04c073fca98ec14299041f6e7e/regex-2026.1.15-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:a30a68e89e5a218b8b23a52292924c1f4b245cb0c68d1cce9aec9bbda6e2c160", size = 850112, upload-time = "2026-01-14T23:16:40.646Z" }, - { url = "https://files.pythonhosted.org/packages/af/d6/43e1dd85df86c49a347aa57c1f69d12c652c7b60e37ec162e3096194a278/regex-2026.1.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9479cae874c81bf610d72b85bb681a94c95722c127b55445285fb0e2c82db8e1", size = 789586, upload-time = "2026-01-14T23:16:42.799Z" }, - { url = "https://files.pythonhosted.org/packages/93/38/77142422f631e013f316aaae83234c629555729a9fbc952b8a63ac91462a/regex-2026.1.15-cp314-cp314-win32.whl", hash = "sha256:d639a750223132afbfb8f429c60d9d318aeba03281a5f1ab49f877456448dcf1", size = 271691, upload-time = "2026-01-14T23:16:44.671Z" }, - { url = "https://files.pythonhosted.org/packages/4a/a9/ab16b4649524ca9e05213c1cdbb7faa85cc2aa90a0230d2f796cbaf22736/regex-2026.1.15-cp314-cp314-win_amd64.whl", hash = "sha256:4161d87f85fa831e31469bfd82c186923070fc970b9de75339b68f0c75b51903", size = 280422, upload-time = "2026-01-14T23:16:46.607Z" }, - { url = "https://files.pythonhosted.org/packages/be/2a/20fd057bf3521cb4791f69f869635f73e0aaf2b9ad2d260f728144f9047c/regex-2026.1.15-cp314-cp314-win_arm64.whl", hash = "sha256:91c5036ebb62663a6b3999bdd2e559fd8456d17e2b485bf509784cd31a8b1705", size = 273467, upload-time = "2026-01-14T23:16:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/ad/77/0b1e81857060b92b9cad239104c46507dd481b3ff1fa79f8e7f865aae38a/regex-2026.1.15-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ee6854c9000a10938c79238de2379bea30c82e4925a371711af45387df35cab8", size = 492073, upload-time = "2026-01-14T23:16:51.154Z" }, - { url = "https://files.pythonhosted.org/packages/70/f3/f8302b0c208b22c1e4f423147e1913fd475ddd6230565b299925353de644/regex-2026.1.15-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c2b80399a422348ce5de4fe40c418d6299a0fa2803dd61dc0b1a2f28e280fcf", size = 292757, upload-time = "2026-01-14T23:16:53.08Z" }, - { url = "https://files.pythonhosted.org/packages/bf/f0/ef55de2460f3b4a6da9d9e7daacd0cb79d4ef75c64a2af316e68447f0df0/regex-2026.1.15-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:dca3582bca82596609959ac39e12b7dad98385b4fefccb1151b937383cec547d", size = 291122, upload-time = "2026-01-14T23:16:55.383Z" }, - { url = "https://files.pythonhosted.org/packages/cf/55/bb8ccbacabbc3a11d863ee62a9f18b160a83084ea95cdfc5d207bfc3dd75/regex-2026.1.15-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef71d476caa6692eea743ae5ea23cde3260677f70122c4d258ca952e5c2d4e84", size = 807761, upload-time = "2026-01-14T23:16:57.251Z" }, - { url = "https://files.pythonhosted.org/packages/8f/84/f75d937f17f81e55679a0509e86176e29caa7298c38bd1db7ce9c0bf6075/regex-2026.1.15-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c243da3436354f4af6c3058a3f81a97d47ea52c9bd874b52fd30274853a1d5df", size = 873538, upload-time = "2026-01-14T23:16:59.349Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d9/0da86327df70349aa8d86390da91171bd3ca4f0e7c1d1d453a9c10344da3/regex-2026.1.15-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8355ad842a7c7e9e5e55653eade3b7d1885ba86f124dd8ab1f722f9be6627434", size = 915066, upload-time = "2026-01-14T23:17:01.607Z" }, - { url = "https://files.pythonhosted.org/packages/2a/5e/f660fb23fc77baa2a61aa1f1fe3a4eea2bbb8a286ddec148030672e18834/regex-2026.1.15-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f192a831d9575271a22d804ff1a5355355723f94f31d9eef25f0d45a152fdc1a", size = 812938, upload-time = "2026-01-14T23:17:04.366Z" }, - { url = "https://files.pythonhosted.org/packages/69/33/a47a29bfecebbbfd1e5cd3f26b28020a97e4820f1c5148e66e3b7d4b4992/regex-2026.1.15-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:166551807ec20d47ceaeec380081f843e88c8949780cd42c40f18d16168bed10", size = 781314, upload-time = "2026-01-14T23:17:06.378Z" }, - { url = "https://files.pythonhosted.org/packages/65/ec/7ec2bbfd4c3f4e494a24dec4c6943a668e2030426b1b8b949a6462d2c17b/regex-2026.1.15-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f9ca1cbdc0fbfe5e6e6f8221ef2309988db5bcede52443aeaee9a4ad555e0dac", size = 795652, upload-time = "2026-01-14T23:17:08.521Z" }, - { url = "https://files.pythonhosted.org/packages/46/79/a5d8651ae131fe27d7c521ad300aa7f1c7be1dbeee4d446498af5411b8a9/regex-2026.1.15-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b30bcbd1e1221783c721483953d9e4f3ab9c5d165aa709693d3f3946747b1aea", size = 868550, upload-time = "2026-01-14T23:17:10.573Z" }, - { url = "https://files.pythonhosted.org/packages/06/b7/25635d2809664b79f183070786a5552dd4e627e5aedb0065f4e3cf8ee37d/regex-2026.1.15-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2a8d7b50c34578d0d3bf7ad58cde9652b7d683691876f83aedc002862a35dc5e", size = 769981, upload-time = "2026-01-14T23:17:12.871Z" }, - { url = "https://files.pythonhosted.org/packages/16/8b/fc3fcbb2393dcfa4a6c5ffad92dc498e842df4581ea9d14309fcd3c55fb9/regex-2026.1.15-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9d787e3310c6a6425eb346be4ff2ccf6eece63017916fd77fe8328c57be83521", size = 854780, upload-time = "2026-01-14T23:17:14.837Z" }, - { url = "https://files.pythonhosted.org/packages/d0/38/dde117c76c624713c8a2842530be9c93ca8b606c0f6102d86e8cd1ce8bea/regex-2026.1.15-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:619843841e220adca114118533a574a9cd183ed8a28b85627d2844c500a2b0db", size = 799778, upload-time = "2026-01-14T23:17:17.369Z" }, - { url = "https://files.pythonhosted.org/packages/e3/0d/3a6cfa9ae99606afb612d8fb7a66b245a9d5ff0f29bb347c8a30b6ad561b/regex-2026.1.15-cp314-cp314t-win32.whl", hash = "sha256:e90b8db97f6f2c97eb045b51a6b2c5ed69cedd8392459e0642d4199b94fabd7e", size = 274667, upload-time = "2026-01-14T23:17:19.301Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b2/297293bb0742fd06b8d8e2572db41a855cdf1cae0bf009b1cb74fe07e196/regex-2026.1.15-cp314-cp314t-win_amd64.whl", hash = "sha256:5ef19071f4ac9f0834793af85bd04a920b4407715624e40cb7a0631a11137cdf", size = 284386, upload-time = "2026-01-14T23:17:21.231Z" }, - { url = "https://files.pythonhosted.org/packages/95/e4/a3b9480c78cf8ee86626cb06f8d931d74d775897d44201ccb813097ae697/regex-2026.1.15-cp314-cp314t-win_arm64.whl", hash = "sha256:ca89c5e596fc05b015f27561b3793dc2fa0917ea0d7507eebb448efd35274a70", size = 274837, upload-time = "2026-01-14T23:17:23.146Z" }, -] - -[[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 = "requests-oauthlib" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "oauthlib" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, -] - -[[package]] -name = "rich" -version = "14.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" }, -] - -[[package]] -name = "rich-rst" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, -] - -[[package]] -name = "rsa" -version = "4.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyasn1" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, -] - -[[package]] -name = "rubicon-objc" -version = "0.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/d2/d39ecd205661a5c14c90dbd92a722a203848a3621785c9783716341de427/rubicon_objc-0.5.3.tar.gz", hash = "sha256:74c25920c5951a05db9d3a1aac31d23816ec7dacc841a5b124d911b99ea71b9a", size = 171512, upload-time = "2025-12-03T03:51:10.264Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/ab/e834c01138c272fb2e37d2f3c7cba708bc694dbc7b3f03b743f29ceb92d5/rubicon_objc-0.5.3-py3-none-any.whl", hash = "sha256:31dedcda9be38435f5ec067906e1eea5d0ddb790330e98a22e94ff424758b415", size = 64414, upload-time = "2025-12-03T03:51:09.082Z" }, -] - -[[package]] -name = "safetensors" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, - { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, - { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, - { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, - { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, - { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, - { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, - { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, - { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, - { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, - { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, -] - -[[package]] -name = "scikit-learn" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "joblib" }, - { name = "numpy" }, - { name = "scipy" }, - { name = "threadpoolctl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/74/e6a7cc4b820e95cc38cf36cd74d5aa2b42e8ffc2d21fe5a9a9c45c1c7630/scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e", size = 8548242, upload-time = "2025-12-10T07:07:51.568Z" }, - { url = "https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76", size = 8079075, upload-time = "2025-12-10T07:07:53.697Z" }, - { url = "https://files.pythonhosted.org/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492, upload-time = "2025-12-10T07:07:55.574Z" }, - { url = "https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904, upload-time = "2025-12-10T07:07:57.666Z" }, - { url = "https://files.pythonhosted.org/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809", size = 8019359, upload-time = "2025-12-10T07:07:59.838Z" }, - { url = "https://files.pythonhosted.org/packages/24/90/344a67811cfd561d7335c1b96ca21455e7e472d281c3c279c4d3f2300236/scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb", size = 7641898, upload-time = "2025-12-10T07:08:01.36Z" }, - { url = "https://files.pythonhosted.org/packages/03/aa/e22e0768512ce9255eba34775be2e85c2048da73da1193e841707f8f039c/scikit_learn-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a", size = 8513770, upload-time = "2025-12-10T07:08:03.251Z" }, - { url = "https://files.pythonhosted.org/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e", size = 8044458, upload-time = "2025-12-10T07:08:05.336Z" }, - { url = "https://files.pythonhosted.org/packages/2d/5a/3f1caed8765f33eabb723596666da4ebbf43d11e96550fb18bdec42b467b/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57", size = 8610341, upload-time = "2025-12-10T07:08:07.732Z" }, - { url = "https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e", size = 8900022, upload-time = "2025-12-10T07:08:09.862Z" }, - { url = "https://files.pythonhosted.org/packages/1c/f9/9b7563caf3ec8873e17a31401858efab6b39a882daf6c1bfa88879c0aa11/scikit_learn-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271", size = 7989409, upload-time = "2025-12-10T07:08:12.028Z" }, - { url = "https://files.pythonhosted.org/packages/49/bd/1f4001503650e72c4f6009ac0c4413cb17d2d601cef6f71c0453da2732fc/scikit_learn-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3", size = 7619760, upload-time = "2025-12-10T07:08:13.688Z" }, - { url = "https://files.pythonhosted.org/packages/d2/7d/a630359fc9dcc95496588c8d8e3245cc8fd81980251079bc09c70d41d951/scikit_learn-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735", size = 8826045, upload-time = "2025-12-10T07:08:15.215Z" }, - { url = "https://files.pythonhosted.org/packages/cc/56/a0c86f6930cfcd1c7054a2bc417e26960bb88d32444fe7f71d5c2cfae891/scikit_learn-1.8.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd", size = 8420324, upload-time = "2025-12-10T07:08:17.561Z" }, - { url = "https://files.pythonhosted.org/packages/46/1e/05962ea1cebc1cf3876667ecb14c283ef755bf409993c5946ade3b77e303/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e", size = 8680651, upload-time = "2025-12-10T07:08:19.952Z" }, - { url = "https://files.pythonhosted.org/packages/fe/56/a85473cd75f200c9759e3a5f0bcab2d116c92a8a02ee08ccd73b870f8bb4/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb", size = 8925045, upload-time = "2025-12-10T07:08:22.11Z" }, - { url = "https://files.pythonhosted.org/packages/cc/b7/64d8cfa896c64435ae57f4917a548d7ac7a44762ff9802f75a79b77cb633/scikit_learn-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702", size = 8507994, upload-time = "2025-12-10T07:08:23.943Z" }, - { url = "https://files.pythonhosted.org/packages/5e/37/e192ea709551799379958b4c4771ec507347027bb7c942662c7fbeba31cb/scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde", size = 7869518, upload-time = "2025-12-10T07:08:25.71Z" }, - { url = "https://files.pythonhosted.org/packages/24/05/1af2c186174cc92dcab2233f327336058c077d38f6fe2aceb08e6ab4d509/scikit_learn-1.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3", size = 8528667, upload-time = "2025-12-10T07:08:27.541Z" }, - { url = "https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7", size = 8066524, upload-time = "2025-12-10T07:08:29.822Z" }, - { url = "https://files.pythonhosted.org/packages/be/ce/a0623350aa0b68647333940ee46fe45086c6060ec604874e38e9ab7d8e6c/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6", size = 8657133, upload-time = "2025-12-10T07:08:31.865Z" }, - { url = "https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4", size = 8923223, upload-time = "2025-12-10T07:08:34.166Z" }, - { url = "https://files.pythonhosted.org/packages/76/18/a8def8f91b18cd1ba6e05dbe02540168cb24d47e8dcf69e8d00b7da42a08/scikit_learn-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6", size = 8096518, upload-time = "2025-12-10T07:08:36.339Z" }, - { url = "https://files.pythonhosted.org/packages/d1/77/482076a678458307f0deb44e29891d6022617b2a64c840c725495bee343f/scikit_learn-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242", size = 7754546, upload-time = "2025-12-10T07:08:38.128Z" }, - { url = "https://files.pythonhosted.org/packages/2d/d1/ef294ca754826daa043b2a104e59960abfab4cf653891037d19dd5b6f3cf/scikit_learn-1.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7", size = 8848305, upload-time = "2025-12-10T07:08:41.013Z" }, - { url = "https://files.pythonhosted.org/packages/5b/e2/b1f8b05138ee813b8e1a4149f2f0d289547e60851fd1bb268886915adbda/scikit_learn-1.8.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9", size = 8432257, upload-time = "2025-12-10T07:08:42.873Z" }, - { url = "https://files.pythonhosted.org/packages/26/11/c32b2138a85dcb0c99f6afd13a70a951bfdff8a6ab42d8160522542fb647/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f", size = 8678673, upload-time = "2025-12-10T07:08:45.362Z" }, - { url = "https://files.pythonhosted.org/packages/c7/57/51f2384575bdec454f4fe4e7a919d696c9ebce914590abf3e52d47607ab8/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9", size = 8922467, upload-time = "2025-12-10T07:08:47.408Z" }, - { url = "https://files.pythonhosted.org/packages/35/4d/748c9e2872637a57981a04adc038dacaa16ba8ca887b23e34953f0b3f742/scikit_learn-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2", size = 8774395, upload-time = "2025-12-10T07:08:49.337Z" }, - { url = "https://files.pythonhosted.org/packages/60/22/d7b2ebe4704a5e50790ba089d5c2ae308ab6bb852719e6c3bd4f04c3a363/scikit_learn-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c", size = 8002647, upload-time = "2025-12-10T07:08:51.601Z" }, -] - -[[package]] -name = "scipy" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/56/3e/9cca699f3486ce6bc12ff46dc2031f1ec8eb9ccc9a320fdaf925f1417426/scipy-1.17.0.tar.gz", hash = "sha256:2591060c8e648d8b96439e111ac41fd8342fdeff1876be2e19dea3fe8930454e", size = 30396830, upload-time = "2026-01-10T21:34:23.009Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/11/7241a63e73ba5a516f1930ac8d5b44cbbfabd35ac73a2d08ca206df007c4/scipy-1.17.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:0d5018a57c24cb1dd828bcf51d7b10e65986d549f52ef5adb6b4d1ded3e32a57", size = 31364580, upload-time = "2026-01-10T21:25:25.717Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1d/5057f812d4f6adc91a20a2d6f2ebcdb517fdbc87ae3acc5633c9b97c8ba5/scipy-1.17.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:88c22af9e5d5a4f9e027e26772cc7b5922fab8bcc839edb3ae33de404feebd9e", size = 27969012, upload-time = "2026-01-10T21:25:30.921Z" }, - { url = "https://files.pythonhosted.org/packages/e3/21/f6ec556c1e3b6ec4e088da667d9987bb77cc3ab3026511f427dc8451187d/scipy-1.17.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f3cd947f20fe17013d401b64e857c6b2da83cae567adbb75b9dcba865abc66d8", size = 20140691, upload-time = "2026-01-10T21:25:34.802Z" }, - { url = "https://files.pythonhosted.org/packages/7a/fe/5e5ad04784964ba964a96f16c8d4676aa1b51357199014dce58ab7ec5670/scipy-1.17.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e8c0b331c2c1f531eb51f1b4fc9ba709521a712cce58f1aa627bc007421a5306", size = 22463015, upload-time = "2026-01-10T21:25:39.277Z" }, - { url = "https://files.pythonhosted.org/packages/4a/69/7c347e857224fcaf32a34a05183b9d8a7aca25f8f2d10b8a698b8388561a/scipy-1.17.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5194c445d0a1c7a6c1a4a4681b6b7c71baad98ff66d96b949097e7513c9d6742", size = 32724197, upload-time = "2026-01-10T21:25:44.084Z" }, - { url = "https://files.pythonhosted.org/packages/d1/fe/66d73b76d378ba8cc2fe605920c0c75092e3a65ae746e1e767d9d020a75a/scipy-1.17.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9eeb9b5f5997f75507814ed9d298ab23f62cf79f5a3ef90031b1ee2506abdb5b", size = 35009148, upload-time = "2026-01-10T21:25:50.591Z" }, - { url = "https://files.pythonhosted.org/packages/af/07/07dec27d9dc41c18d8c43c69e9e413431d20c53a0339c388bcf72f353c4b/scipy-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:40052543f7bbe921df4408f46003d6f01c6af109b9e2c8a66dd1cf6cf57f7d5d", size = 34798766, upload-time = "2026-01-10T21:25:59.41Z" }, - { url = "https://files.pythonhosted.org/packages/81/61/0470810c8a093cdacd4ba7504b8a218fd49ca070d79eca23a615f5d9a0b0/scipy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0cf46c8013fec9d3694dc572f0b54100c28405d55d3e2cb15e2895b25057996e", size = 37405953, upload-time = "2026-01-10T21:26:07.75Z" }, - { url = "https://files.pythonhosted.org/packages/92/ce/672ed546f96d5d41ae78c4b9b02006cedd0b3d6f2bf5bb76ea455c320c28/scipy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:0937a0b0d8d593a198cededd4c439a0ea216a3f36653901ea1f3e4be949056f8", size = 36328121, upload-time = "2026-01-10T21:26:16.509Z" }, - { url = "https://files.pythonhosted.org/packages/9d/21/38165845392cae67b61843a52c6455d47d0cc2a40dd495c89f4362944654/scipy-1.17.0-cp312-cp312-win_arm64.whl", hash = "sha256:f603d8a5518c7426414d1d8f82e253e454471de682ce5e39c29adb0df1efb86b", size = 24314368, upload-time = "2026-01-10T21:26:23.087Z" }, - { url = "https://files.pythonhosted.org/packages/0c/51/3468fdfd49387ddefee1636f5cf6d03ce603b75205bf439bbf0e62069bfd/scipy-1.17.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:65ec32f3d32dfc48c72df4291345dae4f048749bc8d5203ee0a3f347f96c5ce6", size = 31344101, upload-time = "2026-01-10T21:26:30.25Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9a/9406aec58268d437636069419e6977af953d1e246df941d42d3720b7277b/scipy-1.17.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:1f9586a58039d7229ce77b52f8472c972448cded5736eaf102d5658bbac4c269", size = 27950385, upload-time = "2026-01-10T21:26:36.801Z" }, - { url = "https://files.pythonhosted.org/packages/4f/98/e7342709e17afdfd1b26b56ae499ef4939b45a23a00e471dfb5375eea205/scipy-1.17.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9fad7d3578c877d606b1150135c2639e9de9cecd3705caa37b66862977cc3e72", size = 20122115, upload-time = "2026-01-10T21:26:42.107Z" }, - { url = "https://files.pythonhosted.org/packages/fd/0e/9eeeb5357a64fd157cbe0302c213517c541cc16b8486d82de251f3c68ede/scipy-1.17.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:423ca1f6584fc03936972b5f7c06961670dbba9f234e71676a7c7ccf938a0d61", size = 22442402, upload-time = "2026-01-10T21:26:48.029Z" }, - { url = "https://files.pythonhosted.org/packages/c9/10/be13397a0e434f98e0c79552b2b584ae5bb1c8b2be95db421533bbca5369/scipy-1.17.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe508b5690e9eaaa9467fc047f833af58f1152ae51a0d0aed67aa5801f4dd7d6", size = 32696338, upload-time = "2026-01-10T21:26:55.521Z" }, - { url = "https://files.pythonhosted.org/packages/63/1e/12fbf2a3bb240161651c94bb5cdd0eae5d4e8cc6eaeceb74ab07b12a753d/scipy-1.17.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6680f2dfd4f6182e7d6db161344537da644d1cf85cf293f015c60a17ecf08752", size = 34977201, upload-time = "2026-01-10T21:27:03.501Z" }, - { url = "https://files.pythonhosted.org/packages/19/5b/1a63923e23ccd20bd32156d7dd708af5bbde410daa993aa2500c847ab2d2/scipy-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eec3842ec9ac9de5917899b277428886042a93db0b227ebbe3a333b64ec7643d", size = 34777384, upload-time = "2026-01-10T21:27:11.423Z" }, - { url = "https://files.pythonhosted.org/packages/39/22/b5da95d74edcf81e540e467202a988c50fef41bd2011f46e05f72ba07df6/scipy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d7425fcafbc09a03731e1bc05581f5fad988e48c6a861f441b7ab729a49a55ea", size = 37379586, upload-time = "2026-01-10T21:27:20.171Z" }, - { url = "https://files.pythonhosted.org/packages/b9/b6/8ac583d6da79e7b9e520579f03007cb006f063642afd6b2eeb16b890bf93/scipy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:87b411e42b425b84777718cc41516b8a7e0795abfa8e8e1d573bf0ef014f0812", size = 36287211, upload-time = "2026-01-10T21:28:43.122Z" }, - { url = "https://files.pythonhosted.org/packages/55/fb/7db19e0b3e52f882b420417644ec81dd57eeef1bd1705b6f689d8ff93541/scipy-1.17.0-cp313-cp313-win_arm64.whl", hash = "sha256:357ca001c6e37601066092e7c89cca2f1ce74e2a520ca78d063a6d2201101df2", size = 24312646, upload-time = "2026-01-10T21:28:49.893Z" }, - { url = "https://files.pythonhosted.org/packages/20/b6/7feaa252c21cc7aff335c6c55e1b90ab3e3306da3f048109b8b639b94648/scipy-1.17.0-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:ec0827aa4d36cb79ff1b81de898e948a51ac0b9b1c43e4a372c0508c38c0f9a3", size = 31693194, upload-time = "2026-01-10T21:27:27.454Z" }, - { url = "https://files.pythonhosted.org/packages/76/bb/bbb392005abce039fb7e672cb78ac7d158700e826b0515cab6b5b60c26fb/scipy-1.17.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:819fc26862b4b3c73a60d486dbb919202f3d6d98c87cf20c223511429f2d1a97", size = 28365415, upload-time = "2026-01-10T21:27:34.26Z" }, - { url = "https://files.pythonhosted.org/packages/37/da/9d33196ecc99fba16a409c691ed464a3a283ac454a34a13a3a57c0d66f3a/scipy-1.17.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:363ad4ae2853d88ebcde3ae6ec46ccca903ea9835ee8ba543f12f575e7b07e4e", size = 20537232, upload-time = "2026-01-10T21:27:40.306Z" }, - { url = "https://files.pythonhosted.org/packages/56/9d/f4b184f6ddb28e9a5caea36a6f98e8ecd2a524f9127354087ce780885d83/scipy-1.17.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:979c3a0ff8e5ba254d45d59ebd38cde48fce4f10b5125c680c7a4bfe177aab07", size = 22791051, upload-time = "2026-01-10T21:27:46.539Z" }, - { url = "https://files.pythonhosted.org/packages/9b/9d/025cccdd738a72140efc582b1641d0dd4caf2e86c3fb127568dc80444e6e/scipy-1.17.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:130d12926ae34399d157de777472bf82e9061c60cc081372b3118edacafe1d00", size = 32815098, upload-time = "2026-01-10T21:27:54.389Z" }, - { url = "https://files.pythonhosted.org/packages/48/5f/09b879619f8bca15ce392bfc1894bd9c54377e01d1b3f2f3b595a1b4d945/scipy-1.17.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e886000eb4919eae3a44f035e63f0fd8b651234117e8f6f29bad1cd26e7bc45", size = 35031342, upload-time = "2026-01-10T21:28:03.012Z" }, - { url = "https://files.pythonhosted.org/packages/f2/9a/f0f0a9f0aa079d2f106555b984ff0fbb11a837df280f04f71f056ea9c6e4/scipy-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:13c4096ac6bc31d706018f06a49abe0485f96499deb82066b94d19b02f664209", size = 34893199, upload-time = "2026-01-10T21:28:10.832Z" }, - { url = "https://files.pythonhosted.org/packages/90/b8/4f0f5cf0c5ea4d7548424e6533e6b17d164f34a6e2fb2e43ffebb6697b06/scipy-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cacbaddd91fcffde703934897c5cd2c7cb0371fac195d383f4e1f1c5d3f3bd04", size = 37438061, upload-time = "2026-01-10T21:28:19.684Z" }, - { url = "https://files.pythonhosted.org/packages/f9/cc/2bd59140ed3b2fa2882fb15da0a9cb1b5a6443d67cfd0d98d4cec83a57ec/scipy-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:edce1a1cf66298cccdc48a1bdf8fb10a3bf58e8b58d6c3883dd1530e103f87c0", size = 36328593, upload-time = "2026-01-10T21:28:28.007Z" }, - { url = "https://files.pythonhosted.org/packages/13/1b/c87cc44a0d2c7aaf0f003aef2904c3d097b422a96c7e7c07f5efd9073c1b/scipy-1.17.0-cp313-cp313t-win_arm64.whl", hash = "sha256:30509da9dbec1c2ed8f168b8d8aa853bc6723fede1dbc23c7d43a56f5ab72a67", size = 24625083, upload-time = "2026-01-10T21:28:35.188Z" }, - { url = "https://files.pythonhosted.org/packages/1a/2d/51006cd369b8e7879e1c630999a19d1fbf6f8b5ed3e33374f29dc87e53b3/scipy-1.17.0-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:c17514d11b78be8f7e6331b983a65a7f5ca1fd037b95e27b280921fe5606286a", size = 31346803, upload-time = "2026-01-10T21:28:57.24Z" }, - { url = "https://files.pythonhosted.org/packages/d6/2e/2349458c3ce445f53a6c93d4386b1c4c5c0c540917304c01222ff95ff317/scipy-1.17.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:4e00562e519c09da34c31685f6acc3aa384d4d50604db0f245c14e1b4488bfa2", size = 27967182, upload-time = "2026-01-10T21:29:04.107Z" }, - { url = "https://files.pythonhosted.org/packages/5e/7c/df525fbfa77b878d1cfe625249529514dc02f4fd5f45f0f6295676a76528/scipy-1.17.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f7df7941d71314e60a481e02d5ebcb3f0185b8d799c70d03d8258f6c80f3d467", size = 20139125, upload-time = "2026-01-10T21:29:10.179Z" }, - { url = "https://files.pythonhosted.org/packages/33/11/fcf9d43a7ed1234d31765ec643b0515a85a30b58eddccc5d5a4d12b5f194/scipy-1.17.0-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:aabf057c632798832f071a8dde013c2e26284043934f53b00489f1773b33527e", size = 22443554, upload-time = "2026-01-10T21:29:15.888Z" }, - { url = "https://files.pythonhosted.org/packages/80/5c/ea5d239cda2dd3d31399424967a24d556cf409fbea7b5b21412b0fd0a44f/scipy-1.17.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a38c3337e00be6fd8a95b4ed66b5d988bac4ec888fd922c2ea9fe5fb1603dd67", size = 32757834, upload-time = "2026-01-10T21:29:23.406Z" }, - { url = "https://files.pythonhosted.org/packages/b8/7e/8c917cc573310e5dc91cbeead76f1b600d3fb17cf0969db02c9cf92e3cfa/scipy-1.17.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00fb5f8ec8398ad90215008d8b6009c9db9fa924fd4c7d6be307c6f945f9cd73", size = 34995775, upload-time = "2026-01-10T21:29:31.915Z" }, - { url = "https://files.pythonhosted.org/packages/c5/43/176c0c3c07b3f7df324e7cdd933d3e2c4898ca202b090bd5ba122f9fe270/scipy-1.17.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f2a4942b0f5f7c23c7cd641a0ca1955e2ae83dedcff537e3a0259096635e186b", size = 34841240, upload-time = "2026-01-10T21:29:39.995Z" }, - { url = "https://files.pythonhosted.org/packages/44/8c/d1f5f4b491160592e7f084d997de53a8e896a3ac01cd07e59f43ca222744/scipy-1.17.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:dbf133ced83889583156566d2bdf7a07ff89228fe0c0cb727f777de92092ec6b", size = 37394463, upload-time = "2026-01-10T21:29:48.723Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ec/42a6657f8d2d087e750e9a5dde0b481fd135657f09eaf1cf5688bb23c338/scipy-1.17.0-cp314-cp314-win_amd64.whl", hash = "sha256:3625c631a7acd7cfd929e4e31d2582cf00f42fcf06011f59281271746d77e061", size = 37053015, upload-time = "2026-01-10T21:30:51.418Z" }, - { url = "https://files.pythonhosted.org/packages/27/58/6b89a6afd132787d89a362d443a7bddd511b8f41336a1ae47f9e4f000dc4/scipy-1.17.0-cp314-cp314-win_arm64.whl", hash = "sha256:9244608d27eafe02b20558523ba57f15c689357c85bdcfe920b1828750aa26eb", size = 24951312, upload-time = "2026-01-10T21:30:56.771Z" }, - { url = "https://files.pythonhosted.org/packages/e9/01/f58916b9d9ae0112b86d7c3b10b9e685625ce6e8248df139d0fcb17f7397/scipy-1.17.0-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:2b531f57e09c946f56ad0b4a3b2abee778789097871fc541e267d2eca081cff1", size = 31706502, upload-time = "2026-01-10T21:29:56.326Z" }, - { url = "https://files.pythonhosted.org/packages/59/8e/2912a87f94a7d1f8b38aabc0faf74b82d3b6c9e22be991c49979f0eceed8/scipy-1.17.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:13e861634a2c480bd237deb69333ac79ea1941b94568d4b0efa5db5e263d4fd1", size = 28380854, upload-time = "2026-01-10T21:30:01.554Z" }, - { url = "https://files.pythonhosted.org/packages/bd/1c/874137a52dddab7d5d595c1887089a2125d27d0601fce8c0026a24a92a0b/scipy-1.17.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:eb2651271135154aa24f6481cbae5cc8af1f0dd46e6533fb7b56aa9727b6a232", size = 20552752, upload-time = "2026-01-10T21:30:05.93Z" }, - { url = "https://files.pythonhosted.org/packages/3f/f0/7518d171cb735f6400f4576cf70f756d5b419a07fe1867da34e2c2c9c11b/scipy-1.17.0-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:c5e8647f60679790c2f5c76be17e2e9247dc6b98ad0d3b065861e082c56e078d", size = 22803972, upload-time = "2026-01-10T21:30:10.651Z" }, - { url = "https://files.pythonhosted.org/packages/7c/74/3498563a2c619e8a3ebb4d75457486c249b19b5b04a30600dfd9af06bea5/scipy-1.17.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fb10d17e649e1446410895639f3385fd2bf4c3c7dfc9bea937bddcbc3d7b9ba", size = 32829770, upload-time = "2026-01-10T21:30:16.359Z" }, - { url = "https://files.pythonhosted.org/packages/48/d1/7b50cedd8c6c9d6f706b4b36fa8544d829c712a75e370f763b318e9638c1/scipy-1.17.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8547e7c57f932e7354a2319fab613981cde910631979f74c9b542bb167a8b9db", size = 35051093, upload-time = "2026-01-10T21:30:22.987Z" }, - { url = "https://files.pythonhosted.org/packages/e2/82/a2d684dfddb87ba1b3ea325df7c3293496ee9accb3a19abe9429bce94755/scipy-1.17.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33af70d040e8af9d5e7a38b5ed3b772adddd281e3062ff23fec49e49681c38cf", size = 34909905, upload-time = "2026-01-10T21:30:28.704Z" }, - { url = "https://files.pythonhosted.org/packages/ef/5e/e565bd73991d42023eb82bb99e51c5b3d9e2c588ca9d4b3e2cc1d3ca62a6/scipy-1.17.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb55bb97d00f8b7ab95cb64f873eb0bf54d9446264d9f3609130381233483f", size = 37457743, upload-time = "2026-01-10T21:30:34.819Z" }, - { url = "https://files.pythonhosted.org/packages/58/a8/a66a75c3d8f1fb2b83f66007d6455a06a6f6cf5618c3dc35bc9b69dd096e/scipy-1.17.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1ff269abf702f6c7e67a4b7aad981d42871a11b9dd83c58d2d2ea624efbd1088", size = 37098574, upload-time = "2026-01-10T21:30:40.782Z" }, - { url = "https://files.pythonhosted.org/packages/56/a5/df8f46ef7da168f1bc52cd86e09a9de5c6f19cc1da04454d51b7d4f43408/scipy-1.17.0-cp314-cp314t-win_arm64.whl", hash = "sha256:031121914e295d9791319a1875444d55079885bbae5bdc9c5e0f2ee5f09d34ff", size = 25246266, upload-time = "2026-01-10T21:30:45.923Z" }, -] - -[[package]] -name = "secretstorage" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "jeepney", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, -] - -[[package]] -name = "sentence-transformers" -version = "5.2.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, - { name = "numpy" }, - { name = "scikit-learn" }, - { name = "scipy" }, - { name = "torch" }, - { name = "tqdm" }, - { name = "transformers" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a6/bc/0bc9c0ec1cf83ab2ec6e6f38667d167349b950fff6dd2086b79bd360eeca/sentence_transformers-5.2.2.tar.gz", hash = "sha256:7033ee0a24bc04c664fd490abf2ef194d387b3a58a97adcc528783ff505159fa", size = 381607, upload-time = "2026-01-27T11:11:02.658Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/21/7e925890636791386e81b52878134f114d63072e79fffe14cdcc5e7a5e6a/sentence_transformers-5.2.2-py3-none-any.whl", hash = "sha256:280ac54bffb84c110726b4d8848ba7b7c60813b9034547f8aea6e9a345cd1c23", size = 494106, upload-time = "2026-01-27T11:11:00.983Z" }, -] - -[[package]] -name = "setuptools" -version = "80.10.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/76/95/faf61eb8363f26aa7e1d762267a8d602a1b26d4f3a1e758e92cb3cb8b054/setuptools-80.10.2.tar.gz", hash = "sha256:8b0e9d10c784bf7d262c4e5ec5d4ec94127ce206e8738f29a437945fbc219b70", size = 1200343, upload-time = "2026-01-25T22:38:17.252Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/b8/f1f62a5e3c0ad2ff1d189590bfa4c46b4f3b6e49cef6f26c6ee4e575394d/setuptools-80.10.2-py3-none-any.whl", hash = "sha256:95b30ddfb717250edb492926c92b5221f7ef3fbcc2b07579bcd4a27da21d0173", size = 1064234, upload-time = "2026-01-25T22:38:15.216Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[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 = "smmap" -version = "5.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329, upload-time = "2025-01-02T07:14:40.909Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" }, -] - -[[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 = "sortedcontainers" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, -] - -[[package]] -name = "soupsieve" -version = "2.8.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349", size = 118627, upload-time = "2026-01-20T04:27:02.457Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016, upload-time = "2026-01-20T04:27:01.012Z" }, -] - -[[package]] -name = "speechrecognition" -version = "3.14.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "audioop-lts", marker = "python_full_version >= '3.13'" }, - { name = "standard-aifc", marker = "python_full_version >= '3.13'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/26/ab/bb1c60e7bfd6b7a736f76439b78ebbfb5e92a81b626b6e94a87e166f2ea4/speechrecognition-3.14.5.tar.gz", hash = "sha256:2d185192986b9b67a1502825a330e971f59a2cae0262f727a19ad1f6b586d00a", size = 32859817, upload-time = "2025-12-31T11:25:46.518Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/a7/903429719d39ac2c42aa37086c90e816d883560f13c87d51f09a2962e021/speechrecognition-3.14.5-py3-none-any.whl", hash = "sha256:0c496d74e9f29b1daadb0d96f5660f47563e42bf09316dacdd57094c5095977e", size = 32856308, upload-time = "2025-12-31T11:25:41.161Z" }, -] - -[[package]] -name = "sse-starlette" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "starlette" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/00d280c03ffd39aaee0e86ec81e2d3b9253036a0f93f51d10503adef0e65/sse_starlette-3.2.0.tar.gz", hash = "sha256:8127594edfb51abe44eac9c49e59b0b01f1039d0c7461c6fd91d4e03b70da422", size = 27253, upload-time = "2026-01-17T13:11:05.62Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/7f/832f015020844a8b8f7a9cbc103dd76ba8e3875004c41e08440ea3a2b41a/sse_starlette-3.2.0-py3-none-any.whl", hash = "sha256:5876954bd51920fc2cd51baee47a080eb88a37b5b784e615abb0b283f801cdbf", size = 12763, upload-time = "2026-01-17T13:11:03.775Z" }, -] - -[[package]] -name = "standard-aifc" -version = "3.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "audioop-lts", marker = "python_full_version >= '3.13'" }, - { name = "standard-chunk", marker = "python_full_version >= '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c4/53/6050dc3dde1671eb3db592c13b55a8005e5040131f7509cef0215212cb84/standard_aifc-3.13.0.tar.gz", hash = "sha256:64e249c7cb4b3daf2fdba4e95721f811bde8bdfc43ad9f936589b7bb2fae2e43", size = 15240, upload-time = "2024-10-30T16:01:31.772Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/52/5fbb203394cc852334d1575cc020f6bcec768d2265355984dfd361968f36/standard_aifc-3.13.0-py3-none-any.whl", hash = "sha256:f7ae09cc57de1224a0dd8e3eb8f73830be7c3d0bc485de4c1f82b4a7f645ac66", size = 10492, upload-time = "2024-10-30T16:01:07.071Z" }, -] - -[[package]] -name = "standard-chunk" -version = "3.13.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/06/ce1bb165c1f111c7d23a1ad17204d67224baa69725bb6857a264db61beaf/standard_chunk-3.13.0.tar.gz", hash = "sha256:4ac345d37d7e686d2755e01836b8d98eda0d1a3ee90375e597ae43aaf064d654", size = 4672, upload-time = "2024-10-30T16:18:28.326Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/90/a5c1084d87767d787a6caba615aa50dc587229646308d9420c960cb5e4c0/standard_chunk-3.13.0-py3-none-any.whl", hash = "sha256:17880a26c285189c644bd5bd8f8ed2bdb795d216e3293e6dbe55bbd848e2982c", size = 4944, upload-time = "2024-10-30T16:18:26.694Z" }, -] - -[[package]] -name = "starlette" -version = "0.50.0" -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/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, -] - -[[package]] -name = "structlog" -version = "25.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ef/52/9ba0f43b686e7f3ddfeaa78ac3af750292662284b3661e91ad5494f21dbc/structlog-25.5.0.tar.gz", hash = "sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98", size = 1460830, upload-time = "2025-10-27T08:28:23.028Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/45/a132b9074aa18e799b891b91ad72133c98d8042c70f6240e4c5f9dabee2f/structlog-25.5.0-py3-none-any.whl", hash = "sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f", size = 72510, upload-time = "2025-10-27T08:28:21.535Z" }, -] - -[[package]] -name = "sympy" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mpmath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, -] - -[[package]] -name = "temporalio" -version = "1.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nexus-rpc" }, - { name = "protobuf" }, - { name = "types-protobuf" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1b/9c/b4faa1f5ebb7cc7c264456012cbad083ea3f350abe6ea0921584ab46a51e/temporalio-1.21.1.tar.gz", hash = "sha256:9d4fbfd5d8cf1afdbf9e9c34f68158073904cee227eb602602ed86c39e992bd8", size = 1854258, upload-time = "2025-12-19T22:10:20.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/87/61b9e53cb5f71ca4474e30c8454be9c3622da7f72109b6d4de9b47490d4c/temporalio-1.21.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:476c575a8eb16ee0ebc388de42c8582465c5b2e01e6c662b23585b96afdda29e", size = 12034086, upload-time = "2025-12-19T22:10:00.615Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ac/d7354048cc02de74cb11edf8d339c1579ffd9080c47782d0c1bf4d78df66/temporalio-1.21.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:1ba3980d6dff925aeff09d7de0bf82336a2c0159096801e9e755e0f01524a9a7", size = 11553488, upload-time = "2025-12-19T22:10:05.209Z" }, - { url = "https://files.pythonhosted.org/packages/52/c2/7cabddc869ccd12cb9b9abdb94277eb401bcaa23185383d8861482b15ac2/temporalio-1.21.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38dd23396e7a8acad1419873d189e2ae49ae4357b1c2a005f19e94aaaf702f90", size = 11809946, upload-time = "2025-12-19T22:10:09.443Z" }, - { url = "https://files.pythonhosted.org/packages/ca/72/4f0b647c6b4b9467dbcd4d7aa69a714bdd63731c7cb6798daade8ad8786d/temporalio-1.21.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3f62aabb4df8855e40a1f66bd2b9d9226ebb4e2641377dceaf4eab4aaf708a6", size = 12144443, upload-time = "2025-12-19T22:10:14.387Z" }, - { url = "https://files.pythonhosted.org/packages/b5/b2/cf5402ab0b962d3f017190dcadebbbffa43f94b9513014aa39e790fc0a1e/temporalio-1.21.1-cp310-abi3-win_amd64.whl", hash = "sha256:a9bebb9f55f287b44fc88e9446e3abf1f91c7e6bff1842b40b04260ce6d9ce24", size = 12692499, upload-time = "2025-12-19T22:10:18.561Z" }, -] - -[[package]] -name = "tenacity" -version = "9.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, -] - -[[package]] -name = "threadpoolctl" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, -] - -[[package]] -name = "tiktoken" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "regex" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806, upload-time = "2025-10-06T20:22:45.419Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/85/be65d39d6b647c79800fd9d29241d081d4eeb06271f383bb87200d74cf76/tiktoken-0.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b97f74aca0d78a1ff21b8cd9e9925714c15a9236d6ceacf5c7327c117e6e21e8", size = 1050728, upload-time = "2025-10-06T20:21:52.756Z" }, - { url = "https://files.pythonhosted.org/packages/4a/42/6573e9129bc55c9bf7300b3a35bef2c6b9117018acca0dc760ac2d93dffe/tiktoken-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b90f5ad190a4bb7c3eb30c5fa32e1e182ca1ca79f05e49b448438c3e225a49b", size = 994049, upload-time = "2025-10-06T20:21:53.782Z" }, - { url = "https://files.pythonhosted.org/packages/66/c5/ed88504d2f4a5fd6856990b230b56d85a777feab84e6129af0822f5d0f70/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:65b26c7a780e2139e73acc193e5c63ac754021f160df919add909c1492c0fb37", size = 1129008, upload-time = "2025-10-06T20:21:54.832Z" }, - { url = "https://files.pythonhosted.org/packages/f4/90/3dae6cc5436137ebd38944d396b5849e167896fc2073da643a49f372dc4f/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:edde1ec917dfd21c1f2f8046b86348b0f54a2c0547f68149d8600859598769ad", size = 1152665, upload-time = "2025-10-06T20:21:56.129Z" }, - { url = "https://files.pythonhosted.org/packages/a3/fe/26df24ce53ffde419a42f5f53d755b995c9318908288c17ec3f3448313a3/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:35a2f8ddd3824608b3d650a000c1ef71f730d0c56486845705a8248da00f9fe5", size = 1194230, upload-time = "2025-10-06T20:21:57.546Z" }, - { url = "https://files.pythonhosted.org/packages/20/cc/b064cae1a0e9fac84b0d2c46b89f4e57051a5f41324e385d10225a984c24/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83d16643edb7fa2c99eff2ab7733508aae1eebb03d5dfc46f5565862810f24e3", size = 1254688, upload-time = "2025-10-06T20:21:58.619Z" }, - { url = "https://files.pythonhosted.org/packages/81/10/b8523105c590c5b8349f2587e2fdfe51a69544bd5a76295fc20f2374f470/tiktoken-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffc5288f34a8bc02e1ea7047b8d041104791d2ddbf42d1e5fa07822cbffe16bd", size = 878694, upload-time = "2025-10-06T20:21:59.876Z" }, - { url = "https://files.pythonhosted.org/packages/00/61/441588ee21e6b5cdf59d6870f86beb9789e532ee9718c251b391b70c68d6/tiktoken-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3", size = 1050802, upload-time = "2025-10-06T20:22:00.96Z" }, - { url = "https://files.pythonhosted.org/packages/1f/05/dcf94486d5c5c8d34496abe271ac76c5b785507c8eae71b3708f1ad9b45a/tiktoken-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160", size = 993995, upload-time = "2025-10-06T20:22:02.788Z" }, - { url = "https://files.pythonhosted.org/packages/a0/70/5163fe5359b943f8db9946b62f19be2305de8c3d78a16f629d4165e2f40e/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa", size = 1128948, upload-time = "2025-10-06T20:22:03.814Z" }, - { url = "https://files.pythonhosted.org/packages/0c/da/c028aa0babf77315e1cef357d4d768800c5f8a6de04d0eac0f377cb619fa/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be", size = 1151986, upload-time = "2025-10-06T20:22:05.173Z" }, - { url = "https://files.pythonhosted.org/packages/a0/5a/886b108b766aa53e295f7216b509be95eb7d60b166049ce2c58416b25f2a/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a", size = 1194222, upload-time = "2025-10-06T20:22:06.265Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f8/4db272048397636ac7a078d22773dd2795b1becee7bc4922fe6207288d57/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3", size = 1255097, upload-time = "2025-10-06T20:22:07.403Z" }, - { url = "https://files.pythonhosted.org/packages/8e/32/45d02e2e0ea2be3a9ed22afc47d93741247e75018aac967b713b2941f8ea/tiktoken-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697", size = 879117, upload-time = "2025-10-06T20:22:08.418Z" }, - { url = "https://files.pythonhosted.org/packages/ce/76/994fc868f88e016e6d05b0da5ac24582a14c47893f4474c3e9744283f1d5/tiktoken-0.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16", size = 1050309, upload-time = "2025-10-06T20:22:10.939Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b8/57ef1456504c43a849821920d582a738a461b76a047f352f18c0b26c6516/tiktoken-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a", size = 993712, upload-time = "2025-10-06T20:22:12.115Z" }, - { url = "https://files.pythonhosted.org/packages/72/90/13da56f664286ffbae9dbcfadcc625439142675845baa62715e49b87b68b/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27", size = 1128725, upload-time = "2025-10-06T20:22:13.541Z" }, - { url = "https://files.pythonhosted.org/packages/05/df/4f80030d44682235bdaecd7346c90f67ae87ec8f3df4a3442cb53834f7e4/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb", size = 1151875, upload-time = "2025-10-06T20:22:14.559Z" }, - { url = "https://files.pythonhosted.org/packages/22/1f/ae535223a8c4ef4c0c1192e3f9b82da660be9eb66b9279e95c99288e9dab/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e", size = 1194451, upload-time = "2025-10-06T20:22:15.545Z" }, - { url = "https://files.pythonhosted.org/packages/78/a7/f8ead382fce0243cb625c4f266e66c27f65ae65ee9e77f59ea1653b6d730/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25", size = 1253794, upload-time = "2025-10-06T20:22:16.624Z" }, - { url = "https://files.pythonhosted.org/packages/93/e0/6cc82a562bc6365785a3ff0af27a2a092d57c47d7a81d9e2295d8c36f011/tiktoken-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f", size = 878777, upload-time = "2025-10-06T20:22:18.036Z" }, - { url = "https://files.pythonhosted.org/packages/72/05/3abc1db5d2c9aadc4d2c76fa5640134e475e58d9fbb82b5c535dc0de9b01/tiktoken-0.12.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a90388128df3b3abeb2bfd1895b0681412a8d7dc644142519e6f0a97c2111646", size = 1050188, upload-time = "2025-10-06T20:22:19.563Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7b/50c2f060412202d6c95f32b20755c7a6273543b125c0985d6fa9465105af/tiktoken-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:da900aa0ad52247d8794e307d6446bd3cdea8e192769b56276695d34d2c9aa88", size = 993978, upload-time = "2025-10-06T20:22:20.702Z" }, - { url = "https://files.pythonhosted.org/packages/14/27/bf795595a2b897e271771cd31cb847d479073497344c637966bdf2853da1/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:285ba9d73ea0d6171e7f9407039a290ca77efcdb026be7769dccc01d2c8d7fff", size = 1129271, upload-time = "2025-10-06T20:22:22.06Z" }, - { url = "https://files.pythonhosted.org/packages/f5/de/9341a6d7a8f1b448573bbf3425fa57669ac58258a667eb48a25dfe916d70/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:d186a5c60c6a0213f04a7a802264083dea1bbde92a2d4c7069e1a56630aef830", size = 1151216, upload-time = "2025-10-06T20:22:23.085Z" }, - { url = "https://files.pythonhosted.org/packages/75/0d/881866647b8d1be4d67cb24e50d0c26f9f807f994aa1510cb9ba2fe5f612/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:604831189bd05480f2b885ecd2d1986dc7686f609de48208ebbbddeea071fc0b", size = 1194860, upload-time = "2025-10-06T20:22:24.602Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1e/b651ec3059474dab649b8d5b69f5c65cd8fcd8918568c1935bd4136c9392/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8f317e8530bb3a222547b85a58583238c8f74fd7a7408305f9f63246d1a0958b", size = 1254567, upload-time = "2025-10-06T20:22:25.671Z" }, - { url = "https://files.pythonhosted.org/packages/80/57/ce64fd16ac390fafde001268c364d559447ba09b509181b2808622420eec/tiktoken-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:399c3dd672a6406719d84442299a490420b458c44d3ae65516302a99675888f3", size = 921067, upload-time = "2025-10-06T20:22:26.753Z" }, - { url = "https://files.pythonhosted.org/packages/ac/a4/72eed53e8976a099539cdd5eb36f241987212c29629d0a52c305173e0a68/tiktoken-0.12.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2c714c72bc00a38ca969dae79e8266ddec999c7ceccd603cc4f0d04ccd76365", size = 1050473, upload-time = "2025-10-06T20:22:27.775Z" }, - { url = "https://files.pythonhosted.org/packages/e6/d7/0110b8f54c008466b19672c615f2168896b83706a6611ba6e47313dbc6e9/tiktoken-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cbb9a3ba275165a2cb0f9a83f5d7025afe6b9d0ab01a22b50f0e74fee2ad253e", size = 993855, upload-time = "2025-10-06T20:22:28.799Z" }, - { url = "https://files.pythonhosted.org/packages/5f/77/4f268c41a3957c418b084dd576ea2fad2e95da0d8e1ab705372892c2ca22/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:dfdfaa5ffff8993a3af94d1125870b1d27aed7cb97aa7eb8c1cefdbc87dbee63", size = 1129022, upload-time = "2025-10-06T20:22:29.981Z" }, - { url = "https://files.pythonhosted.org/packages/4e/2b/fc46c90fe5028bd094cd6ee25a7db321cb91d45dc87531e2bdbb26b4867a/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:584c3ad3d0c74f5269906eb8a659c8bfc6144a52895d9261cdaf90a0ae5f4de0", size = 1150736, upload-time = "2025-10-06T20:22:30.996Z" }, - { url = "https://files.pythonhosted.org/packages/28/c0/3c7a39ff68022ddfd7d93f3337ad90389a342f761c4d71de99a3ccc57857/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:54c891b416a0e36b8e2045b12b33dd66fb34a4fe7965565f1b482da50da3e86a", size = 1194908, upload-time = "2025-10-06T20:22:32.073Z" }, - { url = "https://files.pythonhosted.org/packages/ab/0d/c1ad6f4016a3968c048545f5d9b8ffebf577774b2ede3e2e352553b685fe/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5edb8743b88d5be814b1a8a8854494719080c28faaa1ccbef02e87354fe71ef0", size = 1253706, upload-time = "2025-10-06T20:22:33.385Z" }, - { url = "https://files.pythonhosted.org/packages/af/df/c7891ef9d2712ad774777271d39fdef63941ffba0a9d59b7ad1fd2765e57/tiktoken-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f61c0aea5565ac82e2ec50a05e02a6c44734e91b51c10510b084ea1b8e633a71", size = 920667, upload-time = "2025-10-06T20:22:34.444Z" }, -] - -[[package]] -name = "tokenizers" -version = "0.22.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, - { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, - { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, - { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, - { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, - { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, - { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, - { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, - { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, - { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, - { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, - { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, - { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, - { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, -] - -[[package]] -name = "torch" -version = "2.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-bindings", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "setuptools" }, - { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "typing-extensions" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/54/a2ba279afcca44bbd320d4e73675b282fcee3d81400ea1b53934efca6462/torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:13ec4add8c3faaed8d13e0574f5cd4a323c11655546f91fbe6afa77b57423574", size = 79498202, upload-time = "2026-02-10T21:44:52.603Z" }, - { url = "https://files.pythonhosted.org/packages/ec/23/2c9fe0c9c27f7f6cb865abcea8a4568f29f00acaeadfc6a37f6801f84cb4/torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:e521c9f030a3774ed770a9c011751fb47c4d12029a3d6522116e48431f2ff89e", size = 79498254, upload-time = "2026-02-10T21:44:44.095Z" }, - { url = "https://files.pythonhosted.org/packages/cc/af/758e242e9102e9988969b5e621d41f36b8f258bb4a099109b7a4b4b50ea4/torch-2.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5fd4117d89ffd47e3dcc71e71a22efac24828ad781c7e46aaaf56bf7f2796acf", size = 145996088, upload-time = "2026-01-21T16:24:44.171Z" }, - { url = "https://files.pythonhosted.org/packages/23/8e/3c74db5e53bff7ed9e34c8123e6a8bfef718b2450c35eefab85bb4a7e270/torch-2.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:787124e7db3b379d4f1ed54dd12ae7c741c16a4d29b49c0226a89bea50923ffb", size = 915711952, upload-time = "2026-01-21T16:23:53.503Z" }, - { url = "https://files.pythonhosted.org/packages/6e/01/624c4324ca01f66ae4c7cd1b74eb16fb52596dce66dbe51eff95ef9e7a4c/torch-2.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2c66c61f44c5f903046cc696d088e21062644cbe541c7f1c4eaae88b2ad23547", size = 113757972, upload-time = "2026-01-21T16:24:39.516Z" }, - { url = "https://files.pythonhosted.org/packages/c9/5c/dee910b87c4d5c0fcb41b50839ae04df87c1cfc663cf1b5fca7ea565eeaa/torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:6d3707a61863d1c4d6ebba7be4ca320f42b869ee657e9b2c21c736bf17000294", size = 79498198, upload-time = "2026-01-21T16:24:34.704Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6f/f2e91e34e3fcba2e3fc8d8f74e7d6c22e74e480bbd1db7bc8900fdf3e95c/torch-2.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5c4d217b14741e40776dd7074d9006fd28b8a97ef5654db959d8635b2fe5f29b", size = 146004247, upload-time = "2026-01-21T16:24:29.335Z" }, - { url = "https://files.pythonhosted.org/packages/98/fb/5160261aeb5e1ee12ee95fe599d0541f7c976c3701d607d8fc29e623229f/torch-2.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6b71486353fce0f9714ca0c9ef1c850a2ae766b409808acd58e9678a3edb7738", size = 915716445, upload-time = "2026-01-21T16:22:45.353Z" }, - { url = "https://files.pythonhosted.org/packages/6a/16/502fb1b41e6d868e8deb5b0e3ae926bbb36dab8ceb0d1b769b266ad7b0c3/torch-2.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2ee399c644dc92ef7bc0d4f7e74b5360c37cdbe7c5ba11318dda49ffac2bc57", size = 113757050, upload-time = "2026-01-21T16:24:19.204Z" }, - { url = "https://files.pythonhosted.org/packages/1a/0b/39929b148f4824bc3ad6f9f72a29d4ad865bcf7ebfc2fa67584773e083d2/torch-2.10.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:3202429f58309b9fa96a614885eace4b7995729f44beb54d3e4a47773649d382", size = 79851305, upload-time = "2026-01-21T16:24:09.209Z" }, - { url = "https://files.pythonhosted.org/packages/d8/14/21fbce63bc452381ba5f74a2c0a959fdf5ad5803ccc0c654e752e0dbe91a/torch-2.10.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:aae1b29cd68e50a9397f5ee897b9c24742e9e306f88a807a27d617f07adb3bd8", size = 146005472, upload-time = "2026-01-21T16:22:29.022Z" }, - { url = "https://files.pythonhosted.org/packages/54/fd/b207d1c525cb570ef47f3e9f836b154685011fce11a2f444ba8a4084d042/torch-2.10.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6021db85958db2f07ec94e1bc77212721ba4920c12a18dc552d2ae36a3eb163f", size = 915612644, upload-time = "2026-01-21T16:21:47.019Z" }, - { url = "https://files.pythonhosted.org/packages/36/53/0197f868c75f1050b199fe58f9bf3bf3aecac9b4e85cc9c964383d745403/torch-2.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff43db38af76fda183156153983c9a096fc4c78d0cd1e07b14a2314c7f01c2c8", size = 113997015, upload-time = "2026-01-21T16:23:00.767Z" }, - { url = "https://files.pythonhosted.org/packages/0e/13/e76b4d9c160e89fff48bf16b449ea324bda84745d2ab30294c37c2434c0d/torch-2.10.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:cdf2a523d699b70d613243211ecaac14fe9c5df8a0b0a9c02add60fb2a413e0f", size = 79498248, upload-time = "2026-01-21T16:23:09.315Z" }, - { url = "https://files.pythonhosted.org/packages/4f/93/716b5ac0155f1be70ed81bacc21269c3ece8dba0c249b9994094110bfc51/torch-2.10.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:bf0d9ff448b0218e0433aeb198805192346c4fd659c852370d5cc245f602a06a", size = 79464992, upload-time = "2026-01-21T16:23:05.162Z" }, - { url = "https://files.pythonhosted.org/packages/69/2b/51e663ff190c9d16d4a8271203b71bc73a16aa7619b9f271a69b9d4a936b/torch-2.10.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:233aed0659a2503b831d8a67e9da66a62c996204c0bba4f4c442ccc0c68a3f60", size = 146018567, upload-time = "2026-01-21T16:22:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/5e/cd/4b95ef7f293b927c283db0b136c42be91c8ec6845c44de0238c8c23bdc80/torch-2.10.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:682497e16bdfa6efeec8cde66531bc8d1fbbbb4d8788ec6173c089ed3cc2bfe5", size = 915721646, upload-time = "2026-01-21T16:21:16.983Z" }, - { url = "https://files.pythonhosted.org/packages/56/97/078a007208f8056d88ae43198833469e61a0a355abc0b070edd2c085eb9a/torch-2.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:6528f13d2a8593a1a412ea07a99812495bec07e9224c28b2a25c0a30c7da025c", size = 113752373, upload-time = "2026-01-21T16:22:13.471Z" }, - { url = "https://files.pythonhosted.org/packages/d8/94/71994e7d0d5238393df9732fdab607e37e2b56d26a746cb59fdb415f8966/torch-2.10.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f5ab4ba32383061be0fb74bda772d470140a12c1c3b58a0cfbf3dae94d164c28", size = 79850324, upload-time = "2026-01-21T16:22:09.494Z" }, - { url = "https://files.pythonhosted.org/packages/e2/65/1a05346b418ea8ccd10360eef4b3e0ce688fba544e76edec26913a8d0ee0/torch-2.10.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:716b01a176c2a5659c98f6b01bf868244abdd896526f1c692712ab36dbaf9b63", size = 146006482, upload-time = "2026-01-21T16:22:18.42Z" }, - { url = "https://files.pythonhosted.org/packages/1d/b9/5f6f9d9e859fc3235f60578fa64f52c9c6e9b4327f0fe0defb6de5c0de31/torch-2.10.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d8f5912ba938233f86361e891789595ff35ca4b4e2ac8fe3670895e5976731d6", size = 915613050, upload-time = "2026-01-21T16:20:49.035Z" }, - { url = "https://files.pythonhosted.org/packages/66/4d/35352043ee0eaffdeff154fad67cd4a31dbed7ff8e3be1cc4549717d6d51/torch-2.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:71283a373f0ee2c89e0f0d5f446039bdabe8dbc3c9ccf35f0f784908b0acd185", size = 113995816, upload-time = "2026-01-21T16:22:05.312Z" }, -] - -[[package]] -name = "tqdm" -version = "4.67.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/27/89/4b0001b2dab8df0a5ee2787dcbe771de75ded01f18f1f8d53dedeea2882b/tqdm-4.67.2.tar.gz", hash = "sha256:649aac53964b2cb8dec76a14b405a4c0d13612cb8933aae547dd144eacc99653", size = 169514, upload-time = "2026-01-30T23:12:06.555Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/e2/31eac96de2915cf20ccaed0225035db149dfb9165a9ed28d4b252ef3f7f7/tqdm-4.67.2-py3-none-any.whl", hash = "sha256:9a12abcbbff58b6036b2167d9d3853042b9d436fe7330f06ae047867f2f8e0a7", size = 78354, upload-time = "2026-01-30T23:12:04.368Z" }, -] - -[[package]] -name = "transformers" -version = "5.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "huggingface-hub" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "regex" }, - { name = "safetensors" }, - { name = "tokenizers" }, - { name = "tqdm" }, - { name = "typer-slim" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/79/845941711811789c85fb7e2599cea425a14a07eda40f50896b9d3fda7492/transformers-5.0.0.tar.gz", hash = "sha256:5f5634efed6cf76ad068cc5834c7adbc32db78bbd6211fb70df2325a9c37dec8", size = 8424830, upload-time = "2026-01-26T10:46:46.813Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/f3/ac976fa8e305c9e49772527e09fbdc27cc6831b8a2f6b6063406626be5dd/transformers-5.0.0-py3-none-any.whl", hash = "sha256:587086f249ce64c817213cf36afdb318d087f790723e9b3d4500b97832afd52d", size = 10142091, upload-time = "2026-01-26T10:46:43.88Z" }, -] - -[[package]] -name = "tree-sitter" -version = "0.24.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/a2/698b9d31d08ad5558f8bfbfe3a0781bd4b1f284e89bde3ad18e05101a892/tree-sitter-0.24.0.tar.gz", hash = "sha256:abd95af65ca2f4f7eca356343391ed669e764f37748b5352946f00f7fc78e734", size = 168304, upload-time = "2025-01-17T05:06:38.115Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/57/3a590f287b5aa60c07d5545953912be3d252481bf5e178f750db75572bff/tree_sitter-0.24.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:14beeff5f11e223c37be7d5d119819880601a80d0399abe8c738ae2288804afc", size = 140788, upload-time = "2025-01-17T05:06:08.492Z" }, - { url = "https://files.pythonhosted.org/packages/61/0b/fc289e0cba7dbe77c6655a4dd949cd23c663fd62a8b4d8f02f97e28d7fe5/tree_sitter-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26a5b130f70d5925d67b47db314da209063664585a2fd36fa69e0717738efaf4", size = 133945, upload-time = "2025-01-17T05:06:12.39Z" }, - { url = "https://files.pythonhosted.org/packages/86/d7/80767238308a137e0b5b5c947aa243e3c1e3e430e6d0d5ae94b9a9ffd1a2/tree_sitter-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fc5c3c26d83c9d0ecb4fc4304fba35f034b7761d35286b936c1db1217558b4e", size = 564819, upload-time = "2025-01-17T05:06:13.549Z" }, - { url = "https://files.pythonhosted.org/packages/bf/b3/6c5574f4b937b836601f5fb556b24804b0a6341f2eb42f40c0e6464339f4/tree_sitter-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:772e1bd8c0931c866b848d0369b32218ac97c24b04790ec4b0e409901945dd8e", size = 579303, upload-time = "2025-01-17T05:06:16.685Z" }, - { url = "https://files.pythonhosted.org/packages/0a/f4/bd0ddf9abe242ea67cca18a64810f8af230fc1ea74b28bb702e838ccd874/tree_sitter-0.24.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:24a8dd03b0d6b8812425f3b84d2f4763322684e38baf74e5bb766128b5633dc7", size = 581054, upload-time = "2025-01-17T05:06:19.439Z" }, - { url = "https://files.pythonhosted.org/packages/8c/1c/ff23fa4931b6ef1bbeac461b904ca7e49eaec7e7e5398584e3eef836ec96/tree_sitter-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:f9e8b1605ab60ed43803100f067eed71b0b0e6c1fb9860a262727dbfbbb74751", size = 120221, upload-time = "2025-01-17T05:06:20.654Z" }, - { url = "https://files.pythonhosted.org/packages/b2/2a/9979c626f303177b7612a802237d0533155bf1e425ff6f73cc40f25453e2/tree_sitter-0.24.0-cp312-cp312-win_arm64.whl", hash = "sha256:f733a83d8355fc95561582b66bbea92ffd365c5d7a665bc9ebd25e049c2b2abb", size = 108234, upload-time = "2025-01-17T05:06:21.713Z" }, - { url = "https://files.pythonhosted.org/packages/61/cd/2348339c85803330ce38cee1c6cbbfa78a656b34ff58606ebaf5c9e83bd0/tree_sitter-0.24.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d4a6416ed421c4210f0ca405a4834d5ccfbb8ad6692d4d74f7773ef68f92071", size = 140781, upload-time = "2025-01-17T05:06:22.82Z" }, - { url = "https://files.pythonhosted.org/packages/8b/a3/1ea9d8b64e8dcfcc0051028a9c84a630301290995cd6e947bf88267ef7b1/tree_sitter-0.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0992d483677e71d5c5d37f30dfb2e3afec2f932a9c53eec4fca13869b788c6c", size = 133928, upload-time = "2025-01-17T05:06:25.146Z" }, - { url = "https://files.pythonhosted.org/packages/fe/ae/55c1055609c9428a4aedf4b164400ab9adb0b1bf1538b51f4b3748a6c983/tree_sitter-0.24.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57277a12fbcefb1c8b206186068d456c600dbfbc3fd6c76968ee22614c5cd5ad", size = 564497, upload-time = "2025-01-17T05:06:27.53Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d0/f2ffcd04882c5aa28d205a787353130cbf84b2b8a977fd211bdc3b399ae3/tree_sitter-0.24.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25fa22766d63f73716c6fec1a31ee5cf904aa429484256bd5fdf5259051ed74", size = 578917, upload-time = "2025-01-17T05:06:31.057Z" }, - { url = "https://files.pythonhosted.org/packages/af/82/aebe78ea23a2b3a79324993d4915f3093ad1af43d7c2208ee90be9273273/tree_sitter-0.24.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7d5d9537507e1c8c5fa9935b34f320bfec4114d675e028f3ad94f11cf9db37b9", size = 581148, upload-time = "2025-01-17T05:06:32.409Z" }, - { url = "https://files.pythonhosted.org/packages/a1/b4/6b0291a590c2b0417cfdb64ccb8ea242f270a46ed429c641fbc2bfab77e0/tree_sitter-0.24.0-cp313-cp313-win_amd64.whl", hash = "sha256:f58bb4956917715ec4d5a28681829a8dad5c342cafd4aea269f9132a83ca9b34", size = 120207, upload-time = "2025-01-17T05:06:34.841Z" }, - { url = "https://files.pythonhosted.org/packages/a8/18/542fd844b75272630229c9939b03f7db232c71a9d82aadc59c596319ea6a/tree_sitter-0.24.0-cp313-cp313-win_arm64.whl", hash = "sha256:23641bd25dcd4bb0b6fa91b8fb3f46cc9f1c9f475efe4d536d3f1f688d1b84c8", size = 108232, upload-time = "2025-01-17T05:06:35.831Z" }, -] - -[[package]] -name = "tree-sitter-c-sharp" -version = "0.23.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/22/85/a61c782afbb706a47d990eaee6977e7c2bd013771c5bf5c81c617684f286/tree_sitter_c_sharp-0.23.1.tar.gz", hash = "sha256:322e2cfd3a547a840375276b2aea3335fa6458aeac082f6c60fec3f745c967eb", size = 1317728, upload-time = "2024-11-11T05:25:32.535Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/04/f6c2df4c53a588ccd88d50851155945cff8cd887bd70c175e00aaade7edf/tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2b612a6e5bd17bb7fa2aab4bb6fc1fba45c94f09cb034ab332e45603b86e32fd", size = 372235, upload-time = "2024-11-11T05:25:19.424Z" }, - { url = "https://files.pythonhosted.org/packages/99/10/1aa9486f1e28fc22810fa92cbdc54e1051e7f5536a5e5b5e9695f609b31e/tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a8b98f62bc53efcd4d971151950c9b9cd5cbe3bacdb0cd69fdccac63350d83e", size = 419046, upload-time = "2024-11-11T05:25:20.679Z" }, - { url = "https://files.pythonhosted.org/packages/0f/21/13df29f8fcb9ba9f209b7b413a4764b673dfd58989a0dd67e9c7e19e9c2e/tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:986e93d845a438ec3c4416401aa98e6a6f6631d644bbbc2e43fcb915c51d255d", size = 415999, upload-time = "2024-11-11T05:25:22.359Z" }, - { url = "https://files.pythonhosted.org/packages/ca/72/fc6846795bcdae2f8aa94cc8b1d1af33d634e08be63e294ff0d6794b1efc/tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8024e466b2f5611c6dc90321f232d8584893c7fb88b75e4a831992f877616d2", size = 402830, upload-time = "2024-11-11T05:25:24.198Z" }, - { url = "https://files.pythonhosted.org/packages/fe/3a/b6028c5890ce6653807d5fa88c72232c027c6ceb480dbeb3b186d60e5971/tree_sitter_c_sharp-0.23.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7f9bf876866835492281d336b9e1f9626ab668737f74e914c31d285261507da7", size = 397880, upload-time = "2024-11-11T05:25:25.937Z" }, - { url = "https://files.pythonhosted.org/packages/47/d2/4facaa34b40f8104d8751746d0e1cd2ddf0beb9f1404b736b97f372bd1f3/tree_sitter_c_sharp-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:ae9a9e859e8f44e2b07578d44f9a220d3fa25b688966708af6aa55d42abeebb3", size = 377562, upload-time = "2024-11-11T05:25:27.539Z" }, - { url = "https://files.pythonhosted.org/packages/d8/88/3cf6bd9959d94d1fec1e6a9c530c5f08ff4115a474f62aedb5fedb0f7241/tree_sitter_c_sharp-0.23.1-cp39-abi3-win_arm64.whl", hash = "sha256:c81548347a93347be4f48cb63ec7d60ef4b0efa91313330e69641e49aa5a08c5", size = 375157, upload-time = "2024-11-11T05:25:30.839Z" }, -] - -[[package]] -name = "tree-sitter-embedded-template" -version = "0.25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/a7/77729fefab8b1b5690cfc54328f2f629d1c076d16daf32c96ba39d3a3a3a/tree_sitter_embedded_template-0.25.0.tar.gz", hash = "sha256:7d72d5e8a1d1d501a7c90e841b51f1449a90cc240be050e4fb85c22dab991d50", size = 14114, upload-time = "2025-08-29T00:42:51.078Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/9d/3e3c8ee0c019d3bace728300a1ca807c03df39e66cc51e9a5e7c9d1e1909/tree_sitter_embedded_template-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fa0d06467199aeb33fb3d6fa0665bf9b7d5a32621ffdaf37fd8249f8a8050649", size = 10266, upload-time = "2025-08-29T00:42:44.148Z" }, - { url = "https://files.pythonhosted.org/packages/e8/ab/6d4e43b736b2a895d13baea3791dc8ce7245bedf4677df9e7deb22e23a2a/tree_sitter_embedded_template-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc7aacbc2985a5d7e7fe7334f44dffe24c38fb0a8295c4188a04cf21a3d64a73", size = 10650, upload-time = "2025-08-29T00:42:45.147Z" }, - { url = "https://files.pythonhosted.org/packages/9f/97/ea3d1ea4b320fe66e0468b9f6602966e544c9fe641882484f9105e50ee0c/tree_sitter_embedded_template-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7c88c3dd8b94b3c9efe8ae071ff6b1b936a27ac5f6e651845c3b9631fa4c1c2", size = 18268, upload-time = "2025-08-29T00:42:46.03Z" }, - { url = "https://files.pythonhosted.org/packages/64/40/0f42ca894a8f7c298cf336080046ccc14c10e8f4ea46d455f640193181b2/tree_sitter_embedded_template-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:025f7ca84218dcd8455efc901bdbcc2689fb694f3a636c0448e322a23d4bc96b", size = 19068, upload-time = "2025-08-29T00:42:46.699Z" }, - { url = "https://files.pythonhosted.org/packages/d0/2a/0b720bcae7c2dd0a44889c09e800a2f8eb08c496dede9f2b97683506c4c3/tree_sitter_embedded_template-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b5dc1aef6ffa3fae621fe037d85dd98948b597afba20df29d779c426be813ee5", size = 18518, upload-time = "2025-08-29T00:42:47.694Z" }, - { url = "https://files.pythonhosted.org/packages/14/8a/d745071afa5e8bdf5b381cf84c4dc6be6c79dee6af8e0ff07476c3d8e4aa/tree_sitter_embedded_template-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d0a35cfe634c44981a516243bc039874580e02a2990669313730187ce83a5bc6", size = 18267, upload-time = "2025-08-29T00:42:48.635Z" }, - { url = "https://files.pythonhosted.org/packages/5d/74/728355e594fca140f793f234fdfec195366b6956b35754d00ea97ca18b21/tree_sitter_embedded_template-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:3e05a4ac013d54505e75ae48e1a0e9db9aab19949fe15d9f4c7345b11a84a069", size = 13049, upload-time = "2025-08-29T00:42:49.589Z" }, - { url = "https://files.pythonhosted.org/packages/d8/de/afac475e694d0e626b0808f3c86339c349cd15c5163a6a16a53cc11cf892/tree_sitter_embedded_template-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:2751d402179ac0e83f2065b249d8fe6df0718153f1636bcb6a02bde3e5730db9", size = 11978, upload-time = "2025-08-29T00:42:50.226Z" }, -] - -[[package]] -name = "tree-sitter-language-pack" -version = "0.7.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tree-sitter" }, - { name = "tree-sitter-c-sharp" }, - { name = "tree-sitter-embedded-template" }, - { name = "tree-sitter-yaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cd/ce/bc64ed55b2b8ffd4f599108be44f3fb9da8c00767502701a067be7b62c89/tree_sitter_language_pack-0.7.3.tar.gz", hash = "sha256:49139cb607d81352d33ad18e57520fc1057a009955c9ccced56607cc18e6a3fd", size = 59296297, upload-time = "2025-05-07T07:49:44.259Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/2c/07e297ddf680e8220f4c12d220762176a5837ba8d7835712051f8344e7a5/tree_sitter_language_pack-0.7.3-cp39-abi3-macosx_10_13_universal2.whl", hash = "sha256:6c4e1a48b83d8bab8d54f1d8012ae7d5a816b3972359e3fb4fe19477a6b18658", size = 28163123, upload-time = "2025-05-07T07:49:30.028Z" }, - { url = "https://files.pythonhosted.org/packages/1f/88/8761fecb761eb7b7acd6b9dc8e987813f9b58195895c437c3a7a416813da/tree_sitter_language_pack-0.7.3-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:0be05f63cd1da06bd277570bbb6cd37c9652ddd1d2ee63ff71da20a66ce36cd8", size = 17593772, upload-time = "2025-05-07T07:49:33.287Z" }, - { url = "https://files.pythonhosted.org/packages/23/15/22f731997285a7e0d68934881ffc374f2575a9e3f800af9b86af44d928a0/tree_sitter_language_pack-0.7.3-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:fd6481b0501ae3a957f673d235bdd68bc7095899f3d58882be7e31fa8e06bd66", size = 17449045, upload-time = "2025-05-07T07:49:36.053Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a0/82ee17141c5d4b4b99445ba442f397f3e783b6dc2b48fa49570f89397cdf/tree_sitter_language_pack-0.7.3-cp39-abi3-win_amd64.whl", hash = "sha256:5c0078532d839d45af0477b1b2e5b1a168e88ca3544e94b27dcba6ddbadb6511", size = 14331659, upload-time = "2025-05-07T07:49:39.957Z" }, -] - -[[package]] -name = "tree-sitter-yaml" -version = "0.7.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/b6/941d356ac70c90b9d2927375259e3a4204f38f7499ec6e7e8a95b9664689/tree_sitter_yaml-0.7.2.tar.gz", hash = "sha256:756db4c09c9d9e97c81699e8f941cb8ce4e51104927f6090eefe638ee567d32c", size = 84882, upload-time = "2025-10-07T14:40:36.071Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/29/c0b8dbff302c49ff4284666ffb6f2f21145006843bb4c3a9a85d0ec0b7ae/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:7e269ddcfcab8edb14fbb1f1d34eed1e1e26888f78f94eedfe7cc98c60f8bc9f", size = 43898, upload-time = "2025-10-07T14:40:29.486Z" }, - { url = "https://files.pythonhosted.org/packages/18/0d/15a5add06b3932b5e4ce5f5e8e179197097decfe82a0ef000952c8b98216/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:0807b7966e23ddf7dddc4545216e28b5a58cdadedcecca86b8d8c74271a07870", size = 44691, upload-time = "2025-10-07T14:40:30.369Z" }, - { url = "https://files.pythonhosted.org/packages/72/92/c4b896c90d08deb8308fadbad2210fdcc4c66c44ab4292eac4e80acb4b61/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1a5c60c98b6c4c037aae023569f020d0c489fad8dc26fdfd5510363c9c29a41", size = 91430, upload-time = "2025-10-07T14:40:31.16Z" }, - { url = "https://files.pythonhosted.org/packages/89/59/61f1fed31eb6d46ff080b8c0d53658cf29e10263f41ef5fe34768908037a/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88636d19d0654fd24f4f242eaaafa90f6f5ebdba8a62e4b32d251ed156c51a2a", size = 92428, upload-time = "2025-10-07T14:40:31.954Z" }, - { url = "https://files.pythonhosted.org/packages/e3/62/a33a04d19b7f9a0ded780b9c9fcc6279e37c5d00b89b00425bb807a22cc2/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1d2e8f0bb14aa4537320952d0f9607eef3021d5aada8383c34ebeece17db1e06", size = 90580, upload-time = "2025-10-07T14:40:33.037Z" }, - { url = "https://files.pythonhosted.org/packages/6c/e7/9525defa7b30792623f56b1fba9bbba361752348875b165b8975b87398fd/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:74ca712c50fc9d7dbc68cb36b4a7811d6e67a5466b5a789f19bf8dd6084ef752", size = 90455, upload-time = "2025-10-07T14:40:33.778Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d6/8d1e1ace03db3b02e64e91daf21d1347941d1bbecc606a5473a1a605250d/tree_sitter_yaml-0.7.2-cp310-abi3-win_amd64.whl", hash = "sha256:7587b5ca00fc4f9a548eff649697a3b395370b2304b399ceefa2087d8a6c9186", size = 45514, upload-time = "2025-10-07T14:40:34.562Z" }, - { url = "https://files.pythonhosted.org/packages/d8/c7/dcf3ea1c4f5da9b10353b9af4455d756c92d728a8f58f03c480d3ef0ead5/tree_sitter_yaml-0.7.2-cp310-abi3-win_arm64.whl", hash = "sha256:f63c227b18e7ce7587bce124578f0bbf1f890ac63d3e3cd027417574273642c4", size = 44065, upload-time = "2025-10-07T14:40:35.337Z" }, -] - -[[package]] -name = "triton" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850, upload-time = "2026-01-20T16:00:43.041Z" }, - { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450, upload-time = "2026-01-20T16:00:49.136Z" }, - { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296, upload-time = "2026-01-20T16:00:56.042Z" }, - { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" }, - { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" }, -] - -[[package]] -name = "typer" -version = "0.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, -] - -[[package]] -name = "typer-slim" -version = "0.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/17/d4/064570dec6358aa9049d4708e4a10407d74c99258f8b2136bb8702303f1a/typer_slim-0.21.1.tar.gz", hash = "sha256:73495dd08c2d0940d611c5a8c04e91c2a0a98600cbd4ee19192255a233b6dbfd", size = 110478, upload-time = "2026-01-06T11:21:11.176Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/0a/4aca634faf693e33004796b6cee0ae2e1dba375a800c16ab8d3eff4bb800/typer_slim-0.21.1-py3-none-any.whl", hash = "sha256:6e6c31047f171ac93cc5a973c9e617dbc5ab2bddc4d0a3135dc161b4e2020e0d", size = 47444, upload-time = "2026-01-06T11:21:12.441Z" }, -] - -[[package]] -name = "types-protobuf" -version = "6.32.1.20251210" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c2/59/c743a842911887cd96d56aa8936522b0cd5f7a7f228c96e81b59fced45be/types_protobuf-6.32.1.20251210.tar.gz", hash = "sha256:c698bb3f020274b1a2798ae09dc773728ce3f75209a35187bd11916ebfde6763", size = 63900, upload-time = "2025-12-10T03:14:25.451Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/43/58e75bac4219cbafee83179505ff44cae3153ec279be0e30583a73b8f108/types_protobuf-6.32.1.20251210-py3-none-any.whl", hash = "sha256:2641f78f3696822a048cfb8d0ff42ccd85c25f12f871fbebe86da63793692140", size = 77921, upload-time = "2025-12-10T03:14:24.477Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "tzdata" -version = "2025.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.40.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, -] - -[package.optional-dependencies] -standard = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "httptools" }, - { name = "python-dotenv" }, - { name = "pyyaml" }, - { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, - { name = "watchfiles" }, - { name = "websockets" }, -] - -[[package]] -name = "uvloop" -version = "0.22.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" }, - { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" }, - { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" }, - { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" }, - { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" }, - { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" }, - { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, - { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, - { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, - { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, - { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, - { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, - { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, - { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, - { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, - { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, - { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, - { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, - { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, - { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, - { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, - { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, -] - -[[package]] -name = "watchdog" -version = "6.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, - { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, - { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, - { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, - { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, -] - -[[package]] -name = "watchfiles" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" }, - { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" }, - { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" }, - { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" }, - { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" }, - { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" }, - { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" }, - { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" }, - { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" }, - { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" }, - { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, - { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, - { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, - { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, - { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, - { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, - { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, - { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, - { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, - { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, - { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, - { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, - { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, - { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, - { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, - { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, - { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, - { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, - { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, - { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, - { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, - { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, - { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, - { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, - { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, - { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, - { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, - { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, - { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, - { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, - { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, -] - -[[package]] -name = "wcwidth" -version = "0.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c2/62/a7c072fbfefb2980a00f99ca994279cb9ecf310cb2e6b2a4d2a28fe192b3/wcwidth-0.5.3.tar.gz", hash = "sha256:53123b7af053c74e9fe2e92ac810301f6139e64379031f7124574212fb3b4091", size = 157587, upload-time = "2026-01-31T03:52:10.92Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/c1/d73f12f8cdb1891334a2ccf7389eed244d3941e74d80dd220badb937f3fb/wcwidth-0.5.3-py3-none-any.whl", hash = "sha256:d584eff31cd4753e1e5ff6c12e1edfdb324c995713f75d26c29807bb84bf649e", size = 92981, upload-time = "2026-01-31T03:52:09.14Z" }, -] - -[[package]] -name = "websocket-client" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576, upload-time = "2025-10-07T21:16:36.495Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" }, -] - -[[package]] -name = "websockets" -version = "16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, - { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, - { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, - { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, - { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, - { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, - { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, - { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, - { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, - { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, -] - -[[package]] -name = "whatthepatch" -version = "1.0.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/28/55bc3e107a56fdcf7d5022cb32b8c21d98a9cc2df5cd9f3b93e10419099e/whatthepatch-1.0.7.tar.gz", hash = "sha256:9eefb4ebea5200408e02d413d2b4bc28daea6b78bb4b4d53431af7245f7d7edf", size = 34612, upload-time = "2024-11-16T17:21:22.153Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/93/af1d6ccb69ab6b5a00e03fa0cefa563f9862412667776ea15dd4eece3a90/whatthepatch-1.0.7-py3-none-any.whl", hash = "sha256:1b6f655fd31091c001c209529dfaabbabdbad438f5de14e3951266ea0fc6e7ed", size = 11964, upload-time = "2024-11-16T17:21:20.761Z" }, -] - -[[package]] -name = "win32-setctime" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, -] - -[[package]] -name = "wrapt" -version = "1.17.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998, upload-time = "2025-08-12T05:51:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020, upload-time = "2025-08-12T05:51:35.906Z" }, - { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098, upload-time = "2025-08-12T05:51:57.474Z" }, - { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036, upload-time = "2025-08-12T05:52:34.784Z" }, - { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156, upload-time = "2025-08-12T05:52:13.599Z" }, - { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102, upload-time = "2025-08-12T05:52:14.56Z" }, - { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732, upload-time = "2025-08-12T05:52:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705, upload-time = "2025-08-12T05:53:07.123Z" }, - { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877, upload-time = "2025-08-12T05:53:05.436Z" }, - { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885, upload-time = "2025-08-12T05:52:54.367Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, - { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, - { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, - { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, - { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, - { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, - { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, - { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, - { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, - { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, - { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, - { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, - { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, - { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, - { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, - { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, - { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, - { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, - { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, -] - -[[package]] -name = "xlrd" -version = "2.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/07/5a/377161c2d3538d1990d7af382c79f3b2372e880b65de21b01b1a2b78691e/xlrd-2.0.2.tar.gz", hash = "sha256:08b5e25de58f21ce71dc7db3b3b8106c1fa776f3024c54e45b45b374e89234c9", size = 100167, upload-time = "2025-06-14T08:46:39.039Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/62/c8d562e7766786ba6587d09c5a8ba9f718ed3fa8af7f4553e8f91c36f302/xlrd-2.0.2-py2.py3-none-any.whl", hash = "sha256:ea762c3d29f4cca48d82df517b6d89fbce4db3107f9d78713e48cd321d5c9aa9", size = 96555, upload-time = "2025-06-14T08:46:37.766Z" }, -] - -[[package]] -name = "xlsxwriter" -version = "3.2.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/46/2c/c06ef49dc36e7954e55b802a8b231770d286a9758b3d936bd1e04ce5ba88/xlsxwriter-3.2.9.tar.gz", hash = "sha256:254b1c37a368c444eac6e2f867405cc9e461b0ed97a3233b2ac1e574efb4140c", size = 215940, upload-time = "2025-09-16T00:16:21.63Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/0c/3662f4a66880196a590b202f0db82d919dd2f89e99a27fadef91c4a33d41/xlsxwriter-3.2.9-py3-none-any.whl", hash = "sha256:9a5db42bc5dff014806c58a20b9eae7322a134abb6fce3c92c181bfb275ec5b3", size = 175315, upload-time = "2025-09-16T00:16:20.108Z" }, -] - -[[package]] -name = "yarl" -version = "1.22.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "multidict" }, - { name = "propcache" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" }, - { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" }, - { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" }, - { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" }, - { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" }, - { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" }, - { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" }, - { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" }, - { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" }, - { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" }, - { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" }, - { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" }, - { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" }, - { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" }, - { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" }, - { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" }, - { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" }, - { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" }, - { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" }, - { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" }, - { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" }, - { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" }, - { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" }, - { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" }, - { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" }, - { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" }, - { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" }, - { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" }, - { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" }, - { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" }, - { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" }, - { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" }, - { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" }, - { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" }, - { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" }, - { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" }, - { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" }, - { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" }, - { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" }, - { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" }, - { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" }, - { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" }, - { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" }, - { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" }, - { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" }, - { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" }, - { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" }, - { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" }, - { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" }, - { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" }, - { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" }, - { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" }, - { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" }, - { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" }, - { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" }, - { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" }, - { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" }, - { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" }, - { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" }, - { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" }, - { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" }, - { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" }, - { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" }, - { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" }, - { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" }, - { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" }, - { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" }, - { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" }, - { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" }, - { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" }, - { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" }, - { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, - { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, - { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, - { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, -] - -[[package]] -name = "youtube-transcript-api" -version = "1.2.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "defusedxml" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/60/43/4104185a2eaa839daa693b30e15c37e7e58795e8e09ec414f22b3db54bec/youtube_transcript_api-1.2.4.tar.gz", hash = "sha256:b72d0e96a335df599d67cee51d49e143cff4f45b84bcafc202ff51291603ddcd", size = 469839, upload-time = "2026-01-29T09:09:17.088Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/95/129ea37efd6cd6ed00f62baae6543345c677810b8a3bf0026756e1d3cf3c/youtube_transcript_api-1.2.4-py3-none-any.whl", hash = "sha256:03878759356da5caf5edac77431780b91448fb3d8c21d4496015bdc8a7bc43ff", size = 485227, upload-time = "2026-01-29T09:09:15.427Z" }, -] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] diff --git a/pkg/hanzoai/README.md b/pkg/hanzoai/README.md deleted file mode 100644 index d66759961..000000000 --- a/pkg/hanzoai/README.md +++ /dev/null @@ -1,260 +0,0 @@ -# Hanzo AI Client Library - -[![PyPI version](https://img.shields.io/pypi/v/hanzoai.svg)](https://pypi.org/project/hanzoai/) - -A unified AI client library providing access to 100+ LLM providers through a single OpenAI-compatible interface. Part of the [Hanzo AI SDK ecosystem](https://github.com/hanzoai/python-sdk). - -## Features - -- **100+ LLM Providers**: OpenAI, Anthropic, Google, AWS Bedrock, Azure, and more -- **OpenAI-Compatible**: Drop-in replacement for `openai` package -- **Unified Gateway**: Route requests through Hanzo LLM proxy for cost optimization -- **Type Safety**: Full TypeScript-style type hints for all APIs -- **Async Support**: Both sync and async clients -- **Cost Tracking**: Built-in usage monitoring and rate limiting - -## Installation - -```sh -pip install hanzoai -``` - -## Quick Start - -```python -from hanzoai import Hanzo - -# Initialize client (uses HANZO_API_KEY env var by default) -client = Hanzo() - -# Chat completions (OpenAI-compatible) -response = client.chat.completions.create( - model="gpt-4", # or any supported model - messages=[ - {"role": "user", "content": "Hello, world!"} - ] -) - -print(response.choices[0].message.content) -``` - -## Supported Models - -### OpenAI -- `gpt-4`, `gpt-4-turbo`, `gpt-4o` -- `gpt-3.5-turbo` - -### Anthropic -- `claude-3-5-sonnet`, `claude-3-opus`, `claude-3-haiku` - -### Google -- `gemini-pro`, `gemini-1.5-pro` - -### Open Source -- `llama-3-70b`, `llama-3-8b` -- `mixtral-8x7b`, `mixtral-8x22b` -- `qwen-2-72b` - -And 90+ more models from providers like AWS Bedrock, Azure, Together AI, Replicate, and others. - -## Usage Examples - -### Basic Chat -```python -from hanzoai import Hanzo - -client = Hanzo(api_key="your-hanzo-api-key") - -response = client.chat.completions.create( - model="claude-3-5-sonnet", - messages=[ - {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": "Explain quantum computing"} - ], - max_tokens=1000, - temperature=0.7 -) - -print(response.choices[0].message.content) -``` - -### Streaming Responses -```python -stream = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Write a story"}], - stream=True -) - -for chunk in stream: - if chunk.choices[0].delta.content is not None: - print(chunk.choices[0].delta.content, end="") -``` - -### Async Client -```python -import asyncio -from hanzoai import AsyncHanzo - -async def main(): - client = AsyncHanzo(api_key="your-api-key") - - response = await client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello async world!"}] - ) - - print(response.choices[0].message.content) - -asyncio.run(main()) -``` - -### Multiple Providers -```python -# Route different models through the same interface -responses = [] - -for model in ["gpt-4", "claude-3-5-sonnet", "llama-3-70b"]: - response = client.chat.completions.create( - model=model, - messages=[{"role": "user", "content": "What is AI?"}], - max_tokens=100 - ) - responses.append((model, response.choices[0].message.content)) - -for model, answer in responses: - print(f"\n{model}:") - print(answer) -``` - -## Configuration - -### Environment Variables -```bash -export HANZO_API_KEY="your-api-key" -export HANZO_BASE_URL="https://api.hanzo.ai" # Optional, defaults to hanzo.ai -``` - -### Client Configuration -```python -client = Hanzo( - api_key="your-api-key", - base_url="https://api.hanzo.ai", # Custom endpoint - timeout=60.0, # Request timeout - max_retries=3 # Retry failed requests -) -``` - -## Advanced Features - -### Cost Tracking -```python -# Get usage statistics -usage = client.usage.get() -print(f"Total tokens: {usage.total_tokens}") -print(f"Total cost: ${usage.total_cost}") - -# Set budget limits -client.budget.create( - limit=100.0, # $100 monthly limit - period="monthly" -) -``` - -### Custom Headers -```python -client = Hanzo( - default_headers={ - "User-Agent": "MyApp/1.0", - "X-Custom-Header": "value" - } -) -``` - -### Error Handling -```python -from hanzoai import APIError, RateLimitError, AuthenticationError - -try: - response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello!"}] - ) -except AuthenticationError: - print("Invalid API key") -except RateLimitError: - print("Rate limit exceeded") -except APIError as e: - print(f"API error: {e}") -``` - -## Drop-in OpenAI Replacement - -Replace `openai` imports with `hanzoai` for instant access to 100+ models: - -```python -# Before -from openai import OpenAI -client = OpenAI(api_key="...") - -# After -from hanzoai import Hanzo as OpenAI # Alias for compatibility -client = OpenAI(api_key="...") - -# Same interface, 100x more models! -``` - -## Integration with Hanzo Ecosystem - -This package integrates seamlessly with other Hanzo AI components: - -```python -# Use with Hanzo CLI -import hanzo -client = hanzo.Client() # Auto-configured from CLI - -# Use with Hanzo MCP tools -from hanzo.mcp import llm_tool -llm_tool.set_client(client) - -# Use with Hanzo Agents -from hanzo.agents import Agent -agent = Agent(llm_client=client) -``` - -## API Compatibility - -The `hanzoai` package implements the OpenAI API specification: -- Chat Completions (`/v1/chat/completions`) -- Embeddings (`/v1/embeddings`) -- Images (`/v1/images/generations`) -- Audio (`/v1/audio/transcriptions`, `/v1/audio/speech`) -- Files (`/v1/files`) -- Fine-tuning (`/v1/fine_tuning/jobs`) - -Plus Hanzo-specific extensions: -- Multi-provider routing -- Cost optimization -- Usage analytics -- Model switching - -## Documentation - -- **[Full API Reference](api.md)** - Complete API documentation -- **[Hanzo AI Docs](https://docs.hanzo.ai)** - Official documentation -- **[Model Provider Guide](https://docs.hanzo.ai/providers)** - Supported models -- **[Migration Guide](https://docs.hanzo.ai/migration)** - Migrate from OpenAI - -## Related Packages - -- **[hanzo](https://pypi.org/project/hanzo/)** - CLI and network tools -- **[hanzo-mcp](https://pypi.org/project/hanzo-mcp/)** - MCP development environment -- **[hanzo-agents](https://pypi.org/project/hanzo-agents/)** - Multi-agent workflows - -## Contributing - -We welcome contributions! See [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines. - -## License - -Apache 2.0 - see [LICENSE](../../LICENSE) for details. \ No newline at end of file diff --git a/pkg/hanzoai/__init__.py b/pkg/hanzoai/__init__.py deleted file mode 100644 index c38595dcc..000000000 --- a/pkg/hanzoai/__init__.py +++ /dev/null @@ -1,149 +0,0 @@ -# Hanzo AI SDK - -# Import new modules -from . import mcp, auth, grpo, types, agents, cluster, config, protocols, session -from .config import ConfigLoader, RuntimeConfig -from .session import Session, CompactionConfig, compact_session -from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes -from ._utils import file_from_path -from ._client import ( - ENVIRONMENTS, - Hanzo, - Client, - Stream, - Timeout, - AsyncHanzo, - AsyncClient, - AsyncStream, - RequestOptions, -) -from ._models import BaseModel -from ._version import __title__, __version__ -from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse -from ._constants import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_CONNECTION_LIMITS -from .llm_client import SimpleLLMClient, OpenAICompatibleClient, completion, set_api_key -from ._exceptions import ( - APIError, - HanzoError, - ConflictError, - NotFoundError, - APIStatusError, - RateLimitError, - APITimeoutError, - BadRequestError, - APIConnectionError, - AuthenticationError, - InternalServerError, - PermissionDeniedError, - UnprocessableEntityError, - APIResponseValidationError, -) -from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient -from ._utils._logs import setup_logging as _setup_logging -from .protocols import ( - ApiClient, - ApiRequest, - AssistantEvent, - ConversationRuntime, - PermissionMode, - PermissionOutcome, - PermissionPolicy, - PermissionPrompter, - PermissionRequest, - StaticToolExecutor, - TokenUsage, - ToolExecutor, - TurnSummary, - UsageTracker, -) - -__all__ = [ - "types", - "__version__", - "__title__", - "NoneType", - "Transport", - "ProxiesTypes", - "NotGiven", - "NOT_GIVEN", - "Omit", - "SimpleLLMClient", - "OpenAICompatibleClient", - "completion", - "set_api_key", - "HanzoError", - "APIError", - "APIStatusError", - "APITimeoutError", - "APIConnectionError", - "APIResponseValidationError", - "BadRequestError", - "AuthenticationError", - "PermissionDeniedError", - "NotFoundError", - "ConflictError", - "UnprocessableEntityError", - "RateLimitError", - "InternalServerError", - "Timeout", - "RequestOptions", - "Client", - "AsyncClient", - "Stream", - "AsyncStream", - "Hanzo", - "AsyncHanzo", - "ENVIRONMENTS", - "file_from_path", - "BaseModel", - "DEFAULT_TIMEOUT", - "DEFAULT_MAX_RETRIES", - "DEFAULT_CONNECTION_LIMITS", - "DefaultHttpxClient", - "DefaultAsyncHttpxClient", - # New modules - "agents", - "mcp", - "cluster", - "auth", - "grpo", - "protocols", - "config", - "ConfigLoader", - "RuntimeConfig", - # Protocol abstractions - "ToolExecutor", - "StaticToolExecutor", - "PermissionMode", - "PermissionRequest", - "PermissionOutcome", - "PermissionPrompter", - "PermissionPolicy", - "ApiClient", - "ApiRequest", - "AssistantEvent", - "TokenUsage", - "UsageTracker", - "ConversationRuntime", - "TurnSummary", - # Session management - "session", - "Session", - "CompactionConfig", - "compact_session", -] - -_setup_logging() - -# Update the __module__ attribute for exported symbols so that -# error messages point to this module instead of the module -# it was originally defined in, e.g. -# hanzoai._exceptions.NotFoundError -> hanzoai.NotFoundError -__locals = locals() -for __name in __all__: - if not __name.startswith("__"): - try: - __locals[__name].__module__ = "hanzoai" - except (TypeError, AttributeError): - # Some of our exported symbols are builtins which we can't set attributes for. - pass diff --git a/pkg/hanzoai/_client.py b/pkg/hanzoai/_client.py deleted file mode 100644 index e422727be..000000000 --- a/pkg/hanzoai/_client.py +++ /dev/null @@ -1,1766 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -import os -from typing import Any, Dict, Union, Mapping, cast -from typing_extensions import Self, Literal, override - -import httpx - -from . import _exceptions -from ._qs import Querystring -from ._types import ( - NOT_GIVEN, - Body, - Omit, - Query, - Headers, - Timeout, - NotGiven, - Transport, - ProxiesTypes, - RequestOptions, -) -from ._utils import ( - is_given, - get_async_library, -) -from ._version import __version__ -from ._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .resources import ( - db, - kv, - add, - dns, - iam, - kms, - mpc, - cart, - edge, - jobs, - paas, - pods, - test, - user, - audit, - azure, - build, - chain, - docdb, - miner, - nodes, - pages, - spend, - tasks, - utils, - access, - active, - agents, - budget, - cohere, - delete, - device, - gemini, - graphs, - health, - models, - orders, - policy, - pubsub, - queues, - rerank, - routes, - stores, - tokens, - tunnel, - bedrock, - billing, - coupons, - gateway, - ingress, - network, - release, - secrets, - storage, - vectors, - wallets, - checkout, - commerce, - customer, - identity, - langfuse, - machines, - products, - provider, - registry, - settings, - anthropic, - campaigns, - datastore, - inference, - providers, - referrals, - vertex_ai, - workflows, - affiliates, - assemblyai, - assistants, - containers, - embeddings, - guardrails, - completions, - credentials, - deployments, - mcp_servers, - model_group, - moderations, - eu_assemblyai, - observability, - subscriptions, - team_workspace, -) -from ._streaming import Stream as Stream, AsyncStream as AsyncStream -from ._exceptions import HanzoError, APIStatusError -from ._base_client import ( - DEFAULT_MAX_RETRIES, - SyncAPIClient, - AsyncAPIClient, - make_request_options, -) -from .resources.key import key -from .resources.chat import chat -from .resources.team import team -from .resources.audio import audio -from .resources.cache import cache -from .resources.files import files -from .resources.model import model -from .resources.config import config -from .resources.images import images -from .resources.openai import openai -from .resources.batches import batches -from .resources.engines import engines -from .resources.global_ import global_ -from .resources.threads import threads -from .resources.responses import responses -from .resources.fine_tuning import fine_tuning -from .resources.organization import organization - -__all__ = [ - "ENVIRONMENTS", - "Timeout", - "Transport", - "ProxiesTypes", - "RequestOptions", - "Hanzo", - "AsyncHanzo", - "Client", - "AsyncClient", -] - -ENVIRONMENTS: Dict[str, str] = { - "production": "https://api.hanzo.ai", - "sandbox": "https://api.sandbox.hanzo.ai", -} - - -class Hanzo(SyncAPIClient): - models: models.ModelsResource - openai: openai.OpenAIResource - engines: engines.EnginesResource - chat: chat.ChatResource - completions: completions.CompletionsResource - embeddings: embeddings.EmbeddingsResource - images: images.ImagesResource - audio: audio.AudioResource - assistants: assistants.AssistantsResource - threads: threads.ThreadsResource - moderations: moderations.ModerationsResource - utils: utils.UtilsResource - model: model.ModelResource - model_group: model_group.ModelGroupResource - routes: routes.RoutesResource - responses: responses.ResponsesResource - batches: batches.BatchesResource - rerank: rerank.RerankResource - fine_tuning: fine_tuning.FineTuningResource - credentials: credentials.CredentialsResource - vertex_ai: vertex_ai.VertexAIResource - gemini: gemini.GeminiResource - cohere: cohere.CohereResource - anthropic: anthropic.AnthropicResource - bedrock: bedrock.BedrockResource - eu_assemblyai: eu_assemblyai.EuAssemblyaiResource - assemblyai: assemblyai.AssemblyaiResource - azure: azure.AzureResource - langfuse: langfuse.LangfuseResource - config: config.ConfigResource - test: test.TestResource - health: health.HealthResource - active: active.ActiveResource - settings: settings.SettingsResource - key: key.KeyResource - user: user.UserResource - team: team.TeamResource - organization: organization.OrganizationResource - customer: customer.CustomerResource - spend: spend.SpendResource - global_: global_.GlobalResource - provider: provider.ProviderResource - cache: cache.CacheResource - guardrails: guardrails.GuardrailsResource - add: add.AddResource - delete: delete.DeleteResource - files: files.FilesResource - budget: budget.BudgetResource - # Data/Infrastructure resources - db: db.DBResource - kv: kv.KVResource - dns: dns.DNSResource - pages: pages.PagesResource - kms: kms.KMSResource - cart: cart.CartResource - edge: edge.EdgeResource - jobs: jobs.JobsResource - pods: pods.PodsResource - audit: audit.AuditResource - build: build.BuildResource - chain: chain.ChainResource - miner: miner.MinerResource - nodes: nodes.NodesResource - tasks: tasks.TasksResource - access: access.AccessResource - agents: agents.AgentsResource - device: device.DeviceResource - graphs: graphs.GraphsResource - orders: orders.OrdersResource - policy: policy.PolicyResource - pubsub: pubsub.PubSubResource - queues: queues.QueuesResource - stores: stores.StoresResource - tokens: tokens.TokensResource - tunnel: tunnel.TunnelResource - coupons: coupons.CouponsResource - gateway: gateway.GatewayResource - network: network.NetworkResource - release: release.ReleaseResource - secrets: secrets.SecretsResource - storage: storage.StorageResource - vectors: vectors.VectorsResource - wallets: wallets.WalletsResource - checkout: checkout.CheckoutResource - identity: identity.IdentityResource - machines: machines.MachinesResource - products: products.ProductsResource - registry: registry.RegistryResource - campaigns: campaigns.CampaignsResource - inference: inference.InferenceResource - providers: providers.ProvidersResource - referrals: referrals.ReferralsResource - workflows: workflows.WorkflowsResource - affiliates: affiliates.AffiliatesResource - containers: containers.ContainersResource - deployments: deployments.DeploymentsResource - mcp_servers: mcp_servers.MCPServersResource - observability: observability.ObservabilityResource - subscriptions: subscriptions.SubscriptionsResource - # New platform resources - mpc: mpc.MPCResource - paas: paas.PaaSResource - docdb: docdb.DocDBResource - ingress: ingress.IngressResource - datastore: datastore.DatastoreResource - billing: billing.BillingResource - iam: iam.IAMResource - commerce: commerce.CommerceResource - team_workspace: team_workspace.TeamWorkspaceResource - with_raw_response: HanzoWithRawResponse - with_streaming_response: HanzoWithStreamedResponse - - # client options - api_key: str - - _environment: Literal["production", "sandbox"] | NotGiven - - def __init__( - self, - *, - api_key: str | None = None, - environment: Literal["production", "sandbox"] | NotGiven = NOT_GIVEN, - base_url: str | httpx.URL | None | NotGiven = NOT_GIVEN, - timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - # Configure a custom httpx client. - # We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. - # See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. - http_client: httpx.Client | None = None, - # Enable or disable schema validation for data returned by the API. - # When enabled an error APIResponseValidationError is raised - # if the API responds with invalid data for the expected schema. - # - # This parameter may be removed or changed in the future. - # If you rely on this feature, please open a GitHub issue - # outlining your use-case to help us decide if it should be - # part of our public interface in the future. - _strict_response_validation: bool = False, - ) -> None: - """Construct a new synchronous Hanzo client instance. - - This automatically infers the `api_key` argument from the `HANZO_API_KEY` environment variable if it is not provided. - """ - if api_key is None: - api_key = os.environ.get("HANZO_API_KEY") - if api_key is None: - raise HanzoError( - "The api_key client option must be set either by passing api_key to the client or by setting the HANZO_API_KEY environment variable" - ) - self.api_key = api_key - - self._environment = environment - - base_url_env = os.environ.get("HANZO_BASE_URL") - if is_given(base_url) and base_url is not None: - # cast required because mypy doesn't understand the type narrowing - base_url = cast( - "str | httpx.URL", base_url - ) # pyright: ignore[reportUnnecessaryCast] - elif is_given(environment): - if base_url_env and base_url is not None: - raise ValueError( - "Ambiguous URL; The `HANZO_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None", - ) - - try: - base_url = ENVIRONMENTS[environment] - except KeyError as exc: - raise ValueError(f"Unknown environment: {environment}") from exc - elif base_url_env is not None: - base_url = base_url_env - else: - self._environment = environment = "production" - - try: - base_url = ENVIRONMENTS[environment] - except KeyError as exc: - raise ValueError(f"Unknown environment: {environment}") from exc - - super().__init__( - version=__version__, - base_url=base_url, - max_retries=max_retries, - timeout=timeout, - http_client=http_client, - custom_headers=default_headers, - custom_query=default_query, - _strict_response_validation=_strict_response_validation, - ) - - self.models = models.ModelsResource(self) - self.openai = openai.OpenAIResource(self) - self.engines = engines.EnginesResource(self) - self.chat = chat.ChatResource(self) - self.completions = completions.CompletionsResource(self) - self.embeddings = embeddings.EmbeddingsResource(self) - self.images = images.ImagesResource(self) - self.audio = audio.AudioResource(self) - self.assistants = assistants.AssistantsResource(self) - self.threads = threads.ThreadsResource(self) - self.moderations = moderations.ModerationsResource(self) - self.utils = utils.UtilsResource(self) - self.model = model.ModelResource(self) - self.model_group = model_group.ModelGroupResource(self) - self.routes = routes.RoutesResource(self) - self.responses = responses.ResponsesResource(self) - self.batches = batches.BatchesResource(self) - self.rerank = rerank.RerankResource(self) - self.fine_tuning = fine_tuning.FineTuningResource(self) - self.credentials = credentials.CredentialsResource(self) - self.vertex_ai = vertex_ai.VertexAIResource(self) - self.gemini = gemini.GeminiResource(self) - self.cohere = cohere.CohereResource(self) - self.anthropic = anthropic.AnthropicResource(self) - self.bedrock = bedrock.BedrockResource(self) - self.eu_assemblyai = eu_assemblyai.EuAssemblyaiResource(self) - self.assemblyai = assemblyai.AssemblyaiResource(self) - self.azure = azure.AzureResource(self) - self.langfuse = langfuse.LangfuseResource(self) - self.config = config.ConfigResource(self) - self.test = test.TestResource(self) - self.health = health.HealthResource(self) - self.active = active.ActiveResource(self) - self.settings = settings.SettingsResource(self) - self.key = key.KeyResource(self) - self.user = user.UserResource(self) - self.team = team.TeamResource(self) - self.organization = organization.OrganizationResource(self) - self.customer = customer.CustomerResource(self) - self.spend = spend.SpendResource(self) - self.global_ = global_.GlobalResource(self) - self.provider = provider.ProviderResource(self) - self.cache = cache.CacheResource(self) - self.guardrails = guardrails.GuardrailsResource(self) - self.add = add.AddResource(self) - self.delete = delete.DeleteResource(self) - self.files = files.FilesResource(self) - self.budget = budget.BudgetResource(self) - self.db = db.DBResource(self) - self.kv = kv.KVResource(self) - self.dns = dns.DNSResource(self) - self.pages = pages.PagesResource(self) - self.kms = kms.KMSResource(self) - self.cart = cart.CartResource(self) - self.edge = edge.EdgeResource(self) - self.jobs = jobs.JobsResource(self) - self.pods = pods.PodsResource(self) - self.audit = audit.AuditResource(self) - self.build = build.BuildResource(self) - self.chain = chain.ChainResource(self) - self.miner = miner.MinerResource(self) - self.nodes = nodes.NodesResource(self) - self.tasks = tasks.TasksResource(self) - self.access = access.AccessResource(self) - self.agents = agents.AgentsResource(self) - self.device = device.DeviceResource(self) - self.graphs = graphs.GraphsResource(self) - self.orders = orders.OrdersResource(self) - self.policy = policy.PolicyResource(self) - self.pubsub = pubsub.PubSubResource(self) - self.queues = queues.QueuesResource(self) - self.stores = stores.StoresResource(self) - self.tokens = tokens.TokensResource(self) - self.tunnel = tunnel.TunnelResource(self) - self.coupons = coupons.CouponsResource(self) - self.gateway = gateway.GatewayResource(self) - self.network = network.NetworkResource(self) - self.release = release.ReleaseResource(self) - self.secrets = secrets.SecretsResource(self) - self.storage = storage.StorageResource(self) - self.vectors = vectors.VectorsResource(self) - self.wallets = wallets.WalletsResource(self) - self.checkout = checkout.CheckoutResource(self) - self.identity = identity.IdentityResource(self) - self.machines = machines.MachinesResource(self) - self.products = products.ProductsResource(self) - self.registry = registry.RegistryResource(self) - self.campaigns = campaigns.CampaignsResource(self) - self.inference = inference.InferenceResource(self) - self.providers = providers.ProvidersResource(self) - self.referrals = referrals.ReferralsResource(self) - self.workflows = workflows.WorkflowsResource(self) - self.affiliates = affiliates.AffiliatesResource(self) - self.containers = containers.ContainersResource(self) - self.deployments = deployments.DeploymentsResource(self) - self.mcp_servers = mcp_servers.MCPServersResource(self) - self.observability = observability.ObservabilityResource(self) - self.subscriptions = subscriptions.SubscriptionsResource(self) - self.mpc = mpc.MPCResource(self) - self.paas = paas.PaaSResource(self) - self.docdb = docdb.DocDBResource(self) - self.ingress = ingress.IngressResource(self) - self.datastore = datastore.DatastoreResource(self) - self.billing = billing.BillingResource(self) - self.iam = iam.IAMResource(self) - self.commerce = commerce.CommerceResource(self) - self.team_workspace = team_workspace.TeamWorkspaceResource(self) - self.with_raw_response = HanzoWithRawResponse(self) - self.with_streaming_response = HanzoWithStreamedResponse(self) - - @property - @override - def qs(self) -> Querystring: - return Querystring(array_format="comma") - - @property - @override - def auth_headers(self) -> dict[str, str]: - api_key = self.api_key - return {"Ocp-Apim-Subscription-Key": api_key} - - @property - @override - def default_headers(self) -> dict[str, str | Omit]: - return { - **super().default_headers, - "X-SDK-Async": "false", - **self._custom_headers, - } - - def copy( - self, - *, - api_key: str | None = None, - environment: Literal["production", "sandbox"] | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - http_client: httpx.Client | None = None, - max_retries: int | NotGiven = NOT_GIVEN, - default_headers: Mapping[str, str] | None = None, - set_default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - set_default_query: Mapping[str, object] | None = None, - _extra_kwargs: Mapping[str, Any] = {}, - ) -> Self: - """ - Create a new client instance re-using the same options given to the current client with optional overriding. - """ - if default_headers is not None and set_default_headers is not None: - raise ValueError( - "The `default_headers` and `set_default_headers` arguments are mutually exclusive" - ) - - if default_query is not None and set_default_query is not None: - raise ValueError( - "The `default_query` and `set_default_query` arguments are mutually exclusive" - ) - - headers = self._custom_headers - if default_headers is not None: - headers = {**headers, **default_headers} - elif set_default_headers is not None: - headers = set_default_headers - - params = self._custom_query - if default_query is not None: - params = {**params, **default_query} - elif set_default_query is not None: - params = set_default_query - - http_client = http_client or self._client - return self.__class__( - api_key=api_key or self.api_key, - base_url=base_url or self.base_url, - environment=environment or self._environment, - timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, - http_client=http_client, - max_retries=max_retries if is_given(max_retries) else self.max_retries, - default_headers=headers, - default_query=params, - **_extra_kwargs, - ) - - # Alias for `copy` for nicer inline usage, e.g. - # client.with_options(timeout=10).foo.create(...) - with_options = copy - - def get_home( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Home""" - return self.get( - "/", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - @override - def _make_status_error( - self, - err_msg: str, - *, - body: object, - response: httpx.Response, - ) -> APIStatusError: - if response.status_code == 400: - return _exceptions.BadRequestError(err_msg, response=response, body=body) - - if response.status_code == 401: - return _exceptions.AuthenticationError( - err_msg, response=response, body=body - ) - - if response.status_code == 403: - return _exceptions.PermissionDeniedError( - err_msg, response=response, body=body - ) - - if response.status_code == 404: - return _exceptions.NotFoundError(err_msg, response=response, body=body) - - if response.status_code == 409: - return _exceptions.ConflictError(err_msg, response=response, body=body) - - if response.status_code == 422: - return _exceptions.UnprocessableEntityError( - err_msg, response=response, body=body - ) - - if response.status_code == 429: - return _exceptions.RateLimitError(err_msg, response=response, body=body) - - if response.status_code >= 500: - return _exceptions.InternalServerError( - err_msg, response=response, body=body - ) - return APIStatusError(err_msg, response=response, body=body) - - -class AsyncHanzo(AsyncAPIClient): - models: models.AsyncModelsResource - openai: openai.AsyncOpenAIResource - engines: engines.AsyncEnginesResource - chat: chat.AsyncChatResource - completions: completions.AsyncCompletionsResource - embeddings: embeddings.AsyncEmbeddingsResource - images: images.AsyncImagesResource - audio: audio.AsyncAudioResource - assistants: assistants.AsyncAssistantsResource - threads: threads.AsyncThreadsResource - moderations: moderations.AsyncModerationsResource - utils: utils.AsyncUtilsResource - model: model.AsyncModelResource - model_group: model_group.AsyncModelGroupResource - routes: routes.AsyncRoutesResource - responses: responses.AsyncResponsesResource - batches: batches.AsyncBatchesResource - rerank: rerank.AsyncRerankResource - fine_tuning: fine_tuning.AsyncFineTuningResource - credentials: credentials.AsyncCredentialsResource - vertex_ai: vertex_ai.AsyncVertexAIResource - gemini: gemini.AsyncGeminiResource - cohere: cohere.AsyncCohereResource - anthropic: anthropic.AsyncAnthropicResource - bedrock: bedrock.AsyncBedrockResource - eu_assemblyai: eu_assemblyai.AsyncEuAssemblyaiResource - assemblyai: assemblyai.AsyncAssemblyaiResource - azure: azure.AsyncAzureResource - langfuse: langfuse.AsyncLangfuseResource - config: config.AsyncConfigResource - test: test.AsyncTestResource - health: health.AsyncHealthResource - active: active.AsyncActiveResource - settings: settings.AsyncSettingsResource - key: key.AsyncKeyResource - user: user.AsyncUserResource - team: team.AsyncTeamResource - organization: organization.AsyncOrganizationResource - customer: customer.AsyncCustomerResource - spend: spend.AsyncSpendResource - global_: global_.AsyncGlobalResource - provider: provider.AsyncProviderResource - cache: cache.AsyncCacheResource - guardrails: guardrails.AsyncGuardrailsResource - add: add.AsyncAddResource - delete: delete.AsyncDeleteResource - files: files.AsyncFilesResource - budget: budget.AsyncBudgetResource - # Data/Infrastructure resources - db: db.AsyncDBResource - kv: kv.AsyncKVResource - dns: dns.AsyncDNSResource - pages: pages.AsyncPagesResource - kms: kms.AsyncKMSResource - cart: cart.AsyncCartResource - edge: edge.AsyncEdgeResource - jobs: jobs.AsyncJobsResource - pods: pods.AsyncPodsResource - audit: audit.AsyncAuditResource - build: build.AsyncBuildResource - chain: chain.AsyncChainResource - miner: miner.AsyncMinerResource - nodes: nodes.AsyncNodesResource - tasks: tasks.AsyncTasksResource - access: access.AsyncAccessResource - agents: agents.AsyncAgentsResource - device: device.AsyncDeviceResource - graphs: graphs.AsyncGraphsResource - orders: orders.AsyncOrdersResource - policy: policy.AsyncPolicyResource - pubsub: pubsub.AsyncPubSubResource - queues: queues.AsyncQueuesResource - stores: stores.AsyncStoresResource - tokens: tokens.AsyncTokensResource - tunnel: tunnel.AsyncTunnelResource - coupons: coupons.AsyncCouponsResource - gateway: gateway.AsyncGatewayResource - network: network.AsyncNetworkResource - release: release.AsyncReleaseResource - secrets: secrets.AsyncSecretsResource - storage: storage.AsyncStorageResource - vectors: vectors.AsyncVectorsResource - wallets: wallets.AsyncWalletsResource - checkout: checkout.AsyncCheckoutResource - identity: identity.AsyncIdentityResource - machines: machines.AsyncMachinesResource - products: products.AsyncProductsResource - registry: registry.AsyncRegistryResource - campaigns: campaigns.AsyncCampaignsResource - inference: inference.AsyncInferenceResource - providers: providers.AsyncProvidersResource - referrals: referrals.AsyncReferralsResource - workflows: workflows.AsyncWorkflowsResource - affiliates: affiliates.AsyncAffiliatesResource - containers: containers.AsyncContainersResource - deployments: deployments.AsyncDeploymentsResource - mcp_servers: mcp_servers.AsyncMCPServersResource - observability: observability.AsyncObservabilityResource - subscriptions: subscriptions.AsyncSubscriptionsResource - # New platform resources - mpc: mpc.AsyncMPCResource - paas: paas.AsyncPaaSResource - docdb: docdb.AsyncDocDBResource - ingress: ingress.AsyncIngressResource - datastore: datastore.AsyncDatastoreResource - billing: billing.AsyncBillingResource - iam: iam.AsyncIAMResource - commerce: commerce.AsyncCommerceResource - team_workspace: team_workspace.AsyncTeamWorkspaceResource - with_raw_response: AsyncHanzoWithRawResponse - with_streaming_response: AsyncHanzoWithStreamedResponse - - # client options - api_key: str - - _environment: Literal["production", "sandbox"] | NotGiven - - def __init__( - self, - *, - api_key: str | None = None, - environment: Literal["production", "sandbox"] | NotGiven = NOT_GIVEN, - base_url: str | httpx.URL | None | NotGiven = NOT_GIVEN, - timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - # Configure a custom httpx client. - # We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. - # See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details. - http_client: httpx.AsyncClient | None = None, - # Enable or disable schema validation for data returned by the API. - # When enabled an error APIResponseValidationError is raised - # if the API responds with invalid data for the expected schema. - # - # This parameter may be removed or changed in the future. - # If you rely on this feature, please open a GitHub issue - # outlining your use-case to help us decide if it should be - # part of our public interface in the future. - _strict_response_validation: bool = False, - ) -> None: - """Construct a new async AsyncHanzo client instance. - - This automatically infers the `api_key` argument from the `HANZO_API_KEY` environment variable if it is not provided. - """ - if api_key is None: - api_key = os.environ.get("HANZO_API_KEY") - if api_key is None: - raise HanzoError( - "The api_key client option must be set either by passing api_key to the client or by setting the HANZO_API_KEY environment variable" - ) - self.api_key = api_key - - self._environment = environment - - base_url_env = os.environ.get("HANZO_BASE_URL") - if is_given(base_url) and base_url is not None: - # cast required because mypy doesn't understand the type narrowing - base_url = cast( - "str | httpx.URL", base_url - ) # pyright: ignore[reportUnnecessaryCast] - elif is_given(environment): - if base_url_env and base_url is not None: - raise ValueError( - "Ambiguous URL; The `HANZO_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None", - ) - - try: - base_url = ENVIRONMENTS[environment] - except KeyError as exc: - raise ValueError(f"Unknown environment: {environment}") from exc - elif base_url_env is not None: - base_url = base_url_env - else: - self._environment = environment = "production" - - try: - base_url = ENVIRONMENTS[environment] - except KeyError as exc: - raise ValueError(f"Unknown environment: {environment}") from exc - - super().__init__( - version=__version__, - base_url=base_url, - max_retries=max_retries, - timeout=timeout, - http_client=http_client, - custom_headers=default_headers, - custom_query=default_query, - _strict_response_validation=_strict_response_validation, - ) - - self.models = models.AsyncModelsResource(self) - self.openai = openai.AsyncOpenAIResource(self) - self.engines = engines.AsyncEnginesResource(self) - self.chat = chat.AsyncChatResource(self) - self.completions = completions.AsyncCompletionsResource(self) - self.embeddings = embeddings.AsyncEmbeddingsResource(self) - self.images = images.AsyncImagesResource(self) - self.audio = audio.AsyncAudioResource(self) - self.assistants = assistants.AsyncAssistantsResource(self) - self.threads = threads.AsyncThreadsResource(self) - self.moderations = moderations.AsyncModerationsResource(self) - self.utils = utils.AsyncUtilsResource(self) - self.model = model.AsyncModelResource(self) - self.model_group = model_group.AsyncModelGroupResource(self) - self.routes = routes.AsyncRoutesResource(self) - self.responses = responses.AsyncResponsesResource(self) - self.batches = batches.AsyncBatchesResource(self) - self.rerank = rerank.AsyncRerankResource(self) - self.fine_tuning = fine_tuning.AsyncFineTuningResource(self) - self.credentials = credentials.AsyncCredentialsResource(self) - self.vertex_ai = vertex_ai.AsyncVertexAIResource(self) - self.gemini = gemini.AsyncGeminiResource(self) - self.cohere = cohere.AsyncCohereResource(self) - self.anthropic = anthropic.AsyncAnthropicResource(self) - self.bedrock = bedrock.AsyncBedrockResource(self) - self.eu_assemblyai = eu_assemblyai.AsyncEuAssemblyaiResource(self) - self.assemblyai = assemblyai.AsyncAssemblyaiResource(self) - self.azure = azure.AsyncAzureResource(self) - self.langfuse = langfuse.AsyncLangfuseResource(self) - self.config = config.AsyncConfigResource(self) - self.test = test.AsyncTestResource(self) - self.health = health.AsyncHealthResource(self) - self.active = active.AsyncActiveResource(self) - self.settings = settings.AsyncSettingsResource(self) - self.key = key.AsyncKeyResource(self) - self.user = user.AsyncUserResource(self) - self.team = team.AsyncTeamResource(self) - self.organization = organization.AsyncOrganizationResource(self) - self.customer = customer.AsyncCustomerResource(self) - self.spend = spend.AsyncSpendResource(self) - self.global_ = global_.AsyncGlobalResource(self) - self.provider = provider.AsyncProviderResource(self) - self.cache = cache.AsyncCacheResource(self) - self.guardrails = guardrails.AsyncGuardrailsResource(self) - self.add = add.AsyncAddResource(self) - self.delete = delete.AsyncDeleteResource(self) - self.files = files.AsyncFilesResource(self) - self.budget = budget.AsyncBudgetResource(self) - self.db = db.AsyncDBResource(self) - self.kv = kv.AsyncKVResource(self) - self.dns = dns.AsyncDNSResource(self) - self.pages = pages.AsyncPagesResource(self) - self.kms = kms.AsyncKMSResource(self) - self.cart = cart.AsyncCartResource(self) - self.edge = edge.AsyncEdgeResource(self) - self.jobs = jobs.AsyncJobsResource(self) - self.pods = pods.AsyncPodsResource(self) - self.audit = audit.AsyncAuditResource(self) - self.build = build.AsyncBuildResource(self) - self.chain = chain.AsyncChainResource(self) - self.miner = miner.AsyncMinerResource(self) - self.nodes = nodes.AsyncNodesResource(self) - self.tasks = tasks.AsyncTasksResource(self) - self.access = access.AsyncAccessResource(self) - self.agents = agents.AsyncAgentsResource(self) - self.device = device.AsyncDeviceResource(self) - self.graphs = graphs.AsyncGraphsResource(self) - self.orders = orders.AsyncOrdersResource(self) - self.policy = policy.AsyncPolicyResource(self) - self.pubsub = pubsub.AsyncPubSubResource(self) - self.queues = queues.AsyncQueuesResource(self) - self.stores = stores.AsyncStoresResource(self) - self.tokens = tokens.AsyncTokensResource(self) - self.tunnel = tunnel.AsyncTunnelResource(self) - self.coupons = coupons.AsyncCouponsResource(self) - self.gateway = gateway.AsyncGatewayResource(self) - self.network = network.AsyncNetworkResource(self) - self.release = release.AsyncReleaseResource(self) - self.secrets = secrets.AsyncSecretsResource(self) - self.storage = storage.AsyncStorageResource(self) - self.vectors = vectors.AsyncVectorsResource(self) - self.wallets = wallets.AsyncWalletsResource(self) - self.checkout = checkout.AsyncCheckoutResource(self) - self.identity = identity.AsyncIdentityResource(self) - self.machines = machines.AsyncMachinesResource(self) - self.products = products.AsyncProductsResource(self) - self.registry = registry.AsyncRegistryResource(self) - self.campaigns = campaigns.AsyncCampaignsResource(self) - self.inference = inference.AsyncInferenceResource(self) - self.providers = providers.AsyncProvidersResource(self) - self.referrals = referrals.AsyncReferralsResource(self) - self.workflows = workflows.AsyncWorkflowsResource(self) - self.affiliates = affiliates.AsyncAffiliatesResource(self) - self.containers = containers.AsyncContainersResource(self) - self.deployments = deployments.AsyncDeploymentsResource(self) - self.mcp_servers = mcp_servers.AsyncMCPServersResource(self) - self.observability = observability.AsyncObservabilityResource(self) - self.subscriptions = subscriptions.AsyncSubscriptionsResource(self) - self.mpc = mpc.AsyncMPCResource(self) - self.paas = paas.AsyncPaaSResource(self) - self.docdb = docdb.AsyncDocDBResource(self) - self.ingress = ingress.AsyncIngressResource(self) - self.datastore = datastore.AsyncDatastoreResource(self) - self.billing = billing.AsyncBillingResource(self) - self.iam = iam.AsyncIAMResource(self) - self.commerce = commerce.AsyncCommerceResource(self) - self.team_workspace = team_workspace.AsyncTeamWorkspaceResource(self) - self.with_raw_response = AsyncHanzoWithRawResponse(self) - self.with_streaming_response = AsyncHanzoWithStreamedResponse(self) - - @property - @override - def qs(self) -> Querystring: - return Querystring(array_format="comma") - - @property - @override - def auth_headers(self) -> dict[str, str]: - api_key = self.api_key - return {"Ocp-Apim-Subscription-Key": api_key} - - @property - @override - def default_headers(self) -> dict[str, str | Omit]: - return { - **super().default_headers, - "X-SDK-Async": f"async:{get_async_library()}", - **self._custom_headers, - } - - def copy( - self, - *, - api_key: str | None = None, - environment: Literal["production", "sandbox"] | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - http_client: httpx.AsyncClient | None = None, - max_retries: int | NotGiven = NOT_GIVEN, - default_headers: Mapping[str, str] | None = None, - set_default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - set_default_query: Mapping[str, object] | None = None, - _extra_kwargs: Mapping[str, Any] = {}, - ) -> Self: - """ - Create a new client instance re-using the same options given to the current client with optional overriding. - """ - if default_headers is not None and set_default_headers is not None: - raise ValueError( - "The `default_headers` and `set_default_headers` arguments are mutually exclusive" - ) - - if default_query is not None and set_default_query is not None: - raise ValueError( - "The `default_query` and `set_default_query` arguments are mutually exclusive" - ) - - headers = self._custom_headers - if default_headers is not None: - headers = {**headers, **default_headers} - elif set_default_headers is not None: - headers = set_default_headers - - params = self._custom_query - if default_query is not None: - params = {**params, **default_query} - elif set_default_query is not None: - params = set_default_query - - http_client = http_client or self._client - return self.__class__( - api_key=api_key or self.api_key, - base_url=base_url or self.base_url, - environment=environment or self._environment, - timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, - http_client=http_client, - max_retries=max_retries if is_given(max_retries) else self.max_retries, - default_headers=headers, - default_query=params, - **_extra_kwargs, - ) - - # Alias for `copy` for nicer inline usage, e.g. - # client.with_options(timeout=10).foo.create(...) - with_options = copy - - async def get_home( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Home""" - return await self.get( - "/", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - @override - def _make_status_error( - self, - err_msg: str, - *, - body: object, - response: httpx.Response, - ) -> APIStatusError: - if response.status_code == 400: - return _exceptions.BadRequestError(err_msg, response=response, body=body) - - if response.status_code == 401: - return _exceptions.AuthenticationError( - err_msg, response=response, body=body - ) - - if response.status_code == 403: - return _exceptions.PermissionDeniedError( - err_msg, response=response, body=body - ) - - if response.status_code == 404: - return _exceptions.NotFoundError(err_msg, response=response, body=body) - - if response.status_code == 409: - return _exceptions.ConflictError(err_msg, response=response, body=body) - - if response.status_code == 422: - return _exceptions.UnprocessableEntityError( - err_msg, response=response, body=body - ) - - if response.status_code == 429: - return _exceptions.RateLimitError(err_msg, response=response, body=body) - - if response.status_code >= 500: - return _exceptions.InternalServerError( - err_msg, response=response, body=body - ) - return APIStatusError(err_msg, response=response, body=body) - - -class HanzoWithRawResponse: - def __init__(self, client: Hanzo) -> None: - self.models = models.ModelsResourceWithRawResponse(client.models) - self.openai = openai.OpenAIResourceWithRawResponse(client.openai) - self.engines = engines.EnginesResourceWithRawResponse(client.engines) - self.chat = chat.ChatResourceWithRawResponse(client.chat) - self.completions = completions.CompletionsResourceWithRawResponse( - client.completions - ) - self.embeddings = embeddings.EmbeddingsResourceWithRawResponse( - client.embeddings - ) - self.images = images.ImagesResourceWithRawResponse(client.images) - self.audio = audio.AudioResourceWithRawResponse(client.audio) - self.assistants = assistants.AssistantsResourceWithRawResponse( - client.assistants - ) - self.threads = threads.ThreadsResourceWithRawResponse(client.threads) - self.moderations = moderations.ModerationsResourceWithRawResponse( - client.moderations - ) - self.utils = utils.UtilsResourceWithRawResponse(client.utils) - self.model = model.ModelResourceWithRawResponse(client.model) - self.model_group = model_group.ModelGroupResourceWithRawResponse( - client.model_group - ) - self.routes = routes.RoutesResourceWithRawResponse(client.routes) - self.responses = responses.ResponsesResourceWithRawResponse(client.responses) - self.batches = batches.BatchesResourceWithRawResponse(client.batches) - self.rerank = rerank.RerankResourceWithRawResponse(client.rerank) - self.fine_tuning = fine_tuning.FineTuningResourceWithRawResponse( - client.fine_tuning - ) - self.credentials = credentials.CredentialsResourceWithRawResponse( - client.credentials - ) - self.vertex_ai = vertex_ai.VertexAIResourceWithRawResponse(client.vertex_ai) - self.gemini = gemini.GeminiResourceWithRawResponse(client.gemini) - self.cohere = cohere.CohereResourceWithRawResponse(client.cohere) - self.anthropic = anthropic.AnthropicResourceWithRawResponse(client.anthropic) - self.bedrock = bedrock.BedrockResourceWithRawResponse(client.bedrock) - self.eu_assemblyai = eu_assemblyai.EuAssemblyaiResourceWithRawResponse( - client.eu_assemblyai - ) - self.assemblyai = assemblyai.AssemblyaiResourceWithRawResponse( - client.assemblyai - ) - self.azure = azure.AzureResourceWithRawResponse(client.azure) - self.langfuse = langfuse.LangfuseResourceWithRawResponse(client.langfuse) - self.config = config.ConfigResourceWithRawResponse(client.config) - self.test = test.TestResourceWithRawResponse(client.test) - self.health = health.HealthResourceWithRawResponse(client.health) - self.active = active.ActiveResourceWithRawResponse(client.active) - self.settings = settings.SettingsResourceWithRawResponse(client.settings) - self.key = key.KeyResourceWithRawResponse(client.key) - self.user = user.UserResourceWithRawResponse(client.user) - self.team = team.TeamResourceWithRawResponse(client.team) - self.organization = organization.OrganizationResourceWithRawResponse( - client.organization - ) - self.customer = customer.CustomerResourceWithRawResponse(client.customer) - self.spend = spend.SpendResourceWithRawResponse(client.spend) - self.global_ = global_.GlobalResourceWithRawResponse(client.global_) - self.provider = provider.ProviderResourceWithRawResponse(client.provider) - self.cache = cache.CacheResourceWithRawResponse(client.cache) - self.guardrails = guardrails.GuardrailsResourceWithRawResponse( - client.guardrails - ) - self.add = add.AddResourceWithRawResponse(client.add) - self.delete = delete.DeleteResourceWithRawResponse(client.delete) - self.files = files.FilesResourceWithRawResponse(client.files) - self.budget = budget.BudgetResourceWithRawResponse(client.budget) - self.db = db.DBResourceWithRawResponse(client.db) - self.kv = kv.KVResourceWithRawResponse(client.kv) - self.dns = dns.DNSResourceWithRawResponse(client.dns) - self.pages = pages.PagesResourceWithRawResponse(client.pages) - self.kms = kms.KMSResourceWithRawResponse(client.kms) - self.cart = cart.CartResourceWithRawResponse(client.cart) - self.edge = edge.EdgeResourceWithRawResponse(client.edge) - self.jobs = jobs.JobsResourceWithRawResponse(client.jobs) - self.pods = pods.PodsResourceWithRawResponse(client.pods) - self.audit = audit.AuditResourceWithRawResponse(client.audit) - self.build = build.BuildResourceWithRawResponse(client.build) - self.chain = chain.ChainResourceWithRawResponse(client.chain) - self.miner = miner.MinerResourceWithRawResponse(client.miner) - self.nodes = nodes.NodesResourceWithRawResponse(client.nodes) - self.tasks = tasks.TasksResourceWithRawResponse(client.tasks) - self.access = access.AccessResourceWithRawResponse(client.access) - self.agents = agents.AgentsResourceWithRawResponse(client.agents) - self.device = device.DeviceResourceWithRawResponse(client.device) - self.graphs = graphs.GraphsResourceWithRawResponse(client.graphs) - self.orders = orders.OrdersResourceWithRawResponse(client.orders) - self.policy = policy.PolicyResourceWithRawResponse(client.policy) - self.pubsub = pubsub.PubSubResourceWithRawResponse(client.pubsub) - self.queues = queues.QueuesResourceWithRawResponse(client.queues) - self.stores = stores.StoresResourceWithRawResponse(client.stores) - self.tokens = tokens.TokensResourceWithRawResponse(client.tokens) - self.tunnel = tunnel.TunnelResourceWithRawResponse(client.tunnel) - self.coupons = coupons.CouponsResourceWithRawResponse(client.coupons) - self.gateway = gateway.GatewayResourceWithRawResponse(client.gateway) - self.network = network.NetworkResourceWithRawResponse(client.network) - self.release = release.ReleaseResourceWithRawResponse(client.release) - self.secrets = secrets.SecretsResourceWithRawResponse(client.secrets) - self.storage = storage.StorageResourceWithRawResponse(client.storage) - self.vectors = vectors.VectorsResourceWithRawResponse(client.vectors) - self.wallets = wallets.WalletsResourceWithRawResponse(client.wallets) - self.checkout = checkout.CheckoutResourceWithRawResponse(client.checkout) - self.identity = identity.IdentityResourceWithRawResponse(client.identity) - self.machines = machines.MachinesResourceWithRawResponse(client.machines) - self.products = products.ProductsResourceWithRawResponse(client.products) - self.registry = registry.RegistryResourceWithRawResponse(client.registry) - self.campaigns = campaigns.CampaignsResourceWithRawResponse(client.campaigns) - self.inference = inference.InferenceResourceWithRawResponse(client.inference) - self.providers = providers.ProvidersResourceWithRawResponse(client.providers) - self.referrals = referrals.ReferralsResourceWithRawResponse(client.referrals) - self.workflows = workflows.WorkflowsResourceWithRawResponse(client.workflows) - self.affiliates = affiliates.AffiliatesResourceWithRawResponse( - client.affiliates - ) - self.containers = containers.ContainersResourceWithRawResponse( - client.containers - ) - self.deployments = deployments.DeploymentsResourceWithRawResponse( - client.deployments - ) - self.mcp_servers = mcp_servers.MCPServersResourceWithRawResponse( - client.mcp_servers - ) - self.observability = observability.ObservabilityResourceWithRawResponse( - client.observability - ) - self.subscriptions = subscriptions.SubscriptionsResourceWithRawResponse( - client.subscriptions - ) - self.mpc = mpc.MPCResourceWithRawResponse(client.mpc) - self.paas = paas.PaaSResourceWithRawResponse(client.paas) - self.docdb = docdb.DocDBResourceWithRawResponse(client.docdb) - self.ingress = ingress.IngressResourceWithRawResponse(client.ingress) - self.datastore = datastore.DatastoreResourceWithRawResponse(client.datastore) - self.billing = billing.BillingResourceWithRawResponse(client.billing) - self.iam = iam.IAMResourceWithRawResponse(client.iam) - self.commerce = commerce.CommerceResourceWithRawResponse(client.commerce) - self.team_workspace = team_workspace.TeamWorkspaceResourceWithRawResponse( - client.team_workspace - ) - - self.get_home = to_raw_response_wrapper( - client.get_home, - ) - - -class AsyncHanzoWithRawResponse: - def __init__(self, client: AsyncHanzo) -> None: - self.models = models.AsyncModelsResourceWithRawResponse(client.models) - self.openai = openai.AsyncOpenAIResourceWithRawResponse(client.openai) - self.engines = engines.AsyncEnginesResourceWithRawResponse(client.engines) - self.chat = chat.AsyncChatResourceWithRawResponse(client.chat) - self.completions = completions.AsyncCompletionsResourceWithRawResponse( - client.completions - ) - self.embeddings = embeddings.AsyncEmbeddingsResourceWithRawResponse( - client.embeddings - ) - self.images = images.AsyncImagesResourceWithRawResponse(client.images) - self.audio = audio.AsyncAudioResourceWithRawResponse(client.audio) - self.assistants = assistants.AsyncAssistantsResourceWithRawResponse( - client.assistants - ) - self.threads = threads.AsyncThreadsResourceWithRawResponse(client.threads) - self.moderations = moderations.AsyncModerationsResourceWithRawResponse( - client.moderations - ) - self.utils = utils.AsyncUtilsResourceWithRawResponse(client.utils) - self.model = model.AsyncModelResourceWithRawResponse(client.model) - self.model_group = model_group.AsyncModelGroupResourceWithRawResponse( - client.model_group - ) - self.routes = routes.AsyncRoutesResourceWithRawResponse(client.routes) - self.responses = responses.AsyncResponsesResourceWithRawResponse( - client.responses - ) - self.batches = batches.AsyncBatchesResourceWithRawResponse(client.batches) - self.rerank = rerank.AsyncRerankResourceWithRawResponse(client.rerank) - self.fine_tuning = fine_tuning.AsyncFineTuningResourceWithRawResponse( - client.fine_tuning - ) - self.credentials = credentials.AsyncCredentialsResourceWithRawResponse( - client.credentials - ) - self.vertex_ai = vertex_ai.AsyncVertexAIResourceWithRawResponse( - client.vertex_ai - ) - self.gemini = gemini.AsyncGeminiResourceWithRawResponse(client.gemini) - self.cohere = cohere.AsyncCohereResourceWithRawResponse(client.cohere) - self.anthropic = anthropic.AsyncAnthropicResourceWithRawResponse( - client.anthropic - ) - self.bedrock = bedrock.AsyncBedrockResourceWithRawResponse(client.bedrock) - self.eu_assemblyai = eu_assemblyai.AsyncEuAssemblyaiResourceWithRawResponse( - client.eu_assemblyai - ) - self.assemblyai = assemblyai.AsyncAssemblyaiResourceWithRawResponse( - client.assemblyai - ) - self.azure = azure.AsyncAzureResourceWithRawResponse(client.azure) - self.langfuse = langfuse.AsyncLangfuseResourceWithRawResponse(client.langfuse) - self.config = config.AsyncConfigResourceWithRawResponse(client.config) - self.test = test.AsyncTestResourceWithRawResponse(client.test) - self.health = health.AsyncHealthResourceWithRawResponse(client.health) - self.active = active.AsyncActiveResourceWithRawResponse(client.active) - self.settings = settings.AsyncSettingsResourceWithRawResponse(client.settings) - self.key = key.AsyncKeyResourceWithRawResponse(client.key) - self.user = user.AsyncUserResourceWithRawResponse(client.user) - self.team = team.AsyncTeamResourceWithRawResponse(client.team) - self.organization = organization.AsyncOrganizationResourceWithRawResponse( - client.organization - ) - self.customer = customer.AsyncCustomerResourceWithRawResponse(client.customer) - self.spend = spend.AsyncSpendResourceWithRawResponse(client.spend) - self.global_ = global_.AsyncGlobalResourceWithRawResponse(client.global_) - self.provider = provider.AsyncProviderResourceWithRawResponse(client.provider) - self.cache = cache.AsyncCacheResourceWithRawResponse(client.cache) - self.guardrails = guardrails.AsyncGuardrailsResourceWithRawResponse( - client.guardrails - ) - self.add = add.AsyncAddResourceWithRawResponse(client.add) - self.delete = delete.AsyncDeleteResourceWithRawResponse(client.delete) - self.files = files.AsyncFilesResourceWithRawResponse(client.files) - self.budget = budget.AsyncBudgetResourceWithRawResponse(client.budget) - self.db = db.AsyncDBResourceWithRawResponse(client.db) - self.kv = kv.AsyncKVResourceWithRawResponse(client.kv) - self.dns = dns.AsyncDNSResourceWithRawResponse(client.dns) - self.pages = pages.AsyncPagesResourceWithRawResponse(client.pages) - self.kms = kms.AsyncKMSResourceWithRawResponse(client.kms) - self.cart = cart.AsyncCartResourceWithRawResponse(client.cart) - self.edge = edge.AsyncEdgeResourceWithRawResponse(client.edge) - self.jobs = jobs.AsyncJobsResourceWithRawResponse(client.jobs) - self.pods = pods.AsyncPodsResourceWithRawResponse(client.pods) - self.audit = audit.AsyncAuditResourceWithRawResponse(client.audit) - self.build = build.AsyncBuildResourceWithRawResponse(client.build) - self.chain = chain.AsyncChainResourceWithRawResponse(client.chain) - self.miner = miner.AsyncMinerResourceWithRawResponse(client.miner) - self.nodes = nodes.AsyncNodesResourceWithRawResponse(client.nodes) - self.tasks = tasks.AsyncTasksResourceWithRawResponse(client.tasks) - self.access = access.AsyncAccessResourceWithRawResponse(client.access) - self.agents = agents.AsyncAgentsResourceWithRawResponse(client.agents) - self.device = device.AsyncDeviceResourceWithRawResponse(client.device) - self.graphs = graphs.AsyncGraphsResourceWithRawResponse(client.graphs) - self.orders = orders.AsyncOrdersResourceWithRawResponse(client.orders) - self.policy = policy.AsyncPolicyResourceWithRawResponse(client.policy) - self.pubsub = pubsub.AsyncPubSubResourceWithRawResponse(client.pubsub) - self.queues = queues.AsyncQueuesResourceWithRawResponse(client.queues) - self.stores = stores.AsyncStoresResourceWithRawResponse(client.stores) - self.tokens = tokens.AsyncTokensResourceWithRawResponse(client.tokens) - self.tunnel = tunnel.AsyncTunnelResourceWithRawResponse(client.tunnel) - self.coupons = coupons.AsyncCouponsResourceWithRawResponse(client.coupons) - self.gateway = gateway.AsyncGatewayResourceWithRawResponse(client.gateway) - self.network = network.AsyncNetworkResourceWithRawResponse(client.network) - self.release = release.AsyncReleaseResourceWithRawResponse(client.release) - self.secrets = secrets.AsyncSecretsResourceWithRawResponse(client.secrets) - self.storage = storage.AsyncStorageResourceWithRawResponse(client.storage) - self.vectors = vectors.AsyncVectorsResourceWithRawResponse(client.vectors) - self.wallets = wallets.AsyncWalletsResourceWithRawResponse(client.wallets) - self.checkout = checkout.AsyncCheckoutResourceWithRawResponse(client.checkout) - self.identity = identity.AsyncIdentityResourceWithRawResponse(client.identity) - self.machines = machines.AsyncMachinesResourceWithRawResponse(client.machines) - self.products = products.AsyncProductsResourceWithRawResponse(client.products) - self.registry = registry.AsyncRegistryResourceWithRawResponse(client.registry) - self.campaigns = campaigns.AsyncCampaignsResourceWithRawResponse( - client.campaigns - ) - self.inference = inference.AsyncInferenceResourceWithRawResponse( - client.inference - ) - self.providers = providers.AsyncProvidersResourceWithRawResponse( - client.providers - ) - self.referrals = referrals.AsyncReferralsResourceWithRawResponse( - client.referrals - ) - self.workflows = workflows.AsyncWorkflowsResourceWithRawResponse( - client.workflows - ) - self.affiliates = affiliates.AsyncAffiliatesResourceWithRawResponse( - client.affiliates - ) - self.containers = containers.AsyncContainersResourceWithRawResponse( - client.containers - ) - self.deployments = deployments.AsyncDeploymentsResourceWithRawResponse( - client.deployments - ) - self.mcp_servers = mcp_servers.AsyncMCPServersResourceWithRawResponse( - client.mcp_servers - ) - self.observability = observability.AsyncObservabilityResourceWithRawResponse( - client.observability - ) - self.subscriptions = subscriptions.AsyncSubscriptionsResourceWithRawResponse( - client.subscriptions - ) - self.mpc = mpc.AsyncMPCResourceWithRawResponse(client.mpc) - self.paas = paas.AsyncPaaSResourceWithRawResponse(client.paas) - self.docdb = docdb.AsyncDocDBResourceWithRawResponse(client.docdb) - self.ingress = ingress.AsyncIngressResourceWithRawResponse(client.ingress) - self.datastore = datastore.AsyncDatastoreResourceWithRawResponse( - client.datastore - ) - self.billing = billing.AsyncBillingResourceWithRawResponse(client.billing) - self.iam = iam.AsyncIAMResourceWithRawResponse(client.iam) - self.commerce = commerce.AsyncCommerceResourceWithRawResponse(client.commerce) - self.team_workspace = team_workspace.AsyncTeamWorkspaceResourceWithRawResponse( - client.team_workspace - ) - - self.get_home = async_to_raw_response_wrapper( - client.get_home, - ) - - -class HanzoWithStreamedResponse: - def __init__(self, client: Hanzo) -> None: - self.models = models.ModelsResourceWithStreamingResponse(client.models) - self.openai = openai.OpenAIResourceWithStreamingResponse(client.openai) - self.engines = engines.EnginesResourceWithStreamingResponse(client.engines) - self.chat = chat.ChatResourceWithStreamingResponse(client.chat) - self.completions = completions.CompletionsResourceWithStreamingResponse( - client.completions - ) - self.embeddings = embeddings.EmbeddingsResourceWithStreamingResponse( - client.embeddings - ) - self.images = images.ImagesResourceWithStreamingResponse(client.images) - self.audio = audio.AudioResourceWithStreamingResponse(client.audio) - self.assistants = assistants.AssistantsResourceWithStreamingResponse( - client.assistants - ) - self.threads = threads.ThreadsResourceWithStreamingResponse(client.threads) - self.moderations = moderations.ModerationsResourceWithStreamingResponse( - client.moderations - ) - self.utils = utils.UtilsResourceWithStreamingResponse(client.utils) - self.model = model.ModelResourceWithStreamingResponse(client.model) - self.model_group = model_group.ModelGroupResourceWithStreamingResponse( - client.model_group - ) - self.routes = routes.RoutesResourceWithStreamingResponse(client.routes) - self.responses = responses.ResponsesResourceWithStreamingResponse( - client.responses - ) - self.batches = batches.BatchesResourceWithStreamingResponse(client.batches) - self.rerank = rerank.RerankResourceWithStreamingResponse(client.rerank) - self.fine_tuning = fine_tuning.FineTuningResourceWithStreamingResponse( - client.fine_tuning - ) - self.credentials = credentials.CredentialsResourceWithStreamingResponse( - client.credentials - ) - self.vertex_ai = vertex_ai.VertexAIResourceWithStreamingResponse( - client.vertex_ai - ) - self.gemini = gemini.GeminiResourceWithStreamingResponse(client.gemini) - self.cohere = cohere.CohereResourceWithStreamingResponse(client.cohere) - self.anthropic = anthropic.AnthropicResourceWithStreamingResponse( - client.anthropic - ) - self.bedrock = bedrock.BedrockResourceWithStreamingResponse(client.bedrock) - self.eu_assemblyai = eu_assemblyai.EuAssemblyaiResourceWithStreamingResponse( - client.eu_assemblyai - ) - self.assemblyai = assemblyai.AssemblyaiResourceWithStreamingResponse( - client.assemblyai - ) - self.azure = azure.AzureResourceWithStreamingResponse(client.azure) - self.langfuse = langfuse.LangfuseResourceWithStreamingResponse(client.langfuse) - self.config = config.ConfigResourceWithStreamingResponse(client.config) - self.test = test.TestResourceWithStreamingResponse(client.test) - self.health = health.HealthResourceWithStreamingResponse(client.health) - self.active = active.ActiveResourceWithStreamingResponse(client.active) - self.settings = settings.SettingsResourceWithStreamingResponse(client.settings) - self.key = key.KeyResourceWithStreamingResponse(client.key) - self.user = user.UserResourceWithStreamingResponse(client.user) - self.team = team.TeamResourceWithStreamingResponse(client.team) - self.organization = organization.OrganizationResourceWithStreamingResponse( - client.organization - ) - self.customer = customer.CustomerResourceWithStreamingResponse(client.customer) - self.spend = spend.SpendResourceWithStreamingResponse(client.spend) - self.global_ = global_.GlobalResourceWithStreamingResponse(client.global_) - self.provider = provider.ProviderResourceWithStreamingResponse(client.provider) - self.cache = cache.CacheResourceWithStreamingResponse(client.cache) - self.guardrails = guardrails.GuardrailsResourceWithStreamingResponse( - client.guardrails - ) - self.add = add.AddResourceWithStreamingResponse(client.add) - self.delete = delete.DeleteResourceWithStreamingResponse(client.delete) - self.files = files.FilesResourceWithStreamingResponse(client.files) - self.budget = budget.BudgetResourceWithStreamingResponse(client.budget) - self.db = db.DBResourceWithStreamingResponse(client.db) - self.kv = kv.KVResourceWithStreamingResponse(client.kv) - self.dns = dns.DNSResourceWithStreamingResponse(client.dns) - self.pages = pages.PagesResourceWithStreamingResponse(client.pages) - self.kms = kms.KMSResourceWithStreamingResponse(client.kms) - self.cart = cart.CartResourceWithStreamingResponse(client.cart) - self.edge = edge.EdgeResourceWithStreamingResponse(client.edge) - self.jobs = jobs.JobsResourceWithStreamingResponse(client.jobs) - self.pods = pods.PodsResourceWithStreamingResponse(client.pods) - self.audit = audit.AuditResourceWithStreamingResponse(client.audit) - self.build = build.BuildResourceWithStreamingResponse(client.build) - self.chain = chain.ChainResourceWithStreamingResponse(client.chain) - self.miner = miner.MinerResourceWithStreamingResponse(client.miner) - self.nodes = nodes.NodesResourceWithStreamingResponse(client.nodes) - self.tasks = tasks.TasksResourceWithStreamingResponse(client.tasks) - self.access = access.AccessResourceWithStreamingResponse(client.access) - self.agents = agents.AgentsResourceWithStreamingResponse(client.agents) - self.device = device.DeviceResourceWithStreamingResponse(client.device) - self.graphs = graphs.GraphsResourceWithStreamingResponse(client.graphs) - self.orders = orders.OrdersResourceWithStreamingResponse(client.orders) - self.policy = policy.PolicyResourceWithStreamingResponse(client.policy) - self.pubsub = pubsub.PubSubResourceWithStreamingResponse(client.pubsub) - self.queues = queues.QueuesResourceWithStreamingResponse(client.queues) - self.stores = stores.StoresResourceWithStreamingResponse(client.stores) - self.tokens = tokens.TokensResourceWithStreamingResponse(client.tokens) - self.tunnel = tunnel.TunnelResourceWithStreamingResponse(client.tunnel) - self.coupons = coupons.CouponsResourceWithStreamingResponse(client.coupons) - self.gateway = gateway.GatewayResourceWithStreamingResponse(client.gateway) - self.network = network.NetworkResourceWithStreamingResponse(client.network) - self.release = release.ReleaseResourceWithStreamingResponse(client.release) - self.secrets = secrets.SecretsResourceWithStreamingResponse(client.secrets) - self.storage = storage.StorageResourceWithStreamingResponse(client.storage) - self.vectors = vectors.VectorsResourceWithStreamingResponse(client.vectors) - self.wallets = wallets.WalletsResourceWithStreamingResponse(client.wallets) - self.checkout = checkout.CheckoutResourceWithStreamingResponse(client.checkout) - self.identity = identity.IdentityResourceWithStreamingResponse(client.identity) - self.machines = machines.MachinesResourceWithStreamingResponse(client.machines) - self.products = products.ProductsResourceWithStreamingResponse(client.products) - self.registry = registry.RegistryResourceWithStreamingResponse(client.registry) - self.campaigns = campaigns.CampaignsResourceWithStreamingResponse( - client.campaigns - ) - self.inference = inference.InferenceResourceWithStreamingResponse( - client.inference - ) - self.providers = providers.ProvidersResourceWithStreamingResponse( - client.providers - ) - self.referrals = referrals.ReferralsResourceWithStreamingResponse( - client.referrals - ) - self.workflows = workflows.WorkflowsResourceWithStreamingResponse( - client.workflows - ) - self.affiliates = affiliates.AffiliatesResourceWithStreamingResponse( - client.affiliates - ) - self.containers = containers.ContainersResourceWithStreamingResponse( - client.containers - ) - self.deployments = deployments.DeploymentsResourceWithStreamingResponse( - client.deployments - ) - self.mcp_servers = mcp_servers.MCPServersResourceWithStreamingResponse( - client.mcp_servers - ) - self.observability = observability.ObservabilityResourceWithStreamingResponse( - client.observability - ) - self.subscriptions = subscriptions.SubscriptionsResourceWithStreamingResponse( - client.subscriptions - ) - self.mpc = mpc.MPCResourceWithStreamingResponse(client.mpc) - self.paas = paas.PaaSResourceWithStreamingResponse(client.paas) - self.docdb = docdb.DocDBResourceWithStreamingResponse(client.docdb) - self.ingress = ingress.IngressResourceWithStreamingResponse(client.ingress) - self.datastore = datastore.DatastoreResourceWithStreamingResponse( - client.datastore - ) - self.billing = billing.BillingResourceWithStreamingResponse(client.billing) - self.iam = iam.IAMResourceWithStreamingResponse(client.iam) - self.commerce = commerce.CommerceResourceWithStreamingResponse(client.commerce) - self.team_workspace = team_workspace.TeamWorkspaceResourceWithStreamingResponse( - client.team_workspace - ) - - self.get_home = to_streamed_response_wrapper( - client.get_home, - ) - - -class AsyncHanzoWithStreamedResponse: - def __init__(self, client: AsyncHanzo) -> None: - self.models = models.AsyncModelsResourceWithStreamingResponse(client.models) - self.openai = openai.AsyncOpenAIResourceWithStreamingResponse(client.openai) - self.engines = engines.AsyncEnginesResourceWithStreamingResponse(client.engines) - self.chat = chat.AsyncChatResourceWithStreamingResponse(client.chat) - self.completions = completions.AsyncCompletionsResourceWithStreamingResponse( - client.completions - ) - self.embeddings = embeddings.AsyncEmbeddingsResourceWithStreamingResponse( - client.embeddings - ) - self.images = images.AsyncImagesResourceWithStreamingResponse(client.images) - self.audio = audio.AsyncAudioResourceWithStreamingResponse(client.audio) - self.assistants = assistants.AsyncAssistantsResourceWithStreamingResponse( - client.assistants - ) - self.threads = threads.AsyncThreadsResourceWithStreamingResponse(client.threads) - self.moderations = moderations.AsyncModerationsResourceWithStreamingResponse( - client.moderations - ) - self.utils = utils.AsyncUtilsResourceWithStreamingResponse(client.utils) - self.model = model.AsyncModelResourceWithStreamingResponse(client.model) - self.model_group = model_group.AsyncModelGroupResourceWithStreamingResponse( - client.model_group - ) - self.routes = routes.AsyncRoutesResourceWithStreamingResponse(client.routes) - self.responses = responses.AsyncResponsesResourceWithStreamingResponse( - client.responses - ) - self.batches = batches.AsyncBatchesResourceWithStreamingResponse(client.batches) - self.rerank = rerank.AsyncRerankResourceWithStreamingResponse(client.rerank) - self.fine_tuning = fine_tuning.AsyncFineTuningResourceWithStreamingResponse( - client.fine_tuning - ) - self.credentials = credentials.AsyncCredentialsResourceWithStreamingResponse( - client.credentials - ) - self.vertex_ai = vertex_ai.AsyncVertexAIResourceWithStreamingResponse( - client.vertex_ai - ) - self.gemini = gemini.AsyncGeminiResourceWithStreamingResponse(client.gemini) - self.cohere = cohere.AsyncCohereResourceWithStreamingResponse(client.cohere) - self.anthropic = anthropic.AsyncAnthropicResourceWithStreamingResponse( - client.anthropic - ) - self.bedrock = bedrock.AsyncBedrockResourceWithStreamingResponse(client.bedrock) - self.eu_assemblyai = ( - eu_assemblyai.AsyncEuAssemblyaiResourceWithStreamingResponse( - client.eu_assemblyai - ) - ) - self.assemblyai = assemblyai.AsyncAssemblyaiResourceWithStreamingResponse( - client.assemblyai - ) - self.azure = azure.AsyncAzureResourceWithStreamingResponse(client.azure) - self.langfuse = langfuse.AsyncLangfuseResourceWithStreamingResponse( - client.langfuse - ) - self.config = config.AsyncConfigResourceWithStreamingResponse(client.config) - self.test = test.AsyncTestResourceWithStreamingResponse(client.test) - self.health = health.AsyncHealthResourceWithStreamingResponse(client.health) - self.active = active.AsyncActiveResourceWithStreamingResponse(client.active) - self.settings = settings.AsyncSettingsResourceWithStreamingResponse( - client.settings - ) - self.key = key.AsyncKeyResourceWithStreamingResponse(client.key) - self.user = user.AsyncUserResourceWithStreamingResponse(client.user) - self.team = team.AsyncTeamResourceWithStreamingResponse(client.team) - self.organization = organization.AsyncOrganizationResourceWithStreamingResponse( - client.organization - ) - self.customer = customer.AsyncCustomerResourceWithStreamingResponse( - client.customer - ) - self.spend = spend.AsyncSpendResourceWithStreamingResponse(client.spend) - self.global_ = global_.AsyncGlobalResourceWithStreamingResponse(client.global_) - self.provider = provider.AsyncProviderResourceWithStreamingResponse( - client.provider - ) - self.cache = cache.AsyncCacheResourceWithStreamingResponse(client.cache) - self.guardrails = guardrails.AsyncGuardrailsResourceWithStreamingResponse( - client.guardrails - ) - self.add = add.AsyncAddResourceWithStreamingResponse(client.add) - self.delete = delete.AsyncDeleteResourceWithStreamingResponse(client.delete) - self.files = files.AsyncFilesResourceWithStreamingResponse(client.files) - self.budget = budget.AsyncBudgetResourceWithStreamingResponse(client.budget) - self.db = db.AsyncDBResourceWithStreamingResponse(client.db) - self.kv = kv.AsyncKVResourceWithStreamingResponse(client.kv) - self.dns = dns.AsyncDNSResourceWithStreamingResponse(client.dns) - self.pages = pages.AsyncPagesResourceWithStreamingResponse(client.pages) - self.kms = kms.AsyncKMSResourceWithStreamingResponse(client.kms) - self.cart = cart.AsyncCartResourceWithStreamingResponse(client.cart) - self.edge = edge.AsyncEdgeResourceWithStreamingResponse(client.edge) - self.jobs = jobs.AsyncJobsResourceWithStreamingResponse(client.jobs) - self.pods = pods.AsyncPodsResourceWithStreamingResponse(client.pods) - self.audit = audit.AsyncAuditResourceWithStreamingResponse(client.audit) - self.build = build.AsyncBuildResourceWithStreamingResponse(client.build) - self.chain = chain.AsyncChainResourceWithStreamingResponse(client.chain) - self.miner = miner.AsyncMinerResourceWithStreamingResponse(client.miner) - self.nodes = nodes.AsyncNodesResourceWithStreamingResponse(client.nodes) - self.tasks = tasks.AsyncTasksResourceWithStreamingResponse(client.tasks) - self.access = access.AsyncAccessResourceWithStreamingResponse(client.access) - self.agents = agents.AsyncAgentsResourceWithStreamingResponse(client.agents) - self.device = device.AsyncDeviceResourceWithStreamingResponse(client.device) - self.graphs = graphs.AsyncGraphsResourceWithStreamingResponse(client.graphs) - self.orders = orders.AsyncOrdersResourceWithStreamingResponse(client.orders) - self.policy = policy.AsyncPolicyResourceWithStreamingResponse(client.policy) - self.pubsub = pubsub.AsyncPubSubResourceWithStreamingResponse(client.pubsub) - self.queues = queues.AsyncQueuesResourceWithStreamingResponse(client.queues) - self.stores = stores.AsyncStoresResourceWithStreamingResponse(client.stores) - self.tokens = tokens.AsyncTokensResourceWithStreamingResponse(client.tokens) - self.tunnel = tunnel.AsyncTunnelResourceWithStreamingResponse(client.tunnel) - self.coupons = coupons.AsyncCouponsResourceWithStreamingResponse( - client.coupons - ) - self.gateway = gateway.AsyncGatewayResourceWithStreamingResponse( - client.gateway - ) - self.network = network.AsyncNetworkResourceWithStreamingResponse( - client.network - ) - self.release = release.AsyncReleaseResourceWithStreamingResponse( - client.release - ) - self.secrets = secrets.AsyncSecretsResourceWithStreamingResponse( - client.secrets - ) - self.storage = storage.AsyncStorageResourceWithStreamingResponse( - client.storage - ) - self.vectors = vectors.AsyncVectorsResourceWithStreamingResponse( - client.vectors - ) - self.wallets = wallets.AsyncWalletsResourceWithStreamingResponse( - client.wallets - ) - self.checkout = checkout.AsyncCheckoutResourceWithStreamingResponse( - client.checkout - ) - self.identity = identity.AsyncIdentityResourceWithStreamingResponse( - client.identity - ) - self.machines = machines.AsyncMachinesResourceWithStreamingResponse( - client.machines - ) - self.products = products.AsyncProductsResourceWithStreamingResponse( - client.products - ) - self.registry = registry.AsyncRegistryResourceWithStreamingResponse( - client.registry - ) - self.campaigns = campaigns.AsyncCampaignsResourceWithStreamingResponse( - client.campaigns - ) - self.inference = inference.AsyncInferenceResourceWithStreamingResponse( - client.inference - ) - self.providers = providers.AsyncProvidersResourceWithStreamingResponse( - client.providers - ) - self.referrals = referrals.AsyncReferralsResourceWithStreamingResponse( - client.referrals - ) - self.workflows = workflows.AsyncWorkflowsResourceWithStreamingResponse( - client.workflows - ) - self.affiliates = affiliates.AsyncAffiliatesResourceWithStreamingResponse( - client.affiliates - ) - self.containers = containers.AsyncContainersResourceWithStreamingResponse( - client.containers - ) - self.deployments = deployments.AsyncDeploymentsResourceWithStreamingResponse( - client.deployments - ) - self.mcp_servers = mcp_servers.AsyncMCPServersResourceWithStreamingResponse( - client.mcp_servers - ) - self.observability = ( - observability.AsyncObservabilityResourceWithStreamingResponse( - client.observability - ) - ) - self.subscriptions = ( - subscriptions.AsyncSubscriptionsResourceWithStreamingResponse( - client.subscriptions - ) - ) - self.mpc = mpc.AsyncMPCResourceWithStreamingResponse(client.mpc) - self.paas = paas.AsyncPaaSResourceWithStreamingResponse(client.paas) - self.docdb = docdb.AsyncDocDBResourceWithStreamingResponse(client.docdb) - self.ingress = ingress.AsyncIngressResourceWithStreamingResponse( - client.ingress - ) - self.datastore = datastore.AsyncDatastoreResourceWithStreamingResponse( - client.datastore - ) - self.billing = billing.AsyncBillingResourceWithStreamingResponse(client.billing) - self.iam = iam.AsyncIAMResourceWithStreamingResponse(client.iam) - self.commerce = commerce.AsyncCommerceResourceWithStreamingResponse( - client.commerce - ) - self.team_workspace = ( - team_workspace.AsyncTeamWorkspaceResourceWithStreamingResponse( - client.team_workspace - ) - ) - - self.get_home = async_to_streamed_response_wrapper( - client.get_home, - ) - - -Client = Hanzo - -AsyncClient = AsyncHanzo diff --git a/pkg/hanzoai/_compat.py b/pkg/hanzoai/_compat.py deleted file mode 100644 index b0655cc55..000000000 --- a/pkg/hanzoai/_compat.py +++ /dev/null @@ -1,232 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload -from datetime import date, datetime -from typing_extensions import Self, Literal - -import pydantic -from pydantic.fields import FieldInfo - -from ._types import IncEx, StrBytesIntFloat - -_T = TypeVar("_T") -_ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) - -# --------------- Pydantic v2 compatibility --------------- - -# Pyright incorrectly reports some of our functions as overriding a method when they don't -# pyright: reportIncompatibleMethodOverride=false - -PYDANTIC_V2 = pydantic.VERSION.startswith("2.") - -# v1 re-exports -if TYPE_CHECKING: - - def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001 - ... - - def parse_datetime( - value: Union[datetime, StrBytesIntFloat], - ) -> datetime: # noqa: ARG001 - ... - - def get_args(t: type[Any]) -> tuple[Any, ...]: # noqa: ARG001 - ... - - def is_union(tp: type[Any] | None) -> bool: # noqa: ARG001 - ... - - def get_origin(t: type[Any]) -> type[Any] | None: # noqa: ARG001 - ... - - def is_literal_type(type_: type[Any]) -> bool: # noqa: ARG001 - ... - - def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 - ... - -else: - if PYDANTIC_V2: - from pydantic.v1.typing import ( - get_args as get_args, - is_union as is_union, - get_origin as get_origin, - is_typeddict as is_typeddict, - is_literal_type as is_literal_type, - ) - from pydantic.v1.datetime_parse import ( - parse_date as parse_date, - parse_datetime as parse_datetime, - ) - else: - from pydantic.typing import ( - get_args as get_args, - is_union as is_union, - get_origin as get_origin, - is_typeddict as is_typeddict, - is_literal_type as is_literal_type, - ) - from pydantic.datetime_parse import ( - parse_date as parse_date, - parse_datetime as parse_datetime, - ) - - -# refactored config -if TYPE_CHECKING: - from pydantic import ConfigDict as ConfigDict -else: - if PYDANTIC_V2: - from pydantic import ConfigDict - else: - # TODO: provide an error message here? - ConfigDict = None - - -# renamed methods / properties -def parse_obj(model: type[_ModelT], value: object) -> _ModelT: - if PYDANTIC_V2: - return model.model_validate(value) - else: - return cast( - _ModelT, model.parse_obj(value) - ) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] - - -def field_is_required(field: FieldInfo) -> bool: - if PYDANTIC_V2: - return field.is_required() - return field.required # type: ignore - - -def field_get_default(field: FieldInfo) -> Any: - value = field.get_default() - if PYDANTIC_V2: - from pydantic_core import PydanticUndefined - - if value == PydanticUndefined: - return None - return value - return value - - -def field_outer_type(field: FieldInfo) -> Any: - if PYDANTIC_V2: - return field.annotation - return field.outer_type_ # type: ignore - - -def get_model_config(model: type[pydantic.BaseModel]) -> Any: - if PYDANTIC_V2: - return model.model_config - return model.__config__ # type: ignore - - -def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: - if PYDANTIC_V2: - return model.model_fields - return model.__fields__ # type: ignore - - -def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT: - if PYDANTIC_V2: - return model.model_copy(deep=deep) - return model.copy(deep=deep) # type: ignore - - -def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: - if PYDANTIC_V2: - return model.model_dump_json(indent=indent) - return model.json(indent=indent) # type: ignore - - -def model_dump( - model: pydantic.BaseModel, - *, - exclude: IncEx | None = None, - exclude_unset: bool = False, - exclude_defaults: bool = False, - warnings: bool = True, - mode: Literal["json", "python"] = "python", -) -> dict[str, Any]: - if PYDANTIC_V2 or hasattr(model, "model_dump"): - return model.model_dump( - mode=mode, - exclude=exclude, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - # warnings are not supported in Pydantic v1 - warnings=warnings if PYDANTIC_V2 else True, - ) - return cast( - "dict[str, Any]", - model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] - exclude=exclude, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - ), - ) - - -def model_parse(model: type[_ModelT], data: Any) -> _ModelT: - if PYDANTIC_V2: - return model.model_validate(data) - return model.parse_obj(data) # pyright: ignore[reportDeprecated] - - -# generic models -if TYPE_CHECKING: - - class GenericModel(pydantic.BaseModel): ... - -else: - if PYDANTIC_V2: - # there no longer needs to be a distinction in v2 but - # we still have to create our own subclass to avoid - # inconsistent MRO ordering errors - class GenericModel(pydantic.BaseModel): ... - - else: - import pydantic.generics - - class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... - - -# cached properties -if TYPE_CHECKING: - cached_property = property - - # we define a separate type (copied from typeshed) - # that represents that `cached_property` is `set`able - # at runtime, which differs from `@property`. - # - # this is a separate type as editors likely special case - # `@property` and we don't want to cause issues just to have - # more helpful internal types. - - class typed_cached_property(Generic[_T]): - func: Callable[[Any], _T] - attrname: str | None - - def __init__(self, func: Callable[[Any], _T]) -> None: ... - - @overload - def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: ... - - @overload - def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... - - def __get__( - self, instance: object, owner: type[Any] | None = None - ) -> _T | Self: - raise NotImplementedError() - - def __set_name__(self, owner: type[Any], name: str) -> None: ... - - # __set__ is not defined at runtime, but @cached_property is designed to be settable - def __set__(self, instance: object, value: _T) -> None: ... - -else: - from functools import cached_property as cached_property - - typed_cached_property = cached_property diff --git a/pkg/hanzoai/_constants.py b/pkg/hanzoai/_constants.py deleted file mode 100644 index 68799b6c2..000000000 --- a/pkg/hanzoai/_constants.py +++ /dev/null @@ -1,16 +0,0 @@ -# Hanzo AI SDK - -import httpx - -RAW_RESPONSE_HEADER = "X-SDK-Raw-Response" -OVERRIDE_CAST_TO_HEADER = "____hanzo_override_cast_to" - -# default timeout is 1 minute -DEFAULT_TIMEOUT = httpx.Timeout(timeout=60, connect=5.0) -DEFAULT_MAX_RETRIES = 2 -DEFAULT_CONNECTION_LIMITS = httpx.Limits( - max_connections=100, max_keepalive_connections=20 -) - -INITIAL_RETRY_DELAY = 0.5 -MAX_RETRY_DELAY = 8.0 diff --git a/pkg/hanzoai/_exceptions.py b/pkg/hanzoai/_exceptions.py deleted file mode 100644 index cb7e509c1..000000000 --- a/pkg/hanzoai/_exceptions.py +++ /dev/null @@ -1,138 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Literal - -import httpx - -__all__ = [ - "BadRequestError", - "AuthenticationError", - "PermissionDeniedError", - "NotFoundError", - "ConflictError", - "UnprocessableEntityError", - "RateLimitError", - "InternalServerError", -] - - -class HanzoError(Exception): - pass - - -class APIError(HanzoError): - message: str - request: httpx.Request - - body: object | None - """The API response body. - - If the API responded with a valid JSON structure then this property will be the - decoded result. - - If it isn't a valid JSON structure then this will be the raw response. - - If there was no response associated with this error then it will be `None`. - """ - - def __init__( - self, message: str, request: httpx.Request, *, body: object | None - ) -> None: # noqa: ARG002 - super().__init__(message) - self.request = request - self.message = message - self.body = body - - -class APIResponseValidationError(APIError): - response: httpx.Response - status_code: int - - def __init__( - self, - response: httpx.Response, - body: object | None, - *, - message: str | None = None, - ) -> None: - super().__init__( - message or "Data returned by API invalid for expected schema.", - response.request, - body=body, - ) - self.response = response - self.status_code = response.status_code - - -class APIStatusError(APIError): - """Raised when an API response has a status code of 4xx or 5xx.""" - - response: httpx.Response - status_code: int - - def __init__( - self, message: str, *, response: httpx.Response, body: object | None - ) -> None: - super().__init__(message, response.request, body=body) - self.response = response - self.status_code = response.status_code - - -class APIConnectionError(APIError): - def __init__( - self, *, message: str = "Connection error.", request: httpx.Request - ) -> None: - super().__init__(message, request, body=None) - - -class APITimeoutError(APIConnectionError): - def __init__(self, request: httpx.Request) -> None: - super().__init__(message="Request timed out.", request=request) - - -class BadRequestError(APIStatusError): - status_code: Literal[400] = ( - 400 # pyright: ignore[reportIncompatibleVariableOverride] - ) - - -class AuthenticationError(APIStatusError): - status_code: Literal[401] = ( - 401 # pyright: ignore[reportIncompatibleVariableOverride] - ) - - -class PermissionDeniedError(APIStatusError): - status_code: Literal[403] = ( - 403 # pyright: ignore[reportIncompatibleVariableOverride] - ) - - -class NotFoundError(APIStatusError): - status_code: Literal[404] = ( - 404 # pyright: ignore[reportIncompatibleVariableOverride] - ) - - -class ConflictError(APIStatusError): - status_code: Literal[409] = ( - 409 # pyright: ignore[reportIncompatibleVariableOverride] - ) - - -class UnprocessableEntityError(APIStatusError): - status_code: Literal[422] = ( - 422 # pyright: ignore[reportIncompatibleVariableOverride] - ) - - -class RateLimitError(APIStatusError): - status_code: Literal[429] = ( - 429 # pyright: ignore[reportIncompatibleVariableOverride] - ) - - -class InternalServerError(APIStatusError): - pass diff --git a/pkg/hanzoai/_files.py b/pkg/hanzoai/_files.py deleted file mode 100644 index 62e02d405..000000000 --- a/pkg/hanzoai/_files.py +++ /dev/null @@ -1,138 +0,0 @@ -from __future__ import annotations - -import io -import os -import pathlib -from typing import overload -from typing_extensions import TypeGuard - -import anyio - -from ._types import ( - FileTypes, - FileContent, - RequestFiles, - HttpxFileTypes, - Base64FileInput, - HttpxFileContent, - HttpxRequestFiles, -) -from ._utils import is_tuple_t, is_mapping_t, is_sequence_t - - -def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]: - return isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) - - -def is_file_content(obj: object) -> TypeGuard[FileContent]: - return ( - isinstance(obj, bytes) - or isinstance(obj, tuple) - or isinstance(obj, io.IOBase) - or isinstance(obj, os.PathLike) - ) - - -def assert_is_file_content(obj: object, *, key: str | None = None) -> None: - if not is_file_content(obj): - prefix = ( - f"Expected entry at `{key}`" - if key is not None - else f"Expected file input `{obj!r}`" - ) - raise RuntimeError( - f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/hanzoai/python-sdk/tree/main#file-uploads" - ) from None - - -@overload -def to_httpx_files(files: None) -> None: ... - - -@overload -def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... - - -def to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: - if files is None: - return None - - if is_mapping_t(files): - files = {key: _transform_file(file) for key, file in files.items()} - elif is_sequence_t(files): - files = [(key, _transform_file(file)) for key, file in files] - else: - raise TypeError( - f"Unexpected file type input {type(files)}, expected mapping or sequence" - ) - - return files - - -def _transform_file(file: FileTypes) -> HttpxFileTypes: - if is_file_content(file): - if isinstance(file, os.PathLike): - path = pathlib.Path(file) - return (path.name, path.read_bytes()) - - return file - - if is_tuple_t(file): - return (file[0], _read_file_content(file[1]), *file[2:]) - - raise TypeError( - "Expected file types input to be a FileContent type or to be a tuple" - ) - - -def _read_file_content(file: FileContent) -> HttpxFileContent: - if isinstance(file, os.PathLike): - return pathlib.Path(file).read_bytes() - return file - - -@overload -async def async_to_httpx_files(files: None) -> None: ... - - -@overload -async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... - - -async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: - if files is None: - return None - - if is_mapping_t(files): - files = {key: await _async_transform_file(file) for key, file in files.items()} - elif is_sequence_t(files): - files = [(key, await _async_transform_file(file)) for key, file in files] - else: - raise TypeError( - "Unexpected file type input {type(files)}, expected mapping or sequence" - ) - - return files - - -async def _async_transform_file(file: FileTypes) -> HttpxFileTypes: - if is_file_content(file): - if isinstance(file, os.PathLike): - path = anyio.Path(file) - return (path.name, await path.read_bytes()) - - return file - - if is_tuple_t(file): - return (file[0], await _async_read_file_content(file[1]), *file[2:]) - - raise TypeError( - "Expected file types input to be a FileContent type or to be a tuple" - ) - - -async def _async_read_file_content(file: FileContent) -> HttpxFileContent: - if isinstance(file, os.PathLike): - return await anyio.Path(file).read_bytes() - - return file diff --git a/pkg/hanzoai/_qs.py b/pkg/hanzoai/_qs.py deleted file mode 100644 index ced07c7a2..000000000 --- a/pkg/hanzoai/_qs.py +++ /dev/null @@ -1,166 +0,0 @@ -from __future__ import annotations - -from typing import Any, List, Tuple, Union, Mapping, TypeVar -from urllib.parse import parse_qs, urlencode -from typing_extensions import Literal, get_args - -from ._types import NOT_GIVEN, NotGiven, NotGivenOr -from ._utils import flatten - -_T = TypeVar("_T") - - -ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] -NestedFormat = Literal["dots", "brackets"] - -PrimitiveData = Union[str, int, float, bool, None] -# this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"] -# https://github.com/microsoft/pyright/issues/3555 -Data = Union[PrimitiveData, List[Any], Tuple[Any], "Mapping[str, Any]"] -Params = Mapping[str, Data] - - -class Querystring: - array_format: ArrayFormat - nested_format: NestedFormat - - def __init__( - self, - *, - array_format: ArrayFormat = "repeat", - nested_format: NestedFormat = "brackets", - ) -> None: - self.array_format = array_format - self.nested_format = nested_format - - def parse(self, query: str) -> Mapping[str, object]: - # Note: custom format syntax is not supported yet - return parse_qs(query) - - def stringify( - self, - params: Params, - *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, - ) -> str: - return urlencode( - self.stringify_items( - params, - array_format=array_format, - nested_format=nested_format, - ) - ) - - def stringify_items( - self, - params: Params, - *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, - ) -> list[tuple[str, str]]: - opts = Options( - qs=self, - array_format=array_format, - nested_format=nested_format, - ) - return flatten( - [self._stringify_item(key, value, opts) for key, value in params.items()] - ) - - def _stringify_item( - self, - key: str, - value: Data, - opts: Options, - ) -> list[tuple[str, str]]: - if isinstance(value, Mapping): - items: list[tuple[str, str]] = [] - nested_format = opts.nested_format - for subkey, subvalue in value.items(): - items.extend( - self._stringify_item( - # TODO: error if unknown format - ( - f"{key}.{subkey}" - if nested_format == "dots" - else f"{key}[{subkey}]" - ), - subvalue, - opts, - ) - ) - return items - - if isinstance(value, (list, tuple)): - array_format = opts.array_format - if array_format == "comma": - return [ - ( - key, - ",".join( - self._primitive_value_to_str(item) - for item in value - if item is not None - ), - ), - ] - elif array_format == "repeat": - items = [] - for item in value: - items.extend(self._stringify_item(key, item, opts)) - return items - elif array_format == "indices": - raise NotImplementedError( - "The array indices format is not supported yet" - ) - elif array_format == "brackets": - items = [] - key = key + "[]" - for item in value: - items.extend(self._stringify_item(key, item, opts)) - return items - else: - raise NotImplementedError( - f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" - ) - - serialised = self._primitive_value_to_str(value) - if not serialised: - return [] - return [(key, serialised)] - - def _primitive_value_to_str(self, value: PrimitiveData) -> str: - # copied from httpx - if value is True: - return "true" - elif value is False: - return "false" - elif value is None: - return "" - return str(value) - - -_qs = Querystring() -parse = _qs.parse -stringify = _qs.stringify -stringify_items = _qs.stringify_items - - -class Options: - array_format: ArrayFormat - nested_format: NestedFormat - - def __init__( - self, - qs: Querystring = _qs, - *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, - ) -> None: - self.array_format = ( - qs.array_format if isinstance(array_format, NotGiven) else array_format - ) - self.nested_format = ( - qs.nested_format if isinstance(nested_format, NotGiven) else nested_format - ) diff --git a/pkg/hanzoai/_streaming.py b/pkg/hanzoai/_streaming.py deleted file mode 100644 index bacb5fc04..000000000 --- a/pkg/hanzoai/_streaming.py +++ /dev/null @@ -1,410 +0,0 @@ -# Note: initially copied from https://github.com/florimondmanca/httpx-sse/blob/master/src/httpx_sse/_decoders.py -from __future__ import annotations - -import json -import inspect -from types import TracebackType -from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast -from typing_extensions import ( - Self, - Protocol, - TypeGuard, - override, - get_origin, - runtime_checkable, -) - -import httpx - -from ._utils import extract_type_var_from_base - -if TYPE_CHECKING: - from ._client import Hanzo, AsyncHanzo - - -_T = TypeVar("_T") - - -class Stream(Generic[_T]): - """Provides the core interface to iterate over a synchronous stream response.""" - - response: httpx.Response - - _decoder: SSEBytesDecoder - - def __init__( - self, - *, - cast_to: type[_T], - response: httpx.Response, - client: Hanzo, - ) -> None: - self.response = response - self._cast_to = cast_to - self._client = client - self._decoder = client._make_sse_decoder() - self._iterator = self.__stream__() - - def __next__(self) -> _T: - return self._iterator.__next__() - - def __iter__(self) -> Iterator[_T]: - for item in self._iterator: - yield item - - def _iter_events(self) -> Iterator[ServerSentEvent]: - yield from self._decoder.iter_bytes(self.response.iter_bytes()) - - def __stream__(self) -> Iterator[_T]: - cast_to = cast(Any, self._cast_to) - response = self.response - process_data = self._client._process_response_data - iterator = self._iter_events() - - for sse in iterator: - yield process_data(data=sse.json(), cast_to=cast_to, response=response) - - # Ensure the entire stream is consumed - for _sse in iterator: - ... - - def __enter__(self) -> Self: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.close() - - def close(self) -> None: - """ - Close the response and release the connection. - - Automatically called if the response body is read to completion. - """ - self.response.close() - - -class AsyncStream(Generic[_T]): - """Provides the core interface to iterate over an asynchronous stream response.""" - - response: httpx.Response - - _decoder: SSEDecoder | SSEBytesDecoder - - def __init__( - self, - *, - cast_to: type[_T], - response: httpx.Response, - client: AsyncHanzo, - ) -> None: - self.response = response - self._cast_to = cast_to - self._client = client - self._decoder = client._make_sse_decoder() - self._iterator = self.__stream__() - - async def __anext__(self) -> _T: - return await self._iterator.__anext__() - - async def __aiter__(self) -> AsyncIterator[_T]: - async for item in self._iterator: - yield item - - async def _iter_events(self) -> AsyncIterator[ServerSentEvent]: - async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()): - yield sse - - async def __stream__(self) -> AsyncIterator[_T]: - cast_to = cast(Any, self._cast_to) - response = self.response - process_data = self._client._process_response_data - iterator = self._iter_events() - - async for sse in iterator: - yield process_data(data=sse.json(), cast_to=cast_to, response=response) - - # Ensure the entire stream is consumed - async for _sse in iterator: - ... - - async def __aenter__(self) -> Self: - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - await self.close() - - async def close(self) -> None: - """ - Close the response and release the connection. - - Automatically called if the response body is read to completion. - """ - await self.response.aclose() - - -class ServerSentEvent: - def __init__( - self, - *, - event: str | None = None, - data: str | None = None, - id: str | None = None, - retry: int | None = None, - ) -> None: - if data is None: - data = "" - - self._id = id - self._data = data - self._event = event or None - self._retry = retry - - @property - def event(self) -> str | None: - return self._event - - @property - def id(self) -> str | None: - return self._id - - @property - def retry(self) -> int | None: - return self._retry - - @property - def data(self) -> str: - return self._data - - def json(self) -> Any: - return json.loads(self.data) - - @override - def __repr__(self) -> str: - return f"ServerSentEvent(event={self.event}, data={self.data}, id={self.id}, retry={self.retry})" - - -class SSEDecoder: - _data: list[str] - _event: str | None - _retry: int | None - _last_event_id: str | None - - def __init__(self) -> None: - self._event = None - self._data = [] - self._last_event_id = None - self._retry = None - - def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: - """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" - for chunk in self._iter_chunks(iterator): - # Split before decoding so splitlines() only uses \r and \n - for raw_line in chunk.splitlines(): - line = raw_line.decode("utf-8") - sse = self.decode(line) - if sse: - yield sse - - @staticmethod - def _find_separator(buf: bytes, start: int = 0) -> tuple[int, int] | None: - """Scan buf for an SSE frame separator starting at `start`. - - Returns (end_of_frame, resume_pos) where end_of_frame is the index - just past the separator and resume_pos is where the next frame begins - (same value โ€” the separator is included in the yielded frame). - - Recognized separators: \\n\\n, \\r\\r, \\r\\n\\r\\n. - """ - i = start - length = len(buf) - while i < length: - c = buf[i] - if c == ord(b"\n"): - # Check for \n\n - if i + 1 < length and buf[i + 1] == ord(b"\n"): - pos = i + 2 - return (pos, pos) - elif c == ord(b"\r"): - # Check for \r\r - if i + 1 < length and buf[i + 1] == ord(b"\r"): - pos = i + 2 - return (pos, pos) - # Check for \r\n\r\n - if ( - i + 3 < length - and buf[i + 1] == ord(b"\n") - and buf[i + 2] == ord(b"\r") - and buf[i + 3] == ord(b"\n") - ): - pos = i + 4 - return (pos, pos) - i += 1 - return None - - def _iter_chunks(self, iterator: Iterator[bytes]) -> Iterator[bytes]: - """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks. - - Uses a stateful buffer with separator scanning instead of splitlines() - to avoid issues with cross-chunk \\r\\n splitting. - """ - buf = b"" - for chunk in iterator: - buf += chunk - # Drain all complete frames from the buffer - while True: - sep = self._find_separator(buf) - if sep is None: - break - end, resume = sep - yield buf[:end] - buf = buf[resume:] - if buf: - yield buf - - async def aiter_bytes( - self, iterator: AsyncIterator[bytes] - ) -> AsyncIterator[ServerSentEvent]: - """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" - async for chunk in self._aiter_chunks(iterator): - # Split before decoding so splitlines() only uses \r and \n - for raw_line in chunk.splitlines(): - line = raw_line.decode("utf-8") - sse = self.decode(line) - if sse: - yield sse - - async def _aiter_chunks( - self, iterator: AsyncIterator[bytes] - ) -> AsyncIterator[bytes]: - """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks. - - Uses a stateful buffer with separator scanning instead of splitlines() - to avoid issues with cross-chunk \\r\\n splitting. - """ - buf = b"" - async for chunk in iterator: - buf += chunk - # Drain all complete frames from the buffer - while True: - sep = self._find_separator(buf) - if sep is None: - break - end, resume = sep - yield buf[:end] - buf = buf[resume:] - if buf: - yield buf - - def decode(self, line: str) -> ServerSentEvent | None: - # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 - - if not line: - if ( - not self._event - and not self._data - and not self._last_event_id - and self._retry is None - ): - return None - - sse = ServerSentEvent( - event=self._event, - data="\n".join(self._data), - id=self._last_event_id, - retry=self._retry, - ) - - # NOTE: as per the SSE spec, do not reset last_event_id. - self._event = None - self._data = [] - self._retry = None - - # Skip [DONE] sentinel and ping events โ€” don't surface them - # as they are control signals, not parseable data. - if sse.data == "[DONE]" or sse.event == "ping": - return None - - return sse - - if line.startswith(":"): - return None - - fieldname, _, value = line.partition(":") - - if value.startswith(" "): - value = value[1:] - - if fieldname == "event": - self._event = value - elif fieldname == "data": - self._data.append(value) - elif fieldname == "id": - if "\0" in value: - pass - else: - self._last_event_id = value - elif fieldname == "retry": - try: - self._retry = int(value) - except (TypeError, ValueError): - pass - else: - pass # Field is ignored. - - return None - - -@runtime_checkable -class SSEBytesDecoder(Protocol): - def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: - """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" - ... - - def aiter_bytes( - self, iterator: AsyncIterator[bytes] - ) -> AsyncIterator[ServerSentEvent]: - """Given an async iterator that yields raw binary data, iterate over it & yield every event encountered""" - ... - - -def is_stream_class_type( - typ: type, -) -> TypeGuard[type[Stream[object]] | type[AsyncStream[object]]]: - """TypeGuard for determining whether or not the given type is a subclass of `Stream` / `AsyncStream`""" - origin = get_origin(typ) or typ - return inspect.isclass(origin) and issubclass(origin, (Stream, AsyncStream)) - - -def extract_stream_chunk_type( - stream_cls: type, - *, - failure_message: str | None = None, -) -> type: - """Given a type like `Stream[T]`, returns the generic type variable `T`. - - This also handles the case where a concrete subclass is given, e.g. - ```py - class MyStream(Stream[bytes]): - ... - - extract_stream_chunk_type(MyStream) -> bytes - ``` - """ - from ._base_client import Stream, AsyncStream - - return extract_type_var_from_base( - stream_cls, - index=0, - generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)), - failure_message=failure_message, - ) diff --git a/pkg/hanzoai/_types.py b/pkg/hanzoai/_types.py deleted file mode 100644 index d419ec8ef..000000000 --- a/pkg/hanzoai/_types.py +++ /dev/null @@ -1,234 +0,0 @@ -from __future__ import annotations - -from os import PathLike -from typing import ( - IO, - TYPE_CHECKING, - Any, - Dict, - List, - Type, - Tuple, - Union, - Mapping, - TypeVar, - Callable, - Optional, - Sequence, -) -from typing_extensions import ( - Set, - Literal, - Protocol, - TypeAlias, - TypedDict, - override, - runtime_checkable, -) - -import httpx -import pydantic -from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport - -if TYPE_CHECKING: - from ._models import BaseModel - from ._response import APIResponse, AsyncAPIResponse - -Transport = BaseTransport -AsyncTransport = AsyncBaseTransport -Query = Mapping[str, object] -Body = object -AnyMapping = Mapping[str, object] -ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) -_T = TypeVar("_T") - - -# Approximates httpx internal ProxiesTypes and RequestFiles types -# while adding support for `PathLike` instances -ProxiesDict = Dict["str | URL", Union[None, str, URL, Proxy]] -ProxiesTypes = Union[str, Proxy, ProxiesDict] -if TYPE_CHECKING: - Base64FileInput = Union[IO[bytes], PathLike[str]] - FileContent = Union[IO[bytes], bytes, PathLike[str]] -else: - Base64FileInput = Union[IO[bytes], PathLike] - FileContent = Union[ - IO[bytes], bytes, PathLike - ] # PathLike is not subscriptable in Python 3.8. -FileTypes = Union[ - # file (or bytes) - FileContent, - # (filename, file (or bytes)) - Tuple[Optional[str], FileContent], - # (filename, file (or bytes), content_type) - Tuple[Optional[str], FileContent, Optional[str]], - # (filename, file (or bytes), content_type, headers) - Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], -] -RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]] - -# duplicate of the above but without our custom file support -HttpxFileContent = Union[IO[bytes], bytes] -HttpxFileTypes = Union[ - # file (or bytes) - HttpxFileContent, - # (filename, file (or bytes)) - Tuple[Optional[str], HttpxFileContent], - # (filename, file (or bytes), content_type) - Tuple[Optional[str], HttpxFileContent, Optional[str]], - # (filename, file (or bytes), content_type, headers) - Tuple[Optional[str], HttpxFileContent, Optional[str], Mapping[str, str]], -] -HttpxRequestFiles = Union[ - Mapping[str, HttpxFileTypes], Sequence[Tuple[str, HttpxFileTypes]] -] - -# Workaround to support (cast_to: Type[ResponseT]) -> ResponseT -# where ResponseT includes `None`. In order to support directly -# passing `None`, overloads would have to be defined for every -# method that uses `ResponseT` which would lead to an unacceptable -# amount of code duplication and make it unreadable. See _base_client.py -# for example usage. -# -# This unfortunately means that you will either have -# to import this type and pass it explicitly: -# -# from hanzoai import NoneType -# client.get('/foo', cast_to=NoneType) -# -# or build it yourself: -# -# client.get('/foo', cast_to=type(None)) -if TYPE_CHECKING: - NoneType: Type[None] -else: - NoneType = type(None) - - -class RequestOptions(TypedDict, total=False): - headers: Headers - max_retries: int - timeout: float | Timeout | None - params: Query - extra_json: AnyMapping - idempotency_key: str - - -# Sentinel class used until PEP 0661 is accepted -class NotGiven: - """ - A sentinel singleton class used to distinguish omitted keyword arguments - from those passed in with the value None (which may have different behavior). - - For example: - - ```py - def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ... - - - get(timeout=1) # 1s timeout - get(timeout=None) # No timeout - get() # Default timeout behavior, which may not be statically known at the method definition. - ``` - """ - - def __bool__(self) -> Literal[False]: - return False - - @override - def __repr__(self) -> str: - return "NOT_GIVEN" - - -NotGivenOr = Union[_T, NotGiven] -NOT_GIVEN = NotGiven() - - -class Omit: - """In certain situations you need to be able to represent a case where a default value has - to be explicitly removed and `None` is not an appropriate substitute, for example: - - ```py - # as the default `Content-Type` header is `application/json` that will be sent - client.post("/upload/files", files={"file": b"my raw file content"}) - - # you can't explicitly override the header as it has to be dynamically generated - # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' - client.post(..., headers={"Content-Type": "multipart/form-data"}) - - # instead you can remove the default `application/json` header by passing Omit - client.post(..., headers={"Content-Type": Omit()}) - ``` - """ - - def __bool__(self) -> Literal[False]: - return False - - -@runtime_checkable -class ModelBuilderProtocol(Protocol): - @classmethod - def build( - cls: type[_T], - *, - response: Response, - data: object, - ) -> _T: ... - - -Headers = Mapping[str, Union[str, Omit]] - - -class HeadersLikeProtocol(Protocol): - def get(self, __key: str) -> str | None: ... - - -HeadersLike = Union[Headers, HeadersLikeProtocol] - -ResponseT = TypeVar( - "ResponseT", - bound=Union[ - object, - str, - None, - "BaseModel", - List[Any], - Dict[str, Any], - Response, - ModelBuilderProtocol, - "APIResponse[Any]", - "AsyncAPIResponse[Any]", - ], -) - -StrBytesIntFloat = Union[str, bytes, int, float] - -# Note: copied from Pydantic -# https://github.com/pydantic/pydantic/blob/6f31f8f68ef011f84357330186f603ff295312fd/pydantic/main.py#L79 -IncEx: TypeAlias = Union[ - Set[int], - Set[str], - Mapping[int, Union["IncEx", bool]], - Mapping[str, Union["IncEx", bool]], -] - -PostParser = Callable[[Any], Any] - - -@runtime_checkable -class InheritsGeneric(Protocol): - """Represents a type that has inherited from `Generic` - - The `__orig_bases__` property can be used to determine the resolved - type variable for a given base class. - """ - - __orig_bases__: tuple[_GenericAlias] - - -class _GenericAlias(Protocol): - __origin__: type[object] - - -class HttpxSendArgs(TypedDict, total=False): - auth: httpx.Auth diff --git a/pkg/hanzoai/_utils/__init__.py b/pkg/hanzoai/_utils/__init__.py deleted file mode 100644 index 9fc66baeb..000000000 --- a/pkg/hanzoai/_utils/__init__.py +++ /dev/null @@ -1,60 +0,0 @@ -from ._sync import asyncify as asyncify -from ._proxy import LazyProxy as LazyProxy -from ._utils import ( - flatten as flatten, - is_dict as is_dict, - is_list as is_list, - is_given as is_given, - is_tuple as is_tuple, - json_safe as json_safe, - lru_cache as lru_cache, - is_mapping as is_mapping, - is_tuple_t as is_tuple_t, - parse_date as parse_date, - is_iterable as is_iterable, - is_sequence as is_sequence, - coerce_float as coerce_float, - is_mapping_t as is_mapping_t, - removeprefix as removeprefix, - removesuffix as removesuffix, - extract_files as extract_files, - is_sequence_t as is_sequence_t, - required_args as required_args, - coerce_boolean as coerce_boolean, - coerce_integer as coerce_integer, - file_from_path as file_from_path, - parse_datetime as parse_datetime, - strip_not_given as strip_not_given, - deepcopy_minimal as deepcopy_minimal, - get_async_library as get_async_library, - maybe_coerce_float as maybe_coerce_float, - get_required_header as get_required_header, - maybe_coerce_boolean as maybe_coerce_boolean, - maybe_coerce_integer as maybe_coerce_integer, -) -from ._typing import ( - is_list_type as is_list_type, - is_union_type as is_union_type, - extract_type_arg as extract_type_arg, - is_iterable_type as is_iterable_type, - is_required_type as is_required_type, - is_annotated_type as is_annotated_type, - is_type_alias_type as is_type_alias_type, - strip_annotated_type as strip_annotated_type, - extract_type_var_from_base as extract_type_var_from_base, -) -from ._streams import ( - consume_sync_iterator as consume_sync_iterator, - consume_async_iterator as consume_async_iterator, -) -from ._transform import ( - PropertyInfo as PropertyInfo, - transform as transform, - async_transform as async_transform, - maybe_transform as maybe_transform, - async_maybe_transform as async_maybe_transform, -) -from ._reflection import ( - function_has_argument as function_has_argument, - assert_signatures_in_sync as assert_signatures_in_sync, -) diff --git a/pkg/hanzoai/_utils/_utils.py b/pkg/hanzoai/_utils/_utils.py deleted file mode 100644 index f61b6e1f2..000000000 --- a/pkg/hanzoai/_utils/_utils.py +++ /dev/null @@ -1,425 +0,0 @@ -from __future__ import annotations - -import os -import re -import inspect -import functools -from typing import ( - Any, - Tuple, - Mapping, - TypeVar, - Callable, - Iterable, - Sequence, - cast, - overload, -) -from pathlib import Path -from datetime import date, datetime -from typing_extensions import TypeGuard - -import sniffio - -from .._types import NotGiven, FileTypes, NotGivenOr, HeadersLike -from .._compat import parse_date as parse_date, parse_datetime as parse_datetime - -_T = TypeVar("_T") -_TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) -_MappingT = TypeVar("_MappingT", bound=Mapping[str, object]) -_SequenceT = TypeVar("_SequenceT", bound=Sequence[object]) -CallableT = TypeVar("CallableT", bound=Callable[..., Any]) - - -def flatten(t: Iterable[Iterable[_T]]) -> list[_T]: - return [item for sublist in t for item in sublist] - - -def extract_files( - # TODO: this needs to take Dict but variance issues..... - # create protocol type ? - query: Mapping[str, object], - *, - paths: Sequence[Sequence[str]], -) -> list[tuple[str, FileTypes]]: - """Recursively extract files from the given dictionary based on specified paths. - - A path may look like this ['foo', 'files', '', 'data']. - - Note: this mutates the given dictionary. - """ - files: list[tuple[str, FileTypes]] = [] - for path in paths: - files.extend(_extract_items(query, path, index=0, flattened_key=None)) - return files - - -def _extract_items( - obj: object, - path: Sequence[str], - *, - index: int, - flattened_key: str | None, -) -> list[tuple[str, FileTypes]]: - try: - key = path[index] - except IndexError: - if isinstance(obj, NotGiven): - # no value was provided - we can safely ignore - return [] - - # cyclical import - from .._files import assert_is_file_content - - # We have exhausted the path, return the entry we found. - assert_is_file_content(obj, key=flattened_key) - assert flattened_key is not None - return [(flattened_key, cast(FileTypes, obj))] - - index += 1 - if is_dict(obj): - try: - # We are at the last entry in the path so we must remove the field - if (len(path)) == index: - item = obj.pop(key) - else: - item = obj[key] - except KeyError: - # Key was not present in the dictionary, this is not indicative of an error - # as the given path may not point to a required field. We also do not want - # to enforce required fields as the API may differ from the spec in some cases. - return [] - if flattened_key is None: - flattened_key = key - else: - flattened_key += f"[{key}]" - return _extract_items( - item, - path, - index=index, - flattened_key=flattened_key, - ) - elif is_list(obj): - if key != "": - return [] - - return flatten( - [ - _extract_items( - item, - path, - index=index, - flattened_key=( - flattened_key + "[]" if flattened_key is not None else "[]" - ), - ) - for item in obj - ] - ) - - # Something unexpected was passed, just ignore it. - return [] - - -def is_given(obj: NotGivenOr[_T]) -> TypeGuard[_T]: - return not isinstance(obj, NotGiven) - - -# Type safe methods for narrowing types with TypeVars. -# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], -# however this cause Pyright to rightfully report errors. As we know we don't -# care about the contained types we can safely use `object` in it's place. -# -# There are two separate functions defined, `is_*` and `is_*_t` for different use cases. -# `is_*` is for when you're dealing with an unknown input -# `is_*_t` is for when you're narrowing a known union type to a specific subset - - -def is_tuple(obj: object) -> TypeGuard[tuple[object, ...]]: - return isinstance(obj, tuple) - - -def is_tuple_t(obj: _TupleT | object) -> TypeGuard[_TupleT]: - return isinstance(obj, tuple) - - -def is_sequence(obj: object) -> TypeGuard[Sequence[object]]: - return isinstance(obj, Sequence) - - -def is_sequence_t(obj: _SequenceT | object) -> TypeGuard[_SequenceT]: - return isinstance(obj, Sequence) - - -def is_mapping(obj: object) -> TypeGuard[Mapping[str, object]]: - return isinstance(obj, Mapping) - - -def is_mapping_t(obj: _MappingT | object) -> TypeGuard[_MappingT]: - return isinstance(obj, Mapping) - - -def is_dict(obj: object) -> TypeGuard[dict[object, object]]: - return isinstance(obj, dict) - - -def is_list(obj: object) -> TypeGuard[list[object]]: - return isinstance(obj, list) - - -def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: - return isinstance(obj, Iterable) - - -def deepcopy_minimal(item: _T) -> _T: - """Minimal reimplementation of copy.deepcopy() that will only copy certain object types: - - - mappings, e.g. `dict` - - list - - This is done for performance reasons. - """ - if is_mapping(item): - return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()}) - if is_list(item): - return cast(_T, [deepcopy_minimal(entry) for entry in item]) - return item - - -# copied from https://github.com/Rapptz/RoboDanny -def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str: - size = len(seq) - if size == 0: - return "" - - if size == 1: - return seq[0] - - if size == 2: - return f"{seq[0]} {final} {seq[1]}" - - return delim.join(seq[:-1]) + f" {final} {seq[-1]}" - - -def quote(string: str) -> str: - """Add single quotation marks around the given string. Does *not* do any escaping.""" - return f"'{string}'" - - -def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: - """Decorator to enforce a given set of arguments or variants of arguments are passed to the decorated function. - - Useful for enforcing runtime validation of overloaded functions. - - Example usage: - ```py - @overload - def foo(*, a: str) -> str: ... - - - @overload - def foo(*, b: bool) -> str: ... - - - # This enforces the same constraints that a static type checker would - # i.e. that either a or b must be passed to the function - @required_args(["a"], ["b"]) - def foo(*, a: str | None = None, b: bool | None = None) -> str: ... - ``` - """ - - def inner(func: CallableT) -> CallableT: - params = inspect.signature(func).parameters - positional = [ - name - for name, param in params.items() - if param.kind - in { - param.POSITIONAL_ONLY, - param.POSITIONAL_OR_KEYWORD, - } - ] - - @functools.wraps(func) - def wrapper(*args: object, **kwargs: object) -> object: - given_params: set[str] = set() - for i, _ in enumerate(args): - try: - given_params.add(positional[i]) - except IndexError: - raise TypeError( - f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given" - ) from None - - for key in kwargs: - given_params.add(key) - - for variant in variants: - matches = all((param in given_params for param in variant)) - if matches: - break - else: # no break - if len(variants) > 1: - variations = human_join( - [ - "(" - + human_join([quote(arg) for arg in variant], final="and") - + ")" - for variant in variants - ] - ) - msg = f"Missing required arguments; Expected either {variations} arguments to be given" - else: - assert len(variants) > 0 - - # TODO: this error message is not deterministic - missing = list(set(variants[0]) - given_params) - if len(missing) > 1: - msg = f"Missing required arguments: {human_join([quote(arg) for arg in missing])}" - else: - msg = f"Missing required argument: {quote(missing[0])}" - raise TypeError(msg) - return func(*args, **kwargs) - - return wrapper # type: ignore - - return inner - - -_K = TypeVar("_K") -_V = TypeVar("_V") - - -@overload -def strip_not_given(obj: None) -> None: ... - - -@overload -def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: ... - - -@overload -def strip_not_given(obj: object) -> object: ... - - -def strip_not_given(obj: object | None) -> object: - """Remove all top-level keys where their values are instances of `NotGiven`""" - if obj is None: - return None - - if not is_mapping(obj): - return obj - - return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)} - - -def coerce_integer(val: str) -> int: - return int(val, base=10) - - -def coerce_float(val: str) -> float: - return float(val) - - -def coerce_boolean(val: str) -> bool: - return val == "true" or val == "1" or val == "on" - - -def maybe_coerce_integer(val: str | None) -> int | None: - if val is None: - return None - return coerce_integer(val) - - -def maybe_coerce_float(val: str | None) -> float | None: - if val is None: - return None - return coerce_float(val) - - -def maybe_coerce_boolean(val: str | None) -> bool | None: - if val is None: - return None - return coerce_boolean(val) - - -def removeprefix(string: str, prefix: str) -> str: - """Remove a prefix from a string. - - Backport of `str.removeprefix` for Python < 3.9 - """ - if string.startswith(prefix): - return string[len(prefix) :] - return string - - -def removesuffix(string: str, suffix: str) -> str: - """Remove a suffix from a string. - - Backport of `str.removesuffix` for Python < 3.9 - """ - if string.endswith(suffix): - return string[: -len(suffix)] - return string - - -def file_from_path(path: str) -> FileTypes: - contents = Path(path).read_bytes() - file_name = os.path.basename(path) - return (file_name, contents) - - -def get_required_header(headers: HeadersLike, header: str) -> str: - lower_header = header.lower() - if is_mapping_t(headers): - # mypy doesn't understand the type narrowing here - for k, v in headers.items(): # type: ignore - if k.lower() == lower_header and isinstance(v, str): - return v - - # to deal with the case where the header looks like Hanzo-Event-Id - intercaps_header = re.sub( - r"([^\w])(\w)", - lambda pat: pat.group(1) + pat.group(2).upper(), - header.capitalize(), - ) - - for normalized_header in [header, lower_header, header.upper(), intercaps_header]: - value = headers.get(normalized_header) - if value: - return value - - raise ValueError(f"Could not find {header} header") - - -def get_async_library() -> str: - try: - return sniffio.current_async_library() - except Exception: - return "false" - - -def lru_cache(*, maxsize: int | None = 128) -> Callable[[CallableT], CallableT]: - """A version of functools.lru_cache that retains the type signature - for the wrapped function arguments. - """ - wrapper = functools.lru_cache( # noqa: TID251 - maxsize=maxsize, - ) - return cast(Any, wrapper) # type: ignore[no-any-return] - - -def json_safe(data: object) -> object: - """Translates a mapping / sequence recursively in the same fashion - as `pydantic` v2's `model_dump(mode="json")`. - """ - if is_mapping(data): - return {json_safe(key): json_safe(value) for key, value in data.items()} - - if is_iterable(data) and not isinstance(data, (str, bytes, bytearray)): - return [json_safe(item) for item in data] - - if isinstance(data, (datetime, date)): - return data.isoformat() - - return data diff --git a/pkg/hanzoai/_version.py b/pkg/hanzoai/_version.py deleted file mode 100644 index d37f9867b..000000000 --- a/pkg/hanzoai/_version.py +++ /dev/null @@ -1,4 +0,0 @@ -# Hanzo AI SDK - -__title__ = "hanzoai" -__version__ = "2.2.1" diff --git a/pkg/hanzoai/agents.py b/pkg/hanzoai/agents.py deleted file mode 100644 index 7515a3ee8..000000000 --- a/pkg/hanzoai/agents.py +++ /dev/null @@ -1,141 +0,0 @@ -"""Hanzo AI Agents module. - -This module provides access to the hanzo-agents SDK for building -sophisticated AI agent systems with local and distributed execution. -""" - -try: - # Try to import hanzo-agents if installed - from hanzo_agents import ( - Tool, - Agent, - State, - History, - Network, - ToolCall, - # Core agent types - BaseAgent, - GrokAgent, - LocalAgent, - # Utilities - AgentConfig, - GeminiAgent, - # Network features - PeerNetwork, - RemoteAgent, - SwarmNetwork, - ModelRegistry, - NetworkConfig, - # CLI agents - ClaudeCodeAgent, - InferenceResult, - OpenAICodexAgent, - create_memory_kv, - sequential_router, - state_based_router, - create_memory_vector, - ) - - AGENTS_AVAILABLE = True -except ImportError: - AGENTS_AVAILABLE = False - - # Provide a helpful error message - def _agents_not_installed(*args, **kwargs): - raise ImportError( - "hanzo-agents is not installed. Install it with: pip install hanzo-agents" - ) - - # Create placeholder classes/functions - Agent = State = Network = Tool = History = _agents_not_installed - ModelRegistry = InferenceResult = ToolCall = _agents_not_installed - create_memory_kv = create_memory_vector = _agents_not_installed - sequential_router = state_based_router = _agents_not_installed - BaseAgent = LocalAgent = RemoteAgent = _agents_not_installed - ClaudeCodeAgent = OpenAICodexAgent = _agents_not_installed - GeminiAgent = GrokAgent = _agents_not_installed - PeerNetwork = SwarmNetwork = _agents_not_installed - AgentConfig = NetworkConfig = _agents_not_installed - - -def create_agent( - name: str, model: str = "anthropic/claude-3-5-sonnet-20241022", **kwargs -): - """Create a new AI agent. - - Args: - name: Name of the agent - model: Model to use (default: Claude Sonnet) - **kwargs: Additional configuration options - - Returns: - Agent instance - """ - if not AGENTS_AVAILABLE: - raise ImportError( - "hanzo-agents is not installed. Install it with: pip install hanzo-agents" - ) - - config = AgentConfig(name=name, model=model, **kwargs) - return LocalAgent(config) - - -def create_network(agents: list, router=None, **kwargs): - """Create an agent network. - - Args: - agents: List of agents to include in the network - router: Router to use (default: sequential) - **kwargs: Additional configuration options - - Returns: - Network instance - """ - if not AGENTS_AVAILABLE: - raise ImportError( - "hanzo-agents is not installed. Install it with: pip install hanzo-agents" - ) - - if router is None: - router = sequential_router() - - config = NetworkConfig(agents=agents, router=router, **kwargs) - return Network(config) - - -__all__ = [ - # Core classes - "Agent", - "State", - "Network", - "Tool", - "History", - "ModelRegistry", - "InferenceResult", - "ToolCall", - # Memory functions - "create_memory_kv", - "create_memory_vector", - # Routers - "sequential_router", - "state_based_router", - # Agent types - "BaseAgent", - "LocalAgent", - "RemoteAgent", - "ClaudeCodeAgent", - "OpenAICodexAgent", - "GeminiAgent", - "GrokAgent", - # Network types - "PeerNetwork", - "SwarmNetwork", - # Configs - "AgentConfig", - "NetworkConfig", - # Convenience functions - "create_agent", - "create_network", - # Status flag - "AGENTS_AVAILABLE", -] diff --git a/pkg/hanzoai/auth.py b/pkg/hanzoai/auth.py deleted file mode 100644 index 3a58a5e93..000000000 --- a/pkg/hanzoai/auth.py +++ /dev/null @@ -1,1089 +0,0 @@ -"""Authentication module for Hanzo AI. - -Supports multiple authentication methods: -1. API Key (HANZO_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY) -2. Email/Password via IAM (iam.hanzo.ai - Casdoor) -3. SSO authentication -4. Anthropic OAuth (PKCE via console.anthropic.com) -5. OpenAI/ChatGPT OAuth (device code via auth.openai.com) -6. MCP flow authentication -7. Auto-detect (login_auto) -""" - -import base64 -import hashlib -import os -import sys -import json -import asyncio -import webbrowser -from dataclasses import dataclass -from http.server import HTTPServer, BaseHTTPRequestHandler -from threading import Thread -from typing import Any, Dict, List, Optional -from pathlib import Path -from urllib.parse import urlencode, parse_qs, urlparse - -import httpx - - -# --------------------------------------------------------------------------- -# PKCE (Proof Key for Code Exchange) with S256 -# --------------------------------------------------------------------------- - -def _base64url_encode(data: bytes) -> str: - """Base64url encode with no padding.""" - return base64.urlsafe_b64encode(data).rstrip(b"=").decode("ascii") - - -@dataclass(frozen=True) -class PkceCodePair: - """PKCE verifier/challenge pair.""" - verifier: str - challenge: str - challenge_method: str = "S256" - - -def generate_pkce_pair() -> PkceCodePair: - """Generate a PKCE code verifier and S256 challenge. - - Uses 32 bytes of os.urandom for the verifier, SHA-256 for the challenge. - Both are base64url encoded with no padding. - """ - verifier = _base64url_encode(os.urandom(32)) - challenge = _base64url_encode(hashlib.sha256(verifier.encode("ascii")).digest()) - return PkceCodePair(verifier=verifier, challenge=challenge) - - -def generate_state() -> str: - """Generate a random state token (32 bytes, base64url, no padding).""" - return _base64url_encode(os.urandom(32)) - - -@dataclass(frozen=True) -class OAuthAuthorizationRequest: - """OAuth authorization request with PKCE parameters.""" - authorize_url: str - client_id: str - redirect_uri: str - scopes: List[str] - state: str - code_challenge: str - code_challenge_method: str - - def build_url(self) -> str: - """Build the full authorization URL with query parameters.""" - params = { - "client_id": self.client_id, - "redirect_uri": self.redirect_uri, - "response_type": "code", - "scope": " ".join(self.scopes), - "state": self.state, - "code_challenge": self.code_challenge, - "code_challenge_method": self.code_challenge_method, - } - return f"{self.authorize_url}?{urlencode(params)}" - - -@dataclass(frozen=True) -class OAuthTokenExchangeRequest: - """OAuth token exchange request with PKCE code_verifier.""" - code: str - redirect_uri: str - client_id: str - code_verifier: str - state: str - grant_type: str = "authorization_code" - - def form_params(self) -> Dict[str, str]: - """Return parameters for the token exchange POST body.""" - return { - "grant_type": self.grant_type, - "code": self.code, - "redirect_uri": self.redirect_uri, - "client_id": self.client_id, - "code_verifier": self.code_verifier, - "state": self.state, - } - - -@dataclass -class OAuthTokenSet: - """OAuth token set returned from token exchange.""" - access_token: str - scopes: List[str] - refresh_token: Optional[str] = None - expires_at: Optional[int] = None - - -class OAuthCredentialStore: - """Manages OAuth credentials in ~/.hanzo/credentials.json. - - Respects HANZO_CONFIG_HOME env var. Uses atomic writes. - Preserves other keys in the credentials file. - - Supports multiple providers via the `provider` parameter: - - "hanzo" -> stored under "oauth" key (backwards compatible) - - "anthropic" -> stored under "oauth_anthropic" key - - "openai" -> stored under "oauth_openai" key - """ - - # Map provider names to JSON keys. Default "hanzo" uses "oauth" - # for backwards compatibility with existing credentials files. - _PROVIDER_KEYS = { - "hanzo": "oauth", - "anthropic": "oauth_anthropic", - "openai": "oauth_openai", - } - - def __init__(self, config_home: Optional[str] = None): - self._config_home = config_home - - def _get_config_home(self) -> Path: - if self._config_home: - return Path(self._config_home) - env = os.environ.get("HANZO_CONFIG_HOME") - if env: - return Path(env) - return Path.home() / ".hanzo" - - def _key_for(self, provider: str) -> str: - key = self._PROVIDER_KEYS.get(provider) - if key is None: - raise ValueError(f"unknown provider: {provider!r}") - return key - - def credentials_path(self) -> Path: - return self._get_config_home() / "credentials.json" - - def _read_file(self) -> Dict[str, Any]: - path = self.credentials_path() - if not path.exists(): - return {} - with open(path, "r") as f: - return json.load(f) - - def _write_file(self, data: Dict[str, Any]) -> None: - path = self.credentials_path() - path.parent.mkdir(parents=True, exist_ok=True) - tmp_path = path.with_suffix(".json.tmp") - with open(tmp_path, "w") as f: - json.dump(data, f, indent=2) - os.replace(tmp_path, path) - if sys.platform != "win32": - os.chmod(path, 0o600) - - def save(self, token_set: OAuthTokenSet, provider: str = "hanzo") -> None: - """Save OAuth token set. Atomic write, preserves other keys.""" - key = self._key_for(provider) - existing = self._read_file() - existing[key] = { - "access_token": token_set.access_token, - "refresh_token": token_set.refresh_token, - "expires_at": token_set.expires_at, - "scopes": token_set.scopes, - } - self._write_file(existing) - - def load(self, provider: str = "hanzo") -> Optional[OAuthTokenSet]: - """Load OAuth token set from credentials file. Returns None if absent.""" - key = self._key_for(provider) - data = self._read_file() - oauth = data.get(key) - if not oauth: - return None - return OAuthTokenSet( - access_token=oauth["access_token"], - refresh_token=oauth.get("refresh_token"), - expires_at=oauth.get("expires_at"), - scopes=oauth.get("scopes", []), - ) - - def clear(self, provider: str = "hanzo") -> None: - """Remove provider's oauth key from credentials, preserving other keys.""" - key = self._key_for(provider) - path = self.credentials_path() - if not path.exists(): - return - data = self._read_file() - data.pop(key, None) - self._write_file(data) - - -# --------------------------------------------------------------------------- -# Provider constants (matching codex-rs/login/src/auth/manager.rs) -# --------------------------------------------------------------------------- - -# OpenAI / ChatGPT -OPENAI_ISSUER = "https://auth.openai.com" -OPENAI_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann" - -# Anthropic / Claude -ANTHROPIC_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e" -ANTHROPIC_ISSUER = "https://claude.ai" -ANTHROPIC_TOKEN_URL = "https://platform.claude.com/v1/oauth/token" -ANTHROPIC_AUTHORIZE_URL = "https://claude.ai/oauth/authorize" - -# Hanzo IAM -IAM_CLIENT_ID = "hanzo-dev" - - -class HanzoAuth: - """Hanzo authentication client.""" - - def __init__( - self, - api_key: Optional[str] = None, - base_url: str = "https://hanzo.id", - api_base_url: str = "https://api.hanzo.ai", - ): - """Initialize authentication client. - - Args: - api_key: API key (defaults to HANZO_API_KEY env var) - base_url: IAM service URL (default: https://hanzo.id) - api_base_url: API service URL - """ - self.api_key = api_key or os.environ.get("HANZO_API_KEY") - self.base_url = os.environ.get("IAM_URL", base_url) - self.api_base_url = api_base_url - self._token = None - self._user_info = None - - async def is_authenticated(self) -> bool: - """Check if currently authenticated.""" - if self.api_key: - return True - if self._token: - # Verify token is still valid - try: - await self.get_user_info() - return True - except Exception: - self._token = None - return False - return False - - async def login(self, email: str, password: str) -> Dict[str, Any]: - """Login with email and password. - - Args: - email: User email - password: User password - - Returns: - User information and tokens - """ - async with httpx.AsyncClient() as client: - response = await client.post( - f"{self.base_url}/api/login", - json={ - "username": email, - "password": password, - "application": "app-hanzo", - }, - ) - response.raise_for_status() - - data = response.json() - self._token = data.get("token") - - # Get user info - user_info = await self.get_user_info() - - return {"token": self._token, "email": email, **user_info} - - async def login_with_api_key(self, api_key: str) -> Dict[str, Any]: - """Login with API key. - - Args: - api_key: Hanzo API key - - Returns: - User information - """ - self.api_key = api_key - - # Verify key is valid - user_info = await self.get_user_info() - - return {"api_key": api_key, **user_info} - - async def login_with_device_code( - self, - open_browser: bool = True, - poll_interval: float = 5.0, - timeout: float = 300.0, - ) -> Dict[str, Any]: - """Login with Device Code flow (works on remote/headless systems). - - This is the recommended auth method for CLI tools. User visits a URL - and enters a code, no local server required. - - Args: - open_browser: Whether to automatically open browser (default True) - poll_interval: Seconds between polling attempts (default 5) - timeout: Max seconds to wait for auth (default 300) - - Returns: - User information and tokens - """ - import time - - async with httpx.AsyncClient() as client: - # Step 1: Request device code - response = await client.post( - f"{self.base_url}/api/device/code", - json={ - "client_id": "app-hanzo", - "scope": "openid profile email", - }, - ) - response.raise_for_status() - - data = response.json() - device_code = data["device_code"] - user_code = data["user_code"] - verification_url = data.get("verification_uri", f"{self.base_url}/device") - verification_url_complete = data.get( - "verification_uri_complete", f"{verification_url}?user_code={user_code}" - ) - expires_in = data.get("expires_in", timeout) - interval = data.get("interval", poll_interval) - - # Step 2: Display instructions to user - print(f"\n\033[1;36mTo sign in, visit:\033[0m {verification_url}") - print(f"\033[1;33mEnter code:\033[0m {user_code}\n") - - # Optionally open browser - if open_browser: - try: - webbrowser.open(verification_url_complete) - print("\033[2m(Browser opened automatically)\033[0m\n") - except Exception: - pass - - # Step 3: Poll for completion - start_time = time.time() - while time.time() - start_time < min(expires_in, timeout): - await asyncio.sleep(interval) - - try: - token_response = await client.post( - f"{self.base_url}/api/device/token", - json={ - "client_id": "app-hanzo", - "device_code": device_code, - "grant_type": "urn:ietf:params:oauth:grant-type:device_code", - }, - ) - - if token_response.status_code == 200: - token_data = token_response.json() - self._token = token_data.get("access_token") - - # Get user info - user_info = await self.get_user_info() - print("\033[1;32mโœ“ Authentication successful!\033[0m\n") - - return {"token": self._token, **user_info} - - elif token_response.status_code == 400: - error_data = token_response.json() - error = error_data.get("error") - - if error == "authorization_pending": - # User hasn't completed auth yet, keep polling - continue - elif error == "slow_down": - # Server asking us to slow down - interval += 5 - continue - elif error == "expired_token": - raise RuntimeError("Device code expired. Please try again.") - elif error == "access_denied": - raise RuntimeError("Authentication denied by user.") - else: - raise RuntimeError(f"Authentication error: {error}") - - except httpx.HTTPStatusError as e: - if e.response.status_code != 400: - raise - - raise RuntimeError("Authentication timed out. Please try again.") - - async def login_with_sso(self) -> OAuthTokenSet: - """Login with SSO (browser-based). - - Delegates to login_with_pkce with the SSO redirect port. - - Returns: - OAuthTokenSet with access token and metadata - """ - return await self.login_with_pkce(redirect_port=8899) - - async def login_with_pkce( - self, - authorize_url: Optional[str] = None, - token_url: Optional[str] = None, - client_id: str = "app-hanzo", - scopes: Optional[List[str]] = None, - redirect_port: int = 4545, - ) -> OAuthTokenSet: - """Login with PKCE (Proof Key for Code Exchange) flow. - - Generates a PKCE pair, opens the browser, catches the callback - on a local HTTP server, exchanges the code for tokens, and saves - credentials. - - Args: - authorize_url: Authorization endpoint (default: {base_url}/oauth/authorize) - token_url: Token endpoint (default: {base_url}/oauth/token) - client_id: OAuth client ID - scopes: OAuth scopes - redirect_port: Local port for the redirect callback server - - Returns: - OAuthTokenSet with access token and metadata - """ - if authorize_url is None: - authorize_url = f"{self.base_url}/oauth/authorize" - if token_url is None: - token_url = f"{self.base_url}/oauth/token" - if scopes is None: - scopes = ["openid", "profile", "email"] - - pkce = generate_pkce_pair() - state = generate_state() - redirect_uri = f"http://localhost:{redirect_port}/callback" - - auth_req = OAuthAuthorizationRequest( - authorize_url=authorize_url, - client_id=client_id, - redirect_uri=redirect_uri, - scopes=scopes, - state=state, - code_challenge=pkce.challenge, - code_challenge_method=pkce.challenge_method, - ) - - # Capture the auth code from the callback - result: Dict[str, Optional[str]] = {"code": None, "error": None} - - class CallbackHandler(BaseHTTPRequestHandler): - def do_GET(self): - qs = parse_qs(urlparse(self.path).query) - - cb_state = qs.get("state", [None])[0] - if cb_state != state: - self.send_response(400) - self.send_header("Content-Type", "text/plain") - self.end_headers() - self.wfile.write(b"Invalid state parameter.") - result["error"] = "state mismatch" - return - - if "error" in qs: - self.send_response(400) - self.send_header("Content-Type", "text/plain") - self.end_headers() - msg = qs["error"][0] - self.wfile.write(f"Authorization error: {msg}".encode()) - result["error"] = msg - return - - result["code"] = qs.get("code", [None])[0] - self.send_response(200) - self.send_header("Content-Type", "text/html") - self.end_headers() - self.wfile.write(b"Authentication successful. You can close this window.") - - def log_message(self, format, *args): - pass # Silence request logs - - server = HTTPServer(("127.0.0.1", redirect_port), CallbackHandler) - - # Open browser - url = auth_req.build_url() - try: - webbrowser.open(url) - except Exception: - pass - print(f"\nOpen this URL to authenticate:\n {url}\n") - - # Serve one request to catch the callback - def serve_once(): - server.handle_request() - server.server_close() - - thread = Thread(target=serve_once, daemon=True) - thread.start() - # Wait for the callback (runs in background thread) - await asyncio.get_event_loop().run_in_executor(None, thread.join, 300) - - if result["error"]: - raise RuntimeError(f"PKCE authorization failed: {result['error']}") - if not result["code"]: - raise RuntimeError("No authorization code received.") - - # Exchange code for tokens - exchange_req = OAuthTokenExchangeRequest( - code=result["code"], - redirect_uri=redirect_uri, - client_id=client_id, - code_verifier=pkce.verifier, - state=state, - ) - - async with httpx.AsyncClient() as client: - response = await client.post( - token_url, - data=exchange_req.form_params(), - ) - response.raise_for_status() - data = response.json() - - token_set = OAuthTokenSet( - access_token=data["access_token"], - refresh_token=data.get("refresh_token"), - expires_at=data.get("expires_at"), - scopes=data.get("scope", " ".join(scopes)).split(), - ) - - self._token = token_set.access_token - - # Save credentials - store = OAuthCredentialStore() - store.save(token_set) - - return token_set - - async def login_with_anthropic( - self, - redirect_port: int = 4545, - ) -> OAuthTokenSet: - """Login with Anthropic console OAuth (PKCE flow). - - Opens browser to claude.ai/oauth/authorize, catches callback, - exchanges code for tokens via platform.claude.com. - - Args: - redirect_port: Local port for redirect callback (default 4545) - - Returns: - OAuthTokenSet with access token - """ - token_set = await self.login_with_pkce( - authorize_url=ANTHROPIC_AUTHORIZE_URL, - token_url=ANTHROPIC_TOKEN_URL, - client_id=ANTHROPIC_CLIENT_ID, - scopes=["openid", "profile", "email"], - redirect_port=redirect_port, - ) - - # Also save under anthropic provider key - store = OAuthCredentialStore() - store.save(token_set, provider="anthropic") - - return token_set - - async def login_with_openai( - self, - open_browser: bool = True, - poll_interval: float = 5.0, - timeout: float = 900.0, - ) -> OAuthTokenSet: - """Login with OpenAI/ChatGPT device code flow. - - Requests a device code from auth.openai.com, displays a user code, - and polls until the user completes authentication. - - Args: - open_browser: Whether to open browser automatically - poll_interval: Seconds between poll attempts (default 5) - timeout: Max seconds to wait (default 900 / 15 min) - - Returns: - OAuthTokenSet with access token - """ - import time - - base_url = OPENAI_ISSUER.rstrip("/") - api_url = f"{base_url}/api/accounts" - - async with httpx.AsyncClient() as client: - # Step 1: Request device code - response = await client.post( - f"{api_url}/deviceauth/usercode", - json={"client_id": OPENAI_CLIENT_ID}, - ) - response.raise_for_status() - data = response.json() - - device_auth_id = data["device_auth_id"] - user_code = data["user_code"] - interval = float(data.get("interval", poll_interval)) - verification_url = f"{base_url}/codex/device" - - # Step 2: Display instructions - print(f"\nTo sign in with ChatGPT, visit:") - print(f" {verification_url}") - print(f"\nEnter code: {user_code}") - print(f"(expires in 15 minutes)\n") - - if open_browser: - try: - webbrowser.open(verification_url) - except Exception: - pass - - # Step 3: Poll for completion - start_time = time.time() - while time.time() - start_time < timeout: - await asyncio.sleep(interval) - - poll_resp = await client.post( - f"{api_url}/deviceauth/token", - json={ - "device_auth_id": device_auth_id, - "user_code": user_code, - }, - ) - - if poll_resp.status_code == 200: - code_data = poll_resp.json() - - # Exchange the authorization code for tokens - pkce = PkceCodePair( - verifier=code_data["code_verifier"], - challenge=code_data["code_challenge"], - ) - redirect_uri = f"{base_url}/deviceauth/callback" - - exchange_resp = await client.post( - f"{base_url}/oauth/token", - data={ - "grant_type": "authorization_code", - "code": code_data["authorization_code"], - "redirect_uri": redirect_uri, - "client_id": OPENAI_CLIENT_ID, - "code_verifier": pkce.verifier, - }, - ) - exchange_resp.raise_for_status() - token_data = exchange_resp.json() - - token_set = OAuthTokenSet( - access_token=token_data["access_token"], - refresh_token=token_data.get("refresh_token"), - expires_at=token_data.get("expires_at"), - scopes=token_data.get("scope", "openid profile email").split(), - ) - - self._token = token_set.access_token - - store = OAuthCredentialStore() - store.save(token_set, provider="openai") - - print("Authentication successful.\n") - return token_set - - if poll_resp.status_code in (403, 404): - # User hasn't completed auth yet - continue - - # Unexpected error - poll_resp.raise_for_status() - - raise RuntimeError("OpenAI device code authentication timed out.") - - async def login_auto(self) -> Dict[str, Any]: - """Auto-detect authentication provider. - - Priority: - 1. HANZO_API_KEY env var - 2. ANTHROPIC_API_KEY env var - 3. OPENAI_API_KEY env var - 4. Interactive prompt to choose provider - - Returns: - Dict with auth info (token or api_key, and provider name) - """ - # Check env vars in priority order - if api_key := os.environ.get("HANZO_API_KEY"): - self.api_key = api_key - return {"provider": "hanzo", "api_key": api_key} - - if api_key := os.environ.get("ANTHROPIC_API_KEY"): - self.api_key = api_key - return {"provider": "anthropic", "api_key": api_key} - - if api_key := os.environ.get("OPENAI_API_KEY"): - self.api_key = api_key - return {"provider": "openai", "api_key": api_key} - - # Check saved credentials - store = OAuthCredentialStore() - for provider in ("hanzo", "anthropic", "openai"): - token_set = store.load(provider=provider) - if token_set: - self._token = token_set.access_token - return {"provider": provider, "token": token_set.access_token} - - # Interactive: prompt user to choose - print("\nNo API key or saved credentials found.") - print("Choose authentication provider:") - print(" 1. Hanzo (hanzo.id)") - print(" 2. Anthropic (console.anthropic.com)") - print(" 3. OpenAI (ChatGPT)") - - choice = input("\nEnter choice [1/2/3]: ").strip() - - if choice == "1": - token_set = await self.login_with_pkce() - return {"provider": "hanzo", "token": token_set.access_token} - elif choice == "2": - token_set = await self.login_with_anthropic() - return {"provider": "anthropic", "token": token_set.access_token} - elif choice == "3": - token_set = await self.login_with_openai() - return {"provider": "openai", "token": token_set.access_token} - else: - raise ValueError(f"invalid choice: {choice!r}") - - async def logout(self): - """Logout and clear credentials.""" - if self._token: - # Revoke token - async with httpx.AsyncClient() as client: - await client.post( - f"{self.base_url}/api/logout", headers=self._get_headers() - ) - - self._token = None - self.api_key = None - self._user_info = None - - async def get_user_info(self) -> Dict[str, Any]: - """Get current user information. - - Returns: - User details including permissions - """ - if self._user_info: - return self._user_info - - async with httpx.AsyncClient() as client: - response = await client.get( - f"{self.api_base_url}/v1/user", headers=self._get_headers() - ) - response.raise_for_status() - - self._user_info = response.json() - return self._user_info - - async def create_api_key( - self, - name: str, - permissions: Optional[List[str]] = None, - expires: Optional[str] = None, - ) -> Dict[str, Any]: - """Create a new API key. - - Args: - name: Key name - permissions: List of permissions - expires: Expiration (e.g., "30d", "1y", "never") - - Returns: - API key information - """ - async with httpx.AsyncClient() as client: - response = await client.post( - f"{self.api_base_url}/v1/api-keys", - headers=self._get_headers(), - json={ - "name": name, - "permissions": permissions or ["read"], - "expires": expires or "1y", - }, - ) - response.raise_for_status() - - return response.json() - - async def list_api_keys(self) -> List[Dict[str, Any]]: - """List user's API keys. - - Returns: - List of API key information - """ - async with httpx.AsyncClient() as client: - response = await client.get( - f"{self.api_base_url}/v1/api-keys", headers=self._get_headers() - ) - response.raise_for_status() - - return response.json().get("keys", []) - - async def revoke_api_key(self, name: str): - """Revoke an API key. - - Args: - name: Key name to revoke - """ - async with httpx.AsyncClient() as client: - response = await client.delete( - f"{self.api_base_url}/v1/api-keys/{name}", headers=self._get_headers() - ) - response.raise_for_status() - - async def save_credentials(self, path: Path): - """Save credentials to file. - - Args: - path: File path to save credentials - """ - creds = { - "api_key": self.api_key, - "token": self._token, - "user_info": self._user_info, - } - - path.parent.mkdir(parents=True, exist_ok=True) - - with open(path, "w") as f: - json.dump(creds, f, indent=2) - - # Set restrictive permissions (no-op on Windows, but harmless) - if sys.platform != "win32": - os.chmod(path, 0o600) - - async def load_credentials(self, path: Path) -> Dict[str, Any]: - """Load credentials from file. - - Args: - path: File path to load credentials - - Returns: - Saved credentials - """ - if not path.exists(): - raise FileNotFoundError(f"Credentials file not found: {path}") - - with open(path, "r") as f: - creds = json.load(f) - - self.api_key = creds.get("api_key") - self._token = creds.get("token") - self._user_info = creds.get("user_info") - - return creds - - def _get_headers(self) -> Dict[str, str]: - """Get authentication headers. - - Returns: - Headers with authentication - """ - headers = {} - - if self.api_key: - headers["Authorization"] = f"Bearer {self.api_key}" - elif self._token: - headers["Authorization"] = f"Bearer {self._token}" - - return headers - - -# MCP Authentication Flow -async def authenticate_for_mcp( - server_name: str = "hanzo-mcp", permissions: List[str] = None -) -> str: - """Authenticate and get token for MCP server. - - Args: - server_name: MCP server name - permissions: Required permissions - - Returns: - Authentication token for MCP - """ - auth = HanzoAuth() - - # Check existing authentication - if await auth.is_authenticated(): - # Get or create MCP-specific token - user_info = await auth.get_user_info() - - # Check if user has required permissions - user_perms = set(user_info.get("permissions", [])) - required_perms = set(permissions or ["mcp.connect"]) - - if not required_perms.issubset(user_perms): - raise PermissionError(f"Missing permissions: {required_perms - user_perms}") - - # Return API key or token - return auth.api_key or auth._token - - # Not authenticated - prompt for login - raise RuntimeError("Not authenticated. Run 'hanzo auth login' first.") - - -# Convenience function for environment setup -def setup_auth_from_env(): - """Setup authentication from environment variables. - - Checks for: - - HANZO_API_KEY - - HANZO_AUTH_TOKEN - """ - if api_key := os.environ.get("HANZO_API_KEY"): - return HanzoAuth(api_key=api_key) - - if token := os.environ.get("HANZO_AUTH_TOKEN"): - auth = HanzoAuth() - auth._token = token - return auth - - # Check for saved credentials - config_file = Path.home() / ".hanzo" / "auth.json" - if config_file.exists(): - auth = HanzoAuth() - try: - import asyncio - - asyncio.run(auth.load_credentials(config_file)) - return auth - except Exception: - pass - - return None - - -# Agent Runtime Authentication -class AgentAuth: - """Authentication helper for agent runtime environments. - - Provides seamless auth for agents running in: - - Local development - - Remote VMs - - Docker containers (Operative, etc.) - - Kubernetes pods - """ - - def __init__(self): - self._hanzo_auth = None - - async def ensure_authenticated( - self, - require_interactive: bool = False, - headless: bool = True, - ) -> HanzoAuth: - """Ensure agent is authenticated, prompting if necessary. - - Args: - require_interactive: Force device code flow even if token exists - headless: Don't try to open browser (for containers) - - Returns: - Authenticated HanzoAuth instance - - Raises: - RuntimeError: If authentication fails - """ - # 1. Check environment variables first (highest priority) - if api_key := os.environ.get("HANZO_API_KEY"): - self._hanzo_auth = HanzoAuth(api_key=api_key) - return self._hanzo_auth - - if token := os.environ.get("HANZO_AUTH_TOKEN"): - self._hanzo_auth = HanzoAuth() - self._hanzo_auth._token = token - return self._hanzo_auth - - # 2. Check saved credentials - config_file = Path.home() / ".hanzo" / "auth.json" - if config_file.exists() and not require_interactive: - try: - self._hanzo_auth = HanzoAuth() - await self._hanzo_auth.load_credentials(config_file) - if await self._hanzo_auth.is_authenticated(): - return self._hanzo_auth - except Exception: - pass # Fall through to device code flow - - # 3. Device code flow for interactive authentication - self._hanzo_auth = HanzoAuth() - result = await self._hanzo_auth.login_with_device_code( - open_browser=not headless - ) - - # Save credentials for future use - await self._hanzo_auth.save_credentials(config_file) - - return self._hanzo_auth - - @property - def token(self) -> Optional[str]: - """Get current auth token.""" - if self._hanzo_auth: - return self._hanzo_auth.api_key or self._hanzo_auth._token - return None - - def get_headers(self) -> Dict[str, str]: - """Get auth headers for API requests.""" - if self._hanzo_auth: - return self._hanzo_auth._get_headers() - return {} - - -async def authenticate_agent( - headless: bool = True, - require_interactive: bool = False, -) -> AgentAuth: - """Authenticate an agent in any environment. - - This is the recommended entry point for agent authentication. - Works in: - - Local CLI (opens browser for device code) - - Remote SSH (displays device code) - - Docker containers (uses HANZO_API_KEY or device code) - - Kubernetes pods (uses service account or device code) - - Args: - headless: Don't try to open browser (True for containers) - require_interactive: Force device code even if cached token exists - - Returns: - AgentAuth instance with authenticated session - - Example: - async def main(): - auth = await authenticate_agent() - # Agent is now authenticated - headers = auth.get_headers() - # Use headers for API requests - """ - agent_auth = AgentAuth() - await agent_auth.ensure_authenticated( - require_interactive=require_interactive, - headless=headless, - ) - return agent_auth - - -def sync_authenticate_agent( - headless: bool = True, - require_interactive: bool = False, -) -> AgentAuth: - """Synchronous wrapper for authenticate_agent. - - For use in non-async contexts. - """ - return asyncio.run( - authenticate_agent( - headless=headless, - require_interactive=require_interactive, - ) - ) diff --git a/pkg/hanzoai/cluster.py b/pkg/hanzoai/cluster.py deleted file mode 100644 index a211b1950..000000000 --- a/pkg/hanzoai/cluster.py +++ /dev/null @@ -1,258 +0,0 @@ -"""Hanzo AI Cluster module for local private AI. - -This module integrates exo-explore for distributed AI compute, -enabling local, private, and free AI inference and training. -""" - -import asyncio -import subprocess -from typing import Any, Dict, List -from dataclasses import dataclass - -try: - # Try to import exo if installed - import exo - from exo import ExoNode, ExoCluster - - EXO_AVAILABLE = True -except ImportError: - EXO_AVAILABLE = False - ExoNode = None - ExoCluster = None - - -@dataclass -class ClusterConfig: - """Configuration for a Hanzo AI cluster.""" - - name: str = "hanzo-cluster" - discovery_port: int = 5678 - model_path: str = None - node_id: str = None - broadcast_addresses: List[str] = None - listen_address: str = None - max_ram: int = None - max_vram: int = None - enable_mining: bool = False - mining_address: str = None - - -class HanzoCluster: - """Hanzo AI Cluster for local distributed AI compute.""" - - def __init__(self, config: ClusterConfig = None): - """Initialize the cluster. - - Args: - config: Cluster configuration - """ - self.config = config or ClusterConfig() - self.node = None - self.process = None - self._check_exo() - - def _check_exo(self): - """Check if exo is available.""" - if not EXO_AVAILABLE: - # Try to install exo - print("exo not found. Installing exo-explore...") - try: - subprocess.run( - ["pip", "install", "exo-explore"], check=True, capture_output=True - ) - # Try importing again - import exo - from exo import ExoNode as _ExoNode, ExoCluster as _ExoCluster - - # Update module-level variables - globals()["exo"] = exo - globals()["ExoNode"] = _ExoNode - globals()["ExoCluster"] = _ExoCluster - globals()["EXO_AVAILABLE"] = True - except Exception as e: - print(f"Failed to install exo: {e}") - print("Please install manually: pip install exo-explore") - - async def start(self): - """Start the cluster node.""" - if not EXO_AVAILABLE: - raise RuntimeError( - "exo is not available. Please install: pip install exo-explore" - ) - - # Build exo command - cmd = ["exo"] - - if self.config.node_id: - cmd.extend(["--node-id", self.config.node_id]) - - if self.config.listen_address: - cmd.extend(["--listen-address", self.config.listen_address]) - - if self.config.broadcast_addresses: - for addr in self.config.broadcast_addresses: - cmd.extend(["--broadcast-address", addr]) - - if self.config.discovery_port: - cmd.extend(["--discovery-port", str(self.config.discovery_port)]) - - if self.config.model_path: - cmd.extend(["--model-path", self.config.model_path]) - - if self.config.max_ram: - cmd.extend(["--max-ram", str(self.config.max_ram)]) - - if self.config.max_vram: - cmd.extend(["--max-vram", str(self.config.max_vram)]) - - # Start the process - self.process = await asyncio.create_subprocess_exec( - *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE - ) - - # Log output - asyncio.create_task(self._log_output()) - - print(f"Started Hanzo cluster node: {' '.join(cmd)}") - - async def _log_output(self): - """Log output from the exo process.""" - if not self.process: - return - - async for line in self.process.stdout: - print(f"[CLUSTER] {line.decode().strip()}") - - async def stop(self): - """Stop the cluster node.""" - if self.process: - self.process.terminate() - await self.process.wait() - self.process = None - print("Stopped Hanzo cluster node") - - def get_api_endpoint(self) -> str: - """Get the API endpoint for the cluster. - - Returns: - API endpoint URL - """ - # Default exo API endpoint - return f"http://localhost:8000" - - async def inference( - self, prompt: str, model: str = None, **kwargs - ) -> Dict[str, Any]: - """Run inference on the cluster. - - Args: - prompt: Input prompt - model: Model to use (optional) - **kwargs: Additional inference parameters - - Returns: - Inference result - """ - endpoint = self.get_api_endpoint() - - # This would make an API call to the exo cluster - # For now, we'll use the standard completion endpoint - import httpx - - async with httpx.AsyncClient() as client: - response = await client.post( - f"{endpoint}/v1/completions", - json={"prompt": prompt, "model": model or "llama-3.2-3b", **kwargs}, - ) - response.raise_for_status() - return response.json() - - -class HanzoMiner: - """Hanzo Miner for distributed AI compute contribution.""" - - def __init__( - self, wallet_address: str = None, cluster_config: ClusterConfig = None - ): - """Initialize the miner. - - Args: - wallet_address: Wallet address for mining rewards - cluster_config: Cluster configuration - """ - self.wallet_address = wallet_address - self.cluster_config = cluster_config or ClusterConfig(enable_mining=True) - self.cluster = HanzoCluster(self.cluster_config) - - async def start_mining(self): - """Start mining by contributing compute to the network.""" - if not self.wallet_address: - raise ValueError("Wallet address required for mining") - - self.cluster_config.mining_address = self.wallet_address - await self.cluster.start() - print(f"Started mining with wallet: {self.wallet_address}") - - async def stop_mining(self): - """Stop mining.""" - await self.cluster.stop() - print("Stopped mining") - - def get_stats(self) -> Dict[str, Any]: - """Get mining statistics. - - Returns: - Mining statistics - """ - # This would fetch real stats from the network - return { - "status": "mining" if self.cluster.process else "stopped", - "wallet": self.wallet_address, - "compute_contributed": "N/A", - "rewards_earned": "N/A", - } - - -# Convenience functions -async def start_local_cluster(name: str = "hanzo-local", **kwargs) -> HanzoCluster: - """Start a local AI cluster. - - Args: - name: Cluster name - **kwargs: Additional configuration - - Returns: - HanzoCluster instance - """ - config = ClusterConfig(name=name, **kwargs) - cluster = HanzoCluster(config) - await cluster.start() - return cluster - - -async def join_mining_network(wallet_address: str, **kwargs) -> HanzoMiner: - """Join the Hanzo mining network. - - Args: - wallet_address: Your wallet address for rewards - **kwargs: Additional configuration - - Returns: - HanzoMiner instance - """ - miner = HanzoMiner(wallet_address=wallet_address) - await miner.start_mining() - return miner - - -__all__ = [ - # Classes - "HanzoCluster", - "HanzoMiner", - "ClusterConfig", - # Functions - "start_local_cluster", - "join_mining_network", - # Status - "EXO_AVAILABLE", -] diff --git a/pkg/hanzoai/config.py b/pkg/hanzoai/config.py deleted file mode 100644 index 6e46d1c51..000000000 --- a/pkg/hanzoai/config.py +++ /dev/null @@ -1,397 +0,0 @@ -"""Hierarchical configuration discovery and merging. - -Three layers (User < Project < Local). Later sources override earlier ones. -MCP server blocks replace entirely. Missing files silently skipped. - -Security: Project-level configs (.hanzo/settings.json in a repo) are untrusted. -A cloned repo could contain a malicious config that defines mcpServers with -stdio transport (arbitrary command execution) or overrides oauth URLs (credential -phishing). By default, project and local configs are restricted to safe keys -only. Pass trust_project_mcp=True to override (requires explicit user approval). -""" - -from __future__ import annotations - -import json -import logging -import os -from dataclasses import dataclass, field -from enum import IntEnum -from pathlib import Path -from typing import Any - -logger = logging.getLogger(__name__) - - -class ConfigError(Exception): - """Raised when a config file exists but cannot be parsed.""" - - def __init__(self, path: Path, reason: str) -> None: - self.path = path - self.reason = reason - super().__init__(f"{path}: {reason}") - - -class ConfigSource(IntEnum): - """Config layer precedence. Higher value = higher priority.""" - - User = 0 - Project = 1 - Local = 2 - - -@dataclass(frozen=True) -class ConfigEntry: - """A discovered config file location.""" - - source: ConfigSource - path: Path - - -class McpTransport(IntEnum): - Stdio = 0 - Sse = 1 - Http = 2 - Ws = 3 - Sdk = 4 - ClaudeAiProxy = 5 - - -@dataclass(frozen=True) -class McpStdioConfig: - command: str - args: list[str] = field(default_factory=list) - env: dict[str, str] = field(default_factory=dict) - - @property - def transport(self) -> McpTransport: - return McpTransport.Stdio - - -@dataclass(frozen=True) -class McpOAuthConfig: - client_id: str | None = None - callback_port: int | None = None - auth_server_metadata_url: str | None = None - xaa: bool | None = None - - -@dataclass(frozen=True) -class McpRemoteConfig: - url: str - headers: dict[str, str] = field(default_factory=dict) - headers_helper: str | None = None - oauth: McpOAuthConfig | None = None - transport: McpTransport = McpTransport.Http - - -@dataclass(frozen=True) -class McpWsConfig: - url: str - headers: dict[str, str] = field(default_factory=dict) - headers_helper: str | None = None - - @property - def transport(self) -> McpTransport: - return McpTransport.Ws - - -@dataclass(frozen=True) -class McpSdkConfig: - name: str - - @property - def transport(self) -> McpTransport: - return McpTransport.Sdk - - -@dataclass(frozen=True) -class McpClaudeAiProxyConfig: - url: str - id: str - - @property - def transport(self) -> McpTransport: - return McpTransport.ClaudeAiProxy - - -McpServerConfig = McpStdioConfig | McpRemoteConfig | McpWsConfig | McpSdkConfig | McpClaudeAiProxyConfig - - -@dataclass(frozen=True) -class OAuthConfig: - client_id: str - authorize_url: str - token_url: str - scopes: list[str] = field(default_factory=list) - callback_port: int | None = None - manual_redirect_url: str | None = None - - -def _deep_merge(base: dict, override: dict) -> dict: - """Merge *override* into *base*, recursing into nested dicts. - - Non-dict values in *override* replace those in *base*. - """ - merged = dict(base) - for key, val in override.items(): - if key in merged and isinstance(merged[key], dict) and isinstance(val, dict): - merged[key] = _deep_merge(merged[key], val) - else: - merged[key] = val - return merged - - -_TRANSPORT_MAP: dict[str, McpTransport] = { - "stdio": McpTransport.Stdio, - "sse": McpTransport.Sse, - "http": McpTransport.Http, - "ws": McpTransport.Ws, - "sdk": McpTransport.Sdk, - "claudeai-proxy": McpTransport.ClaudeAiProxy, -} - - -def _parse_mcp_server(name: str, raw: dict) -> McpServerConfig: - transport_str = raw.get("transport", "stdio") - transport = _TRANSPORT_MAP.get(transport_str) - if transport is None: - raise ConfigError( - Path(""), - f"mcpServers.{name}: unknown transport {transport_str!r}", - ) - - if transport == McpTransport.Stdio: - command = raw.get("command") - if not command or not isinstance(command, str): - raise ConfigError( - Path(""), - f"mcpServers.{name}: stdio transport requires 'command' string", - ) - args = raw.get("args", []) - if not isinstance(args, list): - raise ConfigError( - Path(""), - f"mcpServers.{name}: 'args' must be a list", - ) - env = raw.get("env", {}) - if not isinstance(env, dict): - raise ConfigError( - Path(""), - f"mcpServers.{name}: 'env' must be an object", - ) - return McpStdioConfig(command=command, args=args, env=env) - - if transport == McpTransport.Sdk: - sdk_name = raw.get("name") - if not sdk_name or not isinstance(sdk_name, str): - raise ConfigError( - Path(""), - f"mcpServers.{name}: sdk transport requires 'name' string", - ) - return McpSdkConfig(name=sdk_name) - - if transport == McpTransport.ClaudeAiProxy: - url = raw.get("url") - if not url or not isinstance(url, str): - raise ConfigError( - Path(""), - f"mcpServers.{name}: claudeai-proxy transport requires 'url' string", - ) - server_id = raw.get("id") - if not server_id or not isinstance(server_id, str): - raise ConfigError( - Path(""), - f"mcpServers.{name}: claudeai-proxy transport requires 'id' string", - ) - return McpClaudeAiProxyConfig(url=url, id=server_id) - - # Shared validation for url-based transports: sse, http, ws - url = raw.get("url") - if not url or not isinstance(url, str): - raise ConfigError( - Path(""), - f"mcpServers.{name}: {transport_str} transport requires 'url' string", - ) - headers = raw.get("headers", {}) - if not isinstance(headers, dict): - raise ConfigError( - Path(""), - f"mcpServers.{name}: 'headers' must be an object", - ) - headers_helper = raw.get("headersHelper") or raw.get("headers_helper") - - if transport == McpTransport.Ws: - return McpWsConfig(url=url, headers=headers, headers_helper=headers_helper) - - # sse, http -> McpRemoteConfig - oauth_raw = raw.get("oauth") - oauth: McpOAuthConfig | None = None - if isinstance(oauth_raw, dict): - oauth = McpOAuthConfig( - client_id=oauth_raw.get("clientId") or oauth_raw.get("client_id"), - callback_port=oauth_raw.get("callbackPort") or oauth_raw.get("callback_port"), - auth_server_metadata_url=oauth_raw.get("authServerMetadataUrl") or oauth_raw.get("auth_server_metadata_url"), - xaa=oauth_raw.get("xaa"), - ) - return McpRemoteConfig(url=url, headers=headers, headers_helper=headers_helper, oauth=oauth, transport=transport) - - -def _sanitize_project_config(data: dict, entry: ConfigEntry) -> dict: - """Remove dangerous keys from an untrusted project/local config. - - Strips: - - oauth (credential phishing via overridden auth URLs) - - mcpServers entries with stdio transport (arbitrary command execution) - - Non-stdio mcpServers (http, sse, ws, sdk) are kept -- they connect to - remote URLs which are visible and auditable, not local binary execution. - """ - sanitized = dict(data) - - if "oauth" in sanitized: - logger.warning( - "Ignoring 'oauth' from project config %s โ€” " - "project configs cannot override OAuth URLs (phishing risk). " - "Move to user config (~/.hanzo/settings.json) instead.", - entry.path, - ) - del sanitized["oauth"] - - mcp_raw = sanitized.get("mcpServers") - if isinstance(mcp_raw, dict): - safe_servers: dict = {} - for name, cfg in mcp_raw.items(): - transport = cfg.get("transport", "stdio") if isinstance(cfg, dict) else "stdio" - if transport == "stdio": - logger.warning( - "Ignoring mcpServers.%s (stdio) from project config %s โ€” " - "project configs cannot define stdio MCP servers (arbitrary " - "command execution risk). Move to user config or pass " - "trust_project_mcp=True.", - name, - entry.path, - ) - else: - safe_servers[name] = cfg - if safe_servers: - sanitized["mcpServers"] = safe_servers - else: - del sanitized["mcpServers"] - - return sanitized - - -@dataclass -class RuntimeConfig: - """Merged configuration from all discovered layers.""" - - merged: dict = field(default_factory=dict) - loaded_entries: list[ConfigEntry] = field(default_factory=list) - - @classmethod - def empty(cls) -> RuntimeConfig: - return cls() - - def get(self, key: str) -> Any | None: - return self.merged.get(key) - - def mcp_servers(self) -> dict[str, McpServerConfig]: - raw = self.merged.get("mcpServers") - if not isinstance(raw, dict): - return {} - return {name: _parse_mcp_server(name, cfg) for name, cfg in raw.items()} - - def oauth(self) -> OAuthConfig | None: - raw = self.merged.get("oauth") - if not isinstance(raw, dict): - return None - client_id = raw.get("clientId") or raw.get("client_id") - if not client_id or not isinstance(client_id, str): - return None - authorize_url = raw.get("authorizeUrl") or raw.get("authorize_url") - if not authorize_url or not isinstance(authorize_url, str): - return None - token_url = raw.get("tokenUrl") or raw.get("token_url") - if not token_url or not isinstance(token_url, str): - return None - callback_port = raw.get("callbackPort") or raw.get("callback_port") - return OAuthConfig( - client_id=client_id, - authorize_url=authorize_url, - token_url=token_url, - scopes=raw.get("scopes", []), - callback_port=callback_port, - manual_redirect_url=raw.get("manualRedirectUrl") or raw.get("manual_redirect_url"), - ) - - -@dataclass(frozen=True) -class ConfigLoader: - """Discovers and loads hierarchical config files.""" - - cwd: Path - config_home: Path - - @classmethod - def default_for(cls, cwd: Path) -> ConfigLoader: - config_home = Path(os.environ.get("HANZO_CONFIG_HOME", Path.home() / ".hanzo")) - return cls(cwd=cwd, config_home=config_home) - - def discover(self) -> list[ConfigEntry]: - """Return potential config file entries in precedence order (low to high).""" - return [ - ConfigEntry(ConfigSource.User, self.config_home / "settings.json"), - ConfigEntry(ConfigSource.Project, self.cwd / ".hanzo" / "settings.json"), - ConfigEntry(ConfigSource.Local, self.cwd / ".hanzo" / "settings.local.json"), - ] - - # Keys that are safe to accept from project-level (untrusted) configs. - # mcpServers with stdio transport allows arbitrary command execution. - # oauth allows credential phishing by overriding auth URLs. - # Both are blocked from project/local configs unless explicitly trusted. - _PROJECT_SAFE_KEYS = frozenset({"model", "env", "hooks"}) - - def load(self, *, trust_project_mcp: bool = False) -> RuntimeConfig: - """Read and merge all existing config files. - - Missing files are silently skipped. Invalid JSON raises ConfigError. - The top-level value in each file must be a JSON object. - - Args: - trust_project_mcp: When False (default), project-level and - local-level configs cannot define mcpServers with stdio - transport or oauth blocks. This prevents a malicious cloned - repo from executing arbitrary commands or phishing credentials - via .hanzo/settings.json. Set True only after explicit user - approval. - """ - merged: dict = {} - loaded: list[ConfigEntry] = [] - - for entry in self.discover(): - if not entry.path.is_file(): - continue - - text = entry.path.read_text(encoding="utf-8") - try: - data = json.loads(text) - except json.JSONDecodeError as exc: - raise ConfigError(entry.path, f"invalid JSON: {exc}") from exc - - if not isinstance(data, dict): - raise ConfigError(entry.path, "top-level value must be a JSON object") - - # R-06: Project/local configs are untrusted. Strip dangerous keys - # unless the caller has explicitly opted in. - if entry.source in (ConfigSource.Project, ConfigSource.Local) and not trust_project_mcp: - data = _sanitize_project_config(data, entry) - - # MCP servers from later sources replace entirely (not merged). - if "mcpServers" in data and "mcpServers" in merged: - del merged["mcpServers"] - - merged = _deep_merge(merged, data) - loaded.append(entry) - - return RuntimeConfig(merged=merged, loaded_entries=loaded) diff --git a/pkg/hanzoai/grpo/__init__.py b/pkg/hanzoai/grpo/__init__.py deleted file mode 100644 index dd7ca6ea1..000000000 --- a/pkg/hanzoai/grpo/__init__.py +++ /dev/null @@ -1,94 +0,0 @@ -"""Training-Free GRPO implementation for Hanzo AI. - -This module provides Training-Free Group Relative Policy Optimization (GRPO) -from Tencent's youtu-agent paper (arXiv:2510.08191v1). - -Training-Free GRPO improves LLM performance via context expansion rather than -parameter updates, using semantic advantages extracted via LLM introspection. - -Key components: -- ExperienceManager: Manages the experience library (E) with CRUD operations -- SemanticExtractor: Implements 3-stage LLM process for semantic advantage extraction -- Trajectory: Data class for rollout trajectories -- LLMClient: Wrapper for LLM API clients (DeepSeek, OpenAI) -- APIModelAdapter: Use API-hosted models as target model -- DeepSeekAdapter: Convenience wrapper for DeepSeek models -- OpenAIAdapter: Convenience wrapper for OpenAI models - -Example usage: - ```python - from hanzoai.grpo import DeepSeekAdapter, ExperienceManager, SemanticExtractor, LLMClient - - # Initialize components - target_model = DeepSeekAdapter(api_key="your-api-key") - semantic_llm = LLMClient(api_key="your-api-key", base_url="https://api.deepseek.com/v1") - exp_manager = ExperienceManager(checkpoint_path="./experiences/experiences.json") - extractor = SemanticExtractor(semantic_llm, max_operations=5) - - # Generate rollouts with experience injection - query = "Solve: 2x + 5 = 13" - group_size = 5 - trajectories = [] - - for i in range(group_size): - experiences_text = exp_manager.format_for_prompt() - response = target_model.generate_with_experiences( - query=query, experiences=experiences_text, temperature=0.7 + (i * 0.1) - ) - # Evaluate and create trajectory - # trajectories.append(Trajectory(query=query, output=response, reward=score)) - - # Extract semantic advantages and update experience library - group_operations = extractor.extract_group_advantage(trajectories, exp_manager.format_for_prompt()) - exp_manager.apply_operations(group_operations) - exp_manager.save() - ``` - -For more details, see the Training-Free GRPO paper: -https://arxiv.org/abs/2510.08191 -""" - -# Basic implementations -from .api_model_adapter import ( - OpenAIAdapter, - APIModelConfig, - APIModelAdapter, - DeepSeekAdapter, -) -from .experience_manager import ExperienceManager -from .semantic_extractor import LLMClient, Trajectory, SemanticExtractor -from .enhanced_api_model_adapter import ( - EnhancedOpenAIAdapter, - EnhancedAPIModelConfig, - EnhancedAPIModelAdapter, - EnhancedDeepSeekAdapter, -) - -# Enhanced implementations with full Tencent feature parity -from .enhanced_semantic_extractor import ( - Trajectory as EnhancedTrajectory, - EnhancedLLMClient, - EnhancedSemanticExtractor, - rollout_with_timeout, -) - -__all__ = [ - # Basic - "ExperienceManager", - "SemanticExtractor", - "Trajectory", - "LLMClient", - "APIModelAdapter", - "APIModelConfig", - "DeepSeekAdapter", - "OpenAIAdapter", - # Enhanced (recommended for production use) - "EnhancedSemanticExtractor", - "EnhancedLLMClient", - "EnhancedTrajectory", - "EnhancedAPIModelAdapter", - "EnhancedAPIModelConfig", - "EnhancedDeepSeekAdapter", - "EnhancedOpenAIAdapter", - "rollout_with_timeout", -] diff --git a/pkg/hanzoai/grpo/api_model_adapter.py b/pkg/hanzoai/grpo/api_model_adapter.py deleted file mode 100644 index 7610c3c52..000000000 --- a/pkg/hanzoai/grpo/api_model_adapter.py +++ /dev/null @@ -1,247 +0,0 @@ -# Copyright 2025 Zoo Labs Foundation Inc. and the Gym team. -# -# API Model Adapter for Training-Free GRPO -# Enables using DeepSeek API-hosted models as the target model -# -# 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. - -from typing import Any, Dict, List, Optional -from dataclasses import dataclass - - -@dataclass -class APIModelConfig: - """Configuration for API-hosted models. - - Supports DeepSeek, OpenAI, and any OpenAI-compatible API. - """ - - api_key: str - base_url: str = "https://api.deepseek.com/v1" - model: str = "deepseek-chat" - temperature: float = 0.7 - max_tokens: int = 4096 - top_p: float = 0.95 - - -class APIModelAdapter: - """Adapter for using API-hosted models in Training-Free GRPO. - - This enables using models like: - - DeepSeek-V3 (deepseek-chat, deepseek-reasoner) - - OpenAI GPT-4o (gpt-4o, gpt-4o-mini) - - Any OpenAI-compatible API - - Benefits over local models: - - No GPU required - - Faster inference (optimized infrastructure) - - Better base models (DeepSeek-V3 is SOTA) - - Lower total cost (no local compute) - - Easy scaling - - Example: - >>> config = APIModelConfig(api_key="sk-xxx", base_url="https://api.deepseek.com/v1", model="deepseek-chat") - >>> adapter = APIModelAdapter(config) - >>> response = adapter.generate("What is 5 + 3?") - >>> print(response) # "8" - """ - - def __init__(self, config: APIModelConfig): - """Initialize API model adapter. - - Args: - config: API model configuration - """ - self.config = config - - try: - from openai import OpenAI - except ImportError: - raise ImportError( - "OpenAI package not found. Install with: pip install openai" - ) - - self.client = OpenAI(api_key=config.api_key, base_url=config.base_url) - - def generate( - self, - prompt: str, - temperature: Optional[float] = None, - max_tokens: Optional[int] = None, - system_prompt: Optional[str] = None, - ) -> str: - """Generate a response from the API model. - - Args: - prompt: User prompt/query - temperature: Sampling temperature (overrides config) - max_tokens: Max tokens to generate (overrides config) - system_prompt: Optional system prompt - - Returns: - response: Generated text - """ - messages = [] - - if system_prompt: - messages.append({"role": "system", "content": system_prompt}) - - messages.append({"role": "user", "content": prompt}) - - response = self.client.chat.completions.create( - model=self.config.model, - messages=messages, - temperature=temperature or self.config.temperature, - max_tokens=max_tokens or self.config.max_tokens, - top_p=self.config.top_p, - ) - - return response.choices[0].message.content - - def generate_batch( - self, - prompts: List[str], - temperature: Optional[float] = None, - max_tokens: Optional[int] = None, - system_prompt: Optional[str] = None, - ) -> List[str]: - """Generate responses for a batch of prompts. - - Note: Currently sequential. For true parallel execution, - use asyncio version or API batching features. - - Args: - prompts: List of prompts - temperature: Sampling temperature (overrides config) - max_tokens: Max tokens to generate (overrides config) - system_prompt: Optional system prompt - - Returns: - responses: List of generated texts - """ - responses = [] - for prompt in prompts: - response = self.generate( - prompt, - temperature=temperature, - max_tokens=max_tokens, - system_prompt=system_prompt, - ) - responses.append(response) - return responses - - def generate_with_experiences( - self, - query: str, - experiences: str, - temperature: Optional[float] = None, - max_tokens: Optional[int] = None, - ) -> str: - """Generate response with experiences injected into prompt. - - This is the core method for Training-Free GRPO rollout generation. - - Args: - query: Problem/query to solve - experiences: Formatted experience library - temperature: Sampling temperature - max_tokens: Max tokens to generate - - Returns: - response: Generated solution/output - """ - # Inject experiences into prompt - if experiences and experiences != "None": - enhanced_prompt = f"""Please solve the problem: -{query} - -When solving problems, you MUST first carefully read and understand the helpful instructions and experiences: -{experiences} - -Now solve the problem step by step.""" - else: - enhanced_prompt = f"""Please solve the problem: -{query} - -Solve the problem step by step.""" - - return self.generate( - enhanced_prompt, temperature=temperature, max_tokens=max_tokens - ) - - def __repr__(self) -> str: - """Return string representation.""" - return f"APIModelAdapter(model={self.config.model}, base_url={self.config.base_url})" - - -class DeepSeekAdapter(APIModelAdapter): - """Convenience wrapper for DeepSeek models. - - Supports: - - deepseek-chat (V3, recommended for Training-Free GRPO) - - deepseek-reasoner (V3 with reasoning traces) - - Example: - >>> adapter = DeepSeekAdapter(api_key="sk-xxx", model="deepseek-chat") - >>> response = adapter.generate("Solve: x + 5 = 10") - """ - - def __init__( - self, api_key: str, model: str = "deepseek-chat", temperature: float = 0.7 - ): - """Initialize DeepSeek adapter. - - Args: - api_key: DeepSeek API key - model: Model name (deepseek-chat or deepseek-reasoner) - temperature: Sampling temperature - """ - config = APIModelConfig( - api_key=api_key, - base_url="https://api.deepseek.com/v1", - model=model, - temperature=temperature, - ) - super().__init__(config) - - -class OpenAIAdapter(APIModelAdapter): - """Convenience wrapper for OpenAI models. - - Supports: - - gpt-4o (latest GPT-4 Omni) - - gpt-4o-mini (smaller, faster, cheaper) - - o1-preview (reasoning model) - - Example: - >>> adapter = OpenAIAdapter(api_key="sk-xxx", model="gpt-4o-mini") - >>> response = adapter.generate("Solve: x + 5 = 10") - """ - - def __init__( - self, api_key: str, model: str = "gpt-4o-mini", temperature: float = 0.7 - ): - """Initialize OpenAI adapter. - - Args: - api_key: OpenAI API key - model: Model name (gpt-4o, gpt-4o-mini, etc.) - temperature: Sampling temperature - """ - config = APIModelConfig( - api_key=api_key, - base_url="https://api.openai.com/v1", - model=model, - temperature=temperature, - ) - super().__init__(config) diff --git a/pkg/hanzoai/grpo/enhanced_api_model_adapter.py b/pkg/hanzoai/grpo/enhanced_api_model_adapter.py deleted file mode 100644 index 2e0e55978..000000000 --- a/pkg/hanzoai/grpo/enhanced_api_model_adapter.py +++ /dev/null @@ -1,402 +0,0 @@ -# Copyright 2025 Zoo Labs Foundation Inc. and the Gym team. -# -# Enhanced API Model Adapter for Training-Free GRPO -# Based on Tencent youtu-agent: https://arxiv.org/abs/2510.08191v1 -# -# 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. - -import os -import time -import asyncio -from typing import Tuple, Optional -from dataclasses import dataclass - - -@dataclass -class EnhancedAPIModelConfig: - """Enhanced configuration for API-hosted models with full feature support. - - Attributes: - api_key: API key (can be set via HANZO_GRPO_API_KEY or DEEPSEEK_API_KEY env var) - base_url: API base URL (default: https://api.deepseek.com/v1) - model: Model name (e.g., "deepseek-chat", "gpt-4o", "o1-preview") - temperature: Sampling temperature - max_tokens: Maximum tokens to generate - top_p: Nucleus sampling parameter - max_retries: Maximum retry attempts - retry_delay: Initial retry delay in seconds (exponential backoff) - timeout: Request timeout in seconds - support_reasoning: Whether model supports reasoning content (e.g., o1 models) - """ - - api_key: str - base_url: str = "https://api.deepseek.com/v1" - model: str = "deepseek-chat" - temperature: float = 0.7 - max_tokens: int = 4096 - top_p: float = 0.95 - max_retries: int = 3 - retry_delay: float = 1.0 - timeout: Optional[float] = None - support_reasoning: bool = False - - @classmethod - def from_env(cls, **kwargs) -> "EnhancedAPIModelConfig": - """Create config from environment variables with overrides. - - Environment variables: - - HANZO_GRPO_API_KEY or DEEPSEEK_API_KEY: API key - - HANZO_GRPO_BASE_URL: API base URL - - HANZO_GRPO_MODEL: Model name - - HANZO_GRPO_MAX_RETRIES: Maximum retries - - HANZO_GRPO_TIMEOUT: Request timeout - - Args: - **kwargs: Override any config parameter - - Returns: - EnhancedAPIModelConfig instance - """ - api_key = ( - kwargs.get("api_key") - or os.getenv("HANZO_GRPO_API_KEY") - or os.getenv("DEEPSEEK_API_KEY") - ) - if not api_key: - raise ValueError( - "API key required. Set HANZO_GRPO_API_KEY or DEEPSEEK_API_KEY env var" - ) - - defaults = { - "api_key": api_key, - "base_url": os.getenv("HANZO_GRPO_BASE_URL", "https://api.deepseek.com/v1"), - "model": os.getenv("HANZO_GRPO_MODEL", "deepseek-chat"), - "max_retries": int(os.getenv("HANZO_GRPO_MAX_RETRIES", "3")), - "timeout": float(os.getenv("HANZO_GRPO_TIMEOUT", "0")) or None, - } - - # Merge with provided kwargs - defaults.update(kwargs) - - return cls(**defaults) - - -class EnhancedAPIModelAdapter: - """Enhanced API model adapter with retry, reasoning, and async support. - - Features: - - Automatic retry with exponential backoff - - Reasoning content support (for o1 models) - - Async generation with timeout - - Environment variable configuration - - Experience injection for Training-Free GRPO - """ - - def __init__(self, config: EnhancedAPIModelConfig): - """Initialize enhanced adapter. - - Args: - config: Enhanced API model configuration - """ - from openai import OpenAI, AsyncOpenAI - - self.config = config - self.client = OpenAI( - api_key=config.api_key, base_url=config.base_url, timeout=config.timeout - ) - self.async_client = AsyncOpenAI( - api_key=config.api_key, base_url=config.base_url, timeout=config.timeout - ) - - def generate( - self, - prompt: str, - temperature: Optional[float] = None, - max_tokens: Optional[int] = None, - return_reasoning: bool = False, - ) -> str | Tuple[str, Optional[str]]: - """Generate response with retry logic. - - Args: - prompt: User prompt - temperature: Override default temperature - max_tokens: Override default max_tokens - return_reasoning: If True, return (response, reasoning) tuple - - Returns: - Response string, or (response, reasoning) tuple if return_reasoning=True - """ - temp = temperature if temperature is not None else self.config.temperature - tokens = max_tokens if max_tokens is not None else self.config.max_tokens - - for attempt in range(self.config.max_retries): - try: - response = self.client.chat.completions.create( - model=self.config.model, - messages=[{"role": "user", "content": prompt}], - temperature=temp, - max_tokens=tokens, - top_p=self.config.top_p, - ) - - response_text = response.choices[0].message.content.strip() - - if return_reasoning and self.config.support_reasoning: - reasoning = getattr( - response.choices[0].message, "reasoning_content", None - ) - return response_text, reasoning - - return response_text - - except Exception as e: - if attempt < self.config.max_retries - 1: - delay = self.config.retry_delay * (2**attempt) - print( - f"Generation failed (attempt {attempt + 1}/{self.config.max_retries}): {e}" - ) - print(f"Retrying in {delay}s...") - time.sleep(delay) - else: - raise Exception( - f"Generation failed after {self.config.max_retries} attempts: {e}" - ) - - async def generate_async( - self, - prompt: str, - temperature: Optional[float] = None, - max_tokens: Optional[int] = None, - return_reasoning: bool = False, - timeout: Optional[float] = None, - ) -> str | Tuple[str, Optional[str]]: - """Generate response asynchronously with timeout. - - Args: - prompt: User prompt - temperature: Override default temperature - max_tokens: Override default max_tokens - return_reasoning: If True, return (response, reasoning) tuple - timeout: Request timeout (overrides config.timeout) - - Returns: - Response string, or (response, reasoning) tuple if return_reasoning=True - """ - temp = temperature if temperature is not None else self.config.temperature - tokens = max_tokens if max_tokens is not None else self.config.max_tokens - request_timeout = timeout or self.config.timeout - - for attempt in range(self.config.max_retries): - try: - - async def make_request(): - response = await self.async_client.chat.completions.create( - model=self.config.model, - messages=[{"role": "user", "content": prompt}], - temperature=temp, - max_tokens=tokens, - top_p=self.config.top_p, - ) - return response - - if request_timeout: - response = await asyncio.wait_for( - make_request(), timeout=request_timeout - ) - else: - response = await make_request() - - response_text = response.choices[0].message.content.strip() - - if return_reasoning and self.config.support_reasoning: - reasoning = getattr( - response.choices[0].message, "reasoning_content", None - ) - return response_text, reasoning - - return response_text - - except asyncio.TimeoutError: - if attempt < self.config.max_retries - 1: - delay = self.config.retry_delay * (2**attempt) - print( - f"Generation timeout (attempt {attempt + 1}/{self.config.max_retries})" - ) - print(f"Retrying in {delay}s...") - await asyncio.sleep(delay) - else: - raise Exception( - f"Generation timeout after {self.config.max_retries} attempts" - ) - except Exception as e: - if attempt < self.config.max_retries - 1: - delay = self.config.retry_delay * (2**attempt) - print( - f"Generation failed (attempt {attempt + 1}/{self.config.max_retries}): {e}" - ) - print(f"Retrying in {delay}s...") - await asyncio.sleep(delay) - else: - raise Exception( - f"Generation failed after {self.config.max_retries} attempts: {e}" - ) - - def generate_with_experiences( - self, - query: str, - experiences: str, - temperature: Optional[float] = None, - max_tokens: Optional[int] = None, - return_reasoning: bool = False, - ) -> str | Tuple[str, Optional[str]]: - """Generate response with experiences injected into prompt. - - Args: - query: User query/problem - experiences: Formatted experiences string - temperature: Override default temperature - max_tokens: Override default max_tokens - return_reasoning: If True, return (response, reasoning) tuple - - Returns: - Response string, or (response, reasoning) tuple if return_reasoning=True - """ - if experiences and experiences != "None": - enhanced_prompt = f"""Please solve the problem: -{query} - -When solving problems, you MUST first carefully read and understand the helpful instructions and experiences: -{experiences} - -Now solve the problem step by step.""" - else: - enhanced_prompt = f"""Please solve the problem: -{query} - -Solve the problem step by step.""" - - return self.generate( - enhanced_prompt, - temperature=temperature, - max_tokens=max_tokens, - return_reasoning=return_reasoning, - ) - - async def generate_with_experiences_async( - self, - query: str, - experiences: str, - temperature: Optional[float] = None, - max_tokens: Optional[int] = None, - return_reasoning: bool = False, - timeout: Optional[float] = None, - ) -> str | Tuple[str, Optional[str]]: - """Generate response with experiences asynchronously. - - Args: - query: User query/problem - experiences: Formatted experiences string - temperature: Override default temperature - max_tokens: Override default max_tokens - return_reasoning: If True, return (response, reasoning) tuple - timeout: Request timeout - - Returns: - Response string, or (response, reasoning) tuple if return_reasoning=True - """ - if experiences and experiences != "None": - enhanced_prompt = f"""Please solve the problem: -{query} - -When solving problems, you MUST first carefully read and understand the helpful instructions and experiences: -{experiences} - -Now solve the problem step by step.""" - else: - enhanced_prompt = f"""Please solve the problem: -{query} - -Solve the problem step by step.""" - - return await self.generate_async( - enhanced_prompt, - temperature=temperature, - max_tokens=max_tokens, - return_reasoning=return_reasoning, - timeout=timeout, - ) - - -class EnhancedDeepSeekAdapter(EnhancedAPIModelAdapter): - """Convenience wrapper for DeepSeek models with enhanced features.""" - - def __init__( - self, - api_key: Optional[str] = None, - model: str = "deepseek-chat", - temperature: float = 0.7, - max_retries: int = 3, - timeout: Optional[float] = None, - ): - """Initialize DeepSeek adapter with env var fallback. - - Args: - api_key: API key (or set DEEPSEEK_API_KEY env var) - model: Model name - temperature: Sampling temperature - max_retries: Maximum retry attempts - timeout: Request timeout - """ - config = EnhancedAPIModelConfig.from_env( - api_key=api_key, - base_url="https://api.deepseek.com/v1", - model=model, - temperature=temperature, - max_retries=max_retries, - timeout=timeout, - ) - super().__init__(config) - - -class EnhancedOpenAIAdapter(EnhancedAPIModelAdapter): - """Convenience wrapper for OpenAI models with enhanced features.""" - - def __init__( - self, - api_key: Optional[str] = None, - model: str = "gpt-4o", - temperature: float = 0.7, - max_retries: int = 3, - timeout: Optional[float] = None, - support_reasoning: bool = False, - ): - """Initialize OpenAI adapter with env var fallback. - - Args: - api_key: API key (or set OPENAI_API_KEY env var) - model: Model name (e.g., "gpt-4o", "o1-preview") - temperature: Sampling temperature - max_retries: Maximum retry attempts - timeout: Request timeout - support_reasoning: Enable for o1 models - """ - config = EnhancedAPIModelConfig( - api_key=api_key or os.getenv("OPENAI_API_KEY", ""), - base_url="https://api.openai.com/v1", - model=model, - temperature=temperature, - max_retries=max_retries, - timeout=timeout, - support_reasoning=support_reasoning, - ) - super().__init__(config) diff --git a/pkg/hanzoai/grpo/enhanced_semantic_extractor.py b/pkg/hanzoai/grpo/enhanced_semantic_extractor.py deleted file mode 100644 index 4409ece3a..000000000 --- a/pkg/hanzoai/grpo/enhanced_semantic_extractor.py +++ /dev/null @@ -1,654 +0,0 @@ -# Copyright 2025 Zoo Labs Foundation Inc. and the Gym team. -# -# Enhanced Semantic Extractor for Training-Free GRPO with Full Feature Parity -# Based on Tencent youtu-agent: https://arxiv.org/abs/2510.08191v1 -# -# 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. - -import os -import copy -import json -import time -import asyncio -from typing import Any, Dict, List, Callable, Optional -from pathlib import Path -from collections import defaultdict -from dataclasses import dataclass -from concurrent.futures import ThreadPoolExecutor, as_completed - -try: - from tqdm import tqdm - - TQDM_AVAILABLE = True -except ImportError: - TQDM_AVAILABLE = False - - # Fallback progress indicator - class tqdm: - def __init__(self, iterable=None, total=None, desc=None, **kwargs): - self.iterable = iterable - self.total = total - self.desc = desc - self.n = 0 - - def __iter__(self): - return iter(self.iterable) if self.iterable else iter([]) - - def update(self, n=1): - self.n += n - if self.desc and self.total: - print(f"{self.desc}: {self.n}/{self.total}", end="\r") - - -@dataclass -class Trajectory: - """Single rollout trajectory with full feature support. - - Attributes: - query: Input query/problem statement - output: Model-generated output/response - reward: Numerical reward score (e.g., 0 for wrong, 1 for correct) - groundtruth: Optional ground truth answer for supervised learning - summary: Optional LLM-generated step-by-step summary - reasoning: Optional reasoning content (for o1 models) - retry_count: Number of retries attempted - task_time: Time taken for this trajectory in seconds - """ - - query: str - output: str - reward: float - groundtruth: Optional[str] = None - summary: Optional[str] = None - reasoning: Optional[str] = None - retry_count: int = 0 - task_time: Optional[float] = None - - -class EnhancedLLMClient: - """Enhanced LLM client with retry logic, reasoning support, and env config. - - Features: - - Automatic retry with exponential backoff - - Reasoning content support (for OpenAI o1 models) - - Environment variable configuration - - Max retries and timeout control - """ - - def __init__( - self, - api_key: Optional[str] = None, - base_url: Optional[str] = None, - model: Optional[str] = None, - max_retries: int = 3, - retry_delay: float = 1.0, - timeout: Optional[float] = None, - ): - """Initialize LLM client with env var fallback. - - Environment variables (if parameters not provided): - - HANZO_GRPO_API_KEY or DEEPSEEK_API_KEY - - HANZO_GRPO_BASE_URL - - HANZO_GRPO_MODEL - """ - from openai import OpenAI - - # Use env vars as fallback - self.api_key = ( - api_key or os.getenv("HANZO_GRPO_API_KEY") or os.getenv("DEEPSEEK_API_KEY") - ) - self.base_url = base_url or os.getenv( - "HANZO_GRPO_BASE_URL", "https://api.deepseek.com/v1" - ) - self.model = model or os.getenv("HANZO_GRPO_MODEL", "deepseek-chat") - self.max_retries = max_retries - self.retry_delay = retry_delay - self.timeout = timeout - - if not self.api_key: - raise ValueError( - "API key required. Set HANZO_GRPO_API_KEY or pass api_key parameter." - ) - - self.client = OpenAI( - api_key=self.api_key, base_url=self.base_url, timeout=timeout - ) - - def chat( - self, - prompt: str, - temperature: float = 0.7, - max_tokens: int = 4096, - return_reasoning: bool = False, - ) -> str | tuple[str, Optional[str]]: - """Call LLM with automatic retry logic. - - Args: - prompt: User prompt - temperature: Sampling temperature - max_tokens: Maximum tokens to generate - return_reasoning: If True, return (response, reasoning) tuple - - Returns: - Response string, or (response, reasoning) tuple if return_reasoning=True - """ - for attempt in range(self.max_retries): - try: - response = self.client.chat.completions.create( - model=self.model, - messages=[{"role": "user", "content": prompt}], - temperature=temperature, - max_tokens=max_tokens, - ) - - response_text = response.choices[0].message.content.strip() - - if return_reasoning: - # For OpenAI o1 models or models with reasoning_content - reasoning = getattr( - response.choices[0].message, "reasoning_content", None - ) - return response_text, reasoning - - return response_text - - except Exception as e: - if attempt < self.max_retries - 1: - delay = self.retry_delay * (2**attempt) # Exponential backoff - print( - f"LLM call failed (attempt {attempt + 1}/{self.max_retries}): {e}" - ) - print(f"Retrying in {delay}s...") - time.sleep(delay) - else: - raise Exception( - f"LLM call failed after {self.max_retries} attempts: {e}" - ) - - -class EnhancedSemanticExtractor: - """Enhanced semantic extractor with full Tencent feature parity. - - New Features: - - File caching for intermediate results (single_rollout_summary.json, etc.) - - Parallel processing with ThreadPoolExecutor - - Automatic retry logic with exponential backoff - - Partial correct filtering (0 < avg_score < 1) - - Progress tracking with tqdm - - Async support for rollouts with timeout - - Reasoning content support for o1 models - - Environment-based configuration - """ - - def __init__( - self, - llm_client: EnhancedLLMClient, - max_operations: int = 3, - max_workers: int = 16, - cache_dir: Optional[str] = None, - enable_caching: bool = True, - enable_parallel: bool = True, - filter_partial_correct: bool = True, - ): - """Initialize enhanced semantic extractor. - - Args: - llm_client: Enhanced LLM client with retry logic - max_operations: Max operations per group critique - max_workers: Max parallel workers for ThreadPoolExecutor - cache_dir: Directory for caching intermediate results - enable_caching: Whether to enable file caching - enable_parallel: Whether to enable parallel processing - filter_partial_correct: Only process groups with 0 < avg_score < 1 - """ - self.llm = llm_client - self.max_operations = max_operations - self.max_workers = max_workers - self.cache_dir = cache_dir - self.enable_caching = enable_caching - self.enable_parallel = enable_parallel - self.filter_partial_correct = filter_partial_correct - - def _get_cache_path(self, filename: str) -> Optional[Path]: - """Get cache file path if caching enabled.""" - if not self.enable_caching or not self.cache_dir: - return None - return Path(self.cache_dir) / filename - - def _load_cache(self, filename: str) -> Optional[Any]: - """Load from cache if exists.""" - cache_path = self._get_cache_path(filename) - if cache_path and cache_path.exists(): - with open(cache_path) as f: - data = json.load(f) - if len(data) > 0: - print(f"Loaded from cache: {cache_path}") - return data - return None - - def _save_cache(self, filename: str, data: Any): - """Save to cache.""" - cache_path = self._get_cache_path(filename) - if cache_path: - cache_path.parent.mkdir(parents=True, exist_ok=True) - with open(cache_path, "w") as f: - json.dump(data, f, indent=2) - print(f"Saved to cache: {cache_path}") - - def summarize_trajectories( - self, - trajectories: List[Trajectory], - use_groundtruth: bool = True, - ) -> Dict[str, List[Trajectory]]: - """Stage 1: Summarize trajectories step-by-step with caching and parallel processing. - - Args: - trajectories: List of trajectories to summarize - use_groundtruth: Whether to use ground truth in summarization - - Returns: - Dict mapping query to list of summarized trajectories - """ - # Try to load from cache - cached = self._load_cache("single_rollout_summary.json") - if cached: - # Convert back to Trajectory objects - result = defaultdict(list) - for query, trajs in cached.items(): - result[query] = [Trajectory(**t) for t in trajs] - return result - - # Group by query - query_to_trajectories = defaultdict(list) - for traj in trajectories: - query_to_trajectories[traj.query].append(traj) - - # Filter to partial correct groups if enabled - all_trajectories_to_process = [] - for trajs in query_to_trajectories.values(): - if self.filter_partial_correct and use_groundtruth: - scores = [t.reward for t in trajs] - avg_score = sum(scores) / len(scores) - if 0 < avg_score < 1: # Partial correct - all_trajectories_to_process.extend(trajs) - else: - all_trajectories_to_process.extend(trajs) - - def process_trajectory(traj: Trajectory) -> Optional[Trajectory]: - """Process single trajectory with LLM.""" - try: - if use_groundtruth and traj.groundtruth: - prompt = f"""An agent system may be provided with some experiences, and then it produces the following trajectory to solve the given problem. Please summarize the trajectory step-by-step: - -1. For each step, describe what action is being taken, and which experience has been used in this step. -2. Given the grading of this rollout and the correct answer, identify and explain any steps that represent detours, errors, or backtracking... -3. Maintain all the core outcome of each step, even if it was part of a flawed process. - - -{traj.output} - - - -This trajectory delivers **{"correct" if traj.reward > 0 else "wrong"}** answer - - -{traj.groundtruth} - -Only return the trajectory summary of each step, e.g., -1. what happened in the first step and the core outcomes -2. what happened in the second step and the core outcomes -3. ...""" - else: - prompt = f"""An agent system produces the following trajectory. Please summarize the trajectory step-by-step: - - -{traj.output} - - -Only return the trajectory summary of each step.""" - - summary = self.llm.chat(prompt) - result = copy.copy(traj) - result.summary = summary - return result - except Exception as e: - print(f"Warning: Failed to summarize trajectory: {e}") - return None - - # Parallel or sequential processing - results = defaultdict(list) - - if self.enable_parallel and self.max_workers > 1: - with ThreadPoolExecutor(max_workers=self.max_workers) as executor: - future_to_traj = { - executor.submit(process_trajectory, traj): traj - for traj in all_trajectories_to_process - } - - iterator = as_completed(future_to_traj) - if TQDM_AVAILABLE: - iterator = tqdm( - iterator, - total=len(all_trajectories_to_process), - desc="Summarizing trajectories", - ) - - for future in iterator: - result = future.result() - if result: - results[result.query].append(result) - else: - # Sequential processing with progress bar - iterator = all_trajectories_to_process - if TQDM_AVAILABLE: - iterator = tqdm(iterator, desc="Summarizing trajectories") - - for traj in iterator: - result = process_trajectory(traj) - if result: - results[result.query].append(result) - - # Save to cache - cache_data = { - query: [ - { - "query": t.query, - "output": t.output, - "reward": t.reward, - "groundtruth": t.groundtruth, - "summary": t.summary, - } - for t in trajs - ] - for query, trajs in results.items() - } - self._save_cache("single_rollout_summary.json", cache_data) - - return results - - def extract_group_advantages( - self, - query_to_summarized_trajectories: Dict[str, List[Trajectory]], - experiences: str, - use_groundtruth: bool = True, - ) -> List[Dict]: - """Stage 2: Extract group advantages from summarized trajectories. - - Args: - query_to_summarized_trajectories: Dict of query to summarized trajectories - experiences: Formatted experiences string - use_groundtruth: Whether to use ground truth - - Returns: - List of critique dictionaries with operations - """ - # Try to load from cache - cached = self._load_cache("single_query_critique.json") - if cached: - return cached - - # Filter to partial correct groups if enabled - all_groups = [] - for trajs in query_to_summarized_trajectories.values(): - if self.filter_partial_correct and use_groundtruth: - scores = [t.reward for t in trajs] - avg_score = sum(scores) / len(scores) - if 0 < avg_score < 1: - all_groups.append(trajs) - else: - all_groups.append(trajs) - - def process_group(trajs: List[Trajectory]) -> Optional[Dict]: - """Process single group of trajectories.""" - try: - query = trajs[0].query - groundtruth = trajs[0].groundtruth if use_groundtruth else None - - formatted_trajs = "\n\n".join( - [ - f"Trajectory {i + 1} (Answer {'correct' if t.reward > 0 else 'wrong'}):\n{t.summary}" - for i, t in enumerate(trajs) - ] - ) - - if use_groundtruth and groundtruth: - prompt = f"""Given the problem and several trajectory summaries with grading, generate semantic experiences that could help future rollouts. - -Problem: {query} -Correct Answer: {groundtruth} - -Current Experiences: -{experiences} - -Trajectories: -{formatted_trajs} - -Based on comparing these trajectories, generate up to {self.max_operations} operations to update experiences. -Return a JSON array of operations with format: -[{{"option": "add", "experience": "..."}}, {{"option": "modify", "modified_from": "G0", "experience": "..."}}, ...] - -Only return the JSON array.""" - else: - prompt = f"""Given the problem and several trajectory summaries, generate semantic experiences. - -Problem: {query} - -Current Experiences: -{experiences} - -Trajectories: -{formatted_trajs} - -Generate up to {self.max_operations} operations to update experiences. -Return JSON array: [{{"option": "add", "experience": "..."}}, ...]""" - - response = self.llm.chat(prompt) - response_clean = response.split("```json")[-1].split("```")[0].strip() - operations = json.loads(response_clean) - - return { - "trajectories": trajs, - "critique": response, - "operations": operations[: self.max_operations], - } - except Exception as e: - print(f"Warning: Failed to extract group advantage: {e}") - return None - - # Parallel or sequential processing - results = [] - - if self.enable_parallel and self.max_workers > 1: - with ThreadPoolExecutor(max_workers=self.max_workers) as executor: - future_to_group = { - executor.submit(process_group, trajs): trajs for trajs in all_groups - } - - iterator = as_completed(future_to_group) - if TQDM_AVAILABLE: - iterator = tqdm( - iterator, - total=len(all_groups), - desc="Extracting group advantages", - ) - - for future in iterator: - result = future.result() - if result: - results.append(result) - else: - iterator = all_groups - if TQDM_AVAILABLE: - iterator = tqdm(iterator, desc="Extracting group advantages") - - for trajs in iterator: - result = process_group(trajs) - if result: - results.append(result) - - # Save to cache - self._save_cache("single_query_critique.json", results) - - return results - - def consolidate_batch( - self, - all_group_operations: List[List[Dict]], - experiences: Dict[str, str], - ) -> Dict[str, str]: - """Stage 3: Consolidate all group operations into final experience updates. - - Args: - all_group_operations: List of operation lists from each group - experiences: Current experience dictionary - - Returns: - Updated experience dictionary - """ - # Try to load from cache - cached = self._load_cache("batch_update.json") - if cached: - return cached.get("new_experiences", experiences) - - print("Batch consolidation") - - # Collect all operations - all_operations = [] - for ops in all_group_operations: - all_operations.extend(ops) - - print(f"- Total operations to process: {len(all_operations)}") - - # Split into add and modify operations - candidate_experiences = copy.deepcopy(experiences) - to_modify = [] - max_id = 0 - - for operation in all_operations: - if operation.get("option") == "modify": - if operation.get("modified_from") in candidate_experiences: - to_modify.append(operation) - elif operation.get("option") == "add": - candidate_experiences[f"C{max_id}"] = operation["experience"] - max_id += 1 - - print(f"- Added experiences: {max_id}") - print(f"- Experiences to modify: {len(to_modify)}") - print(f"- Candidate experiences: {len(candidate_experiences)}") - - # Use LLM to create revision plan - if len(to_modify) > 0: - prompt = f"""Given candidate experiences and modification requests, create a revision plan. - -Candidate Experiences: -{json.dumps(candidate_experiences, indent=2)} - -Modification Requests: -{json.dumps(to_modify, indent=2)} - -Create a revision plan as JSON array with operations: -- {{"option": "modify", "modified_from": "ID", "experience": "..."}} -- {{"option": "merge", "merged_from": ["ID1", "ID2"], "experience": "..."}} - -Return only the JSON array.""" - - try: - response = self.llm.chat(prompt) - response_clean = response.split("```json")[-1].split("```")[0].strip() - revision_plan = json.loads(response_clean) - except Exception as e: - print(f"Warning: Failed to create revision plan: {e}") - revision_plan = [] - else: - revision_plan = [] - - # Apply revision plan - new_experiences = copy.deepcopy(candidate_experiences) - for operation in revision_plan: - try: - if operation["option"] == "modify": - new_experiences[operation["modified_from"]] = operation[ - "experience" - ] - elif operation["option"] == "merge": - for exp_id in operation["merged_from"]: - if exp_id in new_experiences: - del new_experiences[exp_id] - new_experiences[f"C{max_id}"] = operation["experience"] - max_id += 1 - except Exception as e: - print(f"Error applying operation {operation}: {e}") - - print(f"- Final experiences: {len(new_experiences)}") - - # Save to cache - cache_data = { - "operations": all_operations, - "revision_plan": revision_plan, - "new_experiences": new_experiences, - } - self._save_cache("batch_update.json", cache_data) - - # Reassign IDs - final_experiences = { - f"G{i}": exp for i, exp in enumerate(new_experiences.values()) - } - - return final_experiences - - -# Async rollout support with timeout -async def rollout_with_timeout( - generate_func: Callable, - query: str, - timeout: float = 3600, - max_retries: int = 3, - **kwargs, -) -> Optional[str]: - """Execute rollout with timeout and retry support. - - Args: - generate_func: Function to call for generation (must be async-compatible) - query: Query/prompt to generate from - timeout: Timeout in seconds - max_retries: Maximum retry attempts - **kwargs: Additional arguments to pass to generate_func - - Returns: - Generated response or None if failed - """ - for attempt in range(max_retries): - try: - # Wrap sync function in async if needed - if asyncio.iscoroutinefunction(generate_func): - coro = generate_func(query, **kwargs) - else: - coro = asyncio.to_thread(generate_func, query, **kwargs) - - result = await asyncio.wait_for(coro, timeout=timeout) - return result - - except asyncio.TimeoutError: - print(f"Rollout timeout (attempt {attempt + 1}/{max_retries})") - if attempt < max_retries - 1: - await asyncio.sleep(2**attempt) # Exponential backoff - else: - print(f"Rollout failed after {max_retries} timeout attempts") - return None - except Exception as e: - print(f"Rollout error (attempt {attempt + 1}/{max_retries}): {e}") - if attempt < max_retries - 1: - await asyncio.sleep(2**attempt) - else: - print(f"Rollout failed after {max_retries} attempts") - return None diff --git a/pkg/hanzoai/grpo/experience_manager.py b/pkg/hanzoai/grpo/experience_manager.py deleted file mode 100644 index 156fafb12..000000000 --- a/pkg/hanzoai/grpo/experience_manager.py +++ /dev/null @@ -1,201 +0,0 @@ -# Copyright 2025 Zoo Labs Foundation Inc. and the Gym team. -# -# Experience Manager for Training-Free GRPO -# Based on Tencent youtu-agent: https://arxiv.org/abs/2510.08191v1 -# -# 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. - -import json -from typing import Dict, List, Literal -from pathlib import Path - -Operation = Literal["add", "delete", "modify", "merge", "keep"] - - -class ExperienceManager: - """Manages the experience library E for Training-Free GRPO. - - The experience library stores generalizable strategic insights extracted - from trajectory groups. Each experience is a concise (โ‰ค32 words) natural - language statement that guides future problem-solving. - - Example experiences: - - "When solving equations, verify solutions by substitution to catch errors." - - "For optimization problems, check boundary conditions before applying calculus." - - "In probability, use conditional probability when events are dependent." - """ - - def __init__(self, checkpoint_path: str = None): - """Initialize experience manager. - - Args: - checkpoint_path: Path to load/save experiences (JSON format) - """ - self.experiences: Dict[str, str] = {} - self._next_id = 0 - self.checkpoint_path = checkpoint_path - - if checkpoint_path and Path(checkpoint_path).exists(): - self.load(checkpoint_path) - - def add(self, experience: str) -> str: - """Add new experience, return assigned ID. - - Args: - experience: Natural language experience statement (โ‰ค32 words) - - Returns: - exp_id: Assigned experience ID (format: "G{N}") - """ - exp_id = f"G{self._next_id}" - self.experiences[exp_id] = experience - self._next_id += 1 - return exp_id - - def delete(self, exp_id: str) -> bool: - """Delete experience by ID. - - Args: - exp_id: Experience ID to delete - - Returns: - success: True if deleted, False if ID not found - """ - if exp_id in self.experiences: - del self.experiences[exp_id] - return True - return False - - def modify(self, exp_id: str, new_experience: str) -> bool: - """Modify existing experience. - - Args: - exp_id: Experience ID to modify - new_experience: New experience text - - Returns: - success: True if modified, False if ID not found - """ - if exp_id in self.experiences: - self.experiences[exp_id] = new_experience - return True - return False - - def merge(self, exp_ids: List[str], merged_experience: str) -> str: - """Merge multiple experiences into one. - - Deletes the original experiences and creates a new merged one. - - Args: - exp_ids: List of experience IDs to merge - merged_experience: Merged experience text - - Returns: - new_exp_id: ID of the newly created merged experience - """ - # Delete old experiences - for exp_id in exp_ids: - self.delete(exp_id) - # Add merged experience - return self.add(merged_experience) - - def apply_operations(self, operations: List[Dict]) -> None: - """Apply batch of operations from LLM. - - This is the main interface for updating the experience library based - on LLM-extracted semantic advantages. - - Args: - operations: List of operation dictionaries with keys: - - "option": One of ["add", "modify", "delete", "merge", "keep"] - - "experience": New/modified experience text (for add/modify/merge) - - "modified_from": Original experience ID (for modify) - - "delete_id": Experience ID to delete (for delete) - - "merged_from": List of experience IDs to merge (for merge) - - Example: - operations = [ - {"option": "add", "experience": "When solving..."}, - {"option": "modify", "experience": "Updated...", "modified_from": "G17"}, - {"option": "delete", "delete_id": "G5"}, - {"option": "merge", "experience": "Merged...", "merged_from": ["G1", "G3"]} - ] - """ - for op in operations: - option = op.get("option", "keep") - - if option == "add": - self.add(op["experience"]) - - elif option == "delete": - self.delete(op["delete_id"]) - - elif option == "modify": - self.modify(op["modified_from"], op["experience"]) - - elif option == "merge": - self.merge(op["merged_from"], op["experience"]) - - # "keep" option does nothing - - def format_for_prompt(self) -> str: - """Format experiences for injection into prompts. - - Returns: - formatted: Multi-line string with numbered experiences - - Example output: - [G0]. When solving equations, verify solutions by substitution. - [G1]. For optimization, check boundary conditions first. - [G2]. In probability, use conditional probability when dependent. - """ - if not self.experiences: - return "None" - - formatted = [] - for exp_id, exp_text in self.experiences.items(): - formatted.append(f"[{exp_id}]. {exp_text}") - - return "\n".join(formatted) - - def save(self, path: str) -> None: - """Save experiences to JSON file. - - Args: - path: Output file path - """ - Path(path).parent.mkdir(parents=True, exist_ok=True) - - with open(path, "w") as f: - json.dump( - {"experiences": self.experiences, "next_id": self._next_id}, f, indent=2 - ) - - def load(self, path: str) -> None: - """Load experiences from JSON file. - - Args: - path: Input file path - """ - with open(path) as f: - data = json.load(f) - self.experiences = data["experiences"] - self._next_id = data["next_id"] - - def __len__(self) -> int: - """Return number of experiences in library.""" - return len(self.experiences) - - def __repr__(self) -> str: - """Return string representation.""" - return f"ExperienceManager({len(self)} experiences)" diff --git a/pkg/hanzoai/grpo/semantic_extractor.py b/pkg/hanzoai/grpo/semantic_extractor.py deleted file mode 100644 index 390368f3e..000000000 --- a/pkg/hanzoai/grpo/semantic_extractor.py +++ /dev/null @@ -1,411 +0,0 @@ -# Copyright 2025 Zoo Labs Foundation Inc. and the Gym team. -# -# Semantic Extractor for Training-Free GRPO -# Based on Tencent youtu-agent: https://arxiv.org/abs/2510.08191v1 -# -# 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. - -import re -import json -from typing import Any, Dict, List, Optional -from dataclasses import dataclass - - -@dataclass -class Trajectory: - """Single rollout trajectory. - - Attributes: - query: Input query/problem statement - output: Model-generated output/response - reward: Numerical reward score (e.g., 0 for wrong, 1 for correct) - groundtruth: Optional ground truth answer for supervised learning - summary: Optional LLM-generated step-by-step summary - """ - - query: str - output: str - reward: float - groundtruth: Optional[str] = None - summary: Optional[str] = None - - -class SemanticExtractor: - """Extracts semantic advantages from groups of trajectories. - - Implements the 3-stage LLM process from Training-Free GRPO paper: - 1. Trajectory Summarization (Figure 11): Analyze each rollout step-by-step - 2. Group Advantage Extraction (Figure 12): Compare G trajectories to identify patterns - 3. Batch Consolidation (Figure 13): Merge/modify/delete experiences across batch - - This replaces numerical advantages with natural language experiences, - enabling learning without parameter updates. - """ - - def __init__(self, llm_client: Any, max_operations: int = 3): - """Initialize semantic extractor. - - Args: - llm_client: LLM client with .chat() method (e.g., OpenAI, DeepSeek) - max_operations: Max operations per group critique (default: 3) - """ - self.llm = llm_client - self.max_operations = max_operations - - # ========================================================================= - # STAGE 1: Trajectory Summarization (Figure 11) - # ========================================================================= - - def summarize_trajectory( - self, trajectory: Trajectory, use_groundtruth: bool = True - ) -> str: - """Summarize a single trajectory step-by-step. - - This stage analyzes what happened in each step of the trajectory, - identifies which experiences were used, and highlights any errors - or detours that occurred. - - Args: - trajectory: Trajectory to summarize - use_groundtruth: Whether to include ground truth in prompt - - Returns: - summary: Step-by-step analysis of the trajectory - """ - # Determine evaluation status - evaluation = ( - "This trajectory delivers **correct** answer" - if trajectory.reward > 0 - else "This trajectory delivers **wrong** answer" - ) - - # Include groundtruth if available and requested - groundtruth_section = ( - f"\n{trajectory.groundtruth}" - if use_groundtruth and trajectory.groundtruth - else "" - ) - - prompt = f"""An agent system may be provided with some experiences, and then it produces the following trajectory to solve the given problem. Please summarize the trajectory step-by-step: - -1. For each step, describe what action is being taken, and which experience has been used in this step. -2. Given the grading of this rollout and the correct answer, identify and explain any steps that represent detours, errors, or backtracking, highlighting why they might have occurred and what their impact was on the trajectory's progress. -3. Maintain all the core outcome of each step, even if it was part of a flawed process. - - -{trajectory.output} - - - -{evaluation} -{groundtruth_section} - -Only return the trajectory summary of each step, e.g., -1. what happened in the first step and the core outcomes -2. what happened in the second step and the core outcomes -3. ...""" - - response = self.llm.chat(prompt) - return response - - # ========================================================================= - # STAGE 2: Group Advantage Extraction (Figure 12) - # ========================================================================= - - def extract_group_advantage( - self, - trajectories: List[Trajectory], - experiences: str, - use_groundtruth: bool = True, - ) -> List[Dict]: - """Extract semantic advantage from a group of trajectories. - - This stage compares multiple trajectories (both correct and incorrect) - to identify patterns, extract insights, and propose updates to the - experience library. - - Args: - trajectories: List of G trajectories for the same query - experiences: Formatted experience library string - use_groundtruth: Whether to include ground truth in prompt - - Returns: - operations: List of operations to apply to experience library - Example: [ - {"option": "add", "experience": "When solving..."}, - {"option": "modify", "experience": "...", "modified_from": "G17"} - ] - """ - # Check if group has variation (std > 0) - rewards = [t.reward for t in trajectories] - if len(set(rewards)) <= 1: - return [] # Skip homogeneous groups - - # Format trajectories with summaries - formatted_trajectories = [] - for i, traj in enumerate(trajectories): - status = "correct" if traj.reward > 0 else "wrong" - content = traj.summary or traj.output - formatted_trajectories.append( - f"Attempt {i + 1} (Answer {status}):\n{content}" - ) - - trajectories_text = "\n\n".join(formatted_trajectories) - - # Include groundtruth if available - groundtruth_section = ( - f"\n{trajectories[0].groundtruth}" - if use_groundtruth and trajectories[0].groundtruth - else "" - ) - - prompt = f"""An agent system is provided with a set of experiences and has tried to solve the problem multiple times with both successful and wrong solutions. Review these problem-solving attempt and extract generalizable experiences. Follow these steps: - -1. Trajectory Analysis: - - For successful steps: Identify key correct decisions and insights - - For errors: Pinpoint where and why the reasoning went wrong - - Note any important patterns or strategies used/missed - - Review why some trajectories fail? Is there any existing experiences are missed, or experiences do not provide enough guidance? - -2. Update Existing Experiences - - Some trajectories may be correct and others may be wrong, you should ensure there are experiences can help to run correctly - - You have three options: [modify, add, delete] - * modify: You can modify current experiences to make it helpful - * add: You can introduce new experiences to improve future performance - * delete: You can delete existing experiences - - You can update at most {self.max_operations} clear, generalizable lessons for this case - - Before updating each experience, you need to: - * Specify when it would be most relevant - * List key problem features that make this experience applicable - * Identify similar problem patterns where this advice applies - -3. Requirements for each experience that is modified or added. - - Begin with general background with several words in the experience - - Focus on strategic thinking patterns, not specific calculations - - Emphasize decision points that could apply to similar problems - -Please provide reasoning in details under the guidance of the above 3 steps. After the step-by-step reasoning, you will finish by returning in this JSON format as follows: - -```json -[ - {{ - "option": "modify", - "experience": "the modified experience", - "modified_from": "G17" - }}, - {{ - "option": "add", - "experience": "the added experience" - }}, - {{ - "option": "delete", - "delete_id": "G5" - }} -] -``` - -Note that your updated experiences may not need to cover all the options. - - -{trajectories[0].query} - - - -{trajectories_text} -{groundtruth_section} - - -{experiences} -""" - - response = self.llm.chat(prompt) - - # Parse JSON operations - return self._parse_json_operations(response, max_ops=self.max_operations) - - # ========================================================================= - # STAGE 3: Batch Consolidation (Figure 13) - # ========================================================================= - - def consolidate_batch( - self, all_group_operations: List[List[Dict]], experiences: str - ) -> List[Dict]: - """Consolidate all group advantages into final experience updates. - - This stage merges operations from all groups in the batch, ensuring: - - Experiences are โ‰ค32 words - - No redundancy between experiences - - Strategic focus (not specific calculations) - - Generalizable insights - - Args: - all_group_operations: List of operations from each group - experiences: Current formatted experience library - - Returns: - final_operations: Consolidated list of operations to apply - """ - # Flatten all operations - all_ops = [] - for group_ops in all_group_operations: - all_ops.extend(group_ops) - - if not all_ops: - return [] - - prompt = f"""An agent system is provided with a set of experiences and has tried to solve the problem multiple times. From the reflections, some suggestions on the existing experiences have been posed. Your task is to collect and think for the final experience revision plan. Each final experience must satisfy the following requirements: - -1. It must be clear, generalizable lessons for this case, with no more than 32 words -2. Begin with general background with several words in the experience -3. Focus on strategic thinking patterns, not specific calculations -4. Emphasize decision points that could apply to similar problems -5. Avoid repeating saying similar experience in multiple different experiences - - -{experiences} - - - -{json.dumps(all_ops, indent=2)} - - -Please provide reasoning in each of the suggestions, and think for how to update existing experiences. You have three update options: [modify, merge, delete] - -- modify: You can modify current experiences to make it helpful -- merge: You can merge some similar experiences into a more general forms to reduce duplication -- delete: You can delete an experience - -After generating the step-by-step reasoning, you need to give the final experience revision details by returning in this JSON format as follows: - -```json -[ - {{ - "option": "modify", - "experience": "the modified experience", - "modified_from": "G17" - }}, - {{ - "option": "merge", - "experience": "the merged experience", - "merged_from": ["C1", "C3", "S4"] - }}, - {{ - "option": "delete", - "delete_id": "G5" - }} -] -```""" - - response = self.llm.chat(prompt) - - # Parse JSON operations - return self._parse_json_operations(response) - - # ========================================================================= - # Helper Methods - # ========================================================================= - - def _parse_json_operations( - self, response: str, max_ops: Optional[int] = None - ) -> List[Dict]: - """Parse JSON operations from LLM response. - - Extracts JSON block from markdown code fence and validates format. - - Args: - response: LLM response text - max_ops: Optional limit on number of operations - - Returns: - operations: List of operation dictionaries - """ - # Extract JSON block from markdown code fence - json_match = re.search(r"```json\s*(.*?)\s*```", response, re.DOTALL) - - if json_match: - try: - operations = json.loads(json_match.group(1)) - if max_ops: - return operations[:max_ops] - return operations - except json.JSONDecodeError as e: - # Log error and return empty list - print(f"JSON parse error: {e}") - return [] - - # Also try to find plain JSON arrays (without code fence) - try: - # Look for array pattern - array_match = re.search(r"\[\s*\{.*?\}\s*\]", response, re.DOTALL) - if array_match: - operations = json.loads(array_match.group(0)) - if max_ops: - return operations[:max_ops] - return operations - except json.JSONDecodeError: - pass - - return [] - - -class LLMClient: - """Simple wrapper for LLM API clients. - - Provides a unified .chat() interface for various LLM providers. - Supports OpenAI-compatible APIs (OpenAI, DeepSeek, etc.). - """ - - def __init__( - self, - api_key: str, - base_url: str = "https://api.deepseek.com/v1", - model: str = "deepseek-chat", - ): - """Initialize LLM client. - - Args: - api_key: API key for the LLM service - base_url: Base URL for the API endpoint - model: Model name to use (default: deepseek-chat) - """ - try: - from openai import OpenAI - except ImportError: - raise ImportError( - "OpenAI package not found. Install with: pip install openai" - ) - - self.client = OpenAI(api_key=api_key, base_url=base_url) - self.model = model - - def chat( - self, prompt: str, temperature: float = 0.7, max_tokens: int = 4096 - ) -> str: - """Send chat request to LLM. - - Args: - prompt: User prompt - temperature: Sampling temperature (default: 0.7) - max_tokens: Max tokens to generate (default: 4096) - - Returns: - response: LLM response text - """ - response = self.client.chat.completions.create( - model=self.model, - messages=[{"role": "user", "content": prompt}], - temperature=temperature, - max_tokens=max_tokens, - ) - - return response.choices[0].message.content diff --git a/pkg/hanzoai/lib/.keep b/pkg/hanzoai/lib/.keep deleted file mode 100644 index f56e0fdb3..000000000 --- a/pkg/hanzoai/lib/.keep +++ /dev/null @@ -1,3 +0,0 @@ -# Hanzo AI SDK - -This directory can be used to store custom files to expand the SDK. \ No newline at end of file diff --git a/pkg/hanzoai/llm_client.py b/pkg/hanzoai/llm_client.py deleted file mode 100644 index cadd83de1..000000000 --- a/pkg/hanzoai/llm_client.py +++ /dev/null @@ -1,241 +0,0 @@ -""" -Simple LLM Client for Hanzo AI - -This provides a simpler interface for common LLM operations, -complementing the full-featured auto-generated client. -""" - -import os -from typing import Optional - -# We can import and use llm as a dependency -try: - import llm - - LLM_AVAILABLE = True -except ImportError: - LLM_AVAILABLE = False - llm = None - -from ._client import Hanzo - -# Hanzo AI specific configuration -HANZO_API_BASE = "https://api.hanzo.ai/v1" -IAM_BASE = "https://hanzo.id" - -# Check for Hanzo API key -HANZO_API_KEY = os.getenv("HANZO_API_KEY") - - -class SimpleLLMClient: - """ - Simple LLM client for Hanzo AI with llm compatibility. - - This provides a simpler interface that's compatible with the - patterns from the llm/hanzoai package, while using the - full-featured client under the hood. - - Example: - >>> from hanzoai.llm_client import SimpleLLMClient - >>> client = SimpleLLMClient(api_key="your-api-key") - >>> response = client.completion(model="gpt-4", messages=[{"role": "user", "content": "Hello!"}]) - """ - - def __init__( - self, - api_key: Optional[str] = None, - api_base: Optional[str] = None, - iam_token: Optional[str] = None, - auto_login: bool = False, - ): - """ - Initialize Simple LLM client. - - Args: - api_key: Hanzo API key. If not provided, uses HANZO_API_KEY env var - api_base: API base URL. Defaults to https://api.hanzo.ai/v1 - iam_token: IAM token for authentication - auto_login: Automatically login with IAM if no API key provided - """ - self.api_key = api_key or HANZO_API_KEY - self.api_base = api_base or HANZO_API_BASE - self.iam_token = iam_token - - # Handle IAM token authentication - if self.iam_token: - # Format IAM token for proxy server - self.api_key = f"Bearer iam_{self.iam_token}" - elif not self.api_key and auto_login: - # No API key, attempt IAM login - self._iam_login() - - # Create the underlying Hanzo client - self._client = Hanzo(api_key=self.api_key, base_url=self.api_base) - - # Configure llm if available - if LLM_AVAILABLE and llm: - llm.api_base = self.api_base - llm.drop_params = True # Be permissive with params - if self.api_key: - llm.api_key = self.api_key - os.environ["OPENAI_API_KEY"] = self.api_key # For OpenAI compatibility - - def _iam_login(self): - """Login using IAM (Casdoor) authentication.""" - try: - import webbrowser - from urllib.parse import urlencode - - # IAM OAuth2 flow - client_id = os.getenv("IAM_CLIENT_ID", "hanzoai-sdk") - redirect_uri = "http://localhost:8080/callback" - - # Build authorization URL - auth_params = { - "client_id": client_id, - "response_type": "code", - "redirect_uri": redirect_uri, - "scope": "openid profile email", - "state": "hanzoai-login", - } - auth_url = ( - f"{IAM_BASE}/oauth/authorize?{urlencode(auth_params)}" - ) - - print(f"Opening browser for Hanzo AI login...") - print(f"If browser doesn't open, visit: {auth_url}") - webbrowser.open(auth_url) - - # TODO: Implement local server to catch callback - # For now, prompt for manual token entry - print("\nAfter logging in, copy your API key from the dashboard.") - self.api_key = input("Enter your Hanzo API key: ").strip() - - if self.api_key: - # Save to environment for future use - os.environ["HANZO_API_KEY"] = self.api_key - print("โœ… Authentication successful!") - - # Update the client - self._client = Hanzo(api_key=self.api_key, base_url=self.api_base) - - except Exception as e: - print(f"IAM login failed: {e}") - print( - "Please set HANZO_API_KEY environment variable or pass api_key parameter" - ) - - def completion(self, **kwargs): - """ - Create a completion using Hanzo AI. - - If llm is available, uses llm for compatibility. - Otherwise uses the native client. - """ - if LLM_AVAILABLE and llm: - # Use llm for maximum compatibility - kwargs.setdefault("api_base", self.api_base) - if self.iam_token: - kwargs.setdefault("api_key", f"Bearer iam_{self.iam_token}") - elif self.api_key: - kwargs.setdefault("api_key", self.api_key) - return llm.completion(**kwargs) - else: - # Use native client - return self._client.chat.completions.create(**kwargs) - - def embedding(self, **kwargs): - """Create embeddings using Hanzo AI.""" - if LLM_AVAILABLE and llm: - kwargs.setdefault("api_base", self.api_base) - if self.api_key: - kwargs.setdefault("api_key", self.api_key) - return llm.embedding(**kwargs) - else: - return self._client.embeddings.create(**kwargs) - - def image_generation(self, **kwargs): - """Generate images using Hanzo AI.""" - if LLM_AVAILABLE and llm: - kwargs.setdefault("api_base", self.api_base) - if self.api_key: - kwargs.setdefault("api_key", self.api_key) - return llm.image_generation(**kwargs) - else: - return self._client.images.generations.create(**kwargs) - - def list_models(self): - """List available models on Hanzo AI.""" - return self._client.models.list() - - def list_mcp_tools(self): - """List available MCP tools on Hanzo AI.""" - # This might need a custom endpoint - import requests - - headers = {} - if self.api_key: - headers["Authorization"] = f"Bearer {self.api_key}" - response = requests.get(f"{self.api_base}/mcp/tools/list", headers=headers) - return response.json() - - -# For OpenAI drop-in compatibility -class OpenAICompatibleClient: - """OpenAI-compatible client that uses Hanzo AI.""" - - def __init__(self, api_key: Optional[str] = None, base_url: Optional[str] = None): - self.api_key = api_key or HANZO_API_KEY - self.base_url = base_url or HANZO_API_BASE - self._client = SimpleLLMClient(api_key=self.api_key, api_base=self.base_url) - - @property - def chat(self): - return self - - @property - def completions(self): - return self - - def create(self, **kwargs): - """OpenAI-compatible completion.""" - return self._client.completion(**kwargs) - - -# Convenience functions -def completion(**kwargs): - """ - Quick completion using Hanzo AI with automatic authentication. - - Example: - >>> from hanzoai.llm_client import completion - >>> response = completion(model="gpt-4", messages=[{"role": "user", "content": "Hello!"}]) - """ - # Use Hanzo API base by default - kwargs.setdefault("api_base", HANZO_API_BASE) - - # Use Hanzo API key if available - if HANZO_API_KEY: - kwargs.setdefault("api_key", HANZO_API_KEY) - - if LLM_AVAILABLE and llm: - return llm.completion(**kwargs) - else: - client = SimpleLLMClient() - return client.completion(**kwargs) - - -def set_api_key(api_key: str): - """Set the Hanzo API key globally.""" - global HANZO_API_KEY - HANZO_API_KEY = api_key - os.environ["HANZO_API_KEY"] = api_key - if LLM_AVAILABLE and llm: - llm.api_key = api_key - os.environ["OPENAI_API_KEY"] = api_key - - -# Initialize default client if API key is available -default_client = None -if HANZO_API_KEY: - default_client = SimpleLLMClient() diff --git a/pkg/hanzoai/mcp.py b/pkg/hanzoai/mcp.py deleted file mode 100644 index bc046d15e..000000000 --- a/pkg/hanzoai/mcp.py +++ /dev/null @@ -1,433 +0,0 @@ -"""Hanzo AI MCP (Model Context Protocol) module. - -This module provides access to the hanzo-mcp SDK for building -MCP servers and clients with extensive tool support. - -Includes JSON-RPC 2.0 stdio and HTTP transport clients for -communicating with MCP servers. -""" - -from __future__ import annotations - -import asyncio -import json -import re -from dataclasses import dataclass, field -from typing import Any - -import httpx - -try: - # Try to import hanzo-mcp if installed - from hanzo_mcp import ( - # Base classes - BaseTool, - ToolRegistry, - # Server creation - HanzoMCPServer, - # Permissions - PermissionManager, - # Version - __version__ as mcp_version, - create_server, - get_git_tools, - get_agent_tools, - get_shell_tools, - get_memory_tools, - get_search_tools, - get_jupyter_tools, - # Tool registration - register_all_tools, - # Tool categories - get_filesystem_tools, - register_all_prompts, - ) - - MCP_AVAILABLE = True -except ImportError: - MCP_AVAILABLE = False - - # Provide a helpful error message - def _mcp_not_installed(*args, **kwargs): - raise ImportError( - "hanzo-mcp is not installed. Install it with: pip install hanzo-mcp" - ) - - # Create placeholder classes/functions - HanzoMCPServer = create_server = _mcp_not_installed - register_all_tools = register_all_prompts = _mcp_not_installed - get_filesystem_tools = get_shell_tools = get_agent_tools = _mcp_not_installed - get_search_tools = get_jupyter_tools = get_git_tools = _mcp_not_installed - get_memory_tools = _mcp_not_installed - BaseTool = ToolRegistry = _mcp_not_installed - PermissionManager = _mcp_not_installed - mcp_version = "not installed" - - -def create_mcp_server(name: str = "hanzo-mcp", allowed_paths: list = None, **kwargs): - """Create a new MCP server. - - Args: - name: Name of the server - allowed_paths: List of allowed file paths - **kwargs: Additional server options - - Returns: - HanzoMCPServer instance - """ - if not MCP_AVAILABLE: - raise ImportError( - "hanzo-mcp is not installed. Install it with: pip install hanzo-mcp" - ) - - return create_server(name=name, allowed_paths=allowed_paths, **kwargs) - - -def run_mcp_server(name: str = "hanzo-mcp", transport: str = "stdio", **kwargs): - """Run an MCP server. - - Args: - name: Name of the server - transport: Transport protocol ("stdio" or "sse") - **kwargs: Additional server options - """ - if not MCP_AVAILABLE: - raise ImportError( - "hanzo-mcp is not installed. Install it with: pip install hanzo-mcp" - ) - - server = create_server(name=name, **kwargs) - server.run(transport=transport) - - -# --------------------------------------------------------------------------- -# MCP tool name normalization -# --------------------------------------------------------------------------- - -_NORM_RE = re.compile(r"[^a-zA-Z0-9_\-]") - - -def _collapse_underscores(value: str) -> str: - """Collapse consecutive underscores into a single underscore.""" - out: list[str] = [] - last_was_underscore = False - for ch in value: - if ch == "_": - if not last_was_underscore: - out.append(ch) - last_was_underscore = True - else: - out.append(ch) - last_was_underscore = False - return "".join(out) - - -def normalize_mcp_name(name: str) -> str: - """Normalize an MCP server/tool name for the mcp__ prefix convention. - - Exact port of claw-code's ``normalize_name_for_mcp`` (mcp.rs): - - Replace any char that is not alphanumeric, underscore, or hyphen with ``_``. - - If *name* starts with ``"claude.ai "``, also collapse consecutive - underscores and strip leading/trailing underscores. - - Non-claude.ai names keep hyphens and trailing underscores as-is. - """ - normalized = _NORM_RE.sub("_", name) - if name.startswith("claude.ai "): - normalized = _collapse_underscores(normalized).strip("_") - return normalized - - -def mcp_tool_name(server_name: str, tool_name: str) -> str: - """Build the canonical mcp__server__tool name.""" - return f"mcp__{normalize_mcp_name(server_name)}__{normalize_mcp_name(tool_name)}" - - -# --------------------------------------------------------------------------- -# JSON-RPC 2.0 protocol types -# --------------------------------------------------------------------------- - - -class MCPClientError(Exception): - """Raised on MCP client transport or protocol errors.""" - - -@dataclass -class JsonRpcError: - code: int - message: str - data: Any = None - - @classmethod - def from_dict(cls, d: dict) -> JsonRpcError: - return cls(code=d["code"], message=d["message"], data=d.get("data")) - - -@dataclass -class JsonRpcRequest: - method: str - params: dict[str, Any] = field(default_factory=dict) - id: int = 0 - - def to_dict(self) -> dict[str, Any]: - return {"jsonrpc": "2.0", "method": self.method, "params": self.params, "id": self.id} - - def to_line(self) -> bytes: - return json.dumps(self.to_dict(), separators=(",", ":")).encode() + b"\n" - - -@dataclass -class JsonRpcResponse: - id: int - result: Any = None - error: JsonRpcError | None = None - - @classmethod - def from_dict(cls, d: dict) -> JsonRpcResponse: - err = None - if "error" in d and d["error"] is not None: - err = JsonRpcError.from_dict(d["error"]) - return cls(id=d.get("id", 0), result=d.get("result"), error=err) - - -# --------------------------------------------------------------------------- -# Stdio transport MCP client -# --------------------------------------------------------------------------- - -_MCP_PROTOCOL_VERSION = "2024-11-05" - -_INIT_PARAMS: dict[str, Any] = { - "protocolVersion": _MCP_PROTOCOL_VERSION, - "capabilities": {}, - "clientInfo": {"name": "hanzoai-python", "version": "1.0.0"}, -} - - -class MCPClient: - """MCP client that communicates with a server subprocess over stdio JSON-RPC.""" - - def __init__( - self, - server_command: list[str], - env: dict[str, str] | None = None, - ) -> None: - self.server_command = server_command - self.env = env - self._process: asyncio.subprocess.Process | None = None - self._next_id: int = 1 - self.server_info: dict[str, Any] | None = None - self.capabilities: dict[str, Any] | None = None - - def _require_connected(self) -> None: - if self._process is None or self._process.stdin is None: - raise MCPClientError("not connected - call connect() first") - - async def _send(self, method: str, params: dict[str, Any] | None = None) -> Any: - self._require_connected() - req = JsonRpcRequest(method=method, params=params or {}, id=self._next_id) - self._next_id += 1 - - stdin = self._process.stdin - stdout = self._process.stdout - assert stdin is not None and stdout is not None - - stdin.write(req.to_line()) - await stdin.drain() - - try: - raw = await asyncio.wait_for(stdout.readline(), timeout=30.0) - except asyncio.TimeoutError: - raise MCPClientError("server did not respond within 30 seconds") from None - if not raw: - raise MCPClientError("server closed stdout unexpectedly") - if len(raw) > 10_485_760: # 10 MB guard - raise MCPClientError("server response exceeded 10 MB limit") - - resp = JsonRpcResponse.from_dict(json.loads(raw)) - if resp.error is not None: - raise MCPClientError(f"JSON-RPC error {resp.error.code}: {resp.error.message}") - return resp.result - - async def connect(self) -> None: - """Spawn the server process and perform the MCP initialize handshake.""" - try: - self._process = await asyncio.create_subprocess_exec( - *self.server_command, - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - env=self.env, - ) - except (FileNotFoundError, PermissionError, OSError) as exc: - raise MCPClientError(f"failed to start server process: {exc}") from exc - - result = await self._send("initialize", _INIT_PARAMS) - self.server_info = result.get("serverInfo") - self.capabilities = result.get("capabilities") - - async def disconnect(self) -> None: - """Terminate the server process.""" - proc = self._process - if proc is None: - return - self._process = None - if proc.stdin is not None: - proc.stdin.close() - try: - proc.terminate() - await asyncio.wait_for(proc.wait(), timeout=5.0) - except (ProcessLookupError, asyncio.TimeoutError): - proc.kill() - await proc.wait() - - async def list_tools(self) -> list[dict[str, Any]]: - """Fetch all tools, following pagination cursors.""" - self._require_connected() - tools: list[dict[str, Any]] = [] - cursor: str | None = None - while True: - params: dict[str, Any] = {} - if cursor is not None: - params["cursor"] = cursor - result = await self._send("tools/list", params) - tools.extend(result.get("tools", [])) - cursor = result.get("nextCursor") - if not cursor: - break - return tools - - async def call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> dict[str, Any]: - """Call a tool on the server.""" - self._require_connected() - params: dict[str, Any] = {"name": name} - if arguments: - params["arguments"] = arguments - return await self._send("tools/call", params) - - async def list_resources(self) -> list[dict[str, Any]]: - """Fetch all resources.""" - self._require_connected() - result = await self._send("resources/list") - return result.get("resources", []) - - async def __aenter__(self) -> MCPClient: - await self.connect() - return self - - async def __aexit__(self, *exc: object) -> None: - await self.disconnect() - - -# --------------------------------------------------------------------------- -# HTTP transport MCP client -# --------------------------------------------------------------------------- - - -class MCPHttpClient: - """MCP client that communicates with a server over HTTP POST (JSON-RPC).""" - - def __init__( - self, - url: str, - headers: dict[str, str] | None = None, - timeout: float = 30.0, - ) -> None: - self.url = url - self._client = httpx.AsyncClient( - headers=headers or {}, - timeout=httpx.Timeout(timeout), - ) - self._next_id: int = 1 - self.server_info: dict[str, Any] | None = None - self.capabilities: dict[str, Any] | None = None - - async def _send(self, method: str, params: dict[str, Any] | None = None) -> Any: - req = JsonRpcRequest(method=method, params=params or {}, id=self._next_id) - self._next_id += 1 - - resp = await self._client.post(self.url, json=req.to_dict()) - resp.raise_for_status() - body = resp.json() - - rpc_resp = JsonRpcResponse.from_dict(body) - if rpc_resp.error is not None: - raise MCPClientError(f"JSON-RPC error {rpc_resp.error.code}: {rpc_resp.error.message}") - return rpc_resp.result - - async def connect(self) -> None: - """Perform the MCP initialize handshake over HTTP.""" - result = await self._send("initialize", _INIT_PARAMS) - self.server_info = result.get("serverInfo") - self.capabilities = result.get("capabilities") - - async def disconnect(self) -> None: - """Close the HTTP client.""" - await self._client.aclose() - - async def list_tools(self) -> list[dict[str, Any]]: - tools: list[dict[str, Any]] = [] - cursor: str | None = None - while True: - params: dict[str, Any] = {} - if cursor is not None: - params["cursor"] = cursor - result = await self._send("tools/list", params) - tools.extend(result.get("tools", [])) - cursor = result.get("nextCursor") - if not cursor: - break - return tools - - async def call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> dict[str, Any]: - params: dict[str, Any] = {"name": name} - if arguments: - params["arguments"] = arguments - return await self._send("tools/call", params) - - async def list_resources(self) -> list[dict[str, Any]]: - result = await self._send("resources/list") - return result.get("resources", []) - - async def __aenter__(self) -> MCPHttpClient: - await self.connect() - return self - - async def __aexit__(self, *exc: object) -> None: - await self.disconnect() - - -__all__ = [ - # Server - "HanzoMCPServer", - "create_server", - "create_mcp_server", - "run_mcp_server", - # Tools - "register_all_tools", - "register_all_prompts", - "get_filesystem_tools", - "get_shell_tools", - "get_agent_tools", - "get_search_tools", - "get_jupyter_tools", - "get_git_tools", - "get_memory_tools", - # Base classes - "BaseTool", - "ToolRegistry", - # Permissions - "PermissionManager", - # Protocol types - "JsonRpcRequest", - "JsonRpcResponse", - "JsonRpcError", - "MCPClientError", - # Clients - "MCPClient", - "MCPHttpClient", - # Name helpers - "normalize_mcp_name", - "mcp_tool_name", - # Status - "MCP_AVAILABLE", - "mcp_version", -] diff --git a/pkg/hanzoai/protocols.py b/pkg/hanzoai/protocols.py deleted file mode 100644 index 45f1027d3..000000000 --- a/pkg/hanzoai/protocols.py +++ /dev/null @@ -1,437 +0,0 @@ -"""Protocol abstractions for pluggable tool execution, permissions, and conversation runtime. - -Ported from claw-code's trait-based architecture to enable dependency injection -and testing without network calls. -""" - -from __future__ import annotations - -from collections.abc import Callable -from dataclasses import dataclass, field -from enum import Enum, auto -from typing import Any, Protocol, runtime_checkable - - -# --------------------------------------------------------------------------- -# Tool execution -# --------------------------------------------------------------------------- - -@runtime_checkable -class ToolExecutor(Protocol): - """Execute a named tool with a JSON-encoded input string.""" - - def execute(self, tool_name: str, input: str) -> str: ... - - -# --------------------------------------------------------------------------- -# Permissions -# --------------------------------------------------------------------------- - -class PermissionMode(Enum): - Allow = auto() - Deny = auto() - Prompt = auto() - - -@dataclass(frozen=True, slots=True) -class PermissionRequest: - tool_name: str - input: str - - -@dataclass(frozen=True, slots=True) -class PermissionOutcome: - allowed: bool - reason: str = "" - - @classmethod - def allow(cls) -> PermissionOutcome: - return cls(allowed=True) - - @classmethod - def deny(cls, reason: str) -> PermissionOutcome: - return cls(allowed=False, reason=reason) - - -# Named constructors matching Rust enum variants. -PERMISSION_ALLOW = PermissionOutcome(allowed=True) - - -@runtime_checkable -class PermissionPrompter(Protocol): - """Interactively decide whether a tool invocation is allowed.""" - - def decide(self, request: PermissionRequest) -> PermissionOutcome: ... - - -class PermissionPolicy: - """Default-mode + per-tool override permission policy. - - Mirrors the Rust ``PermissionPolicy`` struct: a default mode with an - ordered map of per-tool overrides. - """ - - __slots__ = ("default_mode", "_tool_modes") - - def __init__( - self, - default_mode: PermissionMode = PermissionMode.Prompt, - tool_modes: dict[str, PermissionMode] | None = None, - ) -> None: - self.default_mode = default_mode - # Sorted dict to match Rust BTreeMap ordering. - self._tool_modes: dict[str, PermissionMode] = dict( - sorted((tool_modes or {}).items()) - ) - - def with_tool_mode(self, name: str, mode: PermissionMode) -> PermissionPolicy: - """Fluent builder: set a per-tool override and return self for chaining.""" - self._tool_modes[name] = mode - self._tool_modes = dict(sorted(self._tool_modes.items())) - return self - - def mode_for(self, tool_name: str) -> PermissionMode: - return self._tool_modes.get(tool_name, self.default_mode) - - def authorize( - self, - tool_name: str, - input: str, - prompter: PermissionPrompter | None = None, - ) -> PermissionOutcome: - mode = self.mode_for(tool_name) - if mode is PermissionMode.Allow: - return PermissionOutcome.allow() - if mode is PermissionMode.Deny: - return PermissionOutcome.deny(f"tool '{tool_name}' denied by policy") - # Prompt - if prompter is None: - return PermissionOutcome.deny("no prompter available for interactive decision") - return prompter.decide(PermissionRequest(tool_name=tool_name, input=input)) - - -# --------------------------------------------------------------------------- -# API client -# --------------------------------------------------------------------------- - -@dataclass(slots=True) -class TokenUsage: - input_tokens: int = 0 - output_tokens: int = 0 - cache_creation_input_tokens: int = 0 - cache_read_input_tokens: int = 0 - - def __iadd__(self, other: TokenUsage) -> TokenUsage: - self.input_tokens += other.input_tokens - self.output_tokens += other.output_tokens - self.cache_creation_input_tokens += other.cache_creation_input_tokens - self.cache_read_input_tokens += other.cache_read_input_tokens - return self - - def total_tokens(self) -> int: - return ( - self.input_tokens - + self.output_tokens - + self.cache_creation_input_tokens - + self.cache_read_input_tokens - ) - - def to_dict(self) -> dict[str, int]: - return { - "input_tokens": self.input_tokens, - "output_tokens": self.output_tokens, - "cache_creation_input_tokens": self.cache_creation_input_tokens, - "cache_read_input_tokens": self.cache_read_input_tokens, - } - - @staticmethod - def from_dict(d: dict[str, Any]) -> TokenUsage: - return TokenUsage( - input_tokens=d.get("input_tokens", 0), - output_tokens=d.get("output_tokens", 0), - cache_creation_input_tokens=d.get("cache_creation_input_tokens", 0), - cache_read_input_tokens=d.get("cache_read_input_tokens", 0), - ) - - -@dataclass(frozen=True, slots=True) -class ModelPricing: - """Per-token prices in USD.""" - input_price_per_token: float - output_price_per_token: float - cache_creation_price_per_token: float = 0.0 - cache_read_price_per_token: float = 0.0 - - def cost(self, usage: TokenUsage) -> float: - return ( - usage.input_tokens * self.input_price_per_token - + usage.output_tokens * self.output_price_per_token - + usage.cache_creation_input_tokens * self.cache_creation_price_per_token - + usage.cache_read_input_tokens * self.cache_read_price_per_token - ) - - -class UsageTracker: - """Accumulates per-turn and cumulative token usage.""" - - __slots__ = ("_turns",) - - def __init__(self) -> None: - self._turns: list[TokenUsage] = [] - - def record(self, usage: TokenUsage) -> None: - self._turns.append(usage) - - @property - def turns(self) -> int: - """Number of recorded turns. Matches Rust ``pub fn turns(&self) -> u32``.""" - return len(self._turns) - - def turns_list(self) -> list[TokenUsage]: - """Return a copy of per-turn usage records.""" - return list(self._turns) - - def cumulative_usage(self) -> TokenUsage: - total = TokenUsage() - for t in self._turns: - total += t - return total - - def cost(self, pricing: ModelPricing) -> float: - return pricing.cost(self.cumulative_usage()) - - -# --------------------------------------------------------------------------- -# API transport types -# --------------------------------------------------------------------------- - -@dataclass(frozen=True, slots=True) -class AssistantEvent: - """Event from the assistant stream. - - ``kind`` uses string values matching Rust enum variant names: - ``"text_delta"``, ``"tool_use"``, ``"usage"``, ``"message_stop"``. - """ - kind: str - text: str = "" - tool_use_id: str = "" - tool_name: str = "" - tool_input: str = "" - usage: TokenUsage | None = None - - -@dataclass(slots=True) -class ApiRequest: - system_prompt: list[str] - messages: list[dict] - - -@runtime_checkable -class ApiClient(Protocol): - """Send a conversation request and receive a stream of events.""" - - def stream(self, request: ApiRequest) -> list[AssistantEvent]: ... - - -# --------------------------------------------------------------------------- -# Turn summary -# --------------------------------------------------------------------------- - -@dataclass(slots=True) -class TurnSummary: - assistant_messages: list[dict] = field(default_factory=list) - tool_results: list[dict] = field(default_factory=list) - usage: TokenUsage = field(default_factory=TokenUsage) - iterations: int = 0 - - -# --------------------------------------------------------------------------- -# Static tool executor (test helper) -# --------------------------------------------------------------------------- - -class StaticToolExecutor: - """Register handlers by name. Useful for testing.""" - - __slots__ = ("_handlers",) - - def __init__(self) -> None: - self._handlers: dict[str, Callable[[str], str]] = {} - - def register(self, name: str, handler: Callable[[str], str]) -> None: - self._handlers[name] = handler - - def execute(self, tool_name: str, input: str) -> str: - handler = self._handlers.get(tool_name) - if handler is None: - raise KeyError(f"no handler registered for tool '{tool_name}'") - return handler(input) - - -# --------------------------------------------------------------------------- -# Conversation runtime -# --------------------------------------------------------------------------- - -_DEFAULT_MAX_ITERATIONS = 16 - - -class ConversationRuntime: - """Orchestrate turn execution over an API client and tool executor. - - Generic over any ``ApiClient`` and ``ToolExecutor`` implementations, - mirroring the Rust ``ConversationRuntime`` generic struct. - """ - - __slots__ = ( - "session", - "api_client", - "tool_executor", - "permission_policy", - "system_prompt", - "max_iterations", - "usage_tracker", - ) - - def __init__( - self, - *, - session: list[dict], - api_client: ApiClient, - tool_executor: ToolExecutor, - permission_policy: PermissionPolicy, - system_prompt: list[str] | None = None, - max_iterations: int = _DEFAULT_MAX_ITERATIONS, - ) -> None: - self.session = session - self.api_client = api_client - self.tool_executor = tool_executor - self.permission_policy = permission_policy - self.system_prompt = system_prompt or [] - self.max_iterations = max_iterations - self.usage_tracker = UsageTracker() - - # ---- public API ------------------------------------------------------- - - def run_turn( - self, - user_input: str, - prompter: PermissionPrompter | None = None, - ) -> TurnSummary: - """Execute one conversational turn, looping on tool use. - - Appends the user message, then repeatedly: - 1. Build ``ApiRequest`` with system_prompt + messages. - 2. Call ``api_client.stream()`` to get assistant events. - 3. Assemble the assistant message from events. - 4. If no tool_use blocks are present, break. - 5. For each tool_use, authorize via permission_policy then execute. - 6. Append results to session. - 7. Guard against ``max_iterations``. - """ - self.session.append({"role": "user", "content": user_input}) - - summary = TurnSummary() - - for iteration in range(1, self.max_iterations + 1): - summary.iterations = iteration - - request = ApiRequest( - system_prompt=self.system_prompt, - messages=list(self.session), - ) - events = self.api_client.stream(request) - assistant_msg, tool_uses, turn_usage = _build_assistant_message(events) - - self.session.append(assistant_msg) - summary.assistant_messages.append(assistant_msg) - summary.usage += turn_usage - self.usage_tracker.record(turn_usage) - - if not tool_uses: - break - - for tu in tool_uses: - outcome = self.permission_policy.authorize( - tu.tool_name, tu.tool_input, prompter, - ) - - if outcome.allowed: - try: - output = self.tool_executor.execute(tu.tool_name, tu.tool_input) - is_error = False - except Exception as exc: - output = str(exc) - is_error = True - else: - output = outcome.reason - is_error = True - - result_msg = { - "role": "tool", - "tool_use_id": tu.tool_use_id, - "tool_name": tu.tool_name, - "output": output, - "is_error": is_error, - } - self.session.append(result_msg) - summary.tool_results.append(result_msg) - - return summary - - -def _build_assistant_message( - events: list[AssistantEvent], -) -> tuple[dict, list[AssistantEvent], TokenUsage]: - """Parse stream events into an assistant message dict, tool uses, and usage.""" - text_parts: list[str] = [] - tool_uses: list[AssistantEvent] = [] - usage = TokenUsage() - blocks: list[dict] = [] - - for event in events: - if event.kind == "text_delta": - text_parts.append(event.text) - elif event.kind == "tool_use": - # Flush accumulated text - if text_parts: - blocks.append({"type": "text", "text": "".join(text_parts)}) - text_parts.clear() - blocks.append({ - "type": "tool_use", - "id": event.tool_use_id, - "name": event.tool_name, - "input": event.tool_input, - }) - tool_uses.append(event) - elif event.kind == "usage" and event.usage is not None: - usage += event.usage - - if text_parts: - blocks.append({"type": "text", "text": "".join(text_parts)}) - - msg = {"role": "assistant", "blocks": blocks} - return msg, tool_uses, usage - - -__all__ = [ - # Tool execution - "ToolExecutor", - "StaticToolExecutor", - # Permissions - "PermissionMode", - "PermissionRequest", - "PermissionOutcome", - "PermissionPrompter", - "PermissionPolicy", - # API client - "ApiClient", - "ApiRequest", - "AssistantEvent", - # (AssistantEvent uses string 'kind' values, no separate enum) - # Usage - "TokenUsage", - "ModelPricing", - "UsageTracker", - # Conversation - "ConversationRuntime", - "TurnSummary", -] diff --git a/pkg/hanzoai/py.typed b/pkg/hanzoai/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/hanzoai/resources/__init__.py b/pkg/hanzoai/resources/__init__.py deleted file mode 100644 index 0cc5988e3..000000000 --- a/pkg/hanzoai/resources/__init__.py +++ /dev/null @@ -1,1556 +0,0 @@ -# Hanzo AI SDK Resources - -# Database Resources -from .db import ( - DBResource, - AsyncDBResource, - DBResourceWithRawResponse, - AsyncDBResourceWithRawResponse, - DBResourceWithStreamingResponse, - AsyncDBResourceWithStreamingResponse, -) -from .kv import ( - KVResource, - AsyncKVResource, - KVResourceWithRawResponse, - AsyncKVResourceWithRawResponse, - KVResourceWithStreamingResponse, - AsyncKVResourceWithStreamingResponse, -) -from .add import ( - AddResource, - AsyncAddResource, - AddResourceWithRawResponse, - AsyncAddResourceWithRawResponse, - AddResourceWithStreamingResponse, - AsyncAddResourceWithStreamingResponse, -) -from .dns import ( - DNSResource, - AsyncDNSResource, - DNSResourceWithRawResponse, - AsyncDNSResourceWithRawResponse, - DNSResourceWithStreamingResponse, - AsyncDNSResourceWithStreamingResponse, -) - -# IAM Resources -from .iam import ( - IAMResource, - AsyncIAMResource, - IAMResourceWithRawResponse, - AsyncIAMResourceWithRawResponse, - IAMResourceWithStreamingResponse, - AsyncIAMResourceWithStreamingResponse, -) -from .key import ( - KeyResource, - AsyncKeyResource, - KeyResourceWithRawResponse, - AsyncKeyResourceWithRawResponse, - KeyResourceWithStreamingResponse, - AsyncKeyResourceWithStreamingResponse, -) -from .kms import ( - KMSResource, - AsyncKMSResource, - KMSResourceWithRawResponse, - AsyncKMSResourceWithRawResponse, - KMSResourceWithStreamingResponse, - AsyncKMSResourceWithStreamingResponse, -) - -# New Platform Resources -from .mpc import ( - MPCResource, - AsyncMPCResource, - MPCResourceWithRawResponse, - AsyncMPCResourceWithRawResponse, - MPCResourceWithStreamingResponse, - AsyncMPCResourceWithStreamingResponse, -) -from .cart import ( - CartResource, - AsyncCartResource, - CartResourceWithRawResponse, - AsyncCartResourceWithRawResponse, - CartResourceWithStreamingResponse, - AsyncCartResourceWithStreamingResponse, -) -from .chat import ( - ChatResource, - AsyncChatResource, - ChatResourceWithRawResponse, - AsyncChatResourceWithRawResponse, - ChatResourceWithStreamingResponse, - AsyncChatResourceWithStreamingResponse, -) -from .edge import ( - EdgeResource, - AsyncEdgeResource, - EdgeResourceWithRawResponse, - AsyncEdgeResourceWithRawResponse, - EdgeResourceWithStreamingResponse, - AsyncEdgeResourceWithStreamingResponse, -) - -# Operations Resources -from .jobs import ( - JobsResource, - AsyncJobsResource, - JobsResourceWithRawResponse, - AsyncJobsResourceWithRawResponse, - JobsResourceWithStreamingResponse, - AsyncJobsResourceWithStreamingResponse, -) -from .paas import ( - PaaSResource, - AsyncPaaSResource, - PaaSResourceWithRawResponse, - AsyncPaaSResourceWithRawResponse, - PaaSResourceWithStreamingResponse, - AsyncPaaSResourceWithStreamingResponse, -) -from .pods import ( - PodsResource, - AsyncPodsResource, - PodsResourceWithRawResponse, - AsyncPodsResourceWithRawResponse, - PodsResourceWithStreamingResponse, - AsyncPodsResourceWithStreamingResponse, -) -from .team import ( - TeamResource, - AsyncTeamResource, - TeamResourceWithRawResponse, - AsyncTeamResourceWithRawResponse, - TeamResourceWithStreamingResponse, - AsyncTeamResourceWithStreamingResponse, -) -from .test import ( - TestResource, - AsyncTestResource, - TestResourceWithRawResponse, - AsyncTestResourceWithRawResponse, - TestResourceWithStreamingResponse, - AsyncTestResourceWithStreamingResponse, -) -from .user import ( - UserResource, - AsyncUserResource, - UserResourceWithRawResponse, - AsyncUserResourceWithRawResponse, - UserResourceWithStreamingResponse, - AsyncUserResourceWithStreamingResponse, -) -from .audio import ( - AudioResource, - AsyncAudioResource, - AudioResourceWithRawResponse, - AsyncAudioResourceWithRawResponse, - AudioResourceWithStreamingResponse, - AsyncAudioResourceWithStreamingResponse, -) -from .audit import ( - AuditResource, - AsyncAuditResource, - AuditResourceWithRawResponse, - AsyncAuditResourceWithRawResponse, - AuditResourceWithStreamingResponse, - AsyncAuditResourceWithStreamingResponse, -) -from .azure import ( - AzureResource, - AsyncAzureResource, - AzureResourceWithRawResponse, - AsyncAzureResourceWithRawResponse, - AzureResourceWithStreamingResponse, - AsyncAzureResourceWithStreamingResponse, -) - -# Build/Release Resources -from .build import ( - BuildResource, - AsyncBuildResource, - BuildResourceWithRawResponse, - AsyncBuildResourceWithRawResponse, - BuildResourceWithStreamingResponse, - AsyncBuildResourceWithStreamingResponse, -) -from .cache import ( - CacheResource, - AsyncCacheResource, - CacheResourceWithRawResponse, - AsyncCacheResourceWithRawResponse, - CacheResourceWithStreamingResponse, - AsyncCacheResourceWithStreamingResponse, -) - -# Chain Resources -from .chain import ( - ChainResource, - AsyncChainResource, - ChainResourceWithRawResponse, - AsyncChainResourceWithRawResponse, - ChainResourceWithStreamingResponse, - AsyncChainResourceWithStreamingResponse, -) -from .docdb import ( - DocDBResource, - AsyncDocDBResource, - DocDBResourceWithRawResponse, - AsyncDocDBResourceWithRawResponse, - DocDBResourceWithStreamingResponse, - AsyncDocDBResourceWithStreamingResponse, -) -from .files import ( - FilesResource, - AsyncFilesResource, - FilesResourceWithRawResponse, - AsyncFilesResourceWithRawResponse, - FilesResourceWithStreamingResponse, - AsyncFilesResourceWithStreamingResponse, -) -from .miner import ( - MinerResource, - AsyncMinerResource, - MinerResourceWithRawResponse, - AsyncMinerResourceWithRawResponse, - MinerResourceWithStreamingResponse, - AsyncMinerResourceWithStreamingResponse, -) -from .model import ( - ModelResource, - AsyncModelResource, - ModelResourceWithRawResponse, - AsyncModelResourceWithRawResponse, - ModelResourceWithStreamingResponse, - AsyncModelResourceWithStreamingResponse, -) -from .nodes import ( - NodesResource, - AsyncNodesResource, - NodesResourceWithRawResponse, - AsyncNodesResourceWithRawResponse, - NodesResourceWithStreamingResponse, - AsyncNodesResourceWithStreamingResponse, -) -from .pages import ( - PagesResource, - AsyncPagesResource, - PagesResourceWithRawResponse, - AsyncPagesResourceWithRawResponse, - PagesResourceWithStreamingResponse, - AsyncPagesResourceWithStreamingResponse, -) -from .spend import ( - SpendResource, - AsyncSpendResource, - SpendResourceWithRawResponse, - AsyncSpendResourceWithRawResponse, - SpendResourceWithStreamingResponse, - AsyncSpendResourceWithStreamingResponse, -) -from .tasks import ( - TasksResource, - AsyncTasksResource, - TasksResourceWithRawResponse, - AsyncTasksResourceWithRawResponse, - TasksResourceWithStreamingResponse, - AsyncTasksResourceWithStreamingResponse, -) -from .utils import ( - UtilsResource, - AsyncUtilsResource, - UtilsResourceWithRawResponse, - AsyncUtilsResourceWithRawResponse, - UtilsResourceWithStreamingResponse, - AsyncUtilsResourceWithStreamingResponse, -) -from .access import ( - AccessResource, - AsyncAccessResource, - AccessResourceWithRawResponse, - AsyncAccessResourceWithRawResponse, - AccessResourceWithStreamingResponse, - AsyncAccessResourceWithStreamingResponse, -) -from .active import ( - ActiveResource, - AsyncActiveResource, - ActiveResourceWithRawResponse, - AsyncActiveResourceWithRawResponse, - ActiveResourceWithStreamingResponse, - AsyncActiveResourceWithStreamingResponse, -) -from .agents import ( - AgentsResource, - AsyncAgentsResource, - AgentsResourceWithRawResponse, - AsyncAgentsResourceWithRawResponse, - AgentsResourceWithStreamingResponse, - AsyncAgentsResourceWithStreamingResponse, -) -from .budget import ( - BudgetResource, - AsyncBudgetResource, - BudgetResourceWithRawResponse, - AsyncBudgetResourceWithRawResponse, - BudgetResourceWithStreamingResponse, - AsyncBudgetResourceWithStreamingResponse, -) -from .cohere import ( - CohereResource, - AsyncCohereResource, - CohereResourceWithRawResponse, - AsyncCohereResourceWithRawResponse, - CohereResourceWithStreamingResponse, - AsyncCohereResourceWithStreamingResponse, -) -from .config import ( - ConfigResource, - AsyncConfigResource, - ConfigResourceWithRawResponse, - AsyncConfigResourceWithRawResponse, - ConfigResourceWithStreamingResponse, - AsyncConfigResourceWithStreamingResponse, -) -from .delete import ( - DeleteResource, - AsyncDeleteResource, - DeleteResourceWithRawResponse, - AsyncDeleteResourceWithRawResponse, - DeleteResourceWithStreamingResponse, - AsyncDeleteResourceWithStreamingResponse, -) -from .device import ( - DeviceResource, - AsyncDeviceResource, - DeviceResourceWithRawResponse, - AsyncDeviceResourceWithRawResponse, - DeviceResourceWithStreamingResponse, - AsyncDeviceResourceWithStreamingResponse, -) -from .gemini import ( - GeminiResource, - AsyncGeminiResource, - GeminiResourceWithRawResponse, - AsyncGeminiResourceWithRawResponse, - GeminiResourceWithStreamingResponse, - AsyncGeminiResourceWithStreamingResponse, -) -from .graphs import ( - GraphsResource, - AsyncGraphsResource, - GraphsResourceWithRawResponse, - AsyncGraphsResourceWithRawResponse, - GraphsResourceWithStreamingResponse, - AsyncGraphsResourceWithStreamingResponse, -) -from .health import ( - HealthResource, - AsyncHealthResource, - HealthResourceWithRawResponse, - AsyncHealthResourceWithRawResponse, - HealthResourceWithStreamingResponse, - AsyncHealthResourceWithStreamingResponse, -) -from .images import ( - ImagesResource, - AsyncImagesResource, - ImagesResourceWithRawResponse, - AsyncImagesResourceWithRawResponse, - ImagesResourceWithStreamingResponse, - AsyncImagesResourceWithStreamingResponse, -) -from .models import ( - ModelsResource, - AsyncModelsResource, - ModelsResourceWithRawResponse, - AsyncModelsResourceWithRawResponse, - ModelsResourceWithStreamingResponse, - AsyncModelsResourceWithStreamingResponse, -) -from .openai import ( - OpenAIResource, - AsyncOpenAIResource, - OpenAIResourceWithRawResponse, - AsyncOpenAIResourceWithRawResponse, - OpenAIResourceWithStreamingResponse, - AsyncOpenAIResourceWithStreamingResponse, -) -from .orders import ( - OrdersResource, - AsyncOrdersResource, - OrdersResourceWithRawResponse, - AsyncOrdersResourceWithRawResponse, - OrdersResourceWithStreamingResponse, - AsyncOrdersResourceWithStreamingResponse, -) -from .policy import ( - PolicyResource, - AsyncPolicyResource, - PolicyResourceWithRawResponse, - AsyncPolicyResourceWithRawResponse, - PolicyResourceWithStreamingResponse, - AsyncPolicyResourceWithStreamingResponse, -) -from .pubsub import ( - PubSubResource, - AsyncPubSubResource, - PubSubResourceWithRawResponse, - AsyncPubSubResourceWithRawResponse, - PubSubResourceWithStreamingResponse, - AsyncPubSubResourceWithStreamingResponse, -) -from .queues import ( - QueuesResource, - AsyncQueuesResource, - QueuesResourceWithRawResponse, - AsyncQueuesResourceWithRawResponse, - QueuesResourceWithStreamingResponse, - AsyncQueuesResourceWithStreamingResponse, -) -from .rerank import ( - RerankResource, - AsyncRerankResource, - RerankResourceWithRawResponse, - AsyncRerankResourceWithRawResponse, - RerankResourceWithStreamingResponse, - AsyncRerankResourceWithStreamingResponse, -) -from .routes import ( - RoutesResource, - AsyncRoutesResource, - RoutesResourceWithRawResponse, - AsyncRoutesResourceWithRawResponse, - RoutesResourceWithStreamingResponse, - AsyncRoutesResourceWithStreamingResponse, -) -from .stores import ( - StoresResource, - AsyncStoresResource, - StoresResourceWithRawResponse, - AsyncStoresResourceWithRawResponse, - StoresResourceWithStreamingResponse, - AsyncStoresResourceWithStreamingResponse, -) -from .tokens import ( - TokensResource, - AsyncTokensResource, - TokensResourceWithRawResponse, - AsyncTokensResourceWithRawResponse, - TokensResourceWithStreamingResponse, - AsyncTokensResourceWithStreamingResponse, -) - -# Connectivity Resources -from .tunnel import ( - TunnelResource, - AsyncTunnelResource, - TunnelResourceWithRawResponse, - AsyncTunnelResourceWithRawResponse, - TunnelResourceWithStreamingResponse, - AsyncTunnelResourceWithStreamingResponse, -) -from .batches import ( - BatchesResource, - AsyncBatchesResource, - BatchesResourceWithRawResponse, - AsyncBatchesResourceWithRawResponse, - BatchesResourceWithStreamingResponse, - AsyncBatchesResourceWithStreamingResponse, -) -from .bedrock import ( - BedrockResource, - AsyncBedrockResource, - BedrockResourceWithRawResponse, - AsyncBedrockResourceWithRawResponse, - BedrockResourceWithStreamingResponse, - AsyncBedrockResourceWithStreamingResponse, -) - -# Billing Resources -from .billing import ( - BillingResource, - AsyncBillingResource, - BillingResourceWithRawResponse, - AsyncBillingResourceWithRawResponse, - BillingResourceWithStreamingResponse, - AsyncBillingResourceWithStreamingResponse, -) -from .coupons import ( - CouponsResource, - AsyncCouponsResource, - CouponsResourceWithRawResponse, - AsyncCouponsResourceWithRawResponse, - CouponsResourceWithStreamingResponse, - AsyncCouponsResourceWithStreamingResponse, -) -from .engines import ( - EnginesResource, - AsyncEnginesResource, - EnginesResourceWithRawResponse, - AsyncEnginesResourceWithRawResponse, - EnginesResourceWithStreamingResponse, - AsyncEnginesResourceWithStreamingResponse, -) -from .gateway import ( - GatewayResource, - AsyncGatewayResource, - GatewayResourceWithRawResponse, - AsyncGatewayResourceWithRawResponse, - GatewayResourceWithStreamingResponse, - AsyncGatewayResourceWithStreamingResponse, -) -from .global_ import ( - GlobalResource, - AsyncGlobalResource, - GlobalResourceWithRawResponse, - AsyncGlobalResourceWithRawResponse, - GlobalResourceWithStreamingResponse, - AsyncGlobalResourceWithStreamingResponse, -) -from .ingress import ( - IngressResource, - AsyncIngressResource, - IngressResourceWithRawResponse, - AsyncIngressResourceWithRawResponse, - IngressResourceWithStreamingResponse, - AsyncIngressResourceWithStreamingResponse, -) - -# Network/Blockchain Resources -from .network import ( - NetworkResource, - AsyncNetworkResource, - NetworkResourceWithRawResponse, - AsyncNetworkResourceWithRawResponse, - NetworkResourceWithStreamingResponse, - AsyncNetworkResourceWithStreamingResponse, -) -from .release import ( - ReleaseResource, - AsyncReleaseResource, - ReleaseResourceWithRawResponse, - AsyncReleaseResourceWithRawResponse, - ReleaseResourceWithStreamingResponse, - AsyncReleaseResourceWithStreamingResponse, -) -from .secrets import ( - SecretsResource, - AsyncSecretsResource, - SecretsResourceWithRawResponse, - AsyncSecretsResourceWithRawResponse, - SecretsResourceWithStreamingResponse, - AsyncSecretsResourceWithStreamingResponse, -) - -# Data Services -from .storage import ( - StorageResource, - AsyncStorageResource, - StorageResourceWithRawResponse, - AsyncStorageResourceWithRawResponse, - StorageResourceWithStreamingResponse, - AsyncStorageResourceWithStreamingResponse, -) -from .threads import ( - ThreadsResource, - AsyncThreadsResource, - ThreadsResourceWithRawResponse, - AsyncThreadsResourceWithRawResponse, - ThreadsResourceWithStreamingResponse, - AsyncThreadsResourceWithStreamingResponse, -) -from .vectors import ( - VectorsResource, - AsyncVectorsResource, - VectorsResourceWithRawResponse, - AsyncVectorsResourceWithRawResponse, - VectorsResourceWithStreamingResponse, - AsyncVectorsResourceWithStreamingResponse, -) -from .wallets import ( - WalletsResource, - AsyncWalletsResource, - WalletsResourceWithRawResponse, - AsyncWalletsResourceWithRawResponse, - WalletsResourceWithStreamingResponse, - AsyncWalletsResourceWithStreamingResponse, -) -from .checkout import ( - CheckoutResource, - AsyncCheckoutResource, - CheckoutResourceWithRawResponse, - AsyncCheckoutResourceWithRawResponse, - CheckoutResourceWithStreamingResponse, - AsyncCheckoutResourceWithStreamingResponse, -) - -# Commerce Resources (unified) -from .commerce import ( - CommerceResource, - AsyncCommerceResource, - CommerceResourceWithRawResponse, - AsyncCommerceResourceWithRawResponse, - CommerceResourceWithStreamingResponse, - AsyncCommerceResourceWithStreamingResponse, -) -from .customer import ( - CustomerResource, - AsyncCustomerResource, - CustomerResourceWithRawResponse, - AsyncCustomerResourceWithRawResponse, - CustomerResourceWithStreamingResponse, - AsyncCustomerResourceWithStreamingResponse, -) - -# Identity/Auth Resources -from .identity import ( - IdentityResource, - AsyncIdentityResource, - IdentityResourceWithRawResponse, - AsyncIdentityResourceWithRawResponse, - IdentityResourceWithStreamingResponse, - AsyncIdentityResourceWithStreamingResponse, -) -from .langfuse import ( - LangfuseResource, - AsyncLangfuseResource, - LangfuseResourceWithRawResponse, - AsyncLangfuseResourceWithRawResponse, - LangfuseResourceWithStreamingResponse, - AsyncLangfuseResourceWithStreamingResponse, -) - -# Infrastructure Resources -from .machines import ( - MachinesResource, - AsyncMachinesResource, - MachinesResourceWithRawResponse, - AsyncMachinesResourceWithRawResponse, - MachinesResourceWithStreamingResponse, - AsyncMachinesResourceWithStreamingResponse, -) - -# Commerce Resources -from .products import ( - ProductsResource, - AsyncProductsResource, - ProductsResourceWithRawResponse, - AsyncProductsResourceWithRawResponse, - ProductsResourceWithStreamingResponse, - AsyncProductsResourceWithStreamingResponse, -) -from .provider import ( - ProviderResource, - AsyncProviderResource, - ProviderResourceWithRawResponse, - AsyncProviderResourceWithRawResponse, - ProviderResourceWithStreamingResponse, - AsyncProviderResourceWithStreamingResponse, -) -from .registry import ( - RegistryResource, - AsyncRegistryResource, - RegistryResourceWithRawResponse, - AsyncRegistryResourceWithRawResponse, - RegistryResourceWithStreamingResponse, - AsyncRegistryResourceWithStreamingResponse, -) -from .settings import ( - SettingsResource, - AsyncSettingsResource, - SettingsResourceWithRawResponse, - AsyncSettingsResourceWithRawResponse, - SettingsResourceWithStreamingResponse, - AsyncSettingsResourceWithStreamingResponse, -) -from .anthropic import ( - AnthropicResource, - AsyncAnthropicResource, - AnthropicResourceWithRawResponse, - AsyncAnthropicResourceWithRawResponse, - AnthropicResourceWithStreamingResponse, - AsyncAnthropicResourceWithStreamingResponse, -) -from .campaigns import ( - CampaignsResource, - AsyncCampaignsResource, - CampaignsResourceWithRawResponse, - AsyncCampaignsResourceWithRawResponse, - CampaignsResourceWithStreamingResponse, - AsyncCampaignsResourceWithStreamingResponse, -) -from .datastore import ( - DatastoreResource, - AsyncDatastoreResource, - DatastoreResourceWithRawResponse, - AsyncDatastoreResourceWithRawResponse, - DatastoreResourceWithStreamingResponse, - AsyncDatastoreResourceWithStreamingResponse, -) - -# AI Runtime Resources -from .inference import ( - InferenceResource, - AsyncInferenceResource, - InferenceResourceWithRawResponse, - AsyncInferenceResourceWithRawResponse, - InferenceResourceWithStreamingResponse, - AsyncInferenceResourceWithStreamingResponse, -) - -# AI/ML Resources -from .providers import ( - ProvidersResource, - AsyncProvidersResource, - ProvidersResourceWithRawResponse, - AsyncProvidersResourceWithRawResponse, - ProvidersResourceWithStreamingResponse, - AsyncProvidersResourceWithStreamingResponse, -) -from .referrals import ( - ReferralsResource, - AsyncReferralsResource, - ReferralsResourceWithRawResponse, - AsyncReferralsResourceWithRawResponse, - ReferralsResourceWithStreamingResponse, - AsyncReferralsResourceWithStreamingResponse, -) -from .responses import ( - ResponsesResource, - AsyncResponsesResource, - ResponsesResourceWithRawResponse, - AsyncResponsesResourceWithRawResponse, - ResponsesResourceWithStreamingResponse, - AsyncResponsesResourceWithStreamingResponse, -) -from .vertex_ai import ( - VertexAIResource, - AsyncVertexAIResource, - VertexAIResourceWithRawResponse, - AsyncVertexAIResourceWithRawResponse, - VertexAIResourceWithStreamingResponse, - AsyncVertexAIResourceWithStreamingResponse, -) -from .workflows import ( - WorkflowsResource, - AsyncWorkflowsResource, - WorkflowsResourceWithRawResponse, - AsyncWorkflowsResourceWithRawResponse, - WorkflowsResourceWithStreamingResponse, - AsyncWorkflowsResourceWithStreamingResponse, -) - -# Marketing Resources -from .affiliates import ( - AffiliatesResource, - AsyncAffiliatesResource, - AffiliatesResourceWithRawResponse, - AsyncAffiliatesResourceWithRawResponse, - AffiliatesResourceWithStreamingResponse, - AsyncAffiliatesResourceWithStreamingResponse, -) -from .assemblyai import ( - AssemblyaiResource, - AsyncAssemblyaiResource, - AssemblyaiResourceWithRawResponse, - AsyncAssemblyaiResourceWithRawResponse, - AssemblyaiResourceWithStreamingResponse, - AsyncAssemblyaiResourceWithStreamingResponse, -) -from .assistants import ( - AssistantsResource, - AsyncAssistantsResource, - AssistantsResourceWithRawResponse, - AsyncAssistantsResourceWithRawResponse, - AssistantsResourceWithStreamingResponse, - AsyncAssistantsResourceWithStreamingResponse, -) -from .containers import ( - ContainersResource, - AsyncContainersResource, - ContainersResourceWithRawResponse, - AsyncContainersResourceWithRawResponse, - ContainersResourceWithStreamingResponse, - AsyncContainersResourceWithStreamingResponse, -) -from .embeddings import ( - EmbeddingsResource, - AsyncEmbeddingsResource, - EmbeddingsResourceWithRawResponse, - AsyncEmbeddingsResourceWithRawResponse, - EmbeddingsResourceWithStreamingResponse, - AsyncEmbeddingsResourceWithStreamingResponse, -) -from .guardrails import ( - GuardrailsResource, - AsyncGuardrailsResource, - GuardrailsResourceWithRawResponse, - AsyncGuardrailsResourceWithRawResponse, - GuardrailsResourceWithStreamingResponse, - AsyncGuardrailsResourceWithStreamingResponse, -) -from .completions import ( - CompletionsResource, - AsyncCompletionsResource, - CompletionsResourceWithRawResponse, - AsyncCompletionsResourceWithRawResponse, - CompletionsResourceWithStreamingResponse, - AsyncCompletionsResourceWithStreamingResponse, -) -from .credentials import ( - CredentialsResource, - AsyncCredentialsResource, - CredentialsResourceWithRawResponse, - AsyncCredentialsResourceWithRawResponse, - CredentialsResourceWithStreamingResponse, - AsyncCredentialsResourceWithStreamingResponse, -) -from .deployments import ( - DeploymentsResource, - AsyncDeploymentsResource, - DeploymentsResourceWithRawResponse, - AsyncDeploymentsResourceWithRawResponse, - DeploymentsResourceWithStreamingResponse, - AsyncDeploymentsResourceWithStreamingResponse, -) -from .fine_tuning import ( - FineTuningResource, - AsyncFineTuningResource, - FineTuningResourceWithRawResponse, - AsyncFineTuningResourceWithRawResponse, - FineTuningResourceWithStreamingResponse, - AsyncFineTuningResourceWithStreamingResponse, -) -from .mcp_servers import ( - MCPServersResource, - AsyncMCPServersResource, - MCPServersResourceWithRawResponse, - AsyncMCPServersResourceWithRawResponse, - MCPServersResourceWithStreamingResponse, - AsyncMCPServersResourceWithStreamingResponse, -) -from .model_group import ( - ModelGroupResource, - AsyncModelGroupResource, - ModelGroupResourceWithRawResponse, - AsyncModelGroupResourceWithRawResponse, - ModelGroupResourceWithStreamingResponse, - AsyncModelGroupResourceWithStreamingResponse, -) -from .moderations import ( - ModerationsResource, - AsyncModerationsResource, - ModerationsResourceWithRawResponse, - AsyncModerationsResourceWithRawResponse, - ModerationsResourceWithStreamingResponse, - AsyncModerationsResourceWithStreamingResponse, -) -from .organization import ( - OrganizationResource, - AsyncOrganizationResource, - OrganizationResourceWithRawResponse, - AsyncOrganizationResourceWithRawResponse, - OrganizationResourceWithStreamingResponse, - AsyncOrganizationResourceWithStreamingResponse, -) -from .eu_assemblyai import ( - EuAssemblyaiResource, - AsyncEuAssemblyaiResource, - EuAssemblyaiResourceWithRawResponse, - AsyncEuAssemblyaiResourceWithRawResponse, - EuAssemblyaiResourceWithStreamingResponse, - AsyncEuAssemblyaiResourceWithStreamingResponse, -) -from .observability import ( - ObservabilityResource, - AsyncObservabilityResource, - ObservabilityResourceWithRawResponse, - AsyncObservabilityResourceWithRawResponse, - ObservabilityResourceWithStreamingResponse, - AsyncObservabilityResourceWithStreamingResponse, -) -from .subscriptions import ( - SubscriptionsResource, - AsyncSubscriptionsResource, - SubscriptionsResourceWithRawResponse, - AsyncSubscriptionsResourceWithRawResponse, - SubscriptionsResourceWithStreamingResponse, - AsyncSubscriptionsResourceWithStreamingResponse, -) - -# Team Workspace Resources -from .team_workspace import ( - TeamWorkspaceResource, - AsyncTeamWorkspaceResource, - TeamWorkspaceResourceWithRawResponse, - AsyncTeamWorkspaceResourceWithRawResponse, - TeamWorkspaceResourceWithStreamingResponse, - AsyncTeamWorkspaceResourceWithStreamingResponse, -) - -__all__ = [ - "ModelsResource", - "AsyncModelsResource", - "ModelsResourceWithRawResponse", - "AsyncModelsResourceWithRawResponse", - "ModelsResourceWithStreamingResponse", - "AsyncModelsResourceWithStreamingResponse", - "OpenAIResource", - "AsyncOpenAIResource", - "OpenAIResourceWithRawResponse", - "AsyncOpenAIResourceWithRawResponse", - "OpenAIResourceWithStreamingResponse", - "AsyncOpenAIResourceWithStreamingResponse", - "EnginesResource", - "AsyncEnginesResource", - "EnginesResourceWithRawResponse", - "AsyncEnginesResourceWithRawResponse", - "EnginesResourceWithStreamingResponse", - "AsyncEnginesResourceWithStreamingResponse", - "ChatResource", - "AsyncChatResource", - "ChatResourceWithRawResponse", - "AsyncChatResourceWithRawResponse", - "ChatResourceWithStreamingResponse", - "AsyncChatResourceWithStreamingResponse", - "CompletionsResource", - "AsyncCompletionsResource", - "CompletionsResourceWithRawResponse", - "AsyncCompletionsResourceWithRawResponse", - "CompletionsResourceWithStreamingResponse", - "AsyncCompletionsResourceWithStreamingResponse", - "EmbeddingsResource", - "AsyncEmbeddingsResource", - "EmbeddingsResourceWithRawResponse", - "AsyncEmbeddingsResourceWithRawResponse", - "EmbeddingsResourceWithStreamingResponse", - "AsyncEmbeddingsResourceWithStreamingResponse", - "ImagesResource", - "AsyncImagesResource", - "ImagesResourceWithRawResponse", - "AsyncImagesResourceWithRawResponse", - "ImagesResourceWithStreamingResponse", - "AsyncImagesResourceWithStreamingResponse", - "AudioResource", - "AsyncAudioResource", - "AudioResourceWithRawResponse", - "AsyncAudioResourceWithRawResponse", - "AudioResourceWithStreamingResponse", - "AsyncAudioResourceWithStreamingResponse", - "AssistantsResource", - "AsyncAssistantsResource", - "AssistantsResourceWithRawResponse", - "AsyncAssistantsResourceWithRawResponse", - "AssistantsResourceWithStreamingResponse", - "AsyncAssistantsResourceWithStreamingResponse", - "ThreadsResource", - "AsyncThreadsResource", - "ThreadsResourceWithRawResponse", - "AsyncThreadsResourceWithRawResponse", - "ThreadsResourceWithStreamingResponse", - "AsyncThreadsResourceWithStreamingResponse", - "ModerationsResource", - "AsyncModerationsResource", - "ModerationsResourceWithRawResponse", - "AsyncModerationsResourceWithRawResponse", - "ModerationsResourceWithStreamingResponse", - "AsyncModerationsResourceWithStreamingResponse", - "UtilsResource", - "AsyncUtilsResource", - "UtilsResourceWithRawResponse", - "AsyncUtilsResourceWithRawResponse", - "UtilsResourceWithStreamingResponse", - "AsyncUtilsResourceWithStreamingResponse", - "ModelResource", - "AsyncModelResource", - "ModelResourceWithRawResponse", - "AsyncModelResourceWithRawResponse", - "ModelResourceWithStreamingResponse", - "AsyncModelResourceWithStreamingResponse", - "ModelGroupResource", - "AsyncModelGroupResource", - "ModelGroupResourceWithRawResponse", - "AsyncModelGroupResourceWithRawResponse", - "ModelGroupResourceWithStreamingResponse", - "AsyncModelGroupResourceWithStreamingResponse", - "RoutesResource", - "AsyncRoutesResource", - "RoutesResourceWithRawResponse", - "AsyncRoutesResourceWithRawResponse", - "RoutesResourceWithStreamingResponse", - "AsyncRoutesResourceWithStreamingResponse", - "ResponsesResource", - "AsyncResponsesResource", - "ResponsesResourceWithRawResponse", - "AsyncResponsesResourceWithRawResponse", - "ResponsesResourceWithStreamingResponse", - "AsyncResponsesResourceWithStreamingResponse", - "BatchesResource", - "AsyncBatchesResource", - "BatchesResourceWithRawResponse", - "AsyncBatchesResourceWithRawResponse", - "BatchesResourceWithStreamingResponse", - "AsyncBatchesResourceWithStreamingResponse", - "RerankResource", - "AsyncRerankResource", - "RerankResourceWithRawResponse", - "AsyncRerankResourceWithRawResponse", - "RerankResourceWithStreamingResponse", - "AsyncRerankResourceWithStreamingResponse", - "FineTuningResource", - "AsyncFineTuningResource", - "FineTuningResourceWithRawResponse", - "AsyncFineTuningResourceWithRawResponse", - "FineTuningResourceWithStreamingResponse", - "AsyncFineTuningResourceWithStreamingResponse", - "CredentialsResource", - "AsyncCredentialsResource", - "CredentialsResourceWithRawResponse", - "AsyncCredentialsResourceWithRawResponse", - "CredentialsResourceWithStreamingResponse", - "AsyncCredentialsResourceWithStreamingResponse", - "VertexAIResource", - "AsyncVertexAIResource", - "VertexAIResourceWithRawResponse", - "AsyncVertexAIResourceWithRawResponse", - "VertexAIResourceWithStreamingResponse", - "AsyncVertexAIResourceWithStreamingResponse", - "GeminiResource", - "AsyncGeminiResource", - "GeminiResourceWithRawResponse", - "AsyncGeminiResourceWithRawResponse", - "GeminiResourceWithStreamingResponse", - "AsyncGeminiResourceWithStreamingResponse", - "CohereResource", - "AsyncCohereResource", - "CohereResourceWithRawResponse", - "AsyncCohereResourceWithRawResponse", - "CohereResourceWithStreamingResponse", - "AsyncCohereResourceWithStreamingResponse", - "AnthropicResource", - "AsyncAnthropicResource", - "AnthropicResourceWithRawResponse", - "AsyncAnthropicResourceWithRawResponse", - "AnthropicResourceWithStreamingResponse", - "AsyncAnthropicResourceWithStreamingResponse", - "BedrockResource", - "AsyncBedrockResource", - "BedrockResourceWithRawResponse", - "AsyncBedrockResourceWithRawResponse", - "BedrockResourceWithStreamingResponse", - "AsyncBedrockResourceWithStreamingResponse", - "EuAssemblyaiResource", - "AsyncEuAssemblyaiResource", - "EuAssemblyaiResourceWithRawResponse", - "AsyncEuAssemblyaiResourceWithRawResponse", - "EuAssemblyaiResourceWithStreamingResponse", - "AsyncEuAssemblyaiResourceWithStreamingResponse", - "AssemblyaiResource", - "AsyncAssemblyaiResource", - "AssemblyaiResourceWithRawResponse", - "AsyncAssemblyaiResourceWithRawResponse", - "AssemblyaiResourceWithStreamingResponse", - "AsyncAssemblyaiResourceWithStreamingResponse", - "AzureResource", - "AsyncAzureResource", - "AzureResourceWithRawResponse", - "AsyncAzureResourceWithRawResponse", - "AzureResourceWithStreamingResponse", - "AsyncAzureResourceWithStreamingResponse", - "LangfuseResource", - "AsyncLangfuseResource", - "LangfuseResourceWithRawResponse", - "AsyncLangfuseResourceWithRawResponse", - "LangfuseResourceWithStreamingResponse", - "AsyncLangfuseResourceWithStreamingResponse", - "ConfigResource", - "AsyncConfigResource", - "ConfigResourceWithRawResponse", - "AsyncConfigResourceWithRawResponse", - "ConfigResourceWithStreamingResponse", - "AsyncConfigResourceWithStreamingResponse", - "TestResource", - "AsyncTestResource", - "TestResourceWithRawResponse", - "AsyncTestResourceWithRawResponse", - "TestResourceWithStreamingResponse", - "AsyncTestResourceWithStreamingResponse", - "HealthResource", - "AsyncHealthResource", - "HealthResourceWithRawResponse", - "AsyncHealthResourceWithRawResponse", - "HealthResourceWithStreamingResponse", - "AsyncHealthResourceWithStreamingResponse", - "ActiveResource", - "AsyncActiveResource", - "ActiveResourceWithRawResponse", - "AsyncActiveResourceWithRawResponse", - "ActiveResourceWithStreamingResponse", - "AsyncActiveResourceWithStreamingResponse", - "SettingsResource", - "AsyncSettingsResource", - "SettingsResourceWithRawResponse", - "AsyncSettingsResourceWithRawResponse", - "SettingsResourceWithStreamingResponse", - "AsyncSettingsResourceWithStreamingResponse", - "KeyResource", - "AsyncKeyResource", - "KeyResourceWithRawResponse", - "AsyncKeyResourceWithRawResponse", - "KeyResourceWithStreamingResponse", - "AsyncKeyResourceWithStreamingResponse", - "UserResource", - "AsyncUserResource", - "UserResourceWithRawResponse", - "AsyncUserResourceWithRawResponse", - "UserResourceWithStreamingResponse", - "AsyncUserResourceWithStreamingResponse", - "TeamResource", - "AsyncTeamResource", - "TeamResourceWithRawResponse", - "AsyncTeamResourceWithRawResponse", - "TeamResourceWithStreamingResponse", - "AsyncTeamResourceWithStreamingResponse", - "OrganizationResource", - "AsyncOrganizationResource", - "OrganizationResourceWithRawResponse", - "AsyncOrganizationResourceWithRawResponse", - "OrganizationResourceWithStreamingResponse", - "AsyncOrganizationResourceWithStreamingResponse", - "CustomerResource", - "AsyncCustomerResource", - "CustomerResourceWithRawResponse", - "AsyncCustomerResourceWithRawResponse", - "CustomerResourceWithStreamingResponse", - "AsyncCustomerResourceWithStreamingResponse", - "SpendResource", - "AsyncSpendResource", - "SpendResourceWithRawResponse", - "AsyncSpendResourceWithRawResponse", - "SpendResourceWithStreamingResponse", - "AsyncSpendResourceWithStreamingResponse", - "GlobalResource", - "AsyncGlobalResource", - "GlobalResourceWithRawResponse", - "AsyncGlobalResourceWithRawResponse", - "GlobalResourceWithStreamingResponse", - "AsyncGlobalResourceWithStreamingResponse", - "ProviderResource", - "AsyncProviderResource", - "ProviderResourceWithRawResponse", - "AsyncProviderResourceWithRawResponse", - "ProviderResourceWithStreamingResponse", - "AsyncProviderResourceWithStreamingResponse", - "CacheResource", - "AsyncCacheResource", - "CacheResourceWithRawResponse", - "AsyncCacheResourceWithRawResponse", - "CacheResourceWithStreamingResponse", - "AsyncCacheResourceWithStreamingResponse", - "GuardrailsResource", - "AsyncGuardrailsResource", - "GuardrailsResourceWithRawResponse", - "AsyncGuardrailsResourceWithRawResponse", - "GuardrailsResourceWithStreamingResponse", - "AsyncGuardrailsResourceWithStreamingResponse", - "AddResource", - "AsyncAddResource", - "AddResourceWithRawResponse", - "AsyncAddResourceWithRawResponse", - "AddResourceWithStreamingResponse", - "AsyncAddResourceWithStreamingResponse", - "DeleteResource", - "AsyncDeleteResource", - "DeleteResourceWithRawResponse", - "AsyncDeleteResourceWithRawResponse", - "DeleteResourceWithStreamingResponse", - "AsyncDeleteResourceWithStreamingResponse", - "FilesResource", - "AsyncFilesResource", - "FilesResourceWithRawResponse", - "AsyncFilesResourceWithRawResponse", - "FilesResourceWithStreamingResponse", - "AsyncFilesResourceWithStreamingResponse", - "BudgetResource", - "AsyncBudgetResource", - "BudgetResourceWithRawResponse", - "AsyncBudgetResourceWithRawResponse", - "BudgetResourceWithStreamingResponse", - "AsyncBudgetResourceWithStreamingResponse", - # AI/ML Resources - "ProvidersResource", - "AsyncProvidersResource", - "ProvidersResourceWithRawResponse", - "AsyncProvidersResourceWithRawResponse", - "ProvidersResourceWithStreamingResponse", - "AsyncProvidersResourceWithStreamingResponse", - "WorkflowsResource", - "AsyncWorkflowsResource", - "WorkflowsResourceWithRawResponse", - "AsyncWorkflowsResourceWithRawResponse", - "WorkflowsResourceWithStreamingResponse", - "AsyncWorkflowsResourceWithStreamingResponse", - "VectorsResource", - "AsyncVectorsResource", - "VectorsResourceWithRawResponse", - "AsyncVectorsResourceWithRawResponse", - "VectorsResourceWithStreamingResponse", - "AsyncVectorsResourceWithStreamingResponse", - "StoresResource", - "AsyncStoresResource", - "StoresResourceWithRawResponse", - "AsyncStoresResourceWithRawResponse", - "StoresResourceWithStreamingResponse", - "AsyncStoresResourceWithStreamingResponse", - "GraphsResource", - "AsyncGraphsResource", - "GraphsResourceWithRawResponse", - "AsyncGraphsResourceWithRawResponse", - "GraphsResourceWithStreamingResponse", - "AsyncGraphsResourceWithStreamingResponse", - # Data Services - "StorageResource", - "AsyncStorageResource", - "StorageResourceWithRawResponse", - "AsyncStorageResourceWithRawResponse", - "StorageResourceWithStreamingResponse", - "AsyncStorageResourceWithStreamingResponse", - "KVResource", - "AsyncKVResource", - "KVResourceWithRawResponse", - "AsyncKVResourceWithRawResponse", - "KVResourceWithStreamingResponse", - "AsyncKVResourceWithStreamingResponse", - "PubSubResource", - "AsyncPubSubResource", - "PubSubResourceWithRawResponse", - "AsyncPubSubResourceWithRawResponse", - "PubSubResourceWithStreamingResponse", - "AsyncPubSubResourceWithStreamingResponse", - "QueuesResource", - "AsyncQueuesResource", - "QueuesResourceWithRawResponse", - "AsyncQueuesResourceWithRawResponse", - "QueuesResourceWithStreamingResponse", - "AsyncQueuesResourceWithStreamingResponse", - # Commerce Resources - "ProductsResource", - "AsyncProductsResource", - "ProductsResourceWithRawResponse", - "AsyncProductsResourceWithRawResponse", - "ProductsResourceWithStreamingResponse", - "AsyncProductsResourceWithStreamingResponse", - "CartResource", - "AsyncCartResource", - "CartResourceWithRawResponse", - "AsyncCartResourceWithRawResponse", - "CartResourceWithStreamingResponse", - "AsyncCartResourceWithStreamingResponse", - "CheckoutResource", - "AsyncCheckoutResource", - "CheckoutResourceWithRawResponse", - "AsyncCheckoutResourceWithRawResponse", - "CheckoutResourceWithStreamingResponse", - "AsyncCheckoutResourceWithStreamingResponse", - "OrdersResource", - "AsyncOrdersResource", - "OrdersResourceWithRawResponse", - "AsyncOrdersResourceWithRawResponse", - "OrdersResourceWithStreamingResponse", - "AsyncOrdersResourceWithStreamingResponse", - "SubscriptionsResource", - "AsyncSubscriptionsResource", - "SubscriptionsResourceWithRawResponse", - "AsyncSubscriptionsResourceWithRawResponse", - "SubscriptionsResourceWithStreamingResponse", - "AsyncSubscriptionsResourceWithStreamingResponse", - # Infrastructure Resources - "MachinesResource", - "AsyncMachinesResource", - "MachinesResourceWithRawResponse", - "AsyncMachinesResourceWithRawResponse", - "MachinesResourceWithStreamingResponse", - "AsyncMachinesResourceWithStreamingResponse", - "ContainersResource", - "AsyncContainersResource", - "ContainersResourceWithRawResponse", - "AsyncContainersResourceWithRawResponse", - "ContainersResourceWithStreamingResponse", - "AsyncContainersResourceWithStreamingResponse", - "PodsResource", - "AsyncPodsResource", - "PodsResourceWithRawResponse", - "AsyncPodsResourceWithRawResponse", - "PodsResourceWithStreamingResponse", - "AsyncPodsResourceWithStreamingResponse", - "DeploymentsResource", - "AsyncDeploymentsResource", - "DeploymentsResourceWithRawResponse", - "AsyncDeploymentsResourceWithRawResponse", - "DeploymentsResourceWithStreamingResponse", - "AsyncDeploymentsResourceWithStreamingResponse", - "SecretsResource", - "AsyncSecretsResource", - "SecretsResourceWithRawResponse", - "AsyncSecretsResourceWithRawResponse", - "SecretsResourceWithStreamingResponse", - "AsyncSecretsResourceWithStreamingResponse", - # Marketing Resources - "AffiliatesResource", - "AsyncAffiliatesResource", - "AffiliatesResourceWithRawResponse", - "AsyncAffiliatesResourceWithRawResponse", - "AffiliatesResourceWithStreamingResponse", - "AsyncAffiliatesResourceWithStreamingResponse", - "ReferralsResource", - "AsyncReferralsResource", - "ReferralsResourceWithRawResponse", - "AsyncReferralsResourceWithRawResponse", - "ReferralsResourceWithStreamingResponse", - "AsyncReferralsResourceWithStreamingResponse", - "CampaignsResource", - "AsyncCampaignsResource", - "CampaignsResourceWithRawResponse", - "AsyncCampaignsResourceWithRawResponse", - "CampaignsResourceWithStreamingResponse", - "AsyncCampaignsResourceWithStreamingResponse", - "CouponsResource", - "AsyncCouponsResource", - "CouponsResourceWithRawResponse", - "AsyncCouponsResourceWithRawResponse", - "CouponsResourceWithStreamingResponse", - "AsyncCouponsResourceWithStreamingResponse", - # Operations Resources - "JobsResource", - "AsyncJobsResource", - "JobsResourceWithRawResponse", - "AsyncJobsResourceWithRawResponse", - "JobsResourceWithStreamingResponse", - "AsyncJobsResourceWithStreamingResponse", - "TasksResource", - "AsyncTasksResource", - "TasksResourceWithRawResponse", - "AsyncTasksResourceWithRawResponse", - "TasksResourceWithStreamingResponse", - "AsyncTasksResourceWithStreamingResponse", - "ObservabilityResource", - "AsyncObservabilityResource", - "ObservabilityResourceWithRawResponse", - "AsyncObservabilityResourceWithRawResponse", - "ObservabilityResourceWithStreamingResponse", - "AsyncObservabilityResourceWithStreamingResponse", - "AgentsResource", - "AsyncAgentsResource", - "AgentsResourceWithRawResponse", - "AsyncAgentsResourceWithRawResponse", - "AgentsResourceWithStreamingResponse", - "AsyncAgentsResourceWithStreamingResponse", - "MCPServersResource", - "AsyncMCPServersResource", - "MCPServersResourceWithRawResponse", - "AsyncMCPServersResourceWithRawResponse", - "MCPServersResourceWithStreamingResponse", - "AsyncMCPServersResourceWithStreamingResponse", - # Network/Blockchain Resources - "NetworkResource", - "AsyncNetworkResource", - "NetworkResourceWithRawResponse", - "AsyncNetworkResourceWithRawResponse", - "NetworkResourceWithStreamingResponse", - "AsyncNetworkResourceWithStreamingResponse", - "NodesResource", - "AsyncNodesResource", - "NodesResourceWithRawResponse", - "AsyncNodesResourceWithRawResponse", - "NodesResourceWithStreamingResponse", - "AsyncNodesResourceWithStreamingResponse", - "WalletsResource", - "AsyncWalletsResource", - "WalletsResourceWithRawResponse", - "AsyncWalletsResourceWithRawResponse", - "WalletsResourceWithStreamingResponse", - "AsyncWalletsResourceWithStreamingResponse", - "TokensResource", - "AsyncTokensResource", - "TokensResourceWithRawResponse", - "AsyncTokensResourceWithRawResponse", - "TokensResourceWithStreamingResponse", - "AsyncTokensResourceWithStreamingResponse", - # Identity/Auth Resources - "IdentityResource", - "AsyncIdentityResource", - "IdentityResourceWithRawResponse", - "AsyncIdentityResourceWithRawResponse", - "IdentityResourceWithStreamingResponse", - "AsyncIdentityResourceWithStreamingResponse", - "KMSResource", - "AsyncKMSResource", - "KMSResourceWithRawResponse", - "AsyncKMSResourceWithRawResponse", - "KMSResourceWithStreamingResponse", - "AsyncKMSResourceWithStreamingResponse", - "PolicyResource", - "AsyncPolicyResource", - "PolicyResourceWithRawResponse", - "AsyncPolicyResourceWithRawResponse", - "PolicyResourceWithStreamingResponse", - "AsyncPolicyResourceWithStreamingResponse", - "AuditResource", - "AsyncAuditResource", - "AuditResourceWithRawResponse", - "AsyncAuditResourceWithRawResponse", - "AuditResourceWithStreamingResponse", - "AsyncAuditResourceWithStreamingResponse", - # Connectivity Resources - "TunnelResource", - "AsyncTunnelResource", - "TunnelResourceWithRawResponse", - "AsyncTunnelResourceWithRawResponse", - "TunnelResourceWithStreamingResponse", - "AsyncTunnelResourceWithStreamingResponse", - "DeviceResource", - "AsyncDeviceResource", - "DeviceResourceWithRawResponse", - "AsyncDeviceResourceWithRawResponse", - "DeviceResourceWithStreamingResponse", - "AsyncDeviceResourceWithStreamingResponse", - "AccessResource", - "AsyncAccessResource", - "AccessResourceWithRawResponse", - "AsyncAccessResourceWithRawResponse", - "AccessResourceWithStreamingResponse", - "AsyncAccessResourceWithStreamingResponse", - "EdgeResource", - "AsyncEdgeResource", - "EdgeResourceWithRawResponse", - "AsyncEdgeResourceWithRawResponse", - "EdgeResourceWithStreamingResponse", - "AsyncEdgeResourceWithStreamingResponse", - "GatewayResource", - "AsyncGatewayResource", - "GatewayResourceWithRawResponse", - "AsyncGatewayResourceWithRawResponse", - "GatewayResourceWithStreamingResponse", - "AsyncGatewayResourceWithStreamingResponse", - "DNSResource", - "AsyncDNSResource", - "DNSResourceWithRawResponse", - "AsyncDNSResourceWithRawResponse", - "DNSResourceWithStreamingResponse", - "AsyncDNSResourceWithStreamingResponse", - "PagesResource", - "AsyncPagesResource", - "PagesResourceWithRawResponse", - "AsyncPagesResourceWithRawResponse", - "PagesResourceWithStreamingResponse", - "AsyncPagesResourceWithStreamingResponse", - # Build/Release Resources - "BuildResource", - "AsyncBuildResource", - "BuildResourceWithRawResponse", - "AsyncBuildResourceWithRawResponse", - "BuildResourceWithStreamingResponse", - "AsyncBuildResourceWithStreamingResponse", - "RegistryResource", - "AsyncRegistryResource", - "RegistryResourceWithRawResponse", - "AsyncRegistryResourceWithRawResponse", - "RegistryResourceWithStreamingResponse", - "AsyncRegistryResourceWithStreamingResponse", - "ReleaseResource", - "AsyncReleaseResource", - "ReleaseResourceWithRawResponse", - "AsyncReleaseResourceWithRawResponse", - "ReleaseResourceWithStreamingResponse", - "AsyncReleaseResourceWithStreamingResponse", - # Database Resources - "DBResource", - "AsyncDBResource", - "DBResourceWithRawResponse", - "AsyncDBResourceWithRawResponse", - "DBResourceWithStreamingResponse", - "AsyncDBResourceWithStreamingResponse", - # AI Runtime Resources - "InferenceResource", - "AsyncInferenceResource", - "InferenceResourceWithRawResponse", - "AsyncInferenceResourceWithRawResponse", - "InferenceResourceWithStreamingResponse", - "AsyncInferenceResourceWithStreamingResponse", - # Chain Resources - "ChainResource", - "AsyncChainResource", - "ChainResourceWithRawResponse", - "AsyncChainResourceWithRawResponse", - "ChainResourceWithStreamingResponse", - "AsyncChainResourceWithStreamingResponse", - "MinerResource", - "AsyncMinerResource", - "MinerResourceWithRawResponse", - "AsyncMinerResourceWithRawResponse", - "MinerResourceWithStreamingResponse", - "AsyncMinerResourceWithStreamingResponse", - # New Platform Resources - "MPCResource", - "AsyncMPCResource", - "MPCResourceWithRawResponse", - "AsyncMPCResourceWithRawResponse", - "MPCResourceWithStreamingResponse", - "AsyncMPCResourceWithStreamingResponse", - "PaaSResource", - "AsyncPaaSResource", - "PaaSResourceWithRawResponse", - "AsyncPaaSResourceWithRawResponse", - "PaaSResourceWithStreamingResponse", - "AsyncPaaSResourceWithStreamingResponse", - "DocDBResource", - "AsyncDocDBResource", - "DocDBResourceWithRawResponse", - "AsyncDocDBResourceWithRawResponse", - "DocDBResourceWithStreamingResponse", - "AsyncDocDBResourceWithStreamingResponse", - "IngressResource", - "AsyncIngressResource", - "IngressResourceWithRawResponse", - "AsyncIngressResourceWithRawResponse", - "IngressResourceWithStreamingResponse", - "AsyncIngressResourceWithStreamingResponse", - "DatastoreResource", - "AsyncDatastoreResource", - "DatastoreResourceWithRawResponse", - "AsyncDatastoreResourceWithRawResponse", - "DatastoreResourceWithStreamingResponse", - "AsyncDatastoreResourceWithStreamingResponse", - # Billing Resources - "BillingResource", - "AsyncBillingResource", - "BillingResourceWithRawResponse", - "AsyncBillingResourceWithRawResponse", - "BillingResourceWithStreamingResponse", - "AsyncBillingResourceWithStreamingResponse", - # IAM Resources - "IAMResource", - "AsyncIAMResource", - "IAMResourceWithRawResponse", - "AsyncIAMResourceWithRawResponse", - "IAMResourceWithStreamingResponse", - "AsyncIAMResourceWithStreamingResponse", - # Commerce Resources (unified) - "CommerceResource", - "AsyncCommerceResource", - "CommerceResourceWithRawResponse", - "AsyncCommerceResourceWithRawResponse", - "CommerceResourceWithStreamingResponse", - "AsyncCommerceResourceWithStreamingResponse", - # Team Workspace Resources - "TeamWorkspaceResource", - "AsyncTeamWorkspaceResource", - "TeamWorkspaceResourceWithRawResponse", - "AsyncTeamWorkspaceResourceWithRawResponse", - "TeamWorkspaceResourceWithStreamingResponse", - "AsyncTeamWorkspaceResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/access.py b/pkg/hanzoai/resources/access.py deleted file mode 100644 index 30ae274f1..000000000 --- a/pkg/hanzoai/resources/access.py +++ /dev/null @@ -1,320 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class AccessResource(SyncAPIResource): - """User-facing access grants and sharing.""" - - @cached_property - def with_raw_response(self) -> AccessResourceWithRawResponse: - return AccessResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AccessResourceWithStreamingResponse: - return AccessResourceWithStreamingResponse(self) - - def grant( - self, - *, - principal: str, - resource: str, - ttl: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Grant access to a service or app.""" - return self._post( - "/access/grant", - body={"principal": principal, "resource": resource, "ttl": ttl}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def revoke( - self, - *, - principal: str, - resource: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Revoke access from a service or app.""" - return self._post( - "/access/revoke", - body={"principal": principal, "resource": resource}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def whoami( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get current access context.""" - return self._get( - "/access/whoami", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def check( - self, - *, - service: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Check access to a service.""" - return self._get( - "/access/check", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"service": service}, - ), - cast_to=object, - ) - - def share( - self, - *, - service: str, - ttl: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a shareable access link.""" - return self._post( - "/access/share", - body={"service": service, "ttl": ttl}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List current access grants.""" - return self._get( - "/access", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncAccessResource(AsyncAPIResource): - """User-facing access grants and sharing.""" - - @cached_property - def with_raw_response(self) -> AsyncAccessResourceWithRawResponse: - return AsyncAccessResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncAccessResourceWithStreamingResponse: - return AsyncAccessResourceWithStreamingResponse(self) - - async def grant( - self, - *, - principal: str, - resource: str, - ttl: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Grant access to a service or app.""" - return await self._post( - "/access/grant", - body={"principal": principal, "resource": resource, "ttl": ttl}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def revoke( - self, - *, - principal: str, - resource: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Revoke access from a service or app.""" - return await self._post( - "/access/revoke", - body={"principal": principal, "resource": resource}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def whoami( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get current access context.""" - return await self._get( - "/access/whoami", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def check( - self, - *, - service: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Check access to a service.""" - return await self._get( - "/access/check", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"service": service}, - ), - cast_to=object, - ) - - async def share( - self, - *, - service: str, - ttl: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a shareable access link.""" - return await self._post( - "/access/share", - body={"service": service, "ttl": ttl}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List current access grants.""" - return await self._get( - "/access", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AccessResourceWithRawResponse: - def __init__(self, access: AccessResource) -> None: - self._access = access - - -class AsyncAccessResourceWithRawResponse: - def __init__(self, access: AsyncAccessResource) -> None: - self._access = access - - -class AccessResourceWithStreamingResponse: - def __init__(self, access: AccessResource) -> None: - self._access = access - - -class AsyncAccessResourceWithStreamingResponse: - def __init__(self, access: AsyncAccessResource) -> None: - self._access = access diff --git a/pkg/hanzoai/resources/active.py b/pkg/hanzoai/resources/active.py deleted file mode 100644 index f18db12f0..000000000 --- a/pkg/hanzoai/resources/active.py +++ /dev/null @@ -1,188 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["ActiveResource", "AsyncActiveResource"] - - -class ActiveResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ActiveResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return ActiveResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ActiveResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return ActiveResourceWithStreamingResponse(self) - - def list_callbacks( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Returns a list of hanzo level settings - - This is useful for debugging and ensuring the proxy server is configured - correctly. - - Response schema: - - ``` - { - "alerting": _alerting, - "hanzo.callbacks": hanzo_callbacks, - "hanzo.input_callback": hanzo_input_callbacks, - "hanzo.failure_callback": hanzo_failure_callbacks, - "hanzo.success_callback": hanzo_success_callbacks, - "hanzo._async_success_callback": hanzo_async_success_callbacks, - "hanzo._async_failure_callback": hanzo_async_failure_callbacks, - "hanzo._async_input_callback": hanzo_async_input_callbacks, - "all_hanzo_callbacks": all_hanzo_callbacks, - "num_callbacks": len(all_hanzo_callbacks), - "num_alerting": _num_alerting, - "hanzo.request_timeout": hanzo.request_timeout, - } - ``` - """ - return self._get( - "/active/callbacks", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncActiveResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncActiveResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncActiveResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncActiveResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncActiveResourceWithStreamingResponse(self) - - async def list_callbacks( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Returns a list of hanzo level settings - - This is useful for debugging and ensuring the proxy server is configured - correctly. - - Response schema: - - ``` - { - "alerting": _alerting, - "hanzo.callbacks": hanzo_callbacks, - "hanzo.input_callback": hanzo_input_callbacks, - "hanzo.failure_callback": hanzo_failure_callbacks, - "hanzo.success_callback": hanzo_success_callbacks, - "hanzo._async_success_callback": hanzo_async_success_callbacks, - "hanzo._async_failure_callback": hanzo_async_failure_callbacks, - "hanzo._async_input_callback": hanzo_async_input_callbacks, - "all_hanzo_callbacks": all_hanzo_callbacks, - "num_callbacks": len(all_hanzo_callbacks), - "num_alerting": _num_alerting, - "hanzo.request_timeout": hanzo.request_timeout, - } - ``` - """ - return await self._get( - "/active/callbacks", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class ActiveResourceWithRawResponse: - def __init__(self, active: ActiveResource) -> None: - self._active = active - - self.list_callbacks = to_raw_response_wrapper( - active.list_callbacks, - ) - - -class AsyncActiveResourceWithRawResponse: - def __init__(self, active: AsyncActiveResource) -> None: - self._active = active - - self.list_callbacks = async_to_raw_response_wrapper( - active.list_callbacks, - ) - - -class ActiveResourceWithStreamingResponse: - def __init__(self, active: ActiveResource) -> None: - self._active = active - - self.list_callbacks = to_streamed_response_wrapper( - active.list_callbacks, - ) - - -class AsyncActiveResourceWithStreamingResponse: - def __init__(self, active: AsyncActiveResource) -> None: - self._active = active - - self.list_callbacks = async_to_streamed_response_wrapper( - active.list_callbacks, - ) diff --git a/pkg/hanzoai/resources/affiliates.py b/pkg/hanzoai/resources/affiliates.py deleted file mode 100644 index f1d0c2bc0..000000000 --- a/pkg/hanzoai/resources/affiliates.py +++ /dev/null @@ -1,411 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["AffiliatesResource", "AsyncAffiliatesResource"] - - -class AffiliatesResource(SyncAPIResource): - """Affiliate program management.""" - - @cached_property - def with_raw_response(self) -> AffiliatesResourceWithRawResponse: - return AffiliatesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AffiliatesResourceWithStreamingResponse: - return AffiliatesResourceWithStreamingResponse(self) - - def list( - self, - *, - status: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all affiliates.""" - return self._get( - "/marketing/affiliates", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"status": status, "limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - def get( - self, - affiliate_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific affiliate.""" - return self._get( - f"/marketing/affiliates/{affiliate_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - user_id: str, - commission_rate: float | NotGiven = NOT_GIVEN, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new affiliate.""" - return self._post( - "/marketing/affiliates", - body={ - "user_id": user_id, - "commission_rate": commission_rate, - "metadata": metadata, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - affiliate_id: str, - *, - commission_rate: float | NotGiven = NOT_GIVEN, - status: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an affiliate.""" - return self._put( - f"/marketing/affiliates/{affiliate_id}", - body={"commission_rate": commission_rate, "status": status}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - affiliate_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete an affiliate.""" - return self._delete( - f"/marketing/affiliates/{affiliate_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stats( - self, - affiliate_id: str, - *, - start_date: str | NotGiven = NOT_GIVEN, - end_date: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get affiliate statistics.""" - return self._get( - f"/marketing/affiliates/{affiliate_id}/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"start_date": start_date, "end_date": end_date}, - ), - cast_to=object, - ) - - def payouts( - self, - affiliate_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get affiliate payouts.""" - return self._get( - f"/marketing/affiliates/{affiliate_id}/payouts", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncAffiliatesResource(AsyncAPIResource): - """Affiliate program management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncAffiliatesResourceWithRawResponse: - return AsyncAffiliatesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncAffiliatesResourceWithStreamingResponse: - return AsyncAffiliatesResourceWithStreamingResponse(self) - - async def list( - self, - *, - status: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/marketing/affiliates", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"status": status, "limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - async def get( - self, - affiliate_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/marketing/affiliates/{affiliate_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - user_id: str, - commission_rate: float | NotGiven = NOT_GIVEN, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/marketing/affiliates", - body={ - "user_id": user_id, - "commission_rate": commission_rate, - "metadata": metadata, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - affiliate_id: str, - *, - commission_rate: float | NotGiven = NOT_GIVEN, - status: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._put( - f"/marketing/affiliates/{affiliate_id}", - body={"commission_rate": commission_rate, "status": status}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - affiliate_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/marketing/affiliates/{affiliate_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stats( - self, - affiliate_id: str, - *, - start_date: str | NotGiven = NOT_GIVEN, - end_date: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/marketing/affiliates/{affiliate_id}/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"start_date": start_date, "end_date": end_date}, - ), - cast_to=object, - ) - - async def payouts( - self, - affiliate_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/marketing/affiliates/{affiliate_id}/payouts", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AffiliatesResourceWithRawResponse: - def __init__(self, affiliates: AffiliatesResource) -> None: - self._affiliates = affiliates - self.list = to_raw_response_wrapper(affiliates.list) - self.get = to_raw_response_wrapper(affiliates.get) - self.create = to_raw_response_wrapper(affiliates.create) - self.update = to_raw_response_wrapper(affiliates.update) - self.delete = to_raw_response_wrapper(affiliates.delete) - self.stats = to_raw_response_wrapper(affiliates.stats) - self.payouts = to_raw_response_wrapper(affiliates.payouts) - - -class AsyncAffiliatesResourceWithRawResponse: - def __init__(self, affiliates: AsyncAffiliatesResource) -> None: - self._affiliates = affiliates - self.list = async_to_raw_response_wrapper(affiliates.list) - self.get = async_to_raw_response_wrapper(affiliates.get) - self.create = async_to_raw_response_wrapper(affiliates.create) - self.update = async_to_raw_response_wrapper(affiliates.update) - self.delete = async_to_raw_response_wrapper(affiliates.delete) - self.stats = async_to_raw_response_wrapper(affiliates.stats) - self.payouts = async_to_raw_response_wrapper(affiliates.payouts) - - -class AffiliatesResourceWithStreamingResponse: - def __init__(self, affiliates: AffiliatesResource) -> None: - self._affiliates = affiliates - self.list = to_streamed_response_wrapper(affiliates.list) - self.get = to_streamed_response_wrapper(affiliates.get) - self.create = to_streamed_response_wrapper(affiliates.create) - self.update = to_streamed_response_wrapper(affiliates.update) - self.delete = to_streamed_response_wrapper(affiliates.delete) - self.stats = to_streamed_response_wrapper(affiliates.stats) - self.payouts = to_streamed_response_wrapper(affiliates.payouts) - - -class AsyncAffiliatesResourceWithStreamingResponse: - def __init__(self, affiliates: AsyncAffiliatesResource) -> None: - self._affiliates = affiliates - self.list = async_to_streamed_response_wrapper(affiliates.list) - self.get = async_to_streamed_response_wrapper(affiliates.get) - self.create = async_to_streamed_response_wrapper(affiliates.create) - self.update = async_to_streamed_response_wrapper(affiliates.update) - self.delete = async_to_streamed_response_wrapper(affiliates.delete) - self.stats = async_to_streamed_response_wrapper(affiliates.stats) - self.payouts = async_to_streamed_response_wrapper(affiliates.payouts) diff --git a/pkg/hanzoai/resources/agents.py b/pkg/hanzoai/resources/agents.py deleted file mode 100644 index 3679d9450..000000000 --- a/pkg/hanzoai/resources/agents.py +++ /dev/null @@ -1,491 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["AgentsResource", "AsyncAgentsResource"] - - -class AgentsResource(SyncAPIResource): - """AI Agent management.""" - - @cached_property - def with_raw_response(self) -> AgentsResourceWithRawResponse: - return AgentsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AgentsResourceWithStreamingResponse: - return AgentsResourceWithStreamingResponse(self) - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all agents.""" - return self._get( - "/agents", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - agent_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific agent.""" - return self._get( - f"/agents/{agent_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - config: Dict[str, Any], - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new agent.""" - return self._post( - "/agents", - body={"name": name, "config": config, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - agent_id: str, - *, - config: Dict[str, Any] | NotGiven = NOT_GIVEN, - name: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an agent.""" - return self._put( - f"/agents/{agent_id}", - body={"config": config, "name": name, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - agent_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete an agent.""" - return self._delete( - f"/agents/{agent_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def run( - self, - agent_id: str, - *, - prompt: str, - context: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Run an agent with a prompt.""" - return self._post( - f"/agents/{agent_id}/run", - body={"prompt": prompt, "context": context}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stop( - self, - agent_id: str, - run_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Stop a running agent.""" - return self._post( - f"/agents/{agent_id}/runs/{run_id}/stop", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_runs( - self, - agent_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List agent runs.""" - return self._get( - f"/agents/{agent_id}/runs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get_run( - self, - agent_id: str, - run_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific run.""" - return self._get( - f"/agents/{agent_id}/runs/{run_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncAgentsResource(AsyncAPIResource): - """AI Agent management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncAgentsResourceWithRawResponse: - return AsyncAgentsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncAgentsResourceWithStreamingResponse: - return AsyncAgentsResourceWithStreamingResponse(self) - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/agents", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - agent_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/agents/{agent_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - config: Dict[str, Any], - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/agents", - body={"name": name, "config": config, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - agent_id: str, - *, - config: Dict[str, Any] | NotGiven = NOT_GIVEN, - name: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._put( - f"/agents/{agent_id}", - body={"config": config, "name": name, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - agent_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/agents/{agent_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def run( - self, - agent_id: str, - *, - prompt: str, - context: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/agents/{agent_id}/run", - body={"prompt": prompt, "context": context}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stop( - self, - agent_id: str, - run_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/agents/{agent_id}/runs/{run_id}/stop", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_runs( - self, - agent_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/agents/{agent_id}/runs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get_run( - self, - agent_id: str, - run_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/agents/{agent_id}/runs/{run_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AgentsResourceWithRawResponse: - def __init__(self, agents: AgentsResource) -> None: - self._agents = agents - self.list = to_raw_response_wrapper(agents.list) - self.get = to_raw_response_wrapper(agents.get) - self.create = to_raw_response_wrapper(agents.create) - self.update = to_raw_response_wrapper(agents.update) - self.delete = to_raw_response_wrapper(agents.delete) - self.run = to_raw_response_wrapper(agents.run) - self.stop = to_raw_response_wrapper(agents.stop) - self.list_runs = to_raw_response_wrapper(agents.list_runs) - self.get_run = to_raw_response_wrapper(agents.get_run) - - -class AsyncAgentsResourceWithRawResponse: - def __init__(self, agents: AsyncAgentsResource) -> None: - self._agents = agents - self.list = async_to_raw_response_wrapper(agents.list) - self.get = async_to_raw_response_wrapper(agents.get) - self.create = async_to_raw_response_wrapper(agents.create) - self.update = async_to_raw_response_wrapper(agents.update) - self.delete = async_to_raw_response_wrapper(agents.delete) - self.run = async_to_raw_response_wrapper(agents.run) - self.stop = async_to_raw_response_wrapper(agents.stop) - self.list_runs = async_to_raw_response_wrapper(agents.list_runs) - self.get_run = async_to_raw_response_wrapper(agents.get_run) - - -class AgentsResourceWithStreamingResponse: - def __init__(self, agents: AgentsResource) -> None: - self._agents = agents - self.list = to_streamed_response_wrapper(agents.list) - self.get = to_streamed_response_wrapper(agents.get) - self.create = to_streamed_response_wrapper(agents.create) - self.update = to_streamed_response_wrapper(agents.update) - self.delete = to_streamed_response_wrapper(agents.delete) - self.run = to_streamed_response_wrapper(agents.run) - self.stop = to_streamed_response_wrapper(agents.stop) - self.list_runs = to_streamed_response_wrapper(agents.list_runs) - self.get_run = to_streamed_response_wrapper(agents.get_run) - - -class AsyncAgentsResourceWithStreamingResponse: - def __init__(self, agents: AsyncAgentsResource) -> None: - self._agents = agents - self.list = async_to_streamed_response_wrapper(agents.list) - self.get = async_to_streamed_response_wrapper(agents.get) - self.create = async_to_streamed_response_wrapper(agents.create) - self.update = async_to_streamed_response_wrapper(agents.update) - self.delete = async_to_streamed_response_wrapper(agents.delete) - self.run = async_to_streamed_response_wrapper(agents.run) - self.stop = async_to_streamed_response_wrapper(agents.stop) - self.list_runs = async_to_streamed_response_wrapper(agents.list_runs) - self.get_run = async_to_streamed_response_wrapper(agents.get_run) diff --git a/pkg/hanzoai/resources/audio/__init__.py b/pkg/hanzoai/resources/audio/__init__.py deleted file mode 100644 index 64b642e82..000000000 --- a/pkg/hanzoai/resources/audio/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# Hanzo AI SDK - -from .audio import ( - AudioResource, - AsyncAudioResource, - AudioResourceWithRawResponse, - AsyncAudioResourceWithRawResponse, - AudioResourceWithStreamingResponse, - AsyncAudioResourceWithStreamingResponse, -) -from .speech import ( - SpeechResource, - AsyncSpeechResource, - SpeechResourceWithRawResponse, - AsyncSpeechResourceWithRawResponse, - SpeechResourceWithStreamingResponse, - AsyncSpeechResourceWithStreamingResponse, -) -from .transcriptions import ( - TranscriptionsResource, - AsyncTranscriptionsResource, - TranscriptionsResourceWithRawResponse, - AsyncTranscriptionsResourceWithRawResponse, - TranscriptionsResourceWithStreamingResponse, - AsyncTranscriptionsResourceWithStreamingResponse, -) - -__all__ = [ - "SpeechResource", - "AsyncSpeechResource", - "SpeechResourceWithRawResponse", - "AsyncSpeechResourceWithRawResponse", - "SpeechResourceWithStreamingResponse", - "AsyncSpeechResourceWithStreamingResponse", - "TranscriptionsResource", - "AsyncTranscriptionsResource", - "TranscriptionsResourceWithRawResponse", - "AsyncTranscriptionsResourceWithRawResponse", - "TranscriptionsResourceWithStreamingResponse", - "AsyncTranscriptionsResourceWithStreamingResponse", - "AudioResource", - "AsyncAudioResource", - "AudioResourceWithRawResponse", - "AsyncAudioResourceWithRawResponse", - "AudioResourceWithStreamingResponse", - "AsyncAudioResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/audit.py b/pkg/hanzoai/resources/audit.py deleted file mode 100644 index 859c9571f..000000000 --- a/pkg/hanzoai/resources/audit.py +++ /dev/null @@ -1,216 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class AuditResource(SyncAPIResource): - """Audit logging and compliance.""" - - @cached_property - def with_raw_response(self) -> AuditResourceWithRawResponse: - return AuditResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AuditResourceWithStreamingResponse: - return AuditResourceWithStreamingResponse(self) - - def events( - self, - *, - since: str | NotGiven = NOT_GIVEN, - until: str | NotGiven = NOT_GIVEN, - actor: str | NotGiven = NOT_GIVEN, - resource: str | NotGiven = NOT_GIVEN, - action: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List audit events.""" - return self._get( - "/audit/events", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "since": since, - "until": until, - "actor": actor, - "resource": resource, - "action": action, - "limit": limit, - }, - ), - cast_to=object, - ) - - def tail( - self, - *, - follow: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Tail audit events in real-time.""" - return self._get( - "/audit/tail", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"follow": follow}, - ), - cast_to=object, - ) - - def export( - self, - *, - since: str, - until: str, - format: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Export audit logs.""" - return self._post( - "/audit/export", - body={"since": since, "until": until, "format": format}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncAuditResource(AsyncAPIResource): - """Audit logging and compliance.""" - - @cached_property - def with_raw_response(self) -> AsyncAuditResourceWithRawResponse: - return AsyncAuditResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncAuditResourceWithStreamingResponse: - return AsyncAuditResourceWithStreamingResponse(self) - - async def events( - self, - *, - since: str | NotGiven = NOT_GIVEN, - until: str | NotGiven = NOT_GIVEN, - actor: str | NotGiven = NOT_GIVEN, - resource: str | NotGiven = NOT_GIVEN, - action: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List audit events.""" - return await self._get( - "/audit/events", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "since": since, - "until": until, - "actor": actor, - "resource": resource, - "action": action, - "limit": limit, - }, - ), - cast_to=object, - ) - - async def tail( - self, - *, - follow: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Tail audit events in real-time.""" - return await self._get( - "/audit/tail", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"follow": follow}, - ), - cast_to=object, - ) - - async def export( - self, - *, - since: str, - until: str, - format: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Export audit logs.""" - return await self._post( - "/audit/export", - body={"since": since, "until": until, "format": format}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AuditResourceWithRawResponse: - def __init__(self, audit: AuditResource) -> None: - self._audit = audit - - -class AsyncAuditResourceWithRawResponse: - def __init__(self, audit: AsyncAuditResource) -> None: - self._audit = audit - - -class AuditResourceWithStreamingResponse: - def __init__(self, audit: AuditResource) -> None: - self._audit = audit - - -class AsyncAuditResourceWithStreamingResponse: - def __init__(self, audit: AsyncAuditResource) -> None: - self._audit = audit diff --git a/pkg/hanzoai/resources/batches/__init__.py b/pkg/hanzoai/resources/batches/__init__.py deleted file mode 100644 index 8f04465ec..000000000 --- a/pkg/hanzoai/resources/batches/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Hanzo AI SDK - -from .cancel import ( - CancelResource, - AsyncCancelResource, - CancelResourceWithRawResponse, - AsyncCancelResourceWithRawResponse, - CancelResourceWithStreamingResponse, - AsyncCancelResourceWithStreamingResponse, -) -from .batches import ( - BatchesResource, - AsyncBatchesResource, - BatchesResourceWithRawResponse, - AsyncBatchesResourceWithRawResponse, - BatchesResourceWithStreamingResponse, - AsyncBatchesResourceWithStreamingResponse, -) - -__all__ = [ - "CancelResource", - "AsyncCancelResource", - "CancelResourceWithRawResponse", - "AsyncCancelResourceWithRawResponse", - "CancelResourceWithStreamingResponse", - "AsyncCancelResourceWithStreamingResponse", - "BatchesResource", - "AsyncBatchesResource", - "BatchesResourceWithRawResponse", - "AsyncBatchesResourceWithRawResponse", - "BatchesResourceWithStreamingResponse", - "AsyncBatchesResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/batches/cancel.py b/pkg/hanzoai/resources/batches/cancel.py deleted file mode 100644 index 3a8ff267c..000000000 --- a/pkg/hanzoai/resources/batches/cancel.py +++ /dev/null @@ -1,211 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional - -import httpx - -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.batches import cancel_cancel_params - -__all__ = ["CancelResource", "AsyncCancelResource"] - - -class CancelResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> CancelResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return CancelResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> CancelResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return CancelResourceWithStreamingResponse(self) - - def cancel( - self, - batch_id: str, - *, - provider: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Cancel a batch. - - This is the equivalent of POST - https://api.openai.com/v1/batches/{batch_id}/cancel - - Supports Identical Params as: - https://platform.openai.com/docs/api-reference/batch/cancel - - Example Curl - - ``` - curl http://localhost:4000/v1/batches/batch_abc123/cancel -H "Authorization: Bearer sk-1234" -H "Content-Type: application/json" -X POST - - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not batch_id: - raise ValueError( - f"Expected a non-empty value for `batch_id` but received {batch_id!r}" - ) - return self._post( - f"/batches/{batch_id}/cancel", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"provider": provider}, cancel_cancel_params.CancelCancelParams - ), - ), - cast_to=object, - ) - - -class AsyncCancelResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncCancelResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncCancelResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncCancelResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncCancelResourceWithStreamingResponse(self) - - async def cancel( - self, - batch_id: str, - *, - provider: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Cancel a batch. - - This is the equivalent of POST - https://api.openai.com/v1/batches/{batch_id}/cancel - - Supports Identical Params as: - https://platform.openai.com/docs/api-reference/batch/cancel - - Example Curl - - ``` - curl http://localhost:4000/v1/batches/batch_abc123/cancel -H "Authorization: Bearer sk-1234" -H "Content-Type: application/json" -X POST - - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not batch_id: - raise ValueError( - f"Expected a non-empty value for `batch_id` but received {batch_id!r}" - ) - return await self._post( - f"/batches/{batch_id}/cancel", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"provider": provider}, cancel_cancel_params.CancelCancelParams - ), - ), - cast_to=object, - ) - - -class CancelResourceWithRawResponse: - def __init__(self, cancel: CancelResource) -> None: - self._cancel = cancel - - self.cancel = to_raw_response_wrapper( - cancel.cancel, - ) - - -class AsyncCancelResourceWithRawResponse: - def __init__(self, cancel: AsyncCancelResource) -> None: - self._cancel = cancel - - self.cancel = async_to_raw_response_wrapper( - cancel.cancel, - ) - - -class CancelResourceWithStreamingResponse: - def __init__(self, cancel: CancelResource) -> None: - self._cancel = cancel - - self.cancel = to_streamed_response_wrapper( - cancel.cancel, - ) - - -class AsyncCancelResourceWithStreamingResponse: - def __init__(self, cancel: AsyncCancelResource) -> None: - self._cancel = cancel - - self.cancel = async_to_streamed_response_wrapper( - cancel.cancel, - ) diff --git a/pkg/hanzoai/resources/billing.py b/pkg/hanzoai/resources/billing.py deleted file mode 100644 index dae19f59a..000000000 --- a/pkg/hanzoai/resources/billing.py +++ /dev/null @@ -1,1458 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List, Optional - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["BillingResource", "AsyncBillingResource"] - - -class BillingResource(SyncAPIResource): - """Billing and subscription management via Commerce API.""" - - @cached_property - def with_raw_response(self) -> BillingResourceWithRawResponse: - return BillingResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> BillingResourceWithStreamingResponse: - return BillingResourceWithStreamingResponse(self) - - # ------------------------------------------------------------------ - # Balance - # ------------------------------------------------------------------ - - def get_balance( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get current account balance.""" - return self._get( - "/billing/balance", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Usage - # ------------------------------------------------------------------ - - def get_usage( - self, - *, - start_date: str, - end_date: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get usage for a date range.""" - return self._get( - "/billing/usage", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "startDate": start_date, - "endDate": end_date, - }, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Meters - # ------------------------------------------------------------------ - - def list_meters( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all billing meters.""" - return self._get( - "/billing/meters", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get_meter( - self, - *, - meter_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific billing meter.""" - return self._get( - f"/billing/meters/{meter_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Credit Grants - # ------------------------------------------------------------------ - - def list_credit_grants( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all credit grants.""" - return self._get( - "/billing/credit-grants", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_credit_grant( - self, - *, - amount: float, - description: str, - expires_at: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a credit grant.""" - return self._post( - "/billing/credit-grants", - body={ - "amount": amount, - "description": description, - "expires_at": expires_at, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Invoices - # ------------------------------------------------------------------ - - def list_invoices( - self, - *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List invoices.""" - return self._get( - "/billing/invoices", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "limit": limit, - "offset": offset, - }, - ), - cast_to=object, - ) - - def get_invoice( - self, - *, - invoice_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific invoice.""" - return self._get( - f"/billing/invoices/{invoice_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Subscriptions - # ------------------------------------------------------------------ - - def list_subscriptions( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all subscriptions.""" - return self._get( - "/billing/subscriptions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get_subscription( - self, - *, - subscription_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific subscription.""" - return self._get( - f"/billing/subscriptions/{subscription_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_subscription( - self, - *, - plan_id: str, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a subscription.""" - return self._post( - "/billing/subscriptions", - body={ - "plan_id": plan_id, - "metadata": metadata, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def cancel_subscription( - self, - *, - subscription_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Cancel a subscription.""" - return self._delete( - f"/billing/subscriptions/{subscription_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Payment Intents - # ------------------------------------------------------------------ - - def list_payment_intents( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all payment intents.""" - return self._get( - "/billing/payment-intents", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_payment_intent( - self, - *, - amount: float, - currency: str, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a payment intent.""" - return self._post( - "/billing/payment-intents", - body={ - "amount": amount, - "currency": currency, - "metadata": metadata, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Payment Methods - # ------------------------------------------------------------------ - - def list_payment_methods( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all payment methods.""" - return self._get( - "/billing/payment-methods", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def add_payment_method( - self, - *, - type: str, - token: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a payment method.""" - return self._post( - "/billing/payment-methods", - body={ - "type": type, - "token": token, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def remove_payment_method( - self, - *, - payment_method_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove a payment method.""" - return self._delete( - f"/billing/payment-methods/{payment_method_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Plans - # ------------------------------------------------------------------ - - def list_plans( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all available plans.""" - return self._get( - "/billing/plans", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get_plan( - self, - *, - plan_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific plan.""" - return self._get( - f"/billing/plans/{plan_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Spend Alerts - # ------------------------------------------------------------------ - - def list_spend_alerts( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all spend alerts.""" - return self._get( - "/billing/spend-alerts", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_spend_alert( - self, - *, - threshold: float, - notification_channels: List[str], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a spend alert.""" - return self._post( - "/billing/spend-alerts", - body={ - "threshold": threshold, - "notification_channels": notification_channels, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_spend_alert( - self, - *, - alert_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a spend alert.""" - return self._delete( - f"/billing/spend-alerts/{alert_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Portal - # ------------------------------------------------------------------ - - def get_portal_url( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get customer billing portal URL.""" - return self._post( - "/billing/portal", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncBillingResource(AsyncAPIResource): - """Billing and subscription management via Commerce API.""" - - @cached_property - def with_raw_response(self) -> AsyncBillingResourceWithRawResponse: - return AsyncBillingResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncBillingResourceWithStreamingResponse: - return AsyncBillingResourceWithStreamingResponse(self) - - # ------------------------------------------------------------------ - # Balance - # ------------------------------------------------------------------ - - async def get_balance( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get current account balance.""" - return await self._get( - "/billing/balance", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Usage - # ------------------------------------------------------------------ - - async def get_usage( - self, - *, - start_date: str, - end_date: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get usage for a date range.""" - return await self._get( - "/billing/usage", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "startDate": start_date, - "endDate": end_date, - }, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Meters - # ------------------------------------------------------------------ - - async def list_meters( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all billing meters.""" - return await self._get( - "/billing/meters", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get_meter( - self, - *, - meter_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific billing meter.""" - return await self._get( - f"/billing/meters/{meter_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Credit Grants - # ------------------------------------------------------------------ - - async def list_credit_grants( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all credit grants.""" - return await self._get( - "/billing/credit-grants", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_credit_grant( - self, - *, - amount: float, - description: str, - expires_at: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a credit grant.""" - return await self._post( - "/billing/credit-grants", - body={ - "amount": amount, - "description": description, - "expires_at": expires_at, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Invoices - # ------------------------------------------------------------------ - - async def list_invoices( - self, - *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List invoices.""" - return await self._get( - "/billing/invoices", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "limit": limit, - "offset": offset, - }, - ), - cast_to=object, - ) - - async def get_invoice( - self, - *, - invoice_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific invoice.""" - return await self._get( - f"/billing/invoices/{invoice_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Subscriptions - # ------------------------------------------------------------------ - - async def list_subscriptions( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all subscriptions.""" - return await self._get( - "/billing/subscriptions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get_subscription( - self, - *, - subscription_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific subscription.""" - return await self._get( - f"/billing/subscriptions/{subscription_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_subscription( - self, - *, - plan_id: str, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a subscription.""" - return await self._post( - "/billing/subscriptions", - body={ - "plan_id": plan_id, - "metadata": metadata, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def cancel_subscription( - self, - *, - subscription_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Cancel a subscription.""" - return await self._delete( - f"/billing/subscriptions/{subscription_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Payment Intents - # ------------------------------------------------------------------ - - async def list_payment_intents( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all payment intents.""" - return await self._get( - "/billing/payment-intents", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_payment_intent( - self, - *, - amount: float, - currency: str, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a payment intent.""" - return await self._post( - "/billing/payment-intents", - body={ - "amount": amount, - "currency": currency, - "metadata": metadata, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Payment Methods - # ------------------------------------------------------------------ - - async def list_payment_methods( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all payment methods.""" - return await self._get( - "/billing/payment-methods", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def add_payment_method( - self, - *, - type: str, - token: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a payment method.""" - return await self._post( - "/billing/payment-methods", - body={ - "type": type, - "token": token, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def remove_payment_method( - self, - *, - payment_method_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove a payment method.""" - return await self._delete( - f"/billing/payment-methods/{payment_method_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Plans - # ------------------------------------------------------------------ - - async def list_plans( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all available plans.""" - return await self._get( - "/billing/plans", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get_plan( - self, - *, - plan_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific plan.""" - return await self._get( - f"/billing/plans/{plan_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Spend Alerts - # ------------------------------------------------------------------ - - async def list_spend_alerts( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all spend alerts.""" - return await self._get( - "/billing/spend-alerts", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_spend_alert( - self, - *, - threshold: float, - notification_channels: List[str], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a spend alert.""" - return await self._post( - "/billing/spend-alerts", - body={ - "threshold": threshold, - "notification_channels": notification_channels, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_spend_alert( - self, - *, - alert_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a spend alert.""" - return await self._delete( - f"/billing/spend-alerts/{alert_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Portal - # ------------------------------------------------------------------ - - async def get_portal_url( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get customer billing portal URL.""" - return await self._post( - "/billing/portal", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class BillingResourceWithRawResponse: - def __init__(self, billing: BillingResource) -> None: - self._billing = billing - - self.get_balance = to_raw_response_wrapper( - billing.get_balance, - ) - self.get_usage = to_raw_response_wrapper( - billing.get_usage, - ) - self.list_meters = to_raw_response_wrapper( - billing.list_meters, - ) - self.get_meter = to_raw_response_wrapper( - billing.get_meter, - ) - self.list_credit_grants = to_raw_response_wrapper( - billing.list_credit_grants, - ) - self.create_credit_grant = to_raw_response_wrapper( - billing.create_credit_grant, - ) - self.list_invoices = to_raw_response_wrapper( - billing.list_invoices, - ) - self.get_invoice = to_raw_response_wrapper( - billing.get_invoice, - ) - self.list_subscriptions = to_raw_response_wrapper( - billing.list_subscriptions, - ) - self.get_subscription = to_raw_response_wrapper( - billing.get_subscription, - ) - self.create_subscription = to_raw_response_wrapper( - billing.create_subscription, - ) - self.cancel_subscription = to_raw_response_wrapper( - billing.cancel_subscription, - ) - self.list_payment_intents = to_raw_response_wrapper( - billing.list_payment_intents, - ) - self.create_payment_intent = to_raw_response_wrapper( - billing.create_payment_intent, - ) - self.list_payment_methods = to_raw_response_wrapper( - billing.list_payment_methods, - ) - self.add_payment_method = to_raw_response_wrapper( - billing.add_payment_method, - ) - self.remove_payment_method = to_raw_response_wrapper( - billing.remove_payment_method, - ) - self.list_plans = to_raw_response_wrapper( - billing.list_plans, - ) - self.get_plan = to_raw_response_wrapper( - billing.get_plan, - ) - self.list_spend_alerts = to_raw_response_wrapper( - billing.list_spend_alerts, - ) - self.create_spend_alert = to_raw_response_wrapper( - billing.create_spend_alert, - ) - self.delete_spend_alert = to_raw_response_wrapper( - billing.delete_spend_alert, - ) - self.get_portal_url = to_raw_response_wrapper( - billing.get_portal_url, - ) - - -class AsyncBillingResourceWithRawResponse: - def __init__(self, billing: AsyncBillingResource) -> None: - self._billing = billing - - self.get_balance = async_to_raw_response_wrapper( - billing.get_balance, - ) - self.get_usage = async_to_raw_response_wrapper( - billing.get_usage, - ) - self.list_meters = async_to_raw_response_wrapper( - billing.list_meters, - ) - self.get_meter = async_to_raw_response_wrapper( - billing.get_meter, - ) - self.list_credit_grants = async_to_raw_response_wrapper( - billing.list_credit_grants, - ) - self.create_credit_grant = async_to_raw_response_wrapper( - billing.create_credit_grant, - ) - self.list_invoices = async_to_raw_response_wrapper( - billing.list_invoices, - ) - self.get_invoice = async_to_raw_response_wrapper( - billing.get_invoice, - ) - self.list_subscriptions = async_to_raw_response_wrapper( - billing.list_subscriptions, - ) - self.get_subscription = async_to_raw_response_wrapper( - billing.get_subscription, - ) - self.create_subscription = async_to_raw_response_wrapper( - billing.create_subscription, - ) - self.cancel_subscription = async_to_raw_response_wrapper( - billing.cancel_subscription, - ) - self.list_payment_intents = async_to_raw_response_wrapper( - billing.list_payment_intents, - ) - self.create_payment_intent = async_to_raw_response_wrapper( - billing.create_payment_intent, - ) - self.list_payment_methods = async_to_raw_response_wrapper( - billing.list_payment_methods, - ) - self.add_payment_method = async_to_raw_response_wrapper( - billing.add_payment_method, - ) - self.remove_payment_method = async_to_raw_response_wrapper( - billing.remove_payment_method, - ) - self.list_plans = async_to_raw_response_wrapper( - billing.list_plans, - ) - self.get_plan = async_to_raw_response_wrapper( - billing.get_plan, - ) - self.list_spend_alerts = async_to_raw_response_wrapper( - billing.list_spend_alerts, - ) - self.create_spend_alert = async_to_raw_response_wrapper( - billing.create_spend_alert, - ) - self.delete_spend_alert = async_to_raw_response_wrapper( - billing.delete_spend_alert, - ) - self.get_portal_url = async_to_raw_response_wrapper( - billing.get_portal_url, - ) - - -class BillingResourceWithStreamingResponse: - def __init__(self, billing: BillingResource) -> None: - self._billing = billing - - self.get_balance = to_streamed_response_wrapper( - billing.get_balance, - ) - self.get_usage = to_streamed_response_wrapper( - billing.get_usage, - ) - self.list_meters = to_streamed_response_wrapper( - billing.list_meters, - ) - self.get_meter = to_streamed_response_wrapper( - billing.get_meter, - ) - self.list_credit_grants = to_streamed_response_wrapper( - billing.list_credit_grants, - ) - self.create_credit_grant = to_streamed_response_wrapper( - billing.create_credit_grant, - ) - self.list_invoices = to_streamed_response_wrapper( - billing.list_invoices, - ) - self.get_invoice = to_streamed_response_wrapper( - billing.get_invoice, - ) - self.list_subscriptions = to_streamed_response_wrapper( - billing.list_subscriptions, - ) - self.get_subscription = to_streamed_response_wrapper( - billing.get_subscription, - ) - self.create_subscription = to_streamed_response_wrapper( - billing.create_subscription, - ) - self.cancel_subscription = to_streamed_response_wrapper( - billing.cancel_subscription, - ) - self.list_payment_intents = to_streamed_response_wrapper( - billing.list_payment_intents, - ) - self.create_payment_intent = to_streamed_response_wrapper( - billing.create_payment_intent, - ) - self.list_payment_methods = to_streamed_response_wrapper( - billing.list_payment_methods, - ) - self.add_payment_method = to_streamed_response_wrapper( - billing.add_payment_method, - ) - self.remove_payment_method = to_streamed_response_wrapper( - billing.remove_payment_method, - ) - self.list_plans = to_streamed_response_wrapper( - billing.list_plans, - ) - self.get_plan = to_streamed_response_wrapper( - billing.get_plan, - ) - self.list_spend_alerts = to_streamed_response_wrapper( - billing.list_spend_alerts, - ) - self.create_spend_alert = to_streamed_response_wrapper( - billing.create_spend_alert, - ) - self.delete_spend_alert = to_streamed_response_wrapper( - billing.delete_spend_alert, - ) - self.get_portal_url = to_streamed_response_wrapper( - billing.get_portal_url, - ) - - -class AsyncBillingResourceWithStreamingResponse: - def __init__(self, billing: AsyncBillingResource) -> None: - self._billing = billing - - self.get_balance = async_to_streamed_response_wrapper( - billing.get_balance, - ) - self.get_usage = async_to_streamed_response_wrapper( - billing.get_usage, - ) - self.list_meters = async_to_streamed_response_wrapper( - billing.list_meters, - ) - self.get_meter = async_to_streamed_response_wrapper( - billing.get_meter, - ) - self.list_credit_grants = async_to_streamed_response_wrapper( - billing.list_credit_grants, - ) - self.create_credit_grant = async_to_streamed_response_wrapper( - billing.create_credit_grant, - ) - self.list_invoices = async_to_streamed_response_wrapper( - billing.list_invoices, - ) - self.get_invoice = async_to_streamed_response_wrapper( - billing.get_invoice, - ) - self.list_subscriptions = async_to_streamed_response_wrapper( - billing.list_subscriptions, - ) - self.get_subscription = async_to_streamed_response_wrapper( - billing.get_subscription, - ) - self.create_subscription = async_to_streamed_response_wrapper( - billing.create_subscription, - ) - self.cancel_subscription = async_to_streamed_response_wrapper( - billing.cancel_subscription, - ) - self.list_payment_intents = async_to_streamed_response_wrapper( - billing.list_payment_intents, - ) - self.create_payment_intent = async_to_streamed_response_wrapper( - billing.create_payment_intent, - ) - self.list_payment_methods = async_to_streamed_response_wrapper( - billing.list_payment_methods, - ) - self.add_payment_method = async_to_streamed_response_wrapper( - billing.add_payment_method, - ) - self.remove_payment_method = async_to_streamed_response_wrapper( - billing.remove_payment_method, - ) - self.list_plans = async_to_streamed_response_wrapper( - billing.list_plans, - ) - self.get_plan = async_to_streamed_response_wrapper( - billing.get_plan, - ) - self.list_spend_alerts = async_to_streamed_response_wrapper( - billing.list_spend_alerts, - ) - self.create_spend_alert = async_to_streamed_response_wrapper( - billing.create_spend_alert, - ) - self.delete_spend_alert = async_to_streamed_response_wrapper( - billing.delete_spend_alert, - ) - self.get_portal_url = async_to_streamed_response_wrapper( - billing.get_portal_url, - ) diff --git a/pkg/hanzoai/resources/build.py b/pkg/hanzoai/resources/build.py deleted file mode 100644 index a45f20903..000000000 --- a/pkg/hanzoai/resources/build.py +++ /dev/null @@ -1,412 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class BuildResource(SyncAPIResource): - """Build service for remote/local builds.""" - - @cached_property - def with_raw_response(self) -> BuildResourceWithRawResponse: - return BuildResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> BuildResourceWithStreamingResponse: - return BuildResourceWithStreamingResponse(self) - - def create( - self, - *, - source: str, - dockerfile: str | NotGiven = NOT_GIVEN, - target: str | NotGiven = NOT_GIVEN, - tags: List[str] | NotGiven = NOT_GIVEN, - build_args: Dict[str, str] | NotGiven = NOT_GIVEN, - cache: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Start a build.""" - return self._post( - "/build", - body={ - "source": source, - "dockerfile": dockerfile, - "target": target, - "tags": tags, - "build_args": build_args, - "cache": cache, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List builds.""" - return self._get( - "/build", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - build_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get build details.""" - return self._get( - f"/build/{build_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def logs( - self, - build_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get build logs.""" - return self._get( - f"/build/{build_id}/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def cancel( - self, - build_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Cancel a build.""" - return self._post( - f"/build/{build_id}/cancel", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def cache_list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List build cache.""" - return self._get( - "/build/cache", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def cache_prune( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Prune build cache.""" - return self._post( - "/build/cache/prune", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def provenance( - self, - build_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get build provenance/SBOM.""" - return self._get( - f"/build/{build_id}/provenance", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncBuildResource(AsyncAPIResource): - """Build service for remote/local builds.""" - - @cached_property - def with_raw_response(self) -> AsyncBuildResourceWithRawResponse: - return AsyncBuildResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncBuildResourceWithStreamingResponse: - return AsyncBuildResourceWithStreamingResponse(self) - - async def create( - self, - *, - source: str, - dockerfile: str | NotGiven = NOT_GIVEN, - target: str | NotGiven = NOT_GIVEN, - tags: List[str] | NotGiven = NOT_GIVEN, - build_args: Dict[str, str] | NotGiven = NOT_GIVEN, - cache: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Start a build.""" - return await self._post( - "/build", - body={ - "source": source, - "dockerfile": dockerfile, - "target": target, - "tags": tags, - "build_args": build_args, - "cache": cache, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List builds.""" - return await self._get( - "/build", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - build_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get build details.""" - return await self._get( - f"/build/{build_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def logs( - self, - build_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get build logs.""" - return await self._get( - f"/build/{build_id}/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def cancel( - self, - build_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Cancel a build.""" - return await self._post( - f"/build/{build_id}/cancel", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def cache_list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List build cache.""" - return await self._get( - "/build/cache", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def cache_prune( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Prune build cache.""" - return await self._post( - "/build/cache/prune", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def provenance( - self, - build_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get build provenance/SBOM.""" - return await self._get( - f"/build/{build_id}/provenance", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class BuildResourceWithRawResponse: - def __init__(self, build: BuildResource) -> None: - self._build = build - - -class AsyncBuildResourceWithRawResponse: - def __init__(self, build: AsyncBuildResource) -> None: - self._build = build - - -class BuildResourceWithStreamingResponse: - def __init__(self, build: BuildResource) -> None: - self._build = build - - -class AsyncBuildResourceWithStreamingResponse: - def __init__(self, build: AsyncBuildResource) -> None: - self._build = build diff --git a/pkg/hanzoai/resources/cache/__init__.py b/pkg/hanzoai/resources/cache/__init__.py deleted file mode 100644 index 33fd00d21..000000000 --- a/pkg/hanzoai/resources/cache/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Hanzo AI SDK - -from .cache import ( - CacheResource, - AsyncCacheResource, - CacheResourceWithRawResponse, - AsyncCacheResourceWithRawResponse, - CacheResourceWithStreamingResponse, - AsyncCacheResourceWithStreamingResponse, -) -from .redis import ( - RedisResource, - AsyncRedisResource, - RedisResourceWithRawResponse, - AsyncRedisResourceWithRawResponse, - RedisResourceWithStreamingResponse, - AsyncRedisResourceWithStreamingResponse, -) - -__all__ = [ - "RedisResource", - "AsyncRedisResource", - "RedisResourceWithRawResponse", - "AsyncRedisResourceWithRawResponse", - "RedisResourceWithStreamingResponse", - "AsyncRedisResourceWithStreamingResponse", - "CacheResource", - "AsyncCacheResource", - "CacheResourceWithRawResponse", - "AsyncCacheResourceWithRawResponse", - "CacheResourceWithStreamingResponse", - "AsyncCacheResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/cache/cache.py b/pkg/hanzoai/resources/cache/cache.py deleted file mode 100644 index 023fa0be2..000000000 --- a/pkg/hanzoai/resources/cache/cache.py +++ /dev/null @@ -1,335 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -import httpx - -from .redis import ( - RedisResource, - AsyncRedisResource, - RedisResourceWithRawResponse, - AsyncRedisResourceWithRawResponse, - RedisResourceWithStreamingResponse, - AsyncRedisResourceWithStreamingResponse, -) -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.cache_ping_response import CachePingResponse - -__all__ = ["CacheResource", "AsyncCacheResource"] - - -class CacheResource(SyncAPIResource): - @cached_property - def redis(self) -> RedisResource: - return RedisResource(self._client) - - @cached_property - def with_raw_response(self) -> CacheResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return CacheResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> CacheResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return CacheResourceWithStreamingResponse(self) - - def delete( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Endpoint for deleting a key from the cache. - - All responses from hanzo proxy - have `x-cache-key` in the headers - - Parameters: - - - **keys**: _Optional[List[str]]_ - A list of keys to delete from the cache. - Example {"keys": ["key1", "key2"]} - - ```shell - curl -X POST "http://0.0.0.0:4000/cache/delete" -H "Authorization: Bearer sk-1234" -d '{"keys": ["key1", "key2"]}' - ``` - """ - return self._post( - "/cache/delete", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def flush_all( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """A function to flush all items from the cache. - - (All items will be deleted from - the cache with this) Raises HTTPException if the cache is not initialized or if - the cache type does not support flushing. Returns a dictionary with the status - of the operation. - - Usage: - - ``` - curl -X POST http://0.0.0.0:4000/cache/flushall -H "Authorization: Bearer sk-1234" - ``` - """ - return self._post( - "/cache/flushall", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def ping( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> CachePingResponse: - """Endpoint for checking if cache can be pinged""" - return self._get( - "/cache/ping", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=CachePingResponse, - ) - - -class AsyncCacheResource(AsyncAPIResource): - @cached_property - def redis(self) -> AsyncRedisResource: - return AsyncRedisResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncCacheResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncCacheResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncCacheResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncCacheResourceWithStreamingResponse(self) - - async def delete( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Endpoint for deleting a key from the cache. - - All responses from hanzo proxy - have `x-cache-key` in the headers - - Parameters: - - - **keys**: _Optional[List[str]]_ - A list of keys to delete from the cache. - Example {"keys": ["key1", "key2"]} - - ```shell - curl -X POST "http://0.0.0.0:4000/cache/delete" -H "Authorization: Bearer sk-1234" -d '{"keys": ["key1", "key2"]}' - ``` - """ - return await self._post( - "/cache/delete", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def flush_all( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """A function to flush all items from the cache. - - (All items will be deleted from - the cache with this) Raises HTTPException if the cache is not initialized or if - the cache type does not support flushing. Returns a dictionary with the status - of the operation. - - Usage: - - ``` - curl -X POST http://0.0.0.0:4000/cache/flushall -H "Authorization: Bearer sk-1234" - ``` - """ - return await self._post( - "/cache/flushall", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def ping( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> CachePingResponse: - """Endpoint for checking if cache can be pinged""" - return await self._get( - "/cache/ping", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=CachePingResponse, - ) - - -class CacheResourceWithRawResponse: - def __init__(self, cache: CacheResource) -> None: - self._cache = cache - - self.delete = to_raw_response_wrapper( - cache.delete, - ) - self.flush_all = to_raw_response_wrapper( - cache.flush_all, - ) - self.ping = to_raw_response_wrapper( - cache.ping, - ) - - @cached_property - def redis(self) -> RedisResourceWithRawResponse: - return RedisResourceWithRawResponse(self._cache.redis) - - -class AsyncCacheResourceWithRawResponse: - def __init__(self, cache: AsyncCacheResource) -> None: - self._cache = cache - - self.delete = async_to_raw_response_wrapper( - cache.delete, - ) - self.flush_all = async_to_raw_response_wrapper( - cache.flush_all, - ) - self.ping = async_to_raw_response_wrapper( - cache.ping, - ) - - @cached_property - def redis(self) -> AsyncRedisResourceWithRawResponse: - return AsyncRedisResourceWithRawResponse(self._cache.redis) - - -class CacheResourceWithStreamingResponse: - def __init__(self, cache: CacheResource) -> None: - self._cache = cache - - self.delete = to_streamed_response_wrapper( - cache.delete, - ) - self.flush_all = to_streamed_response_wrapper( - cache.flush_all, - ) - self.ping = to_streamed_response_wrapper( - cache.ping, - ) - - @cached_property - def redis(self) -> RedisResourceWithStreamingResponse: - return RedisResourceWithStreamingResponse(self._cache.redis) - - -class AsyncCacheResourceWithStreamingResponse: - def __init__(self, cache: AsyncCacheResource) -> None: - self._cache = cache - - self.delete = async_to_streamed_response_wrapper( - cache.delete, - ) - self.flush_all = async_to_streamed_response_wrapper( - cache.flush_all, - ) - self.ping = async_to_streamed_response_wrapper( - cache.ping, - ) - - @cached_property - def redis(self) -> AsyncRedisResourceWithStreamingResponse: - return AsyncRedisResourceWithStreamingResponse(self._cache.redis) diff --git a/pkg/hanzoai/resources/campaigns.py b/pkg/hanzoai/resources/campaigns.py deleted file mode 100644 index 15e6403e1..000000000 --- a/pkg/hanzoai/resources/campaigns.py +++ /dev/null @@ -1,492 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["CampaignsResource", "AsyncCampaignsResource"] - - -class CampaignsResource(SyncAPIResource): - """Marketing campaign management.""" - - @cached_property - def with_raw_response(self) -> CampaignsResourceWithRawResponse: - return CampaignsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> CampaignsResourceWithStreamingResponse: - return CampaignsResourceWithStreamingResponse(self) - - def list( - self, - *, - status: str | NotGiven = NOT_GIVEN, - type: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all campaigns.""" - return self._get( - "/marketing/campaigns", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "status": status, - "type": type, - "limit": limit, - "offset": offset, - }, - ), - cast_to=object, - ) - - def get( - self, - campaign_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific campaign.""" - return self._get( - f"/marketing/campaigns/{campaign_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - type: str, - start_date: str, - end_date: str | NotGiven = NOT_GIVEN, - budget: float | NotGiven = NOT_GIVEN, - targeting: Dict[str, Any] | NotGiven = NOT_GIVEN, - content: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new campaign.""" - return self._post( - "/marketing/campaigns", - body={ - "name": name, - "type": type, - "start_date": start_date, - "end_date": end_date, - "budget": budget, - "targeting": targeting, - "content": content, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - campaign_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - status: str | NotGiven = NOT_GIVEN, - budget: float | NotGiven = NOT_GIVEN, - targeting: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a campaign.""" - return self._put( - f"/marketing/campaigns/{campaign_id}", - body={ - "name": name, - "status": status, - "budget": budget, - "targeting": targeting, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - campaign_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a campaign.""" - return self._delete( - f"/marketing/campaigns/{campaign_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def start( - self, - campaign_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Start a campaign.""" - return self._post( - f"/marketing/campaigns/{campaign_id}/start", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def pause( - self, - campaign_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Pause a campaign.""" - return self._post( - f"/marketing/campaigns/{campaign_id}/pause", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stats( - self, - campaign_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get campaign statistics.""" - return self._get( - f"/marketing/campaigns/{campaign_id}/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncCampaignsResource(AsyncAPIResource): - """Marketing campaign management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncCampaignsResourceWithRawResponse: - return AsyncCampaignsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncCampaignsResourceWithStreamingResponse: - return AsyncCampaignsResourceWithStreamingResponse(self) - - async def list( - self, - *, - status: str | NotGiven = NOT_GIVEN, - type: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/marketing/campaigns", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "status": status, - "type": type, - "limit": limit, - "offset": offset, - }, - ), - cast_to=object, - ) - - async def get( - self, - campaign_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/marketing/campaigns/{campaign_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - type: str, - start_date: str, - end_date: str | NotGiven = NOT_GIVEN, - budget: float | NotGiven = NOT_GIVEN, - targeting: Dict[str, Any] | NotGiven = NOT_GIVEN, - content: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/marketing/campaigns", - body={ - "name": name, - "type": type, - "start_date": start_date, - "end_date": end_date, - "budget": budget, - "targeting": targeting, - "content": content, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - campaign_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - status: str | NotGiven = NOT_GIVEN, - budget: float | NotGiven = NOT_GIVEN, - targeting: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._put( - f"/marketing/campaigns/{campaign_id}", - body={ - "name": name, - "status": status, - "budget": budget, - "targeting": targeting, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - campaign_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/marketing/campaigns/{campaign_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def start( - self, - campaign_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/marketing/campaigns/{campaign_id}/start", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def pause( - self, - campaign_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/marketing/campaigns/{campaign_id}/pause", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stats( - self, - campaign_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/marketing/campaigns/{campaign_id}/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class CampaignsResourceWithRawResponse: - def __init__(self, campaigns: CampaignsResource) -> None: - self._campaigns = campaigns - self.list = to_raw_response_wrapper(campaigns.list) - self.get = to_raw_response_wrapper(campaigns.get) - self.create = to_raw_response_wrapper(campaigns.create) - self.update = to_raw_response_wrapper(campaigns.update) - self.delete = to_raw_response_wrapper(campaigns.delete) - self.start = to_raw_response_wrapper(campaigns.start) - self.pause = to_raw_response_wrapper(campaigns.pause) - self.stats = to_raw_response_wrapper(campaigns.stats) - - -class AsyncCampaignsResourceWithRawResponse: - def __init__(self, campaigns: AsyncCampaignsResource) -> None: - self._campaigns = campaigns - self.list = async_to_raw_response_wrapper(campaigns.list) - self.get = async_to_raw_response_wrapper(campaigns.get) - self.create = async_to_raw_response_wrapper(campaigns.create) - self.update = async_to_raw_response_wrapper(campaigns.update) - self.delete = async_to_raw_response_wrapper(campaigns.delete) - self.start = async_to_raw_response_wrapper(campaigns.start) - self.pause = async_to_raw_response_wrapper(campaigns.pause) - self.stats = async_to_raw_response_wrapper(campaigns.stats) - - -class CampaignsResourceWithStreamingResponse: - def __init__(self, campaigns: CampaignsResource) -> None: - self._campaigns = campaigns - self.list = to_streamed_response_wrapper(campaigns.list) - self.get = to_streamed_response_wrapper(campaigns.get) - self.create = to_streamed_response_wrapper(campaigns.create) - self.update = to_streamed_response_wrapper(campaigns.update) - self.delete = to_streamed_response_wrapper(campaigns.delete) - self.start = to_streamed_response_wrapper(campaigns.start) - self.pause = to_streamed_response_wrapper(campaigns.pause) - self.stats = to_streamed_response_wrapper(campaigns.stats) - - -class AsyncCampaignsResourceWithStreamingResponse: - def __init__(self, campaigns: AsyncCampaignsResource) -> None: - self._campaigns = campaigns - self.list = async_to_streamed_response_wrapper(campaigns.list) - self.get = async_to_streamed_response_wrapper(campaigns.get) - self.create = async_to_streamed_response_wrapper(campaigns.create) - self.update = async_to_streamed_response_wrapper(campaigns.update) - self.delete = async_to_streamed_response_wrapper(campaigns.delete) - self.start = async_to_streamed_response_wrapper(campaigns.start) - self.pause = async_to_streamed_response_wrapper(campaigns.pause) - self.stats = async_to_streamed_response_wrapper(campaigns.stats) diff --git a/pkg/hanzoai/resources/cart.py b/pkg/hanzoai/resources/cart.py deleted file mode 100644 index 6dc077f94..000000000 --- a/pkg/hanzoai/resources/cart.py +++ /dev/null @@ -1,403 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["CartResource", "AsyncCartResource"] - - -class CartResource(SyncAPIResource): - """Shopping cart management.""" - - @cached_property - def with_raw_response(self) -> CartResourceWithRawResponse: - return CartResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> CartResourceWithStreamingResponse: - return CartResourceWithStreamingResponse(self) - - def get( - self, - cart_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get cart contents.""" - return self._get( - f"/commerce/cart/{cart_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new cart.""" - return self._post( - "/commerce/cart", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def add_item( - self, - cart_id: str, - *, - product_id: str, - quantity: int = 1, - variant_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add item to cart.""" - return self._post( - f"/commerce/cart/{cart_id}/items", - body={ - "product_id": product_id, - "quantity": quantity, - "variant_id": variant_id, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_item( - self, - cart_id: str, - item_id: str, - *, - quantity: int, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update cart item quantity.""" - return self._put( - f"/commerce/cart/{cart_id}/items/{item_id}", - body={"quantity": quantity}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def remove_item( - self, - cart_id: str, - item_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove item from cart.""" - return self._delete( - f"/commerce/cart/{cart_id}/items/{item_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def clear( - self, - cart_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Clear all items from cart.""" - return self._delete( - f"/commerce/cart/{cart_id}/items", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def apply_coupon( - self, - cart_id: str, - *, - code: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Apply coupon to cart.""" - return self._post( - f"/commerce/cart/{cart_id}/coupon", - body={"code": code}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncCartResource(AsyncAPIResource): - """Shopping cart management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncCartResourceWithRawResponse: - return AsyncCartResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncCartResourceWithStreamingResponse: - return AsyncCartResourceWithStreamingResponse(self) - - async def get( - self, - cart_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/cart/{cart_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/cart", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def add_item( - self, - cart_id: str, - *, - product_id: str, - quantity: int = 1, - variant_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/commerce/cart/{cart_id}/items", - body={ - "product_id": product_id, - "quantity": quantity, - "variant_id": variant_id, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_item( - self, - cart_id: str, - item_id: str, - *, - quantity: int, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._put( - f"/commerce/cart/{cart_id}/items/{item_id}", - body={"quantity": quantity}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def remove_item( - self, - cart_id: str, - item_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/commerce/cart/{cart_id}/items/{item_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def clear( - self, - cart_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/commerce/cart/{cart_id}/items", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def apply_coupon( - self, - cart_id: str, - *, - code: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/commerce/cart/{cart_id}/coupon", - body={"code": code}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class CartResourceWithRawResponse: - def __init__(self, cart: CartResource) -> None: - self._cart = cart - self.get = to_raw_response_wrapper(cart.get) - self.create = to_raw_response_wrapper(cart.create) - self.add_item = to_raw_response_wrapper(cart.add_item) - self.update_item = to_raw_response_wrapper(cart.update_item) - self.remove_item = to_raw_response_wrapper(cart.remove_item) - self.clear = to_raw_response_wrapper(cart.clear) - self.apply_coupon = to_raw_response_wrapper(cart.apply_coupon) - - -class AsyncCartResourceWithRawResponse: - def __init__(self, cart: AsyncCartResource) -> None: - self._cart = cart - self.get = async_to_raw_response_wrapper(cart.get) - self.create = async_to_raw_response_wrapper(cart.create) - self.add_item = async_to_raw_response_wrapper(cart.add_item) - self.update_item = async_to_raw_response_wrapper(cart.update_item) - self.remove_item = async_to_raw_response_wrapper(cart.remove_item) - self.clear = async_to_raw_response_wrapper(cart.clear) - self.apply_coupon = async_to_raw_response_wrapper(cart.apply_coupon) - - -class CartResourceWithStreamingResponse: - def __init__(self, cart: CartResource) -> None: - self._cart = cart - self.get = to_streamed_response_wrapper(cart.get) - self.create = to_streamed_response_wrapper(cart.create) - self.add_item = to_streamed_response_wrapper(cart.add_item) - self.update_item = to_streamed_response_wrapper(cart.update_item) - self.remove_item = to_streamed_response_wrapper(cart.remove_item) - self.clear = to_streamed_response_wrapper(cart.clear) - self.apply_coupon = to_streamed_response_wrapper(cart.apply_coupon) - - -class AsyncCartResourceWithStreamingResponse: - def __init__(self, cart: AsyncCartResource) -> None: - self._cart = cart - self.get = async_to_streamed_response_wrapper(cart.get) - self.create = async_to_streamed_response_wrapper(cart.create) - self.add_item = async_to_streamed_response_wrapper(cart.add_item) - self.update_item = async_to_streamed_response_wrapper(cart.update_item) - self.remove_item = async_to_streamed_response_wrapper(cart.remove_item) - self.clear = async_to_streamed_response_wrapper(cart.clear) - self.apply_coupon = async_to_streamed_response_wrapper(cart.apply_coupon) diff --git a/pkg/hanzoai/resources/chain.py b/pkg/hanzoai/resources/chain.py deleted file mode 100644 index 8e2d19c71..000000000 --- a/pkg/hanzoai/resources/chain.py +++ /dev/null @@ -1,310 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class ChainResource(SyncAPIResource): - """Blockchain chain operations and status.""" - - @cached_property - def with_raw_response(self) -> ChainResourceWithRawResponse: - return ChainResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ChainResourceWithStreamingResponse: - return ChainResourceWithStreamingResponse(self) - - def status( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get chain status.""" - return self._get( - "/chain/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def rpc( - self, - *, - method: str, - params: List[Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Execute RPC call.""" - return self._post( - "/chain/rpc", - body={"method": method, "params": params}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def validators( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List validators.""" - return self._get( - "/chain/validators", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def params( - self, - param_name: str | NotGiven = NOT_GIVEN, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get chain parameters.""" - return self._get( - "/chain/params", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"param_name": param_name}, - ), - cast_to=object, - ) - - def block( - self, - block_number: int | str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get block by number.""" - return self._get( - f"/chain/blocks/{block_number}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def transaction( - self, - tx_hash: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get transaction by hash.""" - return self._get( - f"/chain/tx/{tx_hash}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncChainResource(AsyncAPIResource): - """Blockchain chain operations and status.""" - - @cached_property - def with_raw_response(self) -> AsyncChainResourceWithRawResponse: - return AsyncChainResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncChainResourceWithStreamingResponse: - return AsyncChainResourceWithStreamingResponse(self) - - async def status( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get chain status.""" - return await self._get( - "/chain/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def rpc( - self, - *, - method: str, - params: List[Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Execute RPC call.""" - return await self._post( - "/chain/rpc", - body={"method": method, "params": params}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def validators( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List validators.""" - return await self._get( - "/chain/validators", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def params( - self, - param_name: str | NotGiven = NOT_GIVEN, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get chain parameters.""" - return await self._get( - "/chain/params", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"param_name": param_name}, - ), - cast_to=object, - ) - - async def block( - self, - block_number: int | str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get block by number.""" - return await self._get( - f"/chain/blocks/{block_number}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def transaction( - self, - tx_hash: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get transaction by hash.""" - return await self._get( - f"/chain/tx/{tx_hash}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class ChainResourceWithRawResponse: - def __init__(self, chain: ChainResource) -> None: - self._chain = chain - - -class AsyncChainResourceWithRawResponse: - def __init__(self, chain: AsyncChainResource) -> None: - self._chain = chain - - -class ChainResourceWithStreamingResponse: - def __init__(self, chain: ChainResource) -> None: - self._chain = chain - - -class AsyncChainResourceWithStreamingResponse: - def __init__(self, chain: AsyncChainResource) -> None: - self._chain = chain diff --git a/pkg/hanzoai/resources/chat/__init__.py b/pkg/hanzoai/resources/chat/__init__.py deleted file mode 100644 index 9ee0a11fc..000000000 --- a/pkg/hanzoai/resources/chat/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Hanzo AI SDK - -from .chat import ( - ChatResource, - AsyncChatResource, - ChatResourceWithRawResponse, - AsyncChatResourceWithRawResponse, - ChatResourceWithStreamingResponse, - AsyncChatResourceWithStreamingResponse, -) -from .completions import ( - CompletionsResource, - AsyncCompletionsResource, - CompletionsResourceWithRawResponse, - AsyncCompletionsResourceWithRawResponse, - CompletionsResourceWithStreamingResponse, - AsyncCompletionsResourceWithStreamingResponse, -) - -__all__ = [ - "CompletionsResource", - "AsyncCompletionsResource", - "CompletionsResourceWithRawResponse", - "AsyncCompletionsResourceWithRawResponse", - "CompletionsResourceWithStreamingResponse", - "AsyncCompletionsResourceWithStreamingResponse", - "ChatResource", - "AsyncChatResource", - "ChatResourceWithRawResponse", - "AsyncChatResourceWithRawResponse", - "ChatResourceWithStreamingResponse", - "AsyncChatResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/chat/chat.py b/pkg/hanzoai/resources/chat/chat.py deleted file mode 100644 index 1165fa09c..000000000 --- a/pkg/hanzoai/resources/chat/chat.py +++ /dev/null @@ -1,102 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from .completions import ( - CompletionsResource, - AsyncCompletionsResource, - CompletionsResourceWithRawResponse, - AsyncCompletionsResourceWithRawResponse, - CompletionsResourceWithStreamingResponse, - AsyncCompletionsResourceWithStreamingResponse, -) - -__all__ = ["ChatResource", "AsyncChatResource"] - - -class ChatResource(SyncAPIResource): - @cached_property - def completions(self) -> CompletionsResource: - return CompletionsResource(self._client) - - @cached_property - def with_raw_response(self) -> ChatResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return ChatResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ChatResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return ChatResourceWithStreamingResponse(self) - - -class AsyncChatResource(AsyncAPIResource): - @cached_property - def completions(self) -> AsyncCompletionsResource: - return AsyncCompletionsResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncChatResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncChatResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncChatResourceWithStreamingResponse(self) - - -class ChatResourceWithRawResponse: - def __init__(self, chat: ChatResource) -> None: - self._chat = chat - - @cached_property - def completions(self) -> CompletionsResourceWithRawResponse: - return CompletionsResourceWithRawResponse(self._chat.completions) - - -class AsyncChatResourceWithRawResponse: - def __init__(self, chat: AsyncChatResource) -> None: - self._chat = chat - - @cached_property - def completions(self) -> AsyncCompletionsResourceWithRawResponse: - return AsyncCompletionsResourceWithRawResponse(self._chat.completions) - - -class ChatResourceWithStreamingResponse: - def __init__(self, chat: ChatResource) -> None: - self._chat = chat - - @cached_property - def completions(self) -> CompletionsResourceWithStreamingResponse: - return CompletionsResourceWithStreamingResponse(self._chat.completions) - - -class AsyncChatResourceWithStreamingResponse: - def __init__(self, chat: AsyncChatResource) -> None: - self._chat = chat - - @cached_property - def completions(self) -> AsyncCompletionsResourceWithStreamingResponse: - return AsyncCompletionsResourceWithStreamingResponse(self._chat.completions) diff --git a/pkg/hanzoai/resources/chat/completions.py b/pkg/hanzoai/resources/chat/completions.py deleted file mode 100644 index 7f1f62074..000000000 --- a/pkg/hanzoai/resources/chat/completions.py +++ /dev/null @@ -1,209 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional - -import httpx - -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...types.chat import completion_create_params -from ..._base_client import make_request_options - -__all__ = ["CompletionsResource", "AsyncCompletionsResource"] - - -class CompletionsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> CompletionsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return CompletionsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> CompletionsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return CompletionsResourceWithStreamingResponse(self) - - def create( - self, - *, - model: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Follows the exact same API spec as - `OpenAI's Chat API https://platform.openai.com/docs/api-reference/chat` - - ```bash - curl -X POST http://localhost:4000/v1/chat/completions - -H "Content-Type: application/json" - -H "Authorization: Bearer sk-1234" - -d '{ - "model": "gpt-4o", - "messages": [ - { - "role": "user", - "content": "Hello!" - } - ] - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/v1/chat/completions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"model": model}, completion_create_params.CompletionCreateParams - ), - ), - cast_to=object, - ) - - -class AsyncCompletionsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncCompletionsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncCompletionsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncCompletionsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncCompletionsResourceWithStreamingResponse(self) - - async def create( - self, - *, - model: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Follows the exact same API spec as - `OpenAI's Chat API https://platform.openai.com/docs/api-reference/chat` - - ```bash - curl -X POST http://localhost:4000/v1/chat/completions - -H "Content-Type: application/json" - -H "Authorization: Bearer sk-1234" - -d '{ - "model": "gpt-4o", - "messages": [ - { - "role": "user", - "content": "Hello!" - } - ] - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/v1/chat/completions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"model": model}, completion_create_params.CompletionCreateParams - ), - ), - cast_to=object, - ) - - -class CompletionsResourceWithRawResponse: - def __init__(self, completions: CompletionsResource) -> None: - self._completions = completions - - self.create = to_raw_response_wrapper( - completions.create, - ) - - -class AsyncCompletionsResourceWithRawResponse: - def __init__(self, completions: AsyncCompletionsResource) -> None: - self._completions = completions - - self.create = async_to_raw_response_wrapper( - completions.create, - ) - - -class CompletionsResourceWithStreamingResponse: - def __init__(self, completions: CompletionsResource) -> None: - self._completions = completions - - self.create = to_streamed_response_wrapper( - completions.create, - ) - - -class AsyncCompletionsResourceWithStreamingResponse: - def __init__(self, completions: AsyncCompletionsResource) -> None: - self._completions = completions - - self.create = async_to_streamed_response_wrapper( - completions.create, - ) diff --git a/pkg/hanzoai/resources/checkout.py b/pkg/hanzoai/resources/checkout.py deleted file mode 100644 index 6ab73f0c6..000000000 --- a/pkg/hanzoai/resources/checkout.py +++ /dev/null @@ -1,317 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["CheckoutResource", "AsyncCheckoutResource"] - - -class CheckoutResource(SyncAPIResource): - """Checkout and payment processing.""" - - @cached_property - def with_raw_response(self) -> CheckoutResourceWithRawResponse: - return CheckoutResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> CheckoutResourceWithStreamingResponse: - return CheckoutResourceWithStreamingResponse(self) - - def create_session( - self, - *, - cart_id: str, - success_url: str, - cancel_url: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a checkout session.""" - return self._post( - "/commerce/checkout/sessions", - body={ - "cart_id": cart_id, - "success_url": success_url, - "cancel_url": cancel_url, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get_session( - self, - session_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get checkout session details.""" - return self._get( - f"/commerce/checkout/sessions/{session_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def complete( - self, - session_id: str, - *, - payment_method: str, - payment_details: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Complete a checkout session.""" - return self._post( - f"/commerce/checkout/sessions/{session_id}/complete", - body={"payment_method": payment_method, "payment_details": payment_details}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def expire( - self, - session_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Expire a checkout session.""" - return self._post( - f"/commerce/checkout/sessions/{session_id}/expire", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_payment_methods( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List available payment methods.""" - return self._get( - "/commerce/checkout/payment-methods", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncCheckoutResource(AsyncAPIResource): - """Checkout and payment processing (async).""" - - @cached_property - def with_raw_response(self) -> AsyncCheckoutResourceWithRawResponse: - return AsyncCheckoutResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncCheckoutResourceWithStreamingResponse: - return AsyncCheckoutResourceWithStreamingResponse(self) - - async def create_session( - self, - *, - cart_id: str, - success_url: str, - cancel_url: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/checkout/sessions", - body={ - "cart_id": cart_id, - "success_url": success_url, - "cancel_url": cancel_url, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get_session( - self, - session_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/checkout/sessions/{session_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def complete( - self, - session_id: str, - *, - payment_method: str, - payment_details: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/commerce/checkout/sessions/{session_id}/complete", - body={"payment_method": payment_method, "payment_details": payment_details}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def expire( - self, - session_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/commerce/checkout/sessions/{session_id}/expire", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_payment_methods( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/checkout/payment-methods", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class CheckoutResourceWithRawResponse: - def __init__(self, checkout: CheckoutResource) -> None: - self._checkout = checkout - self.create_session = to_raw_response_wrapper(checkout.create_session) - self.get_session = to_raw_response_wrapper(checkout.get_session) - self.complete = to_raw_response_wrapper(checkout.complete) - self.expire = to_raw_response_wrapper(checkout.expire) - self.list_payment_methods = to_raw_response_wrapper( - checkout.list_payment_methods - ) - - -class AsyncCheckoutResourceWithRawResponse: - def __init__(self, checkout: AsyncCheckoutResource) -> None: - self._checkout = checkout - self.create_session = async_to_raw_response_wrapper(checkout.create_session) - self.get_session = async_to_raw_response_wrapper(checkout.get_session) - self.complete = async_to_raw_response_wrapper(checkout.complete) - self.expire = async_to_raw_response_wrapper(checkout.expire) - self.list_payment_methods = async_to_raw_response_wrapper( - checkout.list_payment_methods - ) - - -class CheckoutResourceWithStreamingResponse: - def __init__(self, checkout: CheckoutResource) -> None: - self._checkout = checkout - self.create_session = to_streamed_response_wrapper(checkout.create_session) - self.get_session = to_streamed_response_wrapper(checkout.get_session) - self.complete = to_streamed_response_wrapper(checkout.complete) - self.expire = to_streamed_response_wrapper(checkout.expire) - self.list_payment_methods = to_streamed_response_wrapper( - checkout.list_payment_methods - ) - - -class AsyncCheckoutResourceWithStreamingResponse: - def __init__(self, checkout: AsyncCheckoutResource) -> None: - self._checkout = checkout - self.create_session = async_to_streamed_response_wrapper( - checkout.create_session - ) - self.get_session = async_to_streamed_response_wrapper(checkout.get_session) - self.complete = async_to_streamed_response_wrapper(checkout.complete) - self.expire = async_to_streamed_response_wrapper(checkout.expire) - self.list_payment_methods = async_to_streamed_response_wrapper( - checkout.list_payment_methods - ) diff --git a/pkg/hanzoai/resources/commerce.py b/pkg/hanzoai/resources/commerce.py deleted file mode 100644 index bcb4c381f..000000000 --- a/pkg/hanzoai/resources/commerce.py +++ /dev/null @@ -1,3535 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["CommerceResource", "AsyncCommerceResource"] - - -# --------------------------------------------------------------------------- -# Orders -# --------------------------------------------------------------------------- - - -class OrdersResource(SyncAPIResource): - """Order management and payment operations.""" - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List orders.""" - return self._get( - "/commerce/order", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create an order.""" - return self._post( - "/commerce/order", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - order_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get an order by ID.""" - return self._get( - f"/commerce/order/{order_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - order_id: str, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an order.""" - return self._patch( - f"/commerce/order/{order_id}", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - order_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete an order.""" - return self._delete( - f"/commerce/order/{order_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def authorize( - self, - order_id: str, - *, - body: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Authorize payment for an order.""" - return self._post( - f"/commerce/order/{order_id}/authorize", - body=body if not isinstance(body, NotGiven) else None, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def capture( - self, - order_id: str, - *, - body: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Capture payment for an order.""" - return self._post( - f"/commerce/order/{order_id}/capture", - body=body if not isinstance(body, NotGiven) else None, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def charge( - self, - order_id: str, - *, - body: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Charge an order.""" - return self._post( - f"/commerce/order/{order_id}/charge", - body=body if not isinstance(body, NotGiven) else None, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def refund( - self, - order_id: str, - *, - body: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Refund an order.""" - return self._post( - f"/commerce/order/{order_id}/refund", - body=body if not isinstance(body, NotGiven) else None, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def payments( - self, - order_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List payments for an order.""" - return self._get( - f"/commerce/order/{order_id}/payments", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def returns( - self, - order_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List returns for an order.""" - return self._get( - f"/commerce/order/{order_id}/returns", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def status( - self, - order_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get order status.""" - return self._get( - f"/commerce/order/{order_id}/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncOrdersResource(AsyncAPIResource): - """Order management and payment operations (async).""" - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/order", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/order", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - order_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/order/{order_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - order_id: str, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._patch( - f"/commerce/order/{order_id}", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - order_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/commerce/order/{order_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def authorize( - self, - order_id: str, - *, - body: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/commerce/order/{order_id}/authorize", - body=body if not isinstance(body, NotGiven) else None, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def capture( - self, - order_id: str, - *, - body: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/commerce/order/{order_id}/capture", - body=body if not isinstance(body, NotGiven) else None, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def charge( - self, - order_id: str, - *, - body: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/commerce/order/{order_id}/charge", - body=body if not isinstance(body, NotGiven) else None, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def refund( - self, - order_id: str, - *, - body: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/commerce/order/{order_id}/refund", - body=body if not isinstance(body, NotGiven) else None, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def payments( - self, - order_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/order/{order_id}/payments", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def returns( - self, - order_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/order/{order_id}/returns", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def status( - self, - order_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/order/{order_id}/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# --------------------------------------------------------------------------- -# Products -# --------------------------------------------------------------------------- - - -class ProductsResource(SyncAPIResource): - """Product catalog management.""" - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List products.""" - return self._get( - "/commerce/product", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a product.""" - return self._post( - "/commerce/product", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - product_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a product by ID.""" - return self._get( - f"/commerce/product/{product_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - product_id: str, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a product.""" - return self._patch( - f"/commerce/product/{product_id}", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - product_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a product.""" - return self._delete( - f"/commerce/product/{product_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncProductsResource(AsyncAPIResource): - """Product catalog management (async).""" - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/product", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/product", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - product_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/product/{product_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - product_id: str, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._patch( - f"/commerce/product/{product_id}", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - product_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/commerce/product/{product_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# --------------------------------------------------------------------------- -# Variants -# --------------------------------------------------------------------------- - - -class VariantsResource(SyncAPIResource): - """Product variant management.""" - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List variants.""" - return self._get( - "/commerce/variant", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a variant.""" - return self._post( - "/commerce/variant", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - variant_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a variant by ID.""" - return self._get( - f"/commerce/variant/{variant_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - variant_id: str, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a variant.""" - return self._patch( - f"/commerce/variant/{variant_id}", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - variant_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a variant.""" - return self._delete( - f"/commerce/variant/{variant_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncVariantsResource(AsyncAPIResource): - """Product variant management (async).""" - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/variant", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/variant", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - variant_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/variant/{variant_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - variant_id: str, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._patch( - f"/commerce/variant/{variant_id}", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - variant_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/commerce/variant/{variant_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# --------------------------------------------------------------------------- -# Collections -# --------------------------------------------------------------------------- - - -class CollectionsResource(SyncAPIResource): - """Product collection management.""" - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List collections.""" - return self._get( - "/commerce/collection", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a collection.""" - return self._post( - "/commerce/collection", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - collection_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a collection by ID.""" - return self._get( - f"/commerce/collection/{collection_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - collection_id: str, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a collection.""" - return self._patch( - f"/commerce/collection/{collection_id}", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - collection_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a collection.""" - return self._delete( - f"/commerce/collection/{collection_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncCollectionsResource(AsyncAPIResource): - """Product collection management (async).""" - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/collection", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/collection", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - collection_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/collection/{collection_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - collection_id: str, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._patch( - f"/commerce/collection/{collection_id}", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - collection_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/commerce/collection/{collection_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# --------------------------------------------------------------------------- -# Transactions -# --------------------------------------------------------------------------- - - -class TransactionsResource(SyncAPIResource): - """Transaction management.""" - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List transactions.""" - return self._get( - "/commerce/transaction", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a transaction.""" - return self._post( - "/commerce/transaction", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get_by_kind( - self, - kind: str, - transaction_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get transactions by kind and ID.""" - return self._get( - f"/commerce/transaction/{kind}/{transaction_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncTransactionsResource(AsyncAPIResource): - """Transaction management (async).""" - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/transaction", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/transaction", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get_by_kind( - self, - kind: str, - transaction_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/transaction/{kind}/{transaction_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# --------------------------------------------------------------------------- -# Accounts -# --------------------------------------------------------------------------- - - -class AccountsResource(SyncAPIResource): - """Account authentication and management.""" - - def login( - self, - *, - email: str, - password: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Login to an account.""" - return self._post( - "/commerce/account/login", - body={"email": email, "password": password}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new account.""" - return self._post( - "/commerce/account/create", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get current account.""" - return self._get( - "/commerce/account", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def exists( - self, - email: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Check if account exists by email.""" - return self._get( - f"/commerce/account/exists/{email}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncAccountsResource(AsyncAPIResource): - """Account authentication and management (async).""" - - async def login( - self, - *, - email: str, - password: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/account/login", - body={"email": email, "password": password}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/account/create", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/account", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def exists( - self, - email: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/account/exists/{email}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# --------------------------------------------------------------------------- -# Users (admin) -# --------------------------------------------------------------------------- - - -class UsersResource(SyncAPIResource): - """Admin user management.""" - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List users.""" - return self._get( - "/commerce/user", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - user_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a user by ID.""" - return self._get( - f"/commerce/user/{user_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def orders( - self, - user_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get orders for a user.""" - return self._get( - f"/commerce/user/{user_id}/orders", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def transactions( - self, - user_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get transactions for a user.""" - return self._get( - f"/commerce/user/{user_id}/transactions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def wallet( - self, - user_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get wallet for a user.""" - return self._get( - f"/commerce/user/{user_id}/wallet", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncUsersResource(AsyncAPIResource): - """Admin user management (async).""" - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/user", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - user_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/user/{user_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def orders( - self, - user_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/user/{user_id}/orders", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def transactions( - self, - user_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/user/{user_id}/transactions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def wallet( - self, - user_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/user/{user_id}/wallet", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# --------------------------------------------------------------------------- -# Search -# --------------------------------------------------------------------------- - - -class SearchResource(SyncAPIResource): - """Commerce search operations.""" - - def users( - self, - *, - q: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Search users.""" - return self._get( - "/commerce/search/user", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"q": q}, - ), - cast_to=object, - ) - - def orders( - self, - *, - q: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Search orders.""" - return self._get( - "/commerce/search/order", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"q": q}, - ), - cast_to=object, - ) - - -class AsyncSearchResource(AsyncAPIResource): - """Commerce search operations (async).""" - - async def users( - self, - *, - q: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/search/user", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"q": q}, - ), - cast_to=object, - ) - - async def orders( - self, - *, - q: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/search/order", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"q": q}, - ), - cast_to=object, - ) - - -# --------------------------------------------------------------------------- -# Subscriptions -# --------------------------------------------------------------------------- - - -class SubscriptionsResource(SyncAPIResource): - """Subscription management (legacy).""" - - def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a subscription.""" - return self._post( - "/commerce/subscribe", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - subscription_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a subscription by ID.""" - return self._get( - f"/commerce/subscribe/{subscription_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncSubscriptionsResource(AsyncAPIResource): - """Subscription management (legacy, async).""" - - async def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/subscribe", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - subscription_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/subscribe/{subscription_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# --------------------------------------------------------------------------- -# Webhooks -# --------------------------------------------------------------------------- - - -class WebhooksResource(SyncAPIResource): - """Webhook management.""" - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List webhooks.""" - return self._get( - "/commerce/webhook", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a webhook.""" - return self._post( - "/commerce/webhook", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - webhook_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a webhook.""" - return self._delete( - f"/commerce/webhook/{webhook_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncWebhooksResource(AsyncAPIResource): - """Webhook management (async).""" - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/webhook", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/webhook", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - webhook_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/commerce/webhook/{webhook_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# --------------------------------------------------------------------------- -# Stores -# --------------------------------------------------------------------------- - - -class StoresResource(SyncAPIResource): - """Store and storefront management.""" - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List stores.""" - return self._get( - "/commerce/store", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a store.""" - return self._post( - "/commerce/store", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a store by ID.""" - return self._get( - f"/commerce/store/{store_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def product( - self, - store_id: str, - product_key: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a product in a store by key.""" - return self._get( - f"/commerce/store/{store_id}/product/{product_key}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def listings( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get store listings.""" - return self._get( - f"/commerce/store/{store_id}/listing", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncStoresResource(AsyncAPIResource): - """Store and storefront management (async).""" - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/store", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/store", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/store/{store_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def product( - self, - store_id: str, - product_key: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/store/{store_id}/product/{product_key}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def listings( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/store/{store_id}/listing", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# --------------------------------------------------------------------------- -# Discounts -# --------------------------------------------------------------------------- - - -class DiscountsResource(SyncAPIResource): - """Discount management.""" - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List discounts.""" - return self._get( - "/commerce/discount", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a discount.""" - return self._post( - "/commerce/discount", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - discount_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a discount by ID.""" - return self._get( - f"/commerce/discount/{discount_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - discount_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a discount.""" - return self._delete( - f"/commerce/discount/{discount_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncDiscountsResource(AsyncAPIResource): - """Discount management (async).""" - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/discount", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/discount", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - discount_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/discount/{discount_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - discount_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/commerce/discount/{discount_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# --------------------------------------------------------------------------- -# Payments -# --------------------------------------------------------------------------- - - -class PaymentsResource(SyncAPIResource): - """Payment management.""" - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List payments.""" - return self._get( - "/commerce/payment", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a payment.""" - return self._post( - "/commerce/payment", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def refund( - self, - payment_id: str, - *, - body: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Refund a payment.""" - return self._post( - f"/commerce/payment/{payment_id}/refund", - body=body if not isinstance(body, NotGiven) else None, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncPaymentsResource(AsyncAPIResource): - """Payment management (async).""" - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/payment", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - body: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/payment", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def refund( - self, - payment_id: str, - *, - body: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/commerce/payment/{payment_id}/refund", - body=body if not isinstance(body, NotGiven) else None, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# --------------------------------------------------------------------------- -# Top-level Commerce Resource -# --------------------------------------------------------------------------- - - -class CommerceResource(SyncAPIResource): - """Hanzo Commerce API: orders, products, variants, collections, - transactions, accounts, users, search, subscriptions, webhooks, - stores, discounts, and payments.""" - - @cached_property - def orders(self) -> OrdersResource: - return OrdersResource(self._client) - - @cached_property - def products(self) -> ProductsResource: - return ProductsResource(self._client) - - @cached_property - def variants(self) -> VariantsResource: - return VariantsResource(self._client) - - @cached_property - def collections(self) -> CollectionsResource: - return CollectionsResource(self._client) - - @cached_property - def transactions(self) -> TransactionsResource: - return TransactionsResource(self._client) - - @cached_property - def accounts(self) -> AccountsResource: - return AccountsResource(self._client) - - @cached_property - def users(self) -> UsersResource: - return UsersResource(self._client) - - @cached_property - def search(self) -> SearchResource: - return SearchResource(self._client) - - @cached_property - def subscriptions(self) -> SubscriptionsResource: - return SubscriptionsResource(self._client) - - @cached_property - def webhooks(self) -> WebhooksResource: - return WebhooksResource(self._client) - - @cached_property - def stores(self) -> StoresResource: - return StoresResource(self._client) - - @cached_property - def discounts(self) -> DiscountsResource: - return DiscountsResource(self._client) - - @cached_property - def payments(self) -> PaymentsResource: - return PaymentsResource(self._client) - - @cached_property - def with_raw_response(self) -> CommerceResourceWithRawResponse: - return CommerceResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> CommerceResourceWithStreamingResponse: - return CommerceResourceWithStreamingResponse(self) - - def ping( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Health check.""" - return self._get( - "/commerce/ping", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncCommerceResource(AsyncAPIResource): - """Hanzo Commerce API (async): orders, products, variants, collections, - transactions, accounts, users, search, subscriptions, webhooks, - stores, discounts, and payments.""" - - @cached_property - def orders(self) -> AsyncOrdersResource: - return AsyncOrdersResource(self._client) - - @cached_property - def products(self) -> AsyncProductsResource: - return AsyncProductsResource(self._client) - - @cached_property - def variants(self) -> AsyncVariantsResource: - return AsyncVariantsResource(self._client) - - @cached_property - def collections(self) -> AsyncCollectionsResource: - return AsyncCollectionsResource(self._client) - - @cached_property - def transactions(self) -> AsyncTransactionsResource: - return AsyncTransactionsResource(self._client) - - @cached_property - def accounts(self) -> AsyncAccountsResource: - return AsyncAccountsResource(self._client) - - @cached_property - def users(self) -> AsyncUsersResource: - return AsyncUsersResource(self._client) - - @cached_property - def search(self) -> AsyncSearchResource: - return AsyncSearchResource(self._client) - - @cached_property - def subscriptions(self) -> AsyncSubscriptionsResource: - return AsyncSubscriptionsResource(self._client) - - @cached_property - def webhooks(self) -> AsyncWebhooksResource: - return AsyncWebhooksResource(self._client) - - @cached_property - def stores(self) -> AsyncStoresResource: - return AsyncStoresResource(self._client) - - @cached_property - def discounts(self) -> AsyncDiscountsResource: - return AsyncDiscountsResource(self._client) - - @cached_property - def payments(self) -> AsyncPaymentsResource: - return AsyncPaymentsResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncCommerceResourceWithRawResponse: - return AsyncCommerceResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncCommerceResourceWithStreamingResponse: - return AsyncCommerceResourceWithStreamingResponse(self) - - async def ping( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/ping", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# --------------------------------------------------------------------------- -# Raw response wrappers -# --------------------------------------------------------------------------- - - -class CommerceResourceWithRawResponse: - def __init__(self, commerce: CommerceResource) -> None: - self._commerce = commerce - self.ping = to_raw_response_wrapper(commerce.ping) - - @cached_property - def orders(self) -> OrdersResourceWithRawResponse: - return OrdersResourceWithRawResponse(self._commerce.orders) - - @cached_property - def products(self) -> ProductsResourceWithRawResponse: - return ProductsResourceWithRawResponse(self._commerce.products) - - @cached_property - def variants(self) -> VariantsResourceWithRawResponse: - return VariantsResourceWithRawResponse(self._commerce.variants) - - @cached_property - def collections(self) -> CollectionsResourceWithRawResponse: - return CollectionsResourceWithRawResponse(self._commerce.collections) - - @cached_property - def transactions(self) -> TransactionsResourceWithRawResponse: - return TransactionsResourceWithRawResponse(self._commerce.transactions) - - @cached_property - def accounts(self) -> AccountsResourceWithRawResponse: - return AccountsResourceWithRawResponse(self._commerce.accounts) - - @cached_property - def users(self) -> UsersResourceWithRawResponse: - return UsersResourceWithRawResponse(self._commerce.users) - - @cached_property - def search(self) -> SearchResourceWithRawResponse: - return SearchResourceWithRawResponse(self._commerce.search) - - @cached_property - def subscriptions(self) -> SubscriptionsResourceWithRawResponse: - return SubscriptionsResourceWithRawResponse(self._commerce.subscriptions) - - @cached_property - def webhooks(self) -> WebhooksResourceWithRawResponse: - return WebhooksResourceWithRawResponse(self._commerce.webhooks) - - @cached_property - def stores(self) -> StoresResourceWithRawResponse: - return StoresResourceWithRawResponse(self._commerce.stores) - - @cached_property - def discounts(self) -> DiscountsResourceWithRawResponse: - return DiscountsResourceWithRawResponse(self._commerce.discounts) - - @cached_property - def payments(self) -> PaymentsResourceWithRawResponse: - return PaymentsResourceWithRawResponse(self._commerce.payments) - - -class AsyncCommerceResourceWithRawResponse: - def __init__(self, commerce: AsyncCommerceResource) -> None: - self._commerce = commerce - self.ping = async_to_raw_response_wrapper(commerce.ping) - - @cached_property - def orders(self) -> AsyncOrdersResourceWithRawResponse: - return AsyncOrdersResourceWithRawResponse(self._commerce.orders) - - @cached_property - def products(self) -> AsyncProductsResourceWithRawResponse: - return AsyncProductsResourceWithRawResponse(self._commerce.products) - - @cached_property - def variants(self) -> AsyncVariantsResourceWithRawResponse: - return AsyncVariantsResourceWithRawResponse(self._commerce.variants) - - @cached_property - def collections(self) -> AsyncCollectionsResourceWithRawResponse: - return AsyncCollectionsResourceWithRawResponse(self._commerce.collections) - - @cached_property - def transactions(self) -> AsyncTransactionsResourceWithRawResponse: - return AsyncTransactionsResourceWithRawResponse(self._commerce.transactions) - - @cached_property - def accounts(self) -> AsyncAccountsResourceWithRawResponse: - return AsyncAccountsResourceWithRawResponse(self._commerce.accounts) - - @cached_property - def users(self) -> AsyncUsersResourceWithRawResponse: - return AsyncUsersResourceWithRawResponse(self._commerce.users) - - @cached_property - def search(self) -> AsyncSearchResourceWithRawResponse: - return AsyncSearchResourceWithRawResponse(self._commerce.search) - - @cached_property - def subscriptions(self) -> AsyncSubscriptionsResourceWithRawResponse: - return AsyncSubscriptionsResourceWithRawResponse(self._commerce.subscriptions) - - @cached_property - def webhooks(self) -> AsyncWebhooksResourceWithRawResponse: - return AsyncWebhooksResourceWithRawResponse(self._commerce.webhooks) - - @cached_property - def stores(self) -> AsyncStoresResourceWithRawResponse: - return AsyncStoresResourceWithRawResponse(self._commerce.stores) - - @cached_property - def discounts(self) -> AsyncDiscountsResourceWithRawResponse: - return AsyncDiscountsResourceWithRawResponse(self._commerce.discounts) - - @cached_property - def payments(self) -> AsyncPaymentsResourceWithRawResponse: - return AsyncPaymentsResourceWithRawResponse(self._commerce.payments) - - -# --------------------------------------------------------------------------- -# Streaming response wrappers -# --------------------------------------------------------------------------- - - -class CommerceResourceWithStreamingResponse: - def __init__(self, commerce: CommerceResource) -> None: - self._commerce = commerce - self.ping = to_streamed_response_wrapper(commerce.ping) - - @cached_property - def orders(self) -> OrdersResourceWithStreamingResponse: - return OrdersResourceWithStreamingResponse(self._commerce.orders) - - @cached_property - def products(self) -> ProductsResourceWithStreamingResponse: - return ProductsResourceWithStreamingResponse(self._commerce.products) - - @cached_property - def variants(self) -> VariantsResourceWithStreamingResponse: - return VariantsResourceWithStreamingResponse(self._commerce.variants) - - @cached_property - def collections(self) -> CollectionsResourceWithStreamingResponse: - return CollectionsResourceWithStreamingResponse(self._commerce.collections) - - @cached_property - def transactions(self) -> TransactionsResourceWithStreamingResponse: - return TransactionsResourceWithStreamingResponse(self._commerce.transactions) - - @cached_property - def accounts(self) -> AccountsResourceWithStreamingResponse: - return AccountsResourceWithStreamingResponse(self._commerce.accounts) - - @cached_property - def users(self) -> UsersResourceWithStreamingResponse: - return UsersResourceWithStreamingResponse(self._commerce.users) - - @cached_property - def search(self) -> SearchResourceWithStreamingResponse: - return SearchResourceWithStreamingResponse(self._commerce.search) - - @cached_property - def subscriptions(self) -> SubscriptionsResourceWithStreamingResponse: - return SubscriptionsResourceWithStreamingResponse(self._commerce.subscriptions) - - @cached_property - def webhooks(self) -> WebhooksResourceWithStreamingResponse: - return WebhooksResourceWithStreamingResponse(self._commerce.webhooks) - - @cached_property - def stores(self) -> StoresResourceWithStreamingResponse: - return StoresResourceWithStreamingResponse(self._commerce.stores) - - @cached_property - def discounts(self) -> DiscountsResourceWithStreamingResponse: - return DiscountsResourceWithStreamingResponse(self._commerce.discounts) - - @cached_property - def payments(self) -> PaymentsResourceWithStreamingResponse: - return PaymentsResourceWithStreamingResponse(self._commerce.payments) - - -class AsyncCommerceResourceWithStreamingResponse: - def __init__(self, commerce: AsyncCommerceResource) -> None: - self._commerce = commerce - self.ping = async_to_streamed_response_wrapper(commerce.ping) - - @cached_property - def orders(self) -> AsyncOrdersResourceWithStreamingResponse: - return AsyncOrdersResourceWithStreamingResponse(self._commerce.orders) - - @cached_property - def products(self) -> AsyncProductsResourceWithStreamingResponse: - return AsyncProductsResourceWithStreamingResponse(self._commerce.products) - - @cached_property - def variants(self) -> AsyncVariantsResourceWithStreamingResponse: - return AsyncVariantsResourceWithStreamingResponse(self._commerce.variants) - - @cached_property - def collections(self) -> AsyncCollectionsResourceWithStreamingResponse: - return AsyncCollectionsResourceWithStreamingResponse(self._commerce.collections) - - @cached_property - def transactions(self) -> AsyncTransactionsResourceWithStreamingResponse: - return AsyncTransactionsResourceWithStreamingResponse(self._commerce.transactions) - - @cached_property - def accounts(self) -> AsyncAccountsResourceWithStreamingResponse: - return AsyncAccountsResourceWithStreamingResponse(self._commerce.accounts) - - @cached_property - def users(self) -> AsyncUsersResourceWithStreamingResponse: - return AsyncUsersResourceWithStreamingResponse(self._commerce.users) - - @cached_property - def search(self) -> AsyncSearchResourceWithStreamingResponse: - return AsyncSearchResourceWithStreamingResponse(self._commerce.search) - - @cached_property - def subscriptions(self) -> AsyncSubscriptionsResourceWithStreamingResponse: - return AsyncSubscriptionsResourceWithStreamingResponse(self._commerce.subscriptions) - - @cached_property - def webhooks(self) -> AsyncWebhooksResourceWithStreamingResponse: - return AsyncWebhooksResourceWithStreamingResponse(self._commerce.webhooks) - - @cached_property - def stores(self) -> AsyncStoresResourceWithStreamingResponse: - return AsyncStoresResourceWithStreamingResponse(self._commerce.stores) - - @cached_property - def discounts(self) -> AsyncDiscountsResourceWithStreamingResponse: - return AsyncDiscountsResourceWithStreamingResponse(self._commerce.discounts) - - @cached_property - def payments(self) -> AsyncPaymentsResourceWithStreamingResponse: - return AsyncPaymentsResourceWithStreamingResponse(self._commerce.payments) - - -# --------------------------------------------------------------------------- -# Sub-resource raw response wrappers -# --------------------------------------------------------------------------- - - -class OrdersResourceWithRawResponse: - def __init__(self, orders: OrdersResource) -> None: - self._orders = orders - self.list = to_raw_response_wrapper(orders.list) - self.create = to_raw_response_wrapper(orders.create) - self.get = to_raw_response_wrapper(orders.get) - self.update = to_raw_response_wrapper(orders.update) - self.delete = to_raw_response_wrapper(orders.delete) - self.authorize = to_raw_response_wrapper(orders.authorize) - self.capture = to_raw_response_wrapper(orders.capture) - self.charge = to_raw_response_wrapper(orders.charge) - self.refund = to_raw_response_wrapper(orders.refund) - self.payments = to_raw_response_wrapper(orders.payments) - self.returns = to_raw_response_wrapper(orders.returns) - self.status = to_raw_response_wrapper(orders.status) - - -class AsyncOrdersResourceWithRawResponse: - def __init__(self, orders: AsyncOrdersResource) -> None: - self._orders = orders - self.list = async_to_raw_response_wrapper(orders.list) - self.create = async_to_raw_response_wrapper(orders.create) - self.get = async_to_raw_response_wrapper(orders.get) - self.update = async_to_raw_response_wrapper(orders.update) - self.delete = async_to_raw_response_wrapper(orders.delete) - self.authorize = async_to_raw_response_wrapper(orders.authorize) - self.capture = async_to_raw_response_wrapper(orders.capture) - self.charge = async_to_raw_response_wrapper(orders.charge) - self.refund = async_to_raw_response_wrapper(orders.refund) - self.payments = async_to_raw_response_wrapper(orders.payments) - self.returns = async_to_raw_response_wrapper(orders.returns) - self.status = async_to_raw_response_wrapper(orders.status) - - -class ProductsResourceWithRawResponse: - def __init__(self, products: ProductsResource) -> None: - self._products = products - self.list = to_raw_response_wrapper(products.list) - self.create = to_raw_response_wrapper(products.create) - self.get = to_raw_response_wrapper(products.get) - self.update = to_raw_response_wrapper(products.update) - self.delete = to_raw_response_wrapper(products.delete) - - -class AsyncProductsResourceWithRawResponse: - def __init__(self, products: AsyncProductsResource) -> None: - self._products = products - self.list = async_to_raw_response_wrapper(products.list) - self.create = async_to_raw_response_wrapper(products.create) - self.get = async_to_raw_response_wrapper(products.get) - self.update = async_to_raw_response_wrapper(products.update) - self.delete = async_to_raw_response_wrapper(products.delete) - - -class VariantsResourceWithRawResponse: - def __init__(self, variants: VariantsResource) -> None: - self._variants = variants - self.list = to_raw_response_wrapper(variants.list) - self.create = to_raw_response_wrapper(variants.create) - self.get = to_raw_response_wrapper(variants.get) - self.update = to_raw_response_wrapper(variants.update) - self.delete = to_raw_response_wrapper(variants.delete) - - -class AsyncVariantsResourceWithRawResponse: - def __init__(self, variants: AsyncVariantsResource) -> None: - self._variants = variants - self.list = async_to_raw_response_wrapper(variants.list) - self.create = async_to_raw_response_wrapper(variants.create) - self.get = async_to_raw_response_wrapper(variants.get) - self.update = async_to_raw_response_wrapper(variants.update) - self.delete = async_to_raw_response_wrapper(variants.delete) - - -class CollectionsResourceWithRawResponse: - def __init__(self, collections: CollectionsResource) -> None: - self._collections = collections - self.list = to_raw_response_wrapper(collections.list) - self.create = to_raw_response_wrapper(collections.create) - self.get = to_raw_response_wrapper(collections.get) - self.update = to_raw_response_wrapper(collections.update) - self.delete = to_raw_response_wrapper(collections.delete) - - -class AsyncCollectionsResourceWithRawResponse: - def __init__(self, collections: AsyncCollectionsResource) -> None: - self._collections = collections - self.list = async_to_raw_response_wrapper(collections.list) - self.create = async_to_raw_response_wrapper(collections.create) - self.get = async_to_raw_response_wrapper(collections.get) - self.update = async_to_raw_response_wrapper(collections.update) - self.delete = async_to_raw_response_wrapper(collections.delete) - - -class TransactionsResourceWithRawResponse: - def __init__(self, transactions: TransactionsResource) -> None: - self._transactions = transactions - self.list = to_raw_response_wrapper(transactions.list) - self.create = to_raw_response_wrapper(transactions.create) - self.get_by_kind = to_raw_response_wrapper(transactions.get_by_kind) - - -class AsyncTransactionsResourceWithRawResponse: - def __init__(self, transactions: AsyncTransactionsResource) -> None: - self._transactions = transactions - self.list = async_to_raw_response_wrapper(transactions.list) - self.create = async_to_raw_response_wrapper(transactions.create) - self.get_by_kind = async_to_raw_response_wrapper(transactions.get_by_kind) - - -class AccountsResourceWithRawResponse: - def __init__(self, accounts: AccountsResource) -> None: - self._accounts = accounts - self.login = to_raw_response_wrapper(accounts.login) - self.create = to_raw_response_wrapper(accounts.create) - self.get = to_raw_response_wrapper(accounts.get) - self.exists = to_raw_response_wrapper(accounts.exists) - - -class AsyncAccountsResourceWithRawResponse: - def __init__(self, accounts: AsyncAccountsResource) -> None: - self._accounts = accounts - self.login = async_to_raw_response_wrapper(accounts.login) - self.create = async_to_raw_response_wrapper(accounts.create) - self.get = async_to_raw_response_wrapper(accounts.get) - self.exists = async_to_raw_response_wrapper(accounts.exists) - - -class UsersResourceWithRawResponse: - def __init__(self, users: UsersResource) -> None: - self._users = users - self.list = to_raw_response_wrapper(users.list) - self.get = to_raw_response_wrapper(users.get) - self.orders = to_raw_response_wrapper(users.orders) - self.transactions = to_raw_response_wrapper(users.transactions) - self.wallet = to_raw_response_wrapper(users.wallet) - - -class AsyncUsersResourceWithRawResponse: - def __init__(self, users: AsyncUsersResource) -> None: - self._users = users - self.list = async_to_raw_response_wrapper(users.list) - self.get = async_to_raw_response_wrapper(users.get) - self.orders = async_to_raw_response_wrapper(users.orders) - self.transactions = async_to_raw_response_wrapper(users.transactions) - self.wallet = async_to_raw_response_wrapper(users.wallet) - - -class SearchResourceWithRawResponse: - def __init__(self, search: SearchResource) -> None: - self._search = search - self.users = to_raw_response_wrapper(search.users) - self.orders = to_raw_response_wrapper(search.orders) - - -class AsyncSearchResourceWithRawResponse: - def __init__(self, search: AsyncSearchResource) -> None: - self._search = search - self.users = async_to_raw_response_wrapper(search.users) - self.orders = async_to_raw_response_wrapper(search.orders) - - -class SubscriptionsResourceWithRawResponse: - def __init__(self, subscriptions: SubscriptionsResource) -> None: - self._subscriptions = subscriptions - self.create = to_raw_response_wrapper(subscriptions.create) - self.get = to_raw_response_wrapper(subscriptions.get) - - -class AsyncSubscriptionsResourceWithRawResponse: - def __init__(self, subscriptions: AsyncSubscriptionsResource) -> None: - self._subscriptions = subscriptions - self.create = async_to_raw_response_wrapper(subscriptions.create) - self.get = async_to_raw_response_wrapper(subscriptions.get) - - -class WebhooksResourceWithRawResponse: - def __init__(self, webhooks: WebhooksResource) -> None: - self._webhooks = webhooks - self.list = to_raw_response_wrapper(webhooks.list) - self.create = to_raw_response_wrapper(webhooks.create) - self.delete = to_raw_response_wrapper(webhooks.delete) - - -class AsyncWebhooksResourceWithRawResponse: - def __init__(self, webhooks: AsyncWebhooksResource) -> None: - self._webhooks = webhooks - self.list = async_to_raw_response_wrapper(webhooks.list) - self.create = async_to_raw_response_wrapper(webhooks.create) - self.delete = async_to_raw_response_wrapper(webhooks.delete) - - -class StoresResourceWithRawResponse: - def __init__(self, stores: StoresResource) -> None: - self._stores = stores - self.list = to_raw_response_wrapper(stores.list) - self.create = to_raw_response_wrapper(stores.create) - self.get = to_raw_response_wrapper(stores.get) - self.product = to_raw_response_wrapper(stores.product) - self.listings = to_raw_response_wrapper(stores.listings) - - -class AsyncStoresResourceWithRawResponse: - def __init__(self, stores: AsyncStoresResource) -> None: - self._stores = stores - self.list = async_to_raw_response_wrapper(stores.list) - self.create = async_to_raw_response_wrapper(stores.create) - self.get = async_to_raw_response_wrapper(stores.get) - self.product = async_to_raw_response_wrapper(stores.product) - self.listings = async_to_raw_response_wrapper(stores.listings) - - -class DiscountsResourceWithRawResponse: - def __init__(self, discounts: DiscountsResource) -> None: - self._discounts = discounts - self.list = to_raw_response_wrapper(discounts.list) - self.create = to_raw_response_wrapper(discounts.create) - self.get = to_raw_response_wrapper(discounts.get) - self.delete = to_raw_response_wrapper(discounts.delete) - - -class AsyncDiscountsResourceWithRawResponse: - def __init__(self, discounts: AsyncDiscountsResource) -> None: - self._discounts = discounts - self.list = async_to_raw_response_wrapper(discounts.list) - self.create = async_to_raw_response_wrapper(discounts.create) - self.get = async_to_raw_response_wrapper(discounts.get) - self.delete = async_to_raw_response_wrapper(discounts.delete) - - -class PaymentsResourceWithRawResponse: - def __init__(self, payments: PaymentsResource) -> None: - self._payments = payments - self.list = to_raw_response_wrapper(payments.list) - self.create = to_raw_response_wrapper(payments.create) - self.refund = to_raw_response_wrapper(payments.refund) - - -class AsyncPaymentsResourceWithRawResponse: - def __init__(self, payments: AsyncPaymentsResource) -> None: - self._payments = payments - self.list = async_to_raw_response_wrapper(payments.list) - self.create = async_to_raw_response_wrapper(payments.create) - self.refund = async_to_raw_response_wrapper(payments.refund) - - -# --------------------------------------------------------------------------- -# Sub-resource streaming response wrappers -# --------------------------------------------------------------------------- - - -class OrdersResourceWithStreamingResponse: - def __init__(self, orders: OrdersResource) -> None: - self._orders = orders - self.list = to_streamed_response_wrapper(orders.list) - self.create = to_streamed_response_wrapper(orders.create) - self.get = to_streamed_response_wrapper(orders.get) - self.update = to_streamed_response_wrapper(orders.update) - self.delete = to_streamed_response_wrapper(orders.delete) - self.authorize = to_streamed_response_wrapper(orders.authorize) - self.capture = to_streamed_response_wrapper(orders.capture) - self.charge = to_streamed_response_wrapper(orders.charge) - self.refund = to_streamed_response_wrapper(orders.refund) - self.payments = to_streamed_response_wrapper(orders.payments) - self.returns = to_streamed_response_wrapper(orders.returns) - self.status = to_streamed_response_wrapper(orders.status) - - -class AsyncOrdersResourceWithStreamingResponse: - def __init__(self, orders: AsyncOrdersResource) -> None: - self._orders = orders - self.list = async_to_streamed_response_wrapper(orders.list) - self.create = async_to_streamed_response_wrapper(orders.create) - self.get = async_to_streamed_response_wrapper(orders.get) - self.update = async_to_streamed_response_wrapper(orders.update) - self.delete = async_to_streamed_response_wrapper(orders.delete) - self.authorize = async_to_streamed_response_wrapper(orders.authorize) - self.capture = async_to_streamed_response_wrapper(orders.capture) - self.charge = async_to_streamed_response_wrapper(orders.charge) - self.refund = async_to_streamed_response_wrapper(orders.refund) - self.payments = async_to_streamed_response_wrapper(orders.payments) - self.returns = async_to_streamed_response_wrapper(orders.returns) - self.status = async_to_streamed_response_wrapper(orders.status) - - -class ProductsResourceWithStreamingResponse: - def __init__(self, products: ProductsResource) -> None: - self._products = products - self.list = to_streamed_response_wrapper(products.list) - self.create = to_streamed_response_wrapper(products.create) - self.get = to_streamed_response_wrapper(products.get) - self.update = to_streamed_response_wrapper(products.update) - self.delete = to_streamed_response_wrapper(products.delete) - - -class AsyncProductsResourceWithStreamingResponse: - def __init__(self, products: AsyncProductsResource) -> None: - self._products = products - self.list = async_to_streamed_response_wrapper(products.list) - self.create = async_to_streamed_response_wrapper(products.create) - self.get = async_to_streamed_response_wrapper(products.get) - self.update = async_to_streamed_response_wrapper(products.update) - self.delete = async_to_streamed_response_wrapper(products.delete) - - -class VariantsResourceWithStreamingResponse: - def __init__(self, variants: VariantsResource) -> None: - self._variants = variants - self.list = to_streamed_response_wrapper(variants.list) - self.create = to_streamed_response_wrapper(variants.create) - self.get = to_streamed_response_wrapper(variants.get) - self.update = to_streamed_response_wrapper(variants.update) - self.delete = to_streamed_response_wrapper(variants.delete) - - -class AsyncVariantsResourceWithStreamingResponse: - def __init__(self, variants: AsyncVariantsResource) -> None: - self._variants = variants - self.list = async_to_streamed_response_wrapper(variants.list) - self.create = async_to_streamed_response_wrapper(variants.create) - self.get = async_to_streamed_response_wrapper(variants.get) - self.update = async_to_streamed_response_wrapper(variants.update) - self.delete = async_to_streamed_response_wrapper(variants.delete) - - -class CollectionsResourceWithStreamingResponse: - def __init__(self, collections: CollectionsResource) -> None: - self._collections = collections - self.list = to_streamed_response_wrapper(collections.list) - self.create = to_streamed_response_wrapper(collections.create) - self.get = to_streamed_response_wrapper(collections.get) - self.update = to_streamed_response_wrapper(collections.update) - self.delete = to_streamed_response_wrapper(collections.delete) - - -class AsyncCollectionsResourceWithStreamingResponse: - def __init__(self, collections: AsyncCollectionsResource) -> None: - self._collections = collections - self.list = async_to_streamed_response_wrapper(collections.list) - self.create = async_to_streamed_response_wrapper(collections.create) - self.get = async_to_streamed_response_wrapper(collections.get) - self.update = async_to_streamed_response_wrapper(collections.update) - self.delete = async_to_streamed_response_wrapper(collections.delete) - - -class TransactionsResourceWithStreamingResponse: - def __init__(self, transactions: TransactionsResource) -> None: - self._transactions = transactions - self.list = to_streamed_response_wrapper(transactions.list) - self.create = to_streamed_response_wrapper(transactions.create) - self.get_by_kind = to_streamed_response_wrapper(transactions.get_by_kind) - - -class AsyncTransactionsResourceWithStreamingResponse: - def __init__(self, transactions: AsyncTransactionsResource) -> None: - self._transactions = transactions - self.list = async_to_streamed_response_wrapper(transactions.list) - self.create = async_to_streamed_response_wrapper(transactions.create) - self.get_by_kind = async_to_streamed_response_wrapper(transactions.get_by_kind) - - -class AccountsResourceWithStreamingResponse: - def __init__(self, accounts: AccountsResource) -> None: - self._accounts = accounts - self.login = to_streamed_response_wrapper(accounts.login) - self.create = to_streamed_response_wrapper(accounts.create) - self.get = to_streamed_response_wrapper(accounts.get) - self.exists = to_streamed_response_wrapper(accounts.exists) - - -class AsyncAccountsResourceWithStreamingResponse: - def __init__(self, accounts: AsyncAccountsResource) -> None: - self._accounts = accounts - self.login = async_to_streamed_response_wrapper(accounts.login) - self.create = async_to_streamed_response_wrapper(accounts.create) - self.get = async_to_streamed_response_wrapper(accounts.get) - self.exists = async_to_streamed_response_wrapper(accounts.exists) - - -class UsersResourceWithStreamingResponse: - def __init__(self, users: UsersResource) -> None: - self._users = users - self.list = to_streamed_response_wrapper(users.list) - self.get = to_streamed_response_wrapper(users.get) - self.orders = to_streamed_response_wrapper(users.orders) - self.transactions = to_streamed_response_wrapper(users.transactions) - self.wallet = to_streamed_response_wrapper(users.wallet) - - -class AsyncUsersResourceWithStreamingResponse: - def __init__(self, users: AsyncUsersResource) -> None: - self._users = users - self.list = async_to_streamed_response_wrapper(users.list) - self.get = async_to_streamed_response_wrapper(users.get) - self.orders = async_to_streamed_response_wrapper(users.orders) - self.transactions = async_to_streamed_response_wrapper(users.transactions) - self.wallet = async_to_streamed_response_wrapper(users.wallet) - - -class SearchResourceWithStreamingResponse: - def __init__(self, search: SearchResource) -> None: - self._search = search - self.users = to_streamed_response_wrapper(search.users) - self.orders = to_streamed_response_wrapper(search.orders) - - -class AsyncSearchResourceWithStreamingResponse: - def __init__(self, search: AsyncSearchResource) -> None: - self._search = search - self.users = async_to_streamed_response_wrapper(search.users) - self.orders = async_to_streamed_response_wrapper(search.orders) - - -class SubscriptionsResourceWithStreamingResponse: - def __init__(self, subscriptions: SubscriptionsResource) -> None: - self._subscriptions = subscriptions - self.create = to_streamed_response_wrapper(subscriptions.create) - self.get = to_streamed_response_wrapper(subscriptions.get) - - -class AsyncSubscriptionsResourceWithStreamingResponse: - def __init__(self, subscriptions: AsyncSubscriptionsResource) -> None: - self._subscriptions = subscriptions - self.create = async_to_streamed_response_wrapper(subscriptions.create) - self.get = async_to_streamed_response_wrapper(subscriptions.get) - - -class WebhooksResourceWithStreamingResponse: - def __init__(self, webhooks: WebhooksResource) -> None: - self._webhooks = webhooks - self.list = to_streamed_response_wrapper(webhooks.list) - self.create = to_streamed_response_wrapper(webhooks.create) - self.delete = to_streamed_response_wrapper(webhooks.delete) - - -class AsyncWebhooksResourceWithStreamingResponse: - def __init__(self, webhooks: AsyncWebhooksResource) -> None: - self._webhooks = webhooks - self.list = async_to_streamed_response_wrapper(webhooks.list) - self.create = async_to_streamed_response_wrapper(webhooks.create) - self.delete = async_to_streamed_response_wrapper(webhooks.delete) - - -class StoresResourceWithStreamingResponse: - def __init__(self, stores: StoresResource) -> None: - self._stores = stores - self.list = to_streamed_response_wrapper(stores.list) - self.create = to_streamed_response_wrapper(stores.create) - self.get = to_streamed_response_wrapper(stores.get) - self.product = to_streamed_response_wrapper(stores.product) - self.listings = to_streamed_response_wrapper(stores.listings) - - -class AsyncStoresResourceWithStreamingResponse: - def __init__(self, stores: AsyncStoresResource) -> None: - self._stores = stores - self.list = async_to_streamed_response_wrapper(stores.list) - self.create = async_to_streamed_response_wrapper(stores.create) - self.get = async_to_streamed_response_wrapper(stores.get) - self.product = async_to_streamed_response_wrapper(stores.product) - self.listings = async_to_streamed_response_wrapper(stores.listings) - - -class DiscountsResourceWithStreamingResponse: - def __init__(self, discounts: DiscountsResource) -> None: - self._discounts = discounts - self.list = to_streamed_response_wrapper(discounts.list) - self.create = to_streamed_response_wrapper(discounts.create) - self.get = to_streamed_response_wrapper(discounts.get) - self.delete = to_streamed_response_wrapper(discounts.delete) - - -class AsyncDiscountsResourceWithStreamingResponse: - def __init__(self, discounts: AsyncDiscountsResource) -> None: - self._discounts = discounts - self.list = async_to_streamed_response_wrapper(discounts.list) - self.create = async_to_streamed_response_wrapper(discounts.create) - self.get = async_to_streamed_response_wrapper(discounts.get) - self.delete = async_to_streamed_response_wrapper(discounts.delete) - - -class PaymentsResourceWithStreamingResponse: - def __init__(self, payments: PaymentsResource) -> None: - self._payments = payments - self.list = to_streamed_response_wrapper(payments.list) - self.create = to_streamed_response_wrapper(payments.create) - self.refund = to_streamed_response_wrapper(payments.refund) - - -class AsyncPaymentsResourceWithStreamingResponse: - def __init__(self, payments: AsyncPaymentsResource) -> None: - self._payments = payments - self.list = async_to_streamed_response_wrapper(payments.list) - self.create = async_to_streamed_response_wrapper(payments.create) - self.refund = async_to_streamed_response_wrapper(payments.refund) diff --git a/pkg/hanzoai/resources/completions.py b/pkg/hanzoai/resources/completions.py deleted file mode 100644 index 3d0902abd..000000000 --- a/pkg/hanzoai/resources/completions.py +++ /dev/null @@ -1,203 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional - -import httpx - -from ..types import completion_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - maybe_transform, - async_maybe_transform, -) -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["CompletionsResource", "AsyncCompletionsResource"] - - -class CompletionsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> CompletionsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return CompletionsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> CompletionsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return CompletionsResourceWithStreamingResponse(self) - - def create( - self, - *, - model: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Follows the exact same API spec as - `OpenAI's Completions API https://platform.openai.com/docs/api-reference/completions` - - ```bash - curl -X POST http://localhost:4000/v1/completions - -H "Content-Type: application/json" - -H "Authorization: Bearer sk-1234" - -d '{ - "model": "gpt-3.5-turbo-instruct", - "prompt": "Once upon a time", - "max_tokens": 50, - "temperature": 0.7 - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/completions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"model": model}, completion_create_params.CompletionCreateParams - ), - ), - cast_to=object, - ) - - -class AsyncCompletionsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncCompletionsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncCompletionsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncCompletionsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncCompletionsResourceWithStreamingResponse(self) - - async def create( - self, - *, - model: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Follows the exact same API spec as - `OpenAI's Completions API https://platform.openai.com/docs/api-reference/completions` - - ```bash - curl -X POST http://localhost:4000/v1/completions - -H "Content-Type: application/json" - -H "Authorization: Bearer sk-1234" - -d '{ - "model": "gpt-3.5-turbo-instruct", - "prompt": "Once upon a time", - "max_tokens": 50, - "temperature": 0.7 - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/completions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"model": model}, completion_create_params.CompletionCreateParams - ), - ), - cast_to=object, - ) - - -class CompletionsResourceWithRawResponse: - def __init__(self, completions: CompletionsResource) -> None: - self._completions = completions - - self.create = to_raw_response_wrapper( - completions.create, - ) - - -class AsyncCompletionsResourceWithRawResponse: - def __init__(self, completions: AsyncCompletionsResource) -> None: - self._completions = completions - - self.create = async_to_raw_response_wrapper( - completions.create, - ) - - -class CompletionsResourceWithStreamingResponse: - def __init__(self, completions: CompletionsResource) -> None: - self._completions = completions - - self.create = to_streamed_response_wrapper( - completions.create, - ) - - -class AsyncCompletionsResourceWithStreamingResponse: - def __init__(self, completions: AsyncCompletionsResource) -> None: - self._completions = completions - - self.create = async_to_streamed_response_wrapper( - completions.create, - ) diff --git a/pkg/hanzoai/resources/config/__init__.py b/pkg/hanzoai/resources/config/__init__.py deleted file mode 100644 index 5538433f1..000000000 --- a/pkg/hanzoai/resources/config/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Hanzo AI SDK - -from .config import ( - ConfigResource, - AsyncConfigResource, - ConfigResourceWithRawResponse, - AsyncConfigResourceWithRawResponse, - ConfigResourceWithStreamingResponse, - AsyncConfigResourceWithStreamingResponse, -) -from .pass_through_endpoint import ( - PassThroughEndpointResource, - AsyncPassThroughEndpointResource, - PassThroughEndpointResourceWithRawResponse, - AsyncPassThroughEndpointResourceWithRawResponse, - PassThroughEndpointResourceWithStreamingResponse, - AsyncPassThroughEndpointResourceWithStreamingResponse, -) - -__all__ = [ - "PassThroughEndpointResource", - "AsyncPassThroughEndpointResource", - "PassThroughEndpointResourceWithRawResponse", - "AsyncPassThroughEndpointResourceWithRawResponse", - "PassThroughEndpointResourceWithStreamingResponse", - "AsyncPassThroughEndpointResourceWithStreamingResponse", - "ConfigResource", - "AsyncConfigResource", - "ConfigResourceWithRawResponse", - "AsyncConfigResourceWithRawResponse", - "ConfigResourceWithStreamingResponse", - "AsyncConfigResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/config/config.py b/pkg/hanzoai/resources/config/config.py deleted file mode 100644 index f572c1fc1..000000000 --- a/pkg/hanzoai/resources/config/config.py +++ /dev/null @@ -1,112 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from .pass_through_endpoint import ( - PassThroughEndpointResource, - AsyncPassThroughEndpointResource, - PassThroughEndpointResourceWithRawResponse, - AsyncPassThroughEndpointResourceWithRawResponse, - PassThroughEndpointResourceWithStreamingResponse, - AsyncPassThroughEndpointResourceWithStreamingResponse, -) - -__all__ = ["ConfigResource", "AsyncConfigResource"] - - -class ConfigResource(SyncAPIResource): - @cached_property - def pass_through_endpoint(self) -> PassThroughEndpointResource: - return PassThroughEndpointResource(self._client) - - @cached_property - def with_raw_response(self) -> ConfigResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return ConfigResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ConfigResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return ConfigResourceWithStreamingResponse(self) - - -class AsyncConfigResource(AsyncAPIResource): - @cached_property - def pass_through_endpoint(self) -> AsyncPassThroughEndpointResource: - return AsyncPassThroughEndpointResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncConfigResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncConfigResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncConfigResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncConfigResourceWithStreamingResponse(self) - - -class ConfigResourceWithRawResponse: - def __init__(self, config: ConfigResource) -> None: - self._config = config - - @cached_property - def pass_through_endpoint(self) -> PassThroughEndpointResourceWithRawResponse: - return PassThroughEndpointResourceWithRawResponse( - self._config.pass_through_endpoint - ) - - -class AsyncConfigResourceWithRawResponse: - def __init__(self, config: AsyncConfigResource) -> None: - self._config = config - - @cached_property - def pass_through_endpoint(self) -> AsyncPassThroughEndpointResourceWithRawResponse: - return AsyncPassThroughEndpointResourceWithRawResponse( - self._config.pass_through_endpoint - ) - - -class ConfigResourceWithStreamingResponse: - def __init__(self, config: ConfigResource) -> None: - self._config = config - - @cached_property - def pass_through_endpoint(self) -> PassThroughEndpointResourceWithStreamingResponse: - return PassThroughEndpointResourceWithStreamingResponse( - self._config.pass_through_endpoint - ) - - -class AsyncConfigResourceWithStreamingResponse: - def __init__(self, config: AsyncConfigResource) -> None: - self._config = config - - @cached_property - def pass_through_endpoint( - self, - ) -> AsyncPassThroughEndpointResourceWithStreamingResponse: - return AsyncPassThroughEndpointResourceWithStreamingResponse( - self._config.pass_through_endpoint - ) diff --git a/pkg/hanzoai/resources/containers.py b/pkg/hanzoai/resources/containers.py deleted file mode 100644 index ff0c0d9bc..000000000 --- a/pkg/hanzoai/resources/containers.py +++ /dev/null @@ -1,992 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List, Optional - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["ContainersResource", "AsyncContainersResource"] - - -class ContainersResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ContainersResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return ContainersResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ContainersResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return ContainersResourceWithStreamingResponse(self) - - def list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all containers in the infrastructure.""" - return self._get( - "/infrastructure/containers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - container_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Get details of a specific container. - - Args: - container_id: The unique identifier of the container. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not container_id: - raise ValueError( - f"Expected a non-empty value for `container_id` but received {container_id!r}" - ) - return self._get( - f"/infrastructure/containers/{container_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: Optional[str] | NotGiven = NOT_GIVEN, - image: Optional[str] | NotGiven = NOT_GIVEN, - command: Optional[List[str]] | NotGiven = NOT_GIVEN, - env: Optional[dict] | NotGiven = NOT_GIVEN, - ports: Optional[List[dict]] | NotGiven = NOT_GIVEN, - volumes: Optional[List[dict]] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Create a new container. - - Args: - name: The name of the container. - - image: The Docker image to use. - - command: The command to run in the container. - - env: Environment variables for the container. - - ports: Port mappings for the container. - - volumes: Volume mounts for the container. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/infrastructure/containers", - body={ - "name": name, - "image": image, - "command": command, - "env": env, - "ports": ports, - "volumes": volumes, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - container_id: str, - *, - name: Optional[str] | NotGiven = NOT_GIVEN, - image: Optional[str] | NotGiven = NOT_GIVEN, - env: Optional[dict] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Update an existing container. - - Args: - container_id: The unique identifier of the container. - - name: The new name of the container. - - image: The new Docker image. - - env: Updated environment variables. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not container_id: - raise ValueError( - f"Expected a non-empty value for `container_id` but received {container_id!r}" - ) - return self._put( - f"/infrastructure/containers/{container_id}", - body={ - "name": name, - "image": image, - "env": env, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - container_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Delete a container. - - Args: - container_id: The unique identifier of the container. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not container_id: - raise ValueError( - f"Expected a non-empty value for `container_id` but received {container_id!r}" - ) - return self._delete( - f"/infrastructure/containers/{container_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def start( - self, - container_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Start a stopped container. - - Args: - container_id: The unique identifier of the container. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not container_id: - raise ValueError( - f"Expected a non-empty value for `container_id` but received {container_id!r}" - ) - return self._post( - f"/infrastructure/containers/{container_id}/start", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stop( - self, - container_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Stop a running container. - - Args: - container_id: The unique identifier of the container. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not container_id: - raise ValueError( - f"Expected a non-empty value for `container_id` but received {container_id!r}" - ) - return self._post( - f"/infrastructure/containers/{container_id}/stop", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def logs( - self, - container_id: str, - *, - tail: Optional[int] | NotGiven = NOT_GIVEN, - follow: Optional[bool] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Get logs from a container. - - Args: - container_id: The unique identifier of the container. - - tail: Number of lines to return from the end of the logs. - - follow: Whether to stream logs in real-time. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not container_id: - raise ValueError( - f"Expected a non-empty value for `container_id` but received {container_id!r}" - ) - return self._get( - f"/infrastructure/containers/{container_id}/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "tail": tail, - "follow": follow, - }, - ), - cast_to=object, - ) - - def exec( - self, - container_id: str, - *, - command: List[str], - workdir: Optional[str] | NotGiven = NOT_GIVEN, - env: Optional[dict] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Execute a command in a running container. - - Args: - container_id: The unique identifier of the container. - - command: The command to execute. - - workdir: Working directory for the command. - - env: Environment variables for the command. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not container_id: - raise ValueError( - f"Expected a non-empty value for `container_id` but received {container_id!r}" - ) - return self._post( - f"/infrastructure/containers/{container_id}/exec", - body={ - "command": command, - "workdir": workdir, - "env": env, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncContainersResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncContainersResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncContainersResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncContainersResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncContainersResourceWithStreamingResponse(self) - - async def list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all containers in the infrastructure.""" - return await self._get( - "/infrastructure/containers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - container_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Get details of a specific container. - - Args: - container_id: The unique identifier of the container. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not container_id: - raise ValueError( - f"Expected a non-empty value for `container_id` but received {container_id!r}" - ) - return await self._get( - f"/infrastructure/containers/{container_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: Optional[str] | NotGiven = NOT_GIVEN, - image: Optional[str] | NotGiven = NOT_GIVEN, - command: Optional[List[str]] | NotGiven = NOT_GIVEN, - env: Optional[dict] | NotGiven = NOT_GIVEN, - ports: Optional[List[dict]] | NotGiven = NOT_GIVEN, - volumes: Optional[List[dict]] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Create a new container. - - Args: - name: The name of the container. - - image: The Docker image to use. - - command: The command to run in the container. - - env: Environment variables for the container. - - ports: Port mappings for the container. - - volumes: Volume mounts for the container. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/infrastructure/containers", - body={ - "name": name, - "image": image, - "command": command, - "env": env, - "ports": ports, - "volumes": volumes, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - container_id: str, - *, - name: Optional[str] | NotGiven = NOT_GIVEN, - image: Optional[str] | NotGiven = NOT_GIVEN, - env: Optional[dict] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Update an existing container. - - Args: - container_id: The unique identifier of the container. - - name: The new name of the container. - - image: The new Docker image. - - env: Updated environment variables. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not container_id: - raise ValueError( - f"Expected a non-empty value for `container_id` but received {container_id!r}" - ) - return await self._put( - f"/infrastructure/containers/{container_id}", - body={ - "name": name, - "image": image, - "env": env, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - container_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Delete a container. - - Args: - container_id: The unique identifier of the container. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not container_id: - raise ValueError( - f"Expected a non-empty value for `container_id` but received {container_id!r}" - ) - return await self._delete( - f"/infrastructure/containers/{container_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def start( - self, - container_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Start a stopped container. - - Args: - container_id: The unique identifier of the container. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not container_id: - raise ValueError( - f"Expected a non-empty value for `container_id` but received {container_id!r}" - ) - return await self._post( - f"/infrastructure/containers/{container_id}/start", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stop( - self, - container_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Stop a running container. - - Args: - container_id: The unique identifier of the container. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not container_id: - raise ValueError( - f"Expected a non-empty value for `container_id` but received {container_id!r}" - ) - return await self._post( - f"/infrastructure/containers/{container_id}/stop", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def logs( - self, - container_id: str, - *, - tail: Optional[int] | NotGiven = NOT_GIVEN, - follow: Optional[bool] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Get logs from a container. - - Args: - container_id: The unique identifier of the container. - - tail: Number of lines to return from the end of the logs. - - follow: Whether to stream logs in real-time. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not container_id: - raise ValueError( - f"Expected a non-empty value for `container_id` but received {container_id!r}" - ) - return await self._get( - f"/infrastructure/containers/{container_id}/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "tail": tail, - "follow": follow, - }, - ), - cast_to=object, - ) - - async def exec( - self, - container_id: str, - *, - command: List[str], - workdir: Optional[str] | NotGiven = NOT_GIVEN, - env: Optional[dict] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Execute a command in a running container. - - Args: - container_id: The unique identifier of the container. - - command: The command to execute. - - workdir: Working directory for the command. - - env: Environment variables for the command. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not container_id: - raise ValueError( - f"Expected a non-empty value for `container_id` but received {container_id!r}" - ) - return await self._post( - f"/infrastructure/containers/{container_id}/exec", - body={ - "command": command, - "workdir": workdir, - "env": env, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class ContainersResourceWithRawResponse: - def __init__(self, containers: ContainersResource) -> None: - self._containers = containers - - self.list = to_raw_response_wrapper( - containers.list, - ) - self.get = to_raw_response_wrapper( - containers.get, - ) - self.create = to_raw_response_wrapper( - containers.create, - ) - self.update = to_raw_response_wrapper( - containers.update, - ) - self.delete = to_raw_response_wrapper( - containers.delete, - ) - self.start = to_raw_response_wrapper( - containers.start, - ) - self.stop = to_raw_response_wrapper( - containers.stop, - ) - self.logs = to_raw_response_wrapper( - containers.logs, - ) - self.exec = to_raw_response_wrapper( - containers.exec, - ) - - -class AsyncContainersResourceWithRawResponse: - def __init__(self, containers: AsyncContainersResource) -> None: - self._containers = containers - - self.list = async_to_raw_response_wrapper( - containers.list, - ) - self.get = async_to_raw_response_wrapper( - containers.get, - ) - self.create = async_to_raw_response_wrapper( - containers.create, - ) - self.update = async_to_raw_response_wrapper( - containers.update, - ) - self.delete = async_to_raw_response_wrapper( - containers.delete, - ) - self.start = async_to_raw_response_wrapper( - containers.start, - ) - self.stop = async_to_raw_response_wrapper( - containers.stop, - ) - self.logs = async_to_raw_response_wrapper( - containers.logs, - ) - self.exec = async_to_raw_response_wrapper( - containers.exec, - ) - - -class ContainersResourceWithStreamingResponse: - def __init__(self, containers: ContainersResource) -> None: - self._containers = containers - - self.list = to_streamed_response_wrapper( - containers.list, - ) - self.get = to_streamed_response_wrapper( - containers.get, - ) - self.create = to_streamed_response_wrapper( - containers.create, - ) - self.update = to_streamed_response_wrapper( - containers.update, - ) - self.delete = to_streamed_response_wrapper( - containers.delete, - ) - self.start = to_streamed_response_wrapper( - containers.start, - ) - self.stop = to_streamed_response_wrapper( - containers.stop, - ) - self.logs = to_streamed_response_wrapper( - containers.logs, - ) - self.exec = to_streamed_response_wrapper( - containers.exec, - ) - - -class AsyncContainersResourceWithStreamingResponse: - def __init__(self, containers: AsyncContainersResource) -> None: - self._containers = containers - - self.list = async_to_streamed_response_wrapper( - containers.list, - ) - self.get = async_to_streamed_response_wrapper( - containers.get, - ) - self.create = async_to_streamed_response_wrapper( - containers.create, - ) - self.update = async_to_streamed_response_wrapper( - containers.update, - ) - self.delete = async_to_streamed_response_wrapper( - containers.delete, - ) - self.start = async_to_streamed_response_wrapper( - containers.start, - ) - self.stop = async_to_streamed_response_wrapper( - containers.stop, - ) - self.logs = async_to_streamed_response_wrapper( - containers.logs, - ) - self.exec = async_to_streamed_response_wrapper( - containers.exec, - ) diff --git a/pkg/hanzoai/resources/coupons.py b/pkg/hanzoai/resources/coupons.py deleted file mode 100644 index ab4557855..000000000 --- a/pkg/hanzoai/resources/coupons.py +++ /dev/null @@ -1,445 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["CouponsResource", "AsyncCouponsResource"] - - -class CouponsResource(SyncAPIResource): - """Coupon and discount management.""" - - @cached_property - def with_raw_response(self) -> CouponsResourceWithRawResponse: - return CouponsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> CouponsResourceWithStreamingResponse: - return CouponsResourceWithStreamingResponse(self) - - def list( - self, - *, - active: bool | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all coupons.""" - return self._get( - "/marketing/coupons", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"active": active, "limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - def get( - self, - coupon_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific coupon.""" - return self._get( - f"/marketing/coupons/{coupon_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - code: str, - discount_type: str, - discount_value: float, - valid_from: str | NotGiven = NOT_GIVEN, - valid_until: str | NotGiven = NOT_GIVEN, - max_uses: int | NotGiven = NOT_GIVEN, - min_purchase: float | NotGiven = NOT_GIVEN, - applicable_products: List[str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new coupon.""" - return self._post( - "/marketing/coupons", - body={ - "code": code, - "discount_type": discount_type, - "discount_value": discount_value, - "valid_from": valid_from, - "valid_until": valid_until, - "max_uses": max_uses, - "min_purchase": min_purchase, - "applicable_products": applicable_products, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - coupon_id: str, - *, - discount_value: float | NotGiven = NOT_GIVEN, - valid_until: str | NotGiven = NOT_GIVEN, - max_uses: int | NotGiven = NOT_GIVEN, - active: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a coupon.""" - return self._put( - f"/marketing/coupons/{coupon_id}", - body={ - "discount_value": discount_value, - "valid_until": valid_until, - "max_uses": max_uses, - "active": active, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - coupon_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a coupon.""" - return self._delete( - f"/marketing/coupons/{coupon_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def validate( - self, - code: str, - *, - cart_total: float | NotGiven = NOT_GIVEN, - product_ids: List[str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Validate a coupon code.""" - return self._post( - f"/marketing/coupons/{code}/validate", - body={"cart_total": cart_total, "product_ids": product_ids}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stats( - self, - coupon_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get coupon usage statistics.""" - return self._get( - f"/marketing/coupons/{coupon_id}/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncCouponsResource(AsyncAPIResource): - """Coupon and discount management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncCouponsResourceWithRawResponse: - return AsyncCouponsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncCouponsResourceWithStreamingResponse: - return AsyncCouponsResourceWithStreamingResponse(self) - - async def list( - self, - *, - active: bool | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/marketing/coupons", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"active": active, "limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - async def get( - self, - coupon_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/marketing/coupons/{coupon_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - code: str, - discount_type: str, - discount_value: float, - valid_from: str | NotGiven = NOT_GIVEN, - valid_until: str | NotGiven = NOT_GIVEN, - max_uses: int | NotGiven = NOT_GIVEN, - min_purchase: float | NotGiven = NOT_GIVEN, - applicable_products: List[str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/marketing/coupons", - body={ - "code": code, - "discount_type": discount_type, - "discount_value": discount_value, - "valid_from": valid_from, - "valid_until": valid_until, - "max_uses": max_uses, - "min_purchase": min_purchase, - "applicable_products": applicable_products, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - coupon_id: str, - *, - discount_value: float | NotGiven = NOT_GIVEN, - valid_until: str | NotGiven = NOT_GIVEN, - max_uses: int | NotGiven = NOT_GIVEN, - active: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._put( - f"/marketing/coupons/{coupon_id}", - body={ - "discount_value": discount_value, - "valid_until": valid_until, - "max_uses": max_uses, - "active": active, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - coupon_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/marketing/coupons/{coupon_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def validate( - self, - code: str, - *, - cart_total: float | NotGiven = NOT_GIVEN, - product_ids: List[str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/marketing/coupons/{code}/validate", - body={"cart_total": cart_total, "product_ids": product_ids}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stats( - self, - coupon_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/marketing/coupons/{coupon_id}/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class CouponsResourceWithRawResponse: - def __init__(self, coupons: CouponsResource) -> None: - self._coupons = coupons - self.list = to_raw_response_wrapper(coupons.list) - self.get = to_raw_response_wrapper(coupons.get) - self.create = to_raw_response_wrapper(coupons.create) - self.update = to_raw_response_wrapper(coupons.update) - self.delete = to_raw_response_wrapper(coupons.delete) - self.validate = to_raw_response_wrapper(coupons.validate) - self.stats = to_raw_response_wrapper(coupons.stats) - - -class AsyncCouponsResourceWithRawResponse: - def __init__(self, coupons: AsyncCouponsResource) -> None: - self._coupons = coupons - self.list = async_to_raw_response_wrapper(coupons.list) - self.get = async_to_raw_response_wrapper(coupons.get) - self.create = async_to_raw_response_wrapper(coupons.create) - self.update = async_to_raw_response_wrapper(coupons.update) - self.delete = async_to_raw_response_wrapper(coupons.delete) - self.validate = async_to_raw_response_wrapper(coupons.validate) - self.stats = async_to_raw_response_wrapper(coupons.stats) - - -class CouponsResourceWithStreamingResponse: - def __init__(self, coupons: CouponsResource) -> None: - self._coupons = coupons - self.list = to_streamed_response_wrapper(coupons.list) - self.get = to_streamed_response_wrapper(coupons.get) - self.create = to_streamed_response_wrapper(coupons.create) - self.update = to_streamed_response_wrapper(coupons.update) - self.delete = to_streamed_response_wrapper(coupons.delete) - self.validate = to_streamed_response_wrapper(coupons.validate) - self.stats = to_streamed_response_wrapper(coupons.stats) - - -class AsyncCouponsResourceWithStreamingResponse: - def __init__(self, coupons: AsyncCouponsResource) -> None: - self._coupons = coupons - self.list = async_to_streamed_response_wrapper(coupons.list) - self.get = async_to_streamed_response_wrapper(coupons.get) - self.create = async_to_streamed_response_wrapper(coupons.create) - self.update = async_to_streamed_response_wrapper(coupons.update) - self.delete = async_to_streamed_response_wrapper(coupons.delete) - self.validate = async_to_streamed_response_wrapper(coupons.validate) - self.stats = async_to_streamed_response_wrapper(coupons.stats) diff --git a/pkg/hanzoai/resources/credentials.py b/pkg/hanzoai/resources/credentials.py deleted file mode 100644 index d34e95c07..000000000 --- a/pkg/hanzoai/resources/credentials.py +++ /dev/null @@ -1,345 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional - -import httpx - -from ..types import credential_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - maybe_transform, - async_maybe_transform, -) -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["CredentialsResource", "AsyncCredentialsResource"] - - -class CredentialsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> CredentialsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return CredentialsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> CredentialsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return CredentialsResourceWithStreamingResponse(self) - - def create( - self, - *, - credential_info: object, - credential_name: str, - credential_values: Optional[object] | NotGiven = NOT_GIVEN, - model_id: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """[BETA] endpoint. - - This might change unexpectedly. Stores credential in DB. - Reloads credentials in memory. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/credentials", - body=maybe_transform( - { - "credential_info": credential_info, - "credential_name": credential_name, - "credential_values": credential_values, - "model_id": model_id, - }, - credential_create_params.CredentialCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """[BETA] endpoint. This might change unexpectedly.""" - return self._get( - "/credentials", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - credential_name: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """[BETA] endpoint. - - This might change unexpectedly. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not credential_name: - raise ValueError( - f"Expected a non-empty value for `credential_name` but received {credential_name!r}" - ) - return self._delete( - f"/credentials/{credential_name}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncCredentialsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncCredentialsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncCredentialsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncCredentialsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncCredentialsResourceWithStreamingResponse(self) - - async def create( - self, - *, - credential_info: object, - credential_name: str, - credential_values: Optional[object] | NotGiven = NOT_GIVEN, - model_id: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """[BETA] endpoint. - - This might change unexpectedly. Stores credential in DB. - Reloads credentials in memory. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/credentials", - body=await async_maybe_transform( - { - "credential_info": credential_info, - "credential_name": credential_name, - "credential_values": credential_values, - "model_id": model_id, - }, - credential_create_params.CredentialCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """[BETA] endpoint. This might change unexpectedly.""" - return await self._get( - "/credentials", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - credential_name: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """[BETA] endpoint. - - This might change unexpectedly. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not credential_name: - raise ValueError( - f"Expected a non-empty value for `credential_name` but received {credential_name!r}" - ) - return await self._delete( - f"/credentials/{credential_name}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class CredentialsResourceWithRawResponse: - def __init__(self, credentials: CredentialsResource) -> None: - self._credentials = credentials - - self.create = to_raw_response_wrapper( - credentials.create, - ) - self.list = to_raw_response_wrapper( - credentials.list, - ) - self.delete = to_raw_response_wrapper( - credentials.delete, - ) - - -class AsyncCredentialsResourceWithRawResponse: - def __init__(self, credentials: AsyncCredentialsResource) -> None: - self._credentials = credentials - - self.create = async_to_raw_response_wrapper( - credentials.create, - ) - self.list = async_to_raw_response_wrapper( - credentials.list, - ) - self.delete = async_to_raw_response_wrapper( - credentials.delete, - ) - - -class CredentialsResourceWithStreamingResponse: - def __init__(self, credentials: CredentialsResource) -> None: - self._credentials = credentials - - self.create = to_streamed_response_wrapper( - credentials.create, - ) - self.list = to_streamed_response_wrapper( - credentials.list, - ) - self.delete = to_streamed_response_wrapper( - credentials.delete, - ) - - -class AsyncCredentialsResourceWithStreamingResponse: - def __init__(self, credentials: AsyncCredentialsResource) -> None: - self._credentials = credentials - - self.create = async_to_streamed_response_wrapper( - credentials.create, - ) - self.list = async_to_streamed_response_wrapper( - credentials.list, - ) - self.delete = async_to_streamed_response_wrapper( - credentials.delete, - ) diff --git a/pkg/hanzoai/resources/datastore.py b/pkg/hanzoai/resources/datastore.py deleted file mode 100644 index b3c9e9838..000000000 --- a/pkg/hanzoai/resources/datastore.py +++ /dev/null @@ -1,1593 +0,0 @@ -# Hanzo AI SDK โ€” Datastore (RAG Vector Store) - -from __future__ import annotations - -from typing import Any, Dict, List, Mapping, Optional, cast - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from .._utils import extract_files, deepcopy_minimal -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["DatastoreResource", "AsyncDatastoreResource"] - - -class DatastoreResource(SyncAPIResource): - """RAG vector store service for semantic search over embedded documents. - - Manages stores (knowledge bases), files, vectors, and search queries - against the Hanzo Cloud API backend. - """ - - @cached_property - def with_raw_response(self) -> DatastoreResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return DatastoreResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> DatastoreResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return DatastoreResourceWithStreamingResponse(self) - - # ------------------------------------------------------------------ - # Store CRUD - # ------------------------------------------------------------------ - - def list( - self, - *, - owner: str | NotGiven = NOT_GIVEN, - type: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all stores, optionally filtered by owner or type. - - Args: - owner: Filter stores by owner identifier. - type: Filter stores by type. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - query_params: Dict[str, Any] = {} - if not isinstance(owner, NotGiven): - query_params["owner"] = owner - if not isinstance(type, NotGiven): - query_params["type"] = type - return self._get( - "/datastore/stores", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=query_params if query_params else None, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - description: str | NotGiven = NOT_GIVEN, - embedding_provider: str | NotGiven = NOT_GIVEN, - embedding_model: str | NotGiven = NOT_GIVEN, - dimension: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new store (knowledge base) for RAG. - - Args: - name: Name of the store. - description: Human-readable description. - embedding_provider: Embedding provider (e.g. "OpenAI", "Cohere", "Jina"). - embedding_model: Model name for embedding generation. - dimension: Embedding vector dimension. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - body: Dict[str, Any] = {"name": name} - if not isinstance(description, NotGiven): - body["description"] = description - if not isinstance(embedding_provider, NotGiven): - body["embeddingProvider"] = embedding_provider - if not isinstance(embedding_model, NotGiven): - body["embeddingModel"] = embedding_model - if not isinstance(dimension, NotGiven): - body["dimension"] = dimension - return self._post( - "/datastore/stores", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retrieve( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a store by ID. - - Args: - store_id: The store identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - return self._get( - f"/datastore/stores/{store_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - store_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - embedding_provider: str | NotGiven = NOT_GIVEN, - embedding_model: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing store. - - Args: - store_id: The store identifier. - name: Updated name. - description: Updated description. - embedding_provider: Updated embedding provider. - embedding_model: Updated embedding model. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - body: Dict[str, Any] = {} - if not isinstance(name, NotGiven): - body["name"] = name - if not isinstance(description, NotGiven): - body["description"] = description - if not isinstance(embedding_provider, NotGiven): - body["embeddingProvider"] = embedding_provider - if not isinstance(embedding_model, NotGiven): - body["embeddingModel"] = embedding_model - return self._post( - f"/datastore/stores/{store_id}", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a store. - - Args: - store_id: The store identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - return self._delete( - f"/datastore/stores/{store_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # File management - # ------------------------------------------------------------------ - - def list_files( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all files in a store. - - Args: - store_id: The store identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - return self._get( - f"/datastore/stores/{store_id}/files", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_file( - self, - store_id: str, - *, - name: str, - content: str, - type: str | NotGiven = NOT_GIVEN, - split_method: str | NotGiven = NOT_GIVEN, - chunk_size: int | NotGiven = NOT_GIVEN, - chunk_overlap: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a file (text content) to a store for chunking and embedding. - - Args: - store_id: The store identifier. - name: File name. - content: Raw text content of the file. - type: File/content type hint. - split_method: How to split the document (e.g. "character", "token"). - chunk_size: Number of characters/tokens per chunk. - chunk_overlap: Overlap between consecutive chunks. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - body: Dict[str, Any] = { - "store_id": store_id, - "name": name, - "content": content, - } - if not isinstance(type, NotGiven): - body["type"] = type - if not isinstance(split_method, NotGiven): - body["splitMethod"] = split_method - if not isinstance(chunk_size, NotGiven): - body["chunkSize"] = chunk_size - if not isinstance(chunk_overlap, NotGiven): - body["chunkOverlap"] = chunk_overlap - return self._post( - f"/datastore/stores/{store_id}/files", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_file( - self, - file_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a file by ID. - - Args: - file_id: The file identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - return self._get( - f"/datastore/files/{file_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_file( - self, - file_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - content: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing file. - - Args: - file_id: The file identifier. - name: Updated file name. - content: Updated text content. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - body: Dict[str, Any] = {} - if not isinstance(name, NotGiven): - body["name"] = name - if not isinstance(content, NotGiven): - body["content"] = content - return self._post( - f"/datastore/files/{file_id}", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_file( - self, - file_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a file. - - Args: - file_id: The file identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - return self._delete( - f"/datastore/files/{file_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def upload_file( - self, - store_id: str, - *, - file: FileTypes, - split_method: str | NotGiven = NOT_GIVEN, - chunk_size: int | NotGiven = NOT_GIVEN, - chunk_overlap: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Upload a binary file (PDF, DOCX, etc.) to a store via multipart form. - - The backend parses the file, chunks it, and generates embeddings. - - Args: - store_id: The store identifier. - file: The file to upload (path, bytes, or file-like object). - split_method: How to split the document (e.g. "character", "token"). - chunk_size: Number of characters/tokens per chunk. - chunk_overlap: Overlap between consecutive chunks. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - body = deepcopy_minimal( - { - "file": file, - "store_id": store_id, - "split_method": split_method, - "chunk_size": chunk_size, - "chunk_overlap": chunk_overlap, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - f"/datastore/stores/{store_id}/upload", - body=body, - files=files, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Vector operations - # ------------------------------------------------------------------ - - def list_vectors( - self, - store_id: str, - *, - file_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List vectors in a store, optionally filtered by file. - - Args: - store_id: The store identifier. - file_id: Filter vectors by source file. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - query_params: Dict[str, Any] = {} - if not isinstance(file_id, NotGiven): - query_params["file_id"] = file_id - return self._get( - f"/datastore/stores/{store_id}/vectors", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=query_params if query_params else None, - ), - cast_to=object, - ) - - def create_vector( - self, - store_id: str, - *, - file_id: str, - content: str, - embedding: List[float] | NotGiven = NOT_GIVEN, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Manually add a vector to a store. - - Args: - store_id: The store identifier. - file_id: Source file identifier. - content: Text content for this vector chunk. - embedding: Pre-computed embedding vector (optional; backend can generate). - metadata: Arbitrary metadata attached to the vector. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - body: Dict[str, Any] = { - "store_id": store_id, - "file_id": file_id, - "content": content, - } - if not isinstance(embedding, NotGiven): - body["embedding"] = embedding - if not isinstance(metadata, NotGiven): - body["metadata"] = metadata - return self._post( - f"/datastore/stores/{store_id}/vectors", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_vector( - self, - vector_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a vector by ID. - - Args: - vector_id: The vector identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not vector_id: - raise ValueError(f"Expected a non-empty value for `vector_id` but received {vector_id!r}") - return self._delete( - f"/datastore/vectors/{vector_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def refresh_vectors( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Re-embed all documents in a store, regenerating all vectors. - - Args: - store_id: The store identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - return self._post( - f"/datastore/stores/{store_id}/vectors/refresh", - body={"store_id": store_id}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Search / Query - # ------------------------------------------------------------------ - - def query( - self, - store_id: str, - *, - query: str, - top_k: int | NotGiven = NOT_GIVEN, - score_threshold: float | NotGiven = NOT_GIVEN, - filter: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Semantic vector search against a store. - - Args: - store_id: The store identifier. - query: Natural language query string. - top_k: Maximum number of results to return. - score_threshold: Minimum cosine similarity score (0.0-1.0). - filter: Metadata filter object for narrowing results. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - body: Dict[str, Any] = { - "store_id": store_id, - "query": query, - } - if not isinstance(top_k, NotGiven): - body["topK"] = top_k - if not isinstance(score_threshold, NotGiven): - body["scoreThreshold"] = score_threshold - if not isinstance(filter, NotGiven): - body["filter"] = filter - return self._post( - f"/datastore/stores/{store_id}/query", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def search( - self, - store_id: str, - *, - query: str, - top_k: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """LLM-enhanced hierarchical search against a store. - - Uses an LLM to refine and rank results beyond pure vector similarity. - - Args: - store_id: The store identifier. - query: Natural language query string. - top_k: Maximum number of results to return. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - body: Dict[str, Any] = { - "store_id": store_id, - "query": query, - } - if not isinstance(top_k, NotGiven): - body["topK"] = top_k - return self._post( - f"/datastore/stores/{store_id}/search", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncDatastoreResource(AsyncAPIResource): - """RAG vector store service for semantic search over embedded documents (async). - - Manages stores (knowledge bases), files, vectors, and search queries - against the Hanzo Cloud API backend. - """ - - @cached_property - def with_raw_response(self) -> AsyncDatastoreResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncDatastoreResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncDatastoreResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncDatastoreResourceWithStreamingResponse(self) - - # ------------------------------------------------------------------ - # Store CRUD - # ------------------------------------------------------------------ - - async def list( - self, - *, - owner: str | NotGiven = NOT_GIVEN, - type: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all stores, optionally filtered by owner or type. - - Args: - owner: Filter stores by owner identifier. - type: Filter stores by type. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - query_params: Dict[str, Any] = {} - if not isinstance(owner, NotGiven): - query_params["owner"] = owner - if not isinstance(type, NotGiven): - query_params["type"] = type - return await self._get( - "/datastore/stores", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=query_params if query_params else None, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - description: str | NotGiven = NOT_GIVEN, - embedding_provider: str | NotGiven = NOT_GIVEN, - embedding_model: str | NotGiven = NOT_GIVEN, - dimension: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new store (knowledge base) for RAG. - - Args: - name: Name of the store. - description: Human-readable description. - embedding_provider: Embedding provider (e.g. "OpenAI", "Cohere", "Jina"). - embedding_model: Model name for embedding generation. - dimension: Embedding vector dimension. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - body: Dict[str, Any] = {"name": name} - if not isinstance(description, NotGiven): - body["description"] = description - if not isinstance(embedding_provider, NotGiven): - body["embeddingProvider"] = embedding_provider - if not isinstance(embedding_model, NotGiven): - body["embeddingModel"] = embedding_model - if not isinstance(dimension, NotGiven): - body["dimension"] = dimension - return await self._post( - "/datastore/stores", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a store by ID. - - Args: - store_id: The store identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - return await self._get( - f"/datastore/stores/{store_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - store_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - embedding_provider: str | NotGiven = NOT_GIVEN, - embedding_model: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing store. - - Args: - store_id: The store identifier. - name: Updated name. - description: Updated description. - embedding_provider: Updated embedding provider. - embedding_model: Updated embedding model. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - body: Dict[str, Any] = {} - if not isinstance(name, NotGiven): - body["name"] = name - if not isinstance(description, NotGiven): - body["description"] = description - if not isinstance(embedding_provider, NotGiven): - body["embeddingProvider"] = embedding_provider - if not isinstance(embedding_model, NotGiven): - body["embeddingModel"] = embedding_model - return await self._post( - f"/datastore/stores/{store_id}", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a store. - - Args: - store_id: The store identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - return await self._delete( - f"/datastore/stores/{store_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # File management - # ------------------------------------------------------------------ - - async def list_files( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all files in a store. - - Args: - store_id: The store identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - return await self._get( - f"/datastore/stores/{store_id}/files", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_file( - self, - store_id: str, - *, - name: str, - content: str, - type: str | NotGiven = NOT_GIVEN, - split_method: str | NotGiven = NOT_GIVEN, - chunk_size: int | NotGiven = NOT_GIVEN, - chunk_overlap: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a file (text content) to a store for chunking and embedding. - - Args: - store_id: The store identifier. - name: File name. - content: Raw text content of the file. - type: File/content type hint. - split_method: How to split the document (e.g. "character", "token"). - chunk_size: Number of characters/tokens per chunk. - chunk_overlap: Overlap between consecutive chunks. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - body: Dict[str, Any] = { - "store_id": store_id, - "name": name, - "content": content, - } - if not isinstance(type, NotGiven): - body["type"] = type - if not isinstance(split_method, NotGiven): - body["splitMethod"] = split_method - if not isinstance(chunk_size, NotGiven): - body["chunkSize"] = chunk_size - if not isinstance(chunk_overlap, NotGiven): - body["chunkOverlap"] = chunk_overlap - return await self._post( - f"/datastore/stores/{store_id}/files", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_file( - self, - file_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a file by ID. - - Args: - file_id: The file identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - return await self._get( - f"/datastore/files/{file_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_file( - self, - file_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - content: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing file. - - Args: - file_id: The file identifier. - name: Updated file name. - content: Updated text content. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - body: Dict[str, Any] = {} - if not isinstance(name, NotGiven): - body["name"] = name - if not isinstance(content, NotGiven): - body["content"] = content - return await self._post( - f"/datastore/files/{file_id}", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_file( - self, - file_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a file. - - Args: - file_id: The file identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - return await self._delete( - f"/datastore/files/{file_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def upload_file( - self, - store_id: str, - *, - file: FileTypes, - split_method: str | NotGiven = NOT_GIVEN, - chunk_size: int | NotGiven = NOT_GIVEN, - chunk_overlap: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Upload a binary file (PDF, DOCX, etc.) to a store via multipart form. - - The backend parses the file, chunks it, and generates embeddings. - - Args: - store_id: The store identifier. - file: The file to upload (path, bytes, or file-like object). - split_method: How to split the document (e.g. "character", "token"). - chunk_size: Number of characters/tokens per chunk. - chunk_overlap: Overlap between consecutive chunks. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - body = deepcopy_minimal( - { - "file": file, - "store_id": store_id, - "split_method": split_method, - "chunk_size": chunk_size, - "chunk_overlap": chunk_overlap, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - f"/datastore/stores/{store_id}/upload", - body=body, - files=files, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Vector operations - # ------------------------------------------------------------------ - - async def list_vectors( - self, - store_id: str, - *, - file_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List vectors in a store, optionally filtered by file. - - Args: - store_id: The store identifier. - file_id: Filter vectors by source file. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - query_params: Dict[str, Any] = {} - if not isinstance(file_id, NotGiven): - query_params["file_id"] = file_id - return await self._get( - f"/datastore/stores/{store_id}/vectors", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=query_params if query_params else None, - ), - cast_to=object, - ) - - async def create_vector( - self, - store_id: str, - *, - file_id: str, - content: str, - embedding: List[float] | NotGiven = NOT_GIVEN, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Manually add a vector to a store. - - Args: - store_id: The store identifier. - file_id: Source file identifier. - content: Text content for this vector chunk. - embedding: Pre-computed embedding vector (optional; backend can generate). - metadata: Arbitrary metadata attached to the vector. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - body: Dict[str, Any] = { - "store_id": store_id, - "file_id": file_id, - "content": content, - } - if not isinstance(embedding, NotGiven): - body["embedding"] = embedding - if not isinstance(metadata, NotGiven): - body["metadata"] = metadata - return await self._post( - f"/datastore/stores/{store_id}/vectors", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_vector( - self, - vector_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a vector by ID. - - Args: - vector_id: The vector identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not vector_id: - raise ValueError(f"Expected a non-empty value for `vector_id` but received {vector_id!r}") - return await self._delete( - f"/datastore/vectors/{vector_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def refresh_vectors( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Re-embed all documents in a store, regenerating all vectors. - - Args: - store_id: The store identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - return await self._post( - f"/datastore/stores/{store_id}/vectors/refresh", - body={"store_id": store_id}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ - # Search / Query - # ------------------------------------------------------------------ - - async def query( - self, - store_id: str, - *, - query: str, - top_k: int | NotGiven = NOT_GIVEN, - score_threshold: float | NotGiven = NOT_GIVEN, - filter: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Semantic vector search against a store. - - Args: - store_id: The store identifier. - query: Natural language query string. - top_k: Maximum number of results to return. - score_threshold: Minimum cosine similarity score (0.0-1.0). - filter: Metadata filter object for narrowing results. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - body: Dict[str, Any] = { - "store_id": store_id, - "query": query, - } - if not isinstance(top_k, NotGiven): - body["topK"] = top_k - if not isinstance(score_threshold, NotGiven): - body["scoreThreshold"] = score_threshold - if not isinstance(filter, NotGiven): - body["filter"] = filter - return await self._post( - f"/datastore/stores/{store_id}/query", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def search( - self, - store_id: str, - *, - query: str, - top_k: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """LLM-enhanced hierarchical search against a store. - - Uses an LLM to refine and rank results beyond pure vector similarity. - - Args: - store_id: The store identifier. - query: Natural language query string. - top_k: Maximum number of results to return. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - if not store_id: - raise ValueError(f"Expected a non-empty value for `store_id` but received {store_id!r}") - body: Dict[str, Any] = { - "store_id": store_id, - "query": query, - } - if not isinstance(top_k, NotGiven): - body["topK"] = top_k - return await self._post( - f"/datastore/stores/{store_id}/search", - body=body, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class DatastoreResourceWithRawResponse: - def __init__(self, datastore: DatastoreResource) -> None: - self._datastore = datastore - - # Store CRUD - self.list = to_raw_response_wrapper(datastore.list) - self.create = to_raw_response_wrapper(datastore.create) - self.retrieve = to_raw_response_wrapper(datastore.retrieve) - self.update = to_raw_response_wrapper(datastore.update) - self.delete = to_raw_response_wrapper(datastore.delete) - - # File management - self.list_files = to_raw_response_wrapper(datastore.list_files) - self.create_file = to_raw_response_wrapper(datastore.create_file) - self.retrieve_file = to_raw_response_wrapper(datastore.retrieve_file) - self.update_file = to_raw_response_wrapper(datastore.update_file) - self.delete_file = to_raw_response_wrapper(datastore.delete_file) - self.upload_file = to_raw_response_wrapper(datastore.upload_file) - - # Vector operations - self.list_vectors = to_raw_response_wrapper(datastore.list_vectors) - self.create_vector = to_raw_response_wrapper(datastore.create_vector) - self.delete_vector = to_raw_response_wrapper(datastore.delete_vector) - self.refresh_vectors = to_raw_response_wrapper(datastore.refresh_vectors) - - # Search / Query - self.query = to_raw_response_wrapper(datastore.query) - self.search = to_raw_response_wrapper(datastore.search) - - -class AsyncDatastoreResourceWithRawResponse: - def __init__(self, datastore: AsyncDatastoreResource) -> None: - self._datastore = datastore - - # Store CRUD - self.list = async_to_raw_response_wrapper(datastore.list) - self.create = async_to_raw_response_wrapper(datastore.create) - self.retrieve = async_to_raw_response_wrapper(datastore.retrieve) - self.update = async_to_raw_response_wrapper(datastore.update) - self.delete = async_to_raw_response_wrapper(datastore.delete) - - # File management - self.list_files = async_to_raw_response_wrapper(datastore.list_files) - self.create_file = async_to_raw_response_wrapper(datastore.create_file) - self.retrieve_file = async_to_raw_response_wrapper(datastore.retrieve_file) - self.update_file = async_to_raw_response_wrapper(datastore.update_file) - self.delete_file = async_to_raw_response_wrapper(datastore.delete_file) - self.upload_file = async_to_raw_response_wrapper(datastore.upload_file) - - # Vector operations - self.list_vectors = async_to_raw_response_wrapper(datastore.list_vectors) - self.create_vector = async_to_raw_response_wrapper(datastore.create_vector) - self.delete_vector = async_to_raw_response_wrapper(datastore.delete_vector) - self.refresh_vectors = async_to_raw_response_wrapper(datastore.refresh_vectors) - - # Search / Query - self.query = async_to_raw_response_wrapper(datastore.query) - self.search = async_to_raw_response_wrapper(datastore.search) - - -class DatastoreResourceWithStreamingResponse: - def __init__(self, datastore: DatastoreResource) -> None: - self._datastore = datastore - - # Store CRUD - self.list = to_streamed_response_wrapper(datastore.list) - self.create = to_streamed_response_wrapper(datastore.create) - self.retrieve = to_streamed_response_wrapper(datastore.retrieve) - self.update = to_streamed_response_wrapper(datastore.update) - self.delete = to_streamed_response_wrapper(datastore.delete) - - # File management - self.list_files = to_streamed_response_wrapper(datastore.list_files) - self.create_file = to_streamed_response_wrapper(datastore.create_file) - self.retrieve_file = to_streamed_response_wrapper(datastore.retrieve_file) - self.update_file = to_streamed_response_wrapper(datastore.update_file) - self.delete_file = to_streamed_response_wrapper(datastore.delete_file) - self.upload_file = to_streamed_response_wrapper(datastore.upload_file) - - # Vector operations - self.list_vectors = to_streamed_response_wrapper(datastore.list_vectors) - self.create_vector = to_streamed_response_wrapper(datastore.create_vector) - self.delete_vector = to_streamed_response_wrapper(datastore.delete_vector) - self.refresh_vectors = to_streamed_response_wrapper(datastore.refresh_vectors) - - # Search / Query - self.query = to_streamed_response_wrapper(datastore.query) - self.search = to_streamed_response_wrapper(datastore.search) - - -class AsyncDatastoreResourceWithStreamingResponse: - def __init__(self, datastore: AsyncDatastoreResource) -> None: - self._datastore = datastore - - # Store CRUD - self.list = async_to_streamed_response_wrapper(datastore.list) - self.create = async_to_streamed_response_wrapper(datastore.create) - self.retrieve = async_to_streamed_response_wrapper(datastore.retrieve) - self.update = async_to_streamed_response_wrapper(datastore.update) - self.delete = async_to_streamed_response_wrapper(datastore.delete) - - # File management - self.list_files = async_to_streamed_response_wrapper(datastore.list_files) - self.create_file = async_to_streamed_response_wrapper(datastore.create_file) - self.retrieve_file = async_to_streamed_response_wrapper(datastore.retrieve_file) - self.update_file = async_to_streamed_response_wrapper(datastore.update_file) - self.delete_file = async_to_streamed_response_wrapper(datastore.delete_file) - self.upload_file = async_to_streamed_response_wrapper(datastore.upload_file) - - # Vector operations - self.list_vectors = async_to_streamed_response_wrapper(datastore.list_vectors) - self.create_vector = async_to_streamed_response_wrapper(datastore.create_vector) - self.delete_vector = async_to_streamed_response_wrapper(datastore.delete_vector) - self.refresh_vectors = async_to_streamed_response_wrapper(datastore.refresh_vectors) - - # Search / Query - self.query = async_to_streamed_response_wrapper(datastore.query) - self.search = async_to_streamed_response_wrapper(datastore.search) diff --git a/pkg/hanzoai/resources/db.py b/pkg/hanzoai/resources/db.py deleted file mode 100644 index 7e241fdc8..000000000 --- a/pkg/hanzoai/resources/db.py +++ /dev/null @@ -1,550 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class DBResource(SyncAPIResource): - """Managed database service (Postgres/Redis/etc.).""" - - @cached_property - def with_raw_response(self) -> DBResourceWithRawResponse: - return DBResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> DBResourceWithStreamingResponse: - return DBResourceWithStreamingResponse(self) - - def create( - self, - *, - name: str, - engine: str, - version: str | NotGiven = NOT_GIVEN, - size: str | NotGiven = NOT_GIVEN, - region: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a managed database.""" - return self._post( - "/db", - body={ - "name": name, - "engine": engine, - "version": version, - "size": size, - "region": region, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List databases.""" - return self._get( - "/db", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - db_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get database details.""" - return self._get( - f"/db/{db_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - db_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a database.""" - return self._delete( - f"/db/{db_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_user( - self, - db_id: str, - *, - username: str, - password: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a database user.""" - return self._post( - f"/db/{db_id}/users", - body={"username": username, "password": password}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def rotate_user( - self, - db_id: str, - username: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Rotate user credentials.""" - return self._post( - f"/db/{db_id}/users/{username}/rotate", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_user( - self, - db_id: str, - username: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a database user.""" - return self._delete( - f"/db/{db_id}/users/{username}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_backup( - self, - db_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a backup.""" - return self._post( - f"/db/{db_id}/backups", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_backups( - self, - db_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List backups.""" - return self._get( - f"/db/{db_id}/backups", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def restore( - self, - db_id: str, - backup_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Restore from backup.""" - return self._post( - f"/db/{db_id}/backups/{backup_id}/restore", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def connstr( - self, - db_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get connection string.""" - return self._get( - f"/db/{db_id}/connstr", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncDBResource(AsyncAPIResource): - """Managed database service (Postgres/Redis/etc.).""" - - @cached_property - def with_raw_response(self) -> AsyncDBResourceWithRawResponse: - return AsyncDBResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncDBResourceWithStreamingResponse: - return AsyncDBResourceWithStreamingResponse(self) - - async def create( - self, - *, - name: str, - engine: str, - version: str | NotGiven = NOT_GIVEN, - size: str | NotGiven = NOT_GIVEN, - region: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a managed database.""" - return await self._post( - "/db", - body={ - "name": name, - "engine": engine, - "version": version, - "size": size, - "region": region, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List databases.""" - return await self._get( - "/db", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - db_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get database details.""" - return await self._get( - f"/db/{db_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - db_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a database.""" - return await self._delete( - f"/db/{db_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_user( - self, - db_id: str, - *, - username: str, - password: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a database user.""" - return await self._post( - f"/db/{db_id}/users", - body={"username": username, "password": password}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def rotate_user( - self, - db_id: str, - username: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Rotate user credentials.""" - return await self._post( - f"/db/{db_id}/users/{username}/rotate", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_user( - self, - db_id: str, - username: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a database user.""" - return await self._delete( - f"/db/{db_id}/users/{username}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_backup( - self, - db_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a backup.""" - return await self._post( - f"/db/{db_id}/backups", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_backups( - self, - db_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List backups.""" - return await self._get( - f"/db/{db_id}/backups", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def restore( - self, - db_id: str, - backup_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Restore from backup.""" - return await self._post( - f"/db/{db_id}/backups/{backup_id}/restore", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def connstr( - self, - db_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get connection string.""" - return await self._get( - f"/db/{db_id}/connstr", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class DBResourceWithRawResponse: - def __init__(self, db: DBResource) -> None: - self._db = db - - -class AsyncDBResourceWithRawResponse: - def __init__(self, db: AsyncDBResource) -> None: - self._db = db - - -class DBResourceWithStreamingResponse: - def __init__(self, db: DBResource) -> None: - self._db = db - - -class AsyncDBResourceWithStreamingResponse: - def __init__(self, db: AsyncDBResource) -> None: - self._db = db diff --git a/pkg/hanzoai/resources/deployments.py b/pkg/hanzoai/resources/deployments.py deleted file mode 100644 index 2cb173e2d..000000000 --- a/pkg/hanzoai/resources/deployments.py +++ /dev/null @@ -1,513 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["DeploymentsResource", "AsyncDeploymentsResource"] - - -class DeploymentsResource(SyncAPIResource): - """Kubernetes deployment management.""" - - @cached_property - def with_raw_response(self) -> DeploymentsResourceWithRawResponse: - return DeploymentsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> DeploymentsResourceWithStreamingResponse: - return DeploymentsResourceWithStreamingResponse(self) - - def list( - self, - *, - namespace: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all deployments.""" - return self._get( - "/infrastructure/deployments", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"namespace": namespace}, - ), - cast_to=object, - ) - - def get( - self, - deployment_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific deployment.""" - return self._get( - f"/infrastructure/deployments/{deployment_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - namespace: str, - image: str, - replicas: int = 1, - env: Dict[str, str] | NotGiven = NOT_GIVEN, - resources: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new deployment.""" - return self._post( - "/infrastructure/deployments", - body={ - "name": name, - "namespace": namespace, - "image": image, - "replicas": replicas, - "env": env, - "resources": resources, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - deployment_id: str, - *, - image: str | NotGiven = NOT_GIVEN, - replicas: int | NotGiven = NOT_GIVEN, - env: Dict[str, str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a deployment.""" - return self._put( - f"/infrastructure/deployments/{deployment_id}", - body={"image": image, "replicas": replicas, "env": env}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - deployment_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a deployment.""" - return self._delete( - f"/infrastructure/deployments/{deployment_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def scale( - self, - deployment_id: str, - *, - replicas: int, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Scale a deployment.""" - return self._post( - f"/infrastructure/deployments/{deployment_id}/scale", - body={"replicas": replicas}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def restart( - self, - deployment_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Restart a deployment.""" - return self._post( - f"/infrastructure/deployments/{deployment_id}/restart", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def rollback( - self, - deployment_id: str, - *, - revision: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Rollback a deployment.""" - return self._post( - f"/infrastructure/deployments/{deployment_id}/rollback", - body={"revision": revision}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def status( - self, - deployment_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get deployment status.""" - return self._get( - f"/infrastructure/deployments/{deployment_id}/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncDeploymentsResource(AsyncAPIResource): - """Kubernetes deployment management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncDeploymentsResourceWithRawResponse: - return AsyncDeploymentsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncDeploymentsResourceWithStreamingResponse: - return AsyncDeploymentsResourceWithStreamingResponse(self) - - async def list( - self, - *, - namespace: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/infrastructure/deployments", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"namespace": namespace}, - ), - cast_to=object, - ) - - async def get( - self, - deployment_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/infrastructure/deployments/{deployment_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - namespace: str, - image: str, - replicas: int = 1, - env: Dict[str, str] | NotGiven = NOT_GIVEN, - resources: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/infrastructure/deployments", - body={ - "name": name, - "namespace": namespace, - "image": image, - "replicas": replicas, - "env": env, - "resources": resources, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - deployment_id: str, - *, - image: str | NotGiven = NOT_GIVEN, - replicas: int | NotGiven = NOT_GIVEN, - env: Dict[str, str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._put( - f"/infrastructure/deployments/{deployment_id}", - body={"image": image, "replicas": replicas, "env": env}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - deployment_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/infrastructure/deployments/{deployment_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def scale( - self, - deployment_id: str, - *, - replicas: int, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/infrastructure/deployments/{deployment_id}/scale", - body={"replicas": replicas}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def restart( - self, - deployment_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/infrastructure/deployments/{deployment_id}/restart", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def rollback( - self, - deployment_id: str, - *, - revision: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/infrastructure/deployments/{deployment_id}/rollback", - body={"revision": revision}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def status( - self, - deployment_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/infrastructure/deployments/{deployment_id}/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class DeploymentsResourceWithRawResponse: - def __init__(self, deployments: DeploymentsResource) -> None: - self._deployments = deployments - self.list = to_raw_response_wrapper(deployments.list) - self.get = to_raw_response_wrapper(deployments.get) - self.create = to_raw_response_wrapper(deployments.create) - self.update = to_raw_response_wrapper(deployments.update) - self.delete = to_raw_response_wrapper(deployments.delete) - self.scale = to_raw_response_wrapper(deployments.scale) - self.restart = to_raw_response_wrapper(deployments.restart) - self.rollback = to_raw_response_wrapper(deployments.rollback) - self.status = to_raw_response_wrapper(deployments.status) - - -class AsyncDeploymentsResourceWithRawResponse: - def __init__(self, deployments: AsyncDeploymentsResource) -> None: - self._deployments = deployments - self.list = async_to_raw_response_wrapper(deployments.list) - self.get = async_to_raw_response_wrapper(deployments.get) - self.create = async_to_raw_response_wrapper(deployments.create) - self.update = async_to_raw_response_wrapper(deployments.update) - self.delete = async_to_raw_response_wrapper(deployments.delete) - self.scale = async_to_raw_response_wrapper(deployments.scale) - self.restart = async_to_raw_response_wrapper(deployments.restart) - self.rollback = async_to_raw_response_wrapper(deployments.rollback) - self.status = async_to_raw_response_wrapper(deployments.status) - - -class DeploymentsResourceWithStreamingResponse: - def __init__(self, deployments: DeploymentsResource) -> None: - self._deployments = deployments - self.list = to_streamed_response_wrapper(deployments.list) - self.get = to_streamed_response_wrapper(deployments.get) - self.create = to_streamed_response_wrapper(deployments.create) - self.update = to_streamed_response_wrapper(deployments.update) - self.delete = to_streamed_response_wrapper(deployments.delete) - self.scale = to_streamed_response_wrapper(deployments.scale) - self.restart = to_streamed_response_wrapper(deployments.restart) - self.rollback = to_streamed_response_wrapper(deployments.rollback) - self.status = to_streamed_response_wrapper(deployments.status) - - -class AsyncDeploymentsResourceWithStreamingResponse: - def __init__(self, deployments: AsyncDeploymentsResource) -> None: - self._deployments = deployments - self.list = async_to_streamed_response_wrapper(deployments.list) - self.get = async_to_streamed_response_wrapper(deployments.get) - self.create = async_to_streamed_response_wrapper(deployments.create) - self.update = async_to_streamed_response_wrapper(deployments.update) - self.delete = async_to_streamed_response_wrapper(deployments.delete) - self.scale = async_to_streamed_response_wrapper(deployments.scale) - self.restart = async_to_streamed_response_wrapper(deployments.restart) - self.rollback = async_to_streamed_response_wrapper(deployments.rollback) - self.status = async_to_streamed_response_wrapper(deployments.status) diff --git a/pkg/hanzoai/resources/device.py b/pkg/hanzoai/resources/device.py deleted file mode 100644 index 49a9c07f3..000000000 --- a/pkg/hanzoai/resources/device.py +++ /dev/null @@ -1,314 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class DeviceResource(SyncAPIResource): - """Device enrollment and posture management.""" - - @cached_property - def with_raw_response(self) -> DeviceResourceWithRawResponse: - return DeviceResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> DeviceResourceWithStreamingResponse: - return DeviceResourceWithStreamingResponse(self) - - def enroll( - self, - *, - code: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Enroll a device (QR/one-time code).""" - return self._post( - "/device/enroll", - body={"code": code}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List enrolled devices.""" - return self._get( - "/device", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def revoke( - self, - device_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Revoke a device.""" - return self._delete( - f"/device/{device_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def posture( - self, - device_id: str | NotGiven = NOT_GIVEN, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get device posture.""" - return self._get( - "/device/posture", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"device_id": device_id}, - ), - cast_to=object, - ) - - def set_trust( - self, - device_id: str, - *, - trust_level: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Set device trust level.""" - return self._post( - f"/device/{device_id}/trust", - body={"trust_level": trust_level}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get_trust( - self, - device_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get device trust level.""" - return self._get( - f"/device/{device_id}/trust", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncDeviceResource(AsyncAPIResource): - """Device enrollment and posture management.""" - - @cached_property - def with_raw_response(self) -> AsyncDeviceResourceWithRawResponse: - return AsyncDeviceResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncDeviceResourceWithStreamingResponse: - return AsyncDeviceResourceWithStreamingResponse(self) - - async def enroll( - self, - *, - code: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Enroll a device (QR/one-time code).""" - return await self._post( - "/device/enroll", - body={"code": code}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List enrolled devices.""" - return await self._get( - "/device", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def revoke( - self, - device_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Revoke a device.""" - return await self._delete( - f"/device/{device_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def posture( - self, - device_id: str | NotGiven = NOT_GIVEN, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get device posture.""" - return await self._get( - "/device/posture", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"device_id": device_id}, - ), - cast_to=object, - ) - - async def set_trust( - self, - device_id: str, - *, - trust_level: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Set device trust level.""" - return await self._post( - f"/device/{device_id}/trust", - body={"trust_level": trust_level}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get_trust( - self, - device_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get device trust level.""" - return await self._get( - f"/device/{device_id}/trust", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class DeviceResourceWithRawResponse: - def __init__(self, device: DeviceResource) -> None: - self._device = device - - -class AsyncDeviceResourceWithRawResponse: - def __init__(self, device: AsyncDeviceResource) -> None: - self._device = device - - -class DeviceResourceWithStreamingResponse: - def __init__(self, device: DeviceResource) -> None: - self._device = device - - -class AsyncDeviceResourceWithStreamingResponse: - def __init__(self, device: AsyncDeviceResource) -> None: - self._device = device diff --git a/pkg/hanzoai/resources/dns.py b/pkg/hanzoai/resources/dns.py deleted file mode 100644 index 59be33463..000000000 --- a/pkg/hanzoai/resources/dns.py +++ /dev/null @@ -1,534 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class DNSResource(SyncAPIResource): - """DNS management service.""" - - @cached_property - def with_raw_response(self) -> DNSResourceWithRawResponse: - return DNSResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> DNSResourceWithStreamingResponse: - return DNSResourceWithStreamingResponse(self) - - # Zone management - def create_zone( - self, - *, - name: str, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a DNS zone.""" - return self._post( - "/dns/zones", - body={"name": name, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_zones( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List DNS zones.""" - return self._get( - "/dns/zones", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_zone( - self, - zone_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a DNS zone.""" - return self._delete( - f"/dns/zones/{zone_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Record management - def set_record( - self, - zone_id: str, - *, - name: str, - type: str, - value: str, - ttl: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Set a DNS record.""" - return self._post( - f"/dns/zones/{zone_id}/records", - body={"name": name, "type": type, "value": value, "ttl": ttl}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_records( - self, - zone_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List DNS records in a zone.""" - return self._get( - f"/dns/zones/{zone_id}/records", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_record( - self, - zone_id: str, - record_id: str, - *, - type: str, - name: str, - content: str, - proxied: bool | NotGiven = NOT_GIVEN, - ttl: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a DNS record.""" - return self._put( - f"/dns/zones/{zone_id}/records/{record_id}", - body={ - "type": type, - "name": name, - "content": content, - "proxied": proxied, - "ttl": ttl, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_record( - self, - zone_id: str, - record_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a DNS record.""" - return self._delete( - f"/dns/zones/{zone_id}/records/{record_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Domain verification - def verify_domain( - self, - domain: str, - *, - expected_ip: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Verify DNS configuration for a domain.""" - return self._post( - "/dns/verify", - body={"domain": domain, "expectedIp": expected_ip}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Service linking - def link( - self, - *, - service: str, - hostname: str, - zone_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Link a service to a hostname.""" - return self._post( - "/dns/link", - body={"service": service, "hostname": hostname, "zone_id": zone_id}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Health checks - def health( - self, - *, - hostname: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Check DNS resolution and propagation health.""" - return self._get( - "/dns/health", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"hostname": hostname}, - ), - cast_to=object, - ) - - -class AsyncDNSResource(AsyncAPIResource): - """DNS management service.""" - - @cached_property - def with_raw_response(self) -> AsyncDNSResourceWithRawResponse: - return AsyncDNSResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncDNSResourceWithStreamingResponse: - return AsyncDNSResourceWithStreamingResponse(self) - - async def create_zone( - self, - *, - name: str, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a DNS zone.""" - return await self._post( - "/dns/zones", - body={"name": name, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_zones( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List DNS zones.""" - return await self._get( - "/dns/zones", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_zone( - self, - zone_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a DNS zone.""" - return await self._delete( - f"/dns/zones/{zone_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def set_record( - self, - zone_id: str, - *, - name: str, - type: str, - value: str, - ttl: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Set a DNS record.""" - return await self._post( - f"/dns/zones/{zone_id}/records", - body={"name": name, "type": type, "value": value, "ttl": ttl}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_records( - self, - zone_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List DNS records in a zone.""" - return await self._get( - f"/dns/zones/{zone_id}/records", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_record( - self, - zone_id: str, - record_id: str, - *, - type: str, - name: str, - content: str, - proxied: bool | NotGiven = NOT_GIVEN, - ttl: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a DNS record.""" - return await self._put( - f"/dns/zones/{zone_id}/records/{record_id}", - body={ - "type": type, - "name": name, - "content": content, - "proxied": proxied, - "ttl": ttl, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_record( - self, - zone_id: str, - record_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a DNS record.""" - return await self._delete( - f"/dns/zones/{zone_id}/records/{record_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Domain verification - async def verify_domain( - self, - domain: str, - *, - expected_ip: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Verify DNS configuration for a domain.""" - return await self._post( - "/dns/verify", - body={"domain": domain, "expectedIp": expected_ip}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def link( - self, - *, - service: str, - hostname: str, - zone_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Link a service to a hostname.""" - return await self._post( - "/dns/link", - body={"service": service, "hostname": hostname, "zone_id": zone_id}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def health( - self, - *, - hostname: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Check DNS resolution and propagation health.""" - return await self._get( - "/dns/health", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"hostname": hostname}, - ), - cast_to=object, - ) - - -class DNSResourceWithRawResponse: - def __init__(self, dns: DNSResource) -> None: - self._dns = dns - - -class AsyncDNSResourceWithRawResponse: - def __init__(self, dns: AsyncDNSResource) -> None: - self._dns = dns - - -class DNSResourceWithStreamingResponse: - def __init__(self, dns: DNSResource) -> None: - self._dns = dns - - -class AsyncDNSResourceWithStreamingResponse: - def __init__(self, dns: AsyncDNSResource) -> None: - self._dns = dns diff --git a/pkg/hanzoai/resources/docdb.py b/pkg/hanzoai/resources/docdb.py deleted file mode 100644 index 40488f3c7..000000000 --- a/pkg/hanzoai/resources/docdb.py +++ /dev/null @@ -1,1077 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List, Mapping, Optional, cast - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from .._utils import extract_files, deepcopy_minimal -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["DocDBResource", "AsyncDocDBResource"] - - -class DocDBResource(SyncAPIResource): - """Document database service backed by FerretDB + PostgreSQL. - - Manages document stores, files, vectors, and tree-structured documents - via the Hanzo Cloud API. - """ - - @cached_property - def with_raw_response(self) -> DocDBResourceWithRawResponse: - return DocDBResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> DocDBResourceWithStreamingResponse: - return DocDBResourceWithStreamingResponse(self) - - # โ”€โ”€ Stores โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_stores( - self, - *, - owner: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all document stores. - - Args: - owner: Filter stores by owner. - """ - return self._get( - "/docdb/stores", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - def create_store( - self, - *, - name: str, - description: str | NotGiven = NOT_GIVEN, - owner: str | NotGiven = NOT_GIVEN, - tenant: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new document store. - - Args: - name: Store name. - description: Optional store description. - owner: Owner identifier. - tenant: Tenant identifier for multi-tenancy. - """ - return self._post( - "/docdb/stores", - body={ - "name": name, - "description": description, - "owner": owner, - "tenant": tenant, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_store( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific document store. - - Args: - store_id: The store ID. - """ - return self._get( - f"/docdb/stores/{store_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_store( - self, - store_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update store metadata. - - Args: - store_id: The store ID. - name: New store name. - description: New store description. - """ - return self._post( - f"/docdb/stores/{store_id}", - body={"name": name, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_store( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a document store. - - Args: - store_id: The store ID. - """ - return self._delete( - f"/docdb/stores/{store_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Files / Documents โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_files( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List files in a document store. - - Args: - store_id: The store ID. - """ - return self._get( - f"/docdb/stores/{store_id}/files", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_file( - self, - store_id: str, - *, - name: str, - content: str, - type: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a file/document to a store. - - Args: - store_id: The store ID. - name: File name. - content: File content. - type: File type/mime. - metadata: Arbitrary metadata dict. - """ - return self._post( - f"/docdb/stores/{store_id}/files", - body={ - "name": name, - "content": content, - "type": type, - "metadata": metadata, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_file( - self, - file_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific file. - - Args: - file_id: The file ID. - """ - return self._get( - f"/docdb/files/{file_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_file( - self, - file_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - content: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update file metadata or content. - - Args: - file_id: The file ID. - name: New file name. - content: New file content. - metadata: Updated metadata dict. - """ - return self._post( - f"/docdb/files/{file_id}", - body={"name": name, "content": content, "metadata": metadata}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_file( - self, - file_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a file. - - Args: - file_id: The file ID. - """ - return self._delete( - f"/docdb/files/{file_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def upload_file( - self, - store_id: str, - *, - file: FileTypes, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Upload a file to a store via multipart form data. - - Args: - store_id: The store ID. - file: The file to upload. - metadata: Optional metadata dict. - """ - body = deepcopy_minimal({"file": file, "metadata": metadata}) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - f"/docdb/stores/{store_id}/upload", - body=body, - files=files, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Vectors โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_vectors( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List vectors for a store. - - Args: - store_id: The store ID. - """ - return self._get( - f"/docdb/stores/{store_id}/vectors", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_vector( - self, - store_id: str, - *, - file_id: str, - content: str, - embedding: List[float] | NotGiven = NOT_GIVEN, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a vector to a store. - - Args: - store_id: The store ID. - file_id: The associated file ID. - content: Text content for the vector. - embedding: Pre-computed embedding. If omitted, the server will generate one. - metadata: Arbitrary metadata dict. - """ - return self._post( - f"/docdb/stores/{store_id}/vectors", - body={ - "file_id": file_id, - "content": content, - "embedding": embedding, - "metadata": metadata, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_vector( - self, - vector_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a vector. - - Args: - vector_id: The vector ID. - """ - return self._delete( - f"/docdb/vectors/{vector_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def refresh_vectors( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Re-embed all vectors in a store. - - Args: - store_id: The store ID. - """ - return self._post( - f"/docdb/stores/{store_id}/vectors/refresh", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Tree Files โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_tree_files( - self, - store_id: str, - *, - parent_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List tree-structured files in a store. - - Args: - store_id: The store ID. - parent_id: Filter by parent node ID. - """ - return self._get( - f"/docdb/stores/{store_id}/tree", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"parent_id": parent_id}, - ), - cast_to=object, - ) - - def create_tree_file( - self, - store_id: str, - *, - name: str, - parent_id: str | NotGiven = NOT_GIVEN, - content: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a tree node to a store. - - Args: - store_id: The store ID. - name: Node name. - parent_id: Parent node ID. Omit for root-level nodes. - content: Node content. - """ - return self._post( - f"/docdb/stores/{store_id}/tree", - body={"name": name, "parent_id": parent_id, "content": content}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncDocDBResource(AsyncAPIResource): - """Document database service backed by FerretDB + PostgreSQL (async). - - Manages document stores, files, vectors, and tree-structured documents - via the Hanzo Cloud API. - """ - - @cached_property - def with_raw_response(self) -> AsyncDocDBResourceWithRawResponse: - return AsyncDocDBResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncDocDBResourceWithStreamingResponse: - return AsyncDocDBResourceWithStreamingResponse(self) - - # โ”€โ”€ Stores โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_stores( - self, - *, - owner: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all document stores.""" - return await self._get( - "/docdb/stores", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - async def create_store( - self, - *, - name: str, - description: str | NotGiven = NOT_GIVEN, - owner: str | NotGiven = NOT_GIVEN, - tenant: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new document store.""" - return await self._post( - "/docdb/stores", - body={ - "name": name, - "description": description, - "owner": owner, - "tenant": tenant, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_store( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific document store.""" - return await self._get( - f"/docdb/stores/{store_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_store( - self, - store_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update store metadata.""" - return await self._post( - f"/docdb/stores/{store_id}", - body={"name": name, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_store( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a document store.""" - return await self._delete( - f"/docdb/stores/{store_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Files / Documents โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_files( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List files in a document store.""" - return await self._get( - f"/docdb/stores/{store_id}/files", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_file( - self, - store_id: str, - *, - name: str, - content: str, - type: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a file/document to a store.""" - return await self._post( - f"/docdb/stores/{store_id}/files", - body={ - "name": name, - "content": content, - "type": type, - "metadata": metadata, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_file( - self, - file_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific file.""" - return await self._get( - f"/docdb/files/{file_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_file( - self, - file_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - content: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update file metadata or content.""" - return await self._post( - f"/docdb/files/{file_id}", - body={"name": name, "content": content, "metadata": metadata}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_file( - self, - file_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a file.""" - return await self._delete( - f"/docdb/files/{file_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def upload_file( - self, - store_id: str, - *, - file: FileTypes, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Upload a file to a store via multipart form data.""" - body = deepcopy_minimal({"file": file, "metadata": metadata}) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - f"/docdb/stores/{store_id}/upload", - body=body, - files=files, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Vectors โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_vectors( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List vectors for a store.""" - return await self._get( - f"/docdb/stores/{store_id}/vectors", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_vector( - self, - store_id: str, - *, - file_id: str, - content: str, - embedding: List[float] | NotGiven = NOT_GIVEN, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a vector to a store.""" - return await self._post( - f"/docdb/stores/{store_id}/vectors", - body={ - "file_id": file_id, - "content": content, - "embedding": embedding, - "metadata": metadata, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_vector( - self, - vector_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a vector.""" - return await self._delete( - f"/docdb/vectors/{vector_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def refresh_vectors( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Re-embed all vectors in a store.""" - return await self._post( - f"/docdb/stores/{store_id}/vectors/refresh", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Tree Files โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_tree_files( - self, - store_id: str, - *, - parent_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List tree-structured files in a store.""" - return await self._get( - f"/docdb/stores/{store_id}/tree", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"parent_id": parent_id}, - ), - cast_to=object, - ) - - async def create_tree_file( - self, - store_id: str, - *, - name: str, - parent_id: str | NotGiven = NOT_GIVEN, - content: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a tree node to a store.""" - return await self._post( - f"/docdb/stores/{store_id}/tree", - body={"name": name, "parent_id": parent_id, "content": content}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class DocDBResourceWithRawResponse: - def __init__(self, docdb: DocDBResource) -> None: - self._docdb = docdb - # Stores - self.list_stores = to_raw_response_wrapper(docdb.list_stores) - self.create_store = to_raw_response_wrapper(docdb.create_store) - self.retrieve_store = to_raw_response_wrapper(docdb.retrieve_store) - self.update_store = to_raw_response_wrapper(docdb.update_store) - self.delete_store = to_raw_response_wrapper(docdb.delete_store) - # Files - self.list_files = to_raw_response_wrapper(docdb.list_files) - self.create_file = to_raw_response_wrapper(docdb.create_file) - self.retrieve_file = to_raw_response_wrapper(docdb.retrieve_file) - self.update_file = to_raw_response_wrapper(docdb.update_file) - self.delete_file = to_raw_response_wrapper(docdb.delete_file) - self.upload_file = to_raw_response_wrapper(docdb.upload_file) - # Vectors - self.list_vectors = to_raw_response_wrapper(docdb.list_vectors) - self.create_vector = to_raw_response_wrapper(docdb.create_vector) - self.delete_vector = to_raw_response_wrapper(docdb.delete_vector) - self.refresh_vectors = to_raw_response_wrapper(docdb.refresh_vectors) - # Tree - self.list_tree_files = to_raw_response_wrapper(docdb.list_tree_files) - self.create_tree_file = to_raw_response_wrapper(docdb.create_tree_file) - - -class AsyncDocDBResourceWithRawResponse: - def __init__(self, docdb: AsyncDocDBResource) -> None: - self._docdb = docdb - # Stores - self.list_stores = async_to_raw_response_wrapper(docdb.list_stores) - self.create_store = async_to_raw_response_wrapper(docdb.create_store) - self.retrieve_store = async_to_raw_response_wrapper(docdb.retrieve_store) - self.update_store = async_to_raw_response_wrapper(docdb.update_store) - self.delete_store = async_to_raw_response_wrapper(docdb.delete_store) - # Files - self.list_files = async_to_raw_response_wrapper(docdb.list_files) - self.create_file = async_to_raw_response_wrapper(docdb.create_file) - self.retrieve_file = async_to_raw_response_wrapper(docdb.retrieve_file) - self.update_file = async_to_raw_response_wrapper(docdb.update_file) - self.delete_file = async_to_raw_response_wrapper(docdb.delete_file) - self.upload_file = async_to_raw_response_wrapper(docdb.upload_file) - # Vectors - self.list_vectors = async_to_raw_response_wrapper(docdb.list_vectors) - self.create_vector = async_to_raw_response_wrapper(docdb.create_vector) - self.delete_vector = async_to_raw_response_wrapper(docdb.delete_vector) - self.refresh_vectors = async_to_raw_response_wrapper(docdb.refresh_vectors) - # Tree - self.list_tree_files = async_to_raw_response_wrapper(docdb.list_tree_files) - self.create_tree_file = async_to_raw_response_wrapper(docdb.create_tree_file) - - -class DocDBResourceWithStreamingResponse: - def __init__(self, docdb: DocDBResource) -> None: - self._docdb = docdb - # Stores - self.list_stores = to_streamed_response_wrapper(docdb.list_stores) - self.create_store = to_streamed_response_wrapper(docdb.create_store) - self.retrieve_store = to_streamed_response_wrapper(docdb.retrieve_store) - self.update_store = to_streamed_response_wrapper(docdb.update_store) - self.delete_store = to_streamed_response_wrapper(docdb.delete_store) - # Files - self.list_files = to_streamed_response_wrapper(docdb.list_files) - self.create_file = to_streamed_response_wrapper(docdb.create_file) - self.retrieve_file = to_streamed_response_wrapper(docdb.retrieve_file) - self.update_file = to_streamed_response_wrapper(docdb.update_file) - self.delete_file = to_streamed_response_wrapper(docdb.delete_file) - self.upload_file = to_streamed_response_wrapper(docdb.upload_file) - # Vectors - self.list_vectors = to_streamed_response_wrapper(docdb.list_vectors) - self.create_vector = to_streamed_response_wrapper(docdb.create_vector) - self.delete_vector = to_streamed_response_wrapper(docdb.delete_vector) - self.refresh_vectors = to_streamed_response_wrapper(docdb.refresh_vectors) - # Tree - self.list_tree_files = to_streamed_response_wrapper(docdb.list_tree_files) - self.create_tree_file = to_streamed_response_wrapper(docdb.create_tree_file) - - -class AsyncDocDBResourceWithStreamingResponse: - def __init__(self, docdb: AsyncDocDBResource) -> None: - self._docdb = docdb - # Stores - self.list_stores = async_to_streamed_response_wrapper(docdb.list_stores) - self.create_store = async_to_streamed_response_wrapper(docdb.create_store) - self.retrieve_store = async_to_streamed_response_wrapper(docdb.retrieve_store) - self.update_store = async_to_streamed_response_wrapper(docdb.update_store) - self.delete_store = async_to_streamed_response_wrapper(docdb.delete_store) - # Files - self.list_files = async_to_streamed_response_wrapper(docdb.list_files) - self.create_file = async_to_streamed_response_wrapper(docdb.create_file) - self.retrieve_file = async_to_streamed_response_wrapper(docdb.retrieve_file) - self.update_file = async_to_streamed_response_wrapper(docdb.update_file) - self.delete_file = async_to_streamed_response_wrapper(docdb.delete_file) - self.upload_file = async_to_streamed_response_wrapper(docdb.upload_file) - # Vectors - self.list_vectors = async_to_streamed_response_wrapper(docdb.list_vectors) - self.create_vector = async_to_streamed_response_wrapper(docdb.create_vector) - self.delete_vector = async_to_streamed_response_wrapper(docdb.delete_vector) - self.refresh_vectors = async_to_streamed_response_wrapper(docdb.refresh_vectors) - # Tree - self.list_tree_files = async_to_streamed_response_wrapper(docdb.list_tree_files) - self.create_tree_file = async_to_streamed_response_wrapper(docdb.create_tree_file) diff --git a/pkg/hanzoai/resources/edge.py b/pkg/hanzoai/resources/edge.py deleted file mode 100644 index 8c50da50e..000000000 --- a/pkg/hanzoai/resources/edge.py +++ /dev/null @@ -1,476 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class EdgeResource(SyncAPIResource): - """OpenZiti edge fabric operations.""" - - @cached_property - def with_raw_response(self) -> EdgeResourceWithRawResponse: - return EdgeResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> EdgeResourceWithStreamingResponse: - return EdgeResourceWithStreamingResponse(self) - - def list_controllers( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List edge controllers.""" - return self._get( - "/edge/controllers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def controller_status( - self, - controller_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get controller status.""" - return self._get( - f"/edge/controllers/{controller_id}/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_routers( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List edge routers.""" - return self._get( - "/edge/routers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def router_status( - self, - router_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get router status.""" - return self._get( - f"/edge/routers/{router_id}/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_services( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List edge services.""" - return self._get( - "/edge/services", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def describe_service( - self, - service_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Describe an edge service.""" - return self._get( - f"/edge/services/{service_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_policies( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List edge policies.""" - return self._get( - "/edge/policies", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def diff_policies( - self, - *, - policies: List[Dict[str, Any]], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Diff edge policies against current state.""" - return self._post( - "/edge/policies/diff", - body={"policies": policies}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def apply_policies( - self, - *, - policies: List[Dict[str, Any]], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Apply edge policies.""" - return self._post( - "/edge/policies/apply", - body={"policies": policies}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def inspect( - self, - *, - identity: str | NotGiven = NOT_GIVEN, - service: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Inspect identity or service.""" - return self._get( - "/edge/inspect", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"identity": identity, "service": service}, - ), - cast_to=object, - ) - - -class AsyncEdgeResource(AsyncAPIResource): - """OpenZiti edge fabric operations.""" - - @cached_property - def with_raw_response(self) -> AsyncEdgeResourceWithRawResponse: - return AsyncEdgeResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncEdgeResourceWithStreamingResponse: - return AsyncEdgeResourceWithStreamingResponse(self) - - async def list_controllers( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List edge controllers.""" - return await self._get( - "/edge/controllers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def controller_status( - self, - controller_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get controller status.""" - return await self._get( - f"/edge/controllers/{controller_id}/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_routers( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List edge routers.""" - return await self._get( - "/edge/routers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def router_status( - self, - router_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get router status.""" - return await self._get( - f"/edge/routers/{router_id}/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_services( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List edge services.""" - return await self._get( - "/edge/services", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def describe_service( - self, - service_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Describe an edge service.""" - return await self._get( - f"/edge/services/{service_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_policies( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List edge policies.""" - return await self._get( - "/edge/policies", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def diff_policies( - self, - *, - policies: List[Dict[str, Any]], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Diff edge policies against current state.""" - return await self._post( - "/edge/policies/diff", - body={"policies": policies}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def apply_policies( - self, - *, - policies: List[Dict[str, Any]], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Apply edge policies.""" - return await self._post( - "/edge/policies/apply", - body={"policies": policies}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def inspect( - self, - *, - identity: str | NotGiven = NOT_GIVEN, - service: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Inspect identity or service.""" - return await self._get( - "/edge/inspect", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"identity": identity, "service": service}, - ), - cast_to=object, - ) - - -class EdgeResourceWithRawResponse: - def __init__(self, edge: EdgeResource) -> None: - self._edge = edge - - -class AsyncEdgeResourceWithRawResponse: - def __init__(self, edge: AsyncEdgeResource) -> None: - self._edge = edge - - -class EdgeResourceWithStreamingResponse: - def __init__(self, edge: EdgeResource) -> None: - self._edge = edge - - -class AsyncEdgeResourceWithStreamingResponse: - def __init__(self, edge: AsyncEdgeResource) -> None: - self._edge = edge diff --git a/pkg/hanzoai/resources/embeddings.py b/pkg/hanzoai/resources/embeddings.py deleted file mode 100644 index a7e5d27a7..000000000 --- a/pkg/hanzoai/resources/embeddings.py +++ /dev/null @@ -1,199 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional - -import httpx - -from ..types import embedding_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - maybe_transform, - async_maybe_transform, -) -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["EmbeddingsResource", "AsyncEmbeddingsResource"] - - -class EmbeddingsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> EmbeddingsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return EmbeddingsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> EmbeddingsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return EmbeddingsResourceWithStreamingResponse(self) - - def create( - self, - *, - model: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Follows the exact same API spec as - `OpenAI's Embeddings API https://platform.openai.com/docs/api-reference/embeddings` - - ```bash - curl -X POST http://localhost:4000/v1/embeddings - -H "Content-Type: application/json" - -H "Authorization: Bearer sk-1234" - -d '{ - "model": "text-embedding-ada-002", - "input": "The quick brown fox jumps over the lazy dog" - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/embeddings", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"model": model}, embedding_create_params.EmbeddingCreateParams - ), - ), - cast_to=object, - ) - - -class AsyncEmbeddingsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncEmbeddingsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncEmbeddingsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncEmbeddingsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncEmbeddingsResourceWithStreamingResponse(self) - - async def create( - self, - *, - model: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Follows the exact same API spec as - `OpenAI's Embeddings API https://platform.openai.com/docs/api-reference/embeddings` - - ```bash - curl -X POST http://localhost:4000/v1/embeddings - -H "Content-Type: application/json" - -H "Authorization: Bearer sk-1234" - -d '{ - "model": "text-embedding-ada-002", - "input": "The quick brown fox jumps over the lazy dog" - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/embeddings", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"model": model}, embedding_create_params.EmbeddingCreateParams - ), - ), - cast_to=object, - ) - - -class EmbeddingsResourceWithRawResponse: - def __init__(self, embeddings: EmbeddingsResource) -> None: - self._embeddings = embeddings - - self.create = to_raw_response_wrapper( - embeddings.create, - ) - - -class AsyncEmbeddingsResourceWithRawResponse: - def __init__(self, embeddings: AsyncEmbeddingsResource) -> None: - self._embeddings = embeddings - - self.create = async_to_raw_response_wrapper( - embeddings.create, - ) - - -class EmbeddingsResourceWithStreamingResponse: - def __init__(self, embeddings: EmbeddingsResource) -> None: - self._embeddings = embeddings - - self.create = to_streamed_response_wrapper( - embeddings.create, - ) - - -class AsyncEmbeddingsResourceWithStreamingResponse: - def __init__(self, embeddings: AsyncEmbeddingsResource) -> None: - self._embeddings = embeddings - - self.create = async_to_streamed_response_wrapper( - embeddings.create, - ) diff --git a/pkg/hanzoai/resources/engines/__init__.py b/pkg/hanzoai/resources/engines/__init__.py deleted file mode 100644 index 6960d2ed9..000000000 --- a/pkg/hanzoai/resources/engines/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Hanzo AI SDK - -from .chat import ( - ChatResource, - AsyncChatResource, - ChatResourceWithRawResponse, - AsyncChatResourceWithRawResponse, - ChatResourceWithStreamingResponse, - AsyncChatResourceWithStreamingResponse, -) -from .engines import ( - EnginesResource, - AsyncEnginesResource, - EnginesResourceWithRawResponse, - AsyncEnginesResourceWithRawResponse, - EnginesResourceWithStreamingResponse, - AsyncEnginesResourceWithStreamingResponse, -) - -__all__ = [ - "ChatResource", - "AsyncChatResource", - "ChatResourceWithRawResponse", - "AsyncChatResourceWithRawResponse", - "ChatResourceWithStreamingResponse", - "AsyncChatResourceWithStreamingResponse", - "EnginesResource", - "AsyncEnginesResource", - "EnginesResourceWithRawResponse", - "AsyncEnginesResourceWithRawResponse", - "EnginesResourceWithStreamingResponse", - "AsyncEnginesResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/engines/chat.py b/pkg/hanzoai/resources/engines/chat.py deleted file mode 100644 index 0b847ff81..000000000 --- a/pkg/hanzoai/resources/engines/chat.py +++ /dev/null @@ -1,204 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -import httpx - -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options - -__all__ = ["ChatResource", "AsyncChatResource"] - - -class ChatResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ChatResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return ChatResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ChatResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return ChatResourceWithStreamingResponse(self) - - def complete( - self, - model: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Follows the exact same API spec as - `OpenAI's Chat API https://platform.openai.com/docs/api-reference/chat` - - ```bash - curl -X POST http://localhost:4000/v1/chat/completions - -H "Content-Type: application/json" - -H "Authorization: Bearer sk-1234" - -d '{ - "model": "gpt-4o", - "messages": [ - { - "role": "user", - "content": "Hello!" - } - ] - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not model: - raise ValueError( - f"Expected a non-empty value for `model` but received {model!r}" - ) - return self._post( - f"/engines/{model}/chat/completions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncChatResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncChatResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncChatResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncChatResourceWithStreamingResponse(self) - - async def complete( - self, - model: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Follows the exact same API spec as - `OpenAI's Chat API https://platform.openai.com/docs/api-reference/chat` - - ```bash - curl -X POST http://localhost:4000/v1/chat/completions - -H "Content-Type: application/json" - -H "Authorization: Bearer sk-1234" - -d '{ - "model": "gpt-4o", - "messages": [ - { - "role": "user", - "content": "Hello!" - } - ] - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not model: - raise ValueError( - f"Expected a non-empty value for `model` but received {model!r}" - ) - return await self._post( - f"/engines/{model}/chat/completions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class ChatResourceWithRawResponse: - def __init__(self, chat: ChatResource) -> None: - self._chat = chat - - self.complete = to_raw_response_wrapper( - chat.complete, - ) - - -class AsyncChatResourceWithRawResponse: - def __init__(self, chat: AsyncChatResource) -> None: - self._chat = chat - - self.complete = async_to_raw_response_wrapper( - chat.complete, - ) - - -class ChatResourceWithStreamingResponse: - def __init__(self, chat: ChatResource) -> None: - self._chat = chat - - self.complete = to_streamed_response_wrapper( - chat.complete, - ) - - -class AsyncChatResourceWithStreamingResponse: - def __init__(self, chat: AsyncChatResource) -> None: - self._chat = chat - - self.complete = async_to_streamed_response_wrapper( - chat.complete, - ) diff --git a/pkg/hanzoai/resources/files/__init__.py b/pkg/hanzoai/resources/files/__init__.py deleted file mode 100644 index 4f14cd2cb..000000000 --- a/pkg/hanzoai/resources/files/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Hanzo AI SDK - -from .files import ( - FilesResource, - AsyncFilesResource, - FilesResourceWithRawResponse, - AsyncFilesResourceWithRawResponse, - FilesResourceWithStreamingResponse, - AsyncFilesResourceWithStreamingResponse, -) -from .content import ( - ContentResource, - AsyncContentResource, - ContentResourceWithRawResponse, - AsyncContentResourceWithRawResponse, - ContentResourceWithStreamingResponse, - AsyncContentResourceWithStreamingResponse, -) - -__all__ = [ - "ContentResource", - "AsyncContentResource", - "ContentResourceWithRawResponse", - "AsyncContentResourceWithRawResponse", - "ContentResourceWithStreamingResponse", - "AsyncContentResourceWithStreamingResponse", - "FilesResource", - "AsyncFilesResource", - "FilesResourceWithRawResponse", - "AsyncFilesResourceWithRawResponse", - "FilesResourceWithStreamingResponse", - "AsyncFilesResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/files/files.py b/pkg/hanzoai/resources/files/files.py deleted file mode 100644 index 6bcf86cd2..000000000 --- a/pkg/hanzoai/resources/files/files.py +++ /dev/null @@ -1,635 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Mapping, Optional, cast - -import httpx - -from ...types import file_list_params, file_create_params -from .content import ( - ContentResource, - AsyncContentResource, - ContentResourceWithRawResponse, - AsyncContentResourceWithRawResponse, - ContentResourceWithStreamingResponse, - AsyncContentResourceWithStreamingResponse, -) -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from ..._utils import ( - extract_files, - maybe_transform, - deepcopy_minimal, - async_maybe_transform, -) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options - -__all__ = ["FilesResource", "AsyncFilesResource"] - - -class FilesResource(SyncAPIResource): - @cached_property - def content(self) -> ContentResource: - return ContentResource(self._client) - - @cached_property - def with_raw_response(self) -> FilesResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return FilesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> FilesResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return FilesResourceWithStreamingResponse(self) - - def create( - self, - provider: str, - *, - file: FileTypes, - purpose: str, - custom_llm_provider: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Upload a file that can be used across - Assistants API, Batch API This is the - equivalent of POST https://api.openai.com/v1/files - - Supports Identical Params as: - https://platform.openai.com/docs/api-reference/files/create - - Example Curl - - ``` - curl http://localhost:4000/v1/files -H "Authorization: Bearer sk-1234" -F purpose="batch" -F file="@mydata.jsonl" - - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) - body = deepcopy_minimal( - { - "file": file, - "purpose": purpose, - "custom_llm_provider": custom_llm_provider, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - f"/{provider}/v1/files", - body=maybe_transform(body, file_create_params.FileCreateParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retrieve( - self, - file_id: str, - *, - provider: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Returns information about a specific file. - - that can be used across - Assistants - API, Batch API This is the equivalent of GET - https://api.openai.com/v1/files/{file_id} - - Supports Identical Params as: - https://platform.openai.com/docs/api-reference/files/retrieve - - Example Curl - - ``` - curl http://localhost:4000/v1/files/file-abc123 -H "Authorization: Bearer sk-1234" - - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) - if not file_id: - raise ValueError( - f"Expected a non-empty value for `file_id` but received {file_id!r}" - ) - return self._get( - f"/{provider}/v1/files/{file_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list( - self, - provider: str, - *, - purpose: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Returns information about a specific file. - - that can be used across - Assistants - API, Batch API This is the equivalent of GET https://api.openai.com/v1/files/ - - Supports Identical Params as: - https://platform.openai.com/docs/api-reference/files/list - - Example Curl - - ``` - curl http://localhost:4000/v1/files -H "Authorization: Bearer sk-1234" - - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) - return self._get( - f"/{provider}/v1/files", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"purpose": purpose}, file_list_params.FileListParams - ), - ), - cast_to=object, - ) - - def delete( - self, - file_id: str, - *, - provider: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Deletes a specified file. - - that can be used across - Assistants API, Batch API - This is the equivalent of DELETE https://api.openai.com/v1/files/{file_id} - - Supports Identical Params as: - https://platform.openai.com/docs/api-reference/files/delete - - Example Curl - - ``` - curl http://localhost:4000/v1/files/file-abc123 -X DELETE -H "Authorization: Bearer $OPENAI_API_KEY" - - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) - if not file_id: - raise ValueError( - f"Expected a non-empty value for `file_id` but received {file_id!r}" - ) - return self._delete( - f"/{provider}/v1/files/{file_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncFilesResource(AsyncAPIResource): - @cached_property - def content(self) -> AsyncContentResource: - return AsyncContentResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncFilesResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncFilesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncFilesResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncFilesResourceWithStreamingResponse(self) - - async def create( - self, - provider: str, - *, - file: FileTypes, - purpose: str, - custom_llm_provider: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Upload a file that can be used across - Assistants API, Batch API This is the - equivalent of POST https://api.openai.com/v1/files - - Supports Identical Params as: - https://platform.openai.com/docs/api-reference/files/create - - Example Curl - - ``` - curl http://localhost:4000/v1/files -H "Authorization: Bearer sk-1234" -F purpose="batch" -F file="@mydata.jsonl" - - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) - body = deepcopy_minimal( - { - "file": file, - "purpose": purpose, - "custom_llm_provider": custom_llm_provider, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - f"/{provider}/v1/files", - body=await async_maybe_transform(body, file_create_params.FileCreateParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve( - self, - file_id: str, - *, - provider: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Returns information about a specific file. - - that can be used across - Assistants - API, Batch API This is the equivalent of GET - https://api.openai.com/v1/files/{file_id} - - Supports Identical Params as: - https://platform.openai.com/docs/api-reference/files/retrieve - - Example Curl - - ``` - curl http://localhost:4000/v1/files/file-abc123 -H "Authorization: Bearer sk-1234" - - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) - if not file_id: - raise ValueError( - f"Expected a non-empty value for `file_id` but received {file_id!r}" - ) - return await self._get( - f"/{provider}/v1/files/{file_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list( - self, - provider: str, - *, - purpose: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Returns information about a specific file. - - that can be used across - Assistants - API, Batch API This is the equivalent of GET https://api.openai.com/v1/files/ - - Supports Identical Params as: - https://platform.openai.com/docs/api-reference/files/list - - Example Curl - - ``` - curl http://localhost:4000/v1/files -H "Authorization: Bearer sk-1234" - - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) - return await self._get( - f"/{provider}/v1/files", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"purpose": purpose}, file_list_params.FileListParams - ), - ), - cast_to=object, - ) - - async def delete( - self, - file_id: str, - *, - provider: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Deletes a specified file. - - that can be used across - Assistants API, Batch API - This is the equivalent of DELETE https://api.openai.com/v1/files/{file_id} - - Supports Identical Params as: - https://platform.openai.com/docs/api-reference/files/delete - - Example Curl - - ``` - curl http://localhost:4000/v1/files/file-abc123 -X DELETE -H "Authorization: Bearer $OPENAI_API_KEY" - - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) - if not file_id: - raise ValueError( - f"Expected a non-empty value for `file_id` but received {file_id!r}" - ) - return await self._delete( - f"/{provider}/v1/files/{file_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class FilesResourceWithRawResponse: - def __init__(self, files: FilesResource) -> None: - self._files = files - - self.create = to_raw_response_wrapper( - files.create, - ) - self.retrieve = to_raw_response_wrapper( - files.retrieve, - ) - self.list = to_raw_response_wrapper( - files.list, - ) - self.delete = to_raw_response_wrapper( - files.delete, - ) - - @cached_property - def content(self) -> ContentResourceWithRawResponse: - return ContentResourceWithRawResponse(self._files.content) - - -class AsyncFilesResourceWithRawResponse: - def __init__(self, files: AsyncFilesResource) -> None: - self._files = files - - self.create = async_to_raw_response_wrapper( - files.create, - ) - self.retrieve = async_to_raw_response_wrapper( - files.retrieve, - ) - self.list = async_to_raw_response_wrapper( - files.list, - ) - self.delete = async_to_raw_response_wrapper( - files.delete, - ) - - @cached_property - def content(self) -> AsyncContentResourceWithRawResponse: - return AsyncContentResourceWithRawResponse(self._files.content) - - -class FilesResourceWithStreamingResponse: - def __init__(self, files: FilesResource) -> None: - self._files = files - - self.create = to_streamed_response_wrapper( - files.create, - ) - self.retrieve = to_streamed_response_wrapper( - files.retrieve, - ) - self.list = to_streamed_response_wrapper( - files.list, - ) - self.delete = to_streamed_response_wrapper( - files.delete, - ) - - @cached_property - def content(self) -> ContentResourceWithStreamingResponse: - return ContentResourceWithStreamingResponse(self._files.content) - - -class AsyncFilesResourceWithStreamingResponse: - def __init__(self, files: AsyncFilesResource) -> None: - self._files = files - - self.create = async_to_streamed_response_wrapper( - files.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - files.retrieve, - ) - self.list = async_to_streamed_response_wrapper( - files.list, - ) - self.delete = async_to_streamed_response_wrapper( - files.delete, - ) - - @cached_property - def content(self) -> AsyncContentResourceWithStreamingResponse: - return AsyncContentResourceWithStreamingResponse(self._files.content) diff --git a/pkg/hanzoai/resources/fine_tuning/__init__.py b/pkg/hanzoai/resources/fine_tuning/__init__.py deleted file mode 100644 index c2084a8c4..000000000 --- a/pkg/hanzoai/resources/fine_tuning/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Hanzo AI SDK - -from .jobs import ( - JobsResource, - AsyncJobsResource, - JobsResourceWithRawResponse, - AsyncJobsResourceWithRawResponse, - JobsResourceWithStreamingResponse, - AsyncJobsResourceWithStreamingResponse, -) -from .fine_tuning import ( - FineTuningResource, - AsyncFineTuningResource, - FineTuningResourceWithRawResponse, - AsyncFineTuningResourceWithRawResponse, - FineTuningResourceWithStreamingResponse, - AsyncFineTuningResourceWithStreamingResponse, -) - -__all__ = [ - "JobsResource", - "AsyncJobsResource", - "JobsResourceWithRawResponse", - "AsyncJobsResourceWithRawResponse", - "JobsResourceWithStreamingResponse", - "AsyncJobsResourceWithStreamingResponse", - "FineTuningResource", - "AsyncFineTuningResource", - "FineTuningResourceWithRawResponse", - "AsyncFineTuningResourceWithRawResponse", - "FineTuningResourceWithStreamingResponse", - "AsyncFineTuningResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/fine_tuning/jobs/__init__.py b/pkg/hanzoai/resources/fine_tuning/jobs/__init__.py deleted file mode 100644 index 3fc24f2fb..000000000 --- a/pkg/hanzoai/resources/fine_tuning/jobs/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Hanzo AI SDK - -from .jobs import ( - JobsResource, - AsyncJobsResource, - JobsResourceWithRawResponse, - AsyncJobsResourceWithRawResponse, - JobsResourceWithStreamingResponse, - AsyncJobsResourceWithStreamingResponse, -) -from .cancel import ( - CancelResource, - AsyncCancelResource, - CancelResourceWithRawResponse, - AsyncCancelResourceWithRawResponse, - CancelResourceWithStreamingResponse, - AsyncCancelResourceWithStreamingResponse, -) - -__all__ = [ - "CancelResource", - "AsyncCancelResource", - "CancelResourceWithRawResponse", - "AsyncCancelResourceWithRawResponse", - "CancelResourceWithStreamingResponse", - "AsyncCancelResourceWithStreamingResponse", - "JobsResource", - "AsyncJobsResource", - "JobsResourceWithRawResponse", - "AsyncJobsResourceWithRawResponse", - "JobsResourceWithStreamingResponse", - "AsyncJobsResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/fine_tuning/jobs/cancel.py b/pkg/hanzoai/resources/fine_tuning/jobs/cancel.py deleted file mode 100644 index 0ebe99141..000000000 --- a/pkg/hanzoai/resources/fine_tuning/jobs/cancel.py +++ /dev/null @@ -1,188 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -import httpx - -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...._base_client import make_request_options - -__all__ = ["CancelResource", "AsyncCancelResource"] - - -class CancelResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> CancelResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return CancelResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> CancelResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return CancelResourceWithStreamingResponse(self) - - def create( - self, - fine_tuning_job_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Cancel a fine-tuning job. - - This is the equivalent of POST - https://api.openai.com/v1/fine_tuning/jobs/{fine_tuning_job_id}/cancel - - Supported Query Params: - - - `custom_llm_provider`: Name of the Hanzo provider - - `fine_tuning_job_id`: The ID of the fine-tuning job to cancel. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not fine_tuning_job_id: - raise ValueError( - f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}" - ) - return self._post( - f"/v1/fine_tuning/jobs/{fine_tuning_job_id}/cancel", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncCancelResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncCancelResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncCancelResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncCancelResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncCancelResourceWithStreamingResponse(self) - - async def create( - self, - fine_tuning_job_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Cancel a fine-tuning job. - - This is the equivalent of POST - https://api.openai.com/v1/fine_tuning/jobs/{fine_tuning_job_id}/cancel - - Supported Query Params: - - - `custom_llm_provider`: Name of the Hanzo provider - - `fine_tuning_job_id`: The ID of the fine-tuning job to cancel. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not fine_tuning_job_id: - raise ValueError( - f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}" - ) - return await self._post( - f"/v1/fine_tuning/jobs/{fine_tuning_job_id}/cancel", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class CancelResourceWithRawResponse: - def __init__(self, cancel: CancelResource) -> None: - self._cancel = cancel - - self.create = to_raw_response_wrapper( - cancel.create, - ) - - -class AsyncCancelResourceWithRawResponse: - def __init__(self, cancel: AsyncCancelResource) -> None: - self._cancel = cancel - - self.create = async_to_raw_response_wrapper( - cancel.create, - ) - - -class CancelResourceWithStreamingResponse: - def __init__(self, cancel: CancelResource) -> None: - self._cancel = cancel - - self.create = to_streamed_response_wrapper( - cancel.create, - ) - - -class AsyncCancelResourceWithStreamingResponse: - def __init__(self, cancel: AsyncCancelResource) -> None: - self._cancel = cancel - - self.create = async_to_streamed_response_wrapper( - cancel.create, - ) diff --git a/pkg/hanzoai/resources/fine_tuning/jobs/jobs.py b/pkg/hanzoai/resources/fine_tuning/jobs/jobs.py deleted file mode 100644 index 5f0d44b60..000000000 --- a/pkg/hanzoai/resources/fine_tuning/jobs/jobs.py +++ /dev/null @@ -1,514 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal - -import httpx - -from .cancel import ( - CancelResource, - AsyncCancelResource, - CancelResourceWithRawResponse, - AsyncCancelResourceWithRawResponse, - CancelResourceWithStreamingResponse, - AsyncCancelResourceWithStreamingResponse, -) -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( - maybe_transform, - async_maybe_transform, -) -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...._base_client import make_request_options -from ....types.fine_tuning import ( - job_list_params, - job_create_params, - job_retrieve_params, -) - -__all__ = ["JobsResource", "AsyncJobsResource"] - - -class JobsResource(SyncAPIResource): - @cached_property - def cancel(self) -> CancelResource: - return CancelResource(self._client) - - @cached_property - def with_raw_response(self) -> JobsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return JobsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> JobsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return JobsResourceWithStreamingResponse(self) - - def create( - self, - *, - custom_llm_provider: Literal["openai", "azure", "vertex_ai"], - model: str, - training_file: str, - hyperparameters: ( - Optional[job_create_params.Hyperparameters] | NotGiven - ) = NOT_GIVEN, - integrations: Optional[List[str]] | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - suffix: Optional[str] | NotGiven = NOT_GIVEN, - validation_file: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Creates a fine-tuning job which begins the process of creating a new model from - a given dataset. This is the equivalent of POST - https://api.openai.com/v1/fine_tuning/jobs - - Supports Identical Params as: - https://platform.openai.com/docs/api-reference/fine-tuning/create - - Example Curl: - - ``` - curl http://localhost:4000/v1/fine_tuning/jobs -H "Content-Type: application/json" -H "Authorization: Bearer sk-1234" -d '{ - "model": "gpt-3.5-turbo", - "training_file": "file-abc123", - "hyperparameters": { - "n_epochs": 4 - } - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/v1/fine_tuning/jobs", - body=maybe_transform( - { - "custom_llm_provider": custom_llm_provider, - "model": model, - "training_file": training_file, - "hyperparameters": hyperparameters, - "integrations": integrations, - "seed": seed, - "suffix": suffix, - "validation_file": validation_file, - }, - job_create_params.JobCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retrieve( - self, - fine_tuning_job_id: str, - *, - custom_llm_provider: Literal["openai", "azure"], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Retrieves a fine-tuning job. - - This is the equivalent of GET - https://api.openai.com/v1/fine_tuning/jobs/{fine_tuning_job_id} - - Supported Query Params: - - - `custom_llm_provider`: Name of the Hanzo provider - - `fine_tuning_job_id`: The ID of the fine-tuning job to retrieve. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not fine_tuning_job_id: - raise ValueError( - f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}" - ) - return self._get( - f"/v1/fine_tuning/jobs/{fine_tuning_job_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"custom_llm_provider": custom_llm_provider}, - job_retrieve_params.JobRetrieveParams, - ), - ), - cast_to=object, - ) - - def list( - self, - *, - custom_llm_provider: Literal["openai", "azure"], - after: Optional[str] | NotGiven = NOT_GIVEN, - limit: Optional[int] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Lists fine-tuning jobs for the organization. - - This is the equivalent of GET - https://api.openai.com/v1/fine_tuning/jobs - - Supported Query Params: - - - `custom_llm_provider`: Name of the Hanzo provider - - `after`: Identifier for the last job from the previous pagination request. - - `limit`: Number of fine-tuning jobs to retrieve (default is 20). - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/v1/fine_tuning/jobs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "custom_llm_provider": custom_llm_provider, - "after": after, - "limit": limit, - }, - job_list_params.JobListParams, - ), - ), - cast_to=object, - ) - - -class AsyncJobsResource(AsyncAPIResource): - @cached_property - def cancel(self) -> AsyncCancelResource: - return AsyncCancelResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncJobsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncJobsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncJobsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncJobsResourceWithStreamingResponse(self) - - async def create( - self, - *, - custom_llm_provider: Literal["openai", "azure", "vertex_ai"], - model: str, - training_file: str, - hyperparameters: ( - Optional[job_create_params.Hyperparameters] | NotGiven - ) = NOT_GIVEN, - integrations: Optional[List[str]] | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - suffix: Optional[str] | NotGiven = NOT_GIVEN, - validation_file: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Creates a fine-tuning job which begins the process of creating a new model from - a given dataset. This is the equivalent of POST - https://api.openai.com/v1/fine_tuning/jobs - - Supports Identical Params as: - https://platform.openai.com/docs/api-reference/fine-tuning/create - - Example Curl: - - ``` - curl http://localhost:4000/v1/fine_tuning/jobs -H "Content-Type: application/json" -H "Authorization: Bearer sk-1234" -d '{ - "model": "gpt-3.5-turbo", - "training_file": "file-abc123", - "hyperparameters": { - "n_epochs": 4 - } - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/v1/fine_tuning/jobs", - body=await async_maybe_transform( - { - "custom_llm_provider": custom_llm_provider, - "model": model, - "training_file": training_file, - "hyperparameters": hyperparameters, - "integrations": integrations, - "seed": seed, - "suffix": suffix, - "validation_file": validation_file, - }, - job_create_params.JobCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve( - self, - fine_tuning_job_id: str, - *, - custom_llm_provider: Literal["openai", "azure"], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Retrieves a fine-tuning job. - - This is the equivalent of GET - https://api.openai.com/v1/fine_tuning/jobs/{fine_tuning_job_id} - - Supported Query Params: - - - `custom_llm_provider`: Name of the Hanzo provider - - `fine_tuning_job_id`: The ID of the fine-tuning job to retrieve. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not fine_tuning_job_id: - raise ValueError( - f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}" - ) - return await self._get( - f"/v1/fine_tuning/jobs/{fine_tuning_job_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"custom_llm_provider": custom_llm_provider}, - job_retrieve_params.JobRetrieveParams, - ), - ), - cast_to=object, - ) - - async def list( - self, - *, - custom_llm_provider: Literal["openai", "azure"], - after: Optional[str] | NotGiven = NOT_GIVEN, - limit: Optional[int] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Lists fine-tuning jobs for the organization. - - This is the equivalent of GET - https://api.openai.com/v1/fine_tuning/jobs - - Supported Query Params: - - - `custom_llm_provider`: Name of the Hanzo provider - - `after`: Identifier for the last job from the previous pagination request. - - `limit`: Number of fine-tuning jobs to retrieve (default is 20). - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/v1/fine_tuning/jobs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "custom_llm_provider": custom_llm_provider, - "after": after, - "limit": limit, - }, - job_list_params.JobListParams, - ), - ), - cast_to=object, - ) - - -class JobsResourceWithRawResponse: - def __init__(self, jobs: JobsResource) -> None: - self._jobs = jobs - - self.create = to_raw_response_wrapper( - jobs.create, - ) - self.retrieve = to_raw_response_wrapper( - jobs.retrieve, - ) - self.list = to_raw_response_wrapper( - jobs.list, - ) - - @cached_property - def cancel(self) -> CancelResourceWithRawResponse: - return CancelResourceWithRawResponse(self._jobs.cancel) - - -class AsyncJobsResourceWithRawResponse: - def __init__(self, jobs: AsyncJobsResource) -> None: - self._jobs = jobs - - self.create = async_to_raw_response_wrapper( - jobs.create, - ) - self.retrieve = async_to_raw_response_wrapper( - jobs.retrieve, - ) - self.list = async_to_raw_response_wrapper( - jobs.list, - ) - - @cached_property - def cancel(self) -> AsyncCancelResourceWithRawResponse: - return AsyncCancelResourceWithRawResponse(self._jobs.cancel) - - -class JobsResourceWithStreamingResponse: - def __init__(self, jobs: JobsResource) -> None: - self._jobs = jobs - - self.create = to_streamed_response_wrapper( - jobs.create, - ) - self.retrieve = to_streamed_response_wrapper( - jobs.retrieve, - ) - self.list = to_streamed_response_wrapper( - jobs.list, - ) - - @cached_property - def cancel(self) -> CancelResourceWithStreamingResponse: - return CancelResourceWithStreamingResponse(self._jobs.cancel) - - -class AsyncJobsResourceWithStreamingResponse: - def __init__(self, jobs: AsyncJobsResource) -> None: - self._jobs = jobs - - self.create = async_to_streamed_response_wrapper( - jobs.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - jobs.retrieve, - ) - self.list = async_to_streamed_response_wrapper( - jobs.list, - ) - - @cached_property - def cancel(self) -> AsyncCancelResourceWithStreamingResponse: - return AsyncCancelResourceWithStreamingResponse(self._jobs.cancel) diff --git a/pkg/hanzoai/resources/gateway.py b/pkg/hanzoai/resources/gateway.py deleted file mode 100644 index 649c92971..000000000 --- a/pkg/hanzoai/resources/gateway.py +++ /dev/null @@ -1,542 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class GatewayResource(SyncAPIResource): - """API gateway management service.""" - - @cached_property - def with_raw_response(self) -> GatewayResourceWithRawResponse: - return GatewayResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> GatewayResourceWithStreamingResponse: - return GatewayResourceWithStreamingResponse(self) - - # Status - def status( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get gateway status.""" - return self._get( - "/gateway/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Rate limit management - def list_rate_limits( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all rate limit rules.""" - return self._get( - "/gateway/rate-limits", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_rate_limit( - self, - *, - name: str, - scope: str, - requests_per_minute: int, - burst_size: int, - scope_id: str | NotGiven = NOT_GIVEN, - enabled: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a rate limit rule.""" - return self._post( - "/gateway/rate-limits", - body={ - "name": name, - "scope": scope, - "scopeId": scope_id, - "requestsPerMinute": requests_per_minute, - "burstSize": burst_size, - "enabled": enabled, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_rate_limit( - self, - rule_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - requests_per_minute: int | NotGiven = NOT_GIVEN, - burst_size: int | NotGiven = NOT_GIVEN, - enabled: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a rate limit rule.""" - return self._put( - f"/gateway/rate-limits/{rule_id}", - body={ - "name": name, - "requestsPerMinute": requests_per_minute, - "burstSize": burst_size, - "enabled": enabled, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_rate_limit( - self, - rule_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a rate limit rule.""" - return self._delete( - f"/gateway/rate-limits/{rule_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Route management - def list_routes( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all gateway routes.""" - return self._get( - "/gateway/routes", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_route( - self, - *, - name: str, - host: str, - backend: str, - path_prefix: str | NotGiven = NOT_GIVEN, - middlewares: List[str] | NotGiven = NOT_GIVEN, - priority: int | NotGiven = NOT_GIVEN, - enabled: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a gateway route.""" - return self._post( - "/gateway/routes", - body={ - "name": name, - "host": host, - "backend": backend, - "pathPrefix": path_prefix, - "middlewares": middlewares, - "priority": priority, - "enabled": enabled, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_route( - self, - rule_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - host: str | NotGiven = NOT_GIVEN, - backend: str | NotGiven = NOT_GIVEN, - path_prefix: str | NotGiven = NOT_GIVEN, - middlewares: List[str] | NotGiven = NOT_GIVEN, - priority: int | NotGiven = NOT_GIVEN, - enabled: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a gateway route.""" - return self._put( - f"/gateway/routes/{rule_id}", - body={ - "name": name, - "host": host, - "backend": backend, - "pathPrefix": path_prefix, - "middlewares": middlewares, - "priority": priority, - "enabled": enabled, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_route( - self, - rule_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a gateway route.""" - return self._delete( - f"/gateway/routes/{rule_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncGatewayResource(AsyncAPIResource): - """API gateway management service.""" - - @cached_property - def with_raw_response(self) -> AsyncGatewayResourceWithRawResponse: - return AsyncGatewayResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncGatewayResourceWithStreamingResponse: - return AsyncGatewayResourceWithStreamingResponse(self) - - # Status - async def status( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get gateway status.""" - return await self._get( - "/gateway/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Rate limit management - async def list_rate_limits( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all rate limit rules.""" - return await self._get( - "/gateway/rate-limits", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_rate_limit( - self, - *, - name: str, - scope: str, - requests_per_minute: int, - burst_size: int, - scope_id: str | NotGiven = NOT_GIVEN, - enabled: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a rate limit rule.""" - return await self._post( - "/gateway/rate-limits", - body={ - "name": name, - "scope": scope, - "scopeId": scope_id, - "requestsPerMinute": requests_per_minute, - "burstSize": burst_size, - "enabled": enabled, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_rate_limit( - self, - rule_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - requests_per_minute: int | NotGiven = NOT_GIVEN, - burst_size: int | NotGiven = NOT_GIVEN, - enabled: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a rate limit rule.""" - return await self._put( - f"/gateway/rate-limits/{rule_id}", - body={ - "name": name, - "requestsPerMinute": requests_per_minute, - "burstSize": burst_size, - "enabled": enabled, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_rate_limit( - self, - rule_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a rate limit rule.""" - return await self._delete( - f"/gateway/rate-limits/{rule_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Route management - async def list_routes( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all gateway routes.""" - return await self._get( - "/gateway/routes", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_route( - self, - *, - name: str, - host: str, - backend: str, - path_prefix: str | NotGiven = NOT_GIVEN, - middlewares: List[str] | NotGiven = NOT_GIVEN, - priority: int | NotGiven = NOT_GIVEN, - enabled: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a gateway route.""" - return await self._post( - "/gateway/routes", - body={ - "name": name, - "host": host, - "backend": backend, - "pathPrefix": path_prefix, - "middlewares": middlewares, - "priority": priority, - "enabled": enabled, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_route( - self, - rule_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - host: str | NotGiven = NOT_GIVEN, - backend: str | NotGiven = NOT_GIVEN, - path_prefix: str | NotGiven = NOT_GIVEN, - middlewares: List[str] | NotGiven = NOT_GIVEN, - priority: int | NotGiven = NOT_GIVEN, - enabled: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a gateway route.""" - return await self._put( - f"/gateway/routes/{rule_id}", - body={ - "name": name, - "host": host, - "backend": backend, - "pathPrefix": path_prefix, - "middlewares": middlewares, - "priority": priority, - "enabled": enabled, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_route( - self, - rule_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a gateway route.""" - return await self._delete( - f"/gateway/routes/{rule_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class GatewayResourceWithRawResponse: - def __init__(self, gateway: GatewayResource) -> None: - self._gateway = gateway - - -class AsyncGatewayResourceWithRawResponse: - def __init__(self, gateway: AsyncGatewayResource) -> None: - self._gateway = gateway - - -class GatewayResourceWithStreamingResponse: - def __init__(self, gateway: GatewayResource) -> None: - self._gateway = gateway - - -class AsyncGatewayResourceWithStreamingResponse: - def __init__(self, gateway: AsyncGatewayResource) -> None: - self._gateway = gateway diff --git a/pkg/hanzoai/resources/global_/__init__.py b/pkg/hanzoai/resources/global_/__init__.py deleted file mode 100644 index e0c1b211a..000000000 --- a/pkg/hanzoai/resources/global_/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Hanzo AI SDK - -from .spend import ( - SpendResource, - AsyncSpendResource, - SpendResourceWithRawResponse, - AsyncSpendResourceWithRawResponse, - SpendResourceWithStreamingResponse, - AsyncSpendResourceWithStreamingResponse, -) -from .global_ import ( - GlobalResource, - AsyncGlobalResource, - GlobalResourceWithRawResponse, - AsyncGlobalResourceWithRawResponse, - GlobalResourceWithStreamingResponse, - AsyncGlobalResourceWithStreamingResponse, -) - -__all__ = [ - "SpendResource", - "AsyncSpendResource", - "SpendResourceWithRawResponse", - "AsyncSpendResourceWithRawResponse", - "SpendResourceWithStreamingResponse", - "AsyncSpendResourceWithStreamingResponse", - "GlobalResource", - "AsyncGlobalResource", - "GlobalResourceWithRawResponse", - "AsyncGlobalResourceWithRawResponse", - "GlobalResourceWithStreamingResponse", - "AsyncGlobalResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/global_/spend.py b/pkg/hanzoai/resources/global_/spend.py deleted file mode 100644 index 3e3e0d7ab..000000000 --- a/pkg/hanzoai/resources/global_/spend.py +++ /dev/null @@ -1,468 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal - -import httpx - -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.global_ import spend_list_tags_params, spend_retrieve_report_params -from ...types.global_.spend_list_tags_response import SpendListTagsResponse -from ...types.global_.spend_retrieve_report_response import SpendRetrieveReportResponse - -__all__ = ["SpendResource", "AsyncSpendResource"] - - -class SpendResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> SpendResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return SpendResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> SpendResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return SpendResourceWithStreamingResponse(self) - - def list_tags( - self, - *, - end_date: Optional[str] | NotGiven = NOT_GIVEN, - start_date: Optional[str] | NotGiven = NOT_GIVEN, - tags: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SpendListTagsResponse: - """Hanzo Enterprise - View Spend Per Request Tag. - - Used by Hanzo UI - - Example Request: - - ``` - curl -X GET "http://0.0.0.0:4000/spend/tags" -H "Authorization: Bearer sk-1234" - ``` - - Spend with Start Date and End Date - - ``` - curl -X GET "http://0.0.0.0:4000/spend/tags?start_date=2022-01-01&end_date=2022-02-01" -H "Authorization: Bearer sk-1234" - ``` - - Args: - end_date: Time till which to view key spend - - start_date: Time from which to start viewing key spend - - tags: comman separated tags to filter on - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/global/spend/tags", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "end_date": end_date, - "start_date": start_date, - "tags": tags, - }, - spend_list_tags_params.SpendListTagsParams, - ), - ), - cast_to=SpendListTagsResponse, - ) - - def reset( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - ADMIN ONLY / MASTER KEY Only Endpoint - - Globally reset spend for All API Keys and Teams, maintain Hanzo_SpendLogs - - 1. Hanzo_SpendLogs will maintain the logs on spend, no data gets deleted from - there - 2. Hanzo_VerificationTokens spend will be set = 0 - 3. Hanzo_TeamTable spend will be set = 0 - """ - return self._post( - "/global/spend/reset", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_report( - self, - *, - api_key: Optional[str] | NotGiven = NOT_GIVEN, - customer_id: Optional[str] | NotGiven = NOT_GIVEN, - end_date: Optional[str] | NotGiven = NOT_GIVEN, - group_by: ( - Optional[Literal["team", "customer", "api_key"]] | NotGiven - ) = NOT_GIVEN, - internal_user_id: Optional[str] | NotGiven = NOT_GIVEN, - start_date: Optional[str] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SpendRetrieveReportResponse: - """Get Daily Spend per Team, based on specific startTime and endTime. - - Per team, - view usage by each key, model [ { "group-by-day": "2024-05-10", "teams": [ { - "team_name": "team-1" "spend": 10, "keys": [ "key": "1213", "usage": { - "model-1": { "cost": 12.50, "input_tokens": 1000, "output_tokens": 5000, - "requests": 100 }, "audio-modelname1": { "cost": 25.50, "seconds": 25, - "requests": 50 }, } } ] ] } - - Args: - api_key: View spend for a specific api_key. Example api_key='sk-1234 - - customer_id: View spend for a specific customer_id. Example customer_id='1234. Can be used in - conjunction with team_id as well. - - end_date: Time till which to view spend - - group_by: Group spend by internal team or customer or api_key - - internal_user_id: View spend for a specific internal_user_id. Example internal_user_id='1234 - - start_date: Time from which to start viewing spend - - team_id: View spend for a specific team_id. Example team_id='1234 - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/global/spend/report", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "api_key": api_key, - "customer_id": customer_id, - "end_date": end_date, - "group_by": group_by, - "internal_user_id": internal_user_id, - "start_date": start_date, - "team_id": team_id, - }, - spend_retrieve_report_params.SpendRetrieveReportParams, - ), - ), - cast_to=SpendRetrieveReportResponse, - ) - - -class AsyncSpendResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncSpendResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncSpendResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncSpendResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncSpendResourceWithStreamingResponse(self) - - async def list_tags( - self, - *, - end_date: Optional[str] | NotGiven = NOT_GIVEN, - start_date: Optional[str] | NotGiven = NOT_GIVEN, - tags: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SpendListTagsResponse: - """Hanzo Enterprise - View Spend Per Request Tag. - - Used by Hanzo UI - - Example Request: - - ``` - curl -X GET "http://0.0.0.0:4000/spend/tags" -H "Authorization: Bearer sk-1234" - ``` - - Spend with Start Date and End Date - - ``` - curl -X GET "http://0.0.0.0:4000/spend/tags?start_date=2022-01-01&end_date=2022-02-01" -H "Authorization: Bearer sk-1234" - ``` - - Args: - end_date: Time till which to view key spend - - start_date: Time from which to start viewing key spend - - tags: comman separated tags to filter on - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/global/spend/tags", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "end_date": end_date, - "start_date": start_date, - "tags": tags, - }, - spend_list_tags_params.SpendListTagsParams, - ), - ), - cast_to=SpendListTagsResponse, - ) - - async def reset( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - ADMIN ONLY / MASTER KEY Only Endpoint - - Globally reset spend for All API Keys and Teams, maintain Hanzo_SpendLogs - - 1. Hanzo_SpendLogs will maintain the logs on spend, no data gets deleted from - there - 2. Hanzo_VerificationTokens spend will be set = 0 - 3. Hanzo_TeamTable spend will be set = 0 - """ - return await self._post( - "/global/spend/reset", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_report( - self, - *, - api_key: Optional[str] | NotGiven = NOT_GIVEN, - customer_id: Optional[str] | NotGiven = NOT_GIVEN, - end_date: Optional[str] | NotGiven = NOT_GIVEN, - group_by: ( - Optional[Literal["team", "customer", "api_key"]] | NotGiven - ) = NOT_GIVEN, - internal_user_id: Optional[str] | NotGiven = NOT_GIVEN, - start_date: Optional[str] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SpendRetrieveReportResponse: - """Get Daily Spend per Team, based on specific startTime and endTime. - - Per team, - view usage by each key, model [ { "group-by-day": "2024-05-10", "teams": [ { - "team_name": "team-1" "spend": 10, "keys": [ "key": "1213", "usage": { - "model-1": { "cost": 12.50, "input_tokens": 1000, "output_tokens": 5000, - "requests": 100 }, "audio-modelname1": { "cost": 25.50, "seconds": 25, - "requests": 50 }, } } ] ] } - - Args: - api_key: View spend for a specific api_key. Example api_key='sk-1234 - - customer_id: View spend for a specific customer_id. Example customer_id='1234. Can be used in - conjunction with team_id as well. - - end_date: Time till which to view spend - - group_by: Group spend by internal team or customer or api_key - - internal_user_id: View spend for a specific internal_user_id. Example internal_user_id='1234 - - start_date: Time from which to start viewing spend - - team_id: View spend for a specific team_id. Example team_id='1234 - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/global/spend/report", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "api_key": api_key, - "customer_id": customer_id, - "end_date": end_date, - "group_by": group_by, - "internal_user_id": internal_user_id, - "start_date": start_date, - "team_id": team_id, - }, - spend_retrieve_report_params.SpendRetrieveReportParams, - ), - ), - cast_to=SpendRetrieveReportResponse, - ) - - -class SpendResourceWithRawResponse: - def __init__(self, spend: SpendResource) -> None: - self._spend = spend - - self.list_tags = to_raw_response_wrapper( - spend.list_tags, - ) - self.reset = to_raw_response_wrapper( - spend.reset, - ) - self.retrieve_report = to_raw_response_wrapper( - spend.retrieve_report, - ) - - -class AsyncSpendResourceWithRawResponse: - def __init__(self, spend: AsyncSpendResource) -> None: - self._spend = spend - - self.list_tags = async_to_raw_response_wrapper( - spend.list_tags, - ) - self.reset = async_to_raw_response_wrapper( - spend.reset, - ) - self.retrieve_report = async_to_raw_response_wrapper( - spend.retrieve_report, - ) - - -class SpendResourceWithStreamingResponse: - def __init__(self, spend: SpendResource) -> None: - self._spend = spend - - self.list_tags = to_streamed_response_wrapper( - spend.list_tags, - ) - self.reset = to_streamed_response_wrapper( - spend.reset, - ) - self.retrieve_report = to_streamed_response_wrapper( - spend.retrieve_report, - ) - - -class AsyncSpendResourceWithStreamingResponse: - def __init__(self, spend: AsyncSpendResource) -> None: - self._spend = spend - - self.list_tags = async_to_streamed_response_wrapper( - spend.list_tags, - ) - self.reset = async_to_streamed_response_wrapper( - spend.reset, - ) - self.retrieve_report = async_to_streamed_response_wrapper( - spend.retrieve_report, - ) diff --git a/pkg/hanzoai/resources/graphs.py b/pkg/hanzoai/resources/graphs.py deleted file mode 100644 index b53dc8240..000000000 --- a/pkg/hanzoai/resources/graphs.py +++ /dev/null @@ -1,513 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["GraphsResource", "AsyncGraphsResource"] - - -class GraphsResource(SyncAPIResource): - """Knowledge graph management.""" - - @cached_property - def with_raw_response(self) -> GraphsResourceWithRawResponse: - return GraphsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> GraphsResourceWithStreamingResponse: - return GraphsResourceWithStreamingResponse(self) - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all graphs.""" - return self._get( - "/ai/graphs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - graph_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific graph.""" - return self._get( - f"/ai/graphs/{graph_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - description: str | NotGiven = NOT_GIVEN, - schema: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new graph.""" - return self._post( - "/ai/graphs", - body={"name": name, "description": description, "schema": schema}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - graph_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a graph.""" - return self._delete( - f"/ai/graphs/{graph_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def add_node( - self, - graph_id: str, - *, - type: str, - properties: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a node to the graph.""" - return self._post( - f"/ai/graphs/{graph_id}/nodes", - body={"type": type, "properties": properties}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def add_edge( - self, - graph_id: str, - *, - source_id: str, - target_id: str, - type: str, - properties: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add an edge to the graph.""" - return self._post( - f"/ai/graphs/{graph_id}/edges", - body={ - "source_id": source_id, - "target_id": target_id, - "type": type, - "properties": properties, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def query( - self, - graph_id: str, - *, - query: str, - params: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Query the graph.""" - return self._post( - f"/ai/graphs/{graph_id}/query", - body={"query": query, "params": params}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def search( - self, - graph_id: str, - *, - text: str, - node_types: List[str] | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Search the graph.""" - return self._post( - f"/ai/graphs/{graph_id}/search", - body={"text": text, "node_types": node_types, "limit": limit}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stats( - self, - graph_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get graph statistics.""" - return self._get( - f"/ai/graphs/{graph_id}/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncGraphsResource(AsyncAPIResource): - """Knowledge graph management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncGraphsResourceWithRawResponse: - return AsyncGraphsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncGraphsResourceWithStreamingResponse: - return AsyncGraphsResourceWithStreamingResponse(self) - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/ai/graphs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - graph_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/ai/graphs/{graph_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - description: str | NotGiven = NOT_GIVEN, - schema: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/ai/graphs", - body={"name": name, "description": description, "schema": schema}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - graph_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/ai/graphs/{graph_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def add_node( - self, - graph_id: str, - *, - type: str, - properties: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/ai/graphs/{graph_id}/nodes", - body={"type": type, "properties": properties}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def add_edge( - self, - graph_id: str, - *, - source_id: str, - target_id: str, - type: str, - properties: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/ai/graphs/{graph_id}/edges", - body={ - "source_id": source_id, - "target_id": target_id, - "type": type, - "properties": properties, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def query( - self, - graph_id: str, - *, - query: str, - params: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/ai/graphs/{graph_id}/query", - body={"query": query, "params": params}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def search( - self, - graph_id: str, - *, - text: str, - node_types: List[str] | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/ai/graphs/{graph_id}/search", - body={"text": text, "node_types": node_types, "limit": limit}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stats( - self, - graph_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/ai/graphs/{graph_id}/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class GraphsResourceWithRawResponse: - def __init__(self, graphs: GraphsResource) -> None: - self._graphs = graphs - self.list = to_raw_response_wrapper(graphs.list) - self.get = to_raw_response_wrapper(graphs.get) - self.create = to_raw_response_wrapper(graphs.create) - self.delete = to_raw_response_wrapper(graphs.delete) - self.add_node = to_raw_response_wrapper(graphs.add_node) - self.add_edge = to_raw_response_wrapper(graphs.add_edge) - self.query = to_raw_response_wrapper(graphs.query) - self.search = to_raw_response_wrapper(graphs.search) - self.stats = to_raw_response_wrapper(graphs.stats) - - -class AsyncGraphsResourceWithRawResponse: - def __init__(self, graphs: AsyncGraphsResource) -> None: - self._graphs = graphs - self.list = async_to_raw_response_wrapper(graphs.list) - self.get = async_to_raw_response_wrapper(graphs.get) - self.create = async_to_raw_response_wrapper(graphs.create) - self.delete = async_to_raw_response_wrapper(graphs.delete) - self.add_node = async_to_raw_response_wrapper(graphs.add_node) - self.add_edge = async_to_raw_response_wrapper(graphs.add_edge) - self.query = async_to_raw_response_wrapper(graphs.query) - self.search = async_to_raw_response_wrapper(graphs.search) - self.stats = async_to_raw_response_wrapper(graphs.stats) - - -class GraphsResourceWithStreamingResponse: - def __init__(self, graphs: GraphsResource) -> None: - self._graphs = graphs - self.list = to_streamed_response_wrapper(graphs.list) - self.get = to_streamed_response_wrapper(graphs.get) - self.create = to_streamed_response_wrapper(graphs.create) - self.delete = to_streamed_response_wrapper(graphs.delete) - self.add_node = to_streamed_response_wrapper(graphs.add_node) - self.add_edge = to_streamed_response_wrapper(graphs.add_edge) - self.query = to_streamed_response_wrapper(graphs.query) - self.search = to_streamed_response_wrapper(graphs.search) - self.stats = to_streamed_response_wrapper(graphs.stats) - - -class AsyncGraphsResourceWithStreamingResponse: - def __init__(self, graphs: AsyncGraphsResource) -> None: - self._graphs = graphs - self.list = async_to_streamed_response_wrapper(graphs.list) - self.get = async_to_streamed_response_wrapper(graphs.get) - self.create = async_to_streamed_response_wrapper(graphs.create) - self.delete = async_to_streamed_response_wrapper(graphs.delete) - self.add_node = async_to_streamed_response_wrapper(graphs.add_node) - self.add_edge = async_to_streamed_response_wrapper(graphs.add_edge) - self.query = async_to_streamed_response_wrapper(graphs.query) - self.search = async_to_streamed_response_wrapper(graphs.search) - self.stats = async_to_streamed_response_wrapper(graphs.stats) diff --git a/pkg/hanzoai/resources/health.py b/pkg/hanzoai/resources/health.py deleted file mode 100644 index ed2ae6eae..000000000 --- a/pkg/hanzoai/resources/health.py +++ /dev/null @@ -1,506 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal - -import httpx - -from ..types import health_check_all_params, health_check_services_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - maybe_transform, - async_maybe_transform, -) -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["HealthResource", "AsyncHealthResource"] - - -class HealthResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> HealthResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return HealthResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> HealthResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return HealthResourceWithStreamingResponse(self) - - def check_all( - self, - *, - model: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - ๐Ÿšจ USE `/health/liveliness` to health check the proxy ๐Ÿšจ - - See more ๐Ÿ‘‰ https://docs.hanzo.ai/docs/proxy/health - - Check the health of all the endpoints in config.yaml - - To run health checks in the background, add this to config.yaml: - - ``` - general_settings: - # ... other settings - background_health_checks: True - ``` - - else, the health checks will be run on models when /health is called. - - Args: - model: Specify the model name (optional) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/health", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"model": model}, health_check_all_params.HealthCheckAllParams - ), - ), - cast_to=object, - ) - - def check_liveliness( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Unprotected endpoint for checking if worker is alive""" - return self._get( - "/health/liveliness", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def check_liveness( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Unprotected endpoint for checking if worker is alive""" - return self._get( - "/health/liveness", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def check_readiness( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Unprotected endpoint for checking if worker can receive requests""" - return self._get( - "/health/readiness", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def check_services( - self, - *, - service: Union[ - Literal[ - "slack_budget_alerts", - "langfuse", - "slack", - "openmeter", - "webhook", - "email", - "braintrust", - "datadog", - ], - str, - ], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Use this admin-only endpoint to check if the service is healthy. - - Example: - - ``` - curl -L -X GET 'http://0.0.0.0:4000/health/services?service=datadog' -H 'Authorization: Bearer sk-1234' - ``` - - Args: - service: Specify the service being hit. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/health/services", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"service": service}, - health_check_services_params.HealthCheckServicesParams, - ), - ), - cast_to=object, - ) - - -class AsyncHealthResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncHealthResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncHealthResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncHealthResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncHealthResourceWithStreamingResponse(self) - - async def check_all( - self, - *, - model: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - ๐Ÿšจ USE `/health/liveliness` to health check the proxy ๐Ÿšจ - - See more ๐Ÿ‘‰ https://docs.hanzo.ai/docs/proxy/health - - Check the health of all the endpoints in config.yaml - - To run health checks in the background, add this to config.yaml: - - ``` - general_settings: - # ... other settings - background_health_checks: True - ``` - - else, the health checks will be run on models when /health is called. - - Args: - model: Specify the model name (optional) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/health", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"model": model}, health_check_all_params.HealthCheckAllParams - ), - ), - cast_to=object, - ) - - async def check_liveliness( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Unprotected endpoint for checking if worker is alive""" - return await self._get( - "/health/liveliness", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def check_liveness( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Unprotected endpoint for checking if worker is alive""" - return await self._get( - "/health/liveness", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def check_readiness( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Unprotected endpoint for checking if worker can receive requests""" - return await self._get( - "/health/readiness", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def check_services( - self, - *, - service: Union[ - Literal[ - "slack_budget_alerts", - "langfuse", - "slack", - "openmeter", - "webhook", - "email", - "braintrust", - "datadog", - ], - str, - ], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Use this admin-only endpoint to check if the service is healthy. - - Example: - - ``` - curl -L -X GET 'http://0.0.0.0:4000/health/services?service=datadog' -H 'Authorization: Bearer sk-1234' - ``` - - Args: - service: Specify the service being hit. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/health/services", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"service": service}, - health_check_services_params.HealthCheckServicesParams, - ), - ), - cast_to=object, - ) - - -class HealthResourceWithRawResponse: - def __init__(self, health: HealthResource) -> None: - self._health = health - - self.check_all = to_raw_response_wrapper( - health.check_all, - ) - self.check_liveliness = to_raw_response_wrapper( - health.check_liveliness, - ) - self.check_liveness = to_raw_response_wrapper( - health.check_liveness, - ) - self.check_readiness = to_raw_response_wrapper( - health.check_readiness, - ) - self.check_services = to_raw_response_wrapper( - health.check_services, - ) - - -class AsyncHealthResourceWithRawResponse: - def __init__(self, health: AsyncHealthResource) -> None: - self._health = health - - self.check_all = async_to_raw_response_wrapper( - health.check_all, - ) - self.check_liveliness = async_to_raw_response_wrapper( - health.check_liveliness, - ) - self.check_liveness = async_to_raw_response_wrapper( - health.check_liveness, - ) - self.check_readiness = async_to_raw_response_wrapper( - health.check_readiness, - ) - self.check_services = async_to_raw_response_wrapper( - health.check_services, - ) - - -class HealthResourceWithStreamingResponse: - def __init__(self, health: HealthResource) -> None: - self._health = health - - self.check_all = to_streamed_response_wrapper( - health.check_all, - ) - self.check_liveliness = to_streamed_response_wrapper( - health.check_liveliness, - ) - self.check_liveness = to_streamed_response_wrapper( - health.check_liveness, - ) - self.check_readiness = to_streamed_response_wrapper( - health.check_readiness, - ) - self.check_services = to_streamed_response_wrapper( - health.check_services, - ) - - -class AsyncHealthResourceWithStreamingResponse: - def __init__(self, health: AsyncHealthResource) -> None: - self._health = health - - self.check_all = async_to_streamed_response_wrapper( - health.check_all, - ) - self.check_liveliness = async_to_streamed_response_wrapper( - health.check_liveliness, - ) - self.check_liveness = async_to_streamed_response_wrapper( - health.check_liveness, - ) - self.check_readiness = async_to_streamed_response_wrapper( - health.check_readiness, - ) - self.check_services = async_to_streamed_response_wrapper( - health.check_services, - ) diff --git a/pkg/hanzoai/resources/iam.py b/pkg/hanzoai/resources/iam.py deleted file mode 100644 index 1078c7547..000000000 --- a/pkg/hanzoai/resources/iam.py +++ /dev/null @@ -1,2835 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List, Optional - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["IAMResource", "AsyncIAMResource"] - - -class IAMResource(SyncAPIResource): - """Hanzo IAM โ€” identity, access, and organization management. - - Wraps the Casdoor-compatible API at hanzo.id/api/ behind the /iam/ gateway - prefix. Auth endpoints accept explicit params; CRUD endpoints for users, - organizations, applications, roles, permissions, groups, tokens, sessions, - and invitations accept full object dicts matching the Casdoor schema. - """ - - @cached_property - def with_raw_response(self) -> IAMResourceWithRawResponse: - return IAMResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> IAMResourceWithStreamingResponse: - return IAMResourceWithStreamingResponse(self) - - # โ”€โ”€ Auth โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def signup( - self, - *, - application: str, - organization: str, - username: str, - password: str, - name: str | NotGiven = NOT_GIVEN, - email: str | NotGiven = NOT_GIVEN, - phone: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Sign up a new user.""" - return self._post( - "/iam/api/signup", - body={ - "application": application, - "organization": organization, - "username": username, - "password": password, - "name": name, - "email": email, - "phone": phone, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def login( - self, - *, - application: str, - organization: str, - username: str, - password: str, - type: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Log in and obtain a session token.""" - return self._post( - "/iam/api/login", - body={ - "application": application, - "organization": organization, - "username": username, - "password": password, - "type": type, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get_account( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get the current authenticated account.""" - return self._get( - "/iam/api/get-account", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get_userinfo( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get OIDC userinfo for the current session.""" - return self._get( - "/iam/api/userinfo", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def set_password( - self, - *, - user_owner: str, - user_name: str, - old_password: str, - new_password: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Set or change a user's password.""" - return self._post( - "/iam/api/set-password", - body={ - "userOwner": user_owner, - "userName": user_name, - "oldPassword": old_password, - "newPassword": new_password, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def send_verification_code( - self, - *, - dest: str, - type: str, - application_id: str | NotGiven = NOT_GIVEN, - method: str | NotGiven = NOT_GIVEN, - check_user: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Send an email or SMS verification code.""" - return self._post( - "/iam/api/send-verification-code", - body={ - "dest": dest, - "type": type, - "applicationId": application_id, - "method": method, - "checkUser": check_user, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def verify_code( - self, - *, - dest: str, - type: str, - code: str, - application_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Verify an email or SMS code.""" - return self._post( - "/iam/api/verify-code", - body={ - "dest": dest, - "type": type, - "code": code, - "applicationId": application_id, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Users โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_users( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all users in an organization.""" - return self._get( - "/iam/api/get-users", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - def get_user( - self, - *, - id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a user by id (format: ``owner/name``).""" - return self._get( - "/iam/api/get-user", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"id": id}, - ), - cast_to=object, - ) - - def get_user_count( - self, - *, - owner: str, - is_online: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get the count of users in an organization.""" - return self._get( - "/iam/api/get-user-count", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner, "isOnline": is_online}, - ), - cast_to=object, - ) - - def add_user( - self, - *, - user: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new user.""" - return self._post( - "/iam/api/add-user", - body=user, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_user( - self, - *, - user: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing user.""" - return self._post( - "/iam/api/update-user", - body=user, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_user( - self, - *, - user: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a user.""" - return self._post( - "/iam/api/delete-user", - body=user, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Organizations โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_organizations( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all organizations.""" - return self._get( - "/iam/api/get-organizations", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - def get_organization( - self, - *, - id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get an organization by id (format: ``owner/name``).""" - return self._get( - "/iam/api/get-organization", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"id": id}, - ), - cast_to=object, - ) - - def add_organization( - self, - *, - organization: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new organization.""" - return self._post( - "/iam/api/add-organization", - body=organization, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_organization( - self, - *, - organization: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing organization.""" - return self._post( - "/iam/api/update-organization", - body=organization, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_organization( - self, - *, - organization: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete an organization.""" - return self._post( - "/iam/api/delete-organization", - body=organization, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Applications โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_applications( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all applications.""" - return self._get( - "/iam/api/get-applications", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - def get_application( - self, - *, - id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get an application by id (format: ``owner/name``).""" - return self._get( - "/iam/api/get-application", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"id": id}, - ), - cast_to=object, - ) - - def add_application( - self, - *, - application: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new application.""" - return self._post( - "/iam/api/add-application", - body=application, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_application( - self, - *, - application: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing application.""" - return self._post( - "/iam/api/update-application", - body=application, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_application( - self, - *, - application: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete an application.""" - return self._post( - "/iam/api/delete-application", - body=application, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Roles โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_roles( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all roles.""" - return self._get( - "/iam/api/get-roles", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - def get_role( - self, - *, - id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a role by id (format: ``owner/name``).""" - return self._get( - "/iam/api/get-role", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"id": id}, - ), - cast_to=object, - ) - - def add_role( - self, - *, - role: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new role.""" - return self._post( - "/iam/api/add-role", - body=role, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_role( - self, - *, - role: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing role.""" - return self._post( - "/iam/api/update-role", - body=role, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_role( - self, - *, - role: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a role.""" - return self._post( - "/iam/api/delete-role", - body=role, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Permissions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_permissions( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all permissions.""" - return self._get( - "/iam/api/get-permissions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - def get_permission( - self, - *, - id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a permission by id (format: ``owner/name``).""" - return self._get( - "/iam/api/get-permission", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"id": id}, - ), - cast_to=object, - ) - - def add_permission( - self, - *, - permission: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new permission.""" - return self._post( - "/iam/api/add-permission", - body=permission, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_permission( - self, - *, - permission: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing permission.""" - return self._post( - "/iam/api/update-permission", - body=permission, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_permission( - self, - *, - permission: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a permission.""" - return self._post( - "/iam/api/delete-permission", - body=permission, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def enforce( - self, - *, - permission_id: str, - model_name: str, - resource_id: str, - action: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Check if a permission is allowed (Casbin enforce).""" - return self._post( - "/iam/api/enforce", - body={ - "id": permission_id, - "v0": model_name, - "v1": resource_id, - "v2": action, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def batch_enforce( - self, - *, - permission_id: str, - requests: List[List[str]], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Batch check multiple permission rules.""" - return self._post( - "/iam/api/batch-enforce", - body={ - "id": permission_id, - "requests": requests, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Groups โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_groups( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all groups.""" - return self._get( - "/iam/api/get-groups", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - def get_group( - self, - *, - id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a group by id (format: ``owner/name``).""" - return self._get( - "/iam/api/get-group", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"id": id}, - ), - cast_to=object, - ) - - def add_group( - self, - *, - group: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new group.""" - return self._post( - "/iam/api/add-group", - body=group, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_group( - self, - *, - group: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing group.""" - return self._post( - "/iam/api/update-group", - body=group, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_group( - self, - *, - group: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a group.""" - return self._post( - "/iam/api/delete-group", - body=group, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Providers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_providers( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all identity providers.""" - return self._get( - "/iam/api/get-providers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - def get_provider( - self, - *, - id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get an identity provider by id (format: ``owner/name``).""" - return self._get( - "/iam/api/get-provider", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"id": id}, - ), - cast_to=object, - ) - - # โ”€โ”€ Tokens โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_tokens( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all tokens.""" - return self._get( - "/iam/api/get-tokens", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - def add_token( - self, - *, - token: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new token.""" - return self._post( - "/iam/api/add-token", - body=token, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_token( - self, - *, - token: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a token.""" - return self._post( - "/iam/api/delete-token", - body=token, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Sessions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_sessions( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all sessions.""" - return self._get( - "/iam/api/get-sessions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - def delete_session( - self, - *, - session: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a session.""" - return self._post( - "/iam/api/delete-session", - body=session, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Invitations โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_invitations( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all invitations.""" - return self._get( - "/iam/api/get-invitations", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - def add_invitation( - self, - *, - invitation: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new invitation.""" - return self._post( - "/iam/api/add-invitation", - body=invitation, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def send_invitation( - self, - *, - invitation: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Send an invitation email.""" - return self._post( - "/iam/api/send-invitation", - body=invitation, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_invitation( - self, - *, - invitation: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete an invitation.""" - return self._post( - "/iam/api/delete-invitation", - body=invitation, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Records / Audit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_records( - self, - *, - owner: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List audit records.""" - return self._get( - "/iam/api/get-records", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - # โ”€โ”€ System โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def get_system_info( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get IAM system information.""" - return self._get( - "/iam/api/get-system-info", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def health( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Health check.""" - return self._get( - "/iam/api/health", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ MFA โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def mfa_initiate( - self, - *, - mfa_type: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Initiate MFA setup (returns secret/QR code).""" - return self._post( - "/iam/api/mfa/setup/initiate", - body={"mfaType": mfa_type}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def mfa_verify( - self, - *, - passcode: str, - mfa_type: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Verify an MFA passcode during setup.""" - return self._post( - "/iam/api/mfa/setup/verify", - body={"passcode": passcode, "mfaType": mfa_type}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def mfa_enable( - self, - *, - mfa_type: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Enable MFA for the current user.""" - return self._post( - "/iam/api/mfa/setup/enable", - body={"mfaType": mfa_type}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_mfa( - self, - *, - owner: str, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete MFA configuration for a user.""" - return self._post( - "/iam/api/delete-mfa", - body={"owner": owner, "name": name}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -# Async resource -# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - - -class AsyncIAMResource(AsyncAPIResource): - """Hanzo IAM โ€” identity, access, and organization management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncIAMResourceWithRawResponse: - return AsyncIAMResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncIAMResourceWithStreamingResponse: - return AsyncIAMResourceWithStreamingResponse(self) - - # โ”€โ”€ Auth โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def signup( - self, - *, - application: str, - organization: str, - username: str, - password: str, - name: str | NotGiven = NOT_GIVEN, - email: str | NotGiven = NOT_GIVEN, - phone: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Sign up a new user.""" - return await self._post( - "/iam/api/signup", - body={ - "application": application, - "organization": organization, - "username": username, - "password": password, - "name": name, - "email": email, - "phone": phone, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def login( - self, - *, - application: str, - organization: str, - username: str, - password: str, - type: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Log in and obtain a session token.""" - return await self._post( - "/iam/api/login", - body={ - "application": application, - "organization": organization, - "username": username, - "password": password, - "type": type, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get_account( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get the current authenticated account.""" - return await self._get( - "/iam/api/get-account", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get_userinfo( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get OIDC userinfo for the current session.""" - return await self._get( - "/iam/api/userinfo", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def set_password( - self, - *, - user_owner: str, - user_name: str, - old_password: str, - new_password: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Set or change a user's password.""" - return await self._post( - "/iam/api/set-password", - body={ - "userOwner": user_owner, - "userName": user_name, - "oldPassword": old_password, - "newPassword": new_password, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def send_verification_code( - self, - *, - dest: str, - type: str, - application_id: str | NotGiven = NOT_GIVEN, - method: str | NotGiven = NOT_GIVEN, - check_user: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Send an email or SMS verification code.""" - return await self._post( - "/iam/api/send-verification-code", - body={ - "dest": dest, - "type": type, - "applicationId": application_id, - "method": method, - "checkUser": check_user, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def verify_code( - self, - *, - dest: str, - type: str, - code: str, - application_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Verify an email or SMS code.""" - return await self._post( - "/iam/api/verify-code", - body={ - "dest": dest, - "type": type, - "code": code, - "applicationId": application_id, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Users โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_users( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all users in an organization.""" - return await self._get( - "/iam/api/get-users", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - async def get_user( - self, - *, - id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a user by id (format: ``owner/name``).""" - return await self._get( - "/iam/api/get-user", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"id": id}, - ), - cast_to=object, - ) - - async def get_user_count( - self, - *, - owner: str, - is_online: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get the count of users in an organization.""" - return await self._get( - "/iam/api/get-user-count", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner, "isOnline": is_online}, - ), - cast_to=object, - ) - - async def add_user( - self, - *, - user: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new user.""" - return await self._post( - "/iam/api/add-user", - body=user, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_user( - self, - *, - user: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing user.""" - return await self._post( - "/iam/api/update-user", - body=user, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_user( - self, - *, - user: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a user.""" - return await self._post( - "/iam/api/delete-user", - body=user, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Organizations โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_organizations( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all organizations.""" - return await self._get( - "/iam/api/get-organizations", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - async def get_organization( - self, - *, - id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get an organization by id (format: ``owner/name``).""" - return await self._get( - "/iam/api/get-organization", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"id": id}, - ), - cast_to=object, - ) - - async def add_organization( - self, - *, - organization: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new organization.""" - return await self._post( - "/iam/api/add-organization", - body=organization, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_organization( - self, - *, - organization: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing organization.""" - return await self._post( - "/iam/api/update-organization", - body=organization, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_organization( - self, - *, - organization: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete an organization.""" - return await self._post( - "/iam/api/delete-organization", - body=organization, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Applications โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_applications( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all applications.""" - return await self._get( - "/iam/api/get-applications", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - async def get_application( - self, - *, - id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get an application by id (format: ``owner/name``).""" - return await self._get( - "/iam/api/get-application", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"id": id}, - ), - cast_to=object, - ) - - async def add_application( - self, - *, - application: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new application.""" - return await self._post( - "/iam/api/add-application", - body=application, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_application( - self, - *, - application: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing application.""" - return await self._post( - "/iam/api/update-application", - body=application, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_application( - self, - *, - application: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete an application.""" - return await self._post( - "/iam/api/delete-application", - body=application, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Roles โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_roles( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all roles.""" - return await self._get( - "/iam/api/get-roles", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - async def get_role( - self, - *, - id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a role by id (format: ``owner/name``).""" - return await self._get( - "/iam/api/get-role", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"id": id}, - ), - cast_to=object, - ) - - async def add_role( - self, - *, - role: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new role.""" - return await self._post( - "/iam/api/add-role", - body=role, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_role( - self, - *, - role: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing role.""" - return await self._post( - "/iam/api/update-role", - body=role, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_role( - self, - *, - role: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a role.""" - return await self._post( - "/iam/api/delete-role", - body=role, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Permissions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_permissions( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all permissions.""" - return await self._get( - "/iam/api/get-permissions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - async def get_permission( - self, - *, - id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a permission by id (format: ``owner/name``).""" - return await self._get( - "/iam/api/get-permission", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"id": id}, - ), - cast_to=object, - ) - - async def add_permission( - self, - *, - permission: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new permission.""" - return await self._post( - "/iam/api/add-permission", - body=permission, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_permission( - self, - *, - permission: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing permission.""" - return await self._post( - "/iam/api/update-permission", - body=permission, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_permission( - self, - *, - permission: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a permission.""" - return await self._post( - "/iam/api/delete-permission", - body=permission, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def enforce( - self, - *, - permission_id: str, - model_name: str, - resource_id: str, - action: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Check if a permission is allowed (Casbin enforce).""" - return await self._post( - "/iam/api/enforce", - body={ - "id": permission_id, - "v0": model_name, - "v1": resource_id, - "v2": action, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def batch_enforce( - self, - *, - permission_id: str, - requests: List[List[str]], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Batch check multiple permission rules.""" - return await self._post( - "/iam/api/batch-enforce", - body={ - "id": permission_id, - "requests": requests, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Groups โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_groups( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all groups.""" - return await self._get( - "/iam/api/get-groups", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - async def get_group( - self, - *, - id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a group by id (format: ``owner/name``).""" - return await self._get( - "/iam/api/get-group", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"id": id}, - ), - cast_to=object, - ) - - async def add_group( - self, - *, - group: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new group.""" - return await self._post( - "/iam/api/add-group", - body=group, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_group( - self, - *, - group: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an existing group.""" - return await self._post( - "/iam/api/update-group", - body=group, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_group( - self, - *, - group: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a group.""" - return await self._post( - "/iam/api/delete-group", - body=group, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Providers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_providers( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all identity providers.""" - return await self._get( - "/iam/api/get-providers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - async def get_provider( - self, - *, - id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get an identity provider by id (format: ``owner/name``).""" - return await self._get( - "/iam/api/get-provider", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"id": id}, - ), - cast_to=object, - ) - - # โ”€โ”€ Tokens โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_tokens( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all tokens.""" - return await self._get( - "/iam/api/get-tokens", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - async def add_token( - self, - *, - token: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new token.""" - return await self._post( - "/iam/api/add-token", - body=token, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_token( - self, - *, - token: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a token.""" - return await self._post( - "/iam/api/delete-token", - body=token, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Sessions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_sessions( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all sessions.""" - return await self._get( - "/iam/api/get-sessions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - async def delete_session( - self, - *, - session: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a session.""" - return await self._post( - "/iam/api/delete-session", - body=session, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Invitations โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_invitations( - self, - *, - owner: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all invitations.""" - return await self._get( - "/iam/api/get-invitations", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - async def add_invitation( - self, - *, - invitation: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a new invitation.""" - return await self._post( - "/iam/api/add-invitation", - body=invitation, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def send_invitation( - self, - *, - invitation: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Send an invitation email.""" - return await self._post( - "/iam/api/send-invitation", - body=invitation, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_invitation( - self, - *, - invitation: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete an invitation.""" - return await self._post( - "/iam/api/delete-invitation", - body=invitation, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Records / Audit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_records( - self, - *, - owner: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List audit records.""" - return await self._get( - "/iam/api/get-records", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"owner": owner}, - ), - cast_to=object, - ) - - # โ”€โ”€ System โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def get_system_info( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get IAM system information.""" - return await self._get( - "/iam/api/get-system-info", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def health( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Health check.""" - return await self._get( - "/iam/api/health", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ MFA โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def mfa_initiate( - self, - *, - mfa_type: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Initiate MFA setup (returns secret/QR code).""" - return await self._post( - "/iam/api/mfa/setup/initiate", - body={"mfaType": mfa_type}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def mfa_verify( - self, - *, - passcode: str, - mfa_type: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Verify an MFA passcode during setup.""" - return await self._post( - "/iam/api/mfa/setup/verify", - body={"passcode": passcode, "mfaType": mfa_type}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def mfa_enable( - self, - *, - mfa_type: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Enable MFA for the current user.""" - return await self._post( - "/iam/api/mfa/setup/enable", - body={"mfaType": mfa_type}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_mfa( - self, - *, - owner: str, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete MFA configuration for a user.""" - return await self._post( - "/iam/api/delete-mfa", - body={"owner": owner, "name": name}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -# Raw / Streaming response wrappers -# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - -# All 49 methods wrapped for each of the four wrapper classes. - -_SYNC_METHODS = [ - "signup", "login", "get_account", "get_userinfo", "set_password", - "send_verification_code", "verify_code", - "list_users", "get_user", "get_user_count", "add_user", "update_user", - "delete_user", - "list_organizations", "get_organization", "add_organization", - "update_organization", "delete_organization", - "list_applications", "get_application", "add_application", - "update_application", "delete_application", - "list_roles", "get_role", "add_role", "update_role", "delete_role", - "list_permissions", "get_permission", "add_permission", - "update_permission", "delete_permission", "enforce", "batch_enforce", - "list_groups", "get_group", "add_group", "update_group", "delete_group", - "list_providers", "get_provider", - "list_tokens", "add_token", "delete_token", - "list_sessions", "delete_session", - "list_invitations", "add_invitation", "send_invitation", - "delete_invitation", - "list_records", - "get_system_info", "health", - "mfa_initiate", "mfa_verify", "mfa_enable", "delete_mfa", -] - - -class IAMResourceWithRawResponse: - def __init__(self, iam: IAMResource) -> None: - self._iam = iam - for name in _SYNC_METHODS: - setattr(self, name, to_raw_response_wrapper(getattr(iam, name))) - - -class AsyncIAMResourceWithRawResponse: - def __init__(self, iam: AsyncIAMResource) -> None: - self._iam = iam - for name in _SYNC_METHODS: - setattr(self, name, async_to_raw_response_wrapper(getattr(iam, name))) - - -class IAMResourceWithStreamingResponse: - def __init__(self, iam: IAMResource) -> None: - self._iam = iam - for name in _SYNC_METHODS: - setattr(self, name, to_streamed_response_wrapper(getattr(iam, name))) - - -class AsyncIAMResourceWithStreamingResponse: - def __init__(self, iam: AsyncIAMResource) -> None: - self._iam = iam - for name in _SYNC_METHODS: - setattr(self, name, async_to_streamed_response_wrapper(getattr(iam, name))) diff --git a/pkg/hanzoai/resources/identity.py b/pkg/hanzoai/resources/identity.py deleted file mode 100644 index 5ceb775dd..000000000 --- a/pkg/hanzoai/resources/identity.py +++ /dev/null @@ -1,888 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class IdentityResource(SyncAPIResource): - """Identity and authentication management (Casdoor-backed).""" - - @cached_property - def with_raw_response(self) -> IdentityResourceWithRawResponse: - return IdentityResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> IdentityResourceWithStreamingResponse: - return IdentityResourceWithStreamingResponse(self) - - # Auth methods - def login( - self, - *, - provider: str | NotGiven = NOT_GIVEN, - redirect_uri: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Initiate login flow.""" - return self._post( - "/identity/login", - body={"provider": provider, "redirect_uri": redirect_uri}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def logout( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Logout current session.""" - return self._post( - "/identity/logout", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def whoami( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get current identity information.""" - return self._get( - "/identity/whoami", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def token( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get current access token.""" - return self._get( - "/identity/token", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def refresh( - self, - *, - refresh_token: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Refresh access token.""" - return self._post( - "/identity/refresh", - body={"refresh_token": refresh_token}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Service accounts - def create_service_account( - self, - *, - name: str, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a service account.""" - return self._post( - "/identity/sa", - body={"name": name, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_service_accounts( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List service accounts.""" - return self._get( - "/identity/sa", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_service_account( - self, - sa_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a service account.""" - return self._delete( - f"/identity/sa/{sa_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_sa_key( - self, - sa_id: str, - *, - ttl: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a key for service account.""" - return self._post( - f"/identity/sa/{sa_id}/keys", - body={"ttl": ttl}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_sa_keys( - self, - sa_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List keys for service account.""" - return self._get( - f"/identity/sa/{sa_id}/keys", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def revoke_sa_key( - self, - sa_id: str, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Revoke a service account key.""" - return self._delete( - f"/identity/sa/{sa_id}/keys/{key_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Roles - def create_role( - self, - *, - name: str, - permissions: List[str], - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a role.""" - return self._post( - "/identity/roles", - body={"name": name, "permissions": permissions, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_roles( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List roles.""" - return self._get( - "/identity/roles", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_role( - self, - role_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a role.""" - return self._delete( - f"/identity/roles/{role_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def grant( - self, - *, - principal: str, - role: str, - scope: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Grant role to principal.""" - return self._post( - "/identity/grants", - body={"principal": principal, "role": role, "scope": scope}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def revoke( - self, - *, - principal: str, - role: str, - scope: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Revoke role from principal.""" - return self._delete( - "/identity/grants", - body={"principal": principal, "role": role, "scope": scope}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # OIDC/SAML Apps - def create_app( - self, - *, - name: str, - type: str, - redirect_uris: List[str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create an OIDC/SAML app.""" - return self._post( - "/identity/apps", - body={"name": name, "type": type, "redirect_uris": redirect_uris}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get_app_credentials( - self, - app_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get app credentials.""" - return self._get( - f"/identity/apps/{app_id}/credentials", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def rotate_app_secret( - self, - app_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Rotate app secret.""" - return self._post( - f"/identity/apps/{app_id}/rotate-secret", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncIdentityResource(AsyncAPIResource): - """Identity and authentication management (Casdoor-backed).""" - - @cached_property - def with_raw_response(self) -> AsyncIdentityResourceWithRawResponse: - return AsyncIdentityResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncIdentityResourceWithStreamingResponse: - return AsyncIdentityResourceWithStreamingResponse(self) - - async def login( - self, - *, - provider: str | NotGiven = NOT_GIVEN, - redirect_uri: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Initiate login flow.""" - return await self._post( - "/identity/login", - body={"provider": provider, "redirect_uri": redirect_uri}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def logout( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Logout current session.""" - return await self._post( - "/identity/logout", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def whoami( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get current identity information.""" - return await self._get( - "/identity/whoami", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def token( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get current access token.""" - return await self._get( - "/identity/token", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def refresh( - self, - *, - refresh_token: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Refresh access token.""" - return await self._post( - "/identity/refresh", - body={"refresh_token": refresh_token}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_service_account( - self, - *, - name: str, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a service account.""" - return await self._post( - "/identity/sa", - body={"name": name, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_service_accounts( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List service accounts.""" - return await self._get( - "/identity/sa", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_service_account( - self, - sa_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a service account.""" - return await self._delete( - f"/identity/sa/{sa_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_sa_key( - self, - sa_id: str, - *, - ttl: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a key for service account.""" - return await self._post( - f"/identity/sa/{sa_id}/keys", - body={"ttl": ttl}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_sa_keys( - self, - sa_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List keys for service account.""" - return await self._get( - f"/identity/sa/{sa_id}/keys", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def revoke_sa_key( - self, - sa_id: str, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Revoke a service account key.""" - return await self._delete( - f"/identity/sa/{sa_id}/keys/{key_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_role( - self, - *, - name: str, - permissions: List[str], - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a role.""" - return await self._post( - "/identity/roles", - body={"name": name, "permissions": permissions, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_roles( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List roles.""" - return await self._get( - "/identity/roles", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_role( - self, - role_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a role.""" - return await self._delete( - f"/identity/roles/{role_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def grant( - self, - *, - principal: str, - role: str, - scope: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Grant role to principal.""" - return await self._post( - "/identity/grants", - body={"principal": principal, "role": role, "scope": scope}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def revoke( - self, - *, - principal: str, - role: str, - scope: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Revoke role from principal.""" - return await self._delete( - "/identity/grants", - body={"principal": principal, "role": role, "scope": scope}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_app( - self, - *, - name: str, - type: str, - redirect_uris: List[str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create an OIDC/SAML app.""" - return await self._post( - "/identity/apps", - body={"name": name, "type": type, "redirect_uris": redirect_uris}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get_app_credentials( - self, - app_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get app credentials.""" - return await self._get( - f"/identity/apps/{app_id}/credentials", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def rotate_app_secret( - self, - app_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Rotate app secret.""" - return await self._post( - f"/identity/apps/{app_id}/rotate-secret", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class IdentityResourceWithRawResponse: - def __init__(self, identity: IdentityResource) -> None: - self._identity = identity - - -class AsyncIdentityResourceWithRawResponse: - def __init__(self, identity: AsyncIdentityResource) -> None: - self._identity = identity - - -class IdentityResourceWithStreamingResponse: - def __init__(self, identity: IdentityResource) -> None: - self._identity = identity - - -class AsyncIdentityResourceWithStreamingResponse: - def __init__(self, identity: AsyncIdentityResource) -> None: - self._identity = identity diff --git a/pkg/hanzoai/resources/images/__init__.py b/pkg/hanzoai/resources/images/__init__.py deleted file mode 100644 index 42de38a06..000000000 --- a/pkg/hanzoai/resources/images/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Hanzo AI SDK - -from .images import ( - ImagesResource, - AsyncImagesResource, - ImagesResourceWithRawResponse, - AsyncImagesResourceWithRawResponse, - ImagesResourceWithStreamingResponse, - AsyncImagesResourceWithStreamingResponse, -) -from .generations import ( - GenerationsResource, - AsyncGenerationsResource, - GenerationsResourceWithRawResponse, - AsyncGenerationsResourceWithRawResponse, - GenerationsResourceWithStreamingResponse, - AsyncGenerationsResourceWithStreamingResponse, -) - -__all__ = [ - "GenerationsResource", - "AsyncGenerationsResource", - "GenerationsResourceWithRawResponse", - "AsyncGenerationsResourceWithRawResponse", - "GenerationsResourceWithStreamingResponse", - "AsyncGenerationsResourceWithStreamingResponse", - "ImagesResource", - "AsyncImagesResource", - "ImagesResourceWithRawResponse", - "AsyncImagesResourceWithRawResponse", - "ImagesResourceWithStreamingResponse", - "AsyncImagesResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/inference.py b/pkg/hanzoai/resources/inference.py deleted file mode 100644 index 37ce9bd1a..000000000 --- a/pkg/hanzoai/resources/inference.py +++ /dev/null @@ -1,524 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class InferenceResource(SyncAPIResource): - """Local AI runtime and inference service (Ollama-like).""" - - @cached_property - def with_raw_response(self) -> InferenceResourceWithRawResponse: - return InferenceResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> InferenceResourceWithStreamingResponse: - return InferenceResourceWithStreamingResponse(self) - - def start( - self, - *, - model: str | NotGiven = NOT_GIVEN, - port: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Start inference server.""" - return self._post( - "/inference/start", - body={"model": model, "port": port}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stop( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Stop inference server.""" - return self._post( - "/inference/stop", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def status( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get inference server status.""" - return self._get( - "/inference/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def logs( - self, - *, - follow: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get inference server logs.""" - return self._get( - "/inference/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"follow": follow}, - ), - cast_to=object, - ) - - def chat( - self, - *, - model: str, - messages: List[Dict[str, str]], - temperature: float | NotGiven = NOT_GIVEN, - max_tokens: int | NotGiven = NOT_GIVEN, - stream: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Chat completion.""" - return self._post( - "/inference/chat", - body={ - "model": model, - "messages": messages, - "temperature": temperature, - "max_tokens": max_tokens, - "stream": stream, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def generate( - self, - *, - model: str, - prompt: str, - temperature: float | NotGiven = NOT_GIVEN, - max_tokens: int | NotGiven = NOT_GIVEN, - stream: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Text generation.""" - return self._post( - "/inference/generate", - body={ - "model": model, - "prompt": prompt, - "temperature": temperature, - "max_tokens": max_tokens, - "stream": stream, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def embed( - self, - *, - model: str, - input: str | List[str], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Generate embeddings.""" - return self._post( - "/inference/embed", - body={"model": model, "input": input}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def api( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get OpenAI-compatible API endpoint info.""" - return self._get( - "/inference/api", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def bench( - self, - *, - model: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Benchmark model performance.""" - return self._post( - "/inference/bench", - body={"model": model}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def ps( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List running inference processes.""" - return self._get( - "/inference/ps", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncInferenceResource(AsyncAPIResource): - """Local AI runtime and inference service (Ollama-like).""" - - @cached_property - def with_raw_response(self) -> AsyncInferenceResourceWithRawResponse: - return AsyncInferenceResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncInferenceResourceWithStreamingResponse: - return AsyncInferenceResourceWithStreamingResponse(self) - - async def start( - self, - *, - model: str | NotGiven = NOT_GIVEN, - port: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Start inference server.""" - return await self._post( - "/inference/start", - body={"model": model, "port": port}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stop( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Stop inference server.""" - return await self._post( - "/inference/stop", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def status( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get inference server status.""" - return await self._get( - "/inference/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def logs( - self, - *, - follow: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get inference server logs.""" - return await self._get( - "/inference/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"follow": follow}, - ), - cast_to=object, - ) - - async def chat( - self, - *, - model: str, - messages: List[Dict[str, str]], - temperature: float | NotGiven = NOT_GIVEN, - max_tokens: int | NotGiven = NOT_GIVEN, - stream: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Chat completion.""" - return await self._post( - "/inference/chat", - body={ - "model": model, - "messages": messages, - "temperature": temperature, - "max_tokens": max_tokens, - "stream": stream, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def generate( - self, - *, - model: str, - prompt: str, - temperature: float | NotGiven = NOT_GIVEN, - max_tokens: int | NotGiven = NOT_GIVEN, - stream: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Text generation.""" - return await self._post( - "/inference/generate", - body={ - "model": model, - "prompt": prompt, - "temperature": temperature, - "max_tokens": max_tokens, - "stream": stream, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def embed( - self, - *, - model: str, - input: str | List[str], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Generate embeddings.""" - return await self._post( - "/inference/embed", - body={"model": model, "input": input}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def api( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get OpenAI-compatible API endpoint info.""" - return await self._get( - "/inference/api", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def bench( - self, - *, - model: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Benchmark model performance.""" - return await self._post( - "/inference/bench", - body={"model": model}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def ps( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List running inference processes.""" - return await self._get( - "/inference/ps", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class InferenceResourceWithRawResponse: - def __init__(self, inference: InferenceResource) -> None: - self._inference = inference - - -class AsyncInferenceResourceWithRawResponse: - def __init__(self, inference: AsyncInferenceResource) -> None: - self._inference = inference - - -class InferenceResourceWithStreamingResponse: - def __init__(self, inference: InferenceResource) -> None: - self._inference = inference - - -class AsyncInferenceResourceWithStreamingResponse: - def __init__(self, inference: AsyncInferenceResource) -> None: - self._inference = inference diff --git a/pkg/hanzoai/resources/ingress.py b/pkg/hanzoai/resources/ingress.py deleted file mode 100644 index abc677fa3..000000000 --- a/pkg/hanzoai/resources/ingress.py +++ /dev/null @@ -1,840 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List, Optional - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["IngressResource", "AsyncIngressResource"] - - -class IngressResource(SyncAPIResource): - """Ingress resource โ€” Traefik reverse proxy inspection + PaaS domain management.""" - - @cached_property - def with_raw_response(self) -> IngressResourceWithRawResponse: - return IngressResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> IngressResourceWithStreamingResponse: - return IngressResourceWithStreamingResponse(self) - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Traefik API โ€” HTTP routers - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_routers( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all HTTP routers (Traefik ingress rules).""" - return self._get( - "/ingress/routers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_router( - self, - name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific HTTP router by name (e.g. 'my-router@docker').""" - return self._get( - f"/ingress/routers/{name}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Traefik API โ€” HTTP services - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_services( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all backend services registered in Traefik.""" - return self._get( - "/ingress/services", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_service( - self, - name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific backend service by name (e.g. 'my-svc@docker').""" - return self._get( - f"/ingress/services/{name}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Traefik API โ€” middlewares - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_middlewares( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all middlewares (rate limiting, auth, headers, etc.).""" - return self._get( - "/ingress/middlewares", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_middleware( - self, - name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific middleware by name (e.g. 'rate-limit@docker').""" - return self._get( - f"/ingress/middlewares/{name}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Traefik API โ€” entrypoints & overview - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_entrypoints( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all entrypoints (ports/protocols Traefik listens on).""" - return self._get( - "/ingress/entrypoints", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def overview( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get Traefik dashboard overview stats (router/service/middleware counts).""" - return self._get( - "/ingress/overview", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Traefik API โ€” TCP routers & services - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_tcp_routers( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all TCP routers.""" - return self._get( - "/ingress/tcp/routers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_tcp_services( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all TCP services.""" - return self._get( - "/ingress/tcp/services", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # PaaS Domain Management (tRPC proxy) - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_domains( - self, - *, - project_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List custom domains for a project.""" - return self._get( - "/ingress/domains", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"project_id": project_id}, - ), - cast_to=object, - ) - - def add_domain( - self, - *, - project_id: str, - domain: str, - certificate_id: str | NotGiven = NOT_GIVEN, - force_ssl: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a custom domain to a project (creates DNS + TLS cert).""" - return self._post( - "/ingress/domains", - body={ - "project_id": project_id, - "domain": domain, - "certificate_id": certificate_id, - "force_ssl": force_ssl, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def remove_domain( - self, - domain: str, - *, - project_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove a custom domain from a project.""" - return self._delete( - f"/ingress/domains/{domain}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"project_id": project_id}, - ), - cast_to=object, - ) - - def verify_domain( - self, - domain: str, - *, - project_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Verify domain DNS configuration.""" - return self._post( - f"/ingress/domains/{domain}/verify", - body={"project_id": project_id}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def tls_status( - self, - domain: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get TLS certificate status for a domain.""" - return self._get( - f"/ingress/domains/{domain}/tls", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncIngressResource(AsyncAPIResource): - """Ingress resource โ€” Traefik reverse proxy inspection + PaaS domain management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncIngressResourceWithRawResponse: - return AsyncIngressResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncIngressResourceWithStreamingResponse: - return AsyncIngressResourceWithStreamingResponse(self) - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Traefik API โ€” HTTP routers - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_routers( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all HTTP routers (Traefik ingress rules).""" - return await self._get( - "/ingress/routers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_router( - self, - name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific HTTP router by name (e.g. 'my-router@docker').""" - return await self._get( - f"/ingress/routers/{name}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Traefik API โ€” HTTP services - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_services( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all backend services registered in Traefik.""" - return await self._get( - "/ingress/services", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_service( - self, - name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific backend service by name (e.g. 'my-svc@docker').""" - return await self._get( - f"/ingress/services/{name}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Traefik API โ€” middlewares - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_middlewares( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all middlewares (rate limiting, auth, headers, etc.).""" - return await self._get( - "/ingress/middlewares", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_middleware( - self, - name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific middleware by name (e.g. 'rate-limit@docker').""" - return await self._get( - f"/ingress/middlewares/{name}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Traefik API โ€” entrypoints & overview - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_entrypoints( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all entrypoints (ports/protocols Traefik listens on).""" - return await self._get( - "/ingress/entrypoints", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def overview( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get Traefik dashboard overview stats (router/service/middleware counts).""" - return await self._get( - "/ingress/overview", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # Traefik API โ€” TCP routers & services - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_tcp_routers( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all TCP routers.""" - return await self._get( - "/ingress/tcp/routers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_tcp_services( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all TCP services.""" - return await self._get( - "/ingress/tcp/services", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # PaaS Domain Management (tRPC proxy) - # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_domains( - self, - *, - project_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List custom domains for a project.""" - return await self._get( - "/ingress/domains", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"project_id": project_id}, - ), - cast_to=object, - ) - - async def add_domain( - self, - *, - project_id: str, - domain: str, - certificate_id: str | NotGiven = NOT_GIVEN, - force_ssl: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a custom domain to a project (creates DNS + TLS cert).""" - return await self._post( - "/ingress/domains", - body={ - "project_id": project_id, - "domain": domain, - "certificate_id": certificate_id, - "force_ssl": force_ssl, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def remove_domain( - self, - domain: str, - *, - project_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove a custom domain from a project.""" - return await self._delete( - f"/ingress/domains/{domain}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"project_id": project_id}, - ), - cast_to=object, - ) - - async def verify_domain( - self, - domain: str, - *, - project_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Verify domain DNS configuration.""" - return await self._post( - f"/ingress/domains/{domain}/verify", - body={"project_id": project_id}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def tls_status( - self, - domain: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get TLS certificate status for a domain.""" - return await self._get( - f"/ingress/domains/{domain}/tls", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class IngressResourceWithRawResponse: - def __init__(self, ingress: IngressResource) -> None: - self._ingress = ingress - # Traefik โ€” routers - self.list_routers = to_raw_response_wrapper(ingress.list_routers) - self.retrieve_router = to_raw_response_wrapper(ingress.retrieve_router) - # Traefik โ€” services - self.list_services = to_raw_response_wrapper(ingress.list_services) - self.retrieve_service = to_raw_response_wrapper(ingress.retrieve_service) - # Traefik โ€” middlewares - self.list_middlewares = to_raw_response_wrapper(ingress.list_middlewares) - self.retrieve_middleware = to_raw_response_wrapper(ingress.retrieve_middleware) - # Traefik โ€” entrypoints & overview - self.list_entrypoints = to_raw_response_wrapper(ingress.list_entrypoints) - self.overview = to_raw_response_wrapper(ingress.overview) - # Traefik โ€” TCP - self.list_tcp_routers = to_raw_response_wrapper(ingress.list_tcp_routers) - self.list_tcp_services = to_raw_response_wrapper(ingress.list_tcp_services) - # PaaS domains - self.list_domains = to_raw_response_wrapper(ingress.list_domains) - self.add_domain = to_raw_response_wrapper(ingress.add_domain) - self.remove_domain = to_raw_response_wrapper(ingress.remove_domain) - self.verify_domain = to_raw_response_wrapper(ingress.verify_domain) - self.tls_status = to_raw_response_wrapper(ingress.tls_status) - - -class AsyncIngressResourceWithRawResponse: - def __init__(self, ingress: AsyncIngressResource) -> None: - self._ingress = ingress - # Traefik โ€” routers - self.list_routers = async_to_raw_response_wrapper(ingress.list_routers) - self.retrieve_router = async_to_raw_response_wrapper(ingress.retrieve_router) - # Traefik โ€” services - self.list_services = async_to_raw_response_wrapper(ingress.list_services) - self.retrieve_service = async_to_raw_response_wrapper(ingress.retrieve_service) - # Traefik โ€” middlewares - self.list_middlewares = async_to_raw_response_wrapper(ingress.list_middlewares) - self.retrieve_middleware = async_to_raw_response_wrapper(ingress.retrieve_middleware) - # Traefik โ€” entrypoints & overview - self.list_entrypoints = async_to_raw_response_wrapper(ingress.list_entrypoints) - self.overview = async_to_raw_response_wrapper(ingress.overview) - # Traefik โ€” TCP - self.list_tcp_routers = async_to_raw_response_wrapper(ingress.list_tcp_routers) - self.list_tcp_services = async_to_raw_response_wrapper(ingress.list_tcp_services) - # PaaS domains - self.list_domains = async_to_raw_response_wrapper(ingress.list_domains) - self.add_domain = async_to_raw_response_wrapper(ingress.add_domain) - self.remove_domain = async_to_raw_response_wrapper(ingress.remove_domain) - self.verify_domain = async_to_raw_response_wrapper(ingress.verify_domain) - self.tls_status = async_to_raw_response_wrapper(ingress.tls_status) - - -class IngressResourceWithStreamingResponse: - def __init__(self, ingress: IngressResource) -> None: - self._ingress = ingress - # Traefik โ€” routers - self.list_routers = to_streamed_response_wrapper(ingress.list_routers) - self.retrieve_router = to_streamed_response_wrapper(ingress.retrieve_router) - # Traefik โ€” services - self.list_services = to_streamed_response_wrapper(ingress.list_services) - self.retrieve_service = to_streamed_response_wrapper(ingress.retrieve_service) - # Traefik โ€” middlewares - self.list_middlewares = to_streamed_response_wrapper(ingress.list_middlewares) - self.retrieve_middleware = to_streamed_response_wrapper(ingress.retrieve_middleware) - # Traefik โ€” entrypoints & overview - self.list_entrypoints = to_streamed_response_wrapper(ingress.list_entrypoints) - self.overview = to_streamed_response_wrapper(ingress.overview) - # Traefik โ€” TCP - self.list_tcp_routers = to_streamed_response_wrapper(ingress.list_tcp_routers) - self.list_tcp_services = to_streamed_response_wrapper(ingress.list_tcp_services) - # PaaS domains - self.list_domains = to_streamed_response_wrapper(ingress.list_domains) - self.add_domain = to_streamed_response_wrapper(ingress.add_domain) - self.remove_domain = to_streamed_response_wrapper(ingress.remove_domain) - self.verify_domain = to_streamed_response_wrapper(ingress.verify_domain) - self.tls_status = to_streamed_response_wrapper(ingress.tls_status) - - -class AsyncIngressResourceWithStreamingResponse: - def __init__(self, ingress: AsyncIngressResource) -> None: - self._ingress = ingress - # Traefik โ€” routers - self.list_routers = async_to_streamed_response_wrapper(ingress.list_routers) - self.retrieve_router = async_to_streamed_response_wrapper(ingress.retrieve_router) - # Traefik โ€” services - self.list_services = async_to_streamed_response_wrapper(ingress.list_services) - self.retrieve_service = async_to_streamed_response_wrapper(ingress.retrieve_service) - # Traefik โ€” middlewares - self.list_middlewares = async_to_streamed_response_wrapper(ingress.list_middlewares) - self.retrieve_middleware = async_to_streamed_response_wrapper(ingress.retrieve_middleware) - # Traefik โ€” entrypoints & overview - self.list_entrypoints = async_to_streamed_response_wrapper(ingress.list_entrypoints) - self.overview = async_to_streamed_response_wrapper(ingress.overview) - # Traefik โ€” TCP - self.list_tcp_routers = async_to_streamed_response_wrapper(ingress.list_tcp_routers) - self.list_tcp_services = async_to_streamed_response_wrapper(ingress.list_tcp_services) - # PaaS domains - self.list_domains = async_to_streamed_response_wrapper(ingress.list_domains) - self.add_domain = async_to_streamed_response_wrapper(ingress.add_domain) - self.remove_domain = async_to_streamed_response_wrapper(ingress.remove_domain) - self.verify_domain = async_to_streamed_response_wrapper(ingress.verify_domain) - self.tls_status = async_to_streamed_response_wrapper(ingress.tls_status) diff --git a/pkg/hanzoai/resources/jobs.py b/pkg/hanzoai/resources/jobs.py deleted file mode 100644 index 02ecab351..000000000 --- a/pkg/hanzoai/resources/jobs.py +++ /dev/null @@ -1,534 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, Optional - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["JobsResource", "AsyncJobsResource"] - - -class JobsResource(SyncAPIResource): - """Background job management.""" - - @cached_property - def with_raw_response(self) -> JobsResourceWithRawResponse: - return JobsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> JobsResourceWithStreamingResponse: - return JobsResourceWithStreamingResponse(self) - - def list( - self, - *, - status: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all jobs.""" - return self._get( - "/jobs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"status": status, "limit": limit}, - ), - cast_to=object, - ) - - def get( - self, - job_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific job.""" - return self._get( - f"/jobs/{job_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - handler: str, - payload: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new background job.""" - return self._post( - "/jobs", - body={"name": name, "handler": handler, "payload": payload}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def cancel( - self, - job_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Cancel a running job.""" - return self._post( - f"/jobs/{job_id}/cancel", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retry( - self, - job_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Retry a failed job.""" - return self._post( - f"/jobs/{job_id}/retry", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def logs( - self, - job_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get job logs.""" - return self._get( - f"/jobs/{job_id}/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stats( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get job statistics.""" - return self._get( - "/jobs/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_schedules( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List job schedules.""" - return self._get( - "/jobs/schedules", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_schedule( - self, - *, - cron: str, - job_config: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a job schedule.""" - return self._post( - "/jobs/schedules", - body={"cron": cron, "job_config": job_config}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_schedule( - self, - schedule_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a job schedule.""" - return self._delete( - f"/jobs/schedules/{schedule_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncJobsResource(AsyncAPIResource): - """Background job management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncJobsResourceWithRawResponse: - return AsyncJobsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncJobsResourceWithStreamingResponse: - return AsyncJobsResourceWithStreamingResponse(self) - - async def list( - self, - *, - status: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all jobs.""" - return await self._get( - "/jobs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"status": status, "limit": limit}, - ), - cast_to=object, - ) - - async def get( - self, - job_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific job.""" - return await self._get( - f"/jobs/{job_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - handler: str, - payload: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new background job.""" - return await self._post( - "/jobs", - body={"name": name, "handler": handler, "payload": payload}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def cancel( - self, - job_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Cancel a running job.""" - return await self._post( - f"/jobs/{job_id}/cancel", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retry( - self, - job_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Retry a failed job.""" - return await self._post( - f"/jobs/{job_id}/retry", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def logs( - self, - job_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get job logs.""" - return await self._get( - f"/jobs/{job_id}/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stats( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get job statistics.""" - return await self._get( - "/jobs/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_schedules( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List job schedules.""" - return await self._get( - "/jobs/schedules", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_schedule( - self, - *, - cron: str, - job_config: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a job schedule.""" - return await self._post( - "/jobs/schedules", - body={"cron": cron, "job_config": job_config}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_schedule( - self, - schedule_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a job schedule.""" - return await self._delete( - f"/jobs/schedules/{schedule_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class JobsResourceWithRawResponse: - def __init__(self, jobs: JobsResource) -> None: - self._jobs = jobs - self.list = to_raw_response_wrapper(jobs.list) - self.get = to_raw_response_wrapper(jobs.get) - self.create = to_raw_response_wrapper(jobs.create) - self.cancel = to_raw_response_wrapper(jobs.cancel) - self.retry = to_raw_response_wrapper(jobs.retry) - self.logs = to_raw_response_wrapper(jobs.logs) - self.stats = to_raw_response_wrapper(jobs.stats) - self.list_schedules = to_raw_response_wrapper(jobs.list_schedules) - self.create_schedule = to_raw_response_wrapper(jobs.create_schedule) - self.delete_schedule = to_raw_response_wrapper(jobs.delete_schedule) - - -class AsyncJobsResourceWithRawResponse: - def __init__(self, jobs: AsyncJobsResource) -> None: - self._jobs = jobs - self.list = async_to_raw_response_wrapper(jobs.list) - self.get = async_to_raw_response_wrapper(jobs.get) - self.create = async_to_raw_response_wrapper(jobs.create) - self.cancel = async_to_raw_response_wrapper(jobs.cancel) - self.retry = async_to_raw_response_wrapper(jobs.retry) - self.logs = async_to_raw_response_wrapper(jobs.logs) - self.stats = async_to_raw_response_wrapper(jobs.stats) - self.list_schedules = async_to_raw_response_wrapper(jobs.list_schedules) - self.create_schedule = async_to_raw_response_wrapper(jobs.create_schedule) - self.delete_schedule = async_to_raw_response_wrapper(jobs.delete_schedule) - - -class JobsResourceWithStreamingResponse: - def __init__(self, jobs: JobsResource) -> None: - self._jobs = jobs - self.list = to_streamed_response_wrapper(jobs.list) - self.get = to_streamed_response_wrapper(jobs.get) - self.create = to_streamed_response_wrapper(jobs.create) - self.cancel = to_streamed_response_wrapper(jobs.cancel) - self.retry = to_streamed_response_wrapper(jobs.retry) - self.logs = to_streamed_response_wrapper(jobs.logs) - self.stats = to_streamed_response_wrapper(jobs.stats) - self.list_schedules = to_streamed_response_wrapper(jobs.list_schedules) - self.create_schedule = to_streamed_response_wrapper(jobs.create_schedule) - self.delete_schedule = to_streamed_response_wrapper(jobs.delete_schedule) - - -class AsyncJobsResourceWithStreamingResponse: - def __init__(self, jobs: AsyncJobsResource) -> None: - self._jobs = jobs - self.list = async_to_streamed_response_wrapper(jobs.list) - self.get = async_to_streamed_response_wrapper(jobs.get) - self.create = async_to_streamed_response_wrapper(jobs.create) - self.cancel = async_to_streamed_response_wrapper(jobs.cancel) - self.retry = async_to_streamed_response_wrapper(jobs.retry) - self.logs = async_to_streamed_response_wrapper(jobs.logs) - self.stats = async_to_streamed_response_wrapper(jobs.stats) - self.list_schedules = async_to_streamed_response_wrapper(jobs.list_schedules) - self.create_schedule = async_to_streamed_response_wrapper(jobs.create_schedule) - self.delete_schedule = async_to_streamed_response_wrapper(jobs.delete_schedule) diff --git a/pkg/hanzoai/resources/key/__init__.py b/pkg/hanzoai/resources/key/__init__.py deleted file mode 100644 index 4fe13666b..000000000 --- a/pkg/hanzoai/resources/key/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Hanzo AI SDK - -from .key import ( - KeyResource, - AsyncKeyResource, - KeyResourceWithRawResponse, - AsyncKeyResourceWithRawResponse, - KeyResourceWithStreamingResponse, - AsyncKeyResourceWithStreamingResponse, -) - -__all__ = [ - "KeyResource", - "AsyncKeyResource", - "KeyResourceWithRawResponse", - "AsyncKeyResourceWithRawResponse", - "KeyResourceWithStreamingResponse", - "AsyncKeyResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/key/key.py b/pkg/hanzoai/resources/key/key.py deleted file mode 100644 index c7aed831b..000000000 --- a/pkg/hanzoai/resources/key/key.py +++ /dev/null @@ -1,1980 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List, Union, Iterable, Optional -from datetime import datetime - -import httpx - -from ...types import ( - key_list_params, - key_block_params, - key_delete_params, - key_update_params, - key_unblock_params, - key_generate_params, - key_retrieve_info_params, - key_regenerate_by_key_params, -) -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - strip_not_given, - async_maybe_transform, -) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.key_list_response import KeyListResponse -from ...types.key_block_response import KeyBlockResponse -from ...types.generate_key_response import GenerateKeyResponse -from ...types.key_check_health_response import KeyCheckHealthResponse - -__all__ = ["KeyResource", "AsyncKeyResource"] - - -class KeyResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> KeyResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return KeyResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> KeyResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return KeyResourceWithStreamingResponse(self) - - def update( - self, - *, - key: str, - aliases: Optional[object] | NotGiven = NOT_GIVEN, - allowed_cache_controls: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - blocked: Optional[bool] | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - config: Optional[object] | NotGiven = NOT_GIVEN, - duration: Optional[str] | NotGiven = NOT_GIVEN, - enforced_params: Optional[List[str]] | NotGiven = NOT_GIVEN, - guardrails: Optional[List[str]] | NotGiven = NOT_GIVEN, - key_alias: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_max_budget: Optional[object] | NotGiven = NOT_GIVEN, - model_rpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - model_tpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - models: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - permissions: Optional[object] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - spend: Optional[float] | NotGiven = NOT_GIVEN, - tags: Optional[List[str]] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - temp_budget_expiry: Union[str, datetime, None] | NotGiven = NOT_GIVEN, - temp_budget_increase: Optional[float] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Update an existing API key's parameters. - - Parameters: - - - key: str - The key to update - - key_alias: Optional[str] - User-friendly key alias - - user_id: Optional[str] - User ID associated with key - - team_id: Optional[str] - Team ID associated with key - - budget_id: Optional[str] - The budget id associated with the key. Created by - calling `/budget/new`. - - models: Optional[list] - Model_name's a user is allowed to call - - tags: Optional[List[str]] - Tags for organizing keys (Enterprise only) - - enforced_params: Optional[List[str]] - List of enforced params for the key - (Enterprise only). - [Docs](https://docs.hanzo.ai/docs/proxy/enterprise#enforce-required-params-for-llm-requests) - - spend: Optional[float] - Amount spent by key - - max_budget: Optional[float] - Max budget for key - - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets - {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}} - - budget_duration: Optional[str] - Budget reset period ("30d", "1h", etc.) - - soft_budget: Optional[float] - [TODO] Soft budget limit (warning vs. hard - stop). Will trigger a slack alert when this soft budget is reached. - - max_parallel_requests: Optional[int] - Rate limit for parallel requests - - metadata: Optional[dict] - Metadata for key. Example {"team": "core-infra", - "app": "app2"} - - tpm_limit: Optional[int] - Tokens per minute limit - - rpm_limit: Optional[int] - Requests per minute limit - - model_rpm_limit: Optional[dict] - Model-specific RPM limits {"gpt-4": 100, - "claude-v1": 200} - - model_tpm_limit: Optional[dict] - Model-specific TPM limits {"gpt-4": 100000, - "claude-v1": 200000} - - allowed_cache_controls: Optional[list] - List of allowed cache control values - - duration: Optional[str] - Key validity duration ("30d", "1h", etc.) - - permissions: Optional[dict] - Key-specific permissions - - send_invite_email: Optional[bool] - Send invite email to user_id - - guardrails: Optional[List[str]] - List of active guardrails for the key - - blocked: Optional[bool] - Whether the key is blocked - - aliases: Optional[dict] - Model aliases for the key - - [Docs](https://hanzo.vercel.app/docs/proxy/virtual_keys#model-aliases) - - config: Optional[dict] - [DEPRECATED PARAM] Key-specific config. - - temp_budget_increase: Optional[float] - Temporary budget increase for the key - (Enterprise only). - - temp_budget_expiry: Optional[str] - Expiry time for the temporary budget - increase (Enterprise only). - - Example: - - ```bash - curl --location 'http://0.0.0.0:4000/key/update' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "key": "sk-1234", - "key_alias": "my-key", - "user_id": "user-1234", - "team_id": "team-1234", - "max_budget": 100, - "metadata": {"any_key": "any-val"}, - }' - ``` - - Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } - return self._post( - "/key/update", - body=maybe_transform( - { - "key": key, - "aliases": aliases, - "allowed_cache_controls": allowed_cache_controls, - "blocked": blocked, - "budget_duration": budget_duration, - "budget_id": budget_id, - "config": config, - "duration": duration, - "enforced_params": enforced_params, - "guardrails": guardrails, - "key_alias": key_alias, - "max_budget": max_budget, - "max_parallel_requests": max_parallel_requests, - "metadata": metadata, - "model_max_budget": model_max_budget, - "model_rpm_limit": model_rpm_limit, - "model_tpm_limit": model_tpm_limit, - "models": models, - "permissions": permissions, - "rpm_limit": rpm_limit, - "spend": spend, - "tags": tags, - "team_id": team_id, - "temp_budget_expiry": temp_budget_expiry, - "temp_budget_increase": temp_budget_increase, - "tpm_limit": tpm_limit, - "user_id": user_id, - }, - key_update_params.KeyUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list( - self, - *, - include_team_keys: bool | NotGiven = NOT_GIVEN, - key_alias: Optional[str] | NotGiven = NOT_GIVEN, - organization_id: Optional[str] | NotGiven = NOT_GIVEN, - page: int | NotGiven = NOT_GIVEN, - return_full_object: bool | NotGiven = NOT_GIVEN, - size: int | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> KeyListResponse: - """ - List all keys for a given user / team / organization. - - Returns: { "keys": List[str] or List[UserAPIKeyAuth], "total_count": int, - "current_page": int, "total_pages": int, } - - Args: - include_team_keys: Include all keys for teams that user is an admin of. - - key_alias: Filter keys by key alias - - organization_id: Filter keys by organization ID - - page: Page number - - return_full_object: Return full key object - - size: Page size - - team_id: Filter keys by team ID - - user_id: Filter keys by user ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/key/list", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "include_team_keys": include_team_keys, - "key_alias": key_alias, - "organization_id": organization_id, - "page": page, - "return_full_object": return_full_object, - "size": size, - "team_id": team_id, - "user_id": user_id, - }, - key_list_params.KeyListParams, - ), - ), - cast_to=KeyListResponse, - ) - - def delete( - self, - *, - key_aliases: Optional[List[str]] | NotGiven = NOT_GIVEN, - keys: Optional[List[str]] | NotGiven = NOT_GIVEN, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Delete a key from the key management system. - - Parameters:: - - - keys (List[str]): A list of keys or hashed keys to delete. Example {"keys": - ["sk-QWrxEynunsNpV1zT48HIrw", - "837e17519f44683334df5291321d97b8bf1098cd490e49e215f6fea935aa28be"]} - - key_aliases (List[str]): A list of key aliases to delete. Can be passed - instead of `keys`.Example {"key_aliases": ["alias1", "alias2"]} - - Returns: - - - deleted_keys (List[str]): A list of deleted keys. Example {"deleted_keys": - ["sk-QWrxEynunsNpV1zT48HIrw", - "837e17519f44683334df5291321d97b8bf1098cd490e49e215f6fea935aa28be"]} - - Example: - - ```bash - curl --location 'http://0.0.0.0:4000/key/delete' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "keys": ["sk-QWrxEynunsNpV1zT48HIrw"] - }' - ``` - - Raises: HTTPException: If an error occurs during key deletion. - - Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } - return self._post( - "/key/delete", - body=maybe_transform( - { - "key_aliases": key_aliases, - "keys": keys, - }, - key_delete_params.KeyDeleteParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def block( - self, - *, - key: str, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Optional[KeyBlockResponse]: - """ - Block an Virtual key from making any requests. - - Parameters: - - - key: str - The key to block. Can be either the unhashed key (sk-...) or the - hashed key value - - Example: - - ```bash - curl --location 'http://0.0.0.0:4000/key/block' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "key": "sk-Fn8Ej39NxjAXrvpUGKghGw" - }' - ``` - - Note: This is an admin-only endpoint. Only proxy admins can block keys. - - Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } - return self._post( - "/key/block", - body=maybe_transform({"key": key}, key_block_params.KeyBlockParams), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=KeyBlockResponse, - ) - - def check_health( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> KeyCheckHealthResponse: - """ - Check the health of the key - - Checks: - - - If key based logging is configured correctly - sends a test log - - Usage - - Pass the key in the request header - - ```bash - curl -X POST "http://localhost:4000/key/health" -H "Authorization: Bearer sk-1234" -H "Content-Type: application/json" - ``` - - Response when logging callbacks are setup correctly: - - ```json - { - "key": "healthy", - "logging_callbacks": { - "callbacks": ["gcs_bucket"], - "status": "healthy", - "details": "No logger exceptions triggered, system is healthy. Manually check if logs were sent to ['gcs_bucket']" - } - } - ``` - - Response when logging callbacks are not setup correctly: - - ```json - { - "key": "unhealthy", - "logging_callbacks": { - "callbacks": ["gcs_bucket"], - "status": "unhealthy", - "details": "Logger exceptions triggered, system is unhealthy: Failed to load vertex credentials. Check to see if credentials containing partial/invalid information." - } - } - ``` - """ - return self._post( - "/key/health", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=KeyCheckHealthResponse, - ) - - def generate( - self, - *, - aliases: Optional[object] | NotGiven = NOT_GIVEN, - allowed_cache_controls: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - blocked: Optional[bool] | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - config: Optional[object] | NotGiven = NOT_GIVEN, - duration: Optional[str] | NotGiven = NOT_GIVEN, - enforced_params: Optional[List[str]] | NotGiven = NOT_GIVEN, - guardrails: Optional[List[str]] | NotGiven = NOT_GIVEN, - key: Optional[str] | NotGiven = NOT_GIVEN, - key_alias: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_max_budget: Optional[object] | NotGiven = NOT_GIVEN, - model_rpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - model_tpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - models: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - permissions: Optional[object] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - send_invite_email: Optional[bool] | NotGiven = NOT_GIVEN, - soft_budget: Optional[float] | NotGiven = NOT_GIVEN, - spend: Optional[float] | NotGiven = NOT_GIVEN, - tags: Optional[List[str]] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> GenerateKeyResponse: - """ - Generate an API key based on the provided data. - - Docs: https://docs.hanzo.ai/docs/proxy/virtual_keys - - Parameters: - - - duration: Optional[str] - Specify the length of time the token is valid for. - You can set duration as seconds ("30s"), minutes ("30m"), hours ("30h"), days - ("30d"). - - key_alias: Optional[str] - User defined key alias - - key: Optional[str] - User defined key value. If not set, a 16-digit unique - sk-key is created for you. - - team_id: Optional[str] - The team id of the key - - user_id: Optional[str] - The user id of the key - - budget_id: Optional[str] - The budget id associated with the key. Created by - calling `/budget/new`. - - models: Optional[list] - Model_name's a user is allowed to call. (if empty, - key is allowed to call all models) - - aliases: Optional[dict] - Any alias mappings, on top of anything in the - config.yaml model list. - - https://docs.hanzo.ai/docs/proxy/virtual_keys#managing-auth---upgradedowngrade-models - - config: Optional[dict] - any key-specific configs, overrides config in - config.yaml - - spend: Optional[int] - Amount spent by key. Default is 0. Will be updated by - proxy whenever key is used. - https://docs.hanzo.ai/docs/proxy/virtual_keys#managing-auth---tracking-spend - - send_invite_email: Optional[bool] - Whether to send an invite email to the - user_id, with the generate key - - max_budget: Optional[float] - Specify max budget for a given key. - - budget_duration: Optional[str] - Budget is reset at the end of specified - duration. If not set, budget is never reset. You can set duration as seconds - ("30s"), minutes ("30m"), hours ("30h"), days ("30d"). - - max_parallel_requests: Optional[int] - Rate limit a user based on the number - of parallel requests. Raises 429 error, if user's parallel requests > x. - - metadata: Optional[dict] - Metadata for key, store information for key. - Example metadata = {"team": "core-infra", "app": "app2", "email": - "ishaan@berri.ai" } - - guardrails: Optional[List[str]] - List of active guardrails for the key - - permissions: Optional[dict] - key-specific permissions. Currently just used - for turning off pii masking (if connected). Example - {"pii": false} - - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets - {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}}}. IF null or {} then - no model specific budget. - - model_rpm_limit: Optional[dict] - key-specific model rpm limit. Example - - {"text-davinci-002": 1000, "gpt-3.5-turbo": 1000}. IF null or {} then no model - specific rpm limit. - - model_tpm_limit: Optional[dict] - key-specific model tpm limit. Example - - {"text-davinci-002": 1000, "gpt-3.5-turbo": 1000}. IF null or {} then no model - specific tpm limit. - - allowed_cache_controls: Optional[list] - List of allowed cache control values. - Example - ["no-cache", "no-store"]. See all values - - https://docs.hanzo.ai/docs/proxy/caching#turn-on--off-caching-per-request - - blocked: Optional[bool] - Whether the key is blocked. - - rpm_limit: Optional[int] - Specify rpm limit for a given key (Requests per - minute) - - tpm_limit: Optional[int] - Specify tpm limit for a given key (Tokens per - minute) - - soft_budget: Optional[float] - Specify soft budget for a given key. Will - trigger a slack alert when this soft budget is reached. - - tags: Optional[List[str]] - Tags for - [tracking spend](https://hanzo.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) - and/or doing - [tag-based routing](https://hanzo.vercel.app/docs/proxy/tag_routing). - - enforced_params: Optional[List[str]] - List of enforced params for the key - (Enterprise only). - [Docs](https://docs.hanzo.ai/docs/proxy/enterprise#enforce-required-params-for-llm-requests) - - Examples: - - 1. Allow users to turn on/off pii masking - - ```bash - curl --location 'http://0.0.0.0:4000/key/generate' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "permissions": {"allow_pii_controls": true} - }' - ``` - - Returns: - - - key: (str) The generated api key - - expires: (datetime) Datetime object for when key expires. - - user_id: (str) Unique user id - used for tracking spend across multiple keys - for same user id. - - Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } - return self._post( - "/key/generate", - body=maybe_transform( - { - "aliases": aliases, - "allowed_cache_controls": allowed_cache_controls, - "blocked": blocked, - "budget_duration": budget_duration, - "budget_id": budget_id, - "config": config, - "duration": duration, - "enforced_params": enforced_params, - "guardrails": guardrails, - "key": key, - "key_alias": key_alias, - "max_budget": max_budget, - "max_parallel_requests": max_parallel_requests, - "metadata": metadata, - "model_max_budget": model_max_budget, - "model_rpm_limit": model_rpm_limit, - "model_tpm_limit": model_tpm_limit, - "models": models, - "permissions": permissions, - "rpm_limit": rpm_limit, - "send_invite_email": send_invite_email, - "soft_budget": soft_budget, - "spend": spend, - "tags": tags, - "team_id": team_id, - "tpm_limit": tpm_limit, - "user_id": user_id, - }, - key_generate_params.KeyGenerateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=GenerateKeyResponse, - ) - - def regenerate_by_key( - self, - path_key: str, - *, - aliases: Optional[object] | NotGiven = NOT_GIVEN, - allowed_cache_controls: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - blocked: Optional[bool] | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - config: Optional[object] | NotGiven = NOT_GIVEN, - duration: Optional[str] | NotGiven = NOT_GIVEN, - enforced_params: Optional[List[str]] | NotGiven = NOT_GIVEN, - guardrails: Optional[List[str]] | NotGiven = NOT_GIVEN, - body_key: Optional[str] | NotGiven = NOT_GIVEN, - key_alias: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_max_budget: Optional[object] | NotGiven = NOT_GIVEN, - model_rpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - model_tpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - models: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - new_master_key: Optional[str] | NotGiven = NOT_GIVEN, - permissions: Optional[object] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - send_invite_email: Optional[bool] | NotGiven = NOT_GIVEN, - soft_budget: Optional[float] | NotGiven = NOT_GIVEN, - spend: Optional[float] | NotGiven = NOT_GIVEN, - tags: Optional[List[str]] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Optional[GenerateKeyResponse]: - """ - Regenerate an existing API key while optionally updating its parameters. - - Parameters: - - - key: str (path parameter) - The key to regenerate - - data: Optional[RegenerateKeyRequest] - Request body containing optional - parameters to update - - key_alias: Optional[str] - User-friendly key alias - - user_id: Optional[str] - User ID associated with key - - team_id: Optional[str] - Team ID associated with key - - models: Optional[list] - Model_name's a user is allowed to call - - tags: Optional[List[str]] - Tags for organizing keys (Enterprise only) - - spend: Optional[float] - Amount spent by key - - max_budget: Optional[float] - Max budget for key - - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets - {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}} - - budget_duration: Optional[str] - Budget reset period ("30d", "1h", etc.) - - soft_budget: Optional[float] - Soft budget limit (warning vs. hard stop). - Will trigger a slack alert when this soft budget is reached. - - max_parallel_requests: Optional[int] - Rate limit for parallel requests - - metadata: Optional[dict] - Metadata for key. Example {"team": "core-infra", - "app": "app2"} - - tpm_limit: Optional[int] - Tokens per minute limit - - rpm_limit: Optional[int] - Requests per minute limit - - model_rpm_limit: Optional[dict] - Model-specific RPM limits {"gpt-4": 100, - "claude-v1": 200} - - model_tpm_limit: Optional[dict] - Model-specific TPM limits {"gpt-4": - 100000, "claude-v1": 200000} - - allowed_cache_controls: Optional[list] - List of allowed cache control - values - - duration: Optional[str] - Key validity duration ("30d", "1h", etc.) - - permissions: Optional[dict] - Key-specific permissions - - guardrails: Optional[List[str]] - List of active guardrails for the key - - blocked: Optional[bool] - Whether the key is blocked - - Returns: - - - GenerateKeyResponse containing the new key and its updated parameters - - Example: - - ```bash - curl --location --request POST 'http://localhost:4000/key/sk-1234/regenerate' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data-raw '{ - "max_budget": 100, - "metadata": {"team": "core-infra"}, - "models": ["gpt-4", "gpt-3.5-turbo"] - }' - ``` - - Note: This is an Enterprise feature. It requires a premium license to use. - - Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not path_key: - raise ValueError( - f"Expected a non-empty value for `path_key` but received {path_key!r}" - ) - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } - return self._post( - f"/key/{path_key}/regenerate", - body=maybe_transform( - { - "aliases": aliases, - "allowed_cache_controls": allowed_cache_controls, - "blocked": blocked, - "budget_duration": budget_duration, - "budget_id": budget_id, - "config": config, - "duration": duration, - "enforced_params": enforced_params, - "guardrails": guardrails, - "body_key": body_key, - "key_alias": key_alias, - "max_budget": max_budget, - "max_parallel_requests": max_parallel_requests, - "metadata": metadata, - "model_max_budget": model_max_budget, - "model_rpm_limit": model_rpm_limit, - "model_tpm_limit": model_tpm_limit, - "models": models, - "new_master_key": new_master_key, - "permissions": permissions, - "rpm_limit": rpm_limit, - "send_invite_email": send_invite_email, - "soft_budget": soft_budget, - "spend": spend, - "tags": tags, - "team_id": team_id, - "tpm_limit": tpm_limit, - "user_id": user_id, - }, - key_regenerate_by_key_params.KeyRegenerateByKeyParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=GenerateKeyResponse, - ) - - def retrieve_info( - self, - *, - key: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Retrieve information about a key. - - Parameters: key: Optional[str] = Query - parameter representing the key in the request user_api_key_dict: UserAPIKeyAuth - = Dependency representing the user's API key Returns: Dict containing the key - and its associated information - - Example Curl: - - ``` - curl -X GET "http://0.0.0.0:4000/key/info?key=sk-02Wr4IAlN3NvPXvL5JVvDA" -H "Authorization: Bearer sk-1234" - ``` - - Example Curl - if no key is passed, it will use the Key Passed in Authorization - Header - - ``` - curl -X GET "http://0.0.0.0:4000/key/info" -H "Authorization: Bearer sk-02Wr4IAlN3NvPXvL5JVvDA" - ``` - - Args: - key: Key in the request parameters - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/key/info", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"key": key}, key_retrieve_info_params.KeyRetrieveInfoParams - ), - ), - cast_to=object, - ) - - def unblock( - self, - *, - key: str, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Unblock a Virtual key to allow it to make requests again. - - Parameters: - - - key: str - The key to unblock. Can be either the unhashed key (sk-...) or the - hashed key value - - Example: - - ```bash - curl --location 'http://0.0.0.0:4000/key/unblock' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "key": "sk-Fn8Ej39NxjAXrvpUGKghGw" - }' - ``` - - Note: This is an admin-only endpoint. Only proxy admins can unblock keys. - - Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } - return self._post( - "/key/unblock", - body=maybe_transform({"key": key}, key_unblock_params.KeyUnblockParams), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncKeyResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncKeyResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncKeyResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncKeyResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncKeyResourceWithStreamingResponse(self) - - async def update( - self, - *, - key: str, - aliases: Optional[object] | NotGiven = NOT_GIVEN, - allowed_cache_controls: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - blocked: Optional[bool] | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - config: Optional[object] | NotGiven = NOT_GIVEN, - duration: Optional[str] | NotGiven = NOT_GIVEN, - enforced_params: Optional[List[str]] | NotGiven = NOT_GIVEN, - guardrails: Optional[List[str]] | NotGiven = NOT_GIVEN, - key_alias: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_max_budget: Optional[object] | NotGiven = NOT_GIVEN, - model_rpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - model_tpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - models: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - permissions: Optional[object] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - spend: Optional[float] | NotGiven = NOT_GIVEN, - tags: Optional[List[str]] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - temp_budget_expiry: Union[str, datetime, None] | NotGiven = NOT_GIVEN, - temp_budget_increase: Optional[float] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Update an existing API key's parameters. - - Parameters: - - - key: str - The key to update - - key_alias: Optional[str] - User-friendly key alias - - user_id: Optional[str] - User ID associated with key - - team_id: Optional[str] - Team ID associated with key - - budget_id: Optional[str] - The budget id associated with the key. Created by - calling `/budget/new`. - - models: Optional[list] - Model_name's a user is allowed to call - - tags: Optional[List[str]] - Tags for organizing keys (Enterprise only) - - enforced_params: Optional[List[str]] - List of enforced params for the key - (Enterprise only). - [Docs](https://docs.hanzo.ai/docs/proxy/enterprise#enforce-required-params-for-llm-requests) - - spend: Optional[float] - Amount spent by key - - max_budget: Optional[float] - Max budget for key - - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets - {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}} - - budget_duration: Optional[str] - Budget reset period ("30d", "1h", etc.) - - soft_budget: Optional[float] - [TODO] Soft budget limit (warning vs. hard - stop). Will trigger a slack alert when this soft budget is reached. - - max_parallel_requests: Optional[int] - Rate limit for parallel requests - - metadata: Optional[dict] - Metadata for key. Example {"team": "core-infra", - "app": "app2"} - - tpm_limit: Optional[int] - Tokens per minute limit - - rpm_limit: Optional[int] - Requests per minute limit - - model_rpm_limit: Optional[dict] - Model-specific RPM limits {"gpt-4": 100, - "claude-v1": 200} - - model_tpm_limit: Optional[dict] - Model-specific TPM limits {"gpt-4": 100000, - "claude-v1": 200000} - - allowed_cache_controls: Optional[list] - List of allowed cache control values - - duration: Optional[str] - Key validity duration ("30d", "1h", etc.) - - permissions: Optional[dict] - Key-specific permissions - - send_invite_email: Optional[bool] - Send invite email to user_id - - guardrails: Optional[List[str]] - List of active guardrails for the key - - blocked: Optional[bool] - Whether the key is blocked - - aliases: Optional[dict] - Model aliases for the key - - [Docs](https://hanzo.vercel.app/docs/proxy/virtual_keys#model-aliases) - - config: Optional[dict] - [DEPRECATED PARAM] Key-specific config. - - temp_budget_increase: Optional[float] - Temporary budget increase for the key - (Enterprise only). - - temp_budget_expiry: Optional[str] - Expiry time for the temporary budget - increase (Enterprise only). - - Example: - - ```bash - curl --location 'http://0.0.0.0:4000/key/update' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "key": "sk-1234", - "key_alias": "my-key", - "user_id": "user-1234", - "team_id": "team-1234", - "max_budget": 100, - "metadata": {"any_key": "any-val"}, - }' - ``` - - Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } - return await self._post( - "/key/update", - body=await async_maybe_transform( - { - "key": key, - "aliases": aliases, - "allowed_cache_controls": allowed_cache_controls, - "blocked": blocked, - "budget_duration": budget_duration, - "budget_id": budget_id, - "config": config, - "duration": duration, - "enforced_params": enforced_params, - "guardrails": guardrails, - "key_alias": key_alias, - "max_budget": max_budget, - "max_parallel_requests": max_parallel_requests, - "metadata": metadata, - "model_max_budget": model_max_budget, - "model_rpm_limit": model_rpm_limit, - "model_tpm_limit": model_tpm_limit, - "models": models, - "permissions": permissions, - "rpm_limit": rpm_limit, - "spend": spend, - "tags": tags, - "team_id": team_id, - "temp_budget_expiry": temp_budget_expiry, - "temp_budget_increase": temp_budget_increase, - "tpm_limit": tpm_limit, - "user_id": user_id, - }, - key_update_params.KeyUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list( - self, - *, - include_team_keys: bool | NotGiven = NOT_GIVEN, - key_alias: Optional[str] | NotGiven = NOT_GIVEN, - organization_id: Optional[str] | NotGiven = NOT_GIVEN, - page: int | NotGiven = NOT_GIVEN, - return_full_object: bool | NotGiven = NOT_GIVEN, - size: int | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> KeyListResponse: - """ - List all keys for a given user / team / organization. - - Returns: { "keys": List[str] or List[UserAPIKeyAuth], "total_count": int, - "current_page": int, "total_pages": int, } - - Args: - include_team_keys: Include all keys for teams that user is an admin of. - - key_alias: Filter keys by key alias - - organization_id: Filter keys by organization ID - - page: Page number - - return_full_object: Return full key object - - size: Page size - - team_id: Filter keys by team ID - - user_id: Filter keys by user ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/key/list", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "include_team_keys": include_team_keys, - "key_alias": key_alias, - "organization_id": organization_id, - "page": page, - "return_full_object": return_full_object, - "size": size, - "team_id": team_id, - "user_id": user_id, - }, - key_list_params.KeyListParams, - ), - ), - cast_to=KeyListResponse, - ) - - async def delete( - self, - *, - key_aliases: Optional[List[str]] | NotGiven = NOT_GIVEN, - keys: Optional[List[str]] | NotGiven = NOT_GIVEN, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Delete a key from the key management system. - - Parameters:: - - - keys (List[str]): A list of keys or hashed keys to delete. Example {"keys": - ["sk-QWrxEynunsNpV1zT48HIrw", - "837e17519f44683334df5291321d97b8bf1098cd490e49e215f6fea935aa28be"]} - - key_aliases (List[str]): A list of key aliases to delete. Can be passed - instead of `keys`.Example {"key_aliases": ["alias1", "alias2"]} - - Returns: - - - deleted_keys (List[str]): A list of deleted keys. Example {"deleted_keys": - ["sk-QWrxEynunsNpV1zT48HIrw", - "837e17519f44683334df5291321d97b8bf1098cd490e49e215f6fea935aa28be"]} - - Example: - - ```bash - curl --location 'http://0.0.0.0:4000/key/delete' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "keys": ["sk-QWrxEynunsNpV1zT48HIrw"] - }' - ``` - - Raises: HTTPException: If an error occurs during key deletion. - - Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } - return await self._post( - "/key/delete", - body=await async_maybe_transform( - { - "key_aliases": key_aliases, - "keys": keys, - }, - key_delete_params.KeyDeleteParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def block( - self, - *, - key: str, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Optional[KeyBlockResponse]: - """ - Block an Virtual key from making any requests. - - Parameters: - - - key: str - The key to block. Can be either the unhashed key (sk-...) or the - hashed key value - - Example: - - ```bash - curl --location 'http://0.0.0.0:4000/key/block' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "key": "sk-Fn8Ej39NxjAXrvpUGKghGw" - }' - ``` - - Note: This is an admin-only endpoint. Only proxy admins can block keys. - - Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } - return await self._post( - "/key/block", - body=await async_maybe_transform( - {"key": key}, key_block_params.KeyBlockParams - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=KeyBlockResponse, - ) - - async def check_health( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> KeyCheckHealthResponse: - """ - Check the health of the key - - Checks: - - - If key based logging is configured correctly - sends a test log - - Usage - - Pass the key in the request header - - ```bash - curl -X POST "http://localhost:4000/key/health" -H "Authorization: Bearer sk-1234" -H "Content-Type: application/json" - ``` - - Response when logging callbacks are setup correctly: - - ```json - { - "key": "healthy", - "logging_callbacks": { - "callbacks": ["gcs_bucket"], - "status": "healthy", - "details": "No logger exceptions triggered, system is healthy. Manually check if logs were sent to ['gcs_bucket']" - } - } - ``` - - Response when logging callbacks are not setup correctly: - - ```json - { - "key": "unhealthy", - "logging_callbacks": { - "callbacks": ["gcs_bucket"], - "status": "unhealthy", - "details": "Logger exceptions triggered, system is unhealthy: Failed to load vertex credentials. Check to see if credentials containing partial/invalid information." - } - } - ``` - """ - return await self._post( - "/key/health", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=KeyCheckHealthResponse, - ) - - async def generate( - self, - *, - aliases: Optional[object] | NotGiven = NOT_GIVEN, - allowed_cache_controls: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - blocked: Optional[bool] | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - config: Optional[object] | NotGiven = NOT_GIVEN, - duration: Optional[str] | NotGiven = NOT_GIVEN, - enforced_params: Optional[List[str]] | NotGiven = NOT_GIVEN, - guardrails: Optional[List[str]] | NotGiven = NOT_GIVEN, - key: Optional[str] | NotGiven = NOT_GIVEN, - key_alias: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_max_budget: Optional[object] | NotGiven = NOT_GIVEN, - model_rpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - model_tpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - models: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - permissions: Optional[object] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - send_invite_email: Optional[bool] | NotGiven = NOT_GIVEN, - soft_budget: Optional[float] | NotGiven = NOT_GIVEN, - spend: Optional[float] | NotGiven = NOT_GIVEN, - tags: Optional[List[str]] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> GenerateKeyResponse: - """ - Generate an API key based on the provided data. - - Docs: https://docs.hanzo.ai/docs/proxy/virtual_keys - - Parameters: - - - duration: Optional[str] - Specify the length of time the token is valid for. - You can set duration as seconds ("30s"), minutes ("30m"), hours ("30h"), days - ("30d"). - - key_alias: Optional[str] - User defined key alias - - key: Optional[str] - User defined key value. If not set, a 16-digit unique - sk-key is created for you. - - team_id: Optional[str] - The team id of the key - - user_id: Optional[str] - The user id of the key - - budget_id: Optional[str] - The budget id associated with the key. Created by - calling `/budget/new`. - - models: Optional[list] - Model_name's a user is allowed to call. (if empty, - key is allowed to call all models) - - aliases: Optional[dict] - Any alias mappings, on top of anything in the - config.yaml model list. - - https://docs.hanzo.ai/docs/proxy/virtual_keys#managing-auth---upgradedowngrade-models - - config: Optional[dict] - any key-specific configs, overrides config in - config.yaml - - spend: Optional[int] - Amount spent by key. Default is 0. Will be updated by - proxy whenever key is used. - https://docs.hanzo.ai/docs/proxy/virtual_keys#managing-auth---tracking-spend - - send_invite_email: Optional[bool] - Whether to send an invite email to the - user_id, with the generate key - - max_budget: Optional[float] - Specify max budget for a given key. - - budget_duration: Optional[str] - Budget is reset at the end of specified - duration. If not set, budget is never reset. You can set duration as seconds - ("30s"), minutes ("30m"), hours ("30h"), days ("30d"). - - max_parallel_requests: Optional[int] - Rate limit a user based on the number - of parallel requests. Raises 429 error, if user's parallel requests > x. - - metadata: Optional[dict] - Metadata for key, store information for key. - Example metadata = {"team": "core-infra", "app": "app2", "email": - "ishaan@berri.ai" } - - guardrails: Optional[List[str]] - List of active guardrails for the key - - permissions: Optional[dict] - key-specific permissions. Currently just used - for turning off pii masking (if connected). Example - {"pii": false} - - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets - {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}}}. IF null or {} then - no model specific budget. - - model_rpm_limit: Optional[dict] - key-specific model rpm limit. Example - - {"text-davinci-002": 1000, "gpt-3.5-turbo": 1000}. IF null or {} then no model - specific rpm limit. - - model_tpm_limit: Optional[dict] - key-specific model tpm limit. Example - - {"text-davinci-002": 1000, "gpt-3.5-turbo": 1000}. IF null or {} then no model - specific tpm limit. - - allowed_cache_controls: Optional[list] - List of allowed cache control values. - Example - ["no-cache", "no-store"]. See all values - - https://docs.hanzo.ai/docs/proxy/caching#turn-on--off-caching-per-request - - blocked: Optional[bool] - Whether the key is blocked. - - rpm_limit: Optional[int] - Specify rpm limit for a given key (Requests per - minute) - - tpm_limit: Optional[int] - Specify tpm limit for a given key (Tokens per - minute) - - soft_budget: Optional[float] - Specify soft budget for a given key. Will - trigger a slack alert when this soft budget is reached. - - tags: Optional[List[str]] - Tags for - [tracking spend](https://hanzo.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) - and/or doing - [tag-based routing](https://hanzo.vercel.app/docs/proxy/tag_routing). - - enforced_params: Optional[List[str]] - List of enforced params for the key - (Enterprise only). - [Docs](https://docs.hanzo.ai/docs/proxy/enterprise#enforce-required-params-for-llm-requests) - - Examples: - - 1. Allow users to turn on/off pii masking - - ```bash - curl --location 'http://0.0.0.0:4000/key/generate' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "permissions": {"allow_pii_controls": true} - }' - ``` - - Returns: - - - key: (str) The generated api key - - expires: (datetime) Datetime object for when key expires. - - user_id: (str) Unique user id - used for tracking spend across multiple keys - for same user id. - - Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } - return await self._post( - "/key/generate", - body=await async_maybe_transform( - { - "aliases": aliases, - "allowed_cache_controls": allowed_cache_controls, - "blocked": blocked, - "budget_duration": budget_duration, - "budget_id": budget_id, - "config": config, - "duration": duration, - "enforced_params": enforced_params, - "guardrails": guardrails, - "key": key, - "key_alias": key_alias, - "max_budget": max_budget, - "max_parallel_requests": max_parallel_requests, - "metadata": metadata, - "model_max_budget": model_max_budget, - "model_rpm_limit": model_rpm_limit, - "model_tpm_limit": model_tpm_limit, - "models": models, - "permissions": permissions, - "rpm_limit": rpm_limit, - "send_invite_email": send_invite_email, - "soft_budget": soft_budget, - "spend": spend, - "tags": tags, - "team_id": team_id, - "tpm_limit": tpm_limit, - "user_id": user_id, - }, - key_generate_params.KeyGenerateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=GenerateKeyResponse, - ) - - async def regenerate_by_key( - self, - path_key: str, - *, - aliases: Optional[object] | NotGiven = NOT_GIVEN, - allowed_cache_controls: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - blocked: Optional[bool] | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - config: Optional[object] | NotGiven = NOT_GIVEN, - duration: Optional[str] | NotGiven = NOT_GIVEN, - enforced_params: Optional[List[str]] | NotGiven = NOT_GIVEN, - guardrails: Optional[List[str]] | NotGiven = NOT_GIVEN, - body_key: Optional[str] | NotGiven = NOT_GIVEN, - key_alias: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_max_budget: Optional[object] | NotGiven = NOT_GIVEN, - model_rpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - model_tpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - models: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - new_master_key: Optional[str] | NotGiven = NOT_GIVEN, - permissions: Optional[object] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - send_invite_email: Optional[bool] | NotGiven = NOT_GIVEN, - soft_budget: Optional[float] | NotGiven = NOT_GIVEN, - spend: Optional[float] | NotGiven = NOT_GIVEN, - tags: Optional[List[str]] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Optional[GenerateKeyResponse]: - """ - Regenerate an existing API key while optionally updating its parameters. - - Parameters: - - - key: str (path parameter) - The key to regenerate - - data: Optional[RegenerateKeyRequest] - Request body containing optional - parameters to update - - key_alias: Optional[str] - User-friendly key alias - - user_id: Optional[str] - User ID associated with key - - team_id: Optional[str] - Team ID associated with key - - models: Optional[list] - Model_name's a user is allowed to call - - tags: Optional[List[str]] - Tags for organizing keys (Enterprise only) - - spend: Optional[float] - Amount spent by key - - max_budget: Optional[float] - Max budget for key - - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets - {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}} - - budget_duration: Optional[str] - Budget reset period ("30d", "1h", etc.) - - soft_budget: Optional[float] - Soft budget limit (warning vs. hard stop). - Will trigger a slack alert when this soft budget is reached. - - max_parallel_requests: Optional[int] - Rate limit for parallel requests - - metadata: Optional[dict] - Metadata for key. Example {"team": "core-infra", - "app": "app2"} - - tpm_limit: Optional[int] - Tokens per minute limit - - rpm_limit: Optional[int] - Requests per minute limit - - model_rpm_limit: Optional[dict] - Model-specific RPM limits {"gpt-4": 100, - "claude-v1": 200} - - model_tpm_limit: Optional[dict] - Model-specific TPM limits {"gpt-4": - 100000, "claude-v1": 200000} - - allowed_cache_controls: Optional[list] - List of allowed cache control - values - - duration: Optional[str] - Key validity duration ("30d", "1h", etc.) - - permissions: Optional[dict] - Key-specific permissions - - guardrails: Optional[List[str]] - List of active guardrails for the key - - blocked: Optional[bool] - Whether the key is blocked - - Returns: - - - GenerateKeyResponse containing the new key and its updated parameters - - Example: - - ```bash - curl --location --request POST 'http://localhost:4000/key/sk-1234/regenerate' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data-raw '{ - "max_budget": 100, - "metadata": {"team": "core-infra"}, - "models": ["gpt-4", "gpt-3.5-turbo"] - }' - ``` - - Note: This is an Enterprise feature. It requires a premium license to use. - - Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not path_key: - raise ValueError( - f"Expected a non-empty value for `path_key` but received {path_key!r}" - ) - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } - return await self._post( - f"/key/{path_key}/regenerate", - body=await async_maybe_transform( - { - "aliases": aliases, - "allowed_cache_controls": allowed_cache_controls, - "blocked": blocked, - "budget_duration": budget_duration, - "budget_id": budget_id, - "config": config, - "duration": duration, - "enforced_params": enforced_params, - "guardrails": guardrails, - "body_key": body_key, - "key_alias": key_alias, - "max_budget": max_budget, - "max_parallel_requests": max_parallel_requests, - "metadata": metadata, - "model_max_budget": model_max_budget, - "model_rpm_limit": model_rpm_limit, - "model_tpm_limit": model_tpm_limit, - "models": models, - "new_master_key": new_master_key, - "permissions": permissions, - "rpm_limit": rpm_limit, - "send_invite_email": send_invite_email, - "soft_budget": soft_budget, - "spend": spend, - "tags": tags, - "team_id": team_id, - "tpm_limit": tpm_limit, - "user_id": user_id, - }, - key_regenerate_by_key_params.KeyRegenerateByKeyParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=GenerateKeyResponse, - ) - - async def retrieve_info( - self, - *, - key: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Retrieve information about a key. - - Parameters: key: Optional[str] = Query - parameter representing the key in the request user_api_key_dict: UserAPIKeyAuth - = Dependency representing the user's API key Returns: Dict containing the key - and its associated information - - Example Curl: - - ``` - curl -X GET "http://0.0.0.0:4000/key/info?key=sk-02Wr4IAlN3NvPXvL5JVvDA" -H "Authorization: Bearer sk-1234" - ``` - - Example Curl - if no key is passed, it will use the Key Passed in Authorization - Header - - ``` - curl -X GET "http://0.0.0.0:4000/key/info" -H "Authorization: Bearer sk-02Wr4IAlN3NvPXvL5JVvDA" - ``` - - Args: - key: Key in the request parameters - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/key/info", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"key": key}, key_retrieve_info_params.KeyRetrieveInfoParams - ), - ), - cast_to=object, - ) - - async def unblock( - self, - *, - key: str, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Unblock a Virtual key to allow it to make requests again. - - Parameters: - - - key: str - The key to unblock. Can be either the unhashed key (sk-...) or the - hashed key value - - Example: - - ```bash - curl --location 'http://0.0.0.0:4000/key/unblock' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "key": "sk-Fn8Ej39NxjAXrvpUGKghGw" - }' - ``` - - Note: This is an admin-only endpoint. Only proxy admins can unblock keys. - - Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } - return await self._post( - "/key/unblock", - body=await async_maybe_transform( - {"key": key}, key_unblock_params.KeyUnblockParams - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class KeyResourceWithRawResponse: - def __init__(self, key: KeyResource) -> None: - self._key = key - - self.update = to_raw_response_wrapper( - key.update, - ) - self.list = to_raw_response_wrapper( - key.list, - ) - self.delete = to_raw_response_wrapper( - key.delete, - ) - self.block = to_raw_response_wrapper( - key.block, - ) - self.check_health = to_raw_response_wrapper( - key.check_health, - ) - self.generate = to_raw_response_wrapper( - key.generate, - ) - self.regenerate_by_key = to_raw_response_wrapper( - key.regenerate_by_key, - ) - self.retrieve_info = to_raw_response_wrapper( - key.retrieve_info, - ) - self.unblock = to_raw_response_wrapper( - key.unblock, - ) - - -class AsyncKeyResourceWithRawResponse: - def __init__(self, key: AsyncKeyResource) -> None: - self._key = key - - self.update = async_to_raw_response_wrapper( - key.update, - ) - self.list = async_to_raw_response_wrapper( - key.list, - ) - self.delete = async_to_raw_response_wrapper( - key.delete, - ) - self.block = async_to_raw_response_wrapper( - key.block, - ) - self.check_health = async_to_raw_response_wrapper( - key.check_health, - ) - self.generate = async_to_raw_response_wrapper( - key.generate, - ) - self.regenerate_by_key = async_to_raw_response_wrapper( - key.regenerate_by_key, - ) - self.retrieve_info = async_to_raw_response_wrapper( - key.retrieve_info, - ) - self.unblock = async_to_raw_response_wrapper( - key.unblock, - ) - - -class KeyResourceWithStreamingResponse: - def __init__(self, key: KeyResource) -> None: - self._key = key - - self.update = to_streamed_response_wrapper( - key.update, - ) - self.list = to_streamed_response_wrapper( - key.list, - ) - self.delete = to_streamed_response_wrapper( - key.delete, - ) - self.block = to_streamed_response_wrapper( - key.block, - ) - self.check_health = to_streamed_response_wrapper( - key.check_health, - ) - self.generate = to_streamed_response_wrapper( - key.generate, - ) - self.regenerate_by_key = to_streamed_response_wrapper( - key.regenerate_by_key, - ) - self.retrieve_info = to_streamed_response_wrapper( - key.retrieve_info, - ) - self.unblock = to_streamed_response_wrapper( - key.unblock, - ) - - -class AsyncKeyResourceWithStreamingResponse: - def __init__(self, key: AsyncKeyResource) -> None: - self._key = key - - self.update = async_to_streamed_response_wrapper( - key.update, - ) - self.list = async_to_streamed_response_wrapper( - key.list, - ) - self.delete = async_to_streamed_response_wrapper( - key.delete, - ) - self.block = async_to_streamed_response_wrapper( - key.block, - ) - self.check_health = async_to_streamed_response_wrapper( - key.check_health, - ) - self.generate = async_to_streamed_response_wrapper( - key.generate, - ) - self.regenerate_by_key = async_to_streamed_response_wrapper( - key.regenerate_by_key, - ) - self.retrieve_info = async_to_streamed_response_wrapper( - key.retrieve_info, - ) - self.unblock = async_to_streamed_response_wrapper( - key.unblock, - ) diff --git a/pkg/hanzoai/resources/kms.py b/pkg/hanzoai/resources/kms.py deleted file mode 100644 index e35a281f3..000000000 --- a/pkg/hanzoai/resources/kms.py +++ /dev/null @@ -1,842 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class KMSResource(SyncAPIResource): - """Key Management Service with HSM semantics.""" - - @cached_property - def with_raw_response(self) -> KMSResourceWithRawResponse: - return KMSResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> KMSResourceWithStreamingResponse: - return KMSResourceWithStreamingResponse(self) - - # Key management - def create_key( - self, - *, - name: str, - type: str, - algorithm: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new cryptographic key.""" - return self._post( - "/kms/keys", - body={ - "name": name, - "type": type, - "algorithm": algorithm, - "description": description, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_keys( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all keys.""" - return self._get( - "/kms/keys", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def describe_key( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get key details.""" - return self._get( - f"/kms/keys/{key_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def enable_key( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Enable a key.""" - return self._post( - f"/kms/keys/{key_id}/enable", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def disable_key( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Disable a key.""" - return self._post( - f"/kms/keys/{key_id}/disable", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_key( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Schedule key for deletion.""" - return self._delete( - f"/kms/keys/{key_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def rotate_key( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Rotate a key to new version.""" - return self._post( - f"/kms/keys/{key_id}/rotate", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_key_versions( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List key versions.""" - return self._get( - f"/kms/keys/{key_id}/versions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def rollback_key( - self, - key_id: str, - *, - version: int, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Rollback to previous key version.""" - return self._post( - f"/kms/keys/{key_id}/rollback", - body={"version": version}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Crypto operations - def encrypt( - self, - *, - key_id: str, - plaintext: str, - context: Dict[str, str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Encrypt data with a key.""" - return self._post( - "/kms/encrypt", - body={"key_id": key_id, "plaintext": plaintext, "context": context}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def decrypt( - self, - *, - key_id: str, - ciphertext: str, - context: Dict[str, str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Decrypt data with a key.""" - return self._post( - "/kms/decrypt", - body={"key_id": key_id, "ciphertext": ciphertext, "context": context}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def sign( - self, - *, - key_id: str, - message: str, - algorithm: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Sign data with a key.""" - return self._post( - "/kms/sign", - body={"key_id": key_id, "message": message, "algorithm": algorithm}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def verify( - self, - *, - key_id: str, - message: str, - signature: str, - algorithm: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Verify signature with a key.""" - return self._post( - "/kms/verify", - body={ - "key_id": key_id, - "message": message, - "signature": signature, - "algorithm": algorithm, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Access control - def create_grant( - self, - key_id: str, - *, - principal: str, - operations: List[str], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a grant for key access.""" - return self._post( - f"/kms/keys/{key_id}/grants", - body={"principal": principal, "operations": operations}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def revoke_grant( - self, - key_id: str, - grant_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Revoke a grant.""" - return self._delete( - f"/kms/keys/{key_id}/grants/{grant_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_grants( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List grants for a key.""" - return self._get( - f"/kms/keys/{key_id}/grants", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Audit - def audit( - self, - *, - key_id: str | NotGiven = NOT_GIVEN, - since: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get audit logs for KMS operations.""" - return self._get( - "/kms/audit", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"key_id": key_id, "since": since}, - ), - cast_to=object, - ) - - -class AsyncKMSResource(AsyncAPIResource): - """Key Management Service with HSM semantics.""" - - @cached_property - def with_raw_response(self) -> AsyncKMSResourceWithRawResponse: - return AsyncKMSResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncKMSResourceWithStreamingResponse: - return AsyncKMSResourceWithStreamingResponse(self) - - async def create_key( - self, - *, - name: str, - type: str, - algorithm: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new cryptographic key.""" - return await self._post( - "/kms/keys", - body={ - "name": name, - "type": type, - "algorithm": algorithm, - "description": description, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_keys( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all keys.""" - return await self._get( - "/kms/keys", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def describe_key( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get key details.""" - return await self._get( - f"/kms/keys/{key_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def enable_key( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Enable a key.""" - return await self._post( - f"/kms/keys/{key_id}/enable", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def disable_key( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Disable a key.""" - return await self._post( - f"/kms/keys/{key_id}/disable", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_key( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Schedule key for deletion.""" - return await self._delete( - f"/kms/keys/{key_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def rotate_key( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Rotate a key to new version.""" - return await self._post( - f"/kms/keys/{key_id}/rotate", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_key_versions( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List key versions.""" - return await self._get( - f"/kms/keys/{key_id}/versions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def rollback_key( - self, - key_id: str, - *, - version: int, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Rollback to previous key version.""" - return await self._post( - f"/kms/keys/{key_id}/rollback", - body={"version": version}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def encrypt( - self, - *, - key_id: str, - plaintext: str, - context: Dict[str, str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Encrypt data with a key.""" - return await self._post( - "/kms/encrypt", - body={"key_id": key_id, "plaintext": plaintext, "context": context}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def decrypt( - self, - *, - key_id: str, - ciphertext: str, - context: Dict[str, str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Decrypt data with a key.""" - return await self._post( - "/kms/decrypt", - body={"key_id": key_id, "ciphertext": ciphertext, "context": context}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def sign( - self, - *, - key_id: str, - message: str, - algorithm: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Sign data with a key.""" - return await self._post( - "/kms/sign", - body={"key_id": key_id, "message": message, "algorithm": algorithm}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def verify( - self, - *, - key_id: str, - message: str, - signature: str, - algorithm: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Verify signature with a key.""" - return await self._post( - "/kms/verify", - body={ - "key_id": key_id, - "message": message, - "signature": signature, - "algorithm": algorithm, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_grant( - self, - key_id: str, - *, - principal: str, - operations: List[str], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a grant for key access.""" - return await self._post( - f"/kms/keys/{key_id}/grants", - body={"principal": principal, "operations": operations}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def revoke_grant( - self, - key_id: str, - grant_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Revoke a grant.""" - return await self._delete( - f"/kms/keys/{key_id}/grants/{grant_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_grants( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List grants for a key.""" - return await self._get( - f"/kms/keys/{key_id}/grants", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def audit( - self, - *, - key_id: str | NotGiven = NOT_GIVEN, - since: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get audit logs for KMS operations.""" - return await self._get( - "/kms/audit", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"key_id": key_id, "since": since}, - ), - cast_to=object, - ) - - -class KMSResourceWithRawResponse: - def __init__(self, kms: KMSResource) -> None: - self._kms = kms - - -class AsyncKMSResourceWithRawResponse: - def __init__(self, kms: AsyncKMSResource) -> None: - self._kms = kms - - -class KMSResourceWithStreamingResponse: - def __init__(self, kms: KMSResource) -> None: - self._kms = kms - - -class AsyncKMSResourceWithStreamingResponse: - def __init__(self, kms: AsyncKMSResource) -> None: - self._kms = kms diff --git a/pkg/hanzoai/resources/kv.py b/pkg/hanzoai/resources/kv.py deleted file mode 100644 index 010256a18..000000000 --- a/pkg/hanzoai/resources/kv.py +++ /dev/null @@ -1,397 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List, Optional - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["KVResource", "AsyncKVResource"] - - -class KVResource(SyncAPIResource): - """Key-value store service.""" - - @cached_property - def with_raw_response(self) -> KVResourceWithRawResponse: - return KVResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> KVResourceWithStreamingResponse: - return KVResourceWithStreamingResponse(self) - - def list_namespaces( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all KV namespaces.""" - return self._get( - "/kv/namespaces", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_namespace( - self, - *, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new KV namespace.""" - return self._post( - "/kv/namespaces", - body={"name": name}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_namespace( - self, - namespace_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a KV namespace.""" - return self._delete( - f"/kv/namespaces/{namespace_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - namespace_id: str, - key: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a value from KV store.""" - return self._get( - f"/kv/namespaces/{namespace_id}/values/{key}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def put( - self, - namespace_id: str, - key: str, - *, - value: str, - ttl: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Put a value into KV store.""" - return self._put( - f"/kv/namespaces/{namespace_id}/values/{key}", - body={"value": value, "ttl": ttl}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - namespace_id: str, - key: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a value from KV store.""" - return self._delete( - f"/kv/namespaces/{namespace_id}/values/{key}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_keys( - self, - namespace_id: str, - *, - prefix: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List keys in a namespace.""" - return self._get( - f"/kv/namespaces/{namespace_id}/keys", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"prefix": prefix, "limit": limit}, - ), - cast_to=object, - ) - - -class AsyncKVResource(AsyncAPIResource): - """Key-value store service (async).""" - - @cached_property - def with_raw_response(self) -> AsyncKVResourceWithRawResponse: - return AsyncKVResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncKVResourceWithStreamingResponse: - return AsyncKVResourceWithStreamingResponse(self) - - async def list_namespaces( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/kv/namespaces", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_namespace( - self, - *, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/kv/namespaces", - body={"name": name}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_namespace( - self, - namespace_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/kv/namespaces/{namespace_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - namespace_id: str, - key: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/kv/namespaces/{namespace_id}/values/{key}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def put( - self, - namespace_id: str, - key: str, - *, - value: str, - ttl: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._put( - f"/kv/namespaces/{namespace_id}/values/{key}", - body={"value": value, "ttl": ttl}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - namespace_id: str, - key: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/kv/namespaces/{namespace_id}/values/{key}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_keys( - self, - namespace_id: str, - *, - prefix: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/kv/namespaces/{namespace_id}/keys", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"prefix": prefix, "limit": limit}, - ), - cast_to=object, - ) - - -class KVResourceWithRawResponse: - def __init__(self, kv: KVResource) -> None: - self._kv = kv - self.list_namespaces = to_raw_response_wrapper(kv.list_namespaces) - self.create_namespace = to_raw_response_wrapper(kv.create_namespace) - self.delete_namespace = to_raw_response_wrapper(kv.delete_namespace) - self.get = to_raw_response_wrapper(kv.get) - self.put = to_raw_response_wrapper(kv.put) - self.delete = to_raw_response_wrapper(kv.delete) - self.list_keys = to_raw_response_wrapper(kv.list_keys) - - -class AsyncKVResourceWithRawResponse: - def __init__(self, kv: AsyncKVResource) -> None: - self._kv = kv - self.list_namespaces = async_to_raw_response_wrapper(kv.list_namespaces) - self.create_namespace = async_to_raw_response_wrapper(kv.create_namespace) - self.delete_namespace = async_to_raw_response_wrapper(kv.delete_namespace) - self.get = async_to_raw_response_wrapper(kv.get) - self.put = async_to_raw_response_wrapper(kv.put) - self.delete = async_to_raw_response_wrapper(kv.delete) - self.list_keys = async_to_raw_response_wrapper(kv.list_keys) - - -class KVResourceWithStreamingResponse: - def __init__(self, kv: KVResource) -> None: - self._kv = kv - self.list_namespaces = to_streamed_response_wrapper(kv.list_namespaces) - self.create_namespace = to_streamed_response_wrapper(kv.create_namespace) - self.delete_namespace = to_streamed_response_wrapper(kv.delete_namespace) - self.get = to_streamed_response_wrapper(kv.get) - self.put = to_streamed_response_wrapper(kv.put) - self.delete = to_streamed_response_wrapper(kv.delete) - self.list_keys = to_streamed_response_wrapper(kv.list_keys) - - -class AsyncKVResourceWithStreamingResponse: - def __init__(self, kv: AsyncKVResource) -> None: - self._kv = kv - self.list_namespaces = async_to_streamed_response_wrapper(kv.list_namespaces) - self.create_namespace = async_to_streamed_response_wrapper(kv.create_namespace) - self.delete_namespace = async_to_streamed_response_wrapper(kv.delete_namespace) - self.get = async_to_streamed_response_wrapper(kv.get) - self.put = async_to_streamed_response_wrapper(kv.put) - self.delete = async_to_streamed_response_wrapper(kv.delete) - self.list_keys = async_to_streamed_response_wrapper(kv.list_keys) diff --git a/pkg/hanzoai/resources/machines.py b/pkg/hanzoai/resources/machines.py deleted file mode 100644 index 5b1bea35e..000000000 --- a/pkg/hanzoai/resources/machines.py +++ /dev/null @@ -1,828 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["MachinesResource", "AsyncMachinesResource"] - - -class MachinesResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> MachinesResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return MachinesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> MachinesResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return MachinesResourceWithStreamingResponse(self) - - def list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all machines in the infrastructure.""" - return self._get( - "/infrastructure/machines", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - machine_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Get details of a specific machine. - - Args: - machine_id: The unique identifier of the machine. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not machine_id: - raise ValueError( - f"Expected a non-empty value for `machine_id` but received {machine_id!r}" - ) - return self._get( - f"/infrastructure/machines/{machine_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: Optional[str] | NotGiven = NOT_GIVEN, - machine_type: Optional[str] | NotGiven = NOT_GIVEN, - region: Optional[str] | NotGiven = NOT_GIVEN, - image: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Create a new machine. - - Args: - name: The name of the machine. - - machine_type: The type/size of the machine. - - region: The region to deploy the machine in. - - image: The image to use for the machine. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/infrastructure/machines", - body={ - "name": name, - "machine_type": machine_type, - "region": region, - "image": image, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - machine_id: str, - *, - name: Optional[str] | NotGiven = NOT_GIVEN, - machine_type: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Update an existing machine. - - Args: - machine_id: The unique identifier of the machine. - - name: The new name of the machine. - - machine_type: The new type/size of the machine. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not machine_id: - raise ValueError( - f"Expected a non-empty value for `machine_id` but received {machine_id!r}" - ) - return self._put( - f"/infrastructure/machines/{machine_id}", - body={ - "name": name, - "machine_type": machine_type, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - machine_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Delete a machine. - - Args: - machine_id: The unique identifier of the machine. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not machine_id: - raise ValueError( - f"Expected a non-empty value for `machine_id` but received {machine_id!r}" - ) - return self._delete( - f"/infrastructure/machines/{machine_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def start( - self, - machine_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Start a stopped machine. - - Args: - machine_id: The unique identifier of the machine. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not machine_id: - raise ValueError( - f"Expected a non-empty value for `machine_id` but received {machine_id!r}" - ) - return self._post( - f"/infrastructure/machines/{machine_id}/start", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stop( - self, - machine_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Stop a running machine. - - Args: - machine_id: The unique identifier of the machine. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not machine_id: - raise ValueError( - f"Expected a non-empty value for `machine_id` but received {machine_id!r}" - ) - return self._post( - f"/infrastructure/machines/{machine_id}/stop", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def restart( - self, - machine_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Restart a machine. - - Args: - machine_id: The unique identifier of the machine. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not machine_id: - raise ValueError( - f"Expected a non-empty value for `machine_id` but received {machine_id!r}" - ) - return self._post( - f"/infrastructure/machines/{machine_id}/restart", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncMachinesResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncMachinesResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncMachinesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncMachinesResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncMachinesResourceWithStreamingResponse(self) - - async def list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all machines in the infrastructure.""" - return await self._get( - "/infrastructure/machines", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - machine_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Get details of a specific machine. - - Args: - machine_id: The unique identifier of the machine. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not machine_id: - raise ValueError( - f"Expected a non-empty value for `machine_id` but received {machine_id!r}" - ) - return await self._get( - f"/infrastructure/machines/{machine_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: Optional[str] | NotGiven = NOT_GIVEN, - machine_type: Optional[str] | NotGiven = NOT_GIVEN, - region: Optional[str] | NotGiven = NOT_GIVEN, - image: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Create a new machine. - - Args: - name: The name of the machine. - - machine_type: The type/size of the machine. - - region: The region to deploy the machine in. - - image: The image to use for the machine. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/infrastructure/machines", - body={ - "name": name, - "machine_type": machine_type, - "region": region, - "image": image, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - machine_id: str, - *, - name: Optional[str] | NotGiven = NOT_GIVEN, - machine_type: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Update an existing machine. - - Args: - machine_id: The unique identifier of the machine. - - name: The new name of the machine. - - machine_type: The new type/size of the machine. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not machine_id: - raise ValueError( - f"Expected a non-empty value for `machine_id` but received {machine_id!r}" - ) - return await self._put( - f"/infrastructure/machines/{machine_id}", - body={ - "name": name, - "machine_type": machine_type, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - machine_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Delete a machine. - - Args: - machine_id: The unique identifier of the machine. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not machine_id: - raise ValueError( - f"Expected a non-empty value for `machine_id` but received {machine_id!r}" - ) - return await self._delete( - f"/infrastructure/machines/{machine_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def start( - self, - machine_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Start a stopped machine. - - Args: - machine_id: The unique identifier of the machine. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not machine_id: - raise ValueError( - f"Expected a non-empty value for `machine_id` but received {machine_id!r}" - ) - return await self._post( - f"/infrastructure/machines/{machine_id}/start", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stop( - self, - machine_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Stop a running machine. - - Args: - machine_id: The unique identifier of the machine. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not machine_id: - raise ValueError( - f"Expected a non-empty value for `machine_id` but received {machine_id!r}" - ) - return await self._post( - f"/infrastructure/machines/{machine_id}/stop", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def restart( - self, - machine_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Restart a machine. - - Args: - machine_id: The unique identifier of the machine. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not machine_id: - raise ValueError( - f"Expected a non-empty value for `machine_id` but received {machine_id!r}" - ) - return await self._post( - f"/infrastructure/machines/{machine_id}/restart", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class MachinesResourceWithRawResponse: - def __init__(self, machines: MachinesResource) -> None: - self._machines = machines - - self.list = to_raw_response_wrapper( - machines.list, - ) - self.get = to_raw_response_wrapper( - machines.get, - ) - self.create = to_raw_response_wrapper( - machines.create, - ) - self.update = to_raw_response_wrapper( - machines.update, - ) - self.delete = to_raw_response_wrapper( - machines.delete, - ) - self.start = to_raw_response_wrapper( - machines.start, - ) - self.stop = to_raw_response_wrapper( - machines.stop, - ) - self.restart = to_raw_response_wrapper( - machines.restart, - ) - - -class AsyncMachinesResourceWithRawResponse: - def __init__(self, machines: AsyncMachinesResource) -> None: - self._machines = machines - - self.list = async_to_raw_response_wrapper( - machines.list, - ) - self.get = async_to_raw_response_wrapper( - machines.get, - ) - self.create = async_to_raw_response_wrapper( - machines.create, - ) - self.update = async_to_raw_response_wrapper( - machines.update, - ) - self.delete = async_to_raw_response_wrapper( - machines.delete, - ) - self.start = async_to_raw_response_wrapper( - machines.start, - ) - self.stop = async_to_raw_response_wrapper( - machines.stop, - ) - self.restart = async_to_raw_response_wrapper( - machines.restart, - ) - - -class MachinesResourceWithStreamingResponse: - def __init__(self, machines: MachinesResource) -> None: - self._machines = machines - - self.list = to_streamed_response_wrapper( - machines.list, - ) - self.get = to_streamed_response_wrapper( - machines.get, - ) - self.create = to_streamed_response_wrapper( - machines.create, - ) - self.update = to_streamed_response_wrapper( - machines.update, - ) - self.delete = to_streamed_response_wrapper( - machines.delete, - ) - self.start = to_streamed_response_wrapper( - machines.start, - ) - self.stop = to_streamed_response_wrapper( - machines.stop, - ) - self.restart = to_streamed_response_wrapper( - machines.restart, - ) - - -class AsyncMachinesResourceWithStreamingResponse: - def __init__(self, machines: AsyncMachinesResource) -> None: - self._machines = machines - - self.list = async_to_streamed_response_wrapper( - machines.list, - ) - self.get = async_to_streamed_response_wrapper( - machines.get, - ) - self.create = async_to_streamed_response_wrapper( - machines.create, - ) - self.update = async_to_streamed_response_wrapper( - machines.update, - ) - self.delete = async_to_streamed_response_wrapper( - machines.delete, - ) - self.start = async_to_streamed_response_wrapper( - machines.start, - ) - self.stop = async_to_streamed_response_wrapper( - machines.stop, - ) - self.restart = async_to_streamed_response_wrapper( - machines.restart, - ) diff --git a/pkg/hanzoai/resources/mcp_servers.py b/pkg/hanzoai/resources/mcp_servers.py deleted file mode 100644 index 3124125ee..000000000 --- a/pkg/hanzoai/resources/mcp_servers.py +++ /dev/null @@ -1,444 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["MCPServersResource", "AsyncMCPServersResource"] - - -class MCPServersResource(SyncAPIResource): - """Model Context Protocol server management.""" - - @cached_property - def with_raw_response(self) -> MCPServersResourceWithRawResponse: - return MCPServersResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> MCPServersResourceWithStreamingResponse: - return MCPServersResourceWithStreamingResponse(self) - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all MCP servers.""" - return self._get( - "/mcp/servers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - server_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific MCP server.""" - return self._get( - f"/mcp/servers/{server_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - url: str, - api_key: str | NotGiven = NOT_GIVEN, - tools: List[str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Register a new MCP server.""" - return self._post( - "/mcp/servers", - body={"name": name, "url": url, "api_key": api_key, "tools": tools}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - server_id: str, - *, - url: str | NotGiven = NOT_GIVEN, - api_key: str | NotGiven = NOT_GIVEN, - enabled: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an MCP server.""" - return self._put( - f"/mcp/servers/{server_id}", - body={"url": url, "api_key": api_key, "enabled": enabled}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - server_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove an MCP server.""" - return self._delete( - f"/mcp/servers/{server_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_tools( - self, - server_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List tools from an MCP server.""" - return self._get( - f"/mcp/servers/{server_id}/tools", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def call_tool( - self, - server_id: str, - tool_name: str, - *, - arguments: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Call a tool on an MCP server.""" - return self._post( - f"/mcp/servers/{server_id}/tools/{tool_name}", - body={"arguments": arguments}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def refresh( - self, - server_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Refresh MCP server tools.""" - return self._post( - f"/mcp/servers/{server_id}/refresh", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncMCPServersResource(AsyncAPIResource): - """Model Context Protocol server management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncMCPServersResourceWithRawResponse: - return AsyncMCPServersResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncMCPServersResourceWithStreamingResponse: - return AsyncMCPServersResourceWithStreamingResponse(self) - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/mcp/servers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - server_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/mcp/servers/{server_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - url: str, - api_key: str | NotGiven = NOT_GIVEN, - tools: List[str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/mcp/servers", - body={"name": name, "url": url, "api_key": api_key, "tools": tools}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - server_id: str, - *, - url: str | NotGiven = NOT_GIVEN, - api_key: str | NotGiven = NOT_GIVEN, - enabled: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._put( - f"/mcp/servers/{server_id}", - body={"url": url, "api_key": api_key, "enabled": enabled}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - server_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/mcp/servers/{server_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_tools( - self, - server_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/mcp/servers/{server_id}/tools", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def call_tool( - self, - server_id: str, - tool_name: str, - *, - arguments: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/mcp/servers/{server_id}/tools/{tool_name}", - body={"arguments": arguments}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def refresh( - self, - server_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/mcp/servers/{server_id}/refresh", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class MCPServersResourceWithRawResponse: - def __init__(self, mcp_servers: MCPServersResource) -> None: - self._mcp_servers = mcp_servers - self.list = to_raw_response_wrapper(mcp_servers.list) - self.get = to_raw_response_wrapper(mcp_servers.get) - self.create = to_raw_response_wrapper(mcp_servers.create) - self.update = to_raw_response_wrapper(mcp_servers.update) - self.delete = to_raw_response_wrapper(mcp_servers.delete) - self.list_tools = to_raw_response_wrapper(mcp_servers.list_tools) - self.call_tool = to_raw_response_wrapper(mcp_servers.call_tool) - self.refresh = to_raw_response_wrapper(mcp_servers.refresh) - - -class AsyncMCPServersResourceWithRawResponse: - def __init__(self, mcp_servers: AsyncMCPServersResource) -> None: - self._mcp_servers = mcp_servers - self.list = async_to_raw_response_wrapper(mcp_servers.list) - self.get = async_to_raw_response_wrapper(mcp_servers.get) - self.create = async_to_raw_response_wrapper(mcp_servers.create) - self.update = async_to_raw_response_wrapper(mcp_servers.update) - self.delete = async_to_raw_response_wrapper(mcp_servers.delete) - self.list_tools = async_to_raw_response_wrapper(mcp_servers.list_tools) - self.call_tool = async_to_raw_response_wrapper(mcp_servers.call_tool) - self.refresh = async_to_raw_response_wrapper(mcp_servers.refresh) - - -class MCPServersResourceWithStreamingResponse: - def __init__(self, mcp_servers: MCPServersResource) -> None: - self._mcp_servers = mcp_servers - self.list = to_streamed_response_wrapper(mcp_servers.list) - self.get = to_streamed_response_wrapper(mcp_servers.get) - self.create = to_streamed_response_wrapper(mcp_servers.create) - self.update = to_streamed_response_wrapper(mcp_servers.update) - self.delete = to_streamed_response_wrapper(mcp_servers.delete) - self.list_tools = to_streamed_response_wrapper(mcp_servers.list_tools) - self.call_tool = to_streamed_response_wrapper(mcp_servers.call_tool) - self.refresh = to_streamed_response_wrapper(mcp_servers.refresh) - - -class AsyncMCPServersResourceWithStreamingResponse: - def __init__(self, mcp_servers: AsyncMCPServersResource) -> None: - self._mcp_servers = mcp_servers - self.list = async_to_streamed_response_wrapper(mcp_servers.list) - self.get = async_to_streamed_response_wrapper(mcp_servers.get) - self.create = async_to_streamed_response_wrapper(mcp_servers.create) - self.update = async_to_streamed_response_wrapper(mcp_servers.update) - self.delete = async_to_streamed_response_wrapper(mcp_servers.delete) - self.list_tools = async_to_streamed_response_wrapper(mcp_servers.list_tools) - self.call_tool = async_to_streamed_response_wrapper(mcp_servers.call_tool) - self.refresh = async_to_streamed_response_wrapper(mcp_servers.refresh) diff --git a/pkg/hanzoai/resources/miner.py b/pkg/hanzoai/resources/miner.py deleted file mode 100644 index f929011b8..000000000 --- a/pkg/hanzoai/resources/miner.py +++ /dev/null @@ -1,396 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class MinerResource(SyncAPIResource): - """Mining and staking operations.""" - - @cached_property - def with_raw_response(self) -> MinerResourceWithRawResponse: - return MinerResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> MinerResourceWithStreamingResponse: - return MinerResourceWithStreamingResponse(self) - - def start( - self, - *, - threads: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Start miner.""" - return self._post( - "/miner/start", - body={"threads": threads}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stop( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Stop miner.""" - return self._post( - "/miner/stop", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def status( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get miner status.""" - return self._get( - "/miner/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stats( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get mining stats.""" - return self._get( - "/miner/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def logs( - self, - *, - follow: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get miner logs.""" - return self._get( - "/miner/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"follow": follow}, - ), - cast_to=object, - ) - - def stake( - self, - *, - amount: str, - validator: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Stake tokens.""" - return self._post( - "/miner/stake", - body={"amount": amount, "validator": validator}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def unstake( - self, - *, - amount: str, - validator: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Unstake tokens.""" - return self._post( - "/miner/unstake", - body={"amount": amount, "validator": validator}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def benchmarks( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Run mining benchmarks.""" - return self._post( - "/miner/benchmarks", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncMinerResource(AsyncAPIResource): - """Mining and staking operations.""" - - @cached_property - def with_raw_response(self) -> AsyncMinerResourceWithRawResponse: - return AsyncMinerResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncMinerResourceWithStreamingResponse: - return AsyncMinerResourceWithStreamingResponse(self) - - async def start( - self, - *, - threads: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Start miner.""" - return await self._post( - "/miner/start", - body={"threads": threads}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stop( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Stop miner.""" - return await self._post( - "/miner/stop", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def status( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get miner status.""" - return await self._get( - "/miner/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stats( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get mining stats.""" - return await self._get( - "/miner/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def logs( - self, - *, - follow: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get miner logs.""" - return await self._get( - "/miner/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"follow": follow}, - ), - cast_to=object, - ) - - async def stake( - self, - *, - amount: str, - validator: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Stake tokens.""" - return await self._post( - "/miner/stake", - body={"amount": amount, "validator": validator}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def unstake( - self, - *, - amount: str, - validator: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Unstake tokens.""" - return await self._post( - "/miner/unstake", - body={"amount": amount, "validator": validator}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def benchmarks( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Run mining benchmarks.""" - return await self._post( - "/miner/benchmarks", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class MinerResourceWithRawResponse: - def __init__(self, miner: MinerResource) -> None: - self._miner = miner - - -class AsyncMinerResourceWithRawResponse: - def __init__(self, miner: AsyncMinerResource) -> None: - self._miner = miner - - -class MinerResourceWithStreamingResponse: - def __init__(self, miner: MinerResource) -> None: - self._miner = miner - - -class AsyncMinerResourceWithStreamingResponse: - def __init__(self, miner: AsyncMinerResource) -> None: - self._miner = miner diff --git a/pkg/hanzoai/resources/model/__init__.py b/pkg/hanzoai/resources/model/__init__.py deleted file mode 100644 index c01ebb408..000000000 --- a/pkg/hanzoai/resources/model/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# Hanzo AI SDK - -from .info import ( - InfoResource, - AsyncInfoResource, - InfoResourceWithRawResponse, - AsyncInfoResourceWithRawResponse, - InfoResourceWithStreamingResponse, - AsyncInfoResourceWithStreamingResponse, -) -from .model import ( - ModelResource, - AsyncModelResource, - ModelResourceWithRawResponse, - AsyncModelResourceWithRawResponse, - ModelResourceWithStreamingResponse, - AsyncModelResourceWithStreamingResponse, -) -from .update import ( - UpdateResource, - AsyncUpdateResource, - UpdateResourceWithRawResponse, - AsyncUpdateResourceWithRawResponse, - UpdateResourceWithStreamingResponse, - AsyncUpdateResourceWithStreamingResponse, -) - -__all__ = [ - "InfoResource", - "AsyncInfoResource", - "InfoResourceWithRawResponse", - "AsyncInfoResourceWithRawResponse", - "InfoResourceWithStreamingResponse", - "AsyncInfoResourceWithStreamingResponse", - "UpdateResource", - "AsyncUpdateResource", - "UpdateResourceWithRawResponse", - "AsyncUpdateResourceWithRawResponse", - "UpdateResourceWithStreamingResponse", - "AsyncUpdateResourceWithStreamingResponse", - "ModelResource", - "AsyncModelResource", - "ModelResourceWithRawResponse", - "AsyncModelResourceWithRawResponse", - "ModelResourceWithStreamingResponse", - "AsyncModelResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/model/info.py b/pkg/hanzoai/resources/model/info.py deleted file mode 100644 index ef9e2f3e0..000000000 --- a/pkg/hanzoai/resources/model/info.py +++ /dev/null @@ -1,235 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional - -import httpx - -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...types.model import info_list_params -from ..._base_client import make_request_options - -__all__ = ["InfoResource", "AsyncInfoResource"] - - -class InfoResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> InfoResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return InfoResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> InfoResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return InfoResourceWithStreamingResponse(self) - - def list( - self, - *, - hanzo_model_id: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Provides more info about each model in /models, including config.yaml - descriptions (except api key and api base) - - Parameters: hanzo_model_id: Optional[str] = None (this is the value of - `x-model-id` returned in response headers) - - - When hanzo_model_id is passed, it will return the info for that specific model - - When hanzo_model_id is not passed, it will return the info for all models - - Returns: Returns a dictionary containing information about each model. - - Example Response: - - ```json - { - "data": [ - { - "model_name": "fake-openai-endpoint", - "hanzo_params": { - "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", - "model": "openai/fake" - }, - "model_info": { - "id": "112f74fab24a7a5245d2ced3536dd8f5f9192c57ee6e332af0f0512e08bed5af", - "db_model": false - } - } - ] - } - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/model/info", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"hanzo_model_id": hanzo_model_id}, info_list_params.InfoListParams - ), - ), - cast_to=object, - ) - - -class AsyncInfoResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncInfoResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncInfoResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncInfoResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncInfoResourceWithStreamingResponse(self) - - async def list( - self, - *, - hanzo_model_id: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Provides more info about each model in /models, including config.yaml - descriptions (except api key and api base) - - Parameters: hanzo_model_id: Optional[str] = None (this is the value of - `x-model-id` returned in response headers) - - - When hanzo_model_id is passed, it will return the info for that specific model - - When hanzo_model_id is not passed, it will return the info for all models - - Returns: Returns a dictionary containing information about each model. - - Example Response: - - ```json - { - "data": [ - { - "model_name": "fake-openai-endpoint", - "hanzo_params": { - "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", - "model": "openai/fake" - }, - "model_info": { - "id": "112f74fab24a7a5245d2ced3536dd8f5f9192c57ee6e332af0f0512e08bed5af", - "db_model": false - } - } - ] - } - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/model/info", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"hanzo_model_id": hanzo_model_id}, info_list_params.InfoListParams - ), - ), - cast_to=object, - ) - - -class InfoResourceWithRawResponse: - def __init__(self, info: InfoResource) -> None: - self._info = info - - self.list = to_raw_response_wrapper( - info.list, - ) - - -class AsyncInfoResourceWithRawResponse: - def __init__(self, info: AsyncInfoResource) -> None: - self._info = info - - self.list = async_to_raw_response_wrapper( - info.list, - ) - - -class InfoResourceWithStreamingResponse: - def __init__(self, info: InfoResource) -> None: - self._info = info - - self.list = to_streamed_response_wrapper( - info.list, - ) - - -class AsyncInfoResourceWithStreamingResponse: - def __init__(self, info: AsyncInfoResource) -> None: - self._info = info - - self.list = async_to_streamed_response_wrapper( - info.list, - ) diff --git a/pkg/hanzoai/resources/model/model.py b/pkg/hanzoai/resources/model/model.py deleted file mode 100644 index 491747ca2..000000000 --- a/pkg/hanzoai/resources/model/model.py +++ /dev/null @@ -1,342 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -import httpx - -from .info import ( - InfoResource, - AsyncInfoResource, - InfoResourceWithRawResponse, - AsyncInfoResourceWithRawResponse, - InfoResourceWithStreamingResponse, - AsyncInfoResourceWithStreamingResponse, -) -from .update import ( - UpdateResource, - AsyncUpdateResource, - UpdateResourceWithRawResponse, - AsyncUpdateResourceWithRawResponse, - UpdateResourceWithStreamingResponse, - AsyncUpdateResourceWithStreamingResponse, -) -from ...types import model_create_params, model_delete_params -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.model_info_param import ModelInfoParam - -__all__ = ["ModelResource", "AsyncModelResource"] - - -class ModelResource(SyncAPIResource): - @cached_property - def info(self) -> InfoResource: - return InfoResource(self._client) - - @cached_property - def update(self) -> UpdateResource: - return UpdateResource(self._client) - - @cached_property - def with_raw_response(self) -> ModelResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return ModelResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ModelResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return ModelResourceWithStreamingResponse(self) - - def create( - self, - *, - hanzo_params: model_create_params.LitellmParams, - model_info: ModelInfoParam, - model_name: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Allows adding new models to the model list in the config.yaml - - Args: - hanzo_params: Hanzo Params with 'model' requirement - used for completions - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/model/new", - body=maybe_transform( - { - "hanzo_params": hanzo_params, - "model_info": model_info, - "model_name": model_name, - }, - model_create_params.ModelCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - *, - id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Allows deleting models in the model list in the config.yaml - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/model/delete", - body=maybe_transform({"id": id}, model_delete_params.ModelDeleteParams), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncModelResource(AsyncAPIResource): - @cached_property - def info(self) -> AsyncInfoResource: - return AsyncInfoResource(self._client) - - @cached_property - def update(self) -> AsyncUpdateResource: - return AsyncUpdateResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncModelResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncModelResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncModelResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncModelResourceWithStreamingResponse(self) - - async def create( - self, - *, - hanzo_params: model_create_params.LitellmParams, - model_info: ModelInfoParam, - model_name: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Allows adding new models to the model list in the config.yaml - - Args: - hanzo_params: Hanzo Params with 'model' requirement - used for completions - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/model/new", - body=await async_maybe_transform( - { - "hanzo_params": hanzo_params, - "model_info": model_info, - "model_name": model_name, - }, - model_create_params.ModelCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - *, - id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Allows deleting models in the model list in the config.yaml - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/model/delete", - body=await async_maybe_transform( - {"id": id}, model_delete_params.ModelDeleteParams - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class ModelResourceWithRawResponse: - def __init__(self, model: ModelResource) -> None: - self._model = model - - self.create = to_raw_response_wrapper( - model.create, - ) - self.delete = to_raw_response_wrapper( - model.delete, - ) - - @cached_property - def info(self) -> InfoResourceWithRawResponse: - return InfoResourceWithRawResponse(self._model.info) - - @cached_property - def update(self) -> UpdateResourceWithRawResponse: - return UpdateResourceWithRawResponse(self._model.update) - - -class AsyncModelResourceWithRawResponse: - def __init__(self, model: AsyncModelResource) -> None: - self._model = model - - self.create = async_to_raw_response_wrapper( - model.create, - ) - self.delete = async_to_raw_response_wrapper( - model.delete, - ) - - @cached_property - def info(self) -> AsyncInfoResourceWithRawResponse: - return AsyncInfoResourceWithRawResponse(self._model.info) - - @cached_property - def update(self) -> AsyncUpdateResourceWithRawResponse: - return AsyncUpdateResourceWithRawResponse(self._model.update) - - -class ModelResourceWithStreamingResponse: - def __init__(self, model: ModelResource) -> None: - self._model = model - - self.create = to_streamed_response_wrapper( - model.create, - ) - self.delete = to_streamed_response_wrapper( - model.delete, - ) - - @cached_property - def info(self) -> InfoResourceWithStreamingResponse: - return InfoResourceWithStreamingResponse(self._model.info) - - @cached_property - def update(self) -> UpdateResourceWithStreamingResponse: - return UpdateResourceWithStreamingResponse(self._model.update) - - -class AsyncModelResourceWithStreamingResponse: - def __init__(self, model: AsyncModelResource) -> None: - self._model = model - - self.create = async_to_streamed_response_wrapper( - model.create, - ) - self.delete = async_to_streamed_response_wrapper( - model.delete, - ) - - @cached_property - def info(self) -> AsyncInfoResourceWithStreamingResponse: - return AsyncInfoResourceWithStreamingResponse(self._model.info) - - @cached_property - def update(self) -> AsyncUpdateResourceWithStreamingResponse: - return AsyncUpdateResourceWithStreamingResponse(self._model.update) diff --git a/pkg/hanzoai/resources/models.py b/pkg/hanzoai/resources/models.py deleted file mode 100644 index bccf1e07e..000000000 --- a/pkg/hanzoai/resources/models.py +++ /dev/null @@ -1,193 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional - -import httpx - -from ..types import model_list_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - maybe_transform, - async_maybe_transform, -) -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["ModelsResource", "AsyncModelsResource"] - - -class ModelsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ModelsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return ModelsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ModelsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return ModelsResourceWithStreamingResponse(self) - - def list( - self, - *, - return_wildcard_routes: Optional[bool] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Use `/model/info` - to get detailed model information, example - pricing, mode, - etc. - - This is just for compatibility with openai projects like aider. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/v1/models", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "return_wildcard_routes": return_wildcard_routes, - "team_id": team_id, - }, - model_list_params.ModelListParams, - ), - ), - cast_to=object, - ) - - -class AsyncModelsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncModelsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncModelsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncModelsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncModelsResourceWithStreamingResponse(self) - - async def list( - self, - *, - return_wildcard_routes: Optional[bool] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Use `/model/info` - to get detailed model information, example - pricing, mode, - etc. - - This is just for compatibility with openai projects like aider. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/v1/models", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "return_wildcard_routes": return_wildcard_routes, - "team_id": team_id, - }, - model_list_params.ModelListParams, - ), - ), - cast_to=object, - ) - - -class ModelsResourceWithRawResponse: - def __init__(self, models: ModelsResource) -> None: - self._models = models - - self.list = to_raw_response_wrapper( - models.list, - ) - - -class AsyncModelsResourceWithRawResponse: - def __init__(self, models: AsyncModelsResource) -> None: - self._models = models - - self.list = async_to_raw_response_wrapper( - models.list, - ) - - -class ModelsResourceWithStreamingResponse: - def __init__(self, models: ModelsResource) -> None: - self._models = models - - self.list = to_streamed_response_wrapper( - models.list, - ) - - -class AsyncModelsResourceWithStreamingResponse: - def __init__(self, models: AsyncModelsResource) -> None: - self._models = models - - self.list = async_to_streamed_response_wrapper( - models.list, - ) diff --git a/pkg/hanzoai/resources/mpc.py b/pkg/hanzoai/resources/mpc.py deleted file mode 100644 index b6c9106f9..000000000 --- a/pkg/hanzoai/resources/mpc.py +++ /dev/null @@ -1,2130 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["MPCResource", "AsyncMPCResource"] - - -class MPCResource(SyncAPIResource): - """Multi-party computation: CGGMP21/FROST threshold signing, vaults, policies, smart wallets.""" - - @cached_property - def with_raw_response(self) -> MPCResourceWithRawResponse: - return MPCResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> MPCResourceWithStreamingResponse: - return MPCResourceWithStreamingResponse(self) - - # โ”€โ”€ Status โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def status( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get MPC service health and status.""" - return self._get( - "/mpc/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Vaults โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_vaults( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all vaults.""" - return self._get( - "/mpc/vaults", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_vault( - self, - *, - name: str, - description: str | NotGiven = NOT_GIVEN, - type: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new vault.""" - return self._post( - "/mpc/vaults", - body={ - "name": name, - "description": description, - "type": type, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_vault( - self, - vault_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get vault details.""" - return self._get( - f"/mpc/vaults/{vault_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_vault( - self, - vault_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a vault.""" - return self._patch( - f"/mpc/vaults/{vault_id}", - body={ - "name": name, - "description": description, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_vault( - self, - vault_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a vault.""" - return self._delete( - f"/mpc/vaults/{vault_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Wallets (Key Generation Ceremony) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_wallets( - self, - vault_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List wallets in a vault.""" - return self._get( - f"/mpc/vaults/{vault_id}/wallets", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_wallet( - self, - vault_id: str, - *, - name: str, - curve: str | NotGiven = NOT_GIVEN, - threshold: int | NotGiven = NOT_GIVEN, - parties: int | NotGiven = NOT_GIVEN, - protocol: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a wallet and initiate key generation ceremony. - - Args: - vault_id: Vault to create the wallet in. - name: Wallet name. - curve: Elliptic curve - "secp256k1", "ed25519", or "stark". - threshold: Minimum signers required (t of n). - parties: Total number of key shares (n). - protocol: MPC protocol - "cggmp21" or "frost". - """ - return self._post( - f"/mpc/vaults/{vault_id}/wallets", - body={ - "name": name, - "curve": curve, - "threshold": threshold, - "parties": parties, - "protocol": protocol, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_wallet( - self, - wallet_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get wallet details including public key and addresses.""" - return self._get( - f"/mpc/wallets/{wallet_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_wallet( - self, - wallet_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a wallet.""" - return self._delete( - f"/mpc/wallets/{wallet_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Transactions (Signing Ceremony) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_transactions( - self, - *, - wallet_id: str | NotGiven = NOT_GIVEN, - status: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List transactions, optionally filtered by wallet or status.""" - return self._get( - "/mpc/transactions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "wallet_id": wallet_id, - "status": status, - }, - ), - cast_to=object, - ) - - def create_transaction( - self, - *, - wallet_id: str, - type: str, - chain: str | NotGiven = NOT_GIVEN, - to: str | NotGiven = NOT_GIVEN, - amount: str | NotGiven = NOT_GIVEN, - data: str | NotGiven = NOT_GIVEN, - gas_limit: str | NotGiven = NOT_GIVEN, - gas_price: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a signing request. - - Args: - wallet_id: Wallet to sign with. - type: Transaction type - "transfer", "contract_call", "message_sign", or "typed_data". - chain: Target blockchain network. - to: Recipient address. - amount: Transfer amount. - data: Calldata or message payload. - gas_limit: Gas limit for EVM transactions. - gas_price: Gas price for EVM transactions. - """ - return self._post( - "/mpc/transactions", - body={ - "wallet_id": wallet_id, - "type": type, - "chain": chain, - "to": to, - "amount": amount, - "data": data, - "gas_limit": gas_limit, - "gas_price": gas_price, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_transaction( - self, - transaction_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get transaction status and details.""" - return self._get( - f"/mpc/transactions/{transaction_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def approve_transaction( - self, - transaction_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Approve a signing request (as a party).""" - return self._post( - f"/mpc/transactions/{transaction_id}/approve", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def reject_transaction( - self, - transaction_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Reject a signing request.""" - return self._post( - f"/mpc/transactions/{transaction_id}/reject", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def broadcast_transaction( - self, - transaction_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Broadcast a signed transaction to the network.""" - return self._post( - f"/mpc/transactions/{transaction_id}/broadcast", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Policies โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_policies( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List signing policies.""" - return self._get( - "/mpc/policies", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_policy( - self, - *, - name: str, - rules: List[Dict[str, Any]], - vault_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a signing policy. - - Args: - name: Policy name. - rules: Policy rules (spending limits, time locks, whitelist, multi-approval). - vault_id: Vault to attach the policy to. - """ - return self._post( - "/mpc/policies", - body={ - "name": name, - "rules": rules, - "vault_id": vault_id, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_policy( - self, - policy_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get policy details.""" - return self._get( - f"/mpc/policies/{policy_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_policy( - self, - policy_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - rules: List[Dict[str, Any]] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a policy.""" - return self._patch( - f"/mpc/policies/{policy_id}", - body={ - "name": name, - "rules": rules, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_policy( - self, - policy_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a policy.""" - return self._delete( - f"/mpc/policies/{policy_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Webhooks โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_webhooks( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List webhooks.""" - return self._get( - "/mpc/webhooks", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_webhook( - self, - *, - url: str, - events: List[str], - secret: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a webhook. - - Args: - url: Webhook endpoint URL. - events: Events to subscribe to (e.g. "transaction.created", - "transaction.signed", "transaction.broadcast", - "wallet.created", "policy.triggered"). - secret: HMAC secret for webhook signature verification. - """ - return self._post( - "/mpc/webhooks", - body={ - "url": url, - "events": events, - "secret": secret, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_webhook( - self, - webhook_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a webhook.""" - return self._delete( - f"/mpc/webhooks/{webhook_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Smart Wallets (Account Abstraction) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_smart_wallets( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List smart wallets.""" - return self._get( - "/mpc/smart-wallets", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def deploy_smart_wallet( - self, - *, - wallet_id: str, - chain: str, - type: str | NotGiven = NOT_GIVEN, - salt: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Deploy a smart wallet (account abstraction). - - Args: - wallet_id: MPC wallet backing the smart wallet. - chain: Target chain for deployment. - type: Smart wallet type - "safe", "light", or "kernel". - salt: Deterministic deployment salt. - """ - return self._post( - "/mpc/smart-wallets", - body={ - "wallet_id": wallet_id, - "chain": chain, - "type": type, - "salt": salt, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_smart_wallet( - self, - smart_wallet_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get smart wallet details.""" - return self._get( - f"/mpc/smart-wallets/{smart_wallet_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def execute_smart_wallet( - self, - smart_wallet_id: str, - *, - calls: List[Dict[str, Any]], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Execute a user operation on a smart wallet. - - Args: - smart_wallet_id: Smart wallet to execute on. - calls: Batch of calls to execute (to, value, data). - """ - return self._post( - f"/mpc/smart-wallets/{smart_wallet_id}/execute", - body={"calls": calls}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Bridge Signing โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def sign_bridge( - self, - *, - wallet_id: str, - source_chain: str, - dest_chain: str, - token: str, - amount: str, - recipient: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Sign a cross-chain bridge transaction. - - Args: - wallet_id: MPC wallet to sign with. - source_chain: Source chain identifier. - dest_chain: Destination chain identifier. - token: Token address or symbol. - amount: Amount to bridge. - recipient: Recipient address on destination chain. - """ - return self._post( - "/mpc/bridge/sign", - body={ - "wallet_id": wallet_id, - "source_chain": source_chain, - "dest_chain": dest_chain, - "token": token, - "amount": amount, - "recipient": recipient, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def bridge_status( - self, - tx_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get bridge transaction status.""" - return self._get( - f"/mpc/bridge/status/{tx_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ API Keys โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_api_keys( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List API keys.""" - return self._get( - "/mpc/api-keys", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_api_key( - self, - *, - name: str, - permissions: List[str] | NotGiven = NOT_GIVEN, - expires_at: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create an API key. - - Args: - name: Key name. - permissions: List of permissions for the key. - expires_at: ISO 8601 expiration timestamp. - """ - return self._post( - "/mpc/api-keys", - body={ - "name": name, - "permissions": permissions, - "expires_at": expires_at, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_api_key( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Revoke an API key.""" - return self._delete( - f"/mpc/api-keys/{key_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Whitelist โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_whitelist( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List whitelisted addresses.""" - return self._get( - "/mpc/whitelist", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def add_to_whitelist( - self, - *, - address: str, - chain: str, - label: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add an address to the whitelist. - - Args: - address: Blockchain address to whitelist. - chain: Chain the address belongs to. - label: Human-readable label. - """ - return self._post( - "/mpc/whitelist", - body={ - "address": address, - "chain": chain, - "label": label, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def remove_from_whitelist( - self, - whitelist_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove an address from the whitelist.""" - return self._delete( - f"/mpc/whitelist/{whitelist_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncMPCResource(AsyncAPIResource): - """Multi-party computation: CGGMP21/FROST threshold signing, vaults, policies, smart wallets (async).""" - - @cached_property - def with_raw_response(self) -> AsyncMPCResourceWithRawResponse: - return AsyncMPCResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncMPCResourceWithStreamingResponse: - return AsyncMPCResourceWithStreamingResponse(self) - - # โ”€โ”€ Status โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def status( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get MPC service health and status.""" - return await self._get( - "/mpc/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Vaults โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_vaults( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all vaults.""" - return await self._get( - "/mpc/vaults", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_vault( - self, - *, - name: str, - description: str | NotGiven = NOT_GIVEN, - type: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new vault.""" - return await self._post( - "/mpc/vaults", - body={ - "name": name, - "description": description, - "type": type, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_vault( - self, - vault_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get vault details.""" - return await self._get( - f"/mpc/vaults/{vault_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_vault( - self, - vault_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a vault.""" - return await self._patch( - f"/mpc/vaults/{vault_id}", - body={ - "name": name, - "description": description, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_vault( - self, - vault_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a vault.""" - return await self._delete( - f"/mpc/vaults/{vault_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Wallets (Key Generation Ceremony) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_wallets( - self, - vault_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List wallets in a vault.""" - return await self._get( - f"/mpc/vaults/{vault_id}/wallets", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_wallet( - self, - vault_id: str, - *, - name: str, - curve: str | NotGiven = NOT_GIVEN, - threshold: int | NotGiven = NOT_GIVEN, - parties: int | NotGiven = NOT_GIVEN, - protocol: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a wallet and initiate key generation ceremony. - - Args: - vault_id: Vault to create the wallet in. - name: Wallet name. - curve: Elliptic curve - "secp256k1", "ed25519", or "stark". - threshold: Minimum signers required (t of n). - parties: Total number of key shares (n). - protocol: MPC protocol - "cggmp21" or "frost". - """ - return await self._post( - f"/mpc/vaults/{vault_id}/wallets", - body={ - "name": name, - "curve": curve, - "threshold": threshold, - "parties": parties, - "protocol": protocol, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_wallet( - self, - wallet_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get wallet details including public key and addresses.""" - return await self._get( - f"/mpc/wallets/{wallet_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_wallet( - self, - wallet_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a wallet.""" - return await self._delete( - f"/mpc/wallets/{wallet_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Transactions (Signing Ceremony) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_transactions( - self, - *, - wallet_id: str | NotGiven = NOT_GIVEN, - status: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List transactions, optionally filtered by wallet or status.""" - return await self._get( - "/mpc/transactions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "wallet_id": wallet_id, - "status": status, - }, - ), - cast_to=object, - ) - - async def create_transaction( - self, - *, - wallet_id: str, - type: str, - chain: str | NotGiven = NOT_GIVEN, - to: str | NotGiven = NOT_GIVEN, - amount: str | NotGiven = NOT_GIVEN, - data: str | NotGiven = NOT_GIVEN, - gas_limit: str | NotGiven = NOT_GIVEN, - gas_price: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a signing request. - - Args: - wallet_id: Wallet to sign with. - type: Transaction type - "transfer", "contract_call", "message_sign", or "typed_data". - chain: Target blockchain network. - to: Recipient address. - amount: Transfer amount. - data: Calldata or message payload. - gas_limit: Gas limit for EVM transactions. - gas_price: Gas price for EVM transactions. - """ - return await self._post( - "/mpc/transactions", - body={ - "wallet_id": wallet_id, - "type": type, - "chain": chain, - "to": to, - "amount": amount, - "data": data, - "gas_limit": gas_limit, - "gas_price": gas_price, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_transaction( - self, - transaction_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get transaction status and details.""" - return await self._get( - f"/mpc/transactions/{transaction_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def approve_transaction( - self, - transaction_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Approve a signing request (as a party).""" - return await self._post( - f"/mpc/transactions/{transaction_id}/approve", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def reject_transaction( - self, - transaction_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Reject a signing request.""" - return await self._post( - f"/mpc/transactions/{transaction_id}/reject", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def broadcast_transaction( - self, - transaction_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Broadcast a signed transaction to the network.""" - return await self._post( - f"/mpc/transactions/{transaction_id}/broadcast", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Policies โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_policies( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List signing policies.""" - return await self._get( - "/mpc/policies", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_policy( - self, - *, - name: str, - rules: List[Dict[str, Any]], - vault_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a signing policy. - - Args: - name: Policy name. - rules: Policy rules (spending limits, time locks, whitelist, multi-approval). - vault_id: Vault to attach the policy to. - """ - return await self._post( - "/mpc/policies", - body={ - "name": name, - "rules": rules, - "vault_id": vault_id, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_policy( - self, - policy_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get policy details.""" - return await self._get( - f"/mpc/policies/{policy_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_policy( - self, - policy_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - rules: List[Dict[str, Any]] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a policy.""" - return await self._patch( - f"/mpc/policies/{policy_id}", - body={ - "name": name, - "rules": rules, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_policy( - self, - policy_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a policy.""" - return await self._delete( - f"/mpc/policies/{policy_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Webhooks โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_webhooks( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List webhooks.""" - return await self._get( - "/mpc/webhooks", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_webhook( - self, - *, - url: str, - events: List[str], - secret: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a webhook. - - Args: - url: Webhook endpoint URL. - events: Events to subscribe to (e.g. "transaction.created", - "transaction.signed", "transaction.broadcast", - "wallet.created", "policy.triggered"). - secret: HMAC secret for webhook signature verification. - """ - return await self._post( - "/mpc/webhooks", - body={ - "url": url, - "events": events, - "secret": secret, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_webhook( - self, - webhook_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a webhook.""" - return await self._delete( - f"/mpc/webhooks/{webhook_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Smart Wallets (Account Abstraction) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_smart_wallets( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List smart wallets.""" - return await self._get( - "/mpc/smart-wallets", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def deploy_smart_wallet( - self, - *, - wallet_id: str, - chain: str, - type: str | NotGiven = NOT_GIVEN, - salt: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Deploy a smart wallet (account abstraction). - - Args: - wallet_id: MPC wallet backing the smart wallet. - chain: Target chain for deployment. - type: Smart wallet type - "safe", "light", or "kernel". - salt: Deterministic deployment salt. - """ - return await self._post( - "/mpc/smart-wallets", - body={ - "wallet_id": wallet_id, - "chain": chain, - "type": type, - "salt": salt, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_smart_wallet( - self, - smart_wallet_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get smart wallet details.""" - return await self._get( - f"/mpc/smart-wallets/{smart_wallet_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def execute_smart_wallet( - self, - smart_wallet_id: str, - *, - calls: List[Dict[str, Any]], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Execute a user operation on a smart wallet. - - Args: - smart_wallet_id: Smart wallet to execute on. - calls: Batch of calls to execute (to, value, data). - """ - return await self._post( - f"/mpc/smart-wallets/{smart_wallet_id}/execute", - body={"calls": calls}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Bridge Signing โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def sign_bridge( - self, - *, - wallet_id: str, - source_chain: str, - dest_chain: str, - token: str, - amount: str, - recipient: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Sign a cross-chain bridge transaction. - - Args: - wallet_id: MPC wallet to sign with. - source_chain: Source chain identifier. - dest_chain: Destination chain identifier. - token: Token address or symbol. - amount: Amount to bridge. - recipient: Recipient address on destination chain. - """ - return await self._post( - "/mpc/bridge/sign", - body={ - "wallet_id": wallet_id, - "source_chain": source_chain, - "dest_chain": dest_chain, - "token": token, - "amount": amount, - "recipient": recipient, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def bridge_status( - self, - tx_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get bridge transaction status.""" - return await self._get( - f"/mpc/bridge/status/{tx_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ API Keys โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_api_keys( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List API keys.""" - return await self._get( - "/mpc/api-keys", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_api_key( - self, - *, - name: str, - permissions: List[str] | NotGiven = NOT_GIVEN, - expires_at: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create an API key. - - Args: - name: Key name. - permissions: List of permissions for the key. - expires_at: ISO 8601 expiration timestamp. - """ - return await self._post( - "/mpc/api-keys", - body={ - "name": name, - "permissions": permissions, - "expires_at": expires_at, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_api_key( - self, - key_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Revoke an API key.""" - return await self._delete( - f"/mpc/api-keys/{key_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Whitelist โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_whitelist( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List whitelisted addresses.""" - return await self._get( - "/mpc/whitelist", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def add_to_whitelist( - self, - *, - address: str, - chain: str, - label: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add an address to the whitelist. - - Args: - address: Blockchain address to whitelist. - chain: Chain the address belongs to. - label: Human-readable label. - """ - return await self._post( - "/mpc/whitelist", - body={ - "address": address, - "chain": chain, - "label": label, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def remove_from_whitelist( - self, - whitelist_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove an address from the whitelist.""" - return await self._delete( - f"/mpc/whitelist/{whitelist_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class MPCResourceWithRawResponse: - def __init__(self, mpc: MPCResource) -> None: - self._mpc = mpc - # Status - self.status = to_raw_response_wrapper(mpc.status) - # Vaults - self.list_vaults = to_raw_response_wrapper(mpc.list_vaults) - self.create_vault = to_raw_response_wrapper(mpc.create_vault) - self.retrieve_vault = to_raw_response_wrapper(mpc.retrieve_vault) - self.update_vault = to_raw_response_wrapper(mpc.update_vault) - self.delete_vault = to_raw_response_wrapper(mpc.delete_vault) - # Wallets - self.list_wallets = to_raw_response_wrapper(mpc.list_wallets) - self.create_wallet = to_raw_response_wrapper(mpc.create_wallet) - self.retrieve_wallet = to_raw_response_wrapper(mpc.retrieve_wallet) - self.delete_wallet = to_raw_response_wrapper(mpc.delete_wallet) - # Transactions - self.list_transactions = to_raw_response_wrapper(mpc.list_transactions) - self.create_transaction = to_raw_response_wrapper(mpc.create_transaction) - self.retrieve_transaction = to_raw_response_wrapper(mpc.retrieve_transaction) - self.approve_transaction = to_raw_response_wrapper(mpc.approve_transaction) - self.reject_transaction = to_raw_response_wrapper(mpc.reject_transaction) - self.broadcast_transaction = to_raw_response_wrapper(mpc.broadcast_transaction) - # Policies - self.list_policies = to_raw_response_wrapper(mpc.list_policies) - self.create_policy = to_raw_response_wrapper(mpc.create_policy) - self.retrieve_policy = to_raw_response_wrapper(mpc.retrieve_policy) - self.update_policy = to_raw_response_wrapper(mpc.update_policy) - self.delete_policy = to_raw_response_wrapper(mpc.delete_policy) - # Webhooks - self.list_webhooks = to_raw_response_wrapper(mpc.list_webhooks) - self.create_webhook = to_raw_response_wrapper(mpc.create_webhook) - self.delete_webhook = to_raw_response_wrapper(mpc.delete_webhook) - # Smart Wallets - self.list_smart_wallets = to_raw_response_wrapper(mpc.list_smart_wallets) - self.deploy_smart_wallet = to_raw_response_wrapper(mpc.deploy_smart_wallet) - self.retrieve_smart_wallet = to_raw_response_wrapper(mpc.retrieve_smart_wallet) - self.execute_smart_wallet = to_raw_response_wrapper(mpc.execute_smart_wallet) - # Bridge - self.sign_bridge = to_raw_response_wrapper(mpc.sign_bridge) - self.bridge_status = to_raw_response_wrapper(mpc.bridge_status) - # API Keys - self.list_api_keys = to_raw_response_wrapper(mpc.list_api_keys) - self.create_api_key = to_raw_response_wrapper(mpc.create_api_key) - self.delete_api_key = to_raw_response_wrapper(mpc.delete_api_key) - # Whitelist - self.list_whitelist = to_raw_response_wrapper(mpc.list_whitelist) - self.add_to_whitelist = to_raw_response_wrapper(mpc.add_to_whitelist) - self.remove_from_whitelist = to_raw_response_wrapper(mpc.remove_from_whitelist) - - -class AsyncMPCResourceWithRawResponse: - def __init__(self, mpc: AsyncMPCResource) -> None: - self._mpc = mpc - # Status - self.status = async_to_raw_response_wrapper(mpc.status) - # Vaults - self.list_vaults = async_to_raw_response_wrapper(mpc.list_vaults) - self.create_vault = async_to_raw_response_wrapper(mpc.create_vault) - self.retrieve_vault = async_to_raw_response_wrapper(mpc.retrieve_vault) - self.update_vault = async_to_raw_response_wrapper(mpc.update_vault) - self.delete_vault = async_to_raw_response_wrapper(mpc.delete_vault) - # Wallets - self.list_wallets = async_to_raw_response_wrapper(mpc.list_wallets) - self.create_wallet = async_to_raw_response_wrapper(mpc.create_wallet) - self.retrieve_wallet = async_to_raw_response_wrapper(mpc.retrieve_wallet) - self.delete_wallet = async_to_raw_response_wrapper(mpc.delete_wallet) - # Transactions - self.list_transactions = async_to_raw_response_wrapper(mpc.list_transactions) - self.create_transaction = async_to_raw_response_wrapper(mpc.create_transaction) - self.retrieve_transaction = async_to_raw_response_wrapper(mpc.retrieve_transaction) - self.approve_transaction = async_to_raw_response_wrapper(mpc.approve_transaction) - self.reject_transaction = async_to_raw_response_wrapper(mpc.reject_transaction) - self.broadcast_transaction = async_to_raw_response_wrapper(mpc.broadcast_transaction) - # Policies - self.list_policies = async_to_raw_response_wrapper(mpc.list_policies) - self.create_policy = async_to_raw_response_wrapper(mpc.create_policy) - self.retrieve_policy = async_to_raw_response_wrapper(mpc.retrieve_policy) - self.update_policy = async_to_raw_response_wrapper(mpc.update_policy) - self.delete_policy = async_to_raw_response_wrapper(mpc.delete_policy) - # Webhooks - self.list_webhooks = async_to_raw_response_wrapper(mpc.list_webhooks) - self.create_webhook = async_to_raw_response_wrapper(mpc.create_webhook) - self.delete_webhook = async_to_raw_response_wrapper(mpc.delete_webhook) - # Smart Wallets - self.list_smart_wallets = async_to_raw_response_wrapper(mpc.list_smart_wallets) - self.deploy_smart_wallet = async_to_raw_response_wrapper(mpc.deploy_smart_wallet) - self.retrieve_smart_wallet = async_to_raw_response_wrapper(mpc.retrieve_smart_wallet) - self.execute_smart_wallet = async_to_raw_response_wrapper(mpc.execute_smart_wallet) - # Bridge - self.sign_bridge = async_to_raw_response_wrapper(mpc.sign_bridge) - self.bridge_status = async_to_raw_response_wrapper(mpc.bridge_status) - # API Keys - self.list_api_keys = async_to_raw_response_wrapper(mpc.list_api_keys) - self.create_api_key = async_to_raw_response_wrapper(mpc.create_api_key) - self.delete_api_key = async_to_raw_response_wrapper(mpc.delete_api_key) - # Whitelist - self.list_whitelist = async_to_raw_response_wrapper(mpc.list_whitelist) - self.add_to_whitelist = async_to_raw_response_wrapper(mpc.add_to_whitelist) - self.remove_from_whitelist = async_to_raw_response_wrapper(mpc.remove_from_whitelist) - - -class MPCResourceWithStreamingResponse: - def __init__(self, mpc: MPCResource) -> None: - self._mpc = mpc - # Status - self.status = to_streamed_response_wrapper(mpc.status) - # Vaults - self.list_vaults = to_streamed_response_wrapper(mpc.list_vaults) - self.create_vault = to_streamed_response_wrapper(mpc.create_vault) - self.retrieve_vault = to_streamed_response_wrapper(mpc.retrieve_vault) - self.update_vault = to_streamed_response_wrapper(mpc.update_vault) - self.delete_vault = to_streamed_response_wrapper(mpc.delete_vault) - # Wallets - self.list_wallets = to_streamed_response_wrapper(mpc.list_wallets) - self.create_wallet = to_streamed_response_wrapper(mpc.create_wallet) - self.retrieve_wallet = to_streamed_response_wrapper(mpc.retrieve_wallet) - self.delete_wallet = to_streamed_response_wrapper(mpc.delete_wallet) - # Transactions - self.list_transactions = to_streamed_response_wrapper(mpc.list_transactions) - self.create_transaction = to_streamed_response_wrapper(mpc.create_transaction) - self.retrieve_transaction = to_streamed_response_wrapper(mpc.retrieve_transaction) - self.approve_transaction = to_streamed_response_wrapper(mpc.approve_transaction) - self.reject_transaction = to_streamed_response_wrapper(mpc.reject_transaction) - self.broadcast_transaction = to_streamed_response_wrapper(mpc.broadcast_transaction) - # Policies - self.list_policies = to_streamed_response_wrapper(mpc.list_policies) - self.create_policy = to_streamed_response_wrapper(mpc.create_policy) - self.retrieve_policy = to_streamed_response_wrapper(mpc.retrieve_policy) - self.update_policy = to_streamed_response_wrapper(mpc.update_policy) - self.delete_policy = to_streamed_response_wrapper(mpc.delete_policy) - # Webhooks - self.list_webhooks = to_streamed_response_wrapper(mpc.list_webhooks) - self.create_webhook = to_streamed_response_wrapper(mpc.create_webhook) - self.delete_webhook = to_streamed_response_wrapper(mpc.delete_webhook) - # Smart Wallets - self.list_smart_wallets = to_streamed_response_wrapper(mpc.list_smart_wallets) - self.deploy_smart_wallet = to_streamed_response_wrapper(mpc.deploy_smart_wallet) - self.retrieve_smart_wallet = to_streamed_response_wrapper(mpc.retrieve_smart_wallet) - self.execute_smart_wallet = to_streamed_response_wrapper(mpc.execute_smart_wallet) - # Bridge - self.sign_bridge = to_streamed_response_wrapper(mpc.sign_bridge) - self.bridge_status = to_streamed_response_wrapper(mpc.bridge_status) - # API Keys - self.list_api_keys = to_streamed_response_wrapper(mpc.list_api_keys) - self.create_api_key = to_streamed_response_wrapper(mpc.create_api_key) - self.delete_api_key = to_streamed_response_wrapper(mpc.delete_api_key) - # Whitelist - self.list_whitelist = to_streamed_response_wrapper(mpc.list_whitelist) - self.add_to_whitelist = to_streamed_response_wrapper(mpc.add_to_whitelist) - self.remove_from_whitelist = to_streamed_response_wrapper(mpc.remove_from_whitelist) - - -class AsyncMPCResourceWithStreamingResponse: - def __init__(self, mpc: AsyncMPCResource) -> None: - self._mpc = mpc - # Status - self.status = async_to_streamed_response_wrapper(mpc.status) - # Vaults - self.list_vaults = async_to_streamed_response_wrapper(mpc.list_vaults) - self.create_vault = async_to_streamed_response_wrapper(mpc.create_vault) - self.retrieve_vault = async_to_streamed_response_wrapper(mpc.retrieve_vault) - self.update_vault = async_to_streamed_response_wrapper(mpc.update_vault) - self.delete_vault = async_to_streamed_response_wrapper(mpc.delete_vault) - # Wallets - self.list_wallets = async_to_streamed_response_wrapper(mpc.list_wallets) - self.create_wallet = async_to_streamed_response_wrapper(mpc.create_wallet) - self.retrieve_wallet = async_to_streamed_response_wrapper(mpc.retrieve_wallet) - self.delete_wallet = async_to_streamed_response_wrapper(mpc.delete_wallet) - # Transactions - self.list_transactions = async_to_streamed_response_wrapper(mpc.list_transactions) - self.create_transaction = async_to_streamed_response_wrapper(mpc.create_transaction) - self.retrieve_transaction = async_to_streamed_response_wrapper(mpc.retrieve_transaction) - self.approve_transaction = async_to_streamed_response_wrapper(mpc.approve_transaction) - self.reject_transaction = async_to_streamed_response_wrapper(mpc.reject_transaction) - self.broadcast_transaction = async_to_streamed_response_wrapper(mpc.broadcast_transaction) - # Policies - self.list_policies = async_to_streamed_response_wrapper(mpc.list_policies) - self.create_policy = async_to_streamed_response_wrapper(mpc.create_policy) - self.retrieve_policy = async_to_streamed_response_wrapper(mpc.retrieve_policy) - self.update_policy = async_to_streamed_response_wrapper(mpc.update_policy) - self.delete_policy = async_to_streamed_response_wrapper(mpc.delete_policy) - # Webhooks - self.list_webhooks = async_to_streamed_response_wrapper(mpc.list_webhooks) - self.create_webhook = async_to_streamed_response_wrapper(mpc.create_webhook) - self.delete_webhook = async_to_streamed_response_wrapper(mpc.delete_webhook) - # Smart Wallets - self.list_smart_wallets = async_to_streamed_response_wrapper(mpc.list_smart_wallets) - self.deploy_smart_wallet = async_to_streamed_response_wrapper(mpc.deploy_smart_wallet) - self.retrieve_smart_wallet = async_to_streamed_response_wrapper(mpc.retrieve_smart_wallet) - self.execute_smart_wallet = async_to_streamed_response_wrapper(mpc.execute_smart_wallet) - # Bridge - self.sign_bridge = async_to_streamed_response_wrapper(mpc.sign_bridge) - self.bridge_status = async_to_streamed_response_wrapper(mpc.bridge_status) - # API Keys - self.list_api_keys = async_to_streamed_response_wrapper(mpc.list_api_keys) - self.create_api_key = async_to_streamed_response_wrapper(mpc.create_api_key) - self.delete_api_key = async_to_streamed_response_wrapper(mpc.delete_api_key) - # Whitelist - self.list_whitelist = async_to_streamed_response_wrapper(mpc.list_whitelist) - self.add_to_whitelist = async_to_streamed_response_wrapper(mpc.add_to_whitelist) - self.remove_from_whitelist = async_to_streamed_response_wrapper(mpc.remove_from_whitelist) diff --git a/pkg/hanzoai/resources/network.py b/pkg/hanzoai/resources/network.py deleted file mode 100644 index fdd5d146f..000000000 --- a/pkg/hanzoai/resources/network.py +++ /dev/null @@ -1,287 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["NetworkResource", "AsyncNetworkResource"] - - -class NetworkResource(SyncAPIResource): - """Blockchain network management.""" - - @cached_property - def with_raw_response(self) -> NetworkResourceWithRawResponse: - return NetworkResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> NetworkResourceWithStreamingResponse: - return NetworkResourceWithStreamingResponse(self) - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all networks.""" - return self._get( - "/network/networks", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - network_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific network.""" - return self._get( - f"/network/networks/{network_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def status( - self, - network_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get network status.""" - return self._get( - f"/network/networks/{network_id}/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stats( - self, - network_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get network statistics.""" - return self._get( - f"/network/networks/{network_id}/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def peers( - self, - network_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List network peers.""" - return self._get( - f"/network/networks/{network_id}/peers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncNetworkResource(AsyncAPIResource): - """Blockchain network management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncNetworkResourceWithRawResponse: - return AsyncNetworkResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncNetworkResourceWithStreamingResponse: - return AsyncNetworkResourceWithStreamingResponse(self) - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/network/networks", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - network_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/network/networks/{network_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def status( - self, - network_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/network/networks/{network_id}/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stats( - self, - network_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/network/networks/{network_id}/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def peers( - self, - network_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/network/networks/{network_id}/peers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class NetworkResourceWithRawResponse: - def __init__(self, network: NetworkResource) -> None: - self._network = network - self.list = to_raw_response_wrapper(network.list) - self.get = to_raw_response_wrapper(network.get) - self.status = to_raw_response_wrapper(network.status) - self.stats = to_raw_response_wrapper(network.stats) - self.peers = to_raw_response_wrapper(network.peers) - - -class AsyncNetworkResourceWithRawResponse: - def __init__(self, network: AsyncNetworkResource) -> None: - self._network = network - self.list = async_to_raw_response_wrapper(network.list) - self.get = async_to_raw_response_wrapper(network.get) - self.status = async_to_raw_response_wrapper(network.status) - self.stats = async_to_raw_response_wrapper(network.stats) - self.peers = async_to_raw_response_wrapper(network.peers) - - -class NetworkResourceWithStreamingResponse: - def __init__(self, network: NetworkResource) -> None: - self._network = network - self.list = to_streamed_response_wrapper(network.list) - self.get = to_streamed_response_wrapper(network.get) - self.status = to_streamed_response_wrapper(network.status) - self.stats = to_streamed_response_wrapper(network.stats) - self.peers = to_streamed_response_wrapper(network.peers) - - -class AsyncNetworkResourceWithStreamingResponse: - def __init__(self, network: AsyncNetworkResource) -> None: - self._network = network - self.list = async_to_streamed_response_wrapper(network.list) - self.get = async_to_streamed_response_wrapper(network.get) - self.status = async_to_streamed_response_wrapper(network.status) - self.stats = async_to_streamed_response_wrapper(network.stats) - self.peers = async_to_streamed_response_wrapper(network.peers) diff --git a/pkg/hanzoai/resources/nodes.py b/pkg/hanzoai/resources/nodes.py deleted file mode 100644 index 18c15eb34..000000000 --- a/pkg/hanzoai/resources/nodes.py +++ /dev/null @@ -1,438 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["NodesResource", "AsyncNodesResource"] - - -class NodesResource(SyncAPIResource): - """Blockchain node management.""" - - @cached_property - def with_raw_response(self) -> NodesResourceWithRawResponse: - return NodesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> NodesResourceWithStreamingResponse: - return NodesResourceWithStreamingResponse(self) - - def list( - self, - *, - network_id: str | NotGiven = NOT_GIVEN, - status: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all nodes.""" - return self._get( - "/network/nodes", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"network_id": network_id, "status": status}, - ), - cast_to=object, - ) - - def get( - self, - node_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific node.""" - return self._get( - f"/network/nodes/{node_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - network_id: str, - type: str, - config: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new node.""" - return self._post( - "/network/nodes", - body={"network_id": network_id, "type": type, "config": config}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - node_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a node.""" - return self._delete( - f"/network/nodes/{node_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def start( - self, - node_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Start a node.""" - return self._post( - f"/network/nodes/{node_id}/start", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stop( - self, - node_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Stop a node.""" - return self._post( - f"/network/nodes/{node_id}/stop", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def logs( - self, - node_id: str, - *, - lines: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get node logs.""" - return self._get( - f"/network/nodes/{node_id}/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"lines": lines}, - ), - cast_to=object, - ) - - def stats( - self, - node_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get node statistics.""" - return self._get( - f"/network/nodes/{node_id}/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncNodesResource(AsyncAPIResource): - """Blockchain node management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncNodesResourceWithRawResponse: - return AsyncNodesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncNodesResourceWithStreamingResponse: - return AsyncNodesResourceWithStreamingResponse(self) - - async def list( - self, - *, - network_id: str | NotGiven = NOT_GIVEN, - status: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/network/nodes", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"network_id": network_id, "status": status}, - ), - cast_to=object, - ) - - async def get( - self, - node_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/network/nodes/{node_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - network_id: str, - type: str, - config: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/network/nodes", - body={"network_id": network_id, "type": type, "config": config}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - node_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/network/nodes/{node_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def start( - self, - node_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/network/nodes/{node_id}/start", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stop( - self, - node_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/network/nodes/{node_id}/stop", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def logs( - self, - node_id: str, - *, - lines: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/network/nodes/{node_id}/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"lines": lines}, - ), - cast_to=object, - ) - - async def stats( - self, - node_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/network/nodes/{node_id}/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class NodesResourceWithRawResponse: - def __init__(self, nodes: NodesResource) -> None: - self._nodes = nodes - self.list = to_raw_response_wrapper(nodes.list) - self.get = to_raw_response_wrapper(nodes.get) - self.create = to_raw_response_wrapper(nodes.create) - self.delete = to_raw_response_wrapper(nodes.delete) - self.start = to_raw_response_wrapper(nodes.start) - self.stop = to_raw_response_wrapper(nodes.stop) - self.logs = to_raw_response_wrapper(nodes.logs) - self.stats = to_raw_response_wrapper(nodes.stats) - - -class AsyncNodesResourceWithRawResponse: - def __init__(self, nodes: AsyncNodesResource) -> None: - self._nodes = nodes - self.list = async_to_raw_response_wrapper(nodes.list) - self.get = async_to_raw_response_wrapper(nodes.get) - self.create = async_to_raw_response_wrapper(nodes.create) - self.delete = async_to_raw_response_wrapper(nodes.delete) - self.start = async_to_raw_response_wrapper(nodes.start) - self.stop = async_to_raw_response_wrapper(nodes.stop) - self.logs = async_to_raw_response_wrapper(nodes.logs) - self.stats = async_to_raw_response_wrapper(nodes.stats) - - -class NodesResourceWithStreamingResponse: - def __init__(self, nodes: NodesResource) -> None: - self._nodes = nodes - self.list = to_streamed_response_wrapper(nodes.list) - self.get = to_streamed_response_wrapper(nodes.get) - self.create = to_streamed_response_wrapper(nodes.create) - self.delete = to_streamed_response_wrapper(nodes.delete) - self.start = to_streamed_response_wrapper(nodes.start) - self.stop = to_streamed_response_wrapper(nodes.stop) - self.logs = to_streamed_response_wrapper(nodes.logs) - self.stats = to_streamed_response_wrapper(nodes.stats) - - -class AsyncNodesResourceWithStreamingResponse: - def __init__(self, nodes: AsyncNodesResource) -> None: - self._nodes = nodes - self.list = async_to_streamed_response_wrapper(nodes.list) - self.get = async_to_streamed_response_wrapper(nodes.get) - self.create = async_to_streamed_response_wrapper(nodes.create) - self.delete = async_to_streamed_response_wrapper(nodes.delete) - self.start = async_to_streamed_response_wrapper(nodes.start) - self.stop = async_to_streamed_response_wrapper(nodes.stop) - self.logs = async_to_streamed_response_wrapper(nodes.logs) - self.stats = async_to_streamed_response_wrapper(nodes.stats) diff --git a/pkg/hanzoai/resources/observability.py b/pkg/hanzoai/resources/observability.py deleted file mode 100644 index d729f3af8..000000000 --- a/pkg/hanzoai/resources/observability.py +++ /dev/null @@ -1,463 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["ObservabilityResource", "AsyncObservabilityResource"] - - -class ObservabilityResource(SyncAPIResource): - """Metrics, logs, and tracing.""" - - @cached_property - def with_raw_response(self) -> ObservabilityResourceWithRawResponse: - return ObservabilityResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ObservabilityResourceWithStreamingResponse: - return ObservabilityResourceWithStreamingResponse(self) - - def metrics( - self, - *, - names: List[str] | NotGiven = NOT_GIVEN, - start_time: str | NotGiven = NOT_GIVEN, - end_time: str | NotGiven = NOT_GIVEN, - step: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Query metrics.""" - return self._get( - "/operations/observability/metrics", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "names": names, - "start_time": start_time, - "end_time": end_time, - "step": step, - }, - ), - cast_to=object, - ) - - def logs( - self, - *, - query: str | NotGiven = NOT_GIVEN, - start_time: str | NotGiven = NOT_GIVEN, - end_time: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Query logs.""" - return self._get( - "/operations/observability/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "query": query, - "start_time": start_time, - "end_time": end_time, - "limit": limit, - }, - ), - cast_to=object, - ) - - def traces( - self, - *, - trace_id: str | NotGiven = NOT_GIVEN, - service: str | NotGiven = NOT_GIVEN, - start_time: str | NotGiven = NOT_GIVEN, - end_time: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Query traces.""" - return self._get( - "/operations/observability/traces", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "trace_id": trace_id, - "service": service, - "start_time": start_time, - "end_time": end_time, - "limit": limit, - }, - ), - cast_to=object, - ) - - def get_trace( - self, - trace_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific trace.""" - return self._get( - f"/operations/observability/traces/{trace_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def alerts( - self, - *, - status: str | NotGiven = NOT_GIVEN, - severity: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List alerts.""" - return self._get( - "/operations/observability/alerts", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"status": status, "severity": severity, "limit": limit}, - ), - cast_to=object, - ) - - def create_alert( - self, - *, - name: str, - query: str, - threshold: float, - severity: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create an alert rule.""" - return self._post( - "/operations/observability/alerts", - body={ - "name": name, - "query": query, - "threshold": threshold, - "severity": severity, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def health( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get system health status.""" - return self._get( - "/operations/observability/health", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncObservabilityResource(AsyncAPIResource): - """Metrics, logs, and tracing (async).""" - - @cached_property - def with_raw_response(self) -> AsyncObservabilityResourceWithRawResponse: - return AsyncObservabilityResourceWithRawResponse(self) - - @cached_property - def with_streaming_response( - self, - ) -> AsyncObservabilityResourceWithStreamingResponse: - return AsyncObservabilityResourceWithStreamingResponse(self) - - async def metrics( - self, - *, - names: List[str] | NotGiven = NOT_GIVEN, - start_time: str | NotGiven = NOT_GIVEN, - end_time: str | NotGiven = NOT_GIVEN, - step: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/operations/observability/metrics", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "names": names, - "start_time": start_time, - "end_time": end_time, - "step": step, - }, - ), - cast_to=object, - ) - - async def logs( - self, - *, - query: str | NotGiven = NOT_GIVEN, - start_time: str | NotGiven = NOT_GIVEN, - end_time: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/operations/observability/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "query": query, - "start_time": start_time, - "end_time": end_time, - "limit": limit, - }, - ), - cast_to=object, - ) - - async def traces( - self, - *, - trace_id: str | NotGiven = NOT_GIVEN, - service: str | NotGiven = NOT_GIVEN, - start_time: str | NotGiven = NOT_GIVEN, - end_time: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/operations/observability/traces", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "trace_id": trace_id, - "service": service, - "start_time": start_time, - "end_time": end_time, - "limit": limit, - }, - ), - cast_to=object, - ) - - async def get_trace( - self, - trace_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/operations/observability/traces/{trace_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def alerts( - self, - *, - status: str | NotGiven = NOT_GIVEN, - severity: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/operations/observability/alerts", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"status": status, "severity": severity, "limit": limit}, - ), - cast_to=object, - ) - - async def create_alert( - self, - *, - name: str, - query: str, - threshold: float, - severity: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/operations/observability/alerts", - body={ - "name": name, - "query": query, - "threshold": threshold, - "severity": severity, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def health( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/operations/observability/health", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class ObservabilityResourceWithRawResponse: - def __init__(self, observability: ObservabilityResource) -> None: - self._observability = observability - self.metrics = to_raw_response_wrapper(observability.metrics) - self.logs = to_raw_response_wrapper(observability.logs) - self.traces = to_raw_response_wrapper(observability.traces) - self.get_trace = to_raw_response_wrapper(observability.get_trace) - self.alerts = to_raw_response_wrapper(observability.alerts) - self.create_alert = to_raw_response_wrapper(observability.create_alert) - self.health = to_raw_response_wrapper(observability.health) - - -class AsyncObservabilityResourceWithRawResponse: - def __init__(self, observability: AsyncObservabilityResource) -> None: - self._observability = observability - self.metrics = async_to_raw_response_wrapper(observability.metrics) - self.logs = async_to_raw_response_wrapper(observability.logs) - self.traces = async_to_raw_response_wrapper(observability.traces) - self.get_trace = async_to_raw_response_wrapper(observability.get_trace) - self.alerts = async_to_raw_response_wrapper(observability.alerts) - self.create_alert = async_to_raw_response_wrapper(observability.create_alert) - self.health = async_to_raw_response_wrapper(observability.health) - - -class ObservabilityResourceWithStreamingResponse: - def __init__(self, observability: ObservabilityResource) -> None: - self._observability = observability - self.metrics = to_streamed_response_wrapper(observability.metrics) - self.logs = to_streamed_response_wrapper(observability.logs) - self.traces = to_streamed_response_wrapper(observability.traces) - self.get_trace = to_streamed_response_wrapper(observability.get_trace) - self.alerts = to_streamed_response_wrapper(observability.alerts) - self.create_alert = to_streamed_response_wrapper(observability.create_alert) - self.health = to_streamed_response_wrapper(observability.health) - - -class AsyncObservabilityResourceWithStreamingResponse: - def __init__(self, observability: AsyncObservabilityResource) -> None: - self._observability = observability - self.metrics = async_to_streamed_response_wrapper(observability.metrics) - self.logs = async_to_streamed_response_wrapper(observability.logs) - self.traces = async_to_streamed_response_wrapper(observability.traces) - self.get_trace = async_to_streamed_response_wrapper(observability.get_trace) - self.alerts = async_to_streamed_response_wrapper(observability.alerts) - self.create_alert = async_to_streamed_response_wrapper( - observability.create_alert - ) - self.health = async_to_streamed_response_wrapper(observability.health) diff --git a/pkg/hanzoai/resources/openai/__init__.py b/pkg/hanzoai/resources/openai/__init__.py deleted file mode 100644 index b79046ca4..000000000 --- a/pkg/hanzoai/resources/openai/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Hanzo AI SDK - -from .openai import ( - OpenAIResource, - AsyncOpenAIResource, - OpenAIResourceWithRawResponse, - AsyncOpenAIResourceWithRawResponse, - OpenAIResourceWithStreamingResponse, - AsyncOpenAIResourceWithStreamingResponse, -) -from .deployments import ( - DeploymentsResource, - AsyncDeploymentsResource, - DeploymentsResourceWithRawResponse, - AsyncDeploymentsResourceWithRawResponse, - DeploymentsResourceWithStreamingResponse, - AsyncDeploymentsResourceWithStreamingResponse, -) - -__all__ = [ - "DeploymentsResource", - "AsyncDeploymentsResource", - "DeploymentsResourceWithRawResponse", - "AsyncDeploymentsResourceWithRawResponse", - "DeploymentsResourceWithStreamingResponse", - "AsyncDeploymentsResourceWithStreamingResponse", - "OpenAIResource", - "AsyncOpenAIResource", - "OpenAIResourceWithRawResponse", - "AsyncOpenAIResourceWithRawResponse", - "OpenAIResourceWithStreamingResponse", - "AsyncOpenAIResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/openai/deployments/__init__.py b/pkg/hanzoai/resources/openai/deployments/__init__.py deleted file mode 100644 index 457805415..000000000 --- a/pkg/hanzoai/resources/openai/deployments/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Hanzo AI SDK - -from .chat import ( - ChatResource, - AsyncChatResource, - ChatResourceWithRawResponse, - AsyncChatResourceWithRawResponse, - ChatResourceWithStreamingResponse, - AsyncChatResourceWithStreamingResponse, -) -from .deployments import ( - DeploymentsResource, - AsyncDeploymentsResource, - DeploymentsResourceWithRawResponse, - AsyncDeploymentsResourceWithRawResponse, - DeploymentsResourceWithStreamingResponse, - AsyncDeploymentsResourceWithStreamingResponse, -) - -__all__ = [ - "ChatResource", - "AsyncChatResource", - "ChatResourceWithRawResponse", - "AsyncChatResourceWithRawResponse", - "ChatResourceWithStreamingResponse", - "AsyncChatResourceWithStreamingResponse", - "DeploymentsResource", - "AsyncDeploymentsResource", - "DeploymentsResourceWithRawResponse", - "AsyncDeploymentsResourceWithRawResponse", - "DeploymentsResourceWithStreamingResponse", - "AsyncDeploymentsResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/openai/deployments/chat.py b/pkg/hanzoai/resources/openai/deployments/chat.py deleted file mode 100644 index 9bae5725d..000000000 --- a/pkg/hanzoai/resources/openai/deployments/chat.py +++ /dev/null @@ -1,204 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -import httpx - -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...._base_client import make_request_options - -__all__ = ["ChatResource", "AsyncChatResource"] - - -class ChatResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ChatResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return ChatResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ChatResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return ChatResourceWithStreamingResponse(self) - - def complete( - self, - model: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Follows the exact same API spec as - `OpenAI's Chat API https://platform.openai.com/docs/api-reference/chat` - - ```bash - curl -X POST http://localhost:4000/v1/chat/completions - -H "Content-Type: application/json" - -H "Authorization: Bearer sk-1234" - -d '{ - "model": "gpt-4o", - "messages": [ - { - "role": "user", - "content": "Hello!" - } - ] - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not model: - raise ValueError( - f"Expected a non-empty value for `model` but received {model!r}" - ) - return self._post( - f"/openai/deployments/{model}/chat/completions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncChatResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncChatResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncChatResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncChatResourceWithStreamingResponse(self) - - async def complete( - self, - model: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Follows the exact same API spec as - `OpenAI's Chat API https://platform.openai.com/docs/api-reference/chat` - - ```bash - curl -X POST http://localhost:4000/v1/chat/completions - -H "Content-Type: application/json" - -H "Authorization: Bearer sk-1234" - -d '{ - "model": "gpt-4o", - "messages": [ - { - "role": "user", - "content": "Hello!" - } - ] - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not model: - raise ValueError( - f"Expected a non-empty value for `model` but received {model!r}" - ) - return await self._post( - f"/openai/deployments/{model}/chat/completions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class ChatResourceWithRawResponse: - def __init__(self, chat: ChatResource) -> None: - self._chat = chat - - self.complete = to_raw_response_wrapper( - chat.complete, - ) - - -class AsyncChatResourceWithRawResponse: - def __init__(self, chat: AsyncChatResource) -> None: - self._chat = chat - - self.complete = async_to_raw_response_wrapper( - chat.complete, - ) - - -class ChatResourceWithStreamingResponse: - def __init__(self, chat: ChatResource) -> None: - self._chat = chat - - self.complete = to_streamed_response_wrapper( - chat.complete, - ) - - -class AsyncChatResourceWithStreamingResponse: - def __init__(self, chat: AsyncChatResource) -> None: - self._chat = chat - - self.complete = async_to_streamed_response_wrapper( - chat.complete, - ) diff --git a/pkg/hanzoai/resources/openai/deployments/deployments.py b/pkg/hanzoai/resources/openai/deployments/deployments.py deleted file mode 100644 index 95df276ef..000000000 --- a/pkg/hanzoai/resources/openai/deployments/deployments.py +++ /dev/null @@ -1,340 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -import httpx - -from .chat import ( - ChatResource, - AsyncChatResource, - ChatResourceWithRawResponse, - AsyncChatResourceWithRawResponse, - ChatResourceWithStreamingResponse, - AsyncChatResourceWithStreamingResponse, -) -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...._base_client import make_request_options - -__all__ = ["DeploymentsResource", "AsyncDeploymentsResource"] - - -class DeploymentsResource(SyncAPIResource): - @cached_property - def chat(self) -> ChatResource: - return ChatResource(self._client) - - @cached_property - def with_raw_response(self) -> DeploymentsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return DeploymentsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> DeploymentsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return DeploymentsResourceWithStreamingResponse(self) - - def complete( - self, - model: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Follows the exact same API spec as - `OpenAI's Completions API https://platform.openai.com/docs/api-reference/completions` - - ```bash - curl -X POST http://localhost:4000/v1/completions - -H "Content-Type: application/json" - -H "Authorization: Bearer sk-1234" - -d '{ - "model": "gpt-3.5-turbo-instruct", - "prompt": "Once upon a time", - "max_tokens": 50, - "temperature": 0.7 - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not model: - raise ValueError( - f"Expected a non-empty value for `model` but received {model!r}" - ) - return self._post( - f"/openai/deployments/{model}/completions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def embed( - self, - model: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Follows the exact same API spec as - `OpenAI's Embeddings API https://platform.openai.com/docs/api-reference/embeddings` - - ```bash - curl -X POST http://localhost:4000/v1/embeddings - -H "Content-Type: application/json" - -H "Authorization: Bearer sk-1234" - -d '{ - "model": "text-embedding-ada-002", - "input": "The quick brown fox jumps over the lazy dog" - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not model: - raise ValueError( - f"Expected a non-empty value for `model` but received {model!r}" - ) - return self._post( - f"/openai/deployments/{model}/embeddings", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncDeploymentsResource(AsyncAPIResource): - @cached_property - def chat(self) -> AsyncChatResource: - return AsyncChatResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncDeploymentsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncDeploymentsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncDeploymentsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncDeploymentsResourceWithStreamingResponse(self) - - async def complete( - self, - model: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Follows the exact same API spec as - `OpenAI's Completions API https://platform.openai.com/docs/api-reference/completions` - - ```bash - curl -X POST http://localhost:4000/v1/completions - -H "Content-Type: application/json" - -H "Authorization: Bearer sk-1234" - -d '{ - "model": "gpt-3.5-turbo-instruct", - "prompt": "Once upon a time", - "max_tokens": 50, - "temperature": 0.7 - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not model: - raise ValueError( - f"Expected a non-empty value for `model` but received {model!r}" - ) - return await self._post( - f"/openai/deployments/{model}/completions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def embed( - self, - model: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Follows the exact same API spec as - `OpenAI's Embeddings API https://platform.openai.com/docs/api-reference/embeddings` - - ```bash - curl -X POST http://localhost:4000/v1/embeddings - -H "Content-Type: application/json" - -H "Authorization: Bearer sk-1234" - -d '{ - "model": "text-embedding-ada-002", - "input": "The quick brown fox jumps over the lazy dog" - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not model: - raise ValueError( - f"Expected a non-empty value for `model` but received {model!r}" - ) - return await self._post( - f"/openai/deployments/{model}/embeddings", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class DeploymentsResourceWithRawResponse: - def __init__(self, deployments: DeploymentsResource) -> None: - self._deployments = deployments - - self.complete = to_raw_response_wrapper( - deployments.complete, - ) - self.embed = to_raw_response_wrapper( - deployments.embed, - ) - - @cached_property - def chat(self) -> ChatResourceWithRawResponse: - return ChatResourceWithRawResponse(self._deployments.chat) - - -class AsyncDeploymentsResourceWithRawResponse: - def __init__(self, deployments: AsyncDeploymentsResource) -> None: - self._deployments = deployments - - self.complete = async_to_raw_response_wrapper( - deployments.complete, - ) - self.embed = async_to_raw_response_wrapper( - deployments.embed, - ) - - @cached_property - def chat(self) -> AsyncChatResourceWithRawResponse: - return AsyncChatResourceWithRawResponse(self._deployments.chat) - - -class DeploymentsResourceWithStreamingResponse: - def __init__(self, deployments: DeploymentsResource) -> None: - self._deployments = deployments - - self.complete = to_streamed_response_wrapper( - deployments.complete, - ) - self.embed = to_streamed_response_wrapper( - deployments.embed, - ) - - @cached_property - def chat(self) -> ChatResourceWithStreamingResponse: - return ChatResourceWithStreamingResponse(self._deployments.chat) - - -class AsyncDeploymentsResourceWithStreamingResponse: - def __init__(self, deployments: AsyncDeploymentsResource) -> None: - self._deployments = deployments - - self.complete = async_to_streamed_response_wrapper( - deployments.complete, - ) - self.embed = async_to_streamed_response_wrapper( - deployments.embed, - ) - - @cached_property - def chat(self) -> AsyncChatResourceWithStreamingResponse: - return AsyncChatResourceWithStreamingResponse(self._deployments.chat) diff --git a/pkg/hanzoai/resources/orders.py b/pkg/hanzoai/resources/orders.py deleted file mode 100644 index fc8059291..000000000 --- a/pkg/hanzoai/resources/orders.py +++ /dev/null @@ -1,415 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["OrdersResource", "AsyncOrdersResource"] - - -class OrdersResource(SyncAPIResource): - """Order management.""" - - @cached_property - def with_raw_response(self) -> OrdersResourceWithRawResponse: - return OrdersResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> OrdersResourceWithStreamingResponse: - return OrdersResourceWithStreamingResponse(self) - - def list( - self, - *, - status: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all orders.""" - return self._get( - "/commerce/orders", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"status": status, "limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - def get( - self, - order_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific order.""" - return self._get( - f"/commerce/orders/{order_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - cart_id: str, - shipping_address: Dict[str, Any], - billing_address: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new order.""" - return self._post( - "/commerce/orders", - body={ - "cart_id": cart_id, - "shipping_address": shipping_address, - "billing_address": billing_address, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - order_id: str, - *, - status: str | NotGiven = NOT_GIVEN, - tracking_number: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an order.""" - return self._put( - f"/commerce/orders/{order_id}", - body={"status": status, "tracking_number": tracking_number}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def cancel( - self, - order_id: str, - *, - reason: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Cancel an order.""" - return self._post( - f"/commerce/orders/{order_id}/cancel", - body={"reason": reason}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def refund( - self, - order_id: str, - *, - amount: float | NotGiven = NOT_GIVEN, - reason: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Refund an order.""" - return self._post( - f"/commerce/orders/{order_id}/refund", - body={"amount": amount, "reason": reason}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_items( - self, - order_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List order items.""" - return self._get( - f"/commerce/orders/{order_id}/items", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncOrdersResource(AsyncAPIResource): - """Order management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncOrdersResourceWithRawResponse: - return AsyncOrdersResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncOrdersResourceWithStreamingResponse: - return AsyncOrdersResourceWithStreamingResponse(self) - - async def list( - self, - *, - status: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/orders", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"status": status, "limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - async def get( - self, - order_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/orders/{order_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - cart_id: str, - shipping_address: Dict[str, Any], - billing_address: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/orders", - body={ - "cart_id": cart_id, - "shipping_address": shipping_address, - "billing_address": billing_address, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - order_id: str, - *, - status: str | NotGiven = NOT_GIVEN, - tracking_number: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._put( - f"/commerce/orders/{order_id}", - body={"status": status, "tracking_number": tracking_number}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def cancel( - self, - order_id: str, - *, - reason: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/commerce/orders/{order_id}/cancel", - body={"reason": reason}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def refund( - self, - order_id: str, - *, - amount: float | NotGiven = NOT_GIVEN, - reason: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/commerce/orders/{order_id}/refund", - body={"amount": amount, "reason": reason}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_items( - self, - order_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/orders/{order_id}/items", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class OrdersResourceWithRawResponse: - def __init__(self, orders: OrdersResource) -> None: - self._orders = orders - self.list = to_raw_response_wrapper(orders.list) - self.get = to_raw_response_wrapper(orders.get) - self.create = to_raw_response_wrapper(orders.create) - self.update = to_raw_response_wrapper(orders.update) - self.cancel = to_raw_response_wrapper(orders.cancel) - self.refund = to_raw_response_wrapper(orders.refund) - self.list_items = to_raw_response_wrapper(orders.list_items) - - -class AsyncOrdersResourceWithRawResponse: - def __init__(self, orders: AsyncOrdersResource) -> None: - self._orders = orders - self.list = async_to_raw_response_wrapper(orders.list) - self.get = async_to_raw_response_wrapper(orders.get) - self.create = async_to_raw_response_wrapper(orders.create) - self.update = async_to_raw_response_wrapper(orders.update) - self.cancel = async_to_raw_response_wrapper(orders.cancel) - self.refund = async_to_raw_response_wrapper(orders.refund) - self.list_items = async_to_raw_response_wrapper(orders.list_items) - - -class OrdersResourceWithStreamingResponse: - def __init__(self, orders: OrdersResource) -> None: - self._orders = orders - self.list = to_streamed_response_wrapper(orders.list) - self.get = to_streamed_response_wrapper(orders.get) - self.create = to_streamed_response_wrapper(orders.create) - self.update = to_streamed_response_wrapper(orders.update) - self.cancel = to_streamed_response_wrapper(orders.cancel) - self.refund = to_streamed_response_wrapper(orders.refund) - self.list_items = to_streamed_response_wrapper(orders.list_items) - - -class AsyncOrdersResourceWithStreamingResponse: - def __init__(self, orders: AsyncOrdersResource) -> None: - self._orders = orders - self.list = async_to_streamed_response_wrapper(orders.list) - self.get = async_to_streamed_response_wrapper(orders.get) - self.create = async_to_streamed_response_wrapper(orders.create) - self.update = async_to_streamed_response_wrapper(orders.update) - self.cancel = async_to_streamed_response_wrapper(orders.cancel) - self.refund = async_to_streamed_response_wrapper(orders.refund) - self.list_items = async_to_streamed_response_wrapper(orders.list_items) diff --git a/pkg/hanzoai/resources/organization/__init__.py b/pkg/hanzoai/resources/organization/__init__.py deleted file mode 100644 index 3f0571580..000000000 --- a/pkg/hanzoai/resources/organization/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Hanzo AI SDK - -from .info import ( - InfoResource, - AsyncInfoResource, - InfoResourceWithRawResponse, - AsyncInfoResourceWithRawResponse, - InfoResourceWithStreamingResponse, - AsyncInfoResourceWithStreamingResponse, -) -from .organization import ( - OrganizationResource, - AsyncOrganizationResource, - OrganizationResourceWithRawResponse, - AsyncOrganizationResourceWithRawResponse, - OrganizationResourceWithStreamingResponse, - AsyncOrganizationResourceWithStreamingResponse, -) - -__all__ = [ - "InfoResource", - "AsyncInfoResource", - "InfoResourceWithRawResponse", - "AsyncInfoResourceWithRawResponse", - "InfoResourceWithStreamingResponse", - "AsyncInfoResourceWithStreamingResponse", - "OrganizationResource", - "AsyncOrganizationResource", - "OrganizationResourceWithRawResponse", - "AsyncOrganizationResourceWithRawResponse", - "OrganizationResourceWithStreamingResponse", - "AsyncOrganizationResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/organization/info.py b/pkg/hanzoai/resources/organization/info.py deleted file mode 100644 index 05ce5c664..000000000 --- a/pkg/hanzoai/resources/organization/info.py +++ /dev/null @@ -1,268 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List - -import httpx - -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.organization import info_retrieve_params, info_deprecated_params -from ...types.organization.info_retrieve_response import InfoRetrieveResponse - -__all__ = ["InfoResource", "AsyncInfoResource"] - - -class InfoResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> InfoResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return InfoResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> InfoResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return InfoResourceWithStreamingResponse(self) - - def retrieve( - self, - *, - organization_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> InfoRetrieveResponse: - """ - Get the org specific information - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/organization/info", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"organization_id": organization_id}, - info_retrieve_params.InfoRetrieveParams, - ), - ), - cast_to=InfoRetrieveResponse, - ) - - def deprecated( - self, - *, - organizations: List[str], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - DEPRECATED: Use GET /organization/info instead - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/organization/info", - body=maybe_transform( - {"organizations": organizations}, - info_deprecated_params.InfoDeprecatedParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncInfoResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncInfoResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncInfoResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncInfoResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncInfoResourceWithStreamingResponse(self) - - async def retrieve( - self, - *, - organization_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> InfoRetrieveResponse: - """ - Get the org specific information - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/organization/info", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"organization_id": organization_id}, - info_retrieve_params.InfoRetrieveParams, - ), - ), - cast_to=InfoRetrieveResponse, - ) - - async def deprecated( - self, - *, - organizations: List[str], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - DEPRECATED: Use GET /organization/info instead - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/organization/info", - body=await async_maybe_transform( - {"organizations": organizations}, - info_deprecated_params.InfoDeprecatedParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class InfoResourceWithRawResponse: - def __init__(self, info: InfoResource) -> None: - self._info = info - - self.retrieve = to_raw_response_wrapper( - info.retrieve, - ) - self.deprecated = to_raw_response_wrapper( - info.deprecated, - ) - - -class AsyncInfoResourceWithRawResponse: - def __init__(self, info: AsyncInfoResource) -> None: - self._info = info - - self.retrieve = async_to_raw_response_wrapper( - info.retrieve, - ) - self.deprecated = async_to_raw_response_wrapper( - info.deprecated, - ) - - -class InfoResourceWithStreamingResponse: - def __init__(self, info: InfoResource) -> None: - self._info = info - - self.retrieve = to_streamed_response_wrapper( - info.retrieve, - ) - self.deprecated = to_streamed_response_wrapper( - info.deprecated, - ) - - -class AsyncInfoResourceWithStreamingResponse: - def __init__(self, info: AsyncInfoResource) -> None: - self._info = info - - self.retrieve = async_to_streamed_response_wrapper( - info.retrieve, - ) - self.deprecated = async_to_streamed_response_wrapper( - info.deprecated, - ) diff --git a/pkg/hanzoai/resources/paas.py b/pkg/hanzoai/resources/paas.py deleted file mode 100644 index d7761f26f..000000000 --- a/pkg/hanzoai/resources/paas.py +++ /dev/null @@ -1,2057 +0,0 @@ -# Hanzo AI SDK โ€” PaaS Resource (platform.hanzo.ai tRPC backend) - -from __future__ import annotations - -from typing import Any, Dict, List, Optional - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["PaaSResource", "AsyncPaaSResource"] - - -class PaaSResource(SyncAPIResource): - """Platform-as-a-Service: orgs, projects, environments, containers, clusters, - builds, domains, repos, VMs, team, audit. Maps to tRPC routers at - platform.hanzo.ai/api/trpc.""" - - @cached_property - def with_raw_response(self) -> PaaSResourceWithRawResponse: - return PaaSResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> PaaSResourceWithStreamingResponse: - return PaaSResourceWithStreamingResponse(self) - - # โ”€โ”€ System โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def health( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Health check.""" - return self._get( - "/paas/system.health", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def stats( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Platform statistics.""" - return self._get( - "/paas/system.stats", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ User โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def me( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get current user info.""" - return self._get( - "/paas/user.me", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Organizations โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_organizations( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List organizations for the current user.""" - return self._get( - "/paas/organization.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def create_organization( - self, - *, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create an organization.""" - return self._post( - "/paas/organization.create", - body={"name": name}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_organization( - self, - org_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get an organization by ID.""" - return self._get( - "/paas/organization.get", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id}, - ), - cast_to=object, - ) - - def update_organization( - self, - org_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an organization.""" - return self._post( - "/paas/organization.update", - body={"orgId": org_id, "name": name}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def delete_organization( - self, - org_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete an organization.""" - return self._post( - "/paas/organization.delete", - body={"orgId": org_id}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Projects โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_projects( - self, - org_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List projects in an organization.""" - return self._get( - "/paas/project.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id}, - ), - cast_to=object, - ) - - def create_project( - self, - *, - org_id: str, - name: str, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a project.""" - return self._post( - "/paas/project.create", - body={"orgId": org_id, "name": name, "description": description}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_project( - self, - org_id: str, - project_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a project.""" - return self._get( - "/paas/project.get", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "projectId": project_id}, - ), - cast_to=object, - ) - - def update_project( - self, - org_id: str, - project_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a project.""" - return self._post( - "/paas/project.update", - body={ - "orgId": org_id, "projectId": project_id, - "name": name, "description": description, - }, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def delete_project( - self, - org_id: str, - project_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a project.""" - return self._post( - "/paas/project.delete", - body={"orgId": org_id, "projectId": project_id}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Environments โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_environments( - self, - org_id: str, - project_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List environments for a project.""" - return self._get( - "/paas/environment.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "projectId": project_id}, - ), - cast_to=object, - ) - - def create_environment( - self, - *, - org_id: str, - project_id: str, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create an environment.""" - return self._post( - "/paas/environment.create", - body={"orgId": org_id, "projectId": project_id, "name": name}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Containers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_containers( - self, - org_id: str, - project_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List containers for a project.""" - return self._get( - "/paas/container.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "projectId": project_id}, - ), - cast_to=object, - ) - - def create_container( - self, - *, - org_id: str, - project_id: str, - name: str, - image: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a container.""" - return self._post( - "/paas/container.create", - body={ - "orgId": org_id, "projectId": project_id, - "name": name, "image": image, - }, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_container( - self, - org_id: str, - project_id: str, - container_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a container.""" - return self._get( - "/paas/container.get", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={ - "orgId": org_id, "projectId": project_id, - "containerId": container_id, - }, - ), - cast_to=object, - ) - - def update_container( - self, - org_id: str, - project_id: str, - container_id: str, - *, - image: str | NotGiven = NOT_GIVEN, - name: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a container.""" - return self._post( - "/paas/container.update", - body={ - "orgId": org_id, "projectId": project_id, - "containerId": container_id, - "image": image, "name": name, - }, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def delete_container( - self, - org_id: str, - project_id: str, - container_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a container.""" - return self._post( - "/paas/container.delete", - body={ - "orgId": org_id, "projectId": project_id, - "containerId": container_id, - }, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def redeploy_container( - self, - org_id: str, - project_id: str, - container_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Redeploy a container.""" - return self._post( - "/paas/container.redeploy", - body={ - "orgId": org_id, "projectId": project_id, - "containerId": container_id, - }, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def container_logs( - self, - org_id: str, - project_id: str, - container_id: str, - *, - lines: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get container logs.""" - return self._get( - "/paas/container.logs", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={ - "orgId": org_id, "projectId": project_id, - "containerId": container_id, "lines": lines, - }, - ), - cast_to=object, - ) - - # โ”€โ”€ Clusters โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_clusters( - self, - org_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List clusters for an organization.""" - return self._get( - "/paas/cluster.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id}, - ), - cast_to=object, - ) - - def create_cluster( - self, - *, - org_id: str, - name: str, - provider: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a cluster.""" - return self._post( - "/paas/cluster.create", - body={"orgId": org_id, "name": name, "provider": provider}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_cluster( - self, - org_id: str, - cluster_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a cluster.""" - return self._get( - "/paas/cluster.get", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "clusterId": cluster_id}, - ), - cast_to=object, - ) - - # โ”€โ”€ Builds โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_builds( - self, - org_id: str, - project_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List builds for a project.""" - return self._get( - "/paas/build.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "projectId": project_id}, - ), - cast_to=object, - ) - - def trigger_build( - self, - *, - org_id: str, - project_id: str, - container_id: str, - ref: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Trigger a build.""" - return self._post( - "/paas/build.trigger", - body={ - "orgId": org_id, "projectId": project_id, - "containerId": container_id, "ref": ref, - }, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_build( - self, - org_id: str, - project_id: str, - build_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get build status.""" - return self._get( - "/paas/build.get", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={ - "orgId": org_id, "projectId": project_id, - "buildId": build_id, - }, - ), - cast_to=object, - ) - - def build_logs( - self, - org_id: str, - project_id: str, - build_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get build logs.""" - return self._get( - "/paas/build.logs", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={ - "orgId": org_id, "projectId": project_id, - "buildId": build_id, - }, - ), - cast_to=object, - ) - - def cancel_build( - self, - org_id: str, - project_id: str, - build_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Cancel a build.""" - return self._post( - "/paas/build.cancel", - body={ - "orgId": org_id, "projectId": project_id, - "buildId": build_id, - }, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Domains โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_domains( - self, - org_id: str, - project_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List domains for a project.""" - return self._get( - "/paas/domain.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "projectId": project_id}, - ), - cast_to=object, - ) - - def add_domain( - self, - *, - org_id: str, - project_id: str, - domain: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a domain to a project.""" - return self._post( - "/paas/domain.add", - body={"orgId": org_id, "projectId": project_id, "domain": domain}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def remove_domain( - self, - *, - org_id: str, - project_id: str, - domain: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove a domain from a project.""" - return self._post( - "/paas/domain.remove", - body={"orgId": org_id, "projectId": project_id, "domain": domain}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def verify_domain( - self, - *, - org_id: str, - project_id: str, - domain: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Verify a domain.""" - return self._post( - "/paas/domain.verify", - body={"orgId": org_id, "projectId": project_id, "domain": domain}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Repositories โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_repositories( - self, - org_id: str, - *, - search: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List repositories.""" - return self._get( - "/paas/repository.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "search": search, "limit": limit}, - ), - cast_to=object, - ) - - # โ”€โ”€ Team โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_team( - self, - org_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List team members for an organization.""" - return self._get( - "/paas/orgTeam.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id}, - ), - cast_to=object, - ) - - def invite_member( - self, - *, - org_id: str, - email: str, - role: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Invite a team member.""" - return self._post( - "/paas/orgTeam.invite", - body={"orgId": org_id, "email": email, "role": role}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def remove_member( - self, - *, - org_id: str, - user_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove a team member.""" - return self._post( - "/paas/orgTeam.remove", - body={"orgId": org_id, "userId": user_id}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Audit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_audit_events( - self, - org_id: str, - *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List audit events for an organization.""" - return self._get( - "/paas/audit.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - # โ”€โ”€ VMs โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - def list_vms( - self, - org_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List VMs for an organization.""" - return self._get( - "/paas/vm.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id}, - ), - cast_to=object, - ) - - def create_vm( - self, - *, - org_id: str, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a VM.""" - return self._post( - "/paas/vm.create", - body={"orgId": org_id, "name": name}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - def retrieve_vm( - self, - org_id: str, - vm_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a VM.""" - return self._get( - "/paas/vm.get", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "vmId": vm_id}, - ), - cast_to=object, - ) - - def delete_vm( - self, - org_id: str, - vm_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a VM.""" - return self._post( - "/paas/vm.delete", - body={"orgId": org_id, "vmId": vm_id}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - -# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -# Async mirror -# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - - -class AsyncPaaSResource(AsyncAPIResource): - """Platform-as-a-Service (async). Maps to tRPC routers at - platform.hanzo.ai/api/trpc.""" - - @cached_property - def with_raw_response(self) -> AsyncPaaSResourceWithRawResponse: - return AsyncPaaSResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncPaaSResourceWithStreamingResponse: - return AsyncPaaSResourceWithStreamingResponse(self) - - # โ”€โ”€ System โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def health( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Health check.""" - return await self._get( - "/paas/system.health", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def stats( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Platform statistics.""" - return await self._get( - "/paas/system.stats", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ User โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def me( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get current user info.""" - return await self._get( - "/paas/user.me", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Organizations โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_organizations( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List organizations for the current user.""" - return await self._get( - "/paas/organization.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def create_organization( - self, - *, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create an organization.""" - return await self._post( - "/paas/organization.create", - body={"name": name}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_organization( - self, - org_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get an organization by ID.""" - return await self._get( - "/paas/organization.get", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id}, - ), - cast_to=object, - ) - - async def update_organization( - self, - org_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update an organization.""" - return await self._post( - "/paas/organization.update", - body={"orgId": org_id, "name": name}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def delete_organization( - self, - org_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete an organization.""" - return await self._post( - "/paas/organization.delete", - body={"orgId": org_id}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Projects โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_projects( - self, - org_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List projects in an organization.""" - return await self._get( - "/paas/project.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id}, - ), - cast_to=object, - ) - - async def create_project( - self, - *, - org_id: str, - name: str, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a project.""" - return await self._post( - "/paas/project.create", - body={"orgId": org_id, "name": name, "description": description}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_project( - self, - org_id: str, - project_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a project.""" - return await self._get( - "/paas/project.get", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "projectId": project_id}, - ), - cast_to=object, - ) - - async def update_project( - self, - org_id: str, - project_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a project.""" - return await self._post( - "/paas/project.update", - body={ - "orgId": org_id, "projectId": project_id, - "name": name, "description": description, - }, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def delete_project( - self, - org_id: str, - project_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a project.""" - return await self._post( - "/paas/project.delete", - body={"orgId": org_id, "projectId": project_id}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Environments โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_environments( - self, - org_id: str, - project_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List environments for a project.""" - return await self._get( - "/paas/environment.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "projectId": project_id}, - ), - cast_to=object, - ) - - async def create_environment( - self, - *, - org_id: str, - project_id: str, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create an environment.""" - return await self._post( - "/paas/environment.create", - body={"orgId": org_id, "projectId": project_id, "name": name}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Containers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_containers( - self, - org_id: str, - project_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List containers for a project.""" - return await self._get( - "/paas/container.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "projectId": project_id}, - ), - cast_to=object, - ) - - async def create_container( - self, - *, - org_id: str, - project_id: str, - name: str, - image: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a container.""" - return await self._post( - "/paas/container.create", - body={ - "orgId": org_id, "projectId": project_id, - "name": name, "image": image, - }, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_container( - self, - org_id: str, - project_id: str, - container_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a container.""" - return await self._get( - "/paas/container.get", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={ - "orgId": org_id, "projectId": project_id, - "containerId": container_id, - }, - ), - cast_to=object, - ) - - async def update_container( - self, - org_id: str, - project_id: str, - container_id: str, - *, - image: str | NotGiven = NOT_GIVEN, - name: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a container.""" - return await self._post( - "/paas/container.update", - body={ - "orgId": org_id, "projectId": project_id, - "containerId": container_id, - "image": image, "name": name, - }, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def delete_container( - self, - org_id: str, - project_id: str, - container_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a container.""" - return await self._post( - "/paas/container.delete", - body={ - "orgId": org_id, "projectId": project_id, - "containerId": container_id, - }, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def redeploy_container( - self, - org_id: str, - project_id: str, - container_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Redeploy a container.""" - return await self._post( - "/paas/container.redeploy", - body={ - "orgId": org_id, "projectId": project_id, - "containerId": container_id, - }, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def container_logs( - self, - org_id: str, - project_id: str, - container_id: str, - *, - lines: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get container logs.""" - return await self._get( - "/paas/container.logs", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={ - "orgId": org_id, "projectId": project_id, - "containerId": container_id, "lines": lines, - }, - ), - cast_to=object, - ) - - # โ”€โ”€ Clusters โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_clusters( - self, - org_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List clusters for an organization.""" - return await self._get( - "/paas/cluster.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id}, - ), - cast_to=object, - ) - - async def create_cluster( - self, - *, - org_id: str, - name: str, - provider: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a cluster.""" - return await self._post( - "/paas/cluster.create", - body={"orgId": org_id, "name": name, "provider": provider}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_cluster( - self, - org_id: str, - cluster_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a cluster.""" - return await self._get( - "/paas/cluster.get", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "clusterId": cluster_id}, - ), - cast_to=object, - ) - - # โ”€โ”€ Builds โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_builds( - self, - org_id: str, - project_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List builds for a project.""" - return await self._get( - "/paas/build.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "projectId": project_id}, - ), - cast_to=object, - ) - - async def trigger_build( - self, - *, - org_id: str, - project_id: str, - container_id: str, - ref: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Trigger a build.""" - return await self._post( - "/paas/build.trigger", - body={ - "orgId": org_id, "projectId": project_id, - "containerId": container_id, "ref": ref, - }, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_build( - self, - org_id: str, - project_id: str, - build_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get build status.""" - return await self._get( - "/paas/build.get", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={ - "orgId": org_id, "projectId": project_id, - "buildId": build_id, - }, - ), - cast_to=object, - ) - - async def build_logs( - self, - org_id: str, - project_id: str, - build_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get build logs.""" - return await self._get( - "/paas/build.logs", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={ - "orgId": org_id, "projectId": project_id, - "buildId": build_id, - }, - ), - cast_to=object, - ) - - async def cancel_build( - self, - org_id: str, - project_id: str, - build_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Cancel a build.""" - return await self._post( - "/paas/build.cancel", - body={ - "orgId": org_id, "projectId": project_id, - "buildId": build_id, - }, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Domains โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_domains( - self, - org_id: str, - project_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List domains for a project.""" - return await self._get( - "/paas/domain.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "projectId": project_id}, - ), - cast_to=object, - ) - - async def add_domain( - self, - *, - org_id: str, - project_id: str, - domain: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a domain to a project.""" - return await self._post( - "/paas/domain.add", - body={"orgId": org_id, "projectId": project_id, "domain": domain}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def remove_domain( - self, - *, - org_id: str, - project_id: str, - domain: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove a domain from a project.""" - return await self._post( - "/paas/domain.remove", - body={"orgId": org_id, "projectId": project_id, "domain": domain}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def verify_domain( - self, - *, - org_id: str, - project_id: str, - domain: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Verify a domain.""" - return await self._post( - "/paas/domain.verify", - body={"orgId": org_id, "projectId": project_id, "domain": domain}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Repositories โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_repositories( - self, - org_id: str, - *, - search: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List repositories.""" - return await self._get( - "/paas/repository.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "search": search, "limit": limit}, - ), - cast_to=object, - ) - - # โ”€โ”€ Team โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_team( - self, - org_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List team members for an organization.""" - return await self._get( - "/paas/orgTeam.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id}, - ), - cast_to=object, - ) - - async def invite_member( - self, - *, - org_id: str, - email: str, - role: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Invite a team member.""" - return await self._post( - "/paas/orgTeam.invite", - body={"orgId": org_id, "email": email, "role": role}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def remove_member( - self, - *, - org_id: str, - user_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove a team member.""" - return await self._post( - "/paas/orgTeam.remove", - body={"orgId": org_id, "userId": user_id}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - # โ”€โ”€ Audit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_audit_events( - self, - org_id: str, - *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List audit events for an organization.""" - return await self._get( - "/paas/audit.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - # โ”€โ”€ VMs โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - async def list_vms( - self, - org_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List VMs for an organization.""" - return await self._get( - "/paas/vm.list", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id}, - ), - cast_to=object, - ) - - async def create_vm( - self, - *, - org_id: str, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a VM.""" - return await self._post( - "/paas/vm.create", - body={"orgId": org_id, "name": name}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - async def retrieve_vm( - self, - org_id: str, - vm_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a VM.""" - return await self._get( - "/paas/vm.get", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - query={"orgId": org_id, "vmId": vm_id}, - ), - cast_to=object, - ) - - async def delete_vm( - self, - org_id: str, - vm_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a VM.""" - return await self._post( - "/paas/vm.delete", - body={"orgId": org_id, "vmId": vm_id}, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, - extra_body=extra_body, timeout=timeout, - ), - cast_to=object, - ) - - -# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -# Raw response wrappers -# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - -# All method names that exist on both sync and async resources. -_METHOD_NAMES = [ - "health", "stats", "me", - # orgs - "list_organizations", "create_organization", "retrieve_organization", - "update_organization", "delete_organization", - # projects - "list_projects", "create_project", "retrieve_project", - "update_project", "delete_project", - # environments - "list_environments", "create_environment", - # containers - "list_containers", "create_container", "retrieve_container", - "update_container", "delete_container", "redeploy_container", - "container_logs", - # clusters - "list_clusters", "create_cluster", "retrieve_cluster", - # builds - "list_builds", "trigger_build", "retrieve_build", - "build_logs", "cancel_build", - # domains - "list_domains", "add_domain", "remove_domain", "verify_domain", - # repos - "list_repositories", - # team - "list_team", "invite_member", "remove_member", - # audit - "list_audit_events", - # vms - "list_vms", "create_vm", "retrieve_vm", "delete_vm", -] - - -class PaaSResourceWithRawResponse: - def __init__(self, paas: PaaSResource) -> None: - self._paas = paas - for name in _METHOD_NAMES: - setattr(self, name, to_raw_response_wrapper(getattr(paas, name))) - - -class AsyncPaaSResourceWithRawResponse: - def __init__(self, paas: AsyncPaaSResource) -> None: - self._paas = paas - for name in _METHOD_NAMES: - setattr(self, name, async_to_raw_response_wrapper(getattr(paas, name))) - - -class PaaSResourceWithStreamingResponse: - def __init__(self, paas: PaaSResource) -> None: - self._paas = paas - for name in _METHOD_NAMES: - setattr(self, name, to_streamed_response_wrapper(getattr(paas, name))) - - -class AsyncPaaSResourceWithStreamingResponse: - def __init__(self, paas: AsyncPaaSResource) -> None: - self._paas = paas - for name in _METHOD_NAMES: - setattr(self, name, async_to_streamed_response_wrapper(getattr(paas, name))) diff --git a/pkg/hanzoai/resources/pages.py b/pkg/hanzoai/resources/pages.py deleted file mode 100644 index b4dbcec15..000000000 --- a/pkg/hanzoai/resources/pages.py +++ /dev/null @@ -1,410 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class PagesResource(SyncAPIResource): - """Cloudflare Pages project management service.""" - - @cached_property - def with_raw_response(self) -> PagesResourceWithRawResponse: - return PagesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> PagesResourceWithStreamingResponse: - return PagesResourceWithStreamingResponse(self) - - # Project management - def list_projects( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all Pages projects.""" - return self._get( - "/pages/projects", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get_project( - self, - project_name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a Pages project by name.""" - return self._get( - f"/pages/projects/{project_name}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_project( - self, - *, - name: str, - production_branch: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new Pages project.""" - return self._post( - "/pages/projects", - body={"name": name, "production_branch": production_branch}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_project( - self, - project_name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a Pages project.""" - return self._delete( - f"/pages/projects/{project_name}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Deployment management - def list_deployments( - self, - project_name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List deployments for a Pages project.""" - return self._get( - f"/pages/projects/{project_name}/deployments", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def trigger_deploy( - self, - project_name: str, - *, - branch: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Trigger a new deployment for a Pages project.""" - return self._post( - f"/pages/projects/{project_name}/deployments", - body={"branch": branch}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Domain management - def add_domain( - self, - project_name: str, - *, - domain: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a custom domain to a Pages project.""" - return self._post( - f"/pages/projects/{project_name}/domains", - body={"domain": domain}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def remove_domain( - self, - project_name: str, - domain_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove a custom domain from a Pages project.""" - return self._delete( - f"/pages/projects/{project_name}/domains/{domain_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncPagesResource(AsyncAPIResource): - """Cloudflare Pages project management service.""" - - @cached_property - def with_raw_response(self) -> AsyncPagesResourceWithRawResponse: - return AsyncPagesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncPagesResourceWithStreamingResponse: - return AsyncPagesResourceWithStreamingResponse(self) - - # Project management - async def list_projects( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all Pages projects.""" - return await self._get( - "/pages/projects", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get_project( - self, - project_name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a Pages project by name.""" - return await self._get( - f"/pages/projects/{project_name}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_project( - self, - *, - name: str, - production_branch: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new Pages project.""" - return await self._post( - "/pages/projects", - body={"name": name, "production_branch": production_branch}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_project( - self, - project_name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a Pages project.""" - return await self._delete( - f"/pages/projects/{project_name}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Deployment management - async def list_deployments( - self, - project_name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List deployments for a Pages project.""" - return await self._get( - f"/pages/projects/{project_name}/deployments", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def trigger_deploy( - self, - project_name: str, - *, - branch: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Trigger a new deployment for a Pages project.""" - return await self._post( - f"/pages/projects/{project_name}/deployments", - body={"branch": branch}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # Domain management - async def add_domain( - self, - project_name: str, - *, - domain: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add a custom domain to a Pages project.""" - return await self._post( - f"/pages/projects/{project_name}/domains", - body={"domain": domain}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def remove_domain( - self, - project_name: str, - domain_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove a custom domain from a Pages project.""" - return await self._delete( - f"/pages/projects/{project_name}/domains/{domain_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class PagesResourceWithRawResponse: - def __init__(self, pages: PagesResource) -> None: - self._pages = pages - - -class AsyncPagesResourceWithRawResponse: - def __init__(self, pages: AsyncPagesResource) -> None: - self._pages = pages - - -class PagesResourceWithStreamingResponse: - def __init__(self, pages: PagesResource) -> None: - self._pages = pages - - -class AsyncPagesResourceWithStreamingResponse: - def __init__(self, pages: AsyncPagesResource) -> None: - self._pages = pages diff --git a/pkg/hanzoai/resources/pods.py b/pkg/hanzoai/resources/pods.py deleted file mode 100644 index 7ae8c9e7a..000000000 --- a/pkg/hanzoai/resources/pods.py +++ /dev/null @@ -1,378 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["PodsResource", "AsyncPodsResource"] - - -class PodsResource(SyncAPIResource): - """Kubernetes pod management.""" - - @cached_property - def with_raw_response(self) -> PodsResourceWithRawResponse: - return PodsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> PodsResourceWithStreamingResponse: - return PodsResourceWithStreamingResponse(self) - - def list( - self, - *, - namespace: str | NotGiven = NOT_GIVEN, - label_selector: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all pods.""" - return self._get( - "/infrastructure/pods", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"namespace": namespace, "label_selector": label_selector}, - ), - cast_to=object, - ) - - def get( - self, - pod_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific pod.""" - return self._get( - f"/infrastructure/pods/{pod_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - namespace: str, - image: str, - command: List[str] | NotGiven = NOT_GIVEN, - env: Dict[str, str] | NotGiven = NOT_GIVEN, - resources: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new pod.""" - return self._post( - "/infrastructure/pods", - body={ - "name": name, - "namespace": namespace, - "image": image, - "command": command, - "env": env, - "resources": resources, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - pod_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a pod.""" - return self._delete( - f"/infrastructure/pods/{pod_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def logs( - self, - pod_id: str, - *, - container: str | NotGiven = NOT_GIVEN, - tail: int | NotGiven = NOT_GIVEN, - follow: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get pod logs.""" - return self._get( - f"/infrastructure/pods/{pod_id}/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"container": container, "tail": tail, "follow": follow}, - ), - cast_to=object, - ) - - def exec( - self, - pod_id: str, - *, - command: List[str], - container: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Execute a command in a pod.""" - return self._post( - f"/infrastructure/pods/{pod_id}/exec", - body={"command": command, "container": container}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncPodsResource(AsyncAPIResource): - """Kubernetes pod management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncPodsResourceWithRawResponse: - return AsyncPodsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncPodsResourceWithStreamingResponse: - return AsyncPodsResourceWithStreamingResponse(self) - - async def list( - self, - *, - namespace: str | NotGiven = NOT_GIVEN, - label_selector: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/infrastructure/pods", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"namespace": namespace, "label_selector": label_selector}, - ), - cast_to=object, - ) - - async def get( - self, - pod_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/infrastructure/pods/{pod_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - namespace: str, - image: str, - command: List[str] | NotGiven = NOT_GIVEN, - env: Dict[str, str] | NotGiven = NOT_GIVEN, - resources: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/infrastructure/pods", - body={ - "name": name, - "namespace": namespace, - "image": image, - "command": command, - "env": env, - "resources": resources, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - pod_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/infrastructure/pods/{pod_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def logs( - self, - pod_id: str, - *, - container: str | NotGiven = NOT_GIVEN, - tail: int | NotGiven = NOT_GIVEN, - follow: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/infrastructure/pods/{pod_id}/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"container": container, "tail": tail, "follow": follow}, - ), - cast_to=object, - ) - - async def exec( - self, - pod_id: str, - *, - command: List[str], - container: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/infrastructure/pods/{pod_id}/exec", - body={"command": command, "container": container}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class PodsResourceWithRawResponse: - def __init__(self, pods: PodsResource) -> None: - self._pods = pods - self.list = to_raw_response_wrapper(pods.list) - self.get = to_raw_response_wrapper(pods.get) - self.create = to_raw_response_wrapper(pods.create) - self.delete = to_raw_response_wrapper(pods.delete) - self.logs = to_raw_response_wrapper(pods.logs) - self.exec = to_raw_response_wrapper(pods.exec) - - -class AsyncPodsResourceWithRawResponse: - def __init__(self, pods: AsyncPodsResource) -> None: - self._pods = pods - self.list = async_to_raw_response_wrapper(pods.list) - self.get = async_to_raw_response_wrapper(pods.get) - self.create = async_to_raw_response_wrapper(pods.create) - self.delete = async_to_raw_response_wrapper(pods.delete) - self.logs = async_to_raw_response_wrapper(pods.logs) - self.exec = async_to_raw_response_wrapper(pods.exec) - - -class PodsResourceWithStreamingResponse: - def __init__(self, pods: PodsResource) -> None: - self._pods = pods - self.list = to_streamed_response_wrapper(pods.list) - self.get = to_streamed_response_wrapper(pods.get) - self.create = to_streamed_response_wrapper(pods.create) - self.delete = to_streamed_response_wrapper(pods.delete) - self.logs = to_streamed_response_wrapper(pods.logs) - self.exec = to_streamed_response_wrapper(pods.exec) - - -class AsyncPodsResourceWithStreamingResponse: - def __init__(self, pods: AsyncPodsResource) -> None: - self._pods = pods - self.list = async_to_streamed_response_wrapper(pods.list) - self.get = async_to_streamed_response_wrapper(pods.get) - self.create = async_to_streamed_response_wrapper(pods.create) - self.delete = async_to_streamed_response_wrapper(pods.delete) - self.logs = async_to_streamed_response_wrapper(pods.logs) - self.exec = async_to_streamed_response_wrapper(pods.exec) diff --git a/pkg/hanzoai/resources/policy.py b/pkg/hanzoai/resources/policy.py deleted file mode 100644 index 7d124bf22..000000000 --- a/pkg/hanzoai/resources/policy.py +++ /dev/null @@ -1,414 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class PolicyResource(SyncAPIResource): - """Policy management and access control.""" - - @cached_property - def with_raw_response(self) -> PolicyResourceWithRawResponse: - return PolicyResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> PolicyResourceWithStreamingResponse: - return PolicyResourceWithStreamingResponse(self) - - def lint( - self, - *, - policy: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Lint a policy file.""" - return self._post( - "/policy/lint", - body={"policy": policy}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def validate( - self, - *, - policy: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Validate a policy.""" - return self._post( - "/policy/validate", - body={"policy": policy}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def format( - self, - *, - policy: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Format a policy file.""" - return self._post( - "/policy/format", - body={"policy": policy}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def diff( - self, - *, - policy: str, - dry_run: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Diff policy against current state.""" - return self._post( - "/policy/diff", - body={"policy": policy, "dry_run": dry_run}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def apply( - self, - *, - policy: str, - dry_run: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Apply a policy.""" - return self._post( - "/policy/apply", - body={"policy": policy, "dry_run": dry_run}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def explain( - self, - *, - principal: str, - action: str, - resource: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Explain policy evaluation.""" - return self._post( - "/policy/explain", - body={"principal": principal, "action": action, "resource": resource}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def who_can( - self, - *, - action: str, - resource: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Find who can perform an action.""" - return self._get( - "/policy/who-can", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"action": action, "resource": resource}, - ), - cast_to=object, - ) - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List policies.""" - return self._get( - "/policy", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncPolicyResource(AsyncAPIResource): - """Policy management and access control.""" - - @cached_property - def with_raw_response(self) -> AsyncPolicyResourceWithRawResponse: - return AsyncPolicyResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncPolicyResourceWithStreamingResponse: - return AsyncPolicyResourceWithStreamingResponse(self) - - async def lint( - self, - *, - policy: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Lint a policy file.""" - return await self._post( - "/policy/lint", - body={"policy": policy}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def validate( - self, - *, - policy: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Validate a policy.""" - return await self._post( - "/policy/validate", - body={"policy": policy}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def format( - self, - *, - policy: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Format a policy file.""" - return await self._post( - "/policy/format", - body={"policy": policy}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def diff( - self, - *, - policy: str, - dry_run: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Diff policy against current state.""" - return await self._post( - "/policy/diff", - body={"policy": policy, "dry_run": dry_run}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def apply( - self, - *, - policy: str, - dry_run: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Apply a policy.""" - return await self._post( - "/policy/apply", - body={"policy": policy, "dry_run": dry_run}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def explain( - self, - *, - principal: str, - action: str, - resource: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Explain policy evaluation.""" - return await self._post( - "/policy/explain", - body={"principal": principal, "action": action, "resource": resource}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def who_can( - self, - *, - action: str, - resource: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Find who can perform an action.""" - return await self._get( - "/policy/who-can", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"action": action, "resource": resource}, - ), - cast_to=object, - ) - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List policies.""" - return await self._get( - "/policy", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class PolicyResourceWithRawResponse: - def __init__(self, policy: PolicyResource) -> None: - self._policy = policy - - -class AsyncPolicyResourceWithRawResponse: - def __init__(self, policy: AsyncPolicyResource) -> None: - self._policy = policy - - -class PolicyResourceWithStreamingResponse: - def __init__(self, policy: PolicyResource) -> None: - self._policy = policy - - -class AsyncPolicyResourceWithStreamingResponse: - def __init__(self, policy: AsyncPolicyResource) -> None: - self._policy = policy diff --git a/pkg/hanzoai/resources/products.py b/pkg/hanzoai/resources/products.py deleted file mode 100644 index b48d65991..000000000 --- a/pkg/hanzoai/resources/products.py +++ /dev/null @@ -1,378 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["ProductsResource", "AsyncProductsResource"] - - -class ProductsResource(SyncAPIResource): - """Product catalog management.""" - - @cached_property - def with_raw_response(self) -> ProductsResourceWithRawResponse: - return ProductsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ProductsResourceWithStreamingResponse: - return ProductsResourceWithStreamingResponse(self) - - def list( - self, - *, - collection_id: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all products.""" - return self._get( - "/commerce/products", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "collection_id": collection_id, - "limit": limit, - "offset": offset, - }, - ), - cast_to=object, - ) - - def get( - self, - product_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific product.""" - return self._get( - f"/commerce/products/{product_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - price: float, - description: str | NotGiven = NOT_GIVEN, - images: List[str] | NotGiven = NOT_GIVEN, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new product.""" - return self._post( - "/commerce/products", - body={ - "name": name, - "price": price, - "description": description, - "images": images, - "metadata": metadata, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - product_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - price: float | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a product.""" - return self._put( - f"/commerce/products/{product_id}", - body={"name": name, "price": price, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - product_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a product.""" - return self._delete( - f"/commerce/products/{product_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_variants( - self, - product_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List product variants.""" - return self._get( - f"/commerce/products/{product_id}/variants", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncProductsResource(AsyncAPIResource): - """Product catalog management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncProductsResourceWithRawResponse: - return AsyncProductsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncProductsResourceWithStreamingResponse: - return AsyncProductsResourceWithStreamingResponse(self) - - async def list( - self, - *, - collection_id: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/products", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "collection_id": collection_id, - "limit": limit, - "offset": offset, - }, - ), - cast_to=object, - ) - - async def get( - self, - product_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/products/{product_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - price: float, - description: str | NotGiven = NOT_GIVEN, - images: List[str] | NotGiven = NOT_GIVEN, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/products", - body={ - "name": name, - "price": price, - "description": description, - "images": images, - "metadata": metadata, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - product_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - price: float | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._put( - f"/commerce/products/{product_id}", - body={"name": name, "price": price, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - product_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/commerce/products/{product_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_variants( - self, - product_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/products/{product_id}/variants", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class ProductsResourceWithRawResponse: - def __init__(self, products: ProductsResource) -> None: - self._products = products - self.list = to_raw_response_wrapper(products.list) - self.get = to_raw_response_wrapper(products.get) - self.create = to_raw_response_wrapper(products.create) - self.update = to_raw_response_wrapper(products.update) - self.delete = to_raw_response_wrapper(products.delete) - self.list_variants = to_raw_response_wrapper(products.list_variants) - - -class AsyncProductsResourceWithRawResponse: - def __init__(self, products: AsyncProductsResource) -> None: - self._products = products - self.list = async_to_raw_response_wrapper(products.list) - self.get = async_to_raw_response_wrapper(products.get) - self.create = async_to_raw_response_wrapper(products.create) - self.update = async_to_raw_response_wrapper(products.update) - self.delete = async_to_raw_response_wrapper(products.delete) - self.list_variants = async_to_raw_response_wrapper(products.list_variants) - - -class ProductsResourceWithStreamingResponse: - def __init__(self, products: ProductsResource) -> None: - self._products = products - self.list = to_streamed_response_wrapper(products.list) - self.get = to_streamed_response_wrapper(products.get) - self.create = to_streamed_response_wrapper(products.create) - self.update = to_streamed_response_wrapper(products.update) - self.delete = to_streamed_response_wrapper(products.delete) - self.list_variants = to_streamed_response_wrapper(products.list_variants) - - -class AsyncProductsResourceWithStreamingResponse: - def __init__(self, products: AsyncProductsResource) -> None: - self._products = products - self.list = async_to_streamed_response_wrapper(products.list) - self.get = async_to_streamed_response_wrapper(products.get) - self.create = async_to_streamed_response_wrapper(products.create) - self.update = async_to_streamed_response_wrapper(products.update) - self.delete = async_to_streamed_response_wrapper(products.delete) - self.list_variants = async_to_streamed_response_wrapper(products.list_variants) diff --git a/pkg/hanzoai/resources/provider.py b/pkg/hanzoai/resources/provider.py deleted file mode 100644 index e3b22311c..000000000 --- a/pkg/hanzoai/resources/provider.py +++ /dev/null @@ -1,231 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options -from ..types.provider_list_budgets_response import ProviderListBudgetsResponse - -__all__ = ["ProviderResource", "AsyncProviderResource"] - - -class ProviderResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ProviderResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return ProviderResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ProviderResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return ProviderResourceWithStreamingResponse(self) - - def list_budgets( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ProviderListBudgetsResponse: - """ - Provider Budget Routing - Get Budget, Spend Details - https://docs.hanzo.ai/docs/proxy/provider_budget_routing - - Use this endpoint to check current budget, spend and budget reset time for a - provider - - Example Request - - ```bash - curl -X GET http://localhost:4000/provider/budgets -H "Content-Type: application/json" -H "Authorization: Bearer sk-1234" - ``` - - Example Response - - ```json - { - "providers": { - "openai": { - "budget_limit": 1e-12, - "time_period": "1d", - "spend": 0.0, - "budget_reset_at": null - }, - "azure": { - "budget_limit": 100.0, - "time_period": "1d", - "spend": 0.0, - "budget_reset_at": null - }, - "anthropic": { - "budget_limit": 100.0, - "time_period": "10d", - "spend": 0.0, - "budget_reset_at": null - }, - "vertex_ai": { - "budget_limit": 100.0, - "time_period": "12d", - "spend": 0.0, - "budget_reset_at": null - } - } - } - ``` - """ - return self._get( - "/provider/budgets", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=ProviderListBudgetsResponse, - ) - - -class AsyncProviderResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncProviderResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncProviderResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncProviderResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncProviderResourceWithStreamingResponse(self) - - async def list_budgets( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ProviderListBudgetsResponse: - """ - Provider Budget Routing - Get Budget, Spend Details - https://docs.hanzo.ai/docs/proxy/provider_budget_routing - - Use this endpoint to check current budget, spend and budget reset time for a - provider - - Example Request - - ```bash - curl -X GET http://localhost:4000/provider/budgets -H "Content-Type: application/json" -H "Authorization: Bearer sk-1234" - ``` - - Example Response - - ```json - { - "providers": { - "openai": { - "budget_limit": 1e-12, - "time_period": "1d", - "spend": 0.0, - "budget_reset_at": null - }, - "azure": { - "budget_limit": 100.0, - "time_period": "1d", - "spend": 0.0, - "budget_reset_at": null - }, - "anthropic": { - "budget_limit": 100.0, - "time_period": "10d", - "spend": 0.0, - "budget_reset_at": null - }, - "vertex_ai": { - "budget_limit": 100.0, - "time_period": "12d", - "spend": 0.0, - "budget_reset_at": null - } - } - } - ``` - """ - return await self._get( - "/provider/budgets", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=ProviderListBudgetsResponse, - ) - - -class ProviderResourceWithRawResponse: - def __init__(self, provider: ProviderResource) -> None: - self._provider = provider - - self.list_budgets = to_raw_response_wrapper( - provider.list_budgets, - ) - - -class AsyncProviderResourceWithRawResponse: - def __init__(self, provider: AsyncProviderResource) -> None: - self._provider = provider - - self.list_budgets = async_to_raw_response_wrapper( - provider.list_budgets, - ) - - -class ProviderResourceWithStreamingResponse: - def __init__(self, provider: ProviderResource) -> None: - self._provider = provider - - self.list_budgets = to_streamed_response_wrapper( - provider.list_budgets, - ) - - -class AsyncProviderResourceWithStreamingResponse: - def __init__(self, provider: AsyncProviderResource) -> None: - self._provider = provider - - self.list_budgets = async_to_streamed_response_wrapper( - provider.list_budgets, - ) diff --git a/pkg/hanzoai/resources/providers.py b/pkg/hanzoai/resources/providers.py deleted file mode 100644 index cd9d6c3e3..000000000 --- a/pkg/hanzoai/resources/providers.py +++ /dev/null @@ -1,356 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, Optional - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["ProvidersResource", "AsyncProvidersResource"] - - -class ProvidersResource(SyncAPIResource): - """LLM Provider management for AI/ML operations.""" - - @cached_property - def with_raw_response(self) -> ProvidersResourceWithRawResponse: - return ProvidersResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ProvidersResourceWithStreamingResponse: - return ProvidersResourceWithStreamingResponse(self) - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all configured LLM providers.""" - return self._get( - "/providers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - provider_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific provider configuration.""" - return self._get( - f"/providers/{provider_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - type: str, - config: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new LLM provider configuration.""" - return self._post( - "/providers", - body={"name": name, "type": type, "config": config}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - provider_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - config: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a provider configuration.""" - return self._put( - f"/providers/{provider_id}", - body={"name": name, "config": config}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - provider_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a provider configuration.""" - return self._delete( - f"/providers/{provider_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def refresh_mcp_tools( - self, - provider_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Refresh MCP tools for a provider.""" - return self._post( - f"/providers/{provider_id}/mcp/refresh", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncProvidersResource(AsyncAPIResource): - """LLM Provider management for AI/ML operations (async).""" - - @cached_property - def with_raw_response(self) -> AsyncProvidersResourceWithRawResponse: - return AsyncProvidersResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncProvidersResourceWithStreamingResponse: - return AsyncProvidersResourceWithStreamingResponse(self) - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all configured LLM providers.""" - return await self._get( - "/providers", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - provider_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific provider configuration.""" - return await self._get( - f"/providers/{provider_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - type: str, - config: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new LLM provider configuration.""" - return await self._post( - "/providers", - body={"name": name, "type": type, "config": config}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - provider_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - config: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a provider configuration.""" - return await self._put( - f"/providers/{provider_id}", - body={"name": name, "config": config}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - provider_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a provider configuration.""" - return await self._delete( - f"/providers/{provider_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def refresh_mcp_tools( - self, - provider_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Refresh MCP tools for a provider.""" - return await self._post( - f"/providers/{provider_id}/mcp/refresh", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class ProvidersResourceWithRawResponse: - def __init__(self, providers: ProvidersResource) -> None: - self._providers = providers - self.list = to_raw_response_wrapper(providers.list) - self.get = to_raw_response_wrapper(providers.get) - self.create = to_raw_response_wrapper(providers.create) - self.update = to_raw_response_wrapper(providers.update) - self.delete = to_raw_response_wrapper(providers.delete) - self.refresh_mcp_tools = to_raw_response_wrapper(providers.refresh_mcp_tools) - - -class AsyncProvidersResourceWithRawResponse: - def __init__(self, providers: AsyncProvidersResource) -> None: - self._providers = providers - self.list = async_to_raw_response_wrapper(providers.list) - self.get = async_to_raw_response_wrapper(providers.get) - self.create = async_to_raw_response_wrapper(providers.create) - self.update = async_to_raw_response_wrapper(providers.update) - self.delete = async_to_raw_response_wrapper(providers.delete) - self.refresh_mcp_tools = async_to_raw_response_wrapper( - providers.refresh_mcp_tools - ) - - -class ProvidersResourceWithStreamingResponse: - def __init__(self, providers: ProvidersResource) -> None: - self._providers = providers - self.list = to_streamed_response_wrapper(providers.list) - self.get = to_streamed_response_wrapper(providers.get) - self.create = to_streamed_response_wrapper(providers.create) - self.update = to_streamed_response_wrapper(providers.update) - self.delete = to_streamed_response_wrapper(providers.delete) - self.refresh_mcp_tools = to_streamed_response_wrapper( - providers.refresh_mcp_tools - ) - - -class AsyncProvidersResourceWithStreamingResponse: - def __init__(self, providers: AsyncProvidersResource) -> None: - self._providers = providers - self.list = async_to_streamed_response_wrapper(providers.list) - self.get = async_to_streamed_response_wrapper(providers.get) - self.create = async_to_streamed_response_wrapper(providers.create) - self.update = async_to_streamed_response_wrapper(providers.update) - self.delete = async_to_streamed_response_wrapper(providers.delete) - self.refresh_mcp_tools = async_to_streamed_response_wrapper( - providers.refresh_mcp_tools - ) diff --git a/pkg/hanzoai/resources/pubsub.py b/pkg/hanzoai/resources/pubsub.py deleted file mode 100644 index 99a6bba42..000000000 --- a/pkg/hanzoai/resources/pubsub.py +++ /dev/null @@ -1,511 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["PubSubResource", "AsyncPubSubResource"] - - -class PubSubResource(SyncAPIResource): - """Pub/Sub messaging service.""" - - @cached_property - def with_raw_response(self) -> PubSubResourceWithRawResponse: - return PubSubResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> PubSubResourceWithStreamingResponse: - return PubSubResourceWithStreamingResponse(self) - - def list_topics( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all topics.""" - return self._get( - "/pubsub/topics", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_topic( - self, - *, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new topic.""" - return self._post( - "/pubsub/topics", - body={"name": name}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_topic( - self, - topic_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a topic.""" - return self._delete( - f"/pubsub/topics/{topic_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def publish( - self, - topic_id: str, - *, - message: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Publish a message to a topic.""" - return self._post( - f"/pubsub/topics/{topic_id}/publish", - body={"message": message}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_subscriptions( - self, - topic_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List subscriptions for a topic.""" - return self._get( - f"/pubsub/topics/{topic_id}/subscriptions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_subscription( - self, - topic_id: str, - *, - name: str, - endpoint: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a subscription.""" - return self._post( - f"/pubsub/topics/{topic_id}/subscriptions", - body={"name": name, "endpoint": endpoint}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_subscription( - self, - topic_id: str, - subscription_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a subscription.""" - return self._delete( - f"/pubsub/topics/{topic_id}/subscriptions/{subscription_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def pull( - self, - topic_id: str, - subscription_id: str, - *, - max_messages: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Pull messages from a subscription.""" - return self._get( - f"/pubsub/topics/{topic_id}/subscriptions/{subscription_id}/pull", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"max_messages": max_messages}, - ), - cast_to=object, - ) - - def ack( - self, - topic_id: str, - subscription_id: str, - *, - ack_ids: List[str], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Acknowledge messages.""" - return self._post( - f"/pubsub/topics/{topic_id}/subscriptions/{subscription_id}/ack", - body={"ack_ids": ack_ids}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncPubSubResource(AsyncAPIResource): - """Pub/Sub messaging service (async).""" - - @cached_property - def with_raw_response(self) -> AsyncPubSubResourceWithRawResponse: - return AsyncPubSubResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncPubSubResourceWithStreamingResponse: - return AsyncPubSubResourceWithStreamingResponse(self) - - async def list_topics( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/pubsub/topics", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_topic( - self, - *, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/pubsub/topics", - body={"name": name}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_topic( - self, - topic_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/pubsub/topics/{topic_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def publish( - self, - topic_id: str, - *, - message: Dict[str, Any], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/pubsub/topics/{topic_id}/publish", - body={"message": message}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_subscriptions( - self, - topic_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/pubsub/topics/{topic_id}/subscriptions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_subscription( - self, - topic_id: str, - *, - name: str, - endpoint: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/pubsub/topics/{topic_id}/subscriptions", - body={"name": name, "endpoint": endpoint}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_subscription( - self, - topic_id: str, - subscription_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/pubsub/topics/{topic_id}/subscriptions/{subscription_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def pull( - self, - topic_id: str, - subscription_id: str, - *, - max_messages: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/pubsub/topics/{topic_id}/subscriptions/{subscription_id}/pull", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"max_messages": max_messages}, - ), - cast_to=object, - ) - - async def ack( - self, - topic_id: str, - subscription_id: str, - *, - ack_ids: List[str], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/pubsub/topics/{topic_id}/subscriptions/{subscription_id}/ack", - body={"ack_ids": ack_ids}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class PubSubResourceWithRawResponse: - def __init__(self, pubsub: PubSubResource) -> None: - self._pubsub = pubsub - self.list_topics = to_raw_response_wrapper(pubsub.list_topics) - self.create_topic = to_raw_response_wrapper(pubsub.create_topic) - self.delete_topic = to_raw_response_wrapper(pubsub.delete_topic) - self.publish = to_raw_response_wrapper(pubsub.publish) - self.list_subscriptions = to_raw_response_wrapper(pubsub.list_subscriptions) - self.create_subscription = to_raw_response_wrapper(pubsub.create_subscription) - self.delete_subscription = to_raw_response_wrapper(pubsub.delete_subscription) - self.pull = to_raw_response_wrapper(pubsub.pull) - self.ack = to_raw_response_wrapper(pubsub.ack) - - -class AsyncPubSubResourceWithRawResponse: - def __init__(self, pubsub: AsyncPubSubResource) -> None: - self._pubsub = pubsub - self.list_topics = async_to_raw_response_wrapper(pubsub.list_topics) - self.create_topic = async_to_raw_response_wrapper(pubsub.create_topic) - self.delete_topic = async_to_raw_response_wrapper(pubsub.delete_topic) - self.publish = async_to_raw_response_wrapper(pubsub.publish) - self.list_subscriptions = async_to_raw_response_wrapper( - pubsub.list_subscriptions - ) - self.create_subscription = async_to_raw_response_wrapper( - pubsub.create_subscription - ) - self.delete_subscription = async_to_raw_response_wrapper( - pubsub.delete_subscription - ) - self.pull = async_to_raw_response_wrapper(pubsub.pull) - self.ack = async_to_raw_response_wrapper(pubsub.ack) - - -class PubSubResourceWithStreamingResponse: - def __init__(self, pubsub: PubSubResource) -> None: - self._pubsub = pubsub - self.list_topics = to_streamed_response_wrapper(pubsub.list_topics) - self.create_topic = to_streamed_response_wrapper(pubsub.create_topic) - self.delete_topic = to_streamed_response_wrapper(pubsub.delete_topic) - self.publish = to_streamed_response_wrapper(pubsub.publish) - self.list_subscriptions = to_streamed_response_wrapper( - pubsub.list_subscriptions - ) - self.create_subscription = to_streamed_response_wrapper( - pubsub.create_subscription - ) - self.delete_subscription = to_streamed_response_wrapper( - pubsub.delete_subscription - ) - self.pull = to_streamed_response_wrapper(pubsub.pull) - self.ack = to_streamed_response_wrapper(pubsub.ack) - - -class AsyncPubSubResourceWithStreamingResponse: - def __init__(self, pubsub: AsyncPubSubResource) -> None: - self._pubsub = pubsub - self.list_topics = async_to_streamed_response_wrapper(pubsub.list_topics) - self.create_topic = async_to_streamed_response_wrapper(pubsub.create_topic) - self.delete_topic = async_to_streamed_response_wrapper(pubsub.delete_topic) - self.publish = async_to_streamed_response_wrapper(pubsub.publish) - self.list_subscriptions = async_to_streamed_response_wrapper( - pubsub.list_subscriptions - ) - self.create_subscription = async_to_streamed_response_wrapper( - pubsub.create_subscription - ) - self.delete_subscription = async_to_streamed_response_wrapper( - pubsub.delete_subscription - ) - self.pull = async_to_streamed_response_wrapper(pubsub.pull) - self.ack = async_to_streamed_response_wrapper(pubsub.ack) diff --git a/pkg/hanzoai/resources/queues.py b/pkg/hanzoai/resources/queues.py deleted file mode 100644 index 3c4483dc9..000000000 --- a/pkg/hanzoai/resources/queues.py +++ /dev/null @@ -1,399 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["QueuesResource", "AsyncQueuesResource"] - - -class QueuesResource(SyncAPIResource): - """Job queue service.""" - - @cached_property - def with_raw_response(self) -> QueuesResourceWithRawResponse: - return QueuesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> QueuesResourceWithStreamingResponse: - return QueuesResourceWithStreamingResponse(self) - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all queues.""" - return self._get( - "/queues", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new queue.""" - return self._post( - "/queues", - body={"name": name}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - queue_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a queue.""" - return self._delete( - f"/queues/{queue_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def send( - self, - queue_id: str, - *, - message: Dict[str, Any], - delay: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Send a message to a queue.""" - return self._post( - f"/queues/{queue_id}/messages", - body={"message": message, "delay": delay}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def receive( - self, - queue_id: str, - *, - batch_size: int | NotGiven = NOT_GIVEN, - visibility_timeout: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Receive messages from a queue.""" - return self._get( - f"/queues/{queue_id}/messages", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "batch_size": batch_size, - "visibility_timeout": visibility_timeout, - }, - ), - cast_to=object, - ) - - def ack( - self, - queue_id: str, - message_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Acknowledge a message.""" - return self._post( - f"/queues/{queue_id}/messages/{message_id}/ack", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stats( - self, - queue_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get queue statistics.""" - return self._get( - f"/queues/{queue_id}/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncQueuesResource(AsyncAPIResource): - """Job queue service (async).""" - - @cached_property - def with_raw_response(self) -> AsyncQueuesResourceWithRawResponse: - return AsyncQueuesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncQueuesResourceWithStreamingResponse: - return AsyncQueuesResourceWithStreamingResponse(self) - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/queues", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/queues", - body={"name": name}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - queue_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/queues/{queue_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def send( - self, - queue_id: str, - *, - message: Dict[str, Any], - delay: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/queues/{queue_id}/messages", - body={"message": message, "delay": delay}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def receive( - self, - queue_id: str, - *, - batch_size: int | NotGiven = NOT_GIVEN, - visibility_timeout: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/queues/{queue_id}/messages", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "batch_size": batch_size, - "visibility_timeout": visibility_timeout, - }, - ), - cast_to=object, - ) - - async def ack( - self, - queue_id: str, - message_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/queues/{queue_id}/messages/{message_id}/ack", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stats( - self, - queue_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/queues/{queue_id}/stats", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class QueuesResourceWithRawResponse: - def __init__(self, queues: QueuesResource) -> None: - self._queues = queues - self.list = to_raw_response_wrapper(queues.list) - self.create = to_raw_response_wrapper(queues.create) - self.delete = to_raw_response_wrapper(queues.delete) - self.send = to_raw_response_wrapper(queues.send) - self.receive = to_raw_response_wrapper(queues.receive) - self.ack = to_raw_response_wrapper(queues.ack) - self.stats = to_raw_response_wrapper(queues.stats) - - -class AsyncQueuesResourceWithRawResponse: - def __init__(self, queues: AsyncQueuesResource) -> None: - self._queues = queues - self.list = async_to_raw_response_wrapper(queues.list) - self.create = async_to_raw_response_wrapper(queues.create) - self.delete = async_to_raw_response_wrapper(queues.delete) - self.send = async_to_raw_response_wrapper(queues.send) - self.receive = async_to_raw_response_wrapper(queues.receive) - self.ack = async_to_raw_response_wrapper(queues.ack) - self.stats = async_to_raw_response_wrapper(queues.stats) - - -class QueuesResourceWithStreamingResponse: - def __init__(self, queues: QueuesResource) -> None: - self._queues = queues - self.list = to_streamed_response_wrapper(queues.list) - self.create = to_streamed_response_wrapper(queues.create) - self.delete = to_streamed_response_wrapper(queues.delete) - self.send = to_streamed_response_wrapper(queues.send) - self.receive = to_streamed_response_wrapper(queues.receive) - self.ack = to_streamed_response_wrapper(queues.ack) - self.stats = to_streamed_response_wrapper(queues.stats) - - -class AsyncQueuesResourceWithStreamingResponse: - def __init__(self, queues: AsyncQueuesResource) -> None: - self._queues = queues - self.list = async_to_streamed_response_wrapper(queues.list) - self.create = async_to_streamed_response_wrapper(queues.create) - self.delete = async_to_streamed_response_wrapper(queues.delete) - self.send = async_to_streamed_response_wrapper(queues.send) - self.receive = async_to_streamed_response_wrapper(queues.receive) - self.ack = async_to_streamed_response_wrapper(queues.ack) - self.stats = async_to_streamed_response_wrapper(queues.stats) diff --git a/pkg/hanzoai/resources/referrals.py b/pkg/hanzoai/resources/referrals.py deleted file mode 100644 index c35dcf9e2..000000000 --- a/pkg/hanzoai/resources/referrals.py +++ /dev/null @@ -1,405 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["ReferralsResource", "AsyncReferralsResource"] - - -class ReferralsResource(SyncAPIResource): - """Referral program management.""" - - @cached_property - def with_raw_response(self) -> ReferralsResourceWithRawResponse: - return ReferralsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ReferralsResourceWithStreamingResponse: - return ReferralsResourceWithStreamingResponse(self) - - def list( - self, - *, - status: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all referrals.""" - return self._get( - "/marketing/referrals", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"status": status, "limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - def get( - self, - referral_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific referral.""" - return self._get( - f"/marketing/referrals/{referral_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - referrer_id: str, - referred_email: str, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new referral.""" - return self._post( - "/marketing/referrals", - body={ - "referrer_id": referrer_id, - "referred_email": referred_email, - "metadata": metadata, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def track( - self, - referral_code: str, - *, - event: str, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Track a referral event.""" - return self._post( - f"/marketing/referrals/{referral_code}/track", - body={"event": event, "metadata": metadata}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def validate( - self, - referral_code: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Validate a referral code.""" - return self._get( - f"/marketing/referrals/{referral_code}/validate", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def complete( - self, - referral_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Mark a referral as completed.""" - return self._post( - f"/marketing/referrals/{referral_id}/complete", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stats( - self, - user_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get referral stats for a user.""" - return self._get( - f"/marketing/referrals/stats/{user_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncReferralsResource(AsyncAPIResource): - """Referral program management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncReferralsResourceWithRawResponse: - return AsyncReferralsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncReferralsResourceWithStreamingResponse: - return AsyncReferralsResourceWithStreamingResponse(self) - - async def list( - self, - *, - status: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/marketing/referrals", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"status": status, "limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - async def get( - self, - referral_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/marketing/referrals/{referral_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - referrer_id: str, - referred_email: str, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/marketing/referrals", - body={ - "referrer_id": referrer_id, - "referred_email": referred_email, - "metadata": metadata, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def track( - self, - referral_code: str, - *, - event: str, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/marketing/referrals/{referral_code}/track", - body={"event": event, "metadata": metadata}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def validate( - self, - referral_code: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/marketing/referrals/{referral_code}/validate", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def complete( - self, - referral_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/marketing/referrals/{referral_id}/complete", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stats( - self, - user_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/marketing/referrals/stats/{user_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class ReferralsResourceWithRawResponse: - def __init__(self, referrals: ReferralsResource) -> None: - self._referrals = referrals - self.list = to_raw_response_wrapper(referrals.list) - self.get = to_raw_response_wrapper(referrals.get) - self.create = to_raw_response_wrapper(referrals.create) - self.track = to_raw_response_wrapper(referrals.track) - self.validate = to_raw_response_wrapper(referrals.validate) - self.complete = to_raw_response_wrapper(referrals.complete) - self.stats = to_raw_response_wrapper(referrals.stats) - - -class AsyncReferralsResourceWithRawResponse: - def __init__(self, referrals: AsyncReferralsResource) -> None: - self._referrals = referrals - self.list = async_to_raw_response_wrapper(referrals.list) - self.get = async_to_raw_response_wrapper(referrals.get) - self.create = async_to_raw_response_wrapper(referrals.create) - self.track = async_to_raw_response_wrapper(referrals.track) - self.validate = async_to_raw_response_wrapper(referrals.validate) - self.complete = async_to_raw_response_wrapper(referrals.complete) - self.stats = async_to_raw_response_wrapper(referrals.stats) - - -class ReferralsResourceWithStreamingResponse: - def __init__(self, referrals: ReferralsResource) -> None: - self._referrals = referrals - self.list = to_streamed_response_wrapper(referrals.list) - self.get = to_streamed_response_wrapper(referrals.get) - self.create = to_streamed_response_wrapper(referrals.create) - self.track = to_streamed_response_wrapper(referrals.track) - self.validate = to_streamed_response_wrapper(referrals.validate) - self.complete = to_streamed_response_wrapper(referrals.complete) - self.stats = to_streamed_response_wrapper(referrals.stats) - - -class AsyncReferralsResourceWithStreamingResponse: - def __init__(self, referrals: AsyncReferralsResource) -> None: - self._referrals = referrals - self.list = async_to_streamed_response_wrapper(referrals.list) - self.get = async_to_streamed_response_wrapper(referrals.get) - self.create = async_to_streamed_response_wrapper(referrals.create) - self.track = async_to_streamed_response_wrapper(referrals.track) - self.validate = async_to_streamed_response_wrapper(referrals.validate) - self.complete = async_to_streamed_response_wrapper(referrals.complete) - self.stats = async_to_streamed_response_wrapper(referrals.stats) diff --git a/pkg/hanzoai/resources/registry.py b/pkg/hanzoai/resources/registry.py deleted file mode 100644 index 23b927a93..000000000 --- a/pkg/hanzoai/resources/registry.py +++ /dev/null @@ -1,358 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class RegistryResource(SyncAPIResource): - """Container registry management.""" - - @cached_property - def with_raw_response(self) -> RegistryResourceWithRawResponse: - return RegistryResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> RegistryResourceWithStreamingResponse: - return RegistryResourceWithStreamingResponse(self) - - def list_repos( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List repositories.""" - return self._get( - "/registry/repos", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_repo( - self, - *, - name: str, - visibility: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a repository.""" - return self._post( - "/registry/repos", - body={"name": name, "visibility": visibility}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_repo( - self, - repo_name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a repository.""" - return self._delete( - f"/registry/repos/{repo_name}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_images( - self, - repo_name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List images in a repository.""" - return self._get( - f"/registry/repos/{repo_name}/images", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def tag_image( - self, - repo_name: str, - *, - source_tag: str, - target_tag: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Tag an image.""" - return self._post( - f"/registry/repos/{repo_name}/tag", - body={"source_tag": source_tag, "target_tag": target_tag}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_image( - self, - repo_name: str, - tag: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete an image.""" - return self._delete( - f"/registry/repos/{repo_name}/images/{tag}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def login( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get registry login credentials.""" - return self._post( - "/registry/login", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncRegistryResource(AsyncAPIResource): - """Container registry management.""" - - @cached_property - def with_raw_response(self) -> AsyncRegistryResourceWithRawResponse: - return AsyncRegistryResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncRegistryResourceWithStreamingResponse: - return AsyncRegistryResourceWithStreamingResponse(self) - - async def list_repos( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List repositories.""" - return await self._get( - "/registry/repos", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_repo( - self, - *, - name: str, - visibility: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a repository.""" - return await self._post( - "/registry/repos", - body={"name": name, "visibility": visibility}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_repo( - self, - repo_name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a repository.""" - return await self._delete( - f"/registry/repos/{repo_name}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_images( - self, - repo_name: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List images in a repository.""" - return await self._get( - f"/registry/repos/{repo_name}/images", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def tag_image( - self, - repo_name: str, - *, - source_tag: str, - target_tag: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Tag an image.""" - return await self._post( - f"/registry/repos/{repo_name}/tag", - body={"source_tag": source_tag, "target_tag": target_tag}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_image( - self, - repo_name: str, - tag: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete an image.""" - return await self._delete( - f"/registry/repos/{repo_name}/images/{tag}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def login( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get registry login credentials.""" - return await self._post( - "/registry/login", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class RegistryResourceWithRawResponse: - def __init__(self, registry: RegistryResource) -> None: - self._registry = registry - - -class AsyncRegistryResourceWithRawResponse: - def __init__(self, registry: AsyncRegistryResource) -> None: - self._registry = registry - - -class RegistryResourceWithStreamingResponse: - def __init__(self, registry: RegistryResource) -> None: - self._registry = registry - - -class AsyncRegistryResourceWithStreamingResponse: - def __init__(self, registry: AsyncRegistryResource) -> None: - self._registry = registry diff --git a/pkg/hanzoai/resources/release.py b/pkg/hanzoai/resources/release.py deleted file mode 100644 index c98a0541f..000000000 --- a/pkg/hanzoai/resources/release.py +++ /dev/null @@ -1,274 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class ReleaseResource(SyncAPIResource): - """Release management for environment promotion.""" - - @cached_property - def with_raw_response(self) -> ReleaseResourceWithRawResponse: - return ReleaseResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ReleaseResourceWithStreamingResponse: - return ReleaseResourceWithStreamingResponse(self) - - def create( - self, - *, - name: str, - source: str, - revision: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a release from a revision.""" - return self._post( - "/release", - body={"name": name, "source": source, "revision": revision}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List releases.""" - return self._get( - "/release", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - release_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get release details.""" - return self._get( - f"/release/{release_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def promote( - self, - release_id: str, - *, - target_env: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Promote release to target environment.""" - return self._post( - f"/release/{release_id}/promote", - body={"target_env": target_env}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def rollback( - self, - release_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Rollback a release.""" - return self._post( - f"/release/{release_id}/rollback", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncReleaseResource(AsyncAPIResource): - """Release management for environment promotion.""" - - @cached_property - def with_raw_response(self) -> AsyncReleaseResourceWithRawResponse: - return AsyncReleaseResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncReleaseResourceWithStreamingResponse: - return AsyncReleaseResourceWithStreamingResponse(self) - - async def create( - self, - *, - name: str, - source: str, - revision: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a release from a revision.""" - return await self._post( - "/release", - body={"name": name, "source": source, "revision": revision}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List releases.""" - return await self._get( - "/release", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - release_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get release details.""" - return await self._get( - f"/release/{release_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def promote( - self, - release_id: str, - *, - target_env: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Promote release to target environment.""" - return await self._post( - f"/release/{release_id}/promote", - body={"target_env": target_env}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def rollback( - self, - release_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Rollback a release.""" - return await self._post( - f"/release/{release_id}/rollback", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class ReleaseResourceWithRawResponse: - def __init__(self, release: ReleaseResource) -> None: - self._release = release - - -class AsyncReleaseResourceWithRawResponse: - def __init__(self, release: AsyncReleaseResource) -> None: - self._release = release - - -class ReleaseResourceWithStreamingResponse: - def __init__(self, release: ReleaseResource) -> None: - self._release = release - - -class AsyncReleaseResourceWithStreamingResponse: - def __init__(self, release: AsyncReleaseResource) -> None: - self._release = release diff --git a/pkg/hanzoai/resources/responses/__init__.py b/pkg/hanzoai/resources/responses/__init__.py deleted file mode 100644 index 350930c8f..000000000 --- a/pkg/hanzoai/resources/responses/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Hanzo AI SDK - -from .responses import ( - ResponsesResource, - AsyncResponsesResource, - ResponsesResourceWithRawResponse, - AsyncResponsesResourceWithRawResponse, - ResponsesResourceWithStreamingResponse, - AsyncResponsesResourceWithStreamingResponse, -) -from .input_items import ( - InputItemsResource, - AsyncInputItemsResource, - InputItemsResourceWithRawResponse, - AsyncInputItemsResourceWithRawResponse, - InputItemsResourceWithStreamingResponse, - AsyncInputItemsResourceWithStreamingResponse, -) - -__all__ = [ - "InputItemsResource", - "AsyncInputItemsResource", - "InputItemsResourceWithRawResponse", - "AsyncInputItemsResourceWithRawResponse", - "InputItemsResourceWithStreamingResponse", - "AsyncInputItemsResourceWithStreamingResponse", - "ResponsesResource", - "AsyncResponsesResource", - "ResponsesResourceWithRawResponse", - "AsyncResponsesResourceWithRawResponse", - "ResponsesResourceWithStreamingResponse", - "AsyncResponsesResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/secrets.py b/pkg/hanzoai/resources/secrets.py deleted file mode 100644 index 1ba6be4ff..000000000 --- a/pkg/hanzoai/resources/secrets.py +++ /dev/null @@ -1,303 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Dict - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["SecretsResource", "AsyncSecretsResource"] - - -class SecretsResource(SyncAPIResource): - """Secret and credential management.""" - - @cached_property - def with_raw_response(self) -> SecretsResourceWithRawResponse: - return SecretsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> SecretsResourceWithStreamingResponse: - return SecretsResourceWithStreamingResponse(self) - - def list( - self, - *, - namespace: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all secrets.""" - return self._get( - "/infrastructure/secrets", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"namespace": namespace}, - ), - cast_to=object, - ) - - def get( - self, - secret_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific secret (metadata only).""" - return self._get( - f"/infrastructure/secrets/{secret_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - namespace: str, - data: Dict[str, str], - type: str = "Opaque", - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new secret.""" - return self._post( - "/infrastructure/secrets", - body={"name": name, "namespace": namespace, "data": data, "type": type}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - secret_id: str, - *, - data: Dict[str, str], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a secret.""" - return self._put( - f"/infrastructure/secrets/{secret_id}", - body={"data": data}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - secret_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a secret.""" - return self._delete( - f"/infrastructure/secrets/{secret_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncSecretsResource(AsyncAPIResource): - """Secret and credential management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncSecretsResourceWithRawResponse: - return AsyncSecretsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncSecretsResourceWithStreamingResponse: - return AsyncSecretsResourceWithStreamingResponse(self) - - async def list( - self, - *, - namespace: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/infrastructure/secrets", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"namespace": namespace}, - ), - cast_to=object, - ) - - async def get( - self, - secret_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/infrastructure/secrets/{secret_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - namespace: str, - data: Dict[str, str], - type: str = "Opaque", - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/infrastructure/secrets", - body={"name": name, "namespace": namespace, "data": data, "type": type}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - secret_id: str, - *, - data: Dict[str, str], - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._put( - f"/infrastructure/secrets/{secret_id}", - body={"data": data}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - secret_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/infrastructure/secrets/{secret_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class SecretsResourceWithRawResponse: - def __init__(self, secrets: SecretsResource) -> None: - self._secrets = secrets - self.list = to_raw_response_wrapper(secrets.list) - self.get = to_raw_response_wrapper(secrets.get) - self.create = to_raw_response_wrapper(secrets.create) - self.update = to_raw_response_wrapper(secrets.update) - self.delete = to_raw_response_wrapper(secrets.delete) - - -class AsyncSecretsResourceWithRawResponse: - def __init__(self, secrets: AsyncSecretsResource) -> None: - self._secrets = secrets - self.list = async_to_raw_response_wrapper(secrets.list) - self.get = async_to_raw_response_wrapper(secrets.get) - self.create = async_to_raw_response_wrapper(secrets.create) - self.update = async_to_raw_response_wrapper(secrets.update) - self.delete = async_to_raw_response_wrapper(secrets.delete) - - -class SecretsResourceWithStreamingResponse: - def __init__(self, secrets: SecretsResource) -> None: - self._secrets = secrets - self.list = to_streamed_response_wrapper(secrets.list) - self.get = to_streamed_response_wrapper(secrets.get) - self.create = to_streamed_response_wrapper(secrets.create) - self.update = to_streamed_response_wrapper(secrets.update) - self.delete = to_streamed_response_wrapper(secrets.delete) - - -class AsyncSecretsResourceWithStreamingResponse: - def __init__(self, secrets: AsyncSecretsResource) -> None: - self._secrets = secrets - self.list = async_to_streamed_response_wrapper(secrets.list) - self.get = async_to_streamed_response_wrapper(secrets.get) - self.create = async_to_streamed_response_wrapper(secrets.create) - self.update = async_to_streamed_response_wrapper(secrets.update) - self.delete = async_to_streamed_response_wrapper(secrets.delete) diff --git a/pkg/hanzoai/resources/settings.py b/pkg/hanzoai/resources/settings.py deleted file mode 100644 index d9a809d6c..000000000 --- a/pkg/hanzoai/resources/settings.py +++ /dev/null @@ -1,188 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["SettingsResource", "AsyncSettingsResource"] - - -class SettingsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> SettingsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return SettingsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> SettingsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return SettingsResourceWithStreamingResponse(self) - - def retrieve( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Returns a list of hanzo level settings - - This is useful for debugging and ensuring the proxy server is configured - correctly. - - Response schema: - - ``` - { - "alerting": _alerting, - "hanzo.callbacks": hanzo_callbacks, - "hanzo.input_callback": hanzo_input_callbacks, - "hanzo.failure_callback": hanzo_failure_callbacks, - "hanzo.success_callback": hanzo_success_callbacks, - "hanzo._async_success_callback": hanzo_async_success_callbacks, - "hanzo._async_failure_callback": hanzo_async_failure_callbacks, - "hanzo._async_input_callback": hanzo_async_input_callbacks, - "all_hanzo_callbacks": all_hanzo_callbacks, - "num_callbacks": len(all_hanzo_callbacks), - "num_alerting": _num_alerting, - "hanzo.request_timeout": hanzo.request_timeout, - } - ``` - """ - return self._get( - "/settings", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncSettingsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncSettingsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncSettingsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncSettingsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncSettingsResourceWithStreamingResponse(self) - - async def retrieve( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Returns a list of hanzo level settings - - This is useful for debugging and ensuring the proxy server is configured - correctly. - - Response schema: - - ``` - { - "alerting": _alerting, - "hanzo.callbacks": hanzo_callbacks, - "hanzo.input_callback": hanzo_input_callbacks, - "hanzo.failure_callback": hanzo_failure_callbacks, - "hanzo.success_callback": hanzo_success_callbacks, - "hanzo._async_success_callback": hanzo_async_success_callbacks, - "hanzo._async_failure_callback": hanzo_async_failure_callbacks, - "hanzo._async_input_callback": hanzo_async_input_callbacks, - "all_hanzo_callbacks": all_hanzo_callbacks, - "num_callbacks": len(all_hanzo_callbacks), - "num_alerting": _num_alerting, - "hanzo.request_timeout": hanzo.request_timeout, - } - ``` - """ - return await self._get( - "/settings", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class SettingsResourceWithRawResponse: - def __init__(self, settings: SettingsResource) -> None: - self._settings = settings - - self.retrieve = to_raw_response_wrapper( - settings.retrieve, - ) - - -class AsyncSettingsResourceWithRawResponse: - def __init__(self, settings: AsyncSettingsResource) -> None: - self._settings = settings - - self.retrieve = async_to_raw_response_wrapper( - settings.retrieve, - ) - - -class SettingsResourceWithStreamingResponse: - def __init__(self, settings: SettingsResource) -> None: - self._settings = settings - - self.retrieve = to_streamed_response_wrapper( - settings.retrieve, - ) - - -class AsyncSettingsResourceWithStreamingResponse: - def __init__(self, settings: AsyncSettingsResource) -> None: - self._settings = settings - - self.retrieve = async_to_streamed_response_wrapper( - settings.retrieve, - ) diff --git a/pkg/hanzoai/resources/spend.py b/pkg/hanzoai/resources/spend.py deleted file mode 100644 index 9407f6022..000000000 --- a/pkg/hanzoai/resources/spend.py +++ /dev/null @@ -1,597 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Iterable, Optional - -import httpx - -from ..types import ( - spend_list_logs_params, - spend_list_tags_params, - spend_calculate_spend_params, -) -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - maybe_transform, - async_maybe_transform, -) -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options -from ..types.spend_list_logs_response import SpendListLogsResponse -from ..types.spend_list_tags_response import SpendListTagsResponse - -__all__ = ["SpendResource", "AsyncSpendResource"] - - -class SpendResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> SpendResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return SpendResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> SpendResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return SpendResourceWithStreamingResponse(self) - - def calculate_spend( - self, - *, - completion_response: Optional[object] | NotGiven = NOT_GIVEN, - messages: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Accepts all the params of completion_cost. - - Calculate spend **before** making call: - - Note: If you see a spend of $0.0 you need to set custom_pricing for your model: - https://docs.hanzo.ai/docs/proxy/custom_pricing - - ``` - curl --location 'http://localhost:4000/spend/calculate' - --header 'Authorization: Bearer sk-1234' - --header 'Content-Type: application/json' - --data '{ - "model": "anthropic.claude-v2", - "messages": [{"role": "user", "content": "Hey, how'''s it going?"}] - }' - ``` - - Calculate spend **after** making call: - - ``` - curl --location 'http://localhost:4000/spend/calculate' - --header 'Authorization: Bearer sk-1234' - --header 'Content-Type: application/json' - --data '{ - "completion_response": { - "id": "chatcmpl-123", - "object": "chat.completion", - "created": 1677652288, - "model": "gpt-3.5-turbo-0125", - "system_fingerprint": "fp_44709d6fcb", - "choices": [{ - "index": 0, - "message": { - "role": "assistant", - "content": "Hello there, how may I assist you today?" - }, - "logprobs": null, - "finish_reason": "stop" - }] - "usage": { - "prompt_tokens": 9, - "completion_tokens": 12, - "total_tokens": 21 - } - } - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/spend/calculate", - body=maybe_transform( - { - "completion_response": completion_response, - "messages": messages, - "model": model, - }, - spend_calculate_spend_params.SpendCalculateSpendParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_logs( - self, - *, - api_key: Optional[str] | NotGiven = NOT_GIVEN, - end_date: Optional[str] | NotGiven = NOT_GIVEN, - request_id: Optional[str] | NotGiven = NOT_GIVEN, - start_date: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SpendListLogsResponse: - """ - View all spend logs, if request_id is provided, only logs for that request_id - will be returned - - Example Request for all logs - - ``` - curl -X GET "http://0.0.0.0:8000/spend/logs" -H "Authorization: Bearer sk-1234" - ``` - - Example Request for specific request_id - - ``` - curl -X GET "http://0.0.0.0:8000/spend/logs?request_id=chatcmpl-6dcb2540-d3d7-4e49-bb27-291f863f112e" -H "Authorization: Bearer sk-1234" - ``` - - Example Request for specific api_key - - ``` - curl -X GET "http://0.0.0.0:8000/spend/logs?api_key=sk-Fn8Ej39NkBQmUagFEoUWPQ" -H "Authorization: Bearer sk-1234" - ``` - - Example Request for specific user_id - - ``` - curl -X GET "http://0.0.0.0:8000/spend/logs?user_id=ishaan@berri.ai" -H "Authorization: Bearer sk-1234" - ``` - - Args: - api_key: Get spend logs based on api key - - end_date: Time till which to view key spend - - request_id: request_id to get spend logs for specific request_id. If none passed then pass - spend logs for all requests - - start_date: Time from which to start viewing key spend - - user_id: Get spend logs based on user_id - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/spend/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "api_key": api_key, - "end_date": end_date, - "request_id": request_id, - "start_date": start_date, - "user_id": user_id, - }, - spend_list_logs_params.SpendListLogsParams, - ), - ), - cast_to=SpendListLogsResponse, - ) - - def list_tags( - self, - *, - end_date: Optional[str] | NotGiven = NOT_GIVEN, - start_date: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SpendListTagsResponse: - """ - Hanzo Enterprise - View Spend Per Request Tag - - Example Request: - - ``` - curl -X GET "http://0.0.0.0:8000/spend/tags" -H "Authorization: Bearer sk-1234" - ``` - - Spend with Start Date and End Date - - ``` - curl -X GET "http://0.0.0.0:8000/spend/tags?start_date=2022-01-01&end_date=2022-02-01" -H "Authorization: Bearer sk-1234" - ``` - - Args: - end_date: Time till which to view key spend - - start_date: Time from which to start viewing key spend - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/spend/tags", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "end_date": end_date, - "start_date": start_date, - }, - spend_list_tags_params.SpendListTagsParams, - ), - ), - cast_to=SpendListTagsResponse, - ) - - -class AsyncSpendResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncSpendResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncSpendResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncSpendResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncSpendResourceWithStreamingResponse(self) - - async def calculate_spend( - self, - *, - completion_response: Optional[object] | NotGiven = NOT_GIVEN, - messages: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """ - Accepts all the params of completion_cost. - - Calculate spend **before** making call: - - Note: If you see a spend of $0.0 you need to set custom_pricing for your model: - https://docs.hanzo.ai/docs/proxy/custom_pricing - - ``` - curl --location 'http://localhost:4000/spend/calculate' - --header 'Authorization: Bearer sk-1234' - --header 'Content-Type: application/json' - --data '{ - "model": "anthropic.claude-v2", - "messages": [{"role": "user", "content": "Hey, how'''s it going?"}] - }' - ``` - - Calculate spend **after** making call: - - ``` - curl --location 'http://localhost:4000/spend/calculate' - --header 'Authorization: Bearer sk-1234' - --header 'Content-Type: application/json' - --data '{ - "completion_response": { - "id": "chatcmpl-123", - "object": "chat.completion", - "created": 1677652288, - "model": "gpt-3.5-turbo-0125", - "system_fingerprint": "fp_44709d6fcb", - "choices": [{ - "index": 0, - "message": { - "role": "assistant", - "content": "Hello there, how may I assist you today?" - }, - "logprobs": null, - "finish_reason": "stop" - }] - "usage": { - "prompt_tokens": 9, - "completion_tokens": 12, - "total_tokens": 21 - } - } - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/spend/calculate", - body=await async_maybe_transform( - { - "completion_response": completion_response, - "messages": messages, - "model": model, - }, - spend_calculate_spend_params.SpendCalculateSpendParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_logs( - self, - *, - api_key: Optional[str] | NotGiven = NOT_GIVEN, - end_date: Optional[str] | NotGiven = NOT_GIVEN, - request_id: Optional[str] | NotGiven = NOT_GIVEN, - start_date: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SpendListLogsResponse: - """ - View all spend logs, if request_id is provided, only logs for that request_id - will be returned - - Example Request for all logs - - ``` - curl -X GET "http://0.0.0.0:8000/spend/logs" -H "Authorization: Bearer sk-1234" - ``` - - Example Request for specific request_id - - ``` - curl -X GET "http://0.0.0.0:8000/spend/logs?request_id=chatcmpl-6dcb2540-d3d7-4e49-bb27-291f863f112e" -H "Authorization: Bearer sk-1234" - ``` - - Example Request for specific api_key - - ``` - curl -X GET "http://0.0.0.0:8000/spend/logs?api_key=sk-Fn8Ej39NkBQmUagFEoUWPQ" -H "Authorization: Bearer sk-1234" - ``` - - Example Request for specific user_id - - ``` - curl -X GET "http://0.0.0.0:8000/spend/logs?user_id=ishaan@berri.ai" -H "Authorization: Bearer sk-1234" - ``` - - Args: - api_key: Get spend logs based on api key - - end_date: Time till which to view key spend - - request_id: request_id to get spend logs for specific request_id. If none passed then pass - spend logs for all requests - - start_date: Time from which to start viewing key spend - - user_id: Get spend logs based on user_id - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/spend/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "api_key": api_key, - "end_date": end_date, - "request_id": request_id, - "start_date": start_date, - "user_id": user_id, - }, - spend_list_logs_params.SpendListLogsParams, - ), - ), - cast_to=SpendListLogsResponse, - ) - - async def list_tags( - self, - *, - end_date: Optional[str] | NotGiven = NOT_GIVEN, - start_date: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SpendListTagsResponse: - """ - Hanzo Enterprise - View Spend Per Request Tag - - Example Request: - - ``` - curl -X GET "http://0.0.0.0:8000/spend/tags" -H "Authorization: Bearer sk-1234" - ``` - - Spend with Start Date and End Date - - ``` - curl -X GET "http://0.0.0.0:8000/spend/tags?start_date=2022-01-01&end_date=2022-02-01" -H "Authorization: Bearer sk-1234" - ``` - - Args: - end_date: Time till which to view key spend - - start_date: Time from which to start viewing key spend - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/spend/tags", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "end_date": end_date, - "start_date": start_date, - }, - spend_list_tags_params.SpendListTagsParams, - ), - ), - cast_to=SpendListTagsResponse, - ) - - -class SpendResourceWithRawResponse: - def __init__(self, spend: SpendResource) -> None: - self._spend = spend - - self.calculate_spend = to_raw_response_wrapper( - spend.calculate_spend, - ) - self.list_logs = to_raw_response_wrapper( - spend.list_logs, - ) - self.list_tags = to_raw_response_wrapper( - spend.list_tags, - ) - - -class AsyncSpendResourceWithRawResponse: - def __init__(self, spend: AsyncSpendResource) -> None: - self._spend = spend - - self.calculate_spend = async_to_raw_response_wrapper( - spend.calculate_spend, - ) - self.list_logs = async_to_raw_response_wrapper( - spend.list_logs, - ) - self.list_tags = async_to_raw_response_wrapper( - spend.list_tags, - ) - - -class SpendResourceWithStreamingResponse: - def __init__(self, spend: SpendResource) -> None: - self._spend = spend - - self.calculate_spend = to_streamed_response_wrapper( - spend.calculate_spend, - ) - self.list_logs = to_streamed_response_wrapper( - spend.list_logs, - ) - self.list_tags = to_streamed_response_wrapper( - spend.list_tags, - ) - - -class AsyncSpendResourceWithStreamingResponse: - def __init__(self, spend: AsyncSpendResource) -> None: - self._spend = spend - - self.calculate_spend = async_to_streamed_response_wrapper( - spend.calculate_spend, - ) - self.list_logs = async_to_streamed_response_wrapper( - spend.list_logs, - ) - self.list_tags = async_to_streamed_response_wrapper( - spend.list_tags, - ) diff --git a/pkg/hanzoai/resources/storage.py b/pkg/hanzoai/resources/storage.py deleted file mode 100644 index 229b8a475..000000000 --- a/pkg/hanzoai/resources/storage.py +++ /dev/null @@ -1,462 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, Optional - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["StorageResource", "AsyncStorageResource"] - - -class StorageResource(SyncAPIResource): - """Object storage service.""" - - @cached_property - def with_raw_response(self) -> StorageResourceWithRawResponse: - return StorageResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> StorageResourceWithStreamingResponse: - return StorageResourceWithStreamingResponse(self) - - def list_buckets( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all storage buckets.""" - return self._get( - "/storage/buckets", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create_bucket( - self, - *, - name: str, - region: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new storage bucket.""" - return self._post( - "/storage/buckets", - body={"name": name, "region": region}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_bucket( - self, - bucket_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a storage bucket.""" - return self._delete( - f"/storage/buckets/{bucket_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_objects( - self, - bucket_id: str, - *, - prefix: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List objects in a bucket.""" - return self._get( - f"/storage/buckets/{bucket_id}/objects", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"prefix": prefix, "limit": limit}, - ), - cast_to=object, - ) - - def get_object( - self, - bucket_id: str, - key: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get an object from a bucket.""" - return self._get( - f"/storage/buckets/{bucket_id}/objects/{key}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def put_object( - self, - bucket_id: str, - key: str, - *, - data: bytes, - content_type: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Put an object into a bucket.""" - return self._put( - f"/storage/buckets/{bucket_id}/objects/{key}", - body=data, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_object( - self, - bucket_id: str, - key: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete an object from a bucket.""" - return self._delete( - f"/storage/buckets/{bucket_id}/objects/{key}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get_presigned_url( - self, - bucket_id: str, - key: str, - *, - expires_in: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a presigned URL for an object.""" - return self._post( - f"/storage/buckets/{bucket_id}/presign", - body={"key": key, "expires_in": expires_in}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncStorageResource(AsyncAPIResource): - """Object storage service (async).""" - - @cached_property - def with_raw_response(self) -> AsyncStorageResourceWithRawResponse: - return AsyncStorageResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncStorageResourceWithStreamingResponse: - return AsyncStorageResourceWithStreamingResponse(self) - - async def list_buckets( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all storage buckets.""" - return await self._get( - "/storage/buckets", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create_bucket( - self, - *, - name: str, - region: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new storage bucket.""" - return await self._post( - "/storage/buckets", - body={"name": name, "region": region}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_bucket( - self, - bucket_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a storage bucket.""" - return await self._delete( - f"/storage/buckets/{bucket_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_objects( - self, - bucket_id: str, - *, - prefix: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List objects in a bucket.""" - return await self._get( - f"/storage/buckets/{bucket_id}/objects", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"prefix": prefix, "limit": limit}, - ), - cast_to=object, - ) - - async def get_object( - self, - bucket_id: str, - key: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get an object from a bucket.""" - return await self._get( - f"/storage/buckets/{bucket_id}/objects/{key}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def put_object( - self, - bucket_id: str, - key: str, - *, - data: bytes, - content_type: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Put an object into a bucket.""" - return await self._put( - f"/storage/buckets/{bucket_id}/objects/{key}", - body=data, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_object( - self, - bucket_id: str, - key: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete an object from a bucket.""" - return await self._delete( - f"/storage/buckets/{bucket_id}/objects/{key}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get_presigned_url( - self, - bucket_id: str, - key: str, - *, - expires_in: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a presigned URL for an object.""" - return await self._post( - f"/storage/buckets/{bucket_id}/presign", - body={"key": key, "expires_in": expires_in}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class StorageResourceWithRawResponse: - def __init__(self, storage: StorageResource) -> None: - self._storage = storage - self.list_buckets = to_raw_response_wrapper(storage.list_buckets) - self.create_bucket = to_raw_response_wrapper(storage.create_bucket) - self.delete_bucket = to_raw_response_wrapper(storage.delete_bucket) - self.list_objects = to_raw_response_wrapper(storage.list_objects) - self.get_object = to_raw_response_wrapper(storage.get_object) - self.put_object = to_raw_response_wrapper(storage.put_object) - self.delete_object = to_raw_response_wrapper(storage.delete_object) - self.get_presigned_url = to_raw_response_wrapper(storage.get_presigned_url) - - -class AsyncStorageResourceWithRawResponse: - def __init__(self, storage: AsyncStorageResource) -> None: - self._storage = storage - self.list_buckets = async_to_raw_response_wrapper(storage.list_buckets) - self.create_bucket = async_to_raw_response_wrapper(storage.create_bucket) - self.delete_bucket = async_to_raw_response_wrapper(storage.delete_bucket) - self.list_objects = async_to_raw_response_wrapper(storage.list_objects) - self.get_object = async_to_raw_response_wrapper(storage.get_object) - self.put_object = async_to_raw_response_wrapper(storage.put_object) - self.delete_object = async_to_raw_response_wrapper(storage.delete_object) - self.get_presigned_url = async_to_raw_response_wrapper( - storage.get_presigned_url - ) - - -class StorageResourceWithStreamingResponse: - def __init__(self, storage: StorageResource) -> None: - self._storage = storage - self.list_buckets = to_streamed_response_wrapper(storage.list_buckets) - self.create_bucket = to_streamed_response_wrapper(storage.create_bucket) - self.delete_bucket = to_streamed_response_wrapper(storage.delete_bucket) - self.list_objects = to_streamed_response_wrapper(storage.list_objects) - self.get_object = to_streamed_response_wrapper(storage.get_object) - self.put_object = to_streamed_response_wrapper(storage.put_object) - self.delete_object = to_streamed_response_wrapper(storage.delete_object) - self.get_presigned_url = to_streamed_response_wrapper(storage.get_presigned_url) - - -class AsyncStorageResourceWithStreamingResponse: - def __init__(self, storage: AsyncStorageResource) -> None: - self._storage = storage - self.list_buckets = async_to_streamed_response_wrapper(storage.list_buckets) - self.create_bucket = async_to_streamed_response_wrapper(storage.create_bucket) - self.delete_bucket = async_to_streamed_response_wrapper(storage.delete_bucket) - self.list_objects = async_to_streamed_response_wrapper(storage.list_objects) - self.get_object = async_to_streamed_response_wrapper(storage.get_object) - self.put_object = async_to_streamed_response_wrapper(storage.put_object) - self.delete_object = async_to_streamed_response_wrapper(storage.delete_object) - self.get_presigned_url = async_to_streamed_response_wrapper( - storage.get_presigned_url - ) diff --git a/pkg/hanzoai/resources/stores.py b/pkg/hanzoai/resources/stores.py deleted file mode 100644 index f0115b0d1..000000000 --- a/pkg/hanzoai/resources/stores.py +++ /dev/null @@ -1,352 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, Optional - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["StoresResource", "AsyncStoresResource"] - - -class StoresResource(SyncAPIResource): - """Vector store management.""" - - @cached_property - def with_raw_response(self) -> StoresResourceWithRawResponse: - return StoresResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> StoresResourceWithStreamingResponse: - return StoresResourceWithStreamingResponse(self) - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all vector stores.""" - return self._get( - "/stores", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific vector store.""" - return self._get( - f"/stores/{store_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - provider: str | NotGiven = NOT_GIVEN, - config: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new vector store.""" - return self._post( - "/stores", - body={"name": name, "provider": provider, "config": config}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - store_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - config: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a vector store.""" - return self._put( - f"/stores/{store_id}", - body={"name": name, "config": config}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a vector store.""" - return self._delete( - f"/stores/{store_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def refresh_vectors( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Refresh vectors in a store.""" - return self._post( - f"/stores/{store_id}/vectors/refresh", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncStoresResource(AsyncAPIResource): - """Vector store management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncStoresResourceWithRawResponse: - return AsyncStoresResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncStoresResourceWithStreamingResponse: - return AsyncStoresResourceWithStreamingResponse(self) - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all vector stores.""" - return await self._get( - "/stores", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific vector store.""" - return await self._get( - f"/stores/{store_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - provider: str | NotGiven = NOT_GIVEN, - config: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new vector store.""" - return await self._post( - "/stores", - body={"name": name, "provider": provider, "config": config}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - store_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - config: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a vector store.""" - return await self._put( - f"/stores/{store_id}", - body={"name": name, "config": config}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a vector store.""" - return await self._delete( - f"/stores/{store_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def refresh_vectors( - self, - store_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Refresh vectors in a store.""" - return await self._post( - f"/stores/{store_id}/vectors/refresh", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class StoresResourceWithRawResponse: - def __init__(self, stores: StoresResource) -> None: - self._stores = stores - self.list = to_raw_response_wrapper(stores.list) - self.get = to_raw_response_wrapper(stores.get) - self.create = to_raw_response_wrapper(stores.create) - self.update = to_raw_response_wrapper(stores.update) - self.delete = to_raw_response_wrapper(stores.delete) - self.refresh_vectors = to_raw_response_wrapper(stores.refresh_vectors) - - -class AsyncStoresResourceWithRawResponse: - def __init__(self, stores: AsyncStoresResource) -> None: - self._stores = stores - self.list = async_to_raw_response_wrapper(stores.list) - self.get = async_to_raw_response_wrapper(stores.get) - self.create = async_to_raw_response_wrapper(stores.create) - self.update = async_to_raw_response_wrapper(stores.update) - self.delete = async_to_raw_response_wrapper(stores.delete) - self.refresh_vectors = async_to_raw_response_wrapper(stores.refresh_vectors) - - -class StoresResourceWithStreamingResponse: - def __init__(self, stores: StoresResource) -> None: - self._stores = stores - self.list = to_streamed_response_wrapper(stores.list) - self.get = to_streamed_response_wrapper(stores.get) - self.create = to_streamed_response_wrapper(stores.create) - self.update = to_streamed_response_wrapper(stores.update) - self.delete = to_streamed_response_wrapper(stores.delete) - self.refresh_vectors = to_streamed_response_wrapper(stores.refresh_vectors) - - -class AsyncStoresResourceWithStreamingResponse: - def __init__(self, stores: AsyncStoresResource) -> None: - self._stores = stores - self.list = async_to_streamed_response_wrapper(stores.list) - self.get = async_to_streamed_response_wrapper(stores.get) - self.create = async_to_streamed_response_wrapper(stores.create) - self.update = async_to_streamed_response_wrapper(stores.update) - self.delete = async_to_streamed_response_wrapper(stores.delete) - self.refresh_vectors = async_to_streamed_response_wrapper( - stores.refresh_vectors - ) diff --git a/pkg/hanzoai/resources/subscriptions.py b/pkg/hanzoai/resources/subscriptions.py deleted file mode 100644 index db49fbb30..000000000 --- a/pkg/hanzoai/resources/subscriptions.py +++ /dev/null @@ -1,460 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["SubscriptionsResource", "AsyncSubscriptionsResource"] - - -class SubscriptionsResource(SyncAPIResource): - """Subscription management.""" - - @cached_property - def with_raw_response(self) -> SubscriptionsResourceWithRawResponse: - return SubscriptionsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> SubscriptionsResourceWithStreamingResponse: - return SubscriptionsResourceWithStreamingResponse(self) - - def list( - self, - *, - status: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all subscriptions.""" - return self._get( - "/commerce/subscriptions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"status": status, "limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - def get( - self, - subscription_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific subscription.""" - return self._get( - f"/commerce/subscriptions/{subscription_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - plan_id: str, - customer_id: str, - payment_method_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new subscription.""" - return self._post( - "/commerce/subscriptions", - body={ - "plan_id": plan_id, - "customer_id": customer_id, - "payment_method_id": payment_method_id, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - subscription_id: str, - *, - plan_id: str | NotGiven = NOT_GIVEN, - quantity: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a subscription.""" - return self._put( - f"/commerce/subscriptions/{subscription_id}", - body={"plan_id": plan_id, "quantity": quantity}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def cancel( - self, - subscription_id: str, - *, - at_period_end: bool = True, - reason: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Cancel a subscription.""" - return self._post( - f"/commerce/subscriptions/{subscription_id}/cancel", - body={"at_period_end": at_period_end, "reason": reason}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def pause( - self, - subscription_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Pause a subscription.""" - return self._post( - f"/commerce/subscriptions/{subscription_id}/pause", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def resume( - self, - subscription_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Resume a paused subscription.""" - return self._post( - f"/commerce/subscriptions/{subscription_id}/resume", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_invoices( - self, - subscription_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List subscription invoices.""" - return self._get( - f"/commerce/subscriptions/{subscription_id}/invoices", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncSubscriptionsResource(AsyncAPIResource): - """Subscription management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncSubscriptionsResourceWithRawResponse: - return AsyncSubscriptionsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response( - self, - ) -> AsyncSubscriptionsResourceWithStreamingResponse: - return AsyncSubscriptionsResourceWithStreamingResponse(self) - - async def list( - self, - *, - status: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/commerce/subscriptions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"status": status, "limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - async def get( - self, - subscription_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/subscriptions/{subscription_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - plan_id: str, - customer_id: str, - payment_method_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/commerce/subscriptions", - body={ - "plan_id": plan_id, - "customer_id": customer_id, - "payment_method_id": payment_method_id, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - subscription_id: str, - *, - plan_id: str | NotGiven = NOT_GIVEN, - quantity: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._put( - f"/commerce/subscriptions/{subscription_id}", - body={"plan_id": plan_id, "quantity": quantity}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def cancel( - self, - subscription_id: str, - *, - at_period_end: bool = True, - reason: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/commerce/subscriptions/{subscription_id}/cancel", - body={"at_period_end": at_period_end, "reason": reason}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def pause( - self, - subscription_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/commerce/subscriptions/{subscription_id}/pause", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def resume( - self, - subscription_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/commerce/subscriptions/{subscription_id}/resume", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_invoices( - self, - subscription_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/commerce/subscriptions/{subscription_id}/invoices", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class SubscriptionsResourceWithRawResponse: - def __init__(self, subscriptions: SubscriptionsResource) -> None: - self._subscriptions = subscriptions - self.list = to_raw_response_wrapper(subscriptions.list) - self.get = to_raw_response_wrapper(subscriptions.get) - self.create = to_raw_response_wrapper(subscriptions.create) - self.update = to_raw_response_wrapper(subscriptions.update) - self.cancel = to_raw_response_wrapper(subscriptions.cancel) - self.pause = to_raw_response_wrapper(subscriptions.pause) - self.resume = to_raw_response_wrapper(subscriptions.resume) - self.list_invoices = to_raw_response_wrapper(subscriptions.list_invoices) - - -class AsyncSubscriptionsResourceWithRawResponse: - def __init__(self, subscriptions: AsyncSubscriptionsResource) -> None: - self._subscriptions = subscriptions - self.list = async_to_raw_response_wrapper(subscriptions.list) - self.get = async_to_raw_response_wrapper(subscriptions.get) - self.create = async_to_raw_response_wrapper(subscriptions.create) - self.update = async_to_raw_response_wrapper(subscriptions.update) - self.cancel = async_to_raw_response_wrapper(subscriptions.cancel) - self.pause = async_to_raw_response_wrapper(subscriptions.pause) - self.resume = async_to_raw_response_wrapper(subscriptions.resume) - self.list_invoices = async_to_raw_response_wrapper(subscriptions.list_invoices) - - -class SubscriptionsResourceWithStreamingResponse: - def __init__(self, subscriptions: SubscriptionsResource) -> None: - self._subscriptions = subscriptions - self.list = to_streamed_response_wrapper(subscriptions.list) - self.get = to_streamed_response_wrapper(subscriptions.get) - self.create = to_streamed_response_wrapper(subscriptions.create) - self.update = to_streamed_response_wrapper(subscriptions.update) - self.cancel = to_streamed_response_wrapper(subscriptions.cancel) - self.pause = to_streamed_response_wrapper(subscriptions.pause) - self.resume = to_streamed_response_wrapper(subscriptions.resume) - self.list_invoices = to_streamed_response_wrapper(subscriptions.list_invoices) - - -class AsyncSubscriptionsResourceWithStreamingResponse: - def __init__(self, subscriptions: AsyncSubscriptionsResource) -> None: - self._subscriptions = subscriptions - self.list = async_to_streamed_response_wrapper(subscriptions.list) - self.get = async_to_streamed_response_wrapper(subscriptions.get) - self.create = async_to_streamed_response_wrapper(subscriptions.create) - self.update = async_to_streamed_response_wrapper(subscriptions.update) - self.cancel = async_to_streamed_response_wrapper(subscriptions.cancel) - self.pause = async_to_streamed_response_wrapper(subscriptions.pause) - self.resume = async_to_streamed_response_wrapper(subscriptions.resume) - self.list_invoices = async_to_streamed_response_wrapper( - subscriptions.list_invoices - ) diff --git a/pkg/hanzoai/resources/tasks.py b/pkg/hanzoai/resources/tasks.py deleted file mode 100644 index 462689431..000000000 --- a/pkg/hanzoai/resources/tasks.py +++ /dev/null @@ -1,476 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["TasksResource", "AsyncTasksResource"] - - -class TasksResource(SyncAPIResource): - """Task management and scheduling.""" - - @cached_property - def with_raw_response(self) -> TasksResourceWithRawResponse: - return TasksResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> TasksResourceWithStreamingResponse: - return TasksResourceWithStreamingResponse(self) - - def list( - self, - *, - status: str | NotGiven = NOT_GIVEN, - type: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all tasks.""" - return self._get( - "/operations/tasks", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "status": status, - "type": type, - "limit": limit, - "offset": offset, - }, - ), - cast_to=object, - ) - - def get( - self, - task_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific task.""" - return self._get( - f"/operations/tasks/{task_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - type: str, - config: Dict[str, Any], - schedule: str | NotGiven = NOT_GIVEN, - priority: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new task.""" - return self._post( - "/operations/tasks", - body={ - "name": name, - "type": type, - "config": config, - "schedule": schedule, - "priority": priority, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - task_id: str, - *, - config: Dict[str, Any] | NotGiven = NOT_GIVEN, - schedule: str | NotGiven = NOT_GIVEN, - priority: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a task.""" - return self._put( - f"/operations/tasks/{task_id}", - body={"config": config, "schedule": schedule, "priority": priority}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - task_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a task.""" - return self._delete( - f"/operations/tasks/{task_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def run( - self, - task_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Run a task immediately.""" - return self._post( - f"/operations/tasks/{task_id}/run", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def cancel( - self, - task_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Cancel a running task.""" - return self._post( - f"/operations/tasks/{task_id}/cancel", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def logs( - self, - task_id: str, - *, - lines: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get task logs.""" - return self._get( - f"/operations/tasks/{task_id}/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"lines": lines}, - ), - cast_to=object, - ) - - -class AsyncTasksResource(AsyncAPIResource): - """Task management and scheduling (async).""" - - @cached_property - def with_raw_response(self) -> AsyncTasksResourceWithRawResponse: - return AsyncTasksResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncTasksResourceWithStreamingResponse: - return AsyncTasksResourceWithStreamingResponse(self) - - async def list( - self, - *, - status: str | NotGiven = NOT_GIVEN, - type: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/operations/tasks", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={ - "status": status, - "type": type, - "limit": limit, - "offset": offset, - }, - ), - cast_to=object, - ) - - async def get( - self, - task_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/operations/tasks/{task_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - type: str, - config: Dict[str, Any], - schedule: str | NotGiven = NOT_GIVEN, - priority: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/operations/tasks", - body={ - "name": name, - "type": type, - "config": config, - "schedule": schedule, - "priority": priority, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - task_id: str, - *, - config: Dict[str, Any] | NotGiven = NOT_GIVEN, - schedule: str | NotGiven = NOT_GIVEN, - priority: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._put( - f"/operations/tasks/{task_id}", - body={"config": config, "schedule": schedule, "priority": priority}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - task_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/operations/tasks/{task_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def run( - self, - task_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/operations/tasks/{task_id}/run", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def cancel( - self, - task_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/operations/tasks/{task_id}/cancel", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def logs( - self, - task_id: str, - *, - lines: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/operations/tasks/{task_id}/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"lines": lines}, - ), - cast_to=object, - ) - - -class TasksResourceWithRawResponse: - def __init__(self, tasks: TasksResource) -> None: - self._tasks = tasks - self.list = to_raw_response_wrapper(tasks.list) - self.get = to_raw_response_wrapper(tasks.get) - self.create = to_raw_response_wrapper(tasks.create) - self.update = to_raw_response_wrapper(tasks.update) - self.delete = to_raw_response_wrapper(tasks.delete) - self.run = to_raw_response_wrapper(tasks.run) - self.cancel = to_raw_response_wrapper(tasks.cancel) - self.logs = to_raw_response_wrapper(tasks.logs) - - -class AsyncTasksResourceWithRawResponse: - def __init__(self, tasks: AsyncTasksResource) -> None: - self._tasks = tasks - self.list = async_to_raw_response_wrapper(tasks.list) - self.get = async_to_raw_response_wrapper(tasks.get) - self.create = async_to_raw_response_wrapper(tasks.create) - self.update = async_to_raw_response_wrapper(tasks.update) - self.delete = async_to_raw_response_wrapper(tasks.delete) - self.run = async_to_raw_response_wrapper(tasks.run) - self.cancel = async_to_raw_response_wrapper(tasks.cancel) - self.logs = async_to_raw_response_wrapper(tasks.logs) - - -class TasksResourceWithStreamingResponse: - def __init__(self, tasks: TasksResource) -> None: - self._tasks = tasks - self.list = to_streamed_response_wrapper(tasks.list) - self.get = to_streamed_response_wrapper(tasks.get) - self.create = to_streamed_response_wrapper(tasks.create) - self.update = to_streamed_response_wrapper(tasks.update) - self.delete = to_streamed_response_wrapper(tasks.delete) - self.run = to_streamed_response_wrapper(tasks.run) - self.cancel = to_streamed_response_wrapper(tasks.cancel) - self.logs = to_streamed_response_wrapper(tasks.logs) - - -class AsyncTasksResourceWithStreamingResponse: - def __init__(self, tasks: AsyncTasksResource) -> None: - self._tasks = tasks - self.list = async_to_streamed_response_wrapper(tasks.list) - self.get = async_to_streamed_response_wrapper(tasks.get) - self.create = async_to_streamed_response_wrapper(tasks.create) - self.update = async_to_streamed_response_wrapper(tasks.update) - self.delete = async_to_streamed_response_wrapper(tasks.delete) - self.run = async_to_streamed_response_wrapper(tasks.run) - self.cancel = async_to_streamed_response_wrapper(tasks.cancel) - self.logs = async_to_streamed_response_wrapper(tasks.logs) diff --git a/pkg/hanzoai/resources/team/__init__.py b/pkg/hanzoai/resources/team/__init__.py deleted file mode 100644 index d135a10ba..000000000 --- a/pkg/hanzoai/resources/team/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# Hanzo AI SDK - -from .team import ( - TeamResource, - AsyncTeamResource, - TeamResourceWithRawResponse, - AsyncTeamResourceWithRawResponse, - TeamResourceWithStreamingResponse, - AsyncTeamResourceWithStreamingResponse, -) -from .model import ( - ModelResource, - AsyncModelResource, - ModelResourceWithRawResponse, - AsyncModelResourceWithRawResponse, - ModelResourceWithStreamingResponse, - AsyncModelResourceWithStreamingResponse, -) -from .callback import ( - CallbackResource, - AsyncCallbackResource, - CallbackResourceWithRawResponse, - AsyncCallbackResourceWithRawResponse, - CallbackResourceWithStreamingResponse, - AsyncCallbackResourceWithStreamingResponse, -) - -__all__ = [ - "ModelResource", - "AsyncModelResource", - "ModelResourceWithRawResponse", - "AsyncModelResourceWithRawResponse", - "ModelResourceWithStreamingResponse", - "AsyncModelResourceWithStreamingResponse", - "CallbackResource", - "AsyncCallbackResource", - "CallbackResourceWithRawResponse", - "AsyncCallbackResourceWithRawResponse", - "CallbackResourceWithStreamingResponse", - "AsyncCallbackResourceWithStreamingResponse", - "TeamResource", - "AsyncTeamResource", - "TeamResourceWithRawResponse", - "AsyncTeamResourceWithRawResponse", - "TeamResourceWithStreamingResponse", - "AsyncTeamResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/team/model.py b/pkg/hanzoai/resources/team/model.py deleted file mode 100644 index 23cb44697..000000000 --- a/pkg/hanzoai/resources/team/model.py +++ /dev/null @@ -1,347 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List - -import httpx - -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...types.team import model_add_params, model_remove_params -from ..._base_client import make_request_options - -__all__ = ["ModelResource", "AsyncModelResource"] - - -class ModelResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ModelResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return ModelResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ModelResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return ModelResourceWithStreamingResponse(self) - - def add( - self, - *, - models: List[str], - team_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add models to a team's allowed model list. - - Only proxy admin or team admin can - add models. - - Parameters: - - - team_id: str - Required. The team to add models to - - models: List[str] - Required. List of models to add to the team - - Example Request: - - ``` - curl --location 'http://0.0.0.0:4000/team/model/add' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "team_id": "team-1234", - "models": ["gpt-4", "claude-2"] - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/team/model/add", - body=maybe_transform( - { - "models": models, - "team_id": team_id, - }, - model_add_params.ModelAddParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def remove( - self, - *, - models: List[str], - team_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove models from a team's allowed model list. - - Only proxy admin or team admin - can remove models. - - Parameters: - - - team_id: str - Required. The team to remove models from - - models: List[str] - Required. List of models to remove from the team - - Example Request: - - ``` - curl --location 'http://0.0.0.0:4000/team/model/delete' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "team_id": "team-1234", - "models": ["gpt-4"] - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/team/model/delete", - body=maybe_transform( - { - "models": models, - "team_id": team_id, - }, - model_remove_params.ModelRemoveParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncModelResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncModelResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncModelResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncModelResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncModelResourceWithStreamingResponse(self) - - async def add( - self, - *, - models: List[str], - team_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Add models to a team's allowed model list. - - Only proxy admin or team admin can - add models. - - Parameters: - - - team_id: str - Required. The team to add models to - - models: List[str] - Required. List of models to add to the team - - Example Request: - - ``` - curl --location 'http://0.0.0.0:4000/team/model/add' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "team_id": "team-1234", - "models": ["gpt-4", "claude-2"] - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/team/model/add", - body=await async_maybe_transform( - { - "models": models, - "team_id": team_id, - }, - model_add_params.ModelAddParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def remove( - self, - *, - models: List[str], - team_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Remove models from a team's allowed model list. - - Only proxy admin or team admin - can remove models. - - Parameters: - - - team_id: str - Required. The team to remove models from - - models: List[str] - Required. List of models to remove from the team - - Example Request: - - ``` - curl --location 'http://0.0.0.0:4000/team/model/delete' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "team_id": "team-1234", - "models": ["gpt-4"] - }' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/team/model/delete", - body=await async_maybe_transform( - { - "models": models, - "team_id": team_id, - }, - model_remove_params.ModelRemoveParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class ModelResourceWithRawResponse: - def __init__(self, model: ModelResource) -> None: - self._model = model - - self.add = to_raw_response_wrapper( - model.add, - ) - self.remove = to_raw_response_wrapper( - model.remove, - ) - - -class AsyncModelResourceWithRawResponse: - def __init__(self, model: AsyncModelResource) -> None: - self._model = model - - self.add = async_to_raw_response_wrapper( - model.add, - ) - self.remove = async_to_raw_response_wrapper( - model.remove, - ) - - -class ModelResourceWithStreamingResponse: - def __init__(self, model: ModelResource) -> None: - self._model = model - - self.add = to_streamed_response_wrapper( - model.add, - ) - self.remove = to_streamed_response_wrapper( - model.remove, - ) - - -class AsyncModelResourceWithStreamingResponse: - def __init__(self, model: AsyncModelResource) -> None: - self._model = model - - self.add = async_to_streamed_response_wrapper( - model.add, - ) - self.remove = async_to_streamed_response_wrapper( - model.remove, - ) diff --git a/pkg/hanzoai/resources/team_workspace.py b/pkg/hanzoai/resources/team_workspace.py deleted file mode 100644 index 7cb406390..000000000 --- a/pkg/hanzoai/resources/team_workspace.py +++ /dev/null @@ -1,1614 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List, Optional - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["TeamWorkspaceResource", "AsyncTeamWorkspaceResource"] - - -class TeamWorkspaceResource(SyncAPIResource): - """Hanzo Team collaborative workspace platform. - - Backed by the Team account-service (Huly/Santeam-derived). - Provides account management, workspace CRUD, member invitations, - OTP/MFA, social login, and admin statistics. - - All endpoints use the ``/team/`` path prefix. The SDK gateway - translates REST calls to the underlying JSON-RPC account service. - """ - - @cached_property - def with_raw_response(self) -> TeamWorkspaceResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return TeamWorkspaceResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> TeamWorkspaceResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return TeamWorkspaceResourceWithStreamingResponse(self) - - # ------------------------------------------------------------------ # - # Accounts - # ------------------------------------------------------------------ # - - def create_account( - self, - *, - email: str, - password: str, - first: str, - last: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new Team workspace account. - - Args: - email: Account email address. - password: Account password (hashed server-side). - first: First name. - last: Last name. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._post( - "/team/accounts", - body={ - "email": email, - "password": password, - "first": first, - "last": last, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def login( - self, - *, - email: str, - password: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Authenticate and obtain a session token. - - Args: - email: Account email address. - password: Account password. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._post( - "/team/accounts/login", - body={"email": email, "password": password}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get_account( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get the current account info (requires auth token in header). - - Args: - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._get( - "/team/accounts/me", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def change_password( - self, - *, - old_password: str, - new_password: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Change the current account password. - - Args: - old_password: Current password for verification. - new_password: New password to set. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._post( - "/team/accounts/password", - body={ - "old_password": old_password, - "new_password": new_password, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def request_password_recovery( - self, - *, - email: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Request a password recovery email. - - Args: - email: Email address associated with the account. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._post( - "/team/accounts/password/recovery", - body={"email": email}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def confirm_password_recovery( - self, - *, - recovery_token: str, - new_password: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Confirm password recovery with a token and set a new password. - - Args: - recovery_token: Token received via recovery email. - new_password: New password to set. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._post( - "/team/accounts/password/recovery/confirm", - body={ - "token": recovery_token, - "new_password": new_password, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ # - # Workspaces - # ------------------------------------------------------------------ # - - def create_workspace( - self, - *, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new collaborative workspace. - - Args: - name: Display name for the workspace. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._post( - "/team/workspaces", - body={"name": name}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def list_workspaces( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all workspaces accessible to the current account. - - Args: - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._get( - "/team/workspaces", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get_workspace( - self, - workspace_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get detailed information for a single workspace. - - Args: - workspace_id: Unique workspace identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._get( - f"/team/workspaces/{workspace_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update_workspace( - self, - workspace_id: str, - *, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update workspace metadata. - - Args: - workspace_id: Unique workspace identifier. - name: New display name for the workspace. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._patch( - f"/team/workspaces/{workspace_id}", - body={"name": name}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_workspace( - self, - workspace_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a workspace permanently. - - Args: - workspace_id: Unique workspace identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._delete( - f"/team/workspaces/{workspace_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ # - # Members - # ------------------------------------------------------------------ # - - def invite_member( - self, - workspace_id: str, - *, - email: str, - role: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Invite a user to a workspace by email. - - Args: - workspace_id: Unique workspace identifier. - email: Email of the user to invite. - role: Role to assign (e.g. ``"admin"``, ``"member"``, ``"guest"``). - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._post( - f"/team/workspaces/{workspace_id}/members", - body={"email": email, "role": role}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def join_workspace( - self, - workspace_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Join a workspace the current account has been invited to. - - Args: - workspace_id: Unique workspace identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._post( - f"/team/workspaces/{workspace_id}/join", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def leave_workspace( - self, - workspace_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Leave a workspace. - - Args: - workspace_id: Unique workspace identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._post( - f"/team/workspaces/{workspace_id}/leave", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ # - # OTP / MFA - # ------------------------------------------------------------------ # - - def send_otp( - self, - *, - email: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Send a one-time password to the given email for verification. - - Args: - email: Email address to send the OTP to. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._post( - "/team/otp/send", - body={"email": email}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def verify_otp( - self, - *, - email: str, - code: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Verify a one-time password. - - Args: - email: Email address the OTP was sent to. - code: The OTP code to verify. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._post( - "/team/otp/verify", - body={"email": email, "code": code}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ # - # Social login - # ------------------------------------------------------------------ # - - def add_social_id( - self, - *, - provider: str, - social_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Link a social login provider to the current account. - - Args: - provider: Social provider name (e.g. ``"google"``, ``"github"``). - social_id: Provider-specific user identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._post( - "/team/social", - body={"provider": provider, "social_id": social_id}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def remove_social_id( - self, - provider: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Unlink a social login provider from the current account. - - Args: - provider: Social provider name to remove (e.g. ``"google"``, ``"github"``). - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._delete( - f"/team/social/{provider}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ # - # Admin - # ------------------------------------------------------------------ # - - def statistics( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get Team platform server statistics. - - Maps to ``GET /api/v1/statistics`` on the account service. - - Args: - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return self._get( - "/team/statistics", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncTeamWorkspaceResource(AsyncAPIResource): - """Async variant of :class:`TeamWorkspaceResource`. - - Hanzo Team collaborative workspace platform. - Backed by the Team account-service (Huly/Santeam-derived). - """ - - @cached_property - def with_raw_response(self) -> AsyncTeamWorkspaceResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncTeamWorkspaceResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncTeamWorkspaceResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncTeamWorkspaceResourceWithStreamingResponse(self) - - # ------------------------------------------------------------------ # - # Accounts - # ------------------------------------------------------------------ # - - async def create_account( - self, - *, - email: str, - password: str, - first: str, - last: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new Team workspace account. - - Args: - email: Account email address. - password: Account password (hashed server-side). - first: First name. - last: Last name. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._post( - "/team/accounts", - body={ - "email": email, - "password": password, - "first": first, - "last": last, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def login( - self, - *, - email: str, - password: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Authenticate and obtain a session token. - - Args: - email: Account email address. - password: Account password. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._post( - "/team/accounts/login", - body={"email": email, "password": password}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get_account( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get the current account info (requires auth token in header). - - Args: - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._get( - "/team/accounts/me", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def change_password( - self, - *, - old_password: str, - new_password: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Change the current account password. - - Args: - old_password: Current password for verification. - new_password: New password to set. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._post( - "/team/accounts/password", - body={ - "old_password": old_password, - "new_password": new_password, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def request_password_recovery( - self, - *, - email: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Request a password recovery email. - - Args: - email: Email address associated with the account. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._post( - "/team/accounts/password/recovery", - body={"email": email}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def confirm_password_recovery( - self, - *, - recovery_token: str, - new_password: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Confirm password recovery with a token and set a new password. - - Args: - recovery_token: Token received via recovery email. - new_password: New password to set. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._post( - "/team/accounts/password/recovery/confirm", - body={ - "token": recovery_token, - "new_password": new_password, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ # - # Workspaces - # ------------------------------------------------------------------ # - - async def create_workspace( - self, - *, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new collaborative workspace. - - Args: - name: Display name for the workspace. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._post( - "/team/workspaces", - body={"name": name}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def list_workspaces( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all workspaces accessible to the current account. - - Args: - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._get( - "/team/workspaces", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get_workspace( - self, - workspace_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get detailed information for a single workspace. - - Args: - workspace_id: Unique workspace identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._get( - f"/team/workspaces/{workspace_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update_workspace( - self, - workspace_id: str, - *, - name: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update workspace metadata. - - Args: - workspace_id: Unique workspace identifier. - name: New display name for the workspace. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._patch( - f"/team/workspaces/{workspace_id}", - body={"name": name}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_workspace( - self, - workspace_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a workspace permanently. - - Args: - workspace_id: Unique workspace identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._delete( - f"/team/workspaces/{workspace_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ # - # Members - # ------------------------------------------------------------------ # - - async def invite_member( - self, - workspace_id: str, - *, - email: str, - role: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Invite a user to a workspace by email. - - Args: - workspace_id: Unique workspace identifier. - email: Email of the user to invite. - role: Role to assign (e.g. ``"admin"``, ``"member"``, ``"guest"``). - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._post( - f"/team/workspaces/{workspace_id}/members", - body={"email": email, "role": role}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def join_workspace( - self, - workspace_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Join a workspace the current account has been invited to. - - Args: - workspace_id: Unique workspace identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._post( - f"/team/workspaces/{workspace_id}/join", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def leave_workspace( - self, - workspace_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Leave a workspace. - - Args: - workspace_id: Unique workspace identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._post( - f"/team/workspaces/{workspace_id}/leave", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ # - # OTP / MFA - # ------------------------------------------------------------------ # - - async def send_otp( - self, - *, - email: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Send a one-time password to the given email for verification. - - Args: - email: Email address to send the OTP to. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._post( - "/team/otp/send", - body={"email": email}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def verify_otp( - self, - *, - email: str, - code: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Verify a one-time password. - - Args: - email: Email address the OTP was sent to. - code: The OTP code to verify. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._post( - "/team/otp/verify", - body={"email": email, "code": code}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ # - # Social login - # ------------------------------------------------------------------ # - - async def add_social_id( - self, - *, - provider: str, - social_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Link a social login provider to the current account. - - Args: - provider: Social provider name (e.g. ``"google"``, ``"github"``). - social_id: Provider-specific user identifier. - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._post( - "/team/social", - body={"provider": provider, "social_id": social_id}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def remove_social_id( - self, - provider: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Unlink a social login provider from the current account. - - Args: - provider: Social provider name to remove (e.g. ``"google"``, ``"github"``). - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._delete( - f"/team/social/{provider}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - # ------------------------------------------------------------------ # - # Admin - # ------------------------------------------------------------------ # - - async def statistics( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get Team platform server statistics. - - Maps to ``GET /api/v1/statistics`` on the account service. - - Args: - extra_headers: Send extra headers. - extra_query: Add additional query parameters to the request. - extra_body: Add additional JSON properties to the request. - timeout: Override the client-level default timeout for this request, in seconds. - """ - return await self._get( - "/team/statistics", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -# ====================================================================== # -# Raw response wrappers -# ====================================================================== # - - -class TeamWorkspaceResourceWithRawResponse: - def __init__(self, team_workspace: TeamWorkspaceResource) -> None: - self._team_workspace = team_workspace - - # Accounts - self.create_account = to_raw_response_wrapper( - team_workspace.create_account, - ) - self.login = to_raw_response_wrapper( - team_workspace.login, - ) - self.get_account = to_raw_response_wrapper( - team_workspace.get_account, - ) - self.change_password = to_raw_response_wrapper( - team_workspace.change_password, - ) - self.request_password_recovery = to_raw_response_wrapper( - team_workspace.request_password_recovery, - ) - self.confirm_password_recovery = to_raw_response_wrapper( - team_workspace.confirm_password_recovery, - ) - - # Workspaces - self.create_workspace = to_raw_response_wrapper( - team_workspace.create_workspace, - ) - self.list_workspaces = to_raw_response_wrapper( - team_workspace.list_workspaces, - ) - self.get_workspace = to_raw_response_wrapper( - team_workspace.get_workspace, - ) - self.update_workspace = to_raw_response_wrapper( - team_workspace.update_workspace, - ) - self.delete_workspace = to_raw_response_wrapper( - team_workspace.delete_workspace, - ) - - # Members - self.invite_member = to_raw_response_wrapper( - team_workspace.invite_member, - ) - self.join_workspace = to_raw_response_wrapper( - team_workspace.join_workspace, - ) - self.leave_workspace = to_raw_response_wrapper( - team_workspace.leave_workspace, - ) - - # OTP - self.send_otp = to_raw_response_wrapper( - team_workspace.send_otp, - ) - self.verify_otp = to_raw_response_wrapper( - team_workspace.verify_otp, - ) - - # Social - self.add_social_id = to_raw_response_wrapper( - team_workspace.add_social_id, - ) - self.remove_social_id = to_raw_response_wrapper( - team_workspace.remove_social_id, - ) - - # Admin - self.statistics = to_raw_response_wrapper( - team_workspace.statistics, - ) - - -class AsyncTeamWorkspaceResourceWithRawResponse: - def __init__(self, team_workspace: AsyncTeamWorkspaceResource) -> None: - self._team_workspace = team_workspace - - # Accounts - self.create_account = async_to_raw_response_wrapper( - team_workspace.create_account, - ) - self.login = async_to_raw_response_wrapper( - team_workspace.login, - ) - self.get_account = async_to_raw_response_wrapper( - team_workspace.get_account, - ) - self.change_password = async_to_raw_response_wrapper( - team_workspace.change_password, - ) - self.request_password_recovery = async_to_raw_response_wrapper( - team_workspace.request_password_recovery, - ) - self.confirm_password_recovery = async_to_raw_response_wrapper( - team_workspace.confirm_password_recovery, - ) - - # Workspaces - self.create_workspace = async_to_raw_response_wrapper( - team_workspace.create_workspace, - ) - self.list_workspaces = async_to_raw_response_wrapper( - team_workspace.list_workspaces, - ) - self.get_workspace = async_to_raw_response_wrapper( - team_workspace.get_workspace, - ) - self.update_workspace = async_to_raw_response_wrapper( - team_workspace.update_workspace, - ) - self.delete_workspace = async_to_raw_response_wrapper( - team_workspace.delete_workspace, - ) - - # Members - self.invite_member = async_to_raw_response_wrapper( - team_workspace.invite_member, - ) - self.join_workspace = async_to_raw_response_wrapper( - team_workspace.join_workspace, - ) - self.leave_workspace = async_to_raw_response_wrapper( - team_workspace.leave_workspace, - ) - - # OTP - self.send_otp = async_to_raw_response_wrapper( - team_workspace.send_otp, - ) - self.verify_otp = async_to_raw_response_wrapper( - team_workspace.verify_otp, - ) - - # Social - self.add_social_id = async_to_raw_response_wrapper( - team_workspace.add_social_id, - ) - self.remove_social_id = async_to_raw_response_wrapper( - team_workspace.remove_social_id, - ) - - # Admin - self.statistics = async_to_raw_response_wrapper( - team_workspace.statistics, - ) - - -# ====================================================================== # -# Streaming response wrappers -# ====================================================================== # - - -class TeamWorkspaceResourceWithStreamingResponse: - def __init__(self, team_workspace: TeamWorkspaceResource) -> None: - self._team_workspace = team_workspace - - # Accounts - self.create_account = to_streamed_response_wrapper( - team_workspace.create_account, - ) - self.login = to_streamed_response_wrapper( - team_workspace.login, - ) - self.get_account = to_streamed_response_wrapper( - team_workspace.get_account, - ) - self.change_password = to_streamed_response_wrapper( - team_workspace.change_password, - ) - self.request_password_recovery = to_streamed_response_wrapper( - team_workspace.request_password_recovery, - ) - self.confirm_password_recovery = to_streamed_response_wrapper( - team_workspace.confirm_password_recovery, - ) - - # Workspaces - self.create_workspace = to_streamed_response_wrapper( - team_workspace.create_workspace, - ) - self.list_workspaces = to_streamed_response_wrapper( - team_workspace.list_workspaces, - ) - self.get_workspace = to_streamed_response_wrapper( - team_workspace.get_workspace, - ) - self.update_workspace = to_streamed_response_wrapper( - team_workspace.update_workspace, - ) - self.delete_workspace = to_streamed_response_wrapper( - team_workspace.delete_workspace, - ) - - # Members - self.invite_member = to_streamed_response_wrapper( - team_workspace.invite_member, - ) - self.join_workspace = to_streamed_response_wrapper( - team_workspace.join_workspace, - ) - self.leave_workspace = to_streamed_response_wrapper( - team_workspace.leave_workspace, - ) - - # OTP - self.send_otp = to_streamed_response_wrapper( - team_workspace.send_otp, - ) - self.verify_otp = to_streamed_response_wrapper( - team_workspace.verify_otp, - ) - - # Social - self.add_social_id = to_streamed_response_wrapper( - team_workspace.add_social_id, - ) - self.remove_social_id = to_streamed_response_wrapper( - team_workspace.remove_social_id, - ) - - # Admin - self.statistics = to_streamed_response_wrapper( - team_workspace.statistics, - ) - - -class AsyncTeamWorkspaceResourceWithStreamingResponse: - def __init__(self, team_workspace: AsyncTeamWorkspaceResource) -> None: - self._team_workspace = team_workspace - - # Accounts - self.create_account = async_to_streamed_response_wrapper( - team_workspace.create_account, - ) - self.login = async_to_streamed_response_wrapper( - team_workspace.login, - ) - self.get_account = async_to_streamed_response_wrapper( - team_workspace.get_account, - ) - self.change_password = async_to_streamed_response_wrapper( - team_workspace.change_password, - ) - self.request_password_recovery = async_to_streamed_response_wrapper( - team_workspace.request_password_recovery, - ) - self.confirm_password_recovery = async_to_streamed_response_wrapper( - team_workspace.confirm_password_recovery, - ) - - # Workspaces - self.create_workspace = async_to_streamed_response_wrapper( - team_workspace.create_workspace, - ) - self.list_workspaces = async_to_streamed_response_wrapper( - team_workspace.list_workspaces, - ) - self.get_workspace = async_to_streamed_response_wrapper( - team_workspace.get_workspace, - ) - self.update_workspace = async_to_streamed_response_wrapper( - team_workspace.update_workspace, - ) - self.delete_workspace = async_to_streamed_response_wrapper( - team_workspace.delete_workspace, - ) - - # Members - self.invite_member = async_to_streamed_response_wrapper( - team_workspace.invite_member, - ) - self.join_workspace = async_to_streamed_response_wrapper( - team_workspace.join_workspace, - ) - self.leave_workspace = async_to_streamed_response_wrapper( - team_workspace.leave_workspace, - ) - - # OTP - self.send_otp = async_to_streamed_response_wrapper( - team_workspace.send_otp, - ) - self.verify_otp = async_to_streamed_response_wrapper( - team_workspace.verify_otp, - ) - - # Social - self.add_social_id = async_to_streamed_response_wrapper( - team_workspace.add_social_id, - ) - self.remove_social_id = async_to_streamed_response_wrapper( - team_workspace.remove_social_id, - ) - - # Admin - self.statistics = async_to_streamed_response_wrapper( - team_workspace.statistics, - ) diff --git a/pkg/hanzoai/resources/threads/__init__.py b/pkg/hanzoai/resources/threads/__init__.py deleted file mode 100644 index 28c57544c..000000000 --- a/pkg/hanzoai/resources/threads/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# Hanzo AI SDK - -from .runs import ( - RunsResource, - AsyncRunsResource, - RunsResourceWithRawResponse, - AsyncRunsResourceWithRawResponse, - RunsResourceWithStreamingResponse, - AsyncRunsResourceWithStreamingResponse, -) -from .threads import ( - ThreadsResource, - AsyncThreadsResource, - ThreadsResourceWithRawResponse, - AsyncThreadsResourceWithRawResponse, - ThreadsResourceWithStreamingResponse, - AsyncThreadsResourceWithStreamingResponse, -) -from .messages import ( - MessagesResource, - AsyncMessagesResource, - MessagesResourceWithRawResponse, - AsyncMessagesResourceWithRawResponse, - MessagesResourceWithStreamingResponse, - AsyncMessagesResourceWithStreamingResponse, -) - -__all__ = [ - "MessagesResource", - "AsyncMessagesResource", - "MessagesResourceWithRawResponse", - "AsyncMessagesResourceWithRawResponse", - "MessagesResourceWithStreamingResponse", - "AsyncMessagesResourceWithStreamingResponse", - "RunsResource", - "AsyncRunsResource", - "RunsResourceWithRawResponse", - "AsyncRunsResourceWithRawResponse", - "RunsResourceWithStreamingResponse", - "AsyncRunsResourceWithStreamingResponse", - "ThreadsResource", - "AsyncThreadsResource", - "ThreadsResourceWithRawResponse", - "AsyncThreadsResourceWithRawResponse", - "ThreadsResourceWithStreamingResponse", - "AsyncThreadsResourceWithStreamingResponse", -] diff --git a/pkg/hanzoai/resources/tokens.py b/pkg/hanzoai/resources/tokens.py deleted file mode 100644 index 4bf0f9160..000000000 --- a/pkg/hanzoai/resources/tokens.py +++ /dev/null @@ -1,374 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["TokensResource", "AsyncTokensResource"] - - -class TokensResource(SyncAPIResource): - """Blockchain token management.""" - - @cached_property - def with_raw_response(self) -> TokensResourceWithRawResponse: - return TokensResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> TokensResourceWithStreamingResponse: - return TokensResourceWithStreamingResponse(self) - - def list( - self, - *, - network_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all tokens.""" - return self._get( - "/network/tokens", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"network_id": network_id}, - ), - cast_to=object, - ) - - def get( - self, - token_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific token.""" - return self._get( - f"/network/tokens/{token_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - symbol: str, - network_id: str, - total_supply: str, - decimals: int = 18, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new token.""" - return self._post( - "/network/tokens", - body={ - "name": name, - "symbol": symbol, - "network_id": network_id, - "total_supply": total_supply, - "decimals": decimals, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def mint( - self, - token_id: str, - *, - to_address: str, - amount: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Mint new tokens.""" - return self._post( - f"/network/tokens/{token_id}/mint", - body={"to_address": to_address, "amount": amount}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def burn( - self, - token_id: str, - *, - amount: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Burn tokens.""" - return self._post( - f"/network/tokens/{token_id}/burn", - body={"amount": amount}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def holders( - self, - token_id: str, - *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get token holders.""" - return self._get( - f"/network/tokens/{token_id}/holders", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - -class AsyncTokensResource(AsyncAPIResource): - """Blockchain token management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncTokensResourceWithRawResponse: - return AsyncTokensResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncTokensResourceWithStreamingResponse: - return AsyncTokensResourceWithStreamingResponse(self) - - async def list( - self, - *, - network_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/network/tokens", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"network_id": network_id}, - ), - cast_to=object, - ) - - async def get( - self, - token_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/network/tokens/{token_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - symbol: str, - network_id: str, - total_supply: str, - decimals: int = 18, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/network/tokens", - body={ - "name": name, - "symbol": symbol, - "network_id": network_id, - "total_supply": total_supply, - "decimals": decimals, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def mint( - self, - token_id: str, - *, - to_address: str, - amount: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/network/tokens/{token_id}/mint", - body={"to_address": to_address, "amount": amount}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def burn( - self, - token_id: str, - *, - amount: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/network/tokens/{token_id}/burn", - body={"amount": amount}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def holders( - self, - token_id: str, - *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/network/tokens/{token_id}/holders", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - -class TokensResourceWithRawResponse: - def __init__(self, tokens: TokensResource) -> None: - self._tokens = tokens - self.list = to_raw_response_wrapper(tokens.list) - self.get = to_raw_response_wrapper(tokens.get) - self.create = to_raw_response_wrapper(tokens.create) - self.mint = to_raw_response_wrapper(tokens.mint) - self.burn = to_raw_response_wrapper(tokens.burn) - self.holders = to_raw_response_wrapper(tokens.holders) - - -class AsyncTokensResourceWithRawResponse: - def __init__(self, tokens: AsyncTokensResource) -> None: - self._tokens = tokens - self.list = async_to_raw_response_wrapper(tokens.list) - self.get = async_to_raw_response_wrapper(tokens.get) - self.create = async_to_raw_response_wrapper(tokens.create) - self.mint = async_to_raw_response_wrapper(tokens.mint) - self.burn = async_to_raw_response_wrapper(tokens.burn) - self.holders = async_to_raw_response_wrapper(tokens.holders) - - -class TokensResourceWithStreamingResponse: - def __init__(self, tokens: TokensResource) -> None: - self._tokens = tokens - self.list = to_streamed_response_wrapper(tokens.list) - self.get = to_streamed_response_wrapper(tokens.get) - self.create = to_streamed_response_wrapper(tokens.create) - self.mint = to_streamed_response_wrapper(tokens.mint) - self.burn = to_streamed_response_wrapper(tokens.burn) - self.holders = to_streamed_response_wrapper(tokens.holders) - - -class AsyncTokensResourceWithStreamingResponse: - def __init__(self, tokens: AsyncTokensResource) -> None: - self._tokens = tokens - self.list = async_to_streamed_response_wrapper(tokens.list) - self.get = async_to_streamed_response_wrapper(tokens.get) - self.create = async_to_streamed_response_wrapper(tokens.create) - self.mint = async_to_streamed_response_wrapper(tokens.mint) - self.burn = async_to_streamed_response_wrapper(tokens.burn) - self.holders = async_to_streamed_response_wrapper(tokens.holders) diff --git a/pkg/hanzoai/resources/tunnel.py b/pkg/hanzoai/resources/tunnel.py deleted file mode 100644 index 96d8bb22b..000000000 --- a/pkg/hanzoai/resources/tunnel.py +++ /dev/null @@ -1,490 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._base_client import make_request_options - - -class TunnelResource(SyncAPIResource): - """Zero trust tunnel connectivity (end-user ZTNA).""" - - @cached_property - def with_raw_response(self) -> TunnelResourceWithRawResponse: - return TunnelResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> TunnelResourceWithStreamingResponse: - return TunnelResourceWithStreamingResponse(self) - - def install( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get tunnel installation instructions.""" - return self._get( - "/tunnel/install", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def start( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Start tunnel daemon.""" - return self._post( - "/tunnel/start", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def stop( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Stop tunnel daemon.""" - return self._post( - "/tunnel/stop", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def status( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get tunnel status.""" - return self._get( - "/tunnel/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def connect( - self, - *, - env: str | NotGiven = NOT_GIVEN, - network: str | NotGiven = NOT_GIVEN, - profile: str | NotGiven = NOT_GIVEN, - identity: str | NotGiven = NOT_GIVEN, - mode: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Connect to environment or network.""" - return self._post( - "/tunnel/connect", - body={ - "env": env, - "network": network, - "profile": profile, - "identity": identity, - "mode": mode, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def disconnect( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Disconnect from current network.""" - return self._post( - "/tunnel/disconnect", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def routes( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get active routes.""" - return self._get( - "/tunnel/routes", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def dns( - self, - *, - action: str, - split_dns: List[str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Configure tunnel DNS.""" - return self._post( - "/tunnel/dns", - body={"action": action, "split_dns": split_dns}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def logs( - self, - *, - follow: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get tunnel logs.""" - return self._get( - "/tunnel/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"follow": follow}, - ), - cast_to=object, - ) - - def diagnose( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Run tunnel diagnostics.""" - return self._get( - "/tunnel/diagnose", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncTunnelResource(AsyncAPIResource): - """Zero trust tunnel connectivity (end-user ZTNA).""" - - @cached_property - def with_raw_response(self) -> AsyncTunnelResourceWithRawResponse: - return AsyncTunnelResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncTunnelResourceWithStreamingResponse: - return AsyncTunnelResourceWithStreamingResponse(self) - - async def install( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get tunnel installation instructions.""" - return await self._get( - "/tunnel/install", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def start( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Start tunnel daemon.""" - return await self._post( - "/tunnel/start", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def stop( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Stop tunnel daemon.""" - return await self._post( - "/tunnel/stop", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def status( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get tunnel status.""" - return await self._get( - "/tunnel/status", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def connect( - self, - *, - env: str | NotGiven = NOT_GIVEN, - network: str | NotGiven = NOT_GIVEN, - profile: str | NotGiven = NOT_GIVEN, - identity: str | NotGiven = NOT_GIVEN, - mode: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Connect to environment or network.""" - return await self._post( - "/tunnel/connect", - body={ - "env": env, - "network": network, - "profile": profile, - "identity": identity, - "mode": mode, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def disconnect( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Disconnect from current network.""" - return await self._post( - "/tunnel/disconnect", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def routes( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get active routes.""" - return await self._get( - "/tunnel/routes", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def dns( - self, - *, - action: str, - split_dns: List[str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Configure tunnel DNS.""" - return await self._post( - "/tunnel/dns", - body={"action": action, "split_dns": split_dns}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def logs( - self, - *, - follow: bool | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get tunnel logs.""" - return await self._get( - "/tunnel/logs", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"follow": follow}, - ), - cast_to=object, - ) - - async def diagnose( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Run tunnel diagnostics.""" - return await self._get( - "/tunnel/diagnose", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class TunnelResourceWithRawResponse: - def __init__(self, tunnel: TunnelResource) -> None: - self._tunnel = tunnel - - -class AsyncTunnelResourceWithRawResponse: - def __init__(self, tunnel: AsyncTunnelResource) -> None: - self._tunnel = tunnel - - -class TunnelResourceWithStreamingResponse: - def __init__(self, tunnel: TunnelResource) -> None: - self._tunnel = tunnel - - -class AsyncTunnelResourceWithStreamingResponse: - def __init__(self, tunnel: AsyncTunnelResource) -> None: - self._tunnel = tunnel diff --git a/pkg/hanzoai/resources/utils.py b/pkg/hanzoai/resources/utils.py deleted file mode 100644 index 4673cb90f..000000000 --- a/pkg/hanzoai/resources/utils.py +++ /dev/null @@ -1,524 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Iterable, Optional -from typing_extensions import Literal - -import httpx - -from ..types import ( - util_token_counter_params, - util_transform_request_params, - util_get_supported_openai_params_params, -) -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - maybe_transform, - async_maybe_transform, -) -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options -from ..types.util_token_counter_response import UtilTokenCounterResponse -from ..types.util_transform_request_response import UtilTransformRequestResponse - -__all__ = ["UtilsResource", "AsyncUtilsResource"] - - -class UtilsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> UtilsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return UtilsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> UtilsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return UtilsResourceWithStreamingResponse(self) - - def get_supported_openai_params( - self, - *, - model: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Returns supported openai params for a given hanzo model name - - e.g. - - `gpt-4` vs `gpt-3.5-turbo` - - Example curl: - - ``` - curl -X GET --location 'http://localhost:4000/utils/supported_openai_params?model=gpt-3.5-turbo-16k' --header 'Authorization: Bearer sk-1234' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/utils/supported_openai_params", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"model": model}, - util_get_supported_openai_params_params.UtilGetSupportedOpenAIParamsParams, - ), - ), - cast_to=object, - ) - - def token_counter( - self, - *, - model: str, - messages: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - prompt: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> UtilTokenCounterResponse: - """ - Token Counter - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/utils/token_counter", - body=maybe_transform( - { - "model": model, - "messages": messages, - "prompt": prompt, - }, - util_token_counter_params.UtilTokenCounterParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=UtilTokenCounterResponse, - ) - - def transform_request( - self, - *, - call_type: Literal[ - "embedding", - "aembedding", - "completion", - "acompletion", - "atext_completion", - "text_completion", - "image_generation", - "aimage_generation", - "moderation", - "amoderation", - "atranscription", - "transcription", - "aspeech", - "speech", - "rerank", - "arerank", - "_arealtime", - "create_batch", - "acreate_batch", - "aretrieve_batch", - "retrieve_batch", - "pass_through_endpoint", - "anthropic_messages", - "get_assistants", - "aget_assistants", - "create_assistants", - "acreate_assistants", - "delete_assistant", - "adelete_assistant", - "acreate_thread", - "create_thread", - "aget_thread", - "get_thread", - "a_add_message", - "add_message", - "aget_messages", - "get_messages", - "arun_thread", - "run_thread", - "arun_thread_stream", - "run_thread_stream", - "afile_retrieve", - "file_retrieve", - "afile_delete", - "file_delete", - "afile_list", - "file_list", - "acreate_file", - "create_file", - "afile_content", - "file_content", - "create_fine_tuning_job", - "acreate_fine_tuning_job", - "acancel_fine_tuning_job", - "cancel_fine_tuning_job", - "alist_fine_tuning_jobs", - "list_fine_tuning_jobs", - "aretrieve_fine_tuning_job", - "retrieve_fine_tuning_job", - "responses", - "aresponses", - ], - request_body: object, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> UtilTransformRequestResponse: - """ - Transform Request - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/utils/transform_request", - body=maybe_transform( - { - "call_type": call_type, - "request_body": request_body, - }, - util_transform_request_params.UtilTransformRequestParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=UtilTransformRequestResponse, - ) - - -class AsyncUtilsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncUtilsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncUtilsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncUtilsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response - """ - return AsyncUtilsResourceWithStreamingResponse(self) - - async def get_supported_openai_params( - self, - *, - model: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Returns supported openai params for a given hanzo model name - - e.g. - - `gpt-4` vs `gpt-3.5-turbo` - - Example curl: - - ``` - curl -X GET --location 'http://localhost:4000/utils/supported_openai_params?model=gpt-3.5-turbo-16k' --header 'Authorization: Bearer sk-1234' - ``` - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/utils/supported_openai_params", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"model": model}, - util_get_supported_openai_params_params.UtilGetSupportedOpenAIParamsParams, - ), - ), - cast_to=object, - ) - - async def token_counter( - self, - *, - model: str, - messages: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - prompt: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> UtilTokenCounterResponse: - """ - Token Counter - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/utils/token_counter", - body=await async_maybe_transform( - { - "model": model, - "messages": messages, - "prompt": prompt, - }, - util_token_counter_params.UtilTokenCounterParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=UtilTokenCounterResponse, - ) - - async def transform_request( - self, - *, - call_type: Literal[ - "embedding", - "aembedding", - "completion", - "acompletion", - "atext_completion", - "text_completion", - "image_generation", - "aimage_generation", - "moderation", - "amoderation", - "atranscription", - "transcription", - "aspeech", - "speech", - "rerank", - "arerank", - "_arealtime", - "create_batch", - "acreate_batch", - "aretrieve_batch", - "retrieve_batch", - "pass_through_endpoint", - "anthropic_messages", - "get_assistants", - "aget_assistants", - "create_assistants", - "acreate_assistants", - "delete_assistant", - "adelete_assistant", - "acreate_thread", - "create_thread", - "aget_thread", - "get_thread", - "a_add_message", - "add_message", - "aget_messages", - "get_messages", - "arun_thread", - "run_thread", - "arun_thread_stream", - "run_thread_stream", - "afile_retrieve", - "file_retrieve", - "afile_delete", - "file_delete", - "afile_list", - "file_list", - "acreate_file", - "create_file", - "afile_content", - "file_content", - "create_fine_tuning_job", - "acreate_fine_tuning_job", - "acancel_fine_tuning_job", - "cancel_fine_tuning_job", - "alist_fine_tuning_jobs", - "list_fine_tuning_jobs", - "aretrieve_fine_tuning_job", - "retrieve_fine_tuning_job", - "responses", - "aresponses", - ], - request_body: object, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> UtilTransformRequestResponse: - """ - Transform Request - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/utils/transform_request", - body=await async_maybe_transform( - { - "call_type": call_type, - "request_body": request_body, - }, - util_transform_request_params.UtilTransformRequestParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=UtilTransformRequestResponse, - ) - - -class UtilsResourceWithRawResponse: - def __init__(self, utils: UtilsResource) -> None: - self._utils = utils - - self.get_supported_openai_params = to_raw_response_wrapper( - utils.get_supported_openai_params, - ) - self.token_counter = to_raw_response_wrapper( - utils.token_counter, - ) - self.transform_request = to_raw_response_wrapper( - utils.transform_request, - ) - - -class AsyncUtilsResourceWithRawResponse: - def __init__(self, utils: AsyncUtilsResource) -> None: - self._utils = utils - - self.get_supported_openai_params = async_to_raw_response_wrapper( - utils.get_supported_openai_params, - ) - self.token_counter = async_to_raw_response_wrapper( - utils.token_counter, - ) - self.transform_request = async_to_raw_response_wrapper( - utils.transform_request, - ) - - -class UtilsResourceWithStreamingResponse: - def __init__(self, utils: UtilsResource) -> None: - self._utils = utils - - self.get_supported_openai_params = to_streamed_response_wrapper( - utils.get_supported_openai_params, - ) - self.token_counter = to_streamed_response_wrapper( - utils.token_counter, - ) - self.transform_request = to_streamed_response_wrapper( - utils.transform_request, - ) - - -class AsyncUtilsResourceWithStreamingResponse: - def __init__(self, utils: AsyncUtilsResource) -> None: - self._utils = utils - - self.get_supported_openai_params = async_to_streamed_response_wrapper( - utils.get_supported_openai_params, - ) - self.token_counter = async_to_streamed_response_wrapper( - utils.token_counter, - ) - self.transform_request = async_to_streamed_response_wrapper( - utils.transform_request, - ) diff --git a/pkg/hanzoai/resources/vectors.py b/pkg/hanzoai/resources/vectors.py deleted file mode 100644 index fa944347a..000000000 --- a/pkg/hanzoai/resources/vectors.py +++ /dev/null @@ -1,372 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List, Optional - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["VectorsResource", "AsyncVectorsResource"] - - -class VectorsResource(SyncAPIResource): - """Vector embedding operations.""" - - @cached_property - def with_raw_response(self) -> VectorsResourceWithRawResponse: - return VectorsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> VectorsResourceWithStreamingResponse: - return VectorsResourceWithStreamingResponse(self) - - def list( - self, - *, - store_id: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all vectors.""" - return self._get( - "/vectors", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"store_id": store_id, "limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - def get( - self, - vector_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific vector.""" - return self._get( - f"/vectors/{vector_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - content: str, - store_id: str, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new vector embedding.""" - return self._post( - "/vectors", - body={"content": content, "store_id": store_id, "metadata": metadata}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - vector_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a vector.""" - return self._delete( - f"/vectors/{vector_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete_all( - self, - *, - store_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete all vectors in a store.""" - return self._delete( - "/vectors", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"store_id": store_id}, - ), - cast_to=object, - ) - - def search( - self, - *, - query: str, - store_id: str, - limit: int | NotGiven = NOT_GIVEN, - threshold: float | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Search vectors by semantic similarity.""" - return self._post( - "/vectors/search", - body={ - "query": query, - "store_id": store_id, - "limit": limit, - "threshold": threshold, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncVectorsResource(AsyncAPIResource): - """Vector embedding operations (async).""" - - @cached_property - def with_raw_response(self) -> AsyncVectorsResourceWithRawResponse: - return AsyncVectorsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncVectorsResourceWithStreamingResponse: - return AsyncVectorsResourceWithStreamingResponse(self) - - async def list( - self, - *, - store_id: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all vectors.""" - return await self._get( - "/vectors", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"store_id": store_id, "limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - async def get( - self, - vector_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific vector.""" - return await self._get( - f"/vectors/{vector_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - content: str, - store_id: str, - metadata: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new vector embedding.""" - return await self._post( - "/vectors", - body={"content": content, "store_id": store_id, "metadata": metadata}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - vector_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a vector.""" - return await self._delete( - f"/vectors/{vector_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete_all( - self, - *, - store_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete all vectors in a store.""" - return await self._delete( - "/vectors", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"store_id": store_id}, - ), - cast_to=object, - ) - - async def search( - self, - *, - query: str, - store_id: str, - limit: int | NotGiven = NOT_GIVEN, - threshold: float | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Search vectors by semantic similarity.""" - return await self._post( - "/vectors/search", - body={ - "query": query, - "store_id": store_id, - "limit": limit, - "threshold": threshold, - }, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class VectorsResourceWithRawResponse: - def __init__(self, vectors: VectorsResource) -> None: - self._vectors = vectors - self.list = to_raw_response_wrapper(vectors.list) - self.get = to_raw_response_wrapper(vectors.get) - self.create = to_raw_response_wrapper(vectors.create) - self.delete = to_raw_response_wrapper(vectors.delete) - self.delete_all = to_raw_response_wrapper(vectors.delete_all) - self.search = to_raw_response_wrapper(vectors.search) - - -class AsyncVectorsResourceWithRawResponse: - def __init__(self, vectors: AsyncVectorsResource) -> None: - self._vectors = vectors - self.list = async_to_raw_response_wrapper(vectors.list) - self.get = async_to_raw_response_wrapper(vectors.get) - self.create = async_to_raw_response_wrapper(vectors.create) - self.delete = async_to_raw_response_wrapper(vectors.delete) - self.delete_all = async_to_raw_response_wrapper(vectors.delete_all) - self.search = async_to_raw_response_wrapper(vectors.search) - - -class VectorsResourceWithStreamingResponse: - def __init__(self, vectors: VectorsResource) -> None: - self._vectors = vectors - self.list = to_streamed_response_wrapper(vectors.list) - self.get = to_streamed_response_wrapper(vectors.get) - self.create = to_streamed_response_wrapper(vectors.create) - self.delete = to_streamed_response_wrapper(vectors.delete) - self.delete_all = to_streamed_response_wrapper(vectors.delete_all) - self.search = to_streamed_response_wrapper(vectors.search) - - -class AsyncVectorsResourceWithStreamingResponse: - def __init__(self, vectors: AsyncVectorsResource) -> None: - self._vectors = vectors - self.list = async_to_streamed_response_wrapper(vectors.list) - self.get = async_to_streamed_response_wrapper(vectors.get) - self.create = async_to_streamed_response_wrapper(vectors.create) - self.delete = async_to_streamed_response_wrapper(vectors.delete) - self.delete_all = async_to_streamed_response_wrapper(vectors.delete_all) - self.search = async_to_streamed_response_wrapper(vectors.search) diff --git a/pkg/hanzoai/resources/wallets.py b/pkg/hanzoai/resources/wallets.py deleted file mode 100644 index 3df873f1a..000000000 --- a/pkg/hanzoai/resources/wallets.py +++ /dev/null @@ -1,444 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["WalletsResource", "AsyncWalletsResource"] - - -class WalletsResource(SyncAPIResource): - """Blockchain wallet management.""" - - @cached_property - def with_raw_response(self) -> WalletsResourceWithRawResponse: - return WalletsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> WalletsResourceWithStreamingResponse: - return WalletsResourceWithStreamingResponse(self) - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all wallets.""" - return self._get( - "/network/wallets", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - wallet_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific wallet.""" - return self._get( - f"/network/wallets/{wallet_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - network_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new wallet.""" - return self._post( - "/network/wallets", - body={"name": name, "network_id": network_id}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - wallet_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a wallet.""" - return self._delete( - f"/network/wallets/{wallet_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def balance( - self, - wallet_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get wallet balance.""" - return self._get( - f"/network/wallets/{wallet_id}/balance", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def transactions( - self, - wallet_id: str, - *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get wallet transactions.""" - return self._get( - f"/network/wallets/{wallet_id}/transactions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - def transfer( - self, - wallet_id: str, - *, - to_address: str, - amount: str, - token_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Transfer from wallet.""" - return self._post( - f"/network/wallets/{wallet_id}/transfer", - body={"to_address": to_address, "amount": amount, "token_id": token_id}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def sign( - self, - wallet_id: str, - *, - message: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Sign a message with wallet.""" - return self._post( - f"/network/wallets/{wallet_id}/sign", - body={"message": message}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncWalletsResource(AsyncAPIResource): - """Blockchain wallet management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncWalletsResourceWithRawResponse: - return AsyncWalletsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncWalletsResourceWithStreamingResponse: - return AsyncWalletsResourceWithStreamingResponse(self) - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - "/network/wallets", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - wallet_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/network/wallets/{wallet_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - network_id: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - "/network/wallets", - body={"name": name, "network_id": network_id}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - wallet_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._delete( - f"/network/wallets/{wallet_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def balance( - self, - wallet_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/network/wallets/{wallet_id}/balance", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def transactions( - self, - wallet_id: str, - *, - limit: int | NotGiven = NOT_GIVEN, - offset: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._get( - f"/network/wallets/{wallet_id}/transactions", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query={"limit": limit, "offset": offset}, - ), - cast_to=object, - ) - - async def transfer( - self, - wallet_id: str, - *, - to_address: str, - amount: str, - token_id: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/network/wallets/{wallet_id}/transfer", - body={"to_address": to_address, "amount": amount, "token_id": token_id}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def sign( - self, - wallet_id: str, - *, - message: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - return await self._post( - f"/network/wallets/{wallet_id}/sign", - body={"message": message}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class WalletsResourceWithRawResponse: - def __init__(self, wallets: WalletsResource) -> None: - self._wallets = wallets - self.list = to_raw_response_wrapper(wallets.list) - self.get = to_raw_response_wrapper(wallets.get) - self.create = to_raw_response_wrapper(wallets.create) - self.delete = to_raw_response_wrapper(wallets.delete) - self.balance = to_raw_response_wrapper(wallets.balance) - self.transactions = to_raw_response_wrapper(wallets.transactions) - self.transfer = to_raw_response_wrapper(wallets.transfer) - self.sign = to_raw_response_wrapper(wallets.sign) - - -class AsyncWalletsResourceWithRawResponse: - def __init__(self, wallets: AsyncWalletsResource) -> None: - self._wallets = wallets - self.list = async_to_raw_response_wrapper(wallets.list) - self.get = async_to_raw_response_wrapper(wallets.get) - self.create = async_to_raw_response_wrapper(wallets.create) - self.delete = async_to_raw_response_wrapper(wallets.delete) - self.balance = async_to_raw_response_wrapper(wallets.balance) - self.transactions = async_to_raw_response_wrapper(wallets.transactions) - self.transfer = async_to_raw_response_wrapper(wallets.transfer) - self.sign = async_to_raw_response_wrapper(wallets.sign) - - -class WalletsResourceWithStreamingResponse: - def __init__(self, wallets: WalletsResource) -> None: - self._wallets = wallets - self.list = to_streamed_response_wrapper(wallets.list) - self.get = to_streamed_response_wrapper(wallets.get) - self.create = to_streamed_response_wrapper(wallets.create) - self.delete = to_streamed_response_wrapper(wallets.delete) - self.balance = to_streamed_response_wrapper(wallets.balance) - self.transactions = to_streamed_response_wrapper(wallets.transactions) - self.transfer = to_streamed_response_wrapper(wallets.transfer) - self.sign = to_streamed_response_wrapper(wallets.sign) - - -class AsyncWalletsResourceWithStreamingResponse: - def __init__(self, wallets: AsyncWalletsResource) -> None: - self._wallets = wallets - self.list = async_to_streamed_response_wrapper(wallets.list) - self.get = async_to_streamed_response_wrapper(wallets.get) - self.create = async_to_streamed_response_wrapper(wallets.create) - self.delete = async_to_streamed_response_wrapper(wallets.delete) - self.balance = async_to_streamed_response_wrapper(wallets.balance) - self.transactions = async_to_streamed_response_wrapper(wallets.transactions) - self.transfer = async_to_streamed_response_wrapper(wallets.transfer) - self.sign = async_to_streamed_response_wrapper(wallets.sign) diff --git a/pkg/hanzoai/resources/workflows.py b/pkg/hanzoai/resources/workflows.py deleted file mode 100644 index fa1da9f95..000000000 --- a/pkg/hanzoai/resources/workflows.py +++ /dev/null @@ -1,356 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Any, Dict, List, Optional - -import httpx - -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["WorkflowsResource", "AsyncWorkflowsResource"] - - -class WorkflowsResource(SyncAPIResource): - """AI Workflow pipeline management.""" - - @cached_property - def with_raw_response(self) -> WorkflowsResourceWithRawResponse: - return WorkflowsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> WorkflowsResourceWithStreamingResponse: - return WorkflowsResourceWithStreamingResponse(self) - - def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all workflows.""" - return self._get( - "/workflows", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def get( - self, - workflow_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific workflow.""" - return self._get( - f"/workflows/{workflow_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def create( - self, - *, - name: str, - steps: List[Dict[str, Any]], - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new workflow.""" - return self._post( - "/workflows", - body={"name": name, "steps": steps, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def update( - self, - workflow_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - steps: List[Dict[str, Any]] | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a workflow.""" - return self._put( - f"/workflows/{workflow_id}", - body={"name": name, "steps": steps, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def delete( - self, - workflow_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a workflow.""" - return self._delete( - f"/workflows/{workflow_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - def run( - self, - workflow_id: str, - *, - inputs: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Run a workflow.""" - return self._post( - f"/workflows/{workflow_id}/run", - body={"inputs": inputs}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class AsyncWorkflowsResource(AsyncAPIResource): - """AI Workflow pipeline management (async).""" - - @cached_property - def with_raw_response(self) -> AsyncWorkflowsResourceWithRawResponse: - return AsyncWorkflowsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncWorkflowsResourceWithStreamingResponse: - return AsyncWorkflowsResourceWithStreamingResponse(self) - - async def list( - self, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """List all workflows.""" - return await self._get( - "/workflows", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def get( - self, - workflow_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Get a specific workflow.""" - return await self._get( - f"/workflows/{workflow_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def create( - self, - *, - name: str, - steps: List[Dict[str, Any]], - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Create a new workflow.""" - return await self._post( - "/workflows", - body={"name": name, "steps": steps, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def update( - self, - workflow_id: str, - *, - name: str | NotGiven = NOT_GIVEN, - steps: List[Dict[str, Any]] | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Update a workflow.""" - return await self._put( - f"/workflows/{workflow_id}", - body={"name": name, "steps": steps, "description": description}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def delete( - self, - workflow_id: str, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Delete a workflow.""" - return await self._delete( - f"/workflows/{workflow_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - async def run( - self, - workflow_id: str, - *, - inputs: Dict[str, Any] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> object: - """Run a workflow.""" - return await self._post( - f"/workflows/{workflow_id}/run", - body={"inputs": inputs}, - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ), - cast_to=object, - ) - - -class WorkflowsResourceWithRawResponse: - def __init__(self, workflows: WorkflowsResource) -> None: - self._workflows = workflows - self.list = to_raw_response_wrapper(workflows.list) - self.get = to_raw_response_wrapper(workflows.get) - self.create = to_raw_response_wrapper(workflows.create) - self.update = to_raw_response_wrapper(workflows.update) - self.delete = to_raw_response_wrapper(workflows.delete) - self.run = to_raw_response_wrapper(workflows.run) - - -class AsyncWorkflowsResourceWithRawResponse: - def __init__(self, workflows: AsyncWorkflowsResource) -> None: - self._workflows = workflows - self.list = async_to_raw_response_wrapper(workflows.list) - self.get = async_to_raw_response_wrapper(workflows.get) - self.create = async_to_raw_response_wrapper(workflows.create) - self.update = async_to_raw_response_wrapper(workflows.update) - self.delete = async_to_raw_response_wrapper(workflows.delete) - self.run = async_to_raw_response_wrapper(workflows.run) - - -class WorkflowsResourceWithStreamingResponse: - def __init__(self, workflows: WorkflowsResource) -> None: - self._workflows = workflows - self.list = to_streamed_response_wrapper(workflows.list) - self.get = to_streamed_response_wrapper(workflows.get) - self.create = to_streamed_response_wrapper(workflows.create) - self.update = to_streamed_response_wrapper(workflows.update) - self.delete = to_streamed_response_wrapper(workflows.delete) - self.run = to_streamed_response_wrapper(workflows.run) - - -class AsyncWorkflowsResourceWithStreamingResponse: - def __init__(self, workflows: AsyncWorkflowsResource) -> None: - self._workflows = workflows - self.list = async_to_streamed_response_wrapper(workflows.list) - self.get = async_to_streamed_response_wrapper(workflows.get) - self.create = async_to_streamed_response_wrapper(workflows.create) - self.update = async_to_streamed_response_wrapper(workflows.update) - self.delete = async_to_streamed_response_wrapper(workflows.delete) - self.run = async_to_streamed_response_wrapper(workflows.run) diff --git a/pkg/hanzoai/session.py b/pkg/hanzoai/session.py deleted file mode 100644 index b1269c5b4..000000000 --- a/pkg/hanzoai/session.py +++ /dev/null @@ -1,395 +0,0 @@ -"""Session management with compaction for Hanzo AI SDK. - -Ported from claw-code's session.rs and compact.rs. Provides conversation -message types, session persistence, and context window compaction. -""" - -from __future__ import annotations - -import json -import re -from dataclasses import dataclass, field -from enum import Enum -from pathlib import Path -from typing import Any, Union - - -# --------------------------------------------------------------------------- -# Message primitives -# --------------------------------------------------------------------------- - -class MessageRole(Enum): - System = "system" - User = "user" - Assistant = "assistant" - Tool = "tool" - - -@dataclass -class TextBlock: - text: str - - def to_dict(self) -> dict[str, Any]: - return {"type": "text", "text": self.text} - - @staticmethod - def from_dict(d: dict[str, Any]) -> TextBlock: - return TextBlock(text=d["text"]) - - -@dataclass -class ToolUseBlock: - id: str - name: str - input: str # Raw JSON string, matching Rust's String type - - def to_dict(self) -> dict[str, Any]: - return {"type": "tool_use", "id": self.id, "name": self.name, "input": self.input} - - @staticmethod - def from_dict(d: dict[str, Any]) -> ToolUseBlock: - raw = d["input"] - # Backwards compat: accept dict from old serialized data, convert to JSON string - if isinstance(raw, dict): - raw = json.dumps(raw, separators=(",", ":")) - return ToolUseBlock(id=d["id"], name=d["name"], input=raw) - - -@dataclass -class ToolResultBlock: - tool_use_id: str - tool_name: str - output: str - is_error: bool = False - - def to_dict(self) -> dict[str, Any]: - return { - "type": "tool_result", - "tool_use_id": self.tool_use_id, - "tool_name": self.tool_name, - "output": self.output, - "is_error": self.is_error, - } - - @staticmethod - def from_dict(d: dict[str, Any]) -> ToolResultBlock: - return ToolResultBlock( - tool_use_id=d["tool_use_id"], - tool_name=d["tool_name"], - output=d["output"], - is_error=d.get("is_error", False), - ) - - -ContentBlock = Union[TextBlock, ToolUseBlock, ToolResultBlock] - -_BLOCK_PARSERS = { - "text": TextBlock.from_dict, - "tool_use": ToolUseBlock.from_dict, - "tool_result": ToolResultBlock.from_dict, -} - - -def _block_from_dict(d: dict[str, Any]) -> ContentBlock: - parser = _BLOCK_PARSERS.get(d["type"]) - if parser is None: - raise ValueError(f"unknown block type: {d['type']}") - return parser(d) - - -# --------------------------------------------------------------------------- -# Token usage โ€” canonical definition lives in protocols.py -# --------------------------------------------------------------------------- - -from hanzoai.protocols import TokenUsage - - -# --------------------------------------------------------------------------- -# ConversationMessage -# --------------------------------------------------------------------------- - -@dataclass -class ConversationMessage: - role: MessageRole - blocks: list[ContentBlock] - usage: TokenUsage | None = None - - # -- Factory methods -- - - @staticmethod - def user_text(text: str) -> ConversationMessage: - return ConversationMessage(role=MessageRole.User, blocks=[TextBlock(text=text)]) - - @staticmethod - def assistant(blocks: list[ContentBlock]) -> ConversationMessage: - return ConversationMessage(role=MessageRole.Assistant, blocks=blocks) - - @staticmethod - def assistant_with_usage(blocks: list[ContentBlock], usage: TokenUsage) -> ConversationMessage: - return ConversationMessage(role=MessageRole.Assistant, blocks=blocks, usage=usage) - - @staticmethod - def tool_result(tool_use_id: str, tool_name: str, output: str, is_error: bool = False) -> ConversationMessage: - block = ToolResultBlock(tool_use_id=tool_use_id, tool_name=tool_name, output=output, is_error=is_error) - return ConversationMessage(role=MessageRole.Tool, blocks=[block]) - - # -- Serialization -- - - def to_dict(self) -> dict[str, Any]: - d: dict[str, Any] = { - "role": self.role.value, - "blocks": [b.to_dict() for b in self.blocks], - } - if self.usage is not None: - d["usage"] = self.usage.to_dict() - return d - - @staticmethod - def from_dict(d: dict[str, Any]) -> ConversationMessage: - return ConversationMessage( - role=MessageRole(d["role"]), - blocks=[_block_from_dict(b) for b in d["blocks"]], - usage=TokenUsage.from_dict(d["usage"]) if "usage" in d else None, - ) - - -# --------------------------------------------------------------------------- -# Session -# --------------------------------------------------------------------------- - -@dataclass -class Session: - version: int = 1 - messages: list[ConversationMessage] = field(default_factory=list) - - def to_dict(self) -> dict[str, Any]: - return { - "version": self.version, - "messages": [m.to_dict() for m in self.messages], - } - - @staticmethod - def from_dict(d: dict[str, Any]) -> Session: - return Session( - version=d.get("version", 1), - messages=[ConversationMessage.from_dict(m) for m in d.get("messages", [])], - ) - - def save(self, path: str | Path) -> None: - Path(path).write_text(json.dumps(self.to_dict(), indent=2), encoding="utf-8") - - @staticmethod - def load(path: str | Path) -> Session: - data = json.loads(Path(path).read_text(encoding="utf-8")) - return Session.from_dict(data) - - -# --------------------------------------------------------------------------- -# Compaction -# --------------------------------------------------------------------------- - -@dataclass -class CompactionConfig: - preserve_recent_messages: int = 4 - max_estimated_tokens: int = 10_000 - - -@dataclass -class CompactionResult: - summary: str - formatted_summary: str - compacted_session: Session - removed_message_count: int - - -_FILE_EXTENSIONS = frozenset({ - ".py", ".rs", ".go", ".ts", ".tsx", ".js", ".jsx", ".json", ".yaml", ".yml", - ".toml", ".md", ".sh", ".sql", ".html", ".css", ".c", ".h", ".cpp", ".hpp", -}) - -_PENDING_KEYWORDS = frozenset({"todo", "next", "pending", "follow up", "remaining"}) - - -def estimate_session_tokens(session: Session) -> int: - """Estimate token count: len(text)/4 + 1 per block.""" - total = 0 - for msg in session.messages: - for block in msg.blocks: - if isinstance(block, TextBlock): - total += len(block.text) // 4 + 1 - elif isinstance(block, ToolUseBlock): - total += len(block.input) // 4 + 1 - elif isinstance(block, ToolResultBlock): - total += len(block.output) // 4 + 1 - return total - - -def should_compact(session: Session, config: CompactionConfig) -> bool: - return ( - len(session.messages) > config.preserve_recent_messages - and estimate_session_tokens(session) >= config.max_estimated_tokens - ) - - -def compact_session(session: Session, config: CompactionConfig) -> CompactionResult: - """Compact a session by summarizing old messages and keeping recent ones.""" - if not should_compact(session, config): - return CompactionResult( - summary="", - formatted_summary="", - compacted_session=session, - removed_message_count=0, - ) - - msgs = session.messages - keep_count = min(config.preserve_recent_messages, len(msgs)) - split = len(msgs) - keep_count - removed = msgs[:split] - kept = msgs[split:] - - summary = _build_summary(removed) - formatted = format_compact_summary(summary) - - continuation_text = ( - "This session is being continued from a previous conversation that ran " - "out of context. The summary below covers the earlier portion of the " - "conversation.\n\n" - f"{formatted}\n\n" - "Recent messages are preserved verbatim.\n" - "Continue the conversation from where it left off without asking the " - "user any further questions. Resume directly \u2014 do not acknowledge the " - "summary, do not recap what was happening, and do not preface with " - "continuation text." - ) - - system_msg = ConversationMessage( - role=MessageRole.System, - blocks=[TextBlock(text=continuation_text)], - ) - - compacted = Session( - version=session.version, - messages=[system_msg] + kept, - ) - - return CompactionResult( - summary=summary, - formatted_summary=formatted, - compacted_session=compacted, - removed_message_count=len(removed), - ) - - -def _build_summary(messages: list[ConversationMessage]) -> str: - """Build a structured summary of removed messages, matching Rust summarize_messages().""" - role_counts: dict[str, int] = {} - tool_names: list[str] = [] - user_texts: list[str] = [] - pending_items: list[str] = [] - file_candidates: set[str] = set() - last_text = "" - timeline: list[tuple[str, str]] = [] # (role, block_summary) - - for msg in messages: - role_counts[msg.role.value] = role_counts.get(msg.role.value, 0) + 1 - - block_parts: list[str] = [] - for block in msg.blocks: - if isinstance(block, ToolUseBlock): - if block.name not in tool_names: - tool_names.append(block.name) - block_parts.append(f"[tool_use: {block.name}]") - elif isinstance(block, ToolResultBlock): - if block.tool_name not in tool_names: - tool_names.append(block.tool_name) - status = "error" if block.is_error else "ok" - block_parts.append(f"[tool_result: {block.tool_name} ({status})]") - - if isinstance(block, TextBlock): - if msg.role == MessageRole.User: - user_texts.append(block.text) - last_text = block.text - - text_lower = block.text.lower() - for kw in _PENDING_KEYWORDS: - if kw in text_lower: - for line in block.text.splitlines(): - if kw in line.lower(): - pending_items.append(line.strip()) - break - break - - for token in block.text.split(): - if "/" in token and _has_interesting_extension(token): - cleaned = token.strip("(),;:\"'`") - if "/" in cleaned: - file_candidates.add(cleaned) - - truncated = block.text[:80] + "..." if len(block.text) > 80 else block.text - block_parts.append(truncated.replace("\n", " ")) - - if block_parts: - timeline.append((msg.role.value, "; ".join(block_parts))) - - recent_requests = user_texts[-3:] if user_texts else [] - - u = role_counts.get("user", 0) - a = role_counts.get("assistant", 0) - t = role_counts.get("tool", 0) - tools_str = ", ".join(tool_names) if tool_names else "none" - files_str = ", ".join(sorted(file_candidates)[:20]) if file_candidates else "none" - last_text_truncated = last_text[:100].replace("\n", " ") if last_text else "n/a" - - lines: list[str] = ["", "Conversation summary:"] - lines.append(f"- Scope: {len(messages)} earlier messages compacted (user={u}, assistant={a}, tool={t}).") - lines.append(f"- Tools mentioned: {tools_str}.") - - if recent_requests: - lines.append("- Recent user requests:") - for req in recent_requests: - truncated = req[:200] + "..." if len(req) > 200 else req - lines.append(f" - {truncated}") - - if pending_items: - lines.append("- Pending work:") - for item in pending_items[:5]: - lines.append(f" - {item}") - - lines.append(f"- Key files referenced: {files_str}.") - lines.append(f"- Current work: {last_text_truncated}") - lines.append("- Key timeline:") - for role, block_summary in timeline[-10:]: - lines.append(f" - {role}: {block_summary}") - lines.append("") - - return "\n".join(lines) - - -def _has_interesting_extension(token: str) -> bool: - """Check if a token looks like a file path with a known extension.""" - for ext in _FILE_EXTENSIONS: - if token.endswith(ext): - return True - return False - - -def format_compact_summary(summary: str) -> str: - """Strip analysis tags, extract summary content, collapse blank lines. - - Matches Rust's format_compact_summary: - - Strip ... block - - Replace content with "Summary:\\ncontent" - - Collapse consecutive blank lines - """ - # Strip ... block - result = re.sub(r".*?\s*", "", summary, flags=re.DOTALL) - # Replace content with "Summary:\ncontent" - result = re.sub( - r"(.*?)", - lambda m: "Summary:\n" + m.group(1).strip(), - result, - flags=re.DOTALL, - ) - # Collapse consecutive blank lines - result = re.sub(r"\n{3,}", "\n\n", result) - return result.strip() diff --git a/pkg/hanzoai/types/__init__.py b/pkg/hanzoai/types/__init__.py deleted file mode 100644 index 3ce25a22c..000000000 --- a/pkg/hanzoai/types/__init__.py +++ /dev/null @@ -1,165 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from .member import Member as Member -from .user_roles import UserRoles as UserRoles -from .budget_table import ( - LlmBudgetTable as BudgetTable, - LlmBudgetTable as LlmBudgetTable, -) -from .member_param import MemberParam as MemberParam -from .key_list_params import KeyListParams as KeyListParams -from .file_list_params import FileListParams as FileListParams -from .key_block_params import KeyBlockParams as KeyBlockParams -from .model_info_param import ModelInfoParam as ModelInfoParam -from .org_member_param import OrgMemberParam as OrgMemberParam -from .team_list_params import TeamListParams as TeamListParams -from .user_list_params import UserListParams as UserListParams -from .batch_list_params import BatchListParams as BatchListParams -from .key_delete_params import KeyDeleteParams as KeyDeleteParams -from .key_list_response import KeyListResponse as KeyListResponse -from .key_update_params import KeyUpdateParams as KeyUpdateParams -from .model_list_params import ModelListParams as ModelListParams -from .team_block_params import TeamBlockParams as TeamBlockParams -from .budget_info_params import BudgetInfoParams as BudgetInfoParams -from .file_create_params import FileCreateParams as FileCreateParams -from .key_block_response import KeyBlockResponse as KeyBlockResponse -from .key_unblock_params import KeyUnblockParams as KeyUnblockParams -from .team_create_params import TeamCreateParams as TeamCreateParams -from .team_delete_params import TeamDeleteParams as TeamDeleteParams -from .team_update_params import TeamUpdateParams as TeamUpdateParams -from .user_create_params import UserCreateParams as UserCreateParams -from .user_delete_params import UserDeleteParams as UserDeleteParams -from .user_update_params import UserUpdateParams as UserUpdateParams -from .batch_create_params import BatchCreateParams as BatchCreateParams -from .cache_ping_response import CachePingResponse as CachePingResponse -from .key_generate_params import KeyGenerateParams as KeyGenerateParams -from .lite_llm_spend_logs import HanzoSpendLogs as HanzoSpendLogs -from .lite_llm_team_table import HanzoTeamTable as HanzoTeamTable -from .lite_llm_user_table import HanzoUserTable as HanzoUserTable -from .model_create_params import ModelCreateParams as ModelCreateParams -from .model_delete_params import ModelDeleteParams as ModelDeleteParams -from .team_unblock_params import TeamUnblockParams as TeamUnblockParams -from .budget_create_params import BudgetCreateParams as BudgetCreateParams -from .budget_delete_params import BudgetDeleteParams as BudgetDeleteParams -from .budget_update_params import BudgetUpdateParams as BudgetUpdateParams -from .lite_llm_model_table import HanzoModelTable as HanzoModelTable -from .user_create_response import UserCreateResponse as UserCreateResponse -from .batch_retrieve_params import BatchRetrieveParams as BatchRetrieveParams -from .customer_block_params import CustomerBlockParams as CustomerBlockParams -from .generate_key_response import GenerateKeyResponse as GenerateKeyResponse -from .budget_settings_params import BudgetSettingsParams as BudgetSettingsParams -from .customer_create_params import CustomerCreateParams as CustomerCreateParams -from .customer_delete_params import CustomerDeleteParams as CustomerDeleteParams -from .customer_list_response import CustomerListResponse as CustomerListResponse -from .customer_update_params import CustomerUpdateParams as CustomerUpdateParams -from .spend_list_logs_params import SpendListLogsParams as SpendListLogsParams -from .spend_list_tags_params import SpendListTagsParams as SpendListTagsParams -from .team_add_member_params import TeamAddMemberParams as TeamAddMemberParams -from .customer_unblock_params import CustomerUnblockParams as CustomerUnblockParams -from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams -from .guardrail_list_response import GuardrailListResponse as GuardrailListResponse -from .health_check_all_params import HealthCheckAllParams as HealthCheckAllParams -from .lite_llm_end_user_table import HanzoEndUserTable as HanzoEndUserTable -from .completion_create_params import CompletionCreateParams as CompletionCreateParams -from .credential_create_params import CredentialCreateParams as CredentialCreateParams -from .credential_update_params import CredentialUpdateParams as CredentialUpdateParams -from .key_retrieve_info_params import KeyRetrieveInfoParams as KeyRetrieveInfoParams -from .spend_list_logs_response import SpendListLogsResponse as SpendListLogsResponse -from .spend_list_tags_response import SpendListTagsResponse as SpendListTagsResponse -from .team_add_member_response import TeamAddMemberResponse as TeamAddMemberResponse -from .add_add_allowed_ip_params import AddAddAllowedIPParams as AddAddAllowedIPParams -from .key_check_health_response import KeyCheckHealthResponse as KeyCheckHealthResponse -from .team_remove_member_params import TeamRemoveMemberParams as TeamRemoveMemberParams -from .team_retrieve_info_params import TeamRetrieveInfoParams as TeamRetrieveInfoParams -from .team_update_member_params import TeamUpdateMemberParams as TeamUpdateMemberParams -from .user_retrieve_info_params import UserRetrieveInfoParams as UserRetrieveInfoParams -from .util_token_counter_params import UtilTokenCounterParams as UtilTokenCounterParams -from .organization_create_params import ( - OrganizationCreateParams as OrganizationCreateParams, -) -from .organization_delete_params import ( - OrganizationDeleteParams as OrganizationDeleteParams, -) -from .organization_list_response import ( - OrganizationListResponse as OrganizationListResponse, -) -from .organization_update_params import ( - OrganizationUpdateParams as OrganizationUpdateParams, -) -from .team_list_available_params import ( - TeamListAvailableParams as TeamListAvailableParams, -) -from .team_update_member_response import ( - TeamUpdateMemberResponse as TeamUpdateMemberResponse, -) -from .util_token_counter_response import ( - UtilTokenCounterResponse as UtilTokenCounterResponse, -) -from .health_check_services_params import ( - HealthCheckServicesParams as HealthCheckServicesParams, -) -from .key_regenerate_by_key_params import ( - KeyRegenerateByKeyParams as KeyRegenerateByKeyParams, -) -from .organization_create_response import ( - OrganizationCreateResponse as OrganizationCreateResponse, -) -from .organization_delete_response import ( - OrganizationDeleteResponse as OrganizationDeleteResponse, -) -from .organization_update_response import ( - OrganizationUpdateResponse as OrganizationUpdateResponse, -) -from .spend_calculate_spend_params import ( - SpendCalculateSpendParams as SpendCalculateSpendParams, -) -from .customer_retrieve_info_params import ( - CustomerRetrieveInfoParams as CustomerRetrieveInfoParams, -) -from .organization_membership_table import ( - OrganizationMembershipTable as OrganizationMembershipTable, -) -from .util_transform_request_params import ( - UtilTransformRequestParams as UtilTransformRequestParams, -) -from .organization_add_member_params import ( - OrganizationAddMemberParams as OrganizationAddMemberParams, -) -from .provider_list_budgets_response import ( - ProviderListBudgetsResponse as ProviderListBudgetsResponse, -) -from .batch_list_with_provider_params import ( - BatchListWithProviderParams as BatchListWithProviderParams, -) -from .delete_create_allowed_ip_params import ( - DeleteCreateAllowedIPParams as DeleteCreateAllowedIPParams, -) -from .organization_table_with_members import ( - OrganizationTableWithMembers as OrganizationTableWithMembers, -) -from .util_transform_request_response import ( - UtilTransformRequestResponse as UtilTransformRequestResponse, -) -from .model_group_retrieve_info_params import ( - ModelGroupRetrieveInfoParams as ModelGroupRetrieveInfoParams, -) -from .organization_add_member_response import ( - OrganizationAddMemberResponse as OrganizationAddMemberResponse, -) -from .organization_delete_member_params import ( - OrganizationDeleteMemberParams as OrganizationDeleteMemberParams, -) -from .organization_update_member_params import ( - OrganizationUpdateMemberParams as OrganizationUpdateMemberParams, -) -from .organization_update_member_response import ( - OrganizationUpdateMemberResponse as OrganizationUpdateMemberResponse, -) -from .util_get_supported_openai_params_params import ( - UtilGetSupportedOpenAIParamsParams as UtilGetSupportedOpenAIParamsParams, -) -from .configurable_clientside_params_custom_auth_param import ( - ConfigurableClientsideParamsCustomAuthParam as ConfigurableClientsideParamsCustomAuthParam, -) diff --git a/pkg/hanzoai/types/add_add_allowed_ip_params.py b/pkg/hanzoai/types/add_add_allowed_ip_params.py deleted file mode 100644 index f5432dc8a..000000000 --- a/pkg/hanzoai/types/add_add_allowed_ip_params.py +++ /dev/null @@ -1,11 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["AddAddAllowedIPParams"] - - -class AddAddAllowedIPParams(TypedDict, total=False): - ip: Required[str] diff --git a/pkg/hanzoai/types/audio/__init__.py b/pkg/hanzoai/types/audio/__init__.py deleted file mode 100644 index 24de9f37e..000000000 --- a/pkg/hanzoai/types/audio/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from .transcription_create_params import ( - TranscriptionCreateParams as TranscriptionCreateParams, -) diff --git a/pkg/hanzoai/types/audio/transcription_create_params.py b/pkg/hanzoai/types/audio/transcription_create_params.py deleted file mode 100644 index 238fa9fac..000000000 --- a/pkg/hanzoai/types/audio/transcription_create_params.py +++ /dev/null @@ -1,13 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -from ..._types import FileTypes - -__all__ = ["TranscriptionCreateParams"] - - -class TranscriptionCreateParams(TypedDict, total=False): - file: Required[FileTypes] diff --git a/pkg/hanzoai/types/batch_create_params.py b/pkg/hanzoai/types/batch_create_params.py deleted file mode 100644 index 87063dfe2..000000000 --- a/pkg/hanzoai/types/batch_create_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["BatchCreateParams"] - - -class BatchCreateParams(TypedDict, total=False): - provider: Optional[str] diff --git a/pkg/hanzoai/types/batch_retrieve_params.py b/pkg/hanzoai/types/batch_retrieve_params.py deleted file mode 100644 index 9857c06b6..000000000 --- a/pkg/hanzoai/types/batch_retrieve_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["BatchRetrieveParams"] - - -class BatchRetrieveParams(TypedDict, total=False): - provider: Optional[str] diff --git a/pkg/hanzoai/types/batches/__init__.py b/pkg/hanzoai/types/batches/__init__.py deleted file mode 100644 index b35403a57..000000000 --- a/pkg/hanzoai/types/batches/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from .cancel_cancel_params import CancelCancelParams as CancelCancelParams diff --git a/pkg/hanzoai/types/batches/cancel_cancel_params.py b/pkg/hanzoai/types/batches/cancel_cancel_params.py deleted file mode 100644 index 5dd26ded4..000000000 --- a/pkg/hanzoai/types/batches/cancel_cancel_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["CancelCancelParams"] - - -class CancelCancelParams(TypedDict, total=False): - provider: Optional[str] diff --git a/pkg/hanzoai/types/budget_delete_params.py b/pkg/hanzoai/types/budget_delete_params.py deleted file mode 100644 index 0e7a9c7a0..000000000 --- a/pkg/hanzoai/types/budget_delete_params.py +++ /dev/null @@ -1,11 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["BudgetDeleteParams"] - - -class BudgetDeleteParams(TypedDict, total=False): - id: Required[str] diff --git a/pkg/hanzoai/types/budget_info_params.py b/pkg/hanzoai/types/budget_info_params.py deleted file mode 100644 index b4209a47d..000000000 --- a/pkg/hanzoai/types/budget_info_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List -from typing_extensions import Required, TypedDict - -__all__ = ["BudgetInfoParams"] - - -class BudgetInfoParams(TypedDict, total=False): - budgets: Required[List[str]] diff --git a/pkg/hanzoai/types/budget_settings_params.py b/pkg/hanzoai/types/budget_settings_params.py deleted file mode 100644 index d0bb03a97..000000000 --- a/pkg/hanzoai/types/budget_settings_params.py +++ /dev/null @@ -1,11 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["BudgetSettingsParams"] - - -class BudgetSettingsParams(TypedDict, total=False): - budget_id: Required[str] diff --git a/pkg/hanzoai/types/budget_table.py b/pkg/hanzoai/types/budget_table.py deleted file mode 100644 index 55baa7e70..000000000 --- a/pkg/hanzoai/types/budget_table.py +++ /dev/null @@ -1,45 +0,0 @@ -# Hanzo AI SDK - -from typing import Optional -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["CustomerRetrieveInfoResponse", "LlmBudgetTable"] - - -class LlmBudgetTable(BaseModel): - budget_duration: Optional[str] = None - - max_budget: Optional[float] = None - - max_parallel_requests: Optional[int] = None - - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) - - rpm_limit: Optional[int] = None - - soft_budget: Optional[float] = None - - tpm_limit: Optional[int] = None - - -class CustomerRetrieveInfoResponse(BaseModel): - blocked: bool - - user_id: str - - alias: Optional[str] = None - - allowed_model_region: Optional[Literal["eu", "us"]] = None - - default_model: Optional[str] = None - - llm_budget_table: Optional[LlmBudgetTable] = None - """Represents user-controllable params for a LLM_BudgetTable record""" - - spend: Optional[float] = None diff --git a/pkg/hanzoai/types/cache/__init__.py b/pkg/hanzoai/types/cache/__init__.py deleted file mode 100644 index 78708ffaf..000000000 --- a/pkg/hanzoai/types/cache/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations diff --git a/pkg/hanzoai/types/chat/__init__.py b/pkg/hanzoai/types/chat/__init__.py deleted file mode 100644 index 6b3479470..000000000 --- a/pkg/hanzoai/types/chat/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from .completion_create_params import CompletionCreateParams as CompletionCreateParams diff --git a/pkg/hanzoai/types/chat/completion_create_params.py b/pkg/hanzoai/types/chat/completion_create_params.py deleted file mode 100644 index 3de6ebaa8..000000000 --- a/pkg/hanzoai/types/chat/completion_create_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["CompletionCreateParams"] - - -class CompletionCreateParams(TypedDict, total=False): - model: Optional[str] diff --git a/pkg/hanzoai/types/completion_create_params.py b/pkg/hanzoai/types/completion_create_params.py deleted file mode 100644 index 3de6ebaa8..000000000 --- a/pkg/hanzoai/types/completion_create_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["CompletionCreateParams"] - - -class CompletionCreateParams(TypedDict, total=False): - model: Optional[str] diff --git a/pkg/hanzoai/types/config/__init__.py b/pkg/hanzoai/types/config/__init__.py deleted file mode 100644 index 685a2ca6c..000000000 --- a/pkg/hanzoai/types/config/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from .pass_through_generic_endpoint import ( - PassThroughGenericEndpoint as PassThroughGenericEndpoint, -) -from .pass_through_endpoint_response import ( - PassThroughEndpointResponse as PassThroughEndpointResponse, -) -from .pass_through_endpoint_list_params import ( - PassThroughEndpointListParams as PassThroughEndpointListParams, -) -from .pass_through_endpoint_create_params import ( - PassThroughEndpointCreateParams as PassThroughEndpointCreateParams, -) -from .pass_through_endpoint_delete_params import ( - PassThroughEndpointDeleteParams as PassThroughEndpointDeleteParams, -) diff --git a/pkg/hanzoai/types/config/pass_through_endpoint_delete_params.py b/pkg/hanzoai/types/config/pass_through_endpoint_delete_params.py deleted file mode 100644 index 575967874..000000000 --- a/pkg/hanzoai/types/config/pass_through_endpoint_delete_params.py +++ /dev/null @@ -1,11 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["PassThroughEndpointDeleteParams"] - - -class PassThroughEndpointDeleteParams(TypedDict, total=False): - endpoint_id: Required[str] diff --git a/pkg/hanzoai/types/config/pass_through_endpoint_list_params.py b/pkg/hanzoai/types/config/pass_through_endpoint_list_params.py deleted file mode 100644 index a93a97c49..000000000 --- a/pkg/hanzoai/types/config/pass_through_endpoint_list_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["PassThroughEndpointListParams"] - - -class PassThroughEndpointListParams(TypedDict, total=False): - endpoint_id: Optional[str] diff --git a/pkg/hanzoai/types/config/pass_through_generic_endpoint.py b/pkg/hanzoai/types/config/pass_through_generic_endpoint.py deleted file mode 100644 index 1579f349f..000000000 --- a/pkg/hanzoai/types/config/pass_through_generic_endpoint.py +++ /dev/null @@ -1,21 +0,0 @@ -# Hanzo AI SDK - - -from ..._models import BaseModel - -__all__ = ["PassThroughGenericEndpoint"] - - -class PassThroughGenericEndpoint(BaseModel): - headers: object - """Key-value pairs of headers to be forwarded with the request. - - You can set any key value pair here and it will be forwarded to your target - endpoint - """ - - path: str - """The route to be added to the Hanzo Proxy Server.""" - - target: str - """The URL to which requests for this path should be forwarded.""" diff --git a/pkg/hanzoai/types/configurable_clientside_params_custom_auth_param.py b/pkg/hanzoai/types/configurable_clientside_params_custom_auth_param.py deleted file mode 100644 index e5d4700e9..000000000 --- a/pkg/hanzoai/types/configurable_clientside_params_custom_auth_param.py +++ /dev/null @@ -1,11 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["ConfigurableClientsideParamsCustomAuthParam"] - - -class ConfigurableClientsideParamsCustomAuthParam(TypedDict, total=False): - api_base: Required[str] diff --git a/pkg/hanzoai/types/credential_update_params.py b/pkg/hanzoai/types/credential_update_params.py deleted file mode 100644 index 31808432a..000000000 --- a/pkg/hanzoai/types/credential_update_params.py +++ /dev/null @@ -1,19 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["CredentialUpdateParams"] - - -class CredentialUpdateParams(TypedDict, total=False): - credential_info: Required[object] - - body_credential_name: Required[ - Annotated[str, PropertyInfo(alias="credential_name")] - ] - - credential_values: Required[object] diff --git a/pkg/hanzoai/types/customer_block_params.py b/pkg/hanzoai/types/customer_block_params.py deleted file mode 100644 index 485be7f0f..000000000 --- a/pkg/hanzoai/types/customer_block_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List -from typing_extensions import Required, TypedDict - -__all__ = ["CustomerBlockParams"] - - -class CustomerBlockParams(TypedDict, total=False): - user_ids: Required[List[str]] diff --git a/pkg/hanzoai/types/customer_delete_params.py b/pkg/hanzoai/types/customer_delete_params.py deleted file mode 100644 index c03b5b906..000000000 --- a/pkg/hanzoai/types/customer_delete_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List -from typing_extensions import Required, TypedDict - -__all__ = ["CustomerDeleteParams"] - - -class CustomerDeleteParams(TypedDict, total=False): - user_ids: Required[List[str]] diff --git a/pkg/hanzoai/types/customer_list_response.py b/pkg/hanzoai/types/customer_list_response.py deleted file mode 100644 index c5f67f12b..000000000 --- a/pkg/hanzoai/types/customer_list_response.py +++ /dev/null @@ -1,10 +0,0 @@ -# Hanzo AI SDK - -from typing import List -from typing_extensions import TypeAlias - -from .lite_llm_end_user_table import HanzoEndUserTable - -__all__ = ["CustomerListResponse"] - -CustomerListResponse: TypeAlias = List[HanzoEndUserTable] diff --git a/pkg/hanzoai/types/customer_unblock_params.py b/pkg/hanzoai/types/customer_unblock_params.py deleted file mode 100644 index 764a33214..000000000 --- a/pkg/hanzoai/types/customer_unblock_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List -from typing_extensions import Required, TypedDict - -__all__ = ["CustomerUnblockParams"] - - -class CustomerUnblockParams(TypedDict, total=False): - user_ids: Required[List[str]] diff --git a/pkg/hanzoai/types/delete_create_allowed_ip_params.py b/pkg/hanzoai/types/delete_create_allowed_ip_params.py deleted file mode 100644 index d191f6001..000000000 --- a/pkg/hanzoai/types/delete_create_allowed_ip_params.py +++ /dev/null @@ -1,11 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["DeleteCreateAllowedIPParams"] - - -class DeleteCreateAllowedIPParams(TypedDict, total=False): - ip: Required[str] diff --git a/pkg/hanzoai/types/embedding_create_params.py b/pkg/hanzoai/types/embedding_create_params.py deleted file mode 100644 index 166f8c08b..000000000 --- a/pkg/hanzoai/types/embedding_create_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["EmbeddingCreateParams"] - - -class EmbeddingCreateParams(TypedDict, total=False): - model: Optional[str] diff --git a/pkg/hanzoai/types/engines/__init__.py b/pkg/hanzoai/types/engines/__init__.py deleted file mode 100644 index 78708ffaf..000000000 --- a/pkg/hanzoai/types/engines/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations diff --git a/pkg/hanzoai/types/file_list_params.py b/pkg/hanzoai/types/file_list_params.py deleted file mode 100644 index a74ed7a33..000000000 --- a/pkg/hanzoai/types/file_list_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["FileListParams"] - - -class FileListParams(TypedDict, total=False): - purpose: Optional[str] diff --git a/pkg/hanzoai/types/files/__init__.py b/pkg/hanzoai/types/files/__init__.py deleted file mode 100644 index 78708ffaf..000000000 --- a/pkg/hanzoai/types/files/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations diff --git a/pkg/hanzoai/types/fine_tuning/__init__.py b/pkg/hanzoai/types/fine_tuning/__init__.py deleted file mode 100644 index b19486a0e..000000000 --- a/pkg/hanzoai/types/fine_tuning/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from .job_list_params import JobListParams as JobListParams -from .job_create_params import JobCreateParams as JobCreateParams -from .job_retrieve_params import JobRetrieveParams as JobRetrieveParams diff --git a/pkg/hanzoai/types/fine_tuning/job_retrieve_params.py b/pkg/hanzoai/types/fine_tuning/job_retrieve_params.py deleted file mode 100644 index 90a35f449..000000000 --- a/pkg/hanzoai/types/fine_tuning/job_retrieve_params.py +++ /dev/null @@ -1,11 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["JobRetrieveParams"] - - -class JobRetrieveParams(TypedDict, total=False): - custom_llm_provider: Required[Literal["openai", "azure"]] diff --git a/pkg/hanzoai/types/fine_tuning/jobs/__init__.py b/pkg/hanzoai/types/fine_tuning/jobs/__init__.py deleted file mode 100644 index 78708ffaf..000000000 --- a/pkg/hanzoai/types/fine_tuning/jobs/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations diff --git a/pkg/hanzoai/types/global_/__init__.py b/pkg/hanzoai/types/global_/__init__.py deleted file mode 100644 index bdffd9a74..000000000 --- a/pkg/hanzoai/types/global_/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from .spend_list_tags_params import SpendListTagsParams as SpendListTagsParams -from .spend_list_tags_response import SpendListTagsResponse as SpendListTagsResponse -from .spend_retrieve_report_params import ( - SpendRetrieveReportParams as SpendRetrieveReportParams, -) -from .spend_retrieve_report_response import ( - SpendRetrieveReportResponse as SpendRetrieveReportResponse, -) diff --git a/pkg/hanzoai/types/global_/spend_list_tags_params.py b/pkg/hanzoai/types/global_/spend_list_tags_params.py deleted file mode 100644 index 216dc498b..000000000 --- a/pkg/hanzoai/types/global_/spend_list_tags_params.py +++ /dev/null @@ -1,19 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["SpendListTagsParams"] - - -class SpendListTagsParams(TypedDict, total=False): - end_date: Optional[str] - """Time till which to view key spend""" - - start_date: Optional[str] - """Time from which to start viewing key spend""" - - tags: Optional[str] - """comman separated tags to filter on""" diff --git a/pkg/hanzoai/types/global_/spend_list_tags_response.py b/pkg/hanzoai/types/global_/spend_list_tags_response.py deleted file mode 100644 index ecac0a475..000000000 --- a/pkg/hanzoai/types/global_/spend_list_tags_response.py +++ /dev/null @@ -1,10 +0,0 @@ -# Hanzo AI SDK - -from typing import List -from typing_extensions import TypeAlias - -from ..lite_llm_spend_logs import HanzoSpendLogs - -__all__ = ["SpendListTagsResponse"] - -SpendListTagsResponse: TypeAlias = List[HanzoSpendLogs] diff --git a/pkg/hanzoai/types/global_/spend_retrieve_report_response.py b/pkg/hanzoai/types/global_/spend_retrieve_report_response.py deleted file mode 100644 index 5d7c6769d..000000000 --- a/pkg/hanzoai/types/global_/spend_retrieve_report_response.py +++ /dev/null @@ -1,10 +0,0 @@ -# Hanzo AI SDK - -from typing import List -from typing_extensions import TypeAlias - -from ..lite_llm_spend_logs import HanzoSpendLogs - -__all__ = ["SpendRetrieveReportResponse"] - -SpendRetrieveReportResponse: TypeAlias = List[HanzoSpendLogs] diff --git a/pkg/hanzoai/types/guardrail_list_response.py b/pkg/hanzoai/types/guardrail_list_response.py deleted file mode 100644 index d0d50823a..000000000 --- a/pkg/hanzoai/types/guardrail_list_response.py +++ /dev/null @@ -1,28 +0,0 @@ -# Hanzo AI SDK - -from typing import List, Union, Optional - -from .._models import BaseModel - -__all__ = ["GuardrailListResponse", "Guardrail", "GuardrailLitellmParams"] - - -class GuardrailLitellmParams(BaseModel): - guardrail: str - - mode: Union[str, List[str]] - - default_on: Optional[bool] = None - - -class Guardrail(BaseModel): - guardrail_info: Optional[object] = None - - guardrail_name: str - - hanzo_params: GuardrailLitellmParams - """The returned Hanzo Params object for /guardrails/list""" - - -class GuardrailListResponse(BaseModel): - guardrails: List[Guardrail] diff --git a/pkg/hanzoai/types/health_check_services_params.py b/pkg/hanzoai/types/health_check_services_params.py deleted file mode 100644 index a37ea8309..000000000 --- a/pkg/hanzoai/types/health_check_services_params.py +++ /dev/null @@ -1,27 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["HealthCheckServicesParams"] - - -class HealthCheckServicesParams(TypedDict, total=False): - service: Required[ - Union[ - Literal[ - "slack_budget_alerts", - "langfuse", - "slack", - "openmeter", - "webhook", - "email", - "braintrust", - "datadog", - ], - str, - ] - ] - """Specify the service being hit.""" diff --git a/pkg/hanzoai/types/images/__init__.py b/pkg/hanzoai/types/images/__init__.py deleted file mode 100644 index 78708ffaf..000000000 --- a/pkg/hanzoai/types/images/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations diff --git a/pkg/hanzoai/types/key/__init__.py b/pkg/hanzoai/types/key/__init__.py deleted file mode 100644 index 78708ffaf..000000000 --- a/pkg/hanzoai/types/key/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations diff --git a/pkg/hanzoai/types/key_block_params.py b/pkg/hanzoai/types/key_block_params.py deleted file mode 100644 index 25c25ceb0..000000000 --- a/pkg/hanzoai/types/key_block_params.py +++ /dev/null @@ -1,20 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["KeyBlockParams"] - - -class KeyBlockParams(TypedDict, total=False): - key: Required[str] - - hanzo_changed_by: Annotated[str, PropertyInfo(alias="hanzo-changed-by")] - """ - The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - """ diff --git a/pkg/hanzoai/types/key_delete_params.py b/pkg/hanzoai/types/key_delete_params.py deleted file mode 100644 index 206978bbe..000000000 --- a/pkg/hanzoai/types/key_delete_params.py +++ /dev/null @@ -1,23 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["KeyDeleteParams"] - - -class KeyDeleteParams(TypedDict, total=False): - key_aliases: Optional[List[str]] - - keys: Optional[List[str]] - - hanzo_changed_by: Annotated[str, PropertyInfo(alias="hanzo-changed-by")] - """ - The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - """ diff --git a/pkg/hanzoai/types/key_generate_params.py b/pkg/hanzoai/types/key_generate_params.py deleted file mode 100644 index 60428baab..000000000 --- a/pkg/hanzoai/types/key_generate_params.py +++ /dev/null @@ -1,73 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List, Iterable, Optional -from typing_extensions import Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["KeyGenerateParams"] - - -class KeyGenerateParams(TypedDict, total=False): - aliases: Optional[object] - - allowed_cache_controls: Optional[Iterable[object]] - - blocked: Optional[bool] - - budget_duration: Optional[str] - - budget_id: Optional[str] - - config: Optional[object] - - duration: Optional[str] - - enforced_params: Optional[List[str]] - - guardrails: Optional[List[str]] - - key: Optional[str] - - key_alias: Optional[str] - - max_budget: Optional[float] - - max_parallel_requests: Optional[int] - - metadata: Optional[object] - - model_max_budget: Optional[object] - - model_rpm_limit: Optional[object] - - model_tpm_limit: Optional[object] - - models: Optional[Iterable[object]] - - permissions: Optional[object] - - rpm_limit: Optional[int] - - send_invite_email: Optional[bool] - - soft_budget: Optional[float] - - spend: Optional[float] - - tags: Optional[List[str]] - - team_id: Optional[str] - - tpm_limit: Optional[int] - - user_id: Optional[str] - - hanzo_changed_by: Annotated[str, PropertyInfo(alias="hanzo-changed-by")] - """ - The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - """ diff --git a/pkg/hanzoai/types/key_regenerate_by_key_params.py b/pkg/hanzoai/types/key_regenerate_by_key_params.py deleted file mode 100644 index 80a43a34d..000000000 --- a/pkg/hanzoai/types/key_regenerate_by_key_params.py +++ /dev/null @@ -1,75 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List, Iterable, Optional -from typing_extensions import Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["KeyRegenerateByKeyParams"] - - -class KeyRegenerateByKeyParams(TypedDict, total=False): - aliases: Optional[object] - - allowed_cache_controls: Optional[Iterable[object]] - - blocked: Optional[bool] - - budget_duration: Optional[str] - - budget_id: Optional[str] - - config: Optional[object] - - duration: Optional[str] - - enforced_params: Optional[List[str]] - - guardrails: Optional[List[str]] - - body_key: Annotated[Optional[str], PropertyInfo(alias="key")] - - key_alias: Optional[str] - - max_budget: Optional[float] - - max_parallel_requests: Optional[int] - - metadata: Optional[object] - - model_max_budget: Optional[object] - - model_rpm_limit: Optional[object] - - model_tpm_limit: Optional[object] - - models: Optional[Iterable[object]] - - new_master_key: Optional[str] - - permissions: Optional[object] - - rpm_limit: Optional[int] - - send_invite_email: Optional[bool] - - soft_budget: Optional[float] - - spend: Optional[float] - - tags: Optional[List[str]] - - team_id: Optional[str] - - tpm_limit: Optional[int] - - user_id: Optional[str] - - hanzo_changed_by: Annotated[str, PropertyInfo(alias="hanzo-changed-by")] - """ - The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - """ diff --git a/pkg/hanzoai/types/key_unblock_params.py b/pkg/hanzoai/types/key_unblock_params.py deleted file mode 100644 index 97dcf8a44..000000000 --- a/pkg/hanzoai/types/key_unblock_params.py +++ /dev/null @@ -1,20 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["KeyUnblockParams"] - - -class KeyUnblockParams(TypedDict, total=False): - key: Required[str] - - hanzo_changed_by: Annotated[str, PropertyInfo(alias="hanzo-changed-by")] - """ - The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - """ diff --git a/pkg/hanzoai/types/key_update_params.py b/pkg/hanzoai/types/key_update_params.py deleted file mode 100644 index 110220dfd..000000000 --- a/pkg/hanzoai/types/key_update_params.py +++ /dev/null @@ -1,76 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List, Union, Iterable, Optional -from datetime import datetime -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["KeyUpdateParams"] - - -class KeyUpdateParams(TypedDict, total=False): - key: Required[str] - - aliases: Optional[object] - - allowed_cache_controls: Optional[Iterable[object]] - - blocked: Optional[bool] - - budget_duration: Optional[str] - - budget_id: Optional[str] - - config: Optional[object] - - duration: Optional[str] - - enforced_params: Optional[List[str]] - - guardrails: Optional[List[str]] - - key_alias: Optional[str] - - max_budget: Optional[float] - - max_parallel_requests: Optional[int] - - metadata: Optional[object] - - model_max_budget: Optional[object] - - model_rpm_limit: Optional[object] - - model_tpm_limit: Optional[object] - - models: Optional[Iterable[object]] - - permissions: Optional[object] - - rpm_limit: Optional[int] - - spend: Optional[float] - - tags: Optional[List[str]] - - team_id: Optional[str] - - temp_budget_expiry: Annotated[ - Union[str, datetime, None], PropertyInfo(format="iso8601") - ] - - temp_budget_increase: Optional[float] - - tpm_limit: Optional[int] - - user_id: Optional[str] - - hanzo_changed_by: Annotated[str, PropertyInfo(alias="hanzo-changed-by")] - """ - The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - """ diff --git a/pkg/hanzoai/types/lite_llm_end_user_table.py b/pkg/hanzoai/types/lite_llm_end_user_table.py deleted file mode 100644 index 2a3b159e7..000000000 --- a/pkg/hanzoai/types/lite_llm_end_user_table.py +++ /dev/null @@ -1,26 +0,0 @@ -# Hanzo AI SDK - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel -from .budget_table import LlmBudgetTable as BudgetTable - -__all__ = ["HanzoEndUserTable"] - - -class HanzoEndUserTable(BaseModel): - blocked: bool - - user_id: str - - alias: Optional[str] = None - - allowed_model_region: Optional[Literal["eu", "us"]] = None - - default_model: Optional[str] = None - - hanzo_budget_table: Optional[BudgetTable] = None - """Represents user-controllable params for a Hanzo_BudgetTable record""" - - spend: Optional[float] = None diff --git a/pkg/hanzoai/types/lite_llm_model_table.py b/pkg/hanzoai/types/lite_llm_model_table.py deleted file mode 100644 index 258399e51..000000000 --- a/pkg/hanzoai/types/lite_llm_model_table.py +++ /dev/null @@ -1,19 +0,0 @@ -# Hanzo AI SDK - -from typing import Union - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["HanzoModelTable"] - - -class HanzoModelTable(BaseModel): - created_by: str - - updated_by: str - - api_model_aliases: Union[str, object, None] = FieldInfo( - alias="model_aliases", default=None - ) diff --git a/pkg/hanzoai/types/lite_llm_spend_logs.py b/pkg/hanzoai/types/lite_llm_spend_logs.py deleted file mode 100644 index 12f6baf4e..000000000 --- a/pkg/hanzoai/types/lite_llm_spend_logs.py +++ /dev/null @@ -1,50 +0,0 @@ -# Hanzo AI SDK - -from typing import List, Union, Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["HanzoSpendLogs"] - - -class HanzoSpendLogs(BaseModel): - api_key: str - - call_type: str - - end_time: Union[str, datetime, None] = FieldInfo(alias="endTime", default=None) - - messages: Union[str, List[object], object, None] = None - - request_id: str - - response: Union[str, List[object], object, None] = None - - start_time: Union[str, datetime, None] = FieldInfo(alias="startTime", default=None) - - api_base: Optional[str] = None - - cache_hit: Optional[str] = None - - cache_key: Optional[str] = None - - completion_tokens: Optional[int] = None - - metadata: Optional[object] = None - - model: Optional[str] = None - - prompt_tokens: Optional[int] = None - - request_tags: Optional[object] = None - - requester_ip_address: Optional[str] = None - - spend: Optional[float] = None - - total_tokens: Optional[int] = None - - user: Optional[str] = None diff --git a/pkg/hanzoai/types/lite_llm_team_table.py b/pkg/hanzoai/types/lite_llm_team_table.py deleted file mode 100644 index 7523c2b8f..000000000 --- a/pkg/hanzoai/types/lite_llm_team_table.py +++ /dev/null @@ -1,52 +0,0 @@ -# Hanzo AI SDK - -from typing import List, Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from .member import Member -from .._models import BaseModel -from .lite_llm_model_table import HanzoModelTable - -__all__ = ["HanzoTeamTable"] - - -class HanzoTeamTable(BaseModel): - team_id: str - - admins: Optional[List[object]] = None - - blocked: Optional[bool] = None - - budget_duration: Optional[str] = None - - budget_reset_at: Optional[datetime] = None - - created_at: Optional[datetime] = None - - hanzo_model_table: Optional[HanzoModelTable] = None - - max_budget: Optional[float] = None - - max_parallel_requests: Optional[int] = None - - members: Optional[List[object]] = None - - members_with_roles: Optional[List[Member]] = None - - metadata: Optional[object] = None - - api_model_id: Optional[int] = FieldInfo(alias="model_id", default=None) - - models: Optional[List[object]] = None - - organization_id: Optional[str] = None - - rpm_limit: Optional[int] = None - - spend: Optional[float] = None - - team_alias: Optional[str] = None - - tpm_limit: Optional[int] = None diff --git a/pkg/hanzoai/types/lite_llm_user_table.py b/pkg/hanzoai/types/lite_llm_user_table.py deleted file mode 100644 index 4025f9779..000000000 --- a/pkg/hanzoai/types/lite_llm_user_table.py +++ /dev/null @@ -1,47 +0,0 @@ -# Hanzo AI SDK - -from typing import List, Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from .._models import BaseModel -from .organization_membership_table import OrganizationMembershipTable - -__all__ = ["HanzoUserTable"] - - -class HanzoUserTable(BaseModel): - user_id: str - - budget_duration: Optional[str] = None - - budget_reset_at: Optional[datetime] = None - - max_budget: Optional[float] = None - - metadata: Optional[object] = None - - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) - - api_model_spend: Optional[object] = FieldInfo(alias="model_spend", default=None) - - models: Optional[List[object]] = None - - organization_memberships: Optional[List[OrganizationMembershipTable]] = None - - rpm_limit: Optional[int] = None - - spend: Optional[float] = None - - sso_user_id: Optional[str] = None - - teams: Optional[List[str]] = None - - tpm_limit: Optional[int] = None - - user_email: Optional[str] = None - - user_role: Optional[str] = None diff --git a/pkg/hanzoai/types/member.py b/pkg/hanzoai/types/member.py deleted file mode 100644 index b7b48a9de..000000000 --- a/pkg/hanzoai/types/member.py +++ /dev/null @@ -1,16 +0,0 @@ -# Hanzo AI SDK - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["Member"] - - -class Member(BaseModel): - role: Literal["admin", "user"] - - user_email: Optional[str] = None - - user_id: Optional[str] = None diff --git a/pkg/hanzoai/types/model/__init__.py b/pkg/hanzoai/types/model/__init__.py deleted file mode 100644 index dc96a4dc2..000000000 --- a/pkg/hanzoai/types/model/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from .info_list_params import InfoListParams as InfoListParams -from .update_full_params import UpdateFullParams as UpdateFullParams -from .update_partial_params import UpdatePartialParams as UpdatePartialParams diff --git a/pkg/hanzoai/types/model/info_list_params.py b/pkg/hanzoai/types/model/info_list_params.py deleted file mode 100644 index 3a30926e5..000000000 --- a/pkg/hanzoai/types/model/info_list_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["InfoListParams"] - - -class InfoListParams(TypedDict, total=False): - hanzo_model_id: Optional[str] diff --git a/pkg/hanzoai/types/model/update_full_params.py b/pkg/hanzoai/types/model/update_full_params.py deleted file mode 100644 index 5b5e5e032..000000000 --- a/pkg/hanzoai/types/model/update_full_params.py +++ /dev/null @@ -1,99 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Dict, List, Union, Optional -from typing_extensions import TypeAlias, TypedDict - -from ..model_info_param import ModelInfoParam -from ..configurable_clientside_params_custom_auth_param import ( - ConfigurableClientsideParamsCustomAuthParam, -) - -__all__ = [ - "UpdateFullParams", - "LitellmParams", - "LitellmParamsConfigurableClientsideAuthParam", -] - - -class UpdateFullParams(TypedDict, total=False): - hanzo_params: Optional[LitellmParams] - - model_info: Optional[ModelInfoParam] - - model_name: Optional[str] - - -LitellmParamsConfigurableClientsideAuthParam: TypeAlias = Union[ - str, ConfigurableClientsideParamsCustomAuthParam -] - - -class LitellmParamsTyped(TypedDict, total=False): - api_base: Optional[str] - - api_key: Optional[str] - - api_version: Optional[str] - - aws_access_key_id: Optional[str] - - aws_region_name: Optional[str] - - aws_secret_access_key: Optional[str] - - budget_duration: Optional[str] - - configurable_clientside_auth_params: Optional[ - List[LitellmParamsConfigurableClientsideAuthParam] - ] - - custom_llm_provider: Optional[str] - - input_cost_per_second: Optional[float] - - input_cost_per_token: Optional[float] - - hanzo_trace_id: Optional[str] - - max_budget: Optional[float] - - max_file_size_mb: Optional[float] - - max_retries: Optional[int] - - merge_reasoning_content_in_choices: Optional[bool] - - model: Optional[str] - - model_info: Optional[object] - - organization: Optional[str] - - output_cost_per_second: Optional[float] - - output_cost_per_token: Optional[float] - - region_name: Optional[str] - - rpm: Optional[int] - - stream_timeout: Union[float, str, None] - - timeout: Union[float, str, None] - - tpm: Optional[int] - - use_in_pass_through: Optional[bool] - - vertex_credentials: Union[str, object, None] - - vertex_location: Optional[str] - - vertex_project: Optional[str] - - watsonx_region_name: Optional[str] - - -LitellmParams: TypeAlias = Union[LitellmParamsTyped, Dict[str, object]] diff --git a/pkg/hanzoai/types/model/update_partial_params.py b/pkg/hanzoai/types/model/update_partial_params.py deleted file mode 100644 index 0c009d9c1..000000000 --- a/pkg/hanzoai/types/model/update_partial_params.py +++ /dev/null @@ -1,99 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Dict, List, Union, Optional -from typing_extensions import TypeAlias, TypedDict - -from ..model_info_param import ModelInfoParam -from ..configurable_clientside_params_custom_auth_param import ( - ConfigurableClientsideParamsCustomAuthParam, -) - -__all__ = [ - "UpdatePartialParams", - "LitellmParams", - "LitellmParamsConfigurableClientsideAuthParam", -] - - -class UpdatePartialParams(TypedDict, total=False): - hanzo_params: Optional[LitellmParams] - - model_info: Optional[ModelInfoParam] - - model_name: Optional[str] - - -LitellmParamsConfigurableClientsideAuthParam: TypeAlias = Union[ - str, ConfigurableClientsideParamsCustomAuthParam -] - - -class LitellmParamsTyped(TypedDict, total=False): - api_base: Optional[str] - - api_key: Optional[str] - - api_version: Optional[str] - - aws_access_key_id: Optional[str] - - aws_region_name: Optional[str] - - aws_secret_access_key: Optional[str] - - budget_duration: Optional[str] - - configurable_clientside_auth_params: Optional[ - List[LitellmParamsConfigurableClientsideAuthParam] - ] - - custom_llm_provider: Optional[str] - - input_cost_per_second: Optional[float] - - input_cost_per_token: Optional[float] - - hanzo_trace_id: Optional[str] - - max_budget: Optional[float] - - max_file_size_mb: Optional[float] - - max_retries: Optional[int] - - merge_reasoning_content_in_choices: Optional[bool] - - model: Optional[str] - - model_info: Optional[object] - - organization: Optional[str] - - output_cost_per_second: Optional[float] - - output_cost_per_token: Optional[float] - - region_name: Optional[str] - - rpm: Optional[int] - - stream_timeout: Union[float, str, None] - - timeout: Union[float, str, None] - - tpm: Optional[int] - - use_in_pass_through: Optional[bool] - - vertex_credentials: Union[str, object, None] - - vertex_location: Optional[str] - - vertex_project: Optional[str] - - watsonx_region_name: Optional[str] - - -LitellmParams: TypeAlias = Union[LitellmParamsTyped, Dict[str, object]] diff --git a/pkg/hanzoai/types/model_create_params.py b/pkg/hanzoai/types/model_create_params.py deleted file mode 100644 index 88103aa5e..000000000 --- a/pkg/hanzoai/types/model_create_params.py +++ /dev/null @@ -1,100 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Dict, List, Union, Optional -from typing_extensions import Required, TypeAlias, TypedDict - -from .model_info_param import ModelInfoParam -from .configurable_clientside_params_custom_auth_param import ( - ConfigurableClientsideParamsCustomAuthParam, -) - -__all__ = [ - "ModelCreateParams", - "LitellmParams", - "LitellmParamsConfigurableClientsideAuthParam", -] - - -class ModelCreateParams(TypedDict, total=False): - hanzo_params: Required[LitellmParams] - """Hanzo Params with 'model' requirement - used for completions""" - - model_info: Required[ModelInfoParam] - - model_name: Required[str] - - -LitellmParamsConfigurableClientsideAuthParam: TypeAlias = Union[ - str, ConfigurableClientsideParamsCustomAuthParam -] - - -class LitellmParamsTyped(TypedDict, total=False): - model: Required[str] - - api_base: Optional[str] - - api_key: Optional[str] - - api_version: Optional[str] - - aws_access_key_id: Optional[str] - - aws_region_name: Optional[str] - - aws_secret_access_key: Optional[str] - - budget_duration: Optional[str] - - configurable_clientside_auth_params: Optional[ - List[LitellmParamsConfigurableClientsideAuthParam] - ] - - custom_llm_provider: Optional[str] - - input_cost_per_second: Optional[float] - - input_cost_per_token: Optional[float] - - hanzo_trace_id: Optional[str] - - max_budget: Optional[float] - - max_file_size_mb: Optional[float] - - max_retries: Optional[int] - - merge_reasoning_content_in_choices: Optional[bool] - - model_info: Optional[object] - - organization: Optional[str] - - output_cost_per_second: Optional[float] - - output_cost_per_token: Optional[float] - - region_name: Optional[str] - - rpm: Optional[int] - - stream_timeout: Union[float, str, None] - - timeout: Union[float, str, None] - - tpm: Optional[int] - - use_in_pass_through: Optional[bool] - - vertex_credentials: Union[str, object, None] - - vertex_location: Optional[str] - - vertex_project: Optional[str] - - watsonx_region_name: Optional[str] - - -LitellmParams: TypeAlias = Union[LitellmParamsTyped, Dict[str, object]] diff --git a/pkg/hanzoai/types/model_delete_params.py b/pkg/hanzoai/types/model_delete_params.py deleted file mode 100644 index 7ee108f04..000000000 --- a/pkg/hanzoai/types/model_delete_params.py +++ /dev/null @@ -1,11 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["ModelDeleteParams"] - - -class ModelDeleteParams(TypedDict, total=False): - id: Required[str] diff --git a/pkg/hanzoai/types/model_group_retrieve_info_params.py b/pkg/hanzoai/types/model_group_retrieve_info_params.py deleted file mode 100644 index 4fb788dcb..000000000 --- a/pkg/hanzoai/types/model_group_retrieve_info_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["ModelGroupRetrieveInfoParams"] - - -class ModelGroupRetrieveInfoParams(TypedDict, total=False): - model_group: Optional[str] diff --git a/pkg/hanzoai/types/model_list_params.py b/pkg/hanzoai/types/model_list_params.py deleted file mode 100644 index 0d4b18793..000000000 --- a/pkg/hanzoai/types/model_list_params.py +++ /dev/null @@ -1,14 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["ModelListParams"] - - -class ModelListParams(TypedDict, total=False): - return_wildcard_routes: Optional[bool] - - team_id: Optional[str] diff --git a/pkg/hanzoai/types/openai/__init__.py b/pkg/hanzoai/types/openai/__init__.py deleted file mode 100644 index 78708ffaf..000000000 --- a/pkg/hanzoai/types/openai/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations diff --git a/pkg/hanzoai/types/openai/deployments/__init__.py b/pkg/hanzoai/types/openai/deployments/__init__.py deleted file mode 100644 index 78708ffaf..000000000 --- a/pkg/hanzoai/types/openai/deployments/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations diff --git a/pkg/hanzoai/types/organization/__init__.py b/pkg/hanzoai/types/organization/__init__.py deleted file mode 100644 index 8edbefdae..000000000 --- a/pkg/hanzoai/types/organization/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from .info_retrieve_params import InfoRetrieveParams as InfoRetrieveParams -from .info_deprecated_params import InfoDeprecatedParams as InfoDeprecatedParams -from .info_retrieve_response import InfoRetrieveResponse as InfoRetrieveResponse diff --git a/pkg/hanzoai/types/organization/info_deprecated_params.py b/pkg/hanzoai/types/organization/info_deprecated_params.py deleted file mode 100644 index ec1e6fd90..000000000 --- a/pkg/hanzoai/types/organization/info_deprecated_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List -from typing_extensions import Required, TypedDict - -__all__ = ["InfoDeprecatedParams"] - - -class InfoDeprecatedParams(TypedDict, total=False): - organizations: Required[List[str]] diff --git a/pkg/hanzoai/types/organization/info_retrieve_params.py b/pkg/hanzoai/types/organization/info_retrieve_params.py deleted file mode 100644 index 38d7668ca..000000000 --- a/pkg/hanzoai/types/organization/info_retrieve_params.py +++ /dev/null @@ -1,11 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["InfoRetrieveParams"] - - -class InfoRetrieveParams(TypedDict, total=False): - organization_id: Required[str] diff --git a/pkg/hanzoai/types/organization_add_member_response.py b/pkg/hanzoai/types/organization_add_member_response.py deleted file mode 100644 index a62f12dc7..000000000 --- a/pkg/hanzoai/types/organization_add_member_response.py +++ /dev/null @@ -1,17 +0,0 @@ -# Hanzo AI SDK - -from typing import List - -from .._models import BaseModel -from .lite_llm_user_table import HanzoUserTable -from .organization_membership_table import OrganizationMembershipTable - -__all__ = ["OrganizationAddMemberResponse"] - - -class OrganizationAddMemberResponse(BaseModel): - organization_id: str - - updated_organization_memberships: List[OrganizationMembershipTable] - - updated_users: List[HanzoUserTable] diff --git a/pkg/hanzoai/types/organization_delete_params.py b/pkg/hanzoai/types/organization_delete_params.py deleted file mode 100644 index 3c1e1697e..000000000 --- a/pkg/hanzoai/types/organization_delete_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List -from typing_extensions import Required, TypedDict - -__all__ = ["OrganizationDeleteParams"] - - -class OrganizationDeleteParams(TypedDict, total=False): - organization_ids: Required[List[str]] diff --git a/pkg/hanzoai/types/organization_membership_table.py b/pkg/hanzoai/types/organization_membership_table.py deleted file mode 100644 index c9e5cfa19..000000000 --- a/pkg/hanzoai/types/organization_membership_table.py +++ /dev/null @@ -1,30 +0,0 @@ -# Hanzo AI SDK - -from typing import Optional -from datetime import datetime - -from .._models import BaseModel -from .budget_table import LlmBudgetTable as BudgetTable - -__all__ = ["OrganizationMembershipTable"] - - -class OrganizationMembershipTable(BaseModel): - created_at: datetime - - organization_id: str - - updated_at: datetime - - user_id: str - - budget_id: Optional[str] = None - - hanzo_budget_table: Optional[BudgetTable] = None - """Represents user-controllable params for a Hanzo_BudgetTable record""" - - spend: Optional[float] = None - - user: Optional[object] = None - - user_role: Optional[str] = None diff --git a/pkg/hanzoai/types/organization_table_with_members.py b/pkg/hanzoai/types/organization_table_with_members.py deleted file mode 100644 index 41d4800ec..000000000 --- a/pkg/hanzoai/types/organization_table_with_members.py +++ /dev/null @@ -1,40 +0,0 @@ -# Hanzo AI SDK - -from typing import List, Optional -from datetime import datetime - -from .._models import BaseModel -from .budget_table import LlmBudgetTable as BudgetTable -from .lite_llm_team_table import HanzoTeamTable -from .organization_membership_table import OrganizationMembershipTable - -__all__ = ["OrganizationTableWithMembers"] - - -class OrganizationTableWithMembers(BaseModel): - budget_id: str - - created_at: datetime - - created_by: str - - models: List[str] - - updated_at: datetime - - updated_by: str - - hanzo_budget_table: Optional[BudgetTable] = None - """Represents user-controllable params for a Hanzo_BudgetTable record""" - - members: Optional[List[OrganizationMembershipTable]] = None - - metadata: Optional[object] = None - - organization_alias: Optional[str] = None - - organization_id: Optional[str] = None - - spend: Optional[float] = None - - teams: Optional[List[HanzoTeamTable]] = None diff --git a/pkg/hanzoai/types/organization_update_params.py b/pkg/hanzoai/types/organization_update_params.py deleted file mode 100644 index 2ed44e817..000000000 --- a/pkg/hanzoai/types/organization_update_params.py +++ /dev/null @@ -1,24 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import TypedDict - -__all__ = ["OrganizationUpdateParams"] - - -class OrganizationUpdateParams(TypedDict, total=False): - budget_id: Optional[str] - - metadata: Optional[object] - - models: Optional[List[str]] - - organization_alias: Optional[str] - - organization_id: Optional[str] - - spend: Optional[float] - - updated_by: Optional[str] diff --git a/pkg/hanzoai/types/responses/__init__.py b/pkg/hanzoai/types/responses/__init__.py deleted file mode 100644 index 78708ffaf..000000000 --- a/pkg/hanzoai/types/responses/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations diff --git a/pkg/hanzoai/types/spend_list_logs_response.py b/pkg/hanzoai/types/spend_list_logs_response.py deleted file mode 100644 index 76c7b6cac..000000000 --- a/pkg/hanzoai/types/spend_list_logs_response.py +++ /dev/null @@ -1,10 +0,0 @@ -# Hanzo AI SDK - -from typing import List -from typing_extensions import TypeAlias - -from .lite_llm_spend_logs import HanzoSpendLogs - -__all__ = ["SpendListLogsResponse"] - -SpendListLogsResponse: TypeAlias = List[HanzoSpendLogs] diff --git a/pkg/hanzoai/types/spend_list_tags_params.py b/pkg/hanzoai/types/spend_list_tags_params.py deleted file mode 100644 index dd3270793..000000000 --- a/pkg/hanzoai/types/spend_list_tags_params.py +++ /dev/null @@ -1,16 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["SpendListTagsParams"] - - -class SpendListTagsParams(TypedDict, total=False): - end_date: Optional[str] - """Time till which to view key spend""" - - start_date: Optional[str] - """Time from which to start viewing key spend""" diff --git a/pkg/hanzoai/types/spend_list_tags_response.py b/pkg/hanzoai/types/spend_list_tags_response.py deleted file mode 100644 index 753a02e66..000000000 --- a/pkg/hanzoai/types/spend_list_tags_response.py +++ /dev/null @@ -1,10 +0,0 @@ -# Hanzo AI SDK - -from typing import List -from typing_extensions import TypeAlias - -from .lite_llm_spend_logs import HanzoSpendLogs - -__all__ = ["SpendListTagsResponse"] - -SpendListTagsResponse: TypeAlias = List[HanzoSpendLogs] diff --git a/pkg/hanzoai/types/team/__init__.py b/pkg/hanzoai/types/team/__init__.py deleted file mode 100644 index a56487bed..000000000 --- a/pkg/hanzoai/types/team/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from .model_add_params import ModelAddParams as ModelAddParams -from .callback_add_params import CallbackAddParams as CallbackAddParams -from .model_remove_params import ModelRemoveParams as ModelRemoveParams diff --git a/pkg/hanzoai/types/team/callback_add_params.py b/pkg/hanzoai/types/team/callback_add_params.py deleted file mode 100644 index ca015bcb4..000000000 --- a/pkg/hanzoai/types/team/callback_add_params.py +++ /dev/null @@ -1,25 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import Dict, Optional -from typing_extensions import Literal, Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["CallbackAddParams"] - - -class CallbackAddParams(TypedDict, total=False): - callback_name: Required[str] - - callback_vars: Required[Dict[str, str]] - - callback_type: Optional[Literal["success", "failure", "success_and_failure"]] - - hanzo_changed_by: Annotated[str, PropertyInfo(alias="hanzo-changed-by")] - """ - The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - """ diff --git a/pkg/hanzoai/types/team/model_add_params.py b/pkg/hanzoai/types/team/model_add_params.py deleted file mode 100644 index f5c72952f..000000000 --- a/pkg/hanzoai/types/team/model_add_params.py +++ /dev/null @@ -1,14 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List -from typing_extensions import Required, TypedDict - -__all__ = ["ModelAddParams"] - - -class ModelAddParams(TypedDict, total=False): - models: Required[List[str]] - - team_id: Required[str] diff --git a/pkg/hanzoai/types/team/model_remove_params.py b/pkg/hanzoai/types/team/model_remove_params.py deleted file mode 100644 index 378bba7de..000000000 --- a/pkg/hanzoai/types/team/model_remove_params.py +++ /dev/null @@ -1,14 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List -from typing_extensions import Required, TypedDict - -__all__ = ["ModelRemoveParams"] - - -class ModelRemoveParams(TypedDict, total=False): - models: Required[List[str]] - - team_id: Required[str] diff --git a/pkg/hanzoai/types/team_add_member_response.py b/pkg/hanzoai/types/team_add_member_response.py deleted file mode 100644 index f0cbdb4ac..000000000 --- a/pkg/hanzoai/types/team_add_member_response.py +++ /dev/null @@ -1,69 +0,0 @@ -# Hanzo AI SDK - -from typing import List, Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from .member import Member -from .._models import BaseModel -from .budget_table import LlmBudgetTable as BudgetTable -from .lite_llm_user_table import HanzoUserTable -from .lite_llm_model_table import HanzoModelTable - -__all__ = ["TeamAddMemberResponse", "UpdatedTeamMembership"] - - -class UpdatedTeamMembership(BaseModel): - budget_id: str - - hanzo_budget_table: Optional[BudgetTable] = None - """Represents user-controllable params for a Hanzo_BudgetTable record""" - - team_id: str - - user_id: str - - -class TeamAddMemberResponse(BaseModel): - team_id: str - - updated_team_memberships: List[UpdatedTeamMembership] - - updated_users: List[HanzoUserTable] - - admins: Optional[List[object]] = None - - blocked: Optional[bool] = None - - budget_duration: Optional[str] = None - - budget_reset_at: Optional[datetime] = None - - created_at: Optional[datetime] = None - - hanzo_model_table: Optional[HanzoModelTable] = None - - max_budget: Optional[float] = None - - max_parallel_requests: Optional[int] = None - - members: Optional[List[object]] = None - - members_with_roles: Optional[List[Member]] = None - - metadata: Optional[object] = None - - api_model_id: Optional[int] = FieldInfo(alias="model_id", default=None) - - models: Optional[List[object]] = None - - organization_id: Optional[str] = None - - rpm_limit: Optional[int] = None - - spend: Optional[float] = None - - team_alias: Optional[str] = None - - tpm_limit: Optional[int] = None diff --git a/pkg/hanzoai/types/team_block_params.py b/pkg/hanzoai/types/team_block_params.py deleted file mode 100644 index 7ffeb3ba9..000000000 --- a/pkg/hanzoai/types/team_block_params.py +++ /dev/null @@ -1,11 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["TeamBlockParams"] - - -class TeamBlockParams(TypedDict, total=False): - team_id: Required[str] diff --git a/pkg/hanzoai/types/team_create_params.py b/pkg/hanzoai/types/team_create_params.py deleted file mode 100644 index 687b92eb7..000000000 --- a/pkg/hanzoai/types/team_create_params.py +++ /dev/null @@ -1,52 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List, Iterable, Optional -from typing_extensions import Annotated, TypedDict - -from .._utils import PropertyInfo -from .member_param import MemberParam - -__all__ = ["TeamCreateParams"] - - -class TeamCreateParams(TypedDict, total=False): - admins: Iterable[object] - - blocked: bool - - budget_duration: Optional[str] - - guardrails: Optional[List[str]] - - max_budget: Optional[float] - - members: Iterable[object] - - members_with_roles: Iterable[MemberParam] - - metadata: Optional[object] - - model_aliases: Optional[object] - - models: Iterable[object] - - organization_id: Optional[str] - - rpm_limit: Optional[int] - - tags: Optional[Iterable[object]] - - team_alias: Optional[str] - - team_id: Optional[str] - - tpm_limit: Optional[int] - - hanzo_changed_by: Annotated[str, PropertyInfo(alias="hanzo-changed-by")] - """ - The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - """ diff --git a/pkg/hanzoai/types/team_delete_params.py b/pkg/hanzoai/types/team_delete_params.py deleted file mode 100644 index 180e9f7b0..000000000 --- a/pkg/hanzoai/types/team_delete_params.py +++ /dev/null @@ -1,21 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["TeamDeleteParams"] - - -class TeamDeleteParams(TypedDict, total=False): - team_ids: Required[List[str]] - - hanzo_changed_by: Annotated[str, PropertyInfo(alias="hanzo-changed-by")] - """ - The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - """ diff --git a/pkg/hanzoai/types/team_list_available_params.py b/pkg/hanzoai/types/team_list_available_params.py deleted file mode 100644 index 8211f8fc5..000000000 --- a/pkg/hanzoai/types/team_list_available_params.py +++ /dev/null @@ -1,11 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["TeamListAvailableParams"] - - -class TeamListAvailableParams(TypedDict, total=False): - response_model: object diff --git a/pkg/hanzoai/types/team_retrieve_info_params.py b/pkg/hanzoai/types/team_retrieve_info_params.py deleted file mode 100644 index f0a2af74c..000000000 --- a/pkg/hanzoai/types/team_retrieve_info_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["TeamRetrieveInfoParams"] - - -class TeamRetrieveInfoParams(TypedDict, total=False): - team_id: str - """Team ID in the request parameters""" diff --git a/pkg/hanzoai/types/team_unblock_params.py b/pkg/hanzoai/types/team_unblock_params.py deleted file mode 100644 index 75127db79..000000000 --- a/pkg/hanzoai/types/team_unblock_params.py +++ /dev/null @@ -1,11 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["TeamUnblockParams"] - - -class TeamUnblockParams(TypedDict, total=False): - team_id: Required[str] diff --git a/pkg/hanzoai/types/team_update_params.py b/pkg/hanzoai/types/team_update_params.py deleted file mode 100644 index ba02439d7..000000000 --- a/pkg/hanzoai/types/team_update_params.py +++ /dev/null @@ -1,45 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List, Iterable, Optional -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["TeamUpdateParams"] - - -class TeamUpdateParams(TypedDict, total=False): - team_id: Required[str] - - blocked: Optional[bool] - - budget_duration: Optional[str] - - guardrails: Optional[List[str]] - - max_budget: Optional[float] - - metadata: Optional[object] - - model_aliases: Optional[object] - - models: Optional[Iterable[object]] - - organization_id: Optional[str] - - rpm_limit: Optional[int] - - tags: Optional[Iterable[object]] - - team_alias: Optional[str] - - tpm_limit: Optional[int] - - hanzo_changed_by: Annotated[str, PropertyInfo(alias="hanzo-changed-by")] - """ - The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - """ diff --git a/pkg/hanzoai/types/threads/__init__.py b/pkg/hanzoai/types/threads/__init__.py deleted file mode 100644 index 78708ffaf..000000000 --- a/pkg/hanzoai/types/threads/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations diff --git a/pkg/hanzoai/types/user_create_response.py b/pkg/hanzoai/types/user_create_response.py deleted file mode 100644 index 4f485a16d..000000000 --- a/pkg/hanzoai/types/user_create_response.py +++ /dev/null @@ -1,95 +0,0 @@ -# Hanzo AI SDK - -from typing import List, Optional -from datetime import datetime -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["UserCreateResponse"] - - -class UserCreateResponse(BaseModel): - expires: Optional[datetime] = None - - key: str - - token: Optional[str] = None - - aliases: Optional[object] = None - - allowed_cache_controls: Optional[List[object]] = None - - blocked: Optional[bool] = None - - budget_duration: Optional[str] = None - - budget_id: Optional[str] = None - - config: Optional[object] = None - - created_by: Optional[str] = None - - duration: Optional[str] = None - - enforced_params: Optional[List[str]] = None - - guardrails: Optional[List[str]] = None - - key_alias: Optional[str] = None - - key_name: Optional[str] = None - - hanzo_budget_table: Optional[object] = None - - max_budget: Optional[float] = None - - max_parallel_requests: Optional[int] = None - - metadata: Optional[object] = None - - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) - - api_model_rpm_limit: Optional[object] = FieldInfo( - alias="model_rpm_limit", default=None - ) - - api_model_tpm_limit: Optional[object] = FieldInfo( - alias="model_tpm_limit", default=None - ) - - models: Optional[List[object]] = None - - permissions: Optional[object] = None - - rpm_limit: Optional[int] = None - - spend: Optional[float] = None - - tags: Optional[List[str]] = None - - team_id: Optional[str] = None - - teams: Optional[List[object]] = None - - token_id: Optional[str] = None - - tpm_limit: Optional[int] = None - - updated_by: Optional[str] = None - - user_alias: Optional[str] = None - - user_email: Optional[str] = None - - user_id: Optional[str] = None - - user_role: Optional[ - Literal[ - "proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer" - ] - ] = None diff --git a/pkg/hanzoai/types/user_delete_params.py b/pkg/hanzoai/types/user_delete_params.py deleted file mode 100644 index 83a7ff956..000000000 --- a/pkg/hanzoai/types/user_delete_params.py +++ /dev/null @@ -1,21 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["UserDeleteParams"] - - -class UserDeleteParams(TypedDict, total=False): - user_ids: Required[List[str]] - - hanzo_changed_by: Annotated[str, PropertyInfo(alias="hanzo-changed-by")] - """ - The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability - """ diff --git a/pkg/hanzoai/types/user_roles.py b/pkg/hanzoai/types/user_roles.py deleted file mode 100644 index 22e79a15c..000000000 --- a/pkg/hanzoai/types/user_roles.py +++ /dev/null @@ -1,15 +0,0 @@ -# Hanzo AI SDK - -from typing_extensions import Literal, TypeAlias - -__all__ = ["UserRoles"] - -UserRoles: TypeAlias = Literal[ - "proxy_admin", - "proxy_admin_viewer", - "org_admin", - "internal_user", - "internal_user_viewer", - "team", - "customer", -] diff --git a/pkg/hanzoai/types/user_update_params.py b/pkg/hanzoai/types/user_update_params.py deleted file mode 100644 index 4b00d178e..000000000 --- a/pkg/hanzoai/types/user_update_params.py +++ /dev/null @@ -1,62 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing import List, Iterable, Optional -from typing_extensions import Literal, TypedDict - -__all__ = ["UserUpdateParams"] - - -class UserUpdateParams(TypedDict, total=False): - aliases: Optional[object] - - allowed_cache_controls: Optional[Iterable[object]] - - blocked: Optional[bool] - - budget_duration: Optional[str] - - config: Optional[object] - - duration: Optional[str] - - guardrails: Optional[List[str]] - - key_alias: Optional[str] - - max_budget: Optional[float] - - max_parallel_requests: Optional[int] - - metadata: Optional[object] - - model_max_budget: Optional[object] - - model_rpm_limit: Optional[object] - - model_tpm_limit: Optional[object] - - models: Optional[Iterable[object]] - - password: Optional[str] - - permissions: Optional[object] - - rpm_limit: Optional[int] - - spend: Optional[float] - - team_id: Optional[str] - - tpm_limit: Optional[int] - - user_email: Optional[str] - - user_id: Optional[str] - - user_role: Optional[ - Literal[ - "proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer" - ] - ] diff --git a/pkg/hanzoai/types/util_get_supported_openai_params_params.py b/pkg/hanzoai/types/util_get_supported_openai_params_params.py deleted file mode 100644 index 087791d8f..000000000 --- a/pkg/hanzoai/types/util_get_supported_openai_params_params.py +++ /dev/null @@ -1,11 +0,0 @@ -# Hanzo AI SDK - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["UtilGetSupportedOpenAIParamsParams"] - - -class UtilGetSupportedOpenAIParamsParams(TypedDict, total=False): - model: Required[str] diff --git a/proto/zap/README.md b/proto/zap/README.md deleted file mode 100644 index f3d5ebc7f..000000000 --- a/proto/zap/README.md +++ /dev/null @@ -1,164 +0,0 @@ -# ZAP Protocol: MCP Superset for Agentic Operations - -ZAP (Zero-latency Agent Protocol) extends MCP with capabilities for: -- **Interactive Sessions**: REPL/notebook execution across languages -- **Streaming**: Real-time output for long-running operations -- **IDE Integration**: Full editor control (VS Code, JetBrains, Neovim) -- **Browser DevTools**: Console, network, debugger control -- **Extension Bridge**: Browser/IDE extension communication - -## Protocol Versions - -| Version | MCP Compat | Features | -|---------|------------|----------| -| 1.0.0 | 2025-06-18 | Full MCP + REPL + Browser + IDE | - -## Wire Formats - -- **Primary**: Cap'n Proto (binary, zero-copy) -- **Fallback**: JSON-RPC 2.0 (MCP compatible) - -## Message Types - -### Tool Execution (MCP Compatible) -```json -{ - "id": "1", - "method": "tools/call", - "params": { - "name": "read", - "arguments": {"file_path": "/path/to/file"} - } -} -``` - -### REPL Session -```json -{ - "id": "1", - "method": "repl/eval", - "params": { - "session_id": "py_abc123", - "language": "python", - "code": "x = 1 + 1\nprint(x)", - "stream_output": true - } -} -``` - -### Browser Console -```json -{ - "id": "1", - "method": "browser/console", - "params": { - "action": "evaluate", - "code": "document.title" - } -} -``` - -### IDE Control -```json -{ - "id": "1", - "method": "ide/command", - "params": { - "action": "rename", - "line": 10, - "column": 5, - "new_name": "betterName" - } -} -``` - -### Streaming -```json -{ - "id": "1", - "method": "stream/start", - "params": { - "type": "repl_output", - "session_id": "py_abc123" - } -} -``` - -## Capabilities - -### REPL Languages -- Python (ipykernel) -- Node.js/TypeScript (tslab) -- Bash (bash_kernel) -- Ruby, Go, Rust, Julia (with kernels) - -### Browser Actions -- JavaScript evaluation -- Console message capture -- Network interception -- DOM manipulation -- Debugger control - -### IDE Actions -- File operations (open, save, close) -- Editor operations (select, insert, replace) -- Navigation (go to definition, find references) -- Refactoring (rename, format, extract) -- Terminal control -- Diagnostics and quick fixes - -## Extension Bridge - -ZAP communicates with browser/IDE extensions via WebSocket: - -| Extension | Port | Protocol | -|-----------|------|----------| -| Browser | 9224 | HTTP/WS | -| VS Code | 9225 | WebSocket | -| Cursor | 9226 | WebSocket | -| Windsurf | 9227 | WebSocket | -| Neovim | 9228 | WebSocket | - -## Proto Files - -- `zap.proto` - Main protocol definition -- Generated code available for: - - Python: `pip install hanzo-zap` - - TypeScript: `npm install @hanzo/zap` - - Go: `go get github.com/hanzoai/zap` - - Rust: `cargo add hanzo-zap` - -## Usage with hanzo-mcp - -ZAP is fully integrated into hanzo-mcp tools: - -```python -# REPL evaluation -repl(action="eval", code="print('hello')", language="python") - -# Browser console -browser(action="console") -browser(action="evaluate", code="document.title") - -# IDE control -ide(action="open", path="/src/main.py") -ide(action="rename", new_name="newFunc", line=10, column=5) -``` - -## Compared to MCP - -| Feature | MCP | ZAP | -|---------|-----|-----| -| Tool calls | โœ“ | โœ“ | -| Resources | โœ“ | โœ“ | -| Prompts | โœ“ | โœ“ | -| Streaming | โœ— | โœ“ | -| REPL sessions | โœ— | โœ“ | -| IDE integration | โœ— | โœ“ | -| Browser DevTools | โœ— | โœ“ | -| Binary protocol | โœ— | โœ“ (Cap'n Proto) | -| Extension bridge | โœ— | โœ“ | - -## License - -MIT - Hanzo Industries Inc diff --git a/proto/zap/extension-bridge.md b/proto/zap/extension-bridge.md deleted file mode 100644 index 972ae6545..000000000 --- a/proto/zap/extension-bridge.md +++ /dev/null @@ -1,258 +0,0 @@ -# ZAP Extension Bridge Protocol - -The ZAP Extension Bridge enables AI agents to control browsers and IDEs through native extensions. - -## Architecture - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” WebSocket โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ hanzo-mcp โ”‚ โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚ Browser Extension โ”‚ -โ”‚ (AI Agent) โ”‚ Port 9224 โ”‚ (Chrome/Firefox) โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ”‚ WebSocket - โ”‚ โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” - โ”‚ Port 9225 โ”‚ VS Code Extension โ”‚ - โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ”‚ WebSocket - โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” - Port 9228 โ”‚ Neovim Plugin โ”‚ - โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## Browser Extension - -### Installation -- Chrome: [Chrome Web Store - Hanzo AI](https://chrome.google.com/webstore/detail/hanzo-ai) -- Firefox: [Firefox Add-ons - Hanzo AI](https://addons.mozilla.org/firefox/addon/hanzo-ai) - -### Exposed APIs - -#### Console -```typescript -// Subscribe to console messages -{ action: "console.subscribe" } - -// Evaluate in console -{ action: "console.eval", code: "1 + 1" } - -// Get console history -{ action: "console.history", level?: "error" | "warn" | "log" } -``` - -#### DOM -```typescript -// Get accessibility snapshot -{ action: "dom.snapshot" } - -// Query selector -{ action: "dom.query", selector: ".button" } - -// Click element -{ action: "dom.click", selector: "#submit" } -``` - -#### Network -```typescript -// Subscribe to network events -{ action: "network.subscribe" } - -// Intercept requests -{ action: "network.intercept", pattern: "*/api/*" } - -// Mock response -{ action: "network.mock", url: "/api/user", response: { name: "Test" } } -``` - -#### DevTools -```typescript -// Set breakpoint -{ action: "debugger.breakpoint", url: "app.js", line: 42 } - -// Step through code -{ action: "debugger.step" } - -// Get call stack -{ action: "debugger.stack" } -``` - -## VS Code Extension - -### Installation -```bash -code --install-extension hanzo.hanzo-ai -``` - -### Exposed APIs - -#### Files -```typescript -// Open file -{ action: "file.open", path: "/src/main.ts", line?: 42 } - -// Save file -{ action: "file.save", path?: "/src/main.ts" } - -// Close file -{ action: "file.close", path?: "/src/main.ts" } - -// List open files -{ action: "file.list" } -``` - -#### Editor -```typescript -// Get selection -{ action: "editor.selection" } - -// Set selection -{ action: "editor.select", line: 10, column: 5, endLine: 10, endColumn: 15 } - -// Insert text -{ action: "editor.insert", text: "// TODO", line: 10 } - -// Replace text -{ action: "editor.replace", text: "newCode", line: 10, column: 0, endLine: 12, endColumn: 0 } -``` - -#### Navigation -```typescript -// Go to definition -{ action: "nav.definition", line: 10, column: 5 } - -// Find references -{ action: "nav.references", line: 10, column: 5 } - -// Go to symbol -{ action: "nav.symbol", query: "MyClass" } -``` - -#### Refactoring -```typescript -// Rename symbol -{ action: "refactor.rename", line: 10, column: 5, newName: "betterName" } - -// Format document -{ action: "refactor.format" } - -// Organize imports -{ action: "refactor.imports" } - -// Extract function -{ action: "refactor.extract", type: "function", line: 10, endLine: 20 } -``` - -#### Diagnostics -```typescript -// Get diagnostics -{ action: "diagnostics.get", severity?: "error" | "warning" } - -// Apply quick fix -{ action: "diagnostics.fix", line: 10, column: 5, index?: 0 } -``` - -#### Terminal -```typescript -// Create terminal -{ action: "terminal.create", name?: "Build" } - -// Send to terminal -{ action: "terminal.send", command: "npm test", name?: "Build" } - -// Get terminal output -{ action: "terminal.output", name?: "Build" } -``` - -#### Commands -```typescript -// Execute VS Code command -{ action: "command.execute", command: "workbench.action.togglePanel" } - -// List available commands -{ action: "command.list", filter?: "workbench" } -``` - -## Message Format - -All messages use JSON-RPC 2.0: - -### Request -```json -{ - "jsonrpc": "2.0", - "id": "unique-id", - "method": "action", - "params": { ... } -} -``` - -### Response -```json -{ - "jsonrpc": "2.0", - "id": "unique-id", - "result": { ... } -} -``` - -### Error -```json -{ - "jsonrpc": "2.0", - "id": "unique-id", - "error": { - "code": -32600, - "message": "Invalid Request" - } -} -``` - -### Event (no id) -```json -{ - "jsonrpc": "2.0", - "method": "event", - "params": { - "type": "console.message", - "data": { "level": "log", "text": "Hello" } - } -} -``` - -## Security - -- Extensions only accept connections from localhost -- Optional authentication via shared secret -- Sandboxed execution (no arbitrary code without user consent) -- Rate limiting on sensitive operations -- User must enable "AI Control" in extension settings - -## Development - -### Building Browser Extension -```bash -cd extensions/browser -npm install -npm run build -# Load unpacked extension from dist/ -``` - -### Building VS Code Extension -```bash -cd extensions/vscode -npm install -npm run build -code --install-extension hanzo-ai-*.vsix -``` - -### Testing -```bash -# Run integration tests -npm test - -# Manual testing -curl -X POST http://localhost:9224 \ - -H "Content-Type: application/json" \ - -d '{"action": "status"}' -``` diff --git a/proto/zap/zap.proto b/proto/zap/zap.proto deleted file mode 100644 index 1604b6aa9..000000000 --- a/proto/zap/zap.proto +++ /dev/null @@ -1,652 +0,0 @@ -// ZAP Protocol: MCP Superset for Agentic Operations -// Version: 1.0.0 -// -// ZAP extends MCP (Model Context Protocol) with: -// - Streaming capabilities for real-time output -// - Interactive REPL sessions across languages -// - IDE/Editor integration primitives -// - Browser DevTools integration -// - Extension messaging protocol -// -// Wire format: Cap'n Proto (primary), JSON-RPC (fallback) - -syntax = "proto3"; - -package zap.v1; - -option go_package = "github.com/hanzoai/zap/proto/zap/v1"; - -// ============================================================================ -// Core Message Types -// ============================================================================ - -// Base request envelope -message ZapRequest { - string id = 1; - string method = 2; - oneof params { - ToolCallParams tool_call = 10; - ReplParams repl = 11; - BrowserParams browser = 12; - IdeParams ide = 13; - ExtensionParams extension = 14; - StreamParams stream = 15; - } - map metadata = 100; -} - -// Base response envelope -message ZapResponse { - string id = 1; - oneof result { - ToolResult tool_result = 10; - ReplResult repl_result = 11; - BrowserResult browser_result = 12; - IdeResult ide_result = 13; - ExtensionResult extension_result = 14; - StreamChunk stream_chunk = 15; - ErrorResult error = 99; - } - map metadata = 100; -} - -message ErrorResult { - int32 code = 1; - string message = 2; - string data = 3; -} - -// ============================================================================ -// Tool Execution (MCP Compatible) -// ============================================================================ - -message ToolCallParams { - string name = 1; - map arguments = 2; -} - -message ToolResult { - bool success = 1; - repeated ContentBlock content = 2; - bool is_error = 3; -} - -message ContentBlock { - oneof content { - TextContent text = 1; - ImageContent image = 2; - ResourceContent resource = 3; - BinaryContent binary = 4; - } -} - -message TextContent { - string text = 1; - string mime_type = 2; // text/plain, text/markdown, application/json -} - -message ImageContent { - bytes data = 1; - string mime_type = 2; // image/png, image/jpeg - string alt_text = 3; -} - -message ResourceContent { - string uri = 1; - string mime_type = 2; - bytes data = 3; -} - -message BinaryContent { - bytes data = 1; - string mime_type = 2; -} - -// Generic value for arguments -message Value { - oneof value { - string string_value = 1; - int64 int_value = 2; - double double_value = 3; - bool bool_value = 4; - ListValue list_value = 5; - MapValue map_value = 6; - bytes bytes_value = 7; - } -} - -message ListValue { - repeated Value values = 1; -} - -message MapValue { - map fields = 1; -} - -// ============================================================================ -// REPL / Interactive Sessions -// ============================================================================ - -message ReplParams { - ReplAction action = 1; - string session_id = 2; - string language = 3; - string code = 4; - int32 timeout_ms = 5; - bool stream_output = 6; -} - -enum ReplAction { - REPL_ACTION_UNSPECIFIED = 0; - REPL_ACTION_START = 1; // Start new kernel session - REPL_ACTION_EVAL = 2; // Execute code - REPL_ACTION_STOP = 3; // Stop session - REPL_ACTION_INTERRUPT = 4; // Interrupt execution - REPL_ACTION_COMPLETE = 5; // Tab completion - REPL_ACTION_INSPECT = 6; // Inspect object - REPL_ACTION_HISTORY = 7; // Get history - REPL_ACTION_LIST = 8; // List sessions -} - -message ReplResult { - string session_id = 1; - bool success = 2; - int32 execution_count = 3; - string output = 4; - string error = 5; - repeated ReplOutput outputs = 6; - repeated CompletionItem completions = 7; - InspectResult inspect = 8; -} - -message ReplOutput { - ReplOutputType type = 1; - string text = 2; - bytes data = 3; - string mime_type = 4; -} - -enum ReplOutputType { - REPL_OUTPUT_UNSPECIFIED = 0; - REPL_OUTPUT_STDOUT = 1; - REPL_OUTPUT_STDERR = 2; - REPL_OUTPUT_RESULT = 3; - REPL_OUTPUT_DISPLAY = 4; - REPL_OUTPUT_ERROR = 5; -} - -message CompletionItem { - string text = 1; - string type = 2; // function, variable, module, etc. - string signature = 3; - string docstring = 4; -} - -message InspectResult { - string name = 1; - string type = 2; - string signature = 3; - string docstring = 4; - string source = 5; -} - -// ============================================================================ -// Browser / DevTools Integration -// ============================================================================ - -message BrowserParams { - BrowserAction action = 1; - string context_id = 2; - string page_id = 3; - - // Action-specific params - string url = 10; - string selector = 11; - string code = 12; // JavaScript to evaluate - string expression = 13; // Console expression - - // DevTools specific - bool enable_console = 20; - bool enable_network = 21; - bool enable_dom = 22; - bool enable_debugger = 23; -} - -enum BrowserAction { - BROWSER_ACTION_UNSPECIFIED = 0; - - // Navigation - BROWSER_ACTION_NAVIGATE = 1; - BROWSER_ACTION_RELOAD = 2; - BROWSER_ACTION_BACK = 3; - BROWSER_ACTION_FORWARD = 4; - - // DOM interaction - BROWSER_ACTION_CLICK = 10; - BROWSER_ACTION_TYPE = 11; - BROWSER_ACTION_SELECT = 12; - BROWSER_ACTION_SCROLL = 13; - - // JavaScript execution - BROWSER_ACTION_EVALUATE = 20; - BROWSER_ACTION_CONSOLE_EVAL = 21; - - // DevTools - BROWSER_ACTION_CONSOLE_SUBSCRIBE = 30; - BROWSER_ACTION_NETWORK_SUBSCRIBE = 31; - BROWSER_ACTION_DOM_SNAPSHOT = 32; - BROWSER_ACTION_SET_BREAKPOINT = 33; - BROWSER_ACTION_REMOVE_BREAKPOINT = 34; - BROWSER_ACTION_PAUSE = 35; - BROWSER_ACTION_RESUME = 36; - BROWSER_ACTION_STEP = 37; - - // Context management - BROWSER_ACTION_NEW_CONTEXT = 40; - BROWSER_ACTION_CLOSE_CONTEXT = 41; - BROWSER_ACTION_NEW_PAGE = 42; - BROWSER_ACTION_CLOSE_PAGE = 43; - BROWSER_ACTION_LIST_PAGES = 44; - - // Screenshots/capture - BROWSER_ACTION_SCREENSHOT = 50; - BROWSER_ACTION_PDF = 51; - BROWSER_ACTION_ACCESSIBILITY_SNAPSHOT = 52; -} - -message BrowserResult { - bool success = 1; - string page_id = 2; - string context_id = 3; - - oneof result { - EvaluateResult evaluate = 10; - ConsoleMessage console_message = 11; - NetworkEvent network_event = 12; - DomSnapshot dom_snapshot = 13; - DebuggerEvent debugger_event = 14; - bytes screenshot = 15; - PageInfo page_info = 16; - repeated PageInfo pages = 17; - } -} - -message EvaluateResult { - string type = 1; // undefined, string, number, object, etc. - string value = 2; // Serialized value - string preview = 3; // Short preview for objects - string class_name = 4; - string description = 5; -} - -message ConsoleMessage { - ConsoleLevel level = 1; - string text = 2; - string url = 3; - int32 line = 4; - int32 column = 5; - int64 timestamp = 6; - repeated string args = 7; -} - -enum ConsoleLevel { - CONSOLE_LEVEL_UNSPECIFIED = 0; - CONSOLE_LEVEL_LOG = 1; - CONSOLE_LEVEL_DEBUG = 2; - CONSOLE_LEVEL_INFO = 3; - CONSOLE_LEVEL_WARNING = 4; - CONSOLE_LEVEL_ERROR = 5; -} - -message NetworkEvent { - NetworkEventType type = 1; - string request_id = 2; - string url = 3; - string method = 4; - int32 status = 5; - map headers = 6; - bytes body = 7; - int64 timestamp = 8; -} - -enum NetworkEventType { - NETWORK_EVENT_UNSPECIFIED = 0; - NETWORK_EVENT_REQUEST = 1; - NETWORK_EVENT_RESPONSE = 2; - NETWORK_EVENT_FAILED = 3; -} - -message DomSnapshot { - string html = 1; - string accessibility_tree = 2; - repeated DomNode nodes = 3; -} - -message DomNode { - int32 node_id = 1; - string tag = 2; - map attributes = 3; - string text = 4; - repeated int32 children = 5; - BoundingBox bounds = 6; -} - -message BoundingBox { - float x = 1; - float y = 2; - float width = 3; - float height = 4; -} - -message DebuggerEvent { - DebuggerEventType type = 1; - string script_id = 2; - string url = 3; - int32 line = 4; - int32 column = 5; - repeated StackFrame call_frames = 6; - string reason = 7; -} - -enum DebuggerEventType { - DEBUGGER_EVENT_UNSPECIFIED = 0; - DEBUGGER_EVENT_PAUSED = 1; - DEBUGGER_EVENT_RESUMED = 2; - DEBUGGER_EVENT_SCRIPT_PARSED = 3; - DEBUGGER_EVENT_BREAKPOINT_HIT = 4; -} - -message StackFrame { - string function_name = 1; - string script_id = 2; - string url = 3; - int32 line = 4; - int32 column = 5; - repeated Scope scopes = 6; -} - -message Scope { - string type = 1; // global, local, closure, etc. - map variables = 2; -} - -message PageInfo { - string page_id = 1; - string context_id = 2; - string url = 3; - string title = 4; -} - -// ============================================================================ -// IDE / Editor Integration -// ============================================================================ - -message IdeParams { - IdeAction action = 1; - string workspace_path = 2; - string file_path = 3; - - // Position - int32 line = 10; - int32 column = 11; - int32 end_line = 12; - int32 end_column = 13; - - // Content - string text = 20; - string new_name = 21; - - // Diagnostics - DiagnosticSeverity min_severity = 30; -} - -enum IdeAction { - IDE_ACTION_UNSPECIFIED = 0; - - // File operations - IDE_ACTION_OPEN_FILE = 1; - IDE_ACTION_CLOSE_FILE = 2; - IDE_ACTION_SAVE_FILE = 3; - IDE_ACTION_GET_ACTIVE_FILE = 4; - IDE_ACTION_LIST_OPEN_FILES = 5; - - // Editor operations - IDE_ACTION_GET_SELECTION = 10; - IDE_ACTION_SET_SELECTION = 11; - IDE_ACTION_INSERT_TEXT = 12; - IDE_ACTION_REPLACE_TEXT = 13; - IDE_ACTION_GET_LINE = 14; - IDE_ACTION_GET_RANGE = 15; - - // Navigation - IDE_ACTION_GO_TO_DEFINITION = 20; - IDE_ACTION_FIND_REFERENCES = 21; - IDE_ACTION_GO_TO_LINE = 22; - IDE_ACTION_REVEAL_RANGE = 23; - - // Refactoring - IDE_ACTION_RENAME = 30; - IDE_ACTION_FORMAT_DOCUMENT = 31; - IDE_ACTION_ORGANIZE_IMPORTS = 32; - IDE_ACTION_EXTRACT_FUNCTION = 33; - IDE_ACTION_EXTRACT_VARIABLE = 34; - - // Diagnostics - IDE_ACTION_GET_DIAGNOSTICS = 40; - IDE_ACTION_QUICK_FIX = 41; - - // Commands - IDE_ACTION_EXECUTE_COMMAND = 50; - IDE_ACTION_LIST_COMMANDS = 51; - - // Workspace - IDE_ACTION_GET_WORKSPACE_FOLDERS = 60; - IDE_ACTION_FIND_FILES = 61; - IDE_ACTION_SEARCH_TEXT = 62; - - // Terminal - IDE_ACTION_CREATE_TERMINAL = 70; - IDE_ACTION_SEND_TO_TERMINAL = 71; - IDE_ACTION_GET_TERMINAL_OUTPUT = 72; -} - -message IdeResult { - bool success = 1; - - oneof result { - FileInfo file_info = 10; - repeated FileInfo files = 11; - TextRange selection = 12; - string text = 13; - repeated Location locations = 14; - repeated Diagnostic diagnostics = 15; - repeated CodeAction code_actions = 16; - repeated string commands = 17; - WorkspaceInfo workspace = 18; - TerminalInfo terminal = 19; - } -} - -message FileInfo { - string path = 1; - string uri = 2; - string language_id = 3; - bool is_dirty = 4; - int32 line_count = 5; -} - -message TextRange { - int32 start_line = 1; - int32 start_column = 2; - int32 end_line = 3; - int32 end_column = 4; - string text = 5; -} - -message Location { - string uri = 1; - TextRange range = 2; -} - -message Diagnostic { - DiagnosticSeverity severity = 1; - TextRange range = 2; - string message = 3; - string source = 4; - string code = 5; -} - -enum DiagnosticSeverity { - DIAGNOSTIC_SEVERITY_UNSPECIFIED = 0; - DIAGNOSTIC_SEVERITY_ERROR = 1; - DIAGNOSTIC_SEVERITY_WARNING = 2; - DIAGNOSTIC_SEVERITY_INFO = 3; - DIAGNOSTIC_SEVERITY_HINT = 4; -} - -message CodeAction { - string title = 1; - string kind = 2; // quickfix, refactor, source, etc. - bool is_preferred = 3; - repeated TextEdit edits = 4; - string command = 5; -} - -message TextEdit { - TextRange range = 1; - string new_text = 2; -} - -message WorkspaceInfo { - repeated WorkspaceFolder folders = 1; -} - -message WorkspaceFolder { - string uri = 1; - string name = 2; -} - -message TerminalInfo { - string id = 1; - string name = 2; - int32 pid = 3; -} - -// ============================================================================ -// Extension / Plugin Messaging -// ============================================================================ - -message ExtensionParams { - string extension_id = 1; - ExtensionAction action = 2; - string channel = 3; - bytes payload = 4; - map headers = 5; -} - -enum ExtensionAction { - EXTENSION_ACTION_UNSPECIFIED = 0; - EXTENSION_ACTION_SEND = 1; // Send message to extension - EXTENSION_ACTION_BROADCAST = 2; // Broadcast to all extensions - EXTENSION_ACTION_SUBSCRIBE = 3; // Subscribe to channel - EXTENSION_ACTION_UNSUBSCRIBE = 4; - EXTENSION_ACTION_LIST = 5; // List connected extensions - EXTENSION_ACTION_PING = 6; // Health check -} - -message ExtensionResult { - bool success = 1; - string extension_id = 2; - bytes payload = 3; - repeated ExtensionInfo extensions = 4; -} - -message ExtensionInfo { - string id = 1; - string name = 2; - string version = 3; - ExtensionType type = 4; - repeated string capabilities = 5; - bool connected = 6; -} - -enum ExtensionType { - EXTENSION_TYPE_UNSPECIFIED = 0; - EXTENSION_TYPE_BROWSER = 1; // Browser extension - EXTENSION_TYPE_IDE = 2; // IDE plugin (VS Code, JetBrains) - EXTENSION_TYPE_TERMINAL = 3; // Terminal integration - EXTENSION_TYPE_MOBILE = 4; // Mobile app - EXTENSION_TYPE_DESKTOP = 5; // Desktop app -} - -// ============================================================================ -// Streaming -// ============================================================================ - -message StreamParams { - StreamAction action = 1; - string stream_id = 2; - StreamType type = 3; - map filters = 4; -} - -enum StreamAction { - STREAM_ACTION_UNSPECIFIED = 0; - STREAM_ACTION_START = 1; - STREAM_ACTION_STOP = 2; - STREAM_ACTION_PAUSE = 3; - STREAM_ACTION_RESUME = 4; -} - -enum StreamType { - STREAM_TYPE_UNSPECIFIED = 0; - STREAM_TYPE_REPL_OUTPUT = 1; - STREAM_TYPE_CONSOLE_MESSAGES = 2; - STREAM_TYPE_NETWORK_EVENTS = 3; - STREAM_TYPE_DIAGNOSTICS = 4; - STREAM_TYPE_FILE_CHANGES = 5; - STREAM_TYPE_DEBUGGER = 6; -} - -message StreamChunk { - string stream_id = 1; - int64 sequence = 2; - int64 timestamp = 3; - - oneof data { - ReplOutput repl_output = 10; - ConsoleMessage console_message = 11; - NetworkEvent network_event = 12; - Diagnostic diagnostic = 13; - FileChangeEvent file_change = 14; - DebuggerEvent debugger_event = 15; - bytes raw = 99; - } -} - -message FileChangeEvent { - FileChangeType type = 1; - string uri = 2; -} - -enum FileChangeType { - FILE_CHANGE_UNSPECIFIED = 0; - FILE_CHANGE_CREATED = 1; - FILE_CHANGE_CHANGED = 2; - FILE_CHANGE_DELETED = 3; -} - -// ============================================================================ -// Service Definition -// ============================================================================ - -service ZapService { - // Unary RPCs (MCP compatible) - rpc Call(ZapRequest) returns (ZapResponse); - - // Server streaming (real-time output) - rpc Stream(StreamParams) returns (stream StreamChunk); - - // Bidirectional streaming (interactive sessions) - rpc Interactive(stream ZapRequest) returns (stream ZapResponse); -} diff --git a/pyproject.toml b/pyproject.toml index c0d41e444..d47ec9b8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,29 +1,28 @@ [project] name = "hanzoai" -version = "2.2.1" +version = "2.1.0" description = "The official Python library for the Hanzo API" dynamic = ["readme"] -license = "BSD-3-Clause" +license = "Apache-2.0" authors = [ { name = "Hanzo", email = "dev@hanzo.ai" }, ] dependencies = [ - # All runtime deps below are pure-Python โ€” fully compatible with free-threaded 3.13t. "httpx>=0.23.0, <1", - # Bump pydantic floor so we land on a pydantic-core (โ‰ฅ2.27) that ships cp313t wheels. - "pydantic>=2.10, <3", + "pydantic>=1.9.0, <3", "typing-extensions>=4.10, <5", "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", - # Security: pin minimum versions to address vulnerabilities - "h11>=0.16.0", - "urllib3>=2.6.0", ] -requires-python = ">=3.12" +requires-python = ">= 3.8" classifiers = [ "Typing :: Typed", "Intended Audience :: Developers", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Operating System :: OS Independent", @@ -32,51 +31,32 @@ classifiers = [ "Operating System :: POSIX :: Linux", "Operating System :: Microsoft :: Windows", "Topic :: Software Development :: Libraries :: Python Modules", - "License :: OSI Approved :: BSD License" + "License :: OSI Approved :: Apache Software License" ] -[project.optional-dependencies] -llm = ["hanzo-llm>=1.0.0"] - [project.urls] Homepage = "https://github.com/hanzoai/python-sdk" Repository = "https://github.com/hanzoai/python-sdk" +[project.optional-dependencies] +aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] + [tool.rye] managed = true # version pins are in requirements-dev.lock dev-dependencies = [ - "pyright>=1.1.359", + "pyright==1.1.399", "mypy", "respx", "pytest", - "pytest-asyncio>=0.26.0,<1.0.0", + "pytest-asyncio", "ruff", "time-machine", "nox", "dirty-equals>=0.6.0", "importlib-metadata>=6.7.0", "rich>=13.7.1", - "nest_asyncio==1.6.0", - # hanzo-memory test dependencies - "polars>=1.0.0", - "httpx>=0.27.0", - "fastapi>=0.115.0", - "fastembed>=0.4.0", -] - -[tool.rye.workspace] -members = [ - "pkg/hanzo", - "pkg/hanzo-aci", - "pkg/hanzo-mcp", - "pkg/hanzo-memory", - "pkg/hanzo-network", - "pkg/hanzo-dev-py", - "pkg/hanzo-s3", - "pkg/hanzo-tools-ui", - "pkg/hanzo-zap", - "pkg/hanzoai", + "pytest-xdist>=3.6.1", ] [tool.rye.scripts] @@ -106,41 +86,19 @@ typecheck = { chain = [ ]} "typecheck:pyright" = "pyright" "typecheck:verify-types" = "pyright --verifytypes hanzoai --ignoreexternal" -"typecheck:mypy" = "mypy pkg/hanzoai" +"typecheck:mypy" = "mypy ." [build-system] requires = ["hatchling==1.26.3", "hatch-fancy-pypi-readme"] build-backend = "hatchling.build" -[dependency-groups] -dev = [ - "build>=1.2.2.post1", - "dirty-equals>=0.6.0", - "fastapi>=0.128.0", - "fastembed>=0.7.4", - "hanzo-memory", - "httpx>=0.28.1", - "importlib-metadata>=6.7.0", - "mypy", - "nest-asyncio==1.6.0", - "nox", - "polars>=1.36.1", - "pyright>=1.1.359", - "pytest>=8.4.2", - "pytest-asyncio>=0.26.0,<1.0.0", - "respx", - "rich>=13.7.1", - "ruff", - "time-machine", -] - [tool.hatch.build] include = [ - "pkg/hanzoai" + "src/*" ] [tool.hatch.build.targets.wheel] -packages = ["pkg/hanzoai"] +packages = ["src/hanzoai"] [tool.hatch.build.targets.sdist] # Basically everything except hidden files/directories (such as .github, .devcontainers, .python-version, etc) @@ -153,7 +111,7 @@ include = [ "/noxfile.py", "bin/*", "examples/*", - "pkg/hanzoai/*", + "src/*", "tests/*", ] @@ -170,118 +128,90 @@ replacement = '[\1](https://github.com/hanzoai/python-sdk/tree/main/\g<2>)' [tool.pytest.ini_options] testpaths = ["tests"] -addopts = "--tb=short" +addopts = "--tb=short -n auto" xfail_strict = true asyncio_mode = "auto" -asyncio_default_fixture_loop_scope = "function" -markers = [ - "integration: marks tests as integration tests (deselect with '-m \"not integration\"')" -] +asyncio_default_fixture_loop_scope = "session" filterwarnings = [ - "error", - "ignore:OpenTelemetry configuration.*is not supported by Datadog:UserWarning", - "ignore::DeprecationWarning:ddtrace.*", - "ignore:Support for class-based `config` is deprecated:pydantic.warnings.PydanticDeprecatedSince20", - "ignore:hanzo-tools-core is deprecated:DeprecationWarning" + "error" ] [tool.pyright] -# Use basic mode โ€” strict mode with 22+ disabled checks is contradictory. -# SDK has many optional dependencies and generated code that cannot be -# fully type-checked in strict mode. -typeCheckingMode = "basic" -pythonVersion = "3.12" +# this enables practically every flag given by pyright. +# there are a couple of flags that are still disabled by +# default in strict mode as they are experimental and niche. +typeCheckingMode = "strict" +pythonVersion = "3.8" exclude = [ "_dev", ".venv", ".nox", - # All subpackages have their own linting/type checking - "pkg/hanzo", - "pkg/hanzo-aci", - "pkg/hanzo-agent", - "pkg/hanzo-agents", - "pkg/hanzo-async", - "pkg/hanzo-cli", - "pkg/hanzo-consensus", - "pkg/hanzo-iam", - "pkg/hanzo-kms", - "pkg/hanzo-mcp", - "pkg/hanzo-memory", - "pkg/hanzo-network", - "pkg/hanzo-node", - "pkg/hanzo-dev-py", - "pkg/hanzo-tools", - "pkg/hanzo-tools-agent", - "pkg/hanzo-tools-api", - "pkg/hanzo-tools-browser", - "pkg/hanzo-tools-code", - "pkg/hanzo-tools-computer", - "pkg/hanzo-tools-config", - "pkg/hanzo-tools-core", - "pkg/hanzo-tools-database", - "pkg/hanzo-tools-editor", - "pkg/hanzo-tools-fs", - "pkg/hanzo-tools-ide", - "pkg/hanzo-tools-jupyter", - "pkg/hanzo-tools-llm", - "pkg/hanzo-tools-lsp", - "pkg/hanzo-tools-mcp", - "pkg/hanzo-tools-memory", - "pkg/hanzo-tools-net", - "pkg/hanzo-tools-plan", - "pkg/hanzo-tools-reasoning", - "pkg/hanzo-tools-refactor", - "pkg/hanzo-tools-repl", - "pkg/hanzo-tools-shell", - "pkg/hanzo-tools-test", - "pkg/hanzo-tools-todo", - "pkg/hanzo-tools-ui", - "pkg/hanzo-tools-vcs", - "pkg/hanzo-tools-vector", - "pkg/hanzo-web3", - # Non-production code - "bin", - "examples", - "tests", - "scripts", + ".git", ] +reportImplicitOverride = true +reportOverlappingOverload = false + reportImportCycles = false reportPrivateUsage = false -# Relax strict checks for this SDK with many optional dependencies -reportMissingTypeStubs = false -reportUnknownVariableType = false -reportUnknownMemberType = false -reportUnknownParameterType = false -reportUnknownArgumentType = false -reportMissingParameterType = false -reportUnusedImport = "warning" -reportIncompatibleMethodOverride = false -reportMissingImports = false -reportConstantRedefinition = false -reportMissingTypeArgument = false -reportArgumentType = false -reportReturnType = false -reportIndexIssue = false -reportUnnecessaryContains = false -reportAttributeAccessIssue = false -reportCallIssue = false -reportOptionalMemberAccess = false -reportGeneralTypeIssues = false -reportImplicitOverride = false -reportAssignmentType = false -reportOptionalIterable = false +[tool.mypy] +pretty = true +show_error_codes = true + +# Exclude _files.py because mypy isn't smart enough to apply +# the correct type narrowing and as this is an internal module +# it's fine to just use Pyright. +# +# We also exclude our `tests` as mypy doesn't always infer +# types correctly and Pyright will still catch any type errors. +exclude = ['src/hanzoai/_files.py', '_dev/.*.py', 'tests/.*'] + +strict_equality = true +implicit_reexport = true +check_untyped_defs = true +no_implicit_optional = true + +warn_return_any = true +warn_unreachable = true +warn_unused_configs = true + +# Turn these options off as it could cause conflicts +# with the Pyright options. +warn_unused_ignores = false +warn_redundant_casts = false + +disallow_any_generics = true +disallow_untyped_defs = true +disallow_untyped_calls = true +disallow_subclassing_any = true +disallow_incomplete_defs = true +disallow_untyped_decorators = true +cache_fine_grained = true + +# By default, mypy reports an error if you assign a value to the result +# of a function call that doesn't return anything. We do this in our test +# cases: +# ``` +# result = ... +# assert result is None +# ``` +# Changing this codegen to make mypy happy would increase complexity +# and would not be worth it. +disable_error_code = "func-returns-value,overload-cannot-match" + +# https://github.com/python/mypy/issues/12162 +[[tool.mypy.overrides]] +module = "black.files.*" +ignore_errors = true +ignore_missing_imports = true + [tool.ruff] line-length = 120 output-format = "grouped" -target-version = "py312" -exclude = [ - "pkg/hanzo-agent", # submodule - linted separately - "pkg/hanzo-memory", # needs careful review - has complex redefinitions -] +target-version = "py38" [tool.ruff.format] docstring-code-format = true @@ -294,149 +224,23 @@ select = [ "B", # remove unused imports "F401", + # check for missing future annotations + "FA102", # bare except statements "E722", # unused arguments "ARG", + # print statements + "T201", + "T203", # misuse of typing.TYPE_CHECKING + "TC004", # import rules "TID251", - # docstring issues - "D", - # logging format - "G", - # security - "S", - # module imports - "E402", - # star imports - "F403", - # undefined names - "F821", ] ignore = [ # mutable defaults "B006", - # Unused function/method arguments - OK for protocol methods - "ARG001", - "ARG002", - "ARG003", - "ARG004", - "ARG005", - # Docstring formatting - "D205", - "D415", - # Line too long - handled by formatter - "E501", - # Ambiguous variable name - "E741", - # subprocess call - OK for CLI tools - "S603", - # Empty method in abstract base class - "B027", - # print statements - OK in examples and CLI tools - "T201", - "T203", - # Unused imports - we'll fix these manually - "F401", - # Undefined name - we'll fix these manually - "F821", - # raise without from inside except - "B904", - # logging format issues - OK for now - "G001", - "G002", - "G003", - # Unused loop control variable - "B007", - # subprocess without shell validation - OK for our use - "S605", - # try-except-pass - OK for optional imports - "S110", - # Missing docstrings - too many to fix now - "D100", - "D101", - "D102", - "D103", - "D104", - "D105", - "D106", - "D107", - # Other docstring issues - "D200", - "D201", - "D202", - "D203", - "D204", - "D206", - "D207", - "D208", - "D209", - "D210", - "D211", - "D212", - "D213", - "D214", - "D215", - "D300", - "D301", - "D400", - "D401", - "D402", - "D403", - "D404", - "D405", - "D406", - "D407", - "D408", - "D409", - "D410", - "D411", - "D412", - "D413", - "D414", - "D416", - "D417", - # Module level import not at top - "E402", - # Star imports - "F403", - # Loop variable binding - "B023", - # Use single if instead of nested - sometimes nested is clearer - "SIM102", - # Use of assert - OK in tests - "S101", - # Logging with f-string - performance is not critical - "G004", - # Starting process with partial path - OK for our use - "S607", - # Hardcoded temp directory - OK for tests - "S108", - # Popen without shell - OK - "S602", - # Starting process with shell - already handled - "S608", - # Use of random - OK for non-crypto use - "S311", - # Try-except-continue - OK - "S112", - # Insecure hash function - OK for non-crypto use - "S324", - # Request without timeout - OK for some cases - "S113", - # Hardcoded password - only in tests - "S106", - # os.system - OK for some CLI tools - "S606", - # eval - used safely in specific contexts - "S307", - # Hardcoded bind address - OK for examples - "S105", - # Use of exec - OK for specific use cases - "S102", - # Exception info in logging - OK - "G201", ] unfixable = [ # disable auto fix for print statements @@ -444,6 +248,8 @@ unfixable = [ "T203", ] +extend-safe-fixes = ["FA102"] + [tool.ruff.lint.flake8-tidy-imports.banned-api] "functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead" @@ -454,61 +260,8 @@ combine-as-imports = true extra-standard-library = ["typing_extensions"] known-first-party = ["hanzoai", "tests"] -[tool.mypy] -python_version = "3.12" -ignore_missing_imports = true -# Relax type checking - SDK has many optional dependencies and dynamic imports -check_untyped_defs = false -disallow_untyped_defs = false -disallow_untyped_calls = false -disallow_incomplete_defs = false -warn_return_any = false -warn_unused_ignores = false -exclude = [ - # Subpackages - have their own linting/type checking - "pkg/hanzo-agent/", - "pkg/hanzo-aci/", - "pkg/hanzo-mcp/", - "pkg/hanzo-memory/", - "pkg/hanzo-network/", - "pkg/hanzo-dev-py/", - "pkg/hanzo/", - # Non-production code - "bin/", - "examples/", - "scripts/", - "tests/", -] - -[tool.uv.workspace] -members = [ - "pkg/hanzo-memory", - "pkg/hanzo-s3", - "pkg/hanzo-tools-ui", -] - -[tool.uv.sources] -hanzo-memory = { workspace = true } -hanzo-tools-ui = { workspace = true } - -[tool.ruff.lint.extend-per-file-ignores] -# Allow print statements in scripts and examples +[tool.ruff.lint.per-file-ignores] "bin/**.py" = ["T201", "T203"] -"scripts/**.py" = ["T201", "T203", "E722"] -"tests/**.py" = ["T201", "T203", "F401", "F841"] -"examples/**.py" = ["T201", "T203", "F841"] -"**/examples/**.py" = ["T201", "T203", "F841"] -"pkg/**/examples/**.py" = ["T201", "T203", "F841"] -# CLI tools can use print -"**/cli*.py" = ["T201", "T203"] -"**/__main__.py" = ["T201", "T203"] -# Specific file ignores -"pkg/hanzo-memory/src/hanzo_memory/db/base.py" = ["B027"] -"pkg/hanzo-dev-py/src/hanzo_dev/backends.py" = ["S603"] -"pkg/hanzo-dev-py/src/hanzo_dev/repl.py" = ["S605"] -"pkg/hanzo-dev-py/src/hanzo_dev/tests.py" = ["ARG001"] -"pkg/hanzo-dev-py/src/hanzo_dev/ipython_repl.py" = ["ARG002"] -"**/cli.py" = ["T201", "T203"] -# Test files -"**/test_*.py" = ["F401", "F841"] -"**/tests/**.py" = ["F401", "F841"] +"scripts/**.py" = ["T201", "T203"] +"tests/**.py" = ["T201", "T203"] +"examples/**.py" = ["T201", "T203"] diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 000000000..16818d01d --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,66 @@ +{ + "packages": { + ".": {} + }, + "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", + "include-v-in-tag": true, + "include-component-in-tag": false, + "versioning": "prerelease", + "prerelease": true, + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "pull-request-header": "Automated Release PR", + "pull-request-title-pattern": "release: ${version}", + "changelog-sections": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "revert", + "section": "Reverts" + }, + { + "type": "chore", + "section": "Chores" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "style", + "section": "Styles" + }, + { + "type": "refactor", + "section": "Refactors" + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "build", + "section": "Build System" + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": true + } + ], + "release-type": "python", + "extra-files": [ + "src/hanzoai/_version.py" + ] +} \ No newline at end of file diff --git a/requirements-dev.lock b/requirements-dev.lock index d1b1490c5..e10407bf8 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -10,1697 +10,128 @@ # universal: false -e file:. - # via hanzo --e file:pkg/hanzo --e file:pkg/hanzo-aci - # via hanzo --e file:pkg/hanzo-mcp - # via hanzo - # via hanzo-repl --e file:pkg/hanzo-memory - # via hanzo - # via hanzo-tools-memory --e file:pkg/hanzo-network - # via hanzo --e file:pkg/hanzo-repl - # via hanzo -aiobotocore==3.1.1 - # via hanzo -aiofiles==25.1.0 - # via hanzo-async - # via hanzo-tools-api - # via meilisearch-python-sdk aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.13.3 - # via aiobotocore - # via hanzo-aci - # via hanzo-tools-browser - # via hanzo-tools-ide - # via litellm -aioitertools==0.13.0 - # via aiobotocore -aiosignal==1.4.0 +aiohttp==3.12.8 + # via hanzoai + # via httpx-aiohttp +aiosignal==1.3.2 # via aiohttp -alabaster==1.0.0 - # via sphinx -annotated-doc==0.0.4 - # via fastapi -annotated-types==0.7.0 +annotated-types==0.6.0 # via pydantic -anthropic==0.77.0 - # via hanzo -anyio==4.12.1 - # via anthropic +anyio==4.4.0 # via hanzoai # via httpx - # via mcp - # via openai - # via sse-starlette - # via starlette -appnope==0.1.4 - # via ipykernel -argcomplete==3.6.3 +argcomplete==3.1.2 # via nox -asttokens==3.0.1 - # via stack-data -attrs==25.4.0 +async-timeout==5.0.1 # via aiohttp - # via cyclopts - # via jsonschema - # via nox - # via referencing -authlib==1.6.6 - # via fastmcp -babel==2.18.0 - # via mkdocs-material - # via sphinx -backrefs==6.1 - # via mkdocs-material -beartype==0.22.9 - # via py-key-value-aio - # via py-key-value-shared -binaryornot==0.4.4 - # via hanzo-aci -black==26.1.0 - # via hanzo-mcp - # via hanzo-memory - # via hanzo-network - # via hanzo-repl -botocore==1.42.30 - # via aiobotocore - # via hanzo -build==1.4.0 - # via hanzo-mcp -cachetools==7.0.0 - # via hanzo-aci - # via py-key-value-aio -camel-converter==5.0.0 - # via meilisearch-python-sdk -certifi==2026.1.4 +attrs==25.3.0 + # via aiohttp +certifi==2023.7.22 # via httpcore # via httpx - # via requests -cffi==2.0.0 - # via cryptography - # via sounddevice -cfgv==3.5.0 - # via pre-commit -chardet==5.2.0 - # via binaryornot -charset-normalizer==3.4.4 - # via hanzo-aci - # via requests -click==8.3.1 - # via black - # via hanzo - # via hanzo-agents - # via hanzo-repl - # via litellm - # via mkdocs - # via typer - # via typer-slim - # via uvicorn -cloudpickle==3.1.2 - # via pydocket -colorama==0.4.6 - # via griffe - # via hanzo-repl - # via mkdocs-material -coloredlogs==15.0.1 - # via onnxruntime -colorlog==6.10.1 +colorlog==6.7.0 # via nox -comm==0.2.3 - # via ipykernel -coverage==7.13.2 - # via pytest-cov -croniter==6.0.0 - # via hanzo -cryptography==46.0.4 - # via authlib - # via pyjwt -cyclopts==4.5.1 - # via fastmcp -debugpy==1.8.20 - # via ipykernel -decorator==5.2.1 - # via ipdb - # via ipython -dependency-groups==1.3.1 - # via nox -dirty-equals==0.11 -diskcache==5.6.3 - # via py-key-value-aio -distlib==0.4.0 +dirty-equals==0.6.0 +distlib==0.3.7 # via virtualenv -distro==1.9.0 - # via anthropic +distro==1.8.0 # via hanzoai - # via openai -dnspython==2.8.0 - # via email-validator - # via pymongo -docstring-parser==0.17.0 - # via anthropic - # via cyclopts -docutils==0.22.4 - # via myst-parser - # via readme-renderer - # via rich-rst - # via sphinx - # via sphinx-rtd-theme -email-validator==2.3.0 - # via pydantic -exceptiongroup==1.3.1 - # via fastmcp -executing==2.2.1 - # via stack-data -factory-boy==3.3.3 - # via hanzo-memory -faker==40.1.2 - # via factory-boy - # via hanzo-memory -fakeredis==2.33.0 - # via pydocket -fastapi==0.128.0 -fastembed==0.7.4 - # via hanzo-mcp -fastmcp==2.14.4 - # via hanzo-mcp - # via hanzo-tools - # via hanzo-tools-agent - # via hanzo-tools-config - # via hanzo-tools-core - # via hanzo-tools-fs - # via hanzo-tools-llm - # via hanzo-tools-refactor -fastuuid==0.14.0 - # via litellm -ffind==1.6.1 - # via hanzo-tools-fs -filelock==3.20.3 - # via hanzo-aci - # via huggingface-hub +exceptiongroup==1.2.2 + # via anyio + # via pytest +execnet==2.1.1 + # via pytest-xdist +filelock==3.12.4 # via virtualenv -flake8==7.3.0 - # via hanzo-aci -flatbuffers==25.12.19 - # via onnxruntime -frozenlist==1.8.0 +frozenlist==1.6.2 # via aiohttp # via aiosignal -fsspec==2026.1.0 - # via huggingface-hub -ghp-import==2.1.0 - # via mkdocs -gitdb==4.0.12 - # via gitpython -gitpython==3.1.46 - # via hanzo-aci -grep-ast==0.9.0 - # via hanzo-aci - # via hanzo-tools-fs -griffe==1.15.0 - # via mkdocstrings-python -grpcio==1.76.0 - # via grpcio-tools - # via hanzo-network - # via qdrant-client -grpcio-tools==1.76.0 - # via hanzo-network h11==0.16.0 - # via hanzo-aci - # via hanzoai # via httpcore - # via uvicorn -h2==4.3.0 - # via httpx -hanzo-agents==0.1.2 - # via hanzo - # via hanzo-network -hanzo-async==0.1.1 - # via hanzo-mcp - # via hanzo-tools-agent - # via hanzo-tools-fs - # via hanzo-tools-shell -hanzo-persona==1.0.0 - # via hanzo-mcp -hanzo-tools==0.3.0 - # via hanzo-mcp - # via hanzo-tools-agent - # via hanzo-tools-browser - # via hanzo-tools-computer - # via hanzo-tools-shell -hanzo-tools-agent==0.3.1 - # via hanzo-agents - # via hanzo-mcp -hanzo-tools-api==0.3.1 - # via hanzo-mcp -hanzo-tools-browser==0.2.2 - # via hanzo-mcp -hanzo-tools-computer==0.5.2 - # via hanzo-mcp -hanzo-tools-config==0.2.0 - # via hanzo-mcp -hanzo-tools-core==0.2.0 - # via hanzo-tools-api - # via hanzo-tools-config - # via hanzo-tools-ide - # via hanzo-tools-llm - # via hanzo-tools-lsp - # via hanzo-tools-memory - # via hanzo-tools-reasoning - # via hanzo-tools-refactor - # via hanzo-tools-repl - # via hanzo-tools-todo -hanzo-tools-fs==0.3.1 - # via hanzo-mcp -hanzo-tools-ide==0.1.0 - # via hanzo-mcp -hanzo-tools-llm==0.2.0 - # via hanzo-mcp -hanzo-tools-lsp==0.2.0 - # via hanzo-mcp -hanzo-tools-memory==0.2.0 - # via hanzo-mcp -hanzo-tools-reasoning==0.2.0 - # via hanzo-mcp -hanzo-tools-refactor==0.2.0 - # via hanzo-mcp -hanzo-tools-repl==0.1.0 - # via hanzo-mcp -hanzo-tools-shell==0.6.1 - # via hanzo-mcp - # via hanzo-tools-agent -hanzo-tools-todo==0.2.0 - # via hanzo-mcp -hf-xet==1.2.0 - # via huggingface-hub -hpack==4.1.0 - # via h2 httpcore==1.0.9 # via httpx httpx==0.28.1 - # via anthropic - # via fastmcp - # via hanzo - # via hanzo-memory - # via hanzo-network - # via hanzo-tools-api # via hanzoai - # via huggingface-hub - # via litellm - # via mcp - # via meilisearch-python-sdk - # via openai - # via qdrant-client + # via httpx-aiohttp # via respx -httpx-sse==0.4.3 - # via mcp -huggingface-hub==1.3.7 - # via fastembed - # via tokenizers -humanfriendly==10.0 - # via coloredlogs -humanize==4.15.0 - # via nox -hyperframe==6.1.0 - # via h2 -id==1.5.0 - # via twine -identify==2.6.16 - # via pre-commit -idna==3.11 +httpx-aiohttp==0.1.9 + # via hanzoai +idna==3.4 # via anyio - # via email-validator # via httpx - # via requests # via yarl -imagesize==1.4.1 - # via sphinx -importlib-metadata==8.7.1 - # via litellm - # via opentelemetry-api -iniconfig==2.3.0 +importlib-metadata==7.0.0 +iniconfig==2.0.0 # via pytest -ipdb==0.13.13 - # via hanzo-memory -ipykernel==7.1.0 - # via hanzo-mcp -ipython==9.10.0 - # via hanzo-memory - # via hanzo-repl - # via ipdb - # via ipykernel -ipython-pygments-lexers==1.1.1 - # via ipython -jaraco-classes==3.4.0 - # via keyring -jaraco-context==6.1.0 - # via keyring -jaraco-functools==4.4.0 - # via keyring -jedi==0.19.2 - # via ipython -jinja2==3.1.6 - # via litellm - # via mkdocs - # via mkdocs-material - # via mkdocstrings - # via myst-parser - # via sphinx -jiter==0.13.0 - # via anthropic - # via openai -jmespath==1.1.0 - # via aiobotocore - # via botocore -jsonref==1.1.0 - # via fastmcp -jsonschema==4.26.0 - # via litellm - # via mcp -jsonschema-path==0.3.4 - # via fastmcp -jsonschema-specifications==2025.9.1 - # via jsonschema -jupyter-client==8.8.0 - # via hanzo-mcp - # via hanzo-tools-repl - # via ipykernel -jupyter-core==5.9.1 - # via hanzo-tools-repl - # via ipykernel - # via jupyter-client -keyring==25.7.0 - # via py-key-value-aio - # via twine -librt==0.7.8 - # via mypy -linkify-it-py==2.0.3 - # via markdown-it-py -litellm==1.81.6 - # via hanzo-aci - # via hanzo-repl - # via hanzoai -loguru==0.7.3 - # via fastembed -lupa==2.6 - # via fakeredis -markdown==3.10.1 - # via mkdocs - # via mkdocs-autorefs - # via mkdocs-material - # via mkdocstrings - # via pymdown-extensions -markdown-it-py==4.0.0 - # via mdit-py-plugins - # via myst-parser +markdown-it-py==3.0.0 # via rich - # via textual -markupsafe==3.0.3 - # via jinja2 - # via mkdocs - # via mkdocs-autorefs - # via mkdocstrings -matplotlib-inline==0.2.1 - # via ipykernel - # via ipython -mccabe==0.7.0 - # via flake8 -mcp==1.26.0 - # via fastmcp - # via hanzo-mcp - # via hanzo-memory - # via hanzo-tools - # via hanzo-tools-agent - # via hanzo-tools-browser - # via hanzo-tools-computer - # via hanzo-tools-config - # via hanzo-tools-core - # via hanzo-tools-fs - # via hanzo-tools-llm - # via hanzo-tools-lsp - # via hanzo-tools-memory - # via hanzo-tools-reasoning - # via hanzo-tools-refactor - # via hanzo-tools-shell - # via hanzo-tools-todo -mdit-py-plugins==0.5.0 - # via myst-parser - # via textual mdurl==0.1.2 # via markdown-it-py -meilisearch-python-sdk==6.1.0 - # via hanzo -mergedeep==1.3.4 - # via mkdocs - # via mkdocs-get-deps -mkdocs==1.6.1 - # via hanzo-memory - # via mkdocs-autorefs - # via mkdocs-material - # via mkdocstrings -mkdocs-autorefs==1.4.3 - # via mkdocstrings - # via mkdocstrings-python -mkdocs-get-deps==0.2.0 - # via mkdocs -mkdocs-material==9.7.1 - # via hanzo-memory -mkdocs-material-extensions==1.3.1 - # via mkdocs-material -mkdocstrings==1.0.2 - # via hanzo-memory - # via mkdocstrings-python -mkdocstrings-python==2.0.1 - # via mkdocstrings -mmh3==5.2.0 - # via fastembed -more-itertools==10.8.0 - # via jaraco-classes - # via jaraco-functools -motor==3.7.1 - # via hanzo -mouseinfo==0.1.3 - # via pyautogui -mpmath==1.3.0 - # via sympy -multidict==6.7.1 - # via aiobotocore +multidict==6.4.4 # via aiohttp # via yarl -mypy==1.19.1 - # via hanzo-mcp - # via hanzo-memory - # via hanzo-network - # via hanzo-repl -mypy-extensions==1.1.0 - # via black +mypy==1.14.1 +mypy-extensions==1.0.0 # via mypy -myst-parser==5.0.0 - # via hanzo-mcp -nats-py==2.12.0 - # via hanzo -nest-asyncio==1.6.0 - # via ipykernel -networkx==3.6.1 - # via hanzo-aci -nexus-rpc==1.3.0 - # via temporalio -nh3==0.3.2 - # via readme-renderer -nodeenv==1.10.0 - # via pre-commit +nodeenv==1.8.0 # via pyright -nox==2025.11.12 -numpy==2.4.2 - # via fastembed - # via hanzo-aci - # via hanzo-memory - # via hanzo-repl - # via hanzo-tools-computer - # via onnxruntime - # via pandas - # via qdrant-client - # via scipy -onnxruntime==1.23.2 - # via fastembed -openai==2.16.0 - # via hanzo - # via litellm -openapi-pydantic==0.5.1 - # via fastmcp -opentelemetry-api==1.39.1 - # via opentelemetry-exporter-prometheus - # via opentelemetry-instrumentation - # via opentelemetry-sdk - # via opentelemetry-semantic-conventions - # via pydocket -opentelemetry-exporter-prometheus==0.60b1 - # via pydocket -opentelemetry-instrumentation==0.60b1 - # via pydocket -opentelemetry-sdk==1.39.1 - # via opentelemetry-exporter-prometheus -opentelemetry-semantic-conventions==0.60b1 - # via opentelemetry-instrumentation - # via opentelemetry-sdk -orjson==3.11.7 - # via hanzo-mcp -packaging==26.0 - # via black - # via build - # via dependency-groups - # via fastmcp - # via huggingface-hub - # via ipykernel - # via mkdocs +nox==2023.4.22 +packaging==23.2 # via nox - # via onnxruntime - # via opentelemetry-instrumentation # via pytest - # via sphinx - # via twine -paginate==0.5.7 - # via mkdocs-material -pandas==3.0.0 - # via hanzo-aci -parso==0.8.5 - # via jedi -pathable==0.4.4 - # via jsonschema-path -pathspec==1.0.4 - # via black - # via grep-ast - # via mkdocs - # via mypy -pathvalidate==3.3.1 - # via py-key-value-aio -pexpect==4.9.0 - # via ipython -pillow==11.3.0 - # via fastembed - # via hanzo-tools-computer -platformdirs==4.5.1 - # via black - # via fastmcp - # via jupyter-core - # via mkdocs-get-deps - # via textual +platformdirs==3.11.0 # via virtualenv -pluggy==1.6.0 +pluggy==1.5.0 # via pytest - # via pytest-cov -polars==1.37.1 - # via hanzo-memory -polars-runtime-32==1.37.1 - # via polars -portalocker==3.2.0 - # via qdrant-client -pre-commit==4.5.1 - # via hanzo-aci - # via hanzo-memory -prometheus-client==0.24.1 - # via opentelemetry-exporter-prometheus - # via pydocket -prompt-toolkit==3.0.52 - # via hanzo - # via hanzo-repl - # via ipython -propcache==0.4.1 +propcache==0.3.1 # via aiohttp # via yarl -protobuf==6.33.5 - # via grpcio-tools - # via hanzo-network - # via onnxruntime - # via qdrant-client - # via temporalio -psutil==7.2.2 - # via hanzo-aci - # via hanzo-network - # via ipykernel -ptyprocess==0.7.0 - # via pexpect -pure-eval==0.2.3 - # via stack-data -py-key-value-aio==0.3.0 - # via fastmcp - # via pydocket -py-key-value-shared==0.3.0 - # via py-key-value-aio -py-rust-stemmers==0.1.5 - # via fastembed -pyautogui==0.9.54 - # via hanzo-tools-computer -pycodestyle==2.14.0 - # via flake8 -pycparser==3.0 - # via cffi -pydantic==2.12.5 - # via anthropic - # via camel-converter - # via fastapi - # via fastmcp - # via hanzo - # via hanzo-mcp - # via hanzo-memory - # via hanzo-network - # via hanzo-tools - # via hanzo-tools-agent - # via hanzo-tools-api - # via hanzo-tools-browser - # via hanzo-tools-computer - # via hanzo-tools-config - # via hanzo-tools-core - # via hanzo-tools-fs - # via hanzo-tools-llm - # via hanzo-tools-lsp - # via hanzo-tools-memory - # via hanzo-tools-reasoning - # via hanzo-tools-refactor - # via hanzo-tools-shell - # via hanzo-tools-todo +pydantic==2.11.9 # via hanzoai - # via litellm - # via mcp - # via meilisearch-python-sdk - # via openai - # via openapi-pydantic - # via pydantic-settings - # via qdrant-client -pydantic-core==2.41.5 +pydantic-core==2.33.2 # via pydantic -pydantic-settings==2.12.0 - # via hanzo-mcp - # via hanzo-memory - # via mcp -pydocket==0.16.6 - # via fastmcp -pyflakes==3.4.0 - # via flake8 -pygetwindow==0.0.9 - # via pyautogui -pygments==2.19.2 - # via hanzo-repl - # via ipython - # via ipython-pygments-lexers - # via mkdocs-material - # via pytest - # via readme-renderer +pygments==2.18.0 # via rich - # via sphinx - # via textual -pyjwt==2.11.0 - # via mcp - # via meilisearch-python-sdk -pymdown-extensions==10.20.1 - # via mkdocs-material - # via mkdocstrings -pymongo==4.16.0 - # via hanzo - # via motor -pymsgbox==2.0.1 - # via pyautogui -pyobjc==12.1 - # via pyttsx3 -pyobjc-core==12.1 - # via pyautogui - # via pyobjc - # via pyobjc-framework-accessibility - # via pyobjc-framework-accounts - # via pyobjc-framework-addressbook - # via pyobjc-framework-adservices - # via pyobjc-framework-adsupport - # via pyobjc-framework-applescriptkit - # via pyobjc-framework-applescriptobjc - # via pyobjc-framework-applicationservices - # via pyobjc-framework-apptrackingtransparency - # via pyobjc-framework-arkit - # via pyobjc-framework-audiovideobridging - # via pyobjc-framework-authenticationservices - # via pyobjc-framework-automaticassessmentconfiguration - # via pyobjc-framework-automator - # via pyobjc-framework-avfoundation - # via pyobjc-framework-avkit - # via pyobjc-framework-avrouting - # via pyobjc-framework-backgroundassets - # via pyobjc-framework-browserenginekit - # via pyobjc-framework-businesschat - # via pyobjc-framework-callkit - # via pyobjc-framework-carbon - # via pyobjc-framework-cfnetwork - # via pyobjc-framework-cinematic - # via pyobjc-framework-classkit - # via pyobjc-framework-cloudkit - # via pyobjc-framework-cocoa - # via pyobjc-framework-colorsync - # via pyobjc-framework-compositorservices - # via pyobjc-framework-contacts - # via pyobjc-framework-contactsui - # via pyobjc-framework-coreaudio - # via pyobjc-framework-coreaudiokit - # via pyobjc-framework-corebluetooth - # via pyobjc-framework-coredata - # via pyobjc-framework-corehaptics - # via pyobjc-framework-corelocation - # via pyobjc-framework-coremedia - # via pyobjc-framework-coremediaio - # via pyobjc-framework-coremidi - # via pyobjc-framework-coreml - # via pyobjc-framework-coremotion - # via pyobjc-framework-coreservices - # via pyobjc-framework-corespotlight - # via pyobjc-framework-coretext - # via pyobjc-framework-corewlan - # via pyobjc-framework-cryptotokenkit - # via pyobjc-framework-datadetection - # via pyobjc-framework-devicecheck - # via pyobjc-framework-devicediscoveryextension - # via pyobjc-framework-discrecording - # via pyobjc-framework-discrecordingui - # via pyobjc-framework-diskarbitration - # via pyobjc-framework-dvdplayback - # via pyobjc-framework-eventkit - # via pyobjc-framework-exceptionhandling - # via pyobjc-framework-executionpolicy - # via pyobjc-framework-extensionkit - # via pyobjc-framework-externalaccessory - # via pyobjc-framework-fileprovider - # via pyobjc-framework-fileproviderui - # via pyobjc-framework-findersync - # via pyobjc-framework-fsevents - # via pyobjc-framework-fskit - # via pyobjc-framework-gamecenter - # via pyobjc-framework-gamecontroller - # via pyobjc-framework-gamekit - # via pyobjc-framework-gameplaykit - # via pyobjc-framework-gamesave - # via pyobjc-framework-healthkit - # via pyobjc-framework-imagecapturecore - # via pyobjc-framework-installerplugins - # via pyobjc-framework-intents - # via pyobjc-framework-intentsui - # via pyobjc-framework-iobluetooth - # via pyobjc-framework-iobluetoothui - # via pyobjc-framework-iosurface - # via pyobjc-framework-ituneslibrary - # via pyobjc-framework-kernelmanagement - # via pyobjc-framework-latentsemanticmapping - # via pyobjc-framework-launchservices - # via pyobjc-framework-libdispatch - # via pyobjc-framework-libxpc - # via pyobjc-framework-linkpresentation - # via pyobjc-framework-localauthentication - # via pyobjc-framework-localauthenticationembeddedui - # via pyobjc-framework-mailkit - # via pyobjc-framework-mapkit - # via pyobjc-framework-mediaaccessibility - # via pyobjc-framework-mediaextension - # via pyobjc-framework-medialibrary - # via pyobjc-framework-mediaplayer - # via pyobjc-framework-mediatoolbox - # via pyobjc-framework-metal - # via pyobjc-framework-metalfx - # via pyobjc-framework-metalkit - # via pyobjc-framework-metalperformanceshaders - # via pyobjc-framework-metalperformanceshadersgraph - # via pyobjc-framework-metrickit - # via pyobjc-framework-mlcompute - # via pyobjc-framework-modelio - # via pyobjc-framework-multipeerconnectivity - # via pyobjc-framework-naturallanguage - # via pyobjc-framework-netfs - # via pyobjc-framework-network - # via pyobjc-framework-networkextension - # via pyobjc-framework-notificationcenter - # via pyobjc-framework-opendirectory - # via pyobjc-framework-osakit - # via pyobjc-framework-oslog - # via pyobjc-framework-passkit - # via pyobjc-framework-pencilkit - # via pyobjc-framework-phase - # via pyobjc-framework-photos - # via pyobjc-framework-photosui - # via pyobjc-framework-preferencepanes - # via pyobjc-framework-pushkit - # via pyobjc-framework-quartz - # via pyobjc-framework-quicklookthumbnailing - # via pyobjc-framework-replaykit - # via pyobjc-framework-safariservices - # via pyobjc-framework-safetykit - # via pyobjc-framework-scenekit - # via pyobjc-framework-screencapturekit - # via pyobjc-framework-screensaver - # via pyobjc-framework-screentime - # via pyobjc-framework-searchkit - # via pyobjc-framework-security - # via pyobjc-framework-securityfoundation - # via pyobjc-framework-securityinterface - # via pyobjc-framework-securityui - # via pyobjc-framework-sensitivecontentanalysis - # via pyobjc-framework-servicemanagement - # via pyobjc-framework-sharedwithyou - # via pyobjc-framework-sharedwithyoucore - # via pyobjc-framework-shazamkit - # via pyobjc-framework-social - # via pyobjc-framework-soundanalysis - # via pyobjc-framework-speech - # via pyobjc-framework-spritekit - # via pyobjc-framework-storekit - # via pyobjc-framework-symbols - # via pyobjc-framework-syncservices - # via pyobjc-framework-systemconfiguration - # via pyobjc-framework-systemextensions - # via pyobjc-framework-threadnetwork - # via pyobjc-framework-uniformtypeidentifiers - # via pyobjc-framework-usernotifications - # via pyobjc-framework-usernotificationsui - # via pyobjc-framework-videosubscriberaccount - # via pyobjc-framework-videotoolbox - # via pyobjc-framework-virtualization - # via pyobjc-framework-vision - # via pyobjc-framework-webkit -pyobjc-framework-accessibility==12.1 - # via pyobjc -pyobjc-framework-accounts==12.1 - # via pyobjc - # via pyobjc-framework-cloudkit -pyobjc-framework-addressbook==12.1 - # via pyobjc -pyobjc-framework-adservices==12.1 - # via pyobjc -pyobjc-framework-adsupport==12.1 - # via pyobjc -pyobjc-framework-applescriptkit==12.1 - # via pyobjc -pyobjc-framework-applescriptobjc==12.1 - # via pyobjc -pyobjc-framework-applicationservices==12.1 - # via pyobjc -pyobjc-framework-apptrackingtransparency==12.1 - # via pyobjc -pyobjc-framework-arkit==12.1 - # via pyobjc -pyobjc-framework-audiovideobridging==12.1 - # via pyobjc -pyobjc-framework-authenticationservices==12.1 - # via pyobjc -pyobjc-framework-automaticassessmentconfiguration==12.1 - # via pyobjc -pyobjc-framework-automator==12.1 - # via pyobjc -pyobjc-framework-avfoundation==12.1 - # via pyobjc - # via pyobjc-framework-cinematic - # via pyobjc-framework-mediaextension - # via pyobjc-framework-mediaplayer - # via pyobjc-framework-phase -pyobjc-framework-avkit==12.1 - # via pyobjc -pyobjc-framework-avrouting==12.1 - # via pyobjc -pyobjc-framework-backgroundassets==12.1 - # via pyobjc -pyobjc-framework-browserenginekit==12.1 - # via pyobjc -pyobjc-framework-businesschat==12.1 - # via pyobjc -pyobjc-framework-callkit==12.1 - # via pyobjc -pyobjc-framework-carbon==12.1 - # via pyobjc -pyobjc-framework-cfnetwork==12.1 - # via pyobjc -pyobjc-framework-cinematic==12.1 - # via pyobjc -pyobjc-framework-classkit==12.1 - # via pyobjc -pyobjc-framework-cloudkit==12.1 - # via pyobjc -pyobjc-framework-cocoa==12.1 - # via pyobjc - # via pyobjc-framework-accessibility - # via pyobjc-framework-accounts - # via pyobjc-framework-addressbook - # via pyobjc-framework-adservices - # via pyobjc-framework-adsupport - # via pyobjc-framework-applescriptkit - # via pyobjc-framework-applescriptobjc - # via pyobjc-framework-applicationservices - # via pyobjc-framework-apptrackingtransparency - # via pyobjc-framework-arkit - # via pyobjc-framework-audiovideobridging - # via pyobjc-framework-authenticationservices - # via pyobjc-framework-automaticassessmentconfiguration - # via pyobjc-framework-automator - # via pyobjc-framework-avfoundation - # via pyobjc-framework-avkit - # via pyobjc-framework-avrouting - # via pyobjc-framework-backgroundassets - # via pyobjc-framework-browserenginekit - # via pyobjc-framework-businesschat - # via pyobjc-framework-callkit - # via pyobjc-framework-carbon - # via pyobjc-framework-cfnetwork - # via pyobjc-framework-cinematic - # via pyobjc-framework-classkit - # via pyobjc-framework-cloudkit - # via pyobjc-framework-colorsync - # via pyobjc-framework-compositorservices - # via pyobjc-framework-contacts - # via pyobjc-framework-contactsui - # via pyobjc-framework-coreaudio - # via pyobjc-framework-coreaudiokit - # via pyobjc-framework-corebluetooth - # via pyobjc-framework-coredata - # via pyobjc-framework-corehaptics - # via pyobjc-framework-corelocation - # via pyobjc-framework-coremedia - # via pyobjc-framework-coremediaio - # via pyobjc-framework-coremidi - # via pyobjc-framework-coreml - # via pyobjc-framework-coremotion - # via pyobjc-framework-coreservices - # via pyobjc-framework-corespotlight - # via pyobjc-framework-coretext - # via pyobjc-framework-corewlan - # via pyobjc-framework-cryptotokenkit - # via pyobjc-framework-datadetection - # via pyobjc-framework-devicecheck - # via pyobjc-framework-devicediscoveryextension - # via pyobjc-framework-discrecording - # via pyobjc-framework-discrecordingui - # via pyobjc-framework-diskarbitration - # via pyobjc-framework-dvdplayback - # via pyobjc-framework-eventkit - # via pyobjc-framework-exceptionhandling - # via pyobjc-framework-executionpolicy - # via pyobjc-framework-extensionkit - # via pyobjc-framework-externalaccessory - # via pyobjc-framework-fileprovider - # via pyobjc-framework-findersync - # via pyobjc-framework-fsevents - # via pyobjc-framework-fskit - # via pyobjc-framework-gamecenter - # via pyobjc-framework-gamecontroller - # via pyobjc-framework-gamekit - # via pyobjc-framework-gameplaykit - # via pyobjc-framework-gamesave - # via pyobjc-framework-healthkit - # via pyobjc-framework-imagecapturecore - # via pyobjc-framework-installerplugins - # via pyobjc-framework-intents - # via pyobjc-framework-iobluetooth - # via pyobjc-framework-iosurface - # via pyobjc-framework-ituneslibrary - # via pyobjc-framework-kernelmanagement - # via pyobjc-framework-latentsemanticmapping - # via pyobjc-framework-libdispatch - # via pyobjc-framework-libxpc - # via pyobjc-framework-linkpresentation - # via pyobjc-framework-localauthentication - # via pyobjc-framework-localauthenticationembeddedui - # via pyobjc-framework-mailkit - # via pyobjc-framework-mapkit - # via pyobjc-framework-mediaaccessibility - # via pyobjc-framework-mediaextension - # via pyobjc-framework-medialibrary - # via pyobjc-framework-mediatoolbox - # via pyobjc-framework-metal - # via pyobjc-framework-metalkit - # via pyobjc-framework-metrickit - # via pyobjc-framework-mlcompute - # via pyobjc-framework-modelio - # via pyobjc-framework-multipeerconnectivity - # via pyobjc-framework-naturallanguage - # via pyobjc-framework-netfs - # via pyobjc-framework-network - # via pyobjc-framework-networkextension - # via pyobjc-framework-notificationcenter - # via pyobjc-framework-opendirectory - # via pyobjc-framework-osakit - # via pyobjc-framework-oslog - # via pyobjc-framework-passkit - # via pyobjc-framework-pencilkit - # via pyobjc-framework-photos - # via pyobjc-framework-photosui - # via pyobjc-framework-preferencepanes - # via pyobjc-framework-pushkit - # via pyobjc-framework-quartz - # via pyobjc-framework-quicklookthumbnailing - # via pyobjc-framework-replaykit - # via pyobjc-framework-safariservices - # via pyobjc-framework-safetykit - # via pyobjc-framework-scenekit - # via pyobjc-framework-screencapturekit - # via pyobjc-framework-screensaver - # via pyobjc-framework-screentime - # via pyobjc-framework-security - # via pyobjc-framework-securityfoundation - # via pyobjc-framework-securityinterface - # via pyobjc-framework-securityui - # via pyobjc-framework-sensitivecontentanalysis - # via pyobjc-framework-servicemanagement - # via pyobjc-framework-sharedwithyoucore - # via pyobjc-framework-shazamkit - # via pyobjc-framework-social - # via pyobjc-framework-soundanalysis - # via pyobjc-framework-speech - # via pyobjc-framework-spritekit - # via pyobjc-framework-storekit - # via pyobjc-framework-symbols - # via pyobjc-framework-syncservices - # via pyobjc-framework-systemconfiguration - # via pyobjc-framework-systemextensions - # via pyobjc-framework-threadnetwork - # via pyobjc-framework-uniformtypeidentifiers - # via pyobjc-framework-usernotifications - # via pyobjc-framework-usernotificationsui - # via pyobjc-framework-videosubscriberaccount - # via pyobjc-framework-videotoolbox - # via pyobjc-framework-virtualization - # via pyobjc-framework-vision - # via pyobjc-framework-webkit -pyobjc-framework-colorsync==12.1 - # via pyobjc -pyobjc-framework-compositorservices==12.1 - # via pyobjc -pyobjc-framework-contacts==12.1 - # via pyobjc - # via pyobjc-framework-contactsui -pyobjc-framework-contactsui==12.1 - # via pyobjc -pyobjc-framework-coreaudio==12.1 - # via pyobjc - # via pyobjc-framework-avfoundation - # via pyobjc-framework-browserenginekit - # via pyobjc-framework-coreaudiokit -pyobjc-framework-coreaudiokit==12.1 - # via pyobjc -pyobjc-framework-corebluetooth==12.1 - # via pyobjc -pyobjc-framework-coredata==12.1 - # via pyobjc - # via pyobjc-framework-cloudkit - # via pyobjc-framework-syncservices -pyobjc-framework-corehaptics==12.1 - # via pyobjc -pyobjc-framework-corelocation==12.1 - # via pyobjc - # via pyobjc-framework-cloudkit - # via pyobjc-framework-mapkit -pyobjc-framework-coremedia==12.1 - # via pyobjc - # via pyobjc-framework-avfoundation - # via pyobjc-framework-browserenginekit - # via pyobjc-framework-cinematic - # via pyobjc-framework-mediaextension - # via pyobjc-framework-oslog - # via pyobjc-framework-screencapturekit - # via pyobjc-framework-videotoolbox -pyobjc-framework-coremediaio==12.1 - # via pyobjc -pyobjc-framework-coremidi==12.1 - # via pyobjc -pyobjc-framework-coreml==12.1 - # via pyobjc - # via pyobjc-framework-vision -pyobjc-framework-coremotion==12.1 - # via pyobjc -pyobjc-framework-coreservices==12.1 - # via pyobjc - # via pyobjc-framework-launchservices - # via pyobjc-framework-searchkit -pyobjc-framework-corespotlight==12.1 - # via pyobjc -pyobjc-framework-coretext==12.1 - # via pyobjc - # via pyobjc-framework-applicationservices -pyobjc-framework-corewlan==12.1 - # via pyobjc -pyobjc-framework-cryptotokenkit==12.1 - # via pyobjc -pyobjc-framework-datadetection==12.1 - # via pyobjc -pyobjc-framework-devicecheck==12.1 - # via pyobjc -pyobjc-framework-devicediscoveryextension==12.1 - # via pyobjc -pyobjc-framework-discrecording==12.1 - # via pyobjc - # via pyobjc-framework-discrecordingui -pyobjc-framework-discrecordingui==12.1 - # via pyobjc -pyobjc-framework-diskarbitration==12.1 - # via pyobjc -pyobjc-framework-dvdplayback==12.1 - # via pyobjc -pyobjc-framework-eventkit==12.1 - # via pyobjc -pyobjc-framework-exceptionhandling==12.1 - # via pyobjc -pyobjc-framework-executionpolicy==12.1 - # via pyobjc -pyobjc-framework-extensionkit==12.1 - # via pyobjc -pyobjc-framework-externalaccessory==12.1 - # via pyobjc -pyobjc-framework-fileprovider==12.1 - # via pyobjc - # via pyobjc-framework-fileproviderui -pyobjc-framework-fileproviderui==12.1 - # via pyobjc -pyobjc-framework-findersync==12.1 - # via pyobjc -pyobjc-framework-fsevents==12.1 - # via pyobjc-framework-coreservices -pyobjc-framework-fskit==12.1 - # via pyobjc -pyobjc-framework-gamecenter==12.1 - # via pyobjc -pyobjc-framework-gamecontroller==12.1 - # via pyobjc -pyobjc-framework-gamekit==12.1 - # via pyobjc -pyobjc-framework-gameplaykit==12.1 - # via pyobjc -pyobjc-framework-gamesave==12.1 - # via pyobjc -pyobjc-framework-healthkit==12.1 - # via pyobjc -pyobjc-framework-imagecapturecore==12.1 - # via pyobjc -pyobjc-framework-installerplugins==12.1 - # via pyobjc -pyobjc-framework-intents==12.1 - # via pyobjc - # via pyobjc-framework-intentsui -pyobjc-framework-intentsui==12.1 - # via pyobjc -pyobjc-framework-iobluetooth==12.1 - # via pyobjc - # via pyobjc-framework-iobluetoothui -pyobjc-framework-iobluetoothui==12.1 - # via pyobjc -pyobjc-framework-iosurface==12.1 - # via pyobjc -pyobjc-framework-ituneslibrary==12.1 - # via pyobjc -pyobjc-framework-kernelmanagement==12.1 - # via pyobjc -pyobjc-framework-latentsemanticmapping==12.1 - # via pyobjc -pyobjc-framework-launchservices==12.1 - # via pyobjc -pyobjc-framework-libdispatch==12.1 - # via pyobjc -pyobjc-framework-libxpc==12.1 - # via pyobjc -pyobjc-framework-linkpresentation==12.1 - # via pyobjc -pyobjc-framework-localauthentication==12.1 - # via pyobjc - # via pyobjc-framework-localauthenticationembeddedui -pyobjc-framework-localauthenticationembeddedui==12.1 - # via pyobjc -pyobjc-framework-mailkit==12.1 - # via pyobjc -pyobjc-framework-mapkit==12.1 - # via pyobjc -pyobjc-framework-mediaaccessibility==12.1 - # via pyobjc -pyobjc-framework-mediaextension==12.1 - # via pyobjc -pyobjc-framework-medialibrary==12.1 - # via pyobjc -pyobjc-framework-mediaplayer==12.1 - # via pyobjc -pyobjc-framework-mediatoolbox==12.1 - # via pyobjc -pyobjc-framework-metal==12.1 - # via pyobjc - # via pyobjc-framework-cinematic - # via pyobjc-framework-compositorservices - # via pyobjc-framework-metalfx - # via pyobjc-framework-metalkit - # via pyobjc-framework-metalperformanceshaders -pyobjc-framework-metalfx==12.1 - # via pyobjc -pyobjc-framework-metalkit==12.1 - # via pyobjc -pyobjc-framework-metalperformanceshaders==12.1 - # via pyobjc - # via pyobjc-framework-metalperformanceshadersgraph -pyobjc-framework-metalperformanceshadersgraph==12.1 - # via pyobjc -pyobjc-framework-metrickit==12.1 - # via pyobjc -pyobjc-framework-mlcompute==12.1 - # via pyobjc -pyobjc-framework-modelio==12.1 - # via pyobjc -pyobjc-framework-multipeerconnectivity==12.1 - # via pyobjc -pyobjc-framework-naturallanguage==12.1 - # via pyobjc -pyobjc-framework-netfs==12.1 - # via pyobjc -pyobjc-framework-network==12.1 - # via pyobjc -pyobjc-framework-networkextension==12.1 - # via pyobjc -pyobjc-framework-notificationcenter==12.1 - # via pyobjc -pyobjc-framework-opendirectory==12.1 - # via pyobjc -pyobjc-framework-osakit==12.1 - # via pyobjc -pyobjc-framework-oslog==12.1 - # via pyobjc -pyobjc-framework-passkit==12.1 - # via pyobjc -pyobjc-framework-pencilkit==12.1 - # via pyobjc -pyobjc-framework-phase==12.1 - # via pyobjc -pyobjc-framework-photos==12.1 - # via pyobjc -pyobjc-framework-photosui==12.1 - # via pyobjc -pyobjc-framework-preferencepanes==12.1 - # via pyobjc -pyobjc-framework-pushkit==12.1 - # via pyobjc -pyobjc-framework-quartz==12.1 - # via pyautogui - # via pyobjc - # via pyobjc-framework-accessibility - # via pyobjc-framework-applicationservices - # via pyobjc-framework-avfoundation - # via pyobjc-framework-avkit - # via pyobjc-framework-browserenginekit - # via pyobjc-framework-coretext - # via pyobjc-framework-gamekit - # via pyobjc-framework-linkpresentation - # via pyobjc-framework-mapkit - # via pyobjc-framework-medialibrary - # via pyobjc-framework-modelio - # via pyobjc-framework-oslog - # via pyobjc-framework-quicklookthumbnailing - # via pyobjc-framework-safetykit - # via pyobjc-framework-scenekit - # via pyobjc-framework-sensitivecontentanalysis - # via pyobjc-framework-spritekit - # via pyobjc-framework-videotoolbox - # via pyobjc-framework-vision -pyobjc-framework-quicklookthumbnailing==12.1 - # via pyobjc -pyobjc-framework-replaykit==12.1 - # via pyobjc -pyobjc-framework-safariservices==12.1 - # via pyobjc -pyobjc-framework-safetykit==12.1 - # via pyobjc -pyobjc-framework-scenekit==12.1 - # via pyobjc -pyobjc-framework-screencapturekit==12.1 - # via pyobjc -pyobjc-framework-screensaver==12.1 - # via pyobjc -pyobjc-framework-screentime==12.1 - # via pyobjc -pyobjc-framework-searchkit==12.1 - # via pyobjc -pyobjc-framework-security==12.1 - # via pyobjc - # via pyobjc-framework-localauthentication - # via pyobjc-framework-securityfoundation - # via pyobjc-framework-securityinterface - # via pyobjc-framework-securityui -pyobjc-framework-securityfoundation==12.1 - # via pyobjc -pyobjc-framework-securityinterface==12.1 - # via pyobjc -pyobjc-framework-securityui==12.1 - # via pyobjc -pyobjc-framework-sensitivecontentanalysis==12.1 - # via pyobjc -pyobjc-framework-servicemanagement==12.1 - # via pyobjc -pyobjc-framework-sharedwithyou==12.1 - # via pyobjc -pyobjc-framework-sharedwithyoucore==12.1 - # via pyobjc - # via pyobjc-framework-sharedwithyou -pyobjc-framework-shazamkit==12.1 - # via pyobjc -pyobjc-framework-social==12.1 - # via pyobjc -pyobjc-framework-soundanalysis==12.1 - # via pyobjc -pyobjc-framework-speech==12.1 - # via pyobjc -pyobjc-framework-spritekit==12.1 - # via pyobjc - # via pyobjc-framework-gameplaykit -pyobjc-framework-storekit==12.1 - # via pyobjc -pyobjc-framework-symbols==12.1 - # via pyobjc -pyobjc-framework-syncservices==12.1 - # via pyobjc -pyobjc-framework-systemconfiguration==12.1 - # via pyobjc -pyobjc-framework-systemextensions==12.1 - # via pyobjc -pyobjc-framework-threadnetwork==12.1 - # via pyobjc -pyobjc-framework-uniformtypeidentifiers==12.1 - # via pyobjc -pyobjc-framework-usernotifications==12.1 - # via pyobjc - # via pyobjc-framework-usernotificationsui -pyobjc-framework-usernotificationsui==12.1 - # via pyobjc -pyobjc-framework-videosubscriberaccount==12.1 - # via pyobjc -pyobjc-framework-videotoolbox==12.1 - # via pyobjc -pyobjc-framework-virtualization==12.1 - # via pyobjc -pyobjc-framework-vision==12.1 - # via pyobjc -pyobjc-framework-webkit==12.1 - # via pyobjc -pyperclip==1.11.0 - # via fastmcp - # via mouseinfo -pyproject-hooks==1.2.0 - # via build -pyrect==0.2.0 - # via pygetwindow -pyright==1.1.408 -pyscreeze==1.0.1 - # via pyautogui -pytest==9.0.2 - # via hanzo-aci - # via hanzo-mcp - # via hanzo-memory - # via hanzo-network - # via hanzo-repl +pyright==1.1.399 +pytest==8.3.3 # via pytest-asyncio - # via pytest-cov - # via pytest-mock -pytest-asyncio==1.3.0 - # via hanzo-mcp - # via hanzo-memory - # via hanzo-network - # via hanzo-repl -pytest-cov==7.0.0 - # via hanzo-mcp - # via hanzo-memory - # via hanzo-network - # via hanzo-repl -pytest-mock==3.15.1 - # via hanzo-mcp - # via hanzo-memory -python-dateutil==2.9.0.post0 - # via aiobotocore - # via botocore - # via croniter - # via ghp-import - # via jupyter-client - # via pandas -python-dotenv==1.2.1 - # via fastmcp - # via hanzo-repl - # via litellm - # via pydantic-settings -python-json-logger==4.0.0 - # via pydocket -python-multipart==0.0.22 - # via hanzo-memory - # via mcp -pytokens==0.4.1 - # via black -pyttsx3==2.99 - # via hanzo-repl -pytweening==1.2.0 - # via pyautogui -pytz==2025.2 - # via croniter -pyyaml==6.0.3 - # via hanzo - # via hanzo-tools-api - # via huggingface-hub - # via jsonschema-path - # via mkdocs - # via mkdocs-get-deps - # via myst-parser - # via pre-commit - # via pymdown-extensions - # via pyyaml-env-tag -pyyaml-env-tag==1.1 - # via mkdocs -pyzmq==27.1.0 - # via ipykernel - # via jupyter-client -qdrant-client==1.16.2 - # via hanzo -qrcode==8.2 - # via hanzo -readme-renderer==44.0 - # via twine -redis==7.1.0 - # via fakeredis - # via hanzo - # via py-key-value-aio - # via pydocket -referencing==0.36.2 - # via jsonschema - # via jsonschema-path - # via jsonschema-specifications -regex==2026.1.15 - # via tiktoken -requests==2.32.5 - # via fastembed - # via hanzo-aci - # via id - # via jsonschema-path - # via mkdocs-material - # via requests-toolbelt - # via sphinx - # via tiktoken - # via twine -requests-toolbelt==1.0.0 - # via twine + # via pytest-xdist +pytest-asyncio==0.24.0 +pytest-xdist==3.7.0 +python-dateutil==2.8.2 + # via time-machine +pytz==2023.3.post1 + # via dirty-equals respx==0.22.0 - # via hanzo-memory -rfc3986==2.0.0 - # via twine -rich==14.3.2 - # via cyclopts - # via fastmcp - # via hanzo - # via hanzo-agents - # via hanzo-memory - # via hanzo-network - # via hanzo-repl - # via pydocket - # via rich-rst - # via textual - # via twine - # via typer -rich-rst==1.3.2 - # via cyclopts -roman-numerals==4.1.0 - # via sphinx -rpds-py==0.30.0 - # via jsonschema - # via referencing -rubicon-objc==0.5.3 - # via mouseinfo -ruff==0.14.14 - # via hanzo-aci - # via hanzo-mcp - # via hanzo-memory - # via hanzo-network - # via hanzo-repl -scipy==1.17.0 - # via hanzo-aci -setuptools==80.10.2 - # via grpcio-tools -shellingham==1.5.4 - # via huggingface-hub - # via typer -six==1.17.0 +rich==13.7.1 +ruff==0.9.4 +setuptools==68.2.2 + # via nodeenv +six==1.16.0 # via python-dateutil -smmap==5.0.2 - # via gitdb -sniffio==1.3.1 - # via anthropic +sniffio==1.3.0 + # via anyio # via hanzoai - # via openai -snowballstemmer==3.0.1 - # via sphinx -sortedcontainers==2.4.0 - # via fakeredis -sounddevice==0.5.5 - # via hanzo-repl -speechrecognition==3.14.5 - # via hanzo-repl -sphinx==9.1.0 - # via hanzo-mcp - # via myst-parser - # via sphinx-copybutton - # via sphinx-rtd-theme - # via sphinxcontrib-jquery -sphinx-copybutton==0.5.2 - # via hanzo-mcp -sphinx-rtd-theme==3.1.0 - # via hanzo-mcp -sphinxcontrib-applehelp==2.0.0 - # via sphinx -sphinxcontrib-devhelp==2.0.0 - # via sphinx -sphinxcontrib-htmlhelp==2.1.0 - # via sphinx -sphinxcontrib-jquery==4.1 - # via sphinx-rtd-theme -sphinxcontrib-jsmath==1.0.1 - # via sphinx -sphinxcontrib-qthelp==2.0.0 - # via sphinx -sphinxcontrib-serializinghtml==2.0.0 - # via sphinx -sqlite-vec==0.1.6 - # via hanzo-mcp - # via hanzo-memory -sse-starlette==3.2.0 - # via mcp -stack-data==0.6.3 - # via ipython -starlette==0.50.0 - # via fastapi - # via mcp - # via sse-starlette -structlog==25.5.0 - # via hanzo-memory -sympy==1.14.0 - # via onnxruntime -temporalio==1.21.1 - # via hanzo -textual==7.5.0 - # via hanzo-repl -tiktoken==0.12.0 - # via hanzo-tools-shell - # via litellm -time-machine==3.2.0 -tokenizers==0.22.2 - # via fastembed - # via litellm -tornado==6.5.4 - # via ipykernel - # via jupyter-client -tqdm==4.67.2 - # via fastembed - # via huggingface-hub - # via openai -traitlets==5.14.3 - # via ipykernel - # via ipython - # via jupyter-client - # via jupyter-core - # via matplotlib-inline -tree-sitter==0.25.2 - # via hanzo-aci - # via tree-sitter-language-pack -tree-sitter-c-sharp==0.23.1 - # via tree-sitter-language-pack -tree-sitter-embedded-template==0.25.0 - # via tree-sitter-language-pack -tree-sitter-javascript==0.25.0 - # via hanzo-aci -tree-sitter-language-pack==0.13.0 - # via grep-ast -tree-sitter-python==0.25.0 - # via hanzo-aci -tree-sitter-ruby==0.23.1 - # via hanzo-aci -tree-sitter-typescript==0.23.2 - # via hanzo-aci -tree-sitter-yaml==0.7.2 - # via tree-sitter-language-pack -twine==6.2.0 - # via hanzo-mcp - # via hanzo-memory -typer==0.21.1 - # via hanzo - # via pydocket -typer-slim==0.21.1 - # via huggingface-hub -types-aiofiles==25.1.0.20251011 - # via hanzo-mcp -types-protobuf==6.32.1.20251210 - # via temporalio -types-psutil==7.2.2.20260130 - # via hanzo-mcp -types-setuptools==80.10.0.20260124 - # via hanzo-mcp -typing-extensions==4.15.0 - # via aiosignal - # via anthropic +time-machine==2.9.0 +tomli==2.0.2 + # via mypy + # via pytest +typing-extensions==4.12.2 # via anyio - # via exceptiongroup - # via fastapi - # via grpcio - # via hanzo-mcp - # via hanzo-tools - # via hanzo-tools-core # via hanzoai - # via huggingface-hub - # via mcp + # via multidict # via mypy - # via nexus-rpc - # via openai - # via opentelemetry-api - # via opentelemetry-sdk - # via opentelemetry-semantic-conventions - # via py-key-value-shared # via pydantic # via pydantic-core - # via pydocket # via pyright - # via pytest-asyncio - # via referencing - # via speechrecognition - # via starlette - # via temporalio - # via textual - # via typer - # via typer-slim # via typing-inspection -typing-inspection==0.4.2 - # via mcp +typing-inspection==0.4.1 # via pydantic - # via pydantic-settings -uc-micro-py==1.0.3 - # via linkify-it-py -ujson==5.11.0 - # via hanzo-mcp -urllib3==2.6.3 - # via botocore - # via hanzo-aci - # via hanzoai - # via qdrant-client - # via requests - # via twine -uvicorn==0.40.0 - # via fastmcp - # via mcp -uvloop==0.22.1 - # via hanzo-mcp -virtualenv==20.36.1 +virtualenv==20.24.5 # via nox - # via pre-commit -watchdog==6.0.0 - # via hanzo-tools-fs - # via mkdocs -wcwidth==0.5.3 - # via prompt-toolkit -websockets==16.0 - # via fastmcp - # via hanzo-mcp - # via hanzo-tools-ide -whatthepatch==1.0.7 - # via hanzo-aci -wrapt==1.17.3 - # via aiobotocore - # via opentelemetry-instrumentation -yarl==1.22.0 +yarl==1.20.0 # via aiohttp -zipp==3.23.0 +zipp==3.17.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index 86791fc8a..1d274971e 100644 --- a/requirements.lock +++ b/requirements.lock @@ -10,1673 +10,66 @@ # universal: false -e file:. - # via hanzo --e file:pkg/hanzo --e file:pkg/hanzo-aci - # via hanzo --e file:pkg/hanzo-mcp - # via hanzo - # via hanzo-repl --e file:pkg/hanzo-memory - # via hanzo - # via hanzo-tools-memory --e file:pkg/hanzo-network - # via hanzo --e file:pkg/hanzo-repl - # via hanzo -aiobotocore==3.1.1 - # via hanzo -aiofiles==25.1.0 - # via hanzo-async - # via hanzo-tools-api - # via meilisearch-python-sdk aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.13.3 - # via aiobotocore - # via hanzo-aci - # via hanzo-tools-browser - # via hanzo-tools-ide - # via litellm -aioitertools==0.13.0 - # via aiobotocore -aiosignal==1.4.0 +aiohttp==3.12.8 + # via hanzoai + # via httpx-aiohttp +aiosignal==1.3.2 # via aiohttp -alabaster==1.0.0 - # via sphinx -annotated-types==0.7.0 +annotated-types==0.6.0 # via pydantic -anthropic==0.77.0 - # via hanzo -anyio==4.12.1 - # via anthropic +anyio==4.4.0 # via hanzoai # via httpx - # via mcp - # via openai - # via sse-starlette - # via starlette -appnope==0.1.4 - # via ipykernel -asttokens==3.0.1 - # via stack-data -attrs==25.4.0 +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 # via aiohttp - # via cyclopts - # via jsonschema - # via referencing -authlib==1.6.6 - # via fastmcp -babel==2.18.0 - # via mkdocs-material - # via sphinx -backrefs==6.1 - # via mkdocs-material -beartype==0.22.9 - # via py-key-value-aio - # via py-key-value-shared -binaryornot==0.4.4 - # via hanzo-aci -black==26.1.0 - # via hanzo-mcp - # via hanzo-memory - # via hanzo-network - # via hanzo-repl -botocore==1.42.30 - # via aiobotocore - # via hanzo -build==1.4.0 - # via hanzo-mcp -cachetools==7.0.0 - # via hanzo-aci - # via py-key-value-aio -camel-converter==5.0.0 - # via meilisearch-python-sdk -certifi==2026.1.4 +certifi==2023.7.22 # via httpcore # via httpx - # via requests -cffi==2.0.0 - # via cryptography - # via sounddevice -cfgv==3.5.0 - # via pre-commit -chardet==5.2.0 - # via binaryornot -charset-normalizer==3.4.4 - # via hanzo-aci - # via requests -click==8.3.1 - # via black - # via hanzo - # via hanzo-agents - # via hanzo-repl - # via litellm - # via mkdocs - # via typer - # via typer-slim - # via uvicorn -cloudpickle==3.1.2 - # via pydocket -colorama==0.4.6 - # via griffe - # via hanzo-repl - # via mkdocs-material -coloredlogs==15.0.1 - # via onnxruntime -comm==0.2.3 - # via ipykernel -coverage==7.13.2 - # via pytest-cov -croniter==6.0.0 - # via hanzo -cryptography==46.0.4 - # via authlib - # via pyjwt -cyclopts==4.5.1 - # via fastmcp -debugpy==1.8.20 - # via ipykernel -decorator==5.2.1 - # via ipdb - # via ipython -diskcache==5.6.3 - # via py-key-value-aio -distlib==0.4.0 - # via virtualenv -distro==1.9.0 - # via anthropic +distro==1.8.0 # via hanzoai - # via openai -dnspython==2.8.0 - # via email-validator - # via pymongo -docstring-parser==0.17.0 - # via anthropic - # via cyclopts -docutils==0.22.4 - # via myst-parser - # via readme-renderer - # via rich-rst - # via sphinx - # via sphinx-rtd-theme -email-validator==2.3.0 - # via pydantic -exceptiongroup==1.3.1 - # via fastmcp -executing==2.2.1 - # via stack-data -factory-boy==3.3.3 - # via hanzo-memory -faker==40.1.2 - # via factory-boy - # via hanzo-memory -fakeredis==2.33.0 - # via pydocket -fastembed==0.7.4 - # via hanzo-mcp -fastmcp==2.14.4 - # via hanzo-mcp - # via hanzo-tools - # via hanzo-tools-agent - # via hanzo-tools-config - # via hanzo-tools-core - # via hanzo-tools-fs - # via hanzo-tools-llm - # via hanzo-tools-refactor -fastuuid==0.14.0 - # via litellm -ffind==1.6.1 - # via hanzo-tools-fs -filelock==3.20.3 - # via hanzo-aci - # via huggingface-hub - # via virtualenv -flake8==7.3.0 - # via hanzo-aci -flatbuffers==25.12.19 - # via onnxruntime -frozenlist==1.8.0 +exceptiongroup==1.2.2 + # via anyio +frozenlist==1.6.2 # via aiohttp # via aiosignal -fsspec==2026.1.0 - # via huggingface-hub -ghp-import==2.1.0 - # via mkdocs -gitdb==4.0.12 - # via gitpython -gitpython==3.1.46 - # via hanzo-aci -grep-ast==0.9.0 - # via hanzo-aci - # via hanzo-tools-fs -griffe==1.15.0 - # via mkdocstrings-python -grpcio==1.76.0 - # via grpcio-tools - # via hanzo-network - # via qdrant-client -grpcio-tools==1.76.0 - # via hanzo-network h11==0.16.0 - # via hanzo-aci - # via hanzoai # via httpcore - # via uvicorn -h2==4.3.0 - # via httpx -hanzo-agents==0.1.2 - # via hanzo - # via hanzo-network -hanzo-async==0.1.1 - # via hanzo-mcp - # via hanzo-tools-agent - # via hanzo-tools-fs - # via hanzo-tools-shell -hanzo-persona==1.0.0 - # via hanzo-mcp -hanzo-tools==0.3.0 - # via hanzo-mcp - # via hanzo-tools-agent - # via hanzo-tools-browser - # via hanzo-tools-computer - # via hanzo-tools-shell -hanzo-tools-agent==0.3.1 - # via hanzo-agents - # via hanzo-mcp -hanzo-tools-api==0.3.1 - # via hanzo-mcp -hanzo-tools-browser==0.2.2 - # via hanzo-mcp -hanzo-tools-computer==0.5.2 - # via hanzo-mcp -hanzo-tools-config==0.2.0 - # via hanzo-mcp -hanzo-tools-core==0.2.0 - # via hanzo-tools-api - # via hanzo-tools-config - # via hanzo-tools-ide - # via hanzo-tools-llm - # via hanzo-tools-lsp - # via hanzo-tools-memory - # via hanzo-tools-reasoning - # via hanzo-tools-refactor - # via hanzo-tools-repl - # via hanzo-tools-todo -hanzo-tools-fs==0.3.1 - # via hanzo-mcp -hanzo-tools-ide==0.1.0 - # via hanzo-mcp -hanzo-tools-llm==0.2.0 - # via hanzo-mcp -hanzo-tools-lsp==0.2.0 - # via hanzo-mcp -hanzo-tools-memory==0.2.0 - # via hanzo-mcp -hanzo-tools-reasoning==0.2.0 - # via hanzo-mcp -hanzo-tools-refactor==0.2.0 - # via hanzo-mcp -hanzo-tools-repl==0.1.0 - # via hanzo-mcp -hanzo-tools-shell==0.6.1 - # via hanzo-mcp - # via hanzo-tools-agent -hanzo-tools-todo==0.2.0 - # via hanzo-mcp -hf-xet==1.2.0 - # via huggingface-hub -hpack==4.1.0 - # via h2 httpcore==1.0.9 # via httpx httpx==0.28.1 - # via anthropic - # via fastmcp - # via hanzo - # via hanzo-memory - # via hanzo-network - # via hanzo-tools-api # via hanzoai - # via huggingface-hub - # via litellm - # via mcp - # via meilisearch-python-sdk - # via openai - # via qdrant-client - # via respx -httpx-sse==0.4.3 - # via mcp -huggingface-hub==1.3.7 - # via fastembed - # via tokenizers -humanfriendly==10.0 - # via coloredlogs -hyperframe==6.1.0 - # via h2 -id==1.5.0 - # via twine -identify==2.6.16 - # via pre-commit -idna==3.11 + # via httpx-aiohttp +httpx-aiohttp==0.1.9 + # via hanzoai +idna==3.4 # via anyio - # via email-validator # via httpx - # via requests # via yarl -imagesize==1.4.1 - # via sphinx -importlib-metadata==8.7.1 - # via litellm - # via opentelemetry-api -iniconfig==2.3.0 - # via pytest -ipdb==0.13.13 - # via hanzo-memory -ipykernel==7.1.0 - # via hanzo-mcp -ipython==9.10.0 - # via hanzo-memory - # via hanzo-repl - # via ipdb - # via ipykernel -ipython-pygments-lexers==1.1.1 - # via ipython -jaraco-classes==3.4.0 - # via keyring -jaraco-context==6.1.0 - # via keyring -jaraco-functools==4.4.0 - # via keyring -jedi==0.19.2 - # via ipython -jinja2==3.1.6 - # via litellm - # via mkdocs - # via mkdocs-material - # via mkdocstrings - # via myst-parser - # via sphinx -jiter==0.13.0 - # via anthropic - # via openai -jmespath==1.1.0 - # via aiobotocore - # via botocore -jsonref==1.1.0 - # via fastmcp -jsonschema==4.26.0 - # via litellm - # via mcp -jsonschema-path==0.3.4 - # via fastmcp -jsonschema-specifications==2025.9.1 - # via jsonschema -jupyter-client==8.8.0 - # via hanzo-mcp - # via hanzo-tools-repl - # via ipykernel -jupyter-core==5.9.1 - # via hanzo-tools-repl - # via ipykernel - # via jupyter-client -keyring==25.7.0 - # via py-key-value-aio - # via twine -librt==0.7.8 - # via mypy -linkify-it-py==2.0.3 - # via markdown-it-py -litellm==1.81.6 - # via hanzo-aci - # via hanzo-repl - # via hanzoai -loguru==0.7.3 - # via fastembed -lupa==2.6 - # via fakeredis -markdown==3.10.1 - # via mkdocs - # via mkdocs-autorefs - # via mkdocs-material - # via mkdocstrings - # via pymdown-extensions -markdown-it-py==4.0.0 - # via mdit-py-plugins - # via myst-parser - # via rich - # via textual -markupsafe==3.0.3 - # via jinja2 - # via mkdocs - # via mkdocs-autorefs - # via mkdocstrings -matplotlib-inline==0.2.1 - # via ipykernel - # via ipython -mccabe==0.7.0 - # via flake8 -mcp==1.26.0 - # via fastmcp - # via hanzo-mcp - # via hanzo-memory - # via hanzo-tools - # via hanzo-tools-agent - # via hanzo-tools-browser - # via hanzo-tools-computer - # via hanzo-tools-config - # via hanzo-tools-core - # via hanzo-tools-fs - # via hanzo-tools-llm - # via hanzo-tools-lsp - # via hanzo-tools-memory - # via hanzo-tools-reasoning - # via hanzo-tools-refactor - # via hanzo-tools-shell - # via hanzo-tools-todo -mdit-py-plugins==0.5.0 - # via myst-parser - # via textual -mdurl==0.1.2 - # via markdown-it-py -meilisearch-python-sdk==6.1.0 - # via hanzo -mergedeep==1.3.4 - # via mkdocs - # via mkdocs-get-deps -mkdocs==1.6.1 - # via hanzo-memory - # via mkdocs-autorefs - # via mkdocs-material - # via mkdocstrings -mkdocs-autorefs==1.4.3 - # via mkdocstrings - # via mkdocstrings-python -mkdocs-get-deps==0.2.0 - # via mkdocs -mkdocs-material==9.7.1 - # via hanzo-memory -mkdocs-material-extensions==1.3.1 - # via mkdocs-material -mkdocstrings==1.0.2 - # via hanzo-memory - # via mkdocstrings-python -mkdocstrings-python==2.0.1 - # via mkdocstrings -mmh3==5.2.0 - # via fastembed -more-itertools==10.8.0 - # via jaraco-classes - # via jaraco-functools -motor==3.7.1 - # via hanzo -mouseinfo==0.1.3 - # via pyautogui -mpmath==1.3.0 - # via sympy -multidict==6.7.1 - # via aiobotocore +multidict==6.4.4 # via aiohttp # via yarl -mypy==1.19.1 - # via hanzo-mcp - # via hanzo-memory - # via hanzo-network - # via hanzo-repl -mypy-extensions==1.1.0 - # via black - # via mypy -myst-parser==5.0.0 - # via hanzo-mcp -nats-py==2.12.0 - # via hanzo -nest-asyncio==1.6.0 - # via ipykernel -networkx==3.6.1 - # via hanzo-aci -nexus-rpc==1.3.0 - # via temporalio -nh3==0.3.2 - # via readme-renderer -nodeenv==1.10.0 - # via pre-commit -numpy==2.4.2 - # via fastembed - # via hanzo-aci - # via hanzo-memory - # via hanzo-repl - # via hanzo-tools-computer - # via onnxruntime - # via pandas - # via qdrant-client - # via scipy -onnxruntime==1.23.2 - # via fastembed -openai==2.16.0 - # via hanzo - # via litellm -openapi-pydantic==0.5.1 - # via fastmcp -opentelemetry-api==1.39.1 - # via opentelemetry-exporter-prometheus - # via opentelemetry-instrumentation - # via opentelemetry-sdk - # via opentelemetry-semantic-conventions - # via pydocket -opentelemetry-exporter-prometheus==0.60b1 - # via pydocket -opentelemetry-instrumentation==0.60b1 - # via pydocket -opentelemetry-sdk==1.39.1 - # via opentelemetry-exporter-prometheus -opentelemetry-semantic-conventions==0.60b1 - # via opentelemetry-instrumentation - # via opentelemetry-sdk -orjson==3.11.7 - # via hanzo-mcp -packaging==26.0 - # via black - # via build - # via fastmcp - # via huggingface-hub - # via ipykernel - # via mkdocs - # via onnxruntime - # via opentelemetry-instrumentation - # via pytest - # via sphinx - # via twine -paginate==0.5.7 - # via mkdocs-material -pandas==3.0.0 - # via hanzo-aci -parso==0.8.5 - # via jedi -pathable==0.4.4 - # via jsonschema-path -pathspec==1.0.4 - # via black - # via grep-ast - # via mkdocs - # via mypy -pathvalidate==3.3.1 - # via py-key-value-aio -pexpect==4.9.0 - # via ipython -pillow==11.3.0 - # via fastembed - # via hanzo-tools-computer -platformdirs==4.5.1 - # via black - # via fastmcp - # via jupyter-core - # via mkdocs-get-deps - # via textual - # via virtualenv -pluggy==1.6.0 - # via pytest - # via pytest-cov -polars==1.37.1 - # via hanzo-memory -polars-runtime-32==1.37.1 - # via polars -portalocker==3.2.0 - # via qdrant-client -pre-commit==4.5.1 - # via hanzo-aci - # via hanzo-memory -prometheus-client==0.24.1 - # via opentelemetry-exporter-prometheus - # via pydocket -prompt-toolkit==3.0.52 - # via hanzo - # via hanzo-repl - # via ipython -propcache==0.4.1 +propcache==0.3.1 # via aiohttp # via yarl -protobuf==6.33.5 - # via grpcio-tools - # via hanzo-network - # via onnxruntime - # via qdrant-client - # via temporalio -psutil==7.2.2 - # via hanzo-aci - # via hanzo-network - # via ipykernel -ptyprocess==0.7.0 - # via pexpect -pure-eval==0.2.3 - # via stack-data -py-key-value-aio==0.3.0 - # via fastmcp - # via pydocket -py-key-value-shared==0.3.0 - # via py-key-value-aio -py-rust-stemmers==0.1.5 - # via fastembed -pyautogui==0.9.54 - # via hanzo-tools-computer -pycodestyle==2.14.0 - # via flake8 -pycparser==3.0 - # via cffi -pydantic==2.12.5 - # via anthropic - # via camel-converter - # via fastmcp - # via hanzo - # via hanzo-mcp - # via hanzo-memory - # via hanzo-network - # via hanzo-tools - # via hanzo-tools-agent - # via hanzo-tools-api - # via hanzo-tools-browser - # via hanzo-tools-computer - # via hanzo-tools-config - # via hanzo-tools-core - # via hanzo-tools-fs - # via hanzo-tools-llm - # via hanzo-tools-lsp - # via hanzo-tools-memory - # via hanzo-tools-reasoning - # via hanzo-tools-refactor - # via hanzo-tools-shell - # via hanzo-tools-todo +pydantic==2.11.9 # via hanzoai - # via litellm - # via mcp - # via meilisearch-python-sdk - # via openai - # via openapi-pydantic - # via pydantic-settings - # via qdrant-client -pydantic-core==2.41.5 +pydantic-core==2.33.2 # via pydantic -pydantic-settings==2.12.0 - # via hanzo-mcp - # via hanzo-memory - # via mcp -pydocket==0.16.6 - # via fastmcp -pyflakes==3.4.0 - # via flake8 -pygetwindow==0.0.9 - # via pyautogui -pygments==2.19.2 - # via hanzo-repl - # via ipython - # via ipython-pygments-lexers - # via mkdocs-material - # via pytest - # via readme-renderer - # via rich - # via sphinx - # via textual -pyjwt==2.11.0 - # via mcp - # via meilisearch-python-sdk -pymdown-extensions==10.20.1 - # via mkdocs-material - # via mkdocstrings -pymongo==4.16.0 - # via hanzo - # via motor -pymsgbox==2.0.1 - # via pyautogui -pyobjc==12.1 - # via pyttsx3 -pyobjc-core==12.1 - # via pyautogui - # via pyobjc - # via pyobjc-framework-accessibility - # via pyobjc-framework-accounts - # via pyobjc-framework-addressbook - # via pyobjc-framework-adservices - # via pyobjc-framework-adsupport - # via pyobjc-framework-applescriptkit - # via pyobjc-framework-applescriptobjc - # via pyobjc-framework-applicationservices - # via pyobjc-framework-apptrackingtransparency - # via pyobjc-framework-arkit - # via pyobjc-framework-audiovideobridging - # via pyobjc-framework-authenticationservices - # via pyobjc-framework-automaticassessmentconfiguration - # via pyobjc-framework-automator - # via pyobjc-framework-avfoundation - # via pyobjc-framework-avkit - # via pyobjc-framework-avrouting - # via pyobjc-framework-backgroundassets - # via pyobjc-framework-browserenginekit - # via pyobjc-framework-businesschat - # via pyobjc-framework-callkit - # via pyobjc-framework-carbon - # via pyobjc-framework-cfnetwork - # via pyobjc-framework-cinematic - # via pyobjc-framework-classkit - # via pyobjc-framework-cloudkit - # via pyobjc-framework-cocoa - # via pyobjc-framework-colorsync - # via pyobjc-framework-compositorservices - # via pyobjc-framework-contacts - # via pyobjc-framework-contactsui - # via pyobjc-framework-coreaudio - # via pyobjc-framework-coreaudiokit - # via pyobjc-framework-corebluetooth - # via pyobjc-framework-coredata - # via pyobjc-framework-corehaptics - # via pyobjc-framework-corelocation - # via pyobjc-framework-coremedia - # via pyobjc-framework-coremediaio - # via pyobjc-framework-coremidi - # via pyobjc-framework-coreml - # via pyobjc-framework-coremotion - # via pyobjc-framework-coreservices - # via pyobjc-framework-corespotlight - # via pyobjc-framework-coretext - # via pyobjc-framework-corewlan - # via pyobjc-framework-cryptotokenkit - # via pyobjc-framework-datadetection - # via pyobjc-framework-devicecheck - # via pyobjc-framework-devicediscoveryextension - # via pyobjc-framework-discrecording - # via pyobjc-framework-discrecordingui - # via pyobjc-framework-diskarbitration - # via pyobjc-framework-dvdplayback - # via pyobjc-framework-eventkit - # via pyobjc-framework-exceptionhandling - # via pyobjc-framework-executionpolicy - # via pyobjc-framework-extensionkit - # via pyobjc-framework-externalaccessory - # via pyobjc-framework-fileprovider - # via pyobjc-framework-fileproviderui - # via pyobjc-framework-findersync - # via pyobjc-framework-fsevents - # via pyobjc-framework-fskit - # via pyobjc-framework-gamecenter - # via pyobjc-framework-gamecontroller - # via pyobjc-framework-gamekit - # via pyobjc-framework-gameplaykit - # via pyobjc-framework-gamesave - # via pyobjc-framework-healthkit - # via pyobjc-framework-imagecapturecore - # via pyobjc-framework-installerplugins - # via pyobjc-framework-intents - # via pyobjc-framework-intentsui - # via pyobjc-framework-iobluetooth - # via pyobjc-framework-iobluetoothui - # via pyobjc-framework-iosurface - # via pyobjc-framework-ituneslibrary - # via pyobjc-framework-kernelmanagement - # via pyobjc-framework-latentsemanticmapping - # via pyobjc-framework-launchservices - # via pyobjc-framework-libdispatch - # via pyobjc-framework-libxpc - # via pyobjc-framework-linkpresentation - # via pyobjc-framework-localauthentication - # via pyobjc-framework-localauthenticationembeddedui - # via pyobjc-framework-mailkit - # via pyobjc-framework-mapkit - # via pyobjc-framework-mediaaccessibility - # via pyobjc-framework-mediaextension - # via pyobjc-framework-medialibrary - # via pyobjc-framework-mediaplayer - # via pyobjc-framework-mediatoolbox - # via pyobjc-framework-metal - # via pyobjc-framework-metalfx - # via pyobjc-framework-metalkit - # via pyobjc-framework-metalperformanceshaders - # via pyobjc-framework-metalperformanceshadersgraph - # via pyobjc-framework-metrickit - # via pyobjc-framework-mlcompute - # via pyobjc-framework-modelio - # via pyobjc-framework-multipeerconnectivity - # via pyobjc-framework-naturallanguage - # via pyobjc-framework-netfs - # via pyobjc-framework-network - # via pyobjc-framework-networkextension - # via pyobjc-framework-notificationcenter - # via pyobjc-framework-opendirectory - # via pyobjc-framework-osakit - # via pyobjc-framework-oslog - # via pyobjc-framework-passkit - # via pyobjc-framework-pencilkit - # via pyobjc-framework-phase - # via pyobjc-framework-photos - # via pyobjc-framework-photosui - # via pyobjc-framework-preferencepanes - # via pyobjc-framework-pushkit - # via pyobjc-framework-quartz - # via pyobjc-framework-quicklookthumbnailing - # via pyobjc-framework-replaykit - # via pyobjc-framework-safariservices - # via pyobjc-framework-safetykit - # via pyobjc-framework-scenekit - # via pyobjc-framework-screencapturekit - # via pyobjc-framework-screensaver - # via pyobjc-framework-screentime - # via pyobjc-framework-searchkit - # via pyobjc-framework-security - # via pyobjc-framework-securityfoundation - # via pyobjc-framework-securityinterface - # via pyobjc-framework-securityui - # via pyobjc-framework-sensitivecontentanalysis - # via pyobjc-framework-servicemanagement - # via pyobjc-framework-sharedwithyou - # via pyobjc-framework-sharedwithyoucore - # via pyobjc-framework-shazamkit - # via pyobjc-framework-social - # via pyobjc-framework-soundanalysis - # via pyobjc-framework-speech - # via pyobjc-framework-spritekit - # via pyobjc-framework-storekit - # via pyobjc-framework-symbols - # via pyobjc-framework-syncservices - # via pyobjc-framework-systemconfiguration - # via pyobjc-framework-systemextensions - # via pyobjc-framework-threadnetwork - # via pyobjc-framework-uniformtypeidentifiers - # via pyobjc-framework-usernotifications - # via pyobjc-framework-usernotificationsui - # via pyobjc-framework-videosubscriberaccount - # via pyobjc-framework-videotoolbox - # via pyobjc-framework-virtualization - # via pyobjc-framework-vision - # via pyobjc-framework-webkit -pyobjc-framework-accessibility==12.1 - # via pyobjc -pyobjc-framework-accounts==12.1 - # via pyobjc - # via pyobjc-framework-cloudkit -pyobjc-framework-addressbook==12.1 - # via pyobjc -pyobjc-framework-adservices==12.1 - # via pyobjc -pyobjc-framework-adsupport==12.1 - # via pyobjc -pyobjc-framework-applescriptkit==12.1 - # via pyobjc -pyobjc-framework-applescriptobjc==12.1 - # via pyobjc -pyobjc-framework-applicationservices==12.1 - # via pyobjc -pyobjc-framework-apptrackingtransparency==12.1 - # via pyobjc -pyobjc-framework-arkit==12.1 - # via pyobjc -pyobjc-framework-audiovideobridging==12.1 - # via pyobjc -pyobjc-framework-authenticationservices==12.1 - # via pyobjc -pyobjc-framework-automaticassessmentconfiguration==12.1 - # via pyobjc -pyobjc-framework-automator==12.1 - # via pyobjc -pyobjc-framework-avfoundation==12.1 - # via pyobjc - # via pyobjc-framework-cinematic - # via pyobjc-framework-mediaextension - # via pyobjc-framework-mediaplayer - # via pyobjc-framework-phase -pyobjc-framework-avkit==12.1 - # via pyobjc -pyobjc-framework-avrouting==12.1 - # via pyobjc -pyobjc-framework-backgroundassets==12.1 - # via pyobjc -pyobjc-framework-browserenginekit==12.1 - # via pyobjc -pyobjc-framework-businesschat==12.1 - # via pyobjc -pyobjc-framework-callkit==12.1 - # via pyobjc -pyobjc-framework-carbon==12.1 - # via pyobjc -pyobjc-framework-cfnetwork==12.1 - # via pyobjc -pyobjc-framework-cinematic==12.1 - # via pyobjc -pyobjc-framework-classkit==12.1 - # via pyobjc -pyobjc-framework-cloudkit==12.1 - # via pyobjc -pyobjc-framework-cocoa==12.1 - # via pyobjc - # via pyobjc-framework-accessibility - # via pyobjc-framework-accounts - # via pyobjc-framework-addressbook - # via pyobjc-framework-adservices - # via pyobjc-framework-adsupport - # via pyobjc-framework-applescriptkit - # via pyobjc-framework-applescriptobjc - # via pyobjc-framework-applicationservices - # via pyobjc-framework-apptrackingtransparency - # via pyobjc-framework-arkit - # via pyobjc-framework-audiovideobridging - # via pyobjc-framework-authenticationservices - # via pyobjc-framework-automaticassessmentconfiguration - # via pyobjc-framework-automator - # via pyobjc-framework-avfoundation - # via pyobjc-framework-avkit - # via pyobjc-framework-avrouting - # via pyobjc-framework-backgroundassets - # via pyobjc-framework-browserenginekit - # via pyobjc-framework-businesschat - # via pyobjc-framework-callkit - # via pyobjc-framework-carbon - # via pyobjc-framework-cfnetwork - # via pyobjc-framework-cinematic - # via pyobjc-framework-classkit - # via pyobjc-framework-cloudkit - # via pyobjc-framework-colorsync - # via pyobjc-framework-compositorservices - # via pyobjc-framework-contacts - # via pyobjc-framework-contactsui - # via pyobjc-framework-coreaudio - # via pyobjc-framework-coreaudiokit - # via pyobjc-framework-corebluetooth - # via pyobjc-framework-coredata - # via pyobjc-framework-corehaptics - # via pyobjc-framework-corelocation - # via pyobjc-framework-coremedia - # via pyobjc-framework-coremediaio - # via pyobjc-framework-coremidi - # via pyobjc-framework-coreml - # via pyobjc-framework-coremotion - # via pyobjc-framework-coreservices - # via pyobjc-framework-corespotlight - # via pyobjc-framework-coretext - # via pyobjc-framework-corewlan - # via pyobjc-framework-cryptotokenkit - # via pyobjc-framework-datadetection - # via pyobjc-framework-devicecheck - # via pyobjc-framework-devicediscoveryextension - # via pyobjc-framework-discrecording - # via pyobjc-framework-discrecordingui - # via pyobjc-framework-diskarbitration - # via pyobjc-framework-dvdplayback - # via pyobjc-framework-eventkit - # via pyobjc-framework-exceptionhandling - # via pyobjc-framework-executionpolicy - # via pyobjc-framework-extensionkit - # via pyobjc-framework-externalaccessory - # via pyobjc-framework-fileprovider - # via pyobjc-framework-findersync - # via pyobjc-framework-fsevents - # via pyobjc-framework-fskit - # via pyobjc-framework-gamecenter - # via pyobjc-framework-gamecontroller - # via pyobjc-framework-gamekit - # via pyobjc-framework-gameplaykit - # via pyobjc-framework-gamesave - # via pyobjc-framework-healthkit - # via pyobjc-framework-imagecapturecore - # via pyobjc-framework-installerplugins - # via pyobjc-framework-intents - # via pyobjc-framework-iobluetooth - # via pyobjc-framework-iosurface - # via pyobjc-framework-ituneslibrary - # via pyobjc-framework-kernelmanagement - # via pyobjc-framework-latentsemanticmapping - # via pyobjc-framework-libdispatch - # via pyobjc-framework-libxpc - # via pyobjc-framework-linkpresentation - # via pyobjc-framework-localauthentication - # via pyobjc-framework-localauthenticationembeddedui - # via pyobjc-framework-mailkit - # via pyobjc-framework-mapkit - # via pyobjc-framework-mediaaccessibility - # via pyobjc-framework-mediaextension - # via pyobjc-framework-medialibrary - # via pyobjc-framework-mediatoolbox - # via pyobjc-framework-metal - # via pyobjc-framework-metalkit - # via pyobjc-framework-metrickit - # via pyobjc-framework-mlcompute - # via pyobjc-framework-modelio - # via pyobjc-framework-multipeerconnectivity - # via pyobjc-framework-naturallanguage - # via pyobjc-framework-netfs - # via pyobjc-framework-network - # via pyobjc-framework-networkextension - # via pyobjc-framework-notificationcenter - # via pyobjc-framework-opendirectory - # via pyobjc-framework-osakit - # via pyobjc-framework-oslog - # via pyobjc-framework-passkit - # via pyobjc-framework-pencilkit - # via pyobjc-framework-photos - # via pyobjc-framework-photosui - # via pyobjc-framework-preferencepanes - # via pyobjc-framework-pushkit - # via pyobjc-framework-quartz - # via pyobjc-framework-quicklookthumbnailing - # via pyobjc-framework-replaykit - # via pyobjc-framework-safariservices - # via pyobjc-framework-safetykit - # via pyobjc-framework-scenekit - # via pyobjc-framework-screencapturekit - # via pyobjc-framework-screensaver - # via pyobjc-framework-screentime - # via pyobjc-framework-security - # via pyobjc-framework-securityfoundation - # via pyobjc-framework-securityinterface - # via pyobjc-framework-securityui - # via pyobjc-framework-sensitivecontentanalysis - # via pyobjc-framework-servicemanagement - # via pyobjc-framework-sharedwithyoucore - # via pyobjc-framework-shazamkit - # via pyobjc-framework-social - # via pyobjc-framework-soundanalysis - # via pyobjc-framework-speech - # via pyobjc-framework-spritekit - # via pyobjc-framework-storekit - # via pyobjc-framework-symbols - # via pyobjc-framework-syncservices - # via pyobjc-framework-systemconfiguration - # via pyobjc-framework-systemextensions - # via pyobjc-framework-threadnetwork - # via pyobjc-framework-uniformtypeidentifiers - # via pyobjc-framework-usernotifications - # via pyobjc-framework-usernotificationsui - # via pyobjc-framework-videosubscriberaccount - # via pyobjc-framework-videotoolbox - # via pyobjc-framework-virtualization - # via pyobjc-framework-vision - # via pyobjc-framework-webkit -pyobjc-framework-colorsync==12.1 - # via pyobjc -pyobjc-framework-compositorservices==12.1 - # via pyobjc -pyobjc-framework-contacts==12.1 - # via pyobjc - # via pyobjc-framework-contactsui -pyobjc-framework-contactsui==12.1 - # via pyobjc -pyobjc-framework-coreaudio==12.1 - # via pyobjc - # via pyobjc-framework-avfoundation - # via pyobjc-framework-browserenginekit - # via pyobjc-framework-coreaudiokit -pyobjc-framework-coreaudiokit==12.1 - # via pyobjc -pyobjc-framework-corebluetooth==12.1 - # via pyobjc -pyobjc-framework-coredata==12.1 - # via pyobjc - # via pyobjc-framework-cloudkit - # via pyobjc-framework-syncservices -pyobjc-framework-corehaptics==12.1 - # via pyobjc -pyobjc-framework-corelocation==12.1 - # via pyobjc - # via pyobjc-framework-cloudkit - # via pyobjc-framework-mapkit -pyobjc-framework-coremedia==12.1 - # via pyobjc - # via pyobjc-framework-avfoundation - # via pyobjc-framework-browserenginekit - # via pyobjc-framework-cinematic - # via pyobjc-framework-mediaextension - # via pyobjc-framework-oslog - # via pyobjc-framework-screencapturekit - # via pyobjc-framework-videotoolbox -pyobjc-framework-coremediaio==12.1 - # via pyobjc -pyobjc-framework-coremidi==12.1 - # via pyobjc -pyobjc-framework-coreml==12.1 - # via pyobjc - # via pyobjc-framework-vision -pyobjc-framework-coremotion==12.1 - # via pyobjc -pyobjc-framework-coreservices==12.1 - # via pyobjc - # via pyobjc-framework-launchservices - # via pyobjc-framework-searchkit -pyobjc-framework-corespotlight==12.1 - # via pyobjc -pyobjc-framework-coretext==12.1 - # via pyobjc - # via pyobjc-framework-applicationservices -pyobjc-framework-corewlan==12.1 - # via pyobjc -pyobjc-framework-cryptotokenkit==12.1 - # via pyobjc -pyobjc-framework-datadetection==12.1 - # via pyobjc -pyobjc-framework-devicecheck==12.1 - # via pyobjc -pyobjc-framework-devicediscoveryextension==12.1 - # via pyobjc -pyobjc-framework-discrecording==12.1 - # via pyobjc - # via pyobjc-framework-discrecordingui -pyobjc-framework-discrecordingui==12.1 - # via pyobjc -pyobjc-framework-diskarbitration==12.1 - # via pyobjc -pyobjc-framework-dvdplayback==12.1 - # via pyobjc -pyobjc-framework-eventkit==12.1 - # via pyobjc -pyobjc-framework-exceptionhandling==12.1 - # via pyobjc -pyobjc-framework-executionpolicy==12.1 - # via pyobjc -pyobjc-framework-extensionkit==12.1 - # via pyobjc -pyobjc-framework-externalaccessory==12.1 - # via pyobjc -pyobjc-framework-fileprovider==12.1 - # via pyobjc - # via pyobjc-framework-fileproviderui -pyobjc-framework-fileproviderui==12.1 - # via pyobjc -pyobjc-framework-findersync==12.1 - # via pyobjc -pyobjc-framework-fsevents==12.1 - # via pyobjc-framework-coreservices -pyobjc-framework-fskit==12.1 - # via pyobjc -pyobjc-framework-gamecenter==12.1 - # via pyobjc -pyobjc-framework-gamecontroller==12.1 - # via pyobjc -pyobjc-framework-gamekit==12.1 - # via pyobjc -pyobjc-framework-gameplaykit==12.1 - # via pyobjc -pyobjc-framework-gamesave==12.1 - # via pyobjc -pyobjc-framework-healthkit==12.1 - # via pyobjc -pyobjc-framework-imagecapturecore==12.1 - # via pyobjc -pyobjc-framework-installerplugins==12.1 - # via pyobjc -pyobjc-framework-intents==12.1 - # via pyobjc - # via pyobjc-framework-intentsui -pyobjc-framework-intentsui==12.1 - # via pyobjc -pyobjc-framework-iobluetooth==12.1 - # via pyobjc - # via pyobjc-framework-iobluetoothui -pyobjc-framework-iobluetoothui==12.1 - # via pyobjc -pyobjc-framework-iosurface==12.1 - # via pyobjc -pyobjc-framework-ituneslibrary==12.1 - # via pyobjc -pyobjc-framework-kernelmanagement==12.1 - # via pyobjc -pyobjc-framework-latentsemanticmapping==12.1 - # via pyobjc -pyobjc-framework-launchservices==12.1 - # via pyobjc -pyobjc-framework-libdispatch==12.1 - # via pyobjc -pyobjc-framework-libxpc==12.1 - # via pyobjc -pyobjc-framework-linkpresentation==12.1 - # via pyobjc -pyobjc-framework-localauthentication==12.1 - # via pyobjc - # via pyobjc-framework-localauthenticationembeddedui -pyobjc-framework-localauthenticationembeddedui==12.1 - # via pyobjc -pyobjc-framework-mailkit==12.1 - # via pyobjc -pyobjc-framework-mapkit==12.1 - # via pyobjc -pyobjc-framework-mediaaccessibility==12.1 - # via pyobjc -pyobjc-framework-mediaextension==12.1 - # via pyobjc -pyobjc-framework-medialibrary==12.1 - # via pyobjc -pyobjc-framework-mediaplayer==12.1 - # via pyobjc -pyobjc-framework-mediatoolbox==12.1 - # via pyobjc -pyobjc-framework-metal==12.1 - # via pyobjc - # via pyobjc-framework-cinematic - # via pyobjc-framework-compositorservices - # via pyobjc-framework-metalfx - # via pyobjc-framework-metalkit - # via pyobjc-framework-metalperformanceshaders -pyobjc-framework-metalfx==12.1 - # via pyobjc -pyobjc-framework-metalkit==12.1 - # via pyobjc -pyobjc-framework-metalperformanceshaders==12.1 - # via pyobjc - # via pyobjc-framework-metalperformanceshadersgraph -pyobjc-framework-metalperformanceshadersgraph==12.1 - # via pyobjc -pyobjc-framework-metrickit==12.1 - # via pyobjc -pyobjc-framework-mlcompute==12.1 - # via pyobjc -pyobjc-framework-modelio==12.1 - # via pyobjc -pyobjc-framework-multipeerconnectivity==12.1 - # via pyobjc -pyobjc-framework-naturallanguage==12.1 - # via pyobjc -pyobjc-framework-netfs==12.1 - # via pyobjc -pyobjc-framework-network==12.1 - # via pyobjc -pyobjc-framework-networkextension==12.1 - # via pyobjc -pyobjc-framework-notificationcenter==12.1 - # via pyobjc -pyobjc-framework-opendirectory==12.1 - # via pyobjc -pyobjc-framework-osakit==12.1 - # via pyobjc -pyobjc-framework-oslog==12.1 - # via pyobjc -pyobjc-framework-passkit==12.1 - # via pyobjc -pyobjc-framework-pencilkit==12.1 - # via pyobjc -pyobjc-framework-phase==12.1 - # via pyobjc -pyobjc-framework-photos==12.1 - # via pyobjc -pyobjc-framework-photosui==12.1 - # via pyobjc -pyobjc-framework-preferencepanes==12.1 - # via pyobjc -pyobjc-framework-pushkit==12.1 - # via pyobjc -pyobjc-framework-quartz==12.1 - # via pyautogui - # via pyobjc - # via pyobjc-framework-accessibility - # via pyobjc-framework-applicationservices - # via pyobjc-framework-avfoundation - # via pyobjc-framework-avkit - # via pyobjc-framework-browserenginekit - # via pyobjc-framework-coretext - # via pyobjc-framework-gamekit - # via pyobjc-framework-linkpresentation - # via pyobjc-framework-mapkit - # via pyobjc-framework-medialibrary - # via pyobjc-framework-modelio - # via pyobjc-framework-oslog - # via pyobjc-framework-quicklookthumbnailing - # via pyobjc-framework-safetykit - # via pyobjc-framework-scenekit - # via pyobjc-framework-sensitivecontentanalysis - # via pyobjc-framework-spritekit - # via pyobjc-framework-videotoolbox - # via pyobjc-framework-vision -pyobjc-framework-quicklookthumbnailing==12.1 - # via pyobjc -pyobjc-framework-replaykit==12.1 - # via pyobjc -pyobjc-framework-safariservices==12.1 - # via pyobjc -pyobjc-framework-safetykit==12.1 - # via pyobjc -pyobjc-framework-scenekit==12.1 - # via pyobjc -pyobjc-framework-screencapturekit==12.1 - # via pyobjc -pyobjc-framework-screensaver==12.1 - # via pyobjc -pyobjc-framework-screentime==12.1 - # via pyobjc -pyobjc-framework-searchkit==12.1 - # via pyobjc -pyobjc-framework-security==12.1 - # via pyobjc - # via pyobjc-framework-localauthentication - # via pyobjc-framework-securityfoundation - # via pyobjc-framework-securityinterface - # via pyobjc-framework-securityui -pyobjc-framework-securityfoundation==12.1 - # via pyobjc -pyobjc-framework-securityinterface==12.1 - # via pyobjc -pyobjc-framework-securityui==12.1 - # via pyobjc -pyobjc-framework-sensitivecontentanalysis==12.1 - # via pyobjc -pyobjc-framework-servicemanagement==12.1 - # via pyobjc -pyobjc-framework-sharedwithyou==12.1 - # via pyobjc -pyobjc-framework-sharedwithyoucore==12.1 - # via pyobjc - # via pyobjc-framework-sharedwithyou -pyobjc-framework-shazamkit==12.1 - # via pyobjc -pyobjc-framework-social==12.1 - # via pyobjc -pyobjc-framework-soundanalysis==12.1 - # via pyobjc -pyobjc-framework-speech==12.1 - # via pyobjc -pyobjc-framework-spritekit==12.1 - # via pyobjc - # via pyobjc-framework-gameplaykit -pyobjc-framework-storekit==12.1 - # via pyobjc -pyobjc-framework-symbols==12.1 - # via pyobjc -pyobjc-framework-syncservices==12.1 - # via pyobjc -pyobjc-framework-systemconfiguration==12.1 - # via pyobjc -pyobjc-framework-systemextensions==12.1 - # via pyobjc -pyobjc-framework-threadnetwork==12.1 - # via pyobjc -pyobjc-framework-uniformtypeidentifiers==12.1 - # via pyobjc -pyobjc-framework-usernotifications==12.1 - # via pyobjc - # via pyobjc-framework-usernotificationsui -pyobjc-framework-usernotificationsui==12.1 - # via pyobjc -pyobjc-framework-videosubscriberaccount==12.1 - # via pyobjc -pyobjc-framework-videotoolbox==12.1 - # via pyobjc -pyobjc-framework-virtualization==12.1 - # via pyobjc -pyobjc-framework-vision==12.1 - # via pyobjc -pyobjc-framework-webkit==12.1 - # via pyobjc -pyperclip==1.11.0 - # via fastmcp - # via mouseinfo -pyproject-hooks==1.2.0 - # via build -pyrect==0.2.0 - # via pygetwindow -pyscreeze==1.0.1 - # via pyautogui -pytest==9.0.2 - # via hanzo-aci - # via hanzo-mcp - # via hanzo-memory - # via hanzo-network - # via hanzo-repl - # via pytest-asyncio - # via pytest-cov - # via pytest-mock -pytest-asyncio==1.3.0 - # via hanzo-mcp - # via hanzo-memory - # via hanzo-network - # via hanzo-repl -pytest-cov==7.0.0 - # via hanzo-mcp - # via hanzo-memory - # via hanzo-network - # via hanzo-repl -pytest-mock==3.15.1 - # via hanzo-mcp - # via hanzo-memory -python-dateutil==2.9.0.post0 - # via aiobotocore - # via botocore - # via croniter - # via ghp-import - # via jupyter-client - # via pandas -python-dotenv==1.2.1 - # via fastmcp - # via hanzo-repl - # via litellm - # via pydantic-settings -python-json-logger==4.0.0 - # via pydocket -python-multipart==0.0.22 - # via hanzo-memory - # via mcp -pytokens==0.4.1 - # via black -pyttsx3==2.99 - # via hanzo-repl -pytweening==1.2.0 - # via pyautogui -pytz==2025.2 - # via croniter -pyyaml==6.0.3 - # via hanzo - # via hanzo-tools-api - # via huggingface-hub - # via jsonschema-path - # via mkdocs - # via mkdocs-get-deps - # via myst-parser - # via pre-commit - # via pymdown-extensions - # via pyyaml-env-tag -pyyaml-env-tag==1.1 - # via mkdocs -pyzmq==27.1.0 - # via ipykernel - # via jupyter-client -qdrant-client==1.16.2 - # via hanzo -qrcode==8.2 - # via hanzo -readme-renderer==44.0 - # via twine -redis==7.1.0 - # via fakeredis - # via hanzo - # via py-key-value-aio - # via pydocket -referencing==0.36.2 - # via jsonschema - # via jsonschema-path - # via jsonschema-specifications -regex==2026.1.15 - # via tiktoken -requests==2.32.5 - # via fastembed - # via hanzo-aci - # via id - # via jsonschema-path - # via mkdocs-material - # via requests-toolbelt - # via sphinx - # via tiktoken - # via twine -requests-toolbelt==1.0.0 - # via twine -respx==0.22.0 - # via hanzo-memory -rfc3986==2.0.0 - # via twine -rich==14.3.2 - # via cyclopts - # via fastmcp - # via hanzo - # via hanzo-agents - # via hanzo-memory - # via hanzo-network - # via hanzo-repl - # via pydocket - # via rich-rst - # via textual - # via twine - # via typer -rich-rst==1.3.2 - # via cyclopts -roman-numerals==4.1.0 - # via sphinx -rpds-py==0.30.0 - # via jsonschema - # via referencing -rubicon-objc==0.5.3 - # via mouseinfo -ruff==0.14.14 - # via hanzo-aci - # via hanzo-mcp - # via hanzo-memory - # via hanzo-network - # via hanzo-repl -scipy==1.17.0 - # via hanzo-aci -setuptools==80.10.2 - # via grpcio-tools -shellingham==1.5.4 - # via huggingface-hub - # via typer -six==1.17.0 - # via python-dateutil -smmap==5.0.2 - # via gitdb -sniffio==1.3.1 - # via anthropic +sniffio==1.3.0 + # via anyio # via hanzoai - # via openai -snowballstemmer==3.0.1 - # via sphinx -sortedcontainers==2.4.0 - # via fakeredis -sounddevice==0.5.5 - # via hanzo-repl -speechrecognition==3.14.5 - # via hanzo-repl -sphinx==9.1.0 - # via hanzo-mcp - # via myst-parser - # via sphinx-copybutton - # via sphinx-rtd-theme - # via sphinxcontrib-jquery -sphinx-copybutton==0.5.2 - # via hanzo-mcp -sphinx-rtd-theme==3.1.0 - # via hanzo-mcp -sphinxcontrib-applehelp==2.0.0 - # via sphinx -sphinxcontrib-devhelp==2.0.0 - # via sphinx -sphinxcontrib-htmlhelp==2.1.0 - # via sphinx -sphinxcontrib-jquery==4.1 - # via sphinx-rtd-theme -sphinxcontrib-jsmath==1.0.1 - # via sphinx -sphinxcontrib-qthelp==2.0.0 - # via sphinx -sphinxcontrib-serializinghtml==2.0.0 - # via sphinx -sqlite-vec==0.1.6 - # via hanzo-mcp - # via hanzo-memory -sse-starlette==3.2.0 - # via mcp -stack-data==0.6.3 - # via ipython -starlette==0.52.1 - # via mcp - # via sse-starlette -structlog==25.5.0 - # via hanzo-memory -sympy==1.14.0 - # via onnxruntime -temporalio==1.21.1 - # via hanzo -textual==7.5.0 - # via hanzo-repl -tiktoken==0.12.0 - # via hanzo-tools-shell - # via litellm -tokenizers==0.22.2 - # via fastembed - # via litellm -tornado==6.5.4 - # via ipykernel - # via jupyter-client -tqdm==4.67.2 - # via fastembed - # via huggingface-hub - # via openai -traitlets==5.14.3 - # via ipykernel - # via ipython - # via jupyter-client - # via jupyter-core - # via matplotlib-inline -tree-sitter==0.25.2 - # via hanzo-aci - # via tree-sitter-language-pack -tree-sitter-c-sharp==0.23.1 - # via tree-sitter-language-pack -tree-sitter-embedded-template==0.25.0 - # via tree-sitter-language-pack -tree-sitter-javascript==0.25.0 - # via hanzo-aci -tree-sitter-language-pack==0.13.0 - # via grep-ast -tree-sitter-python==0.25.0 - # via hanzo-aci -tree-sitter-ruby==0.23.1 - # via hanzo-aci -tree-sitter-typescript==0.23.2 - # via hanzo-aci -tree-sitter-yaml==0.7.2 - # via tree-sitter-language-pack -twine==6.2.0 - # via hanzo-mcp - # via hanzo-memory -typer==0.21.1 - # via hanzo - # via pydocket -typer-slim==0.21.1 - # via huggingface-hub -types-aiofiles==25.1.0.20251011 - # via hanzo-mcp -types-protobuf==6.32.1.20251210 - # via temporalio -types-psutil==7.2.2.20260130 - # via hanzo-mcp -types-setuptools==80.10.0.20260124 - # via hanzo-mcp -typing-extensions==4.15.0 - # via aiosignal - # via anthropic +typing-extensions==4.12.2 # via anyio - # via exceptiongroup - # via grpcio - # via hanzo-mcp - # via hanzo-tools - # via hanzo-tools-core # via hanzoai - # via huggingface-hub - # via mcp - # via mypy - # via nexus-rpc - # via openai - # via opentelemetry-api - # via opentelemetry-sdk - # via opentelemetry-semantic-conventions - # via py-key-value-shared + # via multidict # via pydantic # via pydantic-core - # via pydocket - # via pytest-asyncio - # via referencing - # via speechrecognition - # via starlette - # via temporalio - # via textual - # via typer - # via typer-slim # via typing-inspection -typing-inspection==0.4.2 - # via mcp +typing-inspection==0.4.1 # via pydantic - # via pydantic-settings -uc-micro-py==1.0.3 - # via linkify-it-py -ujson==5.11.0 - # via hanzo-mcp -urllib3==2.6.3 - # via botocore - # via hanzo-aci - # via hanzoai - # via qdrant-client - # via requests - # via twine -uvicorn==0.40.0 - # via fastmcp - # via mcp -uvloop==0.22.1 - # via hanzo-mcp -virtualenv==20.36.1 - # via pre-commit -watchdog==6.0.0 - # via hanzo-tools-fs - # via mkdocs -wcwidth==0.5.3 - # via prompt-toolkit -websockets==16.0 - # via fastmcp - # via hanzo-mcp - # via hanzo-tools-ide -whatthepatch==1.0.7 - # via hanzo-aci -wrapt==1.17.3 - # via aiobotocore - # via opentelemetry-instrumentation -yarl==1.22.0 +yarl==1.20.0 # via aiohttp -zipp==3.23.0 - # via importlib-metadata diff --git a/scripts/bootstrap b/scripts/bootstrap index e84fe62c3..b430fee36 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,10 +4,18 @@ set -e cd "$(dirname "$0")/.." -if ! command -v rye >/dev/null 2>&1 && [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { - echo "==> Installing Homebrew dependenciesโ€ฆ" - brew bundle + echo -n "==> Install Homebrew dependencies? (y/N): " + read -r response + case "$response" in + [yY][eE][sS]|[yY]) + brew bundle + ;; + *) + ;; + esac + echo } fi diff --git a/scripts/build-all.sh b/scripts/build-all.sh deleted file mode 100755 index ba5af4ea9..000000000 --- a/scripts/build-all.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/bash - -# Hanzo AI SDK - Build All Packages Script -# Builds all packages without publishing - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -echo -e "${BLUE}====================================${NC}" -echo -e "${BLUE}Hanzo AI SDK - Building All Packages${NC}" -echo -e "${BLUE}====================================${NC}" -echo - -# Check if we're in the right directory -if [ ! -f "pyproject.toml" ] || [ ! -d "pkg" ]; then - echo -e "${RED}Error: Must be run from the python-sdk root directory${NC}" - exit 1 -fi - -# Packages to build -PACKAGES=("hanzoai" "hanzo" "hanzo-mcp" "hanzo-memory" "hanzo-network" "hanzo-agents" "hanzo-dev") - -echo -e "${YELLOW}Packages to build:${NC}" -for pkg in "${PACKAGES[@]}"; do - echo -e " โ€ข $pkg" -done -echo - -# Function to build a package -build_package() { - local pkg_name=$1 - local pkg_dir="" - - # Find package directory - if [ -f "pyproject.toml" ] && grep -q "name = \"$pkg_name\"" pyproject.toml; then - pkg_dir="." - elif [ -d "pkg/$pkg_name" ] && [ -f "pkg/$pkg_name/pyproject.toml" ]; then - pkg_dir="pkg/$pkg_name" - else - echo -e "${YELLOW}Skipping $pkg_name - not found${NC}" - return 0 - fi - - echo -e "${BLUE}Building $pkg_name...${NC}" - - # Get version - local version=$(cd "$pkg_dir" && python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])") - echo -e "${YELLOW}Version: $version${NC}" - - # Clean previous builds - rm -rf "$pkg_dir/dist/" "$pkg_dir/build/" "$pkg_dir"/*.egg-info 2>/dev/null || true - - # Build package - (cd "$pkg_dir" && python -m build) - - # Show built files - echo "Built files:" - ls -la "$pkg_dir/dist/" | grep -E '\.(whl|tar\.gz)$' || echo " No dist files found" - - echo -e "${GREEN}โœ“ $pkg_name built successfully${NC}" - echo -} - -# Build each package -for pkg in "${PACKAGES[@]}"; do - build_package "$pkg" -done - -echo -e "${GREEN}====================================${NC}" -echo -e "${GREEN}All packages built successfully!${NC}" -echo -e "${GREEN}====================================${NC}" -echo -echo "To publish all packages:" -echo -e "${YELLOW} export PYPI_TOKEN='your-token'${NC}" -echo -e "${YELLOW} ./scripts/publish-all.sh${NC}" -echo -echo "To publish individual package:" -echo -e "${YELLOW} ./scripts/publish-single.sh hanzoai${NC}" -echo \ No newline at end of file diff --git a/scripts/check_empty_functions.py b/scripts/check_empty_functions.py deleted file mode 100755 index 13deabf46..000000000 --- a/scripts/check_empty_functions.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python3 -"""Check for empty/stub functions in the codebase.""" - -import ast -import sys -from pathlib import Path - - -def check_file(filepath: Path) -> list: - """Check a Python file for empty functions.""" - issues = [] - - try: - content = filepath.read_text() - tree = ast.parse(content) - except: - return issues - - for node in ast.walk(tree): - if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): - # Skip test functions - if node.name.startswith("test_"): - continue - - # Check for empty body - if len(node.body) == 1: - stmt = node.body[0] - - # Check for pass - if isinstance(stmt, ast.Pass): - issues.append(f"{filepath}:{node.lineno} - Function '{node.name}' only contains 'pass'") - - # Check for ellipsis - elif isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Constant): - if stmt.value.value is Ellipsis: - issues.append(f"{filepath}:{node.lineno} - Function '{node.name}' only contains '...'") - - # Check for NotImplementedError - elif isinstance(stmt, ast.Raise): - if hasattr(stmt, "exc") and isinstance(stmt.exc, ast.Call): - if hasattr(stmt.exc.func, "id") and stmt.exc.func.id == "NotImplementedError": - issues.append( - f"{filepath}:{node.lineno} - Function '{node.name}' raises NotImplementedError" - ) - - return issues - - -def main(): - """Main function.""" - pkg_dir = Path("pkg/hanzo-mcp") - if not pkg_dir.exists(): - pkg_dir = Path(".") - - all_issues = [] - - # Check all Python files - for pyfile in pkg_dir.rglob("*.py"): - # Skip test files - if "test" in str(pyfile) or "__pycache__" in str(pyfile): - continue - - issues = check_file(pyfile) - all_issues.extend(issues) - - if all_issues: - print("โŒ EMPTY/STUB FUNCTIONS FOUND:") - for issue in all_issues: - print(f" {issue}") - print(f"\n๐Ÿšซ Found {len(all_issues)} empty/stub functions. Implement them!") - sys.exit(1) - - print("โœ… No empty/stub functions found") - sys.exit(0) - - -if __name__ == "__main__": - main() diff --git a/scripts/check_packages.py b/scripts/check_packages.py deleted file mode 100755 index 3ba365713..000000000 --- a/scripts/check_packages.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env python3 -"""Check package configurations in the monorepo.""" - -import sys -import tomllib -from typing import List, Tuple -from pathlib import Path - -# ANSI color codes -CYAN = "\033[0;36m" -GREEN = "\033[0;32m" -RED = "\033[0;31m" -YELLOW = "\033[0;33m" -NC = "\033[0m" # No Color - - -def check_package(pkg_path: Path) -> Tuple[str, List[str], List[str]]: - """Check a single package configuration.""" - issues = [] - warnings = [] - - pyproject_path = pkg_path / "pyproject.toml" - - if not pyproject_path.exists(): - return str(pkg_path), [f"Missing pyproject.toml"], warnings - - try: - with open(pyproject_path, "rb") as f: - config = tomllib.load(f) - except Exception as e: - return str(pkg_path), [f"Failed to parse pyproject.toml: {e}"], warnings - - # Check project metadata - project = config.get("project", {}) - if not project.get("name"): - issues.append("Missing project.name") - if not project.get("version"): - issues.append("Missing project.version") - if not project.get("description"): - warnings.append("Missing project.description") - - # Check build system - build_system = config.get("build-system", {}) - if not build_system: - issues.append("Missing [build-system] section") - else: - backend = build_system.get("build-backend") - if not backend: - issues.append("Missing build-backend") - elif backend not in ["hatchling.build", "setuptools.build_meta"]: - warnings.append(f"Non-standard build backend: {backend}") - - # Check Python version requirement - requires_python = project.get("requires-python") - if not requires_python: - warnings.append("Missing requires-python") - - # Check for dist directory (built packages) - dist_path = pkg_path / "dist" - if not dist_path.exists(): - warnings.append("Package not built (no dist/ directory)") - - # Check for tests - test_paths = [pkg_path / "tests", pkg_path / "test"] - if not any(p.exists() for p in test_paths): - warnings.append("No tests directory found") - - # Check package name consistency - pkg_name = project.get("name", "") - expected_name = pkg_path.name - if pkg_name and expected_name.startswith("hanzo-") and pkg_name != expected_name: - issues.append(f"Package name mismatch: {pkg_name} != {expected_name}") - - return str(pkg_path), issues, warnings - - -def main(): - """Check all packages in the monorepo.""" - print(f"{CYAN}Checking package configurations...{NC}\n") - - # Find all packages - pkg_dir = Path("pkg") - packages = [] - - # Add main package - packages.append(Path(".")) - - # Add all subdirectories in pkg/ - if pkg_dir.exists(): - for item in pkg_dir.iterdir(): - if item.is_dir() and (item / "pyproject.toml").exists(): - packages.append(item) - - all_issues = [] - all_warnings = [] - - for pkg_path in sorted(packages): - pkg_name, issues, warnings = check_package(pkg_path) - - print(f"{GREEN}๐Ÿ“ฆ {pkg_name}{NC}") - - if issues: - all_issues.extend([(pkg_name, issue) for issue in issues]) - for issue in issues: - print(f" {RED}โœ— {issue}{NC}") - - if warnings: - all_warnings.extend([(pkg_name, warning) for warning in warnings]) - for warning in warnings: - print(f" {YELLOW}โš  {warning}{NC}") - - if not issues and not warnings: - print(f" {GREEN}โœ“ All checks passed{NC}") - - print() - - # Summary - print(f"\n{CYAN}Summary:{NC}") - print(f" Packages checked: {len(packages)}") - print(f" Issues found: {len(all_issues)}") - print(f" Warnings found: {len(all_warnings)}") - - if all_issues: - print(f"\n{RED}โŒ Fix these issues before publishing:{NC}") - for pkg, issue in all_issues: - print(f" {pkg}: {issue}") - sys.exit(1) - else: - print(f"\n{GREEN}โœ… All packages ready for publishing!{NC}") - - if all_warnings: - print(f"\n{YELLOW}โš ๏ธ Consider addressing these warnings:{NC}") - for pkg, warning in all_warnings[:5]: # Show first 5 warnings - print(f" {pkg}: {warning}") - if len(all_warnings) > 5: - print(f" ... and {len(all_warnings) - 5} more") - - -if __name__ == "__main__": - main() diff --git a/scripts/mock b/scripts/mock index d2814ae6a..0b28f6ea2 100755 --- a/scripts/mock +++ b/scripts/mock @@ -21,7 +21,7 @@ echo "==> Starting mock server with URL ${URL}" # Run prism mock on the given spec if [ "$1" == "--daemon" ]; then - npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" &> .prism.log & + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & # Wait for server to come online echo -n "Waiting for server" @@ -37,5 +37,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" fi diff --git a/scripts/publish-all.sh b/scripts/publish-all.sh deleted file mode 100755 index 820ad922c..000000000 --- a/scripts/publish-all.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/bash - -# Hanzo AI SDK - Complete Package Publishing Script -# Publishes all packages in the monorepo to PyPI - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -echo -e "${BLUE}====================================${NC}" -echo -e "${BLUE}Hanzo AI SDK - Publishing All Packages${NC}" -echo -e "${BLUE}====================================${NC}" -echo - -# Check if we're in the right directory -if [ ! -f "pyproject.toml" ] || [ ! -d "pkg" ]; then - echo -e "${RED}Error: Must be run from the python-sdk root directory${NC}" - exit 1 -fi - -# Check for PYPI_TOKEN -if [ -z "$PYPI_TOKEN" ]; then - echo -e "${RED}Error: PYPI_TOKEN environment variable not set${NC}" - echo "Set it with: export PYPI_TOKEN='your-token'" - exit 1 -fi - -# Packages to publish (in order) -PACKAGES=("hanzoai" "hanzo" "hanzo-mcp" "hanzo-memory" "hanzo-network" "hanzo-agents" "hanzo-dev") - -echo -e "${YELLOW}Packages to publish:${NC}" -for pkg in "${PACKAGES[@]}"; do - echo -e " โ€ข $pkg" -done -echo - -# Function to publish a package -publish_package() { - local pkg_name=$1 - local pkg_dir="" - - # Find package directory - if [ -f "pyproject.toml" ] && grep -q "name = \"$pkg_name\"" pyproject.toml; then - pkg_dir="." - elif [ -d "pkg/$pkg_name" ] && [ -f "pkg/$pkg_name/pyproject.toml" ]; then - pkg_dir="pkg/$pkg_name" - else - echo -e "${YELLOW}Skipping $pkg_name - not found${NC}" - return 0 - fi - - echo -e "${BLUE}Publishing $pkg_name...${NC}" - - # Clean previous builds - rm -rf "$pkg_dir/dist/" "$pkg_dir/build/" "$pkg_dir"/*.egg-info 2>/dev/null || true - - # Build package - (cd "$pkg_dir" && python -m build) - - # Upload to PyPI - (cd "$pkg_dir" && python -m twine upload --username __token__ --password "$PYPI_TOKEN" dist/*) - - echo -e "${GREEN}โœ“ $pkg_name published successfully${NC}" - echo -} - -# Publish each package -for pkg in "${PACKAGES[@]}"; do - publish_package "$pkg" -done - -echo -e "${GREEN}====================================${NC}" -echo -e "${GREEN}All packages published successfully!${NC}" -echo -e "${GREEN}====================================${NC}" -echo -echo "Install with:" -echo -e "${YELLOW} pip install hanzo[all] # Complete ecosystem${NC}" -echo -e "${YELLOW} pip install hanzoai # AI client library${NC}" -echo -e "${YELLOW} pip install hanzo-mcp # MCP development tools${NC}" -echo \ No newline at end of file diff --git a/scripts/publish-single.sh b/scripts/publish-single.sh deleted file mode 100755 index 277c4daeb..000000000 --- a/scripts/publish-single.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/bash - -# Hanzo AI SDK - Single Package Publishing Script -# Usage: ./scripts/publish-single.sh package-name - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -if [ $# -eq 0 ]; then - echo -e "${RED}Usage: $0 ${NC}" - echo "Available packages:" - echo " โ€ข hanzoai (main AI client)" - echo " โ€ข hanzo (CLI and network)" - echo " โ€ข hanzo-mcp (MCP tools)" - echo " โ€ข hanzo-memory (memory systems)" - echo " โ€ข hanzo-network (distributed compute)" - echo " โ€ข hanzo-agents (multi-agent workflows)" - echo " โ€ข hanzo-dev (interactive dev environment)" - exit 1 -fi - -PKG_NAME=$1 - -echo -e "${BLUE}====================================${NC}" -echo -e "${BLUE}Publishing $PKG_NAME${NC}" -echo -e "${BLUE}====================================${NC}" -echo - -# Check for PYPI_TOKEN -if [ -z "$PYPI_TOKEN" ]; then - echo -e "${RED}Error: PYPI_TOKEN environment variable not set${NC}" - echo "Set it with: export PYPI_TOKEN='your-token'" - exit 1 -fi - -# Find package directory -PKG_DIR="" -if [ -f "pyproject.toml" ] && grep -q "name = \"$PKG_NAME\"" pyproject.toml; then - PKG_DIR="." -elif [ -d "pkg/$PKG_NAME" ] && [ -f "pkg/$PKG_NAME/pyproject.toml" ]; then - PKG_DIR="pkg/$PKG_NAME" -else - echo -e "${RED}Error: Package $PKG_NAME not found${NC}" - exit 1 -fi - -echo -e "${YELLOW}Package directory: $PKG_DIR${NC}" - -# Get current version -VERSION=$(cd "$PKG_DIR" && python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])") -echo -e "${YELLOW}Current version: $VERSION${NC}" -echo - -# Clean previous builds -echo "Cleaning previous builds..." -rm -rf "$PKG_DIR/dist/" "$PKG_DIR/build/" "$PKG_DIR"/*.egg-info 2>/dev/null || true - -# Build package -echo "Building package..." -(cd "$PKG_DIR" && python -m build) - -# Show built files -echo "Built files:" -ls -la "$PKG_DIR/dist/" -echo - -# Upload to PyPI -echo "Uploading to PyPI..." -(cd "$PKG_DIR" && python -m twine upload --username __token__ --password "$PYPI_TOKEN" dist/*) - -echo -echo -e "${GREEN}====================================${NC}" -echo -e "${GREEN}$PKG_NAME $VERSION published successfully!${NC}" -echo -e "${GREEN}====================================${NC}" -echo -echo -e "${YELLOW}Install with: pip install $PKG_NAME${NC}" -echo \ No newline at end of file diff --git a/scripts/test b/scripts/test index 8babef619..dbeda2d21 100755 --- a/scripts/test +++ b/scripts/test @@ -9,19 +9,53 @@ GREEN='\033[0;32m' YELLOW='\033[0;33m' NC='\033[0m' # No Color -# No longer need Prism server - using respx mocks instead -echo -e "${GREEN}โœ” Using respx mocks for testing (no external server required)${NC}" -echo +function prism_is_running() { + curl --silent "http://localhost:4010" >/dev/null 2>&1 +} + +kill_server_on_port() { + pids=$(lsof -t -i tcp:"$1" || echo "") + if [ "$pids" != "" ]; then + kill "$pids" + echo "Stopped $pids." + fi +} + +function is_overriding_api_base_url() { + [ -n "$TEST_API_BASE_URL" ] +} + +if ! is_overriding_api_base_url && ! prism_is_running ; then + # When we exit this script, make sure to kill the background mock server process + trap 'kill_server_on_port 4010' EXIT + + # Start the dev server + ./scripts/mock --daemon +fi + +if is_overriding_api_base_url ; then + echo -e "${GREEN}โœ” Running tests against ${TEST_API_BASE_URL}${NC}" + echo +elif ! prism_is_running ; then + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" + echo -e "running against your OpenAPI spec." + echo + echo -e "To run the server, pass in the path or url of your OpenAPI" + echo -e "spec to the prism command:" + echo + echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" + echo + + exit 1 +else + echo -e "${GREEN}โœ” Mock prism server is running with your OpenAPI spec${NC}" + echo +fi export DEFER_PYDANTIC_BUILD=false echo "==> Running tests" rye run pytest "$@" -# Skip Pydantic v1 tests if nox is not available -if command -v nox >/dev/null 2>&1 || rye run python -c "import nox" 2>/dev/null; then - echo "==> Running Pydantic v1 tests" - rye run nox -s test-pydantic-v1 -- "$@" -else - echo "==> Skipping Pydantic v1 tests (nox not available)" -fi +echo "==> Running Pydantic v1 tests" +rye run nox -s test-pydantic-v1 -- "$@" diff --git a/scripts/utils/ruffen-docs.py b/scripts/utils/ruffen-docs.py index 4b809c544..0cf2bd2fd 100644 --- a/scripts/utils/ruffen-docs.py +++ b/scripts/utils/ruffen-docs.py @@ -10,15 +10,11 @@ from typing import Match, Optional, Sequence, Generator, NamedTuple, cast MD_RE = re.compile( - r"(?P^(?P *)```\s*python\n)" - r"(?P.*?)" - r"(?P^(?P=indent)```\s*$)", + r"(?P^(?P *)```\s*python\n)" r"(?P.*?)" r"(?P^(?P=indent)```\s*$)", re.DOTALL | re.MULTILINE, ) MD_PYCON_RE = re.compile( - r"(?P^(?P *)```\s*pycon\n)" - r"(?P.*?)" - r"(?P^(?P=indent)```.*$)", + r"(?P^(?P *)```\s*pycon\n)" r"(?P.*?)" r"(?P^(?P=indent)```.*$)", re.DOTALL | re.MULTILINE, ) PYCON_PREFIX = ">>> " diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh new file mode 100755 index 000000000..cb7ad79c4 --- /dev/null +++ b/scripts/utils/upload-artifact.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -exuo pipefail + +FILENAME=$(basename dist/*.whl) + +RESPONSE=$(curl -X POST "$URL?filename=$FILENAME" \ + -H "Authorization: Bearer $AUTH" \ + -H "Content-Type: application/json") + +SIGNED_URL=$(echo "$RESPONSE" | jq -r '.url') + +if [[ "$SIGNED_URL" == "null" ]]; then + echo -e "\033[31mFailed to get signed URL.\033[0m" + exit 1 +fi + +UPLOAD_RESPONSE=$(curl -v -X PUT \ + -H "Content-Type: binary/octet-stream" \ + --data-binary "@dist/$FILENAME" "$SIGNED_URL" 2>&1) + +if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then + echo -e "\033[32mUploaded build to Stainless storage.\033[0m" + echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/hanzo-ai-python/$SHA/$FILENAME'\033[0m" +else + echo -e "\033[31mFailed to upload artifact.\033[0m" + exit 1 +fi diff --git a/src/Hanzo_AI/lib/.keep b/src/Hanzo_AI/lib/.keep new file mode 100644 index 000000000..5e2c99fdb --- /dev/null +++ b/src/Hanzo_AI/lib/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store custom files to expand the SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/src/hanzoai/__init__.py b/src/hanzoai/__init__.py new file mode 100644 index 000000000..a48f4b275 --- /dev/null +++ b/src/hanzoai/__init__.py @@ -0,0 +1,104 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import typing as _t + +from . import types +from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given +from ._utils import file_from_path +from ._client import ( + ENVIRONMENTS, + Hanzo, + Client, + Stream, + Timeout, + Transport, + AsyncHanzo, + AsyncClient, + AsyncStream, + RequestOptions, +) +from ._models import BaseModel +from ._version import __title__, __version__ +from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse +from ._constants import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_CONNECTION_LIMITS +from ._exceptions import ( + APIError, + HanzoError, + ConflictError, + NotFoundError, + APIStatusError, + RateLimitError, + APITimeoutError, + BadRequestError, + APIConnectionError, + AuthenticationError, + InternalServerError, + PermissionDeniedError, + UnprocessableEntityError, + APIResponseValidationError, +) +from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient +from ._utils._logs import setup_logging as _setup_logging + +__all__ = [ + "types", + "__version__", + "__title__", + "NoneType", + "Transport", + "ProxiesTypes", + "NotGiven", + "NOT_GIVEN", + "not_given", + "Omit", + "omit", + "HanzoError", + "APIError", + "APIStatusError", + "APITimeoutError", + "APIConnectionError", + "APIResponseValidationError", + "BadRequestError", + "AuthenticationError", + "PermissionDeniedError", + "NotFoundError", + "ConflictError", + "UnprocessableEntityError", + "RateLimitError", + "InternalServerError", + "Timeout", + "RequestOptions", + "Client", + "AsyncClient", + "Stream", + "AsyncStream", + "Hanzo", + "AsyncHanzo", + "ENVIRONMENTS", + "file_from_path", + "BaseModel", + "DEFAULT_TIMEOUT", + "DEFAULT_MAX_RETRIES", + "DEFAULT_CONNECTION_LIMITS", + "DefaultHttpxClient", + "DefaultAsyncHttpxClient", + "DefaultAioHttpClient", +] + +if not _t.TYPE_CHECKING: + from ._utils._resources_proxy import resources as resources + +_setup_logging() + +# Update the __module__ attribute for exported symbols so that +# error messages point to this module instead of the module +# it was originally defined in, e.g. +# hanzoai._exceptions.NotFoundError -> hanzoai.NotFoundError +__locals = locals() +for __name in __all__: + if not __name.startswith("__"): + try: + __locals[__name].__module__ = "hanzoai" + except (TypeError, AttributeError): + # Some of our exported symbols are builtins which we can't set attributes for. + pass diff --git a/pkg/hanzoai/_base_client.py b/src/hanzoai/_base_client.py similarity index 76% rename from pkg/hanzoai/_base_client.py rename to src/hanzoai/_base_client.py index 49bacc9d6..13615c8c6 100644 --- a/pkg/hanzoai/_base_client.py +++ b/src/hanzoai/_base_client.py @@ -42,7 +42,6 @@ from ._qs import Querystring from ._files import to_httpx_files, async_to_httpx_files from ._types import ( - NOT_GIVEN, Body, Omit, Query, @@ -57,9 +56,10 @@ RequestOptions, HttpxRequestFiles, ModelBuilderProtocol, + not_given, ) from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping -from ._compat import PYDANTIC_V2, model_copy, model_dump +from ._compat import PYDANTIC_V1, model_copy, model_dump from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type from ._response import ( APIResponse, @@ -98,7 +98,11 @@ _AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any]) if TYPE_CHECKING: - from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT + from httpx._config import ( + DEFAULT_TIMEOUT_CONFIG, # pyright: ignore[reportPrivateImportUsage] + ) + + HTTPX_DEFAULT_TIMEOUT = DEFAULT_TIMEOUT_CONFIG else: try: from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT @@ -115,6 +119,7 @@ class PageInfo: url: URL | NotGiven params: Query | NotGiven + json: Body | NotGiven @overload def __init__( @@ -130,19 +135,30 @@ def __init__( params: Query, ) -> None: ... + @overload + def __init__( + self, + *, + json: Body, + ) -> None: ... + def __init__( self, *, - url: URL | NotGiven = NOT_GIVEN, - params: Query | NotGiven = NOT_GIVEN, + url: URL | NotGiven = not_given, + json: Body | NotGiven = not_given, + params: Query | NotGiven = not_given, ) -> None: self.url = url + self.json = json self.params = params @override def __repr__(self) -> str: if self.url: return f"{self.__class__.__name__}(url={self.url})" + if self.json: + return f"{self.__class__.__name__}(json={self.json})" return f"{self.__class__.__name__}(params={self.params})" @@ -191,6 +207,19 @@ def _info_to_options(self, info: PageInfo) -> FinalRequestOptions: options.url = str(url) return options + if not isinstance(info.json, NotGiven): + if not is_mapping(info.json): + raise TypeError("Pagination is only supported with mappings") + + if not options.json_data: + options.json_data = {**info.json} + else: + if not is_mapping(options.json_data): + raise TypeError("Pagination is only supported with mappings") + + options.json_data = {**options.json_data, **info.json} + return options + raise ValueError("Unexpected PageInfo state") @@ -203,7 +232,7 @@ def _set_private_attributes( model: Type[_T], options: FinalRequestOptions, ) -> None: - if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: self.__pydantic_private__ = {} self._model = model @@ -240,9 +269,7 @@ def get_next_page(self: SyncPageT) -> SyncPageT: ) options = self._info_to_options(info) - return self._client._request_api_list( - self._model, page=self.__class__, options=options - ) + return self._client._request_api_list(self._model, page=self.__class__, options=options) class AsyncPaginator(Generic[_T, AsyncPageT]): @@ -293,7 +320,7 @@ def _set_private_attributes( client: AsyncAPIClient, options: FinalRequestOptions, ) -> None: - if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: self.__pydantic_private__ = {} self._model = model @@ -322,9 +349,7 @@ async def get_next_page(self: AsyncPageT) -> AsyncPageT: ) options = self._info_to_options(info) - return await self._client._request_api_list( - self._model, page=self.__class__, options=options - ) + return await self._client._request_api_list(self._model, page=self.__class__, options=options) _HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) @@ -403,9 +428,7 @@ def _make_status_error( ) -> _exceptions.APIStatusError: raise NotImplementedError() - def _build_headers( - self, options: FinalRequestOptions, *, retries_taken: int = 0 - ) -> httpx.Headers: + def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx.Headers: custom_headers = options.headers or {} headers_dict = _merge_mappings(self.default_headers, custom_headers) self._validate_headers(headers_dict, custom_headers) @@ -414,30 +437,20 @@ def _build_headers( headers = httpx.Headers(headers_dict) idempotency_header = self._idempotency_header - if ( - idempotency_header - and options.method.lower() != "get" - and idempotency_header not in headers - ): - headers[idempotency_header] = ( - options.idempotency_key or self._idempotency_key() - ) + if idempotency_header and options.idempotency_key and idempotency_header not in headers: + headers[idempotency_header] = options.idempotency_key # Don't set these headers if they were already set or removed by the caller. We check # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case. lower_custom_headers = [header.lower() for header in custom_headers] - if "x-sdk-retry-count" not in lower_custom_headers: - headers["x-sdk-retry-count"] = str(retries_taken) - if "x-sdk-read-timeout" not in lower_custom_headers: - timeout = ( - self.timeout - if isinstance(options.timeout, NotGiven) - else options.timeout - ) + if "x-stainless-retry-count" not in lower_custom_headers: + headers["x-stainless-retry-count"] = str(retries_taken) + if "x-stainless-read-timeout" not in lower_custom_headers: + timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout if isinstance(timeout, Timeout): timeout = timeout.read if timeout is not None: - headers["x-sdk-read-timeout"] = str(timeout) + headers["x-stainless-read-timeout"] = str(timeout) return headers @@ -475,9 +488,7 @@ def _build_request( elif is_mapping(json_data): json_data = _merge_mappings(json_data, options.extra_json) else: - raise RuntimeError( - f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`" - ) + raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`") headers = self._build_headers(options, retries_taken=retries_taken) params = _merge_mappings(self.default_query, options.params) @@ -518,31 +529,33 @@ def _build_request( # work around https://github.com/encode/httpx/discussions/2880 kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} + is_body_allowed = options.method.lower() != "get" + + if is_body_allowed: + if isinstance(json_data, bytes): + kwargs["content"] = json_data + else: + kwargs["json"] = json_data if is_given(json_data) else None + kwargs["files"] = files + else: + headers.pop("Content-Type", None) + kwargs.pop("data", None) + # TODO: report this error to httpx return self._client.build_request( # pyright: ignore[reportUnknownMemberType] headers=headers, - timeout=( - self.timeout - if isinstance(options.timeout, NotGiven) - else options.timeout - ), + timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout, method=options.method, url=prepared_url, # the `Query` type that we use is incompatible with qs' # `Params` type as it needs to be typed as `Mapping[str, object]` # so that passing a `TypedDict` doesn't cause an error. # https://github.com/microsoft/pyright/issues/3526#event-6715453066 - params=( - self.qs.stringify(cast(Mapping[str, Any], params)) if params else None - ), - json=json_data if is_given(json_data) else None, - files=files, + params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, **kwargs, ) - def _serialize_multipartform( - self, data: Mapping[object, object] - ) -> dict[str, object]: + def _serialize_multipartform(self, data: Mapping[object, object]) -> dict[str, object]: items = self.qs.stringify_items( # TODO: type ignore is required as stringify_items is well typed but we can't be # well typed without heavy validation. @@ -572,9 +585,7 @@ def _serialize_multipartform( return serialized - def _maybe_override_cast_to( - self, cast_to: type[ResponseT], options: FinalRequestOptions - ) -> type[ResponseT]: + def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalRequestOptions) -> type[ResponseT]: if not is_given(options.headers): return cast_to @@ -584,7 +595,7 @@ def _maybe_override_cast_to( # we internally support defining a temporary header to override the # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response` # see _response.py for implementation details - override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, NOT_GIVEN) + override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, not_given) if is_given(override_cast_to): options.headers = headers return cast(Type[ResponseT], override_cast_to) @@ -668,9 +679,7 @@ def base_url(self) -> URL: @base_url.setter def base_url(self, url: URL | str) -> None: - self._base_url = self._enforce_trailing_slash( - url if isinstance(url, URL) else URL(url) - ) + self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url)) def platform_headers(self) -> Dict[str, str]: # the actual implementation is in a separate `lru_cache` decorated @@ -678,9 +687,7 @@ def platform_headers(self) -> Dict[str, str]: # https://github.com/python/cpython/issues/88476 return platform_headers(self._version, platform=self._platform) - def _parse_retry_after_header( - self, response_headers: Optional[httpx.Headers] = None - ) -> float | None: + def _parse_retry_after_header(self, response_headers: Optional[httpx.Headers] = None) -> float | None: """Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified. About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After @@ -774,7 +781,7 @@ def _should_retry(self, response: httpx.Response) -> bool: return False def _idempotency_key(self) -> str: - return f"hanzo-python-retry-{uuid.uuid4()}" + return f"stainless-python-retry-{uuid.uuid4()}" class _DefaultHttpxClient(httpx.Client): @@ -818,7 +825,7 @@ def __init__( version: str, base_url: str | URL, max_retries: int = DEFAULT_MAX_RETRIES, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, @@ -837,9 +844,7 @@ def __init__( else: timeout = DEFAULT_TIMEOUT - if http_client is not None and not isinstance( - http_client, httpx.Client - ): # pyright: ignore[reportUnnecessaryIsInstance] + if http_client is not None and not isinstance(http_client, httpx.Client): # pyright: ignore[reportUnnecessaryIsInstance] raise TypeError( f"Invalid `http_client` argument; Expected an instance of `httpx.Client` but got {type(http_client)}" ) @@ -907,7 +912,6 @@ def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, - remaining_retries: Optional[int] = None, *, stream: Literal[True], stream_cls: Type[_StreamT], @@ -918,7 +922,6 @@ def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, - remaining_retries: Optional[int] = None, *, stream: Literal[False] = False, ) -> ResponseT: ... @@ -928,7 +931,6 @@ def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, - remaining_retries: Optional[int] = None, *, stream: bool = False, stream_cls: Type[_StreamT] | None = None, @@ -938,123 +940,112 @@ def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, - remaining_retries: Optional[int] = None, *, stream: bool = False, stream_cls: type[_StreamT] | None = None, ) -> ResponseT | _StreamT: - if remaining_retries is not None: - retries_taken = ( - options.get_max_retries(self.max_retries) - remaining_retries - ) - else: - retries_taken = 0 - - return self._request( - cast_to=cast_to, - options=options, - stream=stream, - stream_cls=stream_cls, - retries_taken=retries_taken, - ) + cast_to = self._maybe_override_cast_to(cast_to, options) - def _request( - self, - *, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - retries_taken: int, - stream: bool, - stream_cls: type[_StreamT] | None, - ) -> ResponseT | _StreamT: # create a copy of the options we were given so that if the # options are mutated later & we then retry, the retries are # given the original options input_options = model_copy(options) + if input_options.idempotency_key is None and input_options.method.lower() != "get": + # ensure the idempotency key is reused between requests + input_options.idempotency_key = self._idempotency_key() - cast_to = self._maybe_override_cast_to(cast_to, options) - options = self._prepare_options(options) + response: httpx.Response | None = None + max_retries = input_options.get_max_retries(self.max_retries) - remaining_retries = options.get_max_retries(self.max_retries) - retries_taken - request = self._build_request(options, retries_taken=retries_taken) - self._prepare_request(request) + retries_taken = 0 + for retries_taken in range(max_retries + 1): + options = model_copy(input_options) + options = self._prepare_options(options) - kwargs: HttpxSendArgs = {} - if self.custom_auth is not None: - kwargs["auth"] = self.custom_auth + remaining_retries = max_retries - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + self._prepare_request(request) - log.debug("Sending HTTP Request: %s %s", request.method, request.url) + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth - try: - response = self._client.send( - request, - stream=stream or self._should_stream_response_body(request=request), - **kwargs, - ) - except httpx.TimeoutException as err: - log.debug("Encountered httpx.TimeoutException", exc_info=True) - - if remaining_retries > 0: - return self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - stream=stream, - stream_cls=stream_cls, - response_headers=None, - ) + if options.follow_redirects is not None: + kwargs["follow_redirects"] = options.follow_redirects - log.debug("Raising timeout error") - raise APITimeoutError(request=request) from err - except Exception as err: - log.debug("Encountered Exception", exc_info=True) + log.debug("Sending HTTP Request: %s %s", request.method, request.url) - if remaining_retries > 0: - return self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - stream=stream, - stream_cls=stream_cls, - response_headers=None, + response = None + try: + response = self._client.send( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, ) + except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + + if remaining_retries > 0: + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except Exception as err: + log.debug("Encountered Exception", exc_info=True) + + if remaining_retries > 0: + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + 'HTTP Response: %s %s "%i %s" %s', + request.method, + request.url, + response.status_code, + response.reason_phrase, + response.headers, + ) - log.debug("Raising connection error") - raise APIConnectionError(request=request) from err - - log.debug( - 'HTTP Response: %s %s "%i %s" %s', - request.method, - request.url, - response.status_code, - response.reason_phrase, - response.headers, - ) + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + + if remaining_retries > 0 and self._should_retry(err.response): + err.response.close() + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=response, + ) + continue - try: - response.raise_for_status() - except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code - log.debug("Encountered httpx.HTTPStatusError", exc_info=True) - - if remaining_retries > 0 and self._should_retry(err.response): - err.response.close() - return self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - response_headers=err.response.headers, - stream=stream, - stream_cls=stream_cls, - ) + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + err.response.read() - # If the response is streamed then we need to explicitly read the response - # to completion before attempting to access the response text. - if not err.response.is_closed: - err.response.read() + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None - log.debug("Re-raising status error") - raise self._make_status_error_from_response(err.response) from None + break + assert response is not None, "could not resolve response (should never happen)" return self._process_response( cast_to=cast_to, options=options, @@ -1064,39 +1055,20 @@ def _request( retries_taken=retries_taken, ) - def _retry_request( - self, - options: FinalRequestOptions, - cast_to: Type[ResponseT], - *, - retries_taken: int, - response_headers: httpx.Headers | None, - stream: bool, - stream_cls: type[_StreamT] | None, - ) -> ResponseT | _StreamT: - remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + def _sleep_for_retry( + self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None + ) -> None: + remaining_retries = max_retries - retries_taken if remaining_retries == 1: log.debug("1 retry left") else: log.debug("%i retries left", remaining_retries) - timeout = self._calculate_retry_timeout( - remaining_retries, options, response_headers - ) + timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) log.info("Retrying request to %s in %f seconds", options.url, timeout) - # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a - # different thread if necessary. time.sleep(timeout) - return self._request( - options=options, - cast_to=cast_to, - retries_taken=retries_taken + 1, - stream=stream, - stream_cls=stream_cls, - ) - def _process_response( self, *, @@ -1109,11 +1081,16 @@ def _process_response( ) -> ResponseT: origin = get_origin(cast_to) or cast_to - if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse): + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): if not issubclass(origin, APIResponse): - raise TypeError( - f"API Response types must subclass {APIResponse}; Received {origin}" - ) + raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}") response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) return cast( @@ -1135,9 +1112,7 @@ def _process_response( api_response = APIResponse( raw=response, client=self, - cast_to=cast( - "type[ResponseT]", cast_to - ), # pyright: ignore[reportUnnecessaryCast] + cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] stream=stream, stream_cls=stream_cls, options=options, @@ -1210,9 +1185,7 @@ def get( opts = FinalRequestOptions.construct(method="get", url=path, **options) # cast is required because mypy complains about returning Any even though # it understands the type variables - return cast( - ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) - ) + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) @overload def post( @@ -1264,15 +1237,9 @@ def post( stream_cls: type[_StreamT] | None = None, ) -> ResponseT | _StreamT: opts = FinalRequestOptions.construct( - method="post", - url=path, - json_data=body, - files=to_httpx_files(files), - **options, - ) - return cast( - ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) + method="post", url=path, json_data=body, files=to_httpx_files(files), **options ) + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) def patch( self, @@ -1282,9 +1249,7 @@ def patch( body: Body | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct( - method="patch", url=path, json_data=body, **options - ) + opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) return self.request(cast_to, opts) def put( @@ -1297,11 +1262,7 @@ def put( options: RequestOptions = {}, ) -> ResponseT: opts = FinalRequestOptions.construct( - method="put", - url=path, - json_data=body, - files=to_httpx_files(files), - **options, + method="put", url=path, json_data=body, files=to_httpx_files(files), **options ) return self.request(cast_to, opts) @@ -1313,9 +1274,7 @@ def delete( body: Body | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct( - method="delete", url=path, json_data=body, **options - ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) return self.request(cast_to, opts) def get_api_list( @@ -1328,9 +1287,7 @@ def get_api_list( options: RequestOptions = {}, method: str = "get", ) -> SyncPageT: - opts = FinalRequestOptions.construct( - method=method, url=path, json_data=body, **options - ) + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) return self._request_api_list(model, page, opts) @@ -1342,6 +1299,24 @@ def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) +try: + import httpx_aiohttp +except ImportError: + + class _DefaultAioHttpClient(httpx.AsyncClient): + def __init__(self, **_kwargs: Any) -> None: + raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra") +else: + + class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + + super().__init__(**kwargs) + + if TYPE_CHECKING: DefaultAsyncHttpxClient = httpx.AsyncClient """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK @@ -1350,8 +1325,12 @@ def __init__(self, **kwargs: Any) -> None: This is useful because overriding the `http_client` with your own instance of `httpx.AsyncClient` will result in httpx's defaults being used, not ours. """ + + DefaultAioHttpClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`.""" else: DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient + DefaultAioHttpClient = _DefaultAioHttpClient class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): @@ -1377,7 +1356,7 @@ def __init__( base_url: str | URL, _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, @@ -1395,9 +1374,7 @@ def __init__( else: timeout = DEFAULT_TIMEOUT - if http_client is not None and not isinstance( - http_client, httpx.AsyncClient - ): # pyright: ignore[reportUnnecessaryIsInstance] + if http_client is not None and not isinstance(http_client, httpx.AsyncClient): # pyright: ignore[reportUnnecessaryIsInstance] raise TypeError( f"Invalid `http_client` argument; Expected an instance of `httpx.AsyncClient` but got {type(http_client)}" ) @@ -1464,7 +1441,6 @@ async def request( options: FinalRequestOptions, *, stream: Literal[False] = False, - remaining_retries: Optional[int] = None, ) -> ResponseT: ... @overload @@ -1475,7 +1451,6 @@ async def request( *, stream: Literal[True], stream_cls: type[_AsyncStreamT], - remaining_retries: Optional[int] = None, ) -> _AsyncStreamT: ... @overload @@ -1486,7 +1461,6 @@ async def request( *, stream: bool, stream_cls: type[_AsyncStreamT] | None = None, - remaining_retries: Optional[int] = None, ) -> ResponseT | _AsyncStreamT: ... async def request( @@ -1496,122 +1470,114 @@ async def request( *, stream: bool = False, stream_cls: type[_AsyncStreamT] | None = None, - remaining_retries: Optional[int] = None, - ) -> ResponseT | _AsyncStreamT: - if remaining_retries is not None: - retries_taken = ( - options.get_max_retries(self.max_retries) - remaining_retries - ) - else: - retries_taken = 0 - - return await self._request( - cast_to=cast_to, - options=options, - stream=stream, - stream_cls=stream_cls, - retries_taken=retries_taken, - ) - - async def _request( - self, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - *, - stream: bool, - stream_cls: type[_AsyncStreamT] | None, - retries_taken: int, ) -> ResponseT | _AsyncStreamT: if self._platform is None: # `get_platform` can make blocking IO calls so we # execute it earlier while we are in an async context self._platform = await asyncify(get_platform)() + cast_to = self._maybe_override_cast_to(cast_to, options) + # create a copy of the options we were given so that if the # options are mutated later & we then retry, the retries are # given the original options input_options = model_copy(options) + if input_options.idempotency_key is None and input_options.method.lower() != "get": + # ensure the idempotency key is reused between requests + input_options.idempotency_key = self._idempotency_key() - cast_to = self._maybe_override_cast_to(cast_to, options) - options = await self._prepare_options(options) + response: httpx.Response | None = None + max_retries = input_options.get_max_retries(self.max_retries) - remaining_retries = options.get_max_retries(self.max_retries) - retries_taken - request = self._build_request(options, retries_taken=retries_taken) - await self._prepare_request(request) + retries_taken = 0 + for retries_taken in range(max_retries + 1): + options = model_copy(input_options) + options = await self._prepare_options(options) - kwargs: HttpxSendArgs = {} - if self.custom_auth is not None: - kwargs["auth"] = self.custom_auth + remaining_retries = max_retries - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + await self._prepare_request(request) - try: - response = await self._client.send( - request, - stream=stream or self._should_stream_response_body(request=request), - **kwargs, - ) - except httpx.TimeoutException as err: - log.debug("Encountered httpx.TimeoutException", exc_info=True) + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth - if remaining_retries > 0: - return await self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - stream=stream, - stream_cls=stream_cls, - response_headers=None, - ) + if options.follow_redirects is not None: + kwargs["follow_redirects"] = options.follow_redirects - log.debug("Raising timeout error") - raise APITimeoutError(request=request) from err - except Exception as err: - log.debug("Encountered Exception", exc_info=True) + log.debug("Sending HTTP Request: %s %s", request.method, request.url) - if remaining_retries > 0: - return await self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - stream=stream, - stream_cls=stream_cls, - response_headers=None, + response = None + try: + response = await self._client.send( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, ) + except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + + if remaining_retries > 0: + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except Exception as err: + log.debug("Encountered Exception", exc_info=True) + + if remaining_retries > 0: + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + 'HTTP Response: %s %s "%i %s" %s', + request.method, + request.url, + response.status_code, + response.reason_phrase, + response.headers, + ) - log.debug("Raising connection error") - raise APIConnectionError(request=request) from err - - log.debug( - 'HTTP Request: %s %s "%i %s"', - request.method, - request.url, - response.status_code, - response.reason_phrase, - ) + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + + if remaining_retries > 0 and self._should_retry(err.response): + await err.response.aclose() + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=response, + ) + continue - try: - response.raise_for_status() - except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code - log.debug("Encountered httpx.HTTPStatusError", exc_info=True) - - if remaining_retries > 0 and self._should_retry(err.response): - await err.response.aclose() - return await self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - response_headers=err.response.headers, - stream=stream, - stream_cls=stream_cls, - ) + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + await err.response.aread() - # If the response is streamed then we need to explicitly read the response - # to completion before attempting to access the response text. - if not err.response.is_closed: - await err.response.aread() + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None - log.debug("Re-raising status error") - raise self._make_status_error_from_response(err.response) from None + break + assert response is not None, "could not resolve response (should never happen)" return await self._process_response( cast_to=cast_to, options=options, @@ -1621,37 +1587,20 @@ async def _request( retries_taken=retries_taken, ) - async def _retry_request( - self, - options: FinalRequestOptions, - cast_to: Type[ResponseT], - *, - retries_taken: int, - response_headers: httpx.Headers | None, - stream: bool, - stream_cls: type[_AsyncStreamT] | None, - ) -> ResponseT | _AsyncStreamT: - remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + async def _sleep_for_retry( + self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None + ) -> None: + remaining_retries = max_retries - retries_taken if remaining_retries == 1: log.debug("1 retry left") else: log.debug("%i retries left", remaining_retries) - timeout = self._calculate_retry_timeout( - remaining_retries, options, response_headers - ) + timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) log.info("Retrying request to %s in %f seconds", options.url, timeout) await anyio.sleep(timeout) - return await self._request( - options=options, - cast_to=cast_to, - retries_taken=retries_taken + 1, - stream=stream, - stream_cls=stream_cls, - ) - async def _process_response( self, *, @@ -1664,11 +1613,16 @@ async def _process_response( ) -> ResponseT: origin = get_origin(cast_to) or cast_to - if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse): + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): if not issubclass(origin, AsyncAPIResponse): - raise TypeError( - f"API Response types must subclass {AsyncAPIResponse}; Received {origin}" - ) + raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}") response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) return cast( @@ -1690,9 +1644,7 @@ async def _process_response( api_response = AsyncAPIResponse( raw=response, client=self, - cast_to=cast( - "type[ResponseT]", cast_to - ), # pyright: ignore[reportUnnecessaryCast] + cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] stream=stream, stream_cls=stream_cls, options=options, @@ -1805,11 +1757,7 @@ async def post( stream_cls: type[_AsyncStreamT] | None = None, ) -> ResponseT | _AsyncStreamT: opts = FinalRequestOptions.construct( - method="post", - url=path, - json_data=body, - files=await async_to_httpx_files(files), - **options, + method="post", url=path, json_data=body, files=await async_to_httpx_files(files), **options ) return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) @@ -1821,9 +1769,7 @@ async def patch( body: Body | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct( - method="patch", url=path, json_data=body, **options - ) + opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) return await self.request(cast_to, opts) async def put( @@ -1836,11 +1782,7 @@ async def put( options: RequestOptions = {}, ) -> ResponseT: opts = FinalRequestOptions.construct( - method="put", - url=path, - json_data=body, - files=await async_to_httpx_files(files), - **options, + method="put", url=path, json_data=body, files=await async_to_httpx_files(files), **options ) return await self.request(cast_to, opts) @@ -1852,9 +1794,7 @@ async def delete( body: Body | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct( - method="delete", url=path, json_data=body, **options - ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) return await self.request(cast_to, opts) def get_api_list( @@ -1867,9 +1807,7 @@ def get_api_list( options: RequestOptions = {}, method: str = "get", ) -> AsyncPaginator[_T, AsyncPageT]: - opts = FinalRequestOptions.construct( - method=method, url=path, json_data=body, **options - ) + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) return self._request_api_list(model, page, opts) @@ -1880,8 +1818,8 @@ def make_request_options( extra_query: Query | None = None, extra_body: Body | None = None, idempotency_key: str | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - post_parser: PostParser | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + post_parser: PostParser | NotGiven = not_given, ) -> RequestOptions: """Create a dict of type RequestOptions without keys of NotGiven values.""" options: RequestOptions = {} @@ -1984,12 +1922,12 @@ def get_platform() -> Platform: @lru_cache(maxsize=None) def platform_headers(version: str, *, platform: Platform | None) -> Dict[str, str]: return { - "X-SDK-Lang": "python", - "X-SDK-Package-Version": version, - "X-SDK-OS": str(platform or get_platform()), - "X-SDK-Arch": str(get_architecture()), - "X-SDK-Runtime": get_python_runtime(), - "X-SDK-Runtime-Version": get_python_version(), + "X-Stainless-Lang": "python", + "X-Stainless-Package-Version": version, + "X-Stainless-OS": str(platform or get_platform()), + "X-Stainless-Arch": str(get_architecture()), + "X-Stainless-Runtime": get_python_runtime(), + "X-Stainless-Runtime-Version": get_python_version(), } diff --git a/src/hanzoai/_client.py b/src/hanzoai/_client.py new file mode 100644 index 000000000..4c5934721 --- /dev/null +++ b/src/hanzoai/_client.py @@ -0,0 +1,950 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, Dict, Mapping, cast +from typing_extensions import Self, Literal, override + +import httpx + +from . import _exceptions +from ._qs import Querystring +from ._types import ( + Body, + Omit, + Query, + Headers, + Timeout, + NotGiven, + Transport, + ProxiesTypes, + RequestOptions, + not_given, +) +from ._utils import is_given, get_async_library +from ._version import __version__ +from ._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .resources import ( + add, + test, + user, + azure, + spend, + utils, + active, + budget, + cohere, + delete, + gemini, + health, + models, + rerank, + routes, + bedrock, + customer, + langfuse, + provider, + settings, + anthropic, + vertex_ai, + assemblyai, + assistants, + embeddings, + guardrails, + completions, + credentials, + model_group, + moderations, + eu_assemblyai, +) +from ._streaming import Stream as Stream, AsyncStream as AsyncStream +from ._exceptions import HanzoError, APIStatusError +from ._base_client import ( + DEFAULT_MAX_RETRIES, + SyncAPIClient, + AsyncAPIClient, + make_request_options, +) +from .resources.key import key +from .resources.chat import chat +from .resources.team import team +from .resources.audio import audio +from .resources.cache import cache +from .resources.files import files +from .resources.model import model +from .resources.config import config +from .resources.images import images +from .resources.openai import openai +from .resources.batches import batches +from .resources.engines import engines +from .resources.global_ import global_ +from .resources.threads import threads +from .resources.responses import responses +from .resources.fine_tuning import fine_tuning +from .resources.organization import organization + +__all__ = [ + "ENVIRONMENTS", + "Timeout", + "Transport", + "ProxiesTypes", + "RequestOptions", + "Hanzo", + "AsyncHanzo", + "Client", + "AsyncClient", +] + +ENVIRONMENTS: Dict[str, str] = { + "production": "https://api.hanzo.ai", + "sandbox": "https://api.sandbox.hanzo.ai", +} + + +class Hanzo(SyncAPIClient): + models: models.ModelsResource + openai: openai.OpenAIResource + engines: engines.EnginesResource + chat: chat.ChatResource + completions: completions.CompletionsResource + embeddings: embeddings.EmbeddingsResource + images: images.ImagesResource + audio: audio.AudioResource + assistants: assistants.AssistantsResource + threads: threads.ThreadsResource + moderations: moderations.ModerationsResource + utils: utils.UtilsResource + model: model.ModelResource + model_group: model_group.ModelGroupResource + routes: routes.RoutesResource + responses: responses.ResponsesResource + batches: batches.BatchesResource + rerank: rerank.RerankResource + fine_tuning: fine_tuning.FineTuningResource + credentials: credentials.CredentialsResource + vertex_ai: vertex_ai.VertexAIResource + gemini: gemini.GeminiResource + cohere: cohere.CohereResource + anthropic: anthropic.AnthropicResource + bedrock: bedrock.BedrockResource + eu_assemblyai: eu_assemblyai.EuAssemblyaiResource + assemblyai: assemblyai.AssemblyaiResource + azure: azure.AzureResource + langfuse: langfuse.LangfuseResource + config: config.ConfigResource + test: test.TestResource + health: health.HealthResource + active: active.ActiveResource + settings: settings.SettingsResource + key: key.KeyResource + user: user.UserResource + team: team.TeamResource + organization: organization.OrganizationResource + customer: customer.CustomerResource + spend: spend.SpendResource + global_: global_.GlobalResource + provider: provider.ProviderResource + cache: cache.CacheResource + guardrails: guardrails.GuardrailsResource + add: add.AddResource + delete: delete.DeleteResource + files: files.FilesResource + budget: budget.BudgetResource + with_raw_response: HanzoWithRawResponse + with_streaming_response: HanzoWithStreamedResponse + + # client options + api_key: str + + _environment: Literal["production", "sandbox"] | NotGiven + + def __init__( + self, + *, + api_key: str | None = None, + environment: Literal["production", "sandbox"] | NotGiven = not_given, + base_url: str | httpx.URL | None | NotGiven = not_given, + timeout: float | Timeout | None | NotGiven = not_given, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + # Configure a custom httpx client. + # We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. + http_client: httpx.Client | None = None, + # Enable or disable schema validation for data returned by the API. + # When enabled an error APIResponseValidationError is raised + # if the API responds with invalid data for the expected schema. + # + # This parameter may be removed or changed in the future. + # If you rely on this feature, please open a GitHub issue + # outlining your use-case to help us decide if it should be + # part of our public interface in the future. + _strict_response_validation: bool = False, + ) -> None: + """Construct a new synchronous Hanzo client instance. + + This automatically infers the `api_key` argument from the `HANZO_API_KEY` environment variable if it is not provided. + """ + if api_key is None: + api_key = os.environ.get("HANZO_API_KEY") + if api_key is None: + raise HanzoError( + "The api_key client option must be set either by passing api_key to the client or by setting the HANZO_API_KEY environment variable" + ) + self.api_key = api_key + + self._environment = environment + + base_url_env = os.environ.get("HANZO_BASE_URL") + if is_given(base_url) and base_url is not None: + # cast required because mypy doesn't understand the type narrowing + base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast] + elif is_given(environment): + if base_url_env and base_url is not None: + raise ValueError( + "Ambiguous URL; The `HANZO_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None", + ) + + try: + base_url = ENVIRONMENTS[environment] + except KeyError as exc: + raise ValueError(f"Unknown environment: {environment}") from exc + elif base_url_env is not None: + base_url = base_url_env + else: + self._environment = environment = "production" + + try: + base_url = ENVIRONMENTS[environment] + except KeyError as exc: + raise ValueError(f"Unknown environment: {environment}") from exc + + super().__init__( + version=__version__, + base_url=base_url, + max_retries=max_retries, + timeout=timeout, + http_client=http_client, + custom_headers=default_headers, + custom_query=default_query, + _strict_response_validation=_strict_response_validation, + ) + + self.models = models.ModelsResource(self) + self.openai = openai.OpenAIResource(self) + self.engines = engines.EnginesResource(self) + self.chat = chat.ChatResource(self) + self.completions = completions.CompletionsResource(self) + self.embeddings = embeddings.EmbeddingsResource(self) + self.images = images.ImagesResource(self) + self.audio = audio.AudioResource(self) + self.assistants = assistants.AssistantsResource(self) + self.threads = threads.ThreadsResource(self) + self.moderations = moderations.ModerationsResource(self) + self.utils = utils.UtilsResource(self) + self.model = model.ModelResource(self) + self.model_group = model_group.ModelGroupResource(self) + self.routes = routes.RoutesResource(self) + self.responses = responses.ResponsesResource(self) + self.batches = batches.BatchesResource(self) + self.rerank = rerank.RerankResource(self) + self.fine_tuning = fine_tuning.FineTuningResource(self) + self.credentials = credentials.CredentialsResource(self) + self.vertex_ai = vertex_ai.VertexAIResource(self) + self.gemini = gemini.GeminiResource(self) + self.cohere = cohere.CohereResource(self) + self.anthropic = anthropic.AnthropicResource(self) + self.bedrock = bedrock.BedrockResource(self) + self.eu_assemblyai = eu_assemblyai.EuAssemblyaiResource(self) + self.assemblyai = assemblyai.AssemblyaiResource(self) + self.azure = azure.AzureResource(self) + self.langfuse = langfuse.LangfuseResource(self) + self.config = config.ConfigResource(self) + self.test = test.TestResource(self) + self.health = health.HealthResource(self) + self.active = active.ActiveResource(self) + self.settings = settings.SettingsResource(self) + self.key = key.KeyResource(self) + self.user = user.UserResource(self) + self.team = team.TeamResource(self) + self.organization = organization.OrganizationResource(self) + self.customer = customer.CustomerResource(self) + self.spend = spend.SpendResource(self) + self.global_ = global_.GlobalResource(self) + self.provider = provider.ProviderResource(self) + self.cache = cache.CacheResource(self) + self.guardrails = guardrails.GuardrailsResource(self) + self.add = add.AddResource(self) + self.delete = delete.DeleteResource(self) + self.files = files.FilesResource(self) + self.budget = budget.BudgetResource(self) + self.with_raw_response = HanzoWithRawResponse(self) + self.with_streaming_response = HanzoWithStreamedResponse(self) + + @property + @override + def qs(self) -> Querystring: + return Querystring(array_format="comma") + + @property + @override + def auth_headers(self) -> dict[str, str]: + api_key = self.api_key + return {"Ocp-Apim-Subscription-Key": api_key} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "X-Stainless-Async": "false", + **self._custom_headers, + } + + def copy( + self, + *, + api_key: str | None = None, + environment: Literal["production", "sandbox"] | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx.Client | None = None, + max_retries: int | NotGiven = not_given, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + """ + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + http_client = http_client or self._client + return self.__class__( + api_key=api_key or self.api_key, + base_url=base_url or self.base_url, + environment=environment or self._environment, + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + **_extra_kwargs, + ) + + # Alias for `copy` for nicer inline usage, e.g. + # client.with_options(timeout=10).foo.create(...) + with_options = copy + + def get_home( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Home""" + return self.get( + "/", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + @override + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> APIStatusError: + if response.status_code == 400: + return _exceptions.BadRequestError(err_msg, response=response, body=body) + + if response.status_code == 401: + return _exceptions.AuthenticationError(err_msg, response=response, body=body) + + if response.status_code == 403: + return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) + + if response.status_code == 404: + return _exceptions.NotFoundError(err_msg, response=response, body=body) + + if response.status_code == 409: + return _exceptions.ConflictError(err_msg, response=response, body=body) + + if response.status_code == 422: + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) + + if response.status_code == 429: + return _exceptions.RateLimitError(err_msg, response=response, body=body) + + if response.status_code >= 500: + return _exceptions.InternalServerError(err_msg, response=response, body=body) + return APIStatusError(err_msg, response=response, body=body) + + +class AsyncHanzo(AsyncAPIClient): + models: models.AsyncModelsResource + openai: openai.AsyncOpenAIResource + engines: engines.AsyncEnginesResource + chat: chat.AsyncChatResource + completions: completions.AsyncCompletionsResource + embeddings: embeddings.AsyncEmbeddingsResource + images: images.AsyncImagesResource + audio: audio.AsyncAudioResource + assistants: assistants.AsyncAssistantsResource + threads: threads.AsyncThreadsResource + moderations: moderations.AsyncModerationsResource + utils: utils.AsyncUtilsResource + model: model.AsyncModelResource + model_group: model_group.AsyncModelGroupResource + routes: routes.AsyncRoutesResource + responses: responses.AsyncResponsesResource + batches: batches.AsyncBatchesResource + rerank: rerank.AsyncRerankResource + fine_tuning: fine_tuning.AsyncFineTuningResource + credentials: credentials.AsyncCredentialsResource + vertex_ai: vertex_ai.AsyncVertexAIResource + gemini: gemini.AsyncGeminiResource + cohere: cohere.AsyncCohereResource + anthropic: anthropic.AsyncAnthropicResource + bedrock: bedrock.AsyncBedrockResource + eu_assemblyai: eu_assemblyai.AsyncEuAssemblyaiResource + assemblyai: assemblyai.AsyncAssemblyaiResource + azure: azure.AsyncAzureResource + langfuse: langfuse.AsyncLangfuseResource + config: config.AsyncConfigResource + test: test.AsyncTestResource + health: health.AsyncHealthResource + active: active.AsyncActiveResource + settings: settings.AsyncSettingsResource + key: key.AsyncKeyResource + user: user.AsyncUserResource + team: team.AsyncTeamResource + organization: organization.AsyncOrganizationResource + customer: customer.AsyncCustomerResource + spend: spend.AsyncSpendResource + global_: global_.AsyncGlobalResource + provider: provider.AsyncProviderResource + cache: cache.AsyncCacheResource + guardrails: guardrails.AsyncGuardrailsResource + add: add.AsyncAddResource + delete: delete.AsyncDeleteResource + files: files.AsyncFilesResource + budget: budget.AsyncBudgetResource + with_raw_response: AsyncHanzoWithRawResponse + with_streaming_response: AsyncHanzoWithStreamedResponse + + # client options + api_key: str + + _environment: Literal["production", "sandbox"] | NotGiven + + def __init__( + self, + *, + api_key: str | None = None, + environment: Literal["production", "sandbox"] | NotGiven = not_given, + base_url: str | httpx.URL | None | NotGiven = not_given, + timeout: float | Timeout | None | NotGiven = not_given, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + # Configure a custom httpx client. + # We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details. + http_client: httpx.AsyncClient | None = None, + # Enable or disable schema validation for data returned by the API. + # When enabled an error APIResponseValidationError is raised + # if the API responds with invalid data for the expected schema. + # + # This parameter may be removed or changed in the future. + # If you rely on this feature, please open a GitHub issue + # outlining your use-case to help us decide if it should be + # part of our public interface in the future. + _strict_response_validation: bool = False, + ) -> None: + """Construct a new async AsyncHanzo client instance. + + This automatically infers the `api_key` argument from the `HANZO_API_KEY` environment variable if it is not provided. + """ + if api_key is None: + api_key = os.environ.get("HANZO_API_KEY") + if api_key is None: + raise HanzoError( + "The api_key client option must be set either by passing api_key to the client or by setting the HANZO_API_KEY environment variable" + ) + self.api_key = api_key + + self._environment = environment + + base_url_env = os.environ.get("HANZO_BASE_URL") + if is_given(base_url) and base_url is not None: + # cast required because mypy doesn't understand the type narrowing + base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast] + elif is_given(environment): + if base_url_env and base_url is not None: + raise ValueError( + "Ambiguous URL; The `HANZO_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None", + ) + + try: + base_url = ENVIRONMENTS[environment] + except KeyError as exc: + raise ValueError(f"Unknown environment: {environment}") from exc + elif base_url_env is not None: + base_url = base_url_env + else: + self._environment = environment = "production" + + try: + base_url = ENVIRONMENTS[environment] + except KeyError as exc: + raise ValueError(f"Unknown environment: {environment}") from exc + + super().__init__( + version=__version__, + base_url=base_url, + max_retries=max_retries, + timeout=timeout, + http_client=http_client, + custom_headers=default_headers, + custom_query=default_query, + _strict_response_validation=_strict_response_validation, + ) + + self.models = models.AsyncModelsResource(self) + self.openai = openai.AsyncOpenAIResource(self) + self.engines = engines.AsyncEnginesResource(self) + self.chat = chat.AsyncChatResource(self) + self.completions = completions.AsyncCompletionsResource(self) + self.embeddings = embeddings.AsyncEmbeddingsResource(self) + self.images = images.AsyncImagesResource(self) + self.audio = audio.AsyncAudioResource(self) + self.assistants = assistants.AsyncAssistantsResource(self) + self.threads = threads.AsyncThreadsResource(self) + self.moderations = moderations.AsyncModerationsResource(self) + self.utils = utils.AsyncUtilsResource(self) + self.model = model.AsyncModelResource(self) + self.model_group = model_group.AsyncModelGroupResource(self) + self.routes = routes.AsyncRoutesResource(self) + self.responses = responses.AsyncResponsesResource(self) + self.batches = batches.AsyncBatchesResource(self) + self.rerank = rerank.AsyncRerankResource(self) + self.fine_tuning = fine_tuning.AsyncFineTuningResource(self) + self.credentials = credentials.AsyncCredentialsResource(self) + self.vertex_ai = vertex_ai.AsyncVertexAIResource(self) + self.gemini = gemini.AsyncGeminiResource(self) + self.cohere = cohere.AsyncCohereResource(self) + self.anthropic = anthropic.AsyncAnthropicResource(self) + self.bedrock = bedrock.AsyncBedrockResource(self) + self.eu_assemblyai = eu_assemblyai.AsyncEuAssemblyaiResource(self) + self.assemblyai = assemblyai.AsyncAssemblyaiResource(self) + self.azure = azure.AsyncAzureResource(self) + self.langfuse = langfuse.AsyncLangfuseResource(self) + self.config = config.AsyncConfigResource(self) + self.test = test.AsyncTestResource(self) + self.health = health.AsyncHealthResource(self) + self.active = active.AsyncActiveResource(self) + self.settings = settings.AsyncSettingsResource(self) + self.key = key.AsyncKeyResource(self) + self.user = user.AsyncUserResource(self) + self.team = team.AsyncTeamResource(self) + self.organization = organization.AsyncOrganizationResource(self) + self.customer = customer.AsyncCustomerResource(self) + self.spend = spend.AsyncSpendResource(self) + self.global_ = global_.AsyncGlobalResource(self) + self.provider = provider.AsyncProviderResource(self) + self.cache = cache.AsyncCacheResource(self) + self.guardrails = guardrails.AsyncGuardrailsResource(self) + self.add = add.AsyncAddResource(self) + self.delete = delete.AsyncDeleteResource(self) + self.files = files.AsyncFilesResource(self) + self.budget = budget.AsyncBudgetResource(self) + self.with_raw_response = AsyncHanzoWithRawResponse(self) + self.with_streaming_response = AsyncHanzoWithStreamedResponse(self) + + @property + @override + def qs(self) -> Querystring: + return Querystring(array_format="comma") + + @property + @override + def auth_headers(self) -> dict[str, str]: + api_key = self.api_key + return {"Ocp-Apim-Subscription-Key": api_key} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "X-Stainless-Async": f"async:{get_async_library()}", + **self._custom_headers, + } + + def copy( + self, + *, + api_key: str | None = None, + environment: Literal["production", "sandbox"] | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx.AsyncClient | None = None, + max_retries: int | NotGiven = not_given, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + """ + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + http_client = http_client or self._client + return self.__class__( + api_key=api_key or self.api_key, + base_url=base_url or self.base_url, + environment=environment or self._environment, + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + **_extra_kwargs, + ) + + # Alias for `copy` for nicer inline usage, e.g. + # client.with_options(timeout=10).foo.create(...) + with_options = copy + + async def get_home( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Home""" + return await self.get( + "/", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + @override + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> APIStatusError: + if response.status_code == 400: + return _exceptions.BadRequestError(err_msg, response=response, body=body) + + if response.status_code == 401: + return _exceptions.AuthenticationError(err_msg, response=response, body=body) + + if response.status_code == 403: + return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) + + if response.status_code == 404: + return _exceptions.NotFoundError(err_msg, response=response, body=body) + + if response.status_code == 409: + return _exceptions.ConflictError(err_msg, response=response, body=body) + + if response.status_code == 422: + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) + + if response.status_code == 429: + return _exceptions.RateLimitError(err_msg, response=response, body=body) + + if response.status_code >= 500: + return _exceptions.InternalServerError(err_msg, response=response, body=body) + return APIStatusError(err_msg, response=response, body=body) + + +class HanzoWithRawResponse: + def __init__(self, client: Hanzo) -> None: + self.models = models.ModelsResourceWithRawResponse(client.models) + self.openai = openai.OpenAIResourceWithRawResponse(client.openai) + self.engines = engines.EnginesResourceWithRawResponse(client.engines) + self.chat = chat.ChatResourceWithRawResponse(client.chat) + self.completions = completions.CompletionsResourceWithRawResponse(client.completions) + self.embeddings = embeddings.EmbeddingsResourceWithRawResponse(client.embeddings) + self.images = images.ImagesResourceWithRawResponse(client.images) + self.audio = audio.AudioResourceWithRawResponse(client.audio) + self.assistants = assistants.AssistantsResourceWithRawResponse(client.assistants) + self.threads = threads.ThreadsResourceWithRawResponse(client.threads) + self.moderations = moderations.ModerationsResourceWithRawResponse(client.moderations) + self.utils = utils.UtilsResourceWithRawResponse(client.utils) + self.model = model.ModelResourceWithRawResponse(client.model) + self.model_group = model_group.ModelGroupResourceWithRawResponse(client.model_group) + self.routes = routes.RoutesResourceWithRawResponse(client.routes) + self.responses = responses.ResponsesResourceWithRawResponse(client.responses) + self.batches = batches.BatchesResourceWithRawResponse(client.batches) + self.rerank = rerank.RerankResourceWithRawResponse(client.rerank) + self.fine_tuning = fine_tuning.FineTuningResourceWithRawResponse(client.fine_tuning) + self.credentials = credentials.CredentialsResourceWithRawResponse(client.credentials) + self.vertex_ai = vertex_ai.VertexAIResourceWithRawResponse(client.vertex_ai) + self.gemini = gemini.GeminiResourceWithRawResponse(client.gemini) + self.cohere = cohere.CohereResourceWithRawResponse(client.cohere) + self.anthropic = anthropic.AnthropicResourceWithRawResponse(client.anthropic) + self.bedrock = bedrock.BedrockResourceWithRawResponse(client.bedrock) + self.eu_assemblyai = eu_assemblyai.EuAssemblyaiResourceWithRawResponse(client.eu_assemblyai) + self.assemblyai = assemblyai.AssemblyaiResourceWithRawResponse(client.assemblyai) + self.azure = azure.AzureResourceWithRawResponse(client.azure) + self.langfuse = langfuse.LangfuseResourceWithRawResponse(client.langfuse) + self.config = config.ConfigResourceWithRawResponse(client.config) + self.test = test.TestResourceWithRawResponse(client.test) + self.health = health.HealthResourceWithRawResponse(client.health) + self.active = active.ActiveResourceWithRawResponse(client.active) + self.settings = settings.SettingsResourceWithRawResponse(client.settings) + self.key = key.KeyResourceWithRawResponse(client.key) + self.user = user.UserResourceWithRawResponse(client.user) + self.team = team.TeamResourceWithRawResponse(client.team) + self.organization = organization.OrganizationResourceWithRawResponse(client.organization) + self.customer = customer.CustomerResourceWithRawResponse(client.customer) + self.spend = spend.SpendResourceWithRawResponse(client.spend) + self.global_ = global_.GlobalResourceWithRawResponse(client.global_) + self.provider = provider.ProviderResourceWithRawResponse(client.provider) + self.cache = cache.CacheResourceWithRawResponse(client.cache) + self.guardrails = guardrails.GuardrailsResourceWithRawResponse(client.guardrails) + self.add = add.AddResourceWithRawResponse(client.add) + self.delete = delete.DeleteResourceWithRawResponse(client.delete) + self.files = files.FilesResourceWithRawResponse(client.files) + self.budget = budget.BudgetResourceWithRawResponse(client.budget) + + self.get_home = to_raw_response_wrapper( + client.get_home, + ) + + +class AsyncHanzoWithRawResponse: + def __init__(self, client: AsyncHanzo) -> None: + self.models = models.AsyncModelsResourceWithRawResponse(client.models) + self.openai = openai.AsyncOpenAIResourceWithRawResponse(client.openai) + self.engines = engines.AsyncEnginesResourceWithRawResponse(client.engines) + self.chat = chat.AsyncChatResourceWithRawResponse(client.chat) + self.completions = completions.AsyncCompletionsResourceWithRawResponse(client.completions) + self.embeddings = embeddings.AsyncEmbeddingsResourceWithRawResponse(client.embeddings) + self.images = images.AsyncImagesResourceWithRawResponse(client.images) + self.audio = audio.AsyncAudioResourceWithRawResponse(client.audio) + self.assistants = assistants.AsyncAssistantsResourceWithRawResponse(client.assistants) + self.threads = threads.AsyncThreadsResourceWithRawResponse(client.threads) + self.moderations = moderations.AsyncModerationsResourceWithRawResponse(client.moderations) + self.utils = utils.AsyncUtilsResourceWithRawResponse(client.utils) + self.model = model.AsyncModelResourceWithRawResponse(client.model) + self.model_group = model_group.AsyncModelGroupResourceWithRawResponse(client.model_group) + self.routes = routes.AsyncRoutesResourceWithRawResponse(client.routes) + self.responses = responses.AsyncResponsesResourceWithRawResponse(client.responses) + self.batches = batches.AsyncBatchesResourceWithRawResponse(client.batches) + self.rerank = rerank.AsyncRerankResourceWithRawResponse(client.rerank) + self.fine_tuning = fine_tuning.AsyncFineTuningResourceWithRawResponse(client.fine_tuning) + self.credentials = credentials.AsyncCredentialsResourceWithRawResponse(client.credentials) + self.vertex_ai = vertex_ai.AsyncVertexAIResourceWithRawResponse(client.vertex_ai) + self.gemini = gemini.AsyncGeminiResourceWithRawResponse(client.gemini) + self.cohere = cohere.AsyncCohereResourceWithRawResponse(client.cohere) + self.anthropic = anthropic.AsyncAnthropicResourceWithRawResponse(client.anthropic) + self.bedrock = bedrock.AsyncBedrockResourceWithRawResponse(client.bedrock) + self.eu_assemblyai = eu_assemblyai.AsyncEuAssemblyaiResourceWithRawResponse(client.eu_assemblyai) + self.assemblyai = assemblyai.AsyncAssemblyaiResourceWithRawResponse(client.assemblyai) + self.azure = azure.AsyncAzureResourceWithRawResponse(client.azure) + self.langfuse = langfuse.AsyncLangfuseResourceWithRawResponse(client.langfuse) + self.config = config.AsyncConfigResourceWithRawResponse(client.config) + self.test = test.AsyncTestResourceWithRawResponse(client.test) + self.health = health.AsyncHealthResourceWithRawResponse(client.health) + self.active = active.AsyncActiveResourceWithRawResponse(client.active) + self.settings = settings.AsyncSettingsResourceWithRawResponse(client.settings) + self.key = key.AsyncKeyResourceWithRawResponse(client.key) + self.user = user.AsyncUserResourceWithRawResponse(client.user) + self.team = team.AsyncTeamResourceWithRawResponse(client.team) + self.organization = organization.AsyncOrganizationResourceWithRawResponse(client.organization) + self.customer = customer.AsyncCustomerResourceWithRawResponse(client.customer) + self.spend = spend.AsyncSpendResourceWithRawResponse(client.spend) + self.global_ = global_.AsyncGlobalResourceWithRawResponse(client.global_) + self.provider = provider.AsyncProviderResourceWithRawResponse(client.provider) + self.cache = cache.AsyncCacheResourceWithRawResponse(client.cache) + self.guardrails = guardrails.AsyncGuardrailsResourceWithRawResponse(client.guardrails) + self.add = add.AsyncAddResourceWithRawResponse(client.add) + self.delete = delete.AsyncDeleteResourceWithRawResponse(client.delete) + self.files = files.AsyncFilesResourceWithRawResponse(client.files) + self.budget = budget.AsyncBudgetResourceWithRawResponse(client.budget) + + self.get_home = async_to_raw_response_wrapper( + client.get_home, + ) + + +class HanzoWithStreamedResponse: + def __init__(self, client: Hanzo) -> None: + self.models = models.ModelsResourceWithStreamingResponse(client.models) + self.openai = openai.OpenAIResourceWithStreamingResponse(client.openai) + self.engines = engines.EnginesResourceWithStreamingResponse(client.engines) + self.chat = chat.ChatResourceWithStreamingResponse(client.chat) + self.completions = completions.CompletionsResourceWithStreamingResponse(client.completions) + self.embeddings = embeddings.EmbeddingsResourceWithStreamingResponse(client.embeddings) + self.images = images.ImagesResourceWithStreamingResponse(client.images) + self.audio = audio.AudioResourceWithStreamingResponse(client.audio) + self.assistants = assistants.AssistantsResourceWithStreamingResponse(client.assistants) + self.threads = threads.ThreadsResourceWithStreamingResponse(client.threads) + self.moderations = moderations.ModerationsResourceWithStreamingResponse(client.moderations) + self.utils = utils.UtilsResourceWithStreamingResponse(client.utils) + self.model = model.ModelResourceWithStreamingResponse(client.model) + self.model_group = model_group.ModelGroupResourceWithStreamingResponse(client.model_group) + self.routes = routes.RoutesResourceWithStreamingResponse(client.routes) + self.responses = responses.ResponsesResourceWithStreamingResponse(client.responses) + self.batches = batches.BatchesResourceWithStreamingResponse(client.batches) + self.rerank = rerank.RerankResourceWithStreamingResponse(client.rerank) + self.fine_tuning = fine_tuning.FineTuningResourceWithStreamingResponse(client.fine_tuning) + self.credentials = credentials.CredentialsResourceWithStreamingResponse(client.credentials) + self.vertex_ai = vertex_ai.VertexAIResourceWithStreamingResponse(client.vertex_ai) + self.gemini = gemini.GeminiResourceWithStreamingResponse(client.gemini) + self.cohere = cohere.CohereResourceWithStreamingResponse(client.cohere) + self.anthropic = anthropic.AnthropicResourceWithStreamingResponse(client.anthropic) + self.bedrock = bedrock.BedrockResourceWithStreamingResponse(client.bedrock) + self.eu_assemblyai = eu_assemblyai.EuAssemblyaiResourceWithStreamingResponse(client.eu_assemblyai) + self.assemblyai = assemblyai.AssemblyaiResourceWithStreamingResponse(client.assemblyai) + self.azure = azure.AzureResourceWithStreamingResponse(client.azure) + self.langfuse = langfuse.LangfuseResourceWithStreamingResponse(client.langfuse) + self.config = config.ConfigResourceWithStreamingResponse(client.config) + self.test = test.TestResourceWithStreamingResponse(client.test) + self.health = health.HealthResourceWithStreamingResponse(client.health) + self.active = active.ActiveResourceWithStreamingResponse(client.active) + self.settings = settings.SettingsResourceWithStreamingResponse(client.settings) + self.key = key.KeyResourceWithStreamingResponse(client.key) + self.user = user.UserResourceWithStreamingResponse(client.user) + self.team = team.TeamResourceWithStreamingResponse(client.team) + self.organization = organization.OrganizationResourceWithStreamingResponse(client.organization) + self.customer = customer.CustomerResourceWithStreamingResponse(client.customer) + self.spend = spend.SpendResourceWithStreamingResponse(client.spend) + self.global_ = global_.GlobalResourceWithStreamingResponse(client.global_) + self.provider = provider.ProviderResourceWithStreamingResponse(client.provider) + self.cache = cache.CacheResourceWithStreamingResponse(client.cache) + self.guardrails = guardrails.GuardrailsResourceWithStreamingResponse(client.guardrails) + self.add = add.AddResourceWithStreamingResponse(client.add) + self.delete = delete.DeleteResourceWithStreamingResponse(client.delete) + self.files = files.FilesResourceWithStreamingResponse(client.files) + self.budget = budget.BudgetResourceWithStreamingResponse(client.budget) + + self.get_home = to_streamed_response_wrapper( + client.get_home, + ) + + +class AsyncHanzoWithStreamedResponse: + def __init__(self, client: AsyncHanzo) -> None: + self.models = models.AsyncModelsResourceWithStreamingResponse(client.models) + self.openai = openai.AsyncOpenAIResourceWithStreamingResponse(client.openai) + self.engines = engines.AsyncEnginesResourceWithStreamingResponse(client.engines) + self.chat = chat.AsyncChatResourceWithStreamingResponse(client.chat) + self.completions = completions.AsyncCompletionsResourceWithStreamingResponse(client.completions) + self.embeddings = embeddings.AsyncEmbeddingsResourceWithStreamingResponse(client.embeddings) + self.images = images.AsyncImagesResourceWithStreamingResponse(client.images) + self.audio = audio.AsyncAudioResourceWithStreamingResponse(client.audio) + self.assistants = assistants.AsyncAssistantsResourceWithStreamingResponse(client.assistants) + self.threads = threads.AsyncThreadsResourceWithStreamingResponse(client.threads) + self.moderations = moderations.AsyncModerationsResourceWithStreamingResponse(client.moderations) + self.utils = utils.AsyncUtilsResourceWithStreamingResponse(client.utils) + self.model = model.AsyncModelResourceWithStreamingResponse(client.model) + self.model_group = model_group.AsyncModelGroupResourceWithStreamingResponse(client.model_group) + self.routes = routes.AsyncRoutesResourceWithStreamingResponse(client.routes) + self.responses = responses.AsyncResponsesResourceWithStreamingResponse(client.responses) + self.batches = batches.AsyncBatchesResourceWithStreamingResponse(client.batches) + self.rerank = rerank.AsyncRerankResourceWithStreamingResponse(client.rerank) + self.fine_tuning = fine_tuning.AsyncFineTuningResourceWithStreamingResponse(client.fine_tuning) + self.credentials = credentials.AsyncCredentialsResourceWithStreamingResponse(client.credentials) + self.vertex_ai = vertex_ai.AsyncVertexAIResourceWithStreamingResponse(client.vertex_ai) + self.gemini = gemini.AsyncGeminiResourceWithStreamingResponse(client.gemini) + self.cohere = cohere.AsyncCohereResourceWithStreamingResponse(client.cohere) + self.anthropic = anthropic.AsyncAnthropicResourceWithStreamingResponse(client.anthropic) + self.bedrock = bedrock.AsyncBedrockResourceWithStreamingResponse(client.bedrock) + self.eu_assemblyai = eu_assemblyai.AsyncEuAssemblyaiResourceWithStreamingResponse(client.eu_assemblyai) + self.assemblyai = assemblyai.AsyncAssemblyaiResourceWithStreamingResponse(client.assemblyai) + self.azure = azure.AsyncAzureResourceWithStreamingResponse(client.azure) + self.langfuse = langfuse.AsyncLangfuseResourceWithStreamingResponse(client.langfuse) + self.config = config.AsyncConfigResourceWithStreamingResponse(client.config) + self.test = test.AsyncTestResourceWithStreamingResponse(client.test) + self.health = health.AsyncHealthResourceWithStreamingResponse(client.health) + self.active = active.AsyncActiveResourceWithStreamingResponse(client.active) + self.settings = settings.AsyncSettingsResourceWithStreamingResponse(client.settings) + self.key = key.AsyncKeyResourceWithStreamingResponse(client.key) + self.user = user.AsyncUserResourceWithStreamingResponse(client.user) + self.team = team.AsyncTeamResourceWithStreamingResponse(client.team) + self.organization = organization.AsyncOrganizationResourceWithStreamingResponse(client.organization) + self.customer = customer.AsyncCustomerResourceWithStreamingResponse(client.customer) + self.spend = spend.AsyncSpendResourceWithStreamingResponse(client.spend) + self.global_ = global_.AsyncGlobalResourceWithStreamingResponse(client.global_) + self.provider = provider.AsyncProviderResourceWithStreamingResponse(client.provider) + self.cache = cache.AsyncCacheResourceWithStreamingResponse(client.cache) + self.guardrails = guardrails.AsyncGuardrailsResourceWithStreamingResponse(client.guardrails) + self.add = add.AsyncAddResourceWithStreamingResponse(client.add) + self.delete = delete.AsyncDeleteResourceWithStreamingResponse(client.delete) + self.files = files.AsyncFilesResourceWithStreamingResponse(client.files) + self.budget = budget.AsyncBudgetResourceWithStreamingResponse(client.budget) + + self.get_home = async_to_streamed_response_wrapper( + client.get_home, + ) + + +Client = Hanzo + +AsyncClient = AsyncHanzo diff --git a/src/hanzoai/_compat.py b/src/hanzoai/_compat.py new file mode 100644 index 000000000..bdef67f04 --- /dev/null +++ b/src/hanzoai/_compat.py @@ -0,0 +1,219 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload +from datetime import date, datetime +from typing_extensions import Self, Literal + +import pydantic +from pydantic.fields import FieldInfo + +from ._types import IncEx, StrBytesIntFloat + +_T = TypeVar("_T") +_ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) + +# --------------- Pydantic v2, v3 compatibility --------------- + +# Pyright incorrectly reports some of our functions as overriding a method when they don't +# pyright: reportIncompatibleMethodOverride=false + +PYDANTIC_V1 = pydantic.VERSION.startswith("1.") + +if TYPE_CHECKING: + + def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001 + ... + + def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: # noqa: ARG001 + ... + + def get_args(t: type[Any]) -> tuple[Any, ...]: # noqa: ARG001 + ... + + def is_union(tp: type[Any] | None) -> bool: # noqa: ARG001 + ... + + def get_origin(t: type[Any]) -> type[Any] | None: # noqa: ARG001 + ... + + def is_literal_type(type_: type[Any]) -> bool: # noqa: ARG001 + ... + + def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 + ... + +else: + # v1 re-exports + if PYDANTIC_V1: + from pydantic.typing import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, + ) + from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime + else: + from ._utils import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + parse_date as parse_date, + is_typeddict as is_typeddict, + parse_datetime as parse_datetime, + is_literal_type as is_literal_type, + ) + + +# refactored config +if TYPE_CHECKING: + from pydantic import ConfigDict as ConfigDict +else: + if PYDANTIC_V1: + # TODO: provide an error message here? + ConfigDict = None + else: + from pydantic import ConfigDict as ConfigDict + + +# renamed methods / properties +def parse_obj(model: type[_ModelT], value: object) -> _ModelT: + if PYDANTIC_V1: + return cast(_ModelT, model.parse_obj(value)) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + else: + return model.model_validate(value) + + +def field_is_required(field: FieldInfo) -> bool: + if PYDANTIC_V1: + return field.required # type: ignore + return field.is_required() + + +def field_get_default(field: FieldInfo) -> Any: + value = field.get_default() + if PYDANTIC_V1: + return value + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None + return value + + +def field_outer_type(field: FieldInfo) -> Any: + if PYDANTIC_V1: + return field.outer_type_ # type: ignore + return field.annotation + + +def get_model_config(model: type[pydantic.BaseModel]) -> Any: + if PYDANTIC_V1: + return model.__config__ # type: ignore + return model.model_config + + +def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: + if PYDANTIC_V1: + return model.__fields__ # type: ignore + return model.model_fields + + +def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT: + if PYDANTIC_V1: + return model.copy(deep=deep) # type: ignore + return model.model_copy(deep=deep) + + +def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: + if PYDANTIC_V1: + return model.json(indent=indent) # type: ignore + return model.model_dump_json(indent=indent) + + +def model_dump( + model: pydantic.BaseModel, + *, + exclude: IncEx | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + warnings: bool = True, + mode: Literal["json", "python"] = "python", +) -> dict[str, Any]: + if (not PYDANTIC_V1) or hasattr(model, "model_dump"): + return model.model_dump( + mode=mode, + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + # warnings are not supported in Pydantic v1 + warnings=True if PYDANTIC_V1 else warnings, + ) + return cast( + "dict[str, Any]", + model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + ), + ) + + +def model_parse(model: type[_ModelT], data: Any) -> _ModelT: + if PYDANTIC_V1: + return model.parse_obj(data) # pyright: ignore[reportDeprecated] + return model.model_validate(data) + + +# generic models +if TYPE_CHECKING: + + class GenericModel(pydantic.BaseModel): ... + +else: + if PYDANTIC_V1: + import pydantic.generics + + class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... + else: + # there no longer needs to be a distinction in v2 but + # we still have to create our own subclass to avoid + # inconsistent MRO ordering errors + class GenericModel(pydantic.BaseModel): ... + + +# cached properties +if TYPE_CHECKING: + cached_property = property + + # we define a separate type (copied from typeshed) + # that represents that `cached_property` is `set`able + # at runtime, which differs from `@property`. + # + # this is a separate type as editors likely special case + # `@property` and we don't want to cause issues just to have + # more helpful internal types. + + class typed_cached_property(Generic[_T]): + func: Callable[[Any], _T] + attrname: str | None + + def __init__(self, func: Callable[[Any], _T]) -> None: ... + + @overload + def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: ... + + @overload + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... + + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T | Self: + raise NotImplementedError() + + def __set_name__(self, owner: type[Any], name: str) -> None: ... + + # __set__ is not defined at runtime, but @cached_property is designed to be settable + def __set__(self, instance: object, value: _T) -> None: ... +else: + from functools import cached_property as cached_property + + typed_cached_property = cached_property diff --git a/src/hanzoai/_constants.py b/src/hanzoai/_constants.py new file mode 100644 index 000000000..6ddf2c717 --- /dev/null +++ b/src/hanzoai/_constants.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import httpx + +RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response" +OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to" + +# default timeout is 1 minute +DEFAULT_TIMEOUT = httpx.Timeout(timeout=60, connect=5.0) +DEFAULT_MAX_RETRIES = 2 +DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20) + +INITIAL_RETRY_DELAY = 0.5 +MAX_RETRY_DELAY = 8.0 diff --git a/src/hanzoai/_exceptions.py b/src/hanzoai/_exceptions.py new file mode 100644 index 000000000..4b465c106 --- /dev/null +++ b/src/hanzoai/_exceptions.py @@ -0,0 +1,108 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +__all__ = [ + "BadRequestError", + "AuthenticationError", + "PermissionDeniedError", + "NotFoundError", + "ConflictError", + "UnprocessableEntityError", + "RateLimitError", + "InternalServerError", +] + + +class HanzoError(Exception): + pass + + +class APIError(HanzoError): + message: str + request: httpx.Request + + body: object | None + """The API response body. + + If the API responded with a valid JSON structure then this property will be the + decoded result. + + If it isn't a valid JSON structure then this will be the raw response. + + If there was no response associated with this error then it will be `None`. + """ + + def __init__(self, message: str, request: httpx.Request, *, body: object | None) -> None: # noqa: ARG002 + super().__init__(message) + self.request = request + self.message = message + self.body = body + + +class APIResponseValidationError(APIError): + response: httpx.Response + status_code: int + + def __init__(self, response: httpx.Response, body: object | None, *, message: str | None = None) -> None: + super().__init__(message or "Data returned by API invalid for expected schema.", response.request, body=body) + self.response = response + self.status_code = response.status_code + + +class APIStatusError(APIError): + """Raised when an API response has a status code of 4xx or 5xx.""" + + response: httpx.Response + status_code: int + + def __init__(self, message: str, *, response: httpx.Response, body: object | None) -> None: + super().__init__(message, response.request, body=body) + self.response = response + self.status_code = response.status_code + + +class APIConnectionError(APIError): + def __init__(self, *, message: str = "Connection error.", request: httpx.Request) -> None: + super().__init__(message, request, body=None) + + +class APITimeoutError(APIConnectionError): + def __init__(self, request: httpx.Request) -> None: + super().__init__(message="Request timed out.", request=request) + + +class BadRequestError(APIStatusError): + status_code: Literal[400] = 400 # pyright: ignore[reportIncompatibleVariableOverride] + + +class AuthenticationError(APIStatusError): + status_code: Literal[401] = 401 # pyright: ignore[reportIncompatibleVariableOverride] + + +class PermissionDeniedError(APIStatusError): + status_code: Literal[403] = 403 # pyright: ignore[reportIncompatibleVariableOverride] + + +class NotFoundError(APIStatusError): + status_code: Literal[404] = 404 # pyright: ignore[reportIncompatibleVariableOverride] + + +class ConflictError(APIStatusError): + status_code: Literal[409] = 409 # pyright: ignore[reportIncompatibleVariableOverride] + + +class UnprocessableEntityError(APIStatusError): + status_code: Literal[422] = 422 # pyright: ignore[reportIncompatibleVariableOverride] + + +class RateLimitError(APIStatusError): + status_code: Literal[429] = 429 # pyright: ignore[reportIncompatibleVariableOverride] + + +class InternalServerError(APIStatusError): + pass diff --git a/src/hanzoai/_files.py b/src/hanzoai/_files.py new file mode 100644 index 000000000..4fab3241b --- /dev/null +++ b/src/hanzoai/_files.py @@ -0,0 +1,123 @@ +from __future__ import annotations + +import io +import os +import pathlib +from typing import overload +from typing_extensions import TypeGuard + +import anyio + +from ._types import ( + FileTypes, + FileContent, + RequestFiles, + HttpxFileTypes, + Base64FileInput, + HttpxFileContent, + HttpxRequestFiles, +) +from ._utils import is_tuple_t, is_mapping_t, is_sequence_t + + +def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]: + return isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) + + +def is_file_content(obj: object) -> TypeGuard[FileContent]: + return ( + isinstance(obj, bytes) or isinstance(obj, tuple) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) + ) + + +def assert_is_file_content(obj: object, *, key: str | None = None) -> None: + if not is_file_content(obj): + prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`" + raise RuntimeError( + f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/hanzoai/python-sdk/tree/main#file-uploads" + ) from None + + +@overload +def to_httpx_files(files: None) -> None: ... + + +@overload +def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... + + +def to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: + if files is None: + return None + + if is_mapping_t(files): + files = {key: _transform_file(file) for key, file in files.items()} + elif is_sequence_t(files): + files = [(key, _transform_file(file)) for key, file in files] + else: + raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") + + return files + + +def _transform_file(file: FileTypes) -> HttpxFileTypes: + if is_file_content(file): + if isinstance(file, os.PathLike): + path = pathlib.Path(file) + return (path.name, path.read_bytes()) + + return file + + if is_tuple_t(file): + return (file[0], read_file_content(file[1]), *file[2:]) + + raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") + + +def read_file_content(file: FileContent) -> HttpxFileContent: + if isinstance(file, os.PathLike): + return pathlib.Path(file).read_bytes() + return file + + +@overload +async def async_to_httpx_files(files: None) -> None: ... + + +@overload +async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... + + +async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: + if files is None: + return None + + if is_mapping_t(files): + files = {key: await _async_transform_file(file) for key, file in files.items()} + elif is_sequence_t(files): + files = [(key, await _async_transform_file(file)) for key, file in files] + else: + raise TypeError("Unexpected file type input {type(files)}, expected mapping or sequence") + + return files + + +async def _async_transform_file(file: FileTypes) -> HttpxFileTypes: + if is_file_content(file): + if isinstance(file, os.PathLike): + path = anyio.Path(file) + return (path.name, await path.read_bytes()) + + return file + + if is_tuple_t(file): + return (file[0], await async_read_file_content(file[1]), *file[2:]) + + raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") + + +async def async_read_file_content(file: FileContent) -> HttpxFileContent: + if isinstance(file, os.PathLike): + return await anyio.Path(file).read_bytes() + + return file diff --git a/pkg/hanzoai/_models.py b/src/hanzoai/_models.py similarity index 86% rename from pkg/hanzoai/_models.py rename to src/hanzoai/_models.py index 280516b0d..6a3cd1d26 100644 --- a/pkg/hanzoai/_models.py +++ b/src/hanzoai/_models.py @@ -2,9 +2,10 @@ import os import inspect -from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, cast +from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast from datetime import date, datetime from typing_extensions import ( + List, Unpack, Literal, ClassVar, @@ -19,7 +20,6 @@ ) import pydantic -import pydantic.generics from pydantic.fields import FieldInfo from ._types import ( @@ -50,7 +50,7 @@ strip_annotated_type, ) from ._compat import ( - PYDANTIC_V2, + PYDANTIC_V1, ConfigDict, GenericModel as BaseGenericModel, get_args, @@ -65,12 +65,7 @@ from ._constants import RAW_RESPONSE_HEADER if TYPE_CHECKING: - from pydantic_core.core_schema import ( - ModelField, - ModelSchema, - LiteralSchema, - ModelFieldsSchema, - ) + from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema __all__ = ["BaseModel", "GenericModel"] @@ -86,12 +81,7 @@ class _ConfigProtocol(Protocol): class BaseModel(pydantic.BaseModel): - if PYDANTIC_V2: - model_config: ClassVar[ConfigDict] = ConfigDict( - extra="allow", - defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")), - ) - else: + if PYDANTIC_V1: @property @override @@ -101,6 +91,10 @@ def model_fields_set(self) -> set[str]: class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] extra: Any = pydantic.Extra.allow # type: ignore + else: + model_config: ClassVar[ConfigDict] = ConfigDict( + extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) + ) def to_dict( self, @@ -209,35 +203,37 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride] key = name if key in values: - fields_values[name] = _construct_field( - value=values[key], field=field, key=key - ) + fields_values[name] = _construct_field(value=values[key], field=field, key=key) _fields_set.add(name) else: fields_values[name] = field_get_default(field) + extra_field_type = _get_extra_fields_type(__cls) + _extra = {} for key, value in values.items(): if key not in model_fields: - if PYDANTIC_V2: - _extra[key] = value - else: + parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value + + if PYDANTIC_V1: _fields_set.add(key) - fields_values[key] = value + fields_values[key] = parsed + else: + _extra[key] = parsed object.__setattr__(m, "__dict__", fields_values) - if PYDANTIC_V2: - # these properties are copied from Pydantic's `model_construct()` method - object.__setattr__(m, "__pydantic_private__", None) - object.__setattr__(m, "__pydantic_extra__", _extra) - object.__setattr__(m, "__pydantic_fields_set__", _fields_set) - else: + if PYDANTIC_V1: # init_private_attributes() does not exist in v2 m._init_private_attributes() # type: ignore # copied from Pydantic v1's `construct()` method object.__setattr__(m, "__fields_set__", _fields_set) + else: + # these properties are copied from Pydantic's `model_construct()` method + object.__setattr__(m, "__pydantic_private__", None) + object.__setattr__(m, "__pydantic_extra__", _extra) + object.__setattr__(m, "__pydantic_fields_set__", _fields_set) return m @@ -247,7 +243,7 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride] # although not in practice model_construct = construct - if not PYDANTIC_V2: + if PYDANTIC_V1: # we define aliases for some of the new pydantic v2 methods so # that we can just document these methods without having to specify # a specific pydantic version as some users may not know which @@ -260,7 +256,7 @@ def model_dump( mode: Literal["json", "python"] | str = "python", include: IncEx | None = None, exclude: IncEx | None = None, - by_alias: bool = False, + by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, @@ -268,6 +264,7 @@ def model_dump( warnings: bool | Literal["none", "warn", "error"] = True, context: dict[str, Any] | None = None, serialize_as_any: bool = False, + fallback: Callable[[Any], Any] | None = None, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -291,24 +288,26 @@ def model_dump( """ if mode not in {"json", "python"}: raise ValueError("mode must be either 'json' or 'python'") - if round_trip is not False: + if round_trip != False: raise ValueError("round_trip is only supported in Pydantic v2") - if warnings is not True: + if warnings != True: raise ValueError("warnings is only supported in Pydantic v2") if context is not None: raise ValueError("context is only supported in Pydantic v2") - if serialize_as_any is not False: + if serialize_as_any != False: raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, - by_alias=by_alias, + by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, ) - return cast(dict[str, Any], json_safe(dumped)) if mode == "json" else dumped + return cast("dict[str, Any]", json_safe(dumped)) if mode == "json" else dumped @override def model_dump_json( @@ -317,13 +316,14 @@ def model_dump_json( indent: int | None = None, include: IncEx | None = None, exclude: IncEx | None = None, - by_alias: bool = False, + by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, context: dict[str, Any] | None = None, + fallback: Callable[[Any], Any] | None = None, serialize_as_any: bool = False, ) -> str: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json @@ -344,19 +344,21 @@ def model_dump_json( Returns: A JSON string representation of the model. """ - if round_trip is not False: + if round_trip != False: raise ValueError("round_trip is only supported in Pydantic v2") - if warnings is not True: + if warnings != True: raise ValueError("warnings is only supported in Pydantic v2") if context is not None: raise ValueError("context is only supported in Pydantic v2") - if serialize_as_any is not False: + if serialize_as_any != False: raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, exclude=exclude, - by_alias=by_alias, + by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, @@ -367,15 +369,32 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object: if value is None: return field_get_default(field) - if PYDANTIC_V2: - type_ = field.annotation - else: + if PYDANTIC_V1: type_ = cast(type, field.outer_type_) # type: ignore + else: + type_ = field.annotation # type: ignore if type_ is None: raise RuntimeError(f"Unexpected field type is None for {key}") - return construct_type(value=value, type_=type_) + return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None)) + + +def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None: + if PYDANTIC_V1: + # TODO + return None + + schema = cls.__pydantic_core_schema__ + if schema["type"] == "model": + fields = schema["schema"] + if fields["type"] == "model-fields": + extras = fields.get("extras_schema") + if extras and "cls" in extras: + # mypy can't narrow the type + return extras["cls"] # type: ignore[no-any-return] + + return None def is_basemodel(type_: type) -> bool: @@ -429,7 +448,7 @@ def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T: return cast(_T, construct_type(value=value, type_=type_)) -def construct_type(*, value: object, type_: object) -> object: +def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]] = None) -> object: """Loose coercion to the expected type with construction of nested values. If the given value does not match the expected type then it is returned as-is. @@ -447,8 +466,10 @@ def construct_type(*, value: object, type_: object) -> object: type_ = type_.__value__ # type: ignore[unreachable] # unwrap `Annotated[T, ...]` -> `T` - if is_annotated_type(type_): - meta: tuple[Any, ...] = get_args(type_)[1:] + if metadata is not None and len(metadata) > 0: + meta: tuple[Any, ...] = tuple(metadata) + elif is_annotated_type(type_): + meta = get_args(type_)[1:] type_ = extract_type_arg(type_, 0) else: meta = tuple() @@ -460,9 +481,7 @@ def construct_type(*, value: object, type_: object) -> object: if is_union(origin): try: - return validate_type( - type_=cast("type[object]", original_type or type_), value=value - ) + return validate_type(type_=cast("type[object]", original_type or type_), value=value) except Exception: pass @@ -480,13 +499,9 @@ def construct_type(*, value: object, type_: object) -> object: # # without this block, if the data we get is something like `{'kind': 'bar', 'value': 'foo'}` then # we'd end up constructing `FooType` when it should be `BarType`. - discriminator = _build_discriminated_union_meta( - union=type_, meta_annotations=meta - ) + discriminator = _build_discriminated_union_meta(union=type_, meta_annotations=meta) if discriminator and is_mapping(value): - variant_value = value.get( - discriminator.field_alias_from or discriminator.field_name - ) + variant_value = value.get(discriminator.field_alias_from or discriminator.field_name) if variant_value and isinstance(variant_value, str): variant_type = discriminator.mapping.get(variant_value) if variant_type: @@ -501,15 +516,12 @@ def construct_type(*, value: object, type_: object) -> object: raise RuntimeError(f"Could not convert data into a valid instance of {type_}") - if origin is dict: + if origin == dict: if not is_mapping(value): return value _, items_type = get_args(type_) # Dict[_, items_type] - return { - key: construct_type(value=item, type_=items_type) - for key, item in value.items() - } + return {key: construct_type(value=item, type_=items_type) for key, item in value.items()} if ( not is_literal_type(type_) @@ -517,10 +529,7 @@ def construct_type(*, value: object, type_: object) -> object: and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)) ): if is_list(value): - return [ - cast(Any, type_).construct(**entry) if is_mapping(entry) else entry - for entry in value - ] + return [cast(Any, type_).construct(**entry) if is_mapping(entry) else entry for entry in value] if is_mapping(value): if issubclass(type_, BaseModel): @@ -528,14 +537,14 @@ def construct_type(*, value: object, type_: object) -> object: return cast(Any, type_).construct(**value) - if origin is list: + if origin == list: if not is_list(value): return value inner_type = args[0] # List[inner_type] return [construct_type(value=entry, type_=inner_type) for entry in value] - if origin is float: + if origin == float: if isinstance(value, int): coerced = float(value) if coerced != value: @@ -605,19 +614,14 @@ def __init__( self.field_alias_from = discriminator_alias -def _build_discriminated_union_meta( - *, union: type, meta_annotations: tuple[Any, ...] -) -> DiscriminatorDetails | None: +def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: if isinstance(union, CachedDiscriminatorType): return union.__discriminator__ discriminator_field_name: str | None = None for annotation in meta_annotations: - if ( - isinstance(annotation, PropertyInfo) - and annotation.discriminator is not None - ): + if isinstance(annotation, PropertyInfo) and annotation.discriminator is not None: discriminator_field_name = annotation.discriminator break @@ -630,32 +634,30 @@ def _build_discriminated_union_meta( for variant in get_args(union): variant = strip_annotated_type(variant) if is_basemodel_type(variant): - if PYDANTIC_V2: - field = _extract_field_schema_pv2(variant, discriminator_field_name) - if not field: + if PYDANTIC_V1: + field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + if not field_info: continue # Note: if one variant defines an alias then they all should - discriminator_alias = field.get("serialization_alias") - - field_schema = field["schema"] + discriminator_alias = field_info.alias - if field_schema["type"] == "literal": - for entry in cast("LiteralSchema", field_schema)["expected"]: + if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation): + for entry in get_args(annotation): if isinstance(entry, str): mapping[entry] = variant else: - field_info = cast("dict[str, FieldInfo]", variant.__fields__).get( - discriminator_field_name - ) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] - if not field_info: + field = _extract_field_schema_pv2(variant, discriminator_field_name) + if not field: continue # Note: if one variant defines an alias then they all should - discriminator_alias = field_info.alias + discriminator_alias = field.get("serialization_alias") - if field_info.annotation and is_literal_type(field_info.annotation): - for entry in get_args(field_info.annotation): + field_schema = field["schema"] + + if field_schema["type"] == "literal": + for entry in cast("LiteralSchema", field_schema)["expected"]: if isinstance(entry, str): mapping[entry] = variant @@ -671,9 +673,7 @@ def _build_discriminated_union_meta( return details -def _extract_field_schema_pv2( - model: type[BaseModel], field_name: str -) -> ModelField | None: +def _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None: schema = model.__pydantic_core_schema__ if schema["type"] == "definitions": schema = schema["schema"] @@ -720,12 +720,10 @@ class GenericModel(BaseGenericModel, BaseModel): pass -if PYDANTIC_V2: +if not PYDANTIC_V1: from pydantic import TypeAdapter as _TypeAdapter - _CachedTypeAdapter = cast( - "TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter) - ) + _CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter)) if TYPE_CHECKING: from pydantic import TypeAdapter @@ -769,6 +767,7 @@ class FinalRequestOptionsInput(TypedDict, total=False): idempotency_key: str json_data: Body extra_json: AnyMapping + follow_redirects: bool @final @@ -782,18 +781,19 @@ class FinalRequestOptions(pydantic.BaseModel): files: Union[HttpxRequestFiles, None] = None idempotency_key: Union[str, None] = None post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() + follow_redirects: Union[bool, None] = None # It should be noted that we cannot use `json` here as that would override # a BaseModel method in an incompatible fashion. json_data: Union[Body, None] = None extra_json: Union[AnyMapping, None] = None - if PYDANTIC_V2: - model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) - else: + if PYDANTIC_V1: class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] arbitrary_types_allowed: bool = True + else: + model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) def get_max_retries(self, max_retries: int) -> int: if isinstance(self.max_retries, NotGiven): @@ -826,11 +826,9 @@ def construct( # type: ignore key: strip_not_given(value) for key, value in values.items() } - if PYDANTIC_V2: - return super().model_construct(_fields_set, **kwargs) - return cast( - FinalRequestOptions, super().construct(_fields_set, **kwargs) - ) # pyright: ignore[reportDeprecated] + if PYDANTIC_V1: + return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] + return super().model_construct(_fields_set, **kwargs) if not TYPE_CHECKING: # type checkers incorrectly complain about this assignment diff --git a/src/hanzoai/_qs.py b/src/hanzoai/_qs.py new file mode 100644 index 000000000..ada6fd3f7 --- /dev/null +++ b/src/hanzoai/_qs.py @@ -0,0 +1,150 @@ +from __future__ import annotations + +from typing import Any, List, Tuple, Union, Mapping, TypeVar +from urllib.parse import parse_qs, urlencode +from typing_extensions import Literal, get_args + +from ._types import NotGiven, not_given +from ._utils import flatten + +_T = TypeVar("_T") + + +ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] +NestedFormat = Literal["dots", "brackets"] + +PrimitiveData = Union[str, int, float, bool, None] +# this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"] +# https://github.com/microsoft/pyright/issues/3555 +Data = Union[PrimitiveData, List[Any], Tuple[Any], "Mapping[str, Any]"] +Params = Mapping[str, Data] + + +class Querystring: + array_format: ArrayFormat + nested_format: NestedFormat + + def __init__( + self, + *, + array_format: ArrayFormat = "repeat", + nested_format: NestedFormat = "brackets", + ) -> None: + self.array_format = array_format + self.nested_format = nested_format + + def parse(self, query: str) -> Mapping[str, object]: + # Note: custom format syntax is not supported yet + return parse_qs(query) + + def stringify( + self, + params: Params, + *, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, + ) -> str: + return urlencode( + self.stringify_items( + params, + array_format=array_format, + nested_format=nested_format, + ) + ) + + def stringify_items( + self, + params: Params, + *, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, + ) -> list[tuple[str, str]]: + opts = Options( + qs=self, + array_format=array_format, + nested_format=nested_format, + ) + return flatten([self._stringify_item(key, value, opts) for key, value in params.items()]) + + def _stringify_item( + self, + key: str, + value: Data, + opts: Options, + ) -> list[tuple[str, str]]: + if isinstance(value, Mapping): + items: list[tuple[str, str]] = [] + nested_format = opts.nested_format + for subkey, subvalue in value.items(): + items.extend( + self._stringify_item( + # TODO: error if unknown format + f"{key}.{subkey}" if nested_format == "dots" else f"{key}[{subkey}]", + subvalue, + opts, + ) + ) + return items + + if isinstance(value, (list, tuple)): + array_format = opts.array_format + if array_format == "comma": + return [ + ( + key, + ",".join(self._primitive_value_to_str(item) for item in value if item is not None), + ), + ] + elif array_format == "repeat": + items = [] + for item in value: + items.extend(self._stringify_item(key, item, opts)) + return items + elif array_format == "indices": + raise NotImplementedError("The array indices format is not supported yet") + elif array_format == "brackets": + items = [] + key = key + "[]" + for item in value: + items.extend(self._stringify_item(key, item, opts)) + return items + else: + raise NotImplementedError( + f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" + ) + + serialised = self._primitive_value_to_str(value) + if not serialised: + return [] + return [(key, serialised)] + + def _primitive_value_to_str(self, value: PrimitiveData) -> str: + # copied from httpx + if value is True: + return "true" + elif value is False: + return "false" + elif value is None: + return "" + return str(value) + + +_qs = Querystring() +parse = _qs.parse +stringify = _qs.stringify +stringify_items = _qs.stringify_items + + +class Options: + array_format: ArrayFormat + nested_format: NestedFormat + + def __init__( + self, + qs: Querystring = _qs, + *, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, + ) -> None: + self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format + self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format diff --git a/pkg/hanzoai/_resource.py b/src/hanzoai/_resource.py similarity index 78% rename from pkg/hanzoai/_resource.py rename to src/hanzoai/_resource.py index 7fd1d92e1..f3eef0db9 100644 --- a/pkg/hanzoai/_resource.py +++ b/src/hanzoai/_resource.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -15,14 +15,12 @@ class SyncAPIResource: _client: Hanzo def __init__(self, client: Hanzo) -> None: - from ._base_client import SyncAPIClient - self._client = client self._get = client.get self._post = client.post self._patch = client.patch self._put = client.put - self._delete = SyncAPIClient.delete.__get__(client, type(client)) + self._delete = client.delete self._get_api_list = client.get_api_list def _sleep(self, seconds: float) -> None: @@ -33,14 +31,12 @@ class AsyncAPIResource: _client: AsyncHanzo def __init__(self, client: AsyncHanzo) -> None: - from ._base_client import AsyncAPIClient - self._client = client self._get = client.get self._post = client.post self._patch = client.patch self._put = client.put - self._delete = AsyncAPIClient.delete.__get__(client, type(client)) + self._delete = client.delete self._get_api_list = client.get_api_list async def _sleep(self, seconds: float) -> None: diff --git a/pkg/hanzoai/_response.py b/src/hanzoai/_response.py similarity index 88% rename from pkg/hanzoai/_response.py rename to src/hanzoai/_response.py index 0826d66ec..a99ced031 100644 --- a/pkg/hanzoai/_response.py +++ b/src/hanzoai/_response.py @@ -25,21 +25,10 @@ import pydantic from ._types import NoneType -from ._utils import ( - is_given, - extract_type_arg, - is_annotated_type, - is_type_alias_type, - extract_type_var_from_base, -) +from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type, extract_type_var_from_base from ._models import BaseModel, is_basemodel from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER -from ._streaming import ( - Stream, - AsyncStream, - is_stream_class_type, - extract_stream_chunk_type, -) +from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type from ._exceptions import HanzoError, APIResponseValidationError if TYPE_CHECKING: @@ -132,7 +121,9 @@ def is_closed(self) -> bool: @override def __repr__(self) -> str: - return f"<{self.__class__.__name__} [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_to}>" + return ( + f"<{self.__class__.__name__} [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_to}>" + ) def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to = to if to is not None else self._cast_to @@ -150,9 +141,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: if self._is_sse_stream: if to: if not is_stream_class_type(to): - raise TypeError( - f"Expected custom parse type to be a subclass of {Stream} or {AsyncStream}" - ) + raise TypeError(f"Expected custom parse type to be a subclass of {Stream} or {AsyncStream}") return cast( _T, @@ -176,10 +165,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: ), ) - stream_cls = cast( - "type[Stream[Any]] | type[AsyncStream[Any]] | None", - self._client._default_stream_cls, - ) + stream_cls = cast("type[Stream[Any]] | type[AsyncStream[Any]] | None", self._client._default_stream_cls) if stream_cls is None: raise MissingStreamClassError() @@ -196,19 +182,19 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: return cast(R, None) response = self.http_response - if cast_to is str: + if cast_to == str: return cast(R, response.text) - if cast_to is bytes: + if cast_to == bytes: return cast(R, response.content) - if cast_to is int: + if cast_to == int: return cast(R, int(response.text)) - if cast_to is float: + if cast_to == float: return cast(R, float(response.text)) - if cast_to is bool: + if cast_to == bool: return cast(R, response.text.lower() == "true") if origin == APIResponse: @@ -221,19 +207,17 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: # the response class ourselves but that is something that should be supported directly in httpx # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. if cast_to != httpx.Response: - raise ValueError( - f"Subclasses of httpx.Response cannot be passed to `cast_to`" - ) + raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") return cast(R, response) if ( - inspect.isclass(origin) # pyright: ignore[reportUnknownArgumentType] + inspect.isclass( + origin # pyright: ignore[reportUnknownArgumentType] + ) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel) ): - raise TypeError( - "Pydantic models must subclass our base model type, e.g. `from hanzoai import BaseModel`" - ) + raise TypeError("Pydantic models must subclass our base model type, e.g. `from hanzoai import BaseModel`") if ( cast_to is not object @@ -249,16 +233,12 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: # split is required to handle cases where additional information is included # in the response, e.g. application/json; charset=utf-8 content_type, *_ = response.headers.get("content-type", "*").split(";") - if content_type != "application/json": + if not content_type.endswith("json"): if is_basemodel(cast_to): try: data = response.json() except Exception as exc: - log.debug( - "Could not read JSON from response data due to %s - %s", - type(exc), - exc, - ) + log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc) else: return self._client._process_response_data( data=data, @@ -662,20 +642,14 @@ async def __aexit__( await self.__response.close() -def to_streamed_response_wrapper( - func: Callable[P, R], -) -> Callable[P, ResponseContextManager[APIResponse[R]]]: +def to_streamed_response_wrapper(func: Callable[P, R]) -> Callable[P, ResponseContextManager[APIResponse[R]]]: """Higher order function that takes one of our bound API methods and wraps it to support streaming and returning the raw `APIResponse` object directly. """ @functools.wraps(func) - def wrapped( - *args: P.args, **kwargs: P.kwargs - ) -> ResponseContextManager[APIResponse[R]]: - extra_headers: dict[str, str] = { - **(cast(Any, kwargs.get("extra_headers")) or {}) - } + def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[APIResponse[R]]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" kwargs["extra_headers"] = extra_headers @@ -695,21 +669,15 @@ def async_to_streamed_response_wrapper( """ @functools.wraps(func) - def wrapped( - *args: P.args, **kwargs: P.kwargs - ) -> AsyncResponseContextManager[AsyncAPIResponse[R]]: - extra_headers: dict[str, str] = { - **(cast(Any, kwargs.get("extra_headers")) or {}) - } + def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[AsyncAPIResponse[R]]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" kwargs["extra_headers"] = extra_headers make_request = func(*args, **kwargs) - return AsyncResponseContextManager( - cast(Awaitable[AsyncAPIResponse[R]], make_request) - ) + return AsyncResponseContextManager(cast(Awaitable[AsyncAPIResponse[R]], make_request)) return wrapped @@ -725,12 +693,8 @@ def to_custom_streamed_response_wrapper( """ @functools.wraps(func) - def wrapped( - *args: P.args, **kwargs: P.kwargs - ) -> ResponseContextManager[_APIResponseT]: - extra_headers: dict[str, Any] = { - **(cast(Any, kwargs.get("extra_headers")) or {}) - } + def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[_APIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls @@ -754,12 +718,8 @@ def async_to_custom_streamed_response_wrapper( """ @functools.wraps(func) - def wrapped( - *args: P.args, **kwargs: P.kwargs - ) -> AsyncResponseContextManager[_AsyncAPIResponseT]: - extra_headers: dict[str, Any] = { - **(cast(Any, kwargs.get("extra_headers")) or {}) - } + def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[_AsyncAPIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls @@ -767,9 +727,7 @@ def wrapped( make_request = func(*args, **kwargs) - return AsyncResponseContextManager( - cast(Awaitable[_AsyncAPIResponseT], make_request) - ) + return AsyncResponseContextManager(cast(Awaitable[_AsyncAPIResponseT], make_request)) return wrapped @@ -781,9 +739,7 @@ def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]] @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: - extra_headers: dict[str, str] = { - **(cast(Any, kwargs.get("extra_headers")) or {}) - } + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" kwargs["extra_headers"] = extra_headers @@ -793,18 +749,14 @@ def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: return wrapped -def async_to_raw_response_wrapper( - func: Callable[P, Awaitable[R]], -) -> Callable[P, Awaitable[AsyncAPIResponse[R]]]: +def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[AsyncAPIResponse[R]]]: """Higher order function that takes one of our bound API methods and wraps it to support returning the raw `APIResponse` object directly. """ @functools.wraps(func) async def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncAPIResponse[R]: - extra_headers: dict[str, str] = { - **(cast(Any, kwargs.get("extra_headers")) or {}) - } + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" kwargs["extra_headers"] = extra_headers @@ -826,9 +778,7 @@ def to_custom_raw_response_wrapper( @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> _APIResponseT: - extra_headers: dict[str, Any] = { - **(cast(Any, kwargs.get("extra_headers")) or {}) - } + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls @@ -851,9 +801,7 @@ def async_to_custom_raw_response_wrapper( @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> Awaitable[_AsyncAPIResponseT]: - extra_headers: dict[str, Any] = { - **(cast(Any, kwargs.get("extra_headers")) or {}) - } + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls @@ -877,8 +825,6 @@ class MyResponse(APIResponse[bytes]): """ return extract_type_var_from_base( typ, - generic_bases=cast( - "tuple[type, ...]", (BaseAPIResponse, APIResponse, AsyncAPIResponse) - ), + generic_bases=cast("tuple[type, ...]", (BaseAPIResponse, APIResponse, AsyncAPIResponse)), index=0, ) diff --git a/src/hanzoai/_streaming.py b/src/hanzoai/_streaming.py new file mode 100644 index 000000000..c9b62fe51 --- /dev/null +++ b/src/hanzoai/_streaming.py @@ -0,0 +1,331 @@ +# Note: initially copied from https://github.com/florimondmanca/httpx-sse/blob/master/src/httpx_sse/_decoders.py +from __future__ import annotations + +import json +import inspect +from types import TracebackType +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast +from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable + +import httpx + +from ._utils import extract_type_var_from_base + +if TYPE_CHECKING: + from ._client import Hanzo, AsyncHanzo + + +_T = TypeVar("_T") + + +class Stream(Generic[_T]): + """Provides the core interface to iterate over a synchronous stream response.""" + + response: httpx.Response + + _decoder: SSEBytesDecoder + + def __init__( + self, + *, + cast_to: type[_T], + response: httpx.Response, + client: Hanzo, + ) -> None: + self.response = response + self._cast_to = cast_to + self._client = client + self._decoder = client._make_sse_decoder() + self._iterator = self.__stream__() + + def __next__(self) -> _T: + return self._iterator.__next__() + + def __iter__(self) -> Iterator[_T]: + for item in self._iterator: + yield item + + def _iter_events(self) -> Iterator[ServerSentEvent]: + yield from self._decoder.iter_bytes(self.response.iter_bytes()) + + def __stream__(self) -> Iterator[_T]: + cast_to = cast(Any, self._cast_to) + response = self.response + process_data = self._client._process_response_data + iterator = self._iter_events() + + for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + + # As we might not fully consume the response stream, we need to close it explicitly + response.close() + + def __enter__(self) -> Self: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self.response.close() + + +class AsyncStream(Generic[_T]): + """Provides the core interface to iterate over an asynchronous stream response.""" + + response: httpx.Response + + _decoder: SSEDecoder | SSEBytesDecoder + + def __init__( + self, + *, + cast_to: type[_T], + response: httpx.Response, + client: AsyncHanzo, + ) -> None: + self.response = response + self._cast_to = cast_to + self._client = client + self._decoder = client._make_sse_decoder() + self._iterator = self.__stream__() + + async def __anext__(self) -> _T: + return await self._iterator.__anext__() + + async def __aiter__(self) -> AsyncIterator[_T]: + async for item in self._iterator: + yield item + + async def _iter_events(self) -> AsyncIterator[ServerSentEvent]: + async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()): + yield sse + + async def __stream__(self) -> AsyncIterator[_T]: + cast_to = cast(Any, self._cast_to) + response = self.response + process_data = self._client._process_response_data + iterator = self._iter_events() + + async for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + + # As we might not fully consume the response stream, we need to close it explicitly + await response.aclose() + + async def __aenter__(self) -> Self: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self.response.aclose() + + +class ServerSentEvent: + def __init__( + self, + *, + event: str | None = None, + data: str | None = None, + id: str | None = None, + retry: int | None = None, + ) -> None: + if data is None: + data = "" + + self._id = id + self._data = data + self._event = event or None + self._retry = retry + + @property + def event(self) -> str | None: + return self._event + + @property + def id(self) -> str | None: + return self._id + + @property + def retry(self) -> int | None: + return self._retry + + @property + def data(self) -> str: + return self._data + + def json(self) -> Any: + return json.loads(self.data) + + @override + def __repr__(self) -> str: + return f"ServerSentEvent(event={self.event}, data={self.data}, id={self.id}, retry={self.retry})" + + +class SSEDecoder: + _data: list[str] + _event: str | None + _retry: int | None + _last_event_id: str | None + + def __init__(self) -> None: + self._event = None + self._data = [] + self._last_event_id = None + self._retry = None + + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + for chunk in self._iter_chunks(iterator): + # Split before decoding so splitlines() only uses \r and \n + for raw_line in chunk.splitlines(): + line = raw_line.decode("utf-8") + sse = self.decode(line) + if sse: + yield sse + + def _iter_chunks(self, iterator: Iterator[bytes]) -> Iterator[bytes]: + """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" + data = b"" + for chunk in iterator: + for line in chunk.splitlines(keepends=True): + data += line + if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): + yield data + data = b"" + if data: + yield data + + async def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + async for chunk in self._aiter_chunks(iterator): + # Split before decoding so splitlines() only uses \r and \n + for raw_line in chunk.splitlines(): + line = raw_line.decode("utf-8") + sse = self.decode(line) + if sse: + yield sse + + async def _aiter_chunks(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[bytes]: + """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" + data = b"" + async for chunk in iterator: + for line in chunk.splitlines(keepends=True): + data += line + if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): + yield data + data = b"" + if data: + yield data + + def decode(self, line: str) -> ServerSentEvent | None: + # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 + + if not line: + if not self._event and not self._data and not self._last_event_id and self._retry is None: + return None + + sse = ServerSentEvent( + event=self._event, + data="\n".join(self._data), + id=self._last_event_id, + retry=self._retry, + ) + + # NOTE: as per the SSE spec, do not reset last_event_id. + self._event = None + self._data = [] + self._retry = None + + return sse + + if line.startswith(":"): + return None + + fieldname, _, value = line.partition(":") + + if value.startswith(" "): + value = value[1:] + + if fieldname == "event": + self._event = value + elif fieldname == "data": + self._data.append(value) + elif fieldname == "id": + if "\0" in value: + pass + else: + self._last_event_id = value + elif fieldname == "retry": + try: + self._retry = int(value) + except (TypeError, ValueError): + pass + else: + pass # Field is ignored. + + return None + + +@runtime_checkable +class SSEBytesDecoder(Protocol): + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given an async iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + +def is_stream_class_type(typ: type) -> TypeGuard[type[Stream[object]] | type[AsyncStream[object]]]: + """TypeGuard for determining whether or not the given type is a subclass of `Stream` / `AsyncStream`""" + origin = get_origin(typ) or typ + return inspect.isclass(origin) and issubclass(origin, (Stream, AsyncStream)) + + +def extract_stream_chunk_type( + stream_cls: type, + *, + failure_message: str | None = None, +) -> type: + """Given a type like `Stream[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyStream(Stream[bytes]): + ... + + extract_stream_chunk_type(MyStream) -> bytes + ``` + """ + from ._base_client import Stream, AsyncStream + + return extract_type_var_from_base( + stream_cls, + index=0, + generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)), + failure_message=failure_message, + ) diff --git a/src/hanzoai/_types.py b/src/hanzoai/_types.py new file mode 100644 index 000000000..b81cfc770 --- /dev/null +++ b/src/hanzoai/_types.py @@ -0,0 +1,260 @@ +from __future__ import annotations + +from os import PathLike +from typing import ( + IO, + TYPE_CHECKING, + Any, + Dict, + List, + Type, + Tuple, + Union, + Mapping, + TypeVar, + Callable, + Iterator, + Optional, + Sequence, +) +from typing_extensions import ( + Set, + Literal, + Protocol, + TypeAlias, + TypedDict, + SupportsIndex, + overload, + override, + runtime_checkable, +) + +import httpx +import pydantic +from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport + +if TYPE_CHECKING: + from ._models import BaseModel + from ._response import APIResponse, AsyncAPIResponse + +Transport = BaseTransport +AsyncTransport = AsyncBaseTransport +Query = Mapping[str, object] +Body = object +AnyMapping = Mapping[str, object] +ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) +_T = TypeVar("_T") + + +# Approximates httpx internal ProxiesTypes and RequestFiles types +# while adding support for `PathLike` instances +ProxiesDict = Dict["str | URL", Union[None, str, URL, Proxy]] +ProxiesTypes = Union[str, Proxy, ProxiesDict] +if TYPE_CHECKING: + Base64FileInput = Union[IO[bytes], PathLike[str]] + FileContent = Union[IO[bytes], bytes, PathLike[str]] +else: + Base64FileInput = Union[IO[bytes], PathLike] + FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8. +FileTypes = Union[ + # file (or bytes) + FileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], FileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], FileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], +] +RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]] + +# duplicate of the above but without our custom file support +HttpxFileContent = Union[IO[bytes], bytes] +HttpxFileTypes = Union[ + # file (or bytes) + HttpxFileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], HttpxFileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], HttpxFileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], HttpxFileContent, Optional[str], Mapping[str, str]], +] +HttpxRequestFiles = Union[Mapping[str, HttpxFileTypes], Sequence[Tuple[str, HttpxFileTypes]]] + +# Workaround to support (cast_to: Type[ResponseT]) -> ResponseT +# where ResponseT includes `None`. In order to support directly +# passing `None`, overloads would have to be defined for every +# method that uses `ResponseT` which would lead to an unacceptable +# amount of code duplication and make it unreadable. See _base_client.py +# for example usage. +# +# This unfortunately means that you will either have +# to import this type and pass it explicitly: +# +# from hanzoai import NoneType +# client.get('/foo', cast_to=NoneType) +# +# or build it yourself: +# +# client.get('/foo', cast_to=type(None)) +if TYPE_CHECKING: + NoneType: Type[None] +else: + NoneType = type(None) + + +class RequestOptions(TypedDict, total=False): + headers: Headers + max_retries: int + timeout: float | Timeout | None + params: Query + extra_json: AnyMapping + idempotency_key: str + follow_redirects: bool + + +# Sentinel class used until PEP 0661 is accepted +class NotGiven: + """ + For parameters with a meaningful None value, we need to distinguish between + the user explicitly passing None, and the user not passing the parameter at + all. + + User code shouldn't need to use not_given directly. + + For example: + + ```py + def create(timeout: Timeout | None | NotGiven = not_given): ... + + + create(timeout=1) # 1s timeout + create(timeout=None) # No timeout + create() # Default timeout behavior + ``` + """ + + def __bool__(self) -> Literal[False]: + return False + + @override + def __repr__(self) -> str: + return "NOT_GIVEN" + + +not_given = NotGiven() +# for backwards compatibility: +NOT_GIVEN = NotGiven() + + +class Omit: + """ + To explicitly omit something from being sent in a request, use `omit`. + + ```py + # as the default `Content-Type` header is `application/json` that will be sent + client.post("/upload/files", files={"file": b"my raw file content"}) + + # you can't explicitly override the header as it has to be dynamically generated + # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' + client.post(..., headers={"Content-Type": "multipart/form-data"}) + + # instead you can remove the default `application/json` header by passing omit + client.post(..., headers={"Content-Type": omit}) + ``` + """ + + def __bool__(self) -> Literal[False]: + return False + + +omit = Omit() + + +@runtime_checkable +class ModelBuilderProtocol(Protocol): + @classmethod + def build( + cls: type[_T], + *, + response: Response, + data: object, + ) -> _T: ... + + +Headers = Mapping[str, Union[str, Omit]] + + +class HeadersLikeProtocol(Protocol): + def get(self, __key: str) -> str | None: ... + + +HeadersLike = Union[Headers, HeadersLikeProtocol] + +ResponseT = TypeVar( + "ResponseT", + bound=Union[ + object, + str, + None, + "BaseModel", + List[Any], + Dict[str, Any], + Response, + ModelBuilderProtocol, + "APIResponse[Any]", + "AsyncAPIResponse[Any]", + ], +) + +StrBytesIntFloat = Union[str, bytes, int, float] + +# Note: copied from Pydantic +# https://github.com/pydantic/pydantic/blob/6f31f8f68ef011f84357330186f603ff295312fd/pydantic/main.py#L79 +IncEx: TypeAlias = Union[Set[int], Set[str], Mapping[int, Union["IncEx", bool]], Mapping[str, Union["IncEx", bool]]] + +PostParser = Callable[[Any], Any] + + +@runtime_checkable +class InheritsGeneric(Protocol): + """Represents a type that has inherited from `Generic` + + The `__orig_bases__` property can be used to determine the resolved + type variable for a given base class. + """ + + __orig_bases__: tuple[_GenericAlias] + + +class _GenericAlias(Protocol): + __origin__: type[object] + + +class HttpxSendArgs(TypedDict, total=False): + auth: httpx.Auth + follow_redirects: bool + + +_T_co = TypeVar("_T_co", covariant=True) + + +if TYPE_CHECKING: + # This works because str.__contains__ does not accept object (either in typeshed or at runtime) + # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285 + class SequenceNotStr(Protocol[_T_co]): + @overload + def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... + @overload + def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... + def __contains__(self, value: object, /) -> bool: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[_T_co]: ... + def index(self, value: Any, start: int = 0, stop: int = ..., /) -> int: ... + def count(self, value: Any, /) -> int: ... + def __reversed__(self) -> Iterator[_T_co]: ... +else: + # just point this to a normal `Sequence` at runtime to avoid having to special case + # deserializing our custom sequence type + SequenceNotStr = Sequence diff --git a/src/hanzoai/_utils/__init__.py b/src/hanzoai/_utils/__init__.py new file mode 100644 index 000000000..dc64e29a1 --- /dev/null +++ b/src/hanzoai/_utils/__init__.py @@ -0,0 +1,64 @@ +from ._sync import asyncify as asyncify +from ._proxy import LazyProxy as LazyProxy +from ._utils import ( + flatten as flatten, + is_dict as is_dict, + is_list as is_list, + is_given as is_given, + is_tuple as is_tuple, + json_safe as json_safe, + lru_cache as lru_cache, + is_mapping as is_mapping, + is_tuple_t as is_tuple_t, + is_iterable as is_iterable, + is_sequence as is_sequence, + coerce_float as coerce_float, + is_mapping_t as is_mapping_t, + removeprefix as removeprefix, + removesuffix as removesuffix, + extract_files as extract_files, + is_sequence_t as is_sequence_t, + required_args as required_args, + coerce_boolean as coerce_boolean, + coerce_integer as coerce_integer, + file_from_path as file_from_path, + strip_not_given as strip_not_given, + deepcopy_minimal as deepcopy_minimal, + get_async_library as get_async_library, + maybe_coerce_float as maybe_coerce_float, + get_required_header as get_required_header, + maybe_coerce_boolean as maybe_coerce_boolean, + maybe_coerce_integer as maybe_coerce_integer, +) +from ._compat import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, +) +from ._typing import ( + is_list_type as is_list_type, + is_union_type as is_union_type, + extract_type_arg as extract_type_arg, + is_iterable_type as is_iterable_type, + is_required_type as is_required_type, + is_sequence_type as is_sequence_type, + is_annotated_type as is_annotated_type, + is_type_alias_type as is_type_alias_type, + strip_annotated_type as strip_annotated_type, + extract_type_var_from_base as extract_type_var_from_base, +) +from ._streams import consume_sync_iterator as consume_sync_iterator, consume_async_iterator as consume_async_iterator +from ._transform import ( + PropertyInfo as PropertyInfo, + transform as transform, + async_transform as async_transform, + maybe_transform as maybe_transform, + async_maybe_transform as async_maybe_transform, +) +from ._reflection import ( + function_has_argument as function_has_argument, + assert_signatures_in_sync as assert_signatures_in_sync, +) +from ._datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime diff --git a/src/hanzoai/_utils/_compat.py b/src/hanzoai/_utils/_compat.py new file mode 100644 index 000000000..dd703233c --- /dev/null +++ b/src/hanzoai/_utils/_compat.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import sys +import typing_extensions +from typing import Any, Type, Union, Literal, Optional +from datetime import date, datetime +from typing_extensions import get_args as _get_args, get_origin as _get_origin + +from .._types import StrBytesIntFloat +from ._datetime_parse import parse_date as _parse_date, parse_datetime as _parse_datetime + +_LITERAL_TYPES = {Literal, typing_extensions.Literal} + + +def get_args(tp: type[Any]) -> tuple[Any, ...]: + return _get_args(tp) + + +def get_origin(tp: type[Any]) -> type[Any] | None: + return _get_origin(tp) + + +def is_union(tp: Optional[Type[Any]]) -> bool: + if sys.version_info < (3, 10): + return tp is Union # type: ignore[comparison-overlap] + else: + import types + + return tp is Union or tp is types.UnionType + + +def is_typeddict(tp: Type[Any]) -> bool: + return typing_extensions.is_typeddict(tp) + + +def is_literal_type(tp: Type[Any]) -> bool: + return get_origin(tp) in _LITERAL_TYPES + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + return _parse_date(value) + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + return _parse_datetime(value) diff --git a/src/hanzoai/_utils/_datetime_parse.py b/src/hanzoai/_utils/_datetime_parse.py new file mode 100644 index 000000000..7cb9d9e66 --- /dev/null +++ b/src/hanzoai/_utils/_datetime_parse.py @@ -0,0 +1,136 @@ +""" +This file contains code from https://github.com/pydantic/pydantic/blob/main/pydantic/v1/datetime_parse.py +without the Pydantic v1 specific errors. +""" + +from __future__ import annotations + +import re +from typing import Dict, Union, Optional +from datetime import date, datetime, timezone, timedelta + +from .._types import StrBytesIntFloat + +date_expr = r"(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})" +time_expr = ( + r"(?P\d{1,2}):(?P\d{1,2})" + r"(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?" + r"(?PZ|[+-]\d{2}(?::?\d{2})?)?$" +) + +date_re = re.compile(f"{date_expr}$") +datetime_re = re.compile(f"{date_expr}[T ]{time_expr}") + + +EPOCH = datetime(1970, 1, 1) +# if greater than this, the number is in ms, if less than or equal it's in seconds +# (in seconds this is 11th October 2603, in ms it's 20th August 1970) +MS_WATERSHED = int(2e10) +# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9 +MAX_NUMBER = int(3e20) + + +def _get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]: + if isinstance(value, (int, float)): + return value + try: + return float(value) + except ValueError: + return None + except TypeError: + raise TypeError(f"invalid type; expected {native_expected_type}, string, bytes, int or float") from None + + +def _from_unix_seconds(seconds: Union[int, float]) -> datetime: + if seconds > MAX_NUMBER: + return datetime.max + elif seconds < -MAX_NUMBER: + return datetime.min + + while abs(seconds) > MS_WATERSHED: + seconds /= 1000 + dt = EPOCH + timedelta(seconds=seconds) + return dt.replace(tzinfo=timezone.utc) + + +def _parse_timezone(value: Optional[str]) -> Union[None, int, timezone]: + if value == "Z": + return timezone.utc + elif value is not None: + offset_mins = int(value[-2:]) if len(value) > 3 else 0 + offset = 60 * int(value[1:3]) + offset_mins + if value[0] == "-": + offset = -offset + return timezone(timedelta(minutes=offset)) + else: + return None + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + """ + Parse a datetime/int/float/string and return a datetime.datetime. + + This function supports time zone offsets. When the input contains one, + the output uses a timezone with a fixed offset from UTC. + + Raise ValueError if the input is well formatted but not a valid datetime. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, datetime): + return value + + number = _get_numeric(value, "datetime") + if number is not None: + return _from_unix_seconds(number) + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + + match = datetime_re.match(value) + if match is None: + raise ValueError("invalid datetime format") + + kw = match.groupdict() + if kw["microsecond"]: + kw["microsecond"] = kw["microsecond"].ljust(6, "0") + + tzinfo = _parse_timezone(kw.pop("tzinfo")) + kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None} + kw_["tzinfo"] = tzinfo + + return datetime(**kw_) # type: ignore + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + """ + Parse a date/int/float/string and return a datetime.date. + + Raise ValueError if the input is well formatted but not a valid date. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, date): + if isinstance(value, datetime): + return value.date() + else: + return value + + number = _get_numeric(value, "date") + if number is not None: + return _from_unix_seconds(number).date() + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + match = date_re.match(value) + if match is None: + raise ValueError("invalid date format") + + kw = {k: int(v) for k, v in match.groupdict().items()} + + try: + return date(**kw) + except ValueError: + raise ValueError("invalid date format") from None diff --git a/pkg/hanzoai/_utils/_logs.py b/src/hanzoai/_utils/_logs.py similarity index 100% rename from pkg/hanzoai/_utils/_logs.py rename to src/hanzoai/_utils/_logs.py diff --git a/pkg/hanzoai/_utils/_proxy.py b/src/hanzoai/_utils/_proxy.py similarity index 94% rename from pkg/hanzoai/_utils/_proxy.py rename to src/hanzoai/_utils/_proxy.py index ffd883e9d..0f239a33c 100644 --- a/pkg/hanzoai/_utils/_proxy.py +++ b/src/hanzoai/_utils/_proxy.py @@ -46,7 +46,10 @@ def __dir__(self) -> Iterable[str]: @property # type: ignore @override def __class__(self) -> type: # pyright: ignore - proxied = self.__get_proxied__() + try: + proxied = self.__get_proxied__() + except Exception: + return type(self) if issubclass(type(proxied), LazyProxy): return type(proxied) return proxied.__class__ diff --git a/pkg/hanzoai/_utils/_reflection.py b/src/hanzoai/_utils/_reflection.py similarity index 89% rename from pkg/hanzoai/_utils/_reflection.py rename to src/hanzoai/_utils/_reflection.py index 0f52d5eba..89aa712ac 100644 --- a/pkg/hanzoai/_utils/_reflection.py +++ b/src/hanzoai/_utils/_reflection.py @@ -39,7 +39,4 @@ def assert_signatures_in_sync( continue if errors: - raise AssertionError( - f"{len(errors)} errors encountered when comparing signatures:\n\n" - + "\n\n".join(errors) - ) + raise AssertionError(f"{len(errors)} errors encountered when comparing signatures:\n\n" + "\n\n".join(errors)) diff --git a/src/hanzoai/_utils/_resources_proxy.py b/src/hanzoai/_utils/_resources_proxy.py new file mode 100644 index 000000000..f877db10b --- /dev/null +++ b/src/hanzoai/_utils/_resources_proxy.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import Any +from typing_extensions import override + +from ._proxy import LazyProxy + + +class ResourcesProxy(LazyProxy[Any]): + """A proxy for the `hanzoai.resources` module. + + This is used so that we can lazily import `hanzoai.resources` only when + needed *and* so that users can just import `hanzoai` and reference `hanzoai.resources` + """ + + @override + def __load__(self) -> Any: + import importlib + + mod = importlib.import_module("hanzoai.resources") + return mod + + +resources = ResourcesProxy().__as_proxied__() diff --git a/pkg/hanzoai/_utils/_streams.py b/src/hanzoai/_utils/_streams.py similarity index 100% rename from pkg/hanzoai/_utils/_streams.py rename to src/hanzoai/_utils/_streams.py diff --git a/pkg/hanzoai/_utils/_sync.py b/src/hanzoai/_utils/_sync.py similarity index 84% rename from pkg/hanzoai/_utils/_sync.py rename to src/hanzoai/_utils/_sync.py index 5cc28c845..ad7ec71b7 100644 --- a/pkg/hanzoai/_utils/_sync.py +++ b/src/hanzoai/_utils/_sync.py @@ -21,10 +21,7 @@ # backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread # for Python 3.8 support async def _asyncio_to_thread( - func: Callable[T_ParamSpec, T_Retval], - /, - *args: T_ParamSpec.args, - **kwargs: T_ParamSpec.kwargs, + func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs ) -> Any: """Asynchronously run function *func* in a separate thread. @@ -42,10 +39,7 @@ async def _asyncio_to_thread( async def to_thread( - func: Callable[T_ParamSpec, T_Retval], - /, - *args: T_ParamSpec.args, - **kwargs: T_ParamSpec.kwargs, + func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs ) -> T_Retval: if sniffio.current_async_library() == "asyncio": return await _asyncio_to_thread(func, *args, **kwargs) @@ -56,9 +50,7 @@ async def to_thread( # inspired by `asyncer`, https://github.com/tiangolo/asyncer -def asyncify( - function: Callable[T_ParamSpec, T_Retval], -) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: +def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: """ Take a blocking function and create an async one that receives the same positional and keyword arguments. For python version 3.9 and above, it uses @@ -88,9 +80,7 @@ def blocking_func(arg1, arg2, kwarg1=None): and returns the result. """ - async def wrapper( - *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs - ) -> T_Retval: + async def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval: return await to_thread(function, *args, **kwargs) return wrapper diff --git a/pkg/hanzoai/_utils/_transform.py b/src/hanzoai/_utils/_transform.py similarity index 78% rename from pkg/hanzoai/_utils/_transform.py rename to src/hanzoai/_utils/_transform.py index c56af4d93..520754920 100644 --- a/pkg/hanzoai/_utils/_transform.py +++ b/src/hanzoai/_utils/_transform.py @@ -5,27 +5,31 @@ import pathlib from typing import Any, Mapping, TypeVar, cast from datetime import date, datetime -from typing_extensions import Literal, get_args, override, get_type_hints +from typing_extensions import Literal, get_args, override, get_type_hints as _get_type_hints import anyio import pydantic from ._utils import ( is_list, + is_given, + lru_cache, is_mapping, is_iterable, + is_sequence, ) from .._files import is_base64_file_input +from ._compat import get_origin, is_typeddict from ._typing import ( is_list_type, is_union_type, extract_type_arg, is_iterable_type, is_required_type, + is_sequence_type, is_annotated_type, strip_annotated_type, ) -from .._compat import get_origin, model_dump, is_typeddict _T = TypeVar("_T") @@ -108,6 +112,7 @@ class Params(TypedDict, total=False): return cast(_T, transformed) +@lru_cache(maxsize=8096) def _get_annotated_type(type_: type) -> type | None: """If the given type is an `Annotated` type then it is returned, if not `None` is returned. @@ -142,6 +147,10 @@ def _maybe_transform_key(key: str, type_: type) -> str: return key +def _no_transform_needed(annotation: type) -> bool: + return annotation == float or annotation == int + + def _transform_recursive( data: object, *, @@ -160,6 +169,8 @@ def _transform_recursive( Defaults to the same value as the `annotation` argument. """ + from .._compat import model_dump + if inner_type is None: inner_type = annotation @@ -170,20 +181,15 @@ def _transform_recursive( if origin == dict and is_mapping(data): items_type = get_args(stripped_type)[1] - return { - key: _transform_recursive(value, annotation=items_type) - for key, value in data.items() - } + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} if ( # List[T] (is_list_type(stripped_type) and is_list(data)) # Iterable[T] - or ( - is_iterable_type(stripped_type) - and is_iterable(data) - and not isinstance(data, str) - ) + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) ): # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually # intended as an iterable, so we don't transform it. @@ -191,10 +197,16 @@ def _transform_recursive( return cast(object, data) inner_type = extract_type_arg(stripped_type, 0) - return [ - _transform_recursive(d, annotation=annotation, inner_type=inner_type) - for d in data - ] + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + + return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] if is_union_type(stripped_type): # For union types we run the transformation against all subtypes to ensure that everything is transformed. @@ -221,9 +233,7 @@ def _transform_recursive( return data -def _format_data( - data: object, format_: PropertyFormat, format_template: str | None -) -> object: +def _format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: if isinstance(data, (date, datetime)): if format_ == "iso8601": return data.isoformat() @@ -243,9 +253,7 @@ def _format_data( binary = binary.encode() if not isinstance(binary, bytes): - raise RuntimeError( - f"Could not read bytes from {data}; Received {type(binary)}" - ) + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") return base64.b64encode(binary).decode("ascii") @@ -259,14 +267,17 @@ def _transform_typeddict( result: dict[str, object] = {} annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): + if not is_given(value): + # we don't need to include omitted values here as they'll + # be stripped out before the request is sent anyway + continue + type_ = annotations.get(key) if type_ is None: # we do not have a type annotation for this field, leave it as is result[key] = value else: - result[_maybe_transform_key(key, type_)] = _transform_recursive( - value, annotation=type_ - ) + result[_maybe_transform_key(key, type_)] = _transform_recursive(value, annotation=type_) return result @@ -302,9 +313,7 @@ class Params(TypedDict, total=False): It should be noted that the transformations that this function does are not represented in the type system. """ - transformed = await _async_transform_recursive( - data, annotation=cast(type, expected_type) - ) + transformed = await _async_transform_recursive(data, annotation=cast(type, expected_type)) return cast(_T, transformed) @@ -326,6 +335,8 @@ async def _async_transform_recursive( Defaults to the same value as the `annotation` argument. """ + from .._compat import model_dump + if inner_type is None: inner_type = annotation @@ -336,20 +347,15 @@ async def _async_transform_recursive( if origin == dict and is_mapping(data): items_type = get_args(stripped_type)[1] - return { - key: _transform_recursive(value, annotation=items_type) - for key, value in data.items() - } + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} if ( # List[T] (is_list_type(stripped_type) and is_list(data)) # Iterable[T] - or ( - is_iterable_type(stripped_type) - and is_iterable(data) - and not isinstance(data, str) - ) + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) ): # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually # intended as an iterable, so we don't transform it. @@ -357,12 +363,16 @@ async def _async_transform_recursive( return cast(object, data) inner_type = extract_type_arg(stripped_type, 0) - return [ - await _async_transform_recursive( - d, annotation=annotation, inner_type=inner_type - ) - for d in data - ] + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + + return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] if is_union_type(stripped_type): # For union types we run the transformation against all subtypes to ensure that everything is transformed. @@ -370,9 +380,7 @@ async def _async_transform_recursive( # TODO: there may be edge cases where the same normalized field name will transform to two different names # in different subtypes. for subtype in get_args(stripped_type): - data = await _async_transform_recursive( - data, annotation=annotation, inner_type=subtype - ) + data = await _async_transform_recursive(data, annotation=annotation, inner_type=subtype) return data if isinstance(data, pydantic.BaseModel): @@ -386,16 +394,12 @@ async def _async_transform_recursive( annotations = get_args(annotated_type)[1:] for annotation in annotations: if isinstance(annotation, PropertyInfo) and annotation.format is not None: - return await _async_format_data( - data, annotation.format, annotation.format_template - ) + return await _async_format_data(data, annotation.format, annotation.format_template) return data -async def _async_format_data( - data: object, format_: PropertyFormat, format_template: str | None -) -> object: +async def _async_format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: if isinstance(data, (date, datetime)): if format_ == "iso8601": return data.isoformat() @@ -415,9 +419,7 @@ async def _async_format_data( binary = binary.encode() if not isinstance(binary, bytes): - raise RuntimeError( - f"Could not read bytes from {data}; Received {type(binary)}" - ) + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") return base64.b64encode(binary).decode("ascii") @@ -431,12 +433,25 @@ async def _async_transform_typeddict( result: dict[str, object] = {} annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): + if not is_given(value): + # we don't need to include omitted values here as they'll + # be stripped out before the request is sent anyway + continue + type_ = annotations.get(key) if type_ is None: # we do not have a type annotation for this field, leave it as is result[key] = value else: - result[_maybe_transform_key(key, type_)] = await _async_transform_recursive( - value, annotation=type_ - ) + result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_) return result + + +@lru_cache(maxsize=8096) +def get_type_hints( + obj: Any, + globalns: dict[str, Any] | None = None, + localns: Mapping[str, Any] | None = None, + include_extras: bool = False, +) -> dict[str, Any]: + return _get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras) diff --git a/pkg/hanzoai/_utils/_typing.py b/src/hanzoai/_utils/_typing.py similarity index 86% rename from pkg/hanzoai/_utils/_typing.py rename to src/hanzoai/_utils/_typing.py index 18ed72993..193109f3a 100644 --- a/pkg/hanzoai/_utils/_typing.py +++ b/src/hanzoai/_utils/_typing.py @@ -13,8 +13,9 @@ get_origin, ) +from ._utils import lru_cache from .._types import InheritsGeneric -from .._compat import is_union as _is_union +from ._compat import is_union as _is_union def is_annotated_type(typ: type) -> bool: @@ -25,6 +26,11 @@ def is_list_type(typ: type) -> bool: return (get_origin(typ) or typ) == list +def is_sequence_type(typ: type) -> bool: + origin = get_origin(typ) or typ + return origin == typing_extensions.Sequence or origin == typing.Sequence or origin == _c_abc.Sequence + + def is_iterable_type(typ: type) -> bool: """If the given type is `typing.Iterable[T]`""" origin = get_origin(typ) or typ @@ -45,9 +51,7 @@ def is_typevar(typ: type) -> bool: return type(typ) == TypeVar # type: ignore -_TYPE_ALIAS_TYPES: tuple[type[typing_extensions.TypeAliasType], ...] = ( - typing_extensions.TypeAliasType, -) +_TYPE_ALIAS_TYPES: tuple[type[typing_extensions.TypeAliasType], ...] = (typing_extensions.TypeAliasType,) if sys.version_info >= (3, 12): _TYPE_ALIAS_TYPES = (*_TYPE_ALIAS_TYPES, typing.TypeAliasType) @@ -68,6 +72,7 @@ def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]: # Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]] +@lru_cache(maxsize=8096) def strip_annotated_type(typ: type) -> type: if is_required_type(typ) or is_annotated_type(typ): return strip_annotated_type(cast(type, get_args(typ)[0])) @@ -80,9 +85,7 @@ def extract_type_arg(typ: type, index: int) -> type: try: return cast(type, args[index]) except IndexError as err: - raise RuntimeError( - f"Expected type {typ} to have a type argument at index {index} but it did not" - ) from err + raise RuntimeError(f"Expected type {typ} to have a type argument at index {index} but it did not") from err def extract_type_var_from_base( @@ -112,7 +115,7 @@ class MyResponse(Foo[_T]): ``` """ cls = cast(object, get_origin(typ) or typ) - if cls in generic_bases: + if cls in generic_bases: # pyright: ignore[reportUnnecessaryContains] # we're given the class directly return extract_type_arg(typ, index) @@ -150,7 +153,4 @@ class MyResponse(Foo[_T]): return extracted - raise RuntimeError( - failure_message - or f"Could not resolve inner type variable at index {index} for {typ}" - ) + raise RuntimeError(failure_message or f"Could not resolve inner type variable at index {index} for {typ}") diff --git a/src/hanzoai/_utils/_utils.py b/src/hanzoai/_utils/_utils.py new file mode 100644 index 000000000..eec7f4a1f --- /dev/null +++ b/src/hanzoai/_utils/_utils.py @@ -0,0 +1,421 @@ +from __future__ import annotations + +import os +import re +import inspect +import functools +from typing import ( + Any, + Tuple, + Mapping, + TypeVar, + Callable, + Iterable, + Sequence, + cast, + overload, +) +from pathlib import Path +from datetime import date, datetime +from typing_extensions import TypeGuard + +import sniffio + +from .._types import Omit, NotGiven, FileTypes, HeadersLike + +_T = TypeVar("_T") +_TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) +_MappingT = TypeVar("_MappingT", bound=Mapping[str, object]) +_SequenceT = TypeVar("_SequenceT", bound=Sequence[object]) +CallableT = TypeVar("CallableT", bound=Callable[..., Any]) + + +def flatten(t: Iterable[Iterable[_T]]) -> list[_T]: + return [item for sublist in t for item in sublist] + + +def extract_files( + # TODO: this needs to take Dict but variance issues..... + # create protocol type ? + query: Mapping[str, object], + *, + paths: Sequence[Sequence[str]], +) -> list[tuple[str, FileTypes]]: + """Recursively extract files from the given dictionary based on specified paths. + + A path may look like this ['foo', 'files', '', 'data']. + + Note: this mutates the given dictionary. + """ + files: list[tuple[str, FileTypes]] = [] + for path in paths: + files.extend(_extract_items(query, path, index=0, flattened_key=None)) + return files + + +def _extract_items( + obj: object, + path: Sequence[str], + *, + index: int, + flattened_key: str | None, +) -> list[tuple[str, FileTypes]]: + try: + key = path[index] + except IndexError: + if not is_given(obj): + # no value was provided - we can safely ignore + return [] + + # cyclical import + from .._files import assert_is_file_content + + # We have exhausted the path, return the entry we found. + assert flattened_key is not None + + if is_list(obj): + files: list[tuple[str, FileTypes]] = [] + for entry in obj: + assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "") + files.append((flattened_key + "[]", cast(FileTypes, entry))) + return files + + assert_is_file_content(obj, key=flattened_key) + return [(flattened_key, cast(FileTypes, obj))] + + index += 1 + if is_dict(obj): + try: + # We are at the last entry in the path so we must remove the field + if (len(path)) == index: + item = obj.pop(key) + else: + item = obj[key] + except KeyError: + # Key was not present in the dictionary, this is not indicative of an error + # as the given path may not point to a required field. We also do not want + # to enforce required fields as the API may differ from the spec in some cases. + return [] + if flattened_key is None: + flattened_key = key + else: + flattened_key += f"[{key}]" + return _extract_items( + item, + path, + index=index, + flattened_key=flattened_key, + ) + elif is_list(obj): + if key != "": + return [] + + return flatten( + [ + _extract_items( + item, + path, + index=index, + flattened_key=flattened_key + "[]" if flattened_key is not None else "[]", + ) + for item in obj + ] + ) + + # Something unexpected was passed, just ignore it. + return [] + + +def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]: + return not isinstance(obj, NotGiven) and not isinstance(obj, Omit) + + +# Type safe methods for narrowing types with TypeVars. +# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], +# however this cause Pyright to rightfully report errors. As we know we don't +# care about the contained types we can safely use `object` in its place. +# +# There are two separate functions defined, `is_*` and `is_*_t` for different use cases. +# `is_*` is for when you're dealing with an unknown input +# `is_*_t` is for when you're narrowing a known union type to a specific subset + + +def is_tuple(obj: object) -> TypeGuard[tuple[object, ...]]: + return isinstance(obj, tuple) + + +def is_tuple_t(obj: _TupleT | object) -> TypeGuard[_TupleT]: + return isinstance(obj, tuple) + + +def is_sequence(obj: object) -> TypeGuard[Sequence[object]]: + return isinstance(obj, Sequence) + + +def is_sequence_t(obj: _SequenceT | object) -> TypeGuard[_SequenceT]: + return isinstance(obj, Sequence) + + +def is_mapping(obj: object) -> TypeGuard[Mapping[str, object]]: + return isinstance(obj, Mapping) + + +def is_mapping_t(obj: _MappingT | object) -> TypeGuard[_MappingT]: + return isinstance(obj, Mapping) + + +def is_dict(obj: object) -> TypeGuard[dict[object, object]]: + return isinstance(obj, dict) + + +def is_list(obj: object) -> TypeGuard[list[object]]: + return isinstance(obj, list) + + +def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: + return isinstance(obj, Iterable) + + +def deepcopy_minimal(item: _T) -> _T: + """Minimal reimplementation of copy.deepcopy() that will only copy certain object types: + + - mappings, e.g. `dict` + - list + + This is done for performance reasons. + """ + if is_mapping(item): + return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()}) + if is_list(item): + return cast(_T, [deepcopy_minimal(entry) for entry in item]) + return item + + +# copied from https://github.com/Rapptz/RoboDanny +def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str: + size = len(seq) + if size == 0: + return "" + + if size == 1: + return seq[0] + + if size == 2: + return f"{seq[0]} {final} {seq[1]}" + + return delim.join(seq[:-1]) + f" {final} {seq[-1]}" + + +def quote(string: str) -> str: + """Add single quotation marks around the given string. Does *not* do any escaping.""" + return f"'{string}'" + + +def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: + """Decorator to enforce a given set of arguments or variants of arguments are passed to the decorated function. + + Useful for enforcing runtime validation of overloaded functions. + + Example usage: + ```py + @overload + def foo(*, a: str) -> str: ... + + + @overload + def foo(*, b: bool) -> str: ... + + + # This enforces the same constraints that a static type checker would + # i.e. that either a or b must be passed to the function + @required_args(["a"], ["b"]) + def foo(*, a: str | None = None, b: bool | None = None) -> str: ... + ``` + """ + + def inner(func: CallableT) -> CallableT: + params = inspect.signature(func).parameters + positional = [ + name + for name, param in params.items() + if param.kind + in { + param.POSITIONAL_ONLY, + param.POSITIONAL_OR_KEYWORD, + } + ] + + @functools.wraps(func) + def wrapper(*args: object, **kwargs: object) -> object: + given_params: set[str] = set() + for i, _ in enumerate(args): + try: + given_params.add(positional[i]) + except IndexError: + raise TypeError( + f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given" + ) from None + + for key in kwargs.keys(): + given_params.add(key) + + for variant in variants: + matches = all((param in given_params for param in variant)) + if matches: + break + else: # no break + if len(variants) > 1: + variations = human_join( + ["(" + human_join([quote(arg) for arg in variant], final="and") + ")" for variant in variants] + ) + msg = f"Missing required arguments; Expected either {variations} arguments to be given" + else: + assert len(variants) > 0 + + # TODO: this error message is not deterministic + missing = list(set(variants[0]) - given_params) + if len(missing) > 1: + msg = f"Missing required arguments: {human_join([quote(arg) for arg in missing])}" + else: + msg = f"Missing required argument: {quote(missing[0])}" + raise TypeError(msg) + return func(*args, **kwargs) + + return wrapper # type: ignore + + return inner + + +_K = TypeVar("_K") +_V = TypeVar("_V") + + +@overload +def strip_not_given(obj: None) -> None: ... + + +@overload +def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: ... + + +@overload +def strip_not_given(obj: object) -> object: ... + + +def strip_not_given(obj: object | None) -> object: + """Remove all top-level keys where their values are instances of `NotGiven`""" + if obj is None: + return None + + if not is_mapping(obj): + return obj + + return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)} + + +def coerce_integer(val: str) -> int: + return int(val, base=10) + + +def coerce_float(val: str) -> float: + return float(val) + + +def coerce_boolean(val: str) -> bool: + return val == "true" or val == "1" or val == "on" + + +def maybe_coerce_integer(val: str | None) -> int | None: + if val is None: + return None + return coerce_integer(val) + + +def maybe_coerce_float(val: str | None) -> float | None: + if val is None: + return None + return coerce_float(val) + + +def maybe_coerce_boolean(val: str | None) -> bool | None: + if val is None: + return None + return coerce_boolean(val) + + +def removeprefix(string: str, prefix: str) -> str: + """Remove a prefix from a string. + + Backport of `str.removeprefix` for Python < 3.9 + """ + if string.startswith(prefix): + return string[len(prefix) :] + return string + + +def removesuffix(string: str, suffix: str) -> str: + """Remove a suffix from a string. + + Backport of `str.removesuffix` for Python < 3.9 + """ + if string.endswith(suffix): + return string[: -len(suffix)] + return string + + +def file_from_path(path: str) -> FileTypes: + contents = Path(path).read_bytes() + file_name = os.path.basename(path) + return (file_name, contents) + + +def get_required_header(headers: HeadersLike, header: str) -> str: + lower_header = header.lower() + if is_mapping_t(headers): + # mypy doesn't understand the type narrowing here + for k, v in headers.items(): # type: ignore + if k.lower() == lower_header and isinstance(v, str): + return v + + # to deal with the case where the header looks like Stainless-Event-Id + intercaps_header = re.sub(r"([^\w])(\w)", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize()) + + for normalized_header in [header, lower_header, header.upper(), intercaps_header]: + value = headers.get(normalized_header) + if value: + return value + + raise ValueError(f"Could not find {header} header") + + +def get_async_library() -> str: + try: + return sniffio.current_async_library() + except Exception: + return "false" + + +def lru_cache(*, maxsize: int | None = 128) -> Callable[[CallableT], CallableT]: + """A version of functools.lru_cache that retains the type signature + for the wrapped function arguments. + """ + wrapper = functools.lru_cache( # noqa: TID251 + maxsize=maxsize, + ) + return cast(Any, wrapper) # type: ignore[no-any-return] + + +def json_safe(data: object) -> object: + """Translates a mapping / sequence recursively in the same fashion + as `pydantic` v2's `model_dump(mode="json")`. + """ + if is_mapping(data): + return {json_safe(key): json_safe(value) for key, value in data.items()} + + if is_iterable(data) and not isinstance(data, (str, bytes, bytearray)): + return [json_safe(item) for item in data] + + if isinstance(data, (datetime, date)): + return data.isoformat() + + return data diff --git a/src/hanzoai/_version.py b/src/hanzoai/_version.py new file mode 100644 index 000000000..add0fc286 --- /dev/null +++ b/src/hanzoai/_version.py @@ -0,0 +1,4 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +__title__ = "hanzoai" +__version__ = "2.1.0" # x-release-please-version diff --git a/src/hanzoai/lib/.keep b/src/hanzoai/lib/.keep new file mode 100644 index 000000000..5e2c99fdb --- /dev/null +++ b/src/hanzoai/lib/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store custom files to expand the SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/pkg/hanzo-iam/hanzo_iam/py.typed b/src/hanzoai/py.typed similarity index 100% rename from pkg/hanzo-iam/hanzo_iam/py.typed rename to src/hanzoai/py.typed diff --git a/src/hanzoai/resources/__init__.py b/src/hanzoai/resources/__init__.py new file mode 100644 index 000000000..2f6ef7de3 --- /dev/null +++ b/src/hanzoai/resources/__init__.py @@ -0,0 +1,677 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .add import ( + AddResource, + AsyncAddResource, + AddResourceWithRawResponse, + AsyncAddResourceWithRawResponse, + AddResourceWithStreamingResponse, + AsyncAddResourceWithStreamingResponse, +) +from .key import ( + KeyResource, + AsyncKeyResource, + KeyResourceWithRawResponse, + AsyncKeyResourceWithRawResponse, + KeyResourceWithStreamingResponse, + AsyncKeyResourceWithStreamingResponse, +) +from .chat import ( + ChatResource, + AsyncChatResource, + ChatResourceWithRawResponse, + AsyncChatResourceWithRawResponse, + ChatResourceWithStreamingResponse, + AsyncChatResourceWithStreamingResponse, +) +from .team import ( + TeamResource, + AsyncTeamResource, + TeamResourceWithRawResponse, + AsyncTeamResourceWithRawResponse, + TeamResourceWithStreamingResponse, + AsyncTeamResourceWithStreamingResponse, +) +from .test import ( + TestResource, + AsyncTestResource, + TestResourceWithRawResponse, + AsyncTestResourceWithRawResponse, + TestResourceWithStreamingResponse, + AsyncTestResourceWithStreamingResponse, +) +from .user import ( + UserResource, + AsyncUserResource, + UserResourceWithRawResponse, + AsyncUserResourceWithRawResponse, + UserResourceWithStreamingResponse, + AsyncUserResourceWithStreamingResponse, +) +from .audio import ( + AudioResource, + AsyncAudioResource, + AudioResourceWithRawResponse, + AsyncAudioResourceWithRawResponse, + AudioResourceWithStreamingResponse, + AsyncAudioResourceWithStreamingResponse, +) +from .azure import ( + AzureResource, + AsyncAzureResource, + AzureResourceWithRawResponse, + AsyncAzureResourceWithRawResponse, + AzureResourceWithStreamingResponse, + AsyncAzureResourceWithStreamingResponse, +) +from .cache import ( + CacheResource, + AsyncCacheResource, + CacheResourceWithRawResponse, + AsyncCacheResourceWithRawResponse, + CacheResourceWithStreamingResponse, + AsyncCacheResourceWithStreamingResponse, +) +from .files import ( + FilesResource, + AsyncFilesResource, + FilesResourceWithRawResponse, + AsyncFilesResourceWithRawResponse, + FilesResourceWithStreamingResponse, + AsyncFilesResourceWithStreamingResponse, +) +from .model import ( + ModelResource, + AsyncModelResource, + ModelResourceWithRawResponse, + AsyncModelResourceWithRawResponse, + ModelResourceWithStreamingResponse, + AsyncModelResourceWithStreamingResponse, +) +from .spend import ( + SpendResource, + AsyncSpendResource, + SpendResourceWithRawResponse, + AsyncSpendResourceWithRawResponse, + SpendResourceWithStreamingResponse, + AsyncSpendResourceWithStreamingResponse, +) +from .utils import ( + UtilsResource, + AsyncUtilsResource, + UtilsResourceWithRawResponse, + AsyncUtilsResourceWithRawResponse, + UtilsResourceWithStreamingResponse, + AsyncUtilsResourceWithStreamingResponse, +) +from .active import ( + ActiveResource, + AsyncActiveResource, + ActiveResourceWithRawResponse, + AsyncActiveResourceWithRawResponse, + ActiveResourceWithStreamingResponse, + AsyncActiveResourceWithStreamingResponse, +) +from .budget import ( + BudgetResource, + AsyncBudgetResource, + BudgetResourceWithRawResponse, + AsyncBudgetResourceWithRawResponse, + BudgetResourceWithStreamingResponse, + AsyncBudgetResourceWithStreamingResponse, +) +from .cohere import ( + CohereResource, + AsyncCohereResource, + CohereResourceWithRawResponse, + AsyncCohereResourceWithRawResponse, + CohereResourceWithStreamingResponse, + AsyncCohereResourceWithStreamingResponse, +) +from .config import ( + ConfigResource, + AsyncConfigResource, + ConfigResourceWithRawResponse, + AsyncConfigResourceWithRawResponse, + ConfigResourceWithStreamingResponse, + AsyncConfigResourceWithStreamingResponse, +) +from .delete import ( + DeleteResource, + AsyncDeleteResource, + DeleteResourceWithRawResponse, + AsyncDeleteResourceWithRawResponse, + DeleteResourceWithStreamingResponse, + AsyncDeleteResourceWithStreamingResponse, +) +from .gemini import ( + GeminiResource, + AsyncGeminiResource, + GeminiResourceWithRawResponse, + AsyncGeminiResourceWithRawResponse, + GeminiResourceWithStreamingResponse, + AsyncGeminiResourceWithStreamingResponse, +) +from .health import ( + HealthResource, + AsyncHealthResource, + HealthResourceWithRawResponse, + AsyncHealthResourceWithRawResponse, + HealthResourceWithStreamingResponse, + AsyncHealthResourceWithStreamingResponse, +) +from .images import ( + ImagesResource, + AsyncImagesResource, + ImagesResourceWithRawResponse, + AsyncImagesResourceWithRawResponse, + ImagesResourceWithStreamingResponse, + AsyncImagesResourceWithStreamingResponse, +) +from .models import ( + ModelsResource, + AsyncModelsResource, + ModelsResourceWithRawResponse, + AsyncModelsResourceWithRawResponse, + ModelsResourceWithStreamingResponse, + AsyncModelsResourceWithStreamingResponse, +) +from .openai import ( + OpenAIResource, + AsyncOpenAIResource, + OpenAIResourceWithRawResponse, + AsyncOpenAIResourceWithRawResponse, + OpenAIResourceWithStreamingResponse, + AsyncOpenAIResourceWithStreamingResponse, +) +from .rerank import ( + RerankResource, + AsyncRerankResource, + RerankResourceWithRawResponse, + AsyncRerankResourceWithRawResponse, + RerankResourceWithStreamingResponse, + AsyncRerankResourceWithStreamingResponse, +) +from .routes import ( + RoutesResource, + AsyncRoutesResource, + RoutesResourceWithRawResponse, + AsyncRoutesResourceWithRawResponse, + RoutesResourceWithStreamingResponse, + AsyncRoutesResourceWithStreamingResponse, +) +from .batches import ( + BatchesResource, + AsyncBatchesResource, + BatchesResourceWithRawResponse, + AsyncBatchesResourceWithRawResponse, + BatchesResourceWithStreamingResponse, + AsyncBatchesResourceWithStreamingResponse, +) +from .bedrock import ( + BedrockResource, + AsyncBedrockResource, + BedrockResourceWithRawResponse, + AsyncBedrockResourceWithRawResponse, + BedrockResourceWithStreamingResponse, + AsyncBedrockResourceWithStreamingResponse, +) +from .engines import ( + EnginesResource, + AsyncEnginesResource, + EnginesResourceWithRawResponse, + AsyncEnginesResourceWithRawResponse, + EnginesResourceWithStreamingResponse, + AsyncEnginesResourceWithStreamingResponse, +) +from .global_ import ( + GlobalResource, + AsyncGlobalResource, + GlobalResourceWithRawResponse, + AsyncGlobalResourceWithRawResponse, + GlobalResourceWithStreamingResponse, + AsyncGlobalResourceWithStreamingResponse, +) +from .threads import ( + ThreadsResource, + AsyncThreadsResource, + ThreadsResourceWithRawResponse, + AsyncThreadsResourceWithRawResponse, + ThreadsResourceWithStreamingResponse, + AsyncThreadsResourceWithStreamingResponse, +) +from .customer import ( + CustomerResource, + AsyncCustomerResource, + CustomerResourceWithRawResponse, + AsyncCustomerResourceWithRawResponse, + CustomerResourceWithStreamingResponse, + AsyncCustomerResourceWithStreamingResponse, +) +from .langfuse import ( + LangfuseResource, + AsyncLangfuseResource, + LangfuseResourceWithRawResponse, + AsyncLangfuseResourceWithRawResponse, + LangfuseResourceWithStreamingResponse, + AsyncLangfuseResourceWithStreamingResponse, +) +from .provider import ( + ProviderResource, + AsyncProviderResource, + ProviderResourceWithRawResponse, + AsyncProviderResourceWithRawResponse, + ProviderResourceWithStreamingResponse, + AsyncProviderResourceWithStreamingResponse, +) +from .settings import ( + SettingsResource, + AsyncSettingsResource, + SettingsResourceWithRawResponse, + AsyncSettingsResourceWithRawResponse, + SettingsResourceWithStreamingResponse, + AsyncSettingsResourceWithStreamingResponse, +) +from .anthropic import ( + AnthropicResource, + AsyncAnthropicResource, + AnthropicResourceWithRawResponse, + AsyncAnthropicResourceWithRawResponse, + AnthropicResourceWithStreamingResponse, + AsyncAnthropicResourceWithStreamingResponse, +) +from .responses import ( + ResponsesResource, + AsyncResponsesResource, + ResponsesResourceWithRawResponse, + AsyncResponsesResourceWithRawResponse, + ResponsesResourceWithStreamingResponse, + AsyncResponsesResourceWithStreamingResponse, +) +from .vertex_ai import ( + VertexAIResource, + AsyncVertexAIResource, + VertexAIResourceWithRawResponse, + AsyncVertexAIResourceWithRawResponse, + VertexAIResourceWithStreamingResponse, + AsyncVertexAIResourceWithStreamingResponse, +) +from .assemblyai import ( + AssemblyaiResource, + AsyncAssemblyaiResource, + AssemblyaiResourceWithRawResponse, + AsyncAssemblyaiResourceWithRawResponse, + AssemblyaiResourceWithStreamingResponse, + AsyncAssemblyaiResourceWithStreamingResponse, +) +from .assistants import ( + AssistantsResource, + AsyncAssistantsResource, + AssistantsResourceWithRawResponse, + AsyncAssistantsResourceWithRawResponse, + AssistantsResourceWithStreamingResponse, + AsyncAssistantsResourceWithStreamingResponse, +) +from .embeddings import ( + EmbeddingsResource, + AsyncEmbeddingsResource, + EmbeddingsResourceWithRawResponse, + AsyncEmbeddingsResourceWithRawResponse, + EmbeddingsResourceWithStreamingResponse, + AsyncEmbeddingsResourceWithStreamingResponse, +) +from .guardrails import ( + GuardrailsResource, + AsyncGuardrailsResource, + GuardrailsResourceWithRawResponse, + AsyncGuardrailsResourceWithRawResponse, + GuardrailsResourceWithStreamingResponse, + AsyncGuardrailsResourceWithStreamingResponse, +) +from .completions import ( + CompletionsResource, + AsyncCompletionsResource, + CompletionsResourceWithRawResponse, + AsyncCompletionsResourceWithRawResponse, + CompletionsResourceWithStreamingResponse, + AsyncCompletionsResourceWithStreamingResponse, +) +from .credentials import ( + CredentialsResource, + AsyncCredentialsResource, + CredentialsResourceWithRawResponse, + AsyncCredentialsResourceWithRawResponse, + CredentialsResourceWithStreamingResponse, + AsyncCredentialsResourceWithStreamingResponse, +) +from .fine_tuning import ( + FineTuningResource, + AsyncFineTuningResource, + FineTuningResourceWithRawResponse, + AsyncFineTuningResourceWithRawResponse, + FineTuningResourceWithStreamingResponse, + AsyncFineTuningResourceWithStreamingResponse, +) +from .model_group import ( + ModelGroupResource, + AsyncModelGroupResource, + ModelGroupResourceWithRawResponse, + AsyncModelGroupResourceWithRawResponse, + ModelGroupResourceWithStreamingResponse, + AsyncModelGroupResourceWithStreamingResponse, +) +from .moderations import ( + ModerationsResource, + AsyncModerationsResource, + ModerationsResourceWithRawResponse, + AsyncModerationsResourceWithRawResponse, + ModerationsResourceWithStreamingResponse, + AsyncModerationsResourceWithStreamingResponse, +) +from .organization import ( + OrganizationResource, + AsyncOrganizationResource, + OrganizationResourceWithRawResponse, + AsyncOrganizationResourceWithRawResponse, + OrganizationResourceWithStreamingResponse, + AsyncOrganizationResourceWithStreamingResponse, +) +from .eu_assemblyai import ( + EuAssemblyaiResource, + AsyncEuAssemblyaiResource, + EuAssemblyaiResourceWithRawResponse, + AsyncEuAssemblyaiResourceWithRawResponse, + EuAssemblyaiResourceWithStreamingResponse, + AsyncEuAssemblyaiResourceWithStreamingResponse, +) + +__all__ = [ + "ModelsResource", + "AsyncModelsResource", + "ModelsResourceWithRawResponse", + "AsyncModelsResourceWithRawResponse", + "ModelsResourceWithStreamingResponse", + "AsyncModelsResourceWithStreamingResponse", + "OpenAIResource", + "AsyncOpenAIResource", + "OpenAIResourceWithRawResponse", + "AsyncOpenAIResourceWithRawResponse", + "OpenAIResourceWithStreamingResponse", + "AsyncOpenAIResourceWithStreamingResponse", + "EnginesResource", + "AsyncEnginesResource", + "EnginesResourceWithRawResponse", + "AsyncEnginesResourceWithRawResponse", + "EnginesResourceWithStreamingResponse", + "AsyncEnginesResourceWithStreamingResponse", + "ChatResource", + "AsyncChatResource", + "ChatResourceWithRawResponse", + "AsyncChatResourceWithRawResponse", + "ChatResourceWithStreamingResponse", + "AsyncChatResourceWithStreamingResponse", + "CompletionsResource", + "AsyncCompletionsResource", + "CompletionsResourceWithRawResponse", + "AsyncCompletionsResourceWithRawResponse", + "CompletionsResourceWithStreamingResponse", + "AsyncCompletionsResourceWithStreamingResponse", + "EmbeddingsResource", + "AsyncEmbeddingsResource", + "EmbeddingsResourceWithRawResponse", + "AsyncEmbeddingsResourceWithRawResponse", + "EmbeddingsResourceWithStreamingResponse", + "AsyncEmbeddingsResourceWithStreamingResponse", + "ImagesResource", + "AsyncImagesResource", + "ImagesResourceWithRawResponse", + "AsyncImagesResourceWithRawResponse", + "ImagesResourceWithStreamingResponse", + "AsyncImagesResourceWithStreamingResponse", + "AudioResource", + "AsyncAudioResource", + "AudioResourceWithRawResponse", + "AsyncAudioResourceWithRawResponse", + "AudioResourceWithStreamingResponse", + "AsyncAudioResourceWithStreamingResponse", + "AssistantsResource", + "AsyncAssistantsResource", + "AssistantsResourceWithRawResponse", + "AsyncAssistantsResourceWithRawResponse", + "AssistantsResourceWithStreamingResponse", + "AsyncAssistantsResourceWithStreamingResponse", + "ThreadsResource", + "AsyncThreadsResource", + "ThreadsResourceWithRawResponse", + "AsyncThreadsResourceWithRawResponse", + "ThreadsResourceWithStreamingResponse", + "AsyncThreadsResourceWithStreamingResponse", + "ModerationsResource", + "AsyncModerationsResource", + "ModerationsResourceWithRawResponse", + "AsyncModerationsResourceWithRawResponse", + "ModerationsResourceWithStreamingResponse", + "AsyncModerationsResourceWithStreamingResponse", + "UtilsResource", + "AsyncUtilsResource", + "UtilsResourceWithRawResponse", + "AsyncUtilsResourceWithRawResponse", + "UtilsResourceWithStreamingResponse", + "AsyncUtilsResourceWithStreamingResponse", + "ModelResource", + "AsyncModelResource", + "ModelResourceWithRawResponse", + "AsyncModelResourceWithRawResponse", + "ModelResourceWithStreamingResponse", + "AsyncModelResourceWithStreamingResponse", + "ModelGroupResource", + "AsyncModelGroupResource", + "ModelGroupResourceWithRawResponse", + "AsyncModelGroupResourceWithRawResponse", + "ModelGroupResourceWithStreamingResponse", + "AsyncModelGroupResourceWithStreamingResponse", + "RoutesResource", + "AsyncRoutesResource", + "RoutesResourceWithRawResponse", + "AsyncRoutesResourceWithRawResponse", + "RoutesResourceWithStreamingResponse", + "AsyncRoutesResourceWithStreamingResponse", + "ResponsesResource", + "AsyncResponsesResource", + "ResponsesResourceWithRawResponse", + "AsyncResponsesResourceWithRawResponse", + "ResponsesResourceWithStreamingResponse", + "AsyncResponsesResourceWithStreamingResponse", + "BatchesResource", + "AsyncBatchesResource", + "BatchesResourceWithRawResponse", + "AsyncBatchesResourceWithRawResponse", + "BatchesResourceWithStreamingResponse", + "AsyncBatchesResourceWithStreamingResponse", + "RerankResource", + "AsyncRerankResource", + "RerankResourceWithRawResponse", + "AsyncRerankResourceWithRawResponse", + "RerankResourceWithStreamingResponse", + "AsyncRerankResourceWithStreamingResponse", + "FineTuningResource", + "AsyncFineTuningResource", + "FineTuningResourceWithRawResponse", + "AsyncFineTuningResourceWithRawResponse", + "FineTuningResourceWithStreamingResponse", + "AsyncFineTuningResourceWithStreamingResponse", + "CredentialsResource", + "AsyncCredentialsResource", + "CredentialsResourceWithRawResponse", + "AsyncCredentialsResourceWithRawResponse", + "CredentialsResourceWithStreamingResponse", + "AsyncCredentialsResourceWithStreamingResponse", + "VertexAIResource", + "AsyncVertexAIResource", + "VertexAIResourceWithRawResponse", + "AsyncVertexAIResourceWithRawResponse", + "VertexAIResourceWithStreamingResponse", + "AsyncVertexAIResourceWithStreamingResponse", + "GeminiResource", + "AsyncGeminiResource", + "GeminiResourceWithRawResponse", + "AsyncGeminiResourceWithRawResponse", + "GeminiResourceWithStreamingResponse", + "AsyncGeminiResourceWithStreamingResponse", + "CohereResource", + "AsyncCohereResource", + "CohereResourceWithRawResponse", + "AsyncCohereResourceWithRawResponse", + "CohereResourceWithStreamingResponse", + "AsyncCohereResourceWithStreamingResponse", + "AnthropicResource", + "AsyncAnthropicResource", + "AnthropicResourceWithRawResponse", + "AsyncAnthropicResourceWithRawResponse", + "AnthropicResourceWithStreamingResponse", + "AsyncAnthropicResourceWithStreamingResponse", + "BedrockResource", + "AsyncBedrockResource", + "BedrockResourceWithRawResponse", + "AsyncBedrockResourceWithRawResponse", + "BedrockResourceWithStreamingResponse", + "AsyncBedrockResourceWithStreamingResponse", + "EuAssemblyaiResource", + "AsyncEuAssemblyaiResource", + "EuAssemblyaiResourceWithRawResponse", + "AsyncEuAssemblyaiResourceWithRawResponse", + "EuAssemblyaiResourceWithStreamingResponse", + "AsyncEuAssemblyaiResourceWithStreamingResponse", + "AssemblyaiResource", + "AsyncAssemblyaiResource", + "AssemblyaiResourceWithRawResponse", + "AsyncAssemblyaiResourceWithRawResponse", + "AssemblyaiResourceWithStreamingResponse", + "AsyncAssemblyaiResourceWithStreamingResponse", + "AzureResource", + "AsyncAzureResource", + "AzureResourceWithRawResponse", + "AsyncAzureResourceWithRawResponse", + "AzureResourceWithStreamingResponse", + "AsyncAzureResourceWithStreamingResponse", + "LangfuseResource", + "AsyncLangfuseResource", + "LangfuseResourceWithRawResponse", + "AsyncLangfuseResourceWithRawResponse", + "LangfuseResourceWithStreamingResponse", + "AsyncLangfuseResourceWithStreamingResponse", + "ConfigResource", + "AsyncConfigResource", + "ConfigResourceWithRawResponse", + "AsyncConfigResourceWithRawResponse", + "ConfigResourceWithStreamingResponse", + "AsyncConfigResourceWithStreamingResponse", + "TestResource", + "AsyncTestResource", + "TestResourceWithRawResponse", + "AsyncTestResourceWithRawResponse", + "TestResourceWithStreamingResponse", + "AsyncTestResourceWithStreamingResponse", + "HealthResource", + "AsyncHealthResource", + "HealthResourceWithRawResponse", + "AsyncHealthResourceWithRawResponse", + "HealthResourceWithStreamingResponse", + "AsyncHealthResourceWithStreamingResponse", + "ActiveResource", + "AsyncActiveResource", + "ActiveResourceWithRawResponse", + "AsyncActiveResourceWithRawResponse", + "ActiveResourceWithStreamingResponse", + "AsyncActiveResourceWithStreamingResponse", + "SettingsResource", + "AsyncSettingsResource", + "SettingsResourceWithRawResponse", + "AsyncSettingsResourceWithRawResponse", + "SettingsResourceWithStreamingResponse", + "AsyncSettingsResourceWithStreamingResponse", + "KeyResource", + "AsyncKeyResource", + "KeyResourceWithRawResponse", + "AsyncKeyResourceWithRawResponse", + "KeyResourceWithStreamingResponse", + "AsyncKeyResourceWithStreamingResponse", + "UserResource", + "AsyncUserResource", + "UserResourceWithRawResponse", + "AsyncUserResourceWithRawResponse", + "UserResourceWithStreamingResponse", + "AsyncUserResourceWithStreamingResponse", + "TeamResource", + "AsyncTeamResource", + "TeamResourceWithRawResponse", + "AsyncTeamResourceWithRawResponse", + "TeamResourceWithStreamingResponse", + "AsyncTeamResourceWithStreamingResponse", + "OrganizationResource", + "AsyncOrganizationResource", + "OrganizationResourceWithRawResponse", + "AsyncOrganizationResourceWithRawResponse", + "OrganizationResourceWithStreamingResponse", + "AsyncOrganizationResourceWithStreamingResponse", + "CustomerResource", + "AsyncCustomerResource", + "CustomerResourceWithRawResponse", + "AsyncCustomerResourceWithRawResponse", + "CustomerResourceWithStreamingResponse", + "AsyncCustomerResourceWithStreamingResponse", + "SpendResource", + "AsyncSpendResource", + "SpendResourceWithRawResponse", + "AsyncSpendResourceWithRawResponse", + "SpendResourceWithStreamingResponse", + "AsyncSpendResourceWithStreamingResponse", + "GlobalResource", + "AsyncGlobalResource", + "GlobalResourceWithRawResponse", + "AsyncGlobalResourceWithRawResponse", + "GlobalResourceWithStreamingResponse", + "AsyncGlobalResourceWithStreamingResponse", + "ProviderResource", + "AsyncProviderResource", + "ProviderResourceWithRawResponse", + "AsyncProviderResourceWithRawResponse", + "ProviderResourceWithStreamingResponse", + "AsyncProviderResourceWithStreamingResponse", + "CacheResource", + "AsyncCacheResource", + "CacheResourceWithRawResponse", + "AsyncCacheResourceWithRawResponse", + "CacheResourceWithStreamingResponse", + "AsyncCacheResourceWithStreamingResponse", + "GuardrailsResource", + "AsyncGuardrailsResource", + "GuardrailsResourceWithRawResponse", + "AsyncGuardrailsResourceWithRawResponse", + "GuardrailsResourceWithStreamingResponse", + "AsyncGuardrailsResourceWithStreamingResponse", + "AddResource", + "AsyncAddResource", + "AddResourceWithRawResponse", + "AsyncAddResourceWithRawResponse", + "AddResourceWithStreamingResponse", + "AsyncAddResourceWithStreamingResponse", + "DeleteResource", + "AsyncDeleteResource", + "DeleteResourceWithRawResponse", + "AsyncDeleteResourceWithRawResponse", + "DeleteResourceWithStreamingResponse", + "AsyncDeleteResourceWithStreamingResponse", + "FilesResource", + "AsyncFilesResource", + "FilesResourceWithRawResponse", + "AsyncFilesResourceWithRawResponse", + "FilesResourceWithStreamingResponse", + "AsyncFilesResourceWithStreamingResponse", + "BudgetResource", + "AsyncBudgetResource", + "BudgetResourceWithRawResponse", + "AsyncBudgetResourceWithRawResponse", + "BudgetResourceWithStreamingResponse", + "AsyncBudgetResourceWithStreamingResponse", +] diff --git a/src/hanzoai/resources/active.py b/src/hanzoai/resources/active.py new file mode 100644 index 000000000..224400e50 --- /dev/null +++ b/src/hanzoai/resources/active.py @@ -0,0 +1,182 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .._types import Body, Query, Headers, NotGiven, not_given +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options + +__all__ = ["ActiveResource", "AsyncActiveResource"] + + +class ActiveResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ActiveResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return ActiveResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ActiveResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return ActiveResourceWithStreamingResponse(self) + + def list_callbacks( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Returns a list of llm level settings + + This is useful for debugging and ensuring the proxy server is configured + correctly. + + Response schema: + + ``` + { + "alerting": _alerting, + "llm.callbacks": llm_callbacks, + "llm.input_callback": llm_input_callbacks, + "llm.failure_callback": llm_failure_callbacks, + "llm.success_callback": llm_success_callbacks, + "llm._async_success_callback": llm_async_success_callbacks, + "llm._async_failure_callback": llm_async_failure_callbacks, + "llm._async_input_callback": llm_async_input_callbacks, + "all_llm_callbacks": all_llm_callbacks, + "num_callbacks": len(all_llm_callbacks), + "num_alerting": _num_alerting, + "llm.request_timeout": llm.request_timeout, + } + ``` + """ + return self._get( + "/active/callbacks", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncActiveResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncActiveResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncActiveResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncActiveResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncActiveResourceWithStreamingResponse(self) + + async def list_callbacks( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Returns a list of llm level settings + + This is useful for debugging and ensuring the proxy server is configured + correctly. + + Response schema: + + ``` + { + "alerting": _alerting, + "llm.callbacks": llm_callbacks, + "llm.input_callback": llm_input_callbacks, + "llm.failure_callback": llm_failure_callbacks, + "llm.success_callback": llm_success_callbacks, + "llm._async_success_callback": llm_async_success_callbacks, + "llm._async_failure_callback": llm_async_failure_callbacks, + "llm._async_input_callback": llm_async_input_callbacks, + "all_llm_callbacks": all_llm_callbacks, + "num_callbacks": len(all_llm_callbacks), + "num_alerting": _num_alerting, + "llm.request_timeout": llm.request_timeout, + } + ``` + """ + return await self._get( + "/active/callbacks", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class ActiveResourceWithRawResponse: + def __init__(self, active: ActiveResource) -> None: + self._active = active + + self.list_callbacks = to_raw_response_wrapper( + active.list_callbacks, + ) + + +class AsyncActiveResourceWithRawResponse: + def __init__(self, active: AsyncActiveResource) -> None: + self._active = active + + self.list_callbacks = async_to_raw_response_wrapper( + active.list_callbacks, + ) + + +class ActiveResourceWithStreamingResponse: + def __init__(self, active: ActiveResource) -> None: + self._active = active + + self.list_callbacks = to_streamed_response_wrapper( + active.list_callbacks, + ) + + +class AsyncActiveResourceWithStreamingResponse: + def __init__(self, active: AsyncActiveResource) -> None: + self._active = active + + self.list_callbacks = async_to_streamed_response_wrapper( + active.list_callbacks, + ) diff --git a/pkg/hanzoai/resources/add.py b/src/hanzoai/resources/add.py similarity index 84% rename from pkg/hanzoai/resources/add.py rename to src/hanzoai/resources/add.py index 73d9083fc..f20781bf1 100644 --- a/pkg/hanzoai/resources/add.py +++ b/src/hanzoai/resources/add.py @@ -1,15 +1,12 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx from ..types import add_add_allowed_ip_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - maybe_transform, - async_maybe_transform, -) +from .._types import Body, Query, Headers, NotGiven, not_given +from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -52,7 +49,7 @@ def add_allowed_ip( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Add Allowed Ip @@ -68,14 +65,9 @@ def add_allowed_ip( """ return self._post( "/add/allowed_ip", - body=maybe_transform( - {"ip": ip}, add_add_allowed_ip_params.AddAddAllowedIPParams - ), + body=maybe_transform({"ip": ip}, add_add_allowed_ip_params.AddAddAllowedIPParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -110,7 +102,7 @@ async def add_allowed_ip( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Add Allowed Ip @@ -126,14 +118,9 @@ async def add_allowed_ip( """ return await self._post( "/add/allowed_ip", - body=await async_maybe_transform( - {"ip": ip}, add_add_allowed_ip_params.AddAddAllowedIPParams - ), + body=await async_maybe_transform({"ip": ip}, add_add_allowed_ip_params.AddAddAllowedIPParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/pkg/hanzoai/resources/anthropic.py b/src/hanzoai/resources/anthropic.py similarity index 80% rename from pkg/hanzoai/resources/anthropic.py rename to src/hanzoai/resources/anthropic.py index f0140262d..c373d061c 100644 --- a/pkg/hanzoai/resources/anthropic.py +++ b/src/hanzoai/resources/anthropic.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -47,7 +47,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/anthropic_completion) @@ -62,16 +62,11 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._post( f"/anthropic/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -85,7 +80,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/anthropic_completion) @@ -100,16 +95,11 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._get( f"/anthropic/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -123,7 +113,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/anthropic_completion) @@ -138,16 +128,11 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._put( f"/anthropic/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -161,7 +146,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/anthropic_completion) @@ -176,16 +161,11 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._delete( f"/anthropic/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -199,7 +179,7 @@ def modify( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/anthropic_completion) @@ -214,16 +194,11 @@ def modify( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._patch( f"/anthropic/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -258,7 +233,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/anthropic_completion) @@ -273,16 +248,11 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._post( f"/anthropic/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -296,7 +266,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/anthropic_completion) @@ -311,16 +281,11 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._get( f"/anthropic/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -334,7 +299,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/anthropic_completion) @@ -349,16 +314,11 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._put( f"/anthropic/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -372,7 +332,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/anthropic_completion) @@ -387,16 +347,11 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._delete( f"/anthropic/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -410,7 +365,7 @@ async def modify( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/anthropic_completion) @@ -425,16 +380,11 @@ async def modify( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._patch( f"/anthropic/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/pkg/hanzoai/resources/assemblyai.py b/src/hanzoai/resources/assemblyai.py similarity index 79% rename from pkg/hanzoai/resources/assemblyai.py rename to src/hanzoai/resources/assemblyai.py index dd16eebc1..84a4be3af 100644 --- a/pkg/hanzoai/resources/assemblyai.py +++ b/src/hanzoai/resources/assemblyai.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -47,7 +47,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -62,16 +62,11 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._post( f"/assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -85,7 +80,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -100,16 +95,11 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._get( f"/assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -123,7 +113,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -138,16 +128,11 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._put( f"/assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -161,7 +146,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -176,16 +161,11 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._delete( f"/assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -199,7 +179,7 @@ def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -214,16 +194,11 @@ def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._patch( f"/assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -258,7 +233,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -273,16 +248,11 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._post( f"/assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -296,7 +266,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -311,16 +281,11 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._get( f"/assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -334,7 +299,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -349,16 +314,11 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._put( f"/assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -372,7 +332,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -387,16 +347,11 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._delete( f"/assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -410,7 +365,7 @@ async def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -425,16 +380,11 @@ async def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._patch( f"/assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/pkg/hanzoai/resources/assistants.py b/src/hanzoai/resources/assistants.py similarity index 84% rename from pkg/hanzoai/resources/assistants.py rename to src/hanzoai/resources/assistants.py index c16ab9f3b..e6f7a9823 100644 --- a/pkg/hanzoai/resources/assistants.py +++ b/src/hanzoai/resources/assistants.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -46,7 +46,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Create assistant @@ -57,10 +57,7 @@ def create( return self._post( "/v1/assistants", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -73,7 +70,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Returns a list of assistants. @@ -84,10 +81,7 @@ def list( return self._get( "/v1/assistants", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -101,7 +95,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Delete assistant @@ -119,16 +113,11 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not assistant_id: - raise ValueError( - f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") return self._delete( f"/v1/assistants/{assistant_id}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -162,7 +151,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Create assistant @@ -173,10 +162,7 @@ async def create( return await self._post( "/v1/assistants", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -189,7 +175,7 @@ async def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Returns a list of assistants. @@ -200,10 +186,7 @@ async def list( return await self._get( "/v1/assistants", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -217,7 +200,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Delete assistant @@ -235,16 +218,11 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not assistant_id: - raise ValueError( - f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") return await self._delete( f"/v1/assistants/{assistant_id}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/audio/__init__.py b/src/hanzoai/resources/audio/__init__.py new file mode 100644 index 000000000..248512106 --- /dev/null +++ b/src/hanzoai/resources/audio/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .audio import ( + AudioResource, + AsyncAudioResource, + AudioResourceWithRawResponse, + AsyncAudioResourceWithRawResponse, + AudioResourceWithStreamingResponse, + AsyncAudioResourceWithStreamingResponse, +) +from .speech import ( + SpeechResource, + AsyncSpeechResource, + SpeechResourceWithRawResponse, + AsyncSpeechResourceWithRawResponse, + SpeechResourceWithStreamingResponse, + AsyncSpeechResourceWithStreamingResponse, +) +from .transcriptions import ( + TranscriptionsResource, + AsyncTranscriptionsResource, + TranscriptionsResourceWithRawResponse, + AsyncTranscriptionsResourceWithRawResponse, + TranscriptionsResourceWithStreamingResponse, + AsyncTranscriptionsResourceWithStreamingResponse, +) + +__all__ = [ + "SpeechResource", + "AsyncSpeechResource", + "SpeechResourceWithRawResponse", + "AsyncSpeechResourceWithRawResponse", + "SpeechResourceWithStreamingResponse", + "AsyncSpeechResourceWithStreamingResponse", + "TranscriptionsResource", + "AsyncTranscriptionsResource", + "TranscriptionsResourceWithRawResponse", + "AsyncTranscriptionsResourceWithRawResponse", + "TranscriptionsResourceWithStreamingResponse", + "AsyncTranscriptionsResourceWithStreamingResponse", + "AudioResource", + "AsyncAudioResource", + "AudioResourceWithRawResponse", + "AsyncAudioResourceWithRawResponse", + "AudioResourceWithStreamingResponse", + "AsyncAudioResourceWithStreamingResponse", +] diff --git a/pkg/hanzoai/resources/audio/audio.py b/src/hanzoai/resources/audio/audio.py similarity index 97% rename from pkg/hanzoai/resources/audio/audio.py rename to src/hanzoai/resources/audio/audio.py index 5563d3a04..a04738f0d 100644 --- a/pkg/hanzoai/resources/audio/audio.py +++ b/src/hanzoai/resources/audio/audio.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -131,6 +131,4 @@ def speech(self) -> AsyncSpeechResourceWithStreamingResponse: @cached_property def transcriptions(self) -> AsyncTranscriptionsResourceWithStreamingResponse: - return AsyncTranscriptionsResourceWithStreamingResponse( - self._audio.transcriptions - ) + return AsyncTranscriptionsResourceWithStreamingResponse(self._audio.transcriptions) diff --git a/pkg/hanzoai/resources/audio/speech.py b/src/hanzoai/resources/audio/speech.py similarity index 89% rename from pkg/hanzoai/resources/audio/speech.py rename to src/hanzoai/resources/audio/speech.py index 01f311e97..a9f822309 100644 --- a/pkg/hanzoai/resources/audio/speech.py +++ b/src/hanzoai/resources/audio/speech.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Query, Headers, NotGiven, not_given from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -46,7 +46,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Same params as: @@ -56,10 +56,7 @@ def create( return self._post( "/v1/audio/speech", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -93,7 +90,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Same params as: @@ -103,10 +100,7 @@ async def create( return await self._post( "/v1/audio/speech", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/pkg/hanzoai/resources/audio/transcriptions.py b/src/hanzoai/resources/audio/transcriptions.py similarity index 85% rename from pkg/hanzoai/resources/audio/transcriptions.py rename to src/hanzoai/resources/audio/transcriptions.py index 368505397..5d88daa06 100644 --- a/pkg/hanzoai/resources/audio/transcriptions.py +++ b/src/hanzoai/resources/audio/transcriptions.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -6,13 +6,8 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from ..._utils import ( - extract_files, - maybe_transform, - deepcopy_minimal, - async_maybe_transform, -) +from ..._types import Body, Query, Headers, NotGiven, FileTypes, not_given +from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -56,7 +51,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Same params as: @@ -80,15 +75,10 @@ def create( extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/v1/audio/transcriptions", - body=maybe_transform( - body, transcription_create_params.TranscriptionCreateParams - ), + body=maybe_transform(body, transcription_create_params.TranscriptionCreateParams), files=files, options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -106,9 +96,7 @@ def with_raw_response(self) -> AsyncTranscriptionsResourceWithRawResponse: return AsyncTranscriptionsResourceWithRawResponse(self) @cached_property - def with_streaming_response( - self, - ) -> AsyncTranscriptionsResourceWithStreamingResponse: + def with_streaming_response(self) -> AsyncTranscriptionsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. @@ -125,7 +113,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Same params as: @@ -149,15 +137,10 @@ async def create( extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/v1/audio/transcriptions", - body=await async_maybe_transform( - body, transcription_create_params.TranscriptionCreateParams - ), + body=await async_maybe_transform(body, transcription_create_params.TranscriptionCreateParams), files=files, options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/pkg/hanzoai/resources/azure.py b/src/hanzoai/resources/azure.py similarity index 80% rename from pkg/hanzoai/resources/azure.py rename to src/hanzoai/resources/azure.py index d1a8a76e2..6cb187f9f 100644 --- a/pkg/hanzoai/resources/azure.py +++ b/src/hanzoai/resources/azure.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -47,7 +47,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Call any azure endpoint using the proxy. @@ -64,16 +64,11 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._post( f"/azure/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -87,7 +82,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Call any azure endpoint using the proxy. @@ -104,16 +99,11 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._put( f"/azure/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -127,7 +117,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Call any azure endpoint using the proxy. @@ -144,16 +134,11 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._delete( f"/azure/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -167,7 +152,7 @@ def call( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Call any azure endpoint using the proxy. @@ -184,16 +169,11 @@ def call( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._get( f"/azure/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -207,7 +187,7 @@ def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Call any azure endpoint using the proxy. @@ -224,16 +204,11 @@ def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._patch( f"/azure/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -268,7 +243,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Call any azure endpoint using the proxy. @@ -285,16 +260,11 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._post( f"/azure/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -308,7 +278,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Call any azure endpoint using the proxy. @@ -325,16 +295,11 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._put( f"/azure/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -348,7 +313,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Call any azure endpoint using the proxy. @@ -365,16 +330,11 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._delete( f"/azure/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -388,7 +348,7 @@ async def call( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Call any azure endpoint using the proxy. @@ -405,16 +365,11 @@ async def call( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._get( f"/azure/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -428,7 +383,7 @@ async def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Call any azure endpoint using the proxy. @@ -445,16 +400,11 @@ async def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._patch( f"/azure/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/batches/__init__.py b/src/hanzoai/resources/batches/__init__.py new file mode 100644 index 000000000..d83a15b35 --- /dev/null +++ b/src/hanzoai/resources/batches/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .cancel import ( + CancelResource, + AsyncCancelResource, + CancelResourceWithRawResponse, + AsyncCancelResourceWithRawResponse, + CancelResourceWithStreamingResponse, + AsyncCancelResourceWithStreamingResponse, +) +from .batches import ( + BatchesResource, + AsyncBatchesResource, + BatchesResourceWithRawResponse, + AsyncBatchesResourceWithRawResponse, + BatchesResourceWithStreamingResponse, + AsyncBatchesResourceWithStreamingResponse, +) + +__all__ = [ + "CancelResource", + "AsyncCancelResource", + "CancelResourceWithRawResponse", + "AsyncCancelResourceWithRawResponse", + "CancelResourceWithStreamingResponse", + "AsyncCancelResourceWithStreamingResponse", + "BatchesResource", + "AsyncBatchesResource", + "BatchesResourceWithRawResponse", + "AsyncBatchesResourceWithRawResponse", + "BatchesResourceWithStreamingResponse", + "AsyncBatchesResourceWithStreamingResponse", +] diff --git a/pkg/hanzoai/resources/batches/batches.py b/src/hanzoai/resources/batches/batches.py similarity index 84% rename from pkg/hanzoai/resources/batches/batches.py rename to src/hanzoai/resources/batches/batches.py index 1cafc2c47..4d221c5b0 100644 --- a/pkg/hanzoai/resources/batches/batches.py +++ b/src/hanzoai/resources/batches/batches.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -14,17 +14,9 @@ CancelResourceWithStreamingResponse, AsyncCancelResourceWithStreamingResponse, ) -from ...types import ( - batch_list_params, - batch_create_params, - batch_retrieve_params, - batch_list_with_provider_params, -) -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) +from ...types import batch_list_params, batch_create_params, batch_retrieve_params, batch_list_with_provider_params +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -65,13 +57,13 @@ def with_streaming_response(self) -> BatchesResourceWithStreamingResponse: def create( self, *, - provider: Optional[str] | NotGiven = NOT_GIVEN, + provider: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Create large batches of API requests for asynchronous processing. @@ -105,9 +97,7 @@ def create( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform( - {"provider": provider}, batch_create_params.BatchCreateParams - ), + query=maybe_transform({"provider": provider}, batch_create_params.BatchCreateParams), ), cast_to=object, ) @@ -116,13 +106,13 @@ def retrieve( self, batch_id: str, *, - provider: Optional[str] | NotGiven = NOT_GIVEN, + provider: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Retrieves a batch. @@ -148,9 +138,7 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not batch_id: - raise ValueError( - f"Expected a non-empty value for `batch_id` but received {batch_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") return self._get( f"/v1/batches/{batch_id}", options=make_request_options( @@ -158,9 +146,7 @@ def retrieve( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform( - {"provider": provider}, batch_retrieve_params.BatchRetrieveParams - ), + query=maybe_transform({"provider": provider}, batch_retrieve_params.BatchRetrieveParams), ), cast_to=object, ) @@ -168,15 +154,15 @@ def retrieve( def list( self, *, - after: Optional[str] | NotGiven = NOT_GIVEN, - limit: Optional[int] | NotGiven = NOT_GIVEN, - provider: Optional[str] | NotGiven = NOT_GIVEN, + after: Optional[str] | Omit = omit, + limit: Optional[int] | Omit = omit, + provider: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Lists This is the equivalent of GET https://api.openai.com/v1/batches/ Supports @@ -226,7 +212,7 @@ def cancel_with_provider( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Cancel a batch. @@ -253,20 +239,13 @@ def cancel_with_provider( timeout: Override the client-level default timeout for this request, in seconds """ if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") if not batch_id: - raise ValueError( - f"Expected a non-empty value for `batch_id` but received {batch_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") return self._post( f"/{provider}/v1/batches/{batch_id}/cancel", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -280,7 +259,7 @@ def create_with_provider( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Create large batches of API requests for asynchronous processing. @@ -308,16 +287,11 @@ def create_with_provider( timeout: Override the client-level default timeout for this request, in seconds """ if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") return self._post( f"/{provider}/v1/batches", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -326,14 +300,14 @@ def list_with_provider( self, provider: str, *, - after: Optional[str] | NotGiven = NOT_GIVEN, - limit: Optional[int] | NotGiven = NOT_GIVEN, + after: Optional[str] | Omit = omit, + limit: Optional[int] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Lists This is the equivalent of GET https://api.openai.com/v1/batches/ Supports @@ -355,9 +329,7 @@ def list_with_provider( timeout: Override the client-level default timeout for this request, in seconds """ if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") return self._get( f"/{provider}/v1/batches", options=make_request_options( @@ -386,7 +358,7 @@ def retrieve_with_provider( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Retrieves a batch. @@ -412,20 +384,13 @@ def retrieve_with_provider( timeout: Override the client-level default timeout for this request, in seconds """ if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") if not batch_id: - raise ValueError( - f"Expected a non-empty value for `batch_id` but received {batch_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") return self._get( f"/{provider}/v1/batches/{batch_id}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -458,13 +423,13 @@ def with_streaming_response(self) -> AsyncBatchesResourceWithStreamingResponse: async def create( self, *, - provider: Optional[str] | NotGiven = NOT_GIVEN, + provider: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Create large batches of API requests for asynchronous processing. @@ -498,9 +463,7 @@ async def create( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=await async_maybe_transform( - {"provider": provider}, batch_create_params.BatchCreateParams - ), + query=await async_maybe_transform({"provider": provider}, batch_create_params.BatchCreateParams), ), cast_to=object, ) @@ -509,13 +472,13 @@ async def retrieve( self, batch_id: str, *, - provider: Optional[str] | NotGiven = NOT_GIVEN, + provider: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Retrieves a batch. @@ -541,9 +504,7 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not batch_id: - raise ValueError( - f"Expected a non-empty value for `batch_id` but received {batch_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") return await self._get( f"/v1/batches/{batch_id}", options=make_request_options( @@ -551,9 +512,7 @@ async def retrieve( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=await async_maybe_transform( - {"provider": provider}, batch_retrieve_params.BatchRetrieveParams - ), + query=await async_maybe_transform({"provider": provider}, batch_retrieve_params.BatchRetrieveParams), ), cast_to=object, ) @@ -561,15 +520,15 @@ async def retrieve( async def list( self, *, - after: Optional[str] | NotGiven = NOT_GIVEN, - limit: Optional[int] | NotGiven = NOT_GIVEN, - provider: Optional[str] | NotGiven = NOT_GIVEN, + after: Optional[str] | Omit = omit, + limit: Optional[int] | Omit = omit, + provider: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Lists This is the equivalent of GET https://api.openai.com/v1/batches/ Supports @@ -619,7 +578,7 @@ async def cancel_with_provider( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Cancel a batch. @@ -646,20 +605,13 @@ async def cancel_with_provider( timeout: Override the client-level default timeout for this request, in seconds """ if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") if not batch_id: - raise ValueError( - f"Expected a non-empty value for `batch_id` but received {batch_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") return await self._post( f"/{provider}/v1/batches/{batch_id}/cancel", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -673,7 +625,7 @@ async def create_with_provider( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Create large batches of API requests for asynchronous processing. @@ -701,16 +653,11 @@ async def create_with_provider( timeout: Override the client-level default timeout for this request, in seconds """ if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") return await self._post( f"/{provider}/v1/batches", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -719,14 +666,14 @@ async def list_with_provider( self, provider: str, *, - after: Optional[str] | NotGiven = NOT_GIVEN, - limit: Optional[int] | NotGiven = NOT_GIVEN, + after: Optional[str] | Omit = omit, + limit: Optional[int] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Lists This is the equivalent of GET https://api.openai.com/v1/batches/ Supports @@ -748,9 +695,7 @@ async def list_with_provider( timeout: Override the client-level default timeout for this request, in seconds """ if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") return await self._get( f"/{provider}/v1/batches", options=make_request_options( @@ -779,7 +724,7 @@ async def retrieve_with_provider( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Retrieves a batch. @@ -805,20 +750,13 @@ async def retrieve_with_provider( timeout: Override the client-level default timeout for this request, in seconds """ if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") if not batch_id: - raise ValueError( - f"Expected a non-empty value for `batch_id` but received {batch_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") return await self._get( f"/{provider}/v1/batches/{batch_id}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/batches/cancel.py b/src/hanzoai/resources/batches/cancel.py new file mode 100644 index 000000000..6dfd4a578 --- /dev/null +++ b/src/hanzoai/resources/batches/cancel.py @@ -0,0 +1,200 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.batches import cancel_cancel_params + +__all__ = ["CancelResource", "AsyncCancelResource"] + + +class CancelResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CancelResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return CancelResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CancelResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return CancelResourceWithStreamingResponse(self) + + def cancel( + self, + batch_id: str, + *, + provider: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Cancel a batch. + + This is the equivalent of POST + https://api.openai.com/v1/batches/{batch_id}/cancel + + Supports Identical Params as: + https://platform.openai.com/docs/api-reference/batch/cancel + + Example Curl + + ``` + curl http://localhost:4000/v1/batches/batch_abc123/cancel -H "Authorization: Bearer sk-1234" -H "Content-Type: application/json" -X POST + + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + return self._post( + f"/batches/{batch_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"provider": provider}, cancel_cancel_params.CancelCancelParams), + ), + cast_to=object, + ) + + +class AsyncCancelResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCancelResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncCancelResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCancelResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncCancelResourceWithStreamingResponse(self) + + async def cancel( + self, + batch_id: str, + *, + provider: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Cancel a batch. + + This is the equivalent of POST + https://api.openai.com/v1/batches/{batch_id}/cancel + + Supports Identical Params as: + https://platform.openai.com/docs/api-reference/batch/cancel + + Example Curl + + ``` + curl http://localhost:4000/v1/batches/batch_abc123/cancel -H "Authorization: Bearer sk-1234" -H "Content-Type: application/json" -X POST + + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + return await self._post( + f"/batches/{batch_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"provider": provider}, cancel_cancel_params.CancelCancelParams), + ), + cast_to=object, + ) + + +class CancelResourceWithRawResponse: + def __init__(self, cancel: CancelResource) -> None: + self._cancel = cancel + + self.cancel = to_raw_response_wrapper( + cancel.cancel, + ) + + +class AsyncCancelResourceWithRawResponse: + def __init__(self, cancel: AsyncCancelResource) -> None: + self._cancel = cancel + + self.cancel = async_to_raw_response_wrapper( + cancel.cancel, + ) + + +class CancelResourceWithStreamingResponse: + def __init__(self, cancel: CancelResource) -> None: + self._cancel = cancel + + self.cancel = to_streamed_response_wrapper( + cancel.cancel, + ) + + +class AsyncCancelResourceWithStreamingResponse: + def __init__(self, cancel: AsyncCancelResource) -> None: + self._cancel = cancel + + self.cancel = async_to_streamed_response_wrapper( + cancel.cancel, + ) diff --git a/pkg/hanzoai/resources/bedrock.py b/src/hanzoai/resources/bedrock.py similarity index 79% rename from pkg/hanzoai/resources/bedrock.py rename to src/hanzoai/resources/bedrock.py index 492d53bca..3910eeccf 100644 --- a/pkg/hanzoai/resources/bedrock.py +++ b/src/hanzoai/resources/bedrock.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -47,7 +47,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/bedrock) @@ -62,16 +62,11 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._post( f"/bedrock/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -85,7 +80,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/bedrock) @@ -100,16 +95,11 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._get( f"/bedrock/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -123,7 +113,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/bedrock) @@ -138,16 +128,11 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._put( f"/bedrock/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -161,7 +146,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/bedrock) @@ -176,16 +161,11 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._delete( f"/bedrock/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -199,7 +179,7 @@ def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/bedrock) @@ -214,16 +194,11 @@ def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._patch( f"/bedrock/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -258,7 +233,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/bedrock) @@ -273,16 +248,11 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._post( f"/bedrock/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -296,7 +266,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/bedrock) @@ -311,16 +281,11 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._get( f"/bedrock/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -334,7 +299,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/bedrock) @@ -349,16 +314,11 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._put( f"/bedrock/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -372,7 +332,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/bedrock) @@ -387,16 +347,11 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._delete( f"/bedrock/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -410,7 +365,7 @@ async def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/bedrock) @@ -425,16 +380,11 @@ async def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._patch( f"/bedrock/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/pkg/hanzoai/resources/budget.py b/src/hanzoai/resources/budget.py similarity index 82% rename from pkg/hanzoai/resources/budget.py rename to src/hanzoai/resources/budget.py index 93e03d198..3fdc8cc74 100644 --- a/pkg/hanzoai/resources/budget.py +++ b/src/hanzoai/resources/budget.py @@ -1,8 +1,8 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations -from typing import Dict, List, Optional +from typing import Dict, Optional import httpx @@ -13,11 +13,8 @@ budget_update_params, budget_settings_params, ) -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - maybe_transform, - async_maybe_transform, -) +from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -54,22 +51,20 @@ def with_streaming_response(self) -> BudgetResourceWithStreamingResponse: def create( self, *, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - model_max_budget: ( - Optional[Dict[str, budget_create_params.ModelMaxBudget]] | NotGiven - ) = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - soft_budget: Optional[float] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, + budget_duration: Optional[str] | Omit = omit, + budget_id: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + model_max_budget: Optional[Dict[str, budget_create_params.ModelMaxBudget]] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + soft_budget: Optional[float] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Create a new budget object. @@ -132,10 +127,7 @@ def create( budget_create_params.BudgetCreateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -143,22 +135,20 @@ def create( def update( self, *, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - model_max_budget: ( - Optional[Dict[str, budget_update_params.ModelMaxBudget]] | NotGiven - ) = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - soft_budget: Optional[float] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, + budget_duration: Optional[str] | Omit = omit, + budget_id: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + model_max_budget: Optional[Dict[str, budget_update_params.ModelMaxBudget]] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + soft_budget: Optional[float] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Update an existing budget object. @@ -220,10 +210,7 @@ def update( budget_update_params.BudgetUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -236,16 +223,13 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """List all the created budgets in proxy db. Used on Admin UI.""" return self._get( "/budget/list", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -259,7 +243,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Delete budget @@ -281,10 +265,7 @@ def delete( "/budget/delete", body=maybe_transform({"id": id}, budget_delete_params.BudgetDeleteParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -292,13 +273,13 @@ def delete( def info( self, *, - budgets: List[str], + budgets: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Get the budget id specific information @@ -318,14 +299,9 @@ def info( """ return self._post( "/budget/info", - body=maybe_transform( - {"budgets": budgets}, budget_info_params.BudgetInfoParams - ), + body=maybe_transform({"budgets": budgets}, budget_info_params.BudgetInfoParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -339,7 +315,7 @@ def settings( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Get list of configurable params + current value for a budget item + description @@ -367,10 +343,7 @@ def settings( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform( - {"budget_id": budget_id}, - budget_settings_params.BudgetSettingsParams, - ), + query=maybe_transform({"budget_id": budget_id}, budget_settings_params.BudgetSettingsParams), ), cast_to=object, ) @@ -399,22 +372,20 @@ def with_streaming_response(self) -> AsyncBudgetResourceWithStreamingResponse: async def create( self, *, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - model_max_budget: ( - Optional[Dict[str, budget_create_params.ModelMaxBudget]] | NotGiven - ) = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - soft_budget: Optional[float] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, + budget_duration: Optional[str] | Omit = omit, + budget_id: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + model_max_budget: Optional[Dict[str, budget_create_params.ModelMaxBudget]] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + soft_budget: Optional[float] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Create a new budget object. @@ -477,10 +448,7 @@ async def create( budget_create_params.BudgetCreateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -488,22 +456,20 @@ async def create( async def update( self, *, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - model_max_budget: ( - Optional[Dict[str, budget_update_params.ModelMaxBudget]] | NotGiven - ) = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - soft_budget: Optional[float] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, + budget_duration: Optional[str] | Omit = omit, + budget_id: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + model_max_budget: Optional[Dict[str, budget_update_params.ModelMaxBudget]] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + soft_budget: Optional[float] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Update an existing budget object. @@ -565,10 +531,7 @@ async def update( budget_update_params.BudgetUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -581,16 +544,13 @@ async def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """List all the created budgets in proxy db. Used on Admin UI.""" return await self._get( "/budget/list", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -604,7 +564,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Delete budget @@ -624,14 +584,9 @@ async def delete( """ return await self._post( "/budget/delete", - body=await async_maybe_transform( - {"id": id}, budget_delete_params.BudgetDeleteParams - ), + body=await async_maybe_transform({"id": id}, budget_delete_params.BudgetDeleteParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -639,13 +594,13 @@ async def delete( async def info( self, *, - budgets: List[str], + budgets: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Get the budget id specific information @@ -665,14 +620,9 @@ async def info( """ return await self._post( "/budget/info", - body=await async_maybe_transform( - {"budgets": budgets}, budget_info_params.BudgetInfoParams - ), + body=await async_maybe_transform({"budgets": budgets}, budget_info_params.BudgetInfoParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -686,7 +636,7 @@ async def settings( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Get list of configurable params + current value for a budget item + description @@ -715,8 +665,7 @@ async def settings( extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( - {"budget_id": budget_id}, - budget_settings_params.BudgetSettingsParams, + {"budget_id": budget_id}, budget_settings_params.BudgetSettingsParams ), ), cast_to=object, diff --git a/src/hanzoai/resources/cache/__init__.py b/src/hanzoai/resources/cache/__init__.py new file mode 100644 index 000000000..b4dc871be --- /dev/null +++ b/src/hanzoai/resources/cache/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .cache import ( + CacheResource, + AsyncCacheResource, + CacheResourceWithRawResponse, + AsyncCacheResourceWithRawResponse, + CacheResourceWithStreamingResponse, + AsyncCacheResourceWithStreamingResponse, +) +from .redis import ( + RedisResource, + AsyncRedisResource, + RedisResourceWithRawResponse, + AsyncRedisResourceWithRawResponse, + RedisResourceWithStreamingResponse, + AsyncRedisResourceWithStreamingResponse, +) + +__all__ = [ + "RedisResource", + "AsyncRedisResource", + "RedisResourceWithRawResponse", + "AsyncRedisResourceWithRawResponse", + "RedisResourceWithStreamingResponse", + "AsyncRedisResourceWithStreamingResponse", + "CacheResource", + "AsyncCacheResource", + "CacheResourceWithRawResponse", + "AsyncCacheResourceWithRawResponse", + "CacheResourceWithStreamingResponse", + "AsyncCacheResourceWithStreamingResponse", +] diff --git a/src/hanzoai/resources/cache/cache.py b/src/hanzoai/resources/cache/cache.py new file mode 100644 index 000000000..bce946278 --- /dev/null +++ b/src/hanzoai/resources/cache/cache.py @@ -0,0 +1,317 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .redis import ( + RedisResource, + AsyncRedisResource, + RedisResourceWithRawResponse, + AsyncRedisResourceWithRawResponse, + RedisResourceWithStreamingResponse, + AsyncRedisResourceWithStreamingResponse, +) +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.cache_ping_response import CachePingResponse + +__all__ = ["CacheResource", "AsyncCacheResource"] + + +class CacheResource(SyncAPIResource): + @cached_property + def redis(self) -> RedisResource: + return RedisResource(self._client) + + @cached_property + def with_raw_response(self) -> CacheResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return CacheResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CacheResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return CacheResourceWithStreamingResponse(self) + + def delete( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Endpoint for deleting a key from the cache. + + All responses from llm proxy have + `x-llm-cache-key` in the headers + + Parameters: + + - **keys**: _Optional[List[str]]_ - A list of keys to delete from the cache. + Example {"keys": ["key1", "key2"]} + + ```shell + curl -X POST "http://0.0.0.0:4000/cache/delete" -H "Authorization: Bearer sk-1234" -d '{"keys": ["key1", "key2"]}' + ``` + """ + return self._post( + "/cache/delete", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def flush_all( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """A function to flush all items from the cache. + + (All items will be deleted from + the cache with this) Raises HTTPException if the cache is not initialized or if + the cache type does not support flushing. Returns a dictionary with the status + of the operation. + + Usage: + + ``` + curl -X POST http://0.0.0.0:4000/cache/flushall -H "Authorization: Bearer sk-1234" + ``` + """ + return self._post( + "/cache/flushall", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def ping( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CachePingResponse: + """Endpoint for checking if cache can be pinged""" + return self._get( + "/cache/ping", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CachePingResponse, + ) + + +class AsyncCacheResource(AsyncAPIResource): + @cached_property + def redis(self) -> AsyncRedisResource: + return AsyncRedisResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncCacheResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncCacheResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCacheResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncCacheResourceWithStreamingResponse(self) + + async def delete( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Endpoint for deleting a key from the cache. + + All responses from llm proxy have + `x-llm-cache-key` in the headers + + Parameters: + + - **keys**: _Optional[List[str]]_ - A list of keys to delete from the cache. + Example {"keys": ["key1", "key2"]} + + ```shell + curl -X POST "http://0.0.0.0:4000/cache/delete" -H "Authorization: Bearer sk-1234" -d '{"keys": ["key1", "key2"]}' + ``` + """ + return await self._post( + "/cache/delete", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def flush_all( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """A function to flush all items from the cache. + + (All items will be deleted from + the cache with this) Raises HTTPException if the cache is not initialized or if + the cache type does not support flushing. Returns a dictionary with the status + of the operation. + + Usage: + + ``` + curl -X POST http://0.0.0.0:4000/cache/flushall -H "Authorization: Bearer sk-1234" + ``` + """ + return await self._post( + "/cache/flushall", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def ping( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CachePingResponse: + """Endpoint for checking if cache can be pinged""" + return await self._get( + "/cache/ping", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CachePingResponse, + ) + + +class CacheResourceWithRawResponse: + def __init__(self, cache: CacheResource) -> None: + self._cache = cache + + self.delete = to_raw_response_wrapper( + cache.delete, + ) + self.flush_all = to_raw_response_wrapper( + cache.flush_all, + ) + self.ping = to_raw_response_wrapper( + cache.ping, + ) + + @cached_property + def redis(self) -> RedisResourceWithRawResponse: + return RedisResourceWithRawResponse(self._cache.redis) + + +class AsyncCacheResourceWithRawResponse: + def __init__(self, cache: AsyncCacheResource) -> None: + self._cache = cache + + self.delete = async_to_raw_response_wrapper( + cache.delete, + ) + self.flush_all = async_to_raw_response_wrapper( + cache.flush_all, + ) + self.ping = async_to_raw_response_wrapper( + cache.ping, + ) + + @cached_property + def redis(self) -> AsyncRedisResourceWithRawResponse: + return AsyncRedisResourceWithRawResponse(self._cache.redis) + + +class CacheResourceWithStreamingResponse: + def __init__(self, cache: CacheResource) -> None: + self._cache = cache + + self.delete = to_streamed_response_wrapper( + cache.delete, + ) + self.flush_all = to_streamed_response_wrapper( + cache.flush_all, + ) + self.ping = to_streamed_response_wrapper( + cache.ping, + ) + + @cached_property + def redis(self) -> RedisResourceWithStreamingResponse: + return RedisResourceWithStreamingResponse(self._cache.redis) + + +class AsyncCacheResourceWithStreamingResponse: + def __init__(self, cache: AsyncCacheResource) -> None: + self._cache = cache + + self.delete = async_to_streamed_response_wrapper( + cache.delete, + ) + self.flush_all = async_to_streamed_response_wrapper( + cache.flush_all, + ) + self.ping = async_to_streamed_response_wrapper( + cache.ping, + ) + + @cached_property + def redis(self) -> AsyncRedisResourceWithStreamingResponse: + return AsyncRedisResourceWithStreamingResponse(self._cache.redis) diff --git a/pkg/hanzoai/resources/cache/redis.py b/src/hanzoai/resources/cache/redis.py similarity index 88% rename from pkg/hanzoai/resources/cache/redis.py rename to src/hanzoai/resources/cache/redis.py index dffafa8ab..cb199ae9a 100644 --- a/pkg/hanzoai/resources/cache/redis.py +++ b/src/hanzoai/resources/cache/redis.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Query, Headers, NotGiven, not_given from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -46,16 +46,13 @@ def retrieve_info( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Endpoint for getting /redis/info""" return self._get( "/cache/redis/info", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -89,16 +86,13 @@ async def retrieve_info( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Endpoint for getting /redis/info""" return await self._get( "/cache/redis/info", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/chat/__init__.py b/src/hanzoai/resources/chat/__init__.py new file mode 100644 index 000000000..ec960eb44 --- /dev/null +++ b/src/hanzoai/resources/chat/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .chat import ( + ChatResource, + AsyncChatResource, + ChatResourceWithRawResponse, + AsyncChatResourceWithRawResponse, + ChatResourceWithStreamingResponse, + AsyncChatResourceWithStreamingResponse, +) +from .completions import ( + CompletionsResource, + AsyncCompletionsResource, + CompletionsResourceWithRawResponse, + AsyncCompletionsResourceWithRawResponse, + CompletionsResourceWithStreamingResponse, + AsyncCompletionsResourceWithStreamingResponse, +) + +__all__ = [ + "CompletionsResource", + "AsyncCompletionsResource", + "CompletionsResourceWithRawResponse", + "AsyncCompletionsResourceWithRawResponse", + "CompletionsResourceWithStreamingResponse", + "AsyncCompletionsResourceWithStreamingResponse", + "ChatResource", + "AsyncChatResource", + "ChatResourceWithRawResponse", + "AsyncChatResourceWithRawResponse", + "ChatResourceWithStreamingResponse", + "AsyncChatResourceWithStreamingResponse", +] diff --git a/src/hanzoai/resources/chat/chat.py b/src/hanzoai/resources/chat/chat.py new file mode 100644 index 000000000..a70389ebc --- /dev/null +++ b/src/hanzoai/resources/chat/chat.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from .completions import ( + CompletionsResource, + AsyncCompletionsResource, + CompletionsResourceWithRawResponse, + AsyncCompletionsResourceWithRawResponse, + CompletionsResourceWithStreamingResponse, + AsyncCompletionsResourceWithStreamingResponse, +) + +__all__ = ["ChatResource", "AsyncChatResource"] + + +class ChatResource(SyncAPIResource): + @cached_property + def completions(self) -> CompletionsResource: + return CompletionsResource(self._client) + + @cached_property + def with_raw_response(self) -> ChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return ChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return ChatResourceWithStreamingResponse(self) + + +class AsyncChatResource(AsyncAPIResource): + @cached_property + def completions(self) -> AsyncCompletionsResource: + return AsyncCompletionsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncChatResourceWithStreamingResponse(self) + + +class ChatResourceWithRawResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + @cached_property + def completions(self) -> CompletionsResourceWithRawResponse: + return CompletionsResourceWithRawResponse(self._chat.completions) + + +class AsyncChatResourceWithRawResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + @cached_property + def completions(self) -> AsyncCompletionsResourceWithRawResponse: + return AsyncCompletionsResourceWithRawResponse(self._chat.completions) + + +class ChatResourceWithStreamingResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + @cached_property + def completions(self) -> CompletionsResourceWithStreamingResponse: + return CompletionsResourceWithStreamingResponse(self._chat.completions) + + +class AsyncChatResourceWithStreamingResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + @cached_property + def completions(self) -> AsyncCompletionsResourceWithStreamingResponse: + return AsyncCompletionsResourceWithStreamingResponse(self._chat.completions) diff --git a/src/hanzoai/resources/chat/completions.py b/src/hanzoai/resources/chat/completions.py new file mode 100644 index 000000000..226fa58d5 --- /dev/null +++ b/src/hanzoai/resources/chat/completions.py @@ -0,0 +1,202 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...types.chat import completion_create_params +from ..._base_client import make_request_options + +__all__ = ["CompletionsResource", "AsyncCompletionsResource"] + + +class CompletionsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CompletionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return CompletionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CompletionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return CompletionsResourceWithStreamingResponse(self) + + def create( + self, + *, + model: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Follows the exact same API spec as + `OpenAI's Chat API https://platform.openai.com/docs/api-reference/chat` + + ```bash + curl -X POST http://localhost:4000/v1/chat/completions + -H "Content-Type: application/json" + -H "Authorization: Bearer sk-1234" + -d '{ + "model": "gpt-4o", + "messages": [ + { + "role": "user", + "content": "Hello!" + } + ] + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v1/chat/completions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"model": model}, completion_create_params.CompletionCreateParams), + ), + cast_to=object, + ) + + +class AsyncCompletionsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCompletionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncCompletionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCompletionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncCompletionsResourceWithStreamingResponse(self) + + async def create( + self, + *, + model: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Follows the exact same API spec as + `OpenAI's Chat API https://platform.openai.com/docs/api-reference/chat` + + ```bash + curl -X POST http://localhost:4000/v1/chat/completions + -H "Content-Type: application/json" + -H "Authorization: Bearer sk-1234" + -d '{ + "model": "gpt-4o", + "messages": [ + { + "role": "user", + "content": "Hello!" + } + ] + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v1/chat/completions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"model": model}, completion_create_params.CompletionCreateParams), + ), + cast_to=object, + ) + + +class CompletionsResourceWithRawResponse: + def __init__(self, completions: CompletionsResource) -> None: + self._completions = completions + + self.create = to_raw_response_wrapper( + completions.create, + ) + + +class AsyncCompletionsResourceWithRawResponse: + def __init__(self, completions: AsyncCompletionsResource) -> None: + self._completions = completions + + self.create = async_to_raw_response_wrapper( + completions.create, + ) + + +class CompletionsResourceWithStreamingResponse: + def __init__(self, completions: CompletionsResource) -> None: + self._completions = completions + + self.create = to_streamed_response_wrapper( + completions.create, + ) + + +class AsyncCompletionsResourceWithStreamingResponse: + def __init__(self, completions: AsyncCompletionsResource) -> None: + self._completions = completions + + self.create = async_to_streamed_response_wrapper( + completions.create, + ) diff --git a/pkg/hanzoai/resources/cohere.py b/src/hanzoai/resources/cohere.py similarity index 79% rename from pkg/hanzoai/resources/cohere.py rename to src/hanzoai/resources/cohere.py index c03463064..05841bca8 100644 --- a/pkg/hanzoai/resources/cohere.py +++ b/src/hanzoai/resources/cohere.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -47,7 +47,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/cohere) @@ -62,16 +62,11 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._post( f"/cohere/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -85,7 +80,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/cohere) @@ -100,16 +95,11 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._get( f"/cohere/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -123,7 +113,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/cohere) @@ -138,16 +128,11 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._put( f"/cohere/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -161,7 +146,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/cohere) @@ -176,16 +161,11 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._delete( f"/cohere/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -199,7 +179,7 @@ def modify( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/cohere) @@ -214,16 +194,11 @@ def modify( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._patch( f"/cohere/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -258,7 +233,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/cohere) @@ -273,16 +248,11 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._post( f"/cohere/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -296,7 +266,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/cohere) @@ -311,16 +281,11 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._get( f"/cohere/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -334,7 +299,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/cohere) @@ -349,16 +314,11 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._put( f"/cohere/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -372,7 +332,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/cohere) @@ -387,16 +347,11 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._delete( f"/cohere/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -410,7 +365,7 @@ async def modify( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/cohere) @@ -425,16 +380,11 @@ async def modify( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._patch( f"/cohere/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/completions.py b/src/hanzoai/resources/completions.py new file mode 100644 index 000000000..bc9987acd --- /dev/null +++ b/src/hanzoai/resources/completions.py @@ -0,0 +1,196 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..types import completion_create_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options + +__all__ = ["CompletionsResource", "AsyncCompletionsResource"] + + +class CompletionsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CompletionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return CompletionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CompletionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return CompletionsResourceWithStreamingResponse(self) + + def create( + self, + *, + model: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Follows the exact same API spec as + `OpenAI's Completions API https://platform.openai.com/docs/api-reference/completions` + + ```bash + curl -X POST http://localhost:4000/v1/completions + -H "Content-Type: application/json" + -H "Authorization: Bearer sk-1234" + -d '{ + "model": "gpt-3.5-turbo-instruct", + "prompt": "Once upon a time", + "max_tokens": 50, + "temperature": 0.7 + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/completions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"model": model}, completion_create_params.CompletionCreateParams), + ), + cast_to=object, + ) + + +class AsyncCompletionsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCompletionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncCompletionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCompletionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncCompletionsResourceWithStreamingResponse(self) + + async def create( + self, + *, + model: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Follows the exact same API spec as + `OpenAI's Completions API https://platform.openai.com/docs/api-reference/completions` + + ```bash + curl -X POST http://localhost:4000/v1/completions + -H "Content-Type: application/json" + -H "Authorization: Bearer sk-1234" + -d '{ + "model": "gpt-3.5-turbo-instruct", + "prompt": "Once upon a time", + "max_tokens": 50, + "temperature": 0.7 + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/completions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"model": model}, completion_create_params.CompletionCreateParams), + ), + cast_to=object, + ) + + +class CompletionsResourceWithRawResponse: + def __init__(self, completions: CompletionsResource) -> None: + self._completions = completions + + self.create = to_raw_response_wrapper( + completions.create, + ) + + +class AsyncCompletionsResourceWithRawResponse: + def __init__(self, completions: AsyncCompletionsResource) -> None: + self._completions = completions + + self.create = async_to_raw_response_wrapper( + completions.create, + ) + + +class CompletionsResourceWithStreamingResponse: + def __init__(self, completions: CompletionsResource) -> None: + self._completions = completions + + self.create = to_streamed_response_wrapper( + completions.create, + ) + + +class AsyncCompletionsResourceWithStreamingResponse: + def __init__(self, completions: AsyncCompletionsResource) -> None: + self._completions = completions + + self.create = async_to_streamed_response_wrapper( + completions.create, + ) diff --git a/src/hanzoai/resources/config/__init__.py b/src/hanzoai/resources/config/__init__.py new file mode 100644 index 000000000..5e0e69a3f --- /dev/null +++ b/src/hanzoai/resources/config/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .config import ( + ConfigResource, + AsyncConfigResource, + ConfigResourceWithRawResponse, + AsyncConfigResourceWithRawResponse, + ConfigResourceWithStreamingResponse, + AsyncConfigResourceWithStreamingResponse, +) +from .pass_through_endpoint import ( + PassThroughEndpointResource, + AsyncPassThroughEndpointResource, + PassThroughEndpointResourceWithRawResponse, + AsyncPassThroughEndpointResourceWithRawResponse, + PassThroughEndpointResourceWithStreamingResponse, + AsyncPassThroughEndpointResourceWithStreamingResponse, +) + +__all__ = [ + "PassThroughEndpointResource", + "AsyncPassThroughEndpointResource", + "PassThroughEndpointResourceWithRawResponse", + "AsyncPassThroughEndpointResourceWithRawResponse", + "PassThroughEndpointResourceWithStreamingResponse", + "AsyncPassThroughEndpointResourceWithStreamingResponse", + "ConfigResource", + "AsyncConfigResource", + "ConfigResourceWithRawResponse", + "AsyncConfigResourceWithRawResponse", + "ConfigResourceWithStreamingResponse", + "AsyncConfigResourceWithStreamingResponse", +] diff --git a/src/hanzoai/resources/config/config.py b/src/hanzoai/resources/config/config.py new file mode 100644 index 000000000..a5cfa29f6 --- /dev/null +++ b/src/hanzoai/resources/config/config.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from .pass_through_endpoint import ( + PassThroughEndpointResource, + AsyncPassThroughEndpointResource, + PassThroughEndpointResourceWithRawResponse, + AsyncPassThroughEndpointResourceWithRawResponse, + PassThroughEndpointResourceWithStreamingResponse, + AsyncPassThroughEndpointResourceWithStreamingResponse, +) + +__all__ = ["ConfigResource", "AsyncConfigResource"] + + +class ConfigResource(SyncAPIResource): + @cached_property + def pass_through_endpoint(self) -> PassThroughEndpointResource: + return PassThroughEndpointResource(self._client) + + @cached_property + def with_raw_response(self) -> ConfigResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return ConfigResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ConfigResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return ConfigResourceWithStreamingResponse(self) + + +class AsyncConfigResource(AsyncAPIResource): + @cached_property + def pass_through_endpoint(self) -> AsyncPassThroughEndpointResource: + return AsyncPassThroughEndpointResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncConfigResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncConfigResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncConfigResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncConfigResourceWithStreamingResponse(self) + + +class ConfigResourceWithRawResponse: + def __init__(self, config: ConfigResource) -> None: + self._config = config + + @cached_property + def pass_through_endpoint(self) -> PassThroughEndpointResourceWithRawResponse: + return PassThroughEndpointResourceWithRawResponse(self._config.pass_through_endpoint) + + +class AsyncConfigResourceWithRawResponse: + def __init__(self, config: AsyncConfigResource) -> None: + self._config = config + + @cached_property + def pass_through_endpoint(self) -> AsyncPassThroughEndpointResourceWithRawResponse: + return AsyncPassThroughEndpointResourceWithRawResponse(self._config.pass_through_endpoint) + + +class ConfigResourceWithStreamingResponse: + def __init__(self, config: ConfigResource) -> None: + self._config = config + + @cached_property + def pass_through_endpoint(self) -> PassThroughEndpointResourceWithStreamingResponse: + return PassThroughEndpointResourceWithStreamingResponse(self._config.pass_through_endpoint) + + +class AsyncConfigResourceWithStreamingResponse: + def __init__(self, config: AsyncConfigResource) -> None: + self._config = config + + @cached_property + def pass_through_endpoint(self) -> AsyncPassThroughEndpointResourceWithStreamingResponse: + return AsyncPassThroughEndpointResourceWithStreamingResponse(self._config.pass_through_endpoint) diff --git a/pkg/hanzoai/resources/config/pass_through_endpoint.py b/src/hanzoai/resources/config/pass_through_endpoint.py similarity index 85% rename from pkg/hanzoai/resources/config/pass_through_endpoint.py rename to src/hanzoai/resources/config/pass_through_endpoint.py index 3aa6f54fc..e0f22da7d 100644 --- a/pkg/hanzoai/resources/config/pass_through_endpoint.py +++ b/src/hanzoai/resources/config/pass_through_endpoint.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -6,11 +6,8 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -42,9 +39,7 @@ def with_raw_response(self) -> PassThroughEndpointResourceWithRawResponse: return PassThroughEndpointResourceWithRawResponse(self) @cached_property - def with_streaming_response( - self, - ) -> PassThroughEndpointResourceWithStreamingResponse: + def with_streaming_response(self) -> PassThroughEndpointResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. @@ -63,7 +58,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Create new pass-through endpoint @@ -72,7 +67,7 @@ def create( headers: Key-value pairs of headers to be forwarded with the request. You can set any key value pair here and it will be forwarded to your target endpoint - path: The route to be added to the Hanzo Proxy Server. + path: The route to be added to the LLM Proxy Server. target: The URL to which requests for this path should be forwarded. @@ -95,10 +90,7 @@ def create( pass_through_endpoint_create_params.PassThroughEndpointCreateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -112,7 +104,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Update a pass-through endpoint @@ -127,16 +119,11 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint_id: - raise ValueError( - f"Expected a non-empty value for `endpoint_id` but received {endpoint_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint_id` but received {endpoint_id!r}") return self._post( f"/config/pass_through_endpoint/{endpoint_id}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -144,13 +131,13 @@ def update( def list( self, *, - endpoint_id: Optional[str] | NotGiven = NOT_GIVEN, + endpoint_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PassThroughEndpointResponse: """ GET configured pass through endpoint. @@ -174,8 +161,7 @@ def list( extra_body=extra_body, timeout=timeout, query=maybe_transform( - {"endpoint_id": endpoint_id}, - pass_through_endpoint_list_params.PassThroughEndpointListParams, + {"endpoint_id": endpoint_id}, pass_through_endpoint_list_params.PassThroughEndpointListParams ), ), cast_to=PassThroughEndpointResponse, @@ -190,7 +176,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PassThroughEndpointResponse: """ Delete a pass-through endpoint @@ -214,8 +200,7 @@ def delete( extra_body=extra_body, timeout=timeout, query=maybe_transform( - {"endpoint_id": endpoint_id}, - pass_through_endpoint_delete_params.PassThroughEndpointDeleteParams, + {"endpoint_id": endpoint_id}, pass_through_endpoint_delete_params.PassThroughEndpointDeleteParams ), ), cast_to=PassThroughEndpointResponse, @@ -234,9 +219,7 @@ def with_raw_response(self) -> AsyncPassThroughEndpointResourceWithRawResponse: return AsyncPassThroughEndpointResourceWithRawResponse(self) @cached_property - def with_streaming_response( - self, - ) -> AsyncPassThroughEndpointResourceWithStreamingResponse: + def with_streaming_response(self) -> AsyncPassThroughEndpointResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. @@ -255,7 +238,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Create new pass-through endpoint @@ -264,7 +247,7 @@ async def create( headers: Key-value pairs of headers to be forwarded with the request. You can set any key value pair here and it will be forwarded to your target endpoint - path: The route to be added to the Hanzo Proxy Server. + path: The route to be added to the LLM Proxy Server. target: The URL to which requests for this path should be forwarded. @@ -287,10 +270,7 @@ async def create( pass_through_endpoint_create_params.PassThroughEndpointCreateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -304,7 +284,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Update a pass-through endpoint @@ -319,16 +299,11 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint_id: - raise ValueError( - f"Expected a non-empty value for `endpoint_id` but received {endpoint_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint_id` but received {endpoint_id!r}") return await self._post( f"/config/pass_through_endpoint/{endpoint_id}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -336,13 +311,13 @@ async def update( async def list( self, *, - endpoint_id: Optional[str] | NotGiven = NOT_GIVEN, + endpoint_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PassThroughEndpointResponse: """ GET configured pass through endpoint. @@ -366,8 +341,7 @@ async def list( extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( - {"endpoint_id": endpoint_id}, - pass_through_endpoint_list_params.PassThroughEndpointListParams, + {"endpoint_id": endpoint_id}, pass_through_endpoint_list_params.PassThroughEndpointListParams ), ), cast_to=PassThroughEndpointResponse, @@ -382,7 +356,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PassThroughEndpointResponse: """ Delete a pass-through endpoint @@ -406,8 +380,7 @@ async def delete( extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( - {"endpoint_id": endpoint_id}, - pass_through_endpoint_delete_params.PassThroughEndpointDeleteParams, + {"endpoint_id": endpoint_id}, pass_through_endpoint_delete_params.PassThroughEndpointDeleteParams ), ), cast_to=PassThroughEndpointResponse, diff --git a/src/hanzoai/resources/credentials.py b/src/hanzoai/resources/credentials.py new file mode 100644 index 000000000..527cd5f75 --- /dev/null +++ b/src/hanzoai/resources/credentials.py @@ -0,0 +1,320 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..types import credential_create_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options + +__all__ = ["CredentialsResource", "AsyncCredentialsResource"] + + +class CredentialsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CredentialsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return CredentialsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CredentialsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return CredentialsResourceWithStreamingResponse(self) + + def create( + self, + *, + credential_info: object, + credential_name: str, + credential_values: Optional[object] | Omit = omit, + model_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """[BETA] endpoint. + + This might change unexpectedly. Stores credential in DB. + Reloads credentials in memory. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/credentials", + body=maybe_transform( + { + "credential_info": credential_info, + "credential_name": credential_name, + "credential_values": credential_values, + "model_id": model_id, + }, + credential_create_params.CredentialCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """[BETA] endpoint. This might change unexpectedly.""" + return self._get( + "/credentials", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def delete( + self, + credential_name: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """[BETA] endpoint. + + This might change unexpectedly. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not credential_name: + raise ValueError(f"Expected a non-empty value for `credential_name` but received {credential_name!r}") + return self._delete( + f"/credentials/{credential_name}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncCredentialsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCredentialsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncCredentialsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCredentialsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncCredentialsResourceWithStreamingResponse(self) + + async def create( + self, + *, + credential_info: object, + credential_name: str, + credential_values: Optional[object] | Omit = omit, + model_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """[BETA] endpoint. + + This might change unexpectedly. Stores credential in DB. + Reloads credentials in memory. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/credentials", + body=await async_maybe_transform( + { + "credential_info": credential_info, + "credential_name": credential_name, + "credential_values": credential_values, + "model_id": model_id, + }, + credential_create_params.CredentialCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """[BETA] endpoint. This might change unexpectedly.""" + return await self._get( + "/credentials", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def delete( + self, + credential_name: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """[BETA] endpoint. + + This might change unexpectedly. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not credential_name: + raise ValueError(f"Expected a non-empty value for `credential_name` but received {credential_name!r}") + return await self._delete( + f"/credentials/{credential_name}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class CredentialsResourceWithRawResponse: + def __init__(self, credentials: CredentialsResource) -> None: + self._credentials = credentials + + self.create = to_raw_response_wrapper( + credentials.create, + ) + self.list = to_raw_response_wrapper( + credentials.list, + ) + self.delete = to_raw_response_wrapper( + credentials.delete, + ) + + +class AsyncCredentialsResourceWithRawResponse: + def __init__(self, credentials: AsyncCredentialsResource) -> None: + self._credentials = credentials + + self.create = async_to_raw_response_wrapper( + credentials.create, + ) + self.list = async_to_raw_response_wrapper( + credentials.list, + ) + self.delete = async_to_raw_response_wrapper( + credentials.delete, + ) + + +class CredentialsResourceWithStreamingResponse: + def __init__(self, credentials: CredentialsResource) -> None: + self._credentials = credentials + + self.create = to_streamed_response_wrapper( + credentials.create, + ) + self.list = to_streamed_response_wrapper( + credentials.list, + ) + self.delete = to_streamed_response_wrapper( + credentials.delete, + ) + + +class AsyncCredentialsResourceWithStreamingResponse: + def __init__(self, credentials: AsyncCredentialsResource) -> None: + self._credentials = credentials + + self.create = async_to_streamed_response_wrapper( + credentials.create, + ) + self.list = async_to_streamed_response_wrapper( + credentials.list, + ) + self.delete = async_to_streamed_response_wrapper( + credentials.delete, + ) diff --git a/pkg/hanzoai/resources/customer.py b/src/hanzoai/resources/customer.py similarity index 81% rename from pkg/hanzoai/resources/customer.py rename to src/hanzoai/resources/customer.py index e398c9f7a..b45340ac2 100644 --- a/pkg/hanzoai/resources/customer.py +++ b/src/hanzoai/resources/customer.py @@ -1,8 +1,8 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations -from typing import Dict, List, Optional +from typing import Dict, Optional from typing_extensions import Literal import httpx @@ -15,11 +15,8 @@ customer_unblock_params, customer_retrieve_info_params, ) -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - maybe_transform, - async_maybe_transform, -) +from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -30,7 +27,7 @@ ) from .._base_client import make_request_options from ..types.customer_list_response import CustomerListResponse -from ..types.lite_llm_end_user_table import HanzoEndUserTable +from ..types.customer_retrieve_info_response import CustomerRetrieveInfoResponse __all__ = ["CustomerResource", "AsyncCustomerResource"] @@ -59,26 +56,24 @@ def create( self, *, user_id: str, - alias: Optional[str] | NotGiven = NOT_GIVEN, - allowed_model_region: Optional[Literal["eu", "us"]] | NotGiven = NOT_GIVEN, - blocked: bool | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - default_model: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - model_max_budget: ( - Optional[Dict[str, customer_create_params.ModelMaxBudget]] | NotGiven - ) = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - soft_budget: Optional[float] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, + alias: Optional[str] | Omit = omit, + allowed_model_region: Optional[Literal["eu", "us"]] | Omit = omit, + blocked: bool | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + budget_id: Optional[str] | Omit = omit, + default_model: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + model_max_budget: Optional[Dict[str, customer_create_params.ModelMaxBudget]] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + soft_budget: Optional[float] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Allow creating a new Customer @@ -121,7 +116,7 @@ def create( ``` curl --location 'http://0.0.0.0:4000/customer/new' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "user_id" : "ishaan-jaff-3", + "user_id" : "z-jaff-3", "allowed_region": "eu", "budget_id": "free_tier", "default_model": "azure/gpt-3.5-turbo-eu" <- all calls from this user, use this model? @@ -178,10 +173,7 @@ def create( customer_create_params.CustomerCreateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -190,18 +182,18 @@ def update( self, *, user_id: str, - alias: Optional[str] | NotGiven = NOT_GIVEN, - allowed_model_region: Optional[Literal["eu", "us"]] | NotGiven = NOT_GIVEN, - blocked: bool | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - default_model: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, + alias: Optional[str] | Omit = omit, + allowed_model_region: Optional[Literal["eu", "us"]] | Omit = omit, + blocked: bool | Omit = omit, + budget_id: Optional[str] | Omit = omit, + default_model: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Example curl @@ -222,7 +214,7 @@ def update( ``` curl --location 'http://0.0.0.0:4000/customer/update' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "user_id": "test-hanzo-user-4", + "user_id": "test-llm-user-4", "budget_id": "paid_tier" }' @@ -253,10 +245,7 @@ def update( customer_update_params.CustomerUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -269,7 +258,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> CustomerListResponse: """ [Admin-only] List all available customers @@ -283,10 +272,7 @@ def list( return self._get( "/customer/list", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=CustomerListResponse, ) @@ -294,13 +280,13 @@ def list( def delete( self, *, - user_ids: List[str], + user_ids: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Delete multiple end-users. @@ -313,7 +299,7 @@ def delete( ``` curl --location 'http://0.0.0.0:4000/customer/delete' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "user_ids" :["ishaan-jaff-5"] + "user_ids" :["z-jaff-5"] }' See below for all params @@ -330,14 +316,9 @@ def delete( """ return self._post( "/customer/delete", - body=maybe_transform( - {"user_ids": user_ids}, customer_delete_params.CustomerDeleteParams - ), + body=maybe_transform({"user_ids": user_ids}, customer_delete_params.CustomerDeleteParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -345,13 +326,13 @@ def delete( def block( self, *, - user_ids: List[str], + user_ids: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [BETA] Reject calls with this end-user id @@ -382,14 +363,9 @@ def block( """ return self._post( "/customer/block", - body=maybe_transform( - {"user_ids": user_ids}, customer_block_params.CustomerBlockParams - ), + body=maybe_transform({"user_ids": user_ids}, customer_block_params.CustomerBlockParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -403,8 +379,8 @@ def retrieve_info( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> HanzoEndUserTable: + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CustomerRetrieveInfoResponse: """Get information about an end-user. An `end_user` is a customer (external user) @@ -417,7 +393,7 @@ def retrieve_info( Example curl: ``` - curl -X GET 'http://localhost:4000/customer/info?end_user_id=test-hanzo-user-4' -H 'Authorization: Bearer sk-1234' + curl -X GET 'http://localhost:4000/customer/info?end_user_id=test-llm-user-4' -H 'Authorization: Bearer sk-1234' ``` Args: @@ -439,23 +415,22 @@ def retrieve_info( extra_body=extra_body, timeout=timeout, query=maybe_transform( - {"end_user_id": end_user_id}, - customer_retrieve_info_params.CustomerRetrieveInfoParams, + {"end_user_id": end_user_id}, customer_retrieve_info_params.CustomerRetrieveInfoParams ), ), - cast_to=HanzoEndUserTable, + cast_to=CustomerRetrieveInfoResponse, ) def unblock( self, *, - user_ids: List[str], + user_ids: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [BETA] Unblock calls with this user id @@ -481,14 +456,9 @@ def unblock( """ return self._post( "/customer/unblock", - body=maybe_transform( - {"user_ids": user_ids}, customer_unblock_params.CustomerUnblockParams - ), + body=maybe_transform({"user_ids": user_ids}, customer_unblock_params.CustomerUnblockParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -518,26 +488,24 @@ async def create( self, *, user_id: str, - alias: Optional[str] | NotGiven = NOT_GIVEN, - allowed_model_region: Optional[Literal["eu", "us"]] | NotGiven = NOT_GIVEN, - blocked: bool | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - default_model: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - model_max_budget: ( - Optional[Dict[str, customer_create_params.ModelMaxBudget]] | NotGiven - ) = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - soft_budget: Optional[float] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, + alias: Optional[str] | Omit = omit, + allowed_model_region: Optional[Literal["eu", "us"]] | Omit = omit, + blocked: bool | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + budget_id: Optional[str] | Omit = omit, + default_model: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + model_max_budget: Optional[Dict[str, customer_create_params.ModelMaxBudget]] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + soft_budget: Optional[float] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Allow creating a new Customer @@ -580,7 +548,7 @@ async def create( ``` curl --location 'http://0.0.0.0:4000/customer/new' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "user_id" : "ishaan-jaff-3", + "user_id" : "z-jaff-3", "allowed_region": "eu", "budget_id": "free_tier", "default_model": "azure/gpt-3.5-turbo-eu" <- all calls from this user, use this model? @@ -637,10 +605,7 @@ async def create( customer_create_params.CustomerCreateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -649,18 +614,18 @@ async def update( self, *, user_id: str, - alias: Optional[str] | NotGiven = NOT_GIVEN, - allowed_model_region: Optional[Literal["eu", "us"]] | NotGiven = NOT_GIVEN, - blocked: bool | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - default_model: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, + alias: Optional[str] | Omit = omit, + allowed_model_region: Optional[Literal["eu", "us"]] | Omit = omit, + blocked: bool | Omit = omit, + budget_id: Optional[str] | Omit = omit, + default_model: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Example curl @@ -681,7 +646,7 @@ async def update( ``` curl --location 'http://0.0.0.0:4000/customer/update' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "user_id": "test-hanzo-user-4", + "user_id": "test-llm-user-4", "budget_id": "paid_tier" }' @@ -712,10 +677,7 @@ async def update( customer_update_params.CustomerUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -728,7 +690,7 @@ async def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> CustomerListResponse: """ [Admin-only] List all available customers @@ -742,10 +704,7 @@ async def list( return await self._get( "/customer/list", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=CustomerListResponse, ) @@ -753,13 +712,13 @@ async def list( async def delete( self, *, - user_ids: List[str], + user_ids: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Delete multiple end-users. @@ -772,7 +731,7 @@ async def delete( ``` curl --location 'http://0.0.0.0:4000/customer/delete' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "user_ids" :["ishaan-jaff-5"] + "user_ids" :["z-jaff-5"] }' See below for all params @@ -789,14 +748,9 @@ async def delete( """ return await self._post( "/customer/delete", - body=await async_maybe_transform( - {"user_ids": user_ids}, customer_delete_params.CustomerDeleteParams - ), + body=await async_maybe_transform({"user_ids": user_ids}, customer_delete_params.CustomerDeleteParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -804,13 +758,13 @@ async def delete( async def block( self, *, - user_ids: List[str], + user_ids: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [BETA] Reject calls with this end-user id @@ -841,14 +795,9 @@ async def block( """ return await self._post( "/customer/block", - body=await async_maybe_transform( - {"user_ids": user_ids}, customer_block_params.CustomerBlockParams - ), + body=await async_maybe_transform({"user_ids": user_ids}, customer_block_params.CustomerBlockParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -862,8 +811,8 @@ async def retrieve_info( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> HanzoEndUserTable: + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CustomerRetrieveInfoResponse: """Get information about an end-user. An `end_user` is a customer (external user) @@ -876,7 +825,7 @@ async def retrieve_info( Example curl: ``` - curl -X GET 'http://localhost:4000/customer/info?end_user_id=test-hanzo-user-4' -H 'Authorization: Bearer sk-1234' + curl -X GET 'http://localhost:4000/customer/info?end_user_id=test-llm-user-4' -H 'Authorization: Bearer sk-1234' ``` Args: @@ -898,23 +847,22 @@ async def retrieve_info( extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( - {"end_user_id": end_user_id}, - customer_retrieve_info_params.CustomerRetrieveInfoParams, + {"end_user_id": end_user_id}, customer_retrieve_info_params.CustomerRetrieveInfoParams ), ), - cast_to=HanzoEndUserTable, + cast_to=CustomerRetrieveInfoResponse, ) async def unblock( self, *, - user_ids: List[str], + user_ids: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [BETA] Unblock calls with this user id @@ -940,14 +888,9 @@ async def unblock( """ return await self._post( "/customer/unblock", - body=await async_maybe_transform( - {"user_ids": user_ids}, customer_unblock_params.CustomerUnblockParams - ), + body=await async_maybe_transform({"user_ids": user_ids}, customer_unblock_params.CustomerUnblockParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/pkg/hanzoai/resources/delete.py b/src/hanzoai/resources/delete.py similarity index 84% rename from pkg/hanzoai/resources/delete.py rename to src/hanzoai/resources/delete.py index 39f8de8f3..e9cf9d353 100644 --- a/pkg/hanzoai/resources/delete.py +++ b/src/hanzoai/resources/delete.py @@ -1,15 +1,12 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx from ..types import delete_create_allowed_ip_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - maybe_transform, - async_maybe_transform, -) +from .._types import Body, Query, Headers, NotGiven, not_given +from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -52,7 +49,7 @@ def create_allowed_ip( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Delete Allowed Ip @@ -68,14 +65,9 @@ def create_allowed_ip( """ return self._post( "/delete/allowed_ip", - body=maybe_transform( - {"ip": ip}, delete_create_allowed_ip_params.DeleteCreateAllowedIPParams - ), + body=maybe_transform({"ip": ip}, delete_create_allowed_ip_params.DeleteCreateAllowedIPParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -110,7 +102,7 @@ async def create_allowed_ip( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Delete Allowed Ip @@ -126,14 +118,9 @@ async def create_allowed_ip( """ return await self._post( "/delete/allowed_ip", - body=await async_maybe_transform( - {"ip": ip}, delete_create_allowed_ip_params.DeleteCreateAllowedIPParams - ), + body=await async_maybe_transform({"ip": ip}, delete_create_allowed_ip_params.DeleteCreateAllowedIPParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/embeddings.py b/src/hanzoai/resources/embeddings.py new file mode 100644 index 000000000..817525498 --- /dev/null +++ b/src/hanzoai/resources/embeddings.py @@ -0,0 +1,192 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..types import embedding_create_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options + +__all__ = ["EmbeddingsResource", "AsyncEmbeddingsResource"] + + +class EmbeddingsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> EmbeddingsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return EmbeddingsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EmbeddingsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return EmbeddingsResourceWithStreamingResponse(self) + + def create( + self, + *, + model: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Follows the exact same API spec as + `OpenAI's Embeddings API https://platform.openai.com/docs/api-reference/embeddings` + + ```bash + curl -X POST http://localhost:4000/v1/embeddings + -H "Content-Type: application/json" + -H "Authorization: Bearer sk-1234" + -d '{ + "model": "text-embedding-ada-002", + "input": "The quick brown fox jumps over the lazy dog" + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/embeddings", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"model": model}, embedding_create_params.EmbeddingCreateParams), + ), + cast_to=object, + ) + + +class AsyncEmbeddingsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncEmbeddingsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncEmbeddingsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEmbeddingsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncEmbeddingsResourceWithStreamingResponse(self) + + async def create( + self, + *, + model: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Follows the exact same API spec as + `OpenAI's Embeddings API https://platform.openai.com/docs/api-reference/embeddings` + + ```bash + curl -X POST http://localhost:4000/v1/embeddings + -H "Content-Type: application/json" + -H "Authorization: Bearer sk-1234" + -d '{ + "model": "text-embedding-ada-002", + "input": "The quick brown fox jumps over the lazy dog" + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/embeddings", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"model": model}, embedding_create_params.EmbeddingCreateParams), + ), + cast_to=object, + ) + + +class EmbeddingsResourceWithRawResponse: + def __init__(self, embeddings: EmbeddingsResource) -> None: + self._embeddings = embeddings + + self.create = to_raw_response_wrapper( + embeddings.create, + ) + + +class AsyncEmbeddingsResourceWithRawResponse: + def __init__(self, embeddings: AsyncEmbeddingsResource) -> None: + self._embeddings = embeddings + + self.create = async_to_raw_response_wrapper( + embeddings.create, + ) + + +class EmbeddingsResourceWithStreamingResponse: + def __init__(self, embeddings: EmbeddingsResource) -> None: + self._embeddings = embeddings + + self.create = to_streamed_response_wrapper( + embeddings.create, + ) + + +class AsyncEmbeddingsResourceWithStreamingResponse: + def __init__(self, embeddings: AsyncEmbeddingsResource) -> None: + self._embeddings = embeddings + + self.create = async_to_streamed_response_wrapper( + embeddings.create, + ) diff --git a/src/hanzoai/resources/engines/__init__.py b/src/hanzoai/resources/engines/__init__.py new file mode 100644 index 000000000..24c74c582 --- /dev/null +++ b/src/hanzoai/resources/engines/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .chat import ( + ChatResource, + AsyncChatResource, + ChatResourceWithRawResponse, + AsyncChatResourceWithRawResponse, + ChatResourceWithStreamingResponse, + AsyncChatResourceWithStreamingResponse, +) +from .engines import ( + EnginesResource, + AsyncEnginesResource, + EnginesResourceWithRawResponse, + AsyncEnginesResourceWithRawResponse, + EnginesResourceWithStreamingResponse, + AsyncEnginesResourceWithStreamingResponse, +) + +__all__ = [ + "ChatResource", + "AsyncChatResource", + "ChatResourceWithRawResponse", + "AsyncChatResourceWithRawResponse", + "ChatResourceWithStreamingResponse", + "AsyncChatResourceWithStreamingResponse", + "EnginesResource", + "AsyncEnginesResource", + "EnginesResourceWithRawResponse", + "AsyncEnginesResourceWithRawResponse", + "EnginesResourceWithStreamingResponse", + "AsyncEnginesResourceWithStreamingResponse", +] diff --git a/src/hanzoai/resources/engines/chat.py b/src/hanzoai/resources/engines/chat.py new file mode 100644 index 000000000..108231b6d --- /dev/null +++ b/src/hanzoai/resources/engines/chat.py @@ -0,0 +1,194 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options + +__all__ = ["ChatResource", "AsyncChatResource"] + + +class ChatResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return ChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return ChatResourceWithStreamingResponse(self) + + def complete( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Follows the exact same API spec as + `OpenAI's Chat API https://platform.openai.com/docs/api-reference/chat` + + ```bash + curl -X POST http://localhost:4000/v1/chat/completions + -H "Content-Type: application/json" + -H "Authorization: Bearer sk-1234" + -d '{ + "model": "gpt-4o", + "messages": [ + { + "role": "user", + "content": "Hello!" + } + ] + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") + return self._post( + f"/engines/{model}/chat/completions", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncChatResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncChatResourceWithStreamingResponse(self) + + async def complete( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Follows the exact same API spec as + `OpenAI's Chat API https://platform.openai.com/docs/api-reference/chat` + + ```bash + curl -X POST http://localhost:4000/v1/chat/completions + -H "Content-Type: application/json" + -H "Authorization: Bearer sk-1234" + -d '{ + "model": "gpt-4o", + "messages": [ + { + "role": "user", + "content": "Hello!" + } + ] + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") + return await self._post( + f"/engines/{model}/chat/completions", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class ChatResourceWithRawResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + self.complete = to_raw_response_wrapper( + chat.complete, + ) + + +class AsyncChatResourceWithRawResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + self.complete = async_to_raw_response_wrapper( + chat.complete, + ) + + +class ChatResourceWithStreamingResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + self.complete = to_streamed_response_wrapper( + chat.complete, + ) + + +class AsyncChatResourceWithStreamingResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + self.complete = async_to_streamed_response_wrapper( + chat.complete, + ) diff --git a/pkg/hanzoai/resources/engines/engines.py b/src/hanzoai/resources/engines/engines.py similarity index 87% rename from pkg/hanzoai/resources/engines/engines.py rename to src/hanzoai/resources/engines/engines.py index f2d186006..2396f2943 100644 --- a/pkg/hanzoai/resources/engines/engines.py +++ b/src/hanzoai/resources/engines/engines.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -12,7 +12,7 @@ ChatResourceWithStreamingResponse, AsyncChatResourceWithStreamingResponse, ) -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Query, Headers, NotGiven, not_given from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -59,7 +59,7 @@ def complete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Follows the exact same API spec as @@ -87,16 +87,11 @@ def complete( timeout: Override the client-level default timeout for this request, in seconds """ if not model: - raise ValueError( - f"Expected a non-empty value for `model` but received {model!r}" - ) + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") return self._post( f"/engines/{model}/completions", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -110,7 +105,7 @@ def embed( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Follows the exact same API spec as @@ -136,16 +131,11 @@ def embed( timeout: Override the client-level default timeout for this request, in seconds """ if not model: - raise ValueError( - f"Expected a non-empty value for `model` but received {model!r}" - ) + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") return self._post( f"/engines/{model}/embeddings", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -184,7 +174,7 @@ async def complete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Follows the exact same API spec as @@ -212,16 +202,11 @@ async def complete( timeout: Override the client-level default timeout for this request, in seconds """ if not model: - raise ValueError( - f"Expected a non-empty value for `model` but received {model!r}" - ) + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") return await self._post( f"/engines/{model}/completions", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -235,7 +220,7 @@ async def embed( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Follows the exact same API spec as @@ -261,16 +246,11 @@ async def embed( timeout: Override the client-level default timeout for this request, in seconds """ if not model: - raise ValueError( - f"Expected a non-empty value for `model` but received {model!r}" - ) + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") return await self._post( f"/engines/{model}/embeddings", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/pkg/hanzoai/resources/eu_assemblyai.py b/src/hanzoai/resources/eu_assemblyai.py similarity index 80% rename from pkg/hanzoai/resources/eu_assemblyai.py rename to src/hanzoai/resources/eu_assemblyai.py index 9e3b34315..4931aa8cd 100644 --- a/pkg/hanzoai/resources/eu_assemblyai.py +++ b/src/hanzoai/resources/eu_assemblyai.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -47,7 +47,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -62,16 +62,11 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._post( f"/eu.assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -85,7 +80,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -100,16 +95,11 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._get( f"/eu.assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -123,7 +113,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -138,16 +128,11 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._put( f"/eu.assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -161,7 +146,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -176,16 +161,11 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._delete( f"/eu.assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -199,7 +179,7 @@ def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -214,16 +194,11 @@ def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._patch( f"/eu.assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -258,7 +233,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -273,16 +248,11 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._post( f"/eu.assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -296,7 +266,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -311,16 +281,11 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._get( f"/eu.assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -334,7 +299,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -349,16 +314,11 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._put( f"/eu.assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -372,7 +332,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -387,16 +347,11 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._delete( f"/eu.assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -410,7 +365,7 @@ async def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Assemblyai Proxy Route @@ -425,16 +380,11 @@ async def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._patch( f"/eu.assemblyai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/files/__init__.py b/src/hanzoai/resources/files/__init__.py new file mode 100644 index 000000000..d1f68da62 --- /dev/null +++ b/src/hanzoai/resources/files/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .files import ( + FilesResource, + AsyncFilesResource, + FilesResourceWithRawResponse, + AsyncFilesResourceWithRawResponse, + FilesResourceWithStreamingResponse, + AsyncFilesResourceWithStreamingResponse, +) +from .content import ( + ContentResource, + AsyncContentResource, + ContentResourceWithRawResponse, + AsyncContentResourceWithRawResponse, + ContentResourceWithStreamingResponse, + AsyncContentResourceWithStreamingResponse, +) + +__all__ = [ + "ContentResource", + "AsyncContentResource", + "ContentResourceWithRawResponse", + "AsyncContentResourceWithRawResponse", + "ContentResourceWithStreamingResponse", + "AsyncContentResourceWithStreamingResponse", + "FilesResource", + "AsyncFilesResource", + "FilesResourceWithRawResponse", + "AsyncFilesResourceWithRawResponse", + "FilesResourceWithStreamingResponse", + "AsyncFilesResourceWithStreamingResponse", +] diff --git a/pkg/hanzoai/resources/files/content.py b/src/hanzoai/resources/files/content.py similarity index 85% rename from pkg/hanzoai/resources/files/content.py rename to src/hanzoai/resources/files/content.py index 618bdbe4e..0896ea03b 100644 --- a/pkg/hanzoai/resources/files/content.py +++ b/src/hanzoai/resources/files/content.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Query, Headers, NotGiven, not_given from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -48,7 +48,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Returns information about a specific file. @@ -76,20 +76,13 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") if not file_id: - raise ValueError( - f"Expected a non-empty value for `file_id` but received {file_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return self._get( f"/{provider}/v1/files/{file_id}/content", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -125,7 +118,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Returns information about a specific file. @@ -153,20 +146,13 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not provider: - raise ValueError( - f"Expected a non-empty value for `provider` but received {provider!r}" - ) + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") if not file_id: - raise ValueError( - f"Expected a non-empty value for `file_id` but received {file_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return await self._get( f"/{provider}/v1/files/{file_id}/content", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/files/files.py b/src/hanzoai/resources/files/files.py new file mode 100644 index 000000000..6e00beb18 --- /dev/null +++ b/src/hanzoai/resources/files/files.py @@ -0,0 +1,584 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Mapping, Optional, cast + +import httpx + +from ...types import file_list_params, file_create_params +from .content import ( + ContentResource, + AsyncContentResource, + ContentResourceWithRawResponse, + AsyncContentResourceWithRawResponse, + ContentResourceWithStreamingResponse, + AsyncContentResourceWithStreamingResponse, +) +from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given +from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options + +__all__ = ["FilesResource", "AsyncFilesResource"] + + +class FilesResource(SyncAPIResource): + @cached_property + def content(self) -> ContentResource: + return ContentResource(self._client) + + @cached_property + def with_raw_response(self) -> FilesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return FilesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FilesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return FilesResourceWithStreamingResponse(self) + + def create( + self, + provider: str, + *, + file: FileTypes, + purpose: str, + custom_llm_provider: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Upload a file that can be used across - Assistants API, Batch API This is the + equivalent of POST https://api.openai.com/v1/files + + Supports Identical Params as: + https://platform.openai.com/docs/api-reference/files/create + + Example Curl + + ``` + curl http://localhost:4000/v1/files -H "Authorization: Bearer sk-1234" -F purpose="batch" -F file="@mydata.jsonl" + + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not provider: + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") + body = deepcopy_minimal( + { + "file": file, + "purpose": purpose, + "custom_llm_provider": custom_llm_provider, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + f"/{provider}/v1/files", + body=maybe_transform(body, file_create_params.FileCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def retrieve( + self, + file_id: str, + *, + provider: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Returns information about a specific file. + + that can be used across - Assistants + API, Batch API This is the equivalent of GET + https://api.openai.com/v1/files/{file_id} + + Supports Identical Params as: + https://platform.openai.com/docs/api-reference/files/retrieve + + Example Curl + + ``` + curl http://localhost:4000/v1/files/file-abc123 -H "Authorization: Bearer sk-1234" + + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not provider: + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return self._get( + f"/{provider}/v1/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def list( + self, + provider: str, + *, + purpose: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Returns information about a specific file. + + that can be used across - Assistants + API, Batch API This is the equivalent of GET https://api.openai.com/v1/files/ + + Supports Identical Params as: + https://platform.openai.com/docs/api-reference/files/list + + Example Curl + + ``` + curl http://localhost:4000/v1/files -H "Authorization: Bearer sk-1234" + + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not provider: + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") + return self._get( + f"/{provider}/v1/files", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"purpose": purpose}, file_list_params.FileListParams), + ), + cast_to=object, + ) + + def delete( + self, + file_id: str, + *, + provider: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Deletes a specified file. + + that can be used across - Assistants API, Batch API + This is the equivalent of DELETE https://api.openai.com/v1/files/{file_id} + + Supports Identical Params as: + https://platform.openai.com/docs/api-reference/files/delete + + Example Curl + + ``` + curl http://localhost:4000/v1/files/file-abc123 -X DELETE -H "Authorization: Bearer $OPENAI_API_KEY" + + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not provider: + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return self._delete( + f"/{provider}/v1/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncFilesResource(AsyncAPIResource): + @cached_property + def content(self) -> AsyncContentResource: + return AsyncContentResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncFilesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncFilesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFilesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncFilesResourceWithStreamingResponse(self) + + async def create( + self, + provider: str, + *, + file: FileTypes, + purpose: str, + custom_llm_provider: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Upload a file that can be used across - Assistants API, Batch API This is the + equivalent of POST https://api.openai.com/v1/files + + Supports Identical Params as: + https://platform.openai.com/docs/api-reference/files/create + + Example Curl + + ``` + curl http://localhost:4000/v1/files -H "Authorization: Bearer sk-1234" -F purpose="batch" -F file="@mydata.jsonl" + + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not provider: + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") + body = deepcopy_minimal( + { + "file": file, + "purpose": purpose, + "custom_llm_provider": custom_llm_provider, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + f"/{provider}/v1/files", + body=await async_maybe_transform(body, file_create_params.FileCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def retrieve( + self, + file_id: str, + *, + provider: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Returns information about a specific file. + + that can be used across - Assistants + API, Batch API This is the equivalent of GET + https://api.openai.com/v1/files/{file_id} + + Supports Identical Params as: + https://platform.openai.com/docs/api-reference/files/retrieve + + Example Curl + + ``` + curl http://localhost:4000/v1/files/file-abc123 -H "Authorization: Bearer sk-1234" + + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not provider: + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return await self._get( + f"/{provider}/v1/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def list( + self, + provider: str, + *, + purpose: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Returns information about a specific file. + + that can be used across - Assistants + API, Batch API This is the equivalent of GET https://api.openai.com/v1/files/ + + Supports Identical Params as: + https://platform.openai.com/docs/api-reference/files/list + + Example Curl + + ``` + curl http://localhost:4000/v1/files -H "Authorization: Bearer sk-1234" + + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not provider: + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") + return await self._get( + f"/{provider}/v1/files", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"purpose": purpose}, file_list_params.FileListParams), + ), + cast_to=object, + ) + + async def delete( + self, + file_id: str, + *, + provider: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Deletes a specified file. + + that can be used across - Assistants API, Batch API + This is the equivalent of DELETE https://api.openai.com/v1/files/{file_id} + + Supports Identical Params as: + https://platform.openai.com/docs/api-reference/files/delete + + Example Curl + + ``` + curl http://localhost:4000/v1/files/file-abc123 -X DELETE -H "Authorization: Bearer $OPENAI_API_KEY" + + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not provider: + raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return await self._delete( + f"/{provider}/v1/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class FilesResourceWithRawResponse: + def __init__(self, files: FilesResource) -> None: + self._files = files + + self.create = to_raw_response_wrapper( + files.create, + ) + self.retrieve = to_raw_response_wrapper( + files.retrieve, + ) + self.list = to_raw_response_wrapper( + files.list, + ) + self.delete = to_raw_response_wrapper( + files.delete, + ) + + @cached_property + def content(self) -> ContentResourceWithRawResponse: + return ContentResourceWithRawResponse(self._files.content) + + +class AsyncFilesResourceWithRawResponse: + def __init__(self, files: AsyncFilesResource) -> None: + self._files = files + + self.create = async_to_raw_response_wrapper( + files.create, + ) + self.retrieve = async_to_raw_response_wrapper( + files.retrieve, + ) + self.list = async_to_raw_response_wrapper( + files.list, + ) + self.delete = async_to_raw_response_wrapper( + files.delete, + ) + + @cached_property + def content(self) -> AsyncContentResourceWithRawResponse: + return AsyncContentResourceWithRawResponse(self._files.content) + + +class FilesResourceWithStreamingResponse: + def __init__(self, files: FilesResource) -> None: + self._files = files + + self.create = to_streamed_response_wrapper( + files.create, + ) + self.retrieve = to_streamed_response_wrapper( + files.retrieve, + ) + self.list = to_streamed_response_wrapper( + files.list, + ) + self.delete = to_streamed_response_wrapper( + files.delete, + ) + + @cached_property + def content(self) -> ContentResourceWithStreamingResponse: + return ContentResourceWithStreamingResponse(self._files.content) + + +class AsyncFilesResourceWithStreamingResponse: + def __init__(self, files: AsyncFilesResource) -> None: + self._files = files + + self.create = async_to_streamed_response_wrapper( + files.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + files.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + files.list, + ) + self.delete = async_to_streamed_response_wrapper( + files.delete, + ) + + @cached_property + def content(self) -> AsyncContentResourceWithStreamingResponse: + return AsyncContentResourceWithStreamingResponse(self._files.content) diff --git a/src/hanzoai/resources/fine_tuning/__init__.py b/src/hanzoai/resources/fine_tuning/__init__.py new file mode 100644 index 000000000..2423dc870 --- /dev/null +++ b/src/hanzoai/resources/fine_tuning/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .jobs import ( + JobsResource, + AsyncJobsResource, + JobsResourceWithRawResponse, + AsyncJobsResourceWithRawResponse, + JobsResourceWithStreamingResponse, + AsyncJobsResourceWithStreamingResponse, +) +from .fine_tuning import ( + FineTuningResource, + AsyncFineTuningResource, + FineTuningResourceWithRawResponse, + AsyncFineTuningResourceWithRawResponse, + FineTuningResourceWithStreamingResponse, + AsyncFineTuningResourceWithStreamingResponse, +) + +__all__ = [ + "JobsResource", + "AsyncJobsResource", + "JobsResourceWithRawResponse", + "AsyncJobsResourceWithRawResponse", + "JobsResourceWithStreamingResponse", + "AsyncJobsResourceWithStreamingResponse", + "FineTuningResource", + "AsyncFineTuningResource", + "FineTuningResourceWithRawResponse", + "AsyncFineTuningResourceWithRawResponse", + "FineTuningResourceWithStreamingResponse", + "AsyncFineTuningResourceWithStreamingResponse", +] diff --git a/pkg/hanzoai/resources/fine_tuning/fine_tuning.py b/src/hanzoai/resources/fine_tuning/fine_tuning.py similarity index 97% rename from pkg/hanzoai/resources/fine_tuning/fine_tuning.py rename to src/hanzoai/resources/fine_tuning/fine_tuning.py index 767488a9e..49849d2f3 100644 --- a/pkg/hanzoai/resources/fine_tuning/fine_tuning.py +++ b/src/hanzoai/resources/fine_tuning/fine_tuning.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/resources/fine_tuning/jobs/__init__.py b/src/hanzoai/resources/fine_tuning/jobs/__init__.py new file mode 100644 index 000000000..5b507f1b8 --- /dev/null +++ b/src/hanzoai/resources/fine_tuning/jobs/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .jobs import ( + JobsResource, + AsyncJobsResource, + JobsResourceWithRawResponse, + AsyncJobsResourceWithRawResponse, + JobsResourceWithStreamingResponse, + AsyncJobsResourceWithStreamingResponse, +) +from .cancel import ( + CancelResource, + AsyncCancelResource, + CancelResourceWithRawResponse, + AsyncCancelResourceWithRawResponse, + CancelResourceWithStreamingResponse, + AsyncCancelResourceWithStreamingResponse, +) + +__all__ = [ + "CancelResource", + "AsyncCancelResource", + "CancelResourceWithRawResponse", + "AsyncCancelResourceWithRawResponse", + "CancelResourceWithStreamingResponse", + "AsyncCancelResourceWithStreamingResponse", + "JobsResource", + "AsyncJobsResource", + "JobsResourceWithRawResponse", + "AsyncJobsResourceWithRawResponse", + "JobsResourceWithStreamingResponse", + "AsyncJobsResourceWithStreamingResponse", +] diff --git a/src/hanzoai/resources/fine_tuning/jobs/cancel.py b/src/hanzoai/resources/fine_tuning/jobs/cancel.py new file mode 100644 index 000000000..5502db1f5 --- /dev/null +++ b/src/hanzoai/resources/fine_tuning/jobs/cancel.py @@ -0,0 +1,178 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options + +__all__ = ["CancelResource", "AsyncCancelResource"] + + +class CancelResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CancelResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return CancelResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CancelResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return CancelResourceWithStreamingResponse(self) + + def create( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Cancel a fine-tuning job. + + This is the equivalent of POST + https://api.openai.com/v1/fine_tuning/jobs/{fine_tuning_job_id}/cancel + + Supported Query Params: + + - `custom_llm_provider`: Name of the LLM provider + - `fine_tuning_job_id`: The ID of the fine-tuning job to cancel. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return self._post( + f"/v1/fine_tuning/jobs/{fine_tuning_job_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncCancelResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCancelResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncCancelResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCancelResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncCancelResourceWithStreamingResponse(self) + + async def create( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Cancel a fine-tuning job. + + This is the equivalent of POST + https://api.openai.com/v1/fine_tuning/jobs/{fine_tuning_job_id}/cancel + + Supported Query Params: + + - `custom_llm_provider`: Name of the LLM provider + - `fine_tuning_job_id`: The ID of the fine-tuning job to cancel. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return await self._post( + f"/v1/fine_tuning/jobs/{fine_tuning_job_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class CancelResourceWithRawResponse: + def __init__(self, cancel: CancelResource) -> None: + self._cancel = cancel + + self.create = to_raw_response_wrapper( + cancel.create, + ) + + +class AsyncCancelResourceWithRawResponse: + def __init__(self, cancel: AsyncCancelResource) -> None: + self._cancel = cancel + + self.create = async_to_raw_response_wrapper( + cancel.create, + ) + + +class CancelResourceWithStreamingResponse: + def __init__(self, cancel: CancelResource) -> None: + self._cancel = cancel + + self.create = to_streamed_response_wrapper( + cancel.create, + ) + + +class AsyncCancelResourceWithStreamingResponse: + def __init__(self, cancel: AsyncCancelResource) -> None: + self._cancel = cancel + + self.create = async_to_streamed_response_wrapper( + cancel.create, + ) diff --git a/src/hanzoai/resources/fine_tuning/jobs/jobs.py b/src/hanzoai/resources/fine_tuning/jobs/jobs.py new file mode 100644 index 000000000..0da59982f --- /dev/null +++ b/src/hanzoai/resources/fine_tuning/jobs/jobs.py @@ -0,0 +1,491 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx + +from .cancel import ( + CancelResource, + AsyncCancelResource, + CancelResourceWithRawResponse, + AsyncCancelResourceWithRawResponse, + CancelResourceWithStreamingResponse, + AsyncCancelResourceWithStreamingResponse, +) +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.fine_tuning import job_list_params, job_create_params, job_retrieve_params + +__all__ = ["JobsResource", "AsyncJobsResource"] + + +class JobsResource(SyncAPIResource): + @cached_property + def cancel(self) -> CancelResource: + return CancelResource(self._client) + + @cached_property + def with_raw_response(self) -> JobsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return JobsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> JobsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return JobsResourceWithStreamingResponse(self) + + def create( + self, + *, + custom_llm_provider: Literal["openai", "azure", "vertex_ai"], + model: str, + training_file: str, + hyperparameters: Optional[job_create_params.Hyperparameters] | Omit = omit, + integrations: Optional[SequenceNotStr[str]] | Omit = omit, + seed: Optional[int] | Omit = omit, + suffix: Optional[str] | Omit = omit, + validation_file: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Creates a fine-tuning job which begins the process of creating a new model from + a given dataset. This is the equivalent of POST + https://api.openai.com/v1/fine_tuning/jobs + + Supports Identical Params as: + https://platform.openai.com/docs/api-reference/fine-tuning/create + + Example Curl: + + ``` + curl http://localhost:4000/v1/fine_tuning/jobs -H "Content-Type: application/json" -H "Authorization: Bearer sk-1234" -d '{ + "model": "gpt-3.5-turbo", + "training_file": "file-abc123", + "hyperparameters": { + "n_epochs": 4 + } + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v1/fine_tuning/jobs", + body=maybe_transform( + { + "custom_llm_provider": custom_llm_provider, + "model": model, + "training_file": training_file, + "hyperparameters": hyperparameters, + "integrations": integrations, + "seed": seed, + "suffix": suffix, + "validation_file": validation_file, + }, + job_create_params.JobCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def retrieve( + self, + fine_tuning_job_id: str, + *, + custom_llm_provider: Literal["openai", "azure"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Retrieves a fine-tuning job. + + This is the equivalent of GET + https://api.openai.com/v1/fine_tuning/jobs/{fine_tuning_job_id} + + Supported Query Params: + + - `custom_llm_provider`: Name of the LLM provider + - `fine_tuning_job_id`: The ID of the fine-tuning job to retrieve. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return self._get( + f"/v1/fine_tuning/jobs/{fine_tuning_job_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"custom_llm_provider": custom_llm_provider}, job_retrieve_params.JobRetrieveParams + ), + ), + cast_to=object, + ) + + def list( + self, + *, + custom_llm_provider: Literal["openai", "azure"], + after: Optional[str] | Omit = omit, + limit: Optional[int] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Lists fine-tuning jobs for the organization. + + This is the equivalent of GET + https://api.openai.com/v1/fine_tuning/jobs + + Supported Query Params: + + - `custom_llm_provider`: Name of the LLM provider + - `after`: Identifier for the last job from the previous pagination request. + - `limit`: Number of fine-tuning jobs to retrieve (default is 20). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v1/fine_tuning/jobs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "custom_llm_provider": custom_llm_provider, + "after": after, + "limit": limit, + }, + job_list_params.JobListParams, + ), + ), + cast_to=object, + ) + + +class AsyncJobsResource(AsyncAPIResource): + @cached_property + def cancel(self) -> AsyncCancelResource: + return AsyncCancelResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncJobsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncJobsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncJobsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncJobsResourceWithStreamingResponse(self) + + async def create( + self, + *, + custom_llm_provider: Literal["openai", "azure", "vertex_ai"], + model: str, + training_file: str, + hyperparameters: Optional[job_create_params.Hyperparameters] | Omit = omit, + integrations: Optional[SequenceNotStr[str]] | Omit = omit, + seed: Optional[int] | Omit = omit, + suffix: Optional[str] | Omit = omit, + validation_file: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Creates a fine-tuning job which begins the process of creating a new model from + a given dataset. This is the equivalent of POST + https://api.openai.com/v1/fine_tuning/jobs + + Supports Identical Params as: + https://platform.openai.com/docs/api-reference/fine-tuning/create + + Example Curl: + + ``` + curl http://localhost:4000/v1/fine_tuning/jobs -H "Content-Type: application/json" -H "Authorization: Bearer sk-1234" -d '{ + "model": "gpt-3.5-turbo", + "training_file": "file-abc123", + "hyperparameters": { + "n_epochs": 4 + } + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v1/fine_tuning/jobs", + body=await async_maybe_transform( + { + "custom_llm_provider": custom_llm_provider, + "model": model, + "training_file": training_file, + "hyperparameters": hyperparameters, + "integrations": integrations, + "seed": seed, + "suffix": suffix, + "validation_file": validation_file, + }, + job_create_params.JobCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def retrieve( + self, + fine_tuning_job_id: str, + *, + custom_llm_provider: Literal["openai", "azure"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Retrieves a fine-tuning job. + + This is the equivalent of GET + https://api.openai.com/v1/fine_tuning/jobs/{fine_tuning_job_id} + + Supported Query Params: + + - `custom_llm_provider`: Name of the LLM provider + - `fine_tuning_job_id`: The ID of the fine-tuning job to retrieve. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return await self._get( + f"/v1/fine_tuning/jobs/{fine_tuning_job_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"custom_llm_provider": custom_llm_provider}, job_retrieve_params.JobRetrieveParams + ), + ), + cast_to=object, + ) + + async def list( + self, + *, + custom_llm_provider: Literal["openai", "azure"], + after: Optional[str] | Omit = omit, + limit: Optional[int] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Lists fine-tuning jobs for the organization. + + This is the equivalent of GET + https://api.openai.com/v1/fine_tuning/jobs + + Supported Query Params: + + - `custom_llm_provider`: Name of the LLM provider + - `after`: Identifier for the last job from the previous pagination request. + - `limit`: Number of fine-tuning jobs to retrieve (default is 20). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v1/fine_tuning/jobs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "custom_llm_provider": custom_llm_provider, + "after": after, + "limit": limit, + }, + job_list_params.JobListParams, + ), + ), + cast_to=object, + ) + + +class JobsResourceWithRawResponse: + def __init__(self, jobs: JobsResource) -> None: + self._jobs = jobs + + self.create = to_raw_response_wrapper( + jobs.create, + ) + self.retrieve = to_raw_response_wrapper( + jobs.retrieve, + ) + self.list = to_raw_response_wrapper( + jobs.list, + ) + + @cached_property + def cancel(self) -> CancelResourceWithRawResponse: + return CancelResourceWithRawResponse(self._jobs.cancel) + + +class AsyncJobsResourceWithRawResponse: + def __init__(self, jobs: AsyncJobsResource) -> None: + self._jobs = jobs + + self.create = async_to_raw_response_wrapper( + jobs.create, + ) + self.retrieve = async_to_raw_response_wrapper( + jobs.retrieve, + ) + self.list = async_to_raw_response_wrapper( + jobs.list, + ) + + @cached_property + def cancel(self) -> AsyncCancelResourceWithRawResponse: + return AsyncCancelResourceWithRawResponse(self._jobs.cancel) + + +class JobsResourceWithStreamingResponse: + def __init__(self, jobs: JobsResource) -> None: + self._jobs = jobs + + self.create = to_streamed_response_wrapper( + jobs.create, + ) + self.retrieve = to_streamed_response_wrapper( + jobs.retrieve, + ) + self.list = to_streamed_response_wrapper( + jobs.list, + ) + + @cached_property + def cancel(self) -> CancelResourceWithStreamingResponse: + return CancelResourceWithStreamingResponse(self._jobs.cancel) + + +class AsyncJobsResourceWithStreamingResponse: + def __init__(self, jobs: AsyncJobsResource) -> None: + self._jobs = jobs + + self.create = async_to_streamed_response_wrapper( + jobs.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + jobs.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + jobs.list, + ) + + @cached_property + def cancel(self) -> AsyncCancelResourceWithStreamingResponse: + return AsyncCancelResourceWithStreamingResponse(self._jobs.cancel) diff --git a/pkg/hanzoai/resources/gemini.py b/src/hanzoai/resources/gemini.py similarity index 80% rename from pkg/hanzoai/resources/gemini.py rename to src/hanzoai/resources/gemini.py index 942748015..233183b2d 100644 --- a/pkg/hanzoai/resources/gemini.py +++ b/src/hanzoai/resources/gemini.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -47,7 +47,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/google_ai_studio) @@ -62,16 +62,11 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._post( f"/gemini/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -85,7 +80,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/google_ai_studio) @@ -100,16 +95,11 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._get( f"/gemini/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -123,7 +113,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/google_ai_studio) @@ -138,16 +128,11 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._put( f"/gemini/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -161,7 +146,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/google_ai_studio) @@ -176,16 +161,11 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._delete( f"/gemini/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -199,7 +179,7 @@ def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/google_ai_studio) @@ -214,16 +194,11 @@ def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._patch( f"/gemini/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -258,7 +233,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/google_ai_studio) @@ -273,16 +248,11 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._post( f"/gemini/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -296,7 +266,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/google_ai_studio) @@ -311,16 +281,11 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._get( f"/gemini/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -334,7 +299,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/google_ai_studio) @@ -349,16 +314,11 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._put( f"/gemini/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -372,7 +332,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/google_ai_studio) @@ -387,16 +347,11 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._delete( f"/gemini/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -410,7 +365,7 @@ async def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [Docs](https://docs.hanzo.ai/docs/pass_through/google_ai_studio) @@ -425,16 +380,11 @@ async def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._patch( f"/gemini/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/global_/__init__.py b/src/hanzoai/resources/global_/__init__.py new file mode 100644 index 000000000..0c0bcda71 --- /dev/null +++ b/src/hanzoai/resources/global_/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .spend import ( + SpendResource, + AsyncSpendResource, + SpendResourceWithRawResponse, + AsyncSpendResourceWithRawResponse, + SpendResourceWithStreamingResponse, + AsyncSpendResourceWithStreamingResponse, +) +from .global_ import ( + GlobalResource, + AsyncGlobalResource, + GlobalResourceWithRawResponse, + AsyncGlobalResourceWithRawResponse, + GlobalResourceWithStreamingResponse, + AsyncGlobalResourceWithStreamingResponse, +) + +__all__ = [ + "SpendResource", + "AsyncSpendResource", + "SpendResourceWithRawResponse", + "AsyncSpendResourceWithRawResponse", + "SpendResourceWithStreamingResponse", + "AsyncSpendResourceWithStreamingResponse", + "GlobalResource", + "AsyncGlobalResource", + "GlobalResourceWithRawResponse", + "AsyncGlobalResourceWithRawResponse", + "GlobalResourceWithStreamingResponse", + "AsyncGlobalResourceWithStreamingResponse", +] diff --git a/pkg/hanzoai/resources/global_/global_.py b/src/hanzoai/resources/global_/global_.py similarity index 97% rename from pkg/hanzoai/resources/global_/global_.py rename to src/hanzoai/resources/global_/global_.py index 004c71293..b97840a59 100644 --- a/pkg/hanzoai/resources/global_/global_.py +++ b/src/hanzoai/resources/global_/global_.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/resources/global_/spend.py b/src/hanzoai/resources/global_/spend.py new file mode 100644 index 000000000..ce1ce1ed9 --- /dev/null +++ b/src/hanzoai/resources/global_/spend.py @@ -0,0 +1,455 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.global_ import spend_list_tags_params, spend_retrieve_report_params +from ...types.global_.spend_list_tags_response import SpendListTagsResponse +from ...types.global_.spend_retrieve_report_response import SpendRetrieveReportResponse + +__all__ = ["SpendResource", "AsyncSpendResource"] + + +class SpendResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SpendResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return SpendResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SpendResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return SpendResourceWithStreamingResponse(self) + + def list_tags( + self, + *, + end_date: Optional[str] | Omit = omit, + start_date: Optional[str] | Omit = omit, + tags: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SpendListTagsResponse: + """LLM Enterprise - View Spend Per Request Tag. + + Used by LLM UI + + Example Request: + + ``` + curl -X GET "http://0.0.0.0:4000/spend/tags" -H "Authorization: Bearer sk-1234" + ``` + + Spend with Start Date and End Date + + ``` + curl -X GET "http://0.0.0.0:4000/spend/tags?start_date=2022-01-01&end_date=2022-02-01" -H "Authorization: Bearer sk-1234" + ``` + + Args: + end_date: Time till which to view key spend + + start_date: Time from which to start viewing key spend + + tags: comman separated tags to filter on + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/global/spend/tags", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "end_date": end_date, + "start_date": start_date, + "tags": tags, + }, + spend_list_tags_params.SpendListTagsParams, + ), + ), + cast_to=SpendListTagsResponse, + ) + + def reset( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + ADMIN ONLY / MASTER KEY Only Endpoint + + Globally reset spend for All API Keys and Teams, maintain LLM_SpendLogs + + 1. LLM_SpendLogs will maintain the logs on spend, no data gets deleted from + there + 2. LLM_VerificationTokens spend will be set = 0 + 3. LLM_TeamTable spend will be set = 0 + """ + return self._post( + "/global/spend/reset", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def retrieve_report( + self, + *, + api_key: Optional[str] | Omit = omit, + customer_id: Optional[str] | Omit = omit, + end_date: Optional[str] | Omit = omit, + group_by: Optional[Literal["team", "customer", "api_key"]] | Omit = omit, + internal_user_id: Optional[str] | Omit = omit, + start_date: Optional[str] | Omit = omit, + team_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SpendRetrieveReportResponse: + """Get Daily Spend per Team, based on specific startTime and endTime. + + Per team, + view usage by each key, model [ { "group-by-day": "2024-05-10", "teams": [ { + "team_name": "team-1" "spend": 10, "keys": [ "key": "1213", "usage": { + "model-1": { "cost": 12.50, "input_tokens": 1000, "output_tokens": 5000, + "requests": 100 }, "audio-modelname1": { "cost": 25.50, "seconds": 25, + "requests": 50 }, } } ] ] } + + Args: + api_key: View spend for a specific api_key. Example api_key='sk-1234 + + customer_id: View spend for a specific customer_id. Example customer_id='1234. Can be used in + conjunction with team_id as well. + + end_date: Time till which to view spend + + group_by: Group spend by internal team or customer or api_key + + internal_user_id: View spend for a specific internal_user_id. Example internal_user_id='1234 + + start_date: Time from which to start viewing spend + + team_id: View spend for a specific team_id. Example team_id='1234 + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/global/spend/report", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "api_key": api_key, + "customer_id": customer_id, + "end_date": end_date, + "group_by": group_by, + "internal_user_id": internal_user_id, + "start_date": start_date, + "team_id": team_id, + }, + spend_retrieve_report_params.SpendRetrieveReportParams, + ), + ), + cast_to=SpendRetrieveReportResponse, + ) + + +class AsyncSpendResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSpendResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncSpendResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSpendResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncSpendResourceWithStreamingResponse(self) + + async def list_tags( + self, + *, + end_date: Optional[str] | Omit = omit, + start_date: Optional[str] | Omit = omit, + tags: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SpendListTagsResponse: + """LLM Enterprise - View Spend Per Request Tag. + + Used by LLM UI + + Example Request: + + ``` + curl -X GET "http://0.0.0.0:4000/spend/tags" -H "Authorization: Bearer sk-1234" + ``` + + Spend with Start Date and End Date + + ``` + curl -X GET "http://0.0.0.0:4000/spend/tags?start_date=2022-01-01&end_date=2022-02-01" -H "Authorization: Bearer sk-1234" + ``` + + Args: + end_date: Time till which to view key spend + + start_date: Time from which to start viewing key spend + + tags: comman separated tags to filter on + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/global/spend/tags", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "end_date": end_date, + "start_date": start_date, + "tags": tags, + }, + spend_list_tags_params.SpendListTagsParams, + ), + ), + cast_to=SpendListTagsResponse, + ) + + async def reset( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + ADMIN ONLY / MASTER KEY Only Endpoint + + Globally reset spend for All API Keys and Teams, maintain LLM_SpendLogs + + 1. LLM_SpendLogs will maintain the logs on spend, no data gets deleted from + there + 2. LLM_VerificationTokens spend will be set = 0 + 3. LLM_TeamTable spend will be set = 0 + """ + return await self._post( + "/global/spend/reset", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def retrieve_report( + self, + *, + api_key: Optional[str] | Omit = omit, + customer_id: Optional[str] | Omit = omit, + end_date: Optional[str] | Omit = omit, + group_by: Optional[Literal["team", "customer", "api_key"]] | Omit = omit, + internal_user_id: Optional[str] | Omit = omit, + start_date: Optional[str] | Omit = omit, + team_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SpendRetrieveReportResponse: + """Get Daily Spend per Team, based on specific startTime and endTime. + + Per team, + view usage by each key, model [ { "group-by-day": "2024-05-10", "teams": [ { + "team_name": "team-1" "spend": 10, "keys": [ "key": "1213", "usage": { + "model-1": { "cost": 12.50, "input_tokens": 1000, "output_tokens": 5000, + "requests": 100 }, "audio-modelname1": { "cost": 25.50, "seconds": 25, + "requests": 50 }, } } ] ] } + + Args: + api_key: View spend for a specific api_key. Example api_key='sk-1234 + + customer_id: View spend for a specific customer_id. Example customer_id='1234. Can be used in + conjunction with team_id as well. + + end_date: Time till which to view spend + + group_by: Group spend by internal team or customer or api_key + + internal_user_id: View spend for a specific internal_user_id. Example internal_user_id='1234 + + start_date: Time from which to start viewing spend + + team_id: View spend for a specific team_id. Example team_id='1234 + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/global/spend/report", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "api_key": api_key, + "customer_id": customer_id, + "end_date": end_date, + "group_by": group_by, + "internal_user_id": internal_user_id, + "start_date": start_date, + "team_id": team_id, + }, + spend_retrieve_report_params.SpendRetrieveReportParams, + ), + ), + cast_to=SpendRetrieveReportResponse, + ) + + +class SpendResourceWithRawResponse: + def __init__(self, spend: SpendResource) -> None: + self._spend = spend + + self.list_tags = to_raw_response_wrapper( + spend.list_tags, + ) + self.reset = to_raw_response_wrapper( + spend.reset, + ) + self.retrieve_report = to_raw_response_wrapper( + spend.retrieve_report, + ) + + +class AsyncSpendResourceWithRawResponse: + def __init__(self, spend: AsyncSpendResource) -> None: + self._spend = spend + + self.list_tags = async_to_raw_response_wrapper( + spend.list_tags, + ) + self.reset = async_to_raw_response_wrapper( + spend.reset, + ) + self.retrieve_report = async_to_raw_response_wrapper( + spend.retrieve_report, + ) + + +class SpendResourceWithStreamingResponse: + def __init__(self, spend: SpendResource) -> None: + self._spend = spend + + self.list_tags = to_streamed_response_wrapper( + spend.list_tags, + ) + self.reset = to_streamed_response_wrapper( + spend.reset, + ) + self.retrieve_report = to_streamed_response_wrapper( + spend.retrieve_report, + ) + + +class AsyncSpendResourceWithStreamingResponse: + def __init__(self, spend: AsyncSpendResource) -> None: + self._spend = spend + + self.list_tags = async_to_streamed_response_wrapper( + spend.list_tags, + ) + self.reset = async_to_streamed_response_wrapper( + spend.reset, + ) + self.retrieve_report = async_to_streamed_response_wrapper( + spend.retrieve_report, + ) diff --git a/pkg/hanzoai/resources/guardrails.py b/src/hanzoai/resources/guardrails.py similarity index 92% rename from pkg/hanzoai/resources/guardrails.py rename to src/hanzoai/resources/guardrails.py index 1fb23d21d..c1f361b92 100644 --- a/pkg/hanzoai/resources/guardrails.py +++ b/src/hanzoai/resources/guardrails.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -47,7 +47,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> GuardrailListResponse: """ List the guardrails that are available on the proxy server @@ -88,10 +88,7 @@ def list( return self._get( "/guardrails/list", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=GuardrailListResponse, ) @@ -125,7 +122,7 @@ async def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> GuardrailListResponse: """ List the guardrails that are available on the proxy server @@ -166,10 +163,7 @@ async def list( return await self._get( "/guardrails/list", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=GuardrailListResponse, ) diff --git a/src/hanzoai/resources/health.py b/src/hanzoai/resources/health.py new file mode 100644 index 000000000..6d912d9d7 --- /dev/null +++ b/src/hanzoai/resources/health.py @@ -0,0 +1,463 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal + +import httpx + +from ..types import health_check_all_params, health_check_services_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options + +__all__ = ["HealthResource", "AsyncHealthResource"] + + +class HealthResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> HealthResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return HealthResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> HealthResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return HealthResourceWithStreamingResponse(self) + + def check_all( + self, + *, + model: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + ๐Ÿšจ USE `/health/liveliness` to health check the proxy ๐Ÿšจ + + See more ๐Ÿ‘‰ https://docs.hanzo.ai/docs/proxy/health + + Check the health of all the endpoints in config.yaml + + To run health checks in the background, add this to config.yaml: + + ``` + general_settings: + # ... other settings + background_health_checks: True + ``` + + else, the health checks will be run on models when /health is called. + + Args: + model: Specify the model name (optional) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/health", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"model": model}, health_check_all_params.HealthCheckAllParams), + ), + cast_to=object, + ) + + def check_liveliness( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Unprotected endpoint for checking if worker is alive""" + return self._get( + "/health/liveliness", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def check_liveness( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Unprotected endpoint for checking if worker is alive""" + return self._get( + "/health/liveness", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def check_readiness( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Unprotected endpoint for checking if worker can receive requests""" + return self._get( + "/health/readiness", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def check_services( + self, + *, + service: Union[ + Literal[ + "slack_budget_alerts", "langfuse", "slack", "openmeter", "webhook", "email", "braintrust", "datadog" + ], + str, + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Use this admin-only endpoint to check if the service is healthy. + + Example: + + ``` + curl -L -X GET 'http://0.0.0.0:4000/health/services?service=datadog' -H 'Authorization: Bearer sk-1234' + ``` + + Args: + service: Specify the service being hit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/health/services", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"service": service}, health_check_services_params.HealthCheckServicesParams), + ), + cast_to=object, + ) + + +class AsyncHealthResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncHealthResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncHealthResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncHealthResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncHealthResourceWithStreamingResponse(self) + + async def check_all( + self, + *, + model: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + ๐Ÿšจ USE `/health/liveliness` to health check the proxy ๐Ÿšจ + + See more ๐Ÿ‘‰ https://docs.hanzo.ai/docs/proxy/health + + Check the health of all the endpoints in config.yaml + + To run health checks in the background, add this to config.yaml: + + ``` + general_settings: + # ... other settings + background_health_checks: True + ``` + + else, the health checks will be run on models when /health is called. + + Args: + model: Specify the model name (optional) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/health", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"model": model}, health_check_all_params.HealthCheckAllParams), + ), + cast_to=object, + ) + + async def check_liveliness( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Unprotected endpoint for checking if worker is alive""" + return await self._get( + "/health/liveliness", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def check_liveness( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Unprotected endpoint for checking if worker is alive""" + return await self._get( + "/health/liveness", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def check_readiness( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Unprotected endpoint for checking if worker can receive requests""" + return await self._get( + "/health/readiness", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def check_services( + self, + *, + service: Union[ + Literal[ + "slack_budget_alerts", "langfuse", "slack", "openmeter", "webhook", "email", "braintrust", "datadog" + ], + str, + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Use this admin-only endpoint to check if the service is healthy. + + Example: + + ``` + curl -L -X GET 'http://0.0.0.0:4000/health/services?service=datadog' -H 'Authorization: Bearer sk-1234' + ``` + + Args: + service: Specify the service being hit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/health/services", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"service": service}, health_check_services_params.HealthCheckServicesParams + ), + ), + cast_to=object, + ) + + +class HealthResourceWithRawResponse: + def __init__(self, health: HealthResource) -> None: + self._health = health + + self.check_all = to_raw_response_wrapper( + health.check_all, + ) + self.check_liveliness = to_raw_response_wrapper( + health.check_liveliness, + ) + self.check_liveness = to_raw_response_wrapper( + health.check_liveness, + ) + self.check_readiness = to_raw_response_wrapper( + health.check_readiness, + ) + self.check_services = to_raw_response_wrapper( + health.check_services, + ) + + +class AsyncHealthResourceWithRawResponse: + def __init__(self, health: AsyncHealthResource) -> None: + self._health = health + + self.check_all = async_to_raw_response_wrapper( + health.check_all, + ) + self.check_liveliness = async_to_raw_response_wrapper( + health.check_liveliness, + ) + self.check_liveness = async_to_raw_response_wrapper( + health.check_liveness, + ) + self.check_readiness = async_to_raw_response_wrapper( + health.check_readiness, + ) + self.check_services = async_to_raw_response_wrapper( + health.check_services, + ) + + +class HealthResourceWithStreamingResponse: + def __init__(self, health: HealthResource) -> None: + self._health = health + + self.check_all = to_streamed_response_wrapper( + health.check_all, + ) + self.check_liveliness = to_streamed_response_wrapper( + health.check_liveliness, + ) + self.check_liveness = to_streamed_response_wrapper( + health.check_liveness, + ) + self.check_readiness = to_streamed_response_wrapper( + health.check_readiness, + ) + self.check_services = to_streamed_response_wrapper( + health.check_services, + ) + + +class AsyncHealthResourceWithStreamingResponse: + def __init__(self, health: AsyncHealthResource) -> None: + self._health = health + + self.check_all = async_to_streamed_response_wrapper( + health.check_all, + ) + self.check_liveliness = async_to_streamed_response_wrapper( + health.check_liveliness, + ) + self.check_liveness = async_to_streamed_response_wrapper( + health.check_liveness, + ) + self.check_readiness = async_to_streamed_response_wrapper( + health.check_readiness, + ) + self.check_services = async_to_streamed_response_wrapper( + health.check_services, + ) diff --git a/src/hanzoai/resources/images/__init__.py b/src/hanzoai/resources/images/__init__.py new file mode 100644 index 000000000..cf187f1d3 --- /dev/null +++ b/src/hanzoai/resources/images/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .images import ( + ImagesResource, + AsyncImagesResource, + ImagesResourceWithRawResponse, + AsyncImagesResourceWithRawResponse, + ImagesResourceWithStreamingResponse, + AsyncImagesResourceWithStreamingResponse, +) +from .generations import ( + GenerationsResource, + AsyncGenerationsResource, + GenerationsResourceWithRawResponse, + AsyncGenerationsResourceWithRawResponse, + GenerationsResourceWithStreamingResponse, + AsyncGenerationsResourceWithStreamingResponse, +) + +__all__ = [ + "GenerationsResource", + "AsyncGenerationsResource", + "GenerationsResourceWithRawResponse", + "AsyncGenerationsResourceWithRawResponse", + "GenerationsResourceWithStreamingResponse", + "AsyncGenerationsResourceWithStreamingResponse", + "ImagesResource", + "AsyncImagesResource", + "ImagesResourceWithRawResponse", + "AsyncImagesResourceWithRawResponse", + "ImagesResourceWithStreamingResponse", + "AsyncImagesResourceWithStreamingResponse", +] diff --git a/pkg/hanzoai/resources/images/generations.py b/src/hanzoai/resources/images/generations.py similarity index 89% rename from pkg/hanzoai/resources/images/generations.py rename to src/hanzoai/resources/images/generations.py index 31675574c..830a1a8f0 100644 --- a/pkg/hanzoai/resources/images/generations.py +++ b/src/hanzoai/resources/images/generations.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Query, Headers, NotGiven, not_given from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -46,16 +46,13 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Image Generation""" return self._post( "/v1/images/generations", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -89,16 +86,13 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Image Generation""" return await self._post( "/v1/images/generations", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/pkg/hanzoai/resources/images/images.py b/src/hanzoai/resources/images/images.py similarity index 97% rename from pkg/hanzoai/resources/images/images.py rename to src/hanzoai/resources/images/images.py index 486d2e661..f42a3cfeb 100644 --- a/pkg/hanzoai/resources/images/images.py +++ b/src/hanzoai/resources/images/images.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/resources/key/__init__.py b/src/hanzoai/resources/key/__init__.py new file mode 100644 index 000000000..31a09fcfc --- /dev/null +++ b/src/hanzoai/resources/key/__init__.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .key import ( + KeyResource, + AsyncKeyResource, + KeyResourceWithRawResponse, + AsyncKeyResourceWithRawResponse, + KeyResourceWithStreamingResponse, + AsyncKeyResourceWithStreamingResponse, +) + +__all__ = [ + "KeyResource", + "AsyncKeyResource", + "KeyResourceWithRawResponse", + "AsyncKeyResourceWithRawResponse", + "KeyResourceWithStreamingResponse", + "AsyncKeyResourceWithStreamingResponse", +] diff --git a/src/hanzoai/resources/key/key.py b/src/hanzoai/resources/key/key.py new file mode 100644 index 000000000..438346b56 --- /dev/null +++ b/src/hanzoai/resources/key/key.py @@ -0,0 +1,1874 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from datetime import datetime + +import httpx + +from ...types import ( + key_list_params, + key_block_params, + key_delete_params, + key_update_params, + key_unblock_params, + key_generate_params, + key_retrieve_info_params, + key_regenerate_by_key_params, +) +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import maybe_transform, strip_not_given, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.key_list_response import KeyListResponse +from ...types.key_block_response import KeyBlockResponse +from ...types.generate_key_response import GenerateKeyResponse +from ...types.key_check_health_response import KeyCheckHealthResponse + +__all__ = ["KeyResource", "AsyncKeyResource"] + + +class KeyResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> KeyResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return KeyResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> KeyResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return KeyResourceWithStreamingResponse(self) + + def update( + self, + *, + key: str, + aliases: Optional[object] | Omit = omit, + allowed_cache_controls: Optional[Iterable[object]] | Omit = omit, + blocked: Optional[bool] | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + budget_id: Optional[str] | Omit = omit, + config: Optional[object] | Omit = omit, + duration: Optional[str] | Omit = omit, + enforced_params: Optional[SequenceNotStr[str]] | Omit = omit, + guardrails: Optional[SequenceNotStr[str]] | Omit = omit, + key_alias: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_max_budget: Optional[object] | Omit = omit, + model_rpm_limit: Optional[object] | Omit = omit, + model_tpm_limit: Optional[object] | Omit = omit, + models: Optional[Iterable[object]] | Omit = omit, + permissions: Optional[object] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + spend: Optional[float] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + team_id: Optional[str] | Omit = omit, + temp_budget_expiry: Union[str, datetime, None] | Omit = omit, + temp_budget_increase: Optional[float] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, + user_id: Optional[str] | Omit = omit, + llm_changed_by: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Update an existing API key's parameters. + + Parameters: + + - key: str - The key to update + - key_alias: Optional[str] - User-friendly key alias + - user_id: Optional[str] - User ID associated with key + - team_id: Optional[str] - Team ID associated with key + - budget_id: Optional[str] - The budget id associated with the key. Created by + calling `/budget/new`. + - models: Optional[list] - Model_name's a user is allowed to call + - tags: Optional[List[str]] - Tags for organizing keys (Enterprise only) + - enforced_params: Optional[List[str]] - List of enforced params for the key + (Enterprise only). + [Docs](https://docs.hanzo.ai/docs/proxy/enterprise#enforce-required-params-for-llm-requests) + - spend: Optional[float] - Amount spent by key + - max_budget: Optional[float] - Max budget for key + - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets + {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}} + - budget_duration: Optional[str] - Budget reset period ("30d", "1h", etc.) + - soft_budget: Optional[float] - [TODO] Soft budget limit (warning vs. hard + stop). Will trigger a slack alert when this soft budget is reached. + - max_parallel_requests: Optional[int] - Rate limit for parallel requests + - metadata: Optional[dict] - Metadata for key. Example {"team": "core-infra", + "app": "app2"} + - tpm_limit: Optional[int] - Tokens per minute limit + - rpm_limit: Optional[int] - Requests per minute limit + - model_rpm_limit: Optional[dict] - Model-specific RPM limits {"gpt-4": 100, + "claude-v1": 200} + - model_tpm_limit: Optional[dict] - Model-specific TPM limits {"gpt-4": 100000, + "claude-v1": 200000} + - allowed_cache_controls: Optional[list] - List of allowed cache control values + - duration: Optional[str] - Key validity duration ("30d", "1h", etc.) + - permissions: Optional[dict] - Key-specific permissions + - send_invite_email: Optional[bool] - Send invite email to user_id + - guardrails: Optional[List[str]] - List of active guardrails for the key + - blocked: Optional[bool] - Whether the key is blocked + - aliases: Optional[dict] - Model aliases for the key - + [Docs](https://llm.vercel.app/docs/proxy/virtual_keys#model-aliases) + - config: Optional[dict] - [DEPRECATED PARAM] Key-specific config. + - temp_budget_increase: Optional[float] - Temporary budget increase for the key + (Enterprise only). + - temp_budget_expiry: Optional[str] - Expiry time for the temporary budget + increase (Enterprise only). + + Example: + + ```bash + curl --location 'http://0.0.0.0:4000/key/update' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ + "key": "sk-1234", + "key_alias": "my-key", + "user_id": "user-1234", + "team_id": "team-1234", + "max_budget": 100, + "metadata": {"any_key": "any-val"}, + }' + ``` + + Args: + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} + return self._post( + "/key/update", + body=maybe_transform( + { + "key": key, + "aliases": aliases, + "allowed_cache_controls": allowed_cache_controls, + "blocked": blocked, + "budget_duration": budget_duration, + "budget_id": budget_id, + "config": config, + "duration": duration, + "enforced_params": enforced_params, + "guardrails": guardrails, + "key_alias": key_alias, + "max_budget": max_budget, + "max_parallel_requests": max_parallel_requests, + "metadata": metadata, + "model_max_budget": model_max_budget, + "model_rpm_limit": model_rpm_limit, + "model_tpm_limit": model_tpm_limit, + "models": models, + "permissions": permissions, + "rpm_limit": rpm_limit, + "spend": spend, + "tags": tags, + "team_id": team_id, + "temp_budget_expiry": temp_budget_expiry, + "temp_budget_increase": temp_budget_increase, + "tpm_limit": tpm_limit, + "user_id": user_id, + }, + key_update_params.KeyUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def list( + self, + *, + include_team_keys: bool | Omit = omit, + key_alias: Optional[str] | Omit = omit, + organization_id: Optional[str] | Omit = omit, + page: int | Omit = omit, + return_full_object: bool | Omit = omit, + size: int | Omit = omit, + team_id: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyListResponse: + """ + List all keys for a given user / team / organization. + + Returns: { "keys": List[str] or List[UserAPIKeyAuth], "total_count": int, + "current_page": int, "total_pages": int, } + + Args: + include_team_keys: Include all keys for teams that user is an admin of. + + key_alias: Filter keys by key alias + + organization_id: Filter keys by organization ID + + page: Page number + + return_full_object: Return full key object + + size: Page size + + team_id: Filter keys by team ID + + user_id: Filter keys by user ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/key/list", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "include_team_keys": include_team_keys, + "key_alias": key_alias, + "organization_id": organization_id, + "page": page, + "return_full_object": return_full_object, + "size": size, + "team_id": team_id, + "user_id": user_id, + }, + key_list_params.KeyListParams, + ), + ), + cast_to=KeyListResponse, + ) + + def delete( + self, + *, + key_aliases: Optional[SequenceNotStr[str]] | Omit = omit, + keys: Optional[SequenceNotStr[str]] | Omit = omit, + llm_changed_by: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete a key from the key management system. + + Parameters:: + + - keys (List[str]): A list of keys or hashed keys to delete. Example {"keys": + ["sk-QWrxEynunsNpV1zT48HIrw", + "837e17519f44683334df5291321d97b8bf1098cd490e49e215f6fea935aa28be"]} + - key_aliases (List[str]): A list of key aliases to delete. Can be passed + instead of `keys`.Example {"key_aliases": ["alias1", "alias2"]} + + Returns: + + - deleted_keys (List[str]): A list of deleted keys. Example {"deleted_keys": + ["sk-QWrxEynunsNpV1zT48HIrw", + "837e17519f44683334df5291321d97b8bf1098cd490e49e215f6fea935aa28be"]} + + Example: + + ```bash + curl --location 'http://0.0.0.0:4000/key/delete' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ + "keys": ["sk-QWrxEynunsNpV1zT48HIrw"] + }' + ``` + + Raises: HTTPException: If an error occurs during key deletion. + + Args: + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} + return self._post( + "/key/delete", + body=maybe_transform( + { + "key_aliases": key_aliases, + "keys": keys, + }, + key_delete_params.KeyDeleteParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def block( + self, + *, + key: str, + llm_changed_by: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Optional[KeyBlockResponse]: + """ + Block an Virtual key from making any requests. + + Parameters: + + - key: str - The key to block. Can be either the unhashed key (sk-...) or the + hashed key value + + Example: + + ```bash + curl --location 'http://0.0.0.0:4000/key/block' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ + "key": "sk-Fn8Ej39NxjAXrvpUGKghGw" + }' + ``` + + Note: This is an admin-only endpoint. Only proxy admins can block keys. + + Args: + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} + return self._post( + "/key/block", + body=maybe_transform({"key": key}, key_block_params.KeyBlockParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyBlockResponse, + ) + + def check_health( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyCheckHealthResponse: + """ + Check the health of the key + + Checks: + + - If key based logging is configured correctly - sends a test log + + Usage + + Pass the key in the request header + + ```bash + curl -X POST "http://localhost:4000/key/health" -H "Authorization: Bearer sk-1234" -H "Content-Type: application/json" + ``` + + Response when logging callbacks are setup correctly: + + ```json + { + "key": "healthy", + "logging_callbacks": { + "callbacks": ["gcs_bucket"], + "status": "healthy", + "details": "No logger exceptions triggered, system is healthy. Manually check if logs were sent to ['gcs_bucket']" + } + } + ``` + + Response when logging callbacks are not setup correctly: + + ```json + { + "key": "unhealthy", + "logging_callbacks": { + "callbacks": ["gcs_bucket"], + "status": "unhealthy", + "details": "Logger exceptions triggered, system is unhealthy: Failed to load vertex credentials. Check to see if credentials containing partial/invalid information." + } + } + ``` + """ + return self._post( + "/key/health", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyCheckHealthResponse, + ) + + def generate( + self, + *, + aliases: Optional[object] | Omit = omit, + allowed_cache_controls: Optional[Iterable[object]] | Omit = omit, + blocked: Optional[bool] | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + budget_id: Optional[str] | Omit = omit, + config: Optional[object] | Omit = omit, + duration: Optional[str] | Omit = omit, + enforced_params: Optional[SequenceNotStr[str]] | Omit = omit, + guardrails: Optional[SequenceNotStr[str]] | Omit = omit, + key: Optional[str] | Omit = omit, + key_alias: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_max_budget: Optional[object] | Omit = omit, + model_rpm_limit: Optional[object] | Omit = omit, + model_tpm_limit: Optional[object] | Omit = omit, + models: Optional[Iterable[object]] | Omit = omit, + permissions: Optional[object] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + send_invite_email: Optional[bool] | Omit = omit, + soft_budget: Optional[float] | Omit = omit, + spend: Optional[float] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + team_id: Optional[str] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, + user_id: Optional[str] | Omit = omit, + llm_changed_by: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GenerateKeyResponse: + """ + Generate an API key based on the provided data. + + Docs: https://docs.hanzo.ai/docs/proxy/virtual_keys + + Parameters: + + - duration: Optional[str] - Specify the length of time the token is valid for. + You can set duration as seconds ("30s"), minutes ("30m"), hours ("30h"), days + ("30d"). + - key_alias: Optional[str] - User defined key alias + - key: Optional[str] - User defined key value. If not set, a 16-digit unique + sk-key is created for you. + - team_id: Optional[str] - The team id of the key + - user_id: Optional[str] - The user id of the key + - budget_id: Optional[str] - The budget id associated with the key. Created by + calling `/budget/new`. + - models: Optional[list] - Model_name's a user is allowed to call. (if empty, + key is allowed to call all models) + - aliases: Optional[dict] - Any alias mappings, on top of anything in the + config.yaml model list. - + https://docs.hanzo.ai/docs/proxy/virtual_keys#managing-auth---upgradedowngrade-models + - config: Optional[dict] - any key-specific configs, overrides config in + config.yaml + - spend: Optional[int] - Amount spent by key. Default is 0. Will be updated by + proxy whenever key is used. + https://docs.hanzo.ai/docs/proxy/virtual_keys#managing-auth---tracking-spend + - send_invite_email: Optional[bool] - Whether to send an invite email to the + user_id, with the generate key + - max_budget: Optional[float] - Specify max budget for a given key. + - budget_duration: Optional[str] - Budget is reset at the end of specified + duration. If not set, budget is never reset. You can set duration as seconds + ("30s"), minutes ("30m"), hours ("30h"), days ("30d"). + - max_parallel_requests: Optional[int] - Rate limit a user based on the number + of parallel requests. Raises 429 error, if user's parallel requests > x. + - metadata: Optional[dict] - Metadata for key, store information for key. + Example metadata = {"team": "core-infra", "app": "app2", "email": "z@hanzo.ai" + } + - guardrails: Optional[List[str]] - List of active guardrails for the key + - permissions: Optional[dict] - key-specific permissions. Currently just used + for turning off pii masking (if connected). Example - {"pii": false} + - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets + {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}}}. IF null or {} then + no model specific budget. + - model_rpm_limit: Optional[dict] - key-specific model rpm limit. Example - + {"text-davinci-002": 1000, "gpt-3.5-turbo": 1000}. IF null or {} then no model + specific rpm limit. + - model_tpm_limit: Optional[dict] - key-specific model tpm limit. Example - + {"text-davinci-002": 1000, "gpt-3.5-turbo": 1000}. IF null or {} then no model + specific tpm limit. + - allowed_cache_controls: Optional[list] - List of allowed cache control values. + Example - ["no-cache", "no-store"]. See all values - + https://docs.hanzo.ai/docs/proxy/caching#turn-on--off-caching-per-request + - blocked: Optional[bool] - Whether the key is blocked. + - rpm_limit: Optional[int] - Specify rpm limit for a given key (Requests per + minute) + - tpm_limit: Optional[int] - Specify tpm limit for a given key (Tokens per + minute) + - soft_budget: Optional[float] - Specify soft budget for a given key. Will + trigger a slack alert when this soft budget is reached. + - tags: Optional[List[str]] - Tags for + [tracking spend](https://llm.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) + and/or doing + [tag-based routing](https://llm.vercel.app/docs/proxy/tag_routing). + - enforced_params: Optional[List[str]] - List of enforced params for the key + (Enterprise only). + [Docs](https://docs.hanzo.ai/docs/proxy/enterprise#enforce-required-params-for-llm-requests) + + Examples: + + 1. Allow users to turn on/off pii masking + + ```bash + curl --location 'http://0.0.0.0:4000/key/generate' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ + "permissions": {"allow_pii_controls": true} + }' + ``` + + Returns: + + - key: (str) The generated api key + - expires: (datetime) Datetime object for when key expires. + - user_id: (str) Unique user id - used for tracking spend across multiple keys + for same user id. + + Args: + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} + return self._post( + "/key/generate", + body=maybe_transform( + { + "aliases": aliases, + "allowed_cache_controls": allowed_cache_controls, + "blocked": blocked, + "budget_duration": budget_duration, + "budget_id": budget_id, + "config": config, + "duration": duration, + "enforced_params": enforced_params, + "guardrails": guardrails, + "key": key, + "key_alias": key_alias, + "max_budget": max_budget, + "max_parallel_requests": max_parallel_requests, + "metadata": metadata, + "model_max_budget": model_max_budget, + "model_rpm_limit": model_rpm_limit, + "model_tpm_limit": model_tpm_limit, + "models": models, + "permissions": permissions, + "rpm_limit": rpm_limit, + "send_invite_email": send_invite_email, + "soft_budget": soft_budget, + "spend": spend, + "tags": tags, + "team_id": team_id, + "tpm_limit": tpm_limit, + "user_id": user_id, + }, + key_generate_params.KeyGenerateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GenerateKeyResponse, + ) + + def regenerate_by_key( + self, + path_key: str, + *, + aliases: Optional[object] | Omit = omit, + allowed_cache_controls: Optional[Iterable[object]] | Omit = omit, + blocked: Optional[bool] | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + budget_id: Optional[str] | Omit = omit, + config: Optional[object] | Omit = omit, + duration: Optional[str] | Omit = omit, + enforced_params: Optional[SequenceNotStr[str]] | Omit = omit, + guardrails: Optional[SequenceNotStr[str]] | Omit = omit, + body_key: Optional[str] | Omit = omit, + key_alias: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_max_budget: Optional[object] | Omit = omit, + model_rpm_limit: Optional[object] | Omit = omit, + model_tpm_limit: Optional[object] | Omit = omit, + models: Optional[Iterable[object]] | Omit = omit, + new_master_key: Optional[str] | Omit = omit, + permissions: Optional[object] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + send_invite_email: Optional[bool] | Omit = omit, + soft_budget: Optional[float] | Omit = omit, + spend: Optional[float] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + team_id: Optional[str] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, + user_id: Optional[str] | Omit = omit, + llm_changed_by: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Optional[GenerateKeyResponse]: + """ + Regenerate an existing API key while optionally updating its parameters. + + Parameters: + + - key: str (path parameter) - The key to regenerate + - data: Optional[RegenerateKeyRequest] - Request body containing optional + parameters to update + - key_alias: Optional[str] - User-friendly key alias + - user_id: Optional[str] - User ID associated with key + - team_id: Optional[str] - Team ID associated with key + - models: Optional[list] - Model_name's a user is allowed to call + - tags: Optional[List[str]] - Tags for organizing keys (Enterprise only) + - spend: Optional[float] - Amount spent by key + - max_budget: Optional[float] - Max budget for key + - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets + {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}} + - budget_duration: Optional[str] - Budget reset period ("30d", "1h", etc.) + - soft_budget: Optional[float] - Soft budget limit (warning vs. hard stop). + Will trigger a slack alert when this soft budget is reached. + - max_parallel_requests: Optional[int] - Rate limit for parallel requests + - metadata: Optional[dict] - Metadata for key. Example {"team": "core-infra", + "app": "app2"} + - tpm_limit: Optional[int] - Tokens per minute limit + - rpm_limit: Optional[int] - Requests per minute limit + - model_rpm_limit: Optional[dict] - Model-specific RPM limits {"gpt-4": 100, + "claude-v1": 200} + - model_tpm_limit: Optional[dict] - Model-specific TPM limits {"gpt-4": + 100000, "claude-v1": 200000} + - allowed_cache_controls: Optional[list] - List of allowed cache control + values + - duration: Optional[str] - Key validity duration ("30d", "1h", etc.) + - permissions: Optional[dict] - Key-specific permissions + - guardrails: Optional[List[str]] - List of active guardrails for the key + - blocked: Optional[bool] - Whether the key is blocked + + Returns: + + - GenerateKeyResponse containing the new key and its updated parameters + + Example: + + ```bash + curl --location --request POST 'http://localhost:4000/key/sk-1234/regenerate' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data-raw '{ + "max_budget": 100, + "metadata": {"team": "core-infra"}, + "models": ["gpt-4", "gpt-3.5-turbo"] + }' + ``` + + Note: This is an Enterprise feature. It requires a premium license to use. + + Args: + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_key: + raise ValueError(f"Expected a non-empty value for `path_key` but received {path_key!r}") + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} + return self._post( + f"/key/{path_key}/regenerate", + body=maybe_transform( + { + "aliases": aliases, + "allowed_cache_controls": allowed_cache_controls, + "blocked": blocked, + "budget_duration": budget_duration, + "budget_id": budget_id, + "config": config, + "duration": duration, + "enforced_params": enforced_params, + "guardrails": guardrails, + "body_key": body_key, + "key_alias": key_alias, + "max_budget": max_budget, + "max_parallel_requests": max_parallel_requests, + "metadata": metadata, + "model_max_budget": model_max_budget, + "model_rpm_limit": model_rpm_limit, + "model_tpm_limit": model_tpm_limit, + "models": models, + "new_master_key": new_master_key, + "permissions": permissions, + "rpm_limit": rpm_limit, + "send_invite_email": send_invite_email, + "soft_budget": soft_budget, + "spend": spend, + "tags": tags, + "team_id": team_id, + "tpm_limit": tpm_limit, + "user_id": user_id, + }, + key_regenerate_by_key_params.KeyRegenerateByKeyParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GenerateKeyResponse, + ) + + def retrieve_info( + self, + *, + key: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Retrieve information about a key. + + Parameters: key: Optional[str] = Query + parameter representing the key in the request user_api_key_dict: UserAPIKeyAuth + = Dependency representing the user's API key Returns: Dict containing the key + and its associated information + + Example Curl: + + ``` + curl -X GET "http://0.0.0.0:4000/key/info?key=sk-02Wr4IAlN3NvPXvL5JVvDA" -H "Authorization: Bearer sk-1234" + ``` + + Example Curl - if no key is passed, it will use the Key Passed in Authorization + Header + + ``` + curl -X GET "http://0.0.0.0:4000/key/info" -H "Authorization: Bearer sk-02Wr4IAlN3NvPXvL5JVvDA" + ``` + + Args: + key: Key in the request parameters + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/key/info", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"key": key}, key_retrieve_info_params.KeyRetrieveInfoParams), + ), + cast_to=object, + ) + + def unblock( + self, + *, + key: str, + llm_changed_by: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Unblock a Virtual key to allow it to make requests again. + + Parameters: + + - key: str - The key to unblock. Can be either the unhashed key (sk-...) or the + hashed key value + + Example: + + ```bash + curl --location 'http://0.0.0.0:4000/key/unblock' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ + "key": "sk-Fn8Ej39NxjAXrvpUGKghGw" + }' + ``` + + Note: This is an admin-only endpoint. Only proxy admins can unblock keys. + + Args: + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} + return self._post( + "/key/unblock", + body=maybe_transform({"key": key}, key_unblock_params.KeyUnblockParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncKeyResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncKeyResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncKeyResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncKeyResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncKeyResourceWithStreamingResponse(self) + + async def update( + self, + *, + key: str, + aliases: Optional[object] | Omit = omit, + allowed_cache_controls: Optional[Iterable[object]] | Omit = omit, + blocked: Optional[bool] | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + budget_id: Optional[str] | Omit = omit, + config: Optional[object] | Omit = omit, + duration: Optional[str] | Omit = omit, + enforced_params: Optional[SequenceNotStr[str]] | Omit = omit, + guardrails: Optional[SequenceNotStr[str]] | Omit = omit, + key_alias: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_max_budget: Optional[object] | Omit = omit, + model_rpm_limit: Optional[object] | Omit = omit, + model_tpm_limit: Optional[object] | Omit = omit, + models: Optional[Iterable[object]] | Omit = omit, + permissions: Optional[object] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + spend: Optional[float] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + team_id: Optional[str] | Omit = omit, + temp_budget_expiry: Union[str, datetime, None] | Omit = omit, + temp_budget_increase: Optional[float] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, + user_id: Optional[str] | Omit = omit, + llm_changed_by: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Update an existing API key's parameters. + + Parameters: + + - key: str - The key to update + - key_alias: Optional[str] - User-friendly key alias + - user_id: Optional[str] - User ID associated with key + - team_id: Optional[str] - Team ID associated with key + - budget_id: Optional[str] - The budget id associated with the key. Created by + calling `/budget/new`. + - models: Optional[list] - Model_name's a user is allowed to call + - tags: Optional[List[str]] - Tags for organizing keys (Enterprise only) + - enforced_params: Optional[List[str]] - List of enforced params for the key + (Enterprise only). + [Docs](https://docs.hanzo.ai/docs/proxy/enterprise#enforce-required-params-for-llm-requests) + - spend: Optional[float] - Amount spent by key + - max_budget: Optional[float] - Max budget for key + - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets + {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}} + - budget_duration: Optional[str] - Budget reset period ("30d", "1h", etc.) + - soft_budget: Optional[float] - [TODO] Soft budget limit (warning vs. hard + stop). Will trigger a slack alert when this soft budget is reached. + - max_parallel_requests: Optional[int] - Rate limit for parallel requests + - metadata: Optional[dict] - Metadata for key. Example {"team": "core-infra", + "app": "app2"} + - tpm_limit: Optional[int] - Tokens per minute limit + - rpm_limit: Optional[int] - Requests per minute limit + - model_rpm_limit: Optional[dict] - Model-specific RPM limits {"gpt-4": 100, + "claude-v1": 200} + - model_tpm_limit: Optional[dict] - Model-specific TPM limits {"gpt-4": 100000, + "claude-v1": 200000} + - allowed_cache_controls: Optional[list] - List of allowed cache control values + - duration: Optional[str] - Key validity duration ("30d", "1h", etc.) + - permissions: Optional[dict] - Key-specific permissions + - send_invite_email: Optional[bool] - Send invite email to user_id + - guardrails: Optional[List[str]] - List of active guardrails for the key + - blocked: Optional[bool] - Whether the key is blocked + - aliases: Optional[dict] - Model aliases for the key - + [Docs](https://llm.vercel.app/docs/proxy/virtual_keys#model-aliases) + - config: Optional[dict] - [DEPRECATED PARAM] Key-specific config. + - temp_budget_increase: Optional[float] - Temporary budget increase for the key + (Enterprise only). + - temp_budget_expiry: Optional[str] - Expiry time for the temporary budget + increase (Enterprise only). + + Example: + + ```bash + curl --location 'http://0.0.0.0:4000/key/update' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ + "key": "sk-1234", + "key_alias": "my-key", + "user_id": "user-1234", + "team_id": "team-1234", + "max_budget": 100, + "metadata": {"any_key": "any-val"}, + }' + ``` + + Args: + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} + return await self._post( + "/key/update", + body=await async_maybe_transform( + { + "key": key, + "aliases": aliases, + "allowed_cache_controls": allowed_cache_controls, + "blocked": blocked, + "budget_duration": budget_duration, + "budget_id": budget_id, + "config": config, + "duration": duration, + "enforced_params": enforced_params, + "guardrails": guardrails, + "key_alias": key_alias, + "max_budget": max_budget, + "max_parallel_requests": max_parallel_requests, + "metadata": metadata, + "model_max_budget": model_max_budget, + "model_rpm_limit": model_rpm_limit, + "model_tpm_limit": model_tpm_limit, + "models": models, + "permissions": permissions, + "rpm_limit": rpm_limit, + "spend": spend, + "tags": tags, + "team_id": team_id, + "temp_budget_expiry": temp_budget_expiry, + "temp_budget_increase": temp_budget_increase, + "tpm_limit": tpm_limit, + "user_id": user_id, + }, + key_update_params.KeyUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def list( + self, + *, + include_team_keys: bool | Omit = omit, + key_alias: Optional[str] | Omit = omit, + organization_id: Optional[str] | Omit = omit, + page: int | Omit = omit, + return_full_object: bool | Omit = omit, + size: int | Omit = omit, + team_id: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyListResponse: + """ + List all keys for a given user / team / organization. + + Returns: { "keys": List[str] or List[UserAPIKeyAuth], "total_count": int, + "current_page": int, "total_pages": int, } + + Args: + include_team_keys: Include all keys for teams that user is an admin of. + + key_alias: Filter keys by key alias + + organization_id: Filter keys by organization ID + + page: Page number + + return_full_object: Return full key object + + size: Page size + + team_id: Filter keys by team ID + + user_id: Filter keys by user ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/key/list", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "include_team_keys": include_team_keys, + "key_alias": key_alias, + "organization_id": organization_id, + "page": page, + "return_full_object": return_full_object, + "size": size, + "team_id": team_id, + "user_id": user_id, + }, + key_list_params.KeyListParams, + ), + ), + cast_to=KeyListResponse, + ) + + async def delete( + self, + *, + key_aliases: Optional[SequenceNotStr[str]] | Omit = omit, + keys: Optional[SequenceNotStr[str]] | Omit = omit, + llm_changed_by: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete a key from the key management system. + + Parameters:: + + - keys (List[str]): A list of keys or hashed keys to delete. Example {"keys": + ["sk-QWrxEynunsNpV1zT48HIrw", + "837e17519f44683334df5291321d97b8bf1098cd490e49e215f6fea935aa28be"]} + - key_aliases (List[str]): A list of key aliases to delete. Can be passed + instead of `keys`.Example {"key_aliases": ["alias1", "alias2"]} + + Returns: + + - deleted_keys (List[str]): A list of deleted keys. Example {"deleted_keys": + ["sk-QWrxEynunsNpV1zT48HIrw", + "837e17519f44683334df5291321d97b8bf1098cd490e49e215f6fea935aa28be"]} + + Example: + + ```bash + curl --location 'http://0.0.0.0:4000/key/delete' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ + "keys": ["sk-QWrxEynunsNpV1zT48HIrw"] + }' + ``` + + Raises: HTTPException: If an error occurs during key deletion. + + Args: + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} + return await self._post( + "/key/delete", + body=await async_maybe_transform( + { + "key_aliases": key_aliases, + "keys": keys, + }, + key_delete_params.KeyDeleteParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def block( + self, + *, + key: str, + llm_changed_by: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Optional[KeyBlockResponse]: + """ + Block an Virtual key from making any requests. + + Parameters: + + - key: str - The key to block. Can be either the unhashed key (sk-...) or the + hashed key value + + Example: + + ```bash + curl --location 'http://0.0.0.0:4000/key/block' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ + "key": "sk-Fn8Ej39NxjAXrvpUGKghGw" + }' + ``` + + Note: This is an admin-only endpoint. Only proxy admins can block keys. + + Args: + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} + return await self._post( + "/key/block", + body=await async_maybe_transform({"key": key}, key_block_params.KeyBlockParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyBlockResponse, + ) + + async def check_health( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyCheckHealthResponse: + """ + Check the health of the key + + Checks: + + - If key based logging is configured correctly - sends a test log + + Usage + + Pass the key in the request header + + ```bash + curl -X POST "http://localhost:4000/key/health" -H "Authorization: Bearer sk-1234" -H "Content-Type: application/json" + ``` + + Response when logging callbacks are setup correctly: + + ```json + { + "key": "healthy", + "logging_callbacks": { + "callbacks": ["gcs_bucket"], + "status": "healthy", + "details": "No logger exceptions triggered, system is healthy. Manually check if logs were sent to ['gcs_bucket']" + } + } + ``` + + Response when logging callbacks are not setup correctly: + + ```json + { + "key": "unhealthy", + "logging_callbacks": { + "callbacks": ["gcs_bucket"], + "status": "unhealthy", + "details": "Logger exceptions triggered, system is unhealthy: Failed to load vertex credentials. Check to see if credentials containing partial/invalid information." + } + } + ``` + """ + return await self._post( + "/key/health", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyCheckHealthResponse, + ) + + async def generate( + self, + *, + aliases: Optional[object] | Omit = omit, + allowed_cache_controls: Optional[Iterable[object]] | Omit = omit, + blocked: Optional[bool] | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + budget_id: Optional[str] | Omit = omit, + config: Optional[object] | Omit = omit, + duration: Optional[str] | Omit = omit, + enforced_params: Optional[SequenceNotStr[str]] | Omit = omit, + guardrails: Optional[SequenceNotStr[str]] | Omit = omit, + key: Optional[str] | Omit = omit, + key_alias: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_max_budget: Optional[object] | Omit = omit, + model_rpm_limit: Optional[object] | Omit = omit, + model_tpm_limit: Optional[object] | Omit = omit, + models: Optional[Iterable[object]] | Omit = omit, + permissions: Optional[object] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + send_invite_email: Optional[bool] | Omit = omit, + soft_budget: Optional[float] | Omit = omit, + spend: Optional[float] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + team_id: Optional[str] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, + user_id: Optional[str] | Omit = omit, + llm_changed_by: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GenerateKeyResponse: + """ + Generate an API key based on the provided data. + + Docs: https://docs.hanzo.ai/docs/proxy/virtual_keys + + Parameters: + + - duration: Optional[str] - Specify the length of time the token is valid for. + You can set duration as seconds ("30s"), minutes ("30m"), hours ("30h"), days + ("30d"). + - key_alias: Optional[str] - User defined key alias + - key: Optional[str] - User defined key value. If not set, a 16-digit unique + sk-key is created for you. + - team_id: Optional[str] - The team id of the key + - user_id: Optional[str] - The user id of the key + - budget_id: Optional[str] - The budget id associated with the key. Created by + calling `/budget/new`. + - models: Optional[list] - Model_name's a user is allowed to call. (if empty, + key is allowed to call all models) + - aliases: Optional[dict] - Any alias mappings, on top of anything in the + config.yaml model list. - + https://docs.hanzo.ai/docs/proxy/virtual_keys#managing-auth---upgradedowngrade-models + - config: Optional[dict] - any key-specific configs, overrides config in + config.yaml + - spend: Optional[int] - Amount spent by key. Default is 0. Will be updated by + proxy whenever key is used. + https://docs.hanzo.ai/docs/proxy/virtual_keys#managing-auth---tracking-spend + - send_invite_email: Optional[bool] - Whether to send an invite email to the + user_id, with the generate key + - max_budget: Optional[float] - Specify max budget for a given key. + - budget_duration: Optional[str] - Budget is reset at the end of specified + duration. If not set, budget is never reset. You can set duration as seconds + ("30s"), minutes ("30m"), hours ("30h"), days ("30d"). + - max_parallel_requests: Optional[int] - Rate limit a user based on the number + of parallel requests. Raises 429 error, if user's parallel requests > x. + - metadata: Optional[dict] - Metadata for key, store information for key. + Example metadata = {"team": "core-infra", "app": "app2", "email": "z@hanzo.ai" + } + - guardrails: Optional[List[str]] - List of active guardrails for the key + - permissions: Optional[dict] - key-specific permissions. Currently just used + for turning off pii masking (if connected). Example - {"pii": false} + - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets + {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}}}. IF null or {} then + no model specific budget. + - model_rpm_limit: Optional[dict] - key-specific model rpm limit. Example - + {"text-davinci-002": 1000, "gpt-3.5-turbo": 1000}. IF null or {} then no model + specific rpm limit. + - model_tpm_limit: Optional[dict] - key-specific model tpm limit. Example - + {"text-davinci-002": 1000, "gpt-3.5-turbo": 1000}. IF null or {} then no model + specific tpm limit. + - allowed_cache_controls: Optional[list] - List of allowed cache control values. + Example - ["no-cache", "no-store"]. See all values - + https://docs.hanzo.ai/docs/proxy/caching#turn-on--off-caching-per-request + - blocked: Optional[bool] - Whether the key is blocked. + - rpm_limit: Optional[int] - Specify rpm limit for a given key (Requests per + minute) + - tpm_limit: Optional[int] - Specify tpm limit for a given key (Tokens per + minute) + - soft_budget: Optional[float] - Specify soft budget for a given key. Will + trigger a slack alert when this soft budget is reached. + - tags: Optional[List[str]] - Tags for + [tracking spend](https://llm.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) + and/or doing + [tag-based routing](https://llm.vercel.app/docs/proxy/tag_routing). + - enforced_params: Optional[List[str]] - List of enforced params for the key + (Enterprise only). + [Docs](https://docs.hanzo.ai/docs/proxy/enterprise#enforce-required-params-for-llm-requests) + + Examples: + + 1. Allow users to turn on/off pii masking + + ```bash + curl --location 'http://0.0.0.0:4000/key/generate' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ + "permissions": {"allow_pii_controls": true} + }' + ``` + + Returns: + + - key: (str) The generated api key + - expires: (datetime) Datetime object for when key expires. + - user_id: (str) Unique user id - used for tracking spend across multiple keys + for same user id. + + Args: + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} + return await self._post( + "/key/generate", + body=await async_maybe_transform( + { + "aliases": aliases, + "allowed_cache_controls": allowed_cache_controls, + "blocked": blocked, + "budget_duration": budget_duration, + "budget_id": budget_id, + "config": config, + "duration": duration, + "enforced_params": enforced_params, + "guardrails": guardrails, + "key": key, + "key_alias": key_alias, + "max_budget": max_budget, + "max_parallel_requests": max_parallel_requests, + "metadata": metadata, + "model_max_budget": model_max_budget, + "model_rpm_limit": model_rpm_limit, + "model_tpm_limit": model_tpm_limit, + "models": models, + "permissions": permissions, + "rpm_limit": rpm_limit, + "send_invite_email": send_invite_email, + "soft_budget": soft_budget, + "spend": spend, + "tags": tags, + "team_id": team_id, + "tpm_limit": tpm_limit, + "user_id": user_id, + }, + key_generate_params.KeyGenerateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GenerateKeyResponse, + ) + + async def regenerate_by_key( + self, + path_key: str, + *, + aliases: Optional[object] | Omit = omit, + allowed_cache_controls: Optional[Iterable[object]] | Omit = omit, + blocked: Optional[bool] | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + budget_id: Optional[str] | Omit = omit, + config: Optional[object] | Omit = omit, + duration: Optional[str] | Omit = omit, + enforced_params: Optional[SequenceNotStr[str]] | Omit = omit, + guardrails: Optional[SequenceNotStr[str]] | Omit = omit, + body_key: Optional[str] | Omit = omit, + key_alias: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_max_budget: Optional[object] | Omit = omit, + model_rpm_limit: Optional[object] | Omit = omit, + model_tpm_limit: Optional[object] | Omit = omit, + models: Optional[Iterable[object]] | Omit = omit, + new_master_key: Optional[str] | Omit = omit, + permissions: Optional[object] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + send_invite_email: Optional[bool] | Omit = omit, + soft_budget: Optional[float] | Omit = omit, + spend: Optional[float] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + team_id: Optional[str] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, + user_id: Optional[str] | Omit = omit, + llm_changed_by: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Optional[GenerateKeyResponse]: + """ + Regenerate an existing API key while optionally updating its parameters. + + Parameters: + + - key: str (path parameter) - The key to regenerate + - data: Optional[RegenerateKeyRequest] - Request body containing optional + parameters to update + - key_alias: Optional[str] - User-friendly key alias + - user_id: Optional[str] - User ID associated with key + - team_id: Optional[str] - Team ID associated with key + - models: Optional[list] - Model_name's a user is allowed to call + - tags: Optional[List[str]] - Tags for organizing keys (Enterprise only) + - spend: Optional[float] - Amount spent by key + - max_budget: Optional[float] - Max budget for key + - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets + {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}} + - budget_duration: Optional[str] - Budget reset period ("30d", "1h", etc.) + - soft_budget: Optional[float] - Soft budget limit (warning vs. hard stop). + Will trigger a slack alert when this soft budget is reached. + - max_parallel_requests: Optional[int] - Rate limit for parallel requests + - metadata: Optional[dict] - Metadata for key. Example {"team": "core-infra", + "app": "app2"} + - tpm_limit: Optional[int] - Tokens per minute limit + - rpm_limit: Optional[int] - Requests per minute limit + - model_rpm_limit: Optional[dict] - Model-specific RPM limits {"gpt-4": 100, + "claude-v1": 200} + - model_tpm_limit: Optional[dict] - Model-specific TPM limits {"gpt-4": + 100000, "claude-v1": 200000} + - allowed_cache_controls: Optional[list] - List of allowed cache control + values + - duration: Optional[str] - Key validity duration ("30d", "1h", etc.) + - permissions: Optional[dict] - Key-specific permissions + - guardrails: Optional[List[str]] - List of active guardrails for the key + - blocked: Optional[bool] - Whether the key is blocked + + Returns: + + - GenerateKeyResponse containing the new key and its updated parameters + + Example: + + ```bash + curl --location --request POST 'http://localhost:4000/key/sk-1234/regenerate' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data-raw '{ + "max_budget": 100, + "metadata": {"team": "core-infra"}, + "models": ["gpt-4", "gpt-3.5-turbo"] + }' + ``` + + Note: This is an Enterprise feature. It requires a premium license to use. + + Args: + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_key: + raise ValueError(f"Expected a non-empty value for `path_key` but received {path_key!r}") + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} + return await self._post( + f"/key/{path_key}/regenerate", + body=await async_maybe_transform( + { + "aliases": aliases, + "allowed_cache_controls": allowed_cache_controls, + "blocked": blocked, + "budget_duration": budget_duration, + "budget_id": budget_id, + "config": config, + "duration": duration, + "enforced_params": enforced_params, + "guardrails": guardrails, + "body_key": body_key, + "key_alias": key_alias, + "max_budget": max_budget, + "max_parallel_requests": max_parallel_requests, + "metadata": metadata, + "model_max_budget": model_max_budget, + "model_rpm_limit": model_rpm_limit, + "model_tpm_limit": model_tpm_limit, + "models": models, + "new_master_key": new_master_key, + "permissions": permissions, + "rpm_limit": rpm_limit, + "send_invite_email": send_invite_email, + "soft_budget": soft_budget, + "spend": spend, + "tags": tags, + "team_id": team_id, + "tpm_limit": tpm_limit, + "user_id": user_id, + }, + key_regenerate_by_key_params.KeyRegenerateByKeyParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GenerateKeyResponse, + ) + + async def retrieve_info( + self, + *, + key: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Retrieve information about a key. + + Parameters: key: Optional[str] = Query + parameter representing the key in the request user_api_key_dict: UserAPIKeyAuth + = Dependency representing the user's API key Returns: Dict containing the key + and its associated information + + Example Curl: + + ``` + curl -X GET "http://0.0.0.0:4000/key/info?key=sk-02Wr4IAlN3NvPXvL5JVvDA" -H "Authorization: Bearer sk-1234" + ``` + + Example Curl - if no key is passed, it will use the Key Passed in Authorization + Header + + ``` + curl -X GET "http://0.0.0.0:4000/key/info" -H "Authorization: Bearer sk-02Wr4IAlN3NvPXvL5JVvDA" + ``` + + Args: + key: Key in the request parameters + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/key/info", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"key": key}, key_retrieve_info_params.KeyRetrieveInfoParams), + ), + cast_to=object, + ) + + async def unblock( + self, + *, + key: str, + llm_changed_by: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Unblock a Virtual key to allow it to make requests again. + + Parameters: + + - key: str - The key to unblock. Can be either the unhashed key (sk-...) or the + hashed key value + + Example: + + ```bash + curl --location 'http://0.0.0.0:4000/key/unblock' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ + "key": "sk-Fn8Ej39NxjAXrvpUGKghGw" + }' + ``` + + Note: This is an admin-only endpoint. Only proxy admins can unblock keys. + + Args: + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} + return await self._post( + "/key/unblock", + body=await async_maybe_transform({"key": key}, key_unblock_params.KeyUnblockParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class KeyResourceWithRawResponse: + def __init__(self, key: KeyResource) -> None: + self._key = key + + self.update = to_raw_response_wrapper( + key.update, + ) + self.list = to_raw_response_wrapper( + key.list, + ) + self.delete = to_raw_response_wrapper( + key.delete, + ) + self.block = to_raw_response_wrapper( + key.block, + ) + self.check_health = to_raw_response_wrapper( + key.check_health, + ) + self.generate = to_raw_response_wrapper( + key.generate, + ) + self.regenerate_by_key = to_raw_response_wrapper( + key.regenerate_by_key, + ) + self.retrieve_info = to_raw_response_wrapper( + key.retrieve_info, + ) + self.unblock = to_raw_response_wrapper( + key.unblock, + ) + + +class AsyncKeyResourceWithRawResponse: + def __init__(self, key: AsyncKeyResource) -> None: + self._key = key + + self.update = async_to_raw_response_wrapper( + key.update, + ) + self.list = async_to_raw_response_wrapper( + key.list, + ) + self.delete = async_to_raw_response_wrapper( + key.delete, + ) + self.block = async_to_raw_response_wrapper( + key.block, + ) + self.check_health = async_to_raw_response_wrapper( + key.check_health, + ) + self.generate = async_to_raw_response_wrapper( + key.generate, + ) + self.regenerate_by_key = async_to_raw_response_wrapper( + key.regenerate_by_key, + ) + self.retrieve_info = async_to_raw_response_wrapper( + key.retrieve_info, + ) + self.unblock = async_to_raw_response_wrapper( + key.unblock, + ) + + +class KeyResourceWithStreamingResponse: + def __init__(self, key: KeyResource) -> None: + self._key = key + + self.update = to_streamed_response_wrapper( + key.update, + ) + self.list = to_streamed_response_wrapper( + key.list, + ) + self.delete = to_streamed_response_wrapper( + key.delete, + ) + self.block = to_streamed_response_wrapper( + key.block, + ) + self.check_health = to_streamed_response_wrapper( + key.check_health, + ) + self.generate = to_streamed_response_wrapper( + key.generate, + ) + self.regenerate_by_key = to_streamed_response_wrapper( + key.regenerate_by_key, + ) + self.retrieve_info = to_streamed_response_wrapper( + key.retrieve_info, + ) + self.unblock = to_streamed_response_wrapper( + key.unblock, + ) + + +class AsyncKeyResourceWithStreamingResponse: + def __init__(self, key: AsyncKeyResource) -> None: + self._key = key + + self.update = async_to_streamed_response_wrapper( + key.update, + ) + self.list = async_to_streamed_response_wrapper( + key.list, + ) + self.delete = async_to_streamed_response_wrapper( + key.delete, + ) + self.block = async_to_streamed_response_wrapper( + key.block, + ) + self.check_health = async_to_streamed_response_wrapper( + key.check_health, + ) + self.generate = async_to_streamed_response_wrapper( + key.generate, + ) + self.regenerate_by_key = async_to_streamed_response_wrapper( + key.regenerate_by_key, + ) + self.retrieve_info = async_to_streamed_response_wrapper( + key.retrieve_info, + ) + self.unblock = async_to_streamed_response_wrapper( + key.unblock, + ) diff --git a/pkg/hanzoai/resources/langfuse.py b/src/hanzoai/resources/langfuse.py similarity index 78% rename from pkg/hanzoai/resources/langfuse.py rename to src/hanzoai/resources/langfuse.py index 8c60ba6ae..9ce2e3c6f 100644 --- a/pkg/hanzoai/resources/langfuse.py +++ b/src/hanzoai/resources/langfuse.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -47,9 +47,9 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: - """Call Langfuse via Hanzo proxy. + """Call Langfuse via LLM proxy. Works with Langfuse SDK. @@ -65,16 +65,11 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._post( f"/langfuse/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -88,9 +83,9 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: - """Call Langfuse via Hanzo proxy. + """Call Langfuse via LLM proxy. Works with Langfuse SDK. @@ -106,16 +101,11 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._get( f"/langfuse/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -129,9 +119,9 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: - """Call Langfuse via Hanzo proxy. + """Call Langfuse via LLM proxy. Works with Langfuse SDK. @@ -147,16 +137,11 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._put( f"/langfuse/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -170,9 +155,9 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: - """Call Langfuse via Hanzo proxy. + """Call Langfuse via LLM proxy. Works with Langfuse SDK. @@ -188,16 +173,11 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._delete( f"/langfuse/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -211,9 +191,9 @@ def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: - """Call Langfuse via Hanzo proxy. + """Call Langfuse via LLM proxy. Works with Langfuse SDK. @@ -229,16 +209,11 @@ def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._patch( f"/langfuse/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -273,9 +248,9 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: - """Call Langfuse via Hanzo proxy. + """Call Langfuse via LLM proxy. Works with Langfuse SDK. @@ -291,16 +266,11 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._post( f"/langfuse/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -314,9 +284,9 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: - """Call Langfuse via Hanzo proxy. + """Call Langfuse via LLM proxy. Works with Langfuse SDK. @@ -332,16 +302,11 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._get( f"/langfuse/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -355,9 +320,9 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: - """Call Langfuse via Hanzo proxy. + """Call Langfuse via LLM proxy. Works with Langfuse SDK. @@ -373,16 +338,11 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._put( f"/langfuse/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -396,9 +356,9 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: - """Call Langfuse via Hanzo proxy. + """Call Langfuse via LLM proxy. Works with Langfuse SDK. @@ -414,16 +374,11 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._delete( f"/langfuse/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -437,9 +392,9 @@ async def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: - """Call Langfuse via Hanzo proxy. + """Call Langfuse via LLM proxy. Works with Langfuse SDK. @@ -455,16 +410,11 @@ async def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._patch( f"/langfuse/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/model/__init__.py b/src/hanzoai/resources/model/__init__.py new file mode 100644 index 000000000..0fc80b7d7 --- /dev/null +++ b/src/hanzoai/resources/model/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .info import ( + InfoResource, + AsyncInfoResource, + InfoResourceWithRawResponse, + AsyncInfoResourceWithRawResponse, + InfoResourceWithStreamingResponse, + AsyncInfoResourceWithStreamingResponse, +) +from .model import ( + ModelResource, + AsyncModelResource, + ModelResourceWithRawResponse, + AsyncModelResourceWithRawResponse, + ModelResourceWithStreamingResponse, + AsyncModelResourceWithStreamingResponse, +) +from .update import ( + UpdateResource, + AsyncUpdateResource, + UpdateResourceWithRawResponse, + AsyncUpdateResourceWithRawResponse, + UpdateResourceWithStreamingResponse, + AsyncUpdateResourceWithStreamingResponse, +) + +__all__ = [ + "InfoResource", + "AsyncInfoResource", + "InfoResourceWithRawResponse", + "AsyncInfoResourceWithRawResponse", + "InfoResourceWithStreamingResponse", + "AsyncInfoResourceWithStreamingResponse", + "UpdateResource", + "AsyncUpdateResource", + "UpdateResourceWithRawResponse", + "AsyncUpdateResourceWithRawResponse", + "UpdateResourceWithStreamingResponse", + "AsyncUpdateResourceWithStreamingResponse", + "ModelResource", + "AsyncModelResource", + "ModelResourceWithRawResponse", + "AsyncModelResourceWithRawResponse", + "ModelResourceWithStreamingResponse", + "AsyncModelResourceWithStreamingResponse", +] diff --git a/src/hanzoai/resources/model/info.py b/src/hanzoai/resources/model/info.py new file mode 100644 index 000000000..638dd54cf --- /dev/null +++ b/src/hanzoai/resources/model/info.py @@ -0,0 +1,228 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...types.model import info_list_params +from ..._base_client import make_request_options + +__all__ = ["InfoResource", "AsyncInfoResource"] + + +class InfoResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> InfoResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return InfoResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InfoResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return InfoResourceWithStreamingResponse(self) + + def list( + self, + *, + llm_model_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Provides more info about each model in /models, including config.yaml + descriptions (except api key and api base) + + Parameters: llm_model_id: Optional[str] = None (this is the value of + `x-llm-model-id` returned in response headers) + + - When llm_model_id is passed, it will return the info for that specific model + - When llm_model_id is not passed, it will return the info for all models + + Returns: Returns a dictionary containing information about each model. + + Example Response: + + ```json + { + "data": [ + { + "model_name": "fake-openai-endpoint", + "llm_params": { + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "model": "openai/fake" + }, + "model_info": { + "id": "112f74fab24a7a5245d2ced3536dd8f5f9192c57ee6e332af0f0512e08bed5af", + "db_model": false + } + } + ] + } + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/model/info", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"llm_model_id": llm_model_id}, info_list_params.InfoListParams), + ), + cast_to=object, + ) + + +class AsyncInfoResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncInfoResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncInfoResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInfoResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncInfoResourceWithStreamingResponse(self) + + async def list( + self, + *, + llm_model_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Provides more info about each model in /models, including config.yaml + descriptions (except api key and api base) + + Parameters: llm_model_id: Optional[str] = None (this is the value of + `x-llm-model-id` returned in response headers) + + - When llm_model_id is passed, it will return the info for that specific model + - When llm_model_id is not passed, it will return the info for all models + + Returns: Returns a dictionary containing information about each model. + + Example Response: + + ```json + { + "data": [ + { + "model_name": "fake-openai-endpoint", + "llm_params": { + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "model": "openai/fake" + }, + "model_info": { + "id": "112f74fab24a7a5245d2ced3536dd8f5f9192c57ee6e332af0f0512e08bed5af", + "db_model": false + } + } + ] + } + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/model/info", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"llm_model_id": llm_model_id}, info_list_params.InfoListParams), + ), + cast_to=object, + ) + + +class InfoResourceWithRawResponse: + def __init__(self, info: InfoResource) -> None: + self._info = info + + self.list = to_raw_response_wrapper( + info.list, + ) + + +class AsyncInfoResourceWithRawResponse: + def __init__(self, info: AsyncInfoResource) -> None: + self._info = info + + self.list = async_to_raw_response_wrapper( + info.list, + ) + + +class InfoResourceWithStreamingResponse: + def __init__(self, info: InfoResource) -> None: + self._info = info + + self.list = to_streamed_response_wrapper( + info.list, + ) + + +class AsyncInfoResourceWithStreamingResponse: + def __init__(self, info: AsyncInfoResource) -> None: + self._info = info + + self.list = async_to_streamed_response_wrapper( + info.list, + ) diff --git a/src/hanzoai/resources/model/model.py b/src/hanzoai/resources/model/model.py new file mode 100644 index 000000000..8bace8e9d --- /dev/null +++ b/src/hanzoai/resources/model/model.py @@ -0,0 +1,325 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .info import ( + InfoResource, + AsyncInfoResource, + InfoResourceWithRawResponse, + AsyncInfoResourceWithRawResponse, + InfoResourceWithStreamingResponse, + AsyncInfoResourceWithStreamingResponse, +) +from .update import ( + UpdateResource, + AsyncUpdateResource, + UpdateResourceWithRawResponse, + AsyncUpdateResourceWithRawResponse, + UpdateResourceWithStreamingResponse, + AsyncUpdateResourceWithStreamingResponse, +) +from ...types import model_create_params, model_delete_params +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.model_info_param import ModelInfoParam + +__all__ = ["ModelResource", "AsyncModelResource"] + + +class ModelResource(SyncAPIResource): + @cached_property + def info(self) -> InfoResource: + return InfoResource(self._client) + + @cached_property + def update(self) -> UpdateResource: + return UpdateResource(self._client) + + @cached_property + def with_raw_response(self) -> ModelResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return ModelResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ModelResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return ModelResourceWithStreamingResponse(self) + + def create( + self, + *, + llm_params: model_create_params.LlmParams, + model_info: ModelInfoParam, + model_name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Allows adding new models to the model list in the config.yaml + + Args: + llm_params: LLM Params with 'model' requirement - used for completions + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/model/new", + body=maybe_transform( + { + "llm_params": llm_params, + "model_info": model_info, + "model_name": model_name, + }, + model_create_params.ModelCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def delete( + self, + *, + id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Allows deleting models in the model list in the config.yaml + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/model/delete", + body=maybe_transform({"id": id}, model_delete_params.ModelDeleteParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncModelResource(AsyncAPIResource): + @cached_property + def info(self) -> AsyncInfoResource: + return AsyncInfoResource(self._client) + + @cached_property + def update(self) -> AsyncUpdateResource: + return AsyncUpdateResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncModelResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncModelResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncModelResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncModelResourceWithStreamingResponse(self) + + async def create( + self, + *, + llm_params: model_create_params.LlmParams, + model_info: ModelInfoParam, + model_name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Allows adding new models to the model list in the config.yaml + + Args: + llm_params: LLM Params with 'model' requirement - used for completions + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/model/new", + body=await async_maybe_transform( + { + "llm_params": llm_params, + "model_info": model_info, + "model_name": model_name, + }, + model_create_params.ModelCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def delete( + self, + *, + id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Allows deleting models in the model list in the config.yaml + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/model/delete", + body=await async_maybe_transform({"id": id}, model_delete_params.ModelDeleteParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class ModelResourceWithRawResponse: + def __init__(self, model: ModelResource) -> None: + self._model = model + + self.create = to_raw_response_wrapper( + model.create, + ) + self.delete = to_raw_response_wrapper( + model.delete, + ) + + @cached_property + def info(self) -> InfoResourceWithRawResponse: + return InfoResourceWithRawResponse(self._model.info) + + @cached_property + def update(self) -> UpdateResourceWithRawResponse: + return UpdateResourceWithRawResponse(self._model.update) + + +class AsyncModelResourceWithRawResponse: + def __init__(self, model: AsyncModelResource) -> None: + self._model = model + + self.create = async_to_raw_response_wrapper( + model.create, + ) + self.delete = async_to_raw_response_wrapper( + model.delete, + ) + + @cached_property + def info(self) -> AsyncInfoResourceWithRawResponse: + return AsyncInfoResourceWithRawResponse(self._model.info) + + @cached_property + def update(self) -> AsyncUpdateResourceWithRawResponse: + return AsyncUpdateResourceWithRawResponse(self._model.update) + + +class ModelResourceWithStreamingResponse: + def __init__(self, model: ModelResource) -> None: + self._model = model + + self.create = to_streamed_response_wrapper( + model.create, + ) + self.delete = to_streamed_response_wrapper( + model.delete, + ) + + @cached_property + def info(self) -> InfoResourceWithStreamingResponse: + return InfoResourceWithStreamingResponse(self._model.info) + + @cached_property + def update(self) -> UpdateResourceWithStreamingResponse: + return UpdateResourceWithStreamingResponse(self._model.update) + + +class AsyncModelResourceWithStreamingResponse: + def __init__(self, model: AsyncModelResource) -> None: + self._model = model + + self.create = async_to_streamed_response_wrapper( + model.create, + ) + self.delete = async_to_streamed_response_wrapper( + model.delete, + ) + + @cached_property + def info(self) -> AsyncInfoResourceWithStreamingResponse: + return AsyncInfoResourceWithStreamingResponse(self._model.info) + + @cached_property + def update(self) -> AsyncUpdateResourceWithStreamingResponse: + return AsyncUpdateResourceWithStreamingResponse(self._model.update) diff --git a/pkg/hanzoai/resources/model/update.py b/src/hanzoai/resources/model/update.py similarity index 79% rename from pkg/hanzoai/resources/model/update.py rename to src/hanzoai/resources/model/update.py index 06173b444..378305165 100644 --- a/pkg/hanzoai/resources/model/update.py +++ b/src/hanzoai/resources/model/update.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -6,11 +6,8 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -49,15 +46,15 @@ def with_streaming_response(self) -> UpdateResourceWithStreamingResponse: def full( self, *, - hanzo_params: Optional[update_full_params.LitellmParams] | NotGiven = NOT_GIVEN, - model_info: Optional[ModelInfoParam] | NotGiven = NOT_GIVEN, - model_name: Optional[str] | NotGiven = NOT_GIVEN, + llm_params: Optional[update_full_params.LlmParams] | Omit = omit, + model_info: Optional[ModelInfoParam] | Omit = omit, + model_name: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Edit existing model params @@ -75,17 +72,14 @@ def full( "/model/update", body=maybe_transform( { - "hanzo_params": hanzo_params, + "llm_params": llm_params, "model_info": model_info, "model_name": model_name, }, update_full_params.UpdateFullParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -94,17 +88,15 @@ def partial( self, model_id: str, *, - hanzo_params: ( - Optional[update_partial_params.LitellmParams] | NotGiven - ) = NOT_GIVEN, - model_info: Optional[ModelInfoParam] | NotGiven = NOT_GIVEN, - model_name: Optional[str] | NotGiven = NOT_GIVEN, + llm_params: Optional[update_partial_params.LlmParams] | Omit = omit, + model_info: Optional[ModelInfoParam] | Omit = omit, + model_name: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ PATCH Endpoint for partial model updates. @@ -130,24 +122,19 @@ def partial( timeout: Override the client-level default timeout for this request, in seconds """ if not model_id: - raise ValueError( - f"Expected a non-empty value for `model_id` but received {model_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `model_id` but received {model_id!r}") return self._patch( f"/model/{model_id}/update", body=maybe_transform( { - "hanzo_params": hanzo_params, + "llm_params": llm_params, "model_info": model_info, "model_name": model_name, }, update_partial_params.UpdatePartialParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -176,15 +163,15 @@ def with_streaming_response(self) -> AsyncUpdateResourceWithStreamingResponse: async def full( self, *, - hanzo_params: Optional[update_full_params.LitellmParams] | NotGiven = NOT_GIVEN, - model_info: Optional[ModelInfoParam] | NotGiven = NOT_GIVEN, - model_name: Optional[str] | NotGiven = NOT_GIVEN, + llm_params: Optional[update_full_params.LlmParams] | Omit = omit, + model_info: Optional[ModelInfoParam] | Omit = omit, + model_name: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Edit existing model params @@ -202,17 +189,14 @@ async def full( "/model/update", body=await async_maybe_transform( { - "hanzo_params": hanzo_params, + "llm_params": llm_params, "model_info": model_info, "model_name": model_name, }, update_full_params.UpdateFullParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -221,17 +205,15 @@ async def partial( self, model_id: str, *, - hanzo_params: ( - Optional[update_partial_params.LitellmParams] | NotGiven - ) = NOT_GIVEN, - model_info: Optional[ModelInfoParam] | NotGiven = NOT_GIVEN, - model_name: Optional[str] | NotGiven = NOT_GIVEN, + llm_params: Optional[update_partial_params.LlmParams] | Omit = omit, + model_info: Optional[ModelInfoParam] | Omit = omit, + model_name: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ PATCH Endpoint for partial model updates. @@ -257,24 +239,19 @@ async def partial( timeout: Override the client-level default timeout for this request, in seconds """ if not model_id: - raise ValueError( - f"Expected a non-empty value for `model_id` but received {model_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `model_id` but received {model_id!r}") return await self._patch( f"/model/{model_id}/update", body=await async_maybe_transform( { - "hanzo_params": hanzo_params, + "llm_params": llm_params, "model_info": model_info, "model_name": model_name, }, update_partial_params.UpdatePartialParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/pkg/hanzoai/resources/model_group.py b/src/hanzoai/resources/model_group.py similarity index 93% rename from pkg/hanzoai/resources/model_group.py rename to src/hanzoai/resources/model_group.py index a22ba9531..0202bd142 100644 --- a/pkg/hanzoai/resources/model_group.py +++ b/src/hanzoai/resources/model_group.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -7,11 +7,8 @@ import httpx from ..types import model_group_retrieve_info_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - maybe_transform, - async_maybe_transform, -) +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -48,17 +45,17 @@ def with_streaming_response(self) -> ModelGroupResourceWithStreamingResponse: def retrieve_info( self, *, - model_group: Optional[str] | NotGiven = NOT_GIVEN, + model_group: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ - Get information about all the deployments on hanzo proxy, including - config.yaml descriptions (except api key and api base) + Get information about all the deployments on llm proxy, including config.yaml + descriptions (except api key and api base) - /model_group/info returns all model groups. End users of proxy should use /model_group/info since those models will be used for /chat/completions, @@ -217,8 +214,7 @@ def retrieve_info( extra_body=extra_body, timeout=timeout, query=maybe_transform( - {"model_group": model_group}, - model_group_retrieve_info_params.ModelGroupRetrieveInfoParams, + {"model_group": model_group}, model_group_retrieve_info_params.ModelGroupRetrieveInfoParams ), ), cast_to=object, @@ -248,17 +244,17 @@ def with_streaming_response(self) -> AsyncModelGroupResourceWithStreamingRespons async def retrieve_info( self, *, - model_group: Optional[str] | NotGiven = NOT_GIVEN, + model_group: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ - Get information about all the deployments on hanzo proxy, including - config.yaml descriptions (except api key and api base) + Get information about all the deployments on llm proxy, including config.yaml + descriptions (except api key and api base) - /model_group/info returns all model groups. End users of proxy should use /model_group/info since those models will be used for /chat/completions, @@ -417,8 +413,7 @@ async def retrieve_info( extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( - {"model_group": model_group}, - model_group_retrieve_info_params.ModelGroupRetrieveInfoParams, + {"model_group": model_group}, model_group_retrieve_info_params.ModelGroupRetrieveInfoParams ), ), cast_to=object, diff --git a/src/hanzoai/resources/models.py b/src/hanzoai/resources/models.py new file mode 100644 index 000000000..f24def8eb --- /dev/null +++ b/src/hanzoai/resources/models.py @@ -0,0 +1,190 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..types import model_list_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options + +__all__ = ["ModelsResource", "AsyncModelsResource"] + + +class ModelsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ModelsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return ModelsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ModelsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return ModelsResourceWithStreamingResponse(self) + + def list( + self, + *, + return_wildcard_routes: Optional[bool] | Omit = omit, + team_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Use `/model/info` - to get detailed model information, example - pricing, mode, + etc. + + This is just for compatibility with openai projects like aider. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v1/models", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "return_wildcard_routes": return_wildcard_routes, + "team_id": team_id, + }, + model_list_params.ModelListParams, + ), + ), + cast_to=object, + ) + + +class AsyncModelsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncModelsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncModelsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncModelsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncModelsResourceWithStreamingResponse(self) + + async def list( + self, + *, + return_wildcard_routes: Optional[bool] | Omit = omit, + team_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Use `/model/info` - to get detailed model information, example - pricing, mode, + etc. + + This is just for compatibility with openai projects like aider. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v1/models", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "return_wildcard_routes": return_wildcard_routes, + "team_id": team_id, + }, + model_list_params.ModelListParams, + ), + ), + cast_to=object, + ) + + +class ModelsResourceWithRawResponse: + def __init__(self, models: ModelsResource) -> None: + self._models = models + + self.list = to_raw_response_wrapper( + models.list, + ) + + +class AsyncModelsResourceWithRawResponse: + def __init__(self, models: AsyncModelsResource) -> None: + self._models = models + + self.list = async_to_raw_response_wrapper( + models.list, + ) + + +class ModelsResourceWithStreamingResponse: + def __init__(self, models: ModelsResource) -> None: + self._models = models + + self.list = to_streamed_response_wrapper( + models.list, + ) + + +class AsyncModelsResourceWithStreamingResponse: + def __init__(self, models: AsyncModelsResource) -> None: + self._models = models + + self.list = async_to_streamed_response_wrapper( + models.list, + ) diff --git a/pkg/hanzoai/resources/moderations.py b/src/hanzoai/resources/moderations.py similarity index 90% rename from pkg/hanzoai/resources/moderations.py rename to src/hanzoai/resources/moderations.py index f8bd44689..5e63bb8ad 100644 --- a/pkg/hanzoai/resources/moderations.py +++ b/src/hanzoai/resources/moderations.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -46,7 +46,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ The moderations endpoint is a tool you can use to check whether content complies @@ -61,10 +61,7 @@ def create( return self._post( "/v1/moderations", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -98,7 +95,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ The moderations endpoint is a tool you can use to check whether content complies @@ -113,10 +110,7 @@ async def create( return await self._post( "/v1/moderations", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/openai/__init__.py b/src/hanzoai/resources/openai/__init__.py new file mode 100644 index 000000000..70d7bb0c3 --- /dev/null +++ b/src/hanzoai/resources/openai/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .openai import ( + OpenAIResource, + AsyncOpenAIResource, + OpenAIResourceWithRawResponse, + AsyncOpenAIResourceWithRawResponse, + OpenAIResourceWithStreamingResponse, + AsyncOpenAIResourceWithStreamingResponse, +) +from .deployments import ( + DeploymentsResource, + AsyncDeploymentsResource, + DeploymentsResourceWithRawResponse, + AsyncDeploymentsResourceWithRawResponse, + DeploymentsResourceWithStreamingResponse, + AsyncDeploymentsResourceWithStreamingResponse, +) + +__all__ = [ + "DeploymentsResource", + "AsyncDeploymentsResource", + "DeploymentsResourceWithRawResponse", + "AsyncDeploymentsResourceWithRawResponse", + "DeploymentsResourceWithStreamingResponse", + "AsyncDeploymentsResourceWithStreamingResponse", + "OpenAIResource", + "AsyncOpenAIResource", + "OpenAIResourceWithRawResponse", + "AsyncOpenAIResourceWithRawResponse", + "OpenAIResourceWithStreamingResponse", + "AsyncOpenAIResourceWithStreamingResponse", +] diff --git a/src/hanzoai/resources/openai/deployments/__init__.py b/src/hanzoai/resources/openai/deployments/__init__.py new file mode 100644 index 000000000..a8fd431e6 --- /dev/null +++ b/src/hanzoai/resources/openai/deployments/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .chat import ( + ChatResource, + AsyncChatResource, + ChatResourceWithRawResponse, + AsyncChatResourceWithRawResponse, + ChatResourceWithStreamingResponse, + AsyncChatResourceWithStreamingResponse, +) +from .deployments import ( + DeploymentsResource, + AsyncDeploymentsResource, + DeploymentsResourceWithRawResponse, + AsyncDeploymentsResourceWithRawResponse, + DeploymentsResourceWithStreamingResponse, + AsyncDeploymentsResourceWithStreamingResponse, +) + +__all__ = [ + "ChatResource", + "AsyncChatResource", + "ChatResourceWithRawResponse", + "AsyncChatResourceWithRawResponse", + "ChatResourceWithStreamingResponse", + "AsyncChatResourceWithStreamingResponse", + "DeploymentsResource", + "AsyncDeploymentsResource", + "DeploymentsResourceWithRawResponse", + "AsyncDeploymentsResourceWithRawResponse", + "DeploymentsResourceWithStreamingResponse", + "AsyncDeploymentsResourceWithStreamingResponse", +] diff --git a/src/hanzoai/resources/openai/deployments/chat.py b/src/hanzoai/resources/openai/deployments/chat.py new file mode 100644 index 000000000..f0fce545d --- /dev/null +++ b/src/hanzoai/resources/openai/deployments/chat.py @@ -0,0 +1,194 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options + +__all__ = ["ChatResource", "AsyncChatResource"] + + +class ChatResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return ChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return ChatResourceWithStreamingResponse(self) + + def complete( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Follows the exact same API spec as + `OpenAI's Chat API https://platform.openai.com/docs/api-reference/chat` + + ```bash + curl -X POST http://localhost:4000/v1/chat/completions + -H "Content-Type: application/json" + -H "Authorization: Bearer sk-1234" + -d '{ + "model": "gpt-4o", + "messages": [ + { + "role": "user", + "content": "Hello!" + } + ] + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") + return self._post( + f"/openai/deployments/{model}/chat/completions", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncChatResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncChatResourceWithStreamingResponse(self) + + async def complete( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Follows the exact same API spec as + `OpenAI's Chat API https://platform.openai.com/docs/api-reference/chat` + + ```bash + curl -X POST http://localhost:4000/v1/chat/completions + -H "Content-Type: application/json" + -H "Authorization: Bearer sk-1234" + -d '{ + "model": "gpt-4o", + "messages": [ + { + "role": "user", + "content": "Hello!" + } + ] + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") + return await self._post( + f"/openai/deployments/{model}/chat/completions", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class ChatResourceWithRawResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + self.complete = to_raw_response_wrapper( + chat.complete, + ) + + +class AsyncChatResourceWithRawResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + self.complete = async_to_raw_response_wrapper( + chat.complete, + ) + + +class ChatResourceWithStreamingResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + self.complete = to_streamed_response_wrapper( + chat.complete, + ) + + +class AsyncChatResourceWithStreamingResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + self.complete = async_to_streamed_response_wrapper( + chat.complete, + ) diff --git a/src/hanzoai/resources/openai/deployments/deployments.py b/src/hanzoai/resources/openai/deployments/deployments.py new file mode 100644 index 000000000..50fa43d75 --- /dev/null +++ b/src/hanzoai/resources/openai/deployments/deployments.py @@ -0,0 +1,320 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .chat import ( + ChatResource, + AsyncChatResource, + ChatResourceWithRawResponse, + AsyncChatResourceWithRawResponse, + ChatResourceWithStreamingResponse, + AsyncChatResourceWithStreamingResponse, +) +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options + +__all__ = ["DeploymentsResource", "AsyncDeploymentsResource"] + + +class DeploymentsResource(SyncAPIResource): + @cached_property + def chat(self) -> ChatResource: + return ChatResource(self._client) + + @cached_property + def with_raw_response(self) -> DeploymentsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return DeploymentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DeploymentsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return DeploymentsResourceWithStreamingResponse(self) + + def complete( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Follows the exact same API spec as + `OpenAI's Completions API https://platform.openai.com/docs/api-reference/completions` + + ```bash + curl -X POST http://localhost:4000/v1/completions + -H "Content-Type: application/json" + -H "Authorization: Bearer sk-1234" + -d '{ + "model": "gpt-3.5-turbo-instruct", + "prompt": "Once upon a time", + "max_tokens": 50, + "temperature": 0.7 + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") + return self._post( + f"/openai/deployments/{model}/completions", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def embed( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Follows the exact same API spec as + `OpenAI's Embeddings API https://platform.openai.com/docs/api-reference/embeddings` + + ```bash + curl -X POST http://localhost:4000/v1/embeddings + -H "Content-Type: application/json" + -H "Authorization: Bearer sk-1234" + -d '{ + "model": "text-embedding-ada-002", + "input": "The quick brown fox jumps over the lazy dog" + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") + return self._post( + f"/openai/deployments/{model}/embeddings", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncDeploymentsResource(AsyncAPIResource): + @cached_property + def chat(self) -> AsyncChatResource: + return AsyncChatResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncDeploymentsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncDeploymentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDeploymentsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncDeploymentsResourceWithStreamingResponse(self) + + async def complete( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Follows the exact same API spec as + `OpenAI's Completions API https://platform.openai.com/docs/api-reference/completions` + + ```bash + curl -X POST http://localhost:4000/v1/completions + -H "Content-Type: application/json" + -H "Authorization: Bearer sk-1234" + -d '{ + "model": "gpt-3.5-turbo-instruct", + "prompt": "Once upon a time", + "max_tokens": 50, + "temperature": 0.7 + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") + return await self._post( + f"/openai/deployments/{model}/completions", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def embed( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Follows the exact same API spec as + `OpenAI's Embeddings API https://platform.openai.com/docs/api-reference/embeddings` + + ```bash + curl -X POST http://localhost:4000/v1/embeddings + -H "Content-Type: application/json" + -H "Authorization: Bearer sk-1234" + -d '{ + "model": "text-embedding-ada-002", + "input": "The quick brown fox jumps over the lazy dog" + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") + return await self._post( + f"/openai/deployments/{model}/embeddings", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class DeploymentsResourceWithRawResponse: + def __init__(self, deployments: DeploymentsResource) -> None: + self._deployments = deployments + + self.complete = to_raw_response_wrapper( + deployments.complete, + ) + self.embed = to_raw_response_wrapper( + deployments.embed, + ) + + @cached_property + def chat(self) -> ChatResourceWithRawResponse: + return ChatResourceWithRawResponse(self._deployments.chat) + + +class AsyncDeploymentsResourceWithRawResponse: + def __init__(self, deployments: AsyncDeploymentsResource) -> None: + self._deployments = deployments + + self.complete = async_to_raw_response_wrapper( + deployments.complete, + ) + self.embed = async_to_raw_response_wrapper( + deployments.embed, + ) + + @cached_property + def chat(self) -> AsyncChatResourceWithRawResponse: + return AsyncChatResourceWithRawResponse(self._deployments.chat) + + +class DeploymentsResourceWithStreamingResponse: + def __init__(self, deployments: DeploymentsResource) -> None: + self._deployments = deployments + + self.complete = to_streamed_response_wrapper( + deployments.complete, + ) + self.embed = to_streamed_response_wrapper( + deployments.embed, + ) + + @cached_property + def chat(self) -> ChatResourceWithStreamingResponse: + return ChatResourceWithStreamingResponse(self._deployments.chat) + + +class AsyncDeploymentsResourceWithStreamingResponse: + def __init__(self, deployments: AsyncDeploymentsResource) -> None: + self._deployments = deployments + + self.complete = async_to_streamed_response_wrapper( + deployments.complete, + ) + self.embed = async_to_streamed_response_wrapper( + deployments.embed, + ) + + @cached_property + def chat(self) -> AsyncChatResourceWithStreamingResponse: + return AsyncChatResourceWithStreamingResponse(self._deployments.chat) diff --git a/pkg/hanzoai/resources/openai/openai.py b/src/hanzoai/resources/openai/openai.py similarity index 81% rename from pkg/hanzoai/resources/openai/openai.py rename to src/hanzoai/resources/openai/openai.py index 4e900ec50..77a0eab92 100644 --- a/pkg/hanzoai/resources/openai/openai.py +++ b/src/hanzoai/resources/openai/openai.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Query, Headers, NotGiven, not_given from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -59,7 +59,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Simple pass-through for OpenAI. @@ -76,16 +76,11 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._post( f"/openai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -99,7 +94,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Simple pass-through for OpenAI. @@ -116,16 +111,11 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._get( f"/openai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -139,7 +129,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Simple pass-through for OpenAI. @@ -156,16 +146,11 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._put( f"/openai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -179,7 +164,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Simple pass-through for OpenAI. @@ -196,16 +181,11 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._delete( f"/openai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -219,7 +199,7 @@ def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Simple pass-through for OpenAI. @@ -236,16 +216,11 @@ def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._patch( f"/openai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -284,7 +259,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Simple pass-through for OpenAI. @@ -301,16 +276,11 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._post( f"/openai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -324,7 +294,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Simple pass-through for OpenAI. @@ -341,16 +311,11 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._get( f"/openai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -364,7 +329,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Simple pass-through for OpenAI. @@ -381,16 +346,11 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._put( f"/openai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -404,7 +364,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Simple pass-through for OpenAI. @@ -421,16 +381,11 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._delete( f"/openai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -444,7 +399,7 @@ async def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Simple pass-through for OpenAI. @@ -461,16 +416,11 @@ async def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._patch( f"/openai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/organization/__init__.py b/src/hanzoai/resources/organization/__init__.py new file mode 100644 index 000000000..6eadf07f6 --- /dev/null +++ b/src/hanzoai/resources/organization/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .info import ( + InfoResource, + AsyncInfoResource, + InfoResourceWithRawResponse, + AsyncInfoResourceWithRawResponse, + InfoResourceWithStreamingResponse, + AsyncInfoResourceWithStreamingResponse, +) +from .organization import ( + OrganizationResource, + AsyncOrganizationResource, + OrganizationResourceWithRawResponse, + AsyncOrganizationResourceWithRawResponse, + OrganizationResourceWithStreamingResponse, + AsyncOrganizationResourceWithStreamingResponse, +) + +__all__ = [ + "InfoResource", + "AsyncInfoResource", + "InfoResourceWithRawResponse", + "AsyncInfoResourceWithRawResponse", + "InfoResourceWithStreamingResponse", + "AsyncInfoResourceWithStreamingResponse", + "OrganizationResource", + "AsyncOrganizationResource", + "OrganizationResourceWithRawResponse", + "AsyncOrganizationResourceWithRawResponse", + "OrganizationResourceWithStreamingResponse", + "AsyncOrganizationResourceWithStreamingResponse", +] diff --git a/src/hanzoai/resources/organization/info.py b/src/hanzoai/resources/organization/info.py new file mode 100644 index 000000000..aa6549372 --- /dev/null +++ b/src/hanzoai/resources/organization/info.py @@ -0,0 +1,249 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, SequenceNotStr, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.organization import info_retrieve_params, info_deprecated_params +from ...types.organization.info_retrieve_response import InfoRetrieveResponse + +__all__ = ["InfoResource", "AsyncInfoResource"] + + +class InfoResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> InfoResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return InfoResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InfoResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return InfoResourceWithStreamingResponse(self) + + def retrieve( + self, + *, + organization_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InfoRetrieveResponse: + """ + Get the org specific information + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/info", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"organization_id": organization_id}, info_retrieve_params.InfoRetrieveParams), + ), + cast_to=InfoRetrieveResponse, + ) + + def deprecated( + self, + *, + organizations: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + DEPRECATED: Use GET /organization/info instead + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/info", + body=maybe_transform({"organizations": organizations}, info_deprecated_params.InfoDeprecatedParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncInfoResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncInfoResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncInfoResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInfoResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncInfoResourceWithStreamingResponse(self) + + async def retrieve( + self, + *, + organization_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InfoRetrieveResponse: + """ + Get the org specific information + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/info", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"organization_id": organization_id}, info_retrieve_params.InfoRetrieveParams + ), + ), + cast_to=InfoRetrieveResponse, + ) + + async def deprecated( + self, + *, + organizations: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + DEPRECATED: Use GET /organization/info instead + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/info", + body=await async_maybe_transform( + {"organizations": organizations}, info_deprecated_params.InfoDeprecatedParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class InfoResourceWithRawResponse: + def __init__(self, info: InfoResource) -> None: + self._info = info + + self.retrieve = to_raw_response_wrapper( + info.retrieve, + ) + self.deprecated = to_raw_response_wrapper( + info.deprecated, + ) + + +class AsyncInfoResourceWithRawResponse: + def __init__(self, info: AsyncInfoResource) -> None: + self._info = info + + self.retrieve = async_to_raw_response_wrapper( + info.retrieve, + ) + self.deprecated = async_to_raw_response_wrapper( + info.deprecated, + ) + + +class InfoResourceWithStreamingResponse: + def __init__(self, info: InfoResource) -> None: + self._info = info + + self.retrieve = to_streamed_response_wrapper( + info.retrieve, + ) + self.deprecated = to_streamed_response_wrapper( + info.deprecated, + ) + + +class AsyncInfoResourceWithStreamingResponse: + def __init__(self, info: AsyncInfoResource) -> None: + self._info = info + + self.retrieve = async_to_streamed_response_wrapper( + info.retrieve, + ) + self.deprecated = async_to_streamed_response_wrapper( + info.deprecated, + ) diff --git a/pkg/hanzoai/resources/organization/organization.py b/src/hanzoai/resources/organization/organization.py similarity index 81% rename from pkg/hanzoai/resources/organization/organization.py rename to src/hanzoai/resources/organization/organization.py index a4d60f6f5..f3c164a5d 100644 --- a/pkg/hanzoai/resources/organization/organization.py +++ b/src/hanzoai/resources/organization/organization.py @@ -1,8 +1,9 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations -from typing import List, Iterable, Optional +from typing import Iterable, Optional +from typing_extensions import Literal import httpx @@ -15,7 +16,6 @@ AsyncInfoResourceWithStreamingResponse, ) from ...types import ( - UserRoles, organization_create_params, organization_delete_params, organization_update_params, @@ -23,11 +23,8 @@ organization_delete_member_params, organization_update_member_params, ) -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -37,17 +34,12 @@ async_to_streamed_response_wrapper, ) from ..._base_client import make_request_options -from ...types.user_roles import UserRoles from ...types.organization_list_response import OrganizationListResponse from ...types.organization_create_response import OrganizationCreateResponse from ...types.organization_delete_response import OrganizationDeleteResponse from ...types.organization_update_response import OrganizationUpdateResponse -from ...types.organization_membership_table import OrganizationMembershipTable -from ...types.organization_table_with_members import OrganizationTableWithMembers from ...types.organization_add_member_response import OrganizationAddMemberResponse -from ...types.organization_update_member_response import ( - OrganizationUpdateMemberResponse, -) +from ...types.organization_update_member_response import OrganizationUpdateMemberResponse __all__ = ["OrganizationResource", "AsyncOrganizationResource"] @@ -80,23 +72,23 @@ def create( self, *, organization_alias: str, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_max_budget: Optional[object] | NotGiven = NOT_GIVEN, - models: Iterable[object] | NotGiven = NOT_GIVEN, - organization_id: Optional[str] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - soft_budget: Optional[float] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, + budget_duration: Optional[str] | Omit = omit, + budget_id: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_max_budget: Optional[object] | Omit = omit, + models: Iterable[object] | Omit = omit, + organization_id: Optional[str] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + soft_budget: Optional[float] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> OrganizationCreateResponse: """ Allow orgs to own teams @@ -128,9 +120,9 @@ def create( - blocked: _bool_ - Flag indicating if the org is blocked or not - will stop all calls from keys with this org_id. - tags: _Optional[List[str]]_ - Tags for - [tracking spend](https://hanzo.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) + [tracking spend](https://llm.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) and/or doing - [tag-based routing](https://hanzo.vercel.app/docs/proxy/tag_routing). + [tag-based routing](https://llm.vercel.app/docs/proxy/tag_routing). - organization_id: _Optional[str]_ - The organization id of the team. Default is None. Create via `/organization/new`. - model_aliases: Optional[dict] - Model aliases for the team. @@ -193,10 +185,7 @@ def create( organization_create_params.OrganizationCreateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=OrganizationCreateResponse, ) @@ -204,19 +193,19 @@ def create( def update( self, *, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - models: Optional[List[str]] | NotGiven = NOT_GIVEN, - organization_alias: Optional[str] | NotGiven = NOT_GIVEN, - organization_id: Optional[str] | NotGiven = NOT_GIVEN, - spend: Optional[float] | NotGiven = NOT_GIVEN, - updated_by: Optional[str] | NotGiven = NOT_GIVEN, + budget_id: Optional[str] | Omit = omit, + metadata: Optional[object] | Omit = omit, + models: Optional[SequenceNotStr[str]] | Omit = omit, + organization_alias: Optional[str] | Omit = omit, + organization_id: Optional[str] | Omit = omit, + spend: Optional[float] | Omit = omit, + updated_by: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> OrganizationUpdateResponse: """ Update an organization @@ -245,10 +234,7 @@ def update( organization_update_params.OrganizationUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=OrganizationUpdateResponse, ) @@ -261,7 +247,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> OrganizationListResponse: """ ``` @@ -271,10 +257,7 @@ def list( return self._get( "/organization/list", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=OrganizationListResponse, ) @@ -282,13 +265,13 @@ def list( def delete( self, *, - organization_ids: List[str], + organization_ids: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> OrganizationDeleteResponse: """ Delete an organization @@ -309,14 +292,10 @@ def delete( return self._delete( "/organization/delete", body=maybe_transform( - {"organization_ids": organization_ids}, - organization_delete_params.OrganizationDeleteParams, + {"organization_ids": organization_ids}, organization_delete_params.OrganizationDeleteParams ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=OrganizationDeleteResponse, ) @@ -326,13 +305,13 @@ def add_member( *, member: organization_add_member_params.Member, organization_id: str, - max_budget_in_organization: Optional[float] | NotGiven = NOT_GIVEN, + max_budget_in_organization: Optional[float] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> OrganizationAddMemberResponse: """ [BETA] @@ -347,7 +326,7 @@ def add_member( - organization_id: str (required) - member: Union[List[Member], Member] (required) - - role: Literal[LitellmUserRoles] (required) + - role: Literal[LLMUserRoles] (required) - user_id: Optional[str] - user_email: Optional[str] @@ -360,7 +339,7 @@ def add_member( "organization_id": "45e3e396-ee08-4a61-a88e-16b3ce7e0849", "member": { "role": "internal_user", - "user_id": "krrish247652@berri.ai" + "user_id": "dev247652@hanzo.ai" }, "max_budget_in_organization": 100.0 }' @@ -370,8 +349,8 @@ def add_member( 1. Check if organization exists 2. Creates a new Internal User if the user_id or user_email is not found in - Hanzo_UserTable - 3. Add Internal User to the `Hanzo_OrganizationMembership` table + LLM_UserTable + 3. Add Internal User to the `LLM_OrganizationMembership` table Args: extra_headers: Send extra headers @@ -393,10 +372,7 @@ def add_member( organization_add_member_params.OrganizationAddMemberParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=OrganizationAddMemberResponse, ) @@ -405,14 +381,14 @@ def delete_member( self, *, organization_id: str, - user_email: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, + user_email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Delete a member from an organization @@ -437,10 +413,7 @@ def delete_member( organization_delete_member_params.OrganizationDeleteMemberParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -449,16 +422,27 @@ def update_member( self, *, organization_id: str, - max_budget_in_organization: Optional[float] | NotGiven = NOT_GIVEN, - role: Optional[UserRoles] | NotGiven = NOT_GIVEN, - user_email: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, + max_budget_in_organization: Optional[float] | Omit = omit, + role: Optional[ + Literal[ + "proxy_admin", + "proxy_admin_viewer", + "org_admin", + "internal_user", + "internal_user_viewer", + "team", + "customer", + ] + ] + | Omit = omit, + user_email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> OrganizationUpdateMemberResponse: """ Update a member's role in an organization @@ -497,10 +481,7 @@ def update_member( organization_update_member_params.OrganizationUpdateMemberParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=OrganizationUpdateMemberResponse, ) @@ -534,23 +515,23 @@ async def create( self, *, organization_alias: str, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_max_budget: Optional[object] | NotGiven = NOT_GIVEN, - models: Iterable[object] | NotGiven = NOT_GIVEN, - organization_id: Optional[str] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - soft_budget: Optional[float] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, + budget_duration: Optional[str] | Omit = omit, + budget_id: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_max_budget: Optional[object] | Omit = omit, + models: Iterable[object] | Omit = omit, + organization_id: Optional[str] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + soft_budget: Optional[float] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> OrganizationCreateResponse: """ Allow orgs to own teams @@ -582,9 +563,9 @@ async def create( - blocked: _bool_ - Flag indicating if the org is blocked or not - will stop all calls from keys with this org_id. - tags: _Optional[List[str]]_ - Tags for - [tracking spend](https://hanzo.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) + [tracking spend](https://llm.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) and/or doing - [tag-based routing](https://hanzo.vercel.app/docs/proxy/tag_routing). + [tag-based routing](https://llm.vercel.app/docs/proxy/tag_routing). - organization_id: _Optional[str]_ - The organization id of the team. Default is None. Create via `/organization/new`. - model_aliases: Optional[dict] - Model aliases for the team. @@ -647,10 +628,7 @@ async def create( organization_create_params.OrganizationCreateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=OrganizationCreateResponse, ) @@ -658,19 +636,19 @@ async def create( async def update( self, *, - budget_id: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - models: Optional[List[str]] | NotGiven = NOT_GIVEN, - organization_alias: Optional[str] | NotGiven = NOT_GIVEN, - organization_id: Optional[str] | NotGiven = NOT_GIVEN, - spend: Optional[float] | NotGiven = NOT_GIVEN, - updated_by: Optional[str] | NotGiven = NOT_GIVEN, + budget_id: Optional[str] | Omit = omit, + metadata: Optional[object] | Omit = omit, + models: Optional[SequenceNotStr[str]] | Omit = omit, + organization_alias: Optional[str] | Omit = omit, + organization_id: Optional[str] | Omit = omit, + spend: Optional[float] | Omit = omit, + updated_by: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> OrganizationUpdateResponse: """ Update an organization @@ -699,10 +677,7 @@ async def update( organization_update_params.OrganizationUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=OrganizationUpdateResponse, ) @@ -715,7 +690,7 @@ async def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> OrganizationListResponse: """ ``` @@ -725,10 +700,7 @@ async def list( return await self._get( "/organization/list", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=OrganizationListResponse, ) @@ -736,13 +708,13 @@ async def list( async def delete( self, *, - organization_ids: List[str], + organization_ids: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> OrganizationDeleteResponse: """ Delete an organization @@ -763,14 +735,10 @@ async def delete( return await self._delete( "/organization/delete", body=await async_maybe_transform( - {"organization_ids": organization_ids}, - organization_delete_params.OrganizationDeleteParams, + {"organization_ids": organization_ids}, organization_delete_params.OrganizationDeleteParams ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=OrganizationDeleteResponse, ) @@ -780,13 +748,13 @@ async def add_member( *, member: organization_add_member_params.Member, organization_id: str, - max_budget_in_organization: Optional[float] | NotGiven = NOT_GIVEN, + max_budget_in_organization: Optional[float] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> OrganizationAddMemberResponse: """ [BETA] @@ -801,7 +769,7 @@ async def add_member( - organization_id: str (required) - member: Union[List[Member], Member] (required) - - role: Literal[LitellmUserRoles] (required) + - role: Literal[LLMUserRoles] (required) - user_id: Optional[str] - user_email: Optional[str] @@ -814,7 +782,7 @@ async def add_member( "organization_id": "45e3e396-ee08-4a61-a88e-16b3ce7e0849", "member": { "role": "internal_user", - "user_id": "krrish247652@berri.ai" + "user_id": "dev247652@hanzo.ai" }, "max_budget_in_organization": 100.0 }' @@ -824,8 +792,8 @@ async def add_member( 1. Check if organization exists 2. Creates a new Internal User if the user_id or user_email is not found in - Hanzo_UserTable - 3. Add Internal User to the `Hanzo_OrganizationMembership` table + LLM_UserTable + 3. Add Internal User to the `LLM_OrganizationMembership` table Args: extra_headers: Send extra headers @@ -847,10 +815,7 @@ async def add_member( organization_add_member_params.OrganizationAddMemberParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=OrganizationAddMemberResponse, ) @@ -859,14 +824,14 @@ async def delete_member( self, *, organization_id: str, - user_email: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, + user_email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Delete a member from an organization @@ -891,10 +856,7 @@ async def delete_member( organization_delete_member_params.OrganizationDeleteMemberParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -903,16 +865,27 @@ async def update_member( self, *, organization_id: str, - max_budget_in_organization: Optional[float] | NotGiven = NOT_GIVEN, - role: Optional[UserRoles] | NotGiven = NOT_GIVEN, - user_email: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, + max_budget_in_organization: Optional[float] | Omit = omit, + role: Optional[ + Literal[ + "proxy_admin", + "proxy_admin_viewer", + "org_admin", + "internal_user", + "internal_user_viewer", + "team", + "customer", + ] + ] + | Omit = omit, + user_email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> OrganizationUpdateMemberResponse: """ Update a member's role in an organization @@ -951,10 +924,7 @@ async def update_member( organization_update_member_params.OrganizationUpdateMemberParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=OrganizationUpdateMemberResponse, ) diff --git a/src/hanzoai/resources/provider.py b/src/hanzoai/resources/provider.py new file mode 100644 index 000000000..18b2cb1aa --- /dev/null +++ b/src/hanzoai/resources/provider.py @@ -0,0 +1,225 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .._types import Body, Query, Headers, NotGiven, not_given +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.provider_list_budgets_response import ProviderListBudgetsResponse + +__all__ = ["ProviderResource", "AsyncProviderResource"] + + +class ProviderResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ProviderResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return ProviderResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ProviderResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return ProviderResourceWithStreamingResponse(self) + + def list_budgets( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProviderListBudgetsResponse: + """ + Provider Budget Routing - Get Budget, Spend Details + https://docs.hanzo.ai/docs/proxy/provider_budget_routing + + Use this endpoint to check current budget, spend and budget reset time for a + provider + + Example Request + + ```bash + curl -X GET http://localhost:4000/provider/budgets -H "Content-Type: application/json" -H "Authorization: Bearer sk-1234" + ``` + + Example Response + + ```json + { + "providers": { + "openai": { + "budget_limit": 1e-12, + "time_period": "1d", + "spend": 0.0, + "budget_reset_at": null + }, + "azure": { + "budget_limit": 100.0, + "time_period": "1d", + "spend": 0.0, + "budget_reset_at": null + }, + "anthropic": { + "budget_limit": 100.0, + "time_period": "10d", + "spend": 0.0, + "budget_reset_at": null + }, + "vertex_ai": { + "budget_limit": 100.0, + "time_period": "12d", + "spend": 0.0, + "budget_reset_at": null + } + } + } + ``` + """ + return self._get( + "/provider/budgets", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProviderListBudgetsResponse, + ) + + +class AsyncProviderResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncProviderResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncProviderResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncProviderResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncProviderResourceWithStreamingResponse(self) + + async def list_budgets( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProviderListBudgetsResponse: + """ + Provider Budget Routing - Get Budget, Spend Details + https://docs.hanzo.ai/docs/proxy/provider_budget_routing + + Use this endpoint to check current budget, spend and budget reset time for a + provider + + Example Request + + ```bash + curl -X GET http://localhost:4000/provider/budgets -H "Content-Type: application/json" -H "Authorization: Bearer sk-1234" + ``` + + Example Response + + ```json + { + "providers": { + "openai": { + "budget_limit": 1e-12, + "time_period": "1d", + "spend": 0.0, + "budget_reset_at": null + }, + "azure": { + "budget_limit": 100.0, + "time_period": "1d", + "spend": 0.0, + "budget_reset_at": null + }, + "anthropic": { + "budget_limit": 100.0, + "time_period": "10d", + "spend": 0.0, + "budget_reset_at": null + }, + "vertex_ai": { + "budget_limit": 100.0, + "time_period": "12d", + "spend": 0.0, + "budget_reset_at": null + } + } + } + ``` + """ + return await self._get( + "/provider/budgets", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProviderListBudgetsResponse, + ) + + +class ProviderResourceWithRawResponse: + def __init__(self, provider: ProviderResource) -> None: + self._provider = provider + + self.list_budgets = to_raw_response_wrapper( + provider.list_budgets, + ) + + +class AsyncProviderResourceWithRawResponse: + def __init__(self, provider: AsyncProviderResource) -> None: + self._provider = provider + + self.list_budgets = async_to_raw_response_wrapper( + provider.list_budgets, + ) + + +class ProviderResourceWithStreamingResponse: + def __init__(self, provider: ProviderResource) -> None: + self._provider = provider + + self.list_budgets = to_streamed_response_wrapper( + provider.list_budgets, + ) + + +class AsyncProviderResourceWithStreamingResponse: + def __init__(self, provider: AsyncProviderResource) -> None: + self._provider = provider + + self.list_budgets = async_to_streamed_response_wrapper( + provider.list_budgets, + ) diff --git a/pkg/hanzoai/resources/rerank.py b/src/hanzoai/resources/rerank.py similarity index 83% rename from pkg/hanzoai/resources/rerank.py rename to src/hanzoai/resources/rerank.py index a43f3387a..18899b400 100644 --- a/pkg/hanzoai/resources/rerank.py +++ b/src/hanzoai/resources/rerank.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -46,16 +46,13 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Rerank""" return self._post( "/rerank", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -68,16 +65,13 @@ def create_v1( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Rerank""" return self._post( "/v1/rerank", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -90,16 +84,13 @@ def create_v2( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Rerank""" return self._post( "/v2/rerank", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -133,16 +124,13 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Rerank""" return await self._post( "/rerank", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -155,16 +143,13 @@ async def create_v1( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Rerank""" return await self._post( "/v1/rerank", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -177,16 +162,13 @@ async def create_v2( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Rerank""" return await self._post( "/v2/rerank", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/responses/__init__.py b/src/hanzoai/resources/responses/__init__.py new file mode 100644 index 000000000..230ef765b --- /dev/null +++ b/src/hanzoai/resources/responses/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .responses import ( + ResponsesResource, + AsyncResponsesResource, + ResponsesResourceWithRawResponse, + AsyncResponsesResourceWithRawResponse, + ResponsesResourceWithStreamingResponse, + AsyncResponsesResourceWithStreamingResponse, +) +from .input_items import ( + InputItemsResource, + AsyncInputItemsResource, + InputItemsResourceWithRawResponse, + AsyncInputItemsResourceWithRawResponse, + InputItemsResourceWithStreamingResponse, + AsyncInputItemsResourceWithStreamingResponse, +) + +__all__ = [ + "InputItemsResource", + "AsyncInputItemsResource", + "InputItemsResourceWithRawResponse", + "AsyncInputItemsResourceWithRawResponse", + "InputItemsResourceWithStreamingResponse", + "AsyncInputItemsResourceWithStreamingResponse", + "ResponsesResource", + "AsyncResponsesResource", + "ResponsesResourceWithRawResponse", + "AsyncResponsesResourceWithRawResponse", + "ResponsesResourceWithStreamingResponse", + "AsyncResponsesResourceWithStreamingResponse", +] diff --git a/pkg/hanzoai/resources/responses/input_items.py b/src/hanzoai/resources/responses/input_items.py similarity index 87% rename from pkg/hanzoai/resources/responses/input_items.py rename to src/hanzoai/resources/responses/input_items.py index 7b1ad6d24..cfe0c8ebc 100644 --- a/pkg/hanzoai/resources/responses/input_items.py +++ b/src/hanzoai/resources/responses/input_items.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Query, Headers, NotGiven, not_given from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -47,7 +47,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Get input items for a response. @@ -69,16 +69,11 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ if not response_id: - raise ValueError( - f"Expected a non-empty value for `response_id` but received {response_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") return self._get( f"/v1/responses/{response_id}/input_items", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -113,7 +108,7 @@ async def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Get input items for a response. @@ -135,16 +130,11 @@ async def list( timeout: Override the client-level default timeout for this request, in seconds """ if not response_id: - raise ValueError( - f"Expected a non-empty value for `response_id` but received {response_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") return await self._get( f"/v1/responses/{response_id}/input_items", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/pkg/hanzoai/resources/responses/responses.py b/src/hanzoai/resources/responses/responses.py similarity index 85% rename from pkg/hanzoai/resources/responses/responses.py rename to src/hanzoai/resources/responses/responses.py index 262179878..2aa785f6d 100644 --- a/pkg/hanzoai/resources/responses/responses.py +++ b/src/hanzoai/resources/responses/responses.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Query, Headers, NotGiven, not_given from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -58,7 +58,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Follows the OpenAI Responses API spec: @@ -74,10 +74,7 @@ def create( return self._post( "/v1/responses", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -91,7 +88,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Get a response by ID. @@ -113,16 +110,11 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not response_id: - raise ValueError( - f"Expected a non-empty value for `response_id` but received {response_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") return self._get( f"/v1/responses/{response_id}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -136,7 +128,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Delete a response by ID. @@ -158,16 +150,11 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not response_id: - raise ValueError( - f"Expected a non-empty value for `response_id` but received {response_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") return self._delete( f"/v1/responses/{response_id}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -205,7 +192,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Follows the OpenAI Responses API spec: @@ -221,10 +208,7 @@ async def create( return await self._post( "/v1/responses", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -238,7 +222,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Get a response by ID. @@ -260,16 +244,11 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not response_id: - raise ValueError( - f"Expected a non-empty value for `response_id` but received {response_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") return await self._get( f"/v1/responses/{response_id}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -283,7 +262,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Delete a response by ID. @@ -305,16 +284,11 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not response_id: - raise ValueError( - f"Expected a non-empty value for `response_id` but received {response_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") return await self._delete( f"/v1/responses/{response_id}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/pkg/hanzoai/resources/routes.py b/src/hanzoai/resources/routes.py similarity index 88% rename from pkg/hanzoai/resources/routes.py rename to src/hanzoai/resources/routes.py index dbf273f7b..f54ba2cae 100644 --- a/pkg/hanzoai/resources/routes.py +++ b/src/hanzoai/resources/routes.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -46,16 +46,13 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Get a list of available routes in the FastAPI application.""" return self._get( "/routes", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -89,16 +86,13 @@ async def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """Get a list of available routes in the FastAPI application.""" return await self._get( "/routes", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/settings.py b/src/hanzoai/resources/settings.py new file mode 100644 index 000000000..892999394 --- /dev/null +++ b/src/hanzoai/resources/settings.py @@ -0,0 +1,182 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .._types import Body, Query, Headers, NotGiven, not_given +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options + +__all__ = ["SettingsResource", "AsyncSettingsResource"] + + +class SettingsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SettingsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return SettingsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SettingsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return SettingsResourceWithStreamingResponse(self) + + def retrieve( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Returns a list of llm level settings + + This is useful for debugging and ensuring the proxy server is configured + correctly. + + Response schema: + + ``` + { + "alerting": _alerting, + "llm.callbacks": llm_callbacks, + "llm.input_callback": llm_input_callbacks, + "llm.failure_callback": llm_failure_callbacks, + "llm.success_callback": llm_success_callbacks, + "llm._async_success_callback": llm_async_success_callbacks, + "llm._async_failure_callback": llm_async_failure_callbacks, + "llm._async_input_callback": llm_async_input_callbacks, + "all_llm_callbacks": all_llm_callbacks, + "num_callbacks": len(all_llm_callbacks), + "num_alerting": _num_alerting, + "llm.request_timeout": llm.request_timeout, + } + ``` + """ + return self._get( + "/settings", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncSettingsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSettingsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncSettingsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSettingsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncSettingsResourceWithStreamingResponse(self) + + async def retrieve( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Returns a list of llm level settings + + This is useful for debugging and ensuring the proxy server is configured + correctly. + + Response schema: + + ``` + { + "alerting": _alerting, + "llm.callbacks": llm_callbacks, + "llm.input_callback": llm_input_callbacks, + "llm.failure_callback": llm_failure_callbacks, + "llm.success_callback": llm_success_callbacks, + "llm._async_success_callback": llm_async_success_callbacks, + "llm._async_failure_callback": llm_async_failure_callbacks, + "llm._async_input_callback": llm_async_input_callbacks, + "all_llm_callbacks": all_llm_callbacks, + "num_callbacks": len(all_llm_callbacks), + "num_alerting": _num_alerting, + "llm.request_timeout": llm.request_timeout, + } + ``` + """ + return await self._get( + "/settings", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class SettingsResourceWithRawResponse: + def __init__(self, settings: SettingsResource) -> None: + self._settings = settings + + self.retrieve = to_raw_response_wrapper( + settings.retrieve, + ) + + +class AsyncSettingsResourceWithRawResponse: + def __init__(self, settings: AsyncSettingsResource) -> None: + self._settings = settings + + self.retrieve = async_to_raw_response_wrapper( + settings.retrieve, + ) + + +class SettingsResourceWithStreamingResponse: + def __init__(self, settings: SettingsResource) -> None: + self._settings = settings + + self.retrieve = to_streamed_response_wrapper( + settings.retrieve, + ) + + +class AsyncSettingsResourceWithStreamingResponse: + def __init__(self, settings: AsyncSettingsResource) -> None: + self._settings = settings + + self.retrieve = async_to_streamed_response_wrapper( + settings.retrieve, + ) diff --git a/src/hanzoai/resources/spend.py b/src/hanzoai/resources/spend.py new file mode 100644 index 000000000..9554e8dc8 --- /dev/null +++ b/src/hanzoai/resources/spend.py @@ -0,0 +1,584 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional + +import httpx + +from ..types import spend_list_logs_params, spend_list_tags_params, spend_calculate_spend_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.spend_list_logs_response import SpendListLogsResponse +from ..types.spend_list_tags_response import SpendListTagsResponse + +__all__ = ["SpendResource", "AsyncSpendResource"] + + +class SpendResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SpendResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return SpendResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SpendResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return SpendResourceWithStreamingResponse(self) + + def calculate_spend( + self, + *, + completion_response: Optional[object] | Omit = omit, + messages: Optional[Iterable[object]] | Omit = omit, + model: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Accepts all the params of completion_cost. + + Calculate spend **before** making call: + + Note: If you see a spend of $0.0 you need to set custom_pricing for your model: + https://docs.hanzo.ai/docs/proxy/custom_pricing + + ``` + curl --location 'http://localhost:4000/spend/calculate' + --header 'Authorization: Bearer sk-1234' + --header 'Content-Type: application/json' + --data '{ + "model": "anthropic.claude-v2", + "messages": [{"role": "user", "content": "Hey, how'''s it going?"}] + }' + ``` + + Calculate spend **after** making call: + + ``` + curl --location 'http://localhost:4000/spend/calculate' + --header 'Authorization: Bearer sk-1234' + --header 'Content-Type: application/json' + --data '{ + "completion_response": { + "id": "chatcmpl-123", + "object": "chat.completion", + "created": 1677652288, + "model": "gpt-3.5-turbo-0125", + "system_fingerprint": "fp_44709d6fcb", + "choices": [{ + "index": 0, + "message": { + "role": "assistant", + "content": "Hello there, how may I assist you today?" + }, + "logprobs": null, + "finish_reason": "stop" + }] + "usage": { + "prompt_tokens": 9, + "completion_tokens": 12, + "total_tokens": 21 + } + } + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/spend/calculate", + body=maybe_transform( + { + "completion_response": completion_response, + "messages": messages, + "model": model, + }, + spend_calculate_spend_params.SpendCalculateSpendParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def list_logs( + self, + *, + api_key: Optional[str] | Omit = omit, + end_date: Optional[str] | Omit = omit, + request_id: Optional[str] | Omit = omit, + start_date: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SpendListLogsResponse: + """ + View all spend logs, if request_id is provided, only logs for that request_id + will be returned + + Example Request for all logs + + ``` + curl -X GET "http://0.0.0.0:8000/spend/logs" -H "Authorization: Bearer sk-1234" + ``` + + Example Request for specific request_id + + ``` + curl -X GET "http://0.0.0.0:8000/spend/logs?request_id=chatcmpl-6dcb2540-d3d7-4e49-bb27-291f863f112e" -H "Authorization: Bearer sk-1234" + ``` + + Example Request for specific api_key + + ``` + curl -X GET "http://0.0.0.0:8000/spend/logs?api_key=sk-Fn8Ej39NkBQmUagFEoUWPQ" -H "Authorization: Bearer sk-1234" + ``` + + Example Request for specific user_id + + ``` + curl -X GET "http://0.0.0.0:8000/spend/logs?user_id=z@hanzo.ai" -H "Authorization: Bearer sk-1234" + ``` + + Args: + api_key: Get spend logs based on api key + + end_date: Time till which to view key spend + + request_id: request_id to get spend logs for specific request_id. If none passed then pass + spend logs for all requests + + start_date: Time from which to start viewing key spend + + user_id: Get spend logs based on user_id + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/spend/logs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "api_key": api_key, + "end_date": end_date, + "request_id": request_id, + "start_date": start_date, + "user_id": user_id, + }, + spend_list_logs_params.SpendListLogsParams, + ), + ), + cast_to=SpendListLogsResponse, + ) + + def list_tags( + self, + *, + end_date: Optional[str] | Omit = omit, + start_date: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SpendListTagsResponse: + """ + LLM Enterprise - View Spend Per Request Tag + + Example Request: + + ``` + curl -X GET "http://0.0.0.0:8000/spend/tags" -H "Authorization: Bearer sk-1234" + ``` + + Spend with Start Date and End Date + + ``` + curl -X GET "http://0.0.0.0:8000/spend/tags?start_date=2022-01-01&end_date=2022-02-01" -H "Authorization: Bearer sk-1234" + ``` + + Args: + end_date: Time till which to view key spend + + start_date: Time from which to start viewing key spend + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/spend/tags", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "end_date": end_date, + "start_date": start_date, + }, + spend_list_tags_params.SpendListTagsParams, + ), + ), + cast_to=SpendListTagsResponse, + ) + + +class AsyncSpendResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSpendResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncSpendResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSpendResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncSpendResourceWithStreamingResponse(self) + + async def calculate_spend( + self, + *, + completion_response: Optional[object] | Omit = omit, + messages: Optional[Iterable[object]] | Omit = omit, + model: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Accepts all the params of completion_cost. + + Calculate spend **before** making call: + + Note: If you see a spend of $0.0 you need to set custom_pricing for your model: + https://docs.hanzo.ai/docs/proxy/custom_pricing + + ``` + curl --location 'http://localhost:4000/spend/calculate' + --header 'Authorization: Bearer sk-1234' + --header 'Content-Type: application/json' + --data '{ + "model": "anthropic.claude-v2", + "messages": [{"role": "user", "content": "Hey, how'''s it going?"}] + }' + ``` + + Calculate spend **after** making call: + + ``` + curl --location 'http://localhost:4000/spend/calculate' + --header 'Authorization: Bearer sk-1234' + --header 'Content-Type: application/json' + --data '{ + "completion_response": { + "id": "chatcmpl-123", + "object": "chat.completion", + "created": 1677652288, + "model": "gpt-3.5-turbo-0125", + "system_fingerprint": "fp_44709d6fcb", + "choices": [{ + "index": 0, + "message": { + "role": "assistant", + "content": "Hello there, how may I assist you today?" + }, + "logprobs": null, + "finish_reason": "stop" + }] + "usage": { + "prompt_tokens": 9, + "completion_tokens": 12, + "total_tokens": 21 + } + } + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/spend/calculate", + body=await async_maybe_transform( + { + "completion_response": completion_response, + "messages": messages, + "model": model, + }, + spend_calculate_spend_params.SpendCalculateSpendParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def list_logs( + self, + *, + api_key: Optional[str] | Omit = omit, + end_date: Optional[str] | Omit = omit, + request_id: Optional[str] | Omit = omit, + start_date: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SpendListLogsResponse: + """ + View all spend logs, if request_id is provided, only logs for that request_id + will be returned + + Example Request for all logs + + ``` + curl -X GET "http://0.0.0.0:8000/spend/logs" -H "Authorization: Bearer sk-1234" + ``` + + Example Request for specific request_id + + ``` + curl -X GET "http://0.0.0.0:8000/spend/logs?request_id=chatcmpl-6dcb2540-d3d7-4e49-bb27-291f863f112e" -H "Authorization: Bearer sk-1234" + ``` + + Example Request for specific api_key + + ``` + curl -X GET "http://0.0.0.0:8000/spend/logs?api_key=sk-Fn8Ej39NkBQmUagFEoUWPQ" -H "Authorization: Bearer sk-1234" + ``` + + Example Request for specific user_id + + ``` + curl -X GET "http://0.0.0.0:8000/spend/logs?user_id=z@hanzo.ai" -H "Authorization: Bearer sk-1234" + ``` + + Args: + api_key: Get spend logs based on api key + + end_date: Time till which to view key spend + + request_id: request_id to get spend logs for specific request_id. If none passed then pass + spend logs for all requests + + start_date: Time from which to start viewing key spend + + user_id: Get spend logs based on user_id + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/spend/logs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "api_key": api_key, + "end_date": end_date, + "request_id": request_id, + "start_date": start_date, + "user_id": user_id, + }, + spend_list_logs_params.SpendListLogsParams, + ), + ), + cast_to=SpendListLogsResponse, + ) + + async def list_tags( + self, + *, + end_date: Optional[str] | Omit = omit, + start_date: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SpendListTagsResponse: + """ + LLM Enterprise - View Spend Per Request Tag + + Example Request: + + ``` + curl -X GET "http://0.0.0.0:8000/spend/tags" -H "Authorization: Bearer sk-1234" + ``` + + Spend with Start Date and End Date + + ``` + curl -X GET "http://0.0.0.0:8000/spend/tags?start_date=2022-01-01&end_date=2022-02-01" -H "Authorization: Bearer sk-1234" + ``` + + Args: + end_date: Time till which to view key spend + + start_date: Time from which to start viewing key spend + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/spend/tags", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "end_date": end_date, + "start_date": start_date, + }, + spend_list_tags_params.SpendListTagsParams, + ), + ), + cast_to=SpendListTagsResponse, + ) + + +class SpendResourceWithRawResponse: + def __init__(self, spend: SpendResource) -> None: + self._spend = spend + + self.calculate_spend = to_raw_response_wrapper( + spend.calculate_spend, + ) + self.list_logs = to_raw_response_wrapper( + spend.list_logs, + ) + self.list_tags = to_raw_response_wrapper( + spend.list_tags, + ) + + +class AsyncSpendResourceWithRawResponse: + def __init__(self, spend: AsyncSpendResource) -> None: + self._spend = spend + + self.calculate_spend = async_to_raw_response_wrapper( + spend.calculate_spend, + ) + self.list_logs = async_to_raw_response_wrapper( + spend.list_logs, + ) + self.list_tags = async_to_raw_response_wrapper( + spend.list_tags, + ) + + +class SpendResourceWithStreamingResponse: + def __init__(self, spend: SpendResource) -> None: + self._spend = spend + + self.calculate_spend = to_streamed_response_wrapper( + spend.calculate_spend, + ) + self.list_logs = to_streamed_response_wrapper( + spend.list_logs, + ) + self.list_tags = to_streamed_response_wrapper( + spend.list_tags, + ) + + +class AsyncSpendResourceWithStreamingResponse: + def __init__(self, spend: AsyncSpendResource) -> None: + self._spend = spend + + self.calculate_spend = async_to_streamed_response_wrapper( + spend.calculate_spend, + ) + self.list_logs = async_to_streamed_response_wrapper( + spend.list_logs, + ) + self.list_tags = async_to_streamed_response_wrapper( + spend.list_tags, + ) diff --git a/src/hanzoai/resources/team/__init__.py b/src/hanzoai/resources/team/__init__.py new file mode 100644 index 000000000..3bf886307 --- /dev/null +++ b/src/hanzoai/resources/team/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .team import ( + TeamResource, + AsyncTeamResource, + TeamResourceWithRawResponse, + AsyncTeamResourceWithRawResponse, + TeamResourceWithStreamingResponse, + AsyncTeamResourceWithStreamingResponse, +) +from .model import ( + ModelResource, + AsyncModelResource, + ModelResourceWithRawResponse, + AsyncModelResourceWithRawResponse, + ModelResourceWithStreamingResponse, + AsyncModelResourceWithStreamingResponse, +) +from .callback import ( + CallbackResource, + AsyncCallbackResource, + CallbackResourceWithRawResponse, + AsyncCallbackResourceWithRawResponse, + CallbackResourceWithStreamingResponse, + AsyncCallbackResourceWithStreamingResponse, +) + +__all__ = [ + "ModelResource", + "AsyncModelResource", + "ModelResourceWithRawResponse", + "AsyncModelResourceWithRawResponse", + "ModelResourceWithStreamingResponse", + "AsyncModelResourceWithStreamingResponse", + "CallbackResource", + "AsyncCallbackResource", + "CallbackResourceWithRawResponse", + "AsyncCallbackResourceWithRawResponse", + "CallbackResourceWithStreamingResponse", + "AsyncCallbackResourceWithStreamingResponse", + "TeamResource", + "AsyncTeamResource", + "TeamResourceWithRawResponse", + "AsyncTeamResourceWithRawResponse", + "TeamResourceWithStreamingResponse", + "AsyncTeamResourceWithStreamingResponse", +] diff --git a/pkg/hanzoai/resources/team/callback.py b/src/hanzoai/resources/team/callback.py similarity index 83% rename from pkg/hanzoai/resources/team/callback.py rename to src/hanzoai/resources/team/callback.py index 4f533fa81..5fb50093d 100644 --- a/pkg/hanzoai/resources/team/callback.py +++ b/src/hanzoai/resources/team/callback.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -7,12 +7,8 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - strip_not_given, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, strip_not_given, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -56,7 +52,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Get the success/failure callbacks and variables for a team @@ -89,16 +85,11 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not team_id: - raise ValueError( - f"Expected a non-empty value for `team_id` but received {team_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `team_id` but received {team_id!r}") return self._get( f"/team/{team_id}/callback", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -109,16 +100,14 @@ def add( *, callback_name: str, callback_vars: Dict[str, str], - callback_type: ( - Optional[Literal["success", "failure", "success_and_failure"]] | NotGiven - ) = NOT_GIVEN, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, + callback_type: Optional[Literal["success", "failure", "success_and_failure"]] | Omit = omit, + llm_changed_by: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Add a success/failure callback to a team @@ -163,9 +152,8 @@ def add( the secret key sk-xxxxx Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability extra_headers: Send extra headers @@ -176,13 +164,8 @@ def add( timeout: Override the client-level default timeout for this request, in seconds """ if not team_id: - raise ValueError( - f"Expected a non-empty value for `team_id` but received {team_id!r}" - ) - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } + raise ValueError(f"Expected a non-empty value for `team_id` but received {team_id!r}") + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} return self._post( f"/team/{team_id}/callback", body=maybe_transform( @@ -194,10 +177,7 @@ def add( callback_add_params.CallbackAddParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -232,7 +212,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Get the success/failure callbacks and variables for a team @@ -265,16 +245,11 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not team_id: - raise ValueError( - f"Expected a non-empty value for `team_id` but received {team_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `team_id` but received {team_id!r}") return await self._get( f"/team/{team_id}/callback", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -285,16 +260,14 @@ async def add( *, callback_name: str, callback_vars: Dict[str, str], - callback_type: ( - Optional[Literal["success", "failure", "success_and_failure"]] | NotGiven - ) = NOT_GIVEN, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, + callback_type: Optional[Literal["success", "failure", "success_and_failure"]] | Omit = omit, + llm_changed_by: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Add a success/failure callback to a team @@ -339,9 +312,8 @@ async def add( the secret key sk-xxxxx Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability extra_headers: Send extra headers @@ -352,13 +324,8 @@ async def add( timeout: Override the client-level default timeout for this request, in seconds """ if not team_id: - raise ValueError( - f"Expected a non-empty value for `team_id` but received {team_id!r}" - ) - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } + raise ValueError(f"Expected a non-empty value for `team_id` but received {team_id!r}") + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} return await self._post( f"/team/{team_id}/callback", body=await async_maybe_transform( @@ -370,10 +337,7 @@ async def add( callback_add_params.CallbackAddParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/team/model.py b/src/hanzoai/resources/team/model.py new file mode 100644 index 000000000..d88758a2d --- /dev/null +++ b/src/hanzoai/resources/team/model.py @@ -0,0 +1,330 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, SequenceNotStr, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...types.team import model_add_params, model_remove_params +from ..._base_client import make_request_options + +__all__ = ["ModelResource", "AsyncModelResource"] + + +class ModelResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ModelResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return ModelResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ModelResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return ModelResourceWithStreamingResponse(self) + + def add( + self, + *, + models: SequenceNotStr[str], + team_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Add models to a team's allowed model list. + + Only proxy admin or team admin can + add models. + + Parameters: + + - team_id: str - Required. The team to add models to + - models: List[str] - Required. List of models to add to the team + + Example Request: + + ``` + curl --location 'http://0.0.0.0:4000/team/model/add' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ + "team_id": "team-1234", + "models": ["gpt-4", "claude-2"] + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/team/model/add", + body=maybe_transform( + { + "models": models, + "team_id": team_id, + }, + model_add_params.ModelAddParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def remove( + self, + *, + models: SequenceNotStr[str], + team_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Remove models from a team's allowed model list. + + Only proxy admin or team admin + can remove models. + + Parameters: + + - team_id: str - Required. The team to remove models from + - models: List[str] - Required. List of models to remove from the team + + Example Request: + + ``` + curl --location 'http://0.0.0.0:4000/team/model/delete' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ + "team_id": "team-1234", + "models": ["gpt-4"] + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/team/model/delete", + body=maybe_transform( + { + "models": models, + "team_id": team_id, + }, + model_remove_params.ModelRemoveParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncModelResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncModelResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncModelResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncModelResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncModelResourceWithStreamingResponse(self) + + async def add( + self, + *, + models: SequenceNotStr[str], + team_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Add models to a team's allowed model list. + + Only proxy admin or team admin can + add models. + + Parameters: + + - team_id: str - Required. The team to add models to + - models: List[str] - Required. List of models to add to the team + + Example Request: + + ``` + curl --location 'http://0.0.0.0:4000/team/model/add' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ + "team_id": "team-1234", + "models": ["gpt-4", "claude-2"] + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/team/model/add", + body=await async_maybe_transform( + { + "models": models, + "team_id": team_id, + }, + model_add_params.ModelAddParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def remove( + self, + *, + models: SequenceNotStr[str], + team_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Remove models from a team's allowed model list. + + Only proxy admin or team admin + can remove models. + + Parameters: + + - team_id: str - Required. The team to remove models from + - models: List[str] - Required. List of models to remove from the team + + Example Request: + + ``` + curl --location 'http://0.0.0.0:4000/team/model/delete' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ + "team_id": "team-1234", + "models": ["gpt-4"] + }' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/team/model/delete", + body=await async_maybe_transform( + { + "models": models, + "team_id": team_id, + }, + model_remove_params.ModelRemoveParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class ModelResourceWithRawResponse: + def __init__(self, model: ModelResource) -> None: + self._model = model + + self.add = to_raw_response_wrapper( + model.add, + ) + self.remove = to_raw_response_wrapper( + model.remove, + ) + + +class AsyncModelResourceWithRawResponse: + def __init__(self, model: AsyncModelResource) -> None: + self._model = model + + self.add = async_to_raw_response_wrapper( + model.add, + ) + self.remove = async_to_raw_response_wrapper( + model.remove, + ) + + +class ModelResourceWithStreamingResponse: + def __init__(self, model: ModelResource) -> None: + self._model = model + + self.add = to_streamed_response_wrapper( + model.add, + ) + self.remove = to_streamed_response_wrapper( + model.remove, + ) + + +class AsyncModelResourceWithStreamingResponse: + def __init__(self, model: AsyncModelResource) -> None: + self._model = model + + self.add = async_to_streamed_response_wrapper( + model.add, + ) + self.remove = async_to_streamed_response_wrapper( + model.remove, + ) diff --git a/pkg/hanzoai/resources/team/team.py b/src/hanzoai/resources/team/team.py similarity index 79% rename from pkg/hanzoai/resources/team/team.py rename to src/hanzoai/resources/team/team.py index eb04518f4..c51845016 100644 --- a/pkg/hanzoai/resources/team/team.py +++ b/src/hanzoai/resources/team/team.py @@ -1,8 +1,8 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations -from typing import List, Iterable, Optional +from typing import Iterable, Optional from typing_extensions import Literal import httpx @@ -28,12 +28,8 @@ team_update_member_params, team_list_available_params, ) -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - strip_not_given, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import maybe_transform, strip_not_given, async_maybe_transform from .callback import ( CallbackResource, AsyncCallbackResource, @@ -52,7 +48,7 @@ ) from ..._base_client import make_request_options from ...types.member_param import MemberParam -from ...types.lite_llm_team_table import HanzoTeamTable +from ...types.team_create_response import TeamCreateResponse from ...types.team_add_member_response import TeamAddMemberResponse from ...types.team_update_member_response import TeamUpdateMemberResponse @@ -90,30 +86,30 @@ def with_streaming_response(self) -> TeamResourceWithStreamingResponse: def create( self, *, - admins: Iterable[object] | NotGiven = NOT_GIVEN, - blocked: bool | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - guardrails: Optional[List[str]] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - members: Iterable[object] | NotGiven = NOT_GIVEN, - members_with_roles: Iterable[MemberParam] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_aliases: Optional[object] | NotGiven = NOT_GIVEN, - models: Iterable[object] | NotGiven = NOT_GIVEN, - organization_id: Optional[str] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - tags: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - team_alias: Optional[str] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, + admins: Iterable[object] | Omit = omit, + blocked: bool | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + guardrails: Optional[SequenceNotStr[str]] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + members: Iterable[object] | Omit = omit, + members_with_roles: Iterable[MemberParam] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_aliases: Optional[object] | Omit = omit, + models: Iterable[object] | Omit = omit, + organization_id: Optional[str] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + tags: Optional[Iterable[object]] | Omit = omit, + team_alias: Optional[str] | Omit = omit, + team_id: Optional[str] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, + llm_changed_by: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> HanzoTeamTable: + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TeamCreateResponse: """Allow users to create a new team. Apply user permissions to their team. @@ -147,9 +143,9 @@ def create( - members: Optional[List] - Control team members via `/team/member/add` and `/team/member/delete`. - tags: Optional[List[str]] - Tags for - [tracking spend](https://hanzo.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) + [tracking spend](https://llm.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) and/or doing - [tag-based routing](https://hanzo.vercel.app/docs/proxy/tag_routing). + [tag-based routing](https://llm.vercel.app/docs/proxy/tag_routing). - organization_id: Optional[str] - The organization id of the team. Default is None. Create via `/organization/new`. - model_aliases: Optional[dict] - Model aliases for the team. @@ -184,9 +180,8 @@ def create( ``` Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability extra_headers: Send extra headers @@ -196,10 +191,7 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} return self._post( "/team/new", body=maybe_transform( @@ -224,37 +216,34 @@ def create( team_create_params.TeamCreateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=HanzoTeamTable, + cast_to=TeamCreateResponse, ) def update( self, *, team_id: str, - blocked: Optional[bool] | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - guardrails: Optional[List[str]] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_aliases: Optional[object] | NotGiven = NOT_GIVEN, - models: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - organization_id: Optional[str] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - tags: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - team_alias: Optional[str] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, + blocked: Optional[bool] | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + guardrails: Optional[SequenceNotStr[str]] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_aliases: Optional[object] | Omit = omit, + models: Optional[Iterable[object]] | Omit = omit, + organization_id: Optional[str] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + tags: Optional[Iterable[object]] | Omit = omit, + team_alias: Optional[str] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, + llm_changed_by: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Use `/team/member_add` AND `/team/member/delete` to add/remove new team members @@ -266,8 +255,8 @@ def update( - team_id: str - The team id of the user. Required param. - team_alias: Optional[str] - User defined team alias - metadata: Optional[dict] - Metadata for team, store information for team. - Example metadata = {"team": "core-infra", "app": "app2", "email": - "ishaan@berri.ai" } + Example metadata = {"team": "core-infra", "app": "app2", "email": "z@hanzo.ai" + } - tpm_limit: Optional[int] - The TPM (Tokens Per Minute) limit for this team - all keys with this team_id will have at max this TPM limit - rpm_limit: Optional[int] - The RPM (Requests Per Minute) limit for this team - @@ -282,16 +271,16 @@ def update( - blocked: bool - Flag indicating if the team is blocked or not - will stop all calls from keys with this team_id. - tags: Optional[List[str]] - Tags for - [tracking spend](https://hanzo.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) + [tracking spend](https://llm.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) and/or doing - [tag-based routing](https://hanzo.vercel.app/docs/proxy/tag_routing). + [tag-based routing](https://llm.vercel.app/docs/proxy/tag_routing). - organization_id: Optional[str] - The organization id of the team. Default is None. Create via `/organization/new`. - model_aliases: Optional[dict] - Model aliases for the team. [Docs](https://docs.hanzo.ai/docs/proxy/team_based_routing#create-team-with-model-alias) - guardrails: Optional[List[str]] - Guardrails for the team. - [Docs](https://docs.hanzo.ai/docs/proxy/guardrails) Example - update team - TPM Limit + [Docs](https://docs.hanzo.ai/docs/proxy/guardrails) Example - update team TPM + Limit ``` curl --location 'http://0.0.0.0:4000/team/update' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data-raw '{ @@ -310,9 +299,8 @@ def update( ``` Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability extra_headers: Send extra headers @@ -322,10 +310,7 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} return self._post( "/team/update", body=maybe_transform( @@ -347,10 +332,7 @@ def update( team_update_params.TeamUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -358,14 +340,14 @@ def update( def list( self, *, - organization_id: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, + organization_id: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ ``` @@ -412,14 +394,14 @@ def list( def delete( self, *, - team_ids: List[str], - hanzo_changed_by: str | NotGiven = NOT_GIVEN, + team_ids: SequenceNotStr[str], + llm_changed_by: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ delete team and associated team keys @@ -436,9 +418,8 @@ def delete( ``` Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability extra_headers: Send extra headers @@ -448,20 +429,12 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} return self._post( "/team/delete", - body=maybe_transform( - {"team_ids": team_ids}, team_delete_params.TeamDeleteParams - ), + body=maybe_transform({"team_ids": team_ids}, team_delete_params.TeamDeleteParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -471,13 +444,13 @@ def add_member( *, member: team_add_member_params.Member, team_id: str, - max_budget_in_team: Optional[float] | NotGiven = NOT_GIVEN, + max_budget_in_team: Optional[float] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TeamAddMemberResponse: """ [BETA] @@ -490,7 +463,7 @@ def add_member( ``` - curl -X POST 'http://0.0.0.0:4000/team/member_add' -H 'Authorization: Bearer sk-1234' -H 'Content-Type: application/json' -d '{"team_id": "45e3e396-ee08-4a61-a88e-16b3ce7e0849", "member": {"role": "user", "user_id": "krrish247652@berri.ai"}}' + curl -X POST 'http://0.0.0.0:4000/team/member_add' -H 'Authorization: Bearer sk-1234' -H 'Content-Type: application/json' -d '{"team_id": "45e3e396-ee08-4a61-a88e-16b3ce7e0849", "member": {"role": "user", "user_id": "dev247652@hanzo.ai"}}' ``` @@ -514,10 +487,7 @@ def add_member( team_add_member_params.TeamAddMemberParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=TeamAddMemberResponse, ) @@ -531,7 +501,7 @@ def block( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Blocks all calls from keys with this team id. @@ -563,14 +533,9 @@ def block( """ return self._post( "/team/block", - body=maybe_transform( - {"team_id": team_id}, team_block_params.TeamBlockParams - ), + body=maybe_transform({"team_id": team_id}, team_block_params.TeamBlockParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -584,7 +549,7 @@ def disable_logging( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Disable all logging callbacks for a team @@ -609,16 +574,11 @@ def disable_logging( timeout: Override the client-level default timeout for this request, in seconds """ if not team_id: - raise ValueError( - f"Expected a non-empty value for `team_id` but received {team_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `team_id` but received {team_id!r}") return self._post( f"/team/{team_id}/disable_logging", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -626,13 +586,13 @@ def disable_logging( def list_available( self, *, - response_model: object | NotGiven = NOT_GIVEN, + response_model: object | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ List Available Teams @@ -654,8 +614,7 @@ def list_available( extra_body=extra_body, timeout=timeout, query=maybe_transform( - {"response_model": response_model}, - team_list_available_params.TeamListAvailableParams, + {"response_model": response_model}, team_list_available_params.TeamListAvailableParams ), ), cast_to=object, @@ -665,14 +624,14 @@ def remove_member( self, *, team_id: str, - user_email: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, + user_email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [BETA] @@ -687,7 +646,7 @@ def remove_member( -H 'Content-Type: application/json' -d '{ "team_id": "45e3e396-ee08-4a61-a88e-16b3ce7e0849", - "user_id": "krrish247652@berri.ai" + "user_id": "dev247652@hanzo.ai" }' ``` @@ -711,10 +670,7 @@ def remove_member( team_remove_member_params.TeamRemoveMemberParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -722,13 +678,13 @@ def remove_member( def retrieve_info( self, *, - team_id: str | NotGiven = NOT_GIVEN, + team_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """get info on team + related keys @@ -760,10 +716,7 @@ def retrieve_info( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform( - {"team_id": team_id}, - team_retrieve_info_params.TeamRetrieveInfoParams, - ), + query=maybe_transform({"team_id": team_id}, team_retrieve_info_params.TeamRetrieveInfoParams), ), cast_to=object, ) @@ -777,7 +730,7 @@ def unblock( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Blocks all calls from keys with this team id. @@ -805,14 +758,9 @@ def unblock( """ return self._post( "/team/unblock", - body=maybe_transform( - {"team_id": team_id}, team_unblock_params.TeamUnblockParams - ), + body=maybe_transform({"team_id": team_id}, team_unblock_params.TeamUnblockParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -821,16 +769,16 @@ def update_member( self, *, team_id: str, - max_budget_in_team: Optional[float] | NotGiven = NOT_GIVEN, - role: Optional[Literal["admin", "user"]] | NotGiven = NOT_GIVEN, - user_email: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, + max_budget_in_team: Optional[float] | Omit = omit, + role: Optional[Literal["admin", "user"]] | Omit = omit, + user_email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TeamUpdateMemberResponse: """ [BETA] @@ -859,10 +807,7 @@ def update_member( team_update_member_params.TeamUpdateMemberParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=TeamUpdateMemberResponse, ) @@ -899,30 +844,30 @@ def with_streaming_response(self) -> AsyncTeamResourceWithStreamingResponse: async def create( self, *, - admins: Iterable[object] | NotGiven = NOT_GIVEN, - blocked: bool | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - guardrails: Optional[List[str]] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - members: Iterable[object] | NotGiven = NOT_GIVEN, - members_with_roles: Iterable[MemberParam] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_aliases: Optional[object] | NotGiven = NOT_GIVEN, - models: Iterable[object] | NotGiven = NOT_GIVEN, - organization_id: Optional[str] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - tags: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - team_alias: Optional[str] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, + admins: Iterable[object] | Omit = omit, + blocked: bool | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + guardrails: Optional[SequenceNotStr[str]] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + members: Iterable[object] | Omit = omit, + members_with_roles: Iterable[MemberParam] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_aliases: Optional[object] | Omit = omit, + models: Iterable[object] | Omit = omit, + organization_id: Optional[str] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + tags: Optional[Iterable[object]] | Omit = omit, + team_alias: Optional[str] | Omit = omit, + team_id: Optional[str] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, + llm_changed_by: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> HanzoTeamTable: + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TeamCreateResponse: """Allow users to create a new team. Apply user permissions to their team. @@ -956,9 +901,9 @@ async def create( - members: Optional[List] - Control team members via `/team/member/add` and `/team/member/delete`. - tags: Optional[List[str]] - Tags for - [tracking spend](https://hanzo.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) + [tracking spend](https://llm.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) and/or doing - [tag-based routing](https://hanzo.vercel.app/docs/proxy/tag_routing). + [tag-based routing](https://llm.vercel.app/docs/proxy/tag_routing). - organization_id: Optional[str] - The organization id of the team. Default is None. Create via `/organization/new`. - model_aliases: Optional[dict] - Model aliases for the team. @@ -993,9 +938,8 @@ async def create( ``` Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability extra_headers: Send extra headers @@ -1005,10 +949,7 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} return await self._post( "/team/new", body=await async_maybe_transform( @@ -1033,37 +974,34 @@ async def create( team_create_params.TeamCreateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=HanzoTeamTable, + cast_to=TeamCreateResponse, ) async def update( self, *, team_id: str, - blocked: Optional[bool] | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - guardrails: Optional[List[str]] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_aliases: Optional[object] | NotGiven = NOT_GIVEN, - models: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - organization_id: Optional[str] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - tags: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - team_alias: Optional[str] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - hanzo_changed_by: str | NotGiven = NOT_GIVEN, + blocked: Optional[bool] | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + guardrails: Optional[SequenceNotStr[str]] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_aliases: Optional[object] | Omit = omit, + models: Optional[Iterable[object]] | Omit = omit, + organization_id: Optional[str] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + tags: Optional[Iterable[object]] | Omit = omit, + team_alias: Optional[str] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, + llm_changed_by: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Use `/team/member_add` AND `/team/member/delete` to add/remove new team members @@ -1075,8 +1013,8 @@ async def update( - team_id: str - The team id of the user. Required param. - team_alias: Optional[str] - User defined team alias - metadata: Optional[dict] - Metadata for team, store information for team. - Example metadata = {"team": "core-infra", "app": "app2", "email": - "ishaan@berri.ai" } + Example metadata = {"team": "core-infra", "app": "app2", "email": "z@hanzo.ai" + } - tpm_limit: Optional[int] - The TPM (Tokens Per Minute) limit for this team - all keys with this team_id will have at max this TPM limit - rpm_limit: Optional[int] - The RPM (Requests Per Minute) limit for this team - @@ -1091,16 +1029,16 @@ async def update( - blocked: bool - Flag indicating if the team is blocked or not - will stop all calls from keys with this team_id. - tags: Optional[List[str]] - Tags for - [tracking spend](https://hanzo.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) + [tracking spend](https://llm.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) and/or doing - [tag-based routing](https://hanzo.vercel.app/docs/proxy/tag_routing). + [tag-based routing](https://llm.vercel.app/docs/proxy/tag_routing). - organization_id: Optional[str] - The organization id of the team. Default is None. Create via `/organization/new`. - model_aliases: Optional[dict] - Model aliases for the team. [Docs](https://docs.hanzo.ai/docs/proxy/team_based_routing#create-team-with-model-alias) - guardrails: Optional[List[str]] - Guardrails for the team. - [Docs](https://docs.hanzo.ai/docs/proxy/guardrails) Example - update team - TPM Limit + [Docs](https://docs.hanzo.ai/docs/proxy/guardrails) Example - update team TPM + Limit ``` curl --location 'http://0.0.0.0:4000/team/update' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data-raw '{ @@ -1119,9 +1057,8 @@ async def update( ``` Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability extra_headers: Send extra headers @@ -1131,10 +1068,7 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} return await self._post( "/team/update", body=await async_maybe_transform( @@ -1156,10 +1090,7 @@ async def update( team_update_params.TeamUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -1167,14 +1098,14 @@ async def update( async def list( self, *, - organization_id: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, + organization_id: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ ``` @@ -1221,14 +1152,14 @@ async def list( async def delete( self, *, - team_ids: List[str], - hanzo_changed_by: str | NotGiven = NOT_GIVEN, + team_ids: SequenceNotStr[str], + llm_changed_by: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ delete team and associated team keys @@ -1245,9 +1176,8 @@ async def delete( ``` Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability extra_headers: Send extra headers @@ -1257,20 +1187,12 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} return await self._post( "/team/delete", - body=await async_maybe_transform( - {"team_ids": team_ids}, team_delete_params.TeamDeleteParams - ), + body=await async_maybe_transform({"team_ids": team_ids}, team_delete_params.TeamDeleteParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -1280,13 +1202,13 @@ async def add_member( *, member: team_add_member_params.Member, team_id: str, - max_budget_in_team: Optional[float] | NotGiven = NOT_GIVEN, + max_budget_in_team: Optional[float] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TeamAddMemberResponse: """ [BETA] @@ -1299,7 +1221,7 @@ async def add_member( ``` - curl -X POST 'http://0.0.0.0:4000/team/member_add' -H 'Authorization: Bearer sk-1234' -H 'Content-Type: application/json' -d '{"team_id": "45e3e396-ee08-4a61-a88e-16b3ce7e0849", "member": {"role": "user", "user_id": "krrish247652@berri.ai"}}' + curl -X POST 'http://0.0.0.0:4000/team/member_add' -H 'Authorization: Bearer sk-1234' -H 'Content-Type: application/json' -d '{"team_id": "45e3e396-ee08-4a61-a88e-16b3ce7e0849", "member": {"role": "user", "user_id": "dev247652@hanzo.ai"}}' ``` @@ -1323,10 +1245,7 @@ async def add_member( team_add_member_params.TeamAddMemberParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=TeamAddMemberResponse, ) @@ -1340,7 +1259,7 @@ async def block( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Blocks all calls from keys with this team id. @@ -1372,14 +1291,9 @@ async def block( """ return await self._post( "/team/block", - body=await async_maybe_transform( - {"team_id": team_id}, team_block_params.TeamBlockParams - ), + body=await async_maybe_transform({"team_id": team_id}, team_block_params.TeamBlockParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -1393,7 +1307,7 @@ async def disable_logging( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Disable all logging callbacks for a team @@ -1418,16 +1332,11 @@ async def disable_logging( timeout: Override the client-level default timeout for this request, in seconds """ if not team_id: - raise ValueError( - f"Expected a non-empty value for `team_id` but received {team_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `team_id` but received {team_id!r}") return await self._post( f"/team/{team_id}/disable_logging", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -1435,13 +1344,13 @@ async def disable_logging( async def list_available( self, *, - response_model: object | NotGiven = NOT_GIVEN, + response_model: object | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ List Available Teams @@ -1463,8 +1372,7 @@ async def list_available( extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( - {"response_model": response_model}, - team_list_available_params.TeamListAvailableParams, + {"response_model": response_model}, team_list_available_params.TeamListAvailableParams ), ), cast_to=object, @@ -1474,14 +1382,14 @@ async def remove_member( self, *, team_id: str, - user_email: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, + user_email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [BETA] @@ -1496,7 +1404,7 @@ async def remove_member( -H 'Content-Type: application/json' -d '{ "team_id": "45e3e396-ee08-4a61-a88e-16b3ce7e0849", - "user_id": "krrish247652@berri.ai" + "user_id": "dev247652@hanzo.ai" }' ``` @@ -1520,10 +1428,7 @@ async def remove_member( team_remove_member_params.TeamRemoveMemberParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -1531,13 +1436,13 @@ async def remove_member( async def retrieve_info( self, *, - team_id: str | NotGiven = NOT_GIVEN, + team_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """get info on team + related keys @@ -1570,8 +1475,7 @@ async def retrieve_info( extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( - {"team_id": team_id}, - team_retrieve_info_params.TeamRetrieveInfoParams, + {"team_id": team_id}, team_retrieve_info_params.TeamRetrieveInfoParams ), ), cast_to=object, @@ -1586,7 +1490,7 @@ async def unblock( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Blocks all calls from keys with this team id. @@ -1614,14 +1518,9 @@ async def unblock( """ return await self._post( "/team/unblock", - body=await async_maybe_transform( - {"team_id": team_id}, team_unblock_params.TeamUnblockParams - ), + body=await async_maybe_transform({"team_id": team_id}, team_unblock_params.TeamUnblockParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -1630,16 +1529,16 @@ async def update_member( self, *, team_id: str, - max_budget_in_team: Optional[float] | NotGiven = NOT_GIVEN, - role: Optional[Literal["admin", "user"]] | NotGiven = NOT_GIVEN, - user_email: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, + max_budget_in_team: Optional[float] | Omit = omit, + role: Optional[Literal["admin", "user"]] | Omit = omit, + user_email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TeamUpdateMemberResponse: """ [BETA] @@ -1668,10 +1567,7 @@ async def update_member( team_update_member_params.TeamUpdateMemberParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=TeamUpdateMemberResponse, ) diff --git a/pkg/hanzoai/resources/test.py b/src/hanzoai/resources/test.py similarity index 89% rename from pkg/hanzoai/resources/test.py rename to src/hanzoai/resources/test.py index ec80639bb..390a3e612 100644 --- a/pkg/hanzoai/resources/test.py +++ b/src/hanzoai/resources/test.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -48,7 +48,7 @@ def ping( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [DEPRECATED] use `/health/liveliness` instead. @@ -62,10 +62,7 @@ def ping( return self._get( "/test", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -99,7 +96,7 @@ async def ping( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [DEPRECATED] use `/health/liveliness` instead. @@ -113,10 +110,7 @@ async def ping( return await self._get( "/test", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/resources/threads/__init__.py b/src/hanzoai/resources/threads/__init__.py new file mode 100644 index 000000000..d56b6d057 --- /dev/null +++ b/src/hanzoai/resources/threads/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .runs import ( + RunsResource, + AsyncRunsResource, + RunsResourceWithRawResponse, + AsyncRunsResourceWithRawResponse, + RunsResourceWithStreamingResponse, + AsyncRunsResourceWithStreamingResponse, +) +from .threads import ( + ThreadsResource, + AsyncThreadsResource, + ThreadsResourceWithRawResponse, + AsyncThreadsResourceWithRawResponse, + ThreadsResourceWithStreamingResponse, + AsyncThreadsResourceWithStreamingResponse, +) +from .messages import ( + MessagesResource, + AsyncMessagesResource, + MessagesResourceWithRawResponse, + AsyncMessagesResourceWithRawResponse, + MessagesResourceWithStreamingResponse, + AsyncMessagesResourceWithStreamingResponse, +) + +__all__ = [ + "MessagesResource", + "AsyncMessagesResource", + "MessagesResourceWithRawResponse", + "AsyncMessagesResourceWithRawResponse", + "MessagesResourceWithStreamingResponse", + "AsyncMessagesResourceWithStreamingResponse", + "RunsResource", + "AsyncRunsResource", + "RunsResourceWithRawResponse", + "AsyncRunsResourceWithRawResponse", + "RunsResourceWithStreamingResponse", + "AsyncRunsResourceWithStreamingResponse", + "ThreadsResource", + "AsyncThreadsResource", + "ThreadsResourceWithRawResponse", + "AsyncThreadsResourceWithRawResponse", + "ThreadsResourceWithStreamingResponse", + "AsyncThreadsResourceWithStreamingResponse", +] diff --git a/pkg/hanzoai/resources/threads/messages.py b/src/hanzoai/resources/threads/messages.py similarity index 83% rename from pkg/hanzoai/resources/threads/messages.py rename to src/hanzoai/resources/threads/messages.py index ec1d007c0..113133705 100644 --- a/pkg/hanzoai/resources/threads/messages.py +++ b/src/hanzoai/resources/threads/messages.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Query, Headers, NotGiven, not_given from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -47,7 +47,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Create a message. @@ -65,16 +65,11 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ if not thread_id: - raise ValueError( - f"Expected a non-empty value for `thread_id` but received {thread_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") return self._post( f"/v1/threads/{thread_id}/messages", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -88,7 +83,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Returns a list of messages for a given thread. @@ -106,16 +101,11 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ if not thread_id: - raise ValueError( - f"Expected a non-empty value for `thread_id` but received {thread_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") return self._get( f"/v1/threads/{thread_id}/messages", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -150,7 +140,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Create a message. @@ -168,16 +158,11 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ if not thread_id: - raise ValueError( - f"Expected a non-empty value for `thread_id` but received {thread_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") return await self._post( f"/v1/threads/{thread_id}/messages", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -191,7 +176,7 @@ async def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Returns a list of messages for a given thread. @@ -209,16 +194,11 @@ async def list( timeout: Override the client-level default timeout for this request, in seconds """ if not thread_id: - raise ValueError( - f"Expected a non-empty value for `thread_id` but received {thread_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") return await self._get( f"/v1/threads/{thread_id}/messages", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/pkg/hanzoai/resources/threads/runs.py b/src/hanzoai/resources/threads/runs.py similarity index 86% rename from pkg/hanzoai/resources/threads/runs.py rename to src/hanzoai/resources/threads/runs.py index 94b11d21d..107fc3b64 100644 --- a/pkg/hanzoai/resources/threads/runs.py +++ b/src/hanzoai/resources/threads/runs.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Query, Headers, NotGiven, not_given from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -47,7 +47,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Create a run. @@ -64,16 +64,11 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ if not thread_id: - raise ValueError( - f"Expected a non-empty value for `thread_id` but received {thread_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") return self._post( f"/v1/threads/{thread_id}/runs", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -108,7 +103,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Create a run. @@ -125,16 +120,11 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ if not thread_id: - raise ValueError( - f"Expected a non-empty value for `thread_id` but received {thread_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") return await self._post( f"/v1/threads/{thread_id}/runs", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/pkg/hanzoai/resources/threads/threads.py b/src/hanzoai/resources/threads/threads.py similarity index 87% rename from pkg/hanzoai/resources/threads/threads.py rename to src/hanzoai/resources/threads/threads.py index e353ca6b4..e9e61a191 100644 --- a/pkg/hanzoai/resources/threads/threads.py +++ b/src/hanzoai/resources/threads/threads.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -12,7 +12,7 @@ RunsResourceWithStreamingResponse, AsyncRunsResourceWithStreamingResponse, ) -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Query, Headers, NotGiven, not_given from .messages import ( MessagesResource, AsyncMessagesResource, @@ -70,7 +70,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Create a thread. @@ -81,10 +81,7 @@ def create( return self._post( "/v1/threads", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -98,7 +95,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Retrieves a thread. @@ -115,16 +112,11 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not thread_id: - raise ValueError( - f"Expected a non-empty value for `thread_id` but received {thread_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") return self._get( f"/v1/threads/{thread_id}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -166,7 +158,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Create a thread. @@ -177,10 +169,7 @@ async def create( return await self._post( "/v1/threads", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -194,7 +183,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Retrieves a thread. @@ -211,16 +200,11 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not thread_id: - raise ValueError( - f"Expected a non-empty value for `thread_id` but received {thread_id!r}" - ) + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") return await self._get( f"/v1/threads/{thread_id}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/pkg/hanzoai/resources/user.py b/src/hanzoai/resources/user.py similarity index 75% rename from pkg/hanzoai/resources/user.py rename to src/hanzoai/resources/user.py index ad6b4b761..a1a4c2aa8 100644 --- a/pkg/hanzoai/resources/user.py +++ b/src/hanzoai/resources/user.py @@ -1,8 +1,8 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations -from typing import List, Iterable, Optional +from typing import Iterable, Optional from typing_extensions import Literal import httpx @@ -14,12 +14,8 @@ user_update_params, user_retrieve_info_params, ) -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - maybe_transform, - strip_not_given, - async_maybe_transform, -) +from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from .._utils import maybe_transform, strip_not_given, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -57,55 +53,46 @@ def with_streaming_response(self) -> UserResourceWithStreamingResponse: def create( self, *, - aliases: Optional[object] | NotGiven = NOT_GIVEN, - allowed_cache_controls: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - auto_create_key: bool | NotGiven = NOT_GIVEN, - blocked: Optional[bool] | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - config: Optional[object] | NotGiven = NOT_GIVEN, - duration: Optional[str] | NotGiven = NOT_GIVEN, - guardrails: Optional[List[str]] | NotGiven = NOT_GIVEN, - key_alias: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_max_budget: Optional[object] | NotGiven = NOT_GIVEN, - model_rpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - model_tpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - models: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - permissions: Optional[object] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - send_invite_email: Optional[bool] | NotGiven = NOT_GIVEN, - spend: Optional[float] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - teams: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - user_alias: Optional[str] | NotGiven = NOT_GIVEN, - user_email: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - user_role: ( - Optional[ - Literal[ - "proxy_admin", - "proxy_admin_viewer", - "internal_user", - "internal_user_viewer", - ] - ] - | NotGiven - ) = NOT_GIVEN, + aliases: Optional[object] | Omit = omit, + allowed_cache_controls: Optional[Iterable[object]] | Omit = omit, + auto_create_key: bool | Omit = omit, + blocked: Optional[bool] | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + config: Optional[object] | Omit = omit, + duration: Optional[str] | Omit = omit, + guardrails: Optional[SequenceNotStr[str]] | Omit = omit, + key_alias: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_max_budget: Optional[object] | Omit = omit, + model_rpm_limit: Optional[object] | Omit = omit, + model_tpm_limit: Optional[object] | Omit = omit, + models: Optional[Iterable[object]] | Omit = omit, + permissions: Optional[object] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + send_invite_email: Optional[bool] | Omit = omit, + spend: Optional[float] | Omit = omit, + team_id: Optional[str] | Omit = omit, + teams: Optional[Iterable[object]] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, + user_alias: Optional[str] | Omit = omit, + user_email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, + user_role: Optional[Literal["proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer"]] + | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> UserCreateResponse: """Use this to create a new INTERNAL user with a budget. Internal Users can access - Hanzo Admin UI to make keys, request access to models. This creates a new user - and generates a new api key for the new user. The new api key is returned. + LLM Admin UI to make keys, request access to models. This creates a new user and + generates a new api key for the new user. The new api key is returned. Returns user id, budget + new key. @@ -121,7 +108,7 @@ def create( - user_role: Optional[str] - Specify a user role - "proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer", "team", "customer". Info about each role here: - `https://github.com/BerriAI/hanzo/hanzo/proxy/_types.py#L20` + `https://github.com/hanzoai/llm/llm/proxy/_types.py#L20` - max_budget: Optional[float] - Specify max budget for a given user. - budget_duration: Optional[str] - Budget is reset at the end of specified duration. If not set, budget is never reset. You can set duration as seconds @@ -136,7 +123,7 @@ def create( - auto_create_key: bool - Default=True. Flag used for returning a key as part of the /user/new response - aliases: Optional[dict] - Model aliases for the user - - [Docs](https://hanzo.vercel.app/docs/proxy/virtual_keys#model-aliases) + [Docs](https://llm.vercel.app/docs/proxy/virtual_keys#model-aliases) - config: Optional[dict] - [DEPRECATED PARAM] User-specific config. - allowed_cache_controls: Optional[list] - List of allowed cache control values. Example - ["no-cache", "no-store"]. See all values - @@ -147,8 +134,8 @@ def create( - permissions: Optional[dict] - [Not Implemented Yet] User-specific permissions, eg. turning off pii masking. - metadata: Optional[dict] - Metadata for user, store information for user. - Example metadata = {"team": "core-infra", "app": "app2", "email": - "ishaan@berri.ai" } + Example metadata = {"team": "core-infra", "app": "app2", "email": "z@hanzo.ai" + } - max_parallel_requests: Optional[int] - Rate limit a user based on the number of parallel requests. Raises 429 error, if user's parallel requests > x. - soft_budget: Optional[float] - Get alerts when user crosses given budget, @@ -230,10 +217,7 @@ def create( user_create_params.UserCreateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=UserCreateResponse, ) @@ -241,53 +225,44 @@ def create( def update( self, *, - aliases: Optional[object] | NotGiven = NOT_GIVEN, - allowed_cache_controls: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - blocked: Optional[bool] | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - config: Optional[object] | NotGiven = NOT_GIVEN, - duration: Optional[str] | NotGiven = NOT_GIVEN, - guardrails: Optional[List[str]] | NotGiven = NOT_GIVEN, - key_alias: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_max_budget: Optional[object] | NotGiven = NOT_GIVEN, - model_rpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - model_tpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - models: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - password: Optional[str] | NotGiven = NOT_GIVEN, - permissions: Optional[object] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - spend: Optional[float] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - user_email: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - user_role: ( - Optional[ - Literal[ - "proxy_admin", - "proxy_admin_viewer", - "internal_user", - "internal_user_viewer", - ] - ] - | NotGiven - ) = NOT_GIVEN, + aliases: Optional[object] | Omit = omit, + allowed_cache_controls: Optional[Iterable[object]] | Omit = omit, + blocked: Optional[bool] | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + config: Optional[object] | Omit = omit, + duration: Optional[str] | Omit = omit, + guardrails: Optional[SequenceNotStr[str]] | Omit = omit, + key_alias: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_max_budget: Optional[object] | Omit = omit, + model_rpm_limit: Optional[object] | Omit = omit, + model_tpm_limit: Optional[object] | Omit = omit, + models: Optional[Iterable[object]] | Omit = omit, + password: Optional[str] | Omit = omit, + permissions: Optional[object] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + spend: Optional[float] | Omit = omit, + team_id: Optional[str] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, + user_email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, + user_role: Optional[Literal["proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer"]] + | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Example curl ``` curl --location 'http://0.0.0.0:4000/user/update' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "user_id": "test-hanzo-user-4", + "user_id": "test-llm-user-4", "user_role": "proxy_admin_viewer" }' ``` @@ -301,7 +276,7 @@ def update( user_role: Optional[str] - Specify a user role - "proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer", "team", "customer". Info about each role here: - `https://github.com/BerriAI/hanzo/hanzo/proxy/_types.py#L20` - max_budget: + `https://github.com/hanzoai/llm/llm/proxy/_types.py#L20` - max_budget: Optional[float] - Specify max budget for a given user. - budget_duration: Optional[str] - Budget is reset at the end of specified duration. If not set, budget is never reset. You can set duration as seconds ("30s"), minutes ("30m"), @@ -312,8 +287,8 @@ def update( (Requests per minute) - auto_create_key: bool - Default=True. Flag used for returning a key as part of the /user/new response - aliases: Optional[dict] - Model aliases for the user - - [Docs](https://hanzo.vercel.app/docs/proxy/virtual_keys#model-aliases) - - config: Optional[dict] - [DEPRECATED PARAM] User-specific config. - + [Docs](https://llm.vercel.app/docs/proxy/virtual_keys#model-aliases) - config: + Optional[dict] - [DEPRECATED PARAM] User-specific config. - allowed_cache_controls: Optional[list] - List of allowed cache control values. Example - ["no-cache", "no-store"]. See all values - https://docs.hanzo.ai/docs/proxy/caching#turn-on--off-caching-per-request- - @@ -322,7 +297,7 @@ def update( guardrails for the user - permissions: Optional[dict] - [Not Implemented Yet] User-specific permissions, eg. turning off pii masking. - metadata: Optional[dict] - Metadata for user, store information for user. Example metadata - = {"team": "core-infra", "app": "app2", "email": "ishaan@berri.ai" } - + = {"team": "core-infra", "app": "app2", "email": "z@hanzo.ai" } - max_parallel_requests: Optional[int] - Rate limit a user based on the number of parallel requests. Raises 429 error, if user's parallel requests > x. - soft_budget: Optional[float] - Get alerts when user crosses given budget, @@ -381,10 +356,7 @@ def update( user_update_params.UserUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -392,16 +364,16 @@ def update( def list( self, *, - page: int | NotGiven = NOT_GIVEN, - page_size: int | NotGiven = NOT_GIVEN, - role: Optional[str] | NotGiven = NOT_GIVEN, - user_ids: Optional[str] | NotGiven = NOT_GIVEN, + page: int | Omit = omit, + page_size: int | Omit = omit, + role: Optional[str] | Omit = omit, + user_ids: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Get a paginated list of users, optionally filtered by role. @@ -462,14 +434,14 @@ def list( def delete( self, *, - user_ids: List[str], - hanzo_changed_by: str | NotGiven = NOT_GIVEN, + user_ids: SequenceNotStr[str], + llm_changed_by: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ delete user and associated user keys @@ -488,9 +460,8 @@ def delete( - user_ids: List[str] - The list of user id's to be deleted. Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability extra_headers: Send extra headers @@ -500,20 +471,12 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} return self._post( "/user/delete", - body=maybe_transform( - {"user_ids": user_ids}, user_delete_params.UserDeleteParams - ), + body=maybe_transform({"user_ids": user_ids}, user_delete_params.UserDeleteParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -521,13 +484,13 @@ def delete( def retrieve_info( self, *, - user_id: Optional[str] | NotGiven = NOT_GIVEN, + user_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [10/07/2024] Note: To get all users (+pagination), use `/user/list` endpoint. @@ -537,7 +500,7 @@ def retrieve_info( Example request ``` - curl -X GET 'http://localhost:4000/user/info?user_id=krrish7%40berri.ai' --header 'Authorization: Bearer sk-1234' + curl -X GET 'http://localhost:4000/user/info?user_id=dev7%40hanzo.ai' --header 'Authorization: Bearer sk-1234' ``` Args: @@ -558,10 +521,7 @@ def retrieve_info( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform( - {"user_id": user_id}, - user_retrieve_info_params.UserRetrieveInfoParams, - ), + query=maybe_transform({"user_id": user_id}, user_retrieve_info_params.UserRetrieveInfoParams), ), cast_to=object, ) @@ -590,55 +550,46 @@ def with_streaming_response(self) -> AsyncUserResourceWithStreamingResponse: async def create( self, *, - aliases: Optional[object] | NotGiven = NOT_GIVEN, - allowed_cache_controls: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - auto_create_key: bool | NotGiven = NOT_GIVEN, - blocked: Optional[bool] | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - config: Optional[object] | NotGiven = NOT_GIVEN, - duration: Optional[str] | NotGiven = NOT_GIVEN, - guardrails: Optional[List[str]] | NotGiven = NOT_GIVEN, - key_alias: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_max_budget: Optional[object] | NotGiven = NOT_GIVEN, - model_rpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - model_tpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - models: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - permissions: Optional[object] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - send_invite_email: Optional[bool] | NotGiven = NOT_GIVEN, - spend: Optional[float] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - teams: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - user_alias: Optional[str] | NotGiven = NOT_GIVEN, - user_email: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - user_role: ( - Optional[ - Literal[ - "proxy_admin", - "proxy_admin_viewer", - "internal_user", - "internal_user_viewer", - ] - ] - | NotGiven - ) = NOT_GIVEN, + aliases: Optional[object] | Omit = omit, + allowed_cache_controls: Optional[Iterable[object]] | Omit = omit, + auto_create_key: bool | Omit = omit, + blocked: Optional[bool] | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + config: Optional[object] | Omit = omit, + duration: Optional[str] | Omit = omit, + guardrails: Optional[SequenceNotStr[str]] | Omit = omit, + key_alias: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_max_budget: Optional[object] | Omit = omit, + model_rpm_limit: Optional[object] | Omit = omit, + model_tpm_limit: Optional[object] | Omit = omit, + models: Optional[Iterable[object]] | Omit = omit, + permissions: Optional[object] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + send_invite_email: Optional[bool] | Omit = omit, + spend: Optional[float] | Omit = omit, + team_id: Optional[str] | Omit = omit, + teams: Optional[Iterable[object]] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, + user_alias: Optional[str] | Omit = omit, + user_email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, + user_role: Optional[Literal["proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer"]] + | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> UserCreateResponse: """Use this to create a new INTERNAL user with a budget. Internal Users can access - Hanzo Admin UI to make keys, request access to models. This creates a new user - and generates a new api key for the new user. The new api key is returned. + LLM Admin UI to make keys, request access to models. This creates a new user and + generates a new api key for the new user. The new api key is returned. Returns user id, budget + new key. @@ -654,7 +605,7 @@ async def create( - user_role: Optional[str] - Specify a user role - "proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer", "team", "customer". Info about each role here: - `https://github.com/BerriAI/hanzo/hanzo/proxy/_types.py#L20` + `https://github.com/hanzoai/llm/llm/proxy/_types.py#L20` - max_budget: Optional[float] - Specify max budget for a given user. - budget_duration: Optional[str] - Budget is reset at the end of specified duration. If not set, budget is never reset. You can set duration as seconds @@ -669,7 +620,7 @@ async def create( - auto_create_key: bool - Default=True. Flag used for returning a key as part of the /user/new response - aliases: Optional[dict] - Model aliases for the user - - [Docs](https://hanzo.vercel.app/docs/proxy/virtual_keys#model-aliases) + [Docs](https://llm.vercel.app/docs/proxy/virtual_keys#model-aliases) - config: Optional[dict] - [DEPRECATED PARAM] User-specific config. - allowed_cache_controls: Optional[list] - List of allowed cache control values. Example - ["no-cache", "no-store"]. See all values - @@ -680,8 +631,8 @@ async def create( - permissions: Optional[dict] - [Not Implemented Yet] User-specific permissions, eg. turning off pii masking. - metadata: Optional[dict] - Metadata for user, store information for user. - Example metadata = {"team": "core-infra", "app": "app2", "email": - "ishaan@berri.ai" } + Example metadata = {"team": "core-infra", "app": "app2", "email": "z@hanzo.ai" + } - max_parallel_requests: Optional[int] - Rate limit a user based on the number of parallel requests. Raises 429 error, if user's parallel requests > x. - soft_budget: Optional[float] - Get alerts when user crosses given budget, @@ -763,10 +714,7 @@ async def create( user_create_params.UserCreateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=UserCreateResponse, ) @@ -774,53 +722,44 @@ async def create( async def update( self, *, - aliases: Optional[object] | NotGiven = NOT_GIVEN, - allowed_cache_controls: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - blocked: Optional[bool] | NotGiven = NOT_GIVEN, - budget_duration: Optional[str] | NotGiven = NOT_GIVEN, - config: Optional[object] | NotGiven = NOT_GIVEN, - duration: Optional[str] | NotGiven = NOT_GIVEN, - guardrails: Optional[List[str]] | NotGiven = NOT_GIVEN, - key_alias: Optional[str] | NotGiven = NOT_GIVEN, - max_budget: Optional[float] | NotGiven = NOT_GIVEN, - max_parallel_requests: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, - model_max_budget: Optional[object] | NotGiven = NOT_GIVEN, - model_rpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - model_tpm_limit: Optional[object] | NotGiven = NOT_GIVEN, - models: Optional[Iterable[object]] | NotGiven = NOT_GIVEN, - password: Optional[str] | NotGiven = NOT_GIVEN, - permissions: Optional[object] | NotGiven = NOT_GIVEN, - rpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - spend: Optional[float] | NotGiven = NOT_GIVEN, - team_id: Optional[str] | NotGiven = NOT_GIVEN, - tpm_limit: Optional[int] | NotGiven = NOT_GIVEN, - user_email: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - user_role: ( - Optional[ - Literal[ - "proxy_admin", - "proxy_admin_viewer", - "internal_user", - "internal_user_viewer", - ] - ] - | NotGiven - ) = NOT_GIVEN, + aliases: Optional[object] | Omit = omit, + allowed_cache_controls: Optional[Iterable[object]] | Omit = omit, + blocked: Optional[bool] | Omit = omit, + budget_duration: Optional[str] | Omit = omit, + config: Optional[object] | Omit = omit, + duration: Optional[str] | Omit = omit, + guardrails: Optional[SequenceNotStr[str]] | Omit = omit, + key_alias: Optional[str] | Omit = omit, + max_budget: Optional[float] | Omit = omit, + max_parallel_requests: Optional[int] | Omit = omit, + metadata: Optional[object] | Omit = omit, + model_max_budget: Optional[object] | Omit = omit, + model_rpm_limit: Optional[object] | Omit = omit, + model_tpm_limit: Optional[object] | Omit = omit, + models: Optional[Iterable[object]] | Omit = omit, + password: Optional[str] | Omit = omit, + permissions: Optional[object] | Omit = omit, + rpm_limit: Optional[int] | Omit = omit, + spend: Optional[float] | Omit = omit, + team_id: Optional[str] | Omit = omit, + tpm_limit: Optional[int] | Omit = omit, + user_email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, + user_role: Optional[Literal["proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer"]] + | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Example curl ``` curl --location 'http://0.0.0.0:4000/user/update' --header 'Authorization: Bearer sk-1234' --header 'Content-Type: application/json' --data '{ - "user_id": "test-hanzo-user-4", + "user_id": "test-llm-user-4", "user_role": "proxy_admin_viewer" }' ``` @@ -834,7 +773,7 @@ async def update( user_role: Optional[str] - Specify a user role - "proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer", "team", "customer". Info about each role here: - `https://github.com/BerriAI/hanzo/hanzo/proxy/_types.py#L20` - max_budget: + `https://github.com/hanzoai/llm/llm/proxy/_types.py#L20` - max_budget: Optional[float] - Specify max budget for a given user. - budget_duration: Optional[str] - Budget is reset at the end of specified duration. If not set, budget is never reset. You can set duration as seconds ("30s"), minutes ("30m"), @@ -845,8 +784,8 @@ async def update( (Requests per minute) - auto_create_key: bool - Default=True. Flag used for returning a key as part of the /user/new response - aliases: Optional[dict] - Model aliases for the user - - [Docs](https://hanzo.vercel.app/docs/proxy/virtual_keys#model-aliases) - - config: Optional[dict] - [DEPRECATED PARAM] User-specific config. - + [Docs](https://llm.vercel.app/docs/proxy/virtual_keys#model-aliases) - config: + Optional[dict] - [DEPRECATED PARAM] User-specific config. - allowed_cache_controls: Optional[list] - List of allowed cache control values. Example - ["no-cache", "no-store"]. See all values - https://docs.hanzo.ai/docs/proxy/caching#turn-on--off-caching-per-request- - @@ -855,7 +794,7 @@ async def update( guardrails for the user - permissions: Optional[dict] - [Not Implemented Yet] User-specific permissions, eg. turning off pii masking. - metadata: Optional[dict] - Metadata for user, store information for user. Example metadata - = {"team": "core-infra", "app": "app2", "email": "ishaan@berri.ai" } - + = {"team": "core-infra", "app": "app2", "email": "z@hanzo.ai" } - max_parallel_requests: Optional[int] - Rate limit a user based on the number of parallel requests. Raises 429 error, if user's parallel requests > x. - soft_budget: Optional[float] - Get alerts when user crosses given budget, @@ -914,10 +853,7 @@ async def update( user_update_params.UserUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -925,16 +861,16 @@ async def update( async def list( self, *, - page: int | NotGiven = NOT_GIVEN, - page_size: int | NotGiven = NOT_GIVEN, - role: Optional[str] | NotGiven = NOT_GIVEN, - user_ids: Optional[str] | NotGiven = NOT_GIVEN, + page: int | Omit = omit, + page_size: int | Omit = omit, + role: Optional[str] | Omit = omit, + user_ids: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ Get a paginated list of users, optionally filtered by role. @@ -995,14 +931,14 @@ async def list( async def delete( self, *, - user_ids: List[str], - hanzo_changed_by: str | NotGiven = NOT_GIVEN, + user_ids: SequenceNotStr[str], + llm_changed_by: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ delete user and associated user keys @@ -1021,9 +957,8 @@ async def delete( - user_ids: List[str] - The list of user id's to be deleted. Args: - hanzo_changed_by: The hanzo-changed-by header enables tracking of actions performed by - authorized users on behalf of other users, providing an audit trail for - accountability + llm_changed_by: The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability extra_headers: Send extra headers @@ -1033,20 +968,12 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = { - **strip_not_given({"hanzo-changed-by": hanzo_changed_by}), - **(extra_headers or {}), - } + extra_headers = {**strip_not_given({"llm-changed-by": llm_changed_by}), **(extra_headers or {})} return await self._post( "/user/delete", - body=await async_maybe_transform( - {"user_ids": user_ids}, user_delete_params.UserDeleteParams - ), + body=await async_maybe_transform({"user_ids": user_ids}, user_delete_params.UserDeleteParams), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -1054,13 +981,13 @@ async def delete( async def retrieve_info( self, *, - user_id: Optional[str] | NotGiven = NOT_GIVEN, + user_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ [10/07/2024] Note: To get all users (+pagination), use `/user/list` endpoint. @@ -1070,7 +997,7 @@ async def retrieve_info( Example request ``` - curl -X GET 'http://localhost:4000/user/info?user_id=krrish7%40berri.ai' --header 'Authorization: Bearer sk-1234' + curl -X GET 'http://localhost:4000/user/info?user_id=dev7%40hanzo.ai' --header 'Authorization: Bearer sk-1234' ``` Args: @@ -1092,8 +1019,7 @@ async def retrieve_info( extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( - {"user_id": user_id}, - user_retrieve_info_params.UserRetrieveInfoParams, + {"user_id": user_id}, user_retrieve_info_params.UserRetrieveInfoParams ), ), cast_to=object, diff --git a/src/hanzoai/resources/utils.py b/src/hanzoai/resources/utils.py new file mode 100644 index 000000000..d5d32ce76 --- /dev/null +++ b/src/hanzoai/resources/utils.py @@ -0,0 +1,503 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal + +import httpx + +from ..types import util_token_counter_params, util_transform_request_params, util_get_supported_openai_params_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.util_token_counter_response import UtilTokenCounterResponse +from ..types.util_transform_request_response import UtilTransformRequestResponse + +__all__ = ["UtilsResource", "AsyncUtilsResource"] + + +class UtilsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> UtilsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return UtilsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> UtilsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return UtilsResourceWithStreamingResponse(self) + + def get_supported_openai_params( + self, + *, + model: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Returns supported openai params for a given llm model name + + e.g. + + `gpt-4` vs `gpt-3.5-turbo` + + Example curl: + + ``` + curl -X GET --location 'http://localhost:4000/utils/supported_openai_params?model=gpt-3.5-turbo-16k' --header 'Authorization: Bearer sk-1234' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/utils/supported_openai_params", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"model": model}, util_get_supported_openai_params_params.UtilGetSupportedOpenAIParamsParams + ), + ), + cast_to=object, + ) + + def token_counter( + self, + *, + model: str, + messages: Optional[Iterable[object]] | Omit = omit, + prompt: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UtilTokenCounterResponse: + """ + Token Counter + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/utils/token_counter", + body=maybe_transform( + { + "model": model, + "messages": messages, + "prompt": prompt, + }, + util_token_counter_params.UtilTokenCounterParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=UtilTokenCounterResponse, + ) + + def transform_request( + self, + *, + call_type: Literal[ + "embedding", + "aembedding", + "completion", + "acompletion", + "atext_completion", + "text_completion", + "image_generation", + "aimage_generation", + "moderation", + "amoderation", + "atranscription", + "transcription", + "aspeech", + "speech", + "rerank", + "arerank", + "_arealtime", + "create_batch", + "acreate_batch", + "aretrieve_batch", + "retrieve_batch", + "pass_through_endpoint", + "anthropic_messages", + "get_assistants", + "aget_assistants", + "create_assistants", + "acreate_assistants", + "delete_assistant", + "adelete_assistant", + "acreate_thread", + "create_thread", + "aget_thread", + "get_thread", + "a_add_message", + "add_message", + "aget_messages", + "get_messages", + "arun_thread", + "run_thread", + "arun_thread_stream", + "run_thread_stream", + "afile_retrieve", + "file_retrieve", + "afile_delete", + "file_delete", + "afile_list", + "file_list", + "acreate_file", + "create_file", + "afile_content", + "file_content", + "create_fine_tuning_job", + "acreate_fine_tuning_job", + "acancel_fine_tuning_job", + "cancel_fine_tuning_job", + "alist_fine_tuning_jobs", + "list_fine_tuning_jobs", + "aretrieve_fine_tuning_job", + "retrieve_fine_tuning_job", + "responses", + "aresponses", + ], + request_body: object, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UtilTransformRequestResponse: + """ + Transform Request + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/utils/transform_request", + body=maybe_transform( + { + "call_type": call_type, + "request_body": request_body, + }, + util_transform_request_params.UtilTransformRequestParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=UtilTransformRequestResponse, + ) + + +class AsyncUtilsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncUtilsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/hanzoai/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncUtilsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncUtilsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hanzoai/python-sdk#with_streaming_response + """ + return AsyncUtilsResourceWithStreamingResponse(self) + + async def get_supported_openai_params( + self, + *, + model: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Returns supported openai params for a given llm model name + + e.g. + + `gpt-4` vs `gpt-3.5-turbo` + + Example curl: + + ``` + curl -X GET --location 'http://localhost:4000/utils/supported_openai_params?model=gpt-3.5-turbo-16k' --header 'Authorization: Bearer sk-1234' + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/utils/supported_openai_params", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"model": model}, util_get_supported_openai_params_params.UtilGetSupportedOpenAIParamsParams + ), + ), + cast_to=object, + ) + + async def token_counter( + self, + *, + model: str, + messages: Optional[Iterable[object]] | Omit = omit, + prompt: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UtilTokenCounterResponse: + """ + Token Counter + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/utils/token_counter", + body=await async_maybe_transform( + { + "model": model, + "messages": messages, + "prompt": prompt, + }, + util_token_counter_params.UtilTokenCounterParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=UtilTokenCounterResponse, + ) + + async def transform_request( + self, + *, + call_type: Literal[ + "embedding", + "aembedding", + "completion", + "acompletion", + "atext_completion", + "text_completion", + "image_generation", + "aimage_generation", + "moderation", + "amoderation", + "atranscription", + "transcription", + "aspeech", + "speech", + "rerank", + "arerank", + "_arealtime", + "create_batch", + "acreate_batch", + "aretrieve_batch", + "retrieve_batch", + "pass_through_endpoint", + "anthropic_messages", + "get_assistants", + "aget_assistants", + "create_assistants", + "acreate_assistants", + "delete_assistant", + "adelete_assistant", + "acreate_thread", + "create_thread", + "aget_thread", + "get_thread", + "a_add_message", + "add_message", + "aget_messages", + "get_messages", + "arun_thread", + "run_thread", + "arun_thread_stream", + "run_thread_stream", + "afile_retrieve", + "file_retrieve", + "afile_delete", + "file_delete", + "afile_list", + "file_list", + "acreate_file", + "create_file", + "afile_content", + "file_content", + "create_fine_tuning_job", + "acreate_fine_tuning_job", + "acancel_fine_tuning_job", + "cancel_fine_tuning_job", + "alist_fine_tuning_jobs", + "list_fine_tuning_jobs", + "aretrieve_fine_tuning_job", + "retrieve_fine_tuning_job", + "responses", + "aresponses", + ], + request_body: object, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UtilTransformRequestResponse: + """ + Transform Request + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/utils/transform_request", + body=await async_maybe_transform( + { + "call_type": call_type, + "request_body": request_body, + }, + util_transform_request_params.UtilTransformRequestParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=UtilTransformRequestResponse, + ) + + +class UtilsResourceWithRawResponse: + def __init__(self, utils: UtilsResource) -> None: + self._utils = utils + + self.get_supported_openai_params = to_raw_response_wrapper( + utils.get_supported_openai_params, + ) + self.token_counter = to_raw_response_wrapper( + utils.token_counter, + ) + self.transform_request = to_raw_response_wrapper( + utils.transform_request, + ) + + +class AsyncUtilsResourceWithRawResponse: + def __init__(self, utils: AsyncUtilsResource) -> None: + self._utils = utils + + self.get_supported_openai_params = async_to_raw_response_wrapper( + utils.get_supported_openai_params, + ) + self.token_counter = async_to_raw_response_wrapper( + utils.token_counter, + ) + self.transform_request = async_to_raw_response_wrapper( + utils.transform_request, + ) + + +class UtilsResourceWithStreamingResponse: + def __init__(self, utils: UtilsResource) -> None: + self._utils = utils + + self.get_supported_openai_params = to_streamed_response_wrapper( + utils.get_supported_openai_params, + ) + self.token_counter = to_streamed_response_wrapper( + utils.token_counter, + ) + self.transform_request = to_streamed_response_wrapper( + utils.transform_request, + ) + + +class AsyncUtilsResourceWithStreamingResponse: + def __init__(self, utils: AsyncUtilsResource) -> None: + self._utils = utils + + self.get_supported_openai_params = async_to_streamed_response_wrapper( + utils.get_supported_openai_params, + ) + self.token_counter = async_to_streamed_response_wrapper( + utils.token_counter, + ) + self.transform_request = async_to_streamed_response_wrapper( + utils.transform_request, + ) diff --git a/pkg/hanzoai/resources/vertex_ai.py b/src/hanzoai/resources/vertex_ai.py similarity index 78% rename from pkg/hanzoai/resources/vertex_ai.py rename to src/hanzoai/resources/vertex_ai.py index 10cc99829..dd098d402 100644 --- a/pkg/hanzoai/resources/vertex_ai.py +++ b/src/hanzoai/resources/vertex_ai.py @@ -1,10 +1,10 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -47,10 +47,10 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ - Call Hanzo proxy via Vertex AI SDK. + Call LLM proxy via Vertex AI SDK. [Docs](https://docs.hanzo.ai/docs/pass_through/vertex_ai) @@ -64,16 +64,11 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._post( f"/vertex_ai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -87,10 +82,10 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ - Call Hanzo proxy via Vertex AI SDK. + Call LLM proxy via Vertex AI SDK. [Docs](https://docs.hanzo.ai/docs/pass_through/vertex_ai) @@ -104,16 +99,11 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._get( f"/vertex_ai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -127,10 +117,10 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ - Call Hanzo proxy via Vertex AI SDK. + Call LLM proxy via Vertex AI SDK. [Docs](https://docs.hanzo.ai/docs/pass_through/vertex_ai) @@ -144,16 +134,11 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._put( f"/vertex_ai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -167,10 +152,10 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ - Call Hanzo proxy via Vertex AI SDK. + Call LLM proxy via Vertex AI SDK. [Docs](https://docs.hanzo.ai/docs/pass_through/vertex_ai) @@ -184,16 +169,11 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._delete( f"/vertex_ai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -207,10 +187,10 @@ def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ - Call Hanzo proxy via Vertex AI SDK. + Call LLM proxy via Vertex AI SDK. [Docs](https://docs.hanzo.ai/docs/pass_through/vertex_ai) @@ -224,16 +204,11 @@ def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return self._patch( f"/vertex_ai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -268,10 +243,10 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ - Call Hanzo proxy via Vertex AI SDK. + Call LLM proxy via Vertex AI SDK. [Docs](https://docs.hanzo.ai/docs/pass_through/vertex_ai) @@ -285,16 +260,11 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._post( f"/vertex_ai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -308,10 +278,10 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ - Call Hanzo proxy via Vertex AI SDK. + Call LLM proxy via Vertex AI SDK. [Docs](https://docs.hanzo.ai/docs/pass_through/vertex_ai) @@ -325,16 +295,11 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._get( f"/vertex_ai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -348,10 +313,10 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ - Call Hanzo proxy via Vertex AI SDK. + Call LLM proxy via Vertex AI SDK. [Docs](https://docs.hanzo.ai/docs/pass_through/vertex_ai) @@ -365,16 +330,11 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._put( f"/vertex_ai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -388,10 +348,10 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ - Call Hanzo proxy via Vertex AI SDK. + Call LLM proxy via Vertex AI SDK. [Docs](https://docs.hanzo.ai/docs/pass_through/vertex_ai) @@ -405,16 +365,11 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._delete( f"/vertex_ai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) @@ -428,10 +383,10 @@ async def patch( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ - Call Hanzo proxy via Vertex AI SDK. + Call LLM proxy via Vertex AI SDK. [Docs](https://docs.hanzo.ai/docs/pass_through/vertex_ai) @@ -445,16 +400,11 @@ async def patch( timeout: Override the client-level default timeout for this request, in seconds """ if not endpoint: - raise ValueError( - f"Expected a non-empty value for `endpoint` but received {endpoint!r}" - ) + raise ValueError(f"Expected a non-empty value for `endpoint` but received {endpoint!r}") return await self._patch( f"/vertex_ai/{endpoint}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=object, ) diff --git a/src/hanzoai/types/__init__.py b/src/hanzoai/types/__init__.py new file mode 100644 index 000000000..d367db1c4 --- /dev/null +++ b/src/hanzoai/types/__init__.py @@ -0,0 +1,100 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .member import Member as Member +from .member_param import MemberParam as MemberParam +from .key_list_params import KeyListParams as KeyListParams +from .file_list_params import FileListParams as FileListParams +from .key_block_params import KeyBlockParams as KeyBlockParams +from .model_info_param import ModelInfoParam as ModelInfoParam +from .org_member_param import OrgMemberParam as OrgMemberParam +from .team_list_params import TeamListParams as TeamListParams +from .user_list_params import UserListParams as UserListParams +from .batch_list_params import BatchListParams as BatchListParams +from .key_delete_params import KeyDeleteParams as KeyDeleteParams +from .key_list_response import KeyListResponse as KeyListResponse +from .key_update_params import KeyUpdateParams as KeyUpdateParams +from .model_list_params import ModelListParams as ModelListParams +from .team_block_params import TeamBlockParams as TeamBlockParams +from .budget_info_params import BudgetInfoParams as BudgetInfoParams +from .file_create_params import FileCreateParams as FileCreateParams +from .key_block_response import KeyBlockResponse as KeyBlockResponse +from .key_unblock_params import KeyUnblockParams as KeyUnblockParams +from .team_create_params import TeamCreateParams as TeamCreateParams +from .team_delete_params import TeamDeleteParams as TeamDeleteParams +from .team_update_params import TeamUpdateParams as TeamUpdateParams +from .user_create_params import UserCreateParams as UserCreateParams +from .user_delete_params import UserDeleteParams as UserDeleteParams +from .user_update_params import UserUpdateParams as UserUpdateParams +from .batch_create_params import BatchCreateParams as BatchCreateParams +from .cache_ping_response import CachePingResponse as CachePingResponse +from .key_generate_params import KeyGenerateParams as KeyGenerateParams +from .model_create_params import ModelCreateParams as ModelCreateParams +from .model_delete_params import ModelDeleteParams as ModelDeleteParams +from .team_unblock_params import TeamUnblockParams as TeamUnblockParams +from .budget_create_params import BudgetCreateParams as BudgetCreateParams +from .budget_delete_params import BudgetDeleteParams as BudgetDeleteParams +from .budget_update_params import BudgetUpdateParams as BudgetUpdateParams +from .team_create_response import TeamCreateResponse as TeamCreateResponse +from .user_create_response import UserCreateResponse as UserCreateResponse +from .batch_retrieve_params import BatchRetrieveParams as BatchRetrieveParams +from .customer_block_params import CustomerBlockParams as CustomerBlockParams +from .generate_key_response import GenerateKeyResponse as GenerateKeyResponse +from .budget_settings_params import BudgetSettingsParams as BudgetSettingsParams +from .customer_create_params import CustomerCreateParams as CustomerCreateParams +from .customer_delete_params import CustomerDeleteParams as CustomerDeleteParams +from .customer_list_response import CustomerListResponse as CustomerListResponse +from .customer_update_params import CustomerUpdateParams as CustomerUpdateParams +from .spend_list_logs_params import SpendListLogsParams as SpendListLogsParams +from .spend_list_tags_params import SpendListTagsParams as SpendListTagsParams +from .team_add_member_params import TeamAddMemberParams as TeamAddMemberParams +from .customer_unblock_params import CustomerUnblockParams as CustomerUnblockParams +from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams +from .guardrail_list_response import GuardrailListResponse as GuardrailListResponse +from .health_check_all_params import HealthCheckAllParams as HealthCheckAllParams +from .completion_create_params import CompletionCreateParams as CompletionCreateParams +from .credential_create_params import CredentialCreateParams as CredentialCreateParams +from .key_retrieve_info_params import KeyRetrieveInfoParams as KeyRetrieveInfoParams +from .spend_list_logs_response import SpendListLogsResponse as SpendListLogsResponse +from .spend_list_tags_response import SpendListTagsResponse as SpendListTagsResponse +from .team_add_member_response import TeamAddMemberResponse as TeamAddMemberResponse +from .add_add_allowed_ip_params import AddAddAllowedIPParams as AddAddAllowedIPParams +from .key_check_health_response import KeyCheckHealthResponse as KeyCheckHealthResponse +from .team_remove_member_params import TeamRemoveMemberParams as TeamRemoveMemberParams +from .team_retrieve_info_params import TeamRetrieveInfoParams as TeamRetrieveInfoParams +from .team_update_member_params import TeamUpdateMemberParams as TeamUpdateMemberParams +from .user_retrieve_info_params import UserRetrieveInfoParams as UserRetrieveInfoParams +from .util_token_counter_params import UtilTokenCounterParams as UtilTokenCounterParams +from .organization_create_params import OrganizationCreateParams as OrganizationCreateParams +from .organization_delete_params import OrganizationDeleteParams as OrganizationDeleteParams +from .organization_list_response import OrganizationListResponse as OrganizationListResponse +from .organization_update_params import OrganizationUpdateParams as OrganizationUpdateParams +from .team_list_available_params import TeamListAvailableParams as TeamListAvailableParams +from .team_update_member_response import TeamUpdateMemberResponse as TeamUpdateMemberResponse +from .util_token_counter_response import UtilTokenCounterResponse as UtilTokenCounterResponse +from .health_check_services_params import HealthCheckServicesParams as HealthCheckServicesParams +from .key_regenerate_by_key_params import KeyRegenerateByKeyParams as KeyRegenerateByKeyParams +from .organization_create_response import OrganizationCreateResponse as OrganizationCreateResponse +from .organization_delete_response import OrganizationDeleteResponse as OrganizationDeleteResponse +from .organization_update_response import OrganizationUpdateResponse as OrganizationUpdateResponse +from .spend_calculate_spend_params import SpendCalculateSpendParams as SpendCalculateSpendParams +from .customer_retrieve_info_params import CustomerRetrieveInfoParams as CustomerRetrieveInfoParams +from .util_transform_request_params import UtilTransformRequestParams as UtilTransformRequestParams +from .organization_add_member_params import OrganizationAddMemberParams as OrganizationAddMemberParams +from .provider_list_budgets_response import ProviderListBudgetsResponse as ProviderListBudgetsResponse +from .batch_list_with_provider_params import BatchListWithProviderParams as BatchListWithProviderParams +from .customer_retrieve_info_response import CustomerRetrieveInfoResponse as CustomerRetrieveInfoResponse +from .delete_create_allowed_ip_params import DeleteCreateAllowedIPParams as DeleteCreateAllowedIPParams +from .util_transform_request_response import UtilTransformRequestResponse as UtilTransformRequestResponse +from .model_group_retrieve_info_params import ModelGroupRetrieveInfoParams as ModelGroupRetrieveInfoParams +from .organization_add_member_response import OrganizationAddMemberResponse as OrganizationAddMemberResponse +from .organization_delete_member_params import OrganizationDeleteMemberParams as OrganizationDeleteMemberParams +from .organization_update_member_params import OrganizationUpdateMemberParams as OrganizationUpdateMemberParams +from .organization_update_member_response import OrganizationUpdateMemberResponse as OrganizationUpdateMemberResponse +from .util_get_supported_openai_params_params import ( + UtilGetSupportedOpenAIParamsParams as UtilGetSupportedOpenAIParamsParams, +) +from .configurable_clientside_params_custom_auth_param import ( + ConfigurableClientsideParamsCustomAuthParam as ConfigurableClientsideParamsCustomAuthParam, +) diff --git a/src/hanzoai/types/add_add_allowed_ip_params.py b/src/hanzoai/types/add_add_allowed_ip_params.py new file mode 100644 index 000000000..bd1391cc7 --- /dev/null +++ b/src/hanzoai/types/add_add_allowed_ip_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["AddAddAllowedIPParams"] + + +class AddAddAllowedIPParams(TypedDict, total=False): + ip: Required[str] diff --git a/src/hanzoai/types/audio/__init__.py b/src/hanzoai/types/audio/__init__.py new file mode 100644 index 000000000..c2429ee8f --- /dev/null +++ b/src/hanzoai/types/audio/__init__.py @@ -0,0 +1,5 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .transcription_create_params import TranscriptionCreateParams as TranscriptionCreateParams diff --git a/src/hanzoai/types/audio/transcription_create_params.py b/src/hanzoai/types/audio/transcription_create_params.py new file mode 100644 index 000000000..4780dae38 --- /dev/null +++ b/src/hanzoai/types/audio/transcription_create_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ..._types import FileTypes + +__all__ = ["TranscriptionCreateParams"] + + +class TranscriptionCreateParams(TypedDict, total=False): + file: Required[FileTypes] diff --git a/src/hanzoai/types/batch_create_params.py b/src/hanzoai/types/batch_create_params.py new file mode 100644 index 000000000..8540c8c86 --- /dev/null +++ b/src/hanzoai/types/batch_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["BatchCreateParams"] + + +class BatchCreateParams(TypedDict, total=False): + provider: Optional[str] diff --git a/pkg/hanzoai/types/batch_list_params.py b/src/hanzoai/types/batch_list_params.py similarity index 75% rename from pkg/hanzoai/types/batch_list_params.py rename to src/hanzoai/types/batch_list_params.py index 0815b4020..48d74d679 100644 --- a/pkg/hanzoai/types/batch_list_params.py +++ b/src/hanzoai/types/batch_list_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/pkg/hanzoai/types/batch_list_with_provider_params.py b/src/hanzoai/types/batch_list_with_provider_params.py similarity index 75% rename from pkg/hanzoai/types/batch_list_with_provider_params.py rename to src/hanzoai/types/batch_list_with_provider_params.py index 27888b82f..028674e62 100644 --- a/pkg/hanzoai/types/batch_list_with_provider_params.py +++ b/src/hanzoai/types/batch_list_with_provider_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/batch_retrieve_params.py b/src/hanzoai/types/batch_retrieve_params.py new file mode 100644 index 000000000..5a9b63efa --- /dev/null +++ b/src/hanzoai/types/batch_retrieve_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["BatchRetrieveParams"] + + +class BatchRetrieveParams(TypedDict, total=False): + provider: Optional[str] diff --git a/src/hanzoai/types/batches/__init__.py b/src/hanzoai/types/batches/__init__.py new file mode 100644 index 000000000..74d273ed9 --- /dev/null +++ b/src/hanzoai/types/batches/__init__.py @@ -0,0 +1,5 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .cancel_cancel_params import CancelCancelParams as CancelCancelParams diff --git a/src/hanzoai/types/batches/cancel_cancel_params.py b/src/hanzoai/types/batches/cancel_cancel_params.py new file mode 100644 index 000000000..00ba61163 --- /dev/null +++ b/src/hanzoai/types/batches/cancel_cancel_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["CancelCancelParams"] + + +class CancelCancelParams(TypedDict, total=False): + provider: Optional[str] diff --git a/pkg/hanzoai/types/budget_create_params.py b/src/hanzoai/types/budget_create_params.py similarity index 93% rename from pkg/hanzoai/types/budget_create_params.py rename to src/hanzoai/types/budget_create_params.py index 89c5f6e4c..3f6b3e7b0 100644 --- a/pkg/hanzoai/types/budget_create_params.py +++ b/src/hanzoai/types/budget_create_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/budget_delete_params.py b/src/hanzoai/types/budget_delete_params.py new file mode 100644 index 000000000..a45149682 --- /dev/null +++ b/src/hanzoai/types/budget_delete_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["BudgetDeleteParams"] + + +class BudgetDeleteParams(TypedDict, total=False): + id: Required[str] diff --git a/src/hanzoai/types/budget_info_params.py b/src/hanzoai/types/budget_info_params.py new file mode 100644 index 000000000..5676b6743 --- /dev/null +++ b/src/hanzoai/types/budget_info_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["BudgetInfoParams"] + + +class BudgetInfoParams(TypedDict, total=False): + budgets: Required[SequenceNotStr[str]] diff --git a/src/hanzoai/types/budget_settings_params.py b/src/hanzoai/types/budget_settings_params.py new file mode 100644 index 000000000..d7fac4107 --- /dev/null +++ b/src/hanzoai/types/budget_settings_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["BudgetSettingsParams"] + + +class BudgetSettingsParams(TypedDict, total=False): + budget_id: Required[str] diff --git a/pkg/hanzoai/types/budget_update_params.py b/src/hanzoai/types/budget_update_params.py similarity index 93% rename from pkg/hanzoai/types/budget_update_params.py rename to src/hanzoai/types/budget_update_params.py index 1e439be13..e76f3bbcc 100644 --- a/pkg/hanzoai/types/budget_update_params.py +++ b/src/hanzoai/types/budget_update_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/cache/__init__.py b/src/hanzoai/types/cache/__init__.py new file mode 100644 index 000000000..f8ee8b14b --- /dev/null +++ b/src/hanzoai/types/cache/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/pkg/hanzoai/types/cache_ping_response.py b/src/hanzoai/types/cache_ping_response.py similarity index 80% rename from pkg/hanzoai/types/cache_ping_response.py rename to src/hanzoai/types/cache_ping_response.py index 6698cfec8..50efa76ee 100644 --- a/pkg/hanzoai/types/cache_ping_response.py +++ b/src/hanzoai/types/cache_ping_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/hanzoai/types/chat/__init__.py b/src/hanzoai/types/chat/__init__.py new file mode 100644 index 000000000..4a2b8db7a --- /dev/null +++ b/src/hanzoai/types/chat/__init__.py @@ -0,0 +1,5 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .completion_create_params import CompletionCreateParams as CompletionCreateParams diff --git a/src/hanzoai/types/chat/completion_create_params.py b/src/hanzoai/types/chat/completion_create_params.py new file mode 100644 index 000000000..b3fc515f7 --- /dev/null +++ b/src/hanzoai/types/chat/completion_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["CompletionCreateParams"] + + +class CompletionCreateParams(TypedDict, total=False): + model: Optional[str] diff --git a/src/hanzoai/types/completion_create_params.py b/src/hanzoai/types/completion_create_params.py new file mode 100644 index 000000000..b3fc515f7 --- /dev/null +++ b/src/hanzoai/types/completion_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["CompletionCreateParams"] + + +class CompletionCreateParams(TypedDict, total=False): + model: Optional[str] diff --git a/src/hanzoai/types/config/__init__.py b/src/hanzoai/types/config/__init__.py new file mode 100644 index 000000000..a84798990 --- /dev/null +++ b/src/hanzoai/types/config/__init__.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .pass_through_generic_endpoint import PassThroughGenericEndpoint as PassThroughGenericEndpoint +from .pass_through_endpoint_response import PassThroughEndpointResponse as PassThroughEndpointResponse +from .pass_through_endpoint_list_params import PassThroughEndpointListParams as PassThroughEndpointListParams +from .pass_through_endpoint_create_params import PassThroughEndpointCreateParams as PassThroughEndpointCreateParams +from .pass_through_endpoint_delete_params import PassThroughEndpointDeleteParams as PassThroughEndpointDeleteParams diff --git a/pkg/hanzoai/types/config/pass_through_endpoint_create_params.py b/src/hanzoai/types/config/pass_through_endpoint_create_params.py similarity index 78% rename from pkg/hanzoai/types/config/pass_through_endpoint_create_params.py rename to src/hanzoai/types/config/pass_through_endpoint_create_params.py index 8aa09c0a4..a1f475a65 100644 --- a/pkg/hanzoai/types/config/pass_through_endpoint_create_params.py +++ b/src/hanzoai/types/config/pass_through_endpoint_create_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,7 +16,7 @@ class PassThroughEndpointCreateParams(TypedDict, total=False): """ path: Required[str] - """The route to be added to the Hanzo Proxy Server.""" + """The route to be added to the LLM Proxy Server.""" target: Required[str] """The URL to which requests for this path should be forwarded.""" diff --git a/src/hanzoai/types/config/pass_through_endpoint_delete_params.py b/src/hanzoai/types/config/pass_through_endpoint_delete_params.py new file mode 100644 index 000000000..dde243251 --- /dev/null +++ b/src/hanzoai/types/config/pass_through_endpoint_delete_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["PassThroughEndpointDeleteParams"] + + +class PassThroughEndpointDeleteParams(TypedDict, total=False): + endpoint_id: Required[str] diff --git a/src/hanzoai/types/config/pass_through_endpoint_list_params.py b/src/hanzoai/types/config/pass_through_endpoint_list_params.py new file mode 100644 index 000000000..5868f8afb --- /dev/null +++ b/src/hanzoai/types/config/pass_through_endpoint_list_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["PassThroughEndpointListParams"] + + +class PassThroughEndpointListParams(TypedDict, total=False): + endpoint_id: Optional[str] diff --git a/pkg/hanzoai/types/config/pass_through_endpoint_response.py b/src/hanzoai/types/config/pass_through_endpoint_response.py similarity index 75% rename from pkg/hanzoai/types/config/pass_through_endpoint_response.py rename to src/hanzoai/types/config/pass_through_endpoint_response.py index 9fb0f64db..716744f7e 100644 --- a/pkg/hanzoai/types/config/pass_through_endpoint_response.py +++ b/src/hanzoai/types/config/pass_through_endpoint_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List diff --git a/src/hanzoai/types/config/pass_through_generic_endpoint.py b/src/hanzoai/types/config/pass_through_generic_endpoint.py new file mode 100644 index 000000000..c4d7d8f61 --- /dev/null +++ b/src/hanzoai/types/config/pass_through_generic_endpoint.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["PassThroughGenericEndpoint"] + + +class PassThroughGenericEndpoint(BaseModel): + headers: object + """Key-value pairs of headers to be forwarded with the request. + + You can set any key value pair here and it will be forwarded to your target + endpoint + """ + + path: str + """The route to be added to the LLM Proxy Server.""" + + target: str + """The URL to which requests for this path should be forwarded.""" diff --git a/src/hanzoai/types/configurable_clientside_params_custom_auth_param.py b/src/hanzoai/types/configurable_clientside_params_custom_auth_param.py new file mode 100644 index 000000000..539ca4d7b --- /dev/null +++ b/src/hanzoai/types/configurable_clientside_params_custom_auth_param.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ConfigurableClientsideParamsCustomAuthParam"] + + +class ConfigurableClientsideParamsCustomAuthParam(TypedDict, total=False): + api_base: Required[str] diff --git a/pkg/hanzoai/types/credential_create_params.py b/src/hanzoai/types/credential_create_params.py similarity index 80% rename from pkg/hanzoai/types/credential_create_params.py rename to src/hanzoai/types/credential_create_params.py index ecb90bd79..6b824014e 100644 --- a/pkg/hanzoai/types/credential_create_params.py +++ b/src/hanzoai/types/credential_create_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/customer_block_params.py b/src/hanzoai/types/customer_block_params.py new file mode 100644 index 000000000..0e9a4eae9 --- /dev/null +++ b/src/hanzoai/types/customer_block_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["CustomerBlockParams"] + + +class CustomerBlockParams(TypedDict, total=False): + user_ids: Required[SequenceNotStr[str]] diff --git a/pkg/hanzoai/types/customer_create_params.py b/src/hanzoai/types/customer_create_params.py similarity index 94% rename from pkg/hanzoai/types/customer_create_params.py rename to src/hanzoai/types/customer_create_params.py index fae2a414f..c5acff45a 100644 --- a/pkg/hanzoai/types/customer_create_params.py +++ b/src/hanzoai/types/customer_create_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/customer_delete_params.py b/src/hanzoai/types/customer_delete_params.py new file mode 100644 index 000000000..7c1603f5e --- /dev/null +++ b/src/hanzoai/types/customer_delete_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["CustomerDeleteParams"] + + +class CustomerDeleteParams(TypedDict, total=False): + user_ids: Required[SequenceNotStr[str]] diff --git a/src/hanzoai/types/customer_list_response.py b/src/hanzoai/types/customer_list_response.py new file mode 100644 index 000000000..3b93aff63 --- /dev/null +++ b/src/hanzoai/types/customer_list_response.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal, TypeAlias + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["CustomerListResponse", "CustomerListResponseItem", "CustomerListResponseItemLlmBudgetTable"] + + +class CustomerListResponseItemLlmBudgetTable(BaseModel): + budget_duration: Optional[str] = None + + max_budget: Optional[float] = None + + max_parallel_requests: Optional[int] = None + + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) + + rpm_limit: Optional[int] = None + + soft_budget: Optional[float] = None + + tpm_limit: Optional[int] = None + + +class CustomerListResponseItem(BaseModel): + blocked: bool + + user_id: str + + alias: Optional[str] = None + + allowed_model_region: Optional[Literal["eu", "us"]] = None + + default_model: Optional[str] = None + + llm_budget_table: Optional[CustomerListResponseItemLlmBudgetTable] = None + """Represents user-controllable params for a LLM_BudgetTable record""" + + spend: Optional[float] = None + + +CustomerListResponse: TypeAlias = List[CustomerListResponseItem] diff --git a/pkg/hanzoai/types/customer_retrieve_info_params.py b/src/hanzoai/types/customer_retrieve_info_params.py similarity index 75% rename from pkg/hanzoai/types/customer_retrieve_info_params.py rename to src/hanzoai/types/customer_retrieve_info_params.py index 5e8e25f52..725df2faf 100644 --- a/pkg/hanzoai/types/customer_retrieve_info_params.py +++ b/src/hanzoai/types/customer_retrieve_info_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/pkg/hanzoai/types/customer_retrieve_info_response.py b/src/hanzoai/types/customer_retrieve_info_response.py similarity index 82% rename from pkg/hanzoai/types/customer_retrieve_info_response.py rename to src/hanzoai/types/customer_retrieve_info_response.py index 55baa7e70..26b491165 100644 --- a/pkg/hanzoai/types/customer_retrieve_info_response.py +++ b/src/hanzoai/types/customer_retrieve_info_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal @@ -17,9 +17,7 @@ class LlmBudgetTable(BaseModel): max_parallel_requests: Optional[int] = None - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) rpm_limit: Optional[int] = None diff --git a/src/hanzoai/types/customer_unblock_params.py b/src/hanzoai/types/customer_unblock_params.py new file mode 100644 index 000000000..8d4c4b22a --- /dev/null +++ b/src/hanzoai/types/customer_unblock_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["CustomerUnblockParams"] + + +class CustomerUnblockParams(TypedDict, total=False): + user_ids: Required[SequenceNotStr[str]] diff --git a/pkg/hanzoai/types/customer_update_params.py b/src/hanzoai/types/customer_update_params.py similarity index 83% rename from pkg/hanzoai/types/customer_update_params.py rename to src/hanzoai/types/customer_update_params.py index 32ec8eb7e..5aa0b9b24 100644 --- a/pkg/hanzoai/types/customer_update_params.py +++ b/src/hanzoai/types/customer_update_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/delete_create_allowed_ip_params.py b/src/hanzoai/types/delete_create_allowed_ip_params.py new file mode 100644 index 000000000..924612de5 --- /dev/null +++ b/src/hanzoai/types/delete_create_allowed_ip_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["DeleteCreateAllowedIPParams"] + + +class DeleteCreateAllowedIPParams(TypedDict, total=False): + ip: Required[str] diff --git a/src/hanzoai/types/embedding_create_params.py b/src/hanzoai/types/embedding_create_params.py new file mode 100644 index 000000000..d83676aac --- /dev/null +++ b/src/hanzoai/types/embedding_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["EmbeddingCreateParams"] + + +class EmbeddingCreateParams(TypedDict, total=False): + model: Optional[str] diff --git a/src/hanzoai/types/engines/__init__.py b/src/hanzoai/types/engines/__init__.py new file mode 100644 index 000000000..f8ee8b14b --- /dev/null +++ b/src/hanzoai/types/engines/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/pkg/hanzoai/types/file_create_params.py b/src/hanzoai/types/file_create_params.py similarity index 77% rename from pkg/hanzoai/types/file_create_params.py rename to src/hanzoai/types/file_create_params.py index 2a05164c3..93662d72d 100644 --- a/pkg/hanzoai/types/file_create_params.py +++ b/src/hanzoai/types/file_create_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/file_list_params.py b/src/hanzoai/types/file_list_params.py new file mode 100644 index 000000000..8051f8f34 --- /dev/null +++ b/src/hanzoai/types/file_list_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["FileListParams"] + + +class FileListParams(TypedDict, total=False): + purpose: Optional[str] diff --git a/src/hanzoai/types/files/__init__.py b/src/hanzoai/types/files/__init__.py new file mode 100644 index 000000000..f8ee8b14b --- /dev/null +++ b/src/hanzoai/types/files/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/src/hanzoai/types/fine_tuning/__init__.py b/src/hanzoai/types/fine_tuning/__init__.py new file mode 100644 index 000000000..953afb292 --- /dev/null +++ b/src/hanzoai/types/fine_tuning/__init__.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .job_list_params import JobListParams as JobListParams +from .job_create_params import JobCreateParams as JobCreateParams +from .job_retrieve_params import JobRetrieveParams as JobRetrieveParams diff --git a/pkg/hanzoai/types/fine_tuning/job_create_params.py b/src/hanzoai/types/fine_tuning/job_create_params.py similarity index 75% rename from pkg/hanzoai/types/fine_tuning/job_create_params.py rename to src/hanzoai/types/fine_tuning/job_create_params.py index b46579e8d..cc37b2228 100644 --- a/pkg/hanzoai/types/fine_tuning/job_create_params.py +++ b/src/hanzoai/types/fine_tuning/job_create_params.py @@ -1,10 +1,12 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations -from typing import List, Union, Optional +from typing import Union, Optional from typing_extensions import Literal, Required, TypedDict +from ..._types import SequenceNotStr + __all__ = ["JobCreateParams", "Hyperparameters"] @@ -17,7 +19,7 @@ class JobCreateParams(TypedDict, total=False): hyperparameters: Optional[Hyperparameters] - integrations: Optional[List[str]] + integrations: Optional[SequenceNotStr[str]] seed: Optional[int] diff --git a/pkg/hanzoai/types/fine_tuning/job_list_params.py b/src/hanzoai/types/fine_tuning/job_list_params.py similarity index 78% rename from pkg/hanzoai/types/fine_tuning/job_list_params.py rename to src/hanzoai/types/fine_tuning/job_list_params.py index 668d74a97..6e5afbff8 100644 --- a/pkg/hanzoai/types/fine_tuning/job_list_params.py +++ b/src/hanzoai/types/fine_tuning/job_list_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/fine_tuning/job_retrieve_params.py b/src/hanzoai/types/fine_tuning/job_retrieve_params.py new file mode 100644 index 000000000..caf05b109 --- /dev/null +++ b/src/hanzoai/types/fine_tuning/job_retrieve_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["JobRetrieveParams"] + + +class JobRetrieveParams(TypedDict, total=False): + custom_llm_provider: Required[Literal["openai", "azure"]] diff --git a/src/hanzoai/types/fine_tuning/jobs/__init__.py b/src/hanzoai/types/fine_tuning/jobs/__init__.py new file mode 100644 index 000000000..f8ee8b14b --- /dev/null +++ b/src/hanzoai/types/fine_tuning/jobs/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/pkg/hanzoai/types/generate_key_response.py b/src/hanzoai/types/generate_key_response.py similarity index 75% rename from pkg/hanzoai/types/generate_key_response.py rename to src/hanzoai/types/generate_key_response.py index 42ec3b269..de6291855 100644 --- a/pkg/hanzoai/types/generate_key_response.py +++ b/src/hanzoai/types/generate_key_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from datetime import datetime @@ -41,7 +41,7 @@ class GenerateKeyResponse(BaseModel): key_name: Optional[str] = None - hanzo_budget_table: Optional[object] = None + llm_budget_table: Optional[object] = None max_budget: Optional[float] = None @@ -49,17 +49,11 @@ class GenerateKeyResponse(BaseModel): metadata: Optional[object] = None - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) - api_model_rpm_limit: Optional[object] = FieldInfo( - alias="model_rpm_limit", default=None - ) + api_model_rpm_limit: Optional[object] = FieldInfo(alias="model_rpm_limit", default=None) - api_model_tpm_limit: Optional[object] = FieldInfo( - alias="model_tpm_limit", default=None - ) + api_model_tpm_limit: Optional[object] = FieldInfo(alias="model_tpm_limit", default=None) models: Optional[List[object]] = None diff --git a/src/hanzoai/types/global_/__init__.py b/src/hanzoai/types/global_/__init__.py new file mode 100644 index 000000000..9079d7cc3 --- /dev/null +++ b/src/hanzoai/types/global_/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .spend_list_tags_params import SpendListTagsParams as SpendListTagsParams +from .spend_list_tags_response import SpendListTagsResponse as SpendListTagsResponse +from .spend_retrieve_report_params import SpendRetrieveReportParams as SpendRetrieveReportParams +from .spend_retrieve_report_response import SpendRetrieveReportResponse as SpendRetrieveReportResponse diff --git a/src/hanzoai/types/global_/spend_list_tags_params.py b/src/hanzoai/types/global_/spend_list_tags_params.py new file mode 100644 index 000000000..15fe9fac4 --- /dev/null +++ b/src/hanzoai/types/global_/spend_list_tags_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["SpendListTagsParams"] + + +class SpendListTagsParams(TypedDict, total=False): + end_date: Optional[str] + """Time till which to view key spend""" + + start_date: Optional[str] + """Time from which to start viewing key spend""" + + tags: Optional[str] + """comman separated tags to filter on""" diff --git a/src/hanzoai/types/global_/spend_list_tags_response.py b/src/hanzoai/types/global_/spend_list_tags_response.py new file mode 100644 index 000000000..719c0d818 --- /dev/null +++ b/src/hanzoai/types/global_/spend_list_tags_response.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from datetime import datetime +from typing_extensions import TypeAlias + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["SpendListTagsResponse", "SpendListTagsResponseItem"] + + +class SpendListTagsResponseItem(BaseModel): + api_key: str + + call_type: str + + end_time: Union[str, datetime, None] = FieldInfo(alias="endTime", default=None) + + messages: Union[str, List[object], object, None] = None + + request_id: str + + response: Union[str, List[object], object, None] = None + + start_time: Union[str, datetime, None] = FieldInfo(alias="startTime", default=None) + + api_base: Optional[str] = None + + cache_hit: Optional[str] = None + + cache_key: Optional[str] = None + + completion_tokens: Optional[int] = None + + metadata: Optional[object] = None + + model: Optional[str] = None + + prompt_tokens: Optional[int] = None + + request_tags: Optional[object] = None + + requester_ip_address: Optional[str] = None + + spend: Optional[float] = None + + total_tokens: Optional[int] = None + + user: Optional[str] = None + + +SpendListTagsResponse: TypeAlias = List[SpendListTagsResponseItem] diff --git a/pkg/hanzoai/types/global_/spend_retrieve_report_params.py b/src/hanzoai/types/global_/spend_retrieve_report_params.py similarity index 91% rename from pkg/hanzoai/types/global_/spend_retrieve_report_params.py rename to src/hanzoai/types/global_/spend_retrieve_report_params.py index 2b6f20909..a9887fde6 100644 --- a/pkg/hanzoai/types/global_/spend_retrieve_report_params.py +++ b/src/hanzoai/types/global_/spend_retrieve_report_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/global_/spend_retrieve_report_response.py b/src/hanzoai/types/global_/spend_retrieve_report_response.py new file mode 100644 index 000000000..27ecf6680 --- /dev/null +++ b/src/hanzoai/types/global_/spend_retrieve_report_response.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from datetime import datetime +from typing_extensions import TypeAlias + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["SpendRetrieveReportResponse", "SpendRetrieveReportResponseItem"] + + +class SpendRetrieveReportResponseItem(BaseModel): + api_key: str + + call_type: str + + end_time: Union[str, datetime, None] = FieldInfo(alias="endTime", default=None) + + messages: Union[str, List[object], object, None] = None + + request_id: str + + response: Union[str, List[object], object, None] = None + + start_time: Union[str, datetime, None] = FieldInfo(alias="startTime", default=None) + + api_base: Optional[str] = None + + cache_hit: Optional[str] = None + + cache_key: Optional[str] = None + + completion_tokens: Optional[int] = None + + metadata: Optional[object] = None + + model: Optional[str] = None + + prompt_tokens: Optional[int] = None + + request_tags: Optional[object] = None + + requester_ip_address: Optional[str] = None + + spend: Optional[float] = None + + total_tokens: Optional[int] = None + + user: Optional[str] = None + + +SpendRetrieveReportResponse: TypeAlias = List[SpendRetrieveReportResponseItem] diff --git a/src/hanzoai/types/guardrail_list_response.py b/src/hanzoai/types/guardrail_list_response.py new file mode 100644 index 000000000..5ac566eea --- /dev/null +++ b/src/hanzoai/types/guardrail_list_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional + +from .._models import BaseModel + +__all__ = ["GuardrailListResponse", "Guardrail", "GuardrailLlmParams"] + + +class GuardrailLlmParams(BaseModel): + guardrail: str + + mode: Union[str, List[str]] + + default_on: Optional[bool] = None + + +class Guardrail(BaseModel): + guardrail_info: Optional[object] = None + + guardrail_name: str + + llm_params: GuardrailLlmParams + """The returned LLM Params object for /guardrails/list""" + + +class GuardrailListResponse(BaseModel): + guardrails: List[Guardrail] diff --git a/pkg/hanzoai/types/health_check_all_params.py b/src/hanzoai/types/health_check_all_params.py similarity index 75% rename from pkg/hanzoai/types/health_check_all_params.py rename to src/hanzoai/types/health_check_all_params.py index 6b922ab57..72fee34a8 100644 --- a/pkg/hanzoai/types/health_check_all_params.py +++ b/src/hanzoai/types/health_check_all_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/health_check_services_params.py b/src/hanzoai/types/health_check_services_params.py new file mode 100644 index 000000000..6393c9ebc --- /dev/null +++ b/src/hanzoai/types/health_check_services_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["HealthCheckServicesParams"] + + +class HealthCheckServicesParams(TypedDict, total=False): + service: Required[ + Union[ + Literal[ + "slack_budget_alerts", "langfuse", "slack", "openmeter", "webhook", "email", "braintrust", "datadog" + ], + str, + ] + ] + """Specify the service being hit.""" diff --git a/src/hanzoai/types/images/__init__.py b/src/hanzoai/types/images/__init__.py new file mode 100644 index 000000000..f8ee8b14b --- /dev/null +++ b/src/hanzoai/types/images/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/src/hanzoai/types/key/__init__.py b/src/hanzoai/types/key/__init__.py new file mode 100644 index 000000000..f8ee8b14b --- /dev/null +++ b/src/hanzoai/types/key/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/src/hanzoai/types/key_block_params.py b/src/hanzoai/types/key_block_params.py new file mode 100644 index 000000000..a64986fc5 --- /dev/null +++ b/src/hanzoai/types/key_block_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["KeyBlockParams"] + + +class KeyBlockParams(TypedDict, total=False): + key: Required[str] + + llm_changed_by: Annotated[str, PropertyInfo(alias="llm-changed-by")] + """ + The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + """ diff --git a/pkg/hanzoai/types/key_block_response.py b/src/hanzoai/types/key_block_response.py similarity index 85% rename from pkg/hanzoai/types/key_block_response.py rename to src/hanzoai/types/key_block_response.py index 95aeed5cc..6408ef4e3 100644 --- a/pkg/hanzoai/types/key_block_response.py +++ b/src/hanzoai/types/key_block_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from datetime import datetime @@ -35,7 +35,7 @@ class KeyBlockResponse(BaseModel): key_name: Optional[str] = None - hanzo_budget_table: Optional[object] = None + llm_budget_table: Optional[object] = None max_budget: Optional[float] = None @@ -43,9 +43,7 @@ class KeyBlockResponse(BaseModel): metadata: Optional[object] = None - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) api_model_spend: Optional[object] = FieldInfo(alias="model_spend", default=None) diff --git a/pkg/hanzoai/types/key_check_health_response.py b/src/hanzoai/types/key_check_health_response.py similarity index 85% rename from pkg/hanzoai/types/key_check_health_response.py rename to src/hanzoai/types/key_check_health_response.py index 8589c8f11..7d4af5222 100644 --- a/pkg/hanzoai/types/key_check_health_response.py +++ b/src/hanzoai/types/key_check_health_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal diff --git a/src/hanzoai/types/key_delete_params.py b/src/hanzoai/types/key_delete_params.py new file mode 100644 index 000000000..5872e9d04 --- /dev/null +++ b/src/hanzoai/types/key_delete_params.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["KeyDeleteParams"] + + +class KeyDeleteParams(TypedDict, total=False): + key_aliases: Optional[SequenceNotStr[str]] + + keys: Optional[SequenceNotStr[str]] + + llm_changed_by: Annotated[str, PropertyInfo(alias="llm-changed-by")] + """ + The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + """ diff --git a/src/hanzoai/types/key_generate_params.py b/src/hanzoai/types/key_generate_params.py new file mode 100644 index 000000000..aab469a1f --- /dev/null +++ b/src/hanzoai/types/key_generate_params.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["KeyGenerateParams"] + + +class KeyGenerateParams(TypedDict, total=False): + aliases: Optional[object] + + allowed_cache_controls: Optional[Iterable[object]] + + blocked: Optional[bool] + + budget_duration: Optional[str] + + budget_id: Optional[str] + + config: Optional[object] + + duration: Optional[str] + + enforced_params: Optional[SequenceNotStr[str]] + + guardrails: Optional[SequenceNotStr[str]] + + key: Optional[str] + + key_alias: Optional[str] + + max_budget: Optional[float] + + max_parallel_requests: Optional[int] + + metadata: Optional[object] + + model_max_budget: Optional[object] + + model_rpm_limit: Optional[object] + + model_tpm_limit: Optional[object] + + models: Optional[Iterable[object]] + + permissions: Optional[object] + + rpm_limit: Optional[int] + + send_invite_email: Optional[bool] + + soft_budget: Optional[float] + + spend: Optional[float] + + tags: Optional[SequenceNotStr[str]] + + team_id: Optional[str] + + tpm_limit: Optional[int] + + user_id: Optional[str] + + llm_changed_by: Annotated[str, PropertyInfo(alias="llm-changed-by")] + """ + The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + """ diff --git a/pkg/hanzoai/types/key_list_params.py b/src/hanzoai/types/key_list_params.py similarity index 88% rename from pkg/hanzoai/types/key_list_params.py rename to src/hanzoai/types/key_list_params.py index d1a865b19..957637776 100644 --- a/pkg/hanzoai/types/key_list_params.py +++ b/src/hanzoai/types/key_list_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/pkg/hanzoai/types/key_list_response.py b/src/hanzoai/types/key_list_response.py similarity index 87% rename from pkg/hanzoai/types/key_list_response.py rename to src/hanzoai/types/key_list_response.py index 346d16d28..6dae4b6d7 100644 --- a/pkg/hanzoai/types/key_list_response.py +++ b/src/hanzoai/types/key_list_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, List, Union, Optional from datetime import datetime @@ -8,7 +8,6 @@ from .member import Member from .._models import BaseModel -from .user_roles import UserRoles __all__ = ["KeyListResponse", "Key", "KeyUserAPIKeyAuth"] @@ -52,7 +51,7 @@ class KeyUserAPIKeyAuth(BaseModel): last_refreshed_at: Optional[float] = None - hanzo_budget_table: Optional[object] = None + llm_budget_table: Optional[object] = None max_budget: Optional[float] = None @@ -60,9 +59,7 @@ class KeyUserAPIKeyAuth(BaseModel): metadata: Optional[object] = None - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) api_model_spend: Optional[object] = FieldInfo(alias="model_spend", default=None) @@ -120,7 +117,17 @@ class KeyUserAPIKeyAuth(BaseModel): user_id: Optional[str] = None - user_role: Optional[UserRoles] = None + user_role: Optional[ + Literal[ + "proxy_admin", + "proxy_admin_viewer", + "org_admin", + "internal_user", + "internal_user_viewer", + "team", + "customer", + ] + ] = None """ Admin Roles: PROXY_ADMIN: admin over the platform PROXY_ADMIN_VIEW_ONLY: can login, view all own keys, view all spend ORG_ADMIN: admin over a specific diff --git a/src/hanzoai/types/key_regenerate_by_key_params.py b/src/hanzoai/types/key_regenerate_by_key_params.py new file mode 100644 index 000000000..2ed9f961a --- /dev/null +++ b/src/hanzoai/types/key_regenerate_by_key_params.py @@ -0,0 +1,75 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["KeyRegenerateByKeyParams"] + + +class KeyRegenerateByKeyParams(TypedDict, total=False): + aliases: Optional[object] + + allowed_cache_controls: Optional[Iterable[object]] + + blocked: Optional[bool] + + budget_duration: Optional[str] + + budget_id: Optional[str] + + config: Optional[object] + + duration: Optional[str] + + enforced_params: Optional[SequenceNotStr[str]] + + guardrails: Optional[SequenceNotStr[str]] + + body_key: Annotated[Optional[str], PropertyInfo(alias="key")] + + key_alias: Optional[str] + + max_budget: Optional[float] + + max_parallel_requests: Optional[int] + + metadata: Optional[object] + + model_max_budget: Optional[object] + + model_rpm_limit: Optional[object] + + model_tpm_limit: Optional[object] + + models: Optional[Iterable[object]] + + new_master_key: Optional[str] + + permissions: Optional[object] + + rpm_limit: Optional[int] + + send_invite_email: Optional[bool] + + soft_budget: Optional[float] + + spend: Optional[float] + + tags: Optional[SequenceNotStr[str]] + + team_id: Optional[str] + + tpm_limit: Optional[int] + + user_id: Optional[str] + + llm_changed_by: Annotated[str, PropertyInfo(alias="llm-changed-by")] + """ + The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + """ diff --git a/pkg/hanzoai/types/key_retrieve_info_params.py b/src/hanzoai/types/key_retrieve_info_params.py similarity index 75% rename from pkg/hanzoai/types/key_retrieve_info_params.py rename to src/hanzoai/types/key_retrieve_info_params.py index daa01f462..0d6f6865c 100644 --- a/pkg/hanzoai/types/key_retrieve_info_params.py +++ b/src/hanzoai/types/key_retrieve_info_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/key_unblock_params.py b/src/hanzoai/types/key_unblock_params.py new file mode 100644 index 000000000..a6930090b --- /dev/null +++ b/src/hanzoai/types/key_unblock_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["KeyUnblockParams"] + + +class KeyUnblockParams(TypedDict, total=False): + key: Required[str] + + llm_changed_by: Annotated[str, PropertyInfo(alias="llm-changed-by")] + """ + The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + """ diff --git a/src/hanzoai/types/key_update_params.py b/src/hanzoai/types/key_update_params.py new file mode 100644 index 000000000..ebc7bca61 --- /dev/null +++ b/src/hanzoai/types/key_update_params.py @@ -0,0 +1,74 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from datetime import datetime +from typing_extensions import Required, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["KeyUpdateParams"] + + +class KeyUpdateParams(TypedDict, total=False): + key: Required[str] + + aliases: Optional[object] + + allowed_cache_controls: Optional[Iterable[object]] + + blocked: Optional[bool] + + budget_duration: Optional[str] + + budget_id: Optional[str] + + config: Optional[object] + + duration: Optional[str] + + enforced_params: Optional[SequenceNotStr[str]] + + guardrails: Optional[SequenceNotStr[str]] + + key_alias: Optional[str] + + max_budget: Optional[float] + + max_parallel_requests: Optional[int] + + metadata: Optional[object] + + model_max_budget: Optional[object] + + model_rpm_limit: Optional[object] + + model_tpm_limit: Optional[object] + + models: Optional[Iterable[object]] + + permissions: Optional[object] + + rpm_limit: Optional[int] + + spend: Optional[float] + + tags: Optional[SequenceNotStr[str]] + + team_id: Optional[str] + + temp_budget_expiry: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + temp_budget_increase: Optional[float] + + tpm_limit: Optional[int] + + user_id: Optional[str] + + llm_changed_by: Annotated[str, PropertyInfo(alias="llm-changed-by")] + """ + The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + """ diff --git a/src/hanzoai/types/member.py b/src/hanzoai/types/member.py new file mode 100644 index 000000000..933572bd4 --- /dev/null +++ b/src/hanzoai/types/member.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["Member"] + + +class Member(BaseModel): + role: Literal["admin", "user"] + + user_email: Optional[str] = None + + user_id: Optional[str] = None diff --git a/pkg/hanzoai/types/member_param.py b/src/hanzoai/types/member_param.py similarity index 77% rename from pkg/hanzoai/types/member_param.py rename to src/hanzoai/types/member_param.py index 4e315deaf..3f53c24ad 100644 --- a/pkg/hanzoai/types/member_param.py +++ b/src/hanzoai/types/member_param.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/model/__init__.py b/src/hanzoai/types/model/__init__.py new file mode 100644 index 000000000..e2b93ecfe --- /dev/null +++ b/src/hanzoai/types/model/__init__.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .info_list_params import InfoListParams as InfoListParams +from .update_full_params import UpdateFullParams as UpdateFullParams +from .update_partial_params import UpdatePartialParams as UpdatePartialParams diff --git a/src/hanzoai/types/model/info_list_params.py b/src/hanzoai/types/model/info_list_params.py new file mode 100644 index 000000000..af316c837 --- /dev/null +++ b/src/hanzoai/types/model/info_list_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["InfoListParams"] + + +class InfoListParams(TypedDict, total=False): + llm_model_id: Optional[str] diff --git a/src/hanzoai/types/model/update_full_params.py b/src/hanzoai/types/model/update_full_params.py new file mode 100644 index 000000000..0bad3219b --- /dev/null +++ b/src/hanzoai/types/model/update_full_params.py @@ -0,0 +1,90 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from ..model_info_param import ModelInfoParam +from ..configurable_clientside_params_custom_auth_param import ConfigurableClientsideParamsCustomAuthParam + +__all__ = ["UpdateFullParams", "LlmParams", "LlmParamsConfigurableClientsideAuthParam"] + + +class UpdateFullParams(TypedDict, total=False): + llm_params: Optional[LlmParams] + + model_info: Optional[ModelInfoParam] + + model_name: Optional[str] + + +LlmParamsConfigurableClientsideAuthParam: TypeAlias = Union[str, ConfigurableClientsideParamsCustomAuthParam] + + +class LlmParamsTyped(TypedDict, total=False): + api_base: Optional[str] + + api_key: Optional[str] + + api_version: Optional[str] + + aws_access_key_id: Optional[str] + + aws_region_name: Optional[str] + + aws_secret_access_key: Optional[str] + + budget_duration: Optional[str] + + configurable_clientside_auth_params: Optional[SequenceNotStr[LlmParamsConfigurableClientsideAuthParam]] + + custom_llm_provider: Optional[str] + + input_cost_per_second: Optional[float] + + input_cost_per_token: Optional[float] + + llm_trace_id: Optional[str] + + max_budget: Optional[float] + + max_file_size_mb: Optional[float] + + max_retries: Optional[int] + + merge_reasoning_content_in_choices: Optional[bool] + + model: Optional[str] + + model_info: Optional[object] + + organization: Optional[str] + + output_cost_per_second: Optional[float] + + output_cost_per_token: Optional[float] + + region_name: Optional[str] + + rpm: Optional[int] + + stream_timeout: Union[float, str, None] + + timeout: Union[float, str, None] + + tpm: Optional[int] + + use_in_pass_through: Optional[bool] + + vertex_credentials: Union[str, object, None] + + vertex_location: Optional[str] + + vertex_project: Optional[str] + + watsonx_region_name: Optional[str] + + +LlmParams: TypeAlias = Union[LlmParamsTyped, Dict[str, object]] diff --git a/src/hanzoai/types/model/update_partial_params.py b/src/hanzoai/types/model/update_partial_params.py new file mode 100644 index 000000000..d31d31a68 --- /dev/null +++ b/src/hanzoai/types/model/update_partial_params.py @@ -0,0 +1,90 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from ..model_info_param import ModelInfoParam +from ..configurable_clientside_params_custom_auth_param import ConfigurableClientsideParamsCustomAuthParam + +__all__ = ["UpdatePartialParams", "LlmParams", "LlmParamsConfigurableClientsideAuthParam"] + + +class UpdatePartialParams(TypedDict, total=False): + llm_params: Optional[LlmParams] + + model_info: Optional[ModelInfoParam] + + model_name: Optional[str] + + +LlmParamsConfigurableClientsideAuthParam: TypeAlias = Union[str, ConfigurableClientsideParamsCustomAuthParam] + + +class LlmParamsTyped(TypedDict, total=False): + api_base: Optional[str] + + api_key: Optional[str] + + api_version: Optional[str] + + aws_access_key_id: Optional[str] + + aws_region_name: Optional[str] + + aws_secret_access_key: Optional[str] + + budget_duration: Optional[str] + + configurable_clientside_auth_params: Optional[SequenceNotStr[LlmParamsConfigurableClientsideAuthParam]] + + custom_llm_provider: Optional[str] + + input_cost_per_second: Optional[float] + + input_cost_per_token: Optional[float] + + llm_trace_id: Optional[str] + + max_budget: Optional[float] + + max_file_size_mb: Optional[float] + + max_retries: Optional[int] + + merge_reasoning_content_in_choices: Optional[bool] + + model: Optional[str] + + model_info: Optional[object] + + organization: Optional[str] + + output_cost_per_second: Optional[float] + + output_cost_per_token: Optional[float] + + region_name: Optional[str] + + rpm: Optional[int] + + stream_timeout: Union[float, str, None] + + timeout: Union[float, str, None] + + tpm: Optional[int] + + use_in_pass_through: Optional[bool] + + vertex_credentials: Union[str, object, None] + + vertex_location: Optional[str] + + vertex_project: Optional[str] + + watsonx_region_name: Optional[str] + + +LlmParams: TypeAlias = Union[LlmParamsTyped, Dict[str, object]] diff --git a/src/hanzoai/types/model_create_params.py b/src/hanzoai/types/model_create_params.py new file mode 100644 index 000000000..62fac2eb7 --- /dev/null +++ b/src/hanzoai/types/model_create_params.py @@ -0,0 +1,91 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import Required, TypeAlias, TypedDict + +from .._types import SequenceNotStr +from .model_info_param import ModelInfoParam +from .configurable_clientside_params_custom_auth_param import ConfigurableClientsideParamsCustomAuthParam + +__all__ = ["ModelCreateParams", "LlmParams", "LlmParamsConfigurableClientsideAuthParam"] + + +class ModelCreateParams(TypedDict, total=False): + llm_params: Required[LlmParams] + """LLM Params with 'model' requirement - used for completions""" + + model_info: Required[ModelInfoParam] + + model_name: Required[str] + + +LlmParamsConfigurableClientsideAuthParam: TypeAlias = Union[str, ConfigurableClientsideParamsCustomAuthParam] + + +class LlmParamsTyped(TypedDict, total=False): + model: Required[str] + + api_base: Optional[str] + + api_key: Optional[str] + + api_version: Optional[str] + + aws_access_key_id: Optional[str] + + aws_region_name: Optional[str] + + aws_secret_access_key: Optional[str] + + budget_duration: Optional[str] + + configurable_clientside_auth_params: Optional[SequenceNotStr[LlmParamsConfigurableClientsideAuthParam]] + + custom_llm_provider: Optional[str] + + input_cost_per_second: Optional[float] + + input_cost_per_token: Optional[float] + + llm_trace_id: Optional[str] + + max_budget: Optional[float] + + max_file_size_mb: Optional[float] + + max_retries: Optional[int] + + merge_reasoning_content_in_choices: Optional[bool] + + model_info: Optional[object] + + organization: Optional[str] + + output_cost_per_second: Optional[float] + + output_cost_per_token: Optional[float] + + region_name: Optional[str] + + rpm: Optional[int] + + stream_timeout: Union[float, str, None] + + timeout: Union[float, str, None] + + tpm: Optional[int] + + use_in_pass_through: Optional[bool] + + vertex_credentials: Union[str, object, None] + + vertex_location: Optional[str] + + vertex_project: Optional[str] + + watsonx_region_name: Optional[str] + + +LlmParams: TypeAlias = Union[LlmParamsTyped, Dict[str, object]] diff --git a/src/hanzoai/types/model_delete_params.py b/src/hanzoai/types/model_delete_params.py new file mode 100644 index 000000000..0217a2fc8 --- /dev/null +++ b/src/hanzoai/types/model_delete_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ModelDeleteParams"] + + +class ModelDeleteParams(TypedDict, total=False): + id: Required[str] diff --git a/src/hanzoai/types/model_group_retrieve_info_params.py b/src/hanzoai/types/model_group_retrieve_info_params.py new file mode 100644 index 000000000..d7f8fe157 --- /dev/null +++ b/src/hanzoai/types/model_group_retrieve_info_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["ModelGroupRetrieveInfoParams"] + + +class ModelGroupRetrieveInfoParams(TypedDict, total=False): + model_group: Optional[str] diff --git a/pkg/hanzoai/types/model_info_param.py b/src/hanzoai/types/model_info_param.py similarity index 90% rename from pkg/hanzoai/types/model_info_param.py rename to src/hanzoai/types/model_info_param.py index 6414a94ef..5e6edde09 100644 --- a/pkg/hanzoai/types/model_info_param.py +++ b/src/hanzoai/types/model_info_param.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/model_list_params.py b/src/hanzoai/types/model_list_params.py new file mode 100644 index 000000000..1676f1034 --- /dev/null +++ b/src/hanzoai/types/model_list_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["ModelListParams"] + + +class ModelListParams(TypedDict, total=False): + return_wildcard_routes: Optional[bool] + + team_id: Optional[str] diff --git a/src/hanzoai/types/openai/__init__.py b/src/hanzoai/types/openai/__init__.py new file mode 100644 index 000000000..f8ee8b14b --- /dev/null +++ b/src/hanzoai/types/openai/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/src/hanzoai/types/openai/deployments/__init__.py b/src/hanzoai/types/openai/deployments/__init__.py new file mode 100644 index 000000000..f8ee8b14b --- /dev/null +++ b/src/hanzoai/types/openai/deployments/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/pkg/hanzoai/types/org_member_param.py b/src/hanzoai/types/org_member_param.py similarity index 79% rename from pkg/hanzoai/types/org_member_param.py rename to src/hanzoai/types/org_member_param.py index 6c5465331..edfeb44da 100644 --- a/pkg/hanzoai/types/org_member_param.py +++ b/src/hanzoai/types/org_member_param.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/organization/__init__.py b/src/hanzoai/types/organization/__init__.py new file mode 100644 index 000000000..c448898bb --- /dev/null +++ b/src/hanzoai/types/organization/__init__.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .info_retrieve_params import InfoRetrieveParams as InfoRetrieveParams +from .info_deprecated_params import InfoDeprecatedParams as InfoDeprecatedParams +from .info_retrieve_response import InfoRetrieveResponse as InfoRetrieveResponse diff --git a/src/hanzoai/types/organization/info_deprecated_params.py b/src/hanzoai/types/organization/info_deprecated_params.py new file mode 100644 index 000000000..e26d61f40 --- /dev/null +++ b/src/hanzoai/types/organization/info_deprecated_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["InfoDeprecatedParams"] + + +class InfoDeprecatedParams(TypedDict, total=False): + organizations: Required[SequenceNotStr[str]] diff --git a/src/hanzoai/types/organization/info_retrieve_params.py b/src/hanzoai/types/organization/info_retrieve_params.py new file mode 100644 index 000000000..e005813f6 --- /dev/null +++ b/src/hanzoai/types/organization/info_retrieve_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["InfoRetrieveParams"] + + +class InfoRetrieveParams(TypedDict, total=False): + organization_id: Required[str] diff --git a/pkg/hanzoai/types/organization/info_retrieve_response.py b/src/hanzoai/types/organization/info_retrieve_response.py similarity index 84% rename from pkg/hanzoai/types/organization/info_retrieve_response.py rename to src/hanzoai/types/organization/info_retrieve_response.py index 7ae5f4281..60fabc502 100644 --- a/pkg/hanzoai/types/organization/info_retrieve_response.py +++ b/src/hanzoai/types/organization/info_retrieve_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from datetime import datetime @@ -8,14 +8,7 @@ from .. import member from ..._models import BaseModel -__all__ = [ - "InfoRetrieveResponse", - "LlmBudgetTable", - "Member", - "MemberLlmBudgetTable", - "Team", - "TeamLlmModelTable", -] +__all__ = ["InfoRetrieveResponse", "LlmBudgetTable", "Member", "MemberLlmBudgetTable", "Team", "TeamLlmModelTable"] class LlmBudgetTable(BaseModel): @@ -25,9 +18,7 @@ class LlmBudgetTable(BaseModel): max_parallel_requests: Optional[int] = None - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) rpm_limit: Optional[int] = None @@ -43,9 +34,7 @@ class MemberLlmBudgetTable(BaseModel): max_parallel_requests: Optional[int] = None - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) rpm_limit: Optional[int] = None @@ -80,9 +69,7 @@ class TeamLlmModelTable(BaseModel): updated_by: str - api_model_aliases: Union[str, object, None] = FieldInfo( - alias="model_aliases", default=None - ) + api_model_aliases: Union[str, object, None] = FieldInfo(alias="model_aliases", default=None) class Team(BaseModel): diff --git a/pkg/hanzoai/types/organization_add_member_params.py b/src/hanzoai/types/organization_add_member_params.py similarity index 84% rename from pkg/hanzoai/types/organization_add_member_params.py rename to src/hanzoai/types/organization_add_member_params.py index 3608ad472..f91969d42 100644 --- a/pkg/hanzoai/types/organization_add_member_params.py +++ b/src/hanzoai/types/organization_add_member_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/organization_add_member_response.py b/src/hanzoai/types/organization_add_member_response.py new file mode 100644 index 000000000..aab3eb892 --- /dev/null +++ b/src/hanzoai/types/organization_add_member_response.py @@ -0,0 +1,133 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = [ + "OrganizationAddMemberResponse", + "UpdatedOrganizationMembership", + "UpdatedOrganizationMembershipLlmBudgetTable", + "UpdatedUser", + "UpdatedUserOrganizationMembership", + "UpdatedUserOrganizationMembershipLlmBudgetTable", +] + + +class UpdatedOrganizationMembershipLlmBudgetTable(BaseModel): + budget_duration: Optional[str] = None + + max_budget: Optional[float] = None + + max_parallel_requests: Optional[int] = None + + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) + + rpm_limit: Optional[int] = None + + soft_budget: Optional[float] = None + + tpm_limit: Optional[int] = None + + +class UpdatedOrganizationMembership(BaseModel): + created_at: datetime + + organization_id: str + + updated_at: datetime + + user_id: str + + budget_id: Optional[str] = None + + llm_budget_table: Optional[UpdatedOrganizationMembershipLlmBudgetTable] = None + """Represents user-controllable params for a LLM_BudgetTable record""" + + spend: Optional[float] = None + + user: Optional[object] = None + + user_role: Optional[str] = None + + +class UpdatedUserOrganizationMembershipLlmBudgetTable(BaseModel): + budget_duration: Optional[str] = None + + max_budget: Optional[float] = None + + max_parallel_requests: Optional[int] = None + + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) + + rpm_limit: Optional[int] = None + + soft_budget: Optional[float] = None + + tpm_limit: Optional[int] = None + + +class UpdatedUserOrganizationMembership(BaseModel): + created_at: datetime + + organization_id: str + + updated_at: datetime + + user_id: str + + budget_id: Optional[str] = None + + llm_budget_table: Optional[UpdatedUserOrganizationMembershipLlmBudgetTable] = None + """Represents user-controllable params for a LLM_BudgetTable record""" + + spend: Optional[float] = None + + user: Optional[object] = None + + user_role: Optional[str] = None + + +class UpdatedUser(BaseModel): + user_id: str + + budget_duration: Optional[str] = None + + budget_reset_at: Optional[datetime] = None + + max_budget: Optional[float] = None + + metadata: Optional[object] = None + + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) + + api_model_spend: Optional[object] = FieldInfo(alias="model_spend", default=None) + + models: Optional[List[object]] = None + + organization_memberships: Optional[List[UpdatedUserOrganizationMembership]] = None + + rpm_limit: Optional[int] = None + + spend: Optional[float] = None + + sso_user_id: Optional[str] = None + + teams: Optional[List[str]] = None + + tpm_limit: Optional[int] = None + + user_email: Optional[str] = None + + user_role: Optional[str] = None + + +class OrganizationAddMemberResponse(BaseModel): + organization_id: str + + updated_organization_memberships: List[UpdatedOrganizationMembership] + + updated_users: List[UpdatedUser] diff --git a/pkg/hanzoai/types/organization_create_params.py b/src/hanzoai/types/organization_create_params.py similarity index 88% rename from pkg/hanzoai/types/organization_create_params.py rename to src/hanzoai/types/organization_create_params.py index 142b7543e..786673409 100644 --- a/pkg/hanzoai/types/organization_create_params.py +++ b/src/hanzoai/types/organization_create_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/pkg/hanzoai/types/organization_create_response.py b/src/hanzoai/types/organization_create_response.py similarity index 84% rename from pkg/hanzoai/types/organization_create_response.py rename to src/hanzoai/types/organization_create_response.py index 8efc30114..321d2571c 100644 --- a/pkg/hanzoai/types/organization_create_response.py +++ b/src/hanzoai/types/organization_create_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from datetime import datetime diff --git a/pkg/hanzoai/types/organization_delete_member_params.py b/src/hanzoai/types/organization_delete_member_params.py similarity index 78% rename from pkg/hanzoai/types/organization_delete_member_params.py rename to src/hanzoai/types/organization_delete_member_params.py index bc2caf1b5..7054594e9 100644 --- a/pkg/hanzoai/types/organization_delete_member_params.py +++ b/src/hanzoai/types/organization_delete_member_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/organization_delete_params.py b/src/hanzoai/types/organization_delete_params.py new file mode 100644 index 000000000..99901d81a --- /dev/null +++ b/src/hanzoai/types/organization_delete_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["OrganizationDeleteParams"] + + +class OrganizationDeleteParams(TypedDict, total=False): + organization_ids: Required[SequenceNotStr[str]] diff --git a/pkg/hanzoai/types/organization_delete_response.py b/src/hanzoai/types/organization_delete_response.py similarity index 89% rename from pkg/hanzoai/types/organization_delete_response.py rename to src/hanzoai/types/organization_delete_response.py index f56c285e2..3ecb2be33 100644 --- a/pkg/hanzoai/types/organization_delete_response.py +++ b/src/hanzoai/types/organization_delete_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from datetime import datetime @@ -27,9 +27,7 @@ class OrganizationDeleteResponseItemLlmBudgetTable(BaseModel): max_parallel_requests: Optional[int] = None - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) rpm_limit: Optional[int] = None @@ -45,9 +43,7 @@ class OrganizationDeleteResponseItemMemberLlmBudgetTable(BaseModel): max_parallel_requests: Optional[int] = None - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) rpm_limit: Optional[int] = None @@ -67,9 +63,7 @@ class OrganizationDeleteResponseItemMember(BaseModel): budget_id: Optional[str] = None - llm_budget_table: Optional[OrganizationDeleteResponseItemMemberLlmBudgetTable] = ( - None - ) + llm_budget_table: Optional[OrganizationDeleteResponseItemMemberLlmBudgetTable] = None """Represents user-controllable params for a LLM_BudgetTable record""" spend: Optional[float] = None @@ -84,9 +78,7 @@ class OrganizationDeleteResponseItemTeamLlmModelTable(BaseModel): updated_by: str - api_model_aliases: Union[str, object, None] = FieldInfo( - alias="model_aliases", default=None - ) + api_model_aliases: Union[str, object, None] = FieldInfo(alias="model_aliases", default=None) class OrganizationDeleteResponseItemTeam(BaseModel): diff --git a/pkg/hanzoai/types/organization_list_response.py b/src/hanzoai/types/organization_list_response.py similarity index 89% rename from pkg/hanzoai/types/organization_list_response.py rename to src/hanzoai/types/organization_list_response.py index 9d9537f37..51cc25777 100644 --- a/pkg/hanzoai/types/organization_list_response.py +++ b/src/hanzoai/types/organization_list_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from datetime import datetime @@ -27,9 +27,7 @@ class OrganizationListResponseItemLlmBudgetTable(BaseModel): max_parallel_requests: Optional[int] = None - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) rpm_limit: Optional[int] = None @@ -45,9 +43,7 @@ class OrganizationListResponseItemMemberLlmBudgetTable(BaseModel): max_parallel_requests: Optional[int] = None - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) rpm_limit: Optional[int] = None @@ -82,9 +78,7 @@ class OrganizationListResponseItemTeamLlmModelTable(BaseModel): updated_by: str - api_model_aliases: Union[str, object, None] = FieldInfo( - alias="model_aliases", default=None - ) + api_model_aliases: Union[str, object, None] = FieldInfo(alias="model_aliases", default=None) class OrganizationListResponseItemTeam(BaseModel): diff --git a/pkg/hanzoai/types/organization_update_member_params.py b/src/hanzoai/types/organization_update_member_params.py similarity index 93% rename from pkg/hanzoai/types/organization_update_member_params.py rename to src/hanzoai/types/organization_update_member_params.py index 1fb6846b0..c42652ec6 100644 --- a/pkg/hanzoai/types/organization_update_member_params.py +++ b/src/hanzoai/types/organization_update_member_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/pkg/hanzoai/types/organization_update_member_response.py b/src/hanzoai/types/organization_update_member_response.py similarity index 83% rename from pkg/hanzoai/types/organization_update_member_response.py rename to src/hanzoai/types/organization_update_member_response.py index 2a78718aa..f2fc2e812 100644 --- a/pkg/hanzoai/types/organization_update_member_response.py +++ b/src/hanzoai/types/organization_update_member_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime @@ -17,9 +17,7 @@ class LlmBudgetTable(BaseModel): max_parallel_requests: Optional[int] = None - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) rpm_limit: Optional[int] = None diff --git a/src/hanzoai/types/organization_update_params.py b/src/hanzoai/types/organization_update_params.py new file mode 100644 index 000000000..d0e2e0650 --- /dev/null +++ b/src/hanzoai/types/organization_update_params.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +from .._types import SequenceNotStr + +__all__ = ["OrganizationUpdateParams"] + + +class OrganizationUpdateParams(TypedDict, total=False): + budget_id: Optional[str] + + metadata: Optional[object] + + models: Optional[SequenceNotStr[str]] + + organization_alias: Optional[str] + + organization_id: Optional[str] + + spend: Optional[float] + + updated_by: Optional[str] diff --git a/pkg/hanzoai/types/organization_update_response.py b/src/hanzoai/types/organization_update_response.py similarity index 88% rename from pkg/hanzoai/types/organization_update_response.py rename to src/hanzoai/types/organization_update_response.py index d76e50c9b..a7db52986 100644 --- a/pkg/hanzoai/types/organization_update_response.py +++ b/src/hanzoai/types/organization_update_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from datetime import datetime @@ -25,9 +25,7 @@ class LlmBudgetTable(BaseModel): max_parallel_requests: Optional[int] = None - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) rpm_limit: Optional[int] = None @@ -43,9 +41,7 @@ class MemberLlmBudgetTable(BaseModel): max_parallel_requests: Optional[int] = None - api_model_max_budget: Optional[object] = FieldInfo( - alias="model_max_budget", default=None - ) + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) rpm_limit: Optional[int] = None @@ -80,9 +76,7 @@ class TeamLlmModelTable(BaseModel): updated_by: str - api_model_aliases: Union[str, object, None] = FieldInfo( - alias="model_aliases", default=None - ) + api_model_aliases: Union[str, object, None] = FieldInfo(alias="model_aliases", default=None) class Team(BaseModel): diff --git a/pkg/hanzoai/types/provider_list_budgets_response.py b/src/hanzoai/types/provider_list_budgets_response.py similarity index 82% rename from pkg/hanzoai/types/provider_list_budgets_response.py rename to src/hanzoai/types/provider_list_budgets_response.py index e5903da3f..b9c73af8c 100644 --- a/pkg/hanzoai/types/provider_list_budgets_response.py +++ b/src/hanzoai/types/provider_list_budgets_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, Optional diff --git a/src/hanzoai/types/responses/__init__.py b/src/hanzoai/types/responses/__init__.py new file mode 100644 index 000000000..f8ee8b14b --- /dev/null +++ b/src/hanzoai/types/responses/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/pkg/hanzoai/types/spend_calculate_spend_params.py b/src/hanzoai/types/spend_calculate_spend_params.py similarity index 79% rename from pkg/hanzoai/types/spend_calculate_spend_params.py rename to src/hanzoai/types/spend_calculate_spend_params.py index 51abf78c6..3ec95de3b 100644 --- a/pkg/hanzoai/types/spend_calculate_spend_params.py +++ b/src/hanzoai/types/spend_calculate_spend_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/pkg/hanzoai/types/spend_list_logs_params.py b/src/hanzoai/types/spend_list_logs_params.py similarity index 88% rename from pkg/hanzoai/types/spend_list_logs_params.py rename to src/hanzoai/types/spend_list_logs_params.py index 5e5ffc0b3..82a6510d6 100644 --- a/pkg/hanzoai/types/spend_list_logs_params.py +++ b/src/hanzoai/types/spend_list_logs_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/spend_list_logs_response.py b/src/hanzoai/types/spend_list_logs_response.py new file mode 100644 index 000000000..ade57b21d --- /dev/null +++ b/src/hanzoai/types/spend_list_logs_response.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from datetime import datetime +from typing_extensions import TypeAlias + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["SpendListLogsResponse", "SpendListLogsResponseItem"] + + +class SpendListLogsResponseItem(BaseModel): + api_key: str + + call_type: str + + end_time: Union[str, datetime, None] = FieldInfo(alias="endTime", default=None) + + messages: Union[str, List[object], object, None] = None + + request_id: str + + response: Union[str, List[object], object, None] = None + + start_time: Union[str, datetime, None] = FieldInfo(alias="startTime", default=None) + + api_base: Optional[str] = None + + cache_hit: Optional[str] = None + + cache_key: Optional[str] = None + + completion_tokens: Optional[int] = None + + metadata: Optional[object] = None + + model: Optional[str] = None + + prompt_tokens: Optional[int] = None + + request_tags: Optional[object] = None + + requester_ip_address: Optional[str] = None + + spend: Optional[float] = None + + total_tokens: Optional[int] = None + + user: Optional[str] = None + + +SpendListLogsResponse: TypeAlias = List[SpendListLogsResponseItem] diff --git a/src/hanzoai/types/spend_list_tags_params.py b/src/hanzoai/types/spend_list_tags_params.py new file mode 100644 index 000000000..4531da7f1 --- /dev/null +++ b/src/hanzoai/types/spend_list_tags_params.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["SpendListTagsParams"] + + +class SpendListTagsParams(TypedDict, total=False): + end_date: Optional[str] + """Time till which to view key spend""" + + start_date: Optional[str] + """Time from which to start viewing key spend""" diff --git a/src/hanzoai/types/spend_list_tags_response.py b/src/hanzoai/types/spend_list_tags_response.py new file mode 100644 index 000000000..c5d1fc8fc --- /dev/null +++ b/src/hanzoai/types/spend_list_tags_response.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from datetime import datetime +from typing_extensions import TypeAlias + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["SpendListTagsResponse", "SpendListTagsResponseItem"] + + +class SpendListTagsResponseItem(BaseModel): + api_key: str + + call_type: str + + end_time: Union[str, datetime, None] = FieldInfo(alias="endTime", default=None) + + messages: Union[str, List[object], object, None] = None + + request_id: str + + response: Union[str, List[object], object, None] = None + + start_time: Union[str, datetime, None] = FieldInfo(alias="startTime", default=None) + + api_base: Optional[str] = None + + cache_hit: Optional[str] = None + + cache_key: Optional[str] = None + + completion_tokens: Optional[int] = None + + metadata: Optional[object] = None + + model: Optional[str] = None + + prompt_tokens: Optional[int] = None + + request_tags: Optional[object] = None + + requester_ip_address: Optional[str] = None + + spend: Optional[float] = None + + total_tokens: Optional[int] = None + + user: Optional[str] = None + + +SpendListTagsResponse: TypeAlias = List[SpendListTagsResponseItem] diff --git a/src/hanzoai/types/team/__init__.py b/src/hanzoai/types/team/__init__.py new file mode 100644 index 000000000..341f2be1c --- /dev/null +++ b/src/hanzoai/types/team/__init__.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .model_add_params import ModelAddParams as ModelAddParams +from .callback_add_params import CallbackAddParams as CallbackAddParams +from .model_remove_params import ModelRemoveParams as ModelRemoveParams diff --git a/src/hanzoai/types/team/callback_add_params.py b/src/hanzoai/types/team/callback_add_params.py new file mode 100644 index 000000000..c352a22db --- /dev/null +++ b/src/hanzoai/types/team/callback_add_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["CallbackAddParams"] + + +class CallbackAddParams(TypedDict, total=False): + callback_name: Required[str] + + callback_vars: Required[Dict[str, str]] + + callback_type: Optional[Literal["success", "failure", "success_and_failure"]] + + llm_changed_by: Annotated[str, PropertyInfo(alias="llm-changed-by")] + """ + The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + """ diff --git a/src/hanzoai/types/team/model_add_params.py b/src/hanzoai/types/team/model_add_params.py new file mode 100644 index 000000000..bd3521a6c --- /dev/null +++ b/src/hanzoai/types/team/model_add_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["ModelAddParams"] + + +class ModelAddParams(TypedDict, total=False): + models: Required[SequenceNotStr[str]] + + team_id: Required[str] diff --git a/src/hanzoai/types/team/model_remove_params.py b/src/hanzoai/types/team/model_remove_params.py new file mode 100644 index 000000000..4205c5233 --- /dev/null +++ b/src/hanzoai/types/team/model_remove_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["ModelRemoveParams"] + + +class ModelRemoveParams(TypedDict, total=False): + models: Required[SequenceNotStr[str]] + + team_id: Required[str] diff --git a/pkg/hanzoai/types/team_add_member_params.py b/src/hanzoai/types/team_add_member_params.py similarity index 83% rename from pkg/hanzoai/types/team_add_member_params.py rename to src/hanzoai/types/team_add_member_params.py index 4008f5808..dfd77a5db 100644 --- a/pkg/hanzoai/types/team_add_member_params.py +++ b/src/hanzoai/types/team_add_member_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/team_add_member_response.py b/src/hanzoai/types/team_add_member_response.py new file mode 100644 index 000000000..8bffdde29 --- /dev/null +++ b/src/hanzoai/types/team_add_member_response.py @@ -0,0 +1,169 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .member import Member +from .._models import BaseModel + +__all__ = [ + "TeamAddMemberResponse", + "UpdatedTeamMembership", + "UpdatedTeamMembershipLlmBudgetTable", + "UpdatedUser", + "UpdatedUserOrganizationMembership", + "UpdatedUserOrganizationMembershipLlmBudgetTable", + "LlmModelTable", +] + + +class UpdatedTeamMembershipLlmBudgetTable(BaseModel): + budget_duration: Optional[str] = None + + max_budget: Optional[float] = None + + max_parallel_requests: Optional[int] = None + + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) + + rpm_limit: Optional[int] = None + + soft_budget: Optional[float] = None + + tpm_limit: Optional[int] = None + + +class UpdatedTeamMembership(BaseModel): + budget_id: str + + llm_budget_table: Optional[UpdatedTeamMembershipLlmBudgetTable] = None + """Represents user-controllable params for a LLM_BudgetTable record""" + + team_id: str + + user_id: str + + +class UpdatedUserOrganizationMembershipLlmBudgetTable(BaseModel): + budget_duration: Optional[str] = None + + max_budget: Optional[float] = None + + max_parallel_requests: Optional[int] = None + + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) + + rpm_limit: Optional[int] = None + + soft_budget: Optional[float] = None + + tpm_limit: Optional[int] = None + + +class UpdatedUserOrganizationMembership(BaseModel): + created_at: datetime + + organization_id: str + + updated_at: datetime + + user_id: str + + budget_id: Optional[str] = None + + llm_budget_table: Optional[UpdatedUserOrganizationMembershipLlmBudgetTable] = None + """Represents user-controllable params for a LLM_BudgetTable record""" + + spend: Optional[float] = None + + user: Optional[object] = None + + user_role: Optional[str] = None + + +class UpdatedUser(BaseModel): + user_id: str + + budget_duration: Optional[str] = None + + budget_reset_at: Optional[datetime] = None + + max_budget: Optional[float] = None + + metadata: Optional[object] = None + + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) + + api_model_spend: Optional[object] = FieldInfo(alias="model_spend", default=None) + + models: Optional[List[object]] = None + + organization_memberships: Optional[List[UpdatedUserOrganizationMembership]] = None + + rpm_limit: Optional[int] = None + + spend: Optional[float] = None + + sso_user_id: Optional[str] = None + + teams: Optional[List[str]] = None + + tpm_limit: Optional[int] = None + + user_email: Optional[str] = None + + user_role: Optional[str] = None + + +class LlmModelTable(BaseModel): + created_by: str + + updated_by: str + + api_model_aliases: Union[str, object, None] = FieldInfo(alias="model_aliases", default=None) + + +class TeamAddMemberResponse(BaseModel): + team_id: str + + updated_team_memberships: List[UpdatedTeamMembership] + + updated_users: List[UpdatedUser] + + admins: Optional[List[object]] = None + + blocked: Optional[bool] = None + + budget_duration: Optional[str] = None + + budget_reset_at: Optional[datetime] = None + + created_at: Optional[datetime] = None + + llm_model_table: Optional[LlmModelTable] = None + + max_budget: Optional[float] = None + + max_parallel_requests: Optional[int] = None + + members: Optional[List[object]] = None + + members_with_roles: Optional[List[Member]] = None + + metadata: Optional[object] = None + + api_model_id: Optional[int] = FieldInfo(alias="model_id", default=None) + + models: Optional[List[object]] = None + + organization_id: Optional[str] = None + + rpm_limit: Optional[int] = None + + spend: Optional[float] = None + + team_alias: Optional[str] = None + + tpm_limit: Optional[int] = None diff --git a/src/hanzoai/types/team_block_params.py b/src/hanzoai/types/team_block_params.py new file mode 100644 index 000000000..1286bbdb3 --- /dev/null +++ b/src/hanzoai/types/team_block_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["TeamBlockParams"] + + +class TeamBlockParams(TypedDict, total=False): + team_id: Required[str] diff --git a/src/hanzoai/types/team_create_params.py b/src/hanzoai/types/team_create_params.py new file mode 100644 index 000000000..f7a370bb2 --- /dev/null +++ b/src/hanzoai/types/team_create_params.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo +from .member_param import MemberParam + +__all__ = ["TeamCreateParams"] + + +class TeamCreateParams(TypedDict, total=False): + admins: Iterable[object] + + blocked: bool + + budget_duration: Optional[str] + + guardrails: Optional[SequenceNotStr[str]] + + max_budget: Optional[float] + + members: Iterable[object] + + members_with_roles: Iterable[MemberParam] + + metadata: Optional[object] + + model_aliases: Optional[object] + + models: Iterable[object] + + organization_id: Optional[str] + + rpm_limit: Optional[int] + + tags: Optional[Iterable[object]] + + team_alias: Optional[str] + + team_id: Optional[str] + + tpm_limit: Optional[int] + + llm_changed_by: Annotated[str, PropertyInfo(alias="llm-changed-by")] + """ + The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + """ diff --git a/src/hanzoai/types/team_create_response.py b/src/hanzoai/types/team_create_response.py new file mode 100644 index 000000000..d95c55e9e --- /dev/null +++ b/src/hanzoai/types/team_create_response.py @@ -0,0 +1,59 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .member import Member +from .._models import BaseModel + +__all__ = ["TeamCreateResponse", "LlmModelTable"] + + +class LlmModelTable(BaseModel): + created_by: str + + updated_by: str + + api_model_aliases: Union[str, object, None] = FieldInfo(alias="model_aliases", default=None) + + +class TeamCreateResponse(BaseModel): + team_id: str + + admins: Optional[List[object]] = None + + blocked: Optional[bool] = None + + budget_duration: Optional[str] = None + + budget_reset_at: Optional[datetime] = None + + created_at: Optional[datetime] = None + + llm_model_table: Optional[LlmModelTable] = None + + max_budget: Optional[float] = None + + max_parallel_requests: Optional[int] = None + + members: Optional[List[object]] = None + + members_with_roles: Optional[List[Member]] = None + + metadata: Optional[object] = None + + api_model_id: Optional[int] = FieldInfo(alias="model_id", default=None) + + models: Optional[List[object]] = None + + organization_id: Optional[str] = None + + rpm_limit: Optional[int] = None + + spend: Optional[float] = None + + team_alias: Optional[str] = None + + tpm_limit: Optional[int] = None diff --git a/src/hanzoai/types/team_delete_params.py b/src/hanzoai/types/team_delete_params.py new file mode 100644 index 000000000..7e3a49f96 --- /dev/null +++ b/src/hanzoai/types/team_delete_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["TeamDeleteParams"] + + +class TeamDeleteParams(TypedDict, total=False): + team_ids: Required[SequenceNotStr[str]] + + llm_changed_by: Annotated[str, PropertyInfo(alias="llm-changed-by")] + """ + The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + """ diff --git a/src/hanzoai/types/team_list_available_params.py b/src/hanzoai/types/team_list_available_params.py new file mode 100644 index 000000000..68d67049a --- /dev/null +++ b/src/hanzoai/types/team_list_available_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["TeamListAvailableParams"] + + +class TeamListAvailableParams(TypedDict, total=False): + response_model: object diff --git a/pkg/hanzoai/types/team_list_params.py b/src/hanzoai/types/team_list_params.py similarity index 78% rename from pkg/hanzoai/types/team_list_params.py rename to src/hanzoai/types/team_list_params.py index f3c029210..ed9cec724 100644 --- a/pkg/hanzoai/types/team_list_params.py +++ b/src/hanzoai/types/team_list_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/pkg/hanzoai/types/team_remove_member_params.py b/src/hanzoai/types/team_remove_member_params.py similarity index 77% rename from pkg/hanzoai/types/team_remove_member_params.py rename to src/hanzoai/types/team_remove_member_params.py index 7b59476d5..a9f7133d3 100644 --- a/pkg/hanzoai/types/team_remove_member_params.py +++ b/src/hanzoai/types/team_remove_member_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/team_retrieve_info_params.py b/src/hanzoai/types/team_retrieve_info_params.py new file mode 100644 index 000000000..b19d76afb --- /dev/null +++ b/src/hanzoai/types/team_retrieve_info_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["TeamRetrieveInfoParams"] + + +class TeamRetrieveInfoParams(TypedDict, total=False): + team_id: str + """Team ID in the request parameters""" diff --git a/src/hanzoai/types/team_unblock_params.py b/src/hanzoai/types/team_unblock_params.py new file mode 100644 index 000000000..b179bb5b6 --- /dev/null +++ b/src/hanzoai/types/team_unblock_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["TeamUnblockParams"] + + +class TeamUnblockParams(TypedDict, total=False): + team_id: Required[str] diff --git a/pkg/hanzoai/types/team_update_member_params.py b/src/hanzoai/types/team_update_member_params.py similarity index 81% rename from pkg/hanzoai/types/team_update_member_params.py rename to src/hanzoai/types/team_update_member_params.py index 3edf91bc8..97bdb59f8 100644 --- a/pkg/hanzoai/types/team_update_member_params.py +++ b/src/hanzoai/types/team_update_member_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/pkg/hanzoai/types/team_update_member_response.py b/src/hanzoai/types/team_update_member_response.py similarity index 75% rename from pkg/hanzoai/types/team_update_member_response.py rename to src/hanzoai/types/team_update_member_response.py index 15a1786ba..85c9afedf 100644 --- a/pkg/hanzoai/types/team_update_member_response.py +++ b/src/hanzoai/types/team_update_member_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/hanzoai/types/team_update_params.py b/src/hanzoai/types/team_update_params.py new file mode 100644 index 000000000..432c7ecdb --- /dev/null +++ b/src/hanzoai/types/team_update_params.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Required, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["TeamUpdateParams"] + + +class TeamUpdateParams(TypedDict, total=False): + team_id: Required[str] + + blocked: Optional[bool] + + budget_duration: Optional[str] + + guardrails: Optional[SequenceNotStr[str]] + + max_budget: Optional[float] + + metadata: Optional[object] + + model_aliases: Optional[object] + + models: Optional[Iterable[object]] + + organization_id: Optional[str] + + rpm_limit: Optional[int] + + tags: Optional[Iterable[object]] + + team_alias: Optional[str] + + tpm_limit: Optional[int] + + llm_changed_by: Annotated[str, PropertyInfo(alias="llm-changed-by")] + """ + The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + """ diff --git a/src/hanzoai/types/threads/__init__.py b/src/hanzoai/types/threads/__init__.py new file mode 100644 index 000000000..f8ee8b14b --- /dev/null +++ b/src/hanzoai/types/threads/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/pkg/hanzoai/types/user_create_params.py b/src/hanzoai/types/user_create_params.py similarity index 76% rename from pkg/hanzoai/types/user_create_params.py rename to src/hanzoai/types/user_create_params.py index b3e0a11a4..e0b69e95f 100644 --- a/pkg/hanzoai/types/user_create_params.py +++ b/src/hanzoai/types/user_create_params.py @@ -1,10 +1,12 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations -from typing import List, Iterable, Optional +from typing import Iterable, Optional from typing_extensions import Literal, TypedDict +from .._types import SequenceNotStr + __all__ = ["UserCreateParams"] @@ -23,7 +25,7 @@ class UserCreateParams(TypedDict, total=False): duration: Optional[str] - guardrails: Optional[List[str]] + guardrails: Optional[SequenceNotStr[str]] key_alias: Optional[str] @@ -61,8 +63,4 @@ class UserCreateParams(TypedDict, total=False): user_id: Optional[str] - user_role: Optional[ - Literal[ - "proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer" - ] - ] + user_role: Optional[Literal["proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer"]] diff --git a/src/hanzoai/types/user_create_response.py b/src/hanzoai/types/user_create_response.py new file mode 100644 index 000000000..d298841d5 --- /dev/null +++ b/src/hanzoai/types/user_create_response.py @@ -0,0 +1,85 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["UserCreateResponse"] + + +class UserCreateResponse(BaseModel): + expires: Optional[datetime] = None + + key: str + + token: Optional[str] = None + + aliases: Optional[object] = None + + allowed_cache_controls: Optional[List[object]] = None + + blocked: Optional[bool] = None + + budget_duration: Optional[str] = None + + budget_id: Optional[str] = None + + config: Optional[object] = None + + created_by: Optional[str] = None + + duration: Optional[str] = None + + enforced_params: Optional[List[str]] = None + + guardrails: Optional[List[str]] = None + + key_alias: Optional[str] = None + + key_name: Optional[str] = None + + llm_budget_table: Optional[object] = None + + max_budget: Optional[float] = None + + max_parallel_requests: Optional[int] = None + + metadata: Optional[object] = None + + api_model_max_budget: Optional[object] = FieldInfo(alias="model_max_budget", default=None) + + api_model_rpm_limit: Optional[object] = FieldInfo(alias="model_rpm_limit", default=None) + + api_model_tpm_limit: Optional[object] = FieldInfo(alias="model_tpm_limit", default=None) + + models: Optional[List[object]] = None + + permissions: Optional[object] = None + + rpm_limit: Optional[int] = None + + spend: Optional[float] = None + + tags: Optional[List[str]] = None + + team_id: Optional[str] = None + + teams: Optional[List[object]] = None + + token_id: Optional[str] = None + + tpm_limit: Optional[int] = None + + updated_by: Optional[str] = None + + user_alias: Optional[str] = None + + user_email: Optional[str] = None + + user_id: Optional[str] = None + + user_role: Optional[Literal["proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer"]] = None diff --git a/src/hanzoai/types/user_delete_params.py b/src/hanzoai/types/user_delete_params.py new file mode 100644 index 000000000..eac116b3e --- /dev/null +++ b/src/hanzoai/types/user_delete_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["UserDeleteParams"] + + +class UserDeleteParams(TypedDict, total=False): + user_ids: Required[SequenceNotStr[str]] + + llm_changed_by: Annotated[str, PropertyInfo(alias="llm-changed-by")] + """ + The llm-changed-by header enables tracking of actions performed by authorized + users on behalf of other users, providing an audit trail for accountability + """ diff --git a/pkg/hanzoai/types/user_list_params.py b/src/hanzoai/types/user_list_params.py similarity index 82% rename from pkg/hanzoai/types/user_list_params.py rename to src/hanzoai/types/user_list_params.py index ee7ff9a78..06bc8dd51 100644 --- a/pkg/hanzoai/types/user_list_params.py +++ b/src/hanzoai/types/user_list_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/pkg/hanzoai/types/user_retrieve_info_params.py b/src/hanzoai/types/user_retrieve_info_params.py similarity index 75% rename from pkg/hanzoai/types/user_retrieve_info_params.py rename to src/hanzoai/types/user_retrieve_info_params.py index 27345169d..61ffce8f5 100644 --- a/pkg/hanzoai/types/user_retrieve_info_params.py +++ b/src/hanzoai/types/user_retrieve_info_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/hanzoai/types/user_update_params.py b/src/hanzoai/types/user_update_params.py new file mode 100644 index 000000000..0f0a9ddd6 --- /dev/null +++ b/src/hanzoai/types/user_update_params.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["UserUpdateParams"] + + +class UserUpdateParams(TypedDict, total=False): + aliases: Optional[object] + + allowed_cache_controls: Optional[Iterable[object]] + + blocked: Optional[bool] + + budget_duration: Optional[str] + + config: Optional[object] + + duration: Optional[str] + + guardrails: Optional[SequenceNotStr[str]] + + key_alias: Optional[str] + + max_budget: Optional[float] + + max_parallel_requests: Optional[int] + + metadata: Optional[object] + + model_max_budget: Optional[object] + + model_rpm_limit: Optional[object] + + model_tpm_limit: Optional[object] + + models: Optional[Iterable[object]] + + password: Optional[str] + + permissions: Optional[object] + + rpm_limit: Optional[int] + + spend: Optional[float] + + team_id: Optional[str] + + tpm_limit: Optional[int] + + user_email: Optional[str] + + user_id: Optional[str] + + user_role: Optional[Literal["proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer"]] diff --git a/src/hanzoai/types/util_get_supported_openai_params_params.py b/src/hanzoai/types/util_get_supported_openai_params_params.py new file mode 100644 index 000000000..5a16b3b29 --- /dev/null +++ b/src/hanzoai/types/util_get_supported_openai_params_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["UtilGetSupportedOpenAIParamsParams"] + + +class UtilGetSupportedOpenAIParamsParams(TypedDict, total=False): + model: Required[str] diff --git a/pkg/hanzoai/types/util_token_counter_params.py b/src/hanzoai/types/util_token_counter_params.py similarity index 78% rename from pkg/hanzoai/types/util_token_counter_params.py rename to src/hanzoai/types/util_token_counter_params.py index 091c1bedd..ddbc07510 100644 --- a/pkg/hanzoai/types/util_token_counter_params.py +++ b/src/hanzoai/types/util_token_counter_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/pkg/hanzoai/types/util_token_counter_response.py b/src/hanzoai/types/util_token_counter_response.py similarity index 76% rename from pkg/hanzoai/types/util_token_counter_response.py rename to src/hanzoai/types/util_token_counter_response.py index 836a0d1dc..4676deec2 100644 --- a/pkg/hanzoai/types/util_token_counter_response.py +++ b/src/hanzoai/types/util_token_counter_response.py @@ -1,5 +1,4 @@ -# Hanzo AI SDK - +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from pydantic import Field as FieldInfo diff --git a/pkg/hanzoai/types/util_transform_request_params.py b/src/hanzoai/types/util_transform_request_params.py similarity index 96% rename from pkg/hanzoai/types/util_transform_request_params.py rename to src/hanzoai/types/util_transform_request_params.py index 7d15230a5..85941f650 100644 --- a/pkg/hanzoai/types/util_transform_request_params.py +++ b/src/hanzoai/types/util_transform_request_params.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/pkg/hanzoai/types/util_transform_request_response.py b/src/hanzoai/types/util_transform_request_response.py similarity index 79% rename from pkg/hanzoai/types/util_transform_request_response.py rename to src/hanzoai/types/util_transform_request_response.py index 0a735bc20..06cbbafe4 100644 --- a/pkg/hanzoai/types/util_transform_request_response.py +++ b/src/hanzoai/types/util_transform_request_response.py @@ -1,4 +1,4 @@ -# Hanzo AI SDK +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/templates/architecture.md b/templates/architecture.md deleted file mode 100644 index b2a5a8d15..000000000 --- a/templates/architecture.md +++ /dev/null @@ -1,163 +0,0 @@ -# Project Architecture Decisions - -## Database Choice: Hybrid Memory System - -**Date**: 2025-01-12 -**Status**: Implemented -**Context**: Need for transparent, searchable memory management for AI agents - -### Decision -Implemented hybrid memory system combining: -- **Plaintext markdown files** for human-readable rules and context -- **SQLite with FTS5** for full-text search across all content -- **sqlite-vec extension** for vector similarity search -- **Layered search** across markdown + SQLite + vectors - -### Rationale -- **Human-readable**: Markdown files can be edited with any text editor -- **Git-trackable**: Memory changes are version controlled -- **Searchable**: FTS5 provides fast full-text search without external dependencies -- **Semantic search**: sqlite-vec enables embedding-based similarity search -- **Portable**: Single-file databases per project, no external services -- **Standard**: Follows LLM.md pattern already established in the project - -### Implementation -``` -/project/ -โ”œโ”€โ”€ LLM.md # Project context (existing) -โ”œโ”€โ”€ .hanzo/ -โ”‚ โ”œโ”€โ”€ memory/ # Project memories -โ”‚ โ”‚ โ”œโ”€โ”€ architecture.md # This file -โ”‚ โ”‚ โ”œโ”€โ”€ patterns.md # Code patterns -โ”‚ โ”‚ โ”œโ”€โ”€ decisions.md # Technical decisions -โ”‚ โ”‚ โ””โ”€โ”€ sessions/ # Session logs -โ”‚ โ”‚ โ”œโ”€โ”€ 2025-01-12.md -โ”‚ โ”‚ โ””โ”€โ”€ 2025-01-13.md -โ”‚ โ””โ”€โ”€ db/ -โ”‚ โ”œโ”€โ”€ project.db # Existing project DB -โ”‚ โ”œโ”€โ”€ graph.db # Existing graph DB -โ”‚ โ””โ”€โ”€ memory.db # New hybrid memory DB - -~/.hanzo/ -โ”œโ”€โ”€ memory/ # Global memories -โ”‚ โ”œโ”€โ”€ rules.md # System rules -โ”‚ โ”œโ”€โ”€ user_preferences.md # User preferences -โ”‚ โ””โ”€โ”€ coding_standards.md # Coding standards -โ””โ”€โ”€ db/ - โ””โ”€โ”€ global_memory.db # Global memory DB -``` - -### Database Schema -```sql --- Markdown files index -CREATE TABLE markdown_files ( - id INTEGER PRIMARY KEY, - path TEXT NOT NULL UNIQUE, - content TEXT NOT NULL, - category TEXT, - scope TEXT CHECK(scope IN ('global', 'project')), - modified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- Structured memories -CREATE TABLE memories ( - id INTEGER PRIMARY KEY, - content TEXT NOT NULL, - category TEXT, - importance INTEGER DEFAULT 5, - metadata TEXT, -- JSON - scope TEXT CHECK(scope IN ('global', 'project', 'session')), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- Full-text search indexes -CREATE VIRTUAL TABLE markdown_fts USING fts5(...); -CREATE VIRTUAL TABLE memories_fts USING fts5(...); - --- Vector embeddings (when sqlite-vec available) -CREATE TABLE embeddings ( - id INTEGER PRIMARY KEY, - source_table TEXT NOT NULL, - source_id INTEGER NOT NULL, - embedding BLOB, - model TEXT DEFAULT 'bge-small-en-v1.5' -); -``` - -### API Design -```python -# Unified memory tool with actions -memory(action="read", file_path="rules.md", scope="global") -memory(action="write", file_path="architecture.md", content="...", scope="project") -memory(action="append", file_path="sessions/today.md", content="New insight") -memory(action="search", content="database design", scope="both") -memory(action="create", content="Important fact", category="general") -memory(action="list", scope="project") -memory(action="stats") -``` - -### Benefits -1. **Transparency**: All memory storage is human-readable and editable -2. **Performance**: Local SQLite with FTS5 provides fast search -3. **Flexibility**: Supports both structured and unstructured data -4. **Portability**: No external dependencies, works offline -5. **Extensibility**: Can add vector search when embeddings are available - -### Trade-offs -- **Complexity**: More complex than pure markdown or pure database approach -- **Storage**: Dual storage requires synchronization between files and database -- **Vector search**: Requires sqlite-vec extension installation - ---- - -## Tool Architecture: Modular Package System - -**Date**: 2024-12-01 (from LLM.md) -**Status**: Implemented -**Context**: Zero code duplication, dynamic tool discovery - -### Decision -Modular tool package system with entry-point based discovery: -- `hanzo-mcp`: Thin wrapper that discovers tools via entry points -- `hanzo-tools-*`: Independent tool packages -- Dynamic loading without server restart - -### Benefits -- **Zero duplication**: Tools live in single packages only -- **Modularity**: Install only needed tools -- **Dynamic**: Add/remove tools without restart -- **Maintainability**: Clear separation of concerns - ---- - -## Memory Integration Strategy - -**Date**: 2025-01-12 -**Status**: In Progress - -### Phase 1: Add hybrid memory alongside existing system -- โœ“ Implement MemoryManager class -- โœ“ Create MemoryTool with unified actions -- โœ“ Add to hanzo-tools-database package -- โœ“ Support markdown + SQLite + FTS5 - -### Phase 2: Enhanced integration -- [ ] Add vector embedding generation (FastEmbed integration) -- [ ] Implement sqlite-vec vector search -- [ ] Auto-append to session memories from think/critic tools -- [ ] Template system for common memory structures - -### Phase 3: Migration from existing memory system -- [ ] Import important memories from hanzo-memory package -- [ ] Provide migration tools for existing vector data -- [ ] Deprecate complex LLM/InfinityDB system - -### Phase 4: Full optimization -- [ ] Keep SQLite for structured data (file metadata, symbols, graph) -- [ ] Markdown for rules/context, SQLite for search performance -- [ ] Vector search for semantic similarity when available - ---- - -*This file tracks major architectural decisions for the project.* -*Add new decisions with date, status, context, and rationale.* \ No newline at end of file diff --git a/templates/coding_standards.md b/templates/coding_standards.md deleted file mode 100644 index 3b6f4ff37..000000000 --- a/templates/coding_standards.md +++ /dev/null @@ -1,169 +0,0 @@ -# Coding Standards - -## Python Standards - -### Code Formatting -- **Line Length**: 88 characters (Black default) -- **Imports**: Use isort with Black-compatible settings -- **Quotes**: Double quotes for strings, single quotes for string literals in code -- **Indentation**: 4 spaces, no tabs - -### Naming Conventions -- **Variables/Functions**: snake_case (e.g., `user_name`, `calculate_total`) -- **Classes**: PascalCase (e.g., `UserManager`, `DatabaseConnection`) -- **Constants**: UPPER_SNAKE_CASE (e.g., `MAX_RETRY_ATTEMPTS`) -- **Private**: Leading underscore (e.g., `_internal_method`) - -### Type Hints -- **Required**: All function signatures must have type hints -- **Return Types**: Always specify return types, use `None` for procedures -- **Generics**: Use generic types for containers (List[str], Dict[str, Any]) -- **Optional**: Use `Optional[T]` for nullable parameters - -```python -def process_data( - items: List[Dict[str, Any]], - threshold: float = 0.5 -) -> Optional[ProcessedData]: - """Process data items with optional threshold.""" - pass -``` - -### Documentation -- **Docstrings**: Google style for all public functions and classes -- **Comments**: Explain why, not what (code should be self-documenting) -- **TODO/FIXME**: Include issue numbers when applicable - -```python -def calculate_similarity(text1: str, text2: str) -> float: - """Calculate semantic similarity between two texts. - - Args: - text1: First text to compare - text2: Second text to compare - - Returns: - Similarity score between 0 and 1 - - Raises: - ValueError: If either text is empty - """ - pass -``` - -### Error Handling -- **Specific Exceptions**: Catch specific exceptions, not bare `except:` -- **Error Messages**: Include context in error messages -- **Logging**: Log errors with appropriate levels -- **Recovery**: Implement graceful degradation when possible - -```python -try: - result = risky_operation() -except SpecificError as e: - logger.error(f"Operation failed for {context}: {e}") - raise ProcessingError(f"Cannot process {item_name}") from e -``` - -## JavaScript/TypeScript Standards - -### Code Formatting -- **Tool**: Prettier with default settings -- **Semicolons**: Always use semicolons -- **Quotes**: Single quotes for strings, double quotes for JSX attributes -- **Trailing Commas**: Always include trailing commas - -### Naming Conventions -- **Variables/Functions**: camelCase (e.g., `userName`, `calculateTotal`) -- **Classes**: PascalCase (e.g., `UserManager`, `DatabaseConnection`) -- **Constants**: UPPER_SNAKE_CASE (e.g., `MAX_RETRY_ATTEMPTS`) -- **Files**: kebab-case for components, camelCase for utilities - -### TypeScript Specifics -- **Interfaces**: Use interfaces for object shapes -- **Types**: Use type aliases for unions and complex types -- **Strict Mode**: Enable strict TypeScript settings -- **Null Safety**: Use strict null checks - -```typescript -interface UserData { - id: string; - name: string; - email?: string; -} - -type ProcessingResult = 'success' | 'failure' | 'pending'; - -function processUser(user: UserData): ProcessingResult { - // Implementation -} -``` - -## SQL Standards - -### Formatting -- **Keywords**: UPPERCASE for SQL keywords (SELECT, FROM, WHERE) -- **Names**: snake_case for table and column names -- **Indentation**: Align clauses and subqueries -- **Line Breaks**: One clause per line for complex queries - -```sql -SELECT - u.id, - u.name, - COUNT(p.id) as post_count -FROM users u -LEFT JOIN posts p ON p.user_id = u.id -WHERE u.created_at > '2024-01-01' -GROUP BY u.id, u.name -ORDER BY post_count DESC; -``` - -### Design -- **Primary Keys**: Always use surrogate keys (id) -- **Foreign Keys**: Explicit foreign key constraints -- **Indexes**: Index foreign keys and frequently queried columns -- **Naming**: Consistent naming conventions - -## Markdown Standards - -### Structure -- **Headings**: Use ATX-style headers (#, ##, ###) -- **Lists**: Use dashes (-) for unordered lists -- **Code Blocks**: Always specify language for syntax highlighting -- **Links**: Use descriptive link text - -### Organization -- **TOC**: Include table of contents for long documents -- **Sections**: Logical section organization with consistent depth -- **Examples**: Include code examples where relevant -- **Updates**: Track document updates with dates - -## Git Standards - -### Commit Messages -- **Format**: Conventional Commits (feat:, fix:, docs:, etc.) -- **Length**: 50 character summary, detailed description if needed -- **Imperative**: Use imperative mood ("Add feature" not "Added feature") - -``` -feat(memory): add hybrid markdown and SQLite storage - -- Implement MemoryManager class with dual storage -- Add FTS5 support for full-text search -- Include sqlite-vec integration for vector search -- Support both global and project-specific contexts - -Closes #123 -``` - -### Branching -- **Feature**: `feature/description` or `feature/issue-number` -- **Bugfix**: `fix/description` or `fix/issue-number` -- **Hotfix**: `hotfix/description` -- **Release**: `release/version` - ---- - -*Last updated: 2025-01-12* -*Applies to: All Hanzo projects* \ No newline at end of file diff --git a/templates/global_rules.md b/templates/global_rules.md deleted file mode 100644 index 8a4d0e8ea..000000000 --- a/templates/global_rules.md +++ /dev/null @@ -1,84 +0,0 @@ -# Hanzo AI System Rules - -## Core Principles - -### Code Quality Standards -- **Error Handling**: Always implement proper error handling with meaningful messages -- **Type Safety**: Use type hints in Python, TypeScript definitions for JavaScript -- **Testing**: Maintain >80% test coverage, write tests before implementation -- **Documentation**: Document complex algorithms and architectural decisions -- **Performance**: Profile before optimizing, prefer readability over premature optimization - -### Security Guidelines -- **Data Protection**: Never log sensitive data (passwords, tokens, PII) -- **Environment Variables**: Use environment variables for all secrets and configuration -- **Input Validation**: Validate and sanitize all user inputs -- **Least Privilege**: Follow principle of least privilege for permissions -- **Dependencies**: Keep dependencies updated, audit for vulnerabilities - -### Communication Style -- **Clarity**: Be concise but thorough in explanations -- **Context**: Provide reasoning for complex technical decisions -- **Questions**: Ask clarifying questions when requirements are ambiguous -- **Examples**: Include code examples when explaining concepts -- **Feedback**: Request feedback on uncertain implementations - -## Architecture Patterns - -### Database Design -- **SQLite First**: Use SQLite for embedded databases, PostgreSQL for distributed systems -- **Migrations**: Always use database migrations for schema changes -- **Indexing**: Create appropriate indexes for query performance -- **Normalization**: Normalize to 3NF unless denormalization is justified for performance - -### API Design -- **RESTful**: Follow REST principles for HTTP APIs -- **Versioning**: Version APIs from day one (v1, v2, etc.) -- **Error Responses**: Consistent error response format with status codes -- **Pagination**: Implement pagination for list endpoints -- **Rate Limiting**: Implement rate limiting for public APIs - -### Memory Management -- **Hybrid Storage**: Use markdown files for human-readable rules, SQLite for structured data -- **Full-Text Search**: Implement FTS5 for searchable content -- **Vector Search**: Use sqlite-vec for semantic similarity when available -- **Scoping**: Support global and project-specific memory contexts - -## Development Workflow - -### Git Practices -- **Commits**: Write descriptive commit messages following conventional commits -- **Branches**: Use feature branches, never commit directly to main -- **Reviews**: Require code reviews for all changes -- **CI/CD**: Automated testing and deployment pipelines - -### Code Organization -- **Modules**: Keep modules focused and cohesive -- **Dependencies**: Minimize external dependencies, prefer standard libraries -- **Configuration**: Centralize configuration management -- **Logging**: Structured logging with appropriate levels - -## AI Assistant Guidelines - -### Code Generation -- **Completeness**: Generate complete, runnable code examples -- **Best Practices**: Follow established patterns and conventions -- **Error Handling**: Include appropriate error handling in generated code -- **Comments**: Add comments for complex logic or business rules - -### Code Review -- **Constructive**: Provide constructive feedback with specific suggestions -- **Standards**: Check adherence to coding standards and patterns -- **Security**: Review for potential security vulnerabilities -- **Performance**: Identify potential performance issues - -### Problem Solving -- **Analysis**: Break down complex problems into smaller components -- **Research**: Reference relevant documentation and examples -- **Testing**: Suggest appropriate testing strategies -- **Alternatives**: Consider multiple approaches and trade-offs - ---- - -*Last updated: 2025-01-12* -*Version: 1.0* \ No newline at end of file diff --git a/templates/user_preferences.md b/templates/user_preferences.md deleted file mode 100644 index 72b42a603..000000000 --- a/templates/user_preferences.md +++ /dev/null @@ -1,36 +0,0 @@ -# User Preferences - -## Development Environment -- **Editor**: VS Code with Python, TypeScript, and Markdown extensions -- **Shell**: zsh with oh-my-zsh configuration -- **Terminal**: iTerm2 with tmux for session management -- **Package Manager**: uv for Python, npm for JavaScript/TypeScript - -## Coding Style Preferences -- **Python**: Black formatting, isort for imports, flake8 for linting -- **JavaScript/TypeScript**: Prettier formatting, ESLint for linting -- **Markdown**: Standard formatting with consistent heading styles -- **Documentation**: Prefer inline comments for complex logic, README for project overview - -## Communication Preferences -- **Verbosity**: Moderate detail - explain reasoning but avoid excessive verbosity -- **Examples**: Always include code examples for implementation suggestions -- **Format**: Use markdown formatting for structured responses -- **Questions**: Prefer clarifying questions over assumptions - -## Project Organization -- **Structure**: Follow standard project layouts (src/, tests/, docs/) -- **Naming**: Use descriptive names, prefer explicit over clever -- **Dependencies**: Minimize dependencies, prefer well-maintained packages -- **Configuration**: Keep configuration in dedicated files (pyproject.toml, package.json) - -## AI Assistant Behavior -- **Proactive**: Suggest improvements and best practices -- **Context-Aware**: Reference project patterns and existing code -- **Educational**: Explain the reasoning behind suggestions -- **Efficient**: Provide complete, working solutions when possible - ---- - -*Last updated: 2025-01-12* -*User: hanzo-developer* \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/__init__.py b/tests/api_resources/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/__init__.py +++ b/tests/api_resources/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/audio/__init__.py b/tests/api_resources/audio/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/audio/__init__.py +++ b/tests/api_resources/audio/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py index 3bd7aaf95..351c4837f 100644 --- a/tests/api_resources/audio/test_speech.py +++ b/tests/api_resources/audio/test_speech.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,25 +16,28 @@ class TestSpeech: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: speech = client.audio.speech.create() assert_matches_type(object, speech, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.audio.speech.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" speech = response.parse() assert_matches_type(object, speech, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.audio.speech.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" speech = response.parse() assert_matches_type(object, speech, path=["response"]) @@ -43,27 +46,32 @@ def test_streaming_response_create(self, client: Hanzo) -> None: class TestAsyncSpeech: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: speech = await async_client.audio.speech.create() assert_matches_type(object, speech, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.audio.speech.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" speech = await response.parse() assert_matches_type(object, speech, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.audio.speech.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" speech = await response.parse() assert_matches_type(object, speech, path=["response"]) diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py index f62f3144e..d9ffbf8fe 100644 --- a/tests/api_resources/audio/test_transcriptions.py +++ b/tests/api_resources/audio/test_transcriptions.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestTranscriptions: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: transcription = client.audio.transcriptions.create( @@ -23,6 +24,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, transcription, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.audio.transcriptions.with_raw_response.create( @@ -30,17 +32,18 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" transcription = response.parse() assert_matches_type(object, transcription, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.audio.transcriptions.with_streaming_response.create( file=b"raw file contents", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" transcription = response.parse() assert_matches_type(object, transcription, path=["response"]) @@ -49,8 +52,11 @@ def test_streaming_response_create(self, client: Hanzo) -> None: class TestAsyncTranscriptions: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: transcription = await async_client.audio.transcriptions.create( @@ -58,6 +64,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, transcription, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.audio.transcriptions.with_raw_response.create( @@ -65,17 +72,18 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" transcription = await response.parse() assert_matches_type(object, transcription, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.audio.transcriptions.with_streaming_response.create( file=b"raw file contents", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" transcription = await response.parse() assert_matches_type(object, transcription, path=["response"]) diff --git a/tests/api_resources/batches/__init__.py b/tests/api_resources/batches/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/batches/__init__.py +++ b/tests/api_resources/batches/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/batches/test_cancel.py b/tests/api_resources/batches/test_cancel.py index c81703280..740f777db 100644 --- a/tests/api_resources/batches/test_cancel.py +++ b/tests/api_resources/batches/test_cancel.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestCancel: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_cancel(self, client: Hanzo) -> None: cancel = client.batches.cancel.cancel( @@ -23,6 +24,7 @@ def test_method_cancel(self, client: Hanzo) -> None: ) assert_matches_type(object, cancel, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_cancel_with_all_params(self, client: Hanzo) -> None: cancel = client.batches.cancel.cancel( @@ -31,6 +33,7 @@ def test_method_cancel_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, cancel, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_cancel(self, client: Hanzo) -> None: response = client.batches.cancel.with_raw_response.cancel( @@ -38,37 +41,39 @@ def test_raw_response_cancel(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cancel = response.parse() assert_matches_type(object, cancel, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_cancel(self, client: Hanzo) -> None: with client.batches.cancel.with_streaming_response.cancel( batch_id="batch_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cancel = response.parse() assert_matches_type(object, cancel, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_cancel(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `batch_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): client.batches.cancel.with_raw_response.cancel( batch_id="", ) class TestAsyncCancel: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_cancel(self, async_client: AsyncHanzo) -> None: cancel = await async_client.batches.cancel.cancel( @@ -76,6 +81,7 @@ async def test_method_cancel(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, cancel, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_cancel_with_all_params(self, async_client: AsyncHanzo) -> None: cancel = await async_client.batches.cancel.cancel( @@ -84,6 +90,7 @@ async def test_method_cancel_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, cancel, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_cancel(self, async_client: AsyncHanzo) -> None: response = await async_client.batches.cancel.with_raw_response.cancel( @@ -91,29 +98,28 @@ async def test_raw_response_cancel(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cancel = await response.parse() assert_matches_type(object, cancel, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_cancel(self, async_client: AsyncHanzo) -> None: async with async_client.batches.cancel.with_streaming_response.cancel( batch_id="batch_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cancel = await response.parse() assert_matches_type(object, cancel, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_cancel(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `batch_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): await async_client.batches.cancel.with_raw_response.cancel( batch_id="", ) diff --git a/tests/api_resources/cache/__init__.py b/tests/api_resources/cache/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/cache/__init__.py +++ b/tests/api_resources/cache/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/cache/test_redis.py b/tests/api_resources/cache/test_redis.py index eb441aa71..0c9a7a293 100644 --- a/tests/api_resources/cache/test_redis.py +++ b/tests/api_resources/cache/test_redis.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,25 +16,28 @@ class TestRedis: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_info(self, client: Hanzo) -> None: redi = client.cache.redis.retrieve_info() assert_matches_type(object, redi, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve_info(self, client: Hanzo) -> None: response = client.cache.redis.with_raw_response.retrieve_info() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" redi = response.parse() assert_matches_type(object, redi, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve_info(self, client: Hanzo) -> None: with client.cache.redis.with_streaming_response.retrieve_info() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" redi = response.parse() assert_matches_type(object, redi, path=["response"]) @@ -43,27 +46,32 @@ def test_streaming_response_retrieve_info(self, client: Hanzo) -> None: class TestAsyncRedis: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_info(self, async_client: AsyncHanzo) -> None: redi = await async_client.cache.redis.retrieve_info() assert_matches_type(object, redi, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve_info(self, async_client: AsyncHanzo) -> None: response = await async_client.cache.redis.with_raw_response.retrieve_info() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" redi = await response.parse() assert_matches_type(object, redi, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve_info(self, async_client: AsyncHanzo) -> None: async with async_client.cache.redis.with_streaming_response.retrieve_info() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" redi = await response.parse() assert_matches_type(object, redi, path=["response"]) diff --git a/tests/api_resources/chat/__init__.py b/tests/api_resources/chat/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/chat/__init__.py +++ b/tests/api_resources/chat/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index f087689ea..c52205e0f 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,11 +16,13 @@ class TestCompletions: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: completion = client.chat.completions.create() assert_matches_type(object, completion, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: Hanzo) -> None: completion = client.chat.completions.create( @@ -28,20 +30,22 @@ def test_method_create_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, completion, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.chat.completions.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = response.parse() assert_matches_type(object, completion, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.chat.completions.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = response.parse() assert_matches_type(object, completion, path=["response"]) @@ -50,13 +54,17 @@ def test_streaming_response_create(self, client: Hanzo) -> None: class TestAsyncCompletions: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: completion = await async_client.chat.completions.create() assert_matches_type(object, completion, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> None: completion = await async_client.chat.completions.create( @@ -64,20 +72,22 @@ async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, completion, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.chat.completions.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = await response.parse() assert_matches_type(object, completion, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.chat.completions.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = await response.parse() assert_matches_type(object, completion, path=["response"]) diff --git a/tests/api_resources/config/__init__.py b/tests/api_resources/config/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/config/__init__.py +++ b/tests/api_resources/config/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/config/test_pass_through_endpoint.py b/tests/api_resources/config/test_pass_through_endpoint.py index e532b79b9..e8c422a30 100644 --- a/tests/api_resources/config/test_pass_through_endpoint.py +++ b/tests/api_resources/config/test_pass_through_endpoint.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -19,6 +19,7 @@ class TestPassThroughEndpoint: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: pass_through_endpoint = client.config.pass_through_endpoint.create( @@ -28,6 +29,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.config.pass_through_endpoint.with_raw_response.create( @@ -37,10 +39,11 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = response.parse() assert_matches_type(object, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.config.pass_through_endpoint.with_streaming_response.create( @@ -49,13 +52,14 @@ def test_streaming_response_create(self, client: Hanzo) -> None: target="target", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = response.parse() assert_matches_type(object, pass_through_endpoint, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: pass_through_endpoint = client.config.pass_through_endpoint.update( @@ -63,6 +67,7 @@ def test_method_update(self, client: Hanzo) -> None: ) assert_matches_type(object, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.config.pass_through_endpoint.with_raw_response.update( @@ -70,38 +75,39 @@ def test_raw_response_update(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = response.parse() assert_matches_type(object, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.config.pass_through_endpoint.with_streaming_response.update( "endpoint_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = response.parse() assert_matches_type(object, pass_through_endpoint, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint_id` but received ''"): client.config.pass_through_endpoint.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: pass_through_endpoint = client.config.pass_through_endpoint.list() assert_matches_type(PassThroughEndpointResponse, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: Hanzo) -> None: pass_through_endpoint = client.config.pass_through_endpoint.list( @@ -109,26 +115,29 @@ def test_method_list_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(PassThroughEndpointResponse, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.config.pass_through_endpoint.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = response.parse() assert_matches_type(PassThroughEndpointResponse, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.config.pass_through_endpoint.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = response.parse() assert_matches_type(PassThroughEndpointResponse, pass_through_endpoint, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: pass_through_endpoint = client.config.pass_through_endpoint.delete( @@ -136,6 +145,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(PassThroughEndpointResponse, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.config.pass_through_endpoint.with_raw_response.delete( @@ -143,17 +153,18 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = response.parse() assert_matches_type(PassThroughEndpointResponse, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.config.pass_through_endpoint.with_streaming_response.delete( endpoint_id="endpoint_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = response.parse() assert_matches_type(PassThroughEndpointResponse, pass_through_endpoint, path=["response"]) @@ -162,8 +173,11 @@ def test_streaming_response_delete(self, client: Hanzo) -> None: class TestAsyncPassThroughEndpoint: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: pass_through_endpoint = await async_client.config.pass_through_endpoint.create( @@ -173,6 +187,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.config.pass_through_endpoint.with_raw_response.create( @@ -182,10 +197,11 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = await response.parse() assert_matches_type(object, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.config.pass_through_endpoint.with_streaming_response.create( @@ -194,13 +210,14 @@ async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None target="target", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = await response.parse() assert_matches_type(object, pass_through_endpoint, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: pass_through_endpoint = await async_client.config.pass_through_endpoint.update( @@ -208,6 +225,7 @@ async def test_method_update(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.config.pass_through_endpoint.with_raw_response.update( @@ -215,38 +233,39 @@ async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = await response.parse() assert_matches_type(object, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.config.pass_through_endpoint.with_streaming_response.update( "endpoint_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = await response.parse() assert_matches_type(object, pass_through_endpoint, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint_id` but received ''"): await async_client.config.pass_through_endpoint.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: pass_through_endpoint = await async_client.config.pass_through_endpoint.list() assert_matches_type(PassThroughEndpointResponse, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> None: pass_through_endpoint = await async_client.config.pass_through_endpoint.list( @@ -254,26 +273,29 @@ async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> No ) assert_matches_type(PassThroughEndpointResponse, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.config.pass_through_endpoint.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = await response.parse() assert_matches_type(PassThroughEndpointResponse, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.config.pass_through_endpoint.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = await response.parse() assert_matches_type(PassThroughEndpointResponse, pass_through_endpoint, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: pass_through_endpoint = await async_client.config.pass_through_endpoint.delete( @@ -281,6 +303,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(PassThroughEndpointResponse, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.config.pass_through_endpoint.with_raw_response.delete( @@ -288,17 +311,18 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = await response.parse() assert_matches_type(PassThroughEndpointResponse, pass_through_endpoint, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.config.pass_through_endpoint.with_streaming_response.delete( endpoint_id="endpoint_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" pass_through_endpoint = await response.parse() assert_matches_type(PassThroughEndpointResponse, pass_through_endpoint, path=["response"]) diff --git a/tests/api_resources/engines/__init__.py b/tests/api_resources/engines/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/engines/__init__.py +++ b/tests/api_resources/engines/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/engines/test_chat.py b/tests/api_resources/engines/test_chat.py index 0e73525de..28100c129 100644 --- a/tests/api_resources/engines/test_chat.py +++ b/tests/api_resources/engines/test_chat.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestChat: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_complete(self, client: Hanzo) -> None: chat = client.engines.chat.complete( @@ -23,6 +24,7 @@ def test_method_complete(self, client: Hanzo) -> None: ) assert_matches_type(object, chat, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_complete(self, client: Hanzo) -> None: response = client.engines.chat.with_raw_response.complete( @@ -30,23 +32,25 @@ def test_raw_response_complete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" chat = response.parse() assert_matches_type(object, chat, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_complete(self, client: Hanzo) -> None: with client.engines.chat.with_streaming_response.complete( "model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" chat = response.parse() assert_matches_type(object, chat, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_complete(self, client: Hanzo) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): @@ -56,8 +60,11 @@ def test_path_params_complete(self, client: Hanzo) -> None: class TestAsyncChat: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_complete(self, async_client: AsyncHanzo) -> None: chat = await async_client.engines.chat.complete( @@ -65,6 +72,7 @@ async def test_method_complete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, chat, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_complete(self, async_client: AsyncHanzo) -> None: response = await async_client.engines.chat.with_raw_response.complete( @@ -72,23 +80,25 @@ async def test_raw_response_complete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" chat = await response.parse() assert_matches_type(object, chat, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_complete(self, async_client: AsyncHanzo) -> None: async with async_client.engines.chat.with_streaming_response.complete( "model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" chat = await response.parse() assert_matches_type(object, chat, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_complete(self, async_client: AsyncHanzo) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): diff --git a/tests/api_resources/files/__init__.py b/tests/api_resources/files/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/files/__init__.py +++ b/tests/api_resources/files/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/files/test_content.py b/tests/api_resources/files/test_content.py index c3babbbfa..8f414b527 100644 --- a/tests/api_resources/files/test_content.py +++ b/tests/api_resources/files/test_content.py @@ -1,70 +1,68 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os -from typing import TYPE_CHECKING, Any, cast +from typing import Any, cast import pytest from hanzoai import Hanzo, AsyncHanzo from tests.utils import assert_matches_type -if TYPE_CHECKING: - from _pytest.fixtures import FixtureRequest - base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestContent: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_method_retrieve(self, client: Hanzo, request: FixtureRequest) -> None: + def test_method_retrieve(self, client: Hanzo) -> None: content = client.files.content.retrieve( file_id="file_id", provider="provider", ) assert_matches_type(object, content, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_raw_response_retrieve(self, client: Hanzo, request: FixtureRequest) -> None: + def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.files.content.with_raw_response.retrieve( file_id="file_id", provider="provider", ) + assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" content = response.parse() assert_matches_type(object, content, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_streaming_response_retrieve(self, client: Hanzo, request: FixtureRequest) -> None: + def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.files.content.with_streaming_response.retrieve( file_id="file_id", provider="provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + content = response.parse() assert_matches_type(object, content, path=["response"]) + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): client.files.content.with_raw_response.retrieve( file_id="file_id", provider="", ) - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `file_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): client.files.content.with_raw_response.retrieve( file_id="", provider="provider", @@ -72,54 +70,57 @@ def test_path_params_retrieve(self, client: Hanzo) -> None: class TestAsyncContent: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_method_retrieve(self, async_client: AsyncHanzo, request: FixtureRequest) -> None: + async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: content = await async_client.files.content.retrieve( file_id="file_id", provider="provider", ) assert_matches_type(object, content, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncHanzo, request: FixtureRequest) -> None: + async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.files.content.with_raw_response.retrieve( file_id="file_id", provider="provider", ) + assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" content = await response.parse() assert_matches_type(object, content, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncHanzo, request: FixtureRequest) -> None: + async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.files.content.with_streaming_response.retrieve( file_id="file_id", provider="provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + content = await response.parse() - assert content == "file content" or isinstance(content, (str, bytes, object)) + assert_matches_type(object, content, path=["response"]) + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): await async_client.files.content.with_raw_response.retrieve( file_id="file_id", provider="", ) - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `file_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): await async_client.files.content.with_raw_response.retrieve( file_id="", provider="provider", diff --git a/tests/api_resources/fine_tuning/__init__.py b/tests/api_resources/fine_tuning/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/fine_tuning/__init__.py +++ b/tests/api_resources/fine_tuning/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/fine_tuning/jobs/__init__.py b/tests/api_resources/fine_tuning/jobs/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/fine_tuning/jobs/__init__.py +++ b/tests/api_resources/fine_tuning/jobs/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/fine_tuning/jobs/test_cancel.py b/tests/api_resources/fine_tuning/jobs/test_cancel.py index fc8a84b3a..5fa22e8f8 100644 --- a/tests/api_resources/fine_tuning/jobs/test_cancel.py +++ b/tests/api_resources/fine_tuning/jobs/test_cancel.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestCancel: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: cancel = client.fine_tuning.jobs.cancel.create( @@ -23,6 +24,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, cancel, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.fine_tuning.jobs.cancel.with_raw_response.create( @@ -30,37 +32,39 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cancel = response.parse() assert_matches_type(object, cancel, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.fine_tuning.jobs.cancel.with_streaming_response.create( "fine_tuning_job_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cancel = response.parse() assert_matches_type(object, cancel, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): client.fine_tuning.jobs.cancel.with_raw_response.create( "", ) class TestAsyncCancel: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: cancel = await async_client.fine_tuning.jobs.cancel.create( @@ -68,6 +72,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, cancel, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.fine_tuning.jobs.cancel.with_raw_response.create( @@ -75,29 +80,28 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cancel = await response.parse() assert_matches_type(object, cancel, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.fine_tuning.jobs.cancel.with_streaming_response.create( "fine_tuning_job_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cancel = await response.parse() assert_matches_type(object, cancel, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): await async_client.fine_tuning.jobs.cancel.with_raw_response.create( "", ) diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index 9429c8428..c226ef261 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestJobs: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: job = client.fine_tuning.jobs.create( @@ -25,6 +26,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: Hanzo) -> None: job = client.fine_tuning.jobs.create( @@ -43,6 +45,7 @@ def test_method_create_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.fine_tuning.jobs.with_raw_response.create( @@ -52,10 +55,11 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.fine_tuning.jobs.with_streaming_response.create( @@ -64,13 +68,14 @@ def test_streaming_response_create(self, client: Hanzo) -> None: training_file="training_file", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(object, job, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: job = client.fine_tuning.jobs.retrieve( @@ -79,6 +84,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.fine_tuning.jobs.with_raw_response.retrieve( @@ -87,10 +93,11 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.fine_tuning.jobs.with_streaming_response.retrieve( @@ -98,24 +105,23 @@ def test_streaming_response_retrieve(self, client: Hanzo) -> None: custom_llm_provider="openai", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(object, job, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): client.fine_tuning.jobs.with_raw_response.retrieve( fine_tuning_job_id="", custom_llm_provider="openai", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: job = client.fine_tuning.jobs.list( @@ -123,6 +129,7 @@ def test_method_list(self, client: Hanzo) -> None: ) assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: Hanzo) -> None: job = client.fine_tuning.jobs.list( @@ -132,6 +139,7 @@ def test_method_list_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.fine_tuning.jobs.with_raw_response.list( @@ -139,17 +147,18 @@ def test_raw_response_list(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.fine_tuning.jobs.with_streaming_response.list( custom_llm_provider="openai", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(object, job, path=["response"]) @@ -158,8 +167,11 @@ def test_streaming_response_list(self, client: Hanzo) -> None: class TestAsyncJobs: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: job = await async_client.fine_tuning.jobs.create( @@ -169,6 +181,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> None: job = await async_client.fine_tuning.jobs.create( @@ -187,6 +200,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.fine_tuning.jobs.with_raw_response.create( @@ -196,10 +210,11 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = await response.parse() assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.fine_tuning.jobs.with_streaming_response.create( @@ -208,13 +223,14 @@ async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None training_file="training_file", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = await response.parse() assert_matches_type(object, job, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: job = await async_client.fine_tuning.jobs.retrieve( @@ -223,6 +239,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.fine_tuning.jobs.with_raw_response.retrieve( @@ -231,10 +248,11 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = await response.parse() assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.fine_tuning.jobs.with_streaming_response.retrieve( @@ -242,24 +260,23 @@ async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> No custom_llm_provider="openai", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = await response.parse() assert_matches_type(object, job, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): await async_client.fine_tuning.jobs.with_raw_response.retrieve( fine_tuning_job_id="", custom_llm_provider="openai", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: job = await async_client.fine_tuning.jobs.list( @@ -267,6 +284,7 @@ async def test_method_list(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> None: job = await async_client.fine_tuning.jobs.list( @@ -276,6 +294,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> No ) assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.fine_tuning.jobs.with_raw_response.list( @@ -283,17 +302,18 @@ async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = await response.parse() assert_matches_type(object, job, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.fine_tuning.jobs.with_streaming_response.list( custom_llm_provider="openai", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = await response.parse() assert_matches_type(object, job, path=["response"]) diff --git a/tests/api_resources/global_/__init__.py b/tests/api_resources/global_/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/global_/__init__.py +++ b/tests/api_resources/global_/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/global_/test_spend.py b/tests/api_resources/global_/test_spend.py index 9d2dfd2f2..cde16d4e6 100644 --- a/tests/api_resources/global_/test_spend.py +++ b/tests/api_resources/global_/test_spend.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -20,11 +20,13 @@ class TestSpend: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_tags(self, client: Hanzo) -> None: spend = client.global_.spend.list_tags() assert_matches_type(SpendListTagsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_tags_with_all_params(self, client: Hanzo) -> None: spend = client.global_.spend.list_tags( @@ -34,56 +36,63 @@ def test_method_list_tags_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(SpendListTagsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list_tags(self, client: Hanzo) -> None: response = client.global_.spend.with_raw_response.list_tags() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = response.parse() assert_matches_type(SpendListTagsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list_tags(self, client: Hanzo) -> None: with client.global_.spend.with_streaming_response.list_tags() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = response.parse() assert_matches_type(SpendListTagsResponse, spend, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_reset(self, client: Hanzo) -> None: spend = client.global_.spend.reset() assert_matches_type(object, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_reset(self, client: Hanzo) -> None: response = client.global_.spend.with_raw_response.reset() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = response.parse() assert_matches_type(object, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_reset(self, client: Hanzo) -> None: with client.global_.spend.with_streaming_response.reset() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = response.parse() assert_matches_type(object, spend, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_report(self, client: Hanzo) -> None: spend = client.global_.spend.retrieve_report() assert_matches_type(SpendRetrieveReportResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_report_with_all_params(self, client: Hanzo) -> None: spend = client.global_.spend.retrieve_report( @@ -97,20 +106,22 @@ def test_method_retrieve_report_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(SpendRetrieveReportResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve_report(self, client: Hanzo) -> None: response = client.global_.spend.with_raw_response.retrieve_report() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = response.parse() assert_matches_type(SpendRetrieveReportResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve_report(self, client: Hanzo) -> None: with client.global_.spend.with_streaming_response.retrieve_report() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = response.parse() assert_matches_type(SpendRetrieveReportResponse, spend, path=["response"]) @@ -119,13 +130,17 @@ def test_streaming_response_retrieve_report(self, client: Hanzo) -> None: class TestAsyncSpend: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_tags(self, async_client: AsyncHanzo) -> None: spend = await async_client.global_.spend.list_tags() assert_matches_type(SpendListTagsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_tags_with_all_params(self, async_client: AsyncHanzo) -> None: spend = await async_client.global_.spend.list_tags( @@ -135,56 +150,63 @@ async def test_method_list_tags_with_all_params(self, async_client: AsyncHanzo) ) assert_matches_type(SpendListTagsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list_tags(self, async_client: AsyncHanzo) -> None: response = await async_client.global_.spend.with_raw_response.list_tags() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = await response.parse() assert_matches_type(SpendListTagsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list_tags(self, async_client: AsyncHanzo) -> None: async with async_client.global_.spend.with_streaming_response.list_tags() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = await response.parse() assert_matches_type(SpendListTagsResponse, spend, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_reset(self, async_client: AsyncHanzo) -> None: spend = await async_client.global_.spend.reset() assert_matches_type(object, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_reset(self, async_client: AsyncHanzo) -> None: response = await async_client.global_.spend.with_raw_response.reset() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = await response.parse() assert_matches_type(object, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_reset(self, async_client: AsyncHanzo) -> None: async with async_client.global_.spend.with_streaming_response.reset() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = await response.parse() assert_matches_type(object, spend, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_report(self, async_client: AsyncHanzo) -> None: spend = await async_client.global_.spend.retrieve_report() assert_matches_type(SpendRetrieveReportResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_report_with_all_params(self, async_client: AsyncHanzo) -> None: spend = await async_client.global_.spend.retrieve_report( @@ -198,20 +220,22 @@ async def test_method_retrieve_report_with_all_params(self, async_client: AsyncH ) assert_matches_type(SpendRetrieveReportResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve_report(self, async_client: AsyncHanzo) -> None: response = await async_client.global_.spend.with_raw_response.retrieve_report() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = await response.parse() assert_matches_type(SpendRetrieveReportResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve_report(self, async_client: AsyncHanzo) -> None: async with async_client.global_.spend.with_streaming_response.retrieve_report() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = await response.parse() assert_matches_type(SpendRetrieveReportResponse, spend, path=["response"]) diff --git a/tests/api_resources/images/__init__.py b/tests/api_resources/images/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/images/__init__.py +++ b/tests/api_resources/images/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/images/test_generations.py b/tests/api_resources/images/test_generations.py index 6d2591ffb..bc1620b1d 100644 --- a/tests/api_resources/images/test_generations.py +++ b/tests/api_resources/images/test_generations.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,25 +16,28 @@ class TestGenerations: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: generation = client.images.generations.create() assert_matches_type(object, generation, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.images.generations.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" generation = response.parse() assert_matches_type(object, generation, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.images.generations.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" generation = response.parse() assert_matches_type(object, generation, path=["response"]) @@ -43,27 +46,32 @@ def test_streaming_response_create(self, client: Hanzo) -> None: class TestAsyncGenerations: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: generation = await async_client.images.generations.create() assert_matches_type(object, generation, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.images.generations.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" generation = await response.parse() assert_matches_type(object, generation, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.images.generations.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" generation = await response.parse() assert_matches_type(object, generation, path=["response"]) diff --git a/tests/api_resources/key/__init__.py b/tests/api_resources/key/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/key/__init__.py +++ b/tests/api_resources/key/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/model/__init__.py b/tests/api_resources/model/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/model/__init__.py +++ b/tests/api_resources/model/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/model/test_info.py b/tests/api_resources/model/test_info.py index f4d8d1f50..98ddccafd 100644 --- a/tests/api_resources/model/test_info.py +++ b/tests/api_resources/model/test_info.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,32 +16,36 @@ class TestInfo: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: info = client.model.info.list() assert_matches_type(object, info, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: Hanzo) -> None: info = client.model.info.list( - hanzo_model_id="hanzo_model_id", + llm_model_id="llm_model_id", ) assert_matches_type(object, info, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.model.info.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" info = response.parse() assert_matches_type(object, info, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.model.info.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" info = response.parse() assert_matches_type(object, info, path=["response"]) @@ -50,34 +54,40 @@ def test_streaming_response_list(self, client: Hanzo) -> None: class TestAsyncInfo: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: info = await async_client.model.info.list() assert_matches_type(object, info, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> None: info = await async_client.model.info.list( - hanzo_model_id="hanzo_model_id", + llm_model_id="llm_model_id", ) assert_matches_type(object, info, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.model.info.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" info = await response.parse() assert_matches_type(object, info, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.model.info.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" info = await response.parse() assert_matches_type(object, info, path=["response"]) diff --git a/tests/api_resources/model/test_update.py b/tests/api_resources/model/test_update.py index 98f644db6..a228260e7 100644 --- a/tests/api_resources/model/test_update.py +++ b/tests/api_resources/model/test_update.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -17,15 +17,17 @@ class TestUpdate: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_full(self, client: Hanzo) -> None: update = client.model.update.full() assert_matches_type(object, update, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_full_with_all_params(self, client: Hanzo) -> None: update = client.model.update.full( - hanzo_params={ + llm_params={ "api_base": "api_base", "api_key": "api_key", "api_version": "api_version", @@ -37,7 +39,7 @@ def test_method_full_with_all_params(self, client: Hanzo) -> None: "custom_llm_provider": "custom_llm_provider", "input_cost_per_second": 0, "input_cost_per_token": 0, - "hanzo_trace_id": "hanzo_trace_id", + "llm_trace_id": "llm_trace_id", "max_budget": 0, "max_file_size_mb": 0, "max_retries": 0, @@ -74,26 +76,29 @@ def test_method_full_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, update, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_full(self, client: Hanzo) -> None: response = client.model.update.with_raw_response.full() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" update = response.parse() assert_matches_type(object, update, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_full(self, client: Hanzo) -> None: with client.model.update.with_streaming_response.full() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" update = response.parse() assert_matches_type(object, update, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_partial(self, client: Hanzo) -> None: update = client.model.update.partial( @@ -101,11 +106,12 @@ def test_method_partial(self, client: Hanzo) -> None: ) assert_matches_type(object, update, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_partial_with_all_params(self, client: Hanzo) -> None: update = client.model.update.partial( model_id="model_id", - hanzo_params={ + llm_params={ "api_base": "api_base", "api_key": "api_key", "api_version": "api_version", @@ -117,7 +123,7 @@ def test_method_partial_with_all_params(self, client: Hanzo) -> None: "custom_llm_provider": "custom_llm_provider", "input_cost_per_second": 0, "input_cost_per_token": 0, - "hanzo_trace_id": "hanzo_trace_id", + "llm_trace_id": "llm_trace_id", "max_budget": 0, "max_file_size_mb": 0, "max_retries": 0, @@ -154,6 +160,7 @@ def test_method_partial_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, update, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_partial(self, client: Hanzo) -> None: response = client.model.update.with_raw_response.partial( @@ -161,46 +168,49 @@ def test_raw_response_partial(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" update = response.parse() assert_matches_type(object, update, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_partial(self, client: Hanzo) -> None: with client.model.update.with_streaming_response.partial( model_id="model_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" update = response.parse() assert_matches_type(object, update, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_partial(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `model_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `model_id` but received ''"): client.model.update.with_raw_response.partial( model_id="", ) class TestAsyncUpdate: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_full(self, async_client: AsyncHanzo) -> None: update = await async_client.model.update.full() assert_matches_type(object, update, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_full_with_all_params(self, async_client: AsyncHanzo) -> None: update = await async_client.model.update.full( - hanzo_params={ + llm_params={ "api_base": "api_base", "api_key": "api_key", "api_version": "api_version", @@ -212,7 +222,7 @@ async def test_method_full_with_all_params(self, async_client: AsyncHanzo) -> No "custom_llm_provider": "custom_llm_provider", "input_cost_per_second": 0, "input_cost_per_token": 0, - "hanzo_trace_id": "hanzo_trace_id", + "llm_trace_id": "llm_trace_id", "max_budget": 0, "max_file_size_mb": 0, "max_retries": 0, @@ -249,26 +259,29 @@ async def test_method_full_with_all_params(self, async_client: AsyncHanzo) -> No ) assert_matches_type(object, update, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_full(self, async_client: AsyncHanzo) -> None: response = await async_client.model.update.with_raw_response.full() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" update = await response.parse() assert_matches_type(object, update, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_full(self, async_client: AsyncHanzo) -> None: async with async_client.model.update.with_streaming_response.full() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" update = await response.parse() assert_matches_type(object, update, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_partial(self, async_client: AsyncHanzo) -> None: update = await async_client.model.update.partial( @@ -276,11 +289,12 @@ async def test_method_partial(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, update, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_partial_with_all_params(self, async_client: AsyncHanzo) -> None: update = await async_client.model.update.partial( model_id="model_id", - hanzo_params={ + llm_params={ "api_base": "api_base", "api_key": "api_key", "api_version": "api_version", @@ -292,7 +306,7 @@ async def test_method_partial_with_all_params(self, async_client: AsyncHanzo) -> "custom_llm_provider": "custom_llm_provider", "input_cost_per_second": 0, "input_cost_per_token": 0, - "hanzo_trace_id": "hanzo_trace_id", + "llm_trace_id": "llm_trace_id", "max_budget": 0, "max_file_size_mb": 0, "max_retries": 0, @@ -329,6 +343,7 @@ async def test_method_partial_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, update, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_partial(self, async_client: AsyncHanzo) -> None: response = await async_client.model.update.with_raw_response.partial( @@ -336,29 +351,28 @@ async def test_raw_response_partial(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" update = await response.parse() assert_matches_type(object, update, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_partial(self, async_client: AsyncHanzo) -> None: async with async_client.model.update.with_streaming_response.partial( model_id="model_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" update = await response.parse() assert_matches_type(object, update, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_partial(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `model_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `model_id` but received ''"): await async_client.model.update.with_raw_response.partial( model_id="", ) diff --git a/tests/api_resources/openai/__init__.py b/tests/api_resources/openai/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/openai/__init__.py +++ b/tests/api_resources/openai/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/openai/deployments/__init__.py b/tests/api_resources/openai/deployments/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/openai/deployments/__init__.py +++ b/tests/api_resources/openai/deployments/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/openai/deployments/test_chat.py b/tests/api_resources/openai/deployments/test_chat.py index 958a4ab03..0aed99dcc 100644 --- a/tests/api_resources/openai/deployments/test_chat.py +++ b/tests/api_resources/openai/deployments/test_chat.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestChat: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_complete(self, client: Hanzo) -> None: chat = client.openai.deployments.chat.complete( @@ -23,6 +24,7 @@ def test_method_complete(self, client: Hanzo) -> None: ) assert_matches_type(object, chat, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_complete(self, client: Hanzo) -> None: response = client.openai.deployments.chat.with_raw_response.complete( @@ -30,23 +32,25 @@ def test_raw_response_complete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" chat = response.parse() assert_matches_type(object, chat, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_complete(self, client: Hanzo) -> None: with client.openai.deployments.chat.with_streaming_response.complete( "model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" chat = response.parse() assert_matches_type(object, chat, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_complete(self, client: Hanzo) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): @@ -56,8 +60,11 @@ def test_path_params_complete(self, client: Hanzo) -> None: class TestAsyncChat: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_complete(self, async_client: AsyncHanzo) -> None: chat = await async_client.openai.deployments.chat.complete( @@ -65,6 +72,7 @@ async def test_method_complete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, chat, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_complete(self, async_client: AsyncHanzo) -> None: response = await async_client.openai.deployments.chat.with_raw_response.complete( @@ -72,23 +80,25 @@ async def test_raw_response_complete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" chat = await response.parse() assert_matches_type(object, chat, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_complete(self, async_client: AsyncHanzo) -> None: async with async_client.openai.deployments.chat.with_streaming_response.complete( "model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" chat = await response.parse() assert_matches_type(object, chat, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_complete(self, async_client: AsyncHanzo) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): diff --git a/tests/api_resources/openai/test_deployments.py b/tests/api_resources/openai/test_deployments.py index 1cc873ec0..7fa7e1d7e 100644 --- a/tests/api_resources/openai/test_deployments.py +++ b/tests/api_resources/openai/test_deployments.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestDeployments: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_complete(self, client: Hanzo) -> None: deployment = client.openai.deployments.complete( @@ -23,6 +24,7 @@ def test_method_complete(self, client: Hanzo) -> None: ) assert_matches_type(object, deployment, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_complete(self, client: Hanzo) -> None: response = client.openai.deployments.with_raw_response.complete( @@ -30,23 +32,25 @@ def test_raw_response_complete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(object, deployment, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_complete(self, client: Hanzo) -> None: with client.openai.deployments.with_streaming_response.complete( "model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(object, deployment, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_complete(self, client: Hanzo) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): @@ -54,6 +58,7 @@ def test_path_params_complete(self, client: Hanzo) -> None: "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_embed(self, client: Hanzo) -> None: deployment = client.openai.deployments.embed( @@ -61,6 +66,7 @@ def test_method_embed(self, client: Hanzo) -> None: ) assert_matches_type(object, deployment, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_embed(self, client: Hanzo) -> None: response = client.openai.deployments.with_raw_response.embed( @@ -68,23 +74,25 @@ def test_raw_response_embed(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(object, deployment, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_embed(self, client: Hanzo) -> None: with client.openai.deployments.with_streaming_response.embed( "model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(object, deployment, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_embed(self, client: Hanzo) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): @@ -94,8 +102,11 @@ def test_path_params_embed(self, client: Hanzo) -> None: class TestAsyncDeployments: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_complete(self, async_client: AsyncHanzo) -> None: deployment = await async_client.openai.deployments.complete( @@ -103,6 +114,7 @@ async def test_method_complete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, deployment, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_complete(self, async_client: AsyncHanzo) -> None: response = await async_client.openai.deployments.with_raw_response.complete( @@ -110,23 +122,25 @@ async def test_raw_response_complete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = await response.parse() assert_matches_type(object, deployment, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_complete(self, async_client: AsyncHanzo) -> None: async with async_client.openai.deployments.with_streaming_response.complete( "model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = await response.parse() assert_matches_type(object, deployment, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_complete(self, async_client: AsyncHanzo) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): @@ -134,6 +148,7 @@ async def test_path_params_complete(self, async_client: AsyncHanzo) -> None: "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_embed(self, async_client: AsyncHanzo) -> None: deployment = await async_client.openai.deployments.embed( @@ -141,6 +156,7 @@ async def test_method_embed(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, deployment, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_embed(self, async_client: AsyncHanzo) -> None: response = await async_client.openai.deployments.with_raw_response.embed( @@ -148,23 +164,25 @@ async def test_raw_response_embed(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = await response.parse() assert_matches_type(object, deployment, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_embed(self, async_client: AsyncHanzo) -> None: async with async_client.openai.deployments.with_streaming_response.embed( "model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = await response.parse() assert_matches_type(object, deployment, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_embed(self, async_client: AsyncHanzo) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): diff --git a/tests/api_resources/organization/__init__.py b/tests/api_resources/organization/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/organization/__init__.py +++ b/tests/api_resources/organization/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/organization/test_info.py b/tests/api_resources/organization/test_info.py index 8ca3e86de..4ca3b7106 100644 --- a/tests/api_resources/organization/test_info.py +++ b/tests/api_resources/organization/test_info.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -17,6 +17,7 @@ class TestInfo: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: info = client.organization.info.retrieve( @@ -24,6 +25,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(InfoRetrieveResponse, info, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.organization.info.with_raw_response.retrieve( @@ -31,23 +33,25 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" info = response.parse() assert_matches_type(InfoRetrieveResponse, info, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.organization.info.with_streaming_response.retrieve( organization_id="organization_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" info = response.parse() assert_matches_type(InfoRetrieveResponse, info, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_deprecated(self, client: Hanzo) -> None: info = client.organization.info.deprecated( @@ -55,6 +59,7 @@ def test_method_deprecated(self, client: Hanzo) -> None: ) assert_matches_type(object, info, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_deprecated(self, client: Hanzo) -> None: response = client.organization.info.with_raw_response.deprecated( @@ -62,17 +67,18 @@ def test_raw_response_deprecated(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" info = response.parse() assert_matches_type(object, info, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_deprecated(self, client: Hanzo) -> None: with client.organization.info.with_streaming_response.deprecated( organizations=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" info = response.parse() assert_matches_type(object, info, path=["response"]) @@ -81,8 +87,11 @@ def test_streaming_response_deprecated(self, client: Hanzo) -> None: class TestAsyncInfo: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: info = await async_client.organization.info.retrieve( @@ -90,6 +99,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(InfoRetrieveResponse, info, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.organization.info.with_raw_response.retrieve( @@ -97,23 +107,25 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" info = await response.parse() assert_matches_type(InfoRetrieveResponse, info, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.organization.info.with_streaming_response.retrieve( organization_id="organization_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" info = await response.parse() assert_matches_type(InfoRetrieveResponse, info, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_deprecated(self, async_client: AsyncHanzo) -> None: info = await async_client.organization.info.deprecated( @@ -121,6 +133,7 @@ async def test_method_deprecated(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, info, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_deprecated(self, async_client: AsyncHanzo) -> None: response = await async_client.organization.info.with_raw_response.deprecated( @@ -128,17 +141,18 @@ async def test_raw_response_deprecated(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" info = await response.parse() assert_matches_type(object, info, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_deprecated(self, async_client: AsyncHanzo) -> None: async with async_client.organization.info.with_streaming_response.deprecated( organizations=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" info = await response.parse() assert_matches_type(object, info, path=["response"]) diff --git a/tests/api_resources/responses/__init__.py b/tests/api_resources/responses/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/responses/__init__.py +++ b/tests/api_resources/responses/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/responses/test_input_items.py b/tests/api_resources/responses/test_input_items.py index 50bd0c7a2..c25ff052d 100644 --- a/tests/api_resources/responses/test_input_items.py +++ b/tests/api_resources/responses/test_input_items.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestInputItems: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: input_item = client.responses.input_items.list( @@ -23,6 +24,7 @@ def test_method_list(self, client: Hanzo) -> None: ) assert_matches_type(object, input_item, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.responses.input_items.with_raw_response.list( @@ -30,37 +32,39 @@ def test_raw_response_list(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" input_item = response.parse() assert_matches_type(object, input_item, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.responses.input_items.with_streaming_response.list( "response_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" input_item = response.parse() assert_matches_type(object, input_item, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_list(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `response_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): client.responses.input_items.with_raw_response.list( "", ) class TestAsyncInputItems: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: input_item = await async_client.responses.input_items.list( @@ -68,6 +72,7 @@ async def test_method_list(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, input_item, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.responses.input_items.with_raw_response.list( @@ -75,29 +80,28 @@ async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" input_item = await response.parse() assert_matches_type(object, input_item, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.responses.input_items.with_streaming_response.list( "response_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" input_item = await response.parse() assert_matches_type(object, input_item, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_list(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `response_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): await async_client.responses.input_items.with_raw_response.list( "", ) diff --git a/tests/api_resources/team/__init__.py b/tests/api_resources/team/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/team/__init__.py +++ b/tests/api_resources/team/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/team/test_callback.py b/tests/api_resources/team/test_callback.py index 8f75ac988..845dd6409 100644 --- a/tests/api_resources/team/test_callback.py +++ b/tests/api_resources/team/test_callback.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestCallback: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: callback = client.team.callback.retrieve( @@ -23,6 +24,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(object, callback, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.team.callback.with_raw_response.retrieve( @@ -30,33 +32,33 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" callback = response.parse() assert_matches_type(object, callback, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.team.callback.with_streaming_response.retrieve( "team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" callback = response.parse() assert_matches_type(object, callback, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `team_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `team_id` but received ''"): client.team.callback.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_add(self, client: Hanzo) -> None: callback = client.team.callback.add( @@ -66,6 +68,7 @@ def test_method_add(self, client: Hanzo) -> None: ) assert_matches_type(object, callback, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_add_with_all_params(self, client: Hanzo) -> None: callback = client.team.callback.add( @@ -73,10 +76,11 @@ def test_method_add_with_all_params(self, client: Hanzo) -> None: callback_name="callback_name", callback_vars={"foo": "string"}, callback_type="success", - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(object, callback, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_add(self, client: Hanzo) -> None: response = client.team.callback.with_raw_response.add( @@ -86,10 +90,11 @@ def test_raw_response_add(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" callback = response.parse() assert_matches_type(object, callback, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_add(self, client: Hanzo) -> None: with client.team.callback.with_streaming_response.add( @@ -98,19 +103,17 @@ def test_streaming_response_add(self, client: Hanzo) -> None: callback_vars={"foo": "string"}, ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" callback = response.parse() assert_matches_type(object, callback, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_add(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `team_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `team_id` but received ''"): client.team.callback.with_raw_response.add( team_id="", callback_name="callback_name", @@ -119,8 +122,11 @@ def test_path_params_add(self, client: Hanzo) -> None: class TestAsyncCallback: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: callback = await async_client.team.callback.retrieve( @@ -128,6 +134,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, callback, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.team.callback.with_raw_response.retrieve( @@ -135,33 +142,33 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" callback = await response.parse() assert_matches_type(object, callback, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.team.callback.with_streaming_response.retrieve( "team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" callback = await response.parse() assert_matches_type(object, callback, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `team_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `team_id` but received ''"): await async_client.team.callback.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_add(self, async_client: AsyncHanzo) -> None: callback = await async_client.team.callback.add( @@ -171,6 +178,7 @@ async def test_method_add(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, callback, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_add_with_all_params(self, async_client: AsyncHanzo) -> None: callback = await async_client.team.callback.add( @@ -178,10 +186,11 @@ async def test_method_add_with_all_params(self, async_client: AsyncHanzo) -> Non callback_name="callback_name", callback_vars={"foo": "string"}, callback_type="success", - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(object, callback, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_add(self, async_client: AsyncHanzo) -> None: response = await async_client.team.callback.with_raw_response.add( @@ -191,10 +200,11 @@ async def test_raw_response_add(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" callback = await response.parse() assert_matches_type(object, callback, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_add(self, async_client: AsyncHanzo) -> None: async with async_client.team.callback.with_streaming_response.add( @@ -203,19 +213,17 @@ async def test_streaming_response_add(self, async_client: AsyncHanzo) -> None: callback_vars={"foo": "string"}, ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" callback = await response.parse() assert_matches_type(object, callback, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_add(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `team_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `team_id` but received ''"): await async_client.team.callback.with_raw_response.add( team_id="", callback_name="callback_name", diff --git a/tests/api_resources/team/test_model.py b/tests/api_resources/team/test_model.py index ab22f066e..8f037cf29 100644 --- a/tests/api_resources/team/test_model.py +++ b/tests/api_resources/team/test_model.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestModel: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_add(self, client: Hanzo) -> None: model = client.team.model.add( @@ -24,6 +25,7 @@ def test_method_add(self, client: Hanzo) -> None: ) assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_add(self, client: Hanzo) -> None: response = client.team.model.with_raw_response.add( @@ -32,10 +34,11 @@ def test_raw_response_add(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_add(self, client: Hanzo) -> None: with client.team.model.with_streaming_response.add( @@ -43,13 +46,14 @@ def test_streaming_response_add(self, client: Hanzo) -> None: team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(object, model, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_remove(self, client: Hanzo) -> None: model = client.team.model.remove( @@ -58,6 +62,7 @@ def test_method_remove(self, client: Hanzo) -> None: ) assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_remove(self, client: Hanzo) -> None: response = client.team.model.with_raw_response.remove( @@ -66,10 +71,11 @@ def test_raw_response_remove(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_remove(self, client: Hanzo) -> None: with client.team.model.with_streaming_response.remove( @@ -77,7 +83,7 @@ def test_streaming_response_remove(self, client: Hanzo) -> None: team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(object, model, path=["response"]) @@ -86,8 +92,11 @@ def test_streaming_response_remove(self, client: Hanzo) -> None: class TestAsyncModel: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_add(self, async_client: AsyncHanzo) -> None: model = await async_client.team.model.add( @@ -96,6 +105,7 @@ async def test_method_add(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_add(self, async_client: AsyncHanzo) -> None: response = await async_client.team.model.with_raw_response.add( @@ -104,10 +114,11 @@ async def test_raw_response_add(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = await response.parse() assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_add(self, async_client: AsyncHanzo) -> None: async with async_client.team.model.with_streaming_response.add( @@ -115,13 +126,14 @@ async def test_streaming_response_add(self, async_client: AsyncHanzo) -> None: team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = await response.parse() assert_matches_type(object, model, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_remove(self, async_client: AsyncHanzo) -> None: model = await async_client.team.model.remove( @@ -130,6 +142,7 @@ async def test_method_remove(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_remove(self, async_client: AsyncHanzo) -> None: response = await async_client.team.model.with_raw_response.remove( @@ -138,10 +151,11 @@ async def test_raw_response_remove(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = await response.parse() assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_remove(self, async_client: AsyncHanzo) -> None: async with async_client.team.model.with_streaming_response.remove( @@ -149,7 +163,7 @@ async def test_streaming_response_remove(self, async_client: AsyncHanzo) -> None team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = await response.parse() assert_matches_type(object, model, path=["response"]) diff --git a/tests/api_resources/test_active.py b/tests/api_resources/test_active.py index 11b8ebb67..0881e2297 100644 --- a/tests/api_resources/test_active.py +++ b/tests/api_resources/test_active.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,25 +16,28 @@ class TestActive: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_callbacks(self, client: Hanzo) -> None: active = client.active.list_callbacks() assert_matches_type(object, active, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list_callbacks(self, client: Hanzo) -> None: response = client.active.with_raw_response.list_callbacks() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" active = response.parse() assert_matches_type(object, active, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list_callbacks(self, client: Hanzo) -> None: with client.active.with_streaming_response.list_callbacks() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" active = response.parse() assert_matches_type(object, active, path=["response"]) @@ -43,27 +46,32 @@ def test_streaming_response_list_callbacks(self, client: Hanzo) -> None: class TestAsyncActive: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_callbacks(self, async_client: AsyncHanzo) -> None: active = await async_client.active.list_callbacks() assert_matches_type(object, active, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list_callbacks(self, async_client: AsyncHanzo) -> None: response = await async_client.active.with_raw_response.list_callbacks() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" active = await response.parse() assert_matches_type(object, active, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list_callbacks(self, async_client: AsyncHanzo) -> None: async with async_client.active.with_streaming_response.list_callbacks() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" active = await response.parse() assert_matches_type(object, active, path=["response"]) diff --git a/tests/api_resources/test_add.py b/tests/api_resources/test_add.py index b303ec34a..68c428965 100644 --- a/tests/api_resources/test_add.py +++ b/tests/api_resources/test_add.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestAdd: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_add_allowed_ip(self, client: Hanzo) -> None: add = client.add.add_allowed_ip( @@ -23,6 +24,7 @@ def test_method_add_allowed_ip(self, client: Hanzo) -> None: ) assert_matches_type(object, add, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_add_allowed_ip(self, client: Hanzo) -> None: response = client.add.with_raw_response.add_allowed_ip( @@ -30,17 +32,18 @@ def test_raw_response_add_allowed_ip(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" add = response.parse() assert_matches_type(object, add, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_add_allowed_ip(self, client: Hanzo) -> None: with client.add.with_streaming_response.add_allowed_ip( ip="ip", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" add = response.parse() assert_matches_type(object, add, path=["response"]) @@ -49,8 +52,11 @@ def test_streaming_response_add_allowed_ip(self, client: Hanzo) -> None: class TestAsyncAdd: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_add_allowed_ip(self, async_client: AsyncHanzo) -> None: add = await async_client.add.add_allowed_ip( @@ -58,6 +64,7 @@ async def test_method_add_allowed_ip(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, add, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_add_allowed_ip(self, async_client: AsyncHanzo) -> None: response = await async_client.add.with_raw_response.add_allowed_ip( @@ -65,17 +72,18 @@ async def test_raw_response_add_allowed_ip(self, async_client: AsyncHanzo) -> No ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" add = await response.parse() assert_matches_type(object, add, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_add_allowed_ip(self, async_client: AsyncHanzo) -> None: async with async_client.add.with_streaming_response.add_allowed_ip( ip="ip", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" add = await response.parse() assert_matches_type(object, add, path=["response"]) diff --git a/tests/api_resources/test_anthropic.py b/tests/api_resources/test_anthropic.py index fa96e079b..ba1ddd80f 100644 --- a/tests/api_resources/test_anthropic.py +++ b/tests/api_resources/test_anthropic.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestAnthropic: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: anthropic = client.anthropic.create( @@ -23,6 +24,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.anthropic.with_raw_response.create( @@ -30,33 +32,33 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = response.parse() assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.anthropic.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = response.parse() assert_matches_type(object, anthropic, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.anthropic.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: anthropic = client.anthropic.retrieve( @@ -64,6 +66,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.anthropic.with_raw_response.retrieve( @@ -71,33 +74,33 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = response.parse() assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.anthropic.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = response.parse() assert_matches_type(object, anthropic, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.anthropic.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: anthropic = client.anthropic.update( @@ -105,6 +108,7 @@ def test_method_update(self, client: Hanzo) -> None: ) assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.anthropic.with_raw_response.update( @@ -112,33 +116,33 @@ def test_raw_response_update(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = response.parse() assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.anthropic.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = response.parse() assert_matches_type(object, anthropic, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.anthropic.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: anthropic = client.anthropic.delete( @@ -146,6 +150,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.anthropic.with_raw_response.delete( @@ -153,33 +158,33 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = response.parse() assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.anthropic.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = response.parse() assert_matches_type(object, anthropic, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.anthropic.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_modify(self, client: Hanzo) -> None: anthropic = client.anthropic.modify( @@ -187,6 +192,7 @@ def test_method_modify(self, client: Hanzo) -> None: ) assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_modify(self, client: Hanzo) -> None: response = client.anthropic.with_raw_response.modify( @@ -194,37 +200,39 @@ def test_raw_response_modify(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = response.parse() assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_modify(self, client: Hanzo) -> None: with client.anthropic.with_streaming_response.modify( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = response.parse() assert_matches_type(object, anthropic, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_modify(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.anthropic.with_raw_response.modify( "", ) class TestAsyncAnthropic: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: anthropic = await async_client.anthropic.create( @@ -232,6 +240,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.anthropic.with_raw_response.create( @@ -239,33 +248,33 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = await response.parse() assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.anthropic.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = await response.parse() assert_matches_type(object, anthropic, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.anthropic.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: anthropic = await async_client.anthropic.retrieve( @@ -273,6 +282,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.anthropic.with_raw_response.retrieve( @@ -280,33 +290,33 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = await response.parse() assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.anthropic.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = await response.parse() assert_matches_type(object, anthropic, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.anthropic.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: anthropic = await async_client.anthropic.update( @@ -314,6 +324,7 @@ async def test_method_update(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.anthropic.with_raw_response.update( @@ -321,33 +332,33 @@ async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = await response.parse() assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.anthropic.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = await response.parse() assert_matches_type(object, anthropic, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.anthropic.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: anthropic = await async_client.anthropic.delete( @@ -355,6 +366,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.anthropic.with_raw_response.delete( @@ -362,33 +374,33 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = await response.parse() assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.anthropic.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = await response.parse() assert_matches_type(object, anthropic, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.anthropic.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_modify(self, async_client: AsyncHanzo) -> None: anthropic = await async_client.anthropic.modify( @@ -396,6 +408,7 @@ async def test_method_modify(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_modify(self, async_client: AsyncHanzo) -> None: response = await async_client.anthropic.with_raw_response.modify( @@ -403,29 +416,28 @@ async def test_raw_response_modify(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = await response.parse() assert_matches_type(object, anthropic, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_modify(self, async_client: AsyncHanzo) -> None: async with async_client.anthropic.with_streaming_response.modify( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" anthropic = await response.parse() assert_matches_type(object, anthropic, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_modify(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.anthropic.with_raw_response.modify( "", ) diff --git a/tests/api_resources/test_assemblyai.py b/tests/api_resources/test_assemblyai.py index 27fe6b6e8..6a0d50cba 100644 --- a/tests/api_resources/test_assemblyai.py +++ b/tests/api_resources/test_assemblyai.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestAssemblyai: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: assemblyai = client.assemblyai.create( @@ -23,6 +24,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.assemblyai.with_raw_response.create( @@ -30,33 +32,33 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = response.parse() assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.assemblyai.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = response.parse() assert_matches_type(object, assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.assemblyai.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: assemblyai = client.assemblyai.retrieve( @@ -64,6 +66,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.assemblyai.with_raw_response.retrieve( @@ -71,33 +74,33 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = response.parse() assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.assemblyai.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = response.parse() assert_matches_type(object, assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.assemblyai.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: assemblyai = client.assemblyai.update( @@ -105,6 +108,7 @@ def test_method_update(self, client: Hanzo) -> None: ) assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.assemblyai.with_raw_response.update( @@ -112,33 +116,33 @@ def test_raw_response_update(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = response.parse() assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.assemblyai.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = response.parse() assert_matches_type(object, assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.assemblyai.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: assemblyai = client.assemblyai.delete( @@ -146,6 +150,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.assemblyai.with_raw_response.delete( @@ -153,33 +158,33 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = response.parse() assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.assemblyai.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = response.parse() assert_matches_type(object, assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.assemblyai.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_patch(self, client: Hanzo) -> None: assemblyai = client.assemblyai.patch( @@ -187,6 +192,7 @@ def test_method_patch(self, client: Hanzo) -> None: ) assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_patch(self, client: Hanzo) -> None: response = client.assemblyai.with_raw_response.patch( @@ -194,37 +200,39 @@ def test_raw_response_patch(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = response.parse() assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_patch(self, client: Hanzo) -> None: with client.assemblyai.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = response.parse() assert_matches_type(object, assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_patch(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.assemblyai.with_raw_response.patch( "", ) class TestAsyncAssemblyai: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: assemblyai = await async_client.assemblyai.create( @@ -232,6 +240,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.assemblyai.with_raw_response.create( @@ -239,33 +248,33 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = await response.parse() assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.assemblyai.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = await response.parse() assert_matches_type(object, assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.assemblyai.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: assemblyai = await async_client.assemblyai.retrieve( @@ -273,6 +282,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.assemblyai.with_raw_response.retrieve( @@ -280,33 +290,33 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = await response.parse() assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.assemblyai.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = await response.parse() assert_matches_type(object, assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.assemblyai.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: assemblyai = await async_client.assemblyai.update( @@ -314,6 +324,7 @@ async def test_method_update(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.assemblyai.with_raw_response.update( @@ -321,33 +332,33 @@ async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = await response.parse() assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.assemblyai.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = await response.parse() assert_matches_type(object, assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.assemblyai.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: assemblyai = await async_client.assemblyai.delete( @@ -355,6 +366,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.assemblyai.with_raw_response.delete( @@ -362,33 +374,33 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = await response.parse() assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.assemblyai.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = await response.parse() assert_matches_type(object, assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.assemblyai.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_patch(self, async_client: AsyncHanzo) -> None: assemblyai = await async_client.assemblyai.patch( @@ -396,6 +408,7 @@ async def test_method_patch(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: response = await async_client.assemblyai.with_raw_response.patch( @@ -403,29 +416,28 @@ async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = await response.parse() assert_matches_type(object, assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_patch(self, async_client: AsyncHanzo) -> None: async with async_client.assemblyai.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assemblyai = await response.parse() assert_matches_type(object, assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_patch(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.assemblyai.with_raw_response.patch( "", ) diff --git a/tests/api_resources/test_assistants.py b/tests/api_resources/test_assistants.py index af654e428..73be9213e 100644 --- a/tests/api_resources/test_assistants.py +++ b/tests/api_resources/test_assistants.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,56 +16,63 @@ class TestAssistants: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: assistant = client.assistants.create() assert_matches_type(object, assistant, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.assistants.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(object, assistant, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.assistants.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(object, assistant, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: assistant = client.assistants.list() assert_matches_type(object, assistant, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.assistants.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(object, assistant, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.assistants.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(object, assistant, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: assistant = client.assistants.delete( @@ -73,6 +80,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, assistant, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.assistants.with_raw_response.delete( @@ -80,87 +88,95 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(object, assistant, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.assistants.with_streaming_response.delete( "assistant_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(object, assistant, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `assistant_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): client.assistants.with_raw_response.delete( "", ) class TestAsyncAssistants: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: assistant = await async_client.assistants.create() assert_matches_type(object, assistant, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.assistants.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = await response.parse() assert_matches_type(object, assistant, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.assistants.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = await response.parse() assert_matches_type(object, assistant, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: assistant = await async_client.assistants.list() assert_matches_type(object, assistant, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.assistants.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = await response.parse() assert_matches_type(object, assistant, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.assistants.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = await response.parse() assert_matches_type(object, assistant, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: assistant = await async_client.assistants.delete( @@ -168,6 +184,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, assistant, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.assistants.with_raw_response.delete( @@ -175,29 +192,28 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = await response.parse() assert_matches_type(object, assistant, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.assistants.with_streaming_response.delete( "assistant_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = await response.parse() assert_matches_type(object, assistant, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `assistant_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): await async_client.assistants.with_raw_response.delete( "", ) diff --git a/tests/api_resources/test_azure.py b/tests/api_resources/test_azure.py index 12c055bc3..f7b0e1b42 100644 --- a/tests/api_resources/test_azure.py +++ b/tests/api_resources/test_azure.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestAzure: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: azure = client.azure.create( @@ -23,6 +24,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.azure.with_raw_response.create( @@ -30,33 +32,33 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = response.parse() assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.azure.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = response.parse() assert_matches_type(object, azure, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.azure.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: azure = client.azure.update( @@ -64,6 +66,7 @@ def test_method_update(self, client: Hanzo) -> None: ) assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.azure.with_raw_response.update( @@ -71,33 +74,33 @@ def test_raw_response_update(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = response.parse() assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.azure.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = response.parse() assert_matches_type(object, azure, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.azure.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: azure = client.azure.delete( @@ -105,6 +108,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.azure.with_raw_response.delete( @@ -112,33 +116,33 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = response.parse() assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.azure.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = response.parse() assert_matches_type(object, azure, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.azure.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_call(self, client: Hanzo) -> None: azure = client.azure.call( @@ -146,6 +150,7 @@ def test_method_call(self, client: Hanzo) -> None: ) assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_call(self, client: Hanzo) -> None: response = client.azure.with_raw_response.call( @@ -153,33 +158,33 @@ def test_raw_response_call(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = response.parse() assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_call(self, client: Hanzo) -> None: with client.azure.with_streaming_response.call( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = response.parse() assert_matches_type(object, azure, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_call(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.azure.with_raw_response.call( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_patch(self, client: Hanzo) -> None: azure = client.azure.patch( @@ -187,6 +192,7 @@ def test_method_patch(self, client: Hanzo) -> None: ) assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_patch(self, client: Hanzo) -> None: response = client.azure.with_raw_response.patch( @@ -194,37 +200,39 @@ def test_raw_response_patch(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = response.parse() assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_patch(self, client: Hanzo) -> None: with client.azure.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = response.parse() assert_matches_type(object, azure, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_patch(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.azure.with_raw_response.patch( "", ) class TestAsyncAzure: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: azure = await async_client.azure.create( @@ -232,6 +240,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.azure.with_raw_response.create( @@ -239,33 +248,33 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = await response.parse() assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.azure.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = await response.parse() assert_matches_type(object, azure, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.azure.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: azure = await async_client.azure.update( @@ -273,6 +282,7 @@ async def test_method_update(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.azure.with_raw_response.update( @@ -280,33 +290,33 @@ async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = await response.parse() assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.azure.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = await response.parse() assert_matches_type(object, azure, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.azure.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: azure = await async_client.azure.delete( @@ -314,6 +324,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.azure.with_raw_response.delete( @@ -321,33 +332,33 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = await response.parse() assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.azure.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = await response.parse() assert_matches_type(object, azure, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.azure.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_call(self, async_client: AsyncHanzo) -> None: azure = await async_client.azure.call( @@ -355,6 +366,7 @@ async def test_method_call(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_call(self, async_client: AsyncHanzo) -> None: response = await async_client.azure.with_raw_response.call( @@ -362,33 +374,33 @@ async def test_raw_response_call(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = await response.parse() assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_call(self, async_client: AsyncHanzo) -> None: async with async_client.azure.with_streaming_response.call( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = await response.parse() assert_matches_type(object, azure, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_call(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.azure.with_raw_response.call( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_patch(self, async_client: AsyncHanzo) -> None: azure = await async_client.azure.patch( @@ -396,6 +408,7 @@ async def test_method_patch(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: response = await async_client.azure.with_raw_response.patch( @@ -403,29 +416,28 @@ async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = await response.parse() assert_matches_type(object, azure, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_patch(self, async_client: AsyncHanzo) -> None: async with async_client.azure.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" azure = await response.parse() assert_matches_type(object, azure, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_patch(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.azure.with_raw_response.patch( "", ) diff --git a/tests/api_resources/test_batches.py b/tests/api_resources/test_batches.py index 2da39745c..834ae4e4d 100644 --- a/tests/api_resources/test_batches.py +++ b/tests/api_resources/test_batches.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,11 +16,13 @@ class TestBatches: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: batch = client.batches.create() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: Hanzo) -> None: batch = client.batches.create( @@ -28,26 +30,29 @@ def test_method_create_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.batches.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.batches.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(object, batch, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: batch = client.batches.retrieve( @@ -55,6 +60,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_with_all_params(self, client: Hanzo) -> None: batch = client.batches.retrieve( @@ -63,6 +69,7 @@ def test_method_retrieve_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.batches.with_raw_response.retrieve( @@ -70,38 +77,39 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.batches.with_streaming_response.retrieve( batch_id="batch_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(object, batch, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `batch_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): client.batches.with_raw_response.retrieve( batch_id="", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: batch = client.batches.list() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: Hanzo) -> None: batch = client.batches.list( @@ -111,26 +119,29 @@ def test_method_list_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.batches.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.batches.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(object, batch, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_cancel_with_provider(self, client: Hanzo) -> None: batch = client.batches.cancel_with_provider( @@ -139,6 +150,7 @@ def test_method_cancel_with_provider(self, client: Hanzo) -> None: ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_cancel_with_provider(self, client: Hanzo) -> None: response = client.batches.with_raw_response.cancel_with_provider( @@ -147,10 +159,11 @@ def test_raw_response_cancel_with_provider(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_cancel_with_provider(self, client: Hanzo) -> None: with client.batches.with_streaming_response.cancel_with_provider( @@ -158,33 +171,29 @@ def test_streaming_response_cancel_with_provider(self, client: Hanzo) -> None: provider="provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(object, batch, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_cancel_with_provider(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): client.batches.with_raw_response.cancel_with_provider( batch_id="batch_id", provider="", ) - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `batch_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): client.batches.with_raw_response.cancel_with_provider( batch_id="", provider="provider", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_provider(self, client: Hanzo) -> None: batch = client.batches.create_with_provider( @@ -192,6 +201,7 @@ def test_method_create_with_provider(self, client: Hanzo) -> None: ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_with_provider(self, client: Hanzo) -> None: response = client.batches.with_raw_response.create_with_provider( @@ -199,33 +209,33 @@ def test_raw_response_create_with_provider(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_with_provider(self, client: Hanzo) -> None: with client.batches.with_streaming_response.create_with_provider( "provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(object, batch, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create_with_provider(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): client.batches.with_raw_response.create_with_provider( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_provider(self, client: Hanzo) -> None: batch = client.batches.list_with_provider( @@ -233,6 +243,7 @@ def test_method_list_with_provider(self, client: Hanzo) -> None: ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_provider_with_all_params(self, client: Hanzo) -> None: batch = client.batches.list_with_provider( @@ -242,6 +253,7 @@ def test_method_list_with_provider_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list_with_provider(self, client: Hanzo) -> None: response = client.batches.with_raw_response.list_with_provider( @@ -249,33 +261,33 @@ def test_raw_response_list_with_provider(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list_with_provider(self, client: Hanzo) -> None: with client.batches.with_streaming_response.list_with_provider( provider="provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(object, batch, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_list_with_provider(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): client.batches.with_raw_response.list_with_provider( provider="", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_with_provider(self, client: Hanzo) -> None: batch = client.batches.retrieve_with_provider( @@ -284,6 +296,7 @@ def test_method_retrieve_with_provider(self, client: Hanzo) -> None: ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve_with_provider(self, client: Hanzo) -> None: response = client.batches.with_raw_response.retrieve_with_provider( @@ -292,10 +305,11 @@ def test_raw_response_retrieve_with_provider(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve_with_provider(self, client: Hanzo) -> None: with client.batches.with_streaming_response.retrieve_with_provider( @@ -303,28 +317,23 @@ def test_streaming_response_retrieve_with_provider(self, client: Hanzo) -> None: provider="provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(object, batch, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve_with_provider(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): client.batches.with_raw_response.retrieve_with_provider( batch_id="batch_id", provider="", ) - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `batch_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): client.batches.with_raw_response.retrieve_with_provider( batch_id="", provider="provider", @@ -332,13 +341,17 @@ def test_path_params_retrieve_with_provider(self, client: Hanzo) -> None: class TestAsyncBatches: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: batch = await async_client.batches.create() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> None: batch = await async_client.batches.create( @@ -346,26 +359,29 @@ async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.batches.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.batches.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(object, batch, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: batch = await async_client.batches.retrieve( @@ -373,6 +389,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncHanzo) -> None: batch = await async_client.batches.retrieve( @@ -381,6 +398,7 @@ async def test_method_retrieve_with_all_params(self, async_client: AsyncHanzo) - ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.batches.with_raw_response.retrieve( @@ -388,38 +406,39 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.batches.with_streaming_response.retrieve( batch_id="batch_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(object, batch, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `batch_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): await async_client.batches.with_raw_response.retrieve( batch_id="", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: batch = await async_client.batches.list() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> None: batch = await async_client.batches.list( @@ -429,26 +448,29 @@ async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> No ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.batches.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.batches.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(object, batch, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_cancel_with_provider(self, async_client: AsyncHanzo) -> None: batch = await async_client.batches.cancel_with_provider( @@ -457,6 +479,7 @@ async def test_method_cancel_with_provider(self, async_client: AsyncHanzo) -> No ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_cancel_with_provider(self, async_client: AsyncHanzo) -> None: response = await async_client.batches.with_raw_response.cancel_with_provider( @@ -465,10 +488,11 @@ async def test_raw_response_cancel_with_provider(self, async_client: AsyncHanzo) ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_cancel_with_provider(self, async_client: AsyncHanzo) -> None: async with async_client.batches.with_streaming_response.cancel_with_provider( @@ -476,33 +500,29 @@ async def test_streaming_response_cancel_with_provider(self, async_client: Async provider="provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(object, batch, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_cancel_with_provider(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): await async_client.batches.with_raw_response.cancel_with_provider( batch_id="batch_id", provider="", ) - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `batch_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): await async_client.batches.with_raw_response.cancel_with_provider( batch_id="", provider="provider", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_provider(self, async_client: AsyncHanzo) -> None: batch = await async_client.batches.create_with_provider( @@ -510,6 +530,7 @@ async def test_method_create_with_provider(self, async_client: AsyncHanzo) -> No ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_with_provider(self, async_client: AsyncHanzo) -> None: response = await async_client.batches.with_raw_response.create_with_provider( @@ -517,33 +538,33 @@ async def test_raw_response_create_with_provider(self, async_client: AsyncHanzo) ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_with_provider(self, async_client: AsyncHanzo) -> None: async with async_client.batches.with_streaming_response.create_with_provider( "provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(object, batch, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create_with_provider(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): await async_client.batches.with_raw_response.create_with_provider( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_provider(self, async_client: AsyncHanzo) -> None: batch = await async_client.batches.list_with_provider( @@ -551,6 +572,7 @@ async def test_method_list_with_provider(self, async_client: AsyncHanzo) -> None ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_provider_with_all_params(self, async_client: AsyncHanzo) -> None: batch = await async_client.batches.list_with_provider( @@ -560,6 +582,7 @@ async def test_method_list_with_provider_with_all_params(self, async_client: Asy ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list_with_provider(self, async_client: AsyncHanzo) -> None: response = await async_client.batches.with_raw_response.list_with_provider( @@ -567,33 +590,33 @@ async def test_raw_response_list_with_provider(self, async_client: AsyncHanzo) - ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list_with_provider(self, async_client: AsyncHanzo) -> None: async with async_client.batches.with_streaming_response.list_with_provider( provider="provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(object, batch, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_list_with_provider(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): await async_client.batches.with_raw_response.list_with_provider( provider="", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_with_provider(self, async_client: AsyncHanzo) -> None: batch = await async_client.batches.retrieve_with_provider( @@ -602,6 +625,7 @@ async def test_method_retrieve_with_provider(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve_with_provider(self, async_client: AsyncHanzo) -> None: response = await async_client.batches.with_raw_response.retrieve_with_provider( @@ -610,10 +634,11 @@ async def test_raw_response_retrieve_with_provider(self, async_client: AsyncHanz ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(object, batch, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve_with_provider(self, async_client: AsyncHanzo) -> None: async with async_client.batches.with_streaming_response.retrieve_with_provider( @@ -621,28 +646,23 @@ async def test_streaming_response_retrieve_with_provider(self, async_client: Asy provider="provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(object, batch, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve_with_provider(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): await async_client.batches.with_raw_response.retrieve_with_provider( batch_id="batch_id", provider="", ) - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `batch_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): await async_client.batches.with_raw_response.retrieve_with_provider( batch_id="", provider="provider", diff --git a/tests/api_resources/test_bedrock.py b/tests/api_resources/test_bedrock.py index b41c1ca22..800442553 100644 --- a/tests/api_resources/test_bedrock.py +++ b/tests/api_resources/test_bedrock.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestBedrock: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: bedrock = client.bedrock.create( @@ -23,6 +24,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.bedrock.with_raw_response.create( @@ -30,33 +32,33 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = response.parse() assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.bedrock.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = response.parse() assert_matches_type(object, bedrock, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.bedrock.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: bedrock = client.bedrock.retrieve( @@ -64,6 +66,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.bedrock.with_raw_response.retrieve( @@ -71,33 +74,33 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = response.parse() assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.bedrock.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = response.parse() assert_matches_type(object, bedrock, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.bedrock.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: bedrock = client.bedrock.update( @@ -105,6 +108,7 @@ def test_method_update(self, client: Hanzo) -> None: ) assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.bedrock.with_raw_response.update( @@ -112,33 +116,33 @@ def test_raw_response_update(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = response.parse() assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.bedrock.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = response.parse() assert_matches_type(object, bedrock, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.bedrock.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: bedrock = client.bedrock.delete( @@ -146,6 +150,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.bedrock.with_raw_response.delete( @@ -153,33 +158,33 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = response.parse() assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.bedrock.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = response.parse() assert_matches_type(object, bedrock, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.bedrock.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_patch(self, client: Hanzo) -> None: bedrock = client.bedrock.patch( @@ -187,6 +192,7 @@ def test_method_patch(self, client: Hanzo) -> None: ) assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_patch(self, client: Hanzo) -> None: response = client.bedrock.with_raw_response.patch( @@ -194,37 +200,39 @@ def test_raw_response_patch(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = response.parse() assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_patch(self, client: Hanzo) -> None: with client.bedrock.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = response.parse() assert_matches_type(object, bedrock, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_patch(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.bedrock.with_raw_response.patch( "", ) class TestAsyncBedrock: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: bedrock = await async_client.bedrock.create( @@ -232,6 +240,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.bedrock.with_raw_response.create( @@ -239,33 +248,33 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = await response.parse() assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.bedrock.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = await response.parse() assert_matches_type(object, bedrock, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.bedrock.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: bedrock = await async_client.bedrock.retrieve( @@ -273,6 +282,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.bedrock.with_raw_response.retrieve( @@ -280,33 +290,33 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = await response.parse() assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.bedrock.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = await response.parse() assert_matches_type(object, bedrock, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.bedrock.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: bedrock = await async_client.bedrock.update( @@ -314,6 +324,7 @@ async def test_method_update(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.bedrock.with_raw_response.update( @@ -321,33 +332,33 @@ async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = await response.parse() assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.bedrock.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = await response.parse() assert_matches_type(object, bedrock, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.bedrock.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: bedrock = await async_client.bedrock.delete( @@ -355,6 +366,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.bedrock.with_raw_response.delete( @@ -362,33 +374,33 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = await response.parse() assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.bedrock.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = await response.parse() assert_matches_type(object, bedrock, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.bedrock.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_patch(self, async_client: AsyncHanzo) -> None: bedrock = await async_client.bedrock.patch( @@ -396,6 +408,7 @@ async def test_method_patch(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: response = await async_client.bedrock.with_raw_response.patch( @@ -403,29 +416,28 @@ async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = await response.parse() assert_matches_type(object, bedrock, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_patch(self, async_client: AsyncHanzo) -> None: async with async_client.bedrock.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" bedrock = await response.parse() assert_matches_type(object, bedrock, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_patch(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.bedrock.with_raw_response.patch( "", ) diff --git a/tests/api_resources/test_budget.py b/tests/api_resources/test_budget.py index 50ae4b1d9..0bbe9fb0c 100644 --- a/tests/api_resources/test_budget.py +++ b/tests/api_resources/test_budget.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,11 +16,13 @@ class TestBudget: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: budget = client.budget.create() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: Hanzo) -> None: budget = client.budget.create( @@ -42,31 +44,35 @@ def test_method_create_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.budget.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = response.parse() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.budget.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = response.parse() assert_matches_type(object, budget, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: budget = client.budget.update() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: Hanzo) -> None: budget = client.budget.update( @@ -88,51 +94,57 @@ def test_method_update_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.budget.with_raw_response.update() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = response.parse() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.budget.with_streaming_response.update() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = response.parse() assert_matches_type(object, budget, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: budget = client.budget.list() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.budget.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = response.parse() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.budget.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = response.parse() assert_matches_type(object, budget, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: budget = client.budget.delete( @@ -140,6 +152,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.budget.with_raw_response.delete( @@ -147,23 +160,25 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = response.parse() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.budget.with_streaming_response.delete( id="id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = response.parse() assert_matches_type(object, budget, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_info(self, client: Hanzo) -> None: budget = client.budget.info( @@ -171,6 +186,7 @@ def test_method_info(self, client: Hanzo) -> None: ) assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_info(self, client: Hanzo) -> None: response = client.budget.with_raw_response.info( @@ -178,23 +194,25 @@ def test_raw_response_info(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = response.parse() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_info(self, client: Hanzo) -> None: with client.budget.with_streaming_response.info( budgets=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = response.parse() assert_matches_type(object, budget, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_settings(self, client: Hanzo) -> None: budget = client.budget.settings( @@ -202,6 +220,7 @@ def test_method_settings(self, client: Hanzo) -> None: ) assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_settings(self, client: Hanzo) -> None: response = client.budget.with_raw_response.settings( @@ -209,17 +228,18 @@ def test_raw_response_settings(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = response.parse() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_settings(self, client: Hanzo) -> None: with client.budget.with_streaming_response.settings( budget_id="budget_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = response.parse() assert_matches_type(object, budget, path=["response"]) @@ -228,13 +248,17 @@ def test_streaming_response_settings(self, client: Hanzo) -> None: class TestAsyncBudget: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: budget = await async_client.budget.create() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> None: budget = await async_client.budget.create( @@ -256,31 +280,35 @@ async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.budget.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = await response.parse() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.budget.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = await response.parse() assert_matches_type(object, budget, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: budget = await async_client.budget.update() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncHanzo) -> None: budget = await async_client.budget.update( @@ -302,51 +330,57 @@ async def test_method_update_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.budget.with_raw_response.update() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = await response.parse() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.budget.with_streaming_response.update() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = await response.parse() assert_matches_type(object, budget, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: budget = await async_client.budget.list() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.budget.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = await response.parse() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.budget.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = await response.parse() assert_matches_type(object, budget, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: budget = await async_client.budget.delete( @@ -354,6 +388,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.budget.with_raw_response.delete( @@ -361,23 +396,25 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = await response.parse() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.budget.with_streaming_response.delete( id="id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = await response.parse() assert_matches_type(object, budget, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_info(self, async_client: AsyncHanzo) -> None: budget = await async_client.budget.info( @@ -385,6 +422,7 @@ async def test_method_info(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_info(self, async_client: AsyncHanzo) -> None: response = await async_client.budget.with_raw_response.info( @@ -392,23 +430,25 @@ async def test_raw_response_info(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = await response.parse() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_info(self, async_client: AsyncHanzo) -> None: async with async_client.budget.with_streaming_response.info( budgets=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = await response.parse() assert_matches_type(object, budget, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_settings(self, async_client: AsyncHanzo) -> None: budget = await async_client.budget.settings( @@ -416,6 +456,7 @@ async def test_method_settings(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_settings(self, async_client: AsyncHanzo) -> None: response = await async_client.budget.with_raw_response.settings( @@ -423,17 +464,18 @@ async def test_raw_response_settings(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = await response.parse() assert_matches_type(object, budget, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_settings(self, async_client: AsyncHanzo) -> None: async with async_client.budget.with_streaming_response.settings( budget_id="budget_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" budget = await response.parse() assert_matches_type(object, budget, path=["response"]) diff --git a/tests/api_resources/test_cache.py b/tests/api_resources/test_cache.py index a551634cd..d645297df 100644 --- a/tests/api_resources/test_cache.py +++ b/tests/api_resources/test_cache.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -17,75 +17,84 @@ class TestCache: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: cache = client.cache.delete() assert_matches_type(object, cache, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.cache.with_raw_response.delete() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cache = response.parse() assert_matches_type(object, cache, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.cache.with_streaming_response.delete() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cache = response.parse() assert_matches_type(object, cache, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_flush_all(self, client: Hanzo) -> None: cache = client.cache.flush_all() assert_matches_type(object, cache, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_flush_all(self, client: Hanzo) -> None: response = client.cache.with_raw_response.flush_all() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cache = response.parse() assert_matches_type(object, cache, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_flush_all(self, client: Hanzo) -> None: with client.cache.with_streaming_response.flush_all() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cache = response.parse() assert_matches_type(object, cache, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_ping(self, client: Hanzo) -> None: cache = client.cache.ping() assert_matches_type(CachePingResponse, cache, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_ping(self, client: Hanzo) -> None: response = client.cache.with_raw_response.ping() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cache = response.parse() assert_matches_type(CachePingResponse, cache, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_ping(self, client: Hanzo) -> None: with client.cache.with_streaming_response.ping() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cache = response.parse() assert_matches_type(CachePingResponse, cache, path=["response"]) @@ -94,77 +103,88 @@ def test_streaming_response_ping(self, client: Hanzo) -> None: class TestAsyncCache: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: cache = await async_client.cache.delete() assert_matches_type(object, cache, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.cache.with_raw_response.delete() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cache = await response.parse() assert_matches_type(object, cache, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.cache.with_streaming_response.delete() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cache = await response.parse() assert_matches_type(object, cache, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_flush_all(self, async_client: AsyncHanzo) -> None: cache = await async_client.cache.flush_all() assert_matches_type(object, cache, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_flush_all(self, async_client: AsyncHanzo) -> None: response = await async_client.cache.with_raw_response.flush_all() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cache = await response.parse() assert_matches_type(object, cache, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_flush_all(self, async_client: AsyncHanzo) -> None: async with async_client.cache.with_streaming_response.flush_all() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cache = await response.parse() assert_matches_type(object, cache, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_ping(self, async_client: AsyncHanzo) -> None: cache = await async_client.cache.ping() assert_matches_type(CachePingResponse, cache, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_ping(self, async_client: AsyncHanzo) -> None: response = await async_client.cache.with_raw_response.ping() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cache = await response.parse() assert_matches_type(CachePingResponse, cache, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_ping(self, async_client: AsyncHanzo) -> None: async with async_client.cache.with_streaming_response.ping() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cache = await response.parse() assert_matches_type(CachePingResponse, cache, path=["response"]) diff --git a/tests/api_resources/test_client.py b/tests/api_resources/test_client.py index f311a3280..807bad4c9 100644 --- a/tests/api_resources/test_client.py +++ b/tests/api_resources/test_client.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,25 +16,28 @@ class TestClient: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_get_home(self, client: Hanzo) -> None: client_ = client.get_home() assert_matches_type(object, client_, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_get_home(self, client: Hanzo) -> None: response = client.with_raw_response.get_home() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" client_ = response.parse() assert_matches_type(object, client_, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_get_home(self, client: Hanzo) -> None: with client.with_streaming_response.get_home() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" client_ = response.parse() assert_matches_type(object, client_, path=["response"]) @@ -43,27 +46,32 @@ def test_streaming_response_get_home(self, client: Hanzo) -> None: class TestAsyncClient: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_get_home(self, async_client: AsyncHanzo) -> None: client = await async_client.get_home() assert_matches_type(object, client, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_get_home(self, async_client: AsyncHanzo) -> None: response = await async_client.with_raw_response.get_home() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" client = await response.parse() assert_matches_type(object, client, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_get_home(self, async_client: AsyncHanzo) -> None: async with async_client.with_streaming_response.get_home() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" client = await response.parse() assert_matches_type(object, client, path=["response"]) diff --git a/tests/api_resources/test_cohere.py b/tests/api_resources/test_cohere.py index 29bf9021a..0fe845338 100644 --- a/tests/api_resources/test_cohere.py +++ b/tests/api_resources/test_cohere.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestCohere: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: cohere = client.cohere.create( @@ -23,6 +24,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.cohere.with_raw_response.create( @@ -30,33 +32,33 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = response.parse() assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.cohere.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = response.parse() assert_matches_type(object, cohere, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.cohere.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: cohere = client.cohere.retrieve( @@ -64,6 +66,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.cohere.with_raw_response.retrieve( @@ -71,33 +74,33 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = response.parse() assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.cohere.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = response.parse() assert_matches_type(object, cohere, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.cohere.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: cohere = client.cohere.update( @@ -105,6 +108,7 @@ def test_method_update(self, client: Hanzo) -> None: ) assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.cohere.with_raw_response.update( @@ -112,33 +116,33 @@ def test_raw_response_update(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = response.parse() assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.cohere.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = response.parse() assert_matches_type(object, cohere, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.cohere.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: cohere = client.cohere.delete( @@ -146,6 +150,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.cohere.with_raw_response.delete( @@ -153,33 +158,33 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = response.parse() assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.cohere.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = response.parse() assert_matches_type(object, cohere, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.cohere.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_modify(self, client: Hanzo) -> None: cohere = client.cohere.modify( @@ -187,6 +192,7 @@ def test_method_modify(self, client: Hanzo) -> None: ) assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_modify(self, client: Hanzo) -> None: response = client.cohere.with_raw_response.modify( @@ -194,37 +200,39 @@ def test_raw_response_modify(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = response.parse() assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_modify(self, client: Hanzo) -> None: with client.cohere.with_streaming_response.modify( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = response.parse() assert_matches_type(object, cohere, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_modify(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.cohere.with_raw_response.modify( "", ) class TestAsyncCohere: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: cohere = await async_client.cohere.create( @@ -232,6 +240,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.cohere.with_raw_response.create( @@ -239,33 +248,33 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = await response.parse() assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.cohere.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = await response.parse() assert_matches_type(object, cohere, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.cohere.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: cohere = await async_client.cohere.retrieve( @@ -273,6 +282,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.cohere.with_raw_response.retrieve( @@ -280,33 +290,33 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = await response.parse() assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.cohere.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = await response.parse() assert_matches_type(object, cohere, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.cohere.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: cohere = await async_client.cohere.update( @@ -314,6 +324,7 @@ async def test_method_update(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.cohere.with_raw_response.update( @@ -321,33 +332,33 @@ async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = await response.parse() assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.cohere.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = await response.parse() assert_matches_type(object, cohere, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.cohere.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: cohere = await async_client.cohere.delete( @@ -355,6 +366,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.cohere.with_raw_response.delete( @@ -362,33 +374,33 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = await response.parse() assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.cohere.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = await response.parse() assert_matches_type(object, cohere, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.cohere.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_modify(self, async_client: AsyncHanzo) -> None: cohere = await async_client.cohere.modify( @@ -396,6 +408,7 @@ async def test_method_modify(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_modify(self, async_client: AsyncHanzo) -> None: response = await async_client.cohere.with_raw_response.modify( @@ -403,29 +416,28 @@ async def test_raw_response_modify(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = await response.parse() assert_matches_type(object, cohere, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_modify(self, async_client: AsyncHanzo) -> None: async with async_client.cohere.with_streaming_response.modify( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" cohere = await response.parse() assert_matches_type(object, cohere, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_modify(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.cohere.with_raw_response.modify( "", ) diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py index 717634cba..09e98f990 100644 --- a/tests/api_resources/test_completions.py +++ b/tests/api_resources/test_completions.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,11 +16,13 @@ class TestCompletions: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: completion = client.completions.create() assert_matches_type(object, completion, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: Hanzo) -> None: completion = client.completions.create( @@ -28,20 +30,22 @@ def test_method_create_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, completion, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.completions.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = response.parse() assert_matches_type(object, completion, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.completions.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = response.parse() assert_matches_type(object, completion, path=["response"]) @@ -50,13 +54,17 @@ def test_streaming_response_create(self, client: Hanzo) -> None: class TestAsyncCompletions: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: completion = await async_client.completions.create() assert_matches_type(object, completion, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> None: completion = await async_client.completions.create( @@ -64,20 +72,22 @@ async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, completion, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.completions.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = await response.parse() assert_matches_type(object, completion, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.completions.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = await response.parse() assert_matches_type(object, completion, path=["response"]) diff --git a/tests/api_resources/test_credentials.py b/tests/api_resources/test_credentials.py index 461cfd04a..f95954fa9 100644 --- a/tests/api_resources/test_credentials.py +++ b/tests/api_resources/test_credentials.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestCredentials: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: credential = client.credentials.create( @@ -24,6 +25,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, credential, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: Hanzo) -> None: credential = client.credentials.create( @@ -34,6 +36,7 @@ def test_method_create_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, credential, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.credentials.with_raw_response.create( @@ -42,10 +45,11 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(object, credential, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.credentials.with_streaming_response.create( @@ -53,38 +57,42 @@ def test_streaming_response_create(self, client: Hanzo) -> None: credential_name="credential_name", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(object, credential, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: credential = client.credentials.list() assert_matches_type(object, credential, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.credentials.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(object, credential, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.credentials.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(object, credential, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: credential = client.credentials.delete( @@ -92,6 +100,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, credential, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.credentials.with_raw_response.delete( @@ -99,37 +108,39 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(object, credential, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.credentials.with_streaming_response.delete( "credential_name", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(object, credential, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `credential_name` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_name` but received ''"): client.credentials.with_raw_response.delete( "", ) class TestAsyncCredentials: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: credential = await async_client.credentials.create( @@ -138,6 +149,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, credential, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> None: credential = await async_client.credentials.create( @@ -148,6 +160,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, credential, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.credentials.with_raw_response.create( @@ -156,10 +169,11 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = await response.parse() assert_matches_type(object, credential, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.credentials.with_streaming_response.create( @@ -167,38 +181,42 @@ async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None credential_name="credential_name", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = await response.parse() assert_matches_type(object, credential, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: credential = await async_client.credentials.list() assert_matches_type(object, credential, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.credentials.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = await response.parse() assert_matches_type(object, credential, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.credentials.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = await response.parse() assert_matches_type(object, credential, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: credential = await async_client.credentials.delete( @@ -206,6 +224,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, credential, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.credentials.with_raw_response.delete( @@ -213,29 +232,28 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = await response.parse() assert_matches_type(object, credential, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.credentials.with_streaming_response.delete( "credential_name", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = await response.parse() assert_matches_type(object, credential, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `credential_name` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_name` but received ''"): await async_client.credentials.with_raw_response.delete( "", ) diff --git a/tests/api_resources/test_customer.py b/tests/api_resources/test_customer.py index 9dd5d9ded..8cc9c37c9 100644 --- a/tests/api_resources/test_customer.py +++ b/tests/api_resources/test_customer.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -10,8 +10,8 @@ from hanzoai import Hanzo, AsyncHanzo from tests.utils import assert_matches_type from hanzoai.types import ( - HanzoEndUserTable, CustomerListResponse, + CustomerRetrieveInfoResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -20,6 +20,7 @@ class TestCustomer: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: customer = client.customer.create( @@ -27,6 +28,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: Hanzo) -> None: customer = client.customer.create( @@ -53,6 +55,7 @@ def test_method_create_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.customer.with_raw_response.create( @@ -60,23 +63,25 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = response.parse() assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.customer.with_streaming_response.create( user_id="user_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = response.parse() assert_matches_type(object, customer, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: customer = client.customer.update( @@ -84,6 +89,7 @@ def test_method_update(self, client: Hanzo) -> None: ) assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: Hanzo) -> None: customer = client.customer.update( @@ -97,6 +103,7 @@ def test_method_update_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.customer.with_raw_response.update( @@ -104,48 +111,53 @@ def test_raw_response_update(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = response.parse() assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.customer.with_streaming_response.update( user_id="user_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = response.parse() assert_matches_type(object, customer, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: customer = client.customer.list() assert_matches_type(CustomerListResponse, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.customer.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = response.parse() assert_matches_type(CustomerListResponse, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.customer.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = response.parse() assert_matches_type(CustomerListResponse, customer, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: customer = client.customer.delete( @@ -153,6 +165,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.customer.with_raw_response.delete( @@ -160,23 +173,25 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = response.parse() assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.customer.with_streaming_response.delete( user_ids=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = response.parse() assert_matches_type(object, customer, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_block(self, client: Hanzo) -> None: customer = client.customer.block( @@ -184,6 +199,7 @@ def test_method_block(self, client: Hanzo) -> None: ) assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_block(self, client: Hanzo) -> None: response = client.customer.with_raw_response.block( @@ -191,30 +207,33 @@ def test_raw_response_block(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = response.parse() assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_block(self, client: Hanzo) -> None: with client.customer.with_streaming_response.block( user_ids=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = response.parse() assert_matches_type(object, customer, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_info(self, client: Hanzo) -> None: customer = client.customer.retrieve_info( end_user_id="end_user_id", ) - assert_matches_type(HanzoEndUserTable, customer, path=["response"]) + assert_matches_type(CustomerRetrieveInfoResponse, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve_info(self, client: Hanzo) -> None: response = client.customer.with_raw_response.retrieve_info( @@ -222,23 +241,25 @@ def test_raw_response_retrieve_info(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = response.parse() - assert_matches_type(HanzoEndUserTable, customer, path=["response"]) + assert_matches_type(CustomerRetrieveInfoResponse, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve_info(self, client: Hanzo) -> None: with client.customer.with_streaming_response.retrieve_info( end_user_id="end_user_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = response.parse() - assert_matches_type(HanzoEndUserTable, customer, path=["response"]) + assert_matches_type(CustomerRetrieveInfoResponse, customer, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_unblock(self, client: Hanzo) -> None: customer = client.customer.unblock( @@ -246,6 +267,7 @@ def test_method_unblock(self, client: Hanzo) -> None: ) assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_unblock(self, client: Hanzo) -> None: response = client.customer.with_raw_response.unblock( @@ -253,17 +275,18 @@ def test_raw_response_unblock(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = response.parse() assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_unblock(self, client: Hanzo) -> None: with client.customer.with_streaming_response.unblock( user_ids=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = response.parse() assert_matches_type(object, customer, path=["response"]) @@ -272,8 +295,11 @@ def test_streaming_response_unblock(self, client: Hanzo) -> None: class TestAsyncCustomer: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: customer = await async_client.customer.create( @@ -281,6 +307,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> None: customer = await async_client.customer.create( @@ -307,6 +334,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.customer.with_raw_response.create( @@ -314,23 +342,25 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = await response.parse() assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.customer.with_streaming_response.create( user_id="user_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = await response.parse() assert_matches_type(object, customer, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: customer = await async_client.customer.update( @@ -338,6 +368,7 @@ async def test_method_update(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncHanzo) -> None: customer = await async_client.customer.update( @@ -351,6 +382,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.customer.with_raw_response.update( @@ -358,48 +390,53 @@ async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = await response.parse() assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.customer.with_streaming_response.update( user_id="user_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = await response.parse() assert_matches_type(object, customer, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: customer = await async_client.customer.list() assert_matches_type(CustomerListResponse, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.customer.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = await response.parse() assert_matches_type(CustomerListResponse, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.customer.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = await response.parse() assert_matches_type(CustomerListResponse, customer, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: customer = await async_client.customer.delete( @@ -407,6 +444,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.customer.with_raw_response.delete( @@ -414,23 +452,25 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = await response.parse() assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.customer.with_streaming_response.delete( user_ids=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = await response.parse() assert_matches_type(object, customer, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_block(self, async_client: AsyncHanzo) -> None: customer = await async_client.customer.block( @@ -438,6 +478,7 @@ async def test_method_block(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_block(self, async_client: AsyncHanzo) -> None: response = await async_client.customer.with_raw_response.block( @@ -445,30 +486,33 @@ async def test_raw_response_block(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = await response.parse() assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_block(self, async_client: AsyncHanzo) -> None: async with async_client.customer.with_streaming_response.block( user_ids=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = await response.parse() assert_matches_type(object, customer, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_info(self, async_client: AsyncHanzo) -> None: customer = await async_client.customer.retrieve_info( end_user_id="end_user_id", ) - assert_matches_type(HanzoEndUserTable, customer, path=["response"]) + assert_matches_type(CustomerRetrieveInfoResponse, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve_info(self, async_client: AsyncHanzo) -> None: response = await async_client.customer.with_raw_response.retrieve_info( @@ -476,23 +520,25 @@ async def test_raw_response_retrieve_info(self, async_client: AsyncHanzo) -> Non ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = await response.parse() - assert_matches_type(HanzoEndUserTable, customer, path=["response"]) + assert_matches_type(CustomerRetrieveInfoResponse, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve_info(self, async_client: AsyncHanzo) -> None: async with async_client.customer.with_streaming_response.retrieve_info( end_user_id="end_user_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = await response.parse() - assert_matches_type(HanzoEndUserTable, customer, path=["response"]) + assert_matches_type(CustomerRetrieveInfoResponse, customer, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_unblock(self, async_client: AsyncHanzo) -> None: customer = await async_client.customer.unblock( @@ -500,6 +546,7 @@ async def test_method_unblock(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_unblock(self, async_client: AsyncHanzo) -> None: response = await async_client.customer.with_raw_response.unblock( @@ -507,17 +554,18 @@ async def test_raw_response_unblock(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = await response.parse() assert_matches_type(object, customer, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_unblock(self, async_client: AsyncHanzo) -> None: async with async_client.customer.with_streaming_response.unblock( user_ids=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" customer = await response.parse() assert_matches_type(object, customer, path=["response"]) diff --git a/tests/api_resources/test_delete.py b/tests/api_resources/test_delete.py index 93ca0f5b3..4f0ce744e 100644 --- a/tests/api_resources/test_delete.py +++ b/tests/api_resources/test_delete.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestDelete: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_allowed_ip(self, client: Hanzo) -> None: delete = client.delete.create_allowed_ip( @@ -23,6 +24,7 @@ def test_method_create_allowed_ip(self, client: Hanzo) -> None: ) assert_matches_type(object, delete, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_allowed_ip(self, client: Hanzo) -> None: response = client.delete.with_raw_response.create_allowed_ip( @@ -30,17 +32,18 @@ def test_raw_response_create_allowed_ip(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" delete = response.parse() assert_matches_type(object, delete, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_allowed_ip(self, client: Hanzo) -> None: with client.delete.with_streaming_response.create_allowed_ip( ip="ip", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" delete = response.parse() assert_matches_type(object, delete, path=["response"]) @@ -49,8 +52,11 @@ def test_streaming_response_create_allowed_ip(self, client: Hanzo) -> None: class TestAsyncDelete: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_allowed_ip(self, async_client: AsyncHanzo) -> None: delete = await async_client.delete.create_allowed_ip( @@ -58,6 +64,7 @@ async def test_method_create_allowed_ip(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, delete, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_allowed_ip(self, async_client: AsyncHanzo) -> None: response = await async_client.delete.with_raw_response.create_allowed_ip( @@ -65,17 +72,18 @@ async def test_raw_response_create_allowed_ip(self, async_client: AsyncHanzo) -> ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" delete = await response.parse() assert_matches_type(object, delete, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_allowed_ip(self, async_client: AsyncHanzo) -> None: async with async_client.delete.with_streaming_response.create_allowed_ip( ip="ip", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" delete = await response.parse() assert_matches_type(object, delete, path=["response"]) diff --git a/tests/api_resources/test_embeddings.py b/tests/api_resources/test_embeddings.py index 48941a9a0..26880a95a 100644 --- a/tests/api_resources/test_embeddings.py +++ b/tests/api_resources/test_embeddings.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,11 +16,13 @@ class TestEmbeddings: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: embedding = client.embeddings.create() assert_matches_type(object, embedding, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: Hanzo) -> None: embedding = client.embeddings.create( @@ -28,20 +30,22 @@ def test_method_create_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, embedding, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.embeddings.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" embedding = response.parse() assert_matches_type(object, embedding, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.embeddings.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" embedding = response.parse() assert_matches_type(object, embedding, path=["response"]) @@ -50,13 +54,17 @@ def test_streaming_response_create(self, client: Hanzo) -> None: class TestAsyncEmbeddings: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: embedding = await async_client.embeddings.create() assert_matches_type(object, embedding, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> None: embedding = await async_client.embeddings.create( @@ -64,20 +72,22 @@ async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, embedding, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.embeddings.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" embedding = await response.parse() assert_matches_type(object, embedding, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.embeddings.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" embedding = await response.parse() assert_matches_type(object, embedding, path=["response"]) diff --git a/tests/api_resources/test_engines.py b/tests/api_resources/test_engines.py index 0102ebb58..78894a693 100644 --- a/tests/api_resources/test_engines.py +++ b/tests/api_resources/test_engines.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestEngines: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_complete(self, client: Hanzo) -> None: engine = client.engines.complete( @@ -23,6 +24,7 @@ def test_method_complete(self, client: Hanzo) -> None: ) assert_matches_type(object, engine, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_complete(self, client: Hanzo) -> None: response = client.engines.with_raw_response.complete( @@ -30,23 +32,25 @@ def test_raw_response_complete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" engine = response.parse() assert_matches_type(object, engine, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_complete(self, client: Hanzo) -> None: with client.engines.with_streaming_response.complete( "model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" engine = response.parse() assert_matches_type(object, engine, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_complete(self, client: Hanzo) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): @@ -54,6 +58,7 @@ def test_path_params_complete(self, client: Hanzo) -> None: "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_embed(self, client: Hanzo) -> None: engine = client.engines.embed( @@ -61,6 +66,7 @@ def test_method_embed(self, client: Hanzo) -> None: ) assert_matches_type(object, engine, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_embed(self, client: Hanzo) -> None: response = client.engines.with_raw_response.embed( @@ -68,23 +74,25 @@ def test_raw_response_embed(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" engine = response.parse() assert_matches_type(object, engine, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_embed(self, client: Hanzo) -> None: with client.engines.with_streaming_response.embed( "model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" engine = response.parse() assert_matches_type(object, engine, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_embed(self, client: Hanzo) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): @@ -94,8 +102,11 @@ def test_path_params_embed(self, client: Hanzo) -> None: class TestAsyncEngines: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_complete(self, async_client: AsyncHanzo) -> None: engine = await async_client.engines.complete( @@ -103,6 +114,7 @@ async def test_method_complete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, engine, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_complete(self, async_client: AsyncHanzo) -> None: response = await async_client.engines.with_raw_response.complete( @@ -110,23 +122,25 @@ async def test_raw_response_complete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" engine = await response.parse() assert_matches_type(object, engine, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_complete(self, async_client: AsyncHanzo) -> None: async with async_client.engines.with_streaming_response.complete( "model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" engine = await response.parse() assert_matches_type(object, engine, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_complete(self, async_client: AsyncHanzo) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): @@ -134,6 +148,7 @@ async def test_path_params_complete(self, async_client: AsyncHanzo) -> None: "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_embed(self, async_client: AsyncHanzo) -> None: engine = await async_client.engines.embed( @@ -141,6 +156,7 @@ async def test_method_embed(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, engine, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_embed(self, async_client: AsyncHanzo) -> None: response = await async_client.engines.with_raw_response.embed( @@ -148,23 +164,25 @@ async def test_raw_response_embed(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" engine = await response.parse() assert_matches_type(object, engine, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_embed(self, async_client: AsyncHanzo) -> None: async with async_client.engines.with_streaming_response.embed( "model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" engine = await response.parse() assert_matches_type(object, engine, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_embed(self, async_client: AsyncHanzo) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): diff --git a/tests/api_resources/test_eu_assemblyai.py b/tests/api_resources/test_eu_assemblyai.py index 2c61010b8..3bb9074e0 100644 --- a/tests/api_resources/test_eu_assemblyai.py +++ b/tests/api_resources/test_eu_assemblyai.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestEuAssemblyai: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: eu_assemblyai = client.eu_assemblyai.create( @@ -23,6 +24,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.eu_assemblyai.with_raw_response.create( @@ -30,33 +32,33 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.eu_assemblyai.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.eu_assemblyai.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: eu_assemblyai = client.eu_assemblyai.retrieve( @@ -64,6 +66,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.eu_assemblyai.with_raw_response.retrieve( @@ -71,33 +74,33 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.eu_assemblyai.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.eu_assemblyai.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: eu_assemblyai = client.eu_assemblyai.update( @@ -105,6 +108,7 @@ def test_method_update(self, client: Hanzo) -> None: ) assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.eu_assemblyai.with_raw_response.update( @@ -112,33 +116,33 @@ def test_raw_response_update(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.eu_assemblyai.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.eu_assemblyai.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: eu_assemblyai = client.eu_assemblyai.delete( @@ -146,6 +150,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.eu_assemblyai.with_raw_response.delete( @@ -153,33 +158,33 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.eu_assemblyai.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.eu_assemblyai.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_patch(self, client: Hanzo) -> None: eu_assemblyai = client.eu_assemblyai.patch( @@ -187,6 +192,7 @@ def test_method_patch(self, client: Hanzo) -> None: ) assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_patch(self, client: Hanzo) -> None: response = client.eu_assemblyai.with_raw_response.patch( @@ -194,37 +200,39 @@ def test_raw_response_patch(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_patch(self, client: Hanzo) -> None: with client.eu_assemblyai.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_patch(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.eu_assemblyai.with_raw_response.patch( "", ) class TestAsyncEuAssemblyai: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: eu_assemblyai = await async_client.eu_assemblyai.create( @@ -232,6 +240,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.eu_assemblyai.with_raw_response.create( @@ -239,33 +248,33 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = await response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.eu_assemblyai.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = await response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.eu_assemblyai.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: eu_assemblyai = await async_client.eu_assemblyai.retrieve( @@ -273,6 +282,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.eu_assemblyai.with_raw_response.retrieve( @@ -280,33 +290,33 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = await response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.eu_assemblyai.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = await response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.eu_assemblyai.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: eu_assemblyai = await async_client.eu_assemblyai.update( @@ -314,6 +324,7 @@ async def test_method_update(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.eu_assemblyai.with_raw_response.update( @@ -321,33 +332,33 @@ async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = await response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.eu_assemblyai.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = await response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.eu_assemblyai.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: eu_assemblyai = await async_client.eu_assemblyai.delete( @@ -355,6 +366,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.eu_assemblyai.with_raw_response.delete( @@ -362,33 +374,33 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = await response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.eu_assemblyai.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = await response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.eu_assemblyai.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_patch(self, async_client: AsyncHanzo) -> None: eu_assemblyai = await async_client.eu_assemblyai.patch( @@ -396,6 +408,7 @@ async def test_method_patch(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: response = await async_client.eu_assemblyai.with_raw_response.patch( @@ -403,29 +416,28 @@ async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = await response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_patch(self, async_client: AsyncHanzo) -> None: async with async_client.eu_assemblyai.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" eu_assemblyai = await response.parse() assert_matches_type(object, eu_assemblyai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_patch(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.eu_assemblyai.with_raw_response.patch( "", ) diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index b4439a886..8653f2925 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestFiles: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: file = client.files.create( @@ -25,6 +26,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: Hanzo) -> None: file = client.files.create( @@ -35,6 +37,7 @@ def test_method_create_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.files.with_raw_response.create( @@ -44,10 +47,11 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.files.with_streaming_response.create( @@ -56,25 +60,24 @@ def test_streaming_response_create(self, client: Hanzo) -> None: purpose="purpose", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(object, file, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): client.files.with_raw_response.create( provider="", file=b"raw file contents", purpose="purpose", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: file = client.files.retrieve( @@ -83,6 +86,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.files.with_raw_response.retrieve( @@ -91,10 +95,11 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.files.with_streaming_response.retrieve( @@ -102,33 +107,29 @@ def test_streaming_response_retrieve(self, client: Hanzo) -> None: provider="provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(object, file, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): client.files.with_raw_response.retrieve( file_id="file_id", provider="", ) - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `file_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): client.files.with_raw_response.retrieve( file_id="", provider="provider", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: file = client.files.list( @@ -136,6 +137,7 @@ def test_method_list(self, client: Hanzo) -> None: ) assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: Hanzo) -> None: file = client.files.list( @@ -144,6 +146,7 @@ def test_method_list_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.files.with_raw_response.list( @@ -151,33 +154,33 @@ def test_raw_response_list(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.files.with_streaming_response.list( provider="provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(object, file, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_list(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): client.files.with_raw_response.list( provider="", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: file = client.files.delete( @@ -186,6 +189,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.files.with_raw_response.delete( @@ -194,10 +198,11 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.files.with_streaming_response.delete( @@ -205,28 +210,23 @@ def test_streaming_response_delete(self, client: Hanzo) -> None: provider="provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(object, file, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): client.files.with_raw_response.delete( file_id="file_id", provider="", ) - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `file_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): client.files.with_raw_response.delete( file_id="", provider="provider", @@ -234,8 +234,11 @@ def test_path_params_delete(self, client: Hanzo) -> None: class TestAsyncFiles: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: file = await async_client.files.create( @@ -245,6 +248,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> None: file = await async_client.files.create( @@ -255,6 +259,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.files.with_raw_response.create( @@ -264,10 +269,11 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.files.with_streaming_response.create( @@ -276,25 +282,24 @@ async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None purpose="purpose", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() assert_matches_type(object, file, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): await async_client.files.with_raw_response.create( provider="", file=b"raw file contents", purpose="purpose", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: file = await async_client.files.retrieve( @@ -303,6 +308,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.files.with_raw_response.retrieve( @@ -311,10 +317,11 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.files.with_streaming_response.retrieve( @@ -322,33 +329,29 @@ async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> No provider="provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() assert_matches_type(object, file, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): await async_client.files.with_raw_response.retrieve( file_id="file_id", provider="", ) - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `file_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): await async_client.files.with_raw_response.retrieve( file_id="", provider="provider", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: file = await async_client.files.list( @@ -356,6 +359,7 @@ async def test_method_list(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> None: file = await async_client.files.list( @@ -364,6 +368,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> No ) assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.files.with_raw_response.list( @@ -371,33 +376,33 @@ async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.files.with_streaming_response.list( provider="provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() assert_matches_type(object, file, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_list(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): await async_client.files.with_raw_response.list( provider="", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: file = await async_client.files.delete( @@ -406,6 +411,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.files.with_raw_response.delete( @@ -414,10 +420,11 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() assert_matches_type(object, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.files.with_streaming_response.delete( @@ -425,28 +432,23 @@ async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None provider="provider", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() assert_matches_type(object, file, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `provider` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `provider` but received ''"): await async_client.files.with_raw_response.delete( file_id="file_id", provider="", ) - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `file_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): await async_client.files.with_raw_response.delete( file_id="", provider="provider", diff --git a/tests/api_resources/test_gemini.py b/tests/api_resources/test_gemini.py index a97e3cd4d..5cb8bac20 100644 --- a/tests/api_resources/test_gemini.py +++ b/tests/api_resources/test_gemini.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestGemini: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: gemini = client.gemini.create( @@ -23,6 +24,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.gemini.with_raw_response.create( @@ -30,33 +32,33 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = response.parse() assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.gemini.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = response.parse() assert_matches_type(object, gemini, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.gemini.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: gemini = client.gemini.retrieve( @@ -64,6 +66,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.gemini.with_raw_response.retrieve( @@ -71,33 +74,33 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = response.parse() assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.gemini.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = response.parse() assert_matches_type(object, gemini, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.gemini.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: gemini = client.gemini.update( @@ -105,6 +108,7 @@ def test_method_update(self, client: Hanzo) -> None: ) assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.gemini.with_raw_response.update( @@ -112,33 +116,33 @@ def test_raw_response_update(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = response.parse() assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.gemini.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = response.parse() assert_matches_type(object, gemini, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.gemini.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: gemini = client.gemini.delete( @@ -146,6 +150,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.gemini.with_raw_response.delete( @@ -153,33 +158,33 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = response.parse() assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.gemini.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = response.parse() assert_matches_type(object, gemini, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.gemini.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_patch(self, client: Hanzo) -> None: gemini = client.gemini.patch( @@ -187,6 +192,7 @@ def test_method_patch(self, client: Hanzo) -> None: ) assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_patch(self, client: Hanzo) -> None: response = client.gemini.with_raw_response.patch( @@ -194,37 +200,39 @@ def test_raw_response_patch(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = response.parse() assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_patch(self, client: Hanzo) -> None: with client.gemini.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = response.parse() assert_matches_type(object, gemini, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_patch(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.gemini.with_raw_response.patch( "", ) class TestAsyncGemini: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: gemini = await async_client.gemini.create( @@ -232,6 +240,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.gemini.with_raw_response.create( @@ -239,33 +248,33 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = await response.parse() assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.gemini.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = await response.parse() assert_matches_type(object, gemini, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.gemini.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: gemini = await async_client.gemini.retrieve( @@ -273,6 +282,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.gemini.with_raw_response.retrieve( @@ -280,33 +290,33 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = await response.parse() assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.gemini.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = await response.parse() assert_matches_type(object, gemini, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.gemini.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: gemini = await async_client.gemini.update( @@ -314,6 +324,7 @@ async def test_method_update(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.gemini.with_raw_response.update( @@ -321,33 +332,33 @@ async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = await response.parse() assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.gemini.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = await response.parse() assert_matches_type(object, gemini, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.gemini.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: gemini = await async_client.gemini.delete( @@ -355,6 +366,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.gemini.with_raw_response.delete( @@ -362,33 +374,33 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = await response.parse() assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.gemini.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = await response.parse() assert_matches_type(object, gemini, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.gemini.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_patch(self, async_client: AsyncHanzo) -> None: gemini = await async_client.gemini.patch( @@ -396,6 +408,7 @@ async def test_method_patch(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: response = await async_client.gemini.with_raw_response.patch( @@ -403,29 +416,28 @@ async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = await response.parse() assert_matches_type(object, gemini, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_patch(self, async_client: AsyncHanzo) -> None: async with async_client.gemini.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" gemini = await response.parse() assert_matches_type(object, gemini, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_patch(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.gemini.with_raw_response.patch( "", ) diff --git a/tests/api_resources/test_guardrails.py b/tests/api_resources/test_guardrails.py index b0535ab39..bd7a73d8f 100644 --- a/tests/api_resources/test_guardrails.py +++ b/tests/api_resources/test_guardrails.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -17,25 +17,28 @@ class TestGuardrails: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: guardrail = client.guardrails.list() assert_matches_type(GuardrailListResponse, guardrail, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.guardrails.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" guardrail = response.parse() assert_matches_type(GuardrailListResponse, guardrail, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.guardrails.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" guardrail = response.parse() assert_matches_type(GuardrailListResponse, guardrail, path=["response"]) @@ -44,27 +47,32 @@ def test_streaming_response_list(self, client: Hanzo) -> None: class TestAsyncGuardrails: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: guardrail = await async_client.guardrails.list() assert_matches_type(GuardrailListResponse, guardrail, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.guardrails.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" guardrail = await response.parse() assert_matches_type(GuardrailListResponse, guardrail, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.guardrails.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" guardrail = await response.parse() assert_matches_type(GuardrailListResponse, guardrail, path=["response"]) diff --git a/tests/api_resources/test_health.py b/tests/api_resources/test_health.py index 25de893f3..e23071ac6 100644 --- a/tests/api_resources/test_health.py +++ b/tests/api_resources/test_health.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,11 +16,13 @@ class TestHealth: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_check_all(self, client: Hanzo) -> None: health = client.health.check_all() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_check_all_with_all_params(self, client: Hanzo) -> None: health = client.health.check_all( @@ -28,101 +30,113 @@ def test_method_check_all_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_check_all(self, client: Hanzo) -> None: response = client.health.with_raw_response.check_all() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = response.parse() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_check_all(self, client: Hanzo) -> None: with client.health.with_streaming_response.check_all() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = response.parse() assert_matches_type(object, health, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_check_liveliness(self, client: Hanzo) -> None: health = client.health.check_liveliness() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_check_liveliness(self, client: Hanzo) -> None: response = client.health.with_raw_response.check_liveliness() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = response.parse() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_check_liveliness(self, client: Hanzo) -> None: with client.health.with_streaming_response.check_liveliness() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = response.parse() assert_matches_type(object, health, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_check_liveness(self, client: Hanzo) -> None: health = client.health.check_liveness() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_check_liveness(self, client: Hanzo) -> None: response = client.health.with_raw_response.check_liveness() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = response.parse() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_check_liveness(self, client: Hanzo) -> None: with client.health.with_streaming_response.check_liveness() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = response.parse() assert_matches_type(object, health, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_check_readiness(self, client: Hanzo) -> None: health = client.health.check_readiness() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_check_readiness(self, client: Hanzo) -> None: response = client.health.with_raw_response.check_readiness() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = response.parse() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_check_readiness(self, client: Hanzo) -> None: with client.health.with_streaming_response.check_readiness() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = response.parse() assert_matches_type(object, health, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_check_services(self, client: Hanzo) -> None: health = client.health.check_services( @@ -130,6 +144,7 @@ def test_method_check_services(self, client: Hanzo) -> None: ) assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_check_services(self, client: Hanzo) -> None: response = client.health.with_raw_response.check_services( @@ -137,17 +152,18 @@ def test_raw_response_check_services(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = response.parse() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_check_services(self, client: Hanzo) -> None: with client.health.with_streaming_response.check_services( service="slack_budget_alerts", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = response.parse() assert_matches_type(object, health, path=["response"]) @@ -156,13 +172,17 @@ def test_streaming_response_check_services(self, client: Hanzo) -> None: class TestAsyncHealth: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_check_all(self, async_client: AsyncHanzo) -> None: health = await async_client.health.check_all() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_check_all_with_all_params(self, async_client: AsyncHanzo) -> None: health = await async_client.health.check_all( @@ -170,101 +190,113 @@ async def test_method_check_all_with_all_params(self, async_client: AsyncHanzo) ) assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_check_all(self, async_client: AsyncHanzo) -> None: response = await async_client.health.with_raw_response.check_all() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = await response.parse() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_check_all(self, async_client: AsyncHanzo) -> None: async with async_client.health.with_streaming_response.check_all() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = await response.parse() assert_matches_type(object, health, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_check_liveliness(self, async_client: AsyncHanzo) -> None: health = await async_client.health.check_liveliness() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_check_liveliness(self, async_client: AsyncHanzo) -> None: response = await async_client.health.with_raw_response.check_liveliness() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = await response.parse() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_check_liveliness(self, async_client: AsyncHanzo) -> None: async with async_client.health.with_streaming_response.check_liveliness() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = await response.parse() assert_matches_type(object, health, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_check_liveness(self, async_client: AsyncHanzo) -> None: health = await async_client.health.check_liveness() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_check_liveness(self, async_client: AsyncHanzo) -> None: response = await async_client.health.with_raw_response.check_liveness() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = await response.parse() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_check_liveness(self, async_client: AsyncHanzo) -> None: async with async_client.health.with_streaming_response.check_liveness() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = await response.parse() assert_matches_type(object, health, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_check_readiness(self, async_client: AsyncHanzo) -> None: health = await async_client.health.check_readiness() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_check_readiness(self, async_client: AsyncHanzo) -> None: response = await async_client.health.with_raw_response.check_readiness() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = await response.parse() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_check_readiness(self, async_client: AsyncHanzo) -> None: async with async_client.health.with_streaming_response.check_readiness() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = await response.parse() assert_matches_type(object, health, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_check_services(self, async_client: AsyncHanzo) -> None: health = await async_client.health.check_services( @@ -272,6 +304,7 @@ async def test_method_check_services(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_check_services(self, async_client: AsyncHanzo) -> None: response = await async_client.health.with_raw_response.check_services( @@ -279,17 +312,18 @@ async def test_raw_response_check_services(self, async_client: AsyncHanzo) -> No ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = await response.parse() assert_matches_type(object, health, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_check_services(self, async_client: AsyncHanzo) -> None: async with async_client.health.with_streaming_response.check_services( service="slack_budget_alerts", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" health = await response.parse() assert_matches_type(object, health, path=["response"]) diff --git a/tests/api_resources/test_key.py b/tests/api_resources/test_key.py index 90eb40642..9ee427450 100644 --- a/tests/api_resources/test_key.py +++ b/tests/api_resources/test_key.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -23,6 +23,7 @@ class TestKey: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: key = client.key.update( @@ -30,6 +31,7 @@ def test_method_update(self, client: Hanzo) -> None: ) assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: Hanzo) -> None: key = client.key.update( @@ -60,10 +62,11 @@ def test_method_update_with_all_params(self, client: Hanzo) -> None: temp_budget_increase=0, tpm_limit=0, user_id="user_id", - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.key.with_raw_response.update( @@ -71,28 +74,31 @@ def test_raw_response_update(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.key.with_streaming_response.update( key="key", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(object, key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: key = client.key.list() assert_matches_type(KeyListResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: Hanzo) -> None: key = client.key.list( @@ -107,60 +113,67 @@ def test_method_list_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(KeyListResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.key.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(KeyListResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.key.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(KeyListResponse, key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: key = client.key.delete() assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete_with_all_params(self, client: Hanzo) -> None: key = client.key.delete( key_aliases=["string"], keys=["string"], - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.key.with_raw_response.delete() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.key.with_streaming_response.delete() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(object, key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_block(self, client: Hanzo) -> None: key = client.key.block( @@ -168,14 +181,16 @@ def test_method_block(self, client: Hanzo) -> None: ) assert_matches_type(Optional[KeyBlockResponse], key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_block_with_all_params(self, client: Hanzo) -> None: key = client.key.block( key="key", - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(Optional[KeyBlockResponse], key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_block(self, client: Hanzo) -> None: response = client.key.with_raw_response.block( @@ -183,53 +198,59 @@ def test_raw_response_block(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(Optional[KeyBlockResponse], key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_block(self, client: Hanzo) -> None: with client.key.with_streaming_response.block( key="key", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(Optional[KeyBlockResponse], key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_check_health(self, client: Hanzo) -> None: key = client.key.check_health() assert_matches_type(KeyCheckHealthResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_check_health(self, client: Hanzo) -> None: response = client.key.with_raw_response.check_health() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(KeyCheckHealthResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_check_health(self, client: Hanzo) -> None: with client.key.with_streaming_response.check_health() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(KeyCheckHealthResponse, key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_generate(self, client: Hanzo) -> None: key = client.key.generate() assert_matches_type(GenerateKeyResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_generate_with_all_params(self, client: Hanzo) -> None: key = client.key.generate( @@ -260,30 +281,33 @@ def test_method_generate_with_all_params(self, client: Hanzo) -> None: team_id="team_id", tpm_limit=0, user_id="user_id", - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(GenerateKeyResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_generate(self, client: Hanzo) -> None: response = client.key.with_raw_response.generate() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(GenerateKeyResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_generate(self, client: Hanzo) -> None: with client.key.with_streaming_response.generate() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(GenerateKeyResponse, key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_regenerate_by_key(self, client: Hanzo) -> None: key = client.key.regenerate_by_key( @@ -291,6 +315,7 @@ def test_method_regenerate_by_key(self, client: Hanzo) -> None: ) assert_matches_type(Optional[GenerateKeyResponse], key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_regenerate_by_key_with_all_params(self, client: Hanzo) -> None: key = client.key.regenerate_by_key( @@ -323,10 +348,11 @@ def test_method_regenerate_by_key_with_all_params(self, client: Hanzo) -> None: team_id="team_id", tpm_limit=0, user_id="user_id", - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(Optional[GenerateKeyResponse], key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_regenerate_by_key(self, client: Hanzo) -> None: response = client.key.with_raw_response.regenerate_by_key( @@ -334,39 +360,39 @@ def test_raw_response_regenerate_by_key(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(Optional[GenerateKeyResponse], key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_regenerate_by_key(self, client: Hanzo) -> None: with client.key.with_streaming_response.regenerate_by_key( path_key="key", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(Optional[GenerateKeyResponse], key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_regenerate_by_key(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `path_key` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_key` but received ''"): client.key.with_raw_response.regenerate_by_key( path_key="", - body_key="", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_info(self, client: Hanzo) -> None: key = client.key.retrieve_info() assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_info_with_all_params(self, client: Hanzo) -> None: key = client.key.retrieve_info( @@ -374,26 +400,29 @@ def test_method_retrieve_info_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve_info(self, client: Hanzo) -> None: response = client.key.with_raw_response.retrieve_info() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve_info(self, client: Hanzo) -> None: with client.key.with_streaming_response.retrieve_info() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(object, key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_unblock(self, client: Hanzo) -> None: key = client.key.unblock( @@ -401,14 +430,16 @@ def test_method_unblock(self, client: Hanzo) -> None: ) assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_unblock_with_all_params(self, client: Hanzo) -> None: key = client.key.unblock( key="key", - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_unblock(self, client: Hanzo) -> None: response = client.key.with_raw_response.unblock( @@ -416,17 +447,18 @@ def test_raw_response_unblock(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_unblock(self, client: Hanzo) -> None: with client.key.with_streaming_response.unblock( key="key", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = response.parse() assert_matches_type(object, key, path=["response"]) @@ -435,8 +467,11 @@ def test_streaming_response_unblock(self, client: Hanzo) -> None: class TestAsyncKey: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: key = await async_client.key.update( @@ -444,6 +479,7 @@ async def test_method_update(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncHanzo) -> None: key = await async_client.key.update( @@ -474,10 +510,11 @@ async def test_method_update_with_all_params(self, async_client: AsyncHanzo) -> temp_budget_increase=0, tpm_limit=0, user_id="user_id", - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.key.with_raw_response.update( @@ -485,28 +522,31 @@ async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.key.with_streaming_response.update( key="key", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(object, key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: key = await async_client.key.list() assert_matches_type(KeyListResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> None: key = await async_client.key.list( @@ -521,60 +561,67 @@ async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> No ) assert_matches_type(KeyListResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.key.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(KeyListResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.key.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(KeyListResponse, key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: key = await async_client.key.delete() assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete_with_all_params(self, async_client: AsyncHanzo) -> None: key = await async_client.key.delete( key_aliases=["string"], keys=["string"], - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.key.with_raw_response.delete() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.key.with_streaming_response.delete() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(object, key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_block(self, async_client: AsyncHanzo) -> None: key = await async_client.key.block( @@ -582,14 +629,16 @@ async def test_method_block(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(Optional[KeyBlockResponse], key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_block_with_all_params(self, async_client: AsyncHanzo) -> None: key = await async_client.key.block( key="key", - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(Optional[KeyBlockResponse], key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_block(self, async_client: AsyncHanzo) -> None: response = await async_client.key.with_raw_response.block( @@ -597,53 +646,59 @@ async def test_raw_response_block(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(Optional[KeyBlockResponse], key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_block(self, async_client: AsyncHanzo) -> None: async with async_client.key.with_streaming_response.block( key="key", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(Optional[KeyBlockResponse], key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_check_health(self, async_client: AsyncHanzo) -> None: key = await async_client.key.check_health() assert_matches_type(KeyCheckHealthResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_check_health(self, async_client: AsyncHanzo) -> None: response = await async_client.key.with_raw_response.check_health() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(KeyCheckHealthResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_check_health(self, async_client: AsyncHanzo) -> None: async with async_client.key.with_streaming_response.check_health() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(KeyCheckHealthResponse, key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_generate(self, async_client: AsyncHanzo) -> None: key = await async_client.key.generate() assert_matches_type(GenerateKeyResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_generate_with_all_params(self, async_client: AsyncHanzo) -> None: key = await async_client.key.generate( @@ -674,30 +729,33 @@ async def test_method_generate_with_all_params(self, async_client: AsyncHanzo) - team_id="team_id", tpm_limit=0, user_id="user_id", - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(GenerateKeyResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_generate(self, async_client: AsyncHanzo) -> None: response = await async_client.key.with_raw_response.generate() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(GenerateKeyResponse, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_generate(self, async_client: AsyncHanzo) -> None: async with async_client.key.with_streaming_response.generate() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(GenerateKeyResponse, key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_regenerate_by_key(self, async_client: AsyncHanzo) -> None: key = await async_client.key.regenerate_by_key( @@ -705,6 +763,7 @@ async def test_method_regenerate_by_key(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(Optional[GenerateKeyResponse], key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_regenerate_by_key_with_all_params(self, async_client: AsyncHanzo) -> None: key = await async_client.key.regenerate_by_key( @@ -737,10 +796,11 @@ async def test_method_regenerate_by_key_with_all_params(self, async_client: Asyn team_id="team_id", tpm_limit=0, user_id="user_id", - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(Optional[GenerateKeyResponse], key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_regenerate_by_key(self, async_client: AsyncHanzo) -> None: response = await async_client.key.with_raw_response.regenerate_by_key( @@ -748,39 +808,39 @@ async def test_raw_response_regenerate_by_key(self, async_client: AsyncHanzo) -> ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(Optional[GenerateKeyResponse], key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_regenerate_by_key(self, async_client: AsyncHanzo) -> None: async with async_client.key.with_streaming_response.regenerate_by_key( path_key="key", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(Optional[GenerateKeyResponse], key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_regenerate_by_key(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `path_key` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_key` but received ''"): await async_client.key.with_raw_response.regenerate_by_key( path_key="", - body_key="", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_info(self, async_client: AsyncHanzo) -> None: key = await async_client.key.retrieve_info() assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_info_with_all_params(self, async_client: AsyncHanzo) -> None: key = await async_client.key.retrieve_info( @@ -788,26 +848,29 @@ async def test_method_retrieve_info_with_all_params(self, async_client: AsyncHan ) assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve_info(self, async_client: AsyncHanzo) -> None: response = await async_client.key.with_raw_response.retrieve_info() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve_info(self, async_client: AsyncHanzo) -> None: async with async_client.key.with_streaming_response.retrieve_info() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(object, key, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_unblock(self, async_client: AsyncHanzo) -> None: key = await async_client.key.unblock( @@ -815,14 +878,16 @@ async def test_method_unblock(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_unblock_with_all_params(self, async_client: AsyncHanzo) -> None: key = await async_client.key.unblock( key="key", - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_unblock(self, async_client: AsyncHanzo) -> None: response = await async_client.key.with_raw_response.unblock( @@ -830,17 +895,18 @@ async def test_raw_response_unblock(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(object, key, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_unblock(self, async_client: AsyncHanzo) -> None: async with async_client.key.with_streaming_response.unblock( key="key", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" key = await response.parse() assert_matches_type(object, key, path=["response"]) diff --git a/tests/api_resources/test_langfuse.py b/tests/api_resources/test_langfuse.py index b85ba6003..de62cf71c 100644 --- a/tests/api_resources/test_langfuse.py +++ b/tests/api_resources/test_langfuse.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestLangfuse: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: langfuse = client.langfuse.create( @@ -23,6 +24,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.langfuse.with_raw_response.create( @@ -30,33 +32,33 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = response.parse() assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.langfuse.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = response.parse() assert_matches_type(object, langfuse, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.langfuse.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: langfuse = client.langfuse.retrieve( @@ -64,6 +66,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.langfuse.with_raw_response.retrieve( @@ -71,33 +74,33 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = response.parse() assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.langfuse.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = response.parse() assert_matches_type(object, langfuse, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.langfuse.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: langfuse = client.langfuse.update( @@ -105,6 +108,7 @@ def test_method_update(self, client: Hanzo) -> None: ) assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.langfuse.with_raw_response.update( @@ -112,33 +116,33 @@ def test_raw_response_update(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = response.parse() assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.langfuse.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = response.parse() assert_matches_type(object, langfuse, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.langfuse.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: langfuse = client.langfuse.delete( @@ -146,6 +150,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.langfuse.with_raw_response.delete( @@ -153,33 +158,33 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = response.parse() assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.langfuse.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = response.parse() assert_matches_type(object, langfuse, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.langfuse.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_patch(self, client: Hanzo) -> None: langfuse = client.langfuse.patch( @@ -187,6 +192,7 @@ def test_method_patch(self, client: Hanzo) -> None: ) assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_patch(self, client: Hanzo) -> None: response = client.langfuse.with_raw_response.patch( @@ -194,37 +200,39 @@ def test_raw_response_patch(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = response.parse() assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_patch(self, client: Hanzo) -> None: with client.langfuse.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = response.parse() assert_matches_type(object, langfuse, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_patch(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.langfuse.with_raw_response.patch( "", ) class TestAsyncLangfuse: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: langfuse = await async_client.langfuse.create( @@ -232,6 +240,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.langfuse.with_raw_response.create( @@ -239,33 +248,33 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = await response.parse() assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.langfuse.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = await response.parse() assert_matches_type(object, langfuse, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.langfuse.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: langfuse = await async_client.langfuse.retrieve( @@ -273,6 +282,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.langfuse.with_raw_response.retrieve( @@ -280,33 +290,33 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = await response.parse() assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.langfuse.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = await response.parse() assert_matches_type(object, langfuse, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.langfuse.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: langfuse = await async_client.langfuse.update( @@ -314,6 +324,7 @@ async def test_method_update(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.langfuse.with_raw_response.update( @@ -321,33 +332,33 @@ async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = await response.parse() assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.langfuse.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = await response.parse() assert_matches_type(object, langfuse, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.langfuse.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: langfuse = await async_client.langfuse.delete( @@ -355,6 +366,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.langfuse.with_raw_response.delete( @@ -362,33 +374,33 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = await response.parse() assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.langfuse.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = await response.parse() assert_matches_type(object, langfuse, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.langfuse.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_patch(self, async_client: AsyncHanzo) -> None: langfuse = await async_client.langfuse.patch( @@ -396,6 +408,7 @@ async def test_method_patch(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: response = await async_client.langfuse.with_raw_response.patch( @@ -403,29 +416,28 @@ async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = await response.parse() assert_matches_type(object, langfuse, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_patch(self, async_client: AsyncHanzo) -> None: async with async_client.langfuse.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" langfuse = await response.parse() assert_matches_type(object, langfuse, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_patch(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.langfuse.with_raw_response.patch( "", ) diff --git a/tests/api_resources/test_model.py b/tests/api_resources/test_model.py index 192a8d4b6..01e63a210 100644 --- a/tests/api_resources/test_model.py +++ b/tests/api_resources/test_model.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -17,19 +17,21 @@ class TestModel: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: model = client.model.create( - hanzo_params={"model": "model"}, + llm_params={"model": "model"}, model_info={"id": "id"}, model_name="model_name", ) assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: Hanzo) -> None: model = client.model.create( - hanzo_params={ + llm_params={ "model": "model", "api_base": "api_base", "api_key": "api_key", @@ -42,7 +44,7 @@ def test_method_create_with_all_params(self, client: Hanzo) -> None: "custom_llm_provider": "custom_llm_provider", "input_cost_per_second": 0, "input_cost_per_token": 0, - "hanzo_trace_id": "hanzo_trace_id", + "llm_trace_id": "llm_trace_id", "max_budget": 0, "max_file_size_mb": 0, "max_retries": 0, @@ -78,34 +80,37 @@ def test_method_create_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.model.with_raw_response.create( - hanzo_params={"model": "model"}, + llm_params={"model": "model"}, model_info={"id": "id"}, model_name="model_name", ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.model.with_streaming_response.create( - hanzo_params={"model": "model"}, + llm_params={"model": "model"}, model_info={"id": "id"}, model_name="model_name", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(object, model, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: model = client.model.delete( @@ -113,6 +118,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.model.with_raw_response.delete( @@ -120,17 +126,18 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.model.with_streaming_response.delete( id="id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(object, model, path=["response"]) @@ -139,21 +146,25 @@ def test_streaming_response_delete(self, client: Hanzo) -> None: class TestAsyncModel: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: model = await async_client.model.create( - hanzo_params={"model": "model"}, + llm_params={"model": "model"}, model_info={"id": "id"}, model_name="model_name", ) assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> None: model = await async_client.model.create( - hanzo_params={ + llm_params={ "model": "model", "api_base": "api_base", "api_key": "api_key", @@ -166,7 +177,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> "custom_llm_provider": "custom_llm_provider", "input_cost_per_second": 0, "input_cost_per_token": 0, - "hanzo_trace_id": "hanzo_trace_id", + "llm_trace_id": "llm_trace_id", "max_budget": 0, "max_file_size_mb": 0, "max_retries": 0, @@ -202,34 +213,37 @@ async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.model.with_raw_response.create( - hanzo_params={"model": "model"}, + llm_params={"model": "model"}, model_info={"id": "id"}, model_name="model_name", ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = await response.parse() assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.model.with_streaming_response.create( - hanzo_params={"model": "model"}, + llm_params={"model": "model"}, model_info={"id": "id"}, model_name="model_name", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = await response.parse() assert_matches_type(object, model, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: model = await async_client.model.delete( @@ -237,6 +251,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.model.with_raw_response.delete( @@ -244,17 +259,18 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = await response.parse() assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.model.with_streaming_response.delete( id="id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = await response.parse() assert_matches_type(object, model, path=["response"]) diff --git a/tests/api_resources/test_model_group.py b/tests/api_resources/test_model_group.py index b2427cc82..caca5e169 100644 --- a/tests/api_resources/test_model_group.py +++ b/tests/api_resources/test_model_group.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,11 +16,13 @@ class TestModelGroup: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_info(self, client: Hanzo) -> None: model_group = client.model_group.retrieve_info() assert_matches_type(object, model_group, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_info_with_all_params(self, client: Hanzo) -> None: model_group = client.model_group.retrieve_info( @@ -28,20 +30,22 @@ def test_method_retrieve_info_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, model_group, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve_info(self, client: Hanzo) -> None: response = client.model_group.with_raw_response.retrieve_info() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model_group = response.parse() assert_matches_type(object, model_group, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve_info(self, client: Hanzo) -> None: with client.model_group.with_streaming_response.retrieve_info() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model_group = response.parse() assert_matches_type(object, model_group, path=["response"]) @@ -50,13 +54,17 @@ def test_streaming_response_retrieve_info(self, client: Hanzo) -> None: class TestAsyncModelGroup: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_info(self, async_client: AsyncHanzo) -> None: model_group = await async_client.model_group.retrieve_info() assert_matches_type(object, model_group, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_info_with_all_params(self, async_client: AsyncHanzo) -> None: model_group = await async_client.model_group.retrieve_info( @@ -64,20 +72,22 @@ async def test_method_retrieve_info_with_all_params(self, async_client: AsyncHan ) assert_matches_type(object, model_group, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve_info(self, async_client: AsyncHanzo) -> None: response = await async_client.model_group.with_raw_response.retrieve_info() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model_group = await response.parse() assert_matches_type(object, model_group, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve_info(self, async_client: AsyncHanzo) -> None: async with async_client.model_group.with_streaming_response.retrieve_info() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model_group = await response.parse() assert_matches_type(object, model_group, path=["response"]) diff --git a/tests/api_resources/test_models.py b/tests/api_resources/test_models.py index 8fe108169..d1eef4735 100644 --- a/tests/api_resources/test_models.py +++ b/tests/api_resources/test_models.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,11 +16,13 @@ class TestModels: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: model = client.models.list() assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: Hanzo) -> None: model = client.models.list( @@ -29,20 +31,22 @@ def test_method_list_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.models.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.models.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(object, model, path=["response"]) @@ -51,13 +55,17 @@ def test_streaming_response_list(self, client: Hanzo) -> None: class TestAsyncModels: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: model = await async_client.models.list() assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> None: model = await async_client.models.list( @@ -66,20 +74,22 @@ async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> No ) assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.models.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = await response.parse() assert_matches_type(object, model, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.models.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = await response.parse() assert_matches_type(object, model, path=["response"]) diff --git a/tests/api_resources/test_moderations.py b/tests/api_resources/test_moderations.py index d798f016f..5b9b92056 100644 --- a/tests/api_resources/test_moderations.py +++ b/tests/api_resources/test_moderations.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,25 +16,28 @@ class TestModerations: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: moderation = client.moderations.create() assert_matches_type(object, moderation, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.moderations.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" moderation = response.parse() assert_matches_type(object, moderation, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.moderations.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" moderation = response.parse() assert_matches_type(object, moderation, path=["response"]) @@ -43,27 +46,32 @@ def test_streaming_response_create(self, client: Hanzo) -> None: class TestAsyncModerations: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: moderation = await async_client.moderations.create() assert_matches_type(object, moderation, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.moderations.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" moderation = await response.parse() assert_matches_type(object, moderation, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.moderations.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" moderation = await response.parse() assert_matches_type(object, moderation, path=["response"]) diff --git a/tests/api_resources/test_openai.py b/tests/api_resources/test_openai.py index fc752b08c..e24711247 100644 --- a/tests/api_resources/test_openai.py +++ b/tests/api_resources/test_openai.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestOpenAI: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: openai = client.openai.create( @@ -23,6 +24,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.openai.with_raw_response.create( @@ -30,33 +32,33 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = response.parse() assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.openai.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = response.parse() assert_matches_type(object, openai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.openai.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: openai = client.openai.retrieve( @@ -64,6 +66,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.openai.with_raw_response.retrieve( @@ -71,33 +74,33 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = response.parse() assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.openai.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = response.parse() assert_matches_type(object, openai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.openai.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: openai = client.openai.update( @@ -105,6 +108,7 @@ def test_method_update(self, client: Hanzo) -> None: ) assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.openai.with_raw_response.update( @@ -112,33 +116,33 @@ def test_raw_response_update(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = response.parse() assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.openai.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = response.parse() assert_matches_type(object, openai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.openai.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: openai = client.openai.delete( @@ -146,6 +150,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.openai.with_raw_response.delete( @@ -153,33 +158,33 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = response.parse() assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.openai.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = response.parse() assert_matches_type(object, openai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.openai.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_patch(self, client: Hanzo) -> None: openai = client.openai.patch( @@ -187,6 +192,7 @@ def test_method_patch(self, client: Hanzo) -> None: ) assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_patch(self, client: Hanzo) -> None: response = client.openai.with_raw_response.patch( @@ -194,37 +200,39 @@ def test_raw_response_patch(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = response.parse() assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_patch(self, client: Hanzo) -> None: with client.openai.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = response.parse() assert_matches_type(object, openai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_patch(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.openai.with_raw_response.patch( "", ) class TestAsyncOpenAI: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: openai = await async_client.openai.create( @@ -232,6 +240,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.openai.with_raw_response.create( @@ -239,33 +248,33 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = await response.parse() assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.openai.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = await response.parse() assert_matches_type(object, openai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.openai.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: openai = await async_client.openai.retrieve( @@ -273,6 +282,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.openai.with_raw_response.retrieve( @@ -280,33 +290,33 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = await response.parse() assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.openai.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = await response.parse() assert_matches_type(object, openai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.openai.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: openai = await async_client.openai.update( @@ -314,6 +324,7 @@ async def test_method_update(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.openai.with_raw_response.update( @@ -321,33 +332,33 @@ async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = await response.parse() assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.openai.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = await response.parse() assert_matches_type(object, openai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.openai.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: openai = await async_client.openai.delete( @@ -355,6 +366,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.openai.with_raw_response.delete( @@ -362,33 +374,33 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = await response.parse() assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.openai.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = await response.parse() assert_matches_type(object, openai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.openai.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_patch(self, async_client: AsyncHanzo) -> None: openai = await async_client.openai.patch( @@ -396,6 +408,7 @@ async def test_method_patch(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: response = await async_client.openai.with_raw_response.patch( @@ -403,29 +416,28 @@ async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = await response.parse() assert_matches_type(object, openai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_patch(self, async_client: AsyncHanzo) -> None: async with async_client.openai.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" openai = await response.parse() assert_matches_type(object, openai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_patch(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.openai.with_raw_response.patch( "", ) diff --git a/tests/api_resources/test_organization.py b/tests/api_resources/test_organization.py index 39060fa2f..8e6929bf8 100644 --- a/tests/api_resources/test_organization.py +++ b/tests/api_resources/test_organization.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -24,6 +24,7 @@ class TestOrganization: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: organization = client.organization.create( @@ -31,6 +32,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(OrganizationCreateResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: Hanzo) -> None: organization = client.organization.create( @@ -49,6 +51,7 @@ def test_method_create_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(OrganizationCreateResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.organization.with_raw_response.create( @@ -56,28 +59,31 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = response.parse() assert_matches_type(OrganizationCreateResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.organization.with_streaming_response.create( organization_alias="organization_alias", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = response.parse() assert_matches_type(OrganizationCreateResponse, organization, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: organization = client.organization.update() assert_matches_type(OrganizationUpdateResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: Hanzo) -> None: organization = client.organization.update( @@ -91,51 +97,57 @@ def test_method_update_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(OrganizationUpdateResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.organization.with_raw_response.update() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = response.parse() assert_matches_type(OrganizationUpdateResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.organization.with_streaming_response.update() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = response.parse() assert_matches_type(OrganizationUpdateResponse, organization, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: organization = client.organization.list() assert_matches_type(OrganizationListResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.organization.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = response.parse() assert_matches_type(OrganizationListResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.organization.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = response.parse() assert_matches_type(OrganizationListResponse, organization, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: organization = client.organization.delete( @@ -143,6 +155,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(OrganizationDeleteResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.organization.with_raw_response.delete( @@ -150,23 +163,25 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = response.parse() assert_matches_type(OrganizationDeleteResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.organization.with_streaming_response.delete( organization_ids=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = response.parse() assert_matches_type(OrganizationDeleteResponse, organization, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_add_member(self, client: Hanzo) -> None: organization = client.organization.add_member( @@ -175,6 +190,7 @@ def test_method_add_member(self, client: Hanzo) -> None: ) assert_matches_type(OrganizationAddMemberResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_add_member_with_all_params(self, client: Hanzo) -> None: organization = client.organization.add_member( @@ -190,6 +206,7 @@ def test_method_add_member_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(OrganizationAddMemberResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_add_member(self, client: Hanzo) -> None: response = client.organization.with_raw_response.add_member( @@ -198,10 +215,11 @@ def test_raw_response_add_member(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = response.parse() assert_matches_type(OrganizationAddMemberResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_add_member(self, client: Hanzo) -> None: with client.organization.with_streaming_response.add_member( @@ -209,13 +227,14 @@ def test_streaming_response_add_member(self, client: Hanzo) -> None: organization_id="organization_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = response.parse() assert_matches_type(OrganizationAddMemberResponse, organization, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete_member(self, client: Hanzo) -> None: organization = client.organization.delete_member( @@ -223,6 +242,7 @@ def test_method_delete_member(self, client: Hanzo) -> None: ) assert_matches_type(object, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete_member_with_all_params(self, client: Hanzo) -> None: organization = client.organization.delete_member( @@ -232,6 +252,7 @@ def test_method_delete_member_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete_member(self, client: Hanzo) -> None: response = client.organization.with_raw_response.delete_member( @@ -239,23 +260,25 @@ def test_raw_response_delete_member(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = response.parse() assert_matches_type(object, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete_member(self, client: Hanzo) -> None: with client.organization.with_streaming_response.delete_member( organization_id="organization_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = response.parse() assert_matches_type(object, organization, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_member(self, client: Hanzo) -> None: organization = client.organization.update_member( @@ -263,6 +286,7 @@ def test_method_update_member(self, client: Hanzo) -> None: ) assert_matches_type(OrganizationUpdateMemberResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_member_with_all_params(self, client: Hanzo) -> None: organization = client.organization.update_member( @@ -274,6 +298,7 @@ def test_method_update_member_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(OrganizationUpdateMemberResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update_member(self, client: Hanzo) -> None: response = client.organization.with_raw_response.update_member( @@ -281,17 +306,18 @@ def test_raw_response_update_member(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = response.parse() assert_matches_type(OrganizationUpdateMemberResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update_member(self, client: Hanzo) -> None: with client.organization.with_streaming_response.update_member( organization_id="organization_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = response.parse() assert_matches_type(OrganizationUpdateMemberResponse, organization, path=["response"]) @@ -300,8 +326,11 @@ def test_streaming_response_update_member(self, client: Hanzo) -> None: class TestAsyncOrganization: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: organization = await async_client.organization.create( @@ -309,6 +338,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(OrganizationCreateResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> None: organization = await async_client.organization.create( @@ -327,6 +357,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(OrganizationCreateResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.organization.with_raw_response.create( @@ -334,28 +365,31 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = await response.parse() assert_matches_type(OrganizationCreateResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.organization.with_streaming_response.create( organization_alias="organization_alias", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = await response.parse() assert_matches_type(OrganizationCreateResponse, organization, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: organization = await async_client.organization.update() assert_matches_type(OrganizationUpdateResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncHanzo) -> None: organization = await async_client.organization.update( @@ -369,51 +403,57 @@ async def test_method_update_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(OrganizationUpdateResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.organization.with_raw_response.update() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = await response.parse() assert_matches_type(OrganizationUpdateResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.organization.with_streaming_response.update() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = await response.parse() assert_matches_type(OrganizationUpdateResponse, organization, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: organization = await async_client.organization.list() assert_matches_type(OrganizationListResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.organization.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = await response.parse() assert_matches_type(OrganizationListResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.organization.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = await response.parse() assert_matches_type(OrganizationListResponse, organization, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: organization = await async_client.organization.delete( @@ -421,6 +461,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(OrganizationDeleteResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.organization.with_raw_response.delete( @@ -428,23 +469,25 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = await response.parse() assert_matches_type(OrganizationDeleteResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.organization.with_streaming_response.delete( organization_ids=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = await response.parse() assert_matches_type(OrganizationDeleteResponse, organization, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_add_member(self, async_client: AsyncHanzo) -> None: organization = await async_client.organization.add_member( @@ -453,6 +496,7 @@ async def test_method_add_member(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(OrganizationAddMemberResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_add_member_with_all_params(self, async_client: AsyncHanzo) -> None: organization = await async_client.organization.add_member( @@ -468,6 +512,7 @@ async def test_method_add_member_with_all_params(self, async_client: AsyncHanzo) ) assert_matches_type(OrganizationAddMemberResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_add_member(self, async_client: AsyncHanzo) -> None: response = await async_client.organization.with_raw_response.add_member( @@ -476,10 +521,11 @@ async def test_raw_response_add_member(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = await response.parse() assert_matches_type(OrganizationAddMemberResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_add_member(self, async_client: AsyncHanzo) -> None: async with async_client.organization.with_streaming_response.add_member( @@ -487,13 +533,14 @@ async def test_streaming_response_add_member(self, async_client: AsyncHanzo) -> organization_id="organization_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = await response.parse() assert_matches_type(OrganizationAddMemberResponse, organization, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete_member(self, async_client: AsyncHanzo) -> None: organization = await async_client.organization.delete_member( @@ -501,6 +548,7 @@ async def test_method_delete_member(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete_member_with_all_params(self, async_client: AsyncHanzo) -> None: organization = await async_client.organization.delete_member( @@ -510,6 +558,7 @@ async def test_method_delete_member_with_all_params(self, async_client: AsyncHan ) assert_matches_type(object, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete_member(self, async_client: AsyncHanzo) -> None: response = await async_client.organization.with_raw_response.delete_member( @@ -517,23 +566,25 @@ async def test_raw_response_delete_member(self, async_client: AsyncHanzo) -> Non ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = await response.parse() assert_matches_type(object, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete_member(self, async_client: AsyncHanzo) -> None: async with async_client.organization.with_streaming_response.delete_member( organization_id="organization_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = await response.parse() assert_matches_type(object, organization, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_member(self, async_client: AsyncHanzo) -> None: organization = await async_client.organization.update_member( @@ -541,6 +592,7 @@ async def test_method_update_member(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(OrganizationUpdateMemberResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_member_with_all_params(self, async_client: AsyncHanzo) -> None: organization = await async_client.organization.update_member( @@ -552,6 +604,7 @@ async def test_method_update_member_with_all_params(self, async_client: AsyncHan ) assert_matches_type(OrganizationUpdateMemberResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update_member(self, async_client: AsyncHanzo) -> None: response = await async_client.organization.with_raw_response.update_member( @@ -559,17 +612,18 @@ async def test_raw_response_update_member(self, async_client: AsyncHanzo) -> Non ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = await response.parse() assert_matches_type(OrganizationUpdateMemberResponse, organization, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update_member(self, async_client: AsyncHanzo) -> None: async with async_client.organization.with_streaming_response.update_member( organization_id="organization_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" organization = await response.parse() assert_matches_type(OrganizationUpdateMemberResponse, organization, path=["response"]) diff --git a/tests/api_resources/test_provider.py b/tests/api_resources/test_provider.py index f0d023221..65a0bf635 100644 --- a/tests/api_resources/test_provider.py +++ b/tests/api_resources/test_provider.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -17,25 +17,28 @@ class TestProvider: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_budgets(self, client: Hanzo) -> None: provider = client.provider.list_budgets() assert_matches_type(ProviderListBudgetsResponse, provider, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list_budgets(self, client: Hanzo) -> None: response = client.provider.with_raw_response.list_budgets() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" provider = response.parse() assert_matches_type(ProviderListBudgetsResponse, provider, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list_budgets(self, client: Hanzo) -> None: with client.provider.with_streaming_response.list_budgets() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" provider = response.parse() assert_matches_type(ProviderListBudgetsResponse, provider, path=["response"]) @@ -44,27 +47,32 @@ def test_streaming_response_list_budgets(self, client: Hanzo) -> None: class TestAsyncProvider: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_budgets(self, async_client: AsyncHanzo) -> None: provider = await async_client.provider.list_budgets() assert_matches_type(ProviderListBudgetsResponse, provider, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list_budgets(self, async_client: AsyncHanzo) -> None: response = await async_client.provider.with_raw_response.list_budgets() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" provider = await response.parse() assert_matches_type(ProviderListBudgetsResponse, provider, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list_budgets(self, async_client: AsyncHanzo) -> None: async with async_client.provider.with_streaming_response.list_budgets() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" provider = await response.parse() assert_matches_type(ProviderListBudgetsResponse, provider, path=["response"]) diff --git a/tests/api_resources/test_rerank.py b/tests/api_resources/test_rerank.py index e862e4090..891af3cbe 100644 --- a/tests/api_resources/test_rerank.py +++ b/tests/api_resources/test_rerank.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,75 +16,84 @@ class TestRerank: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: rerank = client.rerank.create() assert_matches_type(object, rerank, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.rerank.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" rerank = response.parse() assert_matches_type(object, rerank, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.rerank.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" rerank = response.parse() assert_matches_type(object, rerank, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_v1(self, client: Hanzo) -> None: rerank = client.rerank.create_v1() assert_matches_type(object, rerank, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_v1(self, client: Hanzo) -> None: response = client.rerank.with_raw_response.create_v1() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" rerank = response.parse() assert_matches_type(object, rerank, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_v1(self, client: Hanzo) -> None: with client.rerank.with_streaming_response.create_v1() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" rerank = response.parse() assert_matches_type(object, rerank, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_v2(self, client: Hanzo) -> None: rerank = client.rerank.create_v2() assert_matches_type(object, rerank, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_v2(self, client: Hanzo) -> None: response = client.rerank.with_raw_response.create_v2() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" rerank = response.parse() assert_matches_type(object, rerank, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_v2(self, client: Hanzo) -> None: with client.rerank.with_streaming_response.create_v2() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" rerank = response.parse() assert_matches_type(object, rerank, path=["response"]) @@ -93,77 +102,88 @@ def test_streaming_response_create_v2(self, client: Hanzo) -> None: class TestAsyncRerank: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: rerank = await async_client.rerank.create() assert_matches_type(object, rerank, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.rerank.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" rerank = await response.parse() assert_matches_type(object, rerank, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.rerank.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" rerank = await response.parse() assert_matches_type(object, rerank, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_v1(self, async_client: AsyncHanzo) -> None: rerank = await async_client.rerank.create_v1() assert_matches_type(object, rerank, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_v1(self, async_client: AsyncHanzo) -> None: response = await async_client.rerank.with_raw_response.create_v1() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" rerank = await response.parse() assert_matches_type(object, rerank, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_v1(self, async_client: AsyncHanzo) -> None: async with async_client.rerank.with_streaming_response.create_v1() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" rerank = await response.parse() assert_matches_type(object, rerank, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_v2(self, async_client: AsyncHanzo) -> None: rerank = await async_client.rerank.create_v2() assert_matches_type(object, rerank, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_v2(self, async_client: AsyncHanzo) -> None: response = await async_client.rerank.with_raw_response.create_v2() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" rerank = await response.parse() assert_matches_type(object, rerank, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_v2(self, async_client: AsyncHanzo) -> None: async with async_client.rerank.with_streaming_response.create_v2() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" rerank = await response.parse() assert_matches_type(object, rerank, path=["response"]) diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index f9264f40f..e297b637a 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,31 +16,35 @@ class TestResponses: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: response = client.responses.create() assert_matches_type(object, response, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: http_response = client.responses.with_raw_response.create() assert http_response.is_closed is True - assert http_response.http_request.headers.get("X-SDK-Lang") == "python" + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" response = http_response.parse() assert_matches_type(object, response, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.responses.with_streaming_response.create() as http_response: assert not http_response.is_closed - assert http_response.http_request.headers.get("X-SDK-Lang") == "python" + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" response = http_response.parse() assert_matches_type(object, response, path=["response"]) assert cast(Any, http_response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: response = client.responses.retrieve( @@ -48,6 +52,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(object, response, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: http_response = client.responses.with_raw_response.retrieve( @@ -55,33 +60,33 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert http_response.is_closed is True - assert http_response.http_request.headers.get("X-SDK-Lang") == "python" + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" response = http_response.parse() assert_matches_type(object, response, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.responses.with_streaming_response.retrieve( "response_id", ) as http_response: assert not http_response.is_closed - assert http_response.http_request.headers.get("X-SDK-Lang") == "python" + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" response = http_response.parse() assert_matches_type(object, response, path=["response"]) assert cast(Any, http_response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `response_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): client.responses.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: response = client.responses.delete( @@ -89,6 +94,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, response, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: http_response = client.responses.with_raw_response.delete( @@ -96,62 +102,67 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert http_response.is_closed is True - assert http_response.http_request.headers.get("X-SDK-Lang") == "python" + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" response = http_response.parse() assert_matches_type(object, response, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.responses.with_streaming_response.delete( "response_id", ) as http_response: assert not http_response.is_closed - assert http_response.http_request.headers.get("X-SDK-Lang") == "python" + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" response = http_response.parse() assert_matches_type(object, response, path=["response"]) assert cast(Any, http_response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `response_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): client.responses.with_raw_response.delete( "", ) class TestAsyncResponses: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: response = await async_client.responses.create() assert_matches_type(object, response, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: http_response = await async_client.responses.with_raw_response.create() assert http_response.is_closed is True - assert http_response.http_request.headers.get("X-SDK-Lang") == "python" + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" response = await http_response.parse() assert_matches_type(object, response, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.responses.with_streaming_response.create() as http_response: assert not http_response.is_closed - assert http_response.http_request.headers.get("X-SDK-Lang") == "python" + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" response = await http_response.parse() assert_matches_type(object, response, path=["response"]) assert cast(Any, http_response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.responses.retrieve( @@ -159,6 +170,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, response, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: http_response = await async_client.responses.with_raw_response.retrieve( @@ -166,33 +178,33 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert http_response.is_closed is True - assert http_response.http_request.headers.get("X-SDK-Lang") == "python" + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" response = await http_response.parse() assert_matches_type(object, response, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.responses.with_streaming_response.retrieve( "response_id", ) as http_response: assert not http_response.is_closed - assert http_response.http_request.headers.get("X-SDK-Lang") == "python" + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" response = await http_response.parse() assert_matches_type(object, response, path=["response"]) assert cast(Any, http_response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `response_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): await async_client.responses.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.responses.delete( @@ -200,6 +212,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, response, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: http_response = await async_client.responses.with_raw_response.delete( @@ -207,29 +220,28 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert http_response.is_closed is True - assert http_response.http_request.headers.get("X-SDK-Lang") == "python" + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" response = await http_response.parse() assert_matches_type(object, response, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.responses.with_streaming_response.delete( "response_id", ) as http_response: assert not http_response.is_closed - assert http_response.http_request.headers.get("X-SDK-Lang") == "python" + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" response = await http_response.parse() assert_matches_type(object, response, path=["response"]) assert cast(Any, http_response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `response_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): await async_client.responses.with_raw_response.delete( "", ) diff --git a/tests/api_resources/test_routes.py b/tests/api_resources/test_routes.py index 943a2e8a9..dadbdab95 100644 --- a/tests/api_resources/test_routes.py +++ b/tests/api_resources/test_routes.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,25 +16,28 @@ class TestRoutes: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: route = client.routes.list() assert_matches_type(object, route, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.routes.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" route = response.parse() assert_matches_type(object, route, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.routes.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" route = response.parse() assert_matches_type(object, route, path=["response"]) @@ -43,27 +46,32 @@ def test_streaming_response_list(self, client: Hanzo) -> None: class TestAsyncRoutes: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: route = await async_client.routes.list() assert_matches_type(object, route, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.routes.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" route = await response.parse() assert_matches_type(object, route, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.routes.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" route = await response.parse() assert_matches_type(object, route, path=["response"]) diff --git a/tests/api_resources/test_settings.py b/tests/api_resources/test_settings.py index 38ddc2aef..cfe2ecd58 100644 --- a/tests/api_resources/test_settings.py +++ b/tests/api_resources/test_settings.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,25 +16,28 @@ class TestSettings: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: setting = client.settings.retrieve() assert_matches_type(object, setting, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.settings.with_raw_response.retrieve() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" setting = response.parse() assert_matches_type(object, setting, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.settings.with_streaming_response.retrieve() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" setting = response.parse() assert_matches_type(object, setting, path=["response"]) @@ -43,27 +46,32 @@ def test_streaming_response_retrieve(self, client: Hanzo) -> None: class TestAsyncSettings: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: setting = await async_client.settings.retrieve() assert_matches_type(object, setting, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.settings.with_raw_response.retrieve() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" setting = await response.parse() assert_matches_type(object, setting, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.settings.with_streaming_response.retrieve() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" setting = await response.parse() assert_matches_type(object, setting, path=["response"]) diff --git a/tests/api_resources/test_spend.py b/tests/api_resources/test_spend.py index 7289682c0..44669503f 100644 --- a/tests/api_resources/test_spend.py +++ b/tests/api_resources/test_spend.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -20,11 +20,13 @@ class TestSpend: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_calculate_spend(self, client: Hanzo) -> None: spend = client.spend.calculate_spend() assert_matches_type(object, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_calculate_spend_with_all_params(self, client: Hanzo) -> None: spend = client.spend.calculate_spend( @@ -34,31 +36,35 @@ def test_method_calculate_spend_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_calculate_spend(self, client: Hanzo) -> None: response = client.spend.with_raw_response.calculate_spend() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = response.parse() assert_matches_type(object, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_calculate_spend(self, client: Hanzo) -> None: with client.spend.with_streaming_response.calculate_spend() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = response.parse() assert_matches_type(object, spend, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_logs(self, client: Hanzo) -> None: spend = client.spend.list_logs() assert_matches_type(SpendListLogsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_logs_with_all_params(self, client: Hanzo) -> None: spend = client.spend.list_logs( @@ -70,31 +76,35 @@ def test_method_list_logs_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(SpendListLogsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list_logs(self, client: Hanzo) -> None: response = client.spend.with_raw_response.list_logs() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = response.parse() assert_matches_type(SpendListLogsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list_logs(self, client: Hanzo) -> None: with client.spend.with_streaming_response.list_logs() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = response.parse() assert_matches_type(SpendListLogsResponse, spend, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_tags(self, client: Hanzo) -> None: spend = client.spend.list_tags() assert_matches_type(SpendListTagsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_tags_with_all_params(self, client: Hanzo) -> None: spend = client.spend.list_tags( @@ -103,20 +113,22 @@ def test_method_list_tags_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(SpendListTagsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list_tags(self, client: Hanzo) -> None: response = client.spend.with_raw_response.list_tags() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = response.parse() assert_matches_type(SpendListTagsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list_tags(self, client: Hanzo) -> None: with client.spend.with_streaming_response.list_tags() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = response.parse() assert_matches_type(SpendListTagsResponse, spend, path=["response"]) @@ -125,13 +137,17 @@ def test_streaming_response_list_tags(self, client: Hanzo) -> None: class TestAsyncSpend: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_calculate_spend(self, async_client: AsyncHanzo) -> None: spend = await async_client.spend.calculate_spend() assert_matches_type(object, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_calculate_spend_with_all_params(self, async_client: AsyncHanzo) -> None: spend = await async_client.spend.calculate_spend( @@ -141,31 +157,35 @@ async def test_method_calculate_spend_with_all_params(self, async_client: AsyncH ) assert_matches_type(object, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_calculate_spend(self, async_client: AsyncHanzo) -> None: response = await async_client.spend.with_raw_response.calculate_spend() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = await response.parse() assert_matches_type(object, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_calculate_spend(self, async_client: AsyncHanzo) -> None: async with async_client.spend.with_streaming_response.calculate_spend() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = await response.parse() assert_matches_type(object, spend, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_logs(self, async_client: AsyncHanzo) -> None: spend = await async_client.spend.list_logs() assert_matches_type(SpendListLogsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_logs_with_all_params(self, async_client: AsyncHanzo) -> None: spend = await async_client.spend.list_logs( @@ -177,31 +197,35 @@ async def test_method_list_logs_with_all_params(self, async_client: AsyncHanzo) ) assert_matches_type(SpendListLogsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list_logs(self, async_client: AsyncHanzo) -> None: response = await async_client.spend.with_raw_response.list_logs() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = await response.parse() assert_matches_type(SpendListLogsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list_logs(self, async_client: AsyncHanzo) -> None: async with async_client.spend.with_streaming_response.list_logs() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = await response.parse() assert_matches_type(SpendListLogsResponse, spend, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_tags(self, async_client: AsyncHanzo) -> None: spend = await async_client.spend.list_tags() assert_matches_type(SpendListTagsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_tags_with_all_params(self, async_client: AsyncHanzo) -> None: spend = await async_client.spend.list_tags( @@ -210,20 +234,22 @@ async def test_method_list_tags_with_all_params(self, async_client: AsyncHanzo) ) assert_matches_type(SpendListTagsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list_tags(self, async_client: AsyncHanzo) -> None: response = await async_client.spend.with_raw_response.list_tags() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = await response.parse() assert_matches_type(SpendListTagsResponse, spend, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list_tags(self, async_client: AsyncHanzo) -> None: async with async_client.spend.with_streaming_response.list_tags() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" spend = await response.parse() assert_matches_type(SpendListTagsResponse, spend, path=["response"]) diff --git a/tests/api_resources/test_team.py b/tests/api_resources/test_team.py index f3629c77c..50618bdd9 100644 --- a/tests/api_resources/test_team.py +++ b/tests/api_resources/test_team.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -10,7 +10,7 @@ from hanzoai import Hanzo, AsyncHanzo from tests.utils import assert_matches_type from hanzoai.types import ( - HanzoTeamTable, + TeamCreateResponse, TeamAddMemberResponse, TeamUpdateMemberResponse, ) @@ -21,11 +21,13 @@ class TestTeam: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: team = client.team.create() - assert_matches_type(HanzoTeamTable, team, path=["response"]) + assert_matches_type(TeamCreateResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: Hanzo) -> None: team = client.team.create( @@ -51,30 +53,33 @@ def test_method_create_with_all_params(self, client: Hanzo) -> None: team_alias="team_alias", team_id="team_id", tpm_limit=0, - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) - assert_matches_type(HanzoTeamTable, team, path=["response"]) + assert_matches_type(TeamCreateResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.team.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() - assert_matches_type(HanzoTeamTable, team, path=["response"]) + assert_matches_type(TeamCreateResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.team.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() - assert_matches_type(HanzoTeamTable, team, path=["response"]) + assert_matches_type(TeamCreateResponse, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: team = client.team.update( @@ -82,6 +87,7 @@ def test_method_update(self, client: Hanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: Hanzo) -> None: team = client.team.update( @@ -98,10 +104,11 @@ def test_method_update_with_all_params(self, client: Hanzo) -> None: tags=[{}], team_alias="team_alias", tpm_limit=0, - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.team.with_raw_response.update( @@ -109,28 +116,31 @@ def test_raw_response_update(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.team.with_streaming_response.update( team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: team = client.team.list() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: Hanzo) -> None: team = client.team.list( @@ -139,26 +149,29 @@ def test_method_list_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.team.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.team.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: team = client.team.delete( @@ -166,14 +179,16 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete_with_all_params(self, client: Hanzo) -> None: team = client.team.delete( team_ids=["string"], - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.team.with_raw_response.delete( @@ -181,23 +196,25 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.team.with_streaming_response.delete( team_ids=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_add_member(self, client: Hanzo) -> None: team = client.team.add_member( @@ -206,6 +223,7 @@ def test_method_add_member(self, client: Hanzo) -> None: ) assert_matches_type(TeamAddMemberResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_add_member_with_all_params(self, client: Hanzo) -> None: team = client.team.add_member( @@ -221,6 +239,7 @@ def test_method_add_member_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(TeamAddMemberResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_add_member(self, client: Hanzo) -> None: response = client.team.with_raw_response.add_member( @@ -229,10 +248,11 @@ def test_raw_response_add_member(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(TeamAddMemberResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_add_member(self, client: Hanzo) -> None: with client.team.with_streaming_response.add_member( @@ -240,13 +260,14 @@ def test_streaming_response_add_member(self, client: Hanzo) -> None: team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(TeamAddMemberResponse, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_block(self, client: Hanzo) -> None: team = client.team.block( @@ -254,6 +275,7 @@ def test_method_block(self, client: Hanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_block(self, client: Hanzo) -> None: response = client.team.with_raw_response.block( @@ -261,23 +283,25 @@ def test_raw_response_block(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_block(self, client: Hanzo) -> None: with client.team.with_streaming_response.block( team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_disable_logging(self, client: Hanzo) -> None: team = client.team.disable_logging( @@ -285,6 +309,7 @@ def test_method_disable_logging(self, client: Hanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_disable_logging(self, client: Hanzo) -> None: response = client.team.with_raw_response.disable_logging( @@ -292,38 +317,39 @@ def test_raw_response_disable_logging(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_disable_logging(self, client: Hanzo) -> None: with client.team.with_streaming_response.disable_logging( "team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_disable_logging(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `team_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `team_id` but received ''"): client.team.with_raw_response.disable_logging( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_available(self, client: Hanzo) -> None: team = client.team.list_available() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_available_with_all_params(self, client: Hanzo) -> None: team = client.team.list_available( @@ -331,26 +357,29 @@ def test_method_list_available_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list_available(self, client: Hanzo) -> None: response = client.team.with_raw_response.list_available() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list_available(self, client: Hanzo) -> None: with client.team.with_streaming_response.list_available() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_remove_member(self, client: Hanzo) -> None: team = client.team.remove_member( @@ -358,6 +387,7 @@ def test_method_remove_member(self, client: Hanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_remove_member_with_all_params(self, client: Hanzo) -> None: team = client.team.remove_member( @@ -367,6 +397,7 @@ def test_method_remove_member_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_remove_member(self, client: Hanzo) -> None: response = client.team.with_raw_response.remove_member( @@ -374,28 +405,31 @@ def test_raw_response_remove_member(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_remove_member(self, client: Hanzo) -> None: with client.team.with_streaming_response.remove_member( team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_info(self, client: Hanzo) -> None: team = client.team.retrieve_info() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_info_with_all_params(self, client: Hanzo) -> None: team = client.team.retrieve_info( @@ -403,26 +437,29 @@ def test_method_retrieve_info_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve_info(self, client: Hanzo) -> None: response = client.team.with_raw_response.retrieve_info() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve_info(self, client: Hanzo) -> None: with client.team.with_streaming_response.retrieve_info() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_unblock(self, client: Hanzo) -> None: team = client.team.unblock( @@ -430,6 +467,7 @@ def test_method_unblock(self, client: Hanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_unblock(self, client: Hanzo) -> None: response = client.team.with_raw_response.unblock( @@ -437,23 +475,25 @@ def test_raw_response_unblock(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_unblock(self, client: Hanzo) -> None: with client.team.with_streaming_response.unblock( team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_member(self, client: Hanzo) -> None: team = client.team.update_member( @@ -461,6 +501,7 @@ def test_method_update_member(self, client: Hanzo) -> None: ) assert_matches_type(TeamUpdateMemberResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_member_with_all_params(self, client: Hanzo) -> None: team = client.team.update_member( @@ -472,6 +513,7 @@ def test_method_update_member_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(TeamUpdateMemberResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update_member(self, client: Hanzo) -> None: response = client.team.with_raw_response.update_member( @@ -479,17 +521,18 @@ def test_raw_response_update_member(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(TeamUpdateMemberResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update_member(self, client: Hanzo) -> None: with client.team.with_streaming_response.update_member( team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = response.parse() assert_matches_type(TeamUpdateMemberResponse, team, path=["response"]) @@ -498,13 +541,17 @@ def test_streaming_response_update_member(self, client: Hanzo) -> None: class TestAsyncTeam: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: team = await async_client.team.create() - assert_matches_type(HanzoTeamTable, team, path=["response"]) + assert_matches_type(TeamCreateResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> None: team = await async_client.team.create( @@ -530,30 +577,33 @@ async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> team_alias="team_alias", team_id="team_id", tpm_limit=0, - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) - assert_matches_type(HanzoTeamTable, team, path=["response"]) + assert_matches_type(TeamCreateResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.team.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() - assert_matches_type(HanzoTeamTable, team, path=["response"]) + assert_matches_type(TeamCreateResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.team.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() - assert_matches_type(HanzoTeamTable, team, path=["response"]) + assert_matches_type(TeamCreateResponse, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: team = await async_client.team.update( @@ -561,6 +611,7 @@ async def test_method_update(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncHanzo) -> None: team = await async_client.team.update( @@ -577,10 +628,11 @@ async def test_method_update_with_all_params(self, async_client: AsyncHanzo) -> tags=[{}], team_alias="team_alias", tpm_limit=0, - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.team.with_raw_response.update( @@ -588,28 +640,31 @@ async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.team.with_streaming_response.update( team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: team = await async_client.team.list() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> None: team = await async_client.team.list( @@ -618,26 +673,29 @@ async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> No ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.team.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.team.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: team = await async_client.team.delete( @@ -645,14 +703,16 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete_with_all_params(self, async_client: AsyncHanzo) -> None: team = await async_client.team.delete( team_ids=["string"], - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.team.with_raw_response.delete( @@ -660,23 +720,25 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.team.with_streaming_response.delete( team_ids=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_add_member(self, async_client: AsyncHanzo) -> None: team = await async_client.team.add_member( @@ -685,6 +747,7 @@ async def test_method_add_member(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(TeamAddMemberResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_add_member_with_all_params(self, async_client: AsyncHanzo) -> None: team = await async_client.team.add_member( @@ -700,6 +763,7 @@ async def test_method_add_member_with_all_params(self, async_client: AsyncHanzo) ) assert_matches_type(TeamAddMemberResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_add_member(self, async_client: AsyncHanzo) -> None: response = await async_client.team.with_raw_response.add_member( @@ -708,10 +772,11 @@ async def test_raw_response_add_member(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(TeamAddMemberResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_add_member(self, async_client: AsyncHanzo) -> None: async with async_client.team.with_streaming_response.add_member( @@ -719,13 +784,14 @@ async def test_streaming_response_add_member(self, async_client: AsyncHanzo) -> team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(TeamAddMemberResponse, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_block(self, async_client: AsyncHanzo) -> None: team = await async_client.team.block( @@ -733,6 +799,7 @@ async def test_method_block(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_block(self, async_client: AsyncHanzo) -> None: response = await async_client.team.with_raw_response.block( @@ -740,23 +807,25 @@ async def test_raw_response_block(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_block(self, async_client: AsyncHanzo) -> None: async with async_client.team.with_streaming_response.block( team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_disable_logging(self, async_client: AsyncHanzo) -> None: team = await async_client.team.disable_logging( @@ -764,6 +833,7 @@ async def test_method_disable_logging(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_disable_logging(self, async_client: AsyncHanzo) -> None: response = await async_client.team.with_raw_response.disable_logging( @@ -771,38 +841,39 @@ async def test_raw_response_disable_logging(self, async_client: AsyncHanzo) -> N ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_disable_logging(self, async_client: AsyncHanzo) -> None: async with async_client.team.with_streaming_response.disable_logging( "team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_disable_logging(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `team_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `team_id` but received ''"): await async_client.team.with_raw_response.disable_logging( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_available(self, async_client: AsyncHanzo) -> None: team = await async_client.team.list_available() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_available_with_all_params(self, async_client: AsyncHanzo) -> None: team = await async_client.team.list_available( @@ -810,26 +881,29 @@ async def test_method_list_available_with_all_params(self, async_client: AsyncHa ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list_available(self, async_client: AsyncHanzo) -> None: response = await async_client.team.with_raw_response.list_available() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list_available(self, async_client: AsyncHanzo) -> None: async with async_client.team.with_streaming_response.list_available() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_remove_member(self, async_client: AsyncHanzo) -> None: team = await async_client.team.remove_member( @@ -837,6 +911,7 @@ async def test_method_remove_member(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_remove_member_with_all_params(self, async_client: AsyncHanzo) -> None: team = await async_client.team.remove_member( @@ -846,6 +921,7 @@ async def test_method_remove_member_with_all_params(self, async_client: AsyncHan ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_remove_member(self, async_client: AsyncHanzo) -> None: response = await async_client.team.with_raw_response.remove_member( @@ -853,28 +929,31 @@ async def test_raw_response_remove_member(self, async_client: AsyncHanzo) -> Non ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_remove_member(self, async_client: AsyncHanzo) -> None: async with async_client.team.with_streaming_response.remove_member( team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_info(self, async_client: AsyncHanzo) -> None: team = await async_client.team.retrieve_info() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_info_with_all_params(self, async_client: AsyncHanzo) -> None: team = await async_client.team.retrieve_info( @@ -882,26 +961,29 @@ async def test_method_retrieve_info_with_all_params(self, async_client: AsyncHan ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve_info(self, async_client: AsyncHanzo) -> None: response = await async_client.team.with_raw_response.retrieve_info() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve_info(self, async_client: AsyncHanzo) -> None: async with async_client.team.with_streaming_response.retrieve_info() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_unblock(self, async_client: AsyncHanzo) -> None: team = await async_client.team.unblock( @@ -909,6 +991,7 @@ async def test_method_unblock(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_unblock(self, async_client: AsyncHanzo) -> None: response = await async_client.team.with_raw_response.unblock( @@ -916,23 +999,25 @@ async def test_raw_response_unblock(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_unblock(self, async_client: AsyncHanzo) -> None: async with async_client.team.with_streaming_response.unblock( team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(object, team, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_member(self, async_client: AsyncHanzo) -> None: team = await async_client.team.update_member( @@ -940,6 +1025,7 @@ async def test_method_update_member(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(TeamUpdateMemberResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_member_with_all_params(self, async_client: AsyncHanzo) -> None: team = await async_client.team.update_member( @@ -951,6 +1037,7 @@ async def test_method_update_member_with_all_params(self, async_client: AsyncHan ) assert_matches_type(TeamUpdateMemberResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update_member(self, async_client: AsyncHanzo) -> None: response = await async_client.team.with_raw_response.update_member( @@ -958,17 +1045,18 @@ async def test_raw_response_update_member(self, async_client: AsyncHanzo) -> Non ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(TeamUpdateMemberResponse, team, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update_member(self, async_client: AsyncHanzo) -> None: async with async_client.team.with_streaming_response.update_member( team_id="team_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" team = await response.parse() assert_matches_type(TeamUpdateMemberResponse, team, path=["response"]) diff --git a/tests/api_resources/test_test.py b/tests/api_resources/test_test.py index 724fbeb70..77494cb23 100644 --- a/tests/api_resources/test_test.py +++ b/tests/api_resources/test_test.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,25 +16,28 @@ class TestTest: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_ping(self, client: Hanzo) -> None: test = client.test.ping() assert_matches_type(object, test, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_ping(self, client: Hanzo) -> None: response = client.test.with_raw_response.ping() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" test = response.parse() assert_matches_type(object, test, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_ping(self, client: Hanzo) -> None: with client.test.with_streaming_response.ping() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" test = response.parse() assert_matches_type(object, test, path=["response"]) @@ -43,27 +46,32 @@ def test_streaming_response_ping(self, client: Hanzo) -> None: class TestAsyncTest: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_ping(self, async_client: AsyncHanzo) -> None: test = await async_client.test.ping() assert_matches_type(object, test, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_ping(self, async_client: AsyncHanzo) -> None: response = await async_client.test.with_raw_response.ping() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" test = await response.parse() assert_matches_type(object, test, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_ping(self, async_client: AsyncHanzo) -> None: async with async_client.test.with_streaming_response.ping() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" test = await response.parse() assert_matches_type(object, test, path=["response"]) diff --git a/tests/api_resources/test_threads.py b/tests/api_resources/test_threads.py index 0958ba1b6..1f312cb1e 100644 --- a/tests/api_resources/test_threads.py +++ b/tests/api_resources/test_threads.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,31 +16,35 @@ class TestThreads: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: thread = client.threads.create() assert_matches_type(object, thread, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.threads.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(object, thread, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.threads.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(object, thread, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: thread = client.threads.retrieve( @@ -48,6 +52,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(object, thread, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.threads.with_raw_response.retrieve( @@ -55,62 +60,67 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(object, thread, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.threads.with_streaming_response.retrieve( "thread_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(object, thread, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `thread_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): client.threads.with_raw_response.retrieve( "", ) class TestAsyncThreads: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: thread = await async_client.threads.create() assert_matches_type(object, thread, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.threads.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = await response.parse() assert_matches_type(object, thread, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.threads.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = await response.parse() assert_matches_type(object, thread, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: thread = await async_client.threads.retrieve( @@ -118,6 +128,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, thread, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.threads.with_raw_response.retrieve( @@ -125,29 +136,28 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = await response.parse() assert_matches_type(object, thread, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.threads.with_streaming_response.retrieve( "thread_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = await response.parse() assert_matches_type(object, thread, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `thread_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): await async_client.threads.with_raw_response.retrieve( "", ) diff --git a/tests/api_resources/test_user.py b/tests/api_resources/test_user.py index 8bf92f0c3..b96950bf2 100644 --- a/tests/api_resources/test_user.py +++ b/tests/api_resources/test_user.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -19,11 +19,13 @@ class TestUser: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: user = client.user.create() assert_matches_type(UserCreateResponse, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: Hanzo) -> None: user = client.user.create( @@ -57,31 +59,35 @@ def test_method_create_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(UserCreateResponse, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.user.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() assert_matches_type(UserCreateResponse, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.user.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() assert_matches_type(UserCreateResponse, user, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: user = client.user.update() assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: Hanzo) -> None: user = client.user.update( @@ -112,31 +118,35 @@ def test_method_update_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.user.with_raw_response.update() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.user.with_streaming_response.update() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() assert_matches_type(object, user, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: user = client.user.list() assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: Hanzo) -> None: user = client.user.list( @@ -147,26 +157,29 @@ def test_method_list_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.user.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.user.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() assert_matches_type(object, user, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: user = client.user.delete( @@ -174,14 +187,16 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete_with_all_params(self, client: Hanzo) -> None: user = client.user.delete( user_ids=["string"], - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.user.with_raw_response.delete( @@ -189,28 +204,31 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.user.with_streaming_response.delete( user_ids=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() assert_matches_type(object, user, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_info(self, client: Hanzo) -> None: user = client.user.retrieve_info() assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_info_with_all_params(self, client: Hanzo) -> None: user = client.user.retrieve_info( @@ -218,20 +236,22 @@ def test_method_retrieve_info_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve_info(self, client: Hanzo) -> None: response = client.user.with_raw_response.retrieve_info() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve_info(self, client: Hanzo) -> None: with client.user.with_streaming_response.retrieve_info() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() assert_matches_type(object, user, path=["response"]) @@ -240,13 +260,17 @@ def test_streaming_response_retrieve_info(self, client: Hanzo) -> None: class TestAsyncUser: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: user = await async_client.user.create() assert_matches_type(UserCreateResponse, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> None: user = await async_client.user.create( @@ -280,31 +304,35 @@ async def test_method_create_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(UserCreateResponse, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.user.with_raw_response.create() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = await response.parse() assert_matches_type(UserCreateResponse, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.user.with_streaming_response.create() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = await response.parse() assert_matches_type(UserCreateResponse, user, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: user = await async_client.user.update() assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncHanzo) -> None: user = await async_client.user.update( @@ -335,31 +363,35 @@ async def test_method_update_with_all_params(self, async_client: AsyncHanzo) -> ) assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.user.with_raw_response.update() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = await response.parse() assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.user.with_streaming_response.update() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = await response.parse() assert_matches_type(object, user, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: user = await async_client.user.list() assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> None: user = await async_client.user.list( @@ -370,26 +402,29 @@ async def test_method_list_with_all_params(self, async_client: AsyncHanzo) -> No ) assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.user.with_raw_response.list() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = await response.parse() assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.user.with_streaming_response.list() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = await response.parse() assert_matches_type(object, user, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: user = await async_client.user.delete( @@ -397,14 +432,16 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete_with_all_params(self, async_client: AsyncHanzo) -> None: user = await async_client.user.delete( user_ids=["string"], - hanzo_changed_by="hanzo-changed-by", + llm_changed_by="llm-changed-by", ) assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.user.with_raw_response.delete( @@ -412,28 +449,31 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = await response.parse() assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.user.with_streaming_response.delete( user_ids=["string"], ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = await response.parse() assert_matches_type(object, user, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_info(self, async_client: AsyncHanzo) -> None: user = await async_client.user.retrieve_info() assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_info_with_all_params(self, async_client: AsyncHanzo) -> None: user = await async_client.user.retrieve_info( @@ -441,20 +481,22 @@ async def test_method_retrieve_info_with_all_params(self, async_client: AsyncHan ) assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve_info(self, async_client: AsyncHanzo) -> None: response = await async_client.user.with_raw_response.retrieve_info() assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = await response.parse() assert_matches_type(object, user, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve_info(self, async_client: AsyncHanzo) -> None: async with async_client.user.with_streaming_response.retrieve_info() as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = await response.parse() assert_matches_type(object, user, path=["response"]) diff --git a/tests/api_resources/test_utils.py b/tests/api_resources/test_utils.py index 814704654..6438b37ff 100644 --- a/tests/api_resources/test_utils.py +++ b/tests/api_resources/test_utils.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -20,6 +20,7 @@ class TestUtils: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_get_supported_openai_params(self, client: Hanzo) -> None: util = client.utils.get_supported_openai_params( @@ -27,6 +28,7 @@ def test_method_get_supported_openai_params(self, client: Hanzo) -> None: ) assert_matches_type(object, util, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_get_supported_openai_params(self, client: Hanzo) -> None: response = client.utils.with_raw_response.get_supported_openai_params( @@ -34,23 +36,25 @@ def test_raw_response_get_supported_openai_params(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" util = response.parse() assert_matches_type(object, util, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_get_supported_openai_params(self, client: Hanzo) -> None: with client.utils.with_streaming_response.get_supported_openai_params( model="model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" util = response.parse() assert_matches_type(object, util, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_token_counter(self, client: Hanzo) -> None: util = client.utils.token_counter( @@ -58,6 +62,7 @@ def test_method_token_counter(self, client: Hanzo) -> None: ) assert_matches_type(UtilTokenCounterResponse, util, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_token_counter_with_all_params(self, client: Hanzo) -> None: util = client.utils.token_counter( @@ -67,6 +72,7 @@ def test_method_token_counter_with_all_params(self, client: Hanzo) -> None: ) assert_matches_type(UtilTokenCounterResponse, util, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_token_counter(self, client: Hanzo) -> None: response = client.utils.with_raw_response.token_counter( @@ -74,23 +80,25 @@ def test_raw_response_token_counter(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" util = response.parse() assert_matches_type(UtilTokenCounterResponse, util, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_token_counter(self, client: Hanzo) -> None: with client.utils.with_streaming_response.token_counter( model="model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" util = response.parse() assert_matches_type(UtilTokenCounterResponse, util, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_transform_request(self, client: Hanzo) -> None: util = client.utils.transform_request( @@ -99,6 +107,7 @@ def test_method_transform_request(self, client: Hanzo) -> None: ) assert_matches_type(UtilTransformRequestResponse, util, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_transform_request(self, client: Hanzo) -> None: response = client.utils.with_raw_response.transform_request( @@ -107,10 +116,11 @@ def test_raw_response_transform_request(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" util = response.parse() assert_matches_type(UtilTransformRequestResponse, util, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_transform_request(self, client: Hanzo) -> None: with client.utils.with_streaming_response.transform_request( @@ -118,7 +128,7 @@ def test_streaming_response_transform_request(self, client: Hanzo) -> None: request_body={}, ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" util = response.parse() assert_matches_type(UtilTransformRequestResponse, util, path=["response"]) @@ -127,8 +137,11 @@ def test_streaming_response_transform_request(self, client: Hanzo) -> None: class TestAsyncUtils: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_get_supported_openai_params(self, async_client: AsyncHanzo) -> None: util = await async_client.utils.get_supported_openai_params( @@ -136,6 +149,7 @@ async def test_method_get_supported_openai_params(self, async_client: AsyncHanzo ) assert_matches_type(object, util, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_get_supported_openai_params(self, async_client: AsyncHanzo) -> None: response = await async_client.utils.with_raw_response.get_supported_openai_params( @@ -143,23 +157,25 @@ async def test_raw_response_get_supported_openai_params(self, async_client: Asyn ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" util = await response.parse() assert_matches_type(object, util, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_get_supported_openai_params(self, async_client: AsyncHanzo) -> None: async with async_client.utils.with_streaming_response.get_supported_openai_params( model="model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" util = await response.parse() assert_matches_type(object, util, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_token_counter(self, async_client: AsyncHanzo) -> None: util = await async_client.utils.token_counter( @@ -167,6 +183,7 @@ async def test_method_token_counter(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(UtilTokenCounterResponse, util, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_token_counter_with_all_params(self, async_client: AsyncHanzo) -> None: util = await async_client.utils.token_counter( @@ -176,6 +193,7 @@ async def test_method_token_counter_with_all_params(self, async_client: AsyncHan ) assert_matches_type(UtilTokenCounterResponse, util, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_token_counter(self, async_client: AsyncHanzo) -> None: response = await async_client.utils.with_raw_response.token_counter( @@ -183,23 +201,25 @@ async def test_raw_response_token_counter(self, async_client: AsyncHanzo) -> Non ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" util = await response.parse() assert_matches_type(UtilTokenCounterResponse, util, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_token_counter(self, async_client: AsyncHanzo) -> None: async with async_client.utils.with_streaming_response.token_counter( model="model", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" util = await response.parse() assert_matches_type(UtilTokenCounterResponse, util, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_transform_request(self, async_client: AsyncHanzo) -> None: util = await async_client.utils.transform_request( @@ -208,6 +228,7 @@ async def test_method_transform_request(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(UtilTransformRequestResponse, util, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_transform_request(self, async_client: AsyncHanzo) -> None: response = await async_client.utils.with_raw_response.transform_request( @@ -216,10 +237,11 @@ async def test_raw_response_transform_request(self, async_client: AsyncHanzo) -> ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" util = await response.parse() assert_matches_type(UtilTransformRequestResponse, util, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_transform_request(self, async_client: AsyncHanzo) -> None: async with async_client.utils.with_streaming_response.transform_request( @@ -227,7 +249,7 @@ async def test_streaming_response_transform_request(self, async_client: AsyncHan request_body={}, ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" util = await response.parse() assert_matches_type(UtilTransformRequestResponse, util, path=["response"]) diff --git a/tests/api_resources/test_vertex_ai.py b/tests/api_resources/test_vertex_ai.py index 8d61d6a54..2357c3966 100644 --- a/tests/api_resources/test_vertex_ai.py +++ b/tests/api_resources/test_vertex_ai.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestVertexAI: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: vertex_ai = client.vertex_ai.create( @@ -23,6 +24,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.vertex_ai.with_raw_response.create( @@ -30,33 +32,33 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = response.parse() assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.vertex_ai.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = response.parse() assert_matches_type(object, vertex_ai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.vertex_ai.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Hanzo) -> None: vertex_ai = client.vertex_ai.retrieve( @@ -64,6 +66,7 @@ def test_method_retrieve(self, client: Hanzo) -> None: ) assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Hanzo) -> None: response = client.vertex_ai.with_raw_response.retrieve( @@ -71,33 +74,33 @@ def test_raw_response_retrieve(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = response.parse() assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Hanzo) -> None: with client.vertex_ai.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = response.parse() assert_matches_type(object, vertex_ai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.vertex_ai.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: Hanzo) -> None: vertex_ai = client.vertex_ai.update( @@ -105,6 +108,7 @@ def test_method_update(self, client: Hanzo) -> None: ) assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: Hanzo) -> None: response = client.vertex_ai.with_raw_response.update( @@ -112,33 +116,33 @@ def test_raw_response_update(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = response.parse() assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: Hanzo) -> None: with client.vertex_ai.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = response.parse() assert_matches_type(object, vertex_ai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.vertex_ai.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: Hanzo) -> None: vertex_ai = client.vertex_ai.delete( @@ -146,6 +150,7 @@ def test_method_delete(self, client: Hanzo) -> None: ) assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: Hanzo) -> None: response = client.vertex_ai.with_raw_response.delete( @@ -153,33 +158,33 @@ def test_raw_response_delete(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = response.parse() assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: Hanzo) -> None: with client.vertex_ai.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = response.parse() assert_matches_type(object, vertex_ai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.vertex_ai.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_patch(self, client: Hanzo) -> None: vertex_ai = client.vertex_ai.patch( @@ -187,6 +192,7 @@ def test_method_patch(self, client: Hanzo) -> None: ) assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_patch(self, client: Hanzo) -> None: response = client.vertex_ai.with_raw_response.patch( @@ -194,37 +200,39 @@ def test_raw_response_patch(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = response.parse() assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_patch(self, client: Hanzo) -> None: with client.vertex_ai.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = response.parse() assert_matches_type(object, vertex_ai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_patch(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): client.vertex_ai.with_raw_response.patch( "", ) class TestAsyncVertexAI: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: vertex_ai = await async_client.vertex_ai.create( @@ -232,6 +240,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.vertex_ai.with_raw_response.create( @@ -239,33 +248,33 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = await response.parse() assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.vertex_ai.with_streaming_response.create( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = await response.parse() assert_matches_type(object, vertex_ai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.vertex_ai.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: vertex_ai = await async_client.vertex_ai.retrieve( @@ -273,6 +282,7 @@ async def test_method_retrieve(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: response = await async_client.vertex_ai.with_raw_response.retrieve( @@ -280,33 +290,33 @@ async def test_raw_response_retrieve(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = await response.parse() assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncHanzo) -> None: async with async_client.vertex_ai.with_streaming_response.retrieve( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = await response.parse() assert_matches_type(object, vertex_ai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.vertex_ai.with_raw_response.retrieve( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncHanzo) -> None: vertex_ai = await async_client.vertex_ai.update( @@ -314,6 +324,7 @@ async def test_method_update(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: response = await async_client.vertex_ai.with_raw_response.update( @@ -321,33 +332,33 @@ async def test_raw_response_update(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = await response.parse() assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncHanzo) -> None: async with async_client.vertex_ai.with_streaming_response.update( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = await response.parse() assert_matches_type(object, vertex_ai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.vertex_ai.with_raw_response.update( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncHanzo) -> None: vertex_ai = await async_client.vertex_ai.delete( @@ -355,6 +366,7 @@ async def test_method_delete(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: response = await async_client.vertex_ai.with_raw_response.delete( @@ -362,33 +374,33 @@ async def test_raw_response_delete(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = await response.parse() assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncHanzo) -> None: async with async_client.vertex_ai.with_streaming_response.delete( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = await response.parse() assert_matches_type(object, vertex_ai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.vertex_ai.with_raw_response.delete( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_patch(self, async_client: AsyncHanzo) -> None: vertex_ai = await async_client.vertex_ai.patch( @@ -396,6 +408,7 @@ async def test_method_patch(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: response = await async_client.vertex_ai.with_raw_response.patch( @@ -403,29 +416,28 @@ async def test_raw_response_patch(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = await response.parse() assert_matches_type(object, vertex_ai, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_patch(self, async_client: AsyncHanzo) -> None: async with async_client.vertex_ai.with_streaming_response.patch( "endpoint", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" vertex_ai = await response.parse() assert_matches_type(object, vertex_ai, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_patch(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `endpoint` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `endpoint` but received ''"): await async_client.vertex_ai.with_raw_response.patch( "", ) diff --git a/tests/api_resources/threads/__init__.py b/tests/api_resources/threads/__init__.py index 4f5c25a9d..fd8019a9a 100644 --- a/tests/api_resources/threads/__init__.py +++ b/tests/api_resources/threads/__init__.py @@ -1 +1 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/threads/test_messages.py b/tests/api_resources/threads/test_messages.py index 8d6bfdd38..27405c608 100644 --- a/tests/api_resources/threads/test_messages.py +++ b/tests/api_resources/threads/test_messages.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestMessages: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: message = client.threads.messages.create( @@ -23,6 +24,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, message, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.threads.messages.with_raw_response.create( @@ -30,33 +32,33 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(object, message, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.threads.messages.with_streaming_response.create( "thread_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(object, message, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `thread_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): client.threads.messages.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Hanzo) -> None: message = client.threads.messages.list( @@ -64,6 +66,7 @@ def test_method_list(self, client: Hanzo) -> None: ) assert_matches_type(object, message, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Hanzo) -> None: response = client.threads.messages.with_raw_response.list( @@ -71,37 +74,39 @@ def test_raw_response_list(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(object, message, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Hanzo) -> None: with client.threads.messages.with_streaming_response.list( "thread_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(object, message, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_list(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `thread_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): client.threads.messages.with_raw_response.list( "", ) class TestAsyncMessages: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: message = await async_client.threads.messages.create( @@ -109,6 +114,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, message, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.threads.messages.with_raw_response.create( @@ -116,33 +122,33 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = await response.parse() assert_matches_type(object, message, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.threads.messages.with_streaming_response.create( "thread_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = await response.parse() assert_matches_type(object, message, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `thread_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): await async_client.threads.messages.with_raw_response.create( "", ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncHanzo) -> None: message = await async_client.threads.messages.list( @@ -150,6 +156,7 @@ async def test_method_list(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, message, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: response = await async_client.threads.messages.with_raw_response.list( @@ -157,29 +164,28 @@ async def test_raw_response_list(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = await response.parse() assert_matches_type(object, message, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncHanzo) -> None: async with async_client.threads.messages.with_streaming_response.list( "thread_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = await response.parse() assert_matches_type(object, message, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_list(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `thread_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): await async_client.threads.messages.with_raw_response.list( "", ) diff --git a/tests/api_resources/threads/test_runs.py b/tests/api_resources/threads/test_runs.py index aa93b1337..6b0d05bb3 100644 --- a/tests/api_resources/threads/test_runs.py +++ b/tests/api_resources/threads/test_runs.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -16,6 +16,7 @@ class TestRuns: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: Hanzo) -> None: run = client.threads.runs.create( @@ -23,6 +24,7 @@ def test_method_create(self, client: Hanzo) -> None: ) assert_matches_type(object, run, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Hanzo) -> None: response = client.threads.runs.with_raw_response.create( @@ -30,37 +32,39 @@ def test_raw_response_create(self, client: Hanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = response.parse() assert_matches_type(object, run, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: Hanzo) -> None: with client.threads.runs.with_streaming_response.create( "thread_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = response.parse() assert_matches_type(object, run, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: Hanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `thread_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): client.threads.runs.with_raw_response.create( "", ) class TestAsyncRuns: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncHanzo) -> None: run = await async_client.threads.runs.create( @@ -68,6 +72,7 @@ async def test_method_create(self, async_client: AsyncHanzo) -> None: ) assert_matches_type(object, run, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: response = await async_client.threads.runs.with_raw_response.create( @@ -75,29 +80,28 @@ async def test_raw_response_create(self, async_client: AsyncHanzo) -> None: ) assert response.is_closed is True - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = await response.parse() assert_matches_type(object, run, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncHanzo) -> None: async with async_client.threads.runs.with_streaming_response.create( "thread_id", ) as response: assert not response.is_closed - assert response.http_request.headers.get("X-SDK-Lang") == "python" + assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = await response.parse() assert_matches_type(object, run, path=["response"]) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncHanzo) -> None: - with pytest.raises( - ValueError, - match=r"Expected a non-empty value for `thread_id` but received ''", - ): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): await async_client.threads.runs.with_raw_response.create( "", ) diff --git a/tests/conformance/benchmark_parallel.py b/tests/conformance/benchmark_parallel.py deleted file mode 100644 index 3fe33509a..000000000 --- a/tests/conformance/benchmark_parallel.py +++ /dev/null @@ -1,283 +0,0 @@ -#!/usr/bin/env python3 -""" -HIP-0300 Parallel Performance Benchmark - -Tests that all MCP tools work correctly when invoked concurrently. -Measures throughput and latency under parallel load. - -Usage: - uv run python tests/conformance/benchmark_parallel.py - uv run python tests/conformance/benchmark_parallel.py --concurrency 20 -""" - -from __future__ import annotations - -import os -import sys -import json -import time -import asyncio -import tempfile -import statistics -from typing import Any -from pathlib import Path -from dataclasses import field, dataclass - -HANZO_ROOT = Path(os.environ.get("HANZO_ROOT", Path.home() / "work" / "hanzo")) -TIMEOUT = 30 -STARTUP_TIMEOUT = 20 - - -class MCPClient: - """JSON-RPC 2.0 over stdio client.""" - - def __init__(self, command: list[str], cwd: Path, env: dict[str, str]): - self.command = command - self.cwd = cwd - self.env = env - self.proc: asyncio.subprocess.Process | None = None - self._id = 0 - self._lock = asyncio.Lock() - - async def start(self) -> None: - full_env = {**os.environ, **self.env} - self.proc = await asyncio.create_subprocess_exec( - *self.command, - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - cwd=str(self.cwd), - env=full_env, - ) - resp = await self._request("initialize", { - "protocolVersion": "2024-11-05", - "capabilities": {"tools": {}}, - "clientInfo": {"name": "benchmark", "version": "1.0.0"}, - }) - if "error" in resp: - raise RuntimeError(f"Initialize failed: {resp['error']}") - await self._notify("notifications/initialized") - - async def stop(self) -> None: - if self.proc and self.proc.returncode is None: - self.proc.terminate() - try: - await asyncio.wait_for(self.proc.wait(), timeout=5) - except asyncio.TimeoutError: - self.proc.kill() - - async def call_tool(self, name: str, arguments: dict[str, Any]) -> dict: - return await self._request("tools/call", {"name": name, "arguments": arguments}) - - async def list_tools(self) -> list[dict]: - resp = await self._request("tools/list", {}) - return resp.get("result", {}).get("tools", []) - - async def _request(self, method: str, params: dict) -> dict: - async with self._lock: - self._id += 1 - msg = {"jsonrpc": "2.0", "id": self._id, "method": method, "params": params} - return await self._send_and_recv(msg) - - async def _notify(self, method: str, params: dict | None = None) -> None: - msg: dict[str, Any] = {"jsonrpc": "2.0", "method": method} - if params: - msg["params"] = params - assert self.proc and self.proc.stdin - self.proc.stdin.write(json.dumps(msg).encode() + b"\n") - await self.proc.stdin.drain() - - async def _send_and_recv(self, msg: dict) -> dict: - assert self.proc and self.proc.stdin and self.proc.stdout - data = json.dumps(msg).encode() + b"\n" - self.proc.stdin.write(data) - await self.proc.stdin.drain() - deadline = time.monotonic() + TIMEOUT - while time.monotonic() < deadline: - try: - line = await asyncio.wait_for( - self.proc.stdout.readline(), - timeout=max(0.1, deadline - time.monotonic()), - ) - except asyncio.TimeoutError: - break - if not line: - break - line = line.strip() - if not line: - continue - try: - resp = json.loads(line) - except json.JSONDecodeError: - continue - if resp.get("id") == msg["id"]: - return resp - return {"error": {"code": -1, "message": "Timeout"}} - - -@dataclass -class BenchResult: - tool: str - action: str - concurrency: int - total_calls: int - passed: int - failed: int - latencies_ms: list[float] = field(default_factory=list) - - @property - def p50(self) -> float: - return statistics.median(self.latencies_ms) if self.latencies_ms else 0 - - @property - def p95(self) -> float: - if not self.latencies_ms: - return 0 - sorted_l = sorted(self.latencies_ms) - idx = int(len(sorted_l) * 0.95) - return sorted_l[min(idx, len(sorted_l) - 1)] - - @property - def throughput(self) -> float: - total_s = sum(self.latencies_ms) / 1000 if self.latencies_ms else 1 - return self.total_calls / (total_s / self.concurrency) if total_s > 0 else 0 - - -async def bench_tool( - client: MCPClient, - tool: str, - action: str, - params: dict[str, Any], - concurrency: int = 10, - iterations: int = 50, -) -> BenchResult: - """Benchmark a tool with parallel invocations.""" - result = BenchResult( - tool=tool, action=action, concurrency=concurrency, - total_calls=iterations, passed=0, failed=0, - ) - - sem = asyncio.Semaphore(concurrency) - - async def single_call(): - async with sem: - t0 = time.monotonic() - try: - resp = await asyncio.wait_for( - client.call_tool(tool, {"action": action, **params}), - timeout=TIMEOUT, - ) - duration = (time.monotonic() - t0) * 1000 - result.latencies_ms.append(duration) - if "error" in resp: - result.failed += 1 - else: - result.passed += 1 - except Exception: - result.failed += 1 - result.latencies_ms.append((time.monotonic() - t0) * 1000) - - # Run all calls concurrently - await asyncio.gather(*[single_call() for _ in range(iterations)]) - return result - - -async def main(): - import argparse - parser = argparse.ArgumentParser(description="HIP-0300 Parallel Benchmark") - parser.add_argument("--concurrency", type=int, default=10, help="Max concurrent calls") - parser.add_argument("--iterations", type=int, default=50, help="Total calls per test") - parser.add_argument("--impl", default="python", choices=["python", "typescript"]) - args = parser.parse_args() - - if args.impl == "python": - client = MCPClient( - command=["uv", "run", "hanzo-mcp"], - cwd=HANZO_ROOT / "python-sdk", - env={"LOGLEVEL": "ERROR", "HANZO_MCP_LOG_LEVEL": "ERROR"}, - ) - else: - client = MCPClient( - command=["node", "dist/cli.js", "serve"], - cwd=HANZO_ROOT / "mcp", - env={"NODE_ENV": "test", "MCP_LOG_LEVEL": "error"}, - ) - - print(f"\nHIP-0300 Parallel Benchmark ({args.impl})") - print(f"Concurrency: {args.concurrency}, Iterations: {args.iterations}") - print(f"{'='*70}") - - print("Starting server...", end=" ", flush=True) - await asyncio.wait_for(client.start(), timeout=STARTUP_TIMEOUT) - tools = await client.list_tools() - tool_names = set(t["name"] for t in tools) - print(f"OK ({len(tools)} tools)") - - # Create temp dir for fs tests - tmp = Path(tempfile.mkdtemp(prefix="mcp-bench-")) - - benchmarks = [] - - # Define test workloads - workloads = [ - ("exec", "exec", {"command": "echo bench"}), - ("exec", "ps", {}), - ("git", "status", {}), - ("git", "log", {"limit": 3}), - ("think", "think", {"thought": "Benchmark test"}), - ] - - # Only test tools that are registered - workloads = [(t, a, p) for t, a, p in workloads if t in tool_names] - - # Add fs tests with unique filenames - if "fs" in tool_names: - # Write files first - for i in range(args.iterations): - await client.call_tool("fs", { - "action": "write", - "path": str(tmp / f"bench_{i}.txt"), - "content": f"benchmark content {i}", - }) - workloads.append(("fs", "read", {"path": str(tmp / "bench_0.txt")})) - workloads.append(("fs", "list", {"path": str(tmp)})) - - print(f"\nRunning {len(workloads)} workloads...\n") - print(f"{'Tool':<12} {'Action':<10} {'Pass':<6} {'Fail':<6} {'P50':<10} {'P95':<10} {'Throughput':<12}") - print("-" * 70) - - for tool, action, params in workloads: - result = await bench_tool( - client, tool, action, params, - concurrency=args.concurrency, - iterations=args.iterations, - ) - benchmarks.append(result) - print( - f"{tool:<12} {action:<10} {result.passed:<6} {result.failed:<6} " - f"{result.p50:>7.1f}ms {result.p95:>7.1f}ms " - f"{result.throughput:>8.1f} ops/s" - ) - - # Cleanup - import shutil - shutil.rmtree(tmp, ignore_errors=True) - await client.stop() - - # Summary - total_passed = sum(b.passed for b in benchmarks) - total_failed = sum(b.failed for b in benchmarks) - total = total_passed + total_failed - all_latencies = [l for b in benchmarks for l in b.latencies_ms] - - print(f"\n{'='*70}") - print(f"Total: {total_passed}/{total} passed ({total_failed} failed)") - if all_latencies: - print(f"Overall P50: {statistics.median(all_latencies):.1f}ms P95: {sorted(all_latencies)[int(len(all_latencies)*0.95)]:.1f}ms") - print(f"All tools async-safe: {'YES' if total_failed == 0 else 'NO'}") - - sys.exit(0 if total_failed == 0 else 1) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/tests/conformance/run_conformance.py b/tests/conformance/run_conformance.py deleted file mode 100644 index 9f26c6bb6..000000000 --- a/tests/conformance/run_conformance.py +++ /dev/null @@ -1,751 +0,0 @@ -#!/usr/bin/env python3 -""" -HIP-0300 MCP Conformance Test Suite - -Tests that all MCP implementations (TypeScript, Python, Rust) expose equivalent -tool functionality. Handles the fact that TS uses unified tool names (fs, exec) -while Python currently uses legacy names (read_file, run_command) by mapping both. - -Speaks raw MCP JSON-RPC 2.0 over stdio to each server process. - -Usage: - # Test all enabled implementations - uv run python tests/conformance/run_conformance.py - - # Test specific implementation - uv run python tests/conformance/run_conformance.py --impl python - uv run python tests/conformance/run_conformance.py --impl typescript - - # Test specific category - uv run python tests/conformance/run_conformance.py --cat filesystem - - # Verbose output - uv run python tests/conformance/run_conformance.py -v -""" - -from __future__ import annotations - -import os -import re -import sys -import json -import time -import shutil -import asyncio -import tempfile -from typing import Any -from pathlib import Path -from dataclasses import field, dataclass - -# โ”€โ”€ Configuration โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -HANZO_ROOT = Path(os.environ.get("HANZO_ROOT", Path.home() / "work" / "hanzo")) -TIMEOUT = 30 # seconds per request -STARTUP_TIMEOUT = 20 # seconds to wait for server ready - - -@dataclass -class Implementation: - id: str - name: str - command: list[str] - cwd: Path - env: dict[str, str] = field(default_factory=dict) - enabled: bool = True - startup_timeout: int = STARTUP_TIMEOUT - - -IMPLEMENTATIONS: list[Implementation] = [ - Implementation( - id="python", - name="Hanzo MCP Python", - command=["uv", "run", "hanzo-mcp"], - cwd=HANZO_ROOT / "python-sdk", - env={"LOGLEVEL": "ERROR", "HANZO_MCP_LOG_LEVEL": "ERROR"}, - enabled=True, - ), - Implementation( - id="typescript", - name="Hanzo MCP TypeScript", - command=["node", "dist/cli.js", "serve"], - cwd=HANZO_ROOT / "mcp", - env={"NODE_ENV": "test", "MCP_LOG_LEVEL": "error"}, - enabled=(HANZO_ROOT / "mcp" / "dist" / "cli.js").exists(), - ), - Implementation( - id="rust", - name="Hanzo MCP Rust", - command=["cargo", "run", "--release", "--bin", "hanzo-mcp-server", "--", "serve"], - cwd=HANZO_ROOT / "rust-sdk", - env={"RUST_LOG": "error"}, - enabled=False, - startup_timeout=60, - ), -] - - -# โ”€โ”€ Tool Name Mapping โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -# Maps (category, action) โ†’ {impl_id: (tool_name, arguments)} -# This bridges unified (TS) and legacy (Python) tool surfaces. - -def resolve_tool_call( - impl_id: str, - tool_names: set[str], - category: str, - action: str, - params: dict[str, Any], - tool_schemas: dict[str, dict] | None = None, -) -> tuple[str, dict[str, Any]] | None: - """Resolve a logical test action to the correct tool name + args for an implementation. - - Returns (tool_name, arguments) or None if the tool isn't available. - """ - tool_schemas = tool_schemas or {} - # Unified names (TS default, Python HIP-0300) - # Note: Python's BaseTool-derived tools (fs, git, code, fetch) require a `kwargs` wrapper - # while think/memory/tasks/browser use flat params. We detect which format via tool_names. - unified_map: dict[tuple[str, str], tuple[str, dict]] = { - ("filesystem", "read"): ("fs", {"action": "read", **params}), - ("filesystem", "write"): ("fs", {"action": "write", **params}), - ("filesystem", "stat"): ("fs", {"action": "stat", **params}), - ("filesystem", "list"): ("fs", {"action": "list", **params}), - ("filesystem", "search"): ("fs", {"action": "search_text", **params}), - ("filesystem", "mkdir"): ("fs", {"action": "mkdir", **params}), - ("filesystem", "rm"): ("fs", {"action": "rm", **params}), - ("shell", "exec"): ("exec", {"action": "exec", **params}), - ("shell", "ps"): ("exec", {"action": "ps"}), - ("vcs", "status"): ("git", {"action": "status", **params}), - ("vcs", "branch"): ("git", {"action": "branch", **params}), - ("vcs", "log"): ("git", {"action": "log", **params}), - ("vcs", "diff"): ("git", {"action": "diff", **params}), - ("code", "symbols"): ("code", {"action": "symbols", **params}), - ("code", "summarize"): ("code", {"action": "summarize", **params}), - ("network", "fetch"): ("fetch", {"action": "fetch", **params}), - ("network", "head"): ("fetch", {"action": "head", **params}), - ("reasoning", "think"): ("think", {"action": "think", **params}), - ("browser", "status"): ("browser", {"action": "status"}), - ("memory", "store"): ("memory", {"action": "store", **params}), - ("memory", "recall"): ("memory", {"action": "recall", **params}), - ("memory", "delete"): ("memory", {"action": "delete", **params}), - ("tasks", "add"): ("tasks", {"action": "add", **params}), - ("tasks", "list"): ("tasks", {"action": "list", **params}), - ("mode", "list"): ("mode", {"action": "list"}), - } - - # Python's HIP-0300 BaseTool uses kwargs wrapper for fs/git/code/fetch/exec - # These tools require: {"action": "...", "kwargs": {...params}} - py_kwargs_map: dict[tuple[str, str], tuple[str, dict]] = { - ("filesystem", "read"): ("fs", {"action": "read", "kwargs": params}), - ("filesystem", "write"): ("fs", {"action": "write", "kwargs": params}), - ("filesystem", "stat"): ("fs", {"action": "stat", "kwargs": params}), - ("filesystem", "list"): ("fs", {"action": "list", "kwargs": params}), - ("filesystem", "search"): ("fs", {"action": "search_text", "kwargs": params}), - ("filesystem", "mkdir"): ("fs", {"action": "mkdir", "kwargs": params}), - ("filesystem", "rm"): ("fs", {"action": "rm", "kwargs": params}), - ("shell", "exec"): ("exec", {"action": "exec", "kwargs": params}), - ("shell", "ps"): ("exec", {"action": "ps", "kwargs": {}}), - ("vcs", "status"): ("git", {"action": "status", "kwargs": params}), - ("vcs", "branch"): ("git", {"action": "branch", "kwargs": params}), - ("vcs", "log"): ("git", {"action": "log", "kwargs": params}), - ("vcs", "diff"): ("git", {"action": "diff", "kwargs": params}), - ("code", "symbols"): ("code", {"action": "symbols", "kwargs": params}), - ("code", "summarize"): ("code", {"action": "summarize", "kwargs": params}), - ("network", "fetch"): ("fetch", {"action": "fetch", "kwargs": params}), - ("network", "head"): ("fetch", {"action": "head", "kwargs": params}), - } - - # Legacy names (Python current) - legacy_map: dict[tuple[str, str], tuple[str, dict]] = { - ("filesystem", "read"): ("read_file", params), - ("filesystem", "write"): ("write_file", params), - ("filesystem", "stat"): ("get_file_info", params), - ("filesystem", "list"): ("list_files", params), - ("filesystem", "search"): ("grep", params), - ("filesystem", "mkdir"): ("create_file", {**params, "content": ""}), - ("filesystem", "rm"): ("delete_file", params), - ("shell", "exec"): ("run_command", params), - ("shell", "ps"): ("list_processes", {}), - ("vcs", "status"): ("run_command", {"command": "git status"}), - ("vcs", "branch"): ("run_command", {"command": "git branch"}), - ("vcs", "log"): ("run_command", {"command": f"git log --oneline -n {params.get('limit', 5)}"}), - ("vcs", "diff"): ("run_command", {"command": "git diff"}), - ("reasoning", "think"): ("critic", {"analysis": params.get("thought", params.get("analysis", ""))}), - ("browser", "status"): ("browser", {"action": "status"}), - } - - key = (category, action) - - # Check if tool uses kwargs wrapper (Python BaseTool pattern) - def _needs_kwargs(tool_name: str) -> bool: - schema = tool_schemas.get(tool_name, {}) - return "kwargs" in schema.get("required", []) or "kwargs" in schema.get("properties", {}) - - # Try kwargs-wrapped unified names (Python BaseTool pattern) - if key in py_kwargs_map: - tool, args = py_kwargs_map[key] - if tool in tool_names and _needs_kwargs(tool): - return tool, args - - # Try flat unified names (TS pattern) - if key in unified_map: - tool, args = unified_map[key] - if tool in tool_names: - return tool, args - - # Fall back to legacy name - if key in legacy_map: - tool, args = legacy_map[key] - if tool in tool_names: - return tool, args - - return None - - -# โ”€โ”€ MCP Protocol โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -class MCPClient: - """Speaks JSON-RPC 2.0 over stdio to an MCP server process.""" - - def __init__(self, impl: Implementation): - self.impl = impl - self.proc: asyncio.subprocess.Process | None = None - self._id = 0 - - async def start(self) -> None: - env = {**os.environ, **self.impl.env} - self.proc = await asyncio.create_subprocess_exec( - *self.impl.command, - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - cwd=str(self.impl.cwd), - env=env, - ) - resp = await self._request("initialize", { - "protocolVersion": "2024-11-05", - "capabilities": {"tools": {}}, - "clientInfo": {"name": "conformance-test", "version": "1.0.0"}, - }) - if "error" in resp: - raise RuntimeError(f"Initialize failed: {resp['error']}") - await self._notify("notifications/initialized") - - async def stop(self) -> None: - if self.proc and self.proc.returncode is None: - self.proc.terminate() - try: - await asyncio.wait_for(self.proc.wait(), timeout=5) - except asyncio.TimeoutError: - self.proc.kill() - - async def list_tools(self) -> list[dict]: - resp = await self._request("tools/list", {}) - return resp.get("result", {}).get("tools", []) - - async def call_tool(self, name: str, arguments: dict[str, Any]) -> dict: - return await self._request("tools/call", {"name": name, "arguments": arguments}) - - async def _request(self, method: str, params: dict) -> dict: - self._id += 1 - msg = {"jsonrpc": "2.0", "id": self._id, "method": method, "params": params} - return await self._send_and_recv(msg) - - async def _notify(self, method: str, params: dict | None = None) -> None: - msg: dict[str, Any] = {"jsonrpc": "2.0", "method": method} - if params: - msg["params"] = params - assert self.proc and self.proc.stdin - self.proc.stdin.write(json.dumps(msg).encode() + b"\n") - await self.proc.stdin.drain() - - async def _send_and_recv(self, msg: dict) -> dict: - assert self.proc and self.proc.stdin and self.proc.stdout - data = json.dumps(msg).encode() + b"\n" - self.proc.stdin.write(data) - await self.proc.stdin.drain() - - deadline = time.monotonic() + TIMEOUT - while time.monotonic() < deadline: - try: - line = await asyncio.wait_for( - self.proc.stdout.readline(), - timeout=max(0.1, deadline - time.monotonic()), - ) - except asyncio.TimeoutError: - break - if not line: - break - line = line.strip() - if not line: - continue - try: - resp = json.loads(line) - except json.JSONDecodeError: - continue - if "id" not in resp: - continue - if resp.get("id") == msg["id"]: - return resp - return {"error": {"code": -1, "message": "Timeout waiting for response"}} - - -# โ”€โ”€ Test Cases โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dataclass -class TestCase: - category: str - action: str - name: str - params: dict[str, Any] - expect_success: bool = True - expect_pattern: str | None = None - expect_error_pattern: str | None = None - setup: Any = None - cleanup: Any = None - - -def build_test_cases() -> list[TestCase]: - """Build the HIP-0300 conformance test cases.""" - tmp = Path(tempfile.mkdtemp(prefix="mcp-conformance-")) - cases: list[TestCase] = [] - - def setup_fs(): - tmp.mkdir(parents=True, exist_ok=True) - - def cleanup_fs(): - shutil.rmtree(tmp, ignore_errors=True) - - # โ”€โ”€ Filesystem โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - cases.extend([ - TestCase( - category="filesystem", action="write", name="write creates file", - params={"path": str(tmp / "hello.txt"), "content": "Hello HIP-0300!"}, - setup=setup_fs, - ), - TestCase( - category="filesystem", action="read", name="read reads file back", - params={"path": str(tmp / "hello.txt")}, - expect_pattern="Hello HIP-0300!", - ), - TestCase( - category="filesystem", action="stat", name="stat gets metadata", - params={"path": str(tmp / "hello.txt")}, - expect_pattern="(?i)size|bytes|modified|type|permission", - ), - TestCase( - category="filesystem", action="list", name="list directory", - params={"path": str(tmp)}, - expect_pattern="hello", - ), - TestCase( - category="filesystem", action="rm", name="rm removes file", - params={"path": str(tmp / "hello.txt"), "confirm": True}, - cleanup=cleanup_fs, - ), - ]) - - # โ”€โ”€ Shell / Execution โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - cases.extend([ - TestCase( - category="shell", action="exec", name="exec echo command", - params={"command": 'echo "HIP-0300 OK"'}, - expect_pattern="HIP-0300 OK", - ), - TestCase( - category="shell", action="exec", name="exec true succeeds", - params={"command": "true"}, - ), - TestCase( - category="shell", action="ps", name="ps lists processes", - params={}, - ), - ]) - - # โ”€โ”€ Version Control โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - cases.extend([ - TestCase( - category="vcs", action="status", name="git status", - params={}, - ), - TestCase( - category="vcs", action="branch", name="git branch lists branches", - params={}, - expect_pattern="(?i)main|master|\\*", - ), - TestCase( - category="vcs", action="log", name="git log shows history", - params={"limit": 3}, - ), - ]) - - # โ”€โ”€ Code โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - code_file = tmp / "sample.py" - def setup_code(): - tmp.mkdir(parents=True, exist_ok=True) - code_file.write_text('def greet(name):\n return f"hello {name}"\n\nclass Greeter:\n pass\n') - - cases.extend([ - TestCase( - category="code", action="symbols", name="code.symbols lists symbols", - params={"path": str(code_file)}, - expect_pattern="(?i)greet|hello|Greeter|function|class|def", - setup=setup_code, - ), - TestCase( - category="code", action="summarize", name="code.summarize diff text", - params={"text": "--- a/foo.py\n+++ b/foo.py\n@@ -1 +1 @@\n-old\n+new\n"}, - ), - ]) - - # โ”€โ”€ Network / Fetch โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - cases.extend([ - TestCase( - category="network", action="fetch", name="fetch.fetch retrieves URL", - params={"url": "https://httpbin.org/get"}, - expect_pattern="(?i)origin|headers|url", - ), - TestCase( - category="network", action="head", name="fetch.head gets headers", - params={"url": "https://httpbin.org/get"}, - expect_pattern="(?i)content-type|200|headers|status", - ), - ]) - - # โ”€โ”€ Reasoning โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - cases.append( - TestCase( - category="reasoning", action="think", name="think accepts thought", - params={"action": "think", "thought": "Testing conformance across implementations."}, - ), - ) - - # โ”€โ”€ Browser (non-browser-requiring tests) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - cases.extend([ - TestCase( - category="browser", action="status", name="browser status check", - params={"action": "status"}, - ), - ]) - - # โ”€โ”€ Memory โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - # TS memory uses: key/value. Python memory uses: fact/query. - cases.extend([ - TestCase( - category="memory", action="store", name="memory store", - params={"key": "conformance-test", "value": "HIP-0300 value", "fact": "conformance-test: HIP-0300 value"}, - expect_pattern="(?i)stored|saved|ok|success|written|remembered|added", - ), - TestCase( - category="memory", action="recall", name="memory recall", - params={"key": "conformance-test", "query": "conformance-test"}, - expect_pattern="(?i)HIP-0300|conformance", - ), - TestCase( - category="memory", action="delete", name="memory delete", - params={"key": "conformance-test", "query": "conformance-test"}, - ), - ]) - - # โ”€โ”€ Tasks โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - cases.extend([ - TestCase( - category="tasks", action="add", name="tasks add", - params={"content": "Conformance test task"}, - expect_pattern="(?i)added|created|task|ok", - ), - TestCase( - category="tasks", action="list", name="tasks list", - params={}, - ), - ]) - - # โ”€โ”€ Mode โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - cases.append( - TestCase( - category="mode", action="list", name="mode.list shows modes", - params={}, - ), - ) - - return cases - - -# โ”€โ”€ Runner โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - -@dataclass -class TestResult: - impl: str - category: str - action: str - name: str - passed: bool - duration_ms: float - tool_used: str = "" - error: str = "" - skipped: bool = False - - -def extract_text(resp: dict) -> str: - """Extract all text content from an MCP response.""" - result = resp.get("result", {}) - content = result.get("content", []) - if isinstance(content, list): - texts = [item.get("text", "") for item in content if isinstance(item, dict)] - return " ".join(texts) - if isinstance(result, str): - return result - return json.dumps(result) - - -def is_error_response(resp: dict) -> bool: - """Check if response indicates an error.""" - if "error" in resp: - return True - result = resp.get("result", {}) - if isinstance(result, dict) and result.get("isError"): - return True - return False - - -async def run_tests( - impls: list[Implementation], - cases: list[TestCase], - categories_filter: list[str] | None = None, - verbose: bool = False, -) -> list[TestResult]: - """Run conformance tests against all implementations.""" - results: list[TestResult] = [] - - for impl in impls: - if not impl.enabled: - continue - - print(f"\n{'='*60}") - print(f" {impl.name} ({impl.id})") - print(f"{'='*60}") - - client = MCPClient(impl) - try: - print(f" Starting server...", end=" ", flush=True) - await asyncio.wait_for(client.start(), timeout=impl.startup_timeout) - print("OK") - - tools = await client.list_tools() - tool_names = set(t["name"] for t in tools) - tool_schemas = {t["name"]: t.get("inputSchema", {}) for t in tools} - print(f" Tools registered: {len(tools)}") - - if verbose: - for tn in sorted(tool_names): - print(f" - {tn}") - - for tc in cases: - if categories_filter and tc.category not in categories_filter: - continue - - resolved = resolve_tool_call(impl.id, tool_names, tc.category, tc.action, tc.params, tool_schemas) - if resolved is None: - print(f" SKIP {tc.name} (no matching tool for {tc.category}.{tc.action})") - results.append(TestResult( - impl=impl.id, category=tc.category, action=tc.action, - name=tc.name, passed=False, duration_ms=0, - error=f"No tool for {tc.category}.{tc.action}", skipped=True, - )) - continue - - tool_name, arguments = resolved - - if tc.setup: - tc.setup() - - t0 = time.monotonic() - try: - resp = await asyncio.wait_for( - client.call_tool(tool_name, arguments), - timeout=TIMEOUT, - ) - duration = (time.monotonic() - t0) * 1000 - - is_err = is_error_response(resp) - text = extract_text(resp) - passed = True - error = "" - - if tc.expect_success and is_err: - passed = False - err_msg = resp.get("error", {}).get("message", text) - error = f"Expected success, got error: {err_msg[:200]}" - elif not tc.expect_success and not is_err: - passed = False - error = f"Expected error, got success: {text[:100]}" - - if passed and tc.expect_pattern: - if not re.search(tc.expect_pattern, text, re.IGNORECASE): - passed = False - error = f"Pattern /{tc.expect_pattern}/ not found in: {text[:200]}" - - if passed and not tc.expect_success and tc.expect_error_pattern: - err_text = resp.get("error", {}).get("message", text) - if not re.search(tc.expect_error_pattern, err_text, re.IGNORECASE): - passed = False - error = f"Error pattern /{tc.expect_error_pattern}/ not found" - - status = "PASS" if passed else "FAIL" - print(f" {status} {tc.name} [{tool_name}] ({duration:.0f}ms)") - if not passed: - print(f" {error}") - elif verbose: - print(f" -> {text[:120]}") - - results.append(TestResult( - impl=impl.id, category=tc.category, action=tc.action, - name=tc.name, passed=passed, duration_ms=duration, - tool_used=tool_name, error=error, - )) - - except asyncio.TimeoutError: - duration = (time.monotonic() - t0) * 1000 - print(f" FAIL {tc.name} [{tool_name}] (TIMEOUT {duration:.0f}ms)") - results.append(TestResult( - impl=impl.id, category=tc.category, action=tc.action, - name=tc.name, passed=False, duration_ms=duration, - tool_used=tool_name, error="Timeout", - )) - except Exception as e: - duration = (time.monotonic() - t0) * 1000 - print(f" FAIL {tc.name} [{tool_name}] ({e})") - results.append(TestResult( - impl=impl.id, category=tc.category, action=tc.action, - name=tc.name, passed=False, duration_ms=duration, - tool_used=tool_name, error=str(e), - )) - finally: - if tc.cleanup: - try: - tc.cleanup() - except Exception: - pass - - except Exception as e: - print(f"FAILED to start: {e}") - for tc in cases: - if categories_filter and tc.category not in categories_filter: - continue - results.append(TestResult( - impl=impl.id, category=tc.category, action=tc.action, - name=tc.name, passed=False, duration_ms=0, - error=f"Server start failed: {e}", - )) - finally: - await client.stop() - - return results - - -def print_summary(results: list[TestResult]) -> None: - """Print a summary table of results.""" - if not results: - print("\nNo results.") - return - - impls = sorted(set(r.impl for r in results)) - categories = sorted(set(r.category for r in results)) - - print(f"\n{'='*70}") - print(" HIP-0300 CONFORMANCE SUMMARY") - print(f"{'='*70}\n") - - # Per-implementation breakdown - for impl in impls: - ir = [r for r in results if r.impl == impl and not r.skipped] - passed = sum(1 for r in ir if r.passed) - total = len(ir) - skipped = sum(1 for r in results if r.impl == impl and r.skipped) - pct = (passed / total * 100) if total else 0 - status = "PASS" if passed == total else "FAIL" - print(f" {impl:20s} {passed}/{total} ({pct:.0f}%) [{status}] ({skipped} skipped)") - - for cat in categories: - cr = [r for r in ir if r.category == cat] - if not cr: - continue - cp = sum(1 for r in cr if r.passed) - ct = len(cr) - marker = "+" if cp == ct else "X" - # Show which tool names were used - tool_names = sorted(set(r.tool_used for r in cr if r.tool_used)) - tools_str = ", ".join(tool_names) if tool_names else "?" - print(f" [{marker}] {cat:15s} {cp}/{ct} via: {tools_str}") - - # Cross-implementation parity - if len(impls) > 1: - print(f"\n CROSS-IMPLEMENTATION PARITY:") - for cat in categories: - impl_status = {} - for impl in impls: - cr = [r for r in results if r.impl == impl and r.category == cat and not r.skipped] - if cr: - impl_status[impl] = all(r.passed for r in cr) - - if len(impl_status) < 2: - continue - - values = list(impl_status.values()) - if all(values): - print(f" {cat:15s} PARITY OK") - elif any(values): - passing = [k for k, v in impl_status.items() if v] - failing = [k for k, v in impl_status.items() if not v] - print(f" {cat:15s} MISMATCH pass={passing} fail={failing}") - else: - print(f" {cat:15s} ALL FAIL") - - print() - - -async def main() -> int: - import argparse - - parser = argparse.ArgumentParser(description="HIP-0300 MCP Conformance Tests") - parser.add_argument("--impl", "-i", action="append", help="Implementation ID(s)") - parser.add_argument("--cat", "-c", action="append", help="Category filter") - parser.add_argument("--verbose", "-v", action="store_true") - parser.add_argument("--list", action="store_true", help="List implementations") - args = parser.parse_args() - - if args.list: - for impl in IMPLEMENTATIONS: - status = "enabled" if impl.enabled else "disabled" - exists = impl.cwd.exists() - print(f" {impl.id:15s} {impl.name:30s} [{status}] {'exists' if exists else 'MISSING'}") - return 0 - - impls = IMPLEMENTATIONS - if args.impl: - impls = [i for i in impls if i.id in args.impl] - for i in impls: - i.enabled = True - - cases = build_test_cases() - enabled = [i.id for i in impls if i.enabled] - print(f"HIP-0300 Conformance Suite: {len(cases)} test cases") - print(f"Implementations: {', '.join(enabled)}") - - results = await run_tests(impls, cases, categories_filter=args.cat, verbose=args.verbose) - print_summary(results) - - non_skipped = [r for r in results if not r.skipped] - if all(r.passed for r in non_skipped): - print("ALL TESTS PASSED") - return 0 - else: - failed = sum(1 for r in non_skipped if not r.passed) - print(f"{failed} TEST(S) FAILED") - return 1 - - -if __name__ == "__main__": - sys.exit(asyncio.run(main())) diff --git a/tests/conftest.py b/tests/conftest.py index 7ef0b4ecd..ddb3ba82b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,22 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + from __future__ import annotations import os -import sys import logging from typing import TYPE_CHECKING, Iterator, AsyncIterator -# Add pkg directory to path for hanzoai import -sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(__file__)), "pkg")) - +import httpx import pytest -import pytest_asyncio from pytest_asyncio import is_async_test -from hanzoai import Hanzo, AsyncHanzo -from tests.mock_fixtures import mock_api +from hanzoai import Hanzo, AsyncHanzo, DefaultAioHttpClient +from hanzoai._utils import is_dict if TYPE_CHECKING: - from _pytest.fixtures import FixtureRequest + from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage] pytest.register_assert_rewrite("tests.utils") @@ -27,18 +25,31 @@ # so we don't have to add that boilerplate everywhere def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: pytest_asyncio_tests = (item for item in items if is_async_test(item)) - session_scope_marker = pytest.mark.asyncio() + session_scope_marker = pytest.mark.asyncio(loop_scope="session") for async_test in pytest_asyncio_tests: async_test.add_marker(session_scope_marker, append=False) + # We skip tests that use both the aiohttp client and respx_mock as respx_mock + # doesn't support custom transports. + for item in items: + if "async_client" not in item.fixturenames or "respx_mock" not in item.fixturenames: + continue + + if not hasattr(item, "callspec"): + continue + + async_client_param = item.callspec.params.get("async_client") + if is_dict(async_client_param) and async_client_param.get("http_client") == "aiohttp": + item.add_marker(pytest.mark.skip(reason="aiohttp client is not compatible with respx_mock")) + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "My API Key" -@pytest.fixture(scope="function") -def client(request: FixtureRequest, mock_api) -> Iterator[Hanzo]: +@pytest.fixture(scope="session") +def client(request: FixtureRequest) -> Iterator[Hanzo]: strict = getattr(request, "param", True) if not isinstance(strict, bool): raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") @@ -47,11 +58,27 @@ def client(request: FixtureRequest, mock_api) -> Iterator[Hanzo]: yield client -@pytest_asyncio.fixture(scope="function") -async def async_client(request: FixtureRequest, mock_api) -> AsyncIterator[AsyncHanzo]: - strict = getattr(request, "param", True) - if not isinstance(strict, bool): - raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") +@pytest.fixture(scope="session") +async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncHanzo]: + param = getattr(request, "param", True) + + # defaults + strict = True + http_client: None | httpx.AsyncClient = None + + if isinstance(param, bool): + strict = param + elif is_dict(param): + strict = param.get("strict", True) + assert isinstance(strict, bool) + + http_client_type = param.get("http_client", "httpx") + if http_client_type == "aiohttp": + http_client = DefaultAioHttpClient() + else: + raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") - async with AsyncHanzo(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client: + async with AsyncHanzo( + base_url=base_url, api_key=api_key, _strict_response_validation=strict, http_client=http_client + ) as client: yield client diff --git a/tests/e2e/hanzo-net-orchestration/Makefile b/tests/e2e/hanzo-net-orchestration/Makefile deleted file mode 100644 index 7c58b6deb..000000000 --- a/tests/e2e/hanzo-net-orchestration/Makefile +++ /dev/null @@ -1,201 +0,0 @@ -# Makefile for Hanzo Net E2E Testing - -.PHONY: help setup test quick-test clean docker-up docker-down logs - -# Default target -help: - @echo "Hanzo Net E2E Test - Available Commands" - @echo "=======================================" - @echo "" - @echo "Setup & Installation:" - @echo " make setup - Download models and start services" - @echo " make install - Install hanzo and hanzo-network" - @echo "" - @echo "Testing:" - @echo " make test - Run full E2E test suite" - @echo " make quick-test - Run quick test with single model" - @echo " make test-qwen - Test with Qwen3 model" - @echo " make test-llama - Test with Llama 3.2 model" - @echo " make test-mistral - Test with Mistral 7B" - @echo "" - @echo "Docker Management:" - @echo " make docker-up - Start Docker services" - @echo " make docker-down - Stop Docker services" - @echo " make logs - View Docker logs" - @echo "" - @echo "Hanzo Commands:" - @echo " make start-net - Start hanzo net locally" - @echo " make start-dev - Start hanzo dev with local orchestrator" - @echo " make demo - Run interactive demo" - @echo "" - @echo "Cleanup:" - @echo " make clean - Stop all services and clean up" - -# Installation -install: - @echo "Installing hanzo and dependencies..." - @cd ../../.. && pip install -e pkg/hanzo/ - @cd ../../.. && pip install -e pkg/hanzo-network/ - @cd ../../.. && pip install -e pkg/hanzo-mcp/ - @echo "โœ“ Installation complete" - -# Setup models and services -setup: install - @echo "Setting up models and services..." - @bash setup_models.sh - @echo "โœ“ Setup complete" - -# Run full test suite -test: install - @echo "Running full E2E test suite..." - @bash run_test.sh - -# Quick test with default model -quick-test: install - @echo "Running quick test..." - @bash run_test.sh --quick - -# Test specific models -test-qwen: install - @echo "Testing with Qwen3..." - @bash run_test.sh --model qwen3 --quick - -test-llama: install - @echo "Testing with Llama 3.2..." - @bash run_test.sh --model llama-3.2-3b --quick - -test-mistral: install - @echo "Testing with Mistral 7B..." - @bash run_test.sh --model mistral-7b --quick - -test-deepseek: install - @echo "Testing with DeepSeek..." - @bash run_test.sh --model deepseek-v3 --quick - -# Docker management -docker-up: - @echo "Starting Docker services..." - @docker-compose up -d - @echo "โœ“ Services started" - @echo "Run 'make logs' to view logs" - -docker-down: - @echo "Stopping Docker services..." - @docker-compose down - @echo "โœ“ Services stopped" - -logs: - @docker-compose logs -f - -# Start hanzo net manually -start-net: - @echo "Starting hanzo net with Qwen3..." - hanzo net \ - --name "e2e-test" \ - --port 52415 \ - --models qwen3 \ - --network local \ - --max-jobs 10 - -# Start hanzo dev with local orchestrator -start-dev: - @echo "Starting hanzo dev with local orchestrator..." - hanzo dev \ - --orchestrator local:qwen3 \ - --use-hanzo-net \ - --instances 3 - -# Interactive demo -demo: install - @echo "Starting interactive demo..." - @echo "" - @echo "This will:" - @echo "1. Start hanzo net with Qwen3" - @echo "2. Launch hanzo dev with local orchestrator" - @echo "3. Create a network of local + API agents" - @echo "" - @read -p "Press Enter to continue..." - @bash -c "hanzo net --models qwen3 --network local &" - @sleep 5 - @hanzo dev --orchestrator local:qwen3 --use-hanzo-net - -# Run specific test functions -test-routing: install - @python3 -c "import asyncio; from test_local_ai_orchestration import HanzoNetE2ETest; \ - t = HanzoNetE2ETest(); \ - asyncio.run(t.setup()); \ - asyncio.run(t.start_hanzo_net('qwen3')); \ - network = asyncio.run(t.create_agent_network('qwen3')); \ - asyncio.run(t.test_task_routing(network))" - -test-cost: install - @python3 -c "import asyncio; from test_local_ai_orchestration import HanzoNetE2ETest; \ - t = HanzoNetE2ETest(); \ - asyncio.run(t.setup()); \ - asyncio.run(t.start_hanzo_net('qwen3')); \ - network = asyncio.run(t.create_agent_network('qwen3')); \ - asyncio.run(t.test_cost_optimization(network))" - -test-integration: install - @python3 -c "import asyncio; from test_local_ai_orchestration import HanzoNetE2ETest; \ - t = HanzoNetE2ETest(); \ - asyncio.run(t.setup()); \ - asyncio.run(t.start_hanzo_net('qwen3')); \ - asyncio.run(t.test_hanzo_dev_integration('qwen3'))" - -# Cleanup -clean: - @echo "Cleaning up..." - @-pkill -f "hanzo net" 2>/dev/null || true - @-pkill -f "hanzo dev" 2>/dev/null || true - @-docker-compose down 2>/dev/null || true - @echo "โœ“ Cleanup complete" - -# Check system requirements -check-requirements: - @echo "Checking system requirements..." - @echo "" - @echo -n "Python: " - @python3 --version - @echo -n "Docker: " - @docker --version || echo "Not installed" - @echo -n "Docker Compose: " - @docker-compose --version || docker compose version || echo "Not installed" - @echo "" - @echo "Checking API keys..." - @[ -n "$$OPENAI_API_KEY" ] && echo "โœ“ OPENAI_API_KEY set" || echo "โœ— OPENAI_API_KEY not set" - @[ -n "$$ANTHROPIC_API_KEY" ] && echo "โœ“ ANTHROPIC_API_KEY set" || echo "โœ— ANTHROPIC_API_KEY not set" - @[ -n "$$GOOGLE_API_KEY" ] && echo "โœ“ GOOGLE_API_KEY set" || echo "โœ— GOOGLE_API_KEY not set" - @echo "" - @echo "Checking hanzo installation..." - @which hanzo >/dev/null 2>&1 && echo "โœ“ hanzo CLI installed" || echo "โœ— hanzo CLI not found" - @python3 -c "import hanzo_network" 2>/dev/null && echo "โœ“ hanzo-network installed" || echo "โœ— hanzo-network not found" - -# Performance monitoring -monitor: - @echo "Starting performance monitor..." - @watch -n 1 'echo "=== System Resources ===" && \ - echo "" && \ - echo "CPU & Memory:" && \ - top -l 1 | head -10 && \ - echo "" && \ - echo "Hanzo Processes:" && \ - ps aux | grep -E "hanzo (net|dev)" | grep -v grep && \ - echo "" && \ - echo "Network Ports:" && \ - lsof -i :52415 -i :11434 -i :8080 -i :8000 2>/dev/null | head -10' - -# Run all tests in sequence -test-all: install - @echo "Running all model tests..." - @make test-qwen - @sleep 5 - @make test-llama - @sleep 5 - @make test-mistral - @echo "" - @echo "================================" - @echo "All tests complete!" - @echo "================================" - -.DEFAULT_GOAL := help \ No newline at end of file diff --git a/tests/e2e/hanzo-net-orchestration/README.md b/tests/e2e/hanzo-net-orchestration/README.md deleted file mode 100644 index 7c303b8dd..000000000 --- a/tests/e2e/hanzo-net-orchestration/README.md +++ /dev/null @@ -1,329 +0,0 @@ -# Hanzo Net E2E Test - Local AI Orchestration - -This end-to-end test demonstrates the complete local AI orchestration capability of Hanzo Dev, showcasing: - -1. **Local AI as Orchestrator**: Using Qwen3, Llama 3.2, or other local models to orchestrate -2. **Hybrid Agent Networks**: Local orchestrator managing API-based agents (Claude, GPT-4, Gemini) -3. **Cost Optimization**: 90% cost reduction through intelligent routing -4. **Full MCP Integration**: All agents can use MCP tools and communicate - -## Architecture - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Local Orchestrator (Qwen3) โ”‚ -โ”‚ via hanzo/net โ”‚ -โ”‚ (Strategic Planning & Routing) โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ–ผ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Cost-Optimized Router โ”‚ -โ”‚ (Prefers local for simple tasks) โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ โ”‚ - โ–ผ โ–ผ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Local Workersโ”‚ โ”‚ API Workers โ”‚ -โ”‚ (Qwen3) โ”‚ โ”‚ โ€ข Claude 3.5 Sonnet โ”‚ -โ”‚ Free/Fast โ”‚ โ”‚ โ€ข GPT-4 Turbo โ”‚ -โ”‚ โ”‚ โ”‚ โ€ข Gemini Pro โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## Quick Start - -### 1. Basic Test (Recommended) - -```bash -# Run quick test with default model (qwen3) -./run_test.sh --quick - -# Run with specific model -./run_test.sh --model llama-3.2-3b --quick -``` - -### 2. Full Test with Docker Setup - -```bash -# Setup models and run full test -./run_test.sh --setup - -# This will: -# 1. Download required models -# 2. Start Docker containers -# 3. Run complete test suite -``` - -### 3. Manual Testing - -```bash -# Step 1: Start hanzo net with local model -hanzo net --models qwen3 --network local - -# Step 2: Run hanzo dev with local orchestrator -hanzo dev --orchestrator local:qwen3 --use-hanzo-net - -# Step 3: The system will: -# - Use Qwen3 as the orchestrator -# - Create local workers for simple tasks -# - Use Claude/GPT-4/Gemini for complex tasks -# - Route intelligently to minimize costs -``` - -## Test Scenarios - -### 1. Task Routing Test -Tests that tasks are routed correctly based on complexity: - -- **Simple tasks** โ†’ Local workers (free) - - File listing - - JSON formatting - - String validation - -- **Complex tasks** โ†’ API workers (paid) - - Implementation of algorithms - - System design - - Debugging complex issues - -- **Review tasks** โ†’ Critics - - Code security review - - Performance analysis - -### 2. Cost Optimization Test -Verifies that the system achieves >50% cost savings by: -- Using local models for majority of simple tasks -- Only calling APIs when truly needed -- Tracking task distribution - -### 3. Integration Test -Tests the full `hanzo dev` command with: -- Local orchestrator startup -- Agent network creation -- MCP tool integration -- Graceful shutdown - -## Supported Models - -### Local Models (via hanzo/net) -- **Qwen 2.5/3**: Best for instruction following -- **Llama 3.2**: Good general performance -- **Mistral 7B**: Strong reasoning -- **DeepSeek V3**: Excellent for code - -### API Models (when needed) -- **Claude 3.5 Sonnet**: Complex implementation -- **GPT-4 Turbo**: Analysis and design -- **Gemini Pro**: Multimodal tasks - -## Configuration - -### Environment Variables - -```bash -# API Keys (optional - only for API workers) -export OPENAI_API_KEY="sk-..." -export ANTHROPIC_API_KEY="sk-ant-..." -export GOOGLE_API_KEY="..." - -# Hanzo Net Configuration -export HANZO_NET_PORT=52415 -export HANZO_NET_MODELS="qwen3,llama-3.2-3b" -``` - -### Docker Services - -The test includes Docker Compose configuration for: - -1. **Ollama**: General model serving -2. **LocalAI**: GGUF model support -3. **vLLM**: High-performance inference (GPU) -4. **Text Generation WebUI**: Interactive testing - -Start services: -```bash -docker-compose up -d -``` - -Stop services: -```bash -docker-compose down -``` - -## Expected Results - -### Successful Test Output - -``` -============================================ -HANZO NET E2E TEST - LOCAL AI ORCHESTRATION -============================================ - -Testing with model: qwen3 -============================================ -โœ“ hanzo net started successfully on port 52415 -โœ“ Created local qwen3 orchestrator via hanzo/net -โœ“ Agent network initialized with 7 agents - -=== Testing Task Routing === -โœ“ Simple task โ†’ local_worker_0 -โœ“ Complex task โ†’ claude_worker -โœ“ Review task โ†’ local_critic - -=== Testing Cost Optimization === -Task Distribution: - Local models: 5/8 - API models: 3/8 - Cost savings: ~62.5% - -=== Testing Hanzo Dev Integration === -โœ“ hanzo dev started successfully - -TEST SUMMARY -============================================ -qwen3: - Routing: 5/5 passed - Cost Optimization: โœ“ (62.5% savings) - Hanzo Dev Integration: โœ“ -``` - -### Cost Savings Analysis - -With this setup, you can expect: - -- **90% cost reduction** for simple tasks -- **60-70% overall cost reduction** in typical workflows -- **Full capability** maintained (complex tasks still use best models) - -## Troubleshooting - -### Port Already in Use -```bash -# Check what's using the port -lsof -i :52415 - -# Kill the process or use different port -hanzo net --port 52416 -``` - -### Model Download Issues -```bash -# Download models manually with Ollama -ollama pull qwen2.5:3b -ollama pull llama3.2:3b - -# Or use Hugging Face CLI -huggingface-cli download Qwen/Qwen2.5-3B-Instruct-GGUF -``` - -### API Key Issues -The test will work without API keys but with reduced functionality: -- Local orchestrator and workers will still function -- API-based workers will be skipped -- Cost optimization will show 100% local usage - -### Memory Issues -Adjust memory limits in `docker-compose.yml`: -```yaml -deploy: - resources: - limits: - memory: 8G # Reduce if needed -``` - -## Advanced Usage - -### Custom Model Configuration - -Create a custom model config: -```yaml -# models/custom-model.yaml -name: custom-model -parameters: - model: /path/to/model.gguf - temperature: 0.7 - max_tokens: 2048 -``` - -### Running Specific Tests - -```python -# Run only routing test -python3 -c " -import asyncio -from test_local_ai_orchestration import HanzoNetE2ETest - -async def test(): - t = HanzoNetE2ETest() - await t.setup() - await t.start_hanzo_net('qwen3') - network = await t.create_agent_network('qwen3') - await t.test_task_routing(network) - -asyncio.run(test()) -" -``` - -### Monitoring Performance - -View real-time logs: -```bash -# Hanzo net logs -hanzo net --models qwen3 --network local --verbose - -# Docker logs -docker-compose logs -f - -# System monitoring -htop # CPU/Memory usage -nvidia-smi # GPU usage (if available) -``` - -## CI/CD Integration - -Add to your CI pipeline: - -```yaml -# .github/workflows/e2e-test.yml -name: E2E Test - Local AI Orchestration - -on: [push, pull_request] - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Setup Python - uses: actions/setup-python@v2 - with: - python-version: '3.10' - - - name: Install dependencies - run: | - pip install -e pkg/hanzo/ - pip install -e pkg/hanzo-network/ - - - name: Run E2E Test - run: | - cd tests/e2e/hanzo-net-orchestration - ./run_test.sh --quick --model llama-3.2-3b -``` - -## Summary - -This E2E test demonstrates that Hanzo Dev can: - -1. **Use local AI models as orchestrators** via hanzo/net -2. **Manage hybrid networks** of local and API agents -3. **Optimize costs** by routing intelligently -4. **Maintain full capability** while reducing expenses -5. **Scale from laptop to cloud** seamlessly - -The result is an AI coding assistant that: -- Runs primarily on local/private infrastructure -- Calls expensive APIs only when necessary -- Provides enterprise-grade capabilities -- Reduces costs by up to 90% \ No newline at end of file diff --git a/tests/e2e/hanzo-net-orchestration/compose.yml b/tests/e2e/hanzo-net-orchestration/compose.yml deleted file mode 100644 index d1c66ff3c..000000000 --- a/tests/e2e/hanzo-net-orchestration/compose.yml +++ /dev/null @@ -1,111 +0,0 @@ -version: '3.8' - -services: - # Ollama for running local models - ollama: - image: ollama/ollama:latest - container_name: hanzo-ollama - ports: - - "11434:11434" - volumes: - - ollama_data:/root/.ollama - environment: - - OLLAMA_HOST=0.0.0.0 - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: all - capabilities: [gpu] - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"] - interval: 30s - timeout: 10s - retries: 3 - - # LocalAI for additional model support - localai: - image: quay.io/go-skynet/local-ai:latest - container_name: hanzo-localai - ports: - - "8080:8080" - volumes: - - ./models:/models - - ./images:/tmp/images - environment: - - DEBUG=true - - MODELS_PATH=/models - - GALLERIES=[{"name":"model-gallery","url":"github:go-skynet/model-gallery/index.yaml"}] - - PRELOAD_MODELS=[{"id":"qwen3","name":"qwen3","urls":["https://huggingface.co/Qwen/Qwen2.5-3B-Instruct-GGUF"]}] - deploy: - resources: - limits: - memory: 8G - cpus: '4' - - # Text Generation Web UI for testing - text-generation-webui: - image: atinoda/text-generation-webui:default - container_name: hanzo-text-gen - ports: - - "7860:7860" - - "5000:5000" # API port - volumes: - - ./models:/app/models - - ./characters:/app/characters - - ./presets:/app/presets - environment: - - CLI_ARGS=--api --listen --model-dir /app/models - deploy: - resources: - limits: - memory: 16G - cpus: '8' - - # vLLM for high-performance inference - vllm: - image: vllm/vllm-openai:latest - container_name: hanzo-vllm - ports: - - "8000:8000" - volumes: - - ./models:/models - environment: - - HF_HOME=/models - command: > - --model mistralai/Mistral-7B-Instruct-v0.1 - --host 0.0.0.0 - --port 8000 - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: all - capabilities: [gpu] - limits: - memory: 24G - - # MLX Server for Apple Silicon (Mac only) - # Uncomment if running on Mac with Apple Silicon - # mlx-server: - # build: - # context: . - # dockerfile: Dockerfile.mlx - # container_name: hanzo-mlx - # ports: - # - "5001:5001" - # volumes: - # - ./models:/models - # environment: - # - MLX_MODEL=mlx-community/Llama-3.2-3B-Instruct-4bit - # platform: linux/arm64 - -volumes: - ollama_data: - -networks: - default: - name: hanzo-net - driver: bridge \ No newline at end of file diff --git a/tests/e2e/hanzo-net-orchestration/run_test.sh b/tests/e2e/hanzo-net-orchestration/run_test.sh deleted file mode 100755 index 4fbf60d5a..000000000 --- a/tests/e2e/hanzo-net-orchestration/run_test.sh +++ /dev/null @@ -1,167 +0,0 @@ -#!/bin/bash - -# E2E Test Runner for Hanzo Net Local AI Orchestration -set -e - -echo "==============================================" -echo "Hanzo Net E2E Test Runner" -echo "==============================================" - -# Colors -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -NC='\033[0m' - -# Parse arguments -RUN_SETUP=false -MODEL="qwen3" -QUICK_TEST=false - -while [[ $# -gt 0 ]]; do - case $1 in - --setup) - RUN_SETUP=true - shift - ;; - --model) - MODEL="$2" - shift 2 - ;; - --quick) - QUICK_TEST=true - shift - ;; - --help) - echo "Usage: $0 [options]" - echo "" - echo "Options:" - echo " --setup Run model setup before testing" - echo " --model NAME Specify model to test (default: qwen3)" - echo " --quick Run quick test (single model only)" - echo " --help Show this help message" - exit 0 - ;; - *) - echo "Unknown option: $1" - echo "Use --help for usage information" - exit 1 - ;; - esac -done - -# Check Python version -PYTHON_VERSION=$(python3 --version | cut -d' ' -f2 | cut -d'.' -f1-2) -REQUIRED_VERSION="3.9" - -if [[ $(echo "$PYTHON_VERSION < $REQUIRED_VERSION" | bc) -eq 1 ]]; then - echo -e "${RED}Error: Python $REQUIRED_VERSION or higher required (found $PYTHON_VERSION)${NC}" - exit 1 -fi - -# Run setup if requested -if [ "$RUN_SETUP" = true ]; then - echo -e "${YELLOW}Running model setup...${NC}" - bash setup_models.sh - echo "" -fi - -# Check if hanzo is installed -if ! command -v hanzo >/dev/null 2>&1; then - echo -e "${RED}Error: hanzo CLI not found${NC}" - echo "Please install it first:" - echo " cd ../../.." - echo " pip install -e pkg/hanzo/" - exit 1 -fi - -# Check if hanzo-network is installed -if ! python3 -c "import hanzo_network" 2>/dev/null; then - echo -e "${YELLOW}Installing hanzo-network...${NC}" - pip install -e ../../../pkg/hanzo-network/ -fi - -# Export API keys if they exist in .env -if [ -f .env ]; then - export $(grep -v '^#' .env | xargs) -fi - -# Start hanzo net if not running -echo "Checking hanzo net status..." -if ! nc -z localhost 52415 2>/dev/null; then - echo -e "${YELLOW}Starting hanzo net with $MODEL...${NC}" - - hanzo net \ - --name "e2e-test" \ - --port 52415 \ - --models "$MODEL" \ - --network local \ - --max-jobs 10 & - - HANZO_NET_PID=$! - - # Wait for hanzo net to start - echo -n "Waiting for hanzo net to start" - for i in {1..30}; do - if nc -z localhost 52415 2>/dev/null; then - echo -e " ${GREEN}โœ“${NC}" - break - fi - echo -n "." - sleep 1 - done - - if ! nc -z localhost 52415 2>/dev/null; then - echo -e " ${RED}โœ—${NC}" - echo "Failed to start hanzo net" - exit 1 - fi -else - echo -e "${GREEN}โœ“ hanzo net already running${NC}" -fi - -# Run the test -echo "" -echo "Starting E2E test..." -echo "==============================================" - -if [ "$QUICK_TEST" = true ]; then - # Quick test - single model only - python3 -c " -import asyncio -import sys -sys.path.insert(0, '../../..') -from tests.e2e.hanzo_net_orchestration.test_local_ai_orchestration import HanzoNetE2ETest - -async def quick_test(): - test = HanzoNetE2ETest() - test.models_to_test = ['$MODEL'] - await test.run_full_test() - -asyncio.run(quick_test()) -" -else - # Full test - python3 test_local_ai_orchestration.py -fi - -TEST_RESULT=$? - -# Cleanup -if [ ! -z "$HANZO_NET_PID" ]; then - echo "" - echo "Stopping hanzo net..." - kill $HANZO_NET_PID 2>/dev/null || true -fi - -# Report results -echo "" -echo "==============================================" -if [ $TEST_RESULT -eq 0 ]; then - echo -e "${GREEN}โœ“ E2E Test Passed${NC}" -else - echo -e "${RED}โœ— E2E Test Failed${NC}" -fi -echo "==============================================" - -exit $TEST_RESULT \ No newline at end of file diff --git a/tests/e2e/hanzo-net-orchestration/setup_models.sh b/tests/e2e/hanzo-net-orchestration/setup_models.sh deleted file mode 100755 index 73556b779..000000000 --- a/tests/e2e/hanzo-net-orchestration/setup_models.sh +++ /dev/null @@ -1,286 +0,0 @@ -#!/bin/bash - -# Setup script for E2E test models -set -e - -echo "================================================" -echo "Hanzo Net E2E Test - Model Setup" -echo "================================================" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Create models directory -mkdir -p models - -# Function to check if command exists -command_exists() { - command -v "$1" >/dev/null 2>&1 -} - -# Function to download model with Ollama -download_ollama_model() { - local model=$1 - echo -e "${YELLOW}Downloading $model with Ollama...${NC}" - - if command_exists ollama; then - ollama pull $model - echo -e "${GREEN}โœ“ Downloaded $model${NC}" - else - echo -e "${RED}โœ— Ollama not installed. Install from: https://ollama.ai${NC}" - return 1 - fi -} - -# Function to download Hugging Face model -download_hf_model() { - local model_id=$1 - local model_name=$2 - - echo -e "${YELLOW}Downloading $model_name from Hugging Face...${NC}" - - if command_exists huggingface-cli; then - huggingface-cli download $model_id --local-dir ./models/$model_name - echo -e "${GREEN}โœ“ Downloaded $model_name${NC}" - else - echo -e "${YELLOW}Installing huggingface-hub...${NC}" - pip install huggingface-hub - huggingface-cli download $model_id --local-dir ./models/$model_name - fi -} - -# Check for Docker -if ! command_exists docker; then - echo -e "${RED}Docker is not installed. Please install Docker first.${NC}" - exit 1 -fi - -# Check for Docker Compose -if ! command_exists docker-compose && ! docker compose version >/dev/null 2>&1; then - echo -e "${RED}Docker Compose is not installed. Please install Docker Compose.${NC}" - exit 1 -fi - -echo "" -echo "Setting up local models for testing..." -echo "" - -# 1. Setup Ollama models (if Ollama is installed locally) -if command_exists ollama; then - echo "Setting up Ollama models..." - - # Start Ollama service if not running - if ! pgrep -x "ollama" > /dev/null; then - echo "Starting Ollama service..." - ollama serve & - sleep 5 - fi - - # Download models - download_ollama_model "qwen2.5:3b" - download_ollama_model "llama3.2:3b" - download_ollama_model "mistral:7b" - download_ollama_model "deepseek-coder:6.7b" - -else - echo -e "${YELLOW}Ollama not installed locally. Models will be downloaded in Docker container.${NC}" -fi - -# 2. Download GGUF models for LocalAI -echo "" -echo "Downloading GGUF models for LocalAI..." - -# Create models directory structure -mkdir -p models/gguf - -# Download Qwen 3B GGUF -if [ ! -f "models/gguf/qwen2.5-3b-instruct.gguf" ]; then - echo "Downloading Qwen 2.5 3B GGUF..." - wget -O models/gguf/qwen2.5-3b-instruct.gguf \ - https://huggingface.co/Qwen/Qwen2.5-3B-Instruct-GGUF/resolve/main/qwen2.5-3b-instruct-q4_k_m.gguf -fi - -# Download Llama 3.2 3B GGUF -if [ ! -f "models/gguf/llama-3.2-3b.gguf" ]; then - echo "Downloading Llama 3.2 3B GGUF..." - wget -O models/gguf/llama-3.2-3b.gguf \ - https://huggingface.co/NousResearch/Llama-3.2-3B-Instruct-GGUF/resolve/main/Llama-3.2-3B-Instruct-Q4_K_M.gguf -fi - -# 3. Create LocalAI model configurations -echo "" -echo "Creating LocalAI model configurations..." - -# Qwen config -cat > models/qwen3.yaml <" - - "<|endoftext|>" -template: - chat: | - <|im_start|>system - {{.System}}<|im_end|> - <|im_start|>user - {{.Input}}<|im_end|> - <|im_start|>assistant -EOF - -# Llama config -cat > models/llama32.yaml <" - - "<|eot_id|>" -template: - chat: | - <|begin_of_text|><|start_header_id|>system<|end_header_id|> - {{.System}}<|eot_id|><|start_header_id|>user<|end_header_id|> - {{.Input}}<|eot_id|><|start_header_id|>assistant<|end_header_id|> -EOF - -# 4. Setup API keys check -echo "" -echo "Checking API keys..." - -check_api_key() { - local key_name=$1 - local service=$2 - - if [ -z "${!key_name}" ]; then - echo -e "${YELLOW}โš  $key_name not set. $service will not be available.${NC}" - echo " Export it with: export $key_name='your-key-here'" - return 1 - else - echo -e "${GREEN}โœ“ $key_name is set for $service${NC}" - return 0 - fi -} - -check_api_key "OPENAI_API_KEY" "OpenAI (GPT-4/Codex)" -check_api_key "ANTHROPIC_API_KEY" "Anthropic (Claude)" -check_api_key "GOOGLE_API_KEY" "Google (Gemini)" - -# 5. Create .env file for Docker Compose -echo "" -echo "Creating .env file..." - -cat > .env </dev/null 2>&1; then - COMPOSE_CMD="docker compose" -else - COMPOSE_CMD="docker-compose" -fi - -$COMPOSE_CMD up -d - -# Wait for services to be healthy -echo "" -echo "Waiting for services to start..." - -wait_for_service() { - local service=$1 - local port=$2 - local max_attempts=30 - local attempt=0 - - echo -n "Waiting for $service on port $port..." - - while [ $attempt -lt $max_attempts ]; do - if nc -z localhost $port 2>/dev/null; then - echo -e " ${GREEN}โœ“${NC}" - return 0 - fi - echo -n "." - sleep 2 - attempt=$((attempt + 1)) - done - - echo -e " ${RED}โœ— (timeout)${NC}" - return 1 -} - -wait_for_service "Ollama" 11434 -wait_for_service "LocalAI" 8080 -wait_for_service "vLLM" 8000 - -# 7. Test model availability -echo "" -echo "Testing model availability..." - -# Test Ollama -if curl -s http://localhost:11434/api/tags >/dev/null 2>&1; then - echo -e "${GREEN}โœ“ Ollama is responding${NC}" - - # List available models - echo " Available Ollama models:" - curl -s http://localhost:11434/api/tags | jq -r '.models[].name' | sed 's/^/ - /' -else - echo -e "${RED}โœ— Ollama is not responding${NC}" -fi - -# Test LocalAI -if curl -s http://localhost:8080/v1/models >/dev/null 2>&1; then - echo -e "${GREEN}โœ“ LocalAI is responding${NC}" -else - echo -e "${RED}โœ— LocalAI is not responding${NC}" -fi - -# Test vLLM -if curl -s http://localhost:8000/v1/models >/dev/null 2>&1; then - echo -e "${GREEN}โœ“ vLLM is responding${NC}" -else - echo -e "${YELLOW}โš  vLLM may need GPU support${NC}" -fi - -echo "" -echo "================================================" -echo "Setup Complete!" -echo "================================================" -echo "" -echo "You can now run the E2E test with:" -echo " python test_local_ai_orchestration.py" -echo "" -echo "To stop services:" -echo " $COMPOSE_CMD down" -echo "" -echo "To view logs:" -echo " $COMPOSE_CMD logs -f" -echo "" \ No newline at end of file diff --git a/tests/e2e/hanzo-net-orchestration/test_local_ai_orchestration.py b/tests/e2e/hanzo-net-orchestration/test_local_ai_orchestration.py deleted file mode 100644 index 8171f1331..000000000 --- a/tests/e2e/hanzo-net-orchestration/test_local_ai_orchestration.py +++ /dev/null @@ -1,598 +0,0 @@ -#!/usr/bin/env python3 -""" -End-to-End Test: Hanzo Net Local AI Orchestration - -This test demonstrates: -1. Starting hanzo net with local AI models -2. Loading Qwen3 (or other local models) as orchestrator -3. Using local AI to orchestrate a subnet of API models (Claude, Codex, Gemini) -4. Cost-optimized routing between local and API models -5. Full MCP tool integration across all agents -""" - -import os -import sys -import json -import time -import socket -import asyncio -import logging -import subprocess -from typing import Any, Dict, List, Optional -from pathlib import Path - -# Add parent directories to path -sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent)) - -from hanzo_network import ( - ModelConfig, - NetworkState, - ModelProvider, - create_agent, - create_network, - create_local_agent, - create_routing_agent, - create_distributed_network, -) -from hanzo_network.local_network import check_local_llm_status - -# Configure logging -logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") -logger = logging.getLogger(__name__) - - -class HanzoNetE2ETest: - """End-to-end test for Hanzo Net orchestration.""" - - def __init__(self): - self.hanzo_net_process = None - self.hanzo_dev_process = None - self.test_results = [] - self.models_to_test = [ - "qwen3", # Qwen 3 - "llama-3.2-3b", # Llama 3.2 3B - "deepseek-v3", # DeepSeek V3 - "mistral-7b", # Mistral 7B - ] - self.api_models = [ - "claude-3-5-sonnet-20241022", - "gpt-4-turbo-preview", - "gemini-pro", - ] - - async def setup(self): - """Set up test environment.""" - logger.info("Setting up E2E test environment...") - - # Check if hanzo net is installed - result = subprocess.run(["which", "hanzo"], capture_output=True, text=True) - if result.returncode != 0: - raise RuntimeError("hanzo CLI not found. Please install: pip install -e pkg/hanzo/") - - # Check for required environment variables - self.check_api_keys() - - # Create test workspace - self.workspace = Path.home() / ".hanzo" / "e2e-test" - self.workspace.mkdir(parents=True, exist_ok=True) - - logger.info(f"Test workspace: {self.workspace}") - - def check_api_keys(self): - """Check for required API keys.""" - required_keys = { - "OPENAI_API_KEY": "OpenAI (GPT-4/Codex)", - "ANTHROPIC_API_KEY": "Anthropic (Claude)", - "GOOGLE_API_KEY": "Google (Gemini)", - } - - missing_keys = [] - for key, service in required_keys.items(): - if not os.getenv(key): - missing_keys.append(f"{key} ({service})") - - if missing_keys: - logger.warning(f"Missing API keys: {', '.join(missing_keys)}") - logger.warning("Some API models will not be available") - - async def start_hanzo_net(self, model: str = "qwen3", port: int = 52415) -> bool: - """Start hanzo net with specified model.""" - logger.info(f"Starting hanzo net with model: {model} on port {port}") - - # Check if port is already in use - if self.is_port_open("localhost", port): - logger.warning(f"Port {port} already in use, attempting to use existing instance") - return True - - try: - cmd = [ - "hanzo", - "net", - "--name", - f"e2e-test-{model}", - "--port", - str(port), - "--models", - model, - "--network", - "local", - "--max-jobs", - "10", - ] - - logger.info(f"Command: {' '.join(cmd)}") - - self.hanzo_net_process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - - # Wait for hanzo net to start - for i in range(30): # 30 seconds timeout - await asyncio.sleep(1) - if self.is_port_open("localhost", port): - logger.info(f"โœ“ hanzo net started successfully on port {port}") - return True - - # Check if process has died - if self.hanzo_net_process.poll() is not None: - stdout, stderr = self.hanzo_net_process.communicate() - logger.error(f"hanzo net failed to start:\nSTDOUT: {stdout}\nSTDERR: {stderr}") - return False - - logger.error("Timeout waiting for hanzo net to start") - return False - - except Exception as e: - logger.error(f"Failed to start hanzo net: {e}") - return False - - def is_port_open(self, host: str, port: int) -> bool: - """Check if a port is open.""" - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - result = sock.connect_ex((host, port)) - sock.close() - return result == 0 - - async def create_local_orchestrator(self, model: str, port: int = 52415): - """Create a local AI orchestrator.""" - logger.info(f"Creating local orchestrator with {model}") - - orchestrator = create_local_agent( - name=f"{model}_orchestrator", - description=f"Local {model} orchestrator for E2E test", - system="""You are a local AI orchestrator managing a network of specialized agents. - - Your role: - 1. Coordinate between local and API-based agents - 2. Route tasks based on complexity and cost - 3. Ensure quality through System 2 thinking - 4. Optimize for cost-effectiveness - - Available agents: - - Local workers: Fast, free, good for simple tasks - - API workers: Powerful but expensive, for complex tasks - - Critics: Review and improve output quality - - Always prefer local models when possible to reduce costs.""", - local_model=model, - base_url=f"http://localhost:{port}", - tools=[], - ) - - return orchestrator - - async def create_agent_network(self, orchestrator_model: str = "qwen3"): - """Create a full agent network with local orchestrator and mixed workers.""" - logger.info("Creating agent network...") - - agents = [] - - # 1. Create local orchestrator - orchestrator = await self.create_local_orchestrator(orchestrator_model) - agents.append(orchestrator) - - # 2. Create local worker agents - for i in range(2): - worker = create_local_agent( - name=f"local_worker_{i}", - description=f"Local worker {i} for simple tasks", - system="You are a local worker handling simple code tasks efficiently.", - local_model=orchestrator_model, - base_url="http://localhost:52415", - ) - agents.append(worker) - logger.info(f" Created local worker {i}") - - # 3. Create API-based worker agents (if keys available) - if os.getenv("ANTHROPIC_API_KEY"): - claude_worker = create_agent( - name="claude_worker", - description="Claude worker for complex implementation", - model=ModelConfig( - provider=ModelProvider.ANTHROPIC, - model="claude-3-5-sonnet-20241022", - api_key=os.getenv("ANTHROPIC_API_KEY"), - ), - system="You are Claude, specialized in complex code implementation.", - ) - agents.append(claude_worker) - logger.info(" Created Claude worker") - - if os.getenv("OPENAI_API_KEY"): - gpt_worker = create_agent( - name="gpt4_worker", - description="GPT-4 worker for analysis and design", - model=ModelConfig( - provider=ModelProvider.OPENAI, - model="gpt-4-turbo-preview", - api_key=os.getenv("OPENAI_API_KEY"), - ), - system="You are GPT-4, specialized in code analysis and system design.", - ) - agents.append(gpt_worker) - logger.info(" Created GPT-4 worker") - - # Also create a Codex-style worker - codex_worker = create_agent( - name="codex_worker", - description="Code completion specialist", - model=ModelConfig( - provider=ModelProvider.OPENAI, - model="gpt-4-turbo-preview", # Using GPT-4 as Codex replacement - api_key=os.getenv("OPENAI_API_KEY"), - temperature=0.2, # Lower temperature for code - ), - system="You are a code completion specialist. Generate precise, efficient code.", - ) - agents.append(codex_worker) - logger.info(" Created Codex-style worker") - - if os.getenv("GOOGLE_API_KEY"): - gemini_worker = create_agent( - name="gemini_worker", - description="Gemini worker for multimodal tasks", - model=ModelConfig( - provider=ModelProvider.GOOGLE, - model="gemini-pro", - api_key=os.getenv("GOOGLE_API_KEY"), - ), - system="You are Gemini, capable of handling diverse tasks including code and analysis.", - ) - agents.append(gemini_worker) - logger.info(" Created Gemini worker") - - # 4. Create critic agents for quality assurance - critic = create_local_agent( - name="local_critic", - description="Local critic for code review", - system="""You are a code quality critic. - - Review code for: - - Correctness and bugs - - Performance issues - - Security vulnerabilities - - Best practices - - Provide specific, actionable feedback.""", - local_model=orchestrator_model, - base_url="http://localhost:52415", - ) - agents.append(critic) - logger.info(" Created local critic") - - # 5. Create intelligent router - router = create_routing_agent( - agent=orchestrator, - system="""Route tasks to the most appropriate agent: - - Simple tasks (listing, formatting, validation) โ†’ local_worker_* - Complex implementation โ†’ claude_worker or gpt4_worker - Code completion โ†’ codex_worker - Multimodal or diverse tasks โ†’ gemini_worker - Code review โ†’ local_critic - - Optimize for cost by preferring local models when possible.""", - ) - - # 6. Create the network - network = create_network(agents=agents, router=router, default_agent=orchestrator.name) - - logger.info(f"โœ“ Created agent network with {len(agents)} agents") - return network - - async def test_task_routing(self, network): - """Test that tasks are routed correctly between local and API models.""" - logger.info("\n=== Testing Task Routing ===") - - test_cases = [ - { - "task": "List all Python files in the current directory", - "expected_agent": "local_worker", - "reason": "Simple file listing task", - }, - { - "task": "Format this JSON: {'key':'value','nested':{'a':1}}", - "expected_agent": "local_worker", - "reason": "Simple formatting task", - }, - { - "task": "Implement a binary search tree with insert, delete, and search operations", - "expected_agent": "claude_worker|gpt4_worker", - "reason": "Complex implementation task", - }, - { - "task": "Complete this function: def fibonacci(n): # Calculate nth Fibonacci number", - "expected_agent": "codex_worker|gpt4_worker", - "reason": "Code completion task", - }, - { - "task": "Review this code for security issues: eval(user_input)", - "expected_agent": "local_critic", - "reason": "Code review task", - }, - ] - - results = [] - for test in test_cases: - logger.info(f"\nTest: {test['task'][:50]}...") - - try: - # Create state and run network - state = NetworkState() - result = await network.run(prompt=test["task"], state=state) - - # Check which agent handled it - last_message = state.messages[-1] if state.messages else None - agent_used = last_message.get("agent", "unknown") if last_message else "unknown" - - # Validate routing - expected_agents = test["expected_agent"].split("|") - success = any(expected in agent_used for expected in expected_agents) - - results.append( - { - "task": test["task"][:50], - "expected": test["expected_agent"], - "actual": agent_used, - "success": success, - "reason": test["reason"], - } - ) - - logger.info(f" Agent used: {agent_used}") - logger.info(f" Success: {'โœ“' if success else 'โœ—'}") - - except Exception as e: - logger.error(f" Error: {e}") - results.append({"task": test["task"][:50], "error": str(e), "success": False}) - - # Print summary - logger.info("\n=== Routing Test Summary ===") - successful = sum(1 for r in results if r.get("success")) - logger.info(f"Passed: {successful}/{len(results)}") - - for result in results: - status = "โœ“" if result.get("success") else "โœ—" - logger.info(f" {status} {result.get('task', 'Unknown')}") - if not result.get("success"): - logger.info(f" Expected: {result.get('expected', 'N/A')}") - logger.info(f" Actual: {result.get('actual', result.get('error', 'N/A'))}") - - return results - - async def test_cost_optimization(self, network): - """Test that the system optimizes for cost by using local models when possible.""" - logger.info("\n=== Testing Cost Optimization ===") - - # Track which models handle which tasks - task_distribution = {"local": 0, "api": 0} - - # Run a series of mixed tasks - tasks = [ - "Check if file exists: config.json", - "Validate this email: user@example.com", - "Count lines in a file", - "Convert string to uppercase", - "Parse a simple CSV line", - # These should go to API models - "Design a microservices architecture for an e-commerce platform", - "Debug this complex concurrency issue in a distributed system", - "Optimize this machine learning pipeline for better performance", - ] - - for task in tasks: - try: - state = NetworkState() - result = await network.run(prompt=task, state=state) - - # Check which type of agent handled it - last_message = state.messages[-1] if state.messages else None - agent_used = last_message.get("agent", "") if last_message else "" - - if "local" in agent_used.lower(): - task_distribution["local"] += 1 - else: - task_distribution["api"] += 1 - - except Exception as e: - logger.error(f"Error processing task: {e}") - - # Calculate cost savings - local_ratio = task_distribution["local"] / len(tasks) if tasks else 0 - cost_savings = local_ratio * 100 - - logger.info(f"\nTask Distribution:") - logger.info(f" Local models: {task_distribution['local']}/{len(tasks)}") - logger.info(f" API models: {task_distribution['api']}/{len(tasks)}") - logger.info(f" Cost savings: ~{cost_savings:.1f}%") - - return { - "distribution": task_distribution, - "cost_savings_percent": cost_savings, - "success": cost_savings > 50, # Expect >50% to use local models - } - - async def test_hanzo_dev_integration(self, model: str = "qwen3"): - """Test full hanzo dev integration with local orchestrator.""" - logger.info("\n=== Testing Hanzo Dev Integration ===") - - try: - # Start hanzo dev with local orchestrator - cmd = [ - "hanzo", - "dev", - "--orchestrator", - f"local:{model}", - "--instances", - "3", - "--use-hanzo-net", - "--workspace", - str(self.workspace), - "--no-repl", # Non-interactive mode - "--no-monitor", - ] - - logger.info(f"Starting hanzo dev: {' '.join(cmd)}") - - # Run hanzo dev for a short time to test initialization - process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - - # Give it time to initialize - await asyncio.sleep(10) - - # Check if it's still running - if process.poll() is None: - logger.info("โœ“ hanzo dev started successfully") - - # Terminate gracefully - process.terminate() - await asyncio.sleep(2) - - if process.poll() is None: - process.kill() - - return True - else: - stdout, stderr = process.communicate() - logger.error(f"hanzo dev failed:\nSTDOUT: {stdout}\nSTDERR: {stderr}") - return False - - except Exception as e: - logger.error(f"Failed to test hanzo dev: {e}") - return False - - async def run_full_test(self): - """Run the complete E2E test suite.""" - logger.info("\n" + "=" * 60) - logger.info("HANZO NET E2E TEST - LOCAL AI ORCHESTRATION") - logger.info("=" * 60) - - all_results = {} - - try: - # Setup - await self.setup() - - # Test with different local models - for model in self.models_to_test: - logger.info(f"\n{'=' * 60}") - logger.info(f"Testing with model: {model}") - logger.info(f"{'=' * 60}") - - model_results = {} - - # Start hanzo net with the model - if await self.start_hanzo_net(model): - # Create agent network - network = await self.create_agent_network(model) - - # Run tests - model_results["routing"] = await self.test_task_routing(network) - model_results["cost_optimization"] = await self.test_cost_optimization(network) - model_results["hanzo_dev"] = await self.test_hanzo_dev_integration(model) - - # Stop hanzo net - if self.hanzo_net_process: - self.hanzo_net_process.terminate() - await asyncio.sleep(2) - self.hanzo_net_process = None - else: - logger.warning(f"Skipping tests for {model} - failed to start hanzo net") - model_results["error"] = "Failed to start hanzo net" - - all_results[model] = model_results - - # Brief pause between models - await asyncio.sleep(5) - - # Print final summary - self.print_summary(all_results) - - except Exception as e: - logger.error(f"Test failed: {e}") - raise - finally: - # Cleanup - await self.cleanup() - - def print_summary(self, results: Dict): - """Print test summary.""" - logger.info("\n" + "=" * 60) - logger.info("TEST SUMMARY") - logger.info("=" * 60) - - for model, model_results in results.items(): - logger.info(f"\n{model}:") - - if "error" in model_results: - logger.info(f" โœ— {model_results['error']}") - continue - - # Routing tests - if "routing" in model_results: - routing = model_results["routing"] - passed = sum(1 for r in routing if r.get("success", False)) - logger.info(f" Routing: {passed}/{len(routing)} passed") - - # Cost optimization - if "cost_optimization" in model_results: - cost = model_results["cost_optimization"] - if cost.get("success"): - logger.info(f" Cost Optimization: โœ“ ({cost['cost_savings_percent']:.1f}% savings)") - else: - logger.info(f" Cost Optimization: โœ—") - - # Hanzo dev integration - if "hanzo_dev" in model_results: - if model_results["hanzo_dev"]: - logger.info(f" Hanzo Dev Integration: โœ“") - else: - logger.info(f" Hanzo Dev Integration: โœ—") - - async def cleanup(self): - """Clean up test resources.""" - logger.info("\nCleaning up...") - - # Stop hanzo net - if self.hanzo_net_process: - self.hanzo_net_process.terminate() - await asyncio.sleep(2) - if self.hanzo_net_process.poll() is None: - self.hanzo_net_process.kill() - - # Stop hanzo dev - if self.hanzo_dev_process: - self.hanzo_dev_process.terminate() - await asyncio.sleep(2) - if self.hanzo_dev_process.poll() is None: - self.hanzo_dev_process.kill() - - logger.info("Cleanup complete") - - -async def main(): - """Main entry point.""" - test = HanzoNetE2ETest() - await test.run_full_test() - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/tests/e2e/hanzo-net-orchestration/test_local_ai_simple.py b/tests/e2e/hanzo-net-orchestration/test_local_ai_simple.py deleted file mode 100644 index bccace690..000000000 --- a/tests/e2e/hanzo-net-orchestration/test_local_ai_simple.py +++ /dev/null @@ -1,265 +0,0 @@ -#!/usr/bin/env python3 -""" -Simplified E2E Test for Hanzo Net + Hanzo Dev Integration - -This test demonstrates the core functionality without requiring hanzo-network package. -""" - -import os -import sys -import json -import time -import socket -import asyncio -import logging -import subprocess -from typing import Dict, List, Optional -from pathlib import Path - -# Configure logging -logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") -logger = logging.getLogger(__name__) - - -class SimpleHanzoNetTest: - """Simplified E2E test for hanzo net orchestration.""" - - def __init__(self): - self.hanzo_net_process = None - self.hanzo_dev_process = None - self.workspace = Path.home() / ".hanzo" / "e2e-test" - self.workspace.mkdir(parents=True, exist_ok=True) - - def is_port_open(self, host: str, port: int) -> bool: - """Check if a port is open.""" - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - result = sock.connect_ex((host, port)) - sock.close() - return result == 0 - - async def test_hanzo_net_startup(self, model: str = "llama-3.2-3b") -> bool: - """Test that hanzo net can start with a local model.""" - logger.info(f"\n=== Testing Hanzo Net Startup with {model} ===") - - port = 52415 - - # Check if already running - if self.is_port_open("localhost", port): - logger.info(f"โœ“ Port {port} already in use (hanzo net may be running)") - return True - - try: - # Start hanzo net - cmd = [ - "hanzo", - "net", - "--name", - "e2e-test", - "--port", - str(port), - "--models", - model, - "--network", - "local", - ] - - logger.info(f"Starting: {' '.join(cmd)}") - - self.hanzo_net_process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - - # Wait for startup - for i in range(30): - await asyncio.sleep(1) - if self.is_port_open("localhost", port): - logger.info(f"โœ“ hanzo net started on port {port}") - return True - - # Check if process died - if self.hanzo_net_process.poll() is not None: - stdout, stderr = self.hanzo_net_process.communicate() - logger.error(f"hanzo net failed to start") - logger.error(f"STDERR: {stderr}") - return False - - logger.error("Timeout waiting for hanzo net") - return False - - except Exception as e: - logger.error(f"Error starting hanzo net: {e}") - return False - - async def test_hanzo_dev_with_local(self, model: str = "llama-3.2-3b") -> bool: - """Test hanzo dev with local orchestrator.""" - logger.info(f"\n=== Testing Hanzo Dev with Local Orchestrator ===") - - try: - # Start hanzo dev with local orchestrator - cmd = [ - "hanzo", - "dev", - "--orchestrator", - f"local:{model}", - "--instances", - "2", - "--use-hanzo-net", - "--workspace", - str(self.workspace), - ] - - logger.info(f"Starting: {' '.join(cmd)}") - - # Start process and capture output - process = subprocess.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - bufsize=1, - ) - - # Read output for a few seconds - start_time = time.time() - output_lines = [] - success_indicators = [ - "Agent network initialized", - "Created local", - "orchestrator", - "hanzo/net started", - ] - - while time.time() - start_time < 15: # 15 second timeout - line = process.stdout.readline() - if line: - output_lines.append(line.strip()) - logger.info(f" {line.strip()}") - - # Check for success indicators - for indicator in success_indicators: - if indicator.lower() in line.lower(): - logger.info(f"โœ“ Found: {indicator}") - - # Check if process died - if process.poll() is not None: - break - - await asyncio.sleep(0.1) - - # Terminate the process - process.terminate() - await asyncio.sleep(2) - if process.poll() is None: - process.kill() - - # Check if we saw success indicators - full_output = "\n".join(output_lines) - if any( - indicator in full_output.lower() for indicator in ["network initialized", "orchestrator", "started"] - ): - logger.info("โœ“ hanzo dev initialized successfully") - return True - else: - logger.warning("Could not confirm successful initialization") - return False - - except Exception as e: - logger.error(f"Error testing hanzo dev: {e}") - return False - - async def test_cost_optimization_config(self) -> bool: - """Test that cost optimization is configured correctly.""" - logger.info("\n=== Testing Cost Optimization Configuration ===") - - # Check if the dev.py file has cost optimization logic - dev_file = Path(__file__).parent.parent.parent.parent / "pkg" / "hanzo" / "src" / "hanzo" / "dev.py" - - if dev_file.exists(): - content = dev_file.read_text() - - checks = { - "CostOptimizedRouter": "Cost-optimized router class", - "local_workers": "Local worker configuration", - "simple_keywords": "Simple task detection", - "complex_keywords": "Complex task detection", - "local models preferred": "Local model preference", - } - - results = [] - for key, description in checks.items(): - if key.lower() in content.lower(): - logger.info(f" โœ“ {description} found") - results.append(True) - else: - logger.warning(f" โœ— {description} not found") - results.append(False) - - success = all(results) - if success: - logger.info("โœ“ Cost optimization properly configured") - else: - logger.warning("โš  Some cost optimization features missing") - - return success - else: - logger.error(f"dev.py not found at {dev_file}") - return False - - async def run_all_tests(self): - """Run all tests.""" - logger.info("\n" + "=" * 60) - logger.info("HANZO NET SIMPLE E2E TEST") - logger.info("=" * 60) - - results = {} - - try: - # Test 1: Hanzo net startup - results["hanzo_net"] = await self.test_hanzo_net_startup() - - # Test 2: Hanzo dev with local orchestrator - results["hanzo_dev"] = await self.test_hanzo_dev_with_local() - - # Test 3: Cost optimization configuration - results["cost_optimization"] = await self.test_cost_optimization_config() - - # Summary - logger.info("\n" + "=" * 60) - logger.info("TEST SUMMARY") - logger.info("=" * 60) - - for test_name, success in results.items(): - status = "โœ“ PASSED" if success else "โœ— FAILED" - logger.info(f"{test_name}: {status}") - - all_passed = all(results.values()) - - if all_passed: - logger.info("\nโœ“ All tests passed!") - else: - logger.info("\nโœ— Some tests failed") - - return all_passed - - finally: - # Cleanup - if self.hanzo_net_process: - self.hanzo_net_process.terminate() - await asyncio.sleep(1) - if self.hanzo_net_process.poll() is None: - self.hanzo_net_process.kill() - - if self.hanzo_dev_process: - self.hanzo_dev_process.terminate() - await asyncio.sleep(1) - if self.hanzo_dev_process.poll() is None: - self.hanzo_dev_process.kill() - - -async def main(): - """Main entry point.""" - test = SimpleHanzoNetTest() - success = await test.run_all_tests() - sys.exit(0 if success else 1) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/tests/mock_fixtures.py b/tests/mock_fixtures.py deleted file mode 100644 index bc3be47a8..000000000 --- a/tests/mock_fixtures.py +++ /dev/null @@ -1,412 +0,0 @@ -# Mock fixtures for integration tests -import json - -import respx -import pytest -from httpx import Response - - -@pytest.fixture -def mock_api(request): - """Mock API responses for integration tests""" - # Don't set up mocks if the test is using respx_mock directly - if "respx_mock" in request.fixturenames: - yield None - return - - with respx.mock(base_url="http://127.0.0.1:4010", assert_all_called=False) as respx_mock: - # Audio endpoints - respx_mock.post("/audio/speech").mock(return_value=Response(200, json={})) - respx_mock.post("/audio/transcriptions").mock(return_value=Response(200, json={})) - - # Batch endpoints - respx_mock.post("/batch/cancel").mock(return_value=Response(200, json={})) - respx_mock.post("/batches").mock(return_value=Response(200, json={"id": "batch-123"})) - respx_mock.get("/batches").mock(return_value=Response(200, json={"data": []})) - respx_mock.route(method="GET", path__regex=r"/batches/.*").mock( - return_value=Response(200, json={"id": "batch-123"}) - ) - respx_mock.route(method="POST", path__regex=r"/batches/.*/cancel").mock(return_value=Response(200, json={})) - - # Cache endpoints - respx_mock.get("/cache/ping").mock( - return_value=Response( - 200, - json={ - "status": "healthy", - "cache_type": "redis", - "ping_response": True, - "set_cache_response": None, - "llm_cache_params": None, - "health_check_cache_params": None, - }, - ) - ) - respx_mock.post("/cache/delete").mock(return_value=Response(200, json={})) - respx_mock.post("/cache/flushall").mock(return_value=Response(200, json={})) - respx_mock.get("/cache/redis/ping").mock(return_value=Response(200, json={"status": "ok"})) - respx_mock.delete("/cache").mock(return_value=Response(200, json={})) - respx_mock.get("/cache/flush").mock(return_value=Response(200, json={})) - - # Chat completions - respx_mock.post("/chat/completions").mock( - return_value=Response( - 200, - json={ - "id": "chatcmpl-123", - "object": "chat.completion", - "created": 1677652288, - "model": "gpt-3.5-turbo", - "choices": [ - { - "index": 0, - "message": {"role": "assistant", "content": "Hello!"}, - "finish_reason": "stop", - } - ], - }, - ) - ) - - # Config endpoints - respx_mock.post("/config/pass_through_endpoints").mock(return_value=Response(200, json={})) - respx_mock.post("/config/pass_through_endpoint").mock(return_value=Response(200, json={})) - respx_mock.route(method="PUT", path__regex=r"/config/pass_through_endpoints/.*").mock( - return_value=Response(200, json={}) - ) - respx_mock.get("/config/pass_through_endpoints").mock(return_value=Response(200, json={"data": []})) - respx_mock.get("/config/pass_through_endpoint").mock(return_value=Response(200, json={"endpoints": []})) - respx_mock.delete("/config/pass_through_endpoint").mock(return_value=Response(200, json={"endpoints": []})) - respx_mock.route(method="DELETE", path__regex=r"/config/pass_through_endpoints/.*").mock( - return_value=Response(200, json={}) - ) - - # Engine endpoints - respx_mock.route(method="POST", path__regex=r"/engines/.*/chat/completions").mock( - return_value=Response(200, json={}) - ) - - # Files - respx_mock.route(method="GET", path__regex=r"/files/.*/content").mock( - return_value=Response(200, json="file content") - ) - respx_mock.route(method="GET", path__regex=r"/.*/v1/files/.*/content").mock( - return_value=Response(200, json="file content") - ) - respx_mock.post("/files").mock(return_value=Response(200, json={"id": "file-123"})) - respx_mock.get("/files").mock(return_value=Response(200, json={"data": []})) - respx_mock.route(method="DELETE", path__regex=r"/files/.*").mock(return_value=Response(200, json={})) - - # Fine-tuning - respx_mock.post("/fine_tuning/jobs").mock(return_value=Response(200, json={"id": "ft-123"})) - respx_mock.get("/fine_tuning/jobs").mock(return_value=Response(200, json={"data": []})) - respx_mock.route(method="GET", path__regex=r"/fine_tuning/jobs/[^/]+$").mock( - return_value=Response(200, json={"id": "ft-123"}) - ) - respx_mock.route(method="POST", path__regex=r"/fine_tuning/jobs/.*/cancel").mock( - return_value=Response(200, json={}) - ) - - # Global spend - respx_mock.get("/global/spend/tags").mock(return_value=Response(200, json=[])) - respx_mock.post("/global/spend/reset").mock(return_value=Response(200, json={})) - respx_mock.get("/global/spend/report").mock(return_value=Response(200, json=[])) - - # Guardrails - respx_mock.get("/guardrails/list").mock(return_value=Response(200, json={"guardrails": []})) - - # Images - respx_mock.post("/images/generations").mock(return_value=Response(200, json={"data": []})) - - # Models - respx_mock.get("/model/info").mock(return_value=Response(200, json={"data": []})) - respx_mock.put("/models/update").mock(return_value=Response(200, json={})) - respx_mock.route(method="PATCH", path__regex=r"/models/.*").mock(return_value=Response(200, json={})) - respx_mock.post("/models").mock(return_value=Response(200, json={})) - respx_mock.get("/models").mock(return_value=Response(200, json={"data": []})) - respx_mock.route(method="DELETE", path__regex=r"/models/.*").mock(return_value=Response(200, json={})) - - # OpenAI deployments - respx_mock.route(method="POST", path__regex=r"/openai/deployments/.*/chat/completions").mock( - return_value=Response(200, json={}) - ) - respx_mock.route(method="POST", path__regex=r"/openai/deployments/.*/completions").mock( - return_value=Response(200, json={}) - ) - respx_mock.route(method="POST", path__regex=r"/openai/deployments/.*/embeddings").mock( - return_value=Response(200, json={}) - ) - - # Organization - org_response = { - "budget_id": "budget-123", - "created_at": "2024-01-01T00:00:00Z", - "created_by": "user-123", - "models": ["gpt-3.5-turbo"], - "organization_id": "org-123", - "updated_at": "2024-01-01T00:00:00Z", - "updated_by": "user-123", - } - respx_mock.get("/organization/info").mock( - return_value=Response( - 200, - json={ - "budget_id": "budget-123", - "created_at": "2024-01-01T00:00:00Z", - "created_by": "user-123", - "models": ["gpt-3.5-turbo"], - "updated_at": "2024-01-01T00:00:00Z", - "updated_by": "user-123", - }, - ) - ) - respx_mock.get("/organization/info/deprecated").mock(return_value=Response(200, json={})) - respx_mock.post("/organization/new").mock(return_value=Response(200, json=org_response)) - respx_mock.post("/organization").mock(return_value=Response(200, json={})) - respx_mock.get("/organization/list").mock(return_value=Response(200, json=[])) - respx_mock.get("/organization").mock(return_value=Response(200, json=[])) - org_update_response = { - "budget_id": "budget-123", - "created_at": "2024-01-01T00:00:00Z", - "created_by": "user-123", - "models": ["gpt-3.5-turbo"], - "updated_at": "2024-01-01T00:00:00Z", - "updated_by": "user-123", - "organization_id": "org-123", - "members": [], - "teams": [], - } - respx_mock.patch("/organization/update").mock(return_value=Response(200, json=org_update_response)) - respx_mock.put("/organization/update").mock(return_value=Response(200, json=org_update_response)) - respx_mock.put("/organization").mock(return_value=Response(200, json={})) - respx_mock.delete("/organization/delete").mock(return_value=Response(200, json=[org_response])) - respx_mock.delete("/organization").mock(return_value=Response(200, json={})) - org_member_response = { - "organization_id": "org-123", - "updated_organization_memberships": [], - "updated_users": [], - } - respx_mock.post("/organization/member_add").mock(return_value=Response(200, json=org_member_response)) - respx_mock.post("/organization/members").mock(return_value=Response(200, json={})) - respx_mock.delete("/organization/member_delete").mock(return_value=Response(200, json=org_response)) - respx_mock.delete("/organization/members").mock(return_value=Response(200, json={})) - org_update_member_response = { - "created_at": "2024-01-01T00:00:00Z", - "organization_id": "org-123", - "updated_at": "2024-01-01T00:00:00Z", - "user_id": "user-123", - } - respx_mock.patch("/organization/member_update").mock( - return_value=Response(200, json=org_update_member_response) - ) - respx_mock.put("/organization/member_update").mock(return_value=Response(200, json=org_update_member_response)) - respx_mock.put("/organization/members").mock(return_value=Response(200, json={})) - - # Responses - respx_mock.route(method="GET", path__regex=r"/responses/.*/input_items").mock( - return_value=Response(200, json={"data": []}) - ) - - # Team - team_response = { - "team_id": "team-123", - "models": [], - "members": [], - "admins": [], - } - respx_mock.route(method="GET", path__regex=r"/team/.*/callback").mock(return_value=Response(200, json={})) - respx_mock.route(method="POST", path__regex=r"/team/.*/callback").mock(return_value=Response(200, json={})) - respx_mock.post("/team/model").mock(return_value=Response(200, json={})) - respx_mock.delete("/team/model").mock(return_value=Response(200, json={})) - respx_mock.post("/team/new").mock(return_value=Response(200, json=team_response)) - respx_mock.post("/team").mock(return_value=Response(200, json={})) - respx_mock.get("/team/list").mock(return_value=Response(200, json=[])) - respx_mock.get("/team").mock(return_value=Response(200, json=[])) - respx_mock.get("/team/info").mock(return_value=Response(200, json=team_response)) - respx_mock.patch("/team/update").mock(return_value=Response(200, json=team_response)) - respx_mock.put("/team/update").mock(return_value=Response(200, json=team_response)) - respx_mock.put("/team").mock(return_value=Response(200, json={})) - respx_mock.delete("/team/delete").mock(return_value=Response(200, json=[team_response])) - respx_mock.delete("/team").mock(return_value=Response(200, json={})) - team_add_member_response = { - "team_id": "team-123", - "updated_team_memberships": [], - "updated_users": [], - } - team_update_member_response = {"team_id": "team-123", "user_id": "user-123"} - respx_mock.post("/team/member_add").mock(return_value=Response(200, json=team_add_member_response)) - respx_mock.post("/team/members").mock(return_value=Response(200, json={})) - respx_mock.delete("/team/member_delete").mock(return_value=Response(200, json=team_response)) - respx_mock.delete("/team/members").mock(return_value=Response(200, json={})) - respx_mock.post("/team/member_update").mock(return_value=Response(200, json=team_update_member_response)) - respx_mock.patch("/team/member_update").mock(return_value=Response(200, json=team_update_member_response)) - respx_mock.put("/team/member_update").mock(return_value=Response(200, json=team_update_member_response)) - respx_mock.put("/team/members").mock(return_value=Response(200, json={})) - respx_mock.post("/team/block").mock(return_value=Response(200, json=team_response)) - respx_mock.post("/team/unblock").mock(return_value=Response(200, json=team_response)) - - # Other endpoints - respx_mock.get("/active/callbacks").mock(return_value=Response(200, json={"data": []})) - respx_mock.post("/add/allowed_ip").mock(return_value=Response(200, json={})) - respx_mock.post("/delete/allowed_ip").mock(return_value=Response(200, json={})) - - # Provider endpoints (anthropic, azure, bedrock, etc.) - for provider in [ - "anthropic", - "azure", - "bedrock", - "cohere", - "gemini", - "openai", - "vertex_ai", - "assemblyai", - "eu_assemblyai", - ]: - respx_mock.post(f"/{provider}").mock(return_value=Response(200, json={})) - respx_mock.get(f"/{provider}").mock(return_value=Response(200, json={"data": []})) - respx_mock.route(method="GET", path__regex=rf"/{provider}/.*").mock(return_value=Response(200, json={})) - respx_mock.put(f"/{provider}").mock(return_value=Response(200, json={})) - respx_mock.route(method="PUT", path__regex=rf"/{provider}/.*").mock(return_value=Response(200, json={})) - respx_mock.patch(f"/{provider}").mock(return_value=Response(200, json={})) - respx_mock.route(method="PATCH", path__regex=rf"/{provider}/.*").mock(return_value=Response(200, json={})) - respx_mock.delete(f"/{provider}").mock(return_value=Response(200, json={})) - respx_mock.route(method="DELETE", path__regex=rf"/{provider}/.*").mock(return_value=Response(200, json={})) - respx_mock.post(f"/{provider}/modify").mock(return_value=Response(200, json={})) - respx_mock.post(f"/{provider}/call").mock(return_value=Response(200, json={})) - - # Assistants - respx_mock.post("/assistants").mock(return_value=Response(200, json={})) - respx_mock.get("/assistants").mock(return_value=Response(200, json={"data": []})) - respx_mock.route(method="DELETE", path__regex=r"/assistants/.*").mock(return_value=Response(200, json={})) - - # Budget - respx_mock.post("/budget").mock(return_value=Response(200, json={})) - respx_mock.put("/budget").mock(return_value=Response(200, json={})) - respx_mock.get("/budget").mock(return_value=Response(200, json={"data": []})) - respx_mock.delete("/budget").mock(return_value=Response(200, json={})) - respx_mock.get("/budget/info").mock(return_value=Response(200, json={})) - respx_mock.get("/budget/settings").mock(return_value=Response(200, json={})) - - # Completions - respx_mock.post("/completions").mock(return_value=Response(200, json={})) - - # Credentials - respx_mock.post("/credentials").mock(return_value=Response(200, json={})) - respx_mock.put("/credentials").mock(return_value=Response(200, json={})) - - # Customer - respx_mock.post("/customer").mock(return_value=Response(200, json={})) - respx_mock.get("/customer").mock(return_value=Response(200, json={"data": []})) - respx_mock.get("/customer/list").mock(return_value=Response(200, json=[])) - respx_mock.get("/customer/info").mock( - return_value=Response(200, json={"blocked": False, "user_id": "user-123"}) - ) - respx_mock.put("/customer").mock(return_value=Response(200, json={})) - respx_mock.delete("/customer").mock(return_value=Response(200, json={})) - respx_mock.post("/customer/block").mock(return_value=Response(200, json={})) - respx_mock.post("/customer/unblock").mock(return_value=Response(200, json={})) - - # Embeddings - respx_mock.post("/embeddings").mock(return_value=Response(200, json={"data": []})) - - # Guardrails - respx_mock.get("/guardrails").mock(return_value=Response(200, json={"data": []})) - - # Health - respx_mock.get("/health").mock(return_value=Response(200, json={"status": "healthy"})) - respx_mock.get("/health/services").mock(return_value=Response(200, json={})) - - # Key management - respx_mock.post("/key").mock(return_value=Response(200, json={})) - respx_mock.get("/key").mock(return_value=Response(200, json={"data": []})) - respx_mock.get("/key/info").mock(return_value=Response(200, json={})) - respx_mock.put("/key").mock(return_value=Response(200, json={})) - respx_mock.delete("/key").mock(return_value=Response(200, json={})) - respx_mock.post("/key/generate").mock(return_value=Response(200, json={"key": "sk-123"})) - respx_mock.post("/key/regenerate").mock(return_value=Response(200, json={"key": "sk-456"})) - respx_mock.post("/key/key/regenerate").mock(return_value=Response(200, json={"key": "sk-789"})) - respx_mock.post("/key/block").mock(return_value=Response(200, json={})) - respx_mock.post("/key/unblock").mock(return_value=Response(200, json={})) - respx_mock.get("/key/health").mock(return_value=Response(200, json={"status": "ok"})) - - # Langfuse - respx_mock.post("/langfuse").mock(return_value=Response(200, json={})) - - # Model group - respx_mock.get("/model_group/info").mock(return_value=Response(200, json={})) - - # Moderations - respx_mock.post("/moderations").mock(return_value=Response(200, json={})) - - # Provider - respx_mock.get("/provider/budgets").mock(return_value=Response(200, json={"data": []})) - - # Rerank - respx_mock.post("/rerank").mock(return_value=Response(200, json={})) - - # Routes - respx_mock.get("/routes").mock(return_value=Response(200, json={"data": []})) - - # Settings - respx_mock.get("/settings").mock(return_value=Response(200, json={})) - - # Spend - respx_mock.get("/spend/calculate").mock(return_value=Response(200, json={})) - respx_mock.get("/spend/logs").mock(return_value=Response(200, json=[])) - respx_mock.get("/spend/tags").mock(return_value=Response(200, json=[])) - - # Test - respx_mock.get("/test").mock(return_value=Response(200, json={})) - - # Root endpoint for client tests - respx_mock.get("/").mock(return_value=Response(200, json={})) - - # Threads - respx_mock.post("/threads").mock(return_value=Response(200, json={})) - respx_mock.get("/threads").mock(return_value=Response(200, json={"data": []})) - respx_mock.route(method="GET", path__regex=r"/threads/[^/]+$").mock(return_value=Response(200, json={})) - respx_mock.route(method="PUT", path__regex=r"/threads/.*").mock(return_value=Response(200, json={})) - respx_mock.route(method="DELETE", path__regex=r"/threads/.*").mock(return_value=Response(200, json={})) - respx_mock.route(method="POST", path__regex=r"/threads/.*/messages").mock(return_value=Response(200, json={})) - respx_mock.route(method="GET", path__regex=r"/threads/.*/messages").mock( - return_value=Response(200, json={"data": []}) - ) - respx_mock.route(method="POST", path__regex=r"/threads/.*/runs").mock(return_value=Response(200, json={})) - respx_mock.route(method="GET", path__regex=r"/threads/.*/runs").mock( - return_value=Response(200, json={"data": []}) - ) - - # User - user_response = {"key": "sk-user-123", "user_id": "user-123"} - respx_mock.post("/user/new").mock(return_value=Response(200, json=user_response)) - respx_mock.post("/user").mock(return_value=Response(200, json={})) - respx_mock.get("/user/list").mock(return_value=Response(200, json=[])) - respx_mock.get("/user").mock(return_value=Response(200, json=[])) - respx_mock.get("/user/info").mock(return_value=Response(200, json=user_response)) - respx_mock.put("/user/update").mock(return_value=Response(200, json=user_response)) - respx_mock.put("/user").mock(return_value=Response(200, json={})) - respx_mock.delete("/user/delete").mock(return_value=Response(200, json=user_response)) - respx_mock.delete("/user").mock(return_value=Response(200, json={})) - - # Utils - respx_mock.get("/utils/supported_openai_params").mock(return_value=Response(200, json={})) - respx_mock.post("/utils/token_counter").mock( - return_value=Response( - 200, - json={ - "model_used": "gpt-3.5-turbo", - "request_model": "gpt-3.5-turbo", - "tokenizer_type": "cl100k_base", - "total_tokens": 10, - }, - ) - ) - respx_mock.post("/utils/transform_request").mock(return_value=Response(200, json={})) - - # Home/client - respx_mock.get("/").mock(return_value=Response(200, json={"message": "Welcome"})) - - # Catch-all for any unmocked endpoints - respx_mock.route().mock(return_value=Response(200, json={})) - - yield respx_mock diff --git a/tests/start_mock_server.py b/tests/start_mock_server.py deleted file mode 100755 index 2d9faab9c..000000000 --- a/tests/start_mock_server.py +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env python3 -"""Comprehensive mock server for all test endpoints.""" - -import sys -import json -from http.server import HTTPServer, BaseHTTPRequestHandler - - -class MockHandler(BaseHTTPRequestHandler): - def do_GET(self): - """Handle GET requests.""" - self.send_response(200) - self.send_header("Content-Type", "application/json") - self.end_headers() - - # Default response - response = {} - - # Handle specific endpoints - if "/files/" in self.path and "/content" in self.path: - response = "file content" - elif self.path == "/guardrails/list": - response = {"guardrails": []} - elif self.path == "/global/spend/tags": - response = [] - elif self.path == "/global/spend/report": - response = [] - elif self.path == "/organization/info": - response = { - "budget_id": "budget-123", - "created_at": "2024-01-01T00:00:00Z", - "created_by": "user-123", - "models": ["gpt-3.5-turbo"], - "updated_at": "2024-01-01T00:00:00Z", - "updated_by": "user-123", - } - elif self.path == "/config/pass_through_endpoint": - response = {"endpoints": []} - elif self.path == "/": - response = {} - else: - response = {"data": []} - - self.wfile.write(json.dumps(response).encode()) - - def do_POST(self): - """Handle POST requests.""" - self.send_response(200) - self.send_header("Content-Type", "application/json") - self.end_headers() - - # Default response - response = {} - - # Handle specific endpoints - if self.path == "/utils/token_counter": - response = { - "model_used": "gpt-3.5-turbo", - "request_model": "gpt-3.5-turbo", - "tokenizer_type": "cl100k_base", - "total_tokens": 10, - } - elif self.path == "/chat/completions": - response = { - "id": "chatcmpl-123", - "object": "chat.completion", - "created": 1677652288, - "model": "gpt-3.5-turbo", - "choices": [ - { - "index": 0, - "message": {"role": "assistant", "content": "Hello!"}, - "finish_reason": "stop", - } - ], - } - - self.wfile.write(json.dumps(response).encode()) - - def do_PUT(self): - """Handle PUT requests.""" - self.send_response(200) - self.send_header("Content-Type", "application/json") - self.end_headers() - self.wfile.write(b"{}") - - def do_DELETE(self): - """Handle DELETE requests.""" - self.send_response(200) - self.send_header("Content-Type", "application/json") - self.end_headers() - - # Handle specific endpoints - if self.path == "/config/pass_through_endpoint": - response = {"endpoints": []} - else: - response = {} - - self.wfile.write(json.dumps(response).encode()) - - def log_message(self, format, *args): - pass # Suppress logs - - -if __name__ == "__main__": - try: - server = HTTPServer(("127.0.0.1", 4010), MockHandler) - print("Mock server started on http://127.0.0.1:4010") - print("Press Ctrl+C to stop") - server.serve_forever() - except KeyboardInterrupt: - print("\nShutting down mock server...") - sys.exit(0) - except Exception as e: - print(f"Error: {e}") - sys.exit(1) diff --git a/tests/test_api_response.py b/tests/test_api_response.py deleted file mode 100644 index 4656964ec..000000000 --- a/tests/test_api_response.py +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/env python3 -"""Test actual AI API responses.""" - -import os -import sys -import asyncio -from pathlib import Path - -# Add src to path -sys.path.insert(0, str(Path(__file__).parent / "pkg" / "hanzo" / "src")) - - -async def test_openai_api(): - """Test OpenAI API response.""" - if not os.getenv("OPENAI_API_KEY"): - print("โœ— OpenAI API key not set") - return False - - try: - from openai import AsyncOpenAI - - client = AsyncOpenAI() - response = await client.chat.completions.create( - model="gpt-4", - messages=[ - { - "role": "system", - "content": "You are a helpful assistant. Reply with exactly: 'API test successful'", - }, - {"role": "user", "content": "Test"}, - ], - max_tokens=50, - ) - - if response.choices and "successful" in response.choices[0].message.content: - print(f"โœ“ OpenAI API works: {response.choices[0].message.content}") - return True - else: - print("โœ— OpenAI API response unexpected") - return False - - except Exception as e: - print(f"โœ— OpenAI API error: {e}") - return False - - -async def test_anthropic_api(): - """Test Anthropic API response.""" - if not os.getenv("ANTHROPIC_API_KEY"): - print("โœ— Anthropic API key not set") - return False - - try: - from anthropic import AsyncAnthropic - - client = AsyncAnthropic() - response = await client.messages.create( - model="claude-3-5-sonnet-20241022", - messages=[{"role": "user", "content": "Reply with exactly: 'API test successful'"}], - max_tokens=50, - ) - - if response.content and "successful" in response.content[0].text: - print(f"โœ“ Anthropic API works: {response.content[0].text}") - return True - else: - print("โœ— Anthropic API response unexpected") - return False - - except Exception as e: - print(f"โœ— Anthropic API error: {e}") - return False - - -async def test_ollama_local(): - """Test Ollama local model.""" - try: - import httpx - - async with httpx.AsyncClient() as client: - # Check if Ollama is running - response = await client.get("http://localhost:11434/api/tags") - - if response.status_code == 200: - data = response.json() - models = data.get("models", []) - - if models: - print(f"โœ“ Ollama running with {len(models)} model(s)") - - # Try a simple generation - model_name = models[0]["name"] - gen_response = await client.post( - "http://localhost:11434/api/generate", - json={ - "model": model_name, - "prompt": "Reply with: test", - "stream": False, - }, - timeout=30.0, - ) - - if gen_response.status_code == 200: - print(f"โœ“ Ollama model {model_name} responds") - return True - else: - print("โœ— Ollama running but no models installed") - print(" Install with: ollama pull llama3.2") - return False - else: - print("โœ— Ollama not responding") - return False - - except Exception as e: - print(f"โœ— Ollama not available: {e}") - print(" Install with: curl -fsSL https://ollama.com/install.sh | sh") - return False - - -async def main(): - """Run API tests.""" - print("\n" + "=" * 60) - print("TESTING ACTUAL AI API RESPONSES") - print("=" * 60 + "\n") - - results = [] - - # Test OpenAI - print("Testing OpenAI API...") - results.append(await test_openai_api()) - print() - - # Test Anthropic - print("Testing Anthropic API...") - results.append(await test_anthropic_api()) - print() - - # Test Ollama - print("Testing Ollama (local)...") - results.append(await test_ollama_local()) - print() - - # Summary - print("=" * 60) - if any(results): - print("โœ… At least one AI API is working!") - print("You can use hanzo dev with the working APIs") - else: - print("โš ๏ธ No AI APIs are currently working") - print("You can still use CLI tools or free APIs") - - return any(results) - - -if __name__ == "__main__": - success = asyncio.run(main()) - sys.exit(0 if success else 1) diff --git a/tests/test_auth_pkce.py b/tests/test_auth_pkce.py deleted file mode 100644 index 2bcb479bf..000000000 --- a/tests/test_auth_pkce.py +++ /dev/null @@ -1,511 +0,0 @@ -"""Tests for PKCE OAuth support in hanzoai.auth. - -Run: .venv/bin/python -m pytest tests/test_auth_pkce.py -v -""" - -import os -import json -import stat -import base64 -import hashlib -import tempfile -import unittest -from pathlib import Path -from urllib.parse import parse_qs, urlparse -from unittest.mock import AsyncMock, MagicMock, patch - -from hanzoai.auth import ( - OPENAI_ISSUER, - IAM_CLIENT_ID, - OPENAI_CLIENT_ID, - ANTHROPIC_CLIENT_ID, - ANTHROPIC_TOKEN_URL, - ANTHROPIC_AUTHORIZE_URL, - HanzoAuth, - PkceCodePair, - OAuthTokenSet, - OAuthCredentialStore, - OAuthAuthorizationRequest, - OAuthTokenExchangeRequest, - generate_state, - generate_pkce_pair, -) - - -class TestPkceCodePair(unittest.TestCase): - def test_fields(self): - pair = PkceCodePair(verifier="v", challenge="c") - self.assertEqual(pair.verifier, "v") - self.assertEqual(pair.challenge, "c") - self.assertEqual(pair.challenge_method, "S256") - - def test_custom_method(self): - pair = PkceCodePair(verifier="v", challenge="c", challenge_method="plain") - self.assertEqual(pair.challenge_method, "plain") - - -class TestGeneratePkcePair(unittest.TestCase): - def test_returns_pkce_pair(self): - pair = generate_pkce_pair() - self.assertIsInstance(pair, PkceCodePair) - self.assertEqual(pair.challenge_method, "S256") - - def test_verifier_is_base64url_no_padding(self): - pair = generate_pkce_pair() - self.assertNotIn("=", pair.verifier) - self.assertNotIn("+", pair.verifier) - self.assertNotIn("/", pair.verifier) - # 32 bytes -> 43 base64url chars (no padding) - self.assertEqual(len(pair.verifier), 43) - - def test_challenge_is_sha256_of_verifier(self): - pair = generate_pkce_pair() - # SHA-256 of the verifier string (ASCII), then base64url no padding - expected_hash = hashlib.sha256(pair.verifier.encode("ascii")).digest() - expected_challenge = base64.urlsafe_b64encode(expected_hash).rstrip(b"=").decode("ascii") - self.assertEqual(pair.challenge, expected_challenge) - - def test_challenge_length(self): - pair = generate_pkce_pair() - # SHA-256 is 32 bytes -> 43 base64url chars (no padding) - self.assertEqual(len(pair.challenge), 43) - - def test_pairs_are_unique(self): - a = generate_pkce_pair() - b = generate_pkce_pair() - self.assertNotEqual(a.verifier, b.verifier) - self.assertNotEqual(a.challenge, b.challenge) - - def test_deterministic_with_known_input(self): - """Verify against a known PKCE test vector.""" - # Use a fixed 32-byte input to verify the encoding chain - fixed_bytes = b"\x00" * 32 - verifier = base64.urlsafe_b64encode(fixed_bytes).rstrip(b"=").decode("ascii") - challenge_hash = hashlib.sha256(verifier.encode("ascii")).digest() - challenge = base64.urlsafe_b64encode(challenge_hash).rstrip(b"=").decode("ascii") - - with patch("os.urandom", return_value=fixed_bytes): - pair = generate_pkce_pair() - - self.assertEqual(pair.verifier, verifier) - self.assertEqual(pair.challenge, challenge) - - -class TestGenerateState(unittest.TestCase): - def test_returns_string(self): - state = generate_state() - self.assertIsInstance(state, str) - - def test_base64url_no_padding(self): - state = generate_state() - self.assertNotIn("=", state) - self.assertNotIn("+", state) - self.assertNotIn("/", state) - # 32 bytes -> 43 base64url chars - self.assertEqual(len(state), 43) - - def test_unique(self): - a = generate_state() - b = generate_state() - self.assertNotEqual(a, b) - - -class TestOAuthAuthorizationRequest(unittest.TestCase): - def _make_req(self, **kwargs): - defaults = dict( - authorize_url="https://hanzo.id/oauth/authorize", - client_id="app-hanzo", - redirect_uri="http://localhost:4545/callback", - scopes=["openid", "profile"], - state="test-state", - code_challenge="test-challenge", - code_challenge_method="S256", - ) - defaults.update(kwargs) - return OAuthAuthorizationRequest(**defaults) - - def test_build_url_contains_all_params(self): - req = self._make_req() - url = req.build_url() - parsed = urlparse(url) - params = parse_qs(parsed.query) - - self.assertEqual(parsed.scheme, "https") - self.assertEqual(parsed.netloc, "hanzo.id") - self.assertEqual(parsed.path, "/oauth/authorize") - self.assertEqual(params["client_id"], ["app-hanzo"]) - self.assertEqual(params["redirect_uri"], ["http://localhost:4545/callback"]) - self.assertEqual(params["response_type"], ["code"]) - self.assertEqual(params["scope"], ["openid profile"]) - self.assertEqual(params["state"], ["test-state"]) - self.assertEqual(params["code_challenge"], ["test-challenge"]) - self.assertEqual(params["code_challenge_method"], ["S256"]) - - def test_build_url_scopes_joined(self): - req = self._make_req(scopes=["openid", "profile", "email"]) - url = req.build_url() - params = parse_qs(urlparse(url).query) - self.assertEqual(params["scope"], ["openid profile email"]) - - def test_build_url_starts_with_authorize_url(self): - req = self._make_req(authorize_url="https://custom.example.com/auth") - url = req.build_url() - self.assertTrue(url.startswith("https://custom.example.com/auth?")) - - -class TestOAuthTokenExchangeRequest(unittest.TestCase): - def test_form_params(self): - req = OAuthTokenExchangeRequest( - code="auth-code-123", - redirect_uri="http://localhost:4545/callback", - client_id="app-hanzo", - code_verifier="verifier-abc", - state="state-xyz", - ) - params = req.form_params() - self.assertEqual(params["grant_type"], "authorization_code") - self.assertEqual(params["code"], "auth-code-123") - self.assertEqual(params["redirect_uri"], "http://localhost:4545/callback") - self.assertEqual(params["client_id"], "app-hanzo") - self.assertEqual(params["code_verifier"], "verifier-abc") - self.assertEqual(params["state"], "state-xyz") - - def test_default_grant_type(self): - req = OAuthTokenExchangeRequest( - code="c", redirect_uri="r", client_id="ci", code_verifier="cv", state="s" - ) - self.assertEqual(req.grant_type, "authorization_code") - - def test_form_params_keys(self): - req = OAuthTokenExchangeRequest( - code="c", redirect_uri="r", client_id="ci", code_verifier="cv", state="s" - ) - params = req.form_params() - expected_keys = {"grant_type", "code", "redirect_uri", "client_id", "code_verifier", "state"} - self.assertEqual(set(params.keys()), expected_keys) - - -class TestOAuthTokenSet(unittest.TestCase): - def test_required_fields(self): - ts = OAuthTokenSet(access_token="tok123", scopes=["openid"]) - self.assertEqual(ts.access_token, "tok123") - self.assertEqual(ts.scopes, ["openid"]) - self.assertIsNone(ts.refresh_token) - self.assertIsNone(ts.expires_at) - - def test_all_fields(self): - ts = OAuthTokenSet( - access_token="tok", - scopes=["openid"], - refresh_token="ref", - expires_at=1234567890, - ) - self.assertEqual(ts.refresh_token, "ref") - self.assertEqual(ts.expires_at, 1234567890) - - -class TestOAuthCredentialStore(unittest.TestCase): - def setUp(self): - self.tmpdir = tempfile.mkdtemp() - self.store = OAuthCredentialStore(config_home=self.tmpdir) - - def test_credentials_path_default(self): - with patch.dict(os.environ, {}, clear=False): - # Remove HANZO_CONFIG_HOME if set - env = os.environ.copy() - env.pop("HANZO_CONFIG_HOME", None) - with patch.dict(os.environ, env, clear=True): - store = OAuthCredentialStore() - expected = Path.home() / ".hanzo" / "credentials.json" - self.assertEqual(store.credentials_path(), expected) - - def test_credentials_path_custom_config_home(self): - self.assertEqual( - self.store.credentials_path(), - Path(self.tmpdir) / "credentials.json", - ) - - def test_credentials_path_env_override(self): - with patch.dict(os.environ, {"HANZO_CONFIG_HOME": "/custom/path"}): - store = OAuthCredentialStore() - self.assertEqual(store.credentials_path(), Path("/custom/path/credentials.json")) - - def test_save_and_load_roundtrip(self): - token_set = OAuthTokenSet( - access_token="access123", - refresh_token="refresh456", - expires_at=9999999999, - scopes=["openid", "profile"], - ) - self.store.save(token_set) - - loaded = self.store.load() - self.assertIsNotNone(loaded) - self.assertEqual(loaded.access_token, "access123") - self.assertEqual(loaded.refresh_token, "refresh456") - self.assertEqual(loaded.expires_at, 9999999999) - self.assertEqual(loaded.scopes, ["openid", "profile"]) - - def test_save_preserves_other_keys(self): - creds_path = self.store.credentials_path() - creds_path.parent.mkdir(parents=True, exist_ok=True) - with open(creds_path, "w") as f: - json.dump({"api_key": "keep-me", "other": "data"}, f) - - token_set = OAuthTokenSet(access_token="tok", scopes=["openid"]) - self.store.save(token_set) - - with open(creds_path) as f: - data = json.load(f) - - self.assertEqual(data["api_key"], "keep-me") - self.assertEqual(data["other"], "data") - self.assertIn("oauth", data) - - def test_load_returns_none_when_no_file(self): - self.assertIsNone(self.store.load()) - - def test_load_returns_none_when_no_oauth_key(self): - creds_path = self.store.credentials_path() - creds_path.parent.mkdir(parents=True, exist_ok=True) - with open(creds_path, "w") as f: - json.dump({"api_key": "something"}, f) - - self.assertIsNone(self.store.load()) - - def test_clear_removes_oauth_preserves_rest(self): - creds_path = self.store.credentials_path() - creds_path.parent.mkdir(parents=True, exist_ok=True) - with open(creds_path, "w") as f: - json.dump({"api_key": "keep", "oauth": {"access_token": "remove"}}, f) - - self.store.clear() - - with open(creds_path) as f: - data = json.load(f) - - self.assertEqual(data["api_key"], "keep") - self.assertNotIn("oauth", data) - - def test_clear_noop_when_no_file(self): - self.store.clear() # should not raise - - def test_save_atomic_no_tmp_left(self): - """After save, .tmp file should not exist.""" - token_set = OAuthTokenSet(access_token="tok", scopes=["openid"]) - self.store.save(token_set) - - creds_path = self.store.credentials_path() - tmp_path = creds_path.with_suffix(".json.tmp") - self.assertTrue(creds_path.exists()) - self.assertFalse(tmp_path.exists()) - - def test_save_sets_restrictive_permissions(self): - token_set = OAuthTokenSet(access_token="tok", scopes=["openid"]) - self.store.save(token_set) - - creds_path = self.store.credentials_path() - mode = os.stat(creds_path).st_mode - self.assertEqual(stat.S_IMODE(mode), 0o600) - - def test_save_creates_parent_dirs(self): - nested = os.path.join(self.tmpdir, "deep", "nested") - store = OAuthCredentialStore(config_home=nested) - token_set = OAuthTokenSet(access_token="tok", scopes=["openid"]) - store.save(token_set) - self.assertTrue(store.credentials_path().exists()) - - -class TestOAuthCredentialStoreMultiProvider(unittest.TestCase): - """Tests for multi-provider credential storage.""" - - def setUp(self): - self.tmpdir = tempfile.mkdtemp() - self.store = OAuthCredentialStore(config_home=self.tmpdir) - - def test_save_load_anthropic_provider(self): - token_set = OAuthTokenSet( - access_token="anth-tok", scopes=["openid"], refresh_token="anth-ref" - ) - self.store.save(token_set, provider="anthropic") - loaded = self.store.load(provider="anthropic") - self.assertIsNotNone(loaded) - self.assertEqual(loaded.access_token, "anth-tok") - self.assertEqual(loaded.refresh_token, "anth-ref") - - def test_save_load_openai_provider(self): - token_set = OAuthTokenSet( - access_token="oai-tok", scopes=["openid"], expires_at=123456 - ) - self.store.save(token_set, provider="openai") - loaded = self.store.load(provider="openai") - self.assertIsNotNone(loaded) - self.assertEqual(loaded.access_token, "oai-tok") - self.assertEqual(loaded.expires_at, 123456) - - def test_default_provider_is_hanzo(self): - token_set = OAuthTokenSet(access_token="hz-tok", scopes=["openid"]) - self.store.save(token_set) # no provider arg - loaded = self.store.load() # no provider arg - self.assertIsNotNone(loaded) - self.assertEqual(loaded.access_token, "hz-tok") - - def test_providers_are_independent(self): - """Saving to one provider does not affect another.""" - self.store.save( - OAuthTokenSet(access_token="hz", scopes=["a"]), provider="hanzo" - ) - self.store.save( - OAuthTokenSet(access_token="anth", scopes=["b"]), provider="anthropic" - ) - self.store.save( - OAuthTokenSet(access_token="oai", scopes=["c"]), provider="openai" - ) - - self.assertEqual(self.store.load(provider="hanzo").access_token, "hz") - self.assertEqual(self.store.load(provider="anthropic").access_token, "anth") - self.assertEqual(self.store.load(provider="openai").access_token, "oai") - - def test_clear_only_affects_target_provider(self): - self.store.save( - OAuthTokenSet(access_token="hz", scopes=["a"]), provider="hanzo" - ) - self.store.save( - OAuthTokenSet(access_token="anth", scopes=["b"]), provider="anthropic" - ) - - self.store.clear(provider="hanzo") - - self.assertIsNone(self.store.load(provider="hanzo")) - self.assertEqual(self.store.load(provider="anthropic").access_token, "anth") - - def test_backwards_compatible_json_key(self): - """Default hanzo provider uses 'oauth' key in JSON for backwards compat.""" - self.store.save( - OAuthTokenSet(access_token="hz", scopes=["a"]), provider="hanzo" - ) - with open(self.store.credentials_path()) as f: - data = json.load(f) - self.assertIn("oauth", data) - self.assertEqual(data["oauth"]["access_token"], "hz") - - def test_anthropic_uses_distinct_json_key(self): - self.store.save( - OAuthTokenSet(access_token="anth", scopes=["a"]), provider="anthropic" - ) - with open(self.store.credentials_path()) as f: - data = json.load(f) - self.assertIn("oauth_anthropic", data) - self.assertNotIn("oauth", data) - - def test_openai_uses_distinct_json_key(self): - self.store.save( - OAuthTokenSet(access_token="oai", scopes=["a"]), provider="openai" - ) - with open(self.store.credentials_path()) as f: - data = json.load(f) - self.assertIn("oauth_openai", data) - self.assertNotIn("oauth", data) - - def test_unknown_provider_raises(self): - with self.assertRaises(ValueError): - self.store.save( - OAuthTokenSet(access_token="x", scopes=["a"]), provider="github" - ) - with self.assertRaises(ValueError): - self.store.load(provider="github") - with self.assertRaises(ValueError): - self.store.clear(provider="github") - - def test_load_returns_none_for_missing_provider(self): - self.store.save( - OAuthTokenSet(access_token="hz", scopes=["a"]), provider="hanzo" - ) - self.assertIsNone(self.store.load(provider="anthropic")) - self.assertIsNone(self.store.load(provider="openai")) - - -class TestProviderConstants(unittest.TestCase): - """Verify provider constants match the Rust codex-rs values.""" - - def test_openai_client_id(self): - self.assertEqual(OPENAI_CLIENT_ID, "app_EMoamEEZ73f0CkXaXp7hrann") - - def test_openai_issuer(self): - self.assertEqual(OPENAI_ISSUER, "https://auth.openai.com") - - def test_anthropic_client_id(self): - self.assertEqual(ANTHROPIC_CLIENT_ID, "9d1c250a-e61b-44d9-88ed-5944d1962f5e") - - def test_anthropic_authorize_url(self): - self.assertEqual(ANTHROPIC_AUTHORIZE_URL, "https://claude.ai/oauth/authorize") - - def test_anthropic_token_url(self): - self.assertEqual(ANTHROPIC_TOKEN_URL, "https://platform.claude.com/v1/oauth/token") - - def test_hanzo_client_id(self): - self.assertEqual(IAM_CLIENT_ID, "hanzo-dev") - - -class TestLoginAuto(unittest.TestCase): - """Tests for HanzoAuth.login_auto() env var detection.""" - - def test_hanzo_api_key_takes_priority(self): - import asyncio - auth = HanzoAuth() - env = { - "HANZO_API_KEY": "hk-test", - "ANTHROPIC_API_KEY": "sk-ant-test", - "OPENAI_API_KEY": "sk-test", - } - with patch.dict(os.environ, env): - result = asyncio.run(auth.login_auto()) - self.assertEqual(result["provider"], "hanzo") - self.assertEqual(result["api_key"], "hk-test") - self.assertEqual(auth.api_key, "hk-test") - - def test_anthropic_api_key_second_priority(self): - import asyncio - auth = HanzoAuth() - env = {"ANTHROPIC_API_KEY": "sk-ant-test"} - with patch.dict(os.environ, env, clear=False): - # Ensure HANZO_API_KEY is not set - os.environ.pop("HANZO_API_KEY", None) - os.environ.pop("OPENAI_API_KEY", None) - result = asyncio.run(auth.login_auto()) - self.assertEqual(result["provider"], "anthropic") - self.assertEqual(result["api_key"], "sk-ant-test") - - def test_openai_api_key_third_priority(self): - import asyncio - auth = HanzoAuth() - env = {"OPENAI_API_KEY": "sk-test"} - with patch.dict(os.environ, env, clear=False): - os.environ.pop("HANZO_API_KEY", None) - os.environ.pop("ANTHROPIC_API_KEY", None) - result = asyncio.run(auth.login_auto()) - self.assertEqual(result["provider"], "openai") - self.assertEqual(result["api_key"], "sk-test") - - def test_saved_credentials_used_when_no_env(self): - import asyncio - tmpdir = tempfile.mkdtemp() - store = OAuthCredentialStore(config_home=tmpdir) - store.save( - OAuthTokenSet(access_token="saved-anth", scopes=["openid"]), - provider="anthropic", - ) - auth = HanzoAuth() - with patch.dict(os.environ, {}, clear=False): - os.environ.pop("HANZO_API_KEY", None) - os.environ.pop("ANTHROPIC_API_KEY", None) - os.environ.pop("OPENAI_API_KEY", None) - # Patch the OAuthCredentialStore used inside login_auto - with patch("hanzoai.auth.OAuthCredentialStore", return_value=store): - result = asyncio.run(auth.login_auto()) - self.assertEqual(result["provider"], "anthropic") - self.assertEqual(result["token"], "saved-anth") - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_client.py b/tests/test_client.py index 2fcbc0702..bb118ab62 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,4 +1,4 @@ -# # Hanzo AI SDK Tests +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -6,13 +6,10 @@ import os import sys import json -import time import asyncio import inspect -import subprocess import tracemalloc from typing import Any, Union, cast -from textwrap import dedent from unittest import mock from typing_extensions import Literal @@ -23,18 +20,17 @@ from hanzoai import Hanzo, AsyncHanzo, APIResponseValidationError from hanzoai._types import Omit +from hanzoai._utils import asyncify from hanzoai._models import BaseModel, FinalRequestOptions -from hanzoai._constants import RAW_RESPONSE_HEADER -from hanzoai._exceptions import ( - HanzoError, - APIStatusError, - APITimeoutError, - APIResponseValidationError, -) +from hanzoai._exceptions import HanzoError, APIStatusError, APITimeoutError, APIResponseValidationError from hanzoai._base_client import ( DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, + OtherPlatform, + DefaultHttpxClient, + DefaultAsyncHttpxClient, + get_platform, make_request_options, ) @@ -63,62 +59,53 @@ def _get_open_connections(client: Hanzo | AsyncHanzo) -> int: class TestHanzo: - client = Hanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True) - @pytest.mark.respx(base_url=base_url) - def test_raw_response(self, respx_mock: MockRouter) -> None: + def test_raw_response(self, respx_mock: MockRouter, client: Hanzo) -> None: respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.post("/foo", cast_to=httpx.Response) + response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) - def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + def test_raw_response_for_binary(self, respx_mock: MockRouter, client: Hanzo) -> None: respx_mock.post("/foo").mock( - return_value=httpx.Response( - 200, - headers={"Content-Type": "application/binary"}, - content='{"foo": "bar"}', - ) + return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') ) - response = self.client.post("/foo", cast_to=httpx.Response) + response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} - def test_copy(self) -> None: - copied = self.client.copy() - assert id(copied) != id(self.client) + def test_copy(self, client: Hanzo) -> None: + copied = client.copy() + assert id(copied) != id(client) - copied = self.client.copy(api_key="another My API Key") + copied = client.copy(api_key="another My API Key") assert copied.api_key == "another My API Key" - assert self.client.api_key == "My API Key" + assert client.api_key == "My API Key" - def test_copy_default_options(self) -> None: + def test_copy_default_options(self, client: Hanzo) -> None: # options that have a default are overridden correctly - copied = self.client.copy(max_retries=7) + copied = client.copy(max_retries=7) assert copied.max_retries == 7 - assert self.client.max_retries == 2 + assert client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout - assert isinstance(self.client.timeout, httpx.Timeout) - copied = self.client.copy(timeout=None) + assert isinstance(client.timeout, httpx.Timeout) + copied = client.copy(timeout=None) assert copied.timeout is None - assert isinstance(self.client.timeout, httpx.Timeout) + assert isinstance(client.timeout, httpx.Timeout) def test_copy_default_headers(self) -> None: client = Hanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - default_headers={"X-Foo": "bar"}, + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) assert client.default_headers["X-Foo"] == "bar" @@ -149,13 +136,11 @@ def test_copy_default_headers(self) -> None: match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + client.close() def test_copy_default_query(self) -> None: client = Hanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - default_query={"foo": "bar"}, + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} ) assert _get_params(client)["foo"] == "bar" @@ -189,13 +174,15 @@ def test_copy_default_query(self) -> None: ): client.copy(set_default_query={}, default_query={"foo": "Bar"}) - def test_copy_signature(self) -> None: + client.close() + + def test_copy_signature(self, client: Hanzo) -> None: # ensure the same parameters that can be passed to the client are defined in the `.copy()` method init_signature = inspect.signature( # mypy doesn't like that we access the `__init__` property. - self.client.__init__, # type: ignore[misc] + client.__init__, # type: ignore[misc] ) - copy_signature = inspect.signature(self.client.copy) + copy_signature = inspect.signature(client.copy) exclude_params = {"transport", "proxies", "_strict_response_validation"} for name in init_signature.parameters.keys(): @@ -205,13 +192,13 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" - @pytest.mark.skipif(os.environ.get("CI") == "true", reason="Memory leak test is flaky in CI") - def test_copy_build_request(self) -> None: + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") + def test_copy_build_request(self, client: Hanzo) -> None: options = FinalRequestOptions(method="get", url="/foo") def build_request(options: FinalRequestOptions) -> None: - client = self.client.copy() - client._build_request(options) + client_copy = client.copy() + client_copy._build_request(options) # ensure that the machinery is warmed up before tracing starts. build_request(options) @@ -252,8 +239,6 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic "hanzoai/_compat.py", # Standard library leaks we don't care about. "/logging/__init__.py", - # Regex compilation caching leaks - "/re/__init__.py", ] ): return @@ -270,69 +255,61 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic print(frame) raise AssertionError() - def test_request_timeout(self) -> None: - request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + def test_request_timeout(self, client: Hanzo) -> None: + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT - request = self.client._build_request( - FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) - ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0))) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(100.0) def test_client_timeout_option(self) -> None: - client = Hanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - timeout=httpx.Timeout(0), - ) + client = Hanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0)) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(0) + client.close() + def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used with httpx.Client(timeout=None) as http_client: client = Hanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - http_client=http_client, + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(None) + client.close() + # no timeout given to the httpx client should not use the httpx default with httpx.Client() as http_client: client = Hanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - http_client=http_client, + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT + client.close() + # explicitly passing the default timeout currently results in it being ignored with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = Hanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - http_client=http_client, + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + client.close() + async def test_invalid_http_client(self) -> None: with pytest.raises(TypeError, match="Invalid `http_client` arg"): async with httpx.AsyncClient() as http_client: @@ -344,28 +321,28 @@ async def test_invalid_http_client(self) -> None: ) def test_default_headers_option(self) -> None: - client = Hanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - default_headers={"X-Foo": "bar"}, + test_client = Hanzo( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) - request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" - assert request.headers.get("x-sdk-lang") == "python" + assert request.headers.get("x-stainless-lang") == "python" - client2 = Hanzo( + test_client2 = Hanzo( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={ "X-Foo": "stainless", - "X-SDK-Lang": "my-overriding-header", + "X-Stainless-Lang": "my-overriding-header", }, ) - request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "stainless" - assert request.headers.get("x-sdk-lang") == "my-overriding-header" + assert request.headers.get("x-stainless-lang") == "my-overriding-header" + + test_client.close() + test_client2.close() def test_validate_headers(self) -> None: client = Hanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -379,10 +356,7 @@ def test_validate_headers(self) -> None: def test_default_query_option(self) -> None: client = Hanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - default_query={"query_param": "bar"}, + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) url = httpx.URL(request.url) @@ -398,8 +372,10 @@ def test_default_query_option(self) -> None: url = httpx.URL(request.url) assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} - def test_request_extra_json(self) -> None: - request = self.client._build_request( + client.close() + + def test_request_extra_json(self, client: Hanzo) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -410,7 +386,7 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": False} - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -421,7 +397,7 @@ def test_request_extra_json(self) -> None: assert data == {"baz": False} # `extra_json` takes priority over `json_data` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -432,8 +408,8 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": None} - def test_request_extra_headers(self) -> None: - request = self.client._build_request( + def test_request_extra_headers(self, client: Hanzo) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -443,7 +419,7 @@ def test_request_extra_headers(self) -> None: assert request.headers.get("X-Foo") == "Foo" # `extra_headers` takes priority over `default_headers` when keys clash - request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( FinalRequestOptions( method="post", url="/foo", @@ -454,8 +430,8 @@ def test_request_extra_headers(self) -> None: ) assert request.headers.get("X-Bar") == "false" - def test_request_extra_query(self) -> None: - request = self.client._build_request( + def test_request_extra_query(self, client: Hanzo) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -468,7 +444,7 @@ def test_request_extra_query(self) -> None: assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -482,7 +458,7 @@ def test_request_extra_query(self) -> None: assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -498,7 +474,7 @@ def test_request_extra_query(self) -> None: def test_multipart_repeating_array(self, client: Hanzo) -> None: request = client._build_request( FinalRequestOptions.construct( - method="get", + method="post", url="/foo", headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, json_data={"array": ["foo", "bar"]}, @@ -525,7 +501,7 @@ def test_multipart_repeating_array(self, client: Hanzo) -> None: ] @pytest.mark.respx(base_url=base_url) - def test_basic_union_response(self, respx_mock: MockRouter) -> None: + def test_basic_union_response(self, respx_mock: MockRouter, client: Hanzo) -> None: class Model1(BaseModel): name: str @@ -534,12 +510,12 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" @pytest.mark.respx(base_url=base_url) - def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + def test_union_response_different_types(self, respx_mock: MockRouter, client: Hanzo) -> None: """Union of objects with the same field name using a different type""" class Model1(BaseModel): @@ -550,18 +526,18 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model1) assert response.foo == 1 @pytest.mark.respx(base_url=base_url) - def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter, client: Hanzo) -> None: """ Response that sets Content-Type to something other than application/json but returns json data """ @@ -577,22 +553,20 @@ class Model(BaseModel): ) ) - response = self.client.get("/foo", cast_to=Model) + response = client.get("/foo", cast_to=Model) assert isinstance(response, Model) assert response.foo == 2 def test_base_url_setter(self) -> None: - client = Hanzo( - base_url="https://example.com/from_init", - api_key=api_key, - _strict_response_validation=True, - ) + client = Hanzo(base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True) assert client.base_url == "https://example.com/from_init/" client.base_url = "https://example.com/from_setter" # type: ignore[assignment] assert client.base_url == "https://example.com/from_setter/" + client.close() + def test_base_url_env(self) -> None: with update_env(HANZO_BASE_URL="http://localhost:5000/from/env"): client = Hanzo(api_key=api_key, _strict_response_validation=True) @@ -601,28 +575,17 @@ def test_base_url_env(self) -> None: # explicit environment arg requires explicitness with update_env(HANZO_BASE_URL="http://localhost:5000/from/env"): with pytest.raises(ValueError, match=r"you must pass base_url=None"): - Hanzo( - api_key=api_key, - _strict_response_validation=True, - environment="production", - ) + Hanzo(api_key=api_key, _strict_response_validation=True, environment="production") - client = Hanzo( - base_url=None, - api_key=api_key, - _strict_response_validation=True, - environment="production", - ) + client = Hanzo(base_url=None, api_key=api_key, _strict_response_validation=True, environment="production") assert str(client.base_url).startswith("https://api.hanzo.ai") + client.close() + @pytest.mark.parametrize( "client", [ - Hanzo( - base_url="http://localhost:5000/custom/path/", - api_key=api_key, - _strict_response_validation=True, - ), + Hanzo(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), Hanzo( base_url="http://localhost:5000/custom/path/", api_key=api_key, @@ -641,15 +604,12 @@ def test_base_url_trailing_slash(self, client: Hanzo) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + client.close() @pytest.mark.parametrize( "client", [ - Hanzo( - base_url="http://localhost:5000/custom/path/", - api_key=api_key, - _strict_response_validation=True, - ), + Hanzo(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), Hanzo( base_url="http://localhost:5000/custom/path/", api_key=api_key, @@ -668,15 +628,12 @@ def test_base_url_no_trailing_slash(self, client: Hanzo) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + client.close() @pytest.mark.parametrize( "client", [ - Hanzo( - base_url="http://localhost:5000/custom/path/", - api_key=api_key, - _strict_response_validation=True, - ), + Hanzo(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), Hanzo( base_url="http://localhost:5000/custom/path/", api_key=api_key, @@ -695,46 +652,42 @@ def test_absolute_request_url(self, client: Hanzo) -> None: ), ) assert request.url == "https://myapi.com/foo" + client.close() def test_copied_client_does_not_close_http(self) -> None: - client = Hanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True) - assert not client.is_closed() + test_client = Hanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert not test_client.is_closed() - copied = client.copy() - assert copied is not client + copied = test_client.copy() + assert copied is not test_client del copied - assert not client.is_closed() + assert not test_client.is_closed() def test_client_context_manager(self) -> None: - client = Hanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True) - with client as c2: - assert c2 is client + test_client = Hanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True) + with test_client as c2: + assert c2 is test_client assert not c2.is_closed() - assert not client.is_closed() - assert client.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() @pytest.mark.respx(base_url=base_url) - def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + def test_client_response_validation_error(self, respx_mock: MockRouter, client: Hanzo) -> None: class Model(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) with pytest.raises(APIResponseValidationError) as exc: - self.client.get("/foo", cast_to=Model) + client.get("/foo", cast_to=Model) assert isinstance(exc.value.__cause__, ValidationError) def test_client_max_retries_validation(self) -> None: with pytest.raises(TypeError, match=r"max_retries cannot be None"): - Hanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - max_retries=cast(Any, None), - ) + Hanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None)) @pytest.mark.respx(base_url=base_url) def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: @@ -748,11 +701,14 @@ class Model(BaseModel): with pytest.raises(APIResponseValidationError): strict_client.get("/foo", cast_to=Model) - client = Hanzo(base_url=base_url, api_key=api_key, _strict_response_validation=False) + non_strict_client = Hanzo(base_url=base_url, api_key=api_key, _strict_response_validation=False) - response = client.get("/foo", cast_to=Model) + response = non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] + strict_client.close() + non_strict_client.close() + @pytest.mark.parametrize( "remaining_retries,retry_after,timeout", [ @@ -775,9 +731,9 @@ class Model(BaseModel): ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) - def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: - client = Hanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True) - + def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, client: Hanzo + ) -> None: headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) calculated = client._calculate_retry_timeout(remaining_retries, options, headers) @@ -785,31 +741,22 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str @mock.patch("hanzoai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: Hanzo) -> None: respx_mock.get("/").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - self.client.get( - "/", - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) + client.with_streaming_response.get_home().__enter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(client) == 0 @mock.patch("hanzoai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: Hanzo) -> None: respx_mock.get("/").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - self.client.get( - "/", - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) - - assert _get_open_connections(self.client) == 0 + client.with_streaming_response.get_home().__enter__() + assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("hanzoai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -840,7 +787,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: response = client.with_raw_response.get_home() assert response.retries_taken == failures_before_success - assert int(response.http_request.headers.get("x-sdk-retry-count")) == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("hanzoai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -859,9 +806,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/").mock(side_effect=retry_handler) - response = client.with_raw_response.get_home(extra_headers={"x-sdk-retry-count": Omit()}) + response = client.with_raw_response.get_home(extra_headers={"x-stainless-retry-count": Omit()}) - assert len(response.http_request.headers.get_list("x-sdk-retry-count")) == 0 + assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("hanzoai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -882,70 +829,106 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/").mock(side_effect=retry_handler) - response = client.with_raw_response.get_home(extra_headers={"x-sdk-retry-count": "42"}) + response = client.with_raw_response.get_home(extra_headers={"x-stainless-retry-count": "42"}) - assert response.http_request.headers.get("x-sdk-retry-count") == "42" + assert response.http_request.headers.get("x-stainless-retry-count") == "42" + def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: + # Test that the proxy environment variables are set correctly + monkeypatch.setenv("HTTPS_PROXY", "https://example.org") -class TestAsyncHanzo: - client = AsyncHanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True) + client = DefaultHttpxClient() + + mounts = tuple(client._mounts.items()) + assert len(mounts) == 1 + assert mounts[0][0].pattern == "https://" + + @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning") + def test_default_client_creation(self) -> None: + # Ensure that the client can be initialized without any exceptions + DefaultHttpxClient( + verify=True, + cert=None, + trust_env=True, + http1=True, + http2=False, + limits=httpx.Limits(max_connections=100, max_keepalive_connections=20), + ) + + @pytest.mark.respx(base_url=base_url) + def test_follow_redirects(self, respx_mock: MockRouter, client: Hanzo) -> None: + # Test that the default follow_redirects=True allows following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) + + response = client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + assert response.status_code == 200 + assert response.json() == {"status": "ok"} + + @pytest.mark.respx(base_url=base_url) + def test_follow_redirects_disabled(self, respx_mock: MockRouter, client: Hanzo) -> None: + # Test that follow_redirects=False prevents following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + with pytest.raises(APIStatusError) as exc_info: + client.post("/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response) + + assert exc_info.value.response.status_code == 302 + assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" + + +class TestAsyncHanzo: @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_raw_response(self, respx_mock: MockRouter) -> None: + async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncHanzo) -> None: respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.post("/foo", cast_to=httpx.Response) + response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_client: AsyncHanzo) -> None: respx_mock.post("/foo").mock( - return_value=httpx.Response( - 200, - headers={"Content-Type": "application/binary"}, - content='{"foo": "bar"}', - ) + return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') ) - response = await self.client.post("/foo", cast_to=httpx.Response) + response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} - def test_copy(self) -> None: - copied = self.client.copy() - assert id(copied) != id(self.client) + def test_copy(self, async_client: AsyncHanzo) -> None: + copied = async_client.copy() + assert id(copied) != id(async_client) - copied = self.client.copy(api_key="another My API Key") + copied = async_client.copy(api_key="another My API Key") assert copied.api_key == "another My API Key" - assert self.client.api_key == "My API Key" + assert async_client.api_key == "My API Key" - def test_copy_default_options(self) -> None: + def test_copy_default_options(self, async_client: AsyncHanzo) -> None: # options that have a default are overridden correctly - copied = self.client.copy(max_retries=7) + copied = async_client.copy(max_retries=7) assert copied.max_retries == 7 - assert self.client.max_retries == 2 + assert async_client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout - assert isinstance(self.client.timeout, httpx.Timeout) - copied = self.client.copy(timeout=None) + assert isinstance(async_client.timeout, httpx.Timeout) + copied = async_client.copy(timeout=None) assert copied.timeout is None - assert isinstance(self.client.timeout, httpx.Timeout) + assert isinstance(async_client.timeout, httpx.Timeout) - def test_copy_default_headers(self) -> None: + async def test_copy_default_headers(self) -> None: client = AsyncHanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - default_headers={"X-Foo": "bar"}, + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) assert client.default_headers["X-Foo"] == "bar" @@ -976,13 +959,11 @@ def test_copy_default_headers(self) -> None: match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + await client.close() - def test_copy_default_query(self) -> None: + async def test_copy_default_query(self) -> None: client = AsyncHanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - default_query={"foo": "bar"}, + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} ) assert _get_params(client)["foo"] == "bar" @@ -1016,13 +997,15 @@ def test_copy_default_query(self) -> None: ): client.copy(set_default_query={}, default_query={"foo": "Bar"}) - def test_copy_signature(self) -> None: + await client.close() + + def test_copy_signature(self, async_client: AsyncHanzo) -> None: # ensure the same parameters that can be passed to the client are defined in the `.copy()` method init_signature = inspect.signature( # mypy doesn't like that we access the `__init__` property. - self.client.__init__, # type: ignore[misc] + async_client.__init__, # type: ignore[misc] ) - copy_signature = inspect.signature(self.client.copy) + copy_signature = inspect.signature(async_client.copy) exclude_params = {"transport", "proxies", "_strict_response_validation"} for name in init_signature.parameters.keys(): @@ -1032,13 +1015,13 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" - @pytest.mark.skipif(os.environ.get("CI") == "true", reason="Memory leak test is flaky in CI") - def test_copy_build_request(self) -> None: + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") + def test_copy_build_request(self, async_client: AsyncHanzo) -> None: options = FinalRequestOptions(method="get", url="/foo") def build_request(options: FinalRequestOptions) -> None: - client = self.client.copy() - client._build_request(options) + client_copy = async_client.copy() + client_copy._build_request(options) # ensure that the machinery is warmed up before tracing starts. build_request(options) @@ -1079,8 +1062,6 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic "hanzoai/_compat.py", # Standard library leaks we don't care about. "/logging/__init__.py", - # Regex compilation caching leaks - "/re/__init__.py", ] ): return @@ -1097,12 +1078,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic print(frame) raise AssertionError() - async def test_request_timeout(self) -> None: - request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + async def test_request_timeout(self, async_client: AsyncHanzo) -> None: + request = async_client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT - request = self.client._build_request( + request = async_client._build_request( FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) ) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore @@ -1110,56 +1091,52 @@ async def test_request_timeout(self) -> None: async def test_client_timeout_option(self) -> None: client = AsyncHanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - timeout=httpx.Timeout(0), + base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0) ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(0) + await client.close() + async def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used async with httpx.AsyncClient(timeout=None) as http_client: client = AsyncHanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - http_client=http_client, + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(None) + await client.close() + # no timeout given to the httpx client should not use the httpx default async with httpx.AsyncClient() as http_client: client = AsyncHanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - http_client=http_client, + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT + await client.close() + # explicitly passing the default timeout currently results in it being ignored async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = AsyncHanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - http_client=http_client, + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + await client.close() + def test_invalid_http_client(self) -> None: with pytest.raises(TypeError, match="Invalid `http_client` arg"): with httpx.Client() as http_client: @@ -1170,29 +1147,29 @@ def test_invalid_http_client(self) -> None: http_client=cast(Any, http_client), ) - def test_default_headers_option(self) -> None: - client = AsyncHanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - default_headers={"X-Foo": "bar"}, + async def test_default_headers_option(self) -> None: + test_client = AsyncHanzo( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) - request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" - assert request.headers.get("x-sdk-lang") == "python" + assert request.headers.get("x-stainless-lang") == "python" - client2 = AsyncHanzo( + test_client2 = AsyncHanzo( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={ "X-Foo": "stainless", - "X-SDK-Lang": "my-overriding-header", + "X-Stainless-Lang": "my-overriding-header", }, ) - request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "stainless" - assert request.headers.get("x-sdk-lang") == "my-overriding-header" + assert request.headers.get("x-stainless-lang") == "my-overriding-header" + + await test_client.close() + await test_client2.close() def test_validate_headers(self) -> None: client = AsyncHanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -1204,12 +1181,9 @@ def test_validate_headers(self) -> None: client2 = AsyncHanzo(base_url=base_url, api_key=None, _strict_response_validation=True) _ = client2 - def test_default_query_option(self) -> None: + async def test_default_query_option(self) -> None: client = AsyncHanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - default_query={"query_param": "bar"}, + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) url = httpx.URL(request.url) @@ -1225,8 +1199,10 @@ def test_default_query_option(self) -> None: url = httpx.URL(request.url) assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} - def test_request_extra_json(self) -> None: - request = self.client._build_request( + await client.close() + + def test_request_extra_json(self, client: Hanzo) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1237,7 +1213,7 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": False} - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1248,7 +1224,7 @@ def test_request_extra_json(self) -> None: assert data == {"baz": False} # `extra_json` takes priority over `json_data` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1259,8 +1235,8 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": None} - def test_request_extra_headers(self) -> None: - request = self.client._build_request( + def test_request_extra_headers(self, client: Hanzo) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1270,7 +1246,7 @@ def test_request_extra_headers(self) -> None: assert request.headers.get("X-Foo") == "Foo" # `extra_headers` takes priority over `default_headers` when keys clash - request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1281,8 +1257,8 @@ def test_request_extra_headers(self) -> None: ) assert request.headers.get("X-Bar") == "false" - def test_request_extra_query(self) -> None: - request = self.client._build_request( + def test_request_extra_query(self, client: Hanzo) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1295,7 +1271,7 @@ def test_request_extra_query(self) -> None: assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1309,7 +1285,7 @@ def test_request_extra_query(self) -> None: assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1325,7 +1301,7 @@ def test_request_extra_query(self) -> None: def test_multipart_repeating_array(self, async_client: AsyncHanzo) -> None: request = async_client._build_request( FinalRequestOptions.construct( - method="get", + method="post", url="/foo", headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, json_data={"array": ["foo", "bar"]}, @@ -1352,7 +1328,7 @@ def test_multipart_repeating_array(self, async_client: AsyncHanzo) -> None: ] @pytest.mark.respx(base_url=base_url) - async def test_basic_union_response(self, respx_mock: MockRouter) -> None: + async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncHanzo) -> None: class Model1(BaseModel): name: str @@ -1361,12 +1337,12 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" @pytest.mark.respx(base_url=base_url) - async def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + async def test_union_response_different_types(self, respx_mock: MockRouter, async_client: AsyncHanzo) -> None: """Union of objects with the same field name using a different type""" class Model1(BaseModel): @@ -1377,18 +1353,20 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model1) assert response.foo == 1 @pytest.mark.respx(base_url=base_url) - async def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + async def test_non_application_json_content_type_for_json_data( + self, respx_mock: MockRouter, async_client: AsyncHanzo + ) -> None: """ Response that sets Content-Type to something other than application/json but returns json data """ @@ -1404,23 +1382,21 @@ class Model(BaseModel): ) ) - response = await self.client.get("/foo", cast_to=Model) + response = await async_client.get("/foo", cast_to=Model) assert isinstance(response, Model) assert response.foo == 2 - def test_base_url_setter(self) -> None: - client = AsyncHanzo( - base_url="https://example.com/from_init", - api_key=api_key, - _strict_response_validation=True, - ) + async def test_base_url_setter(self) -> None: + client = AsyncHanzo(base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True) assert client.base_url == "https://example.com/from_init/" client.base_url = "https://example.com/from_setter" # type: ignore[assignment] assert client.base_url == "https://example.com/from_setter/" - def test_base_url_env(self) -> None: + await client.close() + + async def test_base_url_env(self) -> None: with update_env(HANZO_BASE_URL="http://localhost:5000/from/env"): client = AsyncHanzo(api_key=api_key, _strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" @@ -1428,27 +1404,20 @@ def test_base_url_env(self) -> None: # explicit environment arg requires explicitness with update_env(HANZO_BASE_URL="http://localhost:5000/from/env"): with pytest.raises(ValueError, match=r"you must pass base_url=None"): - AsyncHanzo( - api_key=api_key, - _strict_response_validation=True, - environment="production", - ) + AsyncHanzo(api_key=api_key, _strict_response_validation=True, environment="production") client = AsyncHanzo( - base_url=None, - api_key=api_key, - _strict_response_validation=True, - environment="production", + base_url=None, api_key=api_key, _strict_response_validation=True, environment="production" ) assert str(client.base_url).startswith("https://api.hanzo.ai") + await client.close() + @pytest.mark.parametrize( "client", [ AsyncHanzo( - base_url="http://localhost:5000/custom/path/", - api_key=api_key, - _strict_response_validation=True, + base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True ), AsyncHanzo( base_url="http://localhost:5000/custom/path/", @@ -1459,7 +1428,7 @@ def test_base_url_env(self) -> None: ], ids=["standard", "custom http client"], ) - def test_base_url_trailing_slash(self, client: AsyncHanzo) -> None: + async def test_base_url_trailing_slash(self, client: AsyncHanzo) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1468,14 +1437,13 @@ def test_base_url_trailing_slash(self, client: AsyncHanzo) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() @pytest.mark.parametrize( "client", [ AsyncHanzo( - base_url="http://localhost:5000/custom/path/", - api_key=api_key, - _strict_response_validation=True, + base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True ), AsyncHanzo( base_url="http://localhost:5000/custom/path/", @@ -1486,7 +1454,7 @@ def test_base_url_trailing_slash(self, client: AsyncHanzo) -> None: ], ids=["standard", "custom http client"], ) - def test_base_url_no_trailing_slash(self, client: AsyncHanzo) -> None: + async def test_base_url_no_trailing_slash(self, client: AsyncHanzo) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1495,14 +1463,13 @@ def test_base_url_no_trailing_slash(self, client: AsyncHanzo) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() @pytest.mark.parametrize( "client", [ AsyncHanzo( - base_url="http://localhost:5000/custom/path/", - api_key=api_key, - _strict_response_validation=True, + base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True ), AsyncHanzo( base_url="http://localhost:5000/custom/path/", @@ -1513,7 +1480,7 @@ def test_base_url_no_trailing_slash(self, client: AsyncHanzo) -> None: ], ids=["standard", "custom http client"], ) - def test_absolute_request_url(self, client: AsyncHanzo) -> None: + async def test_absolute_request_url(self, client: AsyncHanzo) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1522,51 +1489,47 @@ def test_absolute_request_url(self, client: AsyncHanzo) -> None: ), ) assert request.url == "https://myapi.com/foo" + await client.close() async def test_copied_client_does_not_close_http(self) -> None: - client = AsyncHanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True) - assert not client.is_closed() + test_client = AsyncHanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert not test_client.is_closed() - copied = client.copy() - assert copied is not client + copied = test_client.copy() + assert copied is not test_client del copied await asyncio.sleep(0.2) - assert not client.is_closed() + assert not test_client.is_closed() async def test_client_context_manager(self) -> None: - client = AsyncHanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True) - async with client as c2: - assert c2 is client + test_client = AsyncHanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True) + async with test_client as c2: + assert c2 is test_client assert not c2.is_closed() - assert not client.is_closed() - assert client.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + async def test_client_response_validation_error(self, respx_mock: MockRouter, async_client: AsyncHanzo) -> None: class Model(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) with pytest.raises(APIResponseValidationError) as exc: - await self.client.get("/foo", cast_to=Model) + await async_client.get("/foo", cast_to=Model) assert isinstance(exc.value.__cause__, ValidationError) async def test_client_max_retries_validation(self) -> None: with pytest.raises(TypeError, match=r"max_retries cannot be None"): AsyncHanzo( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - max_retries=cast(Any, None), + base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None) ) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: class Model(BaseModel): name: str @@ -1578,11 +1541,14 @@ class Model(BaseModel): with pytest.raises(APIResponseValidationError): await strict_client.get("/foo", cast_to=Model) - client = AsyncHanzo(base_url=base_url, api_key=api_key, _strict_response_validation=False) + non_strict_client = AsyncHanzo(base_url=base_url, api_key=api_key, _strict_response_validation=False) - response = await client.get("/foo", cast_to=Model) + response = await non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] + await strict_client.close() + await non_strict_client.close() + @pytest.mark.parametrize( "remaining_retries,retry_after,timeout", [ @@ -1605,47 +1571,36 @@ class Model(BaseModel): ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) - @pytest.mark.asyncio - async def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: - client = AsyncHanzo(base_url=base_url, api_key=api_key, _strict_response_validation=True) - + async def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, async_client: AsyncHanzo + ) -> None: headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) - calculated = client._calculate_retry_timeout(remaining_retries, options, headers) + calculated = async_client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] @mock.patch("hanzoai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncHanzo) -> None: respx_mock.get("/").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await self.client.get( - "/", - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) + await async_client.with_streaming_response.get_home().__aenter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(async_client) == 0 @mock.patch("hanzoai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncHanzo) -> None: respx_mock.get("/").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await self.client.get( - "/", - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) - - assert _get_open_connections(self.client) == 0 + await async_client.with_streaming_response.get_home().__aenter__() + assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("hanzoai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio @pytest.mark.parametrize("failure_mode", ["status", "exception"]) async def test_retries_taken( self, @@ -1672,17 +1627,13 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: response = await client.with_raw_response.get_home() assert response.retries_taken == failures_before_success - assert int(response.http_request.headers.get("x-sdk-retry-count")) == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("hanzoai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_omit_retry_count_header( - self, - async_client: AsyncHanzo, - failures_before_success: int, - respx_mock: MockRouter, + self, async_client: AsyncHanzo, failures_before_success: int, respx_mock: MockRouter ) -> None: client = async_client.with_options(max_retries=4) @@ -1697,19 +1648,15 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/").mock(side_effect=retry_handler) - response = await client.with_raw_response.get_home(extra_headers={"x-sdk-retry-count": Omit()}) + response = await client.with_raw_response.get_home(extra_headers={"x-stainless-retry-count": Omit()}) - assert len(response.http_request.headers.get_list("x-sdk-retry-count")) == 0 + assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("hanzoai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_overwrite_retry_count_header( - self, - async_client: AsyncHanzo, - failures_before_success: int, - respx_mock: MockRouter, + self, async_client: AsyncHanzo, failures_before_success: int, respx_mock: MockRouter ) -> None: client = async_client.with_options(max_retries=4) @@ -1724,60 +1671,59 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/").mock(side_effect=retry_handler) - response = await client.with_raw_response.get_home(extra_headers={"x-sdk-retry-count": "42"}) + response = await client.with_raw_response.get_home(extra_headers={"x-stainless-retry-count": "42"}) - assert response.http_request.headers.get("x-sdk-retry-count") == "42" + assert response.http_request.headers.get("x-stainless-retry-count") == "42" - def test_get_platform(self) -> None: - # A previous implementation of asyncify could leave threads unterminated when - # used with nest_asyncio. - # - # Since nest_asyncio.apply() is global and cannot be un-applied, this - # test is run in a separate process to avoid affecting other tests. - test_code = dedent( - """ - import asyncio - import threading + async def test_get_platform(self) -> None: + platform = await asyncify(get_platform)() + assert isinstance(platform, (str, OtherPlatform)) - # Set default event loop policy BEFORE importing hanzoai - # (hanzo-async auto-configures uvloop on import, which conflicts with nest_asyncio) - asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy()) + async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: + # Test that the proxy environment variables are set correctly + monkeypatch.setenv("HTTPS_PROXY", "https://example.org") - import nest_asyncio - from hanzoai._utils import asyncify - from hanzoai._base_client import get_platform + client = DefaultAsyncHttpxClient() - # Reset policy again after imports (in case it changed) - asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy()) + mounts = tuple(client._mounts.items()) + assert len(mounts) == 1 + assert mounts[0][0].pattern == "https://" - async def test_main() -> None: - result = await asyncify(get_platform)() - print(result) - for thread in threading.enumerate(): - print(thread.name) + @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning") + async def test_default_client_creation(self) -> None: + # Ensure that the client can be initialized without any exceptions + DefaultAsyncHttpxClient( + verify=True, + cert=None, + trust_env=True, + http1=True, + http2=False, + limits=httpx.Limits(max_connections=100, max_keepalive_connections=20), + ) - nest_asyncio.apply() - asyncio.run(test_main()) - """ + @pytest.mark.respx(base_url=base_url) + async def test_follow_redirects(self, respx_mock: MockRouter, async_client: AsyncHanzo) -> None: + # Test that the default follow_redirects=True allows following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) - with subprocess.Popen( - [sys.executable, "-c", test_code], - text=True, - ) as process: - timeout = 10 # seconds - - start_time = time.monotonic() - while True: - return_code = process.poll() - if return_code is not None: - if return_code != 0: - raise AssertionError("calling get_platform using asyncify resulted in a non-zero exit code") - - # success - break - - if time.monotonic() - start_time > timeout: - process.kill() - raise AssertionError("calling get_platform using asyncify resulted in a hung process") - - time.sleep(0.1) + respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) + + response = await async_client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + assert response.status_code == 200 + assert response.json() == {"status": "ok"} + + @pytest.mark.respx(base_url=base_url) + async def test_follow_redirects_disabled(self, respx_mock: MockRouter, async_client: AsyncHanzo) -> None: + # Test that follow_redirects=False prevents following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + + with pytest.raises(APIStatusError) as exc_info: + await async_client.post( + "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response + ) + + assert exc_info.value.response.status_code == 302 + assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" diff --git a/tests/test_config.py b/tests/test_config.py deleted file mode 100644 index 7738f4c79..000000000 --- a/tests/test_config.py +++ /dev/null @@ -1,409 +0,0 @@ -"""Tests for hanzoai.config โ€” hierarchical config discovery and merging.""" - -from __future__ import annotations - -import os -import json -from pathlib import Path - -import pytest - -from hanzoai.config import ( - ConfigEntry, - ConfigError, - McpWsConfig, - OAuthConfig, - ConfigLoader, - ConfigSource, - McpSdkConfig, - McpTransport, - RuntimeConfig, - McpOAuthConfig, - McpStdioConfig, - McpRemoteConfig, - McpClaudeAiProxyConfig, - _deep_merge, -) - -# --------------------------------------------------------------------------- -# Deep merge -# --------------------------------------------------------------------------- - - -class TestDeepMerge: - def test_flat_override(self): - assert _deep_merge({"a": 1}, {"a": 2}) == {"a": 2} - - def test_flat_union(self): - assert _deep_merge({"a": 1}, {"b": 2}) == {"a": 1, "b": 2} - - def test_nested_merge(self): - base = {"a": {"x": 1, "y": 2}} - over = {"a": {"y": 3, "z": 4}} - assert _deep_merge(base, over) == {"a": {"x": 1, "y": 3, "z": 4}} - - def test_nested_replace_non_dict(self): - assert _deep_merge({"a": {"x": 1}}, {"a": 42}) == {"a": 42} - - def test_empty(self): - assert _deep_merge({}, {"a": 1}) == {"a": 1} - assert _deep_merge({"a": 1}, {}) == {"a": 1} - - -# --------------------------------------------------------------------------- -# ConfigLoader.discover -# --------------------------------------------------------------------------- - - -class TestDiscover: - def test_returns_three_entries(self, tmp_path: Path): - loader = ConfigLoader(cwd=tmp_path, config_home=tmp_path / ".hanzo") - entries = loader.discover() - assert len(entries) == 3 - assert entries[0].source == ConfigSource.User - assert entries[1].source == ConfigSource.Project - assert entries[2].source == ConfigSource.Local - - def test_paths(self, tmp_path: Path): - home = tmp_path / "home" / ".hanzo" - cwd = tmp_path / "project" - loader = ConfigLoader(cwd=cwd, config_home=home) - entries = loader.discover() - assert entries[0].path == home / "settings.json" - assert entries[1].path == cwd / ".hanzo" / "settings.json" - assert entries[2].path == cwd / ".hanzo" / "settings.local.json" - - -# --------------------------------------------------------------------------- -# ConfigLoader.default_for -# --------------------------------------------------------------------------- - - -class TestDefaultFor: - def test_uses_env_var(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch): - custom = tmp_path / "custom" - monkeypatch.setenv("HANZO_CONFIG_HOME", str(custom)) - loader = ConfigLoader.default_for(tmp_path) - assert loader.config_home == custom - - def test_falls_back_to_home(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch): - monkeypatch.delenv("HANZO_CONFIG_HOME", raising=False) - loader = ConfigLoader.default_for(tmp_path) - assert loader.config_home == Path.home() / ".hanzo" - - -# --------------------------------------------------------------------------- -# ConfigLoader.load โ€” file handling -# --------------------------------------------------------------------------- - - -def _write_json(path: Path, data: object) -> None: - path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(json.dumps(data), encoding="utf-8") - - -class TestLoad: - def test_empty_when_no_files(self, tmp_path: Path): - loader = ConfigLoader(cwd=tmp_path, config_home=tmp_path / "h") - cfg = loader.load() - assert cfg.merged == {} - assert cfg.loaded_entries == [] - - def test_single_user_file(self, tmp_path: Path): - home = tmp_path / "h" - _write_json(home / "settings.json", {"theme": "dark"}) - cfg = ConfigLoader(cwd=tmp_path, config_home=home).load() - assert cfg.merged == {"theme": "dark"} - assert len(cfg.loaded_entries) == 1 - assert cfg.loaded_entries[0].source == ConfigSource.User - - def test_project_overrides_user(self, tmp_path: Path): - home = tmp_path / "h" - _write_json(home / "settings.json", {"a": 1, "b": 2}) - _write_json(tmp_path / ".hanzo" / "settings.json", {"b": 99}) - cfg = ConfigLoader(cwd=tmp_path, config_home=home).load() - assert cfg.merged == {"a": 1, "b": 99} - assert len(cfg.loaded_entries) == 2 - - def test_local_overrides_project(self, tmp_path: Path): - home = tmp_path / "h" - _write_json(home / "settings.json", {"a": 1}) - _write_json(tmp_path / ".hanzo" / "settings.json", {"b": 2}) - _write_json(tmp_path / ".hanzo" / "settings.local.json", {"b": 3, "c": 4}) - cfg = ConfigLoader(cwd=tmp_path, config_home=home).load() - assert cfg.merged == {"a": 1, "b": 3, "c": 4} - assert len(cfg.loaded_entries) == 3 - - def test_invalid_json_raises(self, tmp_path: Path): - home = tmp_path / "h" - p = home / "settings.json" - p.parent.mkdir(parents=True, exist_ok=True) - p.write_text("{bad json", encoding="utf-8") - with pytest.raises(ConfigError, match="invalid JSON"): - ConfigLoader(cwd=tmp_path, config_home=home).load() - - def test_non_object_top_level_raises(self, tmp_path: Path): - home = tmp_path / "h" - _write_json(home / "settings.json", [1, 2, 3]) - with pytest.raises(ConfigError, match="top-level value must be a JSON object"): - ConfigLoader(cwd=tmp_path, config_home=home).load() - - def test_mcp_servers_replaced_not_merged(self, tmp_path: Path): - home = tmp_path / "h" - _write_json(home / "settings.json", { - "mcpServers": { - "alpha": {"command": "a", "transport": "stdio"}, - "beta": {"command": "b", "transport": "stdio"}, - } - }) - _write_json(tmp_path / ".hanzo" / "settings.json", { - "mcpServers": { - "gamma": {"command": "g", "transport": "stdio"}, - } - }) - cfg = ConfigLoader(cwd=tmp_path, config_home=home).load(trust_project_mcp=True) - servers = cfg.mcp_servers() - assert set(servers.keys()) == {"gamma"} - - -# --------------------------------------------------------------------------- -# RuntimeConfig accessors -# --------------------------------------------------------------------------- - - -class TestRuntimeConfig: - def test_empty(self): - cfg = RuntimeConfig.empty() - assert cfg.merged == {} - assert cfg.get("anything") is None - - def test_get(self): - cfg = RuntimeConfig(merged={"foo": "bar"}) - assert cfg.get("foo") == "bar" - assert cfg.get("nope") is None - - -# --------------------------------------------------------------------------- -# MCP server parsing -# --------------------------------------------------------------------------- - - -class TestMcpServers: - def test_stdio(self): - cfg = RuntimeConfig(merged={ - "mcpServers": { - "myserver": { - "transport": "stdio", - "command": "node", - "args": ["server.js"], - "env": {"PORT": "8080"}, - } - } - }) - servers = cfg.mcp_servers() - s = servers["myserver"] - assert isinstance(s, McpStdioConfig) - assert s.command == "node" - assert s.args == ["server.js"] - assert s.env == {"PORT": "8080"} - assert s.transport == McpTransport.Stdio - - def test_stdio_default_transport(self): - cfg = RuntimeConfig(merged={ - "mcpServers": { - "s": {"command": "echo"} - } - }) - s = cfg.mcp_servers()["s"] - assert isinstance(s, McpStdioConfig) - - def test_http_remote(self): - cfg = RuntimeConfig(merged={ - "mcpServers": { - "remote": { - "transport": "http", - "url": "https://example.com/mcp", - "headers": {"Authorization": "Bearer tok"}, - } - } - }) - s = cfg.mcp_servers()["remote"] - assert isinstance(s, McpRemoteConfig) - assert s.url == "https://example.com/mcp" - assert s.transport == McpTransport.Http - - def test_sse_remote(self): - cfg = RuntimeConfig(merged={ - "mcpServers": { - "sse": {"transport": "sse", "url": "https://x.com/events"} - } - }) - s = cfg.mcp_servers()["sse"] - assert isinstance(s, McpRemoteConfig) - assert s.transport == McpTransport.Sse - - def test_ws(self): - cfg = RuntimeConfig(merged={ - "mcpServers": { - "ws": {"transport": "ws", "url": "wss://x.com/ws"} - } - }) - s = cfg.mcp_servers()["ws"] - assert isinstance(s, McpWsConfig) - assert s.transport == McpTransport.Ws - assert s.url == "wss://x.com/ws" - - def test_sdk(self): - cfg = RuntimeConfig(merged={ - "mcpServers": { - "my-sdk": {"transport": "sdk", "name": "my-sdk-server"} - } - }) - s = cfg.mcp_servers()["my-sdk"] - assert isinstance(s, McpSdkConfig) - assert s.transport == McpTransport.Sdk - assert s.name == "my-sdk-server" - - def test_claudeai_proxy(self): - cfg = RuntimeConfig(merged={ - "mcpServers": { - "proxy": { - "transport": "claudeai-proxy", - "url": "https://claude.ai/proxy", - "id": "server-123", - } - } - }) - s = cfg.mcp_servers()["proxy"] - assert isinstance(s, McpClaudeAiProxyConfig) - assert s.transport == McpTransport.ClaudeAiProxy - assert s.url == "https://claude.ai/proxy" - assert s.id == "server-123" - - def test_remote_with_oauth(self): - cfg = RuntimeConfig(merged={ - "mcpServers": { - "authed": { - "transport": "http", - "url": "https://example.com/mcp", - "oauth": { - "clientId": "cid", - "callbackPort": 9999, - "authServerMetadataUrl": "https://auth.example.com/.well-known", - "xaa": True, - }, - } - } - }) - s = cfg.mcp_servers()["authed"] - assert isinstance(s, McpRemoteConfig) - assert s.oauth is not None - assert s.oauth.client_id == "cid" - assert s.oauth.callback_port == 9999 - assert s.oauth.xaa is True - - def test_sdk_missing_name_raises(self): - cfg = RuntimeConfig(merged={ - "mcpServers": {"bad": {"transport": "sdk"}} - }) - with pytest.raises(ConfigError, match="requires 'name'"): - cfg.mcp_servers() - - def test_claudeai_proxy_missing_id_raises(self): - cfg = RuntimeConfig(merged={ - "mcpServers": {"bad": {"transport": "claudeai-proxy", "url": "https://x.com"}} - }) - with pytest.raises(ConfigError, match="requires 'id'"): - cfg.mcp_servers() - - def test_unknown_transport_raises(self): - cfg = RuntimeConfig(merged={ - "mcpServers": {"bad": {"transport": "grpc"}} - }) - with pytest.raises(ConfigError, match="unknown transport"): - cfg.mcp_servers() - - def test_stdio_missing_command_raises(self): - cfg = RuntimeConfig(merged={ - "mcpServers": {"bad": {"transport": "stdio"}} - }) - with pytest.raises(ConfigError, match="requires 'command'"): - cfg.mcp_servers() - - def test_http_missing_url_raises(self): - cfg = RuntimeConfig(merged={ - "mcpServers": {"bad": {"transport": "http"}} - }) - with pytest.raises(ConfigError, match="requires 'url'"): - cfg.mcp_servers() - - def test_no_mcp_servers_returns_empty(self): - assert RuntimeConfig.empty().mcp_servers() == {} - - -# --------------------------------------------------------------------------- -# OAuth parsing -# --------------------------------------------------------------------------- - - -class TestOAuth: - def test_parses_camel_case(self): - cfg = RuntimeConfig(merged={ - "oauth": { - "clientId": "my-id", - "authorizeUrl": "https://hanzo.id/authorize", - "tokenUrl": "https://hanzo.id/token", - "scopes": ["read", "write"], - "callbackPort": 8080, - } - }) - oauth = cfg.oauth() - assert isinstance(oauth, OAuthConfig) - assert oauth.client_id == "my-id" - assert oauth.authorize_url == "https://hanzo.id/authorize" - assert oauth.token_url == "https://hanzo.id/token" - assert oauth.scopes == ["read", "write"] - assert oauth.callback_port == 8080 - - def test_parses_snake_case(self): - cfg = RuntimeConfig(merged={ - "oauth": { - "client_id": "cid", - "authorize_url": "https://hanzo.id/authorize", - "token_url": "https://hanzo.id/token", - } - }) - oauth = cfg.oauth() - assert oauth is not None - assert oauth.client_id == "cid" - assert oauth.authorize_url == "https://hanzo.id/authorize" - - def test_returns_none_when_missing(self): - assert RuntimeConfig.empty().oauth() is None - - def test_returns_none_when_no_client_id(self): - cfg = RuntimeConfig(merged={ - "oauth": { - "authorizeUrl": "https://x.com/auth", - "tokenUrl": "https://x.com/token", - } - }) - assert cfg.oauth() is None - - def test_returns_none_when_no_authorize_url(self): - cfg = RuntimeConfig(merged={ - "oauth": { - "clientId": "cid", - "tokenUrl": "https://x.com/token", - } - }) - assert cfg.oauth() is None - - def test_returns_none_when_no_token_url(self): - cfg = RuntimeConfig(merged={ - "oauth": { - "clientId": "cid", - "authorizeUrl": "https://x.com/auth", - } - }) - assert cfg.oauth() is None diff --git a/tests/test_fallback.py b/tests/test_fallback.py deleted file mode 100644 index 6bbd4a0f6..000000000 --- a/tests/test_fallback.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python3 -"""Test the fallback handler.""" - -import sys -import asyncio -from pathlib import Path - -import pytest -from rich.console import Console - -# Add hanzo src to path -sys.path.insert(0, str(Path(__file__).parent.parent / "pkg" / "hanzo" / "src")) - -from hanzo.fallback_handler import FallbackHandler, smart_chat - - -async def test_fallback(): - """Test the fallback handler.""" - console = Console() - - console.print("\n[bold cyan]Testing Fallback Handler[/bold cyan]\n") - - # Create handler and check status - handler = FallbackHandler() - handler.print_status(console) - - # Test smart chat - console.print("\n[bold]Testing smart chat with automatic fallback:[/bold]") - - test_message = "What is 2 + 2? Reply with just the number." - console.print(f"\nMessage: [cyan]{test_message}[/cyan]") - - response = await smart_chat(test_message, console) - - if response: - console.print(f"\n[green]Success! AI responded:[/green]") - console.print(f"[bold]{response}[/bold]") - else: - console.print("\n[red]Failed to get response from any AI option[/red]") - console.print("\n[yellow]Setup suggestions:[/yellow]") - console.print(handler.suggest_setup()) - - return response is not None - - -if __name__ == "__main__": - success = asyncio.run(test_fallback()) - sys.exit(0 if success else 1) diff --git a/tests/test_files.py b/tests/test_files.py index 2a94cc86b..3001a1001 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -43,10 +43,7 @@ async def test_async_tuple_input() -> None: def test_string_not_allowed() -> None: - with pytest.raises( - TypeError, - match="Expected file types input to be a FileContent type or to be a tuple", - ): + with pytest.raises(TypeError, match="Expected file types input to be a FileContent type or to be a tuple"): to_httpx_files( { "file": "foo", # type: ignore diff --git a/tests/test_gpt5_orchestration.py b/tests/test_gpt5_orchestration.py deleted file mode 100755 index 70a76cd4b..000000000 --- a/tests/test_gpt5_orchestration.py +++ /dev/null @@ -1,305 +0,0 @@ -#!/usr/bin/env python3 -""" -Test GPT-5/Codex as orchestrator for comprehensive code review. - -This script demonstrates using the most powerful models (GPT-5, GPT-4, Codex) -as orchestrators to review all code and provide system-level improvements. -""" - -import os -import sys -import json -import asyncio -import subprocess -from pathlib import Path - -from rich.panel import Panel -from rich.table import Table -from rich.syntax import Syntax -from rich.console import Console - -console = Console() - - -class GPT5CodeReviewer: - """Use GPT-5/Codex for comprehensive code review and orchestration.""" - - def __init__(self, model="gpt-5", api_key=None): - """Initialize with specified model. - - Args: - model: Model to use (gpt-5, gpt-4o, gpt-4-turbo, codex, o3) - api_key: OpenAI API key (reads from env if not provided) - """ - self.model = model - self.api_key = api_key or os.getenv("OPENAI_API_KEY") - - # Model configurations for different review scenarios - self.model_configs = { - "gpt-5": { - "name": "gpt-5-latest", - "context": 128000, - "capabilities": [ - "code_review", - "architecture", - "security", - "performance", - ], - "cost_per_1k": 0.15, - }, - "gpt-4o": { - "name": "gpt-4o", - "context": 128000, - "capabilities": ["code_review", "architecture", "refactoring"], - "cost_per_1k": 0.05, - }, - "gpt-4-turbo": { - "name": "gpt-4-turbo-preview", - "context": 128000, - "capabilities": ["code_review", "documentation"], - "cost_per_1k": 0.03, - }, - "codex": { - "name": "code-davinci-002", - "context": 8000, - "capabilities": ["code_generation", "completion", "review"], - "cost_per_1k": 0.02, - }, - "o3": { - "name": "o3-preview", - "context": 200000, - "capabilities": [ - "reasoning", - "architecture", - "security", - "optimization", - ], - "cost_per_1k": 0.20, - }, - } - - async def review_with_hanzo_dev(self, project_path: Path): - """Use hanzo dev with GPT-5 orchestrator to review entire project.""" - - console.print( - Panel.fit( - f"[bold cyan]Code Review with {self.model.upper()} Orchestrator[/bold cyan]\nProject: {project_path}", - title="๐Ÿค– AI Code Review", - ) - ) - - # Start hanzo dev with GPT-5 orchestrator - cmd = [ - "hanzo", - "dev", - "--orchestrator", - self.model, - "--instances", - "3", # Main coder + 2 reviewers - "--critic-instances", - "2", # 2 critic agents - "--enable-guardrails", - "--workspace", - str(project_path), - "--review-mode", # Special mode for code review - ] - - console.print(f"[yellow]Starting:[/yellow] {' '.join(cmd)}") - - # Create review prompts for different aspects - review_tasks = [ - { - "aspect": "Security", - "prompt": "Review the codebase for security vulnerabilities, focusing on:\n" - "- Input validation\n- Authentication/authorization\n" - "- Data sanitization\n- Secret management\n" - "- Dependency vulnerabilities", - }, - { - "aspect": "Performance", - "prompt": "Analyze performance bottlenecks and optimization opportunities:\n" - "- Algorithm complexity\n- Database queries\n" - "- Caching strategies\n- Async/parallel processing\n" - "- Memory usage patterns", - }, - { - "aspect": "Architecture", - "prompt": "Evaluate the system architecture and design patterns:\n" - "- SOLID principles adherence\n- Coupling and cohesion\n" - "- Scalability considerations\n- Design pattern usage\n" - "- Module boundaries and interfaces", - }, - { - "aspect": "Code Quality", - "prompt": "Assess overall code quality and maintainability:\n" - "- Code duplication\n- Naming conventions\n" - "- Documentation completeness\n- Test coverage\n" - "- Error handling patterns", - }, - { - "aspect": "Best Practices", - "prompt": "Check adherence to language-specific best practices:\n" - "- Python: PEP 8, type hints, docstrings\n" - "- JavaScript: ESLint rules, modern syntax\n" - "- Error handling and logging\n- Configuration management", - }, - ] - - # Display review plan - table = Table(title="Review Plan", show_header=True) - table.add_column("Aspect", style="cyan") - table.add_column("Focus Areas", style="white") - - for task in review_tasks: - table.add_row( - task["aspect"], - task["prompt"].replace("\n", " ").replace("- ", "โ€ข ")[:80] + "...", - ) - - console.print(table) - - # Execute reviews - results = {} - for task in review_tasks: - console.print(f"\n[bold]Reviewing: {task['aspect']}[/bold]") - - # Simulate review execution (in real implementation, this would - # call hanzo dev with the specific review prompt) - result = await self._execute_review(task["aspect"], task["prompt"]) - results[task["aspect"]] = result - - # Display summary - if result.get("issues"): - console.print(f" [red]โœ— Found {len(result['issues'])} issues[/red]") - else: - console.print(f" [green]โœ“ No major issues found[/green]") - - if result.get("suggestions"): - console.print(f" [yellow]๐Ÿ’ก {len(result['suggestions'])} suggestions[/yellow]") - - return results - - async def _execute_review(self, aspect: str, prompt: str): - """Execute a specific review aspect.""" - - # In a real implementation, this would: - # 1. Send the prompt to hanzo dev - # 2. Wait for orchestrator to coordinate agents - # 3. Collect and format results - - # For demonstration, return sample results - return { - "aspect": aspect, - "status": "completed", - "issues": [], - "suggestions": [ - f"Consider improving {aspect.lower()} in module X", - f"Optimize {aspect.lower()} patterns in service Y", - ], - "score": 85, - } - - def generate_review_report(self, results: dict): - """Generate comprehensive review report.""" - - console.print("\n" + "=" * 60) - console.print( - Panel.fit( - "[bold green]Code Review Complete[/bold green]", - title="๐Ÿ“Š Summary Report", - ) - ) - - # Overall score calculation - total_score = sum(r.get("score", 0) for r in results.values()) - avg_score = total_score / len(results) if results else 0 - - # Score interpretation - if avg_score >= 90: - grade = "A" - color = "green" - message = "Excellent code quality!" - elif avg_score >= 80: - grade = "B" - color = "yellow" - message = "Good quality with minor improvements needed" - elif avg_score >= 70: - grade = "C" - color = "orange" - message = "Acceptable but needs attention" - else: - grade = "D" - color = "red" - message = "Significant improvements required" - - console.print(f"\n[bold]Overall Grade:[/bold] [{color}]{grade}[/{color}] ({avg_score:.1f}/100)") - console.print(f"[italic]{message}[/italic]\n") - - # Detailed results - for aspect, result in results.items(): - console.print(f"[bold cyan]{aspect}:[/bold cyan]") - console.print(f" Score: {result.get('score', 0)}/100") - - if result.get("issues"): - console.print(" [red]Issues:[/red]") - for issue in result["issues"][:3]: - console.print(f" โ€ข {issue}") - - if result.get("suggestions"): - console.print(" [yellow]Suggestions:[/yellow]") - for suggestion in result["suggestions"][:3]: - console.print(f" โ€ข {suggestion}") - - console.print() - - -async def main(): - """Run GPT-5 code review demonstration.""" - - # Display available models - console.print( - Panel.fit( - "[bold]Available Orchestrator Models[/bold]\n\n" - "โ€ข [cyan]gpt-5[/cyan] - Most advanced, best for complex reviews\n" - "โ€ข [cyan]gpt-4o[/cyan] - Optimized GPT-4, good balance\n" - "โ€ข [cyan]gpt-4-turbo[/cyan] - Fast GPT-4 variant\n" - "โ€ข [cyan]codex[/cyan] - Specialized for code\n" - "โ€ข [cyan]o3[/cyan] - Advanced reasoning model\n", - title="๐ŸŽฏ Model Selection", - ) - ) - - # Example: Review current project with GPT-5 - reviewer = GPT5CodeReviewer(model="gpt-5") - project_path = Path.cwd() - - console.print("\n[bold]Starting comprehensive code review...[/bold]\n") - - # Run the review - results = await reviewer.review_with_hanzo_dev(project_path) - - # Generate report - reviewer.generate_review_report(results) - - # Show example commands - console.print("\n" + "=" * 60) - console.print( - Panel.fit( - "[bold]Example Commands[/bold]\n\n" - "[cyan]# Use GPT-5 as orchestrator[/cyan]\n" - "hanzo dev --orchestrator gpt-5\n\n" - "[cyan]# Use GPT-4o for balanced performance[/cyan]\n" - "hanzo dev --orchestrator gpt-4o --instances 3\n\n" - "[cyan]# Use Codex for code-specific tasks[/cyan]\n" - "hanzo dev --orchestrator codex --focus code-generation\n\n" - "[cyan]# Use O3 for complex reasoning[/cyan]\n" - "hanzo dev --orchestrator o3 --enable-reasoning\n\n" - "[cyan]# Hybrid: Local for simple, GPT-5 for complex[/cyan]\n" - "hanzo dev --orchestrator gpt-5 --use-hanzo-net\n", - title="๐Ÿ’ก Usage Examples", - ) - ) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/tests/test_hanzo_dev.py b/tests/test_hanzo_dev.py deleted file mode 100644 index 2c8e26513..000000000 --- a/tests/test_hanzo_dev.py +++ /dev/null @@ -1,378 +0,0 @@ -#!/usr/bin/env python3 -""" -Comprehensive test script for hanzo dev functionality. -Tests all orchestrator modes and ensures everything works. -""" - -import os -import sys -import time -import shutil -import asyncio -import subprocess -from pathlib import Path - -# Add src to path for local testing -sys.path.insert(0, str(Path(__file__).parent / "pkg" / "hanzo" / "src")) - - -def print_test(name): - """Print test header.""" - print(f"\n{'=' * 60}") - print(f"TESTING: {name}") - print("=" * 60) - - -def check_command_exists(cmd): - """Check if a command exists.""" - return shutil.which(cmd) is not None - - -async def test_imports(): - """Test that all necessary imports work.""" - print_test("Module Imports") - - try: - from hanzo.cli import cli, __version__ - - print(f"โœ“ CLI module imported successfully (version {__version__})") - - from hanzo.dev import ( - HanzoDevREPL, - HanzoDevOrchestrator, - MultiClaudeOrchestrator, - ) - - print("โœ“ Dev module imported successfully") - - from hanzo.orchestrator_config import OrchestratorMode, get_orchestrator_config - - print("โœ“ Orchestrator config module imported successfully") - - return True - except Exception as e: - print(f"โœ— Import error: {e}") - return False - - -async def test_orchestrator_configs(): - """Test orchestrator configuration system.""" - print_test("Orchestrator Configurations") - - from hanzo.orchestrator_config import OrchestratorMode, get_orchestrator_config - - test_configs = [ - "gpt-4", - "gpt-5", - "codex", - "claude", - "router:gpt-4", - "direct:claude-3-5", - "local:llama3.2", - "codestral", - "gpt-5-pro-codex", - ] - - for config_name in test_configs: - try: - config = get_orchestrator_config(config_name) - print(f"โœ“ Config '{config_name}': mode={config.mode.value}, primary={config.primary_model}") - except Exception as e: - print(f"โœ— Config '{config_name}' failed: {e}") - return False - - return True - - -async def test_repl_initialization(): - """Test REPL initialization without running.""" - print_test("REPL Initialization") - - try: - from hanzo.dev import ( - HanzoDevREPL, - HanzoDevOrchestrator, - MultiClaudeOrchestrator, - ) - from rich.console import Console - - # Create a mock orchestrator - console = Console() - orchestrator = MultiClaudeOrchestrator( - workspace_dir="/tmp/test_workspace", - claude_path="/usr/bin/claude", # Mock path - num_instances=2, - enable_mcp=True, - enable_networking=True, - enable_guardrails=True, - console=console, - orchestrator_model="gpt-4", - ) - - # Create REPL - repl = HanzoDevREPL(orchestrator) - print("โœ“ REPL created successfully") - - # Test command registration - assert "help" in repl.commands - assert "exit" in repl.commands - assert "status" in repl.commands - print("โœ“ Commands registered successfully") - - return True - except Exception as e: - print(f"โœ— REPL initialization failed: {e}") - import traceback - - traceback.print_exc() - return False - - -async def test_cli_tool_detection(): - """Test detection of CLI tools.""" - print_test("CLI Tool Detection") - - tools = { - "openai": "OpenAI CLI", - "claude": "Claude Desktop", - "gemini": "Gemini CLI", - "ollama": "Ollama (local models)", - } - - for cmd, name in tools.items(): - if check_command_exists(cmd): - print(f"โœ“ {name} detected ({cmd})") - else: - print(f"โœ— {name} not found ({cmd})") - - # Check for Hanzo IDE - ide_path = Path.home() / "work" / "hanzo" / "ide" - if ide_path.exists(): - print(f"โœ“ Hanzo IDE detected at {ide_path}") - else: - print(f"โœ— Hanzo IDE not found at {ide_path}") - - return True - - -async def test_api_keys(): - """Test API key availability.""" - print_test("API Keys") - - keys = { - "OPENAI_API_KEY": "OpenAI", - "ANTHROPIC_API_KEY": "Anthropic", - "GOOGLE_API_KEY": "Google/Gemini", - "MISTRAL_API_KEY": "Mistral", - } - - has_any_key = False - for env_var, service in keys.items(): - if os.getenv(env_var): - print(f"โœ“ {service} API key found") - has_any_key = True - else: - print(f"โœ— {service} API key not set") - - if not has_any_key: - print("\nโš ๏ธ No API keys found. You can still use:") - print(" โ€ข Local models (Ollama)") - print(" โ€ข CLI tools (OpenAI CLI, Claude Desktop)") - print(" โ€ข Free APIs (Codestral, StarCoder)") - - return True - - -async def test_ui_components(): - """Test UI components render without errors.""" - print_test("UI Components") - - try: - from rich.box import ROUNDED - from rich.panel import Panel - from rich.console import Console - - console = Console() - - # Test header panel - console.print( - Panel( - "[bold cyan]Test Header[/bold cyan]\n[dim]Test subtitle[/dim]", - box=ROUNDED, - style="dim white", - padding=(0, 1), - ) - ) - print("โœ“ Header panel renders successfully") - - # Test input box borders - console.print("[dim white]โ•ญ" + "โ”€" * 40 + "โ•ฎ[/dim white]") - console.print("[dim white]โ”‚[/dim white] โ€บ Test input") - console.print("[dim white]โ•ฐ" + "โ”€" * 40 + "โ•ฏ[/dim white]") - print("โœ“ Input box borders render successfully") - - # Test response panel - console.print( - Panel( - "Test AI response", - title="[bold cyan]AI Response[/bold cyan]", - title_align="left", - border_style="dim cyan", - padding=(1, 2), - ) - ) - print("โœ“ Response panel renders successfully") - - return True - except Exception as e: - print(f"โœ— UI component error: {e}") - return False - - -async def test_chat_methods(): - """Test chat method availability.""" - print_test("Chat Methods") - - try: - from hanzo.dev import HanzoDevREPL, MultiClaudeOrchestrator - from rich.console import Console - - console = Console() - orchestrator = MultiClaudeOrchestrator( - workspace_dir="/tmp/test", - claude_path="claude", - num_instances=1, - enable_mcp=False, - enable_networking=False, - enable_guardrails=False, - console=console, - orchestrator_model="gpt-4", - ) - - repl = HanzoDevREPL(orchestrator) - - # Check methods exist - methods = [ - "_direct_api_chat", - "_use_openai_cli", - "_use_claude_cli", - "_use_gemini_cli", - "_use_hanzo_ide", - "_use_free_codestral", - "_use_free_starcoder", - "_use_local_model", - ] - - for method in methods: - if hasattr(repl, method): - print(f"โœ“ Method {method} exists") - else: - print(f"โœ— Method {method} missing") - return False - - return True - except Exception as e: - print(f"โœ— Chat method test failed: {e}") - return False - - -async def test_subprocess_commands(): - """Test subprocess command execution.""" - print_test("Subprocess Commands") - - try: - # Test basic command execution - result = subprocess.run(["echo", "test"], capture_output=True, text=True, timeout=5) - - if result.returncode == 0 and result.stdout.strip() == "test": - print("โœ“ Subprocess execution works") - else: - print("โœ— Subprocess execution failed") - return False - - return True - except Exception as e: - print(f"โœ— Subprocess test failed: {e}") - return False - - -async def test_package_version(): - """Test package version consistency.""" - print_test("Package Version") - - try: - # Check pyproject.toml version - pyproject_path = Path(__file__).parent / "pkg" / "hanzo" / "pyproject.toml" - with open(pyproject_path) as f: - content = f.read() - for line in content.split("\n"): - if line.startswith("version ="): - pyproject_version = line.split('"')[1] - break - - # Check CLI version - from hanzo.cli import __version__ as cli_version - - print(f"PyProject version: {pyproject_version}") - print(f"CLI version: {cli_version}") - - if pyproject_version == cli_version: - print("โœ“ Versions match") - return True - else: - print("โœ— Version mismatch!") - return False - - except Exception as e: - print(f"โœ— Version check failed: {e}") - return False - - -async def main(): - """Run all tests.""" - print("\n" + "=" * 60) - print("HANZO DEV COMPREHENSIVE TEST SUITE") - print("=" * 60) - - tests = [ - test_imports(), - test_orchestrator_configs(), - test_repl_initialization(), - test_cli_tool_detection(), - test_api_keys(), - test_ui_components(), - test_chat_methods(), - test_subprocess_commands(), - test_package_version(), - ] - - results = [] - for test in tests: - try: - result = await test - results.append(result) - except Exception as e: - print(f"\nโœ— Test crashed: {e}") - results.append(False) - - # Summary - print("\n" + "=" * 60) - print("TEST SUMMARY") - print("=" * 60) - - passed = sum(results) - total = len(results) - - print(f"Tests passed: {passed}/{total}") - - if passed == total: - print("โœ… All tests passed!") - else: - print(f"โš ๏ธ {total - passed} test(s) failed") - - return passed == total - - -if __name__ == "__main__": - success = asyncio.run(main()) - sys.exit(0 if success else 1) diff --git a/tests/test_integration.py b/tests/test_integration.py deleted file mode 100644 index 535ee9fab..000000000 --- a/tests/test_integration.py +++ /dev/null @@ -1,227 +0,0 @@ -#!/usr/bin/env python3 -""" -Comprehensive integration test for Hanzo Dev v0.3.21. -Tests all major features end-to-end. -""" - -import os -import sys -import asyncio -from pathlib import Path - -from rich.panel import Panel -from rich.console import Console -from rich.progress import Progress, TextColumn, SpinnerColumn - -# Add src to path -sys.path.insert(0, str(Path(__file__).parent / "pkg" / "hanzo" / "src")) - - -async def test_integration(): - """Run comprehensive integration tests.""" - console = Console() - - console.print( - Panel.fit( - "[bold]๐Ÿงช Hanzo Dev Integration Test Suite v0.3.21[/bold]\nTesting all major features end-to-end", - border_style="bold cyan", - ) - ) - - test_results = {} - - with Progress( - SpinnerColumn(), - TextColumn("[progress.description]{task.description}"), - console=console, - ) as progress: - # Test 1: Import and initialization - task = progress.add_task("Testing imports...", total=None) - try: - from hanzo.cli import cli, __version__ - from hanzo.dev import HanzoDevREPL, MultiClaudeOrchestrator - from hanzo.streaming import StreamingHandler - from hanzo.rate_limiter import RateLimiter - from hanzo.memory_manager import MemoryManager - from hanzo.fallback_handler import FallbackHandler - from hanzo.orchestrator_config import get_orchestrator_config - - assert __version__ == "0.3.21" - test_results["imports"] = "โœ… PASS" - progress.update(task, description="โœ… Imports successful") - except Exception as e: - test_results["imports"] = f"โŒ FAIL: {e}" - progress.update(task, description="โŒ Import failed") - - # Test 2: Orchestrator configurations - task = progress.add_task("Testing orchestrators...", total=None) - try: - configs_to_test = ["gpt-4", "claude", "local:llama3.2", "auto"] - for config_name in configs_to_test: - config = get_orchestrator_config(config_name) - assert config is not None - test_results["orchestrators"] = "โœ… PASS" - progress.update(task, description="โœ… Orchestrators configured") - except Exception as e: - test_results["orchestrators"] = f"โŒ FAIL: {e}" - progress.update(task, description="โŒ Orchestrator failed") - - # Test 3: Memory management - task = progress.add_task("Testing memory...", total=None) - try: - manager = MemoryManager("/tmp/test_integration") - - # Add and retrieve memory - mem_id = manager.add_memory("Integration test memory", type="fact") - memories = manager.get_memories() - assert len(memories) > 0 - - # Save and load - manager.save_memories() - manager.load_memories() - - test_results["memory"] = "โœ… PASS" - progress.update(task, description="โœ… Memory management working") - except Exception as e: - test_results["memory"] = f"โŒ FAIL: {e}" - progress.update(task, description="โŒ Memory failed") - - # Test 4: Fallback handler - task = progress.add_task("Testing fallback...", total=None) - try: - handler = FallbackHandler() - - # Check available options - assert handler.available_options is not None - assert len(handler.fallback_order) > 0 - - # Get best option - best = handler.get_best_option() - assert best is not None or len(handler.fallback_order) == 0 - - test_results["fallback"] = "โœ… PASS" - progress.update(task, description="โœ… Fallback handler ready") - except Exception as e: - test_results["fallback"] = f"โŒ FAIL: {e}" - progress.update(task, description="โŒ Fallback failed") - - # Test 5: Rate limiting - task = progress.add_task("Testing rate limiting...", total=None) - try: - from hanzo.rate_limiter import RateLimiter, RateLimitConfig - - config = RateLimitConfig(requests_per_minute=10) - limiter = RateLimiter(config) - - # Test rate limit check - allowed, wait = await limiter.check_rate_limit() - assert isinstance(allowed, bool) - assert isinstance(wait, float) - - # Test acquire - if allowed: - await limiter.acquire() - - test_results["rate_limiting"] = "โœ… PASS" - progress.update(task, description="โœ… Rate limiting active") - except Exception as e: - test_results["rate_limiting"] = f"โŒ FAIL: {e}" - progress.update(task, description="โŒ Rate limiting failed") - - # Test 6: Streaming handler - task = progress.add_task("Testing streaming...", total=None) - try: - # Create a separate console for streaming to avoid conflict - from hanzo.streaming import StreamingHandler - - stream_console = Console() - handler = StreamingHandler(stream_console) - - # Just test that handler initializes correctly - assert handler is not None - assert handler.current_response == "" - assert handler.is_streaming == False - - test_results["streaming"] = "โœ… PASS" - progress.update(task, description="โœ… Streaming ready") - except Exception as e: - test_results["streaming"] = f"โŒ FAIL: {e}" - progress.update(task, description="โŒ Streaming failed") - - # Test 7: REPL initialization - task = progress.add_task("Testing REPL...", total=None) - try: - orchestrator = MultiClaudeOrchestrator( - workspace_dir="/tmp/test_integration", - claude_path="claude", - num_instances=1, - enable_mcp=False, - enable_networking=False, - enable_guardrails=False, - console=console, - orchestrator_model="auto", - ) - - repl = HanzoDevREPL(orchestrator) - - # Check components - assert repl.memory_manager is not None - assert repl.commands is not None - assert "help" in repl.commands - - test_results["repl"] = "โœ… PASS" - progress.update(task, description="โœ… REPL initialized") - except Exception as e: - test_results["repl"] = f"โŒ FAIL: {e}" - progress.update(task, description="โŒ REPL failed") - - # Test 8: AI connectivity (if available) - task = progress.add_task("Testing AI connectivity...", total=None) - try: - from hanzo.fallback_handler import smart_chat - - # Quick test message - response = await smart_chat("Reply with OK", console=None) - - if response: - test_results["ai_connectivity"] = "โœ… PASS" - progress.update(task, description="โœ… AI connected") - else: - test_results["ai_connectivity"] = "โš ๏ธ No API keys" - progress.update(task, description="โš ๏ธ No API keys configured") - except Exception as e: - test_results["ai_connectivity"] = f"โŒ FAIL: {e}" - progress.update(task, description="โŒ AI connection failed") - - # Print results summary - console.print("\n" + "=" * 60) - console.print("[bold]Test Results Summary:[/bold]\n") - - passed = 0 - failed = 0 - warned = 0 - - for test_name, result in test_results.items(): - console.print(f"{test_name:20} {result}") - if "โœ…" in result: - passed += 1 - elif "โŒ" in result: - failed += 1 - elif "โš ๏ธ" in result: - warned += 1 - - console.print("\n" + "=" * 60) - console.print(f"[bold]Total:[/bold] {passed} passed, {failed} failed, {warned} warnings") - - if failed == 0: - console.print("\n[bold green]๐ŸŽ‰ All critical tests passed![/bold green]") - console.print("\nHanzo Dev v0.3.21 is fully operational!") - return True - else: - console.print("\n[bold red]Some tests failed. Please check the errors above.[/bold red]") - return False - - -if __name__ == "__main__": - success = asyncio.run(test_integration()) - sys.exit(0 if success else 1) diff --git a/tests/test_interactive.py b/tests/test_interactive.py deleted file mode 100644 index 695fadb75..000000000 --- a/tests/test_interactive.py +++ /dev/null @@ -1,207 +0,0 @@ -#!/usr/bin/env python3 -""" -Interactive test that validates hanzo dev actually works. -This test will: -1. Create a working REPL -2. Send a real message -3. Get a real AI response -4. Validate the entire flow works -""" - -import os -import sys -import asyncio -from pathlib import Path - -import pytest - -# Add hanzo src to path -sys.path.insert(0, str(Path(__file__).parent.parent / "pkg" / "hanzo" / "src")) - -from hanzo.dev import HanzoDevREPL, MultiClaudeOrchestrator -from rich.console import Console - - -async def test_full_chat_flow(): - """Test the complete chat flow with actual AI response.""" - console = Console() - - print("\n" + "=" * 60) - print("TESTING HANZO DEV CHAT INTERFACE") - print("=" * 60) - - # Step 1: Create orchestrator - print("\n1. Creating orchestrator...") - orchestrator = MultiClaudeOrchestrator( - workspace_dir="/tmp/test", - claude_path="claude", - num_instances=2, - enable_mcp=True, - enable_networking=True, - enable_guardrails=True, - console=console, - orchestrator_model="gpt-4", # Using GPT-4 since we have API key - ) - print(" โœ“ Orchestrator created") - - # Step 2: Create REPL - print("\n2. Creating REPL interface...") - repl = HanzoDevREPL(orchestrator) - print(" โœ“ REPL created") - - # Step 3: Test UI components render - print("\n3. Testing UI components...") - - # Header - from rich.box import ROUNDED - from rich.panel import Panel - - console.print() - console.print( - Panel( - "[bold cyan]Hanzo Dev - AI Chat[/bold cyan]\n[dim]Test Mode - Validating Everything Works[/dim]", - box=ROUNDED, - style="dim white", - padding=(0, 1), - ) - ) - print(" โœ“ Header renders") - - # Input box - console.print() - console.print("[dim white]โ•ญ" + "โ”€" * 78 + "โ•ฎ[/dim white]") - console.print("[dim white]โ”‚[/dim white] โ€บ Test message: Hello AI, respond with 'SUCCESS' if you can hear me") - console.print("[dim white]โ•ฐ" + "โ”€" * 78 + "โ•ฏ[/dim white]") - print(" โœ“ Input box renders") - - # Step 4: Test actual AI response - print("\n4. Testing actual AI chat...") - - test_message = "Please respond with exactly the word: SUCCESS" - - # Check if we have API keys - has_openai = bool(os.getenv("OPENAI_API_KEY")) - has_anthropic = bool(os.getenv("ANTHROPIC_API_KEY")) - - if has_openai or has_anthropic: - print(" Found API keys, sending real message...") - - # Call the chat method directly - try: - # Mock the chat to test it works - await repl.chat_with_agents(test_message) - print(" โœ“ Chat method executed successfully") - - # Also test direct API call - if has_openai: - from openai import AsyncOpenAI - - client = AsyncOpenAI() - response = await client.chat.completions.create( - model="gpt-4", - messages=[ - {"role": "system", "content": "Reply with exactly: SUCCESS"}, - {"role": "user", "content": "Test"}, - ], - max_tokens=10, - ) - - if response.choices: - result = response.choices[0].message.content - console.print() - console.print( - Panel( - result, - title="[bold cyan]AI Response (Direct API)[/bold cyan]", - title_align="left", - border_style="dim cyan", - padding=(1, 2), - ) - ) - - if "SUCCESS" in result.upper(): - print(" โœ“ AI responded correctly with SUCCESS") - else: - print(f" โœ“ AI responded (content: {result})") - - except Exception as e: - print(f" โš  Chat error (expected in test): {e}") - else: - print(" โš  No API keys found, skipping real AI test") - print(" Set OPENAI_API_KEY or ANTHROPIC_API_KEY to test real responses") - - # Step 5: Test command handlers - print("\n5. Testing command handlers...") - - commands_to_test = ["help", "status", "exit"] - for cmd in commands_to_test: - if cmd in repl.commands: - print(f" โœ“ Command '/{cmd}' registered") - else: - print(f" โœ— Command '/{cmd}' missing") - - # Step 6: Validate methods exist - print("\n6. Validating chat methods...") - - methods = [ - "chat_with_agents", - "_direct_api_chat", - "_use_openai_cli", - "_use_claude_cli", - "_use_local_model", - "handle_memory_command", - ] - - for method in methods: - if hasattr(repl, method): - print(f" โœ“ Method {method} exists") - else: - print(f" โœ— Method {method} missing") - - # Step 7: Test orchestrator methods - print("\n7. Testing orchestrator methods...") - - orch_methods = [ - "_call_openai_cli", - "_call_claude_cli", - "_call_api_model", - "_send_to_instance", - ] - - for method in orch_methods: - if hasattr(orchestrator, method): - print(f" โœ“ Orchestrator method {method} exists") - else: - print(f" โœ— Orchestrator method {method} missing") - - # Final summary - print("\n" + "=" * 60) - print("TEST RESULTS") - print("=" * 60) - - print("\nโœ… ALL COMPONENTS VALIDATED:") - print(" โ€ข UI renders correctly") - print(" โ€ข REPL initializes properly") - print(" โ€ข Chat methods are available") - print(" โ€ข Command handlers work") - print(" โ€ข Orchestrator is configured") - - if has_openai or has_anthropic: - print(" โ€ข AI API connection verified") - else: - print(" โ€ข AI API not tested (no keys)") - - print("\n๐ŸŽ‰ HANZO DEV IS WORKING!") - print("\nYou can now run: hanzo dev --orchestrator ") - print("Available orchestrators:") - print(" โ€ข gpt-4 (requires OPENAI_API_KEY)") - print(" โ€ข claude (requires ANTHROPIC_API_KEY)") - print(" โ€ข codex (requires OpenAI CLI)") - print(" โ€ข local:llama3.2 (requires Ollama)") - - return True - - -if __name__ == "__main__": - success = asyncio.run(test_full_chat_flow()) - sys.exit(0 if success else 1) diff --git a/tests/test_mcp.py b/tests/test_mcp.py deleted file mode 100644 index a8eb3a89a..000000000 --- a/tests/test_mcp.py +++ /dev/null @@ -1,241 +0,0 @@ -"""Tests for MCP client implementation.""" - -import sys -import json -import asyncio -import textwrap - -import pytest - -from hanzoai.mcp import ( - MCPClient, - JsonRpcError, - MCPHttpClient, - JsonRpcRequest, - MCPClientError, - JsonRpcResponse, - mcp_tool_name, - normalize_mcp_name, -) - -# --- Name normalization tests --- - - -class TestNormalizeMcpName: - def test_passthrough(self): - assert normalize_mcp_name("hello") == "hello" - - def test_hyphens_kept(self): - # Rust parity: hyphens are valid chars and kept as-is - assert normalize_mcp_name("my-tool") == "my-tool" - - def test_dots_to_underscores(self): - assert normalize_mcp_name("my.tool") == "my_tool" - - def test_slashes_to_underscores(self): - assert normalize_mcp_name("my/tool") == "my_tool" - - def test_mixed_keeps_hyphens(self): - assert normalize_mcp_name("my-tool.v2/action") == "my-tool_v2_action" - - def test_non_claude_keeps_consecutive_underscores(self): - # Non-claude.ai names do NOT collapse underscores - assert normalize_mcp_name("my--tool") == "my--tool" - - def test_non_claude_keeps_leading_trailing(self): - # Non-claude.ai names do NOT strip leading/trailing - assert normalize_mcp_name("-tool-") == "-tool-" - - def test_non_claude_trailing_underscore(self): - assert normalize_mcp_name("tool name!") == "tool_name_" - - def test_github_dot_com(self): - assert normalize_mcp_name("github.com") == "github_com" - - def test_claude_ai_collapses_and_strips(self): - # claude.ai names get underscore collapsing + strip - assert normalize_mcp_name("claude.ai Example Server!!") == "claude_ai_Example_Server" - - def test_claude_ai_simple(self): - assert normalize_mcp_name("claude.ai My Tool") == "claude_ai_My_Tool" - - -class TestMcpToolName: - def test_basic(self): - assert mcp_tool_name("server", "tool") == "mcp__server__tool" - - def test_normalizes_both(self): - # Hyphens kept, so my-server stays my-server - assert mcp_tool_name("my-server", "my-tool") == "mcp__my-server__my-tool" - - def test_claude_ai_server(self): - assert mcp_tool_name("claude.ai Example Server", "weather tool") == "mcp__claude_ai_Example_Server__weather_tool" - - -# --- Protocol types tests --- - - -class TestJsonRpcRequest: - def test_to_json(self): - req = JsonRpcRequest(method="initialize", params={"a": 1}, id=1) - d = req.to_dict() - assert d["jsonrpc"] == "2.0" - assert d["method"] == "initialize" - assert d["params"] == {"a": 1} - assert d["id"] == 1 - - def test_to_line(self): - req = JsonRpcRequest(method="test", params={}, id=42) - line = req.to_line() - assert line.endswith(b"\n") - parsed = json.loads(line) - assert parsed["id"] == 42 - - -class TestJsonRpcResponse: - def test_from_dict_success(self): - resp = JsonRpcResponse.from_dict({"jsonrpc": "2.0", "result": {"ok": True}, "id": 1}) - assert resp.result == {"ok": True} - assert resp.error is None - assert resp.id == 1 - - def test_from_dict_error(self): - resp = JsonRpcResponse.from_dict({ - "jsonrpc": "2.0", - "error": {"code": -32600, "message": "Invalid Request"}, - "id": 1, - }) - assert resp.result is None - assert resp.error is not None - assert resp.error.code == -32600 - assert resp.error.message == "Invalid Request" - - -# --- Stdio MCPClient tests --- - -# A minimal MCP server script that we spawn as a subprocess. -# It reads JSON-RPC from stdin and responds on stdout. -MOCK_SERVER_SCRIPT = textwrap.dedent("""\ - import json - import sys - - def respond(id, result): - msg = json.dumps({"jsonrpc": "2.0", "result": result, "id": id}) - sys.stdout.write(msg + "\\n") - sys.stdout.flush() - - def respond_error(id, code, message): - msg = json.dumps({"jsonrpc": "2.0", "error": {"code": code, "message": message}, "id": id}) - sys.stdout.write(msg + "\\n") - sys.stdout.flush() - - for line in sys.stdin: - line = line.strip() - if not line: - continue - req = json.loads(line) - method = req["method"] - rid = req["id"] - - if method == "initialize": - respond(rid, { - "protocolVersion": "2024-11-05", - "capabilities": {"tools": {}, "resources": {}}, - "serverInfo": {"name": "mock", "version": "0.1.0"}, - }) - elif method == "tools/list": - respond(rid, { - "tools": [ - {"name": "echo", "description": "echoes input", "inputSchema": {"type": "object"}}, - ], - }) - elif method == "tools/call": - tool_name = req["params"]["name"] - args = req["params"].get("arguments", {}) - if tool_name == "echo": - respond(rid, {"content": [{"type": "text", "text": json.dumps(args)}]}) - elif tool_name == "fail": - respond_error(rid, -32000, "tool failed") - else: - respond_error(rid, -32601, f"unknown tool: {tool_name}") - elif method == "resources/list": - respond(rid, {"resources": [{"uri": "file:///test.txt", "name": "test.txt"}]}) - else: - respond_error(rid, -32601, f"unknown method: {method}") -""") - - -class TestMCPClientStdio: - @pytest.fixture - async def client(self, tmp_path): - script = tmp_path / "mock_server.py" - script.write_text(MOCK_SERVER_SCRIPT) - c = MCPClient(server_command=[sys.executable, str(script)]) - await c.connect() - yield c - await c.disconnect() - - async def test_connect_initializes(self, client): - assert client.server_info is not None - assert client.server_info["name"] == "mock" - - async def test_list_tools(self, client): - tools = await client.list_tools() - assert len(tools) == 1 - assert tools[0]["name"] == "echo" - - async def test_call_tool(self, client): - result = await client.call_tool("echo", arguments={"msg": "hello"}) - assert result["content"][0]["text"] == '{"msg": "hello"}' - - async def test_call_tool_error(self, client): - with pytest.raises(MCPClientError, match="tool failed"): - await client.call_tool("fail") - - async def test_list_resources(self, client): - resources = await client.list_resources() - assert len(resources) == 1 - assert resources[0]["uri"] == "file:///test.txt" - - async def test_context_manager(self, tmp_path): - script = tmp_path / "mock_server.py" - script.write_text(MOCK_SERVER_SCRIPT) - async with MCPClient(server_command=[sys.executable, str(script)]) as c: - tools = await c.list_tools() - assert len(tools) == 1 - - async def test_request_ids_increment(self, client): - # Each call increments the request id - id_before = client._next_id - await client.list_tools() - assert client._next_id == id_before + 1 - - async def test_disconnect_kills_process(self, tmp_path): - script = tmp_path / "mock_server.py" - script.write_text(MOCK_SERVER_SCRIPT) - c = MCPClient(server_command=[sys.executable, str(script)]) - await c.connect() - proc = c._process - await c.disconnect() - assert c._process is None - # Process should be terminated - assert proc.returncode is not None - - -class TestMCPClientNotConnected: - async def test_list_tools_without_connect(self): - c = MCPClient(server_command=["true"]) - with pytest.raises(MCPClientError, match="not connected"): - await c.list_tools() - - async def test_call_tool_without_connect(self): - c = MCPClient(server_command=["true"]) - with pytest.raises(MCPClientError, match="not connected"): - await c.call_tool("x") - - -class TestMCPClientBadProcess: - async def test_connect_to_nonexistent_binary(self): - c = MCPClient(server_command=["__nonexistent_binary_xyz__"]) - with pytest.raises(MCPClientError): - await c.connect() diff --git a/tests/test_memory.py b/tests/test_memory.py deleted file mode 100644 index 51c9c7ac2..000000000 --- a/tests/test_memory.py +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env python3 -"""Test memory management system.""" - -import sys -import shutil -import tempfile -from pathlib import Path - -import pytest - -# Add hanzo src to path -sys.path.insert(0, str(Path(__file__).parent.parent / "pkg" / "hanzo" / "src")) - -from hanzo.memory_manager import MemoryManager, handle_memory_command - - -class TestMemoryManager: - """Test memory manager functionality.""" - - @pytest.fixture - def temp_dir(self): - """Create a temporary directory for testing.""" - temp_path = tempfile.mkdtemp() - yield temp_path - shutil.rmtree(temp_path, ignore_errors=True) - - @pytest.fixture - def memory_manager(self, temp_dir): - """Create a memory manager instance.""" - return MemoryManager(temp_dir) - - def test_add_memory_fact(self, memory_manager): - """Test adding a fact memory.""" - memory_id = memory_manager.add_memory("User prefers Python over JavaScript", type="fact", priority=5) - assert memory_id is not None - assert len(memory_id) > 0 - - def test_add_memory_instruction(self, memory_manager): - """Test adding an instruction memory.""" - memory_id = memory_manager.add_memory("Always use type hints in Python code", type="instruction", priority=8) - assert memory_id is not None - - def test_add_memory_context(self, memory_manager): - """Test adding a context memory.""" - memory_id = memory_manager.add_memory("Working on REST API project", type="context", priority=3) - assert memory_id is not None - - def test_get_all_memories(self, memory_manager): - """Test retrieving all memories.""" - # Get initial count (memory manager may start with system memories) - initial_count = len(memory_manager.get_memories()) - - memory_manager.add_memory("Memory 1", type="fact", priority=5) - memory_manager.add_memory("Memory 2", type="instruction", priority=8) - memory_manager.add_memory("Memory 3", type="context", priority=3) - - all_memories = memory_manager.get_memories() - assert len(all_memories) == initial_count + 3 - - def test_get_memories_by_type(self, memory_manager): - """Test retrieving memories filtered by type.""" - # Get initial count for instruction type - initial_instruction_count = len(memory_manager.get_memories(type="instruction")) - - memory_manager.add_memory("Fact 1", type="fact", priority=5) - memory_manager.add_memory("Instruction 1", type="instruction", priority=8) - memory_manager.add_memory("Instruction 2", type="instruction", priority=6) - - instructions = memory_manager.get_memories(type="instruction") - assert len(instructions) == initial_instruction_count + 2 - - def test_set_and_get_preference(self, memory_manager): - """Test setting and getting preferences.""" - memory_manager.set_preference("theme", "dark") - memory_manager.set_preference("language", "python") - - theme = memory_manager.get_preference("theme") - assert theme == "dark" - - language = memory_manager.get_preference("language") - assert language == "python" - - def test_add_and_get_messages(self, memory_manager): - """Test adding and retrieving session messages.""" - memory_manager.add_message("user", "Hello AI!") - memory_manager.add_message("assistant", "Hello! How can I help you today?") - - recent = memory_manager.get_recent_messages(2) - assert len(recent) == 2 - - def test_summarize_for_ai(self, memory_manager): - """Test AI summary generation.""" - memory_manager.add_memory("Test memory", type="fact", priority=5) - summary = memory_manager.summarize_for_ai() - assert summary is not None - assert isinstance(summary, str) - - def test_export_import_memories(self, temp_dir, memory_manager): - """Test exporting and importing memories.""" - # Add some memories - memory_manager.add_memory("Memory 1", type="fact", priority=5) - memory_manager.add_memory("Memory 2", type="instruction", priority=8) - - # Export - export_file = f"{temp_dir}/test_export.json" - memory_manager.export_memories(export_file) - assert Path(export_file).exists() - - # Import to new manager - new_temp_dir = tempfile.mkdtemp() - try: - new_manager = MemoryManager(new_temp_dir) - new_manager.import_memories(export_file) - - imported_memories = new_manager.get_memories() - assert len(imported_memories) >= 2 - finally: - shutil.rmtree(new_temp_dir, ignore_errors=True) - - -if __name__ == "__main__": - sys.exit(pytest.main([__file__, "-v"])) diff --git a/tests/test_mock_server.py b/tests/test_mock_server.py deleted file mode 100644 index 354fa97b8..000000000 --- a/tests/test_mock_server.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python3 -"""Simple mock server for testing cache endpoints.""" - -import sys -import json -from http.server import HTTPServer, BaseHTTPRequestHandler - - -class MockHandler(BaseHTTPRequestHandler): - def do_GET(self): - if self.path == "/cache/ping": - self.send_response(200) - self.send_header("Content-Type", "application/json") - self.end_headers() - response = { - "status": "healthy", - "cache_type": "redis", - "ping_response": True, - "set_cache_response": None, - "llm_cache_params": None, - "health_check_cache_params": None, - } - self.wfile.write(json.dumps(response).encode()) - else: - self.send_response(404) - self.end_headers() - - def do_POST(self): - if self.path == "/cache/delete": - self.send_response(200) - self.send_header("Content-Type", "application/json") - self.end_headers() - self.wfile.write(b"{}") - elif self.path == "/cache/flushall": - self.send_response(200) - self.send_header("Content-Type", "application/json") - self.end_headers() - self.wfile.write(b"{}") - else: - self.send_response(404) - self.end_headers() - - def log_message(self, format, *args): - pass # Suppress logs - - -if __name__ == "__main__": - server = HTTPServer(("127.0.0.1", 4010), MockHandler) - print("Mock server running on http://127.0.0.1:4010") - print("Press Ctrl+C to stop") - try: - server.serve_forever() - except KeyboardInterrupt: - print("\nStopping server...") - sys.exit(0) diff --git a/tests/test_models.py b/tests/test_models.py index d67246bc6..e846633e0 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,5 +1,5 @@ import json -from typing import Any, Dict, List, Union, Optional, cast +from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, cast from datetime import datetime, timezone from typing_extensions import Literal, Annotated, TypeAliasType @@ -8,7 +8,7 @@ from pydantic import Field from hanzoai._utils import PropertyInfo -from hanzoai._compat import PYDANTIC_V2, parse_obj, model_dump, model_json +from hanzoai._compat import PYDANTIC_V1, parse_obj, model_dump, model_json from hanzoai._models import BaseModel, construct_type @@ -294,12 +294,12 @@ class Model(BaseModel): assert cast(bool, m.foo) is True m = Model.construct(foo={"name": 3}) - if PYDANTIC_V2: - assert isinstance(m.foo, Submodel1) - assert m.foo.name == 3 # type: ignore - else: + if PYDANTIC_V1: assert isinstance(m.foo, Submodel2) assert m.foo.name == "3" + else: + assert isinstance(m.foo, Submodel1) + assert m.foo.name == 3 # type: ignore def test_list_of_unions() -> None: @@ -426,10 +426,10 @@ class Model(BaseModel): expected = datetime(2019, 12, 27, 18, 11, 19, 117000, tzinfo=timezone.utc) - if PYDANTIC_V2: - expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}' - else: + if PYDANTIC_V1: expected_json = '{"created_at": "2019-12-27T18:11:19.117000+00:00"}' + else: + expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}' model = Model.construct(created_at="2019-12-27T18:11:19.117Z") assert model.created_at == expected @@ -492,12 +492,15 @@ class Model(BaseModel): resource_id: Optional[str] = None m = Model.construct() + assert m.resource_id is None assert "resource_id" not in m.model_fields_set m = Model.construct(resource_id=None) + assert m.resource_id is None assert "resource_id" in m.model_fields_set m = Model.construct(resource_id="foo") + assert m.resource_id == "foo" assert "resource_id" in m.model_fields_set @@ -528,7 +531,7 @@ class Model2(BaseModel): assert m4.to_dict(mode="python") == {"created_at": datetime.fromisoformat(time_str)} assert m4.to_dict(mode="json") == {"created_at": time_str} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): m.to_dict(warnings=False) @@ -553,7 +556,7 @@ class Model(BaseModel): assert m3.model_dump() == {"foo": None} assert m3.model_dump(exclude_none=True) == {} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): m.model_dump(round_trip=True) @@ -577,10 +580,10 @@ class Model(BaseModel): assert json.loads(m.to_json()) == {"FOO": "hello"} assert json.loads(m.to_json(use_api_names=False)) == {"foo": "hello"} - if PYDANTIC_V2: - assert m.to_json(indent=None) == '{"FOO":"hello"}' - else: + if PYDANTIC_V1: assert m.to_json(indent=None) == '{"FOO": "hello"}' + else: + assert m.to_json(indent=None) == '{"FOO":"hello"}' m2 = Model() assert json.loads(m2.to_json()) == {} @@ -592,7 +595,7 @@ class Model(BaseModel): assert json.loads(m3.to_json()) == {"FOO": None} assert json.loads(m3.to_json(exclude_none=True)) == {} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): m.to_json(warnings=False) @@ -619,7 +622,7 @@ class Model(BaseModel): assert json.loads(m3.model_dump_json()) == {"foo": None} assert json.loads(m3.model_dump_json(exclude_none=True)) == {} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): m.model_dump_json(round_trip=True) @@ -676,12 +679,12 @@ class B(BaseModel): ) assert isinstance(m, A) assert m.type == "a" - if PYDANTIC_V2: - assert m.data == 100 # type: ignore[comparison-overlap] - else: + if PYDANTIC_V1: # pydantic v1 automatically converts inputs to strings # if the expected type is a str assert m.data == "100" + else: + assert m.data == 100 # type: ignore[comparison-overlap] def test_discriminated_unions_unknown_variant() -> None: @@ -765,12 +768,12 @@ class B(BaseModel): ) assert isinstance(m, A) assert m.foo_type == "a" - if PYDANTIC_V2: - assert m.data == 100 # type: ignore[comparison-overlap] - else: + if PYDANTIC_V1: # pydantic v1 automatically converts inputs to strings # if the expected type is a str assert m.data == "100" + else: + assert m.data == 100 # type: ignore[comparison-overlap] def test_discriminated_unions_overlapping_discriminators_invalid_data() -> None: @@ -809,8 +812,7 @@ class B(BaseModel): assert not hasattr(UnionType, "__discriminator__") m = construct_type( - value={"type": "b", "data": "foo"}, - type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]), + value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) ) assert isinstance(m, B) assert m.type == "b" @@ -820,8 +822,7 @@ class B(BaseModel): assert discriminator is not None m = construct_type( - value={"type": "b", "data": "foo"}, - type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]), + value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) ) assert isinstance(m, B) assert m.type == "b" @@ -832,9 +833,9 @@ class B(BaseModel): assert UnionType.__discriminator__ is discriminator -@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") +@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") def test_type_alias_type() -> None: - Alias = TypeAliasType("Alias", str) + Alias = TypeAliasType("Alias", str) # pyright: ignore class Model(BaseModel): alias: Alias @@ -848,7 +849,7 @@ class Model(BaseModel): assert m.union == "bar" -@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") +@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") def test_field_named_cls() -> None: class Model(BaseModel): cls: str @@ -888,3 +889,75 @@ class ModelB(BaseModel): ) assert isinstance(m, ModelB) + + +def test_nested_discriminated_union() -> None: + class InnerType1(BaseModel): + type: Literal["type_1"] + + class InnerModel(BaseModel): + inner_value: str + + class InnerType2(BaseModel): + type: Literal["type_2"] + some_inner_model: InnerModel + + class Type1(BaseModel): + base_type: Literal["base_type_1"] + value: Annotated[ + Union[ + InnerType1, + InnerType2, + ], + PropertyInfo(discriminator="type"), + ] + + class Type2(BaseModel): + base_type: Literal["base_type_2"] + + T = Annotated[ + Union[ + Type1, + Type2, + ], + PropertyInfo(discriminator="base_type"), + ] + + model = construct_type( + type_=T, + value={ + "base_type": "base_type_1", + "value": { + "type": "type_2", + }, + }, + ) + assert isinstance(model, Type1) + assert isinstance(model.value, InnerType2) + + +@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2 for now") +def test_extra_properties() -> None: + class Item(BaseModel): + prop: int + + class Model(BaseModel): + __pydantic_extra__: Dict[str, Item] = Field(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + other: str + + if TYPE_CHECKING: + + def __getattr__(self, attr: str) -> Item: ... + + model = construct_type( + type_=Model, + value={ + "a": {"prop": 1}, + "other": "foo", + }, + ) + assert isinstance(model, Model) + assert model.a.prop == 1 + assert isinstance(model.a, Item) + assert model.other == "foo" diff --git a/tests/test_orchestrator.sh b/tests/test_orchestrator.sh deleted file mode 100755 index a450c4905..000000000 --- a/tests/test_orchestrator.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/bash -# Test script to demonstrate different orchestrator configurations - -echo "โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—" -echo "โ•‘ Hanzo Dev - Orchestrator Test Suite โ•‘" -echo "โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•" -echo "" - -# Function to test a configuration -test_config() { - local name="$1" - local cmd="$2" - local description="$3" - - echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" - echo "๐Ÿ“ฆ Testing: $name" - echo "๐Ÿ“ Description: $description" - echo "๐Ÿ’ป Command: $cmd" - echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" - - # Run the command with a timeout and capture initial output - timeout 5s bash -c "$cmd" 2>&1 | head -20 - - echo "" -} - -# Test 1: GPT-5 Pro + Codex -test_config \ - "GPT-5 Pro + Codex" \ - "hanzo dev --orchestrator gpt-5-pro-codex --instances 2" \ - "Ultimate code development with GPT-5 Pro reasoning + Codex generation" - -# Test 2: Router mode -test_config \ - "Router Mode (GPT-4o)" \ - "hanzo dev --orchestrator router:gpt-4o" \ - "Access GPT-4o via hanzo-router with automatic failover" - -# Test 3: Direct Codex -test_config \ - "Direct Codex" \ - "hanzo dev --orchestrator codex" \ - "Pure Codex mode for specialized code generation" - -# Test 4: Cost-optimized -test_config \ - "Cost Optimized" \ - "hanzo dev --orchestrator cost-optimized --use-hanzo-net" \ - "90% cost reduction with local models + selective API usage" - -# Test 5: Local model -test_config \ - "Local Llama" \ - "hanzo dev --orchestrator local:llama3.2" \ - "Pure local model orchestration (free!)" - -echo "โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—" -echo "โ•‘ Test Complete! โ•‘" -echo "โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•" -echo "" -echo "To run interactively, use any of these commands:" -echo "" -echo " hanzo dev --orchestrator gpt-5-pro-codex # Best for code" -echo " hanzo dev --orchestrator router:gpt-5 # Via router" -echo " hanzo dev --orchestrator codex # Pure Codex" -echo " hanzo dev --orchestrator cost-optimized # 90% savings" -echo "" -echo "For more options: hanzo dev --help" \ No newline at end of file diff --git a/tests/test_parity.py b/tests/test_parity.py deleted file mode 100644 index c98ba3c0b..000000000 --- a/tests/test_parity.py +++ /dev/null @@ -1,482 +0,0 @@ -"""Rust/Python parity tests. - -Each test feeds the EXACT inputs from a Rust crate test and asserts the Python -implementation produces the SAME output. Every test is tagged with the Rust -source file and test name it mirrors. -""" -from __future__ import annotations - -import json -import hashlib -import tempfile -from pathlib import Path - -# โ”€โ”€ 1. SSE Parser โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -from hanzoai._streaming import SSEDecoder, ServerSentEvent - - -def _sse_events(raw: bytes) -> list[ServerSentEvent]: - """Feed raw bytes through SSEDecoder and collect non-None events.""" - decoder = SSEDecoder() - return list(decoder.iter_bytes(iter([raw]))) - - -def _sse_events_chunked(chunks: list[bytes]) -> list[ServerSentEvent]: - decoder = SSEDecoder() - return list(decoder.iter_bytes(iter(chunks))) - - -# Parity: hanzo-dev/sse/src/lib.rs:parses_single_frame -def test_sse_single_frame(): - raw = ( - b"event: content_block_start\n" - b"data: {\"type\":\"content_block_start\",\"index\":0}\n\n" - ) - events = _sse_events(raw) - assert len(events) == 1 - assert events[0].event == "content_block_start" - assert events[0].data == '{"type":"content_block_start","index":0}' - - -# Parity: hanzo-dev/sse/src/lib.rs:parses_chunked_stream -def test_sse_chunked_stream(): - first = b"event: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"text\":\"Hel" - second = b"lo\"}\n\n" - events = _sse_events_chunked([first, second]) - assert len(events) == 1 - assert events[0].event == "content_block_delta" - assert events[0].data == '{"type":"content_block_delta","text":"Hello"}' - - -# Parity: hanzo-dev/sse/src/lib.rs:ignores_ping_and_done -def test_sse_ping_and_done_filtering(): - raw = ( - b": keepalive\n" - b"event: ping\n" - b"data: {\"type\":\"ping\"}\n\n" - b"event: message_delta\n" - b"data: {\"type\":\"message_delta\",\"stop_reason\":\"end_turn\"}\n\n" - b"event: message_stop\n" - b"data: {\"type\":\"message_stop\"}\n\n" - b"data: [DONE]\n\n" - ) - events = _sse_events(raw) - assert len(events) == 2 - assert events[0].event == "message_delta" - assert events[1].event == "message_stop" - - -# Parity: hanzo-dev/sse/src/lib.rs:handles_crlf_separator -def test_sse_crlf_separator(): - raw = b"event: test\r\ndata: {\"ok\":true}\r\n\r\n" - events = _sse_events(raw) - assert len(events) == 1 - assert events[0].data == '{"ok":true}' - - -# Parity: hanzo-dev/sse/src/lib.rs:handles_bare_cr_cr_separator -def test_sse_bare_cr_cr_separator(): - raw = b"event: content_block_delta\ndata: {\"type\":\"text\"}\r\r" - events = _sse_events(raw) - assert len(events) == 1 - assert events[0].event == "content_block_delta" - assert events[0].data == '{"type":"text"}' - - -# Parity: hanzo-dev/sse/src/lib.rs:parses_split_json_across_data_lines -def test_sse_split_json_across_data_lines(): - raw = ( - b"event: content_block_delta\n" - b"data: {\"type\":\"content_block_delta\",\"index\":0,\n" - b"data: \"delta\":{\"type\":\"text_delta\",\"text\":\"Hello\"}}\n\n" - ) - events = _sse_events(raw) - assert len(events) == 1 - assert events[0].event == "content_block_delta" - assert events[0].data == ( - '{"type":"content_block_delta","index":0,\n' - '"delta":{"type":"text_delta","text":"Hello"}}' - ) - - -# Parity: hanzo-dev/sse/src/lib.rs:done_marker_returns_none -def test_sse_done_marker_filtered(): - raw = b"data: [DONE]\n\n" - events = _sse_events(raw) - assert len(events) == 0 - - -# Parity: hanzo-dev/sse/src/lib.rs:skips_comment_only_frame -def test_sse_comment_only(): - raw = b": this is a comment\n\n" - events = _sse_events(raw) - assert len(events) == 0 - - -# โ”€โ”€ 2. MCP Name Normalization โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -from hanzoai.mcp import mcp_tool_name, normalize_mcp_name - - -# Parity: claw-code/rust/crates/runtime/src/mcp.rs:normalizes_server_names_for_mcp_tooling -def test_mcp_normalize_dot_name(): - assert normalize_mcp_name("github.com") == "github_com" - - -def test_mcp_normalize_exclamation(): - assert normalize_mcp_name("tool name!") == "tool_name_" - - -def test_mcp_normalize_claudeai_prefix(): - assert normalize_mcp_name("claude.ai Example Server!!") == "claude_ai_Example_Server" - - -def test_mcp_tool_name_full(): - assert mcp_tool_name("claude.ai Example Server", "weather tool") == ( - "mcp__claude_ai_Example_Server__weather_tool" - ) - - -def test_mcp_hyphens_kept(): - assert normalize_mcp_name("my-tool") == "my-tool" - - -# โ”€โ”€ 3. Permission Policy โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -from hanzoai.protocols import ( - PermissionMode, - PermissionPolicy, - PermissionOutcome, - PermissionRequest, -) - - -# Parity: claw-code/rust/crates/runtime/src/permissions.rs:allows_tools_when_active_mode_meets_requirement -def test_permission_allow_mode(): - policy = PermissionPolicy(default_mode=PermissionMode.Allow) - outcome = policy.authorize("bash", "{}") - assert outcome.allowed is True - - -# Parity: claw-code/rust/crates/runtime/src/permissions.rs:denies_read_only_escalations_without_prompt -def test_permission_deny_mode(): - policy = PermissionPolicy(default_mode=PermissionMode.Deny) - outcome = policy.authorize("bash", "{}") - assert outcome.allowed is False - assert "denied by policy" in outcome.reason - - -# Parity: claw-code/rust/crates/runtime/src/permissions.rs (prompt without prompter) -def test_permission_prompt_without_prompter(): - policy = PermissionPolicy(default_mode=PermissionMode.Prompt) - outcome = policy.authorize("bash", "{}") - assert outcome.allowed is False - assert "no prompter" in outcome.reason - - -# Parity: claw-code/rust/crates/runtime/src/permissions.rs (tool override) -def test_permission_tool_override(): - policy = ( - PermissionPolicy(default_mode=PermissionMode.Deny) - .with_tool_mode("bash", PermissionMode.Allow) - ) - assert policy.authorize("bash", "{}").allowed is True - assert policy.authorize("other", "{}").allowed is False - - -# โ”€โ”€ 4. Session Compaction โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -from hanzoai.session import ( - Session, - TextBlock, - MessageRole, - ToolResultBlock, - CompactionConfig, - ConversationMessage, - compact_session, - format_compact_summary, -) - - -# Parity: claw-code/rust/crates/runtime/src/compact.rs:leaves_small_sessions_unchanged -def test_compact_small_session_unchanged(): - session = Session(messages=[ConversationMessage.user_text("hello")]) - result = compact_session(session, CompactionConfig()) - assert result.removed_message_count == 0 - assert result.compacted_session is session - assert result.summary == "" - - -# Parity: claw-code/rust/crates/runtime/src/compact.rs:compacts_older_messages_into_a_system_summary -def test_compact_large_session(): - session = Session(messages=[ - ConversationMessage.user_text("one " * 200), - ConversationMessage.assistant([TextBlock(text="two " * 200)]), - ConversationMessage.tool_result("1", "bash", "ok " * 200, False), - ConversationMessage( - role=MessageRole.Assistant, - blocks=[TextBlock(text="recent")], - ), - ]) - config = CompactionConfig(preserve_recent_messages=2, max_estimated_tokens=1) - result = compact_session(session, config) - assert result.removed_message_count == 2 - assert result.compacted_session.messages[0].role == MessageRole.System - system_text = result.compacted_session.messages[0].blocks[0].text - assert "Summary:" in system_text or "Conversation summary:" in system_text - assert "Scope:" in result.formatted_summary - assert "Key timeline:" in result.formatted_summary - - -# Parity: claw-code/rust/crates/runtime/src/compact.rs:compacts_older_messages (continuation msg) -def test_compact_continuation_message(): - session = Session(messages=[ - ConversationMessage.user_text("one " * 200), - ConversationMessage.assistant([TextBlock(text="two " * 200)]), - ConversationMessage.tool_result("1", "bash", "ok " * 200, False), - ConversationMessage( - role=MessageRole.Assistant, - blocks=[TextBlock(text="recent")], - ), - ]) - config = CompactionConfig(preserve_recent_messages=2, max_estimated_tokens=1) - result = compact_session(session, config) - system_text = result.compacted_session.messages[0].blocks[0].text - assert "This session is being continued" in system_text - - -# Parity: claw-code/rust/crates/runtime/src/compact.rs:formats_compact_summary_like_upstream -def test_format_compact_summary(): - summary = "scratch\nKept work" - assert format_compact_summary(summary) == "Summary:\nKept work" - - -# โ”€โ”€ 5. Token Usage โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -from hanzoai.protocols import TokenUsage, UsageTracker - - -# Parity: claw-code/rust/crates/runtime/src/usage.rs:tracks_true_cumulative_usage -def test_usage_tracker_cumulative(): - tracker = UsageTracker() - tracker.record(TokenUsage( - input_tokens=10, output_tokens=4, - cache_creation_input_tokens=2, cache_read_input_tokens=1, - )) - tracker.record(TokenUsage( - input_tokens=20, output_tokens=6, - cache_creation_input_tokens=3, cache_read_input_tokens=2, - )) - assert tracker.turns == 2 - cum = tracker.cumulative_usage() - assert cum.input_tokens == 30 - assert cum.output_tokens == 10 - assert cum.total_tokens() == 48 - - -# Parity: claw-code/rust/crates/runtime/src/usage.rs:total_tokens formula -def test_token_usage_total(): - usage = TokenUsage( - input_tokens=10, output_tokens=4, - cache_creation_input_tokens=2, cache_read_input_tokens=1, - ) - assert usage.total_tokens() == 10 + 4 + 2 + 1 - - -# โ”€โ”€ 6. OAuth PKCE โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -from hanzoai.auth import _base64url_encode - - -# Parity: claw-code/rust/crates/runtime/src/oauth.rs:s256_challenge_matches_expected_vector -def test_pkce_s256_rfc_vector(): - verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" - digest = hashlib.sha256(verifier.encode("ascii")).digest() - challenge = _base64url_encode(digest) - assert challenge == "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM" - - -# โ”€โ”€ 7. Config Loader โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -from hanzoai.config import ( - ConfigEntry, - McpWsConfig, - ConfigLoader, - ConfigSource, - McpSdkConfig, - McpTransport, - McpStdioConfig, - McpRemoteConfig, - McpClaudeAiProxyConfig, - _parse_mcp_server, - _sanitize_project_config, -) - - -# Parity: hierarchical merge โ€” local overrides user -def test_config_hierarchical_merge(): - with tempfile.TemporaryDirectory() as tmpdir: - root = Path(tmpdir) - user_dir = root / "user_config" - user_dir.mkdir() - (user_dir / "settings.json").write_text('{"model": "sonnet"}') - - project_dir = root / "project" - project_dir.mkdir() - (project_dir / ".hanzo").mkdir() - (project_dir / ".hanzo" / "settings.json").write_text('{"model": "opus"}') - - loader = ConfigLoader(cwd=project_dir, config_home=user_dir) - cfg = loader.load() - assert cfg.get("model") == "opus" - - -# Parity: MCP server parsing โ€” all 6 transport types -def test_config_mcp_stdio(): - cfg = _parse_mcp_server("test", {"transport": "stdio", "command": "uvx", "args": ["srv"]}) - assert isinstance(cfg, McpStdioConfig) - assert cfg.command == "uvx" - - -def test_config_mcp_http(): - cfg = _parse_mcp_server("test", {"transport": "http", "url": "https://x.com/mcp"}) - assert isinstance(cfg, McpRemoteConfig) - assert cfg.transport == McpTransport.Http - - -def test_config_mcp_sse(): - cfg = _parse_mcp_server("test", {"transport": "sse", "url": "https://x.com/mcp"}) - assert isinstance(cfg, McpRemoteConfig) - assert cfg.transport == McpTransport.Sse - - -def test_config_mcp_ws(): - cfg = _parse_mcp_server("test", {"transport": "ws", "url": "wss://x.com/mcp"}) - assert isinstance(cfg, McpWsConfig) - - -def test_config_mcp_sdk(): - cfg = _parse_mcp_server("test", {"transport": "sdk", "name": "builtin"}) - assert isinstance(cfg, McpSdkConfig) - assert cfg.name == "builtin" - - -def test_config_mcp_claudeai_proxy(): - cfg = _parse_mcp_server("test", {"transport": "claudeai-proxy", "url": "https://p.ai", "id": "abc"}) - assert isinstance(cfg, McpClaudeAiProxyConfig) - - -# Parity: project config sanitization blocks stdio mcpServers -def test_config_sanitize_project_blocks_stdio(): - entry = ConfigEntry(source=ConfigSource.Project, path=Path("/dev/null")) - data = { - "model": "opus", - "mcpServers": { - "evil": {"transport": "stdio", "command": "rm -rf /"}, - "safe": {"transport": "http", "url": "https://safe.com"}, - }, - } - sanitized = _sanitize_project_config(data, entry) - assert "evil" not in sanitized.get("mcpServers", {}) - assert "safe" in sanitized.get("mcpServers", {}) - assert sanitized["model"] == "opus" - - -# โ”€โ”€ 8. Backoff โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -# Parity: hanzo-dev/backoff/src/lib.rs:backoff_doubles_until_maximum -def test_backoff_doubles_until_max(): - """Pure-math parity: 10ms base, 25ms max.""" - initial_ms = 10 - max_ms = 25 - - def delay(attempt: int) -> int: - multiplier = 1 << (attempt - 1) - return min(initial_ms * multiplier, max_ms) - - assert delay(1) == 10 - assert delay(2) == 20 - assert delay(3) == 25 # capped - - -# Parity: hanzo-dev/backoff/src/lib.rs:retryable_statuses -def test_retryable_statuses(): - retryable = {408, 409, 429, 500, 502, 503, 504} - for code in retryable: - assert code in retryable - for code in [200, 401, 404]: - assert code not in retryable - - -# โ”€โ”€ 9. Session Roundtrip โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -from hanzoai.session import ToolUseBlock - - -# Parity: claw-code/rust/crates/runtime/src/session.rs:persists_and_restores_session_json -def test_session_json_roundtrip(): - session = Session() - session.messages.append(ConversationMessage.user_text("hello")) - session.messages.append(ConversationMessage.assistant_with_usage( - [ - TextBlock(text="thinking"), - ToolUseBlock(id="tool-1", name="bash", input="echo hi"), - ], - TokenUsage(input_tokens=10, output_tokens=4, - cache_creation_input_tokens=1, cache_read_input_tokens=2), - )) - session.messages.append(ConversationMessage.tool_result("tool-1", "bash", "hi", False)) - - with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f: - path = Path(f.name) - - try: - session.save(path) - restored = Session.load(path) - assert restored.version == session.version - assert len(restored.messages) == 3 - assert restored.messages[0].role == MessageRole.User - assert restored.messages[2].role == MessageRole.Tool - assert restored.messages[1].usage is not None - assert restored.messages[1].usage.total_tokens() == 17 - - # Verify JSON structure has expected keys - data = json.loads(path.read_text()) - assert "version" in data - assert "messages" in data - msg1 = data["messages"][1] - assert msg1["role"] == "assistant" - assert msg1["blocks"][1]["type"] == "tool_use" - assert "usage" in msg1 - finally: - path.unlink() - - -# โ”€โ”€ 10. Hook Runner โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - -from hanzo_hooks.types import HookConfig -from hanzo_hooks.runner import HookRunner - - -# Parity: claw-code/rust/crates/runtime/src/hooks.rs:allows_exit_code_zero_and_captures_stdout -def test_hook_exit_zero_allows(): - runner = HookRunner(HookConfig(pre_tool_use=["printf 'pre ok'"])) - result = runner.run_pre_tool_use("Read", '{"path":"README.md"}') - assert result.denied is False - assert result.messages == ["pre ok"] - - -# Parity: claw-code/rust/crates/runtime/src/hooks.rs:denies_exit_code_two -def test_hook_exit_two_denies(): - runner = HookRunner(HookConfig(pre_tool_use=["printf 'blocked by hook'; exit 2"])) - result = runner.run_pre_tool_use("Bash", '{"command":"pwd"}') - assert result.denied is True - assert result.messages == ["blocked by hook"] - - -# Parity: claw-code/rust/crates/runtime/src/hooks.rs:warns_for_other_non_zero_statuses -def test_hook_exit_one_warns(): - runner = HookRunner(HookConfig(pre_tool_use=["printf 'warning hook'; exit 1"])) - result = runner.run_pre_tool_use("Edit", '{"file":"src/lib.rs"}') - assert result.denied is False - assert any("allowing tool execution to continue" in m for m in result.messages) diff --git a/tests/test_qs.py b/tests/test_qs.py index 531f7712e..9ea4b754f 100644 --- a/tests/test_qs.py +++ b/tests/test_qs.py @@ -74,8 +74,5 @@ def test_array_brackets(method: str) -> None: def test_unknown_array_format() -> None: - with pytest.raises( - NotImplementedError, - match="Unknown array_format value: foo, choose from comma, repeat", - ): + with pytest.raises(NotImplementedError, match="Unknown array_format value: foo, choose from comma, repeat"): stringify({"a": ["foo", "bar"]}, array_format=cast(Any, "foo")) diff --git a/tests/test_rate_limiter.py b/tests/test_rate_limiter.py deleted file mode 100644 index 72c1e35f7..000000000 --- a/tests/test_rate_limiter.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env python3 -"""Test rate limiting and error recovery.""" - -import sys -import asyncio -from pathlib import Path - -import pytest -from rich.table import Table -from rich.console import Console - -# Add hanzo src to path -sys.path.insert(0, str(Path(__file__).parent.parent / "pkg" / "hanzo" / "src")) - -from hanzo.rate_limiter import ( - RateLimiter, - ErrorRecovery, - RateLimitConfig, - smart_limiter, -) - - -async def test_rate_limiter(): - """Test rate limiter functionality.""" - console = Console() - - console.print("\n[bold cyan]Testing Rate Limiter[/bold cyan]\n") - - # Create a strict rate limiter for testing - config = RateLimitConfig(requests_per_minute=5, requests_per_hour=100, burst_size=2) - limiter = RateLimiter(config) - - # Test rapid requests - console.print("[bold]Testing rate limiting (5 requests/minute):[/bold]") - - for i in range(8): - allowed, wait = await limiter.check_rate_limit() - - if allowed: - await limiter.acquire() - console.print(f"โœ… Request {i + 1} allowed") - else: - console.print(f"โณ Request {i + 1} blocked, wait {wait:.1f}s") - - # Small delay between requests - await asyncio.sleep(0.1) - - # Test status - console.print("\n[bold]Rate limiter status:[/bold]") - status = limiter.get_status() - - table = Table(title="Rate Limiter Status") - table.add_column("Metric", style="cyan") - table.add_column("Value", style="white") - - for key, value in status.items(): - table.add_row(key.replace("_", " ").title(), str(value)) - - console.print(table) - - # Test error recovery - console.print("\n[bold]Testing error recovery:[/bold]") - - recovery = ErrorRecovery(limiter) - - # Simulate a function that fails initially - attempt_count = 0 - - async def flaky_function(): - nonlocal attempt_count - attempt_count += 1 - - if attempt_count < 3: - console.print(f" Attempt {attempt_count} failed") - raise Exception("Simulated error") - - console.print(f" Attempt {attempt_count} succeeded!") - return "Success" - - try: - result = await recovery.with_retry(flaky_function, max_retries=5) - console.print(f"[green]Result: {result}[/green]") - except Exception as e: - console.print(f"[red]Failed after retries: {e}[/red]") - - # Test smart limiter - console.print("\n[bold]Testing smart rate limiter:[/bold]") - - # Simulate API calls - async def mock_api_call(api_type: str): - console.print(f" Calling {api_type} API...") - await asyncio.sleep(0.1) - return f"Response from {api_type}" - - # Test different API types - for api_type in ["openai", "anthropic", "local", "free"]: - try: - result = await smart_limiter.execute_with_limit(api_type, mock_api_call, api_type) - console.print(f" โœ… {api_type}: {result}") - except Exception as e: - console.print(f" โŒ {api_type}: {e}") - - # Show all limiter statuses - console.print("\n[bold]All API limiter statuses:[/bold]") - all_status = smart_limiter.get_all_status() - - for api_type, status in all_status.items(): - if status["total_requests"] > 0: - console.print(f"\n{api_type}:") - console.print(f" Requests: {status['total_requests']}") - console.print(f" Errors: {status['total_errors']}") - console.print(f" Last minute: {status['requests_last_minute']}") - - console.print("\n[green]โœ… Rate limiting tests completed![/green]") - - return True - - -if __name__ == "__main__": - success = asyncio.run(test_rate_limiter()) - sys.exit(0 if success else 1) diff --git a/tests/test_refactoring.py b/tests/test_refactoring.py deleted file mode 100644 index 0e2c4be4c..000000000 --- a/tests/test_refactoring.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python3 -"""Test script to verify DRY refactoring works correctly.""" - -import sys -import asyncio -from pathlib import Path - -import pytest - -# Add packages to path -sys.path.insert(0, str(Path(__file__).parent.parent / "pkg" / "hanzo" / "src")) -sys.path.insert(0, str(Path(__file__).parent.parent / "pkg" / "hanzo-mcp")) - -from hanzo.batch_orchestrator import BatchConfig -from hanzo_mcp.core.base_agent import AgentConfig, AgentOrchestrator -from hanzo_mcp.core.model_registry import ModelProvider, registry - - -def test_model_registry(): - """Test unified model registry.""" - print("Testing Model Registry...") - - # Test model resolution - assert registry.resolve("claude") == "claude-3-5-sonnet-20241022" - assert registry.resolve("cc") == "claude-3-5-sonnet-20241022" - assert registry.resolve("gemini") == "gemini-1.5-pro" - assert registry.resolve("gemini-2.5") == "gemini-exp-1206" - assert registry.resolve("codex") == "gpt-4-turbo" - - # Test getting by provider - anthropic_models = registry.get_by_provider(ModelProvider.ANTHROPIC) - assert len(anthropic_models) > 0 - assert any("claude" in m.full_name for m in anthropic_models) - - # Test feature filtering - vision_models = registry.get_models_supporting(vision=True) - assert len(vision_models) > 0 - - print("โœ“ Model Registry: All tests passed") - - -def test_batch_config(): - """Test batch configuration parsing without duplication.""" - print("\nTesting Batch Config...") - - # Test simple batch - config = BatchConfig.from_command("batch:10 add copyright to files") - assert config.batch_size == 10 - assert config.agent_model == "claude-3-5-sonnet-20241022" - assert config.operation == "add copyright to files" - - # Test with agent - config = BatchConfig.from_command("batch:5 agent:gemini analyze code") - assert config.batch_size == 5 - assert config.agent_model == registry.resolve("gemini") # Should resolve properly - - # Test consensus - config = BatchConfig.from_command("consensus:3 agent:claude,gemini,codex review") - assert config.consensus_mode == True - assert config.batch_size == 3 - assert len(config.consensus_models) == 3 - assert "claude-3-5-sonnet-20241022" in config.consensus_models - assert "gemini-1.5-pro" in config.consensus_models - assert "gpt-4-turbo" in config.consensus_models - - # Test critic chain - config = BatchConfig.from_command("critic:3 chain:true agent:claude,codex review") - assert config.critic_mode == True - assert config.critic_chain == True - assert len(config.critic_models) == 2 - - print("โœ“ Batch Config: All tests passed") - - -async def test_agent_orchestrator(): - """Test agent orchestrator.""" - print("\nTesting Agent Orchestrator...") - - orchestrator = AgentOrchestrator() - - # Test consensus execution (mock) - result = await orchestrator.execute_consensus("Test prompt", ["claude", "gemini", "codex"], threshold=0.66) - - assert "consensus_reached" in result - assert "agreement_score" in result - assert "agents_used" in result - assert result["agents_used"] == ["claude", "gemini", "codex"] - - print("โœ“ Agent Orchestrator: Basic tests passed") - - -def main(): - """Run all tests.""" - print("=" * 60) - print("Testing DRY Refactoring - MAGNUM OPUS") - print("=" * 60) - - # Test model registry - test_model_registry() - - # Test batch config - test_batch_config() - - # Test agent orchestrator - asyncio.run(test_agent_orchestrator()) - - print("\n" + "=" * 60) - print("โœจ ALL TESTS PASSED - DRY REFACTORING SUCCESSFUL! โœจ") - print("=" * 60) - print("\nKey achievements:") - print("1. โœ“ Single model registry - no duplication") - print("2. โœ“ Unified base agent classes") - print("3. โœ“ Clean batch/consensus/critic orchestration") - print("4. โœ“ Proper typing throughout") - print("5. โœ“ Import paths resolved") - print("\nThe code now follows Python best practices:") - print("- Exactly ONE way to do each thing") - print("- No repeated model mappings") - print("- Clean inheritance hierarchy") - print("- Thread-safe singleton pattern") - print("- Proper separation of concerns") - - -if __name__ == "__main__": - main() diff --git a/tests/test_response.py b/tests/test_response.py index 0a431cf22..83ebfefdc 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -74,9 +74,7 @@ def test_response_parse_mismatched_basemodel(client: Hanzo) -> None: @pytest.mark.asyncio -async def test_async_response_parse_mismatched_basemodel( - async_client: AsyncHanzo, -) -> None: +async def test_async_response_parse_mismatched_basemodel(async_client: AsyncHanzo) -> None: response = AsyncAPIResponse( raw=httpx.Response(200, content=b"foo"), client=async_client, @@ -264,9 +262,7 @@ def test_response_parse_expect_model_union_non_json_content(client: Hanzo) -> No @pytest.mark.asyncio @pytest.mark.parametrize("async_client", [False], indirect=True) # loose validation -async def test_async_response_parse_expect_model_union_non_json_content( - async_client: AsyncHanzo, -) -> None: +async def test_async_response_parse_expect_model_union_non_json_content(async_client: AsyncHanzo) -> None: response = AsyncAPIResponse( raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), client=async_client, diff --git a/tests/test_session.py b/tests/test_session.py deleted file mode 100644 index fa367a968..000000000 --- a/tests/test_session.py +++ /dev/null @@ -1,199 +0,0 @@ -"""Tests for session management and compaction.""" - -import json -import tempfile -from pathlib import Path - -import pytest - -from hanzoai.session import ( - Session, - TextBlock, - TokenUsage, - MessageRole, - ToolUseBlock, - ToolResultBlock, - CompactionConfig, - CompactionResult, - ConversationMessage, - should_compact, - compact_session, - format_compact_summary, - estimate_session_tokens, -) - - -class TestTokenUsage: - def test_total_tokens(self): - u = TokenUsage(input_tokens=10, output_tokens=4, cache_creation_input_tokens=2, cache_read_input_tokens=1) - assert u.total_tokens() == 17 - - def test_roundtrip(self): - u = TokenUsage(input_tokens=5, output_tokens=3, cache_creation_input_tokens=1, cache_read_input_tokens=0) - assert TokenUsage.from_dict(u.to_dict()) == u - - -class TestContentBlocks: - def test_text_block_roundtrip(self): - b = TextBlock(text="hello") - d = b.to_dict() - assert d == {"type": "text", "text": "hello"} - assert TextBlock.from_dict(d) == b - - def test_tool_use_block_roundtrip(self): - b = ToolUseBlock(id="t1", name="bash", input='{"command":"ls"}') - d = b.to_dict() - assert d["type"] == "tool_use" - assert ToolUseBlock.from_dict(d) == b - - def test_tool_result_block_roundtrip(self): - b = ToolResultBlock(tool_use_id="t1", tool_name="bash", output="ok", is_error=False) - d = b.to_dict() - assert d["type"] == "tool_result" - assert ToolResultBlock.from_dict(d) == b - - -class TestConversationMessage: - def test_user_text_factory(self): - m = ConversationMessage.user_text("hello") - assert m.role == MessageRole.User - assert len(m.blocks) == 1 - assert isinstance(m.blocks[0], TextBlock) - - def test_tool_result_factory(self): - m = ConversationMessage.tool_result("t1", "bash", "output", is_error=True) - assert m.role == MessageRole.Tool - assert isinstance(m.blocks[0], ToolResultBlock) - assert m.blocks[0].is_error - - def test_assistant_with_usage(self): - u = TokenUsage(input_tokens=10, output_tokens=4) - m = ConversationMessage.assistant_with_usage([TextBlock(text="hi")], u) - assert m.usage == u - - def test_roundtrip(self): - m = ConversationMessage.user_text("test") - assert ConversationMessage.from_dict(m.to_dict()).role == m.role - - -class TestSession: - def test_empty_session(self): - s = Session() - assert s.version == 1 - assert s.messages == [] - - def test_roundtrip_dict(self): - s = Session() - s.messages.append(ConversationMessage.user_text("hello")) - s.messages.append(ConversationMessage.assistant([TextBlock(text="hi")])) - s.messages.append(ConversationMessage.tool_result("t1", "bash", "ok", False)) - - restored = Session.from_dict(s.to_dict()) - assert len(restored.messages) == 3 - assert restored.messages[0].role == MessageRole.User - assert restored.messages[2].role == MessageRole.Tool - - def test_save_and_load(self): - s = Session() - s.messages.append(ConversationMessage.user_text("hello")) - s.messages.append( - ConversationMessage.assistant_with_usage( - [ - TextBlock(text="thinking"), - ToolUseBlock(id="t1", name="bash", input='{"cmd":"echo hi"}'), - ], - TokenUsage(input_tokens=10, output_tokens=4, cache_creation_input_tokens=1, cache_read_input_tokens=2), - ) - ) - s.messages.append(ConversationMessage.tool_result("t1", "bash", "hi", False)) - - with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f: - path = f.name - - s.save(path) - restored = Session.load(path) - - assert restored == s - assert restored.messages[1].usage.total_tokens() == 17 - assert restored.messages[2].role == MessageRole.Tool - Path(path).unlink() - - -class TestCompaction: - def _big_session(self, n: int = 10) -> Session: - s = Session() - for i in range(n): - s.messages.append(ConversationMessage.user_text("x " * 200)) - s.messages.append(ConversationMessage.assistant([TextBlock(text="y " * 200)])) - return s - - def test_small_session_unchanged(self): - s = Session() - s.messages.append(ConversationMessage.user_text("hello")) - result = compact_session(s, CompactionConfig()) - assert result.removed_message_count == 0 - assert result.compacted_session is s - - def test_compacts_old_messages(self): - s = self._big_session(10) - config = CompactionConfig(preserve_recent_messages=2, max_estimated_tokens=1) - result = compact_session(s, config) - - assert result.removed_message_count == 18 - assert result.compacted_session.messages[0].role == MessageRole.System - assert len(result.compacted_session.messages) == 3 # system + 2 preserved - assert result.summary != "" - assert result.formatted_summary != "" - - def test_should_compact_checks_both(self): - s = Session() - s.messages.append(ConversationMessage.user_text("short")) - # Few messages, low tokens - should not compact - assert not should_compact(s, CompactionConfig(preserve_recent_messages=0, max_estimated_tokens=100)) - - def test_estimate_tokens(self): - s = Session() - s.messages.append(ConversationMessage.user_text("a" * 400)) - tokens = estimate_session_tokens(s) - assert tokens == 400 // 4 + 1 # 101 - - def test_compaction_reduces_tokens(self): - s = self._big_session(10) - config = CompactionConfig(preserve_recent_messages=2, max_estimated_tokens=1) - result = compact_session(s, config) - assert estimate_session_tokens(result.compacted_session) < estimate_session_tokens(s) - - def test_summary_contains_tool_names(self): - s = Session() - for _ in range(6): - s.messages.append(ConversationMessage.user_text("x " * 200)) - s.messages.append( - ConversationMessage.assistant( - [TextBlock(text="ok"), ToolUseBlock(id="t1", name="bash", input='{"cmd":"ls"}')] - ) - ) - s.messages.append(ConversationMessage.tool_result("t1", "bash", "output " * 50, False)) - - config = CompactionConfig(preserve_recent_messages=2, max_estimated_tokens=1) - result = compact_session(s, config) - assert "bash" in result.summary - - def test_summary_contains_file_candidates(self): - s = Session() - for _ in range(5): - s.messages.append(ConversationMessage.user_text("Update src/main.rs and pkg/config.py next " + "x " * 200)) - s.messages.append(ConversationMessage.assistant([TextBlock(text="done " * 200)])) - - config = CompactionConfig(preserve_recent_messages=2, max_estimated_tokens=1) - result = compact_session(s, config) - assert "src/main.rs" in result.summary - - def test_format_compact_summary_strips_analysis(self): - summary = "scratch\nKept work" - formatted = format_compact_summary(summary) - assert "analysis" not in formatted - assert "Kept work" in formatted - - def test_format_compact_summary_no_tags(self): - formatted = format_compact_summary("plain text summary") - assert formatted == "plain text summary" diff --git a/tests/test_signal_handling.py b/tests/test_signal_handling.py deleted file mode 100644 index fb1880127..000000000 --- a/tests/test_signal_handling.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python3 -"""Test signal handling for hanzo net command.""" - -import os -import sys -import time -import signal -import subprocess - -import pytest - - -class TestSignalHandling: - """Test signal handling for hanzo network commands.""" - - @pytest.fixture - def env_with_pythonpath(self): - """Create environment with PYTHONPATH set.""" - env = os.environ.copy() - pkg_path = os.path.join(os.path.dirname(__file__), "..", "pkg", "hanzo", "src") - env["PYTHONPATH"] = pkg_path + ":" + env.get("PYTHONPATH", "") - return env - - @pytest.mark.skipif(sys.platform == "win32", reason="Signal handling test not supported on Windows") - def test_sigint_graceful_shutdown(self, env_with_pythonpath): - """Test that Ctrl-C (SIGINT) properly stops hanzo net.""" - import fcntl - - process = subprocess.Popen( - [sys.executable, "-m", "hanzo", "net"], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - env=env_with_pythonpath, - cwd=os.path.dirname(__file__), - preexec_fn=os.setsid, # Create new process group - ) - - # Give it time to start - time.sleep(3) - - # Check if process exited early (which is acceptable in test env) - if process.poll() is not None: - # Process exited - this is OK in CI environment - # where network might not be fully available - stdout, stderr = process.communicate() - # Just verify it ran and exited - assert process.returncode is not None - return - - # Process is running, make stdout/stderr non-blocking - fl = fcntl.fcntl(process.stdout.fileno(), fcntl.F_GETFL) - fcntl.fcntl(process.stdout.fileno(), fcntl.F_SETFL, fl | os.O_NONBLOCK) - - fl = fcntl.fcntl(process.stderr.fileno(), fcntl.F_GETFL) - fcntl.fcntl(process.stderr.fileno(), fcntl.F_SETFL, fl | os.O_NONBLOCK) - - # Send SIGINT to process group - os.killpg(os.getpgid(process.pid), signal.SIGINT) - - # Wait for graceful shutdown - try: - returncode = process.wait(timeout=10) - # 0 = success, -2 = killed by SIGINT (expected) - assert returncode in [0, -2, 1], f"Unexpected return code: {returncode}" - except subprocess.TimeoutExpired: - # Force kill if it didn't shutdown gracefully - process.kill() - process.wait() - pytest.fail("Process did not shut down within 10 seconds") - - -if __name__ == "__main__": - sys.exit(pytest.main([__file__, "-v"])) diff --git a/tests/test_sse_chunking.py b/tests/test_sse_chunking.py deleted file mode 100644 index 2e518d5aa..000000000 --- a/tests/test_sse_chunking.py +++ /dev/null @@ -1,180 +0,0 @@ -"""Tests for SSE chunking and decoding in hanzoai._streaming.""" -from __future__ import annotations - -import sys -import asyncio -from typing import Iterator, AsyncIterator -from pathlib import Path - -import pytest - -sys.path.insert(0, str(Path(__file__).parent.parent / "pkg" / "hanzoai")) - -from hanzoai._streaming import SSEDecoder, ServerSentEvent - - -def make_sync_iterator(chunks: list[bytes]) -> Iterator[bytes]: - yield from chunks - - -async def make_async_iterator(chunks: list[bytes]) -> AsyncIterator[bytes]: - for chunk in chunks: - yield chunk - - -class TestIterChunks: - """Test that _iter_chunks correctly splits SSE frames.""" - - def _collect_chunks(self, raw_chunks: list[bytes]) -> list[bytes]: - decoder = SSEDecoder() - return list(decoder._iter_chunks(make_sync_iterator(raw_chunks))) - - def test_simple_frame_lf(self): - """Single frame terminated by \\n\\n.""" - raw = [b"data: hello\n\n"] - chunks = self._collect_chunks(raw) - assert chunks == [b"data: hello\n\n"] - - def test_simple_frame_crlf(self): - """Single frame terminated by \\r\\n\\r\\n.""" - raw = [b"data: hello\r\n\r\n"] - chunks = self._collect_chunks(raw) - assert chunks == [b"data: hello\r\n\r\n"] - - def test_simple_frame_cr(self): - """Single frame terminated by \\r\\r.""" - raw = [b"data: hello\r\r"] - chunks = self._collect_chunks(raw) - assert chunks == [b"data: hello\r\r"] - - def test_multiple_frames(self): - """Multiple frames in one byte chunk.""" - raw = [b"data: one\n\ndata: two\n\n"] - chunks = self._collect_chunks(raw) - assert chunks == [b"data: one\n\n", b"data: two\n\n"] - - def test_split_across_chunks(self): - """Frame split across multiple byte chunks.""" - raw = [b"data: hel", b"lo\n\n"] - chunks = self._collect_chunks(raw) - assert chunks == [b"data: hello\n\n"] - - def test_separator_split_across_chunks_lf(self): - """\\n\\n separator split across chunk boundary.""" - raw = [b"data: hello\n", b"\n"] - chunks = self._collect_chunks(raw) - assert chunks == [b"data: hello\n\n"] - - def test_separator_split_across_chunks_crlf(self): - """\\r\\n\\r\\n separator split across chunk boundary at various points.""" - # Split between \r\n and \r\n - raw = [b"data: hello\r\n", b"\r\n"] - chunks = self._collect_chunks(raw) - assert chunks == [b"data: hello\r\n\r\n"] - - def test_crlf_split_mid_separator(self): - """The critical bug case: \\r\\n\\r\\n split so \\r and \\n land in different chunks.""" - # This is the case splitlines(keepends=True) gets wrong: - # chunk1 ends with \r, chunk2 starts with \n\r\n - raw = [b"data: hello\r", b"\n\r\n"] - chunks = self._collect_chunks(raw) - assert chunks == [b"data: hello\r\n\r\n"] - - def test_crlf_split_three_ways(self): - """\\r\\n\\r\\n split across three chunks.""" - raw = [b"data: hello\r\n", b"\r", b"\n"] - chunks = self._collect_chunks(raw) - assert chunks == [b"data: hello\r\n\r\n"] - - def test_trailing_data_no_terminator(self): - """Data without a trailing separator is yielded on stream end.""" - raw = [b"data: hello"] - chunks = self._collect_chunks(raw) - assert chunks == [b"data: hello"] - - def test_empty_input(self): - """No data yields nothing.""" - chunks = self._collect_chunks([]) - assert chunks == [] - - def test_only_separator(self): - """Just a separator yields it as a chunk.""" - raw = [b"\n\n"] - chunks = self._collect_chunks(raw) - assert chunks == [b"\n\n"] - - def test_multiple_frames_split(self): - """Two frames, each split across chunks.""" - raw = [b"data: one\n", b"\ndata: tw", b"o\n\n"] - chunks = self._collect_chunks(raw) - assert chunks == [b"data: one\n\n", b"data: two\n\n"] - - -class TestAiterChunks: - """Test that _aiter_chunks matches _iter_chunks behavior.""" - - async def _collect_chunks(self, raw_chunks: list[bytes]) -> list[bytes]: - decoder = SSEDecoder() - result = [] - async for chunk in decoder._aiter_chunks(make_async_iterator(raw_chunks)): - result.append(chunk) - return result - - @pytest.mark.asyncio - async def test_simple_frame(self): - chunks = await self._collect_chunks([b"data: hello\n\n"]) - assert chunks == [b"data: hello\n\n"] - - @pytest.mark.asyncio - async def test_crlf_split_mid_separator(self): - """The critical bug case for async path.""" - raw = [b"data: hello\r", b"\n\r\n"] - chunks = await self._collect_chunks(raw) - assert chunks == [b"data: hello\r\n\r\n"] - - @pytest.mark.asyncio - async def test_multiple_frames_split(self): - raw = [b"data: one\n", b"\ndata: tw", b"o\n\n"] - chunks = await self._collect_chunks(raw) - assert chunks == [b"data: one\n\n", b"data: two\n\n"] - - -class TestDoneAndPingSentinel: - """Test that [DONE] and event: ping are skipped.""" - - def _collect_events(self, raw_chunks: list[bytes]) -> list[ServerSentEvent]: - decoder = SSEDecoder() - return list(decoder.iter_bytes(make_sync_iterator(raw_chunks))) - - def test_done_sentinel_skipped(self): - """data: [DONE] should not produce an event.""" - raw = [b"data: {\"text\": \"hi\"}\n\n", b"data: [DONE]\n\n"] - events = self._collect_events(raw) - assert len(events) == 1 - assert events[0].data == '{"text": "hi"}' - - def test_ping_event_skipped(self): - """event: ping should not produce an event.""" - raw = [b"event: ping\ndata: \n\n", b"data: {\"text\": \"hi\"}\n\n"] - events = self._collect_events(raw) - assert len(events) == 1 - assert events[0].data == '{"text": "hi"}' - - def test_normal_events_pass_through(self): - """Normal events should still work.""" - raw = [b"data: {\"a\": 1}\n\n", b"data: {\"b\": 2}\n\n"] - events = self._collect_events(raw) - assert len(events) == 2 - assert events[0].json() == {"a": 1} - assert events[1].json() == {"b": 2} - - @pytest.mark.asyncio - async def test_done_sentinel_skipped_async(self): - """data: [DONE] should not produce an event in async path.""" - decoder = SSEDecoder() - raw = [b"data: {\"text\": \"hi\"}\n\n", b"data: [DONE]\n\n"] - events = [] - async for sse in decoder.aiter_bytes(make_async_iterator(raw)): - events.append(sse) - assert len(events) == 1 - assert events[0].data == '{"text": "hi"}' diff --git a/tests/test_streaming.py b/tests/test_streaming.py index a4a59119c..48d59fa05 100644 --- a/tests/test_streaming.py +++ b/tests/test_streaming.py @@ -1,64 +1,248 @@ -#!/usr/bin/env python3 -"""Test streaming responses.""" +from __future__ import annotations -import sys -import asyncio -from pathlib import Path +from typing import Iterator, AsyncIterator +import httpx import pytest -from rich.console import Console -# Add hanzo src to path -sys.path.insert(0, str(Path(__file__).parent.parent / "pkg" / "hanzo" / "src")) +from hanzoai import Hanzo, AsyncHanzo +from hanzoai._streaming import Stream, AsyncStream, ServerSentEvent -from hanzo.streaming import StreamingHandler, TypewriterEffect, stream_with_fallback +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_basic(sync: bool, client: Hanzo, async_client: AsyncHanzo) -> None: + def body() -> Iterator[bytes]: + yield b"event: completion\n" + yield b'data: {"foo":true}\n' + yield b"\n" -async def test_streaming(): - """Test streaming functionality.""" - console = Console() + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) - console.print("\n[bold cyan]Testing Streaming Responses[/bold cyan]\n") + sse = await iter_next(iterator) + assert sse.event == "completion" + assert sse.json() == {"foo": True} - # Test typewriter effect - console.print("[bold]Testing typewriter effect:[/bold]") - typewriter = TypewriterEffect(console) + await assert_empty_iter(iterator) - await typewriter.type_text("This is a typewriter effect demonstration...", speed=0.02) - # Test code typing - console.print("\n[bold]Testing code typing:[/bold]") - code = """def fibonacci(n: int) -> int: - if n <= 1: - return n - return fibonacci(n-1) + fibonacci(n-2)""" +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_data_missing_event(sync: bool, client: Hanzo, async_client: AsyncHanzo) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"foo":true}\n' + yield b"\n" - await typewriter.type_code(code, language="python", speed=0.01) + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) - # Test simulated streaming - console.print("\n[bold]Testing simulated streaming:[/bold]") - handler = StreamingHandler(console) + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"foo": True} - sample_text = "This is a simulated streaming response. It will appear word by word as if being generated in real-time. This creates a better user experience!" + await assert_empty_iter(iterator) - await handler.simulate_streaming(sample_text, delay=0.03) - # Test real streaming with fallback - console.print("\n[bold]Testing streaming with fallback:[/bold]") +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_event_missing_data(sync: bool, client: Hanzo, async_client: AsyncHanzo) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"\n" - test_message = "What is 2 + 2? Reply with just the number." + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) - response = await stream_with_fallback(test_message, console) + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.data == "" - if response: - console.print(f"\n[green]โœ… Streaming test successful![/green]") - console.print(f"Response: {response}") - else: - console.print("\n[yellow]โš ๏ธ No streaming available (no API keys)[/yellow]") + await assert_empty_iter(iterator) - return True +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_events(sync: bool, client: Hanzo, async_client: AsyncHanzo) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"\n" + yield b"event: completion\n" + yield b"\n" -if __name__ == "__main__": - success = asyncio.run(test_streaming()) - sys.exit(0 if success else 1) + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.data == "" + + sse = await iter_next(iterator) + assert sse.event == "completion" + assert sse.data == "" + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_events_with_data(sync: bool, client: Hanzo, async_client: AsyncHanzo) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b'data: {"foo":true}\n' + yield b"\n" + yield b"event: completion\n" + yield b'data: {"bar":false}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + + sse = await iter_next(iterator) + assert sse.event == "completion" + assert sse.json() == {"bar": False} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_data_lines_with_empty_line(sync: bool, client: Hanzo, async_client: AsyncHanzo) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"data: {\n" + yield b'data: "foo":\n' + yield b"data: \n" + yield b"data:\n" + yield b"data: true}\n" + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + assert sse.data == '{\n"foo":\n\n\ntrue}' + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_data_json_escaped_double_new_line(sync: bool, client: Hanzo, async_client: AsyncHanzo) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b'data: {"foo": "my long\\n\\ncontent"}' + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": "my long\n\ncontent"} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_data_lines(sync: bool, client: Hanzo, async_client: AsyncHanzo) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"data: {\n" + yield b'data: "foo":\n' + yield b"data: true}\n" + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + + await assert_empty_iter(iterator) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_special_new_line_character( + sync: bool, + client: Hanzo, + async_client: AsyncHanzo, +) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"content":" culpa"}\n' + yield b"\n" + yield b'data: {"content":" \xe2\x80\xa8"}\n' + yield b"\n" + yield b'data: {"content":"foo"}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": " culpa"} + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": " โ€จ"} + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": "foo"} + + await assert_empty_iter(iterator) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multi_byte_character_multiple_chunks( + sync: bool, + client: Hanzo, + async_client: AsyncHanzo, +) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"content":"' + # bytes taken from the string 'ะธะทะฒะตัั‚ะฝะธ' and arbitrarily split + # so that some multi-byte characters span multiple chunks + yield b"\xd0" + yield b"\xb8\xd0\xb7\xd0" + yield b"\xb2\xd0\xb5\xd1\x81\xd1\x82\xd0\xbd\xd0\xb8" + yield b'"}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": "ะธะทะฒะตัั‚ะฝะธ"} + + +async def to_aiter(iter: Iterator[bytes]) -> AsyncIterator[bytes]: + for chunk in iter: + yield chunk + + +async def iter_next(iter: Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]) -> ServerSentEvent: + if isinstance(iter, AsyncIterator): + return await iter.__anext__() + + return next(iter) + + +async def assert_empty_iter(iter: Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]) -> None: + with pytest.raises((StopAsyncIteration, RuntimeError)): + await iter_next(iter) + + +def make_event_iterator( + content: Iterator[bytes], + *, + sync: bool, + client: Hanzo, + async_client: AsyncHanzo, +) -> Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]: + if sync: + return Stream(cast_to=object, client=client, response=httpx.Response(200, content=content))._iter_events() + + return AsyncStream( + cast_to=object, client=async_client, response=httpx.Response(200, content=to_aiter(content)) + )._iter_events() diff --git a/tests/test_todo.py b/tests/test_todo.py deleted file mode 100644 index 436befb8a..000000000 --- a/tests/test_todo.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python -"""Test the todo manager directly.""" - -import sys -import asyncio -from pathlib import Path - -import pytest -from rich.console import Console - -# Add hanzo src to path -sys.path.insert(0, str(Path(__file__).parent.parent / "pkg" / "hanzo" / "src")) - -from hanzo.interactive.todo_manager import TodoManager - - -async def test_todo(): - console = Console() - manager = TodoManager(console) - - # Add some todos - print("Adding todos...") - todo1 = manager.add_todo( - title="Implement new feature", - description="Add support for webhooks", - priority="high", - tags=["dev", "backend"], - due_date="2025-01-15", - ) - print(f"Added: {todo1.title} (ID: {todo1.id})") - - todo2 = manager.quick_add("Fix bug in auth system #bug #urgent !urgent @today") - print(f"Added: {todo2.title} (ID: {todo2.id})") - - todo3 = manager.quick_add("Write documentation #docs !low") - print(f"Added: {todo3.title} (ID: {todo3.id})") - - # Display todos - print("\nAll Todos:") - manager.display_todos() - - # Update a todo - print(f"\nMarking {todo2.id} as in progress...") - manager.update_todo(todo2.id, status="in_progress") - - # Display filtered todos - print("\nHigh priority todos:") - high_priority = manager.list_todos(priority="high") - manager.display_todos(high_priority, "High Priority") - - # Show statistics - print("\nStatistics:") - manager.display_statistics() - - # Show detail - print(f"\nDetail for {todo1.id}:") - manager.display_todo_detail(todo1) - - -if __name__ == "__main__": - asyncio.run(test_todo()) diff --git a/tests/test_transform.py b/tests/test_transform.py index aff96e504..bc50d64c4 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -8,14 +8,14 @@ import pytest -from hanzoai._types import Base64FileInput +from hanzoai._types import Base64FileInput, omit, not_given from hanzoai._utils import ( PropertyInfo, transform as _transform, parse_datetime, async_transform as _async_transform, ) -from hanzoai._compat import PYDANTIC_V2 +from hanzoai._compat import PYDANTIC_V1 from hanzoai._models import BaseModel _T = TypeVar("_T") @@ -189,7 +189,7 @@ class DateModel(BaseModel): @pytest.mark.asyncio async def test_iso8601_format(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") - tz = "Z" if PYDANTIC_V2 else "+00:00" + tz = "+00:00" if PYDANTIC_V1 else "Z" assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692" + tz} # type: ignore[comparison-overlap] @@ -251,11 +251,7 @@ async def test_nested_list_iso6801_format(use_async: bool) -> None: async def test_datetime_custom_format(use_async: bool) -> None: dt = parse_datetime("2022-01-15T06:34:23Z") - result = await transform( - dt, - Annotated[datetime, PropertyInfo(format="custom", format_template="%H")], - use_async, - ) + result = await transform(dt, Annotated[datetime, PropertyInfo(format="custom", format_template="%H")], use_async) assert result == "06" # type: ignore[comparison-overlap] @@ -268,9 +264,7 @@ class DateDictWithRequiredAlias(TypedDict, total=False): async def test_datetime_with_alias(use_async: bool) -> None: assert await transform({"required_prop": None}, DateDictWithRequiredAlias, use_async) == {"prop": None} # type: ignore[comparison-overlap] assert await transform( - {"required_prop": date.fromisoformat("2023-02-23")}, - DateDictWithRequiredAlias, - use_async, + {"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias, use_async ) == {"prop": "2023-02-23"} # type: ignore[comparison-overlap] @@ -303,11 +297,11 @@ async def test_pydantic_unknown_field(use_async: bool) -> None: @pytest.mark.asyncio async def test_pydantic_mismatched_types(use_async: bool) -> None: model = MyModel.construct(foo=True) - if PYDANTIC_V2: + if PYDANTIC_V1: + params = await transform(model, Any, use_async) + else: with pytest.warns(UserWarning): params = await transform(model, Any, use_async) - else: - params = await transform(model, Any, use_async) assert cast(Any, params) == {"foo": True} @@ -315,11 +309,11 @@ async def test_pydantic_mismatched_types(use_async: bool) -> None: @pytest.mark.asyncio async def test_pydantic_mismatched_object_type(use_async: bool) -> None: model = MyModel.construct(foo=MyModel.construct(hello="world")) - if PYDANTIC_V2: + if PYDANTIC_V1: + params = await transform(model, Any, use_async) + else: with pytest.warns(UserWarning): params = await transform(model, Any, use_async) - else: - params = await transform(model, Any, use_async) assert cast(Any, params) == {"foo": {"hello": "world"}} @@ -354,19 +348,13 @@ async def test_pydantic_default_field(use_async: bool) -> None: model = ModelWithDefaultField.construct(with_none_default=None, with_str_default="foo") assert model.with_none_default is None assert model.with_str_default == "foo" - assert cast(Any, await transform(model, Any, use_async)) == { - "with_none_default": None, - "with_str_default": "foo", - } + assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": None, "with_str_default": "foo"} # should be included when a non-default value is explicitly given model = ModelWithDefaultField.construct(with_none_default="bar", with_str_default="baz") assert model.with_none_default == "bar" assert model.with_str_default == "baz" - assert cast(Any, await transform(model, Any, use_async)) == { - "with_none_default": "bar", - "with_str_default": "baz", - } + assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": "bar", "with_str_default": "baz"} class TypedDictIterableUnion(TypedDict): @@ -387,10 +375,9 @@ async def test_iterable_of_dictionaries(use_async: bool) -> None: assert await transform({"foo": [{"foo_baz": "bar"}]}, TypedDictIterableUnion, use_async) == { "FOO": [{"fooBaz": "bar"}] } - assert cast( - Any, - await transform({"foo": ({"foo_baz": "bar"},)}, TypedDictIterableUnion, use_async), - ) == {"FOO": [{"fooBaz": "bar"}]} + assert cast(Any, await transform({"foo": ({"foo_baz": "bar"},)}, TypedDictIterableUnion, use_async)) == { + "FOO": [{"fooBaz": "bar"}] + } def my_iter() -> Iterable[Baz8]: yield {"foo_baz": "hello"} @@ -418,10 +405,9 @@ class TypedDictIterableUnionStr(TypedDict): @pytest.mark.asyncio async def test_iterable_union_str(use_async: bool) -> None: assert await transform({"foo": "bar"}, TypedDictIterableUnionStr, use_async) == {"FOO": "bar"} - assert cast( - Any, - await transform(iter([{"foo_baz": "bar"}]), Union[str, Iterable[Baz8]], use_async), - ) == [{"fooBaz": "bar"}] + assert cast(Any, await transform(iter([{"foo_baz": "bar"}]), Union[str, Iterable[Baz8]], use_async)) == [ + {"fooBaz": "bar"} + ] class TypedDictBase64Input(TypedDict): @@ -446,3 +432,29 @@ async def test_base64_file_input(use_async: bool) -> None: assert await transform({"foo": io.BytesIO(b"Hello, world!")}, TypedDictBase64Input, use_async) == { "foo": "SGVsbG8sIHdvcmxkIQ==" } # type: ignore[comparison-overlap] + + +@parametrize +@pytest.mark.asyncio +async def test_transform_skipping(use_async: bool) -> None: + # lists of ints are left as-is + data = [1, 2, 3] + assert await transform(data, List[int], use_async) is data + + # iterables of ints are converted to a list + data = iter([1, 2, 3]) + assert await transform(data, Iterable[int], use_async) == [1, 2, 3] + + +@parametrize +@pytest.mark.asyncio +async def test_strips_notgiven(use_async: bool) -> None: + assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} + assert await transform({"foo_bar": not_given}, Foo1, use_async) == {} + + +@parametrize +@pytest.mark.asyncio +async def test_strips_omit(use_async: bool) -> None: + assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} + assert await transform({"foo_bar": omit}, Foo1, use_async) == {} diff --git a/tests/test_utils/test_datetime_parse.py b/tests/test_utils/test_datetime_parse.py new file mode 100644 index 000000000..467ceceae --- /dev/null +++ b/tests/test_utils/test_datetime_parse.py @@ -0,0 +1,110 @@ +""" +Copied from https://github.com/pydantic/pydantic/blob/v1.10.22/tests/test_datetime_parse.py +with modifications so it works without pydantic v1 imports. +""" + +from typing import Type, Union +from datetime import date, datetime, timezone, timedelta + +import pytest + +from hanzoai._utils import parse_date, parse_datetime + + +def create_tz(minutes: int) -> timezone: + return timezone(timedelta(minutes=minutes)) + + +@pytest.mark.parametrize( + "value,result", + [ + # Valid inputs + ("1494012444.883309", date(2017, 5, 5)), + (b"1494012444.883309", date(2017, 5, 5)), + (1_494_012_444.883_309, date(2017, 5, 5)), + ("1494012444", date(2017, 5, 5)), + (1_494_012_444, date(2017, 5, 5)), + (0, date(1970, 1, 1)), + ("2012-04-23", date(2012, 4, 23)), + (b"2012-04-23", date(2012, 4, 23)), + ("2012-4-9", date(2012, 4, 9)), + (date(2012, 4, 9), date(2012, 4, 9)), + (datetime(2012, 4, 9, 12, 15), date(2012, 4, 9)), + # Invalid inputs + ("x20120423", ValueError), + ("2012-04-56", ValueError), + (19_999_999_999, date(2603, 10, 11)), # just before watershed + (20_000_000_001, date(1970, 8, 20)), # just after watershed + (1_549_316_052, date(2019, 2, 4)), # nowish in s + (1_549_316_052_104, date(2019, 2, 4)), # nowish in ms + (1_549_316_052_104_324, date(2019, 2, 4)), # nowish in ฮผs + (1_549_316_052_104_324_096, date(2019, 2, 4)), # nowish in ns + ("infinity", date(9999, 12, 31)), + ("inf", date(9999, 12, 31)), + (float("inf"), date(9999, 12, 31)), + ("infinity ", date(9999, 12, 31)), + (int("1" + "0" * 100), date(9999, 12, 31)), + (1e1000, date(9999, 12, 31)), + ("-infinity", date(1, 1, 1)), + ("-inf", date(1, 1, 1)), + ("nan", ValueError), + ], +) +def test_date_parsing(value: Union[str, bytes, int, float], result: Union[date, Type[Exception]]) -> None: + if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance] + with pytest.raises(result): + parse_date(value) + else: + assert parse_date(value) == result + + +@pytest.mark.parametrize( + "value,result", + [ + # Valid inputs + # values in seconds + ("1494012444.883309", datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)), + (1_494_012_444.883_309, datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)), + ("1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + (b"1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + (1_494_012_444, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + # values in ms + ("1494012444000.883309", datetime(2017, 5, 5, 19, 27, 24, 883, tzinfo=timezone.utc)), + ("-1494012444000.883309", datetime(1922, 8, 29, 4, 32, 35, 999117, tzinfo=timezone.utc)), + (1_494_012_444_000, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + ("2012-04-23T09:15:00", datetime(2012, 4, 23, 9, 15)), + ("2012-4-9 4:8:16", datetime(2012, 4, 9, 4, 8, 16)), + ("2012-04-23T09:15:00Z", datetime(2012, 4, 23, 9, 15, 0, 0, timezone.utc)), + ("2012-4-9 4:8:16-0320", datetime(2012, 4, 9, 4, 8, 16, 0, create_tz(-200))), + ("2012-04-23T10:20:30.400+02:30", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(150))), + ("2012-04-23T10:20:30.400+02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(120))), + ("2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))), + (b"2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))), + (datetime(2017, 5, 5), datetime(2017, 5, 5)), + (0, datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc)), + # Invalid inputs + ("x20120423091500", ValueError), + ("2012-04-56T09:15:90", ValueError), + ("2012-04-23T11:05:00-25:00", ValueError), + (19_999_999_999, datetime(2603, 10, 11, 11, 33, 19, tzinfo=timezone.utc)), # just before watershed + (20_000_000_001, datetime(1970, 8, 20, 11, 33, 20, 1000, tzinfo=timezone.utc)), # just after watershed + (1_549_316_052, datetime(2019, 2, 4, 21, 34, 12, 0, tzinfo=timezone.utc)), # nowish in s + (1_549_316_052_104, datetime(2019, 2, 4, 21, 34, 12, 104_000, tzinfo=timezone.utc)), # nowish in ms + (1_549_316_052_104_324, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in ฮผs + (1_549_316_052_104_324_096, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in ns + ("infinity", datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("inf", datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("inf ", datetime(9999, 12, 31, 23, 59, 59, 999999)), + (1e50, datetime(9999, 12, 31, 23, 59, 59, 999999)), + (float("inf"), datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("-infinity", datetime(1, 1, 1, 0, 0)), + ("-inf", datetime(1, 1, 1, 0, 0)), + ("nan", ValueError), + ], +) +def test_datetime_parsing(value: Union[str, bytes, int, float], result: Union[datetime, Type[Exception]]) -> None: + if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance] + with pytest.raises(result): + parse_datetime(value) + else: + assert parse_datetime(value) == result diff --git a/tests/test_utils/test_proxy.py b/tests/test_utils/test_proxy.py index f09915b15..b55f8b0b7 100644 --- a/tests/test_utils/test_proxy.py +++ b/tests/test_utils/test_proxy.py @@ -21,3 +21,14 @@ def test_recursive_proxy() -> None: assert dir(proxy) == [] assert type(proxy).__name__ == "RecursiveLazyProxy" assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy" + + +def test_isinstance_does_not_error() -> None: + class AlwaysErrorProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + raise RuntimeError("Mocking missing dependency") + + proxy = AlwaysErrorProxy() + assert not isinstance(proxy, dict) + assert isinstance(proxy, LazyProxy) diff --git a/tests/test_workflow.py b/tests/test_workflow.py deleted file mode 100755 index 5c84fb39f..000000000 --- a/tests/test_workflow.py +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env python3 -"""Test script to validate the Hanzo workflow is properly configured.""" - -import os -import sys -import subprocess -from pathlib import Path - -import pytest - - -def check_environment(): - """Check required environment variables.""" - required = { - "ANTHROPIC_API_KEY": "Anthropic API key for Claude models", - "OPENAI_API_KEY": "OpenAI API key (optional but recommended)", - } - - optional = { - "HANZO_API_KEY": "Hanzo Router API key", - "HANZO_DEFAULT_MODEL": "Default model selection", - "HANZO_ROUTER_URL": "Hanzo Router URL", - } - - missing = [] - for key in required: - if not os.environ.get(key): - missing.append(key) - - return len(missing) == 0, missing, optional - - -class TestEnvironment: - """Test environment configuration.""" - - @pytest.mark.skipif(not os.environ.get("ANTHROPIC_API_KEY"), reason="ANTHROPIC_API_KEY not set") - def test_anthropic_key_set(self): - """Test that ANTHROPIC_API_KEY is set.""" - assert os.environ.get("ANTHROPIC_API_KEY") is not None - - -class TestImports: - """Test that required packages can be imported.""" - - def test_hanzoai_import(self): - """Test hanzoai package import.""" - import hanzoai - - assert hanzoai is not None - - @pytest.mark.skip(reason="hanzo_mcp may not be installed in test environment") - def test_hanzo_mcp_import(self): - """Test hanzo_mcp package import.""" - import hanzo_mcp - - assert hanzo_mcp is not None - - @pytest.mark.skip(reason="hanzo_agents may not be installed in test environment") - def test_hanzo_agents_import(self): - """Test hanzo_agents package import.""" - import hanzo_agents - - assert hanzo_agents is not None - - @pytest.mark.skip(reason="hanzo_dev may not be installed in test environment") - def test_hanzo_dev_import(self): - """Test hanzo_dev package import.""" - import hanzo_dev - - assert hanzo_dev is not None - - -class TestBasicClient: - """Test basic Hanzo client functionality.""" - - def test_client_initialization(self): - """Test client can be initialized with API key.""" - from hanzoai import Hanzo - - # Use a dummy API key for testing - this only tests client instantiation - # not actual API connectivity - client = Hanzo(api_key="test-api-key-for-unit-tests") - assert client is not None - - @pytest.mark.skipif(not os.environ.get("HANZO_API_KEY"), reason="HANZO_API_KEY not set") - def test_client_initialization_from_env(self): - """Test client can be initialized from environment.""" - from hanzoai import Hanzo - - client = Hanzo() - assert client is not None - - def test_model_list_available(self): - """Test that model list is accessible.""" - # Static list of known models - models = ["claude-3-opus-20240229", "claude-3-5-sonnet-20241022", "gpt-4"] - assert len(models) >= 3 - - -class TestCLI: - """Test CLI command availability.""" - - def test_hanzo_cli_help(self): - """Test hanzo CLI --help works.""" - result = subprocess.run(["python", "-m", "hanzo", "--help"], capture_output=True, text=True, timeout=10) - # Allow both success and some specific error codes - assert result.returncode in [0, 1, 2] - - -class TestCompletion: - """Test completion functionality.""" - - @pytest.mark.skip(reason="Completion test requires live API access - run manually") - def test_simple_completion(self): - """Test a simple completion if API key is available.""" - # This test requires live API access and should be run manually - # when validating the full workflow - pass - - -if __name__ == "__main__": - # When run as script, use pytest - sys.exit(pytest.main([__file__, "-v"])) diff --git a/tests/utils.py b/tests/utils.py index 9434cf5b9..c9e2e5b3a 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -4,7 +4,7 @@ import inspect import traceback import contextlib -from typing import Any, TypeVar, Iterator, cast +from typing import Any, TypeVar, Iterator, Sequence, cast from datetime import date, datetime from typing_extensions import Literal, get_args, get_origin, assert_type @@ -15,10 +15,11 @@ is_list_type, is_union_type, extract_type_arg, + is_sequence_type, is_annotated_type, is_type_alias_type, ) -from hanzoai._compat import PYDANTIC_V2, field_outer_type, get_model_fields +from hanzoai._compat import PYDANTIC_V1, field_outer_type, get_model_fields from hanzoai._models import BaseModel BaseModelT = TypeVar("BaseModelT", bound=BaseModel) @@ -27,12 +28,12 @@ def assert_matches_model(model: type[BaseModelT], value: BaseModelT, *, path: list[str]) -> bool: for name, field in get_model_fields(model).items(): field_value = getattr(value, name) - if PYDANTIC_V2: - allow_none = False - else: + if PYDANTIC_V1: # in v1 nullability was structured differently # https://docs.pydantic.dev/2.0/migration/#required-optional-and-nullable-fields allow_none = getattr(field, "allow_none", False) + else: + allow_none = False assert_matches_type( field_outer_type(field), @@ -71,6 +72,13 @@ def assert_matches_type( if is_list_type(type_): return _assert_list_type(type_, value) + if is_sequence_type(type_): + assert isinstance(value, Sequence) + inner_type = get_args(type_)[0] + for entry in value: # type: ignore + assert_type(inner_type, entry) # type: ignore + return + if origin == str: assert isinstance(value, str) elif origin == int: diff --git a/uv.lock b/uv.lock deleted file mode 100644 index 5e3364888..000000000 --- a/uv.lock +++ /dev/null @@ -1,4354 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" -resolution-markers = [ - "python_full_version >= '3.14'", - "python_full_version == '3.13.*'", - "python_full_version < '3.13'", -] - -[manifest] -members = [ - "hanzo-memory", - "hanzo-s3", - "hanzo-tools-ui", - "hanzoai", -] - -[[package]] -name = "aiofile" -version = "3.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "caio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/67/e2/d7cb819de8df6b5c1968a2756c3cb4122d4fa2b8fc768b53b7c9e5edb646/aiofile-3.9.0.tar.gz", hash = "sha256:e5ad718bb148b265b6df1b3752c4d1d83024b93da9bd599df74b9d9ffcf7919b", size = 17943, upload-time = "2024-10-08T10:39:35.846Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/50/25/da1f0b4dd970e52bf5a36c204c107e11a0c6d3ed195eba0bfbc664c312b2/aiofile-3.9.0-py3-none-any.whl", hash = "sha256:ce2f6c1571538cbdfa0143b04e16b208ecb0e9cb4148e528af8a640ed51cc8aa", size = 19539, upload-time = "2024-10-08T10:39:32.955Z" }, -] - -[[package]] -name = "aiofiles" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/c3/534eac40372d8ee36ef40df62ec129bee4fdb5ad9706e58a29be53b2c970/aiofiles-25.1.0.tar.gz", hash = "sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2", size = 46354, upload-time = "2025-10-09T20:51:04.358Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/8a/340a1555ae33d7354dbca4faa54948d76d89a27ceef032c8c3bc661d003e/aiofiles-25.1.0-py3-none-any.whl", hash = "sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695", size = 14668, upload-time = "2025-10-09T20:51:03.174Z" }, -] - -[[package]] -name = "aiohappyeyeballs" -version = "2.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, -] - -[[package]] -name = "aiohttp" -version = "3.13.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohappyeyeballs" }, - { name = "aiosignal" }, - { name = "attrs" }, - { name = "frozenlist" }, - { name = "multidict" }, - { name = "propcache" }, - { name = "yarl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732, upload-time = "2026-01-03T17:30:14.23Z" }, - { url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293, upload-time = "2026-01-03T17:30:15.96Z" }, - { url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533, upload-time = "2026-01-03T17:30:17.431Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839, upload-time = "2026-01-03T17:30:19.422Z" }, - { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932, upload-time = "2026-01-03T17:30:21.756Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906, upload-time = "2026-01-03T17:30:23.932Z" }, - { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020, upload-time = "2026-01-03T17:30:26Z" }, - { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181, upload-time = "2026-01-03T17:30:27.554Z" }, - { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794, upload-time = "2026-01-03T17:30:29.254Z" }, - { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900, upload-time = "2026-01-03T17:30:31.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239, upload-time = "2026-01-03T17:30:32.703Z" }, - { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527, upload-time = "2026-01-03T17:30:34.695Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489, upload-time = "2026-01-03T17:30:36.864Z" }, - { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852, upload-time = "2026-01-03T17:30:39.433Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379, upload-time = "2026-01-03T17:30:41.081Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4a/1a3fee7c21350cac78e5c5cef711bac1b94feca07399f3d406972e2d8fcd/aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", size = 428253, upload-time = "2026-01-03T17:30:42.644Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b7/76175c7cb4eb73d91ad63c34e29fc4f77c9386bba4a65b53ba8e05ee3c39/aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", size = 455407, upload-time = "2026-01-03T17:30:44.195Z" }, - { url = "https://files.pythonhosted.org/packages/97/8a/12ca489246ca1faaf5432844adbfce7ff2cc4997733e0af120869345643a/aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", size = 734190, upload-time = "2026-01-03T17:30:45.832Z" }, - { url = "https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", size = 491783, upload-time = "2026-01-03T17:30:47.466Z" }, - { url = "https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", size = 490704, upload-time = "2026-01-03T17:30:49.373Z" }, - { url = "https://files.pythonhosted.org/packages/6d/40/a46b03ca03936f832bc7eaa47cfbb1ad012ba1be4790122ee4f4f8cba074/aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", size = 1720652, upload-time = "2026-01-03T17:30:50.974Z" }, - { url = "https://files.pythonhosted.org/packages/f7/7e/917fe18e3607af92657e4285498f500dca797ff8c918bd7d90b05abf6c2a/aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", size = 1692014, upload-time = "2026-01-03T17:30:52.729Z" }, - { url = "https://files.pythonhosted.org/packages/71/b6/cefa4cbc00d315d68973b671cf105b21a609c12b82d52e5d0c9ae61d2a09/aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", size = 1759777, upload-time = "2026-01-03T17:30:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e3/e06ee07b45e59e6d81498b591fc589629be1553abb2a82ce33efe2a7b068/aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", size = 1861276, upload-time = "2026-01-03T17:30:56.512Z" }, - { url = "https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", size = 1743131, upload-time = "2026-01-03T17:30:58.256Z" }, - { url = "https://files.pythonhosted.org/packages/04/98/3d21dde21889b17ca2eea54fdcff21b27b93f45b7bb94ca029c31ab59dc3/aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", size = 1556863, upload-time = "2026-01-03T17:31:00.445Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/da0c3ab1192eaf64782b03971ab4055b475d0db07b17eff925e8c93b3aa5/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", size = 1682793, upload-time = "2026-01-03T17:31:03.024Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0f/5802ada182f575afa02cbd0ec5180d7e13a402afb7c2c03a9aa5e5d49060/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", size = 1716676, upload-time = "2026-01-03T17:31:04.842Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8c/714d53bd8b5a4560667f7bbbb06b20c2382f9c7847d198370ec6526af39c/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", size = 1733217, upload-time = "2026-01-03T17:31:06.868Z" }, - { url = "https://files.pythonhosted.org/packages/7d/79/e2176f46d2e963facea939f5be2d26368ce543622be6f00a12844d3c991f/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", size = 1552303, upload-time = "2026-01-03T17:31:08.958Z" }, - { url = "https://files.pythonhosted.org/packages/ab/6a/28ed4dea1759916090587d1fe57087b03e6c784a642b85ef48217b0277ae/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", size = 1763673, upload-time = "2026-01-03T17:31:10.676Z" }, - { url = "https://files.pythonhosted.org/packages/e8/35/4a3daeb8b9fab49240d21c04d50732313295e4bd813a465d840236dd0ce1/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", size = 1721120, upload-time = "2026-01-03T17:31:12.575Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9f/d643bb3c5fb99547323e635e251c609fbbc660d983144cfebec529e09264/aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", size = 427383, upload-time = "2026-01-03T17:31:14.382Z" }, - { url = "https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", size = 453899, upload-time = "2026-01-03T17:31:15.958Z" }, - { url = "https://files.pythonhosted.org/packages/99/36/5b6514a9f5d66f4e2597e40dea2e3db271e023eb7a5d22defe96ba560996/aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808", size = 737238, upload-time = "2026-01-03T17:31:17.909Z" }, - { url = "https://files.pythonhosted.org/packages/f7/49/459327f0d5bcd8c6c9ca69e60fdeebc3622861e696490d8674a6d0cb90a6/aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415", size = 492292, upload-time = "2026-01-03T17:31:19.919Z" }, - { url = "https://files.pythonhosted.org/packages/e8/0b/b97660c5fd05d3495b4eb27f2d0ef18dc1dc4eff7511a9bf371397ff0264/aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f", size = 493021, upload-time = "2026-01-03T17:31:21.636Z" }, - { url = "https://files.pythonhosted.org/packages/54/d4/438efabdf74e30aeceb890c3290bbaa449780583b1270b00661126b8aae4/aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6", size = 1717263, upload-time = "2026-01-03T17:31:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/71/f2/7bddc7fd612367d1459c5bcf598a9e8f7092d6580d98de0e057eb42697ad/aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687", size = 1669107, upload-time = "2026-01-03T17:31:25.334Z" }, - { url = "https://files.pythonhosted.org/packages/00/5a/1aeaecca40e22560f97610a329e0e5efef5e0b5afdf9f857f0d93839ab2e/aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26", size = 1760196, upload-time = "2026-01-03T17:31:27.394Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f8/0ff6992bea7bd560fc510ea1c815f87eedd745fe035589c71ce05612a19a/aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a", size = 1843591, upload-time = "2026-01-03T17:31:29.238Z" }, - { url = "https://files.pythonhosted.org/packages/e3/d1/e30e537a15f53485b61f5be525f2157da719819e8377298502aebac45536/aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1", size = 1720277, upload-time = "2026-01-03T17:31:31.053Z" }, - { url = "https://files.pythonhosted.org/packages/84/45/23f4c451d8192f553d38d838831ebbc156907ea6e05557f39563101b7717/aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25", size = 1548575, upload-time = "2026-01-03T17:31:32.87Z" }, - { url = "https://files.pythonhosted.org/packages/6a/ed/0a42b127a43712eda7807e7892c083eadfaf8429ca8fb619662a530a3aab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603", size = 1679455, upload-time = "2026-01-03T17:31:34.76Z" }, - { url = "https://files.pythonhosted.org/packages/2e/b5/c05f0c2b4b4fe2c9d55e73b6d3ed4fd6c9dc2684b1d81cbdf77e7fad9adb/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a", size = 1687417, upload-time = "2026-01-03T17:31:36.699Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6b/915bc5dad66aef602b9e459b5a973529304d4e89ca86999d9d75d80cbd0b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926", size = 1729968, upload-time = "2026-01-03T17:31:38.622Z" }, - { url = "https://files.pythonhosted.org/packages/11/3b/e84581290a9520024a08640b63d07673057aec5ca548177a82026187ba73/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba", size = 1545690, upload-time = "2026-01-03T17:31:40.57Z" }, - { url = "https://files.pythonhosted.org/packages/f5/04/0c3655a566c43fd647c81b895dfe361b9f9ad6d58c19309d45cff52d6c3b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c", size = 1746390, upload-time = "2026-01-03T17:31:42.857Z" }, - { url = "https://files.pythonhosted.org/packages/1f/53/71165b26978f719c3419381514c9690bd5980e764a09440a10bb816ea4ab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43", size = 1702188, upload-time = "2026-01-03T17:31:44.984Z" }, - { url = "https://files.pythonhosted.org/packages/29/a7/cbe6c9e8e136314fa1980da388a59d2f35f35395948a08b6747baebb6aa6/aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1", size = 433126, upload-time = "2026-01-03T17:31:47.463Z" }, - { url = "https://files.pythonhosted.org/packages/de/56/982704adea7d3b16614fc5936014e9af85c0e34b58f9046655817f04306e/aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984", size = 459128, upload-time = "2026-01-03T17:31:49.2Z" }, - { url = "https://files.pythonhosted.org/packages/6c/2a/3c79b638a9c3d4658d345339d22070241ea341ed4e07b5ac60fb0f418003/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c", size = 769512, upload-time = "2026-01-03T17:31:51.134Z" }, - { url = "https://files.pythonhosted.org/packages/29/b9/3e5014d46c0ab0db8707e0ac2711ed28c4da0218c358a4e7c17bae0d8722/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592", size = 506444, upload-time = "2026-01-03T17:31:52.85Z" }, - { url = "https://files.pythonhosted.org/packages/90/03/c1d4ef9a054e151cd7839cdc497f2638f00b93cbe8043983986630d7a80c/aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f", size = 510798, upload-time = "2026-01-03T17:31:54.91Z" }, - { url = "https://files.pythonhosted.org/packages/ea/76/8c1e5abbfe8e127c893fe7ead569148a4d5a799f7cf958d8c09f3eedf097/aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29", size = 1868835, upload-time = "2026-01-03T17:31:56.733Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ac/984c5a6f74c363b01ff97adc96a3976d9c98940b8969a1881575b279ac5d/aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc", size = 1720486, upload-time = "2026-01-03T17:31:58.65Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9a/b7039c5f099c4eb632138728828b33428585031a1e658d693d41d07d89d1/aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2", size = 1847951, upload-time = "2026-01-03T17:32:00.989Z" }, - { url = "https://files.pythonhosted.org/packages/3c/02/3bec2b9a1ba3c19ff89a43a19324202b8eb187ca1e928d8bdac9bbdddebd/aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587", size = 1941001, upload-time = "2026-01-03T17:32:03.122Z" }, - { url = "https://files.pythonhosted.org/packages/37/df/d879401cedeef27ac4717f6426c8c36c3091c6e9f08a9178cc87549c537f/aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8", size = 1797246, upload-time = "2026-01-03T17:32:05.255Z" }, - { url = "https://files.pythonhosted.org/packages/8d/15/be122de1f67e6953add23335c8ece6d314ab67c8bebb3f181063010795a7/aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632", size = 1627131, upload-time = "2026-01-03T17:32:07.607Z" }, - { url = "https://files.pythonhosted.org/packages/12/12/70eedcac9134cfa3219ab7af31ea56bc877395b1ac30d65b1bc4b27d0438/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64", size = 1795196, upload-time = "2026-01-03T17:32:09.59Z" }, - { url = "https://files.pythonhosted.org/packages/32/11/b30e1b1cd1f3054af86ebe60df96989c6a414dd87e27ad16950eee420bea/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0", size = 1782841, upload-time = "2026-01-03T17:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/88/0d/d98a9367b38912384a17e287850f5695c528cff0f14f791ce8ee2e4f7796/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56", size = 1795193, upload-time = "2026-01-03T17:32:13.705Z" }, - { url = "https://files.pythonhosted.org/packages/43/a5/a2dfd1f5ff5581632c7f6a30e1744deda03808974f94f6534241ef60c751/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72", size = 1621979, upload-time = "2026-01-03T17:32:15.965Z" }, - { url = "https://files.pythonhosted.org/packages/fa/f0/12973c382ae7c1cccbc4417e129c5bf54c374dfb85af70893646e1f0e749/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df", size = 1822193, upload-time = "2026-01-03T17:32:18.219Z" }, - { url = "https://files.pythonhosted.org/packages/3c/5f/24155e30ba7f8c96918af1350eb0663e2430aad9e001c0489d89cd708ab1/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", size = 1769801, upload-time = "2026-01-03T17:32:20.25Z" }, - { url = "https://files.pythonhosted.org/packages/eb/f8/7314031ff5c10e6ece114da79b338ec17eeff3a079e53151f7e9f43c4723/aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", size = 466523, upload-time = "2026-01-03T17:32:22.215Z" }, - { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694, upload-time = "2026-01-03T17:32:24.546Z" }, -] - -[[package]] -name = "aiosignal" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "frozenlist" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, -] - -[[package]] -name = "annotated-doc" -version = "0.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, -] - -[[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.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, -] - -[[package]] -name = "argcomplete" -version = "3.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/38/61/0b9ae6399dd4a58d8c1b1dc5a27d6f2808023d0b5dd3104bb99f45a33ff6/argcomplete-3.6.3.tar.gz", hash = "sha256:62e8ed4fd6a45864acc8235409461b72c9a28ee785a2011cc5eb78318786c89c", size = 73754, upload-time = "2025-10-20T03:33:34.741Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/f5/9373290775639cb67a2fce7f629a1c240dce9f12fe927bc32b2736e16dfc/argcomplete-3.6.3-py3-none-any.whl", hash = "sha256:f5007b3a600ccac5d25bbce33089211dfd49eab4a7718da3f10e3082525a92ce", size = 43846, upload-time = "2025-10-20T03:33:33.021Z" }, -] - -[[package]] -name = "argon2-cffi" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "argon2-cffi-bindings" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" }, -] - -[[package]] -name = "argon2-cffi-bindings" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441, upload-time = "2025-07-30T10:02:05.147Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/3c0a35f46e52108d4707c44b95cfe2afcafc50800b5450c197454569b776/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f", size = 54393, upload-time = "2025-07-30T10:01:40.97Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f4/98bbd6ee89febd4f212696f13c03ca302b8552e7dbf9c8efa11ea4a388c3/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b", size = 29328, upload-time = "2025-07-30T10:01:41.916Z" }, - { url = "https://files.pythonhosted.org/packages/43/24/90a01c0ef12ac91a6be05969f29944643bc1e5e461155ae6559befa8f00b/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a", size = 31269, upload-time = "2025-07-30T10:01:42.716Z" }, - { url = "https://files.pythonhosted.org/packages/d4/d3/942aa10782b2697eee7af5e12eeff5ebb325ccfb86dd8abda54174e377e4/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44", size = 86558, upload-time = "2025-07-30T10:01:43.943Z" }, - { url = "https://files.pythonhosted.org/packages/0d/82/b484f702fec5536e71836fc2dbc8c5267b3f6e78d2d539b4eaa6f0db8bf8/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb", size = 92364, upload-time = "2025-07-30T10:01:44.887Z" }, - { url = "https://files.pythonhosted.org/packages/c9/c1/a606ff83b3f1735f3759ad0f2cd9e038a0ad11a3de3b6c673aa41c24bb7b/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92", size = 85637, upload-time = "2025-07-30T10:01:46.225Z" }, - { url = "https://files.pythonhosted.org/packages/44/b4/678503f12aceb0262f84fa201f6027ed77d71c5019ae03b399b97caa2f19/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85", size = 91934, upload-time = "2025-07-30T10:01:47.203Z" }, - { url = "https://files.pythonhosted.org/packages/f0/c7/f36bd08ef9bd9f0a9cff9428406651f5937ce27b6c5b07b92d41f91ae541/argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f", size = 28158, upload-time = "2025-07-30T10:01:48.341Z" }, - { url = "https://files.pythonhosted.org/packages/b3/80/0106a7448abb24a2c467bf7d527fe5413b7fdfa4ad6d6a96a43a62ef3988/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6", size = 32597, upload-time = "2025-07-30T10:01:49.112Z" }, - { url = "https://files.pythonhosted.org/packages/05/b8/d663c9caea07e9180b2cb662772865230715cbd573ba3b5e81793d580316/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623", size = 28231, upload-time = "2025-07-30T10:01:49.92Z" }, - { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121, upload-time = "2025-07-30T10:01:50.815Z" }, - { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177, upload-time = "2025-07-30T10:01:51.681Z" }, - { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090, upload-time = "2025-07-30T10:01:53.184Z" }, - { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246, upload-time = "2025-07-30T10:01:54.145Z" }, - { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126, upload-time = "2025-07-30T10:01:55.074Z" }, - { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343, upload-time = "2025-07-30T10:01:56.007Z" }, - { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777, upload-time = "2025-07-30T10:01:56.943Z" }, - { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180, upload-time = "2025-07-30T10:01:57.759Z" }, - { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715, upload-time = "2025-07-30T10:01:58.56Z" }, - { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149, upload-time = "2025-07-30T10:01:59.329Z" }, -] - -[[package]] -name = "asttokens" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, -] - -[[package]] -name = "attrs" -version = "26.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, -] - -[[package]] -name = "authlib" -version = "1.6.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/af/98/00d3dd826d46959ad8e32af2dbb2398868fd9fd0683c26e56d0789bd0e68/authlib-1.6.9.tar.gz", hash = "sha256:d8f2421e7e5980cc1ddb4e32d3f5fa659cfaf60d8eaf3281ebed192e4ab74f04", size = 165134, upload-time = "2026-03-02T07:44:01.998Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/23/b65f568ed0c22f1efacb744d2db1a33c8068f384b8c9b482b52ebdbc3ef6/authlib-1.6.9-py2.py3-none-any.whl", hash = "sha256:f08b4c14e08f0861dc18a32357b33fbcfd2ea86cfe3fe149484b4d764c4a0ac3", size = 244197, upload-time = "2026-03-02T07:44:00.307Z" }, -] - -[[package]] -name = "babel" -version = "2.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, -] - -[[package]] -name = "backrefs" -version = "6.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/a6/e325ec73b638d3ede4421b5445d4a0b8b219481826cc079d510100af356c/backrefs-6.2.tar.gz", hash = "sha256:f44ff4d48808b243b6c0cdc6231e22195c32f77046018141556c66f8bab72a49", size = 7012303, upload-time = "2026-02-16T19:10:15.828Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/39/3765df263e08a4df37f4f43cb5aa3c6c17a4bdd42ecfe841e04c26037171/backrefs-6.2-py310-none-any.whl", hash = "sha256:0fdc7b012420b6b144410342caeb8adc54c6866cf12064abc9bb211302e496f8", size = 381075, upload-time = "2026-02-16T19:10:04.322Z" }, - { url = "https://files.pythonhosted.org/packages/0f/f0/35240571e1b67ffb19dafb29ab34150b6f59f93f717b041082cdb1bfceb1/backrefs-6.2-py311-none-any.whl", hash = "sha256:08aa7fae530c6b2361d7bdcbda1a7c454e330cc9dbcd03f5c23205e430e5c3be", size = 392874, upload-time = "2026-02-16T19:10:06.314Z" }, - { url = "https://files.pythonhosted.org/packages/e3/63/77e8c9745b4d227cce9f5e0a6f68041278c5f9b18588b35905f5f19c1beb/backrefs-6.2-py312-none-any.whl", hash = "sha256:c3f4b9cb2af8cda0d87ab4f57800b57b95428488477be164dd2b47be54db0c90", size = 398787, upload-time = "2026-02-16T19:10:08.274Z" }, - { url = "https://files.pythonhosted.org/packages/c5/71/c754b1737ad99102e03fa3235acb6cb6d3ac9d6f596cbc3e5f236705abd8/backrefs-6.2-py313-none-any.whl", hash = "sha256:12df81596ab511f783b7d87c043ce26bc5b0288cf3bb03610fe76b8189282b2b", size = 400747, upload-time = "2026-02-16T19:10:09.791Z" }, - { url = "https://files.pythonhosted.org/packages/af/75/be12ba31a6eb20dccef2320cd8ccb3f7d9013b68ba4c70156259fee9e409/backrefs-6.2-py314-none-any.whl", hash = "sha256:e5f805ae09819caa1aa0623b4a83790e7028604aa2b8c73ba602c4454e665de7", size = 412602, upload-time = "2026-02-16T19:10:12.317Z" }, - { url = "https://files.pythonhosted.org/packages/21/f8/d02f650c47d05034dcd6f9c8cf94f39598b7a89c00ecda0ecb2911bc27e9/backrefs-6.2-py39-none-any.whl", hash = "sha256:664e33cd88c6840b7625b826ecf2555f32d491800900f5a541f772c485f7cda7", size = 381077, upload-time = "2026-02-16T19:10:13.74Z" }, -] - -[[package]] -name = "beartype" -version = "0.22.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, -] - -[[package]] -name = "black" -version = "26.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "mypy-extensions" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "platformdirs" }, - { name = "pytokens" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e1/c5/61175d618685d42b005847464b8fb4743a67b1b8fdb75e50e5a96c31a27a/black-26.3.1.tar.gz", hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07", size = 666155, upload-time = "2026-03-12T03:36:03.593Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/f8/da5eae4fc75e78e6dceb60624e1b9662ab00d6b452996046dfa9b8a6025b/black-26.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e6f89631eb88a7302d416594a32faeee9fb8fb848290da9d0a5f2903519fc1", size = 1895920, upload-time = "2026-03-12T03:40:13.921Z" }, - { url = "https://files.pythonhosted.org/packages/2c/9f/04e6f26534da2e1629b2b48255c264cabf5eedc5141d04516d9d68a24111/black-26.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cd2012d35b47d589cb8a16faf8a32ef7a336f56356babd9fcf70939ad1897f", size = 1718499, upload-time = "2026-03-12T03:40:15.239Z" }, - { url = "https://files.pythonhosted.org/packages/04/91/a5935b2a63e31b331060c4a9fdb5a6c725840858c599032a6f3aac94055f/black-26.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f76ff19ec5297dd8e66eb64deda23631e642c9393ab592826fd4bdc97a4bce7", size = 1794994, upload-time = "2026-03-12T03:40:17.124Z" }, - { url = "https://files.pythonhosted.org/packages/e7/0a/86e462cdd311a3c2a8ece708d22aba17d0b2a0d5348ca34b40cdcbea512e/black-26.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ddb113db38838eb9f043623ba274cfaf7d51d5b0c22ecb30afe58b1bb8322983", size = 1420867, upload-time = "2026-03-12T03:40:18.83Z" }, - { url = "https://files.pythonhosted.org/packages/5b/e5/22515a19cb7eaee3440325a6b0d95d2c0e88dd180cb011b12ae488e031d1/black-26.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:dfdd51fc3e64ea4f35873d1b3fb25326773d55d2329ff8449139ebaad7357efb", size = 1230124, upload-time = "2026-03-12T03:40:20.425Z" }, - { url = "https://files.pythonhosted.org/packages/f5/77/5728052a3c0450c53d9bb3945c4c46b91baa62b2cafab6801411b6271e45/black-26.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:855822d90f884905362f602880ed8b5df1b7e3ee7d0db2502d4388a954cc8c54", size = 1895034, upload-time = "2026-03-12T03:40:21.813Z" }, - { url = "https://files.pythonhosted.org/packages/52/73/7cae55fdfdfbe9d19e9a8d25d145018965fe2079fa908101c3733b0c55a0/black-26.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8a33d657f3276328ce00e4d37fe70361e1ec7614da5d7b6e78de5426cb56332f", size = 1718503, upload-time = "2026-03-12T03:40:23.666Z" }, - { url = "https://files.pythonhosted.org/packages/e1/87/af89ad449e8254fdbc74654e6467e3c9381b61472cc532ee350d28cfdafb/black-26.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1cd08e99d2f9317292a311dfe578fd2a24b15dbce97792f9c4d752275c1fa56", size = 1793557, upload-time = "2026-03-12T03:40:25.497Z" }, - { url = "https://files.pythonhosted.org/packages/43/10/d6c06a791d8124b843bf325ab4ac7d2f5b98731dff84d6064eafd687ded1/black-26.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:c7e72339f841b5a237ff14f7d3880ddd0fc7f98a1199e8c4327f9a4f478c1839", size = 1422766, upload-time = "2026-03-12T03:40:27.14Z" }, - { url = "https://files.pythonhosted.org/packages/59/4f/40a582c015f2d841ac24fed6390bd68f0fc896069ff3a886317959c9daf8/black-26.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc622538b430aa4c8c853f7f63bc582b3b8030fd8c80b70fb5fa5b834e575c2", size = 1232140, upload-time = "2026-03-12T03:40:28.882Z" }, - { url = "https://files.pythonhosted.org/packages/d5/da/e36e27c9cebc1311b7579210df6f1c86e50f2d7143ae4fcf8a5017dc8809/black-26.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2d6bfaf7fd0993b420bed691f20f9492d53ce9a2bcccea4b797d34e947318a78", size = 1889234, upload-time = "2026-03-12T03:40:30.964Z" }, - { url = "https://files.pythonhosted.org/packages/0e/7b/9871acf393f64a5fa33668c19350ca87177b181f44bb3d0c33b2d534f22c/black-26.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f89f2ab047c76a9c03f78d0d66ca519e389519902fa27e7a91117ef7611c0568", size = 1720522, upload-time = "2026-03-12T03:40:32.346Z" }, - { url = "https://files.pythonhosted.org/packages/03/87/e766c7f2e90c07fb7586cc787c9ae6462b1eedab390191f2b7fc7f6170a9/black-26.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b07fc0dab849d24a80a29cfab8d8a19187d1c4685d8a5e6385a5ce323c1f015f", size = 1787824, upload-time = "2026-03-12T03:40:33.636Z" }, - { url = "https://files.pythonhosted.org/packages/ac/94/2424338fb2d1875e9e83eed4c8e9c67f6905ec25afd826a911aea2b02535/black-26.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:0126ae5b7c09957da2bdbd91a9ba1207453feada9e9fe51992848658c6c8e01c", size = 1445855, upload-time = "2026-03-12T03:40:35.442Z" }, - { url = "https://files.pythonhosted.org/packages/86/43/0c3338bd928afb8ee7471f1a4eec3bdbe2245ccb4a646092a222e8669840/black-26.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:92c0ec1f2cc149551a2b7b47efc32c866406b6891b0ee4625e95967c8f4acfb1", size = 1258109, upload-time = "2026-03-12T03:40:36.832Z" }, - { url = "https://files.pythonhosted.org/packages/8e/0d/52d98722666d6fc6c3dd4c76df339501d6efd40e0ff95e6186a7b7f0befd/black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b", size = 207542, upload-time = "2026-03-12T03:36:01.668Z" }, -] - -[[package]] -name = "build" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "os_name == 'nt'" }, - { name = "packaging" }, - { name = "pyproject-hooks" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/18/94eaffda7b329535d91f00fe605ab1f1e5cd68b2074d03f255c7d250687d/build-1.4.0.tar.gz", hash = "sha256:f1b91b925aa322be454f8330c6fb48b465da993d1e7e7e6fa35027ec49f3c936", size = 50054, upload-time = "2026-01-08T16:41:47.696Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl", hash = "sha256:6a07c1b8eb6f2b311b96fcbdbce5dab5fe637ffda0fd83c9cac622e927501596", size = 24141, upload-time = "2026-01-08T16:41:46.453Z" }, -] - -[[package]] -name = "cachetools" -version = "7.0.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/dd/57fe3fdb6e65b25a5987fd2cdc7e22db0aef508b91634d2e57d22928d41b/cachetools-7.0.5.tar.gz", hash = "sha256:0cd042c24377200c1dcd225f8b7b12b0ca53cc2c961b43757e774ebe190fd990", size = 37367, upload-time = "2026-03-09T20:51:29.451Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/06/f3/39cf3367b8107baa44f861dc802cbf16263c945b62d8265d36034fc07bea/cachetools-7.0.5-py3-none-any.whl", hash = "sha256:46bc8ebefbe485407621d0a4264b23c080cedd913921bad7ac3ed2f26c183114", size = 13918, upload-time = "2026-03-09T20:51:27.33Z" }, -] - -[[package]] -name = "caio" -version = "0.9.25" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/92/88/b8527e1b00c1811db339a1df8bd1ae49d146fcea9d6a5c40e3a80aaeb38d/caio-0.9.25.tar.gz", hash = "sha256:16498e7f81d1d0f5a4c0ad3f2540e65fe25691376e0a5bd367f558067113ed10", size = 26781, upload-time = "2025-12-26T15:21:36.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/25/79c98ebe12df31548ba4eaf44db11b7cad6b3e7b4203718335620939083c/caio-0.9.25-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fb7ff95af4c31ad3f03179149aab61097a71fd85e05f89b4786de0359dffd044", size = 36983, upload-time = "2025-12-26T15:21:36.075Z" }, - { url = "https://files.pythonhosted.org/packages/a3/2b/21288691f16d479945968a0a4f2856818c1c5be56881d51d4dac9b255d26/caio-0.9.25-cp312-cp312-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:97084e4e30dfa598449d874c4d8e0c8d5ea17d2f752ef5e48e150ff9d240cd64", size = 82012, upload-time = "2025-12-26T15:22:20.983Z" }, - { url = "https://files.pythonhosted.org/packages/03/c4/8a1b580875303500a9c12b9e0af58cb82e47f5bcf888c2457742a138273c/caio-0.9.25-cp312-cp312-manylinux_2_34_aarch64.whl", hash = "sha256:4fa69eba47e0f041b9d4f336e2ad40740681c43e686b18b191b6c5f4c5544bfb", size = 81502, upload-time = "2026-03-04T22:08:22.381Z" }, - { url = "https://files.pythonhosted.org/packages/d1/1c/0fe770b8ffc8362c48134d1592d653a81a3d8748d764bec33864db36319d/caio-0.9.25-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:6bebf6f079f1341d19f7386db9b8b1f07e8cc15ae13bfdaff573371ba0575d69", size = 80200, upload-time = "2026-03-04T22:08:23.382Z" }, - { url = "https://files.pythonhosted.org/packages/31/57/5e6ff127e6f62c9f15d989560435c642144aa4210882f9494204bc892305/caio-0.9.25-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d6c2a3411af97762a2b03840c3cec2f7f728921ff8adda53d7ea2315a8563451", size = 36979, upload-time = "2025-12-26T15:21:35.484Z" }, - { url = "https://files.pythonhosted.org/packages/a3/9f/f21af50e72117eb528c422d4276cbac11fb941b1b812b182e0a9c70d19c5/caio-0.9.25-cp313-cp313-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0998210a4d5cd5cb565b32ccfe4e53d67303f868a76f212e002a8554692870e6", size = 81900, upload-time = "2025-12-26T15:22:21.919Z" }, - { url = "https://files.pythonhosted.org/packages/9c/12/c39ae2a4037cb10ad5eb3578eb4d5f8c1a2575c62bba675f3406b7ef0824/caio-0.9.25-cp313-cp313-manylinux_2_34_aarch64.whl", hash = "sha256:1a177d4777141b96f175fe2c37a3d96dec7911ed9ad5f02bac38aaa1c936611f", size = 81523, upload-time = "2026-03-04T22:08:25.187Z" }, - { url = "https://files.pythonhosted.org/packages/22/59/f8f2e950eb4f1a5a3883e198dca514b9d475415cb6cd7b78b9213a0dd45a/caio-0.9.25-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:9ed3cfb28c0e99fec5e208c934e5c157d0866aa9c32aa4dc5e9b6034af6286b7", size = 80243, upload-time = "2026-03-04T22:08:26.449Z" }, - { url = "https://files.pythonhosted.org/packages/69/ca/a08fdc7efdcc24e6a6131a93c85be1f204d41c58f474c42b0670af8c016b/caio-0.9.25-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fab6078b9348e883c80a5e14b382e6ad6aabbc4429ca034e76e730cf464269db", size = 36978, upload-time = "2025-12-26T15:21:41.055Z" }, - { url = "https://files.pythonhosted.org/packages/5e/6c/d4d24f65e690213c097174d26eda6831f45f4734d9d036d81790a27e7b78/caio-0.9.25-cp314-cp314-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:44a6b58e52d488c75cfaa5ecaa404b2b41cc965e6c417e03251e868ecd5b6d77", size = 81832, upload-time = "2025-12-26T15:22:22.757Z" }, - { url = "https://files.pythonhosted.org/packages/87/a4/e534cf7d2d0e8d880e25dd61e8d921ffcfe15bd696734589826f5a2df727/caio-0.9.25-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:628a630eb7fb22381dd8e3c8ab7f59e854b9c806639811fc3f4310c6bd711d79", size = 81565, upload-time = "2026-03-04T22:08:27.483Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ed/bf81aeac1d290017e5e5ac3e880fd56ee15e50a6d0353986799d1bc5cfd5/caio-0.9.25-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:0ba16aa605ccb174665357fc729cf500679c2d94d5f1458a6f0d5ca48f2060a7", size = 80071, upload-time = "2026-03-04T22:08:28.751Z" }, - { url = "https://files.pythonhosted.org/packages/86/93/1f76c8d1bafe3b0614e06b2195784a3765bbf7b0a067661af9e2dd47fc33/caio-0.9.25-py3-none-any.whl", hash = "sha256:06c0bb02d6b929119b1cfbe1ca403c768b2013a369e2db46bfa2a5761cf82e40", size = 19087, upload-time = "2025-12-26T15:22:00.221Z" }, -] - -[[package]] -name = "certifi" -version = "2026.2.25" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "cfgv" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/60/e3bec1881450851b087e301bedc3daa9377a4d45f1c26aa90b0b235e38aa/charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", size = 143363, upload-time = "2026-03-15T18:53:25.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/62/c0815c992c9545347aeea7859b50dc9044d147e2e7278329c6e02ac9a616/charset_normalizer-3.4.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab", size = 295154, upload-time = "2026-03-15T18:50:50.88Z" }, - { url = "https://files.pythonhosted.org/packages/a8/37/bdca6613c2e3c58c7421891d80cc3efa1d32e882f7c4a7ee6039c3fc951a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21", size = 199191, upload-time = "2026-03-15T18:50:52.658Z" }, - { url = "https://files.pythonhosted.org/packages/6c/92/9934d1bbd69f7f398b38c5dae1cbf9cc672e7c34a4adf7b17c0a9c17d15d/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2", size = 218674, upload-time = "2026-03-15T18:50:54.102Z" }, - { url = "https://files.pythonhosted.org/packages/af/90/25f6ab406659286be929fd89ab0e78e38aa183fc374e03aa3c12d730af8a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff", size = 215259, upload-time = "2026-03-15T18:50:55.616Z" }, - { url = "https://files.pythonhosted.org/packages/4e/ef/79a463eb0fff7f96afa04c1d4c51f8fc85426f918db467854bfb6a569ce3/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5", size = 207276, upload-time = "2026-03-15T18:50:57.054Z" }, - { url = "https://files.pythonhosted.org/packages/f7/72/d0426afec4b71dc159fa6b4e68f868cd5a3ecd918fec5813a15d292a7d10/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0", size = 195161, upload-time = "2026-03-15T18:50:58.686Z" }, - { url = "https://files.pythonhosted.org/packages/bf/18/c82b06a68bfcb6ce55e508225d210c7e6a4ea122bfc0748892f3dc4e8e11/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a", size = 203452, upload-time = "2026-03-15T18:51:00.196Z" }, - { url = "https://files.pythonhosted.org/packages/44/d6/0c25979b92f8adafdbb946160348d8d44aa60ce99afdc27df524379875cb/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2", size = 202272, upload-time = "2026-03-15T18:51:01.703Z" }, - { url = "https://files.pythonhosted.org/packages/2e/3d/7fea3e8fe84136bebbac715dd1221cc25c173c57a699c030ab9b8900cbb7/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5", size = 195622, upload-time = "2026-03-15T18:51:03.526Z" }, - { url = "https://files.pythonhosted.org/packages/57/8a/d6f7fd5cb96c58ef2f681424fbca01264461336d2a7fc875e4446b1f1346/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6", size = 220056, upload-time = "2026-03-15T18:51:05.269Z" }, - { url = "https://files.pythonhosted.org/packages/16/50/478cdda782c8c9c3fb5da3cc72dd7f331f031e7f1363a893cdd6ca0f8de0/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d", size = 203751, upload-time = "2026-03-15T18:51:06.858Z" }, - { url = "https://files.pythonhosted.org/packages/75/fc/cc2fcac943939c8e4d8791abfa139f685e5150cae9f94b60f12520feaa9b/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2", size = 216563, upload-time = "2026-03-15T18:51:08.564Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b7/a4add1d9a5f68f3d037261aecca83abdb0ab15960a3591d340e829b37298/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923", size = 209265, upload-time = "2026-03-15T18:51:10.312Z" }, - { url = "https://files.pythonhosted.org/packages/6c/18/c094561b5d64a24277707698e54b7f67bd17a4f857bbfbb1072bba07c8bf/charset_normalizer-3.4.6-cp312-cp312-win32.whl", hash = "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4", size = 144229, upload-time = "2026-03-15T18:51:11.694Z" }, - { url = "https://files.pythonhosted.org/packages/ab/20/0567efb3a8fd481b8f34f739ebddc098ed062a59fed41a8d193a61939e8f/charset_normalizer-3.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb", size = 154277, upload-time = "2026-03-15T18:51:13.004Z" }, - { url = "https://files.pythonhosted.org/packages/15/57/28d79b44b51933119e21f65479d0864a8d5893e494cf5daab15df0247c17/charset_normalizer-3.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4", size = 142817, upload-time = "2026-03-15T18:51:14.408Z" }, - { url = "https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f", size = 294823, upload-time = "2026-03-15T18:51:15.755Z" }, - { url = "https://files.pythonhosted.org/packages/47/7b/20e809b89c69d37be748d98e84dce6820bf663cf19cf6b942c951a3e8f41/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843", size = 198527, upload-time = "2026-03-15T18:51:17.177Z" }, - { url = "https://files.pythonhosted.org/packages/37/a6/4f8d27527d59c039dce6f7622593cdcd3d70a8504d87d09eb11e9fdc6062/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf", size = 218388, upload-time = "2026-03-15T18:51:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/f6/9b/4770ccb3e491a9bacf1c46cc8b812214fe367c86a96353ccc6daf87b01ec/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8", size = 214563, upload-time = "2026-03-15T18:51:20.374Z" }, - { url = "https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9", size = 206587, upload-time = "2026-03-15T18:51:21.807Z" }, - { url = "https://files.pythonhosted.org/packages/7e/70/3def227f1ec56f5c69dfc8392b8bd63b11a18ca8178d9211d7cc5e5e4f27/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88", size = 194724, upload-time = "2026-03-15T18:51:23.508Z" }, - { url = "https://files.pythonhosted.org/packages/58/ab/9318352e220c05efd31c2779a23b50969dc94b985a2efa643ed9077bfca5/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84", size = 202956, upload-time = "2026-03-15T18:51:25.239Z" }, - { url = "https://files.pythonhosted.org/packages/75/13/f3550a3ac25b70f87ac98c40d3199a8503676c2f1620efbf8d42095cfc40/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd", size = 201923, upload-time = "2026-03-15T18:51:26.682Z" }, - { url = "https://files.pythonhosted.org/packages/1b/db/c5c643b912740b45e8eec21de1bbab8e7fc085944d37e1e709d3dcd9d72f/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c", size = 195366, upload-time = "2026-03-15T18:51:28.129Z" }, - { url = "https://files.pythonhosted.org/packages/5a/67/3b1c62744f9b2448443e0eb160d8b001c849ec3fef591e012eda6484787c/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194", size = 219752, upload-time = "2026-03-15T18:51:29.556Z" }, - { url = "https://files.pythonhosted.org/packages/f6/98/32ffbaf7f0366ffb0445930b87d103f6b406bc2c271563644bde8a2b1093/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc", size = 203296, upload-time = "2026-03-15T18:51:30.921Z" }, - { url = "https://files.pythonhosted.org/packages/41/12/5d308c1bbe60cabb0c5ef511574a647067e2a1f631bc8634fcafaccd8293/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f", size = 215956, upload-time = "2026-03-15T18:51:32.399Z" }, - { url = "https://files.pythonhosted.org/packages/53/e9/5f85f6c5e20669dbe56b165c67b0260547dea97dba7e187938833d791687/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2", size = 208652, upload-time = "2026-03-15T18:51:34.214Z" }, - { url = "https://files.pythonhosted.org/packages/f1/11/897052ea6af56df3eef3ca94edafee410ca699ca0c7b87960ad19932c55e/charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d", size = 143940, upload-time = "2026-03-15T18:51:36.15Z" }, - { url = "https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389", size = 154101, upload-time = "2026-03-15T18:51:37.876Z" }, - { url = "https://files.pythonhosted.org/packages/01/a5/7abf15b4c0968e47020f9ca0935fb3274deb87cb288cd187cad92e8cdffd/charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f", size = 143109, upload-time = "2026-03-15T18:51:39.565Z" }, - { url = "https://files.pythonhosted.org/packages/25/6f/ffe1e1259f384594063ea1869bfb6be5cdb8bc81020fc36c3636bc8302a1/charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8", size = 294458, upload-time = "2026-03-15T18:51:41.134Z" }, - { url = "https://files.pythonhosted.org/packages/56/60/09bb6c13a8c1016c2ed5c6a6488e4ffef506461aa5161662bd7636936fb1/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421", size = 199277, upload-time = "2026-03-15T18:51:42.953Z" }, - { url = "https://files.pythonhosted.org/packages/00/50/dcfbb72a5138bbefdc3332e8d81a23494bf67998b4b100703fd15fa52d81/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2", size = 218758, upload-time = "2026-03-15T18:51:44.339Z" }, - { url = "https://files.pythonhosted.org/packages/03/b3/d79a9a191bb75f5aa81f3aaaa387ef29ce7cb7a9e5074ba8ea095cc073c2/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30", size = 215299, upload-time = "2026-03-15T18:51:45.871Z" }, - { url = "https://files.pythonhosted.org/packages/76/7e/bc8911719f7084f72fd545f647601ea3532363927f807d296a8c88a62c0d/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db", size = 206811, upload-time = "2026-03-15T18:51:47.308Z" }, - { url = "https://files.pythonhosted.org/packages/e2/40/c430b969d41dda0c465aa36cc7c2c068afb67177bef50905ac371b28ccc7/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8", size = 193706, upload-time = "2026-03-15T18:51:48.849Z" }, - { url = "https://files.pythonhosted.org/packages/48/15/e35e0590af254f7df984de1323640ef375df5761f615b6225ba8deb9799a/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815", size = 202706, upload-time = "2026-03-15T18:51:50.257Z" }, - { url = "https://files.pythonhosted.org/packages/5e/bd/f736f7b9cc5e93a18b794a50346bb16fbfd6b37f99e8f306f7951d27c17c/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a", size = 202497, upload-time = "2026-03-15T18:51:52.012Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ba/2cc9e3e7dfdf7760a6ed8da7446d22536f3d0ce114ac63dee2a5a3599e62/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43", size = 193511, upload-time = "2026-03-15T18:51:53.723Z" }, - { url = "https://files.pythonhosted.org/packages/9e/cb/5be49b5f776e5613be07298c80e1b02a2d900f7a7de807230595c85a8b2e/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0", size = 220133, upload-time = "2026-03-15T18:51:55.333Z" }, - { url = "https://files.pythonhosted.org/packages/83/43/99f1b5dad345accb322c80c7821071554f791a95ee50c1c90041c157ae99/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1", size = 203035, upload-time = "2026-03-15T18:51:56.736Z" }, - { url = "https://files.pythonhosted.org/packages/87/9a/62c2cb6a531483b55dddff1a68b3d891a8b498f3ca555fbcf2978e804d9d/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f", size = 216321, upload-time = "2026-03-15T18:51:58.17Z" }, - { url = "https://files.pythonhosted.org/packages/6e/79/94a010ff81e3aec7c293eb82c28f930918e517bc144c9906a060844462eb/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815", size = 208973, upload-time = "2026-03-15T18:51:59.998Z" }, - { url = "https://files.pythonhosted.org/packages/2a/57/4ecff6d4ec8585342f0c71bc03efaa99cb7468f7c91a57b105bcd561cea8/charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d", size = 144610, upload-time = "2026-03-15T18:52:02.213Z" }, - { url = "https://files.pythonhosted.org/packages/80/94/8434a02d9d7f168c25767c64671fead8d599744a05d6a6c877144c754246/charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f", size = 154962, upload-time = "2026-03-15T18:52:03.658Z" }, - { url = "https://files.pythonhosted.org/packages/46/4c/48f2cdbfd923026503dfd67ccea45c94fd8fe988d9056b468579c66ed62b/charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e", size = 143595, upload-time = "2026-03-15T18:52:05.123Z" }, - { url = "https://files.pythonhosted.org/packages/31/93/8878be7569f87b14f1d52032946131bcb6ebbd8af3e20446bc04053dc3f1/charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866", size = 314828, upload-time = "2026-03-15T18:52:06.831Z" }, - { url = "https://files.pythonhosted.org/packages/06/b6/fae511ca98aac69ecc35cde828b0a3d146325dd03d99655ad38fc2cc3293/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc", size = 208138, upload-time = "2026-03-15T18:52:08.239Z" }, - { url = "https://files.pythonhosted.org/packages/54/57/64caf6e1bf07274a1e0b7c160a55ee9e8c9ec32c46846ce59b9c333f7008/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e", size = 224679, upload-time = "2026-03-15T18:52:10.043Z" }, - { url = "https://files.pythonhosted.org/packages/aa/cb/9ff5a25b9273ef160861b41f6937f86fae18b0792fe0a8e75e06acb08f1d/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077", size = 223475, upload-time = "2026-03-15T18:52:11.854Z" }, - { url = "https://files.pythonhosted.org/packages/fc/97/440635fc093b8d7347502a377031f9605a1039c958f3cd18dcacffb37743/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f", size = 215230, upload-time = "2026-03-15T18:52:13.325Z" }, - { url = "https://files.pythonhosted.org/packages/cd/24/afff630feb571a13f07c8539fbb502d2ab494019492aaffc78ef41f1d1d0/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e", size = 199045, upload-time = "2026-03-15T18:52:14.752Z" }, - { url = "https://files.pythonhosted.org/packages/e5/17/d1399ecdaf7e0498c327433e7eefdd862b41236a7e484355b8e0e5ebd64b/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484", size = 211658, upload-time = "2026-03-15T18:52:16.278Z" }, - { url = "https://files.pythonhosted.org/packages/b5/38/16baa0affb957b3d880e5ac2144caf3f9d7de7bc4a91842e447fbb5e8b67/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7", size = 210769, upload-time = "2026-03-15T18:52:17.782Z" }, - { url = "https://files.pythonhosted.org/packages/05/34/c531bc6ac4c21da9ddfddb3107be2287188b3ea4b53b70fc58f2a77ac8d8/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff", size = 201328, upload-time = "2026-03-15T18:52:19.553Z" }, - { url = "https://files.pythonhosted.org/packages/fa/73/a5a1e9ca5f234519c1953608a03fe109c306b97fdfb25f09182babad51a7/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e", size = 225302, upload-time = "2026-03-15T18:52:21.043Z" }, - { url = "https://files.pythonhosted.org/packages/ba/f6/cd782923d112d296294dea4bcc7af5a7ae0f86ab79f8fefbda5526b6cfc0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659", size = 211127, upload-time = "2026-03-15T18:52:22.491Z" }, - { url = "https://files.pythonhosted.org/packages/0e/c5/0b6898950627af7d6103a449b22320372c24c6feda91aa24e201a478d161/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602", size = 222840, upload-time = "2026-03-15T18:52:24.113Z" }, - { url = "https://files.pythonhosted.org/packages/7d/25/c4bba773bef442cbdc06111d40daa3de5050a676fa26e85090fc54dd12f0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407", size = 216890, upload-time = "2026-03-15T18:52:25.541Z" }, - { url = "https://files.pythonhosted.org/packages/35/1a/05dacadb0978da72ee287b0143097db12f2e7e8d3ffc4647da07a383b0b7/charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579", size = 155379, upload-time = "2026-03-15T18:52:27.05Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7a/d269d834cb3a76291651256f3b9a5945e81d0a49ab9f4a498964e83c0416/charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4", size = 169043, upload-time = "2026-03-15T18:52:28.502Z" }, - { url = "https://files.pythonhosted.org/packages/23/06/28b29fba521a37a8932c6a84192175c34d49f84a6d4773fa63d05f9aff22/charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c", size = 148523, upload-time = "2026-03-15T18:52:29.956Z" }, - { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - -[[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 = "colorlog" -version = "6.10.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321", size = 17162, upload-time = "2025-10-16T16:14:11.978Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743, upload-time = "2025-10-16T16:14:10.512Z" }, -] - -[[package]] -name = "coverage" -version = "7.13.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/e0/70553e3000e345daff267cec284ce4cbf3fc141b6da229ac52775b5428f1/coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179", size = 915967, upload-time = "2026-03-17T10:33:18.341Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c3/a396306ba7db865bf96fc1fb3b7fd29bcbf3d829df642e77b13555163cd6/coverage-7.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01", size = 219554, upload-time = "2026-03-17T10:30:42.208Z" }, - { url = "https://files.pythonhosted.org/packages/a6/16/a68a19e5384e93f811dccc51034b1fd0b865841c390e3c931dcc4699e035/coverage-7.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422", size = 219908, upload-time = "2026-03-17T10:30:43.906Z" }, - { url = "https://files.pythonhosted.org/packages/29/72/20b917c6793af3a5ceb7fb9c50033f3ec7865f2911a1416b34a7cfa0813b/coverage-7.13.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f", size = 251419, upload-time = "2026-03-17T10:30:45.545Z" }, - { url = "https://files.pythonhosted.org/packages/8c/49/cd14b789536ac6a4778c453c6a2338bc0a2fb60c5a5a41b4008328b9acc1/coverage-7.13.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5", size = 254159, upload-time = "2026-03-17T10:30:47.204Z" }, - { url = "https://files.pythonhosted.org/packages/9d/00/7b0edcfe64e2ed4c0340dac14a52ad0f4c9bd0b8b5e531af7d55b703db7c/coverage-7.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376", size = 255270, upload-time = "2026-03-17T10:30:48.812Z" }, - { url = "https://files.pythonhosted.org/packages/93/89/7ffc4ba0f5d0a55c1e84ea7cee39c9fc06af7b170513d83fbf3bbefce280/coverage-7.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256", size = 257538, upload-time = "2026-03-17T10:30:50.77Z" }, - { url = "https://files.pythonhosted.org/packages/81/bd/73ddf85f93f7e6fa83e77ccecb6162d9415c79007b4bc124008a4995e4a7/coverage-7.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c", size = 251821, upload-time = "2026-03-17T10:30:52.5Z" }, - { url = "https://files.pythonhosted.org/packages/a0/81/278aff4e8dec4926a0bcb9486320752811f543a3ce5b602cc7a29978d073/coverage-7.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5", size = 253191, upload-time = "2026-03-17T10:30:54.543Z" }, - { url = "https://files.pythonhosted.org/packages/70/ee/fe1621488e2e0a58d7e94c4800f0d96f79671553488d401a612bebae324b/coverage-7.13.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09", size = 251337, upload-time = "2026-03-17T10:30:56.663Z" }, - { url = "https://files.pythonhosted.org/packages/37/a6/f79fb37aa104b562207cc23cb5711ab6793608e246cae1e93f26b2236ed9/coverage-7.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9", size = 255404, upload-time = "2026-03-17T10:30:58.427Z" }, - { url = "https://files.pythonhosted.org/packages/75/f0/ed15262a58ec81ce457ceb717b7f78752a1713556b19081b76e90896e8d4/coverage-7.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf", size = 250903, upload-time = "2026-03-17T10:31:00.093Z" }, - { url = "https://files.pythonhosted.org/packages/0f/e9/9129958f20e7e9d4d56d51d42ccf708d15cac355ff4ac6e736e97a9393d2/coverage-7.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c", size = 252780, upload-time = "2026-03-17T10:31:01.916Z" }, - { url = "https://files.pythonhosted.org/packages/a4/d7/0ad9b15812d81272db94379fe4c6df8fd17781cc7671fdfa30c76ba5ff7b/coverage-7.13.5-cp312-cp312-win32.whl", hash = "sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf", size = 222093, upload-time = "2026-03-17T10:31:03.642Z" }, - { url = "https://files.pythonhosted.org/packages/29/3d/821a9a5799fac2556bcf0bd37a70d1d11fa9e49784b6d22e92e8b2f85f18/coverage-7.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810", size = 222900, upload-time = "2026-03-17T10:31:05.651Z" }, - { url = "https://files.pythonhosted.org/packages/d4/fa/2238c2ad08e35cf4f020ea721f717e09ec3152aea75d191a7faf3ef009a8/coverage-7.13.5-cp312-cp312-win_arm64.whl", hash = "sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de", size = 221515, upload-time = "2026-03-17T10:31:07.293Z" }, - { url = "https://files.pythonhosted.org/packages/74/8c/74fedc9663dcf168b0a059d4ea756ecae4da77a489048f94b5f512a8d0b3/coverage-7.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1", size = 219576, upload-time = "2026-03-17T10:31:09.045Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c9/44fb661c55062f0818a6ffd2685c67aa30816200d5f2817543717d4b92eb/coverage-7.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3", size = 219942, upload-time = "2026-03-17T10:31:10.708Z" }, - { url = "https://files.pythonhosted.org/packages/5f/13/93419671cee82b780bab7ea96b67c8ef448f5f295f36bf5031154ec9a790/coverage-7.13.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26", size = 250935, upload-time = "2026-03-17T10:31:12.392Z" }, - { url = "https://files.pythonhosted.org/packages/ac/68/1666e3a4462f8202d836920114fa7a5ee9275d1fa45366d336c551a162dd/coverage-7.13.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3", size = 253541, upload-time = "2026-03-17T10:31:14.247Z" }, - { url = "https://files.pythonhosted.org/packages/4e/5e/3ee3b835647be646dcf3c65a7c6c18f87c27326a858f72ab22c12730773d/coverage-7.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b", size = 254780, upload-time = "2026-03-17T10:31:16.193Z" }, - { url = "https://files.pythonhosted.org/packages/44/b3/cb5bd1a04cfcc49ede6cd8409d80bee17661167686741e041abc7ee1b9a9/coverage-7.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a", size = 256912, upload-time = "2026-03-17T10:31:17.89Z" }, - { url = "https://files.pythonhosted.org/packages/1b/66/c1dceb7b9714473800b075f5c8a84f4588f887a90eb8645282031676e242/coverage-7.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969", size = 251165, upload-time = "2026-03-17T10:31:19.605Z" }, - { url = "https://files.pythonhosted.org/packages/b7/62/5502b73b97aa2e53ea22a39cf8649ff44827bef76d90bf638777daa27a9d/coverage-7.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161", size = 252908, upload-time = "2026-03-17T10:31:21.312Z" }, - { url = "https://files.pythonhosted.org/packages/7d/37/7792c2d69854397ca77a55c4646e5897c467928b0e27f2d235d83b5d08c6/coverage-7.13.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15", size = 250873, upload-time = "2026-03-17T10:31:23.565Z" }, - { url = "https://files.pythonhosted.org/packages/a3/23/bc866fb6163be52a8a9e5d708ba0d3b1283c12158cefca0a8bbb6e247a43/coverage-7.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1", size = 255030, upload-time = "2026-03-17T10:31:25.58Z" }, - { url = "https://files.pythonhosted.org/packages/7d/8b/ef67e1c222ef49860701d346b8bbb70881bef283bd5f6cbba68a39a086c7/coverage-7.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6", size = 250694, upload-time = "2026-03-17T10:31:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/46/0d/866d1f74f0acddbb906db212e096dee77a8e2158ca5e6bb44729f9d93298/coverage-7.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17", size = 252469, upload-time = "2026-03-17T10:31:29.472Z" }, - { url = "https://files.pythonhosted.org/packages/7a/f5/be742fec31118f02ce42b21c6af187ad6a344fed546b56ca60caacc6a9a0/coverage-7.13.5-cp313-cp313-win32.whl", hash = "sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85", size = 222112, upload-time = "2026-03-17T10:31:31.526Z" }, - { url = "https://files.pythonhosted.org/packages/66/40/7732d648ab9d069a46e686043241f01206348e2bbf128daea85be4d6414b/coverage-7.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b", size = 222923, upload-time = "2026-03-17T10:31:33.633Z" }, - { url = "https://files.pythonhosted.org/packages/48/af/fea819c12a095781f6ccd504890aaddaf88b8fab263c4940e82c7b770124/coverage-7.13.5-cp313-cp313-win_arm64.whl", hash = "sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664", size = 221540, upload-time = "2026-03-17T10:31:35.445Z" }, - { url = "https://files.pythonhosted.org/packages/23/d2/17879af479df7fbbd44bd528a31692a48f6b25055d16482fdf5cdb633805/coverage-7.13.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d", size = 220262, upload-time = "2026-03-17T10:31:37.184Z" }, - { url = "https://files.pythonhosted.org/packages/5b/4c/d20e554f988c8f91d6a02c5118f9abbbf73a8768a3048cb4962230d5743f/coverage-7.13.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0", size = 220617, upload-time = "2026-03-17T10:31:39.245Z" }, - { url = "https://files.pythonhosted.org/packages/29/9c/f9f5277b95184f764b24e7231e166dfdb5780a46d408a2ac665969416d61/coverage-7.13.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806", size = 261912, upload-time = "2026-03-17T10:31:41.324Z" }, - { url = "https://files.pythonhosted.org/packages/d5/f6/7f1ab39393eeb50cfe4747ae8ef0e4fc564b989225aa1152e13a180d74f8/coverage-7.13.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3", size = 263987, upload-time = "2026-03-17T10:31:43.724Z" }, - { url = "https://files.pythonhosted.org/packages/a0/d7/62c084fb489ed9c6fbdf57e006752e7c516ea46fd690e5ed8b8617c7d52e/coverage-7.13.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9", size = 266416, upload-time = "2026-03-17T10:31:45.769Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f6/df63d8660e1a0bff6125947afda112a0502736f470d62ca68b288ea762d8/coverage-7.13.5-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd", size = 267558, upload-time = "2026-03-17T10:31:48.293Z" }, - { url = "https://files.pythonhosted.org/packages/5b/02/353ca81d36779bd108f6d384425f7139ac3c58c750dcfaafe5d0bee6436b/coverage-7.13.5-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606", size = 261163, upload-time = "2026-03-17T10:31:50.125Z" }, - { url = "https://files.pythonhosted.org/packages/2c/16/2e79106d5749bcaf3aee6d309123548e3276517cd7851faa8da213bc61bf/coverage-7.13.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e", size = 263981, upload-time = "2026-03-17T10:31:51.961Z" }, - { url = "https://files.pythonhosted.org/packages/29/c7/c29e0c59ffa6942030ae6f50b88ae49988e7e8da06de7ecdbf49c6d4feae/coverage-7.13.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0", size = 261604, upload-time = "2026-03-17T10:31:53.872Z" }, - { url = "https://files.pythonhosted.org/packages/40/48/097cdc3db342f34006a308ab41c3a7c11c3f0d84750d340f45d88a782e00/coverage-7.13.5-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87", size = 265321, upload-time = "2026-03-17T10:31:55.997Z" }, - { url = "https://files.pythonhosted.org/packages/bb/1f/4994af354689e14fd03a75f8ec85a9a68d94e0188bbdab3fc1516b55e512/coverage-7.13.5-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479", size = 260502, upload-time = "2026-03-17T10:31:58.308Z" }, - { url = "https://files.pythonhosted.org/packages/22/c6/9bb9ef55903e628033560885f5c31aa227e46878118b63ab15dc7ba87797/coverage-7.13.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2", size = 262688, upload-time = "2026-03-17T10:32:00.141Z" }, - { url = "https://files.pythonhosted.org/packages/14/4f/f5df9007e50b15e53e01edea486814783a7f019893733d9e4d6caad75557/coverage-7.13.5-cp313-cp313t-win32.whl", hash = "sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a", size = 222788, upload-time = "2026-03-17T10:32:02.246Z" }, - { url = "https://files.pythonhosted.org/packages/e1/98/aa7fccaa97d0f3192bec013c4e6fd6d294a6ed44b640e6bb61f479e00ed5/coverage-7.13.5-cp313-cp313t-win_amd64.whl", hash = "sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819", size = 223851, upload-time = "2026-03-17T10:32:04.416Z" }, - { url = "https://files.pythonhosted.org/packages/3d/8b/e5c469f7352651e5f013198e9e21f97510b23de957dd06a84071683b4b60/coverage-7.13.5-cp313-cp313t-win_arm64.whl", hash = "sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911", size = 222104, upload-time = "2026-03-17T10:32:06.65Z" }, - { url = "https://files.pythonhosted.org/packages/8e/77/39703f0d1d4b478bfd30191d3c14f53caf596fac00efb3f8f6ee23646439/coverage-7.13.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f", size = 219621, upload-time = "2026-03-17T10:32:08.589Z" }, - { url = "https://files.pythonhosted.org/packages/e2/3e/51dff36d99ae14639a133d9b164d63e628532e2974d8b1edb99dd1ebc733/coverage-7.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e", size = 219953, upload-time = "2026-03-17T10:32:10.507Z" }, - { url = "https://files.pythonhosted.org/packages/6a/6c/1f1917b01eb647c2f2adc9962bd66c79eb978951cab61bdc1acab3290c07/coverage-7.13.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a", size = 250992, upload-time = "2026-03-17T10:32:12.41Z" }, - { url = "https://files.pythonhosted.org/packages/22/e5/06b1f88f42a5a99df42ce61208bdec3bddb3d261412874280a19796fc09c/coverage-7.13.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510", size = 253503, upload-time = "2026-03-17T10:32:14.449Z" }, - { url = "https://files.pythonhosted.org/packages/80/28/2a148a51e5907e504fa7b85490277734e6771d8844ebcc48764a15e28155/coverage-7.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247", size = 254852, upload-time = "2026-03-17T10:32:16.56Z" }, - { url = "https://files.pythonhosted.org/packages/61/77/50e8d3d85cc0b7ebe09f30f151d670e302c7ff4a1bf6243f71dd8b0981fa/coverage-7.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6", size = 257161, upload-time = "2026-03-17T10:32:19.004Z" }, - { url = "https://files.pythonhosted.org/packages/3b/c4/b5fd1d4b7bf8d0e75d997afd3925c59ba629fc8616f1b3aae7605132e256/coverage-7.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0", size = 251021, upload-time = "2026-03-17T10:32:21.344Z" }, - { url = "https://files.pythonhosted.org/packages/f8/66/6ea21f910e92d69ef0b1c3346ea5922a51bad4446c9126db2ae96ee24c4c/coverage-7.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882", size = 252858, upload-time = "2026-03-17T10:32:23.506Z" }, - { url = "https://files.pythonhosted.org/packages/9e/ea/879c83cb5d61aa2a35fb80e72715e92672daef8191b84911a643f533840c/coverage-7.13.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740", size = 250823, upload-time = "2026-03-17T10:32:25.516Z" }, - { url = "https://files.pythonhosted.org/packages/8a/fb/616d95d3adb88b9803b275580bdeee8bd1b69a886d057652521f83d7322f/coverage-7.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16", size = 255099, upload-time = "2026-03-17T10:32:27.944Z" }, - { url = "https://files.pythonhosted.org/packages/1c/93/25e6917c90ec1c9a56b0b26f6cad6408e5f13bb6b35d484a0d75c9cf000d/coverage-7.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0", size = 250638, upload-time = "2026-03-17T10:32:29.914Z" }, - { url = "https://files.pythonhosted.org/packages/fc/7b/dc1776b0464145a929deed214aef9fb1493f159b59ff3c7eeeedf91eddd0/coverage-7.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0", size = 252295, upload-time = "2026-03-17T10:32:31.981Z" }, - { url = "https://files.pythonhosted.org/packages/ea/fb/99cbbc56a26e07762a2740713f3c8f9f3f3106e3a3dd8cc4474954bccd34/coverage-7.13.5-cp314-cp314-win32.whl", hash = "sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc", size = 222360, upload-time = "2026-03-17T10:32:34.233Z" }, - { url = "https://files.pythonhosted.org/packages/8d/b7/4758d4f73fb536347cc5e4ad63662f9d60ba9118cb6785e9616b2ce5d7fa/coverage-7.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633", size = 223174, upload-time = "2026-03-17T10:32:36.369Z" }, - { url = "https://files.pythonhosted.org/packages/2c/f2/24d84e1dfe70f8ac9fdf30d338239860d0d1d5da0bda528959d0ebc9da28/coverage-7.13.5-cp314-cp314-win_arm64.whl", hash = "sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8", size = 221739, upload-time = "2026-03-17T10:32:38.736Z" }, - { url = "https://files.pythonhosted.org/packages/60/5b/4a168591057b3668c2428bff25dd3ebc21b629d666d90bcdfa0217940e84/coverage-7.13.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b", size = 220351, upload-time = "2026-03-17T10:32:41.196Z" }, - { url = "https://files.pythonhosted.org/packages/f5/21/1fd5c4dbfe4a58b6b99649125635df46decdfd4a784c3cd6d410d303e370/coverage-7.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c", size = 220612, upload-time = "2026-03-17T10:32:43.204Z" }, - { url = "https://files.pythonhosted.org/packages/d6/fe/2a924b3055a5e7e4512655a9d4609781b0d62334fa0140c3e742926834e2/coverage-7.13.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9", size = 261985, upload-time = "2026-03-17T10:32:45.514Z" }, - { url = "https://files.pythonhosted.org/packages/d7/0d/c8928f2bd518c45990fe1a2ab8db42e914ef9b726c975facc4282578c3eb/coverage-7.13.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29", size = 264107, upload-time = "2026-03-17T10:32:47.971Z" }, - { url = "https://files.pythonhosted.org/packages/ef/ae/4ae35bbd9a0af9d820362751f0766582833c211224b38665c0f8de3d487f/coverage-7.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607", size = 266513, upload-time = "2026-03-17T10:32:50.1Z" }, - { url = "https://files.pythonhosted.org/packages/9c/20/d326174c55af36f74eac6ae781612d9492f060ce8244b570bb9d50d9d609/coverage-7.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90", size = 267650, upload-time = "2026-03-17T10:32:52.391Z" }, - { url = "https://files.pythonhosted.org/packages/7a/5e/31484d62cbd0eabd3412e30d74386ece4a0837d4f6c3040a653878bfc019/coverage-7.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3", size = 261089, upload-time = "2026-03-17T10:32:54.544Z" }, - { url = "https://files.pythonhosted.org/packages/e9/d8/49a72d6de146eebb0b7e48cc0f4bc2c0dd858e3d4790ab2b39a2872b62bd/coverage-7.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab", size = 263982, upload-time = "2026-03-17T10:32:56.803Z" }, - { url = "https://files.pythonhosted.org/packages/06/3b/0351f1bd566e6e4dd39e978efe7958bde1d32f879e85589de147654f57bb/coverage-7.13.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562", size = 261579, upload-time = "2026-03-17T10:32:59.466Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ce/796a2a2f4017f554d7810f5c573449b35b1e46788424a548d4d19201b222/coverage-7.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2", size = 265316, upload-time = "2026-03-17T10:33:01.847Z" }, - { url = "https://files.pythonhosted.org/packages/3d/16/d5ae91455541d1a78bc90abf495be600588aff8f6db5c8b0dae739fa39c9/coverage-7.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea", size = 260427, upload-time = "2026-03-17T10:33:03.945Z" }, - { url = "https://files.pythonhosted.org/packages/48/11/07f413dba62db21fb3fad5d0de013a50e073cc4e2dc4306e770360f6dfc8/coverage-7.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a", size = 262745, upload-time = "2026-03-17T10:33:06.285Z" }, - { url = "https://files.pythonhosted.org/packages/91/15/d792371332eb4663115becf4bad47e047d16234b1aff687b1b18c58d60ae/coverage-7.13.5-cp314-cp314t-win32.whl", hash = "sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215", size = 223146, upload-time = "2026-03-17T10:33:08.756Z" }, - { url = "https://files.pythonhosted.org/packages/db/51/37221f59a111dca5e85be7dbf09696323b5b9f13ff65e0641d535ed06ea8/coverage-7.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43", size = 224254, upload-time = "2026-03-17T10:33:11.174Z" }, - { url = "https://files.pythonhosted.org/packages/54/83/6acacc889de8987441aa7d5adfbdbf33d288dad28704a67e574f1df9bcbb/coverage-7.13.5-cp314-cp314t-win_arm64.whl", hash = "sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45", size = 222276, upload-time = "2026-03-17T10:33:13.466Z" }, - { url = "https://files.pythonhosted.org/packages/9e/ee/a4cf96b8ce1e566ed238f0659ac2d3f007ed1d14b181bcb684e19561a69a/coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61", size = 211346, upload-time = "2026-03-17T10:33:15.691Z" }, -] - -[[package]] -name = "cryptography" -version = "46.0.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" }, - { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, - { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, - { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, - { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, - { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, - { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, - { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, - { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, - { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", size = 3028230, upload-time = "2026-02-10T19:17:30.518Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", size = 3480909, upload-time = "2026-02-10T19:17:32.083Z" }, - { url = "https://files.pythonhosted.org/packages/00/13/3d278bfa7a15a96b9dc22db5a12ad1e48a9eb3d40e1827ef66a5df75d0d0/cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", size = 7119287, upload-time = "2026-02-10T19:17:33.801Z" }, - { url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" }, - { url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" }, - { url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", size = 4927539, upload-time = "2026-02-10T19:17:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" }, - { url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131, upload-time = "2026-02-10T19:17:43.379Z" }, - { url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" }, - { url = "https://files.pythonhosted.org/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", size = 4892170, upload-time = "2026-02-10T19:17:46.997Z" }, - { url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" }, - { url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" }, - { url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" }, - { url = "https://files.pythonhosted.org/packages/86/ef/5d00ef966ddd71ac2e6951d278884a84a40ffbd88948ef0e294b214ae9e4/cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a", size = 3003637, upload-time = "2026-02-10T19:17:52.997Z" }, - { url = "https://files.pythonhosted.org/packages/b7/57/f3f4160123da6d098db78350fdfd9705057aad21de7388eacb2401dceab9/cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4", size = 3469487, upload-time = "2026-02-10T19:17:54.549Z" }, - { url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" }, - { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, - { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, - { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, - { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, - { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, - { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, - { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, - { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, - { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, - { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", size = 3010220, upload-time = "2026-02-10T19:18:17.361Z" }, - { url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050, upload-time = "2026-02-10T19:18:18.899Z" }, -] - -[[package]] -name = "cyclopts" -version = "4.10.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "docstring-parser" }, - { name = "rich" }, - { name = "rich-rst" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6c/c4/2ce2ca1451487dc7d59f09334c3fa1182c46cfcf0a2d5f19f9b26d53ac74/cyclopts-4.10.1.tar.gz", hash = "sha256:ad4e4bb90576412d32276b14a76f55d43353753d16217f2c3cd5bdceba7f15a0", size = 166623, upload-time = "2026-03-23T14:43:01.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0b/2261922126b2e50c601fe22d7ff5194e0a4d50e654836260c0665e24d862/cyclopts-4.10.1-py3-none-any.whl", hash = "sha256:35f37257139380a386d9fe4475e1e7c87ca7795765ef4f31abba579fcfcb6ecd", size = 204331, upload-time = "2026-03-23T14:43:02.625Z" }, -] - -[[package]] -name = "decorator" -version = "5.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, -] - -[[package]] -name = "dependency-groups" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/62/55/f054de99871e7beb81935dea8a10b90cd5ce42122b1c3081d5282fdb3621/dependency_groups-1.3.1.tar.gz", hash = "sha256:78078301090517fd938c19f64a53ce98c32834dfe0dee6b88004a569a6adfefd", size = 10093, upload-time = "2025-05-02T00:34:29.452Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/c7/d1ec24fb280caa5a79b6b950db565dab30210a66259d17d5bb2b3a9f878d/dependency_groups-1.3.1-py3-none-any.whl", hash = "sha256:51aeaa0dfad72430fcfb7bcdbefbd75f3792e5919563077f30bc0d73f4493030", size = 8664, upload-time = "2025-05-02T00:34:27.085Z" }, -] - -[[package]] -name = "dirty-equals" -version = "0.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/1d/c5913ac9d6615515a00f4bdc71356d302437cb74ff2e9aaccd3c14493b78/dirty_equals-0.11.tar.gz", hash = "sha256:f4ac74ee88f2d11e2fa0f65eb30ee4f07105c5f86f4dc92b09eb1138775027c3", size = 128067, upload-time = "2025-11-17T01:51:24.451Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/8d/dbff05239043271dbeace563a7686212a3dd517864a35623fe4d4a64ca19/dirty_equals-0.11-py3-none-any.whl", hash = "sha256:b1d7093273fc2f9be12f443a8ead954ef6daaf6746fd42ef3a5616433ee85286", size = 28051, upload-time = "2025-11-17T01:51:22.849Z" }, -] - -[[package]] -name = "distlib" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, -] - -[[package]] -name = "distro" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, -] - -[[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.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, -] - -[[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.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "executing" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, -] - -[[package]] -name = "factory-boy" -version = "3.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "faker" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ba/98/75cacae9945f67cfe323829fc2ac451f64517a8a330b572a06a323997065/factory_boy-3.3.3.tar.gz", hash = "sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03", size = 164146, upload-time = "2025-02-03T09:49:04.433Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/8d/2bc5f5546ff2ccb3f7de06742853483ab75bf74f36a92254702f8baecc79/factory_boy-3.3.3-py2.py3-none-any.whl", hash = "sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc", size = 37036, upload-time = "2025-02-03T09:49:01.659Z" }, -] - -[[package]] -name = "faker" -version = "40.11.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tzdata", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/94/dc/b68e5378e5a7db0ab776efcdd53b6fe374b29d703e156fd5bb4c5437069e/faker-40.11.0.tar.gz", hash = "sha256:7c419299103b13126bd02ec14bd2b47b946edb5a5eedf305e66a193b25f9a734", size = 1957570, upload-time = "2026-03-13T14:36:11.844Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/fa/a86c6ba66f0308c95b9288b1e3eaccd934b545646f63494a86f1ec2f8c8e/faker-40.11.0-py3-none-any.whl", hash = "sha256:0e9816c950528d2a37d74863f3ef389ea9a3a936cbcde0b11b8499942e25bf90", size = 1989457, upload-time = "2026-03-13T14:36:09.792Z" }, -] - -[[package]] -name = "fastapi" -version = "0.135.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-doc" }, - { name = "pydantic" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e7/7b/f8e0211e9380f7195ba3f3d40c292594fd81ba8ec4629e3854c353aaca45/fastapi-0.135.1.tar.gz", hash = "sha256:d04115b508d936d254cea545b7312ecaa58a7b3a0f84952535b4c9afae7668cd", size = 394962, upload-time = "2026-03-01T18:18:29.369Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/72/42e900510195b23a56bde950d26a51f8b723846bfcaa0286e90287f0422b/fastapi-0.135.1-py3-none-any.whl", hash = "sha256:46e2fc5745924b7c840f71ddd277382af29ce1cdb7d5eab5bf697e3fb9999c9e", size = 116999, upload-time = "2026-03-01T18:18:30.831Z" }, -] - -[[package]] -name = "fastembed" -version = "0.7.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, - { name = "loguru" }, - { name = "mmh3" }, - { name = "numpy" }, - { name = "onnxruntime" }, - { name = "pillow" }, - { name = "py-rust-stemmers" }, - { name = "requests" }, - { name = "tokenizers" }, - { name = "tqdm" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4c/c2/9c708680de1b54480161e0505f9d6d3d8eb47a1dc1a1f7f3c5106ba355d2/fastembed-0.7.4.tar.gz", hash = "sha256:8b8a4ea860ca295002f4754e8f5820a636e1065a9444959e18d5988d7f27093b", size = 68807, upload-time = "2025-12-05T12:08:10.447Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/3b/8da01492bc8b69184257d0c951bf0e77aec8ce110f06d8ce16c6ed9084f7/fastembed-0.7.4-py3-none-any.whl", hash = "sha256:79250a775f70bd6addb0e054204df042b5029ecae501e40e5bbd08e75844ad83", size = 108491, upload-time = "2025-12-05T12:08:09.059Z" }, -] - -[[package]] -name = "fastmcp" -version = "3.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "authlib" }, - { name = "cyclopts" }, - { name = "exceptiongroup" }, - { name = "httpx" }, - { name = "jsonref" }, - { name = "jsonschema-path" }, - { name = "mcp" }, - { name = "openapi-pydantic" }, - { name = "opentelemetry-api" }, - { name = "packaging" }, - { name = "platformdirs" }, - { name = "py-key-value-aio", extra = ["filetree", "keyring", "memory"] }, - { name = "pydantic", extra = ["email"] }, - { name = "pyperclip" }, - { name = "python-dotenv" }, - { name = "pyyaml" }, - { name = "rich" }, - { name = "uncalled-for" }, - { name = "uvicorn" }, - { name = "watchfiles" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/25/83/c95d3bf717698a693eccb43e137a32939d2549876e884e246028bff6ecce/fastmcp-3.1.1.tar.gz", hash = "sha256:db184b5391a31199323766a3abf3a8bfbb8010479f77eca84c0e554f18655c48", size = 17347644, upload-time = "2026-03-14T19:12:20.235Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/70/ea/570122de7e24f72138d006f799768e14cc1ccf7fcb22b7750b2bd276c711/fastmcp-3.1.1-py3-none-any.whl", hash = "sha256:8132ba069d89f14566b3266919d6d72e2ec23dd45d8944622dca407e9beda7eb", size = 633754, upload-time = "2026-03-14T19:12:22.736Z" }, -] - -[[package]] -name = "fastuuid" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232, upload-time = "2025-10-19T22:19:22.402Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/a2/e78fcc5df65467f0d207661b7ef86c5b7ac62eea337c0c0fcedbeee6fb13/fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77e94728324b63660ebf8adb27055e92d2e4611645bf12ed9d88d30486471d0a", size = 510164, upload-time = "2025-10-19T22:31:45.635Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b3/c846f933f22f581f558ee63f81f29fa924acd971ce903dab1a9b6701816e/fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:caa1f14d2102cb8d353096bc6ef6c13b2c81f347e6ab9d6fbd48b9dea41c153d", size = 261837, upload-time = "2025-10-19T22:38:38.53Z" }, - { url = "https://files.pythonhosted.org/packages/54/ea/682551030f8c4fa9a769d9825570ad28c0c71e30cf34020b85c1f7ee7382/fastuuid-0.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d23ef06f9e67163be38cece704170486715b177f6baae338110983f99a72c070", size = 251370, upload-time = "2025-10-19T22:40:26.07Z" }, - { url = "https://files.pythonhosted.org/packages/14/dd/5927f0a523d8e6a76b70968e6004966ee7df30322f5fc9b6cdfb0276646a/fastuuid-0.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c9ec605ace243b6dbe3bd27ebdd5d33b00d8d1d3f580b39fdd15cd96fd71796", size = 277766, upload-time = "2025-10-19T22:37:23.779Z" }, - { url = "https://files.pythonhosted.org/packages/16/6e/c0fb547eef61293153348f12e0f75a06abb322664b34a1573a7760501336/fastuuid-0.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:808527f2407f58a76c916d6aa15d58692a4a019fdf8d4c32ac7ff303b7d7af09", size = 278105, upload-time = "2025-10-19T22:26:56.821Z" }, - { url = "https://files.pythonhosted.org/packages/2d/b1/b9c75e03b768f61cf2e84ee193dc18601aeaf89a4684b20f2f0e9f52b62c/fastuuid-0.14.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fb3c0d7fef6674bbeacdd6dbd386924a7b60b26de849266d1ff6602937675c8", size = 301564, upload-time = "2025-10-19T22:30:31.604Z" }, - { url = "https://files.pythonhosted.org/packages/fc/fa/f7395fdac07c7a54f18f801744573707321ca0cee082e638e36452355a9d/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab3f5d36e4393e628a4df337c2c039069344db5f4b9d2a3c9cea48284f1dd741", size = 459659, upload-time = "2025-10-19T22:31:32.341Z" }, - { url = "https://files.pythonhosted.org/packages/66/49/c9fd06a4a0b1f0f048aacb6599e7d96e5d6bc6fa680ed0d46bf111929d1b/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b9a0ca4f03b7e0b01425281ffd44e99d360e15c895f1907ca105854ed85e2057", size = 478430, upload-time = "2025-10-19T22:26:22.962Z" }, - { url = "https://files.pythonhosted.org/packages/be/9c/909e8c95b494e8e140e8be6165d5fc3f61fdc46198c1554df7b3e1764471/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3acdf655684cc09e60fb7e4cf524e8f42ea760031945aa8086c7eae2eeeabeb8", size = 450894, upload-time = "2025-10-19T22:27:01.647Z" }, - { url = "https://files.pythonhosted.org/packages/90/eb/d29d17521976e673c55ef7f210d4cdd72091a9ec6755d0fd4710d9b3c871/fastuuid-0.14.0-cp312-cp312-win32.whl", hash = "sha256:9579618be6280700ae36ac42c3efd157049fe4dd40ca49b021280481c78c3176", size = 154374, upload-time = "2025-10-19T22:29:19.879Z" }, - { url = "https://files.pythonhosted.org/packages/cc/fc/f5c799a6ea6d877faec0472d0b27c079b47c86b1cdc577720a5386483b36/fastuuid-0.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:d9e4332dc4ba054434a9594cbfaf7823b57993d7d8e7267831c3e059857cf397", size = 156550, upload-time = "2025-10-19T22:27:49.658Z" }, - { url = "https://files.pythonhosted.org/packages/a5/83/ae12dd39b9a39b55d7f90abb8971f1a5f3c321fd72d5aa83f90dc67fe9ed/fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77a09cb7427e7af74c594e409f7731a0cf887221de2f698e1ca0ebf0f3139021", size = 510720, upload-time = "2025-10-19T22:42:34.633Z" }, - { url = "https://files.pythonhosted.org/packages/53/b0/a4b03ff5d00f563cc7546b933c28cb3f2a07344b2aec5834e874f7d44143/fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:9bd57289daf7b153bfa3e8013446aa144ce5e8c825e9e366d455155ede5ea2dc", size = 262024, upload-time = "2025-10-19T22:30:25.482Z" }, - { url = "https://files.pythonhosted.org/packages/9c/6d/64aee0a0f6a58eeabadd582e55d0d7d70258ffdd01d093b30c53d668303b/fastuuid-0.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ac60fc860cdf3c3f327374db87ab8e064c86566ca8c49d2e30df15eda1b0c2d5", size = 251679, upload-time = "2025-10-19T22:36:14.096Z" }, - { url = "https://files.pythonhosted.org/packages/60/f5/a7e9cda8369e4f7919d36552db9b2ae21db7915083bc6336f1b0082c8b2e/fastuuid-0.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab32f74bd56565b186f036e33129da77db8be09178cd2f5206a5d4035fb2a23f", size = 277862, upload-time = "2025-10-19T22:36:23.302Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d3/8ce11827c783affffd5bd4d6378b28eb6cc6d2ddf41474006b8d62e7448e/fastuuid-0.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e678459cf4addaedd9936bbb038e35b3f6b2061330fd8f2f6a1d80414c0f87", size = 278278, upload-time = "2025-10-19T22:29:43.809Z" }, - { url = "https://files.pythonhosted.org/packages/a2/51/680fb6352d0bbade04036da46264a8001f74b7484e2fd1f4da9e3db1c666/fastuuid-0.14.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1e3cc56742f76cd25ecb98e4b82a25f978ccffba02e4bdce8aba857b6d85d87b", size = 301788, upload-time = "2025-10-19T22:36:06.825Z" }, - { url = "https://files.pythonhosted.org/packages/fa/7c/2014b5785bd8ebdab04ec857635ebd84d5ee4950186a577db9eff0fb8ff6/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cb9a030f609194b679e1660f7e32733b7a0f332d519c5d5a6a0a580991290022", size = 459819, upload-time = "2025-10-19T22:35:31.623Z" }, - { url = "https://files.pythonhosted.org/packages/01/d2/524d4ceeba9160e7a9bc2ea3e8f4ccf1ad78f3bde34090ca0c51f09a5e91/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:09098762aad4f8da3a888eb9ae01c84430c907a297b97166b8abc07b640f2995", size = 478546, upload-time = "2025-10-19T22:26:03.023Z" }, - { url = "https://files.pythonhosted.org/packages/bc/17/354d04951ce114bf4afc78e27a18cfbd6ee319ab1829c2d5fb5e94063ac6/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1383fff584fa249b16329a059c68ad45d030d5a4b70fb7c73a08d98fd53bcdab", size = 450921, upload-time = "2025-10-19T22:31:02.151Z" }, - { url = "https://files.pythonhosted.org/packages/fb/be/d7be8670151d16d88f15bb121c5b66cdb5ea6a0c2a362d0dcf30276ade53/fastuuid-0.14.0-cp313-cp313-win32.whl", hash = "sha256:a0809f8cc5731c066c909047f9a314d5f536c871a7a22e815cc4967c110ac9ad", size = 154559, upload-time = "2025-10-19T22:36:36.011Z" }, - { url = "https://files.pythonhosted.org/packages/22/1d/5573ef3624ceb7abf4a46073d3554e37191c868abc3aecd5289a72f9810a/fastuuid-0.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:0df14e92e7ad3276327631c9e7cec09e32572ce82089c55cb1bb8df71cf394ed", size = 156539, upload-time = "2025-10-19T22:33:35.898Z" }, - { url = "https://files.pythonhosted.org/packages/16/c9/8c7660d1fe3862e3f8acabd9be7fc9ad71eb270f1c65cce9a2b7a31329ab/fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b852a870a61cfc26c884af205d502881a2e59cc07076b60ab4a951cc0c94d1ad", size = 510600, upload-time = "2025-10-19T22:43:44.17Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f4/a989c82f9a90d0ad995aa957b3e572ebef163c5299823b4027986f133dfb/fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c7502d6f54cd08024c3ea9b3514e2d6f190feb2f46e6dbcd3747882264bb5f7b", size = 262069, upload-time = "2025-10-19T22:43:38.38Z" }, - { url = "https://files.pythonhosted.org/packages/da/6c/a1a24f73574ac995482b1326cf7ab41301af0fabaa3e37eeb6b3df00e6e2/fastuuid-0.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ca61b592120cf314cfd66e662a5b54a578c5a15b26305e1b8b618a6f22df714", size = 251543, upload-time = "2025-10-19T22:32:22.537Z" }, - { url = "https://files.pythonhosted.org/packages/1a/20/2a9b59185ba7a6c7b37808431477c2d739fcbdabbf63e00243e37bd6bf49/fastuuid-0.14.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa75b6657ec129d0abded3bec745e6f7ab642e6dba3a5272a68247e85f5f316f", size = 277798, upload-time = "2025-10-19T22:33:53.821Z" }, - { url = "https://files.pythonhosted.org/packages/ef/33/4105ca574f6ded0af6a797d39add041bcfb468a1255fbbe82fcb6f592da2/fastuuid-0.14.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8a0dfea3972200f72d4c7df02c8ac70bad1bb4c58d7e0ec1e6f341679073a7f", size = 278283, upload-time = "2025-10-19T22:29:02.812Z" }, - { url = "https://files.pythonhosted.org/packages/fe/8c/fca59f8e21c4deb013f574eae05723737ddb1d2937ce87cb2a5d20992dc3/fastuuid-0.14.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1bf539a7a95f35b419f9ad105d5a8a35036df35fdafae48fb2fd2e5f318f0d75", size = 301627, upload-time = "2025-10-19T22:35:54.985Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e2/f78c271b909c034d429218f2798ca4e89eeda7983f4257d7865976ddbb6c/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:9a133bf9cc78fdbd1179cb58a59ad0100aa32d8675508150f3658814aeefeaa4", size = 459778, upload-time = "2025-10-19T22:28:00.999Z" }, - { url = "https://files.pythonhosted.org/packages/1e/f0/5ff209d865897667a2ff3e7a572267a9ced8f7313919f6d6043aed8b1caa/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_i686.whl", hash = "sha256:f54d5b36c56a2d5e1a31e73b950b28a0d83eb0c37b91d10408875a5a29494bad", size = 478605, upload-time = "2025-10-19T22:36:21.764Z" }, - { url = "https://files.pythonhosted.org/packages/e0/c8/2ce1c78f983a2c4987ea865d9516dbdfb141a120fd3abb977ae6f02ba7ca/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:ec27778c6ca3393ef662e2762dba8af13f4ec1aaa32d08d77f71f2a70ae9feb8", size = 450837, upload-time = "2025-10-19T22:34:37.178Z" }, - { url = "https://files.pythonhosted.org/packages/df/60/dad662ec9a33b4a5fe44f60699258da64172c39bd041da2994422cdc40fe/fastuuid-0.14.0-cp314-cp314-win32.whl", hash = "sha256:e23fc6a83f112de4be0cc1990e5b127c27663ae43f866353166f87df58e73d06", size = 154532, upload-time = "2025-10-19T22:35:18.217Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/da4db31001e854025ffd26bc9ba0740a9cbba2c3259695f7c5834908b336/fastuuid-0.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:df61342889d0f5e7a32f7284e55ef95103f2110fee433c2ae7c2c0956d76ac8a", size = 156457, upload-time = "2025-10-19T22:33:44.579Z" }, -] - -[[package]] -name = "filelock" -version = "3.25.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, -] - -[[package]] -name = "flatbuffers" -version = "25.12.19" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" }, -] - -[[package]] -name = "frozenlist" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, - { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, - { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, - { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, - { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, - { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, - { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, - { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, - { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, - { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, - { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, - { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, - { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, - { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, - { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, - { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, - { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, - { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, - { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, - { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, - { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, - { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, - { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, - { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, - { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, - { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, - { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, - { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, - { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, - { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, - { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, - { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, - { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, - { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, - { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, - { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, - { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, - { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, - { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, - { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, - { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, - { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, - { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, - { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, - { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, - { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, - { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, - { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, - { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, - { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, - { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, - { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, - { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, - { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, - { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, - { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, - { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, - { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, - { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, - { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, - { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, - { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, - { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, -] - -[[package]] -name = "fsspec" -version = "2026.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/7c/f60c259dcbf4f0c47cc4ddb8f7720d2dcdc8888c8e5ad84c73ea4531cc5b/fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff", size = 313441, upload-time = "2026-02-05T21:50:53.743Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437", size = 202505, upload-time = "2026-02-05T21:50:51.819Z" }, -] - -[[package]] -name = "ghp-import" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, -] - -[[package]] -name = "griffelib" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ad/06/eccbd311c9e2b3ca45dbc063b93134c57a1ccc7607c5e545264ad092c4a9/griffelib-2.0.0.tar.gz", hash = "sha256:e504d637a089f5cab9b5daf18f7645970509bf4f53eda8d79ed71cce8bd97934", size = 166312, upload-time = "2026-03-23T21:06:55.954Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl", hash = "sha256:01284878c966508b6d6f1dbff9b6fa607bc062d8261c5c7253cb285b06422a7f", size = 142004, upload-time = "2026-02-09T19:09:40.561Z" }, -] - -[[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 = "hanzo-llm" -version = "1.81.15" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "click" }, - { name = "fastuuid" }, - { name = "httpx" }, - { name = "importlib-metadata" }, - { name = "jinja2" }, - { name = "jsonschema" }, - { name = "openai" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "tiktoken" }, - { name = "tokenizers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/03/33/7464f7ce2376374ac3dc080cf38ded49f53e4e8e3d5786038faa745349ab/hanzo_llm-1.81.15.tar.gz", hash = "sha256:f9a6c1c2c83f0cfd67856a61f8b1e22825a6d02c60e7a364fd3ce1c193798366", size = 16520951, upload-time = "2026-03-02T15:15:04.283Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/37/15/3cd25b2908b6ec404d073ca57bbe1580e332a0ee094d0a33c36cd32d2e22/hanzo_llm-1.81.15-py3-none-any.whl", hash = "sha256:be3849567dc6a59b711a34f85bff1a74ad417df322cc38f3aebd16f9fa29823c", size = 14677430, upload-time = "2026-02-26T04:25:46.164Z" }, -] - -[[package]] -name = "hanzo-memory" -version = "1.0.1" -source = { editable = "pkg/hanzo-memory" } -dependencies = [ - { name = "httpx" }, - { name = "mcp" }, - { name = "numpy" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "python-multipart" }, - { name = "rich" }, - { name = "sqlite-vec" }, - { name = "structlog" }, -] - -[package.optional-dependencies] -dev = [ - { name = "black" }, - { name = "ipdb" }, - { name = "ipython" }, - { name = "mypy" }, - { name = "pre-commit" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "ruff" }, - { name = "twine" }, -] -docs = [ - { name = "mkdocs" }, - { name = "mkdocs-material" }, - { name = "mkdocstrings", extra = ["python"] }, -] -test = [ - { name = "factory-boy" }, - { name = "faker" }, - { name = "httpx" }, - { name = "polars" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "pytest-mock" }, - { name = "respx" }, -] - -[package.metadata] -requires-dist = [ - { name = "black", marker = "extra == 'dev'", specifier = ">=24.10.0" }, - { name = "factory-boy", marker = "extra == 'test'", specifier = ">=3.3.0" }, - { name = "faker", marker = "extra == 'test'", specifier = ">=30.0.0" }, - { name = "httpx", specifier = ">=0.28.0" }, - { name = "httpx", marker = "extra == 'test'", specifier = ">=0.27.0" }, - { name = "ipdb", marker = "extra == 'dev'", specifier = ">=0.13.0" }, - { name = "ipython", marker = "extra == 'dev'", specifier = ">=8.29.0" }, - { name = "mcp", specifier = ">=1.2.0" }, - { name = "mkdocs", marker = "extra == 'docs'", specifier = ">=1.6.0" }, - { name = "mkdocs-material", marker = "extra == 'docs'", specifier = ">=9.5.0" }, - { name = "mkdocstrings", extras = ["python"], marker = "extra == 'docs'", specifier = ">=0.27.0" }, - { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.13.0" }, - { name = "numpy", specifier = ">=1.26.0" }, - { name = "polars", marker = "extra == 'test'", specifier = ">=1.0.0" }, - { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.0.0" }, - { name = "pydantic", specifier = ">=2.9.0" }, - { name = "pydantic-settings", specifier = ">=2.6.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.0" }, - { name = "pytest", marker = "extra == 'test'", specifier = ">=8.3.0" }, - { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24.0" }, - { name = "pytest-asyncio", marker = "extra == 'test'", specifier = ">=0.24.0" }, - { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=6.0.0" }, - { name = "pytest-cov", marker = "extra == 'test'", specifier = ">=6.0.0" }, - { name = "pytest-mock", marker = "extra == 'test'", specifier = ">=3.14.0" }, - { name = "python-multipart", specifier = ">=0.0.12" }, - { name = "respx", marker = "extra == 'test'", specifier = ">=0.21.0" }, - { name = "rich", specifier = ">=13.7.1" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.8.0" }, - { name = "sqlite-vec", specifier = ">=0.1.0" }, - { name = "structlog", specifier = ">=24.4.0" }, - { name = "twine", marker = "extra == 'dev'", specifier = ">=4.0.0" }, -] -provides-extras = ["dev", "test", "docs"] - -[[package]] -name = "hanzo-s3" -version = "1.0.0" -source = { editable = "pkg/hanzo-s3" } -dependencies = [ - { name = "minio" }, -] - -[package.optional-dependencies] -dev = [ - { name = "mypy" }, - { name = "pytest" }, - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "minio", specifier = ">=7.2.0" }, - { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.10.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.5.0" }, -] -provides-extras = ["dev"] - -[[package]] -name = "hanzo-tools" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastmcp" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/3e/2d94dc54e202bdb11f6e4597dd68eebc554d2b92fffb4f6918cdf3f91fe2/hanzo_tools-0.3.0.tar.gz", hash = "sha256:d00cb3212a707e22f9bb5a21f0f9eb34a74f22ff2b5f24e2f8b6321f9880e2fb", size = 10929, upload-time = "2025-12-27T18:56:38.843Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/07/6ebbcf371aafa5f2d171de2916ef92c73978b927b34a8863af53e1b1a80b/hanzo_tools-0.3.0-py3-none-any.whl", hash = "sha256:c7b0f6f7c3089f06329bc1aaca39fbce4b7108fdbd048e2bbc450a3aff9941f2", size = 11928, upload-time = "2025-12-27T18:56:37.528Z" }, -] - -[[package]] -name = "hanzo-tools-core" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hanzo-tools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/19/fe28273c3e6de3872eebb37c76bd873e7fc9e9c290da3316bc1abac1fda2/hanzo_tools_core-0.3.0.tar.gz", hash = "sha256:7351b37c33cc0bba08fea2c697f832b4fdd1a31336ee4fc946194636ac8341c5", size = 17451, upload-time = "2026-02-21T19:58:30.28Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/36/f2/a0bdbc43b160a32dae34eac683779a5aa70d326665b758a4972101fb0b44/hanzo_tools_core-0.3.0-py3-none-any.whl", hash = "sha256:75bba9a3fb6f9203e9bd6c14fff86fdebbd50b0b7b44f6fb663ddfccbba25bf3", size = 18252, upload-time = "2026-02-21T19:58:29.008Z" }, -] - -[[package]] -name = "hanzo-tools-ui" -version = "0.2.0" -source = { editable = "pkg/hanzo-tools-ui" } -dependencies = [ - { name = "aiofiles" }, - { name = "hanzo-tools-core" }, - { name = "httpx" }, - { name = "mcp" }, -] - -[package.optional-dependencies] -dev = [ - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "ruff" }, -] -server = [ - { name = "fastapi" }, - { name = "uvicorn", extra = ["standard"] }, -] - -[package.metadata] -requires-dist = [ - { name = "aiofiles", specifier = ">=24.0.0" }, - { name = "fastapi", marker = "extra == 'server'", specifier = ">=0.115.0" }, - { name = "hanzo-tools-core", specifier = ">=0.1.0" }, - { name = "httpx", specifier = ">=0.25.0" }, - { name = "mcp", specifier = ">=1.0.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, - { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.0" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" }, - { name = "uvicorn", extras = ["standard"], marker = "extra == 'server'", specifier = ">=0.34.0" }, -] -provides-extras = ["server", "dev"] - -[[package]] -name = "hanzoai" -version = "2.2.0" -source = { editable = "." } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "h11" }, - { name = "httpx" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "typing-extensions" }, - { name = "urllib3" }, -] - -[package.optional-dependencies] -llm = [ - { name = "hanzo-llm" }, -] - -[package.dev-dependencies] -dev = [ - { name = "build" }, - { name = "dirty-equals" }, - { name = "fastapi" }, - { name = "fastembed" }, - { name = "hanzo-memory" }, - { name = "httpx" }, - { name = "importlib-metadata" }, - { name = "mypy" }, - { name = "nest-asyncio" }, - { name = "nox" }, - { name = "polars" }, - { name = "pyright" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "respx" }, - { name = "rich" }, - { name = "ruff" }, - { name = "time-machine" }, -] - -[package.metadata] -requires-dist = [ - { name = "anyio", specifier = ">=3.5.0,<5" }, - { name = "distro", specifier = ">=1.7.0,<2" }, - { name = "h11", specifier = ">=0.16.0" }, - { name = "hanzo-llm", marker = "extra == 'llm'", specifier = ">=1.0.0" }, - { name = "httpx", specifier = ">=0.23.0,<1" }, - { name = "pydantic", specifier = ">=1.9.0,<3" }, - { name = "sniffio" }, - { name = "typing-extensions", specifier = ">=4.10,<5" }, - { name = "urllib3", specifier = ">=2.6.0" }, -] -provides-extras = ["llm"] - -[package.metadata.requires-dev] -dev = [ - { name = "build", specifier = ">=1.2.2.post1" }, - { name = "dirty-equals", specifier = ">=0.6.0" }, - { name = "fastapi", specifier = ">=0.128.0" }, - { name = "fastembed", specifier = ">=0.7.4" }, - { name = "hanzo-memory", editable = "pkg/hanzo-memory" }, - { name = "httpx", specifier = ">=0.28.1" }, - { name = "importlib-metadata", specifier = ">=6.7.0" }, - { name = "mypy" }, - { name = "nest-asyncio", specifier = "==1.6.0" }, - { name = "nox" }, - { name = "polars", specifier = ">=1.36.1" }, - { name = "pyright", specifier = ">=1.1.359" }, - { name = "pytest", specifier = ">=8.4.2" }, - { name = "pytest-asyncio", specifier = ">=0.26.0,<1.0.0" }, - { name = "respx" }, - { name = "rich", specifier = ">=13.7.1" }, - { name = "ruff" }, - { name = "time-machine" }, -] - -[[package]] -name = "hf-xet" -version = "1.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/08/23c84a26716382c89151b5b447b4beb19e3345f3a93d3b73009a71a57ad3/hf_xet-1.4.2.tar.gz", hash = "sha256:b7457b6b482d9e0743bd116363239b1fa904a5e65deede350fbc0c4ea67c71ea", size = 672357, upload-time = "2026-03-13T06:58:51.077Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/06/e8cf74c3c48e5485c7acc5a990d0d8516cdfb5fdf80f799174f1287cc1b5/hf_xet-1.4.2-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:ac8202ae1e664b2c15cdfc7298cbb25e80301ae596d602ef7870099a126fcad4", size = 3796125, upload-time = "2026-03-13T06:58:33.177Z" }, - { url = "https://files.pythonhosted.org/packages/66/d4/b73ebab01cbf60777323b7de9ef05550790451eb5172a220d6b9845385ec/hf_xet-1.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6d2f8ee39fa9fba9af929f8c0d0482f8ee6e209179ad14a909b6ad78ffcb7c81", size = 3555985, upload-time = "2026-03-13T06:58:31.797Z" }, - { url = "https://files.pythonhosted.org/packages/ff/e7/ded6d1bd041c3f2bca9e913a0091adfe32371988e047dd3a68a2463c15a2/hf_xet-1.4.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4642a6cf249c09da8c1f87fe50b24b2a3450b235bf8adb55700b52f0ea6e2eb6", size = 4212085, upload-time = "2026-03-13T06:58:24.323Z" }, - { url = "https://files.pythonhosted.org/packages/97/c1/a0a44d1f98934f7bdf17f7a915b934f9fca44bb826628c553589900f6df8/hf_xet-1.4.2-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:769431385e746c92dc05492dde6f687d304584b89c33d79def8367ace06cb555", size = 3988266, upload-time = "2026-03-13T06:58:22.887Z" }, - { url = "https://files.pythonhosted.org/packages/7a/82/be713b439060e7d1f1d93543c8053d4ef2fe7e6922c5b31642eaa26f3c4b/hf_xet-1.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c9dd1c1bc4cc56168f81939b0e05b4c36dd2d28c13dc1364b17af89aa0082496", size = 4188513, upload-time = "2026-03-13T06:58:40.858Z" }, - { url = "https://files.pythonhosted.org/packages/21/a6/cbd4188b22abd80ebd0edbb2b3e87f2633e958983519980815fb8314eae5/hf_xet-1.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:fca58a2ae4e6f6755cc971ac6fcdf777ea9284d7e540e350bb000813b9a3008d", size = 4428287, upload-time = "2026-03-13T06:58:42.601Z" }, - { url = "https://files.pythonhosted.org/packages/b2/4e/84e45b25e2e3e903ed3db68d7eafa96dae9a1d1f6d0e7fc85120347a852f/hf_xet-1.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:163aab46854ccae0ab6a786f8edecbbfbaa38fcaa0184db6feceebf7000c93c0", size = 3665574, upload-time = "2026-03-13T06:58:53.881Z" }, - { url = "https://files.pythonhosted.org/packages/ee/71/c5ac2b9a7ae39c14e91973035286e73911c31980fe44e7b1d03730c00adc/hf_xet-1.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:09b138422ecbe50fd0c84d4da5ff537d27d487d3607183cd10e3e53f05188e82", size = 3528760, upload-time = "2026-03-13T06:58:52.187Z" }, - { url = "https://files.pythonhosted.org/packages/1e/0f/fcd2504015eab26358d8f0f232a1aed6b8d363a011adef83fe130bff88f7/hf_xet-1.4.2-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:949dcf88b484bb9d9276ca83f6599e4aa03d493c08fc168c124ad10b2e6f75d7", size = 3796493, upload-time = "2026-03-13T06:58:39.267Z" }, - { url = "https://files.pythonhosted.org/packages/82/56/19c25105ff81731ca6d55a188b5de2aa99d7a2644c7aa9de1810d5d3b726/hf_xet-1.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:41659966020d59eb9559c57de2cde8128b706a26a64c60f0531fa2318f409418", size = 3555797, upload-time = "2026-03-13T06:58:37.546Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/8933c073186849b5e06762aa89847991d913d10a95d1603eb7f2c3834086/hf_xet-1.4.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5c588e21d80010119458dd5d02a69093f0d115d84e3467efe71ffb2c67c19146", size = 4212127, upload-time = "2026-03-13T06:58:30.539Z" }, - { url = "https://files.pythonhosted.org/packages/eb/01/f89ebba4e369b4ed699dcb60d3152753870996f41c6d22d3d7cac01310e1/hf_xet-1.4.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a296744d771a8621ad1d50c098d7ab975d599800dae6d48528ba3944e5001ba0", size = 3987788, upload-time = "2026-03-13T06:58:29.139Z" }, - { url = "https://files.pythonhosted.org/packages/84/4d/8a53e5ffbc2cc33bbf755382ac1552c6d9af13f623ed125fe67cc3e6772f/hf_xet-1.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f563f7efe49588b7d0629d18d36f46d1658fe7e08dce3fa3d6526e1c98315e2d", size = 4188315, upload-time = "2026-03-13T06:58:48.017Z" }, - { url = "https://files.pythonhosted.org/packages/d1/b8/b7a1c1b5592254bd67050632ebbc1b42cc48588bf4757cb03c2ef87e704a/hf_xet-1.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5b2e0132c56d7ee1bf55bdb638c4b62e7106f6ac74f0b786fed499d5548c5570", size = 4428306, upload-time = "2026-03-13T06:58:49.502Z" }, - { url = "https://files.pythonhosted.org/packages/a0/0c/40779e45b20e11c7c5821a94135e0207080d6b3d76e7b78ccb413c6f839b/hf_xet-1.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:2f45c712c2fa1215713db10df6ac84b49d0e1c393465440e9cb1de73ecf7bbf6", size = 3665826, upload-time = "2026-03-13T06:58:59.88Z" }, - { url = "https://files.pythonhosted.org/packages/51/4c/e2688c8ad1760d7c30f7c429c79f35f825932581bc7c9ec811436d2f21a0/hf_xet-1.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:6d53df40616f7168abfccff100d232e9d460583b9d86fa4912c24845f192f2b8", size = 3529113, upload-time = "2026-03-13T06:58:58.491Z" }, - { url = "https://files.pythonhosted.org/packages/b4/86/b40b83a2ff03ef05c4478d2672b1fc2b9683ff870e2b25f4f3af240f2e7b/hf_xet-1.4.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:71f02d6e4cdd07f344f6844845d78518cc7186bd2bc52d37c3b73dc26a3b0bc5", size = 3800339, upload-time = "2026-03-13T06:58:36.245Z" }, - { url = "https://files.pythonhosted.org/packages/64/2e/af4475c32b4378b0e92a587adb1aa3ec53e3450fd3e5fe0372a874531c00/hf_xet-1.4.2-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:e9b38d876e94d4bdcf650778d6ebbaa791dd28de08db9736c43faff06ede1b5a", size = 3559664, upload-time = "2026-03-13T06:58:34.787Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4c/781267da3188db679e601de18112021a5cb16506fe86b246e22c5401a9c4/hf_xet-1.4.2-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:77e8c180b7ef12d8a96739a4e1e558847002afe9ea63b6f6358b2271a8bdda1c", size = 4217422, upload-time = "2026-03-13T06:58:27.472Z" }, - { url = "https://files.pythonhosted.org/packages/68/47/d6cf4a39ecf6c7705f887a46f6ef5c8455b44ad9eb0d391aa7e8a2ff7fea/hf_xet-1.4.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c3b3c6a882016b94b6c210957502ff7877802d0dbda8ad142c8595db8b944271", size = 3992847, upload-time = "2026-03-13T06:58:25.989Z" }, - { url = "https://files.pythonhosted.org/packages/2d/ef/e80815061abff54697239803948abc665c6b1d237102c174f4f7a9a5ffc5/hf_xet-1.4.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9d9a634cc929cfbaf2e1a50c0e532ae8c78fa98618426769480c58501e8c8ac2", size = 4193843, upload-time = "2026-03-13T06:58:44.59Z" }, - { url = "https://files.pythonhosted.org/packages/54/75/07f6aa680575d9646c4167db6407c41340cbe2357f5654c4e72a1b01ca14/hf_xet-1.4.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6b0932eb8b10317ea78b7da6bab172b17be03bbcd7809383d8d5abd6a2233e04", size = 4432751, upload-time = "2026-03-13T06:58:46.533Z" }, - { url = "https://files.pythonhosted.org/packages/cd/71/193eabd7e7d4b903c4aa983a215509c6114915a5a237525ec562baddb868/hf_xet-1.4.2-cp37-abi3-win_amd64.whl", hash = "sha256:ad185719fb2e8ac26f88c8100562dbf9dbdcc3d9d2add00faa94b5f106aea53f", size = 3671149, upload-time = "2026-03-13T06:58:57.07Z" }, - { url = "https://files.pythonhosted.org/packages/b4/7e/ccf239da366b37ba7f0b36095450efae4a64980bdc7ec2f51354205fdf39/hf_xet-1.4.2-cp37-abi3-win_arm64.whl", hash = "sha256:32c012286b581f783653e718c1862aea5b9eb140631685bb0c5e7012c8719a87", size = 3533426, upload-time = "2026-03-13T06:58:55.46Z" }, -] - -[[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 = "httptools" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/7f/403e5d787dc4942316e515e949b0c8a013d84078a915910e9f391ba9b3ed/httptools-0.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:38e0c83a2ea9746ebbd643bdfb521b9aa4a91703e2cd705c20443405d2fd16a5", size = 206280, upload-time = "2025-10-10T03:54:39.274Z" }, - { url = "https://files.pythonhosted.org/packages/2a/0d/7f3fd28e2ce311ccc998c388dd1c53b18120fda3b70ebb022b135dc9839b/httptools-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f25bbaf1235e27704f1a7b86cd3304eabc04f569c828101d94a0e605ef7205a5", size = 110004, upload-time = "2025-10-10T03:54:40.403Z" }, - { url = "https://files.pythonhosted.org/packages/84/a6/b3965e1e146ef5762870bbe76117876ceba51a201e18cc31f5703e454596/httptools-0.7.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c15f37ef679ab9ecc06bfc4e6e8628c32a8e4b305459de7cf6785acd57e4d03", size = 517655, upload-time = "2025-10-10T03:54:41.347Z" }, - { url = "https://files.pythonhosted.org/packages/11/7d/71fee6f1844e6fa378f2eddde6c3e41ce3a1fb4b2d81118dd544e3441ec0/httptools-0.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fe6e96090df46b36ccfaf746f03034e5ab723162bc51b0a4cf58305324036f2", size = 511440, upload-time = "2025-10-10T03:54:42.452Z" }, - { url = "https://files.pythonhosted.org/packages/22/a5/079d216712a4f3ffa24af4a0381b108aa9c45b7a5cc6eb141f81726b1823/httptools-0.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362", size = 495186, upload-time = "2025-10-10T03:54:43.937Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9e/025ad7b65278745dee3bd0ebf9314934c4592560878308a6121f7f812084/httptools-0.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e99c7b90a29fd82fea9ef57943d501a16f3404d7b9ee81799d41639bdaae412c", size = 499192, upload-time = "2025-10-10T03:54:45.003Z" }, - { url = "https://files.pythonhosted.org/packages/6d/de/40a8f202b987d43afc4d54689600ff03ce65680ede2f31df348d7f368b8f/httptools-0.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3e14f530fefa7499334a79b0cf7e7cd2992870eb893526fb097d51b4f2d0f321", size = 86694, upload-time = "2025-10-10T03:54:45.923Z" }, - { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889, upload-time = "2025-10-10T03:54:47.089Z" }, - { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180, upload-time = "2025-10-10T03:54:48.052Z" }, - { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596, upload-time = "2025-10-10T03:54:48.919Z" }, - { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268, upload-time = "2025-10-10T03:54:49.993Z" }, - { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517, upload-time = "2025-10-10T03:54:51.066Z" }, - { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337, upload-time = "2025-10-10T03:54:52.196Z" }, - { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743, upload-time = "2025-10-10T03:54:53.448Z" }, - { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" }, - { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" }, - { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" }, - { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" }, - { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" }, - { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" }, - { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" }, -] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, -] - -[[package]] -name = "huggingface-hub" -version = "1.7.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "fsspec" }, - { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, - { name = "httpx" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "tqdm" }, - { name = "typer" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/15/eafc1c57bf0f8afffb243dcd4c0cceb785e956acc17bba4d9bf2ae21fc9c/huggingface_hub-1.7.2.tar.gz", hash = "sha256:7f7e294e9bbb822e025bdb2ada025fa4344d978175a7f78e824d86e35f7ab43b", size = 724684, upload-time = "2026-03-20T10:36:08.767Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/de/3ad061a05f74728927ded48c90b73521b9a9328c85d841bdefb30e01fb85/huggingface_hub-1.7.2-py3-none-any.whl", hash = "sha256:288f33a0a17b2a73a1359e2a5fd28d1becb2c121748c6173ab8643fb342c850e", size = 618036, upload-time = "2026-03-20T10:36:06.824Z" }, -] - -[[package]] -name = "humanize" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/66/a3921783d54be8a6870ac4ccffcd15c4dc0dd7fcce51c6d63b8c63935276/humanize-4.15.0.tar.gz", hash = "sha256:1dd098483eb1c7ee8e32eb2e99ad1910baefa4b75c3aff3a82f4d78688993b10", size = 83599, upload-time = "2025-12-20T20:16:13.19Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/7b/bca5613a0c3b542420cf92bd5e5fb8ebd5435ce1011a091f66bb7693285e/humanize-4.15.0-py3-none-any.whl", hash = "sha256:b1186eb9f5a9749cd9cb8565aee77919dd7c8d076161cf44d70e59e3301e1769", size = 132203, upload-time = "2025-12-20T20:16:11.67Z" }, -] - -[[package]] -name = "id" -version = "1.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/04/c2156091427636080787aac190019dc64096e56a23b7364d3c1764ee3a06/id-1.6.1.tar.gz", hash = "sha256:d0732d624fb46fd4e7bc4e5152f00214450953b9e772c182c1c22964def1a069", size = 18088, upload-time = "2026-02-04T16:19:41.26Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/77/de194443bf38daed9452139e960c632b0ef9f9a5dd9ce605fdf18ca9f1b1/id-1.6.1-py3-none-any.whl", hash = "sha256:f5ec41ed2629a508f5d0988eda142e190c9c6da971100612c4de9ad9f9b237ca", size = 14689, upload-time = "2026-02-04T16:19:40.051Z" }, -] - -[[package]] -name = "identify" -version = "2.6.18" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/46/c4/7fb4db12296cdb11893d61c92048fe617ee853f8523b9b296ac03b43757e/identify-2.6.18.tar.gz", hash = "sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd", size = 99580, upload-time = "2026-03-15T18:39:50.319Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl", hash = "sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737", size = 99394, upload-time = "2026-03-15T18:39:48.915Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "ipdb" -version = "0.13.13" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "decorator" }, - { name = "ipython" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/1b/7e07e7b752017f7693a0f4d41c13e5ca29ce8cbcfdcc1fd6c4ad8c0a27a0/ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726", size = 17042, upload-time = "2023-03-09T15:40:57.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/4c/b075da0092003d9a55cf2ecc1cae9384a1ca4f650d51b00fc59875fe76f6/ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4", size = 12130, upload-time = "2023-03-09T15:40:55.021Z" }, -] - -[[package]] -name = "ipython" -version = "9.11.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "decorator" }, - { name = "ipython-pygments-lexers" }, - { name = "jedi" }, - { name = "matplotlib-inline" }, - { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit" }, - { name = "pygments" }, - { name = "stack-data" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/86/28/a4698eda5a8928a45d6b693578b135b753e14fa1c2b36ee9441e69a45576/ipython-9.11.0.tar.gz", hash = "sha256:2a94bc4406b22ecc7e4cb95b98450f3ea493a76bec8896cda11b78d7752a6667", size = 4427354, upload-time = "2026-03-05T08:57:30.549Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/90/45c72becc57158facc6a6404f663b77bbcea2519ca57f760e2879ae1315d/ipython-9.11.0-py3-none-any.whl", hash = "sha256:6922d5bcf944c6e525a76a0a304451b60a2b6f875e86656d8bc2dfda5d710e19", size = 624222, upload-time = "2026-03-05T08:57:28.94Z" }, -] - -[[package]] -name = "ipython-pygments-lexers" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, -] - -[[package]] -name = "jaraco-context" -version = "6.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/50/4763cd07e722bb6285316d390a164bc7e479db9d90daa769f22578f698b4/jaraco_context-6.1.2.tar.gz", hash = "sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3", size = 16801, upload-time = "2026-03-20T22:13:33.922Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/58/bc8954bda5fcda97bd7c19be11b85f91973d67a706ed4a3aec33e7de22db/jaraco_context-6.1.2-py3-none-any.whl", hash = "sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535", size = 7871, upload-time = "2026-03-20T22:13:32.808Z" }, -] - -[[package]] -name = "jaraco-functools" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, -] - -[[package]] -name = "jedi" -version = "0.19.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "parso" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, -] - -[[package]] -name = "jiter" -version = "0.13.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/5e/4ec91646aee381d01cdb9974e30882c9cd3b8c5d1079d6b5ff4af522439a/jiter-0.13.0.tar.gz", hash = "sha256:f2839f9c2c7e2dffc1bc5929a510e14ce0a946be9365fd1219e7ef342dae14f4", size = 164847, upload-time = "2026-02-02T12:37:56.441Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/30/7687e4f87086829955013ca12a9233523349767f69653ebc27036313def9/jiter-0.13.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0a2bd69fc1d902e89925fc34d1da51b2128019423d7b339a45d9e99c894e0663", size = 307958, upload-time = "2026-02-02T12:35:57.165Z" }, - { url = "https://files.pythonhosted.org/packages/c3/27/e57f9a783246ed95481e6749cc5002a8a767a73177a83c63ea71f0528b90/jiter-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f917a04240ef31898182f76a332f508f2cc4b57d2b4d7ad2dbfebbfe167eb505", size = 318597, upload-time = "2026-02-02T12:35:58.591Z" }, - { url = "https://files.pythonhosted.org/packages/cf/52/e5719a60ac5d4d7c5995461a94ad5ef962a37c8bf5b088390e6fad59b2ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e2b199f446d3e82246b4fd9236d7cb502dc2222b18698ba0d986d2fecc6152", size = 348821, upload-time = "2026-02-02T12:36:00.093Z" }, - { url = "https://files.pythonhosted.org/packages/61/db/c1efc32b8ba4c740ab3fc2d037d8753f67685f475e26b9d6536a4322bcdd/jiter-0.13.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04670992b576fa65bd056dbac0c39fe8bd67681c380cb2b48efa885711d9d726", size = 364163, upload-time = "2026-02-02T12:36:01.937Z" }, - { url = "https://files.pythonhosted.org/packages/55/8a/fb75556236047c8806995671a18e4a0ad646ed255276f51a20f32dceaeec/jiter-0.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a1aff1fbdb803a376d4d22a8f63f8e7ccbce0b4890c26cc7af9e501ab339ef0", size = 483709, upload-time = "2026-02-02T12:36:03.41Z" }, - { url = "https://files.pythonhosted.org/packages/7e/16/43512e6ee863875693a8e6f6d532e19d650779d6ba9a81593ae40a9088ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b3fb8c2053acaef8580809ac1d1f7481a0a0bdc012fd7f5d8b18fb696a5a089", size = 370480, upload-time = "2026-02-02T12:36:04.791Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4c/09b93e30e984a187bc8aaa3510e1ec8dcbdcd71ca05d2f56aac0492453aa/jiter-0.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdaba7d87e66f26a2c45d8cbadcbfc4bf7884182317907baf39cfe9775bb4d93", size = 360735, upload-time = "2026-02-02T12:36:06.994Z" }, - { url = "https://files.pythonhosted.org/packages/1a/1b/46c5e349019874ec5dfa508c14c37e29864ea108d376ae26d90bee238cd7/jiter-0.13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b88d649135aca526da172e48083da915ec086b54e8e73a425ba50999468cc08", size = 391814, upload-time = "2026-02-02T12:36:08.368Z" }, - { url = "https://files.pythonhosted.org/packages/15/9e/26184760e85baee7162ad37b7912797d2077718476bf91517641c92b3639/jiter-0.13.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e404ea551d35438013c64b4f357b0474c7abf9f781c06d44fcaf7a14c69ff9e2", size = 513990, upload-time = "2026-02-02T12:36:09.993Z" }, - { url = "https://files.pythonhosted.org/packages/e9/34/2c9355247d6debad57a0a15e76ab1566ab799388042743656e566b3b7de1/jiter-0.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f4748aad1b4a93c8bdd70f604d0f748cdc0e8744c5547798acfa52f10e79228", size = 548021, upload-time = "2026-02-02T12:36:11.376Z" }, - { url = "https://files.pythonhosted.org/packages/ac/4a/9f2c23255d04a834398b9c2e0e665382116911dc4d06b795710503cdad25/jiter-0.13.0-cp312-cp312-win32.whl", hash = "sha256:0bf670e3b1445fc4d31612199f1744f67f889ee1bbae703c4b54dc097e5dd394", size = 203024, upload-time = "2026-02-02T12:36:12.682Z" }, - { url = "https://files.pythonhosted.org/packages/09/ee/f0ae675a957ae5a8f160be3e87acea6b11dc7b89f6b7ab057e77b2d2b13a/jiter-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:15db60e121e11fe186c0b15236bd5d18381b9ddacdcf4e659feb96fc6c969c92", size = 205424, upload-time = "2026-02-02T12:36:13.93Z" }, - { url = "https://files.pythonhosted.org/packages/1b/02/ae611edf913d3cbf02c97cdb90374af2082c48d7190d74c1111dde08bcdd/jiter-0.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:41f92313d17989102f3cb5dd533a02787cdb99454d494344b0361355da52fcb9", size = 186818, upload-time = "2026-02-02T12:36:15.308Z" }, - { url = "https://files.pythonhosted.org/packages/91/9c/7ee5a6ff4b9991e1a45263bfc46731634c4a2bde27dfda6c8251df2d958c/jiter-0.13.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1f8a55b848cbabf97d861495cd65f1e5c590246fabca8b48e1747c4dfc8f85bf", size = 306897, upload-time = "2026-02-02T12:36:16.748Z" }, - { url = "https://files.pythonhosted.org/packages/7c/02/be5b870d1d2be5dd6a91bdfb90f248fbb7dcbd21338f092c6b89817c3dbf/jiter-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f556aa591c00f2c45eb1b89f68f52441a016034d18b65da60e2d2875bbbf344a", size = 317507, upload-time = "2026-02-02T12:36:18.351Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/b25d2ec333615f5f284f3a4024f7ce68cfa0604c322c6808b2344c7f5d2b/jiter-0.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7e1d61da332ec412350463891923f960c3073cf1aae93b538f0bb4c8cd46efb", size = 350560, upload-time = "2026-02-02T12:36:19.746Z" }, - { url = "https://files.pythonhosted.org/packages/be/ec/74dcb99fef0aca9fbe56b303bf79f6bd839010cb18ad41000bf6cc71eec0/jiter-0.13.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3097d665a27bc96fd9bbf7f86178037db139f319f785e4757ce7ccbf390db6c2", size = 363232, upload-time = "2026-02-02T12:36:21.243Z" }, - { url = "https://files.pythonhosted.org/packages/1b/37/f17375e0bb2f6a812d4dd92d7616e41917f740f3e71343627da9db2824ce/jiter-0.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d01ecc3a8cbdb6f25a37bd500510550b64ddf9f7d64a107d92f3ccb25035d0f", size = 483727, upload-time = "2026-02-02T12:36:22.688Z" }, - { url = "https://files.pythonhosted.org/packages/77/d2/a71160a5ae1a1e66c1395b37ef77da67513b0adba73b993a27fbe47eb048/jiter-0.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed9bbc30f5d60a3bdf63ae76beb3f9db280d7f195dfcfa61af792d6ce912d159", size = 370799, upload-time = "2026-02-02T12:36:24.106Z" }, - { url = "https://files.pythonhosted.org/packages/01/99/ed5e478ff0eb4e8aa5fd998f9d69603c9fd3f32de3bd16c2b1194f68361c/jiter-0.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fbafb6e88256f4454de33c1f40203d09fc33ed19162a68b3b257b29ca7f663", size = 359120, upload-time = "2026-02-02T12:36:25.519Z" }, - { url = "https://files.pythonhosted.org/packages/16/be/7ffd08203277a813f732ba897352797fa9493faf8dc7995b31f3d9cb9488/jiter-0.13.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5467696f6b827f1116556cb0db620440380434591e93ecee7fd14d1a491b6daa", size = 390664, upload-time = "2026-02-02T12:36:26.866Z" }, - { url = "https://files.pythonhosted.org/packages/d1/84/e0787856196d6d346264d6dcccb01f741e5f0bd014c1d9a2ebe149caf4f3/jiter-0.13.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2d08c9475d48b92892583df9da592a0e2ac49bcd41fae1fec4f39ba6cf107820", size = 513543, upload-time = "2026-02-02T12:36:28.217Z" }, - { url = "https://files.pythonhosted.org/packages/65/50/ecbd258181c4313cf79bca6c88fb63207d04d5bf5e4f65174114d072aa55/jiter-0.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:aed40e099404721d7fcaf5b89bd3b4568a4666358bcac7b6b15c09fb6252ab68", size = 547262, upload-time = "2026-02-02T12:36:29.678Z" }, - { url = "https://files.pythonhosted.org/packages/27/da/68f38d12e7111d2016cd198161b36e1f042bd115c169255bcb7ec823a3bf/jiter-0.13.0-cp313-cp313-win32.whl", hash = "sha256:36ebfbcffafb146d0e6ffb3e74d51e03d9c35ce7c625c8066cdbfc7b953bdc72", size = 200630, upload-time = "2026-02-02T12:36:31.808Z" }, - { url = "https://files.pythonhosted.org/packages/25/65/3bd1a972c9a08ecd22eb3b08a95d1941ebe6938aea620c246cf426ae09c2/jiter-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:8d76029f077379374cf0dbc78dbe45b38dec4a2eb78b08b5194ce836b2517afc", size = 202602, upload-time = "2026-02-02T12:36:33.679Z" }, - { url = "https://files.pythonhosted.org/packages/15/fe/13bd3678a311aa67686bb303654792c48206a112068f8b0b21426eb6851e/jiter-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:bb7613e1a427cfcb6ea4544f9ac566b93d5bf67e0d48c787eca673ff9c9dff2b", size = 185939, upload-time = "2026-02-02T12:36:35.065Z" }, - { url = "https://files.pythonhosted.org/packages/49/19/a929ec002ad3228bc97ca01dbb14f7632fffdc84a95ec92ceaf4145688ae/jiter-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fa476ab5dd49f3bf3a168e05f89358c75a17608dbabb080ef65f96b27c19ab10", size = 316616, upload-time = "2026-02-02T12:36:36.579Z" }, - { url = "https://files.pythonhosted.org/packages/52/56/d19a9a194afa37c1728831e5fb81b7722c3de18a3109e8f282bfc23e587a/jiter-0.13.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade8cb6ff5632a62b7dbd4757d8c5573f7a2e9ae285d6b5b841707d8363205ef", size = 346850, upload-time = "2026-02-02T12:36:38.058Z" }, - { url = "https://files.pythonhosted.org/packages/36/4a/94e831c6bf287754a8a019cb966ed39ff8be6ab78cadecf08df3bb02d505/jiter-0.13.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9950290340acc1adaded363edd94baebcee7dabdfa8bee4790794cd5cfad2af6", size = 358551, upload-time = "2026-02-02T12:36:39.417Z" }, - { url = "https://files.pythonhosted.org/packages/a2/ec/a4c72c822695fa80e55d2b4142b73f0012035d9fcf90eccc56bc060db37c/jiter-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2b4972c6df33731aac0742b64fd0d18e0a69bc7d6e03108ce7d40c85fd9e3e6d", size = 201950, upload-time = "2026-02-02T12:36:40.791Z" }, - { url = "https://files.pythonhosted.org/packages/b6/00/393553ec27b824fbc29047e9c7cd4a3951d7fbe4a76743f17e44034fa4e4/jiter-0.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:701a1e77d1e593c1b435315ff625fd071f0998c5f02792038a5ca98899261b7d", size = 185852, upload-time = "2026-02-02T12:36:42.077Z" }, - { url = "https://files.pythonhosted.org/packages/6e/f5/f1997e987211f6f9bd71b8083047b316208b4aca0b529bb5f8c96c89ef3e/jiter-0.13.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:cc5223ab19fe25e2f0bf2643204ad7318896fe3729bf12fde41b77bfc4fafff0", size = 308804, upload-time = "2026-02-02T12:36:43.496Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8f/5482a7677731fd44881f0204981ce2d7175db271f82cba2085dd2212e095/jiter-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9776ebe51713acf438fd9b4405fcd86893ae5d03487546dae7f34993217f8a91", size = 318787, upload-time = "2026-02-02T12:36:45.071Z" }, - { url = "https://files.pythonhosted.org/packages/f3/b9/7257ac59778f1cd025b26a23c5520a36a424f7f1b068f2442a5b499b7464/jiter-0.13.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:879e768938e7b49b5e90b7e3fecc0dbec01b8cb89595861fb39a8967c5220d09", size = 353880, upload-time = "2026-02-02T12:36:47.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/87/719eec4a3f0841dad99e3d3604ee4cba36af4419a76f3cb0b8e2e691ad67/jiter-0.13.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:682161a67adea11e3aae9038c06c8b4a9a71023228767477d683f69903ebc607", size = 366702, upload-time = "2026-02-02T12:36:48.871Z" }, - { url = "https://files.pythonhosted.org/packages/d2/65/415f0a75cf6921e43365a1bc227c565cb949caca8b7532776e430cbaa530/jiter-0.13.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a13b68cd1cd8cc9de8f244ebae18ccb3e4067ad205220ef324c39181e23bbf66", size = 486319, upload-time = "2026-02-02T12:36:53.006Z" }, - { url = "https://files.pythonhosted.org/packages/54/a2/9e12b48e82c6bbc6081fd81abf915e1443add1b13d8fc586e1d90bb02bb8/jiter-0.13.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87ce0f14c6c08892b610686ae8be350bf368467b6acd5085a5b65441e2bf36d2", size = 372289, upload-time = "2026-02-02T12:36:54.593Z" }, - { url = "https://files.pythonhosted.org/packages/4e/c1/e4693f107a1789a239c759a432e9afc592366f04e901470c2af89cfd28e1/jiter-0.13.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c365005b05505a90d1c47856420980d0237adf82f70c4aff7aebd3c1cc143ad", size = 360165, upload-time = "2026-02-02T12:36:56.112Z" }, - { url = "https://files.pythonhosted.org/packages/17/08/91b9ea976c1c758240614bd88442681a87672eebc3d9a6dde476874e706b/jiter-0.13.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1317fdffd16f5873e46ce27d0e0f7f4f90f0cdf1d86bf6abeaea9f63ca2c401d", size = 389634, upload-time = "2026-02-02T12:36:57.495Z" }, - { url = "https://files.pythonhosted.org/packages/18/23/58325ef99390d6d40427ed6005bf1ad54f2577866594bcf13ce55675f87d/jiter-0.13.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c05b450d37ba0c9e21c77fef1f205f56bcee2330bddca68d344baebfc55ae0df", size = 514933, upload-time = "2026-02-02T12:36:58.909Z" }, - { url = "https://files.pythonhosted.org/packages/5b/25/69f1120c7c395fd276c3996bb8adefa9c6b84c12bb7111e5c6ccdcd8526d/jiter-0.13.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:775e10de3849d0631a97c603f996f518159272db00fdda0a780f81752255ee9d", size = 548842, upload-time = "2026-02-02T12:37:00.433Z" }, - { url = "https://files.pythonhosted.org/packages/18/05/981c9669d86850c5fbb0d9e62bba144787f9fba84546ba43d624ee27ef29/jiter-0.13.0-cp314-cp314-win32.whl", hash = "sha256:632bf7c1d28421c00dd8bbb8a3bac5663e1f57d5cd5ed962bce3c73bf62608e6", size = 202108, upload-time = "2026-02-02T12:37:01.718Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/cdcf54dd0b0341db7d25413229888a346c7130bd20820530905fdb65727b/jiter-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:f22ef501c3f87ede88f23f9b11e608581c14f04db59b6a801f354397ae13739f", size = 204027, upload-time = "2026-02-02T12:37:03.075Z" }, - { url = "https://files.pythonhosted.org/packages/fb/f9/724bcaaab7a3cd727031fe4f6995cb86c4bd344909177c186699c8dec51a/jiter-0.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:07b75fe09a4ee8e0c606200622e571e44943f47254f95e2436c8bdcaceb36d7d", size = 187199, upload-time = "2026-02-02T12:37:04.414Z" }, - { url = "https://files.pythonhosted.org/packages/62/92/1661d8b9fd6a3d7a2d89831db26fe3c1509a287d83ad7838831c7b7a5c7e/jiter-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:964538479359059a35fb400e769295d4b315ae61e4105396d355a12f7fef09f0", size = 318423, upload-time = "2026-02-02T12:37:05.806Z" }, - { url = "https://files.pythonhosted.org/packages/4f/3b/f77d342a54d4ebcd128e520fc58ec2f5b30a423b0fd26acdfc0c6fef8e26/jiter-0.13.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e104da1db1c0991b3eaed391ccd650ae8d947eab1480c733e5a3fb28d4313e40", size = 351438, upload-time = "2026-02-02T12:37:07.189Z" }, - { url = "https://files.pythonhosted.org/packages/76/b3/ba9a69f0e4209bd3331470c723c2f5509e6f0482e416b612431a5061ed71/jiter-0.13.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e3a5f0cde8ff433b8e88e41aa40131455420fb3649a3c7abdda6145f8cb7202", size = 364774, upload-time = "2026-02-02T12:37:08.579Z" }, - { url = "https://files.pythonhosted.org/packages/b3/16/6cdb31fa342932602458dbb631bfbd47f601e03d2e4950740e0b2100b570/jiter-0.13.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57aab48f40be1db920a582b30b116fe2435d184f77f0e4226f546794cedd9cf0", size = 487238, upload-time = "2026-02-02T12:37:10.066Z" }, - { url = "https://files.pythonhosted.org/packages/ed/b1/956cc7abaca8d95c13aa8d6c9b3f3797241c246cd6e792934cc4c8b250d2/jiter-0.13.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7772115877c53f62beeb8fd853cab692dbc04374ef623b30f997959a4c0e7e95", size = 372892, upload-time = "2026-02-02T12:37:11.656Z" }, - { url = "https://files.pythonhosted.org/packages/26/c4/97ecde8b1e74f67b8598c57c6fccf6df86ea7861ed29da84629cdbba76c4/jiter-0.13.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1211427574b17b633cfceba5040de8081e5abf114f7a7602f73d2e16f9fdaa59", size = 360309, upload-time = "2026-02-02T12:37:13.244Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d7/eabe3cf46715854ccc80be2cd78dd4c36aedeb30751dbf85a1d08c14373c/jiter-0.13.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7beae3a3d3b5212d3a55d2961db3c292e02e302feb43fce6a3f7a31b90ea6dfe", size = 389607, upload-time = "2026-02-02T12:37:14.881Z" }, - { url = "https://files.pythonhosted.org/packages/df/2d/03963fc0804e6109b82decfb9974eb92df3797fe7222428cae12f8ccaa0c/jiter-0.13.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e5562a0f0e90a6223b704163ea28e831bd3a9faa3512a711f031611e6b06c939", size = 514986, upload-time = "2026-02-02T12:37:16.326Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/8c83b45eb3eb1c1e18d841fe30b4b5bc5619d781267ca9bc03e005d8fd0a/jiter-0.13.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:6c26a424569a59140fb51160a56df13f438a2b0967365e987889186d5fc2f6f9", size = 548756, upload-time = "2026-02-02T12:37:17.736Z" }, - { url = "https://files.pythonhosted.org/packages/47/66/eea81dfff765ed66c68fd2ed8c96245109e13c896c2a5015c7839c92367e/jiter-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:24dc96eca9f84da4131cdf87a95e6ce36765c3b156fc9ae33280873b1c32d5f6", size = 201196, upload-time = "2026-02-02T12:37:19.101Z" }, - { url = "https://files.pythonhosted.org/packages/ff/32/4ac9c7a76402f8f00d00842a7f6b83b284d0cf7c1e9d4227bc95aa6d17fa/jiter-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0a8d76c7524087272c8ae913f5d9d608bd839154b62c4322ef65723d2e5bb0b8", size = 204215, upload-time = "2026-02-02T12:37:20.495Z" }, - { url = "https://files.pythonhosted.org/packages/f9/8e/7def204fea9f9be8b3c21a6f2dd6c020cf56c7d5ff753e0e23ed7f9ea57e/jiter-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2c26cf47e2cad140fa23b6d58d435a7c0161f5c514284802f25e87fddfe11024", size = 187152, upload-time = "2026-02-02T12:37:22.124Z" }, - { url = "https://files.pythonhosted.org/packages/80/60/e50fa45dd7e2eae049f0ce964663849e897300433921198aef94b6ffa23a/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:3d744a6061afba08dd7ae375dcde870cffb14429b7477e10f67e9e6d68772a0a", size = 305169, upload-time = "2026-02-02T12:37:50.376Z" }, - { url = "https://files.pythonhosted.org/packages/d2/73/a009f41c5eed71c49bec53036c4b33555afcdee70682a18c6f66e396c039/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:ff732bd0a0e778f43d5009840f20b935e79087b4dc65bd36f1cd0f9b04b8ff7f", size = 303808, upload-time = "2026-02-02T12:37:52.092Z" }, - { url = "https://files.pythonhosted.org/packages/c4/10/528b439290763bff3d939268085d03382471b442f212dca4ff5f12802d43/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab44b178f7981fcaea7e0a5df20e773c663d06ffda0198f1a524e91b2fde7e59", size = 337384, upload-time = "2026-02-02T12:37:53.582Z" }, - { url = "https://files.pythonhosted.org/packages/67/8a/a342b2f0251f3dac4ca17618265d93bf244a2a4d089126e81e4c1056ac50/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb00b6d26db67a05fe3e12c76edc75f32077fb51deed13822dc648fa373bc19", size = 343768, upload-time = "2026-02-02T12:37:55.055Z" }, -] - -[[package]] -name = "jsonref" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[package]] -name = "jsonschema-path" -version = "0.4.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pathable" }, - { name = "pyyaml" }, - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5b/8a/7e6102f2b8bdc6705a9eb5294f8f6f9ccd3a8420e8e8e19671d1dd773251/jsonschema_path-0.4.5.tar.gz", hash = "sha256:c6cd7d577ae290c7defd4f4029e86fdb248ca1bd41a07557795b3c95e5144918", size = 15113, upload-time = "2026-03-03T09:56:46.87Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/d5/4e96c44f6c1ea3d812cf5391d81a4f5abaa540abf8d04ecd7f66e0ed11df/jsonschema_path-0.4.5-py3-none-any.whl", hash = "sha256:7d77a2c3f3ec569a40efe5c5f942c44c1af2a6f96fe0866794c9ef5b8f87fd65", size = 19368, upload-time = "2026-03-03T09:56:45.39Z" }, -] - -[[package]] -name = "jsonschema-specifications" -version = "2025.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "keyring" -version = "25.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, -] - -[[package]] -name = "librt" -version = "0.8.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/56/9c/b4b0c54d84da4a94b37bd44151e46d5e583c9534c7e02250b961b1b6d8a8/librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73", size = 177471, upload-time = "2026-02-17T16:13:06.101Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/21/d39b0a87ac52fc98f621fb6f8060efb017a767ebbbac2f99fbcbc9ddc0d7/librt-0.8.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a", size = 66516, upload-time = "2026-02-17T16:11:41.604Z" }, - { url = "https://files.pythonhosted.org/packages/69/f1/46375e71441c43e8ae335905e069f1c54febee63a146278bcee8782c84fd/librt-0.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9", size = 68634, upload-time = "2026-02-17T16:11:43.268Z" }, - { url = "https://files.pythonhosted.org/packages/0a/33/c510de7f93bf1fa19e13423a606d8189a02624a800710f6e6a0a0f0784b3/librt-0.8.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb", size = 198941, upload-time = "2026-02-17T16:11:44.28Z" }, - { url = "https://files.pythonhosted.org/packages/dd/36/e725903416409a533d92398e88ce665476f275081d0d7d42f9c4951999e5/librt-0.8.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d", size = 209991, upload-time = "2026-02-17T16:11:45.462Z" }, - { url = "https://files.pythonhosted.org/packages/30/7a/8d908a152e1875c9f8eac96c97a480df425e657cdb47854b9efaa4998889/librt-0.8.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7", size = 224476, upload-time = "2026-02-17T16:11:46.542Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b8/a22c34f2c485b8903a06f3fe3315341fe6876ef3599792344669db98fcff/librt-0.8.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440", size = 217518, upload-time = "2026-02-17T16:11:47.746Z" }, - { url = "https://files.pythonhosted.org/packages/79/6f/5c6fea00357e4f82ba44f81dbfb027921f1ab10e320d4a64e1c408d035d9/librt-0.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9", size = 225116, upload-time = "2026-02-17T16:11:49.298Z" }, - { url = "https://files.pythonhosted.org/packages/f2/a0/95ced4e7b1267fe1e2720a111685bcddf0e781f7e9e0ce59d751c44dcfe5/librt-0.8.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972", size = 217751, upload-time = "2026-02-17T16:11:50.49Z" }, - { url = "https://files.pythonhosted.org/packages/93/c2/0517281cb4d4101c27ab59472924e67f55e375bc46bedae94ac6dc6e1902/librt-0.8.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921", size = 218378, upload-time = "2026-02-17T16:11:51.783Z" }, - { url = "https://files.pythonhosted.org/packages/43/e8/37b3ac108e8976888e559a7b227d0ceac03c384cfd3e7a1c2ee248dbae79/librt-0.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0", size = 241199, upload-time = "2026-02-17T16:11:53.561Z" }, - { url = "https://files.pythonhosted.org/packages/4b/5b/35812d041c53967fedf551a39399271bbe4257e681236a2cf1a69c8e7fa1/librt-0.8.1-cp312-cp312-win32.whl", hash = "sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a", size = 54917, upload-time = "2026-02-17T16:11:54.758Z" }, - { url = "https://files.pythonhosted.org/packages/de/d1/fa5d5331b862b9775aaf2a100f5ef86854e5d4407f71bddf102f4421e034/librt-0.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444", size = 62017, upload-time = "2026-02-17T16:11:55.748Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7c/c614252f9acda59b01a66e2ddfd243ed1c7e1deab0293332dfbccf862808/librt-0.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d", size = 52441, upload-time = "2026-02-17T16:11:56.801Z" }, - { url = "https://files.pythonhosted.org/packages/c5/3c/f614c8e4eaac7cbf2bbdf9528790b21d89e277ee20d57dc6e559c626105f/librt-0.8.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35", size = 66529, upload-time = "2026-02-17T16:11:57.809Z" }, - { url = "https://files.pythonhosted.org/packages/ab/96/5836544a45100ae411eda07d29e3d99448e5258b6e9c8059deb92945f5c2/librt-0.8.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583", size = 68669, upload-time = "2026-02-17T16:11:58.843Z" }, - { url = "https://files.pythonhosted.org/packages/06/53/f0b992b57af6d5531bf4677d75c44f095f2366a1741fb695ee462ae04b05/librt-0.8.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c", size = 199279, upload-time = "2026-02-17T16:11:59.862Z" }, - { url = "https://files.pythonhosted.org/packages/f3/ad/4848cc16e268d14280d8168aee4f31cea92bbd2b79ce33d3e166f2b4e4fc/librt-0.8.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04", size = 210288, upload-time = "2026-02-17T16:12:00.954Z" }, - { url = "https://files.pythonhosted.org/packages/52/05/27fdc2e95de26273d83b96742d8d3b7345f2ea2bdbd2405cc504644f2096/librt-0.8.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363", size = 224809, upload-time = "2026-02-17T16:12:02.108Z" }, - { url = "https://files.pythonhosted.org/packages/7a/d0/78200a45ba3240cb042bc597d6f2accba9193a2c57d0356268cbbe2d0925/librt-0.8.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0", size = 218075, upload-time = "2026-02-17T16:12:03.631Z" }, - { url = "https://files.pythonhosted.org/packages/af/72/a210839fa74c90474897124c064ffca07f8d4b347b6574d309686aae7ca6/librt-0.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012", size = 225486, upload-time = "2026-02-17T16:12:04.725Z" }, - { url = "https://files.pythonhosted.org/packages/a3/c1/a03cc63722339ddbf087485f253493e2b013039f5b707e8e6016141130fa/librt-0.8.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb", size = 218219, upload-time = "2026-02-17T16:12:05.828Z" }, - { url = "https://files.pythonhosted.org/packages/58/f5/fff6108af0acf941c6f274a946aea0e484bd10cd2dc37610287ce49388c5/librt-0.8.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b", size = 218750, upload-time = "2026-02-17T16:12:07.09Z" }, - { url = "https://files.pythonhosted.org/packages/71/67/5a387bfef30ec1e4b4f30562c8586566faf87e47d696768c19feb49e3646/librt-0.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d", size = 241624, upload-time = "2026-02-17T16:12:08.43Z" }, - { url = "https://files.pythonhosted.org/packages/d4/be/24f8502db11d405232ac1162eb98069ca49c3306c1d75c6ccc61d9af8789/librt-0.8.1-cp313-cp313-win32.whl", hash = "sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a", size = 54969, upload-time = "2026-02-17T16:12:09.633Z" }, - { url = "https://files.pythonhosted.org/packages/5c/73/c9fdf6cb2a529c1a092ce769a12d88c8cca991194dfe641b6af12fa964d2/librt-0.8.1-cp313-cp313-win_amd64.whl", hash = "sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79", size = 62000, upload-time = "2026-02-17T16:12:10.632Z" }, - { url = "https://files.pythonhosted.org/packages/d3/97/68f80ca3ac4924f250cdfa6e20142a803e5e50fca96ef5148c52ee8c10ea/librt-0.8.1-cp313-cp313-win_arm64.whl", hash = "sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0", size = 52495, upload-time = "2026-02-17T16:12:11.633Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6a/907ef6800f7bca71b525a05f1839b21f708c09043b1c6aa77b6b827b3996/librt-0.8.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f", size = 66081, upload-time = "2026-02-17T16:12:12.766Z" }, - { url = "https://files.pythonhosted.org/packages/1b/18/25e991cd5640c9fb0f8d91b18797b29066b792f17bf8493da183bf5caabe/librt-0.8.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c", size = 68309, upload-time = "2026-02-17T16:12:13.756Z" }, - { url = "https://files.pythonhosted.org/packages/a4/36/46820d03f058cfb5a9de5940640ba03165ed8aded69e0733c417bb04df34/librt-0.8.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc", size = 196804, upload-time = "2026-02-17T16:12:14.818Z" }, - { url = "https://files.pythonhosted.org/packages/59/18/5dd0d3b87b8ff9c061849fbdb347758d1f724b9a82241aa908e0ec54ccd0/librt-0.8.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c", size = 206907, upload-time = "2026-02-17T16:12:16.513Z" }, - { url = "https://files.pythonhosted.org/packages/d1/96/ef04902aad1424fd7299b62d1890e803e6ab4018c3044dca5922319c4b97/librt-0.8.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3", size = 221217, upload-time = "2026-02-17T16:12:17.906Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ff/7e01f2dda84a8f5d280637a2e5827210a8acca9a567a54507ef1c75b342d/librt-0.8.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14", size = 214622, upload-time = "2026-02-17T16:12:19.108Z" }, - { url = "https://files.pythonhosted.org/packages/1e/8c/5b093d08a13946034fed57619742f790faf77058558b14ca36a6e331161e/librt-0.8.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7", size = 221987, upload-time = "2026-02-17T16:12:20.331Z" }, - { url = "https://files.pythonhosted.org/packages/d3/cc/86b0b3b151d40920ad45a94ce0171dec1aebba8a9d72bb3fa00c73ab25dd/librt-0.8.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6", size = 215132, upload-time = "2026-02-17T16:12:21.54Z" }, - { url = "https://files.pythonhosted.org/packages/fc/be/8588164a46edf1e69858d952654e216a9a91174688eeefb9efbb38a9c799/librt-0.8.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071", size = 215195, upload-time = "2026-02-17T16:12:23.073Z" }, - { url = "https://files.pythonhosted.org/packages/f5/f2/0b9279bea735c734d69344ecfe056c1ba211694a72df10f568745c899c76/librt-0.8.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78", size = 237946, upload-time = "2026-02-17T16:12:24.275Z" }, - { url = "https://files.pythonhosted.org/packages/e9/cc/5f2a34fbc8aeb35314a3641f9956fa9051a947424652fad9882be7a97949/librt-0.8.1-cp314-cp314-win32.whl", hash = "sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023", size = 50689, upload-time = "2026-02-17T16:12:25.766Z" }, - { url = "https://files.pythonhosted.org/packages/a0/76/cd4d010ab2147339ca2b93e959c3686e964edc6de66ddacc935c325883d7/librt-0.8.1-cp314-cp314-win_amd64.whl", hash = "sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730", size = 57875, upload-time = "2026-02-17T16:12:27.465Z" }, - { url = "https://files.pythonhosted.org/packages/84/0f/2143cb3c3ca48bd3379dcd11817163ca50781927c4537345d608b5045998/librt-0.8.1-cp314-cp314-win_arm64.whl", hash = "sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3", size = 48058, upload-time = "2026-02-17T16:12:28.556Z" }, - { url = "https://files.pythonhosted.org/packages/d2/0e/9b23a87e37baf00311c3efe6b48d6b6c168c29902dfc3f04c338372fd7db/librt-0.8.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1", size = 68313, upload-time = "2026-02-17T16:12:29.659Z" }, - { url = "https://files.pythonhosted.org/packages/db/9a/859c41e5a4f1c84200a7d2b92f586aa27133c8243b6cac9926f6e54d01b9/librt-0.8.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee", size = 70994, upload-time = "2026-02-17T16:12:31.516Z" }, - { url = "https://files.pythonhosted.org/packages/4c/28/10605366ee599ed34223ac2bf66404c6fb59399f47108215d16d5ad751a8/librt-0.8.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7", size = 220770, upload-time = "2026-02-17T16:12:33.294Z" }, - { url = "https://files.pythonhosted.org/packages/af/8d/16ed8fd452dafae9c48d17a6bc1ee3e818fd40ef718d149a8eff2c9f4ea2/librt-0.8.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040", size = 235409, upload-time = "2026-02-17T16:12:35.443Z" }, - { url = "https://files.pythonhosted.org/packages/89/1b/7bdf3e49349c134b25db816e4a3db6b94a47ac69d7d46b1e682c2c4949be/librt-0.8.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e", size = 246473, upload-time = "2026-02-17T16:12:36.656Z" }, - { url = "https://files.pythonhosted.org/packages/4e/8a/91fab8e4fd2a24930a17188c7af5380eb27b203d72101c9cc000dbdfd95a/librt-0.8.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732", size = 238866, upload-time = "2026-02-17T16:12:37.849Z" }, - { url = "https://files.pythonhosted.org/packages/b9/e0/c45a098843fc7c07e18a7f8a24ca8496aecbf7bdcd54980c6ca1aaa79a8e/librt-0.8.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624", size = 250248, upload-time = "2026-02-17T16:12:39.445Z" }, - { url = "https://files.pythonhosted.org/packages/82/30/07627de23036640c952cce0c1fe78972e77d7d2f8fd54fa5ef4554ff4a56/librt-0.8.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4", size = 240629, upload-time = "2026-02-17T16:12:40.889Z" }, - { url = "https://files.pythonhosted.org/packages/fb/c1/55bfe1ee3542eba055616f9098eaf6eddb966efb0ca0f44eaa4aba327307/librt-0.8.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382", size = 239615, upload-time = "2026-02-17T16:12:42.446Z" }, - { url = "https://files.pythonhosted.org/packages/2b/39/191d3d28abc26c9099b19852e6c99f7f6d400b82fa5a4e80291bd3803e19/librt-0.8.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994", size = 263001, upload-time = "2026-02-17T16:12:43.627Z" }, - { url = "https://files.pythonhosted.org/packages/b9/eb/7697f60fbe7042ab4e88f4ee6af496b7f222fffb0a4e3593ef1f29f81652/librt-0.8.1-cp314-cp314t-win32.whl", hash = "sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a", size = 51328, upload-time = "2026-02-17T16:12:45.148Z" }, - { url = "https://files.pythonhosted.org/packages/7c/72/34bf2eb7a15414a23e5e70ecb9440c1d3179f393d9349338a91e2781c0fb/librt-0.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4", size = 58722, upload-time = "2026-02-17T16:12:46.85Z" }, - { url = "https://files.pythonhosted.org/packages/b2/c8/d148e041732d631fc76036f8b30fae4e77b027a1e95b7a84bb522481a940/librt-0.8.1-cp314-cp314t-win_arm64.whl", hash = "sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61", size = 48755, upload-time = "2026-02-17T16:12:47.943Z" }, -] - -[[package]] -name = "loguru" -version = "0.7.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "win32-setctime", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, -] - -[[package]] -name = "markdown" -version = "3.10.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, -] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, - { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, - { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, - { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, - { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, - { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, - { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, - { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, - { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, - { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, - { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, - { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, - { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, - { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, - { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, - { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, - { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, - { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, - { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, - { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, - { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, - { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, - { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, - { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, - { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, - { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, - { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, - { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, - { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, - { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, - { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, - { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, - { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, - { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, - { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, - { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, - { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, - { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, - { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, - { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, - { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, - { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, - { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, - { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, - { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, -] - -[[package]] -name = "matplotlib-inline" -version = "0.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, -] - -[[package]] -name = "mcp" -version = "1.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyjwt", extra = ["crypto"] }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, -] - -[[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 = "mergedeep" -version = "1.3.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, -] - -[[package]] -name = "minio" -version = "7.2.20" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "argon2-cffi" }, - { name = "certifi" }, - { name = "pycryptodome" }, - { name = "typing-extensions" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/40/df/6dfc6540f96a74125a11653cce717603fd5b7d0001a8e847b3e54e72d238/minio-7.2.20.tar.gz", hash = "sha256:95898b7a023fbbfde375985aa77e2cd6a0762268db79cf886f002a9ea8e68598", size = 136113, upload-time = "2025-11-27T00:37:15.569Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/9a/b697530a882588a84db616580f2ba5d1d515c815e11c30d219145afeec87/minio-7.2.20-py3-none-any.whl", hash = "sha256:eb33dd2fb80e04c3726a76b13241c6be3c4c46f8d81e1d58e757786f6501897e", size = 93751, upload-time = "2025-11-27T00:37:13.993Z" }, -] - -[[package]] -name = "mkdocs" -version = "1.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "ghp-import" }, - { name = "jinja2" }, - { name = "markdown" }, - { name = "markupsafe" }, - { name = "mergedeep" }, - { name = "mkdocs-get-deps" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "pyyaml" }, - { name = "pyyaml-env-tag" }, - { name = "watchdog" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, -] - -[[package]] -name = "mkdocs-autorefs" -version = "1.4.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown" }, - { name = "markupsafe" }, - { name = "mkdocs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/52/c0/f641843de3f612a6b48253f39244165acff36657a91cc903633d456ae1ac/mkdocs_autorefs-1.4.4.tar.gz", hash = "sha256:d54a284f27a7346b9c38f1f852177940c222da508e66edc816a0fa55fc6da197", size = 56588, upload-time = "2026-02-10T15:23:55.105Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl", hash = "sha256:834ef5408d827071ad1bc69e0f39704fa34c7fc05bc8e1c72b227dfdc5c76089", size = 25530, upload-time = "2026-02-10T15:23:53.817Z" }, -] - -[[package]] -name = "mkdocs-get-deps" -version = "0.2.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mergedeep" }, - { name = "platformdirs" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ce/25/b3cccb187655b9393572bde9b09261d267c3bf2f2cdabe347673be5976a6/mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1", size = 11047, upload-time = "2026-03-10T02:46:33.632Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl", hash = "sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650", size = 9555, upload-time = "2026-03-10T02:46:32.256Z" }, -] - -[[package]] -name = "mkdocs-material" -version = "9.7.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "babel" }, - { name = "backrefs" }, - { name = "colorama" }, - { name = "jinja2" }, - { name = "markdown" }, - { name = "mkdocs" }, - { name = "mkdocs-material-extensions" }, - { name = "paginate" }, - { name = "pygments" }, - { name = "pymdown-extensions" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/45/29/6d2bcf41ae40802c4beda2432396fff97b8456fb496371d1bc7aad6512ec/mkdocs_material-9.7.6.tar.gz", hash = "sha256:00bdde50574f776d328b1862fe65daeaf581ec309bd150f7bff345a098c64a69", size = 4097959, upload-time = "2026-03-19T15:41:58.161Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl", hash = "sha256:71b84353921b8ea1ba84fe11c50912cc512da8fe0881038fcc9a0761c0e635ba", size = 9305470, upload-time = "2026-03-19T15:41:55.217Z" }, -] - -[[package]] -name = "mkdocs-material-extensions" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" }, -] - -[[package]] -name = "mkdocstrings" -version = "1.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "markdown" }, - { name = "markupsafe" }, - { name = "mkdocs" }, - { name = "mkdocs-autorefs" }, - { name = "pymdown-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/46/62/0dfc5719514115bf1781f44b1d7f2a0923fcc01e9c5d7990e48a05c9ae5d/mkdocstrings-1.0.3.tar.gz", hash = "sha256:ab670f55040722b49bb45865b2e93b824450fb4aef638b00d7acb493a9020434", size = 100946, upload-time = "2026-02-07T14:31:40.973Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl", hash = "sha256:0d66d18430c2201dc7fe85134277382baaa15e6b30979f3f3bdbabd6dbdb6046", size = 35523, upload-time = "2026-02-07T14:31:39.27Z" }, -] - -[package.optional-dependencies] -python = [ - { name = "mkdocstrings-python" }, -] - -[[package]] -name = "mkdocstrings-python" -version = "2.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "griffelib" }, - { name = "mkdocs-autorefs" }, - { name = "mkdocstrings" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/29/33/c225eaf898634bdda489a6766fc35d1683c640bffe0e0acd10646b13536d/mkdocstrings_python-2.0.3.tar.gz", hash = "sha256:c518632751cc869439b31c9d3177678ad2bfa5c21b79b863956ad68fc92c13b8", size = 199083, upload-time = "2026-02-20T10:38:36.368Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl", hash = "sha256:0b83513478bdfd803ff05aa43e9b1fca9dd22bcd9471f09ca6257f009bc5ee12", size = 104779, upload-time = "2026-02-20T10:38:34.517Z" }, -] - -[[package]] -name = "mmh3" -version = "5.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/91/1a/edb23803a168f070ded7a3014c6d706f63b90c84ccc024f89d794a3b7a6d/mmh3-5.2.1.tar.gz", hash = "sha256:bbea5b775f0ac84945191fb83f845a6fd9a21a03ea7f2e187defac7e401616ad", size = 33775, upload-time = "2026-03-05T15:55:57.716Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/94/bc5c3b573b40a328c4d141c20e399039ada95e5e2a661df3425c5165fd84/mmh3-5.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0cc21533878e5586b80d74c281d7f8da7932bc8ace50b8d5f6dbf7e3935f63f1", size = 56087, upload-time = "2026-03-05T15:54:21.92Z" }, - { url = "https://files.pythonhosted.org/packages/f6/80/64a02cc3e95c3af0aaa2590849d9ed24a9f14bb93537addde688e039b7c3/mmh3-5.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4eda76074cfca2787c8cf1bec603eaebdddd8b061ad5502f85cddae998d54f00", size = 40500, upload-time = "2026-03-05T15:54:22.953Z" }, - { url = "https://files.pythonhosted.org/packages/8b/72/e6d6602ce18adf4ddcd0e48f2e13590cc92a536199e52109f46f259d3c46/mmh3-5.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:eee884572b06bbe8a2b54f424dbd996139442cf83c76478e1ec162512e0dd2c7", size = 40034, upload-time = "2026-03-05T15:54:23.943Z" }, - { url = "https://files.pythonhosted.org/packages/59/c2/bf4537a8e58e21886ef16477041238cab5095c836496e19fafc34b7445d2/mmh3-5.2.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0d0b7e803191db5f714d264044e06189c8ccd3219e936cc184f07106bd17fd7b", size = 97292, upload-time = "2026-03-05T15:54:25.335Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e2/51ed62063b44d10b06d975ac87af287729eeb5e3ed9772f7584a17983e90/mmh3-5.2.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8e6c219e375f6341d0959af814296372d265a8ca1af63825f65e2e87c618f006", size = 103274, upload-time = "2026-03-05T15:54:26.44Z" }, - { url = "https://files.pythonhosted.org/packages/75/ce/12a7524dca59eec92e5b31fdb13ede1e98eda277cf2b786cf73bfbc24e81/mmh3-5.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:26fb5b9c3946bf7f1daed7b37e0c03898a6f062149127570f8ede346390a0825", size = 106158, upload-time = "2026-03-05T15:54:28.578Z" }, - { url = "https://files.pythonhosted.org/packages/86/1f/d3ba6dd322d01ab5d44c46c8f0c38ab6bbbf9b5e20e666dfc05bf4a23604/mmh3-5.2.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3c38d142c706201db5b2345166eeef1e7740e3e2422b470b8ba5c8727a9b4c7a", size = 113005, upload-time = "2026-03-05T15:54:29.767Z" }, - { url = "https://files.pythonhosted.org/packages/b6/a9/15d6b6f913294ea41b44d901741298e3718e1cb89ee626b3694625826a43/mmh3-5.2.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50885073e2909251d4718634a191c49ae5f527e5e1736d738e365c3e8be8f22b", size = 120744, upload-time = "2026-03-05T15:54:30.931Z" }, - { url = "https://files.pythonhosted.org/packages/76/b3/70b73923fd0284c439860ff5c871b20210dfdbe9a6b9dd0ee6496d77f174/mmh3-5.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b3f99e1756fc48ad507b95e5d86f2fb21b3d495012ff13e6592ebac14033f166", size = 99111, upload-time = "2026-03-05T15:54:32.353Z" }, - { url = "https://files.pythonhosted.org/packages/dd/38/99f7f75cd27d10d8b899a1caafb9d531f3903e4d54d572220e3d8ac35e89/mmh3-5.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62815d2c67f2dd1be76a253d88af4e1da19aeaa1820146dec52cf8bee2958b16", size = 98623, upload-time = "2026-03-05T15:54:33.801Z" }, - { url = "https://files.pythonhosted.org/packages/fd/68/6e292c0853e204c44d2f03ea5f090be3317a0e2d9417ecb62c9eb27687df/mmh3-5.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8f767ba0911602ddef289404e33835a61168314ebd3c729833db2ed685824211", size = 106437, upload-time = "2026-03-05T15:54:35.177Z" }, - { url = "https://files.pythonhosted.org/packages/dd/c6/fedd7284c459cfb58721d461fcf5607a4c1f5d9ab195d113d51d10164d16/mmh3-5.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:67e41a497bac88cc1de96eeba56eeb933c39d54bc227352f8455aa87c4ca4000", size = 110002, upload-time = "2026-03-05T15:54:36.673Z" }, - { url = "https://files.pythonhosted.org/packages/3b/ac/ca8e0c19a34f5b71390171d2ff0b9f7f187550d66801a731bb68925126a4/mmh3-5.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d74a03fb57757ece25aa4b3c1c60157a1cece37a020542785f942e2f827eed5", size = 97507, upload-time = "2026-03-05T15:54:37.804Z" }, - { url = "https://files.pythonhosted.org/packages/df/94/6ebb9094cfc7ac5e7950776b9d13a66bb4a34f83814f32ba2abc9494fc68/mmh3-5.2.1-cp312-cp312-win32.whl", hash = "sha256:7374d6e3ef72afe49697ecd683f3da12f4fc06af2d75433d0580c6746d2fa025", size = 40773, upload-time = "2026-03-05T15:54:40.077Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/cd3527198cf159495966551c84a5f36805a10ac17b294f41f67b83f6a4d6/mmh3-5.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:3a9fed49c6ce4ed7e73f13182760c65c816da006debe67f37635580dfb0fae00", size = 41560, upload-time = "2026-03-05T15:54:41.148Z" }, - { url = "https://files.pythonhosted.org/packages/15/96/6fe5ebd0f970a076e3ed5512871ce7569447b962e96c125528a2f9724470/mmh3-5.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:bbfcb95d9a744e6e2827dfc66ad10e1020e0cac255eb7f85652832d5a264c2fc", size = 39313, upload-time = "2026-03-05T15:54:42.171Z" }, - { url = "https://files.pythonhosted.org/packages/25/a5/9daa0508a1569a54130f6198d5462a92deda870043624aa3ea72721aa765/mmh3-5.2.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:723b2681ed4cc07d3401bbea9c201ad4f2a4ca6ba8cddaff6789f715dd2b391e", size = 40832, upload-time = "2026-03-05T15:54:43.212Z" }, - { url = "https://files.pythonhosted.org/packages/0a/6b/3230c6d80c1f4b766dedf280a92c2241e99f87c1504ff74205ec8cebe451/mmh3-5.2.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:3619473a0e0d329fd4aec8075628f8f616be2da41605300696206d6f36920c3d", size = 41964, upload-time = "2026-03-05T15:54:44.204Z" }, - { url = "https://files.pythonhosted.org/packages/62/fb/648bfddb74a872004b6ee751551bfdda783fe6d70d2e9723bad84dbe5311/mmh3-5.2.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e48d4dbe0f88e53081da605ae68644e5182752803bbc2beb228cca7f1c4454d6", size = 39114, upload-time = "2026-03-05T15:54:45.205Z" }, - { url = "https://files.pythonhosted.org/packages/95/c2/ab7901f87af438468b496728d11264cb397b3574d41506e71b92128e0373/mmh3-5.2.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a482ac121de6973897c92c2f31defc6bafb11c83825109275cffce54bb64933f", size = 39819, upload-time = "2026-03-05T15:54:46.509Z" }, - { url = "https://files.pythonhosted.org/packages/2f/ed/6f88dda0df67de1612f2e130ffea34cf84aaee5bff5b0aff4dbff2babe34/mmh3-5.2.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:17fbb47f0885ace8327ce1235d0416dc86a211dcd8cc1e703f41523be32cfec8", size = 40330, upload-time = "2026-03-05T15:54:47.864Z" }, - { url = "https://files.pythonhosted.org/packages/3d/66/7516d23f53cdf90f43fce24ab80c28f45e6851d78b46bef8c02084edf583/mmh3-5.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d51fde50a77f81330523562e3c2734ffdca9c4c9e9d355478117905e1cfe16c6", size = 56078, upload-time = "2026-03-05T15:54:48.9Z" }, - { url = "https://files.pythonhosted.org/packages/bc/34/4d152fdf4a91a132cb226b671f11c6b796eada9ab78080fb5ce1e95adaab/mmh3-5.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:19bbd3b841174ae6ed588536ab5e1b1fe83d046e668602c20266547298d939a9", size = 40498, upload-time = "2026-03-05T15:54:49.942Z" }, - { url = "https://files.pythonhosted.org/packages/d4/4c/8e3af1b6d85a299767ec97bd923f12b06267089c1472c27c1696870d1175/mmh3-5.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be77c402d5e882b6fbacfd90823f13da8e0a69658405a39a569c6b58fdb17b03", size = 40033, upload-time = "2026-03-05T15:54:50.994Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f2/966ea560e32578d453c9e9db53d602cbb1d0da27317e232afa7c38ceba11/mmh3-5.2.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fd96476f04db5ceba1cfa0f21228f67c1f7402296f0e73fee3513aa680ad237b", size = 97320, upload-time = "2026-03-05T15:54:52.072Z" }, - { url = "https://files.pythonhosted.org/packages/bb/0d/2c5f9893b38aeb6b034d1a44ecd55a010148054f6a516abe53b5e4057297/mmh3-5.2.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:707151644085dd0f20fe4f4b573d28e5130c4aaa5f587e95b60989c5926653b5", size = 103299, upload-time = "2026-03-05T15:54:53.569Z" }, - { url = "https://files.pythonhosted.org/packages/1c/fc/2ebaef4a4d4376f89761274dc274035ffd96006ab496b4ee5af9b08f21a9/mmh3-5.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3737303ca9ea0f7cb83028781148fcda4f1dac7821db0c47672971dabcf63593", size = 106222, upload-time = "2026-03-05T15:54:55.092Z" }, - { url = "https://files.pythonhosted.org/packages/57/09/ea7ffe126d0ba0406622602a2d05e1e1a6841cc92fc322eb576c95b27fad/mmh3-5.2.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2778fed822d7db23ac5008b181441af0c869455b2e7d001f4019636ac31b6fe4", size = 113048, upload-time = "2026-03-05T15:54:56.305Z" }, - { url = "https://files.pythonhosted.org/packages/85/57/9447032edf93a64aa9bef4d9aa596400b1756f40411890f77a284f6293ca/mmh3-5.2.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d57dea657357230cc780e13920d7fa7db059d58fe721c80020f94476da4ca0a1", size = 120742, upload-time = "2026-03-05T15:54:57.453Z" }, - { url = "https://files.pythonhosted.org/packages/53/82/a86cc87cc88c92e9e1a598fee509f0409435b57879a6129bf3b3e40513c7/mmh3-5.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:169e0d178cb59314456ab30772429a802b25d13227088085b0d49b9fe1533104", size = 99132, upload-time = "2026-03-05T15:54:58.583Z" }, - { url = "https://files.pythonhosted.org/packages/54/f7/6b16eb1b40ee89bb740698735574536bc20d6cdafc65ae702ea235578e05/mmh3-5.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7e4e1f580033335c6f76d1e0d6b56baf009d1a64d6a4816347e4271ba951f46d", size = 98686, upload-time = "2026-03-05T15:55:00.078Z" }, - { url = "https://files.pythonhosted.org/packages/e8/88/a601e9f32ad1410f438a6d0544298ea621f989bd34a0731a7190f7dec799/mmh3-5.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2bd9f19f7f1fcebd74e830f4af0f28adad4975d40d80620be19ffb2b2af56c9f", size = 106479, upload-time = "2026-03-05T15:55:01.532Z" }, - { url = "https://files.pythonhosted.org/packages/d6/5c/ce29ae3dfc4feec4007a437a1b7435fb9507532a25147602cd5b52be86db/mmh3-5.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c88653877aeb514c089d1b3d473451677b8b9a6d1497dbddf1ae7934518b06d2", size = 110030, upload-time = "2026-03-05T15:55:02.934Z" }, - { url = "https://files.pythonhosted.org/packages/13/30/ae444ef2ff87c805d525da4fa63d27cda4fe8a48e77003a036b8461cfd5c/mmh3-5.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fceef7fe67c81e1585198215e42ad3fdba3a25644beda8fbdaf85f4d7b93175a", size = 97536, upload-time = "2026-03-05T15:55:04.135Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f9/dc3787ee5c813cc27fe79f45ad4500d9b5437f23a7402435cc34e07c7718/mmh3-5.2.1-cp313-cp313-win32.whl", hash = "sha256:54b64fb2433bc71488e7a449603bf8bd31fbcf9cb56fbe1eb6d459e90b86c37b", size = 40769, upload-time = "2026-03-05T15:55:05.277Z" }, - { url = "https://files.pythonhosted.org/packages/43/67/850e0b5a1e97799822ebfc4ca0e8c6ece3ed8baf7dcdf64de817dfdda2ca/mmh3-5.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:cae6383181f1e345317742d2ddd88f9e7d2682fa4c9432e3a74e47d92dce0229", size = 41563, upload-time = "2026-03-05T15:55:06.283Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cc/98c90b28e1da5458e19fbfaf4adb5289208d3bfccd45dd14eab216a2f0bb/mmh3-5.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:022aa1a528604e6c83d0a7705fdef0b5355d897a9e0fa3a8d26709ceaa06965d", size = 39310, upload-time = "2026-03-05T15:55:07.323Z" }, - { url = "https://files.pythonhosted.org/packages/63/b4/65bc1fb2bb7f83e91c30865023b1847cf89a5f237165575e8c83aa536584/mmh3-5.2.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:d771f085fcdf4035786adfb1d8db026df1eb4b41dac1c3d070d1e49512843227", size = 40794, upload-time = "2026-03-05T15:55:09.773Z" }, - { url = "https://files.pythonhosted.org/packages/c4/86/7168b3d83be8eb553897b1fac9da8bbb06568e5cfe555ffc329ebb46f59d/mmh3-5.2.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:7f196cd7910d71e9d9860da0ff7a77f64d22c1ad931f1dd18559a06e03109fc0", size = 41923, upload-time = "2026-03-05T15:55:10.924Z" }, - { url = "https://files.pythonhosted.org/packages/bf/9b/b653ab611c9060ce8ff0ba25c0226757755725e789292f3ca138a58082cd/mmh3-5.2.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b1f12bd684887a0a5d55e6363ca87056f361e45451105012d329b86ec19dbe0b", size = 39131, upload-time = "2026-03-05T15:55:11.961Z" }, - { url = "https://files.pythonhosted.org/packages/9b/b4/5a2e0d34ab4d33543f01121e832395ea510132ea8e52cdf63926d9d81754/mmh3-5.2.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d106493a60dcb4aef35a0fac85105e150a11cf8bc2b0d388f5a33272d756c966", size = 39825, upload-time = "2026-03-05T15:55:13.013Z" }, - { url = "https://files.pythonhosted.org/packages/bd/69/81699a8f39a3f8d368bec6443435c0c392df0d200ad915bf0d222b588e03/mmh3-5.2.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:44983e45310ee5b9f73397350251cdf6e63a466406a105f1d16cb5baa659270b", size = 40344, upload-time = "2026-03-05T15:55:14.026Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b3/71c8c775807606e8fd8acc5c69016e1caf3200d50b50b6dd4b40ce10b76c/mmh3-5.2.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:368625fb01666655985391dbad3860dc0ba7c0d6b9125819f3121ee7292b4ac8", size = 56291, upload-time = "2026-03-05T15:55:15.137Z" }, - { url = "https://files.pythonhosted.org/packages/6f/75/2c24517d4b2ce9e4917362d24f274d3d541346af764430249ddcc4cb3a08/mmh3-5.2.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:72d1cc63bcc91e14933f77d51b3df899d6a07d184ec515ea7f56bff659e124d7", size = 40575, upload-time = "2026-03-05T15:55:16.518Z" }, - { url = "https://files.pythonhosted.org/packages/bf/b9/e4a360164365ac9f07a25f0f7928e3a66eb9ecc989384060747aa170e6aa/mmh3-5.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e8b4b5580280b9265af3e0409974fb79c64cf7523632d03fbf11df18f8b0181e", size = 40052, upload-time = "2026-03-05T15:55:17.735Z" }, - { url = "https://files.pythonhosted.org/packages/97/ca/120d92223a7546131bbbc31c9174168ee7a73b1366f5463ffe69d9e691fe/mmh3-5.2.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4cbbde66f1183db040daede83dd86c06d663c5bb2af6de1142b7c8c37923dd74", size = 97311, upload-time = "2026-03-05T15:55:18.959Z" }, - { url = "https://files.pythonhosted.org/packages/b6/71/c1a60c1652b8813ef9de6d289784847355417ee0f2980bca002fe87f4ae5/mmh3-5.2.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8ff038d52ef6aa0f309feeba00c5095c9118d0abf787e8e8454d6048db2037fc", size = 103279, upload-time = "2026-03-05T15:55:20.448Z" }, - { url = "https://files.pythonhosted.org/packages/48/29/ad97f4be1509cdcb28ae32c15593ce7c415db47ace37f8fad35b493faa9a/mmh3-5.2.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4130d0b9ce5fad6af07421b1aecc7e079519f70d6c05729ab871794eded8617", size = 106290, upload-time = "2026-03-05T15:55:21.6Z" }, - { url = "https://files.pythonhosted.org/packages/77/29/1f86d22e281bd8827ba373600a4a8b0c0eae5ca6aa55b9a8c26d2a34decc/mmh3-5.2.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e0bfe77d238308839699944164b96a2eeccaf55f2af400f54dc20669d8d5f2", size = 113116, upload-time = "2026-03-05T15:55:22.826Z" }, - { url = "https://files.pythonhosted.org/packages/a7/7c/339971ea7ed4c12d98f421f13db3ea576a9114082ccb59d2d1a0f00ccac1/mmh3-5.2.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f963eafc0a77a6c0562397da004f5876a9bcf7265a7bcc3205e29636bc4a1312", size = 120740, upload-time = "2026-03-05T15:55:24.3Z" }, - { url = "https://files.pythonhosted.org/packages/e4/92/3c7c4bdb8e926bb3c972d1e2907d77960c1c4b250b41e8366cf20c6e4373/mmh3-5.2.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:92883836caf50d5255be03d988d75bc93e3f86ba247b7ca137347c323f731deb", size = 99143, upload-time = "2026-03-05T15:55:25.456Z" }, - { url = "https://files.pythonhosted.org/packages/df/0a/33dd8706e732458c8375eae63c981292de07a406bad4ec03e5269654aa2c/mmh3-5.2.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:57b52603e89355ff318025dd55158f6e71396c0f1f609d548e9ea9c94cc6ce0a", size = 98703, upload-time = "2026-03-05T15:55:26.723Z" }, - { url = "https://files.pythonhosted.org/packages/51/04/76bbce05df76cbc3d396f13b2ea5b1578ef02b6a5187e132c6c33f99d596/mmh3-5.2.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f40a95186a72fa0b67d15fef0f157bfcda00b4f59c8a07cbe5530d41ac35d105", size = 106484, upload-time = "2026-03-05T15:55:28.214Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8f/c6e204a2c70b719c1f62ffd9da27aef2dddcba875ea9c31ca0e87b975a46/mmh3-5.2.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:58370d05d033ee97224c81263af123dea3d931025030fd34b61227a768a8858a", size = 110012, upload-time = "2026-03-05T15:55:29.532Z" }, - { url = "https://files.pythonhosted.org/packages/e3/37/7181efd8e39db386c1ebc3e6b7d1f702a09d7c1197a6f2742ed6b5c16597/mmh3-5.2.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7be6dfb49e48fd0a7d91ff758a2b51336f1cd21f9d44b20f6801f072bd080cdd", size = 97508, upload-time = "2026-03-05T15:55:31.01Z" }, - { url = "https://files.pythonhosted.org/packages/42/0f/afa7ca2615fd85e1469474bb860e381443d0b868c083b62b41cb1d7ca32f/mmh3-5.2.1-cp314-cp314-win32.whl", hash = "sha256:54fe8518abe06a4c3852754bfd498b30cc58e667f376c513eac89a244ce781a4", size = 41387, upload-time = "2026-03-05T15:55:32.403Z" }, - { url = "https://files.pythonhosted.org/packages/71/0d/46d42a260ee1357db3d486e6c7a692e303c017968e14865e00efa10d09fc/mmh3-5.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:3f796b535008708846044c43302719c6956f39ca2d93f2edda5319e79a29efbb", size = 42101, upload-time = "2026-03-05T15:55:33.646Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7b/848a8378059d96501a41159fca90d6a99e89736b0afbe8e8edffeac8c74b/mmh3-5.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:cd471ede0d802dd936b6fab28188302b2d497f68436025857ca72cd3810423fe", size = 39836, upload-time = "2026-03-05T15:55:35.026Z" }, - { url = "https://files.pythonhosted.org/packages/27/61/1dabea76c011ba8547c25d30c91c0ec22544487a8750997a27a0c9e1180b/mmh3-5.2.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:5174a697ce042fa77c407e05efe41e03aa56dae9ec67388055820fb48cf4c3ba", size = 57727, upload-time = "2026-03-05T15:55:36.162Z" }, - { url = "https://files.pythonhosted.org/packages/b7/32/731185950d1cf2d5e28979cc8593016ba1619a295faba10dda664a4931b5/mmh3-5.2.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0a3984146e414684a6be2862d84fcb1035f4984851cb81b26d933bab6119bf00", size = 41308, upload-time = "2026-03-05T15:55:37.254Z" }, - { url = "https://files.pythonhosted.org/packages/76/aa/66c76801c24b8c9418b4edde9b5e57c75e72c94e29c48f707e3962534f18/mmh3-5.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:bd6e7d363aa93bd3421b30b6af97064daf47bc96005bddba67c5ffbc6df426b8", size = 40758, upload-time = "2026-03-05T15:55:38.61Z" }, - { url = "https://files.pythonhosted.org/packages/9e/bb/79a1f638a02f0ae389f706d13891e2fbf7d8c0a22ecde67ba828951bb60a/mmh3-5.2.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:113f78e7463a36dbbcea05bfe688efd7fa759d0f0c56e73c974d60dcfec3dfcc", size = 109670, upload-time = "2026-03-05T15:55:40.13Z" }, - { url = "https://files.pythonhosted.org/packages/26/94/8cd0e187a288985bcfc79bf5144d1d712df9dee74365f59d26e3a1865be6/mmh3-5.2.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e8ec5f606e0809426d2440e0683509fb605a8820a21ebd120dcdba61b74ef7f", size = 117399, upload-time = "2026-03-05T15:55:42.076Z" }, - { url = "https://files.pythonhosted.org/packages/42/94/dfea6059bd5c5beda565f58a4096e43f4858fb6d2862806b8bbd12cbb284/mmh3-5.2.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22b0f9971ec4e07e8223f2beebe96a6cfc779d940b6f27d26604040dd74d3a44", size = 120386, upload-time = "2026-03-05T15:55:43.481Z" }, - { url = "https://files.pythonhosted.org/packages/47/cb/f9c45e62aaa67220179f487772461d891bb582bb2f9783c944832c60efd9/mmh3-5.2.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:85ffc9920ffc39c5eee1e3ac9100c913a0973996fbad5111f939bbda49204bb7", size = 125924, upload-time = "2026-03-05T15:55:44.638Z" }, - { url = "https://files.pythonhosted.org/packages/a5/83/fe54a4a7c11bc9f623dfc1707decd034245602b076dfc1dcc771a4163170/mmh3-5.2.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7aec798c2b01aaa65a55f1124f3405804184373abb318a3091325aece235f67c", size = 135280, upload-time = "2026-03-05T15:55:45.866Z" }, - { url = "https://files.pythonhosted.org/packages/97/67/fe7e9e9c143daddd210cd22aef89cbc425d58ecf238d2b7d9eb0da974105/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:55dbbd8ffbc40d1697d5e2d0375b08599dae8746b0b08dea05eee4ce81648fac", size = 110050, upload-time = "2026-03-05T15:55:47.074Z" }, - { url = "https://files.pythonhosted.org/packages/43/c4/6d4b09fcbef80794de447c9378e39eefc047156b290fa3dd2d5257ca8227/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6c85c38a279ca9295a69b9b088a2e48aa49737bb1b34e6a9dc6297c110e8d912", size = 111158, upload-time = "2026-03-05T15:55:48.239Z" }, - { url = "https://files.pythonhosted.org/packages/81/a6/ca51c864bdb30524beb055a6d8826db3906af0834ec8c41d097a6e8573d5/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:6290289fa5fb4c70fd7f72016e03633d60388185483ff3b162912c81205ae2cf", size = 116890, upload-time = "2026-03-05T15:55:49.405Z" }, - { url = "https://files.pythonhosted.org/packages/cc/04/5a1fe2e2ad843d03e89af25238cbc4f6840a8bb6c4329a98ab694c71deda/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:4fc6cd65dc4d2fdb2625e288939a3566e36127a84811a4913f02f3d5931da52d", size = 123121, upload-time = "2026-03-05T15:55:50.61Z" }, - { url = "https://files.pythonhosted.org/packages/af/4d/3c820c6f4897afd25905270a9f2330a23f77a207ea7356f7aadace7273c0/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:623f938f6a039536cc02b7582a07a080f13fdfd48f87e63201d92d7e34d09a18", size = 110187, upload-time = "2026-03-05T15:55:52.143Z" }, - { url = "https://files.pythonhosted.org/packages/21/54/1d71cd143752361c0aebef16ad3f55926a6faf7b112d355745c1f8a25f7f/mmh3-5.2.1-cp314-cp314t-win32.whl", hash = "sha256:29bc3973676ae334412efdd367fcd11d036b7be3efc1ce2407ef8676dabfeb82", size = 41934, upload-time = "2026-03-05T15:55:53.564Z" }, - { url = "https://files.pythonhosted.org/packages/9d/e4/63a2a88f31d93dea03947cccc2a076946857e799ea4f7acdecbf43b324aa/mmh3-5.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:28cfab66577000b9505a0d068c731aee7ca85cd26d4d63881fab17857e0fe1fb", size = 43036, upload-time = "2026-03-05T15:55:55.252Z" }, - { url = "https://files.pythonhosted.org/packages/a0/0f/59204bf136d1201f8d7884cfbaf7498c5b4674e87a4c693f9bde63741ce1/mmh3-5.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:dfd51b4c56b673dfbc43d7d27ef857dd91124801e2806c69bb45585ce0fa019b", size = 40391, upload-time = "2026-03-05T15:55:56.697Z" }, -] - -[[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 = "mpmath" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, -] - -[[package]] -name = "multidict" -version = "6.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, - { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, - { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, - { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, - { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, - { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, - { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, - { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, - { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, - { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, - { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, - { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, - { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, - { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, - { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, - { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, - { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, - { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, - { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, - { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, - { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, - { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, - { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, - { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, - { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, - { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, - { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, - { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, - { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, - { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, - { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, - { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, - { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, - { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, - { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, - { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, - { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, - { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, - { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, - { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, - { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, - { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, - { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, - { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, - { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, - { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, - { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, - { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, - { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, - { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, - { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, - { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, - { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, - { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, - { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, - { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, - { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, - { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, - { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, - { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, - { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, - { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, - { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, - { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, - { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, - { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, - { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, - { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, - { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, - { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, - { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, - { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, - { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, - { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, - { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, - { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, - { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, -] - -[[package]] -name = "mypy" -version = "1.19.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, - { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, - { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, - { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, - { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, - { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, - { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, - { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, - { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, - { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, - { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, - { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, - { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, - { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, -] - -[[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 = "nh3" -version = "0.3.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/37/ab55eb2b05e334ff9a1ad52c556ace1f9c20a3f63613a165d384d5387657/nh3-0.3.3.tar.gz", hash = "sha256:185ed41b88c910b9ca8edc89ca3b4be688a12cb9de129d84befa2f74a0039fee", size = 18968, upload-time = "2026-02-14T09:35:15.664Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/a4/834f0ebd80844ce67e1bdb011d6f844f61cdb4c1d7cdc56a982bc054cc00/nh3-0.3.3-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:21b058cd20d9f0919421a820a2843fdb5e1749c0bf57a6247ab8f4ba6723c9fc", size = 1428680, upload-time = "2026-02-14T09:34:33.015Z" }, - { url = "https://files.pythonhosted.org/packages/7f/1a/a7d72e750f74c6b71befbeebc4489579fe783466889d41f32e34acde0b6b/nh3-0.3.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4400a73c2a62859e769f9d36d1b5a7a5c65c4179d1dddd2f6f3095b2db0cbfc", size = 799003, upload-time = "2026-02-14T09:34:35.108Z" }, - { url = "https://files.pythonhosted.org/packages/58/d5/089eb6d65da139dc2223b83b2627e00872eccb5e1afdf5b1d76eb6ad3fcc/nh3-0.3.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ef87f8e916321a88b45f2d597f29bd56e560ed4568a50f0f1305afab86b7189", size = 846818, upload-time = "2026-02-14T09:34:37Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c6/44a0b65fc7b213a3a725f041ef986534b100e58cd1a2e00f0fd3c9603893/nh3-0.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a446eae598987f49ee97ac2f18eafcce4e62e7574bd1eb23782e4702e54e217d", size = 1012537, upload-time = "2026-02-14T09:34:38.515Z" }, - { url = "https://files.pythonhosted.org/packages/94/3a/91bcfcc0a61b286b8b25d39e288b9c0ba91c3290d402867d1cd705169844/nh3-0.3.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:0d5eb734a78ac364af1797fef718340a373f626a9ff6b4fb0b4badf7927e7b81", size = 1095435, upload-time = "2026-02-14T09:34:40.022Z" }, - { url = "https://files.pythonhosted.org/packages/fd/fd/4617a19d80cf9f958e65724ff5e97bc2f76f2f4c5194c740016606c87bd1/nh3-0.3.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:92a958e6f6d0100e025a5686aafd67e3c98eac67495728f8bb64fbeb3e474493", size = 1056344, upload-time = "2026-02-14T09:34:41.469Z" }, - { url = "https://files.pythonhosted.org/packages/bd/7d/5bcbbc56e71b7dda7ef1d6008098da9c5426d6334137ef32bb2b9c496984/nh3-0.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9ed40cf8449a59a03aa465114fedce1ff7ac52561688811d047917cc878b19ca", size = 1034533, upload-time = "2026-02-14T09:34:43.313Z" }, - { url = "https://files.pythonhosted.org/packages/3f/9c/054eff8a59a8b23b37f0f4ac84cdd688ee84cf5251664c0e14e5d30a8a67/nh3-0.3.3-cp314-cp314t-win32.whl", hash = "sha256:b50c3770299fb2a7c1113751501e8878d525d15160a4c05194d7fe62b758aad8", size = 608305, upload-time = "2026-02-14T09:34:44.622Z" }, - { url = "https://files.pythonhosted.org/packages/d7/b0/64667b8d522c7b859717a02b1a66ba03b529ca1df623964e598af8db1ed5/nh3-0.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:21a63ccb18ddad3f784bb775955839b8b80e347e597726f01e43ca1abcc5c808", size = 620633, upload-time = "2026-02-14T09:34:46.069Z" }, - { url = "https://files.pythonhosted.org/packages/91/b5/ae9909e4ddfd86ee076c4d6d62ba69e9b31061da9d2f722936c52df8d556/nh3-0.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f508ddd4e2433fdcb78c790fc2d24e3a349ba775e5fa904af89891321d4844a3", size = 607027, upload-time = "2026-02-14T09:34:47.91Z" }, - { url = "https://files.pythonhosted.org/packages/13/3e/aef8cf8e0419b530c95e96ae93a5078e9b36c1e6613eeb1df03a80d5194e/nh3-0.3.3-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e8ee96156f7dfc6e30ecda650e480c5ae0a7d38f0c6fafc3c1c655e2500421d9", size = 1448640, upload-time = "2026-02-14T09:34:49.316Z" }, - { url = "https://files.pythonhosted.org/packages/ca/43/d2011a4f6c0272cb122eeff40062ee06bb2b6e57eabc3a5e057df0d582df/nh3-0.3.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45fe0d6a607264910daec30360c8a3b5b1500fd832d21b2da608256287bcb92d", size = 839405, upload-time = "2026-02-14T09:34:50.779Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f3/965048510c1caf2a34ed04411a46a04a06eb05563cd06f1aa57b71eb2bc8/nh3-0.3.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5bc1d4b30ba1ba896669d944b6003630592665974bd11a3dc2f661bde92798a7", size = 825849, upload-time = "2026-02-14T09:34:52.622Z" }, - { url = "https://files.pythonhosted.org/packages/78/99/b4bbc6ad16329d8db2c2c320423f00b549ca3b129c2b2f9136be2606dbb0/nh3-0.3.3-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f433a2dd66545aad4a720ad1b2150edcdca75bfff6f4e6f378ade1ec138d5e77", size = 1068303, upload-time = "2026-02-14T09:34:54.179Z" }, - { url = "https://files.pythonhosted.org/packages/3f/34/3420d97065aab1b35f3e93ce9c96c8ebd423ce86fe84dee3126790421a2a/nh3-0.3.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52e973cb742e95b9ae1b35822ce23992428750f4b46b619fe86eba4205255b30", size = 1029316, upload-time = "2026-02-14T09:34:56.186Z" }, - { url = "https://files.pythonhosted.org/packages/f1/9a/99eda757b14e596fdb2ca5f599a849d9554181aa899274d0d183faef4493/nh3-0.3.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c730617bdc15d7092dcc0469dc2826b914c8f874996d105b4bc3842a41c1cd9", size = 919944, upload-time = "2026-02-14T09:34:57.886Z" }, - { url = "https://files.pythonhosted.org/packages/6f/84/c0dc75c7fb596135f999e59a410d9f45bdabb989f1cb911f0016d22b747b/nh3-0.3.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e98fa3dbfd54e25487e36ba500bc29bca3a4cab4ffba18cfb1a35a2d02624297", size = 811461, upload-time = "2026-02-14T09:34:59.65Z" }, - { url = "https://files.pythonhosted.org/packages/7e/ec/b1bf57cab6230eec910e4863528dc51dcf21b57aaf7c88ee9190d62c9185/nh3-0.3.3-cp38-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:3a62b8ae7c235481715055222e54c682422d0495a5c73326807d4e44c5d14691", size = 840360, upload-time = "2026-02-14T09:35:01.444Z" }, - { url = "https://files.pythonhosted.org/packages/37/5e/326ae34e904dde09af1de51219a611ae914111f0970f2f111f4f0188f57e/nh3-0.3.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc305a2264868ec8fa16548296f803d8fd9c1fa66cd28b88b605b1bd06667c0b", size = 859872, upload-time = "2026-02-14T09:35:03.348Z" }, - { url = "https://files.pythonhosted.org/packages/09/38/7eba529ce17ab4d3790205da37deabb4cb6edcba15f27b8562e467f2fc97/nh3-0.3.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:90126a834c18af03bfd6ff9a027bfa6bbf0e238527bc780a24de6bd7cc1041e2", size = 1023550, upload-time = "2026-02-14T09:35:04.829Z" }, - { url = "https://files.pythonhosted.org/packages/05/a2/556fdecd37c3681b1edee2cf795a6799c6ed0a5551b2822636960d7e7651/nh3-0.3.3-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:24769a428e9e971e4ccfb24628f83aaa7dc3c8b41b130c8ddc1835fa1c924489", size = 1105212, upload-time = "2026-02-14T09:35:06.821Z" }, - { url = "https://files.pythonhosted.org/packages/dd/e3/5db0b0ad663234967d83702277094687baf7c498831a2d3ad3451c11770f/nh3-0.3.3-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:b7a18ee057761e455d58b9d31445c3e4b2594cff4ddb84d2e331c011ef46f462", size = 1069970, upload-time = "2026-02-14T09:35:08.504Z" }, - { url = "https://files.pythonhosted.org/packages/79/b2/2ea21b79c6e869581ce5f51549b6e185c4762233591455bf2a326fb07f3b/nh3-0.3.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5a4b2c1f3e6f3cbe7048e17f4fefad3f8d3e14cc0fd08fb8599e0d5653f6b181", size = 1047588, upload-time = "2026-02-14T09:35:09.911Z" }, - { url = "https://files.pythonhosted.org/packages/e2/92/2e434619e658c806d9c096eed2cdff9a883084299b7b19a3f0824eb8e63d/nh3-0.3.3-cp38-abi3-win32.whl", hash = "sha256:e974850b131fdffa75e7ad8e0d9c7a855b96227b093417fdf1bd61656e530f37", size = 616179, upload-time = "2026-02-14T09:35:11.366Z" }, - { url = "https://files.pythonhosted.org/packages/73/88/1ce287ef8649dc51365b5094bd3713b76454838140a32ab4f8349973883c/nh3-0.3.3-cp38-abi3-win_amd64.whl", hash = "sha256:2efd17c0355d04d39e6d79122b42662277ac10a17ea48831d90b46e5ef7e4fc0", size = 631159, upload-time = "2026-02-14T09:35:12.77Z" }, - { url = "https://files.pythonhosted.org/packages/31/f1/b4835dbde4fb06f29db89db027576d6014081cd278d9b6751facc3e69e43/nh3-0.3.3-cp38-abi3-win_arm64.whl", hash = "sha256:b838e619f483531483d26d889438e53a880510e832d2aafe73f93b7b1ac2bce2", size = 616645, upload-time = "2026-02-14T09:35:14.062Z" }, -] - -[[package]] -name = "nodeenv" -version = "1.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, -] - -[[package]] -name = "nox" -version = "2026.2.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "argcomplete" }, - { name = "attrs" }, - { name = "colorlog" }, - { name = "dependency-groups" }, - { name = "humanize" }, - { name = "packaging" }, - { name = "virtualenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6e/8e/55a9679b31f1efc48facedd2448eb53c7f1e647fb592aa1403c9dd7a4590/nox-2026.2.9.tar.gz", hash = "sha256:1bc8a202ee8cd69be7aaada63b2a7019126899a06fc930a7aee75585bf8ee41b", size = 4031165, upload-time = "2026-02-10T04:38:58.878Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/58/0d5e5a044f1868bdc45f38afdc2d90ff9867ce398b4e8fa9e666bfc9bfba/nox-2026.2.9-py3-none-any.whl", hash = "sha256:1b7143bc8ecdf25f2353201326152c5303ae4ae56ca097b1fb6179ad75164c47", size = 74615, upload-time = "2026-02-10T04:38:57.266Z" }, -] - -[[package]] -name = "numpy" -version = "2.4.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/10/8b/c265f4823726ab832de836cdd184d0986dcf94480f81e8739692a7ac7af2/numpy-2.4.3.tar.gz", hash = "sha256:483a201202b73495f00dbc83796c6ae63137a9bdade074f7648b3e32613412dd", size = 20727743, upload-time = "2026-03-09T07:58:53.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/ed/6388632536f9788cea23a3a1b629f25b43eaacd7d7377e5d6bc7b9deb69b/numpy-2.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:61b0cbabbb6126c8df63b9a3a0c4b1f44ebca5e12ff6997b80fcf267fb3150ef", size = 16669628, upload-time = "2026-03-09T07:56:24.252Z" }, - { url = "https://files.pythonhosted.org/packages/74/1b/ee2abfc68e1ce728b2958b6ba831d65c62e1b13ce3017c13943f8f9b5b2e/numpy-2.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7395e69ff32526710748f92cd8c9849b361830968ea3e24a676f272653e8983e", size = 14696872, upload-time = "2026-03-09T07:56:26.991Z" }, - { url = "https://files.pythonhosted.org/packages/ba/d1/780400e915ff5638166f11ca9dc2c5815189f3d7cf6f8759a1685e586413/numpy-2.4.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:abdce0f71dcb4a00e4e77f3faf05e4616ceccfe72ccaa07f47ee79cda3b7b0f4", size = 5203489, upload-time = "2026-03-09T07:56:29.414Z" }, - { url = "https://files.pythonhosted.org/packages/0b/bb/baffa907e9da4cc34a6e556d6d90e032f6d7a75ea47968ea92b4858826c4/numpy-2.4.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:48da3a4ee1336454b07497ff7ec83903efa5505792c4e6d9bf83d99dc07a1e18", size = 6550814, upload-time = "2026-03-09T07:56:32.225Z" }, - { url = "https://files.pythonhosted.org/packages/7b/12/8c9f0c6c95f76aeb20fc4a699c33e9f827fa0d0f857747c73bb7b17af945/numpy-2.4.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32e3bef222ad6b052280311d1d60db8e259e4947052c3ae7dd6817451fc8a4c5", size = 15666601, upload-time = "2026-03-09T07:56:34.461Z" }, - { url = "https://files.pythonhosted.org/packages/bd/79/cc665495e4d57d0aa6fbcc0aa57aa82671dfc78fbf95fe733ed86d98f52a/numpy-2.4.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7dd01a46700b1967487141a66ac1a3cf0dd8ebf1f08db37d46389401512ca97", size = 16621358, upload-time = "2026-03-09T07:56:36.852Z" }, - { url = "https://files.pythonhosted.org/packages/a8/40/b4ecb7224af1065c3539f5ecfff879d090de09608ad1008f02c05c770cb3/numpy-2.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:76f0f283506c28b12bba319c0fab98217e9f9b54e6160e9c79e9f7348ba32e9c", size = 17016135, upload-time = "2026-03-09T07:56:39.337Z" }, - { url = "https://files.pythonhosted.org/packages/f7/b1/6a88e888052eed951afed7a142dcdf3b149a030ca59b4c71eef085858e43/numpy-2.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:737f630a337364665aba3b5a77e56a68cc42d350edd010c345d65a3efa3addcc", size = 18345816, upload-time = "2026-03-09T07:56:42.31Z" }, - { url = "https://files.pythonhosted.org/packages/f3/8f/103a60c5f8c3d7fc678c19cd7b2476110da689ccb80bc18050efbaeae183/numpy-2.4.3-cp312-cp312-win32.whl", hash = "sha256:26952e18d82a1dbbc2f008d402021baa8d6fc8e84347a2072a25e08b46d698b9", size = 5960132, upload-time = "2026-03-09T07:56:44.851Z" }, - { url = "https://files.pythonhosted.org/packages/d7/7c/f5ee1bf6ed888494978046a809df2882aad35d414b622893322df7286879/numpy-2.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:65f3c2455188f09678355f5cae1f959a06b778bc66d535da07bf2ef20cd319d5", size = 12316144, upload-time = "2026-03-09T07:56:47.057Z" }, - { url = "https://files.pythonhosted.org/packages/71/46/8d1cb3f7a00f2fb6394140e7e6623696e54c6318a9d9691bb4904672cf42/numpy-2.4.3-cp312-cp312-win_arm64.whl", hash = "sha256:2abad5c7fef172b3377502bde47892439bae394a71bc329f31df0fd829b41a9e", size = 10220364, upload-time = "2026-03-09T07:56:49.849Z" }, - { url = "https://files.pythonhosted.org/packages/b6/d0/1fe47a98ce0df229238b77611340aff92d52691bcbc10583303181abf7fc/numpy-2.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b346845443716c8e542d54112966383b448f4a3ba5c66409771b8c0889485dd3", size = 16665297, upload-time = "2026-03-09T07:56:52.296Z" }, - { url = "https://files.pythonhosted.org/packages/27/d9/4e7c3f0e68dfa91f21c6fb6cf839bc829ec920688b1ce7ec722b1a6202fb/numpy-2.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2629289168f4897a3c4e23dc98d6f1731f0fc0fe52fb9db19f974041e4cc12b9", size = 14691853, upload-time = "2026-03-09T07:56:54.992Z" }, - { url = "https://files.pythonhosted.org/packages/3a/66/bd096b13a87549683812b53ab211e6d413497f84e794fb3c39191948da97/numpy-2.4.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:bb2e3cf95854233799013779216c57e153c1ee67a0bf92138acca0e429aefaee", size = 5198435, upload-time = "2026-03-09T07:56:57.184Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2f/687722910b5a5601de2135c891108f51dfc873d8e43c8ed9f4ebb440b4a2/numpy-2.4.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:7f3408ff897f8ab07a07fbe2823d7aee6ff644c097cc1f90382511fe982f647f", size = 6546347, upload-time = "2026-03-09T07:56:59.531Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ec/7971c4e98d86c564750393fab8d7d83d0a9432a9d78bb8a163a6dc59967a/numpy-2.4.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:decb0eb8a53c3b009b0962378065589685d66b23467ef5dac16cbe818afde27f", size = 15664626, upload-time = "2026-03-09T07:57:01.385Z" }, - { url = "https://files.pythonhosted.org/packages/7e/eb/7daecbea84ec935b7fc732e18f532073064a3816f0932a40a17f3349185f/numpy-2.4.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5f51900414fc9204a0e0da158ba2ac52b75656e7dce7e77fb9f84bfa343b4cc", size = 16608916, upload-time = "2026-03-09T07:57:04.008Z" }, - { url = "https://files.pythonhosted.org/packages/df/58/2a2b4a817ffd7472dca4421d9f0776898b364154e30c95f42195041dc03b/numpy-2.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6bd06731541f89cdc01b261ba2c9e037f1543df7472517836b78dfb15bd6e476", size = 17015824, upload-time = "2026-03-09T07:57:06.347Z" }, - { url = "https://files.pythonhosted.org/packages/4a/ca/627a828d44e78a418c55f82dd4caea8ea4a8ef24e5144d9e71016e52fb40/numpy-2.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:22654fe6be0e5206f553a9250762c653d3698e46686eee53b399ab90da59bd92", size = 18334581, upload-time = "2026-03-09T07:57:09.114Z" }, - { url = "https://files.pythonhosted.org/packages/cd/c0/76f93962fc79955fcba30a429b62304332345f22d4daec1cb33653425643/numpy-2.4.3-cp313-cp313-win32.whl", hash = "sha256:d71e379452a2f670ccb689ec801b1218cd3983e253105d6e83780967e899d687", size = 5958618, upload-time = "2026-03-09T07:57:11.432Z" }, - { url = "https://files.pythonhosted.org/packages/b1/3c/88af0040119209b9b5cb59485fa48b76f372c73068dbf9254784b975ac53/numpy-2.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:0a60e17a14d640f49146cb38e3f105f571318db7826d9b6fef7e4dce758faecd", size = 12312824, upload-time = "2026-03-09T07:57:13.586Z" }, - { url = "https://files.pythonhosted.org/packages/58/ce/3d07743aced3d173f877c3ef6a454c2174ba42b584ab0b7e6d99374f51ed/numpy-2.4.3-cp313-cp313-win_arm64.whl", hash = "sha256:c9619741e9da2059cd9c3f206110b97583c7152c1dc9f8aafd4beb450ac1c89d", size = 10221218, upload-time = "2026-03-09T07:57:16.183Z" }, - { url = "https://files.pythonhosted.org/packages/62/09/d96b02a91d09e9d97862f4fc8bfebf5400f567d8eb1fe4b0cc4795679c15/numpy-2.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7aa4e54f6469300ebca1d9eb80acd5253cdfa36f2c03d79a35883687da430875", size = 14819570, upload-time = "2026-03-09T07:57:18.564Z" }, - { url = "https://files.pythonhosted.org/packages/b5/ca/0b1aba3905fdfa3373d523b2b15b19029f4f3031c87f4066bd9d20ef6c6b/numpy-2.4.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d1b90d840b25874cf5cd20c219af10bac3667db3876d9a495609273ebe679070", size = 5326113, upload-time = "2026-03-09T07:57:21.052Z" }, - { url = "https://files.pythonhosted.org/packages/c0/63/406e0fd32fcaeb94180fd6a4c41e55736d676c54346b7efbce548b94a914/numpy-2.4.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a749547700de0a20a6718293396ec237bb38218049cfce788e08fcb716e8cf73", size = 6646370, upload-time = "2026-03-09T07:57:22.804Z" }, - { url = "https://files.pythonhosted.org/packages/b6/d0/10f7dc157d4b37af92720a196be6f54f889e90dcd30dce9dc657ed92c257/numpy-2.4.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94f3c4a151a2e529adf49c1d54f0f57ff8f9b233ee4d44af623a81553ab86368", size = 15723499, upload-time = "2026-03-09T07:57:24.693Z" }, - { url = "https://files.pythonhosted.org/packages/66/f1/d1c2bf1161396629701bc284d958dc1efa3a5a542aab83cf11ee6eb4cba5/numpy-2.4.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22c31dc07025123aedf7f2db9e91783df13f1776dc52c6b22c620870dc0fab22", size = 16657164, upload-time = "2026-03-09T07:57:27.676Z" }, - { url = "https://files.pythonhosted.org/packages/1a/be/cca19230b740af199ac47331a21c71e7a3d0ba59661350483c1600d28c37/numpy-2.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:148d59127ac95979d6f07e4d460f934ebdd6eed641db9c0db6c73026f2b2101a", size = 17081544, upload-time = "2026-03-09T07:57:30.664Z" }, - { url = "https://files.pythonhosted.org/packages/b9/c5/9602b0cbb703a0936fb40f8a95407e8171935b15846de2f0776e08af04c7/numpy-2.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a97cbf7e905c435865c2d939af3d93f99d18eaaa3cabe4256f4304fb51604349", size = 18380290, upload-time = "2026-03-09T07:57:33.763Z" }, - { url = "https://files.pythonhosted.org/packages/ed/81/9f24708953cd30be9ee36ec4778f4b112b45165812f2ada4cc5ea1c1f254/numpy-2.4.3-cp313-cp313t-win32.whl", hash = "sha256:be3b8487d725a77acccc9924f65fd8bce9af7fac8c9820df1049424a2115af6c", size = 6082814, upload-time = "2026-03-09T07:57:36.491Z" }, - { url = "https://files.pythonhosted.org/packages/e2/9e/52f6eaa13e1a799f0ab79066c17f7016a4a8ae0c1aefa58c82b4dab690b4/numpy-2.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1ec84fd7c8e652b0f4aaaf2e6e9cc8eaa9b1b80a537e06b2e3a2fb176eedcb26", size = 12452673, upload-time = "2026-03-09T07:57:38.281Z" }, - { url = "https://files.pythonhosted.org/packages/c4/04/b8cece6ead0b30c9fbd99bb835ad7ea0112ac5f39f069788c5558e3b1ab2/numpy-2.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:120df8c0a81ebbf5b9020c91439fccd85f5e018a927a39f624845be194a2be02", size = 10290907, upload-time = "2026-03-09T07:57:40.747Z" }, - { url = "https://files.pythonhosted.org/packages/70/ae/3936f79adebf8caf81bd7a599b90a561334a658be4dcc7b6329ebf4ee8de/numpy-2.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:5884ce5c7acfae1e4e1b6fde43797d10aa506074d25b531b4f54bde33c0c31d4", size = 16664563, upload-time = "2026-03-09T07:57:43.817Z" }, - { url = "https://files.pythonhosted.org/packages/9b/62/760f2b55866b496bb1fa7da2a6db076bef908110e568b02fcfc1422e2a3a/numpy-2.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:297837823f5bc572c5f9379b0c9f3a3365f08492cbdc33bcc3af174372ebb168", size = 14702161, upload-time = "2026-03-09T07:57:46.169Z" }, - { url = "https://files.pythonhosted.org/packages/32/af/a7a39464e2c0a21526fb4fb76e346fb172ebc92f6d1c7a07c2c139cc17b1/numpy-2.4.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:a111698b4a3f8dcbe54c64a7708f049355abd603e619013c346553c1fd4ca90b", size = 5208738, upload-time = "2026-03-09T07:57:48.506Z" }, - { url = "https://files.pythonhosted.org/packages/29/8c/2a0cf86a59558fa078d83805589c2de490f29ed4fb336c14313a161d358a/numpy-2.4.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:4bd4741a6a676770e0e97fe9ab2e51de01183df3dcbcec591d26d331a40de950", size = 6543618, upload-time = "2026-03-09T07:57:50.591Z" }, - { url = "https://files.pythonhosted.org/packages/aa/b8/612ce010c0728b1c363fa4ea3aa4c22fe1c5da1de008486f8c2f5cb92fae/numpy-2.4.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:54f29b877279d51e210e0c80709ee14ccbbad647810e8f3d375561c45ef613dd", size = 15680676, upload-time = "2026-03-09T07:57:52.34Z" }, - { url = "https://files.pythonhosted.org/packages/a9/7e/4f120ecc54ba26ddf3dc348eeb9eb063f421de65c05fc961941798feea18/numpy-2.4.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:679f2a834bae9020f81534671c56fd0cc76dd7e5182f57131478e23d0dc59e24", size = 16613492, upload-time = "2026-03-09T07:57:54.91Z" }, - { url = "https://files.pythonhosted.org/packages/2c/86/1b6020db73be330c4b45d5c6ee4295d59cfeef0e3ea323959d053e5a6909/numpy-2.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d84f0f881cb2225c2dfd7f78a10a5645d487a496c6668d6cc39f0f114164f3d0", size = 17031789, upload-time = "2026-03-09T07:57:57.641Z" }, - { url = "https://files.pythonhosted.org/packages/07/3a/3b90463bf41ebc21d1b7e06079f03070334374208c0f9a1f05e4ae8455e7/numpy-2.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d213c7e6e8d211888cc359bab7199670a00f5b82c0978b9d1c75baf1eddbeac0", size = 18339941, upload-time = "2026-03-09T07:58:00.577Z" }, - { url = "https://files.pythonhosted.org/packages/a8/74/6d736c4cd962259fd8bae9be27363eb4883a2f9069763747347544c2a487/numpy-2.4.3-cp314-cp314-win32.whl", hash = "sha256:52077feedeff7c76ed7c9f1a0428558e50825347b7545bbb8523da2cd55c547a", size = 6007503, upload-time = "2026-03-09T07:58:03.331Z" }, - { url = "https://files.pythonhosted.org/packages/48/39/c56ef87af669364356bb011922ef0734fc49dad51964568634c72a009488/numpy-2.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:0448e7f9caefb34b4b7dd2b77f21e8906e5d6f0365ad525f9f4f530b13df2afc", size = 12444915, upload-time = "2026-03-09T07:58:06.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/1f/ab8528e38d295fd349310807496fabb7cf9fe2e1f70b97bc20a483ea9d4a/numpy-2.4.3-cp314-cp314-win_arm64.whl", hash = "sha256:b44fd60341c4d9783039598efadd03617fa28d041fc37d22b62d08f2027fa0e7", size = 10494875, upload-time = "2026-03-09T07:58:08.734Z" }, - { url = "https://files.pythonhosted.org/packages/e6/ef/b7c35e4d5ef141b836658ab21a66d1a573e15b335b1d111d31f26c8ef80f/numpy-2.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0a195f4216be9305a73c0e91c9b026a35f2161237cf1c6de9b681637772ea657", size = 14822225, upload-time = "2026-03-09T07:58:11.034Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8d/7730fa9278cf6648639946cc816e7cc89f0d891602584697923375f801ed/numpy-2.4.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:cd32fbacb9fd1bf041bf8e89e4576b6f00b895f06d00914820ae06a616bdfef7", size = 5328769, upload-time = "2026-03-09T07:58:13.67Z" }, - { url = "https://files.pythonhosted.org/packages/47/01/d2a137317c958b074d338807c1b6a383406cdf8b8e53b075d804cc3d211d/numpy-2.4.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:2e03c05abaee1f672e9d67bc858f300b5ccba1c21397211e8d77d98350972093", size = 6649461, upload-time = "2026-03-09T07:58:15.912Z" }, - { url = "https://files.pythonhosted.org/packages/5c/34/812ce12bc0f00272a4b0ec0d713cd237cb390666eb6206323d1cc9cedbb2/numpy-2.4.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d1ce23cce91fcea443320a9d0ece9b9305d4368875bab09538f7a5b4131938a", size = 15725809, upload-time = "2026-03-09T07:58:17.787Z" }, - { url = "https://files.pythonhosted.org/packages/25/c0/2aed473a4823e905e765fee3dc2cbf504bd3e68ccb1150fbdabd5c39f527/numpy-2.4.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c59020932feb24ed49ffd03704fbab89f22aa9c0d4b180ff45542fe8918f5611", size = 16655242, upload-time = "2026-03-09T07:58:20.476Z" }, - { url = "https://files.pythonhosted.org/packages/f2/c8/7e052b2fc87aa0e86de23f20e2c42bd261c624748aa8efd2c78f7bb8d8c6/numpy-2.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9684823a78a6cd6ad7511fc5e25b07947d1d5b5e2812c93fe99d7d4195130720", size = 17080660, upload-time = "2026-03-09T07:58:23.067Z" }, - { url = "https://files.pythonhosted.org/packages/f3/3d/0876746044db2adcb11549f214d104f2e1be00f07a67edbb4e2812094847/numpy-2.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0200b25c687033316fb39f0ff4e3e690e8957a2c3c8d22499891ec58c37a3eb5", size = 18380384, upload-time = "2026-03-09T07:58:25.839Z" }, - { url = "https://files.pythonhosted.org/packages/07/12/8160bea39da3335737b10308df4f484235fd297f556745f13092aa039d3b/numpy-2.4.3-cp314-cp314t-win32.whl", hash = "sha256:5e10da9e93247e554bb1d22f8edc51847ddd7dde52d85ce31024c1b4312bfba0", size = 6154547, upload-time = "2026-03-09T07:58:28.289Z" }, - { url = "https://files.pythonhosted.org/packages/42/f3/76534f61f80d74cc9cdf2e570d3d4eeb92c2280a27c39b0aaf471eda7b48/numpy-2.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:45f003dbdffb997a03da2d1d0cb41fbd24a87507fb41605c0420a3db5bd4667b", size = 12633645, upload-time = "2026-03-09T07:58:30.384Z" }, - { url = "https://files.pythonhosted.org/packages/1f/b6/7c0d4334c15983cec7f92a69e8ce9b1e6f31857e5ee3a413ac424e6bd63d/numpy-2.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:4d382735cecd7bcf090172489a525cd7d4087bc331f7df9f60ddc9a296cf208e", size = 10565454, upload-time = "2026-03-09T07:58:33.031Z" }, -] - -[[package]] -name = "onnxruntime" -version = "1.24.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flatbuffers" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "sympy" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/38/31db1b232b4ba960065a90c1506ad7a56995cd8482033184e97fadca17cc/onnxruntime-1.24.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cad1c2b3f455c55678ab2a8caa51fb420c25e6e3cf10f4c23653cdabedc8de78", size = 17341875, upload-time = "2026-03-17T22:05:51.669Z" }, - { url = "https://files.pythonhosted.org/packages/aa/60/c4d1c8043eb42f8a9aa9e931c8c293d289c48ff463267130eca97d13357f/onnxruntime-1.24.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1a5c5a544b22f90859c88617ecb30e161ee3349fcc73878854f43d77f00558b5", size = 15172485, upload-time = "2026-03-17T22:03:32.182Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ab/5b68110e0460d73fad814d5bd11c7b1ddcce5c37b10177eb264d6a36e331/onnxruntime-1.24.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d640eb9f3782689b55cfa715094474cd5662f2f137be6a6f847a594b6e9705c", size = 17244912, upload-time = "2026-03-17T22:04:37.251Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f4/6b89e297b93704345f0f3f8c62229bee323ef25682a3f9b4f89a39324950/onnxruntime-1.24.4-cp312-cp312-win_amd64.whl", hash = "sha256:535b29475ca42b593c45fbb2152fbf1cdf3f287315bf650e6a724a0a1d065cdb", size = 12596856, upload-time = "2026-03-17T22:05:41.224Z" }, - { url = "https://files.pythonhosted.org/packages/43/06/8b8ec6e9e6a474fcd5d772453f627ad4549dfe3ab8c0bf70af5afcde551b/onnxruntime-1.24.4-cp312-cp312-win_arm64.whl", hash = "sha256:e6214096e14b7b52e3bee1903dc12dc7ca09cb65e26664668a4620cc5e6f9a90", size = 12270275, upload-time = "2026-03-17T22:05:31.132Z" }, - { url = "https://files.pythonhosted.org/packages/e9/f0/8a21ec0a97e40abb7d8da1e8b20fb9e1af509cc6d191f6faa75f73622fb2/onnxruntime-1.24.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e99a48078baaefa2b50fe5836c319499f71f13f76ed32d0211f39109147a49e0", size = 17341922, upload-time = "2026-03-17T22:03:56.364Z" }, - { url = "https://files.pythonhosted.org/packages/8b/25/d7908de8e08cee9abfa15b8aa82349b79733ae5865162a3609c11598805d/onnxruntime-1.24.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4aaed1e5e1aaacf2343c838a30a7c3ade78f13eeb16817411f929d04040a13", size = 15172290, upload-time = "2026-03-17T22:03:37.124Z" }, - { url = "https://files.pythonhosted.org/packages/7f/72/105ec27a78c5aa0154a7c0cd8c41c19a97799c3b12fc30392928997e3be3/onnxruntime-1.24.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e30c972bc02e072911aabb6891453ec73795386c0af2b761b65444b8a4c4745f", size = 17244738, upload-time = "2026-03-17T22:04:40.625Z" }, - { url = "https://files.pythonhosted.org/packages/05/fb/a592736d968c2f58e12de4d52088dda8e0e724b26ad5c0487263adb45875/onnxruntime-1.24.4-cp313-cp313-win_amd64.whl", hash = "sha256:3b6ba8b0181a3aa88edab00eb01424ffc06f42e71095a91186c2249415fcff93", size = 12597435, upload-time = "2026-03-17T22:05:43.826Z" }, - { url = "https://files.pythonhosted.org/packages/ad/04/ae2479e9841b64bd2eb44f8a64756c62593f896514369a11243b1b86ca5c/onnxruntime-1.24.4-cp313-cp313-win_arm64.whl", hash = "sha256:71d6a5c1821d6e8586a024000ece458db8f2fc0ecd050435d45794827ce81e19", size = 12269852, upload-time = "2026-03-17T22:05:33.353Z" }, - { url = "https://files.pythonhosted.org/packages/b4/af/a479a536c4398ffaf49fbbe755f45d5b8726bdb4335ab31b537f3d7149b8/onnxruntime-1.24.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1700f559c8086d06b2a4d5de51e62cb4ff5e2631822f71a36db8c72383db71ee", size = 15176861, upload-time = "2026-03-17T22:03:40.143Z" }, - { url = "https://files.pythonhosted.org/packages/be/13/19f5da70c346a76037da2c2851ecbf1266e61d7f0dcdb887c667210d4608/onnxruntime-1.24.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c74e268dc808e61e63784d43f9ddcdaf50a776c2819e8bd1d1b11ef64bf7e36", size = 17247454, upload-time = "2026-03-17T22:04:46.643Z" }, - { url = "https://files.pythonhosted.org/packages/89/db/b30dbbd6037847b205ab75d962bc349bf1e46d02a65b30d7047a6893ffd6/onnxruntime-1.24.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:fbff2a248940e3398ae78374c5a839e49a2f39079b488bc64439fa0ec327a3e4", size = 17343300, upload-time = "2026-03-17T22:03:59.223Z" }, - { url = "https://files.pythonhosted.org/packages/61/88/1746c0e7959961475b84c776d35601a21d445f463c93b1433a409ec3e188/onnxruntime-1.24.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2b7969e72d8cb53ffc88ab6d49dd5e75c1c663bda7be7eb0ece192f127343d1", size = 15175936, upload-time = "2026-03-17T22:03:43.671Z" }, - { url = "https://files.pythonhosted.org/packages/5f/ba/4699cde04a52cece66cbebc85bd8335a0d3b9ad485abc9a2e15946a1349d/onnxruntime-1.24.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14ed1f197fab812b695a5eaddb536c635e58a2fbbe50a517c78f082cc6ce9177", size = 17246432, upload-time = "2026-03-17T22:04:49.58Z" }, - { url = "https://files.pythonhosted.org/packages/ef/60/4590910841bb28bd3b4b388a9efbedf4e2d2cca99ddf0c863642b4e87814/onnxruntime-1.24.4-cp314-cp314-win_amd64.whl", hash = "sha256:311e309f573bf3c12aa5723e23823077f83d5e412a18499d4485c7eb41040858", size = 12903276, upload-time = "2026-03-17T22:05:46.349Z" }, - { url = "https://files.pythonhosted.org/packages/7f/6f/60e2c0acea1e1ac09b3e794b5a19c166eebf91c0b860b3e6db8e74983fda/onnxruntime-1.24.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f0b910e86b759a4732663ec61fd57ac42ee1b0066f68299de164220b660546d", size = 12594365, upload-time = "2026-03-17T22:05:35.795Z" }, - { url = "https://files.pythonhosted.org/packages/cf/68/0c05d10f8f6c40fe0912ebec0d5a33884aaa2af2053507e864dab0883208/onnxruntime-1.24.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa12ddc54c9c4594073abcaa265cd9681e95fb89dae982a6f508a794ca42e661", size = 15176889, upload-time = "2026-03-17T22:03:48.021Z" }, - { url = "https://files.pythonhosted.org/packages/6c/1d/1666dc64e78d8587d168fec4e3b7922b92eb286a2ddeebcf6acb55c7dc82/onnxruntime-1.24.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1cc6a518255f012134bc791975a6294806be9a3b20c4a54cca25194c90cf731", size = 17247021, upload-time = "2026-03-17T22:04:52.377Z" }, -] - -[[package]] -name = "openai" -version = "2.29.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b4/15/203d537e58986b5673e7f232453a2a2f110f22757b15921cbdeea392e520/openai-2.29.0.tar.gz", hash = "sha256:32d09eb2f661b38d3edd7d7e1a2943d1633f572596febe64c0cd370c86d52bec", size = 671128, upload-time = "2026-03-17T17:53:49.599Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/b1/35b6f9c8cf9318e3dbb7146cc82dab4cf61182a8d5406fc9b50864362895/openai-2.29.0-py3-none-any.whl", hash = "sha256:b7c5de513c3286d17c5e29b92c4c98ceaf0d775244ac8159aeb1bddf840eb42a", size = 1141533, upload-time = "2026-03-17T17:53:47.348Z" }, -] - -[[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 = "opentelemetry-api" -version = "1.40.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2c/1d/4049a9e8698361cc1a1aa03a6c59e4fa4c71e0c0f94a30f988a6876a2ae6/opentelemetry_api-1.40.0.tar.gz", hash = "sha256:159be641c0b04d11e9ecd576906462773eb97ae1b657730f0ecf64d32071569f", size = 70851, upload-time = "2026-03-04T14:17:21.555Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676, upload-time = "2026-03-04T14:17:01.24Z" }, -] - -[[package]] -name = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[package]] -name = "paginate" -version = "0.5.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" }, -] - -[[package]] -name = "parso" -version = "0.8.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/76/a1e769043c0c0c9fe391b702539d594731a4362334cdf4dc25d0c09761e7/parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", size = 401621, upload-time = "2026-02-09T15:45:24.425Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894, upload-time = "2026-02-09T15:45:21.391Z" }, -] - -[[package]] -name = "pathable" -version = "0.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/55/b748445cb4ea6b125626f15379be7c96d1035d4fa3e8fee362fa92298abf/pathable-0.5.0.tar.gz", hash = "sha256:d81938348a1cacb525e7c75166270644782c0fb9c8cecc16be033e71427e0ef1", size = 16655, upload-time = "2026-02-20T08:47:00.748Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/96/5a770e5c461462575474468e5af931cff9de036e7c2b4fea23c1c58d2cbe/pathable-0.5.0-py3-none-any.whl", hash = "sha256:646e3d09491a6351a0c82632a09c02cdf70a252e73196b36d8a15ba0a114f0a6", size = 16867, upload-time = "2026-02-20T08:46:59.536Z" }, -] - -[[package]] -name = "pathspec" -version = "1.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, -] - -[[package]] -name = "pexpect" -version = "4.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ptyprocess" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, -] - -[[package]] -name = "pillow" -version = "11.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800, upload-time = "2025-07-01T09:14:17.648Z" }, - { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296, upload-time = "2025-07-01T09:14:19.828Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726, upload-time = "2025-07-03T13:10:04.448Z" }, - { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652, upload-time = "2025-07-03T13:10:10.391Z" }, - { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787, upload-time = "2025-07-01T09:14:21.63Z" }, - { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236, upload-time = "2025-07-01T09:14:23.321Z" }, - { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950, upload-time = "2025-07-01T09:14:25.237Z" }, - { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", size = 6723358, upload-time = "2025-07-01T09:14:27.053Z" }, - { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", size = 6275079, upload-time = "2025-07-01T09:14:30.104Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", size = 6986324, upload-time = "2025-07-01T09:14:31.899Z" }, - { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", size = 2423067, upload-time = "2025-07-01T09:14:33.709Z" }, - { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328, upload-time = "2025-07-01T09:14:35.276Z" }, - { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652, upload-time = "2025-07-01T09:14:37.203Z" }, - { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443, upload-time = "2025-07-01T09:14:39.344Z" }, - { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474, upload-time = "2025-07-01T09:14:41.843Z" }, - { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038, upload-time = "2025-07-01T09:14:44.008Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407, upload-time = "2025-07-03T13:10:15.628Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094, upload-time = "2025-07-03T13:10:21.857Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503, upload-time = "2025-07-01T09:14:45.698Z" }, - { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574, upload-time = "2025-07-01T09:14:47.415Z" }, - { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060, upload-time = "2025-07-01T09:14:49.636Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407, upload-time = "2025-07-01T09:14:51.962Z" }, - { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841, upload-time = "2025-07-01T09:14:54.142Z" }, - { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450, upload-time = "2025-07-01T09:14:56.436Z" }, - { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055, upload-time = "2025-07-01T09:14:58.072Z" }, - { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110, upload-time = "2025-07-01T09:14:59.79Z" }, - { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547, upload-time = "2025-07-01T09:15:01.648Z" }, - { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554, upload-time = "2025-07-03T13:10:27.018Z" }, - { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132, upload-time = "2025-07-03T13:10:33.01Z" }, - { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001, upload-time = "2025-07-01T09:15:03.365Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814, upload-time = "2025-07-01T09:15:05.655Z" }, - { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124, upload-time = "2025-07-01T09:15:07.358Z" }, - { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186, upload-time = "2025-07-01T09:15:09.317Z" }, - { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546, upload-time = "2025-07-01T09:15:11.311Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102, upload-time = "2025-07-01T09:15:13.164Z" }, - { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" }, - { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" }, - { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" }, - { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" }, - { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" }, - { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" }, - { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" }, - { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" }, - { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" }, - { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" }, - { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" }, - { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" }, - { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" }, - { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" }, - { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" }, - { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" }, - { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" }, - { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" }, - { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.9.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, -] - -[[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 = "polars" -version = "1.39.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "polars-runtime-32" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/ab/f19e592fce9e000da49c96bf35e77cef67f9cb4b040bfa538a2764c0263e/polars-1.39.3.tar.gz", hash = "sha256:2e016c7f3e8d14fa777ef86fe0477cec6c67023a20ba4c94d6e8431eefe4a63c", size = 728987, upload-time = "2026-03-20T11:16:24.836Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/db/08f4ca10c5018813e7e0b59e4472302328b3d2ab1512f5a2157a814540e0/polars-1.39.3-py3-none-any.whl", hash = "sha256:c2b955ccc0a08a2bc9259785decf3d5c007b489b523bf2390cf21cec2bb82a56", size = 823985, upload-time = "2026-03-20T11:14:23.619Z" }, -] - -[[package]] -name = "polars-runtime-32" -version = "1.39.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/17/39/c8688696bc22b6c501e3b82ef3be10e543c07a785af5660f30997cd22dd2/polars_runtime_32-1.39.3.tar.gz", hash = "sha256:c728e4f469cafab501947585f36311b8fb222d3e934c6209e83791e0df20b29d", size = 2872335, upload-time = "2026-03-20T11:16:26.581Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/74/1b41205f7368c9375ab1dea91178eaa20435fe3eff036390a53a7660b416/polars_runtime_32-1.39.3-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:425c0b220b573fa097b4042edff73114cc6d23432a21dfd2dc41adf329d7d2e9", size = 45273243, upload-time = "2026-03-20T11:14:26.691Z" }, - { url = "https://files.pythonhosted.org/packages/90/bf/297716b3095fe719be20fcf7af1d2b6ab069c38199bbace2469608a69b3a/polars_runtime_32-1.39.3-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:ef5884711e3c617d7dc93519a7d038e242f5741cfe5fe9afd32d58845d86c562", size = 40842924, upload-time = "2026-03-20T11:14:31.154Z" }, - { url = "https://files.pythonhosted.org/packages/3d/3e/e65236d9d0d9babfa0ecba593413c06530fca60a8feb8f66243aa5dba92e/polars_runtime_32-1.39.3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06b47f535eb1f97a9a1e5b0053ef50db3a4276e241178e37bbb1a38b1fa53b14", size = 43220650, upload-time = "2026-03-20T11:14:35.458Z" }, - { url = "https://files.pythonhosted.org/packages/b0/15/fc3e43f3fdf3f20b7dfb5abe871ab6162cf8fb4aeabf4cfad822d5dc4c79/polars_runtime_32-1.39.3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bc9e13dc1d2e828331f2fe8ccbc9757554dc4933a8d3e85e906b988178f95ed", size = 46877498, upload-time = "2026-03-20T11:14:40.14Z" }, - { url = "https://files.pythonhosted.org/packages/3c/81/bd5f895919e32c6ab0a7786cd0c0ca961cb03152c47c3645808b54383f31/polars_runtime_32-1.39.3-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:363d49e3a3e638fc943e2b9887940300a7d06789930855a178a4727949259dc2", size = 43380176, upload-time = "2026-03-20T11:14:45.566Z" }, - { url = "https://files.pythonhosted.org/packages/7a/3e/c86433c3b5ec0315bdfc7640d0c15d41f1216c0103a0eab9a9b5147d6c4c/polars_runtime_32-1.39.3-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7c206bdcc7bc62ea038d6adea8e44b02f0e675e0191a54c810703b4895208ea4", size = 46485933, upload-time = "2026-03-20T11:14:51.155Z" }, - { url = "https://files.pythonhosted.org/packages/54/ce/200b310cf91f98e652eb6ea09fdb3a9718aa0293ebf113dce325797c8572/polars_runtime_32-1.39.3-cp310-abi3-win_amd64.whl", hash = "sha256:d66ca522517554a883446957539c40dc7b75eb0c2220357fb28bc8940d305339", size = 46995458, upload-time = "2026-03-20T11:14:56.074Z" }, - { url = "https://files.pythonhosted.org/packages/da/76/2d48927e0aa2abbdde08cbf4a2536883b73277d47fbeca95e952de86df34/polars_runtime_32-1.39.3-cp310-abi3-win_arm64.whl", hash = "sha256:f49f51461de63f13e5dd4eb080421c8f23f856945f3f8bd5b2b1f59da52c2860", size = 41857648, upload-time = "2026-03-20T11:15:01.142Z" }, -] - -[[package]] -name = "pre-commit" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cfgv" }, - { name = "identify" }, - { name = "nodeenv" }, - { name = "pyyaml" }, - { name = "virtualenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, -] - -[[package]] -name = "prompt-toolkit" -version = "3.0.52" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wcwidth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, -] - -[[package]] -name = "propcache" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, - { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, - { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, - { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, - { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, - { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, - { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, - { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, - { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, - { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, - { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, - { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, - { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, - { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, - { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, - { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, - { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, - { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, - { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, - { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, - { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, - { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, - { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, - { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, - { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, - { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, - { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, - { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, - { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, - { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, - { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, - { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, - { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, - { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, - { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, - { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, - { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, - { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, - { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, - { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, - { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, - { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, - { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, - { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, - { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, - { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, - { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, - { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, - { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, - { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, - { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, - { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, - { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, - { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, - { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, -] - -[[package]] -name = "protobuf" -version = "7.34.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/6b/a0e95cad1ad7cc3f2c6821fcab91671bd5b78bd42afb357bb4765f29bc41/protobuf-7.34.1.tar.gz", hash = "sha256:9ce42245e704cc5027be797c1db1eb93184d44d1cdd71811fb2d9b25ad541280", size = 454708, upload-time = "2026-03-20T17:34:47.036Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/11/3325d41e6ee15bf1125654301211247b042563bcc898784351252549a8ad/protobuf-7.34.1-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8b2cc79c4d8f62b293ad9b11ec3aebce9af481fa73e64556969f7345ebf9fc7", size = 429247, upload-time = "2026-03-20T17:34:37.024Z" }, - { url = "https://files.pythonhosted.org/packages/eb/9d/aa69df2724ff63efa6f72307b483ce0827f4347cc6d6df24b59e26659fef/protobuf-7.34.1-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:5185e0e948d07abe94bb76ec9b8416b604cfe5da6f871d67aad30cbf24c3110b", size = 325753, upload-time = "2026-03-20T17:34:38.751Z" }, - { url = "https://files.pythonhosted.org/packages/92/e8/d174c91fd48e50101943f042b09af9029064810b734e4160bbe282fa1caa/protobuf-7.34.1-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:403b093a6e28a960372b44e5eb081775c9b056e816a8029c61231743d63f881a", size = 340198, upload-time = "2026-03-20T17:34:39.871Z" }, - { url = "https://files.pythonhosted.org/packages/53/1b/3b431694a4dc6d37b9f653f0c64b0a0d9ec074ee810710c0c3da21d67ba7/protobuf-7.34.1-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:8ff40ce8cd688f7265326b38d5a1bed9bfdf5e6723d49961432f83e21d5713e4", size = 324267, upload-time = "2026-03-20T17:34:41.1Z" }, - { url = "https://files.pythonhosted.org/packages/85/29/64de04a0ac142fb685fd09999bc3d337943fb386f3a0ec57f92fd8203f97/protobuf-7.34.1-cp310-abi3-win32.whl", hash = "sha256:34b84ce27680df7cca9f231043ada0daa55d0c44a2ddfaa58ec1d0d89d8bf60a", size = 426628, upload-time = "2026-03-20T17:34:42.536Z" }, - { url = "https://files.pythonhosted.org/packages/4d/87/cb5e585192a22b8bd457df5a2c16a75ea0db9674c3a0a39fc9347d84e075/protobuf-7.34.1-cp310-abi3-win_amd64.whl", hash = "sha256:e97b55646e6ce5cbb0954a8c28cd39a5869b59090dfaa7df4598a7fba869468c", size = 437901, upload-time = "2026-03-20T17:34:44.112Z" }, - { url = "https://files.pythonhosted.org/packages/88/95/608f665226bca68b736b79e457fded9a2a38c4f4379a4a7614303d9db3bc/protobuf-7.34.1-py3-none-any.whl", hash = "sha256:bb3812cd53aefea2b028ef42bd780f5b96407247f20c6ef7c679807e9d188f11", size = 170715, upload-time = "2026-03-20T17:34:45.384Z" }, -] - -[[package]] -name = "ptyprocess" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, -] - -[[package]] -name = "pure-eval" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, -] - -[[package]] -name = "py-key-value-aio" -version = "0.4.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/04/3c/0397c072a38d4bc580994b42e0c90c5f44f679303489e4376289534735e5/py_key_value_aio-0.4.4.tar.gz", hash = "sha256:e3012e6243ed7cc09bb05457bd4d03b1ba5c2b1ca8700096b3927db79ffbbe55", size = 92300, upload-time = "2026-02-16T21:21:43.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/69/f1b537ee70b7def42d63124a539ed3026a11a3ffc3086947a1ca6e861868/py_key_value_aio-0.4.4-py3-none-any.whl", hash = "sha256:18e17564ecae61b987f909fc2cd41ee2012c84b4b1dcb8c055cf8b4bc1bf3f5d", size = 152291, upload-time = "2026-02-16T21:21:44.241Z" }, -] - -[package.optional-dependencies] -filetree = [ - { name = "aiofile" }, - { name = "anyio" }, -] -keyring = [ - { name = "keyring" }, -] -memory = [ - { name = "cachetools" }, -] - -[[package]] -name = "py-rust-stemmers" -version = "0.1.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/63/4fbc14810c32d2a884e2e94e406a7d5bf8eee53e1103f558433817230342/py_rust_stemmers-0.1.5.tar.gz", hash = "sha256:e9c310cfb5c2470d7c7c8a0484725965e7cab8b1237e106a0863d5741da3e1f7", size = 9388, upload-time = "2025-02-19T13:56:28.708Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e1/ea8ac92454a634b1bb1ee0a89c2f75a4e6afec15a8412527e9bbde8c6b7b/py_rust_stemmers-0.1.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:29772837126a28263bf54ecd1bc709dd569d15a94d5e861937813ce51e8a6df4", size = 286085, upload-time = "2025-02-19T13:55:23.871Z" }, - { url = "https://files.pythonhosted.org/packages/cb/32/fe1cc3d36a19c1ce39792b1ed151ddff5ee1d74c8801f0e93ff36e65f885/py_rust_stemmers-0.1.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d62410ada44a01e02974b85d45d82f4b4c511aae9121e5f3c1ba1d0bea9126b", size = 272021, upload-time = "2025-02-19T13:55:25.685Z" }, - { url = "https://files.pythonhosted.org/packages/0a/38/b8f94e5e886e7ab181361a0911a14fb923b0d05b414de85f427e773bf445/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b28ef729a4c83c7d9418be3c23c0372493fcccc67e86783ff04596ef8a208cdf", size = 310547, upload-time = "2025-02-19T13:55:26.891Z" }, - { url = "https://files.pythonhosted.org/packages/a9/08/62e97652d359b75335486f4da134a6f1c281f38bd3169ed6ecfb276448c3/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a979c3f4ff7ad94a0d4cf566ca7bfecebb59e66488cc158e64485cf0c9a7879f", size = 315237, upload-time = "2025-02-19T13:55:28.116Z" }, - { url = "https://files.pythonhosted.org/packages/1c/b9/fc0278432f288d2be4ee4d5cc80fd8013d604506b9b0503e8b8cae4ba1c3/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c3593d895453fa06bf70a7b76d6f00d06def0f91fc253fe4260920650c5e078", size = 324419, upload-time = "2025-02-19T13:55:29.211Z" }, - { url = "https://files.pythonhosted.org/packages/6b/5b/74e96eaf622fe07e83c5c389d101540e305e25f76a6d0d6fb3d9e0506db8/py_rust_stemmers-0.1.5-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:96ccc7fd042ffc3f7f082f2223bb7082ed1423aa6b43d5d89ab23e321936c045", size = 324792, upload-time = "2025-02-19T13:55:30.948Z" }, - { url = "https://files.pythonhosted.org/packages/4f/f7/b76816d7d67166e9313915ad486c21d9e7da0ac02703e14375bb1cb64b5a/py_rust_stemmers-0.1.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef18cfced2c9c676e0d7d172ba61c3fab2aa6969db64cc8f5ca33a7759efbefe", size = 488014, upload-time = "2025-02-19T13:55:32.066Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ed/7d9bed02f78d85527501f86a867cd5002d97deb791b9a6b1b45b00100010/py_rust_stemmers-0.1.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:541d4b5aa911381e3d37ec483abb6a2cf2351b4f16d5e8d77f9aa2722956662a", size = 575582, upload-time = "2025-02-19T13:55:34.005Z" }, - { url = "https://files.pythonhosted.org/packages/93/40/eafd1b33688e8e8ae946d1ef25c4dc93f5b685bd104b9c5573405d7e1d30/py_rust_stemmers-0.1.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ffd946a36e9ac17ca96821963663012e04bc0ee94d21e8b5ae034721070b436c", size = 493267, upload-time = "2025-02-19T13:55:35.294Z" }, - { url = "https://files.pythonhosted.org/packages/2f/6a/15135b69e4fd28369433eb03264d201b1b0040ba534b05eddeb02a276684/py_rust_stemmers-0.1.5-cp312-none-win_amd64.whl", hash = "sha256:6ed61e1207f3b7428e99b5d00c055645c6415bb75033bff2d06394cbe035fd8e", size = 209395, upload-time = "2025-02-19T13:55:36.519Z" }, - { url = "https://files.pythonhosted.org/packages/80/b8/030036311ec25952bf3083b6c105be5dee052a71aa22d5fbeb857ebf8c1c/py_rust_stemmers-0.1.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:398b3a843a9cd4c5d09e726246bc36f66b3d05b0a937996814e91f47708f5db5", size = 286086, upload-time = "2025-02-19T13:55:37.581Z" }, - { url = "https://files.pythonhosted.org/packages/ed/be/0465dcb3a709ee243d464e89231e3da580017f34279d6304de291d65ccb0/py_rust_stemmers-0.1.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4e308fc7687901f0c73603203869908f3156fa9c17c4ba010a7fcc98a7a1c5f2", size = 272019, upload-time = "2025-02-19T13:55:39.183Z" }, - { url = "https://files.pythonhosted.org/packages/ab/b6/76ca5b1f30cba36835938b5d9abee0c130c81833d51b9006264afdf8df3c/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f9efc4da5e734bdd00612e7506de3d0c9b7abc4b89d192742a0569d0d1fe749", size = 310545, upload-time = "2025-02-19T13:55:40.339Z" }, - { url = "https://files.pythonhosted.org/packages/56/8f/5be87618cea2fe2e70e74115a20724802bfd06f11c7c43514b8288eb6514/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cc2cc8d2b36bc05b8b06506199ac63d437360ae38caefd98cd19e479d35afd42", size = 315236, upload-time = "2025-02-19T13:55:41.55Z" }, - { url = "https://files.pythonhosted.org/packages/00/02/ea86a316aee0f0a9d1449ad4dbffff38f4cf0a9a31045168ae8b95d8bdf8/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a231dc6f0b2a5f12a080dfc7abd9e6a4ea0909290b10fd0a4620e5a0f52c3d17", size = 324419, upload-time = "2025-02-19T13:55:42.693Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fd/1612c22545dcc0abe2f30fc08f30a2332f2224dd536fa1508444a9ca0e39/py_rust_stemmers-0.1.5-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5845709d48afc8b29e248f42f92431155a3d8df9ba30418301c49c6072b181b0", size = 324794, upload-time = "2025-02-19T13:55:43.896Z" }, - { url = "https://files.pythonhosted.org/packages/66/18/8a547584d7edac9e7ac9c7bdc53228d6f751c0f70a317093a77c386c8ddc/py_rust_stemmers-0.1.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e48bfd5e3ce9d223bfb9e634dc1425cf93ee57eef6f56aa9a7120ada3990d4be", size = 488014, upload-time = "2025-02-19T13:55:45.088Z" }, - { url = "https://files.pythonhosted.org/packages/3b/87/4619c395b325e26048a6e28a365afed754614788ba1f49b2eefb07621a03/py_rust_stemmers-0.1.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:35d32f6e7bdf6fd90e981765e32293a8be74def807147dea9fdc1f65d6ce382f", size = 575582, upload-time = "2025-02-19T13:55:46.436Z" }, - { url = "https://files.pythonhosted.org/packages/98/6e/214f1a889142b7df6d716e7f3fea6c41e87bd6c29046aa57e175d452b104/py_rust_stemmers-0.1.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:191ea8bf922c984631ffa20bf02ef0ad7eec0465baeaed3852779e8f97c7e7a3", size = 493269, upload-time = "2025-02-19T13:55:49.057Z" }, - { url = "https://files.pythonhosted.org/packages/e1/b9/c5185df277576f995ae34418eb2b2ac12f30835412270f9e05c52face521/py_rust_stemmers-0.1.5-cp313-none-win_amd64.whl", hash = "sha256:e564c9efdbe7621704e222b53bac265b0e4fbea788f07c814094f0ec6b80adcf", size = 209397, upload-time = "2025-02-19T13:55:50.853Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pycryptodome" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276, upload-time = "2025-05-17T17:21:45.242Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/5d/bdb09489b63cd34a976cc9e2a8d938114f7a53a74d3dd4f125ffa49dce82/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4", size = 2495152, upload-time = "2025-05-17T17:20:20.833Z" }, - { url = "https://files.pythonhosted.org/packages/a7/ce/7840250ed4cc0039c433cd41715536f926d6e86ce84e904068eb3244b6a6/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae", size = 1639348, upload-time = "2025-05-17T17:20:23.171Z" }, - { url = "https://files.pythonhosted.org/packages/ee/f0/991da24c55c1f688d6a3b5a11940567353f74590734ee4a64294834ae472/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477", size = 2184033, upload-time = "2025-05-17T17:20:25.424Z" }, - { url = "https://files.pythonhosted.org/packages/54/16/0e11882deddf00f68b68dd4e8e442ddc30641f31afeb2bc25588124ac8de/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7", size = 2270142, upload-time = "2025-05-17T17:20:27.808Z" }, - { url = "https://files.pythonhosted.org/packages/d5/fc/4347fea23a3f95ffb931f383ff28b3f7b1fe868739182cb76718c0da86a1/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446", size = 2309384, upload-time = "2025-05-17T17:20:30.765Z" }, - { url = "https://files.pythonhosted.org/packages/6e/d9/c5261780b69ce66d8cfab25d2797bd6e82ba0241804694cd48be41add5eb/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265", size = 2183237, upload-time = "2025-05-17T17:20:33.736Z" }, - { url = "https://files.pythonhosted.org/packages/5a/6f/3af2ffedd5cfa08c631f89452c6648c4d779e7772dfc388c77c920ca6bbf/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b", size = 2343898, upload-time = "2025-05-17T17:20:36.086Z" }, - { url = "https://files.pythonhosted.org/packages/9a/dc/9060d807039ee5de6e2f260f72f3d70ac213993a804f5e67e0a73a56dd2f/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d", size = 2269197, upload-time = "2025-05-17T17:20:38.414Z" }, - { url = "https://files.pythonhosted.org/packages/f9/34/e6c8ca177cb29dcc4967fef73f5de445912f93bd0343c9c33c8e5bf8cde8/pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a", size = 1768600, upload-time = "2025-05-17T17:20:40.688Z" }, - { url = "https://files.pythonhosted.org/packages/e4/1d/89756b8d7ff623ad0160f4539da571d1f594d21ee6d68be130a6eccb39a4/pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625", size = 1799740, upload-time = "2025-05-17T17:20:42.413Z" }, - { url = "https://files.pythonhosted.org/packages/5d/61/35a64f0feaea9fd07f0d91209e7be91726eb48c0f1bfc6720647194071e4/pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39", size = 1703685, upload-time = "2025-05-17T17:20:44.388Z" }, - { url = "https://files.pythonhosted.org/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627, upload-time = "2025-05-17T17:20:47.139Z" }, - { url = "https://files.pythonhosted.org/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362, upload-time = "2025-05-17T17:20:50.392Z" }, - { url = "https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625, upload-time = "2025-05-17T17:20:52.866Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954, upload-time = "2025-05-17T17:20:55.027Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534, upload-time = "2025-05-17T17:20:57.279Z" }, - { url = "https://files.pythonhosted.org/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853, upload-time = "2025-05-17T17:20:59.322Z" }, - { url = "https://files.pythonhosted.org/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465, upload-time = "2025-05-17T17:21:03.83Z" }, - { url = "https://files.pythonhosted.org/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414, upload-time = "2025-05-17T17:21:06.72Z" }, - { url = "https://files.pythonhosted.org/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484, upload-time = "2025-05-17T17:21:08.535Z" }, - { url = "https://files.pythonhosted.org/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636, upload-time = "2025-05-17T17:21:10.393Z" }, - { url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675, upload-time = "2025-05-17T17:21:13.146Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[package.optional-dependencies] -email = [ - { name = "email-validator" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, -] - -[[package]] -name = "pydantic-settings" -version = "2.13.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/52/6d/fffca34caecc4a3f97bda81b2098da5e8ab7efc9a66e819074a11955d87e/pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", size = 223826, upload-time = "2026-02-19T13:45:08.055Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/4b/ccc026168948fec4f7555b9164c724cf4125eac006e176541483d2c959be/pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237", size = 58929, upload-time = "2026-02-19T13:45:06.034Z" }, -] - -[[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 = "pyjwt" -version = "2.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, -] - -[package.optional-dependencies] -crypto = [ - { name = "cryptography" }, -] - -[[package]] -name = "pymdown-extensions" -version = "10.21" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ba/63/06673d1eb6d8f83c0ea1f677d770e12565fb516928b4109c9e2055656a9e/pymdown_extensions-10.21.tar.gz", hash = "sha256:39f4a020f40773f6b2ff31d2cd2546c2c04d0a6498c31d9c688d2be07e1767d5", size = 853363, upload-time = "2026-02-15T20:44:06.748Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl", hash = "sha256:91b879f9f864d49794c2d9534372b10150e6141096c3908a455e45ca72ad9d3f", size = 268877, upload-time = "2026-02-15T20:44:05.464Z" }, -] - -[[package]] -name = "pyperclip" -version = "1.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, -] - -[[package]] -name = "pyproject-hooks" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, -] - -[[package]] -name = "pyright" -version = "1.1.408" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nodeenv" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/b2/5db700e52554b8f025faa9c3c624c59f1f6c8841ba81ab97641b54322f16/pyright-1.1.408.tar.gz", hash = "sha256:f28f2321f96852fa50b5829ea492f6adb0e6954568d1caa3f3af3a5f555eb684", size = 4400578, upload-time = "2026-01-08T08:07:38.795Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/82/a2c93e32800940d9573fb28c346772a14778b84ba7524e691b324620ab89/pyright-1.1.408-py3-none-any.whl", hash = "sha256:090b32865f4fdb1e0e6cd82bf5618480d48eecd2eb2e70f960982a3d9a4c17c1", size = 6399144, upload-time = "2026-01-08T08:07:37.082Z" }, -] - -[[package]] -name = "pytest" -version = "8.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -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 = "0.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8e/c4/453c52c659521066969523e87d85d54139bbd17b78f09532fb8eb8cdb58e/pytest_asyncio-0.26.0.tar.gz", hash = "sha256:c4df2a697648241ff39e7f0e4a73050b03f123f760673956cf0d72a4990e312f", size = 54156, upload-time = "2025-03-25T06:22:28.883Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/7f/338843f449ace853647ace35870874f69a764d251872ed1b4de9f234822c/pytest_asyncio-0.26.0-py3-none-any.whl", hash = "sha256:7b51ed894f4fbea1340262bdae5135797ebbe21d8638978e35d31c6d19f72fb0", size = 19694, upload-time = "2025-03-25T06:22:27.807Z" }, -] - -[[package]] -name = "pytest-cov" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coverage" }, - { name = "pluggy" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, -] - -[[package]] -name = "pytest-mock" -version = "3.15.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[package]] -name = "python-discovery" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "platformdirs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9c/90/bcce6b46823c9bec1757c964dc37ed332579be512e17a30e9698095dcae4/python_discovery-1.2.0.tar.gz", hash = "sha256:7d33e350704818b09e3da2bd419d37e21e7c30db6e0977bb438916e06b41b5b1", size = 58055, upload-time = "2026-03-19T01:43:08.248Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl", hash = "sha256:1e108f1bbe2ed0ef089823d28805d5ad32be8e734b86a5f212bf89b71c266e4a", size = 31524, upload-time = "2026-03-19T01:43:07.045Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, -] - -[[package]] -name = "pytokens" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, - { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, - { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, - { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, - { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, - { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, - { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, - { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, - { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, - { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, - { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, - { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, - { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, - { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, - { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, - { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, - { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, - { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, - { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, - { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, - { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, -] - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { 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 = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "pyyaml-env-tag" -version = "1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, -] - -[[package]] -name = "readme-renderer" -version = "44.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "nh3" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/a9/104ec9234c8448c4379768221ea6df01260cd6c2ce13182d4eac531c8342/readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", size = 32056, upload-time = "2024-07-08T15:00:57.805Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", size = 13310, upload-time = "2024-07-08T15:00:56.577Z" }, -] - -[[package]] -name = "referencing" -version = "0.37.0" -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/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, -] - -[[package]] -name = "regex" -version = "2026.2.28" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8b/71/41455aa99a5a5ac1eaf311f5d8efd9ce6433c03ac1e0962de163350d0d97/regex-2026.2.28.tar.gz", hash = "sha256:a729e47d418ea11d03469f321aaf67cdee8954cde3ff2cf8403ab87951ad10f2", size = 415184, upload-time = "2026-02-28T02:19:42.792Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/42/9061b03cf0fc4b5fa2c3984cbbaed54324377e440a5c5a29d29a72518d62/regex-2026.2.28-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fcf26c3c6d0da98fada8ae4ef0aa1c3405a431c0a77eb17306d38a89b02adcd7", size = 489574, upload-time = "2026-02-28T02:16:50.455Z" }, - { url = "https://files.pythonhosted.org/packages/77/83/0c8a5623a233015595e3da499c5a1c13720ac63c107897a6037bb97af248/regex-2026.2.28-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02473c954af35dd2defeb07e44182f5705b30ea3f351a7cbffa9177beb14da5d", size = 291426, upload-time = "2026-02-28T02:16:52.52Z" }, - { url = "https://files.pythonhosted.org/packages/9e/06/3ef1ac6910dc3295ebd71b1f9bfa737e82cfead211a18b319d45f85ddd09/regex-2026.2.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b65d33a17101569f86d9c5966a8b1d7fbf8afdda5a8aa219301b0a80f58cf7d", size = 289200, upload-time = "2026-02-28T02:16:54.08Z" }, - { url = "https://files.pythonhosted.org/packages/dd/c9/8cc8d850b35ab5650ff6756a1cb85286e2000b66c97520b29c1587455344/regex-2026.2.28-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e71dcecaa113eebcc96622c17692672c2d104b1d71ddf7adeda90da7ddeb26fc", size = 796765, upload-time = "2026-02-28T02:16:55.905Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5d/57702597627fc23278ebf36fbb497ac91c0ce7fec89ac6c81e420ca3e38c/regex-2026.2.28-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:481df4623fa4969c8b11f3433ed7d5e3dc9cec0f008356c3212b3933fb77e3d8", size = 863093, upload-time = "2026-02-28T02:16:58.094Z" }, - { url = "https://files.pythonhosted.org/packages/02/6d/f3ecad537ca2811b4d26b54ca848cf70e04fcfc138667c146a9f3157779c/regex-2026.2.28-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:64e7c6ad614573e0640f271e811a408d79a9e1fe62a46adb602f598df42a818d", size = 909455, upload-time = "2026-02-28T02:17:00.918Z" }, - { url = "https://files.pythonhosted.org/packages/9e/40/bb226f203caa22c1043c1ca79b36340156eca0f6a6742b46c3bb222a3a57/regex-2026.2.28-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6b08a06976ff4fb0d83077022fde3eca06c55432bb997d8c0495b9a4e9872f4", size = 802037, upload-time = "2026-02-28T02:17:02.842Z" }, - { url = "https://files.pythonhosted.org/packages/44/7c/c6d91d8911ac6803b45ca968e8e500c46934e58c0903cbc6d760ee817a0a/regex-2026.2.28-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:864cdd1a2ef5716b0ab468af40139e62ede1b3a53386b375ec0786bb6783fc05", size = 775113, upload-time = "2026-02-28T02:17:04.506Z" }, - { url = "https://files.pythonhosted.org/packages/dc/8d/4a9368d168d47abd4158580b8c848709667b1cd293ff0c0c277279543bd0/regex-2026.2.28-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:511f7419f7afab475fd4d639d4aedfc54205bcb0800066753ef68a59f0f330b5", size = 784194, upload-time = "2026-02-28T02:17:06.888Z" }, - { url = "https://files.pythonhosted.org/packages/cc/bf/2c72ab5d8b7be462cb1651b5cc333da1d0068740342f350fcca3bca31947/regex-2026.2.28-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b42f7466e32bf15a961cf09f35fa6323cc72e64d3d2c990b10de1274a5da0a59", size = 856846, upload-time = "2026-02-28T02:17:09.11Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f4/6b65c979bb6d09f51bb2d2a7bc85de73c01ec73335d7ddd202dcb8cd1c8f/regex-2026.2.28-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8710d61737b0c0ce6836b1da7109f20d495e49b3809f30e27e9560be67a257bf", size = 763516, upload-time = "2026-02-28T02:17:11.004Z" }, - { url = "https://files.pythonhosted.org/packages/8e/32/29ea5e27400ee86d2cc2b4e80aa059df04eaf78b4f0c18576ae077aeff68/regex-2026.2.28-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4390c365fd2d45278f45afd4673cb90f7285f5701607e3ad4274df08e36140ae", size = 849278, upload-time = "2026-02-28T02:17:12.693Z" }, - { url = "https://files.pythonhosted.org/packages/1d/91/3233d03b5f865111cd517e1c95ee8b43e8b428d61fa73764a80c9bb6f537/regex-2026.2.28-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cb3b1db8ff6c7b8bf838ab05583ea15230cb2f678e569ab0e3a24d1e8320940b", size = 790068, upload-time = "2026-02-28T02:17:14.9Z" }, - { url = "https://files.pythonhosted.org/packages/76/92/abc706c1fb03b4580a09645b206a3fc032f5a9f457bc1a8038ac555658ab/regex-2026.2.28-cp312-cp312-win32.whl", hash = "sha256:f8ed9a5d4612df9d4de15878f0bc6aa7a268afbe5af21a3fdd97fa19516e978c", size = 266416, upload-time = "2026-02-28T02:17:17.15Z" }, - { url = "https://files.pythonhosted.org/packages/fa/06/2a6f7dff190e5fa9df9fb4acf2fdf17a1aa0f7f54596cba8de608db56b3a/regex-2026.2.28-cp312-cp312-win_amd64.whl", hash = "sha256:01d65fd24206c8e1e97e2e31b286c59009636c022eb5d003f52760b0f42155d4", size = 277297, upload-time = "2026-02-28T02:17:18.723Z" }, - { url = "https://files.pythonhosted.org/packages/b7/f0/58a2484851fadf284458fdbd728f580d55c1abac059ae9f048c63b92f427/regex-2026.2.28-cp312-cp312-win_arm64.whl", hash = "sha256:c0b5ccbb8ffb433939d248707d4a8b31993cb76ab1a0187ca886bf50e96df952", size = 270408, upload-time = "2026-02-28T02:17:20.328Z" }, - { url = "https://files.pythonhosted.org/packages/87/f6/dc9ef48c61b79c8201585bf37fa70cd781977da86e466cd94e8e95d2443b/regex-2026.2.28-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6d63a07e5ec8ce7184452cb00c41c37b49e67dc4f73b2955b5b8e782ea970784", size = 489311, upload-time = "2026-02-28T02:17:22.591Z" }, - { url = "https://files.pythonhosted.org/packages/95/c8/c20390f2232d3f7956f420f4ef1852608ad57aa26c3dd78516cb9f3dc913/regex-2026.2.28-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e59bc8f30414d283ae8ee1617b13d8112e7135cb92830f0ec3688cb29152585a", size = 291285, upload-time = "2026-02-28T02:17:24.355Z" }, - { url = "https://files.pythonhosted.org/packages/d2/a6/ba1068a631ebd71a230e7d8013fcd284b7c89c35f46f34a7da02082141b1/regex-2026.2.28-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:de0cf053139f96219ccfabb4a8dd2d217c8c82cb206c91d9f109f3f552d6b43d", size = 289051, upload-time = "2026-02-28T02:17:26.722Z" }, - { url = "https://files.pythonhosted.org/packages/1d/1b/7cc3b7af4c244c204b7a80924bd3d85aecd9ba5bc82b485c5806ee8cda9e/regex-2026.2.28-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb4db2f17e6484904f986c5a657cec85574c76b5c5e61c7aae9ffa1bc6224f95", size = 796842, upload-time = "2026-02-28T02:17:29.064Z" }, - { url = "https://files.pythonhosted.org/packages/24/87/26bd03efc60e0d772ac1e7b60a2e6325af98d974e2358f659c507d3c76db/regex-2026.2.28-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:52b017b35ac2214d0db5f4f90e303634dc44e4aba4bd6235a27f97ecbe5b0472", size = 863083, upload-time = "2026-02-28T02:17:31.363Z" }, - { url = "https://files.pythonhosted.org/packages/ae/54/aeaf4afb1aa0a65e40de52a61dc2ac5b00a83c6cb081c8a1d0dda74f3010/regex-2026.2.28-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:69fc560ccbf08a09dc9b52ab69cacfae51e0ed80dc5693078bdc97db2f91ae96", size = 909412, upload-time = "2026-02-28T02:17:33.248Z" }, - { url = "https://files.pythonhosted.org/packages/12/2f/049901def913954e640d199bbc6a7ca2902b6aeda0e5da9d17f114100ec2/regex-2026.2.28-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e61eea47230eba62a31f3e8a0e3164d0f37ef9f40529fb2c79361bc6b53d2a92", size = 802101, upload-time = "2026-02-28T02:17:35.053Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/512fb9ff7f5b15ea204bb1967ebb649059446decacccb201381f9fa6aad4/regex-2026.2.28-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4f5c0b182ad4269e7381b7c27fdb0408399881f7a92a4624fd5487f2971dfc11", size = 775260, upload-time = "2026-02-28T02:17:37.692Z" }, - { url = "https://files.pythonhosted.org/packages/d1/a8/9a92935878aba19bd72706b9db5646a6f993d99b3f6ed42c02ec8beb1d61/regex-2026.2.28-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:96f6269a2882fbb0ee76967116b83679dc628e68eaea44e90884b8d53d833881", size = 784311, upload-time = "2026-02-28T02:17:39.855Z" }, - { url = "https://files.pythonhosted.org/packages/09/d3/fc51a8a738a49a6b6499626580554c9466d3ea561f2b72cfdc72e4149773/regex-2026.2.28-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b5acd4b6a95f37c3c3828e5d053a7d4edaedb85de551db0153754924cb7c83e3", size = 856876, upload-time = "2026-02-28T02:17:42.317Z" }, - { url = "https://files.pythonhosted.org/packages/08/b7/2e641f3d084b120ca4c52e8c762a78da0b32bf03ef546330db3e2635dc5f/regex-2026.2.28-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2234059cfe33d9813a3677ef7667999caea9eeaa83fef98eb6ce15c6cf9e0215", size = 763632, upload-time = "2026-02-28T02:17:45.073Z" }, - { url = "https://files.pythonhosted.org/packages/fe/6d/0009021d97e79ee99f3d8641f0a8d001eed23479ade4c3125a5480bf3e2d/regex-2026.2.28-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c15af43c72a7fb0c97cbc66fa36a43546eddc5c06a662b64a0cbf30d6ac40944", size = 849320, upload-time = "2026-02-28T02:17:47.192Z" }, - { url = "https://files.pythonhosted.org/packages/05/7a/51cfbad5758f8edae430cb21961a9c8d04bce1dae4d2d18d4186eec7cfa1/regex-2026.2.28-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9185cc63359862a6e80fe97f696e04b0ad9a11c4ac0a4a927f979f611bfe3768", size = 790152, upload-time = "2026-02-28T02:17:49.067Z" }, - { url = "https://files.pythonhosted.org/packages/90/3d/a83e2b6b3daa142acb8c41d51de3876186307d5cb7490087031747662500/regex-2026.2.28-cp313-cp313-win32.whl", hash = "sha256:fb66e5245db9652abd7196ace599b04d9c0e4aa7c8f0e2803938377835780081", size = 266398, upload-time = "2026-02-28T02:17:50.744Z" }, - { url = "https://files.pythonhosted.org/packages/85/4f/16e9ebb1fe5425e11b9596c8d57bf8877dcb32391da0bfd33742e3290637/regex-2026.2.28-cp313-cp313-win_amd64.whl", hash = "sha256:71a911098be38c859ceb3f9a9ce43f4ed9f4c6720ad8684a066ea246b76ad9ff", size = 277282, upload-time = "2026-02-28T02:17:53.074Z" }, - { url = "https://files.pythonhosted.org/packages/07/b4/92851335332810c5a89723bf7a7e35c7209f90b7d4160024501717b28cc9/regex-2026.2.28-cp313-cp313-win_arm64.whl", hash = "sha256:39bb5727650b9a0275c6a6690f9bb3fe693a7e6cc5c3155b1240aedf8926423e", size = 270382, upload-time = "2026-02-28T02:17:54.888Z" }, - { url = "https://files.pythonhosted.org/packages/24/07/6c7e4cec1e585959e96cbc24299d97e4437a81173217af54f1804994e911/regex-2026.2.28-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:97054c55db06ab020342cc0d35d6f62a465fa7662871190175f1ad6c655c028f", size = 492541, upload-time = "2026-02-28T02:17:56.813Z" }, - { url = "https://files.pythonhosted.org/packages/7c/13/55eb22ada7f43d4f4bb3815b6132183ebc331c81bd496e2d1f3b8d862e0d/regex-2026.2.28-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d25a10811de831c2baa6aef3c0be91622f44dd8d31dd12e69f6398efb15e48b", size = 292984, upload-time = "2026-02-28T02:17:58.538Z" }, - { url = "https://files.pythonhosted.org/packages/5b/11/c301f8cb29ce9644a5ef85104c59244e6e7e90994a0f458da4d39baa8e17/regex-2026.2.28-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d6cfe798d8da41bb1862ed6e0cba14003d387c3c0c4a5d45591076ae9f0ce2f8", size = 291509, upload-time = "2026-02-28T02:18:00.208Z" }, - { url = "https://files.pythonhosted.org/packages/b5/43/aabe384ec1994b91796e903582427bc2ffaed9c4103819ed3c16d8e749f3/regex-2026.2.28-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fd0ce43e71d825b7c0661f9c54d4d74bd97c56c3fd102a8985bcfea48236bacb", size = 809429, upload-time = "2026-02-28T02:18:02.328Z" }, - { url = "https://files.pythonhosted.org/packages/04/b8/8d2d987a816720c4f3109cee7c06a4b24ad0e02d4fc74919ab619e543737/regex-2026.2.28-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00945d007fd74a9084d2ab79b695b595c6b7ba3698972fadd43e23230c6979c1", size = 869422, upload-time = "2026-02-28T02:18:04.23Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ad/2c004509e763c0c3719f97c03eca26473bffb3868d54c5f280b8cd4f9e3d/regex-2026.2.28-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bec23c11cbbf09a4df32fe50d57cbdd777bc442269b6e39a1775654f1c95dee2", size = 915175, upload-time = "2026-02-28T02:18:06.791Z" }, - { url = "https://files.pythonhosted.org/packages/55/c2/fd429066da487ef555a9da73bf214894aec77fc8c66a261ee355a69871a8/regex-2026.2.28-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5cdcc17d935c8f9d3f4db5c2ebe2640c332e3822ad5d23c2f8e0228e6947943a", size = 812044, upload-time = "2026-02-28T02:18:08.736Z" }, - { url = "https://files.pythonhosted.org/packages/5b/ca/feedb7055c62a3f7f659971bf45f0e0a87544b6b0cf462884761453f97c5/regex-2026.2.28-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a448af01e3d8031c89c5d902040b124a5e921a25c4e5e07a861ca591ce429341", size = 782056, upload-time = "2026-02-28T02:18:10.777Z" }, - { url = "https://files.pythonhosted.org/packages/95/30/1aa959ed0d25c1dd7dd5047ea8ba482ceaef38ce363c401fd32a6b923e60/regex-2026.2.28-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:10d28e19bd4888e4abf43bd3925f3c134c52fdf7259219003588a42e24c2aa25", size = 798743, upload-time = "2026-02-28T02:18:13.025Z" }, - { url = "https://files.pythonhosted.org/packages/3b/1f/dadb9cf359004784051c897dcf4d5d79895f73a1bbb7b827abaa4814ae80/regex-2026.2.28-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:99985a2c277dcb9ccb63f937451af5d65177af1efdeb8173ac55b61095a0a05c", size = 864633, upload-time = "2026-02-28T02:18:16.84Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f1/b9a25eb24e1cf79890f09e6ec971ee5b511519f1851de3453bc04f6c902b/regex-2026.2.28-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:e1e7b24cb3ae9953a560c563045d1ba56ee4749fbd05cf21ba571069bd7be81b", size = 770862, upload-time = "2026-02-28T02:18:18.892Z" }, - { url = "https://files.pythonhosted.org/packages/02/9a/c5cb10b7aa6f182f9247a30cc9527e326601f46f4df864ac6db588d11fcd/regex-2026.2.28-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d8511a01d0e4ee1992eb3ba19e09bc1866fe03f05129c3aec3fdc4cbc77aad3f", size = 854788, upload-time = "2026-02-28T02:18:21.475Z" }, - { url = "https://files.pythonhosted.org/packages/0a/50/414ba0731c4bd40b011fa4703b2cc86879ec060c64f2a906e65a56452589/regex-2026.2.28-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:aaffaecffcd2479ce87aa1e74076c221700b7c804e48e98e62500ee748f0f550", size = 800184, upload-time = "2026-02-28T02:18:23.492Z" }, - { url = "https://files.pythonhosted.org/packages/69/50/0c7290987f97e7e6830b0d853f69dc4dc5852c934aae63e7fdcd76b4c383/regex-2026.2.28-cp313-cp313t-win32.whl", hash = "sha256:ef77bdde9c9eba3f7fa5b58084b29bbcc74bcf55fdbeaa67c102a35b5bd7e7cc", size = 269137, upload-time = "2026-02-28T02:18:25.375Z" }, - { url = "https://files.pythonhosted.org/packages/68/80/ef26ff90e74ceb4051ad6efcbbb8a4be965184a57e879ebcbdef327d18fa/regex-2026.2.28-cp313-cp313t-win_amd64.whl", hash = "sha256:98adf340100cbe6fbaf8e6dc75e28f2c191b1be50ffefe292fb0e6f6eefdb0d8", size = 280682, upload-time = "2026-02-28T02:18:27.205Z" }, - { url = "https://files.pythonhosted.org/packages/69/8b/fbad9c52e83ffe8f97e3ed1aa0516e6dff6bb633a41da9e64645bc7efdc5/regex-2026.2.28-cp313-cp313t-win_arm64.whl", hash = "sha256:2fb950ac1d88e6b6a9414381f403797b236f9fa17e1eee07683af72b1634207b", size = 271735, upload-time = "2026-02-28T02:18:29.015Z" }, - { url = "https://files.pythonhosted.org/packages/cf/03/691015f7a7cb1ed6dacb2ea5de5682e4858e05a4c5506b2839cd533bbcd6/regex-2026.2.28-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:78454178c7df31372ea737996fb7f36b3c2c92cccc641d251e072478afb4babc", size = 489497, upload-time = "2026-02-28T02:18:30.889Z" }, - { url = "https://files.pythonhosted.org/packages/c6/ba/8db8fd19afcbfa0e1036eaa70c05f20ca8405817d4ad7a38a6b4c2f031ac/regex-2026.2.28-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:5d10303dd18cedfd4d095543998404df656088240bcfd3cd20a8f95b861f74bd", size = 291295, upload-time = "2026-02-28T02:18:33.426Z" }, - { url = "https://files.pythonhosted.org/packages/5a/79/9aa0caf089e8defef9b857b52fc53801f62ff868e19e5c83d4a96612eba1/regex-2026.2.28-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:19a9c9e0a8f24f39d575a6a854d516b48ffe4cbdcb9de55cb0570a032556ecff", size = 289275, upload-time = "2026-02-28T02:18:35.247Z" }, - { url = "https://files.pythonhosted.org/packages/eb/26/ee53117066a30ef9c883bf1127eece08308ccf8ccd45c45a966e7a665385/regex-2026.2.28-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09500be324f49b470d907b3ef8af9afe857f5cca486f853853f7945ddbf75911", size = 797176, upload-time = "2026-02-28T02:18:37.15Z" }, - { url = "https://files.pythonhosted.org/packages/05/1b/67fb0495a97259925f343ae78b5d24d4a6624356ae138b57f18bd43006e4/regex-2026.2.28-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fb1c4ff62277d87a7335f2c1ea4e0387b8f2b3ad88a64efd9943906aafad4f33", size = 863813, upload-time = "2026-02-28T02:18:39.478Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/93ac9bbafc53618091c685c7ed40239a90bf9f2a82c983f0baa97cb7ae07/regex-2026.2.28-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b8b3f1be1738feadc69f62daa250c933e85c6f34fa378f54a7ff43807c1b9117", size = 908678, upload-time = "2026-02-28T02:18:41.619Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7a/a8f5e0561702b25239846a16349feece59712ae20598ebb205580332a471/regex-2026.2.28-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc8ed8c3f41c27acb83f7b6a9eb727a73fc6663441890c5cb3426a5f6a91ce7d", size = 801528, upload-time = "2026-02-28T02:18:43.624Z" }, - { url = "https://files.pythonhosted.org/packages/96/5d/ed6d4cbde80309854b1b9f42d9062fee38ade15f7eb4909f6ef2440403b5/regex-2026.2.28-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa539be029844c0ce1114762d2952ab6cfdd7c7c9bd72e0db26b94c3c36dcc5a", size = 775373, upload-time = "2026-02-28T02:18:46.102Z" }, - { url = "https://files.pythonhosted.org/packages/6a/e9/6e53c34e8068b9deec3e87210086ecb5b9efebdefca6b0d3fa43d66dcecb/regex-2026.2.28-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7900157786428a79615a8264dac1f12c9b02957c473c8110c6b1f972dcecaddf", size = 784859, upload-time = "2026-02-28T02:18:48.269Z" }, - { url = "https://files.pythonhosted.org/packages/48/3c/736e1c7ca7f0dcd2ae33819888fdc69058a349b7e5e84bc3e2f296bbf794/regex-2026.2.28-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0b1d2b07614d95fa2bf8a63fd1e98bd8fa2b4848dc91b1efbc8ba219fdd73952", size = 857813, upload-time = "2026-02-28T02:18:50.576Z" }, - { url = "https://files.pythonhosted.org/packages/6e/7c/48c4659ad9da61f58e79dbe8c05223e0006696b603c16eb6b5cbfbb52c27/regex-2026.2.28-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b389c61aa28a79c2e0527ac36da579869c2e235a5b208a12c5b5318cda2501d8", size = 763705, upload-time = "2026-02-28T02:18:52.59Z" }, - { url = "https://files.pythonhosted.org/packages/cf/a1/bc1c261789283128165f71b71b4b221dd1b79c77023752a6074c102f18d8/regex-2026.2.28-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f467cb602f03fbd1ab1908f68b53c649ce393fde056628dc8c7e634dab6bfc07", size = 848734, upload-time = "2026-02-28T02:18:54.595Z" }, - { url = "https://files.pythonhosted.org/packages/10/d8/979407faf1397036e25a5ae778157366a911c0f382c62501009f4957cf86/regex-2026.2.28-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e8c8cb2deba42f5ec1ede46374e990f8adc5e6456a57ac1a261b19be6f28e4e6", size = 789871, upload-time = "2026-02-28T02:18:57.34Z" }, - { url = "https://files.pythonhosted.org/packages/03/23/da716821277115fcb1f4e3de1e5dc5023a1e6533598c486abf5448612579/regex-2026.2.28-cp314-cp314-win32.whl", hash = "sha256:9036b400b20e4858d56d117108d7813ed07bb7803e3eed766675862131135ca6", size = 271825, upload-time = "2026-02-28T02:18:59.202Z" }, - { url = "https://files.pythonhosted.org/packages/91/ff/90696f535d978d5f16a52a419be2770a8d8a0e7e0cfecdbfc31313df7fab/regex-2026.2.28-cp314-cp314-win_amd64.whl", hash = "sha256:1d367257cd86c1cbb97ea94e77b373a0bbc2224976e247f173d19e8f18b4afa7", size = 280548, upload-time = "2026-02-28T02:19:01.049Z" }, - { url = "https://files.pythonhosted.org/packages/69/f9/5e1b5652fc0af3fcdf7677e7df3ad2a0d47d669b34ac29a63bb177bb731b/regex-2026.2.28-cp314-cp314-win_arm64.whl", hash = "sha256:5e68192bb3a1d6fb2836da24aa494e413ea65853a21505e142e5b1064a595f3d", size = 273444, upload-time = "2026-02-28T02:19:03.255Z" }, - { url = "https://files.pythonhosted.org/packages/d3/eb/8389f9e940ac89bcf58d185e230a677b4fd07c5f9b917603ad5c0f8fa8fe/regex-2026.2.28-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:a5dac14d0872eeb35260a8e30bac07ddf22adc1e3a0635b52b02e180d17c9c7e", size = 492546, upload-time = "2026-02-28T02:19:05.378Z" }, - { url = "https://files.pythonhosted.org/packages/7b/c7/09441d27ce2a6fa6a61ea3150ea4639c1dcda9b31b2ea07b80d6937b24dd/regex-2026.2.28-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ec0c608b7a7465ffadb344ed7c987ff2f11ee03f6a130b569aa74d8a70e8333c", size = 292986, upload-time = "2026-02-28T02:19:07.24Z" }, - { url = "https://files.pythonhosted.org/packages/fb/69/4144b60ed7760a6bd235e4087041f487aa4aa62b45618ce018b0c14833ea/regex-2026.2.28-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7815afb0ca45456613fdaf60ea9c993715511c8d53a83bc468305cbc0ee23c7", size = 291518, upload-time = "2026-02-28T02:19:09.698Z" }, - { url = "https://files.pythonhosted.org/packages/2d/be/77e5426cf5948c82f98c53582009ca9e94938c71f73a8918474f2e2990bb/regex-2026.2.28-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b059e71ec363968671693a78c5053bd9cb2fe410f9b8e4657e88377ebd603a2e", size = 809464, upload-time = "2026-02-28T02:19:12.494Z" }, - { url = "https://files.pythonhosted.org/packages/45/99/2c8c5ac90dc7d05c6e7d8e72c6a3599dc08cd577ac476898e91ca787d7f1/regex-2026.2.28-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8cf76f1a29f0e99dcfd7aef1551a9827588aae5a737fe31442021165f1920dc", size = 869553, upload-time = "2026-02-28T02:19:15.151Z" }, - { url = "https://files.pythonhosted.org/packages/53/34/daa66a342f0271e7737003abf6c3097aa0498d58c668dbd88362ef94eb5d/regex-2026.2.28-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:180e08a435a0319e6a4821c3468da18dc7001987e1c17ae1335488dfe7518dd8", size = 915289, upload-time = "2026-02-28T02:19:17.331Z" }, - { url = "https://files.pythonhosted.org/packages/c5/c7/e22c2aaf0a12e7e22ab19b004bb78d32ca1ecc7ef245949935463c5567de/regex-2026.2.28-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e496956106fd59ba6322a8ea17141a27c5040e5ee8f9433ae92d4e5204462a0", size = 812156, upload-time = "2026-02-28T02:19:20.011Z" }, - { url = "https://files.pythonhosted.org/packages/7f/bb/2dc18c1efd9051cf389cd0d7a3a4d90f6804b9fff3a51b5dc3c85b935f71/regex-2026.2.28-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bba2b18d70eeb7b79950f12f633beeecd923f7c9ad6f6bae28e59b4cb3ab046b", size = 782215, upload-time = "2026-02-28T02:19:22.047Z" }, - { url = "https://files.pythonhosted.org/packages/17/1e/9e4ec9b9013931faa32226ec4aa3c71fe664a6d8a2b91ac56442128b332f/regex-2026.2.28-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6db7bfae0f8a2793ff1f7021468ea55e2699d0790eb58ee6ab36ae43aa00bc5b", size = 798925, upload-time = "2026-02-28T02:19:24.173Z" }, - { url = "https://files.pythonhosted.org/packages/71/57/a505927e449a9ccb41e2cc8d735e2abe3444b0213d1cf9cb364a8c1f2524/regex-2026.2.28-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d0b02e8b7e5874b48ae0f077ecca61c1a6a9f9895e9c6dfb191b55b242862033", size = 864701, upload-time = "2026-02-28T02:19:26.376Z" }, - { url = "https://files.pythonhosted.org/packages/a6/ad/c62cb60cdd93e13eac5b3d9d6bd5d284225ed0e3329426f94d2552dd7cca/regex-2026.2.28-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:25b6eb660c5cf4b8c3407a1ed462abba26a926cc9965e164268a3267bcc06a43", size = 770899, upload-time = "2026-02-28T02:19:29.38Z" }, - { url = "https://files.pythonhosted.org/packages/3c/5a/874f861f5c3d5ab99633e8030dee1bc113db8e0be299d1f4b07f5b5ec349/regex-2026.2.28-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:5a932ea8ad5d0430351ff9c76c8db34db0d9f53c1d78f06022a21f4e290c5c18", size = 854727, upload-time = "2026-02-28T02:19:31.494Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ca/d2c03b0efde47e13db895b975b2be6a73ed90b8ba963677927283d43bf74/regex-2026.2.28-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1c2c95e1a2b0f89d01e821ff4de1be4b5d73d1f4b0bf679fa27c1ad8d2327f1a", size = 800366, upload-time = "2026-02-28T02:19:34.248Z" }, - { url = "https://files.pythonhosted.org/packages/14/bd/ee13b20b763b8989f7c75d592bfd5de37dc1181814a2a2747fedcf97e3ba/regex-2026.2.28-cp314-cp314t-win32.whl", hash = "sha256:bbb882061f742eb5d46f2f1bd5304055be0a66b783576de3d7eef1bed4778a6e", size = 274936, upload-time = "2026-02-28T02:19:36.313Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e7/d8020e39414c93af7f0d8688eabcecece44abfd5ce314b21dfda0eebd3d8/regex-2026.2.28-cp314-cp314t-win_amd64.whl", hash = "sha256:6591f281cb44dc13de9585b552cec6fc6cf47fb2fe7a48892295ee9bc4a612f9", size = 284779, upload-time = "2026-02-28T02:19:38.625Z" }, - { url = "https://files.pythonhosted.org/packages/13/c0/ad225f4a405827486f1955283407cf758b6d2fb966712644c5f5aef33d1b/regex-2026.2.28-cp314-cp314t-win_arm64.whl", hash = "sha256:dee50f1be42222f89767b64b283283ef963189da0dda4a515aa54a5563c62dec", size = 275010, upload-time = "2026-02-28T02:19:40.65Z" }, -] - -[[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 = "requests-toolbelt" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } -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 = "rfc3986" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", size = 49026, upload-time = "2022-01-10T00:52:30.832Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", size = 31326, upload-time = "2022-01-10T00:52:29.594Z" }, -] - -[[package]] -name = "rich" -version = "14.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, -] - -[[package]] -name = "rich-rst" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, -] - -[[package]] -name = "ruff" -version = "0.15.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/22/9e4f66ee588588dc6c9af6a994e12d26e19efbe874d1a909d09a6dac7a59/ruff-0.15.7.tar.gz", hash = "sha256:04f1ae61fc20fe0b148617c324d9d009b5f63412c0b16474f3d5f1a1a665f7ac", size = 4601277, upload-time = "2026-03-19T16:26:22.605Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/2f/0b08ced94412af091807b6119ca03755d651d3d93a242682bf020189db94/ruff-0.15.7-py3-none-linux_armv6l.whl", hash = "sha256:a81cc5b6910fb7dfc7c32d20652e50fa05963f6e13ead3c5915c41ac5d16668e", size = 10489037, upload-time = "2026-03-19T16:26:32.47Z" }, - { url = "https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:722d165bd52403f3bdabc0ce9e41fc47070ac56d7a91b4e0d097b516a53a3477", size = 10955433, upload-time = "2026-03-19T16:27:00.205Z" }, - { url = "https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fbc2448094262552146cbe1b9643a92f66559d3761f1ad0656d4991491af49e", size = 10269302, upload-time = "2026-03-19T16:26:26.183Z" }, - { url = "https://files.pythonhosted.org/packages/eb/5d/32b5c44ccf149a26623671df49cbfbd0a0ae511ff3df9d9d2426966a8d57/ruff-0.15.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b39329b60eba44156d138275323cc726bbfbddcec3063da57caa8a8b1d50adf", size = 10607625, upload-time = "2026-03-19T16:27:03.263Z" }, - { url = "https://files.pythonhosted.org/packages/5d/f1/f0001cabe86173aaacb6eb9bb734aa0605f9a6aa6fa7d43cb49cbc4af9c9/ruff-0.15.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87768c151808505f2bfc93ae44e5f9e7c8518943e5074f76ac21558ef5627c85", size = 10324743, upload-time = "2026-03-19T16:27:09.791Z" }, - { url = "https://files.pythonhosted.org/packages/7a/87/b8a8f3d56b8d848008559e7c9d8bf367934d5367f6d932ba779456e2f73b/ruff-0.15.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb0511670002c6c529ec66c0e30641c976c8963de26a113f3a30456b702468b0", size = 11138536, upload-time = "2026-03-19T16:27:06.101Z" }, - { url = "https://files.pythonhosted.org/packages/e4/f2/4fd0d05aab0c5934b2e1464784f85ba2eab9d54bffc53fb5430d1ed8b829/ruff-0.15.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0d19644f801849229db8345180a71bee5407b429dd217f853ec515e968a6912", size = 11994292, upload-time = "2026-03-19T16:26:48.718Z" }, - { url = "https://files.pythonhosted.org/packages/64/22/fc4483871e767e5e95d1622ad83dad5ebb830f762ed0420fde7dfa9d9b08/ruff-0.15.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4806d8e09ef5e84eb19ba833d0442f7e300b23fe3f0981cae159a248a10f0036", size = 11398981, upload-time = "2026-03-19T16:26:54.513Z" }, - { url = "https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dce0896488562f09a27b9c91b1f58a097457143931f3c4d519690dea54e624c5", size = 11242422, upload-time = "2026-03-19T16:26:29.277Z" }, - { url = "https://files.pythonhosted.org/packages/5d/3a/a7060f145bfdcce4c987ea27788b30c60e2c81d6e9a65157ca8afe646328/ruff-0.15.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1852ce241d2bc89e5dc823e03cff4ce73d816b5c6cdadd27dbfe7b03217d2a12", size = 11232158, upload-time = "2026-03-19T16:26:42.321Z" }, - { url = "https://files.pythonhosted.org/packages/a7/53/90fbb9e08b29c048c403558d3cdd0adf2668b02ce9d50602452e187cd4af/ruff-0.15.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5f3e4b221fb4bd293f79912fc5e93a9063ebd6d0dcbd528f91b89172a9b8436c", size = 10577861, upload-time = "2026-03-19T16:26:57.459Z" }, - { url = "https://files.pythonhosted.org/packages/2f/aa/5f486226538fe4d0f0439e2da1716e1acf895e2a232b26f2459c55f8ddad/ruff-0.15.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b15e48602c9c1d9bdc504b472e90b90c97dc7d46c7028011ae67f3861ceba7b4", size = 10327310, upload-time = "2026-03-19T16:26:35.909Z" }, - { url = "https://files.pythonhosted.org/packages/99/9e/271afdffb81fe7bfc8c43ba079e9d96238f674380099457a74ccb3863857/ruff-0.15.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1b4705e0e85cedc74b0a23cf6a179dbb3df184cb227761979cc76c0440b5ab0d", size = 10840752, upload-time = "2026-03-19T16:26:45.723Z" }, - { url = "https://files.pythonhosted.org/packages/bf/29/a4ae78394f76c7759953c47884eb44de271b03a66634148d9f7d11e721bd/ruff-0.15.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:112c1fa316a558bb34319282c1200a8bf0495f1b735aeb78bfcb2991e6087580", size = 11336961, upload-time = "2026-03-19T16:26:39.076Z" }, - { url = "https://files.pythonhosted.org/packages/26/6b/8786ba5736562220d588a2f6653e6c17e90c59ced34a2d7b512ef8956103/ruff-0.15.7-py3-none-win32.whl", hash = "sha256:6d39e2d3505b082323352f733599f28169d12e891f7dd407f2d4f54b4c2886de", size = 10582538, upload-time = "2026-03-19T16:26:15.992Z" }, - { url = "https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl", hash = "sha256:4d53d712ddebcd7dace1bc395367aec12c057aacfe9adbb6d832302575f4d3a1", size = 11755839, upload-time = "2026-03-19T16:26:19.897Z" }, - { url = "https://files.pythonhosted.org/packages/8f/e8/726643a3ea68c727da31570bde48c7a10f1aa60eddd628d94078fec586ff/ruff-0.15.7-py3-none-win_arm64.whl", hash = "sha256:18e8d73f1c3fdf27931497972250340f92e8c861722161a9caeb89a58ead6ed2", size = 11023304, upload-time = "2026-03-19T16:26:51.669Z" }, -] - -[[package]] -name = "secretstorage" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[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 = "sqlite-vec" -version = "0.1.7" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/50/7ad59cfd3003a2110cc366e526293de4c2520486f5ddaa8dc78b265f8d3e/sqlite_vec-0.1.7-py3-none-macosx_10_6_x86_64.whl", hash = "sha256:c34a136caecff4ae17d4c0cc268fcda89764ee870039caa21431e8e3fb2f4d48", size = 131171, upload-time = "2026-03-17T07:42:50.438Z" }, - { url = "https://files.pythonhosted.org/packages/e6/c9/1cd2f59b539096cd2ce6b540247b2dfe3c47ba04d9368b5e8e3dc86498d4/sqlite_vec-0.1.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6d272593d1b45ec7ea289b160ee6e5fafbaa6e1f5ba15f1305c012b0bda43653", size = 165434, upload-time = "2026-03-17T07:42:51.555Z" }, - { url = "https://files.pythonhosted.org/packages/75/91/30c3c382140dcc7bc6e3a07eac7ca610a2b5b70eb9bc7066dc3e7f748d58/sqlite_vec-0.1.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d27746d8e254a390bd15574aed899a0b9bb915b5321eb130a9c09722898cc03", size = 160076, upload-time = "2026-03-17T07:42:52.451Z" }, - { url = "https://files.pythonhosted.org/packages/59/56/6ff304d917ee79da769708dad0aed5fd34c72cbd0ae5e38bcc56cdc652a4/sqlite_vec-0.1.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux1_x86_64.whl", hash = "sha256:ad654283cb9c059852ce2d82018c757b06a705ada568f8b126022a131189818e", size = 163388, upload-time = "2026-03-17T07:42:53.516Z" }, - { url = "https://files.pythonhosted.org/packages/8b/27/fb1b6e3f9072854fe405f7aa99c46d4b465e84c9cec2ff7778edf29ecbbd/sqlite_vec-0.1.7-py3-none-win_amd64.whl", hash = "sha256:0c67877a87cb49426237b950237e82dbeb77778ab2ba89bea859f391fd169382", size = 292804, upload-time = "2026-03-17T07:42:54.325Z" }, -] - -[[package]] -name = "sse-starlette" -version = "3.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "starlette" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/14/2f/9223c24f568bb7a0c03d751e609844dce0968f13b39a3f73fbb3a96cd27a/sse_starlette-3.3.3.tar.gz", hash = "sha256:72a95d7575fd5129bd0ae15275ac6432bb35ac542fdebb82889c24bb9f3f4049", size = 32420, upload-time = "2026-03-17T20:05:55.529Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/e2/b8cff57a67dddf9a464d7e943218e031617fb3ddc133aeeb0602ff5f6c85/sse_starlette-3.3.3-py3-none-any.whl", hash = "sha256:c5abb5082a1cc1c6294d89c5290c46b5f67808cfdb612b7ec27e8ba061c22e8d", size = 14329, upload-time = "2026-03-17T20:05:54.35Z" }, -] - -[[package]] -name = "stack-data" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asttokens" }, - { name = "executing" }, - { name = "pure-eval" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, -] - -[[package]] -name = "starlette" -version = "0.52.1" -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/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, -] - -[[package]] -name = "structlog" -version = "25.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ef/52/9ba0f43b686e7f3ddfeaa78ac3af750292662284b3661e91ad5494f21dbc/structlog-25.5.0.tar.gz", hash = "sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98", size = 1460830, upload-time = "2025-10-27T08:28:23.028Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/45/a132b9074aa18e799b891b91ad72133c98d8042c70f6240e4c5f9dabee2f/structlog-25.5.0-py3-none-any.whl", hash = "sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f", size = 72510, upload-time = "2025-10-27T08:28:21.535Z" }, -] - -[[package]] -name = "sympy" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mpmath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, -] - -[[package]] -name = "tiktoken" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "regex" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806, upload-time = "2025-10-06T20:22:45.419Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/85/be65d39d6b647c79800fd9d29241d081d4eeb06271f383bb87200d74cf76/tiktoken-0.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b97f74aca0d78a1ff21b8cd9e9925714c15a9236d6ceacf5c7327c117e6e21e8", size = 1050728, upload-time = "2025-10-06T20:21:52.756Z" }, - { url = "https://files.pythonhosted.org/packages/4a/42/6573e9129bc55c9bf7300b3a35bef2c6b9117018acca0dc760ac2d93dffe/tiktoken-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b90f5ad190a4bb7c3eb30c5fa32e1e182ca1ca79f05e49b448438c3e225a49b", size = 994049, upload-time = "2025-10-06T20:21:53.782Z" }, - { url = "https://files.pythonhosted.org/packages/66/c5/ed88504d2f4a5fd6856990b230b56d85a777feab84e6129af0822f5d0f70/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:65b26c7a780e2139e73acc193e5c63ac754021f160df919add909c1492c0fb37", size = 1129008, upload-time = "2025-10-06T20:21:54.832Z" }, - { url = "https://files.pythonhosted.org/packages/f4/90/3dae6cc5436137ebd38944d396b5849e167896fc2073da643a49f372dc4f/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:edde1ec917dfd21c1f2f8046b86348b0f54a2c0547f68149d8600859598769ad", size = 1152665, upload-time = "2025-10-06T20:21:56.129Z" }, - { url = "https://files.pythonhosted.org/packages/a3/fe/26df24ce53ffde419a42f5f53d755b995c9318908288c17ec3f3448313a3/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:35a2f8ddd3824608b3d650a000c1ef71f730d0c56486845705a8248da00f9fe5", size = 1194230, upload-time = "2025-10-06T20:21:57.546Z" }, - { url = "https://files.pythonhosted.org/packages/20/cc/b064cae1a0e9fac84b0d2c46b89f4e57051a5f41324e385d10225a984c24/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83d16643edb7fa2c99eff2ab7733508aae1eebb03d5dfc46f5565862810f24e3", size = 1254688, upload-time = "2025-10-06T20:21:58.619Z" }, - { url = "https://files.pythonhosted.org/packages/81/10/b8523105c590c5b8349f2587e2fdfe51a69544bd5a76295fc20f2374f470/tiktoken-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffc5288f34a8bc02e1ea7047b8d041104791d2ddbf42d1e5fa07822cbffe16bd", size = 878694, upload-time = "2025-10-06T20:21:59.876Z" }, - { url = "https://files.pythonhosted.org/packages/00/61/441588ee21e6b5cdf59d6870f86beb9789e532ee9718c251b391b70c68d6/tiktoken-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3", size = 1050802, upload-time = "2025-10-06T20:22:00.96Z" }, - { url = "https://files.pythonhosted.org/packages/1f/05/dcf94486d5c5c8d34496abe271ac76c5b785507c8eae71b3708f1ad9b45a/tiktoken-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160", size = 993995, upload-time = "2025-10-06T20:22:02.788Z" }, - { url = "https://files.pythonhosted.org/packages/a0/70/5163fe5359b943f8db9946b62f19be2305de8c3d78a16f629d4165e2f40e/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa", size = 1128948, upload-time = "2025-10-06T20:22:03.814Z" }, - { url = "https://files.pythonhosted.org/packages/0c/da/c028aa0babf77315e1cef357d4d768800c5f8a6de04d0eac0f377cb619fa/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be", size = 1151986, upload-time = "2025-10-06T20:22:05.173Z" }, - { url = "https://files.pythonhosted.org/packages/a0/5a/886b108b766aa53e295f7216b509be95eb7d60b166049ce2c58416b25f2a/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a", size = 1194222, upload-time = "2025-10-06T20:22:06.265Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f8/4db272048397636ac7a078d22773dd2795b1becee7bc4922fe6207288d57/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3", size = 1255097, upload-time = "2025-10-06T20:22:07.403Z" }, - { url = "https://files.pythonhosted.org/packages/8e/32/45d02e2e0ea2be3a9ed22afc47d93741247e75018aac967b713b2941f8ea/tiktoken-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697", size = 879117, upload-time = "2025-10-06T20:22:08.418Z" }, - { url = "https://files.pythonhosted.org/packages/ce/76/994fc868f88e016e6d05b0da5ac24582a14c47893f4474c3e9744283f1d5/tiktoken-0.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16", size = 1050309, upload-time = "2025-10-06T20:22:10.939Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b8/57ef1456504c43a849821920d582a738a461b76a047f352f18c0b26c6516/tiktoken-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a", size = 993712, upload-time = "2025-10-06T20:22:12.115Z" }, - { url = "https://files.pythonhosted.org/packages/72/90/13da56f664286ffbae9dbcfadcc625439142675845baa62715e49b87b68b/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27", size = 1128725, upload-time = "2025-10-06T20:22:13.541Z" }, - { url = "https://files.pythonhosted.org/packages/05/df/4f80030d44682235bdaecd7346c90f67ae87ec8f3df4a3442cb53834f7e4/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb", size = 1151875, upload-time = "2025-10-06T20:22:14.559Z" }, - { url = "https://files.pythonhosted.org/packages/22/1f/ae535223a8c4ef4c0c1192e3f9b82da660be9eb66b9279e95c99288e9dab/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e", size = 1194451, upload-time = "2025-10-06T20:22:15.545Z" }, - { url = "https://files.pythonhosted.org/packages/78/a7/f8ead382fce0243cb625c4f266e66c27f65ae65ee9e77f59ea1653b6d730/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25", size = 1253794, upload-time = "2025-10-06T20:22:16.624Z" }, - { url = "https://files.pythonhosted.org/packages/93/e0/6cc82a562bc6365785a3ff0af27a2a092d57c47d7a81d9e2295d8c36f011/tiktoken-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f", size = 878777, upload-time = "2025-10-06T20:22:18.036Z" }, - { url = "https://files.pythonhosted.org/packages/72/05/3abc1db5d2c9aadc4d2c76fa5640134e475e58d9fbb82b5c535dc0de9b01/tiktoken-0.12.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a90388128df3b3abeb2bfd1895b0681412a8d7dc644142519e6f0a97c2111646", size = 1050188, upload-time = "2025-10-06T20:22:19.563Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7b/50c2f060412202d6c95f32b20755c7a6273543b125c0985d6fa9465105af/tiktoken-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:da900aa0ad52247d8794e307d6446bd3cdea8e192769b56276695d34d2c9aa88", size = 993978, upload-time = "2025-10-06T20:22:20.702Z" }, - { url = "https://files.pythonhosted.org/packages/14/27/bf795595a2b897e271771cd31cb847d479073497344c637966bdf2853da1/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:285ba9d73ea0d6171e7f9407039a290ca77efcdb026be7769dccc01d2c8d7fff", size = 1129271, upload-time = "2025-10-06T20:22:22.06Z" }, - { url = "https://files.pythonhosted.org/packages/f5/de/9341a6d7a8f1b448573bbf3425fa57669ac58258a667eb48a25dfe916d70/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:d186a5c60c6a0213f04a7a802264083dea1bbde92a2d4c7069e1a56630aef830", size = 1151216, upload-time = "2025-10-06T20:22:23.085Z" }, - { url = "https://files.pythonhosted.org/packages/75/0d/881866647b8d1be4d67cb24e50d0c26f9f807f994aa1510cb9ba2fe5f612/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:604831189bd05480f2b885ecd2d1986dc7686f609de48208ebbbddeea071fc0b", size = 1194860, upload-time = "2025-10-06T20:22:24.602Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1e/b651ec3059474dab649b8d5b69f5c65cd8fcd8918568c1935bd4136c9392/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8f317e8530bb3a222547b85a58583238c8f74fd7a7408305f9f63246d1a0958b", size = 1254567, upload-time = "2025-10-06T20:22:25.671Z" }, - { url = "https://files.pythonhosted.org/packages/80/57/ce64fd16ac390fafde001268c364d559447ba09b509181b2808622420eec/tiktoken-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:399c3dd672a6406719d84442299a490420b458c44d3ae65516302a99675888f3", size = 921067, upload-time = "2025-10-06T20:22:26.753Z" }, - { url = "https://files.pythonhosted.org/packages/ac/a4/72eed53e8976a099539cdd5eb36f241987212c29629d0a52c305173e0a68/tiktoken-0.12.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2c714c72bc00a38ca969dae79e8266ddec999c7ceccd603cc4f0d04ccd76365", size = 1050473, upload-time = "2025-10-06T20:22:27.775Z" }, - { url = "https://files.pythonhosted.org/packages/e6/d7/0110b8f54c008466b19672c615f2168896b83706a6611ba6e47313dbc6e9/tiktoken-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cbb9a3ba275165a2cb0f9a83f5d7025afe6b9d0ab01a22b50f0e74fee2ad253e", size = 993855, upload-time = "2025-10-06T20:22:28.799Z" }, - { url = "https://files.pythonhosted.org/packages/5f/77/4f268c41a3957c418b084dd576ea2fad2e95da0d8e1ab705372892c2ca22/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:dfdfaa5ffff8993a3af94d1125870b1d27aed7cb97aa7eb8c1cefdbc87dbee63", size = 1129022, upload-time = "2025-10-06T20:22:29.981Z" }, - { url = "https://files.pythonhosted.org/packages/4e/2b/fc46c90fe5028bd094cd6ee25a7db321cb91d45dc87531e2bdbb26b4867a/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:584c3ad3d0c74f5269906eb8a659c8bfc6144a52895d9261cdaf90a0ae5f4de0", size = 1150736, upload-time = "2025-10-06T20:22:30.996Z" }, - { url = "https://files.pythonhosted.org/packages/28/c0/3c7a39ff68022ddfd7d93f3337ad90389a342f761c4d71de99a3ccc57857/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:54c891b416a0e36b8e2045b12b33dd66fb34a4fe7965565f1b482da50da3e86a", size = 1194908, upload-time = "2025-10-06T20:22:32.073Z" }, - { url = "https://files.pythonhosted.org/packages/ab/0d/c1ad6f4016a3968c048545f5d9b8ffebf577774b2ede3e2e352553b685fe/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5edb8743b88d5be814b1a8a8854494719080c28faaa1ccbef02e87354fe71ef0", size = 1253706, upload-time = "2025-10-06T20:22:33.385Z" }, - { url = "https://files.pythonhosted.org/packages/af/df/c7891ef9d2712ad774777271d39fdef63941ffba0a9d59b7ad1fd2765e57/tiktoken-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f61c0aea5565ac82e2ec50a05e02a6c44734e91b51c10510b084ea1b8e633a71", size = 920667, upload-time = "2025-10-06T20:22:34.444Z" }, -] - -[[package]] -name = "time-machine" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/fc/37b02f6094dbb1f851145330460532176ed2f1dc70511a35828166c41e52/time_machine-3.2.0.tar.gz", hash = "sha256:a4ddd1cea17b8950e462d1805a42b20c81eb9aafc8f66b392dd5ce997e037d79", size = 14804, upload-time = "2025-12-17T23:33:02.599Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/8b/080c8eedcd67921a52ba5bd0e075362062509ab63c86fc1a0442fad241a6/time_machine-3.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:cc4bee5b0214d7dc4ebc91f4a4c600f1a598e9b5606ac751f42cb6f6740b1dbb", size = 19255, upload-time = "2025-12-17T23:31:58.057Z" }, - { url = "https://files.pythonhosted.org/packages/66/17/0e5291e9eb705bf8a5a1305f826e979af307bbeb79def4ddbf4b3f9a81e0/time_machine-3.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ca036304b4460ae2fdc1b52dd8b1fa7cf1464daa427fc49567413c09aa839c1", size = 15360, upload-time = "2025-12-17T23:31:59.048Z" }, - { url = "https://files.pythonhosted.org/packages/8b/e8/9ab87b71d2e2b62463b9b058b7ae7ac09fb57f8fcd88729dec169d304340/time_machine-3.2.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5442735b41d7a2abc2f04579b4ca6047ed4698a8338a4fec92c7c9423e7938cb", size = 33029, upload-time = "2025-12-17T23:32:00.413Z" }, - { url = "https://files.pythonhosted.org/packages/4b/26/b5ca19da6f25ea905b3e10a0ea95d697c1aeba0404803a43c68f1af253e6/time_machine-3.2.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:97da3e971e505cb637079fb07ab0bcd36e33279f8ecac888ff131f45ef1e4d8d", size = 34579, upload-time = "2025-12-17T23:32:01.431Z" }, - { url = "https://files.pythonhosted.org/packages/79/ca/6ac7ad5f10ea18cc1d9de49716ba38c32132c7b64532430d92ef240c116b/time_machine-3.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3cdda6dee4966e38aeb487309bb414c6cb23a81fc500291c77a8fcd3098832e7", size = 35961, upload-time = "2025-12-17T23:32:02.521Z" }, - { url = "https://files.pythonhosted.org/packages/33/67/390dd958bed395ab32d79a9fe61fe111825c0dd4ded54dbba7e867f171e6/time_machine-3.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:33d9efd302a6998bcc8baa4d84f259f8a4081105bd3d7f7af7f1d0abd3b1c8aa", size = 34668, upload-time = "2025-12-17T23:32:03.585Z" }, - { url = "https://files.pythonhosted.org/packages/da/57/c88fff034a4e9538b3ae7c68c9cfb283670b14d17522c5a8bc17d29f9a4b/time_machine-3.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3a0b0a33971f14145853c9bd95a6ab0353cf7e0019fa2a7aa1ae9fddfe8eab50", size = 32891, upload-time = "2025-12-17T23:32:04.656Z" }, - { url = "https://files.pythonhosted.org/packages/2d/70/ebbb76022dba0fec8f9156540fc647e4beae1680c787c01b1b6200e56d70/time_machine-3.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2d0be9e5f22c38082d247a2cdcd8a936504e9db60b7b3606855fb39f299e9548", size = 34080, upload-time = "2025-12-17T23:32:06.146Z" }, - { url = "https://files.pythonhosted.org/packages/db/9a/2ca9e7af3df540dc1c79e3de588adeddb7dcc2107829248e6969c4f14167/time_machine-3.2.0-cp312-cp312-win32.whl", hash = "sha256:3f74623648b936fdce5f911caf386c0a0b579456410975de8c0dfeaaffece1d8", size = 17371, upload-time = "2025-12-17T23:32:07.164Z" }, - { url = "https://files.pythonhosted.org/packages/d8/ce/21d23efc9c2151939af1b7ee4e60d86d661b74ef32b8eaa148f6fe8c899c/time_machine-3.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:34e26a41d994b5e4b205136a90e9578470386749cc9a2ecf51ca18f83ce25e23", size = 18132, upload-time = "2025-12-17T23:32:08.447Z" }, - { url = "https://files.pythonhosted.org/packages/2f/34/c2b70be483accf6db9e5d6c3139bce3c38fe51f898ccf64e8d3fe14fbf4d/time_machine-3.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:0615d3d82c418d6293f271c348945c5091a71f37e37173653d5c26d0e74b13a8", size = 16930, upload-time = "2025-12-17T23:32:09.477Z" }, - { url = "https://files.pythonhosted.org/packages/ee/cd/43ad5efc88298af3c59b66769cea7f055567a85071579ed40536188530c1/time_machine-3.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c421a8eb85a4418a7675a41bf8660224318c46cc62e4751c8f1ceca752059090", size = 19318, upload-time = "2025-12-17T23:32:10.518Z" }, - { url = "https://files.pythonhosted.org/packages/b0/f6/084010ef7f4a3f38b5a4900923d7c85b29e797655c4f6ee4ce54d903cca8/time_machine-3.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f4e758f7727d0058c4950c66b58200c187072122d6f7a98b610530a4233ea7b", size = 15390, upload-time = "2025-12-17T23:32:11.625Z" }, - { url = "https://files.pythonhosted.org/packages/25/aa/1cabb74134f492270dc6860cb7865859bf40ecf828be65972827646e91ad/time_machine-3.2.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:154bd3f75c81f70218b2585cc12b60762fb2665c507eec5ec5037d8756d9b4e0", size = 33115, upload-time = "2025-12-17T23:32:13.219Z" }, - { url = "https://files.pythonhosted.org/packages/5e/03/78c5d7dfa366924eb4dbfcc3fc917c39a4280ca234b12819cc1f16c03d88/time_machine-3.2.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d50cfe5ebea422c896ad8d278af9648412b7533b8ea6adeeee698a3fd9b1d3b7", size = 34705, upload-time = "2025-12-17T23:32:14.29Z" }, - { url = "https://files.pythonhosted.org/packages/86/93/d5e877c24541f674c6869ff6e9c56833369796010190252e92c9d7ae5f0f/time_machine-3.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:636576501724bd6a9124e69d86e5aef263479e89ef739c5db361469f0463a0a1", size = 36104, upload-time = "2025-12-17T23:32:15.354Z" }, - { url = "https://files.pythonhosted.org/packages/22/1c/d4bae72f388f67efc9609f89b012e434bb19d9549c7a7b47d6c7d9e5c55d/time_machine-3.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:40e6f40c57197fcf7ec32d2c563f4df0a82c42cdcc3cab27f688e98f6060df10", size = 34765, upload-time = "2025-12-17T23:32:16.434Z" }, - { url = "https://files.pythonhosted.org/packages/1d/c3/ac378cf301d527d8dfad2f0db6bad0dfb1ab73212eaa56d6b96ee5d9d20b/time_machine-3.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a1bcf0b846bbfc19a79bc19e3fa04d8c7b1e8101c1b70340ffdb689cd801ea53", size = 33010, upload-time = "2025-12-17T23:32:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/06/35/7ce897319accda7a6970b288a9a8c52d25227342a7508505a2b3d235b649/time_machine-3.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ae55a56c179f4fe7a62575ad5148b6ed82f6c7e5cf2f9a9ec65f2f5b067db5f5", size = 34185, upload-time = "2025-12-17T23:32:18.566Z" }, - { url = "https://files.pythonhosted.org/packages/bf/28/f922022269749cb02eee2b62919671153c4088994fa955a6b0e50327ff81/time_machine-3.2.0-cp313-cp313-win32.whl", hash = "sha256:a66fe55a107e46916007a391d4030479df8864ec6ad6f6a6528221befc5c886e", size = 17397, upload-time = "2025-12-17T23:32:19.605Z" }, - { url = "https://files.pythonhosted.org/packages/ee/dc/fd87cde397f4a7bea493152f0aca8fd569ec709cad9e0f2ca7011eb8c7f7/time_machine-3.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:30c9ce57165df913e4f74e285a8ab829ff9b7aa3e5ec0973f88f642b9a7b3d15", size = 18139, upload-time = "2025-12-17T23:32:20.991Z" }, - { url = "https://files.pythonhosted.org/packages/75/81/b8ce58233addc5d7d54d2fabc49dcbc02d79e3f079d150aa1bec3d5275ef/time_machine-3.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:89cad7e179e9bdcc84dcf09efe52af232c4cc7a01b3de868356bbd59d95bd9b8", size = 16964, upload-time = "2025-12-17T23:32:22.075Z" }, - { url = "https://files.pythonhosted.org/packages/67/e7/487f0ba5fe6c58186a5e1af2a118dfa2c160fedb37ef53a7e972d410408e/time_machine-3.2.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:59d71545e62525a4b85b6de9ab5c02ee3c61110fd7f636139914a2335dcbfc9c", size = 20000, upload-time = "2025-12-17T23:32:23.058Z" }, - { url = "https://files.pythonhosted.org/packages/e1/17/eb2c0054c8d44dd42df84ccd434539249a9c7d0b8eb53f799be2102500ab/time_machine-3.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:999672c621c35362bc28e03ca0c7df21500195540773c25993421fd8d6cc5003", size = 15657, upload-time = "2025-12-17T23:32:24.125Z" }, - { url = "https://files.pythonhosted.org/packages/43/21/93443b5d1dd850f8bb9442e90d817a9033dcce6bfbdd3aabbb9786251c80/time_machine-3.2.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5faf7397f0580c7b9d67288522c8d7863e85f0cffadc0f1fccdb2c3dfce5783e", size = 39216, upload-time = "2025-12-17T23:32:25.542Z" }, - { url = "https://files.pythonhosted.org/packages/9f/9e/18544cf8acc72bb1dc03762231c82ecc259733f4bb6770a7bbe5cd138603/time_machine-3.2.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3dd886ec49f1fa5a00e844f5947e5c0f98ce574750c24b7424c6f77fc1c3e87", size = 40764, upload-time = "2025-12-17T23:32:26.643Z" }, - { url = "https://files.pythonhosted.org/packages/27/f7/9fe9ce2795636a3a7467307af6bdf38bb613ddb701a8a5cd50ec713beb5e/time_machine-3.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da0ecd96bc7bbe450acaaabe569d84e81688f1be8ad58d1470e42371d145fb53", size = 43526, upload-time = "2025-12-17T23:32:27.693Z" }, - { url = "https://files.pythonhosted.org/packages/03/c1/a93e975ba9dec22e87ec92d18c28e67d36bd536f9119ffa439b2892b0c9c/time_machine-3.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:158220e946c1c4fb8265773a0282c88c35a7e3bb5d78e3561214e3b3231166f3", size = 41727, upload-time = "2025-12-17T23:32:28.985Z" }, - { url = "https://files.pythonhosted.org/packages/5f/fb/e3633e5a6bbed1c76bb2e9810dabc2f8467532ffcd29b9aed404b473061a/time_machine-3.2.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c1aee29bc54356f248d5d7dfdd131e12ca825e850a08c0ebdb022266d073013", size = 38952, upload-time = "2025-12-17T23:32:30.031Z" }, - { url = "https://files.pythonhosted.org/packages/82/3d/02e9fb2526b3d6b1b45bc8e4d912d95d1cd699d1a3f6df985817d37a0600/time_machine-3.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c8ed2224f09d25b1c2fc98683613aca12f90f682a427eabb68fc824d27014e4a", size = 39829, upload-time = "2025-12-17T23:32:31.075Z" }, - { url = "https://files.pythonhosted.org/packages/85/c8/c14265212436da8e0814c45463987b3f57de3eca4de023cc2eabb0c62ef3/time_machine-3.2.0-cp313-cp313t-win32.whl", hash = "sha256:3498719f8dab51da76d29a20c1b5e52ee7db083dddf3056af7fa69c1b94e1fe6", size = 17852, upload-time = "2025-12-17T23:32:32.079Z" }, - { url = "https://files.pythonhosted.org/packages/1d/bc/8acb13cf6149f47508097b158a9a8bec9ec4530a70cb406124e8023581f5/time_machine-3.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e0d90bee170b219e1d15e6a58164aa808f5170090e4f090bd0670303e34181b1", size = 18918, upload-time = "2025-12-17T23:32:33.106Z" }, - { url = "https://files.pythonhosted.org/packages/24/87/c443ee508c2708fd2514ccce9052f5e48888783ce690506919629ebc8eb0/time_machine-3.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:051de220fdb6e20d648111bbad423d9506fdbb2e44d4429cef3dc0382abf1fc2", size = 17261, upload-time = "2025-12-17T23:32:34.446Z" }, - { url = "https://files.pythonhosted.org/packages/61/70/b4b980d126ed155c78d1879c50d60c8dcbd47bd11cb14ee7be50e0dfc07f/time_machine-3.2.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:1398980c017fe5744d66f419e0115ee48a53b00b146d738e1416c225eb610b82", size = 19303, upload-time = "2025-12-17T23:32:35.796Z" }, - { url = "https://files.pythonhosted.org/packages/73/73/eaa33603c69a68fe2b6f54f9dd75481693d62f1d29676531002be06e2d1c/time_machine-3.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:4f8f4e35f4191ef70c2ab8ff490761ee9051b891afce2bf86dde3918eb7b537b", size = 15431, upload-time = "2025-12-17T23:32:37.244Z" }, - { url = "https://files.pythonhosted.org/packages/76/10/b81e138e86cc7bab40cdb59d294b341e172201f4a6c84bb0ec080407977a/time_machine-3.2.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6db498686ecf6163c5aa8cf0bcd57bbe0f4081184f247edf3ee49a2612b584f9", size = 33206, upload-time = "2025-12-17T23:32:38.713Z" }, - { url = "https://files.pythonhosted.org/packages/d3/72/4deab446b579e8bd5dca91de98595c5d6bd6a17ce162abf5c5f2ce40d3d8/time_machine-3.2.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:027c1807efb74d0cd58ad16524dec94212fbe900115d70b0123399883657ac0f", size = 34792, upload-time = "2025-12-17T23:32:40.223Z" }, - { url = "https://files.pythonhosted.org/packages/2c/39/439c6b587ddee76d533fe972289d0646e0a5520e14dc83d0a30aeb5565f7/time_machine-3.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92432610c05676edd5e6946a073c6f0c926923123ce7caee1018dc10782c713d", size = 36187, upload-time = "2025-12-17T23:32:41.705Z" }, - { url = "https://files.pythonhosted.org/packages/4b/db/2da4368db15180989bab83746a857bde05ad16e78f326801c142bb747a06/time_machine-3.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c25586b62480eb77ef3d953fba273209478e1ef49654592cd6a52a68dfe56a67", size = 34855, upload-time = "2025-12-17T23:32:42.817Z" }, - { url = "https://files.pythonhosted.org/packages/88/84/120a431fee50bc4c241425bee4d3a4910df4923b7ab5f7dff1bf0c772f08/time_machine-3.2.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6bf3a2fa738d15e0b95d14469a0b8ea42635467408d8b490e263d5d45c9a177f", size = 33222, upload-time = "2025-12-17T23:32:43.94Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ea/89cfda82bb8c57ff91bb9a26751aa234d6d90e9b4d5ab0ad9dce0f9f0329/time_machine-3.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ce76b82276d7ad2a66cdc85dad4df19d1422b69183170a34e8fbc4c3f35502f7", size = 34270, upload-time = "2025-12-17T23:32:45.037Z" }, - { url = "https://files.pythonhosted.org/packages/8a/aa/235357da4f69a51a8d35fcbfcfa77cdc7dc24f62ae54025006570bda7e2d/time_machine-3.2.0-cp314-cp314-win32.whl", hash = "sha256:14d6778273c543441863dff712cd1d7803dee946b18de35921eb8df10714539d", size = 17544, upload-time = "2025-12-17T23:32:46.099Z" }, - { url = "https://files.pythonhosted.org/packages/7b/51/6c8405a7276be79693b792cff22ce41067ec05db26a7d02f2d5b06324434/time_machine-3.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:cbf821da96dbc80d349fa9e7c36e670b41d68a878d28c8850057992fed430eef", size = 18423, upload-time = "2025-12-17T23:32:47.468Z" }, - { url = "https://files.pythonhosted.org/packages/d9/03/a3cf419e20c35fc203c6e4fed48b5b667c1a2b4da456d9971e605f73ecef/time_machine-3.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:71c75d71f8e68abc8b669bca26ed2ddd558430a6c171e32b8620288565f18c0e", size = 17050, upload-time = "2025-12-17T23:32:48.91Z" }, - { url = "https://files.pythonhosted.org/packages/86/a1/142de946dc4393f910bf4564b5c3ba819906e1f49b06c9cb557519c849e4/time_machine-3.2.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4e374779021446fc2b5c29d80457ec9a3b1a5df043dc2aae07d7c1415d52323c", size = 19991, upload-time = "2025-12-17T23:32:49.933Z" }, - { url = "https://files.pythonhosted.org/packages/ee/62/7f17def6289901f94726921811a16b9adce46e666362c75d45730c60274f/time_machine-3.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:122310a6af9c36e9a636da32830e591e7923e8a07bdd0a43276c3a36c6821c90", size = 15707, upload-time = "2025-12-17T23:32:50.969Z" }, - { url = "https://files.pythonhosted.org/packages/5d/d3/3502fb9bd3acb159c18844b26c43220201a0d4a622c0c853785d07699a92/time_machine-3.2.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ba3eeb0f018cc362dd8128befa3426696a2e16dd223c3fb695fde184892d4d8c", size = 39207, upload-time = "2025-12-17T23:32:52.033Z" }, - { url = "https://files.pythonhosted.org/packages/5a/be/8b27f4aa296fda14a5a2ad7f588ddd450603c33415ab3f8e85b2f1a44678/time_machine-3.2.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:77d38ba664b381a7793f8786efc13b5004f0d5f672dae814430445b8202a67a6", size = 40764, upload-time = "2025-12-17T23:32:53.167Z" }, - { url = "https://files.pythonhosted.org/packages/42/cd/fe4c4e5c8ab6d48fab3624c32be9116fb120173a35fe67e482e5cf68b3d2/time_machine-3.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f09abeb8f03f044d72712207e0489a62098ad3ad16dac38927fcf80baca4d6a7", size = 43508, upload-time = "2025-12-17T23:32:54.597Z" }, - { url = "https://files.pythonhosted.org/packages/b4/28/5a3ba2fce85b97655a425d6bb20a441550acd2b304c96b2c19d3839f721a/time_machine-3.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6b28367ce4f73987a55e230e1d30a57a3af85da8eb1a140074eb6e8c7e6ef19f", size = 41712, upload-time = "2025-12-17T23:32:55.781Z" }, - { url = "https://files.pythonhosted.org/packages/81/58/e38084be7fdabb4835db68a3a47e58c34182d79fc35df1ecbe0db2c5359f/time_machine-3.2.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:903c7751c904581da9f7861c3015bed7cdc40047321291d3694a3cdc783bbca3", size = 38939, upload-time = "2025-12-17T23:32:56.867Z" }, - { url = "https://files.pythonhosted.org/packages/40/d0/ad3feb0a392ef4e0c08bc32024950373ddc0669002cbdcbb9f3bf0c2d114/time_machine-3.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:528217cad85ede5f85c8bc78b0341868d3c3cfefc6ecb5b622e1cacb6c73247b", size = 39837, upload-time = "2025-12-17T23:32:58.283Z" }, - { url = "https://files.pythonhosted.org/packages/5b/9e/5f4b2ea63b267bd78f3245e76f5528836611b5f2d30b5e7300a722fe4428/time_machine-3.2.0-cp314-cp314t-win32.whl", hash = "sha256:75724762ffd517e7e80aaec1fad1ff5a7414bd84e2b3ee7a0bacfeb67c14926e", size = 18091, upload-time = "2025-12-17T23:32:59.403Z" }, - { url = "https://files.pythonhosted.org/packages/39/6f/456b1f4d2700ae02b19eba830f870596a4b89b74bac3b6c80666f1b108c5/time_machine-3.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2526abbd053c5bca898d1b3e7898eec34626b12206718d8c7ce88fd12c1c9c5c", size = 19208, upload-time = "2025-12-17T23:33:00.488Z" }, - { url = "https://files.pythonhosted.org/packages/2f/22/8063101427ecd3d2652aada4d21d0876b07a3dc789125bca2ee858fec3ed/time_machine-3.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:7f2fb6784b414edbe2c0b558bfaab0c251955ba27edd62946cce4a01675a992c", size = 17359, upload-time = "2025-12-17T23:33:01.54Z" }, -] - -[[package]] -name = "tokenizers" -version = "0.22.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, - { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, - { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, - { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, - { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, - { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, - { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, - { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, - { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, - { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, - { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, - { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, - { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, - { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, -] - -[[package]] -name = "tqdm" -version = "4.67.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, -] - -[[package]] -name = "traitlets" -version = "5.14.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, -] - -[[package]] -name = "twine" -version = "6.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "id" }, - { name = "keyring", marker = "platform_machine != 'ppc64le' and platform_machine != 's390x'" }, - { name = "packaging" }, - { name = "readme-renderer" }, - { name = "requests" }, - { name = "requests-toolbelt" }, - { name = "rfc3986" }, - { name = "rich" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e0/a8/949edebe3a82774c1ec34f637f5dd82d1cf22c25e963b7d63771083bbee5/twine-6.2.0.tar.gz", hash = "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf", size = 172262, upload-time = "2025-09-04T15:43:17.255Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/7a/882d99539b19b1490cac5d77c67338d126e4122c8276bf640e411650c830/twine-6.2.0-py3-none-any.whl", hash = "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8", size = 42727, upload-time = "2025-09-04T15:43:15.994Z" }, -] - -[[package]] -name = "typer" -version = "0.24.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-doc" }, - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613, upload-time = "2026-02-21T16:54:40.609Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085, upload-time = "2026-02-21T16:54:41.616Z" }, -] - -[[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.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "tzdata" -version = "2025.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, -] - -[[package]] -name = "uncalled-for" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/7c/b5b7d8136f872e3f13b0584e576886de0489d7213a12de6bebf29ff6ebfc/uncalled_for-0.2.0.tar.gz", hash = "sha256:b4f8fdbcec328c5a113807d653e041c5094473dd4afa7c34599ace69ccb7e69f", size = 49488, upload-time = "2026-02-27T17:40:58.137Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/7f/4320d9ce3be404e6310b915c3629fe27bf1e2f438a1a7a3cb0396e32e9a9/uncalled_for-0.2.0-py3-none-any.whl", hash = "sha256:2c0bd338faff5f930918f79e7eb9ff48290df2cb05fcc0b40a7f334e55d4d85f", size = 11351, upload-time = "2026-02-27T17:40:56.804Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.42.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e3/ad/4a96c425be6fb67e0621e62d86c402b4a17ab2be7f7c055d9bd2f638b9e2/uvicorn-0.42.0.tar.gz", hash = "sha256:9b1f190ce15a2dd22e7758651d9b6d12df09a13d51ba5bf4fc33c383a48e1775", size = 85393, upload-time = "2026-03-16T06:19:50.077Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/89/f8827ccff89c1586027a105e5630ff6139a64da2515e24dafe860bd9ae4d/uvicorn-0.42.0-py3-none-any.whl", hash = "sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359", size = 68830, upload-time = "2026-03-16T06:19:48.325Z" }, -] - -[package.optional-dependencies] -standard = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "httptools" }, - { name = "python-dotenv" }, - { name = "pyyaml" }, - { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, - { name = "watchfiles" }, - { name = "websockets" }, -] - -[[package]] -name = "uvloop" -version = "0.22.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" }, - { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" }, - { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" }, - { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" }, - { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" }, - { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" }, - { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, - { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, - { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, - { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, - { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, - { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, - { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, - { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, - { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, - { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, - { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, - { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, - { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, - { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, - { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, - { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, -] - -[[package]] -name = "virtualenv" -version = "21.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "distlib" }, - { name = "filelock" }, - { name = "platformdirs" }, - { name = "python-discovery" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/aa/92/58199fe10049f9703c2666e809c4f686c54ef0a68b0f6afccf518c0b1eb9/virtualenv-21.2.0.tar.gz", hash = "sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098", size = 5840618, upload-time = "2026-03-09T17:24:38.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl", hash = "sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f", size = 5825084, upload-time = "2026-03-09T17:24:35.378Z" }, -] - -[[package]] -name = "watchdog" -version = "6.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, - { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, - { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, - { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, - { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, -] - -[[package]] -name = "watchfiles" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" }, - { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" }, - { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" }, - { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" }, - { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" }, - { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" }, - { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" }, - { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" }, - { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" }, - { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" }, - { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, - { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, - { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, - { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, - { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, - { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, - { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, - { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, - { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, - { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, - { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, - { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, - { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, - { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, - { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, - { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, - { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, - { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, - { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, - { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, - { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, - { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, - { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, - { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, - { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, - { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, - { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, - { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, - { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, - { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, - { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, -] - -[[package]] -name = "wcwidth" -version = "0.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, -] - -[[package]] -name = "websockets" -version = "16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, - { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, - { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, - { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, - { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, - { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, - { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, - { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, - { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, - { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, -] - -[[package]] -name = "win32-setctime" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, -] - -[[package]] -name = "yarl" -version = "1.23.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "multidict" }, - { name = "propcache" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/8a/94615bc31022f711add374097ad4144d569e95ff3c38d39215d07ac153a0/yarl-1.23.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860", size = 124737, upload-time = "2026-03-01T22:05:12.897Z" }, - { url = "https://files.pythonhosted.org/packages/e3/6f/c6554045d59d64052698add01226bc867b52fe4a12373415d7991fdca95d/yarl-1.23.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069", size = 87029, upload-time = "2026-03-01T22:05:14.376Z" }, - { url = "https://files.pythonhosted.org/packages/19/2a/725ecc166d53438bc88f76822ed4b1e3b10756e790bafd7b523fe97c322d/yarl-1.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25", size = 86310, upload-time = "2026-03-01T22:05:15.71Z" }, - { url = "https://files.pythonhosted.org/packages/99/30/58260ed98e6ff7f90ba84442c1ddd758c9170d70327394a6227b310cd60f/yarl-1.23.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", size = 97587, upload-time = "2026-03-01T22:05:17.384Z" }, - { url = "https://files.pythonhosted.org/packages/76/0a/8b08aac08b50682e65759f7f8dde98ae8168f72487e7357a5d684c581ef9/yarl-1.23.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", size = 92528, upload-time = "2026-03-01T22:05:18.804Z" }, - { url = "https://files.pythonhosted.org/packages/52/07/0b7179101fe5f8385ec6c6bb5d0cb9f76bd9fb4a769591ab6fb5cdbfc69a/yarl-1.23.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", size = 105339, upload-time = "2026-03-01T22:05:20.235Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8a/36d82869ab5ec829ca8574dfcb92b51286fcfb1e9c7a73659616362dc880/yarl-1.23.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", size = 105061, upload-time = "2026-03-01T22:05:22.268Z" }, - { url = "https://files.pythonhosted.org/packages/66/3e/868e5c3364b6cee19ff3e1a122194fa4ce51def02c61023970442162859e/yarl-1.23.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", size = 100132, upload-time = "2026-03-01T22:05:23.638Z" }, - { url = "https://files.pythonhosted.org/packages/cf/26/9c89acf82f08a52cb52d6d39454f8d18af15f9d386a23795389d1d423823/yarl-1.23.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", size = 99289, upload-time = "2026-03-01T22:05:25.749Z" }, - { url = "https://files.pythonhosted.org/packages/6f/54/5b0db00d2cb056922356104468019c0a132e89c8d3ab67d8ede9f4483d2a/yarl-1.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", size = 96950, upload-time = "2026-03-01T22:05:27.318Z" }, - { url = "https://files.pythonhosted.org/packages/f6/40/10fa93811fd439341fad7e0718a86aca0de9548023bbb403668d6555acab/yarl-1.23.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", size = 93960, upload-time = "2026-03-01T22:05:28.738Z" }, - { url = "https://files.pythonhosted.org/packages/bc/d2/8ae2e6cd77d0805f4526e30ec43b6f9a3dfc542d401ac4990d178e4bf0cf/yarl-1.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", size = 104703, upload-time = "2026-03-01T22:05:30.438Z" }, - { url = "https://files.pythonhosted.org/packages/2f/0c/b3ceacf82c3fe21183ce35fa2acf5320af003d52bc1fcf5915077681142e/yarl-1.23.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", size = 98325, upload-time = "2026-03-01T22:05:31.835Z" }, - { url = "https://files.pythonhosted.org/packages/9d/e0/12900edd28bdab91a69bd2554b85ad7b151f64e8b521fe16f9ad2f56477a/yarl-1.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", size = 105067, upload-time = "2026-03-01T22:05:33.358Z" }, - { url = "https://files.pythonhosted.org/packages/15/61/74bb1182cf79c9bbe4eb6b1f14a57a22d7a0be5e9cedf8e2d5c2086474c3/yarl-1.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", size = 100285, upload-time = "2026-03-01T22:05:35.4Z" }, - { url = "https://files.pythonhosted.org/packages/69/7f/cd5ef733f2550de6241bd8bd8c3febc78158b9d75f197d9c7baa113436af/yarl-1.23.0-cp312-cp312-win32.whl", hash = "sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d", size = 82359, upload-time = "2026-03-01T22:05:36.811Z" }, - { url = "https://files.pythonhosted.org/packages/f5/be/25216a49daeeb7af2bec0db22d5e7df08ed1d7c9f65d78b14f3b74fd72fc/yarl-1.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e", size = 87674, upload-time = "2026-03-01T22:05:38.171Z" }, - { url = "https://files.pythonhosted.org/packages/d2/35/aeab955d6c425b227d5b7247eafb24f2653fedc32f95373a001af5dfeb9e/yarl-1.23.0-cp312-cp312-win_arm64.whl", hash = "sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9", size = 81879, upload-time = "2026-03-01T22:05:40.006Z" }, - { url = "https://files.pythonhosted.org/packages/9a/4b/a0a6e5d0ee8a2f3a373ddef8a4097d74ac901ac363eea1440464ccbe0898/yarl-1.23.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", size = 123796, upload-time = "2026-03-01T22:05:41.412Z" }, - { url = "https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", size = 86547, upload-time = "2026-03-01T22:05:42.841Z" }, - { url = "https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", size = 85854, upload-time = "2026-03-01T22:05:44.85Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f4/4e30b250927ffdab4db70da08b9b8d2194d7c7b400167b8fbeca1e4701ca/yarl-1.23.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", size = 98351, upload-time = "2026-03-01T22:05:46.836Z" }, - { url = "https://files.pythonhosted.org/packages/86/fc/4118c5671ea948208bdb1492d8b76bdf1453d3e73df051f939f563e7dcc5/yarl-1.23.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", size = 92711, upload-time = "2026-03-01T22:05:48.316Z" }, - { url = "https://files.pythonhosted.org/packages/56/11/1ed91d42bd9e73c13dc9e7eb0dd92298d75e7ac4dd7f046ad0c472e231cd/yarl-1.23.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", size = 106014, upload-time = "2026-03-01T22:05:50.028Z" }, - { url = "https://files.pythonhosted.org/packages/ce/c9/74e44e056a23fbc33aca71779ef450ca648a5bc472bdad7a82339918f818/yarl-1.23.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", size = 105557, upload-time = "2026-03-01T22:05:51.416Z" }, - { url = "https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", size = 101559, upload-time = "2026-03-01T22:05:52.872Z" }, - { url = "https://files.pythonhosted.org/packages/72/59/c5b8d94b14e3d3c2a9c20cb100119fd534ab5a14b93673ab4cc4a4141ea5/yarl-1.23.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", size = 100502, upload-time = "2026-03-01T22:05:54.954Z" }, - { url = "https://files.pythonhosted.org/packages/77/4f/96976cb54cbfc5c9fd73ed4c51804f92f209481d1fb190981c0f8a07a1d7/yarl-1.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", size = 98027, upload-time = "2026-03-01T22:05:56.409Z" }, - { url = "https://files.pythonhosted.org/packages/63/6e/904c4f476471afdbad6b7e5b70362fb5810e35cd7466529a97322b6f5556/yarl-1.23.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", size = 95369, upload-time = "2026-03-01T22:05:58.141Z" }, - { url = "https://files.pythonhosted.org/packages/9d/40/acfcdb3b5f9d68ef499e39e04d25e141fe90661f9d54114556cf83be8353/yarl-1.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", size = 105565, upload-time = "2026-03-01T22:06:00.286Z" }, - { url = "https://files.pythonhosted.org/packages/5e/c6/31e28f3a6ba2869c43d124f37ea5260cac9c9281df803c354b31f4dd1f3c/yarl-1.23.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", size = 99813, upload-time = "2026-03-01T22:06:01.712Z" }, - { url = "https://files.pythonhosted.org/packages/08/1f/6f65f59e72d54aa467119b63fc0b0b1762eff0232db1f4720cd89e2f4a17/yarl-1.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", size = 105632, upload-time = "2026-03-01T22:06:03.188Z" }, - { url = "https://files.pythonhosted.org/packages/a3/c4/18b178a69935f9e7a338127d5b77d868fdc0f0e49becd286d51b3a18c61d/yarl-1.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", size = 101895, upload-time = "2026-03-01T22:06:04.651Z" }, - { url = "https://files.pythonhosted.org/packages/8f/54/f5b870b5505663911dba950a8e4776a0dbd51c9c54c0ae88e823e4b874a0/yarl-1.23.0-cp313-cp313-win32.whl", hash = "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", size = 82356, upload-time = "2026-03-01T22:06:06.04Z" }, - { url = "https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", size = 87515, upload-time = "2026-03-01T22:06:08.107Z" }, - { url = "https://files.pythonhosted.org/packages/00/fd/7e1c66efad35e1649114fa13f17485f62881ad58edeeb7f49f8c5e748bf9/yarl-1.23.0-cp313-cp313-win_arm64.whl", hash = "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", size = 81785, upload-time = "2026-03-01T22:06:10.181Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fc/119dd07004f17ea43bb91e3ece6587759edd7519d6b086d16bfbd3319982/yarl-1.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", size = 130719, upload-time = "2026-03-01T22:06:11.708Z" }, - { url = "https://files.pythonhosted.org/packages/e6/0d/9f2348502fbb3af409e8f47730282cd6bc80dec6630c1e06374d882d6eb2/yarl-1.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", size = 89690, upload-time = "2026-03-01T22:06:13.429Z" }, - { url = "https://files.pythonhosted.org/packages/50/93/e88f3c80971b42cfc83f50a51b9d165a1dbf154b97005f2994a79f212a07/yarl-1.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", size = 89851, upload-time = "2026-03-01T22:06:15.53Z" }, - { url = "https://files.pythonhosted.org/packages/1c/07/61c9dd8ba8f86473263b4036f70fb594c09e99c0d9737a799dfd8bc85651/yarl-1.23.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", size = 95874, upload-time = "2026-03-01T22:06:17.553Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e9/f9ff8ceefba599eac6abddcfb0b3bee9b9e636e96dbf54342a8577252379/yarl-1.23.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", size = 88710, upload-time = "2026-03-01T22:06:19.004Z" }, - { url = "https://files.pythonhosted.org/packages/eb/78/0231bfcc5d4c8eec220bc2f9ef82cb4566192ea867a7c5b4148f44f6cbcd/yarl-1.23.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", size = 101033, upload-time = "2026-03-01T22:06:21.203Z" }, - { url = "https://files.pythonhosted.org/packages/cd/9b/30ea5239a61786f18fd25797151a17fbb3be176977187a48d541b5447dd4/yarl-1.23.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", size = 100817, upload-time = "2026-03-01T22:06:22.738Z" }, - { url = "https://files.pythonhosted.org/packages/62/e2/a4980481071791bc83bce2b7a1a1f7adcabfa366007518b4b845e92eeee3/yarl-1.23.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", size = 97482, upload-time = "2026-03-01T22:06:24.21Z" }, - { url = "https://files.pythonhosted.org/packages/e5/1e/304a00cf5f6100414c4b5a01fc7ff9ee724b62158a08df2f8170dfc72a2d/yarl-1.23.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", size = 95949, upload-time = "2026-03-01T22:06:25.697Z" }, - { url = "https://files.pythonhosted.org/packages/68/03/093f4055ed4cae649ac53bca3d180bd37102e9e11d048588e9ab0c0108d0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", size = 95839, upload-time = "2026-03-01T22:06:27.309Z" }, - { url = "https://files.pythonhosted.org/packages/b9/28/4c75ebb108f322aa8f917ae10a8ffa4f07cae10a8a627b64e578617df6a0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", size = 90696, upload-time = "2026-03-01T22:06:29.048Z" }, - { url = "https://files.pythonhosted.org/packages/23/9c/42c2e2dd91c1a570402f51bdf066bfdb1241c2240ba001967bad778e77b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", size = 100865, upload-time = "2026-03-01T22:06:30.525Z" }, - { url = "https://files.pythonhosted.org/packages/74/05/1bcd60a8a0a914d462c305137246b6f9d167628d73568505fce3f1cb2e65/yarl-1.23.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", size = 96234, upload-time = "2026-03-01T22:06:32.692Z" }, - { url = "https://files.pythonhosted.org/packages/90/b2/f52381aac396d6778ce516b7bc149c79e65bfc068b5de2857ab69eeea3b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", size = 100295, upload-time = "2026-03-01T22:06:34.268Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/638bae5bbf1113a659b2435d8895474598afe38b4a837103764f603aba56/yarl-1.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", size = 97784, upload-time = "2026-03-01T22:06:35.864Z" }, - { url = "https://files.pythonhosted.org/packages/80/25/a3892b46182c586c202629fc2159aa13975d3741d52ebd7347fd501d48d5/yarl-1.23.0-cp313-cp313t-win32.whl", hash = "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", size = 88313, upload-time = "2026-03-01T22:06:37.39Z" }, - { url = "https://files.pythonhosted.org/packages/43/68/8c5b36aa5178900b37387937bc2c2fe0e9505537f713495472dcf6f6fccc/yarl-1.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", size = 94932, upload-time = "2026-03-01T22:06:39.579Z" }, - { url = "https://files.pythonhosted.org/packages/c6/cc/d79ba8292f51f81f4dc533a8ccfb9fc6992cabf0998ed3245de7589dc07c/yarl-1.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", size = 84786, upload-time = "2026-03-01T22:06:41.988Z" }, - { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455, upload-time = "2026-03-01T22:06:43.615Z" }, - { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752, upload-time = "2026-03-01T22:06:45.425Z" }, - { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291, upload-time = "2026-03-01T22:06:46.974Z" }, - { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026, upload-time = "2026-03-01T22:06:48.459Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355, upload-time = "2026-03-01T22:06:50.306Z" }, - { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417, upload-time = "2026-03-01T22:06:52.1Z" }, - { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422, upload-time = "2026-03-01T22:06:54.424Z" }, - { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915, upload-time = "2026-03-01T22:06:55.895Z" }, - { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690, upload-time = "2026-03-01T22:06:58.21Z" }, - { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750, upload-time = "2026-03-01T22:06:59.794Z" }, - { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685, upload-time = "2026-03-01T22:07:01.375Z" }, - { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009, upload-time = "2026-03-01T22:07:03.151Z" }, - { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033, upload-time = "2026-03-01T22:07:04.701Z" }, - { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483, upload-time = "2026-03-01T22:07:06.231Z" }, - { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175, upload-time = "2026-03-01T22:07:08.4Z" }, - { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871, upload-time = "2026-03-01T22:07:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093, upload-time = "2026-03-01T22:07:11.501Z" }, - { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384, upload-time = "2026-03-01T22:07:13.069Z" }, - { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019, upload-time = "2026-03-01T22:07:14.903Z" }, - { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894, upload-time = "2026-03-01T22:07:17.372Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979, upload-time = "2026-03-01T22:07:19.361Z" }, - { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943, upload-time = "2026-03-01T22:07:21.266Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786, upload-time = "2026-03-01T22:07:23.129Z" }, - { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307, upload-time = "2026-03-01T22:07:24.689Z" }, - { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904, upload-time = "2026-03-01T22:07:26.287Z" }, - { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728, upload-time = "2026-03-01T22:07:27.906Z" }, - { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964, upload-time = "2026-03-01T22:07:30.198Z" }, - { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882, upload-time = "2026-03-01T22:07:32.132Z" }, - { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797, upload-time = "2026-03-01T22:07:34.404Z" }, - { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023, upload-time = "2026-03-01T22:07:35.953Z" }, - { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227, upload-time = "2026-03-01T22:07:37.594Z" }, - { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302, upload-time = "2026-03-01T22:07:39.334Z" }, - { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202, upload-time = "2026-03-01T22:07:41.717Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558, upload-time = "2026-03-01T22:07:43.433Z" }, - { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610, upload-time = "2026-03-01T22:07:45.773Z" }, - { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, - { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, -] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -]